./PaxHeaders/bluez-5.820000644000000000000000000000005014773213553011740 xustar0020 atime=1743591291 20 ctime=1743591275 bluez-5.82/0000755000000000000000000000000014773213553011341 5ustar00rootrootbluez-5.82/PaxHeaders/Makefile.plugins0000644000000000000000000000005014766002272014774 xustar0020 atime=1743515577 20 ctime=1743591275 bluez-5.82/Makefile.plugins0000644000000000000000000000731114766002272014457 0ustar00rootroot# SPDX-License-Identifier: GPL-2.0 builtin_modules += hostname builtin_sources += plugins/hostname.c builtin_modules += autopair builtin_sources += plugins/autopair.c builtin_modules += policy builtin_sources += plugins/policy.c builtin_sources += profiles/audio/media.h profiles/audio/media.c \ profiles/audio/transport.h profiles/audio/transport.c \ profiles/audio/player.h profiles/audio/player.c if ADMIN builtin_modules += admin builtin_sources += plugins/admin.c endif if NFC builtin_modules += neard builtin_sources += plugins/neard.c endif if SAP builtin_modules += sap builtin_sources += profiles/sap/main.c profiles/sap/manager.h \ profiles/sap/manager.c profiles/sap/server.h \ profiles/sap/server.c profiles/sap/sap.h \ profiles/sap/sap-dummy.c endif if A2DP builtin_modules += a2dp builtin_sources += profiles/audio/source.h profiles/audio/source.c \ profiles/audio/sink.h profiles/audio/sink.c \ profiles/audio/a2dp.h profiles/audio/a2dp.c \ profiles/audio/avdtp.h profiles/audio/avdtp.c \ profiles/audio/a2dp-codecs.h endif if AVRCP builtin_modules += avrcp builtin_sources += profiles/audio/control.h profiles/audio/control.c \ profiles/audio/avctp.h profiles/audio/avctp.c \ profiles/audio/avrcp.h profiles/audio/avrcp.c endif if NETWORK builtin_modules += network builtin_sources += profiles/network/manager.c \ profiles/network/bnep.h profiles/network/bnep.c \ profiles/network/server.h profiles/network/server.c \ profiles/network/connection.h \ profiles/network/connection.c endif if HID builtin_modules += input builtin_sources += profiles/input/manager.c \ profiles/input/server.h profiles/input/server.c \ profiles/input/device.h profiles/input/device.c \ profiles/input/hidp_defs.h profiles/input/sixaxis.h endif if HOG builtin_modules += hog builtin_sources += profiles/input/hog.c \ profiles/input/hog-lib.c profiles/input/hog-lib.h \ profiles/deviceinfo/dis.c profiles/deviceinfo/dis.h \ profiles/battery/bas.c profiles/battery/bas.h \ profiles/scanparam/scpp.c profiles/scanparam/scpp.h \ profiles/input/suspend.h profiles/input/suspend-none.c endif if HEALTH builtin_modules += health builtin_sources += profiles/health/mcap.h profiles/health/mcap.c \ profiles/health/hdp_main.c profiles/health/hdp_types.h \ profiles/health/hdp_manager.h \ profiles/health/hdp_manager.c \ profiles/health/hdp.h profiles/health/hdp.c \ profiles/health/hdp_util.h profiles/health/hdp_util.c endif builtin_modules += gap builtin_sources += profiles/gap/gas.c builtin_modules += scanparam builtin_sources += profiles/scanparam/scan.c builtin_modules += deviceinfo builtin_sources += profiles/deviceinfo/deviceinfo.c if MIDI builtin_modules += midi builtin_sources += profiles/midi/midi.c \ profiles/midi/libmidi.h \ profiles/midi/libmidi.c builtin_cppflags += $(ALSA_CFLAGS) builtin_ldadd += $(ALSA_LIBS) endif builtin_modules += battery builtin_sources += profiles/battery/battery.c if SIXAXIS builtin_modules += sixaxis builtin_sources += plugins/sixaxis.c builtin_ldadd += $(UDEV_LIBS) endif if BAP builtin_modules += bap builtin_sources += profiles/audio/bap.c endif if BASS builtin_modules += bass builtin_sources += profiles/audio/bass.c endif if MCP builtin_modules += mcp builtin_sources += profiles/audio/mcp.c endif if VCP builtin_modules += vcp builtin_sources += profiles/audio/vcp.h profiles/audio/vcp.c endif if MICP builtin_modules += micp builtin_sources += profiles/audio/micp.c endif if CCP builtin_modules += ccp builtin_sources += profiles/audio/ccp.c endif if CSIP builtin_modules += csip builtin_sources += profiles/audio/csip.c endif if ASHA builtin_modules += asha builtin_sources += profiles/audio/asha.h profiles/audio/asha.c endif bluez-5.82/PaxHeaders/emulator0000644000000000000000000000005014773213561013432 xustar0020 atime=1743591291 20 ctime=1743591281 bluez-5.82/emulator/0000755000000000000000000000000014773213561013170 5ustar00rootrootbluez-5.82/emulator/PaxHeaders/bthost.h0000644000000000000000000000005014766002272015160 xustar0020 atime=1743515578 20 ctime=1743591278 bluez-5.82/emulator/bthost.h0000644000000000000000000001726614766002272014655 0ustar00rootroot/* SPDX-License-Identifier: LGPL-2.1-or-later */ /* * * BlueZ - Bluetooth protocol stack for Linux * * Copyright (C) 2011-2012 Intel Corporation * Copyright (C) 2004-2010 Marcel Holtmann * Copyright 2023-2024 NXP * * */ #include #include #include "lib/bluetooth.h" typedef void (*bthost_send_func) (const struct iovec *iov, int iovlen, void *user_data); struct bthost; struct bthost *bthost_create(void); void bthost_destroy(struct bthost *bthost); typedef void (*bthost_ready_cb) (void); void bthost_notify_ready(struct bthost *bthost, bthost_ready_cb cb); typedef void (*bthost_debug_func_t)(const char *str, void *user_data); typedef void (*bthost_destroy_func_t)(void *user_data); bool bthost_set_debug(struct bthost *bthost, bthost_debug_func_t callback, void *user_data, bthost_destroy_func_t destroy); void bthost_debug(struct bthost *bthost, const char *format, ...) __attribute__((format(printf, 2, 3))); void bthost_set_send_handler(struct bthost *bthost, bthost_send_func handler, void *user_data); void bthost_receive_h4(struct bthost *bthost, const void *data, uint16_t len); typedef void (*bthost_cmd_complete_cb) (uint16_t opcode, uint8_t status, const void *param, uint8_t len, void *user_data); void bthost_set_cmd_complete_cb(struct bthost *bthost, bthost_cmd_complete_cb cb, void *user_data); typedef uint8_t (*bthost_accept_conn_cb) (uint16_t handle, void *user_data); typedef void (*bthost_new_conn_cb) (uint16_t handle, void *user_data); void bthost_set_connect_cb(struct bthost *bthost, bthost_new_conn_cb cb, void *user_data); void bthost_set_sco_cb(struct bthost *bthost, bthost_new_conn_cb cb, void *user_data); void bthost_set_iso_cb(struct bthost *bthost, bthost_accept_conn_cb accept, bthost_new_conn_cb cb, void *user_data); void bthost_hci_connect(struct bthost *bthost, const uint8_t *bdaddr, uint8_t addr_type); void bthost_hci_ext_connect(struct bthost *bthost, const uint8_t *bdaddr, uint8_t addr_type); void bthost_hci_disconnect(struct bthost *bthost, uint16_t handle, uint8_t reason); typedef void (*bthost_cid_hook_func_t)(const void *data, uint16_t len, void *user_data); void bthost_add_cid_hook(struct bthost *bthost, uint16_t handle, uint16_t cid, bthost_cid_hook_func_t func, void *user_data); typedef void (*bthost_sco_hook_func_t)(const void *data, uint16_t len, uint8_t status, void *user_data); void bthost_add_sco_hook(struct bthost *bthost, uint16_t handle, bthost_sco_hook_func_t func, void *user_data, bthost_destroy_func_t destroy); typedef void (*bthost_iso_hook_func_t)(const void *data, uint16_t len, void *user_data); void bthost_add_iso_hook(struct bthost *bthost, uint16_t handle, bthost_iso_hook_func_t func, void *user_data, bthost_destroy_func_t destroy); void bthost_send_cid(struct bthost *bthost, uint16_t handle, uint16_t cid, const void *data, uint16_t len); void bthost_send_cid_v(struct bthost *bthost, uint16_t handle, uint16_t cid, const struct iovec *iov, int iovcnt); void bthost_send_iso(struct bthost *bthost, uint16_t handle, bool ts, uint16_t sn, uint32_t timestamp, uint8_t pkt_status, const struct iovec *iov, int iovcnt); typedef void (*bthost_l2cap_rsp_cb) (uint8_t code, const void *data, uint16_t len, void *user_data); bool bthost_l2cap_req(struct bthost *bthost, uint16_t handle, uint8_t req, const void *data, uint16_t len, bthost_l2cap_rsp_cb cb, void *user_data); void bthost_write_scan_enable(struct bthost *bthost, uint8_t scan); void bthost_set_adv_data(struct bthost *bthost, const uint8_t *data, uint8_t len); void bthost_set_adv_enable(struct bthost *bthost, uint8_t enable); void bthost_set_ext_adv_data(struct bthost *bthost, const uint8_t *data, uint8_t len); void bthost_set_ext_adv_params(struct bthost *bthost); void bthost_set_ext_adv_enable(struct bthost *bthost, uint8_t enable); void bthost_set_pa_params(struct bthost *bthost); void bthost_set_pa_data(struct bthost *bthost, const uint8_t *data, uint8_t len); void bthost_set_base(struct bthost *bthost, const uint8_t *data, uint8_t len); void bthost_set_pa_enable(struct bthost *bthost, uint8_t enable); void bthost_create_big(struct bthost *bthost, uint8_t num_bis, uint8_t enc, const uint8_t *bcode); bool bthost_search_ext_adv_addr(struct bthost *bthost, const uint8_t *addr); void bthost_set_cig_params(struct bthost *bthost, uint8_t cig_id, uint8_t cis_id, const struct bt_iso_qos *qos); void bthost_create_cis(struct bthost *bthost, uint16_t cis_handle, uint16_t acl_handle); void bthost_set_scan_params(struct bthost *bthost, uint8_t scan_type, uint8_t addr_type, uint8_t filter_policy); void bthost_set_scan_enable(struct bthost *bthost, uint8_t enable); void bthost_write_ssp_mode(struct bthost *bthost, uint8_t mode); void bthost_write_le_host_supported(struct bthost *bthost, uint8_t mode); void bthost_request_auth(struct bthost *bthost, uint16_t handle); void bthost_le_start_encrypt(struct bthost *bthost, uint16_t handle, const uint8_t ltk[16]); typedef void (*bthost_l2cap_connect_cb) (uint16_t handle, uint16_t cid, void *user_data); typedef void (*bthost_l2cap_disconnect_cb) (void *user_data); void bthost_add_l2cap_server(struct bthost *bthost, uint16_t psm, bthost_l2cap_connect_cb func, bthost_l2cap_disconnect_cb disconn_func, void *user_data); void bthost_add_l2cap_server_custom(struct bthost *bthost, uint16_t psm, uint16_t mtu, uint16_t mps, uint16_t credits, bthost_l2cap_connect_cb func, bthost_l2cap_disconnect_cb disconn_func, void *user_data); void bthost_set_sc_support(struct bthost *bthost, bool enable); void bthost_set_pin_code(struct bthost *bthost, const uint8_t *pin, uint8_t pin_len); void bthost_set_io_capability(struct bthost *bthost, uint8_t io_capability); uint8_t bthost_get_io_capability(struct bthost *bthost); void bthost_set_auth_req(struct bthost *bthost, uint8_t auth_req); uint8_t bthost_get_auth_req(struct bthost *bthost); void bthost_set_reject_user_confirm(struct bthost *bthost, bool reject); bool bthost_get_reject_user_confirm(struct bthost *bthost); bool bthost_bredr_capable(struct bthost *bthost); uint64_t bthost_conn_get_fixed_chan(struct bthost *bthost, uint16_t handle); typedef void (*bthost_rfcomm_connect_cb) (uint16_t handle, uint16_t cid, void *user_data, bool status); void bthost_add_rfcomm_server(struct bthost *bthost, uint8_t channel, bthost_rfcomm_connect_cb func, void *user_data); bool bthost_connect_rfcomm(struct bthost *bthost, uint16_t handle, uint8_t channel, bthost_rfcomm_connect_cb func, void *user_data); typedef void (*bthost_rfcomm_chan_hook_func_t) (const void *data, uint16_t len, void *user_data); void bthost_add_rfcomm_chan_hook(struct bthost *bthost, uint16_t handle, uint8_t channel, bthost_rfcomm_chan_hook_func_t func, void *user_data); void bthost_send_rfcomm_data(struct bthost *bthost, uint16_t handle, uint8_t channel, const void *data, uint16_t len); void bthost_start(struct bthost *bthost); /* LE SMP support */ void *smp_start(struct bthost *bthost); void smp_stop(void *smp_data); void *smp_conn_add(void *smp_data, uint16_t handle, const uint8_t *ia, uint8_t ia_type, const uint8_t *ra, uint8_t ra_type, bool conn_init); void smp_conn_del(void *conn_data); void smp_conn_encrypted(void *conn_data, uint8_t encrypt); void smp_data(void *conn_data, const void *data, uint16_t len); void smp_bredr_data(void *conn_data, const void *data, uint16_t len); int smp_get_ltk(void *smp_data, uint64_t rand, uint16_t ediv, uint8_t *ltk); void smp_pair(void *conn_data, uint8_t io_cap, uint8_t auth_req); bluez-5.82/emulator/PaxHeaders/phy.c0000644000000000000000000000005014214376354014453 xustar0020 atime=1743516867 20 ctime=1743591281 bluez-5.82/emulator/phy.c0000644000000000000000000001264014214376354014137 0ustar00rootroot// SPDX-License-Identifier: LGPL-2.1-or-later /* * * BlueZ - Bluetooth protocol stack for Linux * * Copyright (C) 2011-2012 Intel Corporation * Copyright (C) 2004-2010 Marcel Holtmann * * */ #ifdef HAVE_CONFIG_H #include #endif #define _GNU_SOURCE #include #include #include #include #include #include #include #include #include "src/shared/util.h" #include "src/shared/mainloop.h" #include "phy.h" #define BT_PHY_PORT 45023 struct bt_phy { volatile int ref_count; int rx_fd; int tx_fd; uint64_t id; bt_phy_callback_func_t callback; void *user_data; }; struct bt_phy_hdr { uint64_t id; uint32_t flags; uint16_t type; uint16_t len; } __attribute__ ((packed)); static bool get_random_bytes(void *buf, size_t num_bytes) { ssize_t len; int fd; fd = open("/dev/urandom", O_RDONLY); if (fd < 0) return false; len = read(fd, buf, num_bytes); close(fd); if (len < 0) return false; return true; } static void phy_rx_callback(int fd, uint32_t events, void *user_data) { struct bt_phy *phy = user_data; struct msghdr msg; struct iovec iov[2]; struct bt_phy_hdr hdr; unsigned char buf[4096]; ssize_t len; if (events & (EPOLLERR | EPOLLHUP)) { mainloop_remove_fd(fd); return; } iov[0].iov_base = &hdr; iov[0].iov_len = sizeof(hdr); iov[1].iov_base = buf; iov[1].iov_len = sizeof(buf); memset(&msg, 0, sizeof(msg)); msg.msg_iov = iov; msg.msg_iovlen = 2; len = recvmsg(phy->rx_fd, &msg, MSG_DONTWAIT); if (len < 0) return; if ((size_t) len < sizeof(hdr)) return; if (le64_to_cpu(hdr.id) == phy->id) return; if (len - sizeof(hdr) != le16_to_cpu(hdr.len)) return; if (phy->callback) phy->callback(le16_to_cpu(hdr.type), buf, len - sizeof(hdr), phy->user_data); } static int create_rx_socket(void) { struct sockaddr_in addr; int fd, opt = 1; fd = socket(PF_INET, SOCK_DGRAM | SOCK_CLOEXEC, 0); if (fd < 0) return -1; if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt)) < 0) { close(fd); return -1; } memset(&addr, 0, sizeof(addr)); addr.sin_family = AF_INET; addr.sin_port = htons(BT_PHY_PORT); addr.sin_addr.s_addr = INADDR_BROADCAST; if (bind(fd, (struct sockaddr *) &addr, sizeof(addr)) < 0) { close(fd); return -1; } return fd; } static int create_tx_socket(void) { int fd, opt = 1; fd = socket(PF_INET, SOCK_DGRAM | SOCK_CLOEXEC, 0); if (fd < 0) return -1; if (setsockopt(fd, SOL_SOCKET, SO_BROADCAST, &opt, sizeof(opt)) < 0) { close(fd); return -1; } return fd; } struct bt_phy *bt_phy_new(void) { struct bt_phy *phy; phy = calloc(1, sizeof(*phy)); if (!phy) return NULL; phy->rx_fd = create_rx_socket(); if (phy->rx_fd < 0) { free(phy); return NULL; } phy->tx_fd = create_tx_socket(); if (phy->tx_fd < 0) { close(phy->rx_fd); free(phy); return NULL; } mainloop_add_fd(phy->rx_fd, EPOLLIN, phy_rx_callback, phy, NULL); if (!get_random_bytes(&phy->id, sizeof(phy->id))) { if (util_getrandom(&phy->id, sizeof(phy->id), 0) < 0) { mainloop_remove_fd(phy->rx_fd); close(phy->tx_fd); close(phy->rx_fd); free(phy); return NULL; } } bt_phy_send(phy, BT_PHY_PKT_NULL, NULL, 0); return bt_phy_ref(phy); } struct bt_phy *bt_phy_ref(struct bt_phy *phy) { if (!phy) return NULL; __sync_fetch_and_add(&phy->ref_count, 1); return phy; } void bt_phy_unref(struct bt_phy *phy) { if (!phy) return; if (__sync_sub_and_fetch(&phy->ref_count, 1)) return; mainloop_remove_fd(phy->rx_fd); close(phy->tx_fd); close(phy->rx_fd); free(phy); } bool bt_phy_send(struct bt_phy *phy, uint16_t type, const void *data, size_t size) { return bt_phy_send_vector(phy, type, data, size, NULL, 0, NULL, 0); } bool bt_phy_send_vector(struct bt_phy *phy, uint16_t type, const void *data1, size_t size1, const void *data2, size_t size2, const void *data3, size_t size3) { struct bt_phy_hdr hdr; struct sockaddr_in addr; struct msghdr msg; struct iovec iov[4]; ssize_t len; if (!phy) return false; memset(&addr, 0, sizeof(addr)); addr.sin_family = AF_INET; addr.sin_port = htons(BT_PHY_PORT); addr.sin_addr.s_addr = INADDR_BROADCAST; memset(&msg, 0, sizeof(msg)); msg.msg_name = &addr; msg.msg_namelen = sizeof(addr); msg.msg_iov = iov; msg.msg_iovlen = 0; memset(&hdr, 0, sizeof(hdr)); hdr.id = cpu_to_le64(phy->id); hdr.flags = cpu_to_le32(0); hdr.type = cpu_to_le16(type); hdr.len = cpu_to_le16(size1 + size2 + size3); iov[msg.msg_iovlen].iov_base = &hdr; iov[msg.msg_iovlen].iov_len = sizeof(hdr); msg.msg_iovlen++; if (data1 && size1 > 0) { iov[msg.msg_iovlen].iov_base = (void *) data1; iov[msg.msg_iovlen].iov_len = size1; msg.msg_iovlen++; } if (data2 && size2 > 0) { iov[msg.msg_iovlen].iov_base = (void *) data2; iov[msg.msg_iovlen].iov_len = size2; msg.msg_iovlen++; } if (data3 && size3 > 0) { iov[msg.msg_iovlen].iov_base = (void *) data3; iov[msg.msg_iovlen].iov_len = size3; msg.msg_iovlen++; } memset(&addr, 0, sizeof(addr)); addr.sin_family = AF_INET; addr.sin_port = htons(BT_PHY_PORT); addr.sin_addr.s_addr = INADDR_BROADCAST; len = sendmsg(phy->tx_fd, &msg, MSG_DONTWAIT); if (len < 0) return false; return true; } bool bt_phy_register(struct bt_phy *phy, bt_phy_callback_func_t callback, void *user_data) { if (!phy) return false; phy->callback = callback; phy->user_data = user_data; return true; } bluez-5.82/emulator/PaxHeaders/hfp.c0000644000000000000000000000005014015011623014410 xustar0020 atime=1743516867 20 ctime=1743591281 bluez-5.82/emulator/hfp.c0000644000000000000000000000352114015011623014072 0ustar00rootroot// SPDX-License-Identifier: GPL-2.0-or-later /* * * BlueZ - Bluetooth protocol stack for Linux * * Copyright (C) 2011-2012 Intel Corporation * Copyright (C) 2004-2010 Marcel Holtmann * * */ #ifdef HAVE_CONFIG_H #include #endif #include #include #include #include #include #include #include #include "src/shared/mainloop.h" #include "src/shared/hfp.h" static void hfp_debug(const char *str, void *user_data) { const char *prefix = user_data; printf("%s%s\n", prefix, str); } static void command_handler(const char *command, void *user_data) { struct hfp_gw *hfp = user_data; printf("Command: %s\n", command); hfp_gw_send_result(hfp, HFP_RESULT_ERROR); } static bool open_connection(void) { static const char SOCKET_PATH[] = "\0hfp-headset"; struct hfp_gw *hfp; struct sockaddr_un addr; int fd; fd = socket(PF_LOCAL, SOCK_STREAM | SOCK_CLOEXEC, 0); if (fd < 0) { perror("Failed to create Unix socket"); return false; } memset(&addr, 0, sizeof(addr)); addr.sun_family = AF_UNIX; memcpy(addr.sun_path, SOCKET_PATH, sizeof(SOCKET_PATH)); if (connect(fd, (struct sockaddr *) &addr, sizeof(addr)) < 0) { perror("Failed to connect Unix socket"); close(fd); return false; } hfp = hfp_gw_new(fd); if (!hfp) { close(fd); return false; } hfp_gw_set_close_on_unref(hfp, true); hfp_gw_set_debug(hfp, hfp_debug, "HFP: ", NULL); hfp_gw_set_command_handler(hfp, command_handler, hfp, NULL); return true; } static void signal_callback(int signum, void *user_data) { switch (signum) { case SIGINT: case SIGTERM: mainloop_quit(); break; } } int main(int argc, char *argv[]) { mainloop_init(); if (!open_connection()) return EXIT_FAILURE; return mainloop_run_with_signal(signal_callback, NULL); } bluez-5.82/emulator/PaxHeaders/hciemu.h0000644000000000000000000000005014766002272015127 xustar0020 atime=1743515578 20 ctime=1743591278 bluez-5.82/emulator/hciemu.h0000644000000000000000000000553514766002272014620 0ustar00rootroot/* SPDX-License-Identifier: LGPL-2.1-or-later */ /* * * BlueZ - Bluetooth protocol stack for Linux * * Copyright (C) 2012-2014 Intel Corporation. All rights reserved. * * */ #include #include struct hciemu; struct hciemu_client; enum hciemu_type { HCIEMU_TYPE_BREDRLE, HCIEMU_TYPE_BREDR, HCIEMU_TYPE_LE, HCIEMU_TYPE_LEGACY, HCIEMU_TYPE_BREDRLE50, HCIEMU_TYPE_BREDRLE52, }; enum hciemu_hook_type { HCIEMU_HOOK_PRE_CMD, HCIEMU_HOOK_POST_CMD, HCIEMU_HOOK_PRE_EVT, HCIEMU_HOOK_POST_EVT, }; struct hciemu *hciemu_new(enum hciemu_type type); struct hciemu *hciemu_new_num(enum hciemu_type type, uint8_t num); struct hciemu *hciemu_ref(struct hciemu *hciemu); void hciemu_unref(struct hciemu *hciemu); struct hciemu_client *hciemu_get_client(struct hciemu *hciemu, int num); struct bthost *hciemu_client_host(struct hciemu_client *client); const uint8_t *hciemu_client_bdaddr(struct hciemu_client *client); bool hciemu_set_client_bdaddr(struct hciemu_client *client, const uint8_t *bdaddr); typedef void (*hciemu_debug_func_t)(const char *str, void *user_data); typedef void (*hciemu_destroy_func_t)(void *user_data); bool hciemu_set_debug(struct hciemu *hciemu, hciemu_debug_func_t callback, void *user_data, hciemu_destroy_func_t destroy); struct vhci *hciemu_get_vhci(struct hciemu *hciemu); struct bthost *hciemu_client_get_host(struct hciemu *hciemu); /* Process pending client events before new VHCI events */ void hciemu_flush_client_events(struct hciemu *hciemu); const char *hciemu_get_address(struct hciemu *hciemu); uint8_t *hciemu_get_features(struct hciemu *hciemu); uint8_t *hciemu_get_commands(struct hciemu *hciemu); const uint8_t *hciemu_get_central_bdaddr(struct hciemu *hciemu); const uint8_t *hciemu_get_client_bdaddr(struct hciemu *hciemu); uint8_t hciemu_get_central_scan_enable(struct hciemu *hciemu); uint8_t hciemu_get_central_le_scan_enable(struct hciemu *hciemu); void hciemu_set_central_le_states(struct hciemu *hciemu, const uint8_t *le_states); void hciemu_set_central_le_al_len(struct hciemu *hciemu, uint8_t len); void hciemu_set_central_le_rl_len(struct hciemu *hciemu, uint8_t len); const uint8_t *hciemu_get_central_adv_addr(struct hciemu *hciemu, uint8_t handle); typedef void (*hciemu_command_func_t)(uint16_t opcode, const void *data, uint8_t len, void *user_data); typedef bool (*hciemu_hook_func_t)(const void *data, uint16_t len, void *user_data); bool hciemu_add_central_post_command_hook(struct hciemu *hciemu, hciemu_command_func_t function, void *user_data); bool hciemu_clear_central_post_command_hooks(struct hciemu *hciemu); int hciemu_add_hook(struct hciemu *hciemu, enum hciemu_hook_type type, uint16_t opcode, hciemu_hook_func_t function, void *user_data); bool hciemu_del_hook(struct hciemu *hciemu, enum hciemu_hook_type type, uint16_t opcode); bluez-5.82/emulator/PaxHeaders/smp.c0000644000000000000000000000005014131623652014444 xustar0020 atime=1743516864 20 ctime=1743591278 bluez-5.82/emulator/smp.c0000644000000000000000000005054714131623652014140 0ustar00rootroot// SPDX-License-Identifier: LGPL-2.1-or-later /* * * BlueZ - Bluetooth protocol stack for Linux * * Copyright (C) 2013-2014 Intel Corporation * * */ #ifdef HAVE_CONFIG_H #include #endif #include #include #include #include #include #include #include #include #include #include "lib/bluetooth.h" #include "lib/hci.h" #include "src/shared/util.h" #include "src/shared/crypto.h" #include "src/shared/ecc.h" #include "monitor/bt.h" #include "bthost.h" #define SMP_CID 0x0006 #define SMP_BREDR_CID 0x0007 #define L2CAP_FC_SMP_BREDR 0x80 #define SMP_PASSKEY_ENTRY_FAILED 0x01 #define SMP_OOB_NOT_AVAIL 0x02 #define SMP_AUTH_REQUIREMENTS 0x03 #define SMP_CONFIRM_FAILED 0x04 #define SMP_PAIRING_NOTSUPP 0x05 #define SMP_ENC_KEY_SIZE 0x06 #define SMP_CMD_NOTSUPP 0x07 #define SMP_UNSPECIFIED 0x08 #define SMP_REPEATED_ATTEMPTS 0x09 #define SMP_INVALID_PARAMS 0x0a #define SMP_DHKEY_CHECK_FAILED 0x0b #define SMP_NUMERIC_COMP_FAILED 0x0c #define SMP_BREDR_PAIRING_IN_PROGRESS 0x0d #define DIST_ENC_KEY 0x01 #define DIST_ID_KEY 0x02 #define DIST_SIGN 0x04 #define DIST_LINK_KEY 0x08 #define SC_NO_DIST (DIST_ENC_KEY | DIST_LINK_KEY) #define MAX_IO_CAP 0x04 #define SMP_AUTH_NONE 0x00 #define SMP_AUTH_BONDING 0x01 #define SMP_AUTH_MITM 0x04 #define SMP_AUTH_SC 0x08 #define SMP_AUTH_KEYPRESS 0x10 struct smp { struct bthost *bthost; struct smp_conn *conn; struct bt_crypto *crypto; }; struct smp_conn { struct smp *smp; uint16_t handle; uint8_t addr_type; bool out; bool sc; bool initiator; uint8_t method; uint8_t local_key_dist; uint8_t remote_key_dist; uint8_t ia[6]; uint8_t ia_type; uint8_t ra[6]; uint8_t ra_type; uint8_t tk[16]; uint8_t prnd[16]; uint8_t rrnd[16]; uint8_t pcnf[16]; uint8_t preq[7]; uint8_t prsp[7]; uint8_t ltk[16]; uint8_t local_sk[32]; uint8_t local_pk[64]; uint8_t remote_pk[64]; uint8_t dhkey[32]; uint8_t mackey[16]; uint8_t passkey_notify; uint8_t passkey_round; }; enum { JUST_WORKS, JUST_CFM, REQ_PASSKEY, CFM_PASSKEY, REQ_OOB, DSP_PASSKEY, OVERLAP, }; static const uint8_t gen_method[5][5] = { { JUST_WORKS, JUST_CFM, REQ_PASSKEY, JUST_WORKS, REQ_PASSKEY }, { JUST_WORKS, JUST_CFM, REQ_PASSKEY, JUST_WORKS, REQ_PASSKEY }, { CFM_PASSKEY, CFM_PASSKEY, REQ_PASSKEY, JUST_WORKS, CFM_PASSKEY }, { JUST_WORKS, JUST_CFM, JUST_WORKS, JUST_WORKS, JUST_CFM }, { CFM_PASSKEY, CFM_PASSKEY, REQ_PASSKEY, JUST_WORKS, OVERLAP }, }; static const uint8_t sc_method[5][5] = { { JUST_WORKS, JUST_CFM, REQ_PASSKEY, JUST_WORKS, REQ_PASSKEY }, { JUST_WORKS, CFM_PASSKEY, REQ_PASSKEY, JUST_WORKS, CFM_PASSKEY }, { DSP_PASSKEY, DSP_PASSKEY, REQ_PASSKEY, JUST_WORKS, DSP_PASSKEY }, { JUST_WORKS, JUST_CFM, JUST_WORKS, JUST_WORKS, JUST_CFM }, { DSP_PASSKEY, CFM_PASSKEY, REQ_PASSKEY, JUST_WORKS, CFM_PASSKEY }, }; static uint8_t get_auth_method(struct smp_conn *conn, uint8_t local_io, uint8_t remote_io) { /* If either side has unknown io_caps, use JUST_CFM (which gets * converted later to JUST_WORKS if we're initiators. */ if (local_io > MAX_IO_CAP || remote_io > MAX_IO_CAP) return JUST_CFM; if (conn->sc) return sc_method[remote_io][local_io]; return gen_method[remote_io][local_io]; } static uint8_t sc_select_method(struct smp_conn *conn) { struct bt_l2cap_smp_pairing_request *local, *remote; uint8_t local_mitm, remote_mitm, local_io, remote_io, method; if (conn->out) { local = (void *) &conn->preq[1]; remote = (void *) &conn->prsp[1]; } else { local = (void *) &conn->prsp[1]; remote = (void *) &conn->preq[1]; } local_io = local->io_capa; remote_io = remote->io_capa; local_mitm = (local->auth_req & SMP_AUTH_MITM); remote_mitm = (remote->auth_req & SMP_AUTH_MITM); /* If either side wants MITM, look up the method from the table, * otherwise use JUST WORKS. */ if (local_mitm || remote_mitm) method = get_auth_method(conn, local_io, remote_io); else method = JUST_WORKS; /* Don't confirm locally initiated pairing attempts */ if (method == JUST_CFM && conn->initiator) method = JUST_WORKS; return method; } static uint8_t key_dist(struct bthost *host) { if (!bthost_bredr_capable(host)) return (DIST_ENC_KEY | DIST_ID_KEY | DIST_SIGN); return (DIST_ENC_KEY | DIST_ID_KEY | DIST_SIGN | DIST_LINK_KEY); } static void smp_send(struct smp_conn *conn, uint8_t smp_cmd, const void *data, uint8_t len) { struct iovec iov[2]; uint16_t cid; iov[0].iov_base = &smp_cmd; iov[0].iov_len = 1; iov[1].iov_base = (void *) data; iov[1].iov_len = len; if (conn->addr_type == BDADDR_BREDR) cid = SMP_BREDR_CID; else cid = SMP_CID; bthost_send_cid_v(conn->smp->bthost, conn->handle, cid, iov, 2); } static bool send_public_key(struct smp_conn *conn) { if (!ecc_make_key(conn->local_pk, conn->local_sk)) return false; smp_send(conn, BT_L2CAP_SMP_PUBLIC_KEY, conn->local_pk, 64); return true; } static void sc_dhkey_check(struct smp_conn *conn) { uint8_t io_cap[3], r[16], a[7], b[7], *local_addr, *remote_addr; struct bt_l2cap_smp_dhkey_check check; memcpy(a, conn->ia, 6); memcpy(b, conn->ra, 6); a[6] = conn->ia_type; b[6] = conn->ra_type; if (conn->out) { local_addr = a; remote_addr = b; memcpy(io_cap, &conn->preq[1], 3); } else { local_addr = b; remote_addr = a; memcpy(io_cap, &conn->prsp[1], 3); } memset(r, 0, sizeof(r)); bt_crypto_f6(conn->smp->crypto, conn->mackey, conn->prnd, conn->rrnd, r, io_cap, local_addr, remote_addr, check.e); smp_send(conn, BT_L2CAP_SMP_DHKEY_CHECK, &check, sizeof(check)); } static void sc_mackey_and_ltk(struct smp_conn *conn) { uint8_t *na, *nb, a[7], b[7]; if (conn->out) { na = conn->prnd; nb = conn->rrnd; } else { na = conn->rrnd; nb = conn->prnd; } memcpy(a, conn->ia, 6); memcpy(b, conn->ra, 6); a[6] = conn->ia_type; b[6] = conn->ra_type; bt_crypto_f5(conn->smp->crypto, conn->dhkey, na, nb, a, b, conn->mackey, conn->ltk); } static uint8_t sc_passkey_send_confirm(struct smp_conn *conn) { struct bt_l2cap_smp_pairing_confirm cfm; uint8_t r; r = ((conn->passkey_notify >> conn->passkey_round) & 0x01); r |= 0x80; if (!bt_crypto_f4(conn->smp->crypto, conn->local_pk, conn->remote_pk, conn->prnd, r, cfm.value)) return SMP_UNSPECIFIED; smp_send(conn, BT_L2CAP_SMP_PAIRING_CONFIRM, &cfm, sizeof(cfm)); return 0; } static uint8_t sc_passkey_round(struct smp_conn *conn, uint8_t smp_op) { uint8_t cfm[16], r; /* Ignore the PDU if we've already done 20 rounds (0 - 19) */ if (conn->passkey_round >= 20) return 0; switch (smp_op) { case BT_L2CAP_SMP_PAIRING_RANDOM: r = ((conn->passkey_notify >> conn->passkey_round) & 0x01); r |= 0x80; if (!bt_crypto_f4(conn->smp->crypto, conn->remote_pk, conn->local_pk, conn->rrnd, r, cfm)) return SMP_UNSPECIFIED; if (memcmp(conn->pcnf, cfm, 16)) return SMP_CONFIRM_FAILED; conn->passkey_round++; if (conn->passkey_round == 20) { /* Generate MacKey and LTK */ sc_mackey_and_ltk(conn); } /* The round is only complete when the initiator * receives pairing random. */ if (!conn->out) { smp_send(conn, BT_L2CAP_SMP_PAIRING_RANDOM, conn->prnd, sizeof(conn->prnd)); return 0; } /* Start the next round */ if (conn->passkey_round != 20) return sc_passkey_round(conn, 0); /* Passkey rounds are complete - start DHKey Check */ sc_dhkey_check(conn); break; case BT_L2CAP_SMP_PAIRING_CONFIRM: if (conn->out) { smp_send(conn, BT_L2CAP_SMP_PAIRING_RANDOM, conn->prnd, sizeof(conn->prnd)); return 0; } return sc_passkey_send_confirm(conn); case BT_L2CAP_SMP_PUBLIC_KEY: default: /* Initiating device starts the round */ if (!conn->out) return 0; return sc_passkey_send_confirm(conn); } return 0; } static bool verify_random(struct smp_conn *conn, const uint8_t rnd[16]) { uint8_t confirm[16]; if (!bt_crypto_c1(conn->smp->crypto, conn->tk, conn->rrnd, conn->prsp, conn->preq, conn->ia_type, conn->ia, conn->ra_type, conn->ra, confirm)) return false; if (memcmp(conn->pcnf, confirm, sizeof(conn->pcnf)) != 0) { bthost_debug(conn->smp->bthost, "Confirmation values don't match"); return false; } if (conn->out) { bt_crypto_s1(conn->smp->crypto, conn->tk, conn->rrnd, conn->prnd, conn->ltk); bthost_le_start_encrypt(conn->smp->bthost, conn->handle, conn->ltk); } else { bt_crypto_s1(conn->smp->crypto, conn->tk, conn->prnd, conn->rrnd, conn->ltk); } return true; } static void distribute_keys(struct smp_conn *conn) { uint8_t buf[16]; if (conn->local_key_dist & DIST_ENC_KEY) { memset(buf, 0, sizeof(buf)); smp_send(conn, BT_L2CAP_SMP_ENCRYPT_INFO, buf, sizeof(buf)); smp_send(conn, BT_L2CAP_SMP_CENTRAL_IDENT, buf, 10); } if (conn->local_key_dist & DIST_ID_KEY) { memset(buf, 0, sizeof(buf)); smp_send(conn, BT_L2CAP_SMP_IDENT_INFO, buf, sizeof(buf)); memset(buf, 0, sizeof(buf)); if (conn->out) { buf[0] = conn->ia_type; memcpy(&buf[1], conn->ia, 6); } else { buf[0] = conn->ra_type; memcpy(&buf[1], conn->ra, 6); } smp_send(conn, BT_L2CAP_SMP_IDENT_ADDR_INFO, buf, 7); } if (conn->local_key_dist & DIST_SIGN) { memset(buf, 0, sizeof(buf)); smp_send(conn, BT_L2CAP_SMP_SIGNING_INFO, buf, sizeof(buf)); } } static void pairing_req(struct smp_conn *conn, const void *data, uint16_t len) { struct bthost *bthost = conn->smp->bthost; struct bt_l2cap_smp_pairing_response rsp; memcpy(conn->preq, data, sizeof(conn->preq)); if (conn->addr_type == BDADDR_BREDR) { rsp.io_capa = 0x00; rsp.oob_data = 0x00; rsp.auth_req = 0x00; } else { rsp.io_capa = bthost_get_io_capability(bthost); rsp.oob_data = 0x00; rsp.auth_req = bthost_get_auth_req(bthost); } rsp.max_key_size = 0x10; rsp.init_key_dist = conn->preq[5] & key_dist(bthost); rsp.resp_key_dist = conn->preq[6] & key_dist(bthost); conn->prsp[0] = BT_L2CAP_SMP_PAIRING_RESPONSE; memcpy(&conn->prsp[1], &rsp, sizeof(rsp)); conn->local_key_dist = rsp.resp_key_dist; conn->remote_key_dist = rsp.init_key_dist; if (((conn->prsp[3] & 0x08) && (conn->preq[3] & 0x08)) || conn->addr_type == BDADDR_BREDR) { conn->sc = true; conn->local_key_dist &= ~SC_NO_DIST; conn->remote_key_dist &= ~SC_NO_DIST; } smp_send(conn, BT_L2CAP_SMP_PAIRING_RESPONSE, &rsp, sizeof(rsp)); if (conn->addr_type == BDADDR_BREDR) distribute_keys(conn); } static void pairing_rsp(struct smp_conn *conn, const void *data, uint16_t len) { struct smp *smp = conn->smp; uint8_t cfm[16]; memcpy(conn->prsp, data, sizeof(conn->prsp)); conn->local_key_dist = conn->prsp[5]; conn->remote_key_dist = conn->prsp[6]; if (conn->addr_type == BDADDR_BREDR) { conn->local_key_dist &= ~SC_NO_DIST; conn->remote_key_dist &= ~SC_NO_DIST; distribute_keys(conn); return; } if (((conn->prsp[3] & 0x08) && (conn->preq[3] & 0x08)) || conn->addr_type == BDADDR_BREDR) { conn->sc = true; conn->local_key_dist &= ~SC_NO_DIST; conn->remote_key_dist &= ~SC_NO_DIST; if (conn->addr_type == BDADDR_BREDR) distribute_keys(conn); else send_public_key(conn); return; } bt_crypto_c1(smp->crypto, conn->tk, conn->prnd, conn->prsp, conn->preq, conn->ia_type, conn->ia, conn->ra_type, conn->ra, cfm); smp_send(conn, BT_L2CAP_SMP_PAIRING_CONFIRM, cfm, sizeof(cfm)); } static void sc_check_confirm(struct smp_conn *conn) { if (conn->method == REQ_PASSKEY || conn->method == DSP_PASSKEY) { sc_passkey_round(conn, BT_L2CAP_SMP_PAIRING_CONFIRM); return; } if (conn->out) smp_send(conn, BT_L2CAP_SMP_PAIRING_RANDOM, conn->prnd, sizeof(conn->prnd)); } static void pairing_cfm(struct smp_conn *conn, const void *data, uint16_t len) { uint8_t rsp[16]; memcpy(conn->pcnf, data + 1, 16); if (conn->sc) { sc_check_confirm(conn); return; } if (conn->out) { memset(rsp, 0, sizeof(rsp)); smp_send(conn, BT_L2CAP_SMP_PAIRING_RANDOM, conn->prnd, sizeof(conn->prnd)); } else { bt_crypto_c1(conn->smp->crypto, conn->tk, conn->prnd, conn->prsp, conn->preq, conn->ia_type, conn->ia, conn->ra_type, conn->ra, rsp); smp_send(conn, BT_L2CAP_SMP_PAIRING_CONFIRM, rsp, sizeof(rsp)); } } static uint8_t sc_random(struct smp_conn *conn) { /* Passkey entry has special treatment */ if (conn->method == REQ_PASSKEY || conn->method == DSP_PASSKEY) return sc_passkey_round(conn, BT_L2CAP_SMP_PAIRING_RANDOM); if (conn->out) { uint8_t cfm[16]; bt_crypto_f4(conn->smp->crypto, conn->remote_pk, conn->local_pk, conn->rrnd, 0, cfm); if (memcmp(conn->pcnf, cfm, 16)) return 0x04; /* Confirm Value Failed */ } else { smp_send(conn, BT_L2CAP_SMP_PAIRING_RANDOM, conn->prnd, 16); } sc_mackey_and_ltk(conn); if (conn->out) sc_dhkey_check(conn); return 0; } static void pairing_rnd(struct smp_conn *conn, const void *data, uint16_t len) { memcpy(conn->rrnd, data + 1, 16); if (conn->sc) { uint8_t reason = sc_random(conn); if (reason) smp_send(conn, BT_L2CAP_SMP_PAIRING_FAILED, &reason, sizeof(reason)); return; } if (!verify_random(conn, data + 1)) return; if (conn->out) return; smp_send(conn, BT_L2CAP_SMP_PAIRING_RANDOM, conn->prnd, sizeof(conn->prnd)); } static void encrypt_info(struct smp_conn *conn, const void *data, uint16_t len) { } static void central_ident(struct smp_conn *conn, const void *data, uint16_t len) { conn->remote_key_dist &= ~DIST_ENC_KEY; if (conn->out && !conn->remote_key_dist) distribute_keys(conn); } static void ident_addr_info(struct smp_conn *conn, const void *data, uint16_t len) { } static void ident_info(struct smp_conn *conn, const void *data, uint16_t len) { conn->remote_key_dist &= ~DIST_ID_KEY; if (conn->out && !conn->remote_key_dist) distribute_keys(conn); } static void signing_info(struct smp_conn *conn, const void *data, uint16_t len) { conn->remote_key_dist &= ~DIST_SIGN; if (conn->out && !conn->remote_key_dist) distribute_keys(conn); } static void public_key(struct smp_conn *conn, const void *data, uint16_t len) { struct smp *smp = conn->smp; uint8_t buf[16]; memcpy(conn->remote_pk, data + 1, 64); if (!conn->out) { if (!send_public_key(conn)) return; } if (!ecdh_shared_secret(conn->remote_pk, conn->local_sk, conn->dhkey)) return; conn->method = sc_select_method(conn); if (conn->method == DSP_PASSKEY || conn->method == REQ_PASSKEY) { sc_passkey_round(conn, BT_L2CAP_SMP_PUBLIC_KEY); return; } if (conn->out) return; if (!bt_crypto_f4(smp->crypto, conn->local_pk, conn->remote_pk, conn->prnd, 0, buf)) return; smp_send(conn, BT_L2CAP_SMP_PAIRING_CONFIRM, buf, sizeof(buf)); } static void dhkey_check(struct smp_conn *conn, const void *data, uint16_t len) { const struct bt_l2cap_smp_dhkey_check *cmd = data + 1; uint8_t a[7], b[7], *local_addr, *remote_addr; uint8_t io_cap[3], r[16], e[16]; memcpy(a, &conn->ia, 6); memcpy(b, &conn->ra, 6); a[6] = conn->ia_type; b[6] = conn->ra_type; if (conn->out) { local_addr = a; remote_addr = b; memcpy(io_cap, &conn->prsp[1], 3); } else { local_addr = b; remote_addr = a; memcpy(io_cap, &conn->preq[1], 3); } memset(r, 0, sizeof(r)); if (conn->method == REQ_PASSKEY || conn->method == DSP_PASSKEY) put_le32(conn->passkey_notify, r); if (!bt_crypto_f6(conn->smp->crypto, conn->mackey, conn->rrnd, conn->prnd, r, io_cap, remote_addr, local_addr, e)) return; if (memcmp(cmd->e, e, 16)) { uint8_t reason = 0x0b; /* DHKey Check Failed */ smp_send(conn, BT_L2CAP_SMP_PAIRING_FAILED, &reason, sizeof(reason)); } if (conn->out) bthost_le_start_encrypt(conn->smp->bthost, conn->handle, conn->ltk); else sc_dhkey_check(conn); } void smp_pair(void *conn_data, uint8_t io_cap, uint8_t auth_req) { struct smp_conn *conn = conn_data; struct bt_l2cap_smp_pairing_request req; req.io_capa = io_cap; req.oob_data = 0x00; req.auth_req = auth_req; req.max_key_size = 0x10; req.init_key_dist = key_dist(conn->smp->bthost); req.resp_key_dist = key_dist(conn->smp->bthost); conn->preq[0] = BT_L2CAP_SMP_PAIRING_REQUEST; memcpy(&conn->preq[1], &req, sizeof(req)); smp_send(conn, BT_L2CAP_SMP_PAIRING_REQUEST, &req, sizeof(req)); } void smp_data(void *conn_data, const void *data, uint16_t len) { struct smp_conn *conn = conn_data; uint8_t opcode; if (len < 1) { bthost_debug(conn->smp->bthost, "Received too small SMP PDU"); return; } if (conn->addr_type == BDADDR_BREDR) { bthost_debug(conn->smp->bthost, "Received BR/EDR SMP data on LE link"); return; } opcode = *((const uint8_t *) data); switch (opcode) { case BT_L2CAP_SMP_PAIRING_REQUEST: pairing_req(conn, data, len); break; case BT_L2CAP_SMP_PAIRING_RESPONSE: pairing_rsp(conn, data, len); break; case BT_L2CAP_SMP_PAIRING_CONFIRM: pairing_cfm(conn, data, len); break; case BT_L2CAP_SMP_PAIRING_RANDOM: pairing_rnd(conn, data, len); break; case BT_L2CAP_SMP_ENCRYPT_INFO: encrypt_info(conn, data, len); break; case BT_L2CAP_SMP_CENTRAL_IDENT: central_ident(conn, data, len); break; case BT_L2CAP_SMP_IDENT_ADDR_INFO: ident_addr_info(conn, data, len); break; case BT_L2CAP_SMP_IDENT_INFO: ident_info(conn, data, len); break; case BT_L2CAP_SMP_SIGNING_INFO: signing_info(conn, data, len); break; case BT_L2CAP_SMP_PUBLIC_KEY: public_key(conn, data, len); break; case BT_L2CAP_SMP_DHKEY_CHECK: dhkey_check(conn, data, len); break; default: break; } } void smp_bredr_data(void *conn_data, const void *data, uint16_t len) { struct smp_conn *conn = conn_data; uint8_t opcode; if (len < 1) { bthost_debug(conn->smp->bthost, "Received too small SMP PDU"); return; } if (conn->addr_type != BDADDR_BREDR) { bthost_debug(conn->smp->bthost, "Received LE SMP data on BR/EDR link"); return; } opcode = *((const uint8_t *) data); switch (opcode) { case BT_L2CAP_SMP_PAIRING_REQUEST: pairing_req(conn, data, len); break; case BT_L2CAP_SMP_PAIRING_RESPONSE: pairing_rsp(conn, data, len); break; default: break; } } int smp_get_ltk(void *smp_data, uint64_t rand, uint16_t ediv, uint8_t *ltk) { struct smp_conn *conn = smp_data; static const uint8_t no_ltk[16] = { 0 }; if (!memcmp(conn->ltk, no_ltk, 16)) return -ENOENT; memcpy(ltk, conn->ltk, 16); return 0; } static void smp_conn_bredr(struct smp_conn *conn, uint8_t encrypt) { struct smp *smp = conn->smp; struct bt_l2cap_smp_pairing_request req; uint64_t fixed_chan; if (encrypt != 0x02) return; conn->sc = true; if (!conn->out) return; fixed_chan = bthost_conn_get_fixed_chan(smp->bthost, conn->handle); if (!(fixed_chan & L2CAP_FC_SMP_BREDR)) return; memset(&req, 0, sizeof(req)); req.max_key_size = 0x10; req.init_key_dist = key_dist(smp->bthost); req.resp_key_dist = key_dist(smp->bthost); smp_send(conn, BT_L2CAP_SMP_PAIRING_REQUEST, &req, sizeof(req)); } void smp_conn_encrypted(void *conn_data, uint8_t encrypt) { struct smp_conn *conn = conn_data; if (!encrypt) return; if (conn->addr_type == BDADDR_BREDR) { smp_conn_bredr(conn, encrypt); return; } if (conn->out && conn->remote_key_dist) return; distribute_keys(conn); } static uint8_t type2hci(uint8_t addr_type) { switch (addr_type) { case BDADDR_BREDR: case BDADDR_LE_PUBLIC: return LE_PUBLIC_ADDRESS; case BDADDR_LE_RANDOM: return LE_RANDOM_ADDRESS; } return 0x00; } void *smp_conn_add(void *smp_data, uint16_t handle, const uint8_t *ia, uint8_t ia_type, const uint8_t *ra, uint8_t ra_type, bool conn_init) { struct smp *smp = smp_data; struct smp_conn *conn; char ia_str[18], ra_str[18]; conn = malloc(sizeof(struct smp_conn)); if (!conn) return NULL; memset(conn, 0, sizeof(*conn)); conn->smp = smp; conn->handle = handle; conn->out = conn_init; conn->addr_type = conn_init ? ia_type : ra_type; conn->ia_type = type2hci(ia_type); conn->ra_type = type2hci(ra_type); memcpy(conn->ia, ia, 6); memcpy(conn->ra, ra, 6); ba2str((bdaddr_t *) ia, ia_str); ba2str((bdaddr_t *) ra, ra_str); bthost_debug(smp->bthost, "ia %s type 0x%02x ra %s type 0x%02x", ia_str, ia_type, ra_str, ra_type); bt_crypto_random_bytes(smp->crypto, conn->prnd, sizeof(conn->prnd)); return conn; } void smp_conn_del(void *conn_data) { struct smp_conn *conn = conn_data; free(conn); } void *smp_start(struct bthost *bthost) { struct smp *smp; smp = malloc(sizeof(struct smp)); if (!smp) return NULL; memset(smp, 0, sizeof(*smp)); smp->crypto = bt_crypto_new(); if (!smp->crypto) { free(smp); return NULL; } smp->bthost = bthost; return smp; } void smp_stop(void *smp_data) { struct smp *smp = smp_data; bt_crypto_unref(smp->crypto); free(smp); } bluez-5.82/emulator/PaxHeaders/le.h0000644000000000000000000000005014015011623014240 xustar0020 atime=1743516867 20 ctime=1743591281 bluez-5.82/emulator/le.h0000644000000000000000000000057214015011623013725 0ustar00rootroot/* SPDX-License-Identifier: LGPL-2.1-or-later */ /* * * BlueZ - Bluetooth protocol stack for Linux * * Copyright (C) 2011-2012 Intel Corporation * Copyright (C) 2004-2010 Marcel Holtmann * * */ #include struct bt_le; struct bt_le *bt_le_new(void); struct bt_le *bt_le_ref(struct bt_le *le); void bt_le_unref(struct bt_le *le); bluez-5.82/emulator/PaxHeaders/server.c0000644000000000000000000000005014015011623015141 xustar0020 atime=1743516867 20 ctime=1743591281 bluez-5.82/emulator/server.c0000644000000000000000000001625014015011623014626 0ustar00rootroot// SPDX-License-Identifier: LGPL-2.1-or-later /* * * BlueZ - Bluetooth protocol stack for Linux * * Copyright (C) 2011-2014 Intel Corporation * Copyright (C) 2004-2010 Marcel Holtmann * * */ #ifdef HAVE_CONFIG_H #include #endif #include #include #include #include #include #include #include #include #include #include #include #include #include "lib/bluetooth.h" #include "lib/hci.h" #include "src/shared/mainloop.h" #include "btdev.h" #include "server.h" #define uninitialized_var(x) x = x struct server { enum server_type type; uint16_t id; int fd; }; struct client { int fd; struct btdev *btdev; uint8_t *pkt_data; uint8_t pkt_type; uint16_t pkt_expect; uint16_t pkt_len; uint16_t pkt_offset; }; static void server_destroy(void *user_data) { struct server *server = user_data; close(server->fd); free(server); } static void client_destroy(void *user_data) { struct client *client = user_data; btdev_destroy(client->btdev); close(client->fd); free(client); } static void client_write_callback(const struct iovec *iov, int iovlen, void *user_data) { struct client *client = user_data; struct msghdr msg; ssize_t written; memset(&msg, 0, sizeof(msg)); msg.msg_iov = (struct iovec *) iov; msg.msg_iovlen = iovlen; written = sendmsg(client->fd, &msg, MSG_DONTWAIT); if (written < 0) return; } static void client_read_callback(int fd, uint32_t events, void *user_data) { struct client *client = user_data; static uint8_t buf[4096]; uint8_t *ptr = buf; ssize_t len; uint16_t count; if (events & (EPOLLERR | EPOLLHUP)) { mainloop_remove_fd(client->fd); return; } again: len = recv(fd, buf + client->pkt_offset, sizeof(buf) - client->pkt_offset, MSG_DONTWAIT); if (len < 0) { if (errno == EAGAIN) goto again; return; } if (!client->btdev) return; count = client->pkt_offset + len; while (count > 0) { hci_command_hdr *cmd_hdr; hci_acl_hdr *acl_hdr; if (!client->pkt_data) { client->pkt_type = ptr[0]; switch (client->pkt_type) { case HCI_COMMAND_PKT: if (count < HCI_COMMAND_HDR_SIZE + 1) { client->pkt_offset += len; return; } cmd_hdr = (hci_command_hdr *) (ptr + 1); client->pkt_expect = HCI_COMMAND_HDR_SIZE + cmd_hdr->plen + 1; client->pkt_data = malloc(client->pkt_expect); client->pkt_len = 0; break; case HCI_ACLDATA_PKT: acl_hdr = (hci_acl_hdr*)(ptr + 1); client->pkt_expect = HCI_ACL_HDR_SIZE + acl_hdr->dlen + 1; client->pkt_data = malloc(client->pkt_expect); client->pkt_len = 0; break; default: printf("packet error\n"); return; } client->pkt_offset = 0; } if (count >= client->pkt_expect) { memcpy(client->pkt_data + client->pkt_len, ptr, client->pkt_expect); ptr += client->pkt_expect; count -= client->pkt_expect; btdev_receive_h4(client->btdev, client->pkt_data, client->pkt_len + client->pkt_expect); free(client->pkt_data); client->pkt_data = NULL; } else { memcpy(client->pkt_data + client->pkt_len, ptr, count); client->pkt_len += count; client->pkt_expect -= count; count = 0; } } } static int accept_client(int fd) { struct sockaddr_un addr; socklen_t len; int nfd; memset(&addr, 0, sizeof(addr)); len = sizeof(addr); if (getsockname(fd, (struct sockaddr *) &addr, &len) < 0) { perror("Failed to get socket name"); return -1; } printf("Request for %s\n", addr.sun_path); nfd = accept(fd, (struct sockaddr *) &addr, &len); if (nfd < 0) { perror("Failed to accept client socket"); return -1; } return nfd; } static void server_accept_callback(int fd, uint32_t events, void *user_data) { struct server *server = user_data; struct client *client; enum btdev_type uninitialized_var(type); if (events & (EPOLLERR | EPOLLHUP)) { mainloop_remove_fd(server->fd); return; } client = malloc(sizeof(*client)); if (!client) return; memset(client, 0, sizeof(*client)); client->fd = accept_client(server->fd); if (client->fd < 0) { free(client); return; } switch (server->type) { case SERVER_TYPE_BREDRLE: type = BTDEV_TYPE_BREDRLE; break; case SERVER_TYPE_BREDR: type = BTDEV_TYPE_BREDR; break; case SERVER_TYPE_LE: type = BTDEV_TYPE_LE; break; case SERVER_TYPE_AMP: type = BTDEV_TYPE_AMP; break; case SERVER_TYPE_MONITOR: goto done; } client->btdev = btdev_create(type, server->id); if (!client->btdev) { close(client->fd); free(client); return; } btdev_set_send_handler(client->btdev, client_write_callback, client); done: if (mainloop_add_fd(client->fd, EPOLLIN, client_read_callback, client, client_destroy) < 0) { btdev_destroy(client->btdev); close(client->fd); free(client); } } static int open_unix(const char *path) { struct sockaddr_un addr; int fd; unlink(path); fd = socket(PF_UNIX, SOCK_STREAM, 0); if (fd < 0) { perror("Failed to open server socket"); return -1; } memset(&addr, 0, sizeof(addr)); addr.sun_family = AF_UNIX; strcpy(addr.sun_path, path); if (bind(fd, (struct sockaddr *) &addr, sizeof(addr)) < 0) { perror("Failed to bind server socket"); close(fd); return -1; } if (listen(fd, 5) < 0) { perror("Failed to listen server socket"); close(fd); return -1; } return fd; } struct server *server_open_unix(enum server_type type, const char *path) { struct server *server; server = malloc(sizeof(*server)); if (!server) return NULL; memset(server, 0, sizeof(*server)); server->type = type; server->id = 0x42; server->fd = open_unix(path); if (server->fd < 0) { free(server); return NULL; } if (mainloop_add_fd(server->fd, EPOLLIN, server_accept_callback, server, server_destroy) < 0) { close(server->fd); free(server); return NULL; } return server; } static int open_tcp(void) { struct sockaddr_in addr; int fd, opt = 1; fd = socket(PF_INET, SOCK_STREAM, 0); if (fd < 0) { perror("Failed to open server socket"); return -1; } if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt)) < 0) { perror("Failed to set socket option"); close(fd); return -1; } memset(&addr, 0, sizeof(addr)); addr.sin_family = AF_INET; addr.sin_addr.s_addr = INADDR_ANY; addr.sin_addr.s_addr = inet_addr("127.0.0.1"); addr.sin_port = htons(45550); if (bind(fd, (struct sockaddr *) &addr, sizeof(addr)) < 0) { perror("Failed to bind server socket"); close(fd); return -1; } if (listen(fd, 5) < 0) { perror("Failed to listen server socket"); close(fd); return -1; } return fd; } struct server *server_open_tcp(enum server_type type) { struct server *server; server = malloc(sizeof(*server)); if (!server) return server; memset(server, 0, sizeof(*server)); server->type = type; server->id = 0x43; server->fd = open_tcp(); if (server->fd < 0) { free(server); return NULL; } if (mainloop_add_fd(server->fd, EPOLLIN, server_accept_callback, server, server_destroy) < 0) { close(server->fd); free(server); return NULL; } return server; } void server_close(struct server *server) { if (!server) return; mainloop_remove_fd(server->fd); } bluez-5.82/emulator/PaxHeaders/bthost.c0000644000000000000000000000005014766002272015153 xustar0020 atime=1743515578 20 ctime=1743591278 bluez-5.82/emulator/bthost.c0000644000000000000000000025607414766002272014652 0ustar00rootroot// SPDX-License-Identifier: LGPL-2.1-or-later /* * * BlueZ - Bluetooth protocol stack for Linux * * Copyright (C) 2011-2012 Intel Corporation * Copyright (C) 2004-2010 Marcel Holtmann * Copyright 2023-2024 NXP * * */ #ifdef HAVE_CONFIG_H #include #endif #define _GNU_SOURCE #include #include #include #include #include #include #include "lib/bluetooth.h" #include "src/shared/util.h" #include "src/shared/tester.h" #include "src/shared/queue.h" #include "src/shared/ad.h" #include "monitor/bt.h" #include "monitor/rfcomm.h" #include "bthost.h" #define lmp_bredr_capable(bthost) (!((bthost)->features[4] & 0x20)) /* ACL handle and flags pack/unpack */ #define acl_handle_pack(h, f) (uint16_t)((h & 0x0fff)|(f << 12)) #define acl_handle(h) (h & 0x0fff) #define acl_flags(h) (h >> 12) #define sco_flags_status(f) (f & 0x03) #define iso_flags_pb(f) (f & 0x0003) #define iso_flags_ts(f) ((f >> 2) & 0x0001) #define iso_flags_pack(pb, ts) (((pb) & 0x03) | (((ts) & 0x01) << 2)) #define iso_data_len_pack(h, f) ((uint16_t) ((h) | ((f) << 14))) #define L2CAP_FEAT_FIXED_CHAN 0x00000080 #define L2CAP_FC_SIG_BREDR 0x02 #define L2CAP_FC_SMP_BREDR 0x80 #define L2CAP_IT_FEAT_MASK 0x0002 #define L2CAP_IT_FIXED_CHAN 0x0003 /* RFCOMM setters */ #define RFCOMM_ADDR(cr, dlci) (((dlci & 0x3f) << 2) | (cr << 1) | 0x01) #define RFCOMM_CTRL(type, pf) (((type & 0xef) | (pf << 4))) #define RFCOMM_LEN8(len) (((len) << 1) | 1) #define RFCOMM_LEN16(len) ((len) << 1) #define RFCOMM_MCC_TYPE(cr, type) (((type << 2) | (cr << 1) | 0x01)) /* RFCOMM FCS calculation */ #define CRC(data) (rfcomm_crc_table[rfcomm_crc_table[0xff ^ data[0]] ^ data[1]]) static const unsigned char rfcomm_crc_table[256] = { 0x00, 0x91, 0xe3, 0x72, 0x07, 0x96, 0xe4, 0x75, 0x0e, 0x9f, 0xed, 0x7c, 0x09, 0x98, 0xea, 0x7b, 0x1c, 0x8d, 0xff, 0x6e, 0x1b, 0x8a, 0xf8, 0x69, 0x12, 0x83, 0xf1, 0x60, 0x15, 0x84, 0xf6, 0x67, 0x38, 0xa9, 0xdb, 0x4a, 0x3f, 0xae, 0xdc, 0x4d, 0x36, 0xa7, 0xd5, 0x44, 0x31, 0xa0, 0xd2, 0x43, 0x24, 0xb5, 0xc7, 0x56, 0x23, 0xb2, 0xc0, 0x51, 0x2a, 0xbb, 0xc9, 0x58, 0x2d, 0xbc, 0xce, 0x5f, 0x70, 0xe1, 0x93, 0x02, 0x77, 0xe6, 0x94, 0x05, 0x7e, 0xef, 0x9d, 0x0c, 0x79, 0xe8, 0x9a, 0x0b, 0x6c, 0xfd, 0x8f, 0x1e, 0x6b, 0xfa, 0x88, 0x19, 0x62, 0xf3, 0x81, 0x10, 0x65, 0xf4, 0x86, 0x17, 0x48, 0xd9, 0xab, 0x3a, 0x4f, 0xde, 0xac, 0x3d, 0x46, 0xd7, 0xa5, 0x34, 0x41, 0xd0, 0xa2, 0x33, 0x54, 0xc5, 0xb7, 0x26, 0x53, 0xc2, 0xb0, 0x21, 0x5a, 0xcb, 0xb9, 0x28, 0x5d, 0xcc, 0xbe, 0x2f, 0xe0, 0x71, 0x03, 0x92, 0xe7, 0x76, 0x04, 0x95, 0xee, 0x7f, 0x0d, 0x9c, 0xe9, 0x78, 0x0a, 0x9b, 0xfc, 0x6d, 0x1f, 0x8e, 0xfb, 0x6a, 0x18, 0x89, 0xf2, 0x63, 0x11, 0x80, 0xf5, 0x64, 0x16, 0x87, 0xd8, 0x49, 0x3b, 0xaa, 0xdf, 0x4e, 0x3c, 0xad, 0xd6, 0x47, 0x35, 0xa4, 0xd1, 0x40, 0x32, 0xa3, 0xc4, 0x55, 0x27, 0xb6, 0xc3, 0x52, 0x20, 0xb1, 0xca, 0x5b, 0x29, 0xb8, 0xcd, 0x5c, 0x2e, 0xbf, 0x90, 0x01, 0x73, 0xe2, 0x97, 0x06, 0x74, 0xe5, 0x9e, 0x0f, 0x7d, 0xec, 0x99, 0x08, 0x7a, 0xeb, 0x8c, 0x1d, 0x6f, 0xfe, 0x8b, 0x1a, 0x68, 0xf9, 0x82, 0x13, 0x61, 0xf0, 0x85, 0x14, 0x66, 0xf7, 0xa8, 0x39, 0x4b, 0xda, 0xaf, 0x3e, 0x4c, 0xdd, 0xa6, 0x37, 0x45, 0xd4, 0xa1, 0x30, 0x42, 0xd3, 0xb4, 0x25, 0x57, 0xc6, 0xb3, 0x22, 0x50, 0xc1, 0xba, 0x2b, 0x59, 0xc8, 0xbd, 0x2c, 0x5e, 0xcf }; static uint8_t rfcomm_fcs2(uint8_t *data) { return 0xff - rfcomm_crc_table[CRC(data) ^ data[2]]; } static uint8_t rfcomm_fcs(uint8_t *data) { return 0xff - CRC(data); } struct cmd { struct cmd *next; struct cmd *prev; uint8_t data[256 + sizeof(struct bt_hci_cmd_hdr)]; uint16_t len; }; struct cmd_queue { struct cmd *head; struct cmd *tail; }; struct cid_hook { uint16_t cid; bthost_cid_hook_func_t func; void *user_data; struct cid_hook *next; }; struct rfcomm_chan_hook { uint8_t channel; bthost_rfcomm_chan_hook_func_t func; void *user_data; struct rfcomm_chan_hook *next; }; struct sco_hook { bthost_sco_hook_func_t func; void *user_data; bthost_destroy_func_t destroy; }; struct iso_hook { bthost_iso_hook_func_t func; void *user_data; bthost_destroy_func_t destroy; }; struct btconn { uint16_t handle; uint8_t bdaddr[6]; uint8_t addr_type; uint8_t encr_mode; uint16_t next_cid; uint64_t fixed_chan; struct l2conn *l2conns; struct rcconn *rcconns; struct cid_hook *cid_hooks; struct rfcomm_chan_hook *rfcomm_chan_hooks; struct sco_hook *sco_hook; struct iso_hook *iso_hook; struct btconn *next; void *smp_data; uint16_t recv_len; uint16_t data_len; void *recv_data; }; enum l2cap_mode { L2CAP_MODE_OTHER, L2CAP_MODE_LE_CRED, L2CAP_MODE_LE_ENH_CRED, }; struct l2conn { struct l2conn *next; uint16_t scid; uint16_t dcid; uint16_t psm; enum l2cap_mode mode; uint16_t data_len; uint16_t recv_len; void *recv_data; }; struct rcconn { uint8_t channel; uint16_t scid; struct rcconn *next; }; struct l2cap_pending_req { uint8_t ident; bthost_l2cap_rsp_cb cb; void *user_data; struct l2cap_pending_req *next; }; struct l2cap_conn_cb_data { uint16_t psm; uint16_t mtu; uint16_t mps; uint16_t credits; bthost_l2cap_connect_cb func; bthost_l2cap_disconnect_cb disconn_func; void *user_data; struct l2cap_conn_cb_data *next; }; struct rfcomm_conn_cb_data { uint8_t channel; bthost_rfcomm_connect_cb func; void *user_data; struct rfcomm_conn_cb_data *next; }; struct rfcomm_connection_data { uint8_t channel; struct btconn *conn; bthost_rfcomm_connect_cb cb; void *user_data; }; struct le_ext_adv { struct bthost *bthost; uint16_t event_type; uint8_t addr_type; uint8_t addr[6]; uint8_t direct_addr_type; uint8_t direct_addr[6]; }; struct bthost { bool ready; bthost_ready_cb ready_cb; uint8_t bdaddr[6]; uint8_t features[8]; bthost_send_func send_handler; void *send_data; struct cmd_queue cmd_q; uint8_t ncmd; struct btconn *conns; bthost_cmd_complete_cb cmd_complete_cb; void *cmd_complete_data; bthost_new_conn_cb new_conn_cb; void *new_conn_data; bthost_new_conn_cb new_sco_cb; void *new_sco_data; bthost_accept_conn_cb accept_iso_cb; bthost_new_conn_cb new_iso_cb; void *new_iso_data; struct rfcomm_connection_data *rfcomm_conn_data; struct l2cap_conn_cb_data *new_l2cap_conn_data; struct rfcomm_conn_cb_data *new_rfcomm_conn_data; struct l2cap_pending_req *l2reqs; uint8_t pin[16]; uint8_t pin_len; uint8_t io_capability; uint8_t auth_req; bool reject_user_confirm; void *smp_data; bool conn_init; bool le; bool sc; struct queue *le_ext_adv; bthost_debug_func_t debug_callback; bthost_destroy_func_t debug_destroy; void *debug_data; }; struct bthost *bthost_create(void) { struct bthost *bthost; bthost = new0(struct bthost, 1); if (!bthost) return NULL; bthost->smp_data = smp_start(bthost); if (!bthost->smp_data) { free(bthost); return NULL; } bthost->le_ext_adv = queue_new(); /* Set defaults */ bthost->io_capability = 0x03; return bthost; } static void l2conn_free(struct l2conn *conn) { free(conn->recv_data); free(conn); } static void btconn_free(struct btconn *conn) { if (conn->smp_data) smp_conn_del(conn->smp_data); while (conn->l2conns) { struct l2conn *l2conn = conn->l2conns; conn->l2conns = l2conn->next; l2conn_free(l2conn); } while (conn->cid_hooks) { struct cid_hook *hook = conn->cid_hooks; conn->cid_hooks = hook->next; free(hook); } while (conn->rcconns) { struct rcconn *rcconn = conn->rcconns; conn->rcconns = rcconn->next; free(rcconn); } while (conn->rfcomm_chan_hooks) { struct rfcomm_chan_hook *hook = conn->rfcomm_chan_hooks; conn->rfcomm_chan_hooks = hook->next; free(hook); } if (conn->sco_hook && conn->sco_hook->destroy) conn->sco_hook->destroy(conn->sco_hook->user_data); if (conn->iso_hook && conn->iso_hook->destroy) conn->iso_hook->destroy(conn->iso_hook->user_data); free(conn->sco_hook); free(conn->iso_hook); free(conn->recv_data); free(conn); } static struct btconn *bthost_find_conn(struct bthost *bthost, uint16_t handle) { struct btconn *conn; for (conn = bthost->conns; conn != NULL; conn = conn->next) { if (conn->handle == handle) return conn; } return NULL; } static struct btconn *bthost_find_conn_by_bdaddr(struct bthost *bthost, const uint8_t *bdaddr) { struct btconn *conn; for (conn = bthost->conns; conn != NULL; conn = conn->next) { if (!memcmp(conn->bdaddr, bdaddr, 6)) return conn; } return NULL; } static struct l2conn *bthost_add_l2cap_conn(struct bthost *bthost, struct btconn *conn, uint16_t scid, uint16_t dcid, uint16_t psm) { struct l2conn *l2conn; l2conn = malloc(sizeof(*l2conn)); if (!l2conn) return NULL; memset(l2conn, 0, sizeof(*l2conn)); l2conn->psm = psm; l2conn->scid = scid; l2conn->dcid = dcid; l2conn->mode = L2CAP_MODE_OTHER; l2conn->next = conn->l2conns; conn->l2conns = l2conn; return l2conn; } static struct rcconn *bthost_add_rfcomm_conn(struct bthost *bthost, struct btconn *conn, struct l2conn *l2conn, uint8_t channel) { struct rcconn *rcconn; rcconn = malloc(sizeof(*rcconn)); if (!rcconn) return NULL; memset(rcconn, 0, sizeof(*rcconn)); rcconn->channel = channel; rcconn->scid = l2conn->scid; rcconn->next = conn->rcconns; conn->rcconns = rcconn; return rcconn; } static struct rcconn *btconn_find_rfcomm_conn_by_channel(struct btconn *conn, uint8_t chan) { struct rcconn *rcconn; for (rcconn = conn->rcconns; rcconn != NULL; rcconn = rcconn->next) { if (rcconn->channel == chan) return rcconn; } return NULL; } static struct l2conn *btconn_find_l2cap_conn_by_scid(struct btconn *conn, uint16_t scid) { struct l2conn *l2conn; for (l2conn = conn->l2conns; l2conn != NULL; l2conn = l2conn->next) { if (l2conn->scid == scid) return l2conn; } return NULL; } static struct l2conn *btconn_find_l2cap_conn_by_dcid(struct btconn *conn, uint16_t dcid) { struct l2conn *l2conn; for (l2conn = conn->l2conns; l2conn != NULL; l2conn = l2conn->next) { if (l2conn->dcid == dcid) return l2conn; } return NULL; } static struct l2cap_conn_cb_data *bthost_find_l2cap_cb_by_psm( struct bthost *bthost, uint16_t psm) { struct l2cap_conn_cb_data *cb; for (cb = bthost->new_l2cap_conn_data; cb != NULL; cb = cb->next) { if (cb->psm == psm) return cb; } return NULL; } static struct rfcomm_conn_cb_data *bthost_find_rfcomm_cb_by_channel( struct bthost *bthost, uint8_t channel) { struct rfcomm_conn_cb_data *cb; for (cb = bthost->new_rfcomm_conn_data; cb != NULL; cb = cb->next) { if (cb->channel == channel) return cb; } return NULL; } static struct le_ext_adv *le_ext_adv_new(struct bthost *bthost) { struct le_ext_adv *ext_adv; ext_adv = new0(struct le_ext_adv, 1); ext_adv->bthost = bthost; /* Add to queue */ if (!queue_push_tail(bthost->le_ext_adv, ext_adv)) { free(ext_adv); return NULL; } return ext_adv; } static void le_ext_adv_free(void *data) { struct le_ext_adv *ext_adv = data; /* Remove from queue */ queue_remove(ext_adv->bthost->le_ext_adv, ext_adv); free(ext_adv); } void bthost_destroy(struct bthost *bthost) { if (!bthost) return; while (bthost->cmd_q.tail) { struct cmd *cmd = bthost->cmd_q.tail; bthost->cmd_q.tail = cmd->prev; free(cmd); } while (bthost->conns) { struct btconn *conn = bthost->conns; bthost->conns = conn->next; btconn_free(conn); } while (bthost->l2reqs) { struct l2cap_pending_req *req = bthost->l2reqs; bthost->l2reqs = req->next; req->cb(0, NULL, 0, req->user_data); free(req); } while (bthost->new_l2cap_conn_data) { struct l2cap_conn_cb_data *cb = bthost->new_l2cap_conn_data; bthost->new_l2cap_conn_data = cb->next; free(cb); } while (bthost->new_rfcomm_conn_data) { struct rfcomm_conn_cb_data *cb = bthost->new_rfcomm_conn_data; bthost->new_rfcomm_conn_data = cb->next; free(cb); } if (bthost->rfcomm_conn_data) free(bthost->rfcomm_conn_data); smp_stop(bthost->smp_data); queue_destroy(bthost->le_ext_adv, le_ext_adv_free); free(bthost); } void bthost_set_send_handler(struct bthost *bthost, bthost_send_func handler, void *user_data) { if (!bthost) return; bthost->send_handler = handler; bthost->send_data = user_data; } static void queue_command(struct bthost *bthost, const struct iovec *iov, int iovlen) { struct cmd_queue *cmd_q = &bthost->cmd_q; struct cmd *cmd; int i; cmd = malloc(sizeof(*cmd)); if (!cmd) return; memset(cmd, 0, sizeof(*cmd)); for (i = 0; i < iovlen; i++) { memcpy(cmd->data + cmd->len, iov[i].iov_base, iov[i].iov_len); cmd->len += iov[i].iov_len; } if (cmd_q->tail) cmd_q->tail->next = cmd; else cmd_q->head = cmd; cmd->prev = cmd_q->tail; cmd_q->tail = cmd; } static void send_packet(struct bthost *bthost, const struct iovec *iov, int iovlen) { int i; if (!bthost->send_handler) return; for (i = 0; i < iovlen; i++) { if (!i) util_hexdump('<', iov[i].iov_base, iov[i].iov_len, bthost->debug_callback, bthost->debug_data); else util_hexdump(' ', iov[i].iov_base, iov[i].iov_len, bthost->debug_callback, bthost->debug_data); } bthost->send_handler(iov, iovlen, bthost->send_data); } static void send_iov(struct bthost *bthost, uint16_t handle, uint16_t cid, const struct iovec *iov, int iovcnt) { struct bt_hci_acl_hdr acl_hdr; struct bt_l2cap_hdr l2_hdr; uint8_t pkt = BT_H4_ACL_PKT; struct iovec pdu[3 + iovcnt]; int i, len = 0; for (i = 0; i < iovcnt; i++) { pdu[3 + i].iov_base = iov[i].iov_base; pdu[3 + i].iov_len = iov[i].iov_len; len += iov[i].iov_len; } pdu[0].iov_base = &pkt; pdu[0].iov_len = sizeof(pkt); acl_hdr.handle = acl_handle_pack(handle, 0); acl_hdr.dlen = cpu_to_le16(len + sizeof(l2_hdr)); pdu[1].iov_base = &acl_hdr; pdu[1].iov_len = sizeof(acl_hdr); l2_hdr.cid = cpu_to_le16(cid); l2_hdr.len = cpu_to_le16(len); pdu[2].iov_base = &l2_hdr; pdu[2].iov_len = sizeof(l2_hdr); send_packet(bthost, pdu, 3 + iovcnt); } static void send_acl(struct bthost *bthost, uint16_t handle, uint16_t cid, bool sdu_hdr, const void *data, uint16_t len) { struct iovec iov[2]; uint16_t sdu; int num = 0; if (sdu_hdr) { sdu = cpu_to_le16(len); iov[num].iov_base = &sdu; iov[num].iov_len = sizeof(sdu); num++; } iov[num].iov_base = (void *) data; iov[num].iov_len = len; num++; send_iov(bthost, handle, cid, iov, num); } static uint8_t l2cap_sig_send(struct bthost *bthost, struct btconn *conn, uint8_t code, uint8_t ident, const void *data, uint16_t len) { static uint8_t next_ident = 1; struct bt_l2cap_hdr_sig hdr; uint16_t cid; struct iovec iov[2]; if (!ident) { ident = next_ident++; if (!ident) ident = next_ident++; } hdr.code = code; hdr.ident = ident; hdr.len = cpu_to_le16(len); iov[0].iov_base = &hdr; iov[0].iov_len = sizeof(hdr); if (conn->addr_type == BDADDR_BREDR) cid = 0x0001; else cid = 0x0005; if (len == 0) { send_iov(bthost, conn->handle, cid, iov, 1); return ident; } iov[1].iov_base = (void *) data; iov[1].iov_len = len; send_iov(bthost, conn->handle, cid, iov, 2); return ident; } void bthost_add_cid_hook(struct bthost *bthost, uint16_t handle, uint16_t cid, bthost_cid_hook_func_t func, void *user_data) { struct cid_hook *hook; struct btconn *conn; conn = bthost_find_conn(bthost, handle); if (!conn) return; hook = malloc(sizeof(*hook)); if (!hook) return; memset(hook, 0, sizeof(*hook)); hook->cid = cid; hook->func = func; hook->user_data = user_data; hook->next = conn->cid_hooks; conn->cid_hooks = hook; } void bthost_add_sco_hook(struct bthost *bthost, uint16_t handle, bthost_sco_hook_func_t func, void *user_data, bthost_destroy_func_t destroy) { struct sco_hook *hook; struct btconn *conn; conn = bthost_find_conn(bthost, handle); if (!conn || conn->sco_hook) return; hook = malloc(sizeof(*hook)); if (!hook) return; memset(hook, 0, sizeof(*hook)); hook->func = func; hook->user_data = user_data; hook->destroy = destroy; conn->sco_hook = hook; } void bthost_add_iso_hook(struct bthost *bthost, uint16_t handle, bthost_iso_hook_func_t func, void *user_data, bthost_destroy_func_t destroy) { struct iso_hook *hook; struct btconn *conn; conn = bthost_find_conn(bthost, handle); if (!conn || conn->iso_hook) return; hook = malloc(sizeof(*hook)); if (!hook) return; memset(hook, 0, sizeof(*hook)); hook->func = func; hook->user_data = user_data; hook->destroy = destroy; conn->iso_hook = hook; } void bthost_send_cid(struct bthost *bthost, uint16_t handle, uint16_t cid, const void *data, uint16_t len) { struct btconn *conn; struct l2conn *l2conn; bool sdu_hdr = false; conn = bthost_find_conn(bthost, handle); if (!conn) return; l2conn = btconn_find_l2cap_conn_by_dcid(conn, cid); if (l2conn && (l2conn->mode == L2CAP_MODE_LE_CRED || l2conn->mode == L2CAP_MODE_LE_ENH_CRED)) sdu_hdr = true; send_acl(bthost, handle, cid, sdu_hdr, data, len); } void bthost_send_cid_v(struct bthost *bthost, uint16_t handle, uint16_t cid, const struct iovec *iov, int iovcnt) { struct btconn *conn; conn = bthost_find_conn(bthost, handle); if (!conn) return; send_iov(bthost, handle, cid, iov, iovcnt); } static void send_iso(struct bthost *bthost, uint16_t handle, bool ts, uint16_t sn, uint32_t timestamp, uint8_t pkt_status, const struct iovec *iov, int iovcnt) { struct bt_hci_iso_hdr iso_hdr; struct bt_hci_iso_data_start data_hdr; uint8_t pkt = BT_H4_ISO_PKT; struct iovec pdu[4 + iovcnt]; uint16_t flags, dlen; int i, len = 0; for (i = 0; i < iovcnt; i++) { pdu[4 + i].iov_base = iov[i].iov_base; pdu[4 + i].iov_len = iov[i].iov_len; len += iov[i].iov_len; } pdu[0].iov_base = &pkt; pdu[0].iov_len = sizeof(pkt); flags = iso_flags_pack(0x02, ts); dlen = len + sizeof(data_hdr); if (ts) dlen += sizeof(timestamp); iso_hdr.handle = acl_handle_pack(handle, flags); iso_hdr.dlen = cpu_to_le16(dlen); pdu[1].iov_base = &iso_hdr; pdu[1].iov_len = sizeof(iso_hdr); if (ts) { timestamp = cpu_to_le32(timestamp); pdu[2].iov_base = ×tamp; pdu[2].iov_len = sizeof(timestamp); } else { pdu[2].iov_base = NULL; pdu[2].iov_len = 0; } data_hdr.sn = cpu_to_le16(sn); data_hdr.slen = cpu_to_le16(iso_data_len_pack(len, pkt_status)); pdu[3].iov_base = &data_hdr; pdu[3].iov_len = sizeof(data_hdr); send_packet(bthost, pdu, 4 + iovcnt); } void bthost_send_iso(struct bthost *bthost, uint16_t handle, bool ts, uint16_t sn, uint32_t timestamp, uint8_t pkt_status, const struct iovec *iov, int iovcnt) { struct btconn *conn; conn = bthost_find_conn(bthost, handle); if (!conn) return; send_iso(bthost, handle, ts, sn, timestamp, pkt_status, iov, iovcnt); } bool bthost_l2cap_req(struct bthost *bthost, uint16_t handle, uint8_t code, const void *data, uint16_t len, bthost_l2cap_rsp_cb cb, void *user_data) { struct l2cap_pending_req *req; struct btconn *conn; uint8_t ident; conn = bthost_find_conn(bthost, handle); if (!conn) return false; if (code == BT_L2CAP_PDU_CONN_REQ && len == sizeof(struct bt_l2cap_pdu_conn_req)) { const struct bt_l2cap_pdu_conn_req *req = data; bthost_add_l2cap_conn(bthost, conn, le16_to_cpu(req->scid), le16_to_cpu(req->scid), le16_to_cpu(req->psm)); } ident = l2cap_sig_send(bthost, conn, code, 0, data, len); if (!ident) return false; if (!cb) return true; req = malloc(sizeof(*req)); if (!req) return false; memset(req, 0, sizeof(*req)); req->ident = ident; req->cb = cb; req->user_data = user_data; req->next = bthost->l2reqs; bthost->l2reqs = req; return true; } static void send_command(struct bthost *bthost, uint16_t opcode, const void *data, uint8_t len) { struct bt_hci_cmd_hdr hdr; uint8_t pkt = BT_H4_CMD_PKT; struct iovec iov[3]; bthost_debug(bthost, "command 0x%02x", opcode); iov[0].iov_base = &pkt; iov[0].iov_len = sizeof(pkt); hdr.opcode = cpu_to_le16(opcode); hdr.plen = len; iov[1].iov_base = &hdr; iov[1].iov_len = sizeof(hdr); if (len > 0) { iov[2].iov_base = (void *) data; iov[2].iov_len = len; } if (bthost->ncmd) { send_packet(bthost, iov, len > 0 ? 3 : 2); bthost->ncmd--; } else { queue_command(bthost, iov, len > 0 ? 3 : 2); } } static void next_cmd(struct bthost *bthost) { struct cmd_queue *cmd_q = &bthost->cmd_q; struct cmd *cmd = cmd_q->head; struct cmd *next; struct iovec iov; if (!cmd) return; next = cmd->next; if (!bthost->ncmd) return; iov.iov_base = cmd->data; iov.iov_len = cmd->len; send_packet(bthost, &iov, 1); bthost->ncmd--; if (next) next->prev = NULL; else cmd_q->tail = NULL; cmd_q->head = next; free(cmd); } static void read_bd_addr_complete(struct bthost *bthost, const void *data, uint8_t len) { const struct bt_hci_rsp_read_bd_addr *ev = data; if (len < sizeof(*ev)) return; if (ev->status) return; memcpy(bthost->bdaddr, ev->bdaddr, 6); bthost->ready = true; if (bthost->ready_cb) { bthost->ready_cb(); bthost->ready_cb = NULL; } } void bthost_notify_ready(struct bthost *bthost, bthost_ready_cb cb) { if (bthost->ready) { cb(); return; } bthost->ready_cb = cb; } bool bthost_set_debug(struct bthost *bthost, bthost_debug_func_t callback, void *user_data, bthost_destroy_func_t destroy) { if (!bthost) return false; if (bthost->debug_destroy) bthost->debug_destroy(bthost->debug_data); bthost->debug_callback = callback; bthost->debug_destroy = destroy; bthost->debug_data = user_data; return true; } void bthost_debug(struct bthost *host, const char *format, ...) { va_list ap; if (!host || !format || !host->debug_callback) return; va_start(ap, format); util_debug_va(host->debug_callback, host->debug_data, format, ap); va_end(ap); } static void read_local_features_complete(struct bthost *bthost, const void *data, uint8_t len) { const struct bt_hci_rsp_read_local_features *ev = data; if (len < sizeof(*ev)) return; if (ev->status) return; memcpy(bthost->features, ev->features, 8); } static void evt_cmd_complete(struct bthost *bthost, const void *data, uint8_t len) { const struct bt_hci_evt_cmd_complete *ev = data; const void *param; uint16_t opcode; if (len < sizeof(*ev)) return; param = data + sizeof(*ev); bthost->ncmd = ev->ncmd; opcode = le16toh(ev->opcode); switch (opcode) { case BT_HCI_CMD_RESET: break; case BT_HCI_CMD_READ_LOCAL_FEATURES: read_local_features_complete(bthost, param, len - sizeof(*ev)); break; case BT_HCI_CMD_READ_BD_ADDR: read_bd_addr_complete(bthost, param, len - sizeof(*ev)); break; case BT_HCI_CMD_WRITE_SCAN_ENABLE: break; case BT_HCI_CMD_LE_SET_ADV_ENABLE: break; case BT_HCI_CMD_LE_SET_ADV_PARAMETERS: break; case BT_HCI_CMD_PIN_CODE_REQUEST_REPLY: break; case BT_HCI_CMD_PIN_CODE_REQUEST_NEG_REPLY: break; case BT_HCI_CMD_LINK_KEY_REQUEST_NEG_REPLY: break; case BT_HCI_CMD_WRITE_SIMPLE_PAIRING_MODE: break; case BT_HCI_CMD_WRITE_LE_HOST_SUPPORTED: break; case BT_HCI_CMD_WRITE_SECURE_CONN_SUPPORT: break; case BT_HCI_CMD_IO_CAPABILITY_REQUEST_REPLY: break; case BT_HCI_CMD_USER_CONFIRM_REQUEST_REPLY: break; case BT_HCI_CMD_USER_CONFIRM_REQUEST_NEG_REPLY: break; case BT_HCI_CMD_LE_LTK_REQ_REPLY: break; case BT_HCI_CMD_LE_LTK_REQ_NEG_REPLY: break; case BT_HCI_CMD_LE_SET_ADV_DATA: break; case BT_HCI_CMD_LE_SET_EXT_ADV_PARAMS: break; case BT_HCI_CMD_LE_SET_EXT_ADV_DATA: break; case BT_HCI_CMD_LE_SET_EXT_ADV_ENABLE: break; case BT_HCI_CMD_LE_SET_PA_PARAMS: break; case BT_HCI_CMD_LE_SET_PA_ENABLE: break; default: bthost_debug(bthost, "Unhandled cmd_complete opcode 0x%04x", opcode); break; } if (bthost->cmd_complete_cb) bthost->cmd_complete_cb(opcode, 0, param, len - sizeof(*ev), bthost->cmd_complete_data); next_cmd(bthost); } static void evt_cmd_status(struct bthost *bthost, const void *data, uint8_t len) { const struct bt_hci_evt_cmd_status *ev = data; uint16_t opcode; if (len < sizeof(*ev)) return; bthost->ncmd = ev->ncmd; opcode = le16toh(ev->opcode); if (ev->status && bthost->cmd_complete_cb) bthost->cmd_complete_cb(opcode, ev->status, NULL, 0, bthost->cmd_complete_data); next_cmd(bthost); } static void evt_conn_request(struct bthost *bthost, const void *data, uint8_t len) { const struct bt_hci_evt_conn_request *ev = data; struct bt_hci_cmd_accept_conn_request cmd; if (len < sizeof(*ev)) return; memset(&cmd, 0, sizeof(cmd)); memcpy(cmd.bdaddr, ev->bdaddr, sizeof(ev->bdaddr)); send_command(bthost, BT_HCI_CMD_ACCEPT_CONN_REQUEST, &cmd, sizeof(cmd)); } static void init_conn(struct bthost *bthost, uint16_t handle, const uint8_t *bdaddr, uint8_t addr_type) { struct btconn *conn; const uint8_t *ia, *ra; uint8_t ia_type, ra_type; conn = malloc(sizeof(*conn)); if (!conn) return; memset(conn, 0, sizeof(*conn)); conn->handle = handle; memcpy(conn->bdaddr, bdaddr, 6); conn->addr_type = addr_type; conn->next_cid = 0x0040; conn->next = bthost->conns; bthost->conns = conn; if (bthost->conn_init) { ia = bthost->bdaddr; if (addr_type == BDADDR_BREDR) ia_type = addr_type; else ia_type = BDADDR_LE_PUBLIC; ra = bdaddr; ra_type = addr_type; } else { ia = bdaddr; ia_type = addr_type; ra = bthost->bdaddr; if (addr_type == BDADDR_BREDR) ra_type = addr_type; else ra_type = BDADDR_LE_PUBLIC; } conn->smp_data = smp_conn_add(bthost->smp_data, handle, ia, ia_type, ra, ra_type, bthost->conn_init); if (bthost->new_conn_cb) bthost->new_conn_cb(conn->handle, bthost->new_conn_data); if (addr_type == BDADDR_BREDR) { struct bt_l2cap_pdu_info_req req; req.type = L2CAP_IT_FIXED_CHAN; l2cap_sig_send(bthost, conn, BT_L2CAP_PDU_INFO_REQ, 1, &req, sizeof(req)); } } static void init_sco(struct bthost *bthost, uint16_t handle, const uint8_t *bdaddr, uint8_t addr_type) { struct btconn *conn; bthost_debug(bthost, "SCO handle 0x%4.4x", handle); conn = malloc(sizeof(*conn)); if (!conn) return; memset(conn, 0, sizeof(*conn)); conn->handle = handle; memcpy(conn->bdaddr, bdaddr, 6); conn->addr_type = addr_type; conn->next = bthost->conns; bthost->conns = conn; if (bthost->new_sco_cb) bthost->new_sco_cb(handle, bthost->new_sco_data); } static void evt_conn_complete(struct bthost *bthost, const void *data, uint8_t len) { const struct bt_hci_evt_conn_complete *ev = data; if (len < sizeof(*ev)) return; if (ev->status) return; if (ev->link_type == 0x00) { init_sco(bthost, le16_to_cpu(ev->handle), ev->bdaddr, BDADDR_BREDR); } else if (ev->link_type == 0x01) { init_conn(bthost, le16_to_cpu(ev->handle), ev->bdaddr, BDADDR_BREDR); } } static void evt_disconn_complete(struct bthost *bthost, const void *data, uint8_t len) { const struct bt_hci_evt_disconnect_complete *ev = data; struct btconn **curr; uint16_t handle; if (len < sizeof(*ev)) return; if (ev->status) return; handle = le16_to_cpu(ev->handle); for (curr = &bthost->conns; *curr;) { struct btconn *conn = *curr; if (conn->handle == handle) { *curr = conn->next; btconn_free(conn); } else { curr = &conn->next; } } } static void evt_num_completed_packets(struct bthost *bthost, const void *data, uint8_t len) { const struct bt_hci_evt_num_completed_packets *ev = data; if (len < sizeof(*ev)) return; } static void evt_auth_complete(struct bthost *bthost, const void *data, uint8_t len) { const struct bt_hci_evt_auth_complete *ev = data; struct bt_hci_cmd_set_conn_encrypt cp; if (len < sizeof(*ev)) return; if (ev->status) return; cp.handle = ev->handle; cp.encr_mode = 0x01; send_command(bthost, BT_HCI_CMD_SET_CONN_ENCRYPT, &cp, sizeof(cp)); } static void evt_pin_code_request(struct bthost *bthost, const void *data, uint8_t len) { const struct bt_hci_evt_pin_code_request *ev = data; if (len < sizeof(*ev)) return; if (bthost->pin_len > 0) { struct bt_hci_cmd_pin_code_request_reply cp; memset(&cp, 0, sizeof(cp)); memcpy(cp.bdaddr, ev->bdaddr, 6); cp.pin_len = bthost->pin_len; memcpy(cp.pin_code, bthost->pin, bthost->pin_len); send_command(bthost, BT_HCI_CMD_PIN_CODE_REQUEST_REPLY, &cp, sizeof(cp)); } else { struct bt_hci_cmd_pin_code_request_neg_reply cp; memcpy(cp.bdaddr, ev->bdaddr, 6); send_command(bthost, BT_HCI_CMD_PIN_CODE_REQUEST_NEG_REPLY, &cp, sizeof(cp)); } } static void evt_link_key_request(struct bthost *bthost, const void *data, uint8_t len) { const struct bt_hci_evt_link_key_request *ev = data; struct bt_hci_cmd_link_key_request_neg_reply cp; if (len < sizeof(*ev)) return; memset(&cp, 0, sizeof(cp)); memcpy(cp.bdaddr, ev->bdaddr, 6); send_command(bthost, BT_HCI_CMD_LINK_KEY_REQUEST_NEG_REPLY, &cp, sizeof(cp)); } static void evt_link_key_notify(struct bthost *bthost, const void *data, uint8_t len) { const struct bt_hci_evt_link_key_notify *ev = data; if (len < sizeof(*ev)) return; } static void evt_encrypt_change(struct bthost *bthost, const void *data, uint8_t len) { const struct bt_hci_evt_encrypt_change *ev = data; struct btconn *conn; uint16_t handle; if (len < sizeof(*ev)) return; handle = acl_handle(ev->handle); conn = bthost_find_conn(bthost, handle); if (!conn) return; if (ev->status) return; conn->encr_mode = ev->encr_mode; if (conn->smp_data) smp_conn_encrypted(conn->smp_data, conn->encr_mode); } static void evt_io_cap_response(struct bthost *bthost, const void *data, uint8_t len) { const struct bt_hci_evt_io_capability_response *ev = data; struct btconn *conn; if (len < sizeof(*ev)) return; conn = bthost_find_conn_by_bdaddr(bthost, ev->bdaddr); if (!conn) return; } static void evt_io_cap_request(struct bthost *bthost, const void *data, uint8_t len) { const struct bt_hci_evt_io_capability_request *ev = data; struct bt_hci_cmd_io_capability_request_reply cp; struct btconn *conn; if (len < sizeof(*ev)) return; conn = bthost_find_conn_by_bdaddr(bthost, ev->bdaddr); if (!conn) return; memcpy(cp.bdaddr, ev->bdaddr, 6); cp.capability = bthost->io_capability; cp.oob_data = 0x00; cp.authentication = bthost->auth_req; send_command(bthost, BT_HCI_CMD_IO_CAPABILITY_REQUEST_REPLY, &cp, sizeof(cp)); } static void evt_user_confirm_request(struct bthost *bthost, const void *data, uint8_t len) { const struct bt_hci_evt_user_confirm_request *ev = data; struct btconn *conn; if (len < sizeof(*ev)) return; conn = bthost_find_conn_by_bdaddr(bthost, ev->bdaddr); if (!conn) return; if (bthost->reject_user_confirm) { send_command(bthost, BT_HCI_CMD_USER_CONFIRM_REQUEST_NEG_REPLY, ev->bdaddr, 6); return; } send_command(bthost, BT_HCI_CMD_USER_CONFIRM_REQUEST_REPLY, ev->bdaddr, 6); } static void evt_simple_pairing_complete(struct bthost *bthost, const void *data, uint8_t len) { const struct bt_hci_evt_simple_pairing_complete *ev = data; if (len < sizeof(*ev)) return; } static void evt_sync_conn_complete(struct bthost *bthost, const void *data, uint8_t len) { const struct bt_hci_evt_sync_conn_complete *ev = data; if (len < sizeof(*ev)) return; if (ev->status) return; init_sco(bthost, le16_to_cpu(ev->handle), ev->bdaddr, BDADDR_BREDR); } static void evt_le_conn_complete(struct bthost *bthost, const void *data, uint8_t len) { const struct bt_hci_evt_le_conn_complete *ev = data; uint8_t addr_type; if (len < sizeof(*ev)) return; if (ev->status) return; if (ev->peer_addr_type == 0x00) addr_type = BDADDR_LE_PUBLIC; else addr_type = BDADDR_LE_RANDOM; init_conn(bthost, le16_to_cpu(ev->handle), ev->peer_addr, addr_type); } static void evt_le_ext_conn_complete(struct bthost *bthost, const void *data, uint8_t len) { const struct bt_hci_evt_le_enhanced_conn_complete *ev = data; uint8_t addr_type; if (len < sizeof(*ev)) return; if (ev->status) return; if (ev->peer_addr_type == 0x00) addr_type = BDADDR_LE_PUBLIC; else addr_type = BDADDR_LE_RANDOM; init_conn(bthost, le16_to_cpu(ev->handle), ev->peer_addr, addr_type); } static void evt_le_conn_update_complete(struct bthost *bthost, const void *data, uint8_t len) { const struct bt_hci_evt_le_conn_update_complete *ev = data; if (len < sizeof(*ev)) return; if (ev->status) return; } static void evt_le_remote_features_complete(struct bthost *bthost, const void *data, uint8_t len) { const struct bt_hci_evt_le_remote_features_complete *ev = data; if (len < sizeof(*ev)) return; if (ev->status) return; } static void evt_le_ltk_request(struct bthost *bthost, const void *data, uint8_t len) { const struct bt_hci_evt_le_long_term_key_request *ev = data; struct bt_hci_cmd_le_ltk_req_reply cp; struct bt_hci_cmd_le_ltk_req_neg_reply *neg_cp = (void *) &cp; uint16_t handle, ediv; uint64_t rand; struct btconn *conn; int err; if (len < sizeof(*ev)) return; handle = acl_handle(ev->handle); conn = bthost_find_conn(bthost, handle); if (!conn) return; rand = le64_to_cpu(ev->rand); ediv = le16_to_cpu(ev->ediv); cp.handle = ev->handle; err = smp_get_ltk(conn->smp_data, rand, ediv, cp.ltk); if (err < 0) send_command(bthost, BT_HCI_CMD_LE_LTK_REQ_NEG_REPLY, neg_cp, sizeof(*neg_cp)); else send_command(bthost, BT_HCI_CMD_LE_LTK_REQ_REPLY, &cp, sizeof(cp)); } static void init_iso(struct bthost *bthost, uint16_t handle, const uint8_t *bdaddr, uint8_t addr_type) { struct btconn *conn; bthost_debug(bthost, "ISO handle 0x%4.4x", handle); conn = malloc(sizeof(*conn)); if (!conn) return; memset(conn, 0, sizeof(*conn)); conn->handle = handle; memcpy(conn->bdaddr, bdaddr, 6); conn->addr_type = addr_type; conn->next = bthost->conns; bthost->conns = conn; if (bthost->new_iso_cb) bthost->new_iso_cb(handle, bthost->new_iso_data); } static void evt_le_cis_established(struct bthost *bthost, const void *data, uint8_t size) { const struct bt_hci_evt_le_cis_established *ev = data; if (ev->status) return; init_iso(bthost, ev->conn_handle, BDADDR_ANY->b, BDADDR_LE_PUBLIC); } static void evt_le_cis_req(struct bthost *bthost, const void *data, uint8_t len) { const struct bt_hci_evt_le_cis_req *ev = data; struct bt_hci_cmd_le_accept_cis cmd; struct bt_hci_cmd_le_reject_cis rej; if (len < sizeof(*ev)) return; if (bthost->accept_iso_cb) { memset(&rej, 0, sizeof(rej)); rej.reason = bthost->accept_iso_cb(le16_to_cpu(ev->cis_handle), bthost->new_iso_data); if (rej.reason) { rej.handle = ev->cis_handle; send_command(bthost, BT_HCI_CMD_LE_REJECT_CIS, &rej, sizeof(rej)); return; } } memset(&cmd, 0, sizeof(cmd)); cmd.handle = ev->cis_handle; send_command(bthost, BT_HCI_CMD_LE_ACCEPT_CIS, &cmd, sizeof(cmd)); } static void evt_le_ext_adv_report(struct bthost *bthost, const void *data, uint8_t len) { const struct bt_hci_evt_le_ext_adv_report *ev = data; const struct bt_hci_le_ext_adv_report *report; struct le_ext_adv *le_ext_adv; int i; data += sizeof(ev->num_reports); for (i = 0; i < ev->num_reports; i++) { char addr_str[18]; report = data; ba2str((bdaddr_t *) report->addr, addr_str); bthost_debug(bthost, "le ext adv report: %s (0x%02x)", addr_str, report->addr_type); /* Add ext event to the queue */ le_ext_adv = le_ext_adv_new(bthost); if (le_ext_adv) { le_ext_adv->addr_type = report->addr_type; memcpy(le_ext_adv->addr, report->addr, 6); le_ext_adv->direct_addr_type = report->direct_addr_type; memcpy(le_ext_adv->direct_addr, report->direct_addr, 6); } data += (sizeof(*report) + report->data_len); } } static void evt_le_big_complete(struct bthost *bthost, const void *data, uint8_t size) { const struct bt_hci_evt_le_big_complete *ev = data; int i; if (ev->status) return; for (i = 0; i < ev->num_bis; i++) { uint16_t handle = le16_to_cpu(ev->bis_handle[i]); init_iso(bthost, handle, BDADDR_ANY->b, BDADDR_LE_PUBLIC); } } static void evt_le_big_sync_established(struct bthost *bthost, const void *data, uint8_t size) { const struct bt_hci_evt_le_big_sync_estabilished *ev = data; int i; if (ev->status) return; for (i = 0; i < ev->num_bis; i++) { uint16_t handle = le16_to_cpu(ev->bis[i]); init_iso(bthost, handle, BDADDR_ANY->b, BDADDR_LE_PUBLIC); } } static void evt_le_meta_event(struct bthost *bthost, const void *data, uint8_t len) { const uint8_t *event = data; const void *evt_data = data + 1; if (len < 1) return; bthost_debug(bthost, "meta event 0x%02x", *event); switch (*event) { case BT_HCI_EVT_LE_CONN_COMPLETE: evt_le_conn_complete(bthost, evt_data, len - 1); break; case BT_HCI_EVT_LE_CONN_UPDATE_COMPLETE: evt_le_conn_update_complete(bthost, evt_data, len - 1); break; case BT_HCI_EVT_LE_REMOTE_FEATURES_COMPLETE: evt_le_remote_features_complete(bthost, evt_data, len - 1); break; case BT_HCI_EVT_LE_LONG_TERM_KEY_REQUEST: evt_le_ltk_request(bthost, evt_data, len - 1); break; case BT_HCI_EVT_LE_ENHANCED_CONN_COMPLETE: evt_le_ext_conn_complete(bthost, evt_data, len - 1); break; case BT_HCI_EVT_LE_EXT_ADV_REPORT: evt_le_ext_adv_report(bthost, evt_data, len - 1); break; case BT_HCI_EVT_LE_CIS_ESTABLISHED: evt_le_cis_established(bthost, evt_data, len - 1); break; case BT_HCI_EVT_LE_CIS_REQ: evt_le_cis_req(bthost, evt_data, len - 1); break; case BT_HCI_EVT_LE_BIG_COMPLETE: evt_le_big_complete(bthost, evt_data, len - 1); break; case BT_HCI_EVT_LE_BIG_SYNC_ESTABILISHED: evt_le_big_sync_established(bthost, evt_data, len - 1); break; default: bthost_debug(bthost, "Unsupported LE Meta event 0x%2.2x", *event); break; } } static void process_evt(struct bthost *bthost, const void *data, uint16_t len) { const struct bt_hci_evt_hdr *hdr = data; const void *param; if (len < sizeof(*hdr)) return; if (sizeof(*hdr) + hdr->plen != len) return; param = data + sizeof(*hdr); bthost_debug(bthost, "event 0x%02x", hdr->evt); switch (hdr->evt) { case BT_HCI_EVT_CMD_COMPLETE: evt_cmd_complete(bthost, param, hdr->plen); break; case BT_HCI_EVT_CMD_STATUS: evt_cmd_status(bthost, param, hdr->plen); break; case BT_HCI_EVT_CONN_REQUEST: evt_conn_request(bthost, param, hdr->plen); break; case BT_HCI_EVT_CONN_COMPLETE: evt_conn_complete(bthost, param, hdr->plen); break; case BT_HCI_EVT_DISCONNECT_COMPLETE: evt_disconn_complete(bthost, param, hdr->plen); break; case BT_HCI_EVT_SYNC_CONN_COMPLETE: evt_sync_conn_complete(bthost, param, hdr->plen); break; case BT_HCI_EVT_NUM_COMPLETED_PACKETS: evt_num_completed_packets(bthost, param, hdr->plen); break; case BT_HCI_EVT_AUTH_COMPLETE: evt_auth_complete(bthost, param, hdr->plen); break; case BT_HCI_EVT_PIN_CODE_REQUEST: evt_pin_code_request(bthost, param, hdr->plen); break; case BT_HCI_EVT_LINK_KEY_REQUEST: evt_link_key_request(bthost, param, hdr->plen); break; case BT_HCI_EVT_LINK_KEY_NOTIFY: evt_link_key_notify(bthost, param, hdr->plen); break; case BT_HCI_EVT_ENCRYPT_CHANGE: evt_encrypt_change(bthost, param, hdr->plen); break; case BT_HCI_EVT_IO_CAPABILITY_RESPONSE: evt_io_cap_response(bthost, param, hdr->plen); break; case BT_HCI_EVT_IO_CAPABILITY_REQUEST: evt_io_cap_request(bthost, param, hdr->plen); break; case BT_HCI_EVT_USER_CONFIRM_REQUEST: evt_user_confirm_request(bthost, param, hdr->plen); break; case BT_HCI_EVT_SIMPLE_PAIRING_COMPLETE: evt_simple_pairing_complete(bthost, param, hdr->plen); break; case BT_HCI_EVT_LE_META_EVENT: evt_le_meta_event(bthost, param, hdr->plen); break; default: bthost_debug(bthost, "Unsupported event 0x%2.2x", hdr->evt); break; } } static bool l2cap_cmd_rej(struct bthost *bthost, struct btconn *conn, uint8_t ident, const void *data, uint16_t len) { const struct bt_l2cap_pdu_cmd_reject *rsp = data; if (len < sizeof(*rsp)) return false; return true; } static bool l2cap_conn_req(struct bthost *bthost, struct btconn *conn, uint8_t ident, const void *data, uint16_t len) { const struct bt_l2cap_pdu_conn_req *req = data; struct l2cap_conn_cb_data *cb_data; struct bt_l2cap_pdu_conn_rsp rsp; uint16_t psm; if (len < sizeof(*req)) return false; psm = le16_to_cpu(req->psm); memset(&rsp, 0, sizeof(rsp)); rsp.scid = req->scid; cb_data = bthost_find_l2cap_cb_by_psm(bthost, psm); if (cb_data) rsp.dcid = rsp.scid; else rsp.result = cpu_to_le16(0x0002); /* PSM Not Supported */ l2cap_sig_send(bthost, conn, BT_L2CAP_PDU_CONN_RSP, ident, &rsp, sizeof(rsp)); if (!rsp.result) { struct bt_l2cap_pdu_config_req conf_req; struct l2conn *l2conn; l2conn = bthost_add_l2cap_conn(bthost, conn, le16_to_cpu(rsp.dcid), le16_to_cpu(rsp.scid), le16_to_cpu(psm)); memset(&conf_req, 0, sizeof(conf_req)); conf_req.dcid = rsp.scid; l2cap_sig_send(bthost, conn, BT_L2CAP_PDU_CONFIG_REQ, 0, &conf_req, sizeof(conf_req)); if (cb_data && l2conn->psm == cb_data->psm && cb_data->func) cb_data->func(conn->handle, l2conn->dcid, cb_data->user_data); } return true; } static void rfcomm_sabm_send(struct bthost *bthost, struct btconn *conn, struct l2conn *l2conn, uint8_t cr, uint8_t dlci) { struct rfcomm_cmd cmd; memset(&cmd, 0, sizeof(cmd)); cmd.address = RFCOMM_ADDR(cr, dlci); cmd.control = RFCOMM_CTRL(RFCOMM_SABM, 1); cmd.length = RFCOMM_LEN8(0); cmd.fcs = rfcomm_fcs2((uint8_t *)&cmd); send_acl(bthost, conn->handle, l2conn->dcid, false, &cmd, sizeof(cmd)); } static bool l2cap_conn_rsp(struct bthost *bthost, struct btconn *conn, uint8_t ident, const void *data, uint16_t len) { const struct bt_l2cap_pdu_conn_rsp *rsp = data; struct bt_l2cap_pdu_config_req req; struct l2conn *l2conn; if (len < sizeof(*rsp)) return false; l2conn = btconn_find_l2cap_conn_by_scid(conn, le16_to_cpu(rsp->scid)); if (l2conn) l2conn->dcid = le16_to_cpu(rsp->dcid); else return false; if (rsp->result) return true; memset(&req, 0, sizeof(req)); req.dcid = rsp->dcid; l2cap_sig_send(bthost, conn, BT_L2CAP_PDU_CONFIG_REQ, 0, &req, sizeof(req)); return true; } static bool l2cap_config_req(struct bthost *bthost, struct btconn *conn, uint8_t ident, const void *data, uint16_t len) { const struct bt_l2cap_pdu_config_req *req = data; struct bt_l2cap_pdu_config_rsp rsp; struct l2conn *l2conn; uint16_t dcid; if (len < sizeof(*req)) return false; dcid = le16_to_cpu(req->dcid); l2conn = btconn_find_l2cap_conn_by_scid(conn, dcid); if (!l2conn) return false; memset(&rsp, 0, sizeof(rsp)); rsp.scid = cpu_to_le16(l2conn->dcid); rsp.flags = req->flags; l2cap_sig_send(bthost, conn, BT_L2CAP_PDU_CONFIG_RSP, ident, &rsp, sizeof(rsp)); return true; } static bool l2cap_config_rsp(struct bthost *bthost, struct btconn *conn, uint8_t ident, const void *data, uint16_t len) { const struct bt_l2cap_pdu_config_rsp *rsp = data; struct l2conn *l2conn; if (len < sizeof(*rsp)) return false; l2conn = btconn_find_l2cap_conn_by_scid(conn, rsp->scid); if (!l2conn) return false; if (l2conn->psm == 0x0003 && !rsp->result && bthost->rfcomm_conn_data) rfcomm_sabm_send(bthost, conn, l2conn, 1, 0); return true; } static bool l2cap_disconn_req(struct bthost *bthost, struct btconn *conn, uint8_t ident, const void *data, uint16_t len) { const struct bt_l2cap_pdu_disconn_req *req = data; struct l2cap_conn_cb_data *cb_data; struct bt_l2cap_pdu_disconn_rsp rsp; struct l2conn *l2conn; if (len < sizeof(*req)) return false; memset(&rsp, 0, sizeof(rsp)); rsp.dcid = req->dcid; rsp.scid = req->scid; l2cap_sig_send(bthost, conn, BT_L2CAP_PDU_DISCONN_RSP, ident, &rsp, sizeof(rsp)); l2conn = btconn_find_l2cap_conn_by_scid(conn, rsp.scid); if (!l2conn) return true; cb_data = bthost_find_l2cap_cb_by_psm(bthost, l2conn->psm); if (cb_data && cb_data->disconn_func) cb_data->disconn_func(cb_data->user_data); return true; } static bool l2cap_info_req(struct bthost *bthost, struct btconn *conn, uint8_t ident, const void *data, uint16_t len) { const struct bt_l2cap_pdu_info_req *req = data; uint64_t fixed_chan; uint16_t type; uint8_t buf[12]; struct bt_l2cap_pdu_info_rsp *rsp = (void *) buf; if (len < sizeof(*req)) return false; memset(buf, 0, sizeof(buf)); rsp->type = req->type; type = le16_to_cpu(req->type); switch (type) { case L2CAP_IT_FEAT_MASK: rsp->result = 0x0000; put_le32(L2CAP_FEAT_FIXED_CHAN, rsp->data); l2cap_sig_send(bthost, conn, BT_L2CAP_PDU_INFO_RSP, ident, rsp, sizeof(*rsp) + 4); break; case L2CAP_IT_FIXED_CHAN: rsp->result = 0x0000; fixed_chan = L2CAP_FC_SIG_BREDR; if (bthost->sc && bthost->le) fixed_chan |= L2CAP_FC_SMP_BREDR; put_le64(fixed_chan, rsp->data); l2cap_sig_send(bthost, conn, BT_L2CAP_PDU_INFO_RSP, ident, rsp, sizeof(*rsp) + sizeof(fixed_chan)); break; default: rsp->result = cpu_to_le16(0x0001); /* Not Supported */ l2cap_sig_send(bthost, conn, BT_L2CAP_PDU_INFO_RSP, ident, rsp, sizeof(*rsp)); break; } return true; } static bool l2cap_info_rsp(struct bthost *bthost, struct btconn *conn, uint8_t ident, const void *data, uint16_t len) { const struct bt_l2cap_pdu_info_rsp *rsp = data; uint16_t type; if (len < sizeof(*rsp)) return false; if (rsp->result) return true; type = le16_to_cpu(rsp->type); switch (type) { case L2CAP_IT_FIXED_CHAN: if (len < sizeof(*rsp) + 8) return false; conn->fixed_chan = get_le64(rsp->data); if (conn->smp_data && conn->encr_mode) smp_conn_encrypted(conn->smp_data, conn->encr_mode); break; default: break; } return true; } static void handle_pending_l2reqs(struct bthost *bthost, struct btconn *conn, uint8_t ident, uint8_t code, const void *data, uint16_t len) { struct l2cap_pending_req **curr; for (curr = &bthost->l2reqs; *curr != NULL;) { struct l2cap_pending_req *req = *curr; if (req->ident != ident) { curr = &req->next; continue; } *curr = req->next; req->cb(code, data, len, req->user_data); free(req); } } static bool l2cap_rsp(uint8_t code) { switch (code) { case BT_L2CAP_PDU_CMD_REJECT: case BT_L2CAP_PDU_CONN_RSP: case BT_L2CAP_PDU_CONFIG_RSP: case BT_L2CAP_PDU_INFO_RSP: return true; } return false; } static void l2cap_sig(struct bthost *bthost, struct btconn *conn, const void *data, uint16_t len) { const struct bt_l2cap_hdr_sig *hdr = data; struct bt_l2cap_pdu_cmd_reject rej; uint16_t hdr_len; bool ret; if (len < sizeof(*hdr)) goto reject; hdr_len = le16_to_cpu(hdr->len); if (sizeof(*hdr) + hdr_len != len) goto reject; switch (hdr->code) { case BT_L2CAP_PDU_CMD_REJECT: ret = l2cap_cmd_rej(bthost, conn, hdr->ident, data + sizeof(*hdr), hdr_len); break; case BT_L2CAP_PDU_CONN_REQ: ret = l2cap_conn_req(bthost, conn, hdr->ident, data + sizeof(*hdr), hdr_len); break; case BT_L2CAP_PDU_CONN_RSP: ret = l2cap_conn_rsp(bthost, conn, hdr->ident, data + sizeof(*hdr), hdr_len); break; case BT_L2CAP_PDU_CONFIG_REQ: ret = l2cap_config_req(bthost, conn, hdr->ident, data + sizeof(*hdr), hdr_len); break; case BT_L2CAP_PDU_CONFIG_RSP: ret = l2cap_config_rsp(bthost, conn, hdr->ident, data + sizeof(*hdr), hdr_len); break; case BT_L2CAP_PDU_DISCONN_REQ: ret = l2cap_disconn_req(bthost, conn, hdr->ident, data + sizeof(*hdr), hdr_len); break; case BT_L2CAP_PDU_INFO_REQ: ret = l2cap_info_req(bthost, conn, hdr->ident, data + sizeof(*hdr), hdr_len); break; case BT_L2CAP_PDU_INFO_RSP: ret = l2cap_info_rsp(bthost, conn, hdr->ident, data + sizeof(*hdr), hdr_len); break; default: bthost_debug(bthost, "Unknown L2CAP code 0x%02x", hdr->code); ret = false; } if (l2cap_rsp(hdr->code)) handle_pending_l2reqs(bthost, conn, hdr->ident, hdr->code, data + sizeof(*hdr), hdr_len); if (ret) return; reject: memset(&rej, 0, sizeof(rej)); l2cap_sig_send(bthost, conn, BT_L2CAP_PDU_CMD_REJECT, 0, &rej, sizeof(rej)); } static bool l2cap_conn_param_req(struct bthost *bthost, struct btconn *conn, uint8_t ident, const void *data, uint16_t len) { const struct bt_l2cap_pdu_conn_param_req *req = data; struct bt_l2cap_pdu_conn_param_rsp rsp; struct bt_hci_cmd_le_conn_update hci_cmd; if (len < sizeof(*req)) return false; memset(&hci_cmd, 0, sizeof(hci_cmd)); hci_cmd.handle = cpu_to_le16(conn->handle); hci_cmd.min_interval = req->min_interval; hci_cmd.max_interval = req->max_interval; hci_cmd.latency = req->latency; hci_cmd.supv_timeout = req->timeout; hci_cmd.min_length = cpu_to_le16(0x0001); hci_cmd.max_length = cpu_to_le16(0x0001); send_command(bthost, BT_HCI_CMD_LE_CONN_UPDATE, &hci_cmd, sizeof(hci_cmd)); memset(&rsp, 0, sizeof(rsp)); l2cap_sig_send(bthost, conn, BT_L2CAP_PDU_CONN_PARAM_RSP, ident, &rsp, sizeof(rsp)); return true; } static bool l2cap_conn_param_rsp(struct bthost *bthost, struct btconn *conn, uint8_t ident, const void *data, uint16_t len) { const struct bt_l2cap_pdu_conn_param_req *rsp = data; if (len < sizeof(*rsp)) return false; return true; } static bool l2cap_le_conn_req(struct bthost *bthost, struct btconn *conn, uint8_t ident, const void *data, uint16_t len) { const struct bt_l2cap_pdu_le_conn_req *req = data; struct l2cap_conn_cb_data *cb_data; struct bt_l2cap_pdu_le_conn_rsp rsp; uint16_t psm; if (len < sizeof(*req)) return false; psm = le16_to_cpu(req->psm); memset(&rsp, 0, sizeof(rsp)); cb_data = bthost_find_l2cap_cb_by_psm(bthost, psm); if (cb_data) { rsp.dcid = cpu_to_le16(conn->next_cid++); rsp.mtu = cpu_to_le16(cb_data->mtu) ? : cpu_to_le16(23); rsp.mps = cpu_to_le16(cb_data->mps) ? : cpu_to_le16(23); rsp.credits = cpu_to_le16(cb_data->credits) ? : cpu_to_le16(1); } else rsp.result = cpu_to_le16(0x0002); /* PSM Not Supported */ l2cap_sig_send(bthost, conn, BT_L2CAP_PDU_LE_CONN_RSP, ident, &rsp, sizeof(rsp)); if (!rsp.result) { struct l2conn *l2conn; l2conn = bthost_add_l2cap_conn(bthost, conn, le16_to_cpu(rsp.dcid), le16_to_cpu(req->scid), le16_to_cpu(psm)); l2conn->mode = L2CAP_MODE_LE_CRED; if (cb_data && l2conn->psm == cb_data->psm && cb_data->func) cb_data->func(conn->handle, l2conn->dcid, cb_data->user_data); } return true; } static bool l2cap_le_conn_rsp(struct bthost *bthost, struct btconn *conn, uint8_t ident, const void *data, uint16_t len) { const struct bt_l2cap_pdu_le_conn_rsp *rsp = data; struct l2conn *l2conn; if (len < sizeof(*rsp)) return false; /* TODO add L2CAP connection before with proper PSM */ l2conn = bthost_add_l2cap_conn(bthost, conn, 0, le16_to_cpu(rsp->dcid), 0); l2conn->mode = L2CAP_MODE_LE_CRED; return true; } static bool l2cap_ecred_conn_req(struct bthost *bthost, struct btconn *conn, uint8_t ident, const void *data, uint16_t len) { const struct bt_l2cap_pdu_ecred_conn_req *req = data; struct { struct bt_l2cap_pdu_ecred_conn_rsp pdu; uint16_t dcid[5]; } __attribute__ ((packed)) rsp; uint16_t psm; int num_scid, i = 0; if (len < sizeof(*req)) return false; psm = le16_to_cpu(req->psm); memset(&rsp, 0, sizeof(rsp)); rsp.pdu.mtu = 64; rsp.pdu.mps = 64; rsp.pdu.credits = 1; if (!bthost_find_l2cap_cb_by_psm(bthost, psm)) { rsp.pdu.result = cpu_to_le16(0x0002); /* PSM Not Supported */ goto respond; } len -= sizeof(rsp.pdu); num_scid = len / sizeof(*req->scid); for (; i < num_scid; i++) rsp.dcid[i] = cpu_to_le16(conn->next_cid++); respond: l2cap_sig_send(bthost, conn, BT_L2CAP_PDU_ECRED_CONN_RSP, ident, &rsp, sizeof(rsp.pdu) + i * sizeof(*rsp.dcid)); return true; } static bool l2cap_ecred_conn_rsp(struct bthost *bthost, struct btconn *conn, uint8_t ident, const void *data, uint16_t len) { const struct { const struct bt_l2cap_pdu_ecred_conn_rsp *pdu; uint16_t scid[5]; } __attribute__ ((packed)) *rsp = data; int num_scid, i; struct l2conn *l2conn; if (len < sizeof(*rsp)) return false; num_scid = len / sizeof(*rsp->scid); for (i = 0; i < num_scid; i++) { /* TODO add L2CAP connection before with proper PSM */ l2conn = bthost_add_l2cap_conn(bthost, conn, 0, le16_to_cpu(rsp->scid[i]), 0); l2conn->mode = L2CAP_MODE_LE_ENH_CRED; } return true; } static bool l2cap_le_rsp(uint8_t code) { switch (code) { case BT_L2CAP_PDU_CMD_REJECT: case BT_L2CAP_PDU_CONN_PARAM_RSP: case BT_L2CAP_PDU_LE_CONN_RSP: case BT_L2CAP_PDU_ECRED_CONN_RSP: return true; } return false; } static void l2cap_le_sig(struct bthost *bthost, struct btconn *conn, const void *data, uint16_t len) { const struct bt_l2cap_hdr_sig *hdr = data; struct bt_l2cap_pdu_cmd_reject rej; uint16_t hdr_len; bool ret; if (len < sizeof(*hdr)) goto reject; hdr_len = le16_to_cpu(hdr->len); if (sizeof(*hdr) + hdr_len != len) goto reject; switch (hdr->code) { case BT_L2CAP_PDU_CMD_REJECT: ret = l2cap_cmd_rej(bthost, conn, hdr->ident, data + sizeof(*hdr), hdr_len); break; case BT_L2CAP_PDU_DISCONN_REQ: ret = l2cap_disconn_req(bthost, conn, hdr->ident, data + sizeof(*hdr), hdr_len); break; case BT_L2CAP_PDU_CONN_PARAM_REQ: ret = l2cap_conn_param_req(bthost, conn, hdr->ident, data + sizeof(*hdr), hdr_len); break; case BT_L2CAP_PDU_CONN_PARAM_RSP: ret = l2cap_conn_param_rsp(bthost, conn, hdr->ident, data + sizeof(*hdr), hdr_len); break; case BT_L2CAP_PDU_LE_CONN_REQ: ret = l2cap_le_conn_req(bthost, conn, hdr->ident, data + sizeof(*hdr), hdr_len); break; case BT_L2CAP_PDU_LE_CONN_RSP: ret = l2cap_le_conn_rsp(bthost, conn, hdr->ident, data + sizeof(*hdr), hdr_len); break; case BT_L2CAP_PDU_ECRED_CONN_REQ: ret = l2cap_ecred_conn_req(bthost, conn, hdr->ident, data + sizeof(*hdr), hdr_len); break; case BT_L2CAP_PDU_ECRED_CONN_RSP: ret = l2cap_ecred_conn_rsp(bthost, conn, hdr->ident, data + sizeof(*hdr), hdr_len); break; default: bthost_debug(bthost, "Unknown L2CAP code 0x%02x", hdr->code); ret = false; } if (l2cap_le_rsp(hdr->code)) handle_pending_l2reqs(bthost, conn, hdr->ident, hdr->code, data + sizeof(*hdr), hdr_len); if (ret) return; reject: memset(&rej, 0, sizeof(rej)); l2cap_sig_send(bthost, conn, BT_L2CAP_PDU_CMD_REJECT, 0, &rej, sizeof(rej)); } static struct cid_hook *find_cid_hook(struct btconn *conn, uint16_t cid) { struct cid_hook *hook; for (hook = conn->cid_hooks; hook != NULL; hook = hook->next) { if (hook->cid == cid) return hook; } return NULL; } static struct rfcomm_chan_hook *find_rfcomm_chan_hook(struct btconn *conn, uint8_t channel) { struct rfcomm_chan_hook *hook; for (hook = conn->rfcomm_chan_hooks; hook != NULL; hook = hook->next) if (hook->channel == channel) return hook; return NULL; } static void rfcomm_ua_send(struct bthost *bthost, struct btconn *conn, struct l2conn *l2conn, uint8_t cr, uint8_t dlci) { struct rfcomm_cmd cmd; memset(&cmd, 0, sizeof(cmd)); cmd.address = RFCOMM_ADDR(cr, dlci); cmd.control = RFCOMM_CTRL(RFCOMM_UA, 1); cmd.length = RFCOMM_LEN8(0); cmd.fcs = rfcomm_fcs2((uint8_t *)&cmd); send_acl(bthost, conn->handle, l2conn->dcid, false, &cmd, sizeof(cmd)); } static void rfcomm_dm_send(struct bthost *bthost, struct btconn *conn, struct l2conn *l2conn, uint8_t cr, uint8_t dlci) { struct rfcomm_cmd cmd; memset(&cmd, 0, sizeof(cmd)); cmd.address = RFCOMM_ADDR(cr, dlci); cmd.control = RFCOMM_CTRL(RFCOMM_DM, 1); cmd.length = RFCOMM_LEN8(0); cmd.fcs = rfcomm_fcs2((uint8_t *)&cmd); send_acl(bthost, conn->handle, l2conn->dcid, false, &cmd, sizeof(cmd)); } static void rfcomm_sabm_recv(struct bthost *bthost, struct btconn *conn, struct l2conn *l2conn, const void *data, uint16_t len) { const struct rfcomm_cmd *hdr = data; uint8_t dlci; struct rfcomm_conn_cb_data *cb; uint8_t chan; if (len < sizeof(*hdr)) return; chan = RFCOMM_GET_CHANNEL(hdr->address); dlci = RFCOMM_GET_DLCI(hdr->address); cb = bthost_find_rfcomm_cb_by_channel(bthost, chan); if (!dlci || cb) { bthost_add_rfcomm_conn(bthost, conn, l2conn, chan); rfcomm_ua_send(bthost, conn, l2conn, 1, dlci); if (cb && cb->func) cb->func(conn->handle, l2conn->scid, cb->user_data, true); } else { rfcomm_dm_send(bthost, conn, l2conn, 1, dlci); } } static void rfcomm_disc_recv(struct bthost *bthost, struct btconn *conn, struct l2conn *l2conn, const void *data, uint16_t len) { const struct rfcomm_cmd *hdr = data; uint8_t dlci; if (len < sizeof(*hdr)) return; dlci = RFCOMM_GET_DLCI(hdr->address); rfcomm_ua_send(bthost, conn, l2conn, 0, dlci); } static void rfcomm_uih_send(struct bthost *bthost, struct btconn *conn, struct l2conn *l2conn, uint8_t address, uint8_t type, const void *data, uint16_t len) { struct rfcomm_hdr hdr; struct rfcomm_mcc mcc; uint8_t fcs; struct iovec iov[4]; hdr.address = address; hdr.control = RFCOMM_CTRL(RFCOMM_UIH, 0); hdr.length = RFCOMM_LEN8(sizeof(mcc) + len); iov[0].iov_base = &hdr; iov[0].iov_len = sizeof(hdr); mcc.type = type; mcc.length = RFCOMM_LEN8(len); iov[1].iov_base = &mcc; iov[1].iov_len = sizeof(mcc); iov[2].iov_base = (void *) data; iov[2].iov_len = len; fcs = rfcomm_fcs((uint8_t *) &hdr); iov[3].iov_base = &fcs; iov[3].iov_len = sizeof(fcs); send_iov(bthost, conn->handle, l2conn->dcid, iov, 4); } static void rfcomm_ua_recv(struct bthost *bthost, struct btconn *conn, struct l2conn *l2conn, const void *data, uint16_t len) { const struct rfcomm_cmd *ua_hdr = data; uint8_t channel; struct rfcomm_connection_data *conn_data = bthost->rfcomm_conn_data; uint8_t type; struct rfcomm_pn pn_cmd; if (len < sizeof(*ua_hdr)) return; channel = RFCOMM_GET_CHANNEL(ua_hdr->address); type = RFCOMM_GET_TYPE(ua_hdr->control); if (channel && conn_data && conn_data->channel == channel) { bthost_add_rfcomm_conn(bthost, conn, l2conn, channel); if (conn_data->cb) conn_data->cb(conn->handle, l2conn->scid, conn_data->user_data, true); free(bthost->rfcomm_conn_data); bthost->rfcomm_conn_data = NULL; return; } if (!conn_data || !RFCOMM_TEST_CR(type)) return; bthost_add_rfcomm_conn(bthost, conn, l2conn, channel); pn_cmd.dlci = conn_data->channel * 2; pn_cmd.priority = 7; pn_cmd.ack_timer = 0; pn_cmd.max_retrans = 0; pn_cmd.mtu = 667; pn_cmd.credits = 7; rfcomm_uih_send(bthost, conn, l2conn, RFCOMM_ADDR(1, 0), RFCOMM_MCC_TYPE(1, RFCOMM_PN), &pn_cmd, sizeof(pn_cmd)); } static void rfcomm_dm_recv(struct bthost *bthost, struct btconn *conn, struct l2conn *l2conn, const void *data, uint16_t len) { const struct rfcomm_cmd *hdr = data; uint8_t channel; struct rfcomm_connection_data *conn_data = bthost->rfcomm_conn_data; if (len < sizeof(*hdr)) return; channel = RFCOMM_GET_CHANNEL(hdr->address); if (conn_data && conn_data->channel == channel) { if (conn_data->cb) conn_data->cb(conn->handle, l2conn->scid, conn_data->user_data, false); free(bthost->rfcomm_conn_data); bthost->rfcomm_conn_data = NULL; } } static void rfcomm_msc_recv(struct bthost *bthost, struct btconn *conn, struct l2conn *l2conn, uint8_t cr, const struct rfcomm_msc *msc) { struct rfcomm_msc msc_cmd; msc_cmd.dlci = msc->dlci; msc_cmd.v24_sig = msc->v24_sig; rfcomm_uih_send(bthost, conn, l2conn, RFCOMM_ADDR(0, 0), RFCOMM_MCC_TYPE(cr, RFCOMM_MSC), &msc_cmd, sizeof(msc_cmd)); } static void rfcomm_pn_recv(struct bthost *bthost, struct btconn *conn, struct l2conn *l2conn, uint8_t cr, const struct rfcomm_pn *pn) { struct rfcomm_pn pn_cmd; if (!cr) { rfcomm_sabm_send(bthost, conn, l2conn, 1, bthost->rfcomm_conn_data->channel * 2); return; } pn_cmd.dlci = pn->dlci; pn_cmd.flow_ctrl = pn->flow_ctrl; pn_cmd.priority = pn->priority; pn_cmd.ack_timer = pn->ack_timer; pn_cmd.max_retrans = pn->max_retrans; pn_cmd.mtu = pn->mtu; pn_cmd.credits = 255; rfcomm_uih_send(bthost, conn, l2conn, RFCOMM_ADDR(1, 0), RFCOMM_MCC_TYPE(0, RFCOMM_PN), &pn_cmd, sizeof(pn_cmd)); } static void rfcomm_mcc_recv(struct bthost *bthost, struct btconn *conn, struct l2conn *l2conn, const void *data, uint16_t len) { const struct rfcomm_mcc *mcc = data; const struct rfcomm_msc *msc; const struct rfcomm_pn *pn; if (len < sizeof(*mcc)) return; switch (RFCOMM_GET_MCC_TYPE(mcc->type)) { case RFCOMM_MSC: if (len - sizeof(*mcc) < sizeof(*msc)) break; msc = data + sizeof(*mcc); rfcomm_msc_recv(bthost, conn, l2conn, RFCOMM_TEST_CR(mcc->type) / 2, msc); break; case RFCOMM_PN: if (len - sizeof(*mcc) < sizeof(*pn)) break; pn = data + sizeof(*mcc); rfcomm_pn_recv(bthost, conn, l2conn, RFCOMM_TEST_CR(mcc->type) / 2, pn); break; default: break; } } #define GET_LEN8(length) ((length & 0xfe) >> 1) #define GET_LEN16(length) ((length & 0xfffe) >> 1) static void rfcomm_uih_recv(struct bthost *bthost, struct btconn *conn, struct l2conn *l2conn, const void *data, uint16_t len) { const struct rfcomm_hdr *hdr = data; uint16_t hdr_len, data_len; const void *p; if (len < sizeof(*hdr)) { bthost_debug(bthost, "RFCOMM UIH: too short"); return; } if (RFCOMM_TEST_EA(hdr->length)) { data_len = (uint16_t) GET_LEN8(hdr->length); hdr_len = sizeof(*hdr); } else { uint8_t ex_len = *((uint8_t *)(data + sizeof(*hdr))); data_len = GET_LEN16((((uint16_t) ex_len << 8) | hdr->length)); hdr_len = sizeof(*hdr) + sizeof(uint8_t); } if (len < hdr_len + data_len) { bthost_debug(bthost, "RFCOMM UIH: %u != %u", len, hdr_len + data_len); return; } p = data + hdr_len; if (RFCOMM_GET_DLCI(hdr->address)) { struct rfcomm_chan_hook *hook; hook = find_rfcomm_chan_hook(conn, RFCOMM_GET_CHANNEL(hdr->address)); if (hook && data_len) hook->func(p, data_len, hook->user_data); } else { rfcomm_mcc_recv(bthost, conn, l2conn, p, data_len); } } static void process_rfcomm(struct bthost *bthost, struct btconn *conn, struct l2conn *l2conn, const void *data, uint16_t len) { const struct rfcomm_hdr *hdr = data; bthost_debug(bthost, "RFCOMM data: %u bytes", len); switch (RFCOMM_GET_TYPE(hdr->control)) { case RFCOMM_SABM: rfcomm_sabm_recv(bthost, conn, l2conn, data, len); break; case RFCOMM_DISC: rfcomm_disc_recv(bthost, conn, l2conn, data, len); break; case RFCOMM_UA: rfcomm_ua_recv(bthost, conn, l2conn, data, len); break; case RFCOMM_DM: rfcomm_dm_recv(bthost, conn, l2conn, data, len); break; case RFCOMM_UIH: rfcomm_uih_recv(bthost, conn, l2conn, data, len); break; default: bthost_debug(bthost, "Unknown frame type"); break; } } static void append_l2conn_data(struct bthost *bthost, struct l2conn *conn, const void *data, uint16_t len) { if (!conn->recv_data) { bthost_debug(bthost, "Unexpected L2CAP SDU data: sCID 0x%4.4x ", conn->scid); return; } if (conn->recv_len + len > conn->data_len) { bthost_debug(bthost, "Unexpected L2CAP SDU data: sCID 0x%4.4x ", conn->scid); return; } memcpy(conn->recv_data + conn->recv_len, data, len); conn->recv_len += len; bthost_debug(bthost, "L2CAP SDU data: %u/%u bytes", conn->recv_len, conn->data_len); } static void free_l2conn_data(struct l2conn *conn) { free(conn->recv_data); conn->recv_data = NULL; conn->recv_len = 0; conn->data_len = 0; } static void new_l2conn_data(struct bthost *bthost, struct l2conn *conn, uint16_t len) { free(conn->recv_data); conn->recv_data = malloc(len); conn->recv_len = 0; conn->data_len = len; } static bool process_l2cap_conn(struct bthost *bthost, struct btconn *conn, struct l2conn *l2conn, struct iovec *data) { struct bt_l2cap_pdu_le_flowctl_creds creds; uint16_t sdu; if (!l2conn) return true; switch (l2conn->mode) { case L2CAP_MODE_LE_CRED: case L2CAP_MODE_LE_ENH_CRED: break; case L2CAP_MODE_OTHER: return true; } /* Credit-based flow control */ creds.cid = cpu_to_le16(l2conn->scid); creds.credits = cpu_to_le16(1); l2cap_sig_send(bthost, conn, BT_L2CAP_PDU_LE_FLOWCTL_CREDS, 0, &creds, sizeof(creds)); if (!l2conn->data_len) { if (!util_iov_pull_le16(data, &sdu)) { free_l2conn_data(l2conn); bthost_debug(bthost, "L2CAP invalid SDU"); return false; } new_l2conn_data(bthost, l2conn, sdu); } append_l2conn_data(bthost, l2conn, data->iov_base, data->iov_len); if (l2conn->recv_len < l2conn->data_len) return false; /* SDU incomplete */ l2conn->data_len = 0; data->iov_base = l2conn->recv_data; data->iov_len = l2conn->recv_len; return true; } static void process_l2cap(struct bthost *bthost, struct btconn *conn, const void *buf, uint16_t len) { const struct bt_l2cap_hdr *l2_hdr = buf; struct cid_hook *hook; struct l2conn *l2conn; struct iovec data; uint16_t cid, l2_len; l2_len = le16_to_cpu(l2_hdr->len); if (len != sizeof(*l2_hdr) + l2_len) { bthost_debug(bthost, "L2CAP invalid length: %u != %zu", len, sizeof(*l2_hdr) + l2_len); return; } bthost_debug(bthost, "L2CAP data: %u bytes", l2_len); cid = le16_to_cpu(l2_hdr->cid); l2conn = btconn_find_l2cap_conn_by_scid(conn, cid); data.iov_base = (void *)l2_hdr->data; data.iov_len = l2_len; if (!process_l2cap_conn(bthost, conn, l2conn, &data)) return; hook = find_cid_hook(conn, cid); if (hook) { hook->func(data.iov_base, data.iov_len, hook->user_data); return; } switch (cid) { case 0x0001: l2cap_sig(bthost, conn, data.iov_base, data.iov_len); break; case 0x0005: l2cap_le_sig(bthost, conn, data.iov_base, data.iov_len); break; case 0x0006: smp_data(conn->smp_data, data.iov_base, data.iov_len); break; case 0x0007: smp_bredr_data(conn->smp_data, data.iov_base, data.iov_len); break; default: if (l2conn && l2conn->psm == 0x0003) process_rfcomm(bthost, conn, l2conn, data.iov_base, data.iov_len); else bthost_debug(bthost, "Packet for unknown CID 0x%04x (%u)", cid, cid); break; } } static void append_recv_data(struct bthost *bthost, struct btconn *conn, const char *type, uint8_t flags, const void *data, uint16_t len) { if (!conn->recv_data) { bthost_debug(bthost, "Unexpected %s frame: handle 0x%4.4x " "flags 0x%2.2x", type, conn->handle, flags); return; } if (conn->recv_len + len > conn->data_len) { bthost_debug(bthost, "Unexpected %s frame: handle 0x%4.4x " "flags 0x%2.2x", type, conn->handle, flags); return; } memcpy(conn->recv_data + conn->recv_len, data, len); conn->recv_len += len; bthost_debug(bthost, "%s data: %u/%u bytes", type, conn->recv_len, conn->data_len); } static void free_recv_data(struct btconn *conn) { free(conn->recv_data); conn->recv_data = NULL; conn->recv_len = 0; conn->data_len = 0; } static void append_acl_data(struct bthost *bthost, struct btconn *conn, uint8_t flags, const void *data, uint16_t len) { append_recv_data(bthost, conn, "ACL", flags, data, len); if (conn->recv_len < conn->data_len) return; process_l2cap(bthost, conn, conn->recv_data, conn->recv_len); free_recv_data(conn); } static void new_recv_data(struct btconn *conn, uint16_t len) { conn->recv_data = malloc(len); conn->recv_len = 0; conn->data_len = len; } static void process_acl(struct bthost *bthost, const void *data, uint16_t len) { const struct bt_hci_acl_hdr *acl_hdr = data; const struct bt_l2cap_hdr *l2_hdr = (void *) acl_hdr->data; uint16_t handle, acl_len, l2_len; uint8_t flags; struct btconn *conn; acl_len = le16_to_cpu(acl_hdr->dlen); if (len != sizeof(*acl_hdr) + acl_len) return; handle = acl_handle(acl_hdr->handle); flags = acl_flags(acl_hdr->handle); conn = bthost_find_conn(bthost, handle); if (!conn) { bthost_debug(bthost, "Unknown handle: 0x%4.4x", handle); return; } switch (flags) { case 0x00: /* start of a non-automatically-flushable PDU */ case 0x02: /* start of an automatically-flushable PDU */ if (conn->recv_data) { bthost_debug(bthost, "Unexpected ACL start frame"); free_recv_data(conn); } l2_len = le16_to_cpu(l2_hdr->len) + sizeof(*l2_hdr); bthost_debug(bthost, "acl_len %u l2_len %u", acl_len, l2_len); if (acl_len == l2_len) { process_l2cap(bthost, conn, acl_hdr->data, acl_len); break; } new_recv_data(conn, l2_len); /* fall through */ case 0x01: /* continuing fragment */ append_acl_data(bthost, conn, flags, acl_hdr->data, acl_len); break; case 0x03: /* complete automatically-flushable PDU */ process_l2cap(bthost, conn, acl_hdr->data, acl_len); break; default: bthost_debug(bthost, "Invalid ACL frame flags 0x%2.2x", flags); } } static void process_sco(struct bthost *bthost, const void *data, uint16_t len) { const struct bt_hci_sco_hdr *sco_hdr = data; uint16_t handle, sco_len; uint8_t status; struct btconn *conn; struct sco_hook *hook; sco_len = le16_to_cpu(sco_hdr->dlen); if (len != sizeof(*sco_hdr) + sco_len) return; handle = acl_handle(sco_hdr->handle); status = sco_flags_status(acl_flags(sco_hdr->handle)); conn = bthost_find_conn(bthost, handle); if (!conn) { bthost_debug(bthost, "Unknown handle: 0x%4.4x", handle); return; } bthost_debug(bthost, "SCO data: %u bytes", sco_len); hook = conn->sco_hook; if (!hook) return; hook->func(sco_hdr->data, sco_len, status, hook->user_data); } static void process_iso_data(struct bthost *bthost, struct btconn *conn, const void *data, uint16_t len) { const struct bt_hci_iso_data_start *data_hdr = data; uint16_t data_len; struct iso_hook *hook; data_len = le16_to_cpu(data_hdr->slen); if (len != sizeof(*data_hdr) + data_len) { bthost_debug(bthost, "ISO invalid length: %u != %zu", len, sizeof(*data_hdr) + data_len); return; } bthost_debug(bthost, "ISO data: %u bytes (%u)", data_len, data_hdr->sn); hook = conn->iso_hook; if (!hook) return; hook->func(data_hdr->data, data_len, hook->user_data); } static void append_iso_data(struct bthost *bthost, struct btconn *conn, uint8_t flags, const void *data, uint16_t len) { append_recv_data(bthost, conn, "ISO", flags, data, len); if (conn->recv_len < conn->data_len) { if (flags == 0x03) { bthost_debug(bthost, "Unexpected ISO end frame"); free_recv_data(conn); } return; } process_iso_data(bthost, conn, conn->recv_data, conn->recv_len); free_recv_data(conn); } static void process_iso(struct bthost *bthost, const void *data, uint16_t len) { const struct bt_hci_iso_hdr *iso_hdr = data; const struct bt_hci_iso_data_start *data_hdr; uint16_t handle, iso_len, data_len; uint8_t flags; struct btconn *conn; iso_len = le16_to_cpu(iso_hdr->dlen); if (len != sizeof(*iso_hdr) + iso_len) return; handle = acl_handle(iso_hdr->handle); flags = iso_flags_pb(acl_flags(iso_hdr->handle)); conn = bthost_find_conn(bthost, handle); if (!conn) { bthost_debug(bthost, "Unknown handle: 0x%4.4x", handle); return; } data_hdr = (void *) data + sizeof(*iso_hdr); switch (flags) { case 0x00: case 0x02: if (conn->recv_data) { bthost_debug(bthost, "Unexpected ISO start frame"); free_recv_data(conn); } data_len = le16_to_cpu(data_hdr->slen) + sizeof(*data_hdr); bthost_debug(bthost, "iso_len %u data_len %u", iso_len, data_len); if (iso_len == data_len) { process_iso_data(bthost, conn, iso_hdr->data, iso_len); break; } new_recv_data(conn, data_len); /* fall through */ case 0x01: case 0x03: append_iso_data(bthost, conn, flags, iso_hdr->data, iso_len); break; default: bthost_debug(bthost, "Invalid ISO frame flags 0x%2.2x", flags); } } void bthost_receive_h4(struct bthost *bthost, const void *data, uint16_t len) { uint8_t pkt_type; if (!bthost) return; if (len < 1) return; util_hexdump('>', data, len, bthost->debug_callback, bthost->debug_data); pkt_type = ((const uint8_t *) data)[0]; switch (pkt_type) { case BT_H4_EVT_PKT: process_evt(bthost, data + 1, len - 1); break; case BT_H4_ACL_PKT: process_acl(bthost, data + 1, len - 1); break; case BT_H4_SCO_PKT: process_sco(bthost, data + 1, len - 1); break; case BT_H4_ISO_PKT: process_iso(bthost, data + 1, len - 1); break; default: bthost_debug(bthost, "Unsupported packet 0x%2.2x", pkt_type); break; } } void bthost_set_cmd_complete_cb(struct bthost *bthost, bthost_cmd_complete_cb cb, void *user_data) { bthost->cmd_complete_cb = cb; bthost->cmd_complete_data = user_data; } void bthost_set_connect_cb(struct bthost *bthost, bthost_new_conn_cb cb, void *user_data) { bthost->new_conn_cb = cb; bthost->new_conn_data = user_data; } void bthost_set_sco_cb(struct bthost *bthost, bthost_new_conn_cb cb, void *user_data) { bthost->new_sco_cb = cb; bthost->new_sco_data = user_data; } void bthost_set_iso_cb(struct bthost *bthost, bthost_accept_conn_cb accept, bthost_new_conn_cb cb, void *user_data) { bthost->accept_iso_cb = accept; bthost->new_iso_cb = cb; bthost->new_iso_data = user_data; } void bthost_hci_connect(struct bthost *bthost, const uint8_t *bdaddr, uint8_t addr_type) { bthost->conn_init = true; if (addr_type == BDADDR_BREDR) { struct bt_hci_cmd_create_conn cc; memset(&cc, 0, sizeof(cc)); memcpy(cc.bdaddr, bdaddr, sizeof(cc.bdaddr)); send_command(bthost, BT_HCI_CMD_CREATE_CONN, &cc, sizeof(cc)); } else { struct bt_hci_cmd_le_create_conn cc; memset(&cc, 0, sizeof(cc)); memcpy(cc.peer_addr, bdaddr, sizeof(cc.peer_addr)); if (addr_type == BDADDR_LE_RANDOM) cc.peer_addr_type = 0x01; cc.scan_interval = cpu_to_le16(0x0060); cc.scan_window = cpu_to_le16(0x0030); cc.min_interval = cpu_to_le16(0x0028); cc.max_interval = cpu_to_le16(0x0038); cc.supv_timeout = cpu_to_le16(0x002a); send_command(bthost, BT_HCI_CMD_LE_CREATE_CONN, &cc, sizeof(cc)); } } void bthost_hci_ext_connect(struct bthost *bthost, const uint8_t *bdaddr, uint8_t addr_type) { struct bt_hci_cmd_le_ext_create_conn *cc; struct bt_hci_le_ext_create_conn *cp; uint8_t buf[sizeof(*cc) + sizeof(*cp)]; cc = (void *)buf; cp = (void *)cc->data; bthost->conn_init = true; memset(cc, 0, sizeof(*cc)); memset(cp, 0, sizeof(*cp)); memcpy(cc->peer_addr, bdaddr, sizeof(cc->peer_addr)); if (addr_type == BDADDR_LE_RANDOM) cc->peer_addr_type = 0x01; cc->phys = 0x01; cp->scan_interval = cpu_to_le16(0x0060); cp->scan_window = cpu_to_le16(0x0030); cp->min_interval = cpu_to_le16(0x0028); cp->max_interval = cpu_to_le16(0x0038); cp->supv_timeout = cpu_to_le16(0x002a); send_command(bthost, BT_HCI_CMD_LE_EXT_CREATE_CONN, buf, sizeof(buf)); } void bthost_hci_disconnect(struct bthost *bthost, uint16_t handle, uint8_t reason) { struct bt_hci_cmd_disconnect disc; disc.handle = cpu_to_le16(handle); disc.reason = reason; send_command(bthost, BT_HCI_CMD_DISCONNECT, &disc, sizeof(disc)); } void bthost_write_scan_enable(struct bthost *bthost, uint8_t scan) { send_command(bthost, BT_HCI_CMD_WRITE_SCAN_ENABLE, &scan, 1); } void bthost_set_adv_data(struct bthost *bthost, const uint8_t *data, uint8_t len) { struct bt_hci_cmd_le_set_adv_data adv_cp; memset(adv_cp.data, 0, 31); if (len) { adv_cp.len = len; memcpy(adv_cp.data, data, len); } send_command(bthost, BT_HCI_CMD_LE_SET_ADV_DATA, &adv_cp, sizeof(adv_cp)); } void bthost_set_ext_adv_data(struct bthost *bthost, const uint8_t *data, uint8_t len) { struct bt_hci_cmd_le_set_ext_adv_data *adv_cp; uint8_t buf[sizeof(*adv_cp) + 31]; adv_cp = (void *)buf; memset(adv_cp, 0, sizeof(*adv_cp)); memset(adv_cp->data, 0, 31); adv_cp->handle = 1; adv_cp->operation = 0x03; adv_cp->fragment_preference = 0x01; if (len) { adv_cp->data_len = len; memcpy(adv_cp->data, data, len); } send_command(bthost, BT_HCI_CMD_LE_SET_EXT_ADV_DATA, buf, sizeof(buf)); } void bthost_set_adv_enable(struct bthost *bthost, uint8_t enable) { struct bt_hci_cmd_le_set_adv_parameters cp; memset(&cp, 0, sizeof(cp)); send_command(bthost, BT_HCI_CMD_LE_SET_ADV_PARAMETERS, &cp, sizeof(cp)); send_command(bthost, BT_HCI_CMD_LE_SET_ADV_ENABLE, &enable, 1); } void bthost_set_scan_params(struct bthost *bthost, uint8_t scan_type, uint8_t addr_type, uint8_t filter_policy) { struct bt_hci_cmd_le_set_scan_parameters cp; memset(&cp, 0, sizeof(cp)); cp.type = scan_type; cp.own_addr_type = addr_type; cp.filter_policy = filter_policy; send_command(bthost, BT_HCI_CMD_LE_SET_SCAN_PARAMETERS, &cp, sizeof(cp)); } void bthost_set_scan_enable(struct bthost *bthost, uint8_t enable) { struct bt_hci_cmd_le_set_scan_enable cp; memset(&cp, 0, sizeof(cp)); cp.enable = enable; send_command(bthost, BT_HCI_CMD_LE_SET_SCAN_ENABLE, &cp, sizeof(cp)); } void bthost_set_ext_adv_params(struct bthost *bthost) { const uint8_t interval_20ms[] = { 0x20, 0x00, 0x00 }; struct bt_hci_cmd_le_set_ext_adv_params cp; memset(&cp, 0, sizeof(cp)); cp.handle = 0x01; cp.evt_properties = cpu_to_le16(0x0013); memcpy(cp.min_interval, interval_20ms, sizeof(cp.min_interval)); memcpy(cp.max_interval, interval_20ms, sizeof(cp.max_interval)); send_command(bthost, BT_HCI_CMD_LE_SET_EXT_ADV_PARAMS, &cp, sizeof(cp)); } void bthost_set_ext_adv_enable(struct bthost *bthost, uint8_t enable) { struct bt_hci_cmd_le_set_ext_adv_enable *cp_enable; struct bt_hci_cmd_ext_adv_set *cp_set; uint8_t cp[6]; memset(cp, 0, 6); cp_enable = (struct bt_hci_cmd_le_set_ext_adv_enable *)cp; cp_set = (struct bt_hci_cmd_ext_adv_set *)(cp + sizeof(*cp_enable)); cp_enable->enable = enable; cp_enable->num_of_sets = 1; cp_set->handle = 1; send_command(bthost, BT_HCI_CMD_LE_SET_EXT_ADV_ENABLE, cp, 6); } void bthost_set_pa_params(struct bthost *bthost) { struct bt_hci_cmd_le_set_pa_params cp; memset(&cp, 0, sizeof(cp)); cp.handle = 0x01; send_command(bthost, BT_HCI_CMD_LE_SET_PA_PARAMS, &cp, sizeof(cp)); } static void set_pa_data(struct bthost *bthost, const uint8_t *data, uint8_t len, uint8_t offset) { struct bt_hci_cmd_le_set_pa_data *cp; uint8_t buf[sizeof(*cp) + BT_PA_MAX_DATA_LEN]; size_t data_len; cp = (void *)buf; memset(cp, 0, sizeof(*cp)); memset(cp->data, 0, BT_PA_MAX_DATA_LEN); cp->handle = 1; if (len - offset > BT_PA_MAX_DATA_LEN) { data_len = BT_PA_MAX_DATA_LEN; if (!offset) cp->operation = 0x01; else cp->operation = 0x00; } else { data_len = len - offset; if (!offset) cp->operation = 0x03; else cp->operation = 0x02; } memcpy(cp->data, data + offset, data_len); cp->data_len = data_len; send_command(bthost, BT_HCI_CMD_LE_SET_PA_DATA, buf, sizeof(*cp) + cp->data_len); if (cp->operation == 0x01 || cp->operation == 0x00) { offset += cp->data_len; set_pa_data(bthost, data, len, offset); } } void bthost_set_pa_data(struct bthost *bthost, const uint8_t *data, uint8_t len) { set_pa_data(bthost, data, len, 0); } void bthost_set_base(struct bthost *bthost, const uint8_t *data, uint8_t len) { struct bt_ad *ad; bt_uuid_t uuid; uint8_t *pa_data; size_t pa_len; bt_uuid16_create(&uuid, BAA_SERVICE); ad = bt_ad_new(); bt_ad_set_max_len(ad, BT_PA_MAX_DATA_LEN); bt_ad_add_service_data(ad, &uuid, (void *)data, len); pa_data = bt_ad_generate(ad, &pa_len); bthost_set_pa_data(bthost, pa_data, pa_len); bt_ad_unref(ad); } void bthost_set_pa_enable(struct bthost *bthost, uint8_t enable) { struct bt_hci_cmd_le_set_pa_enable cp; memset(&cp, 0, sizeof(cp)); cp.enable = enable; cp.handle = 0x01; send_command(bthost, BT_HCI_CMD_LE_SET_PA_ENABLE, &cp, sizeof(cp)); } void bthost_create_big(struct bthost *bthost, uint8_t num_bis, uint8_t enc, const uint8_t *bcode) { struct bt_hci_cmd_le_create_big cp; memset(&cp, 0, sizeof(cp)); cp.handle = 0x01; cp.adv_handle = 0x01; cp.num_bis = num_bis; put_le24(10000, cp.bis.sdu_interval); cp.bis.sdu = 40; cp.bis.latency = cpu_to_le16(10); cp.bis.rtn = 0x02; cp.bis.phy = 0x02; cp.bis.encryption = enc; memcpy(cp.bis.bcode, bcode, sizeof(cp.bis.bcode)); send_command(bthost, BT_HCI_CMD_LE_CREATE_BIG, &cp, sizeof(cp)); } bool bthost_search_ext_adv_addr(struct bthost *bthost, const uint8_t *addr) { const struct queue_entry *entry; if (queue_isempty(bthost->le_ext_adv)) return false; for (entry = queue_get_entries(bthost->le_ext_adv); entry; entry = entry->next) { struct le_ext_adv *le_ext_adv = entry->data; if (!memcmp(le_ext_adv->addr, addr, 6)) return true; } return false; } void bthost_set_cig_params(struct bthost *bthost, uint8_t cig_id, uint8_t cis_id, const struct bt_iso_qos *qos) { struct bt_hci_cmd_le_set_cig_params *cp; cp = malloc(sizeof(*cp) + sizeof(*cp->cis)); memset(cp, 0, sizeof(*cp) + sizeof(*cp->cis)); cp->cig_id = cig_id; put_le24(qos->ucast.in.interval ? qos->ucast.in.interval : qos->ucast.out.interval, cp->c_interval); put_le24(qos->ucast.out.interval ? qos->ucast.out.interval : qos->ucast.in.interval, cp->p_interval); cp->c_latency = cpu_to_le16(qos->ucast.in.latency ? qos->ucast.in.latency : qos->ucast.out.latency); cp->p_latency = cpu_to_le16(qos->ucast.out.latency ? qos->ucast.out.latency : qos->ucast.in.latency); cp->num_cis = 0x01; cp->cis[0].cis_id = cis_id; cp->cis[0].c_sdu = qos->ucast.in.sdu; cp->cis[0].p_sdu = qos->ucast.out.sdu; cp->cis[0].c_phy = qos->ucast.in.phy ? qos->ucast.in.phy : qos->ucast.out.phy; cp->cis[0].p_phy = qos->ucast.out.phy ? qos->ucast.out.phy : qos->ucast.in.phy; cp->cis[0].c_rtn = qos->ucast.in.rtn; cp->cis[0].p_rtn = qos->ucast.out.rtn; send_command(bthost, BT_HCI_CMD_LE_SET_CIG_PARAMS, cp, sizeof(*cp) + sizeof(*cp->cis)); free(cp); } void bthost_create_cis(struct bthost *bthost, uint16_t cis_handle, uint16_t acl_handle) { struct bt_hci_cmd_le_create_cis *cp; cp = malloc(sizeof(*cp) + sizeof(*cp->cis)); memset(cp, 0, sizeof(*cp) + sizeof(*cp->cis)); cp->num_cis = 0x01; cp->cis[0].cis_handle = cpu_to_le16(cis_handle); cp->cis[0].acl_handle = cpu_to_le16(acl_handle); send_command(bthost, BT_HCI_CMD_LE_CREATE_CIS, cp, sizeof(*cp) + sizeof(*cp->cis)); free(cp); } void bthost_write_ssp_mode(struct bthost *bthost, uint8_t mode) { send_command(bthost, BT_HCI_CMD_WRITE_SIMPLE_PAIRING_MODE, &mode, 1); } void bthost_write_le_host_supported(struct bthost *bthost, uint8_t mode) { struct bt_hci_cmd_write_le_host_supported cmd; bthost->le = mode; memset(&cmd, 0, sizeof(cmd)); cmd.supported = mode; send_command(bthost, BT_HCI_CMD_WRITE_LE_HOST_SUPPORTED, &cmd, sizeof(cmd)); } bool bthost_bredr_capable(struct bthost *bthost) { return lmp_bredr_capable(bthost); } void bthost_request_auth(struct bthost *bthost, uint16_t handle) { struct btconn *conn; conn = bthost_find_conn(bthost, handle); if (!conn) return; if (conn->addr_type == BDADDR_BREDR) { struct bt_hci_cmd_auth_requested cp; cp.handle = cpu_to_le16(handle); send_command(bthost, BT_HCI_CMD_AUTH_REQUESTED, &cp, sizeof(cp)); } else { uint8_t auth_req = bthost->auth_req; if (bthost->sc) auth_req |= 0x08; smp_pair(conn->smp_data, bthost->io_capability, auth_req); } } void bthost_le_start_encrypt(struct bthost *bthost, uint16_t handle, const uint8_t ltk[16]) { struct bt_hci_cmd_le_start_encrypt cmd; memset(&cmd, 0, sizeof(cmd)); cmd.handle = htobs(handle); memcpy(cmd.ltk, ltk, 16); send_command(bthost, BT_HCI_CMD_LE_START_ENCRYPT, &cmd, sizeof(cmd)); } uint64_t bthost_conn_get_fixed_chan(struct bthost *bthost, uint16_t handle) { struct btconn *conn; conn = bthost_find_conn(bthost, handle); if (!conn) return 0; return conn->fixed_chan; } void bthost_add_l2cap_server_custom(struct bthost *bthost, uint16_t psm, uint16_t mtu, uint16_t mps, uint16_t credits, bthost_l2cap_connect_cb func, bthost_l2cap_disconnect_cb disconn_func, void *user_data) { struct l2cap_conn_cb_data *data; data = malloc(sizeof(struct l2cap_conn_cb_data)); if (!data) return; data->psm = psm; data->mtu = mtu; data->mps = mps; data->credits = credits; data->user_data = user_data; data->func = func; data->disconn_func = disconn_func; data->next = bthost->new_l2cap_conn_data; bthost->new_l2cap_conn_data = data; } void bthost_add_l2cap_server(struct bthost *bthost, uint16_t psm, bthost_l2cap_connect_cb func, bthost_l2cap_disconnect_cb disconn_func, void *user_data) { bthost_add_l2cap_server_custom(bthost, psm, 0, 0, 0, func, disconn_func, user_data); } void bthost_set_sc_support(struct bthost *bthost, bool enable) { struct bt_hci_cmd_write_secure_conn_support cmd; bthost->sc = enable; if (!lmp_bredr_capable(bthost)) return; cmd.support = enable; send_command(bthost, BT_HCI_CMD_WRITE_SECURE_CONN_SUPPORT, &cmd, sizeof(cmd)); } void bthost_set_pin_code(struct bthost *bthost, const uint8_t *pin, uint8_t pin_len) { memcpy(bthost->pin, pin, pin_len); bthost->pin_len = pin_len; } void bthost_set_io_capability(struct bthost *bthost, uint8_t io_capability) { bthost->io_capability = io_capability; } uint8_t bthost_get_io_capability(struct bthost *bthost) { return bthost->io_capability; } void bthost_set_auth_req(struct bthost *bthost, uint8_t auth_req) { bthost->auth_req = auth_req; } uint8_t bthost_get_auth_req(struct bthost *bthost) { uint8_t auth_req = bthost->auth_req; if (bthost->sc) auth_req |= 0x08; return auth_req; } void bthost_set_reject_user_confirm(struct bthost *bthost, bool reject) { bthost->reject_user_confirm = reject; } bool bthost_get_reject_user_confirm(struct bthost *bthost) { return bthost->reject_user_confirm; } void bthost_add_rfcomm_server(struct bthost *bthost, uint8_t channel, bthost_rfcomm_connect_cb func, void *user_data) { struct rfcomm_conn_cb_data *data; data = malloc(sizeof(struct rfcomm_conn_cb_data)); if (!data) return; data->channel = channel; data->user_data = user_data; data->func = func; data->next = bthost->new_rfcomm_conn_data; bthost->new_rfcomm_conn_data = data; } void bthost_start(struct bthost *bthost) { if (!bthost) return; bthost->ncmd = 1; send_command(bthost, BT_HCI_CMD_RESET, NULL, 0); send_command(bthost, BT_HCI_CMD_READ_LOCAL_FEATURES, NULL, 0); send_command(bthost, BT_HCI_CMD_READ_BD_ADDR, NULL, 0); } bool bthost_connect_rfcomm(struct bthost *bthost, uint16_t handle, uint8_t channel, bthost_rfcomm_connect_cb func, void *user_data) { struct rfcomm_connection_data *data; struct bt_l2cap_pdu_conn_req req; struct btconn *conn; if (bthost->rfcomm_conn_data) return false; conn = bthost_find_conn(bthost, handle); if (!conn) return false; data = malloc(sizeof(struct rfcomm_connection_data)); if (!data) return false; data->channel = channel; data->conn = conn; data->cb = func; data->user_data = user_data; bthost->rfcomm_conn_data = data; req.psm = cpu_to_le16(0x0003); req.scid = cpu_to_le16(conn->next_cid++); return bthost_l2cap_req(bthost, handle, BT_L2CAP_PDU_CONN_REQ, &req, sizeof(req), NULL, NULL); } void bthost_add_rfcomm_chan_hook(struct bthost *bthost, uint16_t handle, uint8_t channel, bthost_rfcomm_chan_hook_func_t func, void *user_data) { struct rfcomm_chan_hook *hook; struct btconn *conn; conn = bthost_find_conn(bthost, handle); if (!conn) return; hook = malloc(sizeof(*hook)); if (!hook) return; memset(hook, 0, sizeof(*hook)); hook->channel = channel; hook->func = func; hook->user_data = user_data; hook->next = conn->rfcomm_chan_hooks; conn->rfcomm_chan_hooks = hook; } void bthost_send_rfcomm_data(struct bthost *bthost, uint16_t handle, uint8_t channel, const void *data, uint16_t len) { struct btconn *conn; struct rcconn *rcconn; struct rfcomm_hdr *hdr; uint8_t *uih_frame; uint16_t uih_len; conn = bthost_find_conn(bthost, handle); if (!conn) return; rcconn = btconn_find_rfcomm_conn_by_channel(conn, channel); if (!rcconn) return; if (len > 127) uih_len = len + sizeof(struct rfcomm_cmd) + sizeof(uint8_t); else uih_len = len + sizeof(struct rfcomm_cmd); uih_frame = malloc(uih_len); if (!uih_frame) return; hdr = (struct rfcomm_hdr *) uih_frame; hdr->address = RFCOMM_ADDR(1, channel * 2); hdr->control = RFCOMM_CTRL(RFCOMM_UIH, 0); if (len > 127) { hdr->length = RFCOMM_LEN16(cpu_to_le16(sizeof(*hdr) + len)); memcpy(uih_frame + sizeof(*hdr) + 1, data, len); } else { hdr->length = RFCOMM_LEN8(sizeof(*hdr) + len); memcpy(uih_frame + sizeof(*hdr), data, len); } uih_frame[uih_len - 1] = rfcomm_fcs((void *)hdr); send_acl(bthost, handle, rcconn->scid, false, uih_frame, uih_len); free(uih_frame); } bluez-5.82/emulator/PaxHeaders/b1ee.c0000644000000000000000000000005014015011623014447 xustar0020 atime=1743516867 20 ctime=1743591281 bluez-5.82/emulator/b1ee.c0000644000000000000000000001510314015011623014130 0ustar00rootroot// SPDX-License-Identifier: GPL-2.0-or-later /* * * BlueZ - Bluetooth protocol stack for Linux * * Copyright (C) 2011-2012 Intel Corporation * Copyright (C) 2004-2010 Marcel Holtmann * * */ #ifdef HAVE_CONFIG_H #include #endif #define _GNU_SOURCE #include #include #include #include #include #include #include #include #include #include #include "lib/bluetooth.h" #include "lib/hci.h" #include "src/shared/mainloop.h" #define DEFAULT_HOST_PORT "45550" /* 0xb1ee */ #define DEFAULT_SNIFFER_PORT "45551" /* 0xb1ef */ static int sniffer_fd; static int server_fd; static int vhci_fd; static void usage(void) { printf("b1ee - Bluetooth device testing tool over internet\n" "Usage:\n"); printf("\tb1ee [options] \n"); printf("options:\n" "\t-p, --port Specify the server port\n" "\t-s, --sniffer-port Specify the sniffer port\n" "\t-v, --version Show version information\n" "\t-h, --help Show help options\n"); } static const struct option main_options[] = { { "port", required_argument, NULL, 'p' }, { "sniffer-port", required_argument, NULL, 's' }, { "version", no_argument, NULL, 'v' }, { "help", no_argument, NULL, 'h' }, { } }; static char *set_port(char *str) { char *c; if (str == NULL || str[0] == '\0') return NULL; for (c = str; *c != '\0'; c++) if (isdigit(*c) == 0) return NULL; if (atol(str) > 65535) return NULL; return strdup(str); } static void sniffer_read_callback(int fd, uint32_t events, void *user_data) { static uint8_t buf[4096]; ssize_t len; if (events & (EPOLLERR | EPOLLHUP)) return; again: len = recv(fd, buf, sizeof(buf), MSG_DONTWAIT); if (len < 0) { if (errno == EAGAIN) goto again; return; } printf("Sniffer received: %zi bytes\n", len); } static uint8_t *server_pkt_data; static uint8_t server_pkt_type; static uint16_t server_pkt_expect; static uint16_t server_pkt_len; static uint16_t server_pkt_offset; static void server_read_callback(int fd, uint32_t events, void *user_data) { static uint8_t buf[4096]; uint8_t *ptr = buf; ssize_t len; uint16_t count; if (events & (EPOLLERR | EPOLLHUP)) return; again: len = recv(fd, buf + server_pkt_offset, sizeof(buf) - server_pkt_offset, MSG_DONTWAIT); if (len < 0) { if (errno == EAGAIN) goto again; return; } count = server_pkt_offset + len; while (count > 0) { hci_event_hdr *evt_hdr; if (!server_pkt_data) { server_pkt_type = ptr[0]; switch (server_pkt_type) { case HCI_EVENT_PKT: if (count < HCI_EVENT_HDR_SIZE + 1) { server_pkt_offset += len; return; } evt_hdr = (hci_event_hdr *) (ptr + 1); server_pkt_expect = HCI_EVENT_HDR_SIZE + evt_hdr->plen + 1; server_pkt_data = malloc(server_pkt_expect); server_pkt_len = 0; break; default: fprintf(stderr, "Unknown packet from server\n"); return; } server_pkt_offset = 0; } if (count >= server_pkt_expect) { ssize_t written; memcpy(server_pkt_data + server_pkt_len, ptr, server_pkt_expect); ptr += server_pkt_expect; count -= server_pkt_expect; written = write(vhci_fd, server_pkt_data, server_pkt_len + server_pkt_expect); if (written != server_pkt_len + server_pkt_expect) fprintf(stderr, "Write to /dev/vhci failed\n"); free(server_pkt_data); server_pkt_data = NULL; } else { memcpy(server_pkt_data + server_pkt_len, ptr, count); server_pkt_len += count; server_pkt_expect -= count; count = 0; } } } static void vhci_read_callback(int fd, uint32_t events, void *user_data) { unsigned char buf[4096]; ssize_t len, written; if (events & (EPOLLERR | EPOLLHUP)) return; len = read(fd, buf, sizeof(buf)); if (len < 0) return; written = write(server_fd, buf, len); if (written != len) fprintf(stderr, "Write to server failed\n"); } static void signal_callback(int signum, void *user_data) { switch (signum) { case SIGINT: case SIGTERM: mainloop_quit(); break; } } static int do_connect(const char *node, const char *service) { struct addrinfo hints; struct addrinfo *info, *res; int err, fd = -1; memset(&hints, 0, sizeof(hints)); hints.ai_family = PF_UNSPEC; hints.ai_socktype = SOCK_STREAM; err = getaddrinfo(node, service, &hints, &res); if (err) { perror(gai_strerror(err)); exit(1); } for (info = res; info; info = info->ai_next) { char str[INET6_ADDRSTRLEN]; inet_ntop(info->ai_family, info->ai_addr->sa_data, str, sizeof(str)); fd = socket(info->ai_family, info->ai_socktype, info->ai_protocol); if (fd < 0) continue; printf("Trying to connect to %s on port %s\n", str, service); if (connect(fd, res->ai_addr, res->ai_addrlen) < 0) { perror("Failed to connect"); close(fd); continue; } printf("Successfully connected to %s on port %s\n", str, service); break; } freeaddrinfo(res); if (res == NULL) exit(1); return fd; } int main(int argc, char *argv[]) { const char sniff_cmd[] = { 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; char *server_port = NULL, *sniffer_port = NULL; int ret = EXIT_FAILURE; ssize_t written; for (;;) { int opt; opt = getopt_long(argc, argv, "s:p:vh", main_options, NULL); if (opt < 0) break; switch (opt) { case 'p': server_port = set_port(optarg); if (server_port == NULL) goto usage; break; case 's': sniffer_port = set_port(optarg); if (sniffer_port == NULL) goto usage; break; case 'v': printf("%s\n", VERSION); ret = EXIT_SUCCESS; goto done; case 'h': ret = EXIT_SUCCESS; goto usage; default: goto usage; } } argc = argc - optind; argv = argv + optind; optind = 0; if (argv[0] == NULL || argv[0][0] == '\0') goto usage; server_fd = do_connect(argv[0], server_port ? : DEFAULT_HOST_PORT); sniffer_fd = do_connect(argv[0], sniffer_port ? : DEFAULT_SNIFFER_PORT); written = write(sniffer_fd, sniff_cmd, sizeof(sniff_cmd)); if (written < 0) perror("Failed to enable sniffer"); vhci_fd = open("/dev/vhci", O_RDWR | O_NONBLOCK); if (vhci_fd < 0) { perror("Failed to /dev/vhci"); close(server_fd); exit(1); } mainloop_init(); mainloop_add_fd(sniffer_fd, EPOLLIN, sniffer_read_callback, NULL, NULL); mainloop_add_fd(server_fd, EPOLLIN, server_read_callback, NULL, NULL); mainloop_add_fd(vhci_fd, EPOLLIN, vhci_read_callback, NULL, NULL); ret = mainloop_run_with_signal(signal_callback, NULL); goto done; usage: usage(); done: free(server_port); free(sniffer_port); return ret; } bluez-5.82/emulator/PaxHeaders/serial.h0000644000000000000000000000005014015011623015117 xustar0020 atime=1743516867 20 ctime=1743591281 bluez-5.82/emulator/serial.h0000644000000000000000000000071414015011623014602 0ustar00rootroot/* SPDX-License-Identifier: LGPL-2.1-or-later */ /* * * BlueZ - Bluetooth protocol stack for Linux * * Copyright (C) 2011-2014 Intel Corporation * Copyright (C) 2004-2010 Marcel Holtmann * * */ #include enum serial_type { SERIAL_TYPE_BREDRLE, SERIAL_TYPE_BREDR, SERIAL_TYPE_LE, SERIAL_TYPE_AMP, }; struct serial; struct serial *serial_open(enum serial_type type); void serial_close(struct serial *serial); bluez-5.82/emulator/PaxHeaders/vhci.h0000644000000000000000000000005014471706457014617 xustar0020 atime=1743516864 20 ctime=1743591278 bluez-5.82/emulator/vhci.h0000644000000000000000000000224514471706457014303 0ustar00rootroot/* SPDX-License-Identifier: LGPL-2.1-or-later */ /* * * BlueZ - Bluetooth protocol stack for Linux * * Copyright (C) 2011-2014 Intel Corporation * Copyright (C) 2004-2010 Marcel Holtmann * * */ #include struct vhci; typedef void (*vhci_debug_func_t)(const char *str, void *user_data); typedef void (*vhci_destroy_func_t)(void *user_data); bool vhci_set_debug(struct vhci *vhci, vhci_debug_func_t callback, void *user_data, vhci_destroy_func_t destroy); struct vhci *vhci_open(uint8_t type); void vhci_close(struct vhci *vhci); struct btdev *vhci_get_btdev(struct vhci *vhci); int vhci_set_force_suspend(struct vhci *vhci, bool enable); int vhci_set_force_wakeup(struct vhci *vhci, bool enable); int vhci_set_msft_opcode(struct vhci *vhci, uint16_t opcode); int vhci_set_aosp_capable(struct vhci *vhci, bool enable); int vhci_set_emu_opcode(struct vhci *vhci, uint16_t opcode); int vhci_set_force_static_address(struct vhci *vhci, bool enable); int vhci_force_devcd(struct vhci *vhci, const void *data, size_t len); int vhci_read_devcd(struct vhci *vhci, void *buf, size_t size); bool vhci_pause_input(struct vhci *vhci, bool paused); bluez-5.82/emulator/PaxHeaders/phy.h0000644000000000000000000000005014015011623014440 xustar0020 atime=1743516867 20 ctime=1743591281 bluez-5.82/emulator/phy.h0000644000000000000000000000263314015011623014125 0ustar00rootroot/* SPDX-License-Identifier: LGPL-2.1-or-later */ /* * * BlueZ - Bluetooth protocol stack for Linux * * Copyright (C) 2011-2012 Intel Corporation * Copyright (C) 2004-2010 Marcel Holtmann * * */ #include #include struct bt_phy; struct bt_phy *bt_phy_new(void); struct bt_phy *bt_phy_ref(struct bt_phy *phy); void bt_phy_unref(struct bt_phy *phy); bool bt_phy_send(struct bt_phy *phy, uint16_t type, const void *data, size_t size); bool bt_phy_send_vector(struct bt_phy *phy, uint16_t type, const void *data1, size_t size1, const void *data2, size_t size2, const void *data3, size_t size3); typedef void (*bt_phy_callback_func_t)(uint16_t type, const void *data, size_t size, void *user_data); bool bt_phy_register(struct bt_phy *phy, bt_phy_callback_func_t callback, void *user_data); #define BT_PHY_PKT_NULL 0x0000 #define BT_PHY_PKT_ADV 0x0001 struct bt_phy_pkt_adv { uint8_t chan_idx; uint8_t pdu_type; uint8_t tx_addr_type; uint8_t tx_addr[6]; uint8_t rx_addr_type; uint8_t rx_addr[6]; uint8_t adv_data_len; uint8_t scan_rsp_len; } __attribute__ ((packed)); #define BT_PHY_PKT_CONN 0x0002 struct bt_phy_pkt_conn { uint8_t chan_idx; uint8_t link_type; uint8_t tx_addr_type; uint8_t tx_addr[6]; uint8_t rx_addr_type; uint8_t rx_addr[6]; uint8_t features[8]; uint8_t id; } __attribute__ ((packed)); bluez-5.82/emulator/PaxHeaders/btdev.c0000644000000000000000000000005014766002272014754 xustar0020 atime=1743515578 20 ctime=1743591278 bluez-5.82/emulator/btdev.c0000644000000000000000000063263514766002272014454 0ustar00rootroot// SPDX-License-Identifier: LGPL-2.1-or-later /* * * BlueZ - Bluetooth protocol stack for Linux * * Copyright (C) 2011-2012 Intel Corporation * Copyright (C) 2004-2010 Marcel Holtmann * Copyright 2023-2024 NXP * * */ #ifdef HAVE_CONFIG_H #include #endif #include #include #include #include #include #include #include #include #include #include #include "lib/bluetooth.h" #include "lib/hci.h" #include "src/shared/util.h" #include "src/shared/timeout.h" #include "src/shared/crypto.h" #include "src/shared/ecc.h" #include "src/shared/queue.h" #include "monitor/bt.h" #include "monitor/msft.h" #include "monitor/emulator.h" #include "btdev.h" #define AL_SIZE 16 #define RL_SIZE 16 #define CIS_SIZE 3 #define BIS_SIZE 3 #define CIG_SIZE 3 #define MAX_PA_DATA_LEN 252 #define has_bredr(btdev) (!((btdev)->features[4] & 0x20)) #define has_le(btdev) (!!((btdev)->features[4] & 0x40)) #define ACL_HANDLE 42 #define ISO_HANDLE 257 #define SCO_HANDLE 257 #define SYNC_HANDLE 1 #define INV_HANDLE 0xffff struct hook { btdev_hook_func handler; void *user_data; enum btdev_hook_type type; uint16_t opcode; }; #define MAX_HOOK_ENTRIES 16 #define MAX_EXT_ADV_SETS 3 #define MAX_PENDING_CONN 16 struct btdev_conn { uint16_t handle; uint8_t type; struct btdev *dev; struct btdev_conn *link; void *data; }; struct btdev_al { uint8_t type; bdaddr_t addr; }; struct btdev_rl { uint8_t type; bdaddr_t addr; uint8_t mode; uint8_t peer_irk[16]; uint8_t local_irk[16]; }; struct le_ext_adv { struct btdev *dev; uint8_t handle; uint8_t enable; uint32_t interval; uint8_t type; /* evt_properties */ uint8_t own_addr_type; /* own_addr_type */ uint8_t direct_addr_type; /* peer_addr_type */ uint8_t direct_addr[6]; /* peer_addr */ uint8_t filter_policy; /* filter_policy */ uint8_t random_addr[6]; bool rpa; uint8_t adv_data[252]; uint8_t adv_data_len; uint8_t scan_data[252]; uint8_t scan_data_len; unsigned int broadcast_id; unsigned int timeout_id; }; struct le_per_adv { struct btdev *dev; uint8_t addr_type; uint8_t addr[6]; uint16_t sync_handle; }; struct le_big { struct btdev *dev; uint8_t handle; struct queue *bis; }; struct le_cig { struct bt_hci_cmd_le_set_cig_params params; struct bt_hci_cis_params cis[CIS_SIZE]; bool activated; } __attribute__ ((packed)); struct btdev { enum btdev_type type; uint16_t id; struct queue *conns; bool auth_init; uint8_t link_key[16]; uint16_t pin[16]; uint8_t pin_len; uint8_t io_cap; uint8_t auth_req; bool ssp_auth_complete; uint8_t ssp_status; btdev_command_func command_handler; void *command_data; btdev_send_func send_handler; void *send_data; unsigned int inquiry_id; unsigned int inquiry_timeout_id; struct hook *hook_list[MAX_HOOK_ENTRIES]; struct bt_crypto *crypto; uint16_t manufacturer; uint8_t version; uint16_t revision; uint8_t commands[64]; uint8_t max_page; uint8_t features[8]; uint8_t feat_page_2[8]; uint16_t acl_mtu; uint16_t acl_max_pkt; uint16_t sco_mtu; uint16_t sco_max_pkt; uint16_t iso_mtu; uint16_t iso_max_pkt; uint8_t country_code; uint8_t bdaddr[6]; uint8_t random_addr[6]; uint8_t le_features[8]; uint8_t le_states[8]; const struct btdev_cmd *cmds; uint16_t msft_opcode; const struct btdev_cmd *msft_cmds; uint16_t emu_opcode; const struct btdev_cmd *emu_cmds; bool aosp_capable; uint16_t default_link_policy; uint8_t event_mask[8]; uint8_t event_mask_page2[8]; uint8_t event_filter; uint8_t name[248]; uint8_t dev_class[3]; uint16_t voice_setting; uint16_t conn_accept_timeout; uint16_t page_timeout; uint8_t scan_enable; uint16_t page_scan_interval; uint16_t page_scan_window; uint16_t page_scan_type; uint8_t auth_enable; uint16_t inquiry_scan_interval; uint16_t inquiry_scan_window; uint8_t inquiry_mode; uint8_t afh_assessment_mode; uint8_t ext_inquiry_fec; uint8_t ext_inquiry_rsp[240]; uint8_t simple_pairing_mode; uint8_t ssp_debug_mode; uint8_t secure_conn_support; uint8_t host_flow_control; uint8_t sco_flowctl; uint8_t le_supported; uint8_t le_simultaneous; uint8_t le_event_mask[8]; uint8_t le_adv_data[31]; uint8_t le_adv_data_len; uint8_t le_adv_type; uint8_t le_adv_own_addr; uint8_t le_adv_direct_addr_type; uint8_t le_adv_direct_addr[6]; uint8_t le_adv_filter_policy; uint8_t le_scan_data[31]; uint8_t le_scan_data_len; uint8_t le_scan_enable; uint8_t le_scan_type; uint8_t le_scan_own_addr_type; uint8_t le_scan_filter_policy; uint8_t le_filter_dup; uint8_t le_adv_enable; uint8_t le_pa_enable; uint16_t le_pa_properties; uint16_t le_pa_min_interval; uint16_t le_pa_max_interval; uint8_t le_pa_data_len; uint8_t le_pa_data[MAX_PA_DATA_LEN]; uint8_t le_ltk[16]; struct le_cig le_cig[CIG_SIZE]; uint8_t le_iso_path[2]; /* Real time length of AL array */ uint8_t le_al_len; /* Real time length of RL array */ uint8_t le_rl_len; struct btdev_al le_al[AL_SIZE]; struct btdev_rl le_rl[RL_SIZE]; uint8_t le_rl_enable; uint16_t le_rl_timeout; struct btdev *pending_conn[MAX_PENDING_CONN]; uint8_t le_local_sk256[32]; uint16_t sync_train_interval; uint32_t sync_train_timeout; uint8_t sync_train_service_data; uint16_t le_ext_adv_type; struct queue *le_ext_adv; struct queue *le_per_adv; struct queue *le_big; btdev_debug_func_t debug_callback; btdev_destroy_func_t debug_destroy; void *debug_data; }; struct inquiry_data { struct btdev *btdev; int num_resp; int sent_count; int iter; }; #define DEFAULT_INQUIRY_INTERVAL 100 /* 100 miliseconds */ #define MAX_BTDEV_ENTRIES 16 static const uint8_t LINK_KEY_NONE[16] = { 0 }; static const uint8_t LINK_KEY_DUMMY[16] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5 }; static struct btdev *btdev_list[MAX_BTDEV_ENTRIES] = { }; static int get_hook_index(struct btdev *btdev, enum btdev_hook_type type, uint16_t opcode) { int i; for (i = 0; i < MAX_HOOK_ENTRIES; i++) { if (btdev->hook_list[i] == NULL) continue; if (btdev->hook_list[i]->type == type && btdev->hook_list[i]->opcode == opcode) return i; } return -1; } static bool run_hooks(struct btdev *btdev, enum btdev_hook_type type, uint16_t opcode, const void *data, uint16_t len) { int index = get_hook_index(btdev, type, opcode); if (index < 0) return true; return btdev->hook_list[index]->handler(data, len, btdev->hook_list[index]->user_data); } static inline int add_btdev(struct btdev *btdev) { int i, index = -1; for (i = 0; i < MAX_BTDEV_ENTRIES; i++) { if (btdev_list[i] == NULL) { index = i; btdev_list[index] = btdev; break; } } return index; } static inline int del_btdev(struct btdev *btdev) { int i, index = -1; for (i = 0; i < MAX_BTDEV_ENTRIES; i++) { if (btdev_list[i] == btdev) { index = i; btdev_list[index] = NULL; break; } } return index; } static inline bool valid_btdev(struct btdev *btdev) { int i; for (i = 0; i < MAX_BTDEV_ENTRIES; i++) { if (btdev_list[i] == btdev) return true; } return false; } static inline struct btdev *find_btdev_by_bdaddr(const uint8_t *bdaddr) { int i; for (i = 0; i < MAX_BTDEV_ENTRIES; i++) { if (btdev_list[i] && !memcmp(btdev_list[i]->bdaddr, bdaddr, 6)) return btdev_list[i]; } return NULL; } static bool match_adv_addr(const void *data, const void *match_data) { const struct le_ext_adv *adv = data; const uint8_t *bdaddr = match_data; return !memcmp(adv->random_addr, bdaddr, 6); } static inline struct btdev *find_btdev_by_bdaddr_type(const uint8_t *bdaddr, uint8_t bdaddr_type) { int i; for (i = 0; i < MAX_BTDEV_ENTRIES; i++) { struct btdev *dev = btdev_list[i]; int cmp; struct le_ext_adv *adv; if (!dev) continue; if (bdaddr_type == 0x01) cmp = memcmp(dev->random_addr, bdaddr, 6); else cmp = memcmp(dev->bdaddr, bdaddr, 6); if (!cmp) return dev; /* Check for instance own Random addresses */ if (bdaddr_type == 0x01) { adv = queue_find(dev->le_ext_adv, match_adv_addr, bdaddr); if (adv) return dev; } } return NULL; } static void get_bdaddr(uint16_t id, uint8_t index, uint8_t *bdaddr) { bdaddr[0] = id & 0xff; bdaddr[1] = id >> 8; bdaddr[2] = index; bdaddr[3] = 0x01; bdaddr[4] = 0xaa; bdaddr[5] = 0x00; } struct btdev_cmd { uint16_t opcode; int (*func)(struct btdev *dev, const void *data, uint8_t len); int (*complete)(struct btdev *dev, const void *data, uint8_t len); }; #define CMD(_opcode, _func, _complete) \ { \ .opcode = _opcode, \ .func = _func, \ .complete = _complete, \ } static void send_packet(struct btdev *btdev, const struct iovec *iov, int iovlen) { int i; if (!btdev->send_handler) return; for (i = 0; i < iovlen; i++) { if (!i) util_hexdump('<', iov[i].iov_base, iov[i].iov_len, btdev->debug_callback, btdev->debug_data); else util_hexdump(' ', iov[i].iov_base, iov[i].iov_len, btdev->debug_callback, btdev->debug_data); } btdev->send_handler(iov, iovlen, btdev->send_data); } static void send_cmd(struct btdev *btdev, uint8_t evt, uint16_t opcode, const struct iovec *iov, int iovlen) { struct bt_hci_evt_hdr hdr; struct iovec iov2[2 + iovlen]; uint8_t pkt = BT_H4_EVT_PKT; int i; util_debug(btdev->debug_callback, btdev->debug_data, "event 0x%02x opcode 0x%04x", evt, opcode); iov2[0].iov_base = &pkt; iov2[0].iov_len = sizeof(pkt); hdr.evt = evt; hdr.plen = 0; iov2[1].iov_base = &hdr; iov2[1].iov_len = sizeof(hdr); for (i = 0; i < iovlen; i++) { hdr.plen += iov[i].iov_len; iov2[2 + i].iov_base = iov[i].iov_base; iov2[2 + i].iov_len = iov[i].iov_len; } if (run_hooks(btdev, BTDEV_HOOK_POST_CMD, opcode, iov[i -1].iov_base, iov[i -1].iov_len)) send_packet(btdev, iov2, 2 + iovlen); } static void cmd_complete(struct btdev *btdev, uint16_t opcode, const void *data, uint8_t len) { struct bt_hci_evt_cmd_complete cc; struct iovec iov[2]; cc.ncmd = 0x01; cc.opcode = cpu_to_le16(opcode); iov[0].iov_base = &cc; iov[0].iov_len = sizeof(cc); iov[1].iov_base = (void *) data; iov[1].iov_len = len; send_cmd(btdev, BT_HCI_EVT_CMD_COMPLETE, opcode, iov, 2); } static int cmd_set_event_mask(struct btdev *dev, const void *data, uint8_t len) { const struct bt_hci_cmd_set_event_mask *cmd = data; uint8_t status; memcpy(dev->event_mask, cmd->mask, 8); status = BT_HCI_ERR_SUCCESS; cmd_complete(dev, BT_HCI_CMD_SET_EVENT_MASK, &status, sizeof(status)); return 0; } static void al_reset(struct btdev_al *al) { al->type = 0xff; bacpy(&al->addr, BDADDR_ANY); } static void al_clear(struct btdev *dev) { int i; for (i = 0; i < AL_SIZE; i++) al_reset(&dev->le_al[i]); } static void rl_reset(struct btdev_rl *rl) { rl->type = 0xff; bacpy(&rl->addr, BDADDR_ANY); memset(rl->peer_irk, 0, 16); memset(rl->local_irk, 0, 16); } static void rl_clear(struct btdev *dev) { int i; for (i = 0; i < RL_SIZE; i++) rl_reset(&dev->le_rl[i]); } /* Set the real time length of AL array */ void btdev_set_al_len(struct btdev *btdev, uint8_t len) { btdev->le_al_len = len; } /* Set the real time length of RL array */ void btdev_set_rl_len(struct btdev *btdev, uint8_t len) { btdev->le_rl_len = len; } static void conn_unlink(struct btdev_conn *conn1, struct btdev_conn *conn2) { conn1->link = NULL; conn2->link = NULL; } static void conn_remove(void *data) { struct btdev_conn *conn = data; if (conn->link) { struct btdev_conn *link = conn->link; conn_unlink(conn, conn->link); conn_remove(link); } queue_remove(conn->dev->conns, conn); free(conn->data); free(conn); } static void le_ext_adv_free(void *data) { struct le_ext_adv *ext_adv = data; /* Remove to queue */ queue_remove(ext_adv->dev->le_ext_adv, ext_adv); if (ext_adv->broadcast_id) timeout_remove(ext_adv->broadcast_id); if (ext_adv->timeout_id) timeout_remove(ext_adv->timeout_id); free(ext_adv); } static void le_big_free(void *data) { struct le_big *big = data; queue_destroy(big->bis, NULL); free(big); } static void btdev_reset(struct btdev *btdev) { /* FIXME: include here clearing of all states that should be * cleared upon HCI_Reset */ btdev->le_scan_enable = 0x00; btdev->le_adv_enable = 0x00; btdev->le_pa_enable = 0x00; al_clear(btdev); rl_clear(btdev); btdev->le_al_len = AL_SIZE; btdev->le_rl_len = RL_SIZE; queue_remove_all(btdev->conns, NULL, NULL, conn_remove); queue_remove_all(btdev->le_ext_adv, NULL, NULL, le_ext_adv_free); queue_remove_all(btdev->le_per_adv, NULL, NULL, free); queue_remove_all(btdev->le_big, NULL, NULL, le_big_free); } static int cmd_reset(struct btdev *dev, const void *data, uint8_t len) { uint8_t status; btdev_reset(dev); status = BT_HCI_ERR_SUCCESS; cmd_complete(dev, BT_HCI_CMD_RESET, &status, sizeof(status)); return 0; } static int cmd_read_local_version(struct btdev *dev, const void *data, uint8_t len) { struct bt_hci_rsp_read_local_version rsp; memset(&rsp, 0, sizeof(rsp)); rsp.status = BT_HCI_ERR_SUCCESS; rsp.hci_ver = dev->version; rsp.hci_rev = cpu_to_le16(dev->revision); rsp.lmp_ver = dev->version; rsp.manufacturer = cpu_to_le16(dev->manufacturer); rsp.lmp_subver = cpu_to_le16(dev->revision); cmd_complete(dev, BT_HCI_CMD_READ_LOCAL_VERSION, &rsp, sizeof(rsp)); return 0; } static int cmd_read_local_commands(struct btdev *dev, const void *data, uint8_t len) { struct bt_hci_rsp_read_local_commands rsp; rsp.status = BT_HCI_ERR_SUCCESS; memcpy(rsp.commands, dev->commands, 64); cmd_complete(dev, BT_HCI_CMD_READ_LOCAL_COMMANDS, &rsp, sizeof(rsp)); return 0; } static int cmd_read_local_features(struct btdev *dev, const void *data, uint8_t len) { struct bt_hci_rsp_read_local_features rsp; rsp.status = BT_HCI_ERR_SUCCESS; memcpy(rsp.features, dev->features, 8); cmd_complete(dev, BT_HCI_CMD_READ_LOCAL_FEATURES, &rsp, sizeof(rsp)); return 0; } static int cmd_read_buffer_size(struct btdev *dev, const void *data, uint8_t len) { struct bt_hci_rsp_read_buffer_size rsp; rsp.status = BT_HCI_ERR_SUCCESS; rsp.acl_mtu = cpu_to_le16(dev->acl_mtu); rsp.sco_mtu = cpu_to_le16(dev->sco_mtu); rsp.acl_max_pkt = cpu_to_le16(dev->acl_max_pkt); rsp.sco_max_pkt = cpu_to_le16(dev->sco_max_pkt); cmd_complete(dev, BT_HCI_CMD_READ_BUFFER_SIZE, &rsp, sizeof(rsp)); return 0; } #define CMD_COMMON_ALL \ CMD(BT_HCI_CMD_SET_EVENT_MASK, cmd_set_event_mask, NULL), \ CMD(BT_HCI_CMD_RESET, cmd_reset, NULL), \ CMD(BT_HCI_CMD_READ_LOCAL_VERSION, cmd_read_local_version, NULL), \ CMD(BT_HCI_CMD_READ_LOCAL_COMMANDS, cmd_read_local_commands, NULL), \ CMD(BT_HCI_CMD_READ_LOCAL_FEATURES, cmd_read_local_features, NULL), \ CMD(BT_HCI_CMD_READ_BUFFER_SIZE, cmd_read_buffer_size, NULL) static void set_common_commands_all(struct btdev *btdev) { btdev->commands[5] |= 0x40; /* Set Event Mask */ btdev->commands[5] |= 0x80; /* Reset */ btdev->commands[14] |= 0x08; /* Read Local Version */ btdev->commands[14] |= 0x10; /* Read Local Supported Commands */ btdev->commands[14] |= 0x20; /* Read Local Supported Features */ btdev->commands[14] |= 0x80; /* Read Buffer Size */ } static void cmd_status(struct btdev *btdev, uint8_t status, uint16_t opcode) { struct bt_hci_evt_cmd_status cs; struct iovec iov; cs.status = status; cs.ncmd = 0x01; cs.opcode = cpu_to_le16(opcode); iov.iov_base = &cs; iov.iov_len = sizeof(cs); send_cmd(btdev, BT_HCI_EVT_CMD_STATUS, opcode, &iov, 1); } static int cmd_disconnect(struct btdev *dev, const void *data, uint8_t len) { cmd_status(dev, BT_HCI_ERR_SUCCESS, BT_HCI_CMD_DISCONNECT); return 0; } static void send_event(struct btdev *btdev, uint8_t event, const void *data, uint8_t len) { struct bt_hci_evt_hdr hdr; struct iovec iov[3]; uint8_t pkt = BT_H4_EVT_PKT; util_debug(btdev->debug_callback, btdev->debug_data, "event 0x%02x", event); iov[0].iov_base = &pkt; iov[0].iov_len = sizeof(pkt); hdr.evt = event; hdr.plen = len; iov[1].iov_base = &hdr; iov[1].iov_len = sizeof(hdr); if (len > 0) { iov[2].iov_base = (void *) data; iov[2].iov_len = len; } if (run_hooks(btdev, BTDEV_HOOK_POST_EVT, event, data, len)) send_packet(btdev, iov, len > 0 ? 3 : 2); } static bool match_handle(const void *data, const void *match_data) { const struct btdev_conn *conn = data; uint16_t handle = PTR_TO_UINT(match_data); return conn->handle == handle; } static void disconnect_complete(struct btdev *dev, uint16_t handle, uint8_t status, uint8_t reason) { struct bt_hci_evt_disconnect_complete rsp; memset(&rsp, 0, sizeof(rsp)); rsp.status = status; rsp.handle = cpu_to_le16(handle); rsp.reason = reason; send_event(dev, BT_HCI_EVT_DISCONNECT_COMPLETE, &rsp, sizeof(rsp)); } static int cmd_disconnect_complete(struct btdev *dev, const void *data, uint8_t len) { const struct bt_hci_cmd_disconnect *cmd = data; struct bt_hci_evt_disconnect_complete rsp; struct btdev_conn *conn; memset(&rsp, 0, sizeof(rsp)); conn = queue_remove_if(dev->conns, match_handle, UINT_TO_PTR(cpu_to_le16(cmd->handle))); if (!conn) { disconnect_complete(dev, 0x0000, BT_HCI_ERR_UNKNOWN_CONN_ID, 0x00); return 0; } /* Local host has different reason (Core v5.3 Vol 4 Part E Sec 7.1.6) */ disconnect_complete(dev, conn->handle, BT_HCI_ERR_SUCCESS, BT_HCI_ERR_LOCAL_HOST_TERM); if (conn->link) disconnect_complete(conn->link->dev, conn->link->handle, BT_HCI_ERR_SUCCESS, cmd->reason); conn_remove(conn); return 0; } static int cmd_remote_version(struct btdev *dev, const void *data, uint8_t len) { cmd_status(dev, BT_HCI_ERR_SUCCESS, BT_HCI_CMD_READ_REMOTE_VERSION); return 0; } static int cmd_remote_version_complete(struct btdev *dev, const void *data, uint8_t len) { const struct bt_hci_cmd_read_remote_version *cmd = data; struct bt_hci_evt_remote_version_complete ev; struct btdev_conn *conn; memset(&ev, 0, sizeof(ev)); conn = queue_find(dev->conns, match_handle, UINT_TO_PTR(cpu_to_le16(cmd->handle))); if (conn) { ev.status = BT_HCI_ERR_SUCCESS; ev.handle = cpu_to_le16(cmd->handle); ev.lmp_ver = conn->link->dev->version; ev.manufacturer = cpu_to_le16(conn->link->dev->manufacturer); ev.lmp_subver = cpu_to_le16(conn->link->dev->revision); } else { ev.status = BT_HCI_ERR_UNKNOWN_CONN_ID; ev.handle = cpu_to_le16(cmd->handle); ev.lmp_ver = 0x00; ev.manufacturer = cpu_to_le16(0); ev.lmp_subver = cpu_to_le16(0); } send_event(dev, BT_HCI_EVT_REMOTE_VERSION_COMPLETE, &ev, sizeof(ev)); return 0; } static int cmd_set_host_flowctl(struct btdev *dev, const void *data, uint8_t len) { const struct bt_hci_cmd_set_host_flow_control *cmd = data; uint8_t status; if (cmd->enable > 0x03) { status = BT_HCI_ERR_INVALID_PARAMETERS; } else { dev->host_flow_control = cmd->enable; status = BT_HCI_ERR_SUCCESS; } cmd_complete(dev, BT_HCI_CMD_SET_HOST_FLOW_CONTROL, &status, sizeof(status)); return 0; } static int cmd_host_buffer_size(struct btdev *dev, const void *data, uint8_t len) { uint8_t status = BT_HCI_ERR_SUCCESS; cmd_complete(dev, BT_HCI_CMD_HOST_BUFFER_SIZE, &status, sizeof(status)); return 0; } static int cmd_host_num_completed_pkts(struct btdev *dev, const void *data, uint8_t len) { /* This command is special in the sense that no event is * normally generated after the command has completed. */ return 0; } static int cmd_read_bdaddr(struct btdev *dev, const void *data, uint8_t len) { struct bt_hci_rsp_read_bd_addr rsp; memset(&rsp, 0, sizeof(rsp)); rsp.status = BT_HCI_ERR_SUCCESS; memcpy(rsp.bdaddr, dev->bdaddr, 6); cmd_complete(dev, BT_HCI_CMD_READ_BD_ADDR, &rsp, sizeof(rsp)); return 0; } #define CMD_COMMON_BREDR_LE \ CMD(BT_HCI_CMD_DISCONNECT, cmd_disconnect, cmd_disconnect_complete), \ CMD(BT_HCI_CMD_READ_REMOTE_VERSION, cmd_remote_version, \ cmd_remote_version_complete), \ CMD(BT_HCI_CMD_SET_HOST_FLOW_CONTROL, cmd_set_host_flowctl, NULL), \ CMD(BT_HCI_CMD_HOST_BUFFER_SIZE, cmd_host_buffer_size, NULL), \ CMD(BT_HCI_CMD_HOST_NUM_COMPLETED_PACKETS, \ cmd_host_num_completed_pkts, NULL), \ CMD(BT_HCI_CMD_READ_BD_ADDR, cmd_read_bdaddr, NULL) static void set_common_commands_bredrle(struct btdev *btdev) { btdev->commands[0] |= 0x20; /* Disconnect */ btdev->commands[2] |= 0x80; /* Read Remote Version Information */ btdev->commands[10] |= 0x20; /* Set Host Flow Control */ btdev->commands[10] |= 0x40; /* Host Buffer Size */ btdev->commands[15] |= 0x02; /* Read BD ADDR */ } static int cmd_inquiry(struct btdev *dev, const void *data, uint8_t len) { cmd_status(dev, BT_HCI_ERR_SUCCESS, BT_HCI_CMD_INQUIRY); return 0; } static bool inquiry_callback(void *user_data) { struct inquiry_data *data = user_data; struct btdev *btdev = data->btdev; struct bt_hci_evt_inquiry_complete ic; int sent = data->sent_count; int i; /*Report devices only once and wait for inquiry timeout*/ if (data->iter == MAX_BTDEV_ENTRIES) return true; for (i = data->iter; i < MAX_BTDEV_ENTRIES; i++) { /*Lets sent 10 inquiry results at once */ if (sent + 10 == data->sent_count) break; if (!btdev_list[i] || btdev_list[i] == btdev) continue; if (!(btdev_list[i]->scan_enable & 0x02)) continue; if (btdev->inquiry_mode == 0x02 && btdev_list[i]->ext_inquiry_rsp[0]) { struct bt_hci_evt_ext_inquiry_result ir; ir.num_resp = 0x01; memcpy(ir.bdaddr, btdev_list[i]->bdaddr, 6); ir.pscan_rep_mode = 0x00; ir.pscan_period_mode = 0x00; memcpy(ir.dev_class, btdev_list[i]->dev_class, 3); ir.clock_offset = 0x0000; ir.rssi = -60; memcpy(ir.data, btdev_list[i]->ext_inquiry_rsp, 240); send_event(btdev, BT_HCI_EVT_EXT_INQUIRY_RESULT, &ir, sizeof(ir)); data->sent_count++; continue; } if (btdev->inquiry_mode > 0x00) { struct bt_hci_evt_inquiry_result_with_rssi ir; ir.num_resp = 0x01; memcpy(ir.bdaddr, btdev_list[i]->bdaddr, 6); ir.pscan_rep_mode = 0x00; ir.pscan_period_mode = 0x00; memcpy(ir.dev_class, btdev_list[i]->dev_class, 3); ir.clock_offset = 0x0000; ir.rssi = -60; send_event(btdev, BT_HCI_EVT_INQUIRY_RESULT_WITH_RSSI, &ir, sizeof(ir)); data->sent_count++; } else { struct bt_hci_evt_inquiry_result ir; ir.num_resp = 0x01; memcpy(ir.bdaddr, btdev_list[i]->bdaddr, 6); ir.pscan_rep_mode = 0x00; ir.pscan_period_mode = 0x00; ir.pscan_mode = 0x00; memcpy(ir.dev_class, btdev_list[i]->dev_class, 3); ir.clock_offset = 0x0000; send_event(btdev, BT_HCI_EVT_INQUIRY_RESULT, &ir, sizeof(ir)); data->sent_count++; } } data->iter = i; /* Check if we sent already required amount of responses*/ if (data->num_resp && data->sent_count == data->num_resp) goto finish; return true; finish: /* Note that destroy will be called */ ic.status = BT_HCI_ERR_SUCCESS; send_event(btdev, BT_HCI_EVT_INQUIRY_COMPLETE, &ic, sizeof(ic)); return false; } static void inquiry_destroy(void *user_data) { struct inquiry_data *data = user_data; struct btdev *btdev = data->btdev; if (!btdev) goto finish; btdev->inquiry_id = 0; if (btdev->inquiry_timeout_id > 0) { timeout_remove(btdev->inquiry_timeout_id); btdev->inquiry_timeout_id = 0; } finish: free(data); } static bool inquiry_timeout(void *user_data) { struct inquiry_data *data = user_data; struct btdev *btdev = data->btdev; struct bt_hci_evt_inquiry_complete ic; timeout_remove(btdev->inquiry_id); btdev->inquiry_timeout_id = 0; /* Inquiry is stopped, send Inquiry complete event. */ ic.status = BT_HCI_ERR_SUCCESS; send_event(btdev, BT_HCI_EVT_INQUIRY_COMPLETE, &ic, sizeof(ic)); return false; } static int cmd_inquiry_complete(struct btdev *dev, const void *data, uint8_t len) { const struct bt_hci_cmd_inquiry *cmd = data; struct inquiry_data *idata; struct bt_hci_evt_inquiry_complete ic; int status = BT_HCI_ERR_HARDWARE_FAILURE; unsigned int inquiry_len_ms; if (dev->inquiry_id > 0) { status = BT_HCI_ERR_COMMAND_DISALLOWED; goto failed; } idata = malloc0(sizeof(*idata)); if (!idata) goto failed; idata->btdev = dev; idata->num_resp = cmd->num_resp; /* Add timeout to cancel inquiry */ inquiry_len_ms = 1280 * cmd->length; if (inquiry_len_ms) dev->inquiry_timeout_id = timeout_add(inquiry_len_ms, inquiry_timeout, idata, NULL); dev->inquiry_id = timeout_add(DEFAULT_INQUIRY_INTERVAL, inquiry_callback, idata, inquiry_destroy); /* Return if success */ if (dev->inquiry_id > 0) return 0; failed: ic.status = status; send_event(dev, BT_HCI_EVT_INQUIRY_COMPLETE, &ic, sizeof(ic)); return 0; } static int cmd_inquiry_cancel(struct btdev *dev, const void *data, uint8_t len) { uint8_t status; if (!dev->inquiry_id) { status = BT_HCI_ERR_COMMAND_DISALLOWED; goto done; } timeout_remove(dev->inquiry_timeout_id); dev->inquiry_timeout_id = 0; timeout_remove(dev->inquiry_id); dev->inquiry_id = 0; status = BT_HCI_ERR_SUCCESS; done: cmd_complete(dev, BT_HCI_CMD_INQUIRY_CANCEL, &status, sizeof(status)); return 0; } static int cmd_create_conn(struct btdev *dev, const void *data, uint8_t len) { cmd_status(dev, BT_HCI_ERR_SUCCESS, BT_HCI_CMD_CREATE_CONN); return 0; } static struct btdev_conn *conn_new(struct btdev *dev, uint16_t handle, uint8_t type) { struct btdev_conn *conn; while (queue_find(dev->conns, match_handle, UINT_TO_PTR(handle))) handle++; conn = new0(struct btdev_conn, 1); conn->handle = handle; conn->type = type; conn->dev = dev; if (!queue_push_tail(dev->conns, conn)) { free(conn); return NULL; } return conn; } static struct btdev_conn *conn_link(struct btdev *dev, struct btdev *remote, uint16_t handle, uint8_t type) { struct btdev_conn *conn1, *conn2; conn1 = conn_new(dev, handle, type); if (!conn1) return NULL; conn2 = conn_new(remote, handle, type); if (!conn2) { free(conn1); return NULL; } conn1->link = conn2; conn2->link = conn1; util_debug(dev->debug_callback, dev->debug_data, "conn1 %p handle 0x%04x", conn1, conn1->handle); util_debug(dev->debug_callback, dev->debug_data, "conn2 %p handle 0x%04x", conn2, conn2->handle); return conn1; } static struct btdev_conn *conn_add(struct btdev *dev, const uint8_t *bdaddr, uint8_t bdaddr_type, uint16_t handle, uint8_t type) { struct btdev *remote; remote = find_btdev_by_bdaddr_type(bdaddr, bdaddr_type); if (!remote) return NULL; return conn_link(dev, remote, handle, type); } static struct btdev_conn *conn_add_acl(struct btdev *dev, const uint8_t *bdaddr, uint8_t bdaddr_type) { return conn_add(dev, bdaddr, bdaddr_type, ACL_HANDLE, HCI_ACLDATA_PKT); } static struct btdev_conn *conn_add_sco(struct btdev_conn *acl) { return conn_link(acl->dev, acl->link->dev, SCO_HANDLE, HCI_SCODATA_PKT); } static struct btdev_conn *conn_add_cis(struct btdev_conn *acl, uint16_t handle) { return conn_link(acl->dev, acl->link->dev, handle, HCI_ISODATA_PKT); } static struct btdev_conn *conn_add_bis(struct btdev *dev, uint16_t handle, const struct bt_hci_bis *bis) { struct btdev_conn *conn; conn = conn_new(dev, handle, HCI_ISODATA_PKT); if (!conn) return conn; conn->data = util_memdup(bis, sizeof(*bis)); return conn; } static struct btdev_conn *find_bis_index(const struct btdev *remote, uint8_t index) { struct btdev_conn *conn; const struct queue_entry *entry; for (entry = queue_get_entries(remote->conns); entry; entry = entry->next) { conn = entry->data; /* Skip if not a broadcast */ if (conn->type != HCI_ISODATA_PKT || conn->link) continue; if (!index) return conn; index--; } return NULL; } static struct btdev_conn *conn_link_bis(struct btdev *dev, struct btdev *remote, uint8_t index) { struct btdev_conn *conn; struct btdev_conn *bis; bis = find_bis_index(remote, index); if (!bis) return NULL; conn = conn_add_bis(dev, ISO_HANDLE, bis->data); if (!conn) return NULL; bis->link = conn; conn->link = bis; util_debug(dev->debug_callback, dev->debug_data, "bis %p handle 0x%04x", bis, bis->handle); util_debug(dev->debug_callback, dev->debug_data, "conn %p handle 0x%04x", conn, conn->handle); return conn; } static void pending_conn_add(struct btdev *btdev, struct btdev *remote) { unsigned int i; for (i = 0; i < ARRAY_SIZE(btdev->pending_conn); ++i) { if (!btdev->pending_conn[i]) { btdev->pending_conn[i] = remote; return; } } } static bool pending_conn_del(struct btdev *btdev, struct btdev *remote) { unsigned int i; for (i = 0; i < ARRAY_SIZE(btdev->pending_conn); ++i) { if (btdev->pending_conn[i] == remote) { btdev->pending_conn[i] = NULL; return true; } } return false; } static void conn_complete(struct btdev *btdev, const uint8_t *bdaddr, uint8_t status) { struct bt_hci_evt_conn_complete cc; struct btdev *remote = find_btdev_by_bdaddr(bdaddr); if (!remote) return; if (!status) { struct btdev_conn *conn; conn = conn_add_acl(btdev, bdaddr, BDADDR_BREDR); if (!conn) return; pending_conn_del(conn->link->dev, btdev); cc.status = status; memcpy(cc.bdaddr, btdev->bdaddr, 6); cc.encr_mode = 0x00; cc.handle = cpu_to_le16(conn->link->handle); cc.link_type = 0x01; send_event(conn->link->dev, BT_HCI_EVT_CONN_COMPLETE, &cc, sizeof(cc)); cc.handle = cpu_to_le16(conn->handle); cc.link_type = 0x01; } else { cc.handle = cpu_to_le16(0x0000); cc.link_type = 0x01; } pending_conn_del(btdev, remote); cc.status = status; memcpy(cc.bdaddr, bdaddr, 6); cc.encr_mode = 0x00; send_event(btdev, BT_HCI_EVT_CONN_COMPLETE, &cc, sizeof(cc)); } struct page_timeout_data { struct btdev *btdev; uint8_t bdaddr[6]; unsigned int timeout_id; }; static bool page_timeout(void *user_data) { struct page_timeout_data *pt_data = user_data; struct btdev *btdev = pt_data->btdev; const uint8_t *bdaddr = pt_data->bdaddr; timeout_remove(pt_data->timeout_id); pt_data->timeout_id = 0; if (valid_btdev(btdev)) conn_complete(btdev, bdaddr, BT_HCI_ERR_PAGE_TIMEOUT); free(pt_data); return false; } static int cmd_create_conn_complete(struct btdev *dev, const void *data, uint8_t len) { const struct bt_hci_cmd_create_conn *cmd = data; struct btdev *remote = find_btdev_by_bdaddr(cmd->bdaddr); if (remote && remote->scan_enable & 0x02) { struct bt_hci_evt_conn_request cr; memcpy(cr.bdaddr, dev->bdaddr, 6); memcpy(cr.dev_class, dev->dev_class, 3); cr.link_type = 0x01; pending_conn_add(dev, remote); send_event(remote, BT_HCI_EVT_CONN_REQUEST, &cr, sizeof(cr)); } else { struct page_timeout_data *pt_data = new0(struct page_timeout_data, 1); pt_data->btdev = dev; memcpy(pt_data->bdaddr, cmd->bdaddr, 6); /* Send page timeout after 5.12 seconds to emulate real * paging. */ pt_data->timeout_id = timeout_add(5120, page_timeout, pt_data, NULL); } return 0; } static int cmd_add_sco_conn(struct btdev *dev, const void *data, uint8_t len) { const struct bt_hci_cmd_add_sco_conn *cmd = data; struct bt_hci_evt_conn_complete cc; struct btdev_conn *conn; memset(&cc, 0, sizeof(cc)); conn = queue_find(dev->conns, match_handle, UINT_TO_PTR(cpu_to_le16(cmd->handle))); if (!conn) { cc.status = BT_HCI_ERR_UNKNOWN_CONN_ID; goto done; } conn = conn_add_sco(conn); if (!conn) { cc.status = BT_HCI_ERR_MEM_CAPACITY_EXCEEDED; goto done; } cc.status = BT_HCI_ERR_SUCCESS; memcpy(cc.bdaddr, conn->link->dev->bdaddr, 6); cc.handle = cpu_to_le16(conn->handle); cc.link_type = 0x00; cc.encr_mode = 0x00; done: send_event(dev, BT_HCI_EVT_CONN_COMPLETE, &cc, sizeof(cc)); if (conn) send_event(conn->link->dev, BT_HCI_EVT_CONN_COMPLETE, &cc, sizeof(cc)); return 0; } static bool match_bdaddr(const void *data, const void *match_data) { const struct btdev_conn *conn = data; const uint8_t *bdaddr = match_data; return !memcmp(conn->link->dev->bdaddr, bdaddr, 6); } static int cmd_create_conn_cancel(struct btdev *dev, const void *data, uint8_t len) { return 0; } static int cmd_create_conn_cancel_complete(struct btdev *dev, const void *data, uint8_t len) { const struct bt_hci_cmd_create_conn_cancel *cmd = data; struct bt_hci_rsp_create_conn_cancel rp; struct btdev *remote = find_btdev_by_bdaddr(cmd->bdaddr); struct btdev_conn *conn; /* BLUETOOTH CORE SPECIFICATION Version 5.4 | Vol 4, Part E page 1848 * * If the connection is already established, and the * HCI_Connection_Complete event has been sent, then the Controller * shall return an HCI_Command_Complete event with the error code * Connection Already Exists (0x0B). If the HCI_Create_Connection_Cancel * command is sent to the Controller without a preceding * HCI_Create_Connection command to the same device, the BR/EDR * Controller shall return an HCI_Command_Complete event with the error * code Unknown Connection Identifier (0x02). */ if (pending_conn_del(dev, remote)) { rp.status = BT_HCI_ERR_SUCCESS; } else { conn = queue_find(dev->conns, match_bdaddr, cmd->bdaddr); if (conn) rp.status = BT_HCI_ERR_CONN_ALREADY_EXISTS; else rp.status = BT_HCI_ERR_UNKNOWN_CONN_ID; } memcpy(rp.bdaddr, cmd->bdaddr, sizeof(rp.bdaddr)); cmd_complete(dev, BT_HCI_CMD_CREATE_CONN_CANCEL, &rp, sizeof(rp)); if (!rp.status) conn_complete(dev, cmd->bdaddr, BT_HCI_ERR_UNKNOWN_CONN_ID); return 0; } static int cmd_accept_conn(struct btdev *dev, const void *data, uint8_t len) { cmd_status(dev, BT_HCI_ERR_SUCCESS, BT_HCI_CMD_ACCEPT_CONN_REQUEST); return 0; } static int cmd_accept_conn_complete(struct btdev *dev, const void *data, uint8_t len) { const struct bt_hci_cmd_accept_conn_request *cmd = data; struct btdev *remote = find_btdev_by_bdaddr(cmd->bdaddr); if (!remote) return 0; if (dev->auth_enable || remote->auth_enable) send_event(remote, BT_HCI_EVT_LINK_KEY_REQUEST, dev->bdaddr, 6); else conn_complete(dev, cmd->bdaddr, BT_HCI_ERR_SUCCESS); return 0; } static int cmd_reject_conn(struct btdev *dev, const void *data, uint8_t len) { cmd_status(dev, BT_HCI_ERR_SUCCESS, BT_HCI_CMD_REJECT_CONN_REQUEST); return 0; } static int cmd_reject_conn_complete(struct btdev *dev, const void *data, uint8_t len) { const struct bt_hci_cmd_reject_conn_request *cmd = data; conn_complete(dev, cmd->bdaddr, BT_HCI_ERR_UNKNOWN_CONN_ID); return 0; } static int cmd_link_key_reply(struct btdev *dev, const void *data, uint8_t len) { struct bt_hci_rsp_link_key_request_reply rsp; memset(&rsp, 0, sizeof(rsp)); rsp.status = BT_HCI_ERR_SUCCESS; memcpy(rsp.bdaddr, data, 6); cmd_complete(dev, BT_HCI_CMD_LINK_KEY_REQUEST_REPLY, &rsp, sizeof(rsp)); return 0; } static void auth_complete(struct btdev_conn *conn, uint8_t status) { struct bt_hci_evt_auth_complete ev; if (!conn) return; memset(&ev, 0, sizeof(ev)); ev.handle = cpu_to_le16(conn->handle); ev.status = status; send_event(conn->dev, BT_HCI_EVT_AUTH_COMPLETE, &ev, sizeof(ev)); conn->dev->ssp_status = 0; conn->dev->ssp_auth_complete = false; conn->link->dev->ssp_status = 0; conn->link->dev->ssp_auth_complete = false; } static int cmd_link_key_reply_complete(struct btdev *dev, const void *data, uint8_t len) { const struct bt_hci_cmd_link_key_request_reply *cmd = data; struct btdev_conn *conn; uint8_t status; conn = queue_find(dev->conns, match_bdaddr, cmd->bdaddr); if (!conn) { status = BT_HCI_ERR_INVALID_PARAMETERS; goto done; } memcpy(dev->link_key, cmd->link_key, 16); if (!memcmp(conn->link->dev->link_key, LINK_KEY_NONE, 16)) { send_event(conn->link->dev, BT_HCI_EVT_LINK_KEY_REQUEST, dev->bdaddr, 6); return 0; } if (!memcmp(dev->link_key, conn->link->dev->link_key, 16)) status = BT_HCI_ERR_SUCCESS; else status = BT_HCI_ERR_AUTH_FAILURE; done: auth_complete(conn, status); if (conn) auth_complete(conn->link, status); return 0; } static int cmd_link_key_neg_reply(struct btdev *dev, const void *data, uint8_t len) { struct bt_hci_rsp_link_key_request_neg_reply rsp; memset(&rsp, 0, sizeof(rsp)); rsp.status = BT_HCI_ERR_SUCCESS; memcpy(rsp.bdaddr, data, 6); cmd_complete(dev, BT_HCI_CMD_LINK_KEY_REQUEST_NEG_REPLY, &rsp, sizeof(rsp)); return 0; } static bool use_ssp(struct btdev *btdev1, struct btdev *btdev2) { if (btdev1->auth_enable || btdev2->auth_enable) return false; return (btdev1->simple_pairing_mode && btdev2->simple_pairing_mode); } static int cmd_link_key_neg_reply_complete(struct btdev *dev, const void *data, uint8_t len) { const struct bt_hci_cmd_link_key_request_neg_reply *cmd = data; struct btdev *remote; remote = find_btdev_by_bdaddr(cmd->bdaddr); if (!remote) return 0; if (use_ssp(dev, remote)) { struct bt_hci_evt_io_capability_request io_req; memcpy(io_req.bdaddr, cmd->bdaddr, 6); send_event(dev, BT_HCI_EVT_IO_CAPABILITY_REQUEST, &io_req, sizeof(io_req)); } else { struct bt_hci_evt_pin_code_request pin_req; memcpy(pin_req.bdaddr, cmd->bdaddr, 6); send_event(dev, BT_HCI_EVT_PIN_CODE_REQUEST, &pin_req, sizeof(pin_req)); } return 0; } static int cmd_pin_code_reply(struct btdev *dev, const void *data, uint8_t len) { struct bt_hci_rsp_pin_code_request_neg_reply rsp; memset(&rsp, 0, sizeof(rsp)); rsp.status = BT_HCI_ERR_SUCCESS; memcpy(rsp.bdaddr, data, 6); cmd_complete(dev, BT_HCI_CMD_PIN_CODE_REQUEST_REPLY, &rsp, sizeof(rsp)); return 0; } static uint8_t get_link_key_type(struct btdev *btdev, const uint8_t *bdaddr) { struct btdev_conn *conn; uint8_t auth, unauth; conn = queue_find(btdev->conns, match_bdaddr, bdaddr); if (!conn) return 0x00; if (!btdev->simple_pairing_mode) return 0x00; if (btdev->ssp_debug_mode || conn->link->dev->ssp_debug_mode) return 0x03; if (btdev->secure_conn_support && conn->link->dev->secure_conn_support) { unauth = 0x07; auth = 0x08; } else { unauth = 0x04; auth = 0x05; } if (btdev->io_cap == 0x03 || conn->link->dev->io_cap == 0x03) return unauth; if (!(btdev->auth_req & 0x01) && !(conn->link->dev->auth_req & 0x01)) return unauth; /* DisplayOnly only produces authenticated with KeyboardOnly */ if (btdev->io_cap == 0x00 && conn->link->dev->io_cap != 0x02) return unauth; /* DisplayOnly only produces authenticated with KeyboardOnly */ if (conn->link->dev->io_cap == 0x00 && btdev->io_cap != 0x02) return unauth; return auth; } static void link_key_notify(struct btdev *btdev, const uint8_t *bdaddr, const uint8_t *key) { struct bt_hci_evt_link_key_notify ev; memcpy(btdev->link_key, key, 16); memcpy(ev.bdaddr, bdaddr, 6); memcpy(ev.link_key, key, 16); ev.key_type = get_link_key_type(btdev, bdaddr); send_event(btdev, BT_HCI_EVT_LINK_KEY_NOTIFY, &ev, sizeof(ev)); } static int cmd_pin_code_reply_complete(struct btdev *dev, const void *data, uint8_t len) { const struct bt_hci_cmd_pin_code_request_reply *cmd = data; struct btdev *remote; struct btdev_conn *conn; uint8_t status; conn = queue_find(dev->conns, match_bdaddr, cmd->bdaddr); if (!conn) { remote = find_btdev_by_bdaddr(cmd->bdaddr); if (!remote) return 0; } else remote = conn->link->dev; memcpy(dev->pin, cmd->pin_code, cmd->pin_len); dev->pin_len = cmd->pin_len; if (!remote->pin_len) { struct bt_hci_evt_pin_code_request pin_req; memcpy(pin_req.bdaddr, dev->bdaddr, 6); send_event(remote, BT_HCI_EVT_PIN_CODE_REQUEST, &pin_req, sizeof(pin_req)); return 0; } if (dev->pin_len == remote->pin_len && !memcmp(dev->pin, remote->pin, dev->pin_len)) { link_key_notify(dev, remote->bdaddr, LINK_KEY_DUMMY); link_key_notify(remote, dev->bdaddr, LINK_KEY_DUMMY); status = BT_HCI_ERR_SUCCESS; } else { status = BT_HCI_ERR_AUTH_FAILURE; } if (conn) auth_complete(conn->link, status); else conn_complete(remote, dev->bdaddr, status); dev->pin_len = 0; remote->pin_len = 0; return 0; } static int cmd_pin_code_neg_reply(struct btdev *dev, const void *data, uint8_t len) { struct bt_hci_rsp_pin_code_request_neg_reply rsp; memset(&rsp, 0, sizeof(rsp)); rsp.status = BT_HCI_ERR_SUCCESS; memcpy(rsp.bdaddr, data, 6); cmd_complete(dev, BT_HCI_CMD_PIN_CODE_REQUEST_NEG_REPLY, &rsp, sizeof(rsp)); return 0; } static int cmd_pin_code_neg_reply_complete(struct btdev *dev, const void *data, uint8_t len) { const struct bt_hci_cmd_pin_code_request_neg_reply *cmd = data; struct btdev *remote; struct btdev_conn *conn; uint8_t status; remote = find_btdev_by_bdaddr(cmd->bdaddr); if (!remote) return 0; status = BT_HCI_ERR_PIN_OR_KEY_MISSING; conn = queue_find(dev->conns, match_bdaddr, cmd->bdaddr); if (conn) auth_complete(conn, status); else conn_complete(dev, cmd->bdaddr, BT_HCI_ERR_PIN_OR_KEY_MISSING); if (conn) { if (remote->pin_len) auth_complete(conn->link, status); } else { conn_complete(remote, dev->bdaddr, BT_HCI_ERR_PIN_OR_KEY_MISSING); } return 0; } static int cmd_auth_requested(struct btdev *dev, const void *data, uint8_t len) { cmd_status(dev, BT_HCI_ERR_SUCCESS, BT_HCI_CMD_AUTH_REQUESTED); return 0; } static int cmd_auth_requested_complete(struct btdev *dev, const void *data, uint8_t len) { const struct bt_hci_cmd_auth_requested *cmd = data; struct btdev_conn *conn; conn = queue_find(dev->conns, match_handle, UINT_TO_PTR(le16_to_cpu(cmd->handle))); if (!conn) { struct bt_hci_evt_auth_complete ev; ev.status = BT_HCI_ERR_UNKNOWN_CONN_ID; ev.handle = cpu_to_le16(cmd->handle); send_event(dev, BT_HCI_EVT_AUTH_COMPLETE, &ev, sizeof(ev)); return 0; } dev->auth_init = true; send_event(dev, BT_HCI_EVT_LINK_KEY_REQUEST, conn->link->dev->bdaddr, sizeof(conn->link->dev->bdaddr)); return 0; } static int cmd_set_conn_encrypt(struct btdev *dev, const void *data, uint8_t len) { cmd_status(dev, BT_HCI_ERR_SUCCESS, BT_HCI_CMD_SET_CONN_ENCRYPT); return 0; } static void encrypt_change(struct btdev_conn *conn, uint8_t mode, uint8_t status) { struct bt_hci_evt_encrypt_change ev; if (!conn) return; memset(&ev, 0, sizeof(ev)); ev.status = status; ev.handle = cpu_to_le16(conn->handle); ev.encr_mode = mode; send_event(conn->dev, BT_HCI_EVT_ENCRYPT_CHANGE, &ev, sizeof(ev)); } static int cmd_set_conn_encrypt_complete(struct btdev *dev, const void *data, uint8_t len) { const struct bt_hci_cmd_set_conn_encrypt *cmd = data; struct btdev_conn *conn; uint8_t mode; conn = queue_find(dev->conns, match_handle, UINT_TO_PTR(le16_to_cpu(cmd->handle))); if (!conn) return 0; if (!cmd->encr_mode) mode = 0x00; else if (dev->secure_conn_support && conn->link->dev->secure_conn_support) mode = 0x02; else mode = 0x01; encrypt_change(conn, mode, BT_HCI_ERR_SUCCESS); encrypt_change(conn->link, mode, BT_HCI_ERR_SUCCESS); return 0; } static int cmd_remote_name(struct btdev *dev, const void *data, uint8_t len) { cmd_status(dev, BT_HCI_ERR_SUCCESS, BT_HCI_CMD_REMOTE_NAME_REQUEST); return 0; } static void name_request_complete(struct btdev *btdev, const uint8_t *bdaddr, uint8_t status) { struct bt_hci_evt_remote_name_request_complete nc; nc.status = status; memcpy(nc.bdaddr, bdaddr, 6); memset(nc.name, 0, 248); if (!status) { struct btdev *remote = find_btdev_by_bdaddr(bdaddr); if (remote) memcpy(nc.name, remote->name, 248); else nc.status = BT_HCI_ERR_UNKNOWN_CONN_ID; } send_event(btdev, BT_HCI_EVT_REMOTE_NAME_REQUEST_COMPLETE, &nc, sizeof(nc)); } static int cmd_remote_name_complete(struct btdev *dev, const void *data, uint8_t len) { const struct bt_hci_cmd_remote_name_request *cmd = data; name_request_complete(dev, cmd->bdaddr, BT_HCI_ERR_SUCCESS); return 0; } static int cmd_remote_name_cancel(struct btdev *dev, const void *data, uint8_t len) { const struct bt_hci_cmd_remote_name_request_cancel *cmd = data; struct bt_hci_rsp_remote_name_request_cancel rsp; memset(&rsp, 0, sizeof(rsp)); rsp.status = BT_HCI_ERR_SUCCESS; memcpy(rsp.bdaddr, cmd->bdaddr, 6); cmd_complete(dev, BT_HCI_CMD_REMOTE_NAME_REQUEST_CANCEL, &rsp, sizeof(rsp)); return 0; } static int cmd_remote_name_cancel_complete(struct btdev *dev, const void *data, uint8_t len) { const struct bt_hci_cmd_remote_name_request_cancel *cmd = data; name_request_complete(dev, cmd->bdaddr, BT_HCI_ERR_UNKNOWN_CONN_ID); return 0; } static int cmd_read_remote_features(struct btdev *dev, const void *data, uint8_t len) { cmd_status(dev, BT_HCI_ERR_SUCCESS, BT_HCI_CMD_READ_REMOTE_FEATURES); return 0; } static int cmd_read_remote_features_complete(struct btdev *dev, const void *data, uint8_t len) { const struct bt_hci_cmd_read_remote_features *cmd = data; struct bt_hci_evt_remote_features_complete rfc; struct btdev_conn *conn; conn = queue_find(dev->conns, match_handle, UINT_TO_PTR(le16_to_cpu(cmd->handle))); if (conn) { rfc.status = BT_HCI_ERR_SUCCESS; rfc.handle = cpu_to_le16(cmd->handle); memcpy(rfc.features, conn->link->dev->features, 8); } else { rfc.status = BT_HCI_ERR_UNKNOWN_CONN_ID; rfc.handle = cpu_to_le16(cmd->handle); memset(rfc.features, 0, 8); } send_event(dev, BT_HCI_EVT_REMOTE_FEATURES_COMPLETE, &rfc, sizeof(rfc)); return 0; } static int cmd_read_remote_ext_features(struct btdev *dev, const void *data, uint8_t len) { cmd_status(dev, BT_HCI_ERR_SUCCESS, BT_HCI_CMD_READ_REMOTE_EXT_FEATURES); return 0; } static void btdev_get_host_features(struct btdev *btdev, uint8_t features[8]) { memset(features, 0, 8); if (btdev->simple_pairing_mode) features[0] |= 0x01; if (btdev->le_supported) features[0] |= 0x02; if (btdev->le_simultaneous) features[0] |= 0x04; if (btdev->secure_conn_support) features[0] |= 0x08; } static int cmd_read_remote_ext_features_compl(struct btdev *dev, const void *data, uint8_t len) { const struct bt_hci_cmd_read_remote_ext_features *cmd = data; struct bt_hci_evt_remote_ext_features_complete ev; struct btdev_conn *conn; memset(&ev, 0, sizeof(ev)); conn = queue_find(dev->conns, match_handle, UINT_TO_PTR(le16_to_cpu(cmd->handle))); if (conn && cmd->page < 0x02) { ev.handle = cpu_to_le16(cmd->handle); ev.page = cmd->page; ev.max_page = 0x01; switch (cmd->page) { case 0x00: ev.status = BT_HCI_ERR_SUCCESS; memcpy(ev.features, conn->link->dev->features, 8); break; case 0x01: ev.status = BT_HCI_ERR_SUCCESS; btdev_get_host_features(conn->link->dev, ev.features); break; default: ev.status = BT_HCI_ERR_INVALID_PARAMETERS; memset(ev.features, 0, 8); break; } } else { ev.status = BT_HCI_ERR_UNKNOWN_CONN_ID; ev.handle = cpu_to_le16(cmd->handle); ev.page = cmd->page; ev.max_page = 0x01; memset(ev.features, 0, 8); } send_event(dev, BT_HCI_EVT_REMOTE_EXT_FEATURES_COMPLETE, &ev, sizeof(ev)); return 0; } static int cmd_read_clock_offset(struct btdev *dev, const void *data, uint8_t len) { cmd_status(dev, BT_HCI_ERR_SUCCESS, BT_HCI_CMD_READ_CLOCK_OFFSET); return 0; } static int cmd_read_clock_offset_complete(struct btdev *dev, const void *data, uint8_t len) { const struct bt_hci_cmd_read_clock_offset *cmd = data; struct bt_hci_evt_clock_offset_complete ev; struct btdev_conn *conn; memset(&ev, 0, sizeof(ev)); conn = queue_find(dev->conns, match_handle, UINT_TO_PTR(le16_to_cpu(cmd->handle))); if (conn) { ev.status = BT_HCI_ERR_SUCCESS; ev.handle = cpu_to_le16(cmd->handle); ev.clock_offset = 0; } else { ev.status = BT_HCI_ERR_UNKNOWN_CONN_ID; ev.handle = cpu_to_le16(cmd->handle); ev.clock_offset = 0; } send_event(dev, BT_HCI_EVT_CLOCK_OFFSET_COMPLETE, &ev, sizeof(ev)); return 0; } static int cmd_read_link_policy(struct btdev *dev, const void *data, uint8_t len) { struct bt_hci_rsp_read_default_link_policy rsp; memset(&rsp, 0, sizeof(rsp)); rsp.status = BT_HCI_ERR_SUCCESS; rsp.policy = cpu_to_le16(dev->default_link_policy); cmd_complete(dev, BT_HCI_CMD_READ_DEFAULT_LINK_POLICY, &rsp, sizeof(rsp)); return 0; } static int cmd_write_link_policy(struct btdev *dev, const void *data, uint8_t len) { const struct bt_hci_cmd_write_default_link_policy *cmd = data; uint8_t status; dev->default_link_policy = le16_to_cpu(cmd->policy); status = BT_HCI_ERR_SUCCESS; cmd_complete(dev, BT_HCI_CMD_WRITE_DEFAULT_LINK_POLICY, &status, sizeof(status)); return 0; } static int cmd_set_event_filter(struct btdev *dev, const void *data, uint8_t len) { const struct bt_hci_cmd_set_event_filter *cmd = data; uint8_t status; dev->event_filter = cmd->type; status = BT_HCI_ERR_SUCCESS; cmd_complete(dev, BT_HCI_CMD_SET_EVENT_FILTER, &status, sizeof(status)); return 0; } static int cmd_read_link_key(struct btdev *dev, const void *data, uint8_t len) { struct bt_hci_rsp_read_stored_link_key rsp; memset(&rsp, 0, sizeof(rsp)); rsp.status = BT_HCI_ERR_SUCCESS; rsp.max_num_keys = cpu_to_le16(0); rsp.num_keys = cpu_to_le16(0); cmd_complete(dev, BT_HCI_CMD_READ_STORED_LINK_KEY, &rsp, sizeof(rsp)); return 0; } static int cmd_write_link_key(struct btdev *dev, const void *data, uint8_t len) { struct bt_hci_rsp_write_stored_link_key rsp; memset(&rsp, 0, sizeof(rsp)); rsp.status = BT_HCI_ERR_SUCCESS; rsp.num_keys = 0; cmd_complete(dev, BT_HCI_CMD_WRITE_STORED_LINK_KEY, &rsp, sizeof(rsp)); return 0; } static int cmd_delete_link_key(struct btdev *dev, const void *data, uint8_t len) { struct bt_hci_rsp_delete_stored_link_key rsp; memset(&rsp, 0, sizeof(rsp)); rsp.status = BT_HCI_ERR_SUCCESS; rsp.num_keys = cpu_to_le16(0); cmd_complete(dev, BT_HCI_CMD_DELETE_STORED_LINK_KEY, &rsp, sizeof(rsp)); return 0; } static int cmd_read_local_name(struct btdev *dev, const void *data, uint8_t len) { struct bt_hci_rsp_read_local_name rsp; memset(&rsp, 0, sizeof(rsp)); rsp.status = BT_HCI_ERR_SUCCESS; memcpy(rsp.name, dev->name, 248); cmd_complete(dev, BT_HCI_CMD_READ_LOCAL_NAME, &rsp, sizeof(rsp)); return 0; } static int cmd_write_local_name(struct btdev *dev, const void *data, uint8_t len) { const struct bt_hci_cmd_write_local_name *cmd = data; uint8_t status; memcpy(dev->name, cmd->name, 248); status = BT_HCI_ERR_SUCCESS; cmd_complete(dev, BT_HCI_CMD_WRITE_LOCAL_NAME, &status, sizeof(status)); return 0; } static int cmd_read_accept_timeout(struct btdev *dev, const void *data, uint8_t len) { struct bt_hci_rsp_read_conn_accept_timeout rsp; memset(&rsp, 0, sizeof(rsp)); rsp.status = BT_HCI_ERR_SUCCESS; rsp.timeout = cpu_to_le16(dev->conn_accept_timeout); cmd_complete(dev, BT_HCI_CMD_READ_CONN_ACCEPT_TIMEOUT, &rsp, sizeof(rsp)); return 0; } static int cmd_write_accept_timeout(struct btdev *dev, const void *data, uint8_t len) { const struct bt_hci_cmd_write_conn_accept_timeout *cmd = data; uint8_t status; dev->conn_accept_timeout = le16_to_cpu(cmd->timeout); status = BT_HCI_ERR_SUCCESS; cmd_complete(dev, BT_HCI_CMD_WRITE_CONN_ACCEPT_TIMEOUT, &status, sizeof(status)); return 0; } static int cmd_read_page_timeout(struct btdev *dev, const void *data, uint8_t len) { struct bt_hci_rsp_read_page_timeout rsp; memset(&rsp, 0, sizeof(rsp)); rsp.status = BT_HCI_ERR_SUCCESS; rsp.timeout = cpu_to_le16(dev->page_timeout); cmd_complete(dev, BT_HCI_CMD_READ_PAGE_TIMEOUT, &rsp, sizeof(rsp)); return 0; } static int cmd_write_page_timeout(struct btdev *dev, const void *data, uint8_t len) { const struct bt_hci_cmd_write_page_timeout *cmd = data; uint8_t status = BT_HCI_ERR_SUCCESS; dev->page_timeout = le16_to_cpu(cmd->timeout); cmd_complete(dev, BT_HCI_CMD_WRITE_PAGE_TIMEOUT, &status, sizeof(status)); return 0; } static int cmd_read_scan_enable(struct btdev *dev, const void *data, uint8_t len) { struct bt_hci_rsp_read_scan_enable rsp; memset(&rsp, 0, sizeof(rsp)); rsp.status = BT_HCI_ERR_SUCCESS; rsp.enable = dev->scan_enable; cmd_complete(dev, BT_HCI_CMD_READ_SCAN_ENABLE, &rsp, sizeof(rsp)); return 0; } static int cmd_write_scan_enable(struct btdev *dev, const void *data, uint8_t len) { const struct bt_hci_cmd_write_scan_enable *cmd = data; uint8_t status = BT_HCI_ERR_SUCCESS; dev->scan_enable = cmd->enable; cmd_complete(dev, BT_HCI_CMD_WRITE_SCAN_ENABLE, &status, sizeof(status)); return 0; } static int cmd_read_page_scan(struct btdev *dev, const void *data, uint8_t len) { struct bt_hci_rsp_read_page_scan_activity rsp; memset(&rsp, 0, sizeof(rsp)); rsp.status = BT_HCI_ERR_SUCCESS; rsp.interval = cpu_to_le16(dev->page_scan_interval); rsp.window = cpu_to_le16(dev->page_scan_window); cmd_complete(dev, BT_HCI_CMD_READ_PAGE_SCAN_ACTIVITY, &rsp, sizeof(rsp)); return 0; } static int cmd_write_page_scan(struct btdev *dev, const void *data, uint8_t len) { const struct bt_hci_cmd_write_page_scan_activity *cmd = data; uint8_t status = BT_HCI_ERR_SUCCESS; dev->page_scan_interval = le16_to_cpu(cmd->interval); dev->page_scan_window = le16_to_cpu(cmd->window); cmd_complete(dev, BT_HCI_CMD_WRITE_PAGE_SCAN_ACTIVITY, &status, sizeof(status)); return 0; } static int cmd_read_inquiry_scan(struct btdev *dev, const void *data, uint8_t len) { struct bt_hci_rsp_read_inquiry_scan_activity rsp; memset(&rsp, 0, sizeof(rsp)); rsp.status = BT_HCI_ERR_SUCCESS; rsp.interval = cpu_to_le16(dev->inquiry_scan_interval); rsp.window = cpu_to_le16(dev->inquiry_scan_window); cmd_complete(dev, BT_HCI_CMD_READ_INQUIRY_SCAN_ACTIVITY, &rsp, sizeof(rsp)); return 0; } static int cmd_write_inquiry_scan(struct btdev *dev, const void *data, uint8_t len) { const struct bt_hci_cmd_write_inquiry_scan_activity *cmd = data; uint8_t status = BT_HCI_ERR_SUCCESS; dev->inquiry_scan_interval = le16_to_cpu(cmd->interval); dev->inquiry_scan_window = le16_to_cpu(cmd->window); cmd_complete(dev, BT_HCI_CMD_WRITE_INQUIRY_SCAN_ACTIVITY, &status, sizeof(status)); return 0; } static int cmd_read_auth_enable(struct btdev *dev, const void *data, uint8_t len) { struct bt_hci_rsp_read_auth_enable rsp; memset(&rsp, 0, sizeof(rsp)); rsp.status = BT_HCI_ERR_SUCCESS; rsp.enable = dev->auth_enable; cmd_complete(dev, BT_HCI_CMD_READ_AUTH_ENABLE, &rsp, sizeof(rsp)); return 0; } static int cmd_write_auth_enable(struct btdev *dev, const void *data, uint8_t len) { const struct bt_hci_cmd_write_auth_enable *cmd = data; uint8_t status = BT_HCI_ERR_SUCCESS; dev->auth_enable = cmd->enable; cmd_complete(dev, BT_HCI_CMD_WRITE_AUTH_ENABLE, &status, sizeof(status)); return 0; } static int cmd_read_class(struct btdev *dev, const void *data, uint8_t len) { struct bt_hci_rsp_read_class_of_dev rsp; memset(&rsp, 0, sizeof(rsp)); rsp.status = BT_HCI_ERR_SUCCESS; memcpy(rsp.dev_class, dev->dev_class, 3); cmd_complete(dev, BT_HCI_CMD_READ_CLASS_OF_DEV, &rsp, sizeof(rsp)); return 0; } static int cmd_write_class(struct btdev *dev, const void *data, uint8_t len) { const struct bt_hci_cmd_write_class_of_dev *cmd = data; uint8_t status = BT_HCI_ERR_SUCCESS; memcpy(dev->dev_class, cmd->dev_class, 3); cmd_complete(dev, BT_HCI_CMD_WRITE_CLASS_OF_DEV, &status, sizeof(status)); return 0; } static int cmd_read_voice(struct btdev *dev, const void *data, uint8_t len) { struct bt_hci_rsp_read_voice_setting rsp; memset(&rsp, 0, sizeof(rsp)); rsp.status = BT_HCI_ERR_SUCCESS; rsp.setting = cpu_to_le16(dev->voice_setting); cmd_complete(dev, BT_HCI_CMD_READ_VOICE_SETTING, &rsp, sizeof(rsp)); return 0; } static int cmd_write_voice(struct btdev *dev, const void *data, uint8_t len) { const struct bt_hci_cmd_write_voice_setting *cmd = data; uint8_t status = BT_HCI_ERR_SUCCESS; dev->voice_setting = le16_to_cpu(cmd->setting); cmd_complete(dev, BT_HCI_CMD_WRITE_VOICE_SETTING, &status, sizeof(status)); return 0; } static int cmd_read_tx_power_level(struct btdev *dev, const void *data, uint8_t len) { const struct bt_hci_cmd_read_tx_power *cmd = data; struct bt_hci_rsp_read_tx_power rsp; memset(&rsp, 0, sizeof(rsp)); rsp.handle = le16_to_cpu(cmd->handle); rsp.status = BT_HCI_ERR_SUCCESS; if (cmd->type) rsp.level = 4; else rsp.level = -1; cmd_complete(dev, BT_HCI_CMD_READ_TX_POWER, &rsp, sizeof(rsp)); return 0; } static int cmd_write_sync_flowctl(struct btdev *dev, const void *data, uint8_t len) { const struct bt_hci_cmd_write_sync_flow_control *cmd = data; uint8_t status = BT_HCI_ERR_SUCCESS; dev->sco_flowctl = cmd->enable; cmd_complete(dev, BT_HCI_CMD_WRITE_SYNC_FLOW_CONTROL, &status, sizeof(status)); return 0; } static int cmd_read_num_iac(struct btdev *dev, const void *data, uint8_t len) { struct bt_hci_rsp_read_num_supported_iac rsp; memset(&rsp, 0, sizeof(rsp)); rsp.status = BT_HCI_ERR_SUCCESS; rsp.num_iac = 0x01; cmd_complete(dev, BT_HCI_CMD_READ_NUM_SUPPORTED_IAC, &rsp, sizeof(rsp)); return 0; } static int cmd_read_current_iac_lap(struct btdev *dev, const void *data, uint8_t len) { struct bt_hci_rsp_read_current_iac_lap *rsp; rsp = alloca(sizeof(*rsp) + 3); rsp->status = BT_HCI_ERR_SUCCESS; rsp->num_iac = 0x01; rsp->iac_lap[0] = 0x33; rsp->iac_lap[1] = 0x8b; rsp->iac_lap[2] = 0x9e; cmd_complete(dev, BT_HCI_CMD_READ_CURRENT_IAC_LAP, rsp, sizeof(*rsp) + 3); return 0; } static int cmd_write_current_iac_lap(struct btdev *dev, const void *data, uint8_t len) { uint8_t status = BT_HCI_ERR_SUCCESS; cmd_complete(dev, BT_HCI_CMD_WRITE_CURRENT_IAC_LAP, &status, sizeof(status)); return 0; } static int cmd_read_inquiry_mode(struct btdev *dev, const void *data, uint8_t len) { struct bt_hci_rsp_read_inquiry_mode rsp; memset(&rsp, 0, sizeof(rsp)); rsp.status = BT_HCI_ERR_SUCCESS; rsp.mode = dev->inquiry_mode; cmd_complete(dev, BT_HCI_CMD_READ_INQUIRY_MODE, &rsp, sizeof(rsp)); return 0; } static int cmd_write_inquiry_mode(struct btdev *dev, const void *data, uint8_t len) { const struct bt_hci_cmd_write_inquiry_mode *cmd = data; uint8_t status = BT_HCI_ERR_SUCCESS; dev->inquiry_mode = cmd->mode; cmd_complete(dev, BT_HCI_CMD_WRITE_INQUIRY_MODE, &status, sizeof(status)); return 0; } static int cmd_read_page_scan_type(struct btdev *dev, const void *data, uint8_t len) { struct bt_hci_rsp_read_page_scan_type rsp; memset(&rsp, 0, sizeof(rsp)); rsp.status = BT_HCI_ERR_SUCCESS; rsp.type = dev->page_scan_type; cmd_complete(dev, BT_HCI_CMD_READ_PAGE_SCAN_TYPE, &rsp, sizeof(rsp)); return 0; } static int cmd_write_page_scan_type(struct btdev *dev, const void *data, uint8_t len) { const struct bt_hci_cmd_write_page_scan_type *cmd = data; uint8_t status = BT_HCI_ERR_SUCCESS; dev->page_scan_type = cmd->type; cmd_complete(dev, BT_HCI_CMD_WRITE_PAGE_SCAN_TYPE, &status, sizeof(status)); return 0; } static int cmd_read_afh_mode(struct btdev *dev, const void *data, uint8_t len) { struct bt_hci_rsp_read_afh_assessment_mode rsp; memset(&rsp, 0, sizeof(rsp)); rsp.status = BT_HCI_ERR_SUCCESS; rsp.mode = dev->afh_assessment_mode; cmd_complete(dev, BT_HCI_CMD_READ_AFH_ASSESSMENT_MODE, &rsp, sizeof(rsp)); return 0; } static int cmd_write_afh_mode(struct btdev *dev, const void *data, uint8_t len) { const struct bt_hci_cmd_write_afh_assessment_mode *cmd = data; uint8_t status = BT_HCI_ERR_SUCCESS; dev->afh_assessment_mode = cmd->mode; cmd_complete(dev, BT_HCI_CMD_WRITE_AFH_ASSESSMENT_MODE, &status, sizeof(status)); return 0; } static int cmd_read_local_ext_features(struct btdev *dev, const void *data, uint8_t len) { struct bt_hci_rsp_read_local_ext_features rsp; uint8_t page = ((const uint8_t *) data)[0]; memset(&rsp, 0, sizeof(rsp)); rsp.page = page; rsp.max_page = dev->max_page; if (page > dev->max_page) { rsp.status = BT_HCI_ERR_INVALID_PARAMETERS; goto done; } rsp.status = BT_HCI_ERR_SUCCESS; switch (page) { case 0x00: memcpy(rsp.features, dev->features, 8); break; case 0x01: btdev_get_host_features(dev, rsp.features); break; case 0x02: memcpy(rsp.features, dev->feat_page_2, 8); break; default: rsp.status = BT_HCI_ERR_INVALID_PARAMETERS; break; } done: cmd_complete(dev, BT_HCI_CMD_READ_LOCAL_EXT_FEATURES, &rsp, sizeof(rsp)); return 0; } static int cmd_read_country_code(struct btdev *dev, const void *data, uint8_t len) { struct bt_hci_rsp_read_country_code rsp; memset(&rsp, 0, sizeof(rsp)); rsp.status = BT_HCI_ERR_SUCCESS; rsp.code = dev->country_code; cmd_complete(dev, BT_HCI_CMD_READ_COUNTRY_CODE, &rsp, sizeof(rsp)); return 0; } static int cmd_read_rssi(struct btdev *dev, const void *data, uint8_t len) { const struct bt_hci_cmd_read_rssi *cmd = data; struct bt_hci_rsp_read_rssi rsp; memset(&rsp, 0, sizeof(rsp)); rsp.status = BT_HCI_ERR_SUCCESS; rsp.handle = le16_to_cpu(cmd->handle); rsp.rssi = -1; cmd_complete(dev, BT_HCI_CMD_READ_RSSI, &rsp, sizeof(rsp)); return 0; } static int cmd_read_clock(struct btdev *dev, const void *data, uint8_t len) { const struct bt_hci_cmd_read_clock *cmd = data; struct bt_hci_rsp_read_clock rsp; memset(&rsp, 0, sizeof(rsp)); rsp.status = BT_HCI_ERR_SUCCESS; rsp.handle = le16_to_cpu(cmd->handle); rsp.clock = 0x11223344; rsp.accuracy = 0x5566; cmd_complete(dev, BT_HCI_CMD_READ_CLOCK, &rsp, sizeof(rsp)); return 0; } static int cmd_enable_dut_mode(struct btdev *dev, const void *data, uint8_t len) { uint8_t status = BT_HCI_ERR_SUCCESS; cmd_complete(dev, BT_HCI_CMD_ENABLE_DUT_MODE, &status, sizeof(status)); return 0; } #define CMD_COMMON_BREDR_20 \ CMD(BT_HCI_CMD_INQUIRY, cmd_inquiry, cmd_inquiry_complete), \ CMD(BT_HCI_CMD_INQUIRY_CANCEL, cmd_inquiry_cancel, NULL), \ CMD(BT_HCI_CMD_CREATE_CONN, cmd_create_conn, \ cmd_create_conn_complete), \ CMD(BT_HCI_CMD_ADD_SCO_CONN, cmd_add_sco_conn, NULL), \ CMD(BT_HCI_CMD_CREATE_CONN_CANCEL, cmd_create_conn_cancel, \ cmd_create_conn_cancel_complete), \ CMD(BT_HCI_CMD_ACCEPT_CONN_REQUEST, cmd_accept_conn, \ cmd_accept_conn_complete), \ CMD(BT_HCI_CMD_REJECT_CONN_REQUEST, cmd_reject_conn, \ cmd_reject_conn_complete), \ CMD(BT_HCI_CMD_LINK_KEY_REQUEST_REPLY, cmd_link_key_reply, \ cmd_link_key_reply_complete), \ CMD(BT_HCI_CMD_LINK_KEY_REQUEST_NEG_REPLY, \ cmd_link_key_neg_reply, \ cmd_link_key_neg_reply_complete), \ CMD(BT_HCI_CMD_PIN_CODE_REQUEST_REPLY, cmd_pin_code_reply, \ cmd_pin_code_reply_complete), \ CMD(BT_HCI_CMD_PIN_CODE_REQUEST_NEG_REPLY, \ cmd_pin_code_neg_reply, \ cmd_pin_code_neg_reply_complete), \ CMD(BT_HCI_CMD_AUTH_REQUESTED, cmd_auth_requested, \ cmd_auth_requested_complete), \ CMD(BT_HCI_CMD_SET_CONN_ENCRYPT, cmd_set_conn_encrypt, \ cmd_set_conn_encrypt_complete), \ CMD(BT_HCI_CMD_REMOTE_NAME_REQUEST, cmd_remote_name, \ cmd_remote_name_complete), \ CMD(BT_HCI_CMD_REMOTE_NAME_REQUEST_CANCEL, cmd_remote_name_cancel, \ cmd_remote_name_cancel_complete), \ CMD(BT_HCI_CMD_READ_REMOTE_FEATURES, cmd_read_remote_features, \ cmd_read_remote_features_complete), \ CMD(BT_HCI_CMD_READ_REMOTE_EXT_FEATURES, \ cmd_read_remote_ext_features, \ cmd_read_remote_ext_features_compl), \ CMD(BT_HCI_CMD_READ_CLOCK_OFFSET, cmd_read_clock_offset, \ cmd_read_clock_offset_complete), \ CMD(BT_HCI_CMD_READ_DEFAULT_LINK_POLICY, cmd_read_link_policy, NULL), \ CMD(BT_HCI_CMD_WRITE_DEFAULT_LINK_POLICY, cmd_write_link_policy, \ NULL), \ CMD(BT_HCI_CMD_SET_EVENT_FILTER, cmd_set_event_filter, NULL), \ CMD(BT_HCI_CMD_READ_STORED_LINK_KEY, cmd_read_link_key, NULL), \ CMD(BT_HCI_CMD_WRITE_STORED_LINK_KEY, cmd_write_link_key, NULL), \ CMD(BT_HCI_CMD_DELETE_STORED_LINK_KEY, cmd_delete_link_key, NULL), \ CMD(BT_HCI_CMD_READ_LOCAL_NAME, cmd_read_local_name, NULL), \ CMD(BT_HCI_CMD_WRITE_LOCAL_NAME, cmd_write_local_name, NULL), \ CMD(BT_HCI_CMD_READ_CONN_ACCEPT_TIMEOUT, cmd_read_accept_timeout, \ NULL), \ CMD(BT_HCI_CMD_WRITE_CONN_ACCEPT_TIMEOUT, cmd_write_accept_timeout, \ NULL), \ CMD(BT_HCI_CMD_READ_PAGE_TIMEOUT, cmd_read_page_timeout, NULL), \ CMD(BT_HCI_CMD_WRITE_PAGE_TIMEOUT, cmd_write_page_timeout, NULL), \ CMD(BT_HCI_CMD_READ_SCAN_ENABLE, cmd_read_scan_enable, NULL), \ CMD(BT_HCI_CMD_WRITE_SCAN_ENABLE, cmd_write_scan_enable, NULL), \ CMD(BT_HCI_CMD_READ_PAGE_SCAN_ACTIVITY, cmd_read_page_scan, NULL), \ CMD(BT_HCI_CMD_WRITE_PAGE_SCAN_ACTIVITY, cmd_write_page_scan, NULL), \ CMD(BT_HCI_CMD_READ_INQUIRY_SCAN_ACTIVITY, cmd_read_inquiry_scan, \ NULL), \ CMD(BT_HCI_CMD_WRITE_INQUIRY_SCAN_ACTIVITY, cmd_write_inquiry_scan, \ NULL), \ CMD(BT_HCI_CMD_READ_AUTH_ENABLE, cmd_read_auth_enable, NULL), \ CMD(BT_HCI_CMD_WRITE_AUTH_ENABLE, cmd_write_auth_enable, NULL), \ CMD(BT_HCI_CMD_READ_CLASS_OF_DEV, cmd_read_class, NULL), \ CMD(BT_HCI_CMD_WRITE_CLASS_OF_DEV, cmd_write_class, NULL), \ CMD(BT_HCI_CMD_READ_VOICE_SETTING, cmd_read_voice, NULL), \ CMD(BT_HCI_CMD_WRITE_VOICE_SETTING, cmd_write_voice, NULL), \ CMD(BT_HCI_CMD_READ_TX_POWER, cmd_read_tx_power_level, NULL), \ CMD(BT_HCI_CMD_WRITE_SYNC_FLOW_CONTROL, cmd_write_sync_flowctl, NULL), \ CMD(BT_HCI_CMD_READ_NUM_SUPPORTED_IAC, cmd_read_num_iac, NULL), \ CMD(BT_HCI_CMD_READ_CURRENT_IAC_LAP, cmd_read_current_iac_lap, \ NULL), \ CMD(BT_HCI_CMD_WRITE_CURRENT_IAC_LAP, cmd_write_current_iac_lap, \ NULL), \ CMD(BT_HCI_CMD_READ_INQUIRY_MODE, cmd_read_inquiry_mode, NULL), \ CMD(BT_HCI_CMD_WRITE_INQUIRY_MODE, cmd_write_inquiry_mode, NULL), \ CMD(BT_HCI_CMD_READ_PAGE_SCAN_TYPE, cmd_read_page_scan_type, NULL), \ CMD(BT_HCI_CMD_WRITE_PAGE_SCAN_TYPE, cmd_write_page_scan_type, NULL), \ CMD(BT_HCI_CMD_READ_AFH_ASSESSMENT_MODE, cmd_read_afh_mode, NULL), \ CMD(BT_HCI_CMD_WRITE_AFH_ASSESSMENT_MODE, cmd_write_afh_mode, NULL), \ CMD(BT_HCI_CMD_READ_LOCAL_EXT_FEATURES, cmd_read_local_ext_features, \ NULL), \ CMD(BT_HCI_CMD_READ_COUNTRY_CODE, cmd_read_country_code, NULL), \ CMD(BT_HCI_CMD_READ_RSSI, cmd_read_rssi, NULL), \ CMD(BT_HCI_CMD_READ_CLOCK, cmd_read_clock, NULL), \ CMD(BT_HCI_CMD_ENABLE_DUT_MODE, cmd_enable_dut_mode, NULL) static void set_common_commands_bredr20(struct btdev *btdev) { btdev->commands[0] |= 0x01; /* Inquiry */ btdev->commands[0] |= 0x02; /* Inquiry Cancel */ btdev->commands[0] |= 0x10; /* Create Connection */ btdev->commands[0] |= 0x40; /* Add SCO Connection */ btdev->commands[0] |= 0x80; /* Cancel Create Connection */ btdev->commands[1] |= 0x01; /* Accept Connection Request */ btdev->commands[1] |= 0x02; /* Reject Connection Request */ btdev->commands[1] |= 0x04; /* Link Key Request Reply */ btdev->commands[1] |= 0x08; /* Link Key Request Negative Reply */ btdev->commands[1] |= 0x10; /* PIN Code Request Reply */ btdev->commands[1] |= 0x20; /* PIN Code Request Negative Reply */ btdev->commands[1] |= 0x80; /* Authentication Requested */ btdev->commands[2] |= 0x01; /* Set Connection Encryption */ btdev->commands[2] |= 0x08; /* Remote Name Request */ btdev->commands[2] |= 0x10; /* Cancel Remote Name Request */ btdev->commands[2] |= 0x20; /* Read Remote Supported Features */ btdev->commands[2] |= 0x40; /* Read Remote Extended Features */ btdev->commands[3] |= 0x01; /* Read Clock Offset */ btdev->commands[5] |= 0x08; /* Read Default Link Policy */ btdev->commands[5] |= 0x10; /* Write Default Link Policy */ btdev->commands[6] |= 0x01; /* Set Event Filter */ btdev->commands[6] |= 0x20; /* Read Stored Link Key */ btdev->commands[6] |= 0x40; /* Write Stored Link Key */ btdev->commands[6] |= 0x80; /* Delete Stored Link Key */ btdev->commands[7] |= 0x01; /* Write Local Name */ btdev->commands[7] |= 0x02; /* Read Local Name */ btdev->commands[7] |= 0x04; /* Read Connection Accept Timeout */ btdev->commands[7] |= 0x08; /* Write Connection Accept Timeout */ btdev->commands[7] |= 0x10; /* Read Page Timeout */ btdev->commands[7] |= 0x20; /* Write Page Timeout */ btdev->commands[7] |= 0x40; /* Read Scan Enable */ btdev->commands[7] |= 0x80; /* Write Scan Enable */ btdev->commands[8] |= 0x01; /* Read Page Scan Activity */ btdev->commands[8] |= 0x02; /* Write Page Scan Activity */ btdev->commands[8] |= 0x04; /* Read Inquiry Scan Activity */ btdev->commands[8] |= 0x08; /* Write Inquiry Scan Activity */ btdev->commands[8] |= 0x10; /* Read Authentication Enable */ btdev->commands[8] |= 0x20; /* Write Authentication Enable */ btdev->commands[9] |= 0x01; /* Read Class Of Device */ btdev->commands[9] |= 0x02; /* Write Class Of Device */ btdev->commands[9] |= 0x04; /* Read Voice Setting */ btdev->commands[9] |= 0x08; /* Write Voice Setting */ btdev->commands[10] |= 0x04; /* Read TX Power Level */ btdev->commands[10] |= BIT(4); /* Write Sync Flow Control */ btdev->commands[11] |= 0x04; /* Read Number of Supported IAC */ btdev->commands[11] |= 0x08; /* Read Current IAC LAP */ btdev->commands[11] |= 0x10; /* Write Current IAC LAP */ btdev->commands[12] |= 0x40; /* Read Inquiry Mode */ btdev->commands[12] |= 0x80; /* Write Inquiry Mode */ btdev->commands[13] |= 0x01; /* Read Page Scan Type */ btdev->commands[13] |= 0x02; /* Write Page Scan Type */ btdev->commands[13] |= 0x04; /* Read AFH Assess Mode */ btdev->commands[13] |= 0x08; /* Write AFH Assess Mode */ btdev->commands[14] |= 0x40; /* Read Local Extended Features */ btdev->commands[15] |= 0x01; /* Read Country Code */ btdev->commands[15] |= 0x20; /* Read RSSI */ btdev->commands[15] |= 0x80; /* Read Clock */ btdev->commands[16] |= 0x04; /* Enable Device Under Test Mode */ } static int cmd_enhanced_setup_sync_conn(struct btdev *dev, const void *data, uint8_t len) { const struct bt_hci_cmd_enhanced_setup_sync_conn *cmd = data; uint8_t status = BT_HCI_ERR_SUCCESS; if (cmd->tx_coding_format[0] > 5) status = BT_HCI_ERR_INVALID_PARAMETERS; cmd_status(dev, status, BT_HCI_CMD_ENHANCED_SETUP_SYNC_CONN); return 0; } static int cmd_enhanced_setup_sync_conn_complete(struct btdev *dev, const void *data, uint8_t len) { const struct bt_hci_cmd_enhanced_setup_sync_conn *cmd = data; struct bt_hci_evt_sync_conn_complete cc; struct btdev_conn *conn; memset(&cc, 0, sizeof(cc)); conn = queue_find(dev->conns, match_handle, UINT_TO_PTR(le16_to_cpu(cmd->handle))); if (!conn) { cc.status = BT_HCI_ERR_UNKNOWN_CONN_ID; goto done; } conn = conn_add_sco(conn); if (!conn) { cc.status = BT_HCI_ERR_MEM_CAPACITY_EXCEEDED; goto done; } /* TODO: HCI_Connection_Request connection flow */ cc.status = BT_HCI_ERR_SUCCESS; memcpy(cc.bdaddr, conn->link->dev->bdaddr, 6); cc.handle = cpu_to_le16(conn->handle); cc.link_type = 0x02; cc.tx_interval = 0x000c; cc.retrans_window = 0x06; cc.rx_pkt_len = 60; cc.tx_pkt_len = 60; cc.air_mode = cmd->tx_coding_format[0]; done: send_event(dev, BT_HCI_EVT_SYNC_CONN_COMPLETE, &cc, sizeof(cc)); if (conn) send_event(conn->link->dev, BT_HCI_EVT_SYNC_CONN_COMPLETE, &cc, sizeof(cc)); return 0; } static int cmd_setup_sync_conn(struct btdev *dev, const void *data, uint8_t len) { cmd_status(dev, BT_HCI_ERR_SUCCESS, BT_HCI_CMD_SETUP_SYNC_CONN); return 0; } static int cmd_setup_sync_conn_complete(struct btdev *dev, const void *data, uint8_t len) { const struct bt_hci_cmd_setup_sync_conn *cmd = data; struct bt_hci_evt_sync_conn_complete cc; struct btdev_conn *conn; memset(&cc, 0, sizeof(cc)); conn = queue_find(dev->conns, match_handle, UINT_TO_PTR(le16_to_cpu(cmd->handle))); if (!conn) { cc.status = BT_HCI_ERR_UNKNOWN_CONN_ID; goto done; } conn = conn_add_sco(conn); if (!conn) { cc.status = BT_HCI_ERR_MEM_CAPACITY_EXCEEDED; goto done; } cc.status = BT_HCI_ERR_SUCCESS; memcpy(cc.bdaddr, conn->link->dev->bdaddr, 6); cc.handle = cpu_to_le16(conn->handle); cc.link_type = 0x02; cc.tx_interval = 0x000c; cc.retrans_window = 0x06; cc.rx_pkt_len = 60; cc.tx_pkt_len = 60; cc.air_mode = (cmd->voice_setting == 0x0060) ? 0x02 : 0x03; done: send_event(dev, BT_HCI_EVT_SYNC_CONN_COMPLETE, &cc, sizeof(cc)); if (conn) send_event(conn->link->dev, BT_HCI_EVT_SYNC_CONN_COMPLETE, &cc, sizeof(cc)); return 0; } static int cmd_read_ext_inquiry(struct btdev *dev, const void *data, uint8_t len) { struct bt_hci_rsp_read_ext_inquiry_response rsp; memset(&rsp, 0, sizeof(rsp)); rsp.status = BT_HCI_ERR_SUCCESS; rsp.fec = dev->ext_inquiry_fec; memcpy(rsp.data, dev->ext_inquiry_rsp, 240); cmd_complete(dev, BT_HCI_CMD_READ_EXT_INQUIRY_RESPONSE, &rsp, sizeof(rsp)); return 0; } static int cmd_write_ext_inquiry(struct btdev *dev, const void *data, uint8_t len) { const struct bt_hci_cmd_write_ext_inquiry_response *cmd = data; uint8_t status = BT_HCI_ERR_SUCCESS; dev->ext_inquiry_fec = cmd->fec; memcpy(dev->ext_inquiry_rsp, cmd->data, 240); cmd_complete(dev, BT_HCI_CMD_WRITE_EXT_INQUIRY_RESPONSE, &status, sizeof(status)); return 0; } static int cmd_read_ssp_mode(struct btdev *dev, const void *data, uint8_t len) { struct bt_hci_rsp_read_simple_pairing_mode rsp; memset(&rsp, 0, sizeof(rsp)); rsp.status = BT_HCI_ERR_SUCCESS; rsp.mode = dev->simple_pairing_mode; cmd_complete(dev, BT_HCI_CMD_READ_SIMPLE_PAIRING_MODE, &rsp, sizeof(rsp)); return 0; } static int cmd_write_ssp_mode(struct btdev *dev, const void *data, uint8_t len) { const struct bt_hci_cmd_write_simple_pairing_mode *cmd = data; uint8_t status = BT_HCI_ERR_SUCCESS; dev->simple_pairing_mode = cmd->mode; cmd_complete(dev, BT_HCI_CMD_WRITE_SIMPLE_PAIRING_MODE, &status, sizeof(status)); return 0; } static int cmd_read_oob_data(struct btdev *dev, const void *data, uint8_t len) { struct bt_hci_rsp_read_local_oob_data rsp; memset(&rsp, 0, sizeof(rsp)); rsp.status = BT_HCI_ERR_SUCCESS; cmd_complete(dev, BT_HCI_CMD_READ_LOCAL_OOB_DATA, &rsp, sizeof(rsp)); return 0; } static int cmd_read_inquiry_tx_power(struct btdev *dev, const void *data, uint8_t len) { struct bt_hci_rsp_read_inquiry_resp_tx_power rsp; memset(&rsp, 0, sizeof(rsp)); rsp.status = BT_HCI_ERR_SUCCESS; rsp.level = 0; cmd_complete(dev, BT_HCI_CMD_READ_INQUIRY_RESP_TX_POWER, &rsp, sizeof(rsp)); return 0; } static int cmd_write_inquiry_tx_power(struct btdev *dev, const void *data, uint8_t len) { return -ENOTSUP; } static int cmd_io_cap_reply(struct btdev *dev, const void *data, uint8_t len) { const struct bt_hci_cmd_io_capability_request_reply *cmd = data; struct bt_hci_evt_io_capability_response ev; struct bt_hci_rsp_io_capability_request_reply rsp; struct btdev_conn *conn; uint8_t status; conn = queue_find(dev->conns, match_bdaddr, cmd->bdaddr); if (!conn) { status = BT_HCI_ERR_UNKNOWN_CONN_ID; goto done; } status = BT_HCI_ERR_SUCCESS; dev->io_cap = cmd->capability; dev->auth_req = cmd->authentication; memcpy(ev.bdaddr, dev->bdaddr, 6); ev.capability = cmd->capability; ev.oob_data = cmd->oob_data; ev.authentication = cmd->authentication; send_event(conn->link->dev, BT_HCI_EVT_IO_CAPABILITY_RESPONSE, &ev, sizeof(ev)); if (conn->link->dev->io_cap) { struct bt_hci_evt_user_confirm_request cfm; memcpy(cfm.bdaddr, dev->bdaddr, 6); cfm.passkey = 0; send_event(conn->link->dev, BT_HCI_EVT_USER_CONFIRM_REQUEST, &cfm, sizeof(cfm)); memcpy(cfm.bdaddr, cmd->bdaddr, 6); send_event(dev, BT_HCI_EVT_USER_CONFIRM_REQUEST, &cfm, sizeof(cfm)); } else { send_event(conn->link->dev, BT_HCI_EVT_IO_CAPABILITY_REQUEST, dev->bdaddr, 6); } done: rsp.status = status; memcpy(rsp.bdaddr, cmd->bdaddr, 6); cmd_complete(dev, BT_HCI_CMD_IO_CAPABILITY_REQUEST_REPLY, &rsp, sizeof(rsp)); return 0; } static void ssp_complete(struct btdev *btdev, const uint8_t *bdaddr, uint8_t status, bool wait) { struct bt_hci_evt_simple_pairing_complete iev, aev; struct btdev_conn *conn; struct btdev_conn *init, *accp; conn = queue_find(btdev->conns, match_bdaddr, bdaddr); if (!conn) return; btdev->ssp_status = status; btdev->ssp_auth_complete = true; if (!conn->link->dev->ssp_auth_complete && wait) return; if (status == BT_HCI_ERR_SUCCESS && conn->link->dev->ssp_status != BT_HCI_ERR_SUCCESS) status = conn->link->dev->ssp_status; iev.status = status; aev.status = status; if (btdev->auth_init) { init = conn; accp = conn->link; memcpy(iev.bdaddr, bdaddr, 6); memcpy(aev.bdaddr, btdev->bdaddr, 6); } else { init = conn->link; accp = conn; memcpy(iev.bdaddr, btdev->bdaddr, 6); memcpy(aev.bdaddr, bdaddr, 6); } send_event(init->dev, BT_HCI_EVT_SIMPLE_PAIRING_COMPLETE, &iev, sizeof(iev)); send_event(accp->dev, BT_HCI_EVT_SIMPLE_PAIRING_COMPLETE, &aev, sizeof(aev)); if (status == BT_HCI_ERR_SUCCESS) { link_key_notify(init->dev, iev.bdaddr, LINK_KEY_DUMMY); link_key_notify(accp->dev, aev.bdaddr, LINK_KEY_DUMMY); } auth_complete(init, status); } static int cmd_user_confirm_reply(struct btdev *dev, const void *data, uint8_t len) { struct bt_hci_rsp_user_confirm_request_reply rsp; memset(&rsp, 0, sizeof(rsp)); rsp.status = BT_HCI_ERR_SUCCESS; memcpy(rsp.bdaddr, data, 6); cmd_complete(dev, BT_HCI_CMD_USER_CONFIRM_REQUEST_REPLY, &rsp, sizeof(rsp)); ssp_complete(dev, data, BT_HCI_ERR_SUCCESS, true); return 0; } static int cmd_user_confirm_negative_reply(struct btdev *dev, const void *data, uint8_t len) { struct bt_hci_rsp_user_confirm_request_neg_reply rsp; memset(&rsp, 0, sizeof(rsp)); rsp.status = BT_HCI_ERR_SUCCESS; memcpy(rsp.bdaddr, data, 6); cmd_complete(dev, BT_HCI_CMD_USER_CONFIRM_REQUEST_NEG_REPLY, &rsp, sizeof(rsp)); ssp_complete(dev, data, BT_HCI_ERR_AUTH_FAILURE, true); return 0; } static int cmd_user_passkey_reply(struct btdev *dev, const void *data, uint8_t len) { return -ENOTSUP; } static int cmd_user_passkey_negative_reply(struct btdev *dev, const void *data, uint8_t len) { return -ENOTSUP; } static int cmd_io_cap_negative_reply(struct btdev *dev, const void *data, uint8_t len) { const struct bt_hci_cmd_io_capability_request_neg_reply *cmd = data; struct bt_hci_rsp_io_capability_request_neg_reply rsp; memset(&rsp, 0, sizeof(rsp)); rsp.status = BT_HCI_ERR_SUCCESS; memcpy(rsp.bdaddr, cmd->bdaddr, 6); cmd_complete(dev, BT_HCI_CMD_IO_CAPABILITY_REQUEST_NEG_REPLY, &rsp, sizeof(rsp)); ssp_complete(dev, cmd->bdaddr, BT_HCI_ERR_AUTH_FAILURE, false); return 0; } static int cmd_read_encrypt_key_size(struct btdev *dev, const void *data, uint8_t len) { const struct bt_hci_cmd_read_encrypt_key_size *cmd = data; struct bt_hci_rsp_read_encrypt_key_size rsp; struct btdev_conn *conn; memset(&rsp, 0, sizeof(rsp)); rsp.handle = cmd->handle; conn = queue_find(dev->conns, match_handle, UINT_TO_PTR(le16_to_cpu(cmd->handle))); if (conn) { rsp.status = BT_HCI_ERR_SUCCESS; rsp.key_size = 16; } else { rsp.status = BT_HCI_ERR_UNKNOWN_CONN_ID; rsp.key_size = 0; } cmd_complete(dev, BT_HCI_CMD_READ_ENCRYPT_KEY_SIZE, &rsp, sizeof(rsp)); return 0; } static int cmd_read_data_block_size(struct btdev *dev, const void *data, uint8_t len) { struct bt_hci_rsp_read_data_block_size rsp; memset(&rsp, 0, sizeof(rsp)); rsp.status = BT_HCI_ERR_SUCCESS; rsp.max_acl_len = cpu_to_le16(dev->acl_mtu); rsp.block_len = cpu_to_le16(dev->acl_mtu); rsp.num_blocks = cpu_to_le16(dev->acl_max_pkt); cmd_complete(dev, BT_HCI_CMD_READ_DATA_BLOCK_SIZE, &rsp, sizeof(rsp)); return 0; } static int cmd_read_local_codecs(struct btdev *dev, const void *data, uint8_t len) { struct bt_hci_rsp_read_local_codecs *rsp; rsp = alloca(sizeof(*rsp) + 7); rsp->status = BT_HCI_ERR_SUCCESS; rsp->num_codecs = 0x06; rsp->codec[0] = 0x00; rsp->codec[1] = 0x01; rsp->codec[2] = 0x02; rsp->codec[3] = 0x03; rsp->codec[4] = 0x04; rsp->codec[5] = 0x05; rsp->codec[6] = 0x00; cmd_complete(dev, BT_HCI_CMD_READ_LOCAL_CODECS, rsp, sizeof(*rsp) + 7); return 0; } static int cmd_get_mws_transport_config(struct btdev *dev, const void *data, uint8_t len) { struct bt_hci_rsp_get_mws_transport_config *rsp; rsp = alloca(sizeof(*rsp)); rsp->status = BT_HCI_ERR_SUCCESS; rsp->num_transports = 0x00; cmd_complete(dev, BT_HCI_CMD_GET_MWS_TRANSPORT_CONFIG, rsp, sizeof(*rsp)); return 0; } #define CMD_BREDR \ CMD(BT_HCI_CMD_SETUP_SYNC_CONN, cmd_setup_sync_conn, \ cmd_setup_sync_conn_complete), \ CMD(BT_HCI_CMD_READ_EXT_INQUIRY_RESPONSE, cmd_read_ext_inquiry, NULL), \ CMD(BT_HCI_CMD_WRITE_EXT_INQUIRY_RESPONSE, cmd_write_ext_inquiry, \ NULL), \ CMD(BT_HCI_CMD_READ_SIMPLE_PAIRING_MODE, cmd_read_ssp_mode, NULL), \ CMD(BT_HCI_CMD_WRITE_SIMPLE_PAIRING_MODE, cmd_write_ssp_mode, NULL), \ CMD(BT_HCI_CMD_READ_LOCAL_OOB_DATA, cmd_read_oob_data, NULL), \ CMD(BT_HCI_CMD_READ_INQUIRY_RESP_TX_POWER, cmd_read_inquiry_tx_power, \ NULL), \ CMD(BT_HCI_CMD_WRITE_INQUIRY_TX_POWER, cmd_write_inquiry_tx_power, \ NULL), \ CMD(BT_HCI_CMD_IO_CAPABILITY_REQUEST_REPLY, cmd_io_cap_reply, NULL), \ CMD(BT_HCI_CMD_USER_CONFIRM_REQUEST_REPLY, cmd_user_confirm_reply, \ NULL), \ CMD(BT_HCI_CMD_USER_CONFIRM_REQUEST_NEG_REPLY, \ cmd_user_confirm_negative_reply, \ NULL), \ CMD(BT_HCI_CMD_USER_PASSKEY_REQUEST_NEG_REPLY, cmd_user_passkey_reply, \ NULL), \ CMD(BT_HCI_CMD_USER_PASSKEY_REQUEST_NEG_REPLY, \ cmd_user_passkey_negative_reply, \ NULL), \ CMD(BT_HCI_CMD_IO_CAPABILITY_REQUEST_NEG_REPLY, \ cmd_io_cap_negative_reply, NULL), \ CMD(BT_HCI_CMD_READ_ENCRYPT_KEY_SIZE, cmd_read_encrypt_key_size, \ NULL), \ CMD(BT_HCI_CMD_READ_DATA_BLOCK_SIZE, cmd_read_data_block_size, NULL), \ CMD(BT_HCI_CMD_READ_LOCAL_CODECS, cmd_read_local_codecs, NULL), \ CMD(BT_HCI_CMD_GET_MWS_TRANSPORT_CONFIG, cmd_get_mws_transport_config, \ NULL), \ CMD(BT_HCI_CMD_ENHANCED_SETUP_SYNC_CONN, cmd_enhanced_setup_sync_conn, \ cmd_enhanced_setup_sync_conn_complete) static const struct btdev_cmd cmd_bredr[] = { CMD_COMMON_ALL, CMD_COMMON_BREDR_LE, CMD_COMMON_BREDR_20, CMD_BREDR, {} }; static void set_bredr_commands(struct btdev *btdev) { set_common_commands_all(btdev); set_common_commands_bredrle(btdev); set_common_commands_bredr20(btdev); btdev->commands[16] |= 0x08; /* Setup Synchronous Connection */ btdev->commands[17] |= 0x01; /* Read Extended Inquiry Response */ btdev->commands[17] |= 0x02; /* Write Extended Inquiry Response */ btdev->commands[17] |= 0x20; /* Read Simple Pairing Mode */ btdev->commands[17] |= 0x40; /* Write Simple Pairing Mode */ btdev->commands[17] |= 0x80; /* Read Local OOB Data */ btdev->commands[18] |= 0x01; /* Read Inquiry Response TX Power */ btdev->commands[18] |= 0x02; /* Write Inquiry Response TX Power */ btdev->commands[18] |= 0x80; /* IO Capability Request Reply */ btdev->commands[19] |= 0x01; /* User Confirmation Request Reply */ btdev->commands[19] |= 0x02; /* User Confirmation Request N Reply */ btdev->commands[19] |= 0x04; /* User Passkey Request Reply */ btdev->commands[19] |= 0x08; /* User Passkey Request N Reply */ btdev->commands[20] |= 0x08; /* IO Capability Request N Reply */ btdev->commands[20] |= 0x10; /* Read Encryption Key Size */ btdev->commands[23] |= 0x04; /* Read Data Block Size */ btdev->commands[29] |= 0x20; /* Read Local Supported Codecs */ btdev->commands[29] |= 0x08; /* Enhanced Setup Synchronous Conn */ btdev->commands[30] |= 0x08; /* Get MWS Transport Layer Config */ btdev->cmds = cmd_bredr; } static const struct btdev_cmd cmd_bredr_20[] = { CMD_COMMON_ALL, CMD_COMMON_BREDR_LE, CMD_COMMON_BREDR_20, {} }; static void set_bredr20_commands(struct btdev *btdev) { set_common_commands_all(btdev); set_common_commands_bredrle(btdev); set_common_commands_bredr20(btdev); btdev->cmds = cmd_bredr_20; } static int cmd_read_le_host_supported(struct btdev *dev, const void *data, uint8_t len) { struct bt_hci_rsp_read_le_host_supported rsp; memset(&rsp, 0, sizeof(rsp)); rsp.status = BT_HCI_ERR_SUCCESS; rsp.supported = dev->le_supported; rsp.simultaneous = dev->le_simultaneous; cmd_complete(dev, BT_HCI_CMD_READ_LE_HOST_SUPPORTED, &rsp, sizeof(rsp)); return 0; } static int cmd_write_le_host_supported(struct btdev *dev, const void *data, uint8_t len) { const struct bt_hci_cmd_write_le_host_supported *cmd = data; uint8_t status; dev->le_supported = cmd->supported; dev->le_simultaneous = cmd->simultaneous; status = BT_HCI_ERR_SUCCESS; cmd_complete(dev, BT_HCI_CMD_WRITE_LE_HOST_SUPPORTED, &status, sizeof(status)); return 0; } static int cmd_le_set_event_mask(struct btdev *dev, const void *data, uint8_t len) { const struct bt_hci_cmd_le_set_event_mask *cmd = data; uint8_t status; memcpy(dev->le_event_mask, cmd->mask, 8); status = BT_HCI_ERR_SUCCESS; cmd_complete(dev, BT_HCI_CMD_LE_SET_EVENT_MASK, &status, sizeof(status)); return 0; } static int cmd_le_read_buffer_size(struct btdev *dev, const void *data, uint8_t len) { struct bt_hci_rsp_le_read_buffer_size rsp; memset(&rsp, 0, sizeof(rsp)); rsp.status = BT_HCI_ERR_SUCCESS; rsp.le_mtu = cpu_to_le16(dev->acl_mtu); rsp.le_max_pkt = dev->acl_max_pkt; cmd_complete(dev, BT_HCI_CMD_LE_READ_BUFFER_SIZE, &rsp, sizeof(rsp)); return 0; } static int cmd_le_read_local_features(struct btdev *dev, const void *data, uint8_t len) { struct bt_hci_rsp_le_read_local_features rsp; memset(&rsp, 0, sizeof(rsp)); rsp.status = BT_HCI_ERR_SUCCESS; memcpy(rsp.features, dev->le_features, 8); cmd_complete(dev, BT_HCI_CMD_LE_READ_LOCAL_FEATURES, &rsp, sizeof(rsp)); return 0; } static int cmd_set_random_address(struct btdev *dev, const void *data, uint8_t len) { const struct bt_hci_cmd_le_set_random_address *cmd = data; uint8_t status; /* If the Host issues this command when any of advertising * (created using legacy advertising commands), scanning, or initiating * are enabled, the Controller shall return the error code * Command Disallowed (0x0C). */ if (dev->le_scan_enable || (dev->le_adv_enable && queue_isempty(dev->le_ext_adv))) { status = BT_HCI_ERR_COMMAND_DISALLOWED; goto done; } memcpy(dev->random_addr, cmd->addr, 6); status = BT_HCI_ERR_SUCCESS; done: cmd_complete(dev, BT_HCI_CMD_LE_SET_RANDOM_ADDRESS, &status, sizeof(status)); return 0; } static uint16_t ext_legacy_adv_type(uint8_t type) { switch (type) { case 0x00: /* Connectable undirected - ADV_IND" */ return 0x0013; case 0x01: /* Connectable directed - ADV_DIRECT_IND */ return 0x0015; case 0x02: /* Scannable undirected - ADV_SCAN_IND */ return 0x0012; case 0x03: /* Non connectable undirected - ADV_NONCONN_IND */ return 0x0010; case 0x04: /* Scan response - SCAN_RSP */ return 0x0012; } return 0x0000; } static int cmd_set_adv_params(struct btdev *dev, const void *data, uint8_t len) { const struct bt_hci_cmd_le_set_adv_parameters *cmd = data; uint8_t status; if (dev->le_adv_enable) { status = BT_HCI_ERR_COMMAND_DISALLOWED; goto done; } dev->le_adv_type = cmd->type; /* Use Legacy PDU if the remote is using EXT Scan */ dev->le_ext_adv_type = ext_legacy_adv_type(cmd->type); dev->le_adv_own_addr = cmd->own_addr_type; dev->le_adv_direct_addr_type = cmd->direct_addr_type; memcpy(dev->le_adv_direct_addr, cmd->direct_addr, 6); dev->le_adv_filter_policy = cmd->filter_policy; status = BT_HCI_ERR_SUCCESS; done: cmd_complete(dev, BT_HCI_CMD_LE_SET_ADV_PARAMETERS, &status, sizeof(status)); return 0; } static int cmd_read_adv_tx_power(struct btdev *dev, const void *data, uint8_t len) { struct bt_hci_rsp_le_read_adv_tx_power rsp; rsp.status = BT_HCI_ERR_SUCCESS; rsp.level = 0; cmd_complete(dev, BT_HCI_CMD_LE_READ_ADV_TX_POWER, &rsp, sizeof(rsp)); return 0; } static int cmd_set_adv_data(struct btdev *dev, const void *data, uint8_t len) { const struct bt_hci_cmd_le_set_adv_data *cmd = data; uint8_t status; dev->le_adv_data_len = cmd->len; memcpy(dev->le_adv_data, cmd->data, 31); status = BT_HCI_ERR_SUCCESS; cmd_complete(dev, BT_HCI_CMD_LE_SET_ADV_DATA, &status, sizeof(status)); return 0; } static int cmd_set_scan_rsp_data(struct btdev *dev, const void *data, uint8_t len) { const struct bt_hci_cmd_le_set_scan_rsp_data *cmd = data; uint8_t status; dev->le_scan_data_len = cmd->len; memcpy(dev->le_scan_data, cmd->data, 31); status = BT_HCI_ERR_SUCCESS; cmd_complete(dev, BT_HCI_CMD_LE_SET_SCAN_RSP_DATA, &status, sizeof(status)); return 0; } static uint8_t get_ext_adv_type(uint8_t ext_adv_type) { /* * If legacy bit is not set then just reset high duty cycle directed * bit. */ if (!(ext_adv_type & 0x10)) return (ext_adv_type & 0xf7); /* * Connectable low duty cycle directed advertising creates a * connectable directed advertising report type. */ if (ext_adv_type == 0x001d) return 0x0015; return ext_adv_type; } static const uint8_t *scan_addr(const struct btdev *btdev) { if (btdev->le_scan_own_addr_type == 0x01) return btdev->random_addr; return btdev->bdaddr; } static const uint8_t *adv_addr(const struct btdev *btdev) { if (btdev->le_adv_own_addr == 0x01) return btdev->random_addr; return btdev->bdaddr; } static void le_send_adv_report(struct btdev *btdev, const struct btdev *remote, uint8_t type) { struct __packed { uint8_t subevent; union { struct bt_hci_evt_le_adv_report lar; uint8_t raw[10 + 31 + 1]; }; } meta_event; meta_event.subevent = BT_HCI_EVT_LE_ADV_REPORT; memset(&meta_event.lar, 0, sizeof(meta_event.lar)); meta_event.lar.num_reports = 1; meta_event.lar.event_type = type; meta_event.lar.addr_type = remote->le_adv_own_addr; memcpy(meta_event.lar.addr, adv_addr(remote), 6); /* Scan or advertising response */ if (type == 0x04) { meta_event.lar.data_len = remote->le_scan_data_len; memcpy(meta_event.lar.data, remote->le_scan_data, meta_event.lar.data_len); } else { meta_event.lar.data_len = remote->le_adv_data_len; memcpy(meta_event.lar.data, remote->le_adv_data, meta_event.lar.data_len); } /* Not available */ meta_event.raw[10 + meta_event.lar.data_len] = 127; send_event(btdev, BT_HCI_EVT_LE_META_EVENT, &meta_event, 1 + 10 + meta_event.lar.data_len + 1); } static uint8_t get_adv_report_type(uint8_t adv_type) { /* * Connectable low duty cycle directed advertising creates a * connectable directed advertising report type. */ if (adv_type == 0x04) return 0x01; return adv_type; } static bool adv_match(struct btdev *scan, struct btdev *adv) { /* Match everything if this is not directed advertising */ if (adv->le_adv_type != 0x01 && adv->le_adv_type != 0x04) return true; if (scan->le_scan_own_addr_type != adv->le_adv_direct_addr_type) return false; return !memcmp(scan_addr(scan), adv->le_adv_direct_addr, 6); } static void le_set_adv_enable_complete(struct btdev *btdev) { uint8_t report_type; int i; report_type = get_adv_report_type(btdev->le_adv_type); for (i = 0; i < MAX_BTDEV_ENTRIES; i++) { if (!btdev_list[i] || btdev_list[i] == btdev) continue; if (!btdev_list[i]->le_scan_enable) continue; if (!adv_match(btdev_list[i], btdev)) continue; le_send_adv_report(btdev_list[i], btdev, report_type); if (btdev_list[i]->le_scan_type != 0x01) continue; /* ADV_IND & ADV_SCAN_IND generate a scan response */ if (btdev->le_adv_type == 0x00 || btdev->le_adv_type == 0x02) le_send_adv_report(btdev_list[i], btdev, 0x04); } } #define RL_ADDR_EQUAL(_rl, _type, _addr) \ (_rl->type == _type && !bacmp(&_rl->addr, (bdaddr_t *)_addr)) static const struct btdev_rl *rl_find(const struct btdev *dev, uint8_t type, const uint8_t *addr) { unsigned int i; for (i = 0; i < ARRAY_SIZE(dev->le_rl); i++) { const struct btdev_rl *rl = &dev->le_rl[i]; if (RL_ADDR_EQUAL(rl, type, addr)) return rl; } return NULL; } static int cmd_set_adv_enable(struct btdev *dev, const void *data, uint8_t len) { const struct bt_hci_cmd_le_set_ext_adv_enable *cmd = data; uint8_t status; bool random_addr; if (dev->le_adv_enable == cmd->enable) { status = BT_HCI_ERR_COMMAND_DISALLOWED; goto done; } dev->le_adv_enable = cmd->enable; status = BT_HCI_ERR_SUCCESS; if (!cmd->enable) goto done; random_addr = bacmp((bdaddr_t *)dev->random_addr, BDADDR_ANY); /* If Advertising_Enable is set to 0x01, the advertising parameters' * Own_Address_Type parameter is set to 0x01, and the random address for * the device has not been initialized, the Controller shall return the * error code Invalid HCI Command Parameters (0x12). */ if (dev->le_adv_own_addr == 0x01 && !random_addr) { status = BT_HCI_ERR_INVALID_PARAMETERS; goto done; } /* If Advertising_Enable is set to 0x01, the advertising parameters' * Own_Address_Type parameter is set to 0x03, the controller's resolving * list did not contain a matching entry, and the random address for the * device has not been initialized, the Controller shall return the * error code Invalid HCI Command Parameters (0x12). */ if (dev->le_adv_own_addr == 0x03 && !random_addr) { if (!dev->le_rl_enable || !rl_find(dev, dev->le_adv_direct_addr_type, dev->le_adv_direct_addr)) { status = BT_HCI_ERR_INVALID_PARAMETERS; goto done; } } done: cmd_complete(dev, BT_HCI_CMD_LE_SET_ADV_ENABLE, &status, sizeof(status)); if (!status && dev->le_adv_enable) le_set_adv_enable_complete(dev); return 0; } static int cmd_set_scan_params(struct btdev *dev, const void *data, uint8_t len) { const struct bt_hci_cmd_le_set_scan_parameters *cmd = data; uint8_t status; if (dev->le_scan_enable) { status = BT_HCI_ERR_COMMAND_DISALLOWED; goto done; } status = BT_HCI_ERR_SUCCESS; dev->le_scan_type = cmd->type; dev->le_scan_own_addr_type = cmd->own_addr_type; dev->le_scan_filter_policy = cmd->filter_policy; done: cmd_complete(dev, BT_HCI_CMD_LE_SET_SCAN_PARAMETERS, &status, sizeof(status)); return 0; } static int cmd_set_scan_enable(struct btdev *dev, const void *data, uint8_t len) { const struct bt_hci_cmd_le_set_scan_enable *cmd = data; uint8_t status; if (dev->le_scan_enable == cmd->enable) { status = BT_HCI_ERR_COMMAND_DISALLOWED; goto done; } /* If LE_Scan_Enable is set to 0x01, the scanning parameters' * Own_Address_Type parameter is set to 0x01 or 0x03, and the random * address for the device has not been initialized, the Controller shall * return the error code Invalid HCI Command Parameters (0x12). */ if ((dev->le_scan_own_addr_type == 0x01 || dev->le_scan_own_addr_type == 0x03) && !bacmp((bdaddr_t *)dev->random_addr, BDADDR_ANY)) { status = BT_HCI_ERR_INVALID_PARAMETERS; goto done; } dev->le_scan_enable = cmd->enable; dev->le_filter_dup = cmd->filter_dup; status = BT_HCI_ERR_SUCCESS; done: cmd_complete(dev, BT_HCI_CMD_LE_SET_SCAN_ENABLE, &status, sizeof(status)); return 0; } static int cmd_set_scan_enable_complete(struct btdev *dev, const void *data, uint8_t len) { const struct bt_hci_cmd_le_set_scan_enable *cmd = data; int i; if (!dev->le_scan_enable || !cmd->enable) return 0; for (i = 0; i < MAX_BTDEV_ENTRIES; i++) { uint8_t report_type; if (!btdev_list[i] || btdev_list[i] == dev) continue; if (!btdev_list[i]->le_adv_enable) continue; if (!adv_match(dev, btdev_list[i])) continue; report_type = get_adv_report_type(btdev_list[i]->le_adv_type); le_send_adv_report(dev, btdev_list[i], report_type); if (dev->le_scan_type != 0x01) continue; /* ADV_IND & ADV_SCAN_IND generate a scan response */ if (btdev_list[i]->le_adv_type == 0x00 || btdev_list[i]->le_adv_type == 0x02) le_send_adv_report(dev, btdev_list[i], 0x04); } return 0; } static bool adv_connectable(struct btdev *btdev) { if (!btdev->le_adv_enable) return false; return btdev->le_adv_type != 0x03; } static void le_meta_event(struct btdev *btdev, uint8_t event, void *data, uint8_t len) { void *pkt_data; util_debug(btdev->debug_callback, btdev->debug_data, "meta event 0x%02x", event); pkt_data = alloca(1 + len); if (!pkt_data) return; ((uint8_t *) pkt_data)[0] = event; if (len > 0) memcpy(pkt_data + 1, data, len); send_event(btdev, BT_HCI_EVT_LE_META_EVENT, pkt_data, 1 + len); } static void le_conn_complete(struct btdev *btdev, const struct bt_hci_cmd_le_create_conn *lecc, uint8_t status) { struct bt_hci_evt_le_conn_complete cc; memset(&cc, 0, sizeof(cc)); if (!status) { struct btdev_conn *conn; conn = conn_add_acl(btdev, lecc->peer_addr, lecc->peer_addr_type); if (!conn) return; btdev->le_adv_enable = 0; conn->link->dev->le_adv_enable = 0; cc.status = status; cc.peer_addr_type = btdev->le_scan_own_addr_type; if (cc.peer_addr_type == 0x01) memcpy(cc.peer_addr, btdev->random_addr, 6); else memcpy(cc.peer_addr, btdev->bdaddr, 6); cc.role = 0x01; cc.handle = cpu_to_le16(conn->handle); cc.interval = lecc->max_interval; cc.latency = lecc->latency; cc.supv_timeout = lecc->supv_timeout; le_meta_event(conn->link->dev, BT_HCI_EVT_LE_CONN_COMPLETE, &cc, sizeof(cc)); } cc.status = status; cc.peer_addr_type = lecc->peer_addr_type; memcpy(cc.peer_addr, lecc->peer_addr, 6); cc.role = 0x00; le_meta_event(btdev, BT_HCI_EVT_LE_CONN_COMPLETE, &cc, sizeof(cc)); } static int cmd_le_create_conn(struct btdev *dev, const void *data, uint8_t len) { cmd_status(dev, BT_HCI_ERR_SUCCESS, BT_HCI_CMD_LE_CREATE_CONN); return 0; } static int cmd_le_create_conn_complete(struct btdev *dev, const void *data, uint8_t len) { const struct bt_hci_cmd_le_create_conn *cmd = data; struct btdev *remote; dev->le_scan_own_addr_type = cmd->own_addr_type; remote = find_btdev_by_bdaddr_type(cmd->peer_addr, cmd->peer_addr_type); if (remote && adv_connectable(remote) && adv_match(dev, remote) && remote->le_adv_own_addr == cmd->peer_addr_type) le_conn_complete(dev, cmd, 0); else le_conn_complete(dev, cmd, BT_HCI_ERR_CONN_FAILED_TO_ESTABLISH); return 0; } static int cmd_le_create_conn_cancel(struct btdev *dev, const void *data, uint8_t len) { uint8_t status = BT_HCI_ERR_COMMAND_DISALLOWED; cmd_complete(dev, BT_HCI_CMD_LE_CREATE_CONN_CANCEL, &status, sizeof(status)); return 0; } static int cmd_read_al_size(struct btdev *dev, const void *data, uint8_t len) { struct bt_hci_rsp_le_read_accept_list_size rsp; rsp.status = BT_HCI_ERR_SUCCESS; rsp.size = dev->le_al_len; cmd_complete(dev, BT_HCI_CMD_LE_READ_ACCEPT_LIST_SIZE, &rsp, sizeof(rsp)); return 0; } static bool al_can_change(struct btdev *dev) { /* filter policy uses the Accept List and advertising is enable. */ if (dev->le_adv_enable && dev->le_adv_filter_policy) return false; /* scan filter policy uses the Accept List and scanning is enabled */ if (dev->le_scan_enable) { switch (dev->le_scan_filter_policy) { case 0x00: return true; case 0x01: return false; case 0x02: return true; case 0x03: return false; } } return true; } static int cmd_al_clear(struct btdev *dev, const void *data, uint8_t len) { uint8_t status; /* This command shall not be used when: * • any advertising filter policy uses the Accept List and * advertising is enabled, * • the scanning filter policy uses the Accept List and scanning is * enabled, or * • the initiator filter policy uses the Accept List and an * HCI_LE_Create_Connection or HCI_LE_Extended_Create_Connection * command is outstanding. */ if (!al_can_change(dev)) return -EPERM; al_clear(dev); status = BT_HCI_ERR_SUCCESS; cmd_complete(dev, BT_HCI_CMD_LE_CLEAR_ACCEPT_LIST, &status, sizeof(status)); return 0; } #define AL_ADDR_EQUAL(_al, _type, _addr) \ (_al->type == _type && !bacmp(&_al->addr, (bdaddr_t *)_addr)) static void al_add(struct btdev_al *al, uint8_t type, bdaddr_t *addr) { al->type = type; bacpy(&al->addr, addr); } static int cmd_add_al(struct btdev *dev, const void *data, uint8_t len) { const struct bt_hci_cmd_le_add_to_accept_list *cmd = data; uint8_t status; bool exists = false; int i, pos = -1; /* This command shall not be used when: * • any advertising filter policy uses the Accept List and * advertising is enabled, * • the scanning filter policy uses the Accept List and scanning is * enabled, or * • the initiator filter policy uses the Accept List and an * HCI_LE_Create_Connection or HCI_LE_Extended_Create_Connection * command is outstanding. */ if (!al_can_change(dev)) { status = BT_HCI_ERR_COMMAND_DISALLOWED; goto done; } /* Valid range for address type is 0x00 to 0x01 */ if (cmd->addr_type > 0x01) { status = BT_HCI_ERR_INVALID_PARAMETERS; goto done; } for (i = 0; i < dev->le_al_len; i++) { struct btdev_al *al = &dev->le_al[i]; if (AL_ADDR_EQUAL(al, cmd->addr_type, &cmd->addr)) { exists = true; break; } else if (pos < 0 && al->type == 0xff) pos = i; } /* If the device is already in the Filter Accept List, the Controller * should not add the device to the Filter Accept List again and should * return success. */ if (exists) { status = BT_HCI_ERR_SUCCESS; goto done; } if (pos < 0) { status = BT_HCI_ERR_MEM_CAPACITY_EXCEEDED; goto done; } al_add(&dev->le_al[pos], cmd->addr_type, (bdaddr_t *)&cmd->addr); status = BT_HCI_ERR_SUCCESS; done: cmd_complete(dev, BT_HCI_CMD_LE_ADD_TO_ACCEPT_LIST, &status, sizeof(status)); return 0; } static int cmd_remove_al(struct btdev *dev, const void *data, uint8_t len) { const struct bt_hci_cmd_le_remove_from_accept_list *cmd = data; uint8_t status; int i; char addr[18]; /* This command shall not be used when: * • any advertising filter policy uses the Accept List and * advertising is enabled, * • the scanning filter policy uses the Accept List and scanning is * enabled, or * • the initiator filter policy uses the Accept List and an * HCI_LE_Create_Connection or HCI_LE_Extended_Create_Connection * command is outstanding. */ if (!al_can_change(dev)) { status = BT_HCI_ERR_COMMAND_DISALLOWED; goto done; } /* Valid range for address type is 0x00 to 0x01 */ if (cmd->addr_type > 0x01) { status = BT_HCI_ERR_INVALID_PARAMETERS; goto done; } for (i = 0; i < dev->le_al_len; i++) { struct btdev_al *al = &dev->le_al[i]; ba2str(&al->addr, addr); util_debug(dev->debug_callback, dev->debug_data, "type 0x%02x addr %s", dev->le_al[i].type, addr); if (AL_ADDR_EQUAL(al, cmd->addr_type, &cmd->addr)) { al_reset(al); break; } } if (i == dev->le_al_len) { status = BT_HCI_ERR_INVALID_PARAMETERS; goto done; } status = BT_HCI_ERR_SUCCESS; done: cmd_complete(dev, BT_HCI_CMD_LE_REMOVE_FROM_ACCEPT_LIST, &status, sizeof(status)); return 0; } static int cmd_add_rl(struct btdev *dev, const void *data, uint8_t len) { const struct bt_hci_cmd_le_add_to_resolv_list *cmd = data; uint8_t status; bool exists = false; int i, pos = -1; /* This command shall not be used when address resolution is enabled in * the Controller and: * • Advertising (other than periodic advertising) is enabled, * • Scanning is enabled, or * • an HCI_LE_Create_Connection, HCI_LE_Extended_Create_Connection, * or HCI_LE_Periodic_Advertising_Create_Sync command is outstanding. */ if (dev->le_adv_enable || dev->le_scan_enable) { status = BT_HCI_ERR_COMMAND_DISALLOWED; goto done; } /* Valid range for address type is 0x00 to 0x01 */ if (cmd->addr_type > 0x01) { status = BT_HCI_ERR_INVALID_PARAMETERS; goto done; } for (i = 0; i < dev->le_rl_len; i++) { struct btdev_rl *rl = &dev->le_rl[i]; if (RL_ADDR_EQUAL(rl, cmd->addr_type, &cmd->addr)) { exists = true; break; } else if (pos < 0 && rl->type == 0xff) pos = i; } /* If an entry already exists in the resolving list with the same four * parameter values, the Controller shall either reject the command or * not add the device to the resolving list again and return success. */ if (exists) { status = BT_HCI_ERR_SUCCESS; goto done; } if (pos < 0) { status = BT_HCI_ERR_MEM_CAPACITY_EXCEEDED; goto done; } dev->le_rl[pos].type = cmd->addr_type; bacpy(&dev->le_rl[pos].addr, (bdaddr_t *)&cmd->addr); memcpy(dev->le_rl[pos].peer_irk, cmd->peer_irk, 16); memcpy(dev->le_rl[pos].local_irk, cmd->local_irk, 16); status = BT_HCI_ERR_SUCCESS; done: cmd_complete(dev, BT_HCI_CMD_LE_ADD_TO_RESOLV_LIST, &status, sizeof(status)); return 0; } static int cmd_remove_rl(struct btdev *dev, const void *data, uint8_t len) { const struct bt_hci_cmd_le_remove_from_resolv_list *cmd = data; uint8_t status; int i; /* This command shall not be used when address resolution is enabled in * the Controller and: * • Advertising (other than periodic advertising) is enabled, * • Scanning is enabled, or * • an HCI_LE_Create_Connection, HCI_LE_Extended_Create_Connection, * or HCI_LE_Periodic_Advertising_Create_Sync command is outstanding. */ if (dev->le_adv_enable || dev->le_scan_enable) { status = BT_HCI_ERR_COMMAND_DISALLOWED; goto done; } /* Valid range for address type is 0x00 to 0x01 */ if (cmd->addr_type > 0x01) { status = BT_HCI_ERR_INVALID_PARAMETERS; goto done; } for (i = 0; i < dev->le_rl_len; i++) { struct btdev_rl *rl = &dev->le_rl[i]; if (RL_ADDR_EQUAL(rl, cmd->addr_type, &cmd->addr)) { rl_reset(rl); break; } } if (i == dev->le_rl_len) { status = BT_HCI_ERR_INVALID_PARAMETERS; goto done; } status = BT_HCI_ERR_SUCCESS; done: cmd_complete(dev, BT_HCI_CMD_LE_REMOVE_FROM_RESOLV_LIST, &status, sizeof(status)); return 0; } static int cmd_clear_rl(struct btdev *dev, const void *data, uint8_t len) { uint8_t status; /* This command shall not be used when address resolution is enabled in * the Controller and: * • Advertising (other than periodic advertising) is enabled, * • Scanning is enabled, or * • an HCI_LE_Create_Connection, HCI_LE_Extended_Create_Connection, * or HCI_LE_Periodic_Advertising_Create_Sync command is outstanding. */ if (dev->le_adv_enable || dev->le_scan_enable) { status = BT_HCI_ERR_COMMAND_DISALLOWED; goto done; } rl_clear(dev); status = BT_HCI_ERR_SUCCESS; done: cmd_complete(dev, BT_HCI_CMD_LE_CLEAR_RESOLV_LIST, &status, sizeof(status)); return 0; } static int cmd_read_rl_size(struct btdev *dev, const void *data, uint8_t len) { struct bt_hci_rsp_le_read_resolv_list_size rsp; rsp.status = BT_HCI_ERR_SUCCESS; rsp.size = dev->le_rl_len; cmd_complete(dev, BT_HCI_CMD_LE_READ_RESOLV_LIST_SIZE, &rsp, sizeof(rsp)); return 0; } static int cmd_read_peer_rl_addr(struct btdev *dev, const void *data, uint8_t size) { const struct bt_hci_cmd_le_read_peer_resolv_addr *cmd = data; struct bt_hci_rsp_le_read_peer_resolv_addr rsp; /* Valid range for address type is 0x00 to 0x01 */ if (cmd->addr_type > 0x01) { rsp.status = BT_HCI_ERR_INVALID_PARAMETERS; goto done; } rsp.status = BT_HCI_ERR_UNKNOWN_CONN_ID; memset(rsp.addr, 0, 6); done: cmd_complete(dev, BT_HCI_CMD_LE_READ_PEER_RESOLV_ADDR, &rsp, sizeof(rsp)); return 0; } static int cmd_read_local_rl_addr(struct btdev *dev, const void *data, uint8_t size) { const struct bt_hci_cmd_le_read_local_resolv_addr *cmd = data; struct bt_hci_rsp_le_read_local_resolv_addr rsp; /* Valid range for address type is 0x00 to 0x01 */ if (cmd->addr_type > 0x01) { rsp.status = BT_HCI_ERR_INVALID_PARAMETERS; goto done; } rsp.status = BT_HCI_ERR_UNKNOWN_CONN_ID; memset(rsp.addr, 0, 6); done: cmd_complete(dev, BT_HCI_CMD_LE_READ_LOCAL_RESOLV_ADDR, &rsp, sizeof(rsp)); return 0; } static int cmd_set_rl_enable(struct btdev *dev, const void *data, uint8_t len) { const struct bt_hci_cmd_le_set_resolv_enable *cmd = data; uint8_t status; /* This command shall not be used when address resolution is enabled in * the Controller and: * • Advertising (other than periodic advertising) is enabled, * • Scanning is enabled, or * • an HCI_LE_Create_Connection, HCI_LE_Extended_Create_Connection, * or HCI_LE_Periodic_Advertising_Create_Sync command is outstanding. */ if (dev->le_adv_enable || dev->le_scan_enable) return -EPERM; /* Valid range for address resolution enable is 0x00 to 0x01 */ if (cmd->enable > 0x01) return -EINVAL; dev->le_rl_enable = cmd->enable; status = BT_HCI_ERR_SUCCESS; cmd_complete(dev, BT_HCI_CMD_LE_SET_RESOLV_ENABLE, &status, sizeof(status)); return 0; } static int cmd_set_rl_timeout(struct btdev *dev, const void *data, uint8_t len) { const struct bt_hci_cmd_le_set_resolv_timeout *cmd = data; uint16_t timeout; uint8_t status; timeout = le16_to_cpu(cmd->timeout); /* Valid range for RPA timeout is 0x0001 to 0xa1b8 */ if (timeout < 0x0001 || timeout > 0xa1b8) return -EINVAL; dev->le_rl_timeout = timeout; status = BT_HCI_ERR_SUCCESS; cmd_complete(dev, BT_HCI_CMD_LE_SET_RESOLV_TIMEOUT, &status, sizeof(status)); return 0; } static int cmd_conn_update(struct btdev *dev, const void *data, uint8_t len) { cmd_status(dev, BT_HCI_ERR_SUCCESS, BT_HCI_CMD_LE_CONN_UPDATE); return 0; } static void le_conn_update(struct btdev *btdev, uint16_t handle, uint16_t min_interval, uint16_t max_interval, uint16_t latency, uint16_t supv_timeout, uint16_t min_length, uint16_t max_length) { struct bt_hci_evt_le_conn_update_complete ev; struct btdev_conn *conn; memset(&ev, 0, sizeof(ev)); ev.handle = cpu_to_le16(handle); ev.interval = cpu_to_le16(min_interval); ev.latency = cpu_to_le16(latency); ev.supv_timeout = cpu_to_le16(supv_timeout); conn = queue_find(btdev->conns, match_handle, UINT_TO_PTR(handle)); if (conn) ev.status = BT_HCI_ERR_SUCCESS; else ev.status = BT_HCI_ERR_UNKNOWN_CONN_ID; le_meta_event(btdev, BT_HCI_EVT_LE_CONN_UPDATE_COMPLETE, &ev, sizeof(ev)); if (conn) le_meta_event(conn->link->dev, BT_HCI_EVT_LE_CONN_UPDATE_COMPLETE, &ev, sizeof(ev)); } static void le_conn_param_req(struct btdev *btdev, uint16_t handle, uint16_t min_interval, uint16_t max_interval, uint16_t latency, uint16_t supv_timeout, uint16_t min_length, uint16_t max_length) { struct bt_hci_evt_le_conn_param_request ev; struct btdev_conn *conn; conn = queue_find(btdev->conns, match_handle, UINT_TO_PTR(handle)); if (!conn) return; memset(&ev, 0, sizeof(ev)); ev.handle = cpu_to_le16(handle); ev.min_interval = cpu_to_le16(min_interval); ev.max_interval = cpu_to_le16(max_interval); ev.latency = cpu_to_le16(latency); ev.supv_timeout = cpu_to_le16(supv_timeout); le_meta_event(conn->link->dev, BT_HCI_EVT_LE_CONN_PARAM_REQUEST, &ev, sizeof(ev)); } static int cmd_conn_update_complete(struct btdev *dev, const void *data, uint8_t len) { const struct bt_hci_cmd_le_conn_update *cmd = data; if (dev->le_features[0] & 0x02) le_conn_param_req(dev, le16_to_cpu(cmd->handle), le16_to_cpu(cmd->min_interval), le16_to_cpu(cmd->max_interval), le16_to_cpu(cmd->latency), le16_to_cpu(cmd->supv_timeout), le16_to_cpu(cmd->min_length), le16_to_cpu(cmd->max_length)); else le_conn_update(dev, le16_to_cpu(cmd->handle), le16_to_cpu(cmd->min_interval), le16_to_cpu(cmd->max_interval), le16_to_cpu(cmd->latency), le16_to_cpu(cmd->supv_timeout), le16_to_cpu(cmd->min_length), le16_to_cpu(cmd->max_length)); return 0; } static int cmd_le_read_remote_features(struct btdev *dev, const void *data, uint8_t len) { const struct bt_hci_cmd_read_remote_features *cmd = data; struct bt_hci_evt_le_remote_features_complete ev; struct btdev_conn *conn; uint8_t status = BT_HCI_ERR_SUCCESS; conn = queue_find(dev->conns, match_handle, UINT_TO_PTR(le16_to_cpu(cmd->handle))); if (!conn) status = BT_HCI_ERR_UNKNOWN_CONN_ID; cmd_status(dev, status, BT_HCI_CMD_LE_READ_REMOTE_FEATURES); if (status) return 0; memset(&ev, 0, sizeof(ev)); ev.status = BT_HCI_ERR_SUCCESS; ev.handle = cpu_to_le16(conn->handle); memcpy(ev.features, conn->link->dev->le_features, 8); le_meta_event(dev, BT_HCI_EVT_LE_REMOTE_FEATURES_COMPLETE, &ev, sizeof(ev)); return 0; } static int cmd_encrypt(struct btdev *dev, const void *data, uint8_t len) { const struct bt_hci_cmd_le_encrypt *cmd = data; struct bt_hci_rsp_le_encrypt rsp; if (!bt_crypto_e(dev->crypto, cmd->key, cmd->plaintext, rsp.data)) { cmd_status(dev, BT_HCI_ERR_COMMAND_DISALLOWED, BT_HCI_CMD_LE_ENCRYPT); return 0; } rsp.status = BT_HCI_ERR_SUCCESS; cmd_complete(dev, BT_HCI_CMD_LE_ENCRYPT, &rsp, sizeof(rsp)); return 0; } static int cmd_rand(struct btdev *dev, const void *data, uint8_t len) { struct bt_hci_rsp_le_rand rsp; if (!bt_crypto_random_bytes(dev->crypto, (uint8_t *)&rsp.number, 8)) { cmd_status(dev, BT_HCI_ERR_COMMAND_DISALLOWED, BT_HCI_CMD_LE_RAND); return 0; } rsp.status = BT_HCI_ERR_SUCCESS; cmd_complete(dev, BT_HCI_CMD_LE_RAND, &rsp, sizeof(rsp)); return 0; } static int cmd_start_encrypt(struct btdev *dev, const void *data, uint8_t len) { const struct bt_hci_cmd_le_start_encrypt *cmd = data; struct bt_hci_evt_le_long_term_key_request ev; struct btdev_conn *conn; conn = queue_find(dev->conns, match_handle, UINT_TO_PTR(le16_to_cpu(cmd->handle))); if (!conn) { cmd_status(dev, BT_HCI_ERR_UNKNOWN_CONN_ID, BT_HCI_CMD_LE_START_ENCRYPT); return 0; } cmd_status(dev, BT_HCI_ERR_SUCCESS, BT_HCI_CMD_LE_START_ENCRYPT); memcpy(dev->le_ltk, cmd->ltk, 16); ev.handle = cpu_to_le16(conn->handle); ev.ediv = cmd->ediv; ev.rand = cmd->rand; le_meta_event(conn->link->dev, BT_HCI_EVT_LE_LONG_TERM_KEY_REQUEST, &ev, sizeof(ev)); return 0; } static int cmd_ltk_reply(struct btdev *dev, const void *data, uint8_t len) { const struct bt_hci_cmd_le_ltk_req_reply *cmd = data; struct bt_hci_rsp_le_ltk_req_reply rp; struct btdev_conn *conn; uint8_t mode, status; conn = queue_find(dev->conns, match_handle, UINT_TO_PTR(le16_to_cpu(cmd->handle))); if (!conn) { rp.status = BT_HCI_ERR_UNKNOWN_CONN_ID; cmd_complete(dev, BT_HCI_CMD_LE_LTK_REQ_REPLY, &rp, sizeof(rp)); return 0; } memcpy(dev->le_ltk, cmd->ltk, 16); memset(&rp, 0, sizeof(rp)); rp.handle = cpu_to_le16(conn->handle); rp.status = BT_HCI_ERR_SUCCESS; cmd_complete(dev, BT_HCI_CMD_LE_LTK_REQ_REPLY, &rp, sizeof(rp)); if (memcmp(dev->le_ltk, conn->link->dev->le_ltk, 16)) { status = BT_HCI_ERR_AUTH_FAILURE; mode = 0x00; } else { status = BT_HCI_ERR_SUCCESS; mode = 0x01; } encrypt_change(conn, mode, status); encrypt_change(conn->link, mode, status); return 0; } static int cmd_ltk_neg_reply(struct btdev *dev, const void *data, uint8_t len) { const struct bt_hci_cmd_le_ltk_req_neg_reply *cmd = data; struct bt_hci_rsp_le_ltk_req_neg_reply rp; struct btdev_conn *conn; conn = queue_find(dev->conns, match_handle, UINT_TO_PTR(le16_to_cpu(cmd->handle))); if (!conn) { rp.status = BT_HCI_ERR_UNKNOWN_CONN_ID; cmd_complete(dev, BT_HCI_CMD_LE_LTK_REQ_NEG_REPLY, &rp, sizeof(rp)); return 0; } memset(&rp, 0, sizeof(rp)); rp.handle = cpu_to_le16(conn->handle); rp.status = BT_HCI_ERR_SUCCESS; cmd_complete(dev, BT_HCI_CMD_LE_LTK_REQ_NEG_REPLY, &rp, sizeof(rp)); encrypt_change(conn->link, 0x00, BT_HCI_ERR_PIN_OR_KEY_MISSING); return 0; } static int cmd_le_read_supported_states(struct btdev *dev, const void *data, uint8_t len) { struct bt_hci_rsp_le_read_supported_states rsp; memset(&rsp, 0, sizeof(0)); rsp.status = BT_HCI_ERR_SUCCESS; memcpy(rsp.states, dev->le_states, 8); cmd_complete(dev, BT_HCI_CMD_LE_READ_SUPPORTED_STATES, &rsp, sizeof(rsp)); return 0; } static int cmd_rx_test(struct btdev *dev, const void *data, uint8_t len) { return -ENOTSUP; } static int cmd_tx_test(struct btdev *dev, const void *data, uint8_t len) { return -ENOTSUP; } static int cmd_test_end(struct btdev *dev, const void *data, uint8_t len) { return -ENOTSUP; } static int cmd_conn_param_reply(struct btdev *dev, const void *data, uint8_t len) { uint8_t status = BT_HCI_ERR_SUCCESS; cmd_complete(dev, BT_HCI_CMD_LE_CONN_PARAM_REQ_REPLY, &status, sizeof(status)); return 0; } static int cmd_conn_param_reply_complete(struct btdev *dev, const void *data, uint8_t len) { const struct bt_hci_cmd_le_conn_param_req_reply *cmd = data; le_conn_update(dev, le16_to_cpu(cmd->handle), le16_to_cpu(cmd->min_interval), le16_to_cpu(cmd->max_interval), le16_to_cpu(cmd->latency), le16_to_cpu(cmd->supv_timeout), le16_to_cpu(cmd->min_length), le16_to_cpu(cmd->max_length)); return 0; } static int cmd_conn_param_neg_reply(struct btdev *dev, const void *data, uint8_t len) { const struct bt_hci_cmd_le_conn_param_req_neg_reply *cmd = data; struct bt_hci_rsp_le_conn_param_req_neg_reply rsp; memset(&rsp, 0, sizeof(rsp)); rsp.handle = cmd->handle; rsp.status = BT_HCI_ERR_SUCCESS; cmd_complete(dev, BT_HCI_CMD_LE_CONN_PARAM_REQ_NEG_REPLY, &rsp, sizeof(rsp)); return 0; } static int cmd_conn_param_neg_reply_complete(struct btdev *dev, const void *data, uint8_t len) { const struct bt_hci_cmd_le_conn_param_req_neg_reply *cmd = data; struct btdev_conn *conn; struct bt_hci_evt_le_conn_update_complete ev; conn = queue_find(dev->conns, match_handle, UINT_TO_PTR(le16_to_cpu(cmd->handle))); if (!conn) return 0; memset(&ev, 0, sizeof(ev)); ev.handle = cpu_to_le16(cmd->handle); ev.status = cpu_to_le16(cmd->reason); le_meta_event(conn->link->dev, BT_HCI_EVT_LE_CONN_UPDATE_COMPLETE, &ev, sizeof(ev)); return 0; } static int cmd_read_local_pk256(struct btdev *dev, const void *data, uint8_t len) { return -ENOTSUP; } static int cmd_gen_dhkey(struct btdev *dev, const void *data, uint8_t len) { return -ENOTSUP; } #define CMD_LE \ CMD(BT_HCI_CMD_READ_LE_HOST_SUPPORTED, cmd_read_le_host_supported, \ NULL), \ CMD(BT_HCI_CMD_WRITE_LE_HOST_SUPPORTED, cmd_write_le_host_supported, \ NULL), \ CMD(BT_HCI_CMD_LE_SET_EVENT_MASK, cmd_le_set_event_mask, NULL), \ CMD(BT_HCI_CMD_LE_READ_BUFFER_SIZE, cmd_le_read_buffer_size, NULL), \ CMD(BT_HCI_CMD_LE_READ_LOCAL_FEATURES, cmd_le_read_local_features, \ NULL), \ CMD(BT_HCI_CMD_LE_SET_RANDOM_ADDRESS, cmd_set_random_address, NULL), \ CMD(BT_HCI_CMD_LE_SET_ADV_PARAMETERS, cmd_set_adv_params, NULL), \ CMD(BT_HCI_CMD_LE_READ_ADV_TX_POWER, cmd_read_adv_tx_power, NULL), \ CMD(BT_HCI_CMD_LE_SET_ADV_DATA, cmd_set_adv_data, NULL), \ CMD(BT_HCI_CMD_LE_SET_SCAN_RSP_DATA, cmd_set_scan_rsp_data, NULL), \ CMD(BT_HCI_CMD_LE_SET_ADV_ENABLE, cmd_set_adv_enable, NULL), \ CMD(BT_HCI_CMD_LE_SET_SCAN_PARAMETERS, cmd_set_scan_params, NULL), \ CMD(BT_HCI_CMD_LE_SET_SCAN_ENABLE, cmd_set_scan_enable, \ cmd_set_scan_enable_complete), \ CMD(BT_HCI_CMD_LE_CREATE_CONN, cmd_le_create_conn, \ cmd_le_create_conn_complete), \ CMD(BT_HCI_CMD_LE_CREATE_CONN_CANCEL, cmd_le_create_conn_cancel, \ NULL), \ CMD(BT_HCI_CMD_LE_READ_ACCEPT_LIST_SIZE, cmd_read_al_size, NULL), \ CMD(BT_HCI_CMD_LE_CLEAR_ACCEPT_LIST, cmd_al_clear, NULL), \ CMD(BT_HCI_CMD_LE_ADD_TO_ACCEPT_LIST, cmd_add_al, NULL), \ CMD(BT_HCI_CMD_LE_REMOVE_FROM_ACCEPT_LIST, cmd_remove_al, NULL), \ CMD(BT_HCI_CMD_LE_CONN_UPDATE, cmd_conn_update, \ cmd_conn_update_complete), \ CMD(BT_HCI_CMD_LE_READ_REMOTE_FEATURES, cmd_le_read_remote_features, \ NULL), \ CMD(BT_HCI_CMD_LE_ENCRYPT, cmd_encrypt, NULL), \ CMD(BT_HCI_CMD_LE_RAND, cmd_rand, NULL), \ CMD(BT_HCI_CMD_LE_START_ENCRYPT, cmd_start_encrypt, NULL), \ CMD(BT_HCI_CMD_LE_LTK_REQ_REPLY, cmd_ltk_reply, NULL), \ CMD(BT_HCI_CMD_LE_LTK_REQ_NEG_REPLY, cmd_ltk_neg_reply, NULL), \ CMD(BT_HCI_CMD_LE_READ_SUPPORTED_STATES, cmd_le_read_supported_states, \ NULL), \ CMD(BT_HCI_CMD_LE_RECEIVER_TEST, cmd_rx_test, NULL), \ CMD(BT_HCI_CMD_LE_TRANSMITTER_TEST, cmd_tx_test, NULL), \ CMD(BT_HCI_CMD_LE_ISO_TEST_END, cmd_test_end, NULL), \ CMD(BT_HCI_CMD_LE_CONN_PARAM_REQ_REPLY, cmd_conn_param_reply, \ cmd_conn_param_reply_complete), \ CMD(BT_HCI_CMD_LE_CONN_PARAM_REQ_NEG_REPLY, cmd_conn_param_neg_reply, \ cmd_conn_param_neg_reply_complete), \ CMD(BT_HCI_CMD_LE_READ_LOCAL_PK256, cmd_read_local_pk256, NULL), \ CMD(BT_HCI_CMD_LE_GENERATE_DHKEY, cmd_gen_dhkey, NULL), \ CMD(BT_HCI_CMD_LE_ADD_TO_RESOLV_LIST, cmd_add_rl, NULL), \ CMD(BT_HCI_CMD_LE_REMOVE_FROM_RESOLV_LIST, cmd_remove_rl, NULL), \ CMD(BT_HCI_CMD_LE_CLEAR_RESOLV_LIST, cmd_clear_rl, NULL), \ CMD(BT_HCI_CMD_LE_READ_RESOLV_LIST_SIZE, cmd_read_rl_size, NULL), \ CMD(BT_HCI_CMD_LE_READ_PEER_RESOLV_ADDR, cmd_read_peer_rl_addr, NULL), \ CMD(BT_HCI_CMD_LE_READ_LOCAL_RESOLV_ADDR, cmd_read_local_rl_addr, \ NULL), \ CMD(BT_HCI_CMD_LE_SET_RESOLV_ENABLE, cmd_set_rl_enable, NULL), \ CMD(BT_HCI_CMD_LE_SET_RESOLV_TIMEOUT, cmd_set_rl_timeout, NULL) static int cmd_set_default_phy(struct btdev *dev, const void *data, uint8_t len) { const struct bt_hci_cmd_le_set_default_phy *cmd = data; uint8_t status; if (cmd->all_phys > 0x03 || (!(cmd->all_phys & 0x01) && (!cmd->tx_phys || cmd->tx_phys > 0x07)) || (!(cmd->all_phys & 0x02) && (!cmd->rx_phys || cmd->rx_phys > 0x07))) status = BT_HCI_ERR_INVALID_PARAMETERS; else status = BT_HCI_ERR_SUCCESS; cmd_complete(dev, BT_HCI_CMD_LE_SET_DEFAULT_PHY, &status, sizeof(status)); return 0; } static const uint8_t *ext_adv_gen_rpa(const struct btdev *dev, struct le_ext_adv *adv) { const struct btdev_rl *rl; if (adv->rpa) return adv->random_addr; /* Lookup for Local IRK in the resolving list */ rl = rl_find(dev, adv->direct_addr_type, adv->direct_addr); if (rl) { uint8_t rpa[6]; bt_crypto_random_bytes(dev->crypto, rpa + 3, 3); rpa[5] &= 0x3f; /* Clear two most significant bits */ rpa[5] |= 0x40; /* Set second most significant bit */ bt_crypto_ah(dev->crypto, rl->peer_irk, rpa + 3, rpa); memcpy(adv->random_addr, rpa, sizeof(rpa)); adv->rpa = true; } return adv->random_addr; } static const uint8_t *ext_adv_addr(const struct btdev *btdev, struct le_ext_adv *ext_adv) { if (ext_adv->own_addr_type == 0x01) return ext_adv->random_addr; if (ext_adv->own_addr_type == 0x03) return ext_adv_gen_rpa(btdev, ext_adv); return btdev->bdaddr; } static bool ext_adv_match_addr(const struct btdev *btdev, struct le_ext_adv *ext_adv) { /* Match everything if this is not directed advertising */ if (!(ext_adv->type & 0x04)) return true; if (btdev->le_scan_own_addr_type != ext_adv->direct_addr_type) return false; return !memcmp(scan_addr(btdev), ext_adv->direct_addr, 6); } static bool match_ext_adv_handle(const void *data, const void *match_data) { const struct le_ext_adv *ext_adv = data; uint8_t handle = PTR_TO_UINT(match_data); return ext_adv->handle == handle; } static void ext_adv_disable(void *data, void *user_data) { struct le_ext_adv *ext_adv = data; uint8_t handle = PTR_TO_UINT(user_data); if (handle && ext_adv->handle != handle) return; if (ext_adv->broadcast_id) { timeout_remove(ext_adv->broadcast_id); ext_adv->broadcast_id = 0; } if (ext_adv->timeout_id) { timeout_remove(ext_adv->timeout_id); ext_adv->timeout_id = 0; } ext_adv->enable = 0x00; } static bool ext_adv_is_connectable(struct le_ext_adv *ext_adv) { if (!ext_adv->enable) return false; return ext_adv->type & 0x01; } static struct le_ext_adv *le_ext_adv_new(struct btdev *btdev, uint8_t handle) { struct le_ext_adv *ext_adv; ext_adv = new0(struct le_ext_adv, 1); ext_adv->dev = btdev; ext_adv->handle = handle; /* Default value for min/max advertising interval shall be 1.28s. */ ext_adv->interval = 1280; /* Add to queue */ if (!queue_push_tail(btdev->le_ext_adv, ext_adv)) { free(ext_adv); return NULL; } return ext_adv; } static int cmd_set_adv_rand_addr(struct btdev *dev, const void *data, uint8_t len) { const struct bt_hci_cmd_le_set_adv_set_rand_addr *cmd = data; struct le_ext_adv *ext_adv; uint8_t status = BT_HCI_ERR_SUCCESS; /* Check if Ext Adv is already existed */ ext_adv = queue_find(dev->le_ext_adv, match_ext_adv_handle, UINT_TO_PTR(cmd->handle)); if (!ext_adv) { status = BT_HCI_ERR_UNKNOWN_ADVERTISING_ID; cmd_complete(dev, BT_HCI_CMD_LE_SET_ADV_SET_RAND_ADDR, &status, sizeof(status)); return 0; } if (ext_adv_is_connectable(ext_adv)) { status = BT_HCI_ERR_COMMAND_DISALLOWED; cmd_complete(dev, BT_HCI_CMD_LE_SET_ADV_SET_RAND_ADDR, &status, sizeof(status)); return 0; } memcpy(ext_adv->random_addr, cmd->bdaddr, 6); cmd_complete(dev, BT_HCI_CMD_LE_SET_ADV_SET_RAND_ADDR, &status, sizeof(status)); return 0; } static int cmd_set_ext_adv_params(struct btdev *dev, const void *data, uint8_t len) { const struct bt_hci_cmd_le_set_ext_adv_params *cmd = data; struct bt_hci_rsp_le_set_ext_adv_params rsp; struct le_ext_adv *ext_adv; memset(&rsp, 0, sizeof(rsp)); /* Check if Ext Adv is already existed */ ext_adv = queue_find(dev->le_ext_adv, match_ext_adv_handle, UINT_TO_PTR(cmd->handle)); if (!ext_adv) { /* No more than maximum number */ if (queue_length(dev->le_ext_adv) >= MAX_EXT_ADV_SETS) { rsp.status = BT_HCI_ERR_MEM_CAPACITY_EXCEEDED; cmd_complete(dev, BT_HCI_CMD_LE_SET_EXT_ADV_PARAMS, &rsp, sizeof(rsp)); return 0; } /* Create new set */ ext_adv = le_ext_adv_new(dev, cmd->handle); if (!ext_adv) { rsp.status = BT_HCI_ERR_MEM_CAPACITY_EXCEEDED; cmd_complete(dev, BT_HCI_CMD_LE_SET_EXT_ADV_PARAMS, &rsp, sizeof(rsp)); return 0; } } if (ext_adv->enable) { rsp.status = BT_HCI_ERR_COMMAND_DISALLOWED; cmd_complete(dev, BT_HCI_CMD_LE_SET_EXT_ADV_PARAMS, &rsp, sizeof(rsp)); return 0; } ext_adv->type = le16_to_cpu(cmd->evt_properties); /* In case of high duty cycle directed connectable advertising * intervals shall be ignored and high duty cycle shall be used * (advertising interval <= 3.75 ms). */ if ((ext_adv->type & 0x0D) == 0x0D /* 0b1101 */) ext_adv->interval = 3; else { unsigned int min_interval = get_le24(cmd->min_interval); if (min_interval < 0x0020 || min_interval > 0xFFFFFF) { rsp.status = BT_HCI_ERR_UNSUPPORTED_FEATURE; cmd_complete(dev, BT_HCI_CMD_LE_SET_EXT_ADV_PARAMS, &rsp, sizeof(rsp)); return 0; } ext_adv->interval = min_interval * 0.625; } ext_adv->own_addr_type = cmd->own_addr_type; ext_adv->direct_addr_type = cmd->peer_addr_type; memcpy(ext_adv->direct_addr, cmd->peer_addr, 6); ext_adv->filter_policy = cmd->filter_policy; rsp.status = BT_HCI_ERR_SUCCESS; rsp.tx_power = 0; cmd_complete(dev, BT_HCI_CMD_LE_SET_EXT_ADV_PARAMS, &rsp, sizeof(rsp)); return 0; } static int cmd_set_ext_adv_data(struct btdev *dev, const void *data, uint8_t len) { const struct bt_hci_cmd_le_set_ext_adv_data *cmd = data; struct le_ext_adv *ext_adv; uint8_t status = BT_HCI_ERR_SUCCESS; ext_adv = queue_find(dev->le_ext_adv, match_ext_adv_handle, UINT_TO_PTR(cmd->handle)); if (!ext_adv) { status = BT_HCI_ERR_UNKNOWN_ADVERTISING_ID; cmd_complete(dev, BT_HCI_CMD_LE_SET_EXT_ADV_DATA, &status, sizeof(status)); return 0; } ext_adv->adv_data_len = cmd->data_len; memcpy(ext_adv->adv_data, cmd->data, cmd->data_len); cmd_complete(dev, BT_HCI_CMD_LE_SET_EXT_ADV_DATA, &status, sizeof(status)); return 0; } static int cmd_set_ext_scan_rsp_data(struct btdev *dev, const void *data, uint8_t len) { const struct bt_hci_cmd_le_set_ext_scan_rsp_data *cmd = data; struct le_ext_adv *ext_adv; uint8_t status = BT_HCI_ERR_SUCCESS; ext_adv = queue_find(dev->le_ext_adv, match_ext_adv_handle, UINT_TO_PTR(cmd->handle)); if (!ext_adv) { status = BT_HCI_ERR_UNKNOWN_ADVERTISING_ID; cmd_complete(dev, BT_HCI_CMD_LE_SET_EXT_ADV_DATA, &status, sizeof(status)); return 0; } ext_adv->scan_data_len = cmd->data_len; memcpy(ext_adv->scan_data, cmd->data, cmd->data_len); cmd_complete(dev, BT_HCI_CMD_LE_SET_EXT_SCAN_RSP_DATA, &status, sizeof(status)); return 0; } static uint8_t ext_adv_addr_type(struct le_ext_adv *adv) { /* Converts the address type on advertising params to advertising * report. */ switch (adv->own_addr_type) { /* LL RPAs shall be advertised as random type or they need to be * resolved depending on the filter policy. */ case 0x02: case 0x03: return 0x01; } return adv->own_addr_type; } static void send_ext_adv(struct btdev *btdev, const struct btdev *remote, struct le_ext_adv *ext_adv, uint16_t type, bool is_scan_rsp) { struct __packed { uint8_t num_reports; union { struct bt_hci_le_ext_adv_report lear; uint8_t raw[24 + 31]; }; } meta_event; memset(&meta_event.lear, 0, sizeof(meta_event.lear)); meta_event.num_reports = 1; meta_event.lear.event_type = cpu_to_le16(type); meta_event.lear.addr_type = ext_adv_addr_type(ext_adv); memcpy(meta_event.lear.addr, ext_adv_addr(remote, ext_adv), 6); meta_event.lear.rssi = 127; meta_event.lear.tx_power = 127; /* Right now we dont care about phy in adv report */ meta_event.lear.primary_phy = 0x01; meta_event.lear.secondary_phy = 0x01; /* Scan or advertising response */ if (is_scan_rsp) { meta_event.lear.data_len = ext_adv->scan_data_len; memcpy(meta_event.lear.data, ext_adv->scan_data, meta_event.lear.data_len); } else { meta_event.lear.data_len = ext_adv->adv_data_len; memcpy(meta_event.lear.data, ext_adv->adv_data, meta_event.lear.data_len); } le_meta_event(btdev, BT_HCI_EVT_LE_EXT_ADV_REPORT, &meta_event, 1 + 24 + meta_event.lear.data_len); } static bool ext_adv_broadcast(void *user_data) { struct le_ext_adv *ext_adv = user_data; struct btdev *btdev = ext_adv->dev; uint16_t report_type; int i; report_type = get_ext_adv_type(ext_adv->type); for (i = 0; i < MAX_BTDEV_ENTRIES; i++) { if (!btdev_list[i] || btdev_list[i] == btdev) continue; if (!btdev_list[i]->le_scan_enable) continue; if (!ext_adv_match_addr(btdev_list[i], ext_adv)) continue; send_ext_adv(btdev_list[i], btdev, ext_adv, report_type, false); if (btdev_list[i]->le_scan_type != 0x01) continue; /* if scannable bit is set the send scan response */ if (ext_adv->type & 0x02) { if (ext_adv->type == 0x13) report_type = 0x1b; else if (ext_adv->type == 0x12) report_type = 0x1a; else if (!(ext_adv->type & 0x10)) report_type &= 0x08; else continue; send_ext_adv(btdev_list[i], btdev, ext_adv, report_type, true); } } return true; } static void adv_set_terminate(struct btdev *dev, uint8_t status, uint8_t handle, uint16_t conn_handle, uint8_t num_evts) { struct bt_hci_evt_le_adv_set_term ev; memset(&ev, 0, sizeof(ev)); ev.status = status; ev.handle = handle; ev.conn_handle = cpu_to_le16(conn_handle); ev.num_evts = num_evts; le_meta_event(dev, BT_HCI_EVT_LE_ADV_SET_TERM, &ev, sizeof(ev)); } static bool ext_adv_timeout(void *user_data) { struct le_ext_adv *adv = user_data; adv->timeout_id = 0; adv_set_terminate(adv->dev, BT_HCI_ERR_ADV_TIMEOUT, adv->handle, 0x0000, 0x00); le_ext_adv_free(adv); return false; } static int cmd_set_ext_adv_enable(struct btdev *dev, const void *data, uint8_t len) { const struct bt_hci_cmd_le_set_ext_adv_enable *cmd = data; uint8_t status = BT_HCI_ERR_SUCCESS; int i; /* Num of set is zero */ if (!cmd->num_of_sets) { if (cmd->enable) { status = BT_HCI_ERR_INVALID_PARAMETERS; goto exit_complete; } /* Disable all advertising sets */ queue_foreach(dev->le_ext_adv, ext_adv_disable, NULL); dev->le_adv_enable = 0x00; goto exit_complete; } /* Process each sets */ for (i = 0; i < cmd->num_of_sets; i++) { const struct bt_hci_cmd_ext_adv_set *eas; struct le_ext_adv *ext_adv; bool random_addr; eas = data + sizeof(*cmd) + (sizeof(*eas) * i); ext_adv = queue_find(dev->le_ext_adv, match_ext_adv_handle, UINT_TO_PTR(eas->handle)); if (!ext_adv) { status = BT_HCI_ERR_UNKNOWN_ADVERTISING_ID; goto exit_complete; } if (ext_adv->enable == cmd->enable) { status = BT_HCI_ERR_COMMAND_DISALLOWED; goto exit_complete; } random_addr = bacmp((bdaddr_t *)ext_adv->random_addr, BDADDR_ANY); /* If the advertising set's Own_Address_Type parameter * is set to 0x01 and the random address for * the advertising set has not been initialized, the * Controller shall return the error code Invalid HCI * Command Parameters (0x12). */ if (ext_adv->own_addr_type == 0x01 && !random_addr) { status = BT_HCI_ERR_INVALID_PARAMETERS; goto exit_complete; } /* If the advertising set's Own_Address_Type parameter is set * to 0x03, the controller's resolving list did not contain a * matching entry, and the random address for the advertising * set has not been initialized, the Controller shall return the * error code Invalid HCI Command Parameters (0x12). */ if (ext_adv->own_addr_type == 0x03 && !random_addr) { if (!dev->le_rl_enable || !rl_find(dev, ext_adv->direct_addr_type, ext_adv->direct_addr)) { status = BT_HCI_ERR_INVALID_PARAMETERS; goto exit_complete; } } ext_adv->enable = cmd->enable; dev->le_adv_enable = 0x01; if (!cmd->enable) ext_adv_disable(ext_adv, NULL); else { /* Send the first advertising report right away and * start the timer for continuous advertising. */ ext_adv_broadcast(ext_adv); ext_adv->broadcast_id = timeout_add(ext_adv->interval, ext_adv_broadcast, ext_adv, NULL); if (eas->duration) { unsigned int duration_ms = eas->duration * 10; ext_adv->timeout_id = timeout_add(duration_ms, ext_adv_timeout, ext_adv, NULL); } } } exit_complete: cmd_complete(dev, BT_HCI_CMD_LE_SET_EXT_ADV_ENABLE, &status, sizeof(status)); return 0; } static int cmd_read_max_adv_data_len(struct btdev *dev, const void *data, uint8_t len) { /* TODO */ return -ENOTSUP; } static int cmd_read_num_adv_sets(struct btdev *dev, const void *data, uint8_t len) { struct bt_hci_rsp_le_read_num_supported_adv_sets rsp; memset(&rsp, 0, sizeof(rsp)); rsp.status = BT_HCI_ERR_SUCCESS; rsp.num_of_sets = MAX_EXT_ADV_SETS; cmd_complete(dev, BT_HCI_CMD_LE_READ_NUM_SUPPORTED_ADV_SETS, &rsp, sizeof(rsp)); return 0; } static int cmd_remove_adv_set(struct btdev *dev, const void *data, uint8_t len) { const struct bt_hci_cmd_le_remove_adv_set *cmd = data; struct le_ext_adv *ext_adv; uint8_t status = BT_HCI_ERR_SUCCESS; ext_adv = queue_find(dev->le_ext_adv, match_ext_adv_handle, UINT_TO_PTR(cmd->handle)); if (!ext_adv) { status = BT_HCI_ERR_UNKNOWN_ADVERTISING_ID; cmd_complete(dev, BT_HCI_CMD_LE_REMOVE_ADV_SET, &status, sizeof(status)); return 0; } if (ext_adv->enable) { status = BT_HCI_ERR_COMMAND_DISALLOWED; cmd_complete(dev, BT_HCI_CMD_LE_REMOVE_ADV_SET, &status, sizeof(status)); return 0; } queue_remove(dev->le_ext_adv, ext_adv); free(ext_adv); cmd_complete(dev, BT_HCI_CMD_LE_REMOVE_ADV_SET, &status, sizeof(status)); return 0; } static int cmd_clear_adv_sets(struct btdev *dev, const void *data, uint8_t len) { const struct queue_entry *entry; uint8_t status = BT_HCI_ERR_SUCCESS; for (entry = queue_get_entries(dev->le_ext_adv); entry; entry = entry->next) { struct le_ext_adv *ext_adv = entry->data; if (ext_adv->enable) { status = BT_HCI_ERR_COMMAND_DISALLOWED; cmd_complete(dev, BT_HCI_CMD_LE_CLEAR_ADV_SETS, &status, sizeof(status)); return 0; } } queue_remove_all(dev->le_ext_adv, NULL, NULL, le_ext_adv_free); cmd_complete(dev, BT_HCI_CMD_LE_CLEAR_ADV_SETS, &status, sizeof(status)); return 0; } static int cmd_set_pa_params(struct btdev *dev, const void *data, uint8_t len) { const struct bt_hci_cmd_le_set_pa_params *cmd = data; uint8_t status; if (dev->le_pa_enable) { status = BT_HCI_ERR_COMMAND_DISALLOWED; } else { status = BT_HCI_ERR_SUCCESS; dev->le_pa_properties = le16_to_cpu(cmd->properties); dev->le_pa_min_interval = cmd->min_interval; dev->le_pa_max_interval = cmd->max_interval; } cmd_complete(dev, BT_HCI_CMD_LE_SET_PA_PARAMS, &status, sizeof(status)); return 0; } static int cmd_set_pa_data(struct btdev *dev, const void *data, uint8_t len) { const struct bt_hci_cmd_le_set_pa_data *cmd = data; uint8_t status = BT_HCI_ERR_SUCCESS; uint8_t data_len = cmd->data_len; if (data_len > MAX_PA_DATA_LEN) data_len = MAX_PA_DATA_LEN; dev->le_pa_data_len = data_len; memcpy(dev->le_pa_data, cmd->data, data_len); cmd_complete(dev, BT_HCI_CMD_LE_SET_PA_DATA, &status, sizeof(status)); return 0; } static void send_biginfo(struct btdev *dev, const struct btdev *remote, uint16_t sync_handle) { struct bt_hci_evt_le_big_info_adv_report ev; const struct btdev_conn *conn; struct bt_hci_bis *bis; conn = find_bis_index(remote, 0); if (!conn) return; bis = conn->data; memset(&ev, 0, sizeof(ev)); ev.sync_handle = cpu_to_le16(sync_handle); ev.num_bis = 1; while (find_bis_index(remote, ev.num_bis)) ev.num_bis++; ev.nse = 0x01; ev.iso_interval = bis->latency / 1.25; ev.bn = 0x01; ev.pto = 0x00; ev.irc = 0x01; ev.max_pdu = bis->sdu; memcpy(ev.sdu_interval, bis->sdu_interval, sizeof(ev.sdu_interval)); ev.max_sdu = bis->sdu; ev.phy = bis->phy; ev.framing = bis->framing; ev.encryption = bis->encryption; le_meta_event(dev, BT_HCI_EVT_LE_BIG_INFO_ADV_REPORT, &ev, sizeof(ev)); } static void send_pa(struct btdev *dev, const struct btdev *remote, uint8_t offset, uint16_t sync_handle) { struct __packed { struct bt_hci_le_pa_report ev; uint8_t data[247]; } pdu; memset(&pdu.ev, 0, sizeof(pdu.ev)); pdu.ev.handle = cpu_to_le16(sync_handle); pdu.ev.tx_power = 127; pdu.ev.rssi = 127; pdu.ev.cte_type = 0x0ff; if ((size_t) remote->le_pa_data_len - offset > sizeof(pdu.data)) { pdu.ev.data_status = 0x01; pdu.ev.data_len = sizeof(pdu.data); } else { pdu.ev.data_status = 0x00; pdu.ev.data_len = remote->le_pa_data_len - offset; } memcpy(pdu.data, remote->le_pa_data + offset, pdu.ev.data_len); le_meta_event(dev, BT_HCI_EVT_LE_PA_REPORT, &pdu, sizeof(pdu.ev) + pdu.ev.data_len); if (pdu.ev.data_status == 0x01) { offset += pdu.ev.data_len; send_pa(dev, remote, offset, sync_handle); return; } send_biginfo(dev, remote, sync_handle); } static bool match_sync_handle(const void *data, const void *match_data) { const struct le_per_adv *per_adv = data; uint16_t sync_handle = PTR_TO_UINT(match_data); return per_adv->sync_handle == sync_handle; } static bool match_big_handle(const void *data, const void *match_data) { const struct le_big *big = data; uint8_t handle = PTR_TO_UINT(match_data); return big->handle == handle; } static bool match_dev(const void *data, const void *match_data) { const struct le_per_adv *per_adv = data; const struct btdev *dev = match_data; return dev == find_btdev_by_bdaddr_type(per_adv->addr, per_adv->addr_type); } static void le_pa_sync_estabilished(struct btdev *dev, struct btdev *remote, uint8_t status) { struct bt_hci_evt_le_per_sync_established ev; struct le_per_adv *per_adv; uint16_t sync_handle = SYNC_HANDLE; per_adv = queue_find(dev->le_per_adv, match_dev, remote); if (!per_adv) return; memset(&ev, 0, sizeof(ev)); ev.status = status; if (status) { queue_remove(dev->le_per_adv, per_adv); free(per_adv); le_meta_event(dev, BT_HCI_EVT_LE_PA_SYNC_ESTABLISHED, &ev, sizeof(ev)); return; } while (queue_find(dev->le_per_adv, match_sync_handle, UINT_TO_PTR(sync_handle))) sync_handle++; per_adv->sync_handle = sync_handle; ev.handle = cpu_to_le16(per_adv->sync_handle); ev.addr_type = per_adv->addr_type; memcpy(ev.addr, per_adv->addr, sizeof(ev.addr)); ev.phy = 0x01; ev.interval = remote->le_pa_min_interval; ev.clock_accuracy = 0x07; le_meta_event(dev, BT_HCI_EVT_LE_PA_SYNC_ESTABLISHED, &ev, sizeof(ev)); send_pa(dev, remote, 0, per_adv->sync_handle); } static int cmd_set_pa_enable(struct btdev *dev, const void *data, uint8_t len) { const struct bt_hci_cmd_le_set_pa_enable *cmd = data; uint8_t status; int i; if (dev->le_pa_enable == cmd->enable) { status = BT_HCI_ERR_COMMAND_DISALLOWED; } else { dev->le_pa_enable = cmd->enable; status = BT_HCI_ERR_SUCCESS; } cmd_complete(dev, BT_HCI_CMD_LE_SET_PA_ENABLE, &status, sizeof(status)); for (i = 0; i < MAX_BTDEV_ENTRIES; i++) { struct btdev *remote = btdev_list[i]; if (!remote || remote == dev) continue; if (remote->le_scan_enable && queue_find(remote->le_per_adv, match_sync_handle, UINT_TO_PTR(INV_HANDLE))) le_pa_sync_estabilished(remote, dev, BT_HCI_ERR_SUCCESS); } return 0; } static int cmd_set_ext_scan_params(struct btdev *dev, const void *data, uint8_t len) { const struct bt_hci_cmd_le_set_ext_scan_params *cmd = data; const struct bt_hci_le_scan_phy *scan = (void *)cmd->data; uint8_t status; if (dev->le_scan_enable) status = BT_HCI_ERR_COMMAND_DISALLOWED; else if (cmd->num_phys == 0) status = BT_HCI_ERR_INVALID_PARAMETERS; else { status = BT_HCI_ERR_SUCCESS; /* Currently we dont support multiple types in single * command So just take the first one will do. */ dev->le_scan_type = scan->type; dev->le_scan_own_addr_type = cmd->own_addr_type; dev->le_scan_filter_policy = cmd->filter_policy; } cmd_complete(dev, BT_HCI_CMD_LE_SET_EXT_SCAN_PARAMS, &status, sizeof(status)); return 0; } static int cmd_set_ext_scan_enable(struct btdev *dev, const void *data, uint8_t len) { const struct bt_hci_cmd_le_set_ext_scan_enable *cmd = data; uint8_t status; if (dev->le_scan_enable == cmd->enable) { status = BT_HCI_ERR_COMMAND_DISALLOWED; goto done; } /* If Enable is set to 0x01, the scanning parameters' Own_Address_Type * parameter is set to 0x01 or 0x03, and the random address for the * device has not been initialized, the Controller shall return the * error code Invalid HCI Command Parameters (0x12). */ if ((dev->le_scan_own_addr_type == 0x01 || dev->le_scan_own_addr_type == 0x03) && !bacmp((bdaddr_t *)dev->random_addr, BDADDR_ANY)) { status = BT_HCI_ERR_INVALID_PARAMETERS; goto done; } dev->le_scan_enable = cmd->enable; dev->le_filter_dup = cmd->filter_dup; status = BT_HCI_ERR_SUCCESS; done: cmd_complete(dev, BT_HCI_CMD_LE_SET_EXT_SCAN_ENABLE, &status, sizeof(status)); return 0; } static void scan_pa(struct btdev *dev, struct btdev *remote) { struct le_per_adv *per_adv = queue_find(dev->le_per_adv, match_sync_handle, UINT_TO_PTR(INV_HANDLE)); if (!per_adv || !remote->le_pa_enable) return; if (remote != find_btdev_by_bdaddr_type(per_adv->addr, per_adv->addr_type)) return; le_pa_sync_estabilished(dev, remote, BT_HCI_ERR_SUCCESS); } static int cmd_set_ext_scan_enable_complete(struct btdev *dev, const void *data, uint8_t len) { const struct bt_hci_cmd_le_set_ext_scan_enable *cmd = data; int i; if (!dev->le_scan_enable || !cmd->enable) return 0; for (i = 0; i < MAX_BTDEV_ENTRIES; i++) { if (!btdev_list[i] || btdev_list[i] == dev) continue; scan_pa(dev, btdev_list[i]); } return 0; } static int cmd_ext_create_conn(struct btdev *dev, const void *data, uint8_t len) { cmd_status(dev, BT_HCI_ERR_SUCCESS, BT_HCI_CMD_LE_EXT_CREATE_CONN); return 0; } static void ext_adv_term(void *data, void *user_data) { struct le_ext_adv *adv = data; struct btdev_conn *conn = user_data; /* if connectable bit is set the send adv terminate */ if (conn && adv->type & 0x01) { adv_set_terminate(adv->dev, 0x00, adv->handle, conn->handle, 0x00); ext_adv_disable(adv, NULL); } } static void le_ext_conn_complete(struct btdev *btdev, const struct bt_hci_cmd_le_ext_create_conn *cmd, struct le_ext_adv *ext_adv, uint8_t status) { struct btdev_conn *conn = NULL; struct bt_hci_evt_le_enhanced_conn_complete ev; struct bt_hci_le_ext_create_conn *lecc = (void *)cmd->data; memset(&ev, 0, sizeof(ev)); if (!status) { conn = conn_add_acl(btdev, cmd->peer_addr, cmd->peer_addr_type); if (!conn) return; ev.status = status; ev.peer_addr_type = btdev->le_scan_own_addr_type; if (ev.peer_addr_type == 0x01 || ev.peer_addr_type == 0x03) { ev.peer_addr_type = 0x01; memcpy(ev.peer_addr, btdev->random_addr, 6); } else memcpy(ev.peer_addr, btdev->bdaddr, 6); ev.role = 0x01; ev.handle = cpu_to_le16(conn->handle); ev.interval = lecc->max_interval; ev.latency = lecc->latency; ev.supv_timeout = lecc->supv_timeout; /* Set Local RPA if an RPA was generated for the advertising */ if (ext_adv->rpa) memcpy(ev.local_rpa, ext_adv->random_addr, sizeof(ev.local_rpa)); le_meta_event(conn->link->dev, BT_HCI_EVT_LE_ENHANCED_CONN_COMPLETE, &ev, sizeof(ev)); /* Disable EXT ADV */ queue_foreach(conn->link->dev->le_ext_adv, ext_adv_term, conn); } ev.status = status; ev.peer_addr_type = cmd->peer_addr_type; memcpy(ev.peer_addr, cmd->peer_addr, 6); ev.role = 0x00; /* Use random address as Local RPA if Create Connection own_addr_type * is 0x03 since that expects the controller to generate the RPA. */ if (btdev->le_scan_own_addr_type == 0x03) memcpy(ev.local_rpa, btdev->random_addr, 6); else memset(ev.local_rpa, 0, sizeof(ev.local_rpa)); le_meta_event(btdev, BT_HCI_EVT_LE_ENHANCED_CONN_COMPLETE, &ev, sizeof(ev)); /* Disable EXT ADV */ if (conn) queue_foreach(btdev->le_ext_adv, ext_adv_term, conn); } static int cmd_ext_create_conn_complete(struct btdev *dev, const void *data, uint8_t len) { const struct bt_hci_cmd_le_ext_create_conn *cmd = data; const struct queue_entry *entry; struct btdev *remote; dev->le_scan_own_addr_type = cmd->own_addr_type; remote = find_btdev_by_bdaddr_type(cmd->peer_addr, cmd->peer_addr_type); if (!remote) { le_ext_conn_complete(dev, cmd, NULL, BT_HCI_ERR_CONN_FAILED_TO_ESTABLISH); return 0; } for (entry = queue_get_entries(remote->le_ext_adv); entry; entry = entry->next) { struct le_ext_adv *ext_adv = entry->data; if (ext_adv_is_connectable(ext_adv) && ext_adv_match_addr(dev, ext_adv) && ext_adv_addr_type(ext_adv) == cmd->peer_addr_type) { le_ext_conn_complete(dev, cmd, ext_adv, 0); return 0; } } return 0; } static struct le_per_adv *le_per_adv_new(struct btdev *btdev, uint8_t addr_type, const uint8_t *addr) { struct le_per_adv *per_adv; per_adv = new0(struct le_per_adv, 1); per_adv->dev = btdev; per_adv->addr_type = addr_type; memcpy(per_adv->addr, addr, 6); per_adv->sync_handle = INV_HANDLE; /* Add to queue */ if (!queue_push_tail(btdev->le_per_adv, per_adv)) { free(per_adv); return NULL; } return per_adv; } static int cmd_pa_create_sync(struct btdev *dev, const void *data, uint8_t len) { const struct bt_hci_cmd_le_pa_create_sync *cmd = data; uint8_t status = BT_HCI_ERR_SUCCESS; struct le_per_adv *per_adv; /* Create new train */ per_adv = le_per_adv_new(dev, cmd->addr_type, cmd->addr); if (!per_adv) status = BT_HCI_ERR_MEM_CAPACITY_EXCEEDED; cmd_status(dev, status, BT_HCI_CMD_LE_PA_CREATE_SYNC); return 0; } static int cmd_pa_create_sync_complete(struct btdev *dev, const void *data, uint8_t len) { const struct bt_hci_cmd_le_pa_create_sync *cmd = data; struct btdev *remote; /* This command may be issued whether or not scanning is enabled and * scanning may be enabled and disabled (see the LE Set Extended Scan * Enable command) while this command is pending. However, * synchronization can only occur when scanning is enabled. While * scanning is disabled, no attempt to synchronize will take place. */ if (!dev->scan_enable) return 0; remote = find_btdev_by_bdaddr_type(cmd->addr, cmd->addr_type); if (!remote || !remote->le_pa_enable) return 0; le_pa_sync_estabilished(dev, remote, BT_HCI_ERR_SUCCESS); return 0; } static int cmd_pa_create_sync_cancel(struct btdev *dev, const void *data, uint8_t len) { uint8_t status = BT_HCI_ERR_SUCCESS; /* If the Host issues this command while no * HCI_LE_Periodic_Advertising_Create_Sync command is pending, the * Controller shall return the error code Command Disallowed (0x0C). */ if (!queue_find(dev->le_per_adv, match_sync_handle, UINT_TO_PTR(INV_HANDLE))) status = BT_HCI_ERR_COMMAND_DISALLOWED; cmd_complete(dev, BT_HCI_CMD_LE_PA_CREATE_SYNC_CANCEL, &status, sizeof(status)); /* After the HCI_Command_Complete is sent and if the cancellation was * successful, the Controller sends an * HCI_LE_Periodic_Advertising_Sync_Established event to the Host with * the error code Operation Cancelled by Host (0x44). */ if (!status) le_pa_sync_estabilished(dev, NULL, BT_HCI_ERR_CANCELLED); return 0; } static int cmd_pa_term_sync(struct btdev *dev, const void *data, uint8_t len) { const struct bt_hci_cmd_le_pa_term_sync *cmd = data; struct le_per_adv *per_adv; uint16_t sync_handle = le16_to_cpu(cmd->sync_handle); uint8_t status = BT_HCI_ERR_SUCCESS; per_adv = queue_find(dev->le_per_adv, match_sync_handle, UINT_TO_PTR(sync_handle)); /* If the periodic advertising train corresponding to the Sync_Handle * parameter does not exist, then the Controller shall return the error * code Unknown Advertising Identifier (0x42). */ if (!per_adv) { status = BT_HCI_ERR_UNKNOWN_ADVERTISING_ID; } else { queue_remove(dev->le_per_adv, per_adv); free(per_adv); } cmd_complete(dev, BT_HCI_CMD_LE_PA_TERM_SYNC, &status, sizeof(status)); return 0; } static int cmd_pa_add(struct btdev *dev, const void *data, uint8_t len) { /* TODO */ return -ENOTSUP; } static int cmd_pa_remove(struct btdev *dev, const void *data, uint8_t len) { /* TODO */ return -ENOTSUP; } static int cmd_pa_clear(struct btdev *dev, const void *data, uint8_t len) { /* TODO */ return -ENOTSUP; } static int cmd_read_pa_list_size(struct btdev *dev, const void *data, uint8_t len) { /* TODO */ return -ENOTSUP; } static int cmd_read_tx_power(struct btdev *dev, const void *data, uint8_t len) { struct bt_hci_rsp_le_read_tx_power rsp; memset(&rsp, 0, sizeof(rsp)); rsp.status = BT_HCI_ERR_SUCCESS; /* a random default value */ rsp.max_tx_power = 0x07; rsp.min_tx_power = 0xDE; cmd_complete(dev, BT_HCI_CMD_LE_READ_TX_POWER, &rsp, sizeof(rsp)); return 0; } static int cmd_set_privacy_mode(struct btdev *dev, const void *data, uint8_t len) { const struct bt_hci_cmd_le_set_priv_mode *cmd = data; const struct btdev_rl *rl; uint8_t status; /* This command shall not be used when address resolution is enabled in * the Controller and: * • Advertising (other than periodic advertising) is enabled, * • Scanning is enabled, or * • an HCI_LE_Create_Connection, HCI_LE_Extended_Create_Connection, * or HCI_LE_Periodic_Advertising_Create_Sync command is pending. */ if (dev->le_rl_enable || dev->le_adv_enable || dev->le_scan_enable) { status = BT_HCI_ERR_COMMAND_DISALLOWED; goto done; } /* If the device is not on the resolving list, the Controller shall * return the error code Unknown Connection Identifier (0x02). */ rl = rl_find(dev, cmd->peer_id_addr_type, cmd->peer_id_addr); if (!rl) { status = BT_HCI_ERR_UNKNOWN_CONN_ID; goto done; } if (cmd->priv_mode > 0x01) { status = BT_HCI_ERR_INVALID_PARAMETERS; goto done; } ((struct btdev_rl *)rl)->mode = cmd->priv_mode; status = BT_HCI_ERR_SUCCESS; done: cmd_complete(dev, BT_HCI_CMD_LE_SET_PRIV_MODE, &status, sizeof(status)); return 0; } #define CMD_LE_50 \ CMD(BT_HCI_CMD_LE_SET_DEFAULT_PHY, cmd_set_default_phy, NULL), \ CMD(BT_HCI_CMD_LE_SET_ADV_SET_RAND_ADDR, cmd_set_adv_rand_addr, NULL), \ CMD(BT_HCI_CMD_LE_SET_EXT_ADV_PARAMS, cmd_set_ext_adv_params, NULL), \ CMD(BT_HCI_CMD_LE_SET_EXT_ADV_DATA, cmd_set_ext_adv_data, NULL), \ CMD(BT_HCI_CMD_LE_SET_EXT_SCAN_RSP_DATA, cmd_set_ext_scan_rsp_data, \ NULL), \ CMD(BT_HCI_CMD_LE_SET_EXT_ADV_ENABLE, cmd_set_ext_adv_enable, NULL), \ CMD(BT_HCI_CMD_LE_READ_MAX_ADV_DATA_LEN, cmd_read_max_adv_data_len, \ NULL), \ CMD(BT_HCI_CMD_LE_READ_NUM_SUPPORTED_ADV_SETS, cmd_read_num_adv_sets, \ NULL), \ CMD(BT_HCI_CMD_LE_REMOVE_ADV_SET, cmd_remove_adv_set, NULL), \ CMD(BT_HCI_CMD_LE_CLEAR_ADV_SETS, cmd_clear_adv_sets, NULL), \ CMD(BT_HCI_CMD_LE_SET_PA_PARAMS, cmd_set_pa_params, \ NULL), \ CMD(BT_HCI_CMD_LE_SET_PA_DATA, cmd_set_pa_data, NULL), \ CMD(BT_HCI_CMD_LE_SET_PA_ENABLE, cmd_set_pa_enable, NULL), \ CMD(BT_HCI_CMD_LE_SET_EXT_SCAN_PARAMS, cmd_set_ext_scan_params, NULL), \ CMD(BT_HCI_CMD_LE_SET_EXT_SCAN_ENABLE, cmd_set_ext_scan_enable, \ cmd_set_ext_scan_enable_complete), \ CMD(BT_HCI_CMD_LE_EXT_CREATE_CONN, cmd_ext_create_conn, \ cmd_ext_create_conn_complete), \ CMD(BT_HCI_CMD_LE_PA_CREATE_SYNC, cmd_pa_create_sync, \ cmd_pa_create_sync_complete), \ CMD(BT_HCI_CMD_LE_PA_CREATE_SYNC_CANCEL, cmd_pa_create_sync_cancel, \ NULL), \ CMD(BT_HCI_CMD_LE_PA_TERM_SYNC, cmd_pa_term_sync, NULL), \ CMD(BT_HCI_CMD_LE_ADD_DEV_PA_LIST, cmd_pa_add, NULL), \ CMD(BT_HCI_CMD_LE_REMOVE_DEV_PA_LIST, cmd_pa_remove, NULL), \ CMD(BT_HCI_CMD_LE_CLEAR_PA_LIST, cmd_pa_clear, NULL), \ CMD(BT_HCI_CMD_LE_READ_PA_LIST_SIZE, cmd_read_pa_list_size, NULL), \ CMD(BT_HCI_CMD_LE_READ_TX_POWER, cmd_read_tx_power, NULL), \ CMD(BT_HCI_CMD_LE_SET_PRIV_MODE, cmd_set_privacy_mode, NULL) static const struct btdev_cmd cmd_le_5_0[] = { CMD_COMMON_ALL, CMD_COMMON_BREDR_LE, CMD_LE, CMD_LE_50, {} }; static void set_le_50_commands(struct btdev *btdev) { btdev->commands[35] |= 0x20; /* LE Set Default PHY */ btdev->commands[36] |= 0x02; /* LE Set Adv Set Random Address */ btdev->commands[36] |= 0x04; /* LE Set Ext Adv Parameters */ btdev->commands[36] |= 0x08; /* LE Set Ext Adv Data */ btdev->commands[36] |= 0x10; /* LE Set Ext Scan Response Data */ btdev->commands[36] |= 0x20; /* LE Set Ext Adv Enable */ btdev->commands[36] |= 0x40; /* LE Read Maximum Adv Data Length */ btdev->commands[36] |= 0x80; /* LE Read Num of Supported Adv Sets */ btdev->commands[37] |= 0x01; /* LE Remove Adv Set */ btdev->commands[37] |= 0x02; /* LE Clear Adv Sets */ btdev->commands[37] |= 0x04; /* LE Set Periodic Adv Parameters */ btdev->commands[37] |= 0x08; /* LE Set Periodic Adv Data */ btdev->commands[37] |= 0x10; /* LE Set Periodic Adv Enable */ btdev->commands[37] |= 0x20; /* LE Set Ext Scan Parameters */ btdev->commands[37] |= 0x40; /* LE Set Ext Scan Enable */ btdev->commands[37] |= 0x80; /* LE Ext Create Connection */ btdev->commands[38] |= 0x01; /* LE Periodic Adv Create Sync */ btdev->commands[38] |= 0x02; /* LE Periodic Adv Create Sync Cancel */ btdev->commands[38] |= 0x04; /* LE Periodic Adv Terminate Sync */ btdev->commands[38] |= 0x08; /* LE Add Device To Periodic Adv List */ btdev->commands[38] |= 0x10; /* LE Remove Periodic Adv List */ btdev->commands[38] |= 0x20; /* LE Clear Periodic Adv List */ btdev->commands[38] |= 0x40; /* LE Read Periodic Adv List Size */ btdev->commands[38] |= 0x80; /* LE Read Transmit Power */ btdev->commands[39] |= 0x04; /* LE Set Privacy Mode */ btdev->cmds = cmd_le_5_0; } static int cmd_read_size_v2(struct btdev *dev, const void *data, uint8_t len) { struct bt_hci_rsp_le_read_buffer_size_v2 rsp; memset(&rsp, 0, sizeof(rsp)); rsp.status = BT_HCI_ERR_SUCCESS; rsp.acl_mtu = cpu_to_le16(dev->acl_mtu); rsp.acl_max_pkt = dev->acl_max_pkt; rsp.iso_mtu = cpu_to_le16(dev->iso_mtu); rsp.iso_max_pkt = dev->iso_max_pkt; cmd_complete(dev, BT_HCI_CMD_LE_READ_BUFFER_SIZE_V2, &rsp, sizeof(rsp)); return 0; } static int cmd_read_iso_tx_sync(struct btdev *dev, const void *data, uint8_t len) { /* TODO */ return -ENOTSUP; } static int find_cig(struct btdev *dev, uint8_t cig_id) { unsigned int i; for (i = 0; i < ARRAY_SIZE(dev->le_cig); ++i) if (dev->le_cig[i].params.cig_id == cig_id) return i; return -1; } static uint16_t make_cis_handle(uint8_t cig_idx, uint8_t cis_idx) { /* Put CIG+CIS idxs to handle so don't need to track separately */ return ISO_HANDLE + cig_idx*CIS_SIZE + cis_idx; } static int parse_cis_handle(uint16_t handle, int *cis_idx) { int cig_idx; if (handle < ISO_HANDLE || handle >= ISO_HANDLE + CIS_SIZE*CIG_SIZE) return -1; cig_idx = (handle - ISO_HANDLE) / CIS_SIZE; if (cis_idx) *cis_idx = (handle - ISO_HANDLE) % CIS_SIZE; return cig_idx; } static int cmd_set_cig_params(struct btdev *dev, const void *data, uint8_t len) { const struct bt_hci_cmd_le_set_cig_params *cmd = data; struct lescp { struct bt_hci_rsp_le_set_cig_params params; uint16_t handle[CIS_SIZE]; } __attribute__ ((packed)) rsp; int i = 0; int cig_idx; uint32_t interval; uint16_t latency; memset(&rsp, 0, sizeof(rsp)); rsp.params.cig_id = cmd->cig_id; if (cmd->num_cis > ARRAY_SIZE(dev->le_cig[0].cis)) { rsp.params.status = BT_HCI_ERR_MEM_CAPACITY_EXCEEDED; goto done; } if (cmd->cig_id > 0xef) { rsp.params.status = BT_HCI_ERR_INVALID_PARAMETERS; goto done; } interval = get_le24(cmd->c_interval); if (interval < 0x0000ff || interval > 0x0fffff) { rsp.params.status = BT_HCI_ERR_INVALID_PARAMETERS; goto done; } interval = get_le24(cmd->p_interval); if (interval < 0x0000ff || interval > 0x0fffff) { rsp.params.status = BT_HCI_ERR_INVALID_PARAMETERS; goto done; } if (cmd->sca > 0x07) { rsp.params.status = BT_HCI_ERR_INVALID_PARAMETERS; goto done; } if (cmd->packing > 0x01) { rsp.params.status = BT_HCI_ERR_INVALID_PARAMETERS; goto done; } if (cmd->framing > 0x01) { rsp.params.status = BT_HCI_ERR_INVALID_PARAMETERS; goto done; } latency = cpu_to_le16(cmd->c_latency); if (latency < 0x0005 || latency > 0x0fa0) { rsp.params.status = BT_HCI_ERR_INVALID_PARAMETERS; goto done; } latency = cpu_to_le16(cmd->p_latency); if (latency < 0x0005 || latency > 0x0fa0) { rsp.params.status = BT_HCI_ERR_INVALID_PARAMETERS; goto done; } cig_idx = find_cig(dev, cmd->cig_id); if (cig_idx < 0) cig_idx = find_cig(dev, 0xff); if (cig_idx < 0) { rsp.params.status = BT_HCI_ERR_MEM_CAPACITY_EXCEEDED; goto done; } /* BLUETOOTH CORE SPECIFICATION Version 5.3 | Vol 4, Part E * page 2553 * * If the Host issues this command when the CIG is not in the * configurable state, the Controller shall return the error * code Command Disallowed (0x0C). */ if (dev->le_cig[cig_idx].activated) { rsp.params.status = BT_HCI_ERR_COMMAND_DISALLOWED; goto done; } rsp.params.status = BT_HCI_ERR_SUCCESS; for (i = 0; i < cmd->num_cis; i++) { rsp.params.num_handles++; rsp.handle[i] = cpu_to_le16(make_cis_handle(cig_idx, i)); } memcpy(&dev->le_cig[cig_idx], data, len); done: cmd_complete(dev, BT_HCI_CMD_LE_SET_CIG_PARAMS, &rsp, sizeof(rsp.params) + (i * sizeof(uint16_t))); return 0; } static int cmd_set_cig_params_test(struct btdev *dev, const void *data, uint8_t len) { /* TODO */ return -ENOTSUP; } static int cmd_create_cis(struct btdev *dev, const void *data, uint8_t len) { const struct bt_hci_cmd_le_create_cis *cmd = data; int i, j; for (i = 0; i < cmd->num_cis; i++) { const struct bt_hci_cis *cis = &cmd->cis[i]; struct btdev_conn *acl; struct btdev_conn *iso; int cig_idx, cis_idx; /* Check for errors (Core v5.3 Vol 4 Part E Sec. 7.8.99) */ for (j = 0; j < i; j++) if (cis->cis_handle == cmd->cis[j].cis_handle) return -EINVAL; cig_idx = parse_cis_handle(le16_to_cpu(cis->cis_handle), &cis_idx); if (cig_idx < 0) return -ENOENT; if (cis_idx >= dev->le_cig[cig_idx].params.num_cis) return -ENOENT; acl = queue_find(dev->conns, match_handle, UINT_TO_PTR(le16_to_cpu(cis->acl_handle))); if (!acl) return -ENOENT; iso = queue_find(dev->conns, match_handle, UINT_TO_PTR(le16_to_cpu(cis->cis_handle))); if (iso) return -EEXIST; } cmd_status(dev, BT_HCI_ERR_SUCCESS, BT_HCI_CMD_LE_CREATE_CIS); return 0; } static uint8_t le_cis_interval(uint8_t sdu_interval[3]) { /* ISO Interval (slots of 1.25 ms) = SDU_Interval (us) */ return get_le24(sdu_interval) / 1250; } static uint32_t le_cis_latecy(uint8_t ft, uint8_t iso_interval, uint8_t sdu_interval[3]) { uint32_t latency; uint32_t interval = get_le24(sdu_interval); /* Transport_Latency = FT × ISO_Interval - SDU_Interval */ latency = ft * (iso_interval * 1250) - interval; return latency >= interval ? latency : interval; } static void le_cis_estabilished(struct btdev *dev, struct btdev_conn *conn, uint8_t status) { struct bt_hci_evt_le_cis_established evt; int cig_idx, cis_idx = -1; memset(&evt, 0, sizeof(evt)); evt.status = status; evt.conn_handle = conn ? cpu_to_le16(conn->handle) : 0x0000; cig_idx = conn ? parse_cis_handle(conn->link->handle, &cis_idx) : -1; if (cig_idx < 0 && !evt.status) evt.status = BT_HCI_ERR_UNSPECIFIED_ERROR; if (!evt.status) { struct btdev *remote = conn->link->dev; struct le_cig *le_cig = &remote->le_cig[cig_idx]; memset(evt.cig_sync_delay, 0, sizeof(evt.cig_sync_delay)); memset(evt.cis_sync_delay, 0, sizeof(evt.cis_sync_delay)); evt.c_phy = le_cig->cis[cis_idx].c_phy; evt.p_phy = le_cig->cis[cis_idx].p_phy; evt.nse = 0x01; evt.c_bn = 0x01; evt.p_bn = 0x01; evt.c_ft = 0x02; evt.p_ft = 0x02; evt.c_mtu = le_cig->cis[cis_idx].c_sdu; evt.p_mtu = le_cig->cis[cis_idx].p_sdu; evt.interval = le_cis_interval(le_cig->params.c_interval); /* BLUETOOTH CORE SPECIFICATION Version 5.3 | Vol 6, Part G * page 3050: * * Transport_Latency_C_To_P = CIG_Sync_Delay + FT_C_To_P × * ISO_Interval - SDU_Interval_C_To_P * Transport_Latency_P_To_C = CIG_Sync_Delay + FT_P_To_C × * ISO_Interval - SDU_Interval_P_To_C */ put_le24(le_cis_latecy(evt.c_ft, evt.interval, le_cig->params.c_interval), evt.c_latency); put_le24(le_cis_latecy(evt.p_ft, evt.interval, le_cig->params.p_interval), evt.p_latency); } le_meta_event(dev, BT_HCI_EVT_LE_CIS_ESTABLISHED, &evt, sizeof(evt)); if (conn) le_meta_event(conn->link->dev, BT_HCI_EVT_LE_CIS_ESTABLISHED, &evt, sizeof(evt)); } static int cmd_create_cis_complete(struct btdev *dev, const void *data, uint8_t len) { const struct bt_hci_cmd_le_create_cis *cmd = data; int i; for (i = 0; i < cmd->num_cis; i++) { const struct bt_hci_cis *cis = &cmd->cis[i]; struct btdev_conn *acl; struct btdev_conn *iso; struct bt_hci_evt_le_cis_req evt; struct le_cig *le_cig; int cig_idx, cis_idx; cig_idx = parse_cis_handle(le16_to_cpu(cis->cis_handle), &cis_idx); if (cig_idx < 0) { le_cis_estabilished(dev, NULL, BT_HCI_ERR_UNKNOWN_CONN_ID); break; } le_cig = &dev->le_cig[cig_idx]; acl = queue_find(dev->conns, match_handle, UINT_TO_PTR(le16_to_cpu(cis->acl_handle))); if (!acl) { le_cis_estabilished(dev, NULL, BT_HCI_ERR_UNKNOWN_CONN_ID); break; } iso = queue_find(dev->conns, match_handle, UINT_TO_PTR(le16_to_cpu(cis->cis_handle))); if (!iso) { iso = conn_add_cis(acl, le16_to_cpu(cis->cis_handle)); if (!iso) { le_cis_estabilished(dev, NULL, BT_HCI_ERR_UNKNOWN_CONN_ID); break; } } evt.acl_handle = cpu_to_le16(acl->handle); evt.cis_handle = cpu_to_le16(iso->handle); evt.cig_id = le_cig->params.cig_id; evt.cis_id = le_cig->cis[cis_idx].cis_id; le_cig->activated = true; le_meta_event(iso->link->dev, BT_HCI_EVT_LE_CIS_REQ, &evt, sizeof(evt)); } return 0; } static bool match_handle_cig_idx(const void *data, const void *match_data) { const struct btdev_conn *conn = data; int cig_idx = PTR_TO_INT(match_data); return cig_idx >= 0 && parse_cis_handle(conn->handle, NULL) == cig_idx; } static int cmd_remove_cig(struct btdev *dev, const void *data, uint8_t len) { const struct bt_hci_cmd_le_remove_cig *cmd = data; struct bt_hci_rsp_le_remove_cig rsp; struct btdev_conn *conn = NULL; int idx; memset(&rsp, 0, sizeof(rsp)); rsp.cig_id = cmd->cig_id; idx = find_cig(dev, cmd->cig_id); conn = queue_find(dev->conns, match_handle_cig_idx, INT_TO_PTR(idx)); if (idx >= 0 && !conn) { memset(&dev->le_cig[idx], 0, sizeof(struct le_cig)); dev->le_cig[idx].params.cig_id = 0xff; rsp.status = BT_HCI_ERR_SUCCESS; } else if (conn) { rsp.status = BT_HCI_ERR_COMMAND_DISALLOWED; } else { rsp.status = BT_HCI_ERR_UNKNOWN_CONN_ID; } cmd_complete(dev, BT_HCI_CMD_LE_REMOVE_CIG, &rsp, sizeof(rsp)); return 0; } static int cmd_accept_cis(struct btdev *dev, const void *data, uint8_t len) { const struct bt_hci_cmd_le_accept_cis *cmd = data; struct btdev_conn *conn; conn = queue_find(dev->conns, match_handle, UINT_TO_PTR(cpu_to_le16(cmd->handle))); if (!conn) { cmd_status(dev, BT_HCI_ERR_UNKNOWN_CONN_ID, BT_HCI_CMD_LE_ACCEPT_CIS); return 0; } cmd_status(dev, BT_HCI_ERR_SUCCESS, BT_HCI_CMD_LE_ACCEPT_CIS); le_cis_estabilished(dev, conn, BT_HCI_ERR_SUCCESS); return 0; } static int cmd_reject_cis(struct btdev *dev, const void *data, uint8_t len) { const struct bt_hci_cmd_le_reject_cis *cmd = data; struct btdev_conn *conn; conn = queue_find(dev->conns, match_handle, UINT_TO_PTR(cpu_to_le16(cmd->handle))); if (!conn) { cmd_status(dev, BT_HCI_ERR_UNKNOWN_CONN_ID, BT_HCI_CMD_LE_REJECT_CIS); return 0; } cmd_status(dev, BT_HCI_ERR_SUCCESS, BT_HCI_CMD_LE_REJECT_CIS); le_cis_estabilished(dev, conn, cmd->reason); return 0; } static int cmd_create_big(struct btdev *dev, const void *data, uint8_t len) { cmd_status(dev, BT_HCI_ERR_SUCCESS, BT_HCI_CMD_LE_CREATE_BIG); return 0; } static int cmd_create_big_complete(struct btdev *dev, const void *data, uint8_t len) { const struct bt_hci_cmd_le_create_big *cmd = data; const struct bt_hci_bis *bis = &cmd->bis; int i; struct bt_hci_evt_le_big_complete evt; uint16_t *bis_handle; uint8_t *pdu; uint8_t pdu_len; pdu_len = sizeof(evt) + cmd->num_bis * sizeof(*bis_handle); pdu = malloc(pdu_len); if (!pdu) return -ENOMEM; bis_handle = (uint16_t *)(pdu + sizeof(evt)); memset(&evt, 0, sizeof(evt)); for (i = 0; i < cmd->num_bis; i++) { struct btdev_conn *conn; conn = conn_add_bis(dev, ISO_HANDLE + i, bis); if (!conn) { evt.status = BT_HCI_ERR_MEM_CAPACITY_EXCEEDED; goto done; } *bis_handle = cpu_to_le16(conn->handle); bis_handle++; } evt.handle = cmd->handle; evt.phy = bis->phy; evt.max_pdu = bis->sdu; memcpy(evt.sync_delay, bis->sdu_interval, 3); memcpy(evt.latency, bis->sdu_interval, 3); evt.interval = bis->latency / 1.25; evt.num_bis = cmd->num_bis; done: memcpy(pdu, &evt, sizeof(evt)); le_meta_event(dev, BT_HCI_EVT_LE_BIG_COMPLETE, pdu, pdu_len); free(pdu); return 0; } static int cmd_create_big_test(struct btdev *dev, const void *data, uint8_t len) { /* TODO */ return -ENOTSUP; } static int cmd_term_big(struct btdev *dev, const void *data, uint8_t len) { cmd_status(dev, BT_HCI_ERR_SUCCESS, BT_HCI_CMD_LE_TERM_BIG); return 0; } static int cmd_term_big_complete(struct btdev *dev, const void *data, uint8_t len) { const struct bt_hci_cmd_le_term_big *cmd = data; struct bt_hci_evt_le_big_terminate rsp; memset(&rsp, 0, sizeof(rsp)); rsp.reason = cmd->reason; rsp.handle = cmd->handle; le_meta_event(dev, BT_HCI_EVT_LE_BIG_TERMINATE, &rsp, sizeof(rsp)); return 0; } static int cmd_big_create_sync(struct btdev *dev, const void *data, uint8_t len) { const struct bt_hci_cmd_le_big_create_sync *cmd = data; uint8_t status = BT_HCI_ERR_SUCCESS; uint16_t sync_handle = le16_to_cpu(cmd->sync_handle); /* If the Sync_Handle does not exist, the Controller shall return the * error code Unknown Advertising Identifier (0x42). */ if (!queue_find(dev->le_per_adv, match_sync_handle, UINT_TO_PTR(sync_handle))) { status = BT_HCI_ERR_UNKNOWN_ADVERTISING_ID; goto done; } /* If the Host sends this command with a BIG_Handle that is already * allocated, the Controller shall return the error code Command * Disallowed (0x0C). */ if (queue_find(dev->le_big, match_big_handle, UINT_TO_PTR(cmd->handle))) { status = BT_HCI_ERR_COMMAND_DISALLOWED; goto done; } /* If the Num_BIS parameter is greater than the total number of BISes * in the BIG, the Controller shall return the error code Unsupported * Feature or Parameter Value (0x11). */ if (cmd->num_bis != len - sizeof(*cmd)) status = BT_HCI_ERR_UNSUPPORTED_FEATURE; done: cmd_status(dev, status, BT_HCI_CMD_LE_BIG_CREATE_SYNC); return 0; } static struct le_big *le_big_new(struct btdev *btdev, uint8_t handle) { struct le_big *big; big = new0(struct le_big, 1); big->dev = btdev; big->handle = handle; big->bis = queue_new(); /* Add to queue */ if (!queue_push_tail(btdev->le_big, big)) { le_big_free(big); return NULL; } return big; } static int cmd_big_create_sync_complete(struct btdev *dev, const void *data, uint8_t len) { const struct bt_hci_cmd_le_big_create_sync *cmd = data; struct __packed { struct bt_hci_evt_le_big_sync_estabilished ev; uint16_t bis[BIS_SIZE]; } pdu; struct btdev *remote; struct btdev_conn *conn = NULL; struct bt_hci_bis *bis; int i; uint16_t sync_handle = le16_to_cpu(cmd->sync_handle); struct le_per_adv *per_adv = queue_find(dev->le_per_adv, match_sync_handle, UINT_TO_PTR(sync_handle)); struct le_big *big; if (!per_adv) return 0; remote = find_btdev_by_bdaddr_type(per_adv->addr, per_adv->addr_type); if (!remote) return 0; big = le_big_new(dev, cmd->handle); if (!big) { pdu.ev.status = BT_HCI_ERR_MEM_CAPACITY_EXCEEDED; le_meta_event(dev, BT_HCI_EVT_LE_BIG_SYNC_ESTABILISHED, &pdu, sizeof(pdu.ev)); return 0; } memset(&pdu.ev, 0, sizeof(pdu.ev)); for (i = 0; i < cmd->num_bis; i++) { conn = conn_link_bis(dev, remote, i); if (!conn) break; pdu.bis[i] = cpu_to_le16(conn->handle); queue_push_tail(big->bis, conn); } if (i != cmd->num_bis || !conn) { pdu.ev.status = BT_HCI_ERR_MEM_CAPACITY_EXCEEDED; le_meta_event(dev, BT_HCI_EVT_LE_BIG_SYNC_ESTABILISHED, &pdu, sizeof(pdu.ev)); return 0; } bis = conn->data; if (bis->encryption != cmd->encryption) { pdu.ev.status = BT_HCI_ERR_ENC_MODE_NOT_ACCEPTABLE; le_meta_event(dev, BT_HCI_EVT_LE_BIG_SYNC_ESTABILISHED, &pdu, sizeof(pdu.ev)); return 0; } pdu.ev.handle = cmd->handle; memcpy(pdu.ev.latency, bis->sdu_interval, sizeof(pdu.ev.interval)); pdu.ev.nse = 0x01; pdu.ev.bn = 0x01; pdu.ev.pto = 0x00; pdu.ev.irc = 0x01; pdu.ev.max_pdu = bis->sdu; pdu.ev.interval = bis->latency / 1.25; pdu.ev.num_bis = cmd->num_bis; le_meta_event(dev, BT_HCI_EVT_LE_BIG_SYNC_ESTABILISHED, &pdu, sizeof(pdu.ev) + (cmd->num_bis * sizeof(uint16_t))); return 0; } static int cmd_big_term_sync(struct btdev *dev, const void *data, uint8_t len) { const struct bt_hci_cmd_le_big_term_sync *cmd = data; struct bt_hci_rsp_le_big_term_sync rsp; struct btdev_conn *conn; struct le_big *big = queue_find(dev->le_big, match_big_handle, UINT_TO_PTR(cmd->handle)); memset(&rsp, 0, sizeof(rsp)); /* If the Host issues this command with a BIG_Handle that does not * exist, the Controller shall return the error code Unknown * Advertising Identifier (0x42). */ if (!big) { rsp.status = BT_HCI_ERR_UNKNOWN_ADVERTISING_ID; goto done; } rsp.status = BT_HCI_ERR_COMMAND_DISALLOWED; rsp.handle = cmd->handle; /* Cleanup existing connections */ while ((conn = queue_pop_head(big->bis))) { rsp.status = BT_HCI_ERR_SUCCESS; /* Unlink conn from remote BIS */ conn_unlink(conn, conn->link); conn_remove(conn); } done: if (rsp.status == BT_HCI_ERR_SUCCESS) { queue_remove(dev->le_big, big); le_big_free(big); } cmd_complete(dev, BT_HCI_CMD_LE_BIG_TERM_SYNC, &rsp, sizeof(rsp)); return 0; } static int cmd_req_peer_sca(struct btdev *dev, const void *data, uint8_t len) { /* TODO */ return -ENOTSUP; } static int cmd_setup_iso_path(struct btdev *dev, const void *data, uint8_t len) { const struct bt_hci_cmd_le_setup_iso_path *cmd = data; struct bt_hci_rsp_le_setup_iso_path rsp; struct btdev_conn *conn; memset(&rsp, 0, sizeof(rsp)); conn = queue_find(dev->conns, match_handle, UINT_TO_PTR(cpu_to_le16(cmd->handle))); if (!conn) { rsp.status = BT_HCI_ERR_UNKNOWN_CONN_ID; goto done; } /* Only support HCI or disabled paths */ if (cmd->path && cmd->path != 0xff) { rsp.status = BT_HCI_ERR_INVALID_PARAMETERS; goto done; } switch (cmd->direction) { case 0x00: dev->le_iso_path[0] = cmd->path; rsp.handle = cpu_to_le16(conn->handle); break; case 0x01: dev->le_iso_path[1] = cmd->path; rsp.handle = cpu_to_le16(conn->handle); break; default: rsp.status = BT_HCI_ERR_INVALID_PARAMETERS; } done: cmd_complete(dev, BT_HCI_CMD_LE_SETUP_ISO_PATH, &rsp, sizeof(rsp)); return 0; } static int cmd_remove_iso_path(struct btdev *dev, const void *data, uint8_t len) { const struct bt_hci_cmd_le_remove_iso_path *cmd = data; uint8_t status = BT_HCI_ERR_SUCCESS; struct btdev_conn *conn; conn = queue_find(dev->conns, match_handle, UINT_TO_PTR(cpu_to_le16(cmd->handle))); if (!conn) { status = BT_HCI_ERR_UNKNOWN_CONN_ID; goto done; } switch (cmd->direction) { case 0x00: dev->le_iso_path[0] = 0x00; break; case 0x01: dev->le_iso_path[1] = 0x00; break; default: status = BT_HCI_ERR_INVALID_PARAMETERS; } done: cmd_complete(dev, BT_HCI_CMD_LE_REMOVE_ISO_PATH, &status, sizeof(status)); return 0; } static int cmd_iso_tx_test(struct btdev *dev, const void *data, uint8_t len) { /* TODO */ return -ENOTSUP; } static int cmd_iso_rx_test(struct btdev *dev, const void *data, uint8_t len) { /* TODO */ return -ENOTSUP; } static int cmd_iso_read_test_counter(struct btdev *dev, const void *data, uint8_t len) { /* TODO */ return -ENOTSUP; } static int cmd_iso_test_end(struct btdev *dev, const void *data, uint8_t len) { /* TODO */ return -ENOTSUP; } static int cmd_set_host_feature(struct btdev *dev, const void *data, uint8_t len) { uint8_t status = BT_HCI_ERR_SUCCESS; cmd_complete(dev, BT_HCI_CMD_LE_SET_HOST_FEATURE, &status, sizeof(status)); return 0; } static int cmd_read_local_codecs_v2(struct btdev *dev, const void *data, uint8_t len) { struct { struct bt_hci_rsp_read_local_codecs rsp; struct bt_hci_codec codec[6]; uint8_t num_vnd_codecs; } pdu; memset(&pdu, 0, sizeof(pdu)); pdu.rsp.status = BT_HCI_ERR_SUCCESS; pdu.rsp.num_codecs = 0x06; pdu.codec[0].id = 0x00; pdu.codec[0].transport = BT_HCI_LOCAL_CODEC_BREDR_SCO; pdu.codec[1].id = 0x01; pdu.codec[1].transport = BT_HCI_LOCAL_CODEC_BREDR_SCO; pdu.codec[2].id = 0x02; pdu.codec[2].transport = BT_HCI_LOCAL_CODEC_BREDR_SCO; pdu.codec[3].id = 0x03; pdu.codec[3].transport = BT_HCI_LOCAL_CODEC_BREDR_SCO; pdu.codec[4].id = 0x04; pdu.codec[4].transport = BT_HCI_LOCAL_CODEC_BREDR_SCO; pdu.codec[5].id = 0x05; pdu.codec[5].transport = BT_HCI_LOCAL_CODEC_BREDR_SCO; cmd_complete(dev, BT_HCI_CMD_READ_LOCAL_CODECS_V2, &pdu, sizeof(pdu)); return 0; } static int cmd_read_local_codec_caps(struct btdev *dev, const void *data, uint8_t len) { const struct bt_hci_cmd_read_local_codec_caps *cmd = data; struct bt_hci_rsp_read_local_codec_caps rsp; memset(&rsp, 0, sizeof(rsp)); if (cmd->codec.id > 0x05) rsp.status = BT_HCI_ERR_INVALID_PARAMETERS; cmd_complete(dev, BT_HCI_CMD_READ_LOCAL_CODEC_CAPS, &rsp, sizeof(rsp)); return 0; } static int cmd_read_local_ctrl_delay(struct btdev *dev, const void *data, uint8_t len) { const struct bt_hci_cmd_read_local_ctrl_delay *cmd = data; struct bt_hci_rsp_read_local_ctrl_delay rsp; memset(&rsp, 0, sizeof(rsp)); if (cmd->codec.id > 0x05) rsp.status = BT_HCI_ERR_INVALID_PARAMETERS; cmd_complete(dev, BT_HCI_CMD_READ_LOCAL_CTRL_DELAY, &rsp, sizeof(rsp)); return 0; } static int cmd_config_data_path(struct btdev *dev, const void *data, uint8_t len) { const struct bt_hci_cmd_config_data_path *cmd = data; uint8_t status = BT_HCI_ERR_SUCCESS; if (cmd->id > 0x05) status = BT_HCI_ERR_INVALID_PARAMETERS; cmd_complete(dev, BT_HCI_CMD_CONFIG_DATA_PATH, &status, sizeof(status)); return 0; } #define CMD_LE_52 \ CMD(BT_HCI_CMD_LE_READ_BUFFER_SIZE_V2, cmd_read_size_v2, NULL), \ CMD(BT_HCI_CMD_LE_READ_ISO_TX_SYNC, cmd_read_iso_tx_sync, NULL), \ CMD(BT_HCI_CMD_LE_SET_EVENT_MASK, cmd_le_set_event_mask, NULL), \ CMD(BT_HCI_CMD_LE_SET_CIG_PARAMS, cmd_set_cig_params, NULL), \ CMD(BT_HCI_CMD_LE_SET_CIG_PARAMS_TEST, cmd_set_cig_params_test, NULL), \ CMD(BT_HCI_CMD_LE_CREATE_CIS, cmd_create_cis, \ cmd_create_cis_complete), \ CMD(BT_HCI_CMD_LE_REMOVE_CIG, cmd_remove_cig, NULL), \ CMD(BT_HCI_CMD_LE_ACCEPT_CIS, cmd_accept_cis, NULL), \ CMD(BT_HCI_CMD_LE_REJECT_CIS, cmd_reject_cis, NULL), \ CMD(BT_HCI_CMD_LE_CREATE_BIG, cmd_create_big, \ cmd_create_big_complete), \ CMD(BT_HCI_CMD_LE_CREATE_BIG_TEST, cmd_create_big_test, NULL), \ CMD(BT_HCI_CMD_LE_TERM_BIG, cmd_term_big, cmd_term_big_complete), \ CMD(BT_HCI_CMD_LE_BIG_CREATE_SYNC, cmd_big_create_sync, \ cmd_big_create_sync_complete), \ CMD(BT_HCI_CMD_LE_BIG_TERM_SYNC, cmd_big_term_sync, NULL), \ CMD(BT_HCI_CMD_LE_REQ_PEER_SCA, cmd_req_peer_sca, NULL), \ CMD(BT_HCI_CMD_LE_SETUP_ISO_PATH, cmd_setup_iso_path, NULL), \ CMD(BT_HCI_CMD_LE_REMOVE_ISO_PATH, cmd_remove_iso_path, NULL), \ CMD(BT_HCI_CMD_LE_ISO_TX_TEST, cmd_iso_tx_test, NULL), \ CMD(BT_HCI_CMD_LE_ISO_RX_TEST, cmd_iso_rx_test, NULL), \ CMD(BT_HCI_CMD_LE_ISO_READ_TEST_COUNTER, cmd_iso_read_test_counter, \ NULL), \ CMD(BT_HCI_CMD_LE_ISO_TEST_END, cmd_iso_test_end, NULL), \ CMD(BT_HCI_CMD_LE_SET_HOST_FEATURE, cmd_set_host_feature, NULL), \ CMD(BT_HCI_CMD_READ_LOCAL_CODECS_V2, cmd_read_local_codecs_v2, NULL), \ CMD(BT_HCI_CMD_READ_LOCAL_CODEC_CAPS, cmd_read_local_codec_caps, \ NULL), \ CMD(BT_HCI_CMD_READ_LOCAL_CTRL_DELAY, cmd_read_local_ctrl_delay, \ NULL), \ CMD(BT_HCI_CMD_CONFIG_DATA_PATH, cmd_config_data_path, NULL) static const struct btdev_cmd cmd_le_5_2[] = { CMD_COMMON_ALL, CMD_COMMON_BREDR_LE, CMD_LE, CMD_LE_50, CMD_LE_52, {} }; static void set_le_52_commands(struct btdev *btdev) { btdev->commands[41] |= 0x20; /* LE Read Buffer Size v2 */ btdev->commands[41] |= 0x40; /* LE Read ISO TX Sync */ btdev->commands[41] |= 0x80; /* LE Set CIG Parameters */ btdev->commands[42] |= 0x01; /* LE Set CIG Parameters Test */ btdev->commands[42] |= 0x02; /* LE Create CIS */ btdev->commands[42] |= 0x04; /* LE Remove CIG */ btdev->commands[42] |= 0x08; /* LE Accept CIS */ btdev->commands[42] |= 0x10; /* LE Reject CIS */ btdev->commands[42] |= 0x20; /* LE Create BIG */ btdev->commands[42] |= 0x40; /* LE Create BIG Test */ btdev->commands[42] |= 0x80; /* LE Terminate BIG */ btdev->commands[43] |= 0x01; /* LE BIG Create Sync */ btdev->commands[43] |= 0x02; /* LE BIG Terminate Sync */ btdev->commands[43] |= 0x04; /* LE Request Peer SCA */ btdev->commands[43] |= 0x08; /* LE Setup ISO Path */ btdev->commands[43] |= 0x10; /* LE Remove ISO Path */ btdev->commands[43] |= 0x20; /* LE ISO TX Test */ btdev->commands[43] |= 0x40; /* LE ISO RX Test */ btdev->commands[43] |= 0x80; /* LE ISO Read Test Counter */ btdev->commands[44] |= 0x01; /* LE ISO Test End */ btdev->commands[44] |= 0x02; /* LE ISO Set Host Feature */ btdev->commands[45] |= 0x04; /* Read Local Supported Codecs v2 */ btdev->commands[45] |= 0x08; /* Read Local Supported Codecs Caps */ btdev->commands[45] |= 0x10; /* Read Local Supported Ctrl Delay */ btdev->commands[45] |= 0x20; /* Config Data Path */ btdev->cmds = cmd_le_5_2; } static const struct btdev_cmd cmd_le[] = { CMD_COMMON_ALL, CMD_COMMON_BREDR_LE, CMD_LE, {} }; static void set_le_commands(struct btdev *btdev) { set_common_commands_all(btdev); set_common_commands_bredrle(btdev); btdev->commands[24] |= 0x20; /* Read LE Host Supported */ btdev->commands[24] |= 0x20; /* Write LE Host Supported */ btdev->commands[25] |= 0x01; /* LE Set Event Mask */ btdev->commands[25] |= 0x02; /* LE Read Buffer Size */ btdev->commands[25] |= 0x04; /* LE Read Local Features */ btdev->commands[25] |= 0x10; /* LE Set Random Address */ btdev->commands[25] |= 0x20; /* LE Set Adv Parameters */ btdev->commands[25] |= 0x40; /* LE Read Adv TX Power */ btdev->commands[25] |= 0x80; /* LE Set Adv Data */ btdev->commands[26] |= 0x01; /* LE Set Scan Response Data */ btdev->commands[26] |= 0x02; /* LE Set Adv Enable */ btdev->commands[26] |= 0x04; /* LE Set Scan Parameters */ btdev->commands[26] |= 0x08; /* LE Set Scan Enable */ btdev->commands[26] |= 0x10; /* LE Create Connection */ btdev->commands[26] |= 0x20; /* LE Create Connection Cancel */ btdev->commands[26] |= 0x40; /* LE Read Accept List Size */ btdev->commands[26] |= 0x80; /* LE Clear Accept List */ btdev->commands[27] |= 0x01; /* LE Add Device to Accept List */ btdev->commands[27] |= 0x02; /* LE Remove Device from Accept List */ btdev->commands[27] |= 0x04; /* LE Connection Update */ btdev->commands[27] |= 0x20; /* LE Read Remote Used Features */ btdev->commands[27] |= 0x40; /* LE Encrypt */ btdev->commands[27] |= 0x80; /* LE Rand */ btdev->commands[28] |= 0x01; /* LE Start Encryption */ btdev->commands[28] |= 0x02; /* LE Long Term Key Request Reply */ btdev->commands[28] |= 0x04; /* LE Long Term Key Request Neg Reply */ btdev->commands[28] |= 0x08; /* LE Read Supported States */ btdev->commands[28] |= 0x10; /* LE Receiver Test */ btdev->commands[28] |= 0x20; /* LE Transmitter Test */ btdev->commands[28] |= 0x40; /* LE Test End */ /* Extra LE commands for >= 4.1 adapters */ btdev->commands[33] |= 0x10; /* LE Remote Conn Param Req Reply */ btdev->commands[33] |= 0x20; /* LE Remote Conn Param Req Neg Reply */ /* Extra LE commands for >= 4.2 adapters */ btdev->commands[34] |= 0x02; /* LE Read Local P-256 Public Key */ btdev->commands[34] |= 0x04; /* LE Generate DHKey */ btdev->commands[34] |= 0x08; /* LE Add Device To Resolving List */ btdev->commands[34] |= 0x10; /* LE Remove Dev From Resolving List */ btdev->commands[34] |= 0x20; /* LE Clear Resolving List */ btdev->commands[34] |= 0x40; /* LE Read Resolving List Size */ btdev->commands[34] |= 0x80; /* LE Read Peer Resolvable Address */ btdev->commands[35] |= 0x01; /* LE Read Local Resolvable Address */ btdev->commands[35] |= 0x02; /* LE Set Address Resolution Enable */ btdev->commands[35] |= 0x04; /* LE Set RPA Timeout */ btdev->cmds = cmd_le; /* Extra LE commands for >= 5.0 adapters */ if (btdev->type >= BTDEV_TYPE_BREDRLE50) { set_le_50_commands(btdev); btdev->cmds = cmd_le_5_0; } /* Extra LE commands for >= 5.2 adapters */ if (btdev->type >= BTDEV_TYPE_BREDRLE52) { set_le_52_commands(btdev); btdev->cmds = cmd_le_5_2; } } static int cmd_set_event_mask_2(struct btdev *dev, const void *data, uint8_t len) { const struct bt_hci_cmd_set_event_mask_page2 *cmd = data; uint8_t status = BT_HCI_ERR_SUCCESS; memcpy(dev->event_mask_page2, cmd->mask, 8); cmd_complete(dev, BT_HCI_CMD_SET_EVENT_MASK_PAGE2, &status, sizeof(status)); return 0; } static int cmd_read_sync_train_params(struct btdev *dev, const void *data, uint8_t len) { struct bt_hci_rsp_read_sync_train_params rsp; memset(&rsp, 0, sizeof(rsp)); rsp.status = BT_HCI_ERR_SUCCESS; rsp.interval = cpu_to_le16(dev->sync_train_interval); rsp.timeout = cpu_to_le32(dev->sync_train_timeout); rsp.service_data = dev->sync_train_service_data; cmd_complete(dev, BT_HCI_CMD_READ_SYNC_TRAIN_PARAMS, &rsp, sizeof(rsp)); return 0; } static int cmd_read_sc_support(struct btdev *dev, const void *data, uint8_t len) { struct bt_hci_rsp_read_secure_conn_support rsp; memset(&rsp, 0, sizeof(rsp)); rsp.status = BT_HCI_ERR_SUCCESS; rsp.support = dev->secure_conn_support; cmd_complete(dev, BT_HCI_CMD_READ_SECURE_CONN_SUPPORT, &rsp, sizeof(rsp)); return 0; } static int cmd_write_sc_support(struct btdev *dev, const void *data, uint8_t len) { const struct bt_hci_cmd_write_secure_conn_support *cmd = data; uint8_t status = BT_HCI_ERR_SUCCESS; dev->secure_conn_support = cmd->support; cmd_complete(dev, BT_HCI_CMD_WRITE_SECURE_CONN_SUPPORT, &status, sizeof(status)); return 0; } static int cmd_read_auth_payload_timeout(struct btdev *dev, const void *data, uint8_t len) { /* TODO */ return -ENOTSUP; } static int cmd_write_auth_payload_timeout(struct btdev *dev, const void *data, uint8_t len) { /* TODO */ return -ENOTSUP; } static int cmd_read_local_oob_ext_data(struct btdev *dev, const void *data, uint8_t len) { struct bt_hci_rsp_read_local_oob_ext_data rsp; memset(&rsp, 0, sizeof(rsp)); rsp.status = BT_HCI_ERR_SUCCESS; cmd_complete(dev, BT_HCI_CMD_READ_LOCAL_OOB_EXT_DATA, &rsp, sizeof(rsp)); return 0; } #define BT_BREDR_LE \ CMD(BT_HCI_CMD_SET_EVENT_MASK_PAGE2, cmd_set_event_mask_2, NULL), \ CMD(BT_HCI_CMD_READ_SYNC_TRAIN_PARAMS, cmd_read_sync_train_params, \ NULL), \ CMD(BT_HCI_CMD_READ_SECURE_CONN_SUPPORT, cmd_read_sc_support, NULL), \ CMD(BT_HCI_CMD_WRITE_SECURE_CONN_SUPPORT, cmd_write_sc_support, NULL), \ CMD(BT_HCI_CMD_READ_AUTH_PAYLOAD_TIMEOUT, \ cmd_read_auth_payload_timeout, NULL), \ CMD(BT_HCI_CMD_WRITE_AUTH_PAYLOAD_TIMEOUT, \ cmd_write_auth_payload_timeout, NULL), \ CMD(BT_HCI_CMD_READ_LOCAL_OOB_EXT_DATA, \ cmd_read_local_oob_ext_data, NULL) static const struct btdev_cmd cmd_bredr_le[] = { CMD_COMMON_ALL, CMD_COMMON_BREDR_LE, CMD_COMMON_BREDR_20, CMD_BREDR, CMD_LE, CMD_LE_50, CMD_LE_52, BT_BREDR_LE, {} }; static void set_bredrle_commands(struct btdev *btdev) { set_bredr_commands(btdev); set_le_commands(btdev); /* Extra BR/EDR commands we want to only support for >= 4.0 * adapters. */ btdev->commands[22] |= 0x04; /* Set Event Mask Page 2 */ btdev->commands[31] |= 0x80; /* Read Sync Train Parameters */ btdev->commands[32] |= 0x04; /* Read Secure Connections Support */ btdev->commands[32] |= 0x08; /* Write Secure Connections Support */ btdev->commands[32] |= 0x10; /* Read Auth Payload Timeout */ btdev->commands[32] |= 0x20; /* Write Auth Payload Timeout */ btdev->commands[32] |= 0x40; /* Read Local OOB Extended Data */ btdev->cmds = cmd_bredr_le; } static void set_amp_commands(struct btdev *btdev) { set_common_commands_all(btdev); btdev->commands[22] |= 0x20; /* Read Local AMP Info */ } static void set_bredrle_features(struct btdev *btdev) { btdev->features[0] |= 0x04; /* Encryption */ btdev->features[0] |= 0x20; /* Role switch */ btdev->features[0] |= 0x80; /* Sniff mode */ btdev->features[1] |= 0x08; /* SCO link */ btdev->features[2] |= 0x08; /* Transparent SCO */ btdev->features[3] |= 0x40; /* RSSI with inquiry results */ btdev->features[3] |= 0x80; /* Extended SCO link */ btdev->features[4] |= 0x08; /* AFH capable peripheral */ btdev->features[4] |= 0x10; /* AFH classification peripheral */ btdev->features[4] |= 0x40; /* LE Supported */ btdev->features[5] |= 0x02; /* Sniff subrating */ btdev->features[5] |= 0x04; /* Pause encryption */ btdev->features[5] |= 0x08; /* AFH capable central */ btdev->features[5] |= 0x10; /* AFH classification central */ btdev->features[6] |= 0x01; /* Extended Inquiry Response */ btdev->features[6] |= 0x02; /* Simultaneous LE and BR/EDR */ btdev->features[6] |= 0x08; /* Secure Simple Pairing */ btdev->features[6] |= 0x10; /* Encapsulated PDU */ btdev->features[6] |= 0x20; /* Erroneous Data Reporting */ btdev->features[6] |= 0x40; /* Non-flushable Packet Boundary Flag */ btdev->features[7] |= 0x01; /* Link Supervision Timeout Event */ btdev->features[7] |= 0x02; /* Inquiry TX Power Level */ btdev->features[7] |= 0x80; /* Extended features */ if (btdev->type >= BTDEV_TYPE_BREDRLE50) { /* These BREDR features are added to test new configuration * command. If this is added above it will break existing tests */ btdev->features[0] |= 0x01; /* 3 slot Packets */ btdev->features[0] |= 0x02; /* 5 slot Packets */ btdev->features[3] |= 0x02; /* EDR ACL 2M mode */ btdev->features[3] |= 0x04; /* EDR ACL 3M mode */ btdev->features[4] |= 0x80; /* 3 slot EDR ACL packets */ btdev->features[5] |= 0x01; /* 5 slot EDR ACL packets */ btdev->le_features[0] |= 0x40; /* LE PRIVACY */ btdev->le_features[1] |= 0x01; /* LE 2M PHY */ btdev->le_features[1] |= 0x08; /* LE Coded PHY */ btdev->le_features[1] |= 0x10; /* LE EXT ADV */ } if (btdev->type >= BTDEV_TYPE_BREDRLE52) { btdev->le_features[1] |= 0x20; /* LE PER ADV */ btdev->le_features[3] |= 0x10; /* LE CIS Central */ btdev->le_features[3] |= 0x20; /* LE CIS Peripheral */ btdev->le_features[3] |= 0x40; /* LE ISO Broadcaster */ btdev->le_features[3] |= 0x80; /* LE Synchronized Receiver */ btdev->le_features[4] |= 0x01; /* LE ISO channels */ } btdev->feat_page_2[0] |= 0x01; /* CPB - Central Operation */ btdev->feat_page_2[0] |= 0x02; /* CPB - Peripheral Operation */ btdev->feat_page_2[0] |= 0x04; /* Synchronization Train */ btdev->feat_page_2[0] |= 0x08; /* Synchronization Scan */ btdev->feat_page_2[0] |= 0x10; /* Inquiry Response Notification */ btdev->feat_page_2[1] |= 0x01; /* Secure Connections */ btdev->feat_page_2[1] |= 0x02; /* Ping */ btdev->max_page = 2; } static void set_bredr_features(struct btdev *btdev) { btdev->features[0] |= 0x04; /* Encryption */ btdev->features[0] |= 0x20; /* Role switch */ btdev->features[0] |= 0x80; /* Sniff mode */ btdev->features[1] |= 0x08; /* SCO link */ btdev->features[3] |= 0x40; /* RSSI with inquiry results */ btdev->features[3] |= 0x80; /* Extended SCO link */ btdev->features[4] |= 0x08; /* AFH capable peripheral */ btdev->features[4] |= 0x10; /* AFH classification peripheral */ btdev->features[5] |= 0x02; /* Sniff subrating */ btdev->features[5] |= 0x04; /* Pause encryption */ btdev->features[5] |= 0x08; /* AFH capable central */ btdev->features[5] |= 0x10; /* AFH classification central */ btdev->features[6] |= 0x01; /* Extended Inquiry Response */ btdev->features[6] |= 0x08; /* Secure Simple Pairing */ btdev->features[6] |= 0x10; /* Encapsulated PDU */ btdev->features[6] |= 0x20; /* Erroneous Data Reporting */ btdev->features[6] |= 0x40; /* Non-flushable Packet Boundary Flag */ btdev->features[7] |= 0x01; /* Link Supervision Timeout Event */ btdev->features[7] |= 0x02; /* Inquiry TX Power Level */ btdev->features[7] |= 0x80; /* Extended features */ btdev->max_page = 1; } static void set_bredr20_features(struct btdev *btdev) { btdev->features[0] |= 0x04; /* Encryption */ btdev->features[0] |= 0x20; /* Role switch */ btdev->features[0] |= 0x80; /* Sniff mode */ btdev->features[1] |= 0x08; /* SCO link */ btdev->features[3] |= 0x40; /* RSSI with inquiry results */ btdev->features[3] |= 0x80; /* Extended SCO link */ btdev->features[4] |= 0x08; /* AFH capable peripheral */ btdev->features[4] |= 0x10; /* AFH classification peripheral */ btdev->features[5] |= 0x02; /* Sniff subrating */ btdev->features[5] |= 0x04; /* Pause encryption */ btdev->features[5] |= 0x08; /* AFH capable central */ btdev->features[5] |= 0x10; /* AFH classification central */ btdev->features[7] |= 0x80; /* Extended features */ btdev->max_page = 1; } static void set_le_features(struct btdev *btdev) { btdev->features[4] |= 0x20; /* BR/EDR Not Supported */ btdev->features[4] |= 0x40; /* LE Supported */ btdev->max_page = 1; btdev->le_features[0] |= 0x01; /* LE Encryption */ btdev->le_features[0] |= 0x02; /* Connection Parameters Request */ btdev->le_features[0] |= 0x08; /* Peripheral-initd Features Exchange */ } static void set_le_states(struct btdev *btdev) { /* Set all 41 bits as per Bluetooth 5.0 specification */ btdev->le_states[0] = 0xff; btdev->le_states[1] = 0xff; btdev->le_states[2] = 0xff; btdev->le_states[3] = 0xff; btdev->le_states[4] = 0xff; btdev->le_states[5] = 0x03; al_clear(btdev); rl_clear(btdev); btdev->le_rl_enable = 0x00; btdev->le_rl_timeout = 0x0384; /* 900 secs or 15 minutes */ } static void set_amp_features(struct btdev *btdev) { } struct btdev *btdev_create(enum btdev_type type, uint16_t id) { struct btdev *btdev; int index; unsigned int i; btdev = malloc(sizeof(*btdev)); if (!btdev) return NULL; memset(btdev, 0, sizeof(*btdev)); if (type == BTDEV_TYPE_BREDRLE || type == BTDEV_TYPE_LE || type == BTDEV_TYPE_BREDRLE50 || type == BTDEV_TYPE_BREDRLE52) { btdev->crypto = bt_crypto_new(); if (!btdev->crypto) { free(btdev); return NULL; } } btdev->type = type; btdev->id = id; btdev->manufacturer = 1521; btdev->revision = 0x0000; switch (btdev->type) { case BTDEV_TYPE_BREDRLE: case BTDEV_TYPE_BREDRLE50: case BTDEV_TYPE_BREDRLE52: btdev->version = 0x09; set_bredrle_features(btdev); set_bredrle_commands(btdev); set_le_states(btdev); break; case BTDEV_TYPE_BREDR: btdev->version = 0x05; set_bredr_features(btdev); set_bredr_commands(btdev); break; case BTDEV_TYPE_LE: btdev->version = 0x09; set_le_features(btdev); set_le_commands(btdev); set_le_states(btdev); break; case BTDEV_TYPE_AMP: btdev->version = 0x01; set_amp_features(btdev); set_amp_commands(btdev); break; case BTDEV_TYPE_BREDR20: btdev->version = 0x03; set_bredr20_features(btdev); set_bredr20_commands(btdev); break; } btdev->page_scan_interval = 0x0800; btdev->page_scan_window = 0x0012; btdev->page_scan_type = 0x00; btdev->sync_train_interval = 0x0080; btdev->sync_train_timeout = 0x0002ee00; btdev->sync_train_service_data = 0x00; btdev->acl_mtu = 192; btdev->acl_max_pkt = 1; btdev->sco_mtu = 72; btdev->sco_max_pkt = 1; btdev->iso_mtu = 251; btdev->iso_max_pkt = 1; for (i = 0; i < ARRAY_SIZE(btdev->le_cig); ++i) btdev->le_cig[i].params.cig_id = 0xff; btdev->country_code = 0x00; index = add_btdev(btdev); if (index < 0) { bt_crypto_unref(btdev->crypto); free(btdev); return NULL; } get_bdaddr(id, index, btdev->bdaddr); btdev->conns = queue_new(); btdev->le_ext_adv = queue_new(); btdev->le_per_adv = queue_new(); btdev->le_big = queue_new(); btdev->le_al_len = AL_SIZE; btdev->le_rl_len = RL_SIZE; return btdev; } void btdev_destroy(struct btdev *btdev) { if (!btdev) return; if (btdev->inquiry_id > 0) timeout_remove(btdev->inquiry_id); bt_crypto_unref(btdev->crypto); del_btdev(btdev); queue_destroy(btdev->conns, conn_remove); queue_destroy(btdev->le_ext_adv, le_ext_adv_free); queue_destroy(btdev->le_per_adv, free); queue_destroy(btdev->le_big, le_big_free); free(btdev); } bool btdev_set_debug(struct btdev *btdev, btdev_debug_func_t callback, void *user_data, btdev_destroy_func_t destroy) { if (!btdev) return false; if (btdev->debug_destroy) btdev->debug_destroy(btdev->debug_data); btdev->debug_callback = callback; btdev->debug_destroy = destroy; btdev->debug_data = user_data; return true; } const uint8_t *btdev_get_bdaddr(struct btdev *btdev) { return btdev->bdaddr; } bool btdev_set_bdaddr(struct btdev *btdev, const uint8_t *bdaddr) { if (!btdev || !bdaddr) return false; memcpy(btdev->bdaddr, bdaddr, sizeof(btdev->bdaddr)); return true; } uint8_t *btdev_get_features(struct btdev *btdev) { return btdev->features; } uint8_t *btdev_get_commands(struct btdev *btdev) { return btdev->commands; } uint8_t btdev_get_scan_enable(struct btdev *btdev) { return btdev->scan_enable; } uint8_t btdev_get_le_scan_enable(struct btdev *btdev) { return btdev->le_scan_enable; } const uint8_t *btdev_get_adv_addr(struct btdev *btdev, uint8_t handle) { struct le_ext_adv *ext_adv; /* Check if Ext Adv is already existed */ ext_adv = queue_find(btdev->le_ext_adv, match_ext_adv_handle, UINT_TO_PTR(handle)); if (!ext_adv) return NULL; return ext_adv_addr(btdev, ext_adv); } void btdev_set_le_states(struct btdev *btdev, const uint8_t *le_states) { memcpy(btdev->le_states, le_states, sizeof(btdev->le_states)); } void btdev_set_command_handler(struct btdev *btdev, btdev_command_func handler, void *user_data) { if (!btdev) return; btdev->command_handler = handler; btdev->command_data = user_data; } void btdev_set_send_handler(struct btdev *btdev, btdev_send_func handler, void *user_data) { if (!btdev) return; btdev->send_handler = handler; btdev->send_data = user_data; } static void num_completed_packets(struct btdev *btdev, struct btdev_conn *conn) { struct bt_hci_evt_num_completed_packets ncp; ncp.num_handles = 1; ncp.handle = cpu_to_le16(conn->handle); ncp.count = cpu_to_le16(1); send_event(btdev, BT_HCI_EVT_NUM_COMPLETED_PACKETS, &ncp, sizeof(ncp)); } static const struct btdev_cmd *run_cmd(struct btdev *btdev, const struct btdev_cmd *cmd, const void *data, uint8_t len) { uint8_t status = BT_HCI_ERR_UNKNOWN_COMMAND; int err; err = cmd->func(btdev, data, len); switch (err) { case 0: return cmd; case -ENOTSUP: status = BT_HCI_ERR_UNKNOWN_COMMAND; break; case -EINVAL: status = BT_HCI_ERR_INVALID_PARAMETERS; break; case -EPERM: status = BT_HCI_ERR_COMMAND_DISALLOWED; break; case -EEXIST: status = BT_HCI_ERR_CONN_ALREADY_EXISTS; break; case -ENOENT: status = BT_HCI_ERR_UNKNOWN_CONN_ID; break; default: status = BT_HCI_ERR_UNSPECIFIED_ERROR; break; } cmd_status(btdev, status, cmd->opcode); return NULL; } static const struct btdev_cmd *vnd_cmd(struct btdev *btdev, uint8_t op, const struct btdev_cmd *cmd, const void *data, uint8_t len) { uint8_t opcode = ((const uint8_t *)data)[0]; for (; cmd && cmd->func; cmd++) { if (cmd->opcode != opcode) continue; return run_cmd(btdev, cmd, data, len); } util_debug(btdev->debug_callback, btdev->debug_data, "Unsupported Vendor subcommand 0x%2.2x", opcode); cmd_status(btdev, BT_HCI_ERR_UNKNOWN_COMMAND, op); return NULL; } static const struct btdev_cmd *default_cmd(struct btdev *btdev, uint16_t opcode, const void *data, uint8_t len) { const struct btdev_cmd *cmd; if (btdev->emu_opcode == opcode) return vnd_cmd(btdev, opcode, btdev->emu_cmds, data, len); if (btdev->msft_opcode == opcode) return vnd_cmd(btdev, opcode, btdev->msft_cmds, data, len); for (cmd = btdev->cmds; cmd->func; cmd++) { if (cmd->opcode != opcode) continue; return run_cmd(btdev, cmd, data, len); } util_debug(btdev->debug_callback, btdev->debug_data, "Unsupported command 0x%4.4x", opcode); cmd_status(btdev, BT_HCI_ERR_UNKNOWN_COMMAND, opcode); return NULL; } struct btdev_callback { void (*function)(btdev_callback callback, uint8_t response, uint8_t status, const void *data, uint8_t len); void *user_data; uint16_t opcode; const void *data; uint8_t len; }; void btdev_command_response(btdev_callback callback, uint8_t response, uint8_t status, const void *data, uint8_t len) { callback->function(callback, response, status, data, len); } static void handler_callback(btdev_callback callback, uint8_t response, uint8_t status, const void *data, uint8_t len) { struct btdev *btdev = callback->user_data; const struct btdev_cmd *cmd; switch (response) { case BTDEV_RESPONSE_DEFAULT: if (!run_hooks(btdev, BTDEV_HOOK_PRE_CMD, callback->opcode, callback->data, callback->len)) return; cmd = default_cmd(btdev, callback->opcode, callback->data, callback->len); if (!cmd) return; if (!run_hooks(btdev, BTDEV_HOOK_PRE_EVT, callback->opcode, callback->data, callback->len)) return; if (cmd->complete) cmd->complete(btdev, callback->data, callback->len); break; case BTDEV_RESPONSE_COMMAND_STATUS: cmd_status(btdev, status, callback->opcode); break; case BTDEV_RESPONSE_COMMAND_COMPLETE: cmd_complete(btdev, callback->opcode, data, len); break; default: cmd_status(btdev, BT_HCI_ERR_UNKNOWN_COMMAND, callback->opcode); break; } } static void process_cmd(struct btdev *btdev, const void *data, uint16_t len) { struct btdev_callback callback; const struct bt_hci_cmd_hdr *hdr = data; const struct btdev_cmd *cmd; if (len < sizeof(*hdr)) return; callback.function = handler_callback; callback.user_data = btdev; callback.opcode = le16_to_cpu(hdr->opcode); callback.data = data + sizeof(*hdr); callback.len = hdr->plen; util_debug(btdev->debug_callback, btdev->debug_data, "command 0x%04x", callback.opcode); if (btdev->command_handler) btdev->command_handler(callback.opcode, callback.data, callback.len, &callback, btdev->command_data); else { if (!run_hooks(btdev, BTDEV_HOOK_PRE_CMD, callback.opcode, callback.data, callback.len)) return; cmd = default_cmd(btdev, callback.opcode, callback.data, callback.len); if (!cmd) return; if (!run_hooks(btdev, BTDEV_HOOK_PRE_EVT, callback.opcode, callback.data, callback.len)) return; if (cmd->complete) cmd->complete(btdev, callback.data, callback.len); } } static void send_acl(struct btdev *dev, const void *data, uint16_t len) { struct bt_hci_acl_hdr hdr; struct iovec iov[3]; struct btdev_conn *conn; uint8_t pkt_type = BT_H4_ACL_PKT; /* Packet type */ iov[0].iov_base = &pkt_type; iov[0].iov_len = sizeof(pkt_type); memcpy(&hdr, data, sizeof(hdr)); conn = queue_find(dev->conns, match_handle, UINT_TO_PTR(acl_handle(hdr.handle))); if (!conn) return; num_completed_packets(dev, conn); /* ACL_START_NO_FLUSH is only allowed from host to controller. * From controller to host this should be converted to ACL_START. */ if (acl_flags(hdr.handle) == ACL_START_NO_FLUSH) hdr.handle = acl_handle_pack(conn->handle, ACL_START); iov[1].iov_base = &hdr; iov[1].iov_len = sizeof(hdr); iov[2].iov_base = (void *) (data + sizeof(hdr)); iov[2].iov_len = len - sizeof(hdr); send_packet(conn->link->dev, iov, 3); } static void send_sco(struct btdev *dev, const void *data, uint16_t len) { struct bt_hci_sco_hdr *hdr; struct iovec iov[2]; struct btdev_conn *conn; uint8_t pkt_type = BT_H4_SCO_PKT; /* Packet type */ iov[0].iov_base = &pkt_type; iov[0].iov_len = sizeof(pkt_type); iov[1].iov_base = hdr = (void *) (data); iov[1].iov_len = len; conn = queue_find(dev->conns, match_handle, UINT_TO_PTR(acl_handle(hdr->handle))); if (!conn) return; if (dev->sco_flowctl) num_completed_packets(dev, conn); if (conn->link) send_packet(conn->link->dev, iov, 2); } static void send_iso(struct btdev *dev, const void *data, uint16_t len) { struct bt_hci_acl_hdr *hdr; struct iovec iov[2]; struct btdev_conn *conn; uint8_t pkt_type = BT_H4_ISO_PKT; /* Packet type */ iov[0].iov_base = &pkt_type; iov[0].iov_len = sizeof(pkt_type); iov[1].iov_base = hdr = (void *) (data); iov[1].iov_len = len; conn = queue_find(dev->conns, match_handle, UINT_TO_PTR(acl_handle(hdr->handle))); if (!conn) return; num_completed_packets(dev, conn); if (conn->link) send_packet(conn->link->dev, iov, 2); } void btdev_receive_h4(struct btdev *btdev, const void *data, uint16_t len) { uint8_t pkt_type; if (!btdev) return; if (len < 1) return; util_hexdump('>', data, len, btdev->debug_callback, btdev->debug_data); pkt_type = ((const uint8_t *) data)[0]; switch (pkt_type) { case BT_H4_CMD_PKT: process_cmd(btdev, data + 1, len - 1); break; case BT_H4_ACL_PKT: send_acl(btdev, data + 1, len - 1); break; case BT_H4_SCO_PKT: send_sco(btdev, data + 1, len - 1); break; case BT_H4_ISO_PKT: send_iso(btdev, data + 1, len - 1); break; default: util_debug(btdev->debug_callback, btdev->debug_data, "Unsupported packet 0x%2.2x", pkt_type); break; } } int btdev_add_hook(struct btdev *btdev, enum btdev_hook_type type, uint16_t opcode, btdev_hook_func handler, void *user_data) { int i; if (!btdev) return -1; if (get_hook_index(btdev, type, opcode) > 0) return -1; for (i = 0; i < MAX_HOOK_ENTRIES; i++) { if (btdev->hook_list[i] == NULL) { btdev->hook_list[i] = malloc(sizeof(struct hook)); if (btdev->hook_list[i] == NULL) return -1; btdev->hook_list[i]->handler = handler; btdev->hook_list[i]->user_data = user_data; btdev->hook_list[i]->opcode = opcode; btdev->hook_list[i]->type = type; return i; } } return -1; } bool btdev_del_hook(struct btdev *btdev, enum btdev_hook_type type, uint16_t opcode) { int i; if (!btdev) return false; for (i = 0; i < MAX_HOOK_ENTRIES; i++) { if (btdev->hook_list[i] == NULL) continue; if (btdev->hook_list[i]->type != type || btdev->hook_list[i]->opcode != opcode) continue; free(btdev->hook_list[i]); btdev->hook_list[i] = NULL; return true; } return false; } static int cmd_msft_read_features(struct btdev *dev, const void *data, uint8_t len) { struct msft_rsp_read_supported_features rsp; memset(&rsp, 0, sizeof(rsp)); rsp.status = BT_HCI_ERR_SUCCESS; rsp.subcmd = MSFT_SUBCMD_READ_SUPPORTED_FEATURES; rsp.features[0] = MSFT_MONITOR_BREDR_RSSI | MSFT_MONITOR_LE_RSSI | MSFT_MONITOR_LE_LEGACY_RSSI | MSFT_MONITOR_LE_ADV | MSFT_MONITOR_SSP_VALIDATION | MSFT_MONITOR_LE_ADV_CONTINUOS; cmd_complete(dev, dev->msft_opcode, &rsp, sizeof(rsp)); return 0; } static int cmd_msft_monitor_rssi(struct btdev *dev, const void *data, uint8_t len) { const struct msft_cmd_monitor_rssi *cmd = data; struct msft_rsp_monitor_rssi rsp; struct btdev_conn *conn; memset(&rsp, 0, sizeof(rsp)); rsp.status = BT_HCI_ERR_SUCCESS; rsp.subcmd = MSFT_SUBCMD_MONITOR_RSSI; conn = queue_find(dev->conns, match_handle, UINT_TO_PTR(le16_to_cpu(cmd->handle))); if (!conn) rsp.status = BT_HCI_ERR_UNKNOWN_CONN_ID; cmd_complete(dev, dev->msft_opcode, &rsp, sizeof(rsp)); return 0; } static int cmd_msft_cancel_monitor_rssi(struct btdev *dev, const void *data, uint8_t len) { const struct msft_cmd_cancel_monitor_rssi *cmd = data; struct msft_rsp_cancel_monitor_rssi rsp; struct btdev_conn *conn; memset(&rsp, 0, sizeof(rsp)); rsp.status = BT_HCI_ERR_SUCCESS; rsp.subcmd = MSFT_SUBCMD_CANCEL_MONITOR_RSSI; conn = queue_find(dev->conns, match_handle, UINT_TO_PTR(le16_to_cpu(cmd->handle))); if (!conn) rsp.status = BT_HCI_ERR_UNKNOWN_CONN_ID; cmd_complete(dev, dev->msft_opcode, &rsp, sizeof(rsp)); return 0; } static int cmd_msft_le_monitor_adv(struct btdev *dev, const void *data, uint8_t len) { const struct msft_cmd_le_monitor_adv *cmd = data; struct msft_rsp_le_monitor_adv rsp; static uint8_t handle; memset(&rsp, 0, sizeof(rsp)); rsp.status = BT_HCI_ERR_SUCCESS; rsp.subcmd = MSFT_SUBCMD_LE_MONITOR_ADV; switch (cmd->type) { case MSFT_LE_MONITOR_ADV_PATTERN: case MSFT_LE_MONITOR_ADV_UUID: case MSFT_LE_MONITOR_ADV_IRK: case MSFT_LE_MONITOR_ADV_ADDR: rsp.handle = handle++; break; default: rsp.status = BT_HCI_ERR_INVALID_PARAMETERS; break; } cmd_complete(dev, dev->msft_opcode, &rsp, sizeof(rsp)); return 0; } static int cmd_msft_le_cancel_monitor_adv(struct btdev *dev, const void *data, uint8_t len) { struct msft_rsp_le_cancel_monitor_adv rsp; memset(&rsp, 0, sizeof(rsp)); rsp.status = BT_HCI_ERR_SUCCESS; rsp.subcmd = MSFT_SUBCMD_LE_CANCEL_MONITOR_ADV; cmd_complete(dev, dev->msft_opcode, &rsp, sizeof(rsp)); return 0; } static int cmd_msft_le_monitor_adv_enable(struct btdev *dev, const void *data, uint8_t len) { struct msft_rsp_le_cancel_monitor_adv rsp; memset(&rsp, 0, sizeof(rsp)); rsp.status = BT_HCI_ERR_SUCCESS; rsp.subcmd = MSFT_SUBCMD_LE_MONITOR_ADV_ENABLE; cmd_complete(dev, dev->msft_opcode, &rsp, sizeof(rsp)); return 0; } static int cmd_msft_read_abs_rssi(struct btdev *dev, const void *data, uint8_t len) { struct msft_rsp_read_abs_rssi rsp; memset(&rsp, 0, sizeof(rsp)); rsp.status = BT_HCI_ERR_SUCCESS; rsp.subcmd = MSFT_SUBCMD_READ_ABS_RSSI; cmd_complete(dev, dev->msft_opcode, &rsp, sizeof(rsp)); return 0; } #define CMD_MSFT \ CMD(MSFT_SUBCMD_READ_SUPPORTED_FEATURES, cmd_msft_read_features, \ NULL), \ CMD(MSFT_SUBCMD_MONITOR_RSSI, cmd_msft_monitor_rssi, NULL), \ CMD(MSFT_SUBCMD_CANCEL_MONITOR_RSSI, cmd_msft_cancel_monitor_rssi, \ NULL), \ CMD(MSFT_SUBCMD_LE_MONITOR_ADV, cmd_msft_le_monitor_adv, NULL), \ CMD(MSFT_SUBCMD_LE_CANCEL_MONITOR_ADV, cmd_msft_le_cancel_monitor_adv, \ NULL), \ CMD(MSFT_SUBCMD_LE_MONITOR_ADV_ENABLE, cmd_msft_le_monitor_adv_enable, \ NULL), \ CMD(MSFT_SUBCMD_READ_ABS_RSSI, cmd_msft_read_abs_rssi, NULL) static const struct btdev_cmd cmd_msft[] = { CMD_MSFT, {} }; int btdev_set_msft_opcode(struct btdev *btdev, uint16_t opcode) { if (!btdev) return -EINVAL; switch (btdev->type) { case BTDEV_TYPE_BREDRLE: case BTDEV_TYPE_BREDRLE50: case BTDEV_TYPE_BREDRLE52: btdev->msft_opcode = opcode; btdev->msft_cmds = cmd_msft; return 0; case BTDEV_TYPE_BREDR: case BTDEV_TYPE_LE: case BTDEV_TYPE_AMP: case BTDEV_TYPE_BREDR20: default: return -ENOTSUP; } } int btdev_set_aosp_capable(struct btdev *btdev, bool enable) { if (!btdev) return -EINVAL; btdev->aosp_capable = enable; return 0; } static int cmd_emu_test_event(struct btdev *dev, const void *data, uint8_t len) { const struct emu_cmd_test_event *cmd = data; uint8_t status = BT_HCI_ERR_SUCCESS; if (len < sizeof(*cmd)) { status = BT_HCI_ERR_INVALID_PARAMETERS; goto done; } send_event(dev, cmd->evt, cmd->data, len - sizeof(*cmd)); done: cmd_complete(dev, dev->emu_opcode, &status, sizeof(status)); return 0; } #define CMD_EMU \ CMD(EMU_SUBCMD_TEST_EVENT, cmd_emu_test_event, NULL) static const struct btdev_cmd cmd_emu[] = { CMD_EMU, {} }; int btdev_set_emu_opcode(struct btdev *btdev, uint16_t opcode) { if (!btdev) return -EINVAL; switch (btdev->type) { case BTDEV_TYPE_BREDRLE: case BTDEV_TYPE_BREDRLE50: case BTDEV_TYPE_BREDRLE52: btdev->emu_opcode = opcode; btdev->emu_cmds = cmd_emu; return 0; case BTDEV_TYPE_BREDR: case BTDEV_TYPE_LE: case BTDEV_TYPE_AMP: case BTDEV_TYPE_BREDR20: default: return -ENOTSUP; } } bluez-5.82/emulator/PaxHeaders/btdev.h0000644000000000000000000000005014766002272014761 xustar0020 atime=1743515578 20 ctime=1743591278 bluez-5.82/emulator/btdev.h0000644000000000000000000000624514766002272014451 0ustar00rootroot/* SPDX-License-Identifier: LGPL-2.1-or-later */ /* * * BlueZ - Bluetooth protocol stack for Linux * * Copyright (C) 2011-2012 Intel Corporation * Copyright (C) 2004-2010 Marcel Holtmann * * */ #include #include #define BTDEV_RESPONSE_DEFAULT 0 #define BTDEV_RESPONSE_COMMAND_STATUS 1 #define BTDEV_RESPONSE_COMMAND_COMPLETE 2 typedef struct btdev_callback * btdev_callback; void btdev_command_response(btdev_callback callback, uint8_t response, uint8_t status, const void *data, uint8_t len); #define btdev_command_default(callback) \ btdev_command_response(callback, \ BTDEV_RESPONSE_DEFAULT, 0x00, NULL, 0); #define btdev_command_status(callback, status) \ btdev_command_response(callback, \ BTDEV_RESPONSE_COMMAND_STATUS, status, NULL, 0); #define btdev_command_complete(callback, data, len) \ btdev_command_response(callback, \ BTDEV_RESPONSE_COMMAND_COMPLETE, 0x00, data, len); typedef void (*btdev_command_func) (uint16_t opcode, const void *data, uint8_t len, btdev_callback callback, void *user_data); typedef void (*btdev_send_func) (const struct iovec *iov, int iovlen, void *user_data); typedef bool (*btdev_hook_func) (const void *data, uint16_t len, void *user_data); enum btdev_type { BTDEV_TYPE_BREDRLE, BTDEV_TYPE_BREDR, BTDEV_TYPE_LE, BTDEV_TYPE_AMP, BTDEV_TYPE_BREDR20, BTDEV_TYPE_BREDRLE50, BTDEV_TYPE_BREDRLE52, }; enum btdev_hook_type { BTDEV_HOOK_PRE_CMD, BTDEV_HOOK_POST_CMD, BTDEV_HOOK_PRE_EVT, BTDEV_HOOK_POST_EVT, }; struct btdev; struct btdev *btdev_create(enum btdev_type type, uint16_t id); void btdev_destroy(struct btdev *btdev); typedef void (*btdev_debug_func_t)(const char *str, void *user_data); typedef void (*btdev_destroy_func_t)(void *user_data); bool btdev_set_debug(struct btdev *btdev, btdev_debug_func_t callback, void *user_data, btdev_destroy_func_t destroy); const uint8_t *btdev_get_bdaddr(struct btdev *btdev); bool btdev_set_bdaddr(struct btdev *btdev, const uint8_t *bdaddr); uint8_t *btdev_get_features(struct btdev *btdev); uint8_t *btdev_get_commands(struct btdev *btdev); uint8_t btdev_get_scan_enable(struct btdev *btdev); uint8_t btdev_get_le_scan_enable(struct btdev *btdev); const uint8_t *btdev_get_adv_addr(struct btdev *btdev, uint8_t handle); void btdev_set_le_states(struct btdev *btdev, const uint8_t *le_states); void btdev_set_al_len(struct btdev *btdev, uint8_t len); void btdev_set_rl_len(struct btdev *btdev, uint8_t len); void btdev_set_command_handler(struct btdev *btdev, btdev_command_func handler, void *user_data); void btdev_set_send_handler(struct btdev *btdev, btdev_send_func handler, void *user_data); void btdev_receive_h4(struct btdev *btdev, const void *data, uint16_t len); int btdev_add_hook(struct btdev *btdev, enum btdev_hook_type type, uint16_t opcode, btdev_hook_func handler, void *user_data); bool btdev_del_hook(struct btdev *btdev, enum btdev_hook_type type, uint16_t opcode); int btdev_set_msft_opcode(struct btdev *btdev, uint16_t opcode); int btdev_set_aosp_capable(struct btdev *btdev, bool enable); int btdev_set_emu_opcode(struct btdev *btdev, uint16_t opcode); bluez-5.82/emulator/PaxHeaders/main.c0000644000000000000000000000005014766002272014574 xustar0020 atime=1743515578 20 ctime=1743591281 bluez-5.82/emulator/main.c0000644000000000000000000001142514766002272014260 0ustar00rootroot// SPDX-License-Identifier: LGPL-2.1-or-later /* * * BlueZ - Bluetooth protocol stack for Linux * * Copyright (C) 2011-2014 Intel Corporation * Copyright (C) 2004-2010 Marcel Holtmann * * */ #ifdef HAVE_CONFIG_H #include #endif #include #include #include #include #include #include "src/shared/mainloop.h" #include "src/shared/util.h" #include "serial.h" #include "server.h" #include "btdev.h" #include "vhci.h" #include "le.h" static void signal_callback(int signum, void *user_data) { switch (signum) { case SIGINT: case SIGTERM: mainloop_quit(); break; } } static void usage(void) { printf("btvirt - Bluetooth emulator\n" "Usage:\n"); printf("\tbtvirt [options]\n"); printf("options:\n" "\t-d Enable debug\n" "\t-S Create local serial port\n" "\t-s Create local server sockets\n" "\t-l[num] Number of local controllers\n" "\t-L Create LE only controller\n" "\t-U[num] Number of test LE controllers\n" "\t-B Create BR/EDR only controller\n" "\t-A Create AMP controller\n" "\t-T[num] Number of test AMP controllers\n" "\t-h, --help Show help options\n"); } static const struct option main_options[] = { { "debug", no_argument, NULL, 'd' }, { "serial", no_argument, NULL, 'S' }, { "server", no_argument, NULL, 's' }, { "local", optional_argument, NULL, 'l' }, { "le", no_argument, NULL, 'L' }, { "bredr", no_argument, NULL, 'B' }, { "amp", no_argument, NULL, 'A' }, { "letest", optional_argument, NULL, 'U' }, { "version", no_argument, NULL, 'v' }, { "help", no_argument, NULL, 'h' }, { } }; static void vhci_debug(const char *str, void *user_data) { int i = PTR_TO_UINT(user_data); printf("vhci%u: %s\n", i, str); } int main(int argc, char *argv[]) { struct server *server1; struct server *server2; struct server *server3; struct server *server4; struct server *server5; bool debug_enabled = false; bool server_enabled = false; bool serial_enabled = false; int letest_count = 0; int vhci_count = 0; enum btdev_type type = BTDEV_TYPE_BREDRLE52; int i; mainloop_init(); for (;;) { int opt; opt = getopt_long(argc, argv, "dSsl::LBAU::T::vh", main_options, NULL); if (opt < 0) break; switch (opt) { case 'd': debug_enabled = true; break; case 'S': serial_enabled = true; break; case 's': server_enabled = true; break; case 'l': if (optarg) vhci_count = atoi(optarg); else vhci_count = 1; break; case 'L': type = BTDEV_TYPE_LE; break; case 'B': type = BTDEV_TYPE_BREDR; break; case 'A': type = BTDEV_TYPE_AMP; break; case 'U': if (optarg) letest_count = atoi(optarg); else letest_count = 1; break; case 'v': printf("%s\n", VERSION); return EXIT_SUCCESS; case 'h': usage(); return EXIT_SUCCESS; default: return EXIT_FAILURE; } } if (letest_count < 1 && vhci_count < 1 && !server_enabled && !serial_enabled) { fprintf(stderr, "No emulator specified\n"); return EXIT_FAILURE; } printf("Bluetooth emulator ver %s\n", VERSION); for (i = 0; i < letest_count; i++) { struct bt_le *le; le = bt_le_new(); if (!le) { fprintf(stderr, "Failed to create LE controller\n"); return EXIT_FAILURE; } } for (i = 0; i < vhci_count; i++) { struct vhci *vhci; vhci = vhci_open(type); if (!vhci) { fprintf(stderr, "Failed to open Virtual HCI device\n"); return EXIT_FAILURE; } if (debug_enabled) vhci_set_debug(vhci, vhci_debug, UINT_TO_PTR(i), NULL); vhci_set_emu_opcode(vhci, 0xfc10); vhci_set_msft_opcode(vhci, 0xfc1e); } if (serial_enabled) { struct serial *serial; serial = serial_open(SERIAL_TYPE_BREDRLE); if (!serial) fprintf(stderr, "Failed to open serial emulation\n"); } if (server_enabled) { server1 = server_open_unix(SERVER_TYPE_BREDRLE, "/tmp/bt-server-bredrle"); if (!server1) fprintf(stderr, "Failed to open BR/EDR/LE server\n"); server2 = server_open_unix(SERVER_TYPE_BREDR, "/tmp/bt-server-bredr"); if (!server2) fprintf(stderr, "Failed to open BR/EDR server\n"); server3 = server_open_unix(SERVER_TYPE_AMP, "/tmp/bt-server-amp"); if (!server3) fprintf(stderr, "Failed to open AMP server\n"); server4 = server_open_unix(SERVER_TYPE_LE, "/tmp/bt-server-le"); if (!server4) fprintf(stderr, "Failed to open LE server\n"); server5 = server_open_unix(SERVER_TYPE_MONITOR, "/tmp/bt-server-mon"); if (!server5) fprintf(stderr, "Failed to open monitor server\n"); } return mainloop_run_with_signal(signal_callback, NULL); } bluez-5.82/emulator/PaxHeaders/le.c0000644000000000000000000000005014214376354014253 xustar0020 atime=1743516867 20 ctime=1743591281 bluez-5.82/emulator/le.c0000644000000000000000000016237314214376354013750 0ustar00rootroot// SPDX-License-Identifier: LGPL-2.1-or-later /* * * BlueZ - Bluetooth protocol stack for Linux * * Copyright (C) 2011-2012 Intel Corporation * Copyright (C) 2004-2010 Marcel Holtmann * * */ #ifdef HAVE_CONFIG_H #include #endif #include #include #include #include #include #include #include #include #include "lib/bluetooth.h" #include "lib/hci.h" #include "src/shared/util.h" #include "src/shared/crypto.h" #include "src/shared/ecc.h" #include "src/shared/mainloop.h" #include "monitor/bt.h" #include "phy.h" #include "le.h" #define ACCEPT_LIST_SIZE 16 #define RESOLV_LIST_SIZE 16 #define SCAN_CACHE_SIZE 64 #define DEFAULT_TX_LEN 0x001b #define DEFAULT_TX_TIME 0x0148 #define MAX_TX_LEN 0x00fb #define MAX_TX_TIME 0x0848 #define MAX_RX_LEN 0x00fb #define MAX_RX_TIME 0x0848 #define DEFAULT_ALL_PHYS 0x03 #define DEFAULT_TX_PHYS 0x00 #define DEFAULT_RX_PHYS 0x00 struct bt_peer { uint8_t addr_type; uint8_t addr[6]; }; struct bt_le { volatile int ref_count; int vhci_fd; struct bt_phy *phy; struct bt_crypto *crypto; int adv_timeout_id; int scan_timeout_id; bool scan_window_active; uint8_t scan_chan_idx; uint8_t event_mask[16]; uint16_t manufacturer; uint8_t commands[64]; uint8_t features[8]; uint8_t bdaddr[6]; uint8_t le_event_mask[8]; uint16_t le_mtu; uint8_t le_max_pkt; uint8_t le_features[8]; uint8_t le_random_addr[6]; uint16_t le_adv_min_interval; uint16_t le_adv_max_interval; uint8_t le_adv_type; uint8_t le_adv_own_addr_type; uint8_t le_adv_direct_addr_type; uint8_t le_adv_direct_addr[6]; uint8_t le_adv_channel_map; uint8_t le_adv_filter_policy; int8_t le_adv_tx_power; uint8_t le_adv_data_len; uint8_t le_adv_data[31]; uint8_t le_scan_rsp_data_len; uint8_t le_scan_rsp_data[31]; uint8_t le_adv_enable; uint8_t le_scan_type; uint16_t le_scan_interval; uint16_t le_scan_window; uint8_t le_scan_own_addr_type; uint8_t le_scan_filter_policy; uint8_t le_scan_enable; uint8_t le_scan_filter_dup; uint8_t le_conn_peer_addr_type; uint8_t le_conn_peer_addr[6]; uint8_t le_conn_own_addr_type; uint8_t le_conn_enable; uint8_t le_accept_list_size; uint8_t le_accept_list[ACCEPT_LIST_SIZE][7]; uint8_t le_states[8]; uint16_t le_default_tx_len; uint16_t le_default_tx_time; uint8_t le_local_sk256[32]; uint8_t le_resolv_list[RESOLV_LIST_SIZE][39]; uint8_t le_resolv_list_size; uint8_t le_resolv_enable; uint16_t le_resolv_timeout; uint8_t le_default_all_phys; uint8_t le_default_tx_phys; uint8_t le_default_rx_phys; struct bt_peer scan_cache[SCAN_CACHE_SIZE]; uint8_t scan_cache_count; }; static bool is_in_accept_list(struct bt_le *hci, uint8_t addr_type, const uint8_t addr[6]) { int i; for (i = 0; i < hci->le_accept_list_size; i++) { if (hci->le_accept_list[i][0] == addr_type && !memcmp(&hci->le_accept_list[i][1], addr, 6)) return true; } return false; } static void clear_accept_list(struct bt_le *hci) { int i; for (i = 0; i < hci->le_accept_list_size; i++) { hci->le_accept_list[i][0] = 0xff; memset(&hci->le_accept_list[i][1], 0, 6); } } static void resolve_peer_addr(struct bt_le *hci, uint8_t peer_addr_type, const uint8_t peer_addr[6], uint8_t *addr_type, uint8_t addr[6]) { int i; if (!hci->le_resolv_enable) goto done; if (peer_addr_type != 0x01) goto done; if ((peer_addr[5] & 0xc0) != 0x40) goto done; for (i = 0; i < hci->le_resolv_list_size; i++) { uint8_t local_hash[3]; if (hci->le_resolv_list[i][0] == 0xff) continue; bt_crypto_ah(hci->crypto, &hci->le_resolv_list[i][7], peer_addr + 3, local_hash); if (!memcmp(peer_addr, local_hash, 3)) { switch (hci->le_resolv_list[i][0]) { case 0x00: *addr_type = 0x02; break; case 0x01: *addr_type = 0x03; break; default: continue; } memcpy(addr, &hci->le_resolv_list[i][1], 6); return; } } done: *addr_type = peer_addr_type; memcpy(addr, peer_addr, 6); } static void clear_resolv_list(struct bt_le *hci) { int i; for (i = 0; i < hci->le_resolv_list_size; i++) { hci->le_resolv_list[i][0] = 0xff; memset(&hci->le_resolv_list[i][1], 0, 38); } } static void reset_defaults(struct bt_le *hci) { memset(hci->event_mask, 0, sizeof(hci->event_mask)); hci->event_mask[0] |= 0x10; /* Disconnection Complete */ hci->event_mask[0] |= 0x80; /* Encryption Change */ hci->event_mask[1] |= 0x08; /* Read Remote Version Information Complete */ hci->event_mask[1] |= 0x20; /* Command Complete */ hci->event_mask[1] |= 0x40; /* Command Status */ hci->event_mask[1] |= 0x80; /* Hardware Error */ hci->event_mask[2] |= 0x04; /* Number of Completed Packets */ hci->event_mask[3] |= 0x02; /* Data Buffer Overflow */ hci->event_mask[5] |= 0x80; /* Encryption Key Refresh Complete */ //hci->event_mask[7] |= 0x20; /* LE Meta Event */ hci->manufacturer = 0x003f; /* Bluetooth SIG (63) */ memset(hci->commands, 0, sizeof(hci->commands)); hci->commands[0] |= 0x20; /* Disconnect */ //hci->commands[2] |= 0x80; /* Read Remote Version Information */ hci->commands[5] |= 0x40; /* Set Event Mask */ hci->commands[5] |= 0x80; /* Reset */ //hci->commands[10] |= 0x04; /* Read Transmit Power Level */ hci->commands[14] |= 0x08; /* Read Local Version Information */ hci->commands[14] |= 0x10; /* Read Local Supported Commands */ hci->commands[14] |= 0x20; /* Read Local Supported Features */ hci->commands[14] |= 0x80; /* Read Buffer Size */ hci->commands[15] |= 0x02; /* Read BD ADDR */ //hci->commands[15] |= 0x20; /* Read RSSI */ hci->commands[22] |= 0x04; /* Set Event Mask Page 2 */ hci->commands[25] |= 0x01; /* LE Set Event Mask */ hci->commands[25] |= 0x02; /* LE Read Buffer Size */ hci->commands[25] |= 0x04; /* LE Read Local Supported Features */ hci->commands[25] |= 0x10; /* LE Set Random Address */ hci->commands[25] |= 0x20; /* LE Set Advertising Parameters */ hci->commands[25] |= 0x40; /* LE Read Advertising Channel TX Power */ hci->commands[25] |= 0x80; /* LE Set Advertising Data */ hci->commands[26] |= 0x01; /* LE Set Scan Response Data */ hci->commands[26] |= 0x02; /* LE Set Advertise Enable */ hci->commands[26] |= 0x04; /* LE Set Scan Parameters */ hci->commands[26] |= 0x08; /* LE Set Scan Enable */ hci->commands[26] |= 0x10; /* LE Create Connection */ hci->commands[26] |= 0x20; /* LE Create Connection Cancel */ hci->commands[26] |= 0x40; /* LE Read Accept List Size */ hci->commands[26] |= 0x80; /* LE Clear Accept List */ hci->commands[27] |= 0x01; /* LE Add Device To Accept List */ hci->commands[27] |= 0x02; /* LE Remove Device From Accept List */ //hci->commands[27] |= 0x04; /* LE Connection Update */ //hci->commands[27] |= 0x08; /* LE Set Host Channel Classification */ //hci->commands[27] |= 0x10; /* LE Read Channel Map */ //hci->commands[27] |= 0x20; /* LE Read Remote Used Features */ hci->commands[27] |= 0x40; /* LE Encrypt */ hci->commands[27] |= 0x80; /* LE Rand */ //hci->commands[28] |= 0x01; /* LE Start Encryption */ //hci->commands[28] |= 0x02; /* LE Long Term Key Request Reply */ //hci->commands[28] |= 0x04; /* LE Long Term Key Request Negative Reply */ hci->commands[28] |= 0x08; /* LE Read Supported States */ //hci->commands[28] |= 0x10; /* LE Receiver Test */ //hci->commands[28] |= 0x20; /* LE Transmitter Test */ //hci->commands[28] |= 0x40; /* LE Test End */ //hci->commands[33] |= 0x10; /* LE Remote Connection Parameter Request Reply */ //hci->commands[33] |= 0x20; /* LE Remote Connection Parameter Request Negative Reply */ hci->commands[33] |= 0x40; /* LE Set Data Length */ hci->commands[33] |= 0x80; /* LE Read Suggested Default Data Length */ hci->commands[34] |= 0x01; /* LE Write Suggested Default Data Length */ hci->commands[34] |= 0x02; /* LE Read Local P-256 Public Key */ hci->commands[34] |= 0x04; /* LE Generate DHKey */ hci->commands[34] |= 0x08; /* LE Add Device To Resolving List */ hci->commands[34] |= 0x10; /* LE Remove Device From Resolving List */ hci->commands[34] |= 0x20; /* LE Clear Resolving List */ hci->commands[34] |= 0x40; /* LE Read Resolving List Size */ hci->commands[34] |= 0x80; /* LE Read Peer Resolvable Address */ hci->commands[35] |= 0x01; /* LE Read Local Resolvable Address */ hci->commands[35] |= 0x02; /* LE Set Address Resolution Enable */ hci->commands[35] |= 0x04; /* LE Set Resolvable Private Address Timeout */ hci->commands[35] |= 0x08; /* LE Read Maximum Data Length */ hci->commands[35] |= 0x10; /* LE Read PHY */ hci->commands[35] |= 0x20; /* LE Set Default PHY */ hci->commands[35] |= 0x40; /* LE Set PHY */ //hci->commands[35] |= 0x80; /* LE Enhanced Receiver Test */ //hci->commands[36] |= 0x01; /* LE Enhanced Transmitter Test */ //hci->commands[36] |= 0x02; /* LE Set Advertising Set Random Address */ //hci->commands[36] |= 0x04; /* LE Set Extended Advertising Parameters */ //hci->commands[36] |= 0x08; /* LE Set Extended Advertising Data */ //hci->commands[36] |= 0x10; /* LE Set Extended Scan Response Data */ //hci->commands[36] |= 0x20; /* LE Set Extended Advertising Enable */ //hci->commands[36] |= 0x40; /* LE Read Maximum Advertising Data Length */ //hci->commands[36] |= 0x80; /* LE Read Number of Supported Advertising Sets */ //hci->commands[37] |= 0x01; /* LE Remove Advertising Set */ //hci->commands[37] |= 0x02; /* LE Clear Advertising Sets */ //hci->commands[37] |= 0x04; /* LE Set Periodic Advertising Parameters */ //hci->commands[37] |= 0x08; /* LE Set Periodic Advertising Data */ //hci->commands[37] |= 0x10; /* LE Set Periodic Advertising Enable */ //hci->commands[37] |= 0x20; /* LE Set Extended Scan Parameters */ //hci->commands[37] |= 0x40; /* LE Set Extended Scan Enable */ //hci->commands[37] |= 0x80; /* LE Extended Create Connection */ //hci->commands[38] |= 0x01; /* LE Periodic Advertising Create Sync */ //hci->commands[38] |= 0x02; /* LE Periodic Advertising Create Sync Cancel */ //hci->commands[38] |= 0x04; /* LE Periodic Advertising Terminate Sync */ //hci->commands[38] |= 0x08; /* LE Add Device To Periodic Advertiser List */ //hci->commands[38] |= 0x10; /* LE Remove Device From Periodic Advertiser List */ //hci->commands[38] |= 0x20; /* LE Clear Periodic Advertiser List */ //hci->commands[38] |= 0x40; /* LE Read Periodic Advertiser List Size */ //hci->commands[38] |= 0x80; /* LE Read Transmit Power */ //hci->commands[39] |= 0x01; /* LE Read RF Path Compensation */ //hci->commands[39] |= 0x02; /* LE Write RF Path Compensation */ //hci->commands[39] |= 0x04; /* LE Set Privacy Mode */ memset(hci->features, 0, sizeof(hci->features)); hci->features[4] |= 0x20; /* BR/EDR Not Supported */ hci->features[4] |= 0x40; /* LE Supported */ memset(hci->bdaddr, 0, sizeof(hci->bdaddr)); memset(hci->le_event_mask, 0, sizeof(hci->le_event_mask)); hci->le_event_mask[0] |= 0x01; /* LE Connection Complete */ hci->le_event_mask[0] |= 0x02; /* LE Advertising Report */ hci->le_event_mask[0] |= 0x04; /* LE Connection Update Complete */ hci->le_event_mask[0] |= 0x08; /* LE Read Remote Used Features Complete */ hci->le_event_mask[0] |= 0x10; /* LE Long Term Key Request */ //hci->le_event_mask[0] |= 0x20; /* LE Remote Connection Parameter Request */ //hci->le_event_mask[0] |= 0x40; /* LE Data Length Change */ //hci->le_event_mask[0] |= 0x80; /* LE Read Local P-256 Public Key Complete */ //hci->le_event_mask[1] |= 0x01; /* LE Generate DHKey Complete */ //hci->le_event_mask[1] |= 0x02; /* LE Enhanced Connection Complete */ //hci->le_event_mask[1] |= 0x04; /* LE Direct Advertising Report */ //hci->le_event_mask[1] |= 0x08; /* LE PHY Update Complete */ //hci->le_event_mask[1] |= 0x10; /* LE Extended Advertising Report */ //hci->le_event_mask[1] |= 0x20; /* LE Periodic Advertising Sync Established */ //hci->le_event_mask[1] |= 0x40; /* LE Periodic Advertising Report */ //hci->le_event_mask[1] |= 0x80; /* LE Periodic Advertising Sync Lost */ //hci->le_event_mask[2] |= 0x01; /* LE Extended Scan Timeout */ //hci->le_event_mask[2] |= 0x02; /* LE Extended Advertising Set Terminated */ //hci->le_event_mask[2] |= 0x04; /* LE Scan Request Received */ //hci->le_event_mask[2] |= 0x08; /* LE Channel Selection Algorithm */ hci->le_mtu = 64; hci->le_max_pkt = 1; memset(hci->le_features, 0, sizeof(hci->le_features)); hci->le_features[0] |= 0x01; /* LE Encryption */ //hci->le_features[0] |= 0x02; /* Connection Parameter Request Procedure */ //hci->le_features[0] |= 0x04; /* Extended Reject Indication */ //hci->le_features[0] |= 0x08; /* Peripheral-initd Features Exchange */ hci->le_features[0] |= 0x10; /* LE Ping */ hci->le_features[0] |= 0x20; /* LE Data Packet Length Extension */ hci->le_features[0] |= 0x40; /* LL Privacy */ hci->le_features[0] |= 0x80; /* Extended Scanner Filter Policies */ hci->le_features[1] |= 0x01; /* LE 2M PHY */ hci->le_features[1] |= 0x02; /* Stable Modulation Index - Transmitter */ hci->le_features[1] |= 0x04; /* Stable Modulation Index - Receiver */ hci->le_features[1] |= 0x08; /* LE Coded PHY */ //hci->le_features[1] |= 0x10; /* LE Extended Advertising */ //hci->le_features[1] |= 0x20; /* LE Periodic Advertising */ hci->le_features[1] |= 0x40; /* Channel Selection Algorithm #2 */ hci->le_features[1] |= 0x80; /* LE Power Class 1 */ hci->le_features[2] |= 0x01; /* Minimum Number of Used Channels Procedure */ memset(hci->le_random_addr, 0, sizeof(hci->le_random_addr)); hci->le_adv_min_interval = 0x0800; hci->le_adv_max_interval = 0x0800; hci->le_adv_type = 0x00; hci->le_adv_own_addr_type = 0x00; hci->le_adv_direct_addr_type = 0x00; memset(hci->le_adv_direct_addr, 0, 6); hci->le_adv_channel_map = 0x07; hci->le_adv_filter_policy = 0x00; hci->le_adv_tx_power = 0; memset(hci->le_adv_data, 0, sizeof(hci->le_adv_data)); hci->le_adv_data_len = 0; memset(hci->le_scan_rsp_data, 0, sizeof(hci->le_scan_rsp_data)); hci->le_scan_rsp_data_len = 0; hci->le_adv_enable = 0x00; hci->le_scan_type = 0x00; /* Passive Scanning */ hci->le_scan_interval = 0x0010; /* 10 ms */ hci->le_scan_window = 0x0010; /* 10 ms */ hci->le_scan_own_addr_type = 0x00; /* Public Device Address */ hci->le_scan_filter_policy = 0x00; hci->le_scan_enable = 0x00; hci->le_scan_filter_dup = 0x00; hci->le_conn_enable = 0x00; hci->le_accept_list_size = ACCEPT_LIST_SIZE; clear_accept_list(hci); memset(hci->le_states, 0, sizeof(hci->le_states)); hci->le_states[0] |= 0x01; /* Non-connectable Advertising */ hci->le_states[0] |= 0x02; /* Scannable Advertising */ hci->le_states[0] |= 0x04; /* Connectable Advertising */ hci->le_states[0] |= 0x08; /* High Duty Cycle Directed Advertising */ hci->le_states[0] |= 0x10; /* Passive Scanning */ hci->le_states[0] |= 0x20; /* Active Scanning */ hci->le_states[0] |= 0x40; /* Initiating + Conn (Central Role) */ hci->le_states[0] |= 0x80; /* Connection (Peripheral Role) */ hci->le_states[1] |= 0x01; /* Passive Scanning + * Non-connectable Advertising */ hci->le_default_tx_len = DEFAULT_TX_LEN; hci->le_default_tx_time = DEFAULT_TX_TIME; memset(hci->le_local_sk256, 0, sizeof(hci->le_local_sk256)); hci->le_resolv_list_size = RESOLV_LIST_SIZE; clear_resolv_list(hci); hci->le_resolv_enable = 0x00; hci->le_resolv_timeout = 0x0384; /* 900 secs or 15 minutes */ hci->le_default_all_phys = DEFAULT_ALL_PHYS; hci->le_default_tx_phys = DEFAULT_TX_PHYS; hci->le_default_rx_phys = DEFAULT_RX_PHYS; } static void clear_scan_cache(struct bt_le *hci) { memset(hci->scan_cache, 0, sizeof(hci->scan_cache)); hci->scan_cache_count = 0; } static bool add_to_scan_cache(struct bt_le *hci, uint8_t addr_type, const uint8_t addr[6]) { int i; for (i = 0; i < hci->scan_cache_count; i++) { if (hci->scan_cache[i].addr_type == addr_type && !memcmp(hci->scan_cache[i].addr, addr, 6)) return false; } if (hci->scan_cache_count >= SCAN_CACHE_SIZE) return true; hci->scan_cache[hci->scan_cache_count].addr_type = addr_type; memcpy(hci->scan_cache[hci->scan_cache_count].addr, addr, 6); hci->scan_cache_count++; return true; } static void send_event(struct bt_le *hci, uint8_t event, void *data, uint8_t size) { uint8_t type = BT_H4_EVT_PKT; struct bt_hci_evt_hdr hdr; struct iovec iov[3]; int iovcnt; hdr.evt = event; hdr.plen = size; iov[0].iov_base = &type; iov[0].iov_len = 1; iov[1].iov_base = &hdr; iov[1].iov_len = sizeof(hdr); if (size > 0) { iov[2].iov_base = data; iov[2].iov_len = size; iovcnt = 3; } else iovcnt = 2; if (writev(hci->vhci_fd, iov, iovcnt) < 0) fprintf(stderr, "Write to /dev/vhci failed (%m)\n"); } static void send_adv_pkt(struct bt_le *hci, uint8_t channel) { struct bt_phy_pkt_adv pkt; memset(&pkt, 0, sizeof(pkt)); pkt.chan_idx = channel; pkt.pdu_type = hci->le_adv_type; pkt.tx_addr_type = hci->le_adv_own_addr_type; switch (hci->le_adv_own_addr_type) { case 0x00: case 0x02: memcpy(pkt.tx_addr, hci->bdaddr, 6); break; case 0x01: case 0x03: memcpy(pkt.tx_addr, hci->le_random_addr, 6); break; } pkt.rx_addr_type = hci->le_adv_direct_addr_type; memcpy(pkt.rx_addr, hci->le_adv_direct_addr, 6); pkt.adv_data_len = hci->le_adv_data_len; pkt.scan_rsp_len = hci->le_scan_rsp_data_len; bt_phy_send_vector(hci->phy, BT_PHY_PKT_ADV, &pkt, sizeof(pkt), hci->le_adv_data, pkt.adv_data_len, hci->le_scan_rsp_data, pkt.scan_rsp_len); } static unsigned int get_adv_delay(void) { unsigned int val; /* The advertising delay is a pseudo-random value with a range * of 0 ms to 10 ms generated for each advertising event. */ if (util_getrandom(&val, sizeof(val), 0) < 0) { /* If it fails to get the random number, use a static value */ val = 5; } return (val % 11); } static void adv_timeout_callback(int id, void *user_data) { struct bt_le *hci = user_data; unsigned int msec, min_msec, max_msec; if (hci->le_adv_channel_map & 0x01) send_adv_pkt(hci, 37); if (hci->le_adv_channel_map & 0x02) send_adv_pkt(hci, 38); if (hci->le_adv_channel_map & 0x04) send_adv_pkt(hci, 39); min_msec = (hci->le_adv_min_interval * 625) / 1000; max_msec = (hci->le_adv_max_interval * 625) / 1000; msec = ((min_msec + max_msec) / 2) + get_adv_delay(); if (mainloop_modify_timeout(id, msec) < 0) { fprintf(stderr, "Setting advertising timeout failed\n"); hci->le_adv_enable = 0x00; } } static bool start_adv(struct bt_le *hci) { unsigned int msec; if (hci->adv_timeout_id >= 0) return false; msec = ((hci->le_adv_min_interval * 625) / 1000) + get_adv_delay(); hci->adv_timeout_id = mainloop_add_timeout(msec, adv_timeout_callback, hci, NULL); if (hci->adv_timeout_id < 0) return false; return true; } static bool stop_adv(struct bt_le *hci) { if (hci->adv_timeout_id < 0) return false; mainloop_remove_timeout(hci->adv_timeout_id); hci->adv_timeout_id = -1; return true; } static void scan_timeout_callback(int id, void *user_data) { struct bt_le *hci = user_data; unsigned int msec; if (hci->le_scan_window == hci->le_scan_interval || !hci->scan_window_active) { msec = (hci->le_scan_window * 625) / 1000; hci->scan_window_active = true; hci->scan_chan_idx++; if (hci->scan_chan_idx > 39) hci->scan_chan_idx = 37; } else { msec = ((hci->le_scan_interval - hci->le_scan_window) * 625) / 1000; hci->scan_window_active = false; } if (mainloop_modify_timeout(id, msec) < 0) { fprintf(stderr, "Setting scanning timeout failed\n"); hci->le_scan_enable = 0x00; hci->scan_window_active = false; } } static bool start_scan(struct bt_le *hci) { unsigned int msec; if (hci->scan_timeout_id >= 0) return false; msec = (hci->le_scan_window * 625) / 1000; hci->scan_timeout_id = mainloop_add_timeout(msec, scan_timeout_callback, hci, NULL); if (hci->scan_timeout_id < 0) return false; hci->scan_window_active = true; hci->scan_chan_idx = 37; return true; } static bool stop_scan(struct bt_le *hci) { if (hci->scan_timeout_id < 0) return false; mainloop_remove_timeout(hci->scan_timeout_id); hci->scan_timeout_id = -1; hci->scan_window_active = false; return true; } static void cmd_complete(struct bt_le *hci, uint16_t opcode, const void *data, uint8_t len) { struct bt_hci_evt_cmd_complete *cc; void *pkt_data; pkt_data = alloca(sizeof(*cc) + len); if (!pkt_data) return; cc = pkt_data; cc->ncmd = 0x01; cc->opcode = cpu_to_le16(opcode); if (len > 0) memcpy(pkt_data + sizeof(*cc), data, len); send_event(hci, BT_HCI_EVT_CMD_COMPLETE, pkt_data, sizeof(*cc) + len); } static void cmd_status(struct bt_le *hci, uint8_t status, uint16_t opcode) { struct bt_hci_evt_cmd_status cs; cs.status = status; cs.ncmd = 0x01; cs.opcode = cpu_to_le16(opcode); send_event(hci, BT_HCI_EVT_CMD_STATUS, &cs, sizeof(cs)); } static void le_meta_event(struct bt_le *hci, uint8_t event, void *data, uint8_t len) { void *pkt_data; if (!(hci->event_mask[7] & 0x20)) return; pkt_data = alloca(1 + len); if (!pkt_data) return; ((uint8_t *) pkt_data)[0] = event; if (len > 0) memcpy(pkt_data + 1, data, len); send_event(hci, BT_HCI_EVT_LE_META_EVENT, pkt_data, 1 + len); } static void cmd_disconnect(struct bt_le *hci, const void *data, uint8_t size) { cmd_status(hci, BT_HCI_ERR_UNKNOWN_CONN_ID, BT_HCI_CMD_DISCONNECT); } static void cmd_set_event_mask(struct bt_le *hci, const void *data, uint8_t size) { const struct bt_hci_cmd_set_event_mask *cmd = data; uint8_t status; memcpy(hci->event_mask, cmd->mask, 8); status = BT_HCI_ERR_SUCCESS; cmd_complete(hci, BT_HCI_CMD_SET_EVENT_MASK, &status, sizeof(status)); } static void cmd_reset(struct bt_le *hci, const void *data, uint8_t size) { uint8_t status; stop_adv(hci); stop_scan(hci); reset_defaults(hci); status = BT_HCI_ERR_SUCCESS; cmd_complete(hci, BT_HCI_CMD_RESET, &status, sizeof(status)); } static void cmd_set_event_mask_page2(struct bt_le *hci, const void *data, uint8_t size) { const struct bt_hci_cmd_set_event_mask_page2 *cmd = data; uint8_t status; memcpy(hci->event_mask + 8, cmd->mask, 8); status = BT_HCI_ERR_SUCCESS; cmd_complete(hci, BT_HCI_CMD_SET_EVENT_MASK_PAGE2, &status, sizeof(status)); } static void cmd_read_local_version(struct bt_le *hci, const void *data, uint8_t size) { struct bt_hci_rsp_read_local_version rsp; rsp.status = BT_HCI_ERR_SUCCESS; rsp.hci_ver = 0x09; rsp.hci_rev = cpu_to_le16(0x0000); rsp.lmp_ver = 0x09; rsp.manufacturer = cpu_to_le16(hci->manufacturer); rsp.lmp_subver = cpu_to_le16(0x0000); cmd_complete(hci, BT_HCI_CMD_READ_LOCAL_VERSION, &rsp, sizeof(rsp)); } static void cmd_read_local_commands(struct bt_le *hci, const void *data, uint8_t size) { struct bt_hci_rsp_read_local_commands rsp; rsp.status = BT_HCI_ERR_SUCCESS; memcpy(rsp.commands, hci->commands, 64); cmd_complete(hci, BT_HCI_CMD_READ_LOCAL_COMMANDS, &rsp, sizeof(rsp)); } static void cmd_read_local_features(struct bt_le *hci, const void *data, uint8_t size) { struct bt_hci_rsp_read_local_features rsp; rsp.status = BT_HCI_ERR_SUCCESS; memcpy(rsp.features, hci->features, 8); cmd_complete(hci, BT_HCI_CMD_READ_LOCAL_FEATURES, &rsp, sizeof(rsp)); } static void cmd_read_buffer_size(struct bt_le *hci, const void *data, uint8_t size) { struct bt_hci_rsp_read_buffer_size rsp; rsp.status = BT_HCI_ERR_SUCCESS; rsp.acl_mtu = cpu_to_le16(0x0000); rsp.sco_mtu = 0x00; rsp.acl_max_pkt = cpu_to_le16(0x0000); rsp.sco_max_pkt = cpu_to_le16(0x0000); cmd_complete(hci, BT_HCI_CMD_READ_BUFFER_SIZE, &rsp, sizeof(rsp)); } static void cmd_read_bd_addr(struct bt_le *hci, const void *data, uint8_t size) { struct bt_hci_rsp_read_bd_addr rsp; rsp.status = BT_HCI_ERR_SUCCESS; memcpy(rsp.bdaddr, hci->bdaddr, 6); cmd_complete(hci, BT_HCI_CMD_READ_BD_ADDR, &rsp, sizeof(rsp)); } static void cmd_le_set_event_mask(struct bt_le *hci, const void *data, uint8_t size) { const struct bt_hci_cmd_le_set_event_mask *cmd = data; uint8_t status; memcpy(hci->le_event_mask, cmd->mask, 8); status = BT_HCI_ERR_SUCCESS; cmd_complete(hci, BT_HCI_CMD_LE_SET_EVENT_MASK, &status, sizeof(status)); } static void cmd_le_read_buffer_size(struct bt_le *hci, const void *data, uint8_t size) { struct bt_hci_rsp_le_read_buffer_size rsp; rsp.status = BT_HCI_ERR_SUCCESS; rsp.le_mtu = cpu_to_le16(hci->le_mtu); rsp.le_max_pkt = hci->le_max_pkt; cmd_complete(hci, BT_HCI_CMD_LE_READ_BUFFER_SIZE, &rsp, sizeof(rsp)); } static void cmd_le_read_local_features(struct bt_le *hci, const void *data, uint8_t size) { struct bt_hci_rsp_le_read_local_features rsp; rsp.status = BT_HCI_ERR_SUCCESS; memcpy(rsp.features, hci->le_features, 8); cmd_complete(hci, BT_HCI_CMD_LE_READ_LOCAL_FEATURES, &rsp, sizeof(rsp)); } static void cmd_le_set_random_address(struct bt_le *hci, const void *data, uint8_t size) { const struct bt_hci_cmd_le_set_random_address *cmd = data; uint8_t status; memcpy(hci->le_random_addr, cmd->addr, 6); status = BT_HCI_ERR_SUCCESS; cmd_complete(hci, BT_HCI_CMD_LE_SET_RANDOM_ADDRESS, &status, sizeof(status)); } static void cmd_le_set_adv_parameters(struct bt_le *hci, const void *data, uint8_t size) { const struct bt_hci_cmd_le_set_adv_parameters *cmd = data; uint16_t min_interval, max_interval; uint8_t status; if (hci->le_adv_enable == 0x01) { cmd_status(hci, BT_HCI_ERR_COMMAND_DISALLOWED, BT_HCI_CMD_LE_SET_ADV_PARAMETERS); return; } min_interval = le16_to_cpu(cmd->min_interval); max_interval = le16_to_cpu(cmd->max_interval); /* Valid range for advertising type is 0x00 to 0x03 */ switch (cmd->type) { case 0x00: /* ADV_IND */ /* Range for advertising interval min is 0x0020 to 0x4000 */ if (min_interval < 0x0020 || min_interval > 0x4000) { cmd_status(hci, BT_HCI_ERR_INVALID_PARAMETERS, BT_HCI_CMD_LE_SET_ADV_PARAMETERS); return; } /* Range for advertising interval max is 0x0020 to 0x4000 */ if (max_interval < 0x0020 || max_interval > 0x4000) { cmd_status(hci, BT_HCI_ERR_INVALID_PARAMETERS, BT_HCI_CMD_LE_SET_ADV_PARAMETERS); return; } /* Advertising interval max shall be less or equal */ if (min_interval > max_interval) { cmd_status(hci, BT_HCI_ERR_INVALID_PARAMETERS, BT_HCI_CMD_LE_SET_ADV_PARAMETERS); return; } break; case 0x01: /* ADV_DIRECT_IND */ /* Range for direct address type is 0x00 to 0x01 */ if (cmd->direct_addr_type > 0x01) { cmd_status(hci, BT_HCI_ERR_INVALID_PARAMETERS, BT_HCI_CMD_LE_SET_ADV_PARAMETERS); return; } break; case 0x02: /* ADV_SCAN_IND */ case 0x03: /* ADV_NONCONN_IND */ /* Range for advertising interval min is 0x00a0 to 0x4000 */ if (min_interval < 0x00a0 || min_interval > 0x4000) { cmd_status(hci, BT_HCI_ERR_INVALID_PARAMETERS, BT_HCI_CMD_LE_SET_ADV_PARAMETERS); return; } /* Range for advertising interval max is 0x00a0 to 0x4000 */ if (max_interval < 0x00a0 || max_interval > 0x4000) { cmd_status(hci, BT_HCI_ERR_INVALID_PARAMETERS, BT_HCI_CMD_LE_SET_ADV_PARAMETERS); return; } /* Advertising interval min shall be less or equal */ if (min_interval > max_interval) { cmd_status(hci, BT_HCI_ERR_INVALID_PARAMETERS, BT_HCI_CMD_LE_SET_ADV_PARAMETERS); return; } break; default: cmd_status(hci, BT_HCI_ERR_INVALID_PARAMETERS, BT_HCI_CMD_LE_SET_ADV_PARAMETERS); return; } /* Valid range for own address type is 0x00 to 0x03 */ if (cmd->own_addr_type > 0x03) { cmd_status(hci, BT_HCI_ERR_INVALID_PARAMETERS, BT_HCI_CMD_LE_SET_ADV_PARAMETERS); return; } /* Valid range for advertising channel map is 0x01 to 0x07 */ if (cmd->channel_map < 0x01 || cmd->channel_map > 0x07) { cmd_status(hci, BT_HCI_ERR_INVALID_PARAMETERS, BT_HCI_CMD_LE_SET_ADV_PARAMETERS); return; } /* Valid range for advertising filter policy is 0x00 to 0x03 */ if (cmd->filter_policy > 0x03) { cmd_status(hci, BT_HCI_ERR_INVALID_PARAMETERS, BT_HCI_CMD_LE_SET_ADV_PARAMETERS); return; } hci->le_adv_min_interval = min_interval; hci->le_adv_max_interval = max_interval; hci->le_adv_type = cmd->type; hci->le_adv_own_addr_type = cmd->own_addr_type; hci->le_adv_direct_addr_type = cmd->direct_addr_type; memcpy(hci->le_adv_direct_addr, cmd->direct_addr, 6); hci->le_adv_channel_map = cmd->channel_map; hci->le_adv_filter_policy = cmd->filter_policy; status = BT_HCI_ERR_SUCCESS; cmd_complete(hci, BT_HCI_CMD_LE_SET_ADV_PARAMETERS, &status, sizeof(status)); } static void cmd_le_read_adv_tx_power(struct bt_le *hci, const void *data, uint8_t size) { struct bt_hci_rsp_le_read_adv_tx_power rsp; rsp.status = BT_HCI_ERR_SUCCESS; rsp.level = hci->le_adv_tx_power; cmd_complete(hci, BT_HCI_CMD_LE_READ_ADV_TX_POWER, &rsp, sizeof(rsp)); } static void cmd_le_set_adv_data(struct bt_le *hci, const void *data, uint8_t size) { const struct bt_hci_cmd_le_set_adv_data *cmd = data; uint8_t status; /* Valid range for advertising data length is 0x00 to 0x1f */ if (cmd->len > 0x1f) { cmd_status(hci, BT_HCI_ERR_INVALID_PARAMETERS, BT_HCI_CMD_LE_SET_ADV_DATA); return; } hci->le_adv_data_len = cmd->len; memcpy(hci->le_adv_data, cmd->data, 31); status = BT_HCI_ERR_SUCCESS; cmd_complete(hci, BT_HCI_CMD_LE_SET_ADV_DATA, &status, sizeof(status)); } static void cmd_le_set_scan_rsp_data(struct bt_le *hci, const void *data, uint8_t size) { const struct bt_hci_cmd_le_set_scan_rsp_data *cmd = data; uint8_t status; /* Valid range for scan response data length is 0x00 to 0x1f */ if (cmd->len > 0x1f) { cmd_status(hci, BT_HCI_ERR_INVALID_PARAMETERS, BT_HCI_CMD_LE_SET_SCAN_RSP_DATA); return; } hci->le_scan_rsp_data_len = cmd->len; memcpy(hci->le_scan_rsp_data, cmd->data, 31); status = BT_HCI_ERR_SUCCESS; cmd_complete(hci, BT_HCI_CMD_LE_SET_SCAN_RSP_DATA, &status, sizeof(status)); } static void cmd_le_set_adv_enable(struct bt_le *hci, const void *data, uint8_t size) { const struct bt_hci_cmd_le_set_adv_enable *cmd = data; uint8_t status; bool result; /* Valid range for advertising enable is 0x00 to 0x01 */ if (cmd->enable > 0x01) { cmd_status(hci, BT_HCI_ERR_INVALID_PARAMETERS, BT_HCI_CMD_LE_SET_ADV_ENABLE); return; } if (cmd->enable == hci->le_adv_enable) { cmd_status(hci, BT_HCI_ERR_COMMAND_DISALLOWED, BT_HCI_CMD_LE_SET_ADV_ENABLE); return; } if (cmd->enable == 0x01) result = start_adv(hci); else result = stop_adv(hci); if (!result) { cmd_status(hci, BT_HCI_ERR_UNSPECIFIED_ERROR, BT_HCI_CMD_LE_SET_ADV_ENABLE); return; } hci->le_adv_enable = cmd->enable; status = BT_HCI_ERR_SUCCESS; cmd_complete(hci, BT_HCI_CMD_LE_SET_ADV_ENABLE, &status, sizeof(status)); } static void cmd_le_set_scan_parameters(struct bt_le *hci, const void *data, uint8_t size) { const struct bt_hci_cmd_le_set_scan_parameters *cmd = data; uint16_t interval, window; uint8_t status; if (hci->le_scan_enable == 0x01) { cmd_status(hci, BT_HCI_ERR_COMMAND_DISALLOWED, BT_HCI_CMD_LE_SET_SCAN_PARAMETERS); return; } interval = le16_to_cpu(cmd->interval); window = le16_to_cpu(cmd->window); /* Valid range for scan type is 0x00 to 0x01 */ if (cmd->type > 0x01) { cmd_status(hci, BT_HCI_ERR_INVALID_PARAMETERS, BT_HCI_CMD_LE_SET_SCAN_PARAMETERS); return; } /* Valid range for scan interval is 0x0004 to 0x4000 */ if (interval < 0x0004 || interval > 0x4000) { cmd_status(hci, BT_HCI_ERR_INVALID_PARAMETERS, BT_HCI_CMD_LE_SET_SCAN_PARAMETERS); return; } /* Valid range for scan window is 0x0004 to 0x4000 */ if (window < 0x0004 || window > 0x4000) { cmd_status(hci, BT_HCI_ERR_INVALID_PARAMETERS, BT_HCI_CMD_LE_SET_SCAN_PARAMETERS); return; } /* Scan window shall be less or equal than scan interval */ if (window > interval) { cmd_status(hci, BT_HCI_ERR_INVALID_PARAMETERS, BT_HCI_CMD_LE_SET_SCAN_PARAMETERS); return; } /* Valid range for own address type is 0x00 to 0x03 */ if (cmd->own_addr_type > 0x03) { cmd_status(hci, BT_HCI_ERR_INVALID_PARAMETERS, BT_HCI_CMD_LE_SET_SCAN_PARAMETERS); return; } /* Valid range for scanning filter policy is 0x00 to 0x03 */ if (cmd->filter_policy > 0x03) { cmd_status(hci, BT_HCI_ERR_INVALID_PARAMETERS, BT_HCI_CMD_LE_SET_SCAN_PARAMETERS); return; } hci->le_scan_type = cmd->type; hci->le_scan_interval = interval; hci->le_scan_window = window; hci->le_scan_own_addr_type = cmd->own_addr_type; hci->le_scan_filter_policy = cmd->filter_policy; status = BT_HCI_ERR_SUCCESS; cmd_complete(hci, BT_HCI_CMD_LE_SET_SCAN_PARAMETERS, &status, sizeof(status)); } static void cmd_le_set_scan_enable(struct bt_le *hci, const void *data, uint8_t size) { const struct bt_hci_cmd_le_set_scan_enable *cmd = data; uint8_t status; bool result; /* Valid range for scan enable is 0x00 to 0x01 */ if (cmd->enable > 0x01) { cmd_status(hci, BT_HCI_ERR_INVALID_PARAMETERS, BT_HCI_CMD_LE_SET_SCAN_ENABLE); return; } /* Valid range for filter duplicates is 0x00 to 0x01 */ if (cmd->filter_dup > 0x01) { cmd_status(hci, BT_HCI_ERR_INVALID_PARAMETERS, BT_HCI_CMD_LE_SET_SCAN_ENABLE); return; } if (cmd->enable == hci->le_scan_enable) { cmd_status(hci, BT_HCI_ERR_COMMAND_DISALLOWED, BT_HCI_CMD_LE_SET_SCAN_ENABLE); return; } clear_scan_cache(hci); if (cmd->enable == 0x01) result = start_scan(hci); else result = stop_scan(hci); if (!result) { cmd_status(hci, BT_HCI_ERR_UNSPECIFIED_ERROR, BT_HCI_CMD_LE_SET_SCAN_ENABLE); return; } hci->le_scan_enable = cmd->enable; hci->le_scan_filter_dup = cmd->filter_dup; status = BT_HCI_ERR_SUCCESS; cmd_complete(hci, BT_HCI_CMD_LE_SET_SCAN_ENABLE, &status, sizeof(status)); } static void cmd_le_create_conn(struct bt_le *hci, const void *data, uint8_t size) { const struct bt_hci_cmd_le_create_conn *cmd = data; if (hci->le_conn_enable == 0x01) { cmd_status(hci, BT_HCI_ERR_COMMAND_DISALLOWED, BT_HCI_CMD_LE_CREATE_CONN); return; } /* Valid range for peer address type is 0x00 to 0x03 */ if (cmd->peer_addr_type > 0x03) { cmd_status(hci, BT_HCI_ERR_INVALID_PARAMETERS, BT_HCI_CMD_LE_CREATE_CONN); return; } /* Valid range for own address type is 0x00 to 0x03 */ if (cmd->own_addr_type > 0x03) { cmd_status(hci, BT_HCI_ERR_INVALID_PARAMETERS, BT_HCI_CMD_LE_CREATE_CONN); return; } hci->le_conn_peer_addr_type = cmd->peer_addr_type; memcpy(hci->le_conn_peer_addr, cmd->peer_addr, 6); hci->le_conn_own_addr_type = cmd->own_addr_type; hci->le_conn_enable = 0x01; cmd_status(hci, BT_HCI_ERR_SUCCESS, BT_HCI_CMD_LE_CREATE_CONN); } static void cmd_le_create_conn_cancel(struct bt_le *hci, const void *data, uint8_t size) { struct bt_hci_evt_le_conn_complete evt; uint8_t status; if (hci->le_conn_enable == 0x00) { cmd_status(hci, BT_HCI_ERR_COMMAND_DISALLOWED, BT_HCI_CMD_LE_CREATE_CONN_CANCEL); return; } hci->le_conn_enable = 0x00; status = BT_HCI_ERR_SUCCESS; cmd_complete(hci, BT_HCI_CMD_LE_CREATE_CONN_CANCEL, &status, sizeof(status)); evt.status = BT_HCI_ERR_UNKNOWN_CONN_ID; evt.handle = cpu_to_le16(0x0000); evt.role = 0x00; evt.peer_addr_type = 0x00; memset(evt.peer_addr, 0, 6); evt.interval = cpu_to_le16(0x0000); evt.latency = cpu_to_le16(0x0000); evt.supv_timeout = cpu_to_le16(0x0000); evt.clock_accuracy = 0x00; if (hci->le_event_mask[0] & 0x01) le_meta_event(hci, BT_HCI_EVT_LE_CONN_COMPLETE, &evt, sizeof(evt)); } static void cmd_le_read_accept_list_size(struct bt_le *hci, const void *data, uint8_t size) { struct bt_hci_rsp_le_read_accept_list_size rsp; rsp.status = BT_HCI_ERR_SUCCESS; rsp.size = hci->le_accept_list_size; cmd_complete(hci, BT_HCI_CMD_LE_READ_ACCEPT_LIST_SIZE, &rsp, sizeof(rsp)); } static void cmd_le_clear_accept_list(struct bt_le *hci, const void *data, uint8_t size) { uint8_t status; clear_accept_list(hci); status = BT_HCI_ERR_SUCCESS; cmd_complete(hci, BT_HCI_CMD_LE_CLEAR_ACCEPT_LIST, &status, sizeof(status)); } static void cmd_le_add_to_accept_list(struct bt_le *hci, const void *data, uint8_t size) { const struct bt_hci_cmd_le_add_to_accept_list *cmd = data; uint8_t status; bool exists = false; int i, pos = -1; /* Valid range for address type is 0x00 to 0x01 */ if (cmd->addr_type > 0x01) { cmd_status(hci, BT_HCI_ERR_INVALID_PARAMETERS, BT_HCI_CMD_LE_ADD_TO_ACCEPT_LIST); return; } for (i = 0; i < hci->le_accept_list_size; i++) { if (hci->le_accept_list[i][0] == cmd->addr_type && !memcmp(&hci->le_accept_list[i][1], cmd->addr, 6)) { exists = true; break; } else if (pos < 0 && hci->le_accept_list[i][0] == 0xff) pos = i; } if (exists) { cmd_status(hci, BT_HCI_ERR_UNSPECIFIED_ERROR, BT_HCI_CMD_LE_ADD_TO_ACCEPT_LIST); return; } if (pos < 0) { cmd_status(hci, BT_HCI_ERR_MEM_CAPACITY_EXCEEDED, BT_HCI_CMD_LE_ADD_TO_ACCEPT_LIST); return; } hci->le_accept_list[pos][0] = cmd->addr_type; memcpy(&hci->le_accept_list[pos][1], cmd->addr, 6); status = BT_HCI_ERR_SUCCESS; cmd_complete(hci, BT_HCI_CMD_LE_ADD_TO_ACCEPT_LIST, &status, sizeof(status)); } static void cmd_le_remove_from_accept_list(struct bt_le *hci, const void *data, uint8_t size) { const struct bt_hci_cmd_le_remove_from_accept_list *cmd = data; uint8_t status; int i, pos = -1; /* Valid range for address type is 0x00 to 0x01 */ if (cmd->addr_type > 0x01) { cmd_status(hci, BT_HCI_ERR_INVALID_PARAMETERS, BT_HCI_CMD_LE_REMOVE_FROM_ACCEPT_LIST); return; } for (i = 0; i < hci->le_accept_list_size; i++) { if (hci->le_accept_list[i][0] == cmd->addr_type && !memcmp(&hci->le_accept_list[i][1], cmd->addr, 6)) { pos = i; break; } } if (pos < 0) { cmd_status(hci, BT_HCI_ERR_INVALID_PARAMETERS, BT_HCI_CMD_LE_REMOVE_FROM_ACCEPT_LIST); return; } hci->le_accept_list[pos][0] = 0xff; memset(&hci->le_accept_list[pos][1], 0, 6); status = BT_HCI_ERR_SUCCESS; cmd_complete(hci, BT_HCI_CMD_LE_REMOVE_FROM_ACCEPT_LIST, &status, sizeof(status)); } static void cmd_le_encrypt(struct bt_le *hci, const void *data, uint8_t size) { const struct bt_hci_cmd_le_encrypt *cmd = data; struct bt_hci_rsp_le_encrypt rsp; if (!bt_crypto_e(hci->crypto, cmd->key, cmd->plaintext, rsp.data)) { cmd_status(hci, BT_HCI_ERR_COMMAND_DISALLOWED, BT_HCI_CMD_LE_ENCRYPT); return; } rsp.status = BT_HCI_ERR_SUCCESS; cmd_complete(hci, BT_HCI_CMD_LE_ENCRYPT, &rsp, sizeof(rsp)); } static void cmd_le_rand(struct bt_le *hci, const void *data, uint8_t size) { struct bt_hci_rsp_le_rand rsp; uint8_t value[8]; if (!bt_crypto_random_bytes(hci->crypto, value, 8)) { cmd_status(hci, BT_HCI_ERR_COMMAND_DISALLOWED, BT_HCI_CMD_LE_RAND); return; } rsp.status = BT_HCI_ERR_SUCCESS; memcpy(&rsp.number, value, 8); cmd_complete(hci, BT_HCI_CMD_LE_RAND, &rsp, sizeof(rsp)); } static void cmd_le_read_supported_states(struct bt_le *hci, const void *data, uint8_t size) { struct bt_hci_rsp_le_read_supported_states rsp; rsp.status = BT_HCI_ERR_SUCCESS; memcpy(rsp.states, hci->le_states, 8); cmd_complete(hci, BT_HCI_CMD_LE_READ_SUPPORTED_STATES, &rsp, sizeof(rsp)); } static void cmd_le_set_data_length(struct bt_le *hci, const void *data, uint8_t size) { const struct bt_hci_cmd_le_set_data_length *cmd = data; struct bt_hci_rsp_le_set_data_length rsp; uint16_t handle, tx_len, tx_time; handle = le16_to_cpu(cmd->handle); tx_len = le16_to_cpu(cmd->tx_len); tx_time = le16_to_cpu(cmd->tx_time); /* Valid range for connection handle is 0x0000 to 0x0eff */ if (handle > 0x0eff) { cmd_status(hci, BT_HCI_ERR_INVALID_PARAMETERS, BT_HCI_CMD_LE_SET_DATA_LENGTH); return; } /* Valid range for suggested max TX octets is 0x001b to 0x00fb */ if (tx_len < 0x001b || tx_len > 0x00fb) { cmd_status(hci, BT_HCI_ERR_INVALID_PARAMETERS, BT_HCI_CMD_LE_SET_DATA_LENGTH); return; } /* Valid range for suggested max TX time is 0x0148 to 0x0848 */ if (tx_time < 0x0148 || tx_time > 0x0848) { cmd_status(hci, BT_HCI_ERR_INVALID_PARAMETERS, BT_HCI_CMD_LE_SET_DATA_LENGTH); return; } /* Max TX len and time shall be less or equal supported */ if (tx_len > MAX_TX_LEN || tx_time > MAX_TX_TIME) { cmd_status(hci, BT_HCI_ERR_INVALID_PARAMETERS, BT_HCI_CMD_LE_SET_DATA_LENGTH); return; } rsp.status = BT_HCI_ERR_SUCCESS; rsp.handle = cpu_to_le16(handle); cmd_complete(hci, BT_HCI_CMD_LE_SET_DATA_LENGTH, &rsp, sizeof(rsp)); } static void cmd_le_read_default_data_length(struct bt_le *hci, const void *data, uint8_t size) { struct bt_hci_rsp_le_read_default_data_length rsp; rsp.status = BT_HCI_ERR_SUCCESS; rsp.tx_len = cpu_to_le16(hci->le_default_tx_len); rsp.tx_time = cpu_to_le16(hci->le_default_tx_time); cmd_complete(hci, BT_HCI_CMD_LE_READ_DEFAULT_DATA_LENGTH, &rsp, sizeof(rsp)); } static void cmd_le_write_default_data_length(struct bt_le *hci, const void *data, uint8_t size) { const struct bt_hci_cmd_le_write_default_data_length *cmd = data; uint16_t tx_len, tx_time; uint8_t status; tx_len = le16_to_cpu(cmd->tx_len); tx_time = le16_to_cpu(cmd->tx_time); /* Valid range for suggested max TX octets is 0x001b to 0x00fb */ if (tx_len < 0x001b || tx_len > 0x00fb) { cmd_status(hci, BT_HCI_ERR_INVALID_PARAMETERS, BT_HCI_CMD_LE_WRITE_DEFAULT_DATA_LENGTH); return; } /* Valid range for suggested max TX time is 0x0148 to 0x0848 */ if (tx_time < 0x0148 || tx_time > 0x0848) { cmd_status(hci, BT_HCI_ERR_INVALID_PARAMETERS, BT_HCI_CMD_LE_WRITE_DEFAULT_DATA_LENGTH); return; } /* Suggested max TX len and time shall be less or equal supported */ if (tx_len > MAX_TX_LEN || tx_time > MAX_TX_TIME) { cmd_status(hci, BT_HCI_ERR_INVALID_PARAMETERS, BT_HCI_CMD_LE_WRITE_DEFAULT_DATA_LENGTH); return; } hci->le_default_tx_len = tx_len; hci->le_default_tx_time = tx_time; status = BT_HCI_ERR_SUCCESS; cmd_complete(hci, BT_HCI_CMD_LE_WRITE_DEFAULT_DATA_LENGTH, &status, sizeof(status)); } static void cmd_le_read_local_pk256(struct bt_le *hci, const void *data, uint8_t size) { struct bt_hci_evt_le_read_local_pk256_complete evt; cmd_status(hci, BT_HCI_ERR_SUCCESS, BT_HCI_CMD_LE_READ_LOCAL_PK256); evt.status = BT_HCI_ERR_SUCCESS; ecc_make_key(evt.local_pk256, hci->le_local_sk256); if (hci->le_event_mask[0] & 0x80) le_meta_event(hci, BT_HCI_EVT_LE_READ_LOCAL_PK256_COMPLETE, &evt, sizeof(evt)); } static void cmd_le_generate_dhkey(struct bt_le *hci, const void *data, uint8_t size) { const struct bt_hci_cmd_le_generate_dhkey *cmd = data; struct bt_hci_evt_le_generate_dhkey_complete evt; cmd_status(hci, BT_HCI_ERR_SUCCESS, BT_HCI_CMD_LE_GENERATE_DHKEY); evt.status = BT_HCI_ERR_SUCCESS; ecdh_shared_secret(cmd->remote_pk256, hci->le_local_sk256, evt.dhkey); if (hci->le_event_mask[1] & 0x01) le_meta_event(hci, BT_HCI_EVT_LE_GENERATE_DHKEY_COMPLETE, &evt, sizeof(evt)); } static void cmd_le_add_to_resolv_list(struct bt_le *hci, const void *data, uint8_t size) { const struct bt_hci_cmd_le_add_to_resolv_list *cmd = data; uint8_t status; bool exists = false; int i, pos = -1; /* Valid range for address type is 0x00 to 0x01 */ if (cmd->addr_type > 0x01) { cmd_status(hci, BT_HCI_ERR_INVALID_PARAMETERS, BT_HCI_CMD_LE_ADD_TO_RESOLV_LIST); return; } for (i = 0; i < hci->le_resolv_list_size; i++) { if (hci->le_resolv_list[i][0] == cmd->addr_type && !memcmp(&hci->le_resolv_list[i][1], cmd->addr, 6)) { exists = true; break; } else if (pos < 0 && hci->le_resolv_list[i][0] == 0xff) pos = i; } if (exists) { cmd_status(hci, BT_HCI_ERR_UNSPECIFIED_ERROR, BT_HCI_CMD_LE_ADD_TO_RESOLV_LIST); return; } if (pos < 0) { cmd_status(hci, BT_HCI_ERR_MEM_CAPACITY_EXCEEDED, BT_HCI_CMD_LE_ADD_TO_RESOLV_LIST); return; } hci->le_resolv_list[pos][0] = cmd->addr_type; memcpy(&hci->le_resolv_list[pos][1], cmd->addr, 6); memcpy(&hci->le_resolv_list[pos][7], cmd->peer_irk, 16); memcpy(&hci->le_resolv_list[pos][23], cmd->local_irk, 16); status = BT_HCI_ERR_SUCCESS; cmd_complete(hci, BT_HCI_CMD_LE_ADD_TO_RESOLV_LIST, &status, sizeof(status)); } static void cmd_le_remove_from_resolv_list(struct bt_le *hci, const void *data, uint8_t size) { const struct bt_hci_cmd_le_remove_from_resolv_list *cmd = data; uint8_t status; int i, pos = -1; /* Valid range for address type is 0x00 to 0x01 */ if (cmd->addr_type > 0x01) { cmd_status(hci, BT_HCI_ERR_INVALID_PARAMETERS, BT_HCI_CMD_LE_REMOVE_FROM_RESOLV_LIST); return; } for (i = 0; i < hci->le_resolv_list_size; i++) { if (hci->le_resolv_list[i][0] == cmd->addr_type && !memcmp(&hci->le_resolv_list[i][1], cmd->addr, 6)) { pos = i; break; } } if (pos < 0) { cmd_status(hci, BT_HCI_ERR_INVALID_PARAMETERS, BT_HCI_CMD_LE_REMOVE_FROM_RESOLV_LIST); return; } hci->le_resolv_list[pos][0] = 0xff; memset(&hci->le_resolv_list[pos][1], 0, 38); status = BT_HCI_ERR_SUCCESS; cmd_complete(hci, BT_HCI_CMD_LE_REMOVE_FROM_RESOLV_LIST, &status, sizeof(status)); } static void cmd_le_clear_resolv_list(struct bt_le *hci, const void *data, uint8_t size) { uint8_t status; clear_resolv_list(hci); status = BT_HCI_ERR_SUCCESS; cmd_complete(hci, BT_HCI_CMD_LE_CLEAR_RESOLV_LIST, &status, sizeof(status)); } static void cmd_le_read_resolv_list_size(struct bt_le *hci, const void *data, uint8_t size) { struct bt_hci_rsp_le_read_resolv_list_size rsp; rsp.status = BT_HCI_ERR_SUCCESS; rsp.size = hci->le_resolv_list_size; cmd_complete(hci, BT_HCI_CMD_LE_READ_RESOLV_LIST_SIZE, &rsp, sizeof(rsp)); } static void cmd_le_read_peer_resolv_addr(struct bt_le *hci, const void *data, uint8_t size) { const struct bt_hci_cmd_le_read_peer_resolv_addr *cmd = data; struct bt_hci_rsp_le_read_peer_resolv_addr rsp; /* Valid range for address type is 0x00 to 0x01 */ if (cmd->addr_type > 0x01) { cmd_status(hci, BT_HCI_ERR_INVALID_PARAMETERS, BT_HCI_CMD_LE_READ_PEER_RESOLV_ADDR); return; } rsp.status = BT_HCI_ERR_UNKNOWN_CONN_ID; memset(rsp.addr, 0, 6); cmd_complete(hci, BT_HCI_CMD_LE_READ_PEER_RESOLV_ADDR, &rsp, sizeof(rsp)); } static void cmd_le_read_local_resolv_addr(struct bt_le *hci, const void *data, uint8_t size) { const struct bt_hci_cmd_le_read_local_resolv_addr *cmd = data; struct bt_hci_rsp_le_read_local_resolv_addr rsp; /* Valid range for address type is 0x00 to 0x01 */ if (cmd->addr_type > 0x01) { cmd_status(hci, BT_HCI_ERR_INVALID_PARAMETERS, BT_HCI_CMD_LE_READ_LOCAL_RESOLV_ADDR); return; } rsp.status = BT_HCI_ERR_UNKNOWN_CONN_ID; memset(rsp.addr, 0, 6); cmd_complete(hci, BT_HCI_CMD_LE_READ_LOCAL_RESOLV_ADDR, &rsp, sizeof(rsp)); } static void cmd_le_set_resolv_enable(struct bt_le *hci, const void *data, uint8_t size) { const struct bt_hci_cmd_le_set_resolv_enable *cmd = data; uint8_t status; /* Valid range for address resolution enable is 0x00 to 0x01 */ if (cmd->enable > 0x01) { cmd_status(hci, BT_HCI_ERR_INVALID_PARAMETERS, BT_HCI_CMD_LE_SET_RESOLV_ENABLE); return; } hci->le_resolv_enable = cmd->enable; status = BT_HCI_ERR_SUCCESS; cmd_complete(hci, BT_HCI_CMD_LE_SET_RESOLV_ENABLE, &status, sizeof(status)); } static void cmd_le_set_resolv_timeout(struct bt_le *hci, const void *data, uint8_t size) { const struct bt_hci_cmd_le_set_resolv_timeout *cmd = data; uint16_t timeout; uint8_t status; timeout = le16_to_cpu(cmd->timeout); /* Valid range for RPA timeout is 0x0001 to 0xa1b8 */ if (timeout < 0x0001 || timeout > 0xa1b8) { cmd_status(hci, BT_HCI_ERR_INVALID_PARAMETERS, BT_HCI_CMD_LE_SET_RESOLV_TIMEOUT); return; } hci->le_resolv_timeout = timeout; status = BT_HCI_ERR_SUCCESS; cmd_complete(hci, BT_HCI_CMD_LE_SET_RESOLV_TIMEOUT, &status, sizeof(status)); } static void cmd_le_read_max_data_length(struct bt_le *hci, const void *data, uint8_t size) { struct bt_hci_rsp_le_read_max_data_length rsp; rsp.status = BT_HCI_ERR_SUCCESS; rsp.max_tx_len = cpu_to_le16(MAX_TX_LEN); rsp.max_tx_time = cpu_to_le16(MAX_TX_TIME); rsp.max_rx_len = cpu_to_le16(MAX_RX_LEN); rsp.max_rx_time = cpu_to_le16(MAX_RX_TIME); cmd_complete(hci, BT_HCI_CMD_LE_READ_MAX_DATA_LENGTH, &rsp, sizeof(rsp)); } static void cmd_le_read_phy(struct bt_le *hci, const void *data, uint8_t size) { const struct bt_hci_cmd_le_read_phy *cmd = data; struct bt_hci_rsp_le_read_phy rsp; rsp.status = BT_HCI_ERR_SUCCESS; rsp.handle = cmd->handle; rsp.tx_phy = 0x01; /* LE 1M */ rsp.rx_phy = 0x01; /* LE 1M */ cmd_complete(hci, BT_HCI_CMD_LE_READ_PHY, &rsp, sizeof(rsp)); } static void cmd_le_set_default_phy(struct bt_le *hci, const void *data, uint8_t size) { const struct bt_hci_cmd_le_set_default_phy *cmd = data; uint8_t status, tx_phys, rx_phys; uint8_t phys_mask; phys_mask = (true << 0) | ((!!(hci->le_features[1] & 0x01)) << 1) | ((!!(hci->le_features[1] & 0x08)) << 2); if (cmd->all_phys > 0x03) { cmd_status(hci, BT_HCI_ERR_INVALID_PARAMETERS, BT_HCI_CMD_LE_SET_DEFAULT_PHY); return; } /* Transmitter PHYs preferences */ if (!(cmd->all_phys & 0x01)) { /* At least one preference bit shall be set to 1 */ if (!cmd->tx_phys) { cmd_status(hci, BT_HCI_ERR_INVALID_PARAMETERS, BT_HCI_CMD_LE_SET_DEFAULT_PHY); return; } if (cmd->tx_phys & ~phys_mask) { cmd_status(hci, BT_HCI_ERR_INVALID_PARAMETERS, BT_HCI_CMD_LE_SET_DEFAULT_PHY); return; } tx_phys = cmd->tx_phys; } else tx_phys = 0x00; /* Transmitter PHYs preferences */ if (!(cmd->all_phys & 0x02)) { /* At least one preference bit shall be set to 1 */ if (!cmd->rx_phys) { cmd_status(hci, BT_HCI_ERR_INVALID_PARAMETERS, BT_HCI_CMD_LE_SET_DEFAULT_PHY); return; } if (cmd->rx_phys & ~phys_mask) { cmd_status(hci, BT_HCI_ERR_INVALID_PARAMETERS, BT_HCI_CMD_LE_SET_DEFAULT_PHY); return; } rx_phys = cmd->rx_phys; } else rx_phys = 0x00; hci->le_default_all_phys = cmd->all_phys; hci->le_default_tx_phys = tx_phys; hci->le_default_rx_phys = rx_phys; status = BT_HCI_ERR_SUCCESS; cmd_complete(hci, BT_HCI_CMD_LE_SET_DEFAULT_PHY, &status, sizeof(status)); } static void cmd_le_set_phy(struct bt_le *hci, const void *data, uint8_t size) { const struct bt_hci_cmd_le_set_phy *cmd = data; struct bt_hci_evt_le_phy_update_complete evt; cmd_status(hci, BT_HCI_ERR_SUCCESS, BT_HCI_CMD_LE_SET_PHY); evt.status = BT_HCI_ERR_SUCCESS; evt.handle = cmd->handle; evt.tx_phy = 0x01; /* LE 1M */ evt.rx_phy = 0x01; /* LE 1M */ if (hci->le_event_mask[1] & 0x08) le_meta_event(hci, BT_HCI_EVT_LE_PHY_UPDATE_COMPLETE, &evt, sizeof(evt)); } static const struct { uint16_t opcode; void (*func) (struct bt_le *hci, const void *data, uint8_t size); uint8_t size; bool fixed; } cmd_table[] = { { BT_HCI_CMD_DISCONNECT, cmd_disconnect, 3, true }, { BT_HCI_CMD_SET_EVENT_MASK, cmd_set_event_mask, 8, true }, { BT_HCI_CMD_RESET, cmd_reset, 0, true }, { BT_HCI_CMD_SET_EVENT_MASK_PAGE2, cmd_set_event_mask_page2, 8, true }, { BT_HCI_CMD_READ_LOCAL_VERSION, cmd_read_local_version, 0, true }, { BT_HCI_CMD_READ_LOCAL_COMMANDS, cmd_read_local_commands, 0, true }, { BT_HCI_CMD_READ_LOCAL_FEATURES, cmd_read_local_features, 0, true }, { BT_HCI_CMD_READ_BUFFER_SIZE, cmd_read_buffer_size, 0, true }, { BT_HCI_CMD_READ_BD_ADDR, cmd_read_bd_addr, 0, true }, { BT_HCI_CMD_LE_SET_EVENT_MASK, cmd_le_set_event_mask, 8, true }, { BT_HCI_CMD_LE_READ_BUFFER_SIZE, cmd_le_read_buffer_size, 0, true }, { BT_HCI_CMD_LE_READ_LOCAL_FEATURES, cmd_le_read_local_features, 0, true }, { BT_HCI_CMD_LE_SET_RANDOM_ADDRESS, cmd_le_set_random_address, 6, true }, { BT_HCI_CMD_LE_SET_ADV_PARAMETERS, cmd_le_set_adv_parameters, 15, true }, { BT_HCI_CMD_LE_READ_ADV_TX_POWER, cmd_le_read_adv_tx_power, 0, true }, { BT_HCI_CMD_LE_SET_ADV_DATA, cmd_le_set_adv_data, 32, true }, { BT_HCI_CMD_LE_SET_SCAN_RSP_DATA, cmd_le_set_scan_rsp_data, 32, true }, { BT_HCI_CMD_LE_SET_ADV_ENABLE, cmd_le_set_adv_enable, 1, true }, { BT_HCI_CMD_LE_SET_SCAN_PARAMETERS, cmd_le_set_scan_parameters, 7, true }, { BT_HCI_CMD_LE_SET_SCAN_ENABLE, cmd_le_set_scan_enable, 2, true }, { BT_HCI_CMD_LE_CREATE_CONN, cmd_le_create_conn, 25, true }, { BT_HCI_CMD_LE_CREATE_CONN_CANCEL, cmd_le_create_conn_cancel, 0, true }, { BT_HCI_CMD_LE_READ_ACCEPT_LIST_SIZE, cmd_le_read_accept_list_size, 0, true }, { BT_HCI_CMD_LE_CLEAR_ACCEPT_LIST, cmd_le_clear_accept_list, 0, true }, { BT_HCI_CMD_LE_ADD_TO_ACCEPT_LIST, cmd_le_add_to_accept_list, 7, true }, { BT_HCI_CMD_LE_REMOVE_FROM_ACCEPT_LIST, cmd_le_remove_from_accept_list, 7, true }, { BT_HCI_CMD_LE_ENCRYPT, cmd_le_encrypt, 32, true }, { BT_HCI_CMD_LE_RAND, cmd_le_rand, 0, true }, { BT_HCI_CMD_LE_READ_SUPPORTED_STATES, cmd_le_read_supported_states, 0, true }, { BT_HCI_CMD_LE_SET_DATA_LENGTH, cmd_le_set_data_length, 6, true }, { BT_HCI_CMD_LE_READ_DEFAULT_DATA_LENGTH, cmd_le_read_default_data_length, 0, true }, { BT_HCI_CMD_LE_WRITE_DEFAULT_DATA_LENGTH, cmd_le_write_default_data_length, 4, true }, { BT_HCI_CMD_LE_READ_LOCAL_PK256, cmd_le_read_local_pk256, 0, true }, { BT_HCI_CMD_LE_GENERATE_DHKEY, cmd_le_generate_dhkey, 64, true }, { BT_HCI_CMD_LE_ADD_TO_RESOLV_LIST, cmd_le_add_to_resolv_list, 39, true }, { BT_HCI_CMD_LE_REMOVE_FROM_RESOLV_LIST, cmd_le_remove_from_resolv_list, 7, true }, { BT_HCI_CMD_LE_CLEAR_RESOLV_LIST, cmd_le_clear_resolv_list, 0, true }, { BT_HCI_CMD_LE_READ_RESOLV_LIST_SIZE, cmd_le_read_resolv_list_size, 0, true }, { BT_HCI_CMD_LE_READ_PEER_RESOLV_ADDR, cmd_le_read_peer_resolv_addr, 7, true }, { BT_HCI_CMD_LE_READ_LOCAL_RESOLV_ADDR, cmd_le_read_local_resolv_addr, 7, true }, { BT_HCI_CMD_LE_SET_RESOLV_ENABLE, cmd_le_set_resolv_enable, 1, true }, { BT_HCI_CMD_LE_SET_RESOLV_TIMEOUT, cmd_le_set_resolv_timeout, 2, true }, { BT_HCI_CMD_LE_READ_MAX_DATA_LENGTH, cmd_le_read_max_data_length, 0, true }, { BT_HCI_CMD_LE_READ_PHY, cmd_le_read_phy, 2, true }, { BT_HCI_CMD_LE_SET_DEFAULT_PHY, cmd_le_set_default_phy, 3, true }, { BT_HCI_CMD_LE_SET_PHY, cmd_le_set_phy, 7, true }, { } }; static void process_command(struct bt_le *hci, const void *data, size_t size) { const struct bt_hci_cmd_hdr *hdr = data; uint16_t opcode; unsigned int i; if (size < sizeof(*hdr)) return; data += sizeof(*hdr); size -= sizeof(*hdr); opcode = le16_to_cpu(hdr->opcode); if (hdr->plen != size) { cmd_status(hci, BT_HCI_ERR_INVALID_PARAMETERS, opcode); return; } for (i = 0; cmd_table[i].func; i++) { if (cmd_table[i].opcode != opcode) continue; if ((cmd_table[i].fixed && size != cmd_table[i].size) || size < cmd_table[i].size) { cmd_status(hci, BT_HCI_ERR_INVALID_PARAMETERS, opcode); return; } cmd_table[i].func(hci, data, size); return; } cmd_status(hci, BT_HCI_ERR_UNKNOWN_COMMAND, opcode); } static void vhci_read_callback(int fd, uint32_t events, void *user_data) { struct bt_le *hci = user_data; unsigned char buf[4096]; ssize_t len; if (events & (EPOLLERR | EPOLLHUP)) return; len = read(hci->vhci_fd, buf, sizeof(buf)); if (len < 1) return; switch (buf[0]) { case BT_H4_CMD_PKT: process_command(hci, buf + 1, len - 1); break; } } static void phy_recv_callback(uint16_t type, const void *data, size_t size, void *user_data) { struct bt_le *hci = user_data; switch (type) { case BT_PHY_PKT_ADV: if (!(hci->le_event_mask[0] & 0x02)) return; if (hci->scan_window_active) { const struct bt_phy_pkt_adv *pkt = data; uint8_t buf[100]; struct bt_hci_evt_le_adv_report *evt = (void *) buf; uint8_t tx_addr_type, tx_addr[6]; if (hci->scan_chan_idx != pkt->chan_idx) break; resolve_peer_addr(hci, pkt->tx_addr_type, pkt->tx_addr, &tx_addr_type, tx_addr); if (hci->le_scan_filter_policy == 0x01 || hci->le_scan_filter_policy == 0x03) { if (!is_in_accept_list(hci, tx_addr_type, tx_addr)) break; } if (hci->le_scan_filter_dup) { if (!add_to_scan_cache(hci, tx_addr_type, tx_addr)) break; } memset(buf, 0, sizeof(buf)); evt->num_reports = 0x01; evt->event_type = pkt->pdu_type; evt->addr_type = tx_addr_type; memcpy(evt->addr, tx_addr, 6); evt->data_len = pkt->adv_data_len; memcpy(buf + sizeof(*evt), data + sizeof(*pkt), pkt->adv_data_len); le_meta_event(hci, BT_HCI_EVT_LE_ADV_REPORT, buf, sizeof(*evt) + pkt->adv_data_len + 1); if (hci->le_scan_type == 0x00) break; memset(buf, 0, sizeof(buf)); evt->num_reports = 0x01; evt->event_type = 0x04; evt->addr_type = tx_addr_type; memcpy(evt->addr, tx_addr, 6); evt->data_len = pkt->scan_rsp_len; memcpy(buf + sizeof(*evt), data + sizeof(*pkt) + pkt->adv_data_len, pkt->scan_rsp_len); le_meta_event(hci, BT_HCI_EVT_LE_ADV_REPORT, buf, sizeof(*evt) + pkt->scan_rsp_len + 1); } break; } } struct bt_le *bt_le_new(void) { unsigned char setup_cmd[2]; struct bt_le *hci; hci = calloc(1, sizeof(*hci)); if (!hci) return NULL; hci->adv_timeout_id = -1; hci->scan_timeout_id = -1; hci->scan_window_active = false; reset_defaults(hci); hci->vhci_fd = open("/dev/vhci", O_RDWR); if (hci->vhci_fd < 0) { free(hci); return NULL; } setup_cmd[0] = HCI_VENDOR_PKT; setup_cmd[1] = HCI_PRIMARY; if (write(hci->vhci_fd, setup_cmd, sizeof(setup_cmd)) < 0) { close(hci->vhci_fd); free(hci); return NULL; } mainloop_add_fd(hci->vhci_fd, EPOLLIN, vhci_read_callback, hci, NULL); hci->phy = bt_phy_new(); hci->crypto = bt_crypto_new(); bt_phy_register(hci->phy, phy_recv_callback, hci); return bt_le_ref(hci); } struct bt_le *bt_le_ref(struct bt_le *hci) { if (!hci) return NULL; __sync_fetch_and_add(&hci->ref_count, 1); return hci; } void bt_le_unref(struct bt_le *hci) { if (!hci) return; if (__sync_sub_and_fetch(&hci->ref_count, 1)) return; stop_adv(hci); bt_crypto_unref(hci->crypto); bt_phy_unref(hci->phy); mainloop_remove_fd(hci->vhci_fd); close(hci->vhci_fd); free(hci); } bluez-5.82/emulator/PaxHeaders/serial.c0000644000000000000000000000005014766002272015127 xustar0020 atime=1743515578 20 ctime=1743591281 bluez-5.82/emulator/serial.c0000644000000000000000000001073214766002272014613 0ustar00rootroot// SPDX-License-Identifier: LGPL-2.1-or-later /* * * BlueZ - Bluetooth protocol stack for Linux * * Copyright (C) 2011-2014 Intel Corporation * Copyright (C) 2004-2010 Marcel Holtmann * * */ #ifdef HAVE_CONFIG_H #include #endif #define _GNU_SOURCE #include #include #include #include #include #include #include #include #include #include #include #include "lib/bluetooth.h" #include "lib/hci.h" #include "src/shared/mainloop.h" #include "btdev.h" #include "serial.h" #define uninitialized_var(x) x = x struct serial { enum serial_type type; uint16_t id; int fd; char path[PATH_MAX]; struct btdev *btdev; uint8_t *pkt_data; uint8_t pkt_type; uint16_t pkt_expect; uint16_t pkt_len; uint16_t pkt_offset; }; static void open_pty(struct serial *serial); static void serial_destroy(void *user_data) { struct serial *serial = user_data; btdev_destroy(serial->btdev); serial->btdev = NULL; close(serial->fd); serial->fd = -1; } static void serial_write_callback(const struct iovec *iov, int iovlen, void *user_data) { struct serial *serial = user_data; ssize_t written; written = writev(serial->fd, iov, iovlen); if (written < 0) return; } static void serial_read_callback(int fd, uint32_t events, void *user_data) { struct serial *serial = user_data; uint8_t buf[4096]; uint8_t *ptr = buf; ssize_t len; uint16_t count; if (events & (EPOLLERR | EPOLLHUP)) { mainloop_remove_fd(serial->fd); open_pty(serial); return; } again: len = read(serial->fd, buf, sizeof(buf)); if (len < 0) { if (errno == EAGAIN) goto again; return; } if (!serial->btdev) return; count = len; while (count > 0) { hci_command_hdr *cmd_hdr; if (!serial->pkt_data) { serial->pkt_type = ptr[0]; switch (serial->pkt_type) { case HCI_COMMAND_PKT: if (count < HCI_COMMAND_HDR_SIZE + 1) { serial->pkt_offset += len; return; } cmd_hdr = (hci_command_hdr *) (ptr + 1); serial->pkt_expect = HCI_COMMAND_HDR_SIZE + cmd_hdr->plen + 1; serial->pkt_data = malloc(serial->pkt_expect); serial->pkt_len = 0; break; default: printf("packet error\n"); return; } serial->pkt_offset = 0; } if (count >= serial->pkt_expect) { memcpy(serial->pkt_data + serial->pkt_len, ptr, serial->pkt_expect); ptr += serial->pkt_expect; count -= serial->pkt_expect; btdev_receive_h4(serial->btdev, serial->pkt_data, serial->pkt_len + serial->pkt_expect); free(serial->pkt_data); serial->pkt_data = NULL; } else { memcpy(serial->pkt_data + serial->pkt_len, ptr, count); serial->pkt_len += count; serial->pkt_expect -= count; count = 0; } } } static void open_pty(struct serial *serial) { enum btdev_type uninitialized_var(type); serial->fd = posix_openpt(O_RDWR | O_NOCTTY); if (serial->fd < 0) { perror("Failed to get central pseudo terminal"); return; } if (grantpt(serial->fd) < 0) { perror("Failed to grant peripheral pseudo terminal"); close(serial->fd); serial->fd = -1; return; } if (unlockpt(serial->fd) < 0) { perror("Failed to unlock peripheral pseudo terminal"); close(serial->fd); serial->fd = -1; return; } ptsname_r(serial->fd, serial->path, sizeof(serial->path)); printf("Pseudo terminal at %s\n", serial->path); switch (serial->type) { case SERIAL_TYPE_BREDRLE: type = BTDEV_TYPE_BREDRLE; break; case SERIAL_TYPE_BREDR: type = BTDEV_TYPE_BREDR; break; case SERIAL_TYPE_LE: type = BTDEV_TYPE_LE; break; case SERIAL_TYPE_AMP: type = BTDEV_TYPE_AMP; break; } serial->btdev = btdev_create(type, serial->id); if (!serial->btdev) { close(serial->fd); serial->fd = -1; return; } btdev_set_send_handler(serial->btdev, serial_write_callback, serial); if (mainloop_add_fd(serial->fd, EPOLLIN, serial_read_callback, serial, serial_destroy) < 0) { btdev_destroy(serial->btdev); serial->btdev = NULL; close(serial->fd); serial->fd = -1; return; } } struct serial *serial_open(enum serial_type type) { struct serial *serial; enum btdev_type uninitialized_var(dev_type); serial = malloc(sizeof(*serial)); if (!serial) return NULL; memset(serial, 0, sizeof(*serial)); serial->type = type; serial->id = 0x42; open_pty(serial); return serial; } void serial_close(struct serial *serial) { if (!serial) return; mainloop_remove_fd(serial->fd); free(serial); } bluez-5.82/emulator/PaxHeaders/vhci.c0000644000000000000000000000005014711225433014575 xustar0020 atime=1743516864 20 ctime=1743591278 bluez-5.82/emulator/vhci.c0000644000000000000000000001356314711225433014266 0ustar00rootroot// SPDX-License-Identifier: LGPL-2.1-or-later /* * * BlueZ - Bluetooth protocol stack for Linux * * Copyright (C) 2011-2014 Intel Corporation * Copyright (C) 2004-2010 Marcel Holtmann * * */ #ifdef HAVE_CONFIG_H #include #endif #include #include #include #include #include #include #include #include #include #include #include #include "lib/bluetooth.h" #include "lib/hci.h" #include "src/shared/io.h" #include "monitor/bt.h" #include "btdev.h" #include "vhci.h" #define DEBUGFS_PATH "/sys/kernel/debug/bluetooth" #define DEVCORE_PATH "/sys/class/devcoredump" struct vhci { enum btdev_type type; uint16_t index; struct io *io; struct btdev *btdev; }; static void vhci_destroy(void *user_data) { struct vhci *vhci = user_data; btdev_destroy(vhci->btdev); io_destroy(vhci->io); free(vhci); } static void vhci_write_callback(const struct iovec *iov, int iovlen, void *user_data) { struct vhci *vhci = user_data; ssize_t written; written = io_send(vhci->io, iov, iovlen); if (written < 0) return; } static bool vhci_read_callback(struct io *io, void *user_data) { struct vhci *vhci = user_data; int fd = io_get_fd(vhci->io); unsigned char buf[4096]; ssize_t len; len = read(fd, buf, sizeof(buf)); if (len < 1) return false; btdev_receive_h4(vhci->btdev, buf, len); return true; } bool vhci_set_debug(struct vhci *vhci, vhci_debug_func_t callback, void *user_data, vhci_destroy_func_t destroy) { if (!vhci) return false; return btdev_set_debug(vhci->btdev, callback, user_data, destroy); } struct vhci_create_req { uint8_t pkt_type; uint8_t opcode; } __attribute__((packed)); struct vhci_create_rsp { uint8_t pkt_type; uint8_t opcode; uint16_t index; } __attribute__((packed)); struct vhci *vhci_open(uint8_t type) { struct vhci *vhci; struct vhci_create_req req; struct vhci_create_rsp rsp; int fd; fd = open("/dev/vhci", O_RDWR | O_NONBLOCK); if (fd < 0) return NULL; memset(&req, 0, sizeof(req)); req.pkt_type = HCI_VENDOR_PKT; switch (type) { case BTDEV_TYPE_AMP: req.opcode = HCI_AMP; break; default: req.opcode = HCI_PRIMARY; break; } if (write(fd, &req, sizeof(req)) != sizeof(req)) { close(fd); return NULL; } memset(&rsp, 0, sizeof(rsp)); if (read(fd, &rsp, sizeof(rsp)) != sizeof(rsp) || rsp.pkt_type != HCI_VENDOR_PKT || rsp.opcode != req.opcode) { close(fd); return NULL; } vhci = malloc(sizeof(*vhci)); if (!vhci) { close(fd); return NULL; } memset(vhci, 0, sizeof(*vhci)); vhci->type = type; vhci->index = rsp.index; vhci->io = io_new(fd); io_set_close_on_destroy(vhci->io, true); vhci->btdev = btdev_create(type, rsp.index); if (!vhci->btdev) { vhci_destroy(vhci); return NULL; } btdev_set_send_handler(vhci->btdev, vhci_write_callback, vhci); if (!io_set_read_handler(vhci->io, vhci_read_callback, vhci, NULL)) { vhci_destroy(vhci); return NULL; } return vhci; } void vhci_close(struct vhci *vhci) { if (!vhci) return; vhci_destroy(vhci); } bool vhci_pause_input(struct vhci *vhci, bool paused) { if (paused) return io_set_read_handler(vhci->io, NULL, NULL, NULL); else return io_set_read_handler(vhci->io, vhci_read_callback, vhci, NULL); } struct btdev *vhci_get_btdev(struct vhci *vhci) { if (!vhci) return NULL; return vhci->btdev; } static int vhci_debugfs_write(struct vhci *vhci, char *option, const void *data, size_t len) { char path[64]; int fd, err; size_t n; if (!vhci) return -EINVAL; memset(path, 0, sizeof(path)); sprintf(path, DEBUGFS_PATH "/hci%d/%s", vhci->index, option); fd = open(path, O_RDWR); if (fd < 0) return -errno; n = write(fd, data, len); if (n == len) err = 0; else err = -errno; close(fd); return err; } int vhci_set_force_suspend(struct vhci *vhci, bool enable) { char val; val = (enable) ? 'Y' : 'N'; return vhci_debugfs_write(vhci, "force_suspend", &val, sizeof(val)); } int vhci_set_force_wakeup(struct vhci *vhci, bool enable) { char val; val = (enable) ? 'Y' : 'N'; return vhci_debugfs_write(vhci, "force_wakeup", &val, sizeof(val)); } int vhci_set_msft_opcode(struct vhci *vhci, uint16_t opcode) { int err; char val[7]; snprintf(val, sizeof(val), "0x%4x", opcode); err = vhci_debugfs_write(vhci, "msft_opcode", &val, sizeof(val)); if (err) return err; return btdev_set_msft_opcode(vhci->btdev, opcode); } int vhci_set_aosp_capable(struct vhci *vhci, bool enable) { char val; val = (enable) ? 'Y' : 'N'; return vhci_debugfs_write(vhci, "aosp_capable", &val, sizeof(val)); } int vhci_set_emu_opcode(struct vhci *vhci, uint16_t opcode) { return btdev_set_emu_opcode(vhci->btdev, opcode); } int vhci_set_force_static_address(struct vhci *vhci, bool enable) { char val; val = (enable) ? 'Y' : 'N'; return vhci_debugfs_write(vhci, "force_static_address", &val, sizeof(val)); } int vhci_force_devcd(struct vhci *vhci, const void *data, size_t len) { return vhci_debugfs_write(vhci, "force_devcoredump", data, len); } int vhci_read_devcd(struct vhci *vhci, void *buf, size_t size) { DIR *dir; struct dirent *entry; char filename[PATH_MAX]; int fd; int ret; dir = opendir(DEVCORE_PATH); if (dir == NULL) return -errno; while ((entry = readdir(dir)) != NULL) { if (strstr(entry->d_name, "devcd")) break; } if (entry == NULL) { ret = -ENOENT; goto close_dir; } sprintf(filename, DEVCORE_PATH "/%s/data", entry->d_name); fd = open(filename, O_RDWR); if (fd < 0) { ret = -errno; goto close_dir; } ret = read(fd, buf, size); if (ret < 0) { ret = -errno; goto close_file; } /* Once the devcoredump is read, write anything to it to mark it for * cleanup. */ if (write(fd, "0", 1) < 0) { ret = -errno; goto close_file; } close_file: close(fd); close_dir: closedir(dir); return ret; } bluez-5.82/emulator/PaxHeaders/server.h0000644000000000000000000000005014015011623015146 xustar0020 atime=1743516867 20 ctime=1743591281 bluez-5.82/emulator/server.h0000644000000000000000000000106014015011623014624 0ustar00rootroot/* SPDX-License-Identifier: LGPL-2.1-or-later */ /* * * BlueZ - Bluetooth protocol stack for Linux * * Copyright (C) 2011-2014 Intel Corporation * Copyright (C) 2004-2010 Marcel Holtmann * * */ #include enum server_type { SERVER_TYPE_BREDRLE, SERVER_TYPE_BREDR, SERVER_TYPE_LE, SERVER_TYPE_AMP, SERVER_TYPE_MONITOR, }; struct server; struct server *server_open_unix(enum server_type type, const char *path); struct server *server_open_tcp(enum server_type type); void server_close(struct server *server); bluez-5.82/emulator/PaxHeaders/hciemu.c0000644000000000000000000000005014766002272015122 xustar0020 atime=1743515578 20 ctime=1743591278 bluez-5.82/emulator/hciemu.c0000644000000000000000000003725414766002272014616 0ustar00rootroot// SPDX-License-Identifier: LGPL-2.1-or-later /* * * BlueZ - Bluetooth protocol stack for Linux * * Copyright (C) 2012-2014 Intel Corporation. All rights reserved. * * */ #ifdef HAVE_CONFIG_H #include #endif #define _GNU_SOURCE #include #include #include #include #include #include #include #include #include #include #include "lib/bluetooth.h" #include "lib/hci.h" #include "monitor/bt.h" #include "emulator/vhci.h" #include "emulator/btdev.h" #include "emulator/bthost.h" #include "src/shared/util.h" #include "src/shared/queue.h" #include "emulator/hciemu.h" struct hciemu_client { struct bthost *host; struct btdev *dev; guint start_source; guint host_source; guint source; int sock[2]; }; struct hciemu { int ref_count; enum btdev_type btdev_type; struct vhci *vhci; struct queue *clients; struct queue *post_command_hooks; char bdaddr_str[18]; hciemu_debug_func_t debug_callback; hciemu_destroy_func_t debug_destroy; void *debug_data; unsigned int flush_id; }; struct hciemu_command_hook { hciemu_command_func_t function; void *user_data; }; static void destroy_command_hook(void *data) { struct hciemu_command_hook *hook = data; free(hook); } struct run_data { uint16_t opcode; const void *data; uint8_t len; }; static void run_command_hook(void *data, void *user_data) { struct hciemu_command_hook *hook = data; struct run_data *run_data = user_data; if (hook->function) hook->function(run_data->opcode, run_data->data, run_data->len, hook->user_data); } static void central_command_callback(uint16_t opcode, const void *data, uint8_t len, btdev_callback callback, void *user_data) { struct hciemu *hciemu = user_data; struct run_data run_data = { .opcode = opcode, .data = data, .len = len }; btdev_command_default(callback); queue_foreach(hciemu->post_command_hooks, run_command_hook, &run_data); } static void client_command_callback(uint16_t opcode, const void *data, uint8_t len, btdev_callback callback, void *user_data) { btdev_command_default(callback); } static void writev_callback(const struct iovec *iov, int iovlen, void *user_data) { GIOChannel *channel = user_data; ssize_t written; int fd; fd = g_io_channel_unix_get_fd(channel); written = writev(fd, iov, iovlen); if (written < 0) return; } static gboolean receive_bthost(GIOChannel *channel, GIOCondition condition, gpointer user_data) { struct bthost *bthost = user_data; unsigned char buf[4096]; ssize_t len; int fd; if (condition & (G_IO_NVAL | G_IO_ERR | G_IO_HUP)) return FALSE; fd = g_io_channel_unix_get_fd(channel); len = read(fd, buf, sizeof(buf)); if (len < 0) return FALSE; bthost_receive_h4(bthost, buf, len); return TRUE; } static guint create_source_bthost(int fd, struct bthost *bthost) { GIOChannel *channel; guint source; channel = g_io_channel_unix_new(fd); g_io_channel_set_close_on_unref(channel, TRUE); g_io_channel_set_encoding(channel, NULL, NULL); g_io_channel_set_buffered(channel, FALSE); bthost_set_send_handler(bthost, writev_callback, channel); source = g_io_add_watch_full(channel, G_PRIORITY_DEFAULT, G_IO_IN | G_IO_HUP | G_IO_ERR | G_IO_NVAL, receive_bthost, bthost, NULL); g_io_channel_unref(channel); return source; } static gboolean receive_btdev(GIOChannel *channel, GIOCondition condition, gpointer user_data) { struct btdev *btdev = user_data; unsigned char buf[4096]; ssize_t len; int fd; if (condition & (G_IO_NVAL | G_IO_ERR | G_IO_HUP)) return FALSE; fd = g_io_channel_unix_get_fd(channel); len = read(fd, buf, sizeof(buf)); if (len < 0) { if (errno == EAGAIN || errno == EINTR) return TRUE; return FALSE; } if (len < 1) return FALSE; btdev_receive_h4(btdev, buf, len); return TRUE; } static guint create_source_btdev(int fd, struct btdev *btdev) { GIOChannel *channel; guint source; channel = g_io_channel_unix_new(fd); g_io_channel_set_close_on_unref(channel, TRUE); g_io_channel_set_encoding(channel, NULL, NULL); g_io_channel_set_buffered(channel, FALSE); btdev_set_send_handler(btdev, writev_callback, channel); source = g_io_add_watch_full(channel, G_PRIORITY_DEFAULT, G_IO_IN | G_IO_HUP | G_IO_ERR | G_IO_NVAL, receive_btdev, btdev, NULL); g_io_channel_unref(channel); return source; } static bool create_vhci(struct hciemu *hciemu) { struct vhci *vhci; vhci = vhci_open(hciemu->btdev_type); if (!vhci) return false; btdev_set_command_handler(vhci_get_btdev(vhci), central_command_callback, hciemu); hciemu->vhci = vhci; return true; } struct vhci *hciemu_get_vhci(struct hciemu *hciemu) { if (!hciemu) return NULL; return hciemu->vhci; } struct hciemu_client *hciemu_get_client(struct hciemu *hciemu, int num) { const struct queue_entry *entry; if (!hciemu) return NULL; for (entry = queue_get_entries(hciemu->clients); entry; entry = entry->next, num--) { if (!num) return entry->data; } return NULL; } struct bthost *hciemu_client_host(struct hciemu_client *client) { if (!client) return NULL; return client->host; } struct bthost *hciemu_client_get_host(struct hciemu *hciemu) { struct hciemu_client *client; if (!hciemu) return NULL; client = hciemu_get_client(hciemu, 0); return hciemu_client_host(client); } static gboolean start_host(gpointer user_data) { struct hciemu_client *client = user_data; client->start_source = 0; bthost_start(client->host); return FALSE; } static void hciemu_client_destroy(void *data) { struct hciemu_client *client = data; if (client->start_source) g_source_remove(client->start_source); g_source_remove(client->host_source); g_source_remove(client->source); bthost_destroy(client->host); btdev_destroy(client->dev); free(client); } static struct hciemu_client *hciemu_client_new(struct hciemu *hciemu, uint8_t id) { struct hciemu_client *client; int sv[2]; client = new0(struct hciemu_client, 1); if (!client) return NULL; client->dev = btdev_create(hciemu->btdev_type, id++); if (!client->dev) { free(client); return NULL; } client->host = bthost_create(); if (!client->host) { btdev_destroy(client->dev); free(client); return NULL; } btdev_set_command_handler(client->dev, client_command_callback, client); if (socketpair(AF_UNIX, SOCK_SEQPACKET | SOCK_NONBLOCK | SOCK_CLOEXEC, 0, sv) < 0) { bthost_destroy(client->host); btdev_destroy(client->dev); return NULL; } client->sock[0] = sv[0]; client->sock[1] = sv[1]; client->source = create_source_btdev(sv[0], client->dev); client->host_source = create_source_bthost(sv[1], client->host); client->start_source = g_idle_add(start_host, client); return client; } struct hciemu *hciemu_new_num(enum hciemu_type type, uint8_t num) { struct hciemu *hciemu; int i; if (!num) return NULL; hciemu = new0(struct hciemu, 1); if (!hciemu) return NULL; switch (type) { case HCIEMU_TYPE_BREDRLE: hciemu->btdev_type = BTDEV_TYPE_BREDRLE; break; case HCIEMU_TYPE_BREDR: hciemu->btdev_type = BTDEV_TYPE_BREDR; break; case HCIEMU_TYPE_LE: hciemu->btdev_type = BTDEV_TYPE_LE; break; case HCIEMU_TYPE_LEGACY: hciemu->btdev_type = BTDEV_TYPE_BREDR20; break; case HCIEMU_TYPE_BREDRLE50: hciemu->btdev_type = BTDEV_TYPE_BREDRLE50; break; case HCIEMU_TYPE_BREDRLE52: hciemu->btdev_type = BTDEV_TYPE_BREDRLE52; break; default: return NULL; } hciemu->post_command_hooks = queue_new(); if (!hciemu->post_command_hooks) { free(hciemu); return NULL; } if (!create_vhci(hciemu)) { queue_destroy(hciemu->post_command_hooks, NULL); free(hciemu); return NULL; } hciemu->clients = queue_new(); for (i = 0; i < num; i++) { struct hciemu_client *client = hciemu_client_new(hciemu, i); if (!client) { queue_destroy(hciemu->clients, hciemu_client_destroy); break; } queue_push_tail(hciemu->clients, client); } return hciemu_ref(hciemu); } struct hciemu *hciemu_new(enum hciemu_type type) { return hciemu_new_num(type, 1); } struct hciemu *hciemu_ref(struct hciemu *hciemu) { if (!hciemu) return NULL; __sync_fetch_and_add(&hciemu->ref_count, 1); return hciemu; } void hciemu_unref(struct hciemu *hciemu) { if (!hciemu) return; if (__sync_sub_and_fetch(&hciemu->ref_count, 1)) return; queue_destroy(hciemu->post_command_hooks, destroy_command_hook); queue_destroy(hciemu->clients, hciemu_client_destroy); if (hciemu->flush_id) g_source_remove(hciemu->flush_id); vhci_close(hciemu->vhci); free(hciemu); } static void bthost_print(const char *str, void *user_data) { struct hciemu *hciemu = user_data; util_debug(hciemu->debug_callback, hciemu->debug_data, "bthost: %s", str); } static void vhci_debug(const char *str, void *user_data) { struct hciemu *hciemu = user_data; util_debug(hciemu->debug_callback, hciemu->debug_data, "vhci: %s", str); } static void btdev_client_debug(const char *str, void *user_data) { struct hciemu *hciemu = user_data; util_debug(hciemu->debug_callback, hciemu->debug_data, "btdev: %s", str); } static void hciemu_client_set_debug(void *data, void *user_data) { struct hciemu_client *client = data; struct hciemu *hciemu = user_data; btdev_set_debug(client->dev, btdev_client_debug, hciemu, NULL); bthost_set_debug(client->host, bthost_print, hciemu, NULL); } bool hciemu_set_debug(struct hciemu *hciemu, hciemu_debug_func_t callback, void *user_data, hciemu_destroy_func_t destroy) { if (!hciemu) return false; if (hciemu->debug_destroy) hciemu->debug_destroy(hciemu->debug_data); hciemu->debug_callback = callback; hciemu->debug_destroy = destroy; hciemu->debug_data = user_data; vhci_set_debug(hciemu->vhci, vhci_debug, hciemu, NULL); queue_foreach(hciemu->clients, hciemu_client_set_debug, hciemu); return true; } const char *hciemu_get_address(struct hciemu *hciemu) { const uint8_t *addr; struct btdev *dev; if (!hciemu || !hciemu->vhci) return NULL; dev = vhci_get_btdev(hciemu->vhci); if (!dev) return NULL; addr = btdev_get_bdaddr(dev); sprintf(hciemu->bdaddr_str, "%2.2X:%2.2X:%2.2X:%2.2X:%2.2X:%2.2X", addr[5], addr[4], addr[3], addr[2], addr[1], addr[0]); return hciemu->bdaddr_str; } uint8_t *hciemu_get_features(struct hciemu *hciemu) { struct btdev *dev; if (!hciemu || !hciemu->vhci) return NULL; dev = vhci_get_btdev(hciemu->vhci); if (!dev) return NULL; return btdev_get_features(dev); } uint8_t *hciemu_get_commands(struct hciemu *hciemu) { struct btdev *dev; if (!hciemu || !hciemu->vhci) return NULL; dev = vhci_get_btdev(hciemu->vhci); if (!dev) return NULL; return btdev_get_commands(dev); } const uint8_t *hciemu_get_central_bdaddr(struct hciemu *hciemu) { struct btdev *dev; if (!hciemu || !hciemu->vhci) return NULL; dev = vhci_get_btdev(hciemu->vhci); if (!dev) return NULL; return btdev_get_bdaddr(dev); } const uint8_t *hciemu_client_bdaddr(struct hciemu_client *client) { if (!client) return NULL; return btdev_get_bdaddr(client->dev); } bool hciemu_set_client_bdaddr(struct hciemu_client *client, const uint8_t *bdaddr) { if (!client) return NULL; return btdev_set_bdaddr(client->dev, bdaddr); } const uint8_t *hciemu_get_client_bdaddr(struct hciemu *hciemu) { struct hciemu_client *client; if (!hciemu) return NULL; client = hciemu_get_client(hciemu, 0); return hciemu_client_bdaddr(client); } uint8_t hciemu_get_central_scan_enable(struct hciemu *hciemu) { struct btdev *dev; if (!hciemu || !hciemu->vhci) return 0; dev = vhci_get_btdev(hciemu->vhci); if (!dev) return 0; return btdev_get_scan_enable(dev); } uint8_t hciemu_get_central_le_scan_enable(struct hciemu *hciemu) { struct btdev *dev; if (!hciemu || !hciemu->vhci) return 0; dev = vhci_get_btdev(hciemu->vhci); if (!dev) return 0; return btdev_get_le_scan_enable(dev); } void hciemu_set_central_le_states(struct hciemu *hciemu, const uint8_t *le_states) { struct btdev *dev; if (!hciemu || !hciemu->vhci) return; dev = vhci_get_btdev(hciemu->vhci); if (!dev) return; btdev_set_le_states(dev, le_states); } void hciemu_set_central_le_al_len(struct hciemu *hciemu, uint8_t len) { struct btdev *dev; if (!hciemu || !hciemu->vhci) return; dev = vhci_get_btdev(hciemu->vhci); if (!dev) return; btdev_set_al_len(dev, len); } void hciemu_set_central_le_rl_len(struct hciemu *hciemu, uint8_t len) { struct btdev *dev; if (!hciemu || !hciemu->vhci) return; dev = vhci_get_btdev(hciemu->vhci); if (!dev) return; btdev_set_rl_len(dev, len); } const uint8_t *hciemu_get_central_adv_addr(struct hciemu *hciemu, uint8_t handle) { struct btdev *dev; if (!hciemu || !hciemu->vhci) return NULL; dev = vhci_get_btdev(hciemu->vhci); if (!dev) return NULL; return btdev_get_adv_addr(dev, handle); } bool hciemu_add_central_post_command_hook(struct hciemu *hciemu, hciemu_command_func_t function, void *user_data) { struct hciemu_command_hook *hook; if (!hciemu) return false; hook = new0(struct hciemu_command_hook, 1); if (!hook) return false; hook->function = function; hook->user_data = user_data; if (!queue_push_tail(hciemu->post_command_hooks, hook)) { free(hook); return false; } return true; } bool hciemu_clear_central_post_command_hooks(struct hciemu *hciemu) { if (!hciemu) return false; queue_remove_all(hciemu->post_command_hooks, NULL, NULL, destroy_command_hook); return true; } int hciemu_add_hook(struct hciemu *hciemu, enum hciemu_hook_type type, uint16_t opcode, hciemu_hook_func_t function, void *user_data) { enum btdev_hook_type hook_type; struct btdev *dev; if (!hciemu || !hciemu->vhci) return -1; dev = vhci_get_btdev(hciemu->vhci); if (!dev) return 0; switch (type) { case HCIEMU_HOOK_PRE_CMD: hook_type = BTDEV_HOOK_PRE_CMD; break; case HCIEMU_HOOK_POST_CMD: hook_type = BTDEV_HOOK_POST_CMD; break; case HCIEMU_HOOK_PRE_EVT: hook_type = BTDEV_HOOK_PRE_EVT; break; case HCIEMU_HOOK_POST_EVT: hook_type = BTDEV_HOOK_POST_EVT; break; default: return -1; } return btdev_add_hook(dev, hook_type, opcode, function, user_data); } bool hciemu_del_hook(struct hciemu *hciemu, enum hciemu_hook_type type, uint16_t opcode) { enum btdev_hook_type hook_type; struct btdev *dev; if (!hciemu || !hciemu->vhci) return false; dev = vhci_get_btdev(hciemu->vhci); if (!dev) return false; switch (type) { case HCIEMU_HOOK_PRE_CMD: hook_type = BTDEV_HOOK_PRE_CMD; break; case HCIEMU_HOOK_POST_CMD: hook_type = BTDEV_HOOK_POST_CMD; break; case HCIEMU_HOOK_PRE_EVT: hook_type = BTDEV_HOOK_PRE_EVT; break; case HCIEMU_HOOK_POST_EVT: hook_type = BTDEV_HOOK_POST_EVT; break; default: return false; } return btdev_del_hook(dev, hook_type, opcode); } static bool client_is_pending(const void *data, const void *match_data) { struct hciemu_client *client = (struct hciemu_client *)data; int used, i; if (!client->source || !client->host_source) return false; for (i = 0; i < 2; ++i) { if (!ioctl(client->sock[i], TIOCOUTQ, &used) && used > 0) return true; if (!ioctl(client->sock[i], TIOCINQ, &used) && used > 0) return true; } return false; } static gboolean flush_client_events(gpointer user_data) { struct hciemu *hciemu = user_data; if (queue_find(hciemu->clients, client_is_pending, NULL)) return TRUE; hciemu->flush_id = 0; util_debug(hciemu->debug_callback, hciemu->debug_data, "vhci: resume"); if (hciemu->vhci) vhci_pause_input(hciemu->vhci, false); return FALSE; } void hciemu_flush_client_events(struct hciemu *hciemu) { if (hciemu->flush_id || !hciemu->vhci) return; util_debug(hciemu->debug_callback, hciemu->debug_data, "vhci: pause"); vhci_pause_input(hciemu->vhci, true); hciemu->flush_id = g_idle_add(flush_client_events, hciemu); } bluez-5.82/PaxHeaders/aclocal.m40000644000000000000000000000005014773211364013516 xustar0020 atime=1743590133 20 ctime=1743591275 bluez-5.82/aclocal.m40000644000000000000000000137046714773211364013221 0ustar00rootroot# generated automatically by aclocal 1.16.5 -*- Autoconf -*- # Copyright (C) 1996-2021 Free Software Foundation, Inc. # This file is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, # with or without modifications, as long as this notice is preserved. # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY, to the extent permitted by law; without # even the implied warranty of MERCHANTABILITY or FITNESS FOR A # PARTICULAR PURPOSE. m4_ifndef([AC_CONFIG_MACRO_DIRS], [m4_defun([_AM_CONFIG_MACRO_DIRS], [])m4_defun([AC_CONFIG_MACRO_DIRS], [_AM_CONFIG_MACRO_DIRS($@)])]) m4_ifndef([AC_AUTOCONF_VERSION], [m4_copy([m4_PACKAGE_VERSION], [AC_AUTOCONF_VERSION])])dnl m4_if(m4_defn([AC_AUTOCONF_VERSION]), [2.72],, [m4_warning([this file was generated for autoconf 2.72. You have another version of autoconf. It may work, but is not guaranteed to. If you have problems, you may need to regenerate the build system entirely. To do so, use the procedure documented by the package, typically 'autoreconf'.])]) # libtool.m4 - Configure libtool for the host system. -*-Autoconf-*- # # Copyright (C) 1996-2001, 2003-2019, 2021-2022 Free Software # Foundation, Inc. # Written by Gordon Matzigkeit, 1996 # # This file is free software; the Free Software Foundation gives # unlimited permission to copy and/or distribute it, with or without # modifications, as long as this notice is preserved. m4_define([_LT_COPYING], [dnl # Copyright (C) 2014 Free Software Foundation, Inc. # This is free software; see the source for copying conditions. There is NO # warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. # GNU Libtool is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of of the License, or # (at your option) any later version. # # As a special exception to the GNU General Public License, if you # distribute this file as part of a program or library that is built # using GNU Libtool, you may include this file under the same # distribution terms that you use for the rest of that program. # # GNU Libtool is distributed in the hope that it will be useful, but # WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program. If not, see . ]) # serial 59 LT_INIT # LT_PREREQ(VERSION) # ------------------ # Complain and exit if this libtool version is less that VERSION. m4_defun([LT_PREREQ], [m4_if(m4_version_compare(m4_defn([LT_PACKAGE_VERSION]), [$1]), -1, [m4_default([$3], [m4_fatal([Libtool version $1 or higher is required], 63)])], [$2])]) # _LT_CHECK_BUILDDIR # ------------------ # Complain if the absolute build directory name contains unusual characters m4_defun([_LT_CHECK_BUILDDIR], [case `pwd` in *\ * | *\ *) AC_MSG_WARN([Libtool does not cope well with whitespace in `pwd`]) ;; esac ]) # LT_INIT([OPTIONS]) # ------------------ AC_DEFUN([LT_INIT], [AC_PREREQ([2.62])dnl We use AC_PATH_PROGS_FEATURE_CHECK AC_REQUIRE([AC_CONFIG_AUX_DIR_DEFAULT])dnl AC_BEFORE([$0], [LT_LANG])dnl AC_BEFORE([$0], [LT_OUTPUT])dnl AC_BEFORE([$0], [LTDL_INIT])dnl m4_require([_LT_CHECK_BUILDDIR])dnl dnl Autoconf doesn't catch unexpanded LT_ macros by default: m4_pattern_forbid([^_?LT_[A-Z_]+$])dnl m4_pattern_allow([^(_LT_EOF|LT_DLGLOBAL|LT_DLLAZY_OR_NOW|LT_MULTI_MODULE)$])dnl dnl aclocal doesn't pull ltoptions.m4, ltsugar.m4, or ltversion.m4 dnl unless we require an AC_DEFUNed macro: AC_REQUIRE([LTOPTIONS_VERSION])dnl AC_REQUIRE([LTSUGAR_VERSION])dnl AC_REQUIRE([LTVERSION_VERSION])dnl AC_REQUIRE([LTOBSOLETE_VERSION])dnl m4_require([_LT_PROG_LTMAIN])dnl _LT_SHELL_INIT([SHELL=${CONFIG_SHELL-/bin/sh}]) dnl Parse OPTIONS _LT_SET_OPTIONS([$0], [$1]) # This can be used to rebuild libtool when needed LIBTOOL_DEPS=$ltmain # Always use our own libtool. LIBTOOL='$(SHELL) $(top_builddir)/libtool' AC_SUBST(LIBTOOL)dnl _LT_SETUP # Only expand once: m4_define([LT_INIT]) ])# LT_INIT # Old names: AU_ALIAS([AC_PROG_LIBTOOL], [LT_INIT]) AU_ALIAS([AM_PROG_LIBTOOL], [LT_INIT]) dnl aclocal-1.4 backwards compatibility: dnl AC_DEFUN([AC_PROG_LIBTOOL], []) dnl AC_DEFUN([AM_PROG_LIBTOOL], []) # _LT_PREPARE_CC_BASENAME # ----------------------- m4_defun([_LT_PREPARE_CC_BASENAME], [ # Calculate cc_basename. Skip known compiler wrappers and cross-prefix. func_cc_basename () { for cc_temp in @S|@*""; do case $cc_temp in compile | *[[\\/]]compile | ccache | *[[\\/]]ccache ) ;; distcc | *[[\\/]]distcc | purify | *[[\\/]]purify ) ;; \-*) ;; *) break;; esac done func_cc_basename_result=`$ECHO "$cc_temp" | $SED "s%.*/%%; s%^$host_alias-%%"` } ])# _LT_PREPARE_CC_BASENAME # _LT_CC_BASENAME(CC) # ------------------- # It would be clearer to call AC_REQUIREs from _LT_PREPARE_CC_BASENAME, # but that macro is also expanded into generated libtool script, which # arranges for $SED and $ECHO to be set by different means. m4_defun([_LT_CC_BASENAME], [m4_require([_LT_PREPARE_CC_BASENAME])dnl AC_REQUIRE([_LT_DECL_SED])dnl AC_REQUIRE([_LT_PROG_ECHO_BACKSLASH])dnl func_cc_basename $1 cc_basename=$func_cc_basename_result ]) # _LT_FILEUTILS_DEFAULTS # ---------------------- # It is okay to use these file commands and assume they have been set # sensibly after 'm4_require([_LT_FILEUTILS_DEFAULTS])'. m4_defun([_LT_FILEUTILS_DEFAULTS], [: ${CP="cp -f"} : ${MV="mv -f"} : ${RM="rm -f"} ])# _LT_FILEUTILS_DEFAULTS # _LT_SETUP # --------- m4_defun([_LT_SETUP], [AC_REQUIRE([AC_CANONICAL_HOST])dnl AC_REQUIRE([AC_CANONICAL_BUILD])dnl AC_REQUIRE([_LT_PREPARE_SED_QUOTE_VARS])dnl AC_REQUIRE([_LT_PROG_ECHO_BACKSLASH])dnl _LT_DECL([], [PATH_SEPARATOR], [1], [The PATH separator for the build system])dnl dnl _LT_DECL([], [host_alias], [0], [The host system])dnl _LT_DECL([], [host], [0])dnl _LT_DECL([], [host_os], [0])dnl dnl _LT_DECL([], [build_alias], [0], [The build system])dnl _LT_DECL([], [build], [0])dnl _LT_DECL([], [build_os], [0])dnl dnl AC_REQUIRE([AC_PROG_CC])dnl AC_REQUIRE([LT_PATH_LD])dnl AC_REQUIRE([LT_PATH_NM])dnl dnl AC_REQUIRE([AC_PROG_LN_S])dnl test -z "$LN_S" && LN_S="ln -s" _LT_DECL([], [LN_S], [1], [Whether we need soft or hard links])dnl dnl AC_REQUIRE([LT_CMD_MAX_LEN])dnl _LT_DECL([objext], [ac_objext], [0], [Object file suffix (normally "o")])dnl _LT_DECL([], [exeext], [0], [Executable file suffix (normally "")])dnl dnl m4_require([_LT_FILEUTILS_DEFAULTS])dnl m4_require([_LT_CHECK_SHELL_FEATURES])dnl m4_require([_LT_PATH_CONVERSION_FUNCTIONS])dnl m4_require([_LT_CMD_RELOAD])dnl m4_require([_LT_DECL_FILECMD])dnl m4_require([_LT_CHECK_MAGIC_METHOD])dnl m4_require([_LT_CHECK_SHAREDLIB_FROM_LINKLIB])dnl m4_require([_LT_CMD_OLD_ARCHIVE])dnl m4_require([_LT_CMD_GLOBAL_SYMBOLS])dnl m4_require([_LT_WITH_SYSROOT])dnl m4_require([_LT_CMD_TRUNCATE])dnl _LT_CONFIG_LIBTOOL_INIT([ # See if we are running on zsh, and set the options that allow our # commands through without removal of \ escapes INIT. if test -n "\${ZSH_VERSION+set}"; then setopt NO_GLOB_SUBST fi ]) if test -n "${ZSH_VERSION+set}"; then setopt NO_GLOB_SUBST fi _LT_CHECK_OBJDIR m4_require([_LT_TAG_COMPILER])dnl case $host_os in aix3*) # AIX sometimes has problems with the GCC collect2 program. For some # reason, if we set the COLLECT_NAMES environment variable, the problems # vanish in a puff of smoke. if test set != "${COLLECT_NAMES+set}"; then COLLECT_NAMES= export COLLECT_NAMES fi ;; esac # Global variables: ofile=libtool can_build_shared=yes # All known linkers require a '.a' archive for static linking (except MSVC and # ICC, which need '.lib'). libext=a with_gnu_ld=$lt_cv_prog_gnu_ld old_CC=$CC old_CFLAGS=$CFLAGS # Set sane defaults for various variables test -z "$CC" && CC=cc test -z "$LTCC" && LTCC=$CC test -z "$LTCFLAGS" && LTCFLAGS=$CFLAGS test -z "$LD" && LD=ld test -z "$ac_objext" && ac_objext=o _LT_CC_BASENAME([$compiler]) # Only perform the check for file, if the check method requires it test -z "$MAGIC_CMD" && MAGIC_CMD=file case $deplibs_check_method in file_magic*) if test "$file_magic_cmd" = '$MAGIC_CMD'; then _LT_PATH_MAGIC fi ;; esac # Use C for the default configuration in the libtool script LT_SUPPORTED_TAG([CC]) _LT_LANG_C_CONFIG _LT_LANG_DEFAULT_CONFIG _LT_CONFIG_COMMANDS ])# _LT_SETUP # _LT_PREPARE_SED_QUOTE_VARS # -------------------------- # Define a few sed substitution that help us do robust quoting. m4_defun([_LT_PREPARE_SED_QUOTE_VARS], [# Backslashify metacharacters that are still active within # double-quoted strings. sed_quote_subst='s/\([["`$\\]]\)/\\\1/g' # Same as above, but do not quote variable references. double_quote_subst='s/\([["`\\]]\)/\\\1/g' # Sed substitution to delay expansion of an escaped shell variable in a # double_quote_subst'ed string. delay_variable_subst='s/\\\\\\\\\\\$/\\\\\\$/g' # Sed substitution to delay expansion of an escaped single quote. delay_single_quote_subst='s/'\''/'\'\\\\\\\'\''/g' # Sed substitution to avoid accidental globbing in evaled expressions no_glob_subst='s/\*/\\\*/g' ]) # _LT_PROG_LTMAIN # --------------- # Note that this code is called both from 'configure', and 'config.status' # now that we use AC_CONFIG_COMMANDS to generate libtool. Notably, # 'config.status' has no value for ac_aux_dir unless we are using Automake, # so we pass a copy along to make sure it has a sensible value anyway. m4_defun([_LT_PROG_LTMAIN], [m4_ifdef([AC_REQUIRE_AUX_FILE], [AC_REQUIRE_AUX_FILE([ltmain.sh])])dnl _LT_CONFIG_LIBTOOL_INIT([ac_aux_dir='$ac_aux_dir']) ltmain=$ac_aux_dir/ltmain.sh ])# _LT_PROG_LTMAIN # So that we can recreate a full libtool script including additional # tags, we accumulate the chunks of code to send to AC_CONFIG_COMMANDS # in macros and then make a single call at the end using the 'libtool' # label. # _LT_CONFIG_LIBTOOL_INIT([INIT-COMMANDS]) # ---------------------------------------- # Register INIT-COMMANDS to be passed to AC_CONFIG_COMMANDS later. m4_define([_LT_CONFIG_LIBTOOL_INIT], [m4_ifval([$1], [m4_append([_LT_OUTPUT_LIBTOOL_INIT], [$1 ])])]) # Initialize. m4_define([_LT_OUTPUT_LIBTOOL_INIT]) # _LT_CONFIG_LIBTOOL([COMMANDS]) # ------------------------------ # Register COMMANDS to be passed to AC_CONFIG_COMMANDS later. m4_define([_LT_CONFIG_LIBTOOL], [m4_ifval([$1], [m4_append([_LT_OUTPUT_LIBTOOL_COMMANDS], [$1 ])])]) # Initialize. m4_define([_LT_OUTPUT_LIBTOOL_COMMANDS]) # _LT_CONFIG_SAVE_COMMANDS([COMMANDS], [INIT_COMMANDS]) # ----------------------------------------------------- m4_defun([_LT_CONFIG_SAVE_COMMANDS], [_LT_CONFIG_LIBTOOL([$1]) _LT_CONFIG_LIBTOOL_INIT([$2]) ]) # _LT_FORMAT_COMMENT([COMMENT]) # ----------------------------- # Add leading comment marks to the start of each line, and a trailing # full-stop to the whole comment if one is not present already. m4_define([_LT_FORMAT_COMMENT], [m4_ifval([$1], [ m4_bpatsubst([m4_bpatsubst([$1], [^ *], [# ])], [['`$\]], [\\\&])]m4_bmatch([$1], [[!?.]$], [], [.]) )]) # _LT_DECL([CONFIGNAME], VARNAME, VALUE, [DESCRIPTION], [IS-TAGGED?]) # ------------------------------------------------------------------- # CONFIGNAME is the name given to the value in the libtool script. # VARNAME is the (base) name used in the configure script. # VALUE may be 0, 1 or 2 for a computed quote escaped value based on # VARNAME. Any other value will be used directly. m4_define([_LT_DECL], [lt_if_append_uniq([lt_decl_varnames], [$2], [, ], [lt_dict_add_subkey([lt_decl_dict], [$2], [libtool_name], [m4_ifval([$1], [$1], [$2])]) lt_dict_add_subkey([lt_decl_dict], [$2], [value], [$3]) m4_ifval([$4], [lt_dict_add_subkey([lt_decl_dict], [$2], [description], [$4])]) lt_dict_add_subkey([lt_decl_dict], [$2], [tagged?], [m4_ifval([$5], [yes], [no])])]) ]) # _LT_TAGDECL([CONFIGNAME], VARNAME, VALUE, [DESCRIPTION]) # -------------------------------------------------------- m4_define([_LT_TAGDECL], [_LT_DECL([$1], [$2], [$3], [$4], [yes])]) # lt_decl_tag_varnames([SEPARATOR], [VARNAME1...]) # ------------------------------------------------ m4_define([lt_decl_tag_varnames], [_lt_decl_filter([tagged?], [yes], $@)]) # _lt_decl_filter(SUBKEY, VALUE, [SEPARATOR], [VARNAME1..]) # --------------------------------------------------------- m4_define([_lt_decl_filter], [m4_case([$#], [0], [m4_fatal([$0: too few arguments: $#])], [1], [m4_fatal([$0: too few arguments: $#: $1])], [2], [lt_dict_filter([lt_decl_dict], [$1], [$2], [], lt_decl_varnames)], [3], [lt_dict_filter([lt_decl_dict], [$1], [$2], [$3], lt_decl_varnames)], [lt_dict_filter([lt_decl_dict], $@)])[]dnl ]) # lt_decl_quote_varnames([SEPARATOR], [VARNAME1...]) # -------------------------------------------------- m4_define([lt_decl_quote_varnames], [_lt_decl_filter([value], [1], $@)]) # lt_decl_dquote_varnames([SEPARATOR], [VARNAME1...]) # --------------------------------------------------- m4_define([lt_decl_dquote_varnames], [_lt_decl_filter([value], [2], $@)]) # lt_decl_varnames_tagged([SEPARATOR], [VARNAME1...]) # --------------------------------------------------- m4_define([lt_decl_varnames_tagged], [m4_assert([$# <= 2])dnl _$0(m4_quote(m4_default([$1], [[, ]])), m4_ifval([$2], [[$2]], [m4_dquote(lt_decl_tag_varnames)]), m4_split(m4_normalize(m4_quote(_LT_TAGS)), [ ]))]) m4_define([_lt_decl_varnames_tagged], [m4_ifval([$3], [lt_combine([$1], [$2], [_], $3)])]) # lt_decl_all_varnames([SEPARATOR], [VARNAME1...]) # ------------------------------------------------ m4_define([lt_decl_all_varnames], [_$0(m4_quote(m4_default([$1], [[, ]])), m4_if([$2], [], m4_quote(lt_decl_varnames), m4_quote(m4_shift($@))))[]dnl ]) m4_define([_lt_decl_all_varnames], [lt_join($@, lt_decl_varnames_tagged([$1], lt_decl_tag_varnames([[, ]], m4_shift($@))))dnl ]) # _LT_CONFIG_STATUS_DECLARE([VARNAME]) # ------------------------------------ # Quote a variable value, and forward it to 'config.status' so that its # declaration there will have the same value as in 'configure'. VARNAME # must have a single quote delimited value for this to work. m4_define([_LT_CONFIG_STATUS_DECLARE], [$1='`$ECHO "$][$1" | $SED "$delay_single_quote_subst"`']) # _LT_CONFIG_STATUS_DECLARATIONS # ------------------------------ # We delimit libtool config variables with single quotes, so when # we write them to config.status, we have to be sure to quote all # embedded single quotes properly. In configure, this macro expands # each variable declared with _LT_DECL (and _LT_TAGDECL) into: # # ='`$ECHO "$" | $SED "$delay_single_quote_subst"`' m4_defun([_LT_CONFIG_STATUS_DECLARATIONS], [m4_foreach([_lt_var], m4_quote(lt_decl_all_varnames), [m4_n([_LT_CONFIG_STATUS_DECLARE(_lt_var)])])]) # _LT_LIBTOOL_TAGS # ---------------- # Output comment and list of tags supported by the script m4_defun([_LT_LIBTOOL_TAGS], [_LT_FORMAT_COMMENT([The names of the tagged configurations supported by this script])dnl available_tags='_LT_TAGS'dnl ]) # _LT_LIBTOOL_DECLARE(VARNAME, [TAG]) # ----------------------------------- # Extract the dictionary values for VARNAME (optionally with TAG) and # expand to a commented shell variable setting: # # # Some comment about what VAR is for. # visible_name=$lt_internal_name m4_define([_LT_LIBTOOL_DECLARE], [_LT_FORMAT_COMMENT(m4_quote(lt_dict_fetch([lt_decl_dict], [$1], [description])))[]dnl m4_pushdef([_libtool_name], m4_quote(lt_dict_fetch([lt_decl_dict], [$1], [libtool_name])))[]dnl m4_case(m4_quote(lt_dict_fetch([lt_decl_dict], [$1], [value])), [0], [_libtool_name=[$]$1], [1], [_libtool_name=$lt_[]$1], [2], [_libtool_name=$lt_[]$1], [_libtool_name=lt_dict_fetch([lt_decl_dict], [$1], [value])])[]dnl m4_ifval([$2], [_$2])[]m4_popdef([_libtool_name])[]dnl ]) # _LT_LIBTOOL_CONFIG_VARS # ----------------------- # Produce commented declarations of non-tagged libtool config variables # suitable for insertion in the LIBTOOL CONFIG section of the 'libtool' # script. Tagged libtool config variables (even for the LIBTOOL CONFIG # section) are produced by _LT_LIBTOOL_TAG_VARS. m4_defun([_LT_LIBTOOL_CONFIG_VARS], [m4_foreach([_lt_var], m4_quote(_lt_decl_filter([tagged?], [no], [], lt_decl_varnames)), [m4_n([_LT_LIBTOOL_DECLARE(_lt_var)])])]) # _LT_LIBTOOL_TAG_VARS(TAG) # ------------------------- m4_define([_LT_LIBTOOL_TAG_VARS], [m4_foreach([_lt_var], m4_quote(lt_decl_tag_varnames), [m4_n([_LT_LIBTOOL_DECLARE(_lt_var, [$1])])])]) # _LT_TAGVAR(VARNAME, [TAGNAME]) # ------------------------------ m4_define([_LT_TAGVAR], [m4_ifval([$2], [$1_$2], [$1])]) # _LT_CONFIG_COMMANDS # ------------------- # Send accumulated output to $CONFIG_STATUS. Thanks to the lists of # variables for single and double quote escaping we saved from calls # to _LT_DECL, we can put quote escaped variables declarations # into 'config.status', and then the shell code to quote escape them in # for loops in 'config.status'. Finally, any additional code accumulated # from calls to _LT_CONFIG_LIBTOOL_INIT is expanded. m4_defun([_LT_CONFIG_COMMANDS], [AC_PROVIDE_IFELSE([LT_OUTPUT], dnl If the libtool generation code has been placed in $CONFIG_LT, dnl instead of duplicating it all over again into config.status, dnl then we will have config.status run $CONFIG_LT later, so it dnl needs to know what name is stored there: [AC_CONFIG_COMMANDS([libtool], [$SHELL $CONFIG_LT || AS_EXIT(1)], [CONFIG_LT='$CONFIG_LT'])], dnl If the libtool generation code is destined for config.status, dnl expand the accumulated commands and init code now: [AC_CONFIG_COMMANDS([libtool], [_LT_OUTPUT_LIBTOOL_COMMANDS], [_LT_OUTPUT_LIBTOOL_COMMANDS_INIT])]) ])#_LT_CONFIG_COMMANDS # Initialize. m4_define([_LT_OUTPUT_LIBTOOL_COMMANDS_INIT], [ # The HP-UX ksh and POSIX shell print the target directory to stdout # if CDPATH is set. (unset CDPATH) >/dev/null 2>&1 && unset CDPATH sed_quote_subst='$sed_quote_subst' double_quote_subst='$double_quote_subst' delay_variable_subst='$delay_variable_subst' _LT_CONFIG_STATUS_DECLARATIONS LTCC='$LTCC' LTCFLAGS='$LTCFLAGS' compiler='$compiler_DEFAULT' # A function that is used when there is no print builtin or printf. func_fallback_echo () { eval 'cat <<_LTECHO_EOF \$[]1 _LTECHO_EOF' } # Quote evaled strings. for var in lt_decl_all_varnames([[ \ ]], lt_decl_quote_varnames); do case \`eval \\\\\$ECHO \\\\""\\\\\$\$var"\\\\"\` in *[[\\\\\\\`\\"\\\$]]*) eval "lt_\$var=\\\\\\"\\\`\\\$ECHO \\"\\\$\$var\\" | \\\$SED \\"\\\$sed_quote_subst\\"\\\`\\\\\\"" ## exclude from sc_prohibit_nested_quotes ;; *) eval "lt_\$var=\\\\\\"\\\$\$var\\\\\\"" ;; esac done # Double-quote double-evaled strings. for var in lt_decl_all_varnames([[ \ ]], lt_decl_dquote_varnames); do case \`eval \\\\\$ECHO \\\\""\\\\\$\$var"\\\\"\` in *[[\\\\\\\`\\"\\\$]]*) eval "lt_\$var=\\\\\\"\\\`\\\$ECHO \\"\\\$\$var\\" | \\\$SED -e \\"\\\$double_quote_subst\\" -e \\"\\\$sed_quote_subst\\" -e \\"\\\$delay_variable_subst\\"\\\`\\\\\\"" ## exclude from sc_prohibit_nested_quotes ;; *) eval "lt_\$var=\\\\\\"\\\$\$var\\\\\\"" ;; esac done _LT_OUTPUT_LIBTOOL_INIT ]) # _LT_GENERATED_FILE_INIT(FILE, [COMMENT]) # ------------------------------------ # Generate a child script FILE with all initialization necessary to # reuse the environment learned by the parent script, and make the # file executable. If COMMENT is supplied, it is inserted after the # '#!' sequence but before initialization text begins. After this # macro, additional text can be appended to FILE to form the body of # the child script. The macro ends with non-zero status if the # file could not be fully written (such as if the disk is full). m4_ifdef([AS_INIT_GENERATED], [m4_defun([_LT_GENERATED_FILE_INIT],[AS_INIT_GENERATED($@)])], [m4_defun([_LT_GENERATED_FILE_INIT], [m4_require([AS_PREPARE])]dnl [m4_pushdef([AS_MESSAGE_LOG_FD])]dnl [lt_write_fail=0 cat >$1 <<_ASEOF || lt_write_fail=1 #! $SHELL # Generated by $as_me. $2 SHELL=\${CONFIG_SHELL-$SHELL} export SHELL _ASEOF cat >>$1 <<\_ASEOF || lt_write_fail=1 AS_SHELL_SANITIZE _AS_PREPARE exec AS_MESSAGE_FD>&1 _ASEOF test 0 = "$lt_write_fail" && chmod +x $1[]dnl m4_popdef([AS_MESSAGE_LOG_FD])])])# _LT_GENERATED_FILE_INIT # LT_OUTPUT # --------- # This macro allows early generation of the libtool script (before # AC_OUTPUT is called), incase it is used in configure for compilation # tests. AC_DEFUN([LT_OUTPUT], [: ${CONFIG_LT=./config.lt} AC_MSG_NOTICE([creating $CONFIG_LT]) _LT_GENERATED_FILE_INIT(["$CONFIG_LT"], [# Run this file to recreate a libtool stub with the current configuration.]) cat >>"$CONFIG_LT" <<\_LTEOF lt_cl_silent=false exec AS_MESSAGE_LOG_FD>>config.log { echo AS_BOX([Running $as_me.]) } >&AS_MESSAGE_LOG_FD lt_cl_help="\ '$as_me' creates a local libtool stub from the current configuration, for use in further configure time tests before the real libtool is generated. Usage: $[0] [[OPTIONS]] -h, --help print this help, then exit -V, --version print version number, then exit -q, --quiet do not print progress messages -d, --debug don't remove temporary files Report bugs to ." lt_cl_version="\ m4_ifset([AC_PACKAGE_NAME], [AC_PACKAGE_NAME ])config.lt[]dnl m4_ifset([AC_PACKAGE_VERSION], [ AC_PACKAGE_VERSION]) configured by $[0], generated by m4_PACKAGE_STRING. Copyright (C) 2011 Free Software Foundation, Inc. This config.lt script is free software; the Free Software Foundation gives unlimited permision to copy, distribute and modify it." while test 0 != $[#] do case $[1] in --version | --v* | -V ) echo "$lt_cl_version"; exit 0 ;; --help | --h* | -h ) echo "$lt_cl_help"; exit 0 ;; --debug | --d* | -d ) debug=: ;; --quiet | --q* | --silent | --s* | -q ) lt_cl_silent=: ;; -*) AC_MSG_ERROR([unrecognized option: $[1] Try '$[0] --help' for more information.]) ;; *) AC_MSG_ERROR([unrecognized argument: $[1] Try '$[0] --help' for more information.]) ;; esac shift done if $lt_cl_silent; then exec AS_MESSAGE_FD>/dev/null fi _LTEOF cat >>"$CONFIG_LT" <<_LTEOF _LT_OUTPUT_LIBTOOL_COMMANDS_INIT _LTEOF cat >>"$CONFIG_LT" <<\_LTEOF AC_MSG_NOTICE([creating $ofile]) _LT_OUTPUT_LIBTOOL_COMMANDS AS_EXIT(0) _LTEOF chmod +x "$CONFIG_LT" # configure is writing to config.log, but config.lt does its own redirection, # appending to config.log, which fails on DOS, as config.log is still kept # open by configure. Here we exec the FD to /dev/null, effectively closing # config.log, so it can be properly (re)opened and appended to by config.lt. lt_cl_success=: test yes = "$silent" && lt_config_lt_args="$lt_config_lt_args --quiet" exec AS_MESSAGE_LOG_FD>/dev/null $SHELL "$CONFIG_LT" $lt_config_lt_args || lt_cl_success=false exec AS_MESSAGE_LOG_FD>>config.log $lt_cl_success || AS_EXIT(1) ])# LT_OUTPUT # _LT_CONFIG(TAG) # --------------- # If TAG is the built-in tag, create an initial libtool script with a # default configuration from the untagged config vars. Otherwise add code # to config.status for appending the configuration named by TAG from the # matching tagged config vars. m4_defun([_LT_CONFIG], [m4_require([_LT_FILEUTILS_DEFAULTS])dnl _LT_CONFIG_SAVE_COMMANDS([ m4_define([_LT_TAG], m4_if([$1], [], [C], [$1]))dnl m4_if(_LT_TAG, [C], [ # See if we are running on zsh, and set the options that allow our # commands through without removal of \ escapes. if test -n "${ZSH_VERSION+set}"; then setopt NO_GLOB_SUBST fi cfgfile=${ofile}T trap "$RM \"$cfgfile\"; exit 1" 1 2 15 $RM "$cfgfile" cat <<_LT_EOF >> "$cfgfile" #! $SHELL # Generated automatically by $as_me ($PACKAGE) $VERSION # NOTE: Changes made to this file will be lost: look at ltmain.sh. # Provide generalized library-building support services. # Written by Gordon Matzigkeit, 1996 _LT_COPYING _LT_LIBTOOL_TAGS # Configured defaults for sys_lib_dlsearch_path munging. : \${LT_SYS_LIBRARY_PATH="$configure_time_lt_sys_library_path"} # ### BEGIN LIBTOOL CONFIG _LT_LIBTOOL_CONFIG_VARS _LT_LIBTOOL_TAG_VARS # ### END LIBTOOL CONFIG _LT_EOF cat <<'_LT_EOF' >> "$cfgfile" # ### BEGIN FUNCTIONS SHARED WITH CONFIGURE _LT_PREPARE_MUNGE_PATH_LIST _LT_PREPARE_CC_BASENAME # ### END FUNCTIONS SHARED WITH CONFIGURE _LT_EOF case $host_os in aix3*) cat <<\_LT_EOF >> "$cfgfile" # AIX sometimes has problems with the GCC collect2 program. For some # reason, if we set the COLLECT_NAMES environment variable, the problems # vanish in a puff of smoke. if test set != "${COLLECT_NAMES+set}"; then COLLECT_NAMES= export COLLECT_NAMES fi _LT_EOF ;; esac _LT_PROG_LTMAIN # We use sed instead of cat because bash on DJGPP gets confused if # if finds mixed CR/LF and LF-only lines. Since sed operates in # text mode, it properly converts lines to CR/LF. This bash problem # is reportedly fixed, but why not run on old versions too? $SED '$q' "$ltmain" >> "$cfgfile" \ || (rm -f "$cfgfile"; exit 1) mv -f "$cfgfile" "$ofile" || (rm -f "$ofile" && cp "$cfgfile" "$ofile" && rm -f "$cfgfile") chmod +x "$ofile" ], [cat <<_LT_EOF >> "$ofile" dnl Unfortunately we have to use $1 here, since _LT_TAG is not expanded dnl in a comment (ie after a #). # ### BEGIN LIBTOOL TAG CONFIG: $1 _LT_LIBTOOL_TAG_VARS(_LT_TAG) # ### END LIBTOOL TAG CONFIG: $1 _LT_EOF ])dnl /m4_if ], [m4_if([$1], [], [ PACKAGE='$PACKAGE' VERSION='$VERSION' RM='$RM' ofile='$ofile'], []) ])dnl /_LT_CONFIG_SAVE_COMMANDS ])# _LT_CONFIG # LT_SUPPORTED_TAG(TAG) # --------------------- # Trace this macro to discover what tags are supported by the libtool # --tag option, using: # autoconf --trace 'LT_SUPPORTED_TAG:$1' AC_DEFUN([LT_SUPPORTED_TAG], []) # C support is built-in for now m4_define([_LT_LANG_C_enabled], []) m4_define([_LT_TAGS], []) # LT_LANG(LANG) # ------------- # Enable libtool support for the given language if not already enabled. AC_DEFUN([LT_LANG], [AC_BEFORE([$0], [LT_OUTPUT])dnl m4_case([$1], [C], [_LT_LANG(C)], [C++], [_LT_LANG(CXX)], [Go], [_LT_LANG(GO)], [Java], [_LT_LANG(GCJ)], [Fortran 77], [_LT_LANG(F77)], [Fortran], [_LT_LANG(FC)], [Windows Resource], [_LT_LANG(RC)], [m4_ifdef([_LT_LANG_]$1[_CONFIG], [_LT_LANG($1)], [m4_fatal([$0: unsupported language: "$1"])])])dnl ])# LT_LANG # _LT_LANG(LANGNAME) # ------------------ m4_defun([_LT_LANG], [m4_ifdef([_LT_LANG_]$1[_enabled], [], [LT_SUPPORTED_TAG([$1])dnl m4_append([_LT_TAGS], [$1 ])dnl m4_define([_LT_LANG_]$1[_enabled], [])dnl _LT_LANG_$1_CONFIG($1)])dnl ])# _LT_LANG m4_ifndef([AC_PROG_GO], [ # NOTE: This macro has been submitted for inclusion into # # GNU Autoconf as AC_PROG_GO. When it is available in # # a released version of Autoconf we should remove this # # macro and use it instead. # m4_defun([AC_PROG_GO], [AC_LANG_PUSH(Go)dnl AC_ARG_VAR([GOC], [Go compiler command])dnl AC_ARG_VAR([GOFLAGS], [Go compiler flags])dnl _AC_ARG_VAR_LDFLAGS()dnl AC_CHECK_TOOL(GOC, gccgo) if test -z "$GOC"; then if test -n "$ac_tool_prefix"; then AC_CHECK_PROG(GOC, [${ac_tool_prefix}gccgo], [${ac_tool_prefix}gccgo]) fi fi if test -z "$GOC"; then AC_CHECK_PROG(GOC, gccgo, gccgo, false) fi ])#m4_defun ])#m4_ifndef # _LT_LANG_DEFAULT_CONFIG # ----------------------- m4_defun([_LT_LANG_DEFAULT_CONFIG], [AC_PROVIDE_IFELSE([AC_PROG_CXX], [LT_LANG(CXX)], [m4_define([AC_PROG_CXX], defn([AC_PROG_CXX])[LT_LANG(CXX)])]) AC_PROVIDE_IFELSE([AC_PROG_F77], [LT_LANG(F77)], [m4_define([AC_PROG_F77], defn([AC_PROG_F77])[LT_LANG(F77)])]) AC_PROVIDE_IFELSE([AC_PROG_FC], [LT_LANG(FC)], [m4_define([AC_PROG_FC], defn([AC_PROG_FC])[LT_LANG(FC)])]) dnl The call to [A][M_PROG_GCJ] is quoted like that to stop aclocal dnl pulling things in needlessly. AC_PROVIDE_IFELSE([AC_PROG_GCJ], [LT_LANG(GCJ)], [AC_PROVIDE_IFELSE([A][M_PROG_GCJ], [LT_LANG(GCJ)], [AC_PROVIDE_IFELSE([LT_PROG_GCJ], [LT_LANG(GCJ)], [m4_ifdef([AC_PROG_GCJ], [m4_define([AC_PROG_GCJ], defn([AC_PROG_GCJ])[LT_LANG(GCJ)])]) m4_ifdef([A][M_PROG_GCJ], [m4_define([A][M_PROG_GCJ], defn([A][M_PROG_GCJ])[LT_LANG(GCJ)])]) m4_ifdef([LT_PROG_GCJ], [m4_define([LT_PROG_GCJ], defn([LT_PROG_GCJ])[LT_LANG(GCJ)])])])])]) AC_PROVIDE_IFELSE([AC_PROG_GO], [LT_LANG(GO)], [m4_define([AC_PROG_GO], defn([AC_PROG_GO])[LT_LANG(GO)])]) AC_PROVIDE_IFELSE([LT_PROG_RC], [LT_LANG(RC)], [m4_define([LT_PROG_RC], defn([LT_PROG_RC])[LT_LANG(RC)])]) ])# _LT_LANG_DEFAULT_CONFIG # Obsolete macros: AU_DEFUN([AC_LIBTOOL_CXX], [LT_LANG(C++)]) AU_DEFUN([AC_LIBTOOL_F77], [LT_LANG(Fortran 77)]) AU_DEFUN([AC_LIBTOOL_FC], [LT_LANG(Fortran)]) AU_DEFUN([AC_LIBTOOL_GCJ], [LT_LANG(Java)]) AU_DEFUN([AC_LIBTOOL_RC], [LT_LANG(Windows Resource)]) dnl aclocal-1.4 backwards compatibility: dnl AC_DEFUN([AC_LIBTOOL_CXX], []) dnl AC_DEFUN([AC_LIBTOOL_F77], []) dnl AC_DEFUN([AC_LIBTOOL_FC], []) dnl AC_DEFUN([AC_LIBTOOL_GCJ], []) dnl AC_DEFUN([AC_LIBTOOL_RC], []) # _LT_TAG_COMPILER # ---------------- m4_defun([_LT_TAG_COMPILER], [AC_REQUIRE([AC_PROG_CC])dnl _LT_DECL([LTCC], [CC], [1], [A C compiler])dnl _LT_DECL([LTCFLAGS], [CFLAGS], [1], [LTCC compiler flags])dnl _LT_TAGDECL([CC], [compiler], [1], [A language specific compiler])dnl _LT_TAGDECL([with_gcc], [GCC], [0], [Is the compiler the GNU compiler?])dnl # If no C compiler was specified, use CC. LTCC=${LTCC-"$CC"} # If no C compiler flags were specified, use CFLAGS. LTCFLAGS=${LTCFLAGS-"$CFLAGS"} # Allow CC to be a program name with arguments. compiler=$CC ])# _LT_TAG_COMPILER # _LT_COMPILER_BOILERPLATE # ------------------------ # Check for compiler boilerplate output or warnings with # the simple compiler test code. m4_defun([_LT_COMPILER_BOILERPLATE], [m4_require([_LT_DECL_SED])dnl ac_outfile=conftest.$ac_objext echo "$lt_simple_compile_test_code" >conftest.$ac_ext eval "$ac_compile" 2>&1 >/dev/null | $SED '/^$/d; /^ *+/d' >conftest.err _lt_compiler_boilerplate=`cat conftest.err` $RM conftest* ])# _LT_COMPILER_BOILERPLATE # _LT_LINKER_BOILERPLATE # ---------------------- # Check for linker boilerplate output or warnings with # the simple link test code. m4_defun([_LT_LINKER_BOILERPLATE], [m4_require([_LT_DECL_SED])dnl ac_outfile=conftest.$ac_objext echo "$lt_simple_link_test_code" >conftest.$ac_ext eval "$ac_link" 2>&1 >/dev/null | $SED '/^$/d; /^ *+/d' >conftest.err _lt_linker_boilerplate=`cat conftest.err` $RM -r conftest* ])# _LT_LINKER_BOILERPLATE # _LT_REQUIRED_DARWIN_CHECKS # ------------------------- m4_defun_once([_LT_REQUIRED_DARWIN_CHECKS],[ case $host_os in rhapsody* | darwin*) AC_CHECK_TOOL([DSYMUTIL], [dsymutil], [:]) AC_CHECK_TOOL([NMEDIT], [nmedit], [:]) AC_CHECK_TOOL([LIPO], [lipo], [:]) AC_CHECK_TOOL([OTOOL], [otool], [:]) AC_CHECK_TOOL([OTOOL64], [otool64], [:]) _LT_DECL([], [DSYMUTIL], [1], [Tool to manipulate archived DWARF debug symbol files on Mac OS X]) _LT_DECL([], [NMEDIT], [1], [Tool to change global to local symbols on Mac OS X]) _LT_DECL([], [LIPO], [1], [Tool to manipulate fat objects and archives on Mac OS X]) _LT_DECL([], [OTOOL], [1], [ldd/readelf like tool for Mach-O binaries on Mac OS X]) _LT_DECL([], [OTOOL64], [1], [ldd/readelf like tool for 64 bit Mach-O binaries on Mac OS X 10.4]) AC_CACHE_CHECK([for -single_module linker flag],[lt_cv_apple_cc_single_mod], [lt_cv_apple_cc_single_mod=no if test -z "$LT_MULTI_MODULE"; then # By default we will add the -single_module flag. You can override # by either setting the environment variable LT_MULTI_MODULE # non-empty at configure time, or by adding -multi_module to the # link flags. rm -rf libconftest.dylib* echo "int foo(void){return 1;}" > conftest.c echo "$LTCC $LTCFLAGS $LDFLAGS -o libconftest.dylib \ -dynamiclib -Wl,-single_module conftest.c" >&AS_MESSAGE_LOG_FD $LTCC $LTCFLAGS $LDFLAGS -o libconftest.dylib \ -dynamiclib -Wl,-single_module conftest.c 2>conftest.err _lt_result=$? # If there is a non-empty error log, and "single_module" # appears in it, assume the flag caused a linker warning if test -s conftest.err && $GREP single_module conftest.err; then cat conftest.err >&AS_MESSAGE_LOG_FD # Otherwise, if the output was created with a 0 exit code from # the compiler, it worked. elif test -f libconftest.dylib && test 0 = "$_lt_result"; then lt_cv_apple_cc_single_mod=yes else cat conftest.err >&AS_MESSAGE_LOG_FD fi rm -rf libconftest.dylib* rm -f conftest.* fi]) AC_CACHE_CHECK([for -exported_symbols_list linker flag], [lt_cv_ld_exported_symbols_list], [lt_cv_ld_exported_symbols_list=no save_LDFLAGS=$LDFLAGS echo "_main" > conftest.sym LDFLAGS="$LDFLAGS -Wl,-exported_symbols_list,conftest.sym" AC_LINK_IFELSE([AC_LANG_PROGRAM([],[])], [lt_cv_ld_exported_symbols_list=yes], [lt_cv_ld_exported_symbols_list=no]) LDFLAGS=$save_LDFLAGS ]) AC_CACHE_CHECK([for -force_load linker flag],[lt_cv_ld_force_load], [lt_cv_ld_force_load=no cat > conftest.c << _LT_EOF int forced_loaded() { return 2;} _LT_EOF echo "$LTCC $LTCFLAGS -c -o conftest.o conftest.c" >&AS_MESSAGE_LOG_FD $LTCC $LTCFLAGS -c -o conftest.o conftest.c 2>&AS_MESSAGE_LOG_FD echo "$AR $AR_FLAGS libconftest.a conftest.o" >&AS_MESSAGE_LOG_FD $AR $AR_FLAGS libconftest.a conftest.o 2>&AS_MESSAGE_LOG_FD echo "$RANLIB libconftest.a" >&AS_MESSAGE_LOG_FD $RANLIB libconftest.a 2>&AS_MESSAGE_LOG_FD cat > conftest.c << _LT_EOF int main() { return 0;} _LT_EOF echo "$LTCC $LTCFLAGS $LDFLAGS -o conftest conftest.c -Wl,-force_load,./libconftest.a" >&AS_MESSAGE_LOG_FD $LTCC $LTCFLAGS $LDFLAGS -o conftest conftest.c -Wl,-force_load,./libconftest.a 2>conftest.err _lt_result=$? if test -s conftest.err && $GREP force_load conftest.err; then cat conftest.err >&AS_MESSAGE_LOG_FD elif test -f conftest && test 0 = "$_lt_result" && $GREP forced_load conftest >/dev/null 2>&1; then lt_cv_ld_force_load=yes else cat conftest.err >&AS_MESSAGE_LOG_FD fi rm -f conftest.err libconftest.a conftest conftest.c rm -rf conftest.dSYM ]) case $host_os in rhapsody* | darwin1.[[012]]) _lt_dar_allow_undefined='$wl-undefined ${wl}suppress' ;; darwin1.*) _lt_dar_allow_undefined='$wl-flat_namespace $wl-undefined ${wl}suppress' ;; darwin*) case $MACOSX_DEPLOYMENT_TARGET,$host in 10.[[012]],*|,*powerpc*-darwin[[5-8]]*) _lt_dar_allow_undefined='$wl-flat_namespace $wl-undefined ${wl}suppress' ;; *) _lt_dar_allow_undefined='$wl-undefined ${wl}dynamic_lookup' ;; esac ;; esac if test yes = "$lt_cv_apple_cc_single_mod"; then _lt_dar_single_mod='$single_module' fi if test yes = "$lt_cv_ld_exported_symbols_list"; then _lt_dar_export_syms=' $wl-exported_symbols_list,$output_objdir/$libname-symbols.expsym' else _lt_dar_export_syms='~$NMEDIT -s $output_objdir/$libname-symbols.expsym $lib' fi if test : != "$DSYMUTIL" && test no = "$lt_cv_ld_force_load"; then _lt_dsymutil='~$DSYMUTIL $lib || :' else _lt_dsymutil= fi ;; esac ]) # _LT_DARWIN_LINKER_FEATURES([TAG]) # --------------------------------- # Checks for linker and compiler features on darwin m4_defun([_LT_DARWIN_LINKER_FEATURES], [ m4_require([_LT_REQUIRED_DARWIN_CHECKS]) _LT_TAGVAR(archive_cmds_need_lc, $1)=no _LT_TAGVAR(hardcode_direct, $1)=no _LT_TAGVAR(hardcode_automatic, $1)=yes _LT_TAGVAR(hardcode_shlibpath_var, $1)=unsupported if test yes = "$lt_cv_ld_force_load"; then _LT_TAGVAR(whole_archive_flag_spec, $1)='`for conv in $convenience\"\"; do test -n \"$conv\" && new_convenience=\"$new_convenience $wl-force_load,$conv\"; done; func_echo_all \"$new_convenience\"`' m4_case([$1], [F77], [_LT_TAGVAR(compiler_needs_object, $1)=yes], [FC], [_LT_TAGVAR(compiler_needs_object, $1)=yes]) else _LT_TAGVAR(whole_archive_flag_spec, $1)='' fi _LT_TAGVAR(link_all_deplibs, $1)=yes _LT_TAGVAR(allow_undefined_flag, $1)=$_lt_dar_allow_undefined case $cc_basename in ifort*|nagfor*) _lt_dar_can_shared=yes ;; *) _lt_dar_can_shared=$GCC ;; esac if test yes = "$_lt_dar_can_shared"; then output_verbose_link_cmd=func_echo_all _LT_TAGVAR(archive_cmds, $1)="\$CC -dynamiclib \$allow_undefined_flag -o \$lib \$libobjs \$deplibs \$compiler_flags -install_name \$rpath/\$soname \$verstring $_lt_dar_single_mod$_lt_dsymutil" _LT_TAGVAR(module_cmds, $1)="\$CC \$allow_undefined_flag -o \$lib -bundle \$libobjs \$deplibs \$compiler_flags$_lt_dsymutil" _LT_TAGVAR(archive_expsym_cmds, $1)="$SED 's|^|_|' < \$export_symbols > \$output_objdir/\$libname-symbols.expsym~\$CC -dynamiclib \$allow_undefined_flag -o \$lib \$libobjs \$deplibs \$compiler_flags -install_name \$rpath/\$soname \$verstring $_lt_dar_single_mod$_lt_dar_export_syms$_lt_dsymutil" _LT_TAGVAR(module_expsym_cmds, $1)="$SED -e 's|^|_|' < \$export_symbols > \$output_objdir/\$libname-symbols.expsym~\$CC \$allow_undefined_flag -o \$lib -bundle \$libobjs \$deplibs \$compiler_flags$_lt_dar_export_syms$_lt_dsymutil" m4_if([$1], [CXX], [ if test yes != "$lt_cv_apple_cc_single_mod"; then _LT_TAGVAR(archive_cmds, $1)="\$CC -r -keep_private_externs -nostdlib -o \$lib-master.o \$libobjs~\$CC -dynamiclib \$allow_undefined_flag -o \$lib \$lib-master.o \$deplibs \$compiler_flags -install_name \$rpath/\$soname \$verstring$_lt_dsymutil" _LT_TAGVAR(archive_expsym_cmds, $1)="$SED 's|^|_|' < \$export_symbols > \$output_objdir/\$libname-symbols.expsym~\$CC -r -keep_private_externs -nostdlib -o \$lib-master.o \$libobjs~\$CC -dynamiclib \$allow_undefined_flag -o \$lib \$lib-master.o \$deplibs \$compiler_flags -install_name \$rpath/\$soname \$verstring$_lt_dar_export_syms$_lt_dsymutil" fi ],[]) else _LT_TAGVAR(ld_shlibs, $1)=no fi ]) # _LT_SYS_MODULE_PATH_AIX([TAGNAME]) # ---------------------------------- # Links a minimal program and checks the executable # for the system default hardcoded library path. In most cases, # this is /usr/lib:/lib, but when the MPI compilers are used # the location of the communication and MPI libs are included too. # If we don't find anything, use the default library path according # to the aix ld manual. # Store the results from the different compilers for each TAGNAME. # Allow to override them for all tags through lt_cv_aix_libpath. m4_defun([_LT_SYS_MODULE_PATH_AIX], [m4_require([_LT_DECL_SED])dnl if test set = "${lt_cv_aix_libpath+set}"; then aix_libpath=$lt_cv_aix_libpath else AC_CACHE_VAL([_LT_TAGVAR([lt_cv_aix_libpath_], [$1])], [AC_LINK_IFELSE([AC_LANG_PROGRAM],[ lt_aix_libpath_sed='[ /Import File Strings/,/^$/ { /^0/ { s/^0 *\([^ ]*\) *$/\1/ p } }]' _LT_TAGVAR([lt_cv_aix_libpath_], [$1])=`dump -H conftest$ac_exeext 2>/dev/null | $SED -n -e "$lt_aix_libpath_sed"` # Check for a 64-bit object if we didn't find anything. if test -z "$_LT_TAGVAR([lt_cv_aix_libpath_], [$1])"; then _LT_TAGVAR([lt_cv_aix_libpath_], [$1])=`dump -HX64 conftest$ac_exeext 2>/dev/null | $SED -n -e "$lt_aix_libpath_sed"` fi],[]) if test -z "$_LT_TAGVAR([lt_cv_aix_libpath_], [$1])"; then _LT_TAGVAR([lt_cv_aix_libpath_], [$1])=/usr/lib:/lib fi ]) aix_libpath=$_LT_TAGVAR([lt_cv_aix_libpath_], [$1]) fi ])# _LT_SYS_MODULE_PATH_AIX # _LT_SHELL_INIT(ARG) # ------------------- m4_define([_LT_SHELL_INIT], [m4_divert_text([M4SH-INIT], [$1 ])])# _LT_SHELL_INIT # _LT_PROG_ECHO_BACKSLASH # ----------------------- # Find how we can fake an echo command that does not interpret backslash. # In particular, with Autoconf 2.60 or later we add some code to the start # of the generated configure script that will find a shell with a builtin # printf (that we can use as an echo command). m4_defun([_LT_PROG_ECHO_BACKSLASH], [ECHO='\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\' ECHO=$ECHO$ECHO$ECHO$ECHO$ECHO ECHO=$ECHO$ECHO$ECHO$ECHO$ECHO$ECHO AC_MSG_CHECKING([how to print strings]) # Test print first, because it will be a builtin if present. if test "X`( print -r -- -n ) 2>/dev/null`" = X-n && \ test "X`print -r -- $ECHO 2>/dev/null`" = "X$ECHO"; then ECHO='print -r --' elif test "X`printf %s $ECHO 2>/dev/null`" = "X$ECHO"; then ECHO='printf %s\n' else # Use this function as a fallback that always works. func_fallback_echo () { eval 'cat <<_LTECHO_EOF $[]1 _LTECHO_EOF' } ECHO='func_fallback_echo' fi # func_echo_all arg... # Invoke $ECHO with all args, space-separated. func_echo_all () { $ECHO "$*" } case $ECHO in printf*) AC_MSG_RESULT([printf]) ;; print*) AC_MSG_RESULT([print -r]) ;; *) AC_MSG_RESULT([cat]) ;; esac m4_ifdef([_AS_DETECT_SUGGESTED], [_AS_DETECT_SUGGESTED([ test -n "${ZSH_VERSION+set}${BASH_VERSION+set}" || ( ECHO='\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\' ECHO=$ECHO$ECHO$ECHO$ECHO$ECHO ECHO=$ECHO$ECHO$ECHO$ECHO$ECHO$ECHO PATH=/empty FPATH=/empty; export PATH FPATH test "X`printf %s $ECHO`" = "X$ECHO" \ || test "X`print -r -- $ECHO`" = "X$ECHO" )])]) _LT_DECL([], [SHELL], [1], [Shell to use when invoking shell scripts]) _LT_DECL([], [ECHO], [1], [An echo program that protects backslashes]) ])# _LT_PROG_ECHO_BACKSLASH # _LT_WITH_SYSROOT # ---------------- AC_DEFUN([_LT_WITH_SYSROOT], [m4_require([_LT_DECL_SED])dnl AC_MSG_CHECKING([for sysroot]) AC_ARG_WITH([sysroot], [AS_HELP_STRING([--with-sysroot@<:@=DIR@:>@], [Search for dependent libraries within DIR (or the compiler's sysroot if not specified).])], [], [with_sysroot=no]) dnl lt_sysroot will always be passed unquoted. We quote it here dnl in case the user passed a directory name. lt_sysroot= case $with_sysroot in #( yes) if test yes = "$GCC"; then lt_sysroot=`$CC --print-sysroot 2>/dev/null` fi ;; #( /*) lt_sysroot=`echo "$with_sysroot" | $SED -e "$sed_quote_subst"` ;; #( no|'') ;; #( *) AC_MSG_RESULT([$with_sysroot]) AC_MSG_ERROR([The sysroot must be an absolute path.]) ;; esac AC_MSG_RESULT([${lt_sysroot:-no}]) _LT_DECL([], [lt_sysroot], [0], [The root where to search for ]dnl [dependent libraries, and where our libraries should be installed.])]) # _LT_ENABLE_LOCK # --------------- m4_defun([_LT_ENABLE_LOCK], [AC_ARG_ENABLE([libtool-lock], [AS_HELP_STRING([--disable-libtool-lock], [avoid locking (might break parallel builds)])]) test no = "$enable_libtool_lock" || enable_libtool_lock=yes # Some flags need to be propagated to the compiler or linker for good # libtool support. case $host in ia64-*-hpux*) # Find out what ABI is being produced by ac_compile, and set mode # options accordingly. echo 'int i;' > conftest.$ac_ext if AC_TRY_EVAL(ac_compile); then case `$FILECMD conftest.$ac_objext` in *ELF-32*) HPUX_IA64_MODE=32 ;; *ELF-64*) HPUX_IA64_MODE=64 ;; esac fi rm -rf conftest* ;; *-*-irix6*) # Find out what ABI is being produced by ac_compile, and set linker # options accordingly. echo '[#]line '$LINENO' "configure"' > conftest.$ac_ext if AC_TRY_EVAL(ac_compile); then if test yes = "$lt_cv_prog_gnu_ld"; then case `$FILECMD conftest.$ac_objext` in *32-bit*) LD="${LD-ld} -melf32bsmip" ;; *N32*) LD="${LD-ld} -melf32bmipn32" ;; *64-bit*) LD="${LD-ld} -melf64bmip" ;; esac else case `$FILECMD conftest.$ac_objext` in *32-bit*) LD="${LD-ld} -32" ;; *N32*) LD="${LD-ld} -n32" ;; *64-bit*) LD="${LD-ld} -64" ;; esac fi fi rm -rf conftest* ;; mips64*-*linux*) # Find out what ABI is being produced by ac_compile, and set linker # options accordingly. echo '[#]line '$LINENO' "configure"' > conftest.$ac_ext if AC_TRY_EVAL(ac_compile); then emul=elf case `$FILECMD conftest.$ac_objext` in *32-bit*) emul="${emul}32" ;; *64-bit*) emul="${emul}64" ;; esac case `$FILECMD conftest.$ac_objext` in *MSB*) emul="${emul}btsmip" ;; *LSB*) emul="${emul}ltsmip" ;; esac case `$FILECMD conftest.$ac_objext` in *N32*) emul="${emul}n32" ;; esac LD="${LD-ld} -m $emul" fi rm -rf conftest* ;; x86_64-*kfreebsd*-gnu|x86_64-*linux*|powerpc*-*linux*| \ s390*-*linux*|s390*-*tpf*|sparc*-*linux*) # Find out what ABI is being produced by ac_compile, and set linker # options accordingly. Note that the listed cases only cover the # situations where additional linker options are needed (such as when # doing 32-bit compilation for a host where ld defaults to 64-bit, or # vice versa); the common cases where no linker options are needed do # not appear in the list. echo 'int i;' > conftest.$ac_ext if AC_TRY_EVAL(ac_compile); then case `$FILECMD conftest.o` in *32-bit*) case $host in x86_64-*kfreebsd*-gnu) LD="${LD-ld} -m elf_i386_fbsd" ;; x86_64-*linux*) case `$FILECMD conftest.o` in *x86-64*) LD="${LD-ld} -m elf32_x86_64" ;; *) LD="${LD-ld} -m elf_i386" ;; esac ;; powerpc64le-*linux*) LD="${LD-ld} -m elf32lppclinux" ;; powerpc64-*linux*) LD="${LD-ld} -m elf32ppclinux" ;; s390x-*linux*) LD="${LD-ld} -m elf_s390" ;; sparc64-*linux*) LD="${LD-ld} -m elf32_sparc" ;; esac ;; *64-bit*) case $host in x86_64-*kfreebsd*-gnu) LD="${LD-ld} -m elf_x86_64_fbsd" ;; x86_64-*linux*) LD="${LD-ld} -m elf_x86_64" ;; powerpcle-*linux*) LD="${LD-ld} -m elf64lppc" ;; powerpc-*linux*) LD="${LD-ld} -m elf64ppc" ;; s390*-*linux*|s390*-*tpf*) LD="${LD-ld} -m elf64_s390" ;; sparc*-*linux*) LD="${LD-ld} -m elf64_sparc" ;; esac ;; esac fi rm -rf conftest* ;; *-*-sco3.2v5*) # On SCO OpenServer 5, we need -belf to get full-featured binaries. SAVE_CFLAGS=$CFLAGS CFLAGS="$CFLAGS -belf" AC_CACHE_CHECK([whether the C compiler needs -belf], lt_cv_cc_needs_belf, [AC_LANG_PUSH(C) AC_LINK_IFELSE([AC_LANG_PROGRAM([[]],[[]])],[lt_cv_cc_needs_belf=yes],[lt_cv_cc_needs_belf=no]) AC_LANG_POP]) if test yes != "$lt_cv_cc_needs_belf"; then # this is probably gcc 2.8.0, egcs 1.0 or newer; no need for -belf CFLAGS=$SAVE_CFLAGS fi ;; *-*solaris*) # Find out what ABI is being produced by ac_compile, and set linker # options accordingly. echo 'int i;' > conftest.$ac_ext if AC_TRY_EVAL(ac_compile); then case `$FILECMD conftest.o` in *64-bit*) case $lt_cv_prog_gnu_ld in yes*) case $host in i?86-*-solaris*|x86_64-*-solaris*) LD="${LD-ld} -m elf_x86_64" ;; sparc*-*-solaris*) LD="${LD-ld} -m elf64_sparc" ;; esac # GNU ld 2.21 introduced _sol2 emulations. Use them if available. if ${LD-ld} -V | grep _sol2 >/dev/null 2>&1; then LD=${LD-ld}_sol2 fi ;; *) if ${LD-ld} -64 -r -o conftest2.o conftest.o >/dev/null 2>&1; then LD="${LD-ld} -64" fi ;; esac ;; esac fi rm -rf conftest* ;; esac need_locks=$enable_libtool_lock ])# _LT_ENABLE_LOCK # _LT_PROG_AR # ----------- m4_defun([_LT_PROG_AR], [AC_CHECK_TOOLS(AR, [ar], false) : ${AR=ar} _LT_DECL([], [AR], [1], [The archiver]) # Use ARFLAGS variable as AR's operation code to sync the variable naming with # Automake. If both AR_FLAGS and ARFLAGS are specified, AR_FLAGS should have # higher priority because thats what people were doing historically (setting # ARFLAGS for automake and AR_FLAGS for libtool). FIXME: Make the AR_FLAGS # variable obsoleted/removed. test ${AR_FLAGS+y} || AR_FLAGS=${ARFLAGS-cr} lt_ar_flags=$AR_FLAGS _LT_DECL([], [lt_ar_flags], [0], [Flags to create an archive (by configure)]) # Make AR_FLAGS overridable by 'make ARFLAGS='. Don't try to run-time override # by AR_FLAGS because that was never working and AR_FLAGS is about to die. _LT_DECL([], [AR_FLAGS], [\@S|@{ARFLAGS-"\@S|@lt_ar_flags"}], [Flags to create an archive]) AC_CACHE_CHECK([for archiver @FILE support], [lt_cv_ar_at_file], [lt_cv_ar_at_file=no AC_COMPILE_IFELSE([AC_LANG_PROGRAM], [echo conftest.$ac_objext > conftest.lst lt_ar_try='$AR $AR_FLAGS libconftest.a @conftest.lst >&AS_MESSAGE_LOG_FD' AC_TRY_EVAL([lt_ar_try]) if test 0 -eq "$ac_status"; then # Ensure the archiver fails upon bogus file names. rm -f conftest.$ac_objext libconftest.a AC_TRY_EVAL([lt_ar_try]) if test 0 -ne "$ac_status"; then lt_cv_ar_at_file=@ fi fi rm -f conftest.* libconftest.a ]) ]) if test no = "$lt_cv_ar_at_file"; then archiver_list_spec= else archiver_list_spec=$lt_cv_ar_at_file fi _LT_DECL([], [archiver_list_spec], [1], [How to feed a file listing to the archiver]) ])# _LT_PROG_AR # _LT_CMD_OLD_ARCHIVE # ------------------- m4_defun([_LT_CMD_OLD_ARCHIVE], [_LT_PROG_AR AC_CHECK_TOOL(STRIP, strip, :) test -z "$STRIP" && STRIP=: _LT_DECL([], [STRIP], [1], [A symbol stripping program]) AC_CHECK_TOOL(RANLIB, ranlib, :) test -z "$RANLIB" && RANLIB=: _LT_DECL([], [RANLIB], [1], [Commands used to install an old-style archive]) # Determine commands to create old-style static archives. old_archive_cmds='$AR $AR_FLAGS $oldlib$oldobjs' old_postinstall_cmds='chmod 644 $oldlib' old_postuninstall_cmds= if test -n "$RANLIB"; then case $host_os in bitrig* | openbsd*) old_postinstall_cmds="$old_postinstall_cmds~\$RANLIB -t \$tool_oldlib" ;; *) old_postinstall_cmds="$old_postinstall_cmds~\$RANLIB \$tool_oldlib" ;; esac old_archive_cmds="$old_archive_cmds~\$RANLIB \$tool_oldlib" fi case $host_os in darwin*) lock_old_archive_extraction=yes ;; *) lock_old_archive_extraction=no ;; esac _LT_DECL([], [old_postinstall_cmds], [2]) _LT_DECL([], [old_postuninstall_cmds], [2]) _LT_TAGDECL([], [old_archive_cmds], [2], [Commands used to build an old-style archive]) _LT_DECL([], [lock_old_archive_extraction], [0], [Whether to use a lock for old archive extraction]) ])# _LT_CMD_OLD_ARCHIVE # _LT_COMPILER_OPTION(MESSAGE, VARIABLE-NAME, FLAGS, # [OUTPUT-FILE], [ACTION-SUCCESS], [ACTION-FAILURE]) # ---------------------------------------------------------------- # Check whether the given compiler option works AC_DEFUN([_LT_COMPILER_OPTION], [m4_require([_LT_FILEUTILS_DEFAULTS])dnl m4_require([_LT_DECL_SED])dnl AC_CACHE_CHECK([$1], [$2], [$2=no m4_if([$4], , [ac_outfile=conftest.$ac_objext], [ac_outfile=$4]) echo "$lt_simple_compile_test_code" > conftest.$ac_ext lt_compiler_flag="$3" ## exclude from sc_useless_quotes_in_assignment # Insert the option either (1) after the last *FLAGS variable, or # (2) before a word containing "conftest.", or (3) at the end. # Note that $ac_compile itself does not contain backslashes and begins # with a dollar sign (not a hyphen), so the echo should work correctly. # The option is referenced via a variable to avoid confusing sed. lt_compile=`echo "$ac_compile" | $SED \ -e 's:.*FLAGS}\{0,1\} :&$lt_compiler_flag :; t' \ -e 's: [[^ ]]*conftest\.: $lt_compiler_flag&:; t' \ -e 's:$: $lt_compiler_flag:'` (eval echo "\"\$as_me:$LINENO: $lt_compile\"" >&AS_MESSAGE_LOG_FD) (eval "$lt_compile" 2>conftest.err) ac_status=$? cat conftest.err >&AS_MESSAGE_LOG_FD echo "$as_me:$LINENO: \$? = $ac_status" >&AS_MESSAGE_LOG_FD if (exit $ac_status) && test -s "$ac_outfile"; then # The compiler can only warn and ignore the option if not recognized # So say no if there are warnings other than the usual output. $ECHO "$_lt_compiler_boilerplate" | $SED '/^$/d' >conftest.exp $SED '/^$/d; /^ *+/d' conftest.err >conftest.er2 if test ! -s conftest.er2 || diff conftest.exp conftest.er2 >/dev/null; then $2=yes fi fi $RM conftest* ]) if test yes = "[$]$2"; then m4_if([$5], , :, [$5]) else m4_if([$6], , :, [$6]) fi ])# _LT_COMPILER_OPTION # Old name: AU_ALIAS([AC_LIBTOOL_COMPILER_OPTION], [_LT_COMPILER_OPTION]) dnl aclocal-1.4 backwards compatibility: dnl AC_DEFUN([AC_LIBTOOL_COMPILER_OPTION], []) # _LT_LINKER_OPTION(MESSAGE, VARIABLE-NAME, FLAGS, # [ACTION-SUCCESS], [ACTION-FAILURE]) # ---------------------------------------------------- # Check whether the given linker option works AC_DEFUN([_LT_LINKER_OPTION], [m4_require([_LT_FILEUTILS_DEFAULTS])dnl m4_require([_LT_DECL_SED])dnl AC_CACHE_CHECK([$1], [$2], [$2=no save_LDFLAGS=$LDFLAGS LDFLAGS="$LDFLAGS $3" echo "$lt_simple_link_test_code" > conftest.$ac_ext if (eval $ac_link 2>conftest.err) && test -s conftest$ac_exeext; then # The linker can only warn and ignore the option if not recognized # So say no if there are warnings if test -s conftest.err; then # Append any errors to the config.log. cat conftest.err 1>&AS_MESSAGE_LOG_FD $ECHO "$_lt_linker_boilerplate" | $SED '/^$/d' > conftest.exp $SED '/^$/d; /^ *+/d' conftest.err >conftest.er2 if diff conftest.exp conftest.er2 >/dev/null; then $2=yes fi else $2=yes fi fi $RM -r conftest* LDFLAGS=$save_LDFLAGS ]) if test yes = "[$]$2"; then m4_if([$4], , :, [$4]) else m4_if([$5], , :, [$5]) fi ])# _LT_LINKER_OPTION # Old name: AU_ALIAS([AC_LIBTOOL_LINKER_OPTION], [_LT_LINKER_OPTION]) dnl aclocal-1.4 backwards compatibility: dnl AC_DEFUN([AC_LIBTOOL_LINKER_OPTION], []) # LT_CMD_MAX_LEN #--------------- AC_DEFUN([LT_CMD_MAX_LEN], [AC_REQUIRE([AC_CANONICAL_HOST])dnl # find the maximum length of command line arguments AC_MSG_CHECKING([the maximum length of command line arguments]) AC_CACHE_VAL([lt_cv_sys_max_cmd_len], [dnl i=0 teststring=ABCD case $build_os in msdosdjgpp*) # On DJGPP, this test can blow up pretty badly due to problems in libc # (any single argument exceeding 2000 bytes causes a buffer overrun # during glob expansion). Even if it were fixed, the result of this # check would be larger than it should be. lt_cv_sys_max_cmd_len=12288; # 12K is about right ;; gnu*) # Under GNU Hurd, this test is not required because there is # no limit to the length of command line arguments. # Libtool will interpret -1 as no limit whatsoever lt_cv_sys_max_cmd_len=-1; ;; cygwin* | mingw* | cegcc*) # On Win9x/ME, this test blows up -- it succeeds, but takes # about 5 minutes as the teststring grows exponentially. # Worse, since 9x/ME are not pre-emptively multitasking, # you end up with a "frozen" computer, even though with patience # the test eventually succeeds (with a max line length of 256k). # Instead, let's just punt: use the minimum linelength reported by # all of the supported platforms: 8192 (on NT/2K/XP). lt_cv_sys_max_cmd_len=8192; ;; mint*) # On MiNT this can take a long time and run out of memory. lt_cv_sys_max_cmd_len=8192; ;; amigaos*) # On AmigaOS with pdksh, this test takes hours, literally. # So we just punt and use a minimum line length of 8192. lt_cv_sys_max_cmd_len=8192; ;; bitrig* | darwin* | dragonfly* | freebsd* | midnightbsd* | netbsd* | openbsd*) # This has been around since 386BSD, at least. Likely further. if test -x /sbin/sysctl; then lt_cv_sys_max_cmd_len=`/sbin/sysctl -n kern.argmax` elif test -x /usr/sbin/sysctl; then lt_cv_sys_max_cmd_len=`/usr/sbin/sysctl -n kern.argmax` else lt_cv_sys_max_cmd_len=65536 # usable default for all BSDs fi # And add a safety zone lt_cv_sys_max_cmd_len=`expr $lt_cv_sys_max_cmd_len \/ 4` lt_cv_sys_max_cmd_len=`expr $lt_cv_sys_max_cmd_len \* 3` ;; interix*) # We know the value 262144 and hardcode it with a safety zone (like BSD) lt_cv_sys_max_cmd_len=196608 ;; os2*) # The test takes a long time on OS/2. lt_cv_sys_max_cmd_len=8192 ;; osf*) # Dr. Hans Ekkehard Plesser reports seeing a kernel panic running configure # due to this test when exec_disable_arg_limit is 1 on Tru64. It is not # nice to cause kernel panics so lets avoid the loop below. # First set a reasonable default. lt_cv_sys_max_cmd_len=16384 # if test -x /sbin/sysconfig; then case `/sbin/sysconfig -q proc exec_disable_arg_limit` in *1*) lt_cv_sys_max_cmd_len=-1 ;; esac fi ;; sco3.2v5*) lt_cv_sys_max_cmd_len=102400 ;; sysv5* | sco5v6* | sysv4.2uw2*) kargmax=`grep ARG_MAX /etc/conf/cf.d/stune 2>/dev/null` if test -n "$kargmax"; then lt_cv_sys_max_cmd_len=`echo $kargmax | $SED 's/.*[[ ]]//'` else lt_cv_sys_max_cmd_len=32768 fi ;; *) lt_cv_sys_max_cmd_len=`(getconf ARG_MAX) 2> /dev/null` if test -n "$lt_cv_sys_max_cmd_len" && \ test undefined != "$lt_cv_sys_max_cmd_len"; then lt_cv_sys_max_cmd_len=`expr $lt_cv_sys_max_cmd_len \/ 4` lt_cv_sys_max_cmd_len=`expr $lt_cv_sys_max_cmd_len \* 3` else # Make teststring a little bigger before we do anything with it. # a 1K string should be a reasonable start. for i in 1 2 3 4 5 6 7 8; do teststring=$teststring$teststring done SHELL=${SHELL-${CONFIG_SHELL-/bin/sh}} # If test is not a shell built-in, we'll probably end up computing a # maximum length that is only half of the actual maximum length, but # we can't tell. while { test X`env echo "$teststring$teststring" 2>/dev/null` \ = "X$teststring$teststring"; } >/dev/null 2>&1 && test 17 != "$i" # 1/2 MB should be enough do i=`expr $i + 1` teststring=$teststring$teststring done # Only check the string length outside the loop. lt_cv_sys_max_cmd_len=`expr "X$teststring" : ".*" 2>&1` teststring= # Add a significant safety factor because C++ compilers can tack on # massive amounts of additional arguments before passing them to the # linker. It appears as though 1/2 is a usable value. lt_cv_sys_max_cmd_len=`expr $lt_cv_sys_max_cmd_len \/ 2` fi ;; esac ]) if test -n "$lt_cv_sys_max_cmd_len"; then AC_MSG_RESULT($lt_cv_sys_max_cmd_len) else AC_MSG_RESULT(none) fi max_cmd_len=$lt_cv_sys_max_cmd_len _LT_DECL([], [max_cmd_len], [0], [What is the maximum length of a command?]) ])# LT_CMD_MAX_LEN # Old name: AU_ALIAS([AC_LIBTOOL_SYS_MAX_CMD_LEN], [LT_CMD_MAX_LEN]) dnl aclocal-1.4 backwards compatibility: dnl AC_DEFUN([AC_LIBTOOL_SYS_MAX_CMD_LEN], []) # _LT_HEADER_DLFCN # ---------------- m4_defun([_LT_HEADER_DLFCN], [AC_CHECK_HEADERS([dlfcn.h], [], [], [AC_INCLUDES_DEFAULT])dnl ])# _LT_HEADER_DLFCN # _LT_TRY_DLOPEN_SELF (ACTION-IF-TRUE, ACTION-IF-TRUE-W-USCORE, # ACTION-IF-FALSE, ACTION-IF-CROSS-COMPILING) # ---------------------------------------------------------------- m4_defun([_LT_TRY_DLOPEN_SELF], [m4_require([_LT_HEADER_DLFCN])dnl if test yes = "$cross_compiling"; then : [$4] else lt_dlunknown=0; lt_dlno_uscore=1; lt_dlneed_uscore=2 lt_status=$lt_dlunknown cat > conftest.$ac_ext <<_LT_EOF [#line $LINENO "configure" #include "confdefs.h" #if HAVE_DLFCN_H #include #endif #include #ifdef RTLD_GLOBAL # define LT_DLGLOBAL RTLD_GLOBAL #else # ifdef DL_GLOBAL # define LT_DLGLOBAL DL_GLOBAL # else # define LT_DLGLOBAL 0 # endif #endif /* We may have to define LT_DLLAZY_OR_NOW in the command line if we find out it does not work in some platform. */ #ifndef LT_DLLAZY_OR_NOW # ifdef RTLD_LAZY # define LT_DLLAZY_OR_NOW RTLD_LAZY # else # ifdef DL_LAZY # define LT_DLLAZY_OR_NOW DL_LAZY # else # ifdef RTLD_NOW # define LT_DLLAZY_OR_NOW RTLD_NOW # else # ifdef DL_NOW # define LT_DLLAZY_OR_NOW DL_NOW # else # define LT_DLLAZY_OR_NOW 0 # endif # endif # endif # endif #endif /* When -fvisibility=hidden is used, assume the code has been annotated correspondingly for the symbols needed. */ #if defined __GNUC__ && (((__GNUC__ == 3) && (__GNUC_MINOR__ >= 3)) || (__GNUC__ > 3)) int fnord () __attribute__((visibility("default"))); #endif int fnord () { return 42; } int main () { void *self = dlopen (0, LT_DLGLOBAL|LT_DLLAZY_OR_NOW); int status = $lt_dlunknown; if (self) { if (dlsym (self,"fnord")) status = $lt_dlno_uscore; else { if (dlsym( self,"_fnord")) status = $lt_dlneed_uscore; else puts (dlerror ()); } /* dlclose (self); */ } else puts (dlerror ()); return status; }] _LT_EOF if AC_TRY_EVAL(ac_link) && test -s "conftest$ac_exeext" 2>/dev/null; then (./conftest; exit; ) >&AS_MESSAGE_LOG_FD 2>/dev/null lt_status=$? case x$lt_status in x$lt_dlno_uscore) $1 ;; x$lt_dlneed_uscore) $2 ;; x$lt_dlunknown|x*) $3 ;; esac else : # compilation failed $3 fi fi rm -fr conftest* ])# _LT_TRY_DLOPEN_SELF # LT_SYS_DLOPEN_SELF # ------------------ AC_DEFUN([LT_SYS_DLOPEN_SELF], [m4_require([_LT_HEADER_DLFCN])dnl if test yes != "$enable_dlopen"; then enable_dlopen=unknown enable_dlopen_self=unknown enable_dlopen_self_static=unknown else lt_cv_dlopen=no lt_cv_dlopen_libs= case $host_os in beos*) lt_cv_dlopen=load_add_on lt_cv_dlopen_libs= lt_cv_dlopen_self=yes ;; mingw* | pw32* | cegcc*) lt_cv_dlopen=LoadLibrary lt_cv_dlopen_libs= ;; cygwin*) lt_cv_dlopen=dlopen lt_cv_dlopen_libs= ;; darwin*) # if libdl is installed we need to link against it AC_CHECK_LIB([dl], [dlopen], [lt_cv_dlopen=dlopen lt_cv_dlopen_libs=-ldl],[ lt_cv_dlopen=dyld lt_cv_dlopen_libs= lt_cv_dlopen_self=yes ]) ;; tpf*) # Don't try to run any link tests for TPF. We know it's impossible # because TPF is a cross-compiler, and we know how we open DSOs. lt_cv_dlopen=dlopen lt_cv_dlopen_libs= lt_cv_dlopen_self=no ;; *) AC_CHECK_FUNC([shl_load], [lt_cv_dlopen=shl_load], [AC_CHECK_LIB([dld], [shl_load], [lt_cv_dlopen=shl_load lt_cv_dlopen_libs=-ldld], [AC_CHECK_FUNC([dlopen], [lt_cv_dlopen=dlopen], [AC_CHECK_LIB([dl], [dlopen], [lt_cv_dlopen=dlopen lt_cv_dlopen_libs=-ldl], [AC_CHECK_LIB([svld], [dlopen], [lt_cv_dlopen=dlopen lt_cv_dlopen_libs=-lsvld], [AC_CHECK_LIB([dld], [dld_link], [lt_cv_dlopen=dld_link lt_cv_dlopen_libs=-ldld]) ]) ]) ]) ]) ]) ;; esac if test no = "$lt_cv_dlopen"; then enable_dlopen=no else enable_dlopen=yes fi case $lt_cv_dlopen in dlopen) save_CPPFLAGS=$CPPFLAGS test yes = "$ac_cv_header_dlfcn_h" && CPPFLAGS="$CPPFLAGS -DHAVE_DLFCN_H" save_LDFLAGS=$LDFLAGS wl=$lt_prog_compiler_wl eval LDFLAGS=\"\$LDFLAGS $export_dynamic_flag_spec\" save_LIBS=$LIBS LIBS="$lt_cv_dlopen_libs $LIBS" AC_CACHE_CHECK([whether a program can dlopen itself], lt_cv_dlopen_self, [dnl _LT_TRY_DLOPEN_SELF( lt_cv_dlopen_self=yes, lt_cv_dlopen_self=yes, lt_cv_dlopen_self=no, lt_cv_dlopen_self=cross) ]) if test yes = "$lt_cv_dlopen_self"; then wl=$lt_prog_compiler_wl eval LDFLAGS=\"\$LDFLAGS $lt_prog_compiler_static\" AC_CACHE_CHECK([whether a statically linked program can dlopen itself], lt_cv_dlopen_self_static, [dnl _LT_TRY_DLOPEN_SELF( lt_cv_dlopen_self_static=yes, lt_cv_dlopen_self_static=yes, lt_cv_dlopen_self_static=no, lt_cv_dlopen_self_static=cross) ]) fi CPPFLAGS=$save_CPPFLAGS LDFLAGS=$save_LDFLAGS LIBS=$save_LIBS ;; esac case $lt_cv_dlopen_self in yes|no) enable_dlopen_self=$lt_cv_dlopen_self ;; *) enable_dlopen_self=unknown ;; esac case $lt_cv_dlopen_self_static in yes|no) enable_dlopen_self_static=$lt_cv_dlopen_self_static ;; *) enable_dlopen_self_static=unknown ;; esac fi _LT_DECL([dlopen_support], [enable_dlopen], [0], [Whether dlopen is supported]) _LT_DECL([dlopen_self], [enable_dlopen_self], [0], [Whether dlopen of programs is supported]) _LT_DECL([dlopen_self_static], [enable_dlopen_self_static], [0], [Whether dlopen of statically linked programs is supported]) ])# LT_SYS_DLOPEN_SELF # Old name: AU_ALIAS([AC_LIBTOOL_DLOPEN_SELF], [LT_SYS_DLOPEN_SELF]) dnl aclocal-1.4 backwards compatibility: dnl AC_DEFUN([AC_LIBTOOL_DLOPEN_SELF], []) # _LT_COMPILER_C_O([TAGNAME]) # --------------------------- # Check to see if options -c and -o are simultaneously supported by compiler. # This macro does not hard code the compiler like AC_PROG_CC_C_O. m4_defun([_LT_COMPILER_C_O], [m4_require([_LT_DECL_SED])dnl m4_require([_LT_FILEUTILS_DEFAULTS])dnl m4_require([_LT_TAG_COMPILER])dnl AC_CACHE_CHECK([if $compiler supports -c -o file.$ac_objext], [_LT_TAGVAR(lt_cv_prog_compiler_c_o, $1)], [_LT_TAGVAR(lt_cv_prog_compiler_c_o, $1)=no $RM -r conftest 2>/dev/null mkdir conftest cd conftest mkdir out echo "$lt_simple_compile_test_code" > conftest.$ac_ext lt_compiler_flag="-o out/conftest2.$ac_objext" # Insert the option either (1) after the last *FLAGS variable, or # (2) before a word containing "conftest.", or (3) at the end. # Note that $ac_compile itself does not contain backslashes and begins # with a dollar sign (not a hyphen), so the echo should work correctly. lt_compile=`echo "$ac_compile" | $SED \ -e 's:.*FLAGS}\{0,1\} :&$lt_compiler_flag :; t' \ -e 's: [[^ ]]*conftest\.: $lt_compiler_flag&:; t' \ -e 's:$: $lt_compiler_flag:'` (eval echo "\"\$as_me:$LINENO: $lt_compile\"" >&AS_MESSAGE_LOG_FD) (eval "$lt_compile" 2>out/conftest.err) ac_status=$? cat out/conftest.err >&AS_MESSAGE_LOG_FD echo "$as_me:$LINENO: \$? = $ac_status" >&AS_MESSAGE_LOG_FD if (exit $ac_status) && test -s out/conftest2.$ac_objext then # The compiler can only warn and ignore the option if not recognized # So say no if there are warnings $ECHO "$_lt_compiler_boilerplate" | $SED '/^$/d' > out/conftest.exp $SED '/^$/d; /^ *+/d' out/conftest.err >out/conftest.er2 if test ! -s out/conftest.er2 || diff out/conftest.exp out/conftest.er2 >/dev/null; then _LT_TAGVAR(lt_cv_prog_compiler_c_o, $1)=yes fi fi chmod u+w . 2>&AS_MESSAGE_LOG_FD $RM conftest* # SGI C++ compiler will create directory out/ii_files/ for # template instantiation test -d out/ii_files && $RM out/ii_files/* && rmdir out/ii_files $RM out/* && rmdir out cd .. $RM -r conftest $RM conftest* ]) _LT_TAGDECL([compiler_c_o], [lt_cv_prog_compiler_c_o], [1], [Does compiler simultaneously support -c and -o options?]) ])# _LT_COMPILER_C_O # _LT_COMPILER_FILE_LOCKS([TAGNAME]) # ---------------------------------- # Check to see if we can do hard links to lock some files if needed m4_defun([_LT_COMPILER_FILE_LOCKS], [m4_require([_LT_ENABLE_LOCK])dnl m4_require([_LT_FILEUTILS_DEFAULTS])dnl _LT_COMPILER_C_O([$1]) hard_links=nottested if test no = "$_LT_TAGVAR(lt_cv_prog_compiler_c_o, $1)" && test no != "$need_locks"; then # do not overwrite the value of need_locks provided by the user AC_MSG_CHECKING([if we can lock with hard links]) hard_links=yes $RM conftest* ln conftest.a conftest.b 2>/dev/null && hard_links=no touch conftest.a ln conftest.a conftest.b 2>&5 || hard_links=no ln conftest.a conftest.b 2>/dev/null && hard_links=no AC_MSG_RESULT([$hard_links]) if test no = "$hard_links"; then AC_MSG_WARN(['$CC' does not support '-c -o', so 'make -j' may be unsafe]) need_locks=warn fi else need_locks=no fi _LT_DECL([], [need_locks], [1], [Must we lock files when doing compilation?]) ])# _LT_COMPILER_FILE_LOCKS # _LT_CHECK_OBJDIR # ---------------- m4_defun([_LT_CHECK_OBJDIR], [AC_CACHE_CHECK([for objdir], [lt_cv_objdir], [rm -f .libs 2>/dev/null mkdir .libs 2>/dev/null if test -d .libs; then lt_cv_objdir=.libs else # MS-DOS does not allow filenames that begin with a dot. lt_cv_objdir=_libs fi rmdir .libs 2>/dev/null]) objdir=$lt_cv_objdir _LT_DECL([], [objdir], [0], [The name of the directory that contains temporary libtool files])dnl m4_pattern_allow([LT_OBJDIR])dnl AC_DEFINE_UNQUOTED([LT_OBJDIR], "$lt_cv_objdir/", [Define to the sub-directory where libtool stores uninstalled libraries.]) ])# _LT_CHECK_OBJDIR # _LT_LINKER_HARDCODE_LIBPATH([TAGNAME]) # -------------------------------------- # Check hardcoding attributes. m4_defun([_LT_LINKER_HARDCODE_LIBPATH], [AC_MSG_CHECKING([how to hardcode library paths into programs]) _LT_TAGVAR(hardcode_action, $1)= if test -n "$_LT_TAGVAR(hardcode_libdir_flag_spec, $1)" || test -n "$_LT_TAGVAR(runpath_var, $1)" || test yes = "$_LT_TAGVAR(hardcode_automatic, $1)"; then # We can hardcode non-existent directories. if test no != "$_LT_TAGVAR(hardcode_direct, $1)" && # If the only mechanism to avoid hardcoding is shlibpath_var, we # have to relink, otherwise we might link with an installed library # when we should be linking with a yet-to-be-installed one ## test no != "$_LT_TAGVAR(hardcode_shlibpath_var, $1)" && test no != "$_LT_TAGVAR(hardcode_minus_L, $1)"; then # Linking always hardcodes the temporary library directory. _LT_TAGVAR(hardcode_action, $1)=relink else # We can link without hardcoding, and we can hardcode nonexisting dirs. _LT_TAGVAR(hardcode_action, $1)=immediate fi else # We cannot hardcode anything, or else we can only hardcode existing # directories. _LT_TAGVAR(hardcode_action, $1)=unsupported fi AC_MSG_RESULT([$_LT_TAGVAR(hardcode_action, $1)]) if test relink = "$_LT_TAGVAR(hardcode_action, $1)" || test yes = "$_LT_TAGVAR(inherit_rpath, $1)"; then # Fast installation is not supported enable_fast_install=no elif test yes = "$shlibpath_overrides_runpath" || test no = "$enable_shared"; then # Fast installation is not necessary enable_fast_install=needless fi _LT_TAGDECL([], [hardcode_action], [0], [How to hardcode a shared library path into an executable]) ])# _LT_LINKER_HARDCODE_LIBPATH # _LT_CMD_STRIPLIB # ---------------- m4_defun([_LT_CMD_STRIPLIB], [m4_require([_LT_DECL_EGREP]) striplib= old_striplib= AC_MSG_CHECKING([whether stripping libraries is possible]) if test -z "$STRIP"; then AC_MSG_RESULT([no]) else if $STRIP -V 2>&1 | $GREP "GNU strip" >/dev/null; then old_striplib="$STRIP --strip-debug" striplib="$STRIP --strip-unneeded" AC_MSG_RESULT([yes]) else case $host_os in darwin*) # FIXME - insert some real tests, host_os isn't really good enough striplib="$STRIP -x" old_striplib="$STRIP -S" AC_MSG_RESULT([yes]) ;; freebsd*) if $STRIP -V 2>&1 | $GREP "elftoolchain" >/dev/null; then old_striplib="$STRIP --strip-debug" striplib="$STRIP --strip-unneeded" AC_MSG_RESULT([yes]) else AC_MSG_RESULT([no]) fi ;; *) AC_MSG_RESULT([no]) ;; esac fi fi _LT_DECL([], [old_striplib], [1], [Commands to strip libraries]) _LT_DECL([], [striplib], [1]) ])# _LT_CMD_STRIPLIB # _LT_PREPARE_MUNGE_PATH_LIST # --------------------------- # Make sure func_munge_path_list() is defined correctly. m4_defun([_LT_PREPARE_MUNGE_PATH_LIST], [[# func_munge_path_list VARIABLE PATH # ----------------------------------- # VARIABLE is name of variable containing _space_ separated list of # directories to be munged by the contents of PATH, which is string # having a format: # "DIR[:DIR]:" # string "DIR[ DIR]" will be prepended to VARIABLE # ":DIR[:DIR]" # string "DIR[ DIR]" will be appended to VARIABLE # "DIRP[:DIRP]::[DIRA:]DIRA" # string "DIRP[ DIRP]" will be prepended to VARIABLE and string # "DIRA[ DIRA]" will be appended to VARIABLE # "DIR[:DIR]" # VARIABLE will be replaced by "DIR[ DIR]" func_munge_path_list () { case x@S|@2 in x) ;; *:) eval @S|@1=\"`$ECHO @S|@2 | $SED 's/:/ /g'` \@S|@@S|@1\" ;; x:*) eval @S|@1=\"\@S|@@S|@1 `$ECHO @S|@2 | $SED 's/:/ /g'`\" ;; *::*) eval @S|@1=\"\@S|@@S|@1\ `$ECHO @S|@2 | $SED -e 's/.*:://' -e 's/:/ /g'`\" eval @S|@1=\"`$ECHO @S|@2 | $SED -e 's/::.*//' -e 's/:/ /g'`\ \@S|@@S|@1\" ;; *) eval @S|@1=\"`$ECHO @S|@2 | $SED 's/:/ /g'`\" ;; esac } ]])# _LT_PREPARE_PATH_LIST # _LT_SYS_DYNAMIC_LINKER([TAG]) # ----------------------------- # PORTME Fill in your ld.so characteristics m4_defun([_LT_SYS_DYNAMIC_LINKER], [AC_REQUIRE([AC_CANONICAL_HOST])dnl m4_require([_LT_DECL_EGREP])dnl m4_require([_LT_FILEUTILS_DEFAULTS])dnl m4_require([_LT_DECL_OBJDUMP])dnl m4_require([_LT_DECL_SED])dnl m4_require([_LT_CHECK_SHELL_FEATURES])dnl m4_require([_LT_PREPARE_MUNGE_PATH_LIST])dnl AC_MSG_CHECKING([dynamic linker characteristics]) m4_if([$1], [], [ if test yes = "$GCC"; then case $host_os in darwin*) lt_awk_arg='/^libraries:/,/LR/' ;; *) lt_awk_arg='/^libraries:/' ;; esac case $host_os in mingw* | cegcc*) lt_sed_strip_eq='s|=\([[A-Za-z]]:\)|\1|g' ;; *) lt_sed_strip_eq='s|=/|/|g' ;; esac lt_search_path_spec=`$CC -print-search-dirs | awk $lt_awk_arg | $SED -e "s/^libraries://" -e $lt_sed_strip_eq` case $lt_search_path_spec in *\;*) # if the path contains ";" then we assume it to be the separator # otherwise default to the standard path separator (i.e. ":") - it is # assumed that no part of a normal pathname contains ";" but that should # okay in the real world where ";" in dirpaths is itself problematic. lt_search_path_spec=`$ECHO "$lt_search_path_spec" | $SED 's/;/ /g'` ;; *) lt_search_path_spec=`$ECHO "$lt_search_path_spec" | $SED "s/$PATH_SEPARATOR/ /g"` ;; esac # Ok, now we have the path, separated by spaces, we can step through it # and add multilib dir if necessary... lt_tmp_lt_search_path_spec= lt_multi_os_dir=/`$CC $CPPFLAGS $CFLAGS $LDFLAGS -print-multi-os-directory 2>/dev/null` # ...but if some path component already ends with the multilib dir we assume # that all is fine and trust -print-search-dirs as is (GCC 4.2? or newer). case "$lt_multi_os_dir; $lt_search_path_spec " in "/; "* | "/.; "* | "/./; "* | *"$lt_multi_os_dir "* | *"$lt_multi_os_dir/ "*) lt_multi_os_dir= ;; esac for lt_sys_path in $lt_search_path_spec; do if test -d "$lt_sys_path$lt_multi_os_dir"; then lt_tmp_lt_search_path_spec="$lt_tmp_lt_search_path_spec $lt_sys_path$lt_multi_os_dir" elif test -n "$lt_multi_os_dir"; then test -d "$lt_sys_path" && \ lt_tmp_lt_search_path_spec="$lt_tmp_lt_search_path_spec $lt_sys_path" fi done lt_search_path_spec=`$ECHO "$lt_tmp_lt_search_path_spec" | awk ' BEGIN {RS = " "; FS = "/|\n";} { lt_foo = ""; lt_count = 0; for (lt_i = NF; lt_i > 0; lt_i--) { if ($lt_i != "" && $lt_i != ".") { if ($lt_i == "..") { lt_count++; } else { if (lt_count == 0) { lt_foo = "/" $lt_i lt_foo; } else { lt_count--; } } } } if (lt_foo != "") { lt_freq[[lt_foo]]++; } if (lt_freq[[lt_foo]] == 1) { print lt_foo; } }'` # AWK program above erroneously prepends '/' to C:/dos/paths # for these hosts. case $host_os in mingw* | cegcc*) lt_search_path_spec=`$ECHO "$lt_search_path_spec" |\ $SED 's|/\([[A-Za-z]]:\)|\1|g'` ;; esac sys_lib_search_path_spec=`$ECHO "$lt_search_path_spec" | $lt_NL2SP` else sys_lib_search_path_spec="/lib /usr/lib /usr/local/lib" fi]) library_names_spec= libname_spec='lib$name' soname_spec= shrext_cmds=.so postinstall_cmds= postuninstall_cmds= finish_cmds= finish_eval= shlibpath_var= shlibpath_overrides_runpath=unknown version_type=none dynamic_linker="$host_os ld.so" sys_lib_dlsearch_path_spec="/lib /usr/lib" need_lib_prefix=unknown hardcode_into_libs=no # when you set need_version to no, make sure it does not cause -set_version # flags to be left without arguments need_version=unknown AC_ARG_VAR([LT_SYS_LIBRARY_PATH], [User-defined run-time library search path.]) case $host_os in aix3*) version_type=linux # correct to gnu/linux during the next big refactor library_names_spec='$libname$release$shared_ext$versuffix $libname.a' shlibpath_var=LIBPATH # AIX 3 has no versioning support, so we append a major version to the name. soname_spec='$libname$release$shared_ext$major' ;; aix[[4-9]]*) version_type=linux # correct to gnu/linux during the next big refactor need_lib_prefix=no need_version=no hardcode_into_libs=yes if test ia64 = "$host_cpu"; then # AIX 5 supports IA64 library_names_spec='$libname$release$shared_ext$major $libname$release$shared_ext$versuffix $libname$shared_ext' shlibpath_var=LD_LIBRARY_PATH else # With GCC up to 2.95.x, collect2 would create an import file # for dependence libraries. The import file would start with # the line '#! .'. This would cause the generated library to # depend on '.', always an invalid library. This was fixed in # development snapshots of GCC prior to 3.0. case $host_os in aix4 | aix4.[[01]] | aix4.[[01]].*) if { echo '#if __GNUC__ > 2 || (__GNUC__ == 2 && __GNUC_MINOR__ >= 97)' echo ' yes ' echo '#endif'; } | $CC -E - | $GREP yes > /dev/null; then : else can_build_shared=no fi ;; esac # Using Import Files as archive members, it is possible to support # filename-based versioning of shared library archives on AIX. While # this would work for both with and without runtime linking, it will # prevent static linking of such archives. So we do filename-based # shared library versioning with .so extension only, which is used # when both runtime linking and shared linking is enabled. # Unfortunately, runtime linking may impact performance, so we do # not want this to be the default eventually. Also, we use the # versioned .so libs for executables only if there is the -brtl # linker flag in LDFLAGS as well, or --with-aix-soname=svr4 only. # To allow for filename-based versioning support, we need to create # libNAME.so.V as an archive file, containing: # *) an Import File, referring to the versioned filename of the # archive as well as the shared archive member, telling the # bitwidth (32 or 64) of that shared object, and providing the # list of exported symbols of that shared object, eventually # decorated with the 'weak' keyword # *) the shared object with the F_LOADONLY flag set, to really avoid # it being seen by the linker. # At run time we better use the real file rather than another symlink, # but for link time we create the symlink libNAME.so -> libNAME.so.V case $with_aix_soname,$aix_use_runtimelinking in # AIX (on Power*) has no versioning support, so currently we cannot hardcode correct # soname into executable. Probably we can add versioning support to # collect2, so additional links can be useful in future. aix,yes) # traditional libtool dynamic_linker='AIX unversionable lib.so' # If using run time linking (on AIX 4.2 or later) use lib.so # instead of lib.a to let people know that these are not # typical AIX shared libraries. library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext' ;; aix,no) # traditional AIX only dynamic_linker='AIX lib.a[(]lib.so.V[)]' # We preserve .a as extension for shared libraries through AIX4.2 # and later when we are not doing run time linking. library_names_spec='$libname$release.a $libname.a' soname_spec='$libname$release$shared_ext$major' ;; svr4,*) # full svr4 only dynamic_linker="AIX lib.so.V[(]$shared_archive_member_spec.o[)]" library_names_spec='$libname$release$shared_ext$major $libname$shared_ext' # We do not specify a path in Import Files, so LIBPATH fires. shlibpath_overrides_runpath=yes ;; *,yes) # both, prefer svr4 dynamic_linker="AIX lib.so.V[(]$shared_archive_member_spec.o[)], lib.a[(]lib.so.V[)]" library_names_spec='$libname$release$shared_ext$major $libname$shared_ext' # unpreferred sharedlib libNAME.a needs extra handling postinstall_cmds='test -n "$linkname" || linkname="$realname"~func_stripname "" ".so" "$linkname"~$install_shared_prog "$dir/$func_stripname_result.$libext" "$destdir/$func_stripname_result.$libext"~test -z "$tstripme" || test -z "$striplib" || $striplib "$destdir/$func_stripname_result.$libext"' postuninstall_cmds='for n in $library_names $old_library; do :; done~func_stripname "" ".so" "$n"~test "$func_stripname_result" = "$n" || func_append rmfiles " $odir/$func_stripname_result.$libext"' # We do not specify a path in Import Files, so LIBPATH fires. shlibpath_overrides_runpath=yes ;; *,no) # both, prefer aix dynamic_linker="AIX lib.a[(]lib.so.V[)], lib.so.V[(]$shared_archive_member_spec.o[)]" library_names_spec='$libname$release.a $libname.a' soname_spec='$libname$release$shared_ext$major' # unpreferred sharedlib libNAME.so.V and symlink libNAME.so need extra handling postinstall_cmds='test -z "$dlname" || $install_shared_prog $dir/$dlname $destdir/$dlname~test -z "$tstripme" || test -z "$striplib" || $striplib $destdir/$dlname~test -n "$linkname" || linkname=$realname~func_stripname "" ".a" "$linkname"~(cd "$destdir" && $LN_S -f $dlname $func_stripname_result.so)' postuninstall_cmds='test -z "$dlname" || func_append rmfiles " $odir/$dlname"~for n in $old_library $library_names; do :; done~func_stripname "" ".a" "$n"~func_append rmfiles " $odir/$func_stripname_result.so"' ;; esac shlibpath_var=LIBPATH fi ;; amigaos*) case $host_cpu in powerpc) # Since July 2007 AmigaOS4 officially supports .so libraries. # When compiling the executable, add -use-dynld -Lsobjs: to the compileline. library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext' ;; m68k) library_names_spec='$libname.ixlibrary $libname.a' # Create ${libname}_ixlibrary.a entries in /sys/libs. finish_eval='for lib in `ls $libdir/*.ixlibrary 2>/dev/null`; do libname=`func_echo_all "$lib" | $SED '\''s%^.*/\([[^/]]*\)\.ixlibrary$%\1%'\''`; $RM /sys/libs/${libname}_ixlibrary.a; $show "cd /sys/libs && $LN_S $lib ${libname}_ixlibrary.a"; cd /sys/libs && $LN_S $lib ${libname}_ixlibrary.a || exit 1; done' ;; esac ;; beos*) library_names_spec='$libname$shared_ext' dynamic_linker="$host_os ld.so" shlibpath_var=LIBRARY_PATH ;; bsdi[[45]]*) version_type=linux # correct to gnu/linux during the next big refactor need_version=no library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext' soname_spec='$libname$release$shared_ext$major' finish_cmds='PATH="\$PATH:/sbin" ldconfig $libdir' shlibpath_var=LD_LIBRARY_PATH sys_lib_search_path_spec="/shlib /usr/lib /usr/X11/lib /usr/contrib/lib /lib /usr/local/lib" sys_lib_dlsearch_path_spec="/shlib /usr/lib /usr/local/lib" # the default ld.so.conf also contains /usr/contrib/lib and # /usr/X11R6/lib (/usr/X11 is a link to /usr/X11R6), but let us allow # libtool to hard-code these into programs ;; cygwin* | mingw* | pw32* | cegcc*) version_type=windows shrext_cmds=.dll need_version=no need_lib_prefix=no case $GCC,$cc_basename in yes,*) # gcc library_names_spec='$libname.dll.a' # DLL is installed to $(libdir)/../bin by postinstall_cmds postinstall_cmds='base_file=`basename \$file`~ dlpath=`$SHELL 2>&1 -c '\''. $dir/'\''\$base_file'\''i; echo \$dlname'\''`~ dldir=$destdir/`dirname \$dlpath`~ test -d \$dldir || mkdir -p \$dldir~ $install_prog $dir/$dlname \$dldir/$dlname~ chmod a+x \$dldir/$dlname~ if test -n '\''$stripme'\'' && test -n '\''$striplib'\''; then eval '\''$striplib \$dldir/$dlname'\'' || exit \$?; fi' postuninstall_cmds='dldll=`$SHELL 2>&1 -c '\''. $file; echo \$dlname'\''`~ dlpath=$dir/\$dldll~ $RM \$dlpath' shlibpath_overrides_runpath=yes case $host_os in cygwin*) # Cygwin DLLs use 'cyg' prefix rather than 'lib' soname_spec='`echo $libname | $SED -e 's/^lib/cyg/'``echo $release | $SED -e 's/[[.]]/-/g'`$versuffix$shared_ext' m4_if([$1], [],[ sys_lib_search_path_spec="$sys_lib_search_path_spec /usr/lib/w32api"]) ;; mingw* | cegcc*) # MinGW DLLs use traditional 'lib' prefix soname_spec='$libname`echo $release | $SED -e 's/[[.]]/-/g'`$versuffix$shared_ext' ;; pw32*) # pw32 DLLs use 'pw' prefix rather than 'lib' library_names_spec='`echo $libname | $SED -e 's/^lib/pw/'``echo $release | $SED -e 's/[[.]]/-/g'`$versuffix$shared_ext' ;; esac dynamic_linker='Win32 ld.exe' ;; *,cl* | *,icl*) # Native MSVC or ICC libname_spec='$name' soname_spec='$libname`echo $release | $SED -e 's/[[.]]/-/g'`$versuffix$shared_ext' library_names_spec='$libname.dll.lib' case $build_os in mingw*) sys_lib_search_path_spec= lt_save_ifs=$IFS IFS=';' for lt_path in $LIB do IFS=$lt_save_ifs # Let DOS variable expansion print the short 8.3 style file name. lt_path=`cd "$lt_path" 2>/dev/null && cmd //C "for %i in (".") do @echo %~si"` sys_lib_search_path_spec="$sys_lib_search_path_spec $lt_path" done IFS=$lt_save_ifs # Convert to MSYS style. sys_lib_search_path_spec=`$ECHO "$sys_lib_search_path_spec" | $SED -e 's|\\\\|/|g' -e 's| \\([[a-zA-Z]]\\):| /\\1|g' -e 's|^ ||'` ;; cygwin*) # Convert to unix form, then to dos form, then back to unix form # but this time dos style (no spaces!) so that the unix form looks # like /cygdrive/c/PROGRA~1:/cygdr... sys_lib_search_path_spec=`cygpath --path --unix "$LIB"` sys_lib_search_path_spec=`cygpath --path --dos "$sys_lib_search_path_spec" 2>/dev/null` sys_lib_search_path_spec=`cygpath --path --unix "$sys_lib_search_path_spec" | $SED -e "s/$PATH_SEPARATOR/ /g"` ;; *) sys_lib_search_path_spec=$LIB if $ECHO "$sys_lib_search_path_spec" | [$GREP ';[c-zC-Z]:/' >/dev/null]; then # It is most probably a Windows format PATH. sys_lib_search_path_spec=`$ECHO "$sys_lib_search_path_spec" | $SED -e 's/;/ /g'` else sys_lib_search_path_spec=`$ECHO "$sys_lib_search_path_spec" | $SED -e "s/$PATH_SEPARATOR/ /g"` fi # FIXME: find the short name or the path components, as spaces are # common. (e.g. "Program Files" -> "PROGRA~1") ;; esac # DLL is installed to $(libdir)/../bin by postinstall_cmds postinstall_cmds='base_file=`basename \$file`~ dlpath=`$SHELL 2>&1 -c '\''. $dir/'\''\$base_file'\''i; echo \$dlname'\''`~ dldir=$destdir/`dirname \$dlpath`~ test -d \$dldir || mkdir -p \$dldir~ $install_prog $dir/$dlname \$dldir/$dlname' postuninstall_cmds='dldll=`$SHELL 2>&1 -c '\''. $file; echo \$dlname'\''`~ dlpath=$dir/\$dldll~ $RM \$dlpath' shlibpath_overrides_runpath=yes dynamic_linker='Win32 link.exe' ;; *) # Assume MSVC and ICC wrapper library_names_spec='$libname`echo $release | $SED -e 's/[[.]]/-/g'`$versuffix$shared_ext $libname.lib' dynamic_linker='Win32 ld.exe' ;; esac # FIXME: first we should search . and the directory the executable is in shlibpath_var=PATH ;; darwin* | rhapsody*) dynamic_linker="$host_os dyld" version_type=darwin need_lib_prefix=no need_version=no library_names_spec='$libname$release$major$shared_ext $libname$shared_ext' soname_spec='$libname$release$major$shared_ext' shlibpath_overrides_runpath=yes shlibpath_var=DYLD_LIBRARY_PATH shrext_cmds='`test .$module = .yes && echo .so || echo .dylib`' m4_if([$1], [],[ sys_lib_search_path_spec="$sys_lib_search_path_spec /usr/local/lib"]) sys_lib_dlsearch_path_spec='/usr/local/lib /lib /usr/lib' ;; dgux*) version_type=linux # correct to gnu/linux during the next big refactor need_lib_prefix=no need_version=no library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext' soname_spec='$libname$release$shared_ext$major' shlibpath_var=LD_LIBRARY_PATH ;; freebsd* | dragonfly* | midnightbsd*) # DragonFly does not have aout. When/if they implement a new # versioning mechanism, adjust this. if test -x /usr/bin/objformat; then objformat=`/usr/bin/objformat` else case $host_os in freebsd[[23]].*) objformat=aout ;; *) objformat=elf ;; esac fi version_type=freebsd-$objformat case $version_type in freebsd-elf*) library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext' soname_spec='$libname$release$shared_ext$major' need_version=no need_lib_prefix=no ;; freebsd-*) library_names_spec='$libname$release$shared_ext$versuffix $libname$shared_ext$versuffix' need_version=yes ;; esac shlibpath_var=LD_LIBRARY_PATH case $host_os in freebsd2.*) shlibpath_overrides_runpath=yes ;; freebsd3.[[01]]* | freebsdelf3.[[01]]*) shlibpath_overrides_runpath=yes hardcode_into_libs=yes ;; freebsd3.[[2-9]]* | freebsdelf3.[[2-9]]* | \ freebsd4.[[0-5]] | freebsdelf4.[[0-5]] | freebsd4.1.1 | freebsdelf4.1.1) shlibpath_overrides_runpath=no hardcode_into_libs=yes ;; *) # from 4.6 on, and DragonFly shlibpath_overrides_runpath=yes hardcode_into_libs=yes ;; esac ;; haiku*) version_type=linux # correct to gnu/linux during the next big refactor need_lib_prefix=no need_version=no dynamic_linker="$host_os runtime_loader" library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext' soname_spec='$libname$release$shared_ext$major' shlibpath_var=LIBRARY_PATH shlibpath_overrides_runpath=no sys_lib_dlsearch_path_spec='/boot/home/config/lib /boot/common/lib /boot/system/lib' hardcode_into_libs=yes ;; hpux9* | hpux10* | hpux11*) # Give a soname corresponding to the major version so that dld.sl refuses to # link against other versions. version_type=sunos need_lib_prefix=no need_version=no case $host_cpu in ia64*) shrext_cmds='.so' hardcode_into_libs=yes dynamic_linker="$host_os dld.so" shlibpath_var=LD_LIBRARY_PATH shlibpath_overrides_runpath=yes # Unless +noenvvar is specified. library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext' soname_spec='$libname$release$shared_ext$major' if test 32 = "$HPUX_IA64_MODE"; then sys_lib_search_path_spec="/usr/lib/hpux32 /usr/local/lib/hpux32 /usr/local/lib" sys_lib_dlsearch_path_spec=/usr/lib/hpux32 else sys_lib_search_path_spec="/usr/lib/hpux64 /usr/local/lib/hpux64" sys_lib_dlsearch_path_spec=/usr/lib/hpux64 fi ;; hppa*64*) shrext_cmds='.sl' hardcode_into_libs=yes dynamic_linker="$host_os dld.sl" shlibpath_var=LD_LIBRARY_PATH # How should we handle SHLIB_PATH shlibpath_overrides_runpath=yes # Unless +noenvvar is specified. library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext' soname_spec='$libname$release$shared_ext$major' sys_lib_search_path_spec="/usr/lib/pa20_64 /usr/ccs/lib/pa20_64" sys_lib_dlsearch_path_spec=$sys_lib_search_path_spec ;; *) shrext_cmds='.sl' dynamic_linker="$host_os dld.sl" shlibpath_var=SHLIB_PATH shlibpath_overrides_runpath=no # +s is required to enable SHLIB_PATH library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext' soname_spec='$libname$release$shared_ext$major' ;; esac # HP-UX runs *really* slowly unless shared libraries are mode 555, ... postinstall_cmds='chmod 555 $lib' # or fails outright, so override atomically: install_override_mode=555 ;; interix[[3-9]]*) version_type=linux # correct to gnu/linux during the next big refactor need_lib_prefix=no need_version=no library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext' soname_spec='$libname$release$shared_ext$major' dynamic_linker='Interix 3.x ld.so.1 (PE, like ELF)' shlibpath_var=LD_LIBRARY_PATH shlibpath_overrides_runpath=no hardcode_into_libs=yes ;; irix5* | irix6* | nonstopux*) case $host_os in nonstopux*) version_type=nonstopux ;; *) if test yes = "$lt_cv_prog_gnu_ld"; then version_type=linux # correct to gnu/linux during the next big refactor else version_type=irix fi ;; esac need_lib_prefix=no need_version=no soname_spec='$libname$release$shared_ext$major' library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$release$shared_ext $libname$shared_ext' case $host_os in irix5* | nonstopux*) libsuff= shlibsuff= ;; *) case $LD in # libtool.m4 will add one of these switches to LD *-32|*"-32 "|*-melf32bsmip|*"-melf32bsmip ") libsuff= shlibsuff= libmagic=32-bit;; *-n32|*"-n32 "|*-melf32bmipn32|*"-melf32bmipn32 ") libsuff=32 shlibsuff=N32 libmagic=N32;; *-64|*"-64 "|*-melf64bmip|*"-melf64bmip ") libsuff=64 shlibsuff=64 libmagic=64-bit;; *) libsuff= shlibsuff= libmagic=never-match;; esac ;; esac shlibpath_var=LD_LIBRARY${shlibsuff}_PATH shlibpath_overrides_runpath=no sys_lib_search_path_spec="/usr/lib$libsuff /lib$libsuff /usr/local/lib$libsuff" sys_lib_dlsearch_path_spec="/usr/lib$libsuff /lib$libsuff" hardcode_into_libs=yes ;; # No shared lib support for Linux oldld, aout, or coff. linux*oldld* | linux*aout* | linux*coff*) dynamic_linker=no ;; linux*android*) version_type=none # Android doesn't support versioned libraries. need_lib_prefix=no need_version=no library_names_spec='$libname$release$shared_ext' soname_spec='$libname$release$shared_ext' finish_cmds= shlibpath_var=LD_LIBRARY_PATH shlibpath_overrides_runpath=yes # This implies no fast_install, which is unacceptable. # Some rework will be needed to allow for fast_install # before this can be enabled. hardcode_into_libs=yes dynamic_linker='Android linker' # Don't embed -rpath directories since the linker doesn't support them. _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-L$libdir' ;; # This must be glibc/ELF. linux* | k*bsd*-gnu | kopensolaris*-gnu | gnu*) version_type=linux # correct to gnu/linux during the next big refactor need_lib_prefix=no need_version=no library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext' soname_spec='$libname$release$shared_ext$major' finish_cmds='PATH="\$PATH:/sbin" ldconfig -n $libdir' shlibpath_var=LD_LIBRARY_PATH shlibpath_overrides_runpath=no # Some binutils ld are patched to set DT_RUNPATH AC_CACHE_VAL([lt_cv_shlibpath_overrides_runpath], [lt_cv_shlibpath_overrides_runpath=no save_LDFLAGS=$LDFLAGS save_libdir=$libdir eval "libdir=/foo; wl=\"$_LT_TAGVAR(lt_prog_compiler_wl, $1)\"; \ LDFLAGS=\"\$LDFLAGS $_LT_TAGVAR(hardcode_libdir_flag_spec, $1)\"" AC_LINK_IFELSE([AC_LANG_PROGRAM([],[])], [AS_IF([ ($OBJDUMP -p conftest$ac_exeext) 2>/dev/null | grep "RUNPATH.*$libdir" >/dev/null], [lt_cv_shlibpath_overrides_runpath=yes])]) LDFLAGS=$save_LDFLAGS libdir=$save_libdir ]) shlibpath_overrides_runpath=$lt_cv_shlibpath_overrides_runpath # This implies no fast_install, which is unacceptable. # Some rework will be needed to allow for fast_install # before this can be enabled. hardcode_into_libs=yes # Ideally, we could use ldconfig to report *all* directores which are # searched for libraries, however this is still not possible. Aside from not # being certain /sbin/ldconfig is available, command # 'ldconfig -N -X -v | grep ^/' on 64bit Fedora does not report /usr/lib64, # even though it is searched at run-time. Try to do the best guess by # appending ld.so.conf contents (and includes) to the search path. if test -f /etc/ld.so.conf; then lt_ld_extra=`awk '/^include / { system(sprintf("cd /etc; cat %s 2>/dev/null", \[$]2)); skip = 1; } { if (!skip) print \[$]0; skip = 0; }' < /etc/ld.so.conf | $SED -e 's/#.*//;/^[ ]*hwcap[ ]/d;s/[:, ]/ /g;s/=[^=]*$//;s/=[^= ]* / /g;s/"//g;/^$/d' | tr '\n' ' '` sys_lib_dlsearch_path_spec="/lib /usr/lib $lt_ld_extra" fi # We used to test for /lib/ld.so.1 and disable shared libraries on # powerpc, because MkLinux only supported shared libraries with the # GNU dynamic linker. Since this was broken with cross compilers, # most powerpc-linux boxes support dynamic linking these days and # people can always --disable-shared, the test was removed, and we # assume the GNU/Linux dynamic linker is in use. dynamic_linker='GNU/Linux ld.so' ;; netbsdelf*-gnu) version_type=linux need_lib_prefix=no need_version=no library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major ${libname}${shared_ext}' soname_spec='${libname}${release}${shared_ext}$major' shlibpath_var=LD_LIBRARY_PATH shlibpath_overrides_runpath=no hardcode_into_libs=yes dynamic_linker='NetBSD ld.elf_so' ;; netbsd*) version_type=sunos need_lib_prefix=no need_version=no if echo __ELF__ | $CC -E - | $GREP __ELF__ >/dev/null; then library_names_spec='$libname$release$shared_ext$versuffix $libname$shared_ext$versuffix' finish_cmds='PATH="\$PATH:/sbin" ldconfig -m $libdir' dynamic_linker='NetBSD (a.out) ld.so' else library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext' soname_spec='$libname$release$shared_ext$major' dynamic_linker='NetBSD ld.elf_so' fi shlibpath_var=LD_LIBRARY_PATH shlibpath_overrides_runpath=yes hardcode_into_libs=yes ;; newsos6) version_type=linux # correct to gnu/linux during the next big refactor library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext' shlibpath_var=LD_LIBRARY_PATH shlibpath_overrides_runpath=yes ;; *nto* | *qnx*) version_type=qnx need_lib_prefix=no need_version=no library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext' soname_spec='$libname$release$shared_ext$major' shlibpath_var=LD_LIBRARY_PATH shlibpath_overrides_runpath=no hardcode_into_libs=yes dynamic_linker='ldqnx.so' ;; openbsd* | bitrig*) version_type=sunos sys_lib_dlsearch_path_spec=/usr/lib need_lib_prefix=no if test -z "`echo __ELF__ | $CC -E - | $GREP __ELF__`"; then need_version=no else need_version=yes fi library_names_spec='$libname$release$shared_ext$versuffix $libname$shared_ext$versuffix' finish_cmds='PATH="\$PATH:/sbin" ldconfig -m $libdir' shlibpath_var=LD_LIBRARY_PATH shlibpath_overrides_runpath=yes ;; os2*) libname_spec='$name' version_type=windows shrext_cmds=.dll need_version=no need_lib_prefix=no # OS/2 can only load a DLL with a base name of 8 characters or less. soname_spec='`test -n "$os2dllname" && libname="$os2dllname"; v=$($ECHO $release$versuffix | tr -d .-); n=$($ECHO $libname | cut -b -$((8 - ${#v})) | tr . _); $ECHO $n$v`$shared_ext' library_names_spec='${libname}_dll.$libext' dynamic_linker='OS/2 ld.exe' shlibpath_var=BEGINLIBPATH sys_lib_search_path_spec="/lib /usr/lib /usr/local/lib" sys_lib_dlsearch_path_spec=$sys_lib_search_path_spec postinstall_cmds='base_file=`basename \$file`~ dlpath=`$SHELL 2>&1 -c '\''. $dir/'\''\$base_file'\''i; $ECHO \$dlname'\''`~ dldir=$destdir/`dirname \$dlpath`~ test -d \$dldir || mkdir -p \$dldir~ $install_prog $dir/$dlname \$dldir/$dlname~ chmod a+x \$dldir/$dlname~ if test -n '\''$stripme'\'' && test -n '\''$striplib'\''; then eval '\''$striplib \$dldir/$dlname'\'' || exit \$?; fi' postuninstall_cmds='dldll=`$SHELL 2>&1 -c '\''. $file; $ECHO \$dlname'\''`~ dlpath=$dir/\$dldll~ $RM \$dlpath' ;; osf3* | osf4* | osf5*) version_type=osf need_lib_prefix=no need_version=no soname_spec='$libname$release$shared_ext$major' library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext' shlibpath_var=LD_LIBRARY_PATH sys_lib_search_path_spec="/usr/shlib /usr/ccs/lib /usr/lib/cmplrs/cc /usr/lib /usr/local/lib /var/shlib" sys_lib_dlsearch_path_spec=$sys_lib_search_path_spec ;; rdos*) dynamic_linker=no ;; solaris*) version_type=linux # correct to gnu/linux during the next big refactor need_lib_prefix=no need_version=no library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext' soname_spec='$libname$release$shared_ext$major' shlibpath_var=LD_LIBRARY_PATH shlibpath_overrides_runpath=yes hardcode_into_libs=yes # ldd complains unless libraries are executable postinstall_cmds='chmod +x $lib' ;; sunos4*) version_type=sunos library_names_spec='$libname$release$shared_ext$versuffix $libname$shared_ext$versuffix' finish_cmds='PATH="\$PATH:/usr/etc" ldconfig $libdir' shlibpath_var=LD_LIBRARY_PATH shlibpath_overrides_runpath=yes if test yes = "$with_gnu_ld"; then need_lib_prefix=no fi need_version=yes ;; sysv4 | sysv4.3*) version_type=linux # correct to gnu/linux during the next big refactor library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext' soname_spec='$libname$release$shared_ext$major' shlibpath_var=LD_LIBRARY_PATH case $host_vendor in sni) shlibpath_overrides_runpath=no need_lib_prefix=no runpath_var=LD_RUN_PATH ;; siemens) need_lib_prefix=no ;; motorola) need_lib_prefix=no need_version=no shlibpath_overrides_runpath=no sys_lib_search_path_spec='/lib /usr/lib /usr/ccs/lib' ;; esac ;; sysv4*MP*) if test -d /usr/nec; then version_type=linux # correct to gnu/linux during the next big refactor library_names_spec='$libname$shared_ext.$versuffix $libname$shared_ext.$major $libname$shared_ext' soname_spec='$libname$shared_ext.$major' shlibpath_var=LD_LIBRARY_PATH fi ;; sysv5* | sco3.2v5* | sco5v6* | unixware* | OpenUNIX* | sysv4*uw2*) version_type=sco need_lib_prefix=no need_version=no library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext $libname$shared_ext' soname_spec='$libname$release$shared_ext$major' shlibpath_var=LD_LIBRARY_PATH shlibpath_overrides_runpath=yes hardcode_into_libs=yes if test yes = "$with_gnu_ld"; then sys_lib_search_path_spec='/usr/local/lib /usr/gnu/lib /usr/ccs/lib /usr/lib /lib' else sys_lib_search_path_spec='/usr/ccs/lib /usr/lib' case $host_os in sco3.2v5*) sys_lib_search_path_spec="$sys_lib_search_path_spec /lib" ;; esac fi sys_lib_dlsearch_path_spec='/usr/lib' ;; tpf*) # TPF is a cross-target only. Preferred cross-host = GNU/Linux. version_type=linux # correct to gnu/linux during the next big refactor need_lib_prefix=no need_version=no library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext' shlibpath_var=LD_LIBRARY_PATH shlibpath_overrides_runpath=no hardcode_into_libs=yes ;; uts4*) version_type=linux # correct to gnu/linux during the next big refactor library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext' soname_spec='$libname$release$shared_ext$major' shlibpath_var=LD_LIBRARY_PATH ;; *) dynamic_linker=no ;; esac AC_MSG_RESULT([$dynamic_linker]) test no = "$dynamic_linker" && can_build_shared=no variables_saved_for_relink="PATH $shlibpath_var $runpath_var" if test yes = "$GCC"; then variables_saved_for_relink="$variables_saved_for_relink GCC_EXEC_PREFIX COMPILER_PATH LIBRARY_PATH" fi if test set = "${lt_cv_sys_lib_search_path_spec+set}"; then sys_lib_search_path_spec=$lt_cv_sys_lib_search_path_spec fi if test set = "${lt_cv_sys_lib_dlsearch_path_spec+set}"; then sys_lib_dlsearch_path_spec=$lt_cv_sys_lib_dlsearch_path_spec fi # remember unaugmented sys_lib_dlsearch_path content for libtool script decls... configure_time_dlsearch_path=$sys_lib_dlsearch_path_spec # ... but it needs LT_SYS_LIBRARY_PATH munging for other configure-time code func_munge_path_list sys_lib_dlsearch_path_spec "$LT_SYS_LIBRARY_PATH" # to be used as default LT_SYS_LIBRARY_PATH value in generated libtool configure_time_lt_sys_library_path=$LT_SYS_LIBRARY_PATH _LT_DECL([], [variables_saved_for_relink], [1], [Variables whose values should be saved in libtool wrapper scripts and restored at link time]) _LT_DECL([], [need_lib_prefix], [0], [Do we need the "lib" prefix for modules?]) _LT_DECL([], [need_version], [0], [Do we need a version for libraries?]) _LT_DECL([], [version_type], [0], [Library versioning type]) _LT_DECL([], [runpath_var], [0], [Shared library runtime path variable]) _LT_DECL([], [shlibpath_var], [0],[Shared library path variable]) _LT_DECL([], [shlibpath_overrides_runpath], [0], [Is shlibpath searched before the hard-coded library search path?]) _LT_DECL([], [libname_spec], [1], [Format of library name prefix]) _LT_DECL([], [library_names_spec], [1], [[List of archive names. First name is the real one, the rest are links. The last name is the one that the linker finds with -lNAME]]) _LT_DECL([], [soname_spec], [1], [[The coded name of the library, if different from the real name]]) _LT_DECL([], [install_override_mode], [1], [Permission mode override for installation of shared libraries]) _LT_DECL([], [postinstall_cmds], [2], [Command to use after installation of a shared archive]) _LT_DECL([], [postuninstall_cmds], [2], [Command to use after uninstallation of a shared archive]) _LT_DECL([], [finish_cmds], [2], [Commands used to finish a libtool library installation in a directory]) _LT_DECL([], [finish_eval], [1], [[As "finish_cmds", except a single script fragment to be evaled but not shown]]) _LT_DECL([], [hardcode_into_libs], [0], [Whether we should hardcode library paths into libraries]) _LT_DECL([], [sys_lib_search_path_spec], [2], [Compile-time system search path for libraries]) _LT_DECL([sys_lib_dlsearch_path_spec], [configure_time_dlsearch_path], [2], [Detected run-time system search path for libraries]) _LT_DECL([], [configure_time_lt_sys_library_path], [2], [Explicit LT_SYS_LIBRARY_PATH set during ./configure time]) ])# _LT_SYS_DYNAMIC_LINKER # _LT_PATH_TOOL_PREFIX(TOOL) # -------------------------- # find a file program that can recognize shared library AC_DEFUN([_LT_PATH_TOOL_PREFIX], [m4_require([_LT_DECL_EGREP])dnl AC_MSG_CHECKING([for $1]) AC_CACHE_VAL(lt_cv_path_MAGIC_CMD, [case $MAGIC_CMD in [[\\/*] | ?:[\\/]*]) lt_cv_path_MAGIC_CMD=$MAGIC_CMD # Let the user override the test with a path. ;; *) lt_save_MAGIC_CMD=$MAGIC_CMD lt_save_ifs=$IFS; IFS=$PATH_SEPARATOR dnl $ac_dummy forces splitting on constant user-supplied paths. dnl POSIX.2 word splitting is done only on the output of word expansions, dnl not every word. This closes a longstanding sh security hole. ac_dummy="m4_if([$2], , $PATH, [$2])" for ac_dir in $ac_dummy; do IFS=$lt_save_ifs test -z "$ac_dir" && ac_dir=. if test -f "$ac_dir/$1"; then lt_cv_path_MAGIC_CMD=$ac_dir/"$1" if test -n "$file_magic_test_file"; then case $deplibs_check_method in "file_magic "*) file_magic_regex=`expr "$deplibs_check_method" : "file_magic \(.*\)"` MAGIC_CMD=$lt_cv_path_MAGIC_CMD if eval $file_magic_cmd \$file_magic_test_file 2> /dev/null | $EGREP "$file_magic_regex" > /dev/null; then : else cat <<_LT_EOF 1>&2 *** Warning: the command libtool uses to detect shared libraries, *** $file_magic_cmd, produces output that libtool cannot recognize. *** The result is that libtool may fail to recognize shared libraries *** as such. This will affect the creation of libtool libraries that *** depend on shared libraries, but programs linked with such libtool *** libraries will work regardless of this problem. Nevertheless, you *** may want to report the problem to your system manager and/or to *** bug-libtool@gnu.org _LT_EOF fi ;; esac fi break fi done IFS=$lt_save_ifs MAGIC_CMD=$lt_save_MAGIC_CMD ;; esac]) MAGIC_CMD=$lt_cv_path_MAGIC_CMD if test -n "$MAGIC_CMD"; then AC_MSG_RESULT($MAGIC_CMD) else AC_MSG_RESULT(no) fi _LT_DECL([], [MAGIC_CMD], [0], [Used to examine libraries when file_magic_cmd begins with "file"])dnl ])# _LT_PATH_TOOL_PREFIX # Old name: AU_ALIAS([AC_PATH_TOOL_PREFIX], [_LT_PATH_TOOL_PREFIX]) dnl aclocal-1.4 backwards compatibility: dnl AC_DEFUN([AC_PATH_TOOL_PREFIX], []) # _LT_PATH_MAGIC # -------------- # find a file program that can recognize a shared library m4_defun([_LT_PATH_MAGIC], [_LT_PATH_TOOL_PREFIX(${ac_tool_prefix}file, /usr/bin$PATH_SEPARATOR$PATH) if test -z "$lt_cv_path_MAGIC_CMD"; then if test -n "$ac_tool_prefix"; then _LT_PATH_TOOL_PREFIX(file, /usr/bin$PATH_SEPARATOR$PATH) else MAGIC_CMD=: fi fi ])# _LT_PATH_MAGIC # LT_PATH_LD # ---------- # find the pathname to the GNU or non-GNU linker AC_DEFUN([LT_PATH_LD], [AC_REQUIRE([AC_PROG_CC])dnl AC_REQUIRE([AC_CANONICAL_HOST])dnl AC_REQUIRE([AC_CANONICAL_BUILD])dnl m4_require([_LT_DECL_SED])dnl m4_require([_LT_DECL_EGREP])dnl m4_require([_LT_PROG_ECHO_BACKSLASH])dnl AC_ARG_WITH([gnu-ld], [AS_HELP_STRING([--with-gnu-ld], [assume the C compiler uses GNU ld @<:@default=no@:>@])], [test no = "$withval" || with_gnu_ld=yes], [with_gnu_ld=no])dnl ac_prog=ld if test yes = "$GCC"; then # Check if gcc -print-prog-name=ld gives a path. AC_MSG_CHECKING([for ld used by $CC]) case $host in *-*-mingw*) # gcc leaves a trailing carriage return, which upsets mingw ac_prog=`($CC -print-prog-name=ld) 2>&5 | tr -d '\015'` ;; *) ac_prog=`($CC -print-prog-name=ld) 2>&5` ;; esac case $ac_prog in # Accept absolute paths. [[\\/]]* | ?:[[\\/]]*) re_direlt='/[[^/]][[^/]]*/\.\./' # Canonicalize the pathname of ld ac_prog=`$ECHO "$ac_prog"| $SED 's%\\\\%/%g'` while $ECHO "$ac_prog" | $GREP "$re_direlt" > /dev/null 2>&1; do ac_prog=`$ECHO $ac_prog| $SED "s%$re_direlt%/%"` done test -z "$LD" && LD=$ac_prog ;; "") # If it fails, then pretend we aren't using GCC. ac_prog=ld ;; *) # If it is relative, then search for the first ld in PATH. with_gnu_ld=unknown ;; esac elif test yes = "$with_gnu_ld"; then AC_MSG_CHECKING([for GNU ld]) else AC_MSG_CHECKING([for non-GNU ld]) fi AC_CACHE_VAL(lt_cv_path_LD, [if test -z "$LD"; then lt_save_ifs=$IFS; IFS=$PATH_SEPARATOR for ac_dir in $PATH; do IFS=$lt_save_ifs test -z "$ac_dir" && ac_dir=. if test -f "$ac_dir/$ac_prog" || test -f "$ac_dir/$ac_prog$ac_exeext"; then lt_cv_path_LD=$ac_dir/$ac_prog # Check to see if the program is GNU ld. I'd rather use --version, # but apparently some variants of GNU ld only accept -v. # Break only if it was the GNU/non-GNU ld that we prefer. case `"$lt_cv_path_LD" -v 2>&1 &1 conftest.i cat conftest.i conftest.i >conftest2.i : ${lt_DD:=$DD} AC_PATH_PROGS_FEATURE_CHECK([lt_DD], [dd], [if "$ac_path_lt_DD" bs=32 count=1 conftest.out 2>/dev/null; then cmp -s conftest.i conftest.out \ && ac_cv_path_lt_DD="$ac_path_lt_DD" ac_path_lt_DD_found=: fi]) rm -f conftest.i conftest2.i conftest.out]) ])# _LT_PATH_DD # _LT_CMD_TRUNCATE # ---------------- # find command to truncate a binary pipe m4_defun([_LT_CMD_TRUNCATE], [m4_require([_LT_PATH_DD]) AC_CACHE_CHECK([how to truncate binary pipes], [lt_cv_truncate_bin], [printf 0123456789abcdef0123456789abcdef >conftest.i cat conftest.i conftest.i >conftest2.i lt_cv_truncate_bin= if "$ac_cv_path_lt_DD" bs=32 count=1 conftest.out 2>/dev/null; then cmp -s conftest.i conftest.out \ && lt_cv_truncate_bin="$ac_cv_path_lt_DD bs=4096 count=1" fi rm -f conftest.i conftest2.i conftest.out test -z "$lt_cv_truncate_bin" && lt_cv_truncate_bin="$SED -e 4q"]) _LT_DECL([lt_truncate_bin], [lt_cv_truncate_bin], [1], [Command to truncate a binary pipe]) ])# _LT_CMD_TRUNCATE # _LT_CHECK_MAGIC_METHOD # ---------------------- # how to check for library dependencies # -- PORTME fill in with the dynamic library characteristics m4_defun([_LT_CHECK_MAGIC_METHOD], [m4_require([_LT_DECL_EGREP]) m4_require([_LT_DECL_OBJDUMP]) AC_CACHE_CHECK([how to recognize dependent libraries], lt_cv_deplibs_check_method, [lt_cv_file_magic_cmd='$MAGIC_CMD' lt_cv_file_magic_test_file= lt_cv_deplibs_check_method='unknown' # Need to set the preceding variable on all platforms that support # interlibrary dependencies. # 'none' -- dependencies not supported. # 'unknown' -- same as none, but documents that we really don't know. # 'pass_all' -- all dependencies passed with no checks. # 'test_compile' -- check by making test program. # 'file_magic [[regex]]' -- check by looking for files in library path # that responds to the $file_magic_cmd with a given extended regex. # If you have 'file' or equivalent on your system and you're not sure # whether 'pass_all' will *always* work, you probably want this one. case $host_os in aix[[4-9]]*) lt_cv_deplibs_check_method=pass_all ;; beos*) lt_cv_deplibs_check_method=pass_all ;; bsdi[[45]]*) lt_cv_deplibs_check_method='file_magic ELF [[0-9]][[0-9]]*-bit [[ML]]SB (shared object|dynamic lib)' lt_cv_file_magic_cmd='$FILECMD -L' lt_cv_file_magic_test_file=/shlib/libc.so ;; cygwin*) # func_win32_libid is a shell function defined in ltmain.sh lt_cv_deplibs_check_method='file_magic ^x86 archive import|^x86 DLL' lt_cv_file_magic_cmd='func_win32_libid' ;; mingw* | pw32*) # Base MSYS/MinGW do not provide the 'file' command needed by # func_win32_libid shell function, so use a weaker test based on 'objdump', # unless we find 'file', for example because we are cross-compiling. if ( file / ) >/dev/null 2>&1; then lt_cv_deplibs_check_method='file_magic ^x86 archive import|^x86 DLL' lt_cv_file_magic_cmd='func_win32_libid' else # Keep this pattern in sync with the one in func_win32_libid. lt_cv_deplibs_check_method='file_magic file format (pei*-i386(.*architecture: i386)?|pe-arm-wince|pe-x86-64)' lt_cv_file_magic_cmd='$OBJDUMP -f' fi ;; cegcc*) # use the weaker test based on 'objdump'. See mingw*. lt_cv_deplibs_check_method='file_magic file format pe-arm-.*little(.*architecture: arm)?' lt_cv_file_magic_cmd='$OBJDUMP -f' ;; darwin* | rhapsody*) lt_cv_deplibs_check_method=pass_all ;; freebsd* | dragonfly* | midnightbsd*) if echo __ELF__ | $CC -E - | $GREP __ELF__ > /dev/null; then case $host_cpu in i*86 ) # Not sure whether the presence of OpenBSD here was a mistake. # Let's accept both of them until this is cleared up. lt_cv_deplibs_check_method='file_magic (FreeBSD|OpenBSD|DragonFly)/i[[3-9]]86 (compact )?demand paged shared library' lt_cv_file_magic_cmd=$FILECMD lt_cv_file_magic_test_file=`echo /usr/lib/libc.so.*` ;; esac else lt_cv_deplibs_check_method=pass_all fi ;; haiku*) lt_cv_deplibs_check_method=pass_all ;; hpux10.20* | hpux11*) lt_cv_file_magic_cmd=$FILECMD case $host_cpu in ia64*) lt_cv_deplibs_check_method='file_magic (s[[0-9]][[0-9]][[0-9]]|ELF-[[0-9]][[0-9]]) shared object file - IA64' lt_cv_file_magic_test_file=/usr/lib/hpux32/libc.so ;; hppa*64*) [lt_cv_deplibs_check_method='file_magic (s[0-9][0-9][0-9]|ELF[ -][0-9][0-9])(-bit)?( [LM]SB)? shared object( file)?[, -]* PA-RISC [0-9]\.[0-9]'] lt_cv_file_magic_test_file=/usr/lib/pa20_64/libc.sl ;; *) lt_cv_deplibs_check_method='file_magic (s[[0-9]][[0-9]][[0-9]]|PA-RISC[[0-9]]\.[[0-9]]) shared library' lt_cv_file_magic_test_file=/usr/lib/libc.sl ;; esac ;; interix[[3-9]]*) # PIC code is broken on Interix 3.x, that's why |\.a not |_pic\.a here lt_cv_deplibs_check_method='match_pattern /lib[[^/]]+(\.so|\.a)$' ;; irix5* | irix6* | nonstopux*) case $LD in *-32|*"-32 ") libmagic=32-bit;; *-n32|*"-n32 ") libmagic=N32;; *-64|*"-64 ") libmagic=64-bit;; *) libmagic=never-match;; esac lt_cv_deplibs_check_method=pass_all ;; # This must be glibc/ELF. linux* | k*bsd*-gnu | kopensolaris*-gnu | gnu*) lt_cv_deplibs_check_method=pass_all ;; netbsd* | netbsdelf*-gnu) if echo __ELF__ | $CC -E - | $GREP __ELF__ > /dev/null; then lt_cv_deplibs_check_method='match_pattern /lib[[^/]]+(\.so\.[[0-9]]+\.[[0-9]]+|_pic\.a)$' else lt_cv_deplibs_check_method='match_pattern /lib[[^/]]+(\.so|_pic\.a)$' fi ;; newos6*) lt_cv_deplibs_check_method='file_magic ELF [[0-9]][[0-9]]*-bit [[ML]]SB (executable|dynamic lib)' lt_cv_file_magic_cmd=$FILECMD lt_cv_file_magic_test_file=/usr/lib/libnls.so ;; *nto* | *qnx*) lt_cv_deplibs_check_method=pass_all ;; openbsd* | bitrig*) if test -z "`echo __ELF__ | $CC -E - | $GREP __ELF__`"; then lt_cv_deplibs_check_method='match_pattern /lib[[^/]]+(\.so\.[[0-9]]+\.[[0-9]]+|\.so|_pic\.a)$' else lt_cv_deplibs_check_method='match_pattern /lib[[^/]]+(\.so\.[[0-9]]+\.[[0-9]]+|_pic\.a)$' fi ;; osf3* | osf4* | osf5*) lt_cv_deplibs_check_method=pass_all ;; rdos*) lt_cv_deplibs_check_method=pass_all ;; solaris*) lt_cv_deplibs_check_method=pass_all ;; sysv5* | sco3.2v5* | sco5v6* | unixware* | OpenUNIX* | sysv4*uw2*) lt_cv_deplibs_check_method=pass_all ;; sysv4 | sysv4.3*) case $host_vendor in motorola) lt_cv_deplibs_check_method='file_magic ELF [[0-9]][[0-9]]*-bit [[ML]]SB (shared object|dynamic lib) M[[0-9]][[0-9]]* Version [[0-9]]' lt_cv_file_magic_test_file=`echo /usr/lib/libc.so*` ;; ncr) lt_cv_deplibs_check_method=pass_all ;; sequent) lt_cv_file_magic_cmd='/bin/file' lt_cv_deplibs_check_method='file_magic ELF [[0-9]][[0-9]]*-bit [[LM]]SB (shared object|dynamic lib )' ;; sni) lt_cv_file_magic_cmd='/bin/file' lt_cv_deplibs_check_method="file_magic ELF [[0-9]][[0-9]]*-bit [[LM]]SB dynamic lib" lt_cv_file_magic_test_file=/lib/libc.so ;; siemens) lt_cv_deplibs_check_method=pass_all ;; pc) lt_cv_deplibs_check_method=pass_all ;; esac ;; tpf*) lt_cv_deplibs_check_method=pass_all ;; os2*) lt_cv_deplibs_check_method=pass_all ;; esac ]) file_magic_glob= want_nocaseglob=no if test "$build" = "$host"; then case $host_os in mingw* | pw32*) if ( shopt | grep nocaseglob ) >/dev/null 2>&1; then want_nocaseglob=yes else file_magic_glob=`echo aAbBcCdDeEfFgGhHiIjJkKlLmMnNoOpPqQrRsStTuUvVwWxXyYzZ | $SED -e "s/\(..\)/s\/[[\1]]\/[[\1]]\/g;/g"` fi ;; esac fi file_magic_cmd=$lt_cv_file_magic_cmd deplibs_check_method=$lt_cv_deplibs_check_method test -z "$deplibs_check_method" && deplibs_check_method=unknown _LT_DECL([], [deplibs_check_method], [1], [Method to check whether dependent libraries are shared objects]) _LT_DECL([], [file_magic_cmd], [1], [Command to use when deplibs_check_method = "file_magic"]) _LT_DECL([], [file_magic_glob], [1], [How to find potential files when deplibs_check_method = "file_magic"]) _LT_DECL([], [want_nocaseglob], [1], [Find potential files using nocaseglob when deplibs_check_method = "file_magic"]) ])# _LT_CHECK_MAGIC_METHOD # LT_PATH_NM # ---------- # find the pathname to a BSD- or MS-compatible name lister AC_DEFUN([LT_PATH_NM], [AC_REQUIRE([AC_PROG_CC])dnl AC_CACHE_CHECK([for BSD- or MS-compatible name lister (nm)], lt_cv_path_NM, [if test -n "$NM"; then # Let the user override the test. lt_cv_path_NM=$NM else lt_nm_to_check=${ac_tool_prefix}nm if test -n "$ac_tool_prefix" && test "$build" = "$host"; then lt_nm_to_check="$lt_nm_to_check nm" fi for lt_tmp_nm in $lt_nm_to_check; do lt_save_ifs=$IFS; IFS=$PATH_SEPARATOR for ac_dir in $PATH /usr/ccs/bin/elf /usr/ccs/bin /usr/ucb /bin; do IFS=$lt_save_ifs test -z "$ac_dir" && ac_dir=. tmp_nm=$ac_dir/$lt_tmp_nm if test -f "$tmp_nm" || test -f "$tmp_nm$ac_exeext"; then # Check to see if the nm accepts a BSD-compat flag. # Adding the 'sed 1q' prevents false positives on HP-UX, which says: # nm: unknown option "B" ignored # Tru64's nm complains that /dev/null is an invalid object file # MSYS converts /dev/null to NUL, MinGW nm treats NUL as empty case $build_os in mingw*) lt_bad_file=conftest.nm/nofile ;; *) lt_bad_file=/dev/null ;; esac case `"$tmp_nm" -B $lt_bad_file 2>&1 | $SED '1q'` in *$lt_bad_file* | *'Invalid file or object type'*) lt_cv_path_NM="$tmp_nm -B" break 2 ;; *) case `"$tmp_nm" -p /dev/null 2>&1 | $SED '1q'` in */dev/null*) lt_cv_path_NM="$tmp_nm -p" break 2 ;; *) lt_cv_path_NM=${lt_cv_path_NM="$tmp_nm"} # keep the first match, but continue # so that we can try to find one that supports BSD flags ;; esac ;; esac fi done IFS=$lt_save_ifs done : ${lt_cv_path_NM=no} fi]) if test no != "$lt_cv_path_NM"; then NM=$lt_cv_path_NM else # Didn't find any BSD compatible name lister, look for dumpbin. if test -n "$DUMPBIN"; then : # Let the user override the test. else AC_CHECK_TOOLS(DUMPBIN, [dumpbin "link -dump"], :) case `$DUMPBIN -symbols -headers /dev/null 2>&1 | $SED '1q'` in *COFF*) DUMPBIN="$DUMPBIN -symbols -headers" ;; *) DUMPBIN=: ;; esac fi AC_SUBST([DUMPBIN]) if test : != "$DUMPBIN"; then NM=$DUMPBIN fi fi test -z "$NM" && NM=nm AC_SUBST([NM]) _LT_DECL([], [NM], [1], [A BSD- or MS-compatible name lister])dnl AC_CACHE_CHECK([the name lister ($NM) interface], [lt_cv_nm_interface], [lt_cv_nm_interface="BSD nm" echo "int some_variable = 0;" > conftest.$ac_ext (eval echo "\"\$as_me:$LINENO: $ac_compile\"" >&AS_MESSAGE_LOG_FD) (eval "$ac_compile" 2>conftest.err) cat conftest.err >&AS_MESSAGE_LOG_FD (eval echo "\"\$as_me:$LINENO: $NM \\\"conftest.$ac_objext\\\"\"" >&AS_MESSAGE_LOG_FD) (eval "$NM \"conftest.$ac_objext\"" 2>conftest.err > conftest.out) cat conftest.err >&AS_MESSAGE_LOG_FD (eval echo "\"\$as_me:$LINENO: output\"" >&AS_MESSAGE_LOG_FD) cat conftest.out >&AS_MESSAGE_LOG_FD if $GREP 'External.*some_variable' conftest.out > /dev/null; then lt_cv_nm_interface="MS dumpbin" fi rm -f conftest*]) ])# LT_PATH_NM # Old names: AU_ALIAS([AM_PROG_NM], [LT_PATH_NM]) AU_ALIAS([AC_PROG_NM], [LT_PATH_NM]) dnl aclocal-1.4 backwards compatibility: dnl AC_DEFUN([AM_PROG_NM], []) dnl AC_DEFUN([AC_PROG_NM], []) # _LT_CHECK_SHAREDLIB_FROM_LINKLIB # -------------------------------- # how to determine the name of the shared library # associated with a specific link library. # -- PORTME fill in with the dynamic library characteristics m4_defun([_LT_CHECK_SHAREDLIB_FROM_LINKLIB], [m4_require([_LT_DECL_EGREP]) m4_require([_LT_DECL_OBJDUMP]) m4_require([_LT_DECL_DLLTOOL]) AC_CACHE_CHECK([how to associate runtime and link libraries], lt_cv_sharedlib_from_linklib_cmd, [lt_cv_sharedlib_from_linklib_cmd='unknown' case $host_os in cygwin* | mingw* | pw32* | cegcc*) # two different shell functions defined in ltmain.sh; # decide which one to use based on capabilities of $DLLTOOL case `$DLLTOOL --help 2>&1` in *--identify-strict*) lt_cv_sharedlib_from_linklib_cmd=func_cygming_dll_for_implib ;; *) lt_cv_sharedlib_from_linklib_cmd=func_cygming_dll_for_implib_fallback ;; esac ;; *) # fallback: assume linklib IS sharedlib lt_cv_sharedlib_from_linklib_cmd=$ECHO ;; esac ]) sharedlib_from_linklib_cmd=$lt_cv_sharedlib_from_linklib_cmd test -z "$sharedlib_from_linklib_cmd" && sharedlib_from_linklib_cmd=$ECHO _LT_DECL([], [sharedlib_from_linklib_cmd], [1], [Command to associate shared and link libraries]) ])# _LT_CHECK_SHAREDLIB_FROM_LINKLIB # _LT_PATH_MANIFEST_TOOL # ---------------------- # locate the manifest tool m4_defun([_LT_PATH_MANIFEST_TOOL], [AC_CHECK_TOOL(MANIFEST_TOOL, mt, :) test -z "$MANIFEST_TOOL" && MANIFEST_TOOL=mt AC_CACHE_CHECK([if $MANIFEST_TOOL is a manifest tool], [lt_cv_path_mainfest_tool], [lt_cv_path_mainfest_tool=no echo "$as_me:$LINENO: $MANIFEST_TOOL '-?'" >&AS_MESSAGE_LOG_FD $MANIFEST_TOOL '-?' 2>conftest.err > conftest.out cat conftest.err >&AS_MESSAGE_LOG_FD if $GREP 'Manifest Tool' conftest.out > /dev/null; then lt_cv_path_mainfest_tool=yes fi rm -f conftest*]) if test yes != "$lt_cv_path_mainfest_tool"; then MANIFEST_TOOL=: fi _LT_DECL([], [MANIFEST_TOOL], [1], [Manifest tool])dnl ])# _LT_PATH_MANIFEST_TOOL # _LT_DLL_DEF_P([FILE]) # --------------------- # True iff FILE is a Windows DLL '.def' file. # Keep in sync with func_dll_def_p in the libtool script AC_DEFUN([_LT_DLL_DEF_P], [dnl test DEF = "`$SED -n dnl -e '\''s/^[[ ]]*//'\'' dnl Strip leading whitespace -e '\''/^\(;.*\)*$/d'\'' dnl Delete empty lines and comments -e '\''s/^\(EXPORTS\|LIBRARY\)\([[ ]].*\)*$/DEF/p'\'' dnl -e q dnl Only consider the first "real" line $1`" dnl ])# _LT_DLL_DEF_P # LT_LIB_M # -------- # check for math library AC_DEFUN([LT_LIB_M], [AC_REQUIRE([AC_CANONICAL_HOST])dnl LIBM= case $host in *-*-beos* | *-*-cegcc* | *-*-cygwin* | *-*-haiku* | *-*-pw32* | *-*-darwin*) # These system don't have libm, or don't need it ;; *-ncr-sysv4.3*) AC_CHECK_LIB(mw, _mwvalidcheckl, LIBM=-lmw) AC_CHECK_LIB(m, cos, LIBM="$LIBM -lm") ;; *) AC_CHECK_LIB(m, cos, LIBM=-lm) ;; esac AC_SUBST([LIBM]) ])# LT_LIB_M # Old name: AU_ALIAS([AC_CHECK_LIBM], [LT_LIB_M]) dnl aclocal-1.4 backwards compatibility: dnl AC_DEFUN([AC_CHECK_LIBM], []) # _LT_COMPILER_NO_RTTI([TAGNAME]) # ------------------------------- m4_defun([_LT_COMPILER_NO_RTTI], [m4_require([_LT_TAG_COMPILER])dnl _LT_TAGVAR(lt_prog_compiler_no_builtin_flag, $1)= if test yes = "$GCC"; then case $cc_basename in nvcc*) _LT_TAGVAR(lt_prog_compiler_no_builtin_flag, $1)=' -Xcompiler -fno-builtin' ;; *) _LT_TAGVAR(lt_prog_compiler_no_builtin_flag, $1)=' -fno-builtin' ;; esac _LT_COMPILER_OPTION([if $compiler supports -fno-rtti -fno-exceptions], lt_cv_prog_compiler_rtti_exceptions, [-fno-rtti -fno-exceptions], [], [_LT_TAGVAR(lt_prog_compiler_no_builtin_flag, $1)="$_LT_TAGVAR(lt_prog_compiler_no_builtin_flag, $1) -fno-rtti -fno-exceptions"]) fi _LT_TAGDECL([no_builtin_flag], [lt_prog_compiler_no_builtin_flag], [1], [Compiler flag to turn off builtin functions]) ])# _LT_COMPILER_NO_RTTI # _LT_CMD_GLOBAL_SYMBOLS # ---------------------- m4_defun([_LT_CMD_GLOBAL_SYMBOLS], [AC_REQUIRE([AC_CANONICAL_HOST])dnl AC_REQUIRE([AC_PROG_CC])dnl AC_REQUIRE([AC_PROG_AWK])dnl AC_REQUIRE([LT_PATH_NM])dnl AC_REQUIRE([LT_PATH_LD])dnl m4_require([_LT_DECL_SED])dnl m4_require([_LT_DECL_EGREP])dnl m4_require([_LT_TAG_COMPILER])dnl # Check for command to grab the raw symbol name followed by C symbol from nm. AC_MSG_CHECKING([command to parse $NM output from $compiler object]) AC_CACHE_VAL([lt_cv_sys_global_symbol_pipe], [ # These are sane defaults that work on at least a few old systems. # [They come from Ultrix. What could be older than Ultrix?!! ;)] # Character class describing NM global symbol codes. symcode='[[BCDEGRST]]' # Regexp to match symbols that can be accessed directly from C. sympat='\([[_A-Za-z]][[_A-Za-z0-9]]*\)' # Define system-specific variables. case $host_os in aix*) symcode='[[BCDT]]' ;; cygwin* | mingw* | pw32* | cegcc*) symcode='[[ABCDGISTW]]' ;; hpux*) if test ia64 = "$host_cpu"; then symcode='[[ABCDEGRST]]' fi ;; irix* | nonstopux*) symcode='[[BCDEGRST]]' ;; osf*) symcode='[[BCDEGQRST]]' ;; solaris*) symcode='[[BDRT]]' ;; sco3.2v5*) symcode='[[DT]]' ;; sysv4.2uw2*) symcode='[[DT]]' ;; sysv5* | sco5v6* | unixware* | OpenUNIX*) symcode='[[ABDT]]' ;; sysv4) symcode='[[DFNSTU]]' ;; esac # If we're using GNU nm, then use its standard symbol codes. case `$NM -V 2>&1` in *GNU* | *'with BFD'*) symcode='[[ABCDGIRSTW]]' ;; esac if test "$lt_cv_nm_interface" = "MS dumpbin"; then # Gets list of data symbols to import. lt_cv_sys_global_symbol_to_import="$SED -n -e 's/^I .* \(.*\)$/\1/p'" # Adjust the below global symbol transforms to fixup imported variables. lt_cdecl_hook=" -e 's/^I .* \(.*\)$/extern __declspec(dllimport) char \1;/p'" lt_c_name_hook=" -e 's/^I .* \(.*\)$/ {\"\1\", (void *) 0},/p'" lt_c_name_lib_hook="\ -e 's/^I .* \(lib.*\)$/ {\"\1\", (void *) 0},/p'\ -e 's/^I .* \(.*\)$/ {\"lib\1\", (void *) 0},/p'" else # Disable hooks by default. lt_cv_sys_global_symbol_to_import= lt_cdecl_hook= lt_c_name_hook= lt_c_name_lib_hook= fi # Transform an extracted symbol line into a proper C declaration. # Some systems (esp. on ia64) link data and code symbols differently, # so use this general approach. lt_cv_sys_global_symbol_to_cdecl="$SED -n"\ $lt_cdecl_hook\ " -e 's/^T .* \(.*\)$/extern int \1();/p'"\ " -e 's/^$symcode$symcode* .* \(.*\)$/extern char \1;/p'" # Transform an extracted symbol line into symbol name and symbol address lt_cv_sys_global_symbol_to_c_name_address="$SED -n"\ $lt_c_name_hook\ " -e 's/^: \(.*\) .*$/ {\"\1\", (void *) 0},/p'"\ " -e 's/^$symcode$symcode* .* \(.*\)$/ {\"\1\", (void *) \&\1},/p'" # Transform an extracted symbol line into symbol name with lib prefix and # symbol address. lt_cv_sys_global_symbol_to_c_name_address_lib_prefix="$SED -n"\ $lt_c_name_lib_hook\ " -e 's/^: \(.*\) .*$/ {\"\1\", (void *) 0},/p'"\ " -e 's/^$symcode$symcode* .* \(lib.*\)$/ {\"\1\", (void *) \&\1},/p'"\ " -e 's/^$symcode$symcode* .* \(.*\)$/ {\"lib\1\", (void *) \&\1},/p'" # Handle CRLF in mingw tool chain opt_cr= case $build_os in mingw*) opt_cr=`$ECHO 'x\{0,1\}' | tr x '\015'` # option cr in regexp ;; esac # Try without a prefix underscore, then with it. for ac_symprfx in "" "_"; do # Transform symcode, sympat, and symprfx into a raw symbol and a C symbol. symxfrm="\\1 $ac_symprfx\\2 \\2" # Write the raw and C identifiers. if test "$lt_cv_nm_interface" = "MS dumpbin"; then # Fake it for dumpbin and say T for any non-static function, # D for any global variable and I for any imported variable. # Also find C++ and __fastcall symbols from MSVC++ or ICC, # which start with @ or ?. lt_cv_sys_global_symbol_pipe="$AWK ['"\ " {last_section=section; section=\$ 3};"\ " /^COFF SYMBOL TABLE/{for(i in hide) delete hide[i]};"\ " /Section length .*#relocs.*(pick any)/{hide[last_section]=1};"\ " /^ *Symbol name *: /{split(\$ 0,sn,\":\"); si=substr(sn[2],2)};"\ " /^ *Type *: code/{print \"T\",si,substr(si,length(prfx))};"\ " /^ *Type *: data/{print \"I\",si,substr(si,length(prfx))};"\ " \$ 0!~/External *\|/{next};"\ " / 0+ UNDEF /{next}; / UNDEF \([^|]\)*()/{next};"\ " {if(hide[section]) next};"\ " {f=\"D\"}; \$ 0~/\(\).*\|/{f=\"T\"};"\ " {split(\$ 0,a,/\||\r/); split(a[2],s)};"\ " s[1]~/^[@?]/{print f,s[1],s[1]; next};"\ " s[1]~prfx {split(s[1],t,\"@\"); print f,t[1],substr(t[1],length(prfx))}"\ " ' prfx=^$ac_symprfx]" else lt_cv_sys_global_symbol_pipe="$SED -n -e 's/^.*[[ ]]\($symcode$symcode*\)[[ ]][[ ]]*$ac_symprfx$sympat$opt_cr$/$symxfrm/p'" fi lt_cv_sys_global_symbol_pipe="$lt_cv_sys_global_symbol_pipe | $SED '/ __gnu_lto/d'" # Check to see that the pipe works correctly. pipe_works=no rm -f conftest* cat > conftest.$ac_ext <<_LT_EOF #ifdef __cplusplus extern "C" { #endif char nm_test_var; void nm_test_func(void); void nm_test_func(void){} #ifdef __cplusplus } #endif int main(){nm_test_var='a';nm_test_func();return(0);} _LT_EOF if AC_TRY_EVAL(ac_compile); then # Now try to grab the symbols. nlist=conftest.nm $ECHO "$as_me:$LINENO: $NM conftest.$ac_objext | $lt_cv_sys_global_symbol_pipe > $nlist" >&AS_MESSAGE_LOG_FD if eval "$NM" conftest.$ac_objext \| "$lt_cv_sys_global_symbol_pipe" \> $nlist 2>&AS_MESSAGE_LOG_FD && test -s "$nlist"; then # Try sorting and uniquifying the output. if sort "$nlist" | uniq > "$nlist"T; then mv -f "$nlist"T "$nlist" else rm -f "$nlist"T fi # Make sure that we snagged all the symbols we need. if $GREP ' nm_test_var$' "$nlist" >/dev/null; then if $GREP ' nm_test_func$' "$nlist" >/dev/null; then cat <<_LT_EOF > conftest.$ac_ext /* Keep this code in sync between libtool.m4, ltmain, lt_system.h, and tests. */ #if defined _WIN32 || defined __CYGWIN__ || defined _WIN32_WCE /* DATA imports from DLLs on WIN32 can't be const, because runtime relocations are performed -- see ld's documentation on pseudo-relocs. */ # define LT@&t@_DLSYM_CONST #elif defined __osf__ /* This system does not cope well with relocations in const data. */ # define LT@&t@_DLSYM_CONST #else # define LT@&t@_DLSYM_CONST const #endif #ifdef __cplusplus extern "C" { #endif _LT_EOF # Now generate the symbol file. eval "$lt_cv_sys_global_symbol_to_cdecl"' < "$nlist" | $GREP -v main >> conftest.$ac_ext' cat <<_LT_EOF >> conftest.$ac_ext /* The mapping between symbol names and symbols. */ LT@&t@_DLSYM_CONST struct { const char *name; void *address; } lt__PROGRAM__LTX_preloaded_symbols[[]] = { { "@PROGRAM@", (void *) 0 }, _LT_EOF $SED "s/^$symcode$symcode* .* \(.*\)$/ {\"\1\", (void *) \&\1},/" < "$nlist" | $GREP -v main >> conftest.$ac_ext cat <<\_LT_EOF >> conftest.$ac_ext {0, (void *) 0} }; /* This works around a problem in FreeBSD linker */ #ifdef FREEBSD_WORKAROUND static const void *lt_preloaded_setup() { return lt__PROGRAM__LTX_preloaded_symbols; } #endif #ifdef __cplusplus } #endif _LT_EOF # Now try linking the two files. mv conftest.$ac_objext conftstm.$ac_objext lt_globsym_save_LIBS=$LIBS lt_globsym_save_CFLAGS=$CFLAGS LIBS=conftstm.$ac_objext CFLAGS="$CFLAGS$_LT_TAGVAR(lt_prog_compiler_no_builtin_flag, $1)" if AC_TRY_EVAL(ac_link) && test -s conftest$ac_exeext; then pipe_works=yes fi LIBS=$lt_globsym_save_LIBS CFLAGS=$lt_globsym_save_CFLAGS else echo "cannot find nm_test_func in $nlist" >&AS_MESSAGE_LOG_FD fi else echo "cannot find nm_test_var in $nlist" >&AS_MESSAGE_LOG_FD fi else echo "cannot run $lt_cv_sys_global_symbol_pipe" >&AS_MESSAGE_LOG_FD fi else echo "$progname: failed program was:" >&AS_MESSAGE_LOG_FD cat conftest.$ac_ext >&5 fi rm -rf conftest* conftst* # Do not use the global_symbol_pipe unless it works. if test yes = "$pipe_works"; then break else lt_cv_sys_global_symbol_pipe= fi done ]) if test -z "$lt_cv_sys_global_symbol_pipe"; then lt_cv_sys_global_symbol_to_cdecl= fi if test -z "$lt_cv_sys_global_symbol_pipe$lt_cv_sys_global_symbol_to_cdecl"; then AC_MSG_RESULT(failed) else AC_MSG_RESULT(ok) fi # Response file support. if test "$lt_cv_nm_interface" = "MS dumpbin"; then nm_file_list_spec='@' elif $NM --help 2>/dev/null | grep '[[@]]FILE' >/dev/null; then nm_file_list_spec='@' fi _LT_DECL([global_symbol_pipe], [lt_cv_sys_global_symbol_pipe], [1], [Take the output of nm and produce a listing of raw symbols and C names]) _LT_DECL([global_symbol_to_cdecl], [lt_cv_sys_global_symbol_to_cdecl], [1], [Transform the output of nm in a proper C declaration]) _LT_DECL([global_symbol_to_import], [lt_cv_sys_global_symbol_to_import], [1], [Transform the output of nm into a list of symbols to manually relocate]) _LT_DECL([global_symbol_to_c_name_address], [lt_cv_sys_global_symbol_to_c_name_address], [1], [Transform the output of nm in a C name address pair]) _LT_DECL([global_symbol_to_c_name_address_lib_prefix], [lt_cv_sys_global_symbol_to_c_name_address_lib_prefix], [1], [Transform the output of nm in a C name address pair when lib prefix is needed]) _LT_DECL([nm_interface], [lt_cv_nm_interface], [1], [The name lister interface]) _LT_DECL([], [nm_file_list_spec], [1], [Specify filename containing input files for $NM]) ]) # _LT_CMD_GLOBAL_SYMBOLS # _LT_COMPILER_PIC([TAGNAME]) # --------------------------- m4_defun([_LT_COMPILER_PIC], [m4_require([_LT_TAG_COMPILER])dnl _LT_TAGVAR(lt_prog_compiler_wl, $1)= _LT_TAGVAR(lt_prog_compiler_pic, $1)= _LT_TAGVAR(lt_prog_compiler_static, $1)= m4_if([$1], [CXX], [ # C++ specific cases for pic, static, wl, etc. if test yes = "$GXX"; then _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' _LT_TAGVAR(lt_prog_compiler_static, $1)='-static' case $host_os in aix*) # All AIX code is PIC. if test ia64 = "$host_cpu"; then # AIX 5 now supports IA64 processor _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic' fi _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fPIC' ;; amigaos*) case $host_cpu in powerpc) # see comment about AmigaOS4 .so support _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fPIC' ;; m68k) # FIXME: we need at least 68020 code to build shared libraries, but # adding the '-m68020' flag to GCC prevents building anything better, # like '-m68040'. _LT_TAGVAR(lt_prog_compiler_pic, $1)='-m68020 -resident32 -malways-restore-a4' ;; esac ;; beos* | irix5* | irix6* | nonstopux* | osf3* | osf4* | osf5*) # PIC is the default for these OSes. ;; mingw* | cygwin* | os2* | pw32* | cegcc*) # This hack is so that the source file can tell whether it is being # built for inclusion in a dll (and should export symbols for example). # Although the cygwin gcc ignores -fPIC, still need this for old-style # (--disable-auto-import) libraries m4_if([$1], [GCJ], [], [_LT_TAGVAR(lt_prog_compiler_pic, $1)='-DDLL_EXPORT']) case $host_os in os2*) _LT_TAGVAR(lt_prog_compiler_static, $1)='$wl-static' ;; esac ;; darwin* | rhapsody*) # PIC is the default on this platform # Common symbols not allowed in MH_DYLIB files _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fno-common' ;; *djgpp*) # DJGPP does not support shared libraries at all _LT_TAGVAR(lt_prog_compiler_pic, $1)= ;; haiku*) # PIC is the default for Haiku. # The "-static" flag exists, but is broken. _LT_TAGVAR(lt_prog_compiler_static, $1)= ;; interix[[3-9]]*) # Interix 3.x gcc -fpic/-fPIC options generate broken code. # Instead, we relocate shared libraries at runtime. ;; sysv4*MP*) if test -d /usr/nec; then _LT_TAGVAR(lt_prog_compiler_pic, $1)=-Kconform_pic fi ;; hpux*) # PIC is the default for 64-bit PA HP-UX, but not for 32-bit # PA HP-UX. On IA64 HP-UX, PIC is the default but the pic flag # sets the default TLS model and affects inlining. case $host_cpu in hppa*64*) ;; *) _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fPIC' ;; esac ;; *qnx* | *nto*) # QNX uses GNU C++, but need to define -shared option too, otherwise # it will coredump. _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fPIC -shared' ;; *) _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fPIC' ;; esac else case $host_os in aix[[4-9]]*) # All AIX code is PIC. if test ia64 = "$host_cpu"; then # AIX 5 now supports IA64 processor _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic' else _LT_TAGVAR(lt_prog_compiler_static, $1)='-bnso -bI:/lib/syscalls.exp' fi ;; chorus*) case $cc_basename in cxch68*) # Green Hills C++ Compiler # _LT_TAGVAR(lt_prog_compiler_static, $1)="--no_auto_instantiation -u __main -u __premain -u _abort -r $COOL_DIR/lib/libOrb.a $MVME_DIR/lib/CC/libC.a $MVME_DIR/lib/classix/libcx.s.a" ;; esac ;; mingw* | cygwin* | os2* | pw32* | cegcc*) # This hack is so that the source file can tell whether it is being # built for inclusion in a dll (and should export symbols for example). m4_if([$1], [GCJ], [], [_LT_TAGVAR(lt_prog_compiler_pic, $1)='-DDLL_EXPORT']) ;; dgux*) case $cc_basename in ec++*) _LT_TAGVAR(lt_prog_compiler_pic, $1)='-KPIC' ;; ghcx*) # Green Hills C++ Compiler _LT_TAGVAR(lt_prog_compiler_pic, $1)='-pic' ;; *) ;; esac ;; freebsd* | dragonfly* | midnightbsd*) # FreeBSD uses GNU C++ ;; hpux9* | hpux10* | hpux11*) case $cc_basename in CC*) _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' _LT_TAGVAR(lt_prog_compiler_static, $1)='$wl-a ${wl}archive' if test ia64 != "$host_cpu"; then _LT_TAGVAR(lt_prog_compiler_pic, $1)='+Z' fi ;; aCC*) _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' _LT_TAGVAR(lt_prog_compiler_static, $1)='$wl-a ${wl}archive' case $host_cpu in hppa*64*|ia64*) # +Z the default ;; *) _LT_TAGVAR(lt_prog_compiler_pic, $1)='+Z' ;; esac ;; *) ;; esac ;; interix*) # This is c89, which is MS Visual C++ (no shared libs) # Anyone wants to do a port? ;; irix5* | irix6* | nonstopux*) case $cc_basename in CC*) _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' _LT_TAGVAR(lt_prog_compiler_static, $1)='-non_shared' # CC pic flag -KPIC is the default. ;; *) ;; esac ;; linux* | k*bsd*-gnu | kopensolaris*-gnu | gnu*) case $cc_basename in KCC*) # KAI C++ Compiler _LT_TAGVAR(lt_prog_compiler_wl, $1)='--backend -Wl,' _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fPIC' ;; ecpc* ) # old Intel C++ for x86_64, which still supported -KPIC. _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' _LT_TAGVAR(lt_prog_compiler_pic, $1)='-KPIC' _LT_TAGVAR(lt_prog_compiler_static, $1)='-static' ;; icpc* ) # Intel C++, used to be incompatible with GCC. # ICC 10 doesn't accept -KPIC any more. _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fPIC' _LT_TAGVAR(lt_prog_compiler_static, $1)='-static' ;; pgCC* | pgcpp*) # Portland Group C++ compiler _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fpic' _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic' ;; cxx*) # Compaq C++ # Make sure the PIC flag is empty. It appears that all Alpha # Linux and Compaq Tru64 Unix objects are PIC. _LT_TAGVAR(lt_prog_compiler_pic, $1)= _LT_TAGVAR(lt_prog_compiler_static, $1)='-non_shared' ;; xlc* | xlC* | bgxl[[cC]]* | mpixl[[cC]]*) # IBM XL 8.0, 9.0 on PPC and BlueGene _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' _LT_TAGVAR(lt_prog_compiler_pic, $1)='-qpic' _LT_TAGVAR(lt_prog_compiler_static, $1)='-qstaticlink' ;; *) case `$CC -V 2>&1 | $SED 5q` in *Sun\ C*) # Sun C++ 5.9 _LT_TAGVAR(lt_prog_compiler_pic, $1)='-KPIC' _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic' _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Qoption ld ' ;; esac ;; esac ;; lynxos*) ;; m88k*) ;; mvs*) case $cc_basename in cxx*) _LT_TAGVAR(lt_prog_compiler_pic, $1)='-W c,exportall' ;; *) ;; esac ;; netbsd* | netbsdelf*-gnu) ;; *qnx* | *nto*) # QNX uses GNU C++, but need to define -shared option too, otherwise # it will coredump. _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fPIC -shared' ;; osf3* | osf4* | osf5*) case $cc_basename in KCC*) _LT_TAGVAR(lt_prog_compiler_wl, $1)='--backend -Wl,' ;; RCC*) # Rational C++ 2.4.1 _LT_TAGVAR(lt_prog_compiler_pic, $1)='-pic' ;; cxx*) # Digital/Compaq C++ _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' # Make sure the PIC flag is empty. It appears that all Alpha # Linux and Compaq Tru64 Unix objects are PIC. _LT_TAGVAR(lt_prog_compiler_pic, $1)= _LT_TAGVAR(lt_prog_compiler_static, $1)='-non_shared' ;; *) ;; esac ;; psos*) ;; solaris*) case $cc_basename in CC* | sunCC*) # Sun C++ 4.2, 5.x and Centerline C++ _LT_TAGVAR(lt_prog_compiler_pic, $1)='-KPIC' _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic' _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Qoption ld ' ;; gcx*) # Green Hills C++ Compiler _LT_TAGVAR(lt_prog_compiler_pic, $1)='-PIC' ;; *) ;; esac ;; sunos4*) case $cc_basename in CC*) # Sun C++ 4.x _LT_TAGVAR(lt_prog_compiler_pic, $1)='-pic' _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic' ;; lcc*) # Lucid _LT_TAGVAR(lt_prog_compiler_pic, $1)='-pic' ;; *) ;; esac ;; sysv5* | unixware* | sco3.2v5* | sco5v6* | OpenUNIX*) case $cc_basename in CC*) _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' _LT_TAGVAR(lt_prog_compiler_pic, $1)='-KPIC' _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic' ;; esac ;; tandem*) case $cc_basename in NCC*) # NonStop-UX NCC 3.20 _LT_TAGVAR(lt_prog_compiler_pic, $1)='-KPIC' ;; *) ;; esac ;; vxworks*) ;; *) _LT_TAGVAR(lt_prog_compiler_can_build_shared, $1)=no ;; esac fi ], [ if test yes = "$GCC"; then _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' _LT_TAGVAR(lt_prog_compiler_static, $1)='-static' case $host_os in aix*) # All AIX code is PIC. if test ia64 = "$host_cpu"; then # AIX 5 now supports IA64 processor _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic' fi _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fPIC' ;; amigaos*) case $host_cpu in powerpc) # see comment about AmigaOS4 .so support _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fPIC' ;; m68k) # FIXME: we need at least 68020 code to build shared libraries, but # adding the '-m68020' flag to GCC prevents building anything better, # like '-m68040'. _LT_TAGVAR(lt_prog_compiler_pic, $1)='-m68020 -resident32 -malways-restore-a4' ;; esac ;; beos* | irix5* | irix6* | nonstopux* | osf3* | osf4* | osf5*) # PIC is the default for these OSes. ;; mingw* | cygwin* | pw32* | os2* | cegcc*) # This hack is so that the source file can tell whether it is being # built for inclusion in a dll (and should export symbols for example). # Although the cygwin gcc ignores -fPIC, still need this for old-style # (--disable-auto-import) libraries m4_if([$1], [GCJ], [], [_LT_TAGVAR(lt_prog_compiler_pic, $1)='-DDLL_EXPORT']) case $host_os in os2*) _LT_TAGVAR(lt_prog_compiler_static, $1)='$wl-static' ;; esac ;; darwin* | rhapsody*) # PIC is the default on this platform # Common symbols not allowed in MH_DYLIB files _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fno-common' ;; haiku*) # PIC is the default for Haiku. # The "-static" flag exists, but is broken. _LT_TAGVAR(lt_prog_compiler_static, $1)= ;; hpux*) # PIC is the default for 64-bit PA HP-UX, but not for 32-bit # PA HP-UX. On IA64 HP-UX, PIC is the default but the pic flag # sets the default TLS model and affects inlining. case $host_cpu in hppa*64*) # +Z the default ;; *) _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fPIC' ;; esac ;; interix[[3-9]]*) # Interix 3.x gcc -fpic/-fPIC options generate broken code. # Instead, we relocate shared libraries at runtime. ;; msdosdjgpp*) # Just because we use GCC doesn't mean we suddenly get shared libraries # on systems that don't support them. _LT_TAGVAR(lt_prog_compiler_can_build_shared, $1)=no enable_shared=no ;; *nto* | *qnx*) # QNX uses GNU C++, but need to define -shared option too, otherwise # it will coredump. _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fPIC -shared' ;; sysv4*MP*) if test -d /usr/nec; then _LT_TAGVAR(lt_prog_compiler_pic, $1)=-Kconform_pic fi ;; *) _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fPIC' ;; esac case $cc_basename in nvcc*) # Cuda Compiler Driver 2.2 _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Xlinker ' if test -n "$_LT_TAGVAR(lt_prog_compiler_pic, $1)"; then _LT_TAGVAR(lt_prog_compiler_pic, $1)="-Xcompiler $_LT_TAGVAR(lt_prog_compiler_pic, $1)" fi ;; esac else # PORTME Check for flag to pass linker flags through the system compiler. case $host_os in aix*) _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' if test ia64 = "$host_cpu"; then # AIX 5 now supports IA64 processor _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic' else _LT_TAGVAR(lt_prog_compiler_static, $1)='-bnso -bI:/lib/syscalls.exp' fi ;; darwin* | rhapsody*) # PIC is the default on this platform # Common symbols not allowed in MH_DYLIB files _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fno-common' case $cc_basename in nagfor*) # NAG Fortran compiler _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,-Wl,,' _LT_TAGVAR(lt_prog_compiler_pic, $1)='-PIC' _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic' ;; esac ;; mingw* | cygwin* | pw32* | os2* | cegcc*) # This hack is so that the source file can tell whether it is being # built for inclusion in a dll (and should export symbols for example). m4_if([$1], [GCJ], [], [_LT_TAGVAR(lt_prog_compiler_pic, $1)='-DDLL_EXPORT']) case $host_os in os2*) _LT_TAGVAR(lt_prog_compiler_static, $1)='$wl-static' ;; esac ;; hpux9* | hpux10* | hpux11*) _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' # PIC is the default for IA64 HP-UX and 64-bit HP-UX, but # not for PA HP-UX. case $host_cpu in hppa*64*|ia64*) # +Z the default ;; *) _LT_TAGVAR(lt_prog_compiler_pic, $1)='+Z' ;; esac # Is there a better lt_prog_compiler_static that works with the bundled CC? _LT_TAGVAR(lt_prog_compiler_static, $1)='$wl-a ${wl}archive' ;; irix5* | irix6* | nonstopux*) _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' # PIC (with -KPIC) is the default. _LT_TAGVAR(lt_prog_compiler_static, $1)='-non_shared' ;; linux* | k*bsd*-gnu | kopensolaris*-gnu | gnu*) case $cc_basename in # old Intel for x86_64, which still supported -KPIC. ecc*) _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' _LT_TAGVAR(lt_prog_compiler_pic, $1)='-KPIC' _LT_TAGVAR(lt_prog_compiler_static, $1)='-static' ;; # flang / f18. f95 an alias for gfortran or flang on Debian flang* | f18* | f95*) _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fPIC' _LT_TAGVAR(lt_prog_compiler_static, $1)='-static' ;; # icc used to be incompatible with GCC. # ICC 10 doesn't accept -KPIC any more. icc* | ifort*) _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fPIC' _LT_TAGVAR(lt_prog_compiler_static, $1)='-static' ;; # Lahey Fortran 8.1. lf95*) _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' _LT_TAGVAR(lt_prog_compiler_pic, $1)='--shared' _LT_TAGVAR(lt_prog_compiler_static, $1)='--static' ;; nagfor*) # NAG Fortran compiler _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,-Wl,,' _LT_TAGVAR(lt_prog_compiler_pic, $1)='-PIC' _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic' ;; tcc*) # Fabrice Bellard et al's Tiny C Compiler _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fPIC' _LT_TAGVAR(lt_prog_compiler_static, $1)='-static' ;; pgcc* | pgf77* | pgf90* | pgf95* | pgfortran*) # Portland Group compilers (*not* the Pentium gcc compiler, # which looks to be a dead project) _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fpic' _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic' ;; ccc*) _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' # All Alpha code is PIC. _LT_TAGVAR(lt_prog_compiler_static, $1)='-non_shared' ;; xl* | bgxl* | bgf* | mpixl*) # IBM XL C 8.0/Fortran 10.1, 11.1 on PPC and BlueGene _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' _LT_TAGVAR(lt_prog_compiler_pic, $1)='-qpic' _LT_TAGVAR(lt_prog_compiler_static, $1)='-qstaticlink' ;; *) case `$CC -V 2>&1 | $SED 5q` in *Sun\ Ceres\ Fortran* | *Sun*Fortran*\ [[1-7]].* | *Sun*Fortran*\ 8.[[0-3]]*) # Sun Fortran 8.3 passes all unrecognized flags to the linker _LT_TAGVAR(lt_prog_compiler_pic, $1)='-KPIC' _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic' _LT_TAGVAR(lt_prog_compiler_wl, $1)='' ;; *Sun\ F* | *Sun*Fortran*) _LT_TAGVAR(lt_prog_compiler_pic, $1)='-KPIC' _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic' _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Qoption ld ' ;; *Sun\ C*) # Sun C 5.9 _LT_TAGVAR(lt_prog_compiler_pic, $1)='-KPIC' _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic' _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' ;; *Intel*\ [[CF]]*Compiler*) _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fPIC' _LT_TAGVAR(lt_prog_compiler_static, $1)='-static' ;; *Portland\ Group*) _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fpic' _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic' ;; esac ;; esac ;; newsos6) _LT_TAGVAR(lt_prog_compiler_pic, $1)='-KPIC' _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic' ;; *nto* | *qnx*) # QNX uses GNU C++, but need to define -shared option too, otherwise # it will coredump. _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fPIC -shared' ;; osf3* | osf4* | osf5*) _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' # All OSF/1 code is PIC. _LT_TAGVAR(lt_prog_compiler_static, $1)='-non_shared' ;; rdos*) _LT_TAGVAR(lt_prog_compiler_static, $1)='-non_shared' ;; solaris*) _LT_TAGVAR(lt_prog_compiler_pic, $1)='-KPIC' _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic' case $cc_basename in f77* | f90* | f95* | sunf77* | sunf90* | sunf95*) _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Qoption ld ';; *) _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,';; esac ;; sunos4*) _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Qoption ld ' _LT_TAGVAR(lt_prog_compiler_pic, $1)='-PIC' _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic' ;; sysv4 | sysv4.2uw2* | sysv4.3*) _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' _LT_TAGVAR(lt_prog_compiler_pic, $1)='-KPIC' _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic' ;; sysv4*MP*) if test -d /usr/nec; then _LT_TAGVAR(lt_prog_compiler_pic, $1)='-Kconform_pic' _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic' fi ;; sysv5* | unixware* | sco3.2v5* | sco5v6* | OpenUNIX*) _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' _LT_TAGVAR(lt_prog_compiler_pic, $1)='-KPIC' _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic' ;; unicos*) _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' _LT_TAGVAR(lt_prog_compiler_can_build_shared, $1)=no ;; uts4*) _LT_TAGVAR(lt_prog_compiler_pic, $1)='-pic' _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic' ;; *) _LT_TAGVAR(lt_prog_compiler_can_build_shared, $1)=no ;; esac fi ]) case $host_os in # For platforms that do not support PIC, -DPIC is meaningless: *djgpp*) _LT_TAGVAR(lt_prog_compiler_pic, $1)= ;; *) _LT_TAGVAR(lt_prog_compiler_pic, $1)="$_LT_TAGVAR(lt_prog_compiler_pic, $1)@&t@m4_if([$1],[],[ -DPIC],[m4_if([$1],[CXX],[ -DPIC],[])])" ;; esac AC_CACHE_CHECK([for $compiler option to produce PIC], [_LT_TAGVAR(lt_cv_prog_compiler_pic, $1)], [_LT_TAGVAR(lt_cv_prog_compiler_pic, $1)=$_LT_TAGVAR(lt_prog_compiler_pic, $1)]) _LT_TAGVAR(lt_prog_compiler_pic, $1)=$_LT_TAGVAR(lt_cv_prog_compiler_pic, $1) # # Check to make sure the PIC flag actually works. # if test -n "$_LT_TAGVAR(lt_prog_compiler_pic, $1)"; then _LT_COMPILER_OPTION([if $compiler PIC flag $_LT_TAGVAR(lt_prog_compiler_pic, $1) works], [_LT_TAGVAR(lt_cv_prog_compiler_pic_works, $1)], [$_LT_TAGVAR(lt_prog_compiler_pic, $1)@&t@m4_if([$1],[],[ -DPIC],[m4_if([$1],[CXX],[ -DPIC],[])])], [], [case $_LT_TAGVAR(lt_prog_compiler_pic, $1) in "" | " "*) ;; *) _LT_TAGVAR(lt_prog_compiler_pic, $1)=" $_LT_TAGVAR(lt_prog_compiler_pic, $1)" ;; esac], [_LT_TAGVAR(lt_prog_compiler_pic, $1)= _LT_TAGVAR(lt_prog_compiler_can_build_shared, $1)=no]) fi _LT_TAGDECL([pic_flag], [lt_prog_compiler_pic], [1], [Additional compiler flags for building library objects]) _LT_TAGDECL([wl], [lt_prog_compiler_wl], [1], [How to pass a linker flag through the compiler]) # # Check to make sure the static flag actually works. # wl=$_LT_TAGVAR(lt_prog_compiler_wl, $1) eval lt_tmp_static_flag=\"$_LT_TAGVAR(lt_prog_compiler_static, $1)\" _LT_LINKER_OPTION([if $compiler static flag $lt_tmp_static_flag works], _LT_TAGVAR(lt_cv_prog_compiler_static_works, $1), $lt_tmp_static_flag, [], [_LT_TAGVAR(lt_prog_compiler_static, $1)=]) _LT_TAGDECL([link_static_flag], [lt_prog_compiler_static], [1], [Compiler flag to prevent dynamic linking]) ])# _LT_COMPILER_PIC # _LT_LINKER_SHLIBS([TAGNAME]) # ---------------------------- # See if the linker supports building shared libraries. m4_defun([_LT_LINKER_SHLIBS], [AC_REQUIRE([LT_PATH_LD])dnl AC_REQUIRE([LT_PATH_NM])dnl m4_require([_LT_PATH_MANIFEST_TOOL])dnl m4_require([_LT_FILEUTILS_DEFAULTS])dnl m4_require([_LT_DECL_EGREP])dnl m4_require([_LT_DECL_SED])dnl m4_require([_LT_CMD_GLOBAL_SYMBOLS])dnl m4_require([_LT_TAG_COMPILER])dnl AC_MSG_CHECKING([whether the $compiler linker ($LD) supports shared libraries]) m4_if([$1], [CXX], [ _LT_TAGVAR(export_symbols_cmds, $1)='$NM $libobjs $convenience | $global_symbol_pipe | $SED '\''s/.* //'\'' | sort | uniq > $export_symbols' _LT_TAGVAR(exclude_expsyms, $1)=['_GLOBAL_OFFSET_TABLE_|_GLOBAL__F[ID]_.*'] case $host_os in aix[[4-9]]*) # If we're using GNU nm, then we don't want the "-C" option. # -C means demangle to GNU nm, but means don't demangle to AIX nm. # Without the "-l" option, or with the "-B" option, AIX nm treats # weak defined symbols like other global defined symbols, whereas # GNU nm marks them as "W". # While the 'weak' keyword is ignored in the Export File, we need # it in the Import File for the 'aix-soname' feature, so we have # to replace the "-B" option with "-P" for AIX nm. if $NM -V 2>&1 | $GREP 'GNU' > /dev/null; then _LT_TAGVAR(export_symbols_cmds, $1)='$NM -Bpg $libobjs $convenience | awk '\''{ if (((\$ 2 == "T") || (\$ 2 == "D") || (\$ 2 == "B") || (\$ 2 == "W")) && ([substr](\$ 3,1,1) != ".")) { if (\$ 2 == "W") { print \$ 3 " weak" } else { print \$ 3 } } }'\'' | sort -u > $export_symbols' else _LT_TAGVAR(export_symbols_cmds, $1)='`func_echo_all $NM | $SED -e '\''s/B\([[^B]]*\)$/P\1/'\''` -PCpgl $libobjs $convenience | awk '\''{ if (((\$ 2 == "T") || (\$ 2 == "D") || (\$ 2 == "B") || (\$ 2 == "L") || (\$ 2 == "W") || (\$ 2 == "V") || (\$ 2 == "Z")) && ([substr](\$ 1,1,1) != ".")) { if ((\$ 2 == "W") || (\$ 2 == "V") || (\$ 2 == "Z")) { print \$ 1 " weak" } else { print \$ 1 } } }'\'' | sort -u > $export_symbols' fi ;; pw32*) _LT_TAGVAR(export_symbols_cmds, $1)=$ltdll_cmds ;; cygwin* | mingw* | cegcc*) case $cc_basename in cl* | icl*) _LT_TAGVAR(exclude_expsyms, $1)='_NULL_IMPORT_DESCRIPTOR|_IMPORT_DESCRIPTOR_.*' ;; *) _LT_TAGVAR(export_symbols_cmds, $1)='$NM $libobjs $convenience | $global_symbol_pipe | $SED -e '\''/^[[BCDGRS]][[ ]]/s/.*[[ ]]\([[^ ]]*\)/\1 DATA/;s/^.*[[ ]]__nm__\([[^ ]]*\)[[ ]][[^ ]]*/\1 DATA/;/^I[[ ]]/d;/^[[AITW]][[ ]]/s/.* //'\'' | sort | uniq > $export_symbols' _LT_TAGVAR(exclude_expsyms, $1)=['[_]+GLOBAL_OFFSET_TABLE_|[_]+GLOBAL__[FID]_.*|[_]+head_[A-Za-z0-9_]+_dll|[A-Za-z0-9_]+_dll_iname'] ;; esac ;; linux* | k*bsd*-gnu | gnu*) _LT_TAGVAR(link_all_deplibs, $1)=no ;; *) _LT_TAGVAR(export_symbols_cmds, $1)='$NM $libobjs $convenience | $global_symbol_pipe | $SED '\''s/.* //'\'' | sort | uniq > $export_symbols' ;; esac ], [ runpath_var= _LT_TAGVAR(allow_undefined_flag, $1)= _LT_TAGVAR(always_export_symbols, $1)=no _LT_TAGVAR(archive_cmds, $1)= _LT_TAGVAR(archive_expsym_cmds, $1)= _LT_TAGVAR(compiler_needs_object, $1)=no _LT_TAGVAR(enable_shared_with_static_runtimes, $1)=no _LT_TAGVAR(export_dynamic_flag_spec, $1)= _LT_TAGVAR(export_symbols_cmds, $1)='$NM $libobjs $convenience | $global_symbol_pipe | $SED '\''s/.* //'\'' | sort | uniq > $export_symbols' _LT_TAGVAR(hardcode_automatic, $1)=no _LT_TAGVAR(hardcode_direct, $1)=no _LT_TAGVAR(hardcode_direct_absolute, $1)=no _LT_TAGVAR(hardcode_libdir_flag_spec, $1)= _LT_TAGVAR(hardcode_libdir_separator, $1)= _LT_TAGVAR(hardcode_minus_L, $1)=no _LT_TAGVAR(hardcode_shlibpath_var, $1)=unsupported _LT_TAGVAR(inherit_rpath, $1)=no _LT_TAGVAR(link_all_deplibs, $1)=unknown _LT_TAGVAR(module_cmds, $1)= _LT_TAGVAR(module_expsym_cmds, $1)= _LT_TAGVAR(old_archive_from_new_cmds, $1)= _LT_TAGVAR(old_archive_from_expsyms_cmds, $1)= _LT_TAGVAR(thread_safe_flag_spec, $1)= _LT_TAGVAR(whole_archive_flag_spec, $1)= # include_expsyms should be a list of space-separated symbols to be *always* # included in the symbol list _LT_TAGVAR(include_expsyms, $1)= # exclude_expsyms can be an extended regexp of symbols to exclude # it will be wrapped by ' (' and ')$', so one must not match beginning or # end of line. Example: 'a|bc|.*d.*' will exclude the symbols 'a' and 'bc', # as well as any symbol that contains 'd'. _LT_TAGVAR(exclude_expsyms, $1)=['_GLOBAL_OFFSET_TABLE_|_GLOBAL__F[ID]_.*'] # Although _GLOBAL_OFFSET_TABLE_ is a valid symbol C name, most a.out # platforms (ab)use it in PIC code, but their linkers get confused if # the symbol is explicitly referenced. Since portable code cannot # rely on this symbol name, it's probably fine to never include it in # preloaded symbol tables. # Exclude shared library initialization/finalization symbols. dnl Note also adjust exclude_expsyms for C++ above. extract_expsyms_cmds= case $host_os in cygwin* | mingw* | pw32* | cegcc*) # FIXME: the MSVC++ and ICC port hasn't been tested in a loooong time # When not using gcc, we currently assume that we are using # Microsoft Visual C++ or Intel C++ Compiler. if test yes != "$GCC"; then with_gnu_ld=no fi ;; interix*) # we just hope/assume this is gcc and not c89 (= MSVC++ or ICC) with_gnu_ld=yes ;; openbsd* | bitrig*) with_gnu_ld=no ;; linux* | k*bsd*-gnu | gnu*) _LT_TAGVAR(link_all_deplibs, $1)=no ;; esac _LT_TAGVAR(ld_shlibs, $1)=yes # On some targets, GNU ld is compatible enough with the native linker # that we're better off using the native interface for both. lt_use_gnu_ld_interface=no if test yes = "$with_gnu_ld"; then case $host_os in aix*) # The AIX port of GNU ld has always aspired to compatibility # with the native linker. However, as the warning in the GNU ld # block says, versions before 2.19.5* couldn't really create working # shared libraries, regardless of the interface used. case `$LD -v 2>&1` in *\ \(GNU\ Binutils\)\ 2.19.5*) ;; *\ \(GNU\ Binutils\)\ 2.[[2-9]]*) ;; *\ \(GNU\ Binutils\)\ [[3-9]]*) ;; *) lt_use_gnu_ld_interface=yes ;; esac ;; *) lt_use_gnu_ld_interface=yes ;; esac fi if test yes = "$lt_use_gnu_ld_interface"; then # If archive_cmds runs LD, not CC, wlarc should be empty wlarc='$wl' # Set some defaults for GNU ld with shared library support. These # are reset later if shared libraries are not supported. Putting them # here allows them to be overridden if necessary. runpath_var=LD_RUN_PATH _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='$wl-rpath $wl$libdir' _LT_TAGVAR(export_dynamic_flag_spec, $1)='$wl--export-dynamic' # ancient GNU ld didn't support --whole-archive et. al. if $LD --help 2>&1 | $GREP 'no-whole-archive' > /dev/null; then _LT_TAGVAR(whole_archive_flag_spec, $1)=$wlarc'--whole-archive$convenience '$wlarc'--no-whole-archive' else _LT_TAGVAR(whole_archive_flag_spec, $1)= fi supports_anon_versioning=no case `$LD -v | $SED -e 's/([[^)]]\+)\s\+//' 2>&1` in *GNU\ gold*) supports_anon_versioning=yes ;; *\ [[01]].* | *\ 2.[[0-9]].* | *\ 2.10.*) ;; # catch versions < 2.11 *\ 2.11.93.0.2\ *) supports_anon_versioning=yes ;; # RH7.3 ... *\ 2.11.92.0.12\ *) supports_anon_versioning=yes ;; # Mandrake 8.2 ... *\ 2.11.*) ;; # other 2.11 versions *) supports_anon_versioning=yes ;; esac # See if GNU ld supports shared libraries. case $host_os in aix[[3-9]]*) # On AIX/PPC, the GNU linker is very broken if test ia64 != "$host_cpu"; then _LT_TAGVAR(ld_shlibs, $1)=no cat <<_LT_EOF 1>&2 *** Warning: the GNU linker, at least up to release 2.19, is reported *** to be unable to reliably create shared libraries on AIX. *** Therefore, libtool is disabling shared libraries support. If you *** really care for shared libraries, you may want to install binutils *** 2.20 or above, or modify your PATH so that a non-GNU linker is found. *** You will then need to restart the configuration process. _LT_EOF fi ;; amigaos*) case $host_cpu in powerpc) # see comment about AmigaOS4 .so support _LT_TAGVAR(archive_cmds, $1)='$CC -shared $libobjs $deplibs $compiler_flags $wl-soname $wl$soname -o $lib' _LT_TAGVAR(archive_expsym_cmds, $1)='' ;; m68k) _LT_TAGVAR(archive_cmds, $1)='$RM $output_objdir/a2ixlibrary.data~$ECHO "#define NAME $libname" > $output_objdir/a2ixlibrary.data~$ECHO "#define LIBRARY_ID 1" >> $output_objdir/a2ixlibrary.data~$ECHO "#define VERSION $major" >> $output_objdir/a2ixlibrary.data~$ECHO "#define REVISION $revision" >> $output_objdir/a2ixlibrary.data~$AR $AR_FLAGS $lib $libobjs~$RANLIB $lib~(cd $output_objdir && a2ixlibrary -32)' _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-L$libdir' _LT_TAGVAR(hardcode_minus_L, $1)=yes ;; esac ;; beos*) if $LD --help 2>&1 | $GREP ': supported targets:.* elf' > /dev/null; then _LT_TAGVAR(allow_undefined_flag, $1)=unsupported # Joseph Beckenbach says some releases of gcc # support --undefined. This deserves some investigation. FIXME _LT_TAGVAR(archive_cmds, $1)='$CC -nostart $libobjs $deplibs $compiler_flags $wl-soname $wl$soname -o $lib' else _LT_TAGVAR(ld_shlibs, $1)=no fi ;; cygwin* | mingw* | pw32* | cegcc*) # _LT_TAGVAR(hardcode_libdir_flag_spec, $1) is actually meaningless, # as there is no search path for DLLs. _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-L$libdir' _LT_TAGVAR(export_dynamic_flag_spec, $1)='$wl--export-all-symbols' _LT_TAGVAR(allow_undefined_flag, $1)=unsupported _LT_TAGVAR(always_export_symbols, $1)=no _LT_TAGVAR(enable_shared_with_static_runtimes, $1)=yes _LT_TAGVAR(export_symbols_cmds, $1)='$NM $libobjs $convenience | $global_symbol_pipe | $SED -e '\''/^[[BCDGRS]][[ ]]/s/.*[[ ]]\([[^ ]]*\)/\1 DATA/;s/^.*[[ ]]__nm__\([[^ ]]*\)[[ ]][[^ ]]*/\1 DATA/;/^I[[ ]]/d;/^[[AITW]][[ ]]/s/.* //'\'' | sort | uniq > $export_symbols' _LT_TAGVAR(exclude_expsyms, $1)=['[_]+GLOBAL_OFFSET_TABLE_|[_]+GLOBAL__[FID]_.*|[_]+head_[A-Za-z0-9_]+_dll|[A-Za-z0-9_]+_dll_iname'] if $LD --help 2>&1 | $GREP 'auto-import' > /dev/null; then _LT_TAGVAR(archive_cmds, $1)='$CC -shared $libobjs $deplibs $compiler_flags -o $output_objdir/$soname $wl--enable-auto-image-base -Xlinker --out-implib -Xlinker $lib' # If the export-symbols file already is a .def file, use it as # is; otherwise, prepend EXPORTS... _LT_TAGVAR(archive_expsym_cmds, $1)='if _LT_DLL_DEF_P([$export_symbols]); then cp $export_symbols $output_objdir/$soname.def; else echo EXPORTS > $output_objdir/$soname.def; cat $export_symbols >> $output_objdir/$soname.def; fi~ $CC -shared $output_objdir/$soname.def $libobjs $deplibs $compiler_flags -o $output_objdir/$soname $wl--enable-auto-image-base -Xlinker --out-implib -Xlinker $lib' else _LT_TAGVAR(ld_shlibs, $1)=no fi ;; haiku*) _LT_TAGVAR(archive_cmds, $1)='$CC -shared $libobjs $deplibs $compiler_flags $wl-soname $wl$soname -o $lib' _LT_TAGVAR(link_all_deplibs, $1)=yes ;; os2*) _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-L$libdir' _LT_TAGVAR(hardcode_minus_L, $1)=yes _LT_TAGVAR(allow_undefined_flag, $1)=unsupported shrext_cmds=.dll _LT_TAGVAR(archive_cmds, $1)='$ECHO "LIBRARY ${soname%$shared_ext} INITINSTANCE TERMINSTANCE" > $output_objdir/$libname.def~ $ECHO "DESCRIPTION \"$libname\"" >> $output_objdir/$libname.def~ $ECHO "DATA MULTIPLE NONSHARED" >> $output_objdir/$libname.def~ $ECHO EXPORTS >> $output_objdir/$libname.def~ emxexp $libobjs | $SED /"_DLL_InitTerm"/d >> $output_objdir/$libname.def~ $CC -Zdll -Zcrtdll -o $output_objdir/$soname $libobjs $deplibs $compiler_flags $output_objdir/$libname.def~ emximp -o $lib $output_objdir/$libname.def' _LT_TAGVAR(archive_expsym_cmds, $1)='$ECHO "LIBRARY ${soname%$shared_ext} INITINSTANCE TERMINSTANCE" > $output_objdir/$libname.def~ $ECHO "DESCRIPTION \"$libname\"" >> $output_objdir/$libname.def~ $ECHO "DATA MULTIPLE NONSHARED" >> $output_objdir/$libname.def~ $ECHO EXPORTS >> $output_objdir/$libname.def~ prefix_cmds="$SED"~ if test EXPORTS = "`$SED 1q $export_symbols`"; then prefix_cmds="$prefix_cmds -e 1d"; fi~ prefix_cmds="$prefix_cmds -e \"s/^\(.*\)$/_\1/g\""~ cat $export_symbols | $prefix_cmds >> $output_objdir/$libname.def~ $CC -Zdll -Zcrtdll -o $output_objdir/$soname $libobjs $deplibs $compiler_flags $output_objdir/$libname.def~ emximp -o $lib $output_objdir/$libname.def' _LT_TAGVAR(old_archive_From_new_cmds, $1)='emximp -o $output_objdir/${libname}_dll.a $output_objdir/$libname.def' _LT_TAGVAR(enable_shared_with_static_runtimes, $1)=yes _LT_TAGVAR(file_list_spec, $1)='@' ;; interix[[3-9]]*) _LT_TAGVAR(hardcode_direct, $1)=no _LT_TAGVAR(hardcode_shlibpath_var, $1)=no _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='$wl-rpath,$libdir' _LT_TAGVAR(export_dynamic_flag_spec, $1)='$wl-E' # Hack: On Interix 3.x, we cannot compile PIC because of a broken gcc. # Instead, shared libraries are loaded at an image base (0x10000000 by # default) and relocated if they conflict, which is a slow very memory # consuming and fragmenting process. To avoid this, we pick a random, # 256 KiB-aligned image base between 0x50000000 and 0x6FFC0000 at link # time. Moving up from 0x10000000 also allows more sbrk(2) space. _LT_TAGVAR(archive_cmds, $1)='$CC -shared $pic_flag $libobjs $deplibs $compiler_flags $wl-h,$soname $wl--image-base,`expr ${RANDOM-$$} % 4096 / 2 \* 262144 + 1342177280` -o $lib' _LT_TAGVAR(archive_expsym_cmds, $1)='$SED "s|^|_|" $export_symbols >$output_objdir/$soname.expsym~$CC -shared $pic_flag $libobjs $deplibs $compiler_flags $wl-h,$soname $wl--retain-symbols-file,$output_objdir/$soname.expsym $wl--image-base,`expr ${RANDOM-$$} % 4096 / 2 \* 262144 + 1342177280` -o $lib' ;; gnu* | linux* | tpf* | k*bsd*-gnu | kopensolaris*-gnu) tmp_diet=no if test linux-dietlibc = "$host_os"; then case $cc_basename in diet\ *) tmp_diet=yes;; # linux-dietlibc with static linking (!diet-dyn) esac fi if $LD --help 2>&1 | $EGREP ': supported targets:.* elf' > /dev/null \ && test no = "$tmp_diet" then tmp_addflag=' $pic_flag' tmp_sharedflag='-shared' case $cc_basename,$host_cpu in pgcc*) # Portland Group C compiler _LT_TAGVAR(whole_archive_flag_spec, $1)='$wl--whole-archive`for conv in $convenience\"\"; do test -n \"$conv\" && new_convenience=\"$new_convenience,$conv\"; done; func_echo_all \"$new_convenience\"` $wl--no-whole-archive' tmp_addflag=' $pic_flag' ;; pgf77* | pgf90* | pgf95* | pgfortran*) # Portland Group f77 and f90 compilers _LT_TAGVAR(whole_archive_flag_spec, $1)='$wl--whole-archive`for conv in $convenience\"\"; do test -n \"$conv\" && new_convenience=\"$new_convenience,$conv\"; done; func_echo_all \"$new_convenience\"` $wl--no-whole-archive' tmp_addflag=' $pic_flag -Mnomain' ;; ecc*,ia64* | icc*,ia64*) # Intel C compiler on ia64 tmp_addflag=' -i_dynamic' ;; efc*,ia64* | ifort*,ia64*) # Intel Fortran compiler on ia64 tmp_addflag=' -i_dynamic -nofor_main' ;; ifc* | ifort*) # Intel Fortran compiler tmp_addflag=' -nofor_main' ;; lf95*) # Lahey Fortran 8.1 _LT_TAGVAR(whole_archive_flag_spec, $1)= tmp_sharedflag='--shared' ;; nagfor*) # NAGFOR 5.3 tmp_sharedflag='-Wl,-shared' ;; xl[[cC]]* | bgxl[[cC]]* | mpixl[[cC]]*) # IBM XL C 8.0 on PPC (deal with xlf below) tmp_sharedflag='-qmkshrobj' tmp_addflag= ;; nvcc*) # Cuda Compiler Driver 2.2 _LT_TAGVAR(whole_archive_flag_spec, $1)='$wl--whole-archive`for conv in $convenience\"\"; do test -n \"$conv\" && new_convenience=\"$new_convenience,$conv\"; done; func_echo_all \"$new_convenience\"` $wl--no-whole-archive' _LT_TAGVAR(compiler_needs_object, $1)=yes ;; esac case `$CC -V 2>&1 | $SED 5q` in *Sun\ C*) # Sun C 5.9 _LT_TAGVAR(whole_archive_flag_spec, $1)='$wl--whole-archive`new_convenience=; for conv in $convenience\"\"; do test -z \"$conv\" || new_convenience=\"$new_convenience,$conv\"; done; func_echo_all \"$new_convenience\"` $wl--no-whole-archive' _LT_TAGVAR(compiler_needs_object, $1)=yes tmp_sharedflag='-G' ;; *Sun\ F*) # Sun Fortran 8.3 tmp_sharedflag='-G' ;; esac _LT_TAGVAR(archive_cmds, $1)='$CC '"$tmp_sharedflag""$tmp_addflag"' $libobjs $deplibs $compiler_flags $wl-soname $wl$soname -o $lib' if test yes = "$supports_anon_versioning"; then _LT_TAGVAR(archive_expsym_cmds, $1)='echo "{ global:" > $output_objdir/$libname.ver~ cat $export_symbols | $SED -e "s/\(.*\)/\1;/" >> $output_objdir/$libname.ver~ echo "local: *; };" >> $output_objdir/$libname.ver~ $CC '"$tmp_sharedflag""$tmp_addflag"' $libobjs $deplibs $compiler_flags $wl-soname $wl$soname $wl-version-script $wl$output_objdir/$libname.ver -o $lib' fi case $cc_basename in tcc*) _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='$wl-rpath $wl$libdir' _LT_TAGVAR(export_dynamic_flag_spec, $1)='-rdynamic' ;; xlf* | bgf* | bgxlf* | mpixlf*) # IBM XL Fortran 10.1 on PPC cannot create shared libs itself _LT_TAGVAR(whole_archive_flag_spec, $1)='--whole-archive$convenience --no-whole-archive' _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='$wl-rpath $wl$libdir' _LT_TAGVAR(archive_cmds, $1)='$LD -shared $libobjs $deplibs $linker_flags -soname $soname -o $lib' if test yes = "$supports_anon_versioning"; then _LT_TAGVAR(archive_expsym_cmds, $1)='echo "{ global:" > $output_objdir/$libname.ver~ cat $export_symbols | $SED -e "s/\(.*\)/\1;/" >> $output_objdir/$libname.ver~ echo "local: *; };" >> $output_objdir/$libname.ver~ $LD -shared $libobjs $deplibs $linker_flags -soname $soname -version-script $output_objdir/$libname.ver -o $lib' fi ;; esac else _LT_TAGVAR(ld_shlibs, $1)=no fi ;; netbsd* | netbsdelf*-gnu) if echo __ELF__ | $CC -E - | $GREP __ELF__ >/dev/null; then _LT_TAGVAR(archive_cmds, $1)='$LD -Bshareable $libobjs $deplibs $linker_flags -o $lib' wlarc= else _LT_TAGVAR(archive_cmds, $1)='$CC -shared $pic_flag $libobjs $deplibs $compiler_flags $wl-soname $wl$soname -o $lib' _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -shared $pic_flag $libobjs $deplibs $compiler_flags $wl-soname $wl$soname $wl-retain-symbols-file $wl$export_symbols -o $lib' fi ;; solaris*) if $LD -v 2>&1 | $GREP 'BFD 2\.8' > /dev/null; then _LT_TAGVAR(ld_shlibs, $1)=no cat <<_LT_EOF 1>&2 *** Warning: The releases 2.8.* of the GNU linker cannot reliably *** create shared libraries on Solaris systems. Therefore, libtool *** is disabling shared libraries support. We urge you to upgrade GNU *** binutils to release 2.9.1 or newer. Another option is to modify *** your PATH or compiler configuration so that the native linker is *** used, and then restart. _LT_EOF elif $LD --help 2>&1 | $GREP ': supported targets:.* elf' > /dev/null; then _LT_TAGVAR(archive_cmds, $1)='$CC -shared $pic_flag $libobjs $deplibs $compiler_flags $wl-soname $wl$soname -o $lib' _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -shared $pic_flag $libobjs $deplibs $compiler_flags $wl-soname $wl$soname $wl-retain-symbols-file $wl$export_symbols -o $lib' else _LT_TAGVAR(ld_shlibs, $1)=no fi ;; sysv5* | sco3.2v5* | sco5v6* | unixware* | OpenUNIX*) case `$LD -v 2>&1` in *\ [[01]].* | *\ 2.[[0-9]].* | *\ 2.1[[0-5]].*) _LT_TAGVAR(ld_shlibs, $1)=no cat <<_LT_EOF 1>&2 *** Warning: Releases of the GNU linker prior to 2.16.91.0.3 cannot *** reliably create shared libraries on SCO systems. Therefore, libtool *** is disabling shared libraries support. We urge you to upgrade GNU *** binutils to release 2.16.91.0.3 or newer. Another option is to modify *** your PATH or compiler configuration so that the native linker is *** used, and then restart. _LT_EOF ;; *) # For security reasons, it is highly recommended that you always # use absolute paths for naming shared libraries, and exclude the # DT_RUNPATH tag from executables and libraries. But doing so # requires that you compile everything twice, which is a pain. if $LD --help 2>&1 | $GREP ': supported targets:.* elf' > /dev/null; then _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='$wl-rpath $wl$libdir' _LT_TAGVAR(archive_cmds, $1)='$CC -shared $libobjs $deplibs $compiler_flags $wl-soname $wl$soname -o $lib' _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -shared $libobjs $deplibs $compiler_flags $wl-soname $wl$soname $wl-retain-symbols-file $wl$export_symbols -o $lib' else _LT_TAGVAR(ld_shlibs, $1)=no fi ;; esac ;; sunos4*) _LT_TAGVAR(archive_cmds, $1)='$LD -assert pure-text -Bshareable -o $lib $libobjs $deplibs $linker_flags' wlarc= _LT_TAGVAR(hardcode_direct, $1)=yes _LT_TAGVAR(hardcode_shlibpath_var, $1)=no ;; *) if $LD --help 2>&1 | $GREP ': supported targets:.* elf' > /dev/null; then _LT_TAGVAR(archive_cmds, $1)='$CC -shared $pic_flag $libobjs $deplibs $compiler_flags $wl-soname $wl$soname -o $lib' _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -shared $pic_flag $libobjs $deplibs $compiler_flags $wl-soname $wl$soname $wl-retain-symbols-file $wl$export_symbols -o $lib' else _LT_TAGVAR(ld_shlibs, $1)=no fi ;; esac if test no = "$_LT_TAGVAR(ld_shlibs, $1)"; then runpath_var= _LT_TAGVAR(hardcode_libdir_flag_spec, $1)= _LT_TAGVAR(export_dynamic_flag_spec, $1)= _LT_TAGVAR(whole_archive_flag_spec, $1)= fi else # PORTME fill in a description of your system's linker (not GNU ld) case $host_os in aix3*) _LT_TAGVAR(allow_undefined_flag, $1)=unsupported _LT_TAGVAR(always_export_symbols, $1)=yes _LT_TAGVAR(archive_expsym_cmds, $1)='$LD -o $output_objdir/$soname $libobjs $deplibs $linker_flags -bE:$export_symbols -T512 -H512 -bM:SRE~$AR $AR_FLAGS $lib $output_objdir/$soname' # Note: this linker hardcodes the directories in LIBPATH if there # are no directories specified by -L. _LT_TAGVAR(hardcode_minus_L, $1)=yes if test yes = "$GCC" && test -z "$lt_prog_compiler_static"; then # Neither direct hardcoding nor static linking is supported with a # broken collect2. _LT_TAGVAR(hardcode_direct, $1)=unsupported fi ;; aix[[4-9]]*) if test ia64 = "$host_cpu"; then # On IA64, the linker does run time linking by default, so we don't # have to do anything special. aix_use_runtimelinking=no exp_sym_flag='-Bexport' no_entry_flag= else # If we're using GNU nm, then we don't want the "-C" option. # -C means demangle to GNU nm, but means don't demangle to AIX nm. # Without the "-l" option, or with the "-B" option, AIX nm treats # weak defined symbols like other global defined symbols, whereas # GNU nm marks them as "W". # While the 'weak' keyword is ignored in the Export File, we need # it in the Import File for the 'aix-soname' feature, so we have # to replace the "-B" option with "-P" for AIX nm. if $NM -V 2>&1 | $GREP 'GNU' > /dev/null; then _LT_TAGVAR(export_symbols_cmds, $1)='$NM -Bpg $libobjs $convenience | awk '\''{ if (((\$ 2 == "T") || (\$ 2 == "D") || (\$ 2 == "B") || (\$ 2 == "W")) && ([substr](\$ 3,1,1) != ".")) { if (\$ 2 == "W") { print \$ 3 " weak" } else { print \$ 3 } } }'\'' | sort -u > $export_symbols' else _LT_TAGVAR(export_symbols_cmds, $1)='`func_echo_all $NM | $SED -e '\''s/B\([[^B]]*\)$/P\1/'\''` -PCpgl $libobjs $convenience | awk '\''{ if (((\$ 2 == "T") || (\$ 2 == "D") || (\$ 2 == "B") || (\$ 2 == "L") || (\$ 2 == "W") || (\$ 2 == "V") || (\$ 2 == "Z")) && ([substr](\$ 1,1,1) != ".")) { if ((\$ 2 == "W") || (\$ 2 == "V") || (\$ 2 == "Z")) { print \$ 1 " weak" } else { print \$ 1 } } }'\'' | sort -u > $export_symbols' fi aix_use_runtimelinking=no # Test if we are trying to use run time linking or normal # AIX style linking. If -brtl is somewhere in LDFLAGS, we # have runtime linking enabled, and use it for executables. # For shared libraries, we enable/disable runtime linking # depending on the kind of the shared library created - # when "with_aix_soname,aix_use_runtimelinking" is: # "aix,no" lib.a(lib.so.V) shared, rtl:no, for executables # "aix,yes" lib.so shared, rtl:yes, for executables # lib.a static archive # "both,no" lib.so.V(shr.o) shared, rtl:yes # lib.a(lib.so.V) shared, rtl:no, for executables # "both,yes" lib.so.V(shr.o) shared, rtl:yes, for executables # lib.a(lib.so.V) shared, rtl:no # "svr4,*" lib.so.V(shr.o) shared, rtl:yes, for executables # lib.a static archive case $host_os in aix4.[[23]]|aix4.[[23]].*|aix[[5-9]]*) for ld_flag in $LDFLAGS; do if (test x-brtl = "x$ld_flag" || test x-Wl,-brtl = "x$ld_flag"); then aix_use_runtimelinking=yes break fi done if test svr4,no = "$with_aix_soname,$aix_use_runtimelinking"; then # With aix-soname=svr4, we create the lib.so.V shared archives only, # so we don't have lib.a shared libs to link our executables. # We have to force runtime linking in this case. aix_use_runtimelinking=yes LDFLAGS="$LDFLAGS -Wl,-brtl" fi ;; esac exp_sym_flag='-bexport' no_entry_flag='-bnoentry' fi # When large executables or shared objects are built, AIX ld can # have problems creating the table of contents. If linking a library # or program results in "error TOC overflow" add -mminimal-toc to # CXXFLAGS/CFLAGS for g++/gcc. In the cases where that is not # enough to fix the problem, add -Wl,-bbigtoc to LDFLAGS. _LT_TAGVAR(archive_cmds, $1)='' _LT_TAGVAR(hardcode_direct, $1)=yes _LT_TAGVAR(hardcode_direct_absolute, $1)=yes _LT_TAGVAR(hardcode_libdir_separator, $1)=':' _LT_TAGVAR(link_all_deplibs, $1)=yes _LT_TAGVAR(file_list_spec, $1)='$wl-f,' case $with_aix_soname,$aix_use_runtimelinking in aix,*) ;; # traditional, no import file svr4,* | *,yes) # use import file # The Import File defines what to hardcode. _LT_TAGVAR(hardcode_direct, $1)=no _LT_TAGVAR(hardcode_direct_absolute, $1)=no ;; esac if test yes = "$GCC"; then case $host_os in aix4.[[012]]|aix4.[[012]].*) # We only want to do this on AIX 4.2 and lower, the check # below for broken collect2 doesn't work under 4.3+ collect2name=`$CC -print-prog-name=collect2` if test -f "$collect2name" && strings "$collect2name" | $GREP resolve_lib_name >/dev/null then # We have reworked collect2 : else # We have old collect2 _LT_TAGVAR(hardcode_direct, $1)=unsupported # It fails to find uninstalled libraries when the uninstalled # path is not listed in the libpath. Setting hardcode_minus_L # to unsupported forces relinking _LT_TAGVAR(hardcode_minus_L, $1)=yes _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-L$libdir' _LT_TAGVAR(hardcode_libdir_separator, $1)= fi ;; esac shared_flag='-shared' if test yes = "$aix_use_runtimelinking"; then shared_flag="$shared_flag "'$wl-G' fi # Need to ensure runtime linking is disabled for the traditional # shared library, or the linker may eventually find shared libraries # /with/ Import File - we do not want to mix them. shared_flag_aix='-shared' shared_flag_svr4='-shared $wl-G' else # not using gcc if test ia64 = "$host_cpu"; then # VisualAge C++, Version 5.5 for AIX 5L for IA-64, Beta 3 Release # chokes on -Wl,-G. The following line is correct: shared_flag='-G' else if test yes = "$aix_use_runtimelinking"; then shared_flag='$wl-G' else shared_flag='$wl-bM:SRE' fi shared_flag_aix='$wl-bM:SRE' shared_flag_svr4='$wl-G' fi fi _LT_TAGVAR(export_dynamic_flag_spec, $1)='$wl-bexpall' # It seems that -bexpall does not export symbols beginning with # underscore (_), so it is better to generate a list of symbols to export. _LT_TAGVAR(always_export_symbols, $1)=yes if test aix,yes = "$with_aix_soname,$aix_use_runtimelinking"; then # Warning - without using the other runtime loading flags (-brtl), # -berok will link without error, but may produce a broken library. _LT_TAGVAR(allow_undefined_flag, $1)='-berok' # Determine the default libpath from the value encoded in an # empty executable. _LT_SYS_MODULE_PATH_AIX([$1]) _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='$wl-blibpath:$libdir:'"$aix_libpath" _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -o $output_objdir/$soname $libobjs $deplibs $wl'$no_entry_flag' $compiler_flags `if test -n "$allow_undefined_flag"; then func_echo_all "$wl$allow_undefined_flag"; else :; fi` $wl'$exp_sym_flag:\$export_symbols' '$shared_flag else if test ia64 = "$host_cpu"; then _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='$wl-R $libdir:/usr/lib:/lib' _LT_TAGVAR(allow_undefined_flag, $1)="-z nodefs" _LT_TAGVAR(archive_expsym_cmds, $1)="\$CC $shared_flag"' -o $output_objdir/$soname $libobjs $deplibs '"\$wl$no_entry_flag"' $compiler_flags $wl$allow_undefined_flag '"\$wl$exp_sym_flag:\$export_symbols" else # Determine the default libpath from the value encoded in an # empty executable. _LT_SYS_MODULE_PATH_AIX([$1]) _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='$wl-blibpath:$libdir:'"$aix_libpath" # Warning - without using the other run time loading flags, # -berok will link without error, but may produce a broken library. _LT_TAGVAR(no_undefined_flag, $1)=' $wl-bernotok' _LT_TAGVAR(allow_undefined_flag, $1)=' $wl-berok' if test yes = "$with_gnu_ld"; then # We only use this code for GNU lds that support --whole-archive. _LT_TAGVAR(whole_archive_flag_spec, $1)='$wl--whole-archive$convenience $wl--no-whole-archive' else # Exported symbols can be pulled into shared objects from archives _LT_TAGVAR(whole_archive_flag_spec, $1)='$convenience' fi _LT_TAGVAR(archive_cmds_need_lc, $1)=yes _LT_TAGVAR(archive_expsym_cmds, $1)='$RM -r $output_objdir/$realname.d~$MKDIR $output_objdir/$realname.d' # -brtl affects multiple linker settings, -berok does not and is overridden later compiler_flags_filtered='`func_echo_all "$compiler_flags " | $SED -e "s%-brtl\\([[, ]]\\)%-berok\\1%g"`' if test svr4 != "$with_aix_soname"; then # This is similar to how AIX traditionally builds its shared libraries. _LT_TAGVAR(archive_expsym_cmds, $1)="$_LT_TAGVAR(archive_expsym_cmds, $1)"'~$CC '$shared_flag_aix' -o $output_objdir/$realname.d/$soname $libobjs $deplibs $wl-bnoentry '$compiler_flags_filtered'$wl-bE:$export_symbols$allow_undefined_flag~$AR $AR_FLAGS $output_objdir/$libname$release.a $output_objdir/$realname.d/$soname' fi if test aix != "$with_aix_soname"; then _LT_TAGVAR(archive_expsym_cmds, $1)="$_LT_TAGVAR(archive_expsym_cmds, $1)"'~$CC '$shared_flag_svr4' -o $output_objdir/$realname.d/$shared_archive_member_spec.o $libobjs $deplibs $wl-bnoentry '$compiler_flags_filtered'$wl-bE:$export_symbols$allow_undefined_flag~$STRIP -e $output_objdir/$realname.d/$shared_archive_member_spec.o~( func_echo_all "#! $soname($shared_archive_member_spec.o)"; if test shr_64 = "$shared_archive_member_spec"; then func_echo_all "# 64"; else func_echo_all "# 32"; fi; cat $export_symbols ) > $output_objdir/$realname.d/$shared_archive_member_spec.imp~$AR $AR_FLAGS $output_objdir/$soname $output_objdir/$realname.d/$shared_archive_member_spec.o $output_objdir/$realname.d/$shared_archive_member_spec.imp' else # used by -dlpreopen to get the symbols _LT_TAGVAR(archive_expsym_cmds, $1)="$_LT_TAGVAR(archive_expsym_cmds, $1)"'~$MV $output_objdir/$realname.d/$soname $output_objdir' fi _LT_TAGVAR(archive_expsym_cmds, $1)="$_LT_TAGVAR(archive_expsym_cmds, $1)"'~$RM -r $output_objdir/$realname.d' fi fi ;; amigaos*) case $host_cpu in powerpc) # see comment about AmigaOS4 .so support _LT_TAGVAR(archive_cmds, $1)='$CC -shared $libobjs $deplibs $compiler_flags $wl-soname $wl$soname -o $lib' _LT_TAGVAR(archive_expsym_cmds, $1)='' ;; m68k) _LT_TAGVAR(archive_cmds, $1)='$RM $output_objdir/a2ixlibrary.data~$ECHO "#define NAME $libname" > $output_objdir/a2ixlibrary.data~$ECHO "#define LIBRARY_ID 1" >> $output_objdir/a2ixlibrary.data~$ECHO "#define VERSION $major" >> $output_objdir/a2ixlibrary.data~$ECHO "#define REVISION $revision" >> $output_objdir/a2ixlibrary.data~$AR $AR_FLAGS $lib $libobjs~$RANLIB $lib~(cd $output_objdir && a2ixlibrary -32)' _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-L$libdir' _LT_TAGVAR(hardcode_minus_L, $1)=yes ;; esac ;; bsdi[[45]]*) _LT_TAGVAR(export_dynamic_flag_spec, $1)=-rdynamic ;; cygwin* | mingw* | pw32* | cegcc*) # When not using gcc, we currently assume that we are using # Microsoft Visual C++ or Intel C++ Compiler. # hardcode_libdir_flag_spec is actually meaningless, as there is # no search path for DLLs. case $cc_basename in cl* | icl*) # Native MSVC or ICC _LT_TAGVAR(hardcode_libdir_flag_spec, $1)=' ' _LT_TAGVAR(allow_undefined_flag, $1)=unsupported _LT_TAGVAR(always_export_symbols, $1)=yes _LT_TAGVAR(file_list_spec, $1)='@' # Tell ltmain to make .lib files, not .a files. libext=lib # Tell ltmain to make .dll files, not .so files. shrext_cmds=.dll # FIXME: Setting linknames here is a bad hack. _LT_TAGVAR(archive_cmds, $1)='$CC -o $output_objdir/$soname $libobjs $compiler_flags $deplibs -Wl,-DLL,-IMPLIB:"$tool_output_objdir$libname.dll.lib"~linknames=' _LT_TAGVAR(archive_expsym_cmds, $1)='if _LT_DLL_DEF_P([$export_symbols]); then cp "$export_symbols" "$output_objdir/$soname.def"; echo "$tool_output_objdir$soname.def" > "$output_objdir/$soname.exp"; else $SED -e '\''s/^/-link -EXPORT:/'\'' < $export_symbols > $output_objdir/$soname.exp; fi~ $CC -o $tool_output_objdir$soname $libobjs $compiler_flags $deplibs "@$tool_output_objdir$soname.exp" -Wl,-DLL,-IMPLIB:"$tool_output_objdir$libname.dll.lib"~ linknames=' # The linker will not automatically build a static lib if we build a DLL. # _LT_TAGVAR(old_archive_from_new_cmds, $1)='true' _LT_TAGVAR(enable_shared_with_static_runtimes, $1)=yes _LT_TAGVAR(exclude_expsyms, $1)='_NULL_IMPORT_DESCRIPTOR|_IMPORT_DESCRIPTOR_.*' _LT_TAGVAR(export_symbols_cmds, $1)='$NM $libobjs $convenience | $global_symbol_pipe | $SED -e '\''/^[[BCDGRS]][[ ]]/s/.*[[ ]]\([[^ ]]*\)/\1,DATA/'\'' | $SED -e '\''/^[[AITW]][[ ]]/s/.*[[ ]]//'\'' | sort | uniq > $export_symbols' # Don't use ranlib _LT_TAGVAR(old_postinstall_cmds, $1)='chmod 644 $oldlib' _LT_TAGVAR(postlink_cmds, $1)='lt_outputfile="@OUTPUT@"~ lt_tool_outputfile="@TOOL_OUTPUT@"~ case $lt_outputfile in *.exe|*.EXE) ;; *) lt_outputfile=$lt_outputfile.exe lt_tool_outputfile=$lt_tool_outputfile.exe ;; esac~ if test : != "$MANIFEST_TOOL" && test -f "$lt_outputfile.manifest"; then $MANIFEST_TOOL -manifest "$lt_tool_outputfile.manifest" -outputresource:"$lt_tool_outputfile" || exit 1; $RM "$lt_outputfile.manifest"; fi' ;; *) # Assume MSVC and ICC wrapper _LT_TAGVAR(hardcode_libdir_flag_spec, $1)=' ' _LT_TAGVAR(allow_undefined_flag, $1)=unsupported # Tell ltmain to make .lib files, not .a files. libext=lib # Tell ltmain to make .dll files, not .so files. shrext_cmds=.dll # FIXME: Setting linknames here is a bad hack. _LT_TAGVAR(archive_cmds, $1)='$CC -o $lib $libobjs $compiler_flags `func_echo_all "$deplibs" | $SED '\''s/ -lc$//'\''` -link -dll~linknames=' # The linker will automatically build a .lib file if we build a DLL. _LT_TAGVAR(old_archive_from_new_cmds, $1)='true' # FIXME: Should let the user specify the lib program. _LT_TAGVAR(old_archive_cmds, $1)='lib -OUT:$oldlib$oldobjs$old_deplibs' _LT_TAGVAR(enable_shared_with_static_runtimes, $1)=yes ;; esac ;; darwin* | rhapsody*) _LT_DARWIN_LINKER_FEATURES($1) ;; dgux*) _LT_TAGVAR(archive_cmds, $1)='$LD -G -h $soname -o $lib $libobjs $deplibs $linker_flags' _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-L$libdir' _LT_TAGVAR(hardcode_shlibpath_var, $1)=no ;; # FreeBSD 2.2.[012] allows us to include c++rt0.o to get C++ constructor # support. Future versions do this automatically, but an explicit c++rt0.o # does not break anything, and helps significantly (at the cost of a little # extra space). freebsd2.2*) _LT_TAGVAR(archive_cmds, $1)='$LD -Bshareable -o $lib $libobjs $deplibs $linker_flags /usr/lib/c++rt0.o' _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-R$libdir' _LT_TAGVAR(hardcode_direct, $1)=yes _LT_TAGVAR(hardcode_shlibpath_var, $1)=no ;; # Unfortunately, older versions of FreeBSD 2 do not have this feature. freebsd2.*) _LT_TAGVAR(archive_cmds, $1)='$LD -Bshareable -o $lib $libobjs $deplibs $linker_flags' _LT_TAGVAR(hardcode_direct, $1)=yes _LT_TAGVAR(hardcode_minus_L, $1)=yes _LT_TAGVAR(hardcode_shlibpath_var, $1)=no ;; # FreeBSD 3 and greater uses gcc -shared to do shared libraries. freebsd* | dragonfly* | midnightbsd*) _LT_TAGVAR(archive_cmds, $1)='$CC -shared $pic_flag -o $lib $libobjs $deplibs $compiler_flags' _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-R$libdir' _LT_TAGVAR(hardcode_direct, $1)=yes _LT_TAGVAR(hardcode_shlibpath_var, $1)=no ;; hpux9*) if test yes = "$GCC"; then _LT_TAGVAR(archive_cmds, $1)='$RM $output_objdir/$soname~$CC -shared $pic_flag $wl+b $wl$install_libdir -o $output_objdir/$soname $libobjs $deplibs $compiler_flags~test "x$output_objdir/$soname" = "x$lib" || mv $output_objdir/$soname $lib' else _LT_TAGVAR(archive_cmds, $1)='$RM $output_objdir/$soname~$LD -b +b $install_libdir -o $output_objdir/$soname $libobjs $deplibs $linker_flags~test "x$output_objdir/$soname" = "x$lib" || mv $output_objdir/$soname $lib' fi _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='$wl+b $wl$libdir' _LT_TAGVAR(hardcode_libdir_separator, $1)=: _LT_TAGVAR(hardcode_direct, $1)=yes # hardcode_minus_L: Not really in the search PATH, # but as the default location of the library. _LT_TAGVAR(hardcode_minus_L, $1)=yes _LT_TAGVAR(export_dynamic_flag_spec, $1)='$wl-E' ;; hpux10*) if test yes,no = "$GCC,$with_gnu_ld"; then _LT_TAGVAR(archive_cmds, $1)='$CC -shared $pic_flag $wl+h $wl$soname $wl+b $wl$install_libdir -o $lib $libobjs $deplibs $compiler_flags' else _LT_TAGVAR(archive_cmds, $1)='$LD -b +h $soname +b $install_libdir -o $lib $libobjs $deplibs $linker_flags' fi if test no = "$with_gnu_ld"; then _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='$wl+b $wl$libdir' _LT_TAGVAR(hardcode_libdir_separator, $1)=: _LT_TAGVAR(hardcode_direct, $1)=yes _LT_TAGVAR(hardcode_direct_absolute, $1)=yes _LT_TAGVAR(export_dynamic_flag_spec, $1)='$wl-E' # hardcode_minus_L: Not really in the search PATH, # but as the default location of the library. _LT_TAGVAR(hardcode_minus_L, $1)=yes fi ;; hpux11*) if test yes,no = "$GCC,$with_gnu_ld"; then case $host_cpu in hppa*64*) _LT_TAGVAR(archive_cmds, $1)='$CC -shared $wl+h $wl$soname -o $lib $libobjs $deplibs $compiler_flags' ;; ia64*) _LT_TAGVAR(archive_cmds, $1)='$CC -shared $pic_flag $wl+h $wl$soname $wl+nodefaultrpath -o $lib $libobjs $deplibs $compiler_flags' ;; *) _LT_TAGVAR(archive_cmds, $1)='$CC -shared $pic_flag $wl+h $wl$soname $wl+b $wl$install_libdir -o $lib $libobjs $deplibs $compiler_flags' ;; esac else case $host_cpu in hppa*64*) _LT_TAGVAR(archive_cmds, $1)='$CC -b $wl+h $wl$soname -o $lib $libobjs $deplibs $compiler_flags' ;; ia64*) _LT_TAGVAR(archive_cmds, $1)='$CC -b $wl+h $wl$soname $wl+nodefaultrpath -o $lib $libobjs $deplibs $compiler_flags' ;; *) m4_if($1, [], [ # Older versions of the 11.00 compiler do not understand -b yet # (HP92453-01 A.11.01.20 doesn't, HP92453-01 B.11.X.35175-35176.GP does) _LT_LINKER_OPTION([if $CC understands -b], _LT_TAGVAR(lt_cv_prog_compiler__b, $1), [-b], [_LT_TAGVAR(archive_cmds, $1)='$CC -b $wl+h $wl$soname $wl+b $wl$install_libdir -o $lib $libobjs $deplibs $compiler_flags'], [_LT_TAGVAR(archive_cmds, $1)='$LD -b +h $soname +b $install_libdir -o $lib $libobjs $deplibs $linker_flags'])], [_LT_TAGVAR(archive_cmds, $1)='$CC -b $wl+h $wl$soname $wl+b $wl$install_libdir -o $lib $libobjs $deplibs $compiler_flags']) ;; esac fi if test no = "$with_gnu_ld"; then _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='$wl+b $wl$libdir' _LT_TAGVAR(hardcode_libdir_separator, $1)=: case $host_cpu in hppa*64*|ia64*) _LT_TAGVAR(hardcode_direct, $1)=no _LT_TAGVAR(hardcode_shlibpath_var, $1)=no ;; *) _LT_TAGVAR(hardcode_direct, $1)=yes _LT_TAGVAR(hardcode_direct_absolute, $1)=yes _LT_TAGVAR(export_dynamic_flag_spec, $1)='$wl-E' # hardcode_minus_L: Not really in the search PATH, # but as the default location of the library. _LT_TAGVAR(hardcode_minus_L, $1)=yes ;; esac fi ;; irix5* | irix6* | nonstopux*) if test yes = "$GCC"; then _LT_TAGVAR(archive_cmds, $1)='$CC -shared $pic_flag $libobjs $deplibs $compiler_flags $wl-soname $wl$soname `test -n "$verstring" && func_echo_all "$wl-set_version $wl$verstring"` $wl-update_registry $wl$output_objdir/so_locations -o $lib' # Try to use the -exported_symbol ld option, if it does not # work, assume that -exports_file does not work either and # implicitly export all symbols. # This should be the same for all languages, so no per-tag cache variable. AC_CACHE_CHECK([whether the $host_os linker accepts -exported_symbol], [lt_cv_irix_exported_symbol], [save_LDFLAGS=$LDFLAGS LDFLAGS="$LDFLAGS -shared $wl-exported_symbol ${wl}foo $wl-update_registry $wl/dev/null" AC_LINK_IFELSE( [AC_LANG_SOURCE( [AC_LANG_CASE([C], [[int foo (void) { return 0; }]], [C++], [[int foo (void) { return 0; }]], [Fortran 77], [[ subroutine foo end]], [Fortran], [[ subroutine foo end]])])], [lt_cv_irix_exported_symbol=yes], [lt_cv_irix_exported_symbol=no]) LDFLAGS=$save_LDFLAGS]) if test yes = "$lt_cv_irix_exported_symbol"; then _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -shared $pic_flag $libobjs $deplibs $compiler_flags $wl-soname $wl$soname `test -n "$verstring" && func_echo_all "$wl-set_version $wl$verstring"` $wl-update_registry $wl$output_objdir/so_locations $wl-exports_file $wl$export_symbols -o $lib' fi _LT_TAGVAR(link_all_deplibs, $1)=no else _LT_TAGVAR(archive_cmds, $1)='$CC -shared $libobjs $deplibs $compiler_flags -soname $soname `test -n "$verstring" && func_echo_all "-set_version $verstring"` -update_registry $output_objdir/so_locations -o $lib' _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -shared $libobjs $deplibs $compiler_flags -soname $soname `test -n "$verstring" && func_echo_all "-set_version $verstring"` -update_registry $output_objdir/so_locations -exports_file $export_symbols -o $lib' fi _LT_TAGVAR(archive_cmds_need_lc, $1)='no' _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='$wl-rpath $wl$libdir' _LT_TAGVAR(hardcode_libdir_separator, $1)=: _LT_TAGVAR(inherit_rpath, $1)=yes _LT_TAGVAR(link_all_deplibs, $1)=yes ;; linux*) case $cc_basename in tcc*) # Fabrice Bellard et al's Tiny C Compiler _LT_TAGVAR(ld_shlibs, $1)=yes _LT_TAGVAR(archive_cmds, $1)='$CC -shared $pic_flag -o $lib $libobjs $deplibs $compiler_flags' _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='$wl-rpath $wl$libdir' ;; esac ;; netbsd* | netbsdelf*-gnu) if echo __ELF__ | $CC -E - | $GREP __ELF__ >/dev/null; then _LT_TAGVAR(archive_cmds, $1)='$LD -Bshareable -o $lib $libobjs $deplibs $linker_flags' # a.out else _LT_TAGVAR(archive_cmds, $1)='$LD -shared -o $lib $libobjs $deplibs $linker_flags' # ELF fi _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-R$libdir' _LT_TAGVAR(hardcode_direct, $1)=yes _LT_TAGVAR(hardcode_shlibpath_var, $1)=no ;; newsos6) _LT_TAGVAR(archive_cmds, $1)='$LD -G -h $soname -o $lib $libobjs $deplibs $linker_flags' _LT_TAGVAR(hardcode_direct, $1)=yes _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='$wl-rpath $wl$libdir' _LT_TAGVAR(hardcode_libdir_separator, $1)=: _LT_TAGVAR(hardcode_shlibpath_var, $1)=no ;; *nto* | *qnx*) ;; openbsd* | bitrig*) if test -f /usr/libexec/ld.so; then _LT_TAGVAR(hardcode_direct, $1)=yes _LT_TAGVAR(hardcode_shlibpath_var, $1)=no _LT_TAGVAR(hardcode_direct_absolute, $1)=yes if test -z "`echo __ELF__ | $CC -E - | $GREP __ELF__`"; then _LT_TAGVAR(archive_cmds, $1)='$CC -shared $pic_flag -o $lib $libobjs $deplibs $compiler_flags' _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -shared $pic_flag -o $lib $libobjs $deplibs $compiler_flags $wl-retain-symbols-file,$export_symbols' _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='$wl-rpath,$libdir' _LT_TAGVAR(export_dynamic_flag_spec, $1)='$wl-E' else _LT_TAGVAR(archive_cmds, $1)='$CC -shared $pic_flag -o $lib $libobjs $deplibs $compiler_flags' _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='$wl-rpath,$libdir' fi else _LT_TAGVAR(ld_shlibs, $1)=no fi ;; os2*) _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-L$libdir' _LT_TAGVAR(hardcode_minus_L, $1)=yes _LT_TAGVAR(allow_undefined_flag, $1)=unsupported shrext_cmds=.dll _LT_TAGVAR(archive_cmds, $1)='$ECHO "LIBRARY ${soname%$shared_ext} INITINSTANCE TERMINSTANCE" > $output_objdir/$libname.def~ $ECHO "DESCRIPTION \"$libname\"" >> $output_objdir/$libname.def~ $ECHO "DATA MULTIPLE NONSHARED" >> $output_objdir/$libname.def~ $ECHO EXPORTS >> $output_objdir/$libname.def~ emxexp $libobjs | $SED /"_DLL_InitTerm"/d >> $output_objdir/$libname.def~ $CC -Zdll -Zcrtdll -o $output_objdir/$soname $libobjs $deplibs $compiler_flags $output_objdir/$libname.def~ emximp -o $lib $output_objdir/$libname.def' _LT_TAGVAR(archive_expsym_cmds, $1)='$ECHO "LIBRARY ${soname%$shared_ext} INITINSTANCE TERMINSTANCE" > $output_objdir/$libname.def~ $ECHO "DESCRIPTION \"$libname\"" >> $output_objdir/$libname.def~ $ECHO "DATA MULTIPLE NONSHARED" >> $output_objdir/$libname.def~ $ECHO EXPORTS >> $output_objdir/$libname.def~ prefix_cmds="$SED"~ if test EXPORTS = "`$SED 1q $export_symbols`"; then prefix_cmds="$prefix_cmds -e 1d"; fi~ prefix_cmds="$prefix_cmds -e \"s/^\(.*\)$/_\1/g\""~ cat $export_symbols | $prefix_cmds >> $output_objdir/$libname.def~ $CC -Zdll -Zcrtdll -o $output_objdir/$soname $libobjs $deplibs $compiler_flags $output_objdir/$libname.def~ emximp -o $lib $output_objdir/$libname.def' _LT_TAGVAR(old_archive_From_new_cmds, $1)='emximp -o $output_objdir/${libname}_dll.a $output_objdir/$libname.def' _LT_TAGVAR(enable_shared_with_static_runtimes, $1)=yes _LT_TAGVAR(file_list_spec, $1)='@' ;; osf3*) if test yes = "$GCC"; then _LT_TAGVAR(allow_undefined_flag, $1)=' $wl-expect_unresolved $wl\*' _LT_TAGVAR(archive_cmds, $1)='$CC -shared$allow_undefined_flag $libobjs $deplibs $compiler_flags $wl-soname $wl$soname `test -n "$verstring" && func_echo_all "$wl-set_version $wl$verstring"` $wl-update_registry $wl$output_objdir/so_locations -o $lib' else _LT_TAGVAR(allow_undefined_flag, $1)=' -expect_unresolved \*' _LT_TAGVAR(archive_cmds, $1)='$CC -shared$allow_undefined_flag $libobjs $deplibs $compiler_flags -soname $soname `test -n "$verstring" && func_echo_all "-set_version $verstring"` -update_registry $output_objdir/so_locations -o $lib' fi _LT_TAGVAR(archive_cmds_need_lc, $1)='no' _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='$wl-rpath $wl$libdir' _LT_TAGVAR(hardcode_libdir_separator, $1)=: ;; osf4* | osf5*) # as osf3* with the addition of -msym flag if test yes = "$GCC"; then _LT_TAGVAR(allow_undefined_flag, $1)=' $wl-expect_unresolved $wl\*' _LT_TAGVAR(archive_cmds, $1)='$CC -shared$allow_undefined_flag $pic_flag $libobjs $deplibs $compiler_flags $wl-msym $wl-soname $wl$soname `test -n "$verstring" && func_echo_all "$wl-set_version $wl$verstring"` $wl-update_registry $wl$output_objdir/so_locations -o $lib' _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='$wl-rpath $wl$libdir' else _LT_TAGVAR(allow_undefined_flag, $1)=' -expect_unresolved \*' _LT_TAGVAR(archive_cmds, $1)='$CC -shared$allow_undefined_flag $libobjs $deplibs $compiler_flags -msym -soname $soname `test -n "$verstring" && func_echo_all "-set_version $verstring"` -update_registry $output_objdir/so_locations -o $lib' _LT_TAGVAR(archive_expsym_cmds, $1)='for i in `cat $export_symbols`; do printf "%s %s\\n" -exported_symbol "\$i" >> $lib.exp; done; printf "%s\\n" "-hidden">> $lib.exp~ $CC -shared$allow_undefined_flag $wl-input $wl$lib.exp $compiler_flags $libobjs $deplibs -soname $soname `test -n "$verstring" && $ECHO "-set_version $verstring"` -update_registry $output_objdir/so_locations -o $lib~$RM $lib.exp' # Both c and cxx compiler support -rpath directly _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-rpath $libdir' fi _LT_TAGVAR(archive_cmds_need_lc, $1)='no' _LT_TAGVAR(hardcode_libdir_separator, $1)=: ;; solaris*) _LT_TAGVAR(no_undefined_flag, $1)=' -z defs' if test yes = "$GCC"; then wlarc='$wl' _LT_TAGVAR(archive_cmds, $1)='$CC -shared $pic_flag $wl-z ${wl}text $wl-h $wl$soname -o $lib $libobjs $deplibs $compiler_flags' _LT_TAGVAR(archive_expsym_cmds, $1)='echo "{ global:" > $lib.exp~cat $export_symbols | $SED -e "s/\(.*\)/\1;/" >> $lib.exp~echo "local: *; };" >> $lib.exp~ $CC -shared $pic_flag $wl-z ${wl}text $wl-M $wl$lib.exp $wl-h $wl$soname -o $lib $libobjs $deplibs $compiler_flags~$RM $lib.exp' else case `$CC -V 2>&1` in *"Compilers 5.0"*) wlarc='' _LT_TAGVAR(archive_cmds, $1)='$LD -G$allow_undefined_flag -h $soname -o $lib $libobjs $deplibs $linker_flags' _LT_TAGVAR(archive_expsym_cmds, $1)='echo "{ global:" > $lib.exp~cat $export_symbols | $SED -e "s/\(.*\)/\1;/" >> $lib.exp~echo "local: *; };" >> $lib.exp~ $LD -G$allow_undefined_flag -M $lib.exp -h $soname -o $lib $libobjs $deplibs $linker_flags~$RM $lib.exp' ;; *) wlarc='$wl' _LT_TAGVAR(archive_cmds, $1)='$CC -G$allow_undefined_flag -h $soname -o $lib $libobjs $deplibs $compiler_flags' _LT_TAGVAR(archive_expsym_cmds, $1)='echo "{ global:" > $lib.exp~cat $export_symbols | $SED -e "s/\(.*\)/\1;/" >> $lib.exp~echo "local: *; };" >> $lib.exp~ $CC -G$allow_undefined_flag -M $lib.exp -h $soname -o $lib $libobjs $deplibs $compiler_flags~$RM $lib.exp' ;; esac fi _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-R$libdir' _LT_TAGVAR(hardcode_shlibpath_var, $1)=no case $host_os in solaris2.[[0-5]] | solaris2.[[0-5]].*) ;; *) # The compiler driver will combine and reorder linker options, # but understands '-z linker_flag'. GCC discards it without '$wl', # but is careful enough not to reorder. # Supported since Solaris 2.6 (maybe 2.5.1?) if test yes = "$GCC"; then _LT_TAGVAR(whole_archive_flag_spec, $1)='$wl-z ${wl}allextract$convenience $wl-z ${wl}defaultextract' else _LT_TAGVAR(whole_archive_flag_spec, $1)='-z allextract$convenience -z defaultextract' fi ;; esac _LT_TAGVAR(link_all_deplibs, $1)=yes ;; sunos4*) if test sequent = "$host_vendor"; then # Use $CC to link under sequent, because it throws in some extra .o # files that make .init and .fini sections work. _LT_TAGVAR(archive_cmds, $1)='$CC -G $wl-h $soname -o $lib $libobjs $deplibs $compiler_flags' else _LT_TAGVAR(archive_cmds, $1)='$LD -assert pure-text -Bstatic -o $lib $libobjs $deplibs $linker_flags' fi _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-L$libdir' _LT_TAGVAR(hardcode_direct, $1)=yes _LT_TAGVAR(hardcode_minus_L, $1)=yes _LT_TAGVAR(hardcode_shlibpath_var, $1)=no ;; sysv4) case $host_vendor in sni) _LT_TAGVAR(archive_cmds, $1)='$LD -G -h $soname -o $lib $libobjs $deplibs $linker_flags' _LT_TAGVAR(hardcode_direct, $1)=yes # is this really true??? ;; siemens) ## LD is ld it makes a PLAMLIB ## CC just makes a GrossModule. _LT_TAGVAR(archive_cmds, $1)='$LD -G -o $lib $libobjs $deplibs $linker_flags' _LT_TAGVAR(reload_cmds, $1)='$CC -r -o $output$reload_objs' _LT_TAGVAR(hardcode_direct, $1)=no ;; motorola) _LT_TAGVAR(archive_cmds, $1)='$LD -G -h $soname -o $lib $libobjs $deplibs $linker_flags' _LT_TAGVAR(hardcode_direct, $1)=no #Motorola manual says yes, but my tests say they lie ;; esac runpath_var='LD_RUN_PATH' _LT_TAGVAR(hardcode_shlibpath_var, $1)=no ;; sysv4.3*) _LT_TAGVAR(archive_cmds, $1)='$LD -G -h $soname -o $lib $libobjs $deplibs $linker_flags' _LT_TAGVAR(hardcode_shlibpath_var, $1)=no _LT_TAGVAR(export_dynamic_flag_spec, $1)='-Bexport' ;; sysv4*MP*) if test -d /usr/nec; then _LT_TAGVAR(archive_cmds, $1)='$LD -G -h $soname -o $lib $libobjs $deplibs $linker_flags' _LT_TAGVAR(hardcode_shlibpath_var, $1)=no runpath_var=LD_RUN_PATH hardcode_runpath_var=yes _LT_TAGVAR(ld_shlibs, $1)=yes fi ;; sysv4*uw2* | sysv5OpenUNIX* | sysv5UnixWare7.[[01]].[[10]]* | unixware7* | sco3.2v5.0.[[024]]*) _LT_TAGVAR(no_undefined_flag, $1)='$wl-z,text' _LT_TAGVAR(archive_cmds_need_lc, $1)=no _LT_TAGVAR(hardcode_shlibpath_var, $1)=no runpath_var='LD_RUN_PATH' if test yes = "$GCC"; then _LT_TAGVAR(archive_cmds, $1)='$CC -shared $wl-h,$soname -o $lib $libobjs $deplibs $compiler_flags' _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -shared $wl-Bexport:$export_symbols $wl-h,$soname -o $lib $libobjs $deplibs $compiler_flags' else _LT_TAGVAR(archive_cmds, $1)='$CC -G $wl-h,$soname -o $lib $libobjs $deplibs $compiler_flags' _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -G $wl-Bexport:$export_symbols $wl-h,$soname -o $lib $libobjs $deplibs $compiler_flags' fi ;; sysv5* | sco3.2v5* | sco5v6*) # Note: We CANNOT use -z defs as we might desire, because we do not # link with -lc, and that would cause any symbols used from libc to # always be unresolved, which means just about no library would # ever link correctly. If we're not using GNU ld we use -z text # though, which does catch some bad symbols but isn't as heavy-handed # as -z defs. _LT_TAGVAR(no_undefined_flag, $1)='$wl-z,text' _LT_TAGVAR(allow_undefined_flag, $1)='$wl-z,nodefs' _LT_TAGVAR(archive_cmds_need_lc, $1)=no _LT_TAGVAR(hardcode_shlibpath_var, $1)=no _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='$wl-R,$libdir' _LT_TAGVAR(hardcode_libdir_separator, $1)=':' _LT_TAGVAR(link_all_deplibs, $1)=yes _LT_TAGVAR(export_dynamic_flag_spec, $1)='$wl-Bexport' runpath_var='LD_RUN_PATH' if test yes = "$GCC"; then _LT_TAGVAR(archive_cmds, $1)='$CC -shared $wl-h,$soname -o $lib $libobjs $deplibs $compiler_flags' _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -shared $wl-Bexport:$export_symbols $wl-h,$soname -o $lib $libobjs $deplibs $compiler_flags' else _LT_TAGVAR(archive_cmds, $1)='$CC -G $wl-h,$soname -o $lib $libobjs $deplibs $compiler_flags' _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -G $wl-Bexport:$export_symbols $wl-h,$soname -o $lib $libobjs $deplibs $compiler_flags' fi ;; uts4*) _LT_TAGVAR(archive_cmds, $1)='$LD -G -h $soname -o $lib $libobjs $deplibs $linker_flags' _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-L$libdir' _LT_TAGVAR(hardcode_shlibpath_var, $1)=no ;; *) _LT_TAGVAR(ld_shlibs, $1)=no ;; esac if test sni = "$host_vendor"; then case $host in sysv4 | sysv4.2uw2* | sysv4.3* | sysv5*) _LT_TAGVAR(export_dynamic_flag_spec, $1)='$wl-Blargedynsym' ;; esac fi fi ]) AC_MSG_RESULT([$_LT_TAGVAR(ld_shlibs, $1)]) test no = "$_LT_TAGVAR(ld_shlibs, $1)" && can_build_shared=no _LT_TAGVAR(with_gnu_ld, $1)=$with_gnu_ld _LT_DECL([], [libext], [0], [Old archive suffix (normally "a")])dnl _LT_DECL([], [shrext_cmds], [1], [Shared library suffix (normally ".so")])dnl _LT_DECL([], [extract_expsyms_cmds], [2], [The commands to extract the exported symbol list from a shared archive]) # # Do we need to explicitly link libc? # case "x$_LT_TAGVAR(archive_cmds_need_lc, $1)" in x|xyes) # Assume -lc should be added _LT_TAGVAR(archive_cmds_need_lc, $1)=yes if test yes,yes = "$GCC,$enable_shared"; then case $_LT_TAGVAR(archive_cmds, $1) in *'~'*) # FIXME: we may have to deal with multi-command sequences. ;; '$CC '*) # Test whether the compiler implicitly links with -lc since on some # systems, -lgcc has to come before -lc. If gcc already passes -lc # to ld, don't add -lc before -lgcc. AC_CACHE_CHECK([whether -lc should be explicitly linked in], [lt_cv_]_LT_TAGVAR(archive_cmds_need_lc, $1), [$RM conftest* echo "$lt_simple_compile_test_code" > conftest.$ac_ext if AC_TRY_EVAL(ac_compile) 2>conftest.err; then soname=conftest lib=conftest libobjs=conftest.$ac_objext deplibs= wl=$_LT_TAGVAR(lt_prog_compiler_wl, $1) pic_flag=$_LT_TAGVAR(lt_prog_compiler_pic, $1) compiler_flags=-v linker_flags=-v verstring= output_objdir=. libname=conftest lt_save_allow_undefined_flag=$_LT_TAGVAR(allow_undefined_flag, $1) _LT_TAGVAR(allow_undefined_flag, $1)= if AC_TRY_EVAL(_LT_TAGVAR(archive_cmds, $1) 2\>\&1 \| $GREP \" -lc \" \>/dev/null 2\>\&1) then lt_cv_[]_LT_TAGVAR(archive_cmds_need_lc, $1)=no else lt_cv_[]_LT_TAGVAR(archive_cmds_need_lc, $1)=yes fi _LT_TAGVAR(allow_undefined_flag, $1)=$lt_save_allow_undefined_flag else cat conftest.err 1>&5 fi $RM conftest* ]) _LT_TAGVAR(archive_cmds_need_lc, $1)=$lt_cv_[]_LT_TAGVAR(archive_cmds_need_lc, $1) ;; esac fi ;; esac _LT_TAGDECL([build_libtool_need_lc], [archive_cmds_need_lc], [0], [Whether or not to add -lc for building shared libraries]) _LT_TAGDECL([allow_libtool_libs_with_static_runtimes], [enable_shared_with_static_runtimes], [0], [Whether or not to disallow shared libs when runtime libs are static]) _LT_TAGDECL([], [export_dynamic_flag_spec], [1], [Compiler flag to allow reflexive dlopens]) _LT_TAGDECL([], [whole_archive_flag_spec], [1], [Compiler flag to generate shared objects directly from archives]) _LT_TAGDECL([], [compiler_needs_object], [1], [Whether the compiler copes with passing no objects directly]) _LT_TAGDECL([], [old_archive_from_new_cmds], [2], [Create an old-style archive from a shared archive]) _LT_TAGDECL([], [old_archive_from_expsyms_cmds], [2], [Create a temporary old-style archive to link instead of a shared archive]) _LT_TAGDECL([], [archive_cmds], [2], [Commands used to build a shared archive]) _LT_TAGDECL([], [archive_expsym_cmds], [2]) _LT_TAGDECL([], [module_cmds], [2], [Commands used to build a loadable module if different from building a shared archive.]) _LT_TAGDECL([], [module_expsym_cmds], [2]) _LT_TAGDECL([], [with_gnu_ld], [1], [Whether we are building with GNU ld or not]) _LT_TAGDECL([], [allow_undefined_flag], [1], [Flag that allows shared libraries with undefined symbols to be built]) _LT_TAGDECL([], [no_undefined_flag], [1], [Flag that enforces no undefined symbols]) _LT_TAGDECL([], [hardcode_libdir_flag_spec], [1], [Flag to hardcode $libdir into a binary during linking. This must work even if $libdir does not exist]) _LT_TAGDECL([], [hardcode_libdir_separator], [1], [Whether we need a single "-rpath" flag with a separated argument]) _LT_TAGDECL([], [hardcode_direct], [0], [Set to "yes" if using DIR/libNAME$shared_ext during linking hardcodes DIR into the resulting binary]) _LT_TAGDECL([], [hardcode_direct_absolute], [0], [Set to "yes" if using DIR/libNAME$shared_ext during linking hardcodes DIR into the resulting binary and the resulting library dependency is "absolute", i.e impossible to change by setting $shlibpath_var if the library is relocated]) _LT_TAGDECL([], [hardcode_minus_L], [0], [Set to "yes" if using the -LDIR flag during linking hardcodes DIR into the resulting binary]) _LT_TAGDECL([], [hardcode_shlibpath_var], [0], [Set to "yes" if using SHLIBPATH_VAR=DIR during linking hardcodes DIR into the resulting binary]) _LT_TAGDECL([], [hardcode_automatic], [0], [Set to "yes" if building a shared library automatically hardcodes DIR into the library and all subsequent libraries and executables linked against it]) _LT_TAGDECL([], [inherit_rpath], [0], [Set to yes if linker adds runtime paths of dependent libraries to runtime path list]) _LT_TAGDECL([], [link_all_deplibs], [0], [Whether libtool must link a program against all its dependency libraries]) _LT_TAGDECL([], [always_export_symbols], [0], [Set to "yes" if exported symbols are required]) _LT_TAGDECL([], [export_symbols_cmds], [2], [The commands to list exported symbols]) _LT_TAGDECL([], [exclude_expsyms], [1], [Symbols that should not be listed in the preloaded symbols]) _LT_TAGDECL([], [include_expsyms], [1], [Symbols that must always be exported]) _LT_TAGDECL([], [prelink_cmds], [2], [Commands necessary for linking programs (against libraries) with templates]) _LT_TAGDECL([], [postlink_cmds], [2], [Commands necessary for finishing linking programs]) _LT_TAGDECL([], [file_list_spec], [1], [Specify filename containing input files]) dnl FIXME: Not yet implemented dnl _LT_TAGDECL([], [thread_safe_flag_spec], [1], dnl [Compiler flag to generate thread safe objects]) ])# _LT_LINKER_SHLIBS # _LT_LANG_C_CONFIG([TAG]) # ------------------------ # Ensure that the configuration variables for a C compiler are suitably # defined. These variables are subsequently used by _LT_CONFIG to write # the compiler configuration to 'libtool'. m4_defun([_LT_LANG_C_CONFIG], [m4_require([_LT_DECL_EGREP])dnl lt_save_CC=$CC AC_LANG_PUSH(C) # Source file extension for C test sources. ac_ext=c # Object file extension for compiled C test sources. objext=o _LT_TAGVAR(objext, $1)=$objext # Code to be used in simple compile tests lt_simple_compile_test_code="int some_variable = 0;" # Code to be used in simple link tests lt_simple_link_test_code='int main(){return(0);}' _LT_TAG_COMPILER # Save the default compiler, since it gets overwritten when the other # tags are being tested, and _LT_TAGVAR(compiler, []) is a NOP. compiler_DEFAULT=$CC # save warnings/boilerplate of simple test code _LT_COMPILER_BOILERPLATE _LT_LINKER_BOILERPLATE if test -n "$compiler"; then _LT_COMPILER_NO_RTTI($1) _LT_COMPILER_PIC($1) _LT_COMPILER_C_O($1) _LT_COMPILER_FILE_LOCKS($1) _LT_LINKER_SHLIBS($1) _LT_SYS_DYNAMIC_LINKER($1) _LT_LINKER_HARDCODE_LIBPATH($1) LT_SYS_DLOPEN_SELF _LT_CMD_STRIPLIB # Report what library types will actually be built AC_MSG_CHECKING([if libtool supports shared libraries]) AC_MSG_RESULT([$can_build_shared]) AC_MSG_CHECKING([whether to build shared libraries]) test no = "$can_build_shared" && enable_shared=no # On AIX, shared libraries and static libraries use the same namespace, and # are all built from PIC. case $host_os in aix3*) test yes = "$enable_shared" && enable_static=no if test -n "$RANLIB"; then archive_cmds="$archive_cmds~\$RANLIB \$lib" postinstall_cmds='$RANLIB $lib' fi ;; aix[[4-9]]*) if test ia64 != "$host_cpu"; then case $enable_shared,$with_aix_soname,$aix_use_runtimelinking in yes,aix,yes) ;; # shared object as lib.so file only yes,svr4,*) ;; # shared object as lib.so archive member only yes,*) enable_static=no ;; # shared object in lib.a archive as well esac fi ;; esac AC_MSG_RESULT([$enable_shared]) AC_MSG_CHECKING([whether to build static libraries]) # Make sure either enable_shared or enable_static is yes. test yes = "$enable_shared" || enable_static=yes AC_MSG_RESULT([$enable_static]) _LT_CONFIG($1) fi AC_LANG_POP CC=$lt_save_CC ])# _LT_LANG_C_CONFIG # _LT_LANG_CXX_CONFIG([TAG]) # -------------------------- # Ensure that the configuration variables for a C++ compiler are suitably # defined. These variables are subsequently used by _LT_CONFIG to write # the compiler configuration to 'libtool'. m4_defun([_LT_LANG_CXX_CONFIG], [m4_require([_LT_FILEUTILS_DEFAULTS])dnl m4_require([_LT_DECL_EGREP])dnl m4_require([_LT_PATH_MANIFEST_TOOL])dnl if test -n "$CXX" && ( test no != "$CXX" && ( (test g++ = "$CXX" && `g++ -v >/dev/null 2>&1` ) || (test g++ != "$CXX"))); then AC_PROG_CXXCPP else _lt_caught_CXX_error=yes fi AC_LANG_PUSH(C++) _LT_TAGVAR(archive_cmds_need_lc, $1)=no _LT_TAGVAR(allow_undefined_flag, $1)= _LT_TAGVAR(always_export_symbols, $1)=no _LT_TAGVAR(archive_expsym_cmds, $1)= _LT_TAGVAR(compiler_needs_object, $1)=no _LT_TAGVAR(export_dynamic_flag_spec, $1)= _LT_TAGVAR(hardcode_direct, $1)=no _LT_TAGVAR(hardcode_direct_absolute, $1)=no _LT_TAGVAR(hardcode_libdir_flag_spec, $1)= _LT_TAGVAR(hardcode_libdir_separator, $1)= _LT_TAGVAR(hardcode_minus_L, $1)=no _LT_TAGVAR(hardcode_shlibpath_var, $1)=unsupported _LT_TAGVAR(hardcode_automatic, $1)=no _LT_TAGVAR(inherit_rpath, $1)=no _LT_TAGVAR(module_cmds, $1)= _LT_TAGVAR(module_expsym_cmds, $1)= _LT_TAGVAR(link_all_deplibs, $1)=unknown _LT_TAGVAR(old_archive_cmds, $1)=$old_archive_cmds _LT_TAGVAR(reload_flag, $1)=$reload_flag _LT_TAGVAR(reload_cmds, $1)=$reload_cmds _LT_TAGVAR(no_undefined_flag, $1)= _LT_TAGVAR(whole_archive_flag_spec, $1)= _LT_TAGVAR(enable_shared_with_static_runtimes, $1)=no # Source file extension for C++ test sources. ac_ext=cpp # Object file extension for compiled C++ test sources. objext=o _LT_TAGVAR(objext, $1)=$objext # No sense in running all these tests if we already determined that # the CXX compiler isn't working. Some variables (like enable_shared) # are currently assumed to apply to all compilers on this platform, # and will be corrupted by setting them based on a non-working compiler. if test yes != "$_lt_caught_CXX_error"; then # Code to be used in simple compile tests lt_simple_compile_test_code="int some_variable = 0;" # Code to be used in simple link tests lt_simple_link_test_code='int main(int, char *[[]]) { return(0); }' # ltmain only uses $CC for tagged configurations so make sure $CC is set. _LT_TAG_COMPILER # save warnings/boilerplate of simple test code _LT_COMPILER_BOILERPLATE _LT_LINKER_BOILERPLATE # Allow CC to be a program name with arguments. lt_save_CC=$CC lt_save_CFLAGS=$CFLAGS lt_save_LD=$LD lt_save_GCC=$GCC GCC=$GXX lt_save_with_gnu_ld=$with_gnu_ld lt_save_path_LD=$lt_cv_path_LD if test -n "${lt_cv_prog_gnu_ldcxx+set}"; then lt_cv_prog_gnu_ld=$lt_cv_prog_gnu_ldcxx else $as_unset lt_cv_prog_gnu_ld fi if test -n "${lt_cv_path_LDCXX+set}"; then lt_cv_path_LD=$lt_cv_path_LDCXX else $as_unset lt_cv_path_LD fi test -z "${LDCXX+set}" || LD=$LDCXX CC=${CXX-"c++"} CFLAGS=$CXXFLAGS compiler=$CC _LT_TAGVAR(compiler, $1)=$CC _LT_CC_BASENAME([$compiler]) if test -n "$compiler"; then # We don't want -fno-exception when compiling C++ code, so set the # no_builtin_flag separately if test yes = "$GXX"; then _LT_TAGVAR(lt_prog_compiler_no_builtin_flag, $1)=' -fno-builtin' else _LT_TAGVAR(lt_prog_compiler_no_builtin_flag, $1)= fi if test yes = "$GXX"; then # Set up default GNU C++ configuration LT_PATH_LD # Check if GNU C++ uses GNU ld as the underlying linker, since the # archiving commands below assume that GNU ld is being used. if test yes = "$with_gnu_ld"; then _LT_TAGVAR(archive_cmds, $1)='$CC $pic_flag -shared -nostdlib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags $wl-soname $wl$soname -o $lib' _LT_TAGVAR(archive_expsym_cmds, $1)='$CC $pic_flag -shared -nostdlib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags $wl-soname $wl$soname $wl-retain-symbols-file $wl$export_symbols -o $lib' _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='$wl-rpath $wl$libdir' _LT_TAGVAR(export_dynamic_flag_spec, $1)='$wl--export-dynamic' # If archive_cmds runs LD, not CC, wlarc should be empty # XXX I think wlarc can be eliminated in ltcf-cxx, but I need to # investigate it a little bit more. (MM) wlarc='$wl' # ancient GNU ld didn't support --whole-archive et. al. if eval "`$CC -print-prog-name=ld` --help 2>&1" | $GREP 'no-whole-archive' > /dev/null; then _LT_TAGVAR(whole_archive_flag_spec, $1)=$wlarc'--whole-archive$convenience '$wlarc'--no-whole-archive' else _LT_TAGVAR(whole_archive_flag_spec, $1)= fi else with_gnu_ld=no wlarc= # A generic and very simple default shared library creation # command for GNU C++ for the case where it uses the native # linker, instead of GNU ld. If possible, this setting should # overridden to take advantage of the native linker features on # the platform it is being used on. _LT_TAGVAR(archive_cmds, $1)='$CC -shared -nostdlib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags -o $lib' fi # Commands to make compiler produce verbose output that lists # what "hidden" libraries, object files and flags are used when # linking a shared library. output_verbose_link_cmd='$CC -shared $CFLAGS -v conftest.$objext 2>&1 | $GREP -v "^Configured with:" | $GREP " \-L"' else GXX=no with_gnu_ld=no wlarc= fi # PORTME: fill in a description of your system's C++ link characteristics AC_MSG_CHECKING([whether the $compiler linker ($LD) supports shared libraries]) _LT_TAGVAR(ld_shlibs, $1)=yes case $host_os in aix3*) # FIXME: insert proper C++ library support _LT_TAGVAR(ld_shlibs, $1)=no ;; aix[[4-9]]*) if test ia64 = "$host_cpu"; then # On IA64, the linker does run time linking by default, so we don't # have to do anything special. aix_use_runtimelinking=no exp_sym_flag='-Bexport' no_entry_flag= else aix_use_runtimelinking=no # Test if we are trying to use run time linking or normal # AIX style linking. If -brtl is somewhere in LDFLAGS, we # have runtime linking enabled, and use it for executables. # For shared libraries, we enable/disable runtime linking # depending on the kind of the shared library created - # when "with_aix_soname,aix_use_runtimelinking" is: # "aix,no" lib.a(lib.so.V) shared, rtl:no, for executables # "aix,yes" lib.so shared, rtl:yes, for executables # lib.a static archive # "both,no" lib.so.V(shr.o) shared, rtl:yes # lib.a(lib.so.V) shared, rtl:no, for executables # "both,yes" lib.so.V(shr.o) shared, rtl:yes, for executables # lib.a(lib.so.V) shared, rtl:no # "svr4,*" lib.so.V(shr.o) shared, rtl:yes, for executables # lib.a static archive case $host_os in aix4.[[23]]|aix4.[[23]].*|aix[[5-9]]*) for ld_flag in $LDFLAGS; do case $ld_flag in *-brtl*) aix_use_runtimelinking=yes break ;; esac done if test svr4,no = "$with_aix_soname,$aix_use_runtimelinking"; then # With aix-soname=svr4, we create the lib.so.V shared archives only, # so we don't have lib.a shared libs to link our executables. # We have to force runtime linking in this case. aix_use_runtimelinking=yes LDFLAGS="$LDFLAGS -Wl,-brtl" fi ;; esac exp_sym_flag='-bexport' no_entry_flag='-bnoentry' fi # When large executables or shared objects are built, AIX ld can # have problems creating the table of contents. If linking a library # or program results in "error TOC overflow" add -mminimal-toc to # CXXFLAGS/CFLAGS for g++/gcc. In the cases where that is not # enough to fix the problem, add -Wl,-bbigtoc to LDFLAGS. _LT_TAGVAR(archive_cmds, $1)='' _LT_TAGVAR(hardcode_direct, $1)=yes _LT_TAGVAR(hardcode_direct_absolute, $1)=yes _LT_TAGVAR(hardcode_libdir_separator, $1)=':' _LT_TAGVAR(link_all_deplibs, $1)=yes _LT_TAGVAR(file_list_spec, $1)='$wl-f,' case $with_aix_soname,$aix_use_runtimelinking in aix,*) ;; # no import file svr4,* | *,yes) # use import file # The Import File defines what to hardcode. _LT_TAGVAR(hardcode_direct, $1)=no _LT_TAGVAR(hardcode_direct_absolute, $1)=no ;; esac if test yes = "$GXX"; then case $host_os in aix4.[[012]]|aix4.[[012]].*) # We only want to do this on AIX 4.2 and lower, the check # below for broken collect2 doesn't work under 4.3+ collect2name=`$CC -print-prog-name=collect2` if test -f "$collect2name" && strings "$collect2name" | $GREP resolve_lib_name >/dev/null then # We have reworked collect2 : else # We have old collect2 _LT_TAGVAR(hardcode_direct, $1)=unsupported # It fails to find uninstalled libraries when the uninstalled # path is not listed in the libpath. Setting hardcode_minus_L # to unsupported forces relinking _LT_TAGVAR(hardcode_minus_L, $1)=yes _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-L$libdir' _LT_TAGVAR(hardcode_libdir_separator, $1)= fi esac shared_flag='-shared' if test yes = "$aix_use_runtimelinking"; then shared_flag=$shared_flag' $wl-G' fi # Need to ensure runtime linking is disabled for the traditional # shared library, or the linker may eventually find shared libraries # /with/ Import File - we do not want to mix them. shared_flag_aix='-shared' shared_flag_svr4='-shared $wl-G' else # not using gcc if test ia64 = "$host_cpu"; then # VisualAge C++, Version 5.5 for AIX 5L for IA-64, Beta 3 Release # chokes on -Wl,-G. The following line is correct: shared_flag='-G' else if test yes = "$aix_use_runtimelinking"; then shared_flag='$wl-G' else shared_flag='$wl-bM:SRE' fi shared_flag_aix='$wl-bM:SRE' shared_flag_svr4='$wl-G' fi fi _LT_TAGVAR(export_dynamic_flag_spec, $1)='$wl-bexpall' # It seems that -bexpall does not export symbols beginning with # underscore (_), so it is better to generate a list of symbols to # export. _LT_TAGVAR(always_export_symbols, $1)=yes if test aix,yes = "$with_aix_soname,$aix_use_runtimelinking"; then # Warning - without using the other runtime loading flags (-brtl), # -berok will link without error, but may produce a broken library. # The "-G" linker flag allows undefined symbols. _LT_TAGVAR(no_undefined_flag, $1)='-bernotok' # Determine the default libpath from the value encoded in an empty # executable. _LT_SYS_MODULE_PATH_AIX([$1]) _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='$wl-blibpath:$libdir:'"$aix_libpath" _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -o $output_objdir/$soname $libobjs $deplibs $wl'$no_entry_flag' $compiler_flags `if test -n "$allow_undefined_flag"; then func_echo_all "$wl$allow_undefined_flag"; else :; fi` $wl'$exp_sym_flag:\$export_symbols' '$shared_flag else if test ia64 = "$host_cpu"; then _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='$wl-R $libdir:/usr/lib:/lib' _LT_TAGVAR(allow_undefined_flag, $1)="-z nodefs" _LT_TAGVAR(archive_expsym_cmds, $1)="\$CC $shared_flag"' -o $output_objdir/$soname $libobjs $deplibs '"\$wl$no_entry_flag"' $compiler_flags $wl$allow_undefined_flag '"\$wl$exp_sym_flag:\$export_symbols" else # Determine the default libpath from the value encoded in an # empty executable. _LT_SYS_MODULE_PATH_AIX([$1]) _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='$wl-blibpath:$libdir:'"$aix_libpath" # Warning - without using the other run time loading flags, # -berok will link without error, but may produce a broken library. _LT_TAGVAR(no_undefined_flag, $1)=' $wl-bernotok' _LT_TAGVAR(allow_undefined_flag, $1)=' $wl-berok' if test yes = "$with_gnu_ld"; then # We only use this code for GNU lds that support --whole-archive. _LT_TAGVAR(whole_archive_flag_spec, $1)='$wl--whole-archive$convenience $wl--no-whole-archive' else # Exported symbols can be pulled into shared objects from archives _LT_TAGVAR(whole_archive_flag_spec, $1)='$convenience' fi _LT_TAGVAR(archive_cmds_need_lc, $1)=yes _LT_TAGVAR(archive_expsym_cmds, $1)='$RM -r $output_objdir/$realname.d~$MKDIR $output_objdir/$realname.d' # -brtl affects multiple linker settings, -berok does not and is overridden later compiler_flags_filtered='`func_echo_all "$compiler_flags " | $SED -e "s%-brtl\\([[, ]]\\)%-berok\\1%g"`' if test svr4 != "$with_aix_soname"; then # This is similar to how AIX traditionally builds its shared # libraries. Need -bnortl late, we may have -brtl in LDFLAGS. _LT_TAGVAR(archive_expsym_cmds, $1)="$_LT_TAGVAR(archive_expsym_cmds, $1)"'~$CC '$shared_flag_aix' -o $output_objdir/$realname.d/$soname $libobjs $deplibs $wl-bnoentry '$compiler_flags_filtered'$wl-bE:$export_symbols$allow_undefined_flag~$AR $AR_FLAGS $output_objdir/$libname$release.a $output_objdir/$realname.d/$soname' fi if test aix != "$with_aix_soname"; then _LT_TAGVAR(archive_expsym_cmds, $1)="$_LT_TAGVAR(archive_expsym_cmds, $1)"'~$CC '$shared_flag_svr4' -o $output_objdir/$realname.d/$shared_archive_member_spec.o $libobjs $deplibs $wl-bnoentry '$compiler_flags_filtered'$wl-bE:$export_symbols$allow_undefined_flag~$STRIP -e $output_objdir/$realname.d/$shared_archive_member_spec.o~( func_echo_all "#! $soname($shared_archive_member_spec.o)"; if test shr_64 = "$shared_archive_member_spec"; then func_echo_all "# 64"; else func_echo_all "# 32"; fi; cat $export_symbols ) > $output_objdir/$realname.d/$shared_archive_member_spec.imp~$AR $AR_FLAGS $output_objdir/$soname $output_objdir/$realname.d/$shared_archive_member_spec.o $output_objdir/$realname.d/$shared_archive_member_spec.imp' else # used by -dlpreopen to get the symbols _LT_TAGVAR(archive_expsym_cmds, $1)="$_LT_TAGVAR(archive_expsym_cmds, $1)"'~$MV $output_objdir/$realname.d/$soname $output_objdir' fi _LT_TAGVAR(archive_expsym_cmds, $1)="$_LT_TAGVAR(archive_expsym_cmds, $1)"'~$RM -r $output_objdir/$realname.d' fi fi ;; beos*) if $LD --help 2>&1 | $GREP ': supported targets:.* elf' > /dev/null; then _LT_TAGVAR(allow_undefined_flag, $1)=unsupported # Joseph Beckenbach says some releases of gcc # support --undefined. This deserves some investigation. FIXME _LT_TAGVAR(archive_cmds, $1)='$CC -nostart $libobjs $deplibs $compiler_flags $wl-soname $wl$soname -o $lib' else _LT_TAGVAR(ld_shlibs, $1)=no fi ;; chorus*) case $cc_basename in *) # FIXME: insert proper C++ library support _LT_TAGVAR(ld_shlibs, $1)=no ;; esac ;; cygwin* | mingw* | pw32* | cegcc*) case $GXX,$cc_basename in ,cl* | no,cl* | ,icl* | no,icl*) # Native MSVC or ICC # hardcode_libdir_flag_spec is actually meaningless, as there is # no search path for DLLs. _LT_TAGVAR(hardcode_libdir_flag_spec, $1)=' ' _LT_TAGVAR(allow_undefined_flag, $1)=unsupported _LT_TAGVAR(always_export_symbols, $1)=yes _LT_TAGVAR(file_list_spec, $1)='@' # Tell ltmain to make .lib files, not .a files. libext=lib # Tell ltmain to make .dll files, not .so files. shrext_cmds=.dll # FIXME: Setting linknames here is a bad hack. _LT_TAGVAR(archive_cmds, $1)='$CC -o $output_objdir/$soname $libobjs $compiler_flags $deplibs -Wl,-DLL,-IMPLIB:"$tool_output_objdir$libname.dll.lib"~linknames=' _LT_TAGVAR(archive_expsym_cmds, $1)='if _LT_DLL_DEF_P([$export_symbols]); then cp "$export_symbols" "$output_objdir/$soname.def"; echo "$tool_output_objdir$soname.def" > "$output_objdir/$soname.exp"; else $SED -e '\''s/^/-link -EXPORT:/'\'' < $export_symbols > $output_objdir/$soname.exp; fi~ $CC -o $tool_output_objdir$soname $libobjs $compiler_flags $deplibs "@$tool_output_objdir$soname.exp" -Wl,-DLL,-IMPLIB:"$tool_output_objdir$libname.dll.lib"~ linknames=' # The linker will not automatically build a static lib if we build a DLL. # _LT_TAGVAR(old_archive_from_new_cmds, $1)='true' _LT_TAGVAR(enable_shared_with_static_runtimes, $1)=yes # Don't use ranlib _LT_TAGVAR(old_postinstall_cmds, $1)='chmod 644 $oldlib' _LT_TAGVAR(postlink_cmds, $1)='lt_outputfile="@OUTPUT@"~ lt_tool_outputfile="@TOOL_OUTPUT@"~ case $lt_outputfile in *.exe|*.EXE) ;; *) lt_outputfile=$lt_outputfile.exe lt_tool_outputfile=$lt_tool_outputfile.exe ;; esac~ func_to_tool_file "$lt_outputfile"~ if test : != "$MANIFEST_TOOL" && test -f "$lt_outputfile.manifest"; then $MANIFEST_TOOL -manifest "$lt_tool_outputfile.manifest" -outputresource:"$lt_tool_outputfile" || exit 1; $RM "$lt_outputfile.manifest"; fi' ;; *) # g++ # _LT_TAGVAR(hardcode_libdir_flag_spec, $1) is actually meaningless, # as there is no search path for DLLs. _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-L$libdir' _LT_TAGVAR(export_dynamic_flag_spec, $1)='$wl--export-all-symbols' _LT_TAGVAR(allow_undefined_flag, $1)=unsupported _LT_TAGVAR(always_export_symbols, $1)=no _LT_TAGVAR(enable_shared_with_static_runtimes, $1)=yes if $LD --help 2>&1 | $GREP 'auto-import' > /dev/null; then _LT_TAGVAR(archive_cmds, $1)='$CC -shared -nostdlib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags -o $output_objdir/$soname $wl--enable-auto-image-base -Xlinker --out-implib -Xlinker $lib' # If the export-symbols file already is a .def file, use it as # is; otherwise, prepend EXPORTS... _LT_TAGVAR(archive_expsym_cmds, $1)='if _LT_DLL_DEF_P([$export_symbols]); then cp $export_symbols $output_objdir/$soname.def; else echo EXPORTS > $output_objdir/$soname.def; cat $export_symbols >> $output_objdir/$soname.def; fi~ $CC -shared -nostdlib $output_objdir/$soname.def $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags -o $output_objdir/$soname $wl--enable-auto-image-base -Xlinker --out-implib -Xlinker $lib' else _LT_TAGVAR(ld_shlibs, $1)=no fi ;; esac ;; darwin* | rhapsody*) _LT_DARWIN_LINKER_FEATURES($1) ;; os2*) _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-L$libdir' _LT_TAGVAR(hardcode_minus_L, $1)=yes _LT_TAGVAR(allow_undefined_flag, $1)=unsupported shrext_cmds=.dll _LT_TAGVAR(archive_cmds, $1)='$ECHO "LIBRARY ${soname%$shared_ext} INITINSTANCE TERMINSTANCE" > $output_objdir/$libname.def~ $ECHO "DESCRIPTION \"$libname\"" >> $output_objdir/$libname.def~ $ECHO "DATA MULTIPLE NONSHARED" >> $output_objdir/$libname.def~ $ECHO EXPORTS >> $output_objdir/$libname.def~ emxexp $libobjs | $SED /"_DLL_InitTerm"/d >> $output_objdir/$libname.def~ $CC -Zdll -Zcrtdll -o $output_objdir/$soname $libobjs $deplibs $compiler_flags $output_objdir/$libname.def~ emximp -o $lib $output_objdir/$libname.def' _LT_TAGVAR(archive_expsym_cmds, $1)='$ECHO "LIBRARY ${soname%$shared_ext} INITINSTANCE TERMINSTANCE" > $output_objdir/$libname.def~ $ECHO "DESCRIPTION \"$libname\"" >> $output_objdir/$libname.def~ $ECHO "DATA MULTIPLE NONSHARED" >> $output_objdir/$libname.def~ $ECHO EXPORTS >> $output_objdir/$libname.def~ prefix_cmds="$SED"~ if test EXPORTS = "`$SED 1q $export_symbols`"; then prefix_cmds="$prefix_cmds -e 1d"; fi~ prefix_cmds="$prefix_cmds -e \"s/^\(.*\)$/_\1/g\""~ cat $export_symbols | $prefix_cmds >> $output_objdir/$libname.def~ $CC -Zdll -Zcrtdll -o $output_objdir/$soname $libobjs $deplibs $compiler_flags $output_objdir/$libname.def~ emximp -o $lib $output_objdir/$libname.def' _LT_TAGVAR(old_archive_From_new_cmds, $1)='emximp -o $output_objdir/${libname}_dll.a $output_objdir/$libname.def' _LT_TAGVAR(enable_shared_with_static_runtimes, $1)=yes _LT_TAGVAR(file_list_spec, $1)='@' ;; dgux*) case $cc_basename in ec++*) # FIXME: insert proper C++ library support _LT_TAGVAR(ld_shlibs, $1)=no ;; ghcx*) # Green Hills C++ Compiler # FIXME: insert proper C++ library support _LT_TAGVAR(ld_shlibs, $1)=no ;; *) # FIXME: insert proper C++ library support _LT_TAGVAR(ld_shlibs, $1)=no ;; esac ;; freebsd2.*) # C++ shared libraries reported to be fairly broken before # switch to ELF _LT_TAGVAR(ld_shlibs, $1)=no ;; freebsd-elf*) _LT_TAGVAR(archive_cmds_need_lc, $1)=no ;; freebsd* | dragonfly* | midnightbsd*) # FreeBSD 3 and later use GNU C++ and GNU ld with standard ELF # conventions _LT_TAGVAR(ld_shlibs, $1)=yes ;; haiku*) _LT_TAGVAR(archive_cmds, $1)='$CC -shared $libobjs $deplibs $compiler_flags $wl-soname $wl$soname -o $lib' _LT_TAGVAR(link_all_deplibs, $1)=yes ;; hpux9*) _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='$wl+b $wl$libdir' _LT_TAGVAR(hardcode_libdir_separator, $1)=: _LT_TAGVAR(export_dynamic_flag_spec, $1)='$wl-E' _LT_TAGVAR(hardcode_direct, $1)=yes _LT_TAGVAR(hardcode_minus_L, $1)=yes # Not in the search PATH, # but as the default # location of the library. case $cc_basename in CC*) # FIXME: insert proper C++ library support _LT_TAGVAR(ld_shlibs, $1)=no ;; aCC*) _LT_TAGVAR(archive_cmds, $1)='$RM $output_objdir/$soname~$CC -b $wl+b $wl$install_libdir -o $output_objdir/$soname $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags~test "x$output_objdir/$soname" = "x$lib" || mv $output_objdir/$soname $lib' # Commands to make compiler produce verbose output that lists # what "hidden" libraries, object files and flags are used when # linking a shared library. # # There doesn't appear to be a way to prevent this compiler from # explicitly linking system object files so we need to strip them # from the output so that they don't get included in the library # dependencies. output_verbose_link_cmd='templist=`($CC -b $CFLAGS -v conftest.$objext 2>&1) | $EGREP " \-L"`; list= ; for z in $templist; do case $z in conftest.$objext) list="$list $z";; *.$objext);; *) list="$list $z";;esac; done; func_echo_all "$list"' ;; *) if test yes = "$GXX"; then _LT_TAGVAR(archive_cmds, $1)='$RM $output_objdir/$soname~$CC -shared -nostdlib $pic_flag $wl+b $wl$install_libdir -o $output_objdir/$soname $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags~test "x$output_objdir/$soname" = "x$lib" || mv $output_objdir/$soname $lib' else # FIXME: insert proper C++ library support _LT_TAGVAR(ld_shlibs, $1)=no fi ;; esac ;; hpux10*|hpux11*) if test no = "$with_gnu_ld"; then _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='$wl+b $wl$libdir' _LT_TAGVAR(hardcode_libdir_separator, $1)=: case $host_cpu in hppa*64*|ia64*) ;; *) _LT_TAGVAR(export_dynamic_flag_spec, $1)='$wl-E' ;; esac fi case $host_cpu in hppa*64*|ia64*) _LT_TAGVAR(hardcode_direct, $1)=no _LT_TAGVAR(hardcode_shlibpath_var, $1)=no ;; *) _LT_TAGVAR(hardcode_direct, $1)=yes _LT_TAGVAR(hardcode_direct_absolute, $1)=yes _LT_TAGVAR(hardcode_minus_L, $1)=yes # Not in the search PATH, # but as the default # location of the library. ;; esac case $cc_basename in CC*) # FIXME: insert proper C++ library support _LT_TAGVAR(ld_shlibs, $1)=no ;; aCC*) case $host_cpu in hppa*64*) _LT_TAGVAR(archive_cmds, $1)='$CC -b $wl+h $wl$soname -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags' ;; ia64*) _LT_TAGVAR(archive_cmds, $1)='$CC -b $wl+h $wl$soname $wl+nodefaultrpath -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags' ;; *) _LT_TAGVAR(archive_cmds, $1)='$CC -b $wl+h $wl$soname $wl+b $wl$install_libdir -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags' ;; esac # Commands to make compiler produce verbose output that lists # what "hidden" libraries, object files and flags are used when # linking a shared library. # # There doesn't appear to be a way to prevent this compiler from # explicitly linking system object files so we need to strip them # from the output so that they don't get included in the library # dependencies. output_verbose_link_cmd='templist=`($CC -b $CFLAGS -v conftest.$objext 2>&1) | $GREP " \-L"`; list= ; for z in $templist; do case $z in conftest.$objext) list="$list $z";; *.$objext);; *) list="$list $z";;esac; done; func_echo_all "$list"' ;; *) if test yes = "$GXX"; then if test no = "$with_gnu_ld"; then case $host_cpu in hppa*64*) _LT_TAGVAR(archive_cmds, $1)='$CC -shared -nostdlib -fPIC $wl+h $wl$soname -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags' ;; ia64*) _LT_TAGVAR(archive_cmds, $1)='$CC -shared -nostdlib $pic_flag $wl+h $wl$soname $wl+nodefaultrpath -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags' ;; *) _LT_TAGVAR(archive_cmds, $1)='$CC -shared -nostdlib $pic_flag $wl+h $wl$soname $wl+b $wl$install_libdir -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags' ;; esac fi else # FIXME: insert proper C++ library support _LT_TAGVAR(ld_shlibs, $1)=no fi ;; esac ;; interix[[3-9]]*) _LT_TAGVAR(hardcode_direct, $1)=no _LT_TAGVAR(hardcode_shlibpath_var, $1)=no _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='$wl-rpath,$libdir' _LT_TAGVAR(export_dynamic_flag_spec, $1)='$wl-E' # Hack: On Interix 3.x, we cannot compile PIC because of a broken gcc. # Instead, shared libraries are loaded at an image base (0x10000000 by # default) and relocated if they conflict, which is a slow very memory # consuming and fragmenting process. To avoid this, we pick a random, # 256 KiB-aligned image base between 0x50000000 and 0x6FFC0000 at link # time. Moving up from 0x10000000 also allows more sbrk(2) space. _LT_TAGVAR(archive_cmds, $1)='$CC -shared $pic_flag $libobjs $deplibs $compiler_flags $wl-h,$soname $wl--image-base,`expr ${RANDOM-$$} % 4096 / 2 \* 262144 + 1342177280` -o $lib' _LT_TAGVAR(archive_expsym_cmds, $1)='$SED "s|^|_|" $export_symbols >$output_objdir/$soname.expsym~$CC -shared $pic_flag $libobjs $deplibs $compiler_flags $wl-h,$soname $wl--retain-symbols-file,$output_objdir/$soname.expsym $wl--image-base,`expr ${RANDOM-$$} % 4096 / 2 \* 262144 + 1342177280` -o $lib' ;; irix5* | irix6*) case $cc_basename in CC*) # SGI C++ _LT_TAGVAR(archive_cmds, $1)='$CC -shared -all -multigot $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags -soname $soname `test -n "$verstring" && func_echo_all "-set_version $verstring"` -update_registry $output_objdir/so_locations -o $lib' # Archives containing C++ object files must be created using # "CC -ar", where "CC" is the IRIX C++ compiler. This is # necessary to make sure instantiated templates are included # in the archive. _LT_TAGVAR(old_archive_cmds, $1)='$CC -ar -WR,-u -o $oldlib $oldobjs' ;; *) if test yes = "$GXX"; then if test no = "$with_gnu_ld"; then _LT_TAGVAR(archive_cmds, $1)='$CC -shared $pic_flag -nostdlib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags $wl-soname $wl$soname `test -n "$verstring" && func_echo_all "$wl-set_version $wl$verstring"` $wl-update_registry $wl$output_objdir/so_locations -o $lib' else _LT_TAGVAR(archive_cmds, $1)='$CC -shared $pic_flag -nostdlib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags $wl-soname $wl$soname `test -n "$verstring" && func_echo_all "$wl-set_version $wl$verstring"` -o $lib' fi fi _LT_TAGVAR(link_all_deplibs, $1)=yes ;; esac _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='$wl-rpath $wl$libdir' _LT_TAGVAR(hardcode_libdir_separator, $1)=: _LT_TAGVAR(inherit_rpath, $1)=yes ;; linux* | k*bsd*-gnu | kopensolaris*-gnu | gnu*) case $cc_basename in KCC*) # Kuck and Associates, Inc. (KAI) C++ Compiler # KCC will only create a shared library if the output file # ends with ".so" (or ".sl" for HP-UX), so rename the library # to its proper name (with version) after linking. _LT_TAGVAR(archive_cmds, $1)='tempext=`echo $shared_ext | $SED -e '\''s/\([[^()0-9A-Za-z{}]]\)/\\\\\1/g'\''`; templib=`echo $lib | $SED -e "s/\$tempext\..*/.so/"`; $CC $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags --soname $soname -o \$templib; mv \$templib $lib' _LT_TAGVAR(archive_expsym_cmds, $1)='tempext=`echo $shared_ext | $SED -e '\''s/\([[^()0-9A-Za-z{}]]\)/\\\\\1/g'\''`; templib=`echo $lib | $SED -e "s/\$tempext\..*/.so/"`; $CC $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags --soname $soname -o \$templib $wl-retain-symbols-file,$export_symbols; mv \$templib $lib' # Commands to make compiler produce verbose output that lists # what "hidden" libraries, object files and flags are used when # linking a shared library. # # There doesn't appear to be a way to prevent this compiler from # explicitly linking system object files so we need to strip them # from the output so that they don't get included in the library # dependencies. output_verbose_link_cmd='templist=`$CC $CFLAGS -v conftest.$objext -o libconftest$shared_ext 2>&1 | $GREP "ld"`; rm -f libconftest$shared_ext; list= ; for z in $templist; do case $z in conftest.$objext) list="$list $z";; *.$objext);; *) list="$list $z";;esac; done; func_echo_all "$list"' _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='$wl-rpath,$libdir' _LT_TAGVAR(export_dynamic_flag_spec, $1)='$wl--export-dynamic' # Archives containing C++ object files must be created using # "CC -Bstatic", where "CC" is the KAI C++ compiler. _LT_TAGVAR(old_archive_cmds, $1)='$CC -Bstatic -o $oldlib $oldobjs' ;; icpc* | ecpc* ) # Intel C++ with_gnu_ld=yes # version 8.0 and above of icpc choke on multiply defined symbols # if we add $predep_objects and $postdep_objects, however 7.1 and # earlier do not add the objects themselves. case `$CC -V 2>&1` in *"Version 7."*) _LT_TAGVAR(archive_cmds, $1)='$CC -shared $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags $wl-soname $wl$soname -o $lib' _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -shared $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags $wl-soname $wl$soname $wl-retain-symbols-file $wl$export_symbols -o $lib' ;; *) # Version 8.0 or newer tmp_idyn= case $host_cpu in ia64*) tmp_idyn=' -i_dynamic';; esac _LT_TAGVAR(archive_cmds, $1)='$CC -shared'"$tmp_idyn"' $libobjs $deplibs $compiler_flags $wl-soname $wl$soname -o $lib' _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -shared'"$tmp_idyn"' $libobjs $deplibs $compiler_flags $wl-soname $wl$soname $wl-retain-symbols-file $wl$export_symbols -o $lib' ;; esac _LT_TAGVAR(archive_cmds_need_lc, $1)=no _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='$wl-rpath,$libdir' _LT_TAGVAR(export_dynamic_flag_spec, $1)='$wl--export-dynamic' _LT_TAGVAR(whole_archive_flag_spec, $1)='$wl--whole-archive$convenience $wl--no-whole-archive' ;; pgCC* | pgcpp*) # Portland Group C++ compiler case `$CC -V` in *pgCC\ [[1-5]].* | *pgcpp\ [[1-5]].*) _LT_TAGVAR(prelink_cmds, $1)='tpldir=Template.dir~ rm -rf $tpldir~ $CC --prelink_objects --instantiation_dir $tpldir $objs $libobjs $compile_deplibs~ compile_command="$compile_command `find $tpldir -name \*.o | sort | $NL2SP`"' _LT_TAGVAR(old_archive_cmds, $1)='tpldir=Template.dir~ rm -rf $tpldir~ $CC --prelink_objects --instantiation_dir $tpldir $oldobjs$old_deplibs~ $AR $AR_FLAGS $oldlib$oldobjs$old_deplibs `find $tpldir -name \*.o | sort | $NL2SP`~ $RANLIB $oldlib' _LT_TAGVAR(archive_cmds, $1)='tpldir=Template.dir~ rm -rf $tpldir~ $CC --prelink_objects --instantiation_dir $tpldir $predep_objects $libobjs $deplibs $convenience $postdep_objects~ $CC -shared $pic_flag $predep_objects $libobjs $deplibs `find $tpldir -name \*.o | sort | $NL2SP` $postdep_objects $compiler_flags $wl-soname $wl$soname -o $lib' _LT_TAGVAR(archive_expsym_cmds, $1)='tpldir=Template.dir~ rm -rf $tpldir~ $CC --prelink_objects --instantiation_dir $tpldir $predep_objects $libobjs $deplibs $convenience $postdep_objects~ $CC -shared $pic_flag $predep_objects $libobjs $deplibs `find $tpldir -name \*.o | sort | $NL2SP` $postdep_objects $compiler_flags $wl-soname $wl$soname $wl-retain-symbols-file $wl$export_symbols -o $lib' ;; *) # Version 6 and above use weak symbols _LT_TAGVAR(archive_cmds, $1)='$CC -shared $pic_flag $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags $wl-soname $wl$soname -o $lib' _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -shared $pic_flag $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags $wl-soname $wl$soname $wl-retain-symbols-file $wl$export_symbols -o $lib' ;; esac _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='$wl--rpath $wl$libdir' _LT_TAGVAR(export_dynamic_flag_spec, $1)='$wl--export-dynamic' _LT_TAGVAR(whole_archive_flag_spec, $1)='$wl--whole-archive`for conv in $convenience\"\"; do test -n \"$conv\" && new_convenience=\"$new_convenience,$conv\"; done; func_echo_all \"$new_convenience\"` $wl--no-whole-archive' ;; cxx*) # Compaq C++ _LT_TAGVAR(archive_cmds, $1)='$CC -shared $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags $wl-soname $wl$soname -o $lib' _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -shared $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags $wl-soname $wl$soname -o $lib $wl-retain-symbols-file $wl$export_symbols' runpath_var=LD_RUN_PATH _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-rpath $libdir' _LT_TAGVAR(hardcode_libdir_separator, $1)=: # Commands to make compiler produce verbose output that lists # what "hidden" libraries, object files and flags are used when # linking a shared library. # # There doesn't appear to be a way to prevent this compiler from # explicitly linking system object files so we need to strip them # from the output so that they don't get included in the library # dependencies. output_verbose_link_cmd='templist=`$CC -shared $CFLAGS -v conftest.$objext 2>&1 | $GREP "ld"`; templist=`func_echo_all "$templist" | $SED "s/\(^.*ld.*\)\( .*ld .*$\)/\1/"`; list= ; for z in $templist; do case $z in conftest.$objext) list="$list $z";; *.$objext);; *) list="$list $z";;esac; done; func_echo_all "X$list" | $Xsed' ;; xl* | mpixl* | bgxl*) # IBM XL 8.0 on PPC, with GNU ld _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='$wl-rpath $wl$libdir' _LT_TAGVAR(export_dynamic_flag_spec, $1)='$wl--export-dynamic' _LT_TAGVAR(archive_cmds, $1)='$CC -qmkshrobj $libobjs $deplibs $compiler_flags $wl-soname $wl$soname -o $lib' if test yes = "$supports_anon_versioning"; then _LT_TAGVAR(archive_expsym_cmds, $1)='echo "{ global:" > $output_objdir/$libname.ver~ cat $export_symbols | $SED -e "s/\(.*\)/\1;/" >> $output_objdir/$libname.ver~ echo "local: *; };" >> $output_objdir/$libname.ver~ $CC -qmkshrobj $libobjs $deplibs $compiler_flags $wl-soname $wl$soname $wl-version-script $wl$output_objdir/$libname.ver -o $lib' fi ;; *) case `$CC -V 2>&1 | $SED 5q` in *Sun\ C*) # Sun C++ 5.9 _LT_TAGVAR(no_undefined_flag, $1)=' -zdefs' _LT_TAGVAR(archive_cmds, $1)='$CC -G$allow_undefined_flag -h$soname -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags' _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -G$allow_undefined_flag -h$soname -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags $wl-retain-symbols-file $wl$export_symbols' _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-R$libdir' _LT_TAGVAR(whole_archive_flag_spec, $1)='$wl--whole-archive`new_convenience=; for conv in $convenience\"\"; do test -z \"$conv\" || new_convenience=\"$new_convenience,$conv\"; done; func_echo_all \"$new_convenience\"` $wl--no-whole-archive' _LT_TAGVAR(compiler_needs_object, $1)=yes # Not sure whether something based on # $CC $CFLAGS -v conftest.$objext -o libconftest$shared_ext 2>&1 # would be better. output_verbose_link_cmd='func_echo_all' # Archives containing C++ object files must be created using # "CC -xar", where "CC" is the Sun C++ compiler. This is # necessary to make sure instantiated templates are included # in the archive. _LT_TAGVAR(old_archive_cmds, $1)='$CC -xar -o $oldlib $oldobjs' ;; esac ;; esac ;; lynxos*) # FIXME: insert proper C++ library support _LT_TAGVAR(ld_shlibs, $1)=no ;; m88k*) # FIXME: insert proper C++ library support _LT_TAGVAR(ld_shlibs, $1)=no ;; mvs*) case $cc_basename in cxx*) # FIXME: insert proper C++ library support _LT_TAGVAR(ld_shlibs, $1)=no ;; *) # FIXME: insert proper C++ library support _LT_TAGVAR(ld_shlibs, $1)=no ;; esac ;; netbsd*) if echo __ELF__ | $CC -E - | $GREP __ELF__ >/dev/null; then _LT_TAGVAR(archive_cmds, $1)='$LD -Bshareable -o $lib $predep_objects $libobjs $deplibs $postdep_objects $linker_flags' wlarc= _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-R$libdir' _LT_TAGVAR(hardcode_direct, $1)=yes _LT_TAGVAR(hardcode_shlibpath_var, $1)=no fi # Workaround some broken pre-1.5 toolchains output_verbose_link_cmd='$CC -shared $CFLAGS -v conftest.$objext 2>&1 | $GREP conftest.$objext | $SED -e "s:-lgcc -lc -lgcc::"' ;; *nto* | *qnx*) _LT_TAGVAR(ld_shlibs, $1)=yes ;; openbsd* | bitrig*) if test -f /usr/libexec/ld.so; then _LT_TAGVAR(hardcode_direct, $1)=yes _LT_TAGVAR(hardcode_shlibpath_var, $1)=no _LT_TAGVAR(hardcode_direct_absolute, $1)=yes _LT_TAGVAR(archive_cmds, $1)='$CC -shared $pic_flag $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags -o $lib' _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='$wl-rpath,$libdir' if test -z "`echo __ELF__ | $CC -E - | grep __ELF__`"; then _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -shared $pic_flag $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags $wl-retain-symbols-file,$export_symbols -o $lib' _LT_TAGVAR(export_dynamic_flag_spec, $1)='$wl-E' _LT_TAGVAR(whole_archive_flag_spec, $1)=$wlarc'--whole-archive$convenience '$wlarc'--no-whole-archive' fi output_verbose_link_cmd=func_echo_all else _LT_TAGVAR(ld_shlibs, $1)=no fi ;; osf3* | osf4* | osf5*) case $cc_basename in KCC*) # Kuck and Associates, Inc. (KAI) C++ Compiler # KCC will only create a shared library if the output file # ends with ".so" (or ".sl" for HP-UX), so rename the library # to its proper name (with version) after linking. _LT_TAGVAR(archive_cmds, $1)='tempext=`echo $shared_ext | $SED -e '\''s/\([[^()0-9A-Za-z{}]]\)/\\\\\1/g'\''`; templib=`echo "$lib" | $SED -e "s/\$tempext\..*/.so/"`; $CC $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags --soname $soname -o \$templib; mv \$templib $lib' _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='$wl-rpath,$libdir' _LT_TAGVAR(hardcode_libdir_separator, $1)=: # Archives containing C++ object files must be created using # the KAI C++ compiler. case $host in osf3*) _LT_TAGVAR(old_archive_cmds, $1)='$CC -Bstatic -o $oldlib $oldobjs' ;; *) _LT_TAGVAR(old_archive_cmds, $1)='$CC -o $oldlib $oldobjs' ;; esac ;; RCC*) # Rational C++ 2.4.1 # FIXME: insert proper C++ library support _LT_TAGVAR(ld_shlibs, $1)=no ;; cxx*) case $host in osf3*) _LT_TAGVAR(allow_undefined_flag, $1)=' $wl-expect_unresolved $wl\*' _LT_TAGVAR(archive_cmds, $1)='$CC -shared$allow_undefined_flag $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags $wl-soname $soname `test -n "$verstring" && func_echo_all "$wl-set_version $verstring"` -update_registry $output_objdir/so_locations -o $lib' _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='$wl-rpath $wl$libdir' ;; *) _LT_TAGVAR(allow_undefined_flag, $1)=' -expect_unresolved \*' _LT_TAGVAR(archive_cmds, $1)='$CC -shared$allow_undefined_flag $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags -msym -soname $soname `test -n "$verstring" && func_echo_all "-set_version $verstring"` -update_registry $output_objdir/so_locations -o $lib' _LT_TAGVAR(archive_expsym_cmds, $1)='for i in `cat $export_symbols`; do printf "%s %s\\n" -exported_symbol "\$i" >> $lib.exp; done~ echo "-hidden">> $lib.exp~ $CC -shared$allow_undefined_flag $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags -msym -soname $soname $wl-input $wl$lib.exp `test -n "$verstring" && $ECHO "-set_version $verstring"` -update_registry $output_objdir/so_locations -o $lib~ $RM $lib.exp' _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-rpath $libdir' ;; esac _LT_TAGVAR(hardcode_libdir_separator, $1)=: # Commands to make compiler produce verbose output that lists # what "hidden" libraries, object files and flags are used when # linking a shared library. # # There doesn't appear to be a way to prevent this compiler from # explicitly linking system object files so we need to strip them # from the output so that they don't get included in the library # dependencies. output_verbose_link_cmd='templist=`$CC -shared $CFLAGS -v conftest.$objext 2>&1 | $GREP "ld" | $GREP -v "ld:"`; templist=`func_echo_all "$templist" | $SED "s/\(^.*ld.*\)\( .*ld.*$\)/\1/"`; list= ; for z in $templist; do case $z in conftest.$objext) list="$list $z";; *.$objext);; *) list="$list $z";;esac; done; func_echo_all "$list"' ;; *) if test yes,no = "$GXX,$with_gnu_ld"; then _LT_TAGVAR(allow_undefined_flag, $1)=' $wl-expect_unresolved $wl\*' case $host in osf3*) _LT_TAGVAR(archive_cmds, $1)='$CC -shared -nostdlib $allow_undefined_flag $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags $wl-soname $wl$soname `test -n "$verstring" && func_echo_all "$wl-set_version $wl$verstring"` $wl-update_registry $wl$output_objdir/so_locations -o $lib' ;; *) _LT_TAGVAR(archive_cmds, $1)='$CC -shared $pic_flag -nostdlib $allow_undefined_flag $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags $wl-msym $wl-soname $wl$soname `test -n "$verstring" && func_echo_all "$wl-set_version $wl$verstring"` $wl-update_registry $wl$output_objdir/so_locations -o $lib' ;; esac _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='$wl-rpath $wl$libdir' _LT_TAGVAR(hardcode_libdir_separator, $1)=: # Commands to make compiler produce verbose output that lists # what "hidden" libraries, object files and flags are used when # linking a shared library. output_verbose_link_cmd='$CC -shared $CFLAGS -v conftest.$objext 2>&1 | $GREP -v "^Configured with:" | $GREP " \-L"' else # FIXME: insert proper C++ library support _LT_TAGVAR(ld_shlibs, $1)=no fi ;; esac ;; psos*) # FIXME: insert proper C++ library support _LT_TAGVAR(ld_shlibs, $1)=no ;; sunos4*) case $cc_basename in CC*) # Sun C++ 4.x # FIXME: insert proper C++ library support _LT_TAGVAR(ld_shlibs, $1)=no ;; lcc*) # Lucid # FIXME: insert proper C++ library support _LT_TAGVAR(ld_shlibs, $1)=no ;; *) # FIXME: insert proper C++ library support _LT_TAGVAR(ld_shlibs, $1)=no ;; esac ;; solaris*) case $cc_basename in CC* | sunCC*) # Sun C++ 4.2, 5.x and Centerline C++ _LT_TAGVAR(archive_cmds_need_lc,$1)=yes _LT_TAGVAR(no_undefined_flag, $1)=' -zdefs' _LT_TAGVAR(archive_cmds, $1)='$CC -G$allow_undefined_flag -h$soname -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags' _LT_TAGVAR(archive_expsym_cmds, $1)='echo "{ global:" > $lib.exp~cat $export_symbols | $SED -e "s/\(.*\)/\1;/" >> $lib.exp~echo "local: *; };" >> $lib.exp~ $CC -G$allow_undefined_flag $wl-M $wl$lib.exp -h$soname -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags~$RM $lib.exp' _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-R$libdir' _LT_TAGVAR(hardcode_shlibpath_var, $1)=no case $host_os in solaris2.[[0-5]] | solaris2.[[0-5]].*) ;; *) # The compiler driver will combine and reorder linker options, # but understands '-z linker_flag'. # Supported since Solaris 2.6 (maybe 2.5.1?) _LT_TAGVAR(whole_archive_flag_spec, $1)='-z allextract$convenience -z defaultextract' ;; esac _LT_TAGVAR(link_all_deplibs, $1)=yes output_verbose_link_cmd='func_echo_all' # Archives containing C++ object files must be created using # "CC -xar", where "CC" is the Sun C++ compiler. This is # necessary to make sure instantiated templates are included # in the archive. _LT_TAGVAR(old_archive_cmds, $1)='$CC -xar -o $oldlib $oldobjs' ;; gcx*) # Green Hills C++ Compiler _LT_TAGVAR(archive_cmds, $1)='$CC -shared $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags $wl-h $wl$soname -o $lib' # The C++ compiler must be used to create the archive. _LT_TAGVAR(old_archive_cmds, $1)='$CC $LDFLAGS -archive -o $oldlib $oldobjs' ;; *) # GNU C++ compiler with Solaris linker if test yes,no = "$GXX,$with_gnu_ld"; then _LT_TAGVAR(no_undefined_flag, $1)=' $wl-z ${wl}defs' if $CC --version | $GREP -v '^2\.7' > /dev/null; then _LT_TAGVAR(archive_cmds, $1)='$CC -shared $pic_flag -nostdlib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags $wl-h $wl$soname -o $lib' _LT_TAGVAR(archive_expsym_cmds, $1)='echo "{ global:" > $lib.exp~cat $export_symbols | $SED -e "s/\(.*\)/\1;/" >> $lib.exp~echo "local: *; };" >> $lib.exp~ $CC -shared $pic_flag -nostdlib $wl-M $wl$lib.exp $wl-h $wl$soname -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags~$RM $lib.exp' # Commands to make compiler produce verbose output that lists # what "hidden" libraries, object files and flags are used when # linking a shared library. output_verbose_link_cmd='$CC -shared $CFLAGS -v conftest.$objext 2>&1 | $GREP -v "^Configured with:" | $GREP " \-L"' else # g++ 2.7 appears to require '-G' NOT '-shared' on this # platform. _LT_TAGVAR(archive_cmds, $1)='$CC -G -nostdlib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags $wl-h $wl$soname -o $lib' _LT_TAGVAR(archive_expsym_cmds, $1)='echo "{ global:" > $lib.exp~cat $export_symbols | $SED -e "s/\(.*\)/\1;/" >> $lib.exp~echo "local: *; };" >> $lib.exp~ $CC -G -nostdlib $wl-M $wl$lib.exp $wl-h $wl$soname -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags~$RM $lib.exp' # Commands to make compiler produce verbose output that lists # what "hidden" libraries, object files and flags are used when # linking a shared library. output_verbose_link_cmd='$CC -G $CFLAGS -v conftest.$objext 2>&1 | $GREP -v "^Configured with:" | $GREP " \-L"' fi _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='$wl-R $wl$libdir' case $host_os in solaris2.[[0-5]] | solaris2.[[0-5]].*) ;; *) _LT_TAGVAR(whole_archive_flag_spec, $1)='$wl-z ${wl}allextract$convenience $wl-z ${wl}defaultextract' ;; esac fi ;; esac ;; sysv4*uw2* | sysv5OpenUNIX* | sysv5UnixWare7.[[01]].[[10]]* | unixware7* | sco3.2v5.0.[[024]]*) _LT_TAGVAR(no_undefined_flag, $1)='$wl-z,text' _LT_TAGVAR(archive_cmds_need_lc, $1)=no _LT_TAGVAR(hardcode_shlibpath_var, $1)=no runpath_var='LD_RUN_PATH' case $cc_basename in CC*) _LT_TAGVAR(archive_cmds, $1)='$CC -G $wl-h,$soname -o $lib $libobjs $deplibs $compiler_flags' _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -G $wl-Bexport:$export_symbols $wl-h,$soname -o $lib $libobjs $deplibs $compiler_flags' ;; *) _LT_TAGVAR(archive_cmds, $1)='$CC -shared $wl-h,$soname -o $lib $libobjs $deplibs $compiler_flags' _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -shared $wl-Bexport:$export_symbols $wl-h,$soname -o $lib $libobjs $deplibs $compiler_flags' ;; esac ;; sysv5* | sco3.2v5* | sco5v6*) # Note: We CANNOT use -z defs as we might desire, because we do not # link with -lc, and that would cause any symbols used from libc to # always be unresolved, which means just about no library would # ever link correctly. If we're not using GNU ld we use -z text # though, which does catch some bad symbols but isn't as heavy-handed # as -z defs. _LT_TAGVAR(no_undefined_flag, $1)='$wl-z,text' _LT_TAGVAR(allow_undefined_flag, $1)='$wl-z,nodefs' _LT_TAGVAR(archive_cmds_need_lc, $1)=no _LT_TAGVAR(hardcode_shlibpath_var, $1)=no _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='$wl-R,$libdir' _LT_TAGVAR(hardcode_libdir_separator, $1)=':' _LT_TAGVAR(link_all_deplibs, $1)=yes _LT_TAGVAR(export_dynamic_flag_spec, $1)='$wl-Bexport' runpath_var='LD_RUN_PATH' case $cc_basename in CC*) _LT_TAGVAR(archive_cmds, $1)='$CC -G $wl-h,$soname -o $lib $libobjs $deplibs $compiler_flags' _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -G $wl-Bexport:$export_symbols $wl-h,$soname -o $lib $libobjs $deplibs $compiler_flags' _LT_TAGVAR(old_archive_cmds, $1)='$CC -Tprelink_objects $oldobjs~ '"$_LT_TAGVAR(old_archive_cmds, $1)" _LT_TAGVAR(reload_cmds, $1)='$CC -Tprelink_objects $reload_objs~ '"$_LT_TAGVAR(reload_cmds, $1)" ;; *) _LT_TAGVAR(archive_cmds, $1)='$CC -shared $wl-h,$soname -o $lib $libobjs $deplibs $compiler_flags' _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -shared $wl-Bexport:$export_symbols $wl-h,$soname -o $lib $libobjs $deplibs $compiler_flags' ;; esac ;; tandem*) case $cc_basename in NCC*) # NonStop-UX NCC 3.20 # FIXME: insert proper C++ library support _LT_TAGVAR(ld_shlibs, $1)=no ;; *) # FIXME: insert proper C++ library support _LT_TAGVAR(ld_shlibs, $1)=no ;; esac ;; vxworks*) # FIXME: insert proper C++ library support _LT_TAGVAR(ld_shlibs, $1)=no ;; *) # FIXME: insert proper C++ library support _LT_TAGVAR(ld_shlibs, $1)=no ;; esac AC_MSG_RESULT([$_LT_TAGVAR(ld_shlibs, $1)]) test no = "$_LT_TAGVAR(ld_shlibs, $1)" && can_build_shared=no _LT_TAGVAR(GCC, $1)=$GXX _LT_TAGVAR(LD, $1)=$LD ## CAVEAT EMPTOR: ## There is no encapsulation within the following macros, do not change ## the running order or otherwise move them around unless you know exactly ## what you are doing... _LT_SYS_HIDDEN_LIBDEPS($1) _LT_COMPILER_PIC($1) _LT_COMPILER_C_O($1) _LT_COMPILER_FILE_LOCKS($1) _LT_LINKER_SHLIBS($1) _LT_SYS_DYNAMIC_LINKER($1) _LT_LINKER_HARDCODE_LIBPATH($1) _LT_CONFIG($1) fi # test -n "$compiler" CC=$lt_save_CC CFLAGS=$lt_save_CFLAGS LDCXX=$LD LD=$lt_save_LD GCC=$lt_save_GCC with_gnu_ld=$lt_save_with_gnu_ld lt_cv_path_LDCXX=$lt_cv_path_LD lt_cv_path_LD=$lt_save_path_LD lt_cv_prog_gnu_ldcxx=$lt_cv_prog_gnu_ld lt_cv_prog_gnu_ld=$lt_save_with_gnu_ld fi # test yes != "$_lt_caught_CXX_error" AC_LANG_POP ])# _LT_LANG_CXX_CONFIG # _LT_FUNC_STRIPNAME_CNF # ---------------------- # func_stripname_cnf prefix suffix name # strip PREFIX and SUFFIX off of NAME. # PREFIX and SUFFIX must not contain globbing or regex special # characters, hashes, percent signs, but SUFFIX may contain a leading # dot (in which case that matches only a dot). # # This function is identical to the (non-XSI) version of func_stripname, # except this one can be used by m4 code that may be executed by configure, # rather than the libtool script. m4_defun([_LT_FUNC_STRIPNAME_CNF],[dnl AC_REQUIRE([_LT_DECL_SED]) AC_REQUIRE([_LT_PROG_ECHO_BACKSLASH]) func_stripname_cnf () { case @S|@2 in .*) func_stripname_result=`$ECHO "@S|@3" | $SED "s%^@S|@1%%; s%\\\\@S|@2\$%%"`;; *) func_stripname_result=`$ECHO "@S|@3" | $SED "s%^@S|@1%%; s%@S|@2\$%%"`;; esac } # func_stripname_cnf ])# _LT_FUNC_STRIPNAME_CNF # _LT_SYS_HIDDEN_LIBDEPS([TAGNAME]) # --------------------------------- # Figure out "hidden" library dependencies from verbose # compiler output when linking a shared library. # Parse the compiler output and extract the necessary # objects, libraries and library flags. m4_defun([_LT_SYS_HIDDEN_LIBDEPS], [m4_require([_LT_FILEUTILS_DEFAULTS])dnl AC_REQUIRE([_LT_FUNC_STRIPNAME_CNF])dnl # Dependencies to place before and after the object being linked: _LT_TAGVAR(predep_objects, $1)= _LT_TAGVAR(postdep_objects, $1)= _LT_TAGVAR(predeps, $1)= _LT_TAGVAR(postdeps, $1)= _LT_TAGVAR(compiler_lib_search_path, $1)= dnl we can't use the lt_simple_compile_test_code here, dnl because it contains code intended for an executable, dnl not a library. It's possible we should let each dnl tag define a new lt_????_link_test_code variable, dnl but it's only used here... m4_if([$1], [], [cat > conftest.$ac_ext <<_LT_EOF int a; void foo (void) { a = 0; } _LT_EOF ], [$1], [CXX], [cat > conftest.$ac_ext <<_LT_EOF class Foo { public: Foo (void) { a = 0; } private: int a; }; _LT_EOF ], [$1], [F77], [cat > conftest.$ac_ext <<_LT_EOF subroutine foo implicit none integer*4 a a=0 return end _LT_EOF ], [$1], [FC], [cat > conftest.$ac_ext <<_LT_EOF subroutine foo implicit none integer a a=0 return end _LT_EOF ], [$1], [GCJ], [cat > conftest.$ac_ext <<_LT_EOF public class foo { private int a; public void bar (void) { a = 0; } }; _LT_EOF ], [$1], [GO], [cat > conftest.$ac_ext <<_LT_EOF package foo func foo() { } _LT_EOF ]) _lt_libdeps_save_CFLAGS=$CFLAGS case "$CC $CFLAGS " in #( *\ -flto*\ *) CFLAGS="$CFLAGS -fno-lto" ;; *\ -fwhopr*\ *) CFLAGS="$CFLAGS -fno-whopr" ;; *\ -fuse-linker-plugin*\ *) CFLAGS="$CFLAGS -fno-use-linker-plugin" ;; esac dnl Parse the compiler output and extract the necessary dnl objects, libraries and library flags. if AC_TRY_EVAL(ac_compile); then # Parse the compiler output and extract the necessary # objects, libraries and library flags. # Sentinel used to keep track of whether or not we are before # the conftest object file. pre_test_object_deps_done=no for p in `eval "$output_verbose_link_cmd"`; do case $prev$p in -L* | -R* | -l*) # Some compilers place space between "-{L,R}" and the path. # Remove the space. if test x-L = "$p" || test x-R = "$p"; then prev=$p continue fi # Expand the sysroot to ease extracting the directories later. if test -z "$prev"; then case $p in -L*) func_stripname_cnf '-L' '' "$p"; prev=-L; p=$func_stripname_result ;; -R*) func_stripname_cnf '-R' '' "$p"; prev=-R; p=$func_stripname_result ;; -l*) func_stripname_cnf '-l' '' "$p"; prev=-l; p=$func_stripname_result ;; esac fi case $p in =*) func_stripname_cnf '=' '' "$p"; p=$lt_sysroot$func_stripname_result ;; esac if test no = "$pre_test_object_deps_done"; then case $prev in -L | -R) # Internal compiler library paths should come after those # provided the user. The postdeps already come after the # user supplied libs so there is no need to process them. if test -z "$_LT_TAGVAR(compiler_lib_search_path, $1)"; then _LT_TAGVAR(compiler_lib_search_path, $1)=$prev$p else _LT_TAGVAR(compiler_lib_search_path, $1)="${_LT_TAGVAR(compiler_lib_search_path, $1)} $prev$p" fi ;; # The "-l" case would never come before the object being # linked, so don't bother handling this case. esac else if test -z "$_LT_TAGVAR(postdeps, $1)"; then _LT_TAGVAR(postdeps, $1)=$prev$p else _LT_TAGVAR(postdeps, $1)="${_LT_TAGVAR(postdeps, $1)} $prev$p" fi fi prev= ;; *.lto.$objext) ;; # Ignore GCC LTO objects *.$objext) # This assumes that the test object file only shows up # once in the compiler output. if test "$p" = "conftest.$objext"; then pre_test_object_deps_done=yes continue fi if test no = "$pre_test_object_deps_done"; then if test -z "$_LT_TAGVAR(predep_objects, $1)"; then _LT_TAGVAR(predep_objects, $1)=$p else _LT_TAGVAR(predep_objects, $1)="$_LT_TAGVAR(predep_objects, $1) $p" fi else if test -z "$_LT_TAGVAR(postdep_objects, $1)"; then _LT_TAGVAR(postdep_objects, $1)=$p else _LT_TAGVAR(postdep_objects, $1)="$_LT_TAGVAR(postdep_objects, $1) $p" fi fi ;; *) ;; # Ignore the rest. esac done # Clean up. rm -f a.out a.exe else echo "libtool.m4: error: problem compiling $1 test program" fi $RM -f confest.$objext CFLAGS=$_lt_libdeps_save_CFLAGS # PORTME: override above test on systems where it is broken m4_if([$1], [CXX], [case $host_os in interix[[3-9]]*) # Interix 3.5 installs completely hosed .la files for C++, so rather than # hack all around it, let's just trust "g++" to DTRT. _LT_TAGVAR(predep_objects,$1)= _LT_TAGVAR(postdep_objects,$1)= _LT_TAGVAR(postdeps,$1)= ;; esac ]) case " $_LT_TAGVAR(postdeps, $1) " in *" -lc "*) _LT_TAGVAR(archive_cmds_need_lc, $1)=no ;; esac _LT_TAGVAR(compiler_lib_search_dirs, $1)= if test -n "${_LT_TAGVAR(compiler_lib_search_path, $1)}"; then _LT_TAGVAR(compiler_lib_search_dirs, $1)=`echo " ${_LT_TAGVAR(compiler_lib_search_path, $1)}" | $SED -e 's! -L! !g' -e 's!^ !!'` fi _LT_TAGDECL([], [compiler_lib_search_dirs], [1], [The directories searched by this compiler when creating a shared library]) _LT_TAGDECL([], [predep_objects], [1], [Dependencies to place before and after the objects being linked to create a shared library]) _LT_TAGDECL([], [postdep_objects], [1]) _LT_TAGDECL([], [predeps], [1]) _LT_TAGDECL([], [postdeps], [1]) _LT_TAGDECL([], [compiler_lib_search_path], [1], [The library search path used internally by the compiler when linking a shared library]) ])# _LT_SYS_HIDDEN_LIBDEPS # _LT_LANG_F77_CONFIG([TAG]) # -------------------------- # Ensure that the configuration variables for a Fortran 77 compiler are # suitably defined. These variables are subsequently used by _LT_CONFIG # to write the compiler configuration to 'libtool'. m4_defun([_LT_LANG_F77_CONFIG], [AC_LANG_PUSH(Fortran 77) if test -z "$F77" || test no = "$F77"; then _lt_disable_F77=yes fi _LT_TAGVAR(archive_cmds_need_lc, $1)=no _LT_TAGVAR(allow_undefined_flag, $1)= _LT_TAGVAR(always_export_symbols, $1)=no _LT_TAGVAR(archive_expsym_cmds, $1)= _LT_TAGVAR(export_dynamic_flag_spec, $1)= _LT_TAGVAR(hardcode_direct, $1)=no _LT_TAGVAR(hardcode_direct_absolute, $1)=no _LT_TAGVAR(hardcode_libdir_flag_spec, $1)= _LT_TAGVAR(hardcode_libdir_separator, $1)= _LT_TAGVAR(hardcode_minus_L, $1)=no _LT_TAGVAR(hardcode_automatic, $1)=no _LT_TAGVAR(inherit_rpath, $1)=no _LT_TAGVAR(module_cmds, $1)= _LT_TAGVAR(module_expsym_cmds, $1)= _LT_TAGVAR(link_all_deplibs, $1)=unknown _LT_TAGVAR(old_archive_cmds, $1)=$old_archive_cmds _LT_TAGVAR(reload_flag, $1)=$reload_flag _LT_TAGVAR(reload_cmds, $1)=$reload_cmds _LT_TAGVAR(no_undefined_flag, $1)= _LT_TAGVAR(whole_archive_flag_spec, $1)= _LT_TAGVAR(enable_shared_with_static_runtimes, $1)=no # Source file extension for f77 test sources. ac_ext=f # Object file extension for compiled f77 test sources. objext=o _LT_TAGVAR(objext, $1)=$objext # No sense in running all these tests if we already determined that # the F77 compiler isn't working. Some variables (like enable_shared) # are currently assumed to apply to all compilers on this platform, # and will be corrupted by setting them based on a non-working compiler. if test yes != "$_lt_disable_F77"; then # Code to be used in simple compile tests lt_simple_compile_test_code="\ subroutine t return end " # Code to be used in simple link tests lt_simple_link_test_code="\ program t end " # ltmain only uses $CC for tagged configurations so make sure $CC is set. _LT_TAG_COMPILER # save warnings/boilerplate of simple test code _LT_COMPILER_BOILERPLATE _LT_LINKER_BOILERPLATE # Allow CC to be a program name with arguments. lt_save_CC=$CC lt_save_GCC=$GCC lt_save_CFLAGS=$CFLAGS CC=${F77-"f77"} CFLAGS=$FFLAGS compiler=$CC _LT_TAGVAR(compiler, $1)=$CC _LT_CC_BASENAME([$compiler]) GCC=$G77 if test -n "$compiler"; then AC_MSG_CHECKING([if libtool supports shared libraries]) AC_MSG_RESULT([$can_build_shared]) AC_MSG_CHECKING([whether to build shared libraries]) test no = "$can_build_shared" && enable_shared=no # On AIX, shared libraries and static libraries use the same namespace, and # are all built from PIC. case $host_os in aix3*) test yes = "$enable_shared" && enable_static=no if test -n "$RANLIB"; then archive_cmds="$archive_cmds~\$RANLIB \$lib" postinstall_cmds='$RANLIB $lib' fi ;; aix[[4-9]]*) if test ia64 != "$host_cpu"; then case $enable_shared,$with_aix_soname,$aix_use_runtimelinking in yes,aix,yes) ;; # shared object as lib.so file only yes,svr4,*) ;; # shared object as lib.so archive member only yes,*) enable_static=no ;; # shared object in lib.a archive as well esac fi ;; esac AC_MSG_RESULT([$enable_shared]) AC_MSG_CHECKING([whether to build static libraries]) # Make sure either enable_shared or enable_static is yes. test yes = "$enable_shared" || enable_static=yes AC_MSG_RESULT([$enable_static]) _LT_TAGVAR(GCC, $1)=$G77 _LT_TAGVAR(LD, $1)=$LD ## CAVEAT EMPTOR: ## There is no encapsulation within the following macros, do not change ## the running order or otherwise move them around unless you know exactly ## what you are doing... _LT_COMPILER_PIC($1) _LT_COMPILER_C_O($1) _LT_COMPILER_FILE_LOCKS($1) _LT_LINKER_SHLIBS($1) _LT_SYS_DYNAMIC_LINKER($1) _LT_LINKER_HARDCODE_LIBPATH($1) _LT_CONFIG($1) fi # test -n "$compiler" GCC=$lt_save_GCC CC=$lt_save_CC CFLAGS=$lt_save_CFLAGS fi # test yes != "$_lt_disable_F77" AC_LANG_POP ])# _LT_LANG_F77_CONFIG # _LT_LANG_FC_CONFIG([TAG]) # ------------------------- # Ensure that the configuration variables for a Fortran compiler are # suitably defined. These variables are subsequently used by _LT_CONFIG # to write the compiler configuration to 'libtool'. m4_defun([_LT_LANG_FC_CONFIG], [AC_LANG_PUSH(Fortran) if test -z "$FC" || test no = "$FC"; then _lt_disable_FC=yes fi _LT_TAGVAR(archive_cmds_need_lc, $1)=no _LT_TAGVAR(allow_undefined_flag, $1)= _LT_TAGVAR(always_export_symbols, $1)=no _LT_TAGVAR(archive_expsym_cmds, $1)= _LT_TAGVAR(export_dynamic_flag_spec, $1)= _LT_TAGVAR(hardcode_direct, $1)=no _LT_TAGVAR(hardcode_direct_absolute, $1)=no _LT_TAGVAR(hardcode_libdir_flag_spec, $1)= _LT_TAGVAR(hardcode_libdir_separator, $1)= _LT_TAGVAR(hardcode_minus_L, $1)=no _LT_TAGVAR(hardcode_automatic, $1)=no _LT_TAGVAR(inherit_rpath, $1)=no _LT_TAGVAR(module_cmds, $1)= _LT_TAGVAR(module_expsym_cmds, $1)= _LT_TAGVAR(link_all_deplibs, $1)=unknown _LT_TAGVAR(old_archive_cmds, $1)=$old_archive_cmds _LT_TAGVAR(reload_flag, $1)=$reload_flag _LT_TAGVAR(reload_cmds, $1)=$reload_cmds _LT_TAGVAR(no_undefined_flag, $1)= _LT_TAGVAR(whole_archive_flag_spec, $1)= _LT_TAGVAR(enable_shared_with_static_runtimes, $1)=no # Source file extension for fc test sources. ac_ext=${ac_fc_srcext-f} # Object file extension for compiled fc test sources. objext=o _LT_TAGVAR(objext, $1)=$objext # No sense in running all these tests if we already determined that # the FC compiler isn't working. Some variables (like enable_shared) # are currently assumed to apply to all compilers on this platform, # and will be corrupted by setting them based on a non-working compiler. if test yes != "$_lt_disable_FC"; then # Code to be used in simple compile tests lt_simple_compile_test_code="\ subroutine t return end " # Code to be used in simple link tests lt_simple_link_test_code="\ program t end " # ltmain only uses $CC for tagged configurations so make sure $CC is set. _LT_TAG_COMPILER # save warnings/boilerplate of simple test code _LT_COMPILER_BOILERPLATE _LT_LINKER_BOILERPLATE # Allow CC to be a program name with arguments. lt_save_CC=$CC lt_save_GCC=$GCC lt_save_CFLAGS=$CFLAGS CC=${FC-"f95"} CFLAGS=$FCFLAGS compiler=$CC GCC=$ac_cv_fc_compiler_gnu _LT_TAGVAR(compiler, $1)=$CC _LT_CC_BASENAME([$compiler]) if test -n "$compiler"; then AC_MSG_CHECKING([if libtool supports shared libraries]) AC_MSG_RESULT([$can_build_shared]) AC_MSG_CHECKING([whether to build shared libraries]) test no = "$can_build_shared" && enable_shared=no # On AIX, shared libraries and static libraries use the same namespace, and # are all built from PIC. case $host_os in aix3*) test yes = "$enable_shared" && enable_static=no if test -n "$RANLIB"; then archive_cmds="$archive_cmds~\$RANLIB \$lib" postinstall_cmds='$RANLIB $lib' fi ;; aix[[4-9]]*) if test ia64 != "$host_cpu"; then case $enable_shared,$with_aix_soname,$aix_use_runtimelinking in yes,aix,yes) ;; # shared object as lib.so file only yes,svr4,*) ;; # shared object as lib.so archive member only yes,*) enable_static=no ;; # shared object in lib.a archive as well esac fi ;; esac AC_MSG_RESULT([$enable_shared]) AC_MSG_CHECKING([whether to build static libraries]) # Make sure either enable_shared or enable_static is yes. test yes = "$enable_shared" || enable_static=yes AC_MSG_RESULT([$enable_static]) _LT_TAGVAR(GCC, $1)=$ac_cv_fc_compiler_gnu _LT_TAGVAR(LD, $1)=$LD ## CAVEAT EMPTOR: ## There is no encapsulation within the following macros, do not change ## the running order or otherwise move them around unless you know exactly ## what you are doing... _LT_SYS_HIDDEN_LIBDEPS($1) _LT_COMPILER_PIC($1) _LT_COMPILER_C_O($1) _LT_COMPILER_FILE_LOCKS($1) _LT_LINKER_SHLIBS($1) _LT_SYS_DYNAMIC_LINKER($1) _LT_LINKER_HARDCODE_LIBPATH($1) _LT_CONFIG($1) fi # test -n "$compiler" GCC=$lt_save_GCC CC=$lt_save_CC CFLAGS=$lt_save_CFLAGS fi # test yes != "$_lt_disable_FC" AC_LANG_POP ])# _LT_LANG_FC_CONFIG # _LT_LANG_GCJ_CONFIG([TAG]) # -------------------------- # Ensure that the configuration variables for the GNU Java Compiler compiler # are suitably defined. These variables are subsequently used by _LT_CONFIG # to write the compiler configuration to 'libtool'. m4_defun([_LT_LANG_GCJ_CONFIG], [AC_REQUIRE([LT_PROG_GCJ])dnl AC_LANG_SAVE # Source file extension for Java test sources. ac_ext=java # Object file extension for compiled Java test sources. objext=o _LT_TAGVAR(objext, $1)=$objext # Code to be used in simple compile tests lt_simple_compile_test_code="class foo {}" # Code to be used in simple link tests lt_simple_link_test_code='public class conftest { public static void main(String[[]] argv) {}; }' # ltmain only uses $CC for tagged configurations so make sure $CC is set. _LT_TAG_COMPILER # save warnings/boilerplate of simple test code _LT_COMPILER_BOILERPLATE _LT_LINKER_BOILERPLATE # Allow CC to be a program name with arguments. lt_save_CC=$CC lt_save_CFLAGS=$CFLAGS lt_save_GCC=$GCC GCC=yes CC=${GCJ-"gcj"} CFLAGS=$GCJFLAGS compiler=$CC _LT_TAGVAR(compiler, $1)=$CC _LT_TAGVAR(LD, $1)=$LD _LT_CC_BASENAME([$compiler]) # GCJ did not exist at the time GCC didn't implicitly link libc in. _LT_TAGVAR(archive_cmds_need_lc, $1)=no _LT_TAGVAR(old_archive_cmds, $1)=$old_archive_cmds _LT_TAGVAR(reload_flag, $1)=$reload_flag _LT_TAGVAR(reload_cmds, $1)=$reload_cmds if test -n "$compiler"; then _LT_COMPILER_NO_RTTI($1) _LT_COMPILER_PIC($1) _LT_COMPILER_C_O($1) _LT_COMPILER_FILE_LOCKS($1) _LT_LINKER_SHLIBS($1) _LT_LINKER_HARDCODE_LIBPATH($1) _LT_CONFIG($1) fi AC_LANG_RESTORE GCC=$lt_save_GCC CC=$lt_save_CC CFLAGS=$lt_save_CFLAGS ])# _LT_LANG_GCJ_CONFIG # _LT_LANG_GO_CONFIG([TAG]) # -------------------------- # Ensure that the configuration variables for the GNU Go compiler # are suitably defined. These variables are subsequently used by _LT_CONFIG # to write the compiler configuration to 'libtool'. m4_defun([_LT_LANG_GO_CONFIG], [AC_REQUIRE([LT_PROG_GO])dnl AC_LANG_SAVE # Source file extension for Go test sources. ac_ext=go # Object file extension for compiled Go test sources. objext=o _LT_TAGVAR(objext, $1)=$objext # Code to be used in simple compile tests lt_simple_compile_test_code="package main; func main() { }" # Code to be used in simple link tests lt_simple_link_test_code='package main; func main() { }' # ltmain only uses $CC for tagged configurations so make sure $CC is set. _LT_TAG_COMPILER # save warnings/boilerplate of simple test code _LT_COMPILER_BOILERPLATE _LT_LINKER_BOILERPLATE # Allow CC to be a program name with arguments. lt_save_CC=$CC lt_save_CFLAGS=$CFLAGS lt_save_GCC=$GCC GCC=yes CC=${GOC-"gccgo"} CFLAGS=$GOFLAGS compiler=$CC _LT_TAGVAR(compiler, $1)=$CC _LT_TAGVAR(LD, $1)=$LD _LT_CC_BASENAME([$compiler]) # Go did not exist at the time GCC didn't implicitly link libc in. _LT_TAGVAR(archive_cmds_need_lc, $1)=no _LT_TAGVAR(old_archive_cmds, $1)=$old_archive_cmds _LT_TAGVAR(reload_flag, $1)=$reload_flag _LT_TAGVAR(reload_cmds, $1)=$reload_cmds if test -n "$compiler"; then _LT_COMPILER_NO_RTTI($1) _LT_COMPILER_PIC($1) _LT_COMPILER_C_O($1) _LT_COMPILER_FILE_LOCKS($1) _LT_LINKER_SHLIBS($1) _LT_LINKER_HARDCODE_LIBPATH($1) _LT_CONFIG($1) fi AC_LANG_RESTORE GCC=$lt_save_GCC CC=$lt_save_CC CFLAGS=$lt_save_CFLAGS ])# _LT_LANG_GO_CONFIG # _LT_LANG_RC_CONFIG([TAG]) # ------------------------- # Ensure that the configuration variables for the Windows resource compiler # are suitably defined. These variables are subsequently used by _LT_CONFIG # to write the compiler configuration to 'libtool'. m4_defun([_LT_LANG_RC_CONFIG], [AC_REQUIRE([LT_PROG_RC])dnl AC_LANG_SAVE # Source file extension for RC test sources. ac_ext=rc # Object file extension for compiled RC test sources. objext=o _LT_TAGVAR(objext, $1)=$objext # Code to be used in simple compile tests lt_simple_compile_test_code='sample MENU { MENUITEM "&Soup", 100, CHECKED }' # Code to be used in simple link tests lt_simple_link_test_code=$lt_simple_compile_test_code # ltmain only uses $CC for tagged configurations so make sure $CC is set. _LT_TAG_COMPILER # save warnings/boilerplate of simple test code _LT_COMPILER_BOILERPLATE _LT_LINKER_BOILERPLATE # Allow CC to be a program name with arguments. lt_save_CC=$CC lt_save_CFLAGS=$CFLAGS lt_save_GCC=$GCC GCC= CC=${RC-"windres"} CFLAGS= compiler=$CC _LT_TAGVAR(compiler, $1)=$CC _LT_CC_BASENAME([$compiler]) _LT_TAGVAR(lt_cv_prog_compiler_c_o, $1)=yes if test -n "$compiler"; then : _LT_CONFIG($1) fi GCC=$lt_save_GCC AC_LANG_RESTORE CC=$lt_save_CC CFLAGS=$lt_save_CFLAGS ])# _LT_LANG_RC_CONFIG # LT_PROG_GCJ # ----------- AC_DEFUN([LT_PROG_GCJ], [m4_ifdef([AC_PROG_GCJ], [AC_PROG_GCJ], [m4_ifdef([A][M_PROG_GCJ], [A][M_PROG_GCJ], [AC_CHECK_TOOL(GCJ, gcj,) test set = "${GCJFLAGS+set}" || GCJFLAGS="-g -O2" AC_SUBST(GCJFLAGS)])])[]dnl ]) # Old name: AU_ALIAS([LT_AC_PROG_GCJ], [LT_PROG_GCJ]) dnl aclocal-1.4 backwards compatibility: dnl AC_DEFUN([LT_AC_PROG_GCJ], []) # LT_PROG_GO # ---------- AC_DEFUN([LT_PROG_GO], [AC_CHECK_TOOL(GOC, gccgo,) ]) # LT_PROG_RC # ---------- AC_DEFUN([LT_PROG_RC], [AC_CHECK_TOOL(RC, windres,) ]) # Old name: AU_ALIAS([LT_AC_PROG_RC], [LT_PROG_RC]) dnl aclocal-1.4 backwards compatibility: dnl AC_DEFUN([LT_AC_PROG_RC], []) # _LT_DECL_EGREP # -------------- # If we don't have a new enough Autoconf to choose the best grep # available, choose the one first in the user's PATH. m4_defun([_LT_DECL_EGREP], [AC_REQUIRE([AC_PROG_EGREP])dnl AC_REQUIRE([AC_PROG_FGREP])dnl test -z "$GREP" && GREP=grep _LT_DECL([], [GREP], [1], [A grep program that handles long lines]) _LT_DECL([], [EGREP], [1], [An ERE matcher]) _LT_DECL([], [FGREP], [1], [A literal string matcher]) dnl Non-bleeding-edge autoconf doesn't subst GREP, so do it here too AC_SUBST([GREP]) ]) # _LT_DECL_OBJDUMP # -------------- # If we don't have a new enough Autoconf to choose the best objdump # available, choose the one first in the user's PATH. m4_defun([_LT_DECL_OBJDUMP], [AC_CHECK_TOOL(OBJDUMP, objdump, false) test -z "$OBJDUMP" && OBJDUMP=objdump _LT_DECL([], [OBJDUMP], [1], [An object symbol dumper]) AC_SUBST([OBJDUMP]) ]) # _LT_DECL_DLLTOOL # ---------------- # Ensure DLLTOOL variable is set. m4_defun([_LT_DECL_DLLTOOL], [AC_CHECK_TOOL(DLLTOOL, dlltool, false) test -z "$DLLTOOL" && DLLTOOL=dlltool _LT_DECL([], [DLLTOOL], [1], [DLL creation program]) AC_SUBST([DLLTOOL]) ]) # _LT_DECL_FILECMD # ---------------- # Check for a file(cmd) program that can be used to detect file type and magic m4_defun([_LT_DECL_FILECMD], [AC_CHECK_TOOL([FILECMD], [file], [:]) _LT_DECL([], [FILECMD], [1], [A file(cmd) program that detects file types]) ])# _LD_DECL_FILECMD # _LT_DECL_SED # ------------ # Check for a fully-functional sed program, that truncates # as few characters as possible. Prefer GNU sed if found. m4_defun([_LT_DECL_SED], [AC_PROG_SED test -z "$SED" && SED=sed Xsed="$SED -e 1s/^X//" _LT_DECL([], [SED], [1], [A sed program that does not truncate output]) _LT_DECL([], [Xsed], ["\$SED -e 1s/^X//"], [Sed that helps us avoid accidentally triggering echo(1) options like -n]) ])# _LT_DECL_SED m4_ifndef([AC_PROG_SED], [ # NOTE: This macro has been submitted for inclusion into # # GNU Autoconf as AC_PROG_SED. When it is available in # # a released version of Autoconf we should remove this # # macro and use it instead. # m4_defun([AC_PROG_SED], [AC_MSG_CHECKING([for a sed that does not truncate output]) AC_CACHE_VAL(lt_cv_path_SED, [# Loop through the user's path and test for sed and gsed. # Then use that list of sed's as ones to test for truncation. as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for lt_ac_prog in sed gsed; do for ac_exec_ext in '' $ac_executable_extensions; do if $as_executable_p "$as_dir/$lt_ac_prog$ac_exec_ext"; then lt_ac_sed_list="$lt_ac_sed_list $as_dir/$lt_ac_prog$ac_exec_ext" fi done done done IFS=$as_save_IFS lt_ac_max=0 lt_ac_count=0 # Add /usr/xpg4/bin/sed as it is typically found on Solaris # along with /bin/sed that truncates output. for lt_ac_sed in $lt_ac_sed_list /usr/xpg4/bin/sed; do test ! -f "$lt_ac_sed" && continue cat /dev/null > conftest.in lt_ac_count=0 echo $ECHO_N "0123456789$ECHO_C" >conftest.in # Check for GNU sed and select it if it is found. if "$lt_ac_sed" --version 2>&1 < /dev/null | grep 'GNU' > /dev/null; then lt_cv_path_SED=$lt_ac_sed break fi while true; do cat conftest.in conftest.in >conftest.tmp mv conftest.tmp conftest.in cp conftest.in conftest.nl echo >>conftest.nl $lt_ac_sed -e 's/a$//' < conftest.nl >conftest.out || break cmp -s conftest.out conftest.nl || break # 10000 chars as input seems more than enough test 10 -lt "$lt_ac_count" && break lt_ac_count=`expr $lt_ac_count + 1` if test "$lt_ac_count" -gt "$lt_ac_max"; then lt_ac_max=$lt_ac_count lt_cv_path_SED=$lt_ac_sed fi done done ]) SED=$lt_cv_path_SED AC_SUBST([SED]) AC_MSG_RESULT([$SED]) ])#AC_PROG_SED ])#m4_ifndef # Old name: AU_ALIAS([LT_AC_PROG_SED], [AC_PROG_SED]) dnl aclocal-1.4 backwards compatibility: dnl AC_DEFUN([LT_AC_PROG_SED], []) # _LT_CHECK_SHELL_FEATURES # ------------------------ # Find out whether the shell is Bourne or XSI compatible, # or has some other useful features. m4_defun([_LT_CHECK_SHELL_FEATURES], [if ( (MAIL=60; unset MAIL) || exit) >/dev/null 2>&1; then lt_unset=unset else lt_unset=false fi _LT_DECL([], [lt_unset], [0], [whether the shell understands "unset"])dnl # test EBCDIC or ASCII case `echo X|tr X '\101'` in A) # ASCII based system # \n is not interpreted correctly by Solaris 8 /usr/ucb/tr lt_SP2NL='tr \040 \012' lt_NL2SP='tr \015\012 \040\040' ;; *) # EBCDIC based system lt_SP2NL='tr \100 \n' lt_NL2SP='tr \r\n \100\100' ;; esac _LT_DECL([SP2NL], [lt_SP2NL], [1], [turn spaces into newlines])dnl _LT_DECL([NL2SP], [lt_NL2SP], [1], [turn newlines into spaces])dnl ])# _LT_CHECK_SHELL_FEATURES # _LT_PATH_CONVERSION_FUNCTIONS # ----------------------------- # Determine what file name conversion functions should be used by # func_to_host_file (and, implicitly, by func_to_host_path). These are needed # for certain cross-compile configurations and native mingw. m4_defun([_LT_PATH_CONVERSION_FUNCTIONS], [AC_REQUIRE([AC_CANONICAL_HOST])dnl AC_REQUIRE([AC_CANONICAL_BUILD])dnl AC_MSG_CHECKING([how to convert $build file names to $host format]) AC_CACHE_VAL(lt_cv_to_host_file_cmd, [case $host in *-*-mingw* ) case $build in *-*-mingw* ) # actually msys lt_cv_to_host_file_cmd=func_convert_file_msys_to_w32 ;; *-*-cygwin* ) lt_cv_to_host_file_cmd=func_convert_file_cygwin_to_w32 ;; * ) # otherwise, assume *nix lt_cv_to_host_file_cmd=func_convert_file_nix_to_w32 ;; esac ;; *-*-cygwin* ) case $build in *-*-mingw* ) # actually msys lt_cv_to_host_file_cmd=func_convert_file_msys_to_cygwin ;; *-*-cygwin* ) lt_cv_to_host_file_cmd=func_convert_file_noop ;; * ) # otherwise, assume *nix lt_cv_to_host_file_cmd=func_convert_file_nix_to_cygwin ;; esac ;; * ) # unhandled hosts (and "normal" native builds) lt_cv_to_host_file_cmd=func_convert_file_noop ;; esac ]) to_host_file_cmd=$lt_cv_to_host_file_cmd AC_MSG_RESULT([$lt_cv_to_host_file_cmd]) _LT_DECL([to_host_file_cmd], [lt_cv_to_host_file_cmd], [0], [convert $build file names to $host format])dnl AC_MSG_CHECKING([how to convert $build file names to toolchain format]) AC_CACHE_VAL(lt_cv_to_tool_file_cmd, [#assume ordinary cross tools, or native build. lt_cv_to_tool_file_cmd=func_convert_file_noop case $host in *-*-mingw* ) case $build in *-*-mingw* ) # actually msys lt_cv_to_tool_file_cmd=func_convert_file_msys_to_w32 ;; esac ;; esac ]) to_tool_file_cmd=$lt_cv_to_tool_file_cmd AC_MSG_RESULT([$lt_cv_to_tool_file_cmd]) _LT_DECL([to_tool_file_cmd], [lt_cv_to_tool_file_cmd], [0], [convert $build files to toolchain format])dnl ])# _LT_PATH_CONVERSION_FUNCTIONS # Helper functions for option handling. -*- Autoconf -*- # # Copyright (C) 2004-2005, 2007-2009, 2011-2019, 2021-2022 Free # Software Foundation, Inc. # Written by Gary V. Vaughan, 2004 # # This file is free software; the Free Software Foundation gives # unlimited permission to copy and/or distribute it, with or without # modifications, as long as this notice is preserved. # serial 8 ltoptions.m4 # This is to help aclocal find these macros, as it can't see m4_define. AC_DEFUN([LTOPTIONS_VERSION], [m4_if([1])]) # _LT_MANGLE_OPTION(MACRO-NAME, OPTION-NAME) # ------------------------------------------ m4_define([_LT_MANGLE_OPTION], [[_LT_OPTION_]m4_bpatsubst($1__$2, [[^a-zA-Z0-9_]], [_])]) # _LT_SET_OPTION(MACRO-NAME, OPTION-NAME) # --------------------------------------- # Set option OPTION-NAME for macro MACRO-NAME, and if there is a # matching handler defined, dispatch to it. Other OPTION-NAMEs are # saved as a flag. m4_define([_LT_SET_OPTION], [m4_define(_LT_MANGLE_OPTION([$1], [$2]))dnl m4_ifdef(_LT_MANGLE_DEFUN([$1], [$2]), _LT_MANGLE_DEFUN([$1], [$2]), [m4_warning([Unknown $1 option '$2'])])[]dnl ]) # _LT_IF_OPTION(MACRO-NAME, OPTION-NAME, IF-SET, [IF-NOT-SET]) # ------------------------------------------------------------ # Execute IF-SET if OPTION is set, IF-NOT-SET otherwise. m4_define([_LT_IF_OPTION], [m4_ifdef(_LT_MANGLE_OPTION([$1], [$2]), [$3], [$4])]) # _LT_UNLESS_OPTIONS(MACRO-NAME, OPTION-LIST, IF-NOT-SET) # ------------------------------------------------------- # Execute IF-NOT-SET unless all options in OPTION-LIST for MACRO-NAME # are set. m4_define([_LT_UNLESS_OPTIONS], [m4_foreach([_LT_Option], m4_split(m4_normalize([$2])), [m4_ifdef(_LT_MANGLE_OPTION([$1], _LT_Option), [m4_define([$0_found])])])[]dnl m4_ifdef([$0_found], [m4_undefine([$0_found])], [$3 ])[]dnl ]) # _LT_SET_OPTIONS(MACRO-NAME, OPTION-LIST) # ---------------------------------------- # OPTION-LIST is a space-separated list of Libtool options associated # with MACRO-NAME. If any OPTION has a matching handler declared with # LT_OPTION_DEFINE, dispatch to that macro; otherwise complain about # the unknown option and exit. m4_defun([_LT_SET_OPTIONS], [# Set options m4_foreach([_LT_Option], m4_split(m4_normalize([$2])), [_LT_SET_OPTION([$1], _LT_Option)]) m4_if([$1],[LT_INIT],[ dnl dnl Simply set some default values (i.e off) if boolean options were not dnl specified: _LT_UNLESS_OPTIONS([LT_INIT], [dlopen], [enable_dlopen=no ]) _LT_UNLESS_OPTIONS([LT_INIT], [win32-dll], [enable_win32_dll=no ]) dnl dnl If no reference was made to various pairs of opposing options, then dnl we run the default mode handler for the pair. For example, if neither dnl 'shared' nor 'disable-shared' was passed, we enable building of shared dnl archives by default: _LT_UNLESS_OPTIONS([LT_INIT], [shared disable-shared], [_LT_ENABLE_SHARED]) _LT_UNLESS_OPTIONS([LT_INIT], [static disable-static], [_LT_ENABLE_STATIC]) _LT_UNLESS_OPTIONS([LT_INIT], [pic-only no-pic], [_LT_WITH_PIC]) _LT_UNLESS_OPTIONS([LT_INIT], [fast-install disable-fast-install], [_LT_ENABLE_FAST_INSTALL]) _LT_UNLESS_OPTIONS([LT_INIT], [aix-soname=aix aix-soname=both aix-soname=svr4], [_LT_WITH_AIX_SONAME([aix])]) ]) ])# _LT_SET_OPTIONS # _LT_MANGLE_DEFUN(MACRO-NAME, OPTION-NAME) # ----------------------------------------- m4_define([_LT_MANGLE_DEFUN], [[_LT_OPTION_DEFUN_]m4_bpatsubst(m4_toupper([$1__$2]), [[^A-Z0-9_]], [_])]) # LT_OPTION_DEFINE(MACRO-NAME, OPTION-NAME, CODE) # ----------------------------------------------- m4_define([LT_OPTION_DEFINE], [m4_define(_LT_MANGLE_DEFUN([$1], [$2]), [$3])[]dnl ])# LT_OPTION_DEFINE # dlopen # ------ LT_OPTION_DEFINE([LT_INIT], [dlopen], [enable_dlopen=yes ]) AU_DEFUN([AC_LIBTOOL_DLOPEN], [_LT_SET_OPTION([LT_INIT], [dlopen]) AC_DIAGNOSE([obsolete], [$0: Remove this warning and the call to _LT_SET_OPTION when you put the 'dlopen' option into LT_INIT's first parameter.]) ]) dnl aclocal-1.4 backwards compatibility: dnl AC_DEFUN([AC_LIBTOOL_DLOPEN], []) # win32-dll # --------- # Declare package support for building win32 dll's. LT_OPTION_DEFINE([LT_INIT], [win32-dll], [enable_win32_dll=yes case $host in *-*-cygwin* | *-*-mingw* | *-*-pw32* | *-*-cegcc*) AC_CHECK_TOOL(AS, as, false) AC_CHECK_TOOL(DLLTOOL, dlltool, false) AC_CHECK_TOOL(OBJDUMP, objdump, false) ;; esac test -z "$AS" && AS=as _LT_DECL([], [AS], [1], [Assembler program])dnl test -z "$DLLTOOL" && DLLTOOL=dlltool _LT_DECL([], [DLLTOOL], [1], [DLL creation program])dnl test -z "$OBJDUMP" && OBJDUMP=objdump _LT_DECL([], [OBJDUMP], [1], [Object dumper program])dnl ])# win32-dll AU_DEFUN([AC_LIBTOOL_WIN32_DLL], [AC_REQUIRE([AC_CANONICAL_HOST])dnl _LT_SET_OPTION([LT_INIT], [win32-dll]) AC_DIAGNOSE([obsolete], [$0: Remove this warning and the call to _LT_SET_OPTION when you put the 'win32-dll' option into LT_INIT's first parameter.]) ]) dnl aclocal-1.4 backwards compatibility: dnl AC_DEFUN([AC_LIBTOOL_WIN32_DLL], []) # _LT_ENABLE_SHARED([DEFAULT]) # ---------------------------- # implement the --enable-shared flag, and supports the 'shared' and # 'disable-shared' LT_INIT options. # DEFAULT is either 'yes' or 'no'. If omitted, it defaults to 'yes'. m4_define([_LT_ENABLE_SHARED], [m4_define([_LT_ENABLE_SHARED_DEFAULT], [m4_if($1, no, no, yes)])dnl AC_ARG_ENABLE([shared], [AS_HELP_STRING([--enable-shared@<:@=PKGS@:>@], [build shared libraries @<:@default=]_LT_ENABLE_SHARED_DEFAULT[@:>@])], [p=${PACKAGE-default} case $enableval in yes) enable_shared=yes ;; no) enable_shared=no ;; *) enable_shared=no # Look at the argument we got. We use all the common list separators. lt_save_ifs=$IFS; IFS=$IFS$PATH_SEPARATOR, for pkg in $enableval; do IFS=$lt_save_ifs if test "X$pkg" = "X$p"; then enable_shared=yes fi done IFS=$lt_save_ifs ;; esac], [enable_shared=]_LT_ENABLE_SHARED_DEFAULT) _LT_DECL([build_libtool_libs], [enable_shared], [0], [Whether or not to build shared libraries]) ])# _LT_ENABLE_SHARED LT_OPTION_DEFINE([LT_INIT], [shared], [_LT_ENABLE_SHARED([yes])]) LT_OPTION_DEFINE([LT_INIT], [disable-shared], [_LT_ENABLE_SHARED([no])]) # Old names: AC_DEFUN([AC_ENABLE_SHARED], [_LT_SET_OPTION([LT_INIT], m4_if([$1], [no], [disable-])[shared]) ]) AC_DEFUN([AC_DISABLE_SHARED], [_LT_SET_OPTION([LT_INIT], [disable-shared]) ]) AU_DEFUN([AM_ENABLE_SHARED], [AC_ENABLE_SHARED($@)]) AU_DEFUN([AM_DISABLE_SHARED], [AC_DISABLE_SHARED($@)]) dnl aclocal-1.4 backwards compatibility: dnl AC_DEFUN([AM_ENABLE_SHARED], []) dnl AC_DEFUN([AM_DISABLE_SHARED], []) # _LT_ENABLE_STATIC([DEFAULT]) # ---------------------------- # implement the --enable-static flag, and support the 'static' and # 'disable-static' LT_INIT options. # DEFAULT is either 'yes' or 'no'. If omitted, it defaults to 'yes'. m4_define([_LT_ENABLE_STATIC], [m4_define([_LT_ENABLE_STATIC_DEFAULT], [m4_if($1, no, no, yes)])dnl AC_ARG_ENABLE([static], [AS_HELP_STRING([--enable-static@<:@=PKGS@:>@], [build static libraries @<:@default=]_LT_ENABLE_STATIC_DEFAULT[@:>@])], [p=${PACKAGE-default} case $enableval in yes) enable_static=yes ;; no) enable_static=no ;; *) enable_static=no # Look at the argument we got. We use all the common list separators. lt_save_ifs=$IFS; IFS=$IFS$PATH_SEPARATOR, for pkg in $enableval; do IFS=$lt_save_ifs if test "X$pkg" = "X$p"; then enable_static=yes fi done IFS=$lt_save_ifs ;; esac], [enable_static=]_LT_ENABLE_STATIC_DEFAULT) _LT_DECL([build_old_libs], [enable_static], [0], [Whether or not to build static libraries]) ])# _LT_ENABLE_STATIC LT_OPTION_DEFINE([LT_INIT], [static], [_LT_ENABLE_STATIC([yes])]) LT_OPTION_DEFINE([LT_INIT], [disable-static], [_LT_ENABLE_STATIC([no])]) # Old names: AC_DEFUN([AC_ENABLE_STATIC], [_LT_SET_OPTION([LT_INIT], m4_if([$1], [no], [disable-])[static]) ]) AC_DEFUN([AC_DISABLE_STATIC], [_LT_SET_OPTION([LT_INIT], [disable-static]) ]) AU_DEFUN([AM_ENABLE_STATIC], [AC_ENABLE_STATIC($@)]) AU_DEFUN([AM_DISABLE_STATIC], [AC_DISABLE_STATIC($@)]) dnl aclocal-1.4 backwards compatibility: dnl AC_DEFUN([AM_ENABLE_STATIC], []) dnl AC_DEFUN([AM_DISABLE_STATIC], []) # _LT_ENABLE_FAST_INSTALL([DEFAULT]) # ---------------------------------- # implement the --enable-fast-install flag, and support the 'fast-install' # and 'disable-fast-install' LT_INIT options. # DEFAULT is either 'yes' or 'no'. If omitted, it defaults to 'yes'. m4_define([_LT_ENABLE_FAST_INSTALL], [m4_define([_LT_ENABLE_FAST_INSTALL_DEFAULT], [m4_if($1, no, no, yes)])dnl AC_ARG_ENABLE([fast-install], [AS_HELP_STRING([--enable-fast-install@<:@=PKGS@:>@], [optimize for fast installation @<:@default=]_LT_ENABLE_FAST_INSTALL_DEFAULT[@:>@])], [p=${PACKAGE-default} case $enableval in yes) enable_fast_install=yes ;; no) enable_fast_install=no ;; *) enable_fast_install=no # Look at the argument we got. We use all the common list separators. lt_save_ifs=$IFS; IFS=$IFS$PATH_SEPARATOR, for pkg in $enableval; do IFS=$lt_save_ifs if test "X$pkg" = "X$p"; then enable_fast_install=yes fi done IFS=$lt_save_ifs ;; esac], [enable_fast_install=]_LT_ENABLE_FAST_INSTALL_DEFAULT) _LT_DECL([fast_install], [enable_fast_install], [0], [Whether or not to optimize for fast installation])dnl ])# _LT_ENABLE_FAST_INSTALL LT_OPTION_DEFINE([LT_INIT], [fast-install], [_LT_ENABLE_FAST_INSTALL([yes])]) LT_OPTION_DEFINE([LT_INIT], [disable-fast-install], [_LT_ENABLE_FAST_INSTALL([no])]) # Old names: AU_DEFUN([AC_ENABLE_FAST_INSTALL], [_LT_SET_OPTION([LT_INIT], m4_if([$1], [no], [disable-])[fast-install]) AC_DIAGNOSE([obsolete], [$0: Remove this warning and the call to _LT_SET_OPTION when you put the 'fast-install' option into LT_INIT's first parameter.]) ]) AU_DEFUN([AC_DISABLE_FAST_INSTALL], [_LT_SET_OPTION([LT_INIT], [disable-fast-install]) AC_DIAGNOSE([obsolete], [$0: Remove this warning and the call to _LT_SET_OPTION when you put the 'disable-fast-install' option into LT_INIT's first parameter.]) ]) dnl aclocal-1.4 backwards compatibility: dnl AC_DEFUN([AC_ENABLE_FAST_INSTALL], []) dnl AC_DEFUN([AM_DISABLE_FAST_INSTALL], []) # _LT_WITH_AIX_SONAME([DEFAULT]) # ---------------------------------- # implement the --with-aix-soname flag, and support the `aix-soname=aix' # and `aix-soname=both' and `aix-soname=svr4' LT_INIT options. DEFAULT # is either `aix', `both' or `svr4'. If omitted, it defaults to `aix'. m4_define([_LT_WITH_AIX_SONAME], [m4_define([_LT_WITH_AIX_SONAME_DEFAULT], [m4_if($1, svr4, svr4, m4_if($1, both, both, aix))])dnl shared_archive_member_spec= case $host,$enable_shared in power*-*-aix[[5-9]]*,yes) AC_MSG_CHECKING([which variant of shared library versioning to provide]) AC_ARG_WITH([aix-soname], [AS_HELP_STRING([--with-aix-soname=aix|svr4|both], [shared library versioning (aka "SONAME") variant to provide on AIX, @<:@default=]_LT_WITH_AIX_SONAME_DEFAULT[@:>@.])], [case $withval in aix|svr4|both) ;; *) AC_MSG_ERROR([Unknown argument to --with-aix-soname]) ;; esac lt_cv_with_aix_soname=$with_aix_soname], [AC_CACHE_VAL([lt_cv_with_aix_soname], [lt_cv_with_aix_soname=]_LT_WITH_AIX_SONAME_DEFAULT) with_aix_soname=$lt_cv_with_aix_soname]) AC_MSG_RESULT([$with_aix_soname]) if test aix != "$with_aix_soname"; then # For the AIX way of multilib, we name the shared archive member # based on the bitwidth used, traditionally 'shr.o' or 'shr_64.o', # and 'shr.imp' or 'shr_64.imp', respectively, for the Import File. # Even when GNU compilers ignore OBJECT_MODE but need '-maix64' flag, # the AIX toolchain works better with OBJECT_MODE set (default 32). if test 64 = "${OBJECT_MODE-32}"; then shared_archive_member_spec=shr_64 else shared_archive_member_spec=shr fi fi ;; *) with_aix_soname=aix ;; esac _LT_DECL([], [shared_archive_member_spec], [0], [Shared archive member basename, for filename based shared library versioning on AIX])dnl ])# _LT_WITH_AIX_SONAME LT_OPTION_DEFINE([LT_INIT], [aix-soname=aix], [_LT_WITH_AIX_SONAME([aix])]) LT_OPTION_DEFINE([LT_INIT], [aix-soname=both], [_LT_WITH_AIX_SONAME([both])]) LT_OPTION_DEFINE([LT_INIT], [aix-soname=svr4], [_LT_WITH_AIX_SONAME([svr4])]) # _LT_WITH_PIC([MODE]) # -------------------- # implement the --with-pic flag, and support the 'pic-only' and 'no-pic' # LT_INIT options. # MODE is either 'yes' or 'no'. If omitted, it defaults to 'both'. m4_define([_LT_WITH_PIC], [AC_ARG_WITH([pic], [AS_HELP_STRING([--with-pic@<:@=PKGS@:>@], [try to use only PIC/non-PIC objects @<:@default=use both@:>@])], [lt_p=${PACKAGE-default} case $withval in yes|no) pic_mode=$withval ;; *) pic_mode=default # Look at the argument we got. We use all the common list separators. lt_save_ifs=$IFS; IFS=$IFS$PATH_SEPARATOR, for lt_pkg in $withval; do IFS=$lt_save_ifs if test "X$lt_pkg" = "X$lt_p"; then pic_mode=yes fi done IFS=$lt_save_ifs ;; esac], [pic_mode=m4_default([$1], [default])]) _LT_DECL([], [pic_mode], [0], [What type of objects to build])dnl ])# _LT_WITH_PIC LT_OPTION_DEFINE([LT_INIT], [pic-only], [_LT_WITH_PIC([yes])]) LT_OPTION_DEFINE([LT_INIT], [no-pic], [_LT_WITH_PIC([no])]) # Old name: AU_DEFUN([AC_LIBTOOL_PICMODE], [_LT_SET_OPTION([LT_INIT], [pic-only]) AC_DIAGNOSE([obsolete], [$0: Remove this warning and the call to _LT_SET_OPTION when you put the 'pic-only' option into LT_INIT's first parameter.]) ]) dnl aclocal-1.4 backwards compatibility: dnl AC_DEFUN([AC_LIBTOOL_PICMODE], []) m4_define([_LTDL_MODE], []) LT_OPTION_DEFINE([LTDL_INIT], [nonrecursive], [m4_define([_LTDL_MODE], [nonrecursive])]) LT_OPTION_DEFINE([LTDL_INIT], [recursive], [m4_define([_LTDL_MODE], [recursive])]) LT_OPTION_DEFINE([LTDL_INIT], [subproject], [m4_define([_LTDL_MODE], [subproject])]) m4_define([_LTDL_TYPE], []) LT_OPTION_DEFINE([LTDL_INIT], [installable], [m4_define([_LTDL_TYPE], [installable])]) LT_OPTION_DEFINE([LTDL_INIT], [convenience], [m4_define([_LTDL_TYPE], [convenience])]) # ltsugar.m4 -- libtool m4 base layer. -*-Autoconf-*- # # Copyright (C) 2004-2005, 2007-2008, 2011-2019, 2021-2022 Free Software # Foundation, Inc. # Written by Gary V. Vaughan, 2004 # # This file is free software; the Free Software Foundation gives # unlimited permission to copy and/or distribute it, with or without # modifications, as long as this notice is preserved. # serial 6 ltsugar.m4 # This is to help aclocal find these macros, as it can't see m4_define. AC_DEFUN([LTSUGAR_VERSION], [m4_if([0.1])]) # lt_join(SEP, ARG1, [ARG2...]) # ----------------------------- # Produce ARG1SEPARG2...SEPARGn, omitting [] arguments and their # associated separator. # Needed until we can rely on m4_join from Autoconf 2.62, since all earlier # versions in m4sugar had bugs. m4_define([lt_join], [m4_if([$#], [1], [], [$#], [2], [[$2]], [m4_if([$2], [], [], [[$2]_])$0([$1], m4_shift(m4_shift($@)))])]) m4_define([_lt_join], [m4_if([$#$2], [2], [], [m4_if([$2], [], [], [[$1$2]])$0([$1], m4_shift(m4_shift($@)))])]) # lt_car(LIST) # lt_cdr(LIST) # ------------ # Manipulate m4 lists. # These macros are necessary as long as will still need to support # Autoconf-2.59, which quotes differently. m4_define([lt_car], [[$1]]) m4_define([lt_cdr], [m4_if([$#], 0, [m4_fatal([$0: cannot be called without arguments])], [$#], 1, [], [m4_dquote(m4_shift($@))])]) m4_define([lt_unquote], $1) # lt_append(MACRO-NAME, STRING, [SEPARATOR]) # ------------------------------------------ # Redefine MACRO-NAME to hold its former content plus 'SEPARATOR''STRING'. # Note that neither SEPARATOR nor STRING are expanded; they are appended # to MACRO-NAME as is (leaving the expansion for when MACRO-NAME is invoked). # No SEPARATOR is output if MACRO-NAME was previously undefined (different # than defined and empty). # # This macro is needed until we can rely on Autoconf 2.62, since earlier # versions of m4sugar mistakenly expanded SEPARATOR but not STRING. m4_define([lt_append], [m4_define([$1], m4_ifdef([$1], [m4_defn([$1])[$3]])[$2])]) # lt_combine(SEP, PREFIX-LIST, INFIX, SUFFIX1, [SUFFIX2...]) # ---------------------------------------------------------- # Produce a SEP delimited list of all paired combinations of elements of # PREFIX-LIST with SUFFIX1 through SUFFIXn. Each element of the list # has the form PREFIXmINFIXSUFFIXn. # Needed until we can rely on m4_combine added in Autoconf 2.62. m4_define([lt_combine], [m4_if(m4_eval([$# > 3]), [1], [m4_pushdef([_Lt_sep], [m4_define([_Lt_sep], m4_defn([lt_car]))])]]dnl [[m4_foreach([_Lt_prefix], [$2], [m4_foreach([_Lt_suffix], ]m4_dquote(m4_dquote(m4_shift(m4_shift(m4_shift($@)))))[, [_Lt_sep([$1])[]m4_defn([_Lt_prefix])[$3]m4_defn([_Lt_suffix])])])])]) # lt_if_append_uniq(MACRO-NAME, VARNAME, [SEPARATOR], [UNIQ], [NOT-UNIQ]) # ----------------------------------------------------------------------- # Iff MACRO-NAME does not yet contain VARNAME, then append it (delimited # by SEPARATOR if supplied) and expand UNIQ, else NOT-UNIQ. m4_define([lt_if_append_uniq], [m4_ifdef([$1], [m4_if(m4_index([$3]m4_defn([$1])[$3], [$3$2$3]), [-1], [lt_append([$1], [$2], [$3])$4], [$5])], [lt_append([$1], [$2], [$3])$4])]) # lt_dict_add(DICT, KEY, VALUE) # ----------------------------- m4_define([lt_dict_add], [m4_define([$1($2)], [$3])]) # lt_dict_add_subkey(DICT, KEY, SUBKEY, VALUE) # -------------------------------------------- m4_define([lt_dict_add_subkey], [m4_define([$1($2:$3)], [$4])]) # lt_dict_fetch(DICT, KEY, [SUBKEY]) # ---------------------------------- m4_define([lt_dict_fetch], [m4_ifval([$3], m4_ifdef([$1($2:$3)], [m4_defn([$1($2:$3)])]), m4_ifdef([$1($2)], [m4_defn([$1($2)])]))]) # lt_if_dict_fetch(DICT, KEY, [SUBKEY], VALUE, IF-TRUE, [IF-FALSE]) # ----------------------------------------------------------------- m4_define([lt_if_dict_fetch], [m4_if(lt_dict_fetch([$1], [$2], [$3]), [$4], [$5], [$6])]) # lt_dict_filter(DICT, [SUBKEY], VALUE, [SEPARATOR], KEY, [...]) # -------------------------------------------------------------- m4_define([lt_dict_filter], [m4_if([$5], [], [], [lt_join(m4_quote(m4_default([$4], [[, ]])), lt_unquote(m4_split(m4_normalize(m4_foreach(_Lt_key, lt_car([m4_shiftn(4, $@)]), [lt_if_dict_fetch([$1], _Lt_key, [$2], [$3], [_Lt_key ])])))))])[]dnl ]) # ltversion.m4 -- version numbers -*- Autoconf -*- # # Copyright (C) 2004, 2011-2019, 2021-2022 Free Software Foundation, # Inc. # Written by Scott James Remnant, 2004 # # This file is free software; the Free Software Foundation gives # unlimited permission to copy and/or distribute it, with or without # modifications, as long as this notice is preserved. # @configure_input@ # serial 4245 ltversion.m4 # This file is part of GNU Libtool m4_define([LT_PACKAGE_VERSION], [2.4.7]) m4_define([LT_PACKAGE_REVISION], [2.4.7]) AC_DEFUN([LTVERSION_VERSION], [macro_version='2.4.7' macro_revision='2.4.7' _LT_DECL(, macro_version, 0, [Which release of libtool.m4 was used?]) _LT_DECL(, macro_revision, 0) ]) # lt~obsolete.m4 -- aclocal satisfying obsolete definitions. -*-Autoconf-*- # # Copyright (C) 2004-2005, 2007, 2009, 2011-2019, 2021-2022 Free # Software Foundation, Inc. # Written by Scott James Remnant, 2004. # # This file is free software; the Free Software Foundation gives # unlimited permission to copy and/or distribute it, with or without # modifications, as long as this notice is preserved. # serial 5 lt~obsolete.m4 # These exist entirely to fool aclocal when bootstrapping libtool. # # In the past libtool.m4 has provided macros via AC_DEFUN (or AU_DEFUN), # which have later been changed to m4_define as they aren't part of the # exported API, or moved to Autoconf or Automake where they belong. # # The trouble is, aclocal is a bit thick. It'll see the old AC_DEFUN # in /usr/share/aclocal/libtool.m4 and remember it, then when it sees us # using a macro with the same name in our local m4/libtool.m4 it'll # pull the old libtool.m4 in (it doesn't see our shiny new m4_define # and doesn't know about Autoconf macros at all.) # # So we provide this file, which has a silly filename so it's always # included after everything else. This provides aclocal with the # AC_DEFUNs it wants, but when m4 processes it, it doesn't do anything # because those macros already exist, or will be overwritten later. # We use AC_DEFUN over AU_DEFUN for compatibility with aclocal-1.6. # # Anytime we withdraw an AC_DEFUN or AU_DEFUN, remember to add it here. # Yes, that means every name once taken will need to remain here until # we give up compatibility with versions before 1.7, at which point # we need to keep only those names which we still refer to. # This is to help aclocal find these macros, as it can't see m4_define. AC_DEFUN([LTOBSOLETE_VERSION], [m4_if([1])]) m4_ifndef([AC_LIBTOOL_LINKER_OPTION], [AC_DEFUN([AC_LIBTOOL_LINKER_OPTION])]) m4_ifndef([AC_PROG_EGREP], [AC_DEFUN([AC_PROG_EGREP])]) m4_ifndef([_LT_AC_PROG_ECHO_BACKSLASH], [AC_DEFUN([_LT_AC_PROG_ECHO_BACKSLASH])]) m4_ifndef([_LT_AC_SHELL_INIT], [AC_DEFUN([_LT_AC_SHELL_INIT])]) m4_ifndef([_LT_AC_SYS_LIBPATH_AIX], [AC_DEFUN([_LT_AC_SYS_LIBPATH_AIX])]) m4_ifndef([_LT_PROG_LTMAIN], [AC_DEFUN([_LT_PROG_LTMAIN])]) m4_ifndef([_LT_AC_TAGVAR], [AC_DEFUN([_LT_AC_TAGVAR])]) m4_ifndef([AC_LTDL_ENABLE_INSTALL], [AC_DEFUN([AC_LTDL_ENABLE_INSTALL])]) m4_ifndef([AC_LTDL_PREOPEN], [AC_DEFUN([AC_LTDL_PREOPEN])]) m4_ifndef([_LT_AC_SYS_COMPILER], [AC_DEFUN([_LT_AC_SYS_COMPILER])]) m4_ifndef([_LT_AC_LOCK], [AC_DEFUN([_LT_AC_LOCK])]) m4_ifndef([AC_LIBTOOL_SYS_OLD_ARCHIVE], [AC_DEFUN([AC_LIBTOOL_SYS_OLD_ARCHIVE])]) m4_ifndef([_LT_AC_TRY_DLOPEN_SELF], [AC_DEFUN([_LT_AC_TRY_DLOPEN_SELF])]) m4_ifndef([AC_LIBTOOL_PROG_CC_C_O], [AC_DEFUN([AC_LIBTOOL_PROG_CC_C_O])]) m4_ifndef([AC_LIBTOOL_SYS_HARD_LINK_LOCKS], [AC_DEFUN([AC_LIBTOOL_SYS_HARD_LINK_LOCKS])]) m4_ifndef([AC_LIBTOOL_OBJDIR], [AC_DEFUN([AC_LIBTOOL_OBJDIR])]) m4_ifndef([AC_LTDL_OBJDIR], [AC_DEFUN([AC_LTDL_OBJDIR])]) m4_ifndef([AC_LIBTOOL_PROG_LD_HARDCODE_LIBPATH], [AC_DEFUN([AC_LIBTOOL_PROG_LD_HARDCODE_LIBPATH])]) m4_ifndef([AC_LIBTOOL_SYS_LIB_STRIP], [AC_DEFUN([AC_LIBTOOL_SYS_LIB_STRIP])]) m4_ifndef([AC_PATH_MAGIC], [AC_DEFUN([AC_PATH_MAGIC])]) m4_ifndef([AC_PROG_LD_GNU], [AC_DEFUN([AC_PROG_LD_GNU])]) m4_ifndef([AC_PROG_LD_RELOAD_FLAG], [AC_DEFUN([AC_PROG_LD_RELOAD_FLAG])]) m4_ifndef([AC_DEPLIBS_CHECK_METHOD], [AC_DEFUN([AC_DEPLIBS_CHECK_METHOD])]) m4_ifndef([AC_LIBTOOL_PROG_COMPILER_NO_RTTI], [AC_DEFUN([AC_LIBTOOL_PROG_COMPILER_NO_RTTI])]) m4_ifndef([AC_LIBTOOL_SYS_GLOBAL_SYMBOL_PIPE], [AC_DEFUN([AC_LIBTOOL_SYS_GLOBAL_SYMBOL_PIPE])]) m4_ifndef([AC_LIBTOOL_PROG_COMPILER_PIC], [AC_DEFUN([AC_LIBTOOL_PROG_COMPILER_PIC])]) m4_ifndef([AC_LIBTOOL_PROG_LD_SHLIBS], [AC_DEFUN([AC_LIBTOOL_PROG_LD_SHLIBS])]) m4_ifndef([AC_LIBTOOL_POSTDEP_PREDEP], [AC_DEFUN([AC_LIBTOOL_POSTDEP_PREDEP])]) m4_ifndef([LT_AC_PROG_EGREP], [AC_DEFUN([LT_AC_PROG_EGREP])]) m4_ifndef([LT_AC_PROG_SED], [AC_DEFUN([LT_AC_PROG_SED])]) m4_ifndef([_LT_CC_BASENAME], [AC_DEFUN([_LT_CC_BASENAME])]) m4_ifndef([_LT_COMPILER_BOILERPLATE], [AC_DEFUN([_LT_COMPILER_BOILERPLATE])]) m4_ifndef([_LT_LINKER_BOILERPLATE], [AC_DEFUN([_LT_LINKER_BOILERPLATE])]) m4_ifndef([_AC_PROG_LIBTOOL], [AC_DEFUN([_AC_PROG_LIBTOOL])]) m4_ifndef([AC_LIBTOOL_SETUP], [AC_DEFUN([AC_LIBTOOL_SETUP])]) m4_ifndef([_LT_AC_CHECK_DLFCN], [AC_DEFUN([_LT_AC_CHECK_DLFCN])]) m4_ifndef([AC_LIBTOOL_SYS_DYNAMIC_LINKER], [AC_DEFUN([AC_LIBTOOL_SYS_DYNAMIC_LINKER])]) m4_ifndef([_LT_AC_TAGCONFIG], [AC_DEFUN([_LT_AC_TAGCONFIG])]) m4_ifndef([AC_DISABLE_FAST_INSTALL], [AC_DEFUN([AC_DISABLE_FAST_INSTALL])]) m4_ifndef([_LT_AC_LANG_CXX], [AC_DEFUN([_LT_AC_LANG_CXX])]) m4_ifndef([_LT_AC_LANG_F77], [AC_DEFUN([_LT_AC_LANG_F77])]) m4_ifndef([_LT_AC_LANG_GCJ], [AC_DEFUN([_LT_AC_LANG_GCJ])]) m4_ifndef([AC_LIBTOOL_LANG_C_CONFIG], [AC_DEFUN([AC_LIBTOOL_LANG_C_CONFIG])]) m4_ifndef([_LT_AC_LANG_C_CONFIG], [AC_DEFUN([_LT_AC_LANG_C_CONFIG])]) m4_ifndef([AC_LIBTOOL_LANG_CXX_CONFIG], [AC_DEFUN([AC_LIBTOOL_LANG_CXX_CONFIG])]) m4_ifndef([_LT_AC_LANG_CXX_CONFIG], [AC_DEFUN([_LT_AC_LANG_CXX_CONFIG])]) m4_ifndef([AC_LIBTOOL_LANG_F77_CONFIG], [AC_DEFUN([AC_LIBTOOL_LANG_F77_CONFIG])]) m4_ifndef([_LT_AC_LANG_F77_CONFIG], [AC_DEFUN([_LT_AC_LANG_F77_CONFIG])]) m4_ifndef([AC_LIBTOOL_LANG_GCJ_CONFIG], [AC_DEFUN([AC_LIBTOOL_LANG_GCJ_CONFIG])]) m4_ifndef([_LT_AC_LANG_GCJ_CONFIG], [AC_DEFUN([_LT_AC_LANG_GCJ_CONFIG])]) m4_ifndef([AC_LIBTOOL_LANG_RC_CONFIG], [AC_DEFUN([AC_LIBTOOL_LANG_RC_CONFIG])]) m4_ifndef([_LT_AC_LANG_RC_CONFIG], [AC_DEFUN([_LT_AC_LANG_RC_CONFIG])]) m4_ifndef([AC_LIBTOOL_CONFIG], [AC_DEFUN([AC_LIBTOOL_CONFIG])]) m4_ifndef([_LT_AC_FILE_LTDLL_C], [AC_DEFUN([_LT_AC_FILE_LTDLL_C])]) m4_ifndef([_LT_REQUIRED_DARWIN_CHECKS], [AC_DEFUN([_LT_REQUIRED_DARWIN_CHECKS])]) m4_ifndef([_LT_AC_PROG_CXXCPP], [AC_DEFUN([_LT_AC_PROG_CXXCPP])]) m4_ifndef([_LT_PREPARE_SED_QUOTE_VARS], [AC_DEFUN([_LT_PREPARE_SED_QUOTE_VARS])]) m4_ifndef([_LT_PROG_ECHO_BACKSLASH], [AC_DEFUN([_LT_PROG_ECHO_BACKSLASH])]) m4_ifndef([_LT_PROG_F77], [AC_DEFUN([_LT_PROG_F77])]) m4_ifndef([_LT_PROG_FC], [AC_DEFUN([_LT_PROG_FC])]) m4_ifndef([_LT_PROG_CXX], [AC_DEFUN([_LT_PROG_CXX])]) # pkg.m4 - Macros to locate and use pkg-config. -*- Autoconf -*- # serial 12 (pkg-config-0.29.2) dnl Copyright © 2004 Scott James Remnant . dnl Copyright © 2012-2015 Dan Nicholson dnl dnl This program is free software; you can redistribute it and/or modify dnl it under the terms of the GNU General Public License as published by dnl the Free Software Foundation; either version 2 of the License, or dnl (at your option) any later version. dnl dnl This program is distributed in the hope that it will be useful, but dnl WITHOUT ANY WARRANTY; without even the implied warranty of dnl MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU dnl General Public License for more details. dnl dnl You should have received a copy of the GNU General Public License dnl along with this program; if not, write to the Free Software dnl Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA dnl 02111-1307, USA. dnl dnl As a special exception to the GNU General Public License, if you dnl distribute this file as part of a program that contains a dnl configuration script generated by Autoconf, you may include it under dnl the same distribution terms that you use for the rest of that dnl program. dnl PKG_PREREQ(MIN-VERSION) dnl ----------------------- dnl Since: 0.29 dnl dnl Verify that the version of the pkg-config macros are at least dnl MIN-VERSION. Unlike PKG_PROG_PKG_CONFIG, which checks the user's dnl installed version of pkg-config, this checks the developer's version dnl of pkg.m4 when generating configure. dnl dnl To ensure that this macro is defined, also add: dnl m4_ifndef([PKG_PREREQ], dnl [m4_fatal([must install pkg-config 0.29 or later before running autoconf/autogen])]) dnl dnl See the "Since" comment for each macro you use to see what version dnl of the macros you require. m4_defun([PKG_PREREQ], [m4_define([PKG_MACROS_VERSION], [0.29.2]) m4_if(m4_version_compare(PKG_MACROS_VERSION, [$1]), -1, [m4_fatal([pkg.m4 version $1 or higher is required but ]PKG_MACROS_VERSION[ found])]) ])dnl PKG_PREREQ dnl PKG_PROG_PKG_CONFIG([MIN-VERSION]) dnl ---------------------------------- dnl Since: 0.16 dnl dnl Search for the pkg-config tool and set the PKG_CONFIG variable to dnl first found in the path. Checks that the version of pkg-config found dnl is at least MIN-VERSION. If MIN-VERSION is not specified, 0.9.0 is dnl used since that's the first version where most current features of dnl pkg-config existed. AC_DEFUN([PKG_PROG_PKG_CONFIG], [m4_pattern_forbid([^_?PKG_[A-Z_]+$]) m4_pattern_allow([^PKG_CONFIG(_(PATH|LIBDIR|SYSROOT_DIR|ALLOW_SYSTEM_(CFLAGS|LIBS)))?$]) m4_pattern_allow([^PKG_CONFIG_(DISABLE_UNINSTALLED|TOP_BUILD_DIR|DEBUG_SPEW)$]) AC_ARG_VAR([PKG_CONFIG], [path to pkg-config utility]) AC_ARG_VAR([PKG_CONFIG_PATH], [directories to add to pkg-config's search path]) AC_ARG_VAR([PKG_CONFIG_LIBDIR], [path overriding pkg-config's built-in search path]) if test "x$ac_cv_env_PKG_CONFIG_set" != "xset"; then AC_PATH_TOOL([PKG_CONFIG], [pkg-config]) fi if test -n "$PKG_CONFIG"; then _pkg_min_version=m4_default([$1], [0.9.0]) AC_MSG_CHECKING([pkg-config is at least version $_pkg_min_version]) if $PKG_CONFIG --atleast-pkgconfig-version $_pkg_min_version; then AC_MSG_RESULT([yes]) else AC_MSG_RESULT([no]) PKG_CONFIG="" fi fi[]dnl ])dnl PKG_PROG_PKG_CONFIG dnl PKG_CHECK_EXISTS(MODULES, [ACTION-IF-FOUND], [ACTION-IF-NOT-FOUND]) dnl ------------------------------------------------------------------- dnl Since: 0.18 dnl dnl Check to see whether a particular set of modules exists. Similar to dnl PKG_CHECK_MODULES(), but does not set variables or print errors. dnl dnl Please remember that m4 expands AC_REQUIRE([PKG_PROG_PKG_CONFIG]) dnl only at the first occurrence in configure.ac, so if the first place dnl it's called might be skipped (such as if it is within an "if", you dnl have to call PKG_CHECK_EXISTS manually AC_DEFUN([PKG_CHECK_EXISTS], [AC_REQUIRE([PKG_PROG_PKG_CONFIG])dnl if test -n "$PKG_CONFIG" && \ AC_RUN_LOG([$PKG_CONFIG --exists --print-errors "$1"]); then m4_default([$2], [:]) m4_ifvaln([$3], [else $3])dnl fi]) dnl _PKG_CONFIG([VARIABLE], [COMMAND], [MODULES]) dnl --------------------------------------------- dnl Internal wrapper calling pkg-config via PKG_CONFIG and setting dnl pkg_failed based on the result. m4_define([_PKG_CONFIG], [if test -n "$$1"; then pkg_cv_[]$1="$$1" elif test -n "$PKG_CONFIG"; then PKG_CHECK_EXISTS([$3], [pkg_cv_[]$1=`$PKG_CONFIG --[]$2 "$3" 2>/dev/null` test "x$?" != "x0" && pkg_failed=yes ], [pkg_failed=yes]) else pkg_failed=untried fi[]dnl ])dnl _PKG_CONFIG dnl _PKG_SHORT_ERRORS_SUPPORTED dnl --------------------------- dnl Internal check to see if pkg-config supports short errors. AC_DEFUN([_PKG_SHORT_ERRORS_SUPPORTED], [AC_REQUIRE([PKG_PROG_PKG_CONFIG]) if $PKG_CONFIG --atleast-pkgconfig-version 0.20; then _pkg_short_errors_supported=yes else _pkg_short_errors_supported=no fi[]dnl ])dnl _PKG_SHORT_ERRORS_SUPPORTED dnl PKG_CHECK_MODULES(VARIABLE-PREFIX, MODULES, [ACTION-IF-FOUND], dnl [ACTION-IF-NOT-FOUND]) dnl -------------------------------------------------------------- dnl Since: 0.4.0 dnl dnl Note that if there is a possibility the first call to dnl PKG_CHECK_MODULES might not happen, you should be sure to include an dnl explicit call to PKG_PROG_PKG_CONFIG in your configure.ac AC_DEFUN([PKG_CHECK_MODULES], [AC_REQUIRE([PKG_PROG_PKG_CONFIG])dnl AC_ARG_VAR([$1][_CFLAGS], [C compiler flags for $1, overriding pkg-config])dnl AC_ARG_VAR([$1][_LIBS], [linker flags for $1, overriding pkg-config])dnl pkg_failed=no AC_MSG_CHECKING([for $2]) _PKG_CONFIG([$1][_CFLAGS], [cflags], [$2]) _PKG_CONFIG([$1][_LIBS], [libs], [$2]) m4_define([_PKG_TEXT], [Alternatively, you may set the environment variables $1[]_CFLAGS and $1[]_LIBS to avoid the need to call pkg-config. See the pkg-config man page for more details.]) if test $pkg_failed = yes; then AC_MSG_RESULT([no]) _PKG_SHORT_ERRORS_SUPPORTED if test $_pkg_short_errors_supported = yes; then $1[]_PKG_ERRORS=`$PKG_CONFIG --short-errors --print-errors --cflags --libs "$2" 2>&1` else $1[]_PKG_ERRORS=`$PKG_CONFIG --print-errors --cflags --libs "$2" 2>&1` fi # Put the nasty error message in config.log where it belongs echo "$$1[]_PKG_ERRORS" >&AS_MESSAGE_LOG_FD m4_default([$4], [AC_MSG_ERROR( [Package requirements ($2) were not met: $$1_PKG_ERRORS Consider adjusting the PKG_CONFIG_PATH environment variable if you installed software in a non-standard prefix. _PKG_TEXT])[]dnl ]) elif test $pkg_failed = untried; then AC_MSG_RESULT([no]) m4_default([$4], [AC_MSG_FAILURE( [The pkg-config script could not be found or is too old. Make sure it is in your PATH or set the PKG_CONFIG environment variable to the full path to pkg-config. _PKG_TEXT To get pkg-config, see .])[]dnl ]) else $1[]_CFLAGS=$pkg_cv_[]$1[]_CFLAGS $1[]_LIBS=$pkg_cv_[]$1[]_LIBS AC_MSG_RESULT([yes]) $3 fi[]dnl ])dnl PKG_CHECK_MODULES dnl PKG_CHECK_MODULES_STATIC(VARIABLE-PREFIX, MODULES, [ACTION-IF-FOUND], dnl [ACTION-IF-NOT-FOUND]) dnl --------------------------------------------------------------------- dnl Since: 0.29 dnl dnl Checks for existence of MODULES and gathers its build flags with dnl static libraries enabled. Sets VARIABLE-PREFIX_CFLAGS from --cflags dnl and VARIABLE-PREFIX_LIBS from --libs. dnl dnl Note that if there is a possibility the first call to dnl PKG_CHECK_MODULES_STATIC might not happen, you should be sure to dnl include an explicit call to PKG_PROG_PKG_CONFIG in your dnl configure.ac. AC_DEFUN([PKG_CHECK_MODULES_STATIC], [AC_REQUIRE([PKG_PROG_PKG_CONFIG])dnl _save_PKG_CONFIG=$PKG_CONFIG PKG_CONFIG="$PKG_CONFIG --static" PKG_CHECK_MODULES($@) PKG_CONFIG=$_save_PKG_CONFIG[]dnl ])dnl PKG_CHECK_MODULES_STATIC dnl PKG_INSTALLDIR([DIRECTORY]) dnl ------------------------- dnl Since: 0.27 dnl dnl Substitutes the variable pkgconfigdir as the location where a module dnl should install pkg-config .pc files. By default the directory is dnl $libdir/pkgconfig, but the default can be changed by passing dnl DIRECTORY. The user can override through the --with-pkgconfigdir dnl parameter. AC_DEFUN([PKG_INSTALLDIR], [m4_pushdef([pkg_default], [m4_default([$1], ['${libdir}/pkgconfig'])]) m4_pushdef([pkg_description], [pkg-config installation directory @<:@]pkg_default[@:>@]) AC_ARG_WITH([pkgconfigdir], [AS_HELP_STRING([--with-pkgconfigdir], pkg_description)],, [with_pkgconfigdir=]pkg_default) AC_SUBST([pkgconfigdir], [$with_pkgconfigdir]) m4_popdef([pkg_default]) m4_popdef([pkg_description]) ])dnl PKG_INSTALLDIR dnl PKG_NOARCH_INSTALLDIR([DIRECTORY]) dnl -------------------------------- dnl Since: 0.27 dnl dnl Substitutes the variable noarch_pkgconfigdir as the location where a dnl module should install arch-independent pkg-config .pc files. By dnl default the directory is $datadir/pkgconfig, but the default can be dnl changed by passing DIRECTORY. The user can override through the dnl --with-noarch-pkgconfigdir parameter. AC_DEFUN([PKG_NOARCH_INSTALLDIR], [m4_pushdef([pkg_default], [m4_default([$1], ['${datadir}/pkgconfig'])]) m4_pushdef([pkg_description], [pkg-config arch-independent installation directory @<:@]pkg_default[@:>@]) AC_ARG_WITH([noarch-pkgconfigdir], [AS_HELP_STRING([--with-noarch-pkgconfigdir], pkg_description)],, [with_noarch_pkgconfigdir=]pkg_default) AC_SUBST([noarch_pkgconfigdir], [$with_noarch_pkgconfigdir]) m4_popdef([pkg_default]) m4_popdef([pkg_description]) ])dnl PKG_NOARCH_INSTALLDIR dnl PKG_CHECK_VAR(VARIABLE, MODULE, CONFIG-VARIABLE, dnl [ACTION-IF-FOUND], [ACTION-IF-NOT-FOUND]) dnl ------------------------------------------- dnl Since: 0.28 dnl dnl Retrieves the value of the pkg-config variable for the given module. AC_DEFUN([PKG_CHECK_VAR], [AC_REQUIRE([PKG_PROG_PKG_CONFIG])dnl AC_ARG_VAR([$1], [value of $3 for $2, overriding pkg-config])dnl _PKG_CONFIG([$1], [variable="][$3]["], [$2]) AS_VAR_COPY([$1], [pkg_cv_][$1]) AS_VAR_IF([$1], [""], [$5], [$4])dnl ])dnl PKG_CHECK_VAR dnl PKG_WITH_MODULES(VARIABLE-PREFIX, MODULES, dnl [ACTION-IF-FOUND],[ACTION-IF-NOT-FOUND], dnl [DESCRIPTION], [DEFAULT]) dnl ------------------------------------------ dnl dnl Prepare a "--with-" configure option using the lowercase dnl [VARIABLE-PREFIX] name, merging the behaviour of AC_ARG_WITH and dnl PKG_CHECK_MODULES in a single macro. AC_DEFUN([PKG_WITH_MODULES], [ m4_pushdef([with_arg], m4_tolower([$1])) m4_pushdef([description], [m4_default([$5], [build with ]with_arg[ support])]) m4_pushdef([def_arg], [m4_default([$6], [auto])]) m4_pushdef([def_action_if_found], [AS_TR_SH([with_]with_arg)=yes]) m4_pushdef([def_action_if_not_found], [AS_TR_SH([with_]with_arg)=no]) m4_case(def_arg, [yes],[m4_pushdef([with_without], [--without-]with_arg)], [m4_pushdef([with_without],[--with-]with_arg)]) AC_ARG_WITH(with_arg, AS_HELP_STRING(with_without, description[ @<:@default=]def_arg[@:>@]),, [AS_TR_SH([with_]with_arg)=def_arg]) AS_CASE([$AS_TR_SH([with_]with_arg)], [yes],[PKG_CHECK_MODULES([$1],[$2],$3,$4)], [auto],[PKG_CHECK_MODULES([$1],[$2], [m4_n([def_action_if_found]) $3], [m4_n([def_action_if_not_found]) $4])]) m4_popdef([with_arg]) m4_popdef([description]) m4_popdef([def_arg]) ])dnl PKG_WITH_MODULES dnl PKG_HAVE_WITH_MODULES(VARIABLE-PREFIX, MODULES, dnl [DESCRIPTION], [DEFAULT]) dnl ----------------------------------------------- dnl dnl Convenience macro to trigger AM_CONDITIONAL after PKG_WITH_MODULES dnl check._[VARIABLE-PREFIX] is exported as make variable. AC_DEFUN([PKG_HAVE_WITH_MODULES], [ PKG_WITH_MODULES([$1],[$2],,,[$3],[$4]) AM_CONDITIONAL([HAVE_][$1], [test "$AS_TR_SH([with_]m4_tolower([$1]))" = "yes"]) ])dnl PKG_HAVE_WITH_MODULES dnl PKG_HAVE_DEFINE_WITH_MODULES(VARIABLE-PREFIX, MODULES, dnl [DESCRIPTION], [DEFAULT]) dnl ------------------------------------------------------ dnl dnl Convenience macro to run AM_CONDITIONAL and AC_DEFINE after dnl PKG_WITH_MODULES check. HAVE_[VARIABLE-PREFIX] is exported as make dnl and preprocessor variable. AC_DEFUN([PKG_HAVE_DEFINE_WITH_MODULES], [ PKG_HAVE_WITH_MODULES([$1],[$2],[$3],[$4]) AS_IF([test "$AS_TR_SH([with_]m4_tolower([$1]))" = "yes"], [AC_DEFINE([HAVE_][$1], 1, [Enable ]m4_tolower([$1])[ support])]) ])dnl PKG_HAVE_DEFINE_WITH_MODULES # Copyright (C) 2002-2021 Free Software Foundation, Inc. # # This file is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, # with or without modifications, as long as this notice is preserved. # AM_AUTOMAKE_VERSION(VERSION) # ---------------------------- # Automake X.Y traces this macro to ensure aclocal.m4 has been # generated from the m4 files accompanying Automake X.Y. # (This private macro should not be called outside this file.) AC_DEFUN([AM_AUTOMAKE_VERSION], [am__api_version='1.16' dnl Some users find AM_AUTOMAKE_VERSION and mistake it for a way to dnl require some minimum version. Point them to the right macro. m4_if([$1], [1.16.5], [], [AC_FATAL([Do not call $0, use AM_INIT_AUTOMAKE([$1]).])])dnl ]) # _AM_AUTOCONF_VERSION(VERSION) # ----------------------------- # aclocal traces this macro to find the Autoconf version. # This is a private macro too. Using m4_define simplifies # the logic in aclocal, which can simply ignore this definition. m4_define([_AM_AUTOCONF_VERSION], []) # AM_SET_CURRENT_AUTOMAKE_VERSION # ------------------------------- # Call AM_AUTOMAKE_VERSION and AM_AUTOMAKE_VERSION so they can be traced. # This function is AC_REQUIREd by AM_INIT_AUTOMAKE. AC_DEFUN([AM_SET_CURRENT_AUTOMAKE_VERSION], [AM_AUTOMAKE_VERSION([1.16.5])dnl m4_ifndef([AC_AUTOCONF_VERSION], [m4_copy([m4_PACKAGE_VERSION], [AC_AUTOCONF_VERSION])])dnl _AM_AUTOCONF_VERSION(m4_defn([AC_AUTOCONF_VERSION]))]) # AM_AUX_DIR_EXPAND -*- Autoconf -*- # Copyright (C) 2001-2021 Free Software Foundation, Inc. # # This file is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, # with or without modifications, as long as this notice is preserved. # For projects using AC_CONFIG_AUX_DIR([foo]), Autoconf sets # $ac_aux_dir to '$srcdir/foo'. In other projects, it is set to # '$srcdir', '$srcdir/..', or '$srcdir/../..'. # # Of course, Automake must honor this variable whenever it calls a # tool from the auxiliary directory. The problem is that $srcdir (and # therefore $ac_aux_dir as well) can be either absolute or relative, # depending on how configure is run. This is pretty annoying, since # it makes $ac_aux_dir quite unusable in subdirectories: in the top # source directory, any form will work fine, but in subdirectories a # relative path needs to be adjusted first. # # $ac_aux_dir/missing # fails when called from a subdirectory if $ac_aux_dir is relative # $top_srcdir/$ac_aux_dir/missing # fails if $ac_aux_dir is absolute, # fails when called from a subdirectory in a VPATH build with # a relative $ac_aux_dir # # The reason of the latter failure is that $top_srcdir and $ac_aux_dir # are both prefixed by $srcdir. In an in-source build this is usually # harmless because $srcdir is '.', but things will broke when you # start a VPATH build or use an absolute $srcdir. # # So we could use something similar to $top_srcdir/$ac_aux_dir/missing, # iff we strip the leading $srcdir from $ac_aux_dir. That would be: # am_aux_dir='\$(top_srcdir)/'`expr "$ac_aux_dir" : "$srcdir//*\(.*\)"` # and then we would define $MISSING as # MISSING="\${SHELL} $am_aux_dir/missing" # This will work as long as MISSING is not called from configure, because # unfortunately $(top_srcdir) has no meaning in configure. # However there are other variables, like CC, which are often used in # configure, and could therefore not use this "fixed" $ac_aux_dir. # # Another solution, used here, is to always expand $ac_aux_dir to an # absolute PATH. The drawback is that using absolute paths prevent a # configured tree to be moved without reconfiguration. AC_DEFUN([AM_AUX_DIR_EXPAND], [AC_REQUIRE([AC_CONFIG_AUX_DIR_DEFAULT])dnl # Expand $ac_aux_dir to an absolute path. am_aux_dir=`cd "$ac_aux_dir" && pwd` ]) # AM_CONDITIONAL -*- Autoconf -*- # Copyright (C) 1997-2021 Free Software Foundation, Inc. # # This file is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, # with or without modifications, as long as this notice is preserved. # AM_CONDITIONAL(NAME, SHELL-CONDITION) # ------------------------------------- # Define a conditional. AC_DEFUN([AM_CONDITIONAL], [AC_PREREQ([2.52])dnl m4_if([$1], [TRUE], [AC_FATAL([$0: invalid condition: $1])], [$1], [FALSE], [AC_FATAL([$0: invalid condition: $1])])dnl AC_SUBST([$1_TRUE])dnl AC_SUBST([$1_FALSE])dnl _AM_SUBST_NOTMAKE([$1_TRUE])dnl _AM_SUBST_NOTMAKE([$1_FALSE])dnl m4_define([_AM_COND_VALUE_$1], [$2])dnl if $2; then $1_TRUE= $1_FALSE='#' else $1_TRUE='#' $1_FALSE= fi AC_CONFIG_COMMANDS_PRE( [if test -z "${$1_TRUE}" && test -z "${$1_FALSE}"; then AC_MSG_ERROR([[conditional "$1" was never defined. Usually this means the macro was only invoked conditionally.]]) fi])]) # Copyright (C) 1999-2021 Free Software Foundation, Inc. # # This file is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, # with or without modifications, as long as this notice is preserved. # There are a few dirty hacks below to avoid letting 'AC_PROG_CC' be # written in clear, in which case automake, when reading aclocal.m4, # will think it sees a *use*, and therefore will trigger all it's # C support machinery. Also note that it means that autoscan, seeing # CC etc. in the Makefile, will ask for an AC_PROG_CC use... # _AM_DEPENDENCIES(NAME) # ---------------------- # See how the compiler implements dependency checking. # NAME is "CC", "CXX", "OBJC", "OBJCXX", "UPC", or "GJC". # We try a few techniques and use that to set a single cache variable. # # We don't AC_REQUIRE the corresponding AC_PROG_CC since the latter was # modified to invoke _AM_DEPENDENCIES(CC); we would have a circular # dependency, and given that the user is not expected to run this macro, # just rely on AC_PROG_CC. AC_DEFUN([_AM_DEPENDENCIES], [AC_REQUIRE([AM_SET_DEPDIR])dnl AC_REQUIRE([AM_OUTPUT_DEPENDENCY_COMMANDS])dnl AC_REQUIRE([AM_MAKE_INCLUDE])dnl AC_REQUIRE([AM_DEP_TRACK])dnl m4_if([$1], [CC], [depcc="$CC" am_compiler_list=], [$1], [CXX], [depcc="$CXX" am_compiler_list=], [$1], [OBJC], [depcc="$OBJC" am_compiler_list='gcc3 gcc'], [$1], [OBJCXX], [depcc="$OBJCXX" am_compiler_list='gcc3 gcc'], [$1], [UPC], [depcc="$UPC" am_compiler_list=], [$1], [GCJ], [depcc="$GCJ" am_compiler_list='gcc3 gcc'], [depcc="$$1" am_compiler_list=]) AC_CACHE_CHECK([dependency style of $depcc], [am_cv_$1_dependencies_compiler_type], [if test -z "$AMDEP_TRUE" && test -f "$am_depcomp"; then # We make a subdir and do the tests there. Otherwise we can end up # making bogus files that we don't know about and never remove. For # instance it was reported that on HP-UX the gcc test will end up # making a dummy file named 'D' -- because '-MD' means "put the output # in D". rm -rf conftest.dir mkdir conftest.dir # Copy depcomp to subdir because otherwise we won't find it if we're # using a relative directory. cp "$am_depcomp" conftest.dir cd conftest.dir # We will build objects and dependencies in a subdirectory because # it helps to detect inapplicable dependency modes. For instance # both Tru64's cc and ICC support -MD to output dependencies as a # side effect of compilation, but ICC will put the dependencies in # the current directory while Tru64 will put them in the object # directory. mkdir sub am_cv_$1_dependencies_compiler_type=none if test "$am_compiler_list" = ""; then am_compiler_list=`sed -n ['s/^#*\([a-zA-Z0-9]*\))$/\1/p'] < ./depcomp` fi am__universal=false m4_case([$1], [CC], [case " $depcc " in #( *\ -arch\ *\ -arch\ *) am__universal=true ;; esac], [CXX], [case " $depcc " in #( *\ -arch\ *\ -arch\ *) am__universal=true ;; esac]) for depmode in $am_compiler_list; do # Setup a source with many dependencies, because some compilers # like to wrap large dependency lists on column 80 (with \), and # we should not choose a depcomp mode which is confused by this. # # We need to recreate these files for each test, as the compiler may # overwrite some of them when testing with obscure command lines. # This happens at least with the AIX C compiler. : > sub/conftest.c for i in 1 2 3 4 5 6; do echo '#include "conftst'$i'.h"' >> sub/conftest.c # Using ": > sub/conftst$i.h" creates only sub/conftst1.h with # Solaris 10 /bin/sh. echo '/* dummy */' > sub/conftst$i.h done echo "${am__include} ${am__quote}sub/conftest.Po${am__quote}" > confmf # We check with '-c' and '-o' for the sake of the "dashmstdout" # mode. It turns out that the SunPro C++ compiler does not properly # handle '-M -o', and we need to detect this. Also, some Intel # versions had trouble with output in subdirs. am__obj=sub/conftest.${OBJEXT-o} am__minus_obj="-o $am__obj" case $depmode in gcc) # This depmode causes a compiler race in universal mode. test "$am__universal" = false || continue ;; nosideeffect) # After this tag, mechanisms are not by side-effect, so they'll # only be used when explicitly requested. if test "x$enable_dependency_tracking" = xyes; then continue else break fi ;; msvc7 | msvc7msys | msvisualcpp | msvcmsys) # This compiler won't grok '-c -o', but also, the minuso test has # not run yet. These depmodes are late enough in the game, and # so weak that their functioning should not be impacted. am__obj=conftest.${OBJEXT-o} am__minus_obj= ;; none) break ;; esac if depmode=$depmode \ source=sub/conftest.c object=$am__obj \ depfile=sub/conftest.Po tmpdepfile=sub/conftest.TPo \ $SHELL ./depcomp $depcc -c $am__minus_obj sub/conftest.c \ >/dev/null 2>conftest.err && grep sub/conftst1.h sub/conftest.Po > /dev/null 2>&1 && grep sub/conftst6.h sub/conftest.Po > /dev/null 2>&1 && grep $am__obj sub/conftest.Po > /dev/null 2>&1 && ${MAKE-make} -s -f confmf > /dev/null 2>&1; then # icc doesn't choke on unknown options, it will just issue warnings # or remarks (even with -Werror). So we grep stderr for any message # that says an option was ignored or not supported. # When given -MP, icc 7.0 and 7.1 complain thusly: # icc: Command line warning: ignoring option '-M'; no argument required # The diagnosis changed in icc 8.0: # icc: Command line remark: option '-MP' not supported if (grep 'ignoring option' conftest.err || grep 'not supported' conftest.err) >/dev/null 2>&1; then :; else am_cv_$1_dependencies_compiler_type=$depmode break fi fi done cd .. rm -rf conftest.dir else am_cv_$1_dependencies_compiler_type=none fi ]) AC_SUBST([$1DEPMODE], [depmode=$am_cv_$1_dependencies_compiler_type]) AM_CONDITIONAL([am__fastdep$1], [ test "x$enable_dependency_tracking" != xno \ && test "$am_cv_$1_dependencies_compiler_type" = gcc3]) ]) # AM_SET_DEPDIR # ------------- # Choose a directory name for dependency files. # This macro is AC_REQUIREd in _AM_DEPENDENCIES. AC_DEFUN([AM_SET_DEPDIR], [AC_REQUIRE([AM_SET_LEADING_DOT])dnl AC_SUBST([DEPDIR], ["${am__leading_dot}deps"])dnl ]) # AM_DEP_TRACK # ------------ AC_DEFUN([AM_DEP_TRACK], [AC_ARG_ENABLE([dependency-tracking], [dnl AS_HELP_STRING( [--enable-dependency-tracking], [do not reject slow dependency extractors]) AS_HELP_STRING( [--disable-dependency-tracking], [speeds up one-time build])]) if test "x$enable_dependency_tracking" != xno; then am_depcomp="$ac_aux_dir/depcomp" AMDEPBACKSLASH='\' am__nodep='_no' fi AM_CONDITIONAL([AMDEP], [test "x$enable_dependency_tracking" != xno]) AC_SUBST([AMDEPBACKSLASH])dnl _AM_SUBST_NOTMAKE([AMDEPBACKSLASH])dnl AC_SUBST([am__nodep])dnl _AM_SUBST_NOTMAKE([am__nodep])dnl ]) # Generate code to set up dependency tracking. -*- Autoconf -*- # Copyright (C) 1999-2021 Free Software Foundation, Inc. # # This file is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, # with or without modifications, as long as this notice is preserved. # _AM_OUTPUT_DEPENDENCY_COMMANDS # ------------------------------ AC_DEFUN([_AM_OUTPUT_DEPENDENCY_COMMANDS], [{ # Older Autoconf quotes --file arguments for eval, but not when files # are listed without --file. Let's play safe and only enable the eval # if we detect the quoting. # TODO: see whether this extra hack can be removed once we start # requiring Autoconf 2.70 or later. AS_CASE([$CONFIG_FILES], [*\'*], [eval set x "$CONFIG_FILES"], [*], [set x $CONFIG_FILES]) shift # Used to flag and report bootstrapping failures. am_rc=0 for am_mf do # Strip MF so we end up with the name of the file. am_mf=`AS_ECHO(["$am_mf"]) | sed -e 's/:.*$//'` # Check whether this is an Automake generated Makefile which includes # dependency-tracking related rules and includes. # Grep'ing the whole file directly is not great: AIX grep has a line # limit of 2048, but all sed's we know have understand at least 4000. sed -n 's,^am--depfiles:.*,X,p' "$am_mf" | grep X >/dev/null 2>&1 \ || continue am_dirpart=`AS_DIRNAME(["$am_mf"])` am_filepart=`AS_BASENAME(["$am_mf"])` AM_RUN_LOG([cd "$am_dirpart" \ && sed -e '/# am--include-marker/d' "$am_filepart" \ | $MAKE -f - am--depfiles]) || am_rc=$? done if test $am_rc -ne 0; then AC_MSG_FAILURE([Something went wrong bootstrapping makefile fragments for automatic dependency tracking. If GNU make was not used, consider re-running the configure script with MAKE="gmake" (or whatever is necessary). You can also try re-running configure with the '--disable-dependency-tracking' option to at least be able to build the package (albeit without support for automatic dependency tracking).]) fi AS_UNSET([am_dirpart]) AS_UNSET([am_filepart]) AS_UNSET([am_mf]) AS_UNSET([am_rc]) rm -f conftest-deps.mk } ])# _AM_OUTPUT_DEPENDENCY_COMMANDS # AM_OUTPUT_DEPENDENCY_COMMANDS # ----------------------------- # This macro should only be invoked once -- use via AC_REQUIRE. # # This code is only required when automatic dependency tracking is enabled. # This creates each '.Po' and '.Plo' makefile fragment that we'll need in # order to bootstrap the dependency handling code. AC_DEFUN([AM_OUTPUT_DEPENDENCY_COMMANDS], [AC_CONFIG_COMMANDS([depfiles], [test x"$AMDEP_TRUE" != x"" || _AM_OUTPUT_DEPENDENCY_COMMANDS], [AMDEP_TRUE="$AMDEP_TRUE" MAKE="${MAKE-make}"])]) # Do all the work for Automake. -*- Autoconf -*- # Copyright (C) 1996-2021 Free Software Foundation, Inc. # # This file is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, # with or without modifications, as long as this notice is preserved. # This macro actually does too much. Some checks are only needed if # your package does certain things. But this isn't really a big deal. dnl Redefine AC_PROG_CC to automatically invoke _AM_PROG_CC_C_O. m4_define([AC_PROG_CC], m4_defn([AC_PROG_CC]) [_AM_PROG_CC_C_O ]) # AM_INIT_AUTOMAKE(PACKAGE, VERSION, [NO-DEFINE]) # AM_INIT_AUTOMAKE([OPTIONS]) # ----------------------------------------------- # The call with PACKAGE and VERSION arguments is the old style # call (pre autoconf-2.50), which is being phased out. PACKAGE # and VERSION should now be passed to AC_INIT and removed from # the call to AM_INIT_AUTOMAKE. # We support both call styles for the transition. After # the next Automake release, Autoconf can make the AC_INIT # arguments mandatory, and then we can depend on a new Autoconf # release and drop the old call support. AC_DEFUN([AM_INIT_AUTOMAKE], [AC_PREREQ([2.65])dnl m4_ifdef([_$0_ALREADY_INIT], [m4_fatal([$0 expanded multiple times ]m4_defn([_$0_ALREADY_INIT]))], [m4_define([_$0_ALREADY_INIT], m4_expansion_stack)])dnl dnl Autoconf wants to disallow AM_ names. We explicitly allow dnl the ones we care about. m4_pattern_allow([^AM_[A-Z]+FLAGS$])dnl AC_REQUIRE([AM_SET_CURRENT_AUTOMAKE_VERSION])dnl AC_REQUIRE([AC_PROG_INSTALL])dnl if test "`cd $srcdir && pwd`" != "`pwd`"; then # Use -I$(srcdir) only when $(srcdir) != ., so that make's output # is not polluted with repeated "-I." AC_SUBST([am__isrc], [' -I$(srcdir)'])_AM_SUBST_NOTMAKE([am__isrc])dnl # test to see if srcdir already configured if test -f $srcdir/config.status; then AC_MSG_ERROR([source directory already configured; run "make distclean" there first]) fi fi # test whether we have cygpath if test -z "$CYGPATH_W"; then if (cygpath --version) >/dev/null 2>/dev/null; then CYGPATH_W='cygpath -w' else CYGPATH_W=echo fi fi AC_SUBST([CYGPATH_W]) # Define the identity of the package. dnl Distinguish between old-style and new-style calls. m4_ifval([$2], [AC_DIAGNOSE([obsolete], [$0: two- and three-arguments forms are deprecated.]) m4_ifval([$3], [_AM_SET_OPTION([no-define])])dnl AC_SUBST([PACKAGE], [$1])dnl AC_SUBST([VERSION], [$2])], [_AM_SET_OPTIONS([$1])dnl dnl Diagnose old-style AC_INIT with new-style AM_AUTOMAKE_INIT. m4_if( m4_ifset([AC_PACKAGE_NAME], [ok]):m4_ifset([AC_PACKAGE_VERSION], [ok]), [ok:ok],, [m4_fatal([AC_INIT should be called with package and version arguments])])dnl AC_SUBST([PACKAGE], ['AC_PACKAGE_TARNAME'])dnl AC_SUBST([VERSION], ['AC_PACKAGE_VERSION'])])dnl _AM_IF_OPTION([no-define],, [AC_DEFINE_UNQUOTED([PACKAGE], ["$PACKAGE"], [Name of package]) AC_DEFINE_UNQUOTED([VERSION], ["$VERSION"], [Version number of package])])dnl # Some tools Automake needs. AC_REQUIRE([AM_SANITY_CHECK])dnl AC_REQUIRE([AC_ARG_PROGRAM])dnl AM_MISSING_PROG([ACLOCAL], [aclocal-${am__api_version}]) AM_MISSING_PROG([AUTOCONF], [autoconf]) AM_MISSING_PROG([AUTOMAKE], [automake-${am__api_version}]) AM_MISSING_PROG([AUTOHEADER], [autoheader]) AM_MISSING_PROG([MAKEINFO], [makeinfo]) AC_REQUIRE([AM_PROG_INSTALL_SH])dnl AC_REQUIRE([AM_PROG_INSTALL_STRIP])dnl AC_REQUIRE([AC_PROG_MKDIR_P])dnl # For better backward compatibility. To be removed once Automake 1.9.x # dies out for good. For more background, see: # # AC_SUBST([mkdir_p], ['$(MKDIR_P)']) # We need awk for the "check" target (and possibly the TAP driver). The # system "awk" is bad on some platforms. AC_REQUIRE([AC_PROG_AWK])dnl AC_REQUIRE([AC_PROG_MAKE_SET])dnl AC_REQUIRE([AM_SET_LEADING_DOT])dnl _AM_IF_OPTION([tar-ustar], [_AM_PROG_TAR([ustar])], [_AM_IF_OPTION([tar-pax], [_AM_PROG_TAR([pax])], [_AM_PROG_TAR([v7])])]) _AM_IF_OPTION([no-dependencies],, [AC_PROVIDE_IFELSE([AC_PROG_CC], [_AM_DEPENDENCIES([CC])], [m4_define([AC_PROG_CC], m4_defn([AC_PROG_CC])[_AM_DEPENDENCIES([CC])])])dnl AC_PROVIDE_IFELSE([AC_PROG_CXX], [_AM_DEPENDENCIES([CXX])], [m4_define([AC_PROG_CXX], m4_defn([AC_PROG_CXX])[_AM_DEPENDENCIES([CXX])])])dnl AC_PROVIDE_IFELSE([AC_PROG_OBJC], [_AM_DEPENDENCIES([OBJC])], [m4_define([AC_PROG_OBJC], m4_defn([AC_PROG_OBJC])[_AM_DEPENDENCIES([OBJC])])])dnl AC_PROVIDE_IFELSE([AC_PROG_OBJCXX], [_AM_DEPENDENCIES([OBJCXX])], [m4_define([AC_PROG_OBJCXX], m4_defn([AC_PROG_OBJCXX])[_AM_DEPENDENCIES([OBJCXX])])])dnl ]) # Variables for tags utilities; see am/tags.am if test -z "$CTAGS"; then CTAGS=ctags fi AC_SUBST([CTAGS]) if test -z "$ETAGS"; then ETAGS=etags fi AC_SUBST([ETAGS]) if test -z "$CSCOPE"; then CSCOPE=cscope fi AC_SUBST([CSCOPE]) AC_REQUIRE([AM_SILENT_RULES])dnl dnl The testsuite driver may need to know about EXEEXT, so add the dnl 'am__EXEEXT' conditional if _AM_COMPILER_EXEEXT was seen. This dnl macro is hooked onto _AC_COMPILER_EXEEXT early, see below. AC_CONFIG_COMMANDS_PRE(dnl [m4_provide_if([_AM_COMPILER_EXEEXT], [AM_CONDITIONAL([am__EXEEXT], [test -n "$EXEEXT"])])])dnl # POSIX will say in a future version that running "rm -f" with no argument # is OK; and we want to be able to make that assumption in our Makefile # recipes. So use an aggressive probe to check that the usage we want is # actually supported "in the wild" to an acceptable degree. # See automake bug#10828. # To make any issue more visible, cause the running configure to be aborted # by default if the 'rm' program in use doesn't match our expectations; the # user can still override this though. if rm -f && rm -fr && rm -rf; then : OK; else cat >&2 <<'END' Oops! Your 'rm' program seems unable to run without file operands specified on the command line, even when the '-f' option is present. This is contrary to the behaviour of most rm programs out there, and not conforming with the upcoming POSIX standard: Please tell bug-automake@gnu.org about your system, including the value of your $PATH and any error possibly output before this message. This can help us improve future automake versions. END if test x"$ACCEPT_INFERIOR_RM_PROGRAM" = x"yes"; then echo 'Configuration will proceed anyway, since you have set the' >&2 echo 'ACCEPT_INFERIOR_RM_PROGRAM variable to "yes"' >&2 echo >&2 else cat >&2 <<'END' Aborting the configuration process, to ensure you take notice of the issue. You can download and install GNU coreutils to get an 'rm' implementation that behaves properly: . If you want to complete the configuration process using your problematic 'rm' anyway, export the environment variable ACCEPT_INFERIOR_RM_PROGRAM to "yes", and re-run configure. END AC_MSG_ERROR([Your 'rm' program is bad, sorry.]) fi fi dnl The trailing newline in this macro's definition is deliberate, for dnl backward compatibility and to allow trailing 'dnl'-style comments dnl after the AM_INIT_AUTOMAKE invocation. See automake bug#16841. ]) dnl Hook into '_AC_COMPILER_EXEEXT' early to learn its expansion. Do not dnl add the conditional right here, as _AC_COMPILER_EXEEXT may be further dnl mangled by Autoconf and run in a shell conditional statement. m4_define([_AC_COMPILER_EXEEXT], m4_defn([_AC_COMPILER_EXEEXT])[m4_provide([_AM_COMPILER_EXEEXT])]) # When config.status generates a header, we must update the stamp-h file. # This file resides in the same directory as the config header # that is generated. The stamp files are numbered to have different names. # Autoconf calls _AC_AM_CONFIG_HEADER_HOOK (when defined) in the # loop where config.status creates the headers, so we can generate # our stamp files there. AC_DEFUN([_AC_AM_CONFIG_HEADER_HOOK], [# Compute $1's index in $config_headers. _am_arg=$1 _am_stamp_count=1 for _am_header in $config_headers :; do case $_am_header in $_am_arg | $_am_arg:* ) break ;; * ) _am_stamp_count=`expr $_am_stamp_count + 1` ;; esac done echo "timestamp for $_am_arg" >`AS_DIRNAME(["$_am_arg"])`/stamp-h[]$_am_stamp_count]) # Copyright (C) 2001-2021 Free Software Foundation, Inc. # # This file is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, # with or without modifications, as long as this notice is preserved. # AM_PROG_INSTALL_SH # ------------------ # Define $install_sh. AC_DEFUN([AM_PROG_INSTALL_SH], [AC_REQUIRE([AM_AUX_DIR_EXPAND])dnl if test x"${install_sh+set}" != xset; then case $am_aux_dir in *\ * | *\ *) install_sh="\${SHELL} '$am_aux_dir/install-sh'" ;; *) install_sh="\${SHELL} $am_aux_dir/install-sh" esac fi AC_SUBST([install_sh])]) # Copyright (C) 2003-2021 Free Software Foundation, Inc. # # This file is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, # with or without modifications, as long as this notice is preserved. # Check whether the underlying file-system supports filenames # with a leading dot. For instance MS-DOS doesn't. AC_DEFUN([AM_SET_LEADING_DOT], [rm -rf .tst 2>/dev/null mkdir .tst 2>/dev/null if test -d .tst; then am__leading_dot=. else am__leading_dot=_ fi rmdir .tst 2>/dev/null AC_SUBST([am__leading_dot])]) # Add --enable-maintainer-mode option to configure. -*- Autoconf -*- # From Jim Meyering # Copyright (C) 1996-2021 Free Software Foundation, Inc. # # This file is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, # with or without modifications, as long as this notice is preserved. # AM_MAINTAINER_MODE([DEFAULT-MODE]) # ---------------------------------- # Control maintainer-specific portions of Makefiles. # Default is to disable them, unless 'enable' is passed literally. # For symmetry, 'disable' may be passed as well. Anyway, the user # can override the default with the --enable/--disable switch. AC_DEFUN([AM_MAINTAINER_MODE], [m4_case(m4_default([$1], [disable]), [enable], [m4_define([am_maintainer_other], [disable])], [disable], [m4_define([am_maintainer_other], [enable])], [m4_define([am_maintainer_other], [enable]) m4_warn([syntax], [unexpected argument to AM@&t@_MAINTAINER_MODE: $1])]) AC_MSG_CHECKING([whether to enable maintainer-specific portions of Makefiles]) dnl maintainer-mode's default is 'disable' unless 'enable' is passed AC_ARG_ENABLE([maintainer-mode], [AS_HELP_STRING([--]am_maintainer_other[-maintainer-mode], am_maintainer_other[ make rules and dependencies not useful (and sometimes confusing) to the casual installer])], [USE_MAINTAINER_MODE=$enableval], [USE_MAINTAINER_MODE=]m4_if(am_maintainer_other, [enable], [no], [yes])) AC_MSG_RESULT([$USE_MAINTAINER_MODE]) AM_CONDITIONAL([MAINTAINER_MODE], [test $USE_MAINTAINER_MODE = yes]) MAINT=$MAINTAINER_MODE_TRUE AC_SUBST([MAINT])dnl ] ) # Check to see how 'make' treats includes. -*- Autoconf -*- # Copyright (C) 2001-2021 Free Software Foundation, Inc. # # This file is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, # with or without modifications, as long as this notice is preserved. # AM_MAKE_INCLUDE() # ----------------- # Check whether make has an 'include' directive that can support all # the idioms we need for our automatic dependency tracking code. AC_DEFUN([AM_MAKE_INCLUDE], [AC_MSG_CHECKING([whether ${MAKE-make} supports the include directive]) cat > confinc.mk << 'END' am__doit: @echo this is the am__doit target >confinc.out .PHONY: am__doit END am__include="#" am__quote= # BSD make does it like this. echo '.include "confinc.mk" # ignored' > confmf.BSD # Other make implementations (GNU, Solaris 10, AIX) do it like this. echo 'include confinc.mk # ignored' > confmf.GNU _am_result=no for s in GNU BSD; do AM_RUN_LOG([${MAKE-make} -f confmf.$s && cat confinc.out]) AS_CASE([$?:`cat confinc.out 2>/dev/null`], ['0:this is the am__doit target'], [AS_CASE([$s], [BSD], [am__include='.include' am__quote='"'], [am__include='include' am__quote=''])]) if test "$am__include" != "#"; then _am_result="yes ($s style)" break fi done rm -f confinc.* confmf.* AC_MSG_RESULT([${_am_result}]) AC_SUBST([am__include])]) AC_SUBST([am__quote])]) # Fake the existence of programs that GNU maintainers use. -*- Autoconf -*- # Copyright (C) 1997-2021 Free Software Foundation, Inc. # # This file is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, # with or without modifications, as long as this notice is preserved. # AM_MISSING_PROG(NAME, PROGRAM) # ------------------------------ AC_DEFUN([AM_MISSING_PROG], [AC_REQUIRE([AM_MISSING_HAS_RUN]) $1=${$1-"${am_missing_run}$2"} AC_SUBST($1)]) # AM_MISSING_HAS_RUN # ------------------ # Define MISSING if not defined so far and test if it is modern enough. # If it is, set am_missing_run to use it, otherwise, to nothing. AC_DEFUN([AM_MISSING_HAS_RUN], [AC_REQUIRE([AM_AUX_DIR_EXPAND])dnl AC_REQUIRE_AUX_FILE([missing])dnl if test x"${MISSING+set}" != xset; then MISSING="\${SHELL} '$am_aux_dir/missing'" fi # Use eval to expand $SHELL if eval "$MISSING --is-lightweight"; then am_missing_run="$MISSING " else am_missing_run= AC_MSG_WARN(['missing' script is too old or missing]) fi ]) # Helper functions for option handling. -*- Autoconf -*- # Copyright (C) 2001-2021 Free Software Foundation, Inc. # # This file is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, # with or without modifications, as long as this notice is preserved. # _AM_MANGLE_OPTION(NAME) # ----------------------- AC_DEFUN([_AM_MANGLE_OPTION], [[_AM_OPTION_]m4_bpatsubst($1, [[^a-zA-Z0-9_]], [_])]) # _AM_SET_OPTION(NAME) # -------------------- # Set option NAME. Presently that only means defining a flag for this option. AC_DEFUN([_AM_SET_OPTION], [m4_define(_AM_MANGLE_OPTION([$1]), [1])]) # _AM_SET_OPTIONS(OPTIONS) # ------------------------ # OPTIONS is a space-separated list of Automake options. AC_DEFUN([_AM_SET_OPTIONS], [m4_foreach_w([_AM_Option], [$1], [_AM_SET_OPTION(_AM_Option)])]) # _AM_IF_OPTION(OPTION, IF-SET, [IF-NOT-SET]) # ------------------------------------------- # Execute IF-SET if OPTION is set, IF-NOT-SET otherwise. AC_DEFUN([_AM_IF_OPTION], [m4_ifset(_AM_MANGLE_OPTION([$1]), [$2], [$3])]) # Copyright (C) 1999-2021 Free Software Foundation, Inc. # # This file is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, # with or without modifications, as long as this notice is preserved. # _AM_PROG_CC_C_O # --------------- # Like AC_PROG_CC_C_O, but changed for automake. We rewrite AC_PROG_CC # to automatically call this. AC_DEFUN([_AM_PROG_CC_C_O], [AC_REQUIRE([AM_AUX_DIR_EXPAND])dnl AC_REQUIRE_AUX_FILE([compile])dnl AC_LANG_PUSH([C])dnl AC_CACHE_CHECK( [whether $CC understands -c and -o together], [am_cv_prog_cc_c_o], [AC_LANG_CONFTEST([AC_LANG_PROGRAM([])]) # Make sure it works both with $CC and with simple cc. # Following AC_PROG_CC_C_O, we do the test twice because some # compilers refuse to overwrite an existing .o file with -o, # though they will create one. am_cv_prog_cc_c_o=yes for am_i in 1 2; do if AM_RUN_LOG([$CC -c conftest.$ac_ext -o conftest2.$ac_objext]) \ && test -f conftest2.$ac_objext; then : OK else am_cv_prog_cc_c_o=no break fi done rm -f core conftest* unset am_i]) if test "$am_cv_prog_cc_c_o" != yes; then # Losing compiler, so override with the script. # FIXME: It is wrong to rewrite CC. # But if we don't then we get into trouble of one sort or another. # A longer-term fix would be to have automake use am__CC in this case, # and then we could set am__CC="\$(top_srcdir)/compile \$(CC)" CC="$am_aux_dir/compile $CC" fi AC_LANG_POP([C])]) # For backward compatibility. AC_DEFUN_ONCE([AM_PROG_CC_C_O], [AC_REQUIRE([AC_PROG_CC])]) # Copyright (C) 2001-2021 Free Software Foundation, Inc. # # This file is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, # with or without modifications, as long as this notice is preserved. # AM_RUN_LOG(COMMAND) # ------------------- # Run COMMAND, save the exit status in ac_status, and log it. # (This has been adapted from Autoconf's _AC_RUN_LOG macro.) AC_DEFUN([AM_RUN_LOG], [{ echo "$as_me:$LINENO: $1" >&AS_MESSAGE_LOG_FD ($1) >&AS_MESSAGE_LOG_FD 2>&AS_MESSAGE_LOG_FD ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&AS_MESSAGE_LOG_FD (exit $ac_status); }]) # Check to make sure that the build environment is sane. -*- Autoconf -*- # Copyright (C) 1996-2021 Free Software Foundation, Inc. # # This file is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, # with or without modifications, as long as this notice is preserved. # AM_SANITY_CHECK # --------------- AC_DEFUN([AM_SANITY_CHECK], [AC_MSG_CHECKING([whether build environment is sane]) # Reject unsafe characters in $srcdir or the absolute working directory # name. Accept space and tab only in the latter. am_lf=' ' case `pwd` in *[[\\\"\#\$\&\'\`$am_lf]]*) AC_MSG_ERROR([unsafe absolute working directory name]);; esac case $srcdir in *[[\\\"\#\$\&\'\`$am_lf\ \ ]]*) AC_MSG_ERROR([unsafe srcdir value: '$srcdir']);; esac # Do 'set' in a subshell so we don't clobber the current shell's # arguments. Must try -L first in case configure is actually a # symlink; some systems play weird games with the mod time of symlinks # (eg FreeBSD returns the mod time of the symlink's containing # directory). if ( am_has_slept=no for am_try in 1 2; do echo "timestamp, slept: $am_has_slept" > conftest.file set X `ls -Lt "$srcdir/configure" conftest.file 2> /dev/null` if test "$[*]" = "X"; then # -L didn't work. set X `ls -t "$srcdir/configure" conftest.file` fi if test "$[*]" != "X $srcdir/configure conftest.file" \ && test "$[*]" != "X conftest.file $srcdir/configure"; then # If neither matched, then we have a broken ls. This can happen # if, for instance, CONFIG_SHELL is bash and it inherits a # broken ls alias from the environment. This has actually # happened. Such a system could not be considered "sane". AC_MSG_ERROR([ls -t appears to fail. Make sure there is not a broken alias in your environment]) fi if test "$[2]" = conftest.file || test $am_try -eq 2; then break fi # Just in case. sleep 1 am_has_slept=yes done test "$[2]" = conftest.file ) then # Ok. : else AC_MSG_ERROR([newly created file is older than distributed files! Check your system clock]) fi AC_MSG_RESULT([yes]) # If we didn't sleep, we still need to ensure time stamps of config.status and # generated files are strictly newer. am_sleep_pid= if grep 'slept: no' conftest.file >/dev/null 2>&1; then ( sleep 1 ) & am_sleep_pid=$! fi AC_CONFIG_COMMANDS_PRE( [AC_MSG_CHECKING([that generated files are newer than configure]) if test -n "$am_sleep_pid"; then # Hide warnings about reused PIDs. wait $am_sleep_pid 2>/dev/null fi AC_MSG_RESULT([done])]) rm -f conftest.file ]) # Copyright (C) 2009-2021 Free Software Foundation, Inc. # # This file is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, # with or without modifications, as long as this notice is preserved. # AM_SILENT_RULES([DEFAULT]) # -------------------------- # Enable less verbose build rules; with the default set to DEFAULT # ("yes" being less verbose, "no" or empty being verbose). AC_DEFUN([AM_SILENT_RULES], [AC_ARG_ENABLE([silent-rules], [dnl AS_HELP_STRING( [--enable-silent-rules], [less verbose build output (undo: "make V=1")]) AS_HELP_STRING( [--disable-silent-rules], [verbose build output (undo: "make V=0")])dnl ]) case $enable_silent_rules in @%:@ ((( yes) AM_DEFAULT_VERBOSITY=0;; no) AM_DEFAULT_VERBOSITY=1;; *) AM_DEFAULT_VERBOSITY=m4_if([$1], [yes], [0], [1]);; esac dnl dnl A few 'make' implementations (e.g., NonStop OS and NextStep) dnl do not support nested variable expansions. dnl See automake bug#9928 and bug#10237. am_make=${MAKE-make} AC_CACHE_CHECK([whether $am_make supports nested variables], [am_cv_make_support_nested_variables], [if AS_ECHO([['TRUE=$(BAR$(V)) BAR0=false BAR1=true V=1 am__doit: @$(TRUE) .PHONY: am__doit']]) | $am_make -f - >/dev/null 2>&1; then am_cv_make_support_nested_variables=yes else am_cv_make_support_nested_variables=no fi]) if test $am_cv_make_support_nested_variables = yes; then dnl Using '$V' instead of '$(V)' breaks IRIX make. AM_V='$(V)' AM_DEFAULT_V='$(AM_DEFAULT_VERBOSITY)' else AM_V=$AM_DEFAULT_VERBOSITY AM_DEFAULT_V=$AM_DEFAULT_VERBOSITY fi AC_SUBST([AM_V])dnl AM_SUBST_NOTMAKE([AM_V])dnl AC_SUBST([AM_DEFAULT_V])dnl AM_SUBST_NOTMAKE([AM_DEFAULT_V])dnl AC_SUBST([AM_DEFAULT_VERBOSITY])dnl AM_BACKSLASH='\' AC_SUBST([AM_BACKSLASH])dnl _AM_SUBST_NOTMAKE([AM_BACKSLASH])dnl ]) # Copyright (C) 2001-2021 Free Software Foundation, Inc. # # This file is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, # with or without modifications, as long as this notice is preserved. # AM_PROG_INSTALL_STRIP # --------------------- # One issue with vendor 'install' (even GNU) is that you can't # specify the program used to strip binaries. This is especially # annoying in cross-compiling environments, where the build's strip # is unlikely to handle the host's binaries. # Fortunately install-sh will honor a STRIPPROG variable, so we # always use install-sh in "make install-strip", and initialize # STRIPPROG with the value of the STRIP variable (set by the user). AC_DEFUN([AM_PROG_INSTALL_STRIP], [AC_REQUIRE([AM_PROG_INSTALL_SH])dnl # Installed binaries are usually stripped using 'strip' when the user # run "make install-strip". However 'strip' might not be the right # tool to use in cross-compilation environments, therefore Automake # will honor the 'STRIP' environment variable to overrule this program. dnl Don't test for $cross_compiling = yes, because it might be 'maybe'. if test "$cross_compiling" != no; then AC_CHECK_TOOL([STRIP], [strip], :) fi INSTALL_STRIP_PROGRAM="\$(install_sh) -c -s" AC_SUBST([INSTALL_STRIP_PROGRAM])]) # Copyright (C) 2006-2021 Free Software Foundation, Inc. # # This file is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, # with or without modifications, as long as this notice is preserved. # _AM_SUBST_NOTMAKE(VARIABLE) # --------------------------- # Prevent Automake from outputting VARIABLE = @VARIABLE@ in Makefile.in. # This macro is traced by Automake. AC_DEFUN([_AM_SUBST_NOTMAKE]) # AM_SUBST_NOTMAKE(VARIABLE) # -------------------------- # Public sister of _AM_SUBST_NOTMAKE. AC_DEFUN([AM_SUBST_NOTMAKE], [_AM_SUBST_NOTMAKE($@)]) # Check how to create a tarball. -*- Autoconf -*- # Copyright (C) 2004-2021 Free Software Foundation, Inc. # # This file is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, # with or without modifications, as long as this notice is preserved. # _AM_PROG_TAR(FORMAT) # -------------------- # Check how to create a tarball in format FORMAT. # FORMAT should be one of 'v7', 'ustar', or 'pax'. # # Substitute a variable $(am__tar) that is a command # writing to stdout a FORMAT-tarball containing the directory # $tardir. # tardir=directory && $(am__tar) > result.tar # # Substitute a variable $(am__untar) that extract such # a tarball read from stdin. # $(am__untar) < result.tar # AC_DEFUN([_AM_PROG_TAR], [# Always define AMTAR for backward compatibility. Yes, it's still used # in the wild :-( We should find a proper way to deprecate it ... AC_SUBST([AMTAR], ['$${TAR-tar}']) # We'll loop over all known methods to create a tar archive until one works. _am_tools='gnutar m4_if([$1], [ustar], [plaintar]) pax cpio none' m4_if([$1], [v7], [am__tar='$${TAR-tar} chof - "$$tardir"' am__untar='$${TAR-tar} xf -'], [m4_case([$1], [ustar], [# The POSIX 1988 'ustar' format is defined with fixed-size fields. # There is notably a 21 bits limit for the UID and the GID. In fact, # the 'pax' utility can hang on bigger UID/GID (see automake bug#8343 # and bug#13588). am_max_uid=2097151 # 2^21 - 1 am_max_gid=$am_max_uid # The $UID and $GID variables are not portable, so we need to resort # to the POSIX-mandated id(1) utility. Errors in the 'id' calls # below are definitely unexpected, so allow the users to see them # (that is, avoid stderr redirection). am_uid=`id -u || echo unknown` am_gid=`id -g || echo unknown` AC_MSG_CHECKING([whether UID '$am_uid' is supported by ustar format]) if test $am_uid -le $am_max_uid; then AC_MSG_RESULT([yes]) else AC_MSG_RESULT([no]) _am_tools=none fi AC_MSG_CHECKING([whether GID '$am_gid' is supported by ustar format]) if test $am_gid -le $am_max_gid; then AC_MSG_RESULT([yes]) else AC_MSG_RESULT([no]) _am_tools=none fi], [pax], [], [m4_fatal([Unknown tar format])]) AC_MSG_CHECKING([how to create a $1 tar archive]) # Go ahead even if we have the value already cached. We do so because we # need to set the values for the 'am__tar' and 'am__untar' variables. _am_tools=${am_cv_prog_tar_$1-$_am_tools} for _am_tool in $_am_tools; do case $_am_tool in gnutar) for _am_tar in tar gnutar gtar; do AM_RUN_LOG([$_am_tar --version]) && break done am__tar="$_am_tar --format=m4_if([$1], [pax], [posix], [$1]) -chf - "'"$$tardir"' am__tar_="$_am_tar --format=m4_if([$1], [pax], [posix], [$1]) -chf - "'"$tardir"' am__untar="$_am_tar -xf -" ;; plaintar) # Must skip GNU tar: if it does not support --format= it doesn't create # ustar tarball either. (tar --version) >/dev/null 2>&1 && continue am__tar='tar chf - "$$tardir"' am__tar_='tar chf - "$tardir"' am__untar='tar xf -' ;; pax) am__tar='pax -L -x $1 -w "$$tardir"' am__tar_='pax -L -x $1 -w "$tardir"' am__untar='pax -r' ;; cpio) am__tar='find "$$tardir" -print | cpio -o -H $1 -L' am__tar_='find "$tardir" -print | cpio -o -H $1 -L' am__untar='cpio -i -H $1 -d' ;; none) am__tar=false am__tar_=false am__untar=false ;; esac # If the value was cached, stop now. We just wanted to have am__tar # and am__untar set. test -n "${am_cv_prog_tar_$1}" && break # tar/untar a dummy directory, and stop if the command works. rm -rf conftest.dir mkdir conftest.dir echo GrepMe > conftest.dir/file AM_RUN_LOG([tardir=conftest.dir && eval $am__tar_ >conftest.tar]) rm -rf conftest.dir if test -s conftest.tar; then AM_RUN_LOG([$am__untar /dev/null 2>&1 && break fi done rm -rf conftest.dir AC_CACHE_VAL([am_cv_prog_tar_$1], [am_cv_prog_tar_$1=$_am_tool]) AC_MSG_RESULT([$am_cv_prog_tar_$1])]) AC_SUBST([am__tar]) AC_SUBST([am__untar]) ]) # _AM_PROG_TAR m4_include([acinclude.m4]) bluez-5.82/PaxHeaders/depcomp0000644000000000000000000000005014773211373013230 xustar0020 atime=1743590149 20 ctime=1743591275 bluez-5.82/depcomp0000755000000000000000000005602014773211373012717 0ustar00rootroot#! /bin/sh # depcomp - compile a program generating dependencies as side-effects scriptversion=2018-03-07.03; # UTC # Copyright (C) 1999-2021 Free Software Foundation, Inc. # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2, or (at your option) # any later version. # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # You should have received a copy of the GNU General Public License # along with this program. If not, see . # As a special exception to the GNU General Public License, if you # distribute this file as part of a program that contains a # configuration script generated by Autoconf, you may include it under # the same distribution terms that you use for the rest of that program. # Originally written by Alexandre Oliva . case $1 in '') echo "$0: No command. Try '$0 --help' for more information." 1>&2 exit 1; ;; -h | --h*) cat <<\EOF Usage: depcomp [--help] [--version] PROGRAM [ARGS] Run PROGRAMS ARGS to compile a file, generating dependencies as side-effects. Environment variables: depmode Dependency tracking mode. source Source file read by 'PROGRAMS ARGS'. object Object file output by 'PROGRAMS ARGS'. DEPDIR directory where to store dependencies. depfile Dependency file to output. tmpdepfile Temporary file to use when outputting dependencies. libtool Whether libtool is used (yes/no). Report bugs to . EOF exit $? ;; -v | --v*) echo "depcomp $scriptversion" exit $? ;; esac # Get the directory component of the given path, and save it in the # global variables '$dir'. Note that this directory component will # be either empty or ending with a '/' character. This is deliberate. set_dir_from () { case $1 in */*) dir=`echo "$1" | sed -e 's|/[^/]*$|/|'`;; *) dir=;; esac } # Get the suffix-stripped basename of the given path, and save it the # global variable '$base'. set_base_from () { base=`echo "$1" | sed -e 's|^.*/||' -e 's/\.[^.]*$//'` } # If no dependency file was actually created by the compiler invocation, # we still have to create a dummy depfile, to avoid errors with the # Makefile "include basename.Plo" scheme. make_dummy_depfile () { echo "#dummy" > "$depfile" } # Factor out some common post-processing of the generated depfile. # Requires the auxiliary global variable '$tmpdepfile' to be set. aix_post_process_depfile () { # If the compiler actually managed to produce a dependency file, # post-process it. if test -f "$tmpdepfile"; then # Each line is of the form 'foo.o: dependency.h'. # Do two passes, one to just change these to # $object: dependency.h # and one to simply output # dependency.h: # which is needed to avoid the deleted-header problem. { sed -e "s,^.*\.[$lower]*:,$object:," < "$tmpdepfile" sed -e "s,^.*\.[$lower]*:[$tab ]*,," -e 's,$,:,' < "$tmpdepfile" } > "$depfile" rm -f "$tmpdepfile" else make_dummy_depfile fi } # A tabulation character. tab=' ' # A newline character. nl=' ' # Character ranges might be problematic outside the C locale. # These definitions help. upper=ABCDEFGHIJKLMNOPQRSTUVWXYZ lower=abcdefghijklmnopqrstuvwxyz digits=0123456789 alpha=${upper}${lower} if test -z "$depmode" || test -z "$source" || test -z "$object"; then echo "depcomp: Variables source, object and depmode must be set" 1>&2 exit 1 fi # Dependencies for sub/bar.o or sub/bar.obj go into sub/.deps/bar.Po. depfile=${depfile-`echo "$object" | sed 's|[^\\/]*$|'${DEPDIR-.deps}'/&|;s|\.\([^.]*\)$|.P\1|;s|Pobj$|Po|'`} tmpdepfile=${tmpdepfile-`echo "$depfile" | sed 's/\.\([^.]*\)$/.T\1/'`} rm -f "$tmpdepfile" # Avoid interferences from the environment. gccflag= dashmflag= # Some modes work just like other modes, but use different flags. We # parameterize here, but still list the modes in the big case below, # to make depend.m4 easier to write. Note that we *cannot* use a case # here, because this file can only contain one case statement. if test "$depmode" = hp; then # HP compiler uses -M and no extra arg. gccflag=-M depmode=gcc fi if test "$depmode" = dashXmstdout; then # This is just like dashmstdout with a different argument. dashmflag=-xM depmode=dashmstdout fi cygpath_u="cygpath -u -f -" if test "$depmode" = msvcmsys; then # This is just like msvisualcpp but w/o cygpath translation. # Just convert the backslash-escaped backslashes to single forward # slashes to satisfy depend.m4 cygpath_u='sed s,\\\\,/,g' depmode=msvisualcpp fi if test "$depmode" = msvc7msys; then # This is just like msvc7 but w/o cygpath translation. # Just convert the backslash-escaped backslashes to single forward # slashes to satisfy depend.m4 cygpath_u='sed s,\\\\,/,g' depmode=msvc7 fi if test "$depmode" = xlc; then # IBM C/C++ Compilers xlc/xlC can output gcc-like dependency information. gccflag=-qmakedep=gcc,-MF depmode=gcc fi case "$depmode" in gcc3) ## gcc 3 implements dependency tracking that does exactly what ## we want. Yay! Note: for some reason libtool 1.4 doesn't like ## it if -MD -MP comes after the -MF stuff. Hmm. ## Unfortunately, FreeBSD c89 acceptance of flags depends upon ## the command line argument order; so add the flags where they ## appear in depend2.am. Note that the slowdown incurred here ## affects only configure: in makefiles, %FASTDEP% shortcuts this. for arg do case $arg in -c) set fnord "$@" -MT "$object" -MD -MP -MF "$tmpdepfile" "$arg" ;; *) set fnord "$@" "$arg" ;; esac shift # fnord shift # $arg done "$@" stat=$? if test $stat -ne 0; then rm -f "$tmpdepfile" exit $stat fi mv "$tmpdepfile" "$depfile" ;; gcc) ## Note that this doesn't just cater to obsosete pre-3.x GCC compilers. ## but also to in-use compilers like IMB xlc/xlC and the HP C compiler. ## (see the conditional assignment to $gccflag above). ## There are various ways to get dependency output from gcc. Here's ## why we pick this rather obscure method: ## - Don't want to use -MD because we'd like the dependencies to end ## up in a subdir. Having to rename by hand is ugly. ## (We might end up doing this anyway to support other compilers.) ## - The DEPENDENCIES_OUTPUT environment variable makes gcc act like ## -MM, not -M (despite what the docs say). Also, it might not be ## supported by the other compilers which use the 'gcc' depmode. ## - Using -M directly means running the compiler twice (even worse ## than renaming). if test -z "$gccflag"; then gccflag=-MD, fi "$@" -Wp,"$gccflag$tmpdepfile" stat=$? if test $stat -ne 0; then rm -f "$tmpdepfile" exit $stat fi rm -f "$depfile" echo "$object : \\" > "$depfile" # The second -e expression handles DOS-style file names with drive # letters. sed -e 's/^[^:]*: / /' \ -e 's/^['$alpha']:\/[^:]*: / /' < "$tmpdepfile" >> "$depfile" ## This next piece of magic avoids the "deleted header file" problem. ## The problem is that when a header file which appears in a .P file ## is deleted, the dependency causes make to die (because there is ## typically no way to rebuild the header). We avoid this by adding ## dummy dependencies for each header file. Too bad gcc doesn't do ## this for us directly. ## Some versions of gcc put a space before the ':'. On the theory ## that the space means something, we add a space to the output as ## well. hp depmode also adds that space, but also prefixes the VPATH ## to the object. Take care to not repeat it in the output. ## Some versions of the HPUX 10.20 sed can't process this invocation ## correctly. Breaking it into two sed invocations is a workaround. tr ' ' "$nl" < "$tmpdepfile" \ | sed -e 's/^\\$//' -e '/^$/d' -e "s|.*$object$||" -e '/:$/d' \ | sed -e 's/$/ :/' >> "$depfile" rm -f "$tmpdepfile" ;; hp) # This case exists only to let depend.m4 do its work. It works by # looking at the text of this script. This case will never be run, # since it is checked for above. exit 1 ;; sgi) if test "$libtool" = yes; then "$@" "-Wp,-MDupdate,$tmpdepfile" else "$@" -MDupdate "$tmpdepfile" fi stat=$? if test $stat -ne 0; then rm -f "$tmpdepfile" exit $stat fi rm -f "$depfile" if test -f "$tmpdepfile"; then # yes, the sourcefile depend on other files echo "$object : \\" > "$depfile" # Clip off the initial element (the dependent). Don't try to be # clever and replace this with sed code, as IRIX sed won't handle # lines with more than a fixed number of characters (4096 in # IRIX 6.2 sed, 8192 in IRIX 6.5). We also remove comment lines; # the IRIX cc adds comments like '#:fec' to the end of the # dependency line. tr ' ' "$nl" < "$tmpdepfile" \ | sed -e 's/^.*\.o://' -e 's/#.*$//' -e '/^$/ d' \ | tr "$nl" ' ' >> "$depfile" echo >> "$depfile" # The second pass generates a dummy entry for each header file. tr ' ' "$nl" < "$tmpdepfile" \ | sed -e 's/^.*\.o://' -e 's/#.*$//' -e '/^$/ d' -e 's/$/:/' \ >> "$depfile" else make_dummy_depfile fi rm -f "$tmpdepfile" ;; xlc) # This case exists only to let depend.m4 do its work. It works by # looking at the text of this script. This case will never be run, # since it is checked for above. exit 1 ;; aix) # The C for AIX Compiler uses -M and outputs the dependencies # in a .u file. In older versions, this file always lives in the # current directory. Also, the AIX compiler puts '$object:' at the # start of each line; $object doesn't have directory information. # Version 6 uses the directory in both cases. set_dir_from "$object" set_base_from "$object" if test "$libtool" = yes; then tmpdepfile1=$dir$base.u tmpdepfile2=$base.u tmpdepfile3=$dir.libs/$base.u "$@" -Wc,-M else tmpdepfile1=$dir$base.u tmpdepfile2=$dir$base.u tmpdepfile3=$dir$base.u "$@" -M fi stat=$? if test $stat -ne 0; then rm -f "$tmpdepfile1" "$tmpdepfile2" "$tmpdepfile3" exit $stat fi for tmpdepfile in "$tmpdepfile1" "$tmpdepfile2" "$tmpdepfile3" do test -f "$tmpdepfile" && break done aix_post_process_depfile ;; tcc) # tcc (Tiny C Compiler) understand '-MD -MF file' since version 0.9.26 # FIXME: That version still under development at the moment of writing. # Make that this statement remains true also for stable, released # versions. # It will wrap lines (doesn't matter whether long or short) with a # trailing '\', as in: # # foo.o : \ # foo.c \ # foo.h \ # # It will put a trailing '\' even on the last line, and will use leading # spaces rather than leading tabs (at least since its commit 0394caf7 # "Emit spaces for -MD"). "$@" -MD -MF "$tmpdepfile" stat=$? if test $stat -ne 0; then rm -f "$tmpdepfile" exit $stat fi rm -f "$depfile" # Each non-empty line is of the form 'foo.o : \' or ' dep.h \'. # We have to change lines of the first kind to '$object: \'. sed -e "s|.*:|$object :|" < "$tmpdepfile" > "$depfile" # And for each line of the second kind, we have to emit a 'dep.h:' # dummy dependency, to avoid the deleted-header problem. sed -n -e 's|^ *\(.*\) *\\$|\1:|p' < "$tmpdepfile" >> "$depfile" rm -f "$tmpdepfile" ;; ## The order of this option in the case statement is important, since the ## shell code in configure will try each of these formats in the order ## listed in this file. A plain '-MD' option would be understood by many ## compilers, so we must ensure this comes after the gcc and icc options. pgcc) # Portland's C compiler understands '-MD'. # Will always output deps to 'file.d' where file is the root name of the # source file under compilation, even if file resides in a subdirectory. # The object file name does not affect the name of the '.d' file. # pgcc 10.2 will output # foo.o: sub/foo.c sub/foo.h # and will wrap long lines using '\' : # foo.o: sub/foo.c ... \ # sub/foo.h ... \ # ... set_dir_from "$object" # Use the source, not the object, to determine the base name, since # that's sadly what pgcc will do too. set_base_from "$source" tmpdepfile=$base.d # For projects that build the same source file twice into different object # files, the pgcc approach of using the *source* file root name can cause # problems in parallel builds. Use a locking strategy to avoid stomping on # the same $tmpdepfile. lockdir=$base.d-lock trap " echo '$0: caught signal, cleaning up...' >&2 rmdir '$lockdir' exit 1 " 1 2 13 15 numtries=100 i=$numtries while test $i -gt 0; do # mkdir is a portable test-and-set. if mkdir "$lockdir" 2>/dev/null; then # This process acquired the lock. "$@" -MD stat=$? # Release the lock. rmdir "$lockdir" break else # If the lock is being held by a different process, wait # until the winning process is done or we timeout. while test -d "$lockdir" && test $i -gt 0; do sleep 1 i=`expr $i - 1` done fi i=`expr $i - 1` done trap - 1 2 13 15 if test $i -le 0; then echo "$0: failed to acquire lock after $numtries attempts" >&2 echo "$0: check lockdir '$lockdir'" >&2 exit 1 fi if test $stat -ne 0; then rm -f "$tmpdepfile" exit $stat fi rm -f "$depfile" # Each line is of the form `foo.o: dependent.h', # or `foo.o: dep1.h dep2.h \', or ` dep3.h dep4.h \'. # Do two passes, one to just change these to # `$object: dependent.h' and one to simply `dependent.h:'. sed "s,^[^:]*:,$object :," < "$tmpdepfile" > "$depfile" # Some versions of the HPUX 10.20 sed can't process this invocation # correctly. Breaking it into two sed invocations is a workaround. sed 's,^[^:]*: \(.*\)$,\1,;s/^\\$//;/^$/d;/:$/d' < "$tmpdepfile" \ | sed -e 's/$/ :/' >> "$depfile" rm -f "$tmpdepfile" ;; hp2) # The "hp" stanza above does not work with aCC (C++) and HP's ia64 # compilers, which have integrated preprocessors. The correct option # to use with these is +Maked; it writes dependencies to a file named # 'foo.d', which lands next to the object file, wherever that # happens to be. # Much of this is similar to the tru64 case; see comments there. set_dir_from "$object" set_base_from "$object" if test "$libtool" = yes; then tmpdepfile1=$dir$base.d tmpdepfile2=$dir.libs/$base.d "$@" -Wc,+Maked else tmpdepfile1=$dir$base.d tmpdepfile2=$dir$base.d "$@" +Maked fi stat=$? if test $stat -ne 0; then rm -f "$tmpdepfile1" "$tmpdepfile2" exit $stat fi for tmpdepfile in "$tmpdepfile1" "$tmpdepfile2" do test -f "$tmpdepfile" && break done if test -f "$tmpdepfile"; then sed -e "s,^.*\.[$lower]*:,$object:," "$tmpdepfile" > "$depfile" # Add 'dependent.h:' lines. sed -ne '2,${ s/^ *// s/ \\*$// s/$/:/ p }' "$tmpdepfile" >> "$depfile" else make_dummy_depfile fi rm -f "$tmpdepfile" "$tmpdepfile2" ;; tru64) # The Tru64 compiler uses -MD to generate dependencies as a side # effect. 'cc -MD -o foo.o ...' puts the dependencies into 'foo.o.d'. # At least on Alpha/Redhat 6.1, Compaq CCC V6.2-504 seems to put # dependencies in 'foo.d' instead, so we check for that too. # Subdirectories are respected. set_dir_from "$object" set_base_from "$object" if test "$libtool" = yes; then # Libtool generates 2 separate objects for the 2 libraries. These # two compilations output dependencies in $dir.libs/$base.o.d and # in $dir$base.o.d. We have to check for both files, because # one of the two compilations can be disabled. We should prefer # $dir$base.o.d over $dir.libs/$base.o.d because the latter is # automatically cleaned when .libs/ is deleted, while ignoring # the former would cause a distcleancheck panic. tmpdepfile1=$dir$base.o.d # libtool 1.5 tmpdepfile2=$dir.libs/$base.o.d # Likewise. tmpdepfile3=$dir.libs/$base.d # Compaq CCC V6.2-504 "$@" -Wc,-MD else tmpdepfile1=$dir$base.d tmpdepfile2=$dir$base.d tmpdepfile3=$dir$base.d "$@" -MD fi stat=$? if test $stat -ne 0; then rm -f "$tmpdepfile1" "$tmpdepfile2" "$tmpdepfile3" exit $stat fi for tmpdepfile in "$tmpdepfile1" "$tmpdepfile2" "$tmpdepfile3" do test -f "$tmpdepfile" && break done # Same post-processing that is required for AIX mode. aix_post_process_depfile ;; msvc7) if test "$libtool" = yes; then showIncludes=-Wc,-showIncludes else showIncludes=-showIncludes fi "$@" $showIncludes > "$tmpdepfile" stat=$? grep -v '^Note: including file: ' "$tmpdepfile" if test $stat -ne 0; then rm -f "$tmpdepfile" exit $stat fi rm -f "$depfile" echo "$object : \\" > "$depfile" # The first sed program below extracts the file names and escapes # backslashes for cygpath. The second sed program outputs the file # name when reading, but also accumulates all include files in the # hold buffer in order to output them again at the end. This only # works with sed implementations that can handle large buffers. sed < "$tmpdepfile" -n ' /^Note: including file: *\(.*\)/ { s//\1/ s/\\/\\\\/g p }' | $cygpath_u | sort -u | sed -n ' s/ /\\ /g s/\(.*\)/'"$tab"'\1 \\/p s/.\(.*\) \\/\1:/ H $ { s/.*/'"$tab"'/ G p }' >> "$depfile" echo >> "$depfile" # make sure the fragment doesn't end with a backslash rm -f "$tmpdepfile" ;; msvc7msys) # This case exists only to let depend.m4 do its work. It works by # looking at the text of this script. This case will never be run, # since it is checked for above. exit 1 ;; #nosideeffect) # This comment above is used by automake to tell side-effect # dependency tracking mechanisms from slower ones. dashmstdout) # Important note: in order to support this mode, a compiler *must* # always write the preprocessed file to stdout, regardless of -o. "$@" || exit $? # Remove the call to Libtool. if test "$libtool" = yes; then while test "X$1" != 'X--mode=compile'; do shift done shift fi # Remove '-o $object'. IFS=" " for arg do case $arg in -o) shift ;; $object) shift ;; *) set fnord "$@" "$arg" shift # fnord shift # $arg ;; esac done test -z "$dashmflag" && dashmflag=-M # Require at least two characters before searching for ':' # in the target name. This is to cope with DOS-style filenames: # a dependency such as 'c:/foo/bar' could be seen as target 'c' otherwise. "$@" $dashmflag | sed "s|^[$tab ]*[^:$tab ][^:][^:]*:[$tab ]*|$object: |" > "$tmpdepfile" rm -f "$depfile" cat < "$tmpdepfile" > "$depfile" # Some versions of the HPUX 10.20 sed can't process this sed invocation # correctly. Breaking it into two sed invocations is a workaround. tr ' ' "$nl" < "$tmpdepfile" \ | sed -e 's/^\\$//' -e '/^$/d' -e '/:$/d' \ | sed -e 's/$/ :/' >> "$depfile" rm -f "$tmpdepfile" ;; dashXmstdout) # This case only exists to satisfy depend.m4. It is never actually # run, as this mode is specially recognized in the preamble. exit 1 ;; makedepend) "$@" || exit $? # Remove any Libtool call if test "$libtool" = yes; then while test "X$1" != 'X--mode=compile'; do shift done shift fi # X makedepend shift cleared=no eat=no for arg do case $cleared in no) set ""; shift cleared=yes ;; esac if test $eat = yes; then eat=no continue fi case "$arg" in -D*|-I*) set fnord "$@" "$arg"; shift ;; # Strip any option that makedepend may not understand. Remove # the object too, otherwise makedepend will parse it as a source file. -arch) eat=yes ;; -*|$object) ;; *) set fnord "$@" "$arg"; shift ;; esac done obj_suffix=`echo "$object" | sed 's/^.*\././'` touch "$tmpdepfile" ${MAKEDEPEND-makedepend} -o"$obj_suffix" -f"$tmpdepfile" "$@" rm -f "$depfile" # makedepend may prepend the VPATH from the source file name to the object. # No need to regex-escape $object, excess matching of '.' is harmless. sed "s|^.*\($object *:\)|\1|" "$tmpdepfile" > "$depfile" # Some versions of the HPUX 10.20 sed can't process the last invocation # correctly. Breaking it into two sed invocations is a workaround. sed '1,2d' "$tmpdepfile" \ | tr ' ' "$nl" \ | sed -e 's/^\\$//' -e '/^$/d' -e '/:$/d' \ | sed -e 's/$/ :/' >> "$depfile" rm -f "$tmpdepfile" "$tmpdepfile".bak ;; cpp) # Important note: in order to support this mode, a compiler *must* # always write the preprocessed file to stdout. "$@" || exit $? # Remove the call to Libtool. if test "$libtool" = yes; then while test "X$1" != 'X--mode=compile'; do shift done shift fi # Remove '-o $object'. IFS=" " for arg do case $arg in -o) shift ;; $object) shift ;; *) set fnord "$@" "$arg" shift # fnord shift # $arg ;; esac done "$@" -E \ | sed -n -e '/^# [0-9][0-9]* "\([^"]*\)".*/ s:: \1 \\:p' \ -e '/^#line [0-9][0-9]* "\([^"]*\)".*/ s:: \1 \\:p' \ | sed '$ s: \\$::' > "$tmpdepfile" rm -f "$depfile" echo "$object : \\" > "$depfile" cat < "$tmpdepfile" >> "$depfile" sed < "$tmpdepfile" '/^$/d;s/^ //;s/ \\$//;s/$/ :/' >> "$depfile" rm -f "$tmpdepfile" ;; msvisualcpp) # Important note: in order to support this mode, a compiler *must* # always write the preprocessed file to stdout. "$@" || exit $? # Remove the call to Libtool. if test "$libtool" = yes; then while test "X$1" != 'X--mode=compile'; do shift done shift fi IFS=" " for arg do case "$arg" in -o) shift ;; $object) shift ;; "-Gm"|"/Gm"|"-Gi"|"/Gi"|"-ZI"|"/ZI") set fnord "$@" shift shift ;; *) set fnord "$@" "$arg" shift shift ;; esac done "$@" -E 2>/dev/null | sed -n '/^#line [0-9][0-9]* "\([^"]*\)"/ s::\1:p' | $cygpath_u | sort -u > "$tmpdepfile" rm -f "$depfile" echo "$object : \\" > "$depfile" sed < "$tmpdepfile" -n -e 's% %\\ %g' -e '/^\(.*\)$/ s::'"$tab"'\1 \\:p' >> "$depfile" echo "$tab" >> "$depfile" sed < "$tmpdepfile" -n -e 's% %\\ %g' -e '/^\(.*\)$/ s::\1\::p' >> "$depfile" rm -f "$tmpdepfile" ;; msvcmsys) # This case exists only to let depend.m4 do its work. It works by # looking at the text of this script. This case will never be run, # since it is checked for above. exit 1 ;; none) exec "$@" ;; *) echo "Unknown depmode $depmode" 1>&2 exit 1 ;; esac exit 0 # Local Variables: # mode: shell-script # sh-indentation: 2 # eval: (add-hook 'before-save-hook 'time-stamp) # time-stamp-start: "scriptversion=" # time-stamp-format: "%:y-%02m-%02d.%02H" # time-stamp-time-zone: "UTC0" # time-stamp-end: "; # UTC" # End: bluez-5.82/PaxHeaders/INSTALL0000644000000000000000000000005011071665350012703 xustar0020 atime=1743516862 20 ctime=1743591275 bluez-5.82/INSTALL0000644000000000000000000002240611071665350012370 0ustar00rootrootInstallation Instructions ************************* Copyright (C) 1994, 1995, 1996, 1999, 2000, 2001, 2002, 2004, 2005 Free Software Foundation, Inc. This file is free documentation; the Free Software Foundation gives unlimited permission to copy, distribute and modify it. Basic Installation ================== These are generic installation instructions. The `configure' shell script attempts to guess correct values for various system-dependent variables used during compilation. It uses those values to create a `Makefile' in each directory of the package. It may also create one or more `.h' files containing system-dependent definitions. Finally, it creates a shell script `config.status' that you can run in the future to recreate the current configuration, and a file `config.log' containing compiler output (useful mainly for debugging `configure'). It can also use an optional file (typically called `config.cache' and enabled with `--cache-file=config.cache' or simply `-C') that saves the results of its tests to speed up reconfiguring. (Caching is disabled by default to prevent problems with accidental use of stale cache files.) If you need to do unusual things to compile the package, please try to figure out how `configure' could check whether to do them, and mail diffs or instructions to the address given in the `README' so they can be considered for the next release. If you are using the cache, and at some point `config.cache' contains results you don't want to keep, you may remove or edit it. The file `configure.ac' (or `configure.in') is used to create `configure' by a program called `autoconf'. You only need `configure.ac' if you want to change it or regenerate `configure' using a newer version of `autoconf'. The simplest way to compile this package is: 1. `cd' to the directory containing the package's source code and type `./configure' to configure the package for your system. If you're using `csh' on an old version of System V, you might need to type `sh ./configure' instead to prevent `csh' from trying to execute `configure' itself. Running `configure' takes awhile. While running, it prints some messages telling which features it is checking for. 2. Type `make' to compile the package. 3. Optionally, type `make check' to run any self-tests that come with the package. 4. Type `make install' to install the programs and any data files and documentation. 5. You can remove the program binaries and object files from the source code directory by typing `make clean'. To also remove the files that `configure' created (so you can compile the package for a different kind of computer), type `make distclean'. There is also a `make maintainer-clean' target, but that is intended mainly for the package's developers. If you use it, you may have to get all sorts of other programs in order to regenerate files that came with the distribution. Compilers and Options ===================== Some systems require unusual options for compilation or linking that the `configure' script does not know about. Run `./configure --help' for details on some of the pertinent environment variables. You can give `configure' initial values for configuration parameters by setting variables in the command line or in the environment. Here is an example: ./configure CC=c89 CFLAGS=-O2 LIBS=-lposix *Note Defining Variables::, for more details. Compiling For Multiple Architectures ==================================== You can compile the package for more than one kind of computer at the same time, by placing the object files for each architecture in their own directory. To do this, you must use a version of `make' that supports the `VPATH' variable, such as GNU `make'. `cd' to the directory where you want the object files and executables to go and run the `configure' script. `configure' automatically checks for the source code in the directory that `configure' is in and in `..'. If you have to use a `make' that does not support the `VPATH' variable, you have to compile the package for one architecture at a time in the source code directory. After you have installed the package for one architecture, use `make distclean' before reconfiguring for another architecture. Installation Names ================== By default, `make install' will install the package's files in `/usr/local/bin', `/usr/local/man', etc. You can specify an installation prefix other than `/usr/local' by giving `configure' the option `--prefix=PREFIX'. You can specify separate installation prefixes for architecture-specific files and architecture-independent files. If you give `configure' the option `--exec-prefix=PREFIX', the package will use PREFIX as the prefix for installing programs and libraries. Documentation and other data files will still use the regular prefix. In addition, if you use an unusual directory layout you can give options like `--bindir=DIR' to specify different values for particular kinds of files. Run `configure --help' for a list of the directories you can set and what kinds of files go in them. If the package supports it, you can cause programs to be installed with an extra prefix or suffix on their names by giving `configure' the option `--program-prefix=PREFIX' or `--program-suffix=SUFFIX'. Optional Features ================= Some packages pay attention to `--enable-FEATURE' options to `configure', where FEATURE indicates an optional part of the package. They may also pay attention to `--with-PACKAGE' options, where PACKAGE is something like `gnu-as' or `x' (for the X Window System). The `README' should mention any `--enable-' and `--with-' options that the package recognizes. For packages that use the X Window System, `configure' can usually find the X include and library files automatically, but if it doesn't, you can use the `configure' options `--x-includes=DIR' and `--x-libraries=DIR' to specify their locations. Specifying the System Type ========================== There may be some features `configure' cannot figure out automatically, but needs to determine by the type of machine the package will run on. Usually, assuming the package is built to be run on the _same_ architectures, `configure' can figure that out, but if it prints a message saying it cannot guess the machine type, give it the `--build=TYPE' option. TYPE can either be a short name for the system type, such as `sun4', or a canonical name which has the form: CPU-COMPANY-SYSTEM where SYSTEM can have one of these forms: OS KERNEL-OS See the file `config.sub' for the possible values of each field. If `config.sub' isn't included in this package, then this package doesn't need to know the machine type. If you are _building_ compiler tools for cross-compiling, you should use the `--target=TYPE' option to select the type of system they will produce code for. If you want to _use_ a cross compiler, that generates code for a platform different from the build platform, you should specify the "host" platform (i.e., that on which the generated programs will eventually be run) with `--host=TYPE'. Sharing Defaults ================ If you want to set default values for `configure' scripts to share, you can create a site shell script called `config.site' that gives default values for variables like `CC', `cache_file', and `prefix'. `configure' looks for `PREFIX/share/config.site' if it exists, then `PREFIX/etc/config.site' if it exists. Or, you can set the `CONFIG_SITE' environment variable to the location of the site script. A warning: not all `configure' scripts look for a site script. Defining Variables ================== Variables not defined in a site shell script can be set in the environment passed to `configure'. However, some packages may run configure again during the build, and the customized values of these variables may be lost. In order to avoid this problem, you should set them in the `configure' command line, using `VAR=value'. For example: ./configure CC=/usr/local2/bin/gcc causes the specified `gcc' to be used as the C compiler (unless it is overridden in the site shell script). Here is a another example: /bin/bash ./configure CONFIG_SHELL=/bin/bash Here the `CONFIG_SHELL=/bin/bash' operand causes subsequent configuration-related scripts to be executed by `/bin/bash'. `configure' Invocation ====================== `configure' recognizes the following options to control how it operates. `--help' `-h' Print a summary of the options to `configure', and exit. `--version' `-V' Print the version of Autoconf used to generate the `configure' script, and exit. `--cache-file=FILE' Enable the cache: use and save the results of the tests in FILE, traditionally `config.cache'. FILE defaults to `/dev/null' to disable caching. `--config-cache' `-C' Alias for `--cache-file=config.cache'. `--quiet' `--silent' `-q' Do not print messages saying which checks are being made. To suppress all normal output, redirect it to `/dev/null' (any error messages will still be shown). `--srcdir=DIR' Look for the package's source code in directory DIR. Usually `configure' can determine that directory automatically. `configure' also accepts some other, not widely useful, options. Run `configure --help' for more details. bluez-5.82/PaxHeaders/peripheral0000644000000000000000000000005014773213563013737 xustar0020 atime=1743591291 20 ctime=1743591283 bluez-5.82/peripheral/0000755000000000000000000000000014773213563013475 5ustar00rootrootbluez-5.82/peripheral/PaxHeaders/gatt.h0000644000000000000000000000005014015011623015102 xustar0020 atime=1743516869 20 ctime=1743591283 bluez-5.82/peripheral/gatt.h0000644000000000000000000000055614015011623014571 0ustar00rootroot/* SPDX-License-Identifier: LGPL-2.1-or-later */ /* * * BlueZ - Bluetooth protocol stack for Linux * * Copyright (C) 2015 Intel Corporation. All rights reserved. * * */ #include void gatt_set_static_address(uint8_t addr[6]); void gatt_set_device_name(uint8_t name[20], uint8_t len); void gatt_server_start(void); void gatt_server_stop(void); bluez-5.82/peripheral/PaxHeaders/efivars.c0000644000000000000000000000005014711225434015607 xustar0020 atime=1743516869 20 ctime=1743591283 bluez-5.82/peripheral/efivars.c0000644000000000000000000000446614711225434015302 0ustar00rootroot// SPDX-License-Identifier: LGPL-2.1-or-later /* * * BlueZ - Bluetooth protocol stack for Linux * * Copyright (C) 2015 Intel Corporation. All rights reserved. * * */ #ifdef HAVE_CONFIG_H #include #endif #define _GNU_SOURCE #include #include #include #include #include #include #include #include #include #include #include #include #include "peripheral/efivars.h" #define SYSFS_EFIVARS "/sys/firmware/efi/efivars" typedef struct { uint32_t data1; uint16_t data2; uint16_t data3; uint8_t data4[8]; } efi_guid_t; #define VENDOR_GUID \ (efi_guid_t) { 0xd5f9d775, 0x1a09, 0x4e89, \ { 0x96, 0xcf, 0x1d, 0x19, 0x55, 0x4d, 0xa6, 0x67 } } static void efivars_pathname(const char *name, char *pathname, size_t size) { static efi_guid_t guid = VENDOR_GUID; snprintf(pathname, size - 1, "%s/%s-%08x-%04x-%04x-%02x%02x-%02x%02x%02x%02x%02x%02x", SYSFS_EFIVARS, name, guid.data1, guid.data2, guid.data3, guid.data4[0], guid.data4[1], guid.data4[2], guid.data4[3], guid.data4[4], guid.data4[5], guid.data4[6], guid.data4[7]); } int efivars_read(const char *name, uint32_t *attributes, void *data, size_t size) { char pathname[PATH_MAX]; struct iovec iov[2]; uint32_t attr; ssize_t len; int fd; efivars_pathname(name, pathname, PATH_MAX); fd = open(pathname, O_RDONLY | O_CLOEXEC); if (fd < 0) return -EIO; iov[0].iov_base = &attr; iov[0].iov_len = sizeof(attr); iov[1].iov_base = data; iov[1].iov_len = size; len = readv(fd, iov, 2); close(fd); if (len < 0) return -EIO; if (attributes) *attributes = attr; return 0; } int efivars_write(const char *name, uint32_t attributes, const void *data, size_t size) { char pathname[PATH_MAX]; void *buf; ssize_t written; int fd; efivars_pathname(name, pathname, PATH_MAX); buf = malloc(size + sizeof(attributes)); if (!buf) return -ENOMEM; fd = open(pathname, O_CREAT | O_WRONLY | O_TRUNC | O_CLOEXEC, 0644); if (fd < 0) { free(buf); return -EIO; } memcpy(buf, &attributes, sizeof(attributes)); memcpy(buf + sizeof(attributes), data, size); written = write(fd, buf, size + sizeof(attributes)); close(fd); free(buf); if (written < 0) return -EIO; return 0; } bluez-5.82/peripheral/PaxHeaders/log.h0000644000000000000000000000005014015011623014724 xustar0020 atime=1743516869 20 ctime=1743591283 bluez-5.82/peripheral/log.h0000644000000000000000000000033714015011623014410 0ustar00rootroot/* SPDX-License-Identifier: LGPL-2.1-or-later */ /* * * BlueZ - Bluetooth protocol stack for Linux * * Copyright (C) 2015 Intel Corporation. All rights reserved. * * */ void log_open(void); void log_close(void); bluez-5.82/peripheral/PaxHeaders/attach.c0000644000000000000000000000005014015011623015402 xustar0020 atime=1743516869 20 ctime=1743591283 bluez-5.82/peripheral/attach.c0000644000000000000000000000446614015011623015075 0ustar00rootroot// SPDX-License-Identifier: LGPL-2.1-or-later /* * * BlueZ - Bluetooth protocol stack for Linux * * Copyright (C) 2015 Intel Corporation. All rights reserved. * * */ #ifdef HAVE_CONFIG_H #include #endif #define _GNU_SOURCE #include #include #include #include "lib/bluetooth.h" #include "lib/hci.h" #include "lib/hci_lib.h" #include "tools/hciattach.h" #include "peripheral/attach.h" static const char *serial_dev = "/dev/ttyS1"; static int serial_fd = -1; static int open_serial(const char *path) { struct termios ti; int fd, saved_ldisc, ldisc = N_HCI; fd = open(path, O_RDWR | O_NOCTTY); if (fd < 0) { perror("Failed to open serial port"); return -1; } if (tcflush(fd, TCIOFLUSH) < 0) { perror("Failed to flush serial port"); close(fd); return -1; } if (ioctl(fd, TIOCGETD, &saved_ldisc) < 0) { perror("Failed get serial line discipline"); close(fd); return -1; } /* Switch TTY to raw mode */ memset(&ti, 0, sizeof(ti)); cfmakeraw(&ti); ti.c_cflag |= (B115200 | CLOCAL | CREAD); /* Set flow control */ ti.c_cflag |= CRTSCTS; if (tcsetattr(fd, TCSANOW, &ti) < 0) { perror("Failed to set serial port settings"); close(fd); return -1; } if (ioctl(fd, TIOCSETD, &ldisc) < 0) { perror("Failed set serial line discipline"); close(fd); return -1; } printf("Switched line discipline from %d to %d\n", saved_ldisc, ldisc); return fd; } static int attach_proto(const char *path, unsigned int proto, unsigned int flags) { int fd, dev_id; fd = open_serial(path); if (fd < 0) return -1; if (ioctl(fd, HCIUARTSETFLAGS, flags) < 0) { perror("Failed to set flags"); close(fd); return -1; } if (ioctl(fd, HCIUARTSETPROTO, proto) < 0) { perror("Failed to set protocol"); close(fd); return -1; } dev_id = ioctl(fd, HCIUARTGETDEVICE); if (dev_id < 0) { perror("Failed to get device id"); close(fd); return -1; } printf("Device index %d attached\n", dev_id); return fd; } void attach_start(void) { unsigned long flags; if (serial_fd >= 0) return; printf("Attaching BR/EDR controller to %s\n", serial_dev); flags = (1 << HCI_UART_RESET_ON_INIT); serial_fd = attach_proto(serial_dev, HCI_UART_H4, flags); } void attach_stop(void) { if (serial_fd < 0) return; close(serial_fd); serial_fd = -1; } bluez-5.82/peripheral/PaxHeaders/gap.h0000644000000000000000000000005014015011623014712 xustar0020 atime=1743516869 20 ctime=1743591283 bluez-5.82/peripheral/gap.h0000644000000000000000000000044314015011623014374 0ustar00rootroot/* SPDX-License-Identifier: LGPL-2.1-or-later */ /* * * BlueZ - Bluetooth protocol stack for Linux * * Copyright (C) 2015 Intel Corporation. All rights reserved. * * */ #include void gap_set_static_address(uint8_t addr[6]); void gap_start(void); void gap_stop(void); bluez-5.82/peripheral/PaxHeaders/attach.h0000644000000000000000000000005014015011623015407 xustar0020 atime=1743516869 20 ctime=1743591283 bluez-5.82/peripheral/attach.h0000644000000000000000000000034514015011623015072 0ustar00rootroot/* SPDX-License-Identifier: LGPL-2.1-or-later */ /* * * BlueZ - Bluetooth protocol stack for Linux * * Copyright (C) 2015 Intel Corporation. All rights reserved. * * */ void attach_start(void); void attach_stop(void); bluez-5.82/peripheral/PaxHeaders/gap.c0000644000000000000000000000005014015011623014705 xustar0020 atime=1743516869 20 ctime=1743591283 bluez-5.82/peripheral/gap.c0000644000000000000000000003230314015011623014367 0ustar00rootroot// SPDX-License-Identifier: LGPL-2.1-or-later /* * * BlueZ - Bluetooth protocol stack for Linux * * Copyright (C) 2015 Intel Corporation. All rights reserved. * * */ #ifdef HAVE_CONFIG_H #include #endif #include #include #include "lib/bluetooth.h" #include "lib/mgmt.h" #include "src/shared/util.h" #include "src/shared/mgmt.h" #include "peripheral/gatt.h" #include "peripheral/gap.h" static struct mgmt *mgmt = NULL; static uint16_t mgmt_index = MGMT_INDEX_NONE; static bool adv_features = false; static bool adv_instances = false; static bool require_connectable = true; static uint8_t static_addr[6] = { 0x90, 0x78, 0x56, 0x34, 0x12, 0xc0 }; static uint8_t dev_name[260] = { 0x00, }; static uint8_t dev_name_len = 0; void gap_set_static_address(uint8_t addr[6]) { memcpy(static_addr, addr, sizeof(static_addr)); printf("Using static address %02x:%02x:%02x:%02x:%02x:%02x\n", static_addr[5], static_addr[4], static_addr[3], static_addr[2], static_addr[1], static_addr[0]); } static void clear_long_term_keys(uint16_t index) { struct mgmt_cp_load_long_term_keys cp; memset(&cp, 0, sizeof(cp)); cp.key_count = cpu_to_le16(0); mgmt_send(mgmt, MGMT_OP_LOAD_LONG_TERM_KEYS, index, sizeof(cp), &cp, NULL, NULL, NULL); } static void clear_identity_resolving_keys(uint16_t index) { struct mgmt_cp_load_irks cp; memset(&cp, 0, sizeof(cp)); cp.irk_count = cpu_to_le16(0); mgmt_send(mgmt, MGMT_OP_LOAD_IRKS, index, sizeof(cp), &cp, NULL, NULL, NULL); } static void add_advertising(uint16_t index) { const char ad[] = { 0x11, 0x15, 0xd0, 0x00, 0x2d, 0x12, 0x1e, 0x4b, 0x0f, 0xa4, 0x99, 0x4e, 0xce, 0xb5, 0x31, 0xf4, 0x05, 0x79 }; struct mgmt_cp_add_advertising *cp; void *buf; buf = malloc(sizeof(*cp) + sizeof(ad)); if (!buf) return; memset(buf, 0, sizeof(*cp) + sizeof(ad)); cp = buf; cp->instance = 0x01; cp->flags = cpu_to_le32((1 << 0) | (1 << 1) | (1 << 4)); cp->duration = cpu_to_le16(0); cp->timeout = cpu_to_le16(0); cp->adv_data_len = sizeof(ad); cp->scan_rsp_len = 0; memcpy(cp->data, ad, sizeof(ad)); mgmt_send(mgmt, MGMT_OP_ADD_ADVERTISING, index, sizeof(*cp) + sizeof(ad), buf, NULL, NULL, NULL); free(buf); } static void enable_advertising(uint16_t index) { uint8_t val; val = require_connectable ? 0x01 : 0x00; mgmt_send(mgmt, MGMT_OP_SET_CONNECTABLE, index, 1, &val, NULL, NULL, NULL); val = 0x01; mgmt_send(mgmt, MGMT_OP_SET_POWERED, index, 1, &val, NULL, NULL, NULL); if (adv_instances) { add_advertising(index); return; } val = require_connectable ? 0x01 : 0x02; mgmt_send(mgmt, MGMT_OP_SET_ADVERTISING, index, 1, &val, NULL, NULL, NULL); } static void new_settings_event(uint16_t index, uint16_t length, const void *param, void *user_data) { printf("New settings\n"); } static void local_name_changed_event(uint16_t index, uint16_t length, const void *param, void *user_data) { printf("Local name changed\n"); } static void new_long_term_key_event(uint16_t index, uint16_t length, const void *param, void *user_data) { printf("New long term key\n"); } static void device_connected_event(uint16_t index, uint16_t length, const void *param, void *user_data) { printf("Device connected\n"); } static void device_disconnected_event(uint16_t index, uint16_t length, const void *param, void *user_data) { printf("Device disconnected\n"); } static void user_confirm_request_event(uint16_t index, uint16_t length, const void *param, void *user_data) { printf("User confirm request\n"); } static void user_passkey_request_event(uint16_t index, uint16_t length, const void *param, void *user_data) { printf("User passkey request\n"); } static void auth_failed_event(uint16_t index, uint16_t length, const void *param, void *user_data) { printf("Authentication failed\n"); } static void device_unpaired_event(uint16_t index, uint16_t length, const void *param, void *user_data) { printf("Device unpaired\n"); } static void passkey_notify_event(uint16_t index, uint16_t length, const void *param, void *user_data) { printf("Passkey notification\n"); } static void new_irk_event(uint16_t index, uint16_t length, const void *param, void *user_data) { printf("New identify resolving key\n"); } static void new_csrk_event(uint16_t index, uint16_t length, const void *param, void *user_data) { printf("New connection signature resolving key\n"); } static void new_conn_param_event(uint16_t index, uint16_t length, const void *param, void *user_data) { printf("New connection parameter\n"); } static void advertising_added_event(uint16_t index, uint16_t length, const void *param, void *user_data) { printf("Advertising added\n"); } static void advertising_removed_event(uint16_t index, uint16_t length, const void *param, void *user_data) { printf("Advertising removed\n"); } static void read_adv_features_complete(uint8_t status, uint16_t len, const void *param, void *user_data) { const struct mgmt_rp_read_adv_features *rp = param; uint16_t index = PTR_TO_UINT(user_data); uint32_t flags; flags = le32_to_cpu(rp->supported_flags); if (rp->max_instances > 0) { adv_instances = true; if (flags & (1 << 0)) require_connectable = false; } else require_connectable = false; enable_advertising(index); } static void read_info_complete(uint8_t status, uint16_t len, const void *param, void *user_data) { const struct mgmt_rp_read_info *rp = param; uint16_t index = PTR_TO_UINT(user_data); uint32_t required_settings = MGMT_SETTING_LE | MGMT_SETTING_STATIC_ADDRESS; uint32_t supported_settings, current_settings; uint8_t val; required_settings = MGMT_SETTING_LE; if (status) { fprintf(stderr, "Reading info for index %u failed: %s\n", index, mgmt_errstr(status)); return; } if (mgmt_index != MGMT_INDEX_NONE) return; supported_settings = le32_to_cpu(rp->supported_settings); current_settings = le32_to_cpu(rp->current_settings); if ((supported_settings & required_settings) != required_settings) return; printf("Selecting index %u\n", index); mgmt_index = index; mgmt_register(mgmt, MGMT_EV_NEW_SETTINGS, index, new_settings_event, NULL, NULL); mgmt_register(mgmt, MGMT_EV_LOCAL_NAME_CHANGED, index, local_name_changed_event, NULL, NULL); mgmt_register(mgmt, MGMT_EV_NEW_LONG_TERM_KEY, index, new_long_term_key_event, NULL, NULL); mgmt_register(mgmt, MGMT_EV_DEVICE_CONNECTED, index, device_connected_event, NULL, NULL); mgmt_register(mgmt, MGMT_EV_DEVICE_DISCONNECTED, index, device_disconnected_event, NULL, NULL); mgmt_register(mgmt, MGMT_EV_USER_CONFIRM_REQUEST, index, user_confirm_request_event, NULL, NULL); mgmt_register(mgmt, MGMT_EV_USER_PASSKEY_REQUEST, index, user_passkey_request_event, NULL, NULL); mgmt_register(mgmt, MGMT_EV_AUTH_FAILED, index, auth_failed_event, NULL, NULL); mgmt_register(mgmt, MGMT_EV_DEVICE_UNPAIRED, index, device_unpaired_event, NULL, NULL); mgmt_register(mgmt, MGMT_EV_PASSKEY_NOTIFY, index, passkey_notify_event, NULL, NULL); mgmt_register(mgmt, MGMT_EV_NEW_IRK, index, new_irk_event, NULL, NULL); mgmt_register(mgmt, MGMT_EV_NEW_CSRK, index, new_csrk_event, NULL, NULL); mgmt_register(mgmt, MGMT_EV_NEW_CONN_PARAM, index, new_conn_param_event, NULL, NULL); mgmt_register(mgmt, MGMT_EV_ADVERTISING_ADDED, index, advertising_added_event, NULL, NULL); mgmt_register(mgmt, MGMT_EV_ADVERTISING_REMOVED, index, advertising_removed_event, NULL, NULL); dev_name_len = snprintf((char *) dev_name, 26, "BlueZ Peripheral"); if (current_settings & MGMT_SETTING_POWERED) { val = 0x00; mgmt_send(mgmt, MGMT_OP_SET_POWERED, index, 1, &val, NULL, NULL, NULL); } if (!(current_settings & MGMT_SETTING_LE)) { val = 0x01; mgmt_send(mgmt, MGMT_OP_SET_LE, index, 1, &val, NULL, NULL, NULL); } if (current_settings & MGMT_SETTING_BREDR) { val = 0x00; mgmt_send(mgmt, MGMT_OP_SET_BREDR, index, 1, &val, NULL, NULL, NULL); } if ((supported_settings & MGMT_SETTING_SECURE_CONN) && !(current_settings & MGMT_SETTING_SECURE_CONN)) { val = 0x01; mgmt_send(mgmt, MGMT_OP_SET_SECURE_CONN, index, 1, &val, NULL, NULL, NULL); } if (current_settings & MGMT_SETTING_DEBUG_KEYS) { val = 0x00; mgmt_send(mgmt, MGMT_OP_SET_DEBUG_KEYS, index, 1, &val, NULL, NULL, NULL); } if (!(current_settings & MGMT_SETTING_BONDABLE)) { val = 0x01; mgmt_send(mgmt, MGMT_OP_SET_BONDABLE, index, 1, &val, NULL, NULL, NULL); } clear_long_term_keys(mgmt_index); clear_identity_resolving_keys(mgmt_index); mgmt_send(mgmt, MGMT_OP_SET_STATIC_ADDRESS, index, 6, static_addr, NULL, NULL, NULL); mgmt_send(mgmt, MGMT_OP_SET_LOCAL_NAME, index, 260, dev_name, NULL, NULL, NULL); gatt_set_static_address(static_addr); gatt_set_device_name(dev_name, dev_name_len); gatt_server_start(); if (adv_features) mgmt_send(mgmt, MGMT_OP_READ_ADV_FEATURES, index, 0, NULL, read_adv_features_complete, UINT_TO_PTR(index), NULL); else enable_advertising(index); } static void read_index_list_complete(uint8_t status, uint16_t len, const void *param, void *user_data) { const struct mgmt_rp_read_index_list *rp = param; uint16_t count; int i; if (status) { fprintf(stderr, "Reading index list failed: %s\n", mgmt_errstr(status)); return; } count = le16_to_cpu(rp->num_controllers); printf("Index list: %u\n", count); for (i = 0; i < count; i++) { uint16_t index = cpu_to_le16(rp->index[i]); mgmt_send(mgmt, MGMT_OP_READ_INFO, index, 0, NULL, read_info_complete, UINT_TO_PTR(index), NULL); } } static void index_added_event(uint16_t index, uint16_t length, const void *param, void *user_data) { printf("Index added\n"); if (mgmt_index != MGMT_INDEX_NONE) return; mgmt_send(mgmt, MGMT_OP_READ_INFO, index, 0, NULL, read_info_complete, UINT_TO_PTR(index), NULL); } static void index_removed_event(uint16_t index, uint16_t length, const void *param, void *user_data) { printf("Index removed\n"); if (mgmt_index != index) return; mgmt_index = MGMT_INDEX_NONE; } static void read_ext_index_list_complete(uint8_t status, uint16_t len, const void *param, void *user_data) { const struct mgmt_rp_read_ext_index_list *rp = param; uint16_t count; int i; if (status) { fprintf(stderr, "Reading extended index list failed: %s\n", mgmt_errstr(status)); return; } count = le16_to_cpu(rp->num_controllers); printf("Extended index list: %u\n", count); for (i = 0; i < count; i++) { uint16_t index = cpu_to_le16(rp->entry[i].index); if (rp->entry[i].type != 0x00) continue; mgmt_send(mgmt, MGMT_OP_READ_INFO, index, 0, NULL, read_info_complete, UINT_TO_PTR(index), NULL); } } static void ext_index_added_event(uint16_t index, uint16_t length, const void *param, void *user_data) { const struct mgmt_ev_ext_index_added *ev = param; printf("Extended index added: %u\n", ev->type); if (mgmt_index != MGMT_INDEX_NONE) return; if (ev->type != 0x00) return; mgmt_send(mgmt, MGMT_OP_READ_INFO, index, 0, NULL, read_info_complete, UINT_TO_PTR(index), NULL); } static void ext_index_removed_event(uint16_t index, uint16_t length, const void *param, void *user_data) { const struct mgmt_ev_ext_index_added *ev = param; printf("Extended index removed: %u\n", ev->type); if (mgmt_index != index) return; if (ev->type != 0x00) return; mgmt_index = MGMT_INDEX_NONE; } static void read_commands_complete(uint8_t status, uint16_t len, const void *param, void *user_data) { const struct mgmt_rp_read_commands *rp = param; uint16_t num_commands; bool ext_index_list = false; int i; if (status) { fprintf(stderr, "Reading index list failed: %s\n", mgmt_errstr(status)); return; } num_commands = le16_to_cpu(rp->num_commands); for (i = 0; i < num_commands; i++) { uint16_t op = get_le16(rp->opcodes + 1); if (op == MGMT_OP_READ_EXT_INDEX_LIST) ext_index_list = true; else if (op == MGMT_OP_READ_ADV_FEATURES) adv_features = true; } if (ext_index_list) { mgmt_register(mgmt, MGMT_EV_EXT_INDEX_ADDED, MGMT_INDEX_NONE, ext_index_added_event, NULL, NULL); mgmt_register(mgmt, MGMT_EV_EXT_INDEX_REMOVED, MGMT_INDEX_NONE, ext_index_removed_event, NULL, NULL); if (!mgmt_send(mgmt, MGMT_OP_READ_EXT_INDEX_LIST, MGMT_INDEX_NONE, 0, NULL, read_ext_index_list_complete, NULL, NULL)) { fprintf(stderr, "Failed to read extended index list\n"); return; } } else { mgmt_register(mgmt, MGMT_EV_INDEX_ADDED, MGMT_INDEX_NONE, index_added_event, NULL, NULL); mgmt_register(mgmt, MGMT_EV_INDEX_REMOVED, MGMT_INDEX_NONE, index_removed_event, NULL, NULL); if (!mgmt_send(mgmt, MGMT_OP_READ_INDEX_LIST, MGMT_INDEX_NONE, 0, NULL, read_index_list_complete, NULL, NULL)) { fprintf(stderr, "Failed to read index list\n"); return; } } } void gap_start(void) { mgmt = mgmt_new_default(); if (!mgmt) { fprintf(stderr, "Failed to open management socket\n"); return; } if (!mgmt_send(mgmt, MGMT_OP_READ_COMMANDS, MGMT_INDEX_NONE, 0, NULL, read_commands_complete, NULL, NULL)) { fprintf(stderr, "Failed to read supported commands\n"); return; } } void gap_stop(void) { if (!mgmt) return; gatt_server_stop(); mgmt_unref(mgmt); mgmt = NULL; mgmt_index = MGMT_INDEX_NONE; } bluez-5.82/peripheral/PaxHeaders/efivars.h0000644000000000000000000000005014015011623015602 xustar0020 atime=1743516869 20 ctime=1743591283 bluez-5.82/peripheral/efivars.h0000644000000000000000000000113014015011623015256 0ustar00rootroot/* SPDX-License-Identifier: LGPL-2.1-or-later */ /* * * BlueZ - Bluetooth protocol stack for Linux * * Copyright (C) 2015 Intel Corporation. All rights reserved. * * */ #define EFIVARS_NON_VOLATILE 0x00000001 #define EFIVARS_BOOTSERVICE_ACCESS 0x00000002 #define EFIVARS_RUNTIME_ACCESS 0x00000004 #define EFIVARS_HARDWARE_ERROR_RECORD 0x00000008 #define EFIVARS_AUTHENTICATED_WRITE_ACCESS 0x00000010 int efivars_read(const char *name, uint32_t *attributes, void *data, size_t size); int efivars_write(const char *name, uint32_t attributes, const void *data, size_t size); bluez-5.82/peripheral/PaxHeaders/gatt.c0000644000000000000000000000005014015011623015075 xustar0020 atime=1743516869 20 ctime=1743591283 bluez-5.82/peripheral/gatt.c0000644000000000000000000001460014015011623014557 0ustar00rootroot// SPDX-License-Identifier: LGPL-2.1-or-later /* * * BlueZ - Bluetooth protocol stack for Linux * * Copyright (C) 2015 Intel Corporation. All rights reserved. * * */ #ifdef HAVE_CONFIG_H #include #endif #include #include #include #include #include #include "lib/bluetooth.h" #include "lib/l2cap.h" #include "lib/uuid.h" #include "src/shared/mainloop.h" #include "src/shared/util.h" #include "src/shared/queue.h" #include "src/shared/att.h" #include "src/shared/gatt-db.h" #include "src/shared/gatt-server.h" #include "src/shared/gatt-client.h" #include "peripheral/gatt.h" #define ATT_CID 4 #define UUID_GAP 0x1800 struct gatt_conn { struct bt_att *att; struct bt_gatt_server *gatt; struct bt_gatt_client *client; }; static int att_fd = -1; static struct queue *conn_list = NULL; static struct gatt_db *gatt_db = NULL; static struct gatt_db *gatt_cache = NULL; static uint8_t static_addr[6] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; static uint8_t dev_name[20]; static uint8_t dev_name_len = 0; void gatt_set_static_address(uint8_t addr[6]) { memcpy(static_addr, addr, sizeof(static_addr)); } void gatt_set_device_name(uint8_t name[20], uint8_t len) { memcpy(dev_name, name, sizeof(dev_name)); dev_name_len = len; } static void gatt_conn_destroy(void *data) { struct gatt_conn *conn = data; bt_gatt_client_unref(conn->client); bt_gatt_server_unref(conn->gatt); bt_att_unref(conn->att); free(conn); } static void gatt_conn_disconnect(int err, void *user_data) { struct gatt_conn *conn = user_data; printf("Device disconnected: %s\n", strerror(err)); queue_remove(conn_list, conn); gatt_conn_destroy(conn); } static void client_ready_callback(bool success, uint8_t att_ecode, void *user_data) { printf("GATT client discovery complete\n"); } static void client_service_changed_callback(uint16_t start_handle, uint16_t end_handle, void *user_data) { printf("GATT client service changed notification\n"); } static struct gatt_conn *gatt_conn_new(int fd) { struct gatt_conn *conn; uint16_t mtu = 0; conn = new0(struct gatt_conn, 1); if (!conn) return NULL; conn->att = bt_att_new(fd, false); if (!conn->att) { fprintf(stderr, "Failed to initialze ATT transport layer\n"); free(conn); return NULL; } bt_att_set_close_on_unref(conn->att, true); bt_att_register_disconnect(conn->att, gatt_conn_disconnect, conn, NULL); bt_att_set_security(conn->att, BT_SECURITY_MEDIUM); conn->gatt = bt_gatt_server_new(gatt_db, conn->att, mtu, 0); if (!conn->gatt) { fprintf(stderr, "Failed to create GATT server\n"); bt_att_unref(conn->att); free(conn); return NULL; } conn->client = bt_gatt_client_new(gatt_cache, conn->att, mtu, 0); if (!conn->gatt) { fprintf(stderr, "Failed to create GATT client\n"); bt_gatt_server_unref(conn->gatt); bt_att_unref(conn->att); free(conn); return NULL; } bt_gatt_client_ready_register(conn->client, client_ready_callback, conn, NULL); bt_gatt_client_set_service_changed(conn->client, client_service_changed_callback, conn, NULL); return conn; } static void att_conn_callback(int fd, uint32_t events, void *user_data) { struct gatt_conn *conn; struct sockaddr_l2 addr; socklen_t addrlen; int new_fd; if (events & (EPOLLERR | EPOLLHUP)) { mainloop_remove_fd(fd); return; } memset(&addr, 0, sizeof(addr)); addrlen = sizeof(addr); new_fd = accept(att_fd, (struct sockaddr *) &addr, &addrlen); if (new_fd < 0) { fprintf(stderr, "Failed to accept new ATT connection: %m\n"); return; } conn = gatt_conn_new(new_fd); if (!conn) { fprintf(stderr, "Failed to create GATT connection\n"); close(new_fd); return; } if (!queue_push_tail(conn_list, conn)) { fprintf(stderr, "Failed to add GATT connection\n"); gatt_conn_destroy(conn); close(new_fd); } printf("New device connected\n"); } static void gap_device_name_read(struct gatt_db_attribute *attrib, unsigned int id, uint16_t offset, uint8_t opcode, struct bt_att *att, void *user_data) { uint8_t error; const uint8_t *value; size_t len; if (offset > dev_name_len) { error = BT_ATT_ERROR_INVALID_OFFSET; value = NULL; len = dev_name_len; } else { error = 0; len = dev_name_len - offset; value = len ? &dev_name[offset] : NULL; } gatt_db_attribute_read_result(attrib, id, error, value, len); } static void populate_gap_service(struct gatt_db *db) { struct gatt_db_attribute *service; bt_uuid_t uuid; bt_uuid16_create(&uuid, UUID_GAP); service = gatt_db_add_service(db, &uuid, true, 6); bt_uuid16_create(&uuid, GATT_CHARAC_DEVICE_NAME); gatt_db_service_add_characteristic(service, &uuid, BT_ATT_PERM_READ, BT_GATT_CHRC_PROP_READ, gap_device_name_read, NULL, NULL); gatt_db_service_set_active(service, true); } static void populate_devinfo_service(struct gatt_db *db) { struct gatt_db_attribute *service; bt_uuid_t uuid; bt_uuid16_create(&uuid, 0x180a); service = gatt_db_add_service(db, &uuid, true, 17); gatt_db_service_set_active(service, true); } void gatt_server_start(void) { struct sockaddr_l2 addr; if (att_fd >= 0) return; att_fd = socket(PF_BLUETOOTH, SOCK_SEQPACKET | SOCK_CLOEXEC, BTPROTO_L2CAP); if (att_fd < 0) { fprintf(stderr, "Failed to create ATT server socket: %m\n"); return; } memset(&addr, 0, sizeof(addr)); addr.l2_family = AF_BLUETOOTH; addr.l2_cid = htobs(ATT_CID); memcpy(&addr.l2_bdaddr, static_addr, 6); addr.l2_bdaddr_type = BDADDR_LE_RANDOM; if (bind(att_fd, (struct sockaddr *) &addr, sizeof(addr)) < 0) { fprintf(stderr, "Failed to bind ATT server socket: %m\n"); close(att_fd); att_fd = -1; return; } if (listen(att_fd, 1) < 0) { fprintf(stderr, "Failed to listen on ATT server socket: %m\n"); close(att_fd); att_fd = -1; return; } gatt_db = gatt_db_new(); if (!gatt_db) { close(att_fd); att_fd = -1; return; } populate_gap_service(gatt_db); populate_devinfo_service(gatt_db); gatt_cache = gatt_db_new(); conn_list = queue_new(); if (!conn_list) { gatt_db_unref(gatt_db); gatt_db = NULL; close(att_fd); att_fd = -1; return; } mainloop_add_fd(att_fd, EPOLLIN, att_conn_callback, NULL, NULL); } void gatt_server_stop(void) { if (att_fd < 0) return; mainloop_remove_fd(att_fd); queue_destroy(conn_list, gatt_conn_destroy); gatt_db_unref(gatt_cache); gatt_cache = NULL; gatt_db_unref(gatt_db); gatt_db = NULL; close(att_fd); att_fd = -1; } bluez-5.82/peripheral/PaxHeaders/main.c0000644000000000000000000000005014214376354015102 xustar0020 atime=1743516869 20 ctime=1743591283 bluez-5.82/peripheral/main.c0000644000000000000000000001052214214376354014563 0ustar00rootroot// SPDX-License-Identifier: LGPL-2.1-or-later /* * * BlueZ - Bluetooth protocol stack for Linux * * Copyright (C) 2015 Intel Corporation. All rights reserved. * * */ #ifdef HAVE_CONFIG_H #include #endif #define _GNU_SOURCE #include #include #include #include #include #include #include #include #include #include #include #include #ifndef WAIT_ANY #define WAIT_ANY (-1) #endif #include "src/shared/mainloop.h" #include "src/shared/util.h" #include "peripheral/efivars.h" #include "peripheral/attach.h" #include "peripheral/gap.h" #include "peripheral/log.h" static bool is_init = false; static pid_t shell_pid = -1; static const struct { const char *target; const char *linkpath; } dev_table[] = { { "/proc/self/fd", "/dev/fd" }, { "/proc/self/fd/0", "/dev/stdin" }, { "/proc/self/fd/1", "/dev/stdout" }, { "/proc/self/fd/2", "/dev/stderr" }, { } }; static const struct { const char *fstype; const char *target; const char *options; unsigned long flags; } mount_table[] = { { "sysfs", "/sys", NULL, MS_NOSUID|MS_NOEXEC|MS_NODEV }, { "proc", "/proc", NULL, MS_NOSUID|MS_NOEXEC|MS_NODEV }, { "devtmpfs", "/dev", NULL, MS_NOSUID|MS_STRICTATIME }, { "efivarfs", "/sys/firmware/efi/efivars", NULL, MS_NOSUID|MS_NOEXEC|MS_NODEV }, { "pstore", "/sys/fs/pstore", NULL, MS_NOSUID|MS_NOEXEC|MS_NODEV }, { } }; static void prepare_filesystem(void) { int i; if (!is_init) return; for (i = 0; mount_table[i].fstype && mount_table[i].target; i++) { struct stat st; if (lstat(mount_table[i].target, &st) < 0) { printf("Creating %s\n", mount_table[i].target); if (mkdir(mount_table[i].target, 0755) < 0) perror("Failed to create dir"); } printf("Mounting %s to %s\n", mount_table[i].fstype, mount_table[i].target); if (mount(mount_table[i].fstype, mount_table[i].target, mount_table[i].fstype, mount_table[i].flags, NULL) < 0) perror("Failed to mount filesystem"); } for (i = 0; dev_table[i].target; i++) { printf("Linking %s to %s\n", dev_table[i].linkpath, dev_table[i].target); if (symlink(dev_table[i].target, dev_table[i].linkpath) < 0) perror("Failed to create device symlink"); } } static void run_shell(void) { pid_t pid; printf("Starting shell\n"); pid = fork(); if (pid < 0) { perror("Failed to fork new process"); return; } if (pid == 0) { char *prg_argv[] = { "/bin/sh", NULL }; char *prg_envp[] = { NULL }; execve(prg_argv[0], prg_argv, prg_envp); exit(0); } printf("PID %d created\n", pid); shell_pid = pid; } static void exit_shell(void) { shell_pid = -1; if (!is_init) { mainloop_quit(); return; } run_shell(); } static void signal_callback(int signum, void *user_data) { switch (signum) { case SIGINT: case SIGTERM: mainloop_quit(); break; case SIGCHLD: while (1) { pid_t pid; int status; pid = waitpid(WAIT_ANY, &status, WNOHANG); if (pid < 0 || pid == 0) break; if (WIFEXITED(status)) { printf("PID %d exited (status=%d)\n", pid, WEXITSTATUS(status)); if (pid == shell_pid) exit_shell(); } else if (WIFSIGNALED(status)) { printf("PID %d terminated (signal=%d)\n", pid, WTERMSIG(status)); if (pid == shell_pid) exit_shell(); } } break; } } int main(int argc, char *argv[]) { int exit_status; if (getpid() == 1 && getppid() == 0) is_init = true; mainloop_init(); printf("Bluetooth periperhal ver %s\n", VERSION); prepare_filesystem(); if (is_init) { uint8_t addr[6]; if (efivars_read("BluetoothStaticAddress", NULL, addr, 6) < 0) { printf("Generating new persistent static address\n"); if (util_getrandom(addr, sizeof(addr), 0) < 0) { perror("Failed to get random static address"); return EXIT_FAILURE; } /* Overwrite the MSB to make it a static address */ addr[5] = 0xc0; efivars_write("BluetoothStaticAddress", EFIVARS_NON_VOLATILE | EFIVARS_BOOTSERVICE_ACCESS | EFIVARS_RUNTIME_ACCESS, addr, 6); } gap_set_static_address(addr); run_shell(); } log_open(); gap_start(); if (is_init) attach_start(); exit_status = mainloop_run_with_signal(signal_callback, NULL); if (is_init) attach_stop(); gap_stop(); log_close(); return exit_status; } bluez-5.82/peripheral/PaxHeaders/log.c0000644000000000000000000000005014015011623014717 xustar0020 atime=1743516869 20 ctime=1743591283 bluez-5.82/peripheral/log.c0000644000000000000000000000124214015011623014377 0ustar00rootroot// SPDX-License-Identifier: LGPL-2.1-or-later /* * * BlueZ - Bluetooth protocol stack for Linux * * Copyright (C) 2015 Intel Corporation. All rights reserved. * * */ #ifdef HAVE_CONFIG_H #include #endif #define _GNU_SOURCE #include #include #include #include #include "peripheral/log.h" static int kmsg_fd = -1; void log_open(void) { if (kmsg_fd >= 0) return; kmsg_fd = open("/dev/kmsg", O_WRONLY | O_NOCTTY | O_CLOEXEC); if (kmsg_fd < 0) { fprintf(stderr, "Failed to open kernel logging: %m\n"); return; } } void log_close(void) { if (kmsg_fd < 0) return; close(kmsg_fd); kmsg_fd = -1; } bluez-5.82/PaxHeaders/Makefile.obexd0000644000000000000000000000005014711225433014410 xustar0020 atime=1743515718 20 ctime=1743591275 bluez-5.82/Makefile.obexd0000644000000000000000000000776514711225433014110 0ustar00rootroot# SPDX-License-Identifier: GPL-2.0 if OBEX if SYSTEMD systemduserunit_DATA += obexd/src/obex.service dbussessionbusdir = $(DBUS_SESSIONBUSDIR) dbussessionbus_DATA = obexd/src/org.bluez.obex.service obexd-add-service-symlink: $(LN_S) -f obex.service $(DESTDIR)$(SYSTEMD_USERUNITDIR)/dbus-org.bluez.obex.service obexd-remove-service-symlink: rm -f $(DESTDIR)$(SYSTEMD_USERUNITDIR)/dbus-org.bluez.obex.service else obexd-add-service-symlink: obexd-remove-service-symlink: endif obex_plugindir = $(libdir)/obex/plugins obexd_builtin_modules = obexd_builtin_sources = obexd_builtin_nodist = obexd_builtin_modules += filesystem obexd_builtin_sources += obexd/plugins/filesystem.c obexd/plugins/filesystem.h obexd_builtin_modules += bluetooth obexd_builtin_sources += obexd/plugins/bluetooth.c if EXPERIMENTAL obexd_builtin_modules += pcsuite obexd_builtin_sources += obexd/plugins/pcsuite.c endif obexd_builtin_modules += opp obexd_builtin_sources += obexd/plugins/opp.c obexd_builtin_modules += ftp obexd_builtin_sources += obexd/plugins/ftp.c obexd/plugins/ftp.h obexd_builtin_modules += irmc obexd_builtin_sources += obexd/plugins/irmc.c obexd_builtin_modules += pbap obexd_builtin_sources += obexd/plugins/pbap.c \ obexd/plugins/vcard.h obexd/plugins/vcard.c \ obexd/plugins/phonebook.h \ obexd/plugins/phonebook-@PLUGIN_PHONEBOOK@.c EXTRA_DIST += obexd/plugins/phonebook-dummy.c obexd/plugins/phonebook-ebook.c \ obexd/plugins/phonebook-tracker.c obexd_builtin_modules += mas obexd_builtin_sources += obexd/plugins/mas.c obexd/src/map_ap.h \ obexd/plugins/messages.h \ obexd/plugins/messages-dummy.c obexd_builtin_modules += mns obexd_builtin_sources += obexd/client/mns.c obexd/src/map_ap.h \ obexd/client/map-event.h pkglibexec_PROGRAMS += obexd/src/obexd obexd_src_obexd_SOURCES = $(btio_sources) $(gobex_sources) \ $(obexd_builtin_sources) \ obexd/src/main.c obexd/src/obexd.h \ obexd/src/plugin.h obexd/src/plugin.c \ obexd/src/log.h obexd/src/log.c \ obexd/src/manager.h obexd/src/manager.c \ obexd/src/obex.h obexd/src/obex.c obexd/src/obex-priv.h \ obexd/src/mimetype.h obexd/src/mimetype.c \ obexd/src/service.h obexd/src/service.c \ obexd/src/transport.h obexd/src/transport.c \ obexd/src/server.h obexd/src/server.c \ obexd/client/manager.h obexd/client/manager.c \ obexd/client/session.h obexd/client/session.c \ obexd/client/bluetooth.h obexd/client/bluetooth.c \ obexd/client/sync.h obexd/client/sync.c \ obexd/client/pbap.h obexd/client/pbap.c \ obexd/client/ftp.h obexd/client/ftp.c \ obexd/client/opp.h obexd/client/opp.c \ obexd/client/map.h obexd/client/map.c \ obexd/client/bip.h obexd/client/bip.c \ obexd/client/bip-common.h obexd/client/bip-common.c \ obexd/client/map-event.h obexd/client/map-event.c \ obexd/client/transfer.h obexd/client/transfer.c \ obexd/client/transport.h obexd/client/transport.c \ obexd/client/driver.h obexd/client/driver.c \ obexd/src/map_ap.h obexd_src_obexd_LDADD = lib/libbluetooth-internal.la \ gdbus/libgdbus-internal.la \ src/libshared-glib.la \ $(ICAL_LIBS) $(DBUS_LIBS) $(LIBEBOOK_LIBS) \ $(LIBEDATASERVER_LIBS) $(GLIB_LIBS) -ldl if EXTERNAL_PLUGINS obexd_src_obexd_LDFLAGS = $(AM_LDFLAGS) -Wl,--export-dynamic endif obexd_src_obexd_CPPFLAGS = $(AM_CPPFLAGS) $(GLIB_CFLAGS) $(DBUS_CFLAGS) \ $(ICAL_CFLAGS) -DOBEX_PLUGIN_BUILTIN \ -DPLUGINDIR=\""$(obex_plugindir)"\" \ -D_FILE_OFFSET_BITS=64 \ -I$(builddir)/obexd/src else obexd-add-service-symlink: obexd-remove-service-symlink: endif obexd_src_obexd_SHORTNAME = obexd obexd_builtin_files = obexd/src/builtin.h $(obexd_builtin_nodist) nodist_obexd_src_obexd_SOURCES = $(obexd_builtin_files) BUILT_SOURCES += obexd/src/builtin.h obexd/src/plugin.$(OBJEXT): obexd/src/builtin.h obexd/src/builtin.h: obexd/src/genbuiltin $(obexd_builtin_sources) $(AM_V_at)$(MKDIR_P) $(dir $@) $(AM_V_GEN)$(srcdir)/obexd/src/genbuiltin $(obexd_builtin_modules) > $@ CLEANFILES += obexd/src/builtin.h EXTRA_DIST += obexd/src/genbuiltin bluez-5.82/PaxHeaders/mesh0000644000000000000000000000005014773213571012537 xustar0020 atime=1743591291 20 ctime=1743591289 bluez-5.82/mesh/0000755000000000000000000000000014773213571012275 5ustar00rootrootbluez-5.82/mesh/PaxHeaders/dbus.c0000644000000000000000000000005014772767672013734 xustar0020 atime=1743515579 20 ctime=1743591282 bluez-5.82/mesh/dbus.c0000644000000000000000000001033514772767672013417 0ustar00rootroot// SPDX-License-Identifier: LGPL-2.1-or-later /* * * BlueZ - Bluetooth protocol stack for Linux * * Copyright (C) 2018-2019 Intel Corporation. All rights reserved. * * */ #ifdef HAVE_CONFIG_H #include #endif #include #include #include "mesh/mesh-defs.h" #include "mesh/node.h" #include "mesh/manager.h" #include "mesh/mesh.h" #include "mesh/error.h" #include "mesh/dbus.h" static struct l_dbus *dbus; struct error_entry { const char *dbus_err; const char *default_desc; }; struct send_info { struct l_dbus *dbus; struct l_timeout *timeout; l_dbus_message_func_t cb; l_dbus_destroy_func_t destroy; void *user_data; uint32_t serial; }; /* * Important: The entries in this table follow the order of * enumerated values in mesh_error (file error.h) */ static struct error_entry const error_table[] = { { NULL, NULL }, { ERROR_INTERFACE ".Failed", "Operation failed" }, { ERROR_INTERFACE ".NotAuthorized", "Permission denied"}, { ERROR_INTERFACE ".NotFound", "Object not found"}, { ERROR_INTERFACE ".InvalidArgs", "Invalid arguments"}, { ERROR_INTERFACE ".InProgress", "Operation already in progress"}, { ERROR_INTERFACE ".Busy", "Busy"}, { ERROR_INTERFACE ".AlreadyExists", "Already exists"}, { ERROR_INTERFACE ".DoesNotExist", "Does not exist"}, { ERROR_INTERFACE ".Canceled", "Operation canceled"}, { ERROR_INTERFACE ".NotImplemented", "Not implemented"}, }; struct l_dbus_message *dbus_error(struct l_dbus_message *msg, int err, const char *description) { int array_len = L_ARRAY_SIZE(error_table); /* Default to ".Failed" */ if (!err || err >= array_len) err = MESH_ERROR_FAILED; if (description) return l_dbus_message_new_error(msg, error_table[err].dbus_err, "%s", description); else return l_dbus_message_new_error(msg, error_table[err].dbus_err, "%s", error_table[err].default_desc); } struct l_dbus *dbus_get_bus(void) { return dbus; } bool dbus_init(struct l_dbus *bus) { /* Network interface */ if (!mesh_dbus_init(bus)) return false; /* Node interface */ if (!node_dbus_init(bus)) return false; /* Management interface */ if (!manager_dbus_init(bus)) return false; dbus = bus; return true; } bool dbus_match_interface(struct l_dbus_message_iter *interfaces, const char *match) { const char *interface; struct l_dbus_message_iter properties; while (l_dbus_message_iter_next_entry(interfaces, &interface, &properties)) { if (!strcmp(match, interface)) return true; } return false; } void dbus_append_byte_array(struct l_dbus_message_builder *builder, const uint8_t *data, int len) { int i; if (!builder) return; l_dbus_message_builder_enter_array(builder, "y"); for (i = 0; i < len; i++) l_dbus_message_builder_append_basic(builder, 'y', data + i); l_dbus_message_builder_leave_array(builder); } void dbus_append_dict_entry_basic(struct l_dbus_message_builder *builder, const char *key, const char *signature, const void *data) { if (!builder) return; l_dbus_message_builder_enter_dict(builder, "sv"); l_dbus_message_builder_append_basic(builder, 's', key); l_dbus_message_builder_enter_variant(builder, signature); l_dbus_message_builder_append_basic(builder, signature[0], data); l_dbus_message_builder_leave_variant(builder); l_dbus_message_builder_leave_dict(builder); } static void send_reply(struct l_dbus_message *message, void *user_data) { struct send_info *info = user_data; l_timeout_remove(info->timeout); info->cb(message, info->user_data); if (info->destroy) info->destroy(info->user_data); l_free(info); } static void send_timeout(struct l_timeout *timeout, void *user_data) { struct send_info *info = user_data; l_dbus_cancel(info->dbus, info->serial); send_reply(NULL, info); } void dbus_send_with_timeout(struct l_dbus *dbus, struct l_dbus_message *msg, l_dbus_message_func_t cb, void *user_data, l_dbus_destroy_func_t destroy, unsigned int seconds) { struct send_info *info = l_new(struct send_info, 1); info->dbus = dbus; info->cb = cb; info->user_data = user_data; info->destroy = destroy; info->serial = l_dbus_send_with_reply(dbus, msg, send_reply, info, NULL); info->timeout = l_timeout_create(seconds, send_timeout, info, NULL); } bluez-5.82/mesh/PaxHeaders/dbus.h0000644000000000000000000000005014015011623013701 xustar0020 atime=1743516868 20 ctime=1743591282 bluez-5.82/mesh/dbus.h0000644000000000000000000000176614015011623013374 0ustar00rootroot/* SPDX-License-Identifier: LGPL-2.1-or-later */ /* * * BlueZ - Bluetooth protocol stack for Linux * * Copyright (C) 2018 Intel Corporation. All rights reserved. * * */ #define BLUEZ_MESH_PATH "/org/bluez/mesh" #define BLUEZ_MESH_SERVICE "org.bluez.mesh" #define DEFAULT_DBUS_TIMEOUT 30 bool dbus_init(struct l_dbus *dbus); struct l_dbus *dbus_get_bus(void); void dbus_append_byte_array(struct l_dbus_message_builder *builder, const uint8_t *data, int len); void dbus_append_dict_entry_basic(struct l_dbus_message_builder *builder, const char *key, const char *signature, const void *data); bool dbus_match_interface(struct l_dbus_message_iter *interfaces, const char *match); struct l_dbus_message *dbus_error(struct l_dbus_message *msg, int err, const char *description); void dbus_send_with_timeout(struct l_dbus *dbus, struct l_dbus_message *msg, l_dbus_message_func_t cb, void *user_data, l_dbus_destroy_func_t destroy, unsigned int seconds); bluez-5.82/mesh/PaxHeaders/pb-adv.h0000644000000000000000000000005014447506754014144 xustar0020 atime=1743516868 20 ctime=1743591282 bluez-5.82/mesh/pb-adv.h0000644000000000000000000000064214447506754013627 0ustar00rootroot/* SPDX-License-Identifier: LGPL-2.1-or-later */ /* * * BlueZ - Bluetooth protocol stack for Linux * * Copyright (C) 2018 Intel Corporation. All rights reserved. * * */ bool pb_adv_reg(bool initiator, mesh_prov_open_func_t open_cb, mesh_prov_close_func_t close_cb, mesh_prov_receive_func_t rx_cb, mesh_prov_ack_func_t ack_cb, const uint8_t *uuid, void *user_data); void pb_adv_unreg(void *user_data); bluez-5.82/mesh/PaxHeaders/appkey.h0000644000000000000000000000005014214376354014255 xustar0020 atime=1743516868 20 ctime=1743591282 bluez-5.82/mesh/appkey.h0000644000000000000000000000252214214376354013737 0ustar00rootroot/* SPDX-License-Identifier: LGPL-2.1-or-later */ /* * * BlueZ - Bluetooth protocol stack for Linux * * Copyright (C) 2018-2019 Intel Corporation. All rights reserved. * * */ /* TODO: get this number from configuration */ #define MAX_APP_KEYS 32 struct mesh_app_key; bool appkey_key_init(struct mesh_net *net, uint16_t net_idx, uint16_t app_idx, uint8_t *key_value, uint8_t *new_key_value); void appkey_key_free(void *data); void appkey_finalize(struct mesh_net *net, uint16_t net_idx); const uint8_t *appkey_get_key(struct mesh_net *net, uint16_t app_idx, uint8_t *key_aid); int appkey_get_key_idx(struct mesh_app_key *app_key, const uint8_t **key, uint8_t *key_aid, const uint8_t **new_key, uint8_t *new_key_aid); bool appkey_have_key(struct mesh_net *net, uint16_t app_idx); uint16_t appkey_net_idx(struct mesh_net *net, uint16_t app_idx); int appkey_key_add(struct mesh_net *net, uint16_t net_idx, uint16_t app_idx, const uint8_t *new_key); int appkey_key_update(struct mesh_net *net, uint16_t net_idx, uint16_t app_idx, const uint8_t *new_key); int appkey_key_delete(struct mesh_net *net, uint16_t net_idx, uint16_t app_idx); void appkey_delete_bound_keys(struct mesh_net *net, uint16_t net_idx); uint8_t appkey_list(struct mesh_net *net, uint16_t net_idx, uint8_t *buf, uint16_t buf_size, uint16_t *size); bluez-5.82/mesh/PaxHeaders/mesh-mgmt.c0000644000000000000000000000005014772767672014675 xustar0020 atime=1743515579 20 ctime=1743591281 bluez-5.82/mesh/mesh-mgmt.c0000644000000000000000000001472314772767672014365 0ustar00rootroot// SPDX-License-Identifier: LGPL-2.1-or-later /* * * BlueZ - Bluetooth protocol stack for Linux * * Copyright (C) 2019 SILVAIR sp. z o.o. All rights reserved. * * */ #ifdef HAVE_CONFIG_H #include #endif #include #include #include "lib/bluetooth.h" #include "lib/mgmt.h" #include "src/shared/mgmt.h" #include "mesh/mesh-mgmt.h" struct mesh_controler { int index; bool mesh_support; bool powered; }; static mesh_mgmt_read_info_func_t ctl_info; static struct mgmt *mgmt_mesh; static struct l_queue *ctl_list; static void *list_user_data; static bool mesh_detected; static const uint8_t set_exp_feat_param_mesh[] = { 0x76, 0x6e, 0xf3, 0xe8, 0x24, 0x5f, 0x05, 0xbf, /* UUID - Mesh */ 0x8d, 0x4d, 0x03, 0x7a, 0xd7, 0x63, 0xe4, 0x2c, 0x01, /* Action - enable */ }; static bool by_index(const void *a, const void *b) { const struct mesh_controler *ctl = a; int index = L_PTR_TO_UINT(b); return ctl->index == index; } static void index_removed(uint16_t index, uint16_t length, const void *param, void *user_data); static void features_cb(uint8_t status, uint16_t length, const void *param, void *user_data) { int index = L_PTR_TO_UINT(user_data); struct mesh_controler *ctl; ctl = l_queue_find(ctl_list, by_index, L_UINT_TO_PTR(index)); if (!ctl) return; l_debug("Status: %d, Length: %d", status, length); if (status != MGMT_STATUS_NOT_SUPPORTED && status != MGMT_STATUS_UNKNOWN_COMMAND) { ctl->mesh_support = true; if (!mesh_detected) { mgmt_register(mgmt_mesh, MGMT_EV_INDEX_REMOVED, MGMT_INDEX_NONE, index_removed, NULL, NULL); } mesh_detected = true; } else l_debug("Kernel mesh not supported for hci%u", index); if (ctl_info) ctl_info(index, true, ctl->powered, ctl->mesh_support, list_user_data); } static void set_exp_mesh_cb(uint8_t status, uint16_t length, const void *param, void *user_data) { int index = L_PTR_TO_UINT(user_data); mesh_mgmt_send(MGMT_OP_MESH_READ_FEATURES, index, 0, NULL, features_cb, L_UINT_TO_PTR(index), NULL); } static void read_info_cb(uint8_t status, uint16_t length, const void *param, void *user_data) { int index = L_PTR_TO_UINT(user_data); const struct mgmt_rp_read_info *rp = param; uint32_t current_settings, supported_settings; struct mesh_controler *ctl; l_debug("hci %u status 0x%02x", index, status); ctl = l_queue_find(ctl_list, by_index, L_UINT_TO_PTR(index)); if (!ctl) return; if (status != MGMT_STATUS_SUCCESS) { ctl = l_queue_remove_if(ctl_list, by_index, L_UINT_TO_PTR(index)); l_error("Failed to read info for hci index %u: %s (0x%02x)", index, mgmt_errstr(status), status); l_warn("Hci dev %d removal detected", index); if (ctl && ctl_info) ctl_info(index, false, false, false, list_user_data); l_free(ctl); return; } if (length < sizeof(*rp)) { l_error("Read info response too short"); return; } current_settings = btohl(rp->current_settings); supported_settings = btohl(rp->supported_settings); l_debug("settings: supp %8.8x curr %8.8x", supported_settings, current_settings); if (!(supported_settings & MGMT_SETTING_LE)) { l_info("Controller hci %u does not support LE", index); l_queue_remove(ctl_list, ctl); l_free(ctl); return; } if (current_settings & MGMT_SETTING_POWERED) ctl->powered = true; mesh_mgmt_send(MGMT_OP_SET_EXP_FEATURE, index, sizeof(set_exp_feat_param_mesh), set_exp_feat_param_mesh, set_exp_mesh_cb, L_UINT_TO_PTR(index), NULL); } static void index_added(uint16_t index, uint16_t length, const void *param, void *user_data) { struct mesh_controler *ctl = l_queue_find(ctl_list, by_index, L_UINT_TO_PTR(index)); if (!ctl) { ctl = l_new(struct mesh_controler, 1); ctl->index = index; l_queue_push_head(ctl_list, ctl); } else { ctl->mesh_support = ctl->powered = false; } mgmt_send(mgmt_mesh, MGMT_OP_READ_INFO, index, 0, NULL, read_info_cb, L_UINT_TO_PTR(index), NULL); } static void index_removed(uint16_t index, uint16_t length, const void *param, void *user_data) { mgmt_send(mgmt_mesh, MGMT_OP_READ_INFO, index, 0, NULL, read_info_cb, L_UINT_TO_PTR(index), NULL); } static void read_index_list_cb(uint8_t status, uint16_t length, const void *param, void *user_data) { const struct mgmt_rp_read_index_list *rp = param; uint16_t num; int i; if (status != MGMT_STATUS_SUCCESS) { l_error("Failed to read index list: %s (0x%02x)", mgmt_errstr(status), status); return; } if (length < sizeof(*rp)) { l_error("Read index list response sixe too short"); return; } num = btohs(rp->num_controllers); l_debug("Number of controllers: %u", num); if (num * sizeof(uint16_t) + sizeof(*rp) != length) { l_error("Incorrect packet size for index list response"); return; } for (i = 0; i < num; i++) { uint16_t index; index = btohs(rp->index[i]); index_added(index, 0, NULL, user_data); } } static bool mesh_mgmt_init(void) { if (!ctl_list) ctl_list = l_queue_new(); if (!mgmt_mesh) { mgmt_mesh = mgmt_new_default(); if (!mgmt_mesh) { l_error("Failed to initialize mesh management"); return false; } mgmt_register(mgmt_mesh, MGMT_EV_INDEX_ADDED, MGMT_INDEX_NONE, index_added, NULL, NULL); } return true; } bool mesh_mgmt_list(mesh_mgmt_read_info_func_t cb, void *user_data) { if (!mesh_mgmt_init()) return false; ctl_info = cb; list_user_data = user_data; /* Use MGMT to find a candidate controller */ l_debug("send read index_list"); if (mgmt_send(mgmt_mesh, MGMT_OP_READ_INDEX_LIST, MGMT_INDEX_NONE, 0, NULL, read_index_list_cb, NULL, NULL) <= 0) return false; return true; } void mesh_mgmt_destroy(void) { mgmt_unref(mgmt_mesh); mgmt_mesh = NULL; ctl_info = NULL; list_user_data = NULL; l_queue_destroy(ctl_list, l_free); ctl_list = NULL; } unsigned int mesh_mgmt_send(uint16_t opcode, uint16_t index, uint16_t length, const void *param, mgmt_request_func_t callback, void *user_data, mgmt_destroy_func_t destroy) { return mgmt_send_timeout(mgmt_mesh, opcode, index, length, param, callback, user_data, destroy, 0); } unsigned int mesh_mgmt_register(uint16_t event, uint16_t index, mgmt_notify_func_t callback, void *user_data, mgmt_destroy_func_t destroy) { return mgmt_register(mgmt_mesh, event, index, callback, user_data, destroy); } bool mesh_mgmt_unregister(unsigned int id) { return mgmt_unregister(mgmt_mesh, id); } void mesh_mgmt_clear(void) { l_queue_clear(ctl_list, l_free); } bluez-5.82/mesh/PaxHeaders/cfgmod.h0000644000000000000000000000005014015011623014203 xustar0020 atime=1743516868 20 ctime=1743591282 bluez-5.82/mesh/cfgmod.h0000644000000000000000000000626314015011623013673 0ustar00rootroot/* SPDX-License-Identifier: LGPL-2.1-or-later */ /* * * BlueZ - Bluetooth protocol stack for Linux * * Copyright (C) 2018 Intel Corporation. All rights reserved. * * */ #define CONFIG_SRV_MODEL SET_ID(SIG_VENDOR, 0x0000) #define CONFIG_CLI_MODEL SET_ID(SIG_VENDOR, 0x0001) /* New List */ #define OP_APPKEY_ADD 0x00 #define OP_APPKEY_DELETE 0x8000 #define OP_APPKEY_GET 0x8001 #define OP_APPKEY_LIST 0x8002 #define OP_APPKEY_STATUS 0x8003 #define OP_APPKEY_UPDATE 0x01 #define OP_DEV_COMP_GET 0x8008 #define OP_DEV_COMP_STATUS 0x02 #define OP_CONFIG_BEACON_GET 0x8009 #define OP_CONFIG_BEACON_SET 0x800A #define OP_CONFIG_BEACON_STATUS 0x800B #define OP_CONFIG_DEFAULT_TTL_GET 0x800C #define OP_CONFIG_DEFAULT_TTL_SET 0x800D #define OP_CONFIG_DEFAULT_TTL_STATUS 0x800E #define OP_CONFIG_FRIEND_GET 0x800F #define OP_CONFIG_FRIEND_SET 0x8010 #define OP_CONFIG_FRIEND_STATUS 0x8011 #define OP_CONFIG_PROXY_GET 0x8012 #define OP_CONFIG_PROXY_SET 0x8013 #define OP_CONFIG_PROXY_STATUS 0x8014 #define OP_CONFIG_KEY_REFRESH_PHASE_GET 0x8015 #define OP_CONFIG_KEY_REFRESH_PHASE_SET 0x8016 #define OP_CONFIG_KEY_REFRESH_PHASE_STATUS 0x8017 #define OP_CONFIG_MODEL_PUB_GET 0x8018 #define OP_CONFIG_MODEL_PUB_SET 0x03 #define OP_CONFIG_MODEL_PUB_STATUS 0x8019 #define OP_CONFIG_MODEL_PUB_VIRT_SET 0x801A #define OP_CONFIG_MODEL_SUB_ADD 0x801B #define OP_CONFIG_MODEL_SUB_DELETE 0x801C #define OP_CONFIG_MODEL_SUB_DELETE_ALL 0x801D #define OP_CONFIG_MODEL_SUB_OVERWRITE 0x801E #define OP_CONFIG_MODEL_SUB_STATUS 0x801F #define OP_CONFIG_MODEL_SUB_VIRT_ADD 0x8020 #define OP_CONFIG_MODEL_SUB_VIRT_DELETE 0x8021 #define OP_CONFIG_MODEL_SUB_VIRT_OVERWRITE 0x8022 #define OP_CONFIG_NETWORK_TRANSMIT_GET 0x8023 #define OP_CONFIG_NETWORK_TRANSMIT_SET 0x8024 #define OP_CONFIG_NETWORK_TRANSMIT_STATUS 0x8025 #define OP_CONFIG_RELAY_GET 0x8026 #define OP_CONFIG_RELAY_SET 0x8027 #define OP_CONFIG_RELAY_STATUS 0x8028 #define OP_CONFIG_MODEL_SUB_GET 0x8029 #define OP_CONFIG_MODEL_SUB_LIST 0x802A #define OP_CONFIG_VEND_MODEL_SUB_GET 0x802B #define OP_CONFIG_VEND_MODEL_SUB_LIST 0x802C #define OP_CONFIG_POLL_TIMEOUT_GET 0x802D #define OP_CONFIG_POLL_TIMEOUT_STATUS 0x802E /* Health opcodes in health-mod.h */ #define OP_CONFIG_HEARTBEAT_PUB_GET 0x8038 #define OP_CONFIG_HEARTBEAT_PUB_SET 0x8039 #define OP_CONFIG_HEARTBEAT_PUB_STATUS 0x06 #define OP_CONFIG_HEARTBEAT_SUB_GET 0x803A #define OP_CONFIG_HEARTBEAT_SUB_SET 0x803B #define OP_CONFIG_HEARTBEAT_SUB_STATUS 0x803C #define OP_MODEL_APP_BIND 0x803D #define OP_MODEL_APP_STATUS 0x803E #define OP_MODEL_APP_UNBIND 0x803F #define OP_NETKEY_ADD 0x8040 #define OP_NETKEY_DELETE 0x8041 #define OP_NETKEY_GET 0x8042 #define OP_NETKEY_LIST 0x8043 #define OP_NETKEY_STATUS 0x8044 #define OP_NETKEY_UPDATE 0x8045 #define OP_NODE_IDENTITY_GET 0x8046 #define OP_NODE_IDENTITY_SET 0x8047 #define OP_NODE_IDENTITY_STATUS 0x8048 #define OP_NODE_RESET 0x8049 #define OP_NODE_RESET_STATUS 0x804A #define OP_MODEL_APP_GET 0x804B #define OP_MODEL_APP_LIST 0x804C #define OP_VEND_MODEL_APP_GET 0x804D #define OP_VEND_MODEL_APP_LIST 0x804E void cfgmod_server_init(struct mesh_node *node, uint8_t ele_idx); bluez-5.82/mesh/PaxHeaders/remprv-server.c0000644000000000000000000000005014772767672015616 xustar0020 atime=1743515579 20 ctime=1743591282 bluez-5.82/mesh/remprv-server.c0000644000000000000000000005156214772767672015310 0ustar00rootroot/* * * BlueZ - Bluetooth protocol stack for Linux * * Copyright (C) 2023 Intel Corporation. All rights reserved. * * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * */ #ifdef HAVE_CONFIG_H #include #endif #include #include #include #include "src/shared/ad.h" #include "mesh/mesh-defs.h" #include "mesh/mesh-io.h" #include "mesh/util.h" #include "mesh/node.h" #include "mesh/net.h" #include "mesh/appkey.h" #include "mesh/model.h" #include "mesh/prov.h" #include "mesh/provision.h" #include "mesh/pb-adv.h" #include "mesh/remprv.h" #define EXT_LIST_SIZE 60 #define RPR_DEV_KEY 0x00 #define RPR_ADDR 0x01 #define RPR_COMP 0x02 #define RPR_ADV 0xFF /* Internal use only*/ struct rem_scan_data { struct mesh_node *node; struct l_timeout *timeout; uint8_t *list; uint16_t client; uint16_t oob_info; uint16_t net_idx; uint8_t state; uint8_t scanned_limit; uint8_t addr[6]; uint8_t uuid[16]; uint8_t to_secs; uint8_t rxed_ads; uint8_t ext_cnt; bool fltr; uint8_t ext[0]; }; static struct rem_scan_data *rpb_scan; struct rem_prov_data { struct mesh_node *node; struct l_timeout *timeout; void *trans_data; uint16_t client; uint16_t net_idx; uint8_t svr_pdu_num; uint8_t cli_pdu_num; uint8_t state; uint8_t nppi_proc; union { struct { mesh_prov_open_func_t open_cb; mesh_prov_close_func_t close_cb; mesh_prov_receive_func_t rx_cb; mesh_prov_ack_func_t ack_cb; struct mesh_prov_node_info info; } nppi; struct { uint8_t uuid[17]; prov_trans_tx_t tx; } adv; } u; }; static struct rem_prov_data *rpb_prov; static const uint8_t prvb[2] = {BT_AD_MESH_BEACON, 0x00}; static const uint8_t pkt_filter = BT_AD_MESH_PROV; static const char *name = "Test Name"; static const uint8_t zero[] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; static void srv_open(void *user_data, prov_trans_tx_t adv_tx, void *trans_data, uint8_t nppi_proc) { struct rem_prov_data *prov = user_data; uint8_t msg[5]; int n; if (prov != rpb_prov || prov->state != PB_REMOTE_STATE_LINK_OPENING) return; l_debug("Remote Link open confirmed"); prov->u.adv.tx = adv_tx; prov->trans_data = trans_data; prov->state = PB_REMOTE_STATE_LINK_ACTIVE; n = mesh_model_opcode_set(OP_REM_PROV_LINK_REPORT, msg); msg[n++] = PB_REM_ERR_SUCCESS; msg[n++] = prov->state; mesh_model_send(prov->node, 0, prov->client, APP_IDX_DEV_LOCAL, prov->net_idx, DEFAULT_TTL, true, n, msg); } static void srv_rx(void *user_data, const void *dptr, uint16_t len) { struct rem_prov_data *prov = user_data; const uint8_t *data = dptr; uint8_t msg[69]; int n; if (prov != rpb_prov || prov->state < PB_REMOTE_STATE_LINK_ACTIVE || len > 65) return; l_debug("Remote PB IB-PDU"); prov->svr_pdu_num++; n = mesh_model_opcode_set(OP_REM_PROV_PDU_REPORT, msg); msg[n++] = prov->svr_pdu_num; memcpy(msg + n, data, len); n += len; mesh_model_send(prov->node, 0, prov->client, APP_IDX_DEV_LOCAL, prov->net_idx, DEFAULT_TTL, true, n, msg); } static void srv_ack(void *user_data, uint8_t msg_num) { struct rem_prov_data *prov = user_data; uint8_t msg[4]; int n; if (prov != rpb_prov || prov->state != PB_REMOTE_STATE_OB_PKT_TX) return; l_debug("Remote PB ACK"); prov->state = PB_REMOTE_STATE_LINK_ACTIVE; n = mesh_model_opcode_set(OP_REM_PROV_PDU_OB_REPORT, msg); msg[n++] = prov->cli_pdu_num; mesh_model_send(prov->node, 0, prov->client, APP_IDX_DEV_LOCAL, prov->net_idx, DEFAULT_TTL, true, n, msg); } static void srv_close(void *user_data, uint8_t reason) { struct rem_prov_data *prov = user_data; uint8_t msg[4]; int n; if (prov != rpb_prov || prov->state < PB_REMOTE_STATE_LINK_ACTIVE) return; l_debug("Remote PB Close"); prov->state = PB_REMOTE_STATE_LINK_CLOSING; n = mesh_model_opcode_set(OP_REM_PROV_LINK_REPORT, msg); msg[n++] = prov->state; msg[n++] = reason; mesh_model_send(prov->node, 0, prov->client, APP_IDX_DEV_LOCAL, prov->net_idx, DEFAULT_TTL, true, n, msg); } static void send_prov_status(struct rem_prov_data *prov, uint8_t status) { uint16_t n; uint8_t msg[5]; bool segmented = prov->state == PB_REMOTE_STATE_LINK_CLOSING ? true : false; n = mesh_model_opcode_set(OP_REM_PROV_LINK_STATUS, msg); msg[n++] = status; msg[n++] = prov->state; l_info("RPB-Link Status(%d): dst %4.4x", prov->state, prov->client); mesh_model_send(prov->node, 0, prov->client, APP_IDX_DEV_LOCAL, prov->net_idx, DEFAULT_TTL, segmented, n, msg); } static void remprv_prov_cancel(struct l_timeout *timeout, void *user_data) { struct rem_prov_data *prov = user_data; if (prov != rpb_prov) return; l_timeout_remove(prov->timeout); l_free(prov); rpb_prov = NULL; } static void deregister_ext_ad_type(uint8_t ad_type) { uint8_t short_ad; switch (ad_type) { case BT_AD_MESH_BEACON: case BT_AD_MESH_DATA: case BT_AD_MESH_PROV: case BT_AD_UUID16_SOME: case BT_AD_UUID32_SOME: case BT_AD_UUID128_SOME: case BT_AD_NAME_SHORT: return; case BT_AD_UUID16_ALL: case BT_AD_UUID32_ALL: case BT_AD_UUID128_ALL: case BT_AD_NAME_COMPLETE: /* Automatically get short versions */ short_ad = ad_type - 1; mesh_io_deregister_recv_cb(NULL, &short_ad, 1); /* fall through */ default: mesh_io_deregister_recv_cb(NULL, &ad_type, 1); break; } } static void remprv_scan_cancel(struct l_timeout *timeout, void *user_data) { struct rem_scan_data *scan = user_data; uint8_t msg[22 + EXT_LIST_SIZE]; uint16_t i, n; if (!scan || scan != rpb_scan) return; for (n = 0; n < scan->ext_cnt; n++) deregister_ext_ad_type(scan->ext[n]); if (scan->timeout == timeout) { /* Return Extended Results */ if (scan->ext_cnt) { /* Return Extended Result */ n = mesh_model_opcode_set( OP_REM_PROV_EXT_SCAN_REPORT, msg); msg[n++] = PB_REM_ERR_SUCCESS; memcpy(msg + n, scan->uuid, 16); n += 16; if (scan->oob_info) { l_put_le16(0, msg + n); n += 2; } i = 0; while (scan->list[i]) { msg[n++] = scan->list[i]; memcpy(msg + n, &scan->list[i + 1], scan->list[i]); n += scan->list[i]; i += scan->list[i] + 1; } } } l_timeout_remove(scan->timeout); l_free(scan->list); l_free(scan); rpb_scan = NULL; } static void scan_pkt(void *user_data, struct mesh_io_recv_info *info, const uint8_t *data, uint16_t len) { struct rem_scan_data *scan = user_data; uint8_t msg[22 + EXT_LIST_SIZE]; uint8_t addr[6]; uint16_t i, n; int8_t rssi; uint8_t filled = 0; bool report = false; if (scan != rpb_scan) return; if (info) { rssi = info->rssi; memcpy(addr, info->addr, 6); } else { rssi = 0; memset(addr, 0, 6); } if (scan->ext_cnt) goto extended_scan; /* RX Unprovisioned Beacon */ if (data[0] != BT_AD_MESH_BEACON || data[1] || (len != 18 && len != 20 && len != 24)) return; data += 2; len -= 2; for (n = 0; !report && n < scan->scanned_limit; n++) { if (!memcmp(&scan->list[n * 17 + 1], data, 16)) { /* Repeat UUID, check RSSI */ if ((int8_t) scan->list[n * 17] < rssi) { report = true; scan->list[n * 17] = (uint8_t) rssi; } } else if (!memcmp(&scan->list[n * 17 + 1], zero, 16)) { /* Found Empty slot */ report = true; scan->list[n * 17] = (uint8_t) rssi; memcpy(&scan->list[n * 17 + 1], data, 16); } filled++; } if (!report) return; n = mesh_model_opcode_set(OP_REM_PROV_SCAN_REPORT, msg); msg[n++] = (uint8_t) rssi; memcpy(msg + n, data, len); n += len; /* Always return oob_info, even if it wasn't in beacon */ if (len == 16) { l_put_le16(0, msg + n); n += 2; } goto send_report; extended_scan: if (data[0] == BT_AD_MESH_BEACON && !data[1]) { if (len != 18 && len != 20 && len != 24) return; /* Check UUID */ if (memcmp(data + 2, scan->uuid, 16)) return; /* Zero AD list if prior data RXed from different bd_addr */ if (memcmp(scan->addr, addr, 6)) { scan->list[0] = 0; scan->rxed_ads = 0; } memcpy(scan->addr, addr, 6); scan->fltr = true; if (len >= 20) scan->oob_info = l_get_le16(data + 18); if (scan->rxed_ads != scan->ext_cnt) return; } else if (data[0] != BT_AD_MESH_BEACON) { if (!scan->fltr || !memcmp(scan->addr, addr, 6)) { i = 0; while (scan->list[i]) { /* check if seen */ if (scan->list[i + 1] == data[0]) return; i += scan->list[i] + 1; } /* Overflow Protection */ if (i + len + 1 > EXT_LIST_SIZE) return; scan->list[i] = len; scan->list[i + len + 1] = 0; memcpy(scan->list + i + 1, data, len); scan->rxed_ads++; } if (scan->rxed_ads != scan->ext_cnt) return; } else return; n = mesh_model_opcode_set(OP_REM_PROV_EXT_SCAN_REPORT, msg); msg[n++] = PB_REM_ERR_SUCCESS; memcpy(msg + n, scan->uuid, 16); n += 16; l_put_le16(scan->oob_info, msg + n); n += 2; i = 0; while (scan->list[i]) { msg[n++] = scan->list[i]; memcpy(msg + n, &scan->list[i + 1], scan->list[i]); n += scan->list[i]; i += scan->list[i]; } send_report: print_packet("App Tx", msg, n); mesh_model_send(scan->node, 0, scan->client, APP_IDX_DEV_LOCAL, scan->net_idx, DEFAULT_TTL, true, n, msg); /* Clean-up if we are done reporting*/ if (filled == scan->scanned_limit || scan->ext_cnt) remprv_scan_cancel(NULL, scan); } static bool register_ext_ad_type(uint8_t ad_type, struct rem_scan_data *scan) { uint8_t short_ad; switch (ad_type) { case BT_AD_MESH_PROV: case BT_AD_UUID16_SOME: case BT_AD_UUID32_SOME: case BT_AD_UUID128_SOME: case BT_AD_NAME_SHORT: /* Illegal Requests */ return false; case BT_AD_UUID16_ALL: case BT_AD_UUID32_ALL: case BT_AD_UUID128_ALL: case BT_AD_NAME_COMPLETE: /* Automatically get short versions */ short_ad = ad_type - 1; mesh_io_register_recv_cb(NULL, &short_ad, 1, scan_pkt, scan); /* fall through */ default: mesh_io_register_recv_cb(NULL, &ad_type, 1, scan_pkt, scan); /* fall through */ case BT_AD_MESH_BEACON: /* Ignored/auto request */ break; } return true; } static void link_active(void *user_data) { struct rem_prov_data *prov = user_data; uint8_t msg[5]; int n; if (prov != rpb_prov || prov->state != PB_REMOTE_STATE_LINK_OPENING) return; l_debug("Remote Link open confirmed"); prov->state = PB_REMOTE_STATE_LINK_ACTIVE; n = mesh_model_opcode_set(OP_REM_PROV_LINK_REPORT, msg); msg[n++] = PB_REM_ERR_SUCCESS; msg[n++] = PB_REMOTE_STATE_LINK_ACTIVE; mesh_model_send(prov->node, 0, prov->client, APP_IDX_DEV_LOCAL, prov->net_idx, DEFAULT_TTL, true, n, msg); } bool register_nppi_acceptor(mesh_prov_open_func_t open_cb, mesh_prov_close_func_t close_cb, mesh_prov_receive_func_t rx_cb, mesh_prov_ack_func_t ack_cb, void *user_data) { struct rem_prov_data *prov = rpb_prov; if (!prov || prov->nppi_proc == RPR_ADV) return false; prov->u.nppi.open_cb = open_cb; prov->u.nppi.close_cb = close_cb; prov->u.nppi.rx_cb = rx_cb; prov->u.nppi.ack_cb = ack_cb; prov->trans_data = user_data; open_cb(user_data, srv_rx, prov, prov->nppi_proc); l_idle_oneshot(link_active, prov, NULL); return true; } static bool nppi_cmplt(void *user_data, uint8_t status, struct mesh_prov_node_info *info) { struct rem_prov_data *prov = user_data; if (prov != rpb_prov) return false; /* Save new info to apply on Link Close */ prov->u.nppi.info = *info; return true; } static bool start_dev_key_refresh(struct mesh_node *node, uint8_t nppi_proc, struct rem_prov_data *prov) { uint8_t num_ele = node_get_num_elements(node); prov->nppi_proc = nppi_proc; return acceptor_start(num_ele, NULL, 0x0001, 60, NULL, nppi_cmplt, prov); } static bool remprv_srv_pkt(uint16_t src, uint16_t unicast, uint16_t app_idx, uint16_t net_idx, const uint8_t *data, uint16_t size, const void *user_data) { struct rem_prov_data *prov = rpb_prov; struct rem_scan_data *scan = rpb_scan; struct mesh_node *node = (struct mesh_node *) user_data; const uint8_t *pkt = data; bool segmented = false; uint32_t opcode; uint8_t msg[69]; uint8_t old_state, status; uint16_t n; if (app_idx != APP_IDX_DEV_LOCAL) return false; if (mesh_model_opcode_get(pkt, size, &opcode, &n)) { size -= n; pkt += n; } else return false; n = 0; switch (opcode) { default: return false; case OP_REM_PROV_SCAN_CAP_GET: if (size != 0) return true; /* Compose Scan Info Status */ n = mesh_model_opcode_set(OP_REM_PROV_SCAN_CAP_STATUS, msg); msg[n++] = PB_REMOTE_MAX_SCAN_QUEUE_SIZE; msg[n++] = 1; /* Active Scanning Supported */ break; case OP_REM_PROV_EXT_SCAN_START: if (!size || !pkt[0]) return true; /* Size check the message */ if (pkt[0] + 18 == size) { /* Range check the Timeout */ if (!pkt[size - 1] || pkt[size - 1] > 5) return true; } else if (pkt[0] + 1 != size) return true; /* Get local device extended info */ if (pkt[0] + 18 != size) { n = mesh_model_opcode_set( OP_REM_PROV_EXT_SCAN_REPORT, msg); msg[n++] = PB_REM_ERR_SUCCESS; memcpy(msg + n, node_uuid_get(node), 16); n += 16; l_put_le16(0, msg + n); n += 2; size--; pkt++; while (size--) { if (*pkt++ == BT_AD_NAME_COMPLETE) { msg[n] = strlen(name) + 1; if (msg[n] > sizeof(msg) - n - 1) msg[n] = sizeof(msg) - n - 1; n++; msg[n++] = BT_AD_NAME_COMPLETE; memcpy(&msg[n], name, msg[n - 2] - 1); n += msg[n - 2] - 1; goto send_pkt; } } /* Send internal report */ l_debug("Send internal extended info %d", n); goto send_pkt; } status = PB_REM_ERR_SUCCESS; if (scan) { if (scan->client != src || scan->node != node || scan->ext_cnt != pkt[0]) status = PB_REM_ERR_SCANNING_CANNOT_START; else if (memcmp(scan->ext, pkt + 1, pkt[0])) status = PB_REM_ERR_SCANNING_CANNOT_START; else if (memcmp(scan->uuid, pkt + 2, 16)) status = PB_REM_ERR_SCANNING_CANNOT_START; } if (status != PB_REM_ERR_SUCCESS) { n = mesh_model_opcode_set(OP_REM_PROV_EXT_SCAN_REPORT, msg); msg[n++] = status; memset(msg + n, 0, 16); n += 16; segmented = true; break; } /* Ignore extended requests while already scanning */ if (scan) return true; scan = (void *) l_new(uint8_t, sizeof(struct rem_scan_data) + pkt[0]); /* Validate and register Extended AD types */ for (n = 0; n < pkt[0]; n++) { if (!register_ext_ad_type(pkt[1 + n], scan)) { /* Invalid AD type detected -- Undo */ while (n--) deregister_ext_ad_type(pkt[1 + n]); l_free(scan); return true; } } rpb_scan = scan; scan->client = src; scan->net_idx = net_idx; memcpy(scan->uuid, pkt + size - 17, 16); scan->ext_cnt = pkt[0]; memcpy(scan->ext, pkt + 1, pkt[0]); scan->list = l_malloc(EXT_LIST_SIZE); scan->list[0] = 0; mesh_io_register_recv_cb(NULL, prvb, sizeof(prvb), scan_pkt, scan); scan->timeout = l_timeout_create(pkt[size-1], remprv_scan_cancel, scan, NULL); return true; case OP_REM_PROV_SCAN_START: if (size != 2 && size != 18) return true; /* Reject Timeout of Zero */ if (!pkt[1]) return true; status = PB_REM_ERR_SUCCESS; if (scan) { if (scan->ext_cnt || scan->client != src || scan->node != node) status = PB_REM_ERR_SCANNING_CANNOT_START; else if (!!(scan->fltr) != !!(size != 18)) status = PB_REM_ERR_SCANNING_CANNOT_START; else if (scan->fltr && memcmp(scan->uuid, pkt + 2, 16)) status = PB_REM_ERR_SCANNING_CANNOT_START; } if (status != PB_REM_ERR_SUCCESS) { n = mesh_model_opcode_set(OP_REM_PROV_SCAN_STATUS, msg); msg[n++] = status; msg[n++] = scan ? scan->state : 0; msg[n++] = scan ? scan->scanned_limit : PB_REMOTE_MAX_SCAN_QUEUE_SIZE; msg[n++] = scan ? scan->to_secs : 0; break; } if (!scan) scan = l_new(struct rem_scan_data, 1); rpb_scan = scan; if (size == 18) { memcpy(scan->uuid, pkt + 2, 16); scan->fltr = true; scan->state = 0x02; /* Limited */ } else { memset(scan->uuid, 0, 16); scan->fltr = false; scan->state = 0x01; /* Unlimited */ } scan->client = src; scan->net_idx = net_idx; scan->node = node; if (!scan->list) scan->list = l_new(uint8_t, 23 * PB_REMOTE_MAX_SCAN_QUEUE_SIZE); mesh_io_register_recv_cb(NULL, prvb, 2, scan_pkt, scan); scan->to_secs = pkt[1]; if (pkt[0]) scan->scanned_limit = pkt[0]; else scan->scanned_limit = PB_REMOTE_MAX_SCAN_QUEUE_SIZE; scan->timeout = l_timeout_create(pkt[1], remprv_scan_cancel, scan, NULL); /* fall through */ case OP_REM_PROV_SCAN_GET: /* Compose Scan Status */ n = mesh_model_opcode_set(OP_REM_PROV_SCAN_STATUS, msg); msg[n++] = PB_REM_ERR_SUCCESS; msg[n++] = scan ? scan->state : 0; msg[n++] = scan ? scan->scanned_limit : PB_REMOTE_MAX_SCAN_QUEUE_SIZE; msg[n++] = scan ? scan->to_secs : 0; break; case OP_REM_PROV_SCAN_STOP: if (size != 0 || !scan) return true; remprv_scan_cancel(NULL, scan); return true; case OP_REM_PROV_LINK_GET: if (size != 0 || !prov) return true; send_prov_status(prov, PB_REM_ERR_SUCCESS); return true; case OP_REM_PROV_LINK_OPEN: /* Sanity check args */ if (size != 16 && size != 17 && size != 1) return true; if (size == 17 && (pkt[16] == 0 || pkt[16] > 0x3c)) return true; if (size == 1 && pkt[0] > 0x02) return true; if (prov) { if (prov->client != src || prov->node != node || (size == 1 && prov->nppi_proc != pkt[0]) || (size >= 16 && (prov->nppi_proc != RPR_ADV || memcmp(prov->u.adv.uuid, pkt, 16)))) { /* Send Reject (in progress) */ send_prov_status(prov, PB_REM_ERR_CANNOT_OPEN); n = mesh_model_opcode_set( OP_REM_PROV_LINK_STATUS, msg); msg[n++] = PB_REM_ERR_CANNOT_OPEN; msg[n++] = PB_REMOTE_STATE_LINK_ACTIVE; break; } /* Send redundant Success */ send_prov_status(prov, PB_REM_ERR_SUCCESS); return true; } if (scan && scan->client != src && scan->node != node) { n = mesh_model_opcode_set(OP_REM_PROV_LINK_STATUS, msg); msg[n++] = PB_REM_ERR_CANNOT_OPEN; msg[n++] = PB_REMOTE_STATE_LINK_ACTIVE; break; } print_packet("Remote Prov Link Open", pkt, size); remprv_scan_cancel(NULL, scan); rpb_prov = prov = l_new(struct rem_prov_data, 1); prov->client = src; prov->net_idx = net_idx; prov->node = node; prov->state = PB_REMOTE_STATE_LINK_OPENING; if (size == 1) { status = start_dev_key_refresh(node, pkt[0], prov); } else { if (size == 17) prov->timeout = l_timeout_create(pkt[16], remprv_prov_cancel, prov, NULL); prov->nppi_proc = RPR_ADV; memcpy(prov->u.adv.uuid, pkt, 16); status = pb_adv_reg(true, srv_open, srv_close, srv_rx, srv_ack, pkt, prov); } if (status) send_prov_status(prov, PB_REM_ERR_SUCCESS); else { n = mesh_model_opcode_set(OP_REM_PROV_LINK_STATUS, msg); msg[n++] = PB_REM_ERR_CANNOT_OPEN; msg[n++] = PB_REMOTE_STATE_IDLE; remprv_prov_cancel(NULL, prov); } return true; case OP_REM_PROV_LINK_CLOSE: if (size != 1) return true; if (!prov || prov->node != node || prov->client != src) return true; old_state = prov->state; prov->state = PB_REMOTE_STATE_LINK_CLOSING; mesh_io_send_cancel(NULL, &pkt_filter, sizeof(pkt_filter)); send_prov_status(prov, PB_REM_ERR_SUCCESS); if (pkt[0] == 0x02 && old_state >= PB_REMOTE_STATE_LINK_ACTIVE) { msg[0] = PROV_FAILED; msg[1] = PROV_ERR_CANT_ASSIGN_ADDR; if (prov->nppi_proc == RPR_ADV) prov->u.adv.tx(prov->trans_data, msg, 2); else prov->u.nppi.rx_cb(prov->trans_data, msg, 2); } if (prov->nppi_proc == RPR_ADV) pb_adv_unreg(prov); else if (prov->nppi_proc <= RPR_COMP) { /* Hard or Soft refresh of local node, based on NPPI */ node_refresh(prov->node, (prov->nppi_proc == RPR_ADDR), &prov->u.nppi.info); } remprv_prov_cancel(NULL, prov); return true; case OP_REM_PROV_PDU_SEND: if (!prov || prov->node != node || prov->client != src) return true; if (size < 2) return true; prov->cli_pdu_num = *pkt++; size--; prov->state = PB_REMOTE_STATE_OB_PKT_TX; if (prov->nppi_proc == RPR_ADV) prov->u.adv.tx(prov->trans_data, pkt, size); else { srv_ack(prov, prov->cli_pdu_num); prov->u.nppi.rx_cb(prov->trans_data, pkt, size); } return true; } send_pkt: l_info("PB-SVR: src %4.4x dst %4.4x", unicast, src); print_packet("App Tx", msg, n); mesh_model_send(node, 0, src, APP_IDX_DEV_LOCAL, net_idx, DEFAULT_TTL, segmented, n, msg); return true; } static void remprv_srv_unregister(void *user_data) { } static const struct mesh_model_ops ops = { .unregister = remprv_srv_unregister, .recv = remprv_srv_pkt, .bind = NULL, .sub = NULL, .pub = NULL }; void remote_prov_server_init(struct mesh_node *node, uint8_t ele_idx) { mesh_model_register(node, ele_idx, REM_PROV_SRV_MODEL, &ops, node); } bluez-5.82/mesh/PaxHeaders/bluetooth-mesh.conf0000644000000000000000000000005013540747027016421 xustar0020 atime=1743516876 20 ctime=1743591289 bluez-5.82/mesh/bluetooth-mesh.conf0000644000000000000000000000144213540747027016103 0ustar00rootroot bluez-5.82/mesh/PaxHeaders/prov.h0000644000000000000000000000005014447506754013761 xustar0020 atime=1743516868 20 ctime=1743591282 bluez-5.82/mesh/prov.h0000644000000000000000000001154614447506754013451 0ustar00rootroot/* SPDX-License-Identifier: LGPL-2.1-or-later */ /* * * BlueZ - Bluetooth protocol stack for Linux * * Copyright (C) 2018 Intel Corporation. All rights reserved. * * */ #ifndef __packed #define __packed __attribute__((packed)) #endif struct mesh_net; struct mesh_dev; enum mesh_trans { MESH_TRANS_IDLE, MESH_TRANS_TX, MESH_TRANS_RX, }; enum mesh_bearer { MESH_BEARER_IDLE, MESH_BEARER_ADV, }; enum mesh_prov_mode { MESH_PROV_MODE_NONE, MESH_PROV_MODE_INITIATOR, MESH_PROV_MODE_GATT_ACCEPTOR, MESH_PROV_MODE_ADV_ACCEPTOR, MESH_PROV_MODE_GATT_CLIENT, MESH_PROV_MODE_MESH_SERVER, MESH_PROV_MODE_MESH_CLIENT, MESH_PROV_MODE_MESH_GATT_CLIENT, }; struct mesh_prov; typedef void (*prov_trans_tx_t)(void *tx_data, const void *data, uint16_t len); typedef void (*mesh_prov_open_func_t)(void *user_data, prov_trans_tx_t trans_tx, void *trans_data, uint8_t trans_type); typedef void (*mesh_prov_close_func_t)(void *user_data, uint8_t reason); typedef void (*mesh_prov_send_func_t)(bool success, struct mesh_prov *prov); typedef void (*mesh_prov_ack_func_t)(void *user_data, uint8_t msg_num); typedef void (*mesh_prov_receive_func_t)(void *user_data, const void *data, uint16_t size); struct prov_invite { uint8_t attention; } __packed; struct prov_invite_msg { uint8_t opcode; struct prov_invite invite; } __packed; struct prov_start { uint8_t algorithm; uint8_t pub_key; uint8_t auth_method; uint8_t auth_action; uint8_t auth_size; } __packed; struct prov_caps_msg { uint8_t opcode; struct mesh_net_prov_caps caps; } __packed; struct prov_start_msg { uint8_t opcode; struct prov_start start; } __packed; struct prov_pub_key_msg { uint8_t opcode; uint8_t pub_key[64]; } __packed; struct prov_conf_msg { uint8_t opcode; uint8_t conf[16]; } __packed; struct prov_rand_msg { uint8_t opcode; uint8_t rand[16]; } __packed; struct prov_data { uint8_t net_key[16]; uint16_t net_idx; uint8_t flags; uint32_t iv_index; uint16_t primary; } __packed; struct prov_data_msg { uint8_t opcode; struct prov_data data; uint64_t mic; } __packed; struct prov_fail_msg { uint8_t opcode; uint8_t reason; } __packed; struct conf_input { struct prov_invite invite; struct mesh_net_prov_caps caps; struct prov_start start; uint8_t prv_pub_key[64]; uint8_t dev_pub_key[64]; } __packed; struct mesh_prov { int ref_count; struct mesh_dev *dev; struct mesh_net *net; enum mesh_prov_mode mode; enum mesh_trans trans; enum mesh_bearer bearer; uint8_t uuid[16]; uint8_t caps[12]; uint32_t conn_id; uint16_t net_idx; uint16_t remote; uint16_t addr; uint16_t expected_len; uint16_t packet_len; uint8_t local_msg_num; uint8_t peer_msg_num; uint8_t last_peer_msg_num; uint8_t got_segs; uint8_t expected_segs; uint8_t expected_fcs; uint8_t packet_buf[80]; uint8_t peer_buf[80]; struct timeval tx_start; struct l_timeout *tx_timeout; /* Provisioning credentials and crypto material */ struct conf_input conf_inputs; uint8_t dev_key[16]; uint8_t conf_salt[16]; uint8_t s_key[16]; uint8_t s_nonce[13]; uint8_t conf_key[16]; uint8_t conf[16]; uint8_t r_conf[16]; uint8_t rand_auth[32]; uint8_t prov_salt[16]; uint8_t secret[32]; uint8_t r_public[64]; uint8_t l_public[64]; /* End Provisioning credentials and crypto material */ mesh_prov_open_func_t open_callback; mesh_prov_close_func_t close_callback; mesh_prov_receive_func_t receive_callback; void *receive_data; mesh_prov_send_func_t send_callback; void *send_data; }; struct mesh_prov *mesh_prov_new(struct mesh_net *net, uint16_t remote); struct mesh_prov *mesh_prov_ref(struct mesh_prov *prov); void mesh_prov_unref(struct mesh_prov *prov); bool mesh_prov_gatt_client(struct mesh_prov *prov, struct mesh_dev *dev, uint8_t uuid[16], mesh_prov_open_func_t open_callback, mesh_prov_close_func_t close_callback, mesh_prov_receive_func_t recv_callback, void *user_data); bool mesh_prov_listen(struct mesh_net *net, uint8_t uuid[16], uint8_t caps[12], mesh_prov_open_func_t open_callback, mesh_prov_close_func_t close_callback, mesh_prov_receive_func_t recv_callback, void *user_data); bool mesh_prov_connect(struct mesh_prov *prov, struct mesh_dev *dev, uint16_t net_idx, uint8_t uuid[16], mesh_prov_open_func_t open_callback, mesh_prov_close_func_t close_callback, mesh_prov_receive_func_t recv_callback, void *user_data); unsigned int mesh_prov_send(struct mesh_prov *prov, const void *data, uint16_t size, mesh_prov_send_func_t send_callback, void *user_data); bool mesh_prov_cancel(struct mesh_prov *prov, unsigned int id); bool mesh_prov_close(struct mesh_prov *prov, uint8_t reason); void mesh_prov_set_addr(struct mesh_prov *prov, uint16_t addr); uint16_t mesh_prov_get_addr(struct mesh_prov *prov); void mesh_prov_set_idx(struct mesh_prov *prov, uint16_t net_idx); uint16_t mesh_prov_get_idx(struct mesh_prov *prov); bluez-5.82/mesh/PaxHeaders/mesh-io.h0000644000000000000000000000005014447506754014334 xustar0020 atime=1743516868 20 ctime=1743591281 bluez-5.82/mesh/mesh-io.h0000644000000000000000000000407514447506754014023 0ustar00rootroot/* SPDX-License-Identifier: LGPL-2.1-or-later */ /* * * BlueZ - Bluetooth protocol stack for Linux * * Copyright (C) 2018 Intel Corporation. All rights reserved. * * */ #define MESH_IO_TX_COUNT_UNLIMITED 0 enum mesh_io_type { MESH_IO_TYPE_NONE = 0, MESH_IO_TYPE_UNIT_TEST, MESH_IO_TYPE_AUTO, /* If MGMT required, add after here */ MESH_IO_TYPE_MGMT, MESH_IO_TYPE_GENERIC, }; enum mesh_io_timing_type { MESH_IO_TIMING_TYPE_GENERAL = 1, MESH_IO_TIMING_TYPE_POLL, MESH_IO_TIMING_TYPE_POLL_RSP }; struct mesh_io_recv_info { const uint8_t *addr; uint32_t instant; uint8_t chan; int8_t rssi; }; struct mesh_io_send_info { enum mesh_io_timing_type type; union { struct { uint16_t interval; uint8_t cnt; uint8_t min_delay; uint8_t max_delay; } gen; struct { uint16_t scan_duration; uint8_t scan_delay; uint8_t filter_ids[2]; uint8_t min_delay; uint8_t max_delay; } poll; struct { uint32_t instant; uint8_t delay; } poll_rsp; } u; }; struct mesh_io_caps { uint8_t max_num_filters; uint8_t window_accuracy; }; typedef void (*mesh_io_recv_func_t)(void *user_data, struct mesh_io_recv_info *info, const uint8_t *data, uint16_t len); typedef void (*mesh_io_recv_ext_func_t)(void *user_data, struct mesh_io_recv_info *info, const uint8_t *data, uint16_t len, const uint8_t *addr); typedef void (*mesh_io_ready_func_t)(void *user_data, bool result); struct mesh_io *mesh_io_new(enum mesh_io_type type, void *opts, mesh_io_ready_func_t cb, void *user_data); void mesh_io_destroy(struct mesh_io *io); bool mesh_io_get_caps(struct mesh_io *io, struct mesh_io_caps *caps); bool mesh_io_register_recv_cb(struct mesh_io *io, const uint8_t *filter, uint8_t len, mesh_io_recv_func_t cb, void *user_data); bool mesh_io_deregister_recv_cb(struct mesh_io *io, const uint8_t *filter, uint8_t len); bool mesh_io_send(struct mesh_io *io, struct mesh_io_send_info *info, const uint8_t *data, uint16_t len); bool mesh_io_send_cancel(struct mesh_io *io, const uint8_t *pattern, uint8_t len); bluez-5.82/mesh/PaxHeaders/rpl.h0000644000000000000000000000005014015011623013541 xustar0020 atime=1743516868 20 ctime=1743591282 bluez-5.82/mesh/rpl.h0000644000000000000000000000110014015011623013212 0ustar00rootroot/* SPDX-License-Identifier: LGPL-2.1-or-later */ /* * * BlueZ - Bluetooth protocol stack for Linux * * Copyright (C) 2020 Intel Corporation. All rights reserved. * * */ struct mesh_rpl { uint32_t iv_index; uint32_t seq; uint16_t src; }; bool rpl_put_entry(struct mesh_node *node, uint16_t src, uint32_t iv_index, uint32_t seq); void rpl_del_entry(struct mesh_node *node, uint16_t src); bool rpl_get_list(struct mesh_node *node, struct l_queue *rpl_list); void rpl_update(struct mesh_node *node, uint32_t iv_index); bool rpl_init(const char *node_path); bluez-5.82/mesh/PaxHeaders/net.h0000644000000000000000000000005014447506754013561 xustar0020 atime=1743516868 20 ctime=1743591282 bluez-5.82/mesh/net.h0000644000000000000000000002402714447506754013247 0ustar00rootroot/* SPDX-License-Identifier: LGPL-2.1-or-later */ /* * * BlueZ - Bluetooth protocol stack for Linux * * Copyright (C) 2018-2019 Intel Corporation. All rights reserved. * * */ #ifndef __packed #define __packed __attribute__((packed)) #endif struct mesh_io; struct mesh_node; #define DEV_ID 0 #define UNUSED_KEY_IDX 0xffff #define APP_AID_DEV 0x00 #define CTL 0x80 #define KEY_CACHE_SIZE 64 #define FRND_CACHE_MAX 32 #define MAX_UNSEG_LEN 15 /* msg_len == 11 + sizeof(MIC) */ #define MAX_SEG_LEN 12 /* UnSeg length - 3 octets overhead */ #define SEG_MAX(seg, len) ((!seg && len <= MAX_UNSEG_LEN) ? 0 : \ (((len) - 1) / MAX_SEG_LEN)) #define SEG_OFF(seg) ((seg) * MAX_SEG_LEN) #define MAX_SEG_TO_LEN(seg) ((seg) ? SEG_OFF((seg) + 1) : MAX_UNSEG_LEN) #define SEGMENTED 0x80 #define UNSEGMENTED 0x00 #define SEG_HDR_SHIFT 31 #define IS_SEGMENTED(hdr) (!!((hdr) & ((uint32_t) 0x1 << SEG_HDR_SHIFT))) #define KEY_ID_MASK 0x7f #define KEY_AID_MASK 0x3f #define KEY_ID_AKF 0x40 #define KEY_AID_SHIFT 0 #define AKF_HDR_SHIFT 30 #define KEY_HDR_SHIFT 24 #define HAS_APP_KEY(hdr) (!!((hdr) & ((uint32_t) 0x1 << AKF_HDR_SHIFT))) #define OPCODE_MASK 0x7f #define OPCODE_HDR_SHIFT 24 #define RELAY 0x80 #define RELAY_HDR_SHIFT 23 #define SZMIC 0x80 #define SZMIC_HDR_SHIFT 23 #define SEQ_ZERO_MASK 0x1fff #define SEQ_ZERO_HDR_SHIFT 10 #define IS_RELAYED(hdr) (!!((hdr) & ((uint32_t) 0x1 << RELAY_HDR_SHIFT))) #define HAS_MIC64(hdr) (!!((hdr) & ((uint32_t) 0x1 << SZMIC_HDR_SHIFT))) #define SEG_MASK 0x1f #define SEGO_HDR_SHIFT 5 #define SEGN_HDR_SHIFT 0 #define SEG_TOTAL(hdr) (((hdr) >> SEGN_HDR_SHIFT) & SEG_MASK) /* Mask of Hdr bits which must be constant over entire incoming SAR message */ /* (SEG || AKF || AID || SZMIC || SeqZero || SegN) */ #define HDR_KEY_MASK ((true << SEG_HDR_SHIFT) | \ (KEY_ID_MASK << KEY_HDR_SHIFT) | \ (true << SZMIC_HDR_SHIFT) | \ (SEQ_ZERO_MASK << SEQ_ZERO_HDR_SHIFT) | \ (SEG_MASK << SEGN_HDR_SHIFT)) #define HDR_ACK_MASK ((OPCODE_MASK << OPCODE_HDR_SHIFT) | \ (SEQ_ZERO_MASK << SEQ_ZERO_HDR_SHIFT)) #define MSG_CACHE_SIZE 70 #define REPLAY_CACHE_SIZE 10 /* Proxy Configuration Opcodes */ #define PROXY_OP_SET_FILTER_TYPE 0x00 #define PROXY_OP_FILTER_ADD 0x01 #define PROXY_OP_FILTER_DEL 0x02 #define PROXY_OP_FILTER_STATUS 0x03 /* Proxy Filter Defines */ #define PROXY_FILTER_ACCEPT_LIST 0x00 #define PROXY_FILTER_REJECT_LIST 0x01 /* Network Tranport Opcodes */ #define NET_OP_SEG_ACKNOWLEDGE 0x00 #define NET_OP_FRND_POLL 0x01 #define NET_OP_FRND_UPDATE 0x02 #define NET_OP_FRND_REQUEST 0x03 #define NET_OP_FRND_OFFER 0x04 #define NET_OP_FRND_CLEAR 0x05 #define NET_OP_FRND_CLEAR_CONFIRM 0x06 #define NET_OP_PROXY_SUB_ADD 0x07 #define NET_OP_PROXY_SUB_REMOVE 0x08 #define NET_OP_PROXY_SUB_CONFIRM 0x09 #define NET_OP_HEARTBEAT 0x0a #define FRND_OPCODE(x) \ ((x) >= NET_OP_FRND_POLL && (x) <= NET_OP_FRND_CLEAR_CONFIRM) #define DEFAULT_MIN_DELAY 0 #define DEFAULT_MAX_DELAY 25 struct mesh_net_prov_caps { uint8_t num_ele; uint16_t algorithms; uint8_t pub_type; uint8_t static_type; uint8_t output_size; uint16_t output_action; uint8_t input_size; uint16_t input_action; } __packed; struct mesh_net_heartbeat_sub { struct l_timeout *timer; uint32_t start; uint32_t period; uint16_t features; uint16_t src; uint16_t dst; uint16_t count; bool enabled; uint8_t min_hops; uint8_t max_hops; }; struct mesh_net_heartbeat_pub { struct l_timeout *timer; uint32_t period; uint16_t dst; uint16_t count; uint16_t features; uint16_t net_idx; uint8_t ttl; }; struct mesh_key_set { bool frnd; uint8_t nid; uint8_t enc_key[16]; uint8_t privacy_key[16]; }; struct friend_neg { int8_t rssi; bool clearing; }; struct friend_act { uint16_t *grp_list; uint32_t last_hdr; int16_t grp_cnt; bool seq; bool last; }; struct mesh_friend { struct mesh_net *net; struct l_timeout *timeout; struct l_queue *pkt_cache; void *pkt; uint32_t poll_timeout; uint32_t net_key_cur; uint32_t net_key_upd; uint16_t old_friend; uint16_t net_idx; uint16_t lp_addr;/* dst; * Primary Element unicast addr */ uint16_t fn_cnt; uint16_t lp_cnt; uint8_t receive_delay; uint8_t ele_cnt; uint8_t frd; uint8_t frw; union { struct friend_neg negotiate; struct friend_act active; } u; }; struct mesh_friend_seg_one { uint32_t hdr; uint32_t seq; bool sent; bool md; uint8_t data[15]; }; struct mesh_friend_seg_12 { uint32_t hdr; uint32_t seq; bool sent; bool md; uint8_t data[12]; }; struct mesh_friend_msg { uint32_t iv_index; uint32_t flags; uint16_t src; uint16_t dst; uint8_t ttl; uint8_t cnt_in; uint8_t cnt_out; uint8_t last_len; bool done; bool ctl; union { struct mesh_friend_seg_one one[1]; /* Single segment */ struct mesh_friend_seg_12 s12[0]; /* Array of segments */ } u; }; typedef void (*mesh_status_func_t)(void *user_data, bool result); struct mesh_net *mesh_net_new(struct mesh_node *node); void mesh_net_free(void *net); void mesh_net_cleanup(void); void mesh_net_set_iv_index(struct mesh_net *net, uint32_t index, bool update); bool mesh_net_iv_index_update(struct mesh_net *net); bool mesh_net_set_seq_num(struct mesh_net *net, uint32_t number); uint32_t mesh_net_get_seq_num(struct mesh_net *net); uint32_t mesh_net_next_seq_num(struct mesh_net *net); bool mesh_net_set_default_ttl(struct mesh_net *net, uint8_t ttl); uint8_t mesh_net_get_default_ttl(struct mesh_net *net); bool mesh_net_get_frnd_seq(struct mesh_net *net); void mesh_net_set_frnd_seq(struct mesh_net *net, bool seq); uint16_t mesh_net_get_address(struct mesh_net *net); bool mesh_net_register_unicast(struct mesh_net *net, uint16_t unicast, uint8_t num_ele); void net_local_beacon(uint32_t key_id, uint32_t ivi, bool ivu, bool kr); bool mesh_net_set_snb_mode(struct mesh_net *net, bool enable); bool mesh_net_set_mpb_mode(struct mesh_net *net, bool enabla, uint8_t period, bool init); bool mesh_net_set_proxy_mode(struct mesh_net *net, bool enable); bool mesh_net_set_relay_mode(struct mesh_net *net, bool enable, uint8_t cnt, uint8_t interval); bool mesh_net_set_friend_mode(struct mesh_net *net, bool enable); int mesh_net_del_key(struct mesh_net *net, uint16_t net_idx); int mesh_net_add_key(struct mesh_net *net, uint16_t net_idx, const uint8_t *key); int mesh_net_update_key(struct mesh_net *net, uint16_t net_idx, const uint8_t *key); bool mesh_net_set_key(struct mesh_net *net, uint16_t idx, const uint8_t *key, const uint8_t *new_key, uint8_t phase); uint32_t mesh_net_get_iv_index(struct mesh_net *net); void mesh_net_get_snb_state(struct mesh_net *net, uint8_t *flags, uint32_t *iv_index); bool mesh_net_get_key(struct mesh_net *net, bool new_key, uint16_t idx, uint32_t *net_key_id); bool mesh_net_attach(struct mesh_net *net, struct mesh_io *io); struct mesh_io *mesh_net_detach(struct mesh_net *net); struct l_queue *mesh_net_get_app_keys(struct mesh_net *net); void mesh_net_transport_send(struct mesh_net *net, uint32_t net_key_id, uint16_t net_idx, uint32_t iv_index, uint8_t ttl, uint32_t seq, uint16_t src, uint16_t dst, const uint8_t *msg, uint16_t msg_len); bool mesh_net_app_send(struct mesh_net *net, bool frnd_cred, uint16_t src, uint16_t dst, uint8_t key_aid, uint16_t net_idx, uint8_t ttl, uint8_t cnt, uint16_t interval, uint32_t seq, uint32_t iv_index, bool segmented, bool szmic, const void *msg, uint16_t msg_len); void mesh_net_ack_send(struct mesh_net *net, uint32_t net_key_id, uint32_t iv_index, uint8_t ttl, uint32_t seq, uint16_t src, uint16_t dst, bool rly, uint16_t seqZero, uint32_t ack_flags); int mesh_net_get_identity_mode(struct mesh_net *net, uint16_t idx, uint8_t *mode); bool mesh_net_dst_reg(struct mesh_net *net, uint16_t dst); bool mesh_net_dst_unreg(struct mesh_net *net, uint16_t dst); struct mesh_friend *mesh_friend_new(struct mesh_net *net, uint16_t dst, uint8_t ele_cnt, uint8_t frd, uint8_t frw, uint32_t fpt, uint16_t fn_cnt, uint16_t lp_cnt); void mesh_friend_free(void *frnd); bool mesh_friend_clear(struct mesh_net *net, struct mesh_friend *frnd); void mesh_friend_sub_add(struct mesh_net *net, uint16_t lpn, uint8_t ele_cnt, uint8_t grp_cnt, const uint8_t *list); void mesh_friend_sub_del(struct mesh_net *net, uint16_t lpn, uint8_t cnt, const uint8_t *del_list); int mesh_net_key_refresh_phase_set(struct mesh_net *net, uint16_t net_idx, uint8_t transition); int mesh_net_key_refresh_phase_get(struct mesh_net *net, uint16_t net_idx, uint8_t *phase); void mesh_net_send_seg(struct mesh_net *net, uint32_t net_key_id, uint32_t iv_index, uint8_t ttl, uint32_t seq, uint16_t src, uint16_t dst, uint32_t hdr, const void *seg, uint16_t seg_len); struct mesh_net_heartbeat_sub *mesh_net_get_heartbeat_sub(struct mesh_net *net); int mesh_net_set_heartbeat_sub(struct mesh_net *net, uint16_t src, uint16_t dst, uint8_t period_log); struct mesh_net_heartbeat_pub *mesh_net_get_heartbeat_pub(struct mesh_net *net); int mesh_net_set_heartbeat_pub(struct mesh_net *net, uint16_t dst, uint16_t features, uint16_t idx, uint8_t ttl, uint8_t count_log, uint8_t period_log); bool mesh_net_key_list_get(struct mesh_net *net, uint8_t *buf, uint16_t *count); uint16_t mesh_net_get_primary_idx(struct mesh_net *net); uint32_t mesh_net_friend_timeout(struct mesh_net *net, uint16_t addr); struct mesh_io *mesh_net_get_io(struct mesh_net *net); struct mesh_node *mesh_net_node_get(struct mesh_net *net); bool mesh_net_have_key(struct mesh_net *net, uint16_t net_idx); bool mesh_net_is_local_address(struct mesh_net *net, uint16_t src, uint16_t count); void mesh_net_transmit_params_set(struct mesh_net *net, uint8_t count, uint16_t interval); void mesh_net_transmit_params_get(struct mesh_net *net, uint8_t *count, uint16_t *interval); struct mesh_prov *mesh_net_get_prov(struct mesh_net *net); void mesh_net_set_prov(struct mesh_net *net, struct mesh_prov *prov); uint32_t mesh_net_get_instant(struct mesh_net *net); struct l_queue *mesh_net_get_friends(struct mesh_net *net); struct l_queue *mesh_net_get_negotiations(struct mesh_net *net); bool mesh_net_load_rpl(struct mesh_net *net); bluez-5.82/mesh/PaxHeaders/bluetooth-mesh.service.in0000644000000000000000000000005014572354773017551 xustar0020 atime=1743515748 20 ctime=1743591275 bluez-5.82/mesh/bluetooth-mesh.service.in0000644000000000000000000000053114572354773017231 0ustar00rootroot[Unit] Description=Bluetooth mesh service ConditionPathIsDirectory=/sys/class/bluetooth [Service] Type=dbus BusName=org.bluez.mesh ExecStart=@PKGLIBEXECDIR@/bluetooth-meshd NotifyAccess=main LimitNPROC=1 ProtectHome=true ProtectSystem=full Restart=on-failure RestartSec=5s [Install] WantedBy=bluetooth.target Alias=dbus-org.bluez.mesh.service bluez-5.82/mesh/PaxHeaders/util.h0000644000000000000000000000005014711225434013733 xustar0020 atime=1743516868 20 ctime=1743591282 bluez-5.82/mesh/util.h0000644000000000000000000000121514711225434013413 0ustar00rootroot/* SPDX-License-Identifier: LGPL-2.1-or-later */ /* * * BlueZ - Bluetooth protocol stack for Linux * * Copyright (C) 2018 Intel Corporation. All rights reserved. * * */ uint32_t get_timestamp_secs(void); bool str2hex(const char *str, uint16_t in_len, uint8_t *out, uint16_t out_len); size_t hex2str(uint8_t *in, size_t in_len, char *out, size_t out_len); void print_packet(const char *label, const void *data, uint16_t size); int create_dir(const char *dir_name); void del_path(const char *path); void enable_debug(void); #if !HAVE_DECL_BASENAME const char *mesh_basename(const char *path); #else #define mesh_basename basename #endif bluez-5.82/mesh/PaxHeaders/mesh-io-unit.c0000644000000000000000000000005014772767672015315 xustar0020 atime=1743515579 20 ctime=1743591282 bluez-5.82/mesh/mesh-io-unit.c0000644000000000000000000002435014772767672015002 0ustar00rootroot// SPDX-License-Identifier: LGPL-2.1-or-later /* * * BlueZ - Bluetooth protocol stack for Linux * * Copyright (C) 2021 Intel Corporation. All rights reserved. * * */ #ifdef HAVE_CONFIG_H #include #endif #include #include #include #include #include #include #include #include #include #include "mesh/mesh-defs.h" #include "mesh/dbus.h" #include "mesh/mesh-io.h" #include "mesh/mesh-io-api.h" #include "mesh/mesh-io-unit.h" struct mesh_io_private { struct mesh_io *io; struct l_io *sio; void *user_data; char *unique_name; struct l_timeout *tx_timeout; struct l_queue *rx_regs; struct l_queue *tx_pkts; struct sockaddr_un addr; int fd; uint16_t interval; }; struct pvt_rx_reg { mesh_io_recv_func_t cb; void *user_data; uint8_t len; uint8_t filter[0]; }; struct process_data { struct mesh_io_private *pvt; const uint8_t *data; uint8_t len; struct mesh_io_recv_info info; }; struct tx_pkt { struct mesh_io_send_info info; bool delete; uint8_t len; uint8_t pkt[30]; }; struct tx_pattern { const uint8_t *data; uint8_t len; }; static uint32_t get_instant(void) { struct timeval tm; uint32_t instant; gettimeofday(&tm, NULL); instant = tm.tv_sec * 1000; instant += tm.tv_usec / 1000; return instant; } static uint32_t instant_remaining_ms(uint32_t instant) { instant -= get_instant(); return instant; } static void process_rx_callbacks(void *v_reg, void *v_rx) { struct pvt_rx_reg *rx_reg = v_reg; struct process_data *rx = v_rx; if (!memcmp(rx->data, rx_reg->filter, rx_reg->len)) rx_reg->cb(rx_reg->user_data, &rx->info, rx->data, rx->len); } static void process_rx(struct mesh_io_private *pvt, int8_t rssi, uint32_t instant, const uint8_t *addr, const uint8_t *data, uint8_t len) { struct process_data rx = { .pvt = pvt, .data = data, .len = len, .info.instant = instant, .info.addr = addr, .info.chan = 7, .info.rssi = rssi, }; l_queue_foreach(pvt->rx_regs, process_rx_callbacks, &rx); } static bool incoming(struct l_io *sio, void *user_data) { struct mesh_io_private *pvt = user_data; uint32_t instant; uint8_t buf[31]; size_t size; instant = get_instant(); size = recv(pvt->fd, buf, sizeof(buf), MSG_DONTWAIT); if (size > 9 && buf[0]) { process_rx(pvt, -20, instant, NULL, buf + 1, (uint8_t)size); } else if (size == 1 && !buf[0] && pvt->unique_name) { /* Return DBUS unique name */ size = strlen(pvt->unique_name); if (size > sizeof(buf) - 2) return true; buf[0] = 0; memcpy(buf + 1, pvt->unique_name, size + 1); if (send(pvt->fd, buf, size + 2, MSG_DONTWAIT) < 0) l_error("Failed to send(%d)", errno); } return true; } static bool find_by_ad_type(const void *a, const void *b) { const struct tx_pkt *tx = a; uint8_t ad_type = L_PTR_TO_UINT(b); return !ad_type || ad_type == tx->pkt[0]; } static bool find_by_pattern(const void *a, const void *b) { const struct tx_pkt *tx = a; const struct tx_pattern *pattern = b; if (tx->len < pattern->len) return false; return (!memcmp(tx->pkt, pattern->data, pattern->len)); } static void free_socket(struct mesh_io_private *pvt) { l_io_destroy(pvt->sio); close(pvt->fd); unlink(pvt->addr.sun_path); } static void hello_callback(struct l_dbus_message *msg, void *user_data) { struct mesh_io_private *pvt = user_data; pvt->unique_name = l_strdup(l_dbus_message_get_destination(msg)); l_debug("User-Daemon unique name: %s", pvt->unique_name); } static void get_name(struct l_timeout *timeout, void *user_data) { struct mesh_io_private *pvt = user_data; struct l_dbus *dbus = dbus_get_bus(); struct l_dbus_message *msg; l_timeout_remove(timeout); if (!dbus) { l_timeout_create_ms(20, get_name, pvt, NULL); return; } /* Retrieve unique name */ msg = l_dbus_message_new_method_call(dbus, "org.freedesktop.DBus", "/org/freedesktop/DBus", "org.freedesktop.DBus", "GetId"); l_dbus_message_set_arguments(msg, ""); l_dbus_send_with_reply(dbus, msg, hello_callback, pvt, NULL); } static void unit_up(void *user_data) { struct mesh_io_private *pvt = user_data; l_debug("Started io-unit"); if (pvt->io && pvt->io->ready) pvt->io->ready(pvt->user_data, true); l_timeout_create_ms(1, get_name, pvt, NULL); } static bool unit_init(struct mesh_io *io, void *opt, void *user_data) { struct mesh_io_private *pvt; char *sk_path; size_t size; l_debug("Starting Unit test IO"); if (!io || io->pvt) return false; sk_path = (char *) opt; pvt = l_new(struct mesh_io_private, 1); pvt->addr.sun_family = AF_LOCAL; snprintf(pvt->addr.sun_path, sizeof(pvt->addr.sun_path), "%s", sk_path); pvt->fd = socket(PF_LOCAL, SOCK_DGRAM | SOCK_CLOEXEC, 0); if (pvt->fd < 0) goto fail; unlink(pvt->addr.sun_path); size = offsetof(struct sockaddr_un, sun_path) + strlen(pvt->addr.sun_path); if (bind(pvt->fd, (struct sockaddr *) &pvt->addr, size) < 0) goto fail; /* Setup socket handlers */ pvt->sio = l_io_new(pvt->fd); if (!l_io_set_read_handler(pvt->sio, incoming, pvt, NULL)) goto fail; pvt->rx_regs = l_queue_new(); pvt->tx_pkts = l_queue_new(); pvt->io = io; pvt->user_data = user_data; io->pvt = pvt; l_idle_oneshot(unit_up, pvt, NULL); return true; fail: l_error("Failed to bind Unit Test socket"); free_socket(pvt); l_free(pvt); return false; } static bool unit_destroy(struct mesh_io *io) { struct mesh_io_private *pvt = io->pvt; if (!pvt) return true; l_free(pvt->unique_name); l_timeout_remove(pvt->tx_timeout); l_queue_destroy(pvt->rx_regs, l_free); l_queue_destroy(pvt->tx_pkts, l_free); free_socket(pvt); l_free(pvt); io->pvt = NULL; return true; } static bool unit_caps(struct mesh_io *io, struct mesh_io_caps *caps) { struct mesh_io_private *pvt = io->pvt; if (!pvt || !caps) return false; caps->max_num_filters = 255; caps->window_accuracy = 50; return true; } static bool simple_match(const void *a, const void *b) { return a == b; } static void send_pkt(struct mesh_io_private *pvt, struct tx_pkt *tx, uint16_t interval) { if (send(pvt->fd, tx->pkt, tx->len, MSG_DONTWAIT) < 0) l_error("Failed to send(%d)", errno); if (tx->delete) { l_queue_remove_if(pvt->tx_pkts, simple_match, tx); l_free(tx); } } static void tx_to(struct l_timeout *timeout, void *user_data) { struct mesh_io_private *pvt = user_data; struct tx_pkt *tx; uint16_t ms; uint8_t count; if (!pvt) return; tx = l_queue_pop_head(pvt->tx_pkts); if (!tx) { l_timeout_remove(timeout); pvt->tx_timeout = NULL; return; } if (tx->info.type == MESH_IO_TIMING_TYPE_GENERAL) { ms = tx->info.u.gen.interval; count = tx->info.u.gen.cnt; if (count != MESH_IO_TX_COUNT_UNLIMITED) tx->info.u.gen.cnt--; } else { ms = 25; count = 1; } tx->delete = !!(count == 1); send_pkt(pvt, tx, ms); if (count == 1) { /* Recalculate wakeup if we are responding to POLL */ tx = l_queue_peek_head(pvt->tx_pkts); if (tx && tx->info.type == MESH_IO_TIMING_TYPE_POLL_RSP) { ms = instant_remaining_ms(tx->info.u.poll_rsp.instant + tx->info.u.poll_rsp.delay); } } else l_queue_push_tail(pvt->tx_pkts, tx); if (timeout) { pvt->tx_timeout = timeout; l_timeout_modify_ms(timeout, ms); } else pvt->tx_timeout = l_timeout_create_ms(ms, tx_to, pvt, NULL); } static void tx_worker(void *user_data) { struct mesh_io_private *pvt = user_data; struct tx_pkt *tx; uint32_t delay; tx = l_queue_peek_head(pvt->tx_pkts); if (!tx) return; switch (tx->info.type) { case MESH_IO_TIMING_TYPE_GENERAL: if (tx->info.u.gen.min_delay == tx->info.u.gen.max_delay) delay = tx->info.u.gen.min_delay; else { l_getrandom(&delay, sizeof(delay)); delay %= tx->info.u.gen.max_delay - tx->info.u.gen.min_delay; delay += tx->info.u.gen.min_delay; } break; case MESH_IO_TIMING_TYPE_POLL: if (tx->info.u.poll.min_delay == tx->info.u.poll.max_delay) delay = tx->info.u.poll.min_delay; else { l_getrandom(&delay, sizeof(delay)); delay %= tx->info.u.poll.max_delay - tx->info.u.poll.min_delay; delay += tx->info.u.poll.min_delay; } break; case MESH_IO_TIMING_TYPE_POLL_RSP: /* Delay until Instant + Delay */ delay = instant_remaining_ms(tx->info.u.poll_rsp.instant + tx->info.u.poll_rsp.delay); if (delay > 255) delay = 0; break; default: return; } if (!delay) tx_to(pvt->tx_timeout, pvt); else if (pvt->tx_timeout) l_timeout_modify_ms(pvt->tx_timeout, delay); else pvt->tx_timeout = l_timeout_create_ms(delay, tx_to, pvt, NULL); } static bool send_tx(struct mesh_io *io, struct mesh_io_send_info *info, const uint8_t *data, uint16_t len) { struct mesh_io_private *pvt = io->pvt; struct tx_pkt *tx; bool sending = false; if (!info || !data || !len || len > sizeof(tx->pkt)) return false; tx = l_new(struct tx_pkt, 1); memcpy(&tx->info, info, sizeof(tx->info)); memcpy(&tx->pkt, data, len); tx->len = len; if (info->type == MESH_IO_TIMING_TYPE_POLL_RSP) l_queue_push_head(pvt->tx_pkts, tx); else { sending = !l_queue_isempty(pvt->tx_pkts); l_queue_push_tail(pvt->tx_pkts, tx); } if (!sending) { l_timeout_remove(pvt->tx_timeout); pvt->tx_timeout = NULL; l_idle_oneshot(tx_worker, pvt, NULL); } return true; } static bool tx_cancel(struct mesh_io *io, const uint8_t *data, uint8_t len) { struct mesh_io_private *pvt = io->pvt; struct tx_pkt *tx; if (!data) return false; if (len == 1) { do { tx = l_queue_remove_if(pvt->tx_pkts, find_by_ad_type, L_UINT_TO_PTR(data[0])); l_free(tx); } while (tx); } else { struct tx_pattern pattern = { .data = data, .len = len }; do { tx = l_queue_remove_if(pvt->tx_pkts, find_by_pattern, &pattern); l_free(tx); } while (tx); } if (l_queue_isempty(pvt->tx_pkts)) { l_timeout_remove(pvt->tx_timeout); pvt->tx_timeout = NULL; } return true; } static bool recv_register(struct mesh_io *io, const uint8_t *filter, uint8_t len, mesh_io_recv_func_t cb, void *user_data) { return true; } static bool recv_deregister(struct mesh_io *io, const uint8_t *filter, uint8_t len) { return true; } const struct mesh_io_api mesh_io_unit = { .init = unit_init, .destroy = unit_destroy, .caps = unit_caps, .send = send_tx, .reg = recv_register, .dereg = recv_deregister, .cancel = tx_cancel, }; bluez-5.82/mesh/PaxHeaders/prvbeac-server.c0000644000000000000000000000005014772767672015725 xustar0020 atime=1743515579 20 ctime=1743591282 bluez-5.82/mesh/prvbeac-server.c0000644000000000000000000000557314772767672015420 0ustar00rootroot/* * * BlueZ - Bluetooth protocol stack for Linux * * Copyright (C) 2023 Intel Corporation. All rights reserved. * * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * */ #ifdef HAVE_CONFIG_H #include #endif #include #include #include #include "mesh/mesh-defs.h" #include "mesh/node.h" #include "mesh/net.h" #include "mesh/appkey.h" #include "mesh/model.h" #include "mesh/mesh-config.h" #include "mesh/prv-beacon.h" #define NOT_SUPPORTED 0x02 static bool prvbec_srv_pkt(uint16_t src, uint16_t dst, uint16_t app_idx, uint16_t net_idx, const uint8_t *data, uint16_t size, const void *user_data) { struct mesh_node *node = (struct mesh_node *) user_data; const uint8_t *pkt = data; uint32_t opcode; uint8_t msg[5]; uint16_t n; uint8_t period; if (app_idx != APP_IDX_DEV_LOCAL) return false; if (mesh_model_opcode_get(pkt, size, &opcode, &n)) { size -= n; pkt += n; } else return false; l_debug("PRV-BEAC-SRV-opcode 0x%x size %u idx %3.3x", opcode, size, net_idx); n = 0; switch (opcode) { default: return false; case OP_PRIVATE_BEACON_SET: if (size == 1) node_mpb_mode_get(node, &period); else if (size == 2) period = pkt[1]; else return true; if (pkt[0] > 1) return true; node_mpb_mode_set(node, !!pkt[0], period); /* fall through */ case OP_PRIVATE_BEACON_GET: n = mesh_model_opcode_set(OP_PRIVATE_BEACON_STATUS, msg); msg[n++] = node_mpb_mode_get(node, &period); msg[n++] = period; l_debug("Get/Set Private Beacon (%d)", msg[n-2]); break; case OP_PRIVATE_GATT_PROXY_SET: /* fall through */ case OP_PRIVATE_GATT_PROXY_GET: n = mesh_model_opcode_set(OP_PRIVATE_GATT_PROXY_STATUS, msg); msg[n++] = NOT_SUPPORTED; break; case OP_PRIVATE_NODE_ID_SET: /* fall through */ case OP_PRIVATE_NODE_ID_GET: n = mesh_model_opcode_set(OP_PRIVATE_NODE_ID_STATUS, msg); msg[n++] = NOT_SUPPORTED; break; } if (n) mesh_model_send(node, dst, src, APP_IDX_DEV_LOCAL, net_idx, DEFAULT_TTL, false, n, msg); return true; } static void prvbec_srv_unregister(void *user_data) { } static const struct mesh_model_ops ops = { .unregister = prvbec_srv_unregister, .recv = prvbec_srv_pkt, .bind = NULL, .sub = NULL, .pub = NULL }; void prv_beacon_server_init(struct mesh_node *node, uint8_t ele_idx) { l_debug("%2.2x", ele_idx); mesh_model_register(node, ele_idx, PRV_BEACON_SRV_MODEL, &ops, node); } bluez-5.82/mesh/PaxHeaders/friend.c0000644000000000000000000000005014772767672014246 xustar0020 atime=1743515579 20 ctime=1743591282 bluez-5.82/mesh/friend.c0000644000000000000000000003776714772767672013753 0ustar00rootroot// SPDX-License-Identifier: LGPL-2.1-or-later /* * * BlueZ - Bluetooth protocol stack for Linux * * Copyright (C) 2018-2019 Intel Corporation. All rights reserved. * * */ #ifdef HAVE_CONFIG_H #include #endif #include #include #include "mesh/mesh-defs.h" #include "mesh/net-keys.h" #include "mesh/net.h" #include "mesh/model.h" #include "mesh/util.h" #include "mesh/friend.h" #define MAX_FRND_GROUPS 20 #define FRND_RELAY_WINDOW 250 /* 250 ms */ #define FRND_CACHE_SIZE FRND_CACHE_MAX #define FRND_SUB_LIST_SIZE 8 #define RESPONSE_DELAY (100 - 12) /* 100 ms - 12ms hw delay */ #define MIN_RESP_DELAY 10 /* 10 ms */ #define MAX_RESP_DELAY 255 /* 255 ms */ /* Absolute maximum time to wait for LPN to choose us. */ #define RESPONSE_POLL_DELAY 1300 /* 1.300 s */ static uint8_t frnd_relay_window = FRND_RELAY_WINDOW; static uint8_t frnd_cache_size = FRND_CACHE_SIZE; static uint8_t frnd_sublist_size = FRND_SUB_LIST_SIZE; static uint16_t counter; static struct l_queue *retired_lpns; static void response_timeout(struct l_timeout *timeout, void *user_data) { struct mesh_friend *neg = user_data; struct l_queue *negotiations = mesh_net_get_negotiations(neg->net); /* LPN did not choose us */ l_debug("Did not win negotiation for %4.4x", neg->lp_addr); net_key_unref(neg->net_key_cur); net_key_unref(neg->net_key_upd); l_queue_remove(negotiations, neg); l_timeout_remove(timeout); l_free(neg); } static void response_delay(struct l_timeout *timeout, void *user_data) { struct mesh_friend *neg = user_data; uint16_t net_idx = neg->net_idx; uint32_t net_key_id, seq; uint8_t msg[8]; uint16_t n = 0; bool res; l_timeout_remove(timeout); /* Create key Set for this offer */ res = mesh_net_get_key(neg->net, false, net_idx, &net_key_id); if (!res) goto cleanup; neg->net_key_cur = net_key_frnd_add(net_key_id, neg->lp_addr, mesh_net_get_address(neg->net), neg->lp_cnt, counter); if (!neg->net_key_cur) goto cleanup; neg->fn_cnt = counter++; msg[n++] = NET_OP_FRND_OFFER; msg[n++] = frnd_relay_window; msg[n++] = frnd_cache_size; msg[n++] = frnd_sublist_size; msg[n++] = neg->u.negotiate.rssi; l_put_be16(neg->fn_cnt, msg + n); n += 2; seq = mesh_net_next_seq_num(neg->net); print_packet("Tx-NET_OP_FRND_OFFER", msg, n); mesh_net_transport_send(neg->net, net_key_id, 0, mesh_net_get_iv_index(neg->net), 0, seq, 0, neg->lp_addr, msg, n); /* Offer expires in 1.3 seconds, which is the max time for LPN to * receive all offers, 1 second to make decision, and a little extra */ neg->timeout = l_timeout_create_ms(1000 + MAX_RESP_DELAY, response_timeout, neg, NULL); return; cleanup: net_key_unref(neg->net_key_cur); net_key_unref(neg->net_key_upd); l_queue_remove(mesh_net_get_negotiations(neg->net), neg); l_free(neg); } static uint8_t cache_size(uint8_t power) { return 1 << power; } static bool match_by_lpn(const void *a, const void *b) { const struct mesh_friend *neg = a; uint16_t lpn = L_PTR_TO_UINT(b); return neg->lp_addr == lpn; } /* Scaling factors in 1/10 ms */ static const int32_t scaling[] = { 10, 15, 20, 15, }; void friend_request(struct mesh_net *net, uint16_t net_idx, uint16_t src, uint8_t minReq, uint8_t delay, uint32_t timeout, uint16_t prev, uint8_t num_ele, uint16_t cntr, int8_t rssi) { struct l_queue *negotiations = mesh_net_get_negotiations(net); struct mesh_friend *neg; uint8_t rssiScale = (minReq >> 5) & 3; uint8_t winScale = (minReq >> 3) & 3; uint8_t minCache = (minReq >> 0) & 7; int32_t rsp_delay; l_debug("RSSI of Request: %d dbm", rssi); l_debug("Delay: %d ms", delay); l_debug("Poll Timeout of Request: %d ms", timeout * 100); l_debug("Previous Friend: %4.4x", prev); l_debug("Num Elem: %2.2x", num_ele); l_debug("Cache Requested: %d", cache_size(minCache)); l_debug("Cache to offer: %d", frnd_cache_size); /* Determine our own suitability before * deciding to participate in negotiation */ if (minCache == 0 || num_ele == 0) return; if (delay < 0x0A) return; if (timeout < 0x00000A || timeout > 0x34BBFF) return; if (cache_size(minCache) > frnd_cache_size) return; /* TODO: Check RSSI, and then start Negotiation if appropriate */ /* We are participating in this Negotiation */ neg = l_new(struct mesh_friend, 1); l_queue_push_head(negotiations, neg); neg->net = net; neg->lp_addr = src; neg->lp_cnt = cntr; neg->u.negotiate.rssi = rssi; neg->receive_delay = delay; neg->poll_timeout = timeout; neg->old_friend = prev; neg->ele_cnt = num_ele; neg->net_idx = net_idx; /* RSSI (Negative Factor, larger values == less time) * Scaling factor 0-3 == multiplier of 1.0 - 2.5 * Minimum factor of 1. Bit 1 adds additional factor * of 1, bit zero and additional 0.5 */ rsp_delay = -(rssi * scaling[rssiScale]); l_debug("RSSI Factor: %d ms", rsp_delay / 10); /* Relay Window (Positive Factor, larger values == more time) * Scaling factor 0-3 == multiplier of 1.0 - 2.5 * Minimum factor of 1. Bit 1 adds additional factor * of 1, bit zero and additional 0.5 */ rsp_delay += frnd_relay_window * scaling[winScale]; l_debug("Win Size Factor: %d ms", (frnd_relay_window * scaling[winScale]) / 10); /* Normalize to ms */ rsp_delay /= 10; /* Range limits are 10-255 ms */ if (rsp_delay < MIN_RESP_DELAY) rsp_delay = MIN_RESP_DELAY; else if (rsp_delay > MAX_RESP_DELAY) rsp_delay = MAX_RESP_DELAY; l_debug("Total Response Delay: %d ms", rsp_delay); /* Add in 100ms delay before start of "Offer Period" */ rsp_delay += RESPONSE_DELAY; neg->timeout = l_timeout_create_ms(rsp_delay, response_delay, neg, NULL); } void friend_clear_confirm(struct mesh_net *net, uint16_t src, uint16_t lpn, uint16_t lpnCounter) { struct l_queue *negotiations = mesh_net_get_negotiations(net); struct mesh_friend *neg = l_queue_remove_if(negotiations, match_by_lpn, L_UINT_TO_PTR(lpn)); l_debug("Friend Clear confirmed %4.4x (cnt %4.4x)", lpn, lpnCounter); if (!neg) return; l_timeout_remove(neg->timeout); l_queue_remove(negotiations, neg); l_free(neg); } static void friend_poll_timeout(struct l_timeout *timeout, void *user_data) { struct mesh_friend *frnd = user_data; if (mesh_friend_clear(frnd->net, frnd)) l_debug("Friend Poll Timeout %4.4x", frnd->lp_addr); l_timeout_remove(frnd->timeout); frnd->timeout = NULL; /* Friend may be in either Network or Retired list, so try both */ l_queue_remove(retired_lpns, frnd); mesh_friend_free(frnd); } void friend_clear(struct mesh_net *net, uint16_t src, uint16_t lpn, uint16_t lpnCounter, struct mesh_friend *frnd) { struct l_queue *negotiations = mesh_net_get_negotiations(net); uint8_t msg[5] = { NET_OP_FRND_CLEAR_CONFIRM }; bool removed = false; uint16_t lpnDelta; if (frnd) { lpnDelta = lpnCounter - frnd->lp_cnt; /* Ignore old Friend Clear commands */ if (lpnDelta > 0x100) return; /* Move friend from Network list to Retired list */ removed = mesh_friend_clear(net, frnd); if (removed) { struct mesh_friend *neg, *old; neg = l_queue_remove_if(negotiations, match_by_lpn, L_UINT_TO_PTR(lpn)); /* Cancel any negotiations or clears */ if (neg) { l_timeout_remove(neg->timeout); l_free(neg); } /* Find any duplicates */ old = l_queue_find(retired_lpns, match_by_lpn, L_UINT_TO_PTR(lpn)); /* Force time-out of old friendship */ if (old) friend_poll_timeout(old->timeout, old); if (!retired_lpns) retired_lpns = l_queue_new(); /* Retire this LPN (keeps timeout running) */ l_queue_push_tail(retired_lpns, frnd); } } else { frnd = l_queue_find(retired_lpns, match_by_lpn, L_UINT_TO_PTR(lpn)); if (!frnd) return; lpnDelta = lpnCounter - frnd->lp_cnt; /* Ignore old Friend Clear commands */ if (!lpnDelta || (lpnDelta > 0x100)) return; } l_debug("Friend Cleared %4.4x (%4.4x)", lpn, lpnCounter); l_put_be16(lpn, msg + 1); l_put_be16(lpnCounter, msg + 3); mesh_net_transport_send(net, 0, 0, mesh_net_get_iv_index(net), DEFAULT_TTL, 0, 0, src, msg, sizeof(msg)); } static void clear_retry(struct l_timeout *timeout, void *user_data) { struct mesh_friend *neg = user_data; struct l_queue *negotiations = mesh_net_get_negotiations(neg->net); uint8_t msg[5] = { NET_OP_FRND_CLEAR }; uint32_t secs = 1 << neg->receive_delay; l_put_be16(neg->lp_addr, msg + 1); l_put_be16(neg->lp_cnt, msg + 3); mesh_net_transport_send(neg->net, 0, 0, mesh_net_get_iv_index(neg->net), DEFAULT_TTL, 0, 0, neg->old_friend, msg, sizeof(msg)); if (secs && ((secs << 1) < neg->poll_timeout/10)) { neg->receive_delay++; l_debug("Try FRND_CLR again in %d seconds (total timeout %d)", secs, neg->poll_timeout/10); l_timeout_modify(neg->timeout, secs); } else { l_debug("FRND_CLR timed out %d", secs); l_timeout_remove(timeout); l_queue_remove(negotiations, neg); l_free(neg); } } static void friend_delay_rsp(struct l_timeout *timeout, void *user_data) { struct mesh_friend *frnd = user_data; struct mesh_friend_msg *pkt = frnd->pkt; struct mesh_net *net = frnd->net; uint32_t net_seq, iv_index; uint8_t upd[7] = { NET_OP_FRND_UPDATE }; l_timeout_remove(timeout); if (pkt == NULL) goto update; if (pkt->ctl) { /* Make sure we don't change the bit-sense of MD, * once it has been set because that would cause * a "Dirty Nonce" security violation */ if (((pkt->u.one[0].hdr >> OPCODE_HDR_SHIFT) & OPCODE_MASK) == NET_OP_SEG_ACKNOWLEDGE) { bool rly = !!((pkt->u.one[0].hdr >> RELAY_HDR_SHIFT) & true); uint16_t seqZero = pkt->u.one[0].hdr >> SEQ_ZERO_HDR_SHIFT; seqZero &= SEQ_ZERO_MASK; l_debug("Fwd ACK pkt %6.6x-%8.8x", pkt->u.one[0].seq, pkt->iv_index); pkt->u.one[0].sent = true; mesh_net_ack_send(net, frnd->net_key_cur, pkt->iv_index, pkt->ttl, pkt->u.one[0].seq, pkt->src, pkt->dst, rly, seqZero, l_get_be32(pkt->u.one[0].data)); } else { l_debug("Fwd CTL pkt %6.6x-%8.8x", pkt->u.one[0].seq, pkt->iv_index); print_packet("Frnd-CTL", pkt->u.one[0].data, pkt->last_len); pkt->u.one[0].sent = true; mesh_net_transport_send(net, frnd->net_key_cur, 0, pkt->iv_index, pkt->ttl, pkt->u.one[0].seq, pkt->src, pkt->dst, pkt->u.one[0].data, pkt->last_len); } } else { /* If segments after this one, then More Data must be TRUE */ uint8_t len; if (pkt->cnt_out < pkt->cnt_in) len = sizeof(pkt->u.s12[0].data); else len = pkt->last_len; l_debug("Fwd FRND pkt %6.6x", pkt->u.s12[pkt->cnt_out].seq); print_packet("Frnd-Msg", pkt->u.s12[pkt->cnt_out].data, len); pkt->u.s12[pkt->cnt_out].sent = true; mesh_net_send_seg(net, frnd->net_key_cur, pkt->iv_index, pkt->ttl, pkt->u.s12[pkt->cnt_out].seq, pkt->src, pkt->dst, pkt->u.s12[pkt->cnt_out].hdr, pkt->u.s12[pkt->cnt_out].data, len); } return; update: /* No More Data -- send Update message with md = false */ net_seq = mesh_net_get_seq_num(net); l_debug("Fwd FRND UPDATE %6.6x with MD == 0", net_seq); frnd->u.active.last = frnd->u.active.seq; mesh_net_get_snb_state(net, upd + 1, &iv_index); l_put_be32(iv_index, upd + 2); upd[6] = false; /* Queue is Empty */ print_packet("Update", upd, sizeof(upd)); mesh_net_transport_send(net, frnd->net_key_cur, 0, mesh_net_get_iv_index(net), 0, net_seq, 0, frnd->lp_addr, upd, sizeof(upd)); mesh_net_next_seq_num(net); } void friend_poll(struct mesh_net *net, uint16_t src, bool seq, struct mesh_friend *frnd) { struct l_queue *negotiations = mesh_net_get_negotiations(net); struct mesh_friend *neg; struct mesh_friend_msg *pkt; bool md; l_debug("POLL-RXED"); neg = l_queue_find(negotiations, match_by_lpn, L_UINT_TO_PTR(src)); if (neg && !neg->u.negotiate.clearing) { uint8_t msg[5] = { NET_OP_FRND_CLEAR }; l_debug("Won negotiation for %4.4x", neg->lp_addr); /* This call will clean-up and replace if already friends */ frnd = mesh_friend_new(net, src, neg->ele_cnt, neg->receive_delay, neg->frw, neg->poll_timeout, neg->fn_cnt, neg->lp_cnt); frnd->timeout = l_timeout_create_ms( frnd->poll_timeout * 100, friend_poll_timeout, frnd, NULL); l_timeout_remove(neg->timeout); net_key_unref(neg->net_key_cur); net_key_unref(neg->net_key_upd); neg->net_key_upd = neg->net_key_cur = 0; if (neg->old_friend == 0 || neg->old_friend == mesh_net_get_address(net)) { l_queue_remove(negotiations, neg); l_free(neg); } else { neg->u.negotiate.clearing = true; l_put_be16(neg->lp_addr, msg + 1); l_put_be16(neg->lp_cnt, msg + 3); mesh_net_transport_send(net, 0, 0, mesh_net_get_iv_index(net), DEFAULT_TTL, 0, 0, neg->old_friend, msg, sizeof(msg)); /* Reuse receive_delay as a shift counter to * time-out FRIEND_CLEAR */ neg->receive_delay = 1; neg->timeout = l_timeout_create(1, clear_retry, neg, NULL); } } if (!frnd) return; /* Reset Poll Timeout */ l_timeout_modify_ms(frnd->timeout, frnd->poll_timeout * 100); if (!l_queue_length(frnd->pkt_cache)) goto update; if (frnd->u.active.seq != frnd->u.active.last && frnd->u.active.seq != seq) { pkt = l_queue_peek_head(frnd->pkt_cache); if (pkt->cnt_out < pkt->cnt_in) { pkt->cnt_out++; } else { pkt = l_queue_pop_head(frnd->pkt_cache); l_free(pkt); } } pkt = l_queue_peek_head(frnd->pkt_cache); if (!pkt) goto update; frnd->u.active.seq = seq; frnd->u.active.last = !seq; md = !!(l_queue_length(frnd->pkt_cache) > 1); if (pkt->ctl) { /* Make sure we don't change the bit-sense of MD, * once it has been set because that would cause * a "Dirty Nonce" security violation */ if (!(pkt->u.one[0].sent)) pkt->u.one[0].md = md; } else { /* If segments after this one, then More Data must be TRUE */ if (pkt->cnt_out < pkt->cnt_in) md = true; /* Make sure we don't change the bit-sense of MD, once * it has been set because that would cause a * "Dirty Nonce" security violation */ if (!(pkt->u.s12[pkt->cnt_out].sent)) pkt->u.s12[pkt->cnt_out].md = md; } frnd->pkt = pkt; l_timeout_create_ms(frnd->frd, friend_delay_rsp, frnd, NULL); return; update: frnd->pkt = NULL; l_timeout_create_ms(frnd->frd, friend_delay_rsp, frnd, NULL); } void friend_sub_add(struct mesh_net *net, struct mesh_friend *frnd, const uint8_t *pkt, uint8_t len) { uint16_t *new_list; uint32_t net_seq; uint8_t plen = len; uint8_t msg[] = { NET_OP_PROXY_SUB_CONFIRM, 0 }; if (!frnd || MAX_FRND_GROUPS < frnd->u.active.grp_cnt + (len/2)) return; msg[1] = *pkt++; plen--; /* Sanity Check Values, abort if any illegal */ while (plen >= 2) { plen -= 2; if (l_get_be16(pkt + plen) < 0x8000) return; } new_list = l_malloc(frnd->u.active.grp_cnt * sizeof(uint16_t) + len); if (frnd->u.active.grp_list) memcpy(new_list, frnd->u.active.grp_list, frnd->u.active.grp_cnt * sizeof(uint16_t)); while (len >= 2) { new_list[frnd->u.active.grp_cnt++] = l_get_be16(pkt); pkt += 2; len -= 2; } l_free(frnd->u.active.grp_list); frnd->u.active.grp_list = new_list; print_packet("Tx-NET_OP_PROXY_SUB_CONFIRM", msg, sizeof(msg)); net_seq = mesh_net_get_seq_num(net); mesh_net_transport_send(net, frnd->net_key_cur, 0, mesh_net_get_iv_index(net), 0, net_seq, 0, frnd->lp_addr, msg, sizeof(msg)); mesh_net_next_seq_num(net); } void friend_sub_del(struct mesh_net *net, struct mesh_friend *frnd, const uint8_t *pkt, uint8_t len) { uint32_t net_seq; uint8_t msg[] = { NET_OP_PROXY_SUB_CONFIRM, 0 }; int i; if (!frnd) return; msg[1] = *pkt++; len--; while (len >= 2) { uint16_t grp = l_get_be16(pkt); for (i = frnd->u.active.grp_cnt - 1; i >= 0; i--) { if (frnd->u.active.grp_list[i] == grp) { frnd->u.active.grp_cnt--; memcpy(&frnd->u.active.grp_list[i], &frnd->u.active.grp_list[i + 1], (frnd->u.active.grp_cnt - i) * 2); break; } } len -= 2; pkt += 2; } print_packet("Tx-NET_OP_PROXY_SUB_CONFIRM", msg, sizeof(msg)); net_seq = mesh_net_get_seq_num(net); mesh_net_transport_send(net, frnd->net_key_cur, 0, mesh_net_get_iv_index(net), 0, net_seq, 0, frnd->lp_addr, msg, sizeof(msg)); mesh_net_next_seq_num(net); } bluez-5.82/mesh/PaxHeaders/rpl.c0000644000000000000000000000005014772767672013574 xustar0020 atime=1743515579 20 ctime=1743591282 bluez-5.82/mesh/rpl.c0000644000000000000000000001416314772767672013262 0ustar00rootroot// SPDX-License-Identifier: LGPL-2.1-or-later /* * * BlueZ - Bluetooth protocol stack for Linux * * Copyright (C) 2020 Intel Corporation. All rights reserved. * * */ #ifdef HAVE_CONFIG_H #include #endif #define _GNU_SOURCE #include #include #include #include #include #include #include #include #include #include "mesh/mesh-defs.h" #include "mesh/node.h" #include "mesh/net.h" #include "mesh/util.h" #include "mesh/rpl.h" static const char *rpl_dir = "/rpl"; bool rpl_put_entry(struct mesh_node *node, uint16_t src, uint32_t iv_index, uint32_t seq) { const char *node_path; char src_file[PATH_MAX]; char seq_txt[7]; bool result = false; DIR *dir; int fd; if (!IS_UNICAST(src)) return false; node_path = node_get_storage_dir(node); if (strlen(node_path) + strlen(rpl_dir) + 15 >= PATH_MAX) return false; snprintf(src_file, PATH_MAX, "%s%s/%8.8x", node_path, rpl_dir, iv_index); dir = opendir(src_file); if (!dir) { if (mkdir(src_file, 0755) != 0) l_error("Failed to create dir: %s", src_file); } else closedir(dir); snprintf(src_file, PATH_MAX, "%s%s/%8.8x/%4.4x", node_path, rpl_dir, iv_index, src); fd = open(src_file, O_WRONLY | O_CREAT | O_TRUNC, 0600); if (fd >= 0) { snprintf(seq_txt, 7, "%6.6x", seq); if (write(fd, seq_txt, 6) == 6) result = true; close(fd); } if (!result) return false; /* Delete RPL entry from old iv_index (if it exists) */ iv_index--; snprintf(src_file, PATH_MAX, "%s%s/%8.8x/%4.4x", node_path, rpl_dir, iv_index, src); if (remove(src_file) < 0 && errno != ENOENT) l_error("Failed to remove(%d): %s", errno, src_file); return result; } void rpl_del_entry(struct mesh_node *node, uint16_t src) { const char *node_path; char rpl_path[PATH_MAX]; struct dirent *entry; DIR *dir; if (!IS_UNICAST(src)) return; node_path = node_get_storage_dir(node); if (strlen(node_path) + strlen(rpl_dir) + 15 >= PATH_MAX) return; snprintf(rpl_path, PATH_MAX, "%s%s", node_path, rpl_dir); dir = opendir(rpl_path); if (!dir) return; /* Remove all instances of src address */ while ((entry = readdir(dir)) != NULL) { if (entry->d_type == DT_DIR && entry->d_name[0] != '.') { snprintf(rpl_path, PATH_MAX, "%s%s/%s/%4.4x", node_path, rpl_dir, entry->d_name, src); if (remove(rpl_path) < 0) l_error("Failed to remove(%d): %s", errno, rpl_path); } } closedir(dir); } static bool match_src(const void *a, const void *b) { const struct mesh_rpl *rpl = a; uint16_t src = L_PTR_TO_UINT(b); return rpl->src == src; } static void get_entries(const char *iv_path, struct l_queue *rpl_list) { struct mesh_rpl *rpl; struct dirent *entry; DIR *dir; int fd; const char *iv_txt; char src_path[PATH_MAX]; char seq_txt[7]; uint32_t iv_index, seq; uint16_t src; dir = opendir(iv_path); if (!dir) return; iv_txt = mesh_basename(iv_path); if (sscanf(iv_txt, "%08x", &iv_index) != 1) { closedir(dir); return; } memset(seq_txt, 0, sizeof(seq_txt)); while ((entry = readdir(dir)) != NULL) { /* RPL sequences are stored in src files under iv_index */ if (entry->d_type == DT_REG) { if (sscanf(entry->d_name, "%04hx", &src) != 1) continue; snprintf(src_path, PATH_MAX, "%s/%4.4x", iv_path, src); fd = open(src_path, O_RDONLY); if (fd < 0) continue; if (read(fd, seq_txt, 6) == 6 && sscanf(seq_txt, "%06x", &seq) == 1) { rpl = l_queue_find(rpl_list, match_src, L_UINT_TO_PTR(src)); if (rpl) { /* Replace older entries */ if (rpl->iv_index < iv_index) { rpl->iv_index = iv_index; rpl->seq = seq; } } else if (seq <= SEQ_MASK && IS_UNICAST(src)) { rpl = l_new(struct mesh_rpl, 1); rpl->src = src; rpl->iv_index = iv_index; rpl->seq = seq; l_queue_push_head(rpl_list, rpl); } } close(fd); } } closedir(dir); } bool rpl_get_list(struct mesh_node *node, struct l_queue *rpl_list) { const char *node_path; struct dirent *entry; char *rpl_path; size_t len; DIR *dir; if (!rpl_list) return false; node_path = node_get_storage_dir(node); len = strlen(node_path) + strlen(rpl_dir) + 15; if (len > PATH_MAX) return false; rpl_path = l_malloc(len); snprintf(rpl_path, len, "%s%s", node_path, rpl_dir); dir = opendir(rpl_path); if (!dir) { l_error("Failed to read RPL dir: %s", rpl_path); l_free(rpl_path); return false; } while ((entry = readdir(dir)) != NULL) { /* RPL sequences are stored in files under iv_indexs */ if (entry->d_type == DT_DIR && entry->d_name[0] != '.') { snprintf(rpl_path, len, "%s%s/%s", node_path, rpl_dir, entry->d_name); get_entries(rpl_path, rpl_list); } } l_free(rpl_path); closedir(dir); return true; } void rpl_update(struct mesh_node *node, uint32_t cur) { uint32_t old = cur - 1; const char *node_path; struct dirent *entry; char path[PATH_MAX]; DIR *dir; node_path = node_get_storage_dir(node); if (!node_path) return; if (strlen(node_path) + strlen(rpl_dir) + 15 >= PATH_MAX) return; /* Make sure path exists */ snprintf(path, PATH_MAX, "%s%s", node_path, rpl_dir); if (mkdir(path, 0755) != 0 && errno != EEXIST) l_error("Failed to create dir(%d): %s", errno, path); dir = opendir(path); if (!dir) return; /* Cleanup any stale or malformed trees */ while ((entry = readdir(dir)) != NULL) { if (entry->d_type == DT_DIR && entry->d_name[0] != '.') { uint32_t val; bool del = false; if (strlen(entry->d_name) != 8) del = true; else if (sscanf(entry->d_name, "%08x", &val) != 1) del = true; /* Delete all invalid iv_index trees */ if (del || (val != cur && val != old)) { snprintf(path, PATH_MAX, "%s%s/%s", node_path, rpl_dir, entry->d_name); del_path(path); } } } closedir(dir); } bool rpl_init(const char *node_path) { char path[PATH_MAX]; if (strlen(node_path) + strlen(rpl_dir) + 15 >= PATH_MAX) return false; snprintf(path, PATH_MAX, "%s%s", node_path, rpl_dir); if (mkdir(path, 0755) != 0 && errno != EEXIST) l_error("Failed to create dir(%d): %s", errno, path); return true; } bluez-5.82/mesh/PaxHeaders/mesh-io-generic.c0000644000000000000000000000005014772767672015752 xustar0020 atime=1743515579 20 ctime=1743591282 bluez-5.82/mesh/mesh-io-generic.c0000644000000000000000000004553114772767672015443 0ustar00rootroot// SPDX-License-Identifier: LGPL-2.1-or-later /* * * BlueZ - Bluetooth protocol stack for Linux * * Copyright (C) 2018-2019 Intel Corporation. All rights reserved. * * */ #ifdef HAVE_CONFIG_H #include #endif #include #include #include #include #include #include "monitor/bt.h" #include "src/shared/hci.h" #include "src/shared/mgmt.h" #include "lib/bluetooth.h" #include "lib/mgmt.h" #include "mesh/mesh-defs.h" #include "mesh/mesh-mgmt.h" #include "mesh/mesh-io.h" #include "mesh/mesh-io-api.h" #include "mesh/mesh-io-generic.h" struct mesh_io_private { struct mesh_io *io; struct bt_hci *hci; struct l_timeout *tx_timeout; struct l_queue *tx_pkts; struct tx_pkt *tx; uint16_t interval; bool sending; bool active; }; struct process_data { struct mesh_io_private *pvt; const uint8_t *data; uint8_t len; struct mesh_io_recv_info info; }; struct tx_pkt { struct mesh_io_send_info info; bool delete; uint8_t len; uint8_t pkt[30]; }; struct tx_pattern { const uint8_t *data; uint8_t len; }; static uint32_t get_instant(void) { struct timeval tm; uint32_t instant; gettimeofday(&tm, NULL); instant = tm.tv_sec * 1000; instant += tm.tv_usec / 1000; return instant; } static uint32_t instant_remaining_ms(uint32_t instant) { instant -= get_instant(); return instant; } static void process_rx_callbacks(void *v_reg, void *v_rx) { struct mesh_io_reg *rx_reg = v_reg; struct process_data *rx = v_rx; if (!memcmp(rx->data, rx_reg->filter, rx_reg->len)) rx_reg->cb(rx_reg->user_data, &rx->info, rx->data, rx->len); } static void process_rx(struct mesh_io_private *pvt, int8_t rssi, uint32_t instant, const uint8_t *addr, const uint8_t *data, uint8_t len) { struct process_data rx = { .pvt = pvt, .data = data, .len = len, .info.instant = instant, .info.addr = addr, .info.chan = 7, .info.rssi = rssi, }; l_queue_foreach(pvt->io->rx_regs, process_rx_callbacks, &rx); } static void event_adv_report(struct mesh_io *io, const void *buf, uint8_t size) { const struct bt_hci_evt_le_adv_report *evt = buf; const uint8_t *adv; const uint8_t *addr; uint32_t instant; uint8_t adv_len; uint16_t len = 0; int8_t rssi; if (evt->event_type != 0x03) return; instant = get_instant(); adv = evt->data; adv_len = evt->data_len; addr = evt->addr; /* rssi is just beyond last byte of data */ rssi = (int8_t) adv[adv_len]; while (len < adv_len - 1) { uint8_t field_len = adv[0]; /* Check for the end of advertising data */ if (field_len == 0) break; len += field_len + 1; /* Do not continue data parsing if got incorrect length */ if (len > adv_len) break; /* TODO: Create an Instant to use */ process_rx(io->pvt, rssi, instant, addr, adv + 1, adv[0]); adv += field_len + 1; } } static void event_callback(const void *buf, uint8_t size, void *user_data) { uint8_t event = l_get_u8(buf); struct mesh_io *io = user_data; switch (event) { case BT_HCI_EVT_LE_ADV_REPORT: event_adv_report(io, buf + 1, size - 1); break; default: l_debug("Other Meta Evt - %d", event); } } static void local_commands_callback(const void *data, uint8_t size, void *user_data) { const struct bt_hci_rsp_read_local_commands *rsp = data; if (rsp->status) l_error("Failed to read local commands"); } static void local_features_callback(const void *data, uint8_t size, void *user_data) { const struct bt_hci_rsp_read_local_features *rsp = data; if (rsp->status) l_error("Failed to read local features"); } static void hci_generic_callback(const void *data, uint8_t size, void *user_data) { uint8_t status = l_get_u8(data); if (status) l_error("Failed to initialize HCI"); } static void configure_hci(struct mesh_io_private *io) { struct bt_hci_cmd_le_set_scan_parameters cmd; struct bt_hci_cmd_set_event_mask cmd_sem; struct bt_hci_cmd_le_set_event_mask cmd_slem; struct bt_hci_cmd_le_set_random_address cmd_raddr; /* Set scan parameters */ cmd.type = 0x00; /* Passive Scanning. No scanning PDUs shall be sent */ cmd.interval = 0x0030; /* Scan Interval = N * 0.625ms */ cmd.window = 0x0030; /* Scan Window = N * 0.625ms */ cmd.own_addr_type = 0x00; /* Public Device Address */ /* Accept all advertising packets except directed advertising packets * not addressed to this device (default). */ cmd.filter_policy = 0x00; /* Set event mask * * Mask: 0x2000800002008890 * Disconnection Complete * Encryption Change * Read Remote Version Information Complete * Hardware Error * Data Buffer Overflow * Encryption Key Refresh Complete * LE Meta */ cmd_sem.mask[0] = 0x90; cmd_sem.mask[1] = 0x88; cmd_sem.mask[2] = 0x00; cmd_sem.mask[3] = 0x02; cmd_sem.mask[4] = 0x00; cmd_sem.mask[5] = 0x80; cmd_sem.mask[6] = 0x00; cmd_sem.mask[7] = 0x20; /* Set LE event mask * * Mask: 0x000000000000087f * LE Connection Complete * LE Advertising Report * LE Connection Update Complete * LE Read Remote Used Features Complete * LE Long Term Key Request * LE Remote Connection Parameter Request * LE Data Length Change * LE PHY Update Complete */ cmd_slem.mask[0] = 0x7f; cmd_slem.mask[1] = 0x08; cmd_slem.mask[2] = 0x00; cmd_slem.mask[3] = 0x00; cmd_slem.mask[4] = 0x00; cmd_slem.mask[5] = 0x00; cmd_slem.mask[6] = 0x00; cmd_slem.mask[7] = 0x00; /* Set LE random address */ l_getrandom(cmd_raddr.addr, 6); cmd_raddr.addr[5] |= 0xc0; /* TODO: Move to suitable place. Set suitable masks */ /* Reset Command */ bt_hci_send(io->hci, BT_HCI_CMD_RESET, NULL, 0, hci_generic_callback, NULL, NULL); /* Read local supported commands */ bt_hci_send(io->hci, BT_HCI_CMD_READ_LOCAL_COMMANDS, NULL, 0, local_commands_callback, NULL, NULL); /* Read local supported features */ bt_hci_send(io->hci, BT_HCI_CMD_READ_LOCAL_FEATURES, NULL, 0, local_features_callback, NULL, NULL); /* Set event mask */ bt_hci_send(io->hci, BT_HCI_CMD_SET_EVENT_MASK, &cmd_sem, sizeof(cmd_sem), hci_generic_callback, NULL, NULL); /* Set LE event mask */ bt_hci_send(io->hci, BT_HCI_CMD_LE_SET_EVENT_MASK, &cmd_slem, sizeof(cmd_slem), hci_generic_callback, NULL, NULL); /* Set LE random address */ bt_hci_send(io->hci, BT_HCI_CMD_LE_SET_RANDOM_ADDRESS, &cmd_raddr, sizeof(cmd_raddr), hci_generic_callback, NULL, NULL); /* Scan Params */ bt_hci_send(io->hci, BT_HCI_CMD_LE_SET_SCAN_PARAMETERS, &cmd, sizeof(cmd), hci_generic_callback, NULL, NULL); } static void scan_enable_rsp(const void *buf, uint8_t size, void *user_data) { uint8_t status = *((uint8_t *) buf); if (status) l_error("LE Scan enable failed (0x%02x)", status); } static void set_recv_scan_enable(const void *buf, uint8_t size, void *user_data) { struct mesh_io_private *pvt = user_data; struct bt_hci_cmd_le_set_scan_enable cmd; cmd.enable = 0x01; /* Enable scanning */ cmd.filter_dup = 0x00; /* Report duplicates */ bt_hci_send(pvt->hci, BT_HCI_CMD_LE_SET_SCAN_ENABLE, &cmd, sizeof(cmd), scan_enable_rsp, pvt, NULL); } static void scan_disable_rsp(const void *buf, uint8_t size, void *user_data) { struct bt_hci_cmd_le_set_scan_parameters cmd; struct mesh_io_private *pvt = user_data; uint8_t status = *((uint8_t *) buf); if (status) l_error("LE Scan disable failed (0x%02x)", status); cmd.type = pvt->active ? 0x01 : 0x00; /* Passive/Active scanning */ cmd.interval = L_CPU_TO_LE16(0x0010); /* 10 ms */ cmd.window = L_CPU_TO_LE16(0x0010); /* 10 ms */ cmd.own_addr_type = 0x01; /* ADDR_TYPE_RANDOM */ cmd.filter_policy = 0x00; /* Accept all */ bt_hci_send(pvt->hci, BT_HCI_CMD_LE_SET_SCAN_PARAMETERS, &cmd, sizeof(cmd), set_recv_scan_enable, pvt, NULL); } static bool simple_match(const void *a, const void *b) { return a == b; } static bool find_by_ad_type(const void *a, const void *b) { const struct tx_pkt *tx = a; uint8_t ad_type = L_PTR_TO_UINT(b); return !ad_type || ad_type == tx->pkt[0]; } static bool find_by_pattern(const void *a, const void *b) { const struct tx_pkt *tx = a; const struct tx_pattern *pattern = b; if (tx->len < pattern->len) return false; return (!memcmp(tx->pkt, pattern->data, pattern->len)); } static bool find_active(const void *a, const void *b) { const struct mesh_io_reg *rx_reg = a; /* Mesh specific AD types do *not* require active scanning, * so do not turn on Active Scanning on their account. */ if (rx_reg->filter[0] < MESH_AD_TYPE_PROVISION || rx_reg->filter[0] > MESH_AD_TYPE_BEACON) return true; return false; } static void restart_scan(struct mesh_io_private *pvt) { struct bt_hci_cmd_le_set_scan_enable cmd; if (l_queue_isempty(pvt->io->rx_regs)) return; pvt->active = l_queue_find(pvt->io->rx_regs, find_active, NULL); cmd.enable = 0x00; /* Disable scanning */ cmd.filter_dup = 0x00; /* Report duplicates */ bt_hci_send(pvt->hci, BT_HCI_CMD_LE_SET_SCAN_ENABLE, &cmd, sizeof(cmd), scan_disable_rsp, pvt, NULL); } static void hci_init(void *user_data) { struct mesh_io *io = user_data; bool result = true; if (io->pvt->hci) bt_hci_unref(io->pvt->hci); /* Clear controller HCI list to suppress mgmt interface warnings */ mesh_mgmt_clear(); io->pvt->hci = bt_hci_new_user_channel(io->index); if (!io->pvt->hci) { l_error("Failed to start mesh io (hci %u): %s", io->index, strerror(errno)); result = false; } if (result) { configure_hci(io->pvt); bt_hci_register(io->pvt->hci, BT_HCI_EVT_LE_META_EVENT, event_callback, io, NULL); l_debug("Started mesh on hci %u", io->index); restart_scan(io->pvt); } if (io->ready) io->ready(io->user_data, result); } static bool dev_init(struct mesh_io *io, void *opts, void *user_data) { if (!io || io->pvt) return false; io->pvt = l_new(struct mesh_io_private, 1); io->pvt->tx_pkts = l_queue_new(); io->pvt->io = io; l_idle_oneshot(hci_init, io, NULL); return true; } static bool dev_destroy(struct mesh_io *io) { struct mesh_io_private *pvt = io->pvt; if (!pvt) return true; bt_hci_unref(pvt->hci); l_timeout_remove(pvt->tx_timeout); l_queue_remove_if(pvt->tx_pkts, simple_match, pvt->tx); l_queue_destroy(pvt->tx_pkts, l_free); l_free(pvt->tx); l_free(pvt); io->pvt = NULL; return true; } static bool dev_caps(struct mesh_io *io, struct mesh_io_caps *caps) { struct mesh_io_private *pvt = io->pvt; if (!pvt || !caps) return false; caps->max_num_filters = 255; caps->window_accuracy = 50; return true; } static void send_cancel_done(const void *buf, uint8_t size, void *user_data) { struct mesh_io_private *pvt = user_data; struct bt_hci_cmd_le_set_random_address cmd; if (!pvt) return; pvt->sending = false; /* At end of any burst of ADVs, change random address */ l_getrandom(cmd.addr, 6); cmd.addr[5] |= 0xc0; bt_hci_send(pvt->hci, BT_HCI_CMD_LE_SET_RANDOM_ADDRESS, &cmd, sizeof(cmd), NULL, NULL, NULL); } static void send_cancel(struct mesh_io_private *pvt) { struct bt_hci_cmd_le_set_adv_enable cmd; if (!pvt) return; if (!pvt->sending) { send_cancel_done(NULL, 0, pvt); return; } cmd.enable = 0x00; /* Disable advertising */ bt_hci_send(pvt->hci, BT_HCI_CMD_LE_SET_ADV_ENABLE, &cmd, sizeof(cmd), send_cancel_done, pvt, NULL); } static void set_send_adv_enable(const void *buf, uint8_t size, void *user_data) { struct mesh_io_private *pvt = user_data; struct bt_hci_cmd_le_set_adv_enable cmd; if (!pvt) return; pvt->sending = true; cmd.enable = 0x01; /* Enable advertising */ bt_hci_send(pvt->hci, BT_HCI_CMD_LE_SET_ADV_ENABLE, &cmd, sizeof(cmd), NULL, NULL, NULL); } static void set_send_adv_data(const void *buf, uint8_t size, void *user_data) { struct mesh_io_private *pvt = user_data; struct tx_pkt *tx; struct bt_hci_cmd_le_set_adv_data cmd; if (!pvt || !pvt->tx) return; tx = pvt->tx; if (tx->len >= sizeof(cmd.data)) goto done; memset(&cmd, 0, sizeof(cmd)); cmd.len = tx->len + 1; cmd.data[0] = tx->len; memcpy(cmd.data + 1, tx->pkt, tx->len); bt_hci_send(pvt->hci, BT_HCI_CMD_LE_SET_ADV_DATA, &cmd, sizeof(cmd), set_send_adv_enable, pvt, NULL); done: if (tx->delete) { l_queue_remove_if(pvt->tx_pkts, simple_match, tx); l_free(tx); } pvt->tx = NULL; } static void set_send_adv_params(const void *buf, uint8_t size, void *user_data) { struct mesh_io_private *pvt = user_data; struct bt_hci_cmd_le_set_adv_parameters cmd; uint16_t hci_interval; if (!pvt) return; hci_interval = (pvt->interval * 16) / 10; cmd.min_interval = L_CPU_TO_LE16(hci_interval); cmd.max_interval = L_CPU_TO_LE16(hci_interval); cmd.type = 0x03; /* ADV_NONCONN_IND */ cmd.own_addr_type = 0x01; /* ADDR_TYPE_RANDOM */ cmd.direct_addr_type = 0x00; memset(cmd.direct_addr, 0, 6); cmd.channel_map = 0x07; cmd.filter_policy = 0x03; bt_hci_send(pvt->hci, BT_HCI_CMD_LE_SET_ADV_PARAMETERS, &cmd, sizeof(cmd), set_send_adv_data, pvt, NULL); } static void send_pkt(struct mesh_io_private *pvt, struct tx_pkt *tx, uint16_t interval) { struct bt_hci_cmd_le_set_adv_enable cmd; /* Delete superseded packet in favor of new packet */ if (pvt->tx && pvt->tx != tx && pvt->tx->delete) { l_queue_remove_if(pvt->tx_pkts, simple_match, pvt->tx); l_free(pvt->tx); } pvt->tx = tx; pvt->interval = interval; if (!pvt->sending) { set_send_adv_params(NULL, 0, pvt); return; } cmd.enable = 0x00; /* Disable advertising */ bt_hci_send(pvt->hci, BT_HCI_CMD_LE_SET_ADV_ENABLE, &cmd, sizeof(cmd), set_send_adv_params, pvt, NULL); } static void tx_to(struct l_timeout *timeout, void *user_data) { struct mesh_io_private *pvt = user_data; struct tx_pkt *tx; uint16_t ms; uint8_t count; if (!pvt) return; tx = l_queue_pop_head(pvt->tx_pkts); if (!tx) { l_timeout_remove(timeout); pvt->tx_timeout = NULL; send_cancel(pvt); return; } if (tx->info.type == MESH_IO_TIMING_TYPE_GENERAL) { ms = tx->info.u.gen.interval; count = tx->info.u.gen.cnt; if (count != MESH_IO_TX_COUNT_UNLIMITED) tx->info.u.gen.cnt--; } else { ms = 25; count = 1; } tx->delete = !!(count == 1); send_pkt(pvt, tx, ms); if (count == 1) { /* Recalculate wakeup if we are responding to POLL */ tx = l_queue_peek_head(pvt->tx_pkts); if (tx && tx->info.type == MESH_IO_TIMING_TYPE_POLL_RSP) { ms = instant_remaining_ms(tx->info.u.poll_rsp.instant + tx->info.u.poll_rsp.delay); } } else l_queue_push_tail(pvt->tx_pkts, tx); if (timeout) { pvt->tx_timeout = timeout; l_timeout_modify_ms(timeout, ms); } else pvt->tx_timeout = l_timeout_create_ms(ms, tx_to, pvt, NULL); } static void tx_worker(void *user_data) { struct mesh_io_private *pvt = user_data; struct tx_pkt *tx; uint32_t delay; tx = l_queue_peek_head(pvt->tx_pkts); if (!tx) return; switch (tx->info.type) { case MESH_IO_TIMING_TYPE_GENERAL: if (tx->info.u.gen.min_delay == tx->info.u.gen.max_delay) delay = tx->info.u.gen.min_delay; else { l_getrandom(&delay, sizeof(delay)); delay %= tx->info.u.gen.max_delay - tx->info.u.gen.min_delay; delay += tx->info.u.gen.min_delay; } break; case MESH_IO_TIMING_TYPE_POLL: if (tx->info.u.poll.min_delay == tx->info.u.poll.max_delay) delay = tx->info.u.poll.min_delay; else { l_getrandom(&delay, sizeof(delay)); delay %= tx->info.u.poll.max_delay - tx->info.u.poll.min_delay; delay += tx->info.u.poll.min_delay; } break; case MESH_IO_TIMING_TYPE_POLL_RSP: /* Delay until Instant + Delay */ delay = instant_remaining_ms(tx->info.u.poll_rsp.instant + tx->info.u.poll_rsp.delay); if (delay > 255) delay = 0; break; default: return; } if (!delay) tx_to(pvt->tx_timeout, pvt); else if (pvt->tx_timeout) l_timeout_modify_ms(pvt->tx_timeout, delay); else pvt->tx_timeout = l_timeout_create_ms(delay, tx_to, pvt, NULL); } static bool send_tx(struct mesh_io *io, struct mesh_io_send_info *info, const uint8_t *data, uint16_t len) { struct mesh_io_private *pvt = io->pvt; struct tx_pkt *tx; if (!info || !data || !len || len > sizeof(tx->pkt)) return false; tx = l_new(struct tx_pkt, 1); memcpy(&tx->info, info, sizeof(tx->info)); memcpy(&tx->pkt, data, len); tx->len = len; if (info->type == MESH_IO_TIMING_TYPE_POLL_RSP) l_queue_push_head(pvt->tx_pkts, tx); else { /* * If transmitter is idle, send packets at least twice to * guard against in-line cancelation of HCI command chain. */ if (info->type == MESH_IO_TIMING_TYPE_GENERAL && !pvt->tx && l_queue_isempty(pvt->tx_pkts) && tx->info.u.gen.cnt == 1) tx->info.u.gen.cnt++; l_queue_push_tail(pvt->tx_pkts, tx); } /* If not already sending, schedule the tx worker */ if (!pvt->tx) { l_timeout_remove(pvt->tx_timeout); pvt->tx_timeout = NULL; l_idle_oneshot(tx_worker, pvt, NULL); } return true; } static bool tx_cancel(struct mesh_io *io, const uint8_t *data, uint8_t len) { struct mesh_io_private *pvt = io->pvt; struct tx_pkt *tx; if (!data) return false; if (len == 1) { do { tx = l_queue_remove_if(pvt->tx_pkts, find_by_ad_type, L_UINT_TO_PTR(data[0])); l_free(tx); if (tx == pvt->tx) pvt->tx = NULL; } while (tx); } else { struct tx_pattern pattern = { .data = data, .len = len }; do { tx = l_queue_remove_if(pvt->tx_pkts, find_by_pattern, &pattern); l_free(tx); if (tx == pvt->tx) pvt->tx = NULL; } while (tx); } if (l_queue_isempty(pvt->tx_pkts)) { send_cancel(pvt); l_timeout_remove(pvt->tx_timeout); pvt->tx_timeout = NULL; } return true; } static bool recv_register(struct mesh_io *io, const uint8_t *filter, uint8_t len, mesh_io_recv_func_t cb, void *user_data) { struct bt_hci_cmd_le_set_scan_enable cmd; struct mesh_io_private *pvt = io->pvt; bool already_scanning; bool active = false; already_scanning = l_queue_length(io->rx_regs) > 1; /* Look for any AD types requiring Active Scanning */ if (l_queue_find(io->rx_regs, find_active, NULL)) active = true; if (!already_scanning || pvt->active != active) { pvt->active = active; cmd.enable = 0x00; /* Disable scanning */ cmd.filter_dup = 0x00; /* Report duplicates */ bt_hci_send(pvt->hci, BT_HCI_CMD_LE_SET_SCAN_ENABLE, &cmd, sizeof(cmd), scan_disable_rsp, pvt, NULL); } return true; } static bool recv_deregister(struct mesh_io *io, const uint8_t *filter, uint8_t len) { struct bt_hci_cmd_le_set_scan_enable cmd = {0, 0}; struct mesh_io_private *pvt = io->pvt; bool active = false; /* Look for any AD types requiring Active Scanning */ if (l_queue_find(io->rx_regs, find_active, NULL)) active = true; if (l_queue_isempty(io->rx_regs)) { bt_hci_send(pvt->hci, BT_HCI_CMD_LE_SET_SCAN_ENABLE, &cmd, sizeof(cmd), NULL, NULL, NULL); } else if (active != pvt->active) { pvt->active = active; bt_hci_send(pvt->hci, BT_HCI_CMD_LE_SET_SCAN_ENABLE, &cmd, sizeof(cmd), scan_disable_rsp, pvt, NULL); } return true; } const struct mesh_io_api mesh_io_generic = { .init = dev_init, .destroy = dev_destroy, .caps = dev_caps, .send = send_tx, .reg = recv_register, .dereg = recv_deregister, .cancel = tx_cancel, }; bluez-5.82/mesh/PaxHeaders/agent.c0000644000000000000000000000005014772767672014075 xustar0020 atime=1743515579 20 ctime=1743591282 bluez-5.82/mesh/agent.c0000644000000000000000000004064114772767672013563 0ustar00rootroot// SPDX-License-Identifier: LGPL-2.1-or-later /* * * BlueZ - Bluetooth protocol stack for Linux * * Copyright (C) 2018-2019 Intel Corporation. All rights reserved. * * */ #ifdef HAVE_CONFIG_H #include #endif #include #include #include "mesh/mesh.h" #include "mesh/error.h" #include "mesh/dbus.h" #include "mesh/agent.h" typedef enum { MESH_AGENT_REQUEST_BLINK, MESH_AGENT_REQUEST_BEEP, MESH_AGENT_REQUEST_VIBRATE, MESH_AGENT_REQUEST_OUT_NUMERIC, MESH_AGENT_REQUEST_OUT_ALPHA, MESH_AGENT_REQUEST_PUSH, MESH_AGENT_REQUEST_TWIST, MESH_AGENT_REQUEST_IN_NUMERIC, MESH_AGENT_REQUEST_IN_ALPHA, MESH_AGENT_REQUEST_STATIC_OOB, MESH_AGENT_REQUEST_PRIVATE_KEY, MESH_AGENT_REQUEST_PUBLIC_KEY, MESH_AGENT_REQUEST_CAPABILITIES, } agent_request_type_t; struct agent_request { agent_request_type_t type; struct l_dbus_message *msg; void *cb; void *user_data; }; struct mesh_agent { char *path; char *owner; struct mesh_agent_prov_caps caps; struct agent_request *req; }; struct prov_action { const char *action; uint16_t output; uint16_t input; uint8_t size; }; struct oob_info { const char *oob; uint16_t mask; }; static const struct prov_action cap_table[] = { {"blink", 0x0001, 0x0000, 1}, {"beep", 0x0002, 0x0000, 1}, {"vibrate", 0x0004, 0x0000, 1}, {"out-numeric", 0x0008, 0x0000, 8}, {"out-alpha", 0x0010, 0x0000, 8}, {"push", 0x0000, 0x0001, 1}, {"twist", 0x0000, 0x0002, 1}, {"in-numeric", 0x0000, 0x0004, 8}, {"in-alpha", 0x0000, 0x0008, 8} }; static const struct oob_info oob_table[] = { {"other", 0x0001}, {"uri", 0x0002}, {"machine-code-2d", 0x0004}, {"barcode", 0x0008}, {"nfc", 0x0010}, {"number", 0x0020}, {"string", 0x0040}, {"on-box", 0x0800}, {"in-box", 0x1000}, {"on-paper", 0x2000}, {"in-manual", 0x4000}, {"on-device", 0x8000} }; static struct l_queue *agents; static bool simple_match(const void *a, const void *b) { return a == b; } static bool parse_prov_caps(struct mesh_agent_prov_caps *caps, struct l_dbus_message_iter *property) { struct l_dbus_message_iter iter_caps; const char *str; uint32_t i; if (!l_dbus_message_iter_get_variant(property, "as", &iter_caps)) return false; while (l_dbus_message_iter_next_entry(&iter_caps, &str)) { for (i = 0; i < L_ARRAY_SIZE(cap_table); i++) { if (strcmp(str, cap_table[i].action)) continue; caps->output_action |= cap_table[i].output; if (cap_table[i].output && caps->output_size < cap_table[i].size) caps->output_size = cap_table[i].size; caps->input_action |= cap_table[i].input; if (cap_table[i].input && caps->input_size < cap_table[i].size) caps->input_size = cap_table[i].size; break; } if (!strcmp(str, "public-oob")) caps->pub_type = 1; else if (!strcmp(str, "static-oob")) caps->static_type = 1; } return true; } static bool parse_oob_info(struct mesh_agent_prov_caps *caps, struct l_dbus_message_iter *property) { struct l_dbus_message_iter iter_oob; uint32_t i; const char *str; if (!l_dbus_message_iter_get_variant(property, "as", &iter_oob)) return false; while (l_dbus_message_iter_next_entry(&iter_oob, &str)) { for (i = 0; i < L_ARRAY_SIZE(oob_table); i++) { if (strcmp(str, oob_table[i].oob)) continue; caps->oob_info |= oob_table[i].mask; } } return true; } static bool parse_properties(struct mesh_agent *agent, struct l_dbus_message_iter *properties) { const char *key, *uri_string; struct l_dbus_message_iter variant; memset(&agent->caps, 0, sizeof(agent->caps)); while (l_dbus_message_iter_next_entry(properties, &key, &variant)) { if (!strcmp(key, "Capabilities")) { if (!parse_prov_caps(&agent->caps, &variant)) return false; } else if (!strcmp(key, "URI")) { if (!l_dbus_message_iter_get_variant(&variant, "s", &uri_string)) return false; /* TODO: compute hash */ } else if (!strcmp(key, "OutOfBandInfo")) { if (!parse_oob_info(&agent->caps, &variant)) return false; } } return true; } static void agent_free(void *agent_data) { struct mesh_agent *agent = agent_data; int err; mesh_agent_cb_t simple_cb; mesh_agent_key_cb_t key_cb; mesh_agent_number_cb_t number_cb; err = MESH_ERROR_DOES_NOT_EXIST; if (agent->req && agent->req->cb) { struct agent_request *req = agent->req; switch (req->type) { case MESH_AGENT_REQUEST_PUSH: case MESH_AGENT_REQUEST_TWIST: case MESH_AGENT_REQUEST_IN_NUMERIC: number_cb = req->cb; number_cb(req->user_data, err, 0); break; case MESH_AGENT_REQUEST_IN_ALPHA: case MESH_AGENT_REQUEST_STATIC_OOB: case MESH_AGENT_REQUEST_PRIVATE_KEY: case MESH_AGENT_REQUEST_PUBLIC_KEY: key_cb = req->cb; key_cb(req->user_data, err, NULL, 0); break; case MESH_AGENT_REQUEST_BLINK: case MESH_AGENT_REQUEST_BEEP: case MESH_AGENT_REQUEST_VIBRATE: case MESH_AGENT_REQUEST_OUT_NUMERIC: case MESH_AGENT_REQUEST_OUT_ALPHA: case MESH_AGENT_REQUEST_CAPABILITIES: simple_cb = agent->req->cb; simple_cb(req->user_data, err); default: break; } l_dbus_message_unref(req->msg); l_free(req); } l_free(agent->path); l_free(agent->owner); l_free(agent); } void mesh_agent_remove(struct mesh_agent *agent) { if (!agent) return; if (l_queue_remove(agents, agent)) agent_free(agent); } void mesh_agent_cleanup(void) { if (!agents) return; l_queue_destroy(agents, agent_free); agents = NULL; } void mesh_agent_init(void) { if (!agents) agents = l_queue_new(); } struct mesh_agent *mesh_agent_create(const char *path, const char *owner, struct l_dbus_message_iter *properties) { struct mesh_agent *agent; agent = l_new(struct mesh_agent, 1); agent->owner = l_strdup(owner); agent->path = l_strdup(path); if (!parse_properties(agent, properties)) { l_free(agent); return NULL; } l_queue_push_tail(agents, agent); return agent; } struct mesh_agent_prov_caps *mesh_agent_get_caps(struct mesh_agent *agent) { if (!agent || !l_queue_find(agents, simple_match, agent)) return NULL; return &agent->caps; } static struct agent_request *create_request(agent_request_type_t type, void *cb, void *data) { struct agent_request *req; req = l_new(struct agent_request, 1); req->type = type; req->cb = cb; req->user_data = data; return req; } static int get_reply_error(struct l_dbus_message *reply) { const char *name, *desc; if (l_dbus_message_is_error(reply)) { l_dbus_message_get_error(reply, &name, &desc); l_error("Agent failed (%s), %s", name, desc); return MESH_ERROR_FAILED; } return MESH_ERROR_NONE; } static void properties_reply(struct l_dbus_message *reply, void *user_data) { struct mesh_agent *agent = user_data; struct agent_request *req; mesh_agent_cb_t cb; struct l_dbus_message_iter properties; int err; if (!l_queue_find(agents, simple_match, agent) || !agent->req) return; req = agent->req; err = get_reply_error(reply); if (err != MESH_ERROR_NONE) goto done; if (!l_dbus_message_get_arguments(reply, "a{sv}", &properties)) { err = MESH_ERROR_FAILED; goto done; } if (!parse_properties(agent, &properties)) err = MESH_ERROR_FAILED; done: if (req->cb) { cb = req->cb; cb(req->user_data, err); } l_dbus_message_unref(req->msg); l_free(req); agent->req = NULL; } void mesh_agent_refresh(struct mesh_agent *agent, mesh_agent_cb_t cb, void *user_data) { struct l_dbus *dbus = dbus_get_bus(); struct l_dbus_message *msg; struct l_dbus_message_builder *builder; agent->req = create_request(MESH_AGENT_REQUEST_CAPABILITIES, (void *)cb, user_data); msg = l_dbus_message_new_method_call(dbus, agent->owner, agent->path, L_DBUS_INTERFACE_PROPERTIES, "GetAll"); builder = l_dbus_message_builder_new(msg); l_dbus_message_builder_append_basic(builder, 's', MESH_PROVISION_AGENT_INTERFACE); l_dbus_message_builder_finalize(builder); l_dbus_message_builder_destroy(builder); l_dbus_send_with_reply(dbus_get_bus(), msg, properties_reply, agent, NULL); agent->req->msg = l_dbus_message_ref(msg); } static void simple_reply(struct l_dbus_message *reply, void *user_data) { struct mesh_agent *agent = user_data; struct agent_request *req; mesh_agent_cb_t cb; int err; if (!l_queue_find(agents, simple_match, agent) || !agent->req) return; req = agent->req; err = get_reply_error(reply); l_dbus_message_unref(req->msg); if (req->cb) { cb = req->cb; cb(req->user_data, err); } l_free(req); agent->req = NULL; } static void numeric_reply(struct l_dbus_message *reply, void *user_data) { struct mesh_agent *agent = user_data; struct agent_request *req; mesh_agent_number_cb_t cb; uint32_t count; int err; if (!l_queue_find(agents, simple_match, agent) || !agent->req) return; req = agent->req; err = get_reply_error(reply); count = 0; if (err == MESH_ERROR_NONE) { if (!l_dbus_message_get_arguments(reply, "u", &count)) { l_error("Failed to retrieve numeric input"); err = MESH_ERROR_FAILED; } } l_dbus_message_unref(req->msg); if (req->cb) { cb = req->cb; cb(req->user_data, err, count); } l_free(req); agent->req = NULL; } static void key_reply(struct l_dbus_message *reply, void *user_data) { struct mesh_agent *agent = user_data; struct agent_request *req; mesh_agent_key_cb_t cb; struct l_dbus_message_iter iter_array; uint32_t n = 0, expected_len = 0; uint8_t *buf = NULL; int err; if (!l_queue_find(agents, simple_match, agent) || !agent->req) return; req = agent->req; err = get_reply_error(reply); if (err != MESH_ERROR_NONE) goto done; if (!l_dbus_message_get_arguments(reply, "ay", &iter_array)) { l_error("Failed to retrieve key input"); err = MESH_ERROR_FAILED; goto done; } if (!l_dbus_message_iter_get_fixed_array(&iter_array, &buf, &n)) { l_error("Failed to retrieve key input"); err = MESH_ERROR_FAILED; goto done; } if (req->type == MESH_AGENT_REQUEST_PRIVATE_KEY) expected_len = 32; else if (req->type == MESH_AGENT_REQUEST_PUBLIC_KEY) expected_len = 64; else expected_len = 16; if (n != expected_len) { l_error("Bad response length: %u (need %u)", n, expected_len); err = MESH_ERROR_FAILED; n = 0; } done: if (req->cb) { cb = req->cb; cb(req->user_data, err, buf, n); } l_dbus_message_unref(req->msg); l_free(req); agent->req = NULL; } static int output_request(struct mesh_agent *agent, const char *action, agent_request_type_t type, uint32_t cnt, void *cb, void *user_data) { struct l_dbus *dbus = dbus_get_bus(); struct l_dbus_message *msg; struct l_dbus_message_builder *builder; if (!l_queue_find(agents, simple_match, agent)) return MESH_ERROR_DOES_NOT_EXIST; if (agent->req) return MESH_ERROR_BUSY; agent->req = create_request(type, cb, user_data); msg = l_dbus_message_new_method_call(dbus, agent->owner, agent->path, MESH_PROVISION_AGENT_INTERFACE, "DisplayNumeric"); builder = l_dbus_message_builder_new(msg); l_dbus_message_builder_append_basic(builder, 's', action); l_dbus_message_builder_append_basic(builder, 'u', &cnt); l_dbus_message_builder_finalize(builder); l_dbus_message_builder_destroy(builder); l_debug("Send DisplayNumeric request to %s %s", agent->owner, agent->path); l_dbus_send_with_reply(dbus_get_bus(), msg, simple_reply, agent, NULL); agent->req->msg = l_dbus_message_ref(msg); return MESH_ERROR_NONE; } static int prompt_input(struct mesh_agent *agent, const char *action, agent_request_type_t type, bool numeric, void *cb, void *user_data) { struct l_dbus *dbus = dbus_get_bus(); struct l_dbus_message *msg; struct l_dbus_message_builder *builder; const char *method_name; l_dbus_message_func_t reply_cb; if (!l_queue_find(agents, simple_match, agent)) return MESH_ERROR_DOES_NOT_EXIST; if (agent->req) return MESH_ERROR_BUSY; agent->req = create_request(type, cb, user_data); method_name = numeric ? "PromptNumeric" : "PromptStatic"; msg = l_dbus_message_new_method_call(dbus, agent->owner, agent->path, MESH_PROVISION_AGENT_INTERFACE, method_name); builder = l_dbus_message_builder_new(msg); l_dbus_message_builder_append_basic(builder, 's', action); l_dbus_message_builder_finalize(builder); l_dbus_message_builder_destroy(builder); l_debug("Send \"%s\" input request to %s %s", action, agent->owner, agent->path); reply_cb = numeric ? numeric_reply : key_reply; l_dbus_send_with_reply(dbus_get_bus(), msg, reply_cb, agent, NULL); agent->req->msg = l_dbus_message_ref(msg); return MESH_ERROR_NONE; } static int request_key(struct mesh_agent *agent, agent_request_type_t type, void *cb, void *user_data) { struct l_dbus *dbus = dbus_get_bus(); struct l_dbus_message *msg; const char *method_name; if (!l_queue_find(agents, simple_match, agent)) return MESH_ERROR_DOES_NOT_EXIST; if (agent->req) return MESH_ERROR_BUSY; agent->req = create_request(type, cb, user_data); method_name = (type == MESH_AGENT_REQUEST_PRIVATE_KEY) ? "PrivateKey" : "PublicKey"; msg = l_dbus_message_new_method_call(dbus, agent->owner, agent->path, MESH_PROVISION_AGENT_INTERFACE, method_name); l_dbus_message_set_arguments(msg, ""); l_debug("Send key request to %s %s", agent->owner, agent->path); l_dbus_send_with_reply(dbus_get_bus(), msg, key_reply, agent, NULL); agent->req->msg = l_dbus_message_ref(msg); return MESH_ERROR_NONE; } int mesh_agent_display_string(struct mesh_agent *agent, const char *str, mesh_agent_cb_t cb, void *user_data) { struct l_dbus *dbus = dbus_get_bus(); struct l_dbus_message *msg; struct l_dbus_message_builder *builder; if (!l_queue_find(agents, simple_match, agent)) return MESH_ERROR_DOES_NOT_EXIST; if (agent->req) return MESH_ERROR_BUSY; agent->req = create_request(MESH_AGENT_REQUEST_OUT_ALPHA, cb, user_data); msg = l_dbus_message_new_method_call(dbus, agent->owner, agent->path, MESH_PROVISION_AGENT_INTERFACE, "DisplayString"); builder = l_dbus_message_builder_new(msg); l_dbus_message_builder_append_basic(builder, 's', str); l_dbus_message_builder_finalize(builder); l_dbus_message_builder_destroy(builder); l_debug("Send DisplayString request to %s %s", agent->owner, agent->path); l_dbus_send_with_reply(dbus_get_bus(), msg, simple_reply, agent, NULL); agent->req->msg = l_dbus_message_ref(msg); return MESH_ERROR_NONE; } int mesh_agent_display_number(struct mesh_agent *agent, bool initiator, uint8_t action, uint32_t count, mesh_agent_cb_t cb, void *user_data) { const char *str_type; agent_request_type_t type; type = action; if (initiator) type = action + MESH_AGENT_REQUEST_PUSH; if (type >= L_ARRAY_SIZE(cap_table)) return MESH_ERROR_INVALID_ARGS; str_type = cap_table[type].action; return output_request(agent, str_type, type, count, cb, user_data); } int mesh_agent_prompt_number(struct mesh_agent *agent, bool initiator, uint8_t action, mesh_agent_number_cb_t cb, void *user_data) { const char *str_type; agent_request_type_t type; type = action; if (!initiator) type = action + MESH_AGENT_REQUEST_PUSH; if (type >= L_ARRAY_SIZE(cap_table)) return MESH_ERROR_INVALID_ARGS; str_type = cap_table[type].action; return prompt_input(agent, str_type, type, true, cb, user_data); } int mesh_agent_prompt_alpha(struct mesh_agent *agent, bool initiator, mesh_agent_key_cb_t cb, void *user_data) { if (initiator) return prompt_input(agent, cap_table[MESH_AGENT_REQUEST_OUT_ALPHA].action, MESH_AGENT_REQUEST_OUT_ALPHA, false, cb, user_data); else return prompt_input(agent, cap_table[MESH_AGENT_REQUEST_IN_ALPHA].action, MESH_AGENT_REQUEST_IN_ALPHA, false, cb, user_data); } int mesh_agent_request_static(struct mesh_agent *agent, mesh_agent_key_cb_t cb, void *user_data) { return prompt_input(agent, "static-oob", MESH_AGENT_REQUEST_STATIC_OOB, false, cb, user_data); } int mesh_agent_request_private_key(struct mesh_agent *agent, mesh_agent_key_cb_t cb, void *user_data) { return request_key(agent, MESH_AGENT_REQUEST_PRIVATE_KEY, cb, user_data); } int mesh_agent_request_public_key(struct mesh_agent *agent, mesh_agent_key_cb_t cb, void *user_data) { return request_key(agent, MESH_AGENT_REQUEST_PUBLIC_KEY, cb, user_data); } void mesh_agent_cancel(struct mesh_agent *agent) { struct l_dbus *dbus = dbus_get_bus(); struct l_dbus_message *msg; if (!l_queue_find(agents, simple_match, agent)) return; msg = l_dbus_message_new_method_call(dbus, agent->owner, agent->path, MESH_PROVISION_AGENT_INTERFACE, "Cancel"); l_dbus_message_set_arguments(msg, ""); l_dbus_send(dbus, msg); } bluez-5.82/mesh/PaxHeaders/crypto.h0000644000000000000000000000005014447506754014313 xustar0020 atime=1743516868 20 ctime=1743591282 bluez-5.82/mesh/crypto.h0000644000000000000000000001055114447506754013776 0ustar00rootroot/* SPDX-License-Identifier: LGPL-2.1-or-later */ /* * * BlueZ - Bluetooth protocol stack for Linux * * Copyright (C) 2018 Intel Corporation. All rights reserved. * * */ #include #include #include bool mesh_crypto_aes_ccm_encrypt(const uint8_t nonce[13], const uint8_t key[16], const uint8_t *aad, uint16_t aad_len, const void *msg, uint16_t msg_len, void *out_msg, void *out_mic, size_t mic_size); bool mesh_crypto_aes_ccm_decrypt(const uint8_t nonce[13], const uint8_t key[16], const uint8_t *aad, uint16_t aad_len, const void *enc_msg, uint16_t enc_msg_len, void *out_msg, void *out_mic, size_t mic_size); bool mesh_aes_ecb_one(const uint8_t key[16], const uint8_t plaintext[16], uint8_t encrypted[16]); bool mesh_crypto_nkik(const uint8_t network_key[16], uint8_t identity_key[16]); bool mesh_crypto_nkbk(const uint8_t network_key[16], uint8_t beacon_key[16]); bool mesh_crypto_nkpk(const uint8_t network_key[16], uint8_t private_key[16]); bool mesh_crypto_identity(const uint8_t net_key[16], uint16_t addr, uint8_t id[16]); bool mesh_crypto_beacon_cmac(const uint8_t encryption_key[16], const uint8_t network_id[8], uint32_t iv_index, bool kr, bool iu, uint64_t *cmac); bool mesh_crypto_device_key(const uint8_t secret[32], const uint8_t salt[16], uint8_t device_key[16]); bool mesh_crypto_virtual_addr(const uint8_t virtual_label[16], uint16_t *v_addr); bool mesh_crypto_nonce(const uint8_t secret[32], const uint8_t salt[16], uint8_t nonce[13]); bool mesh_crypto_k1(const uint8_t ikm[16], const uint8_t salt[16], const void *info, size_t info_len, uint8_t okm[16]); bool mesh_crypto_k2(const uint8_t n[16], const uint8_t *p, size_t p_len, uint8_t net_id[1], uint8_t enc_key[16], uint8_t priv_key[16]); bool mesh_crypto_k3(const uint8_t n[16], uint8_t out64[8]); bool mesh_crypto_k4(const uint8_t a[16], uint8_t out5[1]); bool mesh_crypto_s1(const void *info, size_t len, uint8_t salt[16]); bool mesh_crypto_prov_prov_salt(const uint8_t conf_salt[16], const uint8_t prov_rand[16], const uint8_t dev_rand[16], uint8_t prov_salt[16]); bool mesh_crypto_prov_conf_key(const uint8_t secret[32], const uint8_t salt[16], uint8_t conf_key[16]); bool mesh_crypto_session_key(const uint8_t secret[32], const uint8_t salt[16], uint8_t session_key[16]); bool mesh_crypto_packet_build(bool ctl, uint8_t ttl, uint32_t seq, uint16_t src, uint16_t dst, uint8_t opcode, bool segmented, uint8_t key_aid, bool szmic, bool relay, uint16_t seqZero, uint8_t segO, uint8_t segN, const uint8_t *payload, uint8_t payload_len, uint8_t *packet, uint8_t *packet_len); bool mesh_crypto_packet_parse(const uint8_t *packet, uint8_t packet_len, bool *ctl, uint8_t *ttl, uint32_t *seq, uint16_t *src, uint16_t *dst, uint32_t *cookie, uint8_t *opcode, bool *segmented, uint8_t *key_aid, bool *szmic, bool *relay, uint16_t *seqZero, uint8_t *segO, uint8_t *segN, const uint8_t **payload, uint8_t *payload_len); bool mesh_crypto_payload_encrypt(uint8_t *aad, const uint8_t *payload, uint8_t *out, uint16_t payload_len, uint16_t src, uint16_t dst, uint8_t key_aid, uint32_t seq_num, uint32_t iv_index, bool aszmic, const uint8_t application_key[16]); bool mesh_crypto_payload_decrypt(uint8_t *aad, uint16_t aad_len, const uint8_t *payload, uint16_t payload_len, bool szmict, uint16_t src, uint16_t dst, uint8_t key_aid, uint32_t seq_num, uint32_t iv_index, uint8_t *out, const uint8_t application_key[16]); bool mesh_crypto_packet_encode(uint8_t *packet, uint8_t packet_len, uint32_t iv_index, const uint8_t network_key[16], const uint8_t privacy_key[16]); bool mesh_crypto_packet_decode(const uint8_t *packet, uint8_t packet_len, bool proxy, uint8_t *out, uint32_t iv_index, const uint8_t network_key[16], const uint8_t privacy_key[16]); bool mesh_crypto_packet_label(uint8_t *packet, uint8_t packet_len, uint16_t iv_index, uint8_t network_id); uint8_t mesh_crypto_compute_fcs(const uint8_t *packet, uint8_t packet_len); bool mesh_crypto_check_fcs(const uint8_t *packet, uint8_t packet_len, uint8_t received_fcs); bool mesh_crypto_aes_cmac(const uint8_t key[16], const uint8_t *msg, size_t msg_len, uint8_t res[16]); bool mesh_crypto_check_avail(void); bluez-5.82/mesh/PaxHeaders/net-keys.c0000644000000000000000000000005014772767672014536 xustar0020 atime=1743515579 20 ctime=1743591281 bluez-5.82/mesh/net-keys.c0000644000000000000000000004117414772767672014226 0ustar00rootroot// SPDX-License-Identifier: LGPL-2.1-or-later /* * * BlueZ - Bluetooth protocol stack for Linux * * Copyright (C) 2019 Intel Corporation. All rights reserved. * * */ #ifdef HAVE_CONFIG_H #include #endif #include #include #include "mesh/mesh-defs.h" #include "mesh/util.h" #include "mesh/crypto.h" #include "mesh/mesh-io.h" #include "mesh/net.h" #include "mesh/net-keys.h" #define BEACON_INTERVAL_MIN 10 #define BEACON_INTERVAL_MAX 600 /* This allows daemon to skip decryption on recently seen beacons */ #define BEACON_CACHE_MAX 10 struct beacon_rx { uint8_t data[28]; uint32_t id; uint32_t ivi; bool kr; bool ivu; }; struct beacon_observe { struct l_timeout *timeout; uint32_t ts; uint16_t period; uint16_t seen; uint16_t expected; bool half_period; }; struct net_key { uint32_t id; struct l_timeout *mpb_to; uint8_t *mpb; uint8_t *snb; struct beacon_observe observe; uint32_t ivi; uint16_t ref_cnt; uint16_t mpb_enables; uint16_t snb_enables; uint8_t mpb_refresh; uint8_t friend_key; uint8_t nid; uint8_t flooding[16]; uint8_t enc_key[16]; uint8_t prv_key[16]; uint8_t snb_key[16]; uint8_t pvt_key[16]; uint8_t net_id[8]; bool kr; bool ivu; }; static struct l_queue *beacons; static struct l_queue *keys; static uint32_t last_flooding_id; /* To avoid re-decrypting same packet for multiple nodes, cache and check */ static uint8_t cache_pkt[29]; static uint8_t cache_plain[29]; static size_t cache_len; static size_t cache_plainlen; static uint32_t cache_id; static uint32_t cache_iv_index; static bool match_flooding(const void *a, const void *b) { const struct net_key *key = a; return (memcmp(key->flooding, b, sizeof(key->flooding)) == 0); } static bool match_id(const void *a, const void *b) { const struct net_key *key = a; uint32_t id = L_PTR_TO_UINT(b); return id == key->id; } static bool match_network(const void *a, const void *b) { const struct net_key *key = a; const uint8_t *net_id = b; return memcmp(key->net_id, net_id, sizeof(key->net_id)) == 0; } /* Key added from Provisioning, NetKey Add or NetKey update */ uint32_t net_key_add(const uint8_t flooding[16]) { struct net_key *key = l_queue_find(keys, match_flooding, flooding); uint8_t p[] = {0}; bool result; if (key) { key->ref_cnt++; return key->id; } if (!keys) keys = l_queue_new(); if (!beacons) beacons = l_queue_new(); key = l_new(struct net_key, 1); memcpy(key->flooding, flooding, 16); key->ref_cnt++; key->mpb_refresh = NET_MPB_REFRESH_DEFAULT; result = mesh_crypto_k2(flooding, p, sizeof(p), &key->nid, key->enc_key, key->prv_key); if (!result) goto fail; result = mesh_crypto_k3(flooding, key->net_id); if (!result) goto fail; result = mesh_crypto_nkbk(flooding, key->snb_key); if (!result) goto fail; result = mesh_crypto_nkpk(flooding, key->pvt_key); if (!result) goto fail; key->id = ++last_flooding_id; l_queue_push_tail(keys, key); return key->id; fail: l_free(key); return 0; } uint32_t net_key_frnd_add(uint32_t flooding_id, uint16_t lpn, uint16_t frnd, uint16_t lp_cnt, uint16_t fn_cnt) { const struct net_key *key = l_queue_find(keys, match_id, L_UINT_TO_PTR(flooding_id)); struct net_key *frnd_key; uint8_t p[9] = {0x01}; bool result; if (!key || key->friend_key) return 0; frnd_key = l_new(struct net_key, 1); l_put_be16(lpn, p + 1); l_put_be16(frnd, p + 3); l_put_be16(lp_cnt, p + 5); l_put_be16(fn_cnt, p + 7); result = mesh_crypto_k2(key->flooding, p, sizeof(p), &frnd_key->nid, frnd_key->enc_key, frnd_key->prv_key); if (!result) { l_free(frnd_key); return 0; } frnd_key->friend_key = true; frnd_key->ref_cnt++; frnd_key->id = ++last_flooding_id; l_queue_push_head(keys, frnd_key); return frnd_key->id; } void net_key_unref(uint32_t id) { struct net_key *key = l_queue_find(keys, match_id, L_UINT_TO_PTR(id)); if (key && key->ref_cnt) { if (--key->ref_cnt == 0) { l_timeout_remove(key->observe.timeout); l_queue_remove(keys, key); l_free(key); } } } bool net_key_confirm(uint32_t id, const uint8_t flooding[16]) { struct net_key *key = l_queue_find(keys, match_id, L_UINT_TO_PTR(id)); if (key) return !memcmp(key->flooding, flooding, sizeof(key->flooding)); return false; } bool net_key_retrieve(uint32_t id, uint8_t *flooding) { struct net_key *key = l_queue_find(keys, match_id, L_UINT_TO_PTR(id)); if (key) { memcpy(flooding, key->flooding, sizeof(key->flooding)); return true; } return false; } static void decrypt_net_pkt(void *a, void *b) { const struct net_key *key = a; bool result; if (cache_id || !key->ref_cnt || (cache_pkt[0] & 0x7f) != key->nid) return; result = mesh_crypto_packet_decode(cache_pkt, cache_len, false, cache_plain, cache_iv_index, key->enc_key, key->prv_key); if (result) { cache_id = key->id; if (cache_plain[1] & 0x80) cache_plainlen = cache_len - 8; else cache_plainlen = cache_len - 4; } } uint32_t net_key_decrypt(uint32_t iv_index, const uint8_t *pkt, size_t len, uint8_t **plain, size_t *plain_len) { /* If we already successfully decrypted this packet, use cached data */ if (cache_id && cache_len == len && !memcmp(pkt, cache_pkt, len)) { /* IV Index must match what was used to decrypt */ if (cache_iv_index != iv_index) return 0; goto done; } cache_id = 0; memcpy(cache_pkt, pkt, len); cache_len = len; cache_iv_index = iv_index; /* Try all network keys known to us */ l_queue_foreach(keys, decrypt_net_pkt, NULL); done: if (cache_id) { *plain = cache_plain; *plain_len = cache_plainlen; } return cache_id; } bool net_key_encrypt(uint32_t id, uint32_t iv_index, uint8_t *pkt, size_t len) { struct net_key *key = l_queue_find(keys, match_id, L_UINT_TO_PTR(id)); bool result; if (!key) return false; result = mesh_crypto_packet_encode(pkt, len, iv_index, key->enc_key, key->prv_key); if (!result) return false; result = mesh_crypto_packet_label(pkt, len, iv_index, key->nid); return result; } uint32_t net_key_network_id(const uint8_t net_id[8]) { struct net_key *key = l_queue_find(keys, match_network, net_id); if (!key) return 0; return key->id; } struct auth_check { const uint8_t *data; uint32_t id; uint32_t ivi; bool ivu; bool kr; }; static void check_auth(void *a, void *b) { struct net_key *key = a; struct auth_check *auth = b; uint8_t out[5]; /* Stop checking if already found */ if (auth->id) return; if (mesh_crypto_aes_ccm_decrypt(auth->data + 1, key->pvt_key, NULL, 0, auth->data + 14, 13, out, NULL, 8)) { auth->id = key->id; auth->ivi = l_get_be32(out + 1); auth->ivu = !!(out[0] & 0x02); auth->kr = !!(out[0] & 0x01); } } static uint32_t private_beacon_check(const void *beacon, uint32_t *ivi, bool *ivu, bool *kr) { struct auth_check auth = { .data = beacon, .id = 0, }; auth.id = 0; l_queue_foreach(keys, check_auth, &auth); if (auth.id) { *ivi = auth.ivi; *ivu = auth.ivu; *kr = auth.kr; } return auth.id; } bool net_key_snb_check(uint32_t id, uint32_t iv_index, bool kr, bool ivu, uint64_t cmac) { struct net_key *key = l_queue_find(keys, match_id, L_UINT_TO_PTR(id)); uint64_t cmac_check; if (!key) return false; /* Any behavioral changes must pass CMAC test */ if (!mesh_crypto_beacon_cmac(key->snb_key, key->net_id, iv_index, kr, ivu, &cmac_check)) { l_error("mesh_crypto_beacon_cmac failed"); return false; } if (cmac != cmac_check) { l_error("cmac compare failed 0x%16" PRIx64 " != 0x%16" PRIx64, cmac, cmac_check); return false; } return true; } static bool mpb_compose(struct net_key *key, uint32_t ivi, bool kr, bool ivu) { uint8_t b_data[5 + 8]; uint8_t random[13]; if (!key) return false; b_data[0] = 0; l_put_be32(ivi, b_data + 1); if (kr) b_data[0] |= KEY_REFRESH; if (ivu) b_data[0] |= IV_INDEX_UPDATE; l_getrandom(random, sizeof(random)); if (!mesh_crypto_aes_ccm_encrypt(random, key->pvt_key, NULL, 0, b_data, 5, b_data, NULL, 8)) return false; key->mpb[0] = MESH_AD_TYPE_BEACON; key->mpb[1] = BEACON_TYPE_MPB; memcpy(key->mpb + 2, random, 13); memcpy(key->mpb + 15, b_data, 13); return true; } static bool snb_compose(struct net_key *key, uint32_t ivi, bool kr, bool ivu) { uint64_t cmac; if (!key) return false; /* Any behavioral changes must pass CMAC test */ if (!mesh_crypto_beacon_cmac(key->snb_key, key->net_id, ivi, kr, ivu, &cmac)) { l_error("mesh_crypto_beacon_cmac failed"); return false; } key->snb[0] = MESH_AD_TYPE_BEACON; key->snb[1] = BEACON_TYPE_SNB; key->snb[2] = 0; if (kr) key->snb[2] |= KEY_REFRESH; if (ivu) key->snb[2] |= IV_INDEX_UPDATE; memcpy(key->snb + 3, key->net_id, 8); l_put_be32(ivi, key->snb + 11); l_put_be64(cmac, key->snb + 15); return true; } static bool match_beacon(const void *a, const void *b) { const struct beacon_rx *cached = a; const uint8_t *incoming = b; if (incoming[0] == BEACON_TYPE_MPB) return !memcmp(cached->data, incoming, 27); if (incoming[0] == BEACON_TYPE_SNB) return !memcmp(cached->data, incoming, 22); return false; } uint32_t net_key_beacon(const uint8_t *data, uint16_t len, uint32_t *ivi, bool *ivu, bool *kr) { struct net_key *key; struct beacon_rx *beacon; uint32_t b_id, b_ivi; bool b_ivu, b_kr; if (data[1] == BEACON_TYPE_SNB && len != 23) return 0; if (data[1] == BEACON_TYPE_MPB && len != 28) return 0; beacon = l_queue_remove_if(beacons, match_beacon, data + 1); if (beacon) goto accept; /* Validate beacon data */ if (data[1] == BEACON_TYPE_SNB) { key = l_queue_find(keys, match_network, data + 3); if (!key) return 0; b_id = key->id; b_ivu = !!(data[2] & 0x02); b_kr = !!(data[2] & 0x01); b_ivi = l_get_be32(data + 11); if (!net_key_snb_check(b_id, b_ivi, b_kr, b_ivu, l_get_be64(data + 15))) return 0; } else if (data[1] == BEACON_TYPE_MPB) { b_id = private_beacon_check(data + 1, &b_ivi, &b_ivu, &b_kr); if (!b_id) return 0; } else return 0; beacon = l_new(struct beacon_rx, 1); memcpy(beacon->data, data + 1, len - 1); beacon->id = b_id; beacon->ivi = b_ivi; beacon->ivu = b_ivu; beacon->kr = b_kr; accept: *ivi = beacon->ivi; *ivu = beacon->ivu; *kr = beacon->kr; l_queue_push_head(beacons, beacon); return beacon->id; } static void send_network_beacon(struct net_key *key) { struct mesh_io_send_info info = { .type = MESH_IO_TIMING_TYPE_GENERAL, .u.gen.interval = 100, .u.gen.cnt = 1, .u.gen.min_delay = DEFAULT_MIN_DELAY, .u.gen.max_delay = DEFAULT_MAX_DELAY }; if (key->mpb_enables) { /* If Interval steps == 0, refresh key every time */ if (!key->mpb_refresh || !key->mpb || !key->mpb[0]) net_key_beacon_refresh(key->id, key->ivi, key->kr, key->ivu, true); mesh_io_send(NULL, &info, key->mpb, 28); } if (key->snb_enables) { if (!key->snb || !key->snb[0]) { net_key_beacon_refresh(key->id, key->ivi, key->kr, key->ivu, true); } mesh_io_send(NULL, &info, key->snb, 23); } } static void beacon_timeout(struct l_timeout *timeout, void *user_data) { struct net_key *key = user_data; uint32_t interval, scale_factor; /* Always send at least one beacon */ send_network_beacon(key); /* Count our own beacons towards the vicinity total */ key->observe.seen++; if (!key->observe.half_period) { l_debug("beacon %d for %d nodes, period %d, obs %d, exp %d", key->id, key->snb_enables + key->mpb_enables, key->observe.period, key->observe.seen, key->observe.expected); interval = (key->observe.period * key->observe.seen) / key->observe.expected; /* Limit Increases and Decreases by 10 seconds Up and * 20 seconds down each step, to avoid going nearly silent * in highly populated environments. */ if (interval - 10 > key->observe.period) interval = key->observe.period + 10; else if (interval + 20 < key->observe.period) interval = key->observe.period - 20; /* Beaconing must be no *slower* than once every 10 minutes, * and no *faster* than once every 10 seconds, per spec. * Observation period is twice beaconing period. */ if (interval < BEACON_INTERVAL_MIN * 2) interval = BEACON_INTERVAL_MIN * 2; else if (interval > BEACON_INTERVAL_MAX * 2) interval = BEACON_INTERVAL_MAX * 2; key->observe.period = interval; key->observe.seen = 0; /* To prevent "over slowing" of the beaconing frequency, * require more significant "over observing" the slower * our own beaconing frequency. */ key->observe.expected = interval / 10; scale_factor = interval / 60; key->observe.expected += scale_factor * 3; } interval = key->observe.period / 2; key->observe.half_period = !key->observe.half_period; if (key->mpb_enables || key->snb_enables) l_timeout_modify(timeout, interval); else { l_timeout_remove(timeout); key->observe.timeout = NULL; } } void net_key_beacon_seen(uint32_t id) { struct net_key *key = l_queue_find(keys, match_id, L_UINT_TO_PTR(id)); if (key) { key->observe.seen++; key->observe.ts = get_timestamp_secs(); } } uint32_t net_key_beacon_last_seen(uint32_t id) { struct net_key *key = l_queue_find(keys, match_id, L_UINT_TO_PTR(id)); if (key) return key->observe.ts; return 0; } bool net_key_beacon_refresh(uint32_t id, uint32_t ivi, bool kr, bool ivu, bool force) { struct net_key *key = l_queue_find(keys, match_id, L_UINT_TO_PTR(id)); bool refresh = force; uint32_t rand_ms; if (!key) return false; if (key->snb_enables && !key->snb) { key->snb = l_new(uint8_t, 23); refresh = true; } if (key->mpb_enables && !key->mpb) { key->mpb = l_new(uint8_t, 28); refresh = true; } if (key->ivi != ivi || key->ivu != ivu || key->kr != kr) refresh = true; if (!refresh) return true; if (key->mpb) { if (!mpb_compose(key, ivi, kr, ivu)) return false; print_packet("Set MPB to", key->mpb, 28); } if (key->snb) { if (!snb_compose(key, ivi, kr, ivu)) return false; print_packet("Set SNB to", key->snb, 23); } l_debug("Set Beacon: IVI: %8.8x, IVU: %d, KR: %d", ivi, ivu, kr); key->ivi = ivi; key->ivu = ivu; key->kr = kr; /* Propagate changes to all local nodes */ net_local_beacon(id, ivi, ivu, kr); /* Send one new SNB soon, after all nodes have seen it */ l_getrandom(&rand_ms, sizeof(rand_ms)); rand_ms %= 1000; key->observe.expected++; if (key->observe.timeout) l_timeout_modify_ms(key->observe.timeout, 500 + rand_ms); else key->observe.timeout = l_timeout_create_ms(500 + rand_ms, beacon_timeout, key, NULL); return true; } static void mpb_timeout(struct l_timeout *timeout, void *user_data) { struct net_key *key = user_data; if (key->mpb_refresh) { l_debug("Refresh in %d seconds", key->mpb_refresh * 10); l_timeout_modify(timeout, key->mpb_refresh * 10); } net_key_beacon_refresh(key->id, key->ivi, key->kr, key->ivu, true); } void net_key_beacon_enable(uint32_t id, bool mpb, uint8_t refresh_count) { struct net_key *key = l_queue_find(keys, match_id, L_UINT_TO_PTR(id)); bool enabled; uint32_t rand_ms; if (!key) return; enabled = !!key->snb_enables || !!key->mpb_enables; if (mpb) { key->mpb_enables++; key->mpb_refresh = refresh_count; l_timeout_remove(key->mpb_to); if (refresh_count) key->mpb_to = l_timeout_create(refresh_count * 10, mpb_timeout, key, NULL); else key->mpb_to = NULL; } else key->snb_enables++; /* If already Enabled, do nothing */ if (enabled) return; /* Randomize first timeout to avoid bursts of beacons */ l_getrandom(&rand_ms, sizeof(rand_ms)); rand_ms %= (BEACON_INTERVAL_MIN * 1000); rand_ms++; /* Enable Periodic Beaconing on this key */ key->observe.period = BEACON_INTERVAL_MIN * 2; key->observe.expected = 2; key->observe.seen = 0; key->observe.half_period = true; l_timeout_remove(key->observe.timeout); key->observe.timeout = l_timeout_create_ms(rand_ms, beacon_timeout, key, NULL); } void net_key_beacon_disable(uint32_t id, bool mpb) { struct net_key *key = l_queue_find(keys, match_id, L_UINT_TO_PTR(id)); if (!key) return; if (mpb) { if (!key->mpb_enables) return; key->mpb_enables--; if (!key->mpb_enables) { l_free(key->mpb); key->mpb = NULL; l_timeout_remove(key->mpb_to); key->mpb_to = NULL; } } else { if (!key->snb_enables) return; key->snb_enables--; if (!key->snb_enables) { l_free(key->snb); key->snb = NULL; } } if (key->snb_enables || key->mpb_enables) return; /* Disable periodic Beaconing on this key */ l_timeout_remove(key->observe.timeout); key->observe.timeout = NULL; } static void free_key(void *data) { struct net_key *key = data; l_timeout_remove(key->mpb_to); l_free(key->snb); l_free(key->mpb); l_free(key); } void net_key_cleanup(void) { l_queue_destroy(keys, free_key); keys = NULL; l_queue_destroy(beacons, l_free); beacons = NULL; } bluez-5.82/mesh/PaxHeaders/keyring.h0000644000000000000000000000005014447506754014443 xustar0020 atime=1743516868 20 ctime=1743591282 bluez-5.82/mesh/keyring.h0000644000000000000000000000300314447506754014120 0ustar00rootroot/* SPDX-License-Identifier: LGPL-2.1-or-later */ /* * * BlueZ - Bluetooth protocol stack for Linux * * Copyright (C) 2019 Intel Corporation. All rights reserved. * * */ struct keyring_net_key { uint16_t net_idx; uint8_t phase; uint8_t old_key[16]; uint8_t new_key[16]; }; struct keyring_app_key { uint16_t app_idx; uint16_t net_idx; uint8_t old_key[16]; uint8_t new_key[16]; }; bool keyring_put_net_key(struct mesh_node *node, uint16_t net_idx, struct keyring_net_key *key); bool keyring_get_net_key(struct mesh_node *node, uint16_t net_idx, struct keyring_net_key *key); bool keyring_del_net_key(struct mesh_node *node, uint16_t net_idx); bool keyring_put_app_key(struct mesh_node *node, uint16_t app_idx, uint16_t net_idx, struct keyring_app_key *key); bool keyring_finalize_app_keys(struct mesh_node *node, uint16_t net_id); bool keyring_get_app_key(struct mesh_node *node, uint16_t app_idx, struct keyring_app_key *key); bool keyring_del_app_key(struct mesh_node *node, uint16_t app_idx); bool keyring_get_remote_dev_key(struct mesh_node *node, uint16_t unicast, uint8_t dev_key[16]); bool keyring_put_remote_dev_key(struct mesh_node *node, uint16_t unicast, uint8_t count, uint8_t dev_key[16]); bool keyring_del_remote_dev_key(struct mesh_node *node, uint16_t unicast, uint8_t count); bool keyring_del_remote_dev_key_all(struct mesh_node *node, uint16_t unicast); bool keyring_build_export_keys_reply(struct mesh_node *node, struct l_dbus_message_builder *builder); bluez-5.82/mesh/PaxHeaders/model.c0000644000000000000000000000005014772767672014077 xustar0020 atime=1743515579 20 ctime=1743591282 bluez-5.82/mesh/model.c0000644000000000000000000013450014772767672013563 0ustar00rootroot// SPDX-License-Identifier: LGPL-2.1-or-later /* * * BlueZ - Bluetooth protocol stack for Linux * * Copyright (C) 2018-2020 Intel Corporation. All rights reserved. * * */ #ifdef HAVE_CONFIG_H #include #endif #include #include #include #include "mesh/mesh-defs.h" #include "mesh/mesh.h" #include "mesh/crypto.h" #include "mesh/node.h" #include "mesh/mesh-config.h" #include "mesh/net.h" #include "mesh/appkey.h" #include "mesh/cfgmod.h" #include "mesh/prov.h" #include "mesh/remprv.h" #include "mesh/prv-beacon.h" #include "mesh/error.h" #include "mesh/dbus.h" #include "mesh/util.h" #include "mesh/model.h" #include "mesh/keyring.h" /* Divide and round to ceiling (up) to calculate segment count */ #define CEILDIV(val, div) (((val) + (div) - 1) / (div)) #define VIRTUAL_BASE 0x10000 struct mesh_model { const struct mesh_model_ops *cbs; void *user_data; struct l_queue *bindings; struct l_queue *subs; struct l_queue *virtuals; struct mesh_model_pub *pub; bool sub_enabled; bool pub_enabled; uint32_t id; }; struct mesh_virtual { uint16_t ref_cnt; uint16_t addr; /* 16-bit virtual address, used in messages */ uint8_t label[16]; /* 128 bit label UUID */ }; /* These struct is used to pass lots of params to l_queue_foreach */ struct mod_forward { struct mesh_virtual *virt; const uint8_t *data; uint16_t src; uint16_t dst; uint16_t unicast; uint16_t app_idx; uint16_t net_idx; uint16_t size; int8_t rssi; bool szmict; bool has_dst; bool done; }; static struct l_queue *mesh_virtuals; static bool is_internal(uint32_t id) { if (id == CONFIG_SRV_MODEL || id == CONFIG_CLI_MODEL) return true; if (id == REM_PROV_SRV_MODEL || id == REM_PROV_CLI_MODEL) return true; if (id == PRV_BEACON_SRV_MODEL || id == PRV_BEACON_CLI_MODEL) return true; return false; } static void unref_virt(void *data) { struct mesh_virtual *virt = data; if (virt->ref_cnt > 0) virt->ref_cnt--; if (virt->ref_cnt) return; l_queue_remove(mesh_virtuals, virt); l_free(virt); } static bool simple_match(const void *a, const void *b) { return a == b; } static bool has_binding(struct l_queue *bindings, uint16_t idx) { const struct l_queue_entry *entry; for (entry = l_queue_get_entries(bindings); entry; entry = entry->next) if (L_PTR_TO_INT(entry->data) == idx) return true; return false; } static bool find_virt_by_label(const void *a, const void *b) { const struct mesh_virtual *virt = a; const uint8_t *label = b; return memcmp(virt->label, label, 16) == 0; } static bool match_model_id(const void *a, const void *b) { const struct mesh_model *mod = a; uint32_t id = L_PTR_TO_UINT(b); return (mod->id == id); } static int compare_model_id(const void *a, const void *b, void *user_data) { const struct mesh_model *mod_a = a; const struct mesh_model *mod_b = b; if (mod_a->id < mod_b->id) return -1; if (mod_a->id > mod_b->id) return 1; return 0; } static struct mesh_model *get_model(struct mesh_node *node, uint8_t ele_idx, uint32_t id) { struct l_queue *mods; struct mesh_model *mod; mods = node_get_element_models(node, ele_idx); if (!mods) return NULL; mod = l_queue_find(mods, match_model_id, L_UINT_TO_PTR(id)); if (!mod) l_debug("Model not found"); return mod; } static uint32_t pub_period_to_ms(uint8_t pub_period) { int step_res, num_steps; step_res = pub_period >> 6; num_steps = pub_period & 0x3f; switch (step_res) { default: return num_steps * 100; case 2: num_steps *= 10; /* Fall Through */ case 1: return num_steps * 1000; case 3: return num_steps * 10 * 60 * 1000; } } static struct l_dbus_message *create_config_update_msg(struct mesh_node *node, uint8_t ele_idx, uint32_t id, struct l_dbus_message_builder **builder) { struct l_dbus *dbus = dbus_get_bus(); struct l_dbus_message *msg; const char *owner; const char *path; uint16_t model_id; owner = node_get_owner(node); path = node_get_element_path(node, ele_idx); if (!path || !owner) return NULL; l_debug("Send \"UpdateModelConfiguration\""); msg = l_dbus_message_new_method_call(dbus, owner, path, MESH_ELEMENT_INTERFACE, "UpdateModelConfiguration"); *builder = l_dbus_message_builder_new(msg); model_id = (uint16_t) MODEL_ID(id); l_dbus_message_builder_append_basic(*builder, 'q', &model_id); l_dbus_message_builder_enter_array(*builder, "{sv}"); if (IS_VENDOR(id)) { uint16_t vendor_id = (uint16_t) VENDOR_ID(id); dbus_append_dict_entry_basic(*builder, "Vendor", "q", &vendor_id); } return msg; } static void config_update_model_pub_period(struct mesh_node *node, uint8_t ele_idx, uint32_t model_id, uint32_t period) { struct l_dbus *dbus = dbus_get_bus(); struct l_dbus_message *msg; struct l_dbus_message_builder *builder; msg = create_config_update_msg(node, ele_idx, model_id, &builder); if (!msg) return; dbus_append_dict_entry_basic(builder, "PublicationPeriod", "u", &period); l_dbus_message_builder_leave_array(builder); l_dbus_message_builder_finalize(builder); l_dbus_message_builder_destroy(builder); l_dbus_send(dbus, msg); } static void append_dict_uint16_array(struct l_dbus_message_builder *builder, struct l_queue *q, const char *key) { const struct l_queue_entry *entry; l_dbus_message_builder_enter_dict(builder, "sv"); l_dbus_message_builder_append_basic(builder, 's', key); l_dbus_message_builder_enter_variant(builder, "aq"); l_dbus_message_builder_enter_array(builder, "q"); for (entry = l_queue_get_entries(q); entry; entry = entry->next) { uint16_t value = (uint16_t) L_PTR_TO_UINT(entry->data); l_dbus_message_builder_append_basic(builder,'q', &value); } l_dbus_message_builder_leave_array(builder); l_dbus_message_builder_leave_variant(builder); l_dbus_message_builder_leave_dict(builder); } static void cfg_update_mod_bindings(struct mesh_node *node, uint16_t ele_idx, struct mesh_model *mod) { struct l_dbus *dbus = dbus_get_bus(); struct l_dbus_message *msg; struct l_dbus_message_builder *builder; msg = create_config_update_msg(node, ele_idx, mod->id, &builder); if (!msg) return; append_dict_uint16_array(builder, mod->bindings, "Bindings"); l_dbus_message_builder_leave_array(builder); l_dbus_message_builder_finalize(builder); l_dbus_message_builder_destroy(builder); l_dbus_send(dbus, msg); } static void append_dict_subs_array(struct l_dbus_message_builder *builder, struct l_queue *subs, struct l_queue *virts, const char *key) { const struct l_queue_entry *entry; l_dbus_message_builder_enter_dict(builder, "sv"); l_dbus_message_builder_append_basic(builder, 's', key); l_dbus_message_builder_enter_variant(builder, "av"); l_dbus_message_builder_enter_array(builder, "v"); if (l_queue_isempty(subs)) goto virts; for (entry = l_queue_get_entries(subs); entry; entry = entry->next) { uint16_t grp = L_PTR_TO_UINT(entry->data); l_dbus_message_builder_enter_variant(builder, "q"); l_dbus_message_builder_append_basic(builder, 'q', &grp); l_dbus_message_builder_leave_variant(builder); } virts: if (l_queue_isempty(virts)) goto done; for (entry = l_queue_get_entries(virts); entry; entry = entry->next) { const struct mesh_virtual *virt = entry->data; l_dbus_message_builder_enter_variant(builder, "ay"); dbus_append_byte_array(builder, virt->label, sizeof(virt->label)); l_dbus_message_builder_leave_variant(builder); } done: l_dbus_message_builder_leave_array(builder); l_dbus_message_builder_leave_variant(builder); l_dbus_message_builder_leave_dict(builder); } static void cfg_update_model_subs(struct mesh_node *node, uint16_t ele_idx, struct mesh_model *mod) { struct l_dbus *dbus = dbus_get_bus(); struct l_dbus_message *msg; struct l_dbus_message_builder *builder; msg = create_config_update_msg(node, ele_idx, mod->id, &builder); if (!msg) return; append_dict_subs_array(builder, mod->subs, mod->virtuals, "Subscriptions"); l_dbus_message_builder_leave_array(builder); l_dbus_message_builder_finalize(builder); l_dbus_message_builder_destroy(builder); l_dbus_send(dbus, msg); } static void forward_model(void *a, void *b) { struct mesh_model *mod = a; struct mod_forward *fwd = b; struct mesh_virtual *virt; uint16_t dst; bool result; if (fwd->app_idx != APP_IDX_DEV_LOCAL && fwd->app_idx != APP_IDX_DEV_REMOTE && !has_binding(mod->bindings, fwd->app_idx)) return; dst = fwd->dst; if (dst == fwd->unicast || IS_FIXED_GROUP_ADDRESS(dst)) { fwd->has_dst = true; } else if (fwd->virt) { virt = l_queue_find(mod->virtuals, simple_match, fwd->virt); if (virt) { fwd->has_dst = true; dst = virt->addr; } } else { if (l_queue_find(mod->subs, simple_match, L_UINT_TO_PTR(dst))) fwd->has_dst = true; } if (!fwd->has_dst) return; /* Return, if this is not a internal model */ if (!mod->cbs) return; result = false; if (mod->cbs->recv) result = mod->cbs->recv(fwd->src, dst, fwd->app_idx, fwd->net_idx, fwd->data, fwd->size, mod->user_data); if (dst == fwd->unicast && result) fwd->done = true; } static int app_packet_decrypt(struct mesh_net *net, const uint8_t *data, uint16_t size, bool szmict, uint16_t src, uint16_t dst, uint8_t *virt, uint16_t virt_size, uint8_t key_aid, uint32_t seq, uint32_t iv_idx, uint8_t *out) { struct l_queue *app_keys = mesh_net_get_app_keys(net); const struct l_queue_entry *entry; if (!app_keys) return -1; for (entry = l_queue_get_entries(app_keys); entry; entry = entry->next) { const uint8_t *old_key = NULL, *new_key = NULL; uint8_t old_key_aid, new_key_aid; int app_idx; bool decrypted; app_idx = appkey_get_key_idx(entry->data, &old_key, &old_key_aid, &new_key, &new_key_aid); if (app_idx < 0) continue; if (old_key && old_key_aid == key_aid) { decrypted = mesh_crypto_payload_decrypt(virt, virt_size, data, size, szmict, src, dst, key_aid, seq, iv_idx, out, old_key); if (decrypted) { print_packet("Used App Key", old_key, 16); return app_idx; } print_packet("Failed App Key", old_key, 16); } if (new_key && new_key_aid == key_aid) { decrypted = mesh_crypto_payload_decrypt(virt, virt_size, data, size, szmict, src, dst, key_aid, seq, iv_idx, out, new_key); if (decrypted) { print_packet("Used App Key", new_key, 16); return app_idx; } print_packet("Failed App Key", new_key, 16); } } return -1; } static int dev_packet_decrypt(struct mesh_node *node, const uint8_t *data, uint16_t size, bool szmict, uint16_t src, uint16_t dst, uint8_t key_aid, uint32_t seq, uint32_t iv_idx, uint8_t *out) { uint8_t dev_key[16]; const uint8_t *key; key = node_get_device_key(node); if (!key) return -1; if (mesh_crypto_payload_decrypt(NULL, 0, data, size, szmict, src, dst, key_aid, seq, iv_idx, out, key)) return APP_IDX_DEV_LOCAL; key = dev_key; if (keyring_get_remote_dev_key(node, src, dev_key)) { if (mesh_crypto_payload_decrypt(NULL, 0, data, size, szmict, src, dst, key_aid, seq, iv_idx, out, key)) return APP_IDX_DEV_REMOTE; } /* See if there is a local Device Key Candidate as last resort */ if (!node_get_device_key_candidate(node, dev_key)) return -1; if (mesh_crypto_payload_decrypt(NULL, 0, data, size, szmict, src, dst, key_aid, seq, iv_idx, out, key)) { /* If candidate dev_key worked, it is considered finalized */ node_finalize_candidate(node); return APP_IDX_DEV_LOCAL; } return -1; } static int virt_packet_decrypt(struct mesh_net *net, const uint8_t *data, uint16_t size, bool szmict, uint16_t src, uint16_t dst, uint8_t key_aid, uint32_t seq, uint32_t iv_idx, uint8_t *out, struct mesh_virtual **decrypt_virt) { const struct l_queue_entry *v; for (v = l_queue_get_entries(mesh_virtuals); v; v = v->next) { struct mesh_virtual *virt = v->data; int decrypt_idx; if (virt->addr != dst) continue; decrypt_idx = app_packet_decrypt(net, data, size, szmict, src, dst, virt->label, 16, key_aid, seq, iv_idx, out); if (decrypt_idx >= 0) { *decrypt_virt = virt; return decrypt_idx; } } return -1; } static bool msg_send(struct mesh_node *node, bool cred, uint16_t src, uint16_t dst, uint16_t app_idx, uint16_t net_idx, uint8_t *label, uint8_t ttl, uint8_t cnt, uint16_t interval, bool segmented, const void *msg, uint16_t msg_len) { uint8_t dev_key[16]; uint32_t iv_index, seq_num; const uint8_t *key; uint8_t *out; uint8_t key_aid = APP_AID_DEV; bool szmic = false; bool ret = false; uint16_t out_len = msg_len + sizeof(uint32_t); struct mesh_net *net = node_get_net(node); /* Use large MIC if it doesn't affect segmentation */ if (msg_len > 11 && msg_len <= 376) { if (CEILDIV(out_len, 12) == CEILDIV(out_len + 4, 12)) { szmic = true; out_len = msg_len + sizeof(uint64_t); } } if (app_idx == APP_IDX_DEV_LOCAL) { key = node_get_device_key(node); if (!key) return false; } else if (app_idx == APP_IDX_DEV_REMOTE) { if (!keyring_get_remote_dev_key(node, dst, dev_key)) return false; key = dev_key; } else { key = appkey_get_key(node_get_net(node), app_idx, &key_aid); if (!key) { l_debug("no app key for (%x)", app_idx); return false; } } out = l_malloc(out_len); iv_index = mesh_net_get_iv_index(net); seq_num = mesh_net_next_seq_num(net); if (!mesh_crypto_payload_encrypt(label, msg, out, msg_len, src, dst, key_aid, seq_num, iv_index, szmic, key)) { l_error("Failed to Encrypt Payload"); goto done; } ret = mesh_net_app_send(net, cred, src, dst, key_aid, net_idx, ttl, cnt, interval, seq_num, iv_index, segmented, szmic, out, out_len); done: l_free(out); return ret; } static void remove_pub(struct mesh_node *node, uint16_t ele_idx, struct mesh_model *mod) { if (mod->pub) { if (mod->pub->virt) unref_virt(mod->pub->virt); l_free(mod->pub); mod->pub = NULL; } if (!mod->cbs) /* External models */ config_update_model_pub_period(node, ele_idx, mod->id, 0); else if (mod->cbs && mod->cbs->pub) /* Internal models */ mod->cbs->pub(NULL); } static void model_unbind_idx(struct mesh_node *node, uint16_t ele_idx, struct mesh_model *mod, uint16_t idx) { l_queue_remove(mod->bindings, L_UINT_TO_PTR(idx)); if (!mod->cbs) /* External model */ cfg_update_mod_bindings(node, ele_idx, mod); else if (mod->cbs->bind) /* Internal model */ mod->cbs->bind(idx, ACTION_DELETE); /* Remove model publication if the publication key is unbound */ if (mod->pub && idx == mod->pub->idx) remove_pub(node, ele_idx, mod); } static void model_bind_idx(struct mesh_node *node, uint16_t ele_idx, struct mesh_model *mod, uint16_t idx) { if (!mod->bindings) mod->bindings = l_queue_new(); l_queue_push_tail(mod->bindings, L_UINT_TO_PTR(idx)); l_debug("Bind key %4.4x to model %8.8x", idx, mod->id); if (!mod->cbs) /* External model */ cfg_update_mod_bindings(node, ele_idx, mod); else if (mod->cbs->bind) /* Internal model */ mod->cbs->bind(idx, ACTION_ADD); } static int update_binding(struct mesh_node *node, uint16_t addr, uint32_t id, uint16_t app_idx, bool unbind) { struct mesh_model *mod; bool vendor; int ele_idx = node_get_element_idx(node, addr); if (ele_idx < 0) return MESH_STATUS_INVALID_ADDRESS; mod = get_model(node, (uint8_t) ele_idx, id); if (!mod) { return MESH_STATUS_INVALID_MODEL; } if (id == CONFIG_SRV_MODEL || id == CONFIG_CLI_MODEL) return MESH_STATUS_INVALID_MODEL; if (id == PRV_BEACON_SRV_MODEL || id == PRV_BEACON_CLI_MODEL) return MESH_STATUS_INVALID_MODEL; if (!appkey_have_key(node_get_net(node), app_idx)) return MESH_STATUS_INVALID_APPKEY; /* * If deleting a binding that is not present, return success. * If adding a binding that already exists, return success. */ if (unbind ^ has_binding(mod->bindings, app_idx)) return MESH_STATUS_SUCCESS; vendor = IS_VENDOR(id); id = vendor ? id : MODEL_ID(id); if (unbind) { model_unbind_idx(node, ele_idx, mod, app_idx); if (!mesh_config_model_binding_del(node_config_get(node), addr, id, vendor, app_idx)) return MESH_STATUS_STORAGE_FAIL; l_debug("Unbind key %4.4x to model %8.8x", app_idx, mod->id); return MESH_STATUS_SUCCESS; } if (l_queue_length(mod->bindings) >= MAX_MODEL_BINDINGS) return MESH_STATUS_INSUFF_RESOURCES; if (!mesh_config_model_binding_add(node_config_get(node), addr, id, vendor, app_idx)) return MESH_STATUS_STORAGE_FAIL; model_bind_idx(node, ele_idx, mod, app_idx); return MESH_STATUS_SUCCESS; } static struct mesh_virtual *add_virtual(const uint8_t *v) { struct mesh_virtual *virt = l_queue_find(mesh_virtuals, find_virt_by_label, v); if (virt) { virt->ref_cnt++; return virt; } virt = l_new(struct mesh_virtual, 1); if (!mesh_crypto_virtual_addr(v, &virt->addr)) { l_free(virt); return NULL; } memcpy(virt->label, v, 16); virt->ref_cnt = 1; l_queue_push_head(mesh_virtuals, virt); return virt; } static int set_pub(struct mesh_model *mod, uint16_t pub_addr, uint16_t idx, bool cred_flag, uint8_t ttl, uint8_t period, uint8_t cnt, uint16_t interval) { if (!mod->pub) mod->pub = l_new(struct mesh_model_pub, 1); mod->pub->addr = pub_addr; mod->pub->credential = cred_flag; mod->pub->idx = idx; mod->pub->ttl = ttl; mod->pub->period = period; mod->pub->rtx.cnt = cnt; mod->pub->rtx.interval = interval; return MESH_STATUS_SUCCESS; } static int set_virt_pub(struct mesh_model *mod, const uint8_t *label, uint16_t idx, bool cred_flag, uint8_t ttl, uint8_t period, uint8_t cnt, uint16_t interval) { struct mesh_virtual *virt = NULL; virt = add_virtual(label); if (!virt) return MESH_STATUS_STORAGE_FAIL; if (!mod->pub) mod->pub = l_new(struct mesh_model_pub, 1); mod->pub->virt = virt; return set_pub(mod, virt->addr, idx, cred_flag, ttl, period, cnt, interval); } static int add_virt_sub(struct mesh_net *net, struct mesh_model *mod, const uint8_t *label, uint16_t *addr) { struct mesh_virtual *virt = l_queue_find(mod->virtuals, find_virt_by_label, label); if (!virt) { virt = add_virtual(label); if (!virt) return MESH_STATUS_INSUFF_RESOURCES; l_queue_push_head(mod->virtuals, virt); mesh_net_dst_reg(net, virt->addr); l_debug("Added virtual sub addr %4.4x", virt->addr); } if (addr) *addr = virt->addr; return MESH_STATUS_SUCCESS; } static int add_sub(struct mesh_net *net, struct mesh_model *mod, uint16_t addr) { if (!mod->subs) mod->subs = l_queue_new(); if (l_queue_find(mod->subs, simple_match, L_UINT_TO_PTR(addr))) return MESH_STATUS_SUCCESS; if ((l_queue_length(mod->subs) + l_queue_length(mod->virtuals)) >= MAX_MODEL_SUBS) return MESH_STATUS_INSUFF_RESOURCES; l_queue_push_tail(mod->subs, L_UINT_TO_PTR(addr)); mesh_net_dst_reg(net, addr); l_debug("Added group subscription %4.4x", addr); return MESH_STATUS_SUCCESS; } static void send_dev_key_msg_rcvd(struct mesh_node *node, uint8_t ele_idx, uint16_t src, uint16_t app_idx, uint16_t net_idx, uint16_t size, const uint8_t *data) { struct l_dbus *dbus = dbus_get_bus(); struct l_dbus_message *msg; struct l_dbus_message_builder *builder; const char *owner; const char *path; bool remote = (app_idx != APP_IDX_DEV_LOCAL); owner = node_get_owner(node); path = node_get_element_path(node, ele_idx); if (!path || !owner) return; l_debug("Send \"DevKeyMessageReceived\""); msg = l_dbus_message_new_method_call(dbus, owner, path, MESH_ELEMENT_INTERFACE, "DevKeyMessageReceived"); builder = l_dbus_message_builder_new(msg); l_dbus_message_builder_append_basic(builder, 'q', &src); l_dbus_message_builder_append_basic(builder, 'b', &remote); l_dbus_message_builder_append_basic(builder, 'q', &net_idx); dbus_append_byte_array(builder, data, size); l_dbus_message_builder_finalize(builder); l_dbus_message_builder_destroy(builder); l_dbus_send(dbus, msg); } static void send_msg_rcvd(struct mesh_node *node, uint8_t ele_idx, uint16_t src, uint16_t dst, const struct mesh_virtual *virt, uint16_t app_idx, uint16_t size, const uint8_t *data) { struct l_dbus *dbus = dbus_get_bus(); struct l_dbus_message *msg; struct l_dbus_message_builder *builder; const char *owner; const char *path; owner = node_get_owner(node); path = node_get_element_path(node, ele_idx); if (!path || !owner) return; l_debug("Send \"MessageReceived\""); msg = l_dbus_message_new_method_call(dbus, owner, path, MESH_ELEMENT_INTERFACE, "MessageReceived"); builder = l_dbus_message_builder_new(msg); l_dbus_message_builder_append_basic(builder, 'q', &src); l_dbus_message_builder_append_basic(builder, 'q', &app_idx); if (virt) { l_dbus_message_builder_enter_variant(builder, "ay"); dbus_append_byte_array(builder, virt->label, sizeof(virt->label)); l_dbus_message_builder_leave_variant(builder); } else { l_dbus_message_builder_enter_variant(builder, "q"); l_dbus_message_builder_append_basic(builder, 'q', &dst); l_dbus_message_builder_leave_variant(builder); } dbus_append_byte_array(builder, data, size); l_dbus_message_builder_finalize(builder); l_dbus_message_builder_destroy(builder); l_dbus_send(dbus, msg); } bool mesh_model_rx(struct mesh_node *node, bool szmict, uint32_t seq0, uint32_t iv_index, uint16_t net_idx, uint16_t src, uint16_t dst, uint8_t key_aid, const uint8_t *data, uint16_t size) { uint8_t *clear_text; struct mod_forward forward = { .src = src, .dst = dst, .data = NULL, .size = size - (szmict ? 8 : 4), .virt = NULL, }; struct mesh_net *net = node_get_net(node); uint8_t num_ele; int decrypt_idx, i, ele_idx; uint16_t addr; struct mesh_virtual *decrypt_virt = NULL; bool result = false; bool is_subscription; l_debug("iv_index %8.8x key_aid = %2.2x", iv_index, key_aid); if (!dst) return false; ele_idx = node_get_element_idx(node, dst); if (dst < 0x8000 && ele_idx < 0) /* Unicast and not addressed to us */ return false; clear_text = l_malloc(size); forward.data = clear_text; /* * The packet needs to be decoded by the correct key which * is hinted by key_aid, but is not necessarily definitive */ if (key_aid == APP_AID_DEV) decrypt_idx = dev_packet_decrypt(node, data, size, szmict, src, dst, key_aid, seq0, iv_index, clear_text); else if ((dst & 0xc000) == 0x8000) decrypt_idx = virt_packet_decrypt(net, data, size, szmict, src, dst, key_aid, seq0, iv_index, clear_text, &decrypt_virt); else decrypt_idx = app_packet_decrypt(net, data, size, szmict, src, dst, NULL, 0, key_aid, seq0, iv_index, clear_text); if (decrypt_idx < 0) { l_error("model.c - Failed to decrypt application payload"); result = false; goto done; } print_packet("Clr Rx", clear_text, size - (szmict ? 8 : 4)); forward.virt = decrypt_virt; forward.app_idx = decrypt_idx; forward.net_idx = net_idx; num_ele = node_get_num_elements(node); addr = node_get_primary(node); if (!num_ele || IS_UNASSIGNED(addr)) goto done; is_subscription = !(IS_UNICAST(dst)); for (i = 0; i < num_ele; i++) { struct l_queue *models; if (!is_subscription && ele_idx != i) continue; forward.unicast = addr + i; forward.has_dst = false; models = node_get_element_models(node, i); /* Internal models */ l_queue_foreach(models, forward_model, &forward); /* * Cycle through external models if the message has not been * handled by internal models */ if (forward.has_dst && !forward.done) { if ((decrypt_idx & APP_IDX_MASK) == decrypt_idx) send_msg_rcvd(node, i, src, dst, decrypt_virt, forward.app_idx, forward.size, forward.data); else if (decrypt_idx == APP_IDX_DEV_REMOTE || decrypt_idx == APP_IDX_DEV_LOCAL) send_dev_key_msg_rcvd(node, i, src, decrypt_idx, net_idx, forward.size, forward.data); } /* * Either the message has been processed internally or * has been passed on to an external model. */ result |= forward.has_dst | forward.done; /* If the message was to unicast address, we are done */ if (!is_subscription && ele_idx == i) break; /* * For the fixed group addresses, i.e., all-proxies, * all-friends, all-relays, all-nodes, the message is delivered * to a primary element only. */ if (IS_FIXED_GROUP_ADDRESS(dst)) break; } done: l_free(clear_text); return result; } int mesh_model_publish(struct mesh_node *node, uint32_t id, uint16_t src, bool segmented, uint16_t msg_len, const void *msg) { struct mesh_net *net = node_get_net(node); struct mesh_model *mod; uint8_t *label = NULL; uint16_t net_idx; bool result; int ele_idx; if (!net || msg_len > 380) return MESH_ERROR_INVALID_ARGS; /* If SRC is 0, use the Primary Element */ if (src == 0) src = mesh_net_get_address(net); ele_idx = node_get_element_idx(node, src); if (ele_idx < 0) return MESH_ERROR_NOT_FOUND; mod = get_model(node, (uint8_t) ele_idx, id); if (!mod) return MESH_ERROR_NOT_FOUND; if (!mod->pub) { l_debug("publication doesn't exist (model %x)", id); return MESH_ERROR_DOES_NOT_EXIST; } if (IS_UNASSIGNED(mod->pub->addr)) return MESH_ERROR_DOES_NOT_EXIST; if (mod->pub->virt) label = mod->pub->virt->label; net_idx = appkey_net_idx(net, mod->pub->idx); result = msg_send(node, mod->pub->credential != 0, src, mod->pub->addr, mod->pub->idx, net_idx, label, mod->pub->ttl, mod->pub->rtx.cnt, mod->pub->rtx.interval, segmented, msg, msg_len); return result ? MESH_ERROR_NONE : MESH_ERROR_FAILED; } bool mesh_model_send(struct mesh_node *node, uint16_t src, uint16_t dst, uint16_t app_idx, uint16_t net_idx, uint8_t ttl, bool segmented, uint16_t msg_len, const void *msg) { struct mesh_net *net = node_get_net(node); uint8_t cnt; uint16_t interval; /* If SRC is 0, use the Primary Element */ if (src == 0) src = node_get_primary(node); if (IS_UNASSIGNED(dst)) return false; mesh_net_transmit_params_get(net, &cnt, &interval); return msg_send(node, false, src, dst, app_idx, net_idx, NULL, ttl, cnt, interval, segmented, msg, msg_len); } int mesh_model_pub_set(struct mesh_node *node, uint16_t addr, uint32_t id, const uint8_t *pub_addr, uint16_t idx, bool cred_flag, uint8_t ttl, uint8_t period, uint8_t cnt, uint16_t interval, bool is_virt, uint16_t *pub_dst) { struct mesh_model *mod; int status, ele_idx = node_get_element_idx(node, addr); if (ele_idx < 0) return MESH_STATUS_INVALID_ADDRESS; mod = get_model(node, (uint8_t) ele_idx, id); if (!mod) return MESH_STATUS_INVALID_MODEL; if (!mod->pub_enabled || (mod->cbs && !(mod->cbs->pub))) return MESH_STATUS_INVALID_PUB_PARAM; if (!appkey_have_key(node_get_net(node), idx) || !has_binding(mod->bindings, idx)) return MESH_STATUS_INVALID_APPKEY; /* * If the publication address is set to unassigned address value, * remove the publication */ if (!is_virt && IS_UNASSIGNED(l_get_le16(pub_addr))) { remove_pub(node, ele_idx, mod); return MESH_STATUS_SUCCESS; } if (cred_flag && node_lpn_mode_get(node) != MESH_MODE_ENABLED) return MESH_STATUS_FEATURE_NO_SUPPORT; /* Check if the old publication destination is a virtual label */ if (mod->pub && mod->pub->virt) { unref_virt(mod->pub->virt); mod->pub->virt = NULL; } if (!is_virt) { status = set_pub(mod, l_get_le16(pub_addr), idx, cred_flag, ttl, period, cnt, interval); } else status = set_virt_pub(mod, pub_addr, idx, cred_flag, ttl, period, cnt, interval); if (status != MESH_STATUS_SUCCESS) return status; *pub_dst = mod->pub->addr; if (!mod->cbs) /* External model */ config_update_model_pub_period(node, ele_idx, id, pub_period_to_ms(period)); else { /* Internal model, call registered callbacks */ if (mod->cbs->pub) mod->cbs->pub(mod->pub); } return MESH_STATUS_SUCCESS; } struct mesh_model_pub *mesh_model_pub_get(struct mesh_node *node, uint16_t addr, uint32_t mod_id, int *status) { struct mesh_model *mod; int ele_idx = node_get_element_idx(node, addr); if (ele_idx < 0) { *status = MESH_STATUS_INVALID_ADDRESS; return NULL; } mod = get_model(node, (uint8_t) ele_idx, mod_id); if (!mod) { *status = MESH_STATUS_INVALID_MODEL; return NULL; } if (!mod->pub_enabled || (mod->cbs && !(mod->cbs->pub))) *status = MESH_STATUS_INVALID_PUB_PARAM; else *status = MESH_STATUS_SUCCESS; return mod->pub; } void mesh_model_free(void *data) { struct mesh_model *mod = data; l_queue_destroy(mod->bindings, NULL); l_queue_destroy(mod->subs, NULL); l_queue_destroy(mod->virtuals, unref_virt); l_free(mod->pub); l_free(mod); } static void remove_subs(struct mesh_node *node, struct mesh_model *mod) { const struct l_queue_entry *entry; struct mesh_net *net = node_get_net(node); entry = l_queue_get_entries(mod->subs); for (; entry; entry = entry->next) mesh_net_dst_unreg(net, (uint16_t) L_PTR_TO_UINT(entry->data)); l_queue_clear(mod->subs, NULL); l_queue_clear(mod->virtuals, unref_virt); } static struct mesh_model *model_new(uint32_t id) { struct mesh_model *mod = l_new(struct mesh_model, 1); mod->id = id; mod->virtuals = l_queue_new(); /* * Unless specifically indicated by an app, subscriptions and * publications are enabled by default */ mod->sub_enabled = true; mod->pub_enabled = true; return mod; } static void model_enable_pub(struct mesh_model *mod, bool enable) { mod->pub_enabled = enable; if (!mod->pub_enabled && mod->pub) { if (mod->pub->virt) unref_virt(mod->pub->virt); l_free(mod->pub); mod->pub = NULL; } } static void model_enable_sub(struct mesh_node *node, struct mesh_model *mod, bool enable) { mod->sub_enabled = enable; if (!mod->sub_enabled) remove_subs(node, mod); } static bool get_model_options(struct mesh_model *mod, struct l_dbus_message_iter *opts) { const char *key; struct l_dbus_message_iter var; bool opt; while (l_dbus_message_iter_next_entry(opts, &key, &var)) { if (!strcmp(key, "Publish")) { if (!l_dbus_message_iter_get_variant(&var, "b", &opt)) return false; mod->pub_enabled = opt; } else if (!strcmp(key, "Subscribe")) { if (!l_dbus_message_iter_get_variant(&var, "b", &opt)) return false; mod->sub_enabled = opt; } else return false; } return true; } bool mesh_model_add(struct mesh_node *node, struct l_queue *mods, uint32_t id, struct l_dbus_message_iter *opts) { struct mesh_model *mod; /* Disallow duplicates */ mod = l_queue_find(mods, match_model_id, L_UINT_TO_PTR(id)); if (mod) return false; mod = model_new(id); if (opts && !get_model_options(mod, opts)) { mesh_model_free(mod); return false; } l_queue_insert(mods, mod, compare_model_id, NULL); return true; } /* Internal models only */ static void restore_model_state(struct mesh_model *mod) { const struct mesh_model_ops *cbs; const struct l_queue_entry *b; cbs = mod->cbs; if (!cbs) return; if (!l_queue_isempty(mod->bindings) && cbs->bind) { for (b = l_queue_get_entries(mod->bindings); b; b = b->next) { if (cbs->bind(L_PTR_TO_UINT(b->data), ACTION_ADD) != MESH_STATUS_SUCCESS) break; } } if (mod->pub && cbs->pub) cbs->pub(mod->pub); } /* This registers an internal model, i.e. implemented within meshd */ bool mesh_model_register(struct mesh_node *node, uint8_t ele_idx, uint32_t id, const struct mesh_model_ops *cbs, void *user_data) { struct mesh_model *mod; mod = get_model(node, ele_idx, id); if (!mod) return MESH_STATUS_INVALID_MODEL; mod->cbs = cbs; mod->user_data = user_data; restore_model_state(mod); return true; } void mesh_model_app_key_delete(struct mesh_node *node, uint16_t ele_idx, struct l_queue *models, uint16_t app_idx) { const struct l_queue_entry *entry = l_queue_get_entries(models); for (; entry; entry = entry->next) { struct mesh_model *mod = entry->data; model_unbind_idx(node, ele_idx, mod, app_idx); } } int mesh_model_binding_del(struct mesh_node *node, uint16_t addr, uint32_t id, uint16_t app_idx) { return update_binding(node, addr, id, app_idx, true); } int mesh_model_binding_add(struct mesh_node *node, uint16_t addr, uint32_t id, uint16_t app_idx) { return update_binding(node, addr, id, app_idx, false); } int mesh_model_get_bindings(struct mesh_node *node, uint16_t addr, uint32_t id, uint8_t *buf, uint16_t buf_size, uint16_t *size) { struct mesh_model *mod; const struct l_queue_entry *entry; uint16_t n; uint32_t idx_pair; int i, ele_idx = node_get_element_idx(node, addr); if (ele_idx < 0) return MESH_STATUS_INVALID_ADDRESS; mod = get_model(node, (uint8_t) ele_idx, id); if (!mod) { *size = 0; return MESH_STATUS_INVALID_MODEL; } entry = l_queue_get_entries(mod->bindings); n = 0; i = 0; idx_pair = 0; for (; entry; entry = entry->next) { uint16_t app_idx = (uint16_t) (L_PTR_TO_UINT(entry->data)); if (!(i & 0x1)) { idx_pair = app_idx; } else { idx_pair <<= 12; idx_pair += app_idx; /* Unlikely, but check for overflow*/ if ((n + 3) > buf_size) { l_warn("Binding list too large"); goto done; } l_put_le32(idx_pair, buf); buf += 3; n += 3; } i++; } /* Process the last app key if present */ if (i & 0x1 && ((n + 2) <= buf_size)) { l_put_le16(idx_pair, buf); n += 2; } done: *size = n; return MESH_STATUS_SUCCESS; } int mesh_model_sub_get(struct mesh_node *node, uint16_t ele_addr, uint32_t id, uint8_t *buf, uint16_t buf_size, uint16_t *size) { int16_t n; struct mesh_model *mod; const struct l_queue_entry *entry; int ele_idx = node_get_element_idx(node, ele_addr); if (ele_idx < 0) return MESH_STATUS_INVALID_ADDRESS; mod = get_model(node, (uint8_t) ele_idx, id); if (!mod) return MESH_STATUS_INVALID_MODEL; if (!mod->sub_enabled || (mod->cbs && !(mod->cbs->sub))) return MESH_STATUS_NOT_SUB_MOD; entry = l_queue_get_entries(mod->subs); *size = 0; n = 0; for (; entry; entry = entry->next) { if ((n + 2) > buf_size) return MESH_STATUS_UNSPECIFIED_ERROR; l_put_le16((uint16_t) L_PTR_TO_UINT(entry->data), buf); buf += 2; n += 2; } entry = l_queue_get_entries(mod->virtuals); for (; entry; entry = entry->next) { struct mesh_virtual *virt = entry->data; if ((n + 2) > buf_size) return MESH_STATUS_UNSPECIFIED_ERROR; l_put_le16((uint16_t) L_PTR_TO_UINT(virt->addr), buf); buf += 2; n += 2; } *size = n; return MESH_STATUS_SUCCESS; } int mesh_model_sub_add(struct mesh_node *node, uint16_t ele_addr, uint32_t id, uint16_t addr) { struct mesh_model *mod; int status, ele_idx = node_get_element_idx(node, ele_addr); if (ele_idx < 0) return MESH_STATUS_INVALID_ADDRESS; mod = get_model(node, (uint8_t) ele_idx, id); if (!mod) return MESH_STATUS_INVALID_MODEL; if (!mod->sub_enabled || (mod->cbs && !(mod->cbs->sub))) return MESH_STATUS_NOT_SUB_MOD; status = add_sub(node_get_net(node), mod, addr); if (status != MESH_STATUS_SUCCESS) return status; if (!mod->cbs) /* External models */ cfg_update_model_subs(node, ele_idx, mod); return MESH_STATUS_SUCCESS; } int mesh_model_virt_sub_add(struct mesh_node *node, uint16_t ele_addr, uint32_t id, const uint8_t *label, uint16_t *pub_addr) { struct mesh_model *mod; int status, ele_idx = node_get_element_idx(node, ele_addr); if (ele_idx < 0) return MESH_STATUS_INVALID_ADDRESS; mod = get_model(node, (uint8_t) ele_idx, id); if (!mod) return MESH_STATUS_INVALID_MODEL; if (!mod->sub_enabled || (mod->cbs && !(mod->cbs->sub))) return MESH_STATUS_NOT_SUB_MOD; status = add_virt_sub(node_get_net(node), mod, label, pub_addr); if (status != MESH_STATUS_SUCCESS) return status; if (!mod->cbs) /* External models */ cfg_update_model_subs(node, ele_idx, mod); return MESH_STATUS_SUCCESS; } int mesh_model_sub_ovrt(struct mesh_node *node, uint16_t ele_addr, uint32_t id, uint16_t addr) { struct mesh_model *mod; int ele_idx = node_get_element_idx(node, ele_addr); if (ele_idx < 0) return MESH_STATUS_INVALID_ADDRESS; mod = get_model(node, (uint8_t) ele_idx, id); if (!mod) return MESH_STATUS_INVALID_MODEL; if (!mod->sub_enabled || (mod->cbs && !(mod->cbs->sub))) return MESH_STATUS_NOT_SUB_MOD; l_queue_clear(mod->subs, NULL); l_queue_clear(mod->virtuals, unref_virt); add_sub(node_get_net(node), mod, addr); if (!mod->cbs) /* External models */ cfg_update_model_subs(node, ele_idx, mod); return MESH_STATUS_SUCCESS; } int mesh_model_virt_sub_ovrt(struct mesh_node *node, uint16_t ele_addr, uint32_t id, const uint8_t *label, uint16_t *addr) { struct mesh_model *mod; int status, ele_idx = node_get_element_idx(node, ele_addr); if (ele_idx < 0) return MESH_STATUS_INVALID_ADDRESS; mod = get_model(node, (uint8_t) ele_idx, id); if (!mod) return MESH_STATUS_INVALID_MODEL; if (!mod->sub_enabled || (mod->cbs && !(mod->cbs->sub))) return MESH_STATUS_NOT_SUB_MOD; l_queue_clear(mod->subs, NULL); l_queue_clear(mod->virtuals, unref_virt); status = add_virt_sub(node_get_net(node), mod, label, addr); if (!mod->cbs) /* External models */ cfg_update_model_subs(node, ele_idx, mod); return status; } int mesh_model_sub_del(struct mesh_node *node, uint16_t ele_addr, uint32_t id, uint16_t addr) { struct mesh_model *mod; int ele_idx = node_get_element_idx(node, ele_addr); if (ele_idx < 0) return MESH_STATUS_INVALID_ADDRESS; mod = get_model(node, (uint8_t) ele_idx, id); if (!mod) return MESH_STATUS_INVALID_MODEL; if (!mod->sub_enabled || (mod->cbs && !(mod->cbs->sub))) return MESH_STATUS_NOT_SUB_MOD; if (l_queue_remove(mod->subs, L_UINT_TO_PTR(addr))) { mesh_net_dst_unreg(node_get_net(node), addr); if (!mod->cbs) /* External models */ cfg_update_model_subs(node, ele_idx, mod); } return MESH_STATUS_SUCCESS; } int mesh_model_virt_sub_del(struct mesh_node *node, uint16_t ele_addr, uint32_t id, const uint8_t *label, uint16_t *addr) { struct mesh_model *mod; struct mesh_virtual *virt; int ele_idx = node_get_element_idx(node, ele_addr); if (ele_idx < 0) return MESH_STATUS_INVALID_ADDRESS; mod = get_model(node, (uint8_t) ele_idx, id); if (!mod) return MESH_STATUS_INVALID_MODEL; if (!mod->sub_enabled || (mod->cbs && !(mod->cbs->sub))) return MESH_STATUS_NOT_SUB_MOD; virt = l_queue_remove_if(mod->virtuals, find_virt_by_label, label); if (virt) { *addr = virt->addr; unref_virt(virt); } else { *addr = UNASSIGNED_ADDRESS; return MESH_STATUS_SUCCESS; } if (l_queue_remove(mod->subs, L_UINT_TO_PTR(*addr))) { mesh_net_dst_unreg(node_get_net(node), *addr); if (!mod->cbs) /* External models */ cfg_update_model_subs(node, ele_idx, mod); } return MESH_STATUS_SUCCESS; } int mesh_model_sub_del_all(struct mesh_node *node, uint16_t ele_addr, uint32_t id) { struct mesh_model *mod; int ele_idx = node_get_element_idx(node, ele_addr); if (ele_idx < 0) return MESH_STATUS_INVALID_ADDRESS; mod = get_model(node, (uint8_t) ele_idx, id); if (!mod) return MESH_STATUS_INVALID_MODEL; if (!mod->sub_enabled || (mod->cbs && !(mod->cbs->sub))) return MESH_STATUS_NOT_SUB_MOD; remove_subs(node, mod); if (!mod->cbs) /* External models */ cfg_update_model_subs(node, ele_idx, mod); return MESH_STATUS_SUCCESS; } static struct mesh_model *model_setup(struct mesh_net *net, uint8_t ele_idx, struct mesh_config_model *db_mod) { struct mesh_model *mod; struct mesh_config_pub *pub = db_mod->pub; uint32_t i; if (db_mod->num_bindings > MAX_MODEL_BINDINGS) { l_warn("Binding list too long %u (max %u)", db_mod->num_bindings, MAX_MODEL_BINDINGS); return NULL; } mod = model_new(db_mod->vendor ? db_mod->id : SET_ID(SIG_VENDOR, db_mod->id)); /* Implicitly bind config server model to device key */ if (db_mod->id == CONFIG_SRV_MODEL || db_mod->id == PRV_BEACON_SRV_MODEL) { if (ele_idx != PRIMARY_ELE_IDX) { l_free(mod); return NULL; } l_queue_push_head(mod->bindings, L_UINT_TO_PTR(APP_IDX_DEV_LOCAL)); return mod; } if (db_mod->id == CONFIG_CLI_MODEL) { l_queue_push_head(mod->bindings, L_UINT_TO_PTR(APP_IDX_DEV_LOCAL)); return mod; } /* Add application key bindings if present */ if (db_mod->bindings) { mod->bindings = l_queue_new(); for (i = 0; i < db_mod->num_bindings; i++) l_queue_push_tail(mod->bindings, L_UINT_TO_PTR(db_mod->bindings[i])); } mod->pub_enabled = db_mod->pub_enabled; /* Add publication if enabled and present */ if (mod->pub_enabled && pub) { if (pub->virt) set_virt_pub(mod, pub->virt_addr, pub->idx, pub->credential, pub->ttl, pub->period, pub->cnt, pub->interval); else if (!IS_UNASSIGNED(pub->addr)) set_pub(mod, pub->addr, pub->idx, pub->credential, pub->ttl, pub->period, pub->cnt, pub->interval); } mod->sub_enabled = db_mod->sub_enabled; /* Add subscriptions if enabled and present */ if (!db_mod->subs || !mod->sub_enabled) return mod; for (i = 0; i < db_mod->num_subs; i++) { struct mesh_config_sub *sub = &db_mod->subs[i]; if (!sub->virt) add_sub(net, mod, sub->addr.grp); else add_virt_sub(net, mod, sub->addr.label, NULL); } return mod; } bool mesh_model_add_from_storage(struct mesh_node *node, uint8_t ele_idx, struct l_queue *mods, struct l_queue *db_mods) { struct mesh_net *net = node_get_net(node); const struct l_queue_entry *entry; /* Allow empty elements */ if (!db_mods) return true; entry = l_queue_get_entries(db_mods); for (; entry; entry = entry->next) { struct mesh_model *mod; struct mesh_config_model *db_mod; uint32_t id; db_mod = entry->data; id = db_mod->vendor ? db_mod->id : SET_ID(SIG_VENDOR, db_mod->id); if (l_queue_find(mods, match_model_id, L_UINT_TO_PTR(id))) return false; mod = model_setup(net, ele_idx, db_mod); if (!mod) return false; l_queue_insert(mods, mod, compare_model_id, NULL); } return true; } void mesh_model_convert_to_storage(struct l_queue *db_mods, struct l_queue *mods) { const struct l_queue_entry *entry = l_queue_get_entries(mods); for (; entry; entry = entry->next) { struct mesh_model *mod = entry->data; struct mesh_config_model *db_mod; db_mod = l_new(struct mesh_config_model, 1); db_mod->id = mod->id; db_mod->vendor = IS_VENDOR(mod->id); db_mod->pub_enabled = mod->pub_enabled; db_mod->sub_enabled = mod->sub_enabled; l_queue_push_tail(db_mods, db_mod); } } uint16_t mesh_model_opcode_set(uint32_t opcode, uint8_t *buf) { if (opcode <= 0x7e) { buf[0] = opcode; return 1; } if (opcode >= 0x8000 && opcode <= 0xbfff) { l_put_be16(opcode, buf); return 2; } if (opcode >= 0xc00000 && opcode <= 0xffffff) { buf[0] = (opcode >> 16) & 0xff; l_put_be16(opcode, buf + 1); return 3; } l_debug("Illegal Opcode %x", opcode); return 0; } bool mesh_model_opcode_get(const uint8_t *buf, uint16_t size, uint32_t *opcode, uint16_t *n) { if (!n || !opcode || size < 1) return false; switch (buf[0] & 0xc0) { case 0x00: case 0x40: /* RFU */ if (buf[0] == 0x7f) return false; *n = 1; *opcode = buf[0]; break; case 0x80: if (size < 2) return false; *n = 2; *opcode = l_get_be16(buf); break; case 0xc0: if (size < 3) return false; *n = 3; *opcode = l_get_be16(buf + 1); *opcode |= buf[0] << 16; break; default: print_packet("Bad", buf, size); return false; } return true; } void mesh_model_build_config(void *model, void *msg_builder) { struct l_dbus_message_builder *builder = msg_builder; struct mesh_model *mod = model; uint16_t id; if (is_internal(mod->id)) return; if (!l_queue_length(mod->subs) && !l_queue_length(mod->virtuals) && !mod->pub && !l_queue_length(mod->bindings)) return; l_dbus_message_builder_enter_struct(builder, "qa{sv}"); /* Model id */ id = MODEL_ID(mod->id); l_dbus_message_builder_append_basic(builder, 'q', &id); l_dbus_message_builder_enter_array(builder, "{sv}"); /* For vendor models, add vendor id */ if (IS_VENDOR(mod->id)) { uint16_t vendor = VENDOR_ID(mod->id); dbus_append_dict_entry_basic(builder, "Vendor", "q", &vendor); } /* Model bindings, if present */ if (l_queue_length(mod->bindings)) append_dict_uint16_array(builder, mod->bindings, "Bindings"); /* Model periodic publication interval, if present */ if (mod->pub) { uint32_t period = pub_period_to_ms(mod->pub->period); dbus_append_dict_entry_basic(builder, "PublicationPeriod", "u", &period); } if (l_queue_length(mod->subs) || l_queue_length(mod->virtuals)) append_dict_subs_array(builder, mod->subs, mod->virtuals, "Subscriptions"); l_dbus_message_builder_leave_array(builder); l_dbus_message_builder_leave_struct(builder); } void mesh_model_update_opts(struct mesh_node *node, uint8_t ele_idx, struct l_queue *curr, struct l_queue *updated) { uint16_t primary; const struct l_queue_entry *entry; primary = node_get_primary(node); entry = l_queue_get_entries(curr); for (; entry; entry = entry->next) { struct mesh_model *mod, *updated_mod = entry->data; uint32_t id = updated_mod->id; bool updated_opt, vendor = IS_VENDOR(id); mod = l_queue_find(curr, match_model_id, L_UINT_TO_PTR(id)); if (!mod) continue; if (!vendor) id = MODEL_ID(id); updated_opt = updated_mod->pub_enabled; if (mod->pub_enabled != updated_opt) { model_enable_pub(mod, updated_opt); mesh_config_model_pub_enable(node_config_get(node), primary + ele_idx, id, vendor, updated_opt); } updated_opt = updated_mod->sub_enabled; if (mod->pub_enabled != updated_opt) { model_enable_sub(node, mod, updated_opt); mesh_config_model_sub_enable(node_config_get(node), primary + ele_idx, id, vendor, updated_opt); } } } /* Populate composition buffer with model IDs */ uint16_t mesh_model_generate_composition(struct l_queue *mods, uint16_t buf_sz, uint8_t *buf) { const struct l_queue_entry *entry; uint8_t num_s = 0, num_v = 0; uint8_t *mod_buf; uint16_t n; /* Store models IDs, store num_s and num_v later */ mod_buf = buf; n = 2; entry = l_queue_get_entries(mods); /* Get SIG models */ for (; entry; entry = entry->next) { struct mesh_model *mod = entry->data; if (n + 2 > buf_sz) goto done; if (IS_VENDOR(mod->id)) continue; l_put_le16((uint16_t) (MODEL_ID(mod->id)), buf + n); n += 2; num_s++; } /* Get vendor models */ entry = l_queue_get_entries(mods); for (; entry; entry = entry->next) { struct mesh_model *mod = entry->data; uint16_t vendor_id; if (n + 4 > buf_sz) goto done; if (!IS_VENDOR(mod->id)) continue; vendor_id = (uint16_t) (VENDOR_ID(mod->id)); l_put_le16(vendor_id, buf + n); n += 2; l_put_le16((uint16_t) (MODEL_ID(mod->id)), buf + n); n += 2; num_v++; } done: mod_buf[0] = num_s; mod_buf[1] = num_v; return n; } void mesh_model_init(void) { mesh_virtuals = l_queue_new(); } void mesh_model_cleanup(void) { l_queue_destroy(mesh_virtuals, l_free); mesh_virtuals = NULL; } bluez-5.82/mesh/PaxHeaders/mesh-config-json.c0000644000000000000000000000005014772767672016145 xustar0020 atime=1743515579 20 ctime=1743591282 bluez-5.82/mesh/mesh-config-json.c0000644000000000000000000016017114772767672015634 0ustar00rootroot// SPDX-License-Identifier: LGPL-2.1-or-later /* * * BlueZ - Bluetooth protocol stack for Linux * * Copyright (C) 2018-2019 Intel Corporation. All rights reserved. * * */ #ifdef HAVE_CONFIG_H #include #endif #define _GNU_SOURCE #include #include #include #include #include #include #include #include #include #include #include #include #include #include "mesh/mesh-defs.h" #include "mesh/util.h" #include "mesh/mesh-config.h" /* To prevent local node JSON cache thrashing, minimum update times */ #define MIN_SEQ_CACHE_TRIGGER 32 #define MIN_SEQ_CACHE_VALUE (2 * 32) #define MIN_SEQ_CACHE_TIME (5 * 60) #define CHECK_KEY_IDX_RANGE(x) ((x) <= 4095) struct mesh_config { json_object *jnode; char *node_dir_path; uint8_t uuid[16]; uint32_t write_seq; struct timeval write_time; struct l_queue *idles; }; struct write_info { struct mesh_config *cfg; void *user_data; mesh_config_status_func_t cb; }; static const char *cfgnode_name = "/node.json"; static const char *bak_ext = ".bak"; static const char *tmp_ext = ".tmp"; /* JSON key words */ static const char *unicastAddress = "unicastAddress"; static const char *deviceCan = "deviceCan"; static const char *deviceKey = "deviceKey"; static const char *defaultTTL = "defaultTTL"; static const char *sequenceNumber = "sequenceNumber"; static const char *netKeys = "netKeys"; static const char *appKeys = "appKeys"; static const char *elements = "elements"; static const char *models = "models"; static const char *modelId = "modelId"; static const char *address = "address"; static const char *bind = "bind"; static const char *publish = "publish"; static const char *subscribe = "subscribe"; static const char *boundNetKey = "boundNetKey"; static const char *keyRefresh = "keyRefresh"; static const char *subEnabled = "subEnabled"; static const char *pubEnabled = "pubEnabled"; static const char *retransmit = "retransmit"; /* Common JSON values */ static const char *enabled = "enabled"; static const char *disabled = "disabled"; static const char *unsupported = "unsupported"; static bool save_config(json_object *jnode, const char *fname) { FILE *outfile; const char *str; bool result = false; outfile = fopen(fname, "w"); if (!outfile) { l_error("Failed to save configuration to %s", fname); return false; } str = json_object_to_json_string_ext(jnode, JSON_C_TO_STRING_PRETTY); if (fwrite(str, sizeof(char), strlen(str), outfile) < strlen(str)) l_warn("Incomplete write of mesh configuration"); else result = true; fclose(outfile); return result; } static bool get_int(json_object *jobj, const char *keyword, int *value) { json_object *jvalue; if (!json_object_object_get_ex(jobj, keyword, &jvalue)) return false; *value = json_object_get_int(jvalue); if (errno == EINVAL) return false; return true; } static bool add_u64_value(json_object *jobj, const char *desc, const uint8_t u64[8]) { json_object *jstring; char hexstr[17]; hex2str((uint8_t *) u64, 8, hexstr, 17); jstring = json_object_new_string(hexstr); if (!jstring) return false; json_object_object_del(jobj, desc); json_object_object_add(jobj, desc, jstring); return true; } static bool add_key_value(json_object *jobj, const char *desc, const uint8_t key[16]) { json_object *jstring; char hexstr[33]; hex2str((uint8_t *) key, 16, hexstr, 33); jstring = json_object_new_string(hexstr); if (!jstring) return false; json_object_object_del(jobj, desc); json_object_object_add(jobj, desc, jstring); return true; } static int get_element_index(json_object *jnode, uint16_t ele_addr) { json_object *jvalue, *jelements; uint16_t addr, num_ele; char *str; if (!json_object_object_get_ex(jnode, unicastAddress, &jvalue)) return -1; str = (char *)json_object_get_string(jvalue); if (sscanf(str, "%04hx", &addr) != 1) return -1; if (!json_object_object_get_ex(jnode, elements, &jelements)) return -1; num_ele = json_object_array_length(jelements); if (ele_addr >= addr + num_ele || ele_addr < addr) return -1; return ele_addr - addr; } static json_object *get_element_model(json_object *jnode, int ele_idx, uint32_t mod_id, bool vendor) { json_object *jelements, *jelement, *jmodels; int i, num_mods, ret; size_t len; char buf[9]; if (!json_object_object_get_ex(jnode, elements, &jelements)) return NULL; jelement = json_object_array_get_idx(jelements, ele_idx); if (!jelement) return NULL; if (!json_object_object_get_ex(jelement, models, &jmodels)) return NULL; num_mods = json_object_array_length(jmodels); if (!num_mods) return NULL; if (!vendor) ret = snprintf(buf, 5, "%4.4x", (uint16_t)mod_id); else ret = snprintf(buf, 9, "%8.8x", mod_id); if (ret < 0) return NULL; len = ret; for (i = 0; i < num_mods; ++i) { json_object *jmodel, *jvalue; char *str; jmodel = json_object_array_get_idx(jmodels, i); if (!json_object_object_get_ex(jmodel, modelId, &jvalue)) return NULL; str = (char *)json_object_get_string(jvalue); if (!str) return NULL; if (!strncmp(str, buf, len)) return jmodel; } return NULL; } static bool jarray_has_string(json_object *jarray, char *str, size_t len) { int i, sz = json_object_array_length(jarray); for (i = 0; i < sz; ++i) { json_object *jentry; char *str_entry; jentry = json_object_array_get_idx(jarray, i); str_entry = (char *)json_object_get_string(jentry); if (!str_entry) continue; if (!strncmp(str, str_entry, len)) return true; } return false; } static void jarray_string_del(json_object *jarray, char *str, size_t len) { int i, sz = json_object_array_length(jarray); for (i = 0; i < sz; ++i) { json_object *jentry; char *str_entry; jentry = json_object_array_get_idx(jarray, i); str_entry = (char *)json_object_get_string(jentry); if (str_entry && !strncmp(str, str_entry, len)) { json_object_array_del_idx(jarray, i, 1); return; } } } static bool get_key_index(json_object *jobj, const char *keyword, uint16_t *index) { int idx; if (!get_int(jobj, keyword, &idx)) return false; if (!CHECK_KEY_IDX_RANGE(idx)) return false; *index = (uint16_t) idx; return true; } static json_object *get_key_object(json_object *jarray, uint16_t idx) { int i, sz = json_object_array_length(jarray); for (i = 0; i < sz; ++i) { json_object *jentry; uint16_t jidx; jentry = json_object_array_get_idx(jarray, i); if (!get_key_index(jentry, "index", &jidx)) return NULL; if (jidx == idx) return jentry; } return NULL; } static void jarray_key_del(json_object *jarray, int16_t idx) { int i, sz = json_object_array_length(jarray); for (i = 0; i < sz; ++i) { json_object *jentry; uint16_t nidx; jentry = json_object_array_get_idx(jarray, i); if (get_key_index(jentry, "index", &nidx) && nidx == idx) { json_object_array_del_idx(jarray, i, 1); return; } } } static bool read_unicast_address(json_object *jobj, uint16_t *unicast) { json_object *jvalue; char *str; if (!json_object_object_get_ex(jobj, unicastAddress, &jvalue)) return false; str = (char *)json_object_get_string(jvalue); if (sscanf(str, "%04hx", unicast) != 1) return false; return true; } static bool read_default_ttl(json_object *jobj, uint8_t *ttl) { json_object *jvalue; int val; /* defaultTTL is optional */ if (!json_object_object_get_ex(jobj, defaultTTL, &jvalue)) return true; val = json_object_get_int(jvalue); if (!val && errno == EINVAL) return false; if (val < 0 || val == 1 || val > TTL_MASK) return false; *ttl = (uint8_t) val; return true; } static bool read_seq_number(json_object *jobj, uint32_t *seq_number) { json_object *jvalue; int val; /* sequenceNumber is optional */ if (!json_object_object_get_ex(jobj, sequenceNumber, &jvalue)) return true; val = json_object_get_int(jvalue); if (!val && errno == EINVAL) return false; if (val < 0 || val > SEQ_MASK + 1) return false; *seq_number = (uint32_t) val; return true; } static bool read_iv_index(json_object *jobj, uint32_t *idx, bool *update) { int tmp; /* IV index */ if (!get_int(jobj, "IVindex", &tmp)) return false; *idx = (uint32_t) tmp; if (!get_int(jobj, "IVupdate", &tmp)) return false; *update = tmp ? true : false; return true; } static bool read_token(json_object *jobj, uint8_t token[8]) { json_object *jvalue; char *str; if (!token) return false; if (!json_object_object_get_ex(jobj, "token", &jvalue)) return false; str = (char *)json_object_get_string(jvalue); if (!str2hex(str, strlen(str), token, 8)) return false; return true; } static bool read_device_key(json_object *jobj, uint8_t key_buf[16]) { json_object *jvalue; char *str; if (!key_buf) return false; if (!json_object_object_get_ex(jobj, deviceKey, &jvalue)) return false; str = (char *)json_object_get_string(jvalue); if (!str2hex(str, strlen(str), key_buf, 16)) return false; return true; } static bool read_candidate(json_object *jobj, uint8_t key_buf[16]) { json_object *jvalue; char *str; if (!key_buf) return false; if (!json_object_object_get_ex(jobj, deviceCan, &jvalue)) return false; str = (char *)json_object_get_string(jvalue); if (!str2hex(str, strlen(str), key_buf, 16)) return false; return true; } static bool read_comp_pages(json_object *jobj, struct mesh_config_node *node) { json_object *jarray, *jentry; struct mesh_config_comp_page *page; int len; int i; if (!json_object_object_get_ex(jobj, "pages", &jarray)) return true; if (json_object_get_type(jarray) != json_type_array) return false; len = json_object_array_length(jarray); for (i = 0; i < len; i++) { size_t clen; char *str; jentry = json_object_array_get_idx(jarray, i); str = (char *)json_object_get_string(jentry); clen = strlen(str); if (clen < ((MIN_COMP_SIZE * 2) + 1)) continue; clen = (clen / 2) - 1; page = l_malloc(sizeof(struct mesh_config_comp_page) + clen); if (!str2hex(str + 2, clen * 2, page->data, clen)) goto parse_fail; if (sscanf(str, "%02hhx", &page->page_num) != 1) goto parse_fail; page->len = clen; l_queue_push_tail(node->pages, page); } return true; parse_fail: l_free(page); return false; } static bool read_app_keys(json_object *jobj, struct mesh_config_node *node) { json_object *jarray; int len; int i; if (!json_object_object_get_ex(jobj, appKeys, &jarray)) return true; if (json_object_get_type(jarray) != json_type_array) return false; /* Allow empty AppKey array */ len = json_object_array_length(jarray); if (!len) return true; for (i = 0; i < len; ++i) { json_object *jtemp, *jvalue; char *str; struct mesh_config_appkey *appkey; appkey = l_new(struct mesh_config_appkey, 1); l_queue_push_tail(node->appkeys, appkey); jtemp = json_object_array_get_idx(jarray, i); if (!get_key_index(jtemp, "index", &appkey->app_idx)) goto fail; if (!get_key_index(jtemp, boundNetKey, &appkey->net_idx)) goto fail; if (!json_object_object_get_ex(jtemp, "key", &jvalue)) goto fail; str = (char *)json_object_get_string(jvalue); if (!str2hex(str, strlen(str), appkey->new_key, 16)) goto fail; if (json_object_object_get_ex(jtemp, "oldKey", &jvalue)) str = (char *)json_object_get_string(jvalue); if (!str2hex(str, strlen(str), appkey->key, 16)) goto fail; } return true; fail: l_queue_destroy(node->appkeys, l_free); node->appkeys = NULL; return false; } static bool read_net_keys(json_object *jobj, struct mesh_config_node *node) { json_object *jarray; int len; int i; /* At least one NetKey must be present for a provisioned node */ if (!json_object_object_get_ex(jobj, netKeys, &jarray)) return false; if (json_object_get_type(jarray) != json_type_array) return false; len = json_object_array_length(jarray); if (!len) return false; for (i = 0; i < len; ++i) { json_object *jtemp, *jvalue; char *str; struct mesh_config_netkey *netkey; netkey = l_new(struct mesh_config_netkey, 1); l_queue_push_tail(node->netkeys, netkey); jtemp = json_object_array_get_idx(jarray, i); if (!get_key_index(jtemp, "index", &netkey->idx)) goto fail; if (!json_object_object_get_ex(jtemp, "key", &jvalue)) goto fail; str = (char *)json_object_get_string(jvalue); if (!str2hex(str, strlen(str), netkey->new_key, 16)) goto fail; if (!json_object_object_get_ex(jtemp, keyRefresh, &jvalue)) netkey->phase = KEY_REFRESH_PHASE_NONE; else netkey->phase = (uint8_t) json_object_get_int(jvalue); if (netkey->phase > KEY_REFRESH_PHASE_TWO) goto fail; if (json_object_object_get_ex(jtemp, "oldKey", &jvalue)) { if (netkey->phase == KEY_REFRESH_PHASE_NONE) goto fail; str = (char *)json_object_get_string(jvalue); } if (!str2hex(str, strlen(str), netkey->key, 16)) goto fail; } return true; fail: l_queue_destroy(node->netkeys, l_free); node->netkeys = NULL; return false; } static bool write_int(json_object *jobj, const char *desc, int val) { json_object *jvalue; jvalue = json_object_new_int(val); if (!jvalue) return false; json_object_object_del(jobj, desc); json_object_object_add(jobj, desc, jvalue); return true; } bool mesh_config_net_key_add(struct mesh_config *cfg, uint16_t idx, const uint8_t key[16]) { json_object *jnode, *jarray = NULL, *jentry = NULL; if (!cfg) return false; jnode = cfg->jnode; l_debug("netKey %4.4x", idx); json_object_object_get_ex(jnode, netKeys, &jarray); if (jarray) jentry = get_key_object(jarray, idx); /* Do not allow direct overwrite */ if (jentry) return false; jentry = json_object_new_object(); if (!jentry) return false; if (!write_int(jentry, "index", idx)) goto fail; if (!add_key_value(jentry, "key", key)) goto fail; json_object_object_add(jentry, keyRefresh, json_object_new_int(KEY_REFRESH_PHASE_NONE)); if (!jarray) { jarray = json_object_new_array(); if (!jarray) goto fail; json_object_object_add(jnode, netKeys, jarray); } json_object_array_add(jarray, jentry); return save_config(jnode, cfg->node_dir_path); fail: if (jentry) json_object_put(jentry); return false; } bool mesh_config_net_key_update(struct mesh_config *cfg, uint16_t idx, const uint8_t key[16]) { json_object *jnode, *jarray, *jentry, *jstring; const char *str; if (!cfg) return false; jnode = cfg->jnode; if (!json_object_object_get_ex(jnode, netKeys, &jarray)) return false; jentry = get_key_object(jarray, idx); /* Net key must be already recorded */ if (!jentry) return false; if (!json_object_object_get_ex(jentry, "key", &jstring)) return false; str = json_object_get_string(jstring); jstring = json_object_new_string(str); json_object_object_add(jentry, "oldKey", jstring); json_object_object_del(jentry, "key"); if (!add_key_value(jentry, "key", key)) return false; json_object_object_add(jentry, keyRefresh, json_object_new_int(KEY_REFRESH_PHASE_ONE)); return save_config(jnode, cfg->node_dir_path); } bool mesh_config_net_key_del(struct mesh_config *cfg, uint16_t idx) { json_object *jnode, *jarray; if (!cfg) return false; jnode = cfg->jnode; if (!json_object_object_get_ex(jnode, netKeys, &jarray)) return true; jarray_key_del(jarray, idx); if (!json_object_array_length(jarray)) json_object_object_del(jnode, netKeys); return save_config(jnode, cfg->node_dir_path); } bool mesh_config_write_device_key(struct mesh_config *cfg, uint8_t *key) { if (!cfg || !add_key_value(cfg->jnode, deviceKey, key)) return false; return save_config(cfg->jnode, cfg->node_dir_path); } bool mesh_config_write_candidate(struct mesh_config *cfg, uint8_t *key) { if (!cfg || !add_key_value(cfg->jnode, deviceCan, key)) return false; return save_config(cfg->jnode, cfg->node_dir_path); } bool mesh_config_read_candidate(struct mesh_config *cfg, uint8_t *key) { if (!cfg) return false; return read_candidate(cfg->jnode, key); } bool mesh_config_finalize_candidate(struct mesh_config *cfg) { uint8_t key[16]; if (!cfg) return false; if (!read_candidate(cfg->jnode, key)) return false; json_object_object_del(cfg->jnode, deviceCan); json_object_object_del(cfg->jnode, deviceKey); if (!add_key_value(cfg->jnode, deviceKey, key)) return false; return save_config(cfg->jnode, cfg->node_dir_path); } bool mesh_config_write_token(struct mesh_config *cfg, uint8_t *token) { if (!cfg || !add_u64_value(cfg->jnode, "token", token)) return false; return save_config(cfg->jnode, cfg->node_dir_path); } bool mesh_config_app_key_add(struct mesh_config *cfg, uint16_t net_idx, uint16_t app_idx, const uint8_t key[16]) { json_object *jnode, *jarray = NULL, *jentry = NULL; if (!cfg) return false; jnode = cfg->jnode; json_object_object_get_ex(jnode, appKeys, &jarray); if (jarray) jentry = get_key_object(jarray, app_idx); /* Do not allow direct overrwrite */ if (jentry) return false; jentry = json_object_new_object(); if (!jentry) return false; if (!write_int(jentry, "index", app_idx)) goto fail; if (!write_int(jentry, boundNetKey, net_idx)) goto fail; if (!add_key_value(jentry, "key", key)) goto fail; if (!jarray) { jarray = json_object_new_array(); if (!jarray) goto fail; json_object_object_add(jnode, appKeys, jarray); } json_object_array_add(jarray, jentry); return save_config(jnode, cfg->node_dir_path); fail: if (jentry) json_object_put(jentry); return false; } bool mesh_config_app_key_update(struct mesh_config *cfg, uint16_t app_idx, const uint8_t key[16]) { json_object *jnode, *jarray, *jentry = NULL, *jstring = NULL; const char *str; if (!cfg) return false; jnode = cfg->jnode; if (!json_object_object_get_ex(jnode, appKeys, &jarray)) return false; /* The key entry should exist if the key is updated */ jentry = get_key_object(jarray, app_idx); if (!jentry) return false; if (!json_object_object_get_ex(jentry, "key", &jstring)) return false; str = json_object_get_string(jstring); jstring = json_object_new_string(str); json_object_object_add(jentry, "oldKey", jstring); json_object_object_del(jentry, "key"); /* TODO: "Rewind" if add_key_value fails */ if (!add_key_value(jentry, "key", key)) return false; return save_config(jnode, cfg->node_dir_path); } bool mesh_config_app_key_del(struct mesh_config *cfg, uint16_t net_idx, uint16_t idx) { json_object *jnode, *jarray; if (!cfg) return false; jnode = cfg->jnode; if (!json_object_object_get_ex(jnode, appKeys, &jarray)) return true; jarray_key_del(jarray, idx); if (!json_object_array_length(jarray)) json_object_object_del(jnode, appKeys); return save_config(jnode, cfg->node_dir_path); } bool mesh_config_model_binding_add(struct mesh_config *cfg, uint16_t ele_addr, uint32_t mod_id, bool vendor, uint16_t app_idx) { json_object *jnode, *jmodel, *jstring, *jarray = NULL; int ele_idx, ret; char buf[5]; if (!cfg) return false; jnode = cfg->jnode; ret = snprintf(buf, 5, "%4.4x", app_idx); if (ret < 0) return false; ele_idx = get_element_index(jnode, ele_addr); if (ele_idx < 0) return false; jmodel = get_element_model(jnode, ele_idx, mod_id, vendor); if (!jmodel) return false; json_object_object_get_ex(jmodel, bind, &jarray); if (jarray && jarray_has_string(jarray, buf, 4)) return true; jstring = json_object_new_string(buf); if (!jstring) return false; if (!jarray) { jarray = json_object_new_array(); if (!jarray) { json_object_put(jstring); return false; } json_object_object_add(jmodel, bind, jarray); } json_object_array_add(jarray, jstring); return save_config(jnode, cfg->node_dir_path); } bool mesh_config_model_binding_del(struct mesh_config *cfg, uint16_t ele_addr, uint32_t mod_id, bool vendor, uint16_t app_idx) { json_object *jnode, *jmodel, *jarray; int ele_idx, ret; char buf[5]; if (!cfg) return false; jnode = cfg->jnode; ret = snprintf(buf, 5, "%4.4x", app_idx); if (ret < 0) return false; ele_idx = get_element_index(jnode, ele_addr); if (ele_idx < 0) return false; jmodel = get_element_model(jnode, ele_idx, mod_id, vendor); if (!jmodel) return false; if (!json_object_object_get_ex(jmodel, bind, &jarray)) return true; jarray_string_del(jarray, buf, 4); if (!json_object_array_length(jarray)) json_object_object_del(jmodel, bind); return save_config(jnode, cfg->node_dir_path); } static void free_model(void *data) { struct mesh_config_model *mod = data; l_free(mod->bindings); l_free(mod->subs); l_free(mod->pub); l_free(mod); } static void free_element(void *data) { struct mesh_config_element *ele = data; l_queue_destroy(ele->models, free_model); l_free(ele); } static bool parse_bindings(json_object *jarray, struct mesh_config_model *mod) { int cnt; int i; cnt = json_object_array_length(jarray); if (cnt > 0xffff) return false; mod->num_bindings = cnt; /* Allow empty bindings list */ if (!cnt) return true; mod->bindings = l_new(uint16_t, cnt); for (i = 0; i < cnt; ++i) { uint16_t idx; const char *str; json_object *jvalue; jvalue = json_object_array_get_idx(jarray, i); if (!jvalue) return false; str = json_object_get_string(jvalue); if (sscanf(str, "%04hx", &idx) != 1) return false; if (!CHECK_KEY_IDX_RANGE(idx)) return false; mod->bindings[i] = (uint16_t) idx; } return true; } static struct mesh_config_pub *parse_model_publication(json_object *jpub) { json_object *jvalue; struct mesh_config_pub *pub; int len, value; char *str; if (!json_object_object_get_ex(jpub, address, &jvalue)) return NULL; str = (char *)json_object_get_string(jvalue); len = strlen(str); pub = l_new(struct mesh_config_pub, 1); switch (len) { case 4: if (sscanf(str, "%04hx", &pub->addr) != 1) goto fail; break; case 32: if (!str2hex(str, len, pub->virt_addr, 16)) goto fail; pub->virt = true; break; default: goto fail; } if (!get_key_index(jpub, "index", &pub->idx)) goto fail; if (!get_int(jpub, "ttl", &value)) goto fail; pub->ttl = (uint8_t) value; if (!get_int(jpub, "period", &value)) goto fail; pub->period = value; if (!get_int(jpub, "credentials", &value)) goto fail; pub->credential = (uint8_t) value; if (!json_object_object_get_ex(jpub, retransmit, &jvalue)) goto fail; if (!get_int(jvalue, "count", &value)) goto fail; pub->cnt = (uint8_t) value; if (!get_int(jvalue, "interval", &value)) goto fail; pub->interval = (uint8_t) value; return pub; fail: l_free(pub); return NULL; } static bool parse_model_subscriptions(json_object *jsubs, struct mesh_config_model *mod) { struct mesh_config_sub *subs; int i, cnt; if (json_object_get_type(jsubs) != json_type_array) return NULL; cnt = json_object_array_length(jsubs); /* Allow empty array */ if (!cnt) return true; subs = l_new(struct mesh_config_sub, cnt); for (i = 0; i < cnt; ++i) { char *str; int len; json_object *jvalue; jvalue = json_object_array_get_idx(jsubs, i); if (!jvalue) goto fail; str = (char *)json_object_get_string(jvalue); len = strlen(str); switch (len) { case 4: if (sscanf(str, "%04hx", &subs[i].addr.grp) != 1) goto fail; break; case 32: if (!str2hex(str, len, subs[i].addr.label, 16)) goto fail; subs[i].virt = true; break; default: goto fail; } } mod->num_subs = cnt; mod->subs = subs; return true; fail: l_free(subs); return false; } static bool parse_models(json_object *jmodels, struct mesh_config_element *ele) { int i, num_models; num_models = json_object_array_length(jmodels); if (!num_models) return true; for (i = 0; i < num_models; ++i) { json_object *jmodel, *jarray, *jvalue; struct mesh_config_model *mod; uint32_t id; int len; char *str; jmodel = json_object_array_get_idx(jmodels, i); if (!jmodel) goto fail; mod = l_new(struct mesh_config_model, 1); l_queue_push_tail(ele->models, mod); if (!json_object_object_get_ex(jmodel, modelId, &jvalue)) goto fail; str = (char *)json_object_get_string(jvalue); len = strlen(str); if (len == 4) { if (sscanf(str, "%04x", &id) != 1) goto fail; } else if (len == 8) { if (sscanf(str, "%08x", &id) != 1) goto fail; mod->vendor = true; } else goto fail; mod->id = id; if (len == 8) mod->vendor = true; if (json_object_object_get_ex(jmodel, bind, &jarray)) { if (json_object_get_type(jarray) != json_type_array || !parse_bindings(jarray, mod)) goto fail; } if (json_object_object_get_ex(jmodel, pubEnabled, &jvalue)) mod->pub_enabled = json_object_get_boolean(jvalue); else mod->pub_enabled = true; if (json_object_object_get_ex(jmodel, subEnabled, &jvalue)) mod->sub_enabled = json_object_get_boolean(jvalue); else mod->sub_enabled = true; if (json_object_object_get_ex(jmodel, publish, &jvalue)) { mod->pub = parse_model_publication(jvalue); if (!mod->pub) goto fail; } if (json_object_object_get_ex(jmodel, subscribe, &jarray)) { if (!parse_model_subscriptions(jarray, mod)) goto fail; } } return true; fail: l_queue_destroy(ele->models, free_model); return false; } static bool parse_elements(json_object *jelems, struct mesh_config_node *node) { int i, num_ele; if (json_object_get_type(jelems) != json_type_array) return false; num_ele = json_object_array_length(jelems); if (!num_ele) /* Allow "empty" nodes */ return true; for (i = 0; i < num_ele; ++i) { json_object *jelement; json_object *jmodels; json_object *jvalue; struct mesh_config_element *ele; int index; char *str; jelement = json_object_array_get_idx(jelems, i); if (!jelement) goto fail; if (!get_int(jelement, "elementIndex", &index) || index > num_ele) goto fail; ele = l_new(struct mesh_config_element, 1); ele->index = index; ele->models = l_queue_new(); l_queue_push_tail(node->elements, ele); if (!json_object_object_get_ex(jelement, "location", &jvalue)) goto fail; str = (char *)json_object_get_string(jvalue); if (sscanf(str, "%04hx", &(ele->location)) != 1) goto fail; if (json_object_object_get_ex(jelement, models, &jmodels)) { if (json_object_get_type(jmodels) != json_type_array || !parse_models(jmodels, ele)) goto fail; } } return true; fail: l_queue_destroy(node->elements, free_element); node->elements = NULL; return false; } static int get_mode(json_object *jvalue) { const char *str; str = json_object_get_string(jvalue); if (!str) return 0xffffffff; if (!strncasecmp(str, disabled, strlen(disabled))) return MESH_MODE_DISABLED; if (!strncasecmp(str, enabled, strlen(enabled))) return MESH_MODE_ENABLED; if (!strncasecmp(str, unsupported, strlen(unsupported))) return MESH_MODE_UNSUPPORTED; return 0xffffffff; } static void parse_features(json_object *jconfig, struct mesh_config_node *node) { json_object *jvalue, *jrelay; int mode, count; uint16_t interval; if (json_object_object_get_ex(jconfig, "proxy", &jvalue)) { mode = get_mode(jvalue); if (mode <= MESH_MODE_UNSUPPORTED) node->modes.proxy = mode; } if (json_object_object_get_ex(jconfig, "friend", &jvalue)) { mode = get_mode(jvalue); if (mode <= MESH_MODE_UNSUPPORTED) node->modes.friend = mode; } if (json_object_object_get_ex(jconfig, "lowPower", &jvalue)) { mode = get_mode(jvalue); if (mode <= MESH_MODE_UNSUPPORTED) node->modes.lpn = mode; } if (json_object_object_get_ex(jconfig, "beacon", &jvalue)) { mode = get_mode(jvalue); if (mode <= MESH_MODE_UNSUPPORTED) node->modes.beacon = mode; } if (json_object_object_get_ex(jconfig, "mpb", &jvalue)) { mode = get_mode(jvalue); if (mode <= MESH_MODE_UNSUPPORTED) node->modes.mpb = mode; if (node->modes.mpb == MESH_MODE_ENABLED) { if (json_object_object_get_ex(jconfig, "mpbPeriod", &jvalue)) node->modes.mpb_period = json_object_get_int(jvalue); } } if (!json_object_object_get_ex(jconfig, "relay", &jrelay)) return; if (json_object_object_get_ex(jrelay, "mode", &jvalue)) { mode = get_mode(jvalue); if (mode <= MESH_MODE_UNSUPPORTED) node->modes.relay.state = mode; else return; } else return; if (!json_object_object_get_ex(jrelay, "count", &jvalue)) return; /* TODO: check range */ count = json_object_get_int(jvalue); node->modes.relay.cnt = count; if (!json_object_object_get_ex(jrelay, "interval", &jvalue)) return; /* TODO: check range */ interval = json_object_get_int(jvalue); node->modes.relay.interval = interval; } static bool parse_composition(json_object *jcomp, struct mesh_config_node *node) { json_object *jvalue; char *str; /* All the fields in node composition are mandatory */ if (!json_object_object_get_ex(jcomp, "cid", &jvalue)) return false; str = (char *)json_object_get_string(jvalue); if (sscanf(str, "%04hx", &node->cid) != 1) return false; if (!json_object_object_get_ex(jcomp, "pid", &jvalue)) return false; str = (char *)json_object_get_string(jvalue); if (sscanf(str, "%04hx", &node->pid) != 1) return false; if (!json_object_object_get_ex(jcomp, "vid", &jvalue)) return false; str = (char *)json_object_get_string(jvalue); if (sscanf(str, "%04hx", &node->vid) != 1) return false; if (!json_object_object_get_ex(jcomp, "crpl", &jvalue)) return false; str = (char *)json_object_get_string(jvalue); if (sscanf(str, "%04hx", &node->crpl) != 1) return false; return true; } static bool read_net_transmit(json_object *jobj, struct mesh_config_node *node) { json_object *jrtx, *jvalue; uint16_t interval; uint8_t cnt; if (!json_object_object_get_ex(jobj, retransmit, &jrtx)) return true; if (!json_object_object_get_ex(jrtx, "count", &jvalue)) return false; /* TODO: add range checking */ cnt = (uint8_t) json_object_get_int(jvalue); if (!json_object_object_get_ex(jrtx, "interval", &jvalue)) return false; interval = (uint16_t) json_object_get_int(jvalue); node->net_transmit = l_new(struct mesh_config_transmit, 1); node->net_transmit->count = cnt; node->net_transmit->interval = interval; return true; } static bool read_node(json_object *jnode, struct mesh_config_node *node) { json_object *jvalue; if (!read_iv_index(jnode, &node->iv_index, &node->iv_update)) { l_info("Failed to read IV index"); return false; } if (!read_token(jnode, node->token)) { l_info("Failed to read node token"); return false; } if (!read_device_key(jnode, node->dev_key)) { l_info("Failed to read node device key"); return false; } if (!parse_composition(jnode, node)) { l_info("Failed to parse local node composition"); return false; } parse_features(jnode, node); if (!read_unicast_address(jnode, &node->unicast)) { l_info("Failed to parse unicast address"); return false; } if (!read_default_ttl(jnode, &node->ttl)) { l_info("Failed to parse default ttl"); return false; } if (!read_seq_number(jnode, &node->seq_number)) { l_info("Failed to parse sequence number"); return false; } /* Check for required "elements" property */ if (!json_object_object_get_ex(jnode, elements, &jvalue)) return false; if (!read_net_transmit(jnode, node)) { l_info("Failed to read node net transmit parameters"); return false; } if (!read_net_keys(jnode, node)) { l_info("Failed to read net keys"); return false; } if (!read_app_keys(jnode, node)) { l_info("Failed to read app keys"); return false; } if (!read_comp_pages(jnode, node)) { l_info("Failed to read Composition Pages"); return false; } if (!parse_elements(jvalue, node)) { l_info("Failed to parse elements"); return false; } return true; } static bool write_uint16_hex(json_object *jobj, const char *desc, uint16_t value) { json_object *jstring; int ret; char buf[5]; ret = snprintf(buf, 5, "%4.4x", value); if (ret < 0) return false; jstring = json_object_new_string(buf); if (!jstring) return false; json_object_object_del(jobj, desc); json_object_object_add(jobj, desc, jstring); return true; } static bool write_uint32_hex(json_object *jobj, const char *desc, uint32_t val) { json_object *jstring; int ret; char buf[9]; ret = snprintf(buf, 9, "%8.8x", val); if (ret < 0) return false; jstring = json_object_new_string(buf); if (!jstring) return false; json_object_object_del(jobj, desc); json_object_object_add(jobj, desc, jstring); return true; } static const char *mode_to_string(int mode) { switch (mode) { case MESH_MODE_DISABLED: return disabled; case MESH_MODE_ENABLED: return enabled; default: return unsupported; } } static bool write_mode(json_object *jobj, const char *desc, int value) { json_object *jstring; jstring = json_object_new_string(mode_to_string(value)); if (!jstring) return false; json_object_object_del(jobj, desc); json_object_object_add(jobj, desc, jstring); return true; } bool mesh_config_write_mode(struct mesh_config *cfg, const char *keyword, int value) { if (!cfg || !write_mode(cfg->jnode, keyword, value)) return false; return save_config(cfg->jnode, cfg->node_dir_path); } bool mesh_config_write_mode_ex(struct mesh_config *cfg, const char *keyword, int value, bool save) { if (!cfg) return false; if (save) return mesh_config_write_mode(cfg, keyword, value); else return write_mode(cfg->jnode, keyword, value); } static bool write_relay_mode(json_object *jobj, uint8_t mode, uint8_t count, uint16_t interval) { json_object *jrelay; json_object_object_del(jobj, "relay"); jrelay = json_object_new_object(); if (!jrelay) return false; if (!write_mode(jrelay, "mode", mode)) goto fail; if (!write_int(jrelay, "count", count)) goto fail; if (!write_int(jrelay, "interval", interval)) goto fail; json_object_object_add(jobj, "relay", jrelay); return true; fail: json_object_put(jrelay); return false; } bool mesh_config_write_unicast(struct mesh_config *cfg, uint16_t unicast) { if (!cfg || !write_uint16_hex(cfg->jnode, unicastAddress, unicast)) return false; return save_config(cfg->jnode, cfg->node_dir_path); } bool mesh_config_write_relay_mode(struct mesh_config *cfg, uint8_t mode, uint8_t count, uint16_t interval) { if (!cfg || !write_relay_mode(cfg->jnode, mode, count, interval)) return false; return save_config(cfg->jnode, cfg->node_dir_path); } bool mesh_config_write_mpb(struct mesh_config *cfg, uint8_t mode, uint8_t period) { if (!cfg || !write_mode(cfg->jnode, "mpb", mode)) return false; if (mode) { if (!write_int(cfg->jnode, "mpbPeriod", period)) return false; } return save_config(cfg->jnode, cfg->node_dir_path); } bool mesh_config_write_net_transmit(struct mesh_config *cfg, uint8_t cnt, uint16_t interval) { json_object *jnode, *jrtx; if (!cfg) return false; jnode = cfg->jnode; jrtx = json_object_new_object(); if (!jrtx) return false; if (!write_int(jrtx, "count", cnt)) goto fail; if (!write_int(jrtx, "interval", interval)) goto fail; json_object_object_del(jnode, retransmit); json_object_object_add(jnode, retransmit, jrtx); return save_config(cfg->jnode, cfg->node_dir_path); fail: json_object_put(jrtx); return false; } bool mesh_config_write_iv_index(struct mesh_config *cfg, uint32_t idx, bool update) { json_object *jnode; int tmp = update ? 1 : 0; if (!cfg) return false; jnode = cfg->jnode; if (!write_int(jnode, "IVindex", idx)) return false; if (!write_int(jnode, "IVupdate", tmp)) return false; return save_config(jnode, cfg->node_dir_path); } static void add_model(void *a, void *b) { struct mesh_config_model *mod = a; json_object *jmodels = b, *jmodel, *jval; bool result; jmodel = json_object_new_object(); if (!jmodel) return; result = (mod->vendor) ? write_uint32_hex(jmodel, modelId, mod->id) : write_uint16_hex(jmodel, modelId, (uint16_t) mod->id); if (!result) { json_object_put(jmodel); return; } jval = json_object_new_boolean(mod->sub_enabled); json_object_object_add(jmodel, subEnabled, jval); jval = json_object_new_boolean(mod->pub_enabled); json_object_object_add(jmodel, pubEnabled, jval); json_object_array_add(jmodels, jmodel); } /* Add unprovisioned node (local) */ static struct mesh_config *create_config(const char *cfg_path, const uint8_t uuid[16], struct mesh_config_node *node) { struct mesh_config_modes *modes = &node->modes; const struct l_queue_entry *entry; json_object *jnode, *jelems; struct mesh_config *cfg; if (!cfg_path || !node) return NULL; jnode = json_object_new_object(); /* CID, PID, VID, crpl */ if (!write_uint16_hex(jnode, "cid", node->cid)) return NULL; if (!write_uint16_hex(jnode, "pid", node->pid)) return NULL; if (!write_uint16_hex(jnode, "vid", node->vid)) return NULL; if (!write_uint16_hex(jnode, "crpl", node->crpl)) return NULL; /* Features: relay, LPN, friend, proxy*/ if (!write_relay_mode(jnode, modes->relay.state, modes->relay.cnt, modes->relay.interval)) return NULL; if (!write_mode(jnode, "lowPower", modes->lpn)) return NULL; if (!write_mode(jnode, "friend", modes->friend)) return NULL; if (!write_mode(jnode, "proxy", modes->proxy)) return NULL; /* Beaconing state */ if (!write_mode(jnode, "beacon", modes->beacon)) return NULL; if (!write_mode(jnode, "mpb", modes->mpb)) return NULL; if (modes->mpb) { if (!write_int(jnode, "mpbPeriod", modes->mpb_period)) return NULL; } /* Sequence number */ json_object_object_add(jnode, sequenceNumber, json_object_new_int(node->seq_number)); /* Default TTL */ json_object_object_add(jnode, defaultTTL, json_object_new_int(node->ttl)); /* Elements */ jelems = json_object_new_array(); if (!jelems) return NULL; entry = l_queue_get_entries(node->elements); for (; entry; entry = entry->next) { struct mesh_config_element *ele = entry->data; json_object *jelement, *jmodels; jelement = json_object_new_object(); if (!jelement) goto fail; json_object_array_add(jelems, jelement); if (!write_int(jelement, "elementIndex", ele->index)) goto fail; if (!write_uint16_hex(jelement, "location", ele->location)) goto fail; /* Models */ if (l_queue_isempty(ele->models)) continue; jmodels = json_object_new_array(); if (!jmodels) goto fail; json_object_object_add(jelement, models, jmodels); l_queue_foreach(ele->models, add_model, jmodels); } json_object_object_add(jnode, elements, jelems); cfg = l_new(struct mesh_config, 1); cfg->jnode = jnode; memcpy(cfg->uuid, uuid, 16); cfg->node_dir_path = l_strdup(cfg_path); cfg->write_seq = node->seq_number; cfg->idles = l_queue_new(); gettimeofday(&cfg->write_time, NULL); return cfg; fail: json_object_put(jelems); return NULL; } void mesh_config_reset(struct mesh_config *cfg, struct mesh_config_node *node) { json_object *jelems; const struct l_queue_entry *entry; if (!cfg || !cfg->jnode) return; /* TODO: Recreate Element Array */ jelems = json_object_new_array(); if (!jelems) return; entry = l_queue_get_entries(node->elements); for (; entry; entry = entry->next) { struct mesh_config_element *ele = entry->data; json_object *jelement, *jmodels; jelement = json_object_new_object(); if (!jelement) { json_object_put(jelems); return; } write_int(jelement, "elementIndex", ele->index); write_uint16_hex(jelement, "location", ele->location); json_object_array_add(jelems, jelement); /* Models */ if (l_queue_isempty(ele->models)) continue; jmodels = json_object_new_array(); if (!jmodels) { json_object_put(jelems); return; } json_object_object_add(jelement, models, jmodels); l_queue_foreach(ele->models, add_model, jmodels); } /* Replace element array */ json_object_object_del(cfg->jnode, elements); json_object_object_add(cfg->jnode, elements, jelems); } struct mesh_config *mesh_config_create(const char *cfgdir_name, const uint8_t uuid[16], struct mesh_config_node *db_node) { char uuid_buf[33]; char name_buf[PATH_MAX]; struct mesh_config *cfg; int ret; if (!hex2str((uint8_t *) uuid, 16, uuid_buf, sizeof(uuid_buf))) return NULL; ret = snprintf(name_buf, PATH_MAX, "%s/%s", cfgdir_name, uuid_buf); if (ret < 0) return NULL; /* Create a new directory and node.json file */ if (mkdir(name_buf, 0755) != 0) return NULL; ret = snprintf(name_buf, PATH_MAX, "%s/%s%s", cfgdir_name, uuid_buf, cfgnode_name); if (ret < 0) return NULL; l_debug("New node config %s", name_buf); cfg = create_config(name_buf, uuid, db_node); if (!cfg) return NULL; if (!mesh_config_save(cfg, true, NULL, NULL)) { mesh_config_release(cfg); return NULL; } return cfg; } static void finish_key_refresh(json_object *jobj, uint16_t net_idx) { json_object *jarray; int i, len; /* Clean up all the bound appkeys */ if (!json_object_object_get_ex(jobj, appKeys, &jarray)) return; len = json_object_array_length(jarray); for (i = 0; i < len; ++i) { json_object *jentry; uint16_t idx; jentry = json_object_array_get_idx(jarray, i); if (!get_key_index(jentry, boundNetKey, &idx)) continue; if (idx != net_idx) continue; json_object_object_del(jentry, "oldKey"); if (!get_key_index(jentry, "index", &idx)) continue; } } bool mesh_config_net_key_set_phase(struct mesh_config *cfg, uint16_t idx, uint8_t phase) { json_object *jnode, *jarray, *jentry = NULL; if (!cfg) return false; jnode = cfg->jnode; if (json_object_object_get_ex(jnode, netKeys, &jarray)) jentry = get_key_object(jarray, idx); if (!jentry) return false; json_object_object_del(jentry, keyRefresh); json_object_object_add(jentry, keyRefresh, json_object_new_int(phase)); if (phase == KEY_REFRESH_PHASE_NONE) { json_object_object_del(jentry, "oldKey"); finish_key_refresh(jnode, idx); } return save_config(jnode, cfg->node_dir_path); } bool mesh_config_model_pub_add(struct mesh_config *cfg, uint16_t ele_addr, uint32_t mod_id, bool vendor, struct mesh_config_pub *pub) { json_object *jnode, *jmodel, *jpub, *jrtx; bool res; int ele_idx; if (!cfg) return false; jnode = cfg->jnode; ele_idx = get_element_index(jnode, ele_addr); if (ele_idx < 0) return false; jmodel = get_element_model(jnode, ele_idx, mod_id, vendor); if (!jmodel) return false; json_object_object_del(jmodel, publish); jpub = json_object_new_object(); if (!jpub) return false; if (pub->virt) res = add_key_value(jpub, address, pub->virt_addr); else res = write_uint16_hex(jpub, address, pub->addr); if (!res) goto fail; if (!write_int(jpub, "index", pub->idx)) goto fail; if (!write_int(jpub, "ttl", pub->ttl)) goto fail; if (!write_int(jpub, "period", pub->period)) goto fail; if (!write_int(jpub, "credentials", pub->credential ? 1 : 0)) goto fail; jrtx = json_object_new_object(); if (!jrtx) goto fail; if (!write_int(jrtx, "count", pub->cnt)) goto fail; if (!write_int(jrtx, "interval", pub->interval)) goto fail; json_object_object_add(jpub, retransmit, jrtx); json_object_object_add(jmodel, publish, jpub); return save_config(jnode, cfg->node_dir_path); fail: json_object_put(jpub); return false; } static bool delete_model_property(json_object *jnode, uint16_t ele_addr, uint32_t mod_id, bool vendor, const char *keyword) { json_object *jmodel; int ele_idx; ele_idx = get_element_index(jnode, ele_addr); if (ele_idx < 0) return false; jmodel = get_element_model(jnode, ele_idx, mod_id, vendor); if (!jmodel) return false; json_object_object_del(jmodel, keyword); return true; } bool mesh_config_model_pub_del(struct mesh_config *cfg, uint16_t addr, uint32_t mod_id, bool vendor) { if (!cfg || !delete_model_property(cfg->jnode, addr, mod_id, vendor, publish)) return false; return save_config(cfg->jnode, cfg->node_dir_path); } static bool del_page(json_object *jarray, uint8_t page) { char buf[3]; int i, len, ret; if (!jarray) return false; ret = snprintf(buf, 3, "%2.2x", page); if (ret < 0) return false; len = json_object_array_length(jarray); for (i = 0; i < len; i++) { json_object *jentry; char *str; jentry = json_object_array_get_idx(jarray, i); str = (char *)json_object_get_string(jentry); /* Delete matching page */ if (!memcmp(str, buf, 2)) { json_object_array_del_idx(jarray, i, 1); break; } } return true; } void mesh_config_comp_page_del(struct mesh_config *cfg, uint8_t page) { json_object *jnode, *jarray = NULL; if (!cfg) return; jnode = cfg->jnode; json_object_object_get_ex(jnode, "pages", &jarray); if (del_page(jarray, page)) save_config(jnode, cfg->node_dir_path); } bool mesh_config_comp_page_add(struct mesh_config *cfg, uint8_t page, uint8_t *data, uint16_t size) { json_object *jnode, *jstring, *jarray = NULL; char *buf; int len, ret; if (!cfg) return false; jnode = cfg->jnode; json_object_object_get_ex(jnode, "pages", &jarray); len = (size * 2) + 3; buf = l_malloc(len); ret = snprintf(buf, len, "%2.2x", page); if (ret < 0) { l_free(buf); return false; } hex2str(data, size, buf + 2, len - 2); if (jarray && jarray_has_string(jarray, buf, len)) { l_free(buf); return true; } else if (!jarray) { jarray = json_object_new_array(); json_object_object_add(jnode, "pages", jarray); } else del_page(jarray, page); jstring = json_object_new_string(buf); json_object_array_add(jarray, jstring); l_free(buf); return save_config(jnode, cfg->node_dir_path); } bool mesh_config_model_sub_add(struct mesh_config *cfg, uint16_t ele_addr, uint32_t mod_id, bool vendor, struct mesh_config_sub *sub) { json_object *jnode, *jmodel, *jstring, *jarray = NULL; int ele_idx, len; char buf[33]; if (!cfg) return false; jnode = cfg->jnode; ele_idx = get_element_index(jnode, ele_addr); if (ele_idx < 0) return false; jmodel = get_element_model(jnode, ele_idx, mod_id, vendor); if (!jmodel) return false; if (!sub->virt) { len = snprintf(buf, 5, "%4.4x", sub->addr.grp); if (len < 0) return false; } else { hex2str(sub->addr.label, 16, buf, 33); len = 32; } json_object_object_get_ex(jmodel, subscribe, &jarray); if (jarray && jarray_has_string(jarray, buf, len)) return true; jstring = json_object_new_string(buf); if (!jstring) return false; if (!jarray) { jarray = json_object_new_array(); if (!jarray) { json_object_put(jstring); return false; } json_object_object_add(jmodel, subscribe, jarray); } json_object_array_add(jarray, jstring); return save_config(jnode, cfg->node_dir_path); } bool mesh_config_model_sub_del(struct mesh_config *cfg, uint16_t ele_addr, uint32_t mod_id, bool vendor, struct mesh_config_sub *sub) { json_object *jnode, *jmodel, *jarray; char buf[33]; int len, ele_idx; if (!cfg) return false; jnode = cfg->jnode; ele_idx = get_element_index(jnode, ele_addr); if (ele_idx < 0) return false; jmodel = get_element_model(jnode, ele_idx, mod_id, vendor); if (!jmodel) return false; if (!json_object_object_get_ex(jmodel, subscribe, &jarray)) return true; if (!sub->virt) { len = snprintf(buf, 5, "%4.4x", sub->addr.grp); if (len < 0) return false; } else { hex2str(sub->addr.label, 16, buf, 33); len = 32; } jarray_string_del(jarray, buf, len); if (!json_object_array_length(jarray)) json_object_object_del(jmodel, subscribe); return save_config(jnode, cfg->node_dir_path); } bool mesh_config_model_sub_del_all(struct mesh_config *cfg, uint16_t addr, uint32_t mod_id, bool vendor) { if (!cfg || !delete_model_property(cfg->jnode, addr, mod_id, vendor, subscribe)) return false; return save_config(cfg->jnode, cfg->node_dir_path); } bool mesh_config_model_pub_enable(struct mesh_config *cfg, uint16_t ele_addr, uint32_t mod_id, bool vendor, bool enable) { json_object *jmodel, *jval; int ele_idx; if (!cfg) return false; ele_idx = get_element_index(cfg->jnode, ele_addr); if (ele_idx < 0) return false; jmodel = get_element_model(cfg->jnode, ele_idx, mod_id, vendor); if (!jmodel) return false; json_object_object_del(jmodel, "pubDisabled"); jval = json_object_new_boolean(!enable); json_object_object_add(jmodel, "pubDisabled", jval); if (!enable) json_object_object_del(jmodel, publish); return save_config(cfg->jnode, cfg->node_dir_path); } bool mesh_config_model_sub_enable(struct mesh_config *cfg, uint16_t ele_addr, uint32_t mod_id, bool vendor, bool enable) { json_object *jmodel, *jval; int ele_idx; if (!cfg) return false; ele_idx = get_element_index(cfg->jnode, ele_addr); if (ele_idx < 0) return false; jmodel = get_element_model(cfg->jnode, ele_idx, mod_id, vendor); if (!jmodel) return false; json_object_object_del(jmodel, subEnabled); jval = json_object_new_boolean(enable); json_object_object_add(jmodel, subEnabled, jval); if (!enable) json_object_object_del(jmodel, subscribe); return save_config(cfg->jnode, cfg->node_dir_path); } bool mesh_config_write_seq_number(struct mesh_config *cfg, uint32_t seq, bool cache) { int value = 0; uint32_t cached = 0; if (!cfg) return false; if (!cache) { if (!write_int(cfg->jnode, sequenceNumber, seq)) return false; return mesh_config_save(cfg, true, NULL, NULL); } /* If resetting seq to Zero, make sure cached value reset as well */ if (seq && get_int(cfg->jnode, sequenceNumber, &value)) cached = (uint32_t)value; /* * When sequence number approaches value stored on disk, calculate * average time between sequence number updates, then overcommit the * sequence number by MIN_SEQ_CACHE_TIME seconds worth of traffic or * MIN_SEQ_CACHE_VALUE (whichever is greater) to avoid frequent writes * to disk and to protect against crashes. * * The real value will be saved when daemon shuts down properly. */ if (seq + MIN_SEQ_CACHE_TRIGGER >= cached) { struct timeval now; struct timeval elapsed; uint64_t elapsed_ms; gettimeofday(&now, NULL); timersub(&now, &cfg->write_time, &elapsed); elapsed_ms = elapsed.tv_sec * 1000 + elapsed.tv_usec / 1000; /* * If time since last write is zero, this means that * idle_save_config is already pending, so we don't need to do * anything. */ if (!elapsed_ms) return true; cached = seq + (seq - cfg->write_seq) * 1000 * MIN_SEQ_CACHE_TIME / elapsed_ms; if (cached < seq + MIN_SEQ_CACHE_VALUE) cached = seq + MIN_SEQ_CACHE_VALUE; /* Cap the seq cache maximum to fixed out-of-range value. * If daemon restarts with out-of-range value, no packets * are to be sent until IV Update procedure completes. */ if (cached > SEQ_MASK) cached = SEQ_MASK + 1; cfg->write_seq = seq; /* Don't rewrite NVM storage if unchanged */ if (value == (int) cached) return true; l_debug("Seq Cache: %d -> %d", seq, cached); if (!write_int(cfg->jnode, sequenceNumber, cached)) return false; return mesh_config_save(cfg, false, NULL, NULL); } return true; } bool mesh_config_write_ttl(struct mesh_config *cfg, uint8_t ttl) { if (!cfg || !write_int(cfg->jnode, defaultTTL, ttl)) return false; return save_config(cfg->jnode, cfg->node_dir_path); } bool mesh_config_update_company_id(struct mesh_config *cfg, uint16_t cid) { if (!cfg || !write_uint16_hex(cfg->jnode, "cid", cid)) return false; return save_config(cfg->jnode, cfg->node_dir_path); } bool mesh_config_update_product_id(struct mesh_config *cfg, uint16_t pid) { if (!cfg || !write_uint16_hex(cfg->jnode, "pid", pid)) return false; return save_config(cfg->jnode, cfg->node_dir_path); } bool mesh_config_update_version_id(struct mesh_config *cfg, uint16_t vid) { if (!cfg || !write_uint16_hex(cfg->jnode, "vid", vid)) return false; return save_config(cfg->jnode, cfg->node_dir_path); } bool mesh_config_update_crpl(struct mesh_config *cfg, uint16_t crpl) { if (!cfg || !write_uint16_hex(cfg->jnode, "crpl", crpl)) return false; return save_config(cfg->jnode, cfg->node_dir_path); } static bool load_node(const char *fname, const uint8_t uuid[16], mesh_config_node_func_t cb, void *user_data) { int fd; char *str; struct stat st; ssize_t sz; bool result = false; json_object *jnode; struct mesh_config_node node; if (!cb) { l_info("Node read callback is required"); return false; } l_info("Loading configuration from %s", fname); fd = open(fname, O_RDONLY); if (fd < 0) return false; if (fstat(fd, &st) == -1) { close(fd); return false; } str = (char *) l_new(char, st.st_size + 1); if (!str) { close(fd); return false; } sz = read(fd, str, st.st_size); if (sz != st.st_size) { l_error("Failed to read configuration file %s", fname); goto done; } jnode = json_tokener_parse(str); if (!jnode) goto done; memset(&node, 0, sizeof(node)); node.elements = l_queue_new(); node.netkeys = l_queue_new(); node.appkeys = l_queue_new(); node.pages = l_queue_new(); result = read_node(jnode, &node); if (result) { struct mesh_config *cfg = l_new(struct mesh_config, 1); cfg->jnode = jnode; memcpy(cfg->uuid, uuid, 16); cfg->node_dir_path = l_strdup(fname); cfg->write_seq = node.seq_number; cfg->idles = l_queue_new(); gettimeofday(&cfg->write_time, NULL); result = cb(&node, uuid, cfg, user_data); if (!result) { l_free(cfg->idles); l_free(cfg->node_dir_path); l_free(cfg); } } /* Done with the node: free resources */ l_free(node.net_transmit); l_queue_destroy(node.netkeys, l_free); l_queue_destroy(node.appkeys, l_free); l_queue_destroy(node.pages, l_free); l_queue_destroy(node.elements, free_element); if (!result) json_object_put(jnode); done: close(fd); if (str) l_free(str); return result; } static void release_idle(void *data) { struct l_idle *idle = data; l_idle_remove(idle); } void mesh_config_release(struct mesh_config *cfg) { if (!cfg) return; l_queue_destroy(cfg->idles, release_idle); l_free(cfg->node_dir_path); json_object_put(cfg->jnode); l_free(cfg); } static void idle_save_config(struct l_idle *idle, void *user_data) { struct write_info *info = user_data; char *fname_tmp, *fname_bak, *fname_cfg; bool result = false; fname_cfg = info->cfg->node_dir_path; fname_tmp = l_strdup_printf("%s%s", fname_cfg, tmp_ext); fname_bak = l_strdup_printf("%s%s", fname_cfg, bak_ext); remove(fname_tmp); result = save_config(info->cfg->jnode, fname_tmp); if (result) { remove(fname_bak); if (rename(fname_cfg, fname_bak) < 0 || rename(fname_tmp, fname_cfg) < 0) result = false; } remove(fname_tmp); l_free(fname_tmp); l_free(fname_bak); gettimeofday(&info->cfg->write_time, NULL); if (info->cb) info->cb(info->user_data, result); if (idle) { l_queue_remove(info->cfg->idles, idle); l_idle_remove(idle); } l_free(info); } bool mesh_config_save(struct mesh_config *cfg, bool no_wait, mesh_config_status_func_t cb, void *user_data) { struct write_info *info; if (!cfg) return false; info = l_new(struct write_info, 1); info->cfg = cfg; info->cb = cb; info->user_data = user_data; if (no_wait) { idle_save_config(NULL, info); } else { struct l_idle *idle; idle = l_idle_create(idle_save_config, info, NULL); l_queue_push_tail(cfg->idles, idle); } return true; } bool mesh_config_load_nodes(const char *cfgdir_name, mesh_config_node_func_t cb, void *user_data) { DIR *cfgdir; struct dirent *entry; size_t path_len = strlen(cfgdir_name) + strlen(cfgnode_name) + strlen(bak_ext); create_dir(cfgdir_name); cfgdir = opendir(cfgdir_name); if (!cfgdir) { l_error("Failed to open mesh node storage directory: %s", cfgdir_name); return false; } while ((entry = readdir(cfgdir)) != NULL) { char *dirname, *fname, *bak; uint8_t uuid[16]; size_t node_len; if (entry->d_type != DT_DIR) continue; /* Check path length */ node_len = strlen(entry->d_name); if (path_len + node_len + 1 >= PATH_MAX) continue; if (!str2hex(entry->d_name, node_len, uuid, sizeof(uuid))) continue; dirname = l_strdup_printf("%s/%s", cfgdir_name, entry->d_name); fname = l_strdup_printf("%s%s", dirname, cfgnode_name); if (!load_node(fname, uuid, cb, user_data)) { /* Fall-back to Backup version */ bak = l_strdup_printf("%s%s", fname, bak_ext); if (load_node(bak, uuid, cb, user_data)) { remove(fname); rename(bak, fname); } l_free(bak); } l_free(fname); l_free(dirname); } closedir(cfgdir); return true; } void mesh_config_destroy_nvm(struct mesh_config *cfg) { char *node_dir; const char *node_name; char uuid[33]; if (!cfg) return; node_dir = dirname(cfg->node_dir_path); l_debug("Delete node config %s", node_dir); if (!hex2str(cfg->uuid, 16, uuid, sizeof(uuid))) return; node_name = mesh_basename(node_dir); /* Make sure path name of node follows expected guidelines */ if (strcmp(node_name, uuid)) return; del_path(node_dir); } bluez-5.82/mesh/PaxHeaders/model.h0000644000000000000000000000005014015011623014044 xustar0020 atime=1743516868 20 ctime=1743591282 bluez-5.82/mesh/model.h0000644000000000000000000001121214015011623013522 0ustar00rootroot/* SPDX-License-Identifier: LGPL-2.1-or-later */ /* * * BlueZ - Bluetooth protocol stack for Linux * * Copyright (C) 2018-2020 Intel Corporation. All rights reserved. * * */ struct mesh_model; #define MAX_MODEL_BINDINGS 10 #define MAX_MODEL_SUBS 10 #define ACTION_ADD 1 #define ACTION_UPDATE 2 #define ACTION_DELETE 3 /* For internal representation of SIG defined models */ #define SIG_VENDOR 0xFFFF #define IS_VENDOR(x) ((x) < ((uint32_t)(SIG_VENDOR) << 16)) #define SET_ID(v, m) ((((uint32_t) (v)) << 16) | (m)) #define MODEL_ID(x) ((x) & ~VENDOR_ID_MASK) #define VENDOR_ID(x) ((x) >> 16) struct mesh_virtual; struct mesh_model_pub { struct mesh_virtual *virt; uint16_t addr; uint16_t idx; struct { uint16_t interval; uint8_t cnt; } rtx; uint8_t ttl; uint8_t credential; uint8_t period; }; typedef void (*mesh_model_unregister)(void *user_data); typedef bool (*mesh_model_recv_cb)(uint16_t src, uint16_t unicast, uint16_t app_idx, uint16_t net_idx, const uint8_t *data, uint16_t len, const void *user_data); typedef int (*mesh_model_bind_cb)(uint16_t app_idx, int action); typedef int (*mesh_model_pub_cb)(struct mesh_model_pub *pub); typedef int (*mesh_model_sub_cb)(uint16_t sub_addr, int action); struct mesh_model_ops { mesh_model_unregister unregister; mesh_model_recv_cb recv; mesh_model_bind_cb bind; mesh_model_pub_cb pub; mesh_model_sub_cb sub; }; bool mesh_model_add(struct mesh_node *node, struct l_queue *mods, uint32_t id, struct l_dbus_message_iter *opts); void mesh_model_free(void *data); bool mesh_model_register(struct mesh_node *node, uint8_t ele_idx, uint32_t id, const struct mesh_model_ops *cbs, void *user_data); bool mesh_model_add_from_storage(struct mesh_node *node, uint8_t ele_idx, struct l_queue *mods, struct l_queue *db_mods); void mesh_model_convert_to_storage(struct l_queue *db_mods, struct l_queue *mods); struct mesh_model_pub *mesh_model_pub_get(struct mesh_node *node, uint16_t ele_addr, uint32_t id, int *status); int mesh_model_pub_set(struct mesh_node *node, uint16_t ele_addr, uint32_t id, const uint8_t *addr, uint16_t idx, bool cred_flag, uint8_t ttl, uint8_t period, uint8_t rtx_cnt, uint16_t rtx_interval, bool is_virt, uint16_t *dst); int mesh_model_binding_add(struct mesh_node *node, uint16_t ele_addr, uint32_t id, uint16_t idx); int mesh_model_binding_del(struct mesh_node *node, uint16_t ele_addr, uint32_t id, uint16_t idx); int mesh_model_get_bindings(struct mesh_node *node, uint16_t ele_addr, uint32_t id, uint8_t *buf, uint16_t buf_sz, uint16_t *len); int mesh_model_sub_add(struct mesh_node *node, uint16_t ele_addr, uint32_t id, uint16_t grp); int mesh_model_virt_sub_add(struct mesh_node *node, uint16_t ele_addr, uint32_t id, const uint8_t *label, uint16_t *addr); int mesh_model_sub_del(struct mesh_node *node, uint16_t ele_addr, uint32_t id, uint16_t grp); int mesh_model_virt_sub_del(struct mesh_node *node, uint16_t ele_addr, uint32_t id, const uint8_t *label, uint16_t *addr); int mesh_model_sub_del_all(struct mesh_node *node, uint16_t ele_addr, uint32_t id); int mesh_model_sub_ovrt(struct mesh_node *node, uint16_t ele_addr, uint32_t id, uint16_t addr); int mesh_model_virt_sub_ovrt(struct mesh_node *node, uint16_t ele_addr, uint32_t id, const uint8_t *label, uint16_t *addr); int mesh_model_sub_get(struct mesh_node *node, uint16_t ele_addr, uint32_t id, uint8_t *buf, uint16_t buf_size, uint16_t *size); uint16_t mesh_model_cfg_blk(uint8_t *pkt); bool mesh_model_send(struct mesh_node *node, uint16_t src, uint16_t dst, uint16_t app_idx, uint16_t net_idx, uint8_t ttl, bool segmented, uint16_t len, const void *data); int mesh_model_publish(struct mesh_node *node, uint32_t id, uint16_t src, bool segmented, uint16_t len, const void *data); bool mesh_model_rx(struct mesh_node *node, bool szmict, uint32_t seq0, uint32_t iv_index, uint16_t net_idx, uint16_t src, uint16_t dst, uint8_t key_aid, const uint8_t *data, uint16_t size); void mesh_model_app_key_delete(struct mesh_node *node, uint16_t ele_idx, struct l_queue *models, uint16_t app_idx); uint16_t mesh_model_opcode_set(uint32_t opcode, uint8_t *buf); bool mesh_model_opcode_get(const uint8_t *buf, uint16_t size, uint32_t *opcode, uint16_t *n); void mesh_model_build_config(void *model, void *msg_builder); void mesh_model_update_opts(struct mesh_node *node, uint8_t ele_idx, struct l_queue *curr, struct l_queue *updated); uint16_t mesh_model_generate_composition(struct l_queue *mods, uint16_t buf_sz, uint8_t *buf); void mesh_model_init(void); void mesh_model_cleanup(void); bluez-5.82/mesh/PaxHeaders/mesh-config.h0000644000000000000000000000005014447506754015172 xustar0020 atime=1743516868 20 ctime=1743591282 bluez-5.82/mesh/mesh-config.h0000644000000000000000000001451114447506754014655 0ustar00rootroot/* SPDX-License-Identifier: LGPL-2.1-or-later */ /* * * BlueZ - Bluetooth protocol stack for Linux * * Copyright (C) 2018-2019 Intel Corporation. All rights reserved. * * */ #define MIN_COMP_SIZE 14 struct mesh_config; struct mesh_config_sub { bool virt; union { uint16_t grp; uint8_t label[16]; } addr; }; struct mesh_config_pub { bool virt; uint32_t period; uint16_t addr; uint16_t idx; uint16_t interval; uint8_t ttl; uint8_t credential; uint8_t cnt; uint8_t virt_addr[16]; }; struct mesh_config_model { struct mesh_config_sub *subs; struct mesh_config_pub *pub; uint16_t *bindings; uint32_t id; bool vendor; bool sub_enabled; bool pub_enabled; uint32_t num_bindings; uint32_t num_subs; }; struct mesh_config_element { struct l_queue *models; uint16_t location; uint8_t index; }; struct mesh_config_modes { struct { uint16_t interval; uint8_t cnt; uint8_t state; } relay; uint8_t lpn; uint8_t friend; uint8_t proxy; uint8_t beacon; uint8_t mpb; uint8_t mpb_period; }; struct mesh_config_netkey { uint16_t idx; uint8_t key[16]; uint8_t new_key[16]; uint8_t phase; }; struct mesh_config_appkey { uint16_t net_idx; uint16_t app_idx; uint8_t key[16]; uint8_t new_key[16]; }; struct mesh_config_transmit { uint16_t interval; uint8_t count; }; struct mesh_config_comp_page { uint16_t len; uint8_t page_num; uint8_t data[]; }; struct mesh_config_node { struct l_queue *elements; struct l_queue *netkeys; struct l_queue *appkeys; struct l_queue *pages; uint32_t seq_number; uint32_t iv_index; bool iv_update; uint16_t cid; uint16_t pid; uint16_t vid; uint16_t crpl; uint16_t unicast; struct mesh_config_transmit *net_transmit; struct mesh_config_modes modes; uint8_t ttl; uint8_t dev_key[16]; uint8_t token[8]; }; typedef void (*mesh_config_status_func_t)(void *user_data, bool result); typedef bool (*mesh_config_node_func_t)(struct mesh_config_node *node, const uint8_t uuid[16], struct mesh_config *cfg, void *user_data); bool mesh_config_load_nodes(const char *cfgdir_name, mesh_config_node_func_t cb, void *user_data); void mesh_config_release(struct mesh_config *cfg); void mesh_config_destroy_nvm(struct mesh_config *cfg); bool mesh_config_save(struct mesh_config *cfg, bool no_wait, mesh_config_status_func_t cb, void *user_data); void mesh_config_reset(struct mesh_config *cfg, struct mesh_config_node *node); struct mesh_config *mesh_config_create(const char *cfgdir_name, const uint8_t uuid[16], struct mesh_config_node *node); bool mesh_config_write_net_transmit(struct mesh_config *cfg, uint8_t cnt, uint16_t interval); bool mesh_config_write_device_key(struct mesh_config *cfg, uint8_t *key); bool mesh_config_write_candidate(struct mesh_config *cfg, uint8_t *key); bool mesh_config_read_candidate(struct mesh_config *cfg, uint8_t *key); bool mesh_config_finalize_candidate(struct mesh_config *cfg); bool mesh_config_write_token(struct mesh_config *cfg, uint8_t *token); bool mesh_config_write_network_key(struct mesh_config *cfg, uint16_t idx, uint8_t *key, uint8_t *new_key, int phase); bool mesh_config_write_app_key(struct mesh_config *cfg, uint16_t net_idx, uint16_t app_idx, uint8_t *key, uint8_t *new_key); bool mesh_config_write_seq_number(struct mesh_config *cfg, uint32_t seq, bool cache); bool mesh_config_write_unicast(struct mesh_config *cfg, uint16_t unicast); bool mesh_config_write_relay_mode(struct mesh_config *cfg, uint8_t mode, uint8_t count, uint16_t interval); bool mesh_config_write_mpb(struct mesh_config *cfg, uint8_t mode, uint8_t period); bool mesh_config_write_ttl(struct mesh_config *cfg, uint8_t ttl); bool mesh_config_write_mode(struct mesh_config *cfg, const char *keyword, int value); bool mesh_config_write_mode_ex(struct mesh_config *cfg, const char *keyword, int value, bool save); bool mesh_config_comp_page_add(struct mesh_config *cfg, uint8_t page, uint8_t *data, uint16_t size); void mesh_config_comp_page_del(struct mesh_config *cfg, uint8_t page); bool mesh_config_model_binding_add(struct mesh_config *cfg, uint16_t ele_addr, uint32_t mod_id, bool vendor, uint16_t app_idx); bool mesh_config_model_binding_del(struct mesh_config *cfg, uint16_t ele_addr, uint32_t mod_id, bool vendor, uint16_t app_idx); bool mesh_config_model_pub_add(struct mesh_config *cfg, uint16_t ele_addr, uint32_t mod_id, bool vendor, struct mesh_config_pub *pub); bool mesh_config_model_pub_del(struct mesh_config *cfg, uint16_t ele_addr, uint32_t mod_id, bool vendor); bool mesh_config_model_sub_add(struct mesh_config *cfg, uint16_t ele_addr, uint32_t mod_id, bool vendor, struct mesh_config_sub *sub); bool mesh_config_model_sub_del(struct mesh_config *cfg, uint16_t ele_addr, uint32_t mod_id, bool vendor, struct mesh_config_sub *sub); bool mesh_config_model_sub_del_all(struct mesh_config *cfg, uint16_t ele_addr, uint32_t mod_id, bool vendor); bool mesh_config_model_pub_enable(struct mesh_config *cfg, uint16_t ele_addr, uint32_t mod_id, bool vendor, bool enable); bool mesh_config_model_sub_enable(struct mesh_config *cfg, uint16_t ele_addr, uint32_t mod_id, bool vendor, bool enable); bool mesh_config_app_key_add(struct mesh_config *cfg, uint16_t net_idx, uint16_t app_idx, const uint8_t key[16]); bool mesh_config_app_key_update(struct mesh_config *cfg, uint16_t app_idx, const uint8_t key[16]); bool mesh_config_app_key_del(struct mesh_config *cfg, uint16_t net_idx, uint16_t idx); bool mesh_config_net_key_add(struct mesh_config *cfg, uint16_t net_idx, const uint8_t key[16]); bool mesh_config_net_key_update(struct mesh_config *cfg, uint16_t idx, const uint8_t key[16]); bool mesh_config_net_key_del(struct mesh_config *cfg, uint16_t net_idx); bool mesh_config_net_key_set_phase(struct mesh_config *cfg, uint16_t idx, uint8_t phase); bool mesh_config_write_address(struct mesh_config *cfg, uint16_t address); bool mesh_config_write_iv_index(struct mesh_config *cfg, uint32_t idx, bool update); bool mesh_config_update_company_id(struct mesh_config *cfg, uint16_t cid); bool mesh_config_update_product_id(struct mesh_config *cfg, uint16_t pid); bool mesh_config_update_version_id(struct mesh_config *cfg, uint16_t vid); bool mesh_config_update_crpl(struct mesh_config *cfg, uint16_t crpl); bluez-5.82/mesh/PaxHeaders/prv-beacon.h0000644000000000000000000000005014447506754015027 xustar0020 atime=1743516868 20 ctime=1743591282 bluez-5.82/mesh/prv-beacon.h0000644000000000000000000000235514447506754014515 0ustar00rootroot/* * * BlueZ - Bluetooth protocol stack for Linux * * Copyright (C) 2023 Intel Corporation. All rights reserved. * * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * */ struct mesh_node; #define PRV_BEACON_SRV_MODEL SET_ID(SIG_VENDOR, 0x0008) #define PRV_BEACON_CLI_MODEL SET_ID(SIG_VENDOR, 0x0009) /* Private Beacon opcodes */ #define OP_PRIVATE_BEACON_GET 0x8060 #define OP_PRIVATE_BEACON_SET 0x8061 #define OP_PRIVATE_BEACON_STATUS 0x8062 #define OP_PRIVATE_GATT_PROXY_GET 0x8063 #define OP_PRIVATE_GATT_PROXY_SET 0x8064 #define OP_PRIVATE_GATT_PROXY_STATUS 0x8065 #define OP_PRIVATE_NODE_ID_GET 0x8066 #define OP_PRIVATE_NODE_ID_SET 0x8067 #define OP_PRIVATE_NODE_ID_STATUS 0x8068 void prv_beacon_server_init(struct mesh_node *node, uint8_t ele_idx); bluez-5.82/mesh/PaxHeaders/cfgmod-server.c0000644000000000000000000000005014772767672015542 xustar0020 atime=1743515579 20 ctime=1743591282 bluez-5.82/mesh/cfgmod-server.c0000644000000000000000000006225314772767672015233 0ustar00rootroot// SPDX-License-Identifier: LGPL-2.1-or-later /* * * BlueZ - Bluetooth protocol stack for Linux * * Copyright (C) 2018-2020 Intel Corporation. All rights reserved. * * */ #ifdef HAVE_CONFIG_H #include #endif #include #include #include #include "mesh/mesh-defs.h" #include "mesh/node.h" #include "mesh/net.h" #include "mesh/appkey.h" #include "mesh/model.h" #include "mesh/mesh-config.h" #include "mesh/cfgmod.h" #define CREDFLAG_MASK 0x1000 #define CFG_GET_ID(vendor, pkt) ((vendor) ? \ (SET_ID(l_get_le16((pkt)), l_get_le16((pkt) + 2))) : \ (SET_ID(SIG_VENDOR, l_get_le16(pkt)))) /* Supported composition pages, sorted high to low */ static const uint8_t supported_pages[] = { 128, 0 }; static uint8_t msg[MAX_MSG_LEN]; static uint16_t set_pub_status(uint8_t status, uint16_t ele_addr, uint32_t id, uint16_t pub_addr, uint16_t idx, bool cred_flag, uint8_t ttl, uint8_t period, uint8_t rtx) { size_t n; n = mesh_model_opcode_set(OP_CONFIG_MODEL_PUB_STATUS, msg); msg[n++] = status; l_put_le16(ele_addr, msg + n); l_put_le16(pub_addr, msg + n + 2); idx |= cred_flag ? CREDFLAG_MASK : 0; l_put_le16(idx, msg + n + 4); n += 6; msg[n++] = ttl; msg[n++] = period; msg[n++] = rtx; if (!IS_VENDOR(id)) { l_put_le16(MODEL_ID(id), msg + n); n += 2; } else { l_put_le16(VENDOR_ID(id), msg + n); l_put_le16(MODEL_ID(id), msg + n + 2); n += 4; } return n; } static uint16_t config_pub_get(struct mesh_node *node, const uint8_t *pkt, bool vendor) { uint32_t id; uint16_t ele_addr; uint8_t rtx; struct mesh_model_pub *pub; int status; ele_addr = l_get_le16(pkt); id = CFG_GET_ID(vendor, pkt + 2); pub = mesh_model_pub_get(node, ele_addr, id, &status); if (pub && status == MESH_STATUS_SUCCESS) { rtx = pub->rtx.cnt + (((pub->rtx.interval / 50) - 1) << 3); return set_pub_status(status, ele_addr, id, pub->addr, pub->idx, pub->credential, pub->ttl, pub->period, rtx); } else return set_pub_status(status, ele_addr, id, 0, 0, 0, 0, 0, 0); } static uint16_t config_pub_set(struct mesh_node *node, const uint8_t *pkt, bool virt, bool vendor) { uint32_t id; uint16_t ele_addr, idx, pub_dst; const uint8_t *pub_addr; uint8_t ttl, period, rtx, cnt, interval; int status; bool cred_flag; ele_addr = l_get_le16(pkt); pub_addr = pkt + 2; pub_dst = l_get_le16(pub_addr); if (!virt && IS_VIRTUAL(pub_dst)) return 0; pkt += (virt ? 14 : 0); idx = l_get_le16(pkt + 4); cred_flag = !!(CREDFLAG_MASK & idx); idx &= APP_IDX_MASK; ttl = pkt[6]; period = pkt[7]; rtx = pkt[8]; id = CFG_GET_ID(vendor, pkt + 9); cnt = rtx & 0x7; interval = ((rtx >> 3) + 1) * 50; status = mesh_model_pub_set(node, ele_addr, id, pub_addr, idx, cred_flag, ttl, period, cnt, interval, virt, &pub_dst); l_debug("pub_set: status %d, ea %4.4x, ota: %4.4x, id: %x, idx: %3.3x", status, ele_addr, pub_dst, id, idx); if (status != MESH_STATUS_SUCCESS) return set_pub_status(status, ele_addr, id, 0, 0, 0, 0, 0, 0); if (IS_UNASSIGNED(pub_dst) && !virt) { ttl = period = idx = 0; /* Remove model publication from config file */ if (!mesh_config_model_pub_del(node_config_get(node), ele_addr, vendor ? id : MODEL_ID(id), vendor)) status = MESH_STATUS_STORAGE_FAIL; } else { struct mesh_config_pub db_pub = { .virt = virt, .addr = pub_dst, .idx = idx, .ttl = ttl, .credential = cred_flag, .period = period, .cnt = cnt, .interval = interval }; if (virt) memcpy(db_pub.virt_addr, pub_addr, 16); /* Save model publication to config file */ if (!mesh_config_model_pub_add(node_config_get(node), ele_addr, vendor ? id : MODEL_ID(id), vendor, &db_pub)) status = MESH_STATUS_STORAGE_FAIL; } return set_pub_status(status, ele_addr, id, pub_dst, idx, cred_flag, ttl, period, rtx); } static uint16_t cfg_sub_get_msg(struct mesh_node *node, const uint8_t *pkt, uint16_t size) { uint16_t ele_addr, n, sub_len; uint32_t id; int opcode; bool vendor = (size == 6); ele_addr = l_get_le16(pkt); id = CFG_GET_ID(vendor, pkt + 2); opcode = vendor ? OP_CONFIG_VEND_MODEL_SUB_LIST : OP_CONFIG_MODEL_SUB_LIST; n = mesh_model_opcode_set(opcode, msg); memcpy(msg + n + 1, pkt, size); msg[n] = mesh_model_sub_get(node, ele_addr, id, msg + n + 1 + size, MAX_MSG_LEN - (n + 1 + size), &sub_len); if (msg[n] == MESH_STATUS_SUCCESS) n += sub_len; n += (size + 1); return n; } static bool save_cfg_sub(struct mesh_node *node, uint16_t ele_addr, uint32_t id, bool vendor, const uint8_t *label, bool virt, uint16_t grp, uint32_t opcode) { struct mesh_config *cfg = node_config_get(node); struct mesh_config_sub db_sub = { .virt = virt, .addr.grp = grp }; id = (vendor) ? id : MODEL_ID(id); if (virt) memcpy(db_sub.addr.label, label, 16); if (opcode == OP_CONFIG_MODEL_SUB_VIRT_DELETE || opcode == OP_CONFIG_MODEL_SUB_DELETE) return mesh_config_model_sub_del(cfg, ele_addr, id, vendor, &db_sub); if (opcode == OP_CONFIG_MODEL_SUB_VIRT_OVERWRITE || opcode == OP_CONFIG_MODEL_SUB_OVERWRITE) if (!mesh_config_model_sub_del_all(cfg, ele_addr, id, vendor)) return false; return mesh_config_model_sub_add(cfg, ele_addr, id, vendor, &db_sub); } static uint16_t cfg_sub_add_msg(struct mesh_node *node, const uint8_t *pkt, bool vendor, uint32_t opcode) { uint16_t addr, ele_addr, n; uint32_t id; addr = l_get_le16(pkt + 2); if (!IS_GROUP(addr)) return 0; ele_addr = l_get_le16(pkt); id = CFG_GET_ID(vendor, pkt + 4); n = mesh_model_opcode_set(OP_CONFIG_MODEL_SUB_STATUS, msg); if (opcode == OP_CONFIG_MODEL_SUB_OVERWRITE) msg[n] = mesh_model_sub_ovrt(node, ele_addr, id, addr); else if (opcode == OP_CONFIG_MODEL_SUB_ADD) msg[n] = mesh_model_sub_add(node, ele_addr, id, addr); else msg[n] = mesh_model_sub_del(node, ele_addr, id, addr); if (msg[n] == MESH_STATUS_SUCCESS && !save_cfg_sub(node, ele_addr, id, vendor, NULL, false, addr, opcode)) msg[n] = MESH_STATUS_STORAGE_FAIL; if (vendor) { memcpy(msg + n + 1, pkt, 8); n += 9; } else { memcpy(msg + n + 1, pkt, 6); n += 7; } return n; } static uint16_t cfg_virt_sub_add_msg(struct mesh_node *node, const uint8_t *pkt, bool vendor, uint32_t opcode) { uint16_t addr, ele_addr, n; uint32_t id; const uint8_t *label; n = mesh_model_opcode_set(OP_CONFIG_MODEL_SUB_STATUS, msg); ele_addr = l_get_le16(pkt); label = pkt + 2; id = CFG_GET_ID(vendor, pkt + 18); if (opcode == OP_CONFIG_MODEL_SUB_VIRT_OVERWRITE) msg[n] = mesh_model_virt_sub_ovrt(node, ele_addr, id, label, &addr); else if (opcode == OP_CONFIG_MODEL_SUB_VIRT_ADD) msg[n] = mesh_model_virt_sub_add(node, ele_addr, id, label, &addr); else msg[n] = mesh_model_virt_sub_del(node, ele_addr, id, label, &addr); if (msg[n] == MESH_STATUS_SUCCESS && !save_cfg_sub(node, ele_addr, id, vendor, label, true, addr, opcode)) msg[n] = MESH_STATUS_STORAGE_FAIL; /* If processing failed, set addr field to zero in reply */ if (msg[n] != MESH_STATUS_SUCCESS) addr = UNASSIGNED_ADDRESS; l_put_le16(ele_addr, msg + n + 1); l_put_le16(addr, msg + n + 3); if (vendor) { l_put_le16(VENDOR_ID(id), msg + n + 5); l_put_le16(MODEL_ID(id), msg + n + 7); n += 9; } else { l_put_le16(MODEL_ID(id), msg + n + 5); n += 7; } return n; } static uint16_t config_sub_del_all(struct mesh_node *node, const uint8_t *pkt, bool vendor) { uint16_t ele_addr, n, grp = UNASSIGNED_ADDRESS; uint32_t id; n = mesh_model_opcode_set(OP_CONFIG_MODEL_SUB_STATUS, msg); ele_addr = l_get_le16(pkt); id = CFG_GET_ID(vendor, pkt + 2); msg[n] = mesh_model_sub_del_all(node, ele_addr, id); if (msg[n] == MESH_STATUS_SUCCESS) { struct mesh_config *cfg = node_config_get(node); if (!mesh_config_model_sub_del_all(cfg, ele_addr, vendor ? id : MODEL_ID(id), vendor)) msg[n] = MESH_STATUS_STORAGE_FAIL; } l_put_le16(ele_addr, msg + n + 1); l_put_le16(grp, msg + n + 3); if (vendor) { l_put_le16(VENDOR_ID(id), msg + n + 5); l_put_le16(MODEL_ID(id), msg + n + 7); n += 9; } else { l_put_le16(MODEL_ID(id), msg + n + 5); n += 7; } return n; } static uint16_t model_app_list(struct mesh_node *node, const uint8_t *pkt, uint16_t size) { uint16_t ele_addr, n, bnd_len; uint32_t id; int opcode; opcode = (size == 4) ? OP_MODEL_APP_LIST : OP_VEND_MODEL_APP_LIST; ele_addr = l_get_le16(pkt); n = mesh_model_opcode_set(opcode, msg); memcpy(msg + n + 1, pkt, size); id = CFG_GET_ID(size == 6, pkt + 2); msg[n] = mesh_model_get_bindings(node, ele_addr, id, msg + n + 1 + size, MAX_MSG_LEN - (n + 1 + size), &bnd_len); if (msg[n] == MESH_STATUS_SUCCESS) n += bnd_len; n += (size + 1); return n; } static uint16_t model_app_bind(struct mesh_node *node, const uint8_t *pkt, uint16_t size, bool unbind) { uint16_t ele_addr, idx, n; uint32_t id; idx = l_get_le16(pkt + 2); if (idx > APP_IDX_MAX) return 0; ele_addr = l_get_le16(pkt); id = CFG_GET_ID(size == 8, pkt + 4); n = mesh_model_opcode_set(OP_MODEL_APP_STATUS, msg); if (unbind) msg[n] = mesh_model_binding_del(node, ele_addr, id, idx); else msg[n] = mesh_model_binding_add(node, ele_addr, id, idx); memcpy(msg + n + 1, pkt, size); n += (size + 1); return n; } static uint16_t cfg_relay_msg(struct mesh_node *node, const uint8_t *pkt, int opcode) { uint8_t count; uint16_t interval; uint16_t n; if (opcode == OP_CONFIG_RELAY_SET) { count = (pkt[1] & 0x7) + 1; interval = ((pkt[1] >> 3) + 1) * 10; node_relay_mode_set(node, !!pkt[0], count, interval); } n = mesh_model_opcode_set(OP_CONFIG_RELAY_STATUS, msg); msg[n++] = node_relay_mode_get(node, &count, &interval); msg[n++] = (count - 1) + ((interval/10 - 1) << 3); return n; } static uint16_t cfg_key_refresh_phase(struct mesh_node *node, const uint8_t *pkt, int opcode) { struct mesh_net *net = node_get_net(node); uint16_t n, idx = l_get_le16(pkt); uint8_t phase; int status; n = mesh_model_opcode_set(OP_CONFIG_KEY_REFRESH_PHASE_STATUS, msg); status = mesh_net_key_refresh_phase_get(net, idx, &phase); if (status == MESH_STATUS_SUCCESS && opcode == OP_CONFIG_KEY_REFRESH_PHASE_SET) { if (pkt[2] == KEY_REFRESH_TRANS_TWO) { if (phase == KEY_REFRESH_PHASE_TWO) goto done; else if (phase != KEY_REFRESH_PHASE_ONE) return 0; } if (pkt[2] == KEY_REFRESH_TRANS_THREE && phase == KEY_REFRESH_PHASE_NONE) goto done; status = mesh_net_key_refresh_phase_set(net, idx, pkt[2]); l_debug("Set KR Phase: net=%3.3x transition=%d", idx, pkt[2]); if (status == MESH_STATUS_SUCCESS) mesh_net_key_refresh_phase_get(net, idx, &phase); } done: msg[n] = status; l_put_le16(idx, &msg[n + 1]); msg[n + 3] = (status != MESH_STATUS_SUCCESS) ? KEY_REFRESH_PHASE_NONE : phase; return n + 4; } static uint8_t uint32_to_log(uint32_t value) { uint32_t val = 1; uint8_t ret = 0; if (!value) return 0; else if (value > 0x10000) return 0xff; while (val <= value) { val <<= 1; ret++; } return ret; } static uint16_t hb_subscription_status(struct mesh_node *node, int status) { struct mesh_net *net = node_get_net(node); struct mesh_net_heartbeat_sub *sub = mesh_net_get_heartbeat_sub(net); struct timeval time_now; uint16_t n; gettimeofday(&time_now, NULL); time_now.tv_sec -= sub->start; if (time_now.tv_sec >= (long) sub->period) time_now.tv_sec = 0; else time_now.tv_sec = sub->period - time_now.tv_sec; l_debug("Sub Period (Log %2.2x) %d sec", uint32_to_log(time_now.tv_sec), (int) time_now.tv_sec); n = mesh_model_opcode_set(OP_CONFIG_HEARTBEAT_SUB_STATUS, msg); msg[n++] = status; l_put_le16(sub->src, msg + n); n += 2; l_put_le16(sub->dst, msg + n); n += 2; msg[n++] = uint32_to_log(time_now.tv_sec); msg[n++] = sub->count != 0xffff ? uint32_to_log(sub->count) : 0xff; msg[n++] = sub->min_hops; msg[n++] = sub->max_hops; return n; } static uint16_t hb_subscription_get(struct mesh_node *node, int status) { struct mesh_net *net = node_get_net(node); struct mesh_net_heartbeat_sub *sub = mesh_net_get_heartbeat_sub(net); /* * MshPRFv1.0.1 section 4.4.1.2.16, Heartbeat Subscription state: * If this is a GET request and the source or destination is unassigned, * all fields shall be set to zero in the status reply. */ if (IS_UNASSIGNED(sub->src) || IS_UNASSIGNED(sub->dst)) { uint16_t n; n = mesh_model_opcode_set(OP_CONFIG_HEARTBEAT_SUB_STATUS, msg); memset(msg + n, 0, 9); n += 9; return n; } return hb_subscription_status(node, status); } static uint16_t hb_subscription_set(struct mesh_node *node, const uint8_t *pkt) { uint16_t src, dst; uint8_t period_log; struct mesh_net *net; int status; src = l_get_le16(pkt); dst = l_get_le16(pkt + 2); /* SRC must be Unicast, DST can be any legal address except Virtual */ if ((!IS_UNASSIGNED(src) && !IS_UNICAST(src)) || IS_VIRTUAL(dst)) return 0; period_log = pkt[4]; if (period_log > 0x11) return 0; net = node_get_net(node); status = mesh_net_set_heartbeat_sub(net, src, dst, period_log); return hb_subscription_status(node, status); } static uint16_t hb_publication_get(struct mesh_node *node, int status) { struct mesh_net *net = node_get_net(node); struct mesh_net_heartbeat_pub *pub = mesh_net_get_heartbeat_pub(net); uint16_t n; n = mesh_model_opcode_set(OP_CONFIG_HEARTBEAT_PUB_STATUS, msg); msg[n++] = status; l_put_le16(pub->dst, msg + n); n += 2; msg[n++] = pub->count != 0xffff ? uint32_to_log(pub->count) : 0xff; msg[n++] = uint32_to_log(pub->period); msg[n++] = pub->ttl; l_put_le16(pub->features, msg + n); n += 2; l_put_le16(pub->net_idx, msg + n); n += 2; return n; } static uint16_t hb_publication_set(struct mesh_node *node, const uint8_t *pkt) { uint16_t dst, features, net_idx; uint8_t period_log, count_log, ttl; struct mesh_net *net; int status; dst = l_get_le16(pkt); count_log = pkt[2]; period_log = pkt[3]; ttl = pkt[4]; if (count_log > 0x11 && count_log != 0xff) return 0; if (period_log > 0x11 || ttl > TTL_MASK || IS_VIRTUAL(dst)) return 0; features = l_get_le16(pkt + 5) & 0xf; net_idx = l_get_le16(pkt + 7); net = node_get_net(node); status = mesh_net_set_heartbeat_pub(net, dst, features, net_idx, ttl, count_log, period_log); if (status != MESH_STATUS_SUCCESS) { uint16_t n; n = mesh_model_opcode_set(OP_CONFIG_HEARTBEAT_PUB_STATUS, msg); msg[n++] = status; memcpy(msg + n, pkt, 9); n += 9; return n; } else return hb_publication_get(node, status); } static void node_reset(void *user_data) { struct mesh_node *node = user_data; l_debug("Node Reset"); node_remove(node); } static uint16_t cfg_appkey_msg(struct mesh_node *node, const uint8_t *pkt, int opcode) { uint16_t n_idx, a_idx, n; struct mesh_net *net = node_get_net(node); n_idx = l_get_le16(pkt) & 0xfff; a_idx = l_get_le16(pkt + 1) >> 4; n = mesh_model_opcode_set(OP_APPKEY_STATUS, msg); if (opcode == OP_APPKEY_ADD) msg[n] = appkey_key_add(net, n_idx, a_idx, pkt + 3); else if (opcode == OP_APPKEY_UPDATE) msg[n] = appkey_key_update(net, n_idx, a_idx, pkt + 3); else msg[n] = appkey_key_delete(net, n_idx, a_idx); l_debug("AppKey Command %s: Net_Idx %3.3x, App_Idx %3.3x", (msg[n] == MESH_STATUS_SUCCESS) ? "success" : "fail", n_idx, a_idx); memcpy(msg + n + 1, &pkt[0], 3); return n + 4; } static uint16_t cfg_netkey_msg(struct mesh_node *node, const uint8_t *pkt, int opcode) { uint16_t n_idx, n; struct mesh_net *net = node_get_net(node); n_idx = l_get_le16(pkt); if (n_idx > NET_IDX_MAX) return 0; n = mesh_model_opcode_set(OP_NETKEY_STATUS, msg); if (opcode == OP_NETKEY_ADD) msg[n] = mesh_net_add_key(net, n_idx, pkt + 2); else if (opcode == OP_NETKEY_UPDATE) msg[n] = mesh_net_update_key(net, n_idx, pkt + 2); else msg[n] = mesh_net_del_key(net, n_idx); l_debug("NetKey Command %s: Net_Idx %3.3x", (msg[n] == MESH_STATUS_SUCCESS) ? "success" : "fail", n_idx); memcpy(msg + n + 1, &pkt[0], 2); return n + 3; } static uint16_t cfg_get_appkeys_msg(struct mesh_node *node, const uint8_t *pkt) { uint16_t n_idx, sz, n; n_idx = l_get_le16(pkt); n = mesh_model_opcode_set(OP_APPKEY_LIST, msg); l_put_le16(n_idx, msg + n + 1); msg[n] = appkey_list(node_get_net(node), n_idx, msg + n + 3, MAX_MSG_LEN - (n + 3), &sz); return n + 3 + sz; } static uint16_t cfg_poll_timeout_msg(struct mesh_node *node, const uint8_t *pkt) { uint16_t n, addr = l_get_le16(pkt); uint32_t poll_to; if (!IS_UNICAST(addr)) return 0; n = mesh_model_opcode_set(OP_CONFIG_POLL_TIMEOUT_STATUS, msg); l_put_le16(addr, msg + n); n += 2; poll_to = mesh_net_friend_timeout(node_get_net(node), addr); msg[n++] = poll_to; msg[n++] = poll_to >> 8; msg[n++] = poll_to >> 16; return n; } static uint16_t cfg_net_tx_msg(struct mesh_node *node, const uint8_t *pkt, int opcode) { uint8_t cnt; uint16_t interval, n; struct mesh_net *net = node_get_net(node); cnt = (pkt[0] & 0x7) + 1; interval = ((pkt[0] >> 3) + 1) * 10; if (opcode == OP_CONFIG_NETWORK_TRANSMIT_SET && mesh_config_write_net_transmit(node_config_get(node), cnt, interval)) mesh_net_transmit_params_set(net, cnt, interval); n = mesh_model_opcode_set(OP_CONFIG_NETWORK_TRANSMIT_STATUS, msg); mesh_net_transmit_params_get(net, &cnt, &interval); msg[n++] = (cnt - 1) + ((interval/10 - 1) << 3); return n; } static uint16_t get_composition(struct mesh_node *node, uint8_t page, uint8_t *buf) { const uint8_t *comp = NULL; uint16_t len = 0; size_t i; for (i = 0; i < sizeof(supported_pages); i++) { if (page < supported_pages[i]) continue; page = supported_pages[i]; comp = node_get_comp(node, page, &len); if (!page || len) break; } if (!len || !comp) return 0; *buf++ = page; memcpy(buf, comp, len); return len + 1; } static bool cfg_srv_pkt(uint16_t src, uint16_t dst, uint16_t app_idx, uint16_t net_idx, const uint8_t *data, uint16_t size, const void *user_data) { struct mesh_node *node = (struct mesh_node *) user_data; struct mesh_net *net; const uint8_t *pkt = data; uint32_t opcode; uint16_t n_idx; uint8_t state; bool virt = false; uint16_t n; if (app_idx != APP_IDX_DEV_LOCAL) return false; if (mesh_model_opcode_get(pkt, size, &opcode, &n)) { size -= n; pkt += n; } else return false; net = node_get_net(node); l_debug("CONFIG-SRV-opcode 0x%x size %u idx %3.3x", opcode, size, net_idx); n = 0; switch (opcode) { default: return false; case OP_DEV_COMP_GET: if (size != 1) return true; n = mesh_model_opcode_set(OP_DEV_COMP_STATUS, msg); n += get_composition(node, pkt[0], msg + n); break; case OP_CONFIG_DEFAULT_TTL_SET: if (size != 1 || pkt[0] > TTL_MASK || pkt[0] == 1) return true; node_default_ttl_set(node, pkt[0]); /* Fall Through */ case OP_CONFIG_DEFAULT_TTL_GET: if (opcode == OP_CONFIG_DEFAULT_TTL_GET && size != 0) return true; l_debug("Get/Set Default TTL"); n = mesh_model_opcode_set(OP_CONFIG_DEFAULT_TTL_STATUS, msg); msg[n++] = node_default_ttl_get(node); break; case OP_CONFIG_MODEL_PUB_VIRT_SET: if (size != 25 && size != 27) return true; virt = true; /* Fall Through */ case OP_CONFIG_MODEL_PUB_SET: if (!virt && (size != 11 && size != 13)) return true; n = config_pub_set(node, pkt, virt, size == 13 || size == 27); break; case OP_CONFIG_MODEL_PUB_GET: if (size != 4 && size != 6) return true; n = config_pub_get(node, pkt, size == 6); break; case OP_CONFIG_VEND_MODEL_SUB_GET: if (size != 6) return true; /* Fall Through */ case OP_CONFIG_MODEL_SUB_GET: if (size != 4 && opcode == OP_CONFIG_MODEL_SUB_GET) return true; n = cfg_sub_get_msg(node, pkt, size); break; case OP_CONFIG_MODEL_SUB_VIRT_OVERWRITE: case OP_CONFIG_MODEL_SUB_VIRT_DELETE: case OP_CONFIG_MODEL_SUB_VIRT_ADD: if (size != 20 && size != 22) return true; n = cfg_virt_sub_add_msg(node, pkt, size == 22, opcode); break; case OP_CONFIG_MODEL_SUB_OVERWRITE: case OP_CONFIG_MODEL_SUB_DELETE: case OP_CONFIG_MODEL_SUB_ADD: if (size != 6 && size != 8) return true; n = cfg_sub_add_msg(node, pkt, size == 8, opcode); break; case OP_CONFIG_MODEL_SUB_DELETE_ALL: if (size != 4 && size != 6) return true; n = config_sub_del_all(node, pkt, size == 6); break; case OP_CONFIG_RELAY_SET: if (size != 2 || pkt[0] > 0x01) return true; /* Fall Through */ case OP_CONFIG_RELAY_GET: if (opcode == OP_CONFIG_RELAY_GET && size != 0) return true; n = cfg_relay_msg(node, pkt, opcode); break; case OP_CONFIG_NETWORK_TRANSMIT_SET: if (size != 1) return true; /* Fall Through */ case OP_CONFIG_NETWORK_TRANSMIT_GET: if (opcode == OP_CONFIG_NETWORK_TRANSMIT_GET && size != 0) return true; n = cfg_net_tx_msg(node, pkt, opcode); break; case OP_CONFIG_PROXY_SET: if (size != 1 || pkt[0] > 0x01) return true; node_proxy_mode_set(node, !!pkt[0]); /* Fall Through */ case OP_CONFIG_PROXY_GET: if (opcode == OP_CONFIG_PROXY_GET && size != 0) return true; n = mesh_model_opcode_set(OP_CONFIG_PROXY_STATUS, msg); msg[n++] = node_proxy_mode_get(node); l_debug("Get/Set Config Proxy (%d)", msg[n-1]); break; case OP_NODE_IDENTITY_SET: if (size != 3) return true; /* Currently setting node identity not supported */ /* Fall Through */ case OP_NODE_IDENTITY_GET: if (opcode == OP_NODE_IDENTITY_GET && size != 2) return true; n_idx = l_get_le16(pkt); if (n_idx > NET_IDX_MAX) return true; n = mesh_model_opcode_set(OP_NODE_IDENTITY_STATUS, msg); msg[n++] = mesh_net_get_identity_mode(net, n_idx, &state); l_put_le16(n_idx, msg + n); n += 2; msg[n++] = state; l_debug("Get/Set Config Identity (%d)", state); break; case OP_CONFIG_BEACON_SET: if (size != 1 || pkt[0] > 0x01) return true; node_beacon_mode_set(node, !!pkt[0]); /* Fall Through */ case OP_CONFIG_BEACON_GET: if (opcode == OP_CONFIG_BEACON_GET && size != 0) return true; n = mesh_model_opcode_set(OP_CONFIG_BEACON_STATUS, msg); msg[n++] = node_beacon_mode_get(node); l_debug("Get/Set Config Beacon (%d)", msg[n-1]); break; case OP_CONFIG_FRIEND_SET: if (size != 1 || pkt[0] > 0x01) return true; node_friend_mode_set(node, !!pkt[0]); /* Fall Through */ case OP_CONFIG_FRIEND_GET: if (opcode == OP_CONFIG_FRIEND_GET && size != 0) return true; n = mesh_model_opcode_set(OP_CONFIG_FRIEND_STATUS, msg); msg[n++] = node_friend_mode_get(node); l_debug("Get/Set Friend (%d)", msg[n-1]); break; case OP_CONFIG_KEY_REFRESH_PHASE_SET: if (size != 3 || (pkt[2] != KEY_REFRESH_TRANS_THREE && pkt[2] != KEY_REFRESH_TRANS_TWO)) return true; /* Fall Through */ case OP_CONFIG_KEY_REFRESH_PHASE_GET: if (size != 2 && opcode == OP_CONFIG_KEY_REFRESH_PHASE_GET) return true; n = cfg_key_refresh_phase(node, pkt, opcode); break; case OP_APPKEY_ADD: case OP_APPKEY_UPDATE: if (size != 19) return true; /* Fall Through */ case OP_APPKEY_DELETE: if (opcode == OP_APPKEY_DELETE && size != 3) return true; n = cfg_appkey_msg(node, pkt, opcode); break; case OP_APPKEY_GET: if (size != 2) return true; n = cfg_get_appkeys_msg(node, pkt); break; case OP_NETKEY_ADD: case OP_NETKEY_UPDATE: if (size != 18) return true; /* Fall Through */ case OP_NETKEY_DELETE: if (opcode == OP_NETKEY_DELETE && size != 2) return true; n = cfg_netkey_msg(node, pkt, opcode); break; case OP_NETKEY_GET: if (size != 0) return true; n = mesh_model_opcode_set(OP_NETKEY_LIST, msg); size = MAX_MSG_LEN - n; if (mesh_net_key_list_get(net, msg + n, &size)) n += size; break; case OP_MODEL_APP_BIND: case OP_MODEL_APP_UNBIND: if (size != 6 && size != 8) return true; n = model_app_bind(node, pkt, size, opcode != OP_MODEL_APP_BIND); break; case OP_VEND_MODEL_APP_GET: if (size != 6) return true; n = model_app_list(node, pkt, size); break; case OP_MODEL_APP_GET: if (size != 4) return true; n = model_app_list(node, pkt, size); break; case OP_CONFIG_HEARTBEAT_PUB_SET: l_debug("Config Heartbeat Publication Set"); if (size != 9) return true; n = hb_publication_set(node, pkt); break; case OP_CONFIG_HEARTBEAT_PUB_GET: if (size != 0) return true; n = hb_publication_get(node, MESH_STATUS_SUCCESS); break; case OP_CONFIG_HEARTBEAT_SUB_SET: if (size != 5) return true; l_debug("Set HB Sub Period Log %2.2x", pkt[4]); n = hb_subscription_set(node, pkt); break; case OP_CONFIG_HEARTBEAT_SUB_GET: if (size != 0) return true; n = hb_subscription_get(node, MESH_STATUS_SUCCESS); break; case OP_CONFIG_POLL_TIMEOUT_GET: if (size != 2) return true; n = cfg_poll_timeout_msg(node, pkt); break; case OP_NODE_RESET: if (size != 0) return true; n = mesh_model_opcode_set(OP_NODE_RESET_STATUS, msg); /* Delay node removal to give it a chance to send the status */ l_idle_oneshot(node_reset, node, NULL); break; } if (n) mesh_model_send(node, dst, src, APP_IDX_DEV_LOCAL, net_idx, DEFAULT_TTL, false, n, msg); return true; } static void cfgmod_srv_unregister(void *user_data) { } static const struct mesh_model_ops ops = { .unregister = cfgmod_srv_unregister, .recv = cfg_srv_pkt, .bind = NULL, .sub = NULL, .pub = NULL }; void cfgmod_server_init(struct mesh_node *node, uint8_t ele_idx) { l_debug("%2.2x", ele_idx); mesh_model_register(node, ele_idx, CONFIG_SRV_MODEL, &ops, node); } bluez-5.82/mesh/PaxHeaders/bluetooth-meshd.rst.in0000644000000000000000000000005014766002272017052 xustar0020 atime=1743515578 20 ctime=1743591275 bluez-5.82/mesh/bluetooth-meshd.rst.in0000644000000000000000000000334114766002272016534 0ustar00rootroot=============== bluetooth-meshd =============== --------------------- Bluetooth Mesh daemon --------------------- :Version: BlueZ :Copyright: Free use of this software is granted under the terms of the GNU Lesser General Public Licenses (LGPL). :Date: March 2021 :Manual section: 8 :Manual group: Linux Connectivity SYNOPSIS ======== **bluetooth-meshd** [*options* ...] DESCRIPTION =========== Daemon for managing Bluetooth Mesh connections on Linux. OPTIONS ======= -h, --help Print bluetooth-meshd options and exit. -n, --nodetach Enable logging in foreground. Directs log output to the controlling terminal in addition to syslog. -i , --io Specifies I/O interface type: *auto* - Use first available controller: via MGMT interface if kernel supports it, otherwise, via raw HCI socket. *generic:[hci]* - Use generic HCI io on interface hci. *unit:*- Specifies open file descriptor for daemon testing. By default, if no type is specified, uses auto I/O on the first available controller. -c , --config Specifies an explicit config file path instead of relying on the default path(*@CONFIGDIR@/mesh-main.conf*) for the config file. -s , --storage Specifies an explicit storage directory path instead of the default path(*@MESH_STORAGEDIR@*) for storing mesh node(s) configuration. -d, --debug Enable debug output. -b, --dbus-debug Enable D-Bus debug output. FILES ===== *@CONFIGDIR@/mesh-main.conf* Location of the global configuration file containing mesh daemon settings. RESOURCES ========= http://www.bluez.org REPORTING BUGS ============== linux-bluetooth@vger.kernel.org bluez-5.82/mesh/PaxHeaders/mesh-main.conf0000644000000000000000000000005013622034624015331 xustar0020 atime=1743516876 20 ctime=1743591289 bluez-5.82/mesh/mesh-main.conf0000644000000000000000000000255113622034624015015 0ustar00rootroot[General] # Default setting for to indicate whether secure network beaconing # is enabled for a node whose Beacon state hasn't been configured # by a configuration client, i.e., for a newly provisioned, created # or imported node. # Defaults to true. #Beacon = true # Default setting for supporting relay. The setting applies # to all local nodes. # If the value is true, then a configuration client can either enable or disable # the relay feature per local node. # If the value is false, then the relay feature cannot be configured for # any local node. # Defaults to true. #Relay = true # Default setting for supporting Friendship. The setting applies # to all local nodes. # If the value is true, then a configuration client can either enable or disable # the Friendship feature per local node. # If the value is false, then the Friendship feature cannot be configured for # any local node. # Defaults to true. #Friendship = true # Default depth of replay protection list. This setting applies to # each individual node. # Valid range 1-65535. # Defaults to 100. #CRPL = 100 # Default size of friend queue: the number of messages that each Friend node can # store for the Low Power node. # Valid range: 0-32. # Defaults to 32. #FriendQueueSize = 32 # Provisioning timeout in seconds. # Setting this value to zero means there's no timeout. # Defaults to 60. #ProvTimeout = 60 bluez-5.82/mesh/PaxHeaders/mesh-mgmt.h0000644000000000000000000000005014447506754014671 xustar0020 atime=1743516868 20 ctime=1743591281 bluez-5.82/mesh/mesh-mgmt.h0000644000000000000000000000146614447506754014361 0ustar00rootroot/* SPDX-License-Identifier: LGPL-2.1-or-later */ /* * * BlueZ - Bluetooth protocol stack for Linux * * Copyright (C) 2019 SILVAIR sp. z o.o. All rights reserved. * * */ #include typedef void (*mesh_mgmt_read_info_func_t)(int index, bool added, bool powered, bool mesh, void *user_data); bool mesh_mgmt_list(mesh_mgmt_read_info_func_t cb, void *user_data); unsigned int mesh_mgmt_send(uint16_t opcode, uint16_t index, uint16_t length, const void *param, mgmt_request_func_t callback, void *user_data, mgmt_destroy_func_t destroy); unsigned int mesh_mgmt_register(uint16_t event, uint16_t index, mgmt_notify_func_t callback, void *user_data, mgmt_destroy_func_t destroy); bool mesh_mgmt_unregister(unsigned int id); void mesh_mgmt_destroy(void); void mesh_mgmt_clear(void); bluez-5.82/mesh/PaxHeaders/mesh-io-mgmt.h0000644000000000000000000000005014333256742015267 xustar0020 atime=1743516868 20 ctime=1743591282 bluez-5.82/mesh/mesh-io-mgmt.h0000644000000000000000000000034214333256742014747 0ustar00rootroot/* SPDX-License-Identifier: LGPL-2.1-or-later */ /* * * BlueZ - Bluetooth protocol stack for Linux * * Copyright (C) 2018 Intel Corporation. All rights reserved. * * */ extern const struct mesh_io_api mesh_io_mgmt; bluez-5.82/mesh/PaxHeaders/provision.h0000644000000000000000000000005014447506754015023 xustar0020 atime=1743516868 20 ctime=1743591282 bluez-5.82/mesh/provision.h0000644000000000000000000000735214447506754014513 0ustar00rootroot/* SPDX-License-Identifier: LGPL-2.1-or-later */ /* * * BlueZ - Bluetooth protocol stack for Linux * * Copyright (C) 2018 Intel Corporation. All rights reserved. * * */ /* * size: hard define (mesh.conf - OOB_NUMBEROOB_NUMBER) * oob size - 8 if alpha or numeric * else 1 if mask is non zero * else 0 */ struct bt_mesh; struct mesh_prov; struct mesh_agent; /* Provisioner Agent Response Types */ #define OOB_CANCEL 0x00 #define OOB_PRIV_KEY 0x01 #define OOB_PUB_KEY 0x02 #define OOB_NUMBER 0x03 #define OOB_STATIC 0x04 #define OOB_NUMBER_DISPLAY 0x05 /* Spec defined Provisioning message types */ #define PROV_INVITE 0x00 #define PROV_CAPS 0x01 #define PROV_START 0x02 #define PROV_PUB_KEY 0x03 #define PROV_INP_CMPLT 0x04 #define PROV_CONFIRM 0x05 #define PROV_RANDOM 0x06 #define PROV_DATA 0x07 #define PROV_COMPLETE 0x08 #define PROV_FAILED 0x09 #define PROV_NONE 0xFF /* Spec defined Error Codes */ #define PROV_ERR_SUCCESS 0x00 #define PROV_ERR_INVALID_PDU 0x01 #define PROV_ERR_INVALID_FORMAT 0x02 #define PROV_ERR_UNEXPECTED_PDU 0x03 #define PROV_ERR_CONFIRM_FAILED 0x04 #define PROV_ERR_INSUF_RESOURCE 0x05 #define PROV_ERR_DECRYPT_FAILED 0x06 #define PROV_ERR_UNEXPECTED_ERR 0x07 #define PROV_ERR_CANT_ASSIGN_ADDR 0x08 /* Internally generated Error Codes */ #define PROV_ERR_TIMEOUT 0xFF /* Provisioner Action values */ /* IN */ #define PROV_ACTION_PUSH 0x00 #define PROV_ACTION_TWIST 0x01 #define PROV_ACTION_IN_NUMERIC 0x02 #define PROV_ACTION_IN_ALPHA 0x03 /* OUT */ #define PROV_ACTION_BLINK 0x00 #define PROV_ACTION_BEEP 0x01 #define PROV_ACTION_VIBRATE 0x02 #define PROV_ACTION_OUT_NUMERIC 0x03 #define PROV_ACTION_OUT_ALPHA 0x04 /* OOB_Info defines from Table 3.54 of Mesh profile Specification v1.0 */ #define OOB_INFO_URI_HASH 0x0002 /* PB_REMOTE not supported from unprovisioned state */ #define PB_NPPI_00 0x00 #define PB_NPPI_01 0x01 #define PB_NPPI_02 0x02 #define PB_ADV 0x03 /* Internal only, and may be reassigned */ #define PB_GATT 0x04 /* Internal only, and may be reassigned */ #define PROV_FLAG_KR 0x01 #define PROV_FLAG_IVU 0x02 struct mesh_prov_node_info { uint32_t iv_index; uint16_t unicast; uint16_t net_index; uint8_t num_ele; uint8_t net_key[16]; uint8_t device_key[16]; uint8_t flags; /* IVU and KR bits */ }; typedef bool (*mesh_prov_acceptor_complete_func_t)(void *user_data, uint8_t status, struct mesh_prov_node_info *info); typedef void (*mesh_prov_initiator_start_func_t)(void *user_data, int err); typedef bool (*mesh_prov_initiator_data_req_func_t)(void *user_data, uint8_t num_elem); typedef bool (*mesh_prov_initiator_complete_func_t)(void *user_data, uint8_t status, struct mesh_prov_node_info *info); typedef void (*mesh_prov_initiator_scan_result_t)(void *user_data, uint16_t server, bool extended, const uint8_t *data, uint16_t len); /* This starts unprovisioned device beacon */ bool acceptor_start(uint8_t num_ele, uint8_t *uuid, uint16_t algorithms, uint32_t timeout, struct mesh_agent *agent, mesh_prov_acceptor_complete_func_t complete_cb, void *caller_data); void acceptor_cancel(void *user_data); bool initiator_start(uint8_t transport, uint16_t server, uint16_t svr_idx, uint8_t uuid[16], uint16_t max_ele, uint32_t timeout, /* in seconds from mesh.conf */ struct mesh_agent *agent, mesh_prov_initiator_start_func_t start_cb, mesh_prov_initiator_data_req_func_t data_req_cb, mesh_prov_initiator_complete_func_t complete_cb, void *node, void *caller_data); void initiator_prov_data(uint16_t net_idx, uint16_t primary, void *caller_data); void initiator_cancel(void *caller_data); void initiator_scan_reg(mesh_prov_initiator_scan_result_t scan_result, void *user_data); void initiator_scan_unreg(void *caller_data); bluez-5.82/mesh/PaxHeaders/mesh-io-generic.h0000644000000000000000000000005014015011623015717 xustar0020 atime=1743516868 20 ctime=1743591282 bluez-5.82/mesh/mesh-io-generic.h0000644000000000000000000000034514015011623015402 0ustar00rootroot/* SPDX-License-Identifier: LGPL-2.1-or-later */ /* * * BlueZ - Bluetooth protocol stack for Linux * * Copyright (C) 2018 Intel Corporation. All rights reserved. * * */ extern const struct mesh_io_api mesh_io_generic; bluez-5.82/mesh/PaxHeaders/mesh-io-unit.h0000644000000000000000000000005014061461364015276 xustar0020 atime=1743516868 20 ctime=1743591282 bluez-5.82/mesh/mesh-io-unit.h0000644000000000000000000000034214061461364014756 0ustar00rootroot/* SPDX-License-Identifier: LGPL-2.1-or-later */ /* * * BlueZ - Bluetooth protocol stack for Linux * * Copyright (C) 2021 Intel Corporation. All rights reserved. * * */ extern const struct mesh_io_api mesh_io_unit; bluez-5.82/mesh/PaxHeaders/main.c0000644000000000000000000000005014772767672013723 xustar0020 atime=1743515579 20 ctime=1743591282 bluez-5.82/mesh/main.c0000644000000000000000000001507314772767672013412 0ustar00rootroot// SPDX-License-Identifier: LGPL-2.1-or-later /* * * BlueZ - Bluetooth protocol stack for Linux * * Copyright (C) 2017-2019 Intel Corporation. All rights reserved. * * */ #ifdef HAVE_CONFIG_H #include #endif #define _GNU_SOURCE #include #include #include #include #include #include #include #include #include #include "lib/bluetooth.h" #include "lib/mgmt.h" #include "mesh/mesh.h" #include "mesh/crypto.h" #include "mesh/dbus.h" #include "mesh/mesh-io.h" #include "mesh/util.h" static const char *storage_dir; static const char *mesh_conf_fname; static enum mesh_io_type io_type; static void *io_opts; static const struct option main_options[] = { { "io", required_argument, NULL, 'i' }, { "storage", required_argument, NULL, 's' }, { "config", required_argument, NULL, 'c' }, { "nodetach", no_argument, NULL, 'n' }, { "debug", no_argument, NULL, 'd' }, { "dbus-debug", no_argument, NULL, 'b' }, { "help", no_argument, NULL, 'h' }, { } }; static const char *io_usage = "\t(auto | generic:[hci] | unit:)\n" "\t\tauto - Use first available controller (MGMT or raw HCI)\n" "\t\tgeneric - Use raw HCI io on interface hci\n" "\t\tunit - Use test IO (for automatic testing only)\n"; static void usage(void) { fprintf(stderr, "Usage:\n" "\tbluetooth-meshd [options]\n"); fprintf(stderr, "Options:\n" "\t--io Use specified io (default: auto)\n" "\t--config Daemon configuration directory\n" "\t--storage Mesh node(s) configuration directory\n" "\t--nodetach Run in foreground\n" "\t--debug Enable debug output\n" "\t--dbus-debug Enable D-Bus debugging\n" "\t--help Show %s information\n", __func__); fprintf(stderr, "\n\t io: %s", io_usage); } static void do_debug(const char *str, void *user_data) { const char *prefix = user_data; l_info("%s%s", prefix, str); } static void mesh_ready_callback(void *user_data, bool success) { struct l_dbus *dbus = user_data; l_info("mesh_ready_callback"); if (!success) { l_error("Failed to start mesh"); l_main_quit(); return; } if (!dbus_init(dbus)) { l_error("Failed to initialize mesh D-Bus resources"); l_main_quit(); } } static void request_name_callback(struct l_dbus *dbus, bool success, bool queued, void *user_data) { if (!success && io_type != MESH_IO_TYPE_UNIT_TEST) { l_info("Request name failed"); l_main_quit(); return; } if (!mesh_init(storage_dir, mesh_conf_fname, io_type, io_opts, mesh_ready_callback, dbus)) { l_error("Failed to initialize mesh"); l_main_quit(); } } static void ready_callback(void *user_data) { struct l_dbus *dbus = user_data; l_info("D-Bus ready"); l_dbus_name_acquire(dbus, BLUEZ_MESH_NAME, false, false, false, request_name_callback, NULL); } static void disconnect_callback(void *user_data) { l_main_quit(); } static void kill_to(struct l_timeout *timeout, void *user_data) { l_timeout_remove(timeout); l_main_quit(); } static void signal_handler(uint32_t signo, void *user_data) { static bool terminated; if (terminated) return; l_info("Terminating"); mesh_cleanup(true); if (io_type != MESH_IO_TYPE_UNIT_TEST) l_timeout_create(1, kill_to, NULL, NULL); else l_main_quit(); terminated = true; } static bool parse_io(const char *optarg, enum mesh_io_type *type, void **opts) { if (strstr(optarg, "auto") == optarg) { int *index = l_new(int, 1); *type = MESH_IO_TYPE_AUTO; *opts = index; optarg += strlen("auto"); *index = MGMT_INDEX_NONE; return true; return false; } else if (strstr(optarg, "generic") == optarg) { int *index = l_new(int, 1); *type = MESH_IO_TYPE_GENERIC; *opts = index; optarg += strlen("generic"); if (!*optarg || *optarg != ':') return false; optarg++; if (sscanf(optarg, "hci%d", index) == 1) return true; if (sscanf(optarg, "%d", index) == 1) return true; return false; } else if (strstr(optarg, "unit") == optarg) { char *test_path; *type = MESH_IO_TYPE_UNIT_TEST; optarg += strlen("unit"); if (*optarg != ':') return false; optarg++; test_path = strdup(optarg); *opts = test_path; return true; } return false; } int main(int argc, char *argv[]) { int status; bool detached = true; bool dbus_debug = false; struct l_dbus *dbus = NULL; char *io = NULL; int hci_index; if (!l_main_init()) return -1; l_log_set_stderr(); if (!mesh_crypto_check_avail()) { l_error("Mesh Crypto functions unavailable"); status = l_main_run_with_signal(signal_handler, NULL); goto done; } for (;;) { int opt; opt = getopt_long(argc, argv, "u:i:s:c:ndbh", main_options, NULL); if (opt < 0) break; switch (opt) { case 'u': if (sscanf(optarg, "%d", &hci_index) == 1 || sscanf(optarg, "%d", &hci_index) == 1) io = l_strdup_printf("unit:%d", hci_index); else io = l_strdup(optarg); break; case 'i': if (sscanf(optarg, "hci%d", &hci_index) == 1 || sscanf(optarg, "%d", &hci_index) == 1) io = l_strdup_printf("generic:%s", optarg); else io = l_strdup(optarg); break; case 'n': detached = false; break; case 'd': enable_debug(); break; case 's': storage_dir = optarg; break; case 'c': mesh_conf_fname = optarg; break; case 'b': dbus_debug = true; break; case 'h': usage(); status = EXIT_SUCCESS; goto done; default: usage(); status = EXIT_FAILURE; goto done; } } if (!io) io = l_strdup_printf("auto"); if (!parse_io(io, &io_type, &io_opts)) { l_error("Invalid io: %s\n%s", io, io_usage); status = EXIT_FAILURE; goto done; } l_free(io); io = NULL; if (!detached) umask(0077); if (io_type != MESH_IO_TYPE_UNIT_TEST) dbus = l_dbus_new_default(L_DBUS_SYSTEM_BUS); else { dbus = l_dbus_new_default(L_DBUS_SESSION_BUS); prctl(PR_SET_PDEATHSIG, SIGSEGV); } if (!dbus) { l_error("unable to connect to D-Bus"); status = EXIT_FAILURE; goto done; } if (dbus_debug) l_dbus_set_debug(dbus, do_debug, "[DBUS] ", NULL); l_dbus_set_ready_handler(dbus, ready_callback, dbus, NULL); l_dbus_set_disconnect_handler(dbus, disconnect_callback, NULL, NULL); if (!l_dbus_object_manager_enable(dbus, "/")) { l_error("Failed to enable Object Manager"); status = EXIT_FAILURE; goto done; } status = l_main_run_with_signal(signal_handler, NULL); done: l_free(io); l_free(io_opts); mesh_cleanup(false); l_dbus_destroy(dbus); l_main_exit(); return status; } bluez-5.82/mesh/PaxHeaders/util.c0000644000000000000000000000005014711225434013726 xustar0020 atime=1743516868 20 ctime=1743591282 bluez-5.82/mesh/util.c0000644000000000000000000000600614711225434013411 0ustar00rootroot// SPDX-License-Identifier: LGPL-2.1-or-later /* * * BlueZ - Bluetooth protocol stack for Linux * * Copyright (C) 2018 Intel Corporation. All rights reserved. * * */ #ifdef HAVE_CONFIG_H #include #endif #define _GNU_SOURCE #include #include #include #include #include #include #include #include #include #include "mesh/util.h" static bool debug_enabled; void print_packet(const char *label, const void *data, uint16_t size) { struct timeval pkt_time; if (!debug_enabled) return; gettimeofday(&pkt_time, NULL); if (size > 0) { char *str; str = l_util_hexstring(data, size); l_debug("%05d.%03d %s: %s", (uint32_t) pkt_time.tv_sec % 100000, (uint32_t) pkt_time.tv_usec/1000, label, str); l_free(str); } else l_debug("%05d.%03d %s: empty", (uint32_t) pkt_time.tv_sec % 100000, (uint32_t) pkt_time.tv_usec/1000, label); } uint32_t get_timestamp_secs(void) { struct timespec ts; clock_gettime(CLOCK_MONOTONIC, &ts); return ts.tv_sec; } bool str2hex(const char *str, uint16_t in_len, uint8_t *out, uint16_t out_len) { uint16_t i; if (in_len < out_len * 2) return false; for (i = 0; i < out_len; i++) { if (sscanf(&str[i * 2], "%02hhx", &out[i]) != 1) return false; } return true; } size_t hex2str(uint8_t *in, size_t in_len, char *out, size_t out_len) { static const char hexdigits[] = "0123456789abcdef"; size_t i; if (in_len * 2 > (out_len - 1)) return 0; for (i = 0; i < in_len; i++) { out[i * 2] = hexdigits[in[i] >> 4]; out[i * 2 + 1] = hexdigits[in[i] & 0xf]; } out[in_len * 2] = '\0'; return i; } int create_dir(const char *dir_name) { struct stat st; char dir[PATH_MAX + 1], *prev, *next; int err; err = stat(dir_name, &st); if (!err && S_ISREG(st.st_mode)) return 0; memset(dir, 0, PATH_MAX + 1); strcat(dir, "/"); prev = strchr(dir_name, '/'); while (prev) { next = strchr(prev + 1, '/'); if (!next) break; if (next - prev == 1) { prev = next; continue; } strncat(dir, prev + 1, next - prev); if (mkdir(dir, 0755) != 0 && errno != EEXIST) l_error("Failed to create dir(%d): %s", errno, dir); prev = next; } if (mkdir(dir_name, 0755) != 0 && errno != EEXIST) l_error("Failed to create dir(%d): %s", errno, dir_name); return 0; } static int del_fobject(const char *fpath, const struct stat *sb, int typeflag, struct FTW *ftwbuf) { switch (typeflag) { case FTW_DP: rmdir(fpath); l_debug("RMDIR %s", fpath); break; case FTW_SL: default: if (remove(fpath) < 0) l_error("Failed to remove(%d): %s", errno, fpath); l_debug("RM %s", fpath); break; } return 0; } void del_path(const char *path) { nftw(path, del_fobject, 5, FTW_DEPTH | FTW_PHYS); } void enable_debug(void) { debug_enabled = true; l_debug_enable("*"); } #if !HAVE_DECL_BASENAME #include const char *mesh_basename(const char *path) { const char *base = strrchr(path, '/'); return base ? base + 1 : path; } #endif bluez-5.82/mesh/PaxHeaders/prov-initiator.c0000644000000000000000000000005014772767672015765 xustar0020 atime=1743515579 20 ctime=1743591282 bluez-5.82/mesh/prov-initiator.c0000644000000000000000000006623314772767672015460 0ustar00rootroot// SPDX-License-Identifier: LGPL-2.1-or-later /* * * BlueZ - Bluetooth protocol stack for Linux * * Copyright (C) 2018-2019 Intel Corporation. All rights reserved. * * */ #ifdef HAVE_CONFIG_H #include #endif #include #include #include "src/shared/ecc.h" #include "mesh/mesh-defs.h" #include "mesh/util.h" #include "mesh/crypto.h" #include "mesh/net.h" #include "mesh/node.h" #include "mesh/model.h" #include "mesh/keyring.h" #include "mesh/prov.h" #include "mesh/provision.h" #include "mesh/pb-adv.h" #include "mesh/remprv.h" #include "mesh/mesh.h" #include "mesh/agent.h" #include "mesh/error.h" #define MIN(x, y) ((x) < (y) ? (x) : (y)) /* Quick size sanity check */ static const uint16_t expected_pdu_size[] = { 2, /* PROV_INVITE */ 12, /* PROV_CAPS */ 6, /* PROV_START */ 65, /* PROV_PUB_KEY */ 1, /* PROV_INP_CMPLT */ 17, /* PROV_CONFIRM */ 17, /* PROV_RANDOM */ 34, /* PROV_DATA */ 1, /* PROV_COMPLETE */ 2, /* PROV_FAILED */ }; #define BEACON_TYPE_UNPROVISIONED 0x00 static const uint8_t pkt_filter = MESH_AD_TYPE_PROVISION; enum int_state { INT_PROV_IDLE = 0, INT_PROV_INVITE_SENT, INT_PROV_INVITE_ACKED, INT_PROV_START_SENT, INT_PROV_START_ACKED, INT_PROV_KEY_SENT, INT_PROV_KEY_ACKED, INT_PROV_CONF_SENT, INT_PROV_CONF_ACKED, INT_PROV_RAND_SENT, INT_PROV_RAND_ACKED, INT_PROV_DATA_SENT, INT_PROV_DATA_ACKED, }; #define MAT_REMOTE_PUBLIC 0x01 #define MAT_LOCAL_PRIVATE 0x02 #define MAT_RAND_AUTH 0x04 #define MAT_SECRET (MAT_REMOTE_PUBLIC | MAT_LOCAL_PRIVATE) struct mesh_prov_initiator { mesh_prov_initiator_start_func_t start_cb; mesh_prov_initiator_complete_func_t complete_cb; mesh_prov_initiator_data_req_func_t data_req_cb; prov_trans_tx_t trans_tx; struct mesh_agent *agent; void *caller_data; void *trans_data; struct mesh_node *node; struct l_timeout *timeout; uint32_t to_secs; enum int_state state; uint16_t net_idx; uint16_t svr_idx; uint16_t unicast; uint16_t server; uint8_t transport; uint8_t material; uint8_t expected; int8_t previous; uint8_t out_num; uint8_t rpr_state; struct conf_input conf_inputs; uint8_t calc_key[16]; uint8_t salt[16]; uint8_t confirm[16]; uint8_t s_key[16]; uint8_t s_nonce[13]; uint8_t private_key[32]; uint8_t secret[32]; uint8_t rand_auth_workspace[48]; uint8_t uuid[16]; }; struct scan_req { mesh_prov_initiator_scan_result_t scan_result; struct mesh_node *node; int count; }; static struct mesh_prov_initiator *prov = NULL; static struct l_queue *scans; static void initiator_free(void) { if (prov) { l_timeout_remove(prov->timeout); if (!prov->server) mesh_send_cancel(&pkt_filter, sizeof(pkt_filter)); } pb_adv_unreg(prov); l_free(prov); prov = NULL; } static void int_prov_close(void *user_data, uint8_t reason) { struct mesh_prov_initiator *prov = user_data; struct mesh_prov_node_info info; uint8_t msg[4]; int n; if (prov->server) { n = mesh_model_opcode_set(OP_REM_PROV_LINK_CLOSE, msg); msg[n++] = reason == PROV_ERR_SUCCESS ? 0x00 : 0x02; mesh_model_send(prov->node, 0, prov->server, APP_IDX_DEV_REMOTE, prov->svr_idx, DEFAULT_TTL, true, n, msg); } if (reason != PROV_ERR_SUCCESS) { prov->complete_cb(prov->caller_data, reason, NULL); initiator_free(); return; } memcpy(info.device_key, prov->calc_key, 16); info.net_index = prov->net_idx; info.unicast = prov->unicast; info.num_ele = prov->conf_inputs.caps.num_ele; prov->complete_cb(prov->caller_data, PROV_ERR_SUCCESS, &info); initiator_free(); } static void swap_u256_bytes(uint8_t *u256) { int i; /* End-to-End byte reflection of 32 octet buffer */ for (i = 0; i < 16; i++) { u256[i] ^= u256[31 - i]; u256[31 - i] ^= u256[i]; u256[i] ^= u256[31 - i]; } } static void int_prov_open(void *user_data, prov_trans_tx_t trans_tx, void *trans_data, uint8_t transport) { struct mesh_prov_initiator *rx_prov = user_data; struct prov_invite_msg msg = { PROV_INVITE, { 30 }}; /* Only one provisioning session may be open at a time */ if (rx_prov != prov) return; /* Only one provisioning session may be open at a time */ if (prov->trans_tx && prov->trans_tx != trans_tx && prov->transport != transport) return; /* We only care here if transport does *not* match */ if (transport != prov->transport) return; /* Always use an ephemeral key when Initiator */ ecc_make_key(prov->conf_inputs.prv_pub_key, prov->private_key); swap_u256_bytes(prov->conf_inputs.prv_pub_key); swap_u256_bytes(prov->conf_inputs.prv_pub_key + 32); prov->material |= MAT_LOCAL_PRIVATE; prov->trans_tx = trans_tx; prov->trans_data = trans_data; prov->state = INT_PROV_INVITE_SENT; prov->expected = PROV_CAPS; prov->conf_inputs.invite.attention = msg.invite.attention; prov->trans_tx(prov->trans_data, &msg, sizeof(msg)); return; } static bool prov_calc_secret(const uint8_t *pub, const uint8_t *priv, uint8_t *secret) { uint8_t tmp[64]; /* Convert to ECC byte order */ memcpy(tmp, pub, 64); swap_u256_bytes(tmp); swap_u256_bytes(tmp + 32); if (!ecdh_shared_secret(tmp, priv, secret)) return false; /* Convert to Mesh byte order */ swap_u256_bytes(secret); return true; } static bool int_credentials(struct mesh_prov_initiator *prov) { if (!memcmp(prov->conf_inputs.prv_pub_key, prov->conf_inputs.dev_pub_key, 64)) return false; if (!prov_calc_secret(prov->conf_inputs.dev_pub_key, prov->private_key, prov->secret)) return false; if (!mesh_crypto_s1(&prov->conf_inputs, sizeof(prov->conf_inputs), prov->salt)) return false; if (!mesh_crypto_prov_conf_key(prov->secret, prov->salt, prov->calc_key)) return false; l_getrandom(prov->rand_auth_workspace, 16); print_packet("PublicKeyProv", prov->conf_inputs.prv_pub_key, 64); print_packet("PublicKeyDev", prov->conf_inputs.dev_pub_key, 64); /* Print DBG out in Mesh order */ swap_u256_bytes(prov->private_key); print_packet("PrivateKeyLocal", prov->private_key, 32); print_packet("ConfirmationInputs", &prov->conf_inputs, sizeof(prov->conf_inputs)); print_packet("ECDHSecret", prov->secret, 32); print_packet("LocalRandom", prov->rand_auth_workspace, 16); print_packet("ConfirmationSalt", prov->salt, 16); print_packet("ConfirmationKey", prov->calc_key, 16); return true; } static uint8_t u16_high_bit(uint16_t mask) { uint8_t cnt = 0; if (!mask) return 0xff; while (mask & 0xfffe) { cnt++; mask >>= 1; } return cnt; } static uint32_t digit_mod(uint8_t power) { uint32_t ret = 1; while (power--) ret *= 10; return ret; } static void calc_local_material(const uint8_t *random) { /* Calculate SessionKey while the data is fresh */ mesh_crypto_prov_prov_salt(prov->salt, prov->rand_auth_workspace, random, prov->salt); mesh_crypto_session_key(prov->secret, prov->salt, prov->s_key); mesh_crypto_nonce(prov->secret, prov->salt, prov->s_nonce); print_packet("SessionKey", prov->s_key, sizeof(prov->s_key)); print_packet("Nonce", prov->s_nonce, sizeof(prov->s_nonce)); } static void send_confirm(struct mesh_prov_initiator *prov) { struct prov_conf_msg msg; msg.opcode = PROV_CONFIRM; mesh_crypto_aes_cmac(prov->calc_key, prov->rand_auth_workspace, 32, msg.conf); memcpy(prov->confirm, msg.conf, sizeof(prov->confirm)); prov->trans_tx(prov->trans_data, &msg, sizeof(msg)); prov->state = INT_PROV_CONF_SENT; prov->expected = PROV_CONFIRM; } static void number_cb(void *user_data, int err, uint32_t number) { struct mesh_prov_initiator *rx_prov = user_data; struct prov_fail_msg msg; if (prov != rx_prov) return; if (err) { msg.opcode = PROV_FAILED; msg.reason = PROV_ERR_UNEXPECTED_ERR; prov->trans_tx(prov->trans_data, &msg, sizeof(msg)); return; } /* Save two copies, to generate two confirmation values */ l_put_be32(number, prov->rand_auth_workspace + 28); l_put_be32(number, prov->rand_auth_workspace + 44); prov->material |= MAT_RAND_AUTH; send_confirm(prov); } static void static_cb(void *user_data, int err, uint8_t *key, uint32_t len) { struct mesh_prov_initiator *rx_prov = user_data; struct prov_fail_msg msg; if (prov != rx_prov) return; if (err || !key || len != 16) { msg.opcode = PROV_FAILED; msg.reason = PROV_ERR_UNEXPECTED_ERR; prov->trans_tx(prov->trans_data, &msg, sizeof(msg)); return; } memcpy(prov->rand_auth_workspace + 16, key, 16); memcpy(prov->rand_auth_workspace + 32, key, 16); prov->material |= MAT_RAND_AUTH; send_confirm(prov); } static void send_pub_key(struct mesh_prov_initiator *prov) { struct prov_pub_key_msg msg; msg.opcode = PROV_PUB_KEY; memcpy(msg.pub_key, prov->conf_inputs.prv_pub_key, 64); prov->trans_tx(prov->trans_data, &msg, sizeof(msg)); prov->state = INT_PROV_KEY_SENT; } static void pub_key_cb(void *user_data, int err, uint8_t *key, uint32_t len) { struct mesh_prov_initiator *rx_prov = user_data; struct prov_fail_msg msg; uint8_t fail_code[2]; if (prov != rx_prov) return; if (err || !key || len != 64) { msg.opcode = PROV_FAILED; msg.reason = PROV_ERR_UNEXPECTED_ERR; prov->trans_tx(prov->trans_data, &msg, sizeof(msg)); return; } memcpy(prov->conf_inputs.dev_pub_key, key, 64); prov->material |= MAT_REMOTE_PUBLIC; if ((prov->material & MAT_SECRET) == MAT_SECRET) { if (!int_credentials(prov)) { fail_code[0] = PROV_FAILED; fail_code[1] = PROV_ERR_UNEXPECTED_ERR; prov->trans_tx(prov->trans_data, fail_code, 2); int_prov_close(prov, fail_code[1]); return; } } send_pub_key(prov); } static void send_random(struct mesh_prov_initiator *prov) { struct prov_rand_msg msg; msg.opcode = PROV_RANDOM; memcpy(msg.rand, prov->rand_auth_workspace, sizeof(msg.rand)); prov->trans_tx(prov->trans_data, &msg, sizeof(msg)); prov->state = INT_PROV_RAND_SENT; prov->expected = PROV_RANDOM; } void initiator_prov_data(uint16_t net_idx, uint16_t primary, void *caller_data) { struct prov_data_msg prov_data; struct prov_fail_msg prov_fail; struct keyring_net_key key; struct mesh_net *net; uint32_t iv_index; uint8_t snb_flags; if (!prov || caller_data != prov->caller_data) return; if (prov->state != INT_PROV_RAND_ACKED) return; net = node_get_net(prov->node); prov->expected = PROV_COMPLETE; /* Calculate remote device key */ mesh_crypto_device_key(prov->secret, prov->salt, prov->calc_key); print_packet("DevKey", prov->calc_key, 16); /* Fill Prov Data Structure */ if (!keyring_get_net_key(prov->node, net_idx, &key)) { prov_fail.reason = PROV_ERR_UNEXPECTED_ERR; goto failure; } prov->unicast = primary; prov->net_idx = net_idx; mesh_net_get_snb_state(net, &snb_flags, &iv_index); prov_data.opcode = PROV_DATA; if (key.phase == KEY_REFRESH_PHASE_TWO) { memcpy(&prov_data.data.net_key, key.new_key, 16); snb_flags |= PROV_FLAG_KR; } else memcpy(&prov_data.data.net_key, key.old_key, 16); l_put_be16(net_idx, &prov_data.data.net_idx); l_put_u8(snb_flags, &prov_data.data.flags); l_put_be32(iv_index, &prov_data.data.iv_index); l_put_be16(primary, &prov_data.data.primary); print_packet("ProvData", &prov_data.data, sizeof(prov_data.data)); /* Encrypt Prov Data */ mesh_crypto_aes_ccm_encrypt(prov->s_nonce, prov->s_key, NULL, 0, &prov_data.data, sizeof(prov_data.data), &prov_data.data, NULL, sizeof(prov_data.mic)); print_packet("EncdData", &prov_data.data, sizeof(prov_data) - 1); prov->trans_tx(prov->trans_data, &prov_data, sizeof(prov_data)); prov->state = INT_PROV_DATA_SENT; return; failure: l_debug("Failing... %d", prov_fail.reason); prov_fail.opcode = PROV_FAILED; prov->trans_tx(prov->trans_data, &prov_fail, sizeof(prov_fail)); /* TODO: Call Complete Callback (Fail)*/ } static void get_random_key(struct mesh_prov_initiator *prov, uint8_t action, uint8_t size) { uint32_t oob_key; int i; if (action >= PROV_ACTION_IN_ALPHA) { uint8_t alpha; char tmp[17]; memset(tmp, 0, sizeof(tmp)); if (size > 16) size = 16; /* Create random alphanumeric string made of 0-9, a-z, A-Z */ for (i = 0; i < size; i++) { l_getrandom(&alpha, sizeof(alpha)); alpha %= (10 + 26 + 26); if (alpha < 10) alpha += '0'; else if (alpha < 10 + 26) alpha += 'a' - 10; else alpha += 'A' - 10 - 26; tmp[i] = (char) alpha; } memcpy(prov->rand_auth_workspace + 16, tmp, size); memcpy(prov->rand_auth_workspace + 32, tmp, size); return; } l_getrandom(&oob_key, sizeof(oob_key)); if (action <= PROV_ACTION_TWIST) oob_key %= size; else oob_key %= digit_mod(size); if (!oob_key) oob_key = size; /* Save two copies, for two confirmation values */ l_put_be32(oob_key, prov->rand_auth_workspace + 28); l_put_be32(oob_key, prov->rand_auth_workspace + 44); } static void int_prov_auth(void) { uint8_t fail_code[2]; uint32_t oob_key; prov->state = INT_PROV_KEY_ACKED; l_debug("auth_method: %d", prov->conf_inputs.start.auth_method); memset(prov->rand_auth_workspace + 16, 0, 32); switch (prov->conf_inputs.start.auth_method) { default: case 0: /* Auth Type 3c - No OOB */ prov->material |= MAT_RAND_AUTH; break; case 1: /* Auth Type 3c - Static OOB */ /* Prompt Agent for Static OOB */ fail_code[1] = mesh_agent_request_static(prov->agent, static_cb, prov); if (fail_code[1]) goto failure; break; case 2: /* Auth Type 3a - Output OOB */ /* Prompt Agent for Output OOB */ if (prov->conf_inputs.start.auth_action == PROV_ACTION_OUT_ALPHA) { fail_code[1] = mesh_agent_prompt_alpha( prov->agent, true, static_cb, prov); } else { fail_code[1] = mesh_agent_prompt_number( prov->agent, true, prov->conf_inputs.start.auth_action, number_cb, prov); } if (fail_code[1]) goto failure; break; case 3: /* Auth Type 3b - input OOB */ get_random_key(prov, prov->conf_inputs.start.auth_action, prov->conf_inputs.start.auth_size); oob_key = l_get_be32(prov->rand_auth_workspace + 28); /* Ask Agent to Display random key */ if (prov->conf_inputs.start.auth_action == PROV_ACTION_IN_ALPHA) { fail_code[1] = mesh_agent_display_string( prov->agent, (char *) prov->rand_auth_workspace + 16, NULL, prov); } else { fail_code[1] = mesh_agent_display_number( prov->agent, true, prov->conf_inputs.start.auth_action, oob_key, NULL, prov); } if (fail_code[1]) goto failure; break; } if (prov->material & MAT_RAND_AUTH) send_confirm(prov); return; failure: l_debug("Failing... %d", fail_code[1]); fail_code[0] = PROV_FAILED; prov->trans_tx(prov->trans_data, fail_code, 2); int_prov_close(prov, fail_code[1]); } static void int_prov_start_auth(const struct mesh_agent_prov_caps *prov_caps, const struct mesh_net_prov_caps *dev_caps, struct prov_start *start) { uint8_t pub_type = prov_caps->pub_type & dev_caps->pub_type; uint8_t static_type = prov_caps->static_type & dev_caps->static_type; uint16_t output_action = prov_caps->output_action & L_BE16_TO_CPU(dev_caps->output_action); uint8_t output_size = MIN(prov_caps->output_size, dev_caps->output_size); uint16_t input_action = prov_caps->input_action & L_BE16_TO_CPU(dev_caps->input_action); uint8_t input_size = MIN(prov_caps->input_size, dev_caps->input_size); if (pub_type) start->pub_key = 0x01; /* Parse OOB Options, prefer static, then out, then in */ if (static_type) { start->auth_method = 0x01; return; } if (output_size && output_action) { start->auth_method = 0x02; start->auth_action = u16_high_bit(output_action); start->auth_size = MIN(output_size, 8); return; } if (input_size && input_action) { start->auth_method = 0x03; start->auth_action = u16_high_bit(input_action); start->auth_size = MIN(input_size, 8); return; } } static void int_prov_rx(void *user_data, const void *dptr, uint16_t len) { struct mesh_prov_initiator *rx_prov = user_data; const uint8_t *data = dptr; uint8_t *out; uint8_t type = *data++; uint8_t fail_code[2]; if (rx_prov != prov || !prov->trans_tx) return; l_debug("Provisioning packet received type: %2.2x (%u octets)", type, len); if (type == prov->previous) { l_error("Ignore repeated %2.2x packet", type); return; } else if (type > prov->expected || type < prov->previous) { l_error("Expected %2.2x, Got:%2.2x", prov->expected, type); fail_code[1] = PROV_ERR_UNEXPECTED_PDU; goto failure; } if (type >= L_ARRAY_SIZE(expected_pdu_size)) { l_error("Invalid PDU type %2.2x", type); fail_code[1] = PROV_ERR_INVALID_FORMAT; goto failure; } if (len != expected_pdu_size[type]) { l_error("Expected PDU size %d, Got %d (type: %2.2x)", expected_pdu_size[type], len, type); fail_code[1] = PROV_ERR_INVALID_FORMAT; goto failure; } switch (type) { case PROV_CAPS: /* Capabilities */ prov->state = INT_PROV_INVITE_ACKED; memcpy(&prov->conf_inputs.caps, data, sizeof(prov->conf_inputs.caps)); l_debug("Got Num Ele %d", data[0]); l_debug("Got alg %4.4x", l_get_be16(data + 1)); l_debug("Got pub_type %d", data[3]); l_debug("Got static_type %d", data[4]); l_debug("Got output_size %d", data[5]); l_debug("Got output_action %d", l_get_be16(data + 6)); l_debug("Got input_size %d", data[8]); l_debug("Got input_action %d", l_get_be16(data + 9)); if (!(l_get_be16(data + 1) & 0x0001)) { l_error("Unsupported Algorithm"); fail_code[1] = PROV_ERR_INVALID_FORMAT; goto failure; } /* * Select auth mechanism from methods supported by both * parties. */ int_prov_start_auth(mesh_agent_get_caps(prov->agent), &prov->conf_inputs.caps, &prov->conf_inputs.start); if (prov->conf_inputs.start.pub_key == 0x01) { prov->expected = PROV_CONFIRM; /* Prompt Agent for remote Public Key */ mesh_agent_request_public_key(prov->agent, pub_key_cb, prov); /* Nothing else for us to do now */ } else prov->expected = PROV_PUB_KEY; out = l_malloc(1 + sizeof(prov->conf_inputs.start)); out[0] = PROV_START; memcpy(out + 1, &prov->conf_inputs.start, sizeof(prov->conf_inputs.start)); prov->state = INT_PROV_START_SENT; prov->trans_tx(prov->trans_data, out, sizeof(prov->conf_inputs.start) + 1); l_free(out); break; case PROV_PUB_KEY: /* Public Key */ /* If we expected Pub Key Out-Of-Band, then fail */ if (prov->conf_inputs.start.pub_key) { fail_code[1] = PROV_ERR_INVALID_PDU; goto failure; } memcpy(prov->conf_inputs.dev_pub_key, data, 64); prov->material |= MAT_REMOTE_PUBLIC; prov->expected = PROV_CONFIRM; if ((prov->material & MAT_SECRET) != MAT_SECRET) return; if (!int_credentials(prov)) { fail_code[1] = PROV_ERR_UNEXPECTED_ERR; goto failure; } int_prov_auth(); break; case PROV_INP_CMPLT: /* Provisioning Input Complete */ /* TODO: Cancel Agent prompt */ prov->material |= MAT_RAND_AUTH; send_confirm(prov); break; case PROV_CONFIRM: /* Confirmation */ prov->state = INT_PROV_CONF_ACKED; /* RXed Device Confirmation */ /* Disallow echoed values */ if (!memcmp(prov->confirm, data, 16)) { fail_code[1] = PROV_ERR_INVALID_PDU; goto failure; } memcpy(prov->confirm, data, 16); print_packet("ConfirmationDevice", prov->confirm, 16); send_random(prov); break; case PROV_RANDOM: /* Random */ prov->state = INT_PROV_RAND_ACKED; /* Disallow matching random values */ if (!memcmp(prov->rand_auth_workspace, data, 16)) { fail_code[1] = PROV_ERR_INVALID_PDU; goto failure; } /* RXed Device Confirmation */ calc_local_material(data); memcpy(prov->rand_auth_workspace + 16, data, 16); print_packet("RandomDevice", data, 16); mesh_crypto_aes_cmac(prov->calc_key, prov->rand_auth_workspace + 16, 32, prov->rand_auth_workspace); print_packet("Dev-Conf", prov->rand_auth_workspace, 16); if (memcmp(prov->rand_auth_workspace, prov->confirm, 16)) { l_error("Provisioning Failed-Confirm compare"); fail_code[1] = PROV_ERR_CONFIRM_FAILED; goto failure; } if (prov->transport == PB_NPPI_00 || prov->transport == PB_NPPI_02) { /* No App data needed */ initiator_prov_data(prov->svr_idx, prov->server, prov->caller_data); } else if (!prov->data_req_cb(prov->caller_data, prov->conf_inputs.caps.num_ele)) { l_error("Provisioning Failed-Data Get"); fail_code[1] = PROV_ERR_CANT_ASSIGN_ADDR; goto failure; } break; case PROV_COMPLETE: /* Complete */ l_debug("Provisioning Complete"); prov->state = INT_PROV_IDLE; int_prov_close(prov, PROV_ERR_SUCCESS); break; case PROV_FAILED: /* Failed */ l_error("Provisioning Failed (reason: %d)", data[0]); prov->state = INT_PROV_IDLE; int_prov_close(prov, data[0]); break; default: l_error("Unknown Pkt %2.2x", type); fail_code[1] = PROV_ERR_UNEXPECTED_PDU; goto failure; } if (prov) prov->previous = type; return; failure: l_debug("Failing... %d", fail_code[1]); fail_code[0] = PROV_FAILED; prov->trans_tx(prov->trans_data, fail_code, 2); int_prov_close(prov, fail_code[1]); } static void int_prov_ack(void *user_data, uint8_t msg_num) { struct mesh_prov_initiator *rx_prov = user_data; if (rx_prov != prov || !prov->trans_tx) return; switch (prov->state) { case INT_PROV_START_SENT: prov->state = INT_PROV_START_ACKED; if (!prov->conf_inputs.start.pub_key) send_pub_key(prov); break; case INT_PROV_DATA_SENT: prov->state = INT_PROV_DATA_ACKED; break; case INT_PROV_KEY_SENT: if (prov->conf_inputs.start.pub_key) int_prov_auth(); break; case INT_PROV_IDLE: case INT_PROV_INVITE_SENT: case INT_PROV_INVITE_ACKED: case INT_PROV_START_ACKED: case INT_PROV_KEY_ACKED: case INT_PROV_CONF_SENT: case INT_PROV_CONF_ACKED: case INT_PROV_RAND_SENT: case INT_PROV_RAND_ACKED: case INT_PROV_DATA_ACKED: default: break; } } static void initiator_open_cb(void *user_data, int err) { uint8_t msg[20]; int n; bool result; if (!prov) return; if (err != MESH_ERROR_NONE) goto fail; if (prov->server) { n = mesh_model_opcode_set(OP_REM_PROV_LINK_OPEN, msg); if (prov->transport <= PB_NPPI_02) { msg[n++] = prov->transport; } else { memcpy(msg + n, prov->uuid, 16); n += 16; } result = mesh_model_send(prov->node, 0, prov->server, APP_IDX_DEV_REMOTE, prov->svr_idx, DEFAULT_TTL, true, n, msg); } else { /* Always register for PB-ADV */ result = pb_adv_reg(true, int_prov_open, int_prov_close, int_prov_rx, int_prov_ack, prov->uuid, prov); } if (!result) { err = MESH_ERROR_FAILED; goto fail; } prov->start_cb(prov->caller_data, MESH_ERROR_NONE); return; fail: prov->start_cb(prov->caller_data, err); initiator_free(); } static void initiate_to(struct l_timeout *timeout, void *user_data) { struct mesh_prov_initiator *rx_prov = user_data; if (rx_prov != prov) { l_timeout_remove(timeout); return; } int_prov_close(user_data, PROV_ERR_TIMEOUT); } bool initiator_start(uint8_t transport, uint16_t server, uint16_t svr_idx, uint8_t uuid[16], uint16_t max_ele, uint32_t timeout, struct mesh_agent *agent, mesh_prov_initiator_start_func_t start_cb, mesh_prov_initiator_data_req_func_t data_req_cb, mesh_prov_initiator_complete_func_t complete_cb, void *node, void *caller_data) { /* Invoked from Add() method in mesh-api.txt, to add a * remote unprovisioned device network. */ if (prov) return false; prov = l_new(struct mesh_prov_initiator, 1); prov->to_secs = timeout; prov->node = node; prov->agent = agent; prov->complete_cb = complete_cb; prov->start_cb = start_cb; prov->data_req_cb = data_req_cb; prov->caller_data = caller_data; prov->previous = -1; prov->server = server; prov->svr_idx = svr_idx; prov->transport = transport; prov->timeout = l_timeout_create(timeout, initiate_to, prov, NULL); memcpy(prov->uuid, uuid, 16); mesh_agent_refresh(prov->agent, initiator_open_cb, prov); return true; } void initiator_cancel(void *user_data) { initiator_free(); } static void rpr_tx(void *user_data, const void *data, uint16_t len) { struct mesh_prov_initiator *prov = user_data; uint8_t msg[72]; int n; n = mesh_model_opcode_set(OP_REM_PROV_PDU_SEND, msg); msg[n++] = ++prov->out_num; memcpy(msg + n, data, len); l_debug("Send OB %2.2x, with packet type %d", msg[n], prov->out_num); n += len; prov->rpr_state = PB_REMOTE_STATE_OB_PKT_TX; mesh_model_send(prov->node, 0, prov->server, APP_IDX_DEV_REMOTE, prov->svr_idx, DEFAULT_TTL, true, n, msg); } static bool match_req_node(const void *a, const void *b) { const struct scan_req *req = a; const struct mesh_node *node = b; return req->node == node; } static bool remprv_cli_pkt(uint16_t src, uint16_t unicast, uint16_t app_idx, uint16_t net_idx, const uint8_t *data, uint16_t size, const void *user_data) { struct mesh_node *node = (struct mesh_node *) user_data; const uint8_t *pkt = data; struct scan_req *req; uint32_t opcode; uint16_t n; if (mesh_model_opcode_get(pkt, size, &opcode, &n)) { size -= n; pkt += n; } else return false; if (opcode < OP_REM_PROV_SCAN_CAP_GET || opcode > OP_REM_PROV_PDU_REPORT) return false; if (app_idx != APP_IDX_DEV_REMOTE && app_idx != APP_IDX_DEV_LOCAL) return true; /* Local Dev key only allowed for Loop-backs */ if (app_idx == APP_IDX_DEV_LOCAL && unicast != src) return true; if (prov && (prov->server != src || prov->node != node)) return true; n = 0; switch (opcode) { default: return false; /* Provisioning Opcodes */ case OP_REM_PROV_LINK_STATUS: if (size != 2 || !prov) break; if (pkt[0] == PB_REM_ERR_SUCCESS) prov->rpr_state = pkt[1]; break; case OP_REM_PROV_LINK_REPORT: if (size != 2 || !prov) return true; if (pkt[0] != PB_REM_ERR_SUCCESS) { if (pkt[0] == PB_REM_ERR_CLOSED_BY_DEVICE || pkt[0] == PB_REM_ERR_CLOSED_BY_SERVER) int_prov_close(prov, pkt[1]); break; } if (prov->rpr_state == PB_REMOTE_STATE_LINK_OPENING) int_prov_open(prov, rpr_tx, prov, prov->transport); else if (prov->rpr_state == PB_REMOTE_STATE_LINK_CLOSING) { prov->rpr_state = PB_REMOTE_STATE_IDLE; int_prov_close(prov, pkt[1]); break; } prov->rpr_state = pkt[1]; break; case OP_REM_PROV_PDU_REPORT: int_prov_rx(prov, pkt + 1, size - 1); break; case OP_REM_PROV_PDU_OB_REPORT: if (size != 1 || !prov) break; l_debug("Got Ack for OB %d", pkt[0]); if (prov->rpr_state == PB_REMOTE_STATE_OB_PKT_TX && pkt[0] == prov->out_num) int_prov_ack(prov, pkt[0]); break; /* Scan Opcodes */ case OP_REM_PROV_SCAN_CAP_STATUS: case OP_REM_PROV_SCAN_STATUS: break; case OP_REM_PROV_SCAN_REPORT: case OP_REM_PROV_EXT_SCAN_REPORT: req = l_queue_find(scans, match_req_node, node); if (req) { req->scan_result(node, src, opcode == OP_REM_PROV_EXT_SCAN_REPORT, pkt, size); } } return true; } void initiator_scan_reg(mesh_prov_initiator_scan_result_t scan_result, void *user_data) { struct scan_req *req; if (!scans) scans = l_queue_new(); req = l_queue_find(scans, match_req_node, user_data); if (!req) { req = l_new(struct scan_req, 1); l_queue_push_head(scans, req); } req->scan_result = scan_result; req->node = user_data; req->count++; } void initiator_scan_unreg(void *user_data) { struct scan_req *req; req = l_queue_find(scans, match_req_node, user_data); if (req) { req->count--; if (!req->count) { l_queue_remove(scans, req); l_free(req); } } } static void remprv_cli_unregister(void *user_data) { } static const struct mesh_model_ops ops = { .unregister = remprv_cli_unregister, .recv = remprv_cli_pkt, .bind = NULL, .sub = NULL, .pub = NULL }; void remote_prov_client_init(struct mesh_node *node, uint8_t ele_idx) { mesh_model_register(node, ele_idx, REM_PROV_CLI_MODEL, &ops, node); } bluez-5.82/mesh/PaxHeaders/appkey.c0000644000000000000000000000005014772767672014270 xustar0020 atime=1743515579 20 ctime=1743591282 bluez-5.82/mesh/appkey.c0000644000000000000000000002236014772767672013754 0ustar00rootroot// SPDX-License-Identifier: LGPL-2.1-or-later /* * * BlueZ - Bluetooth protocol stack for Linux * * Copyright (C) 2017-2019 Intel Corporation. All rights reserved. * * */ #ifdef HAVE_CONFIG_H #include #endif #include #define _GNU_SOURCE #include #include "mesh/mesh-defs.h" #include "mesh/node.h" #include "mesh/net.h" #include "mesh/crypto.h" #include "mesh/util.h" #include "mesh/model.h" #include "mesh/mesh-config.h" #include "mesh/appkey.h" struct mesh_app_key { uint16_t net_idx; uint16_t app_idx; uint8_t key[16]; uint8_t key_aid; uint8_t new_key[16]; uint8_t new_key_aid; }; static bool match_key_index(const void *a, const void *b) { const struct mesh_app_key *key = a; uint16_t idx = L_PTR_TO_UINT(b); return key->app_idx == idx; } static bool match_bound_key(const void *a, const void *b) { const struct mesh_app_key *key = a; uint16_t idx = L_PTR_TO_UINT(b); return key->net_idx == idx; } static void finalize_key(void *a, void *b) { struct mesh_app_key *key = a; uint16_t net_idx = L_PTR_TO_UINT(b); if (key->net_idx != net_idx) return; if (key->new_key_aid == APP_AID_INVALID) return; key->key_aid = key->new_key_aid; key->new_key_aid = APP_AID_INVALID; memcpy(key->key, key->new_key, 16); } void appkey_finalize(struct mesh_net *net, uint16_t net_idx) { struct l_queue *app_keys; app_keys = mesh_net_get_app_keys(net); if (!app_keys) return; l_queue_foreach(app_keys, finalize_key, L_UINT_TO_PTR(net_idx)); } static struct mesh_app_key *app_key_new(void) { struct mesh_app_key *key = l_new(struct mesh_app_key, 1); key->new_key_aid = APP_AID_INVALID; return key; } static bool set_key(struct mesh_app_key *key, uint16_t app_idx, const uint8_t *key_value, bool is_new) { uint8_t key_aid; if (!mesh_crypto_k4(key_value, &key_aid)) return false; key_aid = KEY_ID_AKF | (key_aid << KEY_AID_SHIFT); if (!is_new) key->key_aid = key_aid; else key->new_key_aid = key_aid; memcpy(is_new ? key->new_key : key->key, key_value, 16); return true; } void appkey_key_free(void *data) { struct mesh_app_key *key = data; if (!key) return; l_free(key); } bool appkey_key_init(struct mesh_net *net, uint16_t net_idx, uint16_t app_idx, uint8_t *key_value, uint8_t *new_key_value) { struct mesh_app_key *key; struct l_queue *app_keys; if (net_idx > MAX_KEY_IDX || app_idx > MAX_KEY_IDX) return false; app_keys = mesh_net_get_app_keys(net); if (!app_keys) return NULL; if (!mesh_net_have_key(net, net_idx)) return false; key = app_key_new(); if (!key) return false; key->net_idx = net_idx; key->app_idx = app_idx; if (key_value && !set_key(key, app_idx, key_value, false)) { appkey_key_free(key); return false; } if (new_key_value && !set_key(key, app_idx, new_key_value, true)) { appkey_key_free(key); return false; } l_queue_push_tail(app_keys, key); return true; } const uint8_t *appkey_get_key(struct mesh_net *net, uint16_t app_idx, uint8_t *key_aid) { struct mesh_app_key *app_key; uint8_t phase; struct l_queue *app_keys; app_keys = mesh_net_get_app_keys(net); if (!app_keys) return NULL; app_key = l_queue_find(app_keys, match_key_index, L_UINT_TO_PTR(app_idx)); if (!app_key) return NULL; if (mesh_net_key_refresh_phase_get(net, app_key->net_idx, &phase) != MESH_STATUS_SUCCESS) return NULL; if (phase != KEY_REFRESH_PHASE_TWO) { *key_aid = app_key->key_aid; return app_key->key; } if (app_key->new_key_aid == APP_AID_INVALID) return NULL; *key_aid = app_key->new_key_aid; return app_key->new_key; } int appkey_get_key_idx(struct mesh_app_key *app_key, const uint8_t **key, uint8_t *key_aid, const uint8_t **new_key, uint8_t *new_key_aid) { if (!app_key) return -1; if (key && key_aid) { *key = app_key->key; *key_aid = app_key->key_aid; } if (new_key && new_key_aid) { *new_key = app_key->new_key; *new_key_aid = app_key->new_key_aid; } return app_key->app_idx; } bool appkey_have_key(struct mesh_net *net, uint16_t app_idx) { struct mesh_app_key *key; struct l_queue *app_keys; app_keys = mesh_net_get_app_keys(net); if (!app_keys) return false; key = l_queue_find(app_keys, match_key_index, L_UINT_TO_PTR(app_idx)); if (!key) return false; else return true; } uint16_t appkey_net_idx(struct mesh_net *net, uint16_t app_idx) { struct mesh_app_key *key; struct l_queue *app_keys; app_keys = mesh_net_get_app_keys(net); if (!app_keys) return NET_IDX_INVALID; key = l_queue_find(app_keys, match_key_index, L_UINT_TO_PTR(app_idx)); if (!key) return NET_IDX_INVALID; else return key->net_idx; } int appkey_key_update(struct mesh_net *net, uint16_t net_idx, uint16_t app_idx, const uint8_t *new_key) { struct mesh_app_key *key; struct l_queue *app_keys; uint8_t phase = KEY_REFRESH_PHASE_NONE; struct mesh_node *node; app_keys = mesh_net_get_app_keys(net); if (!app_keys) return MESH_STATUS_INSUFF_RESOURCES; if (!mesh_net_have_key(net, net_idx)) return MESH_STATUS_INVALID_NETKEY; key = l_queue_find(app_keys, match_key_index, L_UINT_TO_PTR(app_idx)); if (!key) return MESH_STATUS_INVALID_APPKEY; if (key->net_idx != net_idx) return MESH_STATUS_INVALID_BINDING; mesh_net_key_refresh_phase_get(net, net_idx, &phase); if (phase != KEY_REFRESH_PHASE_ONE) return MESH_STATUS_CANNOT_UPDATE; /* Check if the key has been already successfully updated */ if (memcmp(new_key, key->new_key, 16) == 0) return MESH_STATUS_SUCCESS; if (!set_key(key, app_idx, new_key, true)) return MESH_STATUS_INSUFF_RESOURCES; node = mesh_net_node_get(net); if (!mesh_config_app_key_update(node_config_get(node), app_idx, new_key)) return MESH_STATUS_STORAGE_FAIL; return MESH_STATUS_SUCCESS; } int appkey_key_add(struct mesh_net *net, uint16_t net_idx, uint16_t app_idx, const uint8_t *new_key) { struct mesh_app_key *key; struct l_queue *app_keys; struct mesh_node *node; app_keys = mesh_net_get_app_keys(net); if (!app_keys) return MESH_STATUS_INSUFF_RESOURCES; key = l_queue_find(app_keys, match_key_index, L_UINT_TO_PTR(app_idx)); if (key) { if (key->net_idx != net_idx) return MESH_STATUS_INVALID_NETKEY; else if (memcmp(new_key, key->key, 16) == 0) return MESH_STATUS_SUCCESS; else return MESH_STATUS_IDX_ALREADY_STORED; } if (!mesh_net_have_key(net, net_idx)) return MESH_STATUS_INVALID_NETKEY; if (l_queue_length(app_keys) >= MAX_APP_KEYS) return MESH_STATUS_INSUFF_RESOURCES; key = app_key_new(); if (!set_key(key, app_idx, new_key, false)) { appkey_key_free(key); return MESH_STATUS_INSUFF_RESOURCES; } node = mesh_net_node_get(net); if (!mesh_config_app_key_add(node_config_get(node), net_idx, app_idx, new_key)) { appkey_key_free(key); return MESH_STATUS_STORAGE_FAIL; } key->net_idx = net_idx; key->app_idx = app_idx; l_queue_push_tail(app_keys, key); return MESH_STATUS_SUCCESS; } int appkey_key_delete(struct mesh_net *net, uint16_t net_idx, uint16_t app_idx) { struct mesh_app_key *key; struct l_queue *app_keys; struct mesh_node *node; app_keys = mesh_net_get_app_keys(net); if (!app_keys) return MESH_STATUS_INVALID_APPKEY; key = l_queue_find(app_keys, match_key_index, L_UINT_TO_PTR(app_idx)); if (!key) return MESH_STATUS_SUCCESS; if (key->net_idx != net_idx) return MESH_STATUS_INVALID_NETKEY; node = mesh_net_node_get(net); node_app_key_delete(node, net_idx, app_idx); l_queue_remove(app_keys, key); appkey_key_free(key); if (!mesh_config_app_key_del(node_config_get(node), net_idx, app_idx)) return MESH_STATUS_STORAGE_FAIL; return MESH_STATUS_SUCCESS; } void appkey_delete_bound_keys(struct mesh_net *net, uint16_t net_idx) { struct l_queue *app_keys; struct mesh_node *node; struct mesh_app_key *key; app_keys = mesh_net_get_app_keys(net); if (!app_keys) return; node = mesh_net_node_get(net); key = l_queue_remove_if(app_keys, match_bound_key, L_UINT_TO_PTR(net_idx)); while (key) { node_app_key_delete(node, net_idx, key->app_idx); mesh_config_app_key_del(node_config_get(node), net_idx, key->app_idx); appkey_key_free(key); key = l_queue_remove_if(app_keys, match_bound_key, L_UINT_TO_PTR(net_idx)); } } uint8_t appkey_list(struct mesh_net *net, uint16_t net_idx, uint8_t *buf, uint16_t buf_size, uint16_t *size) { const struct l_queue_entry *entry; uint32_t idx_pair; int i; uint16_t datalen; struct l_queue *app_keys; *size = 0; if (!mesh_net_have_key(net, net_idx)) return MESH_STATUS_INVALID_NETKEY; app_keys = mesh_net_get_app_keys(net); if (!app_keys || l_queue_isempty(app_keys)) return MESH_STATUS_SUCCESS; idx_pair = 0; i = 0; datalen = 0; entry = l_queue_get_entries(app_keys); for (; entry; entry = entry->next) { struct mesh_app_key *key = entry->data; if (net_idx != key->net_idx) continue; if (!(i & 0x1)) { idx_pair = key->app_idx; } else { idx_pair <<= 12; idx_pair += key->app_idx; /* Unlikely, but check for overflow*/ if ((datalen + 3) > buf_size) { l_warn("Appkey list too large"); goto done; } l_put_le32(idx_pair, buf); buf += 3; datalen += 3; } i++; } /* Process the last app key if present */ if (i & 0x1 && ((datalen + 2) <= buf_size)) { l_put_le16(idx_pair, buf); datalen += 2; } done: *size = datalen; return MESH_STATUS_SUCCESS; } bluez-5.82/mesh/PaxHeaders/manager.h0000644000000000000000000000005014015011623014356 xustar0020 atime=1743516868 20 ctime=1743591282 bluez-5.82/mesh/manager.h0000644000000000000000000000042314015011623014036 0ustar00rootroot/* SPDX-License-Identifier: LGPL-2.1-or-later */ /* * * BlueZ - Bluetooth protocol stack for Linux * * Copyright (C) 2019 Intel Corporation. All rights reserved. * * */ bool manager_dbus_init(struct l_dbus *dbus); void manager_scan_cancel(struct mesh_node *node); bluez-5.82/mesh/PaxHeaders/mesh-io-api.h0000644000000000000000000000005014447506754015103 xustar0020 atime=1743516868 20 ctime=1743591281 bluez-5.82/mesh/mesh-io-api.h0000644000000000000000000000272014447506754014565 0ustar00rootroot/* SPDX-License-Identifier: LGPL-2.1-or-later */ /* * * BlueZ - Bluetooth protocol stack for Linux * * Copyright (C) 2018 Intel Corporation. All rights reserved. * * */ struct mesh_io_private; typedef bool (*mesh_io_init_t)(struct mesh_io *io, void *opts, void *user_data); typedef bool (*mesh_io_destroy_t)(struct mesh_io *io); typedef bool (*mesh_io_caps_t)(struct mesh_io *io, struct mesh_io_caps *caps); typedef bool (*mesh_io_send_t)(struct mesh_io *io, struct mesh_io_send_info *info, const uint8_t *data, uint16_t len); typedef bool (*mesh_io_register_t)(struct mesh_io *io, const uint8_t *filter, uint8_t len, mesh_io_recv_func_t cb, void *user_data); typedef bool (*mesh_io_deregister_t)(struct mesh_io *io, const uint8_t *filter, uint8_t len); typedef bool (*mesh_io_tx_cancel_t)(struct mesh_io *io, const uint8_t *pattern, uint8_t len); struct mesh_io_api { mesh_io_init_t init; mesh_io_destroy_t destroy; mesh_io_caps_t caps; mesh_io_send_t send; mesh_io_register_t reg; mesh_io_deregister_t dereg; mesh_io_tx_cancel_t cancel; }; struct mesh_io_reg { mesh_io_recv_func_t cb; void *user_data; uint8_t len; uint8_t filter[]; }; struct mesh_io { int index; int favored_index; mesh_io_ready_func_t ready; struct l_queue *rx_regs; struct mesh_io_private *pvt; void *user_data; const struct mesh_io_api *api; }; struct mesh_io_table { enum mesh_io_type type; const struct mesh_io_api *api; }; bluez-5.82/mesh/PaxHeaders/mesh-io.c0000644000000000000000000000005014772767672014340 xustar0020 atime=1743515579 20 ctime=1743591281 bluez-5.82/mesh/mesh-io.c0000644000000000000000000001557614772767672014037 0ustar00rootroot// SPDX-License-Identifier: LGPL-2.1-or-later /* * * BlueZ - Bluetooth protocol stack for Linux * * Copyright (C) 2018 Intel Corporation. All rights reserved. * * */ #ifdef HAVE_CONFIG_H #include #endif #include #include #include "lib/bluetooth.h" #include "lib/mgmt.h" #include "src/shared/mgmt.h" #include "mesh/mesh-defs.h" #include "mesh/mesh-mgmt.h" #include "mesh/mesh-io.h" #include "mesh/mesh-io-api.h" /* List of Mesh-IO Type headers */ #include "mesh/mesh-io-mgmt.h" #include "mesh/mesh-io-generic.h" #include "mesh/mesh-io-unit.h" struct loop_data { uint16_t len; uint8_t data[]; }; /* List of Supported Mesh-IO Types */ static const struct mesh_io_table table[] = { {MESH_IO_TYPE_MGMT, &mesh_io_mgmt}, {MESH_IO_TYPE_GENERIC, &mesh_io_generic}, {MESH_IO_TYPE_UNIT_TEST, &mesh_io_unit}, }; static const uint8_t unprv_filter[] = { MESH_AD_TYPE_BEACON, 0 }; static struct mesh_io *default_io; static struct l_timeout *loop_adv_to; static const struct mesh_io_api *io_api(enum mesh_io_type type) { uint16_t i; for (i = 0; i < L_ARRAY_SIZE(table); i++) { if (table[i].type == type) return table[i].api; } return NULL; } static void refresh_rx(void *a, void *b) { struct mesh_io_reg *rx_reg = a; struct mesh_io *io = b; if (io->api && io->api->reg) io->api->reg(io, rx_reg->filter, rx_reg->len, rx_reg->cb, rx_reg->user_data); } static void ctl_alert(int index, bool up, bool pwr, bool mesh, void *user_data) { enum mesh_io_type type = L_PTR_TO_UINT(user_data); const struct mesh_io_api *api = NULL; l_warn("index %u up:%d pwr: %d mesh: %d", index, up, pwr, mesh); /* If specific IO controller requested, honor it */ if (default_io->favored_index != MGMT_INDEX_NONE) { if (default_io->favored_index != index) return; if (!up | pwr) { l_warn("HCI%u failed to start generic IO %s", index, pwr ? ": already powered on" : ""); if (default_io->ready) default_io->ready(default_io->user_data, false); } } if (!up && default_io->index == index) { /* Our controller has disappeared */ if (default_io->api && default_io->api->destroy) { default_io->api->destroy(default_io); default_io->api = NULL; } /* Re-enumerate controllers */ mesh_mgmt_list(ctl_alert, user_data); return; } /* If we already have an API, keep using it */ if (!up || default_io->api) return; if (mesh && type != MESH_IO_TYPE_GENERIC) api = io_api(MESH_IO_TYPE_MGMT); else if (!pwr) api = io_api(MESH_IO_TYPE_GENERIC); if (api) { default_io->index = index; default_io->api = api; api->init(default_io, &index, default_io->user_data); l_queue_foreach(default_io->rx_regs, refresh_rx, default_io); } } static void free_io(struct mesh_io *io) { if (io) { if (io->api && io->api->destroy) io->api->destroy(io); l_queue_destroy(io->rx_regs, l_free); io->rx_regs = NULL; l_free(io); l_warn("Destroy %p", io); } } static struct mesh_io_reg *find_by_filter(struct l_queue *rx_regs, const uint8_t *filter, uint8_t len) { const struct l_queue_entry *entry; entry = l_queue_get_entries(rx_regs); for (; entry; entry = entry->next) { struct mesh_io_reg *rx_reg = entry->data; if (rx_reg->len == len && !memcmp(rx_reg->filter, filter, len)) return rx_reg; } return NULL; } struct mesh_io *mesh_io_new(enum mesh_io_type type, void *opts, mesh_io_ready_func_t cb, void *user_data) { const struct mesh_io_api *api = NULL; /* Only allow one IO */ if (default_io) return NULL; default_io = l_new(struct mesh_io, 1); default_io->ready = cb; default_io->user_data = user_data; default_io->favored_index = *(int *) opts; default_io->rx_regs = l_queue_new(); if (type >= MESH_IO_TYPE_AUTO) { if (!mesh_mgmt_list(ctl_alert, L_UINT_TO_PTR(type))) goto fail; return default_io; } api = io_api(type); if (!api || !api->init) goto fail; default_io->api = api; if (!api->init(default_io, opts, user_data)) goto fail; return default_io; fail: free_io(default_io); default_io = NULL; return NULL; } void mesh_io_destroy(struct mesh_io *io) { } bool mesh_io_get_caps(struct mesh_io *io, struct mesh_io_caps *caps) { if (io != default_io) return false; if (io && io->api && io->api->caps) return io->api->caps(io, caps); return false; } bool mesh_io_register_recv_cb(struct mesh_io *io, const uint8_t *filter, uint8_t len, mesh_io_recv_func_t cb, void *user_data) { struct mesh_io_reg *rx_reg; if (io == NULL) io = default_io; if (io != default_io || !cb || !filter || !len) return false; rx_reg = find_by_filter(io->rx_regs, filter, len); l_free(rx_reg); l_queue_remove(io->rx_regs, rx_reg); rx_reg = l_malloc(sizeof(struct mesh_io_reg) + len); rx_reg->cb = cb; rx_reg->len = len; rx_reg->user_data = user_data; memcpy(rx_reg->filter, filter, len); l_queue_push_head(io->rx_regs, rx_reg); if (io && io->api && io->api->reg) return io->api->reg(io, filter, len, cb, user_data); return false; } bool mesh_io_deregister_recv_cb(struct mesh_io *io, const uint8_t *filter, uint8_t len) { struct mesh_io_reg *rx_reg; if (io != default_io) return false; rx_reg = find_by_filter(io->rx_regs, filter, len); l_queue_remove(io->rx_regs, rx_reg); l_free(rx_reg); if (io && io->api && io->api->dereg) return io->api->dereg(io, filter, len); return false; } static void loop_foreach(void *data, void *user_data) { struct mesh_io_reg *rx_reg = data; struct loop_data *rx = user_data; if (!memcmp(rx_reg->filter, unprv_filter, sizeof(unprv_filter))) rx_reg->cb(rx_reg->user_data, NULL, rx->data, rx->len); } static void loop_rx(struct l_timeout *timeout, void *user_data) { struct loop_data *rx = user_data; l_queue_foreach(default_io->rx_regs, loop_foreach, rx); l_timeout_modify_ms(loop_adv_to, 500); } static void loop_destroy(void *user_data) { l_free(user_data); } static void loop_unprv_beacon(const uint8_t *data, uint16_t len) { struct loop_data *pkt = l_malloc(len + sizeof(struct loop_data)); memcpy(pkt->data, data, len); pkt->len = len; l_timeout_remove(loop_adv_to); loop_adv_to = l_timeout_create_ms(500, loop_rx, pkt, loop_destroy); } bool mesh_io_send(struct mesh_io *io, struct mesh_io_send_info *info, const uint8_t *data, uint16_t len) { if (io && io != default_io) return false; if (!io) io = default_io; /* Loop unprovisioned beacons for local clients */ if (!memcmp(data, unprv_filter, sizeof(unprv_filter))) loop_unprv_beacon(data, len); if (io && io->api && io->api->send) return io->api->send(io, info, data, len); return false; } bool mesh_io_send_cancel(struct mesh_io *io, const uint8_t *pattern, uint8_t len) { if (io && io != default_io) return false; if (!io) io = default_io; if (loop_adv_to && len >= 2 && !memcmp(pattern, unprv_filter, 2)) { l_timeout_remove(loop_adv_to); loop_adv_to = NULL; } if (io && io->api && io->api->cancel) return io->api->cancel(io, pattern, len); return false; } bluez-5.82/mesh/PaxHeaders/net-keys.h0000644000000000000000000000005014447506754014532 xustar0020 atime=1743516868 20 ctime=1743591281 bluez-5.82/mesh/net-keys.h0000644000000000000000000000301014447506754014205 0ustar00rootroot/* SPDX-License-Identifier: LGPL-2.1-or-later */ /* * * BlueZ - Bluetooth protocol stack for Linux * * Copyright (C) 2019 Intel Corporation. All rights reserved. * * */ #define BEACON_TYPE_SNB 0x01 #define BEACON_TYPE_MPB 0x02 #define KEY_REFRESH 0x01 #define IV_INDEX_UPDATE 0x02 #define NET_MPB_REFRESH_DEFAULT 60 void net_key_cleanup(void); bool net_key_confirm(uint32_t id, const uint8_t flooding[16]); bool net_key_retrieve(uint32_t id, uint8_t *flooding); uint32_t net_key_add(const uint8_t flooding[16]); uint32_t net_key_frnd_add(uint32_t flooding_id, uint16_t lpn, uint16_t frnd, uint16_t lp_cnt, uint16_t fn_cnt); void net_key_unref(uint32_t id); uint32_t net_key_decrypt(uint32_t iv_index, const uint8_t *pkt, size_t len, uint8_t **plain, size_t *plain_len); bool net_key_encrypt(uint32_t id, uint32_t iv_index, uint8_t *pkt, size_t len); uint32_t net_key_network_id(const uint8_t network[8]); uint32_t net_key_beacon(const uint8_t *data, uint16_t len, uint32_t *ivi, bool *ivu, bool *kr); bool net_key_snb_check(uint32_t id, uint32_t iv_index, bool kr, bool ivu, uint64_t cmac); bool net_key_snb_compose(uint32_t id, uint32_t iv_index, bool kr, bool ivu, uint8_t *snb); void net_key_beacon_seen(uint32_t id); bool net_key_beacon_refresh(uint32_t id, uint32_t iv_index, bool kr, bool ivu, bool force); void net_key_beacon_enable(uint32_t id, bool mpb, uint8_t refresh_count); void net_key_beacon_disable(uint32_t id, bool mpb); uint32_t net_key_beacon_last_seen(uint32_t id); bluez-5.82/mesh/PaxHeaders/crypto.c0000644000000000000000000000005014772767672014317 xustar0020 atime=1743515579 20 ctime=1743591282 bluez-5.82/mesh/crypto.c0000644000000000000000000005675614772767672014023 0ustar00rootroot// SPDX-License-Identifier: LGPL-2.1-or-later /* * * BlueZ - Bluetooth protocol stack for Linux * * Copyright (C) 2018-2019 Intel Corporation. All rights reserved. * * */ #ifdef HAVE_CONFIG_H #include #endif #define _GNU_SOURCE #include #include #include #include #include "mesh/mesh-defs.h" #include "mesh/net.h" #include "mesh/crypto.h" /* Multiply used Zero array */ static const uint8_t zero[16] = { 0, }; static bool aes_ecb_one(const uint8_t key[16], const uint8_t in[16], uint8_t out[16]) { void *cipher; bool result = false; cipher = l_cipher_new(L_CIPHER_AES, key, 16); if (cipher) { result = l_cipher_encrypt(cipher, in, out, 16); l_cipher_free(cipher); } return result; } static bool aes_cmac(void *checksum, const uint8_t *msg, size_t msg_len, uint8_t res[16]) { if (!l_checksum_update(checksum, msg, msg_len)) return false; if (16 == l_checksum_get_digest(checksum, res, 16)) return true; return false; } static bool aes_cmac_one(const uint8_t key[16], const void *msg, size_t msg_len, uint8_t res[16]) { void *checksum; bool result; checksum = l_checksum_new_cmac_aes(key, 16); if (!checksum) return false; result = l_checksum_update(checksum, msg, msg_len); if (result) { ssize_t len = l_checksum_get_digest(checksum, res, 16); result = !!(len == 16); } l_checksum_free(checksum); return result; } bool mesh_crypto_aes_cmac(const uint8_t key[16], const uint8_t *msg, size_t msg_len, uint8_t res[16]) { return aes_cmac_one(key, msg, msg_len, res); } bool mesh_crypto_aes_ccm_encrypt(const uint8_t nonce[13], const uint8_t key[16], const uint8_t *aad, uint16_t aad_len, const void *msg, uint16_t msg_len, void *out_msg, void *out_mic, size_t mic_size) { void *cipher; bool result; cipher = l_aead_cipher_new(L_AEAD_CIPHER_AES_CCM, key, 16, mic_size); result = l_aead_cipher_encrypt(cipher, msg, msg_len, aad, aad_len, nonce, 13, out_msg, msg_len + mic_size); l_aead_cipher_free(cipher); return result; } bool mesh_crypto_aes_ccm_decrypt(const uint8_t nonce[13], const uint8_t key[16], const uint8_t *aad, uint16_t aad_len, const void *enc_msg, uint16_t enc_msg_len, void *out_msg, void *out_mic, size_t mic_size) { void *cipher; bool result; size_t out_msg_len = enc_msg_len - mic_size; cipher = l_aead_cipher_new(L_AEAD_CIPHER_AES_CCM, key, 16, mic_size); result = l_aead_cipher_decrypt(cipher, enc_msg, enc_msg_len, aad, aad_len, nonce, 13, out_msg, out_msg_len); if (result && out_mic) { if (mic_size == 4) *(uint32_t *)out_mic = l_get_be32(enc_msg + enc_msg_len - mic_size); else *(uint64_t *)out_mic = l_get_be64(enc_msg + enc_msg_len - mic_size); } l_aead_cipher_free(cipher); return result; } bool mesh_crypto_k1(const uint8_t ikm[16], const uint8_t salt[16], const void *info, size_t info_len, uint8_t okm[16]) { uint8_t res[16]; if (!aes_cmac_one(salt, ikm, 16, res)) return false; return aes_cmac_one(res, info, info_len, okm); } bool mesh_crypto_k2(const uint8_t n[16], const uint8_t *p, size_t p_len, uint8_t net_id[1], uint8_t enc_key[16], uint8_t priv_key[16]) { void *checksum; uint8_t output[16]; uint8_t t[16]; uint8_t *stage; bool success = false; stage = l_malloc(sizeof(output) + p_len + 1); if (!stage) return false; if (!mesh_crypto_s1("smk2", 4, stage)) goto fail; if (!aes_cmac_one(stage, n, 16, t)) goto fail; checksum = l_checksum_new_cmac_aes(t, 16); if (!checksum) goto fail; memcpy(stage, p, p_len); stage[p_len] = 1; if (!aes_cmac(checksum, stage, p_len + 1, output)) goto done; net_id[0] = output[15] & 0x7f; memcpy(stage, output, 16); memcpy(stage + 16, p, p_len); stage[p_len + 16] = 2; if (!aes_cmac(checksum, stage, p_len + 16 + 1, output)) goto done; memcpy(enc_key, output, 16); memcpy(stage, output, 16); memcpy(stage + 16, p, p_len); stage[p_len + 16] = 3; if (!aes_cmac(checksum, stage, p_len + 16 + 1, output)) goto done; memcpy(priv_key, output, 16); success = true; done: l_checksum_free(checksum); fail: l_free(stage); return success; } static bool crypto_128(const uint8_t n[16], const char *s, uint8_t out128[16]) { const uint8_t id128[] = { 'i', 'd', '1', '2', '8', 0x01 }; uint8_t salt[16]; if (!mesh_crypto_s1(s, 4, salt)) return false; return mesh_crypto_k1(n, salt, id128, sizeof(id128), out128); } bool mesh_crypto_nkik(const uint8_t n[16], uint8_t identity_key[16]) { return crypto_128(n, "nkik", identity_key); } bool mesh_crypto_identity(const uint8_t net_key[16], uint16_t addr, uint8_t id[16]) { uint8_t id_key[16]; uint8_t tmp[16]; if (!mesh_crypto_nkik(net_key, id_key)) return false; if (!l_get_be64(id + 8)) l_getrandom(id + 8, 8); memset(tmp, 0, sizeof(tmp)); memcpy(tmp + 6, id + 8, 8); l_put_be16(addr, tmp + 14); if (!aes_ecb_one(id_key, tmp, tmp)) return false; memcpy(id, tmp + 8, 8); return true; } bool mesh_crypto_nkbk(const uint8_t n[16], uint8_t beacon_key[16]) { return crypto_128(n, "nkbk", beacon_key); } bool mesh_crypto_nkpk(const uint8_t n[16], uint8_t private_key[16]) { return crypto_128(n, "nkpk", private_key); } bool mesh_crypto_k3(const uint8_t n[16], uint8_t out64[8]) { const uint8_t id64[] = { 'i', 'd', '6', '4', 0x01 }; uint8_t tmp[16]; uint8_t t[16]; if (!mesh_crypto_s1("smk3", 4, tmp)) return false; if (!aes_cmac_one(tmp, n, 16, t)) return false; if (!aes_cmac_one(t, id64, sizeof(id64), tmp)) return false; memcpy(out64, tmp + 8, 8); return true; } bool mesh_crypto_k4(const uint8_t a[16], uint8_t out6[1]) { const uint8_t id6[] = { 'i', 'd', '6', 0x01 }; uint8_t tmp[16]; uint8_t t[16]; if (!mesh_crypto_s1("smk4", 4, tmp)) return false; if (!aes_cmac_one(tmp, a, 16, t)) return false; if (!aes_cmac_one(t, id6, sizeof(id6), tmp)) return false; out6[0] = tmp[15] & 0x3f; return true; } bool mesh_crypto_beacon_cmac(const uint8_t encryption_key[16], const uint8_t network_id[8], uint32_t iv_index, bool kr, bool iu, uint64_t *cmac) { uint8_t msg[13], tmp[16]; if (!cmac) return false; msg[0] = kr ? 0x01 : 0x00; msg[0] |= iu ? 0x02 : 0x00; memcpy(msg + 1, network_id, 8); l_put_be32(iv_index, msg + 9); if (!aes_cmac_one(encryption_key, msg, 13, tmp)) return false; *cmac = l_get_be64(tmp); return true; } static void mesh_crypto_network_nonce(bool ctl, uint8_t ttl, uint32_t seq, uint16_t src, uint32_t iv_index, uint8_t nonce[13]) { nonce[0] = 0x00; nonce[1] = (ttl & TTL_MASK) | (ctl ? CTL : 0x00); nonce[2] = (seq >> 16) & 0xff; nonce[3] = (seq >> 8) & 0xff; nonce[4] = seq & 0xff; l_put_be16(src, nonce + 5); l_put_be16(0, nonce + 7); l_put_be32(iv_index, nonce + 9); } static void mesh_crypto_application_nonce(uint32_t seq, uint16_t src, uint16_t dst, uint32_t iv_index, bool aszmic, uint8_t nonce[13]) { nonce[0] = 0x01; nonce[1] = aszmic ? 0x80 : 0x00; nonce[2] = (seq >> 16) & 0xff; nonce[3] = (seq >> 8) & 0xff; nonce[4] = seq & 0xff; l_put_be16(src, nonce + 5); l_put_be16(dst, nonce + 7); l_put_be32(iv_index, nonce + 9); } static void mesh_crypto_device_nonce(uint32_t seq, uint16_t src, uint16_t dst, uint32_t iv_index, bool aszmic, uint8_t nonce[13]) { nonce[0] = 0x02; nonce[1] = aszmic ? 0x80 : 0x00; nonce[2] = (seq >> 16) & 0xff; nonce[3] = (seq >> 8) & 0xff; nonce[4] = seq & 0xff; l_put_be16(src, nonce + 5); l_put_be16(dst, nonce + 7); l_put_be32(iv_index, nonce + 9); } static void mesh_crypto_proxy_nonce(uint32_t seq, uint16_t src, uint32_t iv_index, uint8_t nonce[13]) { nonce[0] = 0x03; nonce[1] = 0; nonce[2] = (seq >> 16) & 0xff; nonce[3] = (seq >> 8) & 0xff; nonce[4] = seq & 0xff; l_put_be16(src, nonce + 5); l_put_be16(0, nonce + 7); l_put_be32(iv_index, nonce + 9); } bool mesh_crypto_session_key(const uint8_t secret[32], const uint8_t salt[16], uint8_t session_key[16]) { const uint8_t prsk[4] = "prsk"; if (!aes_cmac_one(salt, secret, 32, session_key)) return false; return aes_cmac_one(session_key, prsk, 4, session_key); } bool mesh_crypto_nonce(const uint8_t secret[32], const uint8_t salt[16], uint8_t nonce[13]) { const uint8_t prsn[4] = "prsn"; uint8_t tmp[16]; bool result; if (!aes_cmac_one(salt, secret, 32, tmp)) return false; result = aes_cmac_one(tmp, prsn, 4, tmp); if (result) memcpy(nonce, tmp + 3, 13); return result; } bool mesh_crypto_s1(const void *info, size_t len, uint8_t salt[16]) { return aes_cmac_one(zero, info, len, salt); } bool mesh_crypto_prov_prov_salt(const uint8_t conf_salt[16], const uint8_t prov_rand[16], const uint8_t dev_rand[16], uint8_t prov_salt[16]) { uint8_t tmp[16 * 3]; memcpy(tmp, conf_salt, 16); memcpy(tmp + 16, prov_rand, 16); memcpy(tmp + 32, dev_rand, 16); return aes_cmac_one(zero, tmp, sizeof(tmp), prov_salt); } bool mesh_crypto_prov_conf_key(const uint8_t secret[32], const uint8_t salt[16], uint8_t conf_key[16]) { const uint8_t prck[4] = "prck"; if (!aes_cmac_one(salt, secret, 32, conf_key)) return false; return aes_cmac_one(conf_key, prck, 4, conf_key); } bool mesh_crypto_device_key(const uint8_t secret[32], const uint8_t salt[16], uint8_t device_key[16]) { const uint8_t prdk[4] = "prdk"; if (!aes_cmac_one(salt, secret, 32, device_key)) return false; return aes_cmac_one(device_key, prdk, 4, device_key); } bool mesh_crypto_virtual_addr(const uint8_t virtual_label[16], uint16_t *addr) { uint8_t tmp[16]; if (!mesh_crypto_s1("vtad", 4, tmp)) return false; if (!addr || !aes_cmac_one(tmp, virtual_label, 16, tmp)) return false; *addr = (l_get_be16(tmp + 14) & 0x3fff) | 0x8000; return true; } static void mesh_crypto_privacy_counter(uint32_t iv_index, const uint8_t *payload, uint8_t privacy_counter[16]) { memset(privacy_counter, 0, 5); l_put_be32(iv_index, privacy_counter + 5); memcpy(privacy_counter + 9, payload, 7); } static bool mesh_crypto_pecb(const uint8_t privacy_key[16], uint32_t iv_index, const uint8_t *payload, uint8_t pecb[16]) { mesh_crypto_privacy_counter(iv_index, payload, pecb); return aes_ecb_one(privacy_key, pecb, pecb); } static bool mesh_crypto_network_obfuscate(uint8_t *packet, const uint8_t privacy_key[16], uint32_t iv_index, bool ctl, uint8_t ttl, uint32_t seq, uint16_t src) { uint8_t pecb[16]; uint8_t *net_hdr = packet + 1; int i; if (!mesh_crypto_pecb(privacy_key, iv_index, packet + 7, pecb)) return false; l_put_be16(src, net_hdr + 4); l_put_be32(seq & SEQ_MASK, net_hdr); net_hdr[0] = ((!!ctl) << 7) | (ttl & TTL_MASK); for (i = 0; i < 6; i++) net_hdr[i] = pecb[i] ^ net_hdr[i]; return true; } static bool mesh_crypto_network_clarify(uint8_t *packet, const uint8_t privacy_key[16], uint32_t iv_index, bool *ctl, uint8_t *ttl, uint32_t *seq, uint16_t *src) { uint8_t pecb[16]; uint8_t *net_hdr = packet + 1; int i; if (!mesh_crypto_pecb(privacy_key, iv_index, packet + 7, pecb)) return false; for (i = 0; i < 6; i++) net_hdr[i] = pecb[i] ^ net_hdr[i]; *src = l_get_be16(net_hdr + 4); *seq = l_get_be32(net_hdr) & SEQ_MASK; *ttl = net_hdr[0] & TTL_MASK; *ctl = !!(net_hdr[0] & CTL); return true; } bool mesh_crypto_packet_build(bool ctl, uint8_t ttl, uint32_t seq, uint16_t src, uint16_t dst, uint8_t opcode, bool segmented, uint8_t key_aid, bool szmic, bool relay, uint16_t seqZero, uint8_t segO, uint8_t segN, const uint8_t *payload, uint8_t payload_len, uint8_t *packet, uint8_t *packet_len) { uint32_t hdr; size_t n; if (seq > SEQ_MASK) return false; packet[0] = 0; l_put_be32(seq, packet + 1); packet[1] = (ctl ? CTL : 0) | (ttl & TTL_MASK); l_put_be16(src, packet + 5); l_put_be16(dst, packet + 7); n = 9; if (!ctl) { uint32_t tmp = segmented ? 0x1 : 0; hdr = tmp << SEG_HDR_SHIFT; hdr |= (key_aid & KEY_ID_MASK) << KEY_HDR_SHIFT; if (segmented) { hdr |= szmic << SZMIC_HDR_SHIFT; hdr |= (seqZero & SEQ_ZERO_MASK) << SEQ_ZERO_HDR_SHIFT; hdr |= (segO & SEG_MASK) << SEGO_HDR_SHIFT; hdr |= (segN & SEG_MASK) << SEGN_HDR_SHIFT; } l_put_be32(hdr, packet + n); /* Only first octet is valid for unsegmented messages */ if (segmented) n += 4; else n += 1; memcpy(packet + n, payload, payload_len); l_put_be32(0x00000000, packet + payload_len + n); if (packet_len) *packet_len = payload_len + n + 4; } else { if ((opcode & OPCODE_MASK) != opcode) return false; hdr = opcode << KEY_HDR_SHIFT; l_put_be32(hdr, packet + n); n += 1; memcpy(packet + n, payload, payload_len); n += payload_len; l_put_be64(0x0000000000000000, packet + n); if (packet_len) *packet_len = n + 8; } return true; } static bool network_header_parse(const uint8_t *packet, uint8_t packet_len, bool *ctl, uint8_t *ttl, uint32_t *seq, uint16_t *src, uint16_t *dst) { if (packet_len < 10) return false; /* Try to keep bits in the order they exist within the packet */ if (ctl) *ctl = !!(packet[1] & CTL); if (ttl) *ttl = packet[1] & TTL_MASK; if (seq) *seq = l_get_be32(packet + 1) & SEQ_MASK; if (src) *src = l_get_be16(packet + 5); if (dst) *dst = l_get_be16(packet + 7); return true; } bool mesh_crypto_packet_parse(const uint8_t *packet, uint8_t packet_len, bool *ctl, uint8_t *ttl, uint32_t *seq, uint16_t *src, uint16_t *dst, uint32_t *cookie, uint8_t *opcode, bool *segmented, uint8_t *key_aid, bool *szmic, bool *relay, uint16_t *seqZero, uint8_t *segO, uint8_t *segN, const uint8_t **payload, uint8_t *payload_len) { uint32_t hdr; uint16_t this_dst; bool is_segmented; if (!network_header_parse(packet, packet_len, ctl, ttl, seq, src, &this_dst)) return false; if (dst) *dst = this_dst; hdr = l_get_be32(packet + 9); is_segmented = !!((hdr >> SEG_HDR_SHIFT) & true); if (segmented) *segmented = is_segmented; if (packet[1] & CTL) { uint8_t this_opcode = packet[9] & OPCODE_MASK; if (cookie) *cookie = l_get_be32(packet + 2) ^ packet[6]; if (opcode) *opcode = this_opcode; if (this_dst && this_opcode == NET_OP_SEG_ACKNOWLEDGE) { if (relay) *relay = !!((hdr >> RELAY_HDR_SHIFT) & true); if (seqZero) *seqZero = (hdr >> SEQ_ZERO_HDR_SHIFT) & SEQ_ZERO_MASK; if (payload) *payload = packet + 9; if (payload_len) *payload_len = packet_len - 9; } else { if (payload) *payload = packet + 10; if (payload_len) *payload_len = packet_len - 10; } } else { if (cookie) *cookie = l_get_be32(packet + packet_len - 8); if (key_aid) *key_aid = (hdr >> KEY_HDR_SHIFT) & KEY_ID_MASK; if (is_segmented) { if (szmic) *szmic = !!((hdr >> SZMIC_HDR_SHIFT) & true); if (seqZero) *seqZero = (hdr >> SEQ_ZERO_HDR_SHIFT) & SEQ_ZERO_MASK; if (segO) *segO = (hdr >> SEGO_HDR_SHIFT) & SEG_MASK; if (segN) *segN = (hdr >> SEGN_HDR_SHIFT) & SEG_MASK; if (payload) *payload = packet + 13; if (payload_len) *payload_len = packet_len - 13; } else { if (payload) *payload = packet + 10; if (payload_len) *payload_len = packet_len - 10; } } return true; } bool mesh_crypto_payload_encrypt(uint8_t *aad, const uint8_t *payload, uint8_t *out, uint16_t payload_len, uint16_t src, uint16_t dst, uint8_t key_aid, uint32_t seq, uint32_t iv_index, bool aszmic, const uint8_t app_key[16]) { uint8_t nonce[13]; if (payload_len < 1) return false; if (key_aid == APP_AID_DEV) mesh_crypto_device_nonce(seq, src, dst, iv_index, aszmic, nonce); else mesh_crypto_application_nonce(seq, src, dst, iv_index, aszmic, nonce); if (!mesh_crypto_aes_ccm_encrypt(nonce, app_key, aad, aad ? 16 : 0, payload, payload_len, out, NULL, aszmic ? 8 : 4)) return false; return true; } bool mesh_crypto_payload_decrypt(uint8_t *aad, uint16_t aad_len, const uint8_t *payload, uint16_t payload_len, bool aszmic, uint16_t src, uint16_t dst, uint8_t key_aid, uint32_t seq, uint32_t iv_index, uint8_t *out, const uint8_t app_key[16]) { uint8_t nonce[13]; uint32_t mic32; uint64_t mic64; if (payload_len < 5 || !out) return false; if (key_aid == APP_AID_DEV) mesh_crypto_device_nonce(seq, src, dst, iv_index, aszmic, nonce); else mesh_crypto_application_nonce(seq, src, dst, iv_index, aszmic, nonce); memcpy(out, payload, payload_len); if (aszmic) { if (!mesh_crypto_aes_ccm_decrypt(nonce, app_key, aad, aad_len, payload, payload_len, out, &mic64, sizeof(mic64))) return false; mic64 ^= l_get_be64(out + payload_len - 8); l_put_be64(mic64, out + payload_len - 8); if (mic64) return false; } else { if (!mesh_crypto_aes_ccm_decrypt(nonce, app_key, aad, aad_len, payload, payload_len, out, &mic32, sizeof(mic32))) return false; mic32 ^= l_get_be32(out + payload_len - 4); l_put_be32(mic32, out + payload_len - 4); if (mic32) return false; } return true; } static bool mesh_crypto_packet_encrypt(uint8_t *packet, uint8_t packet_len, const uint8_t network_key[16], uint32_t iv_index, bool proxy, bool ctl, uint8_t ttl, uint32_t seq, uint16_t src) { uint8_t nonce[13]; /* Detect Proxy packet by CTL == true && DST == 0x0000 */ if (ctl && proxy) mesh_crypto_proxy_nonce(seq, src, iv_index, nonce); else mesh_crypto_network_nonce(ctl, ttl, seq, src, iv_index, nonce); /* Check for Long net-MIC */ if (ctl) { if (!mesh_crypto_aes_ccm_encrypt(nonce, network_key, NULL, 0, packet + 7, packet_len - 7 - 8, packet + 7, NULL, 8)) return false; } else { if (!mesh_crypto_aes_ccm_encrypt(nonce, network_key, NULL, 0, packet + 7, packet_len - 7 - 4, packet + 7, NULL, 4)) return false; } return true; } bool mesh_crypto_packet_encode(uint8_t *packet, uint8_t packet_len, uint32_t iv_index, const uint8_t network_key[16], const uint8_t privacy_key[16]) { bool ctl; uint8_t ttl; uint32_t seq; uint16_t src; uint16_t dst; if (!network_header_parse(packet, packet_len, &ctl, &ttl, &seq, &src, &dst)) return false; if (!mesh_crypto_packet_encrypt(packet, packet_len, network_key, iv_index, !dst, ctl, ttl, seq, src)) return false; return mesh_crypto_network_obfuscate(packet, privacy_key, iv_index, ctl, ttl, seq, src); } static bool mesh_crypto_packet_decrypt(uint8_t *packet, uint8_t packet_len, const uint8_t network_key[16], uint32_t iv_index, bool proxy, bool ctl, uint8_t ttl, uint32_t seq, uint16_t src) { uint8_t nonce[13]; /* Pre-check SRC address for illegal values */ if (!IS_UNICAST(src)) return false; /* Detect Proxy packet by CTL == true && proxy == true */ if (ctl & proxy) mesh_crypto_proxy_nonce(seq, src, iv_index, nonce); else mesh_crypto_network_nonce(ctl, ttl, seq, src, iv_index, nonce); /* Check for Long MIC */ if (ctl) { uint64_t mic; if (!mesh_crypto_aes_ccm_decrypt(nonce, network_key, NULL, 0, packet + 7, packet_len - 7, packet + 7, &mic, sizeof(mic))) return false; mic ^= l_get_be64(packet + packet_len - 8); l_put_be64(mic, packet + packet_len - 8); if (mic) return false; } else { uint32_t mic; if (!mesh_crypto_aes_ccm_decrypt(nonce, network_key, NULL, 0, packet + 7, packet_len - 7, packet + 7, &mic, sizeof(mic))) return false; mic ^= l_get_be32(packet + packet_len - 4); l_put_be32(mic, packet + packet_len - 4); if (mic) return false; } return true; } bool mesh_crypto_packet_decode(const uint8_t *packet, uint8_t packet_len, bool proxy, uint8_t *out, uint32_t iv_index, const uint8_t network_key[16], const uint8_t privacy_key[16]) { bool ctl; uint8_t ttl; uint32_t seq; uint16_t src; if (packet_len < 14) return false; memcpy(out, packet, packet_len); if (!mesh_crypto_network_clarify(out, privacy_key, iv_index, &ctl, &ttl, &seq, &src)) return false; return mesh_crypto_packet_decrypt(out, packet_len, network_key, iv_index, proxy, ctl, ttl, seq, src); } bool mesh_crypto_packet_label(uint8_t *packet, uint8_t packet_len, uint16_t iv_index, uint8_t network_id) { packet[0] = (iv_index & 0x0001) << 7 | (network_id & 0x7f); return true; } /* reversed, 8-bit, poly=0x07 */ static const uint8_t crc_table[256] = { 0x00, 0x91, 0xe3, 0x72, 0x07, 0x96, 0xe4, 0x75, 0x0e, 0x9f, 0xed, 0x7c, 0x09, 0x98, 0xea, 0x7b, 0x1c, 0x8d, 0xff, 0x6e, 0x1b, 0x8a, 0xf8, 0x69, 0x12, 0x83, 0xf1, 0x60, 0x15, 0x84, 0xf6, 0x67, 0x38, 0xa9, 0xdb, 0x4a, 0x3f, 0xae, 0xdc, 0x4d, 0x36, 0xa7, 0xd5, 0x44, 0x31, 0xa0, 0xd2, 0x43, 0x24, 0xb5, 0xc7, 0x56, 0x23, 0xb2, 0xc0, 0x51, 0x2a, 0xbb, 0xc9, 0x58, 0x2d, 0xbc, 0xce, 0x5f, 0x70, 0xe1, 0x93, 0x02, 0x77, 0xe6, 0x94, 0x05, 0x7e, 0xef, 0x9d, 0x0c, 0x79, 0xe8, 0x9a, 0x0b, 0x6c, 0xfd, 0x8f, 0x1e, 0x6b, 0xfa, 0x88, 0x19, 0x62, 0xf3, 0x81, 0x10, 0x65, 0xf4, 0x86, 0x17, 0x48, 0xd9, 0xab, 0x3a, 0x4f, 0xde, 0xac, 0x3d, 0x46, 0xd7, 0xa5, 0x34, 0x41, 0xd0, 0xa2, 0x33, 0x54, 0xc5, 0xb7, 0x26, 0x53, 0xc2, 0xb0, 0x21, 0x5a, 0xcb, 0xb9, 0x28, 0x5d, 0xcc, 0xbe, 0x2f, 0xe0, 0x71, 0x03, 0x92, 0xe7, 0x76, 0x04, 0x95, 0xee, 0x7f, 0x0d, 0x9c, 0xe9, 0x78, 0x0a, 0x9b, 0xfc, 0x6d, 0x1f, 0x8e, 0xfb, 0x6a, 0x18, 0x89, 0xf2, 0x63, 0x11, 0x80, 0xf5, 0x64, 0x16, 0x87, 0xd8, 0x49, 0x3b, 0xaa, 0xdf, 0x4e, 0x3c, 0xad, 0xd6, 0x47, 0x35, 0xa4, 0xd1, 0x40, 0x32, 0xa3, 0xc4, 0x55, 0x27, 0xb6, 0xc3, 0x52, 0x20, 0xb1, 0xca, 0x5b, 0x29, 0xb8, 0xcd, 0x5c, 0x2e, 0xbf, 0x90, 0x01, 0x73, 0xe2, 0x97, 0x06, 0x74, 0xe5, 0x9e, 0x0f, 0x7d, 0xec, 0x99, 0x08, 0x7a, 0xeb, 0x8c, 0x1d, 0x6f, 0xfe, 0x8b, 0x1a, 0x68, 0xf9, 0x82, 0x13, 0x61, 0xf0, 0x85, 0x14, 0x66, 0xf7, 0xa8, 0x39, 0x4b, 0xda, 0xaf, 0x3e, 0x4c, 0xdd, 0xa6, 0x37, 0x45, 0xd4, 0xa1, 0x30, 0x42, 0xd3, 0xb4, 0x25, 0x57, 0xc6, 0xb3, 0x22, 0x50, 0xc1, 0xba, 0x2b, 0x59, 0xc8, 0xbd, 0x2c, 0x5e, 0xcf }; uint8_t mesh_crypto_compute_fcs(const uint8_t *packet, uint8_t packet_len) { uint8_t fcs = 0xff; int i; for (i = 0; i < packet_len; i++) fcs = crc_table[fcs ^ packet[i]]; return 0xff - fcs; } bool mesh_crypto_check_fcs(const uint8_t *packet, uint8_t packet_len, uint8_t received_fcs) { uint8_t fcs = 0xff; int i; for (i = 0; i < packet_len; i++) fcs = crc_table[fcs ^ packet[i]]; fcs = crc_table[fcs ^ received_fcs]; return fcs == 0xcf; } /* This function performs a quick-check of ELL and Kernel AEAD encryption. * Some kernel versions before v4.9 have a known AEAD bug. If the system * running this test is using a v4.8 or earlier kernel, a failure here is * likely unless AEAD encryption has been backported. */ static const uint8_t crypto_test_result[] = { 0x75, 0x03, 0x7e, 0xe2, 0x89, 0x81, 0xbe, 0x59, 0xbc, 0xe6, 0xdd, 0x23, 0x63, 0x5b, 0x16, 0x61, 0xb7, 0x23, 0x92, 0xd4, 0x86, 0xee, 0x84, 0x29, 0x9a, 0x2a, 0xbf, 0x96 }; bool mesh_crypto_check_avail(void) { void *cipher; bool result; uint8_t i; union { struct { uint8_t key[16]; uint8_t aad[16]; uint8_t nonce[13]; uint8_t data[20]; uint8_t mic[8]; } crypto; uint8_t bytes[73]; } u; uint8_t out_msg[sizeof(u.crypto.data) + sizeof(u.crypto.mic)]; l_debug("Testing Crypto"); for (i = 0; i < sizeof(u); i++) { u.bytes[i] = 0x60 + i; } cipher = l_aead_cipher_new(L_AEAD_CIPHER_AES_CCM, u.crypto.key, sizeof(u.crypto.key), sizeof(u.crypto.mic)); if (!cipher) return false; result = l_aead_cipher_encrypt(cipher, u.crypto.data, sizeof(u.crypto.data), u.crypto.aad, sizeof(u.crypto.aad), u.crypto.nonce, sizeof(u.crypto.nonce), out_msg, sizeof(out_msg)); if (result) result = !memcmp(out_msg, crypto_test_result, sizeof(out_msg)); l_aead_cipher_free(cipher); return result; } bluez-5.82/mesh/PaxHeaders/pb-adv.c0000644000000000000000000000005014772767672014150 xustar0020 atime=1743515579 20 ctime=1743591282 bluez-5.82/mesh/pb-adv.c0000644000000000000000000003013114772767672013627 0ustar00rootroot// SPDX-License-Identifier: LGPL-2.1-or-later /* * * BlueZ - Bluetooth protocol stack for Linux * * Copyright (C) 2018-2019 Intel Corporation. All rights reserved. * * */ #ifdef HAVE_CONFIG_H #include #endif #include #include #include "mesh/mesh-defs.h" #include "mesh/crypto.h" #include "mesh/net.h" #include "mesh/mesh-io.h" #include "mesh/mesh.h" #include "mesh/prov.h" #include "mesh/provision.h" #include "mesh/pb-adv.h" #include "mesh/util.h" struct pb_adv_session { mesh_prov_open_func_t open_cb; mesh_prov_close_func_t close_cb; mesh_prov_receive_func_t rx_cb; mesh_prov_ack_func_t ack_cb; struct l_timeout *tx_timeout; struct pb_adv_session *loop; uint32_t link_id; uint16_t exp_len; uint8_t exp_fcs; uint8_t exp_segs; uint8_t got_segs; uint8_t trans_num; uint8_t local_acked; uint8_t local_trans_num; uint8_t peer_trans_num; uint8_t last_peer_trans_num; uint8_t sar[80]; uint8_t uuid[16]; bool initiator; bool opened; void *user_data; }; #define PB_ADV_ACK 0x01 #define PB_ADV_OPEN_REQ 0x03 #define PB_ADV_OPEN_CFM 0x07 #define PB_ADV_CLOSE 0x0B #define PB_ADV_MTU 24 struct pb_ack { uint8_t ad_type; uint32_t link_id; uint8_t trans_num; uint8_t opcode; } __packed; struct pb_open_req{ uint8_t ad_type; uint32_t link_id; uint8_t trans_num; uint8_t opcode; uint8_t uuid[16]; } __packed; struct pb_open_cfm{ uint8_t ad_type; uint32_t link_id; uint8_t trans_num; uint8_t opcode; } __packed; struct pb_close_ind { uint8_t ad_type; uint32_t link_id; uint8_t trans_num; uint8_t opcode; uint8_t reason; } __packed; struct idle_rx { struct pb_adv_session *session; uint16_t len; uint8_t data[PB_ADV_MTU + 6]; }; static struct l_queue *pb_sessions = NULL; static const uint8_t filter[1] = { MESH_AD_TYPE_PROVISION }; static void pb_adv_packet(void *user_data, const uint8_t *pkt, uint16_t len); static void idle_rx_adv(void *user_data) { struct idle_rx *rx = user_data; pb_adv_packet(rx->session, rx->data, rx->len); l_free(rx); } static void pb_adv_send(struct pb_adv_session *session, uint8_t count, uint16_t interval, void *data, uint16_t len) { struct idle_rx *rx; if (session->loop) { rx = l_new(struct idle_rx, 1); rx->session = session->loop; rx->len = len; memcpy(rx->data, data, len); l_idle_oneshot(idle_rx_adv, rx, NULL); } else mesh_send_pkt(count, interval, data, len); } static void send_adv_segs(struct pb_adv_session *session, const uint8_t *data, uint16_t size) { uint16_t init_size; uint8_t buf[PB_ADV_MTU + 6] = { MESH_AD_TYPE_PROVISION }; uint8_t max_seg; uint8_t consumed; int i; if (!size) return; mesh_send_cancel(filter, sizeof(filter)); l_put_be32(session->link_id, buf + 1); buf[1 + 4] = ++session->local_trans_num; if (size > PB_ADV_MTU - 4) { max_seg = 1 + (((size - (PB_ADV_MTU - 4)) - 1) / (PB_ADV_MTU - 1)); init_size = PB_ADV_MTU - 4; } else { max_seg = 0; init_size = size; } l_debug("Sending %u fragments for %u octets", max_seg + 1, size); buf[6] = max_seg << 2; l_put_be16(size, buf + 7); buf[9] = mesh_crypto_compute_fcs(data, size); memcpy(buf + 10, data, init_size); l_debug("max_seg: %2.2x", max_seg); l_debug("size: %2.2x, CRC: %2.2x", size, buf[9]); pb_adv_send(session, MESH_IO_TX_COUNT_UNLIMITED, 500, buf, init_size + 10); consumed = init_size; for (i = 1; i <= max_seg; i++) { size_t seg_size; /* Amount of payload data being sent */ if (size - consumed > PB_ADV_MTU - 1) seg_size = PB_ADV_MTU - 1; else seg_size = size - consumed; buf[6] = (i << 2) | 0x02; memcpy(buf + 7, data + consumed, seg_size); pb_adv_send(session, MESH_IO_TX_COUNT_UNLIMITED, 500, buf, seg_size + 7); consumed += seg_size; } } static bool session_match (const void *a, const void *b) { return a == b; } static bool uuid_match (const void *a, const void *b) { const struct pb_adv_session *session = a; const uint8_t *uuid = b; return !memcmp(session->uuid, uuid, sizeof(session->uuid)); } static bool user_match (const void *a, const void *b) { const struct pb_adv_session *session = a; return session->user_data == b; } static void tx_timeout(struct l_timeout *timeout, void *user_data) { struct pb_adv_session *session = user_data; mesh_prov_close_func_t cb; if (!l_queue_find(pb_sessions, session_match, session)) return; mesh_send_cancel(filter, sizeof(filter)); l_debug("TX timeout"); cb = session->close_cb; user_data = session->user_data; cb(user_data, 1); } static void pb_adv_tx(void *user_data, const void *data, uint16_t len) { struct pb_adv_session *session = user_data; if (!l_queue_find(pb_sessions, session_match, session)) return; l_timeout_remove(session->tx_timeout); session->tx_timeout = l_timeout_create(30, tx_timeout, session, NULL); send_adv_segs(session, data, len); } static void send_open_req(struct pb_adv_session *session) { struct pb_open_req open_req = { MESH_AD_TYPE_PROVISION }; l_put_be32(session->link_id, &open_req.link_id); open_req.trans_num = 0; open_req.opcode = PB_ADV_OPEN_REQ; memcpy(open_req.uuid, session->uuid, 16); mesh_send_cancel(filter, sizeof(filter)); pb_adv_send(session, MESH_IO_TX_COUNT_UNLIMITED, 500, &open_req, sizeof(open_req)); } static void send_open_cfm(struct pb_adv_session *session) { struct pb_open_cfm open_cfm = { MESH_AD_TYPE_PROVISION }; l_put_be32(session->link_id, &open_cfm.link_id); open_cfm.trans_num = 0; open_cfm.opcode = PB_ADV_OPEN_CFM; mesh_send_cancel(filter, sizeof(filter)); pb_adv_send(session, MESH_IO_TX_COUNT_UNLIMITED, 500, &open_cfm, sizeof(open_cfm)); } static void send_ack(struct pb_adv_session *session, uint8_t trans_num) { struct pb_ack ack = { MESH_AD_TYPE_PROVISION }; if (!l_queue_find(pb_sessions, session_match, session)) return; l_put_be32(session->link_id, &ack.link_id); ack.trans_num = trans_num; ack.opcode = PB_ADV_ACK; pb_adv_send(session, MESH_IO_TX_COUNT_UNLIMITED, 500, &ack, sizeof(ack)); } static void send_close_ind(struct pb_adv_session *session, uint8_t reason) { struct pb_close_ind close_ind = { MESH_AD_TYPE_PROVISION }; if (!l_queue_find(pb_sessions, session_match, session)) return; l_put_be32(session->link_id, &close_ind.link_id); close_ind.trans_num = 0; close_ind.opcode = PB_ADV_CLOSE; close_ind.reason = reason; mesh_send_cancel(filter, sizeof(filter)); pb_adv_send(session, 10, 100, &close_ind, sizeof(close_ind)); } static void pb_adv_packet(void *user_data, const uint8_t *pkt, uint16_t len) { struct pb_adv_session *session = user_data; uint32_t link_id; size_t offset; uint8_t trans_num; uint8_t type; bool first; if (!l_queue_find(pb_sessions, session_match, session)) return; link_id = l_get_be32(pkt + 1); type = l_get_u8(pkt + 6); /* Validate new or existing Connection ID */ if (session->link_id) { if (session->link_id != link_id) return; } else if (type != 0x03) return; else if (!link_id) return; trans_num = l_get_u8(pkt + 5); pkt += 7; len -= 7; switch (type) { case PB_ADV_OPEN_CFM: /* * Ignore if: * 1. We are acceptor * 2. We are already provisioning on different link_id */ if (!session->initiator) return; first = !session->opened; session->opened = true; /* Only call Open callback once */ if (first) { l_debug("PB-ADV open confirmed"); session->local_trans_num = 0xFF; session->open_cb(session->user_data, pb_adv_tx, session, PB_ADV); } return; case PB_ADV_OPEN_REQ: /* * Ignore if: * 1. We are initiator * 2. Open request not addressed to us * 3. We are already provisioning on different link_id */ if (session->initiator) return; if (memcmp(pkt, session->uuid, 16)) return; first = !session->link_id; session->link_id = link_id; session->last_peer_trans_num = 0xFF; session->peer_trans_num = 0x00; session->local_trans_num = 0x7F; session->opened = true; /* Only call Open callback once */ if (first) { l_debug("PB-ADV open requested"); session->open_cb(session->user_data, pb_adv_tx, session, PB_ADV); } /* Send CFM once per received request */ send_open_cfm(session); break; case PB_ADV_CLOSE: l_debug("Link closed notification: %2.2x", pkt[0]); session->close_cb(session->user_data, pkt[0]); break; case PB_ADV_ACK: if (!session->opened) return; if (trans_num != session->local_trans_num) return; if (session->local_acked > trans_num) return; mesh_send_cancel(filter, sizeof(filter)); session->local_acked = trans_num; session->ack_cb(session->user_data, trans_num); break; default: /* DATA SEGMENT */ if (!session->opened) return; if (trans_num == session->last_peer_trans_num) { send_ack(session, trans_num); return; } switch(type & 0x03) { case 0x00: session->peer_trans_num = trans_num; session->exp_len = l_get_be16(pkt); l_debug("PB-ADV start with %u fragments, %d octets", type >> 2, session->exp_len); if (session->exp_len > sizeof(session->sar)) { l_debug("Incoming length exceeded: %d", session->exp_len); return; } session->exp_fcs = l_get_u8(pkt + 2); session->exp_segs = 0xff >> (7 - (type >> 2)); /* Save first segment */ memcpy(session->sar, pkt + 3, len - 3); session->got_segs |= 1; break; case 0x02: session->peer_trans_num = trans_num; offset = 20 + (((type >> 2) - 1) * 23); if (offset + len - 3 > sizeof(session->sar)) { l_debug("Length exceeded: %d", session->exp_len); return; } l_debug("Processing fragment %u", type >> 2); memcpy(session->sar + offset, pkt, len); session->got_segs |= 1 << (type >> 2); break; default: /* Malformed or unrecognized */ return; } if (session->got_segs != session->exp_segs) return; /* Validate RXed packet and pass up to Provisioning */ if (!mesh_crypto_check_fcs(session->sar, session->exp_len, session->exp_fcs)) { /* This can be a false negative if first * segment missed, and can almost always * be ignored. */ l_debug("Invalid FCS"); return; } send_ack(session, session->peer_trans_num); if (session->last_peer_trans_num != session->peer_trans_num) { session->got_segs = 0; session->last_peer_trans_num = session->peer_trans_num; session->rx_cb(session->user_data, session->sar, session->exp_len); } } } bool pb_adv_reg(bool initiator, mesh_prov_open_func_t open_cb, mesh_prov_close_func_t close_cb, mesh_prov_receive_func_t rx_cb, mesh_prov_ack_func_t ack_cb, const uint8_t *uuid, void *user_data) { struct pb_adv_session *session, *old_session; if (!pb_sessions) pb_sessions = l_queue_new(); old_session = l_queue_find(pb_sessions, uuid_match, uuid); /* Reject 2nd session if not looping back */ if (l_queue_length(pb_sessions) && !old_session) return false; /* Reject looping to more than one session or with same role*/ if (old_session && (old_session->loop || old_session->initiator == initiator)) return false; session = l_new(struct pb_adv_session, 1); session->open_cb = open_cb; session->close_cb = close_cb; session->rx_cb = rx_cb; session->ack_cb = ack_cb; session->user_data = user_data; session->initiator = initiator; memcpy(session->uuid, uuid, 16); l_queue_push_head(pb_sessions, session); if (initiator) { l_getrandom(&session->link_id, sizeof(session->link_id)); session->tx_timeout = l_timeout_create(60, tx_timeout, session, NULL); } /* Setup Loop-back if complementary session with same UUID */ if (old_session) { session->loop = old_session; old_session->loop = session; mesh_unreg_prov_rx(pb_adv_packet); if (initiator) send_open_req(session); else send_open_req(old_session); return true; } mesh_reg_prov_rx(pb_adv_packet, session); if (initiator) send_open_req(session); return true; } void pb_adv_unreg(void *user_data) { struct pb_adv_session *session = l_queue_find(pb_sessions, user_match, user_data); if (!session) return; l_timeout_remove(session->tx_timeout); session->tx_timeout = NULL; send_close_ind(session, 0); l_queue_remove(pb_sessions, session); l_free(session); if (!l_queue_length(pb_sessions)) { l_queue_destroy(pb_sessions, l_free); pb_sessions = NULL; } } bluez-5.82/mesh/PaxHeaders/mesh.h0000644000000000000000000000005014333256742013720 xustar0020 atime=1743516867 20 ctime=1743591281 bluez-5.82/mesh/mesh.h0000644000000000000000000000317214333256742013404 0ustar00rootroot/* SPDX-License-Identifier: LGPL-2.1-or-later */ /* * * BlueZ - Bluetooth protocol stack for Linux * * Copyright (C) 2018-2019 Intel Corporation. All rights reserved. * * */ #define BLUEZ_MESH_NAME "org.bluez.mesh" #define MESH_NETWORK_INTERFACE "org.bluez.mesh.Network1" #define MESH_NODE_INTERFACE "org.bluez.mesh.Node1" #define MESH_MANAGEMENT_INTERFACE "org.bluez.mesh.Management1" #define MESH_ELEMENT_INTERFACE "org.bluez.mesh.Element1" #define MESH_APPLICATION_INTERFACE "org.bluez.mesh.Application1" #define MESH_PROVISION_AGENT_INTERFACE "org.bluez.mesh.ProvisionAgent1" #define MESH_PROVISIONER_INTERFACE "org.bluez.mesh.Provisioner1" #define ERROR_INTERFACE "org.bluez.mesh.Error" enum mesh_io_type; typedef void (*mesh_ready_func_t)(void *user_data, bool success); typedef void (*prov_rx_cb_t)(void *user_data, const uint8_t *data, uint16_t len); bool mesh_init(const char *config_dir, const char *mesh_conf_fname, enum mesh_io_type type, void *opts, mesh_ready_func_t cb, void *user_data); void mesh_cleanup(bool signaled); bool mesh_dbus_init(struct l_dbus *dbus); const char *mesh_status_str(uint8_t err); bool mesh_send_pkt(uint8_t count, uint16_t interval, void *data, uint16_t len); bool mesh_send_cancel(const uint8_t *filter, uint8_t len); bool mesh_reg_prov_rx(prov_rx_cb_t cb, void *user_data); void mesh_unreg_prov_rx(prov_rx_cb_t cb); const char *mesh_prov_status_str(uint8_t status); const char *mesh_get_storage_dir(void); bool mesh_beacon_enabled(void); bool mesh_relay_supported(void); bool mesh_friendship_supported(void); uint16_t mesh_get_crpl(void); uint8_t mesh_get_friend_queue_size(void); bluez-5.82/mesh/PaxHeaders/org.bluez.mesh.service0000644000000000000000000000005013540747027017037 xustar0020 atime=1743516876 20 ctime=1743591289 bluez-5.82/mesh/org.bluez.mesh.service0000644000000000000000000000015113540747027016515 0ustar00rootroot[D-BUS Service] Name=org.bluez.mesh Exec=/bin/false User=root SystemdService=dbus-org.bluez.mesh.service bluez-5.82/mesh/PaxHeaders/net.c0000644000000000000000000000005014772767672013565 xustar0020 atime=1743515579 20 ctime=1743591282 bluez-5.82/mesh/net.c0000644000000000000000000026334214772767672013260 0ustar00rootroot// SPDX-License-Identifier: LGPL-2.1-or-later /* * * BlueZ - Bluetooth protocol stack for Linux * * Copyright (C) 2018-2019 Intel Corporation. All rights reserved. * * */ #ifdef HAVE_CONFIG_H #include #endif #define _GNU_SOURCE #include #include #include #include "mesh/mesh-defs.h" #include "mesh/util.h" #include "mesh/crypto.h" #include "mesh/net-keys.h" #include "mesh/node.h" #include "mesh/net.h" #include "mesh/mesh-io.h" #include "mesh/friend.h" #include "mesh/mesh-config.h" #include "mesh/model.h" #include "mesh/appkey.h" #include "mesh/rpl.h" #define abs_diff(a, b) ((a) > (b) ? (a) - (b) : (b) - (a)) #define IV_IDX_DIFF_RANGE 42 /*#define IV_IDX_UPD_MIN (5 * 60) * 5 minute for Testing */ #define IV_IDX_UPD_MIN (60 * 60 * 96) /* 96 Hours - per Spec */ #define IV_IDX_UPD_HOLD (IV_IDX_UPD_MIN/2) #define IV_IDX_UPD_MAX (IV_IDX_UPD_MIN + IV_IDX_UPD_HOLD) #define iv_is_updating(net) ((net)->iv_upd_state == IV_UPD_UPDATING) #define IV_UPDATE_SEQ_TRIGGER 0x800000 /* Half of Seq-Nums expended */ #define SEG_TO 2 #define MSG_TO 60 #define SAR_DEL 10 #define DEFAULT_TRANSMIT_COUNT 1 #define DEFAULT_TRANSMIT_INTERVAL 100 #define SAR_KEY(src, seq0) ((((uint32_t)(seq0)) << 16) | (src)) #define FAST_CACHE_SIZE 8 enum _relay_advice { RELAY_NONE, /* Relay not enabled in node */ RELAY_ALLOWED, /* Relay enabled, msg not to node's unicast */ RELAY_DISALLOWED, /* Msg was unicast handled by this node */ RELAY_ALWAYS /* Relay enabled, msg to a group */ }; enum _iv_upd_state { /* Allows acceptance of any iv_index secure net beacon */ IV_UPD_INIT, /* Normal, can transition, accept current or old */ IV_UPD_NORMAL, /* Updating proc running, we use old, accept old or new */ IV_UPD_UPDATING, /* Normal, can *not* transition, accept current or old iv_index */ IV_UPD_NORMAL_HOLD, }; struct net_key { struct mesh_key_set key_set; unsigned int beacon_id; uint8_t key[16]; uint8_t beacon_key[16]; uint8_t network_id[8]; }; struct mesh_subnet { struct mesh_net *net; uint16_t idx; uint32_t net_key_tx; uint32_t net_key_cur; uint32_t net_key_upd; uint8_t key_refresh; uint8_t kr_phase; }; struct mesh_net { struct mesh_io *io; struct mesh_node *node; struct mesh_prov *prov; struct l_queue *app_keys; unsigned int pkt_id; unsigned int bea_id; unsigned int beacon_id; unsigned int sar_id_next; bool friend_enable; bool snb_enable; bool mpb_enable; bool proxy_enable; bool friend_seq; struct l_timeout *iv_update_timeout; enum _iv_upd_state iv_upd_state; bool iv_update; uint32_t instant; /* Controller Instant of recent Rx */ uint32_t iv_index; uint32_t seq_num; uint16_t src_addr; uint16_t last_addr; uint16_t tx_interval; uint16_t tx_cnt; uint8_t chan; /* Channel of recent Rx */ uint8_t default_ttl; uint8_t tid; uint8_t mpb_period; struct { bool enable; uint16_t interval; uint8_t count; } relay; /* Heartbeat info */ struct mesh_net_heartbeat_sub hb_sub; struct mesh_net_heartbeat_pub hb_pub; uint16_t features; struct l_queue *subnets; struct l_queue *msg_cache; struct l_queue *replay_cache; struct l_queue *sar_in; struct l_queue *sar_out; struct l_queue *sar_queue; struct l_queue *frnd_msgs; struct l_queue *friends; struct l_queue *negotiations; struct l_queue *destinations; }; struct mesh_msg { uint16_t src; uint32_t seq; uint32_t mic; }; struct mesh_sar { unsigned int id; struct l_timeout *seg_timeout; struct l_timeout *msg_timeout; uint32_t flags; uint32_t last_nak; uint32_t iv_index; uint32_t seqAuth; uint16_t seqZero; uint16_t app_idx; uint16_t net_idx; uint16_t src; uint16_t remote; uint16_t len; bool szmic; bool segmented; bool frnd; bool frnd_cred; bool delete; uint8_t ttl; uint8_t last_seg; uint8_t key_aid; uint8_t buf[4]; /* Large enough for ACK-Flags and MIC */ }; struct mesh_destination { uint16_t dst; uint16_t ref_cnt; }; struct net_decode { struct mesh_net *net; struct mesh_friend *frnd; struct mesh_key_set *key_set; uint8_t *packet; uint32_t iv_index; uint8_t size; uint8_t nid; bool proxy; }; struct net_queue_data { struct mesh_io_recv_info *info; struct mesh_net *net; const uint8_t *data; uint8_t *out; size_t out_size; enum _relay_advice relay_advice; uint32_t net_key_id; uint32_t iv_index; uint16_t len; bool seen; }; struct oneshot_tx { struct mesh_net *net; uint16_t interval; uint8_t cnt; uint8_t size; uint8_t packet[30]; }; struct net_beacon_data { uint32_t net_key_id; uint32_t ivi; bool ivu; bool kr; bool processed; bool local; }; static struct l_queue *fast_cache; static struct l_queue *nets; static void net_rx(void *net_ptr, void *user_data); static inline struct mesh_subnet *get_primary_subnet(struct mesh_net *net) { return l_queue_peek_head(net->subnets); } static bool match_key_index(const void *a, const void *b) { const struct mesh_subnet *subnet = a; uint16_t idx = L_PTR_TO_UINT(b); return subnet->idx == idx; } static bool match_key_id(const void *a, const void *b) { const struct mesh_subnet *subnet = a; uint32_t net_key_id = L_PTR_TO_UINT(b); return (net_key_id == subnet->net_key_cur) || (net_key_id == subnet->net_key_upd); } static bool match_friend_key_id(const void *a, const void *b) { const struct mesh_friend *friend = a; uint32_t net_key_id = L_PTR_TO_UINT(b); return (net_key_id == friend->net_key_cur) || (net_key_id == friend->net_key_upd); } static void send_hb_publication(void *data) { struct mesh_net *net = data; struct mesh_net_heartbeat_pub *pub = &net->hb_pub; uint8_t msg[4]; int n = 0; if (pub->dst == UNASSIGNED_ADDRESS) return; msg[n++] = NET_OP_HEARTBEAT; msg[n++] = pub->ttl; l_put_be16(net->features, msg + n); n += 2; mesh_net_transport_send(net, 0, 0, mesh_net_get_iv_index(net), pub->ttl, 0, 0, pub->dst, msg, n); } static void trigger_heartbeat(struct mesh_net *net, uint16_t feature, bool enable) { l_debug("HB: %4.4x --> %d", feature, enable); if (enable) { if (net->features & feature) return; /* no change */ net->features |= feature; } else { if (!(net->features & feature)) return; /* no change */ net->features &= ~feature; } if (!(net->hb_pub.features & feature)) return; /* no interest in this feature */ l_idle_oneshot(send_hb_publication, net, NULL); } static bool match_by_friend(const void *a, const void *b) { const struct mesh_friend *frnd = a; uint16_t dst = L_PTR_TO_UINT(b); return frnd->lp_addr == dst; } static void free_friend_internals(struct mesh_friend *frnd) { if (frnd->pkt_cache) l_queue_destroy(frnd->pkt_cache, l_free); l_free(frnd->u.active.grp_list); frnd->u.active.grp_list = NULL; frnd->pkt_cache = NULL; net_key_unref(frnd->net_key_cur); net_key_unref(frnd->net_key_upd); frnd->net_key_cur = 0; frnd->net_key_upd = 0; } static void frnd_kr_phase1(void *a, void *b) { struct mesh_friend *frnd = a; uint32_t net_key_id = L_PTR_TO_UINT(b); frnd->net_key_upd = net_key_frnd_add(net_key_id, frnd->lp_addr, frnd->net->src_addr, frnd->lp_cnt, frnd->fn_cnt); } static void frnd_kr_phase2(void *a, void *b) { struct mesh_friend *frnd = a; /* * I think that a Friend should use Old Key as long as possible * Because a Friend Node will enter Phase 3 before it's LPN. * Alternatively, the FN could keep the Old Friend Keys until it * receives it's first Poll using the new keys (?) */ l_debug("Use Both KeySet %d && %d for %4.4x", frnd->net_key_cur, frnd->net_key_upd, frnd->lp_addr); } static void frnd_kr_phase3(void *a, void *b) { struct mesh_friend *frnd = a; l_debug("Replace KeySet %d with %d for %4.4x", frnd->net_key_cur, frnd->net_key_upd, frnd->lp_addr); net_key_unref(frnd->net_key_cur); frnd->net_key_cur = frnd->net_key_upd; frnd->net_key_upd = 0; } /* TODO: add net key idx? For now, use primary net key */ struct mesh_friend *mesh_friend_new(struct mesh_net *net, uint16_t dst, uint8_t ele_cnt, uint8_t frd, uint8_t frw, uint32_t fpt, uint16_t fn_cnt, uint16_t lp_cnt) { struct mesh_subnet *subnet; struct mesh_friend *frnd = l_queue_find(net->friends, match_by_friend, L_UINT_TO_PTR(dst)); if (frnd) { /* Kill all timers and empty cache for this friend */ free_friend_internals(frnd); l_timeout_remove(frnd->timeout); frnd->timeout = NULL; } else { frnd = l_new(struct mesh_friend, 1); l_queue_push_head(net->friends, frnd); } /* add _k2 */ frnd->net = net; frnd->lp_addr = dst; frnd->frd = frd; frnd->frw = frw; frnd->fn_cnt = fn_cnt; frnd->lp_cnt = lp_cnt; frnd->poll_timeout = fpt; frnd->ele_cnt = ele_cnt; frnd->pkt_cache = l_queue_new(); frnd->net_key_upd = 0; subnet = get_primary_subnet(net); /* TODO: the primary key must be present, do we need to add check?. */ frnd->net_key_cur = net_key_frnd_add(subnet->net_key_cur, dst, net->src_addr, lp_cnt, fn_cnt); if (!subnet->net_key_upd) return frnd; frnd->net_idx = subnet->idx; frnd->net_key_upd = net_key_frnd_add(subnet->net_key_upd, dst, net->src_addr, lp_cnt, fn_cnt); return frnd; } void mesh_friend_free(void *data) { struct mesh_friend *frnd = data; free_friend_internals(frnd); l_timeout_remove(frnd->timeout); l_free(frnd); } bool mesh_friend_clear(struct mesh_net *net, struct mesh_friend *frnd) { bool removed = l_queue_remove(net->friends, frnd); free_friend_internals(frnd); return removed; } void mesh_friend_sub_add(struct mesh_net *net, uint16_t lpn, uint8_t ele_cnt, uint8_t grp_cnt, const uint8_t *list) { uint16_t *new_list; uint16_t *grp_list; struct mesh_friend *frnd = l_queue_find(net->friends, match_by_friend, L_UINT_TO_PTR(lpn)); if (!frnd) return; new_list = l_malloc((grp_cnt + frnd->u.active.grp_cnt) * sizeof(uint16_t)); grp_list = frnd->u.active.grp_list; if (grp_list && frnd->u.active.grp_cnt) memcpy(new_list, grp_list, frnd->u.active.grp_cnt * sizeof(uint16_t)); memcpy(&new_list[frnd->u.active.grp_cnt], list, grp_cnt * sizeof(uint16_t)); l_free(grp_list); frnd->ele_cnt = ele_cnt; frnd->u.active.grp_list = new_list; frnd->u.active.grp_cnt += grp_cnt; } void mesh_friend_sub_del(struct mesh_net *net, uint16_t lpn, uint8_t cnt, const uint8_t *del_list) { uint16_t *grp_list; int16_t i, grp_cnt; size_t cnt16 = cnt * sizeof(uint16_t); struct mesh_friend *frnd = l_queue_find(net->friends, match_by_friend, L_UINT_TO_PTR(lpn)); if (!frnd) return; grp_cnt = frnd->u.active.grp_cnt; grp_list = frnd->u.active.grp_list; while (cnt-- && grp_cnt) { cnt16 -= sizeof(uint16_t); for (i = grp_cnt - 1; i >= 0; i--) { if (l_get_le16(del_list + cnt16) == grp_list[i]) { grp_cnt--; memcpy(&grp_list[i], &grp_list[i + 1], (grp_cnt - i) * sizeof(uint16_t)); break; } } } frnd->u.active.grp_cnt = grp_cnt; if (!grp_cnt) { l_free(frnd->u.active.grp_list); frnd->u.active.grp_list = NULL; } } uint32_t mesh_net_next_seq_num(struct mesh_net *net) { uint32_t seq = net->seq_num++; /* * Cap out-of-range seq_num max value to +1. Out of range * seq_nums will not be sent as they would violate spec. * This condition signals a runaway seq_num condition, and * the node must wait for a completed IV Index update procedure * before it can send again. */ if (net->seq_num > SEQ_MASK) net->seq_num = SEQ_MASK + 1; node_set_sequence_number(net->node, net->seq_num); return seq; } static struct mesh_sar *mesh_sar_new(size_t len) { size_t size = sizeof(struct mesh_sar) + len; struct mesh_sar *sar; sar = l_malloc(size); memset(sar, 0, size); return sar; } static void mesh_sar_free(void *data) { struct mesh_sar *sar = data; if (!sar) return; l_timeout_remove(sar->seg_timeout); l_timeout_remove(sar->msg_timeout); l_free(sar); } static void subnet_free(void *data) { struct mesh_subnet *subnet = data; struct mesh_net *net = subnet->net; if (net->snb_enable) net_key_beacon_disable(subnet->net_key_tx, false); if (net->mpb_enable) net_key_beacon_disable(subnet->net_key_tx, true); net_key_unref(subnet->net_key_cur); net_key_unref(subnet->net_key_upd); l_free(subnet); } static struct mesh_subnet *subnet_new(struct mesh_net *net, uint16_t idx) { struct mesh_subnet *subnet; subnet = l_new(struct mesh_subnet, 1); if (!subnet) return NULL; subnet->net = net; subnet->idx = idx; return subnet; } static void enable_snb(void *a, void *b) { struct mesh_subnet *subnet = a; struct mesh_net *net = b; if (net->snb_enable) net_key_beacon_enable(subnet->net_key_tx, false, 0); else net_key_beacon_disable(subnet->net_key_tx, false); } static void enable_mpb(void *a, void *b) { struct mesh_subnet *subnet = a; struct mesh_net *net = b; if (net->mpb_enable) net_key_beacon_enable(subnet->net_key_tx, true, net->mpb_period); else net_key_beacon_disable(subnet->net_key_tx, true); } static void enqueue_update(void *a, void *b); static void queue_friend_update(struct mesh_net *net) { struct mesh_subnet *subnet; struct mesh_friend *frnd; uint8_t flags = 0; if (l_queue_length(net->friends)) { struct mesh_friend_msg update = { .src = net->src_addr, .iv_index = mesh_net_get_iv_index(net), .last_len = 7, .ctl = true, }; frnd = l_queue_peek_head(net->friends); subnet = l_queue_find(net->subnets, match_key_index, L_UINT_TO_PTR(frnd->net_idx)); if (!subnet) return; if (subnet->kr_phase == KEY_REFRESH_PHASE_TWO) flags |= KEY_REFRESH; if (net->iv_update) flags |= IV_INDEX_UPDATE; update.u.one[0].hdr = NET_OP_FRND_UPDATE << OPCODE_HDR_SHIFT; update.u.one[0].seq = mesh_net_next_seq_num(net); update.u.one[0].data[0] = NET_OP_FRND_UPDATE; update.u.one[0].data[1] = flags; l_put_be32(net->iv_index, update.u.one[0].data + 2); update.u.one[0].data[6] = 0x01; /* More Data */ l_queue_foreach(net->friends, enqueue_update, &update); } } static void refresh_beacon(void *a, void *b) { struct mesh_subnet *subnet = a; struct mesh_net *net = b; net_key_beacon_refresh(subnet->net_key_tx, net->iv_index, !!(subnet->kr_phase == KEY_REFRESH_PHASE_TWO), net->iv_update, false); } struct mesh_net *mesh_net_new(struct mesh_node *node) { struct mesh_net *net; net = l_new(struct mesh_net, 1); net->node = node; net->seq_num = DEFAULT_SEQUENCE_NUMBER; net->default_ttl = TTL_MASK; net->tx_cnt = DEFAULT_TRANSMIT_COUNT; net->tx_interval = DEFAULT_TRANSMIT_INTERVAL; net->subnets = l_queue_new(); net->msg_cache = l_queue_new(); net->sar_in = l_queue_new(); net->sar_out = l_queue_new(); net->sar_queue = l_queue_new(); net->frnd_msgs = l_queue_new(); net->destinations = l_queue_new(); net->app_keys = l_queue_new(); net->replay_cache = l_queue_new(); if (!nets) nets = l_queue_new(); if (!fast_cache) fast_cache = l_queue_new(); return net; } void mesh_net_free(void *user_data) { struct mesh_net *net = user_data; if (!net) return; l_queue_destroy(net->subnets, subnet_free); l_queue_destroy(net->msg_cache, l_free); l_queue_destroy(net->replay_cache, l_free); l_queue_destroy(net->sar_in, mesh_sar_free); l_queue_destroy(net->sar_out, mesh_sar_free); l_queue_destroy(net->sar_queue, mesh_sar_free); l_queue_destroy(net->frnd_msgs, l_free); l_queue_destroy(net->friends, mesh_friend_free); l_queue_destroy(net->negotiations, mesh_friend_free); l_queue_destroy(net->destinations, l_free); l_queue_destroy(net->app_keys, appkey_key_free); l_free(net); } void mesh_net_cleanup(void) { l_queue_destroy(fast_cache, l_free); fast_cache = NULL; l_queue_destroy(nets, mesh_net_free); nets = NULL; } bool mesh_net_set_seq_num(struct mesh_net *net, uint32_t seq) { if (!net) return false; net->seq_num = seq; node_set_sequence_number(net->node, net->seq_num); return true; } bool mesh_net_set_default_ttl(struct mesh_net *net, uint8_t ttl) { if (!net) return false; net->default_ttl = ttl; return true; } uint32_t mesh_net_get_seq_num(struct mesh_net *net) { if (!net) return 0; return net->seq_num; } uint8_t mesh_net_get_default_ttl(struct mesh_net *net) { if (!net) return 0; return net->default_ttl; } uint16_t mesh_net_get_address(struct mesh_net *net) { if (!net) return 0; return net->src_addr; } bool mesh_net_register_unicast(struct mesh_net *net, uint16_t address, uint8_t num_ele) { if (!net || !IS_UNICAST(address) || !num_ele) return false; net->src_addr = address; net->last_addr = address + num_ele - 1; if (net->last_addr < net->src_addr) return false; do { mesh_net_dst_reg(net, address); address++; num_ele--; } while (num_ele > 0); return true; } bool mesh_net_set_proxy_mode(struct mesh_net *net, bool enable) { if (!net) return false; /* No support for proxy yet */ if (enable) { l_error("Proxy not supported!"); return false; } trigger_heartbeat(net, FEATURE_PROXY, enable); return true; } bool mesh_net_set_friend_mode(struct mesh_net *net, bool enable) { l_debug("mesh_net_set_friend_mode - %d", enable); if (!net) return false; if (net->friend_enable == enable) return true; if (enable) { net->friends = l_queue_new(); net->negotiations = l_queue_new(); } else { l_queue_destroy(net->friends, mesh_friend_free); l_queue_destroy(net->negotiations, mesh_friend_free); net->friends = net->negotiations = NULL; } net->friend_enable = enable; trigger_heartbeat(net, FEATURE_FRIEND, enable); return true; } bool mesh_net_set_relay_mode(struct mesh_net *net, bool enable, uint8_t cnt, uint8_t interval) { if (!net) return false; net->relay.enable = enable; net->relay.count = cnt; net->relay.interval = interval; trigger_heartbeat(net, FEATURE_RELAY, enable); return true; } int mesh_net_get_identity_mode(struct mesh_net *net, uint16_t idx, uint8_t *mode) { struct mesh_subnet *subnet; if (!net) return MESH_STATUS_UNSPECIFIED_ERROR; subnet = l_queue_find(net->subnets, match_key_index, L_UINT_TO_PTR(idx)); if (!subnet) return MESH_STATUS_INVALID_NETKEY; /* Currently, proxy mode is not supported */ *mode = MESH_MODE_UNSUPPORTED; return MESH_STATUS_SUCCESS; } int mesh_net_del_key(struct mesh_net *net, uint16_t idx) { struct mesh_subnet *subnet; if (!net) return MESH_STATUS_UNSPECIFIED_ERROR; subnet = l_queue_find(net->subnets, match_key_index, L_UINT_TO_PTR(idx)); if (!subnet) return MESH_STATUS_SUCCESS; /* Cannot remove primary key */ if (l_queue_length(net->subnets) <= 1) return MESH_STATUS_CANNOT_REMOVE; /* Delete associated app keys */ appkey_delete_bound_keys(net, idx); /* Disable hearbeat publication on this subnet */ if (idx == net->hb_pub.net_idx) net->hb_pub.dst = UNASSIGNED_ADDRESS; /* TODO: cancel snb_enable on this subnet */ l_queue_remove(net->subnets, subnet); subnet_free(subnet); if (!mesh_config_net_key_del(node_config_get(net->node), idx)) return MESH_STATUS_STORAGE_FAIL; return MESH_STATUS_SUCCESS; } static struct mesh_subnet *add_key(struct mesh_net *net, uint16_t idx, const uint8_t *value) { struct mesh_subnet *subnet; subnet = subnet_new(net, idx); if (!subnet) return NULL; subnet->net_key_tx = subnet->net_key_cur = net_key_add(value); if (!subnet->net_key_cur) { l_free(subnet); return NULL; } net_key_beacon_refresh(subnet->net_key_tx, net->iv_index, false, net->iv_update, false); if (net->snb_enable) net_key_beacon_enable(subnet->net_key_tx, false, 0); if (net->mpb_enable) net_key_beacon_enable(subnet->net_key_tx, true, net->mpb_period); l_queue_push_tail(net->subnets, subnet); return subnet; } /* * This function is called when Configuration Server Model receives * a NETKEY_ADD command */ int mesh_net_add_key(struct mesh_net *net, uint16_t idx, const uint8_t *value) { struct mesh_subnet *subnet; subnet = l_queue_find(net->subnets, match_key_index, L_UINT_TO_PTR(idx)); if (subnet) { if (net_key_confirm(subnet->net_key_cur, value)) return MESH_STATUS_SUCCESS; else return MESH_STATUS_IDX_ALREADY_STORED; } subnet = add_key(net, idx, value); if (!subnet) return MESH_STATUS_INSUFF_RESOURCES; if (!mesh_config_net_key_add(node_config_get(net->node), idx, value)) { l_queue_remove(net->subnets, subnet); subnet_free(subnet); return MESH_STATUS_STORAGE_FAIL; } return MESH_STATUS_SUCCESS; } uint32_t mesh_net_get_iv_index(struct mesh_net *net) { if (!net) return 0xffffffff; return net->iv_index - net->iv_update; } /* TODO: net key index? */ void mesh_net_get_snb_state(struct mesh_net *net, uint8_t *flags, uint32_t *iv_index) { struct mesh_subnet *subnet; if (!net || !flags || !iv_index) return; *iv_index = net->iv_index; *flags = net->iv_update ? IV_INDEX_UPDATE : 0x00; subnet = get_primary_subnet(net); if (subnet) *flags |= subnet->key_refresh ? KEY_REFRESH : 0x00; } bool mesh_net_get_key(struct mesh_net *net, bool new_key, uint16_t idx, uint32_t *net_key_id) { struct mesh_subnet *subnet; if (!net) return false; subnet = l_queue_find(net->subnets, match_key_index, L_UINT_TO_PTR(idx)); if (!subnet) return false; if (!new_key) { *net_key_id = subnet->net_key_cur; return true; } if (!subnet->net_key_upd) return false; *net_key_id = subnet->net_key_upd; return true; } bool mesh_net_key_list_get(struct mesh_net *net, uint8_t *buf, uint16_t *size) { const struct l_queue_entry *entry; uint16_t num_keys, req_size, buf_size; struct mesh_subnet *subnet; if (!net || !buf || !size) return false; buf_size = *size; num_keys = l_queue_length(net->subnets); req_size = (num_keys / 2) * 3 + (num_keys % 2) * 2; if (buf_size < req_size) return false; *size = req_size; /* Pack NetKey indices in 3 octets */ for (entry = l_queue_get_entries(net->subnets); num_keys > 1;) { uint32_t idx_pair; subnet = entry->data; idx_pair = subnet->idx; idx_pair <<= 12; subnet = entry->next->data; idx_pair += subnet->idx; l_put_le32(idx_pair, buf); buf += 3; num_keys -= 2; entry = entry->next->next; } /* If odd number of NetKeys, fill in the end of the buffer */ if (num_keys % 2) { subnet = entry->data; l_put_le16(subnet->idx, buf); } return true; } bool mesh_net_get_frnd_seq(struct mesh_net *net) { if (!net) return false; return net->friend_seq; } void mesh_net_set_frnd_seq(struct mesh_net *net, bool seq) { if (!net) return; net->friend_seq = seq; } static bool match_cache(const void *a, const void *b) { const struct mesh_msg *msg = a; const struct mesh_msg *tst = b; if (msg->seq != tst->seq || msg->mic != tst->mic || msg->src != tst->src) return false; return true; } static bool msg_in_cache(struct mesh_net *net, uint16_t src, uint32_t seq, uint32_t mic) { struct mesh_msg *msg; struct mesh_msg tst = { .src = src, .seq = seq, .mic = mic, }; msg = l_queue_find(net->msg_cache, match_cache, &tst); if (msg) { l_debug("Supressing duplicate %4.4x + %6.6x + %8.8x", src, seq, mic); return true; } msg = l_new(struct mesh_msg, 1); *msg = tst; l_queue_push_head(net->msg_cache, msg); l_debug("Add %4.4x + %6.6x + %8.8x", src, seq, mic); if (l_queue_length(net->msg_cache) > MSG_CACHE_SIZE) { msg = l_queue_peek_tail(net->msg_cache); /* Remove Tail (oldest msg in cache) */ l_debug("Remove %4.4x + %6.6x + %8.8x", msg->src, msg->seq, msg->mic); if (l_queue_remove(net->msg_cache, msg)) l_free(msg); } return false; } static bool match_sar_seq0(const void *a, const void *b) { const struct mesh_sar *sar = a; uint16_t seqZero = L_PTR_TO_UINT(b); return sar->seqZero == seqZero; } static bool match_sar_remote(const void *a, const void *b) { const struct mesh_sar *sar = a; uint16_t remote = L_PTR_TO_UINT(b); return sar->remote == remote; } static bool match_msg_timeout(const void *a, const void *b) { const struct mesh_sar *sar = a; const struct l_timeout *msg_timeout = b; return sar->msg_timeout == msg_timeout; } static bool match_seg_timeout(const void *a, const void *b) { const struct mesh_sar *sar = a; const struct l_timeout *seg_timeout = b; return sar->seg_timeout == seg_timeout; } static bool match_dest_dst(const void *a, const void *b) { const struct mesh_destination *dest = a; uint16_t dst = L_PTR_TO_UINT(b); return dst == dest->dst; } static bool match_frnd_dst(const void *a, const void *b) { const struct mesh_friend *frnd = a; uint16_t dst = L_PTR_TO_UINT(b); int16_t i, grp_cnt = frnd->u.active.grp_cnt; uint16_t *grp_list = frnd->u.active.grp_list; /* * Determine if this message is for this friends unicast * address, and/or one of it's group/virtual addresses */ if (dst >= frnd->lp_addr && dst < (frnd->lp_addr + frnd->ele_cnt)) return true; if (!(dst & 0x8000)) return false; for (i = 0; i < grp_cnt; i++) { if (dst == grp_list[i]) return true; } return false; } static bool is_lpn_friend(struct mesh_net *net, uint16_t addr) { void *tst; tst = l_queue_find(net->friends, match_frnd_dst, L_UINT_TO_PTR(addr)); return tst != NULL; } static bool is_us(struct mesh_net *net, uint16_t addr, bool src) { void *tst; if (IS_ALL_NODES(addr)) return true; if (addr == FRIENDS_ADDRESS) return net->friend_enable; if (addr == RELAYS_ADDRESS) return net->relay.enable; if (addr == PROXIES_ADDRESS) return net->proxy_enable; if (addr >= net->src_addr && addr <= net->last_addr) return true; tst = l_queue_find(net->destinations, match_dest_dst, L_UINT_TO_PTR(addr)); if (tst == NULL && !src) tst = l_queue_find(net->friends, match_frnd_dst, L_UINT_TO_PTR(addr)); return tst != NULL; } static struct mesh_friend_msg *mesh_friend_msg_new(uint8_t seg_max) { struct mesh_friend_msg *frnd_msg; if (seg_max) { size_t size = sizeof(struct mesh_friend_msg) - sizeof(struct mesh_friend_seg_one); size += (seg_max + 1) * sizeof(struct mesh_friend_seg_12); frnd_msg = l_malloc(size); memset(frnd_msg, 0, size); } else frnd_msg = l_new(struct mesh_friend_msg, 1); return frnd_msg; } static bool match_ack(const void *a, const void *b) { const struct mesh_friend_msg *old = a; const struct mesh_friend_msg *rx = b; uint32_t old_hdr; uint32_t new_hdr; /* Determine if old pkt is ACK to same SAR message that new ACK is */ if (!old->ctl || old->src != rx->src) return false; /* Check the quickest items first before digging deeper */ old_hdr = old->u.one[0].hdr & HDR_ACK_MASK; new_hdr = rx->u.one[0].hdr & HDR_ACK_MASK; return old_hdr == new_hdr; } static void enqueue_friend_pkt(void *a, void *b) { struct mesh_friend *frnd = a; struct mesh_friend_msg *pkt, *rx = b; size_t size; int16_t i; if (rx->done) return; /* * Determine if this message is for this friends unicast * address, and/or one of it's group/virtual addresses */ if (rx->dst >= frnd->lp_addr && (rx->dst - frnd->lp_addr) < frnd->ele_cnt) { rx->done = true; goto enqueue; } if (!(rx->dst & 0x8000)) return; if (!IS_ALL_NODES(rx->dst)) { for (i = 0; i < frnd->u.active.grp_cnt; i++) { if (rx->dst == frnd->u.active.grp_list[i]) goto enqueue; } return; } enqueue: /* Special handling for Seg Ack -- Only one per message queue */ if (((rx->u.one[0].hdr >> OPCODE_HDR_SHIFT) & OPCODE_MASK) == NET_OP_SEG_ACKNOWLEDGE) { void *old_head = l_queue_peek_head(frnd->pkt_cache); /* Suppress duplicate ACKs */ do { void *old = l_queue_remove_if(frnd->pkt_cache, match_ack, rx); if (!old) break; if (old_head == old) /* * If we are discarding head for any * reason, reset FRND SEQ */ frnd->u.active.last = frnd->u.active.seq; l_free(old); } while (true); } l_debug("%s for %4.4x from %4.4x ttl: %2.2x (seq: %6.6x) (ctl: %d)", __func__, frnd->lp_addr, rx->src, rx->ttl, rx->u.one[0].seq, rx->ctl); if (rx->cnt_in) { size = sizeof(struct mesh_friend_msg) - sizeof(struct mesh_friend_seg_one); size += (rx->cnt_in + 1) * sizeof(struct mesh_friend_seg_12); } else size = sizeof(struct mesh_friend_msg); pkt = l_malloc(size); memcpy(pkt, rx, size); l_queue_push_tail(frnd->pkt_cache, pkt); if (l_queue_length(frnd->pkt_cache) > FRND_CACHE_MAX) { /* * TODO: Guard against popping UPDATE packets * (disallowed per spec) */ pkt = l_queue_pop_head(frnd->pkt_cache); l_free(pkt); frnd->u.active.last = frnd->u.active.seq; } } static void enqueue_update(void *a, void *b) { struct mesh_friend *frnd = a; struct mesh_friend_msg *pkt = b; pkt->dst = frnd->lp_addr; pkt->done = false; enqueue_friend_pkt(frnd, pkt); } static uint32_t seq_auth(uint32_t seq, uint16_t seqZero) { uint32_t seqAuth = seqZero & SEQ_ZERO_MASK; seqAuth |= seq & (~SEQ_ZERO_MASK); if (seqAuth > seq) seqAuth -= (SEQ_ZERO_MASK + 1); return seqAuth; } static bool friend_packet_queue(struct mesh_net *net, uint32_t iv_index, bool ctl, uint8_t ttl, uint32_t seq, uint16_t src, uint16_t dst, uint32_t hdr, const uint8_t *data, uint16_t size) { struct mesh_friend_msg *frnd_msg; uint8_t seg_max = SEG_TOTAL(hdr); bool ret; if (seg_max && !IS_SEGMENTED(hdr)) return false; frnd_msg = mesh_friend_msg_new(seg_max); if (IS_SEGMENTED(hdr)) { uint32_t seqAuth = seq_auth(seq, hdr >> SEQ_ZERO_HDR_SHIFT); uint8_t i; for (i = 0; i <= seg_max; i++) { memcpy(frnd_msg->u.s12[i].data, data, 12); frnd_msg->u.s12[i].hdr = hdr; frnd_msg->u.s12[i].seq = seqAuth + i; data += 12; hdr += (1 << SEGO_HDR_SHIFT); } frnd_msg->cnt_in = seg_max; frnd_msg->last_len = size % 12; if (!frnd_msg->last_len) frnd_msg->last_len = 12; } else { uint8_t opcode = hdr >> OPCODE_HDR_SHIFT; if (ctl && opcode != NET_OP_SEG_ACKNOWLEDGE) { /* Don't cache Friend Ctl opcodes */ if (FRND_OPCODE(opcode)) { l_free(frnd_msg); return false; } memcpy(frnd_msg->u.one[0].data + 1, data, size); frnd_msg->last_len = size + 1; frnd_msg->u.one[0].data[0] = opcode; } else { memcpy(frnd_msg->u.one[0].data, data, size); frnd_msg->last_len = size; } frnd_msg->u.one[0].hdr = hdr; frnd_msg->u.one[0].seq = seq; } frnd_msg->iv_index = iv_index; frnd_msg->src = src; frnd_msg->dst = dst; frnd_msg->ctl = ctl; frnd_msg->ttl = ttl; /* Re-Package into Friend Delivery payload */ l_queue_foreach(net->friends, enqueue_friend_pkt, frnd_msg); ret = frnd_msg->done; /* TODO Optimization(?): Unicast messages keep this buffer */ l_free(frnd_msg); return ret; } static void friend_ack_rxed(struct mesh_net *net, uint32_t iv_index, uint32_t seq, uint16_t src, uint16_t dst, const uint8_t *pkt) { uint32_t hdr = l_get_be32(pkt) & ((SEQ_ZERO_MASK << SEQ_ZERO_HDR_SHIFT) | /* Preserve SeqZero */ ((uint32_t) 0x01 << RELAY_HDR_SHIFT)); /* Preserve Relay bit */ uint32_t flags = l_get_be32(pkt + 3); struct mesh_friend_msg frnd_ack = { .ctl = true, .iv_index = iv_index, .src = src, .dst = dst, .last_len = sizeof(flags), .u.one[0].seq = seq, .done = false, }; hdr |= NET_OP_SEG_ACKNOWLEDGE << OPCODE_HDR_SHIFT; frnd_ack.u.one[0].hdr = hdr; l_put_be32(flags, frnd_ack.u.one[0].data); l_queue_foreach(net->friends, enqueue_friend_pkt, &frnd_ack); } static bool send_seg(struct mesh_net *net, uint8_t cnt, uint16_t interval, struct mesh_sar *msg, uint8_t seg); static void send_frnd_ack(struct mesh_net *net, uint16_t src, uint16_t dst, uint32_t hdr, uint32_t flags) { uint32_t expected; uint8_t msg[7]; /* We don't ACK from multicast destinations */ if (src & 0x8000) return; /* Calculate the "Full ACK" mask */ expected = 0xffffffff >> (31 - SEG_TOTAL(hdr)); /* Clear Hdr bits that don't apply to Seg ACK */ hdr &= ~(((uint32_t) 0x01 << SEG_HDR_SHIFT) | (OPCODE_MASK << OPCODE_HDR_SHIFT) | ((uint32_t) 0x01 << SZMIC_HDR_SHIFT) | (SEG_MASK << SEGO_HDR_SHIFT) | (SEG_MASK << SEGN_HDR_SHIFT)); hdr |= NET_OP_SEG_ACKNOWLEDGE << OPCODE_HDR_SHIFT; hdr |= (uint32_t) 0x01 << RELAY_HDR_SHIFT; /* Clear all unexpected bits */ flags &= expected; l_put_be32(hdr, msg); l_put_be32(flags, msg + 3); l_debug("Send Friend ACK to Segs: %8.8x", flags); if (is_lpn_friend(net, dst)) { /* If we are acking our LPN Friend, queue, don't send */ friend_ack_rxed(net, mesh_net_get_iv_index(net), mesh_net_next_seq_num(net), 0, dst, msg); } else { mesh_net_transport_send(net, 0, 0, mesh_net_get_iv_index(net), DEFAULT_TTL, 0, 0, dst, msg, sizeof(msg)); } } static void send_net_ack(struct mesh_net *net, struct mesh_sar *sar, uint32_t flags) { uint8_t msg[7]; uint32_t hdr; uint16_t src = sar->src; uint16_t dst = sar->remote; /* We don't ACK from multicast destinations */ if (src & 0x8000) return; hdr = NET_OP_SEG_ACKNOWLEDGE << OPCODE_HDR_SHIFT; hdr |= sar->seqZero << SEQ_ZERO_HDR_SHIFT; if (is_lpn_friend(net, src)) hdr |= (uint32_t) 0x01 << RELAY_HDR_SHIFT; l_put_be32(hdr, msg); l_put_be32(flags, msg + 3); l_debug("Send%s ACK to Segs: %8.8x", sar->frnd ? " Friend" : "", flags); if (is_lpn_friend(net, dst)) { /* If we are acking our LPN Friend, queue, don't send */ friend_ack_rxed(net, mesh_net_get_iv_index(net), mesh_net_next_seq_num(net), src, dst, msg); return; } mesh_net_transport_send(net, 0, sar->net_idx, mesh_net_get_iv_index(net), DEFAULT_TTL, 0, src, dst, msg, sizeof(msg)); } static void inseg_to(struct l_timeout *seg_timeout, void *user_data) { struct mesh_net *net = user_data; struct mesh_sar *sar = l_queue_find(net->sar_in, match_seg_timeout, seg_timeout); l_timeout_remove(seg_timeout); if (!sar) return; /* Send NAK */ l_debug("Timeout %p %3.3x", sar, sar->app_idx); send_net_ack(net, sar, sar->flags); sar->seg_timeout = l_timeout_create(SEG_TO, inseg_to, net, NULL); } static void inmsg_to(struct l_timeout *msg_timeout, void *user_data) { struct mesh_net *net = user_data; struct mesh_sar *sar = l_queue_find(net->sar_in, match_msg_timeout, msg_timeout); if (!sar) { l_timeout_remove(msg_timeout); return; } if (!sar->delete) { /* * Incomplete timer expired, cancel SAR and start * delete timer */ l_timeout_remove(sar->seg_timeout); sar->seg_timeout = NULL; sar->delete = true; l_timeout_modify(sar->msg_timeout, SAR_DEL); return; } l_queue_remove(net->sar_in, sar); mesh_sar_free(sar); } static void outmsg_to(struct l_timeout *msg_timeout, void *user_data) { struct mesh_net *net = user_data; struct mesh_sar *sar = l_queue_remove_if(net->sar_out, match_msg_timeout, msg_timeout); l_timeout_remove(msg_timeout); if (!sar) return; sar->msg_timeout = NULL; mesh_sar_free(sar); } static void outseg_to(struct l_timeout *seg_timeout, void *user_data); static void send_queued_sar(struct mesh_net *net, uint16_t dst) { struct mesh_sar *sar = l_queue_remove_if(net->sar_queue, match_sar_remote, L_UINT_TO_PTR(dst)); if (!sar) return; /* Out to current outgoing, and immediate expire Seg TO */ l_queue_push_head(net->sar_out, sar); sar->seg_timeout = NULL; sar->msg_timeout = l_timeout_create(MSG_TO, outmsg_to, net, NULL); outseg_to(NULL, net); } static void ack_received(struct mesh_net *net, bool timeout, uint16_t src, uint16_t dst, uint16_t seq0, uint32_t ack_flag) { struct mesh_sar *outgoing; uint32_t seg_flag = 0x00000001; uint32_t ack_copy = ack_flag; uint16_t i; l_debug("ACK Rxed (%x) (to:%d): %8.8x", seq0, timeout, ack_flag); outgoing = l_queue_find(net->sar_out, match_sar_seq0, L_UINT_TO_PTR(seq0)); if (!outgoing) { l_debug("Not Found: %4.4x", seq0); return; } /* * TODO -- If we receive from different * SRC than we are sending to, make sure the OBO flag is set */ if ((!timeout && !ack_flag) || (outgoing->flags & ack_flag) == outgoing->flags) { l_debug("ob_sar_removal (%x)", outgoing->flags); /* Note: ack_flags == 0x00000000 is a remote Cancel request */ l_queue_remove(net->sar_out, outgoing); send_queued_sar(net, outgoing->remote); mesh_sar_free(outgoing); return; } outgoing->last_nak |= ack_flag; ack_copy &= outgoing->flags; for (i = 0; i <= SEG_MAX(true, outgoing->len); i++, seg_flag <<= 1) { if (seg_flag & ack_flag) { l_debug("Skipping Seg %d of %d", i, SEG_MAX(true, outgoing->len)); continue; } ack_copy |= seg_flag; l_debug("Resend Seg %d net:%p dst:%x app_idx:%3.3x", i, net, outgoing->remote, outgoing->app_idx); send_seg(net, net->tx_cnt, net->tx_interval, outgoing, i); } l_timeout_remove(outgoing->seg_timeout); outgoing->seg_timeout = l_timeout_create(SEG_TO, outseg_to, net, NULL); } static void outseg_to(struct l_timeout *seg_timeout, void *user_data) { struct mesh_net *net = user_data; struct mesh_sar *sar = l_queue_find(net->sar_out, match_seg_timeout, seg_timeout); l_timeout_remove(seg_timeout); if (!sar) return; sar->seg_timeout = NULL; /* Re-Send missing segments by faking NACK */ ack_received(net, true, sar->remote, sar->src, sar->seqZero, sar->last_nak); } static bool match_replay_cache(const void *a, const void *b) { const struct mesh_rpl *rpe = a; uint16_t src = L_PTR_TO_UINT(b); return src == rpe->src; } static bool clean_old_iv_index(void *a, void *b) { struct mesh_rpl *rpe = a; uint32_t iv_index = L_PTR_TO_UINT(b); if (iv_index < 2) return false; if (rpe->iv_index < iv_index - 1) { l_free(rpe); return true; } return false; } static bool msg_check_replay_cache(struct mesh_net *net, uint16_t src, uint16_t crpl, uint32_t seq, uint32_t iv_index) { struct mesh_rpl *rpe; /* If anything missing reject this message by returning true */ if (!net || !net->node) return true; rpe = l_queue_find(net->replay_cache, match_replay_cache, L_UINT_TO_PTR(src)); if (rpe) { if (iv_index > rpe->iv_index) return false; /* Return true if (iv_index | seq) too low */ if (iv_index < rpe->iv_index || seq <= rpe->seq) { l_debug("Ignoring replayed packet"); return true; } } else if (l_queue_length(net->replay_cache) >= crpl) { /* SRC not in Replay Cache... see if there is space for it */ int ret = l_queue_foreach_remove(net->replay_cache, clean_old_iv_index, L_UINT_TO_PTR(iv_index)); /* Return true if no space could be freed */ if (!ret) { l_debug("Replay cache full"); return true; } } return false; } static void msg_add_replay_cache(struct mesh_net *net, uint16_t src, uint32_t seq, uint32_t iv_index) { struct mesh_rpl *rpe; if (!net || !net->replay_cache) return; rpe = l_queue_remove_if(net->replay_cache, match_replay_cache, L_UINT_TO_PTR(src)); if (!rpe) { rpe = l_new(struct mesh_rpl, 1); rpe->src = src; } rpe->seq = seq; rpe->iv_index = iv_index; rpl_put_entry(net->node, src, iv_index, seq); /* Optimize so that most recent conversations stay earliest in cache */ l_queue_push_head(net->replay_cache, rpe); } static bool msg_rxed(struct mesh_net *net, bool frnd, uint32_t iv_index, uint8_t ttl, uint32_t seq, uint16_t net_idx, uint16_t src, uint16_t dst, uint8_t key_aid, bool segmented, bool szmic, uint16_t seqZero, const uint8_t *data, uint16_t size) { uint32_t seqAuth = seq_auth(seq, seqZero); uint16_t crpl; /* Sanity check seqAuth */ if (seqAuth > seq) return false; /* Save un-decrypted messages for our friends */ if (!frnd && l_queue_length(net->friends)) { uint32_t hdr = key_aid << KEY_HDR_SHIFT; uint8_t frnd_ttl = ttl; /* If not from us, decrement for our hop */ if (src < net->src_addr || src > net->last_addr) { if (frnd_ttl > 1) frnd_ttl--; else goto not_for_friend; } if (szmic || size > 15) { hdr |= (uint32_t) 0x01 << SEG_HDR_SHIFT; hdr |= szmic << SZMIC_HDR_SHIFT; hdr |= (seqZero & SEQ_ZERO_MASK) << SEQ_ZERO_HDR_SHIFT; hdr |= SEG_MAX(true, size) << SEGN_HDR_SHIFT; } if (friend_packet_queue(net, iv_index, false, frnd_ttl, seq, src, dst, hdr, data, size)) return true; } not_for_friend: if (dst == FRIENDS_ADDRESS && !net->friend_enable) return false; if (dst == RELAYS_ADDRESS && !net->relay.enable) return false; if (dst == PROXIES_ADDRESS && !net->proxy_enable) return false; /* Don't process if already in RPL */ crpl = node_get_crpl(net->node); if (msg_check_replay_cache(net, src, crpl, seq, iv_index)) return false; if (!mesh_model_rx(net->node, szmic, seqAuth, iv_index, net_idx, src, dst, key_aid, data, size)) return false; /* If message has been handled by us, add to RPL */ msg_add_replay_cache(net, src, seq, iv_index); return true; } static uint16_t key_id_to_net_idx(struct mesh_net *net, uint32_t net_key_id, bool *frnd) { struct mesh_subnet *subnet; struct mesh_friend *friend; if (!net) return NET_IDX_INVALID; if (frnd) *frnd = false; subnet = l_queue_find(net->subnets, match_key_id, L_UINT_TO_PTR(net_key_id)); if (subnet) return subnet->idx; friend = l_queue_find(net->friends, match_friend_key_id, L_UINT_TO_PTR(net_key_id)); if (friend) { if (frnd) *frnd = true; return friend->net_idx; } friend = l_queue_find(net->negotiations, match_friend_key_id, L_UINT_TO_PTR(net_key_id)); if (friend) return friend->net_idx; else return NET_IDX_INVALID; } static bool match_frnd_sar_dst(const void *a, const void *b) { const struct mesh_friend_msg *frnd_msg = a; uint16_t dst = L_PTR_TO_UINT(b); return frnd_msg->dst == dst; } static void friend_seg_rxed(struct mesh_net *net, uint32_t iv_index, uint8_t ttl, uint32_t seq, uint16_t src, uint16_t dst, uint32_t hdr, const uint8_t *data, uint8_t size) { struct mesh_friend *frnd = NULL; struct mesh_friend_msg *frnd_msg = NULL; uint8_t cnt; uint8_t segN = hdr & 0x1f; uint8_t segO = ((hdr >> 5) & 0x1f); uint32_t expected = 0xffffffff >> (31 - segN); uint32_t this_seg_flag = 0x00000001 << segO; uint32_t largest = (0xffffffff << segO) & expected; uint32_t hdr_key = hdr & HDR_KEY_MASK; frnd = l_queue_find(net->friends, match_frnd_dst, L_UINT_TO_PTR(dst)); if (!frnd) return; if (frnd->u.active.last_hdr == hdr_key) { /* We are no longer receiving this msg. Resend final ACK */ send_frnd_ack(net, dst, src, frnd->u.active.last_hdr, 0xffffffff); return; } /* Check if we have a SAR-in-progress that matches incoming segment */ frnd_msg = l_queue_find(net->frnd_msgs, match_frnd_sar_dst, L_UINT_TO_PTR(dst)); if (frnd_msg) { /* Flush if SZMICN or IV Index has changed */ if (frnd_msg->iv_index != iv_index) frnd_msg->u.s12[0].hdr = 0; /* Flush incomplete old SAR message if it doesn't match */ if ((frnd_msg->u.s12[0].hdr & HDR_KEY_MASK) != hdr_key) { l_queue_remove(net->frnd_msgs, frnd_msg); l_free(frnd_msg); frnd_msg = NULL; } } if (!frnd_msg) { frnd_msg = mesh_friend_msg_new(segN); frnd_msg->iv_index = iv_index; frnd_msg->src = src; frnd_msg->dst = dst; frnd_msg->ttl = ttl; l_queue_push_tail(net->frnd_msgs, frnd_msg); } else if (frnd_msg->flags & this_seg_flag) /* Ignore dup segs */ return; cnt = frnd_msg->cnt_in; frnd_msg->flags |= this_seg_flag; frnd_msg->u.s12[cnt].hdr = hdr; frnd_msg->u.s12[cnt].seq = seq; memcpy(frnd_msg->u.s12[cnt].data, data, size); /* Last segment could be short */ if (segN == segO) frnd_msg->last_len = size; l_debug("RXed Seg %d, Flags %8.8x (cnt: %d)", segO, frnd_msg->flags, cnt); /* In reality, if one of these is true, then *both* must be true */ if ((cnt == segN) || (frnd_msg->flags == expected)) { l_debug("Full ACK"); send_frnd_ack(net, dst, src, hdr, frnd_msg->flags); if (frnd_msg->ttl > 1) { frnd_msg->ttl--; /* Add to friends cache */ l_queue_foreach(net->friends, enqueue_friend_pkt, frnd_msg); } /* Remove from "in progress" queue */ l_queue_remove(net->frnd_msgs, frnd_msg); /* TODO Optimization(?): Unicast messages keep this buffer */ l_free(frnd_msg); return; } /* Always ACK if this is the largest outstanding segment */ if ((largest & frnd_msg->flags) == largest) { l_debug("Partial ACK"); send_frnd_ack(net, dst, src, hdr, frnd_msg->flags); } frnd_msg->cnt_in++; } static bool seg_rxed(struct mesh_net *net, bool frnd, uint32_t iv_index, uint8_t ttl, uint32_t seq, uint16_t net_idx, uint16_t src, uint16_t dst, uint8_t key_aid, bool szmic, uint16_t seqZero, uint8_t segO, uint8_t segN, const uint8_t *data, uint8_t size) { struct mesh_sar *sar_in = NULL; uint16_t seg_off = 0; uint32_t expected, this_seg_flag, largest, seqAuth; bool reset_seg_to = true; /* * DST could receive additional Segments after * completing due to a lost ACK, so re-ACK and discard */ sar_in = l_queue_find(net->sar_in, match_sar_remote, L_UINT_TO_PTR(src)); /* Discard *old* incoming-SAR-in-progress if this segment newer */ seqAuth = seq_auth(seq, seqZero); if (sar_in && (sar_in->seqAuth != seqAuth || sar_in->iv_index != iv_index)) { bool newer; if (iv_index > sar_in->iv_index) newer = true; else if (iv_index == sar_in->iv_index) newer = seqAuth > sar_in->seqAuth; else newer = false; if (newer) { /* Cancel Old, start New */ l_queue_remove(net->sar_in, sar_in); mesh_sar_free(sar_in); sar_in = NULL; } else /* Ignore Old */ return false; } expected = 0xffffffff >> (31 - segN); if (sar_in) { l_debug("RXed (old: %04x %06x size:%d) %d of %d", seqZero, seq, size, segO, segN); /* Sanity Check--> certain things must match */ if (SEG_MAX(true, sar_in->len) != segN || sar_in->key_aid != key_aid) return false; if (sar_in->flags == expected) { /* Re-Send ACK for full msg */ send_net_ack(net, sar_in, expected); return true; } else if (sar_in->delete) /* Ignore cancelled */ return false; } else { uint16_t len = MAX_SEG_TO_LEN(segN); l_debug("RXed (new: %04x %06x size: %d len: %d) %d of %d", seqZero, seq, size, len, segO, segN); l_debug("Queue Size: %d", l_queue_length(net->sar_in)); sar_in = mesh_sar_new(len); sar_in->seqAuth = seqAuth; sar_in->iv_index = iv_index; sar_in->src = dst; sar_in->remote = src; sar_in->seqZero = seqZero; sar_in->key_aid = key_aid; sar_in->len = len; sar_in->last_seg = 0xff; sar_in->net_idx = net_idx; sar_in->msg_timeout = l_timeout_create(MSG_TO, inmsg_to, net, NULL); l_debug("First Seg %4.4x", sar_in->flags); l_queue_push_head(net->sar_in, sar_in); } seg_off = segO * MAX_SEG_LEN; memcpy(sar_in->buf + seg_off, data, size); this_seg_flag = 0x00000001 << segO; /* Don't reset Seg TO or NAK if we already have this seg */ if (this_seg_flag & sar_in->flags) reset_seg_to = false; sar_in->flags |= this_seg_flag; sar_in->ttl = ttl; /* Msg length only definitive on last segment */ if (segO == segN) sar_in->len = segN * MAX_SEG_LEN + size; if (sar_in->flags == expected) { /* Got it all */ send_net_ack(net, sar_in, expected); msg_rxed(net, frnd, iv_index, ttl, seq, net_idx, sar_in->remote, dst, key_aid, true, szmic, sar_in->seqZero, sar_in->buf, sar_in->len); /* Kill Inter-Seg timeout */ l_timeout_remove(sar_in->seg_timeout); sar_in->seg_timeout = NULL; /* Start delete timer */ sar_in->delete = true; l_timeout_modify(sar_in->msg_timeout, SAR_DEL); return true; } if (reset_seg_to) { /* Restart Inter-Seg Timeout */ l_timeout_remove(sar_in->seg_timeout); /* if this is the largest outstanding segment, send NAK now */ largest = (0xffffffff << segO) & expected; if ((largest & sar_in->flags) == largest) send_net_ack(net, sar_in, sar_in->flags); sar_in->seg_timeout = l_timeout_create(SEG_TO, inseg_to, net, NULL); } else largest = 0; l_debug("NAK: %d expected:%08x largest:%08x flags:%08x", reset_seg_to, expected, largest, sar_in->flags); return false; } static bool ctl_received(struct mesh_net *net, uint32_t net_key_id, uint32_t iv_index, uint8_t ttl, uint32_t seq, uint16_t src, uint16_t dst, uint8_t opcode, int8_t rssi, const uint8_t *pkt, uint8_t len) { uint8_t msg[12]; uint8_t rsp_ttl = DEFAULT_TTL; uint8_t n = 0; uint16_t net_idx; if (ttl > 1) { uint32_t hdr = opcode << OPCODE_HDR_SHIFT; uint8_t frnd_ttl = ttl - 1; if (friend_packet_queue(net, iv_index, true, frnd_ttl, seq, src, dst, hdr, pkt, len)) return true; } /* Don't process other peoples Unicast destinations */ if (dst < 0x8000 && (dst < net->src_addr || dst > net->last_addr)) return false; switch (opcode) { default: l_error("Unsupported Ctl Opcode: %2.2x", opcode); break; case NET_OP_FRND_POLL: if (len != 1 || ttl || pkt[0] > 1) return false; print_packet("Rx-NET_OP_FRND_POLL", pkt, len); friend_poll(net, src, !!(pkt[0]), l_queue_find(net->friends, match_by_friend, L_UINT_TO_PTR(src))); break; case NET_OP_FRND_REQUEST: if (!net->friend_enable) return false; if (!IS_ALL_NODES(dst) && dst != FRIENDS_ADDRESS) return false; if (len != 10 || ttl) return false; print_packet("Rx-NET_OP_FRND_REQUEST", pkt, len); net_idx = key_id_to_net_idx(net, net_key_id, NULL); friend_request(net, net_idx, src, pkt[0], pkt[1], l_get_be32(pkt + 1) & 0xffffff, l_get_be16(pkt + 5), pkt[7], l_get_be16(pkt + 8), rssi); break; case NET_OP_FRND_CLEAR_CONFIRM: if (len != 4) return false; print_packet("Rx-NET_OP_FRND_CLEAR_CONFIRM", pkt, len); friend_clear_confirm(net, src, l_get_be16(pkt), l_get_be16(pkt + 2)); break; case NET_OP_FRND_CLEAR: if (len != 4 || dst != net->src_addr) return false; print_packet("Rx-NET_OP_FRND_CLEAR", pkt, len); friend_clear(net, src, l_get_be16(pkt), l_get_be16(pkt + 2), l_queue_find(net->friends, match_by_friend, L_UINT_TO_PTR(l_get_be16(pkt)))); l_debug("Remaining Friends: %d", l_queue_length(net->friends)); break; case NET_OP_PROXY_SUB_ADD: if (ttl) return false; print_packet("Rx-NET_OP_PROXY_SUB_ADD", pkt, len); friend_sub_add(net, l_queue_find(net->friends, match_by_friend, L_UINT_TO_PTR(src)), pkt, len); break; case NET_OP_PROXY_SUB_REMOVE: if (ttl) return false; print_packet("Rx-NET_OP_PROXY_SUB_REMOVE", pkt, len); friend_sub_del(net, l_queue_find(net->friends, match_by_friend, L_UINT_TO_PTR(src)), pkt, len); break; case NET_OP_PROXY_SUB_CONFIRM: if (ttl) return false; print_packet("Rx-NET_OP_PROXY_SUB_CONFIRM", pkt, len); break; case NET_OP_HEARTBEAT: if (net->hb_sub.enabled && src == net->hb_sub.src) { uint8_t hops = pkt[0] - ttl + 1; print_packet("Rx-NET_OP_HEARTBEAT", pkt, len); if (net->hb_sub.count != 0xffff) net->hb_sub.count++; if (net->hb_sub.min_hops > hops) net->hb_sub.min_hops = hops; if (net->hb_sub.max_hops < hops) net->hb_sub.max_hops = hops; l_debug("HB: cnt:%4.4x min:%2.2x max:%2.2x", net->hb_sub.count, net->hb_sub.min_hops, net->hb_sub.max_hops); } break; } if (n) mesh_net_transport_send(net, 0, 0, mesh_net_get_iv_index(net), rsp_ttl, 0, dst & 0x8000 ? 0 : dst, src, msg, n); return true; } static bool find_fast_hash(const void *a, const void *b) { const uint64_t *entry = a; const uint64_t *test = b; return *entry == *test; } static bool check_fast_cache(uint64_t hash) { void *found = l_queue_find(fast_cache, find_fast_hash, &hash); uint64_t *new_hash; if (found) return false; if (l_queue_length(fast_cache) >= FAST_CACHE_SIZE) new_hash = l_queue_pop_head(fast_cache); else new_hash = l_malloc(sizeof(hash)); *new_hash = hash; l_queue_push_tail(fast_cache, new_hash); return true; } static bool match_by_dst(const void *a, const void *b) { const struct mesh_destination *dest = a; uint16_t dst = L_PTR_TO_UINT(b); return dest->dst == dst; } static void send_relay_pkt(struct mesh_net *net, uint8_t *data, uint8_t size) { uint8_t packet[30]; struct mesh_io *io = net->io; struct mesh_io_send_info info = { .type = MESH_IO_TIMING_TYPE_GENERAL, .u.gen.interval = net->relay.interval, .u.gen.cnt = net->relay.count, .u.gen.min_delay = DEFAULT_MIN_DELAY, .u.gen.max_delay = DEFAULT_MAX_DELAY }; packet[0] = MESH_AD_TYPE_NETWORK; memcpy(packet + 1, data, size); mesh_io_send(io, &info, packet, size + 1); } static bool simple_match(const void *a, const void *b) { return a == b; } static void send_msg_pkt_oneshot(void *user_data) { struct oneshot_tx *tx = user_data; struct mesh_net *net; struct mesh_io_send_info info; struct net_queue_data net_data = { .info = NULL, .data = tx->packet + 1, .len = tx->size - 1, .relay_advice = RELAY_NONE, }; /* Send to local nodes first */ l_queue_foreach(nets, net_rx, &net_data); /* Make sure specific network still valid */ net = l_queue_find(nets, simple_match, tx->net); if (!net || net_data.relay_advice == RELAY_DISALLOWED) { l_free(tx); return; } tx->packet[0] = MESH_AD_TYPE_NETWORK; info.type = MESH_IO_TIMING_TYPE_GENERAL; info.u.gen.interval = tx->interval; info.u.gen.cnt = tx->cnt; info.u.gen.min_delay = DEFAULT_MIN_DELAY; /* No extra randomization when sending regular mesh messages */ info.u.gen.max_delay = DEFAULT_MIN_DELAY; mesh_io_send(net->io, &info, tx->packet, tx->size); l_free(tx); } static void send_msg_pkt(struct mesh_net *net, uint8_t cnt, uint16_t interval, uint8_t *packet, uint8_t size) { struct oneshot_tx *tx = l_new(struct oneshot_tx, 1); tx->net = net; tx->interval = interval; tx->cnt = cnt; tx->size = size; memcpy(tx->packet, packet, size); l_idle_oneshot(send_msg_pkt_oneshot, tx, NULL); } static enum _relay_advice packet_received(void *user_data, uint32_t net_key_id, uint16_t net_idx, bool frnd, uint32_t iv_index, const void *data, uint8_t size, int8_t rssi) { struct mesh_net *net = user_data; const uint8_t *msg = data; uint8_t app_msg_len; uint8_t net_ttl, key_aid, net_segO, net_segN, net_opcode; uint32_t net_seq, cache_cookie; uint16_t net_src, net_dst, net_seqZero; uint8_t packet[31]; bool net_ctl, net_segmented, net_szmic, net_relay; memcpy(packet + 2, data, size); print_packet("RX: Network [clr] :", packet + 2, size); if (!mesh_crypto_packet_parse(packet + 2, size, &net_ctl, &net_ttl, &net_seq, &net_src, &net_dst, &cache_cookie, &net_opcode, &net_segmented, &key_aid, &net_szmic, &net_relay, &net_seqZero, &net_segO, &net_segN, &msg, &app_msg_len)) { l_error("Failed to parse packet content"); return RELAY_NONE; } if (net_dst == 0) { l_error("illegal parms: DST: %4.4x Ctl: %d TTL: %2.2x", net_dst, net_ctl, net_ttl); return RELAY_NONE; } /* Ignore if we originally sent this */ if (is_us(net, net_src, true)) return RELAY_NONE; /* * As a Relay, suppress repeats of last N packets that pass through * The "cache_cookie" should be unique part of App message. */ if (msg_in_cache(net, net_src, net_seq, cache_cookie)) return RELAY_NONE; l_debug("RX: Network %04x -> %04x : TTL 0x%02x : IV : %8.8x SEQ 0x%06x", net_src, net_dst, net_ttl, iv_index, net_seq); if (is_us(net, net_dst, false) || (net_ctl && net_opcode == NET_OP_HEARTBEAT)) { l_debug("RX: App 0x%04x -> 0x%04x : TTL 0x%02x : SEQ 0x%06x", net_src, net_dst, net_ttl, net_seq); if (net_ctl) { l_debug("CTL - %4.4x RX", net_seqZero); if (net_opcode == NET_OP_SEG_ACKNOWLEDGE) { /* Illegal to send ACK to non-Unicast Addr */ if (net_dst & 0x8000) return RELAY_NONE; /* Pedantic check for correct size */ if (app_msg_len != 7) return RELAY_NONE; /* If this is an ACK to our friend queue-only */ if (is_lpn_friend(net, net_dst)) friend_ack_rxed(net, iv_index, net_seq, net_src, net_dst, msg); else ack_received(net, false, net_src, net_dst, net_seqZero, l_get_be32(msg + 3)); } else { ctl_received(net, net_key_id, iv_index, net_ttl, net_seq, net_src, net_dst, net_opcode, rssi, msg, app_msg_len); } } else if (net_segmented) { /* * If we accept SAR packets to non-Unicast, then * Friend Sar at least needs to be Unicast Only */ if (is_lpn_friend(net, net_dst) && !(net_dst & 0x8000)) { /* * Check TTL >= 2 before accepting segments * for Friends */ if (net_ttl >= 2) { friend_seg_rxed(net, iv_index, net_ttl, net_seq, net_src, net_dst, l_get_be32(packet + 2 + 9), msg, app_msg_len); } } else { seg_rxed(net, NULL, iv_index, net_ttl, net_seq, net_idx, net_src, net_dst, key_aid, net_szmic, net_seqZero, net_segO, net_segN, msg, app_msg_len); } } else { msg_rxed(net, NULL, iv_index, net_ttl, net_seq, net_idx, net_src, net_dst, key_aid, false, false, net_seq & SEQ_ZERO_MASK, msg, app_msg_len); } /* If this is one of our Unicast addresses, disallow relay */ if (IS_UNICAST(net_dst)) return RELAY_DISALLOWED; } /* * Messages that are encrypted with friendship credentials * should *always* be relayed */ if (frnd) return RELAY_ALWAYS; /* If relay not enable, or no more hops allowed */ if (!net->relay.enable || net_ttl < 0x02) return RELAY_NONE; /* Group or Virtual destinations should *always* be relayed */ if (IS_GROUP(net_dst) || IS_VIRTUAL(net_dst)) return RELAY_ALWAYS; /* Unicast destinations for other nodes *may* be relayed */ else if (IS_UNICAST(net_dst)) return RELAY_ALLOWED; /* Otherwise, do not make a relay decision */ else return RELAY_NONE; } static void net_rx(void *net_ptr, void *user_data) { struct net_queue_data *data = user_data; struct mesh_net *net = net_ptr; enum _relay_advice relay_advice; uint8_t *out; size_t out_size; uint32_t net_key_id; uint16_t net_idx; int8_t rssi = 0; bool frnd; bool ivi_net = !!(net->iv_index & 1); bool ivi_pkt = !!(data->data[0] & 0x80); /* if IVI flag differs, use previous IV Index */ uint32_t iv_index = net->iv_index - (ivi_pkt ^ ivi_net); net_key_id = net_key_decrypt(iv_index, data->data, data->len, &out, &out_size); if (!net_key_id) return; if (!data->seen) { data->seen = true; print_packet("RX: Network [enc] :", data->data, data->len); } if (data->info) { net->instant = data->info->instant; net->chan = data->info->chan; rssi = data->info->rssi; } net_idx = key_id_to_net_idx(net, net_key_id, &frnd); if (net_idx == NET_IDX_INVALID) return; relay_advice = packet_received(net, net_key_id, net_idx, frnd, iv_index, out, out_size, rssi); if (relay_advice > data->relay_advice) { /* * If packet was encrypted with friendship credentials, * relay it using master credentials */ if (frnd && !mesh_net_get_key(net, false, net_idx, &net_key_id)) return; data->iv_index = iv_index; data->relay_advice = relay_advice; data->net_key_id = net_key_id; data->net = net; data->out = out; data->out_size = out_size; } } static void net_msg_recv(void *user_data, struct mesh_io_recv_info *info, const uint8_t *data, uint16_t len) { uint64_t hash; bool isNew; struct net_queue_data net_data = { .info = info, .data = data + 1, .len = len - 1, .relay_advice = RELAY_NONE, .seen = false, }; if (len < 9) return; hash = l_get_le64(data + 1); /* Only process packet once per reception */ isNew = check_fast_cache(hash); if (!isNew) return; l_queue_foreach(nets, net_rx, &net_data); if (net_data.relay_advice == RELAY_ALWAYS || net_data.relay_advice == RELAY_ALLOWED) { uint8_t ttl = net_data.out[1] & TTL_MASK; net_data.out[1] &= ~TTL_MASK; net_data.out[1] |= ttl - 1; net_key_encrypt(net_data.net_key_id, net_data.iv_index, net_data.out, net_data.out_size); send_relay_pkt(net_data.net, net_data.out, net_data.out_size); } } static void iv_upd_to(struct l_timeout *upd_timeout, void *user_data) { struct mesh_net *net = user_data; switch (net->iv_upd_state) { case IV_UPD_UPDATING: if (l_queue_length(net->sar_out) || l_queue_length(net->sar_queue)) { l_debug("don't leave IV Update until sar_out empty"); l_timeout_modify(net->iv_update_timeout, 10); break; } l_debug("iv_upd_state = IV_UPD_NORMAL_HOLD"); net->iv_upd_state = IV_UPD_NORMAL_HOLD; l_timeout_modify(net->iv_update_timeout, IV_IDX_UPD_MIN); if (net->iv_update) mesh_net_set_seq_num(net, 0); net->iv_update = false; mesh_config_write_iv_index(node_config_get(net->node), net->iv_index, false); l_queue_foreach(net->subnets, refresh_beacon, net); queue_friend_update(net); l_queue_clear(net->msg_cache, l_free); break; case IV_UPD_INIT: case IV_UPD_NORMAL_HOLD: case IV_UPD_NORMAL: l_timeout_remove(upd_timeout); net->iv_update_timeout = NULL; l_debug("iv_upd_state = IV_UPD_NORMAL"); net->iv_upd_state = IV_UPD_NORMAL; if (net->iv_update) mesh_net_set_seq_num(net, 0); net->iv_update = false; if (net->seq_num > IV_UPDATE_SEQ_TRIGGER) mesh_net_iv_index_update(net); break; } } static int key_refresh_phase_two(struct mesh_net *net, uint16_t idx) { struct mesh_subnet *subnet; if (!net) return MESH_STATUS_UNSPECIFIED_ERROR; subnet = l_queue_find(net->subnets, match_key_index, L_UINT_TO_PTR(idx)); if (!subnet || !subnet->net_key_upd) return MESH_STATUS_INVALID_NETKEY; l_debug("Key refresh procedure phase 2: start using new net TX keys"); if (subnet->kr_phase == KEY_REFRESH_PHASE_TWO) return MESH_STATUS_SUCCESS; /* Stop beaconing on old key */ if (net->snb_enable) net_key_beacon_disable(subnet->net_key_tx, false); if (net->mpb_enable) net_key_beacon_disable(subnet->net_key_tx, true); subnet->key_refresh = 1; subnet->net_key_tx = subnet->net_key_upd; /* * TODO: Provisioner may need to stay in phase three until * it hears beacons from all the nodes */ subnet->kr_phase = KEY_REFRESH_PHASE_TWO; /* Start beaconing on new key */ if (net->snb_enable) net_key_beacon_enable(subnet->net_key_tx, false, 0); if (net->mpb_enable) net_key_beacon_enable(subnet->net_key_tx, true, net->mpb_period); refresh_beacon(subnet, net); queue_friend_update(net); l_queue_foreach(net->friends, frnd_kr_phase2, net); if (!mesh_config_net_key_set_phase(node_config_get(net->node), idx, KEY_REFRESH_PHASE_TWO)) return MESH_STATUS_STORAGE_FAIL; return MESH_STATUS_SUCCESS; } static int key_refresh_finish(struct mesh_net *net, uint16_t idx) { struct mesh_subnet *subnet; if (!net) return MESH_STATUS_UNSPECIFIED_ERROR; subnet = l_queue_find(net->subnets, match_key_index, L_UINT_TO_PTR(idx)); if (!subnet || !subnet->net_key_upd) return MESH_STATUS_INVALID_NETKEY; if (subnet->kr_phase == KEY_REFRESH_PHASE_NONE) return MESH_STATUS_SUCCESS; l_debug("Key refresh phase 3: use new keys only, discard old ones"); /* Switch to using new keys, discard old ones */ net_key_unref(subnet->net_key_cur); subnet->net_key_tx = subnet->net_key_cur = subnet->net_key_upd; subnet->net_key_upd = 0; subnet->key_refresh = 0; subnet->kr_phase = KEY_REFRESH_PHASE_NONE; refresh_beacon(subnet, net); queue_friend_update(net); l_queue_foreach(net->friends, frnd_kr_phase3, net); appkey_finalize(net, idx); if (!mesh_config_net_key_set_phase(node_config_get(net->node), idx, KEY_REFRESH_PHASE_NONE)) return MESH_STATUS_STORAGE_FAIL; return MESH_STATUS_SUCCESS; } static bool update_kr_state(struct mesh_subnet *subnet, bool kr, uint32_t id) { /* Figure out the key refresh phase */ if (kr) { if (subnet->kr_phase == KEY_REFRESH_PHASE_ONE && id == subnet->net_key_upd) { l_debug("Beacon based KR phase 2 change"); return (key_refresh_phase_two(subnet->net, subnet->idx) == MESH_STATUS_SUCCESS); } } else { if (id == subnet->net_key_upd) { l_debug("Beacon based KR phase 3 change"); return (key_refresh_finish(subnet->net, subnet->idx) == MESH_STATUS_SUCCESS); } } return false; } static bool update_iv_ivu_state(struct mesh_net *net, uint32_t iv_index, bool ivu) { if ((iv_index - ivu) > (net->iv_index - net->iv_update)) { /* Don't accept IV_Index changes when performing SAR Out */ if (l_queue_length(net->sar_out)) return false; } /* If first beacon seen, accept without judgement */ if (net->iv_upd_state == IV_UPD_INIT) { if (ivu) { /* Ignore beacons with IVU if IV already updated */ if (iv_index == net->iv_index && !net->iv_update) return false; /* * Other devices will be accepting old or new iv_index, * but we don't know how far through update they are. * Starting permissive state will allow us maximum * (96 hours) to resync */ l_debug("iv_upd_state = IV_UPD_UPDATING"); net->iv_upd_state = IV_UPD_UPDATING; net->iv_update_timeout = l_timeout_create( IV_IDX_UPD_MIN, iv_upd_to, net, NULL); } else { l_debug("iv_upd_state = IV_UPD_NORMAL"); net->iv_upd_state = IV_UPD_NORMAL; } } else if (ivu) { /* Ignore beacons with IVU if they come too soon */ if (!net->iv_update && net->iv_upd_state == IV_UPD_NORMAL_HOLD) { l_error("Update attempted too soon"); return false; } /* Ignore beacons with IVU if IV already updated */ if (iv_index == net->iv_index) return false; /* Ignore beacon with invalid IV index value */ if (net->iv_update && iv_index == net->iv_index + 1) return false; if (!net->iv_update) { l_debug("iv_upd_state = IV_UPD_UPDATING"); net->iv_upd_state = IV_UPD_UPDATING; net->iv_update_timeout = l_timeout_create( IV_IDX_UPD_MIN, iv_upd_to, net, NULL); } } else if (net->iv_update) { l_error("IVU clear attempted too soon"); return false; } if ((iv_index - ivu) > (net->iv_index - net->iv_update)) mesh_net_set_seq_num(net, 0); if (ivu != net->iv_update || iv_index != net->iv_index) { struct mesh_config *cfg = node_config_get(net->node); mesh_config_write_iv_index(cfg, iv_index, ivu); /* Cleanup Replay Protection List NVM */ rpl_update(net->node, iv_index); } node_property_changed(net->node, "IVIndex"); net->iv_index = iv_index; net->iv_update = ivu; queue_friend_update(net); return true; } static void process_beacon(void *net_ptr, void *user_data) { bool updated = false; struct mesh_net *net = net_ptr; struct net_beacon_data *beacon_data = user_data; uint32_t ivi; bool ivu, kr, local_kr; struct mesh_subnet *subnet, *primary_subnet; ivi = beacon_data->ivi; /* Ignore out-of-range IV_Index for this network */ if ((net->iv_index + IV_IDX_DIFF_RANGE < ivi) || (ivi < net->iv_index)) return; /* Ignore beacons not in this universe */ subnet = l_queue_find(net->subnets, match_key_id, L_UINT_TO_PTR(beacon_data->net_key_id)); if (!subnet) return; /* * @MshPRFv1.0.1 section 3.10.5: IV Update procedure * If this node is a member of a primary subnet and receives a Secure * Network beacon on a secondary subnet with an IV Index greater than * the last known IV Index of the primary subnet, the Secure Network * beacon shall be ignored. */ primary_subnet = get_primary_subnet(net); if (primary_subnet && subnet != primary_subnet && ivi > net->iv_index) return; /* Get IVU and KR boolean bits from beacon */ ivu = beacon_data->ivu; kr = beacon_data->kr; local_kr = !!(subnet->kr_phase == KEY_REFRESH_PHASE_TWO); /* We have officially *seen* this beacon now */ beacon_data->processed = true; /* * Ignore local beacons and beacons that don't change anything, * unless we're doing IV Recovery */ if (!beacon_data->local) { if (net->iv_upd_state == IV_UPD_INIT || ivi != net->iv_index || ivu != net->iv_update) updated |= update_iv_ivu_state(net, ivi, ivu); if (kr != local_kr) updated |= update_kr_state(subnet, kr, beacon_data->net_key_id); if (updated) net_key_beacon_refresh(beacon_data->net_key_id, net->iv_index, !!(subnet->kr_phase == KEY_REFRESH_PHASE_TWO), net->iv_update, false); } } static void beacon_recv(void *user_data, struct mesh_io_recv_info *info, const uint8_t *data, uint16_t len) { struct net_beacon_data beacon_data = { .local = false, .processed = false, }; beacon_data.net_key_id = net_key_beacon(data, len, &beacon_data.ivi, &beacon_data.ivu, &beacon_data.kr); if (!beacon_data.net_key_id) return; l_queue_foreach(nets, process_beacon, &beacon_data); if (beacon_data.processed) net_key_beacon_seen(beacon_data.net_key_id); } void net_local_beacon(uint32_t net_key_id, uint32_t ivi, bool ivu, bool kr) { struct net_beacon_data beacon_data = { .local = true, .processed = false, .net_key_id = net_key_id, .ivu = ivu, .kr = kr, .ivi = ivi, }; /* Deliver locally generated beacons to all nodes */ l_queue_foreach(nets, process_beacon, &beacon_data); } bool mesh_net_set_snb_mode(struct mesh_net *net, bool enable) { if (!net) return false; if (net->snb_enable == enable) return true; net->snb_enable = enable; if (enable) l_queue_foreach(net->subnets, refresh_beacon, net); l_queue_foreach(net->subnets, enable_snb, net); queue_friend_update(net); return true; } bool mesh_net_set_mpb_mode(struct mesh_net *net, bool enable, uint8_t period, bool initialize) { uint8_t old_period; bool old_enable; if (!net) return false; old_enable = net->mpb_enable; old_period = net->mpb_period; if (enable) net->mpb_period = period; if (old_enable == enable && old_period == net->mpb_period) return true; if (enable && !initialize) { /* If enable with different period, disable and re-enable */ net->mpb_enable = false; l_queue_foreach(net->subnets, enable_mpb, net); } net->mpb_enable = enable; if (enable) l_queue_foreach(net->subnets, refresh_beacon, net); l_queue_foreach(net->subnets, enable_mpb, net); queue_friend_update(net); return true; } /* This function is called when network keys are restored from storage. */ bool mesh_net_set_key(struct mesh_net *net, uint16_t idx, const uint8_t *key, const uint8_t *new_key, uint8_t phase) { struct mesh_subnet *subnet; /* Current key must be always present */ if (!key) return false; /* If key refresh is in progress, a new key must be present */ if (phase != KEY_REFRESH_PHASE_NONE && !new_key) return false; /* Check if the subnet with the specified index already exists */ subnet = l_queue_find(net->subnets, match_key_index, L_UINT_TO_PTR(idx)); if (subnet) return false; subnet = add_key(net, idx, key); if (!subnet) return false; if (new_key && phase) subnet->net_key_upd = net_key_add(new_key); /* Preserve key refresh state to generate secure beacon flags*/ if (phase == KEY_REFRESH_PHASE_TWO) { subnet->key_refresh = 1; subnet->net_key_tx = subnet->net_key_upd; if (net->snb_enable) { /* Switch beaconing key */ net_key_beacon_disable(subnet->net_key_cur, false); net_key_beacon_enable(subnet->net_key_upd, false, 0); } if (net->mpb_enable) { /* Switch beaconing key */ net_key_beacon_disable(subnet->net_key_cur, true); net_key_beacon_enable(subnet->net_key_upd, true, net->mpb_period); } } subnet->kr_phase = phase; net_key_beacon_refresh(subnet->net_key_tx, net->iv_index, !!(subnet->kr_phase == KEY_REFRESH_PHASE_TWO), net->iv_update, false); return true; } bool mesh_net_attach(struct mesh_net *net, struct mesh_io *io) { bool first; if (!net) return false; first = l_queue_isempty(nets); if (first) { const uint8_t snb[] = {MESH_AD_TYPE_BEACON, 1}; const uint8_t mpb[] = {MESH_AD_TYPE_BEACON, 2}; const uint8_t pkt[] = {MESH_AD_TYPE_NETWORK}; if (!nets) nets = l_queue_new(); if (!fast_cache) fast_cache = l_queue_new(); mesh_io_register_recv_cb(io, snb, sizeof(snb), beacon_recv, NULL); mesh_io_register_recv_cb(io, mpb, sizeof(mpb), beacon_recv, NULL); mesh_io_register_recv_cb(io, pkt, sizeof(pkt), net_msg_recv, NULL); } if (l_queue_find(nets, simple_match, net)) return false; l_queue_push_head(nets, net); net->io = io; return true; } struct mesh_io *mesh_net_detach(struct mesh_net *net) { const uint8_t snb[] = {MESH_AD_TYPE_BEACON, 1}; const uint8_t mpb[] = {MESH_AD_TYPE_BEACON, 2}; const uint8_t pkt[] = {MESH_AD_TYPE_NETWORK}; struct mesh_io *io; uint8_t type = 0; if (!net || !net->io) return NULL; io = net->io; mesh_io_send_cancel(net->io, &type, 1); /* Only deregister io if this is the last network detached.*/ if (l_queue_length(nets) < 2) { mesh_io_deregister_recv_cb(io, snb, sizeof(snb)); mesh_io_deregister_recv_cb(io, mpb, sizeof(mpb)); mesh_io_deregister_recv_cb(io, pkt, sizeof(pkt)); } net->io = NULL; l_queue_remove(nets, net); return io; } bool mesh_net_iv_index_update(struct mesh_net *net) { if (net->iv_upd_state != IV_UPD_NORMAL) return false; l_debug("iv_upd_state = IV_UPD_UPDATING"); l_queue_clear(net->msg_cache, l_free); if (!mesh_config_write_iv_index(node_config_get(net->node), net->iv_index + 1, true)) return false; net->iv_upd_state = IV_UPD_UPDATING; net->iv_index++; net->iv_update = true; l_queue_foreach(net->subnets, refresh_beacon, net); queue_friend_update(net); net->iv_update_timeout = l_timeout_create( IV_IDX_UPD_MIN, iv_upd_to, net, NULL); return true; } bool mesh_net_dst_reg(struct mesh_net *net, uint16_t dst) { struct mesh_destination *dest = l_queue_find(net->destinations, match_by_dst, L_UINT_TO_PTR(dst)); if (IS_UNASSIGNED(dst) || IS_ALL_NODES(dst)) return false; if (!dest) { dest = l_new(struct mesh_destination, 1); if (dst < 0x8000) l_queue_push_head(net->destinations, dest); else l_queue_push_tail(net->destinations, dest); } dest->dst = dst; dest->ref_cnt++; return true; } bool mesh_net_dst_unreg(struct mesh_net *net, uint16_t dst) { struct mesh_destination *dest = l_queue_find(net->destinations, match_by_dst, L_UINT_TO_PTR(dst)); if (!dest) return false; if (dest->ref_cnt) dest->ref_cnt--; if (dest->ref_cnt) return true; l_queue_remove(net->destinations, dest); l_free(dest); return true; } static bool send_seg(struct mesh_net *net, uint8_t cnt, uint16_t interval, struct mesh_sar *msg, uint8_t segO) { struct mesh_subnet *subnet; uint8_t seg_len; uint8_t gatt_data[30]; uint8_t *packet = gatt_data; uint8_t packet_len; uint8_t segN = SEG_MAX(msg->segmented, msg->len); uint16_t seg_off = SEG_OFF(segO); uint32_t seq_num; if (msg->segmented) { /* Send each segment on unique seq_num */ seq_num = mesh_net_next_seq_num(net); if (msg->len - seg_off > SEG_OFF(1)) seg_len = SEG_OFF(1); else seg_len = msg->len - seg_off; } else { /* Send on same seq_num used for Access Layer */ seq_num = msg->seqAuth; seg_len = msg->len; } /* Start IV Update procedure when we hit our trigger point */ if (!msg->frnd && net->seq_num > IV_UPDATE_SEQ_TRIGGER) mesh_net_iv_index_update(net); l_debug("segN %d segment %d seg_off %d", segN, segO, seg_off); /* TODO: Are we RXing on an LPN's behalf? Then set RLY bit */ if (!mesh_crypto_packet_build(false, msg->ttl, seq_num, msg->src, msg->remote, 0, msg->segmented, msg->key_aid, msg->szmic, false, msg->seqZero, segO, segN, msg->buf + seg_off, seg_len, packet + 1, &packet_len)) { l_error("Failed to build packet"); return false; } print_packet("Clr-Net Tx", packet + 1, packet_len); subnet = l_queue_find(net->subnets, match_key_index, L_UINT_TO_PTR(msg->net_idx)); if (!subnet) return false; if (!net_key_encrypt(subnet->net_key_tx, msg->iv_index, packet + 1, packet_len)) { l_error("Failed to encode packet"); return false; } send_msg_pkt(net, cnt, interval, packet, packet_len + 1); msg->last_seg = segO; return true; } void mesh_net_send_seg(struct mesh_net *net, uint32_t net_key_id, uint32_t iv_index, uint8_t ttl, uint32_t seq, uint16_t src, uint16_t dst, uint32_t hdr, const void *seg, uint16_t seg_len) { uint8_t packet[30]; uint8_t packet_len; bool segmented = !!((hdr >> SEG_HDR_SHIFT) & true); uint8_t key_aid = (hdr >> KEY_HDR_SHIFT) & KEY_ID_MASK; bool szmic = !!((hdr >> SZMIC_HDR_SHIFT) & true); uint16_t seqZero = (hdr >> SEQ_ZERO_HDR_SHIFT) & SEQ_ZERO_MASK; uint8_t segO = (hdr >> SEGO_HDR_SHIFT) & SEG_MASK; uint8_t segN = (hdr >> SEGN_HDR_SHIFT) & SEG_MASK; /* * MshPRFv1.0.1 section 3.4.5.2, Interface output filter: * If TTL is set to 1, message shall be dropped. */ if (ttl == 1) return; /* TODO: Only used for current POLLed segments to LPNs */ l_debug("SEQ: %6.6x", seq + segO); l_debug("SEQ0: %6.6x", seq); l_debug("segO: %d", segO); if (!mesh_crypto_packet_build(false, ttl, seq, src, dst, 0, segmented, key_aid, szmic, false, seqZero, segO, segN, seg, seg_len, packet + 1, &packet_len)) { l_error("Failed to build packet"); return; } if (!net_key_encrypt(net_key_id, iv_index, packet + 1, packet_len)) { l_error("Failed to encode packet"); return; } send_msg_pkt(net, net->tx_cnt, net->tx_interval, packet, packet_len + 1); l_debug("TX: Friend Seg-%d %04x -> %04x : len %u) : TTL %d : SEQ %06x", segO, src, dst, packet_len, ttl, seq); print_packet("TX: Friend", packet + 1, packet_len); } bool mesh_net_app_send(struct mesh_net *net, bool frnd_cred, uint16_t src, uint16_t dst, uint8_t key_aid, uint16_t net_idx, uint8_t ttl, uint8_t cnt, uint16_t interval, uint32_t seq, uint32_t iv_index, bool segmented, bool szmic, const void *msg, uint16_t msg_len) { struct mesh_sar *payload = NULL; uint8_t seg, seg_max; bool result; if (!net || msg_len > 384) return false; if (!src) src = net->src_addr; if (!src || !dst) return false; if (ttl == DEFAULT_TTL) ttl = net->default_ttl; /* Long and sizmic messages *require* segmenting */ segmented |= szmic; seg_max = SEG_MAX(segmented, msg_len); segmented |= !!(seg_max); /* First enqueue to any Friends and internal models */ result = msg_rxed(net, false, iv_index, ttl, seq, net_idx, src, dst, key_aid, segmented, szmic, seq & SEQ_ZERO_MASK, msg, msg_len); /* * If addressed to a unicast address and successfully enqueued, * or delivered to one of our Unicast addresses we are done */ if ((result && IS_UNICAST(dst)) || src == dst || (dst >= net->src_addr && dst <= net->last_addr)) return true; /* * MshPRFv1.0.1 section 3.4.5.2, Interface output filter: * If TTL is set to 1, message shall be dropped. */ if (ttl == 1) return true; /* Setup OTA Network send */ payload = mesh_sar_new(msg_len); memcpy(payload->buf, msg, msg_len); payload->len = msg_len; payload->src = src; payload->remote = dst; payload->ttl = ttl; payload->szmic = szmic; payload->frnd_cred = frnd_cred; payload->key_aid = key_aid; payload->net_idx = net_idx; payload->iv_index = mesh_net_get_iv_index(net); payload->seqAuth = seq; payload->segmented = segmented; if (segmented) { payload->flags = 0xffffffff >> (31 - seg_max); payload->seqZero = seq & SEQ_ZERO_MASK; payload->id = ++net->sar_id_next; /* Single thread SAR messages to same Unicast DST */ if (l_queue_find(net->sar_out, match_sar_remote, L_UINT_TO_PTR(dst))) { /* Delay sending Outbound SAR unless prior * SAR to same DST has completed */ l_debug("OB-Queued SeqZero: %4.4x", payload->seqZero); l_queue_push_tail(net->sar_queue, payload); return true; } } result = true; if (!IS_UNICAST(dst) && segmented) { int i; for (i = 0; i < 4; i++) { for (seg = 0; seg <= seg_max && result; seg++) result = send_seg(net, cnt, interval, payload, seg); } } else { for (seg = 0; seg <= seg_max && result; seg++) result = send_seg(net, cnt, interval, payload, seg); } /* Reliable: Cache; Unreliable: Flush*/ if (result && segmented && IS_UNICAST(dst)) { l_queue_push_head(net->sar_out, payload); payload->seg_timeout = l_timeout_create(SEG_TO, outseg_to, net, NULL); payload->msg_timeout = l_timeout_create(MSG_TO, outmsg_to, net, NULL); payload->id = ++net->sar_id_next; } else mesh_sar_free(payload); return result; } void mesh_net_ack_send(struct mesh_net *net, uint32_t net_key_id, uint32_t iv_index, uint8_t ttl, uint32_t seq, uint16_t src, uint16_t dst, bool rly, uint16_t seqZero, uint32_t ack_flags) { uint32_t hdr; uint8_t data[7]; uint8_t pkt_len; uint8_t pkt[30]; /* * MshPRFv1.0.1 section 3.4.5.2, Interface output filter: * If TTL is set to 1, message shall be dropped. */ if (ttl == 1) return; hdr = NET_OP_SEG_ACKNOWLEDGE << OPCODE_HDR_SHIFT; hdr |= rly << RELAY_HDR_SHIFT; hdr |= (seqZero & SEQ_ZERO_MASK) << SEQ_ZERO_HDR_SHIFT; l_put_be32(hdr, data); l_put_be32(ack_flags, data + 3); /* Not Segmented, no Key ID associated, no segO or segN */ if (!mesh_crypto_packet_build(true, ttl, seq, src, dst, NET_OP_SEG_ACKNOWLEDGE, false, 0, false, rly, seqZero, 0, 0, data + 1, 6, pkt + 1, &pkt_len)) return; if (!net_key_id) { struct mesh_subnet *subnet = get_primary_subnet(net); net_key_id = subnet->net_key_tx; } if (!net_key_encrypt(net_key_id, iv_index, pkt + 1, pkt_len)) { l_error("Failed to encode packet"); return; } send_msg_pkt(net, net->tx_cnt, net->tx_interval, pkt, pkt_len + 1); l_debug("TX: Friend ACK %04x -> %04x : len %u : TTL %d : SEQ %06x", src, dst, pkt_len, ttl, seq); print_packet("TX: Friend ACK", pkt + 1, pkt_len); } void mesh_net_transport_send(struct mesh_net *net, uint32_t net_key_id, uint16_t net_idx, uint32_t iv_index, uint8_t ttl, uint32_t seq, uint16_t src, uint16_t dst, const uint8_t *msg, uint16_t msg_len) { uint32_t use_seq = seq; uint8_t pkt_len; uint8_t pkt[30]; bool result = false; if (!net->src_addr) return; if (!src) src = net->src_addr; if (src == dst) return; if (ttl == DEFAULT_TTL) ttl = net->default_ttl; /* Range check the Opcode and msg length*/ if (*msg & 0xc0 || (9 + msg_len + 8 > 29)) return; /* * MshPRFv1.0.1 section 3.4.5.2, Interface output filter: * If TTL is set to 1, message shall be dropped. */ if (ttl == 1) return; /* Enqueue for Friend if forwardable and from us */ if (!net_key_id && src >= net->src_addr && src <= net->last_addr) { uint32_t hdr = msg[0] << OPCODE_HDR_SHIFT; uint8_t frnd_ttl = ttl; if (friend_packet_queue(net, iv_index, true, frnd_ttl, mesh_net_next_seq_num(net), src, dst, hdr, msg + 1, msg_len - 1)) return; } /* Deliver to Local entities if applicable */ if (!(dst & 0x8000) && src >= net->src_addr && src <= net->last_addr) result = ctl_received(net, net_key_id, iv_index, ttl, mesh_net_next_seq_num(net), src, dst, msg[0], 0, msg + 1, msg_len - 1); if (!net_key_id) { struct mesh_subnet *subnet = l_queue_find(net->subnets, match_key_index, L_UINT_TO_PTR(net_idx)); if (!subnet) return; net_key_id = subnet->net_key_tx; use_seq = mesh_net_next_seq_num(net); if (result || (dst >= net->src_addr && dst <= net->last_addr)) return; } if (!mesh_crypto_packet_build(true, ttl, use_seq, src, dst, msg[0], false, 0, false, false, 0, 0, 0, msg + 1, msg_len - 1, pkt + 1, &pkt_len)) return; if (!net_key_encrypt(net_key_id, iv_index, pkt + 1, pkt_len)) { l_error("Failed to encode packet"); return; } if (!(IS_UNASSIGNED(dst))) send_msg_pkt(net, net->tx_cnt, net->tx_interval, pkt, pkt_len + 1); } int mesh_net_key_refresh_phase_set(struct mesh_net *net, uint16_t idx, uint8_t transition) { switch (transition) { case KEY_REFRESH_TRANS_TWO: return key_refresh_phase_two(net, idx); case KEY_REFRESH_TRANS_THREE: return key_refresh_finish(net, idx); default: return MESH_STATUS_UNSPECIFIED_ERROR; } } int mesh_net_key_refresh_phase_get(struct mesh_net *net, uint16_t idx, uint8_t *phase) { struct mesh_subnet *subnet; if (!net) return MESH_STATUS_UNSPECIFIED_ERROR; subnet = l_queue_find(net->subnets, match_key_index, L_UINT_TO_PTR(idx)); if (!subnet) return MESH_STATUS_INVALID_NETKEY; *phase = subnet->kr_phase; return MESH_STATUS_SUCCESS; } /* * This function is called when Configuration Server Model receives * a NETKEY_UPDATE command */ int mesh_net_update_key(struct mesh_net *net, uint16_t idx, const uint8_t *value) { struct mesh_subnet *subnet; if (!net) return MESH_STATUS_UNSPECIFIED_ERROR; subnet = l_queue_find(net->subnets, match_key_index, L_UINT_TO_PTR(idx)); if (!subnet) return MESH_STATUS_INVALID_NETKEY; /* Check if the key has been already successfully updated */ if (subnet->kr_phase == KEY_REFRESH_PHASE_ONE && net_key_confirm(subnet->net_key_upd, value)) return MESH_STATUS_SUCCESS; if (subnet->kr_phase != KEY_REFRESH_PHASE_NONE) return MESH_STATUS_CANNOT_UPDATE; if (subnet->net_key_upd) { net_key_unref(subnet->net_key_upd); l_debug("Warning: overwriting new keys"); } /* Preserve starting data */ subnet->net_key_upd = net_key_add(value); if (!subnet->net_key_upd) { l_error("Failed to start key refresh phase one"); return MESH_STATUS_CANNOT_UPDATE; } /* If we are a Friend-Node, generate all our new keys */ l_queue_foreach(net->friends, frnd_kr_phase1, L_UINT_TO_PTR(subnet->net_key_upd)); l_debug("key refresh phase 1: Key ID %d", subnet->net_key_upd); if (!mesh_config_net_key_update(node_config_get(net->node), idx, value)) return MESH_STATUS_STORAGE_FAIL; subnet->kr_phase = KEY_REFRESH_PHASE_ONE; return MESH_STATUS_SUCCESS; } struct mesh_net_heartbeat_sub *mesh_net_get_heartbeat_sub(struct mesh_net *net) { return &net->hb_sub; } struct mesh_net_heartbeat_pub *mesh_net_get_heartbeat_pub(struct mesh_net *net) { return &net->hb_pub; } void mesh_net_set_iv_index(struct mesh_net *net, uint32_t index, bool update) { net->iv_index = index; net->iv_update = update; } uint16_t mesh_net_get_primary_idx(struct mesh_net *net) { struct mesh_subnet *subnet; if (!net) return NET_IDX_INVALID; subnet = get_primary_subnet(net); if (!subnet) return NET_IDX_INVALID; return subnet->idx; } uint32_t mesh_net_friend_timeout(struct mesh_net *net, uint16_t addr) { struct mesh_friend *frnd = l_queue_find(net->friends, match_by_friend, L_UINT_TO_PTR(addr)); if (!frnd) return 0; else return frnd->poll_timeout; } struct l_queue *mesh_net_get_friends(struct mesh_net *net) { if (net) return net->friends; return NULL; } struct l_queue *mesh_net_get_negotiations(struct mesh_net *net) { if (net) return net->negotiations; return NULL; } struct mesh_node *mesh_net_node_get(struct mesh_net *net) { return net->node; } struct l_queue *mesh_net_get_app_keys(struct mesh_net *net) { if (!net) return NULL; if (!net->app_keys) net->app_keys = l_queue_new(); return net->app_keys; } bool mesh_net_have_key(struct mesh_net *net, uint16_t idx) { if (!net) return false; return (l_queue_find(net->subnets, match_key_index, L_UINT_TO_PTR(idx)) != NULL); } bool mesh_net_is_local_address(struct mesh_net *net, uint16_t src, uint16_t count) { const uint16_t last = src + count - 1; if (!net) return false; return (src >= net->src_addr && src <= net->last_addr) && (last >= net->src_addr && last <= net->last_addr); } void mesh_net_transmit_params_set(struct mesh_net *net, uint8_t count, uint16_t interval) { if (!net) return; net->tx_interval = interval; net->tx_cnt = count; } void mesh_net_transmit_params_get(struct mesh_net *net, uint8_t *count, uint16_t *interval) { if (!net) return; *interval = net->tx_interval; *count = net->tx_cnt; } struct mesh_io *mesh_net_get_io(struct mesh_net *net) { if (!net) return NULL; return net->io; } struct mesh_prov *mesh_net_get_prov(struct mesh_net *net) { if (!net) return NULL; return net->prov; } void mesh_net_set_prov(struct mesh_net *net, struct mesh_prov *prov) { if (!net) return; net->prov = prov; } static void refresh_instant(void *a, void *b) { struct mesh_subnet *subnet = a; struct mesh_net *net = b; uint32_t instant = net_key_beacon_last_seen(subnet->net_key_tx); if (net->instant < instant) net->instant = instant; } uint32_t mesh_net_get_instant(struct mesh_net *net) { if (!net) return 0; l_queue_foreach(net->subnets, refresh_instant, net); return net->instant; } static void hb_sub_timeout_func(struct l_timeout *timeout, void *user_data) { struct mesh_net *net = user_data; struct mesh_net_heartbeat_sub *sub = &net->hb_sub; l_debug("HB Subscription Ended"); l_timeout_remove(sub->timer); sub->timer = NULL; sub->enabled = false; } static uint32_t log_to_uint32(uint8_t log) { if (!log) return 0x0000; return (1 << (log - 1)); } int mesh_net_set_heartbeat_sub(struct mesh_net *net, uint16_t src, uint16_t dst, uint8_t period_log) { struct mesh_net_heartbeat_sub *sub = &net->hb_sub; struct timeval time_now; if (!net) return MESH_STATUS_UNSPECIFIED_ERROR; /* Check if the subscription should be disabled */ if (IS_UNASSIGNED(src) || IS_UNASSIGNED(dst) || !period_log) { if (IS_GROUP(sub->dst)) mesh_net_dst_unreg(net, sub->dst); /* Preserve collected data, but disable */ sub->enabled = false; sub->dst = UNASSIGNED_ADDRESS; sub->src = UNASSIGNED_ADDRESS; sub->period = 0; } else { if (sub->dst != dst) { if (IS_GROUP(sub->dst)) mesh_net_dst_unreg(net, sub->dst); if (IS_GROUP(dst)) mesh_net_dst_reg(net, dst); } sub->enabled = true; sub->src = src; sub->dst = dst; sub->count = 0; sub->period = log_to_uint32(period_log); sub->min_hops = 0x7f; sub->max_hops = 0x00; gettimeofday(&time_now, NULL); sub->start = time_now.tv_sec; } /* TODO: Save to node config */ if (!sub->enabled) { l_timeout_remove(sub->timer); sub->timer = NULL; return MESH_STATUS_SUCCESS; } if (!sub->timer) sub->timer = l_timeout_create(sub->period, hb_sub_timeout_func, net, NULL); else l_timeout_modify(sub->timer, sub->period); return MESH_STATUS_SUCCESS; } static void hb_pub_timeout_func(struct l_timeout *timeout, void *user_data) { struct mesh_net *net = user_data; struct mesh_net_heartbeat_pub *pub = &net->hb_pub; send_hb_publication(net); if (pub->count != 0xffff) pub->count--; if (pub->count > 0) l_timeout_modify(pub->timer, pub->period); else { l_timeout_remove(pub->timer); pub->timer = NULL; } } static void update_hb_pub_timer(struct mesh_net *net, struct mesh_net_heartbeat_pub *pub) { if (IS_UNASSIGNED(pub->dst) || pub->count == 0) { l_timeout_remove(pub->timer); pub->timer = NULL; return; } if (!pub->timer) pub->timer = l_timeout_create(pub->period, hb_pub_timeout_func, net, NULL); else l_timeout_modify(pub->timer, pub->period); } int mesh_net_set_heartbeat_pub(struct mesh_net *net, uint16_t dst, uint16_t features, uint16_t idx, uint8_t ttl, uint8_t count_log, uint8_t period_log) { struct mesh_subnet *subnet; struct mesh_net_heartbeat_pub *pub = &net->hb_pub; if (!net) return MESH_STATUS_UNSPECIFIED_ERROR; subnet = l_queue_find(net->subnets, match_key_index, L_UINT_TO_PTR(idx)); if (!subnet) return MESH_STATUS_INVALID_NETKEY; pub->dst = dst; if (pub->dst == UNASSIGNED_ADDRESS) { pub->count = 0; pub->period = 0; pub->ttl = 0; } else { pub->count = (count_log != 0xff) ? log_to_uint32(count_log) : 0xffff; pub->period = log_to_uint32(period_log); } pub->ttl = ttl; pub->features = features; pub->net_idx = idx; update_hb_pub_timer(net, pub); /* TODO: Save to node config */ return MESH_STATUS_SUCCESS; } bool mesh_net_load_rpl(struct mesh_net *net) { return rpl_get_list(net->node, net->replay_cache); } bluez-5.82/mesh/PaxHeaders/remprv.h0000644000000000000000000000005014447506754014306 xustar0020 atime=1743516868 20 ctime=1743591282 bluez-5.82/mesh/remprv.h0000644000000000000000000000537114447506754013775 0ustar00rootroot/* * * BlueZ - Bluetooth protocol stack for Linux * * Copyright (C) 2023 Intel Corporation. All rights reserved. * * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * */ #define REM_PROV_SRV_MODEL SET_ID(SIG_VENDOR, 0x0004) #define REM_PROV_CLI_MODEL SET_ID(SIG_VENDOR, 0x0005) #define PB_REMOTE_MAX_SCAN_QUEUE_SIZE 5 #define PB_REMOTE_STATE_IDLE 0x00 #define PB_REMOTE_STATE_LINK_OPENING 0x01 #define PB_REMOTE_STATE_LINK_ACTIVE 0x02 #define PB_REMOTE_STATE_OB_PKT_TX 0x03 #define PB_REMOTE_STATE_LINK_CLOSING 0x04 #define PB_REMOTE_TYPE_LOCAL 0x01 #define PB_REMOTE_TYPE_ADV 0x02 #define PB_REMOTE_TYPE_GATT 0x04 #define PB_REMOTE_SCAN_TYPE_NONE 0x00 #define PB_REMOTE_SCAN_TYPE_UNLIMITED 0x01 #define PB_REMOTE_SCAN_TYPE_LIMITED 0x02 #define PB_REMOTE_SCAN_TYPE_DETAILED 0x03 /* Remote Provisioning Opcode List */ #define OP_REM_PROV_SCAN_CAP_GET 0x804F #define OP_REM_PROV_SCAN_CAP_STATUS 0x8050 #define OP_REM_PROV_SCAN_GET 0x8051 #define OP_REM_PROV_SCAN_START 0x8052 #define OP_REM_PROV_SCAN_STOP 0x8053 #define OP_REM_PROV_SCAN_STATUS 0x8054 #define OP_REM_PROV_SCAN_REPORT 0x8055 #define OP_REM_PROV_EXT_SCAN_START 0x8056 #define OP_REM_PROV_EXT_SCAN_REPORT 0x8057 #define OP_REM_PROV_LINK_GET 0x8058 #define OP_REM_PROV_LINK_OPEN 0x8059 #define OP_REM_PROV_LINK_CLOSE 0x805A #define OP_REM_PROV_LINK_STATUS 0x805B #define OP_REM_PROV_LINK_REPORT 0x805C #define OP_REM_PROV_PDU_SEND 0x805D #define OP_REM_PROV_PDU_OB_REPORT 0x805E #define OP_REM_PROV_PDU_REPORT 0x805F /* Remote Provisioning Errors */ #define PB_REM_ERR_SUCCESS 0x00 #define PB_REM_ERR_SCANNING_CANNOT_START 0x01 #define PB_REM_ERR_INVALID_STATE 0x02 #define PB_REM_ERR_LIMITED_RESOURCES 0x03 #define PB_REM_ERR_CANNOT_OPEN 0x04 #define PB_REM_ERR_OPEN_FAILED 0x05 #define PB_REM_ERR_CLOSED_BY_DEVICE 0x06 #define PB_REM_ERR_CLOSED_BY_SERVER 0x07 #define PB_REM_ERR_CLOSED_BY_CLIENT 0x08 #define PB_REM_ERR_CLOSED_CANNOT_RX_PDU 0x09 #define PB_REM_ERR_CLOSED_CANNOT_TX_PDU 0x0A void remote_prov_server_init(struct mesh_node *node, uint8_t ele_idx); void remote_prov_client_init(struct mesh_node *node, uint8_t ele_idx); bool register_nppi_acceptor(mesh_prov_open_func_t open_cb, mesh_prov_close_func_t close_cb, mesh_prov_receive_func_t rx_cb, mesh_prov_ack_func_t ack_cb, void *user_data); bluez-5.82/mesh/PaxHeaders/manager.c0000644000000000000000000000005014772767672014411 xustar0020 atime=1743515579 20 ctime=1743591282 bluez-5.82/mesh/manager.c0000644000000000000000000007674714772767672014117 0ustar00rootroot// SPDX-License-Identifier: LGPL-2.1-or-later /* * * BlueZ - Bluetooth protocol stack for Linux * * Copyright (C) 2019 Intel Corporation. All rights reserved. * * */ #ifdef HAVE_CONFIG_H #include #endif #include #define _GNU_SOURCE #include #include "mesh/mesh-defs.h" #include "mesh/dbus.h" #include "mesh/error.h" #include "mesh/mesh.h" #include "mesh/mesh-io.h" #include "mesh/node.h" #include "mesh/model.h" #include "mesh/net.h" #include "mesh/keyring.h" #include "mesh/agent.h" #include "mesh/provision.h" #include "mesh/prov.h" #include "mesh/remprv.h" #include "mesh/manager.h" struct prov_remote_data { struct l_dbus_message *msg; struct mesh_agent *agent; struct mesh_node *node; uint32_t disc_watch; uint16_t original; uint16_t primary; uint16_t net_idx; uint8_t transport; uint8_t num_ele; uint8_t uuid[16]; }; struct scan_req { struct mesh_node *node; struct l_timeout *timeout; uint16_t server; uint16_t net_idx; uint8_t uuid[16]; int8_t rssi; bool ext; }; static struct l_queue *scans; static struct prov_remote_data *prov_pending; static const uint8_t prvb[2] = {MESH_AD_TYPE_BEACON, 0x00}; static bool by_scan(const void *a, const void *b) { return a == b; } static bool by_node(const void *a, const void *b) { const struct scan_req *req = a; const struct mesh_node *node = b; return req->node == node; } static bool by_node_svr(const void *a, const void *b) { const struct scan_req *req = a; const struct scan_req *test = b; return req->node == test->node && req->server == test->server; } static void scan_cancel(struct l_timeout *timeout, void *user_data) { struct scan_req *req = user_data; struct mesh_io *io; struct mesh_net *net; uint8_t msg[4]; int n; l_debug(""); req = l_queue_remove_if(scans, by_scan, req); if (!req) return; l_timeout_remove(req->timeout); if (req->server) { n = mesh_model_opcode_set(OP_REM_PROV_SCAN_STOP, msg); mesh_model_send(req->node, 0, req->server, APP_IDX_DEV_REMOTE, req->net_idx, DEFAULT_TTL, true, n, msg); } else { net = node_get_net(req->node); io = mesh_net_get_io(net); mesh_io_deregister_recv_cb(io, prvb, sizeof(prvb)); } initiator_scan_unreg(req->node); l_free(req); } static void free_pending_add_call(void) { if (!prov_pending) return; if (prov_pending->disc_watch) l_dbus_remove_watch(dbus_get_bus(), prov_pending->disc_watch); if (prov_pending->msg) l_dbus_message_unref(prov_pending->msg); l_free(prov_pending); prov_pending = NULL; } static void prov_disc_cb(struct l_dbus *bus, void *user_data) { if (!prov_pending) return; initiator_cancel(prov_pending); prov_pending->disc_watch = 0; free_pending_add_call(); } static void append_dict_entry_basic(struct l_dbus_message_builder *builder, const char *key, const char *signature, const void *data) { if (!builder) return; l_dbus_message_builder_enter_dict(builder, "sv"); l_dbus_message_builder_append_basic(builder, 's', key); l_dbus_message_builder_enter_variant(builder, signature); l_dbus_message_builder_append_basic(builder, signature[0], data); l_dbus_message_builder_leave_variant(builder); l_dbus_message_builder_leave_dict(builder); } static void send_add_failed(const char *owner, const char *path, uint8_t status) { struct l_dbus *dbus = dbus_get_bus(); struct l_dbus_message_builder *builder; struct l_dbus_message *msg; msg = l_dbus_message_new_method_call(dbus, owner, path, MESH_PROVISIONER_INTERFACE, "AddNodeFailed"); builder = l_dbus_message_builder_new(msg); dbus_append_byte_array(builder, prov_pending->uuid, 16); l_dbus_message_builder_append_basic(builder, 's', mesh_prov_status_str(status)); l_dbus_message_builder_finalize(builder); l_dbus_message_builder_destroy(builder); l_dbus_send(dbus, msg); free_pending_add_call(); } static bool add_cmplt(void *user_data, uint8_t status, struct mesh_prov_node_info *info) { struct prov_remote_data *pending = user_data; struct mesh_node *node = pending->node; struct l_dbus *dbus = dbus_get_bus(); struct l_dbus_message_builder *builder; struct l_dbus_message *msg; bool result; if (pending != prov_pending) return false; if (status != PROV_ERR_SUCCESS) { send_add_failed(node_get_owner(node), node_get_app_path(node), status); return false; } /* If Unicast address changing, delete old dev key */ if (pending->transport == PB_NPPI_01) keyring_del_remote_dev_key_all(pending->node, pending->original); result = keyring_put_remote_dev_key(pending->node, info->unicast, info->num_ele, info->device_key); if (!result) { send_add_failed(node_get_owner(node), node_get_app_path(node), PROV_ERR_CANT_ASSIGN_ADDR); return false; } if (pending->transport > PB_NPPI_02) msg = l_dbus_message_new_method_call(dbus, node_get_owner(node), node_get_app_path(node), MESH_PROVISIONER_INTERFACE, "AddNodeComplete"); else msg = l_dbus_message_new_method_call(dbus, node_get_owner(node), node_get_app_path(node), MESH_PROVISIONER_INTERFACE, "ReprovComplete"); builder = l_dbus_message_builder_new(msg); if (pending->transport > PB_NPPI_02) dbus_append_byte_array(builder, pending->uuid, 16); else { uint8_t nppi = (uint8_t) pending->transport; l_dbus_message_builder_append_basic(builder, 'q', &pending->original); l_dbus_message_builder_append_basic(builder, 'y', &nppi); } l_dbus_message_builder_append_basic(builder, 'q', &info->unicast); l_dbus_message_builder_append_basic(builder, 'y', &info->num_ele); l_dbus_message_builder_finalize(builder); l_dbus_message_builder_destroy(builder); l_dbus_send(dbus, msg); free_pending_add_call(); return true; } static void mgr_prov_data (struct l_dbus_message *reply, void *user_data) { struct prov_remote_data *pending = user_data; uint16_t net_idx; uint16_t primary; if (pending != prov_pending) return; if (l_dbus_message_is_error(reply)) return; if (pending->transport == PB_NPPI_01) { /* If performing NPPI, we only get new primary unicast here */ if (!l_dbus_message_get_arguments(reply, "q", &primary)) return; net_idx = pending->net_idx; } else if (!l_dbus_message_get_arguments(reply, "qq", &net_idx, &primary)) return; pending->primary = primary; pending->net_idx = net_idx; initiator_prov_data(net_idx, primary, pending); } static bool add_data_get(void *user_data, uint8_t num_ele) { struct prov_remote_data *pending = user_data; struct l_dbus_message *msg; struct l_dbus *dbus; const char *app_path; const char *sender; if (pending != prov_pending) return false; dbus = dbus_get_bus(); app_path = node_get_app_path(pending->node); sender = node_get_owner(pending->node); if (pending->transport > PB_NPPI_02) { msg = l_dbus_message_new_method_call(dbus, sender, app_path, MESH_PROVISIONER_INTERFACE, "RequestProvData"); l_dbus_message_set_arguments(msg, "y", num_ele); } else if (pending->transport == PB_NPPI_01) { msg = l_dbus_message_new_method_call(dbus, sender, app_path, MESH_PROVISIONER_INTERFACE, "RequestReprovData"); l_dbus_message_set_arguments(msg, "qy", pending->original, num_ele); } else return false; l_dbus_send_with_reply(dbus, msg, mgr_prov_data, pending, NULL); pending->num_ele = num_ele; return true; } static void add_start(void *user_data, int err) { struct l_dbus_message *reply; l_debug("Start callback"); if (err == MESH_ERROR_NONE) reply = l_dbus_message_new_method_return(prov_pending->msg); else reply = dbus_error(prov_pending->msg, MESH_ERROR_FAILED, "Failed to start provisioning initiator"); l_dbus_send(dbus_get_bus(), reply); l_dbus_message_unref(prov_pending->msg); prov_pending->msg = NULL; } static struct l_dbus_message *reprovision_call(struct l_dbus *dbus, struct l_dbus_message *msg, void *user_data) { struct mesh_node *node = user_data; struct l_dbus_message_iter options, var; struct l_dbus_message *reply; struct mesh_net *net = node_get_net(node); const char *key; uint16_t subidx; uint16_t server = 0; uint8_t nppi = 0; l_debug("Reprovision request"); if (!l_dbus_message_get_arguments(msg, "qa{sv}", &server, &options)) return dbus_error(msg, MESH_ERROR_INVALID_ARGS, NULL); if (!IS_UNICAST(server)) return dbus_error(msg, MESH_ERROR_INVALID_ARGS, "Bad Unicast"); /* Default to nodes primary subnet index */ subidx = mesh_net_get_primary_idx(net); /* Get Provisioning Options */ while (l_dbus_message_iter_next_entry(&options, &key, &var)) { bool failed = true; if (!strcmp(key, "NPPI")) { if (l_dbus_message_iter_get_variant(&var, "y", &nppi)) { if (nppi <= 2) failed = false; } } else if (!strcmp(key, "Subnet")) { if (l_dbus_message_iter_get_variant(&var, "q", &subidx)) { if (subidx <= MAX_KEY_IDX) failed = false; } } if (failed) return dbus_error(msg, MESH_ERROR_INVALID_ARGS, "Invalid options"); } /* AddNode cancels all outstanding Scanning from node */ manager_scan_cancel(node); /* Invoke Prov Initiator */ prov_pending = l_new(struct prov_remote_data, 1); prov_pending->transport = nppi; prov_pending->node = node; prov_pending->original = server; prov_pending->agent = node_get_agent(node); if (!node_is_provisioner(node) || (prov_pending->agent == NULL)) { reply = dbus_error(msg, MESH_ERROR_NOT_AUTHORIZED, "Missing Interfaces"); goto fail; } prov_pending->msg = l_dbus_message_ref(msg); initiator_start(prov_pending->transport, server, subidx, NULL, 99, 60, prov_pending->agent, add_start, add_data_get, add_cmplt, node, prov_pending); prov_pending->disc_watch = l_dbus_add_disconnect_watch(dbus, node_get_owner(node), prov_disc_cb, NULL, NULL); return NULL; fail: l_free(prov_pending); prov_pending = NULL; return reply; } static struct l_dbus_message *add_node_call(struct l_dbus *dbus, struct l_dbus_message *msg, void *user_data) { struct mesh_node *node = user_data; struct l_dbus_message_iter iter_uuid, options, var; struct l_dbus_message *reply; struct mesh_net *net = node_get_net(node); const char *key; uint8_t *uuid; uint32_t n = 0; uint16_t subidx; uint16_t sec = 60; uint16_t server = 0; l_debug("AddNode request"); if (!l_dbus_message_get_arguments(msg, "aya{sv}", &iter_uuid, &options)) return dbus_error(msg, MESH_ERROR_INVALID_ARGS, NULL); if (!l_dbus_message_iter_get_fixed_array(&iter_uuid, &uuid, &n) || n != 16) return dbus_error(msg, MESH_ERROR_INVALID_ARGS, "Bad device UUID"); /* Default to nodes primary subnet index */ subidx = mesh_net_get_primary_idx(net); /* Get Provisioning Options */ while (l_dbus_message_iter_next_entry(&options, &key, &var)) { bool failed = true; if (!strcmp(key, "Seconds")) { if (l_dbus_message_iter_get_variant(&var, "q", &sec)) failed = false; } else if (!strcmp(key, "Server")) { if (l_dbus_message_iter_get_variant(&var, "q", &server)) { if (server < 0x8000) failed = false; } } else if (!strcmp(key, "Subnet")) { if (l_dbus_message_iter_get_variant(&var, "q", &subidx)) { if (subidx <= MAX_KEY_IDX) failed = false; } } if (failed) return dbus_error(msg, MESH_ERROR_INVALID_ARGS, "Invalid options"); } /* Device Key update/Composition update requires remote server */ if (!n && !server) return dbus_error(msg, MESH_ERROR_INVALID_ARGS, "Invalid options"); /* If no server specified, use local */ if (!server) server = node_get_primary(node); /* AddNode cancels all outstanding Scanning from node */ manager_scan_cancel(node); /* Invoke Prov Initiator */ prov_pending = l_new(struct prov_remote_data, 1); if (n) memcpy(prov_pending->uuid, uuid, 16); else uuid = NULL; prov_pending->transport = PB_ADV; prov_pending->node = node; prov_pending->agent = node_get_agent(node); if (!node_is_provisioner(node) || (prov_pending->agent == NULL)) { l_debug("Provisioner: %d", node_is_provisioner(node)); l_debug("Agent: %p", prov_pending->agent); reply = dbus_error(msg, MESH_ERROR_NOT_AUTHORIZED, "Missing Interfaces"); goto fail; } prov_pending->msg = l_dbus_message_ref(msg); initiator_start(PB_ADV, server, subidx, uuid, 99, sec, prov_pending->agent, add_start, add_data_get, add_cmplt, node, prov_pending); prov_pending->disc_watch = l_dbus_add_disconnect_watch(dbus, node_get_owner(node), prov_disc_cb, NULL, NULL); return NULL; fail: l_free(prov_pending); prov_pending = NULL; return reply; } static struct l_dbus_message *import_node_call(struct l_dbus *dbus, struct l_dbus_message *msg, void *user_data) { struct mesh_node *node = user_data; struct l_dbus_message_iter iter_key; uint16_t primary; uint8_t num_ele; uint8_t *key; uint32_t n; const char *sender = l_dbus_message_get_sender(msg); if (strcmp(sender, node_get_owner(node))) return dbus_error(msg, MESH_ERROR_NOT_AUTHORIZED, NULL); if (!l_dbus_message_get_arguments(msg, "qyay", &primary, &num_ele, &iter_key)) return dbus_error(msg, MESH_ERROR_INVALID_ARGS, NULL); if (!l_dbus_message_iter_get_fixed_array(&iter_key, &key, &n) || n != 16) return dbus_error(msg, MESH_ERROR_INVALID_ARGS, "Bad device key"); if (!keyring_put_remote_dev_key(node, primary, num_ele, key)) return dbus_error(msg, MESH_ERROR_FAILED, NULL); return l_dbus_message_new_method_return(msg); } static struct l_dbus_message *delete_node_call(struct l_dbus *dbus, struct l_dbus_message *msg, void *user_data) { struct mesh_node *node = user_data; struct mesh_net *net = node_get_net(node); uint16_t primary; uint8_t num_ele; const char *sender = l_dbus_message_get_sender(msg); if (strcmp(sender, node_get_owner(node))) return dbus_error(msg, MESH_ERROR_NOT_AUTHORIZED, NULL); if (!l_dbus_message_get_arguments(msg, "qy", &primary, &num_ele)) return dbus_error(msg, MESH_ERROR_INVALID_ARGS, NULL); if (mesh_net_is_local_address(net, primary, num_ele)) return dbus_error(msg, MESH_ERROR_INVALID_ARGS, "Cannot remove local device key"); keyring_del_remote_dev_key(node, primary, num_ele); return l_dbus_message_new_method_return(msg); } static void manager_scan_result(void *user_data, uint16_t server, bool ext, const uint8_t *data, uint16_t len) { struct scan_req node_svr = { .node = user_data, .server = server, }; struct scan_req *req; struct l_dbus_message_builder *builder; struct l_dbus_message *msg; struct l_dbus *dbus; int16_t rssi; l_debug("scan_result %4.4x %p", server, user_data); req = l_queue_find(scans, by_node_svr, &node_svr); if (!req) { l_debug("No scan_result req"); return; } /* Filter repeats with weaker signal */ if (!memcmp(data + 1, req->uuid, sizeof(req->uuid))) { if (!ext && ((int8_t) data[0] <= req->rssi)) { l_debug("Already Seen"); return; } } if (!ext && ((int8_t) data[0] > req->rssi)) req->rssi = (int8_t) data[0]; rssi = req->rssi; memcpy(req->uuid, data + 1, sizeof(req->uuid)); dbus = dbus_get_bus(); msg = l_dbus_message_new_method_call(dbus, node_get_owner(req->node), node_get_app_path(req->node), MESH_PROVISIONER_INTERFACE, "ScanResult"); builder = l_dbus_message_builder_new(msg); l_dbus_message_builder_append_basic(builder, 'n', &rssi); dbus_append_byte_array(builder, data + 1, len - 1); l_dbus_message_builder_enter_array(builder, "{sv}"); append_dict_entry_basic(builder, "Server", "q", &server); l_dbus_message_builder_leave_array(builder); l_dbus_message_builder_finalize(builder); l_dbus_message_builder_destroy(builder); l_dbus_send(dbus, msg); } static struct l_dbus_message *start_scan_call(struct l_dbus *dbus, struct l_dbus_message *msg, void *user_data) { struct scan_req new_req = { .node = user_data, .server = 0, .timeout = NULL, .ext = false, }; struct scan_req *req; struct mesh_net *net; uint8_t *uuid, *ext = NULL; uint8_t scan_req[21]; int n; uint32_t ext_len; uint32_t flen = 0; uint16_t sec = 60; const char *key; struct l_dbus_message_iter options, var; const char *sender = l_dbus_message_get_sender(msg); if (strcmp(sender, node_get_owner(new_req.node))) return dbus_error(msg, MESH_ERROR_NOT_AUTHORIZED, NULL); if (!l_dbus_message_get_arguments(msg, "a{sv}", &options)) return dbus_error(msg, MESH_ERROR_INVALID_ARGS, NULL); if (!node_is_provisioner(new_req.node)) return dbus_error(msg, MESH_ERROR_NOT_AUTHORIZED, NULL); net = node_get_net(new_req.node); new_req.net_idx = mesh_net_get_primary_idx(net); memset(new_req.uuid, 0, sizeof(new_req.uuid)); while (l_dbus_message_iter_next_entry(&options, &key, &var)) { bool failed = true; if (!strcmp(key, "Seconds")) { if (l_dbus_message_iter_get_variant(&var, "q", &sec)) failed = false; } else if (!strcmp(key, "Subnet")) { if (l_dbus_message_iter_get_variant(&var, "q", &new_req.net_idx)) { if (new_req.net_idx <= MAX_KEY_IDX) failed = false; } } else if (!strcmp(key, "Server")) { if (l_dbus_message_iter_get_variant(&var, "q", &new_req.server)) { if (new_req.server < 0x8000) failed = false; } } else if (!strcmp(key, "Filter")) { if (l_dbus_message_iter_get_variant(&var, "ay", &var)) { if (l_dbus_message_iter_get_fixed_array(&var, &uuid, &flen)) { if (flen == 16) { memcpy(new_req.uuid, uuid, flen); failed = false; } } } } else if (!strcmp(key, "Extended")) { if (l_dbus_message_iter_get_variant(&var, "ay", &var)) { if (l_dbus_message_iter_get_fixed_array(&var, &ext, &ext_len)) failed = false; } } if (failed) return dbus_error(msg, MESH_ERROR_INVALID_ARGS, "Invalid options"); } if (!scans) scans = l_queue_new(); if (new_req.server) { if (!sec || sec > 60) return dbus_error(msg, MESH_ERROR_INVALID_ARGS, "Invalid options"); } else { new_req.server = node_get_primary(new_req.node); if (!sec || sec > 60) sec = 60; } req = l_queue_remove_if(scans, by_node_svr, &new_req); if (!req) req = l_new(struct scan_req, 1); if (req->timeout) { l_timeout_remove(req->timeout); req->timeout = NULL; } *req = new_req; req->rssi = -128; if (sec) req->timeout = l_timeout_create(sec, scan_cancel, req, NULL); n = mesh_model_opcode_set(OP_REM_PROV_SCAN_START, scan_req); scan_req[n++] = 5; scan_req[n++] = sec; if (flen) { memcpy(scan_req + n, req->uuid, flen); n += flen; } mesh_model_send(req->node, 0, req->server, APP_IDX_DEV_REMOTE, req->net_idx, DEFAULT_TTL, true, n, scan_req); initiator_scan_reg(manager_scan_result, req->node); l_queue_push_tail(scans, req); return l_dbus_message_new_method_return(msg); } static struct l_dbus_message *cancel_scan_call(struct l_dbus *dbus, struct l_dbus_message *msg, void *user_data) { struct mesh_node *node = user_data; const char *sender = l_dbus_message_get_sender(msg); if (strcmp(sender, node_get_owner(node)) || !node_is_provisioner(node)) return dbus_error(msg, MESH_ERROR_NOT_AUTHORIZED, NULL); manager_scan_cancel(node); return l_dbus_message_new_method_return(msg); } static struct l_dbus_message *store_new_subnet(struct mesh_node *node, struct l_dbus_message *msg, uint16_t net_idx, uint8_t *new_key) { struct keyring_net_key key; if (net_idx > MAX_KEY_IDX) return dbus_error(msg, MESH_ERROR_INVALID_ARGS, NULL); if (keyring_get_net_key(node, net_idx, &key)) { /* Allow redundant calls only if key values match */ if (!memcmp(key.old_key, new_key, 16)) return l_dbus_message_new_method_return(msg); return dbus_error(msg, MESH_ERROR_ALREADY_EXISTS, NULL); } memcpy(key.old_key, new_key, 16); memcpy(key.new_key, new_key, 16); key.net_idx = net_idx; key.phase = KEY_REFRESH_PHASE_NONE; if (!keyring_put_net_key(node, net_idx, &key)) return dbus_error(msg, MESH_ERROR_FAILED, NULL); return l_dbus_message_new_method_return(msg); } static struct l_dbus_message *create_subnet_call(struct l_dbus *dbus, struct l_dbus_message *msg, void *user_data) { struct mesh_node *node = user_data; uint8_t key[16]; uint16_t net_idx; const char *sender = l_dbus_message_get_sender(msg); if (strcmp(sender, node_get_owner(node))) return dbus_error(msg, MESH_ERROR_NOT_AUTHORIZED, NULL); if (!l_dbus_message_get_arguments(msg, "q", &net_idx) || net_idx == PRIMARY_NET_IDX) return dbus_error(msg, MESH_ERROR_INVALID_ARGS, NULL); /* Generate key and store */ l_getrandom(key, sizeof(key)); return store_new_subnet(node, msg, net_idx, key); } static struct l_dbus_message *update_subnet_call(struct l_dbus *dbus, struct l_dbus_message *msg, void *user_data) { struct mesh_node *node = user_data; struct keyring_net_key key; uint16_t net_idx; const char *sender = l_dbus_message_get_sender(msg); if (strcmp(sender, node_get_owner(node))) return dbus_error(msg, MESH_ERROR_NOT_AUTHORIZED, NULL); if (!l_dbus_message_get_arguments(msg, "q", &net_idx) || net_idx > MAX_KEY_IDX) return dbus_error(msg, MESH_ERROR_INVALID_ARGS, NULL); if (!keyring_get_net_key(node, net_idx, &key)) return dbus_error(msg, MESH_ERROR_DOES_NOT_EXIST, NULL); switch (key.phase) { case KEY_REFRESH_PHASE_NONE: /* Generate Key and update phase */ l_getrandom(key.new_key, sizeof(key.new_key)); key.phase = KEY_REFRESH_PHASE_ONE; if (!keyring_put_net_key(node, net_idx, &key)) return dbus_error(msg, MESH_ERROR_FAILED, NULL); /* Fall Through */ case KEY_REFRESH_PHASE_ONE: /* Allow redundant calls to start Key Refresh */ return l_dbus_message_new_method_return(msg); default: break; } /* All other phases mean KR already in progress over-the-air */ return dbus_error(msg, MESH_ERROR_IN_PROGRESS, "Key Refresh in progress"); } static struct l_dbus_message *delete_subnet_call(struct l_dbus *dbus, struct l_dbus_message *msg, void *user_data) { struct mesh_node *node = user_data; uint16_t net_idx; const char *sender = l_dbus_message_get_sender(msg); if (strcmp(sender, node_get_owner(node))) return dbus_error(msg, MESH_ERROR_NOT_AUTHORIZED, NULL); if (!l_dbus_message_get_arguments(msg, "q", &net_idx) || net_idx > MAX_KEY_IDX) return dbus_error(msg, MESH_ERROR_INVALID_ARGS, NULL); keyring_del_net_key(node, net_idx); return l_dbus_message_new_method_return(msg); } static struct l_dbus_message *import_subnet_call(struct l_dbus *dbus, struct l_dbus_message *msg, void *user_data) { struct mesh_node *node = user_data; struct l_dbus_message_iter iter_key; uint16_t net_idx; uint8_t *key; uint32_t n; const char *sender = l_dbus_message_get_sender(msg); if (strcmp(sender, node_get_owner(node))) return dbus_error(msg, MESH_ERROR_NOT_AUTHORIZED, NULL); if (!l_dbus_message_get_arguments(msg, "qay", &net_idx, &iter_key)) return dbus_error(msg, MESH_ERROR_INVALID_ARGS, NULL); if (!l_dbus_message_iter_get_fixed_array(&iter_key, &key, &n) || n != 16) return dbus_error(msg, MESH_ERROR_INVALID_ARGS, "Bad network key"); return store_new_subnet(node, msg, net_idx, key); } static struct l_dbus_message *store_new_appkey(struct mesh_node *node, struct l_dbus_message *msg, uint16_t net_idx, uint16_t app_idx, uint8_t *new_key) { struct keyring_net_key net_key; struct keyring_app_key app_key; if (net_idx > MAX_KEY_IDX || app_idx > MAX_KEY_IDX) return dbus_error(msg, MESH_ERROR_INVALID_ARGS, NULL); if (!keyring_get_net_key(node, net_idx, &net_key)) return dbus_error(msg, MESH_ERROR_DOES_NOT_EXIST, "Bound net key not found"); if (keyring_get_app_key(node, app_idx, &app_key)) { /* Allow redundant calls with identical values */ if (!memcmp(app_key.old_key, new_key, 16) && app_key.net_idx == net_idx) return l_dbus_message_new_method_return(msg); return dbus_error(msg, MESH_ERROR_ALREADY_EXISTS, NULL); } memcpy(app_key.old_key, new_key, 16); memcpy(app_key.new_key, new_key, 16); app_key.net_idx = net_idx; app_key.app_idx = app_idx; if (!keyring_put_app_key(node, app_idx, net_idx, &app_key)) return dbus_error(msg, MESH_ERROR_FAILED, NULL); return l_dbus_message_new_method_return(msg); } static struct l_dbus_message *create_appkey_call(struct l_dbus *dbus, struct l_dbus_message *msg, void *user_data) { struct mesh_node *node = user_data; uint16_t net_idx, app_idx; uint8_t key[16]; const char *sender = l_dbus_message_get_sender(msg); if (strcmp(sender, node_get_owner(node))) return dbus_error(msg, MESH_ERROR_NOT_AUTHORIZED, NULL); if (!l_dbus_message_get_arguments(msg, "qq", &net_idx, &app_idx)) return dbus_error(msg, MESH_ERROR_INVALID_ARGS, NULL); l_getrandom(key, sizeof(key)); return store_new_appkey(node, msg, net_idx, app_idx, key); } static struct l_dbus_message *update_appkey_call(struct l_dbus *dbus, struct l_dbus_message *msg, void *user_data) { struct mesh_node *node = user_data; struct keyring_net_key net_key; struct keyring_app_key app_key; uint16_t app_idx; const char *sender = l_dbus_message_get_sender(msg); if (strcmp(sender, node_get_owner(node))) return dbus_error(msg, MESH_ERROR_NOT_AUTHORIZED, NULL); if (!l_dbus_message_get_arguments(msg, "q", &app_idx) || app_idx > MAX_KEY_IDX) return dbus_error(msg, MESH_ERROR_INVALID_ARGS, NULL); if (!keyring_get_app_key(node, app_idx, &app_key) || !keyring_get_net_key(node, app_key.net_idx, &net_key)) return dbus_error(msg, MESH_ERROR_DOES_NOT_EXIST, NULL); if (net_key.phase != KEY_REFRESH_PHASE_ONE) return dbus_error(msg, MESH_ERROR_FAILED, "Invalid Phase"); /* Generate Key if in acceptable phase */ l_getrandom(app_key.new_key, sizeof(app_key.new_key)); if (!keyring_put_app_key(node, app_idx, app_key.net_idx, &app_key)) return dbus_error(msg, MESH_ERROR_FAILED, NULL); return l_dbus_message_new_method_return(msg); } static struct l_dbus_message *delete_appkey_call(struct l_dbus *dbus, struct l_dbus_message *msg, void *user_data) { struct mesh_node *node = user_data; uint16_t app_idx; const char *sender = l_dbus_message_get_sender(msg); if (strcmp(sender, node_get_owner(node))) return dbus_error(msg, MESH_ERROR_NOT_AUTHORIZED, NULL); if (!l_dbus_message_get_arguments(msg, "q", &app_idx)) return dbus_error(msg, MESH_ERROR_INVALID_ARGS, NULL); keyring_del_app_key(node, app_idx); return l_dbus_message_new_method_return(msg); } static struct l_dbus_message *import_appkey_call(struct l_dbus *dbus, struct l_dbus_message *msg, void *user_data) { struct mesh_node *node = user_data; struct l_dbus_message_iter iter_key; uint16_t net_idx, app_idx; uint8_t *key; uint32_t n; const char *sender = l_dbus_message_get_sender(msg); if (strcmp(sender, node_get_owner(node))) return dbus_error(msg, MESH_ERROR_NOT_AUTHORIZED, NULL); if (!l_dbus_message_get_arguments(msg, "qqay", &net_idx, &app_idx, &iter_key)) return dbus_error(msg, MESH_ERROR_INVALID_ARGS, NULL); if (!l_dbus_message_iter_get_fixed_array(&iter_key, &key, &n) || n != 16) return dbus_error(msg, MESH_ERROR_INVALID_ARGS, "Bad application key"); return store_new_appkey(node, msg, net_idx, app_idx, key); } static struct l_dbus_message *set_key_phase_call(struct l_dbus *dbus, struct l_dbus_message *msg, void *user_data) { struct mesh_node *node = user_data; struct keyring_net_key key; uint16_t net_idx; uint8_t phase; const char *sender = l_dbus_message_get_sender(msg); if (strcmp(sender, node_get_owner(node))) return dbus_error(msg, MESH_ERROR_NOT_AUTHORIZED, NULL); if (!l_dbus_message_get_arguments(msg, "qy", &net_idx, &phase) || phase == KEY_REFRESH_PHASE_ONE || phase > KEY_REFRESH_PHASE_THREE) return dbus_error(msg, MESH_ERROR_INVALID_ARGS, NULL); if (!keyring_get_net_key(node, net_idx, &key)) return dbus_error(msg, MESH_ERROR_DOES_NOT_EXIST, NULL); /* Canceling Key Refresh only valid from Phase One */ if (phase == KEY_REFRESH_PHASE_NONE && key.phase >= KEY_REFRESH_PHASE_TWO) return dbus_error(msg, MESH_ERROR_INVALID_ARGS, NULL); if (phase == KEY_REFRESH_PHASE_THREE) { /* If we are already in Phase None, then nothing to do */ if (key.phase == KEY_REFRESH_PHASE_NONE) return l_dbus_message_new_method_return(msg); memcpy(key.old_key, key.new_key, 16); key.phase = KEY_REFRESH_PHASE_THREE; if (!keyring_put_net_key(node, net_idx, &key)) return dbus_error(msg, MESH_ERROR_FAILED, NULL); if (!keyring_finalize_app_keys(node, net_idx)) return dbus_error(msg, MESH_ERROR_FAILED, NULL); key.phase = KEY_REFRESH_PHASE_NONE; } else key.phase = phase; if (!keyring_put_net_key(node, net_idx, &key)) return dbus_error(msg, MESH_ERROR_FAILED, NULL); return l_dbus_message_new_method_return(msg); } static struct l_dbus_message *export_keys_call(struct l_dbus *dbus, struct l_dbus_message *msg, void *user_data) { const char *sender = l_dbus_message_get_sender(msg); struct l_dbus_message_builder *builder; struct l_dbus_message *reply; struct mesh_node *node = user_data; l_debug("Export Keys"); if (strcmp(sender, node_get_owner(node))) return dbus_error(msg, MESH_ERROR_NOT_AUTHORIZED, NULL); reply = l_dbus_message_new_method_return(msg); builder = l_dbus_message_builder_new(reply); l_dbus_message_builder_enter_array(builder, "{sv}"); if (!keyring_build_export_keys_reply(node, builder)) { l_dbus_message_builder_destroy(builder); l_dbus_message_unref(reply); return dbus_error(msg, MESH_ERROR_FAILED, NULL); } l_dbus_message_builder_leave_array(builder); l_dbus_message_builder_finalize(builder); l_dbus_message_builder_destroy(builder); return reply; } static void setup_management_interface(struct l_dbus_interface *iface) { l_dbus_interface_method(iface, "AddNode", 0, add_node_call, "", "aya{sv}", "uuid", "options"); l_dbus_interface_method(iface, "ImportRemoteNode", 0, import_node_call, "", "qyay", "primary", "count", "dev_key"); l_dbus_interface_method(iface, "Reprovision", 0, reprovision_call, "", "qa{sv}", "unicast", "options"); l_dbus_interface_method(iface, "DeleteRemoteNode", 0, delete_node_call, "", "qy", "primary", "count"); l_dbus_interface_method(iface, "UnprovisionedScan", 0, start_scan_call, "", "a{sv}", "options"); l_dbus_interface_method(iface, "UnprovisionedScanCancel", 0, cancel_scan_call, "", ""); l_dbus_interface_method(iface, "CreateSubnet", 0, create_subnet_call, "", "q", "net_index"); l_dbus_interface_method(iface, "UpdateSubnet", 0, update_subnet_call, "", "q", "net_index"); l_dbus_interface_method(iface, "DeleteSubnet", 0, delete_subnet_call, "", "q", "net_index"); l_dbus_interface_method(iface, "ImportSubnet", 0, import_subnet_call, "", "qay", "net_index", "net_key"); l_dbus_interface_method(iface, "CreateAppKey", 0, create_appkey_call, "", "qq", "net_index", "app_index"); l_dbus_interface_method(iface, "UpdateAppKey", 0, update_appkey_call, "", "q", "app_index"); l_dbus_interface_method(iface, "DeleteAppKey", 0, delete_appkey_call, "", "q", "app_index"); l_dbus_interface_method(iface, "ImportAppKey", 0, import_appkey_call, "", "qqay", "net_index", "app_index", "app_key"); l_dbus_interface_method(iface, "SetKeyPhase", 0, set_key_phase_call, "", "qy", "net_index", "phase"); l_dbus_interface_method(iface, "ExportKeys", 0, export_keys_call, "a(qaya{sv})a(qay)", "", "net_keys", "dev_keys"); } bool manager_dbus_init(struct l_dbus *bus) { if (!l_dbus_register_interface(bus, MESH_MANAGEMENT_INTERFACE, setup_management_interface, NULL, false)) { l_debug("Unable to register %s interface", MESH_MANAGEMENT_INTERFACE); return false; } return true; } void manager_scan_cancel(struct mesh_node *node) { struct scan_req *req; while ((req = l_queue_find(scans, by_node, node))) scan_cancel(NULL, req); } bluez-5.82/mesh/PaxHeaders/node.c0000644000000000000000000000005014772767672013724 xustar0020 atime=1743515579 20 ctime=1743591282 bluez-5.82/mesh/node.c0000644000000000000000000017015514772767672013416 0ustar00rootroot// SPDX-License-Identifier: LGPL-2.1-or-later /* * * BlueZ - Bluetooth protocol stack for Linux * * Copyright (C) 2017-2020 Intel Corporation. All rights reserved. * * */ #ifdef HAVE_CONFIG_H #include #endif #define _GNU_SOURCE #include #include #include #include #include #include #include "mesh/mesh-defs.h" #include "mesh/mesh.h" #include "mesh/net.h" #include "mesh/net-keys.h" #include "mesh/appkey.h" #include "mesh/mesh-config.h" #include "mesh/provision.h" #include "mesh/prov.h" #include "mesh/keyring.h" #include "mesh/model.h" #include "mesh/cfgmod.h" #include "mesh/remprv.h" #include "mesh/prv-beacon.h" #include "mesh/util.h" #include "mesh/error.h" #include "mesh/dbus.h" #include "mesh/agent.h" #include "mesh/manager.h" #include "mesh/rpl.h" #include "mesh/node.h" #define MESH_NODE_PATH_PREFIX "/node" /* Default values for a new locally created node */ #define DEFAULT_NEW_UNICAST 0x0001 #define DEFAULT_IV_INDEX 0x0000 /* Default element location: unknown */ #define DEFAULT_LOCATION 0x0000 enum request_type { REQUEST_TYPE_JOIN, REQUEST_TYPE_ATTACH, REQUEST_TYPE_CREATE, REQUEST_TYPE_IMPORT, }; struct node_element { char *path; struct l_queue *models; uint16_t location; uint8_t idx; }; struct node_composition { uint16_t cid; uint16_t pid; uint16_t vid; uint16_t crpl; }; struct mesh_node { struct mesh_net *net; struct l_queue *elements; struct l_queue *pages; char *app_path; char *owner; char *obj_path; struct mesh_agent *agent; struct mesh_config *cfg; char *storage_dir; uint32_t disc_watch; uint32_t seq_number; bool busy; bool provisioner; uint16_t primary; struct node_composition comp; struct { uint16_t interval; uint8_t cnt; uint8_t mode; } relay; uint8_t uuid[16]; uint8_t dev_key[16]; uint8_t token[8]; uint8_t num_ele; uint8_t ttl; uint8_t lpn; uint8_t proxy; uint8_t friend; uint8_t beacon; uint8_t mpb; uint8_t mpb_period; }; struct node_import { uint8_t dev_key[16]; uint8_t net_key[16]; uint16_t net_idx; struct { bool ivu; bool kr; } flags; uint32_t iv_index; uint16_t unicast; }; struct managed_obj_request { struct mesh_node *node; union { node_ready_func_t ready_cb; node_join_ready_func_t join_ready_cb; }; struct l_dbus_message *pending_msg; enum request_type type; union { struct mesh_node *attach; struct node_import *import; }; }; struct send_options { bool segmented; uint16_t vendor_id; }; static struct l_queue *nodes; static bool match_device_uuid(const void *a, const void *b) { const struct mesh_node *node = a; const uint8_t *uuid = b; return (memcmp(node->uuid, uuid, 16) == 0); } static bool match_token(const void *a, const void *b) { const struct mesh_node *node = a; const uint64_t *token = b; const uint64_t tmp = l_get_be64(node->token); return *token == tmp; } static bool match_element_idx(const void *a, const void *b) { const struct node_element *element = a; uint32_t index = L_PTR_TO_UINT(b); return (element->idx == index); } static int compare_element_idx(const void *a, const void *b, void *user_data) { uint32_t a_idx = ((const struct node_element *)a)->idx; uint32_t b_idx = ((const struct node_element *)b)->idx; if (a_idx < b_idx) return -1; if (a_idx > b_idx) return 1; return 0; } static bool match_element_path(const void *a, const void *b) { const struct node_element *element = a; const char *path = b; if (!element->path) return false; return (!strcmp(element->path, path)); } struct mesh_node *node_find_by_uuid(uint8_t uuid[16]) { return l_queue_find(nodes, match_device_uuid, uuid); } struct mesh_node *node_find_by_token(uint64_t token) { return l_queue_find(nodes, match_token, (void *) &token); } uint8_t *node_uuid_get(struct mesh_node *node) { if (!node) return NULL; return node->uuid; } static void set_defaults(struct mesh_node *node) { node->lpn = MESH_MODE_UNSUPPORTED; node->proxy = MESH_MODE_UNSUPPORTED; node->mpb = MESH_MODE_DISABLED; node->mpb_period = NET_MPB_REFRESH_DEFAULT; node->friend = (mesh_friendship_supported()) ? MESH_MODE_DISABLED : MESH_MODE_UNSUPPORTED; node->beacon = (mesh_beacon_enabled()) ? MESH_MODE_ENABLED : MESH_MODE_DISABLED; node->relay.mode = (mesh_relay_supported()) ? MESH_MODE_DISABLED : MESH_MODE_UNSUPPORTED; node->ttl = TTL_MASK; node->seq_number = DEFAULT_SEQUENCE_NUMBER; } static struct mesh_node *node_new(const uint8_t uuid[16]) { struct mesh_node *node; node = l_new(struct mesh_node, 1); node->net = mesh_net_new(node); node->elements = l_queue_new(); node->pages = l_queue_new(); memcpy(node->uuid, uuid, sizeof(node->uuid)); set_defaults(node); return node; } static void free_element_path(void *a, void *b) { struct node_element *element = a; l_free(element->path); element->path = NULL; } static void element_free(void *data) { struct node_element *element = data; l_queue_destroy(element->models, mesh_model_free); l_free(element->path); l_free(element); } static void free_node_dbus_resources(struct mesh_node *node) { if (!node) return; if (node->disc_watch) { l_dbus_remove_watch(dbus_get_bus(), node->disc_watch); node->disc_watch = 0; } l_queue_foreach(node->elements, free_element_path, NULL); l_free(node->owner); node->owner = NULL; l_free(node->app_path); node->app_path = NULL; if (node->obj_path) { l_dbus_object_remove_interface(dbus_get_bus(), node->obj_path, MESH_NODE_INTERFACE); l_dbus_object_remove_interface(dbus_get_bus(), node->obj_path, MESH_MANAGEMENT_INTERFACE); l_dbus_object_remove_interface(dbus_get_bus(), node->obj_path, L_DBUS_INTERFACE_PROPERTIES); l_free(node->obj_path); node->obj_path = NULL; } } static void free_node_resources(void *data) { struct mesh_node *node = data; /* Unregister io callbacks */ mesh_net_detach(node->net); /* In case of a provisioner, stop active scanning */ if (node->provisioner) manager_scan_cancel(node); /* Free dynamic resources */ free_node_dbus_resources(node); l_queue_destroy(node->elements, element_free); l_queue_destroy(node->pages, l_free); mesh_agent_remove(node->agent); mesh_config_release(node->cfg); mesh_net_free(node->net); l_free(node->storage_dir); l_free(node); } /* * This function is called to free resources and remove the * configuration files for the specified node. */ void node_remove(struct mesh_node *node) { if (!node) return; l_queue_remove(nodes, node); mesh_config_destroy_nvm(node->cfg); free_node_resources(node); } static bool add_element_from_storage(struct mesh_node *node, struct mesh_config_element *db_ele) { struct node_element *ele; ele = l_new(struct node_element, 1); if (!ele) return false; ele->idx = db_ele->index; ele->location = db_ele->location; ele->models = l_queue_new(); l_queue_push_tail(node->elements, ele); if (!mesh_model_add_from_storage(node, ele->idx, ele->models, db_ele->models)) return false; return true; } static bool add_elements_from_storage(struct mesh_node *node, struct mesh_config_node *db_node) { const struct l_queue_entry *entry; struct node_element *ele; entry = l_queue_get_entries(db_node->elements); for (; entry; entry = entry->next) if (!add_element_from_storage(node, entry->data)) return false; ele = l_queue_find(node->elements, match_element_idx, L_UINT_TO_PTR(PRIMARY_ELE_IDX)); if (!ele) return false; /* Add configuration server model on the primary element */ mesh_model_add(node, ele->models, CONFIG_SRV_MODEL, NULL); /* Add remote provisioning models on the primary element */ mesh_model_add(node, ele->models, REM_PROV_SRV_MODEL, NULL); if (node->provisioner) mesh_model_add(node, ele->models, REM_PROV_CLI_MODEL, NULL); return true; } static void set_net_key(void *a, void *b) { struct mesh_config_netkey *netkey = a; struct mesh_node *node = b; mesh_net_set_key(node->net, netkey->idx, netkey->key, netkey->new_key, netkey->phase); } static void set_appkey(void *a, void *b) { struct mesh_config_appkey *appkey = a; struct mesh_node *node = b; appkey_key_init(node->net, appkey->net_idx, appkey->app_idx, appkey->key, appkey->new_key); } static bool init_storage_dir(struct mesh_node *node) { char uuid[33]; char dir_name[PATH_MAX]; if (node->storage_dir) return true; if (!hex2str(node->uuid, 16, uuid, sizeof(uuid))) return false; snprintf(dir_name, PATH_MAX, "%s/%s", mesh_get_storage_dir(), uuid); if (strlen(dir_name) >= PATH_MAX) return false; create_dir(dir_name); node->storage_dir = l_strdup(dir_name); /* Initialize directory for storing RPL info */ return rpl_init(node->storage_dir); } static void init_net_settings(struct mesh_node *node) { struct mesh_net *net = node->net; mesh_net_set_proxy_mode(net, node->proxy == MESH_MODE_ENABLED); mesh_net_set_friend_mode(net, node->friend == MESH_MODE_ENABLED); mesh_net_set_relay_mode(net, node->relay.mode == MESH_MODE_ENABLED, node->relay.cnt, node->relay.interval); mesh_net_set_snb_mode(net, node->beacon == MESH_MODE_ENABLED); mesh_net_set_mpb_mode(net, node->mpb == MESH_MODE_ENABLED, node->mpb_period, true); } static bool init_from_storage(struct mesh_config_node *db_node, const uint8_t uuid[16], struct mesh_config *cfg, void *user_data) { unsigned int num_ele; struct mesh_node *node = node_new(uuid); if (!nodes) nodes = l_queue_new(); l_queue_push_tail(nodes, node); node->comp.cid = db_node->cid; node->comp.pid = db_node->pid; node->comp.vid = db_node->vid; node->comp.crpl = db_node->crpl; node->lpn = db_node->modes.lpn; node->proxy = db_node->modes.proxy; node->friend = db_node->modes.friend; node->relay.mode = db_node->modes.relay.state; node->relay.cnt = db_node->modes.relay.cnt; node->relay.interval = db_node->modes.relay.interval; node->beacon = db_node->modes.beacon; node->mpb = db_node->modes.mpb; node->mpb_period = db_node->modes.mpb_period; l_debug("relay %2.2x, proxy %2.2x, lpn %2.2x, friend %2.2x", node->relay.mode, node->proxy, node->lpn, node->friend); node->ttl = db_node->ttl; node->seq_number = db_node->seq_number; memcpy(node->dev_key, db_node->dev_key, 16); memcpy(node->token, db_node->token, 8); num_ele = l_queue_length(db_node->elements); if (num_ele > MAX_ELE_COUNT) goto fail; node->num_ele = num_ele; if (num_ele != 0 && !add_elements_from_storage(node, db_node)) goto fail; node->primary = db_node->unicast; if (!db_node->netkeys) goto fail; if (!IS_UNASSIGNED(node->primary) && !mesh_net_register_unicast(node->net, node->primary, num_ele)) goto fail; mesh_net_set_iv_index(node->net, db_node->iv_index, db_node->iv_update); /* Initialize directory for storing keyring and RPL info */ if (!init_storage_dir(node) || !mesh_net_load_rpl(node->net)) goto fail; if (db_node->net_transmit) mesh_net_transmit_params_set(node->net, db_node->net_transmit->count, db_node->net_transmit->interval); l_queue_foreach(db_node->netkeys, set_net_key, node); l_queue_foreach(db_node->appkeys, set_appkey, node); while (l_queue_length(db_node->pages)) { struct mesh_config_comp_page *page; /* Move the composition pages to the node struct */ page = l_queue_pop_head(db_node->pages); l_queue_push_tail(node->pages, page); } mesh_net_set_seq_num(node->net, node->seq_number); mesh_net_set_default_ttl(node->net, node->ttl); init_net_settings(node); /* Initialize configuration server model */ cfgmod_server_init(node, PRIMARY_ELE_IDX); /* Initialize remote provisioning models */ remote_prov_server_init(node, PRIMARY_ELE_IDX); remote_prov_client_init(node, PRIMARY_ELE_IDX); /* Initialize Private Beacon server model */ prv_beacon_server_init(node, PRIMARY_ELE_IDX); node->cfg = cfg; return true; fail: node_remove(node); return false; } static void cleanup_node(void *data) { struct mesh_node *node = data; uint32_t seq_num = mesh_net_get_seq_num(node->net); /* Preserve the last used sequence number */ mesh_config_write_seq_number(node->cfg, seq_num, false); free_node_resources(node); } /* * This function is called to free resources and write the current * sequence numbers to the configuration file for each known node. */ void node_cleanup_all(void) { l_queue_destroy(nodes, cleanup_node); l_dbus_unregister_interface(dbus_get_bus(), MESH_NODE_INTERFACE); l_dbus_unregister_interface(dbus_get_bus(), MESH_MANAGEMENT_INTERFACE); } bool node_is_provisioner(struct mesh_node *node) { return node->provisioner; } bool node_is_busy(struct mesh_node *node) { return node->busy; } void node_app_key_delete(struct mesh_node *node, uint16_t net_idx, uint16_t app_idx) { const struct l_queue_entry *entry; entry = l_queue_get_entries(node->elements); for (; entry; entry = entry->next) { struct node_element *ele = entry->data; mesh_model_app_key_delete(node, ele->idx, ele->models, app_idx); } } uint16_t node_get_primary(struct mesh_node *node) { if (!node) return UNASSIGNED_ADDRESS; else return node->primary; } bool node_refresh(struct mesh_node *node, bool hard, void *prov_info) { struct mesh_prov_node_info *info = prov_info; bool res = true; if (!node || !info) return false; if (!IS_UNICAST(info->unicast)) return false; /* Changing Unicast addresses requires a hard node reset */ if (!hard && info->unicast != node->primary) return false; /* * Hard refresh results in immediate use of new Device Key. * Soft refresh saves new device key as Candidate until we * successfully receive new incoming message on that key. */ if (hard) { if (!mesh_config_write_device_key(node->cfg, info->device_key)) return false; memcpy(node->dev_key, info->device_key, sizeof(node->dev_key)); } else if (!mesh_config_write_candidate(node->cfg, info->device_key)) return false; /* Replace Primary Unicast address if it has changed */ if (node->primary != info->unicast) { res = mesh_config_write_unicast(node->cfg, info->unicast); if (res) { node->primary = info->unicast; node->num_ele = info->num_ele; mesh_net_register_unicast(node->net, node->primary, node->num_ele); } } /* Replace Page 0 with Page 128 if it exists */ if (res) { if (node_replace_comp(node, 0, 128)) return true; } return res; } const uint8_t *node_get_device_key(struct mesh_node *node) { if (!node) return NULL; return node->dev_key; } bool node_get_device_key_candidate(struct mesh_node *node, uint8_t *key) { if (!node) return false; return mesh_config_read_candidate(node->cfg, key); } void node_finalize_candidate(struct mesh_node *node) { if (!node) return; if (mesh_config_read_candidate(node->cfg, node->dev_key)) mesh_config_finalize_candidate(node->cfg); } void node_set_token(struct mesh_node *node, uint8_t token[8]) { memcpy(node->token, token, 8); } const uint8_t *node_get_token(struct mesh_node *node) { if (!node) return NULL; else return node->token; } uint8_t node_get_num_elements(struct mesh_node *node) { return node->num_ele; } struct l_queue *node_get_element_models(struct mesh_node *node, uint8_t ele_idx) { struct node_element *ele; if (!node) return NULL; ele = l_queue_find(node->elements, match_element_idx, L_UINT_TO_PTR(ele_idx)); if (!ele) return NULL; return ele->models; } uint8_t node_default_ttl_get(struct mesh_node *node) { if (!node) return TTL_MASK; return node->ttl; } bool node_default_ttl_set(struct mesh_node *node, uint8_t ttl) { bool res; if (!node) return false; res = mesh_config_write_ttl(node->cfg, ttl); if (res) { node->ttl = ttl; mesh_net_set_default_ttl(node->net, ttl); } return res; } bool node_set_sequence_number(struct mesh_node *node, uint32_t seq) { if (!node) return false; node->seq_number = seq; return mesh_config_write_seq_number(node->cfg, node->seq_number, true); } uint32_t node_get_sequence_number(struct mesh_node *node) { if (!node) return 0xffffffff; return node->seq_number; } int node_get_element_idx(struct mesh_node *node, uint16_t ele_addr) { uint16_t addr; uint8_t num_ele; if (!node) return -1; num_ele = node_get_num_elements(node); if (!num_ele) return -2; addr = node_get_primary(node); if (ele_addr < addr || ele_addr >= addr + num_ele) return -3; else return ele_addr - addr; } uint16_t node_get_crpl(struct mesh_node *node) { if (!node) return 0; return node->comp.crpl; } uint8_t node_relay_mode_get(struct mesh_node *node, uint8_t *count, uint16_t *interval) { if (!node) { *count = 0; *interval = 0; return MESH_MODE_DISABLED; } *count = node->relay.cnt; *interval = node->relay.interval; return node->relay.mode; } uint8_t node_lpn_mode_get(struct mesh_node *node) { if (!node) return MESH_MODE_DISABLED; return node->lpn; } bool node_relay_mode_set(struct mesh_node *node, bool enable, uint8_t cnt, uint16_t interval) { bool res; if (!node || node->relay.mode == MESH_MODE_UNSUPPORTED) return false; res = mesh_config_write_relay_mode(node->cfg, enable, cnt, interval); if (res) { node->relay.mode = enable ? MESH_MODE_ENABLED : MESH_MODE_DISABLED; node->relay.cnt = cnt; node->relay.interval = interval; mesh_net_set_relay_mode(node->net, enable, cnt, interval); } return res; } bool node_proxy_mode_set(struct mesh_node *node, bool enable) { bool res; uint8_t proxy; if (!node || node->proxy == MESH_MODE_UNSUPPORTED) return false; proxy = enable ? MESH_MODE_ENABLED : MESH_MODE_DISABLED; res = mesh_config_write_mode(node->cfg, "proxy", proxy); if (res) { node->proxy = proxy; mesh_net_set_proxy_mode(node->net, enable); } return res; } uint8_t node_proxy_mode_get(struct mesh_node *node) { if (!node) return MESH_MODE_DISABLED; return node->proxy; } bool node_beacon_mode_set(struct mesh_node *node, bool enable) { bool res; uint8_t beacon; if (!node) return false; beacon = enable ? MESH_MODE_ENABLED : MESH_MODE_DISABLED; res = mesh_config_write_mode(node->cfg, "beacon", beacon); if (res) { node->beacon = beacon; mesh_net_set_snb_mode(node->net, enable); } return res; } uint8_t node_beacon_mode_get(struct mesh_node *node) { if (!node) return MESH_MODE_DISABLED; return node->beacon; } bool node_mpb_mode_set(struct mesh_node *node, bool enable, uint8_t period) { bool res; uint8_t beacon; if (!node) return false; beacon = enable ? MESH_MODE_ENABLED : MESH_MODE_DISABLED; res = mesh_config_write_mpb(node->cfg, beacon, period); if (res) { node->mpb = beacon; node->mpb_period = period; mesh_net_set_mpb_mode(node->net, enable, period, false); } return res; } uint8_t node_mpb_mode_get(struct mesh_node *node, uint8_t *period) { if (!node) return MESH_MODE_DISABLED; *period = node->mpb_period; return node->mpb; } bool node_friend_mode_set(struct mesh_node *node, bool enable) { bool res; uint8_t friend; if (!node || node->friend == MESH_MODE_UNSUPPORTED) return false; friend = enable ? MESH_MODE_ENABLED : MESH_MODE_DISABLED; res = mesh_config_write_mode(node->cfg, "friend", friend); if (res) { node->friend = friend; mesh_net_set_friend_mode(node->net, enable); } return res; } uint8_t node_friend_mode_get(struct mesh_node *node) { if (!node) return MESH_MODE_DISABLED; return node->friend; } static uint16_t node_generate_comp(struct mesh_node *node, uint8_t *buf, uint16_t sz) { uint16_t n, features, num_ele = 0; const struct l_queue_entry *entry; n = 0; l_put_le16(node->comp.cid, buf + n); n += 2; l_put_le16(node->comp.pid, buf + n); n += 2; l_put_le16(node->comp.vid, buf + n); n += 2; l_put_le16(node->comp.crpl, buf + n); n += 2; features = 0; if (node->relay.mode != MESH_MODE_UNSUPPORTED) features |= FEATURE_RELAY; if (node->proxy != MESH_MODE_UNSUPPORTED) features |= FEATURE_PROXY; if (node->friend != MESH_MODE_UNSUPPORTED) features |= FEATURE_FRIEND; if (node->lpn != MESH_MODE_UNSUPPORTED) features |= FEATURE_LPN; l_put_le16(features, buf + n); n += 2; entry = l_queue_get_entries(node->elements); for (; entry; entry = entry->next) { struct node_element *ele = entry->data; if (ele->idx != num_ele) return 0; num_ele++; /* At least fit location and zeros for number of models */ if ((n + 4) > sz) return n; l_put_le16(ele->location, buf + n); n += 2; n += mesh_model_generate_composition(ele->models, sz - n, buf + n); } if (!num_ele) return 0; return n; } static bool match_page(const void *a, const void *b) { const struct mesh_config_comp_page *page = a; uint8_t page_num = L_PTR_TO_UINT(b); return page->page_num == page_num; } static void convert_node_to_storage(struct mesh_node *node, struct mesh_config_node *db_node) { const struct l_queue_entry *entry; memset(db_node, 0, sizeof(struct mesh_config_node)); db_node->cid = node->comp.cid; db_node->pid = node->comp.pid; db_node->vid = node->comp.vid; db_node->crpl = node->comp.crpl; db_node->modes.lpn = node->lpn; db_node->modes.proxy = node->proxy; db_node->modes.friend = node->friend; db_node->modes.relay.state = node->relay.mode; db_node->modes.relay.cnt = node->relay.cnt; db_node->modes.relay.interval = node->relay.interval; db_node->modes.beacon = node->beacon; db_node->modes.mpb = node->mpb; db_node->modes.mpb_period = node->mpb_period; db_node->ttl = node->ttl; db_node->seq_number = node->seq_number; db_node->elements = l_queue_new(); entry = l_queue_get_entries(node->elements); for (; entry; entry = entry->next) { struct node_element *ele = entry->data; struct mesh_config_element *db_ele; db_ele = l_new(struct mesh_config_element, 1); db_ele->index = ele->idx; db_ele->location = ele->location; db_ele->models = l_queue_new(); mesh_model_convert_to_storage(db_ele->models, ele->models); l_queue_push_tail(db_node->elements, db_ele); } } static void free_db_storage(struct mesh_config_node *db_node) { const struct l_queue_entry *entry; /* Free temporarily allocated resources */ entry = l_queue_get_entries(db_node->elements); for (; entry; entry = entry->next) { struct mesh_config_element *db_ele = entry->data; l_queue_destroy(db_ele->models, l_free); } l_queue_destroy(db_node->elements, l_free); } static bool create_node_config(struct mesh_node *node, const uint8_t uuid[16]) { struct mesh_config_node db_node; const struct l_queue_entry *entry; const char *storage_dir; convert_node_to_storage(node, &db_node); storage_dir = mesh_get_storage_dir(); node->cfg = mesh_config_create(storage_dir, uuid, &db_node); if (node->cfg) init_storage_dir(node); /* Free temporarily allocated resources */ entry = l_queue_get_entries(db_node.elements); for (; entry; entry = entry->next) { struct mesh_config_element *db_ele = entry->data; l_queue_destroy(db_ele->models, l_free); } l_queue_destroy(db_node.elements, l_free); return node->cfg != NULL; } static void node_del_comp(struct mesh_node *node, uint8_t page_num) { struct mesh_config_comp_page *page; if (!node) return; page = l_queue_remove_if(node->pages, match_page, L_UINT_TO_PTR(page_num)); l_free(page); mesh_config_comp_page_del(node->cfg, page_num); } static bool node_set_comp(struct mesh_node *node, uint8_t page_num, const uint8_t *data, uint16_t len) { struct mesh_config_comp_page *page; if (len < MIN_COMP_SIZE) return false; page = l_queue_remove_if(node->pages, match_page, L_UINT_TO_PTR(page_num)); l_free(page); page = l_malloc(sizeof(struct mesh_config_comp_page) + len); page->len = len; page->page_num = page_num; memcpy(page->data, data, len); l_queue_push_tail(node->pages, page); return mesh_config_comp_page_add(node->cfg, page_num, page->data, len); } const uint8_t *node_get_comp(struct mesh_node *node, uint8_t page_num, uint16_t *len) { struct mesh_config_comp_page *page = NULL; if (node) page = l_queue_find(node->pages, match_page, L_UINT_TO_PTR(page_num)); if (!page) { *len = 0; return NULL; } *len = page->len; return page->data; } bool node_replace_comp(struct mesh_node *node, uint8_t retire, uint8_t with) { struct mesh_config_comp_page *old_page, *keep; bool status; if (!node) return false; keep = l_queue_find(node->pages, match_page, L_UINT_TO_PTR(with)); if (!keep) return false; old_page = l_queue_remove_if(node->pages, match_page, L_UINT_TO_PTR(retire)); l_free(old_page); keep->page_num = retire; status = mesh_config_comp_page_add(node->cfg, keep->page_num, keep->data, keep->len); if (with != retire) mesh_config_comp_page_del(node->cfg, with); return status; } static void attach_io(void *a, void *b) { struct mesh_node *node = a; struct mesh_io *io = b; if (node->net) mesh_net_attach(node->net, io); } /* Register callbacks for all nodes io */ void node_attach_io_all(struct mesh_io *io) { l_queue_foreach(nodes, attach_io, io); } /* Register node object with D-Bus */ static bool register_node_object(struct mesh_node *node) { char uuid[33]; if (!hex2str(node->uuid, sizeof(node->uuid), uuid, sizeof(uuid))) return false; node->obj_path = l_strdup_printf(BLUEZ_MESH_PATH MESH_NODE_PATH_PREFIX "%s", uuid); if (!l_dbus_object_add_interface(dbus_get_bus(), node->obj_path, MESH_NODE_INTERFACE, node)) return false; if (!l_dbus_object_add_interface(dbus_get_bus(), node->obj_path, MESH_MANAGEMENT_INTERFACE, node)) return false; if (!l_dbus_object_add_interface(dbus_get_bus(), node->obj_path, L_DBUS_INTERFACE_PROPERTIES, NULL)) return false; return true; } static void app_disc_cb(struct l_dbus *bus, void *user_data) { struct mesh_node *node = user_data; l_info("App %s disconnected (%u)", node->owner, node->disc_watch); node->disc_watch = 0; /* In case of a provisioner, stop active scanning */ if (node->provisioner) manager_scan_cancel(node); free_node_dbus_resources(node); } static bool get_sig_models_from_properties(struct mesh_node *node, struct node_element *ele, struct l_dbus_message_iter *property) { struct l_dbus_message_iter mods, var; uint16_t m_id; if (!ele->models) ele->models = l_queue_new(); if (!l_dbus_message_iter_get_variant(property, "a(qa{sv})", &mods)) return false; /* Bluetooth SIG defined models */ while (l_dbus_message_iter_next_entry(&mods, &m_id, &var)) { uint32_t id = SET_ID(SIG_VENDOR, m_id); /* * Allow Config Server & Private Beacon Models only on * the primary element */ if (ele->idx != PRIMARY_ELE_IDX) { if (id == CONFIG_SRV_MODEL) return false; if (id == PRV_BEACON_SRV_MODEL) return false; } if (!mesh_model_add(node, ele->models, id, &var)) return false; } return true; } static bool get_vendor_models_from_properties(struct mesh_node *node, struct node_element *ele, struct l_dbus_message_iter *property) { struct l_dbus_message_iter mods, var; uint16_t m_id, v_id; if (!ele->models) ele->models = l_queue_new(); if (!l_dbus_message_iter_get_variant(property, "a(qqa{sv})", &mods)) return false; /* Vendor defined models */ while (l_dbus_message_iter_next_entry(&mods, &v_id, &m_id, &var)) { uint32_t id = SET_ID(v_id, m_id); if (!mesh_model_add(node, ele->models, id, &var)) return false; } return true; } static bool get_element_properties(struct mesh_node *node, const char *path, struct l_dbus_message_iter *properties) { struct node_element *ele = l_new(struct node_element, 1); const char *key; struct l_dbus_message_iter var; bool idx = false; bool mods = false; bool vendor_mods = false; l_debug("path %s", path); ele->location = DEFAULT_LOCATION; while (l_dbus_message_iter_next_entry(properties, &key, &var)) { if (!strcmp(key, "Index")) { if (idx || !l_dbus_message_iter_get_variant(&var, "y", &ele->idx)) goto fail; idx = true; } else if (!strcmp(key, "Models")) { if (mods) goto fail; if (!get_sig_models_from_properties(node, ele, &var)) goto fail; mods = true; } else if (!strcmp(key, "VendorModels")) { if (vendor_mods) goto fail; if (!get_vendor_models_from_properties(node, ele, &var)) goto fail; vendor_mods = true; } else if (!strcmp(key, "Location")) { if (!l_dbus_message_iter_get_variant(&var, "q", &ele->location)) goto fail; } } /* Check for the presence of the required properties */ if (!idx || !mods || !vendor_mods) goto fail; if (l_queue_find(node->elements, match_element_idx, L_UINT_TO_PTR(ele->idx))) goto fail; l_queue_insert(node->elements, ele, compare_element_idx, NULL); ele->path = l_strdup(path); /* * Add configuration server model on the primary element. * We allow the application not to specify the presense of * the Configuration Server model, since it's implemented by the * daemon. If the model is present in the application properties, * the operation below will be a "no-op". */ if (ele->idx == PRIMARY_ELE_IDX) { mesh_model_add(node, ele->models, CONFIG_SRV_MODEL, NULL); mesh_model_add(node, ele->models, PRV_BEACON_SRV_MODEL, NULL); mesh_model_add(node, ele->models, REM_PROV_SRV_MODEL, NULL); if (node->provisioner) mesh_model_add(node, ele->models, REM_PROV_CLI_MODEL, NULL); } return true; fail: l_free(ele); return false; } static bool get_app_properties(struct mesh_node *node, const char *path, struct l_dbus_message_iter *properties) { const char *key; struct l_dbus_message_iter variant; bool cid = false; bool pid = false; bool vid = false; l_debug("path %s", path); node->comp.crpl = mesh_get_crpl(); while (l_dbus_message_iter_next_entry(properties, &key, &variant)) { if (!cid && !strcmp(key, "CompanyID")) { if (!l_dbus_message_iter_get_variant(&variant, "q", &node->comp.cid)) return false; cid = true; continue; } if (!pid && !strcmp(key, "ProductID")) { if (!l_dbus_message_iter_get_variant(&variant, "q", &node->comp.pid)) return false; pid = true; continue; } if (!vid && !strcmp(key, "VersionID")) { if (!l_dbus_message_iter_get_variant(&variant, "q", &node->comp.vid)) return false; vid = true; continue; } if (!strcmp(key, "CRPL")) { if (!l_dbus_message_iter_get_variant(&variant, "q", &node->comp.crpl)) return false; continue; } } if (!cid || !pid || !vid) return false; return true; } static void save_pages(void *data, void *user_data) { struct mesh_config_comp_page *page = data; struct mesh_node *node = user_data; mesh_config_comp_page_add(node->cfg, page->page_num, page->data, page->len); } static bool add_local_node(struct mesh_node *node, uint16_t unicast, bool kr, bool ivu, uint32_t iv_idx, uint8_t dev_key[16], uint16_t net_key_idx, uint8_t net_key[16]) { if (!nodes) nodes = l_queue_new(); l_queue_push_tail(nodes, node); if (!mesh_config_write_iv_index(node->cfg, iv_idx, ivu)) return false; mesh_net_set_iv_index(node->net, iv_idx, ivu); if (!mesh_config_write_unicast(node->cfg, unicast)) return false; l_getrandom(node->token, sizeof(node->token)); if (!mesh_config_write_token(node->cfg, node->token)) return false; memcpy(node->dev_key, dev_key, 16); if (!mesh_config_write_device_key(node->cfg, dev_key)) return false; node->primary = unicast; mesh_net_register_unicast(node->net, unicast, node->num_ele); if (mesh_net_add_key(node->net, net_key_idx, net_key) != MESH_STATUS_SUCCESS) return false; if (kr) { /* Duplicate net key, if the key refresh is on */ if (mesh_net_update_key(node->net, net_key_idx, net_key) != MESH_STATUS_SUCCESS) return false; if (mesh_net_key_refresh_phase_set(node->net, net_key_idx, KEY_REFRESH_PHASE_TWO) != MESH_STATUS_SUCCESS) return false; } l_queue_foreach(node->pages, save_pages, node); init_net_settings(node); /* Initialize internal server models */ cfgmod_server_init(node, PRIMARY_ELE_IDX); remote_prov_server_init(node, PRIMARY_ELE_IDX); remote_prov_client_init(node, PRIMARY_ELE_IDX); /* Initialize Private Beacon server model */ prv_beacon_server_init(node, PRIMARY_ELE_IDX); node->busy = true; return true; } static void update_composition(struct mesh_node *node, struct mesh_node *attach) { if (node->comp.cid != attach->comp.cid) mesh_config_update_company_id(attach->cfg, node->comp.cid); if (node->comp.pid != attach->comp.pid) mesh_config_update_product_id(attach->cfg, node->comp.pid); if (node->comp.vid != attach->comp.vid) mesh_config_update_version_id(attach->cfg, node->comp.vid); if (node->comp.crpl != attach->comp.crpl) mesh_config_update_crpl(attach->cfg, node->comp.crpl); attach->comp = node->comp; } static void update_model_options(struct mesh_node *node, struct mesh_node *attach) { uint32_t len, i; struct node_element *ele, *ele_attach; len = l_queue_length(node->elements); for (i = 0; i < len; i++) { ele = l_queue_find(node->elements, match_element_idx, L_UINT_TO_PTR(i)); ele_attach = l_queue_find(attach->elements, match_element_idx, L_UINT_TO_PTR(i)); if (!ele || !ele_attach) continue; mesh_model_update_opts(node, ele->idx, ele_attach->models, ele->models); } } static bool check_req_node(struct managed_obj_request *req) { struct mesh_node *node; const int offset = 8; uint16_t node_len, len; uint8_t comp[MAX_MSG_LEN - 2]; const uint8_t *node_comp; if (req->type != REQUEST_TYPE_ATTACH) { node = req->node; if (!create_node_config(node, node->uuid)) return false; } else node = req->attach; node_comp = node_get_comp(node, 0, &node_len); len = node_generate_comp(req->node, comp, sizeof(comp)); /* If no page 0 exists, then current composition as valid */ if (req->type != REQUEST_TYPE_ATTACH || !node_len) goto page_zero_valid; /* * If composition has materially changed, save new composition * in page 128 until next NPPI procedure. But we do allow * for CID, PID, VID and/or CRPL to freely change without * requiring a NPPI procedure. */ if (node_len != len || memcmp(&node_comp[offset], &comp[offset], node_len - offset)) return node_set_comp(node, 128, comp, len); page_zero_valid: /* If page 0 represents current App, ensure page 128 doesn't exist */ node_del_comp(node, 128); if (len == node_len && !memcmp(node_comp, comp, len)) return true; return node_set_comp(node, 0, comp, len); } static bool is_zero(const void *a, const void *b) { const struct node_element *element = a; return !element->idx; } static bool attach_req_node(struct mesh_node *attach, struct mesh_node *node) { const struct l_queue_entry *attach_entry; const struct l_queue_entry *node_entry; bool comp_changed = false; attach->obj_path = node->obj_path; node->obj_path = NULL; if (!register_node_object(attach)) { free_node_dbus_resources(attach); return false; } if (attach->num_ele != node->num_ele) { struct mesh_config_node db_node; struct node_element *old_ele, *new_ele; convert_node_to_storage(node, &db_node); /* * If composition has materially changed, we need to discard * everything we knew about elements in the old application, * and start from what they are telling us now. */ old_ele = l_queue_remove_if(attach->elements, is_zero, NULL); new_ele = l_queue_remove_if(node->elements, is_zero, NULL); element_free(new_ele); l_queue_destroy(attach->elements, element_free); attach->elements = node->elements; attach->num_ele = node->num_ele; /* Restore primary elements */ l_queue_push_head(attach->elements, old_ele); comp_changed = true; mesh_config_reset(attach->cfg, &db_node); free_db_storage(&db_node); } attach_entry = l_queue_get_entries(attach->elements); node_entry = l_queue_get_entries(node->elements); /* * Update existing node with paths collected in temporary node, * then remove the temporary. */ while (attach_entry && node_entry) { struct node_element *attach_ele = attach_entry->data; struct node_element *node_ele = node_entry->data; attach_ele->path = node_ele->path; node_ele->path = NULL; attach_entry = attach_entry->next; node_entry = node_entry->next; /* Only need the Primary element during Composition change */ if (comp_changed) break; } mesh_agent_remove(attach->agent); attach->agent = node->agent; node->agent = NULL; attach->provisioner = node->provisioner; attach->app_path = node->app_path; node->app_path = NULL; attach->owner = node->owner; node->owner = NULL; update_composition(node, attach); update_model_options(node, attach); if (comp_changed) node->elements = NULL; node_remove(node); return true; } static void get_managed_objects_cb(struct l_dbus_message *msg, void *user_data) { struct l_dbus_message_iter objects, interfaces; struct managed_obj_request *req = user_data; const char *path; struct mesh_node *node = req->node; struct node_import *import; bool have_app = false; unsigned int num_ele; struct keyring_net_key net_key; uint8_t dev_key[16]; if (req->type == REQUEST_TYPE_ATTACH) req->attach->busy = false; if (!msg || l_dbus_message_is_error(msg)) { l_error("Failed to get app's dbus objects"); goto fail; } if (!l_dbus_message_get_arguments(msg, "a{oa{sa{sv}}}", &objects)) { l_error("Failed to parse app's dbus objects"); goto fail; } while (l_dbus_message_iter_next_entry(&objects, &path, &interfaces)) { struct l_dbus_message_iter properties; const char *interface; while (l_dbus_message_iter_next_entry(&interfaces, &interface, &properties)) { bool res; if (!strcmp(MESH_ELEMENT_INTERFACE, interface)) { res = get_element_properties(node, path, &properties); if (!res) goto fail; } else if (!strcmp(MESH_APPLICATION_INTERFACE, interface)) { if (have_app) goto fail; req->node->app_path = l_strdup(path); res = get_app_properties(node, path, &properties); if (!res) goto fail; have_app = true; } else if (!strcmp(MESH_PROVISION_AGENT_INTERFACE, interface)) { const char *sender; sender = l_dbus_message_get_sender(msg); node->agent = mesh_agent_create(path, sender, &properties); if (!node->agent) goto fail; } else if (!strcmp(MESH_PROVISIONER_INTERFACE, interface)) { node->provisioner = true; } } } if (!have_app) { l_error("Interface %s not found", MESH_APPLICATION_INTERFACE); goto fail; } if (l_queue_isempty(node->elements)) { l_error("Interface %s not found", MESH_ELEMENT_INTERFACE); goto fail; } if (!l_queue_find(node->elements, match_element_idx, L_UINT_TO_PTR(PRIMARY_ELE_IDX))) { l_debug("Primary element not detected"); goto fail; } num_ele = l_queue_length(node->elements); if (num_ele > MAX_ELE_COUNT) goto fail; node->num_ele = num_ele; if (!check_req_node(req)) goto fail; switch (req->type) { case REQUEST_TYPE_ATTACH: if (!attach_req_node(req->attach, node)) goto fail; req->attach->disc_watch = l_dbus_add_disconnect_watch( dbus_get_bus(), req->attach->owner, app_disc_cb, req->attach, NULL); req->ready_cb(req->pending_msg, MESH_ERROR_NONE, req->attach); return; case REQUEST_TYPE_JOIN: if (!node->agent) { l_error("Interface %s not found", MESH_PROVISION_AGENT_INTERFACE); goto fail; } req->join_ready_cb(node, node->agent); return; case REQUEST_TYPE_IMPORT: import = req->import; if (!add_local_node(node, import->unicast, import->flags.kr, import->flags.ivu, import->iv_index, import->dev_key, import->net_idx, import->net_key)) goto fail; req->ready_cb(req->pending_msg, MESH_ERROR_NONE, node); l_free(import); return; case REQUEST_TYPE_CREATE: /* Generate device and primary network keys */ l_getrandom(dev_key, sizeof(dev_key)); l_getrandom(net_key.old_key, sizeof(net_key.old_key)); memcpy(net_key.new_key, net_key.old_key, sizeof(net_key.old_key)); net_key.net_idx = PRIMARY_NET_IDX; net_key.phase = KEY_REFRESH_PHASE_NONE; if (!add_local_node(node, DEFAULT_NEW_UNICAST, false, false, DEFAULT_IV_INDEX, dev_key, PRIMARY_NET_IDX, net_key.old_key)) goto fail; if (!keyring_put_remote_dev_key(node, DEFAULT_NEW_UNICAST, node->num_ele, dev_key)) goto fail; if (!keyring_put_net_key(node, PRIMARY_NET_IDX, &net_key)) goto fail; req->ready_cb(req->pending_msg, MESH_ERROR_NONE, node); return; default: goto fail; } fail: /* Handle failed requests */ node_remove(node); if (req->type == REQUEST_TYPE_JOIN) req->join_ready_cb(NULL, NULL); else req->ready_cb(req->pending_msg, MESH_ERROR_FAILED, NULL); if (req->type == REQUEST_TYPE_IMPORT) l_free(req->import); } static void send_managed_objects_request(const char *destination, const char *path, struct managed_obj_request *req) { struct l_dbus_message *msg; msg = l_dbus_message_new_method_call(dbus_get_bus(), destination, path, L_DBUS_INTERFACE_OBJECT_MANAGER, "GetManagedObjects"); l_dbus_message_set_arguments(msg, ""); dbus_send_with_timeout(dbus_get_bus(), msg, get_managed_objects_cb, req, l_free, DEFAULT_DBUS_TIMEOUT); } /* Establish relationship between application and mesh node */ void node_attach(const char *app_root, const char *sender, uint64_t token, node_ready_func_t cb, void *user_data) { struct managed_obj_request *req; struct mesh_node *node; node = l_queue_find(nodes, match_token, (void *) &token); if (!node) { cb(user_data, MESH_ERROR_NOT_FOUND, NULL); return; } /* Check if there is a pending request associated with this node */ if (node->busy) { cb(user_data, MESH_ERROR_BUSY, NULL); return; } /* Check if the node is already in use */ if (node->owner) { l_warn("The node is already in use"); cb(user_data, MESH_ERROR_ALREADY_EXISTS, NULL); return; } req = l_new(struct managed_obj_request, 1); /* * Create a temporary node to collect composition data from attaching * application. Existing node is passed in req->attach. */ req->node = node_new(node->uuid); req->node->owner = l_strdup(sender); req->ready_cb = cb; req->pending_msg = user_data; req->attach = node; req->type = REQUEST_TYPE_ATTACH; node->busy = true; send_managed_objects_request(sender, app_root, req); } /* Create a temporary pre-provisioned node */ void node_join(const char *app_root, const char *sender, const uint8_t *uuid, node_join_ready_func_t cb) { struct managed_obj_request *req; l_debug(""); req = l_new(struct managed_obj_request, 1); req->node = node_new(uuid); req->join_ready_cb = cb; req->type = REQUEST_TYPE_JOIN; send_managed_objects_request(sender, app_root, req); } void node_import(const char *app_root, const char *sender, const uint8_t *uuid, const uint8_t dev_key[16], const uint8_t net_key[16], uint16_t net_idx, bool kr, bool ivu, uint32_t iv_index, uint16_t unicast, node_ready_func_t cb, void *user_data) { struct managed_obj_request *req; l_debug(""); req = l_new(struct managed_obj_request, 1); req->node = node_new(uuid); req->ready_cb = cb; req->pending_msg = user_data; req->import = l_new(struct node_import, 1); memcpy(req->import->dev_key, dev_key, 16); memcpy(req->import->net_key, net_key, 16); req->import->net_idx = net_idx; req->import->flags.kr = kr; req->import->flags.ivu = ivu; req->import->iv_index = iv_index; req->import->unicast = unicast; req->type = REQUEST_TYPE_IMPORT; send_managed_objects_request(sender, app_root, req); } void node_create(const char *app_root, const char *sender, const uint8_t *uuid, node_ready_func_t cb, void *user_data) { struct managed_obj_request *req; l_debug(""); req = l_new(struct managed_obj_request, 1); req->node = node_new(uuid); req->ready_cb = cb; req->pending_msg = user_data; req->type = REQUEST_TYPE_CREATE; send_managed_objects_request(sender, app_root, req); } static void build_element_config(void *a, void *b) { struct node_element *ele = a; struct l_dbus_message_builder *builder = b; l_debug("Element %u", ele->idx); l_dbus_message_builder_enter_struct(builder, "ya(qa{sv})"); /* Element index */ l_dbus_message_builder_append_basic(builder, 'y', &ele->idx); l_dbus_message_builder_enter_array(builder, "(qa{sv})"); /* Iterate over models */ l_queue_foreach(ele->models, mesh_model_build_config, builder); l_dbus_message_builder_leave_array(builder); l_dbus_message_builder_leave_struct(builder); } void node_build_attach_reply(struct mesh_node *node, struct l_dbus_message *reply) { struct l_dbus_message_builder *builder; builder = l_dbus_message_builder_new(reply); /* Node object path */ l_dbus_message_builder_append_basic(builder, 'o', node->obj_path); /* Array of element configurations "a*/ l_dbus_message_builder_enter_array(builder, "(ya(qa{sv}))"); l_queue_foreach(node->elements, build_element_config, builder); l_dbus_message_builder_leave_array(builder); l_dbus_message_builder_finalize(builder); l_dbus_message_builder_destroy(builder); } static bool parse_send_options(struct l_dbus_message_iter *itr, struct send_options *opts) { const char *key; struct l_dbus_message_iter var; opts->segmented = false; opts->vendor_id = SIG_VENDOR; while (l_dbus_message_iter_next_entry(itr, &key, &var)) { if (!strcmp(key, "ForceSegmented")) { if (!l_dbus_message_iter_get_variant(&var, "b", &opts->segmented)) return false; } if (!strcmp(key, "Vendor")) { if (!l_dbus_message_iter_get_variant(&var, "q", &opts->vendor_id)) return false; } } return true; } static struct l_dbus_message *send_call(struct l_dbus *dbus, struct l_dbus_message *msg, void *user_data) { struct mesh_node *node = user_data; const char *sender, *ele_path; struct l_dbus_message_iter dict, iter_data; struct send_options opts; struct node_element *ele; uint16_t dst, app_idx, net_idx, src; uint8_t *data; uint32_t len; l_debug("Send"); sender = l_dbus_message_get_sender(msg); if (strcmp(sender, node->owner)) return dbus_error(msg, MESH_ERROR_NOT_AUTHORIZED, NULL); if (!l_dbus_message_get_arguments(msg, "oqqa{sv}ay", &ele_path, &dst, &app_idx, &dict, &iter_data)) return dbus_error(msg, MESH_ERROR_INVALID_ARGS, NULL); ele = l_queue_find(node->elements, match_element_path, ele_path); if (!ele) return dbus_error(msg, MESH_ERROR_NOT_FOUND, "Element not found"); if (!parse_send_options(&dict, &opts)) return dbus_error(msg, MESH_ERROR_INVALID_ARGS, NULL); src = node_get_primary(node) + ele->idx; if (!l_dbus_message_iter_get_fixed_array(&iter_data, &data, &len) || !len || len > MAX_MSG_LEN) return dbus_error(msg, MESH_ERROR_INVALID_ARGS, "Incorrect data"); if (app_idx & ~APP_IDX_MASK) return dbus_error(msg, MESH_ERROR_INVALID_ARGS, "Invalid key index"); net_idx = appkey_net_idx(node_get_net(node), app_idx); if (net_idx == NET_IDX_INVALID) return dbus_error(msg, MESH_ERROR_INVALID_ARGS, "Key not found"); if (!mesh_model_send(node, src, dst, app_idx, net_idx, DEFAULT_TTL, opts.segmented, len, data)) return dbus_error(msg, MESH_ERROR_FAILED, NULL); return l_dbus_message_new_method_return(msg); } static struct l_dbus_message *dev_key_send_call(struct l_dbus *dbus, struct l_dbus_message *msg, void *user_data) { struct mesh_node *node = user_data; const char *sender, *ele_path; struct l_dbus_message_iter iter_data, dict; struct send_options opts; struct node_element *ele; uint16_t dst, app_idx, net_idx, src; bool remote; uint8_t *data; uint32_t len; l_debug("DevKeySend"); sender = l_dbus_message_get_sender(msg); if (strcmp(sender, node->owner)) return dbus_error(msg, MESH_ERROR_NOT_AUTHORIZED, NULL); if (!l_dbus_message_get_arguments(msg, "oqbqa{sv}ay", &ele_path, &dst, &remote, &net_idx, &dict, &iter_data)) return dbus_error(msg, MESH_ERROR_INVALID_ARGS, NULL); /* Loopbacks to local servers must use *remote* addressing */ if (!remote && mesh_net_is_local_address(node->net, dst, 1)) return dbus_error(msg, MESH_ERROR_INVALID_ARGS, NULL); ele = l_queue_find(node->elements, match_element_path, ele_path); if (!ele) return dbus_error(msg, MESH_ERROR_NOT_FOUND, "Element not found"); if (!parse_send_options(&dict, &opts)) return dbus_error(msg, MESH_ERROR_INVALID_ARGS, NULL); src = node_get_primary(node) + ele->idx; if (!l_dbus_message_iter_get_fixed_array(&iter_data, &data, &len) || !len || len > MAX_MSG_LEN) return dbus_error(msg, MESH_ERROR_INVALID_ARGS, "Incorrect data"); app_idx = remote ? APP_IDX_DEV_REMOTE : APP_IDX_DEV_LOCAL; if (!mesh_model_send(node, src, dst, app_idx, net_idx, DEFAULT_TTL, opts.segmented, len, data)) return dbus_error(msg, MESH_ERROR_NOT_FOUND, NULL); return l_dbus_message_new_method_return(msg); } static struct l_dbus_message *add_netkey_call(struct l_dbus *dbus, struct l_dbus_message *msg, void *user_data) { struct mesh_node *node = user_data; const char *sender, *ele_path; struct node_element *ele; uint16_t dst, sub_idx, net_idx, src; bool update; struct keyring_net_key key; uint8_t data[20]; l_debug("AddNetKey"); sender = l_dbus_message_get_sender(msg); if (strcmp(sender, node->owner)) return dbus_error(msg, MESH_ERROR_NOT_AUTHORIZED, NULL); if (!l_dbus_message_get_arguments(msg, "oqqqb", &ele_path, &dst, &sub_idx, &net_idx, &update)) return dbus_error(msg, MESH_ERROR_INVALID_ARGS, NULL); ele = l_queue_find(node->elements, match_element_path, ele_path); if (!ele) return dbus_error(msg, MESH_ERROR_NOT_FOUND, "Element not found"); src = node_get_primary(node) + ele->idx; if (!keyring_get_net_key(node, sub_idx, &key)) return dbus_error(msg, MESH_ERROR_NOT_FOUND, "NetKey not found"); if (!update) { l_put_be16(OP_NETKEY_ADD, data); if (key.phase != KEY_REFRESH_PHASE_TWO) memcpy(data + 4, key.old_key, 16); else memcpy(data + 4, key.new_key, 16); } else { if (key.phase != KEY_REFRESH_PHASE_ONE) return dbus_error(msg, MESH_ERROR_FAILED, "Cannot update"); l_put_be16(OP_NETKEY_UPDATE, data); memcpy(data + 4, key.new_key, 16); } l_put_le16(sub_idx, &data[2]); if (!mesh_model_send(node, src, dst, APP_IDX_DEV_REMOTE, net_idx, DEFAULT_TTL, false, 20, data)) return dbus_error(msg, MESH_ERROR_NOT_FOUND, NULL); return l_dbus_message_new_method_return(msg); } static struct l_dbus_message *add_appkey_call(struct l_dbus *dbus, struct l_dbus_message *msg, void *user_data) { struct mesh_node *node = user_data; const char *sender, *ele_path; struct node_element *ele; uint16_t dst, app_idx, net_idx, src; bool update; struct keyring_net_key net_key; struct keyring_app_key app_key; uint8_t data[20]; l_debug("AddAppKey"); sender = l_dbus_message_get_sender(msg); if (strcmp(sender, node->owner)) return dbus_error(msg, MESH_ERROR_NOT_AUTHORIZED, NULL); if (!l_dbus_message_get_arguments(msg, "oqqqb", &ele_path, &dst, &app_idx, &net_idx, &update)) return dbus_error(msg, MESH_ERROR_INVALID_ARGS, NULL); ele = l_queue_find(node->elements, match_element_path, ele_path); if (!ele) return dbus_error(msg, MESH_ERROR_NOT_FOUND, "Element not found"); src = node_get_primary(node) + ele->idx; if (!keyring_get_app_key(node, app_idx, &app_key)) return dbus_error(msg, MESH_ERROR_NOT_FOUND, "AppKey not found"); if (!keyring_get_net_key(node, app_key.net_idx, &net_key)) { return dbus_error(msg, MESH_ERROR_NOT_FOUND, "Bound NetKey not found"); } if (!update) { data[0] = OP_APPKEY_ADD; if (net_key.phase != KEY_REFRESH_PHASE_TWO) memcpy(data + 4, app_key.old_key, 16); else memcpy(data + 4, app_key.new_key, 16); } else { if (net_key.phase != KEY_REFRESH_PHASE_ONE) return dbus_error(msg, MESH_ERROR_FAILED, "Cannot update"); data[0] = OP_APPKEY_UPDATE; memcpy(data + 4, app_key.new_key, 16); } /* Pack bound NetKey and AppKey into 3 octets */ data[1] = app_key.net_idx; data[2] = ((app_key.net_idx >> 8) & 0xf) | ((app_idx << 4) & 0xf0); data[3] = app_idx >> 4; if (!mesh_model_send(node, src, dst, APP_IDX_DEV_REMOTE, net_idx, DEFAULT_TTL, false, 20, data)) return dbus_error(msg, MESH_ERROR_NOT_FOUND, NULL); return l_dbus_message_new_method_return(msg); } static struct l_dbus_message *publish_call(struct l_dbus *dbus, struct l_dbus_message *msg, void *user_data) { struct mesh_node *node = user_data; const char *sender, *ele_path; struct l_dbus_message_iter iter_data, dict; uint16_t mod_id, src; struct send_options opts; struct node_element *ele; uint8_t *data; uint32_t len, id; int result; l_debug("Publish"); sender = l_dbus_message_get_sender(msg); if (strcmp(sender, node->owner)) return dbus_error(msg, MESH_ERROR_NOT_AUTHORIZED, NULL); if (!l_dbus_message_get_arguments(msg, "oqa{sv}ay", &ele_path, &mod_id, &dict, &iter_data)) return dbus_error(msg, MESH_ERROR_INVALID_ARGS, NULL); ele = l_queue_find(node->elements, match_element_path, ele_path); if (!ele) return dbus_error(msg, MESH_ERROR_NOT_FOUND, "Element not found"); if (!parse_send_options(&dict, &opts)) return dbus_error(msg, MESH_ERROR_INVALID_ARGS, NULL); src = node_get_primary(node) + ele->idx; if (!l_dbus_message_iter_get_fixed_array(&iter_data, &data, &len) || !len || len > MAX_MSG_LEN) return dbus_error(msg, MESH_ERROR_INVALID_ARGS, "Incorrect data"); id = SET_ID(opts.vendor_id, mod_id); result = mesh_model_publish(node, id, src, opts.segmented, len, data); if (result != MESH_ERROR_NONE) return dbus_error(msg, result, NULL); return l_dbus_message_new_method_return(msg); } static bool features_getter(struct l_dbus *dbus, struct l_dbus_message *msg, struct l_dbus_message_builder *builder, void *user_data) { struct mesh_node *node = user_data; uint8_t friend = node_friend_mode_get(node); uint8_t lpn = node_lpn_mode_get(node); uint8_t proxy = node_proxy_mode_get(node); uint8_t count; uint16_t interval; uint8_t relay = node_relay_mode_get(node, &count, &interval); l_dbus_message_builder_enter_array(builder, "{sv}"); if (friend != MESH_MODE_UNSUPPORTED) dbus_append_dict_entry_basic(builder, "Friend", "b", &friend); if (lpn != MESH_MODE_UNSUPPORTED) dbus_append_dict_entry_basic(builder, "LowPower", "b", &lpn); if (proxy != MESH_MODE_UNSUPPORTED) dbus_append_dict_entry_basic(builder, "Proxy", "b", &proxy); if (relay != MESH_MODE_UNSUPPORTED) dbus_append_dict_entry_basic(builder, "Relay", "b", &relay); l_dbus_message_builder_leave_array(builder); return true; } static bool beacon_getter(struct l_dbus *dbus, struct l_dbus_message *msg, struct l_dbus_message_builder *builder, void *user_data) { struct mesh_node *node = user_data; bool beacon_mode = node_beacon_mode_get(node) == MESH_MODE_ENABLED; l_dbus_message_builder_append_basic(builder, 'b', &beacon_mode); return true; } static bool ivupdate_getter(struct l_dbus *dbus, struct l_dbus_message *msg, struct l_dbus_message_builder *builder, void *user_data) { struct mesh_node *node = user_data; struct mesh_net *net = node_get_net(node); uint8_t flags; uint32_t iv_index; bool ivu; mesh_net_get_snb_state(net, &flags, &iv_index); ivu = flags & IV_INDEX_UPDATE; l_dbus_message_builder_append_basic(builder, 'b', &ivu); return true; } static bool ivindex_getter(struct l_dbus *dbus, struct l_dbus_message *msg, struct l_dbus_message_builder *builder, void *user_data) { struct mesh_node *node = user_data; struct mesh_net *net = node_get_net(node); uint8_t flags; uint32_t iv_index; mesh_net_get_snb_state(net, &flags, &iv_index); l_dbus_message_builder_append_basic(builder, 'u', &iv_index); return true; } static bool seq_num_getter(struct l_dbus *dbus, struct l_dbus_message *msg, struct l_dbus_message_builder *builder, void *user_data) { struct mesh_node *node = user_data; struct mesh_net *net = node_get_net(node); uint32_t seq_nr = mesh_net_get_seq_num(net); l_dbus_message_builder_append_basic(builder, 'u', &seq_nr); return true; } static bool lastheard_getter(struct l_dbus *dbus, struct l_dbus_message *msg, struct l_dbus_message_builder *builder, void *user_data) { struct mesh_node *node = user_data; struct mesh_net *net = node_get_net(node); struct timeval now; uint32_t last_heard; gettimeofday(&now, NULL); last_heard = now.tv_sec - mesh_net_get_instant(net); l_dbus_message_builder_append_basic(builder, 'u', &last_heard); return true; } static bool addresses_getter(struct l_dbus *dbus, struct l_dbus_message *msg, struct l_dbus_message_builder *builder, void *user_data) { struct mesh_node *node = user_data; const struct l_queue_entry *entry; l_dbus_message_builder_enter_array(builder, "q"); entry = l_queue_get_entries(node->elements); for (; entry; entry = entry->next) { const struct node_element *ele = entry->data; uint16_t address = node->primary + ele->idx; l_dbus_message_builder_append_basic(builder, 'q', &address); } l_dbus_message_builder_leave_array(builder); return true; } static void setup_node_interface(struct l_dbus_interface *iface) { l_dbus_interface_method(iface, "Send", 0, send_call, "", "oqqa{sv}ay", "element_path", "destination", "key_index", "options", "data"); l_dbus_interface_method(iface, "DevKeySend", 0, dev_key_send_call, "", "oqbqa{sv}ay", "element_path", "destination", "remote", "net_index", "options", "data"); l_dbus_interface_method(iface, "AddNetKey", 0, add_netkey_call, "", "oqqqb", "element_path", "destination", "subnet_index", "net_index", "update"); l_dbus_interface_method(iface, "AddAppKey", 0, add_appkey_call, "", "oqqqb", "element_path", "destination", "app_index", "net_index", "update"); l_dbus_interface_method(iface, "Publish", 0, publish_call, "", "oqa{sv}ay", "element_path", "model_id", "options", "data"); l_dbus_interface_property(iface, "Features", 0, "a{sv}", features_getter, NULL); l_dbus_interface_property(iface, "Beacon", 0, "b", beacon_getter, NULL); l_dbus_interface_property(iface, "IvUpdate", 0, "b", ivupdate_getter, NULL); l_dbus_interface_property(iface, "IvIndex", 0, "u", ivindex_getter, NULL); l_dbus_interface_property(iface, "SequenceNumber", 0, "u", seq_num_getter, NULL); l_dbus_interface_property(iface, "SecondsSinceLastHeard", 0, "u", lastheard_getter, NULL); l_dbus_interface_property(iface, "Addresses", 0, "aq", addresses_getter, NULL); } void node_property_changed(struct mesh_node *node, const char *property) { struct l_dbus *bus = dbus_get_bus(); if (bus && node->obj_path) l_dbus_property_changed(dbus_get_bus(), node->obj_path, MESH_NODE_INTERFACE, property); } bool node_dbus_init(struct l_dbus *bus) { if (!l_dbus_register_interface(bus, MESH_NODE_INTERFACE, setup_node_interface, NULL, false)) { l_info("Unable to register %s interface", MESH_NODE_INTERFACE); return false; } return true; } const char *node_get_owner(struct mesh_node *node) { return node->owner; } const char *node_get_element_path(struct mesh_node *node, uint8_t ele_idx) { struct node_element *ele; ele = l_queue_find(node->elements, match_element_idx, L_UINT_TO_PTR(ele_idx)); if (!ele) return NULL; return ele->path; } bool node_add_pending_local(struct mesh_node *node, void *prov_node_info) { struct mesh_prov_node_info *info = prov_node_info; bool kr = !!(info->flags & PROV_FLAG_KR); bool ivu = !!(info->flags & PROV_FLAG_IVU); return add_local_node(node, info->unicast, kr, ivu, info->iv_index, info->device_key, info->net_index, info->net_key); } struct mesh_config *node_config_get(struct mesh_node *node) { return node->cfg; } const char *node_get_storage_dir(struct mesh_node *node) { return node->storage_dir; } const char *node_get_app_path(struct mesh_node *node) { if (!node) return NULL; return node->app_path; } struct mesh_net *node_get_net(struct mesh_node *node) { return node->net; } struct mesh_agent *node_get_agent(struct mesh_node *node) { return node->agent; } bool node_load_from_storage(const char *storage_dir) { return mesh_config_load_nodes(storage_dir, init_from_storage, NULL); } /* * This is called for a new node that: * - has been created as a result of successful completion of Join() * or Create() or Import() methods * and * - has been confirmed via successful token delivery to the application * * After a node has been created, the information gathered during initial * GetManagedObjects() call is cleared. The subsequent call to Attach() would * verify node's integrity and re-initialize node's D-Bus resources. */ void node_finalize_new_node(struct mesh_node *node, struct mesh_io *io) { if (!node) return; free_node_dbus_resources(node); mesh_agent_remove(node->agent); node->agent = NULL; node->busy = false; /* Register callback for the node's io */ attach_io(node, io); } bluez-5.82/mesh/PaxHeaders/mesh-io-mgmt.c0000644000000000000000000000005014772767672015302 xustar0020 atime=1743515579 20 ctime=1743591282 bluez-5.82/mesh/mesh-io-mgmt.c0000644000000000000000000004142714772767672014773 0ustar00rootroot// SPDX-License-Identifier: LGPL-2.1-or-later /* * * BlueZ - Bluetooth protocol stack for Linux * * Copyright (C) 2020 Intel Corporation. All rights reserved. * * */ #ifdef HAVE_CONFIG_H #include #endif #include #include #include #include #include #include #include "monitor/bt.h" #include "lib/bluetooth.h" #include "lib/bluetooth.h" #include "lib/mgmt.h" #include "src/shared/mgmt.h" #include "mesh/mesh-defs.h" #include "mesh/util.h" #include "mesh/mesh-mgmt.h" #include "mesh/mesh-io.h" #include "mesh/mesh-io-api.h" #include "mesh/mesh-io-mgmt.h" struct mesh_io_private { struct mesh_io *io; void *user_data; struct l_timeout *tx_timeout; struct l_timeout *dup_timeout; struct l_queue *dup_filters; struct l_queue *tx_pkts; struct tx_pkt *tx; unsigned int tx_id; unsigned int rx_id; uint16_t send_idx; uint16_t interval; uint8_t handle; bool sending; bool active; }; struct process_data { struct mesh_io_private *pvt; const uint8_t *data; uint8_t len; struct mesh_io_recv_info info; }; struct tx_pkt { struct mesh_io_send_info info; bool delete; uint8_t len; uint8_t pkt[30]; }; struct tx_pattern { const uint8_t *data; uint8_t len; }; #define DUP_FILTER_TIME 1000 /* Accept one instance of unique message a second */ struct dup_filter { uint64_t data; uint32_t instant; uint8_t addr[6]; } __packed; static const uint8_t zero_addr[] = {0, 0, 0, 0, 0, 0}; static struct mesh_io_private *pvt; static uint32_t get_instant(void) { struct timeval tm; uint32_t instant; gettimeofday(&tm, NULL); instant = tm.tv_sec * 1000; instant += tm.tv_usec / 1000; return instant; } static uint32_t instant_remaining_ms(uint32_t instant) { instant -= get_instant(); return instant; } static bool find_by_addr(const void *a, const void *b) { const struct dup_filter *filter = a; return !memcmp(filter->addr, b, 6); } static bool find_by_adv(const void *a, const void *b) { const struct dup_filter *filter = a; uint64_t data = l_get_be64(b); return !memcmp(filter->addr, zero_addr, 6) && filter->data == data; } static void filter_timeout(struct l_timeout *timeout, void *user_data) { struct dup_filter *filter; uint32_t instant, delta; if (!pvt) goto done; instant = get_instant(); filter = l_queue_peek_tail(pvt->dup_filters); while (filter) { delta = instant - filter->instant; if (delta >= DUP_FILTER_TIME) { l_queue_remove(pvt->dup_filters, filter); l_free(filter); } else { l_timeout_modify(timeout, 1); return; } filter = l_queue_peek_tail(pvt->dup_filters); } done: l_timeout_remove(timeout); pvt->dup_timeout = NULL; } /* Ignore consequtive duplicate advertisements within timeout period */ static bool filter_dups(const uint8_t *addr, const uint8_t *adv, uint32_t instant) { struct dup_filter *filter; uint32_t instant_delta; uint64_t data = l_get_be64(adv); if (!addr) addr = zero_addr; if (adv[1] == MESH_AD_TYPE_PROVISION) { filter = l_queue_find(pvt->dup_filters, find_by_adv, adv); if (!filter && addr != zero_addr) return false; l_queue_remove(pvt->dup_filters, filter); } else { filter = l_queue_remove_if(pvt->dup_filters, find_by_addr, addr); } if (!filter) { filter = l_new(struct dup_filter, 1); memcpy(filter->addr, addr, 6); } /* Start filter expiration timer */ if (!l_queue_length(pvt->dup_filters)) pvt->dup_timeout = l_timeout_create(1, filter_timeout, NULL, NULL); l_queue_push_head(pvt->dup_filters, filter); instant_delta = instant - filter->instant; if (instant_delta >= DUP_FILTER_TIME || data != filter->data) { filter->instant = instant; filter->data = data; return false; } return true; } static void process_rx_callbacks(void *v_reg, void *v_rx) { struct mesh_io_reg *rx_reg = v_reg; struct process_data *rx = v_rx; if (!memcmp(rx->data, rx_reg->filter, rx_reg->len)) rx_reg->cb(rx_reg->user_data, &rx->info, rx->data, rx->len); } static void process_rx(uint16_t index, struct mesh_io_private *pvt, int8_t rssi, uint32_t instant, const uint8_t *addr, const uint8_t *data, uint8_t len) { struct process_data rx = { .pvt = pvt, .data = data, .len = len, .info.instant = instant, .info.addr = addr, .info.chan = 7, .info.rssi = rssi, }; /* Accept all traffic except beacons from any controller */ if (index != pvt->send_idx && data[0] == MESH_AD_TYPE_BEACON) return; print_packet("RX", data, len); l_queue_foreach(pvt->io->rx_regs, process_rx_callbacks, &rx); } static void send_cmplt(uint16_t index, uint16_t length, const void *param, void *user_data) { /* print_packet("Mesh Send Complete", param, length); */ } static void event_device_found(uint16_t index, uint16_t length, const void *param, void *user_data) { const struct mgmt_ev_mesh_device_found *ev = param; struct mesh_io_private *pvt = user_data; const uint8_t *adv; const uint8_t *addr; uint32_t instant; uint16_t adv_len; uint16_t len = 0; if (ev->addr.type < 1 || ev->addr.type > 2) return; instant = get_instant(); adv = ev->eir; adv_len = ev->eir_len; addr = ev->addr.bdaddr.b; if (filter_dups(addr, adv, instant)) return; while (len < adv_len - 1) { uint8_t field_len = adv[0]; /* Check for the end of advertising data */ if (field_len == 0) break; len += field_len + 1; /* Do not continue data parsing if got incorrect length */ if (len > adv_len) break; if (adv[1] >= MESH_AD_TYPE_PROVISION && adv[1] <= MESH_AD_TYPE_BEACON) process_rx(index, pvt, ev->rssi, instant, addr, adv + 1, adv[0]); adv += field_len + 1; } } static bool simple_match(const void *a, const void *b) { return a == b; } static bool find_by_ad_type(const void *a, const void *b) { const struct tx_pkt *tx = a; uint8_t ad_type = L_PTR_TO_UINT(b); return !ad_type || ad_type == tx->pkt[0]; } static bool find_by_pattern(const void *a, const void *b) { const struct tx_pkt *tx = a; const struct tx_pattern *pattern = b; if (tx->len < pattern->len) return false; return (!memcmp(tx->pkt, pattern->data, pattern->len)); } static bool find_active(const void *a, const void *b) { const struct mesh_io_reg *rx_reg = a; /* Mesh specific AD types do *not* require active scanning, * so do not turn on Active Scanning on their account. */ if (rx_reg->filter[0] < MESH_AD_TYPE_PROVISION || rx_reg->filter[0] > MESH_AD_TYPE_BEACON) return true; return false; } static void mesh_up(uint8_t status, uint16_t length, const void *param, void *user_data) { int index = L_PTR_TO_UINT(user_data); l_debug("HCI%d Mesh up status: %d", index, status); } static void le_up(uint8_t status, uint16_t length, const void *param, void *user_data) { int index = L_PTR_TO_UINT(user_data); l_debug("HCI%d LE up status: %d", index, status); } static void ctl_up(uint8_t status, uint16_t length, const void *param, void *user_data) { int index = L_PTR_TO_UINT(user_data); uint16_t len; struct mgmt_cp_set_mesh *mesh; uint8_t mesh_ad_types[] = { MESH_AD_TYPE_NETWORK, MESH_AD_TYPE_BEACON, MESH_AD_TYPE_PROVISION }; l_debug("HCI%d is up status: %d", index, status); if (status) return; len = sizeof(struct mgmt_cp_set_mesh) + sizeof(mesh_ad_types); mesh = l_malloc(len); mesh->enable = 1; mesh->window = L_CPU_TO_LE16(0x1000); mesh->period = L_CPU_TO_LE16(0x1000); mesh->num_ad_types = sizeof(mesh_ad_types); memcpy(mesh->ad_types, mesh_ad_types, sizeof(mesh_ad_types)); pvt->rx_id = mesh_mgmt_register(MGMT_EV_MESH_DEVICE_FOUND, MGMT_INDEX_NONE, event_device_found, pvt, NULL); pvt->tx_id = mesh_mgmt_register(MGMT_EV_MESH_PACKET_CMPLT, index, send_cmplt, pvt, NULL); mesh_mgmt_send(MGMT_OP_SET_MESH_RECEIVER, index, len, mesh, mesh_up, L_UINT_TO_PTR(index), NULL); l_debug("done %d mesh startup", index); l_free(mesh); if (pvt->send_idx == MGMT_INDEX_NONE) { pvt->send_idx = index; if (pvt && pvt->io && pvt->io->ready) { pvt->io->ready(pvt->io->user_data, true); pvt->io->ready = NULL; } } } static void read_info_cb(uint8_t status, uint16_t length, const void *param, void *user_data) { unsigned char le[] = { 0x01 }; int index = L_PTR_TO_UINT(user_data); const struct mgmt_rp_read_info *rp = param; uint32_t current_settings, supported_settings; l_debug("hci %u status 0x%02x", index, status); if (!pvt) return; if (status != MGMT_STATUS_SUCCESS) { l_error("Failed to read info for hci index %u: %s (0x%02x)", index, mgmt_errstr(status), status); return; } if (length < sizeof(*rp)) { l_error("Read info response too short"); return; } current_settings = btohl(rp->current_settings); supported_settings = btohl(rp->supported_settings); if (!(supported_settings & MGMT_SETTING_LE)) { l_info("Controller hci %u does not support LE", index); return; } if (!(current_settings & MGMT_SETTING_POWERED)) { unsigned char power[] = { 0x01 }; /* TODO: Initialize this HCI controller */ l_info("Controller hci %u not in use", index); mesh_mgmt_send(MGMT_OP_SET_LE, index, sizeof(le), &le, le_up, L_UINT_TO_PTR(index), NULL); mesh_mgmt_send(MGMT_OP_SET_POWERED, index, sizeof(power), &power, ctl_up, L_UINT_TO_PTR(index), NULL); } else { l_info("Controller hci %u already in use (%x)", index, current_settings); /* Share this controller with bluetoothd */ mesh_mgmt_send(MGMT_OP_SET_LE, index, sizeof(le), &le, ctl_up, L_UINT_TO_PTR(index), NULL); } } static bool dev_init(struct mesh_io *io, void *opts, void *user_data) { uint16_t index = *(int *)opts; if (!io || pvt) return false; pvt = l_new(struct mesh_io_private, 1); pvt->send_idx = MGMT_INDEX_NONE; mesh_mgmt_send(MGMT_OP_READ_INFO, index, 0, NULL, read_info_cb, L_UINT_TO_PTR(index), NULL); pvt->dup_filters = l_queue_new(); pvt->tx_pkts = l_queue_new(); pvt->io = io; io->pvt = pvt; return true; } static bool dev_destroy(struct mesh_io *io) { unsigned char param[] = { 0x00 }; if (io->pvt != pvt) return true; mesh_mgmt_send(MGMT_OP_SET_POWERED, io->index, sizeof(param), ¶m, NULL, NULL, NULL); mesh_mgmt_unregister(pvt->rx_id); mesh_mgmt_unregister(pvt->tx_id); l_timeout_remove(pvt->tx_timeout); l_timeout_remove(pvt->dup_timeout); l_queue_destroy(pvt->dup_filters, l_free); l_queue_destroy(pvt->tx_pkts, l_free); io->pvt = NULL; l_free(pvt); pvt = NULL; return true; } static bool dev_caps(struct mesh_io *io, struct mesh_io_caps *caps) { struct mesh_io_private *pvt = io->pvt; if (!pvt || !caps) return false; caps->max_num_filters = 255; caps->window_accuracy = 50; return true; } static void send_cancel(struct mesh_io_private *pvt) { struct mgmt_cp_mesh_send_cancel remove; if (!pvt) return; if (pvt->handle) { remove.handle = pvt->handle; /* l_debug("Cancel TX"); */ mesh_mgmt_send(MGMT_OP_MESH_SEND_CANCEL, pvt->send_idx, sizeof(remove), &remove, NULL, NULL, NULL); } } static void tx_to(struct l_timeout *timeout, void *user_data); static void send_queued(uint8_t status, uint16_t length, const void *param, void *user_data) { struct tx_pkt *tx = user_data; if (status) l_debug("Mesh Send Failed: %d", status); else if (param && length >= 1) pvt->handle = *(uint8_t *) param; if (tx->delete) { l_queue_remove_if(pvt->tx_pkts, simple_match, tx); l_free(tx); pvt->tx = NULL; } } static void send_pkt(struct mesh_io_private *pvt, struct tx_pkt *tx, uint16_t interval) { uint8_t buffer[sizeof(struct mgmt_cp_mesh_send) + tx->len + 1]; struct mgmt_cp_mesh_send *send = (void *) buffer; uint16_t index; size_t len; if (!pvt) return; index = pvt->send_idx; len = sizeof(buffer); memset(send, 0, len); send->addr.type = BDADDR_LE_RANDOM; send->instant = 0; send->delay = 0; send->cnt = 1; send->adv_data_len = tx->len + 1; send->adv_data[0] = tx->len; memcpy(send->adv_data + 1, tx->pkt, tx->len); /* Filter looped back Provision packets */ if (tx->pkt[0] == MESH_AD_TYPE_PROVISION) filter_dups(NULL, send->adv_data, get_instant()); mesh_mgmt_send(MGMT_OP_MESH_SEND, index, len, send, send_queued, tx, NULL); /* print_packet("Mesh Send Start", tx->pkt, tx->len); */ pvt->tx = tx; } static void tx_to(struct l_timeout *timeout, void *user_data) { struct mesh_io_private *pvt = user_data; struct tx_pkt *tx; uint16_t ms; uint8_t count; if (!pvt) return; tx = l_queue_pop_head(pvt->tx_pkts); if (!tx) { l_timeout_remove(timeout); pvt->tx_timeout = NULL; send_cancel(pvt); pvt->tx = NULL; return; } if (tx->info.type == MESH_IO_TIMING_TYPE_GENERAL) { ms = tx->info.u.gen.interval; count = tx->info.u.gen.cnt; if (count != MESH_IO_TX_COUNT_UNLIMITED) tx->info.u.gen.cnt--; } else { ms = 25; count = 1; } tx->delete = !!(count == 1); send_pkt(pvt, tx, ms); if (count == 1) { /* Recalculate wakeup if we are responding to POLL */ tx = l_queue_peek_head(pvt->tx_pkts); if (tx && tx->info.type == MESH_IO_TIMING_TYPE_POLL_RSP) { ms = instant_remaining_ms(tx->info.u.poll_rsp.instant + tx->info.u.poll_rsp.delay); } } else l_queue_push_tail(pvt->tx_pkts, tx); if (timeout) { pvt->tx_timeout = timeout; l_timeout_modify_ms(timeout, ms); } else pvt->tx_timeout = l_timeout_create_ms(ms, tx_to, pvt, NULL); } static void tx_worker(void *user_data) { struct mesh_io_private *pvt = user_data; struct tx_pkt *tx; uint32_t delay; tx = l_queue_peek_head(pvt->tx_pkts); if (!tx) return; switch (tx->info.type) { case MESH_IO_TIMING_TYPE_GENERAL: if (tx->info.u.gen.min_delay == tx->info.u.gen.max_delay) delay = tx->info.u.gen.min_delay; else { l_getrandom(&delay, sizeof(delay)); delay %= tx->info.u.gen.max_delay - tx->info.u.gen.min_delay; delay += tx->info.u.gen.min_delay; } break; case MESH_IO_TIMING_TYPE_POLL: if (tx->info.u.poll.min_delay == tx->info.u.poll.max_delay) delay = tx->info.u.poll.min_delay; else { l_getrandom(&delay, sizeof(delay)); delay %= tx->info.u.poll.max_delay - tx->info.u.poll.min_delay; delay += tx->info.u.poll.min_delay; } break; case MESH_IO_TIMING_TYPE_POLL_RSP: /* Delay until Instant + Delay */ delay = instant_remaining_ms(tx->info.u.poll_rsp.instant + tx->info.u.poll_rsp.delay); if (delay > 255) delay = 0; break; default: return; } if (!delay) tx_to(pvt->tx_timeout, pvt); else if (pvt->tx_timeout) l_timeout_modify_ms(pvt->tx_timeout, delay); else pvt->tx_timeout = l_timeout_create_ms(delay, tx_to, pvt, NULL); } static bool send_tx(struct mesh_io *io, struct mesh_io_send_info *info, const uint8_t *data, uint16_t len) { struct tx_pkt *tx; bool sending = false; if (!info || !data || !len || len > sizeof(tx->pkt)) return false; tx = l_new(struct tx_pkt, 1); memcpy(&tx->info, info, sizeof(tx->info)); memcpy(&tx->pkt, data, len); tx->len = len; if (info->type == MESH_IO_TIMING_TYPE_POLL_RSP) l_queue_push_head(pvt->tx_pkts, tx); else { if (pvt->tx) sending = true; else sending = !l_queue_isempty(pvt->tx_pkts); l_queue_push_tail(pvt->tx_pkts, tx); } if (!sending) { l_timeout_remove(pvt->tx_timeout); pvt->tx_timeout = NULL; l_idle_oneshot(tx_worker, pvt, NULL); } return true; } static bool tx_cancel(struct mesh_io *io, const uint8_t *data, uint8_t len) { struct mesh_io_private *pvt = io->pvt; struct tx_pkt *tx; if (!data) return false; if (len == 1) { do { tx = l_queue_remove_if(pvt->tx_pkts, find_by_ad_type, L_UINT_TO_PTR(data[0])); l_free(tx); if (tx == pvt->tx) pvt->tx = NULL; } while (tx); } else { struct tx_pattern pattern = { .data = data, .len = len }; do { tx = l_queue_remove_if(pvt->tx_pkts, find_by_pattern, &pattern); l_free(tx); if (tx == pvt->tx) pvt->tx = NULL; } while (tx); } if (l_queue_isempty(pvt->tx_pkts)) { send_cancel(pvt); l_timeout_remove(pvt->tx_timeout); pvt->tx_timeout = NULL; } return true; } static bool recv_register(struct mesh_io *io, const uint8_t *filter, uint8_t len, mesh_io_recv_func_t cb, void *user_data) { bool active = false; if (io->pvt != pvt) return false; /* Look for any AD types requiring Active Scanning */ if (l_queue_find(io->rx_regs, find_active, NULL)) active = true; if (pvt->active != active) { pvt->active = active; /* TODO: Request active or passive scanning */ } return true; } static bool recv_deregister(struct mesh_io *io, const uint8_t *filter, uint8_t len) { bool active = false; if (io->pvt != pvt) return false; /* Look for any AD types requiring Active Scanning */ if (l_queue_find(io->rx_regs, find_active, NULL)) active = true; if (active != pvt->active) { pvt->active = active; /* TODO: Request active or passive scanning */ } return true; } const struct mesh_io_api mesh_io_mgmt = { .init = dev_init, .destroy = dev_destroy, .caps = dev_caps, .send = send_tx, .reg = recv_register, .dereg = recv_deregister, .cancel = tx_cancel, }; bluez-5.82/mesh/PaxHeaders/error.h0000644000000000000000000000005014015011623014075 xustar0020 atime=1743516868 20 ctime=1743591281 bluez-5.82/mesh/error.h0000644000000000000000000000110514015011623013553 0ustar00rootroot/* SPDX-License-Identifier: LGPL-2.1-or-later */ /* * * BlueZ - Bluetooth protocol stack for Linux * * Copyright (C) 2018-2019 Intel Corporation. All rights reserved. * * */ /* * Important: Changes in this table must be reflected in the * the entries of error_table[] in dbus.c */ enum mesh_error { MESH_ERROR_NONE, MESH_ERROR_FAILED, MESH_ERROR_NOT_AUTHORIZED, MESH_ERROR_NOT_FOUND, MESH_ERROR_INVALID_ARGS, MESH_ERROR_IN_PROGRESS, MESH_ERROR_BUSY, MESH_ERROR_ALREADY_EXISTS, MESH_ERROR_DOES_NOT_EXIST, MESH_ERROR_CANCELED, MESH_ERROR_NOT_IMPLEMENTED, }; bluez-5.82/mesh/PaxHeaders/agent.h0000644000000000000000000000005014015011623014042 xustar0020 atime=1743516868 20 ctime=1743591282 bluez-5.82/mesh/agent.h0000644000000000000000000000370114015011623013524 0ustar00rootroot/* SPDX-License-Identifier: LGPL-2.1-or-later */ /* * * BlueZ - Bluetooth protocol stack for Linux * * Copyright (C) 2018 Intel Corporation. All rights reserved. * * */ struct mesh_agent; struct mesh_agent_prov_caps { uint32_t uri_hash; uint16_t oob_info; uint16_t output_action; uint16_t input_action; uint8_t pub_type; uint8_t static_type; uint8_t output_size; uint8_t input_size; }; typedef void (*mesh_agent_cb_t) (void *user_data, int err); typedef void (*mesh_agent_key_cb_t) (void *user_data, int err, uint8_t *key, uint32_t len); typedef void (*mesh_agent_number_cb_t) (void *user_data, int err, uint32_t number); void mesh_agent_init(void); void mesh_agent_cleanup(void); struct mesh_agent *mesh_agent_create(const char *path, const char *owner, struct l_dbus_message_iter *properties); void mesh_agent_refresh(struct mesh_agent *agent, mesh_agent_cb_t cb, void *user_data); void mesh_agent_remove(struct mesh_agent *agent); void mesh_agent_cancel(struct mesh_agent *agent); struct mesh_agent_prov_caps *mesh_agent_get_caps(struct mesh_agent *agent); int mesh_agent_display_number(struct mesh_agent *agent, bool initiator, uint8_t action, uint32_t count, mesh_agent_cb_t cb, void *user_data); int mesh_agent_prompt_number(struct mesh_agent *agent, bool initiator, uint8_t action, mesh_agent_number_cb_t cb, void *user_data); int mesh_agent_prompt_alpha(struct mesh_agent *agent, bool initiator, mesh_agent_key_cb_t cb, void *user_data); int mesh_agent_request_static(struct mesh_agent *agent, mesh_agent_key_cb_t cb, void *user_data); int mesh_agent_request_private_key(struct mesh_agent *agent, mesh_agent_key_cb_t cb, void *user_data); int mesh_agent_request_public_key(struct mesh_agent *agent, mesh_agent_key_cb_t cb, void *user_data); int mesh_agent_display_string(struct mesh_agent *agent, const char *str, mesh_agent_cb_t cb, void *user_data); bluez-5.82/mesh/PaxHeaders/mesh.c0000644000000000000000000000005014772767672013733 xustar0020 atime=1743515579 20 ctime=1743591281 bluez-5.82/mesh/mesh.c0000644000000000000000000005476714772767672013437 0ustar00rootroot// SPDX-License-Identifier: LGPL-2.1-or-later /* * * BlueZ - Bluetooth protocol stack for Linux * * Copyright (C) 2018-2019 Intel Corporation. All rights reserved. * * */ #ifdef HAVE_CONFIG_H #include #endif #include #define _GNU_SOURCE #include #include "mesh/mesh-io.h" #include "mesh/node.h" #include "mesh/net.h" #include "mesh/net-keys.h" #include "mesh/provision.h" #include "mesh/model.h" #include "mesh/dbus.h" #include "mesh/error.h" #include "mesh/agent.h" #include "mesh/mesh.h" #include "mesh/mesh-defs.h" /* * The default values for mesh configuration. Can be * overwritten by values from mesh-main.conf */ #define DEFAULT_PROV_TIMEOUT 60 #define DEFAULT_CRPL 100 #define DEFAULT_FRIEND_QUEUE_SZ 32 #define DEFAULT_ALGORITHMS 0x0001 struct scan_filter { uint8_t id; const char *pattern; }; struct bt_mesh { struct mesh_io *io; struct l_queue *filters; prov_rx_cb_t prov_rx; void *prov_data; uint32_t prov_timeout; bool beacon_enabled; bool friend_support; bool relay_support; bool lpn_support; bool proxy_support; uint16_t crpl; uint16_t algorithms; uint16_t req_index; uint8_t friend_queue_sz; uint8_t max_filters; bool initialized; }; struct join_data{ struct l_dbus_message *msg; char *sender; struct mesh_node *node; uint32_t disc_watch; uint8_t *uuid; }; struct mesh_init_request { mesh_ready_func_t cb; void *user_data; }; static struct bt_mesh mesh = { .algorithms = DEFAULT_ALGORITHMS, .prov_timeout = DEFAULT_PROV_TIMEOUT, .beacon_enabled = true, .friend_support = true, .relay_support = true, .lpn_support = false, .proxy_support = false, .crpl = DEFAULT_CRPL, .friend_queue_sz = DEFAULT_FRIEND_QUEUE_SZ, .initialized = false }; /* We allow only one outstanding Join request */ static struct join_data *join_pending; /* Pending method requests */ static struct l_queue *pending_queue; static const char *storage_dir; /* Forward static decalrations */ static void def_attach(struct l_timeout *timeout, void *user_data); static void def_leave(struct l_timeout *timeout, void *user_data); static bool simple_match(const void *a, const void *b) { return a == b; } /* Used for any outbound traffic that doesn't have Friendship Constraints */ /* This includes Beacons, Provisioning and unrestricted Network Traffic */ bool mesh_send_pkt(uint8_t count, uint16_t interval, void *data, uint16_t len) { struct mesh_io_send_info info = { .type = MESH_IO_TIMING_TYPE_GENERAL, .u.gen.cnt = count, .u.gen.interval = interval, .u.gen.max_delay = 0, .u.gen.min_delay = 0, }; return mesh_io_send(mesh.io, &info, data, len); } bool mesh_send_cancel(const uint8_t *filter, uint8_t len) { return mesh_io_send_cancel(mesh.io, filter, len); } static void prov_rx(void *user_data, struct mesh_io_recv_info *info, const uint8_t *data, uint16_t len) { if (user_data != &mesh) return; if (mesh.prov_rx) mesh.prov_rx(mesh.prov_data, data, len); } bool mesh_reg_prov_rx(prov_rx_cb_t cb, void *user_data) { uint8_t prov_filter[] = {MESH_AD_TYPE_PROVISION}; if (mesh.prov_rx && mesh.prov_rx != cb) return false; mesh.prov_rx = cb; mesh.prov_data = user_data; return mesh_io_register_recv_cb(mesh.io, prov_filter, sizeof(prov_filter), prov_rx, &mesh); } void mesh_unreg_prov_rx(prov_rx_cb_t cb) { uint8_t prov_filter[] = {MESH_AD_TYPE_PROVISION}; if (mesh.prov_rx != cb) return; mesh.prov_rx = NULL; mesh.prov_data = NULL; mesh_io_deregister_recv_cb(mesh.io, prov_filter, sizeof(prov_filter)); } static void io_ready_callback(void *user_data, bool result) { struct mesh_init_request *req = user_data; if (mesh.initialized) return; mesh.initialized = true; if (result) node_attach_io_all(mesh.io); req->cb(req->user_data, result); l_free(req); } bool mesh_beacon_enabled(void) { return mesh.beacon_enabled; } bool mesh_relay_supported(void) { return mesh.relay_support; } bool mesh_friendship_supported(void) { return mesh.friend_support; } uint16_t mesh_get_crpl(void) { return mesh.crpl; } uint8_t mesh_get_friend_queue_size(void) { return mesh.friend_queue_sz; } static void parse_settings(const char *mesh_conf_fname) { struct l_settings *settings; char *str; uint32_t value; settings = l_settings_new(); if (!l_settings_load_from_file(settings, mesh_conf_fname)) goto done; str = l_settings_get_string(settings, "General", "Beacon"); if (str) { if (!strcasecmp(str, "true")) mesh.beacon_enabled = true; l_free(str); } str = l_settings_get_string(settings, "General", "Relay"); if (str) { if (!strcasecmp(str, "false")) mesh.relay_support = false; l_free(str); } str = l_settings_get_string(settings, "General", "Friendship"); if (str) { if (!strcasecmp(str, "false")) mesh.friend_support = false; l_free(str); } if (l_settings_get_uint(settings, "General", "CRPL", &value) && value <= 65535) mesh.crpl = value; if (l_settings_get_uint(settings, "General", "FriendQueueSize", &value) && value < 127) mesh.friend_queue_sz = value; if (l_settings_get_uint(settings, "General", "ProvTimeout", &value)) mesh.prov_timeout = value; done: l_settings_free(settings); } bool mesh_init(const char *config_dir, const char *mesh_conf_fname, enum mesh_io_type type, void *opts, mesh_ready_func_t cb, void *user_data) { struct mesh_io_caps caps; struct mesh_init_request *req; if (mesh.io) return true; mesh_model_init(); mesh_agent_init(); /* TODO: read mesh.conf */ mesh.prov_timeout = DEFAULT_PROV_TIMEOUT; mesh.algorithms = DEFAULT_ALGORITHMS; storage_dir = config_dir ? config_dir : MESH_STORAGEDIR; l_info("Loading node configuration from %s", storage_dir); if (!mesh_conf_fname) mesh_conf_fname = CONFIGDIR "/mesh-main.conf"; parse_settings(mesh_conf_fname); if (!node_load_from_storage(storage_dir)) return false; req = l_new(struct mesh_init_request, 1); req->cb = cb; req->user_data = user_data; mesh.io = mesh_io_new(type, opts, io_ready_callback, req); if (!mesh.io) { l_free(req); return false; } l_debug("io %p", mesh.io); mesh_io_get_caps(mesh.io, &caps); mesh.max_filters = caps.max_num_filters; pending_queue = l_queue_new(); return true; } static void pending_request_exit(void *data) { struct l_dbus_message *reply; struct l_dbus_message *msg = data; reply = dbus_error(msg, MESH_ERROR_FAILED, "Failed. Exiting"); l_dbus_send(dbus_get_bus(), reply); l_dbus_message_unref(msg); } static void free_pending_join_call(bool failed) { if (!join_pending) return; if (join_pending->disc_watch) l_dbus_remove_watch(dbus_get_bus(), join_pending->disc_watch); if (failed) node_remove(join_pending->node); l_free(join_pending->sender); l_free(join_pending); join_pending = NULL; } void mesh_cleanup(bool signaled) { struct l_dbus_message *reply; mesh_io_destroy(mesh.io); mesh.io = NULL; if (signaled) return; if (join_pending) { if (join_pending->msg) { reply = dbus_error(join_pending->msg, MESH_ERROR_FAILED, "Failed. Exiting"); l_dbus_send(dbus_get_bus(), reply); l_dbus_message_unref(join_pending->msg); } acceptor_cancel(&mesh); free_pending_join_call(true); } l_queue_destroy(pending_queue, pending_request_exit); mesh_agent_cleanup(); node_cleanup_all(); mesh_model_cleanup(); mesh_net_cleanup(); net_key_cleanup(); l_dbus_object_remove_interface(dbus_get_bus(), BLUEZ_MESH_PATH, MESH_NETWORK_INTERFACE); l_dbus_unregister_interface(dbus_get_bus(), MESH_NETWORK_INTERFACE); } const char *mesh_status_str(uint8_t err) { switch (err) { case MESH_STATUS_SUCCESS: return "Success"; case MESH_STATUS_INVALID_ADDRESS: return "Invalid Address"; case MESH_STATUS_INVALID_MODEL: return "Invalid Model"; case MESH_STATUS_INVALID_APPKEY: return "Invalid AppKey"; case MESH_STATUS_INVALID_NETKEY: return "Invalid NetKey"; case MESH_STATUS_INSUFF_RESOURCES: return "Insufficient Resources"; case MESH_STATUS_IDX_ALREADY_STORED: return "Key Idx Already Stored"; case MESH_STATUS_INVALID_PUB_PARAM: return "Invalid Publish Parameters"; case MESH_STATUS_NOT_SUB_MOD: return "Not a Subscribe Model"; case MESH_STATUS_STORAGE_FAIL: return "Storage Failure"; case MESH_STATUS_FEATURE_NO_SUPPORT: return "Feature Not Supported"; case MESH_STATUS_CANNOT_UPDATE: return "Cannot Update"; case MESH_STATUS_CANNOT_REMOVE: return "Cannot Remove"; case MESH_STATUS_CANNOT_BIND: return "Cannot bind"; case MESH_STATUS_UNABLE_CHANGE_STATE: return "Unable to change state"; case MESH_STATUS_CANNOT_SET: return "Cannot set"; case MESH_STATUS_UNSPECIFIED_ERROR: return "Unspecified error"; case MESH_STATUS_INVALID_BINDING: return "Invalid Binding"; default: return "Unknown"; } } /* This is being called if the app exits unexpectedly */ static void prov_disc_cb(struct l_dbus *bus, void *user_data) { if (!join_pending) return; acceptor_cancel(&mesh); join_pending->disc_watch = 0; free_pending_join_call(true); } const char *mesh_prov_status_str(uint8_t status) { switch (status) { case PROV_ERR_SUCCESS: return "success"; case PROV_ERR_INVALID_PDU: case PROV_ERR_INVALID_FORMAT: case PROV_ERR_UNEXPECTED_PDU: return "bad-pdu"; case PROV_ERR_CONFIRM_FAILED: return "confirmation-failed"; case PROV_ERR_INSUF_RESOURCE: return "out-of-resources"; case PROV_ERR_DECRYPT_FAILED: return "decryption-error"; case PROV_ERR_CANT_ASSIGN_ADDR: return "cannot-assign-addresses"; case PROV_ERR_TIMEOUT: return "timeout"; case PROV_ERR_UNEXPECTED_ERR: default: return "unexpected-error"; } } static void send_join_failed(const char *owner, const char *path, uint8_t status) { struct l_dbus_message *msg; struct l_dbus *dbus = dbus_get_bus(); msg = l_dbus_message_new_method_call(dbus, owner, path, MESH_APPLICATION_INTERFACE, "JoinFailed"); l_dbus_message_set_arguments(msg, "s", mesh_prov_status_str(status)); l_dbus_send(dbus_get_bus(), msg); free_pending_join_call(true); } static void prov_join_complete_reply_cb(struct l_dbus_message *msg, void *user_data) { bool failed = false; if (!msg || l_dbus_message_is_error(msg)) failed = true; if (!failed) node_finalize_new_node(join_pending->node, mesh.io); free_pending_join_call(failed); } static bool prov_complete_cb(void *user_data, uint8_t status, struct mesh_prov_node_info *info) { struct l_dbus *dbus = dbus_get_bus(); struct l_dbus_message *msg; const char *owner; const char *path; const uint8_t *token; l_debug("Provisioning complete %s", mesh_prov_status_str(status)); if (!join_pending) return false; owner = join_pending->sender; path = node_get_app_path(join_pending->node); if (status == PROV_ERR_SUCCESS && !node_add_pending_local(join_pending->node, info)) status = PROV_ERR_UNEXPECTED_ERR; if (status != PROV_ERR_SUCCESS) { send_join_failed(owner, path, status); return false; } token = node_get_token(join_pending->node); l_debug("Calling JoinComplete (prov)"); msg = l_dbus_message_new_method_call(dbus, owner, path, MESH_APPLICATION_INTERFACE, "JoinComplete"); l_dbus_message_set_arguments(msg, "t", l_get_be64(token)); dbus_send_with_timeout(dbus, msg, prov_join_complete_reply_cb, NULL, NULL, DEFAULT_DBUS_TIMEOUT); return true; } static void node_init_cb(struct mesh_node *node, struct mesh_agent *agent) { struct l_dbus_message *reply; uint8_t num_ele; bool is_error = false; struct l_dbus *dbus = dbus_get_bus(); if (!node) { reply = dbus_error(join_pending->msg, MESH_ERROR_FAILED, "Failed to create node from application"); is_error = true; goto done; } join_pending->node = node; num_ele = node_get_num_elements(node); if (!acceptor_start(num_ele, join_pending->uuid, mesh.algorithms, mesh.prov_timeout, agent, prov_complete_cb, &mesh)) { reply = dbus_error(join_pending->msg, MESH_ERROR_FAILED, "Failed to start provisioning acceptor"); is_error = true; } else reply = l_dbus_message_new_method_return(join_pending->msg); done: l_dbus_send(dbus, reply); l_dbus_message_unref(join_pending->msg); join_pending->msg = NULL; if (is_error) free_pending_join_call(true); else /* Setup disconnect watch */ join_pending->disc_watch = l_dbus_add_disconnect_watch(dbus, join_pending->sender, prov_disc_cb, NULL, NULL); } static struct l_dbus_message *join_network_call(struct l_dbus *dbus, struct l_dbus_message *msg, void *user_data) { const char *app_path, *sender; struct l_dbus_message_iter iter; uint32_t n; l_debug("Join network request"); if (join_pending) return dbus_error(msg, MESH_ERROR_BUSY, "Provisioning in progress"); if (!l_dbus_message_get_arguments(msg, "oay", &app_path, &iter)) return dbus_error(msg, MESH_ERROR_INVALID_ARGS, NULL); join_pending = l_new(struct join_data, 1); if (!l_dbus_message_iter_get_fixed_array(&iter, &join_pending->uuid, &n) || n != 16 || !l_uuid_is_valid(join_pending->uuid)) { l_free(join_pending); join_pending = NULL; return dbus_error(msg, MESH_ERROR_INVALID_ARGS, "Bad device UUID"); } if (node_find_by_uuid(join_pending->uuid)) { l_free(join_pending); join_pending = NULL; return dbus_error(msg, MESH_ERROR_ALREADY_EXISTS, "Node already exists"); } sender = l_dbus_message_get_sender(msg); join_pending->sender = l_strdup(sender); join_pending->msg = l_dbus_message_ref(msg); /* Try to create a temporary node */ node_join(app_path, sender, join_pending->uuid, node_init_cb); return NULL; } static struct l_dbus_message *cancel_join_call(struct l_dbus *dbus, struct l_dbus_message *msg, void *user_data) { struct l_dbus_message *reply; l_debug("Cancel Join"); if (!join_pending) return dbus_error(msg, MESH_ERROR_DOES_NOT_EXIST, "No join in progress"); acceptor_cancel(&mesh); /* Return error to the original Join call */ if (join_pending->msg) { reply = dbus_error(join_pending->msg, MESH_ERROR_FAILED, NULL); l_dbus_send(dbus_get_bus(), reply); l_dbus_message_unref(join_pending->msg); } reply = l_dbus_message_new_method_return(msg); l_dbus_message_set_arguments(reply, ""); free_pending_join_call(true); return reply; } static void attach_ready_cb(void *user_data, int status, struct mesh_node *node) { struct l_dbus_message *reply; struct l_dbus_message *pending_msg; pending_msg = l_queue_remove_if(pending_queue, simple_match, user_data); if (!pending_msg) return; if (status == MESH_ERROR_NONE) { reply = l_dbus_message_new_method_return(pending_msg); node_build_attach_reply(node, reply); } else reply = dbus_error(pending_msg, status, "Attach failed"); l_dbus_send(dbus_get_bus(), reply); l_dbus_message_unref(pending_msg); } static struct l_dbus_message *attach_call(struct l_dbus *dbus, struct l_dbus_message *msg, void *user_data) { uint64_t token; const char *app_path, *sender; struct l_dbus_message *pending_msg; struct mesh_node *node; l_debug("Attach"); if (!l_dbus_message_get_arguments(msg, "ot", &app_path, &token)) return dbus_error(msg, MESH_ERROR_INVALID_ARGS, NULL); node = node_find_by_token(token); if (!node) return dbus_error(msg, MESH_ERROR_NOT_FOUND, "Attach failed"); if (node_is_busy(node)) { if (user_data) return dbus_error(msg, MESH_ERROR_BUSY, NULL); /* Try once more in 1 second */ l_timeout_create(1, def_attach, l_dbus_message_ref(msg), NULL); return NULL; } sender = l_dbus_message_get_sender(msg); pending_msg = l_dbus_message_ref(msg); l_queue_push_tail(pending_queue, pending_msg); node_attach(app_path, sender, token, attach_ready_cb, pending_msg); return NULL; } static void def_attach(struct l_timeout *timeout, void *user_data) { struct l_dbus *dbus = dbus_get_bus(); struct l_dbus_message *msg = user_data; struct l_dbus_message *reply; l_timeout_remove(timeout); reply = attach_call(dbus, msg, (void *) true); l_dbus_send(dbus, reply); l_dbus_message_unref(msg); } static struct l_dbus_message *leave_call(struct l_dbus *dbus, struct l_dbus_message *msg, void *user_data) { uint64_t token; struct mesh_node *node; l_debug("Leave"); if (!l_dbus_message_get_arguments(msg, "t", &token)) return dbus_error(msg, MESH_ERROR_INVALID_ARGS, NULL); node = node_find_by_token(token); if (!node) return dbus_error(msg, MESH_ERROR_NOT_FOUND, NULL); if (node_is_busy(node)) { if (user_data) return dbus_error(msg, MESH_ERROR_BUSY, NULL); /* Try once more in 1 second */ l_timeout_create(1, def_leave, l_dbus_message_ref(msg), NULL); return NULL; } node_remove(node); return l_dbus_message_new_method_return(msg); } static void def_leave(struct l_timeout *timeout, void *user_data) { struct l_dbus *dbus = dbus_get_bus(); struct l_dbus_message *msg = user_data; struct l_dbus_message *reply; l_timeout_remove(timeout); reply = leave_call(dbus, msg, (void *) true); l_dbus_send(dbus, reply); l_dbus_message_unref(msg); } static void create_join_complete_reply_cb(struct l_dbus_message *msg, void *user_data) { struct mesh_node *node = user_data; if (!msg || l_dbus_message_is_error(msg)) { node_remove(node); return; } node_finalize_new_node(node, mesh.io); } static void create_node_ready_cb(void *user_data, int status, struct mesh_node *node) { struct l_dbus *dbus = dbus_get_bus(); struct l_dbus_message *reply; struct l_dbus_message *pending_msg; struct l_dbus_message *msg; const char *owner; const char *path; const uint8_t *token; pending_msg = l_queue_remove_if(pending_queue, simple_match, user_data); if (!pending_msg) return; if (status != MESH_ERROR_NONE) { reply = dbus_error(pending_msg, status, NULL); l_dbus_send(dbus_get_bus(), reply); l_dbus_message_unref(pending_msg); return; } reply = l_dbus_message_new_method_return(pending_msg); l_dbus_send(dbus, reply); owner = l_dbus_message_get_sender(pending_msg); path = node_get_app_path(node); token = node_get_token(node); l_debug("Calling JoinComplete (create)"); msg = l_dbus_message_new_method_call(dbus, owner, path, MESH_APPLICATION_INTERFACE, "JoinComplete"); l_dbus_message_set_arguments(msg, "t", l_get_be64(token)); dbus_send_with_timeout(dbus, msg, create_join_complete_reply_cb, node, NULL, DEFAULT_DBUS_TIMEOUT); l_dbus_message_unref(pending_msg); } static struct l_dbus_message *create_network_call(struct l_dbus *dbus, struct l_dbus_message *msg, void *user_data) { const char *app_path, *sender; struct l_dbus_message_iter iter_uuid; struct l_dbus_message *pending_msg; uint8_t *uuid; uint32_t n; l_debug("Create network request"); if (!l_dbus_message_get_arguments(msg, "oay", &app_path, &iter_uuid)) return dbus_error(msg, MESH_ERROR_INVALID_ARGS, NULL); if (!l_dbus_message_iter_get_fixed_array(&iter_uuid, &uuid, &n) || n != 16 || !l_uuid_is_valid(uuid)) return dbus_error(msg, MESH_ERROR_INVALID_ARGS, "Bad device UUID"); sender = l_dbus_message_get_sender(msg); pending_msg = l_dbus_message_ref(msg); l_queue_push_tail(pending_queue, pending_msg); node_create(app_path, sender, uuid, create_node_ready_cb, pending_msg); return NULL; } static struct l_dbus_message *import_call(struct l_dbus *dbus, struct l_dbus_message *msg, void *user_data) { const char *app_path, *sender; struct l_dbus_message *pending_msg = NULL; struct l_dbus_message_iter iter_uuid; struct l_dbus_message_iter iter_dev_key; struct l_dbus_message_iter iter_net_key; struct l_dbus_message_iter iter_flags; const char *key; struct l_dbus_message_iter var; uint8_t *uuid; uint8_t *dev_key; uint8_t *net_key; uint16_t net_idx; bool kr = false; bool ivu = false; uint32_t iv_index; uint16_t unicast; uint32_t n; l_debug("Import local node request"); if (!l_dbus_message_get_arguments(msg, "oayayayqa{sv}uq", &app_path, &iter_uuid, &iter_dev_key, &iter_net_key, &net_idx, &iter_flags, &iv_index, &unicast)) return dbus_error(msg, MESH_ERROR_INVALID_ARGS, NULL); if (!l_dbus_message_iter_get_fixed_array(&iter_uuid, &uuid, &n) || n != 16 || !l_uuid_is_valid(uuid)) return dbus_error(msg, MESH_ERROR_INVALID_ARGS, "Bad device UUID"); if (node_find_by_uuid(uuid)) return dbus_error(msg, MESH_ERROR_ALREADY_EXISTS, "Node already exists"); if (!l_dbus_message_iter_get_fixed_array(&iter_dev_key, &dev_key, &n) || n != 16) return dbus_error(msg, MESH_ERROR_INVALID_ARGS, "Bad dev key"); if (!l_dbus_message_iter_get_fixed_array(&iter_net_key, &net_key, &n) || n != 16) return dbus_error(msg, MESH_ERROR_INVALID_ARGS, "Bad net key"); if (net_idx > MAX_KEY_IDX) return dbus_error(msg, MESH_ERROR_INVALID_ARGS, "Bad net index"); while (l_dbus_message_iter_next_entry(&iter_flags, &key, &var)) { if (!strcmp(key, "IvUpdate") && l_dbus_message_iter_get_variant(&var, "b", &ivu)) continue; if (!strcmp(key, "KeyRefresh") && l_dbus_message_iter_get_variant(&var, "b", &kr)) continue; return dbus_error(msg, MESH_ERROR_INVALID_ARGS, "Bad flags"); } if (!IS_UNICAST(unicast)) return dbus_error(msg, MESH_ERROR_INVALID_ARGS, "Bad address"); sender = l_dbus_message_get_sender(msg); pending_msg = l_dbus_message_ref(msg); l_queue_push_tail(pending_queue, pending_msg); node_import(app_path, sender, uuid, dev_key, net_key, net_idx, kr, ivu, iv_index, unicast, create_node_ready_cb, pending_msg); return NULL; } static void setup_network_interface(struct l_dbus_interface *iface) { l_dbus_interface_method(iface, "Join", 0, join_network_call, "", "oay", "app", "uuid"); l_dbus_interface_method(iface, "Cancel", 0, cancel_join_call, "", ""); l_dbus_interface_method(iface, "Attach", 0, attach_call, "oa(ya(qa{sv}))", "ot", "node", "configuration", "app", "token"); l_dbus_interface_method(iface, "Leave", 0, leave_call, "", "t", "token"); l_dbus_interface_method(iface, "CreateNetwork", 0, create_network_call, "", "oay", "app", "uuid"); l_dbus_interface_method(iface, "Import", 0, import_call, "", "oayayayqa{sv}uq", "app", "uuid", "dev_key", "net_key", "net_index", "flags", "iv_index", "unicast"); } bool mesh_dbus_init(struct l_dbus *dbus) { if (!l_dbus_register_interface(dbus, MESH_NETWORK_INTERFACE, setup_network_interface, NULL, false)) { l_info("Unable to register %s interface", MESH_NETWORK_INTERFACE); return false; } if (!l_dbus_object_add_interface(dbus, BLUEZ_MESH_PATH, MESH_NETWORK_INTERFACE, NULL)) { l_info("Unable to register the mesh object on '%s'", MESH_NETWORK_INTERFACE); l_dbus_unregister_interface(dbus, MESH_NETWORK_INTERFACE); return false; } l_info("Added Network Interface on %s", BLUEZ_MESH_PATH); return true; } const char *mesh_get_storage_dir(void) { return storage_dir; } bluez-5.82/mesh/PaxHeaders/prov-acceptor.c0000644000000000000000000000005014772767672015563 xustar0020 atime=1743515579 20 ctime=1743591282 bluez-5.82/mesh/prov-acceptor.c0000644000000000000000000004700314772767672015250 0ustar00rootroot// SPDX-License-Identifier: LGPL-2.1-or-later /* * * BlueZ - Bluetooth protocol stack for Linux * * Copyright (C) 2018-2019 Intel Corporation. All rights reserved. * * */ #ifdef HAVE_CONFIG_H #include #endif #include #include #include "src/shared/ecc.h" #include "mesh/mesh-defs.h" #include "mesh/util.h" #include "mesh/crypto.h" #include "mesh/net.h" #include "mesh/prov.h" #include "mesh/provision.h" #include "mesh/remprv.h" #include "mesh/pb-adv.h" #include "mesh/mesh.h" #include "mesh/agent.h" /* Quick size sanity check */ static const uint16_t expected_pdu_size[] = { 2, /* PROV_INVITE */ 12, /* PROV_CAPS */ 6, /* PROV_START */ 65, /* PROV_PUB_KEY */ 1, /* PROV_INP_CMPLT */ 17, /* PROV_CONFIRM */ 17, /* PROV_RANDOM */ 34, /* PROV_DATA */ 1, /* PROV_COMPLETE */ 2, /* PROV_FAILED */ }; #define BEACON_TYPE_UNPROVISIONED 0x00 struct deferred_cmd { uint16_t len; uint8_t cmd[]; }; static const uint8_t pkt_filter = MESH_AD_TYPE_PROVISION; static const uint8_t bec_filter[] = {MESH_AD_TYPE_BEACON, BEACON_TYPE_UNPROVISIONED}; #define MAT_REMOTE_PUBLIC 0x01 #define MAT_LOCAL_PRIVATE 0x02 #define MAT_RAND_AUTH 0x04 #define MAT_SECRET (MAT_REMOTE_PUBLIC | MAT_LOCAL_PRIVATE) struct mesh_prov_acceptor { mesh_prov_acceptor_complete_func_t cmplt; prov_trans_tx_t trans_tx; struct l_queue *ob; void *agent; void *caller_data; void *trans_data; struct l_timeout *timeout; uint32_t to_secs; uint8_t out_opcode; uint8_t transport; uint8_t material; uint8_t expected; int8_t previous; bool failed; struct conf_input conf_inputs; uint8_t calc_key[16]; uint8_t salt[16]; uint8_t confirm[16]; uint8_t s_key[16]; uint8_t s_nonce[13]; uint8_t private_key[32]; uint8_t secret[32]; uint8_t rand_auth_workspace[48]; }; static struct mesh_prov_acceptor *prov = NULL; static void acceptor_free(void) { if (!prov) return; l_timeout_remove(prov->timeout); l_queue_destroy(prov->ob, l_free); mesh_send_cancel(bec_filter, sizeof(bec_filter)); mesh_send_cancel(&pkt_filter, sizeof(pkt_filter)); pb_adv_unreg(prov); l_free(prov); prov = NULL; } static void acp_prov_close(void *user_data, uint8_t reason) { struct mesh_prov_acceptor *rx_prov = user_data; if (rx_prov != prov) return; if (reason == PROV_ERR_SUCCESS) reason = PROV_ERR_UNEXPECTED_ERR; if (prov->cmplt) prov->cmplt(prov->caller_data, reason, NULL); prov->cmplt = NULL; acceptor_free(); } static void prov_send(struct mesh_prov_acceptor *prov, void *cmd, uint16_t len) { struct deferred_cmd *defer; if (prov->out_opcode == PROV_NONE) { prov->out_opcode = *(uint8_t *) cmd; prov->trans_tx(prov->trans_data, cmd, len); } else { defer = l_malloc(len + sizeof(struct deferred_cmd)); defer->len = len; memcpy(defer->cmd, cmd, len); l_queue_push_tail(prov->ob, defer); } } static void prov_to(struct l_timeout *timeout, void *user_data) { struct mesh_prov_acceptor *rx_prov = user_data; uint8_t fail_code[2] = {PROV_FAILED, PROV_ERR_UNEXPECTED_ERR}; if (rx_prov != prov) return; l_timeout_remove(prov->timeout); prov->timeout = NULL; if (prov->cmplt && prov->trans_tx) { prov->cmplt(prov->caller_data, PROV_ERR_TIMEOUT, NULL); prov->cmplt = NULL; prov_send(prov, fail_code, 2); prov->timeout = l_timeout_create(1, prov_to, prov, NULL); return; } acceptor_free(); } static void acp_prov_open(void *user_data, prov_trans_tx_t trans_tx, void *trans_data, uint8_t transport) { struct mesh_prov_acceptor *rx_prov = user_data; /* Only one provisioning session may be open at a time */ if (rx_prov != prov) return; /* Only one provisioning session may be open at a time */ if (prov->trans_tx && prov->trans_tx != trans_tx && prov->transport != transport) return; prov->trans_tx = trans_tx; prov->transport = transport; prov->trans_data = trans_data; prov->timeout = l_timeout_create(prov->to_secs, prov_to, prov, NULL); } static void swap_u256_bytes(uint8_t *u256) { int i; /* End-to-End byte reflection of 32 octet buffer */ for (i = 0; i < 16; i++) { u256[i] ^= u256[31 - i]; u256[31 - i] ^= u256[i]; u256[i] ^= u256[31 - i]; } } static bool prov_calc_secret(const uint8_t *pub, const uint8_t *priv, uint8_t *secret) { uint8_t tmp[64]; /* Convert to ECC byte order */ memcpy(tmp, pub, 64); swap_u256_bytes(tmp); swap_u256_bytes(tmp + 32); if (!ecdh_shared_secret(tmp, priv, secret)) return false; /* Convert to Mesh byte order */ swap_u256_bytes(secret); return true; } static bool acp_credentials(struct mesh_prov_acceptor *prov) { if (!memcmp(prov->conf_inputs.prv_pub_key, prov->conf_inputs.dev_pub_key, 64)) return false; if (!prov_calc_secret(prov->conf_inputs.prv_pub_key, prov->private_key, prov->secret)) return false; if (!mesh_crypto_s1(&prov->conf_inputs, sizeof(prov->conf_inputs), prov->salt)) return false; if (!mesh_crypto_prov_conf_key(prov->secret, prov->salt, prov->calc_key)) return false; l_getrandom(prov->rand_auth_workspace, 16); print_packet("PublicKeyProv", prov->conf_inputs.prv_pub_key, 64); print_packet("PublicKeyDev", prov->conf_inputs.dev_pub_key, 64); /* Normalize for debug out -- No longer needed for calculations */ swap_u256_bytes(prov->private_key); print_packet("PrivateKeyLocal", prov->private_key, 32); print_packet("ConfirmationInputs", &prov->conf_inputs, sizeof(prov->conf_inputs)); print_packet("ECDHSecret", prov->secret, 32); print_packet("LocalRandom", prov->rand_auth_workspace, 16); print_packet("ConfirmationSalt", prov->salt, 16); print_packet("ConfirmationKey", prov->calc_key, 16); return true; } static uint32_t digit_mod(uint8_t power) { uint32_t ret = 1; while (power--) ret *= 10; return ret; } static void number_cb(void *user_data, int err, uint32_t number) { struct mesh_prov_acceptor *rx_prov = user_data; struct prov_fail_msg msg; if (prov != rx_prov) return; if (err) { msg.opcode = PROV_FAILED; msg.reason = PROV_ERR_UNEXPECTED_ERR; prov_send(prov, &msg, sizeof(msg)); return; } /* Save two copies, to generate two confirmation values */ l_put_be32(number, prov->rand_auth_workspace + 28); l_put_be32(number, prov->rand_auth_workspace + 44); prov->material |= MAT_RAND_AUTH; msg.opcode = PROV_INP_CMPLT; prov_send(prov, &msg.opcode, 1); } static void static_cb(void *user_data, int err, uint8_t *key, uint32_t len) { struct mesh_prov_acceptor *rx_prov = user_data; struct prov_fail_msg msg; if (prov != rx_prov) return; if (err || !key || len != 16) { msg.opcode = PROV_FAILED; msg.reason = PROV_ERR_UNEXPECTED_ERR; prov_send(prov, &msg, sizeof(msg)); return; } /* Save two copies, to generate two confirmation values */ memcpy(prov->rand_auth_workspace + 16, key, 16); memcpy(prov->rand_auth_workspace + 32, key, 16); prov->material |= MAT_RAND_AUTH; if (prov->conf_inputs.start.auth_action == PROV_ACTION_IN_ALPHA) { msg.opcode = PROV_INP_CMPLT; prov_send(prov, &msg.opcode, 1); } } static void priv_key_cb(void *user_data, int err, uint8_t *key, uint32_t len) { struct mesh_prov_acceptor *rx_prov = user_data; struct prov_fail_msg msg; if (prov != rx_prov) return; if (err || !key || len != 32) { msg.opcode = PROV_FAILED; msg.reason = PROV_ERR_UNEXPECTED_ERR; prov_send(prov, &msg, sizeof(msg)); return; } /* API delivers Mesh byte order, switch to little endian */ swap_u256_bytes(key); memcpy(prov->private_key, key, 32); ecc_make_public_key(prov->private_key, prov->conf_inputs.dev_pub_key); /* Convert Public key to Mesh byte order */ swap_u256_bytes(prov->conf_inputs.dev_pub_key); swap_u256_bytes(prov->conf_inputs.dev_pub_key + 32); prov->material |= MAT_LOCAL_PRIVATE; if ((prov->material & MAT_SECRET) == MAT_SECRET) { if (!acp_credentials(prov)) { msg.opcode = PROV_FAILED; msg.reason = PROV_ERR_UNEXPECTED_ERR; prov_send(prov, &msg, sizeof(msg)); } } } static void send_caps(struct mesh_prov_acceptor *prov) { struct prov_caps_msg msg; msg.opcode = PROV_CAPS; memcpy(&msg.caps, &prov->conf_inputs.caps, sizeof(prov->conf_inputs.caps)); prov->expected = PROV_START; prov_send(prov, &msg, sizeof(msg)); } static void send_pub_key(struct mesh_prov_acceptor *prov) { struct prov_pub_key_msg msg; msg.opcode = PROV_PUB_KEY; memcpy(msg.pub_key, prov->conf_inputs.dev_pub_key, sizeof(msg.pub_key)); prov_send(prov, &msg, sizeof(msg)); } static bool send_conf(struct mesh_prov_acceptor *prov) { struct prov_conf_msg msg; msg.opcode = PROV_CONFIRM; mesh_crypto_aes_cmac(prov->calc_key, prov->rand_auth_workspace, 32, msg.conf); /* Fail if confirmations match */ if (!memcmp(msg.conf, prov->confirm, sizeof(msg.conf))) return false; prov_send(prov, &msg, sizeof(msg)); return true; } static void send_rand(struct mesh_prov_acceptor *prov) { struct prov_rand_msg msg; msg.opcode = PROV_RANDOM; memcpy(msg.rand, prov->rand_auth_workspace, sizeof(msg.rand)); prov_send(prov, &msg, sizeof(msg)); } static bool prov_start_check(struct prov_start *start, struct mesh_net_prov_caps *caps) { if (start->algorithm || start->pub_key > 1 || start->auth_method > 3) return false; if (start->pub_key && !caps->pub_type) return false; switch (start->auth_method) { case 0: /* No OOB */ if (start->auth_action != 0 || start->auth_size != 0) return false; break; case 1: /* Static OOB */ if (!caps->static_type || start->auth_action != 0 || start->auth_size != 0) return false; break; case 2: /* Output OOB */ if (!(caps->output_action & (1 << start->auth_action)) || start->auth_size == 0) return false; break; case 3: /* Input OOB */ if (!(caps->input_action & (1 << start->auth_action)) || start->auth_size == 0) return false; break; } return true; } static void acp_prov_rx(void *user_data, const void *dptr, uint16_t len) { struct mesh_prov_acceptor *rx_prov = user_data; const uint8_t *data = dptr; struct mesh_prov_node_info *info; struct prov_fail_msg fail; uint8_t type = *data++; uint32_t oob_key; uint64_t decode_mic; bool result; if (rx_prov != prov || !prov->trans_tx) return; l_debug("Provisioning packet received type: %2.2x (%u octets)", type, len); if (type >= L_ARRAY_SIZE(expected_pdu_size)) { l_error("Unknown PDU type: %2.2x", type); fail.reason = PROV_ERR_INVALID_PDU; goto failure; } if (type == prov->previous) { l_error("Ignore repeated %2.2x packet", type); return; } else if (prov->failed || type > prov->expected || type < prov->previous) { l_error("Expected %2.2x, Got:%2.2x", prov->expected, type); fail.reason = PROV_ERR_UNEXPECTED_PDU; goto failure; } if (len != expected_pdu_size[type]) { l_error("Expected PDU size %d, Got %d (type: %2.2x)", len, expected_pdu_size[type], type); fail.reason = PROV_ERR_INVALID_FORMAT; goto failure; } switch (type){ case PROV_INVITE: /* Prov Invite */ prov->conf_inputs.invite.attention = data[0]; send_caps(prov); break; case PROV_START: /* Prov Start */ memcpy(&prov->conf_inputs.start, data, sizeof(prov->conf_inputs.start)); if (!prov_start_check(&prov->conf_inputs.start, &prov->conf_inputs.caps)) { fail.reason = PROV_ERR_INVALID_FORMAT; goto failure; } if (prov->conf_inputs.start.pub_key) { /* Prompt Agent for Private Key of OOB */ mesh_agent_request_private_key(prov->agent, priv_key_cb, prov); } else { /* Ephemeral Public Key requested */ ecc_make_key(prov->conf_inputs.dev_pub_key, prov->private_key); swap_u256_bytes(prov->conf_inputs.dev_pub_key); swap_u256_bytes(prov->conf_inputs.dev_pub_key + 32); prov->material |= MAT_LOCAL_PRIVATE; } prov->expected = PROV_PUB_KEY; break; case PROV_PUB_KEY: /* Public Key */ /* Save Key */ memcpy(prov->conf_inputs.prv_pub_key, data, 64); prov->material |= MAT_REMOTE_PUBLIC; prov->expected = PROV_CONFIRM; if ((prov->material & MAT_SECRET) != MAT_SECRET) return; if (!acp_credentials(prov)) { fail.reason = PROV_ERR_UNEXPECTED_ERR; goto failure; } if (!prov->conf_inputs.start.pub_key) send_pub_key(prov); /* Start Step 3 */ switch (prov->conf_inputs.start.auth_method) { default: case 0: /* Auth Type 3c - No OOB */ break; case 1: /* Auth Type 3c - Static OOB */ /* Prompt Agent for Static OOB */ fail.reason = mesh_agent_request_static(prov->agent, static_cb, prov); if (fail.reason) goto failure; break; case 2: /* Auth Type 3a - Output OOB */ l_getrandom(&oob_key, sizeof(oob_key)); oob_key %= digit_mod(prov->conf_inputs.start.auth_size); /* Save two copies, for two confirmation values */ l_put_be32(oob_key, prov->rand_auth_workspace + 28); l_put_be32(oob_key, prov->rand_auth_workspace + 44); prov->material |= MAT_RAND_AUTH; if (prov->conf_inputs.start.auth_action == PROV_ACTION_OUT_ALPHA) { /* TODO: Construst NUL-term string to pass */ fail.reason = mesh_agent_display_string( prov->agent, NULL, NULL, prov); } else { /* Ask Agent to Display U32 */ fail.reason = mesh_agent_display_number( prov->agent, false, prov->conf_inputs.start.auth_action, oob_key, NULL, prov); } if (fail.reason) goto failure; break; case 3: /* Auth Type 3b - input OOB */ /* Prompt Agent for Input OOB */ if (prov->conf_inputs.start.auth_action == PROV_ACTION_IN_ALPHA) { fail.reason = mesh_agent_prompt_alpha( prov->agent, false, static_cb, prov); } else { fail.reason = mesh_agent_prompt_number( prov->agent, false, prov->conf_inputs.start.auth_action, number_cb, prov); } if (fail.reason) goto failure; break; } prov->expected = PROV_CONFIRM; break; case PROV_CONFIRM: /* Confirmation */ /* Save Provisioners confirmation for later compare */ memcpy(prov->confirm, data, 16); prov->expected = PROV_RANDOM; if (!send_conf(prov)) { fail.reason = PROV_ERR_INVALID_PDU; goto failure; } break; case PROV_RANDOM: /* Random Value */ /* Disallow matching random values */ if (!memcmp(prov->rand_auth_workspace, data, 16)) { fail.reason = PROV_ERR_INVALID_PDU; goto failure; } /* Calculate Session key (needed later) while data is fresh */ mesh_crypto_prov_prov_salt(prov->salt, data, prov->rand_auth_workspace, prov->salt); mesh_crypto_session_key(prov->secret, prov->salt, prov->s_key); mesh_crypto_nonce(prov->secret, prov->salt, prov->s_nonce); /* Calculate expected Provisioner Confirm */ memcpy(prov->rand_auth_workspace + 16, data, 16); mesh_crypto_aes_cmac(prov->calc_key, prov->rand_auth_workspace + 16, 32, prov->calc_key); /* Compare our calculation with Provisioners */ if (memcmp(prov->calc_key, prov->confirm, 16)) { fail.reason = PROV_ERR_CONFIRM_FAILED; goto failure; } /* Send Random value we used */ send_rand(prov); prov->expected = PROV_DATA; break; case PROV_DATA: /* Provisioning Data */ /* Calculate our device key */ mesh_crypto_device_key(prov->secret, prov->salt, prov->calc_key); /* Decrypt new node data into workspace */ mesh_crypto_aes_ccm_decrypt(prov->s_nonce, prov->s_key, NULL, 0, data, len - 1, prov->rand_auth_workspace, &decode_mic, sizeof(decode_mic)); /* Validate that the data hasn't been messed with in transit */ if (l_get_be64(data + 25) != decode_mic) { l_error("Provisioning Failed-MIC compare"); fail.reason = PROV_ERR_DECRYPT_FAILED; goto failure; } info = l_malloc(sizeof(struct mesh_prov_node_info)); memcpy(info->device_key, prov->calc_key, 16); memcpy(info->net_key, prov->rand_auth_workspace, 16); info->net_index = l_get_be16(prov->rand_auth_workspace + 16); info->flags = prov->rand_auth_workspace[18]; info->iv_index = l_get_be32(prov->rand_auth_workspace + 19); info->unicast = l_get_be16(prov->rand_auth_workspace + 23); info->num_ele = prov->conf_inputs.caps.num_ele; /* Send prov complete */ prov->rand_auth_workspace[0] = PROV_COMPLETE; prov->trans_tx(prov->trans_data, prov->rand_auth_workspace, 1); result = prov->cmplt(prov->caller_data, PROV_ERR_SUCCESS, info); prov->cmplt = NULL; l_free(info); if (result) { l_debug("PROV_COMPLETE"); goto cleanup; } else { fail.reason = PROV_ERR_UNEXPECTED_ERR; goto failure; } break; case PROV_FAILED: /* Provisioning Error -- abort */ /* TODO: Call Complete Callback (Fail)*/ prov->cmplt(prov->caller_data, data[0] ? data[0] : PROV_ERR_UNEXPECTED_ERR, NULL); prov->cmplt = NULL; goto cleanup; } if (prov) prov->previous = type; return; failure: fail.opcode = PROV_FAILED; prov_send(prov, &fail, sizeof(fail)); prov->failed = true; prov->previous = -1; if (prov->cmplt) prov->cmplt(prov->caller_data, fail.reason, NULL); prov->cmplt = NULL; cleanup: l_timeout_remove(prov->timeout); /* Give PB Link 5 seconds to end session */ prov->timeout = l_timeout_create(5, prov_to, prov, NULL); } static void acp_prov_ack(void *user_data, uint8_t msg_num) { struct mesh_prov_acceptor *rx_prov = user_data; struct deferred_cmd *deferred; if (rx_prov != prov) return; if (prov->out_opcode == PROV_NONE) return; prov->out_opcode = PROV_NONE; deferred = l_queue_pop_head(prov->ob); if (!deferred) return; prov_send(prov, deferred->cmd, deferred->len); l_free(deferred); } /* This starts unprovisioned device beacon */ bool acceptor_start(uint8_t num_ele, uint8_t *uuid, uint16_t algorithms, uint32_t timeout, struct mesh_agent *agent, mesh_prov_acceptor_complete_func_t complete_cb, void *caller_data) { struct mesh_agent_prov_caps *caps; uint8_t beacon[24] = {MESH_AD_TYPE_BEACON, BEACON_TYPE_UNPROVISIONED}; uint8_t len = sizeof(beacon) - sizeof(uint32_t); bool result; /* * Invoked from Join() method in mesh-api.txt, to join a * remote mesh network. May also be invoked with a NULL * uuid to perform a Device Key Refresh procedure. */ if (prov) return false; prov = l_new(struct mesh_prov_acceptor, 1); prov->to_secs = timeout; prov->agent = agent; prov->cmplt = complete_cb; prov->ob = l_queue_new(); prov->previous = -1; prov->failed = false; prov->out_opcode = PROV_NONE; prov->caller_data = caller_data; caps = mesh_agent_get_caps(agent); prov->conf_inputs.caps.num_ele = num_ele; l_put_be16(algorithms, &prov->conf_inputs.caps.algorithms); if (caps) { /* TODO: Should we sanity check values here or elsewhere? */ prov->conf_inputs.caps.pub_type = caps->pub_type; prov->conf_inputs.caps.static_type = caps->static_type; prov->conf_inputs.caps.output_size = caps->output_size; prov->conf_inputs.caps.input_size = caps->input_size; /* Store UINT16 values in Over-the-Air order, in packed * structure for crypto inputs */ l_put_be16(caps->output_action, &prov->conf_inputs.caps.output_action); l_put_be16(caps->input_action, &prov->conf_inputs.caps.input_action); /* Populate Caps fields of beacon */ l_put_be16(caps->oob_info, beacon + 18); if (caps->oob_info & OOB_INFO_URI_HASH) { l_put_be32(caps->uri_hash, beacon + 20); len += sizeof(uint32_t); } } if (uuid) { /* Compose Unprovisioned Beacon */ memcpy(beacon + 2, uuid, 16); /* Infinitely Beacon until Canceled, or Provisioning Starts */ result = mesh_send_pkt(0, 500, beacon, len); if (!result) goto error_fail; /* Always register for PB-ADV */ result = pb_adv_reg(false, acp_prov_open, acp_prov_close, acp_prov_rx, acp_prov_ack, uuid, prov); } else { /* Run Device Key Refresh Procedure */ result = register_nppi_acceptor(acp_prov_open, acp_prov_close, acp_prov_rx, acp_prov_ack, prov); } if (result) return true; error_fail: acceptor_free(); return false; } void acceptor_cancel(void *user_data) { acceptor_free(); } bluez-5.82/mesh/PaxHeaders/keyring.c0000644000000000000000000000005014772767672014447 xustar0020 atime=1743515579 20 ctime=1743591282 bluez-5.82/mesh/keyring.c0000644000000000000000000003403414772767672014134 0ustar00rootroot// SPDX-License-Identifier: LGPL-2.1-or-later /* * * BlueZ - Bluetooth protocol stack for Linux * * Copyright (C) 2019 Intel Corporation. All rights reserved. * * */ #ifdef HAVE_CONFIG_H #include #endif #define _GNU_SOURCE #include #include #include #include #include #include #include #include #include #include "mesh/mesh-defs.h" #include "mesh/dbus.h" #include "mesh/node.h" #include "mesh/keyring.h" static const char *dev_key_dir = "/dev_keys"; static const char *app_key_dir = "/app_keys"; static const char *net_key_dir = "/net_keys"; static int open_key_file(struct mesh_node *node, const char *key_dir, uint16_t idx, int flags) { const char *node_path; char fname[PATH_MAX]; int ret; if (!node) return -1; node_path = node_get_storage_dir(node); if (flags & O_CREAT) { ret = snprintf(fname, PATH_MAX, "%s%s", node_path, key_dir); if (ret < 0) return -1; if (mkdir(fname, 0755) != 0 && errno != EEXIST) l_error("Failed to create dir(%d): %s", errno, fname); } ret = snprintf(fname, PATH_MAX, "%s%s/%3.3x", node_path, key_dir, idx); if (ret < 0) return -1; if (flags & O_CREAT) return open(fname, flags, 0600); else return open(fname, flags); } bool keyring_put_net_key(struct mesh_node *node, uint16_t net_idx, struct keyring_net_key *key) { bool result = false; int fd; if (!key) return false; fd = open_key_file(node, net_key_dir, net_idx, O_WRONLY | O_CREAT | O_TRUNC); if (fd < 0) return false; if (write(fd, key, sizeof(*key)) == sizeof(*key)) result = true; close(fd); return result; } bool keyring_put_app_key(struct mesh_node *node, uint16_t app_idx, uint16_t net_idx, struct keyring_app_key *key) { bool result = false; int fd; if (!key) return false; fd = open_key_file(node, app_key_dir, app_idx, O_RDWR); if (fd >= 0) { struct keyring_app_key old_key; if (read(fd, &old_key, sizeof(old_key)) == sizeof(old_key)) { if (old_key.net_idx != net_idx) { close(fd); return false; } } lseek(fd, 0, SEEK_SET); } else fd = open_key_file(node, app_key_dir, app_idx, O_WRONLY | O_CREAT | O_TRUNC); if (fd < 0) return false; if (write(fd, key, sizeof(*key)) == sizeof(*key)) result = true; close(fd); return result; } static void finalize(int dir_fd, const char *fname, uint16_t net_idx) { struct keyring_app_key key; int fd; fd = openat(dir_fd, fname, O_RDWR); if (fd < 0) return; if (read(fd, &key, sizeof(key)) != sizeof(key) || key.net_idx != net_idx) goto done; l_debug("Finalize %s", fname); memcpy(key.old_key, key.new_key, 16); lseek(fd, 0, SEEK_SET); if (write(fd, &key, sizeof(key)) != sizeof(key)) goto done; done: close(fd); } bool keyring_finalize_app_keys(struct mesh_node *node, uint16_t net_idx) { const char *node_path; char key_dir[PATH_MAX]; DIR *dir; int ret, dir_fd; struct dirent *entry; if (!node) return false; node_path = node_get_storage_dir(node); ret = snprintf(key_dir, PATH_MAX, "%s%s", node_path, app_key_dir); if (ret < 0) return false; dir = opendir(key_dir); if (!dir) { if (errno == ENOENT) return true; l_error("Failed to open AppKey storage directory: %s", key_dir); return false; } dir_fd = dirfd(dir); while ((entry = readdir(dir)) != NULL) { /* AppKeys are stored in regular files */ if (entry->d_type == DT_REG) finalize(dir_fd, entry->d_name, net_idx); } closedir(dir); return true; } bool keyring_put_remote_dev_key(struct mesh_node *node, uint16_t unicast, uint8_t count, uint8_t dev_key[16]) { const char *node_path; char key_file[PATH_MAX]; bool result = true; int ret, fd, i; if (!IS_UNICAST_RANGE(unicast, count)) return false; if (!node) return false; node_path = node_get_storage_dir(node); ret = snprintf(key_file, PATH_MAX, "%s%s", node_path, dev_key_dir); if (ret < 0) return false; if (mkdir(key_file, 0755) != 0 && errno != EEXIST) l_error("Failed to create dir(%d): %s", errno, key_file); for (i = 0; i < count; i++) { ret = snprintf(key_file, PATH_MAX, "%s%s/%4.4x", node_path, dev_key_dir, unicast + i); if (ret < 0) return false; l_debug("Put Dev Key %s", key_file); fd = open(key_file, O_WRONLY | O_CREAT | O_TRUNC, 0600); if (fd >= 0) { if (write(fd, dev_key, 16) != 16) result = false; close(fd); } else result = false; } return result; } static bool get_key(struct mesh_node *node, const char *key_dir, uint16_t key_idx, void *key, ssize_t sz) { bool result = false; int fd; if (!key) return false; fd = open_key_file(node, key_dir, key_idx, O_RDONLY); if (fd >= 0) { if (read(fd, key, sz) == sz) result = true; close(fd); } return result; } bool keyring_get_net_key(struct mesh_node *node, uint16_t net_idx, struct keyring_net_key *key) { return get_key(node, net_key_dir, net_idx, key, sizeof(*key)); } bool keyring_get_app_key(struct mesh_node *node, uint16_t app_idx, struct keyring_app_key *key) { return get_key(node, app_key_dir, app_idx, key, sizeof(*key)); } bool keyring_get_remote_dev_key(struct mesh_node *node, uint16_t unicast, uint8_t dev_key[16]) { const char *node_path; char key_file[PATH_MAX]; bool result = false; int ret, fd; if (!IS_UNICAST(unicast)) return false; if (!node) return false; node_path = node_get_storage_dir(node); ret = snprintf(key_file, PATH_MAX, "%s%s/%4.4x", node_path, dev_key_dir, unicast); if (ret < 0) return false; fd = open(key_file, O_RDONLY); if (fd >= 0) { if (read(fd, dev_key, 16) == 16) result = true; close(fd); } return result; } bool keyring_del_net_key(struct mesh_node *node, uint16_t net_idx) { const char *node_path; char key_file[PATH_MAX]; int ret; if (!node) return false; node_path = node_get_storage_dir(node); ret = snprintf(key_file, PATH_MAX, "%s%s/%3.3x", node_path, net_key_dir, net_idx); if (ret < 0) return false; l_debug("RM Net Key %s", key_file); remove(key_file); /* TODO: See if it is easiest to delete all bound App keys here */ /* TODO: see nftw() */ return true; } bool keyring_del_app_key(struct mesh_node *node, uint16_t app_idx) { const char *node_path; char key_file[PATH_MAX]; int ret; if (!node) return false; node_path = node_get_storage_dir(node); ret = snprintf(key_file, PATH_MAX, "%s%s/%3.3x", node_path, app_key_dir, app_idx); if (ret < 0) return false; l_debug("RM App Key %s", key_file); remove(key_file); return true; } bool keyring_del_remote_dev_key(struct mesh_node *node, uint16_t unicast, uint8_t count) { const char *node_path; char key_file[PATH_MAX]; int ret, i; if (!IS_UNICAST_RANGE(unicast, count)) return false; if (!node) return false; node_path = node_get_storage_dir(node); for (i = 0; i < count; i++) { ret = snprintf(key_file, PATH_MAX, "%s%s/%4.4x", node_path, dev_key_dir, unicast + i); if (ret < 0) return false; l_debug("RM Dev Key %s", key_file); remove(key_file); } return true; } bool keyring_del_remote_dev_key_all(struct mesh_node *node, uint16_t unicast) { uint8_t dev_key[16]; uint8_t test_key[16]; uint8_t cnt = 1; if (!keyring_get_remote_dev_key(node, unicast, dev_key)) return false; while (keyring_get_remote_dev_key(node, unicast + cnt, test_key)) { if (memcmp(dev_key, test_key, sizeof(dev_key))) break; cnt++; } if (cnt > 1) return keyring_del_remote_dev_key(node, unicast + 1, cnt - 1); return true; } static DIR *open_key_dir(const char *node_path, const char *key_dir_name) { char dir_path[PATH_MAX]; DIR *key_dir; int ret; ret = snprintf(dir_path, PATH_MAX, "%s%s", node_path, key_dir_name); if (ret < 0) return NULL; key_dir = opendir(dir_path); if (!key_dir) l_error("Failed to open keyring storage directory: %s", dir_path); return key_dir; } static int open_key_dir_entry(int dir_fd, struct dirent *entry, uint8_t fname_len) { if (entry->d_type != DT_REG) return -1; /* Check the file name length */ if (strlen(entry->d_name) != fname_len) return -1; return openat(dir_fd, entry->d_name, O_RDONLY); } static void append_old_key(struct l_dbus_message_builder *builder, const uint8_t key[16]) { l_dbus_message_builder_enter_dict(builder, "sv"); l_dbus_message_builder_append_basic(builder, 's', "OldKey"); l_dbus_message_builder_enter_variant(builder, "ay"); dbus_append_byte_array(builder, key, 16); l_dbus_message_builder_leave_variant(builder); l_dbus_message_builder_leave_dict(builder); } static void build_app_keys_reply(const char *node_path, struct l_dbus_message_builder *builder, uint16_t net_idx, uint8_t phase) { DIR *key_dir; int key_dir_fd; struct dirent *entry; key_dir = open_key_dir(node_path, app_key_dir); if (!key_dir) return; key_dir_fd = dirfd(key_dir); l_dbus_message_builder_enter_dict(builder, "sv"); l_dbus_message_builder_append_basic(builder, 's', "AppKeys"); l_dbus_message_builder_enter_variant(builder, "a(qaya{sv})"); l_dbus_message_builder_enter_array(builder, "(qaya{sv})"); while ((entry = readdir(key_dir)) != NULL) { struct keyring_app_key key; int fd = open_key_dir_entry(key_dir_fd, entry, 3); if (fd < 0) continue; if (read(fd, &key, sizeof(key)) != sizeof(key) || key.net_idx != net_idx) { close(fd); continue; } close(fd); l_dbus_message_builder_enter_struct(builder, "qaya{sv}"); l_dbus_message_builder_append_basic(builder, 'q', &key.app_idx); dbus_append_byte_array(builder, key.new_key, 16); l_dbus_message_builder_enter_array(builder, "{sv}"); if (phase != KEY_REFRESH_PHASE_NONE) append_old_key(builder, key.old_key); l_dbus_message_builder_leave_array(builder); l_dbus_message_builder_leave_struct(builder); } l_dbus_message_builder_leave_array(builder); l_dbus_message_builder_leave_variant(builder); l_dbus_message_builder_leave_dict(builder); closedir(key_dir); } static bool build_net_keys_reply(const char *node_path, struct l_dbus_message_builder *builder) { DIR *key_dir; int key_dir_fd; struct dirent *entry; bool result = false; key_dir = open_key_dir(node_path, net_key_dir); if (!key_dir) return false; key_dir_fd = dirfd(key_dir); l_dbus_message_builder_enter_dict(builder, "sv"); l_dbus_message_builder_append_basic(builder, 's', "NetKeys"); l_dbus_message_builder_enter_variant(builder, "a(qaya{sv})"); l_dbus_message_builder_enter_array(builder, "(qaya{sv})"); while ((entry = readdir(key_dir)) != NULL) { struct keyring_net_key key; int fd = open_key_dir_entry(key_dir_fd, entry, 3); if (fd < 0) continue; if (read(fd, &key, sizeof(key)) != sizeof(key)) { close(fd); goto done; } close(fd); /* * If network key is stuck in phase 3, keyring * write failed and this key info is unreliable. */ if (key.phase == KEY_REFRESH_PHASE_THREE) continue; l_dbus_message_builder_enter_struct(builder, "qaya{sv}"); l_dbus_message_builder_append_basic(builder, 'q', &key.net_idx); dbus_append_byte_array(builder, key.new_key, 16); l_dbus_message_builder_enter_array(builder, "{sv}"); if (key.phase != KEY_REFRESH_PHASE_NONE) { dbus_append_dict_entry_basic(builder, "Phase", "y", &key.phase); append_old_key(builder, key.old_key); } build_app_keys_reply(node_path, builder, key.net_idx, key.phase); l_dbus_message_builder_leave_array(builder); l_dbus_message_builder_leave_struct(builder); } l_dbus_message_builder_leave_array(builder); l_dbus_message_builder_leave_variant(builder); l_dbus_message_builder_leave_dict(builder); result = true; done: closedir(key_dir); return result; } struct dev_key_entry { uint16_t unicast; uint8_t value[16]; }; static bool match_key_value(const void *a, const void *b) { const struct dev_key_entry *key = a; const uint8_t *value = b; return (memcmp(key->value, value, 16) == 0); } static void build_dev_key_entry(void *a, void *b) { struct dev_key_entry *key = a; struct l_dbus_message_builder *builder = b; l_dbus_message_builder_enter_struct(builder, "qay"); l_dbus_message_builder_append_basic(builder, 'q', &key->unicast); dbus_append_byte_array(builder, key->value, 16); l_dbus_message_builder_leave_struct(builder); } static bool build_dev_keys_reply(const char *node_path, struct l_dbus_message_builder *builder) { DIR *key_dir; int key_dir_fd; struct dirent *entry; struct l_queue *keys; bool result = false; key_dir = open_key_dir(node_path, dev_key_dir); /* * There is always at least one device key present for a local node. * Therefore, return false, if the directory does not exist. */ if (!key_dir) return false; key_dir_fd = dirfd(key_dir); keys = l_queue_new(); while ((entry = readdir(key_dir)) != NULL) { uint8_t buf[16]; uint16_t unicast; struct dev_key_entry *key; int fd = open_key_dir_entry(key_dir_fd, entry, 4); if (fd < 0) continue; if (read(fd, buf, 16) != 16) { close(fd); goto done; } close(fd); if (sscanf(entry->d_name, "%04hx", &unicast) != 1) goto done; key = l_queue_find(keys, match_key_value, buf); if (key) { if (key->unicast > unicast) key->unicast = unicast; continue; } key = l_new(struct dev_key_entry, 1); key->unicast = unicast; memcpy(key->value, buf, 16); l_queue_push_tail(keys, key); } l_dbus_message_builder_enter_dict(builder, "sv"); l_dbus_message_builder_append_basic(builder, 's', "DevKeys"); l_dbus_message_builder_enter_variant(builder, "a(qay)"); l_dbus_message_builder_enter_array(builder, "(qay)"); l_queue_foreach(keys, build_dev_key_entry, builder); l_dbus_message_builder_leave_array(builder); l_dbus_message_builder_leave_variant(builder); l_dbus_message_builder_leave_dict(builder); result = true; done: l_queue_destroy(keys, l_free); closedir(key_dir); return result; } bool keyring_build_export_keys_reply(struct mesh_node *node, struct l_dbus_message_builder *builder) { const char *node_path; if (!node) return false; node_path = node_get_storage_dir(node); if (!build_net_keys_reply(node_path, builder)) return false; return build_dev_keys_reply(node_path, builder); } bluez-5.82/mesh/PaxHeaders/node.h0000644000000000000000000000005014447506754013720 xustar0020 atime=1743516868 20 ctime=1743591282 bluez-5.82/mesh/node.h0000644000000000000000000001076614447506754013413 0ustar00rootroot/* SPDX-License-Identifier: LGPL-2.1-or-later */ /* * * BlueZ - Bluetooth protocol stack for Linux * * Copyright (C) 2018-2019 Intel Corporation. All rights reserved. * * */ struct mesh_net; struct mesh_node; struct mesh_io; struct mesh_agent; struct mesh_config; struct mesh_config_node; typedef void (*node_ready_func_t) (void *user_data, int status, struct mesh_node *node); typedef void (*node_join_ready_func_t) (struct mesh_node *node, struct mesh_agent *agent); void node_remove(struct mesh_node *node); void node_join(const char *app_root, const char *sender, const uint8_t *uuid, node_join_ready_func_t cb); uint8_t *node_uuid_get(struct mesh_node *node); struct mesh_net *node_get_net(struct mesh_node *node); struct mesh_node *node_find_by_addr(uint16_t addr); struct mesh_node *node_find_by_uuid(uint8_t uuid[16]); struct mesh_node *node_find_by_token(uint64_t token); bool node_is_provisioner(struct mesh_node *node); bool node_is_busy(struct mesh_node *node); void node_app_key_delete(struct mesh_node *node, uint16_t net_idx, uint16_t app_idx); uint16_t node_get_primary(struct mesh_node *node); uint16_t node_get_primary_net_idx(struct mesh_node *node); void node_set_token(struct mesh_node *node, uint8_t token[8]); const uint8_t *node_get_token(struct mesh_node *node); const uint8_t *node_get_device_key(struct mesh_node *node); bool node_get_device_key_candidate(struct mesh_node *node, uint8_t *key); void node_finalize_candidate(struct mesh_node *node); void node_set_num_elements(struct mesh_node *node, uint8_t num_ele); uint8_t node_get_num_elements(struct mesh_node *node); uint8_t node_default_ttl_get(struct mesh_node *node); bool node_default_ttl_set(struct mesh_node *node, uint8_t ttl); bool node_set_sequence_number(struct mesh_node *node, uint32_t seq); uint32_t node_get_sequence_number(struct mesh_node *node); int node_get_element_idx(struct mesh_node *node, uint16_t ele_addr); struct l_queue *node_get_element_models(struct mesh_node *node, uint8_t ele_idx); uint16_t node_get_crpl(struct mesh_node *node); bool node_init_from_storage(struct mesh_node *node, const uint8_t uuid[16], struct mesh_config_node *db_node); const uint8_t *node_get_comp(struct mesh_node *node, uint8_t page_num, uint16_t *len); bool node_replace_comp(struct mesh_node *node, uint8_t retire, uint8_t with); uint8_t node_lpn_mode_get(struct mesh_node *node); bool node_relay_mode_set(struct mesh_node *node, bool enable, uint8_t cnt, uint16_t interval); uint8_t node_relay_mode_get(struct mesh_node *node, uint8_t *cnt, uint16_t *interval); bool node_proxy_mode_set(struct mesh_node *node, bool enable); uint8_t node_proxy_mode_get(struct mesh_node *node); bool node_beacon_mode_set(struct mesh_node *node, bool enable); bool node_mpb_mode_set(struct mesh_node *node, bool enable, uint8_t period); uint8_t node_mpb_mode_get(struct mesh_node *node, uint8_t *period); uint8_t node_beacon_mode_get(struct mesh_node *node); bool node_friend_mode_set(struct mesh_node *node, bool enable); uint8_t node_friend_mode_get(struct mesh_node *node); const char *node_get_element_path(struct mesh_node *node, uint8_t ele_idx); const char *node_get_owner(struct mesh_node *node); const char *node_get_app_path(struct mesh_node *node); bool node_add_pending_local(struct mesh_node *node, void *info); void node_attach_io_all(struct mesh_io *io); void node_attach_io(struct mesh_node *node, struct mesh_io *io); void node_attach(const char *app_root, const char *sender, uint64_t token, node_ready_func_t cb, void *user_data); void node_build_attach_reply(struct mesh_node *node, struct l_dbus_message *reply); void node_create(const char *app_root, const char *sender, const uint8_t *uuid, node_ready_func_t cb, void *user_data); void node_import(const char *app_root, const char *sender, const uint8_t *uuid, const uint8_t dev_key[16], const uint8_t net_key[16], uint16_t net_idx, bool kr, bool ivu, uint32_t iv_index, uint16_t unicast, node_ready_func_t cb, void *user_data); bool node_dbus_init(struct l_dbus *bus); void node_cleanup_all(void); struct mesh_config *node_config_get(struct mesh_node *node); struct mesh_agent *node_get_agent(struct mesh_node *node); const char *node_get_storage_dir(struct mesh_node *node); bool node_load_from_storage(const char *storage_dir); void node_finalize_new_node(struct mesh_node *node, struct mesh_io *io); void node_property_changed(struct mesh_node *node, const char *property); bool node_refresh(struct mesh_node *node, bool hard, void *prov_info); bluez-5.82/mesh/PaxHeaders/mesh-defs.h0000644000000000000000000000005014015011623014617 xustar0020 atime=1743516868 20 ctime=1743591282 bluez-5.82/mesh/mesh-defs.h0000644000000000000000000000655614015011623014314 0ustar00rootroot/* SPDX-License-Identifier: LGPL-2.1-or-later */ /* * * BlueZ - Bluetooth protocol stack for Linux * * Copyright (C) 2018-2019 Intel Corporation. All rights reserved. * * */ #define MESH_AD_TYPE_PROVISION 0x29 #define MESH_AD_TYPE_NETWORK 0x2A #define MESH_AD_TYPE_BEACON 0x2B #define FEATURE_RELAY 1 #define FEATURE_PROXY 2 #define FEATURE_FRIEND 4 #define FEATURE_LPN 8 #define MESH_MODE_DISABLED 0 #define MESH_MODE_ENABLED 1 #define MESH_MODE_UNSUPPORTED 2 #define KEY_REFRESH_PHASE_NONE 0x00 #define KEY_REFRESH_PHASE_ONE 0x01 #define KEY_REFRESH_PHASE_TWO 0x02 #define KEY_REFRESH_PHASE_THREE 0x03 #define KEY_REFRESH_TRANS_TWO 0x02 #define KEY_REFRESH_TRANS_THREE 0x03 #define DEFAULT_TTL 0xff #define TTL_MASK 0x7f /* Supported algorithms for provisioning */ #define ALG_FIPS_256_ECC 0x0001 /* Input OOB action bit flags */ #define OOB_IN_PUSH 0x0001 #define OOB_IN_TWIST 0x0002 #define OOB_IN_NUMBER 0x0004 #define OOB_IN_ALPHA 0x0008 /* Output OOB action bit flags */ #define OOB_OUT_BLINK 0x0001 #define OOB_OUT_BEEP 0x0002 #define OOB_OUT_VIBRATE 0x0004 #define OOB_OUT_NUMBER 0x0008 #define OOB_OUT_ALPHA 0x0010 /* Status codes */ #define MESH_STATUS_SUCCESS 0x00 #define MESH_STATUS_INVALID_ADDRESS 0x01 #define MESH_STATUS_INVALID_MODEL 0x02 #define MESH_STATUS_INVALID_APPKEY 0x03 #define MESH_STATUS_INVALID_NETKEY 0x04 #define MESH_STATUS_INSUFF_RESOURCES 0x05 #define MESH_STATUS_IDX_ALREADY_STORED 0x06 #define MESH_STATUS_INVALID_PUB_PARAM 0x07 #define MESH_STATUS_NOT_SUB_MOD 0x08 #define MESH_STATUS_STORAGE_FAIL 0x09 #define MESH_STATUS_FEATURE_NO_SUPPORT 0x0a #define MESH_STATUS_CANNOT_UPDATE 0x0b #define MESH_STATUS_CANNOT_REMOVE 0x0c #define MESH_STATUS_CANNOT_BIND 0x0d #define MESH_STATUS_UNABLE_CHANGE_STATE 0x0e #define MESH_STATUS_CANNOT_SET 0x0f #define MESH_STATUS_UNSPECIFIED_ERROR 0x10 #define MESH_STATUS_INVALID_BINDING 0x11 #define UNASSIGNED_ADDRESS 0x0000 #define PROXIES_ADDRESS 0xfffc #define FRIENDS_ADDRESS 0xfffd #define RELAYS_ADDRESS 0xfffe #define ALL_NODES_ADDRESS 0xffff #define VIRTUAL_ADDRESS_LOW 0x8000 #define VIRTUAL_ADDRESS_HIGH 0xbfff #define GROUP_ADDRESS_LOW 0xc000 #define GROUP_ADDRESS_HIGH 0xfeff #define FIXED_GROUP_LOW 0xff00 #define FIXED_GROUP_HIGH 0xffff #define NODE_IDENTITY_STOPPED 0x00 #define NODE_IDENTITY_RUNNING 0x01 #define NODE_IDENTITY_NOT_SUPPORTED 0x02 #define PRIMARY_ELE_IDX 0x00 #define PRIMARY_NET_IDX 0x0000 #define MAX_KEY_IDX 0x0fff #define MAX_MODEL_COUNT 0xff #define MAX_ELE_COUNT 0xff #define MAX_MSG_LEN 380 #define VENDOR_ID_MASK 0xffff0000 #define NET_IDX_INVALID 0xffff #define NET_NID_INVALID 0xff #define NET_IDX_MAX 0x0fff #define APP_IDX_MAX 0x0fff #define APP_AID_INVALID 0xff #define APP_IDX_MASK 0x0fff #define APP_IDX_DEV_REMOTE 0x6fff #define APP_IDX_DEV_LOCAL 0x7fff #define DEFAULT_SEQUENCE_NUMBER 0x000000 #define SEQ_MASK 0xffffff #define IS_UNASSIGNED(x) ((x) == UNASSIGNED_ADDRESS) #define IS_UNICAST(x) (((x) > UNASSIGNED_ADDRESS) && \ ((x) < VIRTUAL_ADDRESS_LOW)) #define IS_UNICAST_RANGE(x, c) (IS_UNICAST(x) && IS_UNICAST(x + c - 1)) #define IS_VIRTUAL(x) (((x) >= VIRTUAL_ADDRESS_LOW) && \ ((x) <= VIRTUAL_ADDRESS_HIGH)) #define IS_GROUP(x) ((((x) >= GROUP_ADDRESS_LOW) && \ ((x) < FIXED_GROUP_HIGH)) || \ ((x) == ALL_NODES_ADDRESS)) #define IS_FIXED_GROUP_ADDRESS(x) ((x) >= PROXIES_ADDRESS) #define IS_ALL_NODES(x) ((x) == ALL_NODES_ADDRESS) bluez-5.82/mesh/PaxHeaders/friend.h0000644000000000000000000000005014015011623014213 xustar0020 atime=1743516868 20 ctime=1743591282 bluez-5.82/mesh/friend.h0000644000000000000000000000366014015011623013701 0ustar00rootroot/* SPDX-License-Identifier: LGPL-2.1-or-later */ /* * * BlueZ - Bluetooth protocol stack for Linux * * Copyright (C) 2018 Intel Corporation. All rights reserved. * * */ #define OP_FRND_REQUEST 0x8040 #define OP_FRND_INQUIRY 0x8041 #define OP_FRND_CONFIRM 0x8042 #define OP_FRND_SUB_LIST_ADD 0x8043 #define OP_FRND_SUB_LIST_CONFIRM 0x8044 #define OP_FRND_SUB_LIST_REMOVE 0x8045 #define OP_FRND_NEGOTIATE 0x8046 #define OP_FRND_CLEAR 0x8047 void friend_poll(struct mesh_net *net, uint16_t src, bool seq, struct mesh_friend *frnd); void friend_request(struct mesh_net *net, uint16_t net_idx, uint16_t src, uint8_t minReq, uint8_t delay, uint32_t timeout, uint16_t prev, uint8_t num_elements, uint16_t cntr, int8_t rssi); void friend_clear_confirm(struct mesh_net *net, uint16_t src, uint16_t lpn, uint16_t lpnCounter); void friend_clear(struct mesh_net *net, uint16_t src, uint16_t lpn, uint16_t lpnCounter, struct mesh_friend *frnd); void friend_sub_add(struct mesh_net *net, struct mesh_friend *frnd, const uint8_t *pkt, uint8_t len); void friend_sub_del(struct mesh_net *net, struct mesh_friend *frnd, const uint8_t *pkt, uint8_t len); void mesh_friend_relay_init(struct mesh_net *net, uint16_t addr); /* Low-Power-Node role */ void frnd_sub_add(struct mesh_net *net, uint32_t parms[7]); void frnd_sub_del(struct mesh_net *net, uint32_t parms[7]); void frnd_poll(struct mesh_net *net, bool retry); void frnd_clear(struct mesh_net *net); void frnd_ack_poll(struct mesh_net *net); void frnd_poll_cancel(struct mesh_net *net); void frnd_request_friend(struct mesh_net *net, uint8_t cache, uint8_t offer_delay, uint8_t delay, uint32_t timeout); void frnd_offer(struct mesh_net *net, uint16_t src, uint8_t window, uint8_t cache, uint8_t sub_list_size, int8_t r_rssi, int8_t l_rssi, uint16_t fn_cnt); void frnd_key_refresh(struct mesh_net *net, uint8_t phase); uint32_t frnd_get_key(struct mesh_net *net); bluez-5.82/PaxHeaders/Makefile.tools0000644000000000000000000000005014766002272014453 xustar0020 atime=1743515577 20 ctime=1743591275 bluez-5.82/Makefile.tools0000644000000000000000000004722014766002272014141 0ustar00rootroot# SPDX-License-Identifier: GPL-2.0 if CLIENT bin_PROGRAMS += client/bluetoothctl client_bluetoothctl_SOURCES = client/main.c \ client/print.h client/print.c \ client/display.h client/display.c \ client/agent.h client/agent.c \ client/advertising.h \ client/advertising.c \ client/adv_monitor.h \ client/adv_monitor.c \ client/gatt.h client/gatt.c \ client/admin.h client/admin.c \ client/player.h client/player.c \ client/mgmt.h client/mgmt.c \ client/assistant.h client/assistant.c \ client/hci.h client/hci.c client_bluetoothctl_LDADD = lib/libbluetooth-internal.la \ gdbus/libgdbus-internal.la src/libshared-glib.la \ $(GLIB_LIBS) $(DBUS_LIBS) -lreadline endif if ZSH_COMPLETIONS zshcompletiondir=$(ZSH_COMPLETIONDIR) dist_zshcompletion_DATA = completion/zsh/_bluetoothctl endif if MONITOR bin_PROGRAMS += monitor/btmon monitor_btmon_SOURCES = monitor/main.c monitor/bt.h \ monitor/display.h monitor/display.c \ monitor/hcidump.h monitor/hcidump.c \ monitor/ellisys.h monitor/ellisys.c \ monitor/control.h monitor/control.c \ monitor/packet.h monitor/packet.c \ monitor/vendor.h monitor/vendor.c \ monitor/lmp.h monitor/lmp.c \ monitor/crc.h monitor/crc.c \ monitor/ll.h monitor/ll.c \ monitor/l2cap.h monitor/l2cap.c \ monitor/sdp.h monitor/sdp.c \ monitor/avctp.h monitor/avctp.c \ monitor/avdtp.h monitor/avdtp.c \ monitor/a2dp.h monitor/a2dp.c \ monitor/rfcomm.h monitor/rfcomm.c \ monitor/bnep.h monitor/bnep.c \ monitor/hwdb.h monitor/hwdb.c \ monitor/keys.h monitor/keys.c \ monitor/analyze.h monitor/analyze.c \ monitor/intel.h monitor/intel.c \ monitor/broadcom.h monitor/broadcom.c \ monitor/msft.h monitor/msft.c \ monitor/jlink.h monitor/jlink.c \ monitor/tty.h monitor/emulator.h \ monitor/att.h monitor/att.c \ src/log.h src/log.c \ src/textfile.h src/textfile.c \ src/settings.h src/settings.c monitor_btmon_LDADD = lib/libbluetooth-internal.la \ src/libshared-mainloop.la \ $(GLIB_LIBS) $(UDEV_LIBS) -ldl if MANPAGES man_MANS += monitor/btmon.1 endif endif manual_pages += monitor/btmon.1 if LOGGER pkglibexec_PROGRAMS += tools/btmon-logger tools_btmon_logger_SOURCES = tools/btmon-logger.c tools_btmon_logger_LDADD = src/libshared-mainloop.la if SYSTEMD systemdsystemunit_DATA += tools/bluetooth-logger.service endif endif if TESTING noinst_PROGRAMS += emulator/btvirt emulator/b1ee emulator/hfp \ peripheral/btsensor tools/3dsp \ tools/mgmt-tester tools/gap-tester \ tools/l2cap-tester tools/sco-tester \ tools/smp-tester tools/hci-tester \ tools/rfcomm-tester tools/bnep-tester \ tools/userchan-tester tools/iso-tester \ tools/mesh-tester tools/ioctl-tester emulator_btvirt_SOURCES = emulator/main.c monitor/bt.h \ emulator/serial.h emulator/serial.c \ emulator/server.h emulator/server.c \ emulator/vhci.h emulator/vhci.c \ emulator/btdev.h emulator/btdev.c \ emulator/bthost.h emulator/bthost.c \ emulator/smp.c \ emulator/phy.h emulator/phy.c \ emulator/le.h emulator/le.c emulator_btvirt_LDADD = lib/libbluetooth-internal.la src/libshared-mainloop.la emulator_b1ee_SOURCES = emulator/b1ee.c emulator_b1ee_LDADD = src/libshared-mainloop.la emulator_hfp_SOURCES = emulator/hfp.c emulator_hfp_LDADD = src/libshared-mainloop.la peripheral_btsensor_SOURCES = peripheral/main.c \ peripheral/efivars.h peripheral/efivars.c \ peripheral/attach.h peripheral/attach.c \ peripheral/log.h peripheral/log.c \ peripheral/gap.h peripheral/gap.c \ peripheral/gatt.h peripheral/gatt.c peripheral_btsensor_LDADD = src/libshared-mainloop.la \ lib/libbluetooth-internal.la tools_3dsp_SOURCES = tools/3dsp.c monitor/bt.h tools_3dsp_LDADD = src/libshared-mainloop.la tools_mgmt_tester_SOURCES = tools/mgmt-tester.c monitor/bt.h \ emulator/hciemu.h emulator/hciemu.c \ emulator/vhci.h emulator/vhci.c \ emulator/btdev.h emulator/btdev.c \ emulator/bthost.h emulator/bthost.c \ emulator/smp.c tools_mgmt_tester_LDADD = lib/libbluetooth-internal.la \ src/libshared-glib.la $(GLIB_LIBS) tools_mesh_tester_SOURCES = tools/mesh-tester.c monitor/bt.h \ emulator/hciemu.h emulator/hciemu.c \ emulator/vhci.h emulator/vhci.c \ emulator/btdev.h emulator/btdev.c \ emulator/bthost.h emulator/bthost.c \ emulator/smp.c tools_mesh_tester_LDADD = lib/libbluetooth-internal.la \ src/libshared-glib.la $(GLIB_LIBS) tools_l2cap_tester_SOURCES = tools/l2cap-tester.c tools/tester.h monitor/bt.h \ emulator/hciemu.h emulator/hciemu.c \ emulator/vhci.h emulator/vhci.c \ emulator/btdev.h emulator/btdev.c \ emulator/bthost.h emulator/bthost.c \ emulator/smp.c tools_l2cap_tester_LDADD = lib/libbluetooth-internal.la \ src/libshared-glib.la $(GLIB_LIBS) tools_rfcomm_tester_SOURCES = tools/rfcomm-tester.c monitor/bt.h \ emulator/hciemu.h emulator/hciemu.c \ emulator/vhci.h emulator/vhci.c \ emulator/btdev.h emulator/btdev.c \ emulator/bthost.h emulator/bthost.c \ emulator/smp.c tools_rfcomm_tester_LDADD = lib/libbluetooth-internal.la \ src/libshared-glib.la $(GLIB_LIBS) tools_bnep_tester_SOURCES = tools/bnep-tester.c monitor/bt.h \ emulator/hciemu.h emulator/hciemu.c \ emulator/vhci.h emulator/vhci.c \ emulator/btdev.h emulator/btdev.c \ emulator/bthost.h emulator/bthost.c \ emulator/smp.c tools_bnep_tester_LDADD = lib/libbluetooth-internal.la \ src/libshared-glib.la $(GLIB_LIBS) tools_smp_tester_SOURCES = tools/smp-tester.c monitor/bt.h \ emulator/hciemu.h emulator/hciemu.c \ emulator/vhci.h emulator/vhci.c \ emulator/btdev.h emulator/btdev.c \ emulator/bthost.h emulator/bthost.c \ emulator/smp.c tools_smp_tester_LDADD = lib/libbluetooth-internal.la \ src/libshared-glib.la $(GLIB_LIBS) tools_gap_tester_SOURCES = tools/gap-tester.c monitor/bt.h \ emulator/hciemu.h emulator/hciemu.c \ emulator/vhci.h emulator/vhci.c \ emulator/btdev.h emulator/btdev.c \ emulator/bthost.h emulator/bthost.c \ emulator/smp.c tools_gap_tester_LDADD = lib/libbluetooth-internal.la \ gdbus/libgdbus-internal.la \ src/libshared-glib.la \ $(GLIB_LIBS) $(DBUS_LIBS) tools_sco_tester_SOURCES = tools/sco-tester.c tools/tester.h monitor/bt.h \ emulator/hciemu.h emulator/hciemu.c \ emulator/vhci.h emulator/vhci.c \ emulator/btdev.h emulator/btdev.c \ emulator/bthost.h emulator/bthost.c \ emulator/smp.c tools_sco_tester_LDADD = lib/libbluetooth-internal.la \ src/libshared-glib.la $(GLIB_LIBS) tools_hci_tester_SOURCES = tools/hci-tester.c monitor/bt.h tools_hci_tester_LDADD = src/libshared-glib.la $(GLIB_LIBS) tools_userchan_tester_SOURCES = tools/userchan-tester.c monitor/bt.h \ emulator/hciemu.h emulator/hciemu.c \ emulator/vhci.h emulator/vhci.c \ emulator/btdev.h emulator/btdev.c \ emulator/bthost.h emulator/bthost.c \ emulator/smp.c tools_userchan_tester_LDADD = lib/libbluetooth-internal.la \ src/libshared-glib.la $(GLIB_LIBS) tools_iso_tester_SOURCES = tools/iso-tester.c tools/tester.h monitor/bt.h \ emulator/hciemu.h emulator/hciemu.c \ emulator/vhci.h emulator/vhci.c \ emulator/btdev.h emulator/btdev.c \ emulator/bthost.h emulator/bthost.c \ emulator/smp.c tools_iso_tester_LDADD = lib/libbluetooth-internal.la \ src/libshared-glib.la $(GLIB_LIBS) tools_ioctl_tester_SOURCES = tools/ioctl-tester.c monitor/bt.h \ emulator/hciemu.h emulator/hciemu.c \ emulator/vhci.h emulator/vhci.c \ emulator/btdev.h emulator/btdev.c \ emulator/bthost.h emulator/bthost.c \ emulator/smp.c tools_ioctl_tester_LDADD = lib/libbluetooth-internal.la \ src/libshared-glib.la $(GLIB_LIBS) endif if TOOLS bin_PROGRAMS += tools/rctest tools/l2test tools/l2ping tools/bluemoon \ tools/hex2hcd tools/mpris-proxy tools/btattach tools/isotest noinst_PROGRAMS += tools/bdaddr tools/avinfo tools/avtest \ tools/scotest tools/hwdb \ tools/hcieventmask tools/hcisecfilter \ tools/btinfo tools/btconfig \ tools/btsnoop tools/btproxy \ tools/btiotest tools/bneptest tools/mcaptest \ tools/cltest tools/oobtest tools/advtest \ tools/seq2bseq tools/nokfw tools/rtlfw \ tools/bcmfw tools/create-image \ tools/eddystone tools/ibeacon \ tools/btgatt-client tools/btgatt-server \ tools/test-runner tools/check-selftest \ tools/gatt-service profiles/iap/iapd tools_bdaddr_SOURCES = tools/bdaddr.c src/oui.h src/oui.c tools_bdaddr_LDADD = lib/libbluetooth-internal.la $(UDEV_LIBS) tools_avinfo_LDADD = lib/libbluetooth-internal.la tools_avtest_LDADD = lib/libbluetooth-internal.la tools_scotest_LDADD = lib/libbluetooth-internal.la tools_hwdb_LDADD = lib/libbluetooth-internal.la tools_hcieventmask_LDADD = lib/libbluetooth-internal.la tools_btinfo_SOURCES = tools/btinfo.c monitor/bt.h tools_btinfo_LDADD = src/libshared-mainloop.la tools_btattach_SOURCES = tools/btattach.c monitor/bt.h tools_btattach_LDADD = src/libshared-mainloop.la tools_btconfig_SOURCES = tools/btconfig.c tools_btconfig_LDADD = src/libshared-mainloop.la tools_btsnoop_SOURCES = tools/btsnoop.c tools_btsnoop_LDADD = src/libshared-mainloop.la tools_btproxy_SOURCES = tools/btproxy.c monitor/bt.h tools_btproxy_LDADD = src/libshared-mainloop.la tools_btiotest_SOURCES = tools/btiotest.c btio/btio.h btio/btio.c tools_btiotest_LDADD = lib/libbluetooth-internal.la $(GLIB_LIBS) tools_mcaptest_SOURCES = tools/mcaptest.c \ btio/btio.h btio/btio.c \ src/log.c src/log.h \ profiles/health/mcap.h profiles/health/mcap.c tools_mcaptest_LDADD = lib/libbluetooth-internal.la $(GLIB_LIBS) \ src/libshared-mainloop.la -lrt tools_bneptest_SOURCES = tools/bneptest.c \ btio/btio.h btio/btio.c \ src/log.h src/log.c \ profiles/network/bnep.h profiles/network/bnep.c tools_bneptest_LDADD = lib/libbluetooth-internal.la $(GLIB_LIBS) \ src/libshared-mainloop.la tools_cltest_SOURCES = tools/cltest.c tools_cltest_LDADD = lib/libbluetooth-internal.la src/libshared-mainloop.la tools_oobtest_SOURCES = tools/oobtest.c tools_oobtest_LDADD = lib/libbluetooth-internal.la src/libshared-mainloop.la tools_advtest_SOURCES = tools/advtest.c tools_advtest_LDADD = lib/libbluetooth-internal.la src/libshared-mainloop.la tools_seq2bseq_SOURCES = tools/seq2bseq.c tools_nokfw_SOURCES = tools/nokfw.c tools_rtlfw_SOURCES = tools/rtlfw.c tools_create_image_SOURCES = tools/create-image.c tools_eddystone_SOURCES = tools/eddystone.c monitor/bt.h tools_eddystone_LDADD = src/libshared-mainloop.la tools_ibeacon_SOURCES = tools/ibeacon.c monitor/bt.h tools_ibeacon_LDADD = src/libshared-mainloop.la tools_btgatt_client_SOURCES = tools/btgatt-client.c src/uuid-helper.c tools_btgatt_client_LDADD = src/libshared-mainloop.la \ lib/libbluetooth-internal.la tools_btgatt_server_SOURCES = tools/btgatt-server.c src/uuid-helper.c tools_btgatt_server_LDADD = src/libshared-mainloop.la \ lib/libbluetooth-internal.la tools_rctest_LDADD = lib/libbluetooth-internal.la tools_l2test_LDADD = lib/libbluetooth-internal.la tools_l2ping_LDADD = lib/libbluetooth-internal.la tools_bluemoon_SOURCES = tools/bluemoon.c monitor/bt.h tools_bluemoon_LDADD = src/libshared-mainloop.la tools_hex2hcd_SOURCES = tools/hex2hcd.c tools/missing.h tools_mpris_proxy_SOURCES = tools/mpris-proxy.c tools_mpris_proxy_LDADD = gdbus/libgdbus-internal.la $(GLIB_LIBS) $(DBUS_LIBS) if SYSTEMD systemduserunit_DATA += tools/mpris-proxy.service endif tools_gatt_service_SOURCES = tools/gatt-service.c tools_gatt_service_LDADD = gdbus/libgdbus-internal.la \ src/libshared-mainloop.la $(GLIB_LIBS) $(DBUS_LIBS) tools_isotest_LDADD = lib/libbluetooth-internal.la profiles_iap_iapd_SOURCES = profiles/iap/main.c profiles_iap_iapd_LDADD = gdbus/libgdbus-internal.la $(GLIB_LIBS) $(DBUS_LIBS) if MANPAGES man_MANS += tools/rctest.1 tools/l2ping.1 tools/btattach.1 tools/isotest.1 \ tools/btmgmt.1 client/bluetoothctl.1 \ client/bluetoothctl-mgmt.1 \ client/bluetoothctl-monitor.1 client/bluetoothctl-admin.1 \ client/bluetoothctl-advertise.1 client/bluetoothctl-endpoint.1 \ client/bluetoothctl-gatt.1 client/bluetoothctl-player.1 \ client/bluetoothctl-scan.1 client/bluetoothctl-transport.1 \ client/bluetoothctl-assistant.1 client/bluetoothctl-hci.1 endif if MESH if DEPRECATED bin_PROGRAMS += tools/meshctl tools_meshctl_SOURCES = tools/meshctl.c \ tools/mesh/agent.h tools/mesh/agent.c \ tools/mesh/config-model.h\ tools/mesh-gatt/mesh-net.h \ tools/mesh-gatt/node.h tools/mesh-gatt/node.c \ tools/mesh-gatt/gatt.h tools/mesh-gatt/gatt.c \ tools/mesh-gatt/crypto.h\ tools/mesh-gatt/crypto.c \ tools/mesh-gatt/keys.h \ tools/mesh-gatt/net.h tools/mesh-gatt/net.c \ tools/mesh-gatt/prov.h tools/mesh-gatt/prov.c \ tools/mesh-gatt/util.h tools/mesh-gatt/util.c \ tools/mesh-gatt/prov-db.h \ tools/mesh-gatt/prov-db.c \ tools/mesh-gatt/config-client.c \ tools/mesh-gatt/config-server.c \ tools/mesh-gatt/onoff-model.h \ tools/mesh-gatt/onoff-model.c tools_meshctl_LDADD = gdbus/libgdbus-internal.la src/libshared-glib.la \ lib/libbluetooth-internal.la \ $(GLIB_LIBS) $(DBUS_LIBS) -ljson-c -lreadline EXTRA_DIST += tools/mesh-gatt/local_node.json tools/mesh-gatt/prov_db.json endif bin_PROGRAMS += tools/mesh-cfgclient tools_mesh_cfgclient_SOURCES = tools/mesh-cfgclient.c \ tools/mesh/model.h tools/mesh/config-model.h \ tools/mesh/cfgcli.h tools/mesh/cfgcli.c \ tools/mesh/keys.h tools/mesh/keys.c \ tools/mesh/util.h tools/mesh/util.c \ tools/mesh/remote.h tools/mesh/remote.c \ tools/mesh/agent.h tools/mesh/agent.c \ tools/mesh/mesh-db.h tools/mesh/mesh-db.c \ mesh/util.h mesh/util.c \ mesh/crypto.h mesh/crypto.c tools_mesh_cfgclient_LDADD = lib/libbluetooth-internal.la src/libshared-ell.la \ $(ell_ldadd) -ljson-c -lreadline bin_PROGRAMS += tools/mesh-cfgtest tools_mesh_cfgtest_SOURCES = tools/mesh-cfgtest.c tools_mesh_cfgtest_LDADD = lib/libbluetooth-internal.la src/libshared-ell.la \ $(ell_ldadd) endif if DEPRECATED bin_PROGRAMS += tools/hciattach tools/hciconfig tools/hcitool tools/hcidump \ tools/rfcomm tools/sdptool tools/ciptool tools_hciattach_SOURCES = tools/hciattach.c tools/hciattach.h \ tools/hciattach_st.c \ tools/hciattach_ti.c \ tools/hciattach_tialt.c \ tools/hciattach_ath3k.c \ tools/hciattach_qualcomm.c \ tools/hciattach_intel.c \ tools/hciattach_bcm43xx.c tools_hciattach_LDADD = lib/libbluetooth-internal.la tools_hciconfig_SOURCES = tools/hciconfig.c tools_hciconfig_LDADD = lib/libbluetooth-internal.la tools_hcitool_SOURCES = tools/hcitool.c src/oui.h src/oui.c tools_hcitool_LDADD = lib/libbluetooth-internal.la $(UDEV_LIBS) tools_hcidump_SOURCES = tools/hcidump.c \ tools/parser/parser.h tools/parser/parser.c \ tools/parser/lmp.c \ tools/parser/hci.c \ tools/parser/l2cap.h tools/parser/l2cap.c \ tools/parser/smp.c \ tools/parser/att.c \ tools/parser/sdp.h tools/parser/sdp.c \ tools/parser/rfcomm.h tools/parser/rfcomm.c \ tools/parser/bnep.c \ tools/parser/cmtp.c \ tools/parser/hidp.c \ tools/parser/hcrp.c \ tools/parser/avdtp.c \ tools/parser/avctp.c \ tools/parser/avrcp.c \ tools/parser/sap.c \ tools/parser/obex.c \ tools/parser/capi.c \ tools/parser/ppp.c \ tools/parser/tcpip.c \ tools/parser/ericsson.c \ tools/parser/csr.c \ tools/parser/bpa.c tools_sdptool_SOURCES = tools/sdptool.c src/sdp-xml.h src/sdp-xml.c tools_sdptool_LDADD = lib/libbluetooth-internal.la $(GLIB_LIBS) tools_ciptool_LDADD = lib/libbluetooth-internal.la tools_hcidump_LDADD = lib/libbluetooth-internal.la tools_rfcomm_LDADD = lib/libbluetooth-internal.la if MANPAGES man_MANS += tools/hciattach.1 tools/hciconfig.1 \ tools/hcitool.1 tools/hcidump.1 \ tools/rfcomm.1 tools/sdptool.1 tools/ciptool.1 endif endif endif manual_pages += tools/hciattach.1 tools/hciconfig.1 \ tools/hcitool.1 tools/hcidump.1 \ tools/rfcomm.1 tools/sdptool.1 tools/ciptool.1 \ tools/rctest.1 tools/l2ping.1 tools/btattach.1 \ tools/bdaddr.1 tools/isotest.1 tools/btmgmt.1 \ client/bluetoothctl.1 \ client/bluetoothctl-mgmt.1 \ client/bluetoothctl-monitor.1 \ client/bluetoothctl-admin.1 \ client/bluetoothctl-advertise.1 \ client/bluetoothctl-endpoint.1 \ client/bluetoothctl-gatt.1 \ client/bluetoothctl-player.1 \ client/bluetoothctl-scan.1 \ client/bluetoothctl-transport.1 \ client/bluetoothctl-assistant.1 \ client/bluetoothctl-hci.1 if HID2HCI udevdir = $(UDEV_DIR) udev_PROGRAMS = tools/hid2hci tools_hid2hci_LDADD = $(UDEV_LIBS) if MANPAGES man_MANS += tools/hid2hci.1 endif endif manual_pages += tools/hid2hci.1 if READLINE noinst_PROGRAMS += tools/btmgmt tools/obex-client-tool tools/obex-server-tool \ tools/bluetooth-player tools/obexctl tools_obex_client_tool_SOURCES = $(gobex_sources) $(btio_sources) \ tools/obex-client-tool.c tools_obex_client_tool_LDADD = lib/libbluetooth-internal.la \ src/libshared-glib.la $(GLIB_LIBS) -lreadline tools_obex_server_tool_SOURCES = $(gobex_sources) $(btio_sources) \ tools/obex-server-tool.c tools_obex_server_tool_LDADD = lib/libbluetooth-internal.la \ src/libshared-glib.la $(GLIB_LIBS) tools_bluetooth_player_SOURCES = tools/bluetooth-player.c client/print.c \ client/player.c tools_bluetooth_player_LDADD = gdbus/libgdbus-internal.la \ src/libshared-glib.la \ $(GLIB_LIBS) $(DBUS_LIBS) -lreadline tools_obexctl_SOURCES = tools/obexctl.c tools_obexctl_LDADD = gdbus/libgdbus-internal.la src/libshared-glib.la \ $(GLIB_LIBS) $(DBUS_LIBS) -lreadline tools_btmgmt_SOURCES = tools/btmgmt.c src/uuid-helper.c client/display.c \ client/mgmt.c tools_btmgmt_LDADD = lib/libbluetooth-internal.la src/libshared-mainloop.la \ -lreadline if DEPRECATED noinst_PROGRAMS += attrib/gatttool attrib_gatttool_SOURCES = attrib/gatttool.c attrib/att.c attrib/gatt.c \ attrib/gattrib.c btio/btio.c \ attrib/gatttool.h attrib/interactive.c \ attrib/utils.c src/log.c client/display.c \ client/display.h attrib_gatttool_LDADD = lib/libbluetooth-internal.la \ src/libshared-glib.la $(GLIB_LIBS) -lreadline endif endif if CUPS if CUPS_SERVERBIN cupsdir = $(CUPS_SERVERBIN)/backend else cupsdir = $(libdir)/cups/backend endif cups_PROGRAMS = profiles/cups/bluetooth profiles_cups_bluetooth_SOURCES = profiles/cups/main.c \ profiles/cups/cups.h \ profiles/cups/sdp.c \ profiles/cups/spp.c \ profiles/cups/hcrp.c profiles_cups_bluetooth_LDADD = $(GLIB_LIBS) $(DBUS_LIBS) \ lib/libbluetooth-internal.la \ gdbus/libgdbus-internal.la endif test_scripts += test/sap_client.py test/bluezutils.py \ test/dbusdef.py test/monitor-bluetooth test/list-devices \ test/test-discovery test/test-manager test/test-adapter \ test/test-device test/simple-agent \ test/simple-endpoint test/test-sap-server \ test/test-network test/test-profile test/test-health \ test/test-health-sink test/service-record.dtd \ test/service-did.xml test/service-spp.xml test/service-opp.xml \ test/service-ftp.xml test/simple-player test/test-nap \ test/test-hfp test/opp-client test/ftp-client \ test/pbap-client test/map-client test/example-advertisement \ test/example-gatt-server test/example-gatt-client \ test/test-gatt-profile test/test-mesh test/agent.py if BTPCLIENT noinst_PROGRAMS += tools/btpclient tools/btpclientctl tools_btpclient_SOURCES = tools/btpclient.c src/shared/btp.c src/shared/btp.h tools_btpclient_LDADD = lib/libbluetooth-internal.la \ src/libshared-ell.la $(ell_ldadd) tools/btpclient.$(OBJEXT): src/libshared-ell.la ell/internal tools_btpclientctl_SOURCES = tools/btpclientctl.c client/display.c tools_btpclientctl_LDADD = src/libshared-mainloop.la src/libshared-glib.la \ lib/libbluetooth-internal.la -lreadline endif bluez-5.82/PaxHeaders/config.h.in0000644000000000000000000000005014773211366013703 xustar0020 atime=1743590167 20 ctime=1743591275 bluez-5.82/config.h.in0000644000000000000000000001120214773211366013360 0ustar00rootroot/* config.h.in. Generated from configure.ac by autoheader. */ /* Directory for the Android daemon storage files */ #undef ANDROID_STORAGEDIR /* Directory for the configuration files */ #undef CONFIGDIR /* Define if external plugin support is required */ #undef EXTERNAL_PLUGINS /* Define to 1 if you have A2DP support. */ #undef HAVE_A2DP /* Define to 1 if you have ASHA support. */ #undef HAVE_ASHA /* Define to 1 if you have AVRCP support. */ #undef HAVE_AVRCP /* Define to 1 if you have the backtrace support. */ #undef HAVE_BACKTRACE_SUPPORT /* Define to 1 if you have the declaration of 'basename', and to 0 if you don't. */ #undef HAVE_DECL_BASENAME /* Define to 1 if you have the declaration of 'SCM_TSTAMP_COMPLETION', and to 0 if you don't. */ #undef HAVE_DECL_SCM_TSTAMP_COMPLETION /* Define to 1 if you have the declaration of 'SOF_TIMESTAMPING_TX_COMPLETION', and to 0 if you don't. */ #undef HAVE_DECL_SOF_TIMESTAMPING_TX_COMPLETION /* Define to 1 if you have the header file. */ #undef HAVE_DLFCN_H /* Define to 1 if you have the 'explicit_bzero' function. */ #undef HAVE_EXPLICIT_BZERO /* Define to 1 if you have the 'getrandom' function. */ #undef HAVE_GETRANDOM /* Define to 1 if you have the header file. */ #undef HAVE_INTTYPES_H /* Define to 1 if you have the 'asan' library (-lasan). */ #undef HAVE_LIBASAN /* Define to 1 if you have the 'lsan' library (-llsan). */ #undef HAVE_LIBLSAN /* Define to 1 if you have the 'ubsan' library (-lubsan). */ #undef HAVE_LIBUBSAN /* Define to 1 if you have the header file. */ #undef HAVE_LINUX_IF_ALG_H /* Define to 1 if you have the header file. */ #undef HAVE_LINUX_TYPES_H /* Define to 1 if you have the header file. */ #undef HAVE_LINUX_UHID_H /* Define to 1 if you have the header file. */ #undef HAVE_LINUX_UINPUT_H /* Define to 1 if you have the 'rawmemchr' function. */ #undef HAVE_RAWMEMCHR /* Define to 1 if you have the header file. */ #undef HAVE_READLINE_READLINE_H /* Define to 1 if you have the header file. */ #undef HAVE_STDINT_H /* Define to 1 if you have the header file. */ #undef HAVE_STDIO_H /* Define to 1 if you have the header file. */ #undef HAVE_STDLIB_H /* Define to 1 if you have the header file. */ #undef HAVE_STRINGS_H /* Define to 1 if you have the header file. */ #undef HAVE_STRING_H /* Define to 1 if you have the header file. */ #undef HAVE_SYS_RANDOM_H /* Define to 1 if you have the header file. */ #undef HAVE_SYS_STAT_H /* Define to 1 if you have the header file. */ #undef HAVE_SYS_TYPES_H /* Define to 1 if udev is required */ #undef HAVE_UDEV /* Define to 1 if you have the header file. */ #undef HAVE_UNISTD_H /* Define to 1 if you have the header file. */ #undef HAVE_VALGRIND_MEMCHECK_H /* Define to the sub-directory where libtool stores uninstalled libraries. */ #undef LT_OBJDIR /* Directory for the mesh daemon storage files */ #undef MESH_STORAGEDIR /* Define if threading support is required */ #undef NEED_THREADS /* Name of package */ #undef PACKAGE /* Define to the address where bug reports for this package should be sent. */ #undef PACKAGE_BUGREPORT /* Define to the full name of this package. */ #undef PACKAGE_NAME /* Define to the full name and version of this package. */ #undef PACKAGE_STRING /* Define to the one symbol short name of this package. */ #undef PACKAGE_TARNAME /* Define to the home page for this package. */ #undef PACKAGE_URL /* Define to the version of this package. */ #undef PACKAGE_VERSION /* Define to 1 if all of the C89 standard headers exist (not just the ones required in a freestanding environment). This macro is provided for backward compatibility; new code need not use it. */ #undef STDC_HEADERS /* Directory for the storage files */ #undef STORAGEDIR /* Version number of package */ #undef VERSION /* Define to the equivalent of the C99 'restrict' keyword, or to nothing if this is not supported. Do not define if restrict is supported only directly. */ #undef restrict /* Work around a bug in older versions of Sun C++, which did not #define __restrict__ or support _Restrict or __restrict__ even though the corresponding Sun C compiler ended up with "#define restrict _Restrict" or "#define restrict __restrict__" in the previous line. This workaround can be removed once we assume Oracle Developer Studio 12.5 (2016) or later. */ #if defined __SUNPRO_CC && !defined __RESTRICT && !defined __restrict__ # define _Restrict # define __restrict__ #endif bluez-5.82/PaxHeaders/btio0000644000000000000000000000005014773213556012543 xustar0020 atime=1743591291 20 ctime=1743591278 bluez-5.82/btio/0000755000000000000000000000000014773213556012301 5ustar00rootrootbluez-5.82/btio/PaxHeaders/btio.c0000644000000000000000000000005014766002272013712 xustar0020 atime=1743515577 20 ctime=1743591278 bluez-5.82/btio/btio.c0000644000000000000000000013434514766002272013405 0ustar00rootroot// SPDX-License-Identifier: GPL-2.0-or-later /* * * BlueZ - Bluetooth protocol stack for Linux * * Copyright (C) 2009-2010 Marcel Holtmann * Copyright (C) 2009-2010 Nokia Corporation * Copyright 2023-2025 NXP * * */ #ifdef HAVE_CONFIG_H #include #endif #include #include #include #include #include #include #include #include #include #include "lib/bluetooth.h" #include "lib/l2cap.h" #include "lib/rfcomm.h" #include "lib/sco.h" #include "lib/iso.h" #include "btio.h" #ifndef BT_FLUSHABLE #define BT_FLUSHABLE 8 #endif #define ERROR_FAILED(gerr, str, err) \ g_set_error(gerr, BT_IO_ERROR, err, \ str ": %s (%d)", strerror(err), err) #define DEFAULT_DEFER_TIMEOUT 30 typedef enum { BT_IO_L2CAP, BT_IO_RFCOMM, BT_IO_SCO, BT_IO_ISO, BT_IO_INVALID, } BtIOType; struct set_opts { bdaddr_t src; bdaddr_t dst; BtIOType type; uint8_t src_type; uint8_t dst_type; int defer; int sec_level; uint8_t channel; uint16_t psm; uint16_t cid; uint16_t mtu; int imtu; uint16_t omtu; int central; uint8_t mode; int flushable; uint32_t priority; uint16_t voice; struct bt_iso_qos qos; struct bt_iso_base base; uint8_t bc_sid; uint8_t bc_num_bis; uint8_t bc_bis[ISO_MAX_NUM_BIS]; }; struct connect { BtIOConnect connect; gpointer user_data; GDestroyNotify destroy; bdaddr_t dst; }; struct accept { BtIOConnect connect; gpointer user_data; GDestroyNotify destroy; }; struct server { BtIOConnect connect; BtIOConfirm confirm; gpointer user_data; GDestroyNotify destroy; }; static BtIOType bt_io_get_type(GIOChannel *io, GError **gerr) { int sk = g_io_channel_unix_get_fd(io); int domain, proto, err; socklen_t len; domain = 0; len = sizeof(domain); err = getsockopt(sk, SOL_SOCKET, SO_DOMAIN, &domain, &len); if (err < 0) { ERROR_FAILED(gerr, "getsockopt(SO_DOMAIN)", errno); return BT_IO_INVALID; } if (domain != AF_BLUETOOTH) { g_set_error(gerr, BT_IO_ERROR, EINVAL, "BtIO socket domain not AF_BLUETOOTH"); return BT_IO_INVALID; } proto = 0; len = sizeof(proto); err = getsockopt(sk, SOL_SOCKET, SO_PROTOCOL, &proto, &len); if (err < 0) { ERROR_FAILED(gerr, "getsockopt(SO_PROTOCOL)", errno); return BT_IO_INVALID; } switch (proto) { case BTPROTO_RFCOMM: return BT_IO_RFCOMM; case BTPROTO_SCO: return BT_IO_SCO; case BTPROTO_L2CAP: return BT_IO_L2CAP; case BTPROTO_ISO: return BT_IO_ISO; default: g_set_error(gerr, BT_IO_ERROR, EINVAL, "Unknown BtIO socket type"); return BT_IO_INVALID; } } static void server_remove(struct server *server) { if (server->destroy) server->destroy(server->user_data); g_free(server); } static void connect_remove(struct connect *conn) { if (conn->destroy) conn->destroy(conn->user_data); g_free(conn); } static void accept_remove(struct accept *accept) { if (accept->destroy) accept->destroy(accept->user_data); g_free(accept); } static gboolean check_nval(GIOChannel *io) { struct pollfd fds; memset(&fds, 0, sizeof(fds)); fds.fd = g_io_channel_unix_get_fd(io); fds.events = POLLNVAL; if (poll(&fds, 1, 0) > 0 && (fds.revents & POLLNVAL)) return TRUE; return FALSE; } static gboolean accept_cb(GIOChannel *io, GIOCondition cond, gpointer user_data) { struct accept *accept = user_data; GError *gerr = NULL; /* If the user aborted this accept attempt */ if ((cond & G_IO_NVAL) || check_nval(io)) return FALSE; if (cond & (G_IO_HUP | G_IO_ERR)) { int err, sk_err, sock = g_io_channel_unix_get_fd(io); socklen_t len = sizeof(sk_err); if (getsockopt(sock, SOL_SOCKET, SO_ERROR, &sk_err, &len) < 0) err = -errno; else err = -sk_err; if (err < 0) ERROR_FAILED(&gerr, "HUP or ERR on socket", -err); } accept->connect(io, gerr, accept->user_data); g_clear_error(&gerr); return FALSE; } static gboolean connect_cb(GIOChannel *io, GIOCondition cond, gpointer user_data) { struct connect *conn = user_data; GError *gerr = NULL; int err, sk_err, sock; socklen_t len = sizeof(sk_err); char addr[18]; /* If the user aborted this connect attempt */ if ((cond & G_IO_NVAL) || check_nval(io)) return FALSE; sock = g_io_channel_unix_get_fd(io); if (getsockopt(sock, SOL_SOCKET, SO_ERROR, &sk_err, &len) < 0) err = -errno; else err = -sk_err; if (err < 0) { ba2str(&conn->dst, addr); g_set_error(&gerr, BT_IO_ERROR, -err, "connect to %s: %s (%d)", addr, strerror(-err), -err); } conn->connect(io, gerr, conn->user_data); g_clear_error(&gerr); return FALSE; } static gboolean server_cb(GIOChannel *io, GIOCondition cond, gpointer user_data) { struct server *server = user_data; int srv_sock, cli_sock; GIOChannel *cli_io; /* If the user closed the server */ if ((cond & G_IO_NVAL) || check_nval(io)) return FALSE; srv_sock = g_io_channel_unix_get_fd(io); cli_sock = accept(srv_sock, NULL, NULL); if (cli_sock < 0) return TRUE; cli_io = g_io_channel_unix_new(cli_sock); g_io_channel_set_close_on_unref(cli_io, TRUE); g_io_channel_set_flags(cli_io, G_IO_FLAG_NONBLOCK, NULL); if (server->confirm) server->confirm(cli_io, server->user_data); else server->connect(cli_io, NULL, server->user_data); g_io_channel_unref(cli_io); return TRUE; } static void server_add(GIOChannel *io, BtIOConnect connect, BtIOConfirm confirm, gpointer user_data, GDestroyNotify destroy) { struct server *server; GIOCondition cond; server = g_new0(struct server, 1); server->connect = connect; server->confirm = confirm; server->user_data = user_data; server->destroy = destroy; cond = G_IO_IN | G_IO_ERR | G_IO_HUP | G_IO_NVAL; g_io_add_watch_full(io, G_PRIORITY_HIGH, cond, server_cb, server, (GDestroyNotify) server_remove); } static void connect_add(GIOChannel *io, BtIOConnect connect, bdaddr_t dst, gpointer user_data, GDestroyNotify destroy) { struct connect *conn; GIOCondition cond; conn = g_new0(struct connect, 1); conn->connect = connect; conn->user_data = user_data; conn->destroy = destroy; conn->dst = dst; cond = G_IO_OUT | G_IO_ERR | G_IO_HUP | G_IO_NVAL; g_io_add_watch_full(io, G_PRIORITY_HIGH, cond, connect_cb, conn, (GDestroyNotify) connect_remove); } static void accept_add(GIOChannel *io, BtIOConnect connect, gpointer user_data, GDestroyNotify destroy) { struct accept *accept; GIOCondition cond; accept = g_new0(struct accept, 1); accept->connect = connect; accept->user_data = user_data; accept->destroy = destroy; cond = G_IO_OUT | G_IO_ERR | G_IO_HUP | G_IO_NVAL; g_io_add_watch_full(io, G_PRIORITY_HIGH, cond, accept_cb, accept, (GDestroyNotify) accept_remove); } static int l2cap_bind(int sock, const bdaddr_t *src, uint8_t src_type, uint16_t psm, uint16_t cid, GError **err) { struct sockaddr_l2 addr; memset(&addr, 0, sizeof(addr)); addr.l2_family = AF_BLUETOOTH; bacpy(&addr.l2_bdaddr, src); if (cid) addr.l2_cid = htobs(cid); else addr.l2_psm = htobs(psm); addr.l2_bdaddr_type = src_type; if (bind(sock, (struct sockaddr *) &addr, sizeof(addr)) < 0) { int error = -errno; ERROR_FAILED(err, "l2cap_bind", errno); return error; } return 0; } static int l2cap_connect(int sock, const bdaddr_t *dst, uint8_t dst_type, uint16_t psm, uint16_t cid) { int err; struct sockaddr_l2 addr; memset(&addr, 0, sizeof(addr)); addr.l2_family = AF_BLUETOOTH; bacpy(&addr.l2_bdaddr, dst); if (cid) addr.l2_cid = htobs(cid); else addr.l2_psm = htobs(psm); addr.l2_bdaddr_type = dst_type; err = connect(sock, (struct sockaddr *) &addr, sizeof(addr)); if (err < 0 && !(errno == EAGAIN || errno == EINPROGRESS)) return -errno; return 0; } static int l2cap_set_central(int sock, int central) { int flags; socklen_t len; len = sizeof(flags); if (getsockopt(sock, SOL_L2CAP, L2CAP_LM, &flags, &len) < 0) return -errno; if (central) { if (flags & L2CAP_LM_MASTER) return 0; flags |= L2CAP_LM_MASTER; } else { if (!(flags & L2CAP_LM_MASTER)) return 0; flags &= ~L2CAP_LM_MASTER; } if (setsockopt(sock, SOL_L2CAP, L2CAP_LM, &flags, sizeof(flags)) < 0) return -errno; return 0; } static int rfcomm_set_central(int sock, int central) { int flags; socklen_t len; len = sizeof(flags); if (getsockopt(sock, SOL_RFCOMM, RFCOMM_LM, &flags, &len) < 0) return -errno; if (central) { if (flags & RFCOMM_LM_MASTER) return 0; flags |= RFCOMM_LM_MASTER; } else { if (!(flags & RFCOMM_LM_MASTER)) return 0; flags &= ~RFCOMM_LM_MASTER; } if (setsockopt(sock, SOL_RFCOMM, RFCOMM_LM, &flags, sizeof(flags)) < 0) return -errno; return 0; } static int l2cap_set_lm(int sock, int level) { int lm_map[] = { 0, L2CAP_LM_AUTH, L2CAP_LM_AUTH | L2CAP_LM_ENCRYPT, L2CAP_LM_AUTH | L2CAP_LM_ENCRYPT | L2CAP_LM_SECURE, }, opt = lm_map[level]; if (setsockopt(sock, SOL_L2CAP, L2CAP_LM, &opt, sizeof(opt)) < 0) return -errno; return 0; } static int rfcomm_set_lm(int sock, int level) { int lm_map[] = { 0, RFCOMM_LM_AUTH, RFCOMM_LM_AUTH | RFCOMM_LM_ENCRYPT, RFCOMM_LM_AUTH | RFCOMM_LM_ENCRYPT | RFCOMM_LM_SECURE, }, opt = lm_map[level]; if (setsockopt(sock, SOL_RFCOMM, RFCOMM_LM, &opt, sizeof(opt)) < 0) return -errno; return 0; } static gboolean set_sec_level(int sock, BtIOType type, int level, GError **err) { struct bt_security sec; int ret; if (level < BT_SECURITY_LOW || level > BT_SECURITY_HIGH) { g_set_error(err, BT_IO_ERROR, EINVAL, "Valid security level range is %d-%d", BT_SECURITY_LOW, BT_SECURITY_HIGH); return FALSE; } memset(&sec, 0, sizeof(sec)); sec.level = level; if (setsockopt(sock, SOL_BLUETOOTH, BT_SECURITY, &sec, sizeof(sec)) == 0) return TRUE; if (errno != ENOPROTOOPT) { ERROR_FAILED(err, "setsockopt(BT_SECURITY)", errno); return FALSE; } if (type == BT_IO_L2CAP) ret = l2cap_set_lm(sock, level); else ret = rfcomm_set_lm(sock, level); if (ret < 0) { ERROR_FAILED(err, "setsockopt(LM)", -ret); return FALSE; } return TRUE; } static int l2cap_get_lm(int sock, int *sec_level) { int opt; socklen_t len; len = sizeof(opt); if (getsockopt(sock, SOL_L2CAP, L2CAP_LM, &opt, &len) < 0) return -errno; *sec_level = 0; if (opt & L2CAP_LM_AUTH) *sec_level = BT_SECURITY_LOW; if (opt & L2CAP_LM_ENCRYPT) *sec_level = BT_SECURITY_MEDIUM; if (opt & L2CAP_LM_SECURE) *sec_level = BT_SECURITY_HIGH; return 0; } static int rfcomm_get_lm(int sock, int *sec_level) { int opt; socklen_t len; len = sizeof(opt); if (getsockopt(sock, SOL_RFCOMM, RFCOMM_LM, &opt, &len) < 0) return -errno; *sec_level = 0; if (opt & RFCOMM_LM_AUTH) *sec_level = BT_SECURITY_LOW; if (opt & RFCOMM_LM_ENCRYPT) *sec_level = BT_SECURITY_MEDIUM; if (opt & RFCOMM_LM_SECURE) *sec_level = BT_SECURITY_HIGH; return 0; } static gboolean get_sec_level(int sock, BtIOType type, int *level, GError **err) { struct bt_security sec; socklen_t len; int ret; memset(&sec, 0, sizeof(sec)); len = sizeof(sec); if (getsockopt(sock, SOL_BLUETOOTH, BT_SECURITY, &sec, &len) == 0) { *level = sec.level; return TRUE; } if (errno != ENOPROTOOPT) { ERROR_FAILED(err, "getsockopt(BT_SECURITY)", errno); return FALSE; } if (type == BT_IO_L2CAP) ret = l2cap_get_lm(sock, level); else ret = rfcomm_get_lm(sock, level); if (ret < 0) { ERROR_FAILED(err, "getsockopt(LM)", -ret); return FALSE; } return TRUE; } static int l2cap_set_flushable(int sock, gboolean flushable) { int f; f = flushable; if (setsockopt(sock, SOL_BLUETOOTH, BT_FLUSHABLE, &f, sizeof(f)) < 0) return -errno; return 0; } static int set_priority(int sock, uint32_t prio) { if (setsockopt(sock, SOL_SOCKET, SO_PRIORITY, &prio, sizeof(prio)) < 0) return -errno; return 0; } static gboolean get_key_size(int sock, int *size, GError **err) { struct bt_security sec; socklen_t len; memset(&sec, 0, sizeof(sec)); len = sizeof(sec); if (getsockopt(sock, SOL_BLUETOOTH, BT_SECURITY, &sec, &len) == 0) { *size = sec.key_size; return TRUE; } return FALSE; } static uint8_t mode_l2mode(uint8_t mode) { switch (mode) { case BT_IO_MODE_BASIC: return L2CAP_MODE_BASIC; case BT_IO_MODE_ERTM: return L2CAP_MODE_ERTM; case BT_IO_MODE_STREAMING: return L2CAP_MODE_STREAMING; default: return UINT8_MAX; } } static gboolean set_l2opts(int sock, int imtu, uint16_t omtu, uint8_t mode, GError **err) { struct l2cap_options l2o; socklen_t len; memset(&l2o, 0, sizeof(l2o)); len = sizeof(l2o); if (getsockopt(sock, SOL_L2CAP, L2CAP_OPTIONS, &l2o, &len) < 0) { ERROR_FAILED(err, "getsockopt(L2CAP_OPTIONS)", errno); return FALSE; } if (imtu != -1) l2o.imtu = imtu; if (omtu) l2o.omtu = omtu; if (mode) { l2o.mode = mode_l2mode(mode); if (l2o.mode == UINT8_MAX) { ERROR_FAILED(err, "Unsupported mode", errno); return FALSE; } } if (setsockopt(sock, SOL_L2CAP, L2CAP_OPTIONS, &l2o, sizeof(l2o)) < 0) { ERROR_FAILED(err, "setsockopt(L2CAP_OPTIONS)", errno); return FALSE; } return TRUE; } static gboolean set_le_imtu(int sock, uint16_t imtu, GError **err) { if (setsockopt(sock, SOL_BLUETOOTH, BT_RCVMTU, &imtu, sizeof(imtu)) < 0) { ERROR_FAILED(err, "setsockopt(BT_RCVMTU)", errno); return FALSE; } return TRUE; } static gboolean set_le_mode(int sock, uint8_t mode, GError **err) { if (setsockopt(sock, SOL_BLUETOOTH, BT_MODE, &mode, sizeof(mode)) < 0) { ERROR_FAILED(err, "setsockopt(BT_MODE)", errno); return FALSE; } return TRUE; } static gboolean l2cap_set(int sock, uint8_t src_type, int sec_level, int imtu, uint16_t omtu, uint8_t mode, int central, int flushable, uint32_t priority, GError **err) { if (imtu != -1 || omtu || mode) { gboolean ret = FALSE; if (src_type == BDADDR_BREDR) { ret = set_l2opts(sock, imtu, omtu, mode, err); /* Back to default behavior in case the first call * fails: it may happen if the used kernel still * doesn't support auto-tuning the MTU. */ if (!ret && !imtu) { /* Free existing error */ g_error_free(*err); ret = set_l2opts(sock, -1, omtu, mode, err); } } else { if (imtu != -1) ret = set_le_imtu(sock, imtu, err); if (ret && mode) ret = set_le_mode(sock, mode, err); } if (!ret) return ret; } if (central >= 0 && l2cap_set_central(sock, central) < 0) { ERROR_FAILED(err, "l2cap_set_central", errno); return FALSE; } if (flushable >= 0 && l2cap_set_flushable(sock, flushable) < 0) { ERROR_FAILED(err, "l2cap_set_flushable", errno); return FALSE; } if (priority > 0 && set_priority(sock, priority) < 0) { ERROR_FAILED(err, "set_priority", errno); return FALSE; } if (sec_level && !set_sec_level(sock, BT_IO_L2CAP, sec_level, err)) return FALSE; return TRUE; } static int rfcomm_bind(int sock, const bdaddr_t *src, uint8_t channel, GError **err) { struct sockaddr_rc addr; memset(&addr, 0, sizeof(addr)); addr.rc_family = AF_BLUETOOTH; bacpy(&addr.rc_bdaddr, src); addr.rc_channel = channel; if (bind(sock, (struct sockaddr *) &addr, sizeof(addr)) < 0) { int error = -errno; ERROR_FAILED(err, "rfcomm_bind", errno); return error; } return 0; } static int rfcomm_connect(int sock, const bdaddr_t *dst, uint8_t channel) { int err; struct sockaddr_rc addr; memset(&addr, 0, sizeof(addr)); addr.rc_family = AF_BLUETOOTH; bacpy(&addr.rc_bdaddr, dst); addr.rc_channel = channel; err = connect(sock, (struct sockaddr *) &addr, sizeof(addr)); if (err < 0 && !(errno == EAGAIN || errno == EINPROGRESS)) return -errno; return 0; } static gboolean rfcomm_set(int sock, int sec_level, int central, GError **err) { if (sec_level && !set_sec_level(sock, BT_IO_RFCOMM, sec_level, err)) return FALSE; if (central >= 0 && rfcomm_set_central(sock, central) < 0) { ERROR_FAILED(err, "rfcomm_set_central", errno); return FALSE; } return TRUE; } static int sco_bind(int sock, const bdaddr_t *src, GError **err) { struct sockaddr_sco addr; memset(&addr, 0, sizeof(addr)); addr.sco_family = AF_BLUETOOTH; bacpy(&addr.sco_bdaddr, src); if (bind(sock, (struct sockaddr *) &addr, sizeof(addr)) < 0) { int error = -errno; ERROR_FAILED(err, "sco_bind", errno); return error; } return 0; } static int iso_bind(int sock, bool server, const bdaddr_t *src, uint8_t src_type, const bdaddr_t *dst, uint8_t dst_type, uint8_t bc_sid, uint8_t num_bis, uint8_t *bis, GError **err) { struct sockaddr_iso *addr = NULL; size_t addr_len; int ret = 0; /* If this is an ISO listener and the destination address * is not BDADDR_ANY, the listener should be bound to the * broadcaster address */ if (server && bacmp(dst, BDADDR_ANY)) addr_len = sizeof(*addr) + sizeof(*addr->iso_bc); else addr_len = sizeof(*addr); addr = malloc(addr_len); if (!addr) return -ENOMEM; memset(addr, 0, addr_len); addr->iso_family = AF_BLUETOOTH; bacpy(&addr->iso_bdaddr, src); addr->iso_bdaddr_type = src_type; if (addr_len > sizeof(*addr)) { bacpy(&addr->iso_bc->bc_bdaddr, dst); addr->iso_bc->bc_bdaddr_type = dst_type; addr->iso_bc->bc_sid = bc_sid; addr->iso_bc->bc_num_bis = num_bis; memcpy(addr->iso_bc->bc_bis, bis, addr->iso_bc->bc_num_bis); } if (!bind(sock, (struct sockaddr *)addr, addr_len)) goto done; ret = -errno; ERROR_FAILED(err, "iso_bind", errno); done: free(addr); return ret; } static int sco_connect(int sock, const bdaddr_t *dst) { struct sockaddr_sco addr; int err; memset(&addr, 0, sizeof(addr)); addr.sco_family = AF_BLUETOOTH; bacpy(&addr.sco_bdaddr, dst); err = connect(sock, (struct sockaddr *) &addr, sizeof(addr)); if (err < 0 && !(errno == EAGAIN || errno == EINPROGRESS)) return -errno; return 0; } static int iso_connect(int sock, const bdaddr_t *dst, uint8_t dst_type) { struct sockaddr_iso addr; int err; memset(&addr, 0, sizeof(addr)); addr.iso_family = AF_BLUETOOTH; bacpy(&addr.iso_bdaddr, dst); addr.iso_bdaddr_type = dst_type; err = connect(sock, (struct sockaddr *) &addr, sizeof(addr)); if (err < 0 && !(errno == EAGAIN || errno == EINPROGRESS)) return -errno; return 0; } static gboolean sco_set(int sock, uint16_t mtu, uint16_t voice, GError **err) { struct sco_options sco_opt; struct bt_voice bt_voice; socklen_t len; if (!mtu) goto voice; len = sizeof(sco_opt); memset(&sco_opt, 0, len); if (getsockopt(sock, SOL_SCO, SCO_OPTIONS, &sco_opt, &len) < 0) { ERROR_FAILED(err, "getsockopt(SCO_OPTIONS)", errno); return FALSE; } sco_opt.mtu = mtu; if (setsockopt(sock, SOL_SCO, SCO_OPTIONS, &sco_opt, sizeof(sco_opt)) < 0) { ERROR_FAILED(err, "setsockopt(SCO_OPTIONS)", errno); return FALSE; } voice: if (!voice) return TRUE; memset(&bt_voice, 0, sizeof(bt_voice)); bt_voice.setting = voice; if (setsockopt(sock, SOL_BLUETOOTH, BT_VOICE, &bt_voice, sizeof(bt_voice)) < 0) { ERROR_FAILED(err, "setsockopt(BT_VOICE)", errno); return FALSE; } return TRUE; } static gboolean iso_set_qos(int sock, struct bt_iso_qos *qos, GError **err) { if (setsockopt(sock, SOL_BLUETOOTH, BT_ISO_QOS, qos, sizeof(*qos)) < 0) { ERROR_FAILED(err, "setsockopt(BT_ISO_QOS)", errno); return FALSE; } return TRUE; } static gboolean iso_set_base(int sock, struct bt_iso_base *base, GError **err) { if (setsockopt(sock, SOL_BLUETOOTH, BT_ISO_BASE, base->base, base->base_len) < 0) { ERROR_FAILED(err, "setsockopt(BT_ISO_BASE)", errno); return FALSE; } return TRUE; } static gboolean parse_set_opts(struct set_opts *opts, GError **err, BtIOOption opt1, va_list args) { BtIOOption opt = opt1; const char *str; memset(opts, 0, sizeof(*opts)); /* Set defaults */ opts->type = BT_IO_SCO; opts->defer = DEFAULT_DEFER_TIMEOUT; opts->central = -1; opts->mode = L2CAP_MODE_BASIC; opts->flushable = -1; opts->priority = 0; opts->src_type = BDADDR_BREDR; opts->dst_type = BDADDR_BREDR; opts->imtu = -1; while (opt != BT_IO_OPT_INVALID) { switch (opt) { case BT_IO_OPT_SOURCE: str = va_arg(args, const char *); str2ba(str, &opts->src); break; case BT_IO_OPT_SOURCE_BDADDR: bacpy(&opts->src, va_arg(args, const bdaddr_t *)); break; case BT_IO_OPT_SOURCE_TYPE: opts->src_type = va_arg(args, int); break; case BT_IO_OPT_DEST: str2ba(va_arg(args, const char *), &opts->dst); break; case BT_IO_OPT_DEST_BDADDR: bacpy(&opts->dst, va_arg(args, const bdaddr_t *)); break; case BT_IO_OPT_DEST_TYPE: opts->dst_type = va_arg(args, int); break; case BT_IO_OPT_DEFER_TIMEOUT: opts->defer = va_arg(args, int); break; case BT_IO_OPT_SEC_LEVEL: opts->sec_level = va_arg(args, int); break; case BT_IO_OPT_CHANNEL: opts->type = BT_IO_RFCOMM; opts->channel = va_arg(args, int); break; case BT_IO_OPT_PSM: opts->type = BT_IO_L2CAP; opts->psm = va_arg(args, int); break; case BT_IO_OPT_CID: opts->type = BT_IO_L2CAP; opts->cid = va_arg(args, int); break; case BT_IO_OPT_MTU: opts->mtu = va_arg(args, int); opts->imtu = opts->mtu; opts->omtu = opts->mtu; break; case BT_IO_OPT_OMTU: opts->omtu = va_arg(args, int); if (!opts->mtu) opts->mtu = opts->omtu; break; case BT_IO_OPT_IMTU: opts->imtu = va_arg(args, int); if (!opts->mtu) opts->mtu = opts->imtu; break; case BT_IO_OPT_CENTRAL: opts->central = va_arg(args, gboolean); break; case BT_IO_OPT_MODE: opts->mode = va_arg(args, int); if (opts->mode == BT_IO_MODE_ISO) { opts->type = BT_IO_ISO; if (opts->src_type == BDADDR_BREDR) opts->src_type = BDADDR_LE_PUBLIC; if (opts->dst_type == BDADDR_BREDR) opts->dst_type = BDADDR_LE_PUBLIC; } break; case BT_IO_OPT_FLUSHABLE: opts->flushable = va_arg(args, gboolean); break; case BT_IO_OPT_PRIORITY: opts->priority = va_arg(args, int); break; case BT_IO_OPT_VOICE: opts->voice = va_arg(args, int); break; case BT_IO_OPT_QOS: opts->qos = *va_arg(args, struct bt_iso_qos *); break; case BT_IO_OPT_BASE: opts->base = *va_arg(args, struct bt_iso_base *); break; case BT_IO_OPT_ISO_BC_SID: opts->bc_sid = va_arg(args, int); break; case BT_IO_OPT_ISO_BC_NUM_BIS: opts->bc_num_bis = va_arg(args, int); break; case BT_IO_OPT_ISO_BC_BIS: memcpy(opts->bc_bis, va_arg(args, uint8_t *), opts->bc_num_bis); break; case BT_IO_OPT_INVALID: case BT_IO_OPT_KEY_SIZE: case BT_IO_OPT_SOURCE_CHANNEL: case BT_IO_OPT_DEST_CHANNEL: case BT_IO_OPT_HANDLE: case BT_IO_OPT_CLASS: case BT_IO_OPT_PHY: default: g_set_error(err, BT_IO_ERROR, EINVAL, "Unknown option %d", opt); return FALSE; } opt = va_arg(args, int); } return TRUE; } static gboolean get_src(int sock, void *src, socklen_t len, GError **err) { socklen_t olen; memset(src, 0, len); olen = len; if (getsockname(sock, src, &olen) < 0) { ERROR_FAILED(err, "getsockname", errno); return FALSE; } return TRUE; } static gboolean get_dst(int sock, void *dst, socklen_t len, GError **err) { socklen_t olen; memset(dst, 0, len); olen = len; if (getpeername(sock, dst, &olen) < 0) { ERROR_FAILED(err, "getpeername", errno); return FALSE; } return TRUE; } static int l2cap_get_info(int sock, uint16_t *handle, uint8_t *dev_class) { struct l2cap_conninfo info; socklen_t len; len = sizeof(info); if (getsockopt(sock, SOL_L2CAP, L2CAP_CONNINFO, &info, &len) < 0) return -errno; if (handle) *handle = info.hci_handle; if (dev_class) memcpy(dev_class, info.dev_class, 3); return 0; } static int l2cap_get_flushable(int sock, gboolean *flushable) { int f; socklen_t len; f = 0; len = sizeof(f); if (getsockopt(sock, SOL_BLUETOOTH, BT_FLUSHABLE, &f, &len) < 0) return -errno; if (f) *flushable = TRUE; else *flushable = FALSE; return 0; } static int get_priority(int sock, uint32_t *prio) { socklen_t len; len = sizeof(*prio); if (getsockopt(sock, SOL_SOCKET, SO_PRIORITY, prio, &len) < 0) return -errno; return 0; } static int get_phy(int sock, uint32_t *phy) { socklen_t len; len = sizeof(*phy); if (getsockopt(sock, SOL_BLUETOOTH, BT_PHY, phy, &len) < 0) return -errno; return 0; } static int get_le_imtu(int sock, uint16_t *mtu) { socklen_t len; len = sizeof(*mtu); if (getsockopt(sock, SOL_BLUETOOTH, BT_RCVMTU, mtu, &len) < 0) return -errno; return 0; } static int get_le_mode(int sock, uint8_t *mode) { socklen_t len; len = sizeof(*mode); if (getsockopt(sock, SOL_BLUETOOTH, BT_MODE, mode, &len) < 0) return -errno; return 0; } static gboolean l2cap_get(int sock, GError **err, BtIOOption opt1, va_list args) { BtIOOption opt = opt1; struct sockaddr_l2 src, dst; struct l2cap_options l2o; int flags; uint8_t dev_class[3]; uint16_t handle = 0; socklen_t len; gboolean flushable = FALSE, have_dst = FALSE; uint32_t priority, phy; if (!get_src(sock, &src, sizeof(src), err)) return FALSE; memset(&l2o, 0, sizeof(l2o)); if (src.l2_bdaddr_type != BDADDR_BREDR) { if (get_le_imtu(sock, &l2o.imtu) == 0) { /* Older kernels may not support BT_MODE */ get_le_mode(sock, &l2o.mode); goto parse_opts; } /* Non-LE CoC enabled kernels will return one of these * in which case we need to fall back to L2CAP_OPTIONS. */ if (errno != EPROTONOSUPPORT && errno != ENOPROTOOPT) { ERROR_FAILED(err, "getsockopt(BT_RCVMTU)", errno); return FALSE; } } len = sizeof(l2o); if (getsockopt(sock, SOL_L2CAP, L2CAP_OPTIONS, &l2o, &len) < 0) { ERROR_FAILED(err, "getsockopt(L2CAP_OPTIONS)", errno); return FALSE; } parse_opts: while (opt != BT_IO_OPT_INVALID) { switch (opt) { case BT_IO_OPT_SOURCE: ba2str(&src.l2_bdaddr, va_arg(args, char *)); break; case BT_IO_OPT_SOURCE_BDADDR: bacpy(va_arg(args, bdaddr_t *), &src.l2_bdaddr); break; case BT_IO_OPT_DEST: if (!have_dst) have_dst = get_dst(sock, &dst, sizeof(dst), err); if (!have_dst) return FALSE; ba2str(&dst.l2_bdaddr, va_arg(args, char *)); break; case BT_IO_OPT_DEST_BDADDR: if (!have_dst) have_dst = get_dst(sock, &dst, sizeof(dst), err); if (!have_dst) return FALSE; bacpy(va_arg(args, bdaddr_t *), &dst.l2_bdaddr); break; case BT_IO_OPT_DEST_TYPE: if (!have_dst) have_dst = get_dst(sock, &dst, sizeof(dst), err); if (!have_dst) return FALSE; *(va_arg(args, uint8_t *)) = dst.l2_bdaddr_type; break; case BT_IO_OPT_DEFER_TIMEOUT: len = sizeof(int); if (getsockopt(sock, SOL_BLUETOOTH, BT_DEFER_SETUP, va_arg(args, int *), &len) < 0) { ERROR_FAILED(err, "getsockopt(DEFER_SETUP)", errno); return FALSE; } break; case BT_IO_OPT_SEC_LEVEL: if (!get_sec_level(sock, BT_IO_L2CAP, va_arg(args, int *), err)) return FALSE; break; case BT_IO_OPT_KEY_SIZE: if (!get_key_size(sock, va_arg(args, int *), err)) return FALSE; break; case BT_IO_OPT_PSM: if (src.l2_psm) { *(va_arg(args, uint16_t *)) = btohs(src.l2_psm); break; } if (!have_dst) have_dst = get_dst(sock, &dst, sizeof(dst), err); if (!have_dst) return FALSE; *(va_arg(args, uint16_t *)) = btohs(dst.l2_psm); break; case BT_IO_OPT_CID: if (src.l2_cid) { *(va_arg(args, uint16_t *)) = btohs(src.l2_cid); break; } if (!have_dst) have_dst = get_dst(sock, &dst, sizeof(dst), err); if (!have_dst) return FALSE; *(va_arg(args, uint16_t *)) = btohs(dst.l2_cid); break; case BT_IO_OPT_OMTU: if (src.l2_bdaddr_type == BDADDR_BREDR) { *(va_arg(args, uint16_t *)) = l2o.omtu; break; } len = sizeof(l2o.omtu); if (getsockopt(sock, SOL_BLUETOOTH, BT_SNDMTU, &l2o.omtu, &len) < 0) { ERROR_FAILED(err, "getsockopt(BT_SNDMTU)", errno); return FALSE; } *(va_arg(args, uint16_t *)) = l2o.omtu; break; case BT_IO_OPT_IMTU: *(va_arg(args, uint16_t *)) = l2o.imtu; break; case BT_IO_OPT_CENTRAL: len = sizeof(flags); if (getsockopt(sock, SOL_L2CAP, L2CAP_LM, &flags, &len) < 0) { ERROR_FAILED(err, "getsockopt(L2CAP_LM)", errno); return FALSE; } *(va_arg(args, gboolean *)) = (flags & L2CAP_LM_MASTER) ? TRUE : FALSE; break; case BT_IO_OPT_HANDLE: if (l2cap_get_info(sock, &handle, dev_class) < 0) { ERROR_FAILED(err, "L2CAP_CONNINFO", errno); return FALSE; } *(va_arg(args, uint16_t *)) = handle; break; case BT_IO_OPT_CLASS: if (l2cap_get_info(sock, &handle, dev_class) < 0) { ERROR_FAILED(err, "L2CAP_CONNINFO", errno); return FALSE; } memcpy(va_arg(args, uint8_t *), dev_class, 3); break; case BT_IO_OPT_MODE: *(va_arg(args, uint8_t *)) = l2o.mode; break; case BT_IO_OPT_FLUSHABLE: if (l2cap_get_flushable(sock, &flushable) < 0) { ERROR_FAILED(err, "get_flushable", errno); return FALSE; } *(va_arg(args, gboolean *)) = flushable; break; case BT_IO_OPT_PRIORITY: if (get_priority(sock, &priority) < 0) { ERROR_FAILED(err, "get_priority", errno); return FALSE; } *(va_arg(args, uint32_t *)) = priority; break; case BT_IO_OPT_PHY: if (get_phy(sock, &phy) < 0) { ERROR_FAILED(err, "get_phy", errno); return FALSE; } *(va_arg(args, uint32_t *)) = phy; break; case BT_IO_OPT_INVALID: case BT_IO_OPT_SOURCE_TYPE: case BT_IO_OPT_CHANNEL: case BT_IO_OPT_SOURCE_CHANNEL: case BT_IO_OPT_DEST_CHANNEL: case BT_IO_OPT_MTU: case BT_IO_OPT_VOICE: case BT_IO_OPT_QOS: case BT_IO_OPT_BASE: case BT_IO_OPT_ISO_BC_SID: case BT_IO_OPT_ISO_BC_NUM_BIS: case BT_IO_OPT_ISO_BC_BIS: default: g_set_error(err, BT_IO_ERROR, EINVAL, "Unknown option %d", opt); return FALSE; } opt = va_arg(args, int); } return TRUE; } static int rfcomm_get_info(int sock, uint16_t *handle, uint8_t *dev_class) { struct rfcomm_conninfo info; socklen_t len; len = sizeof(info); if (getsockopt(sock, SOL_RFCOMM, RFCOMM_CONNINFO, &info, &len) < 0) return -errno; if (handle) *handle = info.hci_handle; if (dev_class) memcpy(dev_class, info.dev_class, 3); return 0; } static gboolean rfcomm_get(int sock, GError **err, BtIOOption opt1, va_list args) { BtIOOption opt = opt1; struct sockaddr_rc src, dst; gboolean have_dst = FALSE; int flags; socklen_t len; uint8_t dev_class[3]; uint16_t handle = 0; uint32_t phy; if (!get_src(sock, &src, sizeof(src), err)) return FALSE; while (opt != BT_IO_OPT_INVALID) { switch (opt) { case BT_IO_OPT_SOURCE: ba2str(&src.rc_bdaddr, va_arg(args, char *)); break; case BT_IO_OPT_SOURCE_BDADDR: bacpy(va_arg(args, bdaddr_t *), &src.rc_bdaddr); break; case BT_IO_OPT_DEST: if (!have_dst) have_dst = get_dst(sock, &dst, sizeof(dst), err); if (!have_dst) return FALSE; ba2str(&dst.rc_bdaddr, va_arg(args, char *)); break; case BT_IO_OPT_DEST_BDADDR: if (!have_dst) have_dst = get_dst(sock, &dst, sizeof(dst), err); if (!have_dst) return FALSE; bacpy(va_arg(args, bdaddr_t *), &dst.rc_bdaddr); break; case BT_IO_OPT_DEFER_TIMEOUT: len = sizeof(int); if (getsockopt(sock, SOL_BLUETOOTH, BT_DEFER_SETUP, va_arg(args, int *), &len) < 0) { ERROR_FAILED(err, "getsockopt(DEFER_SETUP)", errno); return FALSE; } break; case BT_IO_OPT_SEC_LEVEL: if (!get_sec_level(sock, BT_IO_RFCOMM, va_arg(args, int *), err)) return FALSE; break; case BT_IO_OPT_CHANNEL: if (src.rc_channel) { *(va_arg(args, uint8_t *)) = src.rc_channel; break; } if (!have_dst) have_dst = get_dst(sock, &dst, sizeof(dst), err); if (!have_dst) return FALSE; *(va_arg(args, uint8_t *)) = dst.rc_channel; break; case BT_IO_OPT_SOURCE_CHANNEL: *(va_arg(args, uint8_t *)) = src.rc_channel; break; case BT_IO_OPT_DEST_CHANNEL: if (!have_dst) have_dst = get_dst(sock, &dst, sizeof(dst), err); if (!have_dst) return FALSE; *(va_arg(args, uint8_t *)) = dst.rc_channel; break; case BT_IO_OPT_CENTRAL: len = sizeof(flags); if (getsockopt(sock, SOL_RFCOMM, RFCOMM_LM, &flags, &len) < 0) { ERROR_FAILED(err, "getsockopt(RFCOMM_LM)", errno); return FALSE; } *(va_arg(args, gboolean *)) = (flags & RFCOMM_LM_MASTER) ? TRUE : FALSE; break; case BT_IO_OPT_HANDLE: if (rfcomm_get_info(sock, &handle, dev_class) < 0) { ERROR_FAILED(err, "RFCOMM_CONNINFO", errno); return FALSE; } *(va_arg(args, uint16_t *)) = handle; break; case BT_IO_OPT_CLASS: if (rfcomm_get_info(sock, &handle, dev_class) < 0) { ERROR_FAILED(err, "RFCOMM_CONNINFO", errno); return FALSE; } memcpy(va_arg(args, uint8_t *), dev_class, 3); break; case BT_IO_OPT_PHY: if (get_phy(sock, &phy) < 0) { ERROR_FAILED(err, "get_phy", errno); return FALSE; } *(va_arg(args, uint32_t *)) = phy; break; case BT_IO_OPT_SOURCE_TYPE: case BT_IO_OPT_DEST_TYPE: case BT_IO_OPT_KEY_SIZE: case BT_IO_OPT_PSM: case BT_IO_OPT_CID: case BT_IO_OPT_MTU: case BT_IO_OPT_OMTU: case BT_IO_OPT_IMTU: case BT_IO_OPT_MODE: case BT_IO_OPT_FLUSHABLE: case BT_IO_OPT_PRIORITY: case BT_IO_OPT_VOICE: case BT_IO_OPT_QOS: case BT_IO_OPT_BASE: case BT_IO_OPT_ISO_BC_SID: case BT_IO_OPT_ISO_BC_NUM_BIS: case BT_IO_OPT_ISO_BC_BIS: case BT_IO_OPT_INVALID: default: g_set_error(err, BT_IO_ERROR, EINVAL, "Unknown option %d", opt); return FALSE; } opt = va_arg(args, int); } return TRUE; } static int sco_get_info(int sock, uint16_t *handle, uint8_t *dev_class) { struct sco_conninfo info; socklen_t len; len = sizeof(info); if (getsockopt(sock, SOL_SCO, SCO_CONNINFO, &info, &len) < 0) return -errno; if (handle) *handle = info.hci_handle; if (dev_class) memcpy(dev_class, info.dev_class, 3); return 0; } static gboolean sco_get(int sock, GError **err, BtIOOption opt1, va_list args) { BtIOOption opt = opt1; struct sockaddr_sco src, dst; struct sco_options sco_opt; socklen_t len; uint8_t dev_class[3]; uint16_t handle = 0; uint32_t phy; len = sizeof(sco_opt); memset(&sco_opt, 0, len); if (getsockopt(sock, SOL_SCO, SCO_OPTIONS, &sco_opt, &len) < 0) { ERROR_FAILED(err, "getsockopt(SCO_OPTIONS)", errno); return FALSE; } if (!get_src(sock, &src, sizeof(src), err)) return FALSE; if (!get_dst(sock, &dst, sizeof(dst), err)) return FALSE; while (opt != BT_IO_OPT_INVALID) { switch (opt) { case BT_IO_OPT_SOURCE: ba2str(&src.sco_bdaddr, va_arg(args, char *)); break; case BT_IO_OPT_SOURCE_BDADDR: bacpy(va_arg(args, bdaddr_t *), &src.sco_bdaddr); break; case BT_IO_OPT_DEST: ba2str(&dst.sco_bdaddr, va_arg(args, char *)); break; case BT_IO_OPT_DEST_BDADDR: bacpy(va_arg(args, bdaddr_t *), &dst.sco_bdaddr); break; case BT_IO_OPT_MTU: case BT_IO_OPT_IMTU: case BT_IO_OPT_OMTU: *(va_arg(args, uint16_t *)) = sco_opt.mtu; break; case BT_IO_OPT_HANDLE: if (sco_get_info(sock, &handle, dev_class) < 0) { ERROR_FAILED(err, "SCO_CONNINFO", errno); return FALSE; } *(va_arg(args, uint16_t *)) = handle; break; case BT_IO_OPT_CLASS: if (sco_get_info(sock, &handle, dev_class) < 0) { ERROR_FAILED(err, "SCO_CONNINFO", errno); return FALSE; } memcpy(va_arg(args, uint8_t *), dev_class, 3); break; case BT_IO_OPT_PHY: if (get_phy(sock, &phy) < 0) { ERROR_FAILED(err, "get_phy", errno); return FALSE; } *(va_arg(args, uint32_t *)) = phy; break; case BT_IO_OPT_SOURCE_TYPE: case BT_IO_OPT_DEST_TYPE: case BT_IO_OPT_DEFER_TIMEOUT: case BT_IO_OPT_SEC_LEVEL: case BT_IO_OPT_KEY_SIZE: case BT_IO_OPT_CHANNEL: case BT_IO_OPT_SOURCE_CHANNEL: case BT_IO_OPT_DEST_CHANNEL: case BT_IO_OPT_PSM: case BT_IO_OPT_CID: case BT_IO_OPT_CENTRAL: case BT_IO_OPT_MODE: case BT_IO_OPT_FLUSHABLE: case BT_IO_OPT_PRIORITY: case BT_IO_OPT_VOICE: case BT_IO_OPT_QOS: case BT_IO_OPT_BASE: case BT_IO_OPT_ISO_BC_SID: case BT_IO_OPT_ISO_BC_NUM_BIS: case BT_IO_OPT_ISO_BC_BIS: case BT_IO_OPT_INVALID: default: g_set_error(err, BT_IO_ERROR, EINVAL, "Unknown option %d", opt); return FALSE; } opt = va_arg(args, int); } return TRUE; } static gboolean iso_get(int sock, GError **err, BtIOOption opt1, va_list args) { BtIOOption opt = opt1; struct sockaddr_iso src, dst; struct bt_iso_qos qos; struct bt_iso_base base; socklen_t len; uint32_t phy; len = sizeof(qos); memset(&qos, 0, len); if (getsockopt(sock, SOL_BLUETOOTH, BT_ISO_QOS, &qos, &len) < 0) { ERROR_FAILED(err, "getsockopt(BT_ISO_QOS)", errno); return FALSE; } len = BASE_MAX_LENGTH; if (getsockopt(sock, SOL_BLUETOOTH, BT_ISO_BASE, &base.base, &len) < 0) { ERROR_FAILED(err, "getsockopt(BT_ISO_BASE)", errno); return FALSE; } base.base_len = len; if (!get_src(sock, &src, sizeof(src), err)) return FALSE; if (!get_dst(sock, &dst, sizeof(dst), err)) return FALSE; while (opt != BT_IO_OPT_INVALID) { switch (opt) { case BT_IO_OPT_SOURCE: ba2str(&src.iso_bdaddr, va_arg(args, char *)); break; case BT_IO_OPT_SOURCE_BDADDR: bacpy(va_arg(args, bdaddr_t *), &src.iso_bdaddr); break; case BT_IO_OPT_SOURCE_TYPE: *(va_arg(args, uint8_t *)) = src.iso_bdaddr_type; break; case BT_IO_OPT_DEST: ba2str(&dst.iso_bdaddr, va_arg(args, char *)); break; case BT_IO_OPT_DEST_BDADDR: bacpy(va_arg(args, bdaddr_t *), &dst.iso_bdaddr); break; case BT_IO_OPT_DEST_TYPE: *(va_arg(args, uint8_t *)) = dst.iso_bdaddr_type; break; case BT_IO_OPT_MTU: *(va_arg(args, uint16_t *)) = qos.ucast.out.sdu; break; case BT_IO_OPT_IMTU: *(va_arg(args, uint16_t *)) = qos.ucast.in.sdu; break; case BT_IO_OPT_OMTU: *(va_arg(args, uint16_t *)) = qos.ucast.out.sdu; break; case BT_IO_OPT_PHY: if (get_phy(sock, &phy) < 0) { ERROR_FAILED(err, "get_phy", errno); return FALSE; } *(va_arg(args, uint32_t *)) = phy; break; case BT_IO_OPT_QOS: *(va_arg(args, struct bt_iso_qos *)) = qos; break; case BT_IO_OPT_BASE: *(va_arg(args, struct bt_iso_base *)) = base; break; case BT_IO_OPT_HANDLE: case BT_IO_OPT_CLASS: case BT_IO_OPT_DEFER_TIMEOUT: case BT_IO_OPT_SEC_LEVEL: case BT_IO_OPT_KEY_SIZE: case BT_IO_OPT_CHANNEL: case BT_IO_OPT_SOURCE_CHANNEL: case BT_IO_OPT_DEST_CHANNEL: case BT_IO_OPT_PSM: case BT_IO_OPT_CID: case BT_IO_OPT_CENTRAL: case BT_IO_OPT_MODE: case BT_IO_OPT_FLUSHABLE: case BT_IO_OPT_PRIORITY: case BT_IO_OPT_VOICE: case BT_IO_OPT_ISO_BC_SID: case BT_IO_OPT_ISO_BC_NUM_BIS: case BT_IO_OPT_ISO_BC_BIS: case BT_IO_OPT_INVALID: default: g_set_error(err, BT_IO_ERROR, EINVAL, "Unknown option %d", opt); return FALSE; } opt = va_arg(args, int); } return TRUE; } static gboolean get_valist(GIOChannel *io, BtIOType type, GError **err, BtIOOption opt1, va_list args) { int sock; sock = g_io_channel_unix_get_fd(io); switch (type) { case BT_IO_L2CAP: return l2cap_get(sock, err, opt1, args); case BT_IO_RFCOMM: return rfcomm_get(sock, err, opt1, args); case BT_IO_SCO: return sco_get(sock, err, opt1, args); case BT_IO_ISO: return iso_get(sock, err, opt1, args); case BT_IO_INVALID: default: g_set_error(err, BT_IO_ERROR, EINVAL, "Unknown BtIO type %d", type); return FALSE; } } gboolean bt_io_accept(GIOChannel *io, BtIOConnect connect, gpointer user_data, GDestroyNotify destroy, GError **err) { int sock; char c; struct pollfd pfd; sock = g_io_channel_unix_get_fd(io); memset(&pfd, 0, sizeof(pfd)); pfd.fd = sock; pfd.events = POLLOUT; if (poll(&pfd, 1, 0) < 0) { ERROR_FAILED(err, "poll", errno); return FALSE; } if (!(pfd.revents & POLLOUT)) { if (read(sock, &c, 1) < 0) { ERROR_FAILED(err, "read", errno); return FALSE; } } accept_add(io, connect, user_data, destroy); return TRUE; } gboolean bt_io_bcast_accept(GIOChannel *io, BtIOConnect connect, gpointer user_data, GDestroyNotify destroy, GError * *err, BtIOOption opt1, ...) { int sock; char c; va_list args; struct sockaddr_iso *addr = NULL; uint8_t bc_num_bis = 0; uint8_t bc_bis[ISO_MAX_NUM_BIS] = {0}; BtIOOption opt = opt1; va_start(args, opt1); while (opt != BT_IO_OPT_INVALID) { if (opt == BT_IO_OPT_ISO_BC_NUM_BIS) { bc_num_bis = va_arg(args, int); } else if (opt == BT_IO_OPT_ISO_BC_BIS) { memcpy(bc_bis, va_arg(args, uint8_t *), bc_num_bis); } else { g_set_error(err, BT_IO_ERROR, EINVAL, "Invalid option %d", opt); break; } opt = va_arg(args, int); } va_end(args); if (*err) return FALSE; sock = g_io_channel_unix_get_fd(io); if (bc_num_bis) { addr = malloc(sizeof(*addr) + sizeof(*addr->iso_bc)); if (!addr) { ERROR_FAILED(err, "poll", ENOMEM); return FALSE; } memset(addr, 0, sizeof(*addr) + sizeof(*addr->iso_bc)); addr->iso_family = AF_BLUETOOTH; addr->iso_bc->bc_num_bis = bc_num_bis; memcpy(addr->iso_bc->bc_bis, bc_bis, addr->iso_bc->bc_num_bis); if (bind(sock, (struct sockaddr *)addr, sizeof(*addr) + sizeof(*addr->iso_bc)) < 0) { ERROR_FAILED(err, "bind", errno); } free(addr); if (*err) return FALSE; } if (read(sock, &c, 1) < 0) { ERROR_FAILED(err, "read", errno); return FALSE; } server_add(io, connect, NULL, user_data, destroy); return TRUE; } gboolean bt_io_set(GIOChannel *io, GError **err, BtIOOption opt1, ...) { va_list args; gboolean ret; struct set_opts opts; int sock; BtIOType type; va_start(args, opt1); ret = parse_set_opts(&opts, err, opt1, args); va_end(args); if (!ret) return ret; type = bt_io_get_type(io, err); if (type == BT_IO_INVALID) return FALSE; sock = g_io_channel_unix_get_fd(io); switch (type) { case BT_IO_L2CAP: return l2cap_set(sock, opts.src_type, opts.sec_level, opts.imtu, opts.omtu, opts.mode, opts.central, opts.flushable, opts.priority, err); case BT_IO_RFCOMM: return rfcomm_set(sock, opts.sec_level, opts.central, err); case BT_IO_SCO: return sco_set(sock, opts.mtu, opts.voice, err); case BT_IO_ISO: return iso_set_qos(sock, &opts.qos, err); case BT_IO_INVALID: default: g_set_error(err, BT_IO_ERROR, EINVAL, "Unknown BtIO type %d", type); return FALSE; } } gboolean bt_io_get(GIOChannel *io, GError **err, BtIOOption opt1, ...) { va_list args; gboolean ret; BtIOType type; type = bt_io_get_type(io, err); if (type == BT_IO_INVALID) return FALSE; va_start(args, opt1); ret = get_valist(io, type, err, opt1, args); va_end(args); return ret; } static GIOChannel *create_io(gboolean server, struct set_opts *opts, GError **err) { int sock; GIOChannel *io; switch (opts->type) { case BT_IO_L2CAP: sock = socket(PF_BLUETOOTH, SOCK_SEQPACKET, BTPROTO_L2CAP); if (sock < 0) { ERROR_FAILED(err, "socket(SEQPACKET, L2CAP)", errno); return NULL; } if (l2cap_bind(sock, &opts->src, opts->src_type, server ? opts->psm : 0, opts->cid, err) < 0) goto failed; if (!l2cap_set(sock, opts->src_type, opts->sec_level, opts->imtu, opts->omtu, opts->mode, opts->central, opts->flushable, opts->priority, err)) goto failed; break; case BT_IO_RFCOMM: sock = socket(PF_BLUETOOTH, SOCK_STREAM, BTPROTO_RFCOMM); if (sock < 0) { ERROR_FAILED(err, "socket(STREAM, RFCOMM)", errno); return NULL; } if (rfcomm_bind(sock, &opts->src, server ? opts->channel : 0, err) < 0) goto failed; if (!rfcomm_set(sock, opts->sec_level, opts->central, err)) goto failed; break; case BT_IO_SCO: sock = socket(PF_BLUETOOTH, SOCK_SEQPACKET, BTPROTO_SCO); if (sock < 0) { ERROR_FAILED(err, "socket(SEQPACKET, SCO)", errno); return NULL; } if (sco_bind(sock, &opts->src, err) < 0) goto failed; if (!sco_set(sock, opts->mtu, opts->voice, err)) goto failed; break; case BT_IO_ISO: sock = socket(PF_BLUETOOTH, SOCK_SEQPACKET, BTPROTO_ISO); if (sock < 0) { ERROR_FAILED(err, "socket(SEQPACKET, ISO)", errno); return NULL; } if (iso_bind(sock, server, &opts->src, opts->src_type, &opts->dst, opts->dst_type, opts->bc_sid, opts->bc_num_bis, opts->bc_bis, err) < 0) goto failed; if (!iso_set_qos(sock, &opts->qos, err)) goto failed; if (opts->base.base_len) if (!iso_set_base(sock, &opts->base, err)) goto failed; break; case BT_IO_INVALID: default: g_set_error(err, BT_IO_ERROR, EINVAL, "Unknown BtIO type %d", opts->type); return NULL; } io = g_io_channel_unix_new(sock); g_io_channel_set_close_on_unref(io, TRUE); g_io_channel_set_flags(io, G_IO_FLAG_NONBLOCK, NULL); return io; failed: close(sock); return NULL; } GIOChannel *bt_io_connect(BtIOConnect connect, gpointer user_data, GDestroyNotify destroy, GError **gerr, BtIOOption opt1, ...) { GIOChannel *io; va_list args; struct set_opts opts; int err, sock; gboolean ret; char addr[18]; va_start(args, opt1); ret = parse_set_opts(&opts, gerr, opt1, args); va_end(args); if (ret == FALSE) return NULL; io = create_io(FALSE, &opts, gerr); if (io == NULL) return NULL; sock = g_io_channel_unix_get_fd(io); /* Use DEFER_SETUP when connecting using Ext-Flowctl or ISO */ if ((opts.mode == BT_IO_MODE_EXT_FLOWCTL && opts.defer) || (opts.mode == BT_IO_MODE_ISO && opts.defer)) { if (setsockopt(sock, SOL_BLUETOOTH, BT_DEFER_SETUP, &opts.defer, sizeof(opts.defer)) < 0) { ERROR_FAILED(gerr, "setsockopt(BT_DEFER_SETUP)", errno); return NULL; } } switch (opts.type) { case BT_IO_L2CAP: err = l2cap_connect(sock, &opts.dst, opts.dst_type, opts.psm, opts.cid); break; case BT_IO_RFCOMM: err = rfcomm_connect(sock, &opts.dst, opts.channel); break; case BT_IO_SCO: err = sco_connect(sock, &opts.dst); break; case BT_IO_ISO: err = iso_connect(sock, &opts.dst, opts.dst_type); break; case BT_IO_INVALID: default: g_set_error(gerr, BT_IO_ERROR, EINVAL, "Unknown BtIO type %d", opts.type); return NULL; } if (err < 0) { ba2str(&opts.dst, addr); g_set_error(gerr, BT_IO_ERROR, -err, "connect to %s: %s (%d)", addr, strerror(-err), -err); g_io_channel_unref(io); return NULL; } connect_add(io, connect, opts.dst, user_data, destroy); return io; } GIOChannel *bt_io_listen(BtIOConnect connect, BtIOConfirm confirm, gpointer user_data, GDestroyNotify destroy, GError **err, BtIOOption opt1, ...) { GIOChannel *io; va_list args; struct set_opts opts; int sock; gboolean ret; va_start(args, opt1); ret = parse_set_opts(&opts, err, opt1, args); va_end(args); if (ret == FALSE) return NULL; io = create_io(TRUE, &opts, err); if (io == NULL) return NULL; sock = g_io_channel_unix_get_fd(io); if (confirm) if (setsockopt(sock, SOL_BLUETOOTH, BT_DEFER_SETUP, &opts.defer, sizeof(opts.defer)) < 0) { ERROR_FAILED(err, "setsockopt(BT_DEFER_SETUP)", errno); return NULL; } if (listen(sock, 5) < 0) { ERROR_FAILED(err, "listen", errno); g_io_channel_unref(io); return NULL; } server_add(io, connect, confirm, user_data, destroy); return io; } GQuark bt_io_error_quark(void) { return g_quark_from_static_string("bt-io-error-quark"); } bluez-5.82/btio/PaxHeaders/btio.h0000644000000000000000000000005014536422313013714 xustar0020 atime=1743516230 20 ctime=1743591278 bluez-5.82/btio/btio.h0000644000000000000000000000420014536422313013371 0ustar00rootroot/* SPDX-License-Identifier: GPL-2.0-or-later */ /* * * BlueZ - Bluetooth protocol stack for Linux * * Copyright (C) 2009-2010 Marcel Holtmann * Copyright (C) 2009-2010 Nokia Corporation * Copyright 2023 NXP * * */ #ifndef BT_IO_H #define BT_IO_H #include #define BT_IO_ERROR bt_io_error_quark() GQuark bt_io_error_quark(void); typedef enum { BT_IO_OPT_INVALID = 0, BT_IO_OPT_SOURCE, BT_IO_OPT_SOURCE_BDADDR, BT_IO_OPT_SOURCE_TYPE, BT_IO_OPT_DEST, BT_IO_OPT_DEST_BDADDR, BT_IO_OPT_DEST_TYPE, BT_IO_OPT_DEFER_TIMEOUT, BT_IO_OPT_SEC_LEVEL, BT_IO_OPT_KEY_SIZE, BT_IO_OPT_CHANNEL, BT_IO_OPT_SOURCE_CHANNEL, BT_IO_OPT_DEST_CHANNEL, BT_IO_OPT_PSM, BT_IO_OPT_CID, BT_IO_OPT_MTU, BT_IO_OPT_OMTU, BT_IO_OPT_IMTU, BT_IO_OPT_CENTRAL, BT_IO_OPT_HANDLE, BT_IO_OPT_CLASS, BT_IO_OPT_MODE, BT_IO_OPT_FLUSHABLE, BT_IO_OPT_PRIORITY, BT_IO_OPT_VOICE, BT_IO_OPT_PHY, BT_IO_OPT_QOS, BT_IO_OPT_BASE, BT_IO_OPT_ISO_BC_SID, BT_IO_OPT_ISO_BC_NUM_BIS, BT_IO_OPT_ISO_BC_BIS, } BtIOOption; typedef enum { BT_IO_SEC_SDP = 0, BT_IO_SEC_LOW, BT_IO_SEC_MEDIUM, BT_IO_SEC_HIGH, } BtIOSecLevel; typedef enum { BT_IO_MODE_BASIC = 0, BT_IO_MODE_ERTM, BT_IO_MODE_STREAMING, BT_IO_MODE_LE_FLOWCTL, BT_IO_MODE_EXT_FLOWCTL, BT_IO_MODE_ISO } BtIOMode; typedef void (*BtIOConfirm)(GIOChannel *io, gpointer user_data); typedef void (*BtIOConnect)(GIOChannel *io, GError *err, gpointer user_data); gboolean bt_io_accept(GIOChannel *io, BtIOConnect connect, gpointer user_data, GDestroyNotify destroy, GError **err); gboolean bt_io_bcast_accept(GIOChannel *io, BtIOConnect connect, gpointer user_data, GDestroyNotify destroy, GError **err, BtIOOption opt1, ...); gboolean bt_io_set(GIOChannel *io, GError **err, BtIOOption opt1, ...); gboolean bt_io_get(GIOChannel *io, GError **err, BtIOOption opt1, ...); GIOChannel *bt_io_connect(BtIOConnect connect, gpointer user_data, GDestroyNotify destroy, GError **gerr, BtIOOption opt1, ...); GIOChannel *bt_io_listen(BtIOConnect connect, BtIOConfirm confirm, gpointer user_data, GDestroyNotify destroy, GError **err, BtIOOption opt1, ...); #endif bluez-5.82/PaxHeaders/Makefile.mesh0000644000000000000000000000005014572354773014262 xustar0020 atime=1743515718 20 ctime=1743591275 bluez-5.82/Makefile.mesh0000644000000000000000000000333314572354773013745 0ustar00rootroot# SPDX-License-Identifier: GPL-2.0 if MESH if DATAFILES dbus_DATA += mesh/bluetooth-mesh.conf conf_DATA += mesh/mesh-main.conf endif if SYSTEMD systemdsystemunit_DATA += mesh/bluetooth-mesh.service dbussystembus_DATA += mesh/org.bluez.mesh.service endif mesh_sources = mesh/mesh.h mesh/mesh.c \ mesh/net-keys.h mesh/net-keys.c \ mesh/mesh-io.h mesh/mesh-io.c \ mesh/mesh-mgmt.h mesh/mesh-mgmt.c \ mesh/error.h mesh/mesh-io-api.h \ mesh/mesh-io-unit.h mesh/mesh-io-unit.c \ mesh/mesh-io-mgmt.h mesh/mesh-io-mgmt.c \ mesh/mesh-io-generic.h mesh/mesh-io-generic.c \ mesh/net.h mesh/net.c \ mesh/crypto.h mesh/crypto.c \ mesh/friend.h mesh/friend.c \ mesh/appkey.h mesh/appkey.c \ mesh/node.h mesh/node.c \ mesh/provision.h mesh/prov.h \ mesh/model.h mesh/model.c \ mesh/cfgmod.h mesh/cfgmod-server.c \ mesh/remprv.h mesh/remprv-server.c \ mesh/mesh-config.h mesh/mesh-config-json.c \ mesh/util.h mesh/util.c \ mesh/dbus.h mesh/dbus.c \ mesh/agent.h mesh/agent.c \ mesh/prov-acceptor.c mesh/prov-initiator.c \ mesh/manager.h mesh/manager.c \ mesh/pb-adv.h mesh/pb-adv.c \ mesh/keyring.h mesh/keyring.c \ mesh/rpl.h mesh/rpl.c \ mesh/prv-beacon.h mesh/prvbeac-server.c \ mesh/mesh-defs.h pkglibexec_PROGRAMS += mesh/bluetooth-meshd mesh/mesh.$(OBJEXT): ell/internal mesh/main.$(OBJEXT): src/builtin.h lib/bluetooth/bluetooth.h mesh_bluetooth_meshd_SOURCES = $(mesh_sources) mesh/main.c mesh_bluetooth_meshd_LDADD = src/libshared-ell.la $(ell_ldadd) -ljson-c if MANPAGES man_MANS += mesh/bluetooth-meshd.8 endif manual_pages += mesh/bluetooth-meshd.8 endif EXTRA_DIST += mesh/bluetooth-mesh.conf mesh/org.bluez.mesh.service mesh/mesh-main.conf bluez-5.82/PaxHeaders/install-sh0000644000000000000000000000005014773211371013655 xustar0020 atime=1743590137 20 ctime=1743591275 bluez-5.82/install-sh0000755000000000000000000003577614773211371013363 0ustar00rootroot#!/bin/sh # install - install a program, script, or datafile scriptversion=2020-11-14.01; # UTC # This originates from X11R5 (mit/util/scripts/install.sh), which was # later released in X11R6 (xc/config/util/install.sh) with the # following copyright and license. # # Copyright (C) 1994 X Consortium # # Permission is hereby granted, free of charge, to any person obtaining a copy # of this software and associated documentation files (the "Software"), to # deal in the Software without restriction, including without limitation the # rights to use, copy, modify, merge, publish, distribute, sublicense, and/or # sell copies of the Software, and to permit persons to whom the Software is # furnished to do so, subject to the following conditions: # # The above copyright notice and this permission notice shall be included in # all copies or substantial portions of the Software. # # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE # X CONSORTIUM BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN # AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNEC- # TION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. # # Except as contained in this notice, the name of the X Consortium shall not # be used in advertising or otherwise to promote the sale, use or other deal- # ings in this Software without prior written authorization from the X Consor- # tium. # # # FSF changes to this file are in the public domain. # # Calling this script install-sh is preferred over install.sh, to prevent # 'make' implicit rules from creating a file called install from it # when there is no Makefile. # # This script is compatible with the BSD install script, but was written # from scratch. tab=' ' nl=' ' IFS=" $tab$nl" # Set DOITPROG to "echo" to test this script. doit=${DOITPROG-} doit_exec=${doit:-exec} # Put in absolute file names if you don't have them in your path; # or use environment vars. chgrpprog=${CHGRPPROG-chgrp} chmodprog=${CHMODPROG-chmod} chownprog=${CHOWNPROG-chown} cmpprog=${CMPPROG-cmp} cpprog=${CPPROG-cp} mkdirprog=${MKDIRPROG-mkdir} mvprog=${MVPROG-mv} rmprog=${RMPROG-rm} stripprog=${STRIPPROG-strip} posix_mkdir= # Desired mode of installed file. mode=0755 # Create dirs (including intermediate dirs) using mode 755. # This is like GNU 'install' as of coreutils 8.32 (2020). mkdir_umask=22 backupsuffix= chgrpcmd= chmodcmd=$chmodprog chowncmd= mvcmd=$mvprog rmcmd="$rmprog -f" stripcmd= src= dst= dir_arg= dst_arg= copy_on_change=false is_target_a_directory=possibly usage="\ Usage: $0 [OPTION]... [-T] SRCFILE DSTFILE or: $0 [OPTION]... SRCFILES... DIRECTORY or: $0 [OPTION]... -t DIRECTORY SRCFILES... or: $0 [OPTION]... -d DIRECTORIES... In the 1st form, copy SRCFILE to DSTFILE. In the 2nd and 3rd, copy all SRCFILES to DIRECTORY. In the 4th, create DIRECTORIES. Options: --help display this help and exit. --version display version info and exit. -c (ignored) -C install only if different (preserve data modification time) -d create directories instead of installing files. -g GROUP $chgrpprog installed files to GROUP. -m MODE $chmodprog installed files to MODE. -o USER $chownprog installed files to USER. -p pass -p to $cpprog. -s $stripprog installed files. -S SUFFIX attempt to back up existing files, with suffix SUFFIX. -t DIRECTORY install into DIRECTORY. -T report an error if DSTFILE is a directory. Environment variables override the default commands: CHGRPPROG CHMODPROG CHOWNPROG CMPPROG CPPROG MKDIRPROG MVPROG RMPROG STRIPPROG By default, rm is invoked with -f; when overridden with RMPROG, it's up to you to specify -f if you want it. If -S is not specified, no backups are attempted. Email bug reports to bug-automake@gnu.org. Automake home page: https://www.gnu.org/software/automake/ " while test $# -ne 0; do case $1 in -c) ;; -C) copy_on_change=true;; -d) dir_arg=true;; -g) chgrpcmd="$chgrpprog $2" shift;; --help) echo "$usage"; exit $?;; -m) mode=$2 case $mode in *' '* | *"$tab"* | *"$nl"* | *'*'* | *'?'* | *'['*) echo "$0: invalid mode: $mode" >&2 exit 1;; esac shift;; -o) chowncmd="$chownprog $2" shift;; -p) cpprog="$cpprog -p";; -s) stripcmd=$stripprog;; -S) backupsuffix="$2" shift;; -t) is_target_a_directory=always dst_arg=$2 # Protect names problematic for 'test' and other utilities. case $dst_arg in -* | [=\(\)!]) dst_arg=./$dst_arg;; esac shift;; -T) is_target_a_directory=never;; --version) echo "$0 $scriptversion"; exit $?;; --) shift break;; -*) echo "$0: invalid option: $1" >&2 exit 1;; *) break;; esac shift done # We allow the use of options -d and -T together, by making -d # take the precedence; this is for compatibility with GNU install. if test -n "$dir_arg"; then if test -n "$dst_arg"; then echo "$0: target directory not allowed when installing a directory." >&2 exit 1 fi fi if test $# -ne 0 && test -z "$dir_arg$dst_arg"; then # When -d is used, all remaining arguments are directories to create. # When -t is used, the destination is already specified. # Otherwise, the last argument is the destination. Remove it from $@. for arg do if test -n "$dst_arg"; then # $@ is not empty: it contains at least $arg. set fnord "$@" "$dst_arg" shift # fnord fi shift # arg dst_arg=$arg # Protect names problematic for 'test' and other utilities. case $dst_arg in -* | [=\(\)!]) dst_arg=./$dst_arg;; esac done fi if test $# -eq 0; then if test -z "$dir_arg"; then echo "$0: no input file specified." >&2 exit 1 fi # It's OK to call 'install-sh -d' without argument. # This can happen when creating conditional directories. exit 0 fi if test -z "$dir_arg"; then if test $# -gt 1 || test "$is_target_a_directory" = always; then if test ! -d "$dst_arg"; then echo "$0: $dst_arg: Is not a directory." >&2 exit 1 fi fi fi if test -z "$dir_arg"; then do_exit='(exit $ret); exit $ret' trap "ret=129; $do_exit" 1 trap "ret=130; $do_exit" 2 trap "ret=141; $do_exit" 13 trap "ret=143; $do_exit" 15 # Set umask so as not to create temps with too-generous modes. # However, 'strip' requires both read and write access to temps. case $mode in # Optimize common cases. *644) cp_umask=133;; *755) cp_umask=22;; *[0-7]) if test -z "$stripcmd"; then u_plus_rw= else u_plus_rw='% 200' fi cp_umask=`expr '(' 777 - $mode % 1000 ')' $u_plus_rw`;; *) if test -z "$stripcmd"; then u_plus_rw= else u_plus_rw=,u+rw fi cp_umask=$mode$u_plus_rw;; esac fi for src do # Protect names problematic for 'test' and other utilities. case $src in -* | [=\(\)!]) src=./$src;; esac if test -n "$dir_arg"; then dst=$src dstdir=$dst test -d "$dstdir" dstdir_status=$? # Don't chown directories that already exist. if test $dstdir_status = 0; then chowncmd="" fi else # Waiting for this to be detected by the "$cpprog $src $dsttmp" command # might cause directories to be created, which would be especially bad # if $src (and thus $dsttmp) contains '*'. if test ! -f "$src" && test ! -d "$src"; then echo "$0: $src does not exist." >&2 exit 1 fi if test -z "$dst_arg"; then echo "$0: no destination specified." >&2 exit 1 fi dst=$dst_arg # If destination is a directory, append the input filename. if test -d "$dst"; then if test "$is_target_a_directory" = never; then echo "$0: $dst_arg: Is a directory" >&2 exit 1 fi dstdir=$dst dstbase=`basename "$src"` case $dst in */) dst=$dst$dstbase;; *) dst=$dst/$dstbase;; esac dstdir_status=0 else dstdir=`dirname "$dst"` test -d "$dstdir" dstdir_status=$? fi fi case $dstdir in */) dstdirslash=$dstdir;; *) dstdirslash=$dstdir/;; esac obsolete_mkdir_used=false if test $dstdir_status != 0; then case $posix_mkdir in '') # With -d, create the new directory with the user-specified mode. # Otherwise, rely on $mkdir_umask. if test -n "$dir_arg"; then mkdir_mode=-m$mode else mkdir_mode= fi posix_mkdir=false # The $RANDOM variable is not portable (e.g., dash). Use it # here however when possible just to lower collision chance. tmpdir=${TMPDIR-/tmp}/ins$RANDOM-$$ trap ' ret=$? rmdir "$tmpdir/a/b" "$tmpdir/a" "$tmpdir" 2>/dev/null exit $ret ' 0 # Because "mkdir -p" follows existing symlinks and we likely work # directly in world-writeable /tmp, make sure that the '$tmpdir' # directory is successfully created first before we actually test # 'mkdir -p'. if (umask $mkdir_umask && $mkdirprog $mkdir_mode "$tmpdir" && exec $mkdirprog $mkdir_mode -p -- "$tmpdir/a/b") >/dev/null 2>&1 then if test -z "$dir_arg" || { # Check for POSIX incompatibilities with -m. # HP-UX 11.23 and IRIX 6.5 mkdir -m -p sets group- or # other-writable bit of parent directory when it shouldn't. # FreeBSD 6.1 mkdir -m -p sets mode of existing directory. test_tmpdir="$tmpdir/a" ls_ld_tmpdir=`ls -ld "$test_tmpdir"` case $ls_ld_tmpdir in d????-?r-*) different_mode=700;; d????-?--*) different_mode=755;; *) false;; esac && $mkdirprog -m$different_mode -p -- "$test_tmpdir" && { ls_ld_tmpdir_1=`ls -ld "$test_tmpdir"` test "$ls_ld_tmpdir" = "$ls_ld_tmpdir_1" } } then posix_mkdir=: fi rmdir "$tmpdir/a/b" "$tmpdir/a" "$tmpdir" else # Remove any dirs left behind by ancient mkdir implementations. rmdir ./$mkdir_mode ./-p ./-- "$tmpdir" 2>/dev/null fi trap '' 0;; esac if $posix_mkdir && ( umask $mkdir_umask && $doit_exec $mkdirprog $mkdir_mode -p -- "$dstdir" ) then : else # mkdir does not conform to POSIX, # or it failed possibly due to a race condition. Create the # directory the slow way, step by step, checking for races as we go. case $dstdir in /*) prefix='/';; [-=\(\)!]*) prefix='./';; *) prefix='';; esac oIFS=$IFS IFS=/ set -f set fnord $dstdir shift set +f IFS=$oIFS prefixes= for d do test X"$d" = X && continue prefix=$prefix$d if test -d "$prefix"; then prefixes= else if $posix_mkdir; then (umask $mkdir_umask && $doit_exec $mkdirprog $mkdir_mode -p -- "$dstdir") && break # Don't fail if two instances are running concurrently. test -d "$prefix" || exit 1 else case $prefix in *\'*) qprefix=`echo "$prefix" | sed "s/'/'\\\\\\\\''/g"`;; *) qprefix=$prefix;; esac prefixes="$prefixes '$qprefix'" fi fi prefix=$prefix/ done if test -n "$prefixes"; then # Don't fail if two instances are running concurrently. (umask $mkdir_umask && eval "\$doit_exec \$mkdirprog $prefixes") || test -d "$dstdir" || exit 1 obsolete_mkdir_used=true fi fi fi if test -n "$dir_arg"; then { test -z "$chowncmd" || $doit $chowncmd "$dst"; } && { test -z "$chgrpcmd" || $doit $chgrpcmd "$dst"; } && { test "$obsolete_mkdir_used$chowncmd$chgrpcmd" = false || test -z "$chmodcmd" || $doit $chmodcmd $mode "$dst"; } || exit 1 else # Make a couple of temp file names in the proper directory. dsttmp=${dstdirslash}_inst.$$_ rmtmp=${dstdirslash}_rm.$$_ # Trap to clean up those temp files at exit. trap 'ret=$?; rm -f "$dsttmp" "$rmtmp" && exit $ret' 0 # Copy the file name to the temp name. (umask $cp_umask && { test -z "$stripcmd" || { # Create $dsttmp read-write so that cp doesn't create it read-only, # which would cause strip to fail. if test -z "$doit"; then : >"$dsttmp" # No need to fork-exec 'touch'. else $doit touch "$dsttmp" fi } } && $doit_exec $cpprog "$src" "$dsttmp") && # and set any options; do chmod last to preserve setuid bits. # # If any of these fail, we abort the whole thing. If we want to # ignore errors from any of these, just make sure not to ignore # errors from the above "$doit $cpprog $src $dsttmp" command. # { test -z "$chowncmd" || $doit $chowncmd "$dsttmp"; } && { test -z "$chgrpcmd" || $doit $chgrpcmd "$dsttmp"; } && { test -z "$stripcmd" || $doit $stripcmd "$dsttmp"; } && { test -z "$chmodcmd" || $doit $chmodcmd $mode "$dsttmp"; } && # If -C, don't bother to copy if it wouldn't change the file. if $copy_on_change && old=`LC_ALL=C ls -dlL "$dst" 2>/dev/null` && new=`LC_ALL=C ls -dlL "$dsttmp" 2>/dev/null` && set -f && set X $old && old=:$2:$4:$5:$6 && set X $new && new=:$2:$4:$5:$6 && set +f && test "$old" = "$new" && $cmpprog "$dst" "$dsttmp" >/dev/null 2>&1 then rm -f "$dsttmp" else # If $backupsuffix is set, and the file being installed # already exists, attempt a backup. Don't worry if it fails, # e.g., if mv doesn't support -f. if test -n "$backupsuffix" && test -f "$dst"; then $doit $mvcmd -f "$dst" "$dst$backupsuffix" 2>/dev/null fi # Rename the file to the real destination. $doit $mvcmd -f "$dsttmp" "$dst" 2>/dev/null || # The rename failed, perhaps because mv can't rename something else # to itself, or perhaps because mv is so ancient that it does not # support -f. { # Now remove or move aside any old file at destination location. # We try this two ways since rm can't unlink itself on some # systems and the destination file might be busy for other # reasons. In this case, the final cleanup might fail but the new # file should still install successfully. { test ! -f "$dst" || $doit $rmcmd "$dst" 2>/dev/null || { $doit $mvcmd -f "$dst" "$rmtmp" 2>/dev/null && { $doit $rmcmd "$rmtmp" 2>/dev/null; :; } } || { echo "$0: cannot unlink or rename $dst" >&2 (exit 1); exit 1 } } && # Now rename the file to the real destination. $doit $mvcmd "$dsttmp" "$dst" } fi || exit 1 trap '' 0 fi done # Local variables: # eval: (add-hook 'before-save-hook 'time-stamp) # time-stamp-start: "scriptversion=" # time-stamp-format: "%:y-%02m-%02d.%02H" # time-stamp-time-zone: "UTC0" # time-stamp-end: "; # UTC" # End: bluez-5.82/PaxHeaders/attrib0000644000000000000000000000005014773213564013072 xustar0020 atime=1743591291 20 ctime=1743591284 bluez-5.82/attrib/0000755000000000000000000000000014773213564012630 5ustar00rootrootbluez-5.82/attrib/PaxHeaders/gatt.h0000644000000000000000000000005014015011623014234 xustar0020 atime=1743516561 20 ctime=1743591279 bluez-5.82/attrib/gatt.h0000644000000000000000000000647214015011623013726 0ustar00rootroot/* SPDX-License-Identifier: GPL-2.0-or-later */ /* * * BlueZ - Bluetooth protocol stack for Linux * * Copyright (C) 2010 Nokia Corporation * Copyright (C) 2010 Marcel Holtmann * * */ /* * GATT Characteristic Property bit field * Reference: Core SPEC 4.1 page 2183 (Table 3.5: Characteristic Properties * bit field) defines how the Characteristic Value can be used, or how the * characteristic descriptors (see Section 3.3.3 - page 2184) can be accessed. * In the core spec, regular properties are included in the characteristic * declaration, and the extended properties are defined as descriptor. */ #define GATT_CHR_PROP_BROADCAST 0x01 #define GATT_CHR_PROP_READ 0x02 #define GATT_CHR_PROP_WRITE_WITHOUT_RESP 0x04 #define GATT_CHR_PROP_WRITE 0x08 #define GATT_CHR_PROP_NOTIFY 0x10 #define GATT_CHR_PROP_INDICATE 0x20 #define GATT_CHR_PROP_AUTH 0x40 #define GATT_CHR_PROP_EXT_PROP 0x80 /* Client Characteristic Configuration bit field */ #define GATT_CLIENT_CHARAC_CFG_NOTIF_BIT 0x0001 #define GATT_CLIENT_CHARAC_CFG_IND_BIT 0x0002 typedef void (*gatt_cb_t) (uint8_t status, GSList *l, void *user_data); struct gatt_primary { char uuid[MAX_LEN_UUID_STR + 1]; gboolean changed; struct att_range range; }; struct gatt_included { char uuid[MAX_LEN_UUID_STR + 1]; uint16_t handle; struct att_range range; }; struct gatt_char { char uuid[MAX_LEN_UUID_STR + 1]; uint16_t handle; uint8_t properties; uint16_t value_handle; }; struct gatt_desc { char uuid[MAX_LEN_UUID_STR + 1]; uint16_t handle; uint16_t uuid16; }; guint gatt_discover_primary(GAttrib *attrib, bt_uuid_t *uuid, gatt_cb_t func, gpointer user_data); unsigned int gatt_find_included(GAttrib *attrib, uint16_t start, uint16_t end, gatt_cb_t func, gpointer user_data); guint gatt_discover_char(GAttrib *attrib, uint16_t start, uint16_t end, bt_uuid_t *uuid, gatt_cb_t func, gpointer user_data); guint gatt_read_char(GAttrib *attrib, uint16_t handle, GAttribResultFunc func, gpointer user_data); guint gatt_write_char(GAttrib *attrib, uint16_t handle, const uint8_t *value, size_t vlen, GAttribResultFunc func, gpointer user_data); guint gatt_discover_desc(GAttrib *attrib, uint16_t start, uint16_t end, bt_uuid_t *uuid, gatt_cb_t func, gpointer user_data); guint gatt_reliable_write_char(GAttrib *attrib, uint16_t handle, const uint8_t *value, size_t vlen, GAttribResultFunc func, gpointer user_data); guint gatt_execute_write(GAttrib *attrib, uint8_t flags, GAttribResultFunc func, gpointer user_data); guint gatt_write_cmd(GAttrib *attrib, uint16_t handle, const uint8_t *value, int vlen, GDestroyNotify notify, gpointer user_data); guint gatt_signed_write_cmd(GAttrib *attrib, uint16_t handle, const uint8_t *value, int vlen, struct bt_crypto *crypto, const uint8_t csrk[16], uint32_t sign_cnt, GDestroyNotify notify, gpointer user_data); guint gatt_read_char_by_uuid(GAttrib *attrib, uint16_t start, uint16_t end, bt_uuid_t *uuid, GAttribResultFunc func, gpointer user_data); guint gatt_exchange_mtu(GAttrib *attrib, uint16_t mtu, GAttribResultFunc func, gpointer user_data); gboolean gatt_parse_record(const sdp_record_t *rec, uuid_t *prim_uuid, uint16_t *psm, uint16_t *start, uint16_t *end); bluez-5.82/attrib/PaxHeaders/gatttool.c0000644000000000000000000000005014572354773015155 xustar0020 atime=1743516867 20 ctime=1743591279 bluez-5.82/attrib/gatttool.c0000644000000000000000000003407114572354773014643 0ustar00rootroot// SPDX-License-Identifier: GPL-2.0-or-later /* * * BlueZ - Bluetooth protocol stack for Linux * * Copyright (C) 2010 Nokia Corporation * Copyright (C) 2010 Marcel Holtmann * * */ #ifdef HAVE_CONFIG_H #include #endif #include #include #include #include #include "lib/bluetooth.h" #include "lib/hci.h" #include "lib/hci_lib.h" #include "lib/sdp.h" #include "lib/uuid.h" #include "src/shared/util.h" #include "att.h" #include "btio/btio.h" #include "gattrib.h" #include "gatt.h" #include "gatttool.h" static char *opt_src = NULL; static char *opt_dst = NULL; static char *opt_dst_type = NULL; static char *opt_value = NULL; static char *opt_sec_level = NULL; static bt_uuid_t *opt_uuid = NULL; static int opt_start = 0x0001; static int opt_end = 0xffff; static int opt_handle = -1; static int opt_mtu = 0; static int opt_psm = 0; static gboolean opt_primary = FALSE; static gboolean opt_characteristics = FALSE; static gboolean opt_char_read = FALSE; static gboolean opt_listen = FALSE; static gboolean opt_char_desc = FALSE; static gboolean opt_char_write = FALSE; static gboolean opt_char_write_req = FALSE; static gboolean opt_interactive = FALSE; static GMainLoop *event_loop; static gboolean got_error = FALSE; static GSourceFunc operation; struct characteristic_data { GAttrib *attrib; uint16_t start; uint16_t end; }; static void events_handler(const uint8_t *pdu, uint16_t len, gpointer user_data) { GAttrib *attrib = user_data; uint8_t *opdu; uint16_t handle, i, olen = 0; size_t plen; handle = get_le16(&pdu[1]); switch (pdu[0]) { case ATT_OP_HANDLE_NOTIFY: g_print("Notification handle = 0x%04x value: ", handle); break; case ATT_OP_HANDLE_IND: g_print("Indication handle = 0x%04x value: ", handle); break; default: g_print("Invalid opcode\n"); return; } for (i = 3; i < len; i++) g_print("%02x ", pdu[i]); g_print("\n"); if (pdu[0] == ATT_OP_HANDLE_NOTIFY) return; opdu = g_attrib_get_buffer(attrib, &plen); olen = enc_confirmation(opdu, plen); if (olen > 0) g_attrib_send(attrib, 0, opdu, olen, NULL, NULL, NULL); } static gboolean listen_start(gpointer user_data) { GAttrib *attrib = user_data; g_attrib_register(attrib, ATT_OP_HANDLE_NOTIFY, GATTRIB_ALL_HANDLES, events_handler, attrib, NULL); g_attrib_register(attrib, ATT_OP_HANDLE_IND, GATTRIB_ALL_HANDLES, events_handler, attrib, NULL); return FALSE; } static void connect_cb(GIOChannel *io, GError *err, gpointer user_data) { GAttrib *attrib; uint16_t mtu; uint16_t cid; GError *gerr = NULL; if (err) { g_printerr("%s\n", err->message); got_error = TRUE; g_main_loop_quit(event_loop); } bt_io_get(io, &gerr, BT_IO_OPT_IMTU, &mtu, BT_IO_OPT_CID, &cid, BT_IO_OPT_INVALID); if (gerr) { g_printerr("Can't detect MTU, using default: %s", gerr->message); g_error_free(gerr); mtu = ATT_DEFAULT_LE_MTU; } if (cid == ATT_CID) mtu = ATT_DEFAULT_LE_MTU; attrib = g_attrib_new(io, mtu, false); if (opt_listen) g_idle_add(listen_start, attrib); operation(attrib); } static void primary_all_cb(uint8_t status, GSList *services, void *user_data) { GSList *l; if (status) { g_printerr("Discover all primary services failed: %s\n", att_ecode2str(status)); goto done; } for (l = services; l; l = l->next) { struct gatt_primary *prim = l->data; g_print("attr handle = 0x%04x, end grp handle = 0x%04x " "uuid: %s\n", prim->range.start, prim->range.end, prim->uuid); } done: g_main_loop_quit(event_loop); } static void primary_by_uuid_cb(uint8_t status, GSList *ranges, void *user_data) { GSList *l; if (status != 0) { g_printerr("Discover primary services by UUID failed: %s\n", att_ecode2str(status)); goto done; } for (l = ranges; l; l = l->next) { struct att_range *range = l->data; g_print("Starting handle: %04x Ending handle: %04x\n", range->start, range->end); } done: g_main_loop_quit(event_loop); } static gboolean primary(gpointer user_data) { GAttrib *attrib = user_data; if (opt_uuid) gatt_discover_primary(attrib, opt_uuid, primary_by_uuid_cb, NULL); else gatt_discover_primary(attrib, NULL, primary_all_cb, NULL); return FALSE; } static void char_discovered_cb(uint8_t status, GSList *characteristics, void *user_data) { GSList *l; if (status) { g_printerr("Discover all characteristics failed: %s\n", att_ecode2str(status)); goto done; } for (l = characteristics; l; l = l->next) { struct gatt_char *chars = l->data; g_print("handle = 0x%04x, char properties = 0x%02x, char value " "handle = 0x%04x, uuid = %s\n", chars->handle, chars->properties, chars->value_handle, chars->uuid); } done: g_main_loop_quit(event_loop); } static gboolean characteristics(gpointer user_data) { GAttrib *attrib = user_data; gatt_discover_char(attrib, opt_start, opt_end, opt_uuid, char_discovered_cb, NULL); return FALSE; } static void char_read_cb(guint8 status, const guint8 *pdu, guint16 plen, gpointer user_data) { uint8_t value[plen]; ssize_t vlen; int i; if (status != 0) { g_printerr("Characteristic value/descriptor read failed: %s\n", att_ecode2str(status)); goto done; } vlen = dec_read_resp(pdu, plen, value, sizeof(value)); if (vlen < 0) { g_printerr("Protocol error\n"); goto done; } g_print("Characteristic value/descriptor: "); for (i = 0; i < vlen; i++) g_print("%02x ", value[i]); g_print("\n"); done: if (!opt_listen) g_main_loop_quit(event_loop); } static void char_read_by_uuid_cb(guint8 status, const guint8 *pdu, guint16 plen, gpointer user_data) { struct att_data_list *list; int i; if (status != 0) { g_printerr("Read characteristics by UUID failed: %s\n", att_ecode2str(status)); goto done; } list = dec_read_by_type_resp(pdu, plen); if (list == NULL) goto done; for (i = 0; i < list->num; i++) { uint8_t *value = list->data[i]; int j; g_print("handle: 0x%04x \t value: ", get_le16(value)); value += 2; for (j = 0; j < list->len - 2; j++, value++) g_print("%02x ", *value); g_print("\n"); } att_data_list_free(list); done: g_main_loop_quit(event_loop); } static gboolean characteristics_read(gpointer user_data) { GAttrib *attrib = user_data; if (opt_uuid != NULL) { gatt_read_char_by_uuid(attrib, opt_start, opt_end, opt_uuid, char_read_by_uuid_cb, NULL); return FALSE; } if (opt_handle <= 0) { g_printerr("A valid handle is required\n"); g_main_loop_quit(event_loop); return FALSE; } gatt_read_char(attrib, opt_handle, char_read_cb, attrib); return FALSE; } static void mainloop_quit(gpointer user_data) { uint8_t *value = user_data; g_free(value); g_main_loop_quit(event_loop); } static gboolean characteristics_write(gpointer user_data) { GAttrib *attrib = user_data; uint8_t *value; size_t len; if (opt_handle <= 0) { g_printerr("A valid handle is required\n"); goto error; } if (opt_value == NULL || opt_value[0] == '\0') { g_printerr("A value is required\n"); goto error; } len = gatt_attr_data_from_string(opt_value, &value); if (len == 0) { g_printerr("Invalid value\n"); goto error; } gatt_write_cmd(attrib, opt_handle, value, len, mainloop_quit, value); g_free(value); return FALSE; error: g_main_loop_quit(event_loop); return FALSE; } static void char_write_req_cb(guint8 status, const guint8 *pdu, guint16 plen, gpointer user_data) { if (status != 0) { g_printerr("Characteristic Write Request failed: " "%s\n", att_ecode2str(status)); goto done; } if (!dec_write_resp(pdu, plen) && !dec_exec_write_resp(pdu, plen)) { g_printerr("Protocol error\n"); goto done; } g_print("Characteristic value was written successfully\n"); done: if (!opt_listen) g_main_loop_quit(event_loop); } static gboolean characteristics_write_req(gpointer user_data) { GAttrib *attrib = user_data; uint8_t *value; size_t len; if (opt_handle <= 0) { g_printerr("A valid handle is required\n"); goto error; } if (opt_value == NULL || opt_value[0] == '\0') { g_printerr("A value is required\n"); goto error; } len = gatt_attr_data_from_string(opt_value, &value); if (len == 0) { g_printerr("Invalid value\n"); goto error; } gatt_write_char(attrib, opt_handle, value, len, char_write_req_cb, NULL); g_free(value); return FALSE; error: g_main_loop_quit(event_loop); return FALSE; } static void char_desc_cb(uint8_t status, GSList *descriptors, void *user_data) { GSList *l; if (status) { g_printerr("Discover descriptors failed: %s\n", att_ecode2str(status)); return; } for (l = descriptors; l; l = l->next) { struct gatt_desc *desc = l->data; g_print("handle = 0x%04x, uuid = %s\n", desc->handle, desc->uuid); } if (!opt_listen) g_main_loop_quit(event_loop); } static gboolean characteristics_desc(gpointer user_data) { GAttrib *attrib = user_data; gatt_discover_desc(attrib, opt_start, opt_end, NULL, char_desc_cb, NULL); return FALSE; } static gboolean parse_uuid(const char *key, const char *value, gpointer user_data, GError **error) { if (!value) return FALSE; opt_uuid = g_try_malloc(sizeof(bt_uuid_t)); if (opt_uuid == NULL) return FALSE; if (bt_string_to_uuid(opt_uuid, value) < 0) return FALSE; return TRUE; } static const GOptionEntry primary_char_options[] = { { "start", 's' , 0, G_OPTION_ARG_INT, &opt_start, "Starting handle (optional)", "0x0001" }, { "end", 'e' , 0, G_OPTION_ARG_INT, &opt_end, "Ending handle (optional)", "0xffff" }, { "uuid", 'u', G_OPTION_FLAG_OPTIONAL_ARG, G_OPTION_ARG_CALLBACK, parse_uuid, "UUID16 or UUID128 (optional)", "0x1801"}, { NULL }, }; static const GOptionEntry char_rw_options[] = { { "handle", 'a' , 0, G_OPTION_ARG_INT, &opt_handle, "Read/Write characteristic by handle (required)", "0x0001" }, { "value", 'n' , 0, G_OPTION_ARG_STRING, &opt_value, "Write characteristic value (required for write operation)", "0x0001" }, {NULL}, }; static const GOptionEntry gatt_options[] = { { "primary", 0, 0, G_OPTION_ARG_NONE, &opt_primary, "Primary Service Discovery", NULL }, { "characteristics", 0, 0, G_OPTION_ARG_NONE, &opt_characteristics, "Characteristics Discovery", NULL }, { "char-read", 0, 0, G_OPTION_ARG_NONE, &opt_char_read, "Characteristics Value/Descriptor Read", NULL }, { "char-write", 0, 0, G_OPTION_ARG_NONE, &opt_char_write, "Characteristics Value Write Without Response (Write Command)", NULL }, { "char-write-req", 0, 0, G_OPTION_ARG_NONE, &opt_char_write_req, "Characteristics Value Write (Write Request)", NULL }, { "char-desc", 0, 0, G_OPTION_ARG_NONE, &opt_char_desc, "Characteristics Descriptor Discovery", NULL }, { "listen", 0, 0, G_OPTION_ARG_NONE, &opt_listen, "Listen for notifications and indications", NULL }, { "interactive", 'I', G_OPTION_FLAG_IN_MAIN, G_OPTION_ARG_NONE, &opt_interactive, "Use interactive mode", NULL }, { NULL }, }; static const GOptionEntry options[] = { { "adapter", 'i', 0, G_OPTION_ARG_STRING, &opt_src, "Specify local adapter interface", "hciX" }, { "device", 'b', 0, G_OPTION_ARG_STRING, &opt_dst, "Specify remote Bluetooth address", "MAC" }, { "addr-type", 't', 0, G_OPTION_ARG_STRING, &opt_dst_type, "Set LE address type. Default: public", "[public | random]"}, { "mtu", 'm', 0, G_OPTION_ARG_INT, &opt_mtu, "Specify the MTU size", "MTU" }, { "psm", 'p', 0, G_OPTION_ARG_INT, &opt_psm, "Specify the PSM for GATT/ATT over BR/EDR", "PSM" }, { "sec-level", 'l', 0, G_OPTION_ARG_STRING, &opt_sec_level, "Set security level. Default: low", "[low | medium | high]"}, { NULL }, }; int main(int argc, char *argv[]) { GOptionContext *context; GOptionGroup *gatt_group, *params_group, *char_rw_group; GError *gerr = NULL; GIOChannel *chan; opt_dst_type = g_strdup("public"); opt_sec_level = g_strdup("low"); context = g_option_context_new(NULL); g_option_context_add_main_entries(context, options, NULL); /* GATT commands */ gatt_group = g_option_group_new("gatt", "GATT commands", "Show all GATT commands", NULL, NULL); g_option_context_add_group(context, gatt_group); g_option_group_add_entries(gatt_group, gatt_options); /* Primary Services and Characteristics arguments */ params_group = g_option_group_new("params", "Primary Services/Characteristics arguments", "Show all Primary Services/Characteristics arguments", NULL, NULL); g_option_context_add_group(context, params_group); g_option_group_add_entries(params_group, primary_char_options); /* Characteristics value/descriptor read/write arguments */ char_rw_group = g_option_group_new("char-read-write", "Characteristics Value/Descriptor Read/Write arguments", "Show all Characteristics Value/Descriptor Read/Write " "arguments", NULL, NULL); g_option_context_add_group(context, char_rw_group); g_option_group_add_entries(char_rw_group, char_rw_options); if (!g_option_context_parse(context, &argc, &argv, &gerr)) { g_printerr("%s\n", gerr->message); g_clear_error(&gerr); } if (opt_interactive) { interactive(opt_src, opt_dst, opt_dst_type, opt_psm); goto done; } if (opt_primary) operation = primary; else if (opt_characteristics) operation = characteristics; else if (opt_char_read) operation = characteristics_read; else if (opt_char_write) operation = characteristics_write; else if (opt_char_write_req) operation = characteristics_write_req; else if (opt_char_desc) operation = characteristics_desc; else { char *help = g_option_context_get_help(context, TRUE, NULL); g_print("%s\n", help); g_free(help); got_error = TRUE; goto done; } if (opt_dst == NULL) { g_print("Remote Bluetooth address required\n"); got_error = TRUE; goto done; } chan = gatt_connect(opt_src, opt_dst, opt_dst_type, opt_sec_level, opt_psm, opt_mtu, connect_cb, &gerr); if (chan == NULL) { g_printerr("%s\n", gerr->message); g_clear_error(&gerr); got_error = TRUE; goto done; } event_loop = g_main_loop_new(NULL, FALSE); g_main_loop_run(event_loop); g_main_loop_unref(event_loop); done: g_option_context_free(context); g_free(opt_src); g_free(opt_dst); g_free(opt_uuid); g_free(opt_sec_level); if (got_error) exit(EXIT_FAILURE); else exit(EXIT_SUCCESS); } bluez-5.82/attrib/PaxHeaders/gattrib.c0000644000000000000000000000005014643061455014743 xustar0020 atime=1743516597 20 ctime=1743591279 bluez-5.82/attrib/gattrib.c0000644000000000000000000002216214643061455014427 0ustar00rootroot// SPDX-License-Identifier: GPL-2.0-or-later /* * * BlueZ - Bluetooth protocol stack for Linux * * Copyright (C) 2010 Nokia Corporation * Copyright (C) 2010 Marcel Holtmann * * */ #ifdef HAVE_CONFIG_H #include #endif #include #include #include #include #include #include "lib/bluetooth.h" #include "lib/uuid.h" #include "btio/btio.h" #include "src/log.h" #include "src/shared/util.h" #include "src/shared/att.h" #include "src/shared/gatt-helpers.h" #include "src/shared/queue.h" #include "src/shared/gatt-db.h" #include "src/shared/gatt-client.h" #include "attrib/att.h" #include "attrib/gattrib.h" struct _GAttrib { int ref_count; struct bt_att *att; struct bt_gatt_client *client; GIOChannel *io; GDestroyNotify destroy; gpointer destroy_user_data; struct queue *callbacks; uint8_t *buf; int buflen; struct queue *track_ids; }; struct attrib_callbacks { unsigned int id; GAttribResultFunc result_func; GAttribNotifyFunc notify_func; GDestroyNotify destroy_func; gpointer user_data; GAttrib *parent; uint16_t notify_handle; }; GAttrib *g_attrib_new(GIOChannel *io, guint16 mtu, bool ext_signed) { gint fd; GAttrib *attr; if (!io) return NULL; fd = g_io_channel_unix_get_fd(io); attr = new0(GAttrib, 1); if (!attr) return NULL; g_io_channel_ref(io); attr->io = io; attr->att = bt_att_new(fd, ext_signed); if (!attr->att) goto fail; bt_att_set_close_on_unref(attr->att, true); g_io_channel_set_close_on_unref(io, FALSE); if (!bt_att_set_mtu(attr->att, mtu)) goto fail; attr->buf = malloc0(mtu); attr->buflen = mtu; if (!attr->buf) goto fail; attr->callbacks = queue_new(); if (!attr->callbacks) goto fail; attr->track_ids = queue_new(); if (!attr->track_ids) goto fail; return g_attrib_ref(attr); fail: free(attr->buf); bt_att_unref(attr->att); g_io_channel_unref(io); free(attr); return NULL; } GAttrib *g_attrib_ref(GAttrib *attrib) { if (!attrib) return NULL; __sync_fetch_and_add(&attrib->ref_count, 1); DBG("%p: g_attrib_ref=%d ", attrib, attrib->ref_count); return attrib; } static void attrib_callbacks_destroy(void *data) { struct attrib_callbacks *cb = data; if (cb->destroy_func) cb->destroy_func(cb->user_data); free(data); } static void attrib_callbacks_remove(void *data) { struct attrib_callbacks *cb = data; if (!data || !queue_remove(cb->parent->callbacks, data)) return; attrib_callbacks_destroy(data); } void g_attrib_unref(GAttrib *attrib) { if (!attrib) return; DBG("%p: g_attrib_unref=%d ", attrib, attrib->ref_count - 1); if (__sync_sub_and_fetch(&attrib->ref_count, 1)) return; if (attrib->destroy) attrib->destroy(attrib->destroy_user_data); bt_gatt_client_unref(attrib->client); bt_att_unref(attrib->att); queue_destroy(attrib->callbacks, attrib_callbacks_destroy); queue_destroy(attrib->track_ids, NULL); free(attrib->buf); g_io_channel_unref(attrib->io); free(attrib); } GIOChannel *g_attrib_get_channel(GAttrib *attrib) { if (!attrib) return NULL; return attrib->io; } struct bt_att *g_attrib_get_att(GAttrib *attrib) { if (!attrib) return NULL; return attrib->att; } gboolean g_attrib_set_destroy_function(GAttrib *attrib, GDestroyNotify destroy, gpointer user_data) { if (!attrib) return FALSE; attrib->destroy = destroy; attrib->destroy_user_data = user_data; return TRUE; } static uint8_t *construct_full_pdu(uint8_t opcode, const void *pdu, uint16_t length) { uint8_t *buf = malloc0(length + 1); if (!buf) return NULL; buf[0] = opcode; if (pdu && length) memcpy(buf + 1, pdu, length); return buf; } static void attrib_callback_result(uint8_t opcode, const void *pdu, uint16_t length, void *user_data) { uint8_t *buf; struct attrib_callbacks *cb = user_data; guint8 status = 0; if (!cb) return; buf = construct_full_pdu(opcode, pdu, length); if (!buf) return; if (opcode == BT_ATT_OP_ERROR_RSP) { /* Error code is the third byte of the PDU data */ if (length < 4) status = BT_ATT_ERROR_UNLIKELY; else status = ((guint8 *)pdu)[3]; } if (cb->result_func) cb->result_func(status, buf, length + 1, cb->user_data); free(buf); } static void attrib_callback_notify(struct bt_att_chan *chan, uint16_t mtu, uint8_t opcode, const void *pdu, uint16_t length, void *user_data) { uint8_t *buf; struct attrib_callbacks *cb = user_data; if (!cb || !cb->notify_func) return; if (cb->notify_handle != GATTRIB_ALL_HANDLES && length < 2) return; if (cb->notify_handle != GATTRIB_ALL_HANDLES && cb->notify_handle != get_le16(pdu)) return; buf = construct_full_pdu(opcode, pdu, length); if (!buf) return; cb->notify_func(buf, length + 1, cb->user_data); free(buf); } guint g_attrib_send(GAttrib *attrib, guint id, const guint8 *pdu, guint16 len, GAttribResultFunc func, gpointer user_data, GDestroyNotify notify) { struct attrib_callbacks *cb = NULL; bt_att_response_func_t response_cb = NULL; bt_att_destroy_func_t destroy_cb = NULL; if (!attrib) return 0; if (!pdu || !len) return 0; if (func || notify) { cb = new0(struct attrib_callbacks, 1); if (!cb) return 0; cb->result_func = func; cb->user_data = user_data; cb->destroy_func = notify; cb->parent = attrib; queue_push_head(attrib->callbacks, cb); response_cb = attrib_callback_result; destroy_cb = attrib_callbacks_remove; } if (id == 0) id = bt_att_send(attrib->att, pdu[0], (void *) pdu + 1, len - 1, response_cb, cb, destroy_cb); else { int err; err = bt_att_resend(attrib->att, id, pdu[0], (void *) pdu + 1, len - 1, response_cb, cb, destroy_cb); if (err) return 0; } if (!id) return id; /* * If user what us to use given id, lets keep track on that so we give * user a possibility to cancel ongoing request. */ if (cb) { cb->id = id; queue_push_tail(attrib->track_ids, UINT_TO_PTR(id)); } return id; } gboolean g_attrib_cancel(GAttrib *attrib, guint id) { if (!attrib) return FALSE; return bt_att_cancel(attrib->att, id); } static void cancel_request(void *data, void *user_data) { unsigned int id = PTR_TO_UINT(data); GAttrib *attrib = user_data; bt_att_cancel(attrib->att, id); } gboolean g_attrib_cancel_all(GAttrib *attrib) { if (!attrib) return FALSE; queue_foreach(attrib->track_ids, cancel_request, attrib); queue_remove_all(attrib->track_ids, NULL, NULL, NULL); return TRUE; } static void client_notify_cb(uint16_t value_handle, const uint8_t *value, uint16_t length, void *user_data) { uint8_t *buf = newa(uint8_t, length + 2); put_le16(value_handle, buf); if (length) memcpy(buf + 2, value, length); attrib_callback_notify(NULL, 0, ATT_OP_HANDLE_NOTIFY, buf, length + 2, user_data); } guint g_attrib_register(GAttrib *attrib, guint8 opcode, guint16 handle, GAttribNotifyFunc func, gpointer user_data, GDestroyNotify notify) { struct attrib_callbacks *cb = NULL; if (!attrib) return 0; if (func || notify) { cb = new0(struct attrib_callbacks, 1); if (!cb) return 0; cb->notify_func = func; cb->notify_handle = handle; cb->user_data = user_data; cb->destroy_func = notify; cb->parent = attrib; queue_push_head(attrib->callbacks, cb); } if (opcode == ATT_OP_HANDLE_NOTIFY && attrib->client) { unsigned int id; id = bt_gatt_client_register_notify(attrib->client, handle, NULL, client_notify_cb, cb, attrib_callbacks_remove); if (id) return id; } if (opcode == GATTRIB_ALL_REQS) opcode = BT_ATT_ALL_REQUESTS; return bt_att_register(attrib->att, opcode, attrib_callback_notify, cb, attrib_callbacks_remove); } uint8_t *g_attrib_get_buffer(GAttrib *attrib, size_t *len) { uint16_t mtu; if (!attrib || !len) return NULL; mtu = bt_att_get_mtu(attrib->att); /* * Clients of this expect a buffer to use. * * Pdu encoding in shared/att verifies if whole buffer fits the mtu, * thus we should set the buflen also when mtu is reduced. But we * need to reallocate the buffer only if mtu is larger. */ if (mtu > attrib->buflen) attrib->buf = g_realloc(attrib->buf, mtu); attrib->buflen = mtu; *len = attrib->buflen; return attrib->buf; } gboolean g_attrib_set_mtu(GAttrib *attrib, int mtu) { if (!attrib) return FALSE; /* * Clients of this expect a buffer to use. * * Pdu encoding in sharred/att verifies if whole buffer fits the mtu, * thus we should set the buflen also when mtu is reduced. But we * need to reallocate the buffer only if mtu is larger. */ if (mtu > attrib->buflen) attrib->buf = g_realloc(attrib->buf, mtu); attrib->buflen = mtu; return bt_att_set_mtu(attrib->att, mtu); } gboolean g_attrib_attach_client(GAttrib *attrib, struct bt_gatt_client *client) { if (!attrib || !client) return FALSE; if (attrib->client) bt_gatt_client_unref(attrib->client); attrib->client = bt_gatt_client_clone(client); if (!attrib->client) return FALSE; return TRUE; } gboolean g_attrib_unregister(GAttrib *attrib, guint id) { if (!attrib) return FALSE; return bt_att_unregister(attrib->att, id); } gboolean g_attrib_unregister_all(GAttrib *attrib) { if (!attrib) return false; return bt_att_unregister_all(attrib->att); } bluez-5.82/attrib/PaxHeaders/gatttool.h0000644000000000000000000000005014015011623015132 xustar0020 atime=1743516867 20 ctime=1743591279 bluez-5.82/attrib/gatttool.h0000644000000000000000000000074614015011623014622 0ustar00rootroot/* SPDX-License-Identifier: GPL-2.0-or-later */ /* * * BlueZ - Bluetooth protocol stack for Linux * * Copyright (C) 2011 Nokia Corporation * * */ int interactive(const char *src, const char *dst, const char *dst_type, int psm); GIOChannel *gatt_connect(const char *src, const char *dst, const char *dst_type, const char *sec_level, int psm, int mtu, BtIOConnect connect_cb, GError **gerr); size_t gatt_attr_data_from_string(const char *str, uint8_t **data); bluez-5.82/attrib/PaxHeaders/att-database.h0000644000000000000000000000005014015011623015627 xustar0020 atime=1743516870 20 ctime=1743591284 bluez-5.82/attrib/att-database.h0000644000000000000000000000142414015011623015311 0ustar00rootroot/* SPDX-License-Identifier: GPL-2.0-or-later */ /* * * BlueZ - Bluetooth protocol stack for Linux * * Copyright (C) 2012 Texas Instruments Corporation * */ /* Requirements for read/write operations */ enum { ATT_NONE, /* No restrictions */ ATT_AUTHENTICATION, /* Authentication required */ ATT_AUTHORIZATION, /* Authorization required */ ATT_NOT_PERMITTED, /* Operation not permitted */ }; struct attribute { uint16_t handle; bt_uuid_t uuid; int read_req; /* Read requirement */ int write_req; /* Write requirement */ uint8_t (*read_cb)(struct attribute *a, struct btd_device *device, gpointer user_data); uint8_t (*write_cb)(struct attribute *a, struct btd_device *device, gpointer user_data); gpointer cb_user_data; size_t len; uint8_t *data; }; bluez-5.82/attrib/PaxHeaders/interactive.c0000644000000000000000000000005014572354773015635 xustar0020 atime=1743516867 20 ctime=1743591280 bluez-5.82/attrib/interactive.c0000644000000000000000000005025114572354773015321 0ustar00rootroot// SPDX-License-Identifier: GPL-2.0-or-later /* * * BlueZ - Bluetooth protocol stack for Linux * * Copyright (C) 2011 Nokia Corporation * * */ #ifdef HAVE_CONFIG_H #include #endif #define _GNU_SOURCE #include #include #include #include #include #include #include #include #include #include #include #include "lib/bluetooth.h" #include "lib/sdp.h" #include "lib/uuid.h" #include "src/shared/util.h" #include "btio/btio.h" #include "att.h" #include "gattrib.h" #include "gatt.h" #include "gatttool.h" #include "client/display.h" static GIOChannel *iochannel = NULL; static GAttrib *attrib = NULL; static GMainLoop *event_loop; static GString *prompt; static char *opt_src = NULL; static char *opt_dst = NULL; static char *opt_dst_type = NULL; static char *opt_sec_level = NULL; static int opt_psm = 0; static int opt_mtu = 0; static int start; static int end; static void cmd_help(int argcp, char **argvp); static enum state { STATE_DISCONNECTED, STATE_CONNECTING, STATE_CONNECTED } conn_state; #define error(fmt, arg...) \ rl_printf(COLOR_RED "Error: " COLOR_OFF fmt, ## arg) #define failed(fmt, arg...) \ rl_printf(COLOR_RED "Command Failed: " COLOR_OFF fmt, ## arg) static char *get_prompt(void) { if (conn_state == STATE_CONNECTED) g_string_assign(prompt, COLOR_BLUE); else g_string_assign(prompt, ""); if (opt_dst) g_string_append_printf(prompt, "[%17s]", opt_dst); else g_string_append_printf(prompt, "[%17s]", ""); if (conn_state == STATE_CONNECTED) g_string_append(prompt, COLOR_OFF); if (opt_psm) g_string_append(prompt, "[BR]"); else g_string_append(prompt, "[LE]"); g_string_append(prompt, "> "); return prompt->str; } static void set_state(enum state st) { conn_state = st; rl_set_prompt(get_prompt()); } static void events_handler(const uint8_t *pdu, uint16_t len, gpointer user_data) { uint8_t *opdu; uint16_t handle, i, olen; size_t plen; GString *s; handle = get_le16(&pdu[1]); switch (pdu[0]) { case ATT_OP_HANDLE_NOTIFY: s = g_string_new(NULL); g_string_printf(s, "Notification handle = 0x%04x value: ", handle); break; case ATT_OP_HANDLE_IND: s = g_string_new(NULL); g_string_printf(s, "Indication handle = 0x%04x value: ", handle); break; default: error("Invalid opcode\n"); return; } for (i = 3; i < len; i++) g_string_append_printf(s, "%02x ", pdu[i]); rl_printf("%s\n", s->str); g_string_free(s, TRUE); if (pdu[0] == ATT_OP_HANDLE_NOTIFY) return; opdu = g_attrib_get_buffer(attrib, &plen); olen = enc_confirmation(opdu, plen); if (olen > 0) g_attrib_send(attrib, 0, opdu, olen, NULL, NULL, NULL); } static void connect_cb(GIOChannel *io, GError *err, gpointer user_data) { uint16_t mtu; uint16_t cid; if (err) { set_state(STATE_DISCONNECTED); error("%s\n", err->message); return; } bt_io_get(io, &err, BT_IO_OPT_IMTU, &mtu, BT_IO_OPT_CID, &cid, BT_IO_OPT_INVALID); if (err) { g_printerr("Can't detect MTU, using default: %s", err->message); g_error_free(err); mtu = ATT_DEFAULT_LE_MTU; } if (cid == ATT_CID) mtu = ATT_DEFAULT_LE_MTU; attrib = g_attrib_new(iochannel, mtu, false); g_attrib_register(attrib, ATT_OP_HANDLE_NOTIFY, GATTRIB_ALL_HANDLES, events_handler, attrib, NULL); g_attrib_register(attrib, ATT_OP_HANDLE_IND, GATTRIB_ALL_HANDLES, events_handler, attrib, NULL); set_state(STATE_CONNECTED); rl_printf("Connection successful\n"); } static void disconnect_io() { if (conn_state == STATE_DISCONNECTED) return; g_attrib_unref(attrib); attrib = NULL; opt_mtu = 0; g_io_channel_shutdown(iochannel, FALSE, NULL); g_io_channel_unref(iochannel); iochannel = NULL; set_state(STATE_DISCONNECTED); } static void primary_all_cb(uint8_t status, GSList *services, void *user_data) { GSList *l; if (status) { error("Discover all primary services failed: %s\n", att_ecode2str(status)); return; } if (services == NULL) { error("No primary service found\n"); return; } for (l = services; l; l = l->next) { struct gatt_primary *prim = l->data; rl_printf("attr handle: 0x%04x, end grp handle: 0x%04x uuid: %s\n", prim->range.start, prim->range.end, prim->uuid); } } static void primary_by_uuid_cb(uint8_t status, GSList *ranges, void *user_data) { GSList *l; if (status) { error("Discover primary services by UUID failed: %s\n", att_ecode2str(status)); return; } if (ranges == NULL) { error("No service UUID found\n"); return; } for (l = ranges; l; l = l->next) { struct att_range *range = l->data; rl_printf("Starting handle: 0x%04x Ending handle: 0x%04x\n", range->start, range->end); } } static void included_cb(uint8_t status, GSList *includes, void *user_data) { GSList *l; if (status) { error("Find included services failed: %s\n", att_ecode2str(status)); return; } if (includes == NULL) { rl_printf("No included services found for this range\n"); return; } for (l = includes; l; l = l->next) { struct gatt_included *incl = l->data; rl_printf("handle: 0x%04x, start handle: 0x%04x, " "end handle: 0x%04x uuid: %s\n", incl->handle, incl->range.start, incl->range.end, incl->uuid); } } static void char_cb(uint8_t status, GSList *characteristics, void *user_data) { GSList *l; if (status) { error("Discover all characteristics failed: %s\n", att_ecode2str(status)); return; } for (l = characteristics; l; l = l->next) { struct gatt_char *chars = l->data; rl_printf("handle: 0x%04x, char properties: 0x%02x, char value " "handle: 0x%04x, uuid: %s\n", chars->handle, chars->properties, chars->value_handle, chars->uuid); } } static void char_desc_cb(uint8_t status, GSList *descriptors, void *user_data) { GSList *l; if (status) { error("Discover descriptors failed: %s\n", att_ecode2str(status)); return; } for (l = descriptors; l; l = l->next) { struct gatt_desc *desc = l->data; rl_printf("handle: 0x%04x, uuid: %s\n", desc->handle, desc->uuid); } } static void char_read_cb(guint8 status, const guint8 *pdu, guint16 plen, gpointer user_data) { uint8_t value[plen]; ssize_t vlen; int i; GString *s; if (status != 0) { error("Characteristic value/descriptor read failed: %s\n", att_ecode2str(status)); return; } vlen = dec_read_resp(pdu, plen, value, sizeof(value)); if (vlen < 0) { error("Protocol error\n"); return; } s = g_string_new("Characteristic value/descriptor: "); for (i = 0; i < vlen; i++) g_string_append_printf(s, "%02x ", value[i]); rl_printf("%s\n", s->str); g_string_free(s, TRUE); } static void char_read_by_uuid_cb(guint8 status, const guint8 *pdu, guint16 plen, gpointer user_data) { struct att_data_list *list; int i; GString *s; if (status != 0) { error("Read characteristics by UUID failed: %s\n", att_ecode2str(status)); return; } list = dec_read_by_type_resp(pdu, plen); if (list == NULL) return; s = g_string_new(NULL); for (i = 0; i < list->num; i++) { uint8_t *value = list->data[i]; int j; g_string_printf(s, "handle: 0x%04x \t value: ", get_le16(value)); value += 2; for (j = 0; j < list->len - 2; j++, value++) g_string_append_printf(s, "%02x ", *value); rl_printf("%s\n", s->str); } att_data_list_free(list); g_string_free(s, TRUE); } static void cmd_exit(int argcp, char **argvp) { rl_callback_handler_remove(); g_main_loop_quit(event_loop); } static gboolean channel_watcher(GIOChannel *chan, GIOCondition cond, gpointer user_data) { disconnect_io(); return FALSE; } static void cmd_connect(int argcp, char **argvp) { GError *gerr = NULL; if (conn_state != STATE_DISCONNECTED) return; if (argcp > 1) { g_free(opt_dst); opt_dst = g_strdup(argvp[1]); g_free(opt_dst_type); if (argcp > 2) opt_dst_type = g_strdup(argvp[2]); else opt_dst_type = g_strdup("public"); } if (opt_dst == NULL) { error("Remote Bluetooth address required\n"); return; } rl_printf("Attempting to connect to %s\n", opt_dst); set_state(STATE_CONNECTING); iochannel = gatt_connect(opt_src, opt_dst, opt_dst_type, opt_sec_level, opt_psm, opt_mtu, connect_cb, &gerr); if (iochannel == NULL) { set_state(STATE_DISCONNECTED); error("%s\n", gerr->message); g_error_free(gerr); } else g_io_add_watch(iochannel, G_IO_HUP, channel_watcher, NULL); } static void cmd_disconnect(int argcp, char **argvp) { disconnect_io(); } static void cmd_primary(int argcp, char **argvp) { bt_uuid_t uuid; if (conn_state != STATE_CONNECTED) { failed("Disconnected\n"); return; } if (argcp == 1) { gatt_discover_primary(attrib, NULL, primary_all_cb, NULL); return; } if (bt_string_to_uuid(&uuid, argvp[1]) < 0) { error("Invalid UUID\n"); return; } gatt_discover_primary(attrib, &uuid, primary_by_uuid_cb, NULL); } static int strtohandle(const char *src) { char *e; int dst; errno = 0; dst = strtoll(src, &e, 16); if (errno != 0 || *e != '\0') return -EINVAL; return dst; } static void cmd_included(int argcp, char **argvp) { int start = 0x0001; int end = 0xffff; if (conn_state != STATE_CONNECTED) { failed("Disconnected\n"); return; } if (argcp > 1) { start = strtohandle(argvp[1]); if (start < 0) { error("Invalid start handle: %s\n", argvp[1]); return; } end = start; } if (argcp > 2) { end = strtohandle(argvp[2]); if (end < 0) { error("Invalid end handle: %s\n", argvp[2]); return; } } gatt_find_included(attrib, start, end, included_cb, NULL); } static void cmd_char(int argcp, char **argvp) { int start = 0x0001; int end = 0xffff; if (conn_state != STATE_CONNECTED) { failed("Disconnected\n"); return; } if (argcp > 1) { start = strtohandle(argvp[1]); if (start < 0) { error("Invalid start handle: %s\n", argvp[1]); return; } } if (argcp > 2) { end = strtohandle(argvp[2]); if (end < 0) { error("Invalid end handle: %s\n", argvp[2]); return; } } if (argcp > 3) { bt_uuid_t uuid; if (bt_string_to_uuid(&uuid, argvp[3]) < 0) { error("Invalid UUID\n"); return; } gatt_discover_char(attrib, start, end, &uuid, char_cb, NULL); return; } gatt_discover_char(attrib, start, end, NULL, char_cb, NULL); } static void cmd_char_desc(int argcp, char **argvp) { if (conn_state != STATE_CONNECTED) { failed("Disconnected\n"); return; } if (argcp > 1) { start = strtohandle(argvp[1]); if (start < 0) { error("Invalid start handle: %s\n", argvp[1]); return; } } else start = 0x0001; if (argcp > 2) { end = strtohandle(argvp[2]); if (end < 0) { error("Invalid end handle: %s\n", argvp[2]); return; } } else end = 0xffff; gatt_discover_desc(attrib, start, end, NULL, char_desc_cb, NULL); } static void cmd_read_hnd(int argcp, char **argvp) { int handle; if (conn_state != STATE_CONNECTED) { failed("Disconnected\n"); return; } if (argcp < 2) { error("Missing argument: handle\n"); return; } handle = strtohandle(argvp[1]); if (handle < 0) { error("Invalid handle: %s\n", argvp[1]); return; } gatt_read_char(attrib, handle, char_read_cb, attrib); } static void cmd_read_uuid(int argcp, char **argvp) { int start = 0x0001; int end = 0xffff; bt_uuid_t uuid; if (conn_state != STATE_CONNECTED) { failed("Disconnected\n"); return; } if (argcp < 2) { error("Missing argument: UUID\n"); return; } if (bt_string_to_uuid(&uuid, argvp[1]) < 0) { error("Invalid UUID\n"); return; } if (argcp > 2) { start = strtohandle(argvp[2]); if (start < 0) { error("Invalid start handle: %s\n", argvp[1]); return; } } if (argcp > 3) { end = strtohandle(argvp[3]); if (end < 0) { error("Invalid end handle: %s\n", argvp[2]); return; } } gatt_read_char_by_uuid(attrib, start, end, &uuid, char_read_by_uuid_cb, NULL); } static void char_write_req_cb(guint8 status, const guint8 *pdu, guint16 plen, gpointer user_data) { if (status != 0) { error("Characteristic Write Request failed: " "%s\n", att_ecode2str(status)); return; } if (!dec_write_resp(pdu, plen) && !dec_exec_write_resp(pdu, plen)) { error("Protocol error\n"); return; } rl_printf("Characteristic value was written successfully\n"); } static void cmd_char_write(int argcp, char **argvp) { uint8_t *value; size_t plen; int handle; if (conn_state != STATE_CONNECTED) { failed("Disconnected\n"); return; } if (argcp < 3) { rl_printf("Usage: %s \n", argvp[0]); return; } handle = strtohandle(argvp[1]); if (handle <= 0) { error("A valid handle is required\n"); return; } plen = gatt_attr_data_from_string(argvp[2], &value); if (plen == 0) { error("Invalid value\n"); return; } if (g_strcmp0("char-write-req", argvp[0]) == 0) gatt_write_char(attrib, handle, value, plen, char_write_req_cb, NULL); else gatt_write_cmd(attrib, handle, value, plen, NULL, NULL); g_free(value); } static void cmd_sec_level(int argcp, char **argvp) { GError *gerr = NULL; BtIOSecLevel sec_level; if (argcp < 2) { rl_printf("sec-level: %s\n", opt_sec_level); return; } if (strcasecmp(argvp[1], "medium") == 0) sec_level = BT_IO_SEC_MEDIUM; else if (strcasecmp(argvp[1], "high") == 0) sec_level = BT_IO_SEC_HIGH; else if (strcasecmp(argvp[1], "low") == 0) sec_level = BT_IO_SEC_LOW; else { rl_printf("Allowed values: low | medium | high\n"); return; } g_free(opt_sec_level); opt_sec_level = g_strdup(argvp[1]); if (conn_state != STATE_CONNECTED) return; if (opt_psm) { rl_printf("Change will take effect on reconnection\n"); return; } bt_io_set(iochannel, &gerr, BT_IO_OPT_SEC_LEVEL, sec_level, BT_IO_OPT_INVALID); if (gerr) { error("%s\n", gerr->message); g_error_free(gerr); } } static void exchange_mtu_cb(guint8 status, const guint8 *pdu, guint16 plen, gpointer user_data) { uint16_t mtu; if (status != 0) { error("Exchange MTU Request failed: %s\n", att_ecode2str(status)); return; } if (!dec_mtu_resp(pdu, plen, &mtu)) { error("Protocol error\n"); return; } mtu = MIN(mtu, opt_mtu); /* Set new value for MTU in client */ if (g_attrib_set_mtu(attrib, mtu)) rl_printf("MTU was exchanged successfully: %d\n", mtu); else error("Error exchanging MTU\n"); } static void cmd_mtu(int argcp, char **argvp) { if (conn_state != STATE_CONNECTED) { failed("Disconnected\n"); return; } if (opt_psm) { failed("Operation is only available for LE transport.\n"); return; } if (argcp < 2) { rl_printf("Usage: mtu \n"); return; } if (opt_mtu) { failed("MTU exchange can only occur once per connection.\n"); return; } errno = 0; opt_mtu = strtoll(argvp[1], NULL, 0); if (errno != 0 || opt_mtu < ATT_DEFAULT_LE_MTU) { error("Invalid value. Minimum MTU size is %d\n", ATT_DEFAULT_LE_MTU); return; } gatt_exchange_mtu(attrib, opt_mtu, exchange_mtu_cb, NULL); } static const struct { const char *cmd; void (*func)(int argcp, char **argvp); const char *params; const char *desc; } commands[] = { { "help", cmd_help, "", "Show this help"}, { "exit", cmd_exit, "", "Exit interactive mode" }, { "quit", cmd_exit, "", "Exit interactive mode" }, { "connect", cmd_connect, "[address [address type]]", "Connect to a remote device" }, { "disconnect", cmd_disconnect, "", "Disconnect from a remote device" }, { "primary", cmd_primary, "[UUID]", "Primary Service Discovery" }, { "included", cmd_included, "[start hnd [end hnd]]", "Find Included Services" }, { "characteristics", cmd_char, "[start hnd [end hnd [UUID]]]", "Characteristics Discovery" }, { "char-desc", cmd_char_desc, "[start hnd] [end hnd]", "Characteristics Descriptor Discovery" }, { "char-read-hnd", cmd_read_hnd, "", "Characteristics Value/Descriptor Read by handle" }, { "char-read-uuid", cmd_read_uuid, " [start hnd] [end hnd]", "Characteristics Value/Descriptor Read by UUID" }, { "char-write-req", cmd_char_write, " ", "Characteristic Value Write (Write Request)" }, { "char-write-cmd", cmd_char_write, " ", "Characteristic Value Write (No response)" }, { "sec-level", cmd_sec_level, "[low | medium | high]", "Set security level. Default: low" }, { "mtu", cmd_mtu, "", "Exchange MTU for GATT/ATT" }, { NULL, NULL, NULL} }; static void cmd_help(int argcp, char **argvp) { int i; for (i = 0; commands[i].cmd; i++) rl_printf("%-15s %-30s %s\n", commands[i].cmd, commands[i].params, commands[i].desc); } static void parse_line(char *line_read) { char **argvp; int argcp; int i; if (line_read == NULL) { rl_printf("\n"); cmd_exit(0, NULL); return; } line_read = g_strstrip(line_read); if (*line_read == '\0') goto done; add_history(line_read); if (g_shell_parse_argv(line_read, &argcp, &argvp, NULL) == FALSE) goto done; for (i = 0; commands[i].cmd; i++) if (strcasecmp(commands[i].cmd, argvp[0]) == 0) break; if (commands[i].cmd) commands[i].func(argcp, argvp); else error("%s: command not found\n", argvp[0]); g_strfreev(argvp); done: free(line_read); } static gboolean prompt_read(GIOChannel *chan, GIOCondition cond, gpointer user_data) { if (cond & (G_IO_HUP | G_IO_ERR | G_IO_NVAL)) { g_io_channel_unref(chan); return FALSE; } rl_callback_read_char(); return TRUE; } static char *completion_generator(const char *text, int state) { static int index = 0, len = 0; const char *cmd = NULL; if (state == 0) { index = 0; len = strlen(text); } while ((cmd = commands[index].cmd) != NULL) { index++; if (strncmp(cmd, text, len) == 0) return strdup(cmd); } return NULL; } static char **commands_completion(const char *text, int start, int end) { if (start == 0) return rl_completion_matches(text, &completion_generator); else return NULL; } static guint setup_standard_input(void) { GIOChannel *channel; guint source; channel = g_io_channel_unix_new(fileno(stdin)); source = g_io_add_watch(channel, G_IO_IN | G_IO_HUP | G_IO_ERR | G_IO_NVAL, prompt_read, NULL); g_io_channel_unref(channel); return source; } static gboolean signal_handler(GIOChannel *channel, GIOCondition condition, gpointer user_data) { static unsigned int __terminated = 0; struct signalfd_siginfo si; ssize_t result; int fd; if (condition & (G_IO_NVAL | G_IO_ERR | G_IO_HUP)) { g_main_loop_quit(event_loop); return FALSE; } fd = g_io_channel_unix_get_fd(channel); result = read(fd, &si, sizeof(si)); if (result != sizeof(si)) return FALSE; switch (si.ssi_signo) { case SIGINT: rl_replace_line("", 0); rl_crlf(); rl_on_new_line(); rl_redisplay(); break; case SIGTERM: if (__terminated == 0) { rl_replace_line("", 0); rl_crlf(); g_main_loop_quit(event_loop); } __terminated = 1; break; } return TRUE; } static guint setup_signalfd(void) { GIOChannel *channel; guint source; sigset_t mask; int fd; sigemptyset(&mask); sigaddset(&mask, SIGINT); sigaddset(&mask, SIGTERM); if (sigprocmask(SIG_BLOCK, &mask, NULL) < 0) { perror("Failed to set signal mask"); return 0; } fd = signalfd(-1, &mask, 0); if (fd < 0) { perror("Failed to create signal descriptor"); return 0; } channel = g_io_channel_unix_new(fd); g_io_channel_set_close_on_unref(channel, TRUE); g_io_channel_set_encoding(channel, NULL, NULL); g_io_channel_set_buffered(channel, FALSE); source = g_io_add_watch(channel, G_IO_IN | G_IO_HUP | G_IO_ERR | G_IO_NVAL, signal_handler, NULL); g_io_channel_unref(channel); return source; } int interactive(const char *src, const char *dst, const char *dst_type, int psm) { guint input; guint signal; opt_sec_level = g_strdup("low"); opt_src = g_strdup(src); opt_dst = g_strdup(dst); opt_dst_type = g_strdup(dst_type); opt_psm = psm; prompt = g_string_new(NULL); event_loop = g_main_loop_new(NULL, FALSE); input = setup_standard_input(); signal = setup_signalfd(); rl_attempted_completion_function = commands_completion; rl_erase_empty_line = 1; rl_callback_handler_install(get_prompt(), parse_line); g_main_loop_run(event_loop); rl_callback_handler_remove(); cmd_disconnect(0, NULL); g_source_remove(input); g_source_remove(signal); g_main_loop_unref(event_loop); g_string_free(prompt, TRUE); g_free(opt_src); g_free(opt_dst); g_free(opt_sec_level); return 0; } bluez-5.82/attrib/PaxHeaders/utils.c0000644000000000000000000000005014015011623014430 xustar0020 atime=1743516867 20 ctime=1743591280 bluez-5.82/attrib/utils.c0000644000000000000000000000422114015011623014110 0ustar00rootroot// SPDX-License-Identifier: GPL-2.0-or-later /* * * BlueZ - Bluetooth protocol stack for Linux * * Copyright (C) 2011 Nokia Corporation * * */ #ifdef HAVE_CONFIG_H #include #endif #include #include #include "lib/bluetooth.h" #include "lib/hci.h" #include "lib/hci_lib.h" #include "lib/sdp.h" #include "lib/uuid.h" #include "btio/btio.h" #include "att.h" #include "gattrib.h" #include "gatt.h" #include "gatttool.h" GIOChannel *gatt_connect(const char *src, const char *dst, const char *dst_type, const char *sec_level, int psm, int mtu, BtIOConnect connect_cb, GError **gerr) { GIOChannel *chan; bdaddr_t sba, dba; uint8_t dest_type; GError *tmp_err = NULL; BtIOSecLevel sec; str2ba(dst, &dba); /* Local adapter */ if (src != NULL) { if (!strncmp(src, "hci", 3)) hci_devba(atoi(src + 3), &sba); else str2ba(src, &sba); } else bacpy(&sba, BDADDR_ANY); /* Not used for BR/EDR */ if (strcmp(dst_type, "random") == 0) dest_type = BDADDR_LE_RANDOM; else dest_type = BDADDR_LE_PUBLIC; if (strcmp(sec_level, "medium") == 0) sec = BT_IO_SEC_MEDIUM; else if (strcmp(sec_level, "high") == 0) sec = BT_IO_SEC_HIGH; else sec = BT_IO_SEC_LOW; if (psm == 0) chan = bt_io_connect(connect_cb, NULL, NULL, &tmp_err, BT_IO_OPT_SOURCE_BDADDR, &sba, BT_IO_OPT_SOURCE_TYPE, BDADDR_LE_PUBLIC, BT_IO_OPT_DEST_BDADDR, &dba, BT_IO_OPT_DEST_TYPE, dest_type, BT_IO_OPT_CID, ATT_CID, BT_IO_OPT_SEC_LEVEL, sec, BT_IO_OPT_INVALID); else chan = bt_io_connect(connect_cb, NULL, NULL, &tmp_err, BT_IO_OPT_SOURCE_BDADDR, &sba, BT_IO_OPT_DEST_BDADDR, &dba, BT_IO_OPT_PSM, psm, BT_IO_OPT_IMTU, mtu, BT_IO_OPT_SEC_LEVEL, sec, BT_IO_OPT_INVALID); if (tmp_err) { g_propagate_error(gerr, tmp_err); return NULL; } return chan; } size_t gatt_attr_data_from_string(const char *str, uint8_t **data) { char tmp[3]; size_t size, i; size = strlen(str) / 2; *data = g_try_malloc0(size); if (*data == NULL) return 0; tmp[2] = '\0'; for (i = 0; i < size; i++) { memcpy(tmp, str + (i * 2), 2); (*data)[i] = (uint8_t) strtol(tmp, NULL, 16); } return size; } bluez-5.82/attrib/PaxHeaders/gatt.c0000644000000000000000000000005014621503015014233 xustar0020 atime=1743516594 20 ctime=1743591279 bluez-5.82/attrib/gatt.c0000644000000000000000000006512014621503015013720 0ustar00rootroot// SPDX-License-Identifier: GPL-2.0-or-later /* * * BlueZ - Bluetooth protocol stack for Linux * * Copyright (C) 2010 Nokia Corporation * Copyright (C) 2010 Marcel Holtmann * * */ #ifdef HAVE_CONFIG_H #include #endif #include #include #include #include "lib/sdp.h" #include "lib/sdp_lib.h" #include "lib/uuid.h" #include "src/shared/util.h" #include "att.h" #include "gattrib.h" #include "gatt.h" struct discover_primary { int ref; GAttrib *attrib; unsigned int id; bt_uuid_t uuid; uint16_t start; GSList *primaries; gatt_cb_t cb; void *user_data; }; /* Used for the Included Services Discovery (ISD) procedure */ struct included_discovery { GAttrib *attrib; unsigned int id; int refs; int err; uint16_t start_handle; uint16_t end_handle; GSList *includes; gatt_cb_t cb; void *user_data; }; struct included_uuid_query { struct included_discovery *isd; struct gatt_included *included; }; struct discover_char { int ref; GAttrib *attrib; unsigned int id; bt_uuid_t *uuid; uint16_t end; uint16_t start; GSList *characteristics; gatt_cb_t cb; void *user_data; }; struct discover_desc { int ref; GAttrib *attrib; unsigned int id; bt_uuid_t *uuid; uint16_t start; uint16_t end; GSList *descriptors; gatt_cb_t cb; void *user_data; }; static void discover_primary_unref(void *data) { struct discover_primary *dp = data; dp->ref--; if (dp->ref > 0) return; g_slist_free_full(dp->primaries, g_free); g_attrib_unref(dp->attrib); g_free(dp); } static struct discover_primary *discover_primary_ref( struct discover_primary *dp) { dp->ref++; return dp; } static struct included_discovery *isd_ref(struct included_discovery *isd) { __sync_fetch_and_add(&isd->refs, 1); return isd; } static void isd_unref(struct included_discovery *isd) { if (__sync_sub_and_fetch(&isd->refs, 1) > 0) return; if (isd->err) isd->cb(isd->err, NULL, isd->user_data); else isd->cb(isd->err, isd->includes, isd->user_data); g_slist_free_full(isd->includes, g_free); g_attrib_unref(isd->attrib); g_free(isd); } static void discover_char_unref(void *data) { struct discover_char *dc = data; dc->ref--; if (dc->ref > 0) return; g_slist_free_full(dc->characteristics, g_free); g_attrib_unref(dc->attrib); free(dc->uuid); g_free(dc); } static struct discover_char *discover_char_ref(struct discover_char *dc) { dc->ref++; return dc; } static void discover_desc_unref(void *data) { struct discover_desc *dd = data; dd->ref--; if (dd->ref > 0) return; g_slist_free_full(dd->descriptors, g_free); g_attrib_unref(dd->attrib); free(dd->uuid); g_free(dd); } static struct discover_desc *discover_desc_ref(struct discover_desc *dd) { dd->ref++; return dd; } static void put_uuid_le(const bt_uuid_t *uuid, void *dst) { if (uuid->type == BT_UUID16) put_le16(uuid->value.u16, dst); else /* Convert from 128-bit BE to LE */ bswap_128(&uuid->value.u128, dst); } static void get_uuid128(uint8_t type, const void *val, bt_uuid_t *uuid) { if (type == BT_UUID16) { bt_uuid_t uuid16; bt_uuid16_create(&uuid16, get_le16(val)); bt_uuid_to_uuid128(&uuid16, uuid); } else { uint128_t u128; /* Convert from 128-bit LE to BE */ bswap_128(val, &u128); bt_uuid128_create(uuid, u128); } } static guint16 encode_discover_primary(uint16_t start, uint16_t end, bt_uuid_t *uuid, uint8_t *pdu, size_t len) { bt_uuid_t prim; guint16 plen; bt_uuid16_create(&prim, GATT_PRIM_SVC_UUID); if (uuid == NULL) { /* Discover all primary services */ plen = enc_read_by_grp_req(start, end, &prim, pdu, len); } else { uint8_t value[16]; size_t vlen; /* Discover primary service by service UUID */ put_uuid_le(uuid, value); vlen = bt_uuid_len(uuid); plen = enc_find_by_type_req(start, end, &prim, value, vlen, pdu, len); } return plen; } static void primary_by_uuid_cb(guint8 status, const guint8 *ipdu, guint16 iplen, gpointer user_data) { struct discover_primary *dp = user_data; GSList *ranges, *last; struct att_range *range; uint8_t *buf; guint16 oplen; int err = 0; size_t buflen; if (status) { err = status == ATT_ECODE_ATTR_NOT_FOUND ? 0 : status; goto done; } ranges = dec_find_by_type_resp(ipdu, iplen); if (ranges == NULL) goto done; dp->primaries = g_slist_concat(dp->primaries, ranges); last = g_slist_last(ranges); range = last->data; if (range->end == 0xffff) goto done; /* * If last handle is lower from previous start handle then it is smth * wrong. Let's stop search, otherwise we might enter infinite loop. */ if (range->end < dp->start) { err = ATT_ECODE_UNLIKELY; goto done; } dp->start = range->end + 1; buf = g_attrib_get_buffer(dp->attrib, &buflen); oplen = encode_discover_primary(dp->start, 0xffff, &dp->uuid, buf, buflen); if (oplen == 0) goto done; g_attrib_send(dp->attrib, dp->id, buf, oplen, primary_by_uuid_cb, discover_primary_ref(dp), discover_primary_unref); return; done: dp->cb(err, dp->primaries, dp->user_data); } static void primary_all_cb(guint8 status, const guint8 *ipdu, guint16 iplen, gpointer user_data) { struct discover_primary *dp = user_data; struct att_data_list *list; unsigned int i, err; uint16_t start, end; uint8_t type; if (status) { err = status == ATT_ECODE_ATTR_NOT_FOUND ? 0 : status; goto done; } list = dec_read_by_grp_resp(ipdu, iplen); if (list == NULL) { err = ATT_ECODE_IO; goto done; } if (list->len == 6) type = BT_UUID16; else if (list->len == 20) type = BT_UUID128; else { att_data_list_free(list); err = ATT_ECODE_INVALID_PDU; goto done; } for (i = 0, end = 0; i < list->num; i++) { const uint8_t *data = list->data[i]; struct gatt_primary *primary; bt_uuid_t uuid128; start = get_le16(&data[0]); end = get_le16(&data[2]); get_uuid128(type, &data[4], &uuid128); primary = g_try_new0(struct gatt_primary, 1); if (!primary) { att_data_list_free(list); err = ATT_ECODE_INSUFF_RESOURCES; goto done; } primary->range.start = start; primary->range.end = end; bt_uuid_to_string(&uuid128, primary->uuid, sizeof(primary->uuid)); dp->primaries = g_slist_append(dp->primaries, primary); } att_data_list_free(list); err = 0; /* * If last handle is lower from previous start handle then it is smth * wrong. Let's stop search, otherwise we might enter infinite loop. */ if (end < dp->start) { err = ATT_ECODE_UNLIKELY; goto done; } dp->start = end + 1; if (end != 0xffff) { size_t buflen; uint8_t *buf = g_attrib_get_buffer(dp->attrib, &buflen); guint16 oplen = encode_discover_primary(dp->start, 0xffff, NULL, buf, buflen); g_attrib_send(dp->attrib, dp->id, buf, oplen, primary_all_cb, discover_primary_ref(dp), discover_primary_unref); return; } done: dp->cb(err, dp->primaries, dp->user_data); } guint gatt_discover_primary(GAttrib *attrib, bt_uuid_t *uuid, gatt_cb_t func, gpointer user_data) { struct discover_primary *dp; size_t buflen; uint8_t *buf = g_attrib_get_buffer(attrib, &buflen); GAttribResultFunc cb; guint16 plen; plen = encode_discover_primary(0x0001, 0xffff, uuid, buf, buflen); if (plen == 0) return 0; dp = g_try_new0(struct discover_primary, 1); if (dp == NULL) return 0; dp->attrib = g_attrib_ref(attrib); dp->cb = func; dp->user_data = user_data; dp->start = 0x0001; if (uuid) { dp->uuid = *uuid; cb = primary_by_uuid_cb; } else cb = primary_all_cb; dp->id = g_attrib_send(attrib, 0, buf, plen, cb, discover_primary_ref(dp), discover_primary_unref); return dp->id; } static void resolve_included_uuid_cb(uint8_t status, const uint8_t *pdu, uint16_t len, gpointer user_data) { struct included_uuid_query *query = user_data; struct included_discovery *isd = query->isd; struct gatt_included *incl = query->included; unsigned int err = status; bt_uuid_t uuid128; size_t buflen; uint8_t *buf; if (err) goto done; buf = g_attrib_get_buffer(isd->attrib, &buflen); if (dec_read_resp(pdu, len, buf, buflen) != 16) { err = ATT_ECODE_IO; goto done; } get_uuid128(BT_UUID128, buf, &uuid128); bt_uuid_to_string(&uuid128, incl->uuid, sizeof(incl->uuid)); isd->includes = g_slist_append(isd->includes, incl); query->included = NULL; done: if (isd->err == 0) isd->err = err; } static void inc_query_free(void *data) { struct included_uuid_query *query = data; isd_unref(query->isd); g_free(query->included); g_free(query); } static guint resolve_included_uuid(struct included_discovery *isd, struct gatt_included *incl) { struct included_uuid_query *query; size_t buflen; uint8_t *buf = g_attrib_get_buffer(isd->attrib, &buflen); guint16 oplen = enc_read_req(incl->range.start, buf, buflen); query = g_new0(struct included_uuid_query, 1); query->isd = isd_ref(isd); query->included = incl; return g_attrib_send(isd->attrib, query->isd->id, buf, oplen, resolve_included_uuid_cb, query, inc_query_free); } static struct gatt_included *included_from_buf(const uint8_t *buf, gsize len) { struct gatt_included *incl = g_new0(struct gatt_included, 1); incl->handle = get_le16(&buf[0]); incl->range.start = get_le16(&buf[2]); incl->range.end = get_le16(&buf[4]); if (len == 8) { bt_uuid_t uuid128; get_uuid128(BT_UUID16, &buf[6], &uuid128); bt_uuid_to_string(&uuid128, incl->uuid, sizeof(incl->uuid)); } return incl; } static void find_included_cb(uint8_t status, const uint8_t *pdu, uint16_t len, gpointer user_data); static guint find_included(struct included_discovery *isd, uint16_t start) { bt_uuid_t uuid; size_t buflen; uint8_t *buf = g_attrib_get_buffer(isd->attrib, &buflen); guint16 oplen; bt_uuid16_create(&uuid, GATT_INCLUDE_UUID); oplen = enc_read_by_type_req(start, isd->end_handle, &uuid, buf, buflen); /* If id != 0 it means we are in the middle of include search */ if (isd->id) return g_attrib_send(isd->attrib, isd->id, buf, oplen, find_included_cb, isd_ref(isd), (GDestroyNotify) isd_unref); /* This is first call from the gattrib user */ isd->id = g_attrib_send(isd->attrib, 0, buf, oplen, find_included_cb, isd_ref(isd), (GDestroyNotify) isd_unref); return isd->id; } static void find_included_cb(uint8_t status, const uint8_t *pdu, uint16_t len, gpointer user_data) { struct included_discovery *isd = user_data; uint16_t last_handle = isd->end_handle; unsigned int err = status; struct att_data_list *list; int i; if (err == ATT_ECODE_ATTR_NOT_FOUND) err = 0; if (status) goto done; list = dec_read_by_type_resp(pdu, len); if (list == NULL) { err = ATT_ECODE_IO; goto done; } if (list->len != 6 && list->len != 8) { err = ATT_ECODE_IO; att_data_list_free(list); goto done; } for (i = 0; i < list->num; i++) { struct gatt_included *incl; incl = included_from_buf(list->data[i], list->len); last_handle = incl->handle; /* 128 bit UUID, needs resolving */ if (list->len == 6) { resolve_included_uuid(isd, incl); continue; } isd->includes = g_slist_append(isd->includes, incl); } att_data_list_free(list); /* * If last handle is lower from previous start handle then it is smth * wrong. Let's stop search, otherwise we might enter infinite loop. */ if (last_handle < isd->start_handle) { isd->err = ATT_ECODE_UNLIKELY; goto done; } isd->start_handle = last_handle + 1; if (last_handle < isd->end_handle) find_included(isd, isd->start_handle); done: if (isd->err == 0) isd->err = err; } unsigned int gatt_find_included(GAttrib *attrib, uint16_t start, uint16_t end, gatt_cb_t func, gpointer user_data) { struct included_discovery *isd; isd = g_new0(struct included_discovery, 1); isd->attrib = g_attrib_ref(attrib); isd->start_handle = start; isd->end_handle = end; isd->cb = func; isd->user_data = user_data; return find_included(isd, start); } static void char_discovered_cb(guint8 status, const guint8 *ipdu, guint16 iplen, gpointer user_data) { struct discover_char *dc = user_data; struct att_data_list *list; unsigned int i, err = 0; uint16_t last = 0; uint8_t type; /* We have all the characteristic now, lets send it up */ if (status == ATT_ECODE_ATTR_NOT_FOUND) { err = dc->characteristics ? 0 : status; goto done; } if (status) { err = status; goto done; } list = dec_read_by_type_resp(ipdu, iplen); if (list == NULL) { err = ATT_ECODE_IO; goto done; } if (list->len == 7) type = BT_UUID16; else type = BT_UUID128; for (i = 0; i < list->num; i++) { uint8_t *value = list->data[i]; struct gatt_char *chars; bt_uuid_t uuid128; last = get_le16(value); get_uuid128(type, &value[5], &uuid128); if (dc->uuid && bt_uuid_cmp(dc->uuid, &uuid128)) continue; chars = g_try_new0(struct gatt_char, 1); if (!chars) { att_data_list_free(list); err = ATT_ECODE_INSUFF_RESOURCES; goto done; } chars->handle = last; chars->properties = value[2]; chars->value_handle = get_le16(&value[3]); bt_uuid_to_string(&uuid128, chars->uuid, sizeof(chars->uuid)); dc->characteristics = g_slist_append(dc->characteristics, chars); } att_data_list_free(list); /* * If last handle is lower from previous start handle then it is smth * wrong. Let's stop search, otherwise we might enter infinite loop. */ if (last < dc->start) { err = ATT_ECODE_UNLIKELY; goto done; } dc->start = last + 1; if (last != 0 && (dc->start < dc->end)) { bt_uuid_t uuid; guint16 oplen; size_t buflen; uint8_t *buf; buf = g_attrib_get_buffer(dc->attrib, &buflen); bt_uuid16_create(&uuid, GATT_CHARAC_UUID); oplen = enc_read_by_type_req(dc->start, dc->end, &uuid, buf, buflen); if (oplen == 0) return; g_attrib_send(dc->attrib, dc->id, buf, oplen, char_discovered_cb, discover_char_ref(dc), discover_char_unref); return; } done: dc->cb(err, dc->characteristics, dc->user_data); } guint gatt_discover_char(GAttrib *attrib, uint16_t start, uint16_t end, bt_uuid_t *uuid, gatt_cb_t func, gpointer user_data) { size_t buflen; uint8_t *buf = g_attrib_get_buffer(attrib, &buflen); struct discover_char *dc; bt_uuid_t type_uuid; guint16 plen; bt_uuid16_create(&type_uuid, GATT_CHARAC_UUID); plen = enc_read_by_type_req(start, end, &type_uuid, buf, buflen); if (plen == 0) return 0; dc = g_try_new0(struct discover_char, 1); if (dc == NULL) return 0; dc->attrib = g_attrib_ref(attrib); dc->cb = func; dc->user_data = user_data; dc->end = end; dc->start = start; dc->uuid = util_memdup(uuid, sizeof(bt_uuid_t)); dc->id = g_attrib_send(attrib, 0, buf, plen, char_discovered_cb, discover_char_ref(dc), discover_char_unref); return dc->id; } guint gatt_read_char_by_uuid(GAttrib *attrib, uint16_t start, uint16_t end, bt_uuid_t *uuid, GAttribResultFunc func, gpointer user_data) { size_t buflen; uint8_t *buf = g_attrib_get_buffer(attrib, &buflen); guint16 plen; plen = enc_read_by_type_req(start, end, uuid, buf, buflen); if (plen == 0) return 0; return g_attrib_send(attrib, 0, buf, plen, func, user_data, NULL); } struct read_long_data { GAttrib *attrib; GAttribResultFunc func; gpointer user_data; guint8 *buffer; guint16 size; guint16 handle; guint id; int ref; }; static void read_long_destroy(gpointer user_data) { struct read_long_data *long_read = user_data; if (__sync_sub_and_fetch(&long_read->ref, 1) > 0) return; g_attrib_unref(long_read->attrib); if (long_read->buffer != NULL) g_free(long_read->buffer); g_free(long_read); } static void read_blob_helper(guint8 status, const guint8 *rpdu, guint16 rlen, gpointer user_data) { struct read_long_data *long_read = user_data; uint8_t *buf; size_t buflen; guint8 *tmp; guint16 plen; guint id; if (status != 0 || rlen == 1) { status = 0; goto done; } tmp = g_try_realloc(long_read->buffer, long_read->size + rlen - 1); if (tmp == NULL) { status = ATT_ECODE_INSUFF_RESOURCES; goto done; } memcpy(&tmp[long_read->size], &rpdu[1], rlen - 1); long_read->buffer = tmp; long_read->size += rlen - 1; buf = g_attrib_get_buffer(long_read->attrib, &buflen); if (rlen < buflen) goto done; plen = enc_read_blob_req(long_read->handle, long_read->size - 1, buf, buflen); id = g_attrib_send(long_read->attrib, long_read->id, buf, plen, read_blob_helper, long_read, read_long_destroy); if (id != 0) { __sync_fetch_and_add(&long_read->ref, 1); return; } status = ATT_ECODE_IO; done: long_read->func(status, long_read->buffer, long_read->size, long_read->user_data); } static void read_char_helper(guint8 status, const guint8 *rpdu, guint16 rlen, gpointer user_data) { struct read_long_data *long_read = user_data; size_t buflen; uint8_t *buf = g_attrib_get_buffer(long_read->attrib, &buflen); guint16 plen; guint id; if (status != 0 || rlen < buflen) goto done; long_read->buffer = g_malloc(rlen); if (long_read->buffer == NULL) { status = ATT_ECODE_INSUFF_RESOURCES; goto done; } memcpy(long_read->buffer, rpdu, rlen); long_read->size = rlen; plen = enc_read_blob_req(long_read->handle, rlen - 1, buf, buflen); id = g_attrib_send(long_read->attrib, long_read->id, buf, plen, read_blob_helper, long_read, read_long_destroy); if (id != 0) { __sync_fetch_and_add(&long_read->ref, 1); return; } status = ATT_ECODE_IO; done: long_read->func(status, rpdu, rlen, long_read->user_data); } guint gatt_read_char(GAttrib *attrib, uint16_t handle, GAttribResultFunc func, gpointer user_data) { uint8_t *buf; size_t buflen; guint16 plen; guint id; struct read_long_data *long_read; long_read = g_try_new0(struct read_long_data, 1); if (long_read == NULL) return 0; long_read->attrib = g_attrib_ref(attrib); long_read->func = func; long_read->user_data = user_data; long_read->handle = handle; buf = g_attrib_get_buffer(attrib, &buflen); plen = enc_read_req(handle, buf, buflen); id = g_attrib_send(attrib, 0, buf, plen, read_char_helper, long_read, read_long_destroy); if (id == 0) { g_attrib_unref(long_read->attrib); g_free(long_read); } else { __sync_fetch_and_add(&long_read->ref, 1); long_read->id = id; } return id; } struct write_long_data { GAttrib *attrib; GAttribResultFunc func; gpointer user_data; guint16 handle; uint16_t offset; uint8_t *value; size_t vlen; }; static guint execute_write(GAttrib *attrib, uint8_t flags, GAttribResultFunc func, gpointer user_data) { uint8_t *buf; size_t buflen; guint16 plen; buf = g_attrib_get_buffer(attrib, &buflen); plen = enc_exec_write_req(flags, buf, buflen); if (plen == 0) return 0; return g_attrib_send(attrib, 0, buf, plen, func, user_data, NULL); } static guint prepare_write(struct write_long_data *long_write); static void prepare_write_cb(guint8 status, const guint8 *rpdu, guint16 rlen, gpointer user_data) { struct write_long_data *long_write = user_data; if (status != 0) { long_write->func(status, rpdu, rlen, long_write->user_data); return; } /* Skip Prepare Write Response PDU header (5 bytes) */ long_write->offset += rlen - 5; if (long_write->offset == long_write->vlen) { execute_write(long_write->attrib, ATT_WRITE_ALL_PREP_WRITES, long_write->func, long_write->user_data); free(long_write->value); g_free(long_write); return; } prepare_write(long_write); } static guint prepare_write(struct write_long_data *long_write) { GAttrib *attrib = long_write->attrib; uint16_t handle = long_write->handle; uint16_t offset = long_write->offset; uint8_t *buf, *value = long_write->value + offset; size_t buflen, vlen = long_write->vlen - offset; guint16 plen; buf = g_attrib_get_buffer(attrib, &buflen); plen = enc_prep_write_req(handle, offset, value, vlen, buf, buflen); if (plen == 0) return 0; return g_attrib_send(attrib, 0, buf, plen, prepare_write_cb, long_write, NULL); } guint gatt_write_char(GAttrib *attrib, uint16_t handle, const uint8_t *value, size_t vlen, GAttribResultFunc func, gpointer user_data) { uint8_t *buf; size_t buflen; struct write_long_data *long_write; buf = g_attrib_get_buffer(attrib, &buflen); /* Use Write Request if payload fits on a single transfer, including 3 * bytes for the header. */ if (vlen <= buflen - 3) { uint16_t plen; plen = enc_write_req(handle, value, vlen, buf, buflen); if (plen == 0) return 0; return g_attrib_send(attrib, 0, buf, plen, func, user_data, NULL); } /* Write Long Characteristic Values */ long_write = g_try_new0(struct write_long_data, 1); if (long_write == NULL) return 0; long_write->attrib = attrib; long_write->func = func; long_write->user_data = user_data; long_write->handle = handle; long_write->value = util_memdup(value, vlen); long_write->vlen = vlen; return prepare_write(long_write); } guint gatt_execute_write(GAttrib *attrib, uint8_t flags, GAttribResultFunc func, gpointer user_data) { return execute_write(attrib, flags, func, user_data); } guint gatt_reliable_write_char(GAttrib *attrib, uint16_t handle, const uint8_t *value, size_t vlen, GAttribResultFunc func, gpointer user_data) { uint8_t *buf; guint16 plen; size_t buflen; buf = g_attrib_get_buffer(attrib, &buflen); plen = enc_prep_write_req(handle, 0, value, vlen, buf, buflen); if (!plen) return 0; return g_attrib_send(attrib, 0, buf, plen, func, user_data, NULL); } guint gatt_exchange_mtu(GAttrib *attrib, uint16_t mtu, GAttribResultFunc func, gpointer user_data) { uint8_t *buf; size_t buflen; guint16 plen; buf = g_attrib_get_buffer(attrib, &buflen); plen = enc_mtu_req(mtu, buf, buflen); return g_attrib_send(attrib, 0, buf, plen, func, user_data, NULL); } static void desc_discovered_cb(guint8 status, const guint8 *ipdu, guint16 iplen, gpointer user_data) { struct discover_desc *dd = user_data; struct att_data_list *list; unsigned int i, err = 0; guint8 format; uint16_t last = 0xffff; uint8_t type; gboolean uuid_found = FALSE; if (status == ATT_ECODE_ATTR_NOT_FOUND) { err = dd->descriptors ? 0 : status; goto done; } if (status) { err = status; goto done; } list = dec_find_info_resp(ipdu, iplen, &format); if (!list) { err = ATT_ECODE_IO; goto done; } if (format == ATT_FIND_INFO_RESP_FMT_16BIT) type = BT_UUID16; else type = BT_UUID128; for (i = 0; i < list->num; i++) { uint8_t *value = list->data[i]; struct gatt_desc *desc; bt_uuid_t uuid128; last = get_le16(value); get_uuid128(type, &value[2], &uuid128); if (dd->uuid) { if (bt_uuid_cmp(dd->uuid, &uuid128)) continue; else uuid_found = TRUE; } desc = g_try_new0(struct gatt_desc, 1); if (!desc) { att_data_list_free(list); err = ATT_ECODE_INSUFF_RESOURCES; goto done; } bt_uuid_to_string(&uuid128, desc->uuid, sizeof(desc->uuid)); desc->handle = last; if (type == BT_UUID16) desc->uuid16 = get_le16(&value[2]); dd->descriptors = g_slist_append(dd->descriptors, desc); if (uuid_found) break; } att_data_list_free(list); /* * If last handle is lower from previous start handle or if iterating * to the next handle from the last possible offset would overflow, then * something is wrong. Let's stop search, otherwise we might enter * infinite loop. */ if (last < dd->start || last == G_MAXUINT16) { err = ATT_ECODE_UNLIKELY; goto done; } dd->start = last + 1; if (last < dd->end && !uuid_found) { guint16 oplen; size_t buflen; uint8_t *buf; buf = g_attrib_get_buffer(dd->attrib, &buflen); oplen = enc_find_info_req(dd->start, dd->end, buf, buflen); if (oplen == 0) return; g_attrib_send(dd->attrib, dd->id, buf, oplen, desc_discovered_cb, discover_desc_ref(dd), discover_desc_unref); return; } done: dd->cb(err, dd->descriptors, dd->user_data); } guint gatt_discover_desc(GAttrib *attrib, uint16_t start, uint16_t end, bt_uuid_t *uuid, gatt_cb_t func, gpointer user_data) { size_t buflen; uint8_t *buf = g_attrib_get_buffer(attrib, &buflen); struct discover_desc *dd; guint16 plen; plen = enc_find_info_req(start, end, buf, buflen); if (plen == 0) return 0; dd = g_try_new0(struct discover_desc, 1); if (dd == NULL) return 0; dd->attrib = g_attrib_ref(attrib); dd->cb = func; dd->user_data = user_data; dd->start = start; dd->end = end; dd->uuid = util_memdup(uuid, sizeof(bt_uuid_t)); dd->id = g_attrib_send(attrib, 0, buf, plen, desc_discovered_cb, discover_desc_ref(dd), discover_desc_unref); return dd->id; } guint gatt_write_cmd(GAttrib *attrib, uint16_t handle, const uint8_t *value, int vlen, GDestroyNotify notify, gpointer user_data) { uint8_t *buf; size_t buflen; guint16 plen; buf = g_attrib_get_buffer(attrib, &buflen); plen = enc_write_cmd(handle, value, vlen, buf, buflen); return g_attrib_send(attrib, 0, buf, plen, NULL, user_data, notify); } guint gatt_signed_write_cmd(GAttrib *attrib, uint16_t handle, const uint8_t *value, int vlen, struct bt_crypto *crypto, const uint8_t csrk[16], uint32_t sign_cnt, GDestroyNotify notify, gpointer user_data) { uint8_t *buf; size_t buflen; guint16 plen; buf = g_attrib_get_buffer(attrib, &buflen); plen = enc_signed_write_cmd(handle, value, vlen, crypto, csrk, sign_cnt, buf, buflen); if (plen == 0) return 0; return g_attrib_send(attrib, 0, buf, plen, NULL, user_data, notify); } static sdp_data_t *proto_seq_find(sdp_list_t *proto_list) { sdp_list_t *list; uuid_t proto; sdp_uuid16_create(&proto, ATT_UUID); for (list = proto_list; list; list = list->next) { sdp_list_t *p; for (p = list->data; p; p = p->next) { sdp_data_t *seq = p->data; if (seq && seq->dtd == SDP_UUID16 && sdp_uuid16_cmp(&proto, &seq->val.uuid) == 0) return seq->next; } } return NULL; } static gboolean parse_proto_params(sdp_list_t *proto_list, uint16_t *psm, uint16_t *start, uint16_t *end) { sdp_data_t *seq1, *seq2; if (psm) *psm = sdp_get_proto_port(proto_list, L2CAP_UUID); /* Getting start and end handle */ seq1 = proto_seq_find(proto_list); if (!seq1 || seq1->dtd != SDP_UINT16) return FALSE; seq2 = seq1->next; if (!seq2 || seq2->dtd != SDP_UINT16) return FALSE; if (start) *start = seq1->val.uint16; if (end) *end = seq2->val.uint16; return TRUE; } gboolean gatt_parse_record(const sdp_record_t *rec, uuid_t *prim_uuid, uint16_t *psm, uint16_t *start, uint16_t *end) { sdp_list_t *list; uuid_t uuid; gboolean ret; if (sdp_get_service_classes(rec, &list) < 0) return FALSE; memcpy(&uuid, list->data, sizeof(uuid)); sdp_list_free(list, free); if (sdp_get_access_protos(rec, &list) < 0) return FALSE; ret = parse_proto_params(list, psm, start, end); sdp_list_foreach(list, (sdp_list_func_t) sdp_list_free, NULL); sdp_list_free(list, NULL); /* FIXME: replace by bt_uuid_t after uuid_t/sdp code cleanup */ if (ret && prim_uuid) memcpy(prim_uuid, &uuid, sizeof(uuid_t)); return ret; } bluez-5.82/attrib/PaxHeaders/gattrib.h0000644000000000000000000000005014447506754014760 xustar0020 atime=1743516561 20 ctime=1743591279 bluez-5.82/attrib/gattrib.h0000644000000000000000000000366714447506754014455 0ustar00rootroot/* SPDX-License-Identifier: GPL-2.0-or-later */ /* * * BlueZ - Bluetooth protocol stack for Linux * * Copyright (C) 2010 Nokia Corporation * Copyright (C) 2010 Marcel Holtmann * * */ #ifndef __GATTRIB_H #define __GATTRIB_H #ifdef __cplusplus extern "C" { #endif #define GATTRIB_ALL_REQS 0xFE #define GATTRIB_ALL_HANDLES 0x0000 struct bt_att; /* Forward declaration for compatibility */ struct bt_gatt_client; /* Forward declaration for compatibility */ struct _GAttrib; typedef struct _GAttrib GAttrib; typedef void (*GAttribResultFunc) (guint8 status, const guint8 *pdu, guint16 len, gpointer user_data); typedef void (*GAttribDisconnectFunc)(gpointer user_data); typedef void (*GAttribDebugFunc)(const char *str, gpointer user_data); typedef void (*GAttribNotifyFunc)(const guint8 *pdu, guint16 len, gpointer user_data); GAttrib *g_attrib_new(GIOChannel *io, guint16 mtu, bool ext_signed); GAttrib *g_attrib_ref(GAttrib *attrib); void g_attrib_unref(GAttrib *attrib); GIOChannel *g_attrib_get_channel(GAttrib *attrib); struct bt_att *g_attrib_get_att(GAttrib *attrib); gboolean g_attrib_set_destroy_function(GAttrib *attrib, GDestroyNotify destroy, gpointer user_data); guint g_attrib_send(GAttrib *attrib, guint id, const guint8 *pdu, guint16 len, GAttribResultFunc func, gpointer user_data, GDestroyNotify notify); gboolean g_attrib_cancel(GAttrib *attrib, guint id); gboolean g_attrib_cancel_all(GAttrib *attrib); guint g_attrib_register(GAttrib *attrib, guint8 opcode, guint16 handle, GAttribNotifyFunc func, gpointer user_data, GDestroyNotify notify); uint8_t *g_attrib_get_buffer(GAttrib *attrib, size_t *len); gboolean g_attrib_set_mtu(GAttrib *attrib, int mtu); gboolean g_attrib_attach_client(GAttrib *attrib, struct bt_gatt_client *client); gboolean g_attrib_unregister(GAttrib *attrib, guint id); gboolean g_attrib_unregister_all(GAttrib *attrib); #ifdef __cplusplus } #endif #endif bluez-5.82/attrib/PaxHeaders/att.c0000644000000000000000000000005014015011623014060 xustar0020 atime=1743516591 20 ctime=1743591279 bluez-5.82/attrib/att.c0000644000000000000000000005710414015011623013550 0ustar00rootroot// SPDX-License-Identifier: GPL-2.0-or-later /* * * BlueZ - Bluetooth protocol stack for Linux * * Copyright (C) 2010 Nokia Corporation * Copyright (C) 2010 Marcel Holtmann * * */ #ifdef HAVE_CONFIG_H #include #endif #include #include #include #include #include "lib/bluetooth.h" #include "lib/uuid.h" #include "src/shared/util.h" #include "att.h" static inline void put_uuid_le(const bt_uuid_t *src, void *dst) { if (src->type == BT_UUID16) put_le16(src->value.u16, dst); else /* Convert from 128-bit BE to LE */ bswap_128(&src->value.u128, dst); } const char *att_ecode2str(uint8_t status) { switch (status) { case ATT_ECODE_INVALID_HANDLE: return "Invalid handle"; case ATT_ECODE_READ_NOT_PERM: return "Attribute can't be read"; case ATT_ECODE_WRITE_NOT_PERM: return "Attribute can't be written"; case ATT_ECODE_INVALID_PDU: return "Attribute PDU was invalid"; case ATT_ECODE_AUTHENTICATION: return "Attribute requires authentication before read/write"; case ATT_ECODE_REQ_NOT_SUPP: return "Server doesn't support the request received"; case ATT_ECODE_INVALID_OFFSET: return "Offset past the end of the attribute"; case ATT_ECODE_AUTHORIZATION: return "Attribute requires authorization before read/write"; case ATT_ECODE_PREP_QUEUE_FULL: return "Too many prepare writes have been queued"; case ATT_ECODE_ATTR_NOT_FOUND: return "No attribute found within the given range"; case ATT_ECODE_ATTR_NOT_LONG: return "Attribute can't be read/written using Read Blob Req"; case ATT_ECODE_INSUFF_ENCR_KEY_SIZE: return "Encryption Key Size is insufficient"; case ATT_ECODE_INVAL_ATTR_VALUE_LEN: return "Attribute value length is invalid"; case ATT_ECODE_UNLIKELY: return "Request attribute has encountered an unlikely error"; case ATT_ECODE_INSUFF_ENC: return "Encryption required before read/write"; case ATT_ECODE_UNSUPP_GRP_TYPE: return "Attribute type is not a supported grouping attribute"; case ATT_ECODE_INSUFF_RESOURCES: return "Insufficient Resources to complete the request"; case ATT_ECODE_IO: return "Internal application error: I/O"; case ATT_ECODE_TIMEOUT: return "A timeout occured"; case ATT_ECODE_ABORTED: return "The operation was aborted"; default: return "Unexpected error code"; } } void att_data_list_free(struct att_data_list *list) { if (list == NULL) return; if (list->data) { int i; for (i = 0; i < list->num; i++) g_free(list->data[i]); } g_free(list->data); g_free(list); } struct att_data_list *att_data_list_alloc(uint16_t num, uint16_t len) { struct att_data_list *list; int i; if (len > UINT8_MAX) return NULL; list = g_new0(struct att_data_list, 1); list->len = len; list->num = num; list->data = g_malloc0(sizeof(uint8_t *) * num); for (i = 0; i < num; i++) list->data[i] = g_malloc0(sizeof(uint8_t) * len); return list; } static void get_uuid(uint8_t type, const void *val, bt_uuid_t *uuid) { if (type == BT_UUID16) bt_uuid16_create(uuid, get_le16(val)); else { uint128_t u128; /* Convert from 128-bit LE to BE */ bswap_128(val, &u128); bt_uuid128_create(uuid, u128); } } uint16_t enc_read_by_grp_req(uint16_t start, uint16_t end, bt_uuid_t *uuid, uint8_t *pdu, size_t len) { uint16_t uuid_len; if (!uuid) return 0; if (uuid->type == BT_UUID16) uuid_len = 2; else if (uuid->type == BT_UUID128) uuid_len = 16; else return 0; /* Attribute Opcode (1 octet) */ pdu[0] = ATT_OP_READ_BY_GROUP_REQ; /* Starting Handle (2 octets) */ put_le16(start, &pdu[1]); /* Ending Handle (2 octets) */ put_le16(end, &pdu[3]); /* Attribute Group Type (2 or 16 octet UUID) */ put_uuid_le(uuid, &pdu[5]); return 5 + uuid_len; } uint16_t dec_read_by_grp_req(const uint8_t *pdu, size_t len, uint16_t *start, uint16_t *end, bt_uuid_t *uuid) { const size_t min_len = sizeof(pdu[0]) + sizeof(*start) + sizeof(*end); uint8_t type; if (pdu == NULL) return 0; if (start == NULL || end == NULL || uuid == NULL) return 0; if (pdu[0] != ATT_OP_READ_BY_GROUP_REQ) return 0; if (len == (min_len + 2)) type = BT_UUID16; else if (len == (min_len + 16)) type = BT_UUID128; else return 0; *start = get_le16(&pdu[1]); *end = get_le16(&pdu[3]); get_uuid(type, &pdu[5], uuid); return len; } uint16_t enc_read_by_grp_resp(struct att_data_list *list, uint8_t *pdu, size_t len) { int i; uint16_t w; uint8_t *ptr; if (list == NULL) return 0; if (len < list->len + sizeof(uint8_t) * 2) return 0; pdu[0] = ATT_OP_READ_BY_GROUP_RESP; pdu[1] = list->len; ptr = &pdu[2]; for (i = 0, w = 2; i < list->num && w + list->len <= len; i++) { memcpy(ptr, list->data[i], list->len); ptr += list->len; w += list->len; } return w; } struct att_data_list *dec_read_by_grp_resp(const uint8_t *pdu, size_t len) { struct att_data_list *list; const uint8_t *ptr; uint16_t elen, num; int i; if (pdu[0] != ATT_OP_READ_BY_GROUP_RESP) return NULL; /* PDU must contain at least: * - Attribute Opcode (1 octet) * - Length (1 octet) * - Attribute Data List (at least one entry): * - Attribute Handle (2 octets) * - End Group Handle (2 octets) * - Attribute Value (at least 1 octet) */ if (len < 7) return NULL; elen = pdu[1]; /* Minimum Attribute Data List size */ if (elen < 5) return NULL; /* Reject incomplete Attribute Data List */ if ((len - 2) % elen) return NULL; num = (len - 2) / elen; list = att_data_list_alloc(num, elen); if (list == NULL) return NULL; ptr = &pdu[2]; for (i = 0; i < num; i++) { memcpy(list->data[i], ptr, list->len); ptr += list->len; } return list; } uint16_t enc_find_by_type_req(uint16_t start, uint16_t end, bt_uuid_t *uuid, const uint8_t *value, size_t vlen, uint8_t *pdu, size_t len) { uint16_t min_len = sizeof(pdu[0]) + sizeof(start) + sizeof(end) + sizeof(uint16_t); if (pdu == NULL) return 0; if (!uuid) return 0; if (uuid->type != BT_UUID16) return 0; if (vlen > len - min_len) vlen = len - min_len; pdu[0] = ATT_OP_FIND_BY_TYPE_REQ; put_le16(start, &pdu[1]); put_le16(end, &pdu[3]); put_le16(uuid->value.u16, &pdu[5]); if (vlen > 0) { memcpy(&pdu[7], value, vlen); return min_len + vlen; } return min_len; } uint16_t dec_find_by_type_req(const uint8_t *pdu, size_t len, uint16_t *start, uint16_t *end, bt_uuid_t *uuid, uint8_t *value, size_t *vlen) { if (pdu == NULL) return 0; if (len < 7) return 0; /* Attribute Opcode (1 octet) */ if (pdu[0] != ATT_OP_FIND_BY_TYPE_REQ) return 0; /* First requested handle number (2 octets) */ *start = get_le16(&pdu[1]); /* Last requested handle number (2 octets) */ *end = get_le16(&pdu[3]); /* 16-bit UUID to find (2 octets) */ bt_uuid16_create(uuid, get_le16(&pdu[5])); /* Attribute value to find */ *vlen = len - 7; if (*vlen > 0) memcpy(value, pdu + 7, *vlen); return len; } uint16_t enc_find_by_type_resp(GSList *matches, uint8_t *pdu, size_t len) { GSList *l; uint16_t offset; if (!pdu) return 0; pdu[0] = ATT_OP_FIND_BY_TYPE_RESP; for (l = matches, offset = 1; l && len >= (offset + sizeof(uint16_t) * 2); l = l->next, offset += sizeof(uint16_t) * 2) { struct att_range *range = l->data; put_le16(range->start, &pdu[offset]); put_le16(range->end, &pdu[offset + 2]); } return offset; } GSList *dec_find_by_type_resp(const uint8_t *pdu, size_t len) { struct att_range *range; GSList *matches; off_t offset; /* PDU should contain at least: * - Attribute Opcode (1 octet) * - Handles Information List (at least one entry): * - Found Attribute Handle (2 octets) * - Group End Handle (2 octets) */ if (pdu == NULL || len < 5) return NULL; if (pdu[0] != ATT_OP_FIND_BY_TYPE_RESP) return NULL; /* Reject incomplete Handles Information List */ if ((len - 1) % 4) return NULL; for (offset = 1, matches = NULL; len >= (offset + sizeof(uint16_t) * 2); offset += sizeof(uint16_t) * 2) { range = g_new0(struct att_range, 1); range->start = get_le16(&pdu[offset]); range->end = get_le16(&pdu[offset + 2]); matches = g_slist_append(matches, range); } return matches; } uint16_t enc_read_by_type_req(uint16_t start, uint16_t end, bt_uuid_t *uuid, uint8_t *pdu, size_t len) { uint16_t uuid_len; if (!uuid) return 0; if (uuid->type == BT_UUID16) uuid_len = 2; else if (uuid->type == BT_UUID128) uuid_len = 16; else return 0; /* Attribute Opcode (1 octet) */ pdu[0] = ATT_OP_READ_BY_TYPE_REQ; /* Starting Handle (2 octets) */ put_le16(start, &pdu[1]); /* Ending Handle (2 octets) */ put_le16(end, &pdu[3]); /* Attribute Type (2 or 16 octet UUID) */ put_uuid_le(uuid, &pdu[5]); return 5 + uuid_len; } uint16_t dec_read_by_type_req(const uint8_t *pdu, size_t len, uint16_t *start, uint16_t *end, bt_uuid_t *uuid) { const size_t min_len = sizeof(pdu[0]) + sizeof(*start) + sizeof(*end); uint8_t type; if (pdu == NULL) return 0; if (start == NULL || end == NULL || uuid == NULL) return 0; if (len == (min_len + 2)) type = BT_UUID16; else if (len == (min_len + 16)) type = BT_UUID128; else return 0; if (pdu[0] != ATT_OP_READ_BY_TYPE_REQ) return 0; *start = get_le16(&pdu[1]); *end = get_le16(&pdu[3]); get_uuid(type, &pdu[5], uuid); return len; } uint16_t enc_read_by_type_resp(struct att_data_list *list, uint8_t *pdu, size_t len) { uint8_t *ptr; size_t i, w, l; if (list == NULL) return 0; if (pdu == NULL) return 0; l = MIN(len - 2, list->len); pdu[0] = ATT_OP_READ_BY_TYPE_RESP; pdu[1] = l; ptr = &pdu[2]; for (i = 0, w = 2; i < list->num && w + l <= len; i++) { memcpy(ptr, list->data[i], l); ptr += l; w += l; } return w; } struct att_data_list *dec_read_by_type_resp(const uint8_t *pdu, size_t len) { struct att_data_list *list; const uint8_t *ptr; uint16_t elen, num; int i; if (pdu[0] != ATT_OP_READ_BY_TYPE_RESP) return NULL; /* PDU must contain at least: * - Attribute Opcode (1 octet) * - Length (1 octet) * - Attribute Data List (at least one entry): * - Attribute Handle (2 octets) * - Attribute Value (at least 1 octet) */ if (len < 5) return NULL; elen = pdu[1]; /* Minimum Attribute Data List size */ if (elen < 3) return NULL; /* Reject incomplete Attribute Data List */ if ((len - 2) % elen) return NULL; num = (len - 2) / elen; list = att_data_list_alloc(num, elen); if (list == NULL) return NULL; ptr = &pdu[2]; for (i = 0; i < num; i++) { memcpy(list->data[i], ptr, list->len); ptr += list->len; } return list; } uint16_t enc_write_cmd(uint16_t handle, const uint8_t *value, size_t vlen, uint8_t *pdu, size_t len) { const uint16_t min_len = sizeof(pdu[0]) + sizeof(handle); if (pdu == NULL) return 0; if (vlen > len - min_len) vlen = len - min_len; pdu[0] = ATT_OP_WRITE_CMD; put_le16(handle, &pdu[1]); if (vlen > 0) { memcpy(&pdu[3], value, vlen); return min_len + vlen; } return min_len; } uint16_t dec_write_cmd(const uint8_t *pdu, size_t len, uint16_t *handle, uint8_t *value, size_t *vlen) { const uint16_t min_len = sizeof(pdu[0]) + sizeof(*handle); if (pdu == NULL) return 0; if (value == NULL || vlen == NULL || handle == NULL) return 0; if (len < min_len) return 0; if (pdu[0] != ATT_OP_WRITE_CMD) return 0; *handle = get_le16(&pdu[1]); memcpy(value, pdu + min_len, len - min_len); *vlen = len - min_len; return len; } uint16_t enc_signed_write_cmd(uint16_t handle, const uint8_t *value, size_t vlen, struct bt_crypto *crypto, const uint8_t csrk[16], uint32_t sign_cnt, uint8_t *pdu, size_t len) { const uint16_t hdr_len = sizeof(pdu[0]) + sizeof(handle); const uint16_t min_len = hdr_len + ATT_SIGNATURE_LEN; if (pdu == NULL) return 0; if (vlen > len - min_len) vlen = len - min_len; pdu[0] = ATT_OP_SIGNED_WRITE_CMD; put_le16(handle, &pdu[1]); if (vlen > 0) memcpy(&pdu[hdr_len], value, vlen); if (!bt_crypto_sign_att(crypto, csrk, pdu, hdr_len + vlen, sign_cnt, &pdu[hdr_len + vlen])) return 0; return min_len + vlen; } uint16_t dec_signed_write_cmd(const uint8_t *pdu, size_t len, uint16_t *handle, uint8_t *value, size_t *vlen, uint8_t signature[12]) { const uint16_t hdr_len = sizeof(pdu[0]) + sizeof(*handle); const uint16_t min_len = hdr_len + ATT_SIGNATURE_LEN; if (pdu == NULL) return 0; if (value == NULL || vlen == NULL || handle == NULL) return 0; if (len < min_len) return 0; if (pdu[0] != ATT_OP_SIGNED_WRITE_CMD) return 0; *vlen = len - min_len; *handle = get_le16(&pdu[1]); memcpy(value, pdu + hdr_len, *vlen); memcpy(signature, pdu + hdr_len + *vlen, ATT_SIGNATURE_LEN); return len; } uint16_t enc_write_req(uint16_t handle, const uint8_t *value, size_t vlen, uint8_t *pdu, size_t len) { const uint16_t min_len = sizeof(pdu[0]) + sizeof(handle); if (pdu == NULL) return 0; if (vlen > len - min_len) vlen = len - min_len; pdu[0] = ATT_OP_WRITE_REQ; put_le16(handle, &pdu[1]); if (vlen > 0) { memcpy(&pdu[3], value, vlen); return min_len + vlen; } return min_len; } uint16_t dec_write_req(const uint8_t *pdu, size_t len, uint16_t *handle, uint8_t *value, size_t *vlen) { const uint16_t min_len = sizeof(pdu[0]) + sizeof(*handle); if (pdu == NULL) return 0; if (value == NULL || vlen == NULL || handle == NULL) return 0; if (len < min_len) return 0; if (pdu[0] != ATT_OP_WRITE_REQ) return 0; *handle = get_le16(&pdu[1]); *vlen = len - min_len; if (*vlen > 0) memcpy(value, pdu + min_len, *vlen); return len; } uint16_t enc_write_resp(uint8_t *pdu) { if (pdu == NULL) return 0; pdu[0] = ATT_OP_WRITE_RESP; return sizeof(pdu[0]); } uint16_t dec_write_resp(const uint8_t *pdu, size_t len) { if (pdu == NULL) return 0; if (pdu[0] != ATT_OP_WRITE_RESP) return 0; return len; } uint16_t enc_read_req(uint16_t handle, uint8_t *pdu, size_t len) { if (pdu == NULL) return 0; /* Attribute Opcode (1 octet) */ pdu[0] = ATT_OP_READ_REQ; /* Attribute Handle (2 octets) */ put_le16(handle, &pdu[1]); return 3; } uint16_t enc_read_blob_req(uint16_t handle, uint16_t offset, uint8_t *pdu, size_t len) { if (pdu == NULL) return 0; /* Attribute Opcode (1 octet) */ pdu[0] = ATT_OP_READ_BLOB_REQ; /* Attribute Handle (2 octets) */ put_le16(handle, &pdu[1]); /* Value Offset (2 octets) */ put_le16(offset, &pdu[3]); return 5; } uint16_t dec_read_req(const uint8_t *pdu, size_t len, uint16_t *handle) { const uint16_t min_len = sizeof(pdu[0]) + sizeof(*handle); if (pdu == NULL) return 0; if (handle == NULL) return 0; if (len < min_len) return 0; if (pdu[0] != ATT_OP_READ_REQ) return 0; *handle = get_le16(&pdu[1]); return min_len; } uint16_t dec_read_blob_req(const uint8_t *pdu, size_t len, uint16_t *handle, uint16_t *offset) { const uint16_t min_len = sizeof(pdu[0]) + sizeof(*handle) + sizeof(*offset); if (pdu == NULL) return 0; if (handle == NULL) return 0; if (offset == NULL) return 0; if (len < min_len) return 0; if (pdu[0] != ATT_OP_READ_BLOB_REQ) return 0; *handle = get_le16(&pdu[1]); *offset = get_le16(&pdu[3]); return min_len; } uint16_t enc_read_resp(uint8_t *value, size_t vlen, uint8_t *pdu, size_t len) { if (pdu == NULL) return 0; /* If the attribute value length is longer than the allowed PDU size, * send only the octets that fit on the PDU. The remaining octets can * be requested using the Read Blob Request. */ if (vlen > len - 1) vlen = len - 1; pdu[0] = ATT_OP_READ_RESP; memcpy(pdu + 1, value, vlen); return vlen + 1; } uint16_t enc_read_blob_resp(uint8_t *value, size_t vlen, uint16_t offset, uint8_t *pdu, size_t len) { if (pdu == NULL) return 0; vlen -= offset; if (vlen > len - 1) vlen = len - 1; pdu[0] = ATT_OP_READ_BLOB_RESP; memcpy(pdu + 1, &value[offset], vlen); return vlen + 1; } ssize_t dec_read_resp(const uint8_t *pdu, size_t len, uint8_t *value, size_t vlen) { if (pdu == NULL) return -EINVAL; if (pdu[0] != ATT_OP_READ_RESP) return -EINVAL; if (value == NULL) return len - 1; if (vlen < (len - 1)) return -ENOBUFS; memcpy(value, pdu + 1, len - 1); return len - 1; } uint16_t enc_error_resp(uint8_t opcode, uint16_t handle, uint8_t status, uint8_t *pdu, size_t len) { /* Attribute Opcode (1 octet) */ pdu[0] = ATT_OP_ERROR; /* Request Opcode In Error (1 octet) */ pdu[1] = opcode; /* Attribute Handle In Error (2 octets) */ put_le16(handle, &pdu[2]); /* Error Code (1 octet) */ pdu[4] = status; return 5; } uint16_t enc_find_info_req(uint16_t start, uint16_t end, uint8_t *pdu, size_t len) { if (pdu == NULL) return 0; /* Attribute Opcode (1 octet) */ pdu[0] = ATT_OP_FIND_INFO_REQ; /* Starting Handle (2 octets) */ put_le16(start, &pdu[1]); /* Ending Handle (2 octets) */ put_le16(end, &pdu[3]); return 5; } uint16_t dec_find_info_req(const uint8_t *pdu, size_t len, uint16_t *start, uint16_t *end) { const uint16_t min_len = sizeof(pdu[0]) + sizeof(*start) + sizeof(*end); if (pdu == NULL) return 0; if (len < min_len) return 0; if (start == NULL || end == NULL) return 0; if (pdu[0] != ATT_OP_FIND_INFO_REQ) return 0; *start = get_le16(&pdu[1]); *end = get_le16(&pdu[3]); return min_len; } uint16_t enc_find_info_resp(uint8_t format, struct att_data_list *list, uint8_t *pdu, size_t len) { uint8_t *ptr; size_t i, w; if (pdu == NULL) return 0; if (list == NULL) return 0; if (len < list->len + sizeof(uint8_t) * 2) return 0; pdu[0] = ATT_OP_FIND_INFO_RESP; pdu[1] = format; ptr = (void *) &pdu[2]; for (i = 0, w = 2; i < list->num && w + list->len <= len; i++) { memcpy(ptr, list->data[i], list->len); ptr += list->len; w += list->len; } return w; } struct att_data_list *dec_find_info_resp(const uint8_t *pdu, size_t len, uint8_t *format) { struct att_data_list *list; uint8_t *ptr; uint16_t elen, num; int i; if (pdu == NULL) return 0; if (format == NULL) return 0; if (pdu[0] != ATT_OP_FIND_INFO_RESP) return 0; *format = pdu[1]; elen = sizeof(pdu[0]) + sizeof(*format); if (*format == 0x01) elen += 2; else if (*format == 0x02) elen += 16; num = (len - 2) / elen; ptr = (void *) &pdu[2]; list = att_data_list_alloc(num, elen); if (list == NULL) return NULL; for (i = 0; i < num; i++) { memcpy(list->data[i], ptr, list->len); ptr += list->len; } return list; } uint16_t enc_notification(uint16_t handle, uint8_t *value, size_t vlen, uint8_t *pdu, size_t len) { const uint16_t min_len = sizeof(pdu[0]) + sizeof(uint16_t); if (pdu == NULL) return 0; if (len < (vlen + min_len)) return 0; pdu[0] = ATT_OP_HANDLE_NOTIFY; put_le16(handle, &pdu[1]); memcpy(&pdu[3], value, vlen); return vlen + min_len; } uint16_t enc_indication(uint16_t handle, uint8_t *value, size_t vlen, uint8_t *pdu, size_t len) { const uint16_t min_len = sizeof(pdu[0]) + sizeof(uint16_t); if (pdu == NULL) return 0; if (len < (vlen + min_len)) return 0; pdu[0] = ATT_OP_HANDLE_IND; put_le16(handle, &pdu[1]); memcpy(&pdu[3], value, vlen); return vlen + min_len; } uint16_t dec_indication(const uint8_t *pdu, size_t len, uint16_t *handle, uint8_t *value, size_t vlen) { const uint16_t min_len = sizeof(pdu[0]) + sizeof(uint16_t); uint16_t dlen; if (pdu == NULL) return 0; if (pdu[0] != ATT_OP_HANDLE_IND) return 0; if (len < min_len) return 0; dlen = MIN(len - min_len, vlen); if (handle) *handle = get_le16(&pdu[1]); memcpy(value, &pdu[3], dlen); return dlen; } uint16_t enc_confirmation(uint8_t *pdu, size_t len) { if (pdu == NULL) return 0; /* Attribute Opcode (1 octet) */ pdu[0] = ATT_OP_HANDLE_CNF; return 1; } uint16_t enc_mtu_req(uint16_t mtu, uint8_t *pdu, size_t len) { if (pdu == NULL) return 0; /* Attribute Opcode (1 octet) */ pdu[0] = ATT_OP_MTU_REQ; /* Client Rx MTU (2 octets) */ put_le16(mtu, &pdu[1]); return 3; } uint16_t dec_mtu_req(const uint8_t *pdu, size_t len, uint16_t *mtu) { const uint16_t min_len = sizeof(pdu[0]) + sizeof(*mtu); if (pdu == NULL) return 0; if (mtu == NULL) return 0; if (len < min_len) return 0; if (pdu[0] != ATT_OP_MTU_REQ) return 0; *mtu = get_le16(&pdu[1]); return min_len; } uint16_t enc_mtu_resp(uint16_t mtu, uint8_t *pdu, size_t len) { if (pdu == NULL) return 0; /* Attribute Opcode (1 octet) */ pdu[0] = ATT_OP_MTU_RESP; /* Server Rx MTU (2 octets) */ put_le16(mtu, &pdu[1]); return 3; } uint16_t dec_mtu_resp(const uint8_t *pdu, size_t len, uint16_t *mtu) { const uint16_t min_len = sizeof(pdu[0]) + sizeof(*mtu); if (pdu == NULL) return 0; if (mtu == NULL) return 0; if (len < min_len) return 0; if (pdu[0] != ATT_OP_MTU_RESP) return 0; *mtu = get_le16(&pdu[1]); return min_len; } uint16_t enc_prep_write_req(uint16_t handle, uint16_t offset, const uint8_t *value, size_t vlen, uint8_t *pdu, size_t len) { const uint16_t min_len = sizeof(pdu[0]) + sizeof(handle) + sizeof(offset); if (pdu == NULL) return 0; if (vlen > len - min_len) vlen = len - min_len; pdu[0] = ATT_OP_PREP_WRITE_REQ; put_le16(handle, &pdu[1]); put_le16(offset, &pdu[3]); if (vlen > 0) { memcpy(&pdu[5], value, vlen); return min_len + vlen; } return min_len; } uint16_t dec_prep_write_req(const uint8_t *pdu, size_t len, uint16_t *handle, uint16_t *offset, uint8_t *value, size_t *vlen) { const uint16_t min_len = sizeof(pdu[0]) + sizeof(*handle) + sizeof(*offset); if (pdu == NULL) return 0; if (handle == NULL || offset == NULL || value == NULL || vlen == NULL) return 0; if (len < min_len) return 0; if (pdu[0] != ATT_OP_PREP_WRITE_REQ) return 0; *handle = get_le16(&pdu[1]); *offset = get_le16(&pdu[3]); *vlen = len - min_len; if (*vlen > 0) memcpy(value, pdu + min_len, *vlen); return len; } uint16_t enc_prep_write_resp(uint16_t handle, uint16_t offset, const uint8_t *value, size_t vlen, uint8_t *pdu, size_t len) { const uint16_t min_len = sizeof(pdu[0]) + sizeof(handle) + sizeof(offset); if (pdu == NULL) return 0; if (vlen > len - min_len) vlen = len - min_len; pdu[0] = ATT_OP_PREP_WRITE_RESP; put_le16(handle, &pdu[1]); put_le16(offset, &pdu[3]); if (vlen > 0) { memcpy(&pdu[5], value, vlen); return min_len + vlen; } return min_len; } uint16_t dec_prep_write_resp(const uint8_t *pdu, size_t len, uint16_t *handle, uint16_t *offset, uint8_t *value, size_t *vlen) { const uint16_t min_len = sizeof(pdu[0]) + sizeof(*handle) + sizeof(*offset); if (pdu == NULL) return 0; if (handle == NULL || offset == NULL || value == NULL || vlen == NULL) return 0; if (len < min_len) return 0; if (pdu[0] != ATT_OP_PREP_WRITE_REQ) return 0; *handle = get_le16(&pdu[1]); *offset = get_le16(&pdu[3]); *vlen = len - min_len; if (*vlen > 0) memcpy(value, pdu + min_len, *vlen); return len; } uint16_t enc_exec_write_req(uint8_t flags, uint8_t *pdu, size_t len) { if (pdu == NULL) return 0; if (flags > 1) return 0; /* Attribute Opcode (1 octet) */ pdu[0] = ATT_OP_EXEC_WRITE_REQ; /* Flags (1 octet) */ pdu[1] = flags; return 2; } uint16_t dec_exec_write_req(const uint8_t *pdu, size_t len, uint8_t *flags) { const uint16_t min_len = sizeof(pdu[0]) + sizeof(*flags); if (pdu == NULL) return 0; if (flags == NULL) return 0; if (len < min_len) return 0; if (pdu[0] != ATT_OP_EXEC_WRITE_REQ) return 0; *flags = pdu[1]; return min_len; } uint16_t enc_exec_write_resp(uint8_t *pdu) { if (pdu == NULL) return 0; /* Attribute Opcode (1 octet) */ pdu[0] = ATT_OP_EXEC_WRITE_RESP; return 1; } uint16_t dec_exec_write_resp(const uint8_t *pdu, size_t len) { const uint16_t min_len = sizeof(pdu[0]); if (pdu == NULL) return 0; if (len < min_len) return 0; if (pdu[0] != ATT_OP_EXEC_WRITE_RESP) return 0; return len; } bluez-5.82/attrib/PaxHeaders/att.h0000644000000000000000000000005014015011623014065 xustar0020 atime=1743516561 20 ctime=1743591279 bluez-5.82/attrib/att.h0000644000000000000000000001653614015011623013561 0ustar00rootroot/* SPDX-License-Identifier: GPL-2.0-or-later */ /* * * BlueZ - Bluetooth protocol stack for Linux * * Copyright (C) 2010 Nokia Corporation * Copyright (C) 2010 Marcel Holtmann * * */ #include "src/shared/crypto.h" /* Len of signature in write signed packet */ #define ATT_SIGNATURE_LEN 12 /* Attribute Protocol Opcodes */ #define ATT_OP_ERROR 0x01 #define ATT_OP_MTU_REQ 0x02 #define ATT_OP_MTU_RESP 0x03 #define ATT_OP_FIND_INFO_REQ 0x04 #define ATT_OP_FIND_INFO_RESP 0x05 #define ATT_OP_FIND_BY_TYPE_REQ 0x06 #define ATT_OP_FIND_BY_TYPE_RESP 0x07 #define ATT_OP_READ_BY_TYPE_REQ 0x08 #define ATT_OP_READ_BY_TYPE_RESP 0x09 #define ATT_OP_READ_REQ 0x0A #define ATT_OP_READ_RESP 0x0B #define ATT_OP_READ_BLOB_REQ 0x0C #define ATT_OP_READ_BLOB_RESP 0x0D #define ATT_OP_READ_MULTI_REQ 0x0E #define ATT_OP_READ_MULTI_RESP 0x0F #define ATT_OP_READ_BY_GROUP_REQ 0x10 #define ATT_OP_READ_BY_GROUP_RESP 0x11 #define ATT_OP_WRITE_REQ 0x12 #define ATT_OP_WRITE_RESP 0x13 #define ATT_OP_WRITE_CMD 0x52 #define ATT_OP_PREP_WRITE_REQ 0x16 #define ATT_OP_PREP_WRITE_RESP 0x17 #define ATT_OP_EXEC_WRITE_REQ 0x18 #define ATT_OP_EXEC_WRITE_RESP 0x19 #define ATT_OP_HANDLE_NOTIFY 0x1B #define ATT_OP_HANDLE_IND 0x1D #define ATT_OP_HANDLE_CNF 0x1E #define ATT_OP_SIGNED_WRITE_CMD 0xD2 /* Error codes for Error response PDU */ #define ATT_ECODE_INVALID_HANDLE 0x01 #define ATT_ECODE_READ_NOT_PERM 0x02 #define ATT_ECODE_WRITE_NOT_PERM 0x03 #define ATT_ECODE_INVALID_PDU 0x04 #define ATT_ECODE_AUTHENTICATION 0x05 #define ATT_ECODE_REQ_NOT_SUPP 0x06 #define ATT_ECODE_INVALID_OFFSET 0x07 #define ATT_ECODE_AUTHORIZATION 0x08 #define ATT_ECODE_PREP_QUEUE_FULL 0x09 #define ATT_ECODE_ATTR_NOT_FOUND 0x0A #define ATT_ECODE_ATTR_NOT_LONG 0x0B #define ATT_ECODE_INSUFF_ENCR_KEY_SIZE 0x0C #define ATT_ECODE_INVAL_ATTR_VALUE_LEN 0x0D #define ATT_ECODE_UNLIKELY 0x0E #define ATT_ECODE_INSUFF_ENC 0x0F #define ATT_ECODE_UNSUPP_GRP_TYPE 0x10 #define ATT_ECODE_INSUFF_RESOURCES 0x11 /* Application error */ #define ATT_ECODE_IO 0x80 #define ATT_ECODE_TIMEOUT 0x81 #define ATT_ECODE_ABORTED 0x82 #define ATT_MAX_VALUE_LEN 512 #define ATT_DEFAULT_L2CAP_MTU 48 #define ATT_DEFAULT_LE_MTU 23 #define ATT_CID 4 #define ATT_PSM 31 /* Flags for Execute Write Request Operation */ #define ATT_CANCEL_ALL_PREP_WRITES 0x00 #define ATT_WRITE_ALL_PREP_WRITES 0x01 /* Find Information Response Formats */ #define ATT_FIND_INFO_RESP_FMT_16BIT 0x01 #define ATT_FIND_INFO_RESP_FMT_128BIT 0x02 struct att_data_list { uint16_t num; uint16_t len; uint8_t **data; }; struct att_range { uint16_t start; uint16_t end; }; struct att_data_list *att_data_list_alloc(uint16_t num, uint16_t len); void att_data_list_free(struct att_data_list *list); const char *att_ecode2str(uint8_t status); uint16_t enc_read_by_grp_req(uint16_t start, uint16_t end, bt_uuid_t *uuid, uint8_t *pdu, size_t len); uint16_t dec_read_by_grp_req(const uint8_t *pdu, size_t len, uint16_t *start, uint16_t *end, bt_uuid_t *uuid); uint16_t enc_read_by_grp_resp(struct att_data_list *list, uint8_t *pdu, size_t len); uint16_t enc_find_by_type_req(uint16_t start, uint16_t end, bt_uuid_t *uuid, const uint8_t *value, size_t vlen, uint8_t *pdu, size_t len); uint16_t dec_find_by_type_req(const uint8_t *pdu, size_t len, uint16_t *start, uint16_t *end, bt_uuid_t *uuid, uint8_t *value, size_t *vlen); uint16_t enc_find_by_type_resp(GSList *ranges, uint8_t *pdu, size_t len); GSList *dec_find_by_type_resp(const uint8_t *pdu, size_t len); struct att_data_list *dec_read_by_grp_resp(const uint8_t *pdu, size_t len); uint16_t enc_read_by_type_req(uint16_t start, uint16_t end, bt_uuid_t *uuid, uint8_t *pdu, size_t len); uint16_t dec_read_by_type_req(const uint8_t *pdu, size_t len, uint16_t *start, uint16_t *end, bt_uuid_t *uuid); uint16_t enc_read_by_type_resp(struct att_data_list *list, uint8_t *pdu, size_t len); uint16_t enc_write_cmd(uint16_t handle, const uint8_t *value, size_t vlen, uint8_t *pdu, size_t len); uint16_t dec_write_cmd(const uint8_t *pdu, size_t len, uint16_t *handle, uint8_t *value, size_t *vlen); uint16_t enc_signed_write_cmd(uint16_t handle, const uint8_t *value, size_t vlen, struct bt_crypto *crypto, const uint8_t csrk[16], uint32_t sign_cnt, uint8_t *pdu, size_t len); uint16_t dec_signed_write_cmd(const uint8_t *pdu, size_t len, uint16_t *handle, uint8_t *value, size_t *vlen, uint8_t signature[12]); struct att_data_list *dec_read_by_type_resp(const uint8_t *pdu, size_t len); uint16_t enc_write_req(uint16_t handle, const uint8_t *value, size_t vlen, uint8_t *pdu, size_t len); uint16_t dec_write_req(const uint8_t *pdu, size_t len, uint16_t *handle, uint8_t *value, size_t *vlen); uint16_t enc_write_resp(uint8_t *pdu); uint16_t dec_write_resp(const uint8_t *pdu, size_t len); uint16_t enc_read_req(uint16_t handle, uint8_t *pdu, size_t len); uint16_t enc_read_blob_req(uint16_t handle, uint16_t offset, uint8_t *pdu, size_t len); uint16_t dec_read_req(const uint8_t *pdu, size_t len, uint16_t *handle); uint16_t dec_read_blob_req(const uint8_t *pdu, size_t len, uint16_t *handle, uint16_t *offset); uint16_t enc_read_resp(uint8_t *value, size_t vlen, uint8_t *pdu, size_t len); uint16_t enc_read_blob_resp(uint8_t *value, size_t vlen, uint16_t offset, uint8_t *pdu, size_t len); ssize_t dec_read_resp(const uint8_t *pdu, size_t len, uint8_t *value, size_t vlen); uint16_t enc_error_resp(uint8_t opcode, uint16_t handle, uint8_t status, uint8_t *pdu, size_t len); uint16_t enc_find_info_req(uint16_t start, uint16_t end, uint8_t *pdu, size_t len); uint16_t dec_find_info_req(const uint8_t *pdu, size_t len, uint16_t *start, uint16_t *end); uint16_t enc_find_info_resp(uint8_t format, struct att_data_list *list, uint8_t *pdu, size_t len); struct att_data_list *dec_find_info_resp(const uint8_t *pdu, size_t len, uint8_t *format); uint16_t enc_notification(uint16_t handle, uint8_t *value, size_t vlen, uint8_t *pdu, size_t len); uint16_t enc_indication(uint16_t handle, uint8_t *value, size_t vlen, uint8_t *pdu, size_t len); uint16_t dec_indication(const uint8_t *pdu, size_t len, uint16_t *handle, uint8_t *value, size_t vlen); uint16_t enc_confirmation(uint8_t *pdu, size_t len); uint16_t enc_mtu_req(uint16_t mtu, uint8_t *pdu, size_t len); uint16_t dec_mtu_req(const uint8_t *pdu, size_t len, uint16_t *mtu); uint16_t enc_mtu_resp(uint16_t mtu, uint8_t *pdu, size_t len); uint16_t dec_mtu_resp(const uint8_t *pdu, size_t len, uint16_t *mtu); uint16_t enc_prep_write_req(uint16_t handle, uint16_t offset, const uint8_t *value, size_t vlen, uint8_t *pdu, size_t len); uint16_t dec_prep_write_req(const uint8_t *pdu, size_t len, uint16_t *handle, uint16_t *offset, uint8_t *value, size_t *vlen); uint16_t enc_prep_write_resp(uint16_t handle, uint16_t offset, const uint8_t *value, size_t vlen, uint8_t *pdu, size_t len); uint16_t dec_prep_write_resp(const uint8_t *pdu, size_t len, uint16_t *handle, uint16_t *offset, uint8_t *value, size_t *vlen); uint16_t enc_exec_write_req(uint8_t flags, uint8_t *pdu, size_t len); uint16_t dec_exec_write_req(const uint8_t *pdu, size_t len, uint8_t *flags); uint16_t enc_exec_write_resp(uint8_t *pdu); uint16_t dec_exec_write_resp(const uint8_t *pdu, size_t len); bluez-5.82/PaxHeaders/missing0000644000000000000000000000005014773211371013250 xustar0020 atime=1743590147 20 ctime=1743591275 bluez-5.82/missing0000755000000000000000000001533614773211371012744 0ustar00rootroot#! /bin/sh # Common wrapper for a few potentially missing GNU programs. scriptversion=2018-03-07.03; # UTC # Copyright (C) 1996-2021 Free Software Foundation, Inc. # Originally written by Fran,cois Pinard , 1996. # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2, or (at your option) # any later version. # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # You should have received a copy of the GNU General Public License # along with this program. If not, see . # As a special exception to the GNU General Public License, if you # distribute this file as part of a program that contains a # configuration script generated by Autoconf, you may include it under # the same distribution terms that you use for the rest of that program. if test $# -eq 0; then echo 1>&2 "Try '$0 --help' for more information" exit 1 fi case $1 in --is-lightweight) # Used by our autoconf macros to check whether the available missing # script is modern enough. exit 0 ;; --run) # Back-compat with the calling convention used by older automake. shift ;; -h|--h|--he|--hel|--help) echo "\ $0 [OPTION]... PROGRAM [ARGUMENT]... Run 'PROGRAM [ARGUMENT]...', returning a proper advice when this fails due to PROGRAM being missing or too old. Options: -h, --help display this help and exit -v, --version output version information and exit Supported PROGRAM values: aclocal autoconf autoheader autom4te automake makeinfo bison yacc flex lex help2man Version suffixes to PROGRAM as well as the prefixes 'gnu-', 'gnu', and 'g' are ignored when checking the name. Send bug reports to ." exit $? ;; -v|--v|--ve|--ver|--vers|--versi|--versio|--version) echo "missing $scriptversion (GNU Automake)" exit $? ;; -*) echo 1>&2 "$0: unknown '$1' option" echo 1>&2 "Try '$0 --help' for more information" exit 1 ;; esac # Run the given program, remember its exit status. "$@"; st=$? # If it succeeded, we are done. test $st -eq 0 && exit 0 # Also exit now if we it failed (or wasn't found), and '--version' was # passed; such an option is passed most likely to detect whether the # program is present and works. case $2 in --version|--help) exit $st;; esac # Exit code 63 means version mismatch. This often happens when the user # tries to use an ancient version of a tool on a file that requires a # minimum version. if test $st -eq 63; then msg="probably too old" elif test $st -eq 127; then # Program was missing. msg="missing on your system" else # Program was found and executed, but failed. Give up. exit $st fi perl_URL=https://www.perl.org/ flex_URL=https://github.com/westes/flex gnu_software_URL=https://www.gnu.org/software program_details () { case $1 in aclocal|automake) echo "The '$1' program is part of the GNU Automake package:" echo "<$gnu_software_URL/automake>" echo "It also requires GNU Autoconf, GNU m4 and Perl in order to run:" echo "<$gnu_software_URL/autoconf>" echo "<$gnu_software_URL/m4/>" echo "<$perl_URL>" ;; autoconf|autom4te|autoheader) echo "The '$1' program is part of the GNU Autoconf package:" echo "<$gnu_software_URL/autoconf/>" echo "It also requires GNU m4 and Perl in order to run:" echo "<$gnu_software_URL/m4/>" echo "<$perl_URL>" ;; esac } give_advice () { # Normalize program name to check for. normalized_program=`echo "$1" | sed ' s/^gnu-//; t s/^gnu//; t s/^g//; t'` printf '%s\n' "'$1' is $msg." configure_deps="'configure.ac' or m4 files included by 'configure.ac'" case $normalized_program in autoconf*) echo "You should only need it if you modified 'configure.ac'," echo "or m4 files included by it." program_details 'autoconf' ;; autoheader*) echo "You should only need it if you modified 'acconfig.h' or" echo "$configure_deps." program_details 'autoheader' ;; automake*) echo "You should only need it if you modified 'Makefile.am' or" echo "$configure_deps." program_details 'automake' ;; aclocal*) echo "You should only need it if you modified 'acinclude.m4' or" echo "$configure_deps." program_details 'aclocal' ;; autom4te*) echo "You might have modified some maintainer files that require" echo "the 'autom4te' program to be rebuilt." program_details 'autom4te' ;; bison*|yacc*) echo "You should only need it if you modified a '.y' file." echo "You may want to install the GNU Bison package:" echo "<$gnu_software_URL/bison/>" ;; lex*|flex*) echo "You should only need it if you modified a '.l' file." echo "You may want to install the Fast Lexical Analyzer package:" echo "<$flex_URL>" ;; help2man*) echo "You should only need it if you modified a dependency" \ "of a man page." echo "You may want to install the GNU Help2man package:" echo "<$gnu_software_URL/help2man/>" ;; makeinfo*) echo "You should only need it if you modified a '.texi' file, or" echo "any other file indirectly affecting the aspect of the manual." echo "You might want to install the Texinfo package:" echo "<$gnu_software_URL/texinfo/>" echo "The spurious makeinfo call might also be the consequence of" echo "using a buggy 'make' (AIX, DU, IRIX), in which case you might" echo "want to install GNU make:" echo "<$gnu_software_URL/make/>" ;; *) echo "You might have modified some files without having the proper" echo "tools for further handling them. Check the 'README' file, it" echo "often tells you about the needed prerequisites for installing" echo "this package. You may also peek at any GNU archive site, in" echo "case some other package contains this missing '$1' program." ;; esac } give_advice "$1" | sed -e '1s/^/WARNING: /' \ -e '2,$s/^/ /' >&2 # Propagate the correct exit status (expected to be 127 for a program # not found, 63 for a program that failed due to version mismatch). exit $st # Local variables: # eval: (add-hook 'before-save-hook 'time-stamp) # time-stamp-start: "scriptversion=" # time-stamp-format: "%:y-%02m-%02d.%02H" # time-stamp-time-zone: "UTC0" # time-stamp-end: "; # UTC" # End: bluez-5.82/PaxHeaders/configure.ac0000644000000000000000000000005014773211264014143 xustar0020 atime=1743590089 20 ctime=1743591275 bluez-5.82/configure.ac0000644000000000000000000004467014773211264013637 0ustar00rootroot# SPDX-License-Identifier: GPL-2.0 AC_PREREQ(2.60) AC_INIT(bluez, 5.82) AM_INIT_AUTOMAKE([foreign subdir-objects color-tests silent-rules tar-pax no-dist-gzip dist-xz]) AC_CONFIG_HEADERS(config.h) m4_ifdef([AM_SILENT_RULES], [AM_SILENT_RULES([yes])]) AM_MAINTAINER_MODE AC_PREFIX_DEFAULT(/usr/local) PKG_PROG_PKG_CONFIG COMPILER_FLAGS AC_LANG([C]) AC_C_RESTRICT AC_PROG_CC AM_PROG_CC_C_O AC_PROG_CC_PIE AC_PROG_CC_ASAN AC_PROG_CC_LSAN AC_PROG_CC_UBSAN AC_PROG_INSTALL AC_PROG_MKDIR_P m4_define([_LT_AC_TAGCONFIG], []) m4_ifdef([AC_LIBTOOL_TAGS], [AC_LIBTOOL_TAGS([])]) LT_PREREQ(2.2) LT_INIT([disable-static]) if (test "$USE_MAINTAINER_MODE" = "yes"); then AC_CHECK_PROG(enable_coverage, [lcov], [yes], [no]) AC_CHECK_PROG(enable_dbus_run_session, [dbus-run-session], [yes]) AC_CHECK_PROG(enable_valgrind, [valgrind], [yes]) AC_CHECK_HEADERS(valgrind/memcheck.h) fi AM_CONDITIONAL(COVERAGE, test "${enable_coverage}" = "yes") AM_CONDITIONAL(DBUS_RUN_SESSION, test "${enable_dbus_run_session}" = "yes") MISC_FLAGS AM_CONDITIONAL(VALGRIND, test "${enable_valgrind}" = "yes" && test "$ASAN_LIB" != "yes" && test "LSAN_LIB" != "yes") AC_ARG_ENABLE(threads, AS_HELP_STRING([--enable-threads], [enable threading support]), [enable_threads=${enableval}]) AC_CHECK_FUNCS(explicit_bzero) AC_CHECK_FUNCS(getrandom) AC_CHECK_FUNCS(rawmemchr) AC_CHECK_FUNC(signalfd, dummy=yes, AC_MSG_ERROR(signalfd support is required)) AC_CHECK_LIB(rt, clock_gettime, dummy=yes, AC_MSG_ERROR(realtime clock support is required)) AC_CHECK_LIB(pthread, pthread_create, dummy=yes, AC_MSG_ERROR(posix thread support is required)) AC_CHECK_LIB(dl, dlopen, dummy=yes, AC_MSG_ERROR(dynamic linking loader is required)) AC_CHECK_HEADERS(stdio.h string.h linux/types.h linux/if_alg.h linux/uinput.h linux/uhid.h sys/random.h) # basename may be only available in libgen.h with the POSIX behavior, # not desired here AC_CHECK_DECLS([basename], [], AC_MSG_WARN([GNU basename extension not found]), [#define _GNU_SOURCE 1 #include ]) PKG_CHECK_MODULES(GLIB, glib-2.0 >= 2.28) if (test "${enable_threads}" = "yes"); then AC_DEFINE(NEED_THREADS, 1, [Define if threading support is required]) PKG_CHECK_MODULES(GTHREAD, gthread-2.0 >= 2.16) GLIB_CFLAGS="$GLIB_CFLAGS $GTHREAD_CFLAGS" GLIB_LIBS="$GLIB_LIBS $GTHREAD_LIBS" fi PKG_CHECK_MODULES(DBUS, dbus-1 >= 1.10) AC_ARG_WITH([dbusconfdir], AS_HELP_STRING([--with-dbusconfdir=DIR], [path to D-Bus configuration directory]), [path_dbusconfdir=${withval}]) if (test -z "${path_dbusconfdir}"); then AC_MSG_CHECKING([D-Bus configuration directory]) path_dbusconfdir="`$PKG_CONFIG --variable=datadir dbus-1`" if (test -z "${path_dbusconfdir}"); then AC_MSG_ERROR([D-Bus configuration directory is required]) fi AC_MSG_RESULT([${path_dbusconfdir}]) fi AC_SUBST(DBUS_CONFDIR, [${path_dbusconfdir}]) AC_ARG_WITH([dbussystembusdir], AS_HELP_STRING([--with-dbussystembusdir=DIR], [path to D-Bus system bus services directory]), [path_dbussystembusdir=${withval}]) if (test -z "${path_dbussystembusdir}"); then AC_MSG_CHECKING([D-Bus system bus services dir]) path_dbussystembusdir="`$PKG_CONFIG --variable=system_bus_services_dir dbus-1`" if (test -z "${path_dbussystembusdir}"); then AC_MSG_ERROR([D-Bus system bus services directory is required]) fi AC_MSG_RESULT([${path_dbussystembusdir}]) fi AC_SUBST(DBUS_SYSTEMBUSDIR, [${path_dbussystembusdir}]) AC_ARG_WITH([dbussessionbusdir], AS_HELP_STRING([--with-dbussessionbusdir=DIR], [path to D-Bus session bus services directory]), [path_dbussessionbusdir=${withval}]) if (test -z "${path_dbussessionbusdir}"); then AC_MSG_CHECKING([D-Bus session bus services dir]) path_dbussessionbusdir="`$PKG_CONFIG --variable=session_bus_services_dir dbus-1`" if (test -z "${path_dbussessionbusdir}"); then AC_MSG_ERROR([D-Bus session bus services directory is required]) fi AC_MSG_RESULT([${path_dbussessionbusdir}]) fi AC_SUBST(DBUS_SESSIONBUSDIR, [${path_dbussessionbusdir}]) AC_ARG_WITH([zsh-completion-dir], AS_HELP_STRING([--with-zsh-completion-dir=DIR], [path to install zsh completions]), [path_zshcompletiondir=${withval}], [path_zshcompletiondir="yes"]) if (test "${path_zshcompletiondir}" = "yes"); then path_zshcompletiondir="$datarootdir/zsh/site-functions" AC_MSG_RESULT([${path_zshcompletiondir}]) fi AC_SUBST(ZSH_COMPLETIONDIR, [${path_zshcompletiondir}]) AM_CONDITIONAL(ZSH_COMPLETIONS, test "${path_zshcompletiondir}" != "no") AC_ARG_ENABLE(backtrace, AS_HELP_STRING([--enable-backtrace], [compile backtrace support]), [enable_backtrace=${enableval}]) if (test "${enable_backtrace}" = "yes"); then AC_CHECK_HEADER(elfutils/libdwfl.h, dummy=yes, AC_MSG_ERROR(elfutils support is required)) AC_DEFINE(HAVE_BACKTRACE_SUPPORT, 1, [Define to 1 if you have the backtrace support.]) BACKTRACE_CFLAGS="" BACKTRACE_LIBS="-ldw" AC_SUBST(BACKTRACE_CFLAGS) AC_SUBST(BACKTRACE_LIBS) fi AC_ARG_ENABLE(library, AS_HELP_STRING([--enable-library], [install Bluetooth library]), [enable_library=${enableval}]) AM_CONDITIONAL(LIBRARY, test "${enable_library}" = "yes") AC_ARG_ENABLE(test, AS_HELP_STRING([--enable-test], [enable test/example scripts]), [enable_test=${enableval}]) AM_CONDITIONAL(TEST, test "${enable_test}" = "yes") AC_ARG_ENABLE(nfc, AS_HELP_STRING([--enable-nfc], [enable NFC paring]), [enable_nfc=${enableval}]) AM_CONDITIONAL(NFC, test "${enable_nfc}" = "yes") AC_ARG_ENABLE(sap, AS_HELP_STRING([--enable-sap], [enable SAP profile]), [enable_sap=${enableval}]) AM_CONDITIONAL(SAP, test "${enable_sap}" = "yes") AC_ARG_ENABLE(a2dp, AS_HELP_STRING([--disable-a2dp], [disable A2DP profile]), [enable_a2dp=${enableval}]) AM_CONDITIONAL(A2DP, test "${enable_a2dp}" != "no") if test "${enable_a2dp}" != "no"; then AC_DEFINE(HAVE_A2DP, 1, [Define to 1 if you have A2DP support.]) fi AC_ARG_ENABLE(avrcp, AS_HELP_STRING([--disable-avrcp], [disable AVRCP profile]), [enable_avrcp=${enableval}]) AM_CONDITIONAL(AVRCP, test "${enable_avrcp}" != "no") if test "${enable_avrcp}" != "no"; then AC_DEFINE(HAVE_AVRCP, 1, [Define to 1 if you have AVRCP support.]) fi AC_ARG_ENABLE(network, AS_HELP_STRING([--disable-network], [disable network profiles]), [enable_network=${enableval}]) AM_CONDITIONAL(NETWORK, test "${enable_network}" != "no") AC_ARG_ENABLE(hid, AS_HELP_STRING([--disable-hid], [disable HID profile]), [enable_hid=${enableval}]) AM_CONDITIONAL(HID, test "${enable_hid}" != "no") AC_ARG_ENABLE(hog, AS_HELP_STRING([--disable-hog], [disable HoG profile]), [enable_hog=${enableval}]) AM_CONDITIONAL(HOG, test "${enable_hog}" != "no") AC_ARG_ENABLE(health, AS_HELP_STRING([--enable-health], [enable health profiles]), [enable_health=${enableval}]) AM_CONDITIONAL(HEALTH, test "${enable_health}" = "yes") AC_ARG_ENABLE(bap, AS_HELP_STRING([--disable-bap], [disable BAP profile]), [enable_bap=${enableval}]) AM_CONDITIONAL(BAP, test "${enable_bap}" != "no") AC_ARG_ENABLE(bass, AS_HELP_STRING([--disable-bass], [disable BASS service]), [enable_bass=${enableval}]) AM_CONDITIONAL(BASS, test "${enable_bass}" != "no") AC_ARG_ENABLE(mcp, AS_HELP_STRING([--disable-mcp], [disable MCP profile]), [enable_mcp=${enableval}]) AM_CONDITIONAL(MCP, test "${enable_mcp}" != "no") AC_ARG_ENABLE(ccp, AS_HELP_STRING([--disable-ccp], [disable CCP profile]), [enable_ccp=${enableval}]) AM_CONDITIONAL(CCP, test "${enable_ccp}" != "no") AC_ARG_ENABLE(vcp, AS_HELP_STRING([--disable-vcp], [disable VCP profile]), [enable_vcp=${enableval}]) AM_CONDITIONAL(VCP, test "${enable_vcp}" != "no") AC_ARG_ENABLE(micp, AS_HELP_STRING([--disable-micp], [disable MICP profile]), [enable_micp=${enableval}]) AM_CONDITIONAL(MICP, test "${enable_micp}" != "no") AC_ARG_ENABLE(csip, AS_HELP_STRING([--disable-csip], [disable CSIP profile]), [enable_csip=${enableval}]) AM_CONDITIONAL(CSIP, test "${enable_csip}" != "no") AC_ARG_ENABLE(asha, AS_HELP_STRING([--disable-asha], [disable ASHA support]), [enable_asha=${enableval}]) AM_CONDITIONAL(ASHA, test "${enable_asha}" != "no") if test "${enable_asha}" != "no"; then AC_DEFINE(HAVE_ASHA, 1, [Define to 1 if you have ASHA support.]) fi AC_ARG_ENABLE(tools, AS_HELP_STRING([--disable-tools], [disable Bluetooth tools]), [enable_tools=${enableval}]) AM_CONDITIONAL(TOOLS, test "${enable_tools}" != "no") AC_ARG_ENABLE(monitor, AS_HELP_STRING([--disable-monitor], [disable Bluetooth monitor]), [enable_monitor=${enableval}]) AM_CONDITIONAL(MONITOR, test "${enable_monitor}" != "no") AC_ARG_ENABLE(udev, AS_HELP_STRING([--disable-udev], [disable udev device support]), [enable_udev=${enableval}]) if (test "${enable_udev}" != "no"); then PKG_CHECK_MODULES(UDEV, libudev >= 196) AC_DEFINE(HAVE_UDEV, 1, [Define to 1 if udev is required]) fi AC_ARG_WITH([udevdir], AS_HELP_STRING([--with-udevdir=DIR], [path to udev directory]), [path_udevdir=${withval}]) if (test "${enable_udev}" != "no" && test -z "${path_udevdir}"); then AC_MSG_CHECKING([udev directory]) path_udevdir="`$PKG_CONFIG --variable=udevdir udev`" if (test -z "${path_udevdir}"); then AC_MSG_ERROR([udev directory is required]) fi AC_MSG_RESULT([${path_udevdir}]) fi AC_SUBST(UDEV_DIR, [${path_udevdir}]) AC_ARG_ENABLE(cups, AS_HELP_STRING([--disable-cups], [disable CUPS printer support]), [enable_cups=${enableval}]) AM_CONDITIONAL(CUPS, test "${enable_cups}" != "no") if (test "${enable_cups}" != "no"); then AC_MSG_CHECKING([cups directory]) cups_serverbin=`$PKG_CONFIG cups --variable=cups_serverbin` AC_MSG_RESULT([${cups_serverbin}]) fi AM_CONDITIONAL(CUPS_SERVERBIN, test "${cups_serverbin}" != "") AS_IF([test "${cups_serverbin}" != ""],[ AC_SUBST(CUPS_SERVERBIN, ${cups_serverbin}) ]) AC_ARG_ENABLE(mesh, AS_HELP_STRING([--enable-mesh], [enable Mesh profile support]), [enable_mesh=${enableval}]) AM_CONDITIONAL(MESH, test "${enable_mesh}" = "yes") if (test "${enable_mesh}" = "yes"); then PKG_CHECK_MODULES(JSONC, json-c >= 0.13) fi AC_ARG_ENABLE(midi, AS_HELP_STRING([--enable-midi], [enable MIDI support]), [enable_midi=${enableval}]) AM_CONDITIONAL(MIDI, test "${enable_midi}" = "yes") if (test "${enable_midi}" = "yes"); then PKG_CHECK_MODULES(ALSA, alsa) fi AC_ARG_ENABLE(obex, AS_HELP_STRING([--disable-obex], [disable OBEX profile support]), [enable_obex=${enableval}]) if (test "${enable_obex}" != "no"); then PKG_CHECK_MODULES(ICAL, libical) fi AM_CONDITIONAL(OBEX, test "${enable_obex}" != "no") AC_ARG_ENABLE(btpclient, AS_HELP_STRING([--enable-btpclient], [enable BTP client]), [enable_btpclient=${enableval}]) AM_CONDITIONAL(BTPCLIENT, test "${enable_btpclient}" = "yes") AC_ARG_ENABLE([external_ell], AS_HELP_STRING([--enable-external-ell], [enable external Embedded Linux library]), [enable_external_ell=${enableval}]) if (test "${enable_external_ell}" = "yes"); then PKG_CHECK_MODULES(ELL, ell >= 0.39) fi if (test "${enable_external_ell}" != "yes" && (test "${enable_btpclient}" = "yes" || test "${enable_mesh}" = "yes")); then if (test ! -f ${srcdir}/ell/ell.h) && (test ! -f ${srcdir}/../ell/ell/ell.h); then AC_MSG_ERROR(ELL source is required or use --enable-external-ell) fi fi AM_CONDITIONAL(EXTERNAL_ELL, test "${enable_external_ell}" = "yes" || (test "${enable_btpclient}" != "yes" && test "${enable_mesh}" != "yes")) AM_CONDITIONAL(LIBSHARED_ELL, test "${enable_btpclient}" = "yes" || test "${enable_mesh}" = "yes") AC_ARG_ENABLE(client, AS_HELP_STRING([--disable-client], [disable command line client]), [enable_client=${enableval}]) AM_CONDITIONAL(CLIENT, test "${enable_client}" != "no") if (test "${enable_client}" != "no" || test "${enable_mesh}" = "yes"); then AC_CHECK_HEADERS(readline/readline.h, enable_readline=yes, AC_MSG_ERROR(readline header files are required)) fi AM_CONDITIONAL(READLINE, test "${enable_readline}" = "yes") AC_ARG_ENABLE(systemd, AS_HELP_STRING([--disable-systemd], [disable systemd integration]), [enable_systemd=${enableval}]) AM_CONDITIONAL(SYSTEMD, test "${enable_systemd}" != "no") AC_ARG_WITH([systemdsystemunitdir], AS_HELP_STRING([--with-systemdsystemunitdir=DIR], [path to systemd system unit directory]), [path_systemunitdir=${withval}]) if (test "${enable_systemd}" != "no" && test -z "${path_systemunitdir}"); then AC_MSG_CHECKING([systemd system unit dir]) path_systemunitdir="`$PKG_CONFIG --variable=systemdsystemunitdir systemd`" if (test -z "${path_systemunitdir}"); then AC_MSG_ERROR([systemd system unit directory is required]) fi AC_MSG_RESULT([${path_systemunitdir}]) fi AC_SUBST(SYSTEMD_SYSTEMUNITDIR, [${path_systemunitdir}]) AC_ARG_WITH([systemduserunitdir], AS_HELP_STRING([--with-systemduserunitdir=DIR], [path to systemd user unit directory]), [path_userunitdir=${withval}]) if (test "${enable_systemd}" != "no" && test -z "${path_userunitdir}"); then AC_MSG_CHECKING([systemd user unit dir]) path_userunitdir="`$PKG_CONFIG --variable=systemduserunitdir systemd`" if (test -z "${path_userunitdir}"); then AC_MSG_ERROR([systemd user unit directory is required]) fi AC_MSG_RESULT([${path_userunitdir}]) fi AC_SUBST(SYSTEMD_USERUNITDIR, [${path_userunitdir}]) AC_ARG_ENABLE(datafiles, AS_HELP_STRING([--disable-datafiles], [do not install configuration and data files]), [enable_datafiles=${enableval}]) AM_CONDITIONAL(DATAFILES, test "${enable_datafiles}" != "no") AC_ARG_ENABLE(manpages, AS_HELP_STRING([--disable-manpages], [disable building of manual pages]), [enable_manpages=${enableval}]) if (test "${enable_manpages}" != "no"); then AC_CHECK_PROGS(RST2MAN, [rst2man rst2man.py], "no") if (test "${RST2MAN}" = "no" ); then AC_MSG_ERROR([rst2man is required]) fi fi AM_CONDITIONAL(MANPAGES, test "${enable_manpages}" != "no") AM_CONDITIONAL(RUN_RST2MAN, test "${enable_manpages}" != "no" && test "${RST2MAN}" != "no") AC_ARG_ENABLE(testing, AS_HELP_STRING([--enable-testing], [enable testing tools]), [enable_testing=${enableval}]) AM_CONDITIONAL(TESTING, test "${enable_testing}" = "yes") if (test "${enable_testing}" = "yes"); then AC_CHECK_DECLS([SOF_TIMESTAMPING_TX_COMPLETION, SCM_TSTAMP_COMPLETION], [], [], [[#include #include #include ]]) fi AC_ARG_ENABLE(experimental, AS_HELP_STRING([--enable-experimental], [enable experimental tools]), [enable_experimental=${enableval}]) AM_CONDITIONAL(EXPERIMENTAL, test "${enable_experimental}" = "yes") AC_ARG_ENABLE(deprecated, AS_HELP_STRING([--enable-deprecated], [enable deprecated tools]), [enable_deprecated=${enableval}]) AM_CONDITIONAL(DEPRECATED, test "${enable_deprecated}" = "yes") AC_ARG_ENABLE(external-plugins, AS_HELP_STRING([--enable-external-plugins], [enable support for external plugins]), [enable_external_plugins=${enableval}]) AM_CONDITIONAL(EXTERNAL_PLUGINS, test "${enable_external_plugins}" = "yes") if (test "${enable_external_plugins}" = "yes"); then AC_DEFINE(EXTERNAL_PLUGINS, 1, [Define if external plugin support is required]) else AC_DEFINE(EXTERNAL_PLUGINS, 0, [Define if external plugin support is required]) fi AC_ARG_ENABLE(sixaxis, AS_HELP_STRING([--enable-sixaxis], [enable sixaxis plugin]), [enable_sixaxis=${enableval}]) AM_CONDITIONAL(SIXAXIS, test "${enable_sixaxis}" = "yes" && test "${enable_udev}" != "no") AC_ARG_ENABLE(hid2hci, AS_HELP_STRING([--enable-hid2hci], [enable hid2hci tool]), [enable_hid2hci=${enableval}]) AM_CONDITIONAL(HID2HCI, test "${enable_hid2hci}" = "yes" && test "${enable_udev}" != "no") AC_ARG_ENABLE(logger, AS_HELP_STRING([--enable-logger], [enable HCI logger service]), [enable_logger=${enableval}]) AM_CONDITIONAL(LOGGER, test "${enable_logger}" = "yes") AC_ARG_ENABLE(admin, AS_HELP_STRING([--enable-admin], [enable admin policy plugin]), [enable_admin=${enableval}]) AM_CONDITIONAL(ADMIN, test "${enable_admin}" = "yes") if (test "${prefix}" = "NONE"); then dnl no prefix and no localstatedir, so default to /var if (test "$localstatedir" = '${prefix}/var'); then AC_SUBST([localstatedir], ['/var']) fi prefix="${ac_default_prefix}" fi if (test "${exec_prefix}" = "NONE"); then # exec_prefix defaults to prefix, although our manual handling of the # latter (above) confuses autoconf. Manually set the exec_prefix. exec_prefix="${prefix}" fi # Expand any variables containing relative references like ${prefix} and co. # # Otherwise we'll end up with literal references in the final binaries or # manuals, which is not something we really want. # pkgbindir="${bindir}" if (test "$bindir" = '${exec_prefix}/bin'); then pkgbindir="${exec_prefix}/bin" else pkgbindir="${bindir}" fi AC_SUBST(PKGBINDIR, "${pkgbindir}") if (test "$libexecdir" = '${exec_prefix}/libexec'); then pkglibexecdir="${exec_prefix}/libexec/bluetooth" else pkglibexecdir="${libexecdir}/bluetooth" fi AC_SUBST(PKGLIBEXECDIR, "${pkglibexecdir}") if (test "$localstatedir" = '${prefix}/var'); then storagedir="${prefix}/var/lib/bluetooth" else storagedir="${localstatedir}/lib/bluetooth" fi AC_DEFINE_UNQUOTED(STORAGEDIR, "${storagedir}", [Directory for the storage files]) if (test "$sysconfdir" = '${prefix}/etc'); then configdir="${prefix}/etc/bluetooth" else configdir="${sysconfdir}/bluetooth" fi AC_DEFINE_UNQUOTED(CONFIGDIR, "${configdir}", [Directory for the configuration files]) AC_SUBST(CONFIGDIR, "${configdir}") AC_DEFINE_UNQUOTED(MESH_STORAGEDIR, "${storagedir}/mesh", [Directory for the mesh daemon storage files]) AC_SUBST(MESH_STORAGEDIR, "${storagedir}/mesh") AC_ARG_ENABLE(android, AS_HELP_STRING([--enable-android], [enable BlueZ for Android]), [enable_android=${enableval}]) AM_CONDITIONAL(ANDROID, test "${enable_android}" = "yes") if (test "${enable_android}" = "yes"); then PKG_CHECK_MODULES(SBC, sbc >= 1.2) fi if (test "${enable_android}" = "yes"); then PKG_CHECK_MODULES(SPEEXDSP, speexdsp >= 1.2) fi AC_DEFINE_UNQUOTED(ANDROID_STORAGEDIR, "${storagedir}/android", [Directory for the Android daemon storage files]) AC_ARG_WITH([phonebook], AS_HELP_STRING([--with-phonebook=PLUGIN], [obexd phonebook plugin (default=dummy)]), [plugin_phonebook=${withval}]) if (test -z "${plugin_phonebook}"); then plugin_phonebook=dummy fi if (test "${plugin_phonebook}" = "ebook"); then PKG_CHECK_MODULES(LIBEBOOK, libebook-1.2 >= 3.3) PKG_CHECK_MODULES(LIBEDATESERVER, libedataserver-1.2 >= 3.3) fi AC_SUBST(PLUGIN_PHONEBOOK, [${plugin_phonebook}]) AC_CONFIG_FILES( lib/bluez.pc Makefile mesh/bluetooth-meshd.rst mesh/bluetooth-mesh.service obexd/src/obex.service obexd/src/org.bluez.obex.service src/bluetoothd.rst src/bluetooth.service tools/bluetooth-logger.service tools/mpris-proxy.service ) AC_OUTPUT bluez-5.82/PaxHeaders/acinclude.m40000644000000000000000000000005014766002272014045 xustar0020 atime=1743515577 20 ctime=1743591275 bluez-5.82/acinclude.m40000644000000000000000000001166514766002272013537 0ustar00rootrootAC_DEFUN([AC_PROG_CC_PIE], [ AC_CACHE_CHECK([whether ${CC-cc} accepts -fPIE], ac_cv_prog_cc_pie, [ echo 'void f(){}' > conftest.c if test -z "`${CC-cc} -fPIE -pie -c conftest.c 2>&1`"; then ac_cv_prog_cc_pie=yes else ac_cv_prog_cc_pie=no fi rm -rf conftest* ]) ]) AC_DEFUN([AC_PROG_CC_ASAN], [ AC_CACHE_CHECK([whether ${CC-cc} accepts -fsanitize=address], ac_cv_prog_cc_asan, [ echo 'void f(){}' > asan.c if test -z "`${CC-cc} -fsanitize=address -c asan.c 2>&1`"; then ac_cv_prog_cc_asan=yes else ac_cv_prog_cc_asan=no fi rm -rf asan* ]) ]) AC_DEFUN([AC_PROG_CC_LSAN], [ AC_CACHE_CHECK([whether ${CC-cc} accepts -fsanitize=leak], ac_cv_prog_cc_lsan, [ echo 'void f(){}' > lsan.c if test -z "`${CC-cc} -fsanitize=leak -c lsan.c 2>&1`"; then ac_cv_prog_cc_lsan=yes else ac_cv_prog_cc_lsan=no fi rm -rf lsan* ]) ]) AC_DEFUN([AC_PROG_CC_UBSAN], [ AC_CACHE_CHECK([whether ${CC-cc} accepts -fsanitize=undefined], ac_cv_prog_cc_ubsan, [ echo 'void f(){}' > ubsan.c if test -z "`${CC-cc} -fsanitize=undefined -c ubsan.c 2>&1`"; then ac_cv_prog_cc_ubsan=yes else ac_cv_prog_cc_ubsan=no fi rm -rf ubsan* ]) ]) AC_DEFUN([COMPILER_FLAGS], [ with_cflags="" if (test "$USE_MAINTAINER_MODE" = "yes"); then with_cflags="$with_cflags -Wall -Werror -Wextra" with_cflags="$with_cflags -Wno-unused-parameter" with_cflags="$with_cflags -Wno-missing-field-initializers" with_cflags="$with_cflags -Wdeclaration-after-statement" with_cflags="$with_cflags -Wmissing-declarations" with_cflags="$with_cflags -Wredundant-decls" with_cflags="$with_cflags -Wcast-align" with_cflags="$with_cflags -Wswitch-enum" with_cflags="$with_cflags -Wformat -Wformat-security" with_cflags="$with_cflags -Wstringop-overflow" with_cflags="$with_cflags -DG_DISABLE_DEPRECATED" with_cflags="$with_cflags -DGLIB_VERSION_MIN_REQUIRED=GLIB_VERSION_2_28" with_cflags="$with_cflags -DGLIB_VERSION_MAX_ALLOWED=GLIB_VERSION_2_32" fi AC_SUBST([WARNING_CFLAGS], $with_cflags) ]) AC_DEFUN([MISC_FLAGS], [ misc_cflags="" misc_ldflags="" AC_ARG_ENABLE(optimization, AS_HELP_STRING([--disable-optimization], [disable code optimization through compiler]), [ if (test "${enableval}" = "no"); then misc_cflags="$misc_cflags -O0" fi ]) AC_ARG_ENABLE(asan, AS_HELP_STRING([--enable-asan], [enable linking with address sanitizer]), [ save_LIBS=$LIBS AC_CHECK_LIB(asan, _init) LIBS=$save_LIBS if (test "${enableval}" = "yes" && test "${ac_cv_lib_asan__init}" = "yes" && test "${ac_cv_prog_cc_asan}" = "yes"); then misc_cflags="$misc_cflags -fsanitize=address"; misc_ldflags="$misc_ldflags -fsanitize=address" AC_SUBST([ASAN_LIB], ${ac_cv_lib_asan__init}) fi ]) AC_ARG_ENABLE(lsan, AS_HELP_STRING([--enable-lsan], [enable linking with address sanitizer]), [ save_LIBS=$LIBS AC_CHECK_LIB(lsan, _init) LIBS=$save_LIBS if (test "${enableval}" = "yes" && test "${ac_cv_lib_lsan__init}" = "yes" && test "${ac_cv_prog_cc_lsan}" = "yes"); then misc_cflags="$misc_cflags -fsanitize=leak"; misc_ldflags="$misc_ldflags -fsanitize=leak" AC_SUBST([ASAN_LIB], ${ac_cv_lib_lsan__init}) fi ]) AC_ARG_ENABLE(ubsan, AS_HELP_STRING([--enable-ubsan], [enable linking with address sanitizer]), [ save_LIBS=$LIBS AC_CHECK_LIB(ubsan, _init) LIBS=$save_LIBS if (test "${enableval}" = "yes" && test "${ac_cv_lib_ubsan__init}" = "yes" && test "${ac_cv_prog_cc_ubsan}" = "yes"); then misc_cflags="$misc_cflags -fsanitize=undefined"; misc_ldflags="$misc_ldflags -fsanitize=undefined"; fi ]) AC_ARG_ENABLE(debug, AS_HELP_STRING([--enable-debug], [enable compiling with debugging information]), [ if (test "${enableval}" = "yes" && test "${ac_cv_prog_cc_g}" = "yes"); then misc_cflags="$misc_cflags -g" fi ]) AC_ARG_ENABLE(pie, AS_HELP_STRING([--enable-pie], [enable position independent executables flag]), [ if (test "${enableval}" = "yes" && test "${ac_cv_prog_cc_pie}" = "yes"); then misc_cflags="$misc_cflags -fPIC" misc_ldflags="$misc_ldflags -pie -Wl,-z,now" fi ]) if (test "$enable_coverage" = "yes"); then misc_cflags="$misc_cflags --coverage" misc_ldflags="$misc_ldflags --coverage" fi if (test "$USE_MAINTAINER_MODE" = "yes"); then AC_CACHE_CHECK([whether ${CC-cc} accepts -D_FORTIFY_SOURCE=3], ac_cv_prog_cc_fortify_source_3, [ echo '#include ' > fortify.c if test -z "`${CC-cc} ${CFLAGS} ${misc_cflags} -U_FORTIFY_SOURCE -D_FORTIFY_SOURCE=3 -c fortify.c 2>&1`"; then ac_cv_prog_cc_fortify_source_3=yes else ac_cv_prog_cc_fortify_source_3=no fi rm -f fortify.c fortify.o ]) if test "${ac_cv_prog_cc_fortify_source_3}" = "yes"; then misc_cflags="$misc_cflags -U_FORTIFY_SOURCE -D_FORTIFY_SOURCE=3" fi fi misc_cflags="$misc_cflags -ffunction-sections -fdata-sections" misc_ldflags="$misc_ldflags -Wl,--gc-sections" AC_SUBST([MISC_CFLAGS], $misc_cflags) AC_SUBST([MISC_LDFLAGS], $misc_ldflags) ]) bluez-5.82/PaxHeaders/NEWS0000644000000000000000000000005011071665350012351 xustar0020 atime=1743516862 20 ctime=1743591275 bluez-5.82/NEWS0000644000000000000000000000000011071665350012020 0ustar00rootrootbluez-5.82/PaxHeaders/configure0000644000000000000000000000005014773211402013553 xustar0020 atime=1743590147 20 ctime=1743591275 bluez-5.82/configure0000755000000000000000000220102514773211402013241 0ustar00rootroot#! /bin/sh # Guess values for system-dependent variables and create Makefiles. # Generated by GNU Autoconf 2.72 for bluez 5.82. # # # Copyright (C) 1992-1996, 1998-2017, 2020-2023 Free Software Foundation, # Inc. # # # This configure script is free software; the Free Software Foundation # gives unlimited permission to copy, distribute and modify it. ## -------------------- ## ## M4sh Initialization. ## ## -------------------- ## # Be more Bourne compatible DUALCASE=1; export DUALCASE # for MKS sh if test ${ZSH_VERSION+y} && (emulate sh) >/dev/null 2>&1 then : emulate sh NULLCMD=: # Pre-4.2 versions of Zsh do word splitting on ${1+"$@"}, which # is contrary to our usage. Disable this feature. alias -g '${1+"$@"}'='"$@"' setopt NO_GLOB_SUBST else case e in #( e) case `(set -o) 2>/dev/null` in #( *posix*) : set -o posix ;; #( *) : ;; esac ;; esac fi # Reset variables that may have inherited troublesome values from # the environment. # IFS needs to be set, to space, tab, and newline, in precisely that order. # (If _AS_PATH_WALK were called with IFS unset, it would have the # side effect of setting IFS to empty, thus disabling word splitting.) # Quoting is to prevent editors from complaining about space-tab. as_nl=' ' export as_nl IFS=" "" $as_nl" PS1='$ ' PS2='> ' PS4='+ ' # Ensure predictable behavior from utilities with locale-dependent output. LC_ALL=C export LC_ALL LANGUAGE=C export LANGUAGE # We cannot yet rely on "unset" to work, but we need these variables # to be unset--not just set to an empty or harmless value--now, to # avoid bugs in old shells (e.g. pre-3.0 UWIN ksh). This construct # also avoids known problems related to "unset" and subshell syntax # in other old shells (e.g. bash 2.01 and pdksh 5.2.14). for as_var in BASH_ENV ENV MAIL MAILPATH CDPATH do eval test \${$as_var+y} \ && ( (unset $as_var) || exit 1) >/dev/null 2>&1 && unset $as_var || : done # Ensure that fds 0, 1, and 2 are open. if (exec 3>&0) 2>/dev/null; then :; else exec 0&1) 2>/dev/null; then :; else exec 1>/dev/null; fi if (exec 3>&2) ; then :; else exec 2>/dev/null; fi # The user is always right. if ${PATH_SEPARATOR+false} :; then PATH_SEPARATOR=: (PATH='/bin;/bin'; FPATH=$PATH; sh -c :) >/dev/null 2>&1 && { (PATH='/bin:/bin'; FPATH=$PATH; sh -c :) >/dev/null 2>&1 || PATH_SEPARATOR=';' } fi # Find who we are. Look in the path if we contain no directory separator. as_myself= case $0 in #(( *[\\/]* ) as_myself=$0 ;; *) as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS case $as_dir in #((( '') as_dir=./ ;; */) ;; *) as_dir=$as_dir/ ;; esac test -r "$as_dir$0" && as_myself=$as_dir$0 && break done IFS=$as_save_IFS ;; esac # We did not find ourselves, most probably we were run as 'sh COMMAND' # in which case we are not to be found in the path. if test "x$as_myself" = x; then as_myself=$0 fi if test ! -f "$as_myself"; then printf "%s\n" "$as_myself: error: cannot find myself; rerun with an absolute file name" >&2 exit 1 fi # Use a proper internal environment variable to ensure we don't fall # into an infinite loop, continuously re-executing ourselves. if test x"${_as_can_reexec}" != xno && test "x$CONFIG_SHELL" != x; then _as_can_reexec=no; export _as_can_reexec; # We cannot yet assume a decent shell, so we have to provide a # neutralization value for shells without unset; and this also # works around shells that cannot unset nonexistent variables. # Preserve -v and -x to the replacement shell. BASH_ENV=/dev/null ENV=/dev/null (unset BASH_ENV) >/dev/null 2>&1 && unset BASH_ENV ENV case $- in # (((( *v*x* | *x*v* ) as_opts=-vx ;; *v* ) as_opts=-v ;; *x* ) as_opts=-x ;; * ) as_opts= ;; esac exec $CONFIG_SHELL $as_opts "$as_myself" ${1+"$@"} # Admittedly, this is quite paranoid, since all the known shells bail # out after a failed 'exec'. printf "%s\n" "$0: could not re-execute with $CONFIG_SHELL" >&2 exit 255 fi # We don't want this to propagate to other subprocesses. { _as_can_reexec=; unset _as_can_reexec;} if test "x$CONFIG_SHELL" = x; then as_bourne_compatible="if test \${ZSH_VERSION+y} && (emulate sh) >/dev/null 2>&1 then : emulate sh NULLCMD=: # Pre-4.2 versions of Zsh do word splitting on \${1+\"\$@\"}, which # is contrary to our usage. Disable this feature. alias -g '\${1+\"\$@\"}'='\"\$@\"' setopt NO_GLOB_SUBST else case e in #( e) case \`(set -o) 2>/dev/null\` in #( *posix*) : set -o posix ;; #( *) : ;; esac ;; esac fi " as_required="as_fn_return () { (exit \$1); } as_fn_success () { as_fn_return 0; } as_fn_failure () { as_fn_return 1; } as_fn_ret_success () { return 0; } as_fn_ret_failure () { return 1; } exitcode=0 as_fn_success || { exitcode=1; echo as_fn_success failed.; } as_fn_failure && { exitcode=1; echo as_fn_failure succeeded.; } as_fn_ret_success || { exitcode=1; echo as_fn_ret_success failed.; } as_fn_ret_failure && { exitcode=1; echo as_fn_ret_failure succeeded.; } if ( set x; as_fn_ret_success y && test x = \"\$1\" ) then : else case e in #( e) exitcode=1; echo positional parameters were not saved. ;; esac fi test x\$exitcode = x0 || exit 1 blah=\$(echo \$(echo blah)) test x\"\$blah\" = xblah || exit 1 test -x / || exit 1" as_suggested=" as_lineno_1=";as_suggested=$as_suggested$LINENO;as_suggested=$as_suggested" as_lineno_1a=\$LINENO as_lineno_2=";as_suggested=$as_suggested$LINENO;as_suggested=$as_suggested" as_lineno_2a=\$LINENO eval 'test \"x\$as_lineno_1'\$as_run'\" != \"x\$as_lineno_2'\$as_run'\" && test \"x\`expr \$as_lineno_1'\$as_run' + 1\`\" = \"x\$as_lineno_2'\$as_run'\"' || exit 1 test -n \"\${ZSH_VERSION+set}\${BASH_VERSION+set}\" || ( ECHO='\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\' ECHO=\$ECHO\$ECHO\$ECHO\$ECHO\$ECHO ECHO=\$ECHO\$ECHO\$ECHO\$ECHO\$ECHO\$ECHO PATH=/empty FPATH=/empty; export PATH FPATH test \"X\`printf %s \$ECHO\`\" = \"X\$ECHO\" \\ || test \"X\`print -r -- \$ECHO\`\" = \"X\$ECHO\" ) || exit 1 test \$(( 1 + 1 )) = 2 || exit 1" if (eval "$as_required") 2>/dev/null then : as_have_required=yes else case e in #( e) as_have_required=no ;; esac fi if test x$as_have_required = xyes && (eval "$as_suggested") 2>/dev/null then : else case e in #( e) as_save_IFS=$IFS; IFS=$PATH_SEPARATOR as_found=false for as_dir in /bin$PATH_SEPARATOR/usr/bin$PATH_SEPARATOR$PATH do IFS=$as_save_IFS case $as_dir in #((( '') as_dir=./ ;; */) ;; *) as_dir=$as_dir/ ;; esac as_found=: case $as_dir in #( /*) for as_base in sh bash ksh sh5; do # Try only shells that exist, to save several forks. as_shell=$as_dir$as_base if { test -f "$as_shell" || test -f "$as_shell.exe"; } && as_run=a "$as_shell" -c "$as_bourne_compatible""$as_required" 2>/dev/null then : CONFIG_SHELL=$as_shell as_have_required=yes if as_run=a "$as_shell" -c "$as_bourne_compatible""$as_suggested" 2>/dev/null then : break 2 fi fi done;; esac as_found=false done IFS=$as_save_IFS if $as_found then : else case e in #( e) if { test -f "$SHELL" || test -f "$SHELL.exe"; } && as_run=a "$SHELL" -c "$as_bourne_compatible""$as_required" 2>/dev/null then : CONFIG_SHELL=$SHELL as_have_required=yes fi ;; esac fi if test "x$CONFIG_SHELL" != x then : export CONFIG_SHELL # We cannot yet assume a decent shell, so we have to provide a # neutralization value for shells without unset; and this also # works around shells that cannot unset nonexistent variables. # Preserve -v and -x to the replacement shell. BASH_ENV=/dev/null ENV=/dev/null (unset BASH_ENV) >/dev/null 2>&1 && unset BASH_ENV ENV case $- in # (((( *v*x* | *x*v* ) as_opts=-vx ;; *v* ) as_opts=-v ;; *x* ) as_opts=-x ;; * ) as_opts= ;; esac exec $CONFIG_SHELL $as_opts "$as_myself" ${1+"$@"} # Admittedly, this is quite paranoid, since all the known shells bail # out after a failed 'exec'. printf "%s\n" "$0: could not re-execute with $CONFIG_SHELL" >&2 exit 255 fi if test x$as_have_required = xno then : printf "%s\n" "$0: This script requires a shell more modern than all" printf "%s\n" "$0: the shells that I found on your system." if test ${ZSH_VERSION+y} ; then printf "%s\n" "$0: In particular, zsh $ZSH_VERSION has bugs and should" printf "%s\n" "$0: be upgraded to zsh 4.3.4 or later." else printf "%s\n" "$0: Please tell bug-autoconf@gnu.org about your system, $0: including any error possibly output before this $0: message. Then install a modern shell, or manually run $0: the script under such a shell if you do have one." fi exit 1 fi ;; esac fi fi SHELL=${CONFIG_SHELL-/bin/sh} export SHELL # Unset more variables known to interfere with behavior of common tools. CLICOLOR_FORCE= GREP_OPTIONS= unset CLICOLOR_FORCE GREP_OPTIONS ## --------------------- ## ## M4sh Shell Functions. ## ## --------------------- ## # as_fn_unset VAR # --------------- # Portably unset VAR. as_fn_unset () { { eval $1=; unset $1;} } as_unset=as_fn_unset # as_fn_set_status STATUS # ----------------------- # Set $? to STATUS, without forking. as_fn_set_status () { return $1 } # as_fn_set_status # as_fn_exit STATUS # ----------------- # Exit the shell with STATUS, even in a "trap 0" or "set -e" context. as_fn_exit () { set +e as_fn_set_status $1 exit $1 } # as_fn_exit # as_fn_mkdir_p # ------------- # Create "$as_dir" as a directory, including parents if necessary. as_fn_mkdir_p () { case $as_dir in #( -*) as_dir=./$as_dir;; esac test -d "$as_dir" || eval $as_mkdir_p || { as_dirs= while :; do case $as_dir in #( *\'*) as_qdir=`printf "%s\n" "$as_dir" | sed "s/'/'\\\\\\\\''/g"`;; #'( *) as_qdir=$as_dir;; esac as_dirs="'$as_qdir' $as_dirs" as_dir=`$as_dirname -- "$as_dir" || $as_expr X"$as_dir" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ X"$as_dir" : 'X\(//\)[^/]' \| \ X"$as_dir" : 'X\(//\)$' \| \ X"$as_dir" : 'X\(/\)' \| . 2>/dev/null || printf "%s\n" X"$as_dir" | sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ s//\1/ q } /^X\(\/\/\)[^/].*/{ s//\1/ q } /^X\(\/\/\)$/{ s//\1/ q } /^X\(\/\).*/{ s//\1/ q } s/.*/./; q'` test -d "$as_dir" && break done test -z "$as_dirs" || eval "mkdir $as_dirs" } || test -d "$as_dir" || as_fn_error $? "cannot create directory $as_dir" } # as_fn_mkdir_p # as_fn_executable_p FILE # ----------------------- # Test if FILE is an executable regular file. as_fn_executable_p () { test -f "$1" && test -x "$1" } # as_fn_executable_p # as_fn_append VAR VALUE # ---------------------- # Append the text in VALUE to the end of the definition contained in VAR. Take # advantage of any shell optimizations that allow amortized linear growth over # repeated appends, instead of the typical quadratic growth present in naive # implementations. if (eval "as_var=1; as_var+=2; test x\$as_var = x12") 2>/dev/null then : eval 'as_fn_append () { eval $1+=\$2 }' else case e in #( e) as_fn_append () { eval $1=\$$1\$2 } ;; esac fi # as_fn_append # as_fn_arith ARG... # ------------------ # Perform arithmetic evaluation on the ARGs, and store the result in the # global $as_val. Take advantage of shells that can avoid forks. The arguments # must be portable across $(()) and expr. if (eval "test \$(( 1 + 1 )) = 2") 2>/dev/null then : eval 'as_fn_arith () { as_val=$(( $* )) }' else case e in #( e) as_fn_arith () { as_val=`expr "$@" || test $? -eq 1` } ;; esac fi # as_fn_arith # as_fn_error STATUS ERROR [LINENO LOG_FD] # ---------------------------------------- # Output "`basename $0`: error: ERROR" to stderr. If LINENO and LOG_FD are # provided, also output the error to LOG_FD, referencing LINENO. Then exit the # script with STATUS, using 1 if that was 0. as_fn_error () { as_status=$1; test $as_status -eq 0 && as_status=1 if test "$4"; then as_lineno=${as_lineno-"$3"} as_lineno_stack=as_lineno_stack=$as_lineno_stack printf "%s\n" "$as_me:${as_lineno-$LINENO}: error: $2" >&$4 fi printf "%s\n" "$as_me: error: $2" >&2 as_fn_exit $as_status } # as_fn_error if expr a : '\(a\)' >/dev/null 2>&1 && test "X`expr 00001 : '.*\(...\)'`" = X001; then as_expr=expr else as_expr=false fi if (basename -- /) >/dev/null 2>&1 && test "X`basename -- / 2>&1`" = "X/"; then as_basename=basename else as_basename=false fi if (as_dir=`dirname -- /` && test "X$as_dir" = X/) >/dev/null 2>&1; then as_dirname=dirname else as_dirname=false fi as_me=`$as_basename -- "$0" || $as_expr X/"$0" : '.*/\([^/][^/]*\)/*$' \| \ X"$0" : 'X\(//\)$' \| \ X"$0" : 'X\(/\)' \| . 2>/dev/null || printf "%s\n" X/"$0" | sed '/^.*\/\([^/][^/]*\)\/*$/{ s//\1/ q } /^X\/\(\/\/\)$/{ s//\1/ q } /^X\/\(\/\).*/{ s//\1/ q } s/.*/./; q'` # Avoid depending upon Character Ranges. as_cr_letters='abcdefghijklmnopqrstuvwxyz' as_cr_LETTERS='ABCDEFGHIJKLMNOPQRSTUVWXYZ' as_cr_Letters=$as_cr_letters$as_cr_LETTERS as_cr_digits='0123456789' as_cr_alnum=$as_cr_Letters$as_cr_digits as_lineno_1=$LINENO as_lineno_1a=$LINENO as_lineno_2=$LINENO as_lineno_2a=$LINENO eval 'test "x$as_lineno_1'$as_run'" != "x$as_lineno_2'$as_run'" && test "x`expr $as_lineno_1'$as_run' + 1`" = "x$as_lineno_2'$as_run'"' || { # Blame Lee E. McMahon (1931-1989) for sed's syntax. :-) sed -n ' p /[$]LINENO/= ' <$as_myself | sed ' t clear :clear s/[$]LINENO.*/&-/ t lineno b :lineno N :loop s/[$]LINENO\([^'$as_cr_alnum'_].*\n\)\(.*\)/\2\1\2/ t loop s/-\n.*// ' >$as_me.lineno && chmod +x "$as_me.lineno" || { printf "%s\n" "$as_me: error: cannot create $as_me.lineno; rerun with a POSIX shell" >&2; as_fn_exit 1; } # If we had to re-execute with $CONFIG_SHELL, we're ensured to have # already done that, so ensure we don't try to do so again and fall # in an infinite loop. This has already happened in practice. _as_can_reexec=no; export _as_can_reexec # Don't try to exec as it changes $[0], causing all sort of problems # (the dirname of $[0] is not the place where we might find the # original and so on. Autoconf is especially sensitive to this). . "./$as_me.lineno" # Exit status is that of the last command. exit } # Determine whether it's possible to make 'echo' print without a newline. # These variables are no longer used directly by Autoconf, but are AC_SUBSTed # for compatibility with existing Makefiles. ECHO_C= ECHO_N= ECHO_T= case `echo -n x` in #((((( -n*) case `echo 'xy\c'` in *c*) ECHO_T=' ';; # ECHO_T is single tab character. xy) ECHO_C='\c';; *) echo `echo ksh88 bug on AIX 6.1` > /dev/null ECHO_T=' ';; esac;; *) ECHO_N='-n';; esac # For backward compatibility with old third-party macros, we provide # the shell variables $as_echo and $as_echo_n. New code should use # AS_ECHO(["message"]) and AS_ECHO_N(["message"]), respectively. as_echo='printf %s\n' as_echo_n='printf %s' rm -f conf$$ conf$$.exe conf$$.file if test -d conf$$.dir; then rm -f conf$$.dir/conf$$.file else rm -f conf$$.dir mkdir conf$$.dir 2>/dev/null fi if (echo >conf$$.file) 2>/dev/null; then if ln -s conf$$.file conf$$ 2>/dev/null; then as_ln_s='ln -s' # ... but there are two gotchas: # 1) On MSYS, both 'ln -s file dir' and 'ln file dir' fail. # 2) DJGPP < 2.04 has no symlinks; 'ln -s' creates a wrapper executable. # In both cases, we have to default to 'cp -pR'. ln -s conf$$.file conf$$.dir 2>/dev/null && test ! -f conf$$.exe || as_ln_s='cp -pR' elif ln conf$$.file conf$$ 2>/dev/null; then as_ln_s=ln else as_ln_s='cp -pR' fi else as_ln_s='cp -pR' fi rm -f conf$$ conf$$.exe conf$$.dir/conf$$.file conf$$.file rmdir conf$$.dir 2>/dev/null if mkdir -p . 2>/dev/null; then as_mkdir_p='mkdir -p "$as_dir"' else test -d ./-p && rmdir ./-p as_mkdir_p=false fi as_test_x='test -x' as_executable_p=as_fn_executable_p # Sed expression to map a string onto a valid CPP name. as_sed_cpp="y%*$as_cr_letters%P$as_cr_LETTERS%;s%[^_$as_cr_alnum]%_%g" as_tr_cpp="eval sed '$as_sed_cpp'" # deprecated # Sed expression to map a string onto a valid variable name. as_sed_sh="y%*+%pp%;s%[^_$as_cr_alnum]%_%g" as_tr_sh="eval sed '$as_sed_sh'" # deprecated SHELL=${CONFIG_SHELL-/bin/sh} test -n "$DJDIR" || exec 7<&0 &1 # Name of the host. # hostname on some systems (SVR3.2, old GNU/Linux) returns a bogus exit status, # so uname gets run too. ac_hostname=`(hostname || uname -n) 2>/dev/null | sed 1q` # # Initializations. # ac_default_prefix=/usr/local ac_clean_files= ac_config_libobj_dir=. LIBOBJS= cross_compiling=no subdirs= MFLAGS= MAKEFLAGS= # Identity of this package. PACKAGE_NAME='bluez' PACKAGE_TARNAME='bluez' PACKAGE_VERSION='5.82' PACKAGE_STRING='bluez 5.82' PACKAGE_BUGREPORT='' PACKAGE_URL='' ac_default_prefix=/usr/local # Factoring default headers for most tests. ac_includes_default="\ #include #ifdef HAVE_STDIO_H # include #endif #ifdef HAVE_STDLIB_H # include #endif #ifdef HAVE_STRING_H # include #endif #ifdef HAVE_INTTYPES_H # include #endif #ifdef HAVE_STDINT_H # include #endif #ifdef HAVE_STRINGS_H # include #endif #ifdef HAVE_SYS_TYPES_H # include #endif #ifdef HAVE_SYS_STAT_H # include #endif #ifdef HAVE_UNISTD_H # include #endif" ac_header_c_list= ac_subst_vars='am__EXEEXT_FALSE am__EXEEXT_TRUE LTLIBOBJS LIBOBJS PLUGIN_PHONEBOOK LIBEDATESERVER_LIBS LIBEDATESERVER_CFLAGS LIBEBOOK_LIBS LIBEBOOK_CFLAGS SPEEXDSP_LIBS SPEEXDSP_CFLAGS SBC_LIBS SBC_CFLAGS ANDROID_FALSE ANDROID_TRUE MESH_STORAGEDIR CONFIGDIR PKGLIBEXECDIR PKGBINDIR ADMIN_FALSE ADMIN_TRUE LOGGER_FALSE LOGGER_TRUE HID2HCI_FALSE HID2HCI_TRUE SIXAXIS_FALSE SIXAXIS_TRUE EXTERNAL_PLUGINS_FALSE EXTERNAL_PLUGINS_TRUE DEPRECATED_FALSE DEPRECATED_TRUE EXPERIMENTAL_FALSE EXPERIMENTAL_TRUE TESTING_FALSE TESTING_TRUE RUN_RST2MAN_FALSE RUN_RST2MAN_TRUE MANPAGES_FALSE MANPAGES_TRUE RST2MAN DATAFILES_FALSE DATAFILES_TRUE SYSTEMD_USERUNITDIR SYSTEMD_SYSTEMUNITDIR SYSTEMD_FALSE SYSTEMD_TRUE READLINE_FALSE READLINE_TRUE CLIENT_FALSE CLIENT_TRUE LIBSHARED_ELL_FALSE LIBSHARED_ELL_TRUE EXTERNAL_ELL_FALSE EXTERNAL_ELL_TRUE ELL_LIBS ELL_CFLAGS BTPCLIENT_FALSE BTPCLIENT_TRUE OBEX_FALSE OBEX_TRUE ICAL_LIBS ICAL_CFLAGS ALSA_LIBS ALSA_CFLAGS MIDI_FALSE MIDI_TRUE JSONC_LIBS JSONC_CFLAGS MESH_FALSE MESH_TRUE CUPS_SERVERBIN CUPS_SERVERBIN_FALSE CUPS_SERVERBIN_TRUE CUPS_FALSE CUPS_TRUE UDEV_DIR UDEV_LIBS UDEV_CFLAGS MONITOR_FALSE MONITOR_TRUE TOOLS_FALSE TOOLS_TRUE ASHA_FALSE ASHA_TRUE CSIP_FALSE CSIP_TRUE MICP_FALSE MICP_TRUE VCP_FALSE VCP_TRUE CCP_FALSE CCP_TRUE MCP_FALSE MCP_TRUE BASS_FALSE BASS_TRUE BAP_FALSE BAP_TRUE HEALTH_FALSE HEALTH_TRUE HOG_FALSE HOG_TRUE HID_FALSE HID_TRUE NETWORK_FALSE NETWORK_TRUE AVRCP_FALSE AVRCP_TRUE A2DP_FALSE A2DP_TRUE SAP_FALSE SAP_TRUE NFC_FALSE NFC_TRUE TEST_FALSE TEST_TRUE LIBRARY_FALSE LIBRARY_TRUE BACKTRACE_LIBS BACKTRACE_CFLAGS ZSH_COMPLETIONS_FALSE ZSH_COMPLETIONS_TRUE ZSH_COMPLETIONDIR DBUS_SESSIONBUSDIR DBUS_SYSTEMBUSDIR DBUS_CONFDIR DBUS_LIBS DBUS_CFLAGS GTHREAD_LIBS GTHREAD_CFLAGS GLIB_LIBS GLIB_CFLAGS VALGRIND_FALSE VALGRIND_TRUE MISC_LDFLAGS MISC_CFLAGS ASAN_LIB DBUS_RUN_SESSION_FALSE DBUS_RUN_SESSION_TRUE COVERAGE_FALSE COVERAGE_TRUE enable_valgrind enable_dbus_run_session enable_coverage LT_SYS_LIBRARY_PATH OTOOL64 OTOOL LIPO NMEDIT DSYMUTIL MANIFEST_TOOL RANLIB ac_ct_AR AR DLLTOOL OBJDUMP FILECMD LN_S NM ac_ct_DUMPBIN DUMPBIN LD FGREP EGREP GREP SED host_os host_vendor host_cpu host build_os build_vendor build_cpu build LIBTOOL am__fastdepCC_FALSE am__fastdepCC_TRUE CCDEPMODE am__nodep AMDEPBACKSLASH AMDEP_FALSE AMDEP_TRUE am__include DEPDIR OBJEXT EXEEXT ac_ct_CC CPPFLAGS LDFLAGS CFLAGS CC WARNING_CFLAGS PKG_CONFIG_LIBDIR PKG_CONFIG_PATH PKG_CONFIG MAINT MAINTAINER_MODE_FALSE MAINTAINER_MODE_TRUE AM_BACKSLASH AM_DEFAULT_VERBOSITY AM_DEFAULT_V AM_V CSCOPE ETAGS CTAGS am__untar am__tar AMTAR am__leading_dot SET_MAKE AWK mkdir_p MKDIR_P INSTALL_STRIP_PROGRAM STRIP install_sh MAKEINFO AUTOHEADER AUTOMAKE AUTOCONF ACLOCAL VERSION PACKAGE CYGPATH_W am__isrc INSTALL_DATA INSTALL_SCRIPT INSTALL_PROGRAM target_alias host_alias build_alias LIBS ECHO_T ECHO_N ECHO_C DEFS mandir localedir libdir psdir pdfdir dvidir htmldir infodir docdir oldincludedir includedir runstatedir localstatedir sharedstatedir sysconfdir datadir datarootdir libexecdir sbindir bindir program_transform_name prefix exec_prefix PACKAGE_URL PACKAGE_BUGREPORT PACKAGE_STRING PACKAGE_VERSION PACKAGE_TARNAME PACKAGE_NAME PATH_SEPARATOR SHELL am__quote' ac_subst_files='' ac_user_opts=' enable_option_checking enable_silent_rules enable_maintainer_mode enable_dependency_tracking enable_static enable_shared with_pic enable_fast_install with_aix_soname with_gnu_ld with_sysroot enable_libtool_lock enable_optimization enable_asan enable_lsan enable_ubsan enable_debug enable_pie enable_threads with_dbusconfdir with_dbussystembusdir with_dbussessionbusdir with_zsh_completion_dir enable_backtrace enable_library enable_test enable_nfc enable_sap enable_a2dp enable_avrcp enable_network enable_hid enable_hog enable_health enable_bap enable_bass enable_mcp enable_ccp enable_vcp enable_micp enable_csip enable_asha enable_tools enable_monitor enable_udev with_udevdir enable_cups enable_mesh enable_midi enable_obex enable_btpclient enable_external_ell enable_client enable_systemd with_systemdsystemunitdir with_systemduserunitdir enable_datafiles enable_manpages enable_testing enable_experimental enable_deprecated enable_external_plugins enable_sixaxis enable_hid2hci enable_logger enable_admin enable_android with_phonebook ' ac_precious_vars='build_alias host_alias target_alias PKG_CONFIG PKG_CONFIG_PATH PKG_CONFIG_LIBDIR CC CFLAGS LDFLAGS LIBS CPPFLAGS LT_SYS_LIBRARY_PATH GLIB_CFLAGS GLIB_LIBS GTHREAD_CFLAGS GTHREAD_LIBS DBUS_CFLAGS DBUS_LIBS UDEV_CFLAGS UDEV_LIBS JSONC_CFLAGS JSONC_LIBS ALSA_CFLAGS ALSA_LIBS ICAL_CFLAGS ICAL_LIBS ELL_CFLAGS ELL_LIBS SBC_CFLAGS SBC_LIBS SPEEXDSP_CFLAGS SPEEXDSP_LIBS LIBEBOOK_CFLAGS LIBEBOOK_LIBS LIBEDATESERVER_CFLAGS LIBEDATESERVER_LIBS' # Initialize some variables set by options. ac_init_help= ac_init_version=false ac_unrecognized_opts= ac_unrecognized_sep= # The variables have the same names as the options, with # dashes changed to underlines. cache_file=/dev/null exec_prefix=NONE no_create= no_recursion= prefix=NONE program_prefix=NONE program_suffix=NONE program_transform_name=s,x,x, silent= site= srcdir= verbose= x_includes=NONE x_libraries=NONE # Installation directory options. # These are left unexpanded so users can "make install exec_prefix=/foo" # and all the variables that are supposed to be based on exec_prefix # by default will actually change. # Use braces instead of parens because sh, perl, etc. also accept them. # (The list follows the same order as the GNU Coding Standards.) bindir='${exec_prefix}/bin' sbindir='${exec_prefix}/sbin' libexecdir='${exec_prefix}/libexec' datarootdir='${prefix}/share' datadir='${datarootdir}' sysconfdir='${prefix}/etc' sharedstatedir='${prefix}/com' localstatedir='${prefix}/var' runstatedir='${localstatedir}/run' includedir='${prefix}/include' oldincludedir='/usr/include' docdir='${datarootdir}/doc/${PACKAGE_TARNAME}' infodir='${datarootdir}/info' htmldir='${docdir}' dvidir='${docdir}' pdfdir='${docdir}' psdir='${docdir}' libdir='${exec_prefix}/lib' localedir='${datarootdir}/locale' mandir='${datarootdir}/man' ac_prev= ac_dashdash= for ac_option do # If the previous option needs an argument, assign it. if test -n "$ac_prev"; then eval $ac_prev=\$ac_option ac_prev= continue fi case $ac_option in *=?*) ac_optarg=`expr "X$ac_option" : '[^=]*=\(.*\)'` ;; *=) ac_optarg= ;; *) ac_optarg=yes ;; esac case $ac_dashdash$ac_option in --) ac_dashdash=yes ;; -bindir | --bindir | --bindi | --bind | --bin | --bi) ac_prev=bindir ;; -bindir=* | --bindir=* | --bindi=* | --bind=* | --bin=* | --bi=*) bindir=$ac_optarg ;; -build | --build | --buil | --bui | --bu) ac_prev=build_alias ;; -build=* | --build=* | --buil=* | --bui=* | --bu=*) build_alias=$ac_optarg ;; -cache-file | --cache-file | --cache-fil | --cache-fi \ | --cache-f | --cache- | --cache | --cach | --cac | --ca | --c) ac_prev=cache_file ;; -cache-file=* | --cache-file=* | --cache-fil=* | --cache-fi=* \ | --cache-f=* | --cache-=* | --cache=* | --cach=* | --cac=* | --ca=* | --c=*) cache_file=$ac_optarg ;; --config-cache | -C) cache_file=config.cache ;; -datadir | --datadir | --datadi | --datad) ac_prev=datadir ;; -datadir=* | --datadir=* | --datadi=* | --datad=*) datadir=$ac_optarg ;; -datarootdir | --datarootdir | --datarootdi | --datarootd | --dataroot \ | --dataroo | --dataro | --datar) ac_prev=datarootdir ;; -datarootdir=* | --datarootdir=* | --datarootdi=* | --datarootd=* \ | --dataroot=* | --dataroo=* | --dataro=* | --datar=*) datarootdir=$ac_optarg ;; -disable-* | --disable-*) ac_useropt=`expr "x$ac_option" : 'x-*disable-\(.*\)'` # Reject names that are not valid shell variable names. expr "x$ac_useropt" : ".*[^-+._$as_cr_alnum]" >/dev/null && as_fn_error $? "invalid feature name: '$ac_useropt'" ac_useropt_orig=$ac_useropt ac_useropt=`printf "%s\n" "$ac_useropt" | sed 's/[-+.]/_/g'` case $ac_user_opts in *" "enable_$ac_useropt" "*) ;; *) ac_unrecognized_opts="$ac_unrecognized_opts$ac_unrecognized_sep--disable-$ac_useropt_orig" ac_unrecognized_sep=', ';; esac eval enable_$ac_useropt=no ;; -docdir | --docdir | --docdi | --doc | --do) ac_prev=docdir ;; -docdir=* | --docdir=* | --docdi=* | --doc=* | --do=*) docdir=$ac_optarg ;; -dvidir | --dvidir | --dvidi | --dvid | --dvi | --dv) ac_prev=dvidir ;; -dvidir=* | --dvidir=* | --dvidi=* | --dvid=* | --dvi=* | --dv=*) dvidir=$ac_optarg ;; -enable-* | --enable-*) ac_useropt=`expr "x$ac_option" : 'x-*enable-\([^=]*\)'` # Reject names that are not valid shell variable names. expr "x$ac_useropt" : ".*[^-+._$as_cr_alnum]" >/dev/null && as_fn_error $? "invalid feature name: '$ac_useropt'" ac_useropt_orig=$ac_useropt ac_useropt=`printf "%s\n" "$ac_useropt" | sed 's/[-+.]/_/g'` case $ac_user_opts in *" "enable_$ac_useropt" "*) ;; *) ac_unrecognized_opts="$ac_unrecognized_opts$ac_unrecognized_sep--enable-$ac_useropt_orig" ac_unrecognized_sep=', ';; esac eval enable_$ac_useropt=\$ac_optarg ;; -exec-prefix | --exec_prefix | --exec-prefix | --exec-prefi \ | --exec-pref | --exec-pre | --exec-pr | --exec-p | --exec- \ | --exec | --exe | --ex) ac_prev=exec_prefix ;; -exec-prefix=* | --exec_prefix=* | --exec-prefix=* | --exec-prefi=* \ | --exec-pref=* | --exec-pre=* | --exec-pr=* | --exec-p=* | --exec-=* \ | --exec=* | --exe=* | --ex=*) exec_prefix=$ac_optarg ;; -gas | --gas | --ga | --g) # Obsolete; use --with-gas. with_gas=yes ;; -help | --help | --hel | --he | -h) ac_init_help=long ;; -help=r* | --help=r* | --hel=r* | --he=r* | -hr*) ac_init_help=recursive ;; -help=s* | --help=s* | --hel=s* | --he=s* | -hs*) ac_init_help=short ;; -host | --host | --hos | --ho) ac_prev=host_alias ;; -host=* | --host=* | --hos=* | --ho=*) host_alias=$ac_optarg ;; -htmldir | --htmldir | --htmldi | --htmld | --html | --htm | --ht) ac_prev=htmldir ;; -htmldir=* | --htmldir=* | --htmldi=* | --htmld=* | --html=* | --htm=* \ | --ht=*) htmldir=$ac_optarg ;; -includedir | --includedir | --includedi | --included | --include \ | --includ | --inclu | --incl | --inc) ac_prev=includedir ;; -includedir=* | --includedir=* | --includedi=* | --included=* | --include=* \ | --includ=* | --inclu=* | --incl=* | --inc=*) includedir=$ac_optarg ;; -infodir | --infodir | --infodi | --infod | --info | --inf) ac_prev=infodir ;; -infodir=* | --infodir=* | --infodi=* | --infod=* | --info=* | --inf=*) infodir=$ac_optarg ;; -libdir | --libdir | --libdi | --libd) ac_prev=libdir ;; -libdir=* | --libdir=* | --libdi=* | --libd=*) libdir=$ac_optarg ;; -libexecdir | --libexecdir | --libexecdi | --libexecd | --libexec \ | --libexe | --libex | --libe) ac_prev=libexecdir ;; -libexecdir=* | --libexecdir=* | --libexecdi=* | --libexecd=* | --libexec=* \ | --libexe=* | --libex=* | --libe=*) libexecdir=$ac_optarg ;; -localedir | --localedir | --localedi | --localed | --locale) ac_prev=localedir ;; -localedir=* | --localedir=* | --localedi=* | --localed=* | --locale=*) localedir=$ac_optarg ;; -localstatedir | --localstatedir | --localstatedi | --localstated \ | --localstate | --localstat | --localsta | --localst | --locals) ac_prev=localstatedir ;; -localstatedir=* | --localstatedir=* | --localstatedi=* | --localstated=* \ | --localstate=* | --localstat=* | --localsta=* | --localst=* | --locals=*) localstatedir=$ac_optarg ;; -mandir | --mandir | --mandi | --mand | --man | --ma | --m) ac_prev=mandir ;; -mandir=* | --mandir=* | --mandi=* | --mand=* | --man=* | --ma=* | --m=*) mandir=$ac_optarg ;; -nfp | --nfp | --nf) # Obsolete; use --without-fp. with_fp=no ;; -no-create | --no-create | --no-creat | --no-crea | --no-cre \ | --no-cr | --no-c | -n) no_create=yes ;; -no-recursion | --no-recursion | --no-recursio | --no-recursi \ | --no-recurs | --no-recur | --no-recu | --no-rec | --no-re | --no-r) no_recursion=yes ;; -oldincludedir | --oldincludedir | --oldincludedi | --oldincluded \ | --oldinclude | --oldinclud | --oldinclu | --oldincl | --oldinc \ | --oldin | --oldi | --old | --ol | --o) ac_prev=oldincludedir ;; -oldincludedir=* | --oldincludedir=* | --oldincludedi=* | --oldincluded=* \ | --oldinclude=* | --oldinclud=* | --oldinclu=* | --oldincl=* | --oldinc=* \ | --oldin=* | --oldi=* | --old=* | --ol=* | --o=*) oldincludedir=$ac_optarg ;; -prefix | --prefix | --prefi | --pref | --pre | --pr | --p) ac_prev=prefix ;; -prefix=* | --prefix=* | --prefi=* | --pref=* | --pre=* | --pr=* | --p=*) prefix=$ac_optarg ;; -program-prefix | --program-prefix | --program-prefi | --program-pref \ | --program-pre | --program-pr | --program-p) ac_prev=program_prefix ;; -program-prefix=* | --program-prefix=* | --program-prefi=* \ | --program-pref=* | --program-pre=* | --program-pr=* | --program-p=*) program_prefix=$ac_optarg ;; -program-suffix | --program-suffix | --program-suffi | --program-suff \ | --program-suf | --program-su | --program-s) ac_prev=program_suffix ;; -program-suffix=* | --program-suffix=* | --program-suffi=* \ | --program-suff=* | --program-suf=* | --program-su=* | --program-s=*) program_suffix=$ac_optarg ;; -program-transform-name | --program-transform-name \ | --program-transform-nam | --program-transform-na \ | --program-transform-n | --program-transform- \ | --program-transform | --program-transfor \ | --program-transfo | --program-transf \ | --program-trans | --program-tran \ | --progr-tra | --program-tr | --program-t) ac_prev=program_transform_name ;; -program-transform-name=* | --program-transform-name=* \ | --program-transform-nam=* | --program-transform-na=* \ | --program-transform-n=* | --program-transform-=* \ | --program-transform=* | --program-transfor=* \ | --program-transfo=* | --program-transf=* \ | --program-trans=* | --program-tran=* \ | --progr-tra=* | --program-tr=* | --program-t=*) program_transform_name=$ac_optarg ;; -pdfdir | --pdfdir | --pdfdi | --pdfd | --pdf | --pd) ac_prev=pdfdir ;; -pdfdir=* | --pdfdir=* | --pdfdi=* | --pdfd=* | --pdf=* | --pd=*) pdfdir=$ac_optarg ;; -psdir | --psdir | --psdi | --psd | --ps) ac_prev=psdir ;; -psdir=* | --psdir=* | --psdi=* | --psd=* | --ps=*) psdir=$ac_optarg ;; -q | -quiet | --quiet | --quie | --qui | --qu | --q \ | -silent | --silent | --silen | --sile | --sil) silent=yes ;; -runstatedir | --runstatedir | --runstatedi | --runstated \ | --runstate | --runstat | --runsta | --runst | --runs \ | --run | --ru | --r) ac_prev=runstatedir ;; -runstatedir=* | --runstatedir=* | --runstatedi=* | --runstated=* \ | --runstate=* | --runstat=* | --runsta=* | --runst=* | --runs=* \ | --run=* | --ru=* | --r=*) runstatedir=$ac_optarg ;; -sbindir | --sbindir | --sbindi | --sbind | --sbin | --sbi | --sb) ac_prev=sbindir ;; -sbindir=* | --sbindir=* | --sbindi=* | --sbind=* | --sbin=* \ | --sbi=* | --sb=*) sbindir=$ac_optarg ;; -sharedstatedir | --sharedstatedir | --sharedstatedi \ | --sharedstated | --sharedstate | --sharedstat | --sharedsta \ | --sharedst | --shareds | --shared | --share | --shar \ | --sha | --sh) ac_prev=sharedstatedir ;; -sharedstatedir=* | --sharedstatedir=* | --sharedstatedi=* \ | --sharedstated=* | --sharedstate=* | --sharedstat=* | --sharedsta=* \ | --sharedst=* | --shareds=* | --shared=* | --share=* | --shar=* \ | --sha=* | --sh=*) sharedstatedir=$ac_optarg ;; -site | --site | --sit) ac_prev=site ;; -site=* | --site=* | --sit=*) site=$ac_optarg ;; -srcdir | --srcdir | --srcdi | --srcd | --src | --sr) ac_prev=srcdir ;; -srcdir=* | --srcdir=* | --srcdi=* | --srcd=* | --src=* | --sr=*) srcdir=$ac_optarg ;; -sysconfdir | --sysconfdir | --sysconfdi | --sysconfd | --sysconf \ | --syscon | --sysco | --sysc | --sys | --sy) ac_prev=sysconfdir ;; -sysconfdir=* | --sysconfdir=* | --sysconfdi=* | --sysconfd=* | --sysconf=* \ | --syscon=* | --sysco=* | --sysc=* | --sys=* | --sy=*) sysconfdir=$ac_optarg ;; -target | --target | --targe | --targ | --tar | --ta | --t) ac_prev=target_alias ;; -target=* | --target=* | --targe=* | --targ=* | --tar=* | --ta=* | --t=*) target_alias=$ac_optarg ;; -v | -verbose | --verbose | --verbos | --verbo | --verb) verbose=yes ;; -version | --version | --versio | --versi | --vers | -V) ac_init_version=: ;; -with-* | --with-*) ac_useropt=`expr "x$ac_option" : 'x-*with-\([^=]*\)'` # Reject names that are not valid shell variable names. expr "x$ac_useropt" : ".*[^-+._$as_cr_alnum]" >/dev/null && as_fn_error $? "invalid package name: '$ac_useropt'" ac_useropt_orig=$ac_useropt ac_useropt=`printf "%s\n" "$ac_useropt" | sed 's/[-+.]/_/g'` case $ac_user_opts in *" "with_$ac_useropt" "*) ;; *) ac_unrecognized_opts="$ac_unrecognized_opts$ac_unrecognized_sep--with-$ac_useropt_orig" ac_unrecognized_sep=', ';; esac eval with_$ac_useropt=\$ac_optarg ;; -without-* | --without-*) ac_useropt=`expr "x$ac_option" : 'x-*without-\(.*\)'` # Reject names that are not valid shell variable names. expr "x$ac_useropt" : ".*[^-+._$as_cr_alnum]" >/dev/null && as_fn_error $? "invalid package name: '$ac_useropt'" ac_useropt_orig=$ac_useropt ac_useropt=`printf "%s\n" "$ac_useropt" | sed 's/[-+.]/_/g'` case $ac_user_opts in *" "with_$ac_useropt" "*) ;; *) ac_unrecognized_opts="$ac_unrecognized_opts$ac_unrecognized_sep--without-$ac_useropt_orig" ac_unrecognized_sep=', ';; esac eval with_$ac_useropt=no ;; --x) # Obsolete; use --with-x. with_x=yes ;; -x-includes | --x-includes | --x-include | --x-includ | --x-inclu \ | --x-incl | --x-inc | --x-in | --x-i) ac_prev=x_includes ;; -x-includes=* | --x-includes=* | --x-include=* | --x-includ=* | --x-inclu=* \ | --x-incl=* | --x-inc=* | --x-in=* | --x-i=*) x_includes=$ac_optarg ;; -x-libraries | --x-libraries | --x-librarie | --x-librari \ | --x-librar | --x-libra | --x-libr | --x-lib | --x-li | --x-l) ac_prev=x_libraries ;; -x-libraries=* | --x-libraries=* | --x-librarie=* | --x-librari=* \ | --x-librar=* | --x-libra=* | --x-libr=* | --x-lib=* | --x-li=* | --x-l=*) x_libraries=$ac_optarg ;; -*) as_fn_error $? "unrecognized option: '$ac_option' Try '$0 --help' for more information" ;; *=*) ac_envvar=`expr "x$ac_option" : 'x\([^=]*\)='` # Reject names that are not valid shell variable names. case $ac_envvar in #( '' | [0-9]* | *[!_$as_cr_alnum]* ) as_fn_error $? "invalid variable name: '$ac_envvar'" ;; esac eval $ac_envvar=\$ac_optarg export $ac_envvar ;; *) # FIXME: should be removed in autoconf 3.0. printf "%s\n" "$as_me: WARNING: you should use --build, --host, --target" >&2 expr "x$ac_option" : ".*[^-._$as_cr_alnum]" >/dev/null && printf "%s\n" "$as_me: WARNING: invalid host type: $ac_option" >&2 : "${build_alias=$ac_option} ${host_alias=$ac_option} ${target_alias=$ac_option}" ;; esac done if test -n "$ac_prev"; then ac_option=--`echo $ac_prev | sed 's/_/-/g'` as_fn_error $? "missing argument to $ac_option" fi if test -n "$ac_unrecognized_opts"; then case $enable_option_checking in no) ;; fatal) as_fn_error $? "unrecognized options: $ac_unrecognized_opts" ;; *) printf "%s\n" "$as_me: WARNING: unrecognized options: $ac_unrecognized_opts" >&2 ;; esac fi # Check all directory arguments for consistency. for ac_var in exec_prefix prefix bindir sbindir libexecdir datarootdir \ datadir sysconfdir sharedstatedir localstatedir includedir \ oldincludedir docdir infodir htmldir dvidir pdfdir psdir \ libdir localedir mandir runstatedir do eval ac_val=\$$ac_var # Remove trailing slashes. case $ac_val in */ ) ac_val=`expr "X$ac_val" : 'X\(.*[^/]\)' \| "X$ac_val" : 'X\(.*\)'` eval $ac_var=\$ac_val;; esac # Be sure to have absolute directory names. case $ac_val in [\\/$]* | ?:[\\/]* ) continue;; NONE | '' ) case $ac_var in *prefix ) continue;; esac;; esac as_fn_error $? "expected an absolute directory name for --$ac_var: $ac_val" done # There might be people who depend on the old broken behavior: '$host' # used to hold the argument of --host etc. # FIXME: To remove some day. build=$build_alias host=$host_alias target=$target_alias # FIXME: To remove some day. if test "x$host_alias" != x; then if test "x$build_alias" = x; then cross_compiling=maybe elif test "x$build_alias" != "x$host_alias"; then cross_compiling=yes fi fi ac_tool_prefix= test -n "$host_alias" && ac_tool_prefix=$host_alias- test "$silent" = yes && exec 6>/dev/null ac_pwd=`pwd` && test -n "$ac_pwd" && ac_ls_di=`ls -di .` && ac_pwd_ls_di=`cd "$ac_pwd" && ls -di .` || as_fn_error $? "working directory cannot be determined" test "X$ac_ls_di" = "X$ac_pwd_ls_di" || as_fn_error $? "pwd does not report name of working directory" # Find the source files, if location was not specified. if test -z "$srcdir"; then ac_srcdir_defaulted=yes # Try the directory containing this script, then the parent directory. ac_confdir=`$as_dirname -- "$as_myself" || $as_expr X"$as_myself" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ X"$as_myself" : 'X\(//\)[^/]' \| \ X"$as_myself" : 'X\(//\)$' \| \ X"$as_myself" : 'X\(/\)' \| . 2>/dev/null || printf "%s\n" X"$as_myself" | sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ s//\1/ q } /^X\(\/\/\)[^/].*/{ s//\1/ q } /^X\(\/\/\)$/{ s//\1/ q } /^X\(\/\).*/{ s//\1/ q } s/.*/./; q'` srcdir=$ac_confdir if test ! -r "$srcdir/$ac_unique_file"; then srcdir=.. fi else ac_srcdir_defaulted=no fi if test ! -r "$srcdir/$ac_unique_file"; then test "$ac_srcdir_defaulted" = yes && srcdir="$ac_confdir or .." as_fn_error $? "cannot find sources ($ac_unique_file) in $srcdir" fi ac_msg="sources are in $srcdir, but 'cd $srcdir' does not work" ac_abs_confdir=`( cd "$srcdir" && test -r "./$ac_unique_file" || as_fn_error $? "$ac_msg" pwd)` # When building in place, set srcdir=. if test "$ac_abs_confdir" = "$ac_pwd"; then srcdir=. fi # Remove unnecessary trailing slashes from srcdir. # Double slashes in file names in object file debugging info # mess up M-x gdb in Emacs. case $srcdir in */) srcdir=`expr "X$srcdir" : 'X\(.*[^/]\)' \| "X$srcdir" : 'X\(.*\)'`;; esac for ac_var in $ac_precious_vars; do eval ac_env_${ac_var}_set=\${${ac_var}+set} eval ac_env_${ac_var}_value=\$${ac_var} eval ac_cv_env_${ac_var}_set=\${${ac_var}+set} eval ac_cv_env_${ac_var}_value=\$${ac_var} done # # Report the --help message. # if test "$ac_init_help" = "long"; then # Omit some internal or obsolete options to make the list less imposing. # This message is too long to be a string in the A/UX 3.1 sh. cat <<_ACEOF 'configure' configures bluez 5.82 to adapt to many kinds of systems. Usage: $0 [OPTION]... [VAR=VALUE]... To assign environment variables (e.g., CC, CFLAGS...), specify them as VAR=VALUE. See below for descriptions of some of the useful variables. Defaults for the options are specified in brackets. Configuration: -h, --help display this help and exit --help=short display options specific to this package --help=recursive display the short help of all the included packages -V, --version display version information and exit -q, --quiet, --silent do not print 'checking ...' messages --cache-file=FILE cache test results in FILE [disabled] -C, --config-cache alias for '--cache-file=config.cache' -n, --no-create do not create output files --srcdir=DIR find the sources in DIR [configure dir or '..'] Installation directories: --prefix=PREFIX install architecture-independent files in PREFIX [$ac_default_prefix] --exec-prefix=EPREFIX install architecture-dependent files in EPREFIX [PREFIX] By default, 'make install' will install all the files in '$ac_default_prefix/bin', '$ac_default_prefix/lib' etc. You can specify an installation prefix other than '$ac_default_prefix' using '--prefix', for instance '--prefix=\$HOME'. For better control, use the options below. Fine tuning of the installation directories: --bindir=DIR user executables [EPREFIX/bin] --sbindir=DIR system admin executables [EPREFIX/sbin] --libexecdir=DIR program executables [EPREFIX/libexec] --sysconfdir=DIR read-only single-machine data [PREFIX/etc] --sharedstatedir=DIR modifiable architecture-independent data [PREFIX/com] --localstatedir=DIR modifiable single-machine data [PREFIX/var] --runstatedir=DIR modifiable per-process data [LOCALSTATEDIR/run] --libdir=DIR object code libraries [EPREFIX/lib] --includedir=DIR C header files [PREFIX/include] --oldincludedir=DIR C header files for non-gcc [/usr/include] --datarootdir=DIR read-only arch.-independent data root [PREFIX/share] --datadir=DIR read-only architecture-independent data [DATAROOTDIR] --infodir=DIR info documentation [DATAROOTDIR/info] --localedir=DIR locale-dependent data [DATAROOTDIR/locale] --mandir=DIR man documentation [DATAROOTDIR/man] --docdir=DIR documentation root [DATAROOTDIR/doc/bluez] --htmldir=DIR html documentation [DOCDIR] --dvidir=DIR dvi documentation [DOCDIR] --pdfdir=DIR pdf documentation [DOCDIR] --psdir=DIR ps documentation [DOCDIR] _ACEOF cat <<\_ACEOF Program names: --program-prefix=PREFIX prepend PREFIX to installed program names --program-suffix=SUFFIX append SUFFIX to installed program names --program-transform-name=PROGRAM run sed PROGRAM on installed program names System types: --build=BUILD configure for building on BUILD [guessed] --host=HOST cross-compile to build programs to run on HOST [BUILD] _ACEOF fi if test -n "$ac_init_help"; then case $ac_init_help in short | recursive ) echo "Configuration of bluez 5.82:";; esac cat <<\_ACEOF Optional Features: --disable-option-checking ignore unrecognized --enable/--with options --disable-FEATURE do not include FEATURE (same as --enable-FEATURE=no) --enable-FEATURE[=ARG] include FEATURE [ARG=yes] --enable-silent-rules less verbose build output (undo: "make V=1") --disable-silent-rules verbose build output (undo: "make V=0") --enable-maintainer-mode enable make rules and dependencies not useful (and sometimes confusing) to the casual installer --enable-dependency-tracking do not reject slow dependency extractors --disable-dependency-tracking speeds up one-time build --enable-static[=PKGS] build static libraries [default=no] --enable-shared[=PKGS] build shared libraries [default=yes] --enable-fast-install[=PKGS] optimize for fast installation [default=yes] --disable-libtool-lock avoid locking (might break parallel builds) --disable-optimization disable code optimization through compiler --enable-asan enable linking with address sanitizer --enable-lsan enable linking with address sanitizer --enable-ubsan enable linking with address sanitizer --enable-debug enable compiling with debugging information --enable-pie enable position independent executables flag --enable-threads enable threading support --enable-backtrace compile backtrace support --enable-library install Bluetooth library --enable-test enable test/example scripts --enable-nfc enable NFC paring --enable-sap enable SAP profile --disable-a2dp disable A2DP profile --disable-avrcp disable AVRCP profile --disable-network disable network profiles --disable-hid disable HID profile --disable-hog disable HoG profile --enable-health enable health profiles --disable-bap disable BAP profile --disable-bass disable BASS service --disable-mcp disable MCP profile --disable-ccp disable CCP profile --disable-vcp disable VCP profile --disable-micp disable MICP profile --disable-csip disable CSIP profile --disable-asha disable ASHA support --disable-tools disable Bluetooth tools --disable-monitor disable Bluetooth monitor --disable-udev disable udev device support --disable-cups disable CUPS printer support --enable-mesh enable Mesh profile support --enable-midi enable MIDI support --disable-obex disable OBEX profile support --enable-btpclient enable BTP client --enable-external-ell enable external Embedded Linux library --disable-client disable command line client --disable-systemd disable systemd integration --disable-datafiles do not install configuration and data files --disable-manpages disable building of manual pages --enable-testing enable testing tools --enable-experimental enable experimental tools --enable-deprecated enable deprecated tools --enable-external-plugins enable support for external plugins --enable-sixaxis enable sixaxis plugin --enable-hid2hci enable hid2hci tool --enable-logger enable HCI logger service --enable-admin enable admin policy plugin --enable-android enable BlueZ for Android Optional Packages: --with-PACKAGE[=ARG] use PACKAGE [ARG=yes] --without-PACKAGE do not use PACKAGE (same as --with-PACKAGE=no) --with-pic[=PKGS] try to use only PIC/non-PIC objects [default=use both] --with-aix-soname=aix|svr4|both shared library versioning (aka "SONAME") variant to provide on AIX, [default=aix]. --with-gnu-ld assume the C compiler uses GNU ld [default=no] --with-sysroot[=DIR] Search for dependent libraries within DIR (or the compiler's sysroot if not specified). --with-dbusconfdir=DIR path to D-Bus configuration directory --with-dbussystembusdir=DIR path to D-Bus system bus services directory --with-dbussessionbusdir=DIR path to D-Bus session bus services directory --with-zsh-completion-dir=DIR path to install zsh completions --with-udevdir=DIR path to udev directory --with-systemdsystemunitdir=DIR path to systemd system unit directory --with-systemduserunitdir=DIR path to systemd user unit directory --with-phonebook=PLUGIN obexd phonebook plugin (default=dummy) Some influential environment variables: PKG_CONFIG path to pkg-config utility PKG_CONFIG_PATH directories to add to pkg-config's search path PKG_CONFIG_LIBDIR path overriding pkg-config's built-in search path CC C compiler command CFLAGS C compiler flags LDFLAGS linker flags, e.g. -L if you have libraries in a nonstandard directory LIBS libraries to pass to the linker, e.g. -l CPPFLAGS (Objective) C/C++ preprocessor flags, e.g. -I if you have headers in a nonstandard directory LT_SYS_LIBRARY_PATH User-defined run-time library search path. GLIB_CFLAGS C compiler flags for GLIB, overriding pkg-config GLIB_LIBS linker flags for GLIB, overriding pkg-config GTHREAD_CFLAGS C compiler flags for GTHREAD, overriding pkg-config GTHREAD_LIBS linker flags for GTHREAD, overriding pkg-config DBUS_CFLAGS C compiler flags for DBUS, overriding pkg-config DBUS_LIBS linker flags for DBUS, overriding pkg-config UDEV_CFLAGS C compiler flags for UDEV, overriding pkg-config UDEV_LIBS linker flags for UDEV, overriding pkg-config JSONC_CFLAGS C compiler flags for JSONC, overriding pkg-config JSONC_LIBS linker flags for JSONC, overriding pkg-config ALSA_CFLAGS C compiler flags for ALSA, overriding pkg-config ALSA_LIBS linker flags for ALSA, overriding pkg-config ICAL_CFLAGS C compiler flags for ICAL, overriding pkg-config ICAL_LIBS linker flags for ICAL, overriding pkg-config ELL_CFLAGS C compiler flags for ELL, overriding pkg-config ELL_LIBS linker flags for ELL, overriding pkg-config SBC_CFLAGS C compiler flags for SBC, overriding pkg-config SBC_LIBS linker flags for SBC, overriding pkg-config SPEEXDSP_CFLAGS C compiler flags for SPEEXDSP, overriding pkg-config SPEEXDSP_LIBS linker flags for SPEEXDSP, overriding pkg-config LIBEBOOK_CFLAGS C compiler flags for LIBEBOOK, overriding pkg-config LIBEBOOK_LIBS linker flags for LIBEBOOK, overriding pkg-config LIBEDATESERVER_CFLAGS C compiler flags for LIBEDATESERVER, overriding pkg-config LIBEDATESERVER_LIBS linker flags for LIBEDATESERVER, overriding pkg-config Use these variables to override the choices made by 'configure' or to help it to find libraries and programs with nonstandard names/locations. Report bugs to the package provider. _ACEOF ac_status=$? fi if test "$ac_init_help" = "recursive"; then # If there are subdirs, report their specific --help. for ac_dir in : $ac_subdirs_all; do test "x$ac_dir" = x: && continue test -d "$ac_dir" || { cd "$srcdir" && ac_pwd=`pwd` && srcdir=. && test -d "$ac_dir"; } || continue ac_builddir=. case "$ac_dir" in .) ac_dir_suffix= ac_top_builddir_sub=. ac_top_build_prefix= ;; *) ac_dir_suffix=/`printf "%s\n" "$ac_dir" | sed 's|^\.[\\/]||'` # A ".." for each directory in $ac_dir_suffix. ac_top_builddir_sub=`printf "%s\n" "$ac_dir_suffix" | sed 's|/[^\\/]*|/..|g;s|/||'` case $ac_top_builddir_sub in "") ac_top_builddir_sub=. ac_top_build_prefix= ;; *) ac_top_build_prefix=$ac_top_builddir_sub/ ;; esac ;; esac ac_abs_top_builddir=$ac_pwd ac_abs_builddir=$ac_pwd$ac_dir_suffix # for backward compatibility: ac_top_builddir=$ac_top_build_prefix case $srcdir in .) # We are building in place. ac_srcdir=. ac_top_srcdir=$ac_top_builddir_sub ac_abs_top_srcdir=$ac_pwd ;; [\\/]* | ?:[\\/]* ) # Absolute name. ac_srcdir=$srcdir$ac_dir_suffix; ac_top_srcdir=$srcdir ac_abs_top_srcdir=$srcdir ;; *) # Relative name. ac_srcdir=$ac_top_build_prefix$srcdir$ac_dir_suffix ac_top_srcdir=$ac_top_build_prefix$srcdir ac_abs_top_srcdir=$ac_pwd/$srcdir ;; esac ac_abs_srcdir=$ac_abs_top_srcdir$ac_dir_suffix cd "$ac_dir" || { ac_status=$?; continue; } # Check for configure.gnu first; this name is used for a wrapper for # Metaconfig's "Configure" on case-insensitive file systems. if test -f "$ac_srcdir/configure.gnu"; then echo && $SHELL "$ac_srcdir/configure.gnu" --help=recursive elif test -f "$ac_srcdir/configure"; then echo && $SHELL "$ac_srcdir/configure" --help=recursive else printf "%s\n" "$as_me: WARNING: no configuration information is in $ac_dir" >&2 fi || ac_status=$? cd "$ac_pwd" || { ac_status=$?; break; } done fi test -n "$ac_init_help" && exit $ac_status if $ac_init_version; then cat <<\_ACEOF bluez configure 5.82 generated by GNU Autoconf 2.72 Copyright (C) 2023 Free Software Foundation, Inc. This configure script is free software; the Free Software Foundation gives unlimited permission to copy, distribute and modify it. _ACEOF exit fi ## ------------------------ ## ## Autoconf initialization. ## ## ------------------------ ## # ac_fn_c_try_compile LINENO # -------------------------- # Try to compile conftest.$ac_ext, and return whether this succeeded. ac_fn_c_try_compile () { as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack rm -f conftest.$ac_objext conftest.beam if { { ac_try="$ac_compile" case "(($ac_try" in *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; *) ac_try_echo=$ac_try;; esac eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" printf "%s\n" "$ac_try_echo"; } >&5 (eval "$ac_compile") 2>conftest.err ac_status=$? if test -s conftest.err; then grep -v '^ *+' conftest.err >conftest.er1 cat conftest.er1 >&5 mv -f conftest.er1 conftest.err fi printf "%s\n" "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; } && { test -z "$ac_c_werror_flag" || test ! -s conftest.err } && test -s conftest.$ac_objext then : ac_retval=0 else case e in #( e) printf "%s\n" "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 ac_retval=1 ;; esac fi eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno as_fn_set_status $ac_retval } # ac_fn_c_try_compile # ac_fn_c_try_link LINENO # ----------------------- # Try to link conftest.$ac_ext, and return whether this succeeded. ac_fn_c_try_link () { as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack rm -f conftest.$ac_objext conftest.beam conftest$ac_exeext if { { ac_try="$ac_link" case "(($ac_try" in *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; *) ac_try_echo=$ac_try;; esac eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" printf "%s\n" "$ac_try_echo"; } >&5 (eval "$ac_link") 2>conftest.err ac_status=$? if test -s conftest.err; then grep -v '^ *+' conftest.err >conftest.er1 cat conftest.er1 >&5 mv -f conftest.er1 conftest.err fi printf "%s\n" "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; } && { test -z "$ac_c_werror_flag" || test ! -s conftest.err } && test -s conftest$ac_exeext && { test "$cross_compiling" = yes || test -x conftest$ac_exeext } then : ac_retval=0 else case e in #( e) printf "%s\n" "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 ac_retval=1 ;; esac fi # Delete the IPA/IPO (Inter Procedural Analysis/Optimization) information # created by the PGI compiler (conftest_ipa8_conftest.oo), as it would # interfere with the next link command; also delete a directory that is # left behind by Apple's compiler. We do this before executing the actions. rm -rf conftest.dSYM conftest_ipa8_conftest.oo eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno as_fn_set_status $ac_retval } # ac_fn_c_try_link # ac_fn_c_check_header_compile LINENO HEADER VAR INCLUDES # ------------------------------------------------------- # Tests whether HEADER exists and can be compiled using the include files in # INCLUDES, setting the cache variable VAR accordingly. ac_fn_c_check_header_compile () { as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $2" >&5 printf %s "checking for $2... " >&6; } if eval test \${$3+y} then : printf %s "(cached) " >&6 else case e in #( e) cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ $4 #include <$2> _ACEOF if ac_fn_c_try_compile "$LINENO" then : eval "$3=yes" else case e in #( e) eval "$3=no" ;; esac fi rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext ;; esac fi eval ac_res=\$$3 { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5 printf "%s\n" "$ac_res" >&6; } eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno } # ac_fn_c_check_header_compile # ac_fn_c_check_func LINENO FUNC VAR # ---------------------------------- # Tests whether FUNC exists, setting the cache variable VAR accordingly ac_fn_c_check_func () { as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $2" >&5 printf %s "checking for $2... " >&6; } if eval test \${$3+y} then : printf %s "(cached) " >&6 else case e in #( e) cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ /* Define $2 to an innocuous variant, in case declares $2. For example, HP-UX 11i declares gettimeofday. */ #define $2 innocuous_$2 /* System header to define __stub macros and hopefully few prototypes, which can conflict with char $2 (void); below. */ #include #undef $2 /* Override any GCC internal prototype to avoid an error. Use char because int might match the return type of a GCC builtin and then its argument prototype would still apply. */ #ifdef __cplusplus extern "C" #endif char $2 (void); /* The GNU C library defines this for functions which it implements to always fail with ENOSYS. Some functions are actually named something starting with __ and the normal name is an alias. */ #if defined __stub_$2 || defined __stub___$2 choke me #endif int main (void) { return $2 (); ; return 0; } _ACEOF if ac_fn_c_try_link "$LINENO" then : eval "$3=yes" else case e in #( e) eval "$3=no" ;; esac fi rm -f core conftest.err conftest.$ac_objext conftest.beam \ conftest$ac_exeext conftest.$ac_ext ;; esac fi eval ac_res=\$$3 { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5 printf "%s\n" "$ac_res" >&6; } eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno } # ac_fn_c_check_func # ac_fn_check_decl LINENO SYMBOL VAR INCLUDES EXTRA-OPTIONS FLAG-VAR # ------------------------------------------------------------------ # Tests whether SYMBOL is declared in INCLUDES, setting cache variable VAR # accordingly. Pass EXTRA-OPTIONS to the compiler, using FLAG-VAR. ac_fn_check_decl () { as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack as_decl_name=`echo $2|sed 's/ *(.*//'` { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking whether $as_decl_name is declared" >&5 printf %s "checking whether $as_decl_name is declared... " >&6; } if eval test \${$3+y} then : printf %s "(cached) " >&6 else case e in #( e) as_decl_use=`echo $2|sed -e 's/(/((/' -e 's/)/) 0&/' -e 's/,/) 0& (/g'` eval ac_save_FLAGS=\$$6 as_fn_append $6 " $5" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ $4 int main (void) { #ifndef $as_decl_name #ifdef __cplusplus (void) $as_decl_use; #else (void) $as_decl_name; #endif #endif ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO" then : eval "$3=yes" else case e in #( e) eval "$3=no" ;; esac fi rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext eval $6=\$ac_save_FLAGS ;; esac fi eval ac_res=\$$3 { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5 printf "%s\n" "$ac_res" >&6; } eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno } # ac_fn_check_decl ac_configure_args_raw= for ac_arg do case $ac_arg in *\'*) ac_arg=`printf "%s\n" "$ac_arg" | sed "s/'/'\\\\\\\\''/g"` ;; esac as_fn_append ac_configure_args_raw " '$ac_arg'" done case $ac_configure_args_raw in *$as_nl*) ac_safe_unquote= ;; *) ac_unsafe_z='|&;<>()$`\\"*?[ '' ' # This string ends in space, tab. ac_unsafe_a="$ac_unsafe_z#~" ac_safe_unquote="s/ '\\([^$ac_unsafe_a][^$ac_unsafe_z]*\\)'/ \\1/g" ac_configure_args_raw=` printf "%s\n" "$ac_configure_args_raw" | sed "$ac_safe_unquote"`;; esac cat >config.log <<_ACEOF This file contains any messages produced by compilers while running configure, to aid debugging if configure makes a mistake. It was created by bluez $as_me 5.82, which was generated by GNU Autoconf 2.72. Invocation command line was $ $0$ac_configure_args_raw _ACEOF exec 5>>config.log { cat <<_ASUNAME ## --------- ## ## Platform. ## ## --------- ## hostname = `(hostname || uname -n) 2>/dev/null | sed 1q` uname -m = `(uname -m) 2>/dev/null || echo unknown` uname -r = `(uname -r) 2>/dev/null || echo unknown` uname -s = `(uname -s) 2>/dev/null || echo unknown` uname -v = `(uname -v) 2>/dev/null || echo unknown` /usr/bin/uname -p = `(/usr/bin/uname -p) 2>/dev/null || echo unknown` /bin/uname -X = `(/bin/uname -X) 2>/dev/null || echo unknown` /bin/arch = `(/bin/arch) 2>/dev/null || echo unknown` /usr/bin/arch -k = `(/usr/bin/arch -k) 2>/dev/null || echo unknown` /usr/convex/getsysinfo = `(/usr/convex/getsysinfo) 2>/dev/null || echo unknown` /usr/bin/hostinfo = `(/usr/bin/hostinfo) 2>/dev/null || echo unknown` /bin/machine = `(/bin/machine) 2>/dev/null || echo unknown` /usr/bin/oslevel = `(/usr/bin/oslevel) 2>/dev/null || echo unknown` /bin/universe = `(/bin/universe) 2>/dev/null || echo unknown` _ASUNAME as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS case $as_dir in #((( '') as_dir=./ ;; */) ;; *) as_dir=$as_dir/ ;; esac printf "%s\n" "PATH: $as_dir" done IFS=$as_save_IFS } >&5 cat >&5 <<_ACEOF ## ----------- ## ## Core tests. ## ## ----------- ## _ACEOF # Keep a trace of the command line. # Strip out --no-create and --no-recursion so they do not pile up. # Strip out --silent because we don't want to record it for future runs. # Also quote any args containing shell meta-characters. # Make two passes to allow for proper duplicate-argument suppression. ac_configure_args= ac_configure_args0= ac_configure_args1= ac_must_keep_next=false for ac_pass in 1 2 do for ac_arg do case $ac_arg in -no-create | --no-c* | -n | -no-recursion | --no-r*) continue ;; -q | -quiet | --quiet | --quie | --qui | --qu | --q \ | -silent | --silent | --silen | --sile | --sil) continue ;; *\'*) ac_arg=`printf "%s\n" "$ac_arg" | sed "s/'/'\\\\\\\\''/g"` ;; esac case $ac_pass in 1) as_fn_append ac_configure_args0 " '$ac_arg'" ;; 2) as_fn_append ac_configure_args1 " '$ac_arg'" if test $ac_must_keep_next = true; then ac_must_keep_next=false # Got value, back to normal. else case $ac_arg in *=* | --config-cache | -C | -disable-* | --disable-* \ | -enable-* | --enable-* | -gas | --g* | -nfp | --nf* \ | -q | -quiet | --q* | -silent | --sil* | -v | -verb* \ | -with-* | --with-* | -without-* | --without-* | --x) case "$ac_configure_args0 " in "$ac_configure_args1"*" '$ac_arg' "* ) continue ;; esac ;; -* ) ac_must_keep_next=true ;; esac fi as_fn_append ac_configure_args " '$ac_arg'" ;; esac done done { ac_configure_args0=; unset ac_configure_args0;} { ac_configure_args1=; unset ac_configure_args1;} # When interrupted or exit'd, cleanup temporary files, and complete # config.log. We remove comments because anyway the quotes in there # would cause problems or look ugly. # WARNING: Use '\'' to represent an apostrophe within the trap. # WARNING: Do not start the trap code with a newline, due to a FreeBSD 4.0 bug. trap 'exit_status=$? # Sanitize IFS. IFS=" "" $as_nl" # Save into config.log some information that might help in debugging. { echo printf "%s\n" "## ---------------- ## ## Cache variables. ## ## ---------------- ##" echo # The following way of writing the cache mishandles newlines in values, ( for ac_var in `(set) 2>&1 | sed -n '\''s/^\([a-zA-Z_][a-zA-Z0-9_]*\)=.*/\1/p'\''`; do eval ac_val=\$$ac_var case $ac_val in #( *${as_nl}*) case $ac_var in #( *_cv_*) { printf "%s\n" "$as_me:${as_lineno-$LINENO}: WARNING: cache variable $ac_var contains a newline" >&5 printf "%s\n" "$as_me: WARNING: cache variable $ac_var contains a newline" >&2;} ;; esac case $ac_var in #( _ | IFS | as_nl) ;; #( BASH_ARGV | BASH_SOURCE) eval $ac_var= ;; #( *) { eval $ac_var=; unset $ac_var;} ;; esac ;; esac done (set) 2>&1 | case $as_nl`(ac_space='\'' '\''; set) 2>&1` in #( *${as_nl}ac_space=\ *) sed -n \ "s/'\''/'\''\\\\'\'''\''/g; s/^\\([_$as_cr_alnum]*_cv_[_$as_cr_alnum]*\\)=\\(.*\\)/\\1='\''\\2'\''/p" ;; #( *) sed -n "/^[_$as_cr_alnum]*_cv_[_$as_cr_alnum]*=/p" ;; esac | sort ) echo printf "%s\n" "## ----------------- ## ## Output variables. ## ## ----------------- ##" echo for ac_var in $ac_subst_vars do eval ac_val=\$$ac_var case $ac_val in *\'\''*) ac_val=`printf "%s\n" "$ac_val" | sed "s/'\''/'\''\\\\\\\\'\'''\''/g"`;; esac printf "%s\n" "$ac_var='\''$ac_val'\''" done | sort echo if test -n "$ac_subst_files"; then printf "%s\n" "## ------------------- ## ## File substitutions. ## ## ------------------- ##" echo for ac_var in $ac_subst_files do eval ac_val=\$$ac_var case $ac_val in *\'\''*) ac_val=`printf "%s\n" "$ac_val" | sed "s/'\''/'\''\\\\\\\\'\'''\''/g"`;; esac printf "%s\n" "$ac_var='\''$ac_val'\''" done | sort echo fi if test -s confdefs.h; then printf "%s\n" "## ----------- ## ## confdefs.h. ## ## ----------- ##" echo cat confdefs.h echo fi test "$ac_signal" != 0 && printf "%s\n" "$as_me: caught signal $ac_signal" printf "%s\n" "$as_me: exit $exit_status" } >&5 rm -f core *.core core.conftest.* && rm -f -r conftest* confdefs* conf$$* $ac_clean_files && exit $exit_status ' 0 for ac_signal in 1 2 13 15; do trap 'ac_signal='$ac_signal'; as_fn_exit 1' $ac_signal done ac_signal=0 # confdefs.h avoids OS command line length limits that DEFS can exceed. rm -f -r conftest* confdefs.h printf "%s\n" "/* confdefs.h */" > confdefs.h # Predefined preprocessor variables. printf "%s\n" "#define PACKAGE_NAME \"$PACKAGE_NAME\"" >>confdefs.h printf "%s\n" "#define PACKAGE_TARNAME \"$PACKAGE_TARNAME\"" >>confdefs.h printf "%s\n" "#define PACKAGE_VERSION \"$PACKAGE_VERSION\"" >>confdefs.h printf "%s\n" "#define PACKAGE_STRING \"$PACKAGE_STRING\"" >>confdefs.h printf "%s\n" "#define PACKAGE_BUGREPORT \"$PACKAGE_BUGREPORT\"" >>confdefs.h printf "%s\n" "#define PACKAGE_URL \"$PACKAGE_URL\"" >>confdefs.h # Let the site file select an alternate cache file if it wants to. # Prefer an explicitly selected file to automatically selected ones. if test -n "$CONFIG_SITE"; then ac_site_files="$CONFIG_SITE" elif test "x$prefix" != xNONE; then ac_site_files="$prefix/share/config.site $prefix/etc/config.site" else ac_site_files="$ac_default_prefix/share/config.site $ac_default_prefix/etc/config.site" fi for ac_site_file in $ac_site_files do case $ac_site_file in #( */*) : ;; #( *) : ac_site_file=./$ac_site_file ;; esac if test -f "$ac_site_file" && test -r "$ac_site_file"; then { printf "%s\n" "$as_me:${as_lineno-$LINENO}: loading site script $ac_site_file" >&5 printf "%s\n" "$as_me: loading site script $ac_site_file" >&6;} sed 's/^/| /' "$ac_site_file" >&5 . "$ac_site_file" \ || { { printf "%s\n" "$as_me:${as_lineno-$LINENO}: error: in '$ac_pwd':" >&5 printf "%s\n" "$as_me: error: in '$ac_pwd':" >&2;} as_fn_error $? "failed to load site script $ac_site_file See 'config.log' for more details" "$LINENO" 5; } fi done if test -r "$cache_file"; then # Some versions of bash will fail to source /dev/null (special files # actually), so we avoid doing that. DJGPP emulates it as a regular file. if test /dev/null != "$cache_file" && test -f "$cache_file"; then { printf "%s\n" "$as_me:${as_lineno-$LINENO}: loading cache $cache_file" >&5 printf "%s\n" "$as_me: loading cache $cache_file" >&6;} case $cache_file in [\\/]* | ?:[\\/]* ) . "$cache_file";; *) . "./$cache_file";; esac fi else { printf "%s\n" "$as_me:${as_lineno-$LINENO}: creating cache $cache_file" >&5 printf "%s\n" "$as_me: creating cache $cache_file" >&6;} >$cache_file fi # Test code for whether the C compiler supports C89 (global declarations) ac_c_conftest_c89_globals=' /* Does the compiler advertise C89 conformance? Do not test the value of __STDC__, because some compilers set it to 0 while being otherwise adequately conformant. */ #if !defined __STDC__ # error "Compiler does not advertise C89 conformance" #endif #include #include struct stat; /* Most of the following tests are stolen from RCS 5.7 src/conf.sh. */ struct buf { int x; }; struct buf * (*rcsopen) (struct buf *, struct stat *, int); static char *e (char **p, int i) { return p[i]; } static char *f (char * (*g) (char **, int), char **p, ...) { char *s; va_list v; va_start (v,p); s = g (p, va_arg (v,int)); va_end (v); return s; } /* C89 style stringification. */ #define noexpand_stringify(a) #a const char *stringified = noexpand_stringify(arbitrary+token=sequence); /* C89 style token pasting. Exercises some of the corner cases that e.g. old MSVC gets wrong, but not very hard. */ #define noexpand_concat(a,b) a##b #define expand_concat(a,b) noexpand_concat(a,b) extern int vA; extern int vbee; #define aye A #define bee B int *pvA = &expand_concat(v,aye); int *pvbee = &noexpand_concat(v,bee); /* OSF 4.0 Compaq cc is some sort of almost-ANSI by default. It has function prototypes and stuff, but not \xHH hex character constants. These do not provoke an error unfortunately, instead are silently treated as an "x". The following induces an error, until -std is added to get proper ANSI mode. Curiously \x00 != x always comes out true, for an array size at least. It is necessary to write \x00 == 0 to get something that is true only with -std. */ int osf4_cc_array ['\''\x00'\'' == 0 ? 1 : -1]; /* IBM C 6 for AIX is almost-ANSI by default, but it replaces macro parameters inside strings and character constants. */ #define FOO(x) '\''x'\'' int xlc6_cc_array[FOO(a) == '\''x'\'' ? 1 : -1]; int test (int i, double x); struct s1 {int (*f) (int a);}; struct s2 {int (*f) (double a);}; int pairnames (int, char **, int *(*)(struct buf *, struct stat *, int), int, int);' # Test code for whether the C compiler supports C89 (body of main). ac_c_conftest_c89_main=' ok |= (argc == 0 || f (e, argv, 0) != argv[0] || f (e, argv, 1) != argv[1]); ' # Test code for whether the C compiler supports C99 (global declarations) ac_c_conftest_c99_globals=' /* Does the compiler advertise C99 conformance? */ #if !defined __STDC_VERSION__ || __STDC_VERSION__ < 199901L # error "Compiler does not advertise C99 conformance" #endif // See if C++-style comments work. #include extern int puts (const char *); extern int printf (const char *, ...); extern int dprintf (int, const char *, ...); extern void *malloc (size_t); extern void free (void *); // Check varargs macros. These examples are taken from C99 6.10.3.5. // dprintf is used instead of fprintf to avoid needing to declare // FILE and stderr. #define debug(...) dprintf (2, __VA_ARGS__) #define showlist(...) puts (#__VA_ARGS__) #define report(test,...) ((test) ? puts (#test) : printf (__VA_ARGS__)) static void test_varargs_macros (void) { int x = 1234; int y = 5678; debug ("Flag"); debug ("X = %d\n", x); showlist (The first, second, and third items.); report (x>y, "x is %d but y is %d", x, y); } // Check long long types. #define BIG64 18446744073709551615ull #define BIG32 4294967295ul #define BIG_OK (BIG64 / BIG32 == 4294967297ull && BIG64 % BIG32 == 0) #if !BIG_OK #error "your preprocessor is broken" #endif #if BIG_OK #else #error "your preprocessor is broken" #endif static long long int bignum = -9223372036854775807LL; static unsigned long long int ubignum = BIG64; struct incomplete_array { int datasize; double data[]; }; struct named_init { int number; const wchar_t *name; double average; }; typedef const char *ccp; static inline int test_restrict (ccp restrict text) { // Iterate through items via the restricted pointer. // Also check for declarations in for loops. for (unsigned int i = 0; *(text+i) != '\''\0'\''; ++i) continue; return 0; } // Check varargs and va_copy. static bool test_varargs (const char *format, ...) { va_list args; va_start (args, format); va_list args_copy; va_copy (args_copy, args); const char *str = ""; int number = 0; float fnumber = 0; while (*format) { switch (*format++) { case '\''s'\'': // string str = va_arg (args_copy, const char *); break; case '\''d'\'': // int number = va_arg (args_copy, int); break; case '\''f'\'': // float fnumber = va_arg (args_copy, double); break; default: break; } } va_end (args_copy); va_end (args); return *str && number && fnumber; } ' # Test code for whether the C compiler supports C99 (body of main). ac_c_conftest_c99_main=' // Check bool. _Bool success = false; success |= (argc != 0); // Check restrict. if (test_restrict ("String literal") == 0) success = true; char *restrict newvar = "Another string"; // Check varargs. success &= test_varargs ("s, d'\'' f .", "string", 65, 34.234); test_varargs_macros (); // Check flexible array members. struct incomplete_array *ia = malloc (sizeof (struct incomplete_array) + (sizeof (double) * 10)); ia->datasize = 10; for (int i = 0; i < ia->datasize; ++i) ia->data[i] = i * 1.234; // Work around memory leak warnings. free (ia); // Check named initializers. struct named_init ni = { .number = 34, .name = L"Test wide string", .average = 543.34343, }; ni.number = 58; int dynamic_array[ni.number]; dynamic_array[0] = argv[0][0]; dynamic_array[ni.number - 1] = 543; // work around unused variable warnings ok |= (!success || bignum == 0LL || ubignum == 0uLL || newvar[0] == '\''x'\'' || dynamic_array[ni.number - 1] != 543); ' # Test code for whether the C compiler supports C11 (global declarations) ac_c_conftest_c11_globals=' /* Does the compiler advertise C11 conformance? */ #if !defined __STDC_VERSION__ || __STDC_VERSION__ < 201112L # error "Compiler does not advertise C11 conformance" #endif // Check _Alignas. char _Alignas (double) aligned_as_double; char _Alignas (0) no_special_alignment; extern char aligned_as_int; char _Alignas (0) _Alignas (int) aligned_as_int; // Check _Alignof. enum { int_alignment = _Alignof (int), int_array_alignment = _Alignof (int[100]), char_alignment = _Alignof (char) }; _Static_assert (0 < -_Alignof (int), "_Alignof is signed"); // Check _Noreturn. int _Noreturn does_not_return (void) { for (;;) continue; } // Check _Static_assert. struct test_static_assert { int x; _Static_assert (sizeof (int) <= sizeof (long int), "_Static_assert does not work in struct"); long int y; }; // Check UTF-8 literals. #define u8 syntax error! char const utf8_literal[] = u8"happens to be ASCII" "another string"; // Check duplicate typedefs. typedef long *long_ptr; typedef long int *long_ptr; typedef long_ptr long_ptr; // Anonymous structures and unions -- taken from C11 6.7.2.1 Example 1. struct anonymous { union { struct { int i; int j; }; struct { int k; long int l; } w; }; int m; } v1; ' # Test code for whether the C compiler supports C11 (body of main). ac_c_conftest_c11_main=' _Static_assert ((offsetof (struct anonymous, i) == offsetof (struct anonymous, w.k)), "Anonymous union alignment botch"); v1.i = 2; v1.w.k = 5; ok |= v1.i != 5; ' # Test code for whether the C compiler supports C11 (complete). ac_c_conftest_c11_program="${ac_c_conftest_c89_globals} ${ac_c_conftest_c99_globals} ${ac_c_conftest_c11_globals} int main (int argc, char **argv) { int ok = 0; ${ac_c_conftest_c89_main} ${ac_c_conftest_c99_main} ${ac_c_conftest_c11_main} return ok; } " # Test code for whether the C compiler supports C99 (complete). ac_c_conftest_c99_program="${ac_c_conftest_c89_globals} ${ac_c_conftest_c99_globals} int main (int argc, char **argv) { int ok = 0; ${ac_c_conftest_c89_main} ${ac_c_conftest_c99_main} return ok; } " # Test code for whether the C compiler supports C89 (complete). ac_c_conftest_c89_program="${ac_c_conftest_c89_globals} int main (int argc, char **argv) { int ok = 0; ${ac_c_conftest_c89_main} return ok; } " as_fn_append ac_header_c_list " stdio.h stdio_h HAVE_STDIO_H" as_fn_append ac_header_c_list " stdlib.h stdlib_h HAVE_STDLIB_H" as_fn_append ac_header_c_list " string.h string_h HAVE_STRING_H" as_fn_append ac_header_c_list " inttypes.h inttypes_h HAVE_INTTYPES_H" as_fn_append ac_header_c_list " stdint.h stdint_h HAVE_STDINT_H" as_fn_append ac_header_c_list " strings.h strings_h HAVE_STRINGS_H" as_fn_append ac_header_c_list " sys/stat.h sys_stat_h HAVE_SYS_STAT_H" as_fn_append ac_header_c_list " sys/types.h sys_types_h HAVE_SYS_TYPES_H" as_fn_append ac_header_c_list " unistd.h unistd_h HAVE_UNISTD_H" # Auxiliary files required by this configure script. ac_aux_files="config.guess config.sub ltmain.sh compile missing install-sh" # Locations in which to look for auxiliary files. ac_aux_dir_candidates="${srcdir}${PATH_SEPARATOR}${srcdir}/..${PATH_SEPARATOR}${srcdir}/../.." # Search for a directory containing all of the required auxiliary files, # $ac_aux_files, from the $PATH-style list $ac_aux_dir_candidates. # If we don't find one directory that contains all the files we need, # we report the set of missing files from the *first* directory in # $ac_aux_dir_candidates and give up. ac_missing_aux_files="" ac_first_candidate=: printf "%s\n" "$as_me:${as_lineno-$LINENO}: looking for aux files: $ac_aux_files" >&5 as_save_IFS=$IFS; IFS=$PATH_SEPARATOR as_found=false for as_dir in $ac_aux_dir_candidates do IFS=$as_save_IFS case $as_dir in #((( '') as_dir=./ ;; */) ;; *) as_dir=$as_dir/ ;; esac as_found=: printf "%s\n" "$as_me:${as_lineno-$LINENO}: trying $as_dir" >&5 ac_aux_dir_found=yes ac_install_sh= for ac_aux in $ac_aux_files do # As a special case, if "install-sh" is required, that requirement # can be satisfied by any of "install-sh", "install.sh", or "shtool", # and $ac_install_sh is set appropriately for whichever one is found. if test x"$ac_aux" = x"install-sh" then if test -f "${as_dir}install-sh"; then printf "%s\n" "$as_me:${as_lineno-$LINENO}: ${as_dir}install-sh found" >&5 ac_install_sh="${as_dir}install-sh -c" elif test -f "${as_dir}install.sh"; then printf "%s\n" "$as_me:${as_lineno-$LINENO}: ${as_dir}install.sh found" >&5 ac_install_sh="${as_dir}install.sh -c" elif test -f "${as_dir}shtool"; then printf "%s\n" "$as_me:${as_lineno-$LINENO}: ${as_dir}shtool found" >&5 ac_install_sh="${as_dir}shtool install -c" else ac_aux_dir_found=no if $ac_first_candidate; then ac_missing_aux_files="${ac_missing_aux_files} install-sh" else break fi fi else if test -f "${as_dir}${ac_aux}"; then printf "%s\n" "$as_me:${as_lineno-$LINENO}: ${as_dir}${ac_aux} found" >&5 else ac_aux_dir_found=no if $ac_first_candidate; then ac_missing_aux_files="${ac_missing_aux_files} ${ac_aux}" else break fi fi fi done if test "$ac_aux_dir_found" = yes; then ac_aux_dir="$as_dir" break fi ac_first_candidate=false as_found=false done IFS=$as_save_IFS if $as_found then : else case e in #( e) as_fn_error $? "cannot find required auxiliary files:$ac_missing_aux_files" "$LINENO" 5 ;; esac fi # These three variables are undocumented and unsupported, # and are intended to be withdrawn in a future Autoconf release. # They can cause serious problems if a builder's source tree is in a directory # whose full name contains unusual characters. if test -f "${ac_aux_dir}config.guess"; then ac_config_guess="$SHELL ${ac_aux_dir}config.guess" fi if test -f "${ac_aux_dir}config.sub"; then ac_config_sub="$SHELL ${ac_aux_dir}config.sub" fi if test -f "$ac_aux_dir/configure"; then ac_configure="$SHELL ${ac_aux_dir}configure" fi # Check that the precious variables saved in the cache have kept the same # value. ac_cache_corrupted=false for ac_var in $ac_precious_vars; do eval ac_old_set=\$ac_cv_env_${ac_var}_set eval ac_new_set=\$ac_env_${ac_var}_set eval ac_old_val=\$ac_cv_env_${ac_var}_value eval ac_new_val=\$ac_env_${ac_var}_value case $ac_old_set,$ac_new_set in set,) { printf "%s\n" "$as_me:${as_lineno-$LINENO}: error: '$ac_var' was set to '$ac_old_val' in the previous run" >&5 printf "%s\n" "$as_me: error: '$ac_var' was set to '$ac_old_val' in the previous run" >&2;} ac_cache_corrupted=: ;; ,set) { printf "%s\n" "$as_me:${as_lineno-$LINENO}: error: '$ac_var' was not set in the previous run" >&5 printf "%s\n" "$as_me: error: '$ac_var' was not set in the previous run" >&2;} ac_cache_corrupted=: ;; ,);; *) if test "x$ac_old_val" != "x$ac_new_val"; then # differences in whitespace do not lead to failure. ac_old_val_w=`echo x $ac_old_val` ac_new_val_w=`echo x $ac_new_val` if test "$ac_old_val_w" != "$ac_new_val_w"; then { printf "%s\n" "$as_me:${as_lineno-$LINENO}: error: '$ac_var' has changed since the previous run:" >&5 printf "%s\n" "$as_me: error: '$ac_var' has changed since the previous run:" >&2;} ac_cache_corrupted=: else { printf "%s\n" "$as_me:${as_lineno-$LINENO}: warning: ignoring whitespace changes in '$ac_var' since the previous run:" >&5 printf "%s\n" "$as_me: warning: ignoring whitespace changes in '$ac_var' since the previous run:" >&2;} eval $ac_var=\$ac_old_val fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: former value: '$ac_old_val'" >&5 printf "%s\n" "$as_me: former value: '$ac_old_val'" >&2;} { printf "%s\n" "$as_me:${as_lineno-$LINENO}: current value: '$ac_new_val'" >&5 printf "%s\n" "$as_me: current value: '$ac_new_val'" >&2;} fi;; esac # Pass precious variables to config.status. if test "$ac_new_set" = set; then case $ac_new_val in *\'*) ac_arg=$ac_var=`printf "%s\n" "$ac_new_val" | sed "s/'/'\\\\\\\\''/g"` ;; *) ac_arg=$ac_var=$ac_new_val ;; esac case " $ac_configure_args " in *" '$ac_arg' "*) ;; # Avoid dups. Use of quotes ensures accuracy. *) as_fn_append ac_configure_args " '$ac_arg'" ;; esac fi done if $ac_cache_corrupted; then { printf "%s\n" "$as_me:${as_lineno-$LINENO}: error: in '$ac_pwd':" >&5 printf "%s\n" "$as_me: error: in '$ac_pwd':" >&2;} { printf "%s\n" "$as_me:${as_lineno-$LINENO}: error: changes in the environment can compromise the build" >&5 printf "%s\n" "$as_me: error: changes in the environment can compromise the build" >&2;} as_fn_error $? "run '${MAKE-make} distclean' and/or 'rm $cache_file' and start over" "$LINENO" 5 fi ## -------------------- ## ## Main body of script. ## ## -------------------- ## ac_ext=c ac_cpp='$CPP $CPPFLAGS' ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' ac_compiler_gnu=$ac_cv_c_compiler_gnu am__api_version='1.16' # Find a good install program. We prefer a C program (faster), # so one script is as good as another. But avoid the broken or # incompatible versions: # SysV /etc/install, /usr/sbin/install # SunOS /usr/etc/install # IRIX /sbin/install # AIX /bin/install # AmigaOS /C/install, which installs bootblocks on floppy discs # AIX 4 /usr/bin/installbsd, which doesn't work without a -g flag # AFS /usr/afsws/bin/install, which mishandles nonexistent args # SVR4 /usr/ucb/install, which tries to use the nonexistent group "staff" # OS/2's system install, which has a completely different semantic # ./install, which can be erroneously created by make from ./install.sh. # Reject install programs that cannot install multiple files. { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for a BSD-compatible install" >&5 printf %s "checking for a BSD-compatible install... " >&6; } if test -z "$INSTALL"; then if test ${ac_cv_path_install+y} then : printf %s "(cached) " >&6 else case e in #( e) as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS case $as_dir in #((( '') as_dir=./ ;; */) ;; *) as_dir=$as_dir/ ;; esac # Account for fact that we put trailing slashes in our PATH walk. case $as_dir in #(( ./ | /[cC]/* | \ /etc/* | /usr/sbin/* | /usr/etc/* | /sbin/* | /usr/afsws/bin/* | \ ?:[\\/]os2[\\/]install[\\/]* | ?:[\\/]OS2[\\/]INSTALL[\\/]* | \ /usr/ucb/* ) ;; *) # OSF1 and SCO ODT 3.0 have their own names for install. # Don't use installbsd from OSF since it installs stuff as root # by default. for ac_prog in ginstall scoinst install; do for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir$ac_prog$ac_exec_ext"; then if test $ac_prog = install && grep dspmsg "$as_dir$ac_prog$ac_exec_ext" >/dev/null 2>&1; then # AIX install. It has an incompatible calling convention. : elif test $ac_prog = install && grep pwplus "$as_dir$ac_prog$ac_exec_ext" >/dev/null 2>&1; then # program-specific install script used by HP pwplus--don't use. : else rm -rf conftest.one conftest.two conftest.dir echo one > conftest.one echo two > conftest.two mkdir conftest.dir if "$as_dir$ac_prog$ac_exec_ext" -c conftest.one conftest.two "`pwd`/conftest.dir/" && test -s conftest.one && test -s conftest.two && test -s conftest.dir/conftest.one && test -s conftest.dir/conftest.two then ac_cv_path_install="$as_dir$ac_prog$ac_exec_ext -c" break 3 fi fi fi done done ;; esac done IFS=$as_save_IFS rm -rf conftest.one conftest.two conftest.dir ;; esac fi if test ${ac_cv_path_install+y}; then INSTALL=$ac_cv_path_install else # As a last resort, use the slow shell script. Don't cache a # value for INSTALL within a source directory, because that will # break other packages using the cache if that directory is # removed, or if the value is a relative name. INSTALL=$ac_install_sh fi fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $INSTALL" >&5 printf "%s\n" "$INSTALL" >&6; } # Use test -z because SunOS4 sh mishandles braces in ${var-val}. # It thinks the first close brace ends the variable substitution. test -z "$INSTALL_PROGRAM" && INSTALL_PROGRAM='${INSTALL}' test -z "$INSTALL_SCRIPT" && INSTALL_SCRIPT='${INSTALL}' test -z "$INSTALL_DATA" && INSTALL_DATA='${INSTALL} -m 644' { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking whether build environment is sane" >&5 printf %s "checking whether build environment is sane... " >&6; } # Reject unsafe characters in $srcdir or the absolute working directory # name. Accept space and tab only in the latter. am_lf=' ' case `pwd` in *[\\\"\#\$\&\'\`$am_lf]*) as_fn_error $? "unsafe absolute working directory name" "$LINENO" 5;; esac case $srcdir in *[\\\"\#\$\&\'\`$am_lf\ \ ]*) as_fn_error $? "unsafe srcdir value: '$srcdir'" "$LINENO" 5;; esac # Do 'set' in a subshell so we don't clobber the current shell's # arguments. Must try -L first in case configure is actually a # symlink; some systems play weird games with the mod time of symlinks # (eg FreeBSD returns the mod time of the symlink's containing # directory). if ( am_has_slept=no for am_try in 1 2; do echo "timestamp, slept: $am_has_slept" > conftest.file set X `ls -Lt "$srcdir/configure" conftest.file 2> /dev/null` if test "$*" = "X"; then # -L didn't work. set X `ls -t "$srcdir/configure" conftest.file` fi if test "$*" != "X $srcdir/configure conftest.file" \ && test "$*" != "X conftest.file $srcdir/configure"; then # If neither matched, then we have a broken ls. This can happen # if, for instance, CONFIG_SHELL is bash and it inherits a # broken ls alias from the environment. This has actually # happened. Such a system could not be considered "sane". as_fn_error $? "ls -t appears to fail. Make sure there is not a broken alias in your environment" "$LINENO" 5 fi if test "$2" = conftest.file || test $am_try -eq 2; then break fi # Just in case. sleep 1 am_has_slept=yes done test "$2" = conftest.file ) then # Ok. : else as_fn_error $? "newly created file is older than distributed files! Check your system clock" "$LINENO" 5 fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: yes" >&5 printf "%s\n" "yes" >&6; } # If we didn't sleep, we still need to ensure time stamps of config.status and # generated files are strictly newer. am_sleep_pid= if grep 'slept: no' conftest.file >/dev/null 2>&1; then ( sleep 1 ) & am_sleep_pid=$! fi rm -f conftest.file test "$program_prefix" != NONE && program_transform_name="s&^&$program_prefix&;$program_transform_name" # Use a double $ so make ignores it. test "$program_suffix" != NONE && program_transform_name="s&\$&$program_suffix&;$program_transform_name" # Double any \ or $. # By default was 's,x,x', remove it if useless. ac_script='s/[\\$]/&&/g;s/;s,x,x,$//' program_transform_name=`printf "%s\n" "$program_transform_name" | sed "$ac_script"` # Expand $ac_aux_dir to an absolute path. am_aux_dir=`cd "$ac_aux_dir" && pwd` if test x"${MISSING+set}" != xset; then MISSING="\${SHELL} '$am_aux_dir/missing'" fi # Use eval to expand $SHELL if eval "$MISSING --is-lightweight"; then am_missing_run="$MISSING " else am_missing_run= { printf "%s\n" "$as_me:${as_lineno-$LINENO}: WARNING: 'missing' script is too old or missing" >&5 printf "%s\n" "$as_me: WARNING: 'missing' script is too old or missing" >&2;} fi if test x"${install_sh+set}" != xset; then case $am_aux_dir in *\ * | *\ *) install_sh="\${SHELL} '$am_aux_dir/install-sh'" ;; *) install_sh="\${SHELL} $am_aux_dir/install-sh" esac fi # Installed binaries are usually stripped using 'strip' when the user # run "make install-strip". However 'strip' might not be the right # tool to use in cross-compilation environments, therefore Automake # will honor the 'STRIP' environment variable to overrule this program. if test "$cross_compiling" != no; then if test -n "$ac_tool_prefix"; then # Extract the first word of "${ac_tool_prefix}strip", so it can be a program name with args. set dummy ${ac_tool_prefix}strip; ac_word=$2 { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 printf %s "checking for $ac_word... " >&6; } if test ${ac_cv_prog_STRIP+y} then : printf %s "(cached) " >&6 else case e in #( e) if test -n "$STRIP"; then ac_cv_prog_STRIP="$STRIP" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS case $as_dir in #((( '') as_dir=./ ;; */) ;; *) as_dir=$as_dir/ ;; esac for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir$ac_word$ac_exec_ext"; then ac_cv_prog_STRIP="${ac_tool_prefix}strip" printf "%s\n" "$as_me:${as_lineno-$LINENO}: found $as_dir$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi ;; esac fi STRIP=$ac_cv_prog_STRIP if test -n "$STRIP"; then { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $STRIP" >&5 printf "%s\n" "$STRIP" >&6; } else { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 printf "%s\n" "no" >&6; } fi fi if test -z "$ac_cv_prog_STRIP"; then ac_ct_STRIP=$STRIP # Extract the first word of "strip", so it can be a program name with args. set dummy strip; ac_word=$2 { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 printf %s "checking for $ac_word... " >&6; } if test ${ac_cv_prog_ac_ct_STRIP+y} then : printf %s "(cached) " >&6 else case e in #( e) if test -n "$ac_ct_STRIP"; then ac_cv_prog_ac_ct_STRIP="$ac_ct_STRIP" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS case $as_dir in #((( '') as_dir=./ ;; */) ;; *) as_dir=$as_dir/ ;; esac for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir$ac_word$ac_exec_ext"; then ac_cv_prog_ac_ct_STRIP="strip" printf "%s\n" "$as_me:${as_lineno-$LINENO}: found $as_dir$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi ;; esac fi ac_ct_STRIP=$ac_cv_prog_ac_ct_STRIP if test -n "$ac_ct_STRIP"; then { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_ct_STRIP" >&5 printf "%s\n" "$ac_ct_STRIP" >&6; } else { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 printf "%s\n" "no" >&6; } fi if test "x$ac_ct_STRIP" = x; then STRIP=":" else case $cross_compiling:$ac_tool_warned in yes:) { printf "%s\n" "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 printf "%s\n" "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} ac_tool_warned=yes ;; esac STRIP=$ac_ct_STRIP fi else STRIP="$ac_cv_prog_STRIP" fi fi INSTALL_STRIP_PROGRAM="\$(install_sh) -c -s" { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for a race-free mkdir -p" >&5 printf %s "checking for a race-free mkdir -p... " >&6; } if test -z "$MKDIR_P"; then if test ${ac_cv_path_mkdir+y} then : printf %s "(cached) " >&6 else case e in #( e) as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH$PATH_SEPARATOR/opt/sfw/bin do IFS=$as_save_IFS case $as_dir in #((( '') as_dir=./ ;; */) ;; *) as_dir=$as_dir/ ;; esac for ac_prog in mkdir gmkdir; do for ac_exec_ext in '' $ac_executable_extensions; do as_fn_executable_p "$as_dir$ac_prog$ac_exec_ext" || continue case `"$as_dir$ac_prog$ac_exec_ext" --version 2>&1` in #( 'mkdir ('*'coreutils) '* | \ *'BusyBox '* | \ 'mkdir (fileutils) '4.1*) ac_cv_path_mkdir=$as_dir$ac_prog$ac_exec_ext break 3;; esac done done done IFS=$as_save_IFS ;; esac fi test -d ./--version && rmdir ./--version if test ${ac_cv_path_mkdir+y}; then MKDIR_P="$ac_cv_path_mkdir -p" else # As a last resort, use plain mkdir -p, # in the hope it doesn't have the bugs of ancient mkdir. MKDIR_P='mkdir -p' fi fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $MKDIR_P" >&5 printf "%s\n" "$MKDIR_P" >&6; } for ac_prog in gawk mawk nawk awk do # Extract the first word of "$ac_prog", so it can be a program name with args. set dummy $ac_prog; ac_word=$2 { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 printf %s "checking for $ac_word... " >&6; } if test ${ac_cv_prog_AWK+y} then : printf %s "(cached) " >&6 else case e in #( e) if test -n "$AWK"; then ac_cv_prog_AWK="$AWK" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS case $as_dir in #((( '') as_dir=./ ;; */) ;; *) as_dir=$as_dir/ ;; esac for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir$ac_word$ac_exec_ext"; then ac_cv_prog_AWK="$ac_prog" printf "%s\n" "$as_me:${as_lineno-$LINENO}: found $as_dir$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi ;; esac fi AWK=$ac_cv_prog_AWK if test -n "$AWK"; then { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $AWK" >&5 printf "%s\n" "$AWK" >&6; } else { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 printf "%s\n" "no" >&6; } fi test -n "$AWK" && break done { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking whether ${MAKE-make} sets \$(MAKE)" >&5 printf %s "checking whether ${MAKE-make} sets \$(MAKE)... " >&6; } set x ${MAKE-make} ac_make=`printf "%s\n" "$2" | sed 's/+/p/g; s/[^a-zA-Z0-9_]/_/g'` if eval test \${ac_cv_prog_make_${ac_make}_set+y} then : printf %s "(cached) " >&6 else case e in #( e) cat >conftest.make <<\_ACEOF SHELL = /bin/sh all: @echo '@@@%%%=$(MAKE)=@@@%%%' _ACEOF # GNU make sometimes prints "make[1]: Entering ...", which would confuse us. case `${MAKE-make} -f conftest.make 2>/dev/null` in *@@@%%%=?*=@@@%%%*) eval ac_cv_prog_make_${ac_make}_set=yes;; *) eval ac_cv_prog_make_${ac_make}_set=no;; esac rm -f conftest.make ;; esac fi if eval test \$ac_cv_prog_make_${ac_make}_set = yes; then { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: yes" >&5 printf "%s\n" "yes" >&6; } SET_MAKE= else { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 printf "%s\n" "no" >&6; } SET_MAKE="MAKE=${MAKE-make}" fi rm -rf .tst 2>/dev/null mkdir .tst 2>/dev/null if test -d .tst; then am__leading_dot=. else am__leading_dot=_ fi rmdir .tst 2>/dev/null # Check whether --enable-silent-rules was given. if test ${enable_silent_rules+y} then : enableval=$enable_silent_rules; fi case $enable_silent_rules in # ((( yes) AM_DEFAULT_VERBOSITY=0;; no) AM_DEFAULT_VERBOSITY=1;; *) AM_DEFAULT_VERBOSITY=1;; esac am_make=${MAKE-make} { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking whether $am_make supports nested variables" >&5 printf %s "checking whether $am_make supports nested variables... " >&6; } if test ${am_cv_make_support_nested_variables+y} then : printf %s "(cached) " >&6 else case e in #( e) if printf "%s\n" 'TRUE=$(BAR$(V)) BAR0=false BAR1=true V=1 am__doit: @$(TRUE) .PHONY: am__doit' | $am_make -f - >/dev/null 2>&1; then am_cv_make_support_nested_variables=yes else am_cv_make_support_nested_variables=no fi ;; esac fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $am_cv_make_support_nested_variables" >&5 printf "%s\n" "$am_cv_make_support_nested_variables" >&6; } if test $am_cv_make_support_nested_variables = yes; then AM_V='$(V)' AM_DEFAULT_V='$(AM_DEFAULT_VERBOSITY)' else AM_V=$AM_DEFAULT_VERBOSITY AM_DEFAULT_V=$AM_DEFAULT_VERBOSITY fi AM_BACKSLASH='\' if test "`cd $srcdir && pwd`" != "`pwd`"; then # Use -I$(srcdir) only when $(srcdir) != ., so that make's output # is not polluted with repeated "-I." am__isrc=' -I$(srcdir)' # test to see if srcdir already configured if test -f $srcdir/config.status; then as_fn_error $? "source directory already configured; run \"make distclean\" there first" "$LINENO" 5 fi fi # test whether we have cygpath if test -z "$CYGPATH_W"; then if (cygpath --version) >/dev/null 2>/dev/null; then CYGPATH_W='cygpath -w' else CYGPATH_W=echo fi fi # Define the identity of the package. PACKAGE='bluez' VERSION='5.82' printf "%s\n" "#define PACKAGE \"$PACKAGE\"" >>confdefs.h printf "%s\n" "#define VERSION \"$VERSION\"" >>confdefs.h # Some tools Automake needs. ACLOCAL=${ACLOCAL-"${am_missing_run}aclocal-${am__api_version}"} AUTOCONF=${AUTOCONF-"${am_missing_run}autoconf"} AUTOMAKE=${AUTOMAKE-"${am_missing_run}automake-${am__api_version}"} AUTOHEADER=${AUTOHEADER-"${am_missing_run}autoheader"} MAKEINFO=${MAKEINFO-"${am_missing_run}makeinfo"} # For better backward compatibility. To be removed once Automake 1.9.x # dies out for good. For more background, see: # # mkdir_p='$(MKDIR_P)' # We need awk for the "check" target (and possibly the TAP driver). The # system "awk" is bad on some platforms. # Always define AMTAR for backward compatibility. Yes, it's still used # in the wild :-( We should find a proper way to deprecate it ... AMTAR='$${TAR-tar}' # We'll loop over all known methods to create a tar archive until one works. _am_tools='gnutar pax cpio none' { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking how to create a pax tar archive" >&5 printf %s "checking how to create a pax tar archive... " >&6; } # Go ahead even if we have the value already cached. We do so because we # need to set the values for the 'am__tar' and 'am__untar' variables. _am_tools=${am_cv_prog_tar_pax-$_am_tools} for _am_tool in $_am_tools; do case $_am_tool in gnutar) for _am_tar in tar gnutar gtar; do { echo "$as_me:$LINENO: $_am_tar --version" >&5 ($_am_tar --version) >&5 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } && break done am__tar="$_am_tar --format=posix -chf - "'"$$tardir"' am__tar_="$_am_tar --format=posix -chf - "'"$tardir"' am__untar="$_am_tar -xf -" ;; plaintar) # Must skip GNU tar: if it does not support --format= it doesn't create # ustar tarball either. (tar --version) >/dev/null 2>&1 && continue am__tar='tar chf - "$$tardir"' am__tar_='tar chf - "$tardir"' am__untar='tar xf -' ;; pax) am__tar='pax -L -x pax -w "$$tardir"' am__tar_='pax -L -x pax -w "$tardir"' am__untar='pax -r' ;; cpio) am__tar='find "$$tardir" -print | cpio -o -H pax -L' am__tar_='find "$tardir" -print | cpio -o -H pax -L' am__untar='cpio -i -H pax -d' ;; none) am__tar=false am__tar_=false am__untar=false ;; esac # If the value was cached, stop now. We just wanted to have am__tar # and am__untar set. test -n "${am_cv_prog_tar_pax}" && break # tar/untar a dummy directory, and stop if the command works. rm -rf conftest.dir mkdir conftest.dir echo GrepMe > conftest.dir/file { echo "$as_me:$LINENO: tardir=conftest.dir && eval $am__tar_ >conftest.tar" >&5 (tardir=conftest.dir && eval $am__tar_ >conftest.tar) >&5 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } rm -rf conftest.dir if test -s conftest.tar; then { echo "$as_me:$LINENO: $am__untar &5 ($am__untar &5 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } { echo "$as_me:$LINENO: cat conftest.dir/file" >&5 (cat conftest.dir/file) >&5 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } grep GrepMe conftest.dir/file >/dev/null 2>&1 && break fi done rm -rf conftest.dir if test ${am_cv_prog_tar_pax+y} then : printf %s "(cached) " >&6 else case e in #( e) am_cv_prog_tar_pax=$_am_tool ;; esac fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $am_cv_prog_tar_pax" >&5 printf "%s\n" "$am_cv_prog_tar_pax" >&6; } # Variables for tags utilities; see am/tags.am if test -z "$CTAGS"; then CTAGS=ctags fi if test -z "$ETAGS"; then ETAGS=etags fi if test -z "$CSCOPE"; then CSCOPE=cscope fi # POSIX will say in a future version that running "rm -f" with no argument # is OK; and we want to be able to make that assumption in our Makefile # recipes. So use an aggressive probe to check that the usage we want is # actually supported "in the wild" to an acceptable degree. # See automake bug#10828. # To make any issue more visible, cause the running configure to be aborted # by default if the 'rm' program in use doesn't match our expectations; the # user can still override this though. if rm -f && rm -fr && rm -rf; then : OK; else cat >&2 <<'END' Oops! Your 'rm' program seems unable to run without file operands specified on the command line, even when the '-f' option is present. This is contrary to the behaviour of most rm programs out there, and not conforming with the upcoming POSIX standard: Please tell bug-automake@gnu.org about your system, including the value of your $PATH and any error possibly output before this message. This can help us improve future automake versions. END if test x"$ACCEPT_INFERIOR_RM_PROGRAM" = x"yes"; then echo 'Configuration will proceed anyway, since you have set the' >&2 echo 'ACCEPT_INFERIOR_RM_PROGRAM variable to "yes"' >&2 echo >&2 else cat >&2 <<'END' Aborting the configuration process, to ensure you take notice of the issue. You can download and install GNU coreutils to get an 'rm' implementation that behaves properly: . If you want to complete the configuration process using your problematic 'rm' anyway, export the environment variable ACCEPT_INFERIOR_RM_PROGRAM to "yes", and re-run configure. END as_fn_error $? "Your 'rm' program is bad, sorry." "$LINENO" 5 fi fi ac_config_headers="$ac_config_headers config.h" # Check whether --enable-silent-rules was given. if test ${enable_silent_rules+y} then : enableval=$enable_silent_rules; fi case $enable_silent_rules in # ((( yes) AM_DEFAULT_VERBOSITY=0;; no) AM_DEFAULT_VERBOSITY=1;; *) AM_DEFAULT_VERBOSITY=0;; esac am_make=${MAKE-make} { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking whether $am_make supports nested variables" >&5 printf %s "checking whether $am_make supports nested variables... " >&6; } if test ${am_cv_make_support_nested_variables+y} then : printf %s "(cached) " >&6 else case e in #( e) if printf "%s\n" 'TRUE=$(BAR$(V)) BAR0=false BAR1=true V=1 am__doit: @$(TRUE) .PHONY: am__doit' | $am_make -f - >/dev/null 2>&1; then am_cv_make_support_nested_variables=yes else am_cv_make_support_nested_variables=no fi ;; esac fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $am_cv_make_support_nested_variables" >&5 printf "%s\n" "$am_cv_make_support_nested_variables" >&6; } if test $am_cv_make_support_nested_variables = yes; then AM_V='$(V)' AM_DEFAULT_V='$(AM_DEFAULT_VERBOSITY)' else AM_V=$AM_DEFAULT_VERBOSITY AM_DEFAULT_V=$AM_DEFAULT_VERBOSITY fi AM_BACKSLASH='\' { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking whether to enable maintainer-specific portions of Makefiles" >&5 printf %s "checking whether to enable maintainer-specific portions of Makefiles... " >&6; } # Check whether --enable-maintainer-mode was given. if test ${enable_maintainer_mode+y} then : enableval=$enable_maintainer_mode; USE_MAINTAINER_MODE=$enableval else case e in #( e) USE_MAINTAINER_MODE=no ;; esac fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $USE_MAINTAINER_MODE" >&5 printf "%s\n" "$USE_MAINTAINER_MODE" >&6; } if test $USE_MAINTAINER_MODE = yes; then MAINTAINER_MODE_TRUE= MAINTAINER_MODE_FALSE='#' else MAINTAINER_MODE_TRUE='#' MAINTAINER_MODE_FALSE= fi MAINT=$MAINTAINER_MODE_TRUE if test "x$ac_cv_env_PKG_CONFIG_set" != "xset"; then if test -n "$ac_tool_prefix"; then # Extract the first word of "${ac_tool_prefix}pkg-config", so it can be a program name with args. set dummy ${ac_tool_prefix}pkg-config; ac_word=$2 { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 printf %s "checking for $ac_word... " >&6; } if test ${ac_cv_path_PKG_CONFIG+y} then : printf %s "(cached) " >&6 else case e in #( e) case $PKG_CONFIG in [\\/]* | ?:[\\/]*) ac_cv_path_PKG_CONFIG="$PKG_CONFIG" # Let the user override the test with a path. ;; *) as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS case $as_dir in #((( '') as_dir=./ ;; */) ;; *) as_dir=$as_dir/ ;; esac for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir$ac_word$ac_exec_ext"; then ac_cv_path_PKG_CONFIG="$as_dir$ac_word$ac_exec_ext" printf "%s\n" "$as_me:${as_lineno-$LINENO}: found $as_dir$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS ;; esac ;; esac fi PKG_CONFIG=$ac_cv_path_PKG_CONFIG if test -n "$PKG_CONFIG"; then { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $PKG_CONFIG" >&5 printf "%s\n" "$PKG_CONFIG" >&6; } else { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 printf "%s\n" "no" >&6; } fi fi if test -z "$ac_cv_path_PKG_CONFIG"; then ac_pt_PKG_CONFIG=$PKG_CONFIG # Extract the first word of "pkg-config", so it can be a program name with args. set dummy pkg-config; ac_word=$2 { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 printf %s "checking for $ac_word... " >&6; } if test ${ac_cv_path_ac_pt_PKG_CONFIG+y} then : printf %s "(cached) " >&6 else case e in #( e) case $ac_pt_PKG_CONFIG in [\\/]* | ?:[\\/]*) ac_cv_path_ac_pt_PKG_CONFIG="$ac_pt_PKG_CONFIG" # Let the user override the test with a path. ;; *) as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS case $as_dir in #((( '') as_dir=./ ;; */) ;; *) as_dir=$as_dir/ ;; esac for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir$ac_word$ac_exec_ext"; then ac_cv_path_ac_pt_PKG_CONFIG="$as_dir$ac_word$ac_exec_ext" printf "%s\n" "$as_me:${as_lineno-$LINENO}: found $as_dir$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS ;; esac ;; esac fi ac_pt_PKG_CONFIG=$ac_cv_path_ac_pt_PKG_CONFIG if test -n "$ac_pt_PKG_CONFIG"; then { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_pt_PKG_CONFIG" >&5 printf "%s\n" "$ac_pt_PKG_CONFIG" >&6; } else { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 printf "%s\n" "no" >&6; } fi if test "x$ac_pt_PKG_CONFIG" = x; then PKG_CONFIG="" else case $cross_compiling:$ac_tool_warned in yes:) { printf "%s\n" "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 printf "%s\n" "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} ac_tool_warned=yes ;; esac PKG_CONFIG=$ac_pt_PKG_CONFIG fi else PKG_CONFIG="$ac_cv_path_PKG_CONFIG" fi fi if test -n "$PKG_CONFIG"; then _pkg_min_version=0.9.0 { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking pkg-config is at least version $_pkg_min_version" >&5 printf %s "checking pkg-config is at least version $_pkg_min_version... " >&6; } if $PKG_CONFIG --atleast-pkgconfig-version $_pkg_min_version; then { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: yes" >&5 printf "%s\n" "yes" >&6; } else { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 printf "%s\n" "no" >&6; } PKG_CONFIG="" fi fi with_cflags="" if (test "$USE_MAINTAINER_MODE" = "yes"); then with_cflags="$with_cflags -Wall -Werror -Wextra" with_cflags="$with_cflags -Wno-unused-parameter" with_cflags="$with_cflags -Wno-missing-field-initializers" with_cflags="$with_cflags -Wdeclaration-after-statement" with_cflags="$with_cflags -Wmissing-declarations" with_cflags="$with_cflags -Wredundant-decls" with_cflags="$with_cflags -Wcast-align" with_cflags="$with_cflags -Wswitch-enum" with_cflags="$with_cflags -Wformat -Wformat-security" with_cflags="$with_cflags -Wstringop-overflow" with_cflags="$with_cflags -DG_DISABLE_DEPRECATED" with_cflags="$with_cflags -DGLIB_VERSION_MIN_REQUIRED=GLIB_VERSION_2_28" with_cflags="$with_cflags -DGLIB_VERSION_MAX_ALLOWED=GLIB_VERSION_2_32" fi WARNING_CFLAGS=$with_cflags ac_ext=c ac_cpp='$CPP $CPPFLAGS' ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' ac_compiler_gnu=$ac_cv_c_compiler_gnu DEPDIR="${am__leading_dot}deps" ac_config_commands="$ac_config_commands depfiles" { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking whether ${MAKE-make} supports the include directive" >&5 printf %s "checking whether ${MAKE-make} supports the include directive... " >&6; } cat > confinc.mk << 'END' am__doit: @echo this is the am__doit target >confinc.out .PHONY: am__doit END am__include="#" am__quote= # BSD make does it like this. echo '.include "confinc.mk" # ignored' > confmf.BSD # Other make implementations (GNU, Solaris 10, AIX) do it like this. echo 'include confinc.mk # ignored' > confmf.GNU _am_result=no for s in GNU BSD; do { echo "$as_me:$LINENO: ${MAKE-make} -f confmf.$s && cat confinc.out" >&5 (${MAKE-make} -f confmf.$s && cat confinc.out) >&5 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } case $?:`cat confinc.out 2>/dev/null` in #( '0:this is the am__doit target') : case $s in #( BSD) : am__include='.include' am__quote='"' ;; #( *) : am__include='include' am__quote='' ;; esac ;; #( *) : ;; esac if test "$am__include" != "#"; then _am_result="yes ($s style)" break fi done rm -f confinc.* confmf.* { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: ${_am_result}" >&5 printf "%s\n" "${_am_result}" >&6; } # Check whether --enable-dependency-tracking was given. if test ${enable_dependency_tracking+y} then : enableval=$enable_dependency_tracking; fi if test "x$enable_dependency_tracking" != xno; then am_depcomp="$ac_aux_dir/depcomp" AMDEPBACKSLASH='\' am__nodep='_no' fi if test "x$enable_dependency_tracking" != xno; then AMDEP_TRUE= AMDEP_FALSE='#' else AMDEP_TRUE='#' AMDEP_FALSE= fi ac_ext=c ac_cpp='$CPP $CPPFLAGS' ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' ac_compiler_gnu=$ac_cv_c_compiler_gnu if test -n "$ac_tool_prefix"; then # Extract the first word of "${ac_tool_prefix}gcc", so it can be a program name with args. set dummy ${ac_tool_prefix}gcc; ac_word=$2 { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 printf %s "checking for $ac_word... " >&6; } if test ${ac_cv_prog_CC+y} then : printf %s "(cached) " >&6 else case e in #( e) if test -n "$CC"; then ac_cv_prog_CC="$CC" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS case $as_dir in #((( '') as_dir=./ ;; */) ;; *) as_dir=$as_dir/ ;; esac for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir$ac_word$ac_exec_ext"; then ac_cv_prog_CC="${ac_tool_prefix}gcc" printf "%s\n" "$as_me:${as_lineno-$LINENO}: found $as_dir$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi ;; esac fi CC=$ac_cv_prog_CC if test -n "$CC"; then { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $CC" >&5 printf "%s\n" "$CC" >&6; } else { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 printf "%s\n" "no" >&6; } fi fi if test -z "$ac_cv_prog_CC"; then ac_ct_CC=$CC # Extract the first word of "gcc", so it can be a program name with args. set dummy gcc; ac_word=$2 { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 printf %s "checking for $ac_word... " >&6; } if test ${ac_cv_prog_ac_ct_CC+y} then : printf %s "(cached) " >&6 else case e in #( e) if test -n "$ac_ct_CC"; then ac_cv_prog_ac_ct_CC="$ac_ct_CC" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS case $as_dir in #((( '') as_dir=./ ;; */) ;; *) as_dir=$as_dir/ ;; esac for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir$ac_word$ac_exec_ext"; then ac_cv_prog_ac_ct_CC="gcc" printf "%s\n" "$as_me:${as_lineno-$LINENO}: found $as_dir$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi ;; esac fi ac_ct_CC=$ac_cv_prog_ac_ct_CC if test -n "$ac_ct_CC"; then { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_ct_CC" >&5 printf "%s\n" "$ac_ct_CC" >&6; } else { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 printf "%s\n" "no" >&6; } fi if test "x$ac_ct_CC" = x; then CC="" else case $cross_compiling:$ac_tool_warned in yes:) { printf "%s\n" "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 printf "%s\n" "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} ac_tool_warned=yes ;; esac CC=$ac_ct_CC fi else CC="$ac_cv_prog_CC" fi if test -z "$CC"; then if test -n "$ac_tool_prefix"; then # Extract the first word of "${ac_tool_prefix}cc", so it can be a program name with args. set dummy ${ac_tool_prefix}cc; ac_word=$2 { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 printf %s "checking for $ac_word... " >&6; } if test ${ac_cv_prog_CC+y} then : printf %s "(cached) " >&6 else case e in #( e) if test -n "$CC"; then ac_cv_prog_CC="$CC" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS case $as_dir in #((( '') as_dir=./ ;; */) ;; *) as_dir=$as_dir/ ;; esac for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir$ac_word$ac_exec_ext"; then ac_cv_prog_CC="${ac_tool_prefix}cc" printf "%s\n" "$as_me:${as_lineno-$LINENO}: found $as_dir$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi ;; esac fi CC=$ac_cv_prog_CC if test -n "$CC"; then { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $CC" >&5 printf "%s\n" "$CC" >&6; } else { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 printf "%s\n" "no" >&6; } fi fi fi if test -z "$CC"; then # Extract the first word of "cc", so it can be a program name with args. set dummy cc; ac_word=$2 { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 printf %s "checking for $ac_word... " >&6; } if test ${ac_cv_prog_CC+y} then : printf %s "(cached) " >&6 else case e in #( e) if test -n "$CC"; then ac_cv_prog_CC="$CC" # Let the user override the test. else ac_prog_rejected=no as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS case $as_dir in #((( '') as_dir=./ ;; */) ;; *) as_dir=$as_dir/ ;; esac for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir$ac_word$ac_exec_ext"; then if test "$as_dir$ac_word$ac_exec_ext" = "/usr/ucb/cc"; then ac_prog_rejected=yes continue fi ac_cv_prog_CC="cc" printf "%s\n" "$as_me:${as_lineno-$LINENO}: found $as_dir$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS if test $ac_prog_rejected = yes; then # We found a bogon in the path, so make sure we never use it. set dummy $ac_cv_prog_CC shift if test $# != 0; then # We chose a different compiler from the bogus one. # However, it has the same basename, so the bogon will be chosen # first if we set CC to just the basename; use the full file name. shift ac_cv_prog_CC="$as_dir$ac_word${1+' '}$@" fi fi fi ;; esac fi CC=$ac_cv_prog_CC if test -n "$CC"; then { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $CC" >&5 printf "%s\n" "$CC" >&6; } else { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 printf "%s\n" "no" >&6; } fi fi if test -z "$CC"; then if test -n "$ac_tool_prefix"; then for ac_prog in cl.exe do # Extract the first word of "$ac_tool_prefix$ac_prog", so it can be a program name with args. set dummy $ac_tool_prefix$ac_prog; ac_word=$2 { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 printf %s "checking for $ac_word... " >&6; } if test ${ac_cv_prog_CC+y} then : printf %s "(cached) " >&6 else case e in #( e) if test -n "$CC"; then ac_cv_prog_CC="$CC" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS case $as_dir in #((( '') as_dir=./ ;; */) ;; *) as_dir=$as_dir/ ;; esac for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir$ac_word$ac_exec_ext"; then ac_cv_prog_CC="$ac_tool_prefix$ac_prog" printf "%s\n" "$as_me:${as_lineno-$LINENO}: found $as_dir$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi ;; esac fi CC=$ac_cv_prog_CC if test -n "$CC"; then { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $CC" >&5 printf "%s\n" "$CC" >&6; } else { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 printf "%s\n" "no" >&6; } fi test -n "$CC" && break done fi if test -z "$CC"; then ac_ct_CC=$CC for ac_prog in cl.exe do # Extract the first word of "$ac_prog", so it can be a program name with args. set dummy $ac_prog; ac_word=$2 { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 printf %s "checking for $ac_word... " >&6; } if test ${ac_cv_prog_ac_ct_CC+y} then : printf %s "(cached) " >&6 else case e in #( e) if test -n "$ac_ct_CC"; then ac_cv_prog_ac_ct_CC="$ac_ct_CC" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS case $as_dir in #((( '') as_dir=./ ;; */) ;; *) as_dir=$as_dir/ ;; esac for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir$ac_word$ac_exec_ext"; then ac_cv_prog_ac_ct_CC="$ac_prog" printf "%s\n" "$as_me:${as_lineno-$LINENO}: found $as_dir$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi ;; esac fi ac_ct_CC=$ac_cv_prog_ac_ct_CC if test -n "$ac_ct_CC"; then { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_ct_CC" >&5 printf "%s\n" "$ac_ct_CC" >&6; } else { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 printf "%s\n" "no" >&6; } fi test -n "$ac_ct_CC" && break done if test "x$ac_ct_CC" = x; then CC="" else case $cross_compiling:$ac_tool_warned in yes:) { printf "%s\n" "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 printf "%s\n" "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} ac_tool_warned=yes ;; esac CC=$ac_ct_CC fi fi fi if test -z "$CC"; then if test -n "$ac_tool_prefix"; then # Extract the first word of "${ac_tool_prefix}clang", so it can be a program name with args. set dummy ${ac_tool_prefix}clang; ac_word=$2 { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 printf %s "checking for $ac_word... " >&6; } if test ${ac_cv_prog_CC+y} then : printf %s "(cached) " >&6 else case e in #( e) if test -n "$CC"; then ac_cv_prog_CC="$CC" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS case $as_dir in #((( '') as_dir=./ ;; */) ;; *) as_dir=$as_dir/ ;; esac for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir$ac_word$ac_exec_ext"; then ac_cv_prog_CC="${ac_tool_prefix}clang" printf "%s\n" "$as_me:${as_lineno-$LINENO}: found $as_dir$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi ;; esac fi CC=$ac_cv_prog_CC if test -n "$CC"; then { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $CC" >&5 printf "%s\n" "$CC" >&6; } else { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 printf "%s\n" "no" >&6; } fi fi if test -z "$ac_cv_prog_CC"; then ac_ct_CC=$CC # Extract the first word of "clang", so it can be a program name with args. set dummy clang; ac_word=$2 { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 printf %s "checking for $ac_word... " >&6; } if test ${ac_cv_prog_ac_ct_CC+y} then : printf %s "(cached) " >&6 else case e in #( e) if test -n "$ac_ct_CC"; then ac_cv_prog_ac_ct_CC="$ac_ct_CC" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS case $as_dir in #((( '') as_dir=./ ;; */) ;; *) as_dir=$as_dir/ ;; esac for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir$ac_word$ac_exec_ext"; then ac_cv_prog_ac_ct_CC="clang" printf "%s\n" "$as_me:${as_lineno-$LINENO}: found $as_dir$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi ;; esac fi ac_ct_CC=$ac_cv_prog_ac_ct_CC if test -n "$ac_ct_CC"; then { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_ct_CC" >&5 printf "%s\n" "$ac_ct_CC" >&6; } else { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 printf "%s\n" "no" >&6; } fi if test "x$ac_ct_CC" = x; then CC="" else case $cross_compiling:$ac_tool_warned in yes:) { printf "%s\n" "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 printf "%s\n" "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} ac_tool_warned=yes ;; esac CC=$ac_ct_CC fi else CC="$ac_cv_prog_CC" fi fi test -z "$CC" && { { printf "%s\n" "$as_me:${as_lineno-$LINENO}: error: in '$ac_pwd':" >&5 printf "%s\n" "$as_me: error: in '$ac_pwd':" >&2;} as_fn_error $? "no acceptable C compiler found in \$PATH See 'config.log' for more details" "$LINENO" 5; } # Provide some information about the compiler. printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for C compiler version" >&5 set X $ac_compile ac_compiler=$2 for ac_option in --version -v -V -qversion -version; do { { ac_try="$ac_compiler $ac_option >&5" case "(($ac_try" in *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; *) ac_try_echo=$ac_try;; esac eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" printf "%s\n" "$ac_try_echo"; } >&5 (eval "$ac_compiler $ac_option >&5") 2>conftest.err ac_status=$? if test -s conftest.err; then sed '10a\ ... rest of stderr output deleted ... 10q' conftest.err >conftest.er1 cat conftest.er1 >&5 fi rm -f conftest.er1 conftest.err printf "%s\n" "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; } done cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ int main (void) { ; return 0; } _ACEOF ac_clean_files_save=$ac_clean_files ac_clean_files="$ac_clean_files a.out a.out.dSYM a.exe b.out" # Try to create an executable without -o first, disregard a.out. # It will help us diagnose broken compilers, and finding out an intuition # of exeext. { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking whether the C compiler works" >&5 printf %s "checking whether the C compiler works... " >&6; } ac_link_default=`printf "%s\n" "$ac_link" | sed 's/ -o *conftest[^ ]*//'` # The possible output files: ac_files="a.out conftest.exe conftest a.exe a_out.exe b.out conftest.*" ac_rmfiles= for ac_file in $ac_files do case $ac_file in *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg | *.map | *.inf | *.dSYM | *.o | *.obj ) ;; * ) ac_rmfiles="$ac_rmfiles $ac_file";; esac done rm -f $ac_rmfiles if { { ac_try="$ac_link_default" case "(($ac_try" in *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; *) ac_try_echo=$ac_try;; esac eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" printf "%s\n" "$ac_try_echo"; } >&5 (eval "$ac_link_default") 2>&5 ac_status=$? printf "%s\n" "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; } then : # Autoconf-2.13 could set the ac_cv_exeext variable to 'no'. # So ignore a value of 'no', otherwise this would lead to 'EXEEXT = no' # in a Makefile. We should not override ac_cv_exeext if it was cached, # so that the user can short-circuit this test for compilers unknown to # Autoconf. for ac_file in $ac_files '' do test -f "$ac_file" || continue case $ac_file in *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg | *.map | *.inf | *.dSYM | *.o | *.obj ) ;; [ab].out ) # We found the default executable, but exeext='' is most # certainly right. break;; *.* ) if test ${ac_cv_exeext+y} && test "$ac_cv_exeext" != no; then :; else ac_cv_exeext=`expr "$ac_file" : '[^.]*\(\..*\)'` fi # We set ac_cv_exeext here because the later test for it is not # safe: cross compilers may not add the suffix if given an '-o' # argument, so we may need to know it at that point already. # Even if this section looks crufty: it has the advantage of # actually working. break;; * ) break;; esac done test "$ac_cv_exeext" = no && ac_cv_exeext= else case e in #( e) ac_file='' ;; esac fi if test -z "$ac_file" then : { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 printf "%s\n" "no" >&6; } printf "%s\n" "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 { { printf "%s\n" "$as_me:${as_lineno-$LINENO}: error: in '$ac_pwd':" >&5 printf "%s\n" "$as_me: error: in '$ac_pwd':" >&2;} as_fn_error 77 "C compiler cannot create executables See 'config.log' for more details" "$LINENO" 5; } else case e in #( e) { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: yes" >&5 printf "%s\n" "yes" >&6; } ;; esac fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for C compiler default output file name" >&5 printf %s "checking for C compiler default output file name... " >&6; } { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_file" >&5 printf "%s\n" "$ac_file" >&6; } ac_exeext=$ac_cv_exeext rm -f -r a.out a.out.dSYM a.exe conftest$ac_cv_exeext b.out ac_clean_files=$ac_clean_files_save { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for suffix of executables" >&5 printf %s "checking for suffix of executables... " >&6; } if { { ac_try="$ac_link" case "(($ac_try" in *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; *) ac_try_echo=$ac_try;; esac eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" printf "%s\n" "$ac_try_echo"; } >&5 (eval "$ac_link") 2>&5 ac_status=$? printf "%s\n" "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; } then : # If both 'conftest.exe' and 'conftest' are 'present' (well, observable) # catch 'conftest.exe'. For instance with Cygwin, 'ls conftest' will # work properly (i.e., refer to 'conftest.exe'), while it won't with # 'rm'. for ac_file in conftest.exe conftest conftest.*; do test -f "$ac_file" || continue case $ac_file in *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg | *.map | *.inf | *.dSYM | *.o | *.obj ) ;; *.* ) ac_cv_exeext=`expr "$ac_file" : '[^.]*\(\..*\)'` break;; * ) break;; esac done else case e in #( e) { { printf "%s\n" "$as_me:${as_lineno-$LINENO}: error: in '$ac_pwd':" >&5 printf "%s\n" "$as_me: error: in '$ac_pwd':" >&2;} as_fn_error $? "cannot compute suffix of executables: cannot compile and link See 'config.log' for more details" "$LINENO" 5; } ;; esac fi rm -f conftest conftest$ac_cv_exeext { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_exeext" >&5 printf "%s\n" "$ac_cv_exeext" >&6; } rm -f conftest.$ac_ext EXEEXT=$ac_cv_exeext ac_exeext=$EXEEXT cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include int main (void) { FILE *f = fopen ("conftest.out", "w"); if (!f) return 1; return ferror (f) || fclose (f) != 0; ; return 0; } _ACEOF ac_clean_files="$ac_clean_files conftest.out" # Check that the compiler produces executables we can run. If not, either # the compiler is broken, or we cross compile. { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking whether we are cross compiling" >&5 printf %s "checking whether we are cross compiling... " >&6; } if test "$cross_compiling" != yes; then { { ac_try="$ac_link" case "(($ac_try" in *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; *) ac_try_echo=$ac_try;; esac eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" printf "%s\n" "$ac_try_echo"; } >&5 (eval "$ac_link") 2>&5 ac_status=$? printf "%s\n" "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; } if { ac_try='./conftest$ac_cv_exeext' { { case "(($ac_try" in *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; *) ac_try_echo=$ac_try;; esac eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" printf "%s\n" "$ac_try_echo"; } >&5 (eval "$ac_try") 2>&5 ac_status=$? printf "%s\n" "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; }; }; then cross_compiling=no else if test "$cross_compiling" = maybe; then cross_compiling=yes else { { printf "%s\n" "$as_me:${as_lineno-$LINENO}: error: in '$ac_pwd':" >&5 printf "%s\n" "$as_me: error: in '$ac_pwd':" >&2;} as_fn_error 77 "cannot run C compiled programs. If you meant to cross compile, use '--host'. See 'config.log' for more details" "$LINENO" 5; } fi fi fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $cross_compiling" >&5 printf "%s\n" "$cross_compiling" >&6; } rm -f conftest.$ac_ext conftest$ac_cv_exeext \ conftest.o conftest.obj conftest.out ac_clean_files=$ac_clean_files_save { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for suffix of object files" >&5 printf %s "checking for suffix of object files... " >&6; } if test ${ac_cv_objext+y} then : printf %s "(cached) " >&6 else case e in #( e) cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ int main (void) { ; return 0; } _ACEOF rm -f conftest.o conftest.obj if { { ac_try="$ac_compile" case "(($ac_try" in *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; *) ac_try_echo=$ac_try;; esac eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" printf "%s\n" "$ac_try_echo"; } >&5 (eval "$ac_compile") 2>&5 ac_status=$? printf "%s\n" "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; } then : for ac_file in conftest.o conftest.obj conftest.*; do test -f "$ac_file" || continue; case $ac_file in *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg | *.map | *.inf | *.dSYM ) ;; *) ac_cv_objext=`expr "$ac_file" : '.*\.\(.*\)'` break;; esac done else case e in #( e) printf "%s\n" "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 { { printf "%s\n" "$as_me:${as_lineno-$LINENO}: error: in '$ac_pwd':" >&5 printf "%s\n" "$as_me: error: in '$ac_pwd':" >&2;} as_fn_error $? "cannot compute suffix of object files: cannot compile See 'config.log' for more details" "$LINENO" 5; } ;; esac fi rm -f conftest.$ac_cv_objext conftest.$ac_ext ;; esac fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_objext" >&5 printf "%s\n" "$ac_cv_objext" >&6; } OBJEXT=$ac_cv_objext ac_objext=$OBJEXT { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking whether the compiler supports GNU C" >&5 printf %s "checking whether the compiler supports GNU C... " >&6; } if test ${ac_cv_c_compiler_gnu+y} then : printf %s "(cached) " >&6 else case e in #( e) cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ int main (void) { #ifndef __GNUC__ choke me #endif ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO" then : ac_compiler_gnu=yes else case e in #( e) ac_compiler_gnu=no ;; esac fi rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext ac_cv_c_compiler_gnu=$ac_compiler_gnu ;; esac fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_c_compiler_gnu" >&5 printf "%s\n" "$ac_cv_c_compiler_gnu" >&6; } ac_compiler_gnu=$ac_cv_c_compiler_gnu if test $ac_compiler_gnu = yes; then GCC=yes else GCC= fi ac_test_CFLAGS=${CFLAGS+y} ac_save_CFLAGS=$CFLAGS { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking whether $CC accepts -g" >&5 printf %s "checking whether $CC accepts -g... " >&6; } if test ${ac_cv_prog_cc_g+y} then : printf %s "(cached) " >&6 else case e in #( e) ac_save_c_werror_flag=$ac_c_werror_flag ac_c_werror_flag=yes ac_cv_prog_cc_g=no CFLAGS="-g" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ int main (void) { ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO" then : ac_cv_prog_cc_g=yes else case e in #( e) CFLAGS="" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ int main (void) { ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO" then : else case e in #( e) ac_c_werror_flag=$ac_save_c_werror_flag CFLAGS="-g" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ int main (void) { ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO" then : ac_cv_prog_cc_g=yes fi rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext ;; esac fi rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext ;; esac fi rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext ac_c_werror_flag=$ac_save_c_werror_flag ;; esac fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_prog_cc_g" >&5 printf "%s\n" "$ac_cv_prog_cc_g" >&6; } if test $ac_test_CFLAGS; then CFLAGS=$ac_save_CFLAGS elif test $ac_cv_prog_cc_g = yes; then if test "$GCC" = yes; then CFLAGS="-g -O2" else CFLAGS="-g" fi else if test "$GCC" = yes; then CFLAGS="-O2" else CFLAGS= fi fi ac_prog_cc_stdc=no if test x$ac_prog_cc_stdc = xno then : { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $CC option to enable C11 features" >&5 printf %s "checking for $CC option to enable C11 features... " >&6; } if test ${ac_cv_prog_cc_c11+y} then : printf %s "(cached) " >&6 else case e in #( e) ac_cv_prog_cc_c11=no ac_save_CC=$CC cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ $ac_c_conftest_c11_program _ACEOF for ac_arg in '' -std=gnu11 do CC="$ac_save_CC $ac_arg" if ac_fn_c_try_compile "$LINENO" then : ac_cv_prog_cc_c11=$ac_arg fi rm -f core conftest.err conftest.$ac_objext conftest.beam test "x$ac_cv_prog_cc_c11" != "xno" && break done rm -f conftest.$ac_ext CC=$ac_save_CC ;; esac fi if test "x$ac_cv_prog_cc_c11" = xno then : { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: unsupported" >&5 printf "%s\n" "unsupported" >&6; } else case e in #( e) if test "x$ac_cv_prog_cc_c11" = x then : { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: none needed" >&5 printf "%s\n" "none needed" >&6; } else case e in #( e) { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_prog_cc_c11" >&5 printf "%s\n" "$ac_cv_prog_cc_c11" >&6; } CC="$CC $ac_cv_prog_cc_c11" ;; esac fi ac_cv_prog_cc_stdc=$ac_cv_prog_cc_c11 ac_prog_cc_stdc=c11 ;; esac fi fi if test x$ac_prog_cc_stdc = xno then : { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $CC option to enable C99 features" >&5 printf %s "checking for $CC option to enable C99 features... " >&6; } if test ${ac_cv_prog_cc_c99+y} then : printf %s "(cached) " >&6 else case e in #( e) ac_cv_prog_cc_c99=no ac_save_CC=$CC cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ $ac_c_conftest_c99_program _ACEOF for ac_arg in '' -std=gnu99 -std=c99 -c99 -qlanglvl=extc1x -qlanglvl=extc99 -AC99 -D_STDC_C99= do CC="$ac_save_CC $ac_arg" if ac_fn_c_try_compile "$LINENO" then : ac_cv_prog_cc_c99=$ac_arg fi rm -f core conftest.err conftest.$ac_objext conftest.beam test "x$ac_cv_prog_cc_c99" != "xno" && break done rm -f conftest.$ac_ext CC=$ac_save_CC ;; esac fi if test "x$ac_cv_prog_cc_c99" = xno then : { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: unsupported" >&5 printf "%s\n" "unsupported" >&6; } else case e in #( e) if test "x$ac_cv_prog_cc_c99" = x then : { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: none needed" >&5 printf "%s\n" "none needed" >&6; } else case e in #( e) { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_prog_cc_c99" >&5 printf "%s\n" "$ac_cv_prog_cc_c99" >&6; } CC="$CC $ac_cv_prog_cc_c99" ;; esac fi ac_cv_prog_cc_stdc=$ac_cv_prog_cc_c99 ac_prog_cc_stdc=c99 ;; esac fi fi if test x$ac_prog_cc_stdc = xno then : { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $CC option to enable C89 features" >&5 printf %s "checking for $CC option to enable C89 features... " >&6; } if test ${ac_cv_prog_cc_c89+y} then : printf %s "(cached) " >&6 else case e in #( e) ac_cv_prog_cc_c89=no ac_save_CC=$CC cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ $ac_c_conftest_c89_program _ACEOF for ac_arg in '' -qlanglvl=extc89 -qlanglvl=ansi -std -Ae "-Aa -D_HPUX_SOURCE" "-Xc -D__EXTENSIONS__" do CC="$ac_save_CC $ac_arg" if ac_fn_c_try_compile "$LINENO" then : ac_cv_prog_cc_c89=$ac_arg fi rm -f core conftest.err conftest.$ac_objext conftest.beam test "x$ac_cv_prog_cc_c89" != "xno" && break done rm -f conftest.$ac_ext CC=$ac_save_CC ;; esac fi if test "x$ac_cv_prog_cc_c89" = xno then : { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: unsupported" >&5 printf "%s\n" "unsupported" >&6; } else case e in #( e) if test "x$ac_cv_prog_cc_c89" = x then : { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: none needed" >&5 printf "%s\n" "none needed" >&6; } else case e in #( e) { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_prog_cc_c89" >&5 printf "%s\n" "$ac_cv_prog_cc_c89" >&6; } CC="$CC $ac_cv_prog_cc_c89" ;; esac fi ac_cv_prog_cc_stdc=$ac_cv_prog_cc_c89 ac_prog_cc_stdc=c89 ;; esac fi fi ac_ext=c ac_cpp='$CPP $CPPFLAGS' ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' ac_compiler_gnu=$ac_cv_c_compiler_gnu ac_ext=c ac_cpp='$CPP $CPPFLAGS' ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' ac_compiler_gnu=$ac_cv_c_compiler_gnu { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking whether $CC understands -c and -o together" >&5 printf %s "checking whether $CC understands -c and -o together... " >&6; } if test ${am_cv_prog_cc_c_o+y} then : printf %s "(cached) " >&6 else case e in #( e) cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ int main (void) { ; return 0; } _ACEOF # Make sure it works both with $CC and with simple cc. # Following AC_PROG_CC_C_O, we do the test twice because some # compilers refuse to overwrite an existing .o file with -o, # though they will create one. am_cv_prog_cc_c_o=yes for am_i in 1 2; do if { echo "$as_me:$LINENO: $CC -c conftest.$ac_ext -o conftest2.$ac_objext" >&5 ($CC -c conftest.$ac_ext -o conftest2.$ac_objext) >&5 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } \ && test -f conftest2.$ac_objext; then : OK else am_cv_prog_cc_c_o=no break fi done rm -f core conftest* unset am_i ;; esac fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $am_cv_prog_cc_c_o" >&5 printf "%s\n" "$am_cv_prog_cc_c_o" >&6; } if test "$am_cv_prog_cc_c_o" != yes; then # Losing compiler, so override with the script. # FIXME: It is wrong to rewrite CC. # But if we don't then we get into trouble of one sort or another. # A longer-term fix would be to have automake use am__CC in this case, # and then we could set am__CC="\$(top_srcdir)/compile \$(CC)" CC="$am_aux_dir/compile $CC" fi ac_ext=c ac_cpp='$CPP $CPPFLAGS' ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' ac_compiler_gnu=$ac_cv_c_compiler_gnu depcc="$CC" am_compiler_list= { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking dependency style of $depcc" >&5 printf %s "checking dependency style of $depcc... " >&6; } if test ${am_cv_CC_dependencies_compiler_type+y} then : printf %s "(cached) " >&6 else case e in #( e) if test -z "$AMDEP_TRUE" && test -f "$am_depcomp"; then # We make a subdir and do the tests there. Otherwise we can end up # making bogus files that we don't know about and never remove. For # instance it was reported that on HP-UX the gcc test will end up # making a dummy file named 'D' -- because '-MD' means "put the output # in D". rm -rf conftest.dir mkdir conftest.dir # Copy depcomp to subdir because otherwise we won't find it if we're # using a relative directory. cp "$am_depcomp" conftest.dir cd conftest.dir # We will build objects and dependencies in a subdirectory because # it helps to detect inapplicable dependency modes. For instance # both Tru64's cc and ICC support -MD to output dependencies as a # side effect of compilation, but ICC will put the dependencies in # the current directory while Tru64 will put them in the object # directory. mkdir sub am_cv_CC_dependencies_compiler_type=none if test "$am_compiler_list" = ""; then am_compiler_list=`sed -n 's/^#*\([a-zA-Z0-9]*\))$/\1/p' < ./depcomp` fi am__universal=false case " $depcc " in #( *\ -arch\ *\ -arch\ *) am__universal=true ;; esac for depmode in $am_compiler_list; do # Setup a source with many dependencies, because some compilers # like to wrap large dependency lists on column 80 (with \), and # we should not choose a depcomp mode which is confused by this. # # We need to recreate these files for each test, as the compiler may # overwrite some of them when testing with obscure command lines. # This happens at least with the AIX C compiler. : > sub/conftest.c for i in 1 2 3 4 5 6; do echo '#include "conftst'$i'.h"' >> sub/conftest.c # Using ": > sub/conftst$i.h" creates only sub/conftst1.h with # Solaris 10 /bin/sh. echo '/* dummy */' > sub/conftst$i.h done echo "${am__include} ${am__quote}sub/conftest.Po${am__quote}" > confmf # We check with '-c' and '-o' for the sake of the "dashmstdout" # mode. It turns out that the SunPro C++ compiler does not properly # handle '-M -o', and we need to detect this. Also, some Intel # versions had trouble with output in subdirs. am__obj=sub/conftest.${OBJEXT-o} am__minus_obj="-o $am__obj" case $depmode in gcc) # This depmode causes a compiler race in universal mode. test "$am__universal" = false || continue ;; nosideeffect) # After this tag, mechanisms are not by side-effect, so they'll # only be used when explicitly requested. if test "x$enable_dependency_tracking" = xyes; then continue else break fi ;; msvc7 | msvc7msys | msvisualcpp | msvcmsys) # This compiler won't grok '-c -o', but also, the minuso test has # not run yet. These depmodes are late enough in the game, and # so weak that their functioning should not be impacted. am__obj=conftest.${OBJEXT-o} am__minus_obj= ;; none) break ;; esac if depmode=$depmode \ source=sub/conftest.c object=$am__obj \ depfile=sub/conftest.Po tmpdepfile=sub/conftest.TPo \ $SHELL ./depcomp $depcc -c $am__minus_obj sub/conftest.c \ >/dev/null 2>conftest.err && grep sub/conftst1.h sub/conftest.Po > /dev/null 2>&1 && grep sub/conftst6.h sub/conftest.Po > /dev/null 2>&1 && grep $am__obj sub/conftest.Po > /dev/null 2>&1 && ${MAKE-make} -s -f confmf > /dev/null 2>&1; then # icc doesn't choke on unknown options, it will just issue warnings # or remarks (even with -Werror). So we grep stderr for any message # that says an option was ignored or not supported. # When given -MP, icc 7.0 and 7.1 complain thusly: # icc: Command line warning: ignoring option '-M'; no argument required # The diagnosis changed in icc 8.0: # icc: Command line remark: option '-MP' not supported if (grep 'ignoring option' conftest.err || grep 'not supported' conftest.err) >/dev/null 2>&1; then :; else am_cv_CC_dependencies_compiler_type=$depmode break fi fi done cd .. rm -rf conftest.dir else am_cv_CC_dependencies_compiler_type=none fi ;; esac fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $am_cv_CC_dependencies_compiler_type" >&5 printf "%s\n" "$am_cv_CC_dependencies_compiler_type" >&6; } CCDEPMODE=depmode=$am_cv_CC_dependencies_compiler_type if test "x$enable_dependency_tracking" != xno \ && test "$am_cv_CC_dependencies_compiler_type" = gcc3; then am__fastdepCC_TRUE= am__fastdepCC_FALSE='#' else am__fastdepCC_TRUE='#' am__fastdepCC_FALSE= fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for C/C++ restrict keyword" >&5 printf %s "checking for C/C++ restrict keyword... " >&6; } if test ${ac_cv_c_restrict+y} then : printf %s "(cached) " >&6 else case e in #( e) ac_cv_c_restrict=no # Put '__restrict__' first, to avoid problems with glibc and non-GCC; see: # https://lists.gnu.org/archive/html/bug-autoconf/2016-02/msg00006.html # Put 'restrict' last, because C++ lacks it. for ac_kw in __restrict__ __restrict _Restrict restrict; do cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ typedef int *int_ptr; int foo (int_ptr $ac_kw ip) { return ip[0]; } int bar (int [$ac_kw]); /* Catch GCC bug 14050. */ int bar (int ip[$ac_kw]) { return ip[0]; } int main (void) { int s[1]; int *$ac_kw t = s; t[0] = 0; return foo (t) + bar (t); ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO" then : ac_cv_c_restrict=$ac_kw fi rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext test "$ac_cv_c_restrict" != no && break done ;; esac fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_c_restrict" >&5 printf "%s\n" "$ac_cv_c_restrict" >&6; } case $ac_cv_c_restrict in restrict) ;; no) printf "%s\n" "#define restrict /**/" >>confdefs.h ;; *) printf "%s\n" "#define restrict $ac_cv_c_restrict" >>confdefs.h ;; esac ac_ext=c ac_cpp='$CPP $CPPFLAGS' ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' ac_compiler_gnu=$ac_cv_c_compiler_gnu if test -n "$ac_tool_prefix"; then # Extract the first word of "${ac_tool_prefix}gcc", so it can be a program name with args. set dummy ${ac_tool_prefix}gcc; ac_word=$2 { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 printf %s "checking for $ac_word... " >&6; } if test ${ac_cv_prog_CC+y} then : printf %s "(cached) " >&6 else case e in #( e) if test -n "$CC"; then ac_cv_prog_CC="$CC" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS case $as_dir in #((( '') as_dir=./ ;; */) ;; *) as_dir=$as_dir/ ;; esac for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir$ac_word$ac_exec_ext"; then ac_cv_prog_CC="${ac_tool_prefix}gcc" printf "%s\n" "$as_me:${as_lineno-$LINENO}: found $as_dir$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi ;; esac fi CC=$ac_cv_prog_CC if test -n "$CC"; then { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $CC" >&5 printf "%s\n" "$CC" >&6; } else { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 printf "%s\n" "no" >&6; } fi fi if test -z "$ac_cv_prog_CC"; then ac_ct_CC=$CC # Extract the first word of "gcc", so it can be a program name with args. set dummy gcc; ac_word=$2 { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 printf %s "checking for $ac_word... " >&6; } if test ${ac_cv_prog_ac_ct_CC+y} then : printf %s "(cached) " >&6 else case e in #( e) if test -n "$ac_ct_CC"; then ac_cv_prog_ac_ct_CC="$ac_ct_CC" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS case $as_dir in #((( '') as_dir=./ ;; */) ;; *) as_dir=$as_dir/ ;; esac for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir$ac_word$ac_exec_ext"; then ac_cv_prog_ac_ct_CC="gcc" printf "%s\n" "$as_me:${as_lineno-$LINENO}: found $as_dir$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi ;; esac fi ac_ct_CC=$ac_cv_prog_ac_ct_CC if test -n "$ac_ct_CC"; then { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_ct_CC" >&5 printf "%s\n" "$ac_ct_CC" >&6; } else { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 printf "%s\n" "no" >&6; } fi if test "x$ac_ct_CC" = x; then CC="" else case $cross_compiling:$ac_tool_warned in yes:) { printf "%s\n" "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 printf "%s\n" "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} ac_tool_warned=yes ;; esac CC=$ac_ct_CC fi else CC="$ac_cv_prog_CC" fi if test -z "$CC"; then if test -n "$ac_tool_prefix"; then # Extract the first word of "${ac_tool_prefix}cc", so it can be a program name with args. set dummy ${ac_tool_prefix}cc; ac_word=$2 { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 printf %s "checking for $ac_word... " >&6; } if test ${ac_cv_prog_CC+y} then : printf %s "(cached) " >&6 else case e in #( e) if test -n "$CC"; then ac_cv_prog_CC="$CC" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS case $as_dir in #((( '') as_dir=./ ;; */) ;; *) as_dir=$as_dir/ ;; esac for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir$ac_word$ac_exec_ext"; then ac_cv_prog_CC="${ac_tool_prefix}cc" printf "%s\n" "$as_me:${as_lineno-$LINENO}: found $as_dir$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi ;; esac fi CC=$ac_cv_prog_CC if test -n "$CC"; then { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $CC" >&5 printf "%s\n" "$CC" >&6; } else { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 printf "%s\n" "no" >&6; } fi fi fi if test -z "$CC"; then # Extract the first word of "cc", so it can be a program name with args. set dummy cc; ac_word=$2 { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 printf %s "checking for $ac_word... " >&6; } if test ${ac_cv_prog_CC+y} then : printf %s "(cached) " >&6 else case e in #( e) if test -n "$CC"; then ac_cv_prog_CC="$CC" # Let the user override the test. else ac_prog_rejected=no as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS case $as_dir in #((( '') as_dir=./ ;; */) ;; *) as_dir=$as_dir/ ;; esac for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir$ac_word$ac_exec_ext"; then if test "$as_dir$ac_word$ac_exec_ext" = "/usr/ucb/cc"; then ac_prog_rejected=yes continue fi ac_cv_prog_CC="cc" printf "%s\n" "$as_me:${as_lineno-$LINENO}: found $as_dir$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS if test $ac_prog_rejected = yes; then # We found a bogon in the path, so make sure we never use it. set dummy $ac_cv_prog_CC shift if test $# != 0; then # We chose a different compiler from the bogus one. # However, it has the same basename, so the bogon will be chosen # first if we set CC to just the basename; use the full file name. shift ac_cv_prog_CC="$as_dir$ac_word${1+' '}$@" fi fi fi ;; esac fi CC=$ac_cv_prog_CC if test -n "$CC"; then { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $CC" >&5 printf "%s\n" "$CC" >&6; } else { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 printf "%s\n" "no" >&6; } fi fi if test -z "$CC"; then if test -n "$ac_tool_prefix"; then for ac_prog in cl.exe do # Extract the first word of "$ac_tool_prefix$ac_prog", so it can be a program name with args. set dummy $ac_tool_prefix$ac_prog; ac_word=$2 { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 printf %s "checking for $ac_word... " >&6; } if test ${ac_cv_prog_CC+y} then : printf %s "(cached) " >&6 else case e in #( e) if test -n "$CC"; then ac_cv_prog_CC="$CC" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS case $as_dir in #((( '') as_dir=./ ;; */) ;; *) as_dir=$as_dir/ ;; esac for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir$ac_word$ac_exec_ext"; then ac_cv_prog_CC="$ac_tool_prefix$ac_prog" printf "%s\n" "$as_me:${as_lineno-$LINENO}: found $as_dir$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi ;; esac fi CC=$ac_cv_prog_CC if test -n "$CC"; then { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $CC" >&5 printf "%s\n" "$CC" >&6; } else { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 printf "%s\n" "no" >&6; } fi test -n "$CC" && break done fi if test -z "$CC"; then ac_ct_CC=$CC for ac_prog in cl.exe do # Extract the first word of "$ac_prog", so it can be a program name with args. set dummy $ac_prog; ac_word=$2 { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 printf %s "checking for $ac_word... " >&6; } if test ${ac_cv_prog_ac_ct_CC+y} then : printf %s "(cached) " >&6 else case e in #( e) if test -n "$ac_ct_CC"; then ac_cv_prog_ac_ct_CC="$ac_ct_CC" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS case $as_dir in #((( '') as_dir=./ ;; */) ;; *) as_dir=$as_dir/ ;; esac for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir$ac_word$ac_exec_ext"; then ac_cv_prog_ac_ct_CC="$ac_prog" printf "%s\n" "$as_me:${as_lineno-$LINENO}: found $as_dir$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi ;; esac fi ac_ct_CC=$ac_cv_prog_ac_ct_CC if test -n "$ac_ct_CC"; then { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_ct_CC" >&5 printf "%s\n" "$ac_ct_CC" >&6; } else { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 printf "%s\n" "no" >&6; } fi test -n "$ac_ct_CC" && break done if test "x$ac_ct_CC" = x; then CC="" else case $cross_compiling:$ac_tool_warned in yes:) { printf "%s\n" "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 printf "%s\n" "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} ac_tool_warned=yes ;; esac CC=$ac_ct_CC fi fi fi if test -z "$CC"; then if test -n "$ac_tool_prefix"; then # Extract the first word of "${ac_tool_prefix}clang", so it can be a program name with args. set dummy ${ac_tool_prefix}clang; ac_word=$2 { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 printf %s "checking for $ac_word... " >&6; } if test ${ac_cv_prog_CC+y} then : printf %s "(cached) " >&6 else case e in #( e) if test -n "$CC"; then ac_cv_prog_CC="$CC" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS case $as_dir in #((( '') as_dir=./ ;; */) ;; *) as_dir=$as_dir/ ;; esac for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir$ac_word$ac_exec_ext"; then ac_cv_prog_CC="${ac_tool_prefix}clang" printf "%s\n" "$as_me:${as_lineno-$LINENO}: found $as_dir$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi ;; esac fi CC=$ac_cv_prog_CC if test -n "$CC"; then { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $CC" >&5 printf "%s\n" "$CC" >&6; } else { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 printf "%s\n" "no" >&6; } fi fi if test -z "$ac_cv_prog_CC"; then ac_ct_CC=$CC # Extract the first word of "clang", so it can be a program name with args. set dummy clang; ac_word=$2 { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 printf %s "checking for $ac_word... " >&6; } if test ${ac_cv_prog_ac_ct_CC+y} then : printf %s "(cached) " >&6 else case e in #( e) if test -n "$ac_ct_CC"; then ac_cv_prog_ac_ct_CC="$ac_ct_CC" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS case $as_dir in #((( '') as_dir=./ ;; */) ;; *) as_dir=$as_dir/ ;; esac for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir$ac_word$ac_exec_ext"; then ac_cv_prog_ac_ct_CC="clang" printf "%s\n" "$as_me:${as_lineno-$LINENO}: found $as_dir$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi ;; esac fi ac_ct_CC=$ac_cv_prog_ac_ct_CC if test -n "$ac_ct_CC"; then { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_ct_CC" >&5 printf "%s\n" "$ac_ct_CC" >&6; } else { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 printf "%s\n" "no" >&6; } fi if test "x$ac_ct_CC" = x; then CC="" else case $cross_compiling:$ac_tool_warned in yes:) { printf "%s\n" "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 printf "%s\n" "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} ac_tool_warned=yes ;; esac CC=$ac_ct_CC fi else CC="$ac_cv_prog_CC" fi fi test -z "$CC" && { { printf "%s\n" "$as_me:${as_lineno-$LINENO}: error: in '$ac_pwd':" >&5 printf "%s\n" "$as_me: error: in '$ac_pwd':" >&2;} as_fn_error $? "no acceptable C compiler found in \$PATH See 'config.log' for more details" "$LINENO" 5; } # Provide some information about the compiler. printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for C compiler version" >&5 set X $ac_compile ac_compiler=$2 for ac_option in --version -v -V -qversion -version; do { { ac_try="$ac_compiler $ac_option >&5" case "(($ac_try" in *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; *) ac_try_echo=$ac_try;; esac eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" printf "%s\n" "$ac_try_echo"; } >&5 (eval "$ac_compiler $ac_option >&5") 2>conftest.err ac_status=$? if test -s conftest.err; then sed '10a\ ... rest of stderr output deleted ... 10q' conftest.err >conftest.er1 cat conftest.er1 >&5 fi rm -f conftest.er1 conftest.err printf "%s\n" "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; } done { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking whether the compiler supports GNU C" >&5 printf %s "checking whether the compiler supports GNU C... " >&6; } if test ${ac_cv_c_compiler_gnu+y} then : printf %s "(cached) " >&6 else case e in #( e) cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ int main (void) { #ifndef __GNUC__ choke me #endif ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO" then : ac_compiler_gnu=yes else case e in #( e) ac_compiler_gnu=no ;; esac fi rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext ac_cv_c_compiler_gnu=$ac_compiler_gnu ;; esac fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_c_compiler_gnu" >&5 printf "%s\n" "$ac_cv_c_compiler_gnu" >&6; } ac_compiler_gnu=$ac_cv_c_compiler_gnu if test $ac_compiler_gnu = yes; then GCC=yes else GCC= fi ac_test_CFLAGS=${CFLAGS+y} ac_save_CFLAGS=$CFLAGS { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking whether $CC accepts -g" >&5 printf %s "checking whether $CC accepts -g... " >&6; } if test ${ac_cv_prog_cc_g+y} then : printf %s "(cached) " >&6 else case e in #( e) ac_save_c_werror_flag=$ac_c_werror_flag ac_c_werror_flag=yes ac_cv_prog_cc_g=no CFLAGS="-g" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ int main (void) { ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO" then : ac_cv_prog_cc_g=yes else case e in #( e) CFLAGS="" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ int main (void) { ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO" then : else case e in #( e) ac_c_werror_flag=$ac_save_c_werror_flag CFLAGS="-g" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ int main (void) { ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO" then : ac_cv_prog_cc_g=yes fi rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext ;; esac fi rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext ;; esac fi rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext ac_c_werror_flag=$ac_save_c_werror_flag ;; esac fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_prog_cc_g" >&5 printf "%s\n" "$ac_cv_prog_cc_g" >&6; } if test $ac_test_CFLAGS; then CFLAGS=$ac_save_CFLAGS elif test $ac_cv_prog_cc_g = yes; then if test "$GCC" = yes; then CFLAGS="-g -O2" else CFLAGS="-g" fi else if test "$GCC" = yes; then CFLAGS="-O2" else CFLAGS= fi fi ac_prog_cc_stdc=no if test x$ac_prog_cc_stdc = xno then : { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $CC option to enable C11 features" >&5 printf %s "checking for $CC option to enable C11 features... " >&6; } if test ${ac_cv_prog_cc_c11+y} then : printf %s "(cached) " >&6 else case e in #( e) ac_cv_prog_cc_c11=no ac_save_CC=$CC cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ $ac_c_conftest_c11_program _ACEOF for ac_arg in '' -std=gnu11 do CC="$ac_save_CC $ac_arg" if ac_fn_c_try_compile "$LINENO" then : ac_cv_prog_cc_c11=$ac_arg fi rm -f core conftest.err conftest.$ac_objext conftest.beam test "x$ac_cv_prog_cc_c11" != "xno" && break done rm -f conftest.$ac_ext CC=$ac_save_CC ;; esac fi if test "x$ac_cv_prog_cc_c11" = xno then : { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: unsupported" >&5 printf "%s\n" "unsupported" >&6; } else case e in #( e) if test "x$ac_cv_prog_cc_c11" = x then : { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: none needed" >&5 printf "%s\n" "none needed" >&6; } else case e in #( e) { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_prog_cc_c11" >&5 printf "%s\n" "$ac_cv_prog_cc_c11" >&6; } CC="$CC $ac_cv_prog_cc_c11" ;; esac fi ac_cv_prog_cc_stdc=$ac_cv_prog_cc_c11 ac_prog_cc_stdc=c11 ;; esac fi fi if test x$ac_prog_cc_stdc = xno then : { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $CC option to enable C99 features" >&5 printf %s "checking for $CC option to enable C99 features... " >&6; } if test ${ac_cv_prog_cc_c99+y} then : printf %s "(cached) " >&6 else case e in #( e) ac_cv_prog_cc_c99=no ac_save_CC=$CC cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ $ac_c_conftest_c99_program _ACEOF for ac_arg in '' -std=gnu99 -std=c99 -c99 -qlanglvl=extc1x -qlanglvl=extc99 -AC99 -D_STDC_C99= do CC="$ac_save_CC $ac_arg" if ac_fn_c_try_compile "$LINENO" then : ac_cv_prog_cc_c99=$ac_arg fi rm -f core conftest.err conftest.$ac_objext conftest.beam test "x$ac_cv_prog_cc_c99" != "xno" && break done rm -f conftest.$ac_ext CC=$ac_save_CC ;; esac fi if test "x$ac_cv_prog_cc_c99" = xno then : { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: unsupported" >&5 printf "%s\n" "unsupported" >&6; } else case e in #( e) if test "x$ac_cv_prog_cc_c99" = x then : { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: none needed" >&5 printf "%s\n" "none needed" >&6; } else case e in #( e) { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_prog_cc_c99" >&5 printf "%s\n" "$ac_cv_prog_cc_c99" >&6; } CC="$CC $ac_cv_prog_cc_c99" ;; esac fi ac_cv_prog_cc_stdc=$ac_cv_prog_cc_c99 ac_prog_cc_stdc=c99 ;; esac fi fi if test x$ac_prog_cc_stdc = xno then : { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $CC option to enable C89 features" >&5 printf %s "checking for $CC option to enable C89 features... " >&6; } if test ${ac_cv_prog_cc_c89+y} then : printf %s "(cached) " >&6 else case e in #( e) ac_cv_prog_cc_c89=no ac_save_CC=$CC cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ $ac_c_conftest_c89_program _ACEOF for ac_arg in '' -qlanglvl=extc89 -qlanglvl=ansi -std -Ae "-Aa -D_HPUX_SOURCE" "-Xc -D__EXTENSIONS__" do CC="$ac_save_CC $ac_arg" if ac_fn_c_try_compile "$LINENO" then : ac_cv_prog_cc_c89=$ac_arg fi rm -f core conftest.err conftest.$ac_objext conftest.beam test "x$ac_cv_prog_cc_c89" != "xno" && break done rm -f conftest.$ac_ext CC=$ac_save_CC ;; esac fi if test "x$ac_cv_prog_cc_c89" = xno then : { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: unsupported" >&5 printf "%s\n" "unsupported" >&6; } else case e in #( e) if test "x$ac_cv_prog_cc_c89" = x then : { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: none needed" >&5 printf "%s\n" "none needed" >&6; } else case e in #( e) { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_prog_cc_c89" >&5 printf "%s\n" "$ac_cv_prog_cc_c89" >&6; } CC="$CC $ac_cv_prog_cc_c89" ;; esac fi ac_cv_prog_cc_stdc=$ac_cv_prog_cc_c89 ac_prog_cc_stdc=c89 ;; esac fi fi ac_ext=c ac_cpp='$CPP $CPPFLAGS' ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' ac_compiler_gnu=$ac_cv_c_compiler_gnu ac_ext=c ac_cpp='$CPP $CPPFLAGS' ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' ac_compiler_gnu=$ac_cv_c_compiler_gnu { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking whether $CC understands -c and -o together" >&5 printf %s "checking whether $CC understands -c and -o together... " >&6; } if test ${am_cv_prog_cc_c_o+y} then : printf %s "(cached) " >&6 else case e in #( e) cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ int main (void) { ; return 0; } _ACEOF # Make sure it works both with $CC and with simple cc. # Following AC_PROG_CC_C_O, we do the test twice because some # compilers refuse to overwrite an existing .o file with -o, # though they will create one. am_cv_prog_cc_c_o=yes for am_i in 1 2; do if { echo "$as_me:$LINENO: $CC -c conftest.$ac_ext -o conftest2.$ac_objext" >&5 ($CC -c conftest.$ac_ext -o conftest2.$ac_objext) >&5 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } \ && test -f conftest2.$ac_objext; then : OK else am_cv_prog_cc_c_o=no break fi done rm -f core conftest* unset am_i ;; esac fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $am_cv_prog_cc_c_o" >&5 printf "%s\n" "$am_cv_prog_cc_c_o" >&6; } if test "$am_cv_prog_cc_c_o" != yes; then # Losing compiler, so override with the script. # FIXME: It is wrong to rewrite CC. # But if we don't then we get into trouble of one sort or another. # A longer-term fix would be to have automake use am__CC in this case, # and then we could set am__CC="\$(top_srcdir)/compile \$(CC)" CC="$am_aux_dir/compile $CC" fi ac_ext=c ac_cpp='$CPP $CPPFLAGS' ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' ac_compiler_gnu=$ac_cv_c_compiler_gnu depcc="$CC" am_compiler_list= { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking dependency style of $depcc" >&5 printf %s "checking dependency style of $depcc... " >&6; } if test ${am_cv_CC_dependencies_compiler_type+y} then : printf %s "(cached) " >&6 else case e in #( e) if test -z "$AMDEP_TRUE" && test -f "$am_depcomp"; then # We make a subdir and do the tests there. Otherwise we can end up # making bogus files that we don't know about and never remove. For # instance it was reported that on HP-UX the gcc test will end up # making a dummy file named 'D' -- because '-MD' means "put the output # in D". rm -rf conftest.dir mkdir conftest.dir # Copy depcomp to subdir because otherwise we won't find it if we're # using a relative directory. cp "$am_depcomp" conftest.dir cd conftest.dir # We will build objects and dependencies in a subdirectory because # it helps to detect inapplicable dependency modes. For instance # both Tru64's cc and ICC support -MD to output dependencies as a # side effect of compilation, but ICC will put the dependencies in # the current directory while Tru64 will put them in the object # directory. mkdir sub am_cv_CC_dependencies_compiler_type=none if test "$am_compiler_list" = ""; then am_compiler_list=`sed -n 's/^#*\([a-zA-Z0-9]*\))$/\1/p' < ./depcomp` fi am__universal=false case " $depcc " in #( *\ -arch\ *\ -arch\ *) am__universal=true ;; esac for depmode in $am_compiler_list; do # Setup a source with many dependencies, because some compilers # like to wrap large dependency lists on column 80 (with \), and # we should not choose a depcomp mode which is confused by this. # # We need to recreate these files for each test, as the compiler may # overwrite some of them when testing with obscure command lines. # This happens at least with the AIX C compiler. : > sub/conftest.c for i in 1 2 3 4 5 6; do echo '#include "conftst'$i'.h"' >> sub/conftest.c # Using ": > sub/conftst$i.h" creates only sub/conftst1.h with # Solaris 10 /bin/sh. echo '/* dummy */' > sub/conftst$i.h done echo "${am__include} ${am__quote}sub/conftest.Po${am__quote}" > confmf # We check with '-c' and '-o' for the sake of the "dashmstdout" # mode. It turns out that the SunPro C++ compiler does not properly # handle '-M -o', and we need to detect this. Also, some Intel # versions had trouble with output in subdirs. am__obj=sub/conftest.${OBJEXT-o} am__minus_obj="-o $am__obj" case $depmode in gcc) # This depmode causes a compiler race in universal mode. test "$am__universal" = false || continue ;; nosideeffect) # After this tag, mechanisms are not by side-effect, so they'll # only be used when explicitly requested. if test "x$enable_dependency_tracking" = xyes; then continue else break fi ;; msvc7 | msvc7msys | msvisualcpp | msvcmsys) # This compiler won't grok '-c -o', but also, the minuso test has # not run yet. These depmodes are late enough in the game, and # so weak that their functioning should not be impacted. am__obj=conftest.${OBJEXT-o} am__minus_obj= ;; none) break ;; esac if depmode=$depmode \ source=sub/conftest.c object=$am__obj \ depfile=sub/conftest.Po tmpdepfile=sub/conftest.TPo \ $SHELL ./depcomp $depcc -c $am__minus_obj sub/conftest.c \ >/dev/null 2>conftest.err && grep sub/conftst1.h sub/conftest.Po > /dev/null 2>&1 && grep sub/conftst6.h sub/conftest.Po > /dev/null 2>&1 && grep $am__obj sub/conftest.Po > /dev/null 2>&1 && ${MAKE-make} -s -f confmf > /dev/null 2>&1; then # icc doesn't choke on unknown options, it will just issue warnings # or remarks (even with -Werror). So we grep stderr for any message # that says an option was ignored or not supported. # When given -MP, icc 7.0 and 7.1 complain thusly: # icc: Command line warning: ignoring option '-M'; no argument required # The diagnosis changed in icc 8.0: # icc: Command line remark: option '-MP' not supported if (grep 'ignoring option' conftest.err || grep 'not supported' conftest.err) >/dev/null 2>&1; then :; else am_cv_CC_dependencies_compiler_type=$depmode break fi fi done cd .. rm -rf conftest.dir else am_cv_CC_dependencies_compiler_type=none fi ;; esac fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $am_cv_CC_dependencies_compiler_type" >&5 printf "%s\n" "$am_cv_CC_dependencies_compiler_type" >&6; } CCDEPMODE=depmode=$am_cv_CC_dependencies_compiler_type if test "x$enable_dependency_tracking" != xno \ && test "$am_cv_CC_dependencies_compiler_type" = gcc3; then am__fastdepCC_TRUE= am__fastdepCC_FALSE='#' else am__fastdepCC_TRUE='#' am__fastdepCC_FALSE= fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking whether ${CC-cc} accepts -fPIE" >&5 printf %s "checking whether ${CC-cc} accepts -fPIE... " >&6; } if test ${ac_cv_prog_cc_pie+y} then : printf %s "(cached) " >&6 else case e in #( e) echo 'void f(){}' > conftest.c if test -z "`${CC-cc} -fPIE -pie -c conftest.c 2>&1`"; then ac_cv_prog_cc_pie=yes else ac_cv_prog_cc_pie=no fi rm -rf conftest* ;; esac fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_prog_cc_pie" >&5 printf "%s\n" "$ac_cv_prog_cc_pie" >&6; } { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking whether ${CC-cc} accepts -fsanitize=address" >&5 printf %s "checking whether ${CC-cc} accepts -fsanitize=address... " >&6; } if test ${ac_cv_prog_cc_asan+y} then : printf %s "(cached) " >&6 else case e in #( e) echo 'void f(){}' > asan.c if test -z "`${CC-cc} -fsanitize=address -c asan.c 2>&1`"; then ac_cv_prog_cc_asan=yes else ac_cv_prog_cc_asan=no fi rm -rf asan* ;; esac fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_prog_cc_asan" >&5 printf "%s\n" "$ac_cv_prog_cc_asan" >&6; } { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking whether ${CC-cc} accepts -fsanitize=leak" >&5 printf %s "checking whether ${CC-cc} accepts -fsanitize=leak... " >&6; } if test ${ac_cv_prog_cc_lsan+y} then : printf %s "(cached) " >&6 else case e in #( e) echo 'void f(){}' > lsan.c if test -z "`${CC-cc} -fsanitize=leak -c lsan.c 2>&1`"; then ac_cv_prog_cc_lsan=yes else ac_cv_prog_cc_lsan=no fi rm -rf lsan* ;; esac fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_prog_cc_lsan" >&5 printf "%s\n" "$ac_cv_prog_cc_lsan" >&6; } { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking whether ${CC-cc} accepts -fsanitize=undefined" >&5 printf %s "checking whether ${CC-cc} accepts -fsanitize=undefined... " >&6; } if test ${ac_cv_prog_cc_ubsan+y} then : printf %s "(cached) " >&6 else case e in #( e) echo 'void f(){}' > ubsan.c if test -z "`${CC-cc} -fsanitize=undefined -c ubsan.c 2>&1`"; then ac_cv_prog_cc_ubsan=yes else ac_cv_prog_cc_ubsan=no fi rm -rf ubsan* ;; esac fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_prog_cc_ubsan" >&5 printf "%s\n" "$ac_cv_prog_cc_ubsan" >&6; } case `pwd` in *\ * | *\ *) { printf "%s\n" "$as_me:${as_lineno-$LINENO}: WARNING: Libtool does not cope well with whitespace in \`pwd\`" >&5 printf "%s\n" "$as_me: WARNING: Libtool does not cope well with whitespace in \`pwd\`" >&2;} ;; esac macro_version='2.4.7' macro_revision='2.4.7' ltmain=$ac_aux_dir/ltmain.sh # Make sure we can run config.sub. $SHELL "${ac_aux_dir}config.sub" sun4 >/dev/null 2>&1 || as_fn_error $? "cannot run $SHELL ${ac_aux_dir}config.sub" "$LINENO" 5 { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking build system type" >&5 printf %s "checking build system type... " >&6; } if test ${ac_cv_build+y} then : printf %s "(cached) " >&6 else case e in #( e) ac_build_alias=$build_alias test "x$ac_build_alias" = x && ac_build_alias=`$SHELL "${ac_aux_dir}config.guess"` test "x$ac_build_alias" = x && as_fn_error $? "cannot guess build type; you must specify one" "$LINENO" 5 ac_cv_build=`$SHELL "${ac_aux_dir}config.sub" $ac_build_alias` || as_fn_error $? "$SHELL ${ac_aux_dir}config.sub $ac_build_alias failed" "$LINENO" 5 ;; esac fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_build" >&5 printf "%s\n" "$ac_cv_build" >&6; } case $ac_cv_build in *-*-*) ;; *) as_fn_error $? "invalid value of canonical build" "$LINENO" 5;; esac build=$ac_cv_build ac_save_IFS=$IFS; IFS='-' set x $ac_cv_build shift build_cpu=$1 build_vendor=$2 shift; shift # Remember, the first character of IFS is used to create $*, # except with old shells: build_os=$* IFS=$ac_save_IFS case $build_os in *\ *) build_os=`echo "$build_os" | sed 's/ /-/g'`;; esac { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking host system type" >&5 printf %s "checking host system type... " >&6; } if test ${ac_cv_host+y} then : printf %s "(cached) " >&6 else case e in #( e) if test "x$host_alias" = x; then ac_cv_host=$ac_cv_build else ac_cv_host=`$SHELL "${ac_aux_dir}config.sub" $host_alias` || as_fn_error $? "$SHELL ${ac_aux_dir}config.sub $host_alias failed" "$LINENO" 5 fi ;; esac fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_host" >&5 printf "%s\n" "$ac_cv_host" >&6; } case $ac_cv_host in *-*-*) ;; *) as_fn_error $? "invalid value of canonical host" "$LINENO" 5;; esac host=$ac_cv_host ac_save_IFS=$IFS; IFS='-' set x $ac_cv_host shift host_cpu=$1 host_vendor=$2 shift; shift # Remember, the first character of IFS is used to create $*, # except with old shells: host_os=$* IFS=$ac_save_IFS case $host_os in *\ *) host_os=`echo "$host_os" | sed 's/ /-/g'`;; esac # Backslashify metacharacters that are still active within # double-quoted strings. sed_quote_subst='s/\(["`$\\]\)/\\\1/g' # Same as above, but do not quote variable references. double_quote_subst='s/\(["`\\]\)/\\\1/g' # Sed substitution to delay expansion of an escaped shell variable in a # double_quote_subst'ed string. delay_variable_subst='s/\\\\\\\\\\\$/\\\\\\$/g' # Sed substitution to delay expansion of an escaped single quote. delay_single_quote_subst='s/'\''/'\'\\\\\\\'\''/g' # Sed substitution to avoid accidental globbing in evaled expressions no_glob_subst='s/\*/\\\*/g' ECHO='\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\' ECHO=$ECHO$ECHO$ECHO$ECHO$ECHO ECHO=$ECHO$ECHO$ECHO$ECHO$ECHO$ECHO { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking how to print strings" >&5 printf %s "checking how to print strings... " >&6; } # Test print first, because it will be a builtin if present. if test "X`( print -r -- -n ) 2>/dev/null`" = X-n && \ test "X`print -r -- $ECHO 2>/dev/null`" = "X$ECHO"; then ECHO='print -r --' elif test "X`printf %s $ECHO 2>/dev/null`" = "X$ECHO"; then ECHO='printf %s\n' else # Use this function as a fallback that always works. func_fallback_echo () { eval 'cat <<_LTECHO_EOF $1 _LTECHO_EOF' } ECHO='func_fallback_echo' fi # func_echo_all arg... # Invoke $ECHO with all args, space-separated. func_echo_all () { $ECHO "" } case $ECHO in printf*) { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: printf" >&5 printf "%s\n" "printf" >&6; } ;; print*) { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: print -r" >&5 printf "%s\n" "print -r" >&6; } ;; *) { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: cat" >&5 printf "%s\n" "cat" >&6; } ;; esac { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for a sed that does not truncate output" >&5 printf %s "checking for a sed that does not truncate output... " >&6; } if test ${ac_cv_path_SED+y} then : printf %s "(cached) " >&6 else case e in #( e) ac_script=s/aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa/bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb/ for ac_i in 1 2 3 4 5 6 7; do ac_script="$ac_script$as_nl$ac_script" done echo "$ac_script" 2>/dev/null | sed 99q >conftest.sed { ac_script=; unset ac_script;} if test -z "$SED"; then ac_path_SED_found=false # Loop through the user's path and test for each of PROGNAME-LIST as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS case $as_dir in #((( '') as_dir=./ ;; */) ;; *) as_dir=$as_dir/ ;; esac for ac_prog in sed gsed do for ac_exec_ext in '' $ac_executable_extensions; do ac_path_SED="$as_dir$ac_prog$ac_exec_ext" as_fn_executable_p "$ac_path_SED" || continue # Check for GNU ac_path_SED and select it if it is found. # Check for GNU $ac_path_SED case `"$ac_path_SED" --version 2>&1` in #( *GNU*) ac_cv_path_SED="$ac_path_SED" ac_path_SED_found=:;; #( *) ac_count=0 printf %s 0123456789 >"conftest.in" while : do cat "conftest.in" "conftest.in" >"conftest.tmp" mv "conftest.tmp" "conftest.in" cp "conftest.in" "conftest.nl" printf "%s\n" '' >> "conftest.nl" "$ac_path_SED" -f conftest.sed < "conftest.nl" >"conftest.out" 2>/dev/null || break diff "conftest.out" "conftest.nl" >/dev/null 2>&1 || break as_fn_arith $ac_count + 1 && ac_count=$as_val if test $ac_count -gt ${ac_path_SED_max-0}; then # Best one so far, save it but keep looking for a better one ac_cv_path_SED="$ac_path_SED" ac_path_SED_max=$ac_count fi # 10*(2^10) chars as input seems more than enough test $ac_count -gt 10 && break done rm -f conftest.in conftest.tmp conftest.nl conftest.out;; esac $ac_path_SED_found && break 3 done done done IFS=$as_save_IFS if test -z "$ac_cv_path_SED"; then as_fn_error $? "no acceptable sed could be found in \$PATH" "$LINENO" 5 fi else ac_cv_path_SED=$SED fi ;; esac fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_path_SED" >&5 printf "%s\n" "$ac_cv_path_SED" >&6; } SED="$ac_cv_path_SED" rm -f conftest.sed test -z "$SED" && SED=sed Xsed="$SED -e 1s/^X//" { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for grep that handles long lines and -e" >&5 printf %s "checking for grep that handles long lines and -e... " >&6; } if test ${ac_cv_path_GREP+y} then : printf %s "(cached) " >&6 else case e in #( e) if test -z "$GREP"; then ac_path_GREP_found=false # Loop through the user's path and test for each of PROGNAME-LIST as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH$PATH_SEPARATOR/usr/xpg4/bin do IFS=$as_save_IFS case $as_dir in #((( '') as_dir=./ ;; */) ;; *) as_dir=$as_dir/ ;; esac for ac_prog in grep ggrep do for ac_exec_ext in '' $ac_executable_extensions; do ac_path_GREP="$as_dir$ac_prog$ac_exec_ext" as_fn_executable_p "$ac_path_GREP" || continue # Check for GNU ac_path_GREP and select it if it is found. # Check for GNU $ac_path_GREP case `"$ac_path_GREP" --version 2>&1` in #( *GNU*) ac_cv_path_GREP="$ac_path_GREP" ac_path_GREP_found=:;; #( *) ac_count=0 printf %s 0123456789 >"conftest.in" while : do cat "conftest.in" "conftest.in" >"conftest.tmp" mv "conftest.tmp" "conftest.in" cp "conftest.in" "conftest.nl" printf "%s\n" 'GREP' >> "conftest.nl" "$ac_path_GREP" -e 'GREP$' -e '-(cannot match)-' < "conftest.nl" >"conftest.out" 2>/dev/null || break diff "conftest.out" "conftest.nl" >/dev/null 2>&1 || break as_fn_arith $ac_count + 1 && ac_count=$as_val if test $ac_count -gt ${ac_path_GREP_max-0}; then # Best one so far, save it but keep looking for a better one ac_cv_path_GREP="$ac_path_GREP" ac_path_GREP_max=$ac_count fi # 10*(2^10) chars as input seems more than enough test $ac_count -gt 10 && break done rm -f conftest.in conftest.tmp conftest.nl conftest.out;; esac $ac_path_GREP_found && break 3 done done done IFS=$as_save_IFS if test -z "$ac_cv_path_GREP"; then as_fn_error $? "no acceptable grep could be found in $PATH$PATH_SEPARATOR/usr/xpg4/bin" "$LINENO" 5 fi else ac_cv_path_GREP=$GREP fi ;; esac fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_path_GREP" >&5 printf "%s\n" "$ac_cv_path_GREP" >&6; } GREP="$ac_cv_path_GREP" { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for egrep" >&5 printf %s "checking for egrep... " >&6; } if test ${ac_cv_path_EGREP+y} then : printf %s "(cached) " >&6 else case e in #( e) if echo a | $GREP -E '(a|b)' >/dev/null 2>&1 then ac_cv_path_EGREP="$GREP -E" else if test -z "$EGREP"; then ac_path_EGREP_found=false # Loop through the user's path and test for each of PROGNAME-LIST as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH$PATH_SEPARATOR/usr/xpg4/bin do IFS=$as_save_IFS case $as_dir in #((( '') as_dir=./ ;; */) ;; *) as_dir=$as_dir/ ;; esac for ac_prog in egrep do for ac_exec_ext in '' $ac_executable_extensions; do ac_path_EGREP="$as_dir$ac_prog$ac_exec_ext" as_fn_executable_p "$ac_path_EGREP" || continue # Check for GNU ac_path_EGREP and select it if it is found. # Check for GNU $ac_path_EGREP case `"$ac_path_EGREP" --version 2>&1` in #( *GNU*) ac_cv_path_EGREP="$ac_path_EGREP" ac_path_EGREP_found=:;; #( *) ac_count=0 printf %s 0123456789 >"conftest.in" while : do cat "conftest.in" "conftest.in" >"conftest.tmp" mv "conftest.tmp" "conftest.in" cp "conftest.in" "conftest.nl" printf "%s\n" 'EGREP' >> "conftest.nl" "$ac_path_EGREP" 'EGREP$' < "conftest.nl" >"conftest.out" 2>/dev/null || break diff "conftest.out" "conftest.nl" >/dev/null 2>&1 || break as_fn_arith $ac_count + 1 && ac_count=$as_val if test $ac_count -gt ${ac_path_EGREP_max-0}; then # Best one so far, save it but keep looking for a better one ac_cv_path_EGREP="$ac_path_EGREP" ac_path_EGREP_max=$ac_count fi # 10*(2^10) chars as input seems more than enough test $ac_count -gt 10 && break done rm -f conftest.in conftest.tmp conftest.nl conftest.out;; esac $ac_path_EGREP_found && break 3 done done done IFS=$as_save_IFS if test -z "$ac_cv_path_EGREP"; then as_fn_error $? "no acceptable egrep could be found in $PATH$PATH_SEPARATOR/usr/xpg4/bin" "$LINENO" 5 fi else ac_cv_path_EGREP=$EGREP fi fi ;; esac fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_path_EGREP" >&5 printf "%s\n" "$ac_cv_path_EGREP" >&6; } EGREP="$ac_cv_path_EGREP" EGREP_TRADITIONAL=$EGREP ac_cv_path_EGREP_TRADITIONAL=$EGREP { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for fgrep" >&5 printf %s "checking for fgrep... " >&6; } if test ${ac_cv_path_FGREP+y} then : printf %s "(cached) " >&6 else case e in #( e) if echo 'ab*c' | $GREP -F 'ab*c' >/dev/null 2>&1 then ac_cv_path_FGREP="$GREP -F" else if test -z "$FGREP"; then ac_path_FGREP_found=false # Loop through the user's path and test for each of PROGNAME-LIST as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH$PATH_SEPARATOR/usr/xpg4/bin do IFS=$as_save_IFS case $as_dir in #((( '') as_dir=./ ;; */) ;; *) as_dir=$as_dir/ ;; esac for ac_prog in fgrep do for ac_exec_ext in '' $ac_executable_extensions; do ac_path_FGREP="$as_dir$ac_prog$ac_exec_ext" as_fn_executable_p "$ac_path_FGREP" || continue # Check for GNU ac_path_FGREP and select it if it is found. # Check for GNU $ac_path_FGREP case `"$ac_path_FGREP" --version 2>&1` in #( *GNU*) ac_cv_path_FGREP="$ac_path_FGREP" ac_path_FGREP_found=:;; #( *) ac_count=0 printf %s 0123456789 >"conftest.in" while : do cat "conftest.in" "conftest.in" >"conftest.tmp" mv "conftest.tmp" "conftest.in" cp "conftest.in" "conftest.nl" printf "%s\n" 'FGREP' >> "conftest.nl" "$ac_path_FGREP" FGREP < "conftest.nl" >"conftest.out" 2>/dev/null || break diff "conftest.out" "conftest.nl" >/dev/null 2>&1 || break as_fn_arith $ac_count + 1 && ac_count=$as_val if test $ac_count -gt ${ac_path_FGREP_max-0}; then # Best one so far, save it but keep looking for a better one ac_cv_path_FGREP="$ac_path_FGREP" ac_path_FGREP_max=$ac_count fi # 10*(2^10) chars as input seems more than enough test $ac_count -gt 10 && break done rm -f conftest.in conftest.tmp conftest.nl conftest.out;; esac $ac_path_FGREP_found && break 3 done done done IFS=$as_save_IFS if test -z "$ac_cv_path_FGREP"; then as_fn_error $? "no acceptable fgrep could be found in $PATH$PATH_SEPARATOR/usr/xpg4/bin" "$LINENO" 5 fi else ac_cv_path_FGREP=$FGREP fi fi ;; esac fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_path_FGREP" >&5 printf "%s\n" "$ac_cv_path_FGREP" >&6; } FGREP="$ac_cv_path_FGREP" test -z "$GREP" && GREP=grep # Check whether --with-gnu-ld was given. if test ${with_gnu_ld+y} then : withval=$with_gnu_ld; test no = "$withval" || with_gnu_ld=yes else case e in #( e) with_gnu_ld=no ;; esac fi ac_prog=ld if test yes = "$GCC"; then # Check if gcc -print-prog-name=ld gives a path. { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for ld used by $CC" >&5 printf %s "checking for ld used by $CC... " >&6; } case $host in *-*-mingw*) # gcc leaves a trailing carriage return, which upsets mingw ac_prog=`($CC -print-prog-name=ld) 2>&5 | tr -d '\015'` ;; *) ac_prog=`($CC -print-prog-name=ld) 2>&5` ;; esac case $ac_prog in # Accept absolute paths. [\\/]* | ?:[\\/]*) re_direlt='/[^/][^/]*/\.\./' # Canonicalize the pathname of ld ac_prog=`$ECHO "$ac_prog"| $SED 's%\\\\%/%g'` while $ECHO "$ac_prog" | $GREP "$re_direlt" > /dev/null 2>&1; do ac_prog=`$ECHO $ac_prog| $SED "s%$re_direlt%/%"` done test -z "$LD" && LD=$ac_prog ;; "") # If it fails, then pretend we aren't using GCC. ac_prog=ld ;; *) # If it is relative, then search for the first ld in PATH. with_gnu_ld=unknown ;; esac elif test yes = "$with_gnu_ld"; then { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for GNU ld" >&5 printf %s "checking for GNU ld... " >&6; } else { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for non-GNU ld" >&5 printf %s "checking for non-GNU ld... " >&6; } fi if test ${lt_cv_path_LD+y} then : printf %s "(cached) " >&6 else case e in #( e) if test -z "$LD"; then lt_save_ifs=$IFS; IFS=$PATH_SEPARATOR for ac_dir in $PATH; do IFS=$lt_save_ifs test -z "$ac_dir" && ac_dir=. if test -f "$ac_dir/$ac_prog" || test -f "$ac_dir/$ac_prog$ac_exeext"; then lt_cv_path_LD=$ac_dir/$ac_prog # Check to see if the program is GNU ld. I'd rather use --version, # but apparently some variants of GNU ld only accept -v. # Break only if it was the GNU/non-GNU ld that we prefer. case `"$lt_cv_path_LD" -v 2>&1 &5 printf "%s\n" "$LD" >&6; } else { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 printf "%s\n" "no" >&6; } fi test -z "$LD" && as_fn_error $? "no acceptable ld found in \$PATH" "$LINENO" 5 { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking if the linker ($LD) is GNU ld" >&5 printf %s "checking if the linker ($LD) is GNU ld... " >&6; } if test ${lt_cv_prog_gnu_ld+y} then : printf %s "(cached) " >&6 else case e in #( e) # I'd rather use --version here, but apparently some GNU lds only accept -v. case `$LD -v 2>&1 &5 printf "%s\n" "$lt_cv_prog_gnu_ld" >&6; } with_gnu_ld=$lt_cv_prog_gnu_ld { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for BSD- or MS-compatible name lister (nm)" >&5 printf %s "checking for BSD- or MS-compatible name lister (nm)... " >&6; } if test ${lt_cv_path_NM+y} then : printf %s "(cached) " >&6 else case e in #( e) if test -n "$NM"; then # Let the user override the test. lt_cv_path_NM=$NM else lt_nm_to_check=${ac_tool_prefix}nm if test -n "$ac_tool_prefix" && test "$build" = "$host"; then lt_nm_to_check="$lt_nm_to_check nm" fi for lt_tmp_nm in $lt_nm_to_check; do lt_save_ifs=$IFS; IFS=$PATH_SEPARATOR for ac_dir in $PATH /usr/ccs/bin/elf /usr/ccs/bin /usr/ucb /bin; do IFS=$lt_save_ifs test -z "$ac_dir" && ac_dir=. tmp_nm=$ac_dir/$lt_tmp_nm if test -f "$tmp_nm" || test -f "$tmp_nm$ac_exeext"; then # Check to see if the nm accepts a BSD-compat flag. # Adding the 'sed 1q' prevents false positives on HP-UX, which says: # nm: unknown option "B" ignored # Tru64's nm complains that /dev/null is an invalid object file # MSYS converts /dev/null to NUL, MinGW nm treats NUL as empty case $build_os in mingw*) lt_bad_file=conftest.nm/nofile ;; *) lt_bad_file=/dev/null ;; esac case `"$tmp_nm" -B $lt_bad_file 2>&1 | $SED '1q'` in *$lt_bad_file* | *'Invalid file or object type'*) lt_cv_path_NM="$tmp_nm -B" break 2 ;; *) case `"$tmp_nm" -p /dev/null 2>&1 | $SED '1q'` in */dev/null*) lt_cv_path_NM="$tmp_nm -p" break 2 ;; *) lt_cv_path_NM=${lt_cv_path_NM="$tmp_nm"} # keep the first match, but continue # so that we can try to find one that supports BSD flags ;; esac ;; esac fi done IFS=$lt_save_ifs done : ${lt_cv_path_NM=no} fi ;; esac fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $lt_cv_path_NM" >&5 printf "%s\n" "$lt_cv_path_NM" >&6; } if test no != "$lt_cv_path_NM"; then NM=$lt_cv_path_NM else # Didn't find any BSD compatible name lister, look for dumpbin. if test -n "$DUMPBIN"; then : # Let the user override the test. else if test -n "$ac_tool_prefix"; then for ac_prog in dumpbin "link -dump" do # Extract the first word of "$ac_tool_prefix$ac_prog", so it can be a program name with args. set dummy $ac_tool_prefix$ac_prog; ac_word=$2 { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 printf %s "checking for $ac_word... " >&6; } if test ${ac_cv_prog_DUMPBIN+y} then : printf %s "(cached) " >&6 else case e in #( e) if test -n "$DUMPBIN"; then ac_cv_prog_DUMPBIN="$DUMPBIN" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS case $as_dir in #((( '') as_dir=./ ;; */) ;; *) as_dir=$as_dir/ ;; esac for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir$ac_word$ac_exec_ext"; then ac_cv_prog_DUMPBIN="$ac_tool_prefix$ac_prog" printf "%s\n" "$as_me:${as_lineno-$LINENO}: found $as_dir$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi ;; esac fi DUMPBIN=$ac_cv_prog_DUMPBIN if test -n "$DUMPBIN"; then { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $DUMPBIN" >&5 printf "%s\n" "$DUMPBIN" >&6; } else { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 printf "%s\n" "no" >&6; } fi test -n "$DUMPBIN" && break done fi if test -z "$DUMPBIN"; then ac_ct_DUMPBIN=$DUMPBIN for ac_prog in dumpbin "link -dump" do # Extract the first word of "$ac_prog", so it can be a program name with args. set dummy $ac_prog; ac_word=$2 { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 printf %s "checking for $ac_word... " >&6; } if test ${ac_cv_prog_ac_ct_DUMPBIN+y} then : printf %s "(cached) " >&6 else case e in #( e) if test -n "$ac_ct_DUMPBIN"; then ac_cv_prog_ac_ct_DUMPBIN="$ac_ct_DUMPBIN" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS case $as_dir in #((( '') as_dir=./ ;; */) ;; *) as_dir=$as_dir/ ;; esac for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir$ac_word$ac_exec_ext"; then ac_cv_prog_ac_ct_DUMPBIN="$ac_prog" printf "%s\n" "$as_me:${as_lineno-$LINENO}: found $as_dir$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi ;; esac fi ac_ct_DUMPBIN=$ac_cv_prog_ac_ct_DUMPBIN if test -n "$ac_ct_DUMPBIN"; then { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_ct_DUMPBIN" >&5 printf "%s\n" "$ac_ct_DUMPBIN" >&6; } else { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 printf "%s\n" "no" >&6; } fi test -n "$ac_ct_DUMPBIN" && break done if test "x$ac_ct_DUMPBIN" = x; then DUMPBIN=":" else case $cross_compiling:$ac_tool_warned in yes:) { printf "%s\n" "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 printf "%s\n" "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} ac_tool_warned=yes ;; esac DUMPBIN=$ac_ct_DUMPBIN fi fi case `$DUMPBIN -symbols -headers /dev/null 2>&1 | $SED '1q'` in *COFF*) DUMPBIN="$DUMPBIN -symbols -headers" ;; *) DUMPBIN=: ;; esac fi if test : != "$DUMPBIN"; then NM=$DUMPBIN fi fi test -z "$NM" && NM=nm { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking the name lister ($NM) interface" >&5 printf %s "checking the name lister ($NM) interface... " >&6; } if test ${lt_cv_nm_interface+y} then : printf %s "(cached) " >&6 else case e in #( e) lt_cv_nm_interface="BSD nm" echo "int some_variable = 0;" > conftest.$ac_ext (eval echo "\"\$as_me:$LINENO: $ac_compile\"" >&5) (eval "$ac_compile" 2>conftest.err) cat conftest.err >&5 (eval echo "\"\$as_me:$LINENO: $NM \\\"conftest.$ac_objext\\\"\"" >&5) (eval "$NM \"conftest.$ac_objext\"" 2>conftest.err > conftest.out) cat conftest.err >&5 (eval echo "\"\$as_me:$LINENO: output\"" >&5) cat conftest.out >&5 if $GREP 'External.*some_variable' conftest.out > /dev/null; then lt_cv_nm_interface="MS dumpbin" fi rm -f conftest* ;; esac fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $lt_cv_nm_interface" >&5 printf "%s\n" "$lt_cv_nm_interface" >&6; } { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking whether ln -s works" >&5 printf %s "checking whether ln -s works... " >&6; } LN_S=$as_ln_s if test "$LN_S" = "ln -s"; then { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: yes" >&5 printf "%s\n" "yes" >&6; } else { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no, using $LN_S" >&5 printf "%s\n" "no, using $LN_S" >&6; } fi # find the maximum length of command line arguments { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking the maximum length of command line arguments" >&5 printf %s "checking the maximum length of command line arguments... " >&6; } if test ${lt_cv_sys_max_cmd_len+y} then : printf %s "(cached) " >&6 else case e in #( e) i=0 teststring=ABCD case $build_os in msdosdjgpp*) # On DJGPP, this test can blow up pretty badly due to problems in libc # (any single argument exceeding 2000 bytes causes a buffer overrun # during glob expansion). Even if it were fixed, the result of this # check would be larger than it should be. lt_cv_sys_max_cmd_len=12288; # 12K is about right ;; gnu*) # Under GNU Hurd, this test is not required because there is # no limit to the length of command line arguments. # Libtool will interpret -1 as no limit whatsoever lt_cv_sys_max_cmd_len=-1; ;; cygwin* | mingw* | cegcc*) # On Win9x/ME, this test blows up -- it succeeds, but takes # about 5 minutes as the teststring grows exponentially. # Worse, since 9x/ME are not pre-emptively multitasking, # you end up with a "frozen" computer, even though with patience # the test eventually succeeds (with a max line length of 256k). # Instead, let's just punt: use the minimum linelength reported by # all of the supported platforms: 8192 (on NT/2K/XP). lt_cv_sys_max_cmd_len=8192; ;; mint*) # On MiNT this can take a long time and run out of memory. lt_cv_sys_max_cmd_len=8192; ;; amigaos*) # On AmigaOS with pdksh, this test takes hours, literally. # So we just punt and use a minimum line length of 8192. lt_cv_sys_max_cmd_len=8192; ;; bitrig* | darwin* | dragonfly* | freebsd* | midnightbsd* | netbsd* | openbsd*) # This has been around since 386BSD, at least. Likely further. if test -x /sbin/sysctl; then lt_cv_sys_max_cmd_len=`/sbin/sysctl -n kern.argmax` elif test -x /usr/sbin/sysctl; then lt_cv_sys_max_cmd_len=`/usr/sbin/sysctl -n kern.argmax` else lt_cv_sys_max_cmd_len=65536 # usable default for all BSDs fi # And add a safety zone lt_cv_sys_max_cmd_len=`expr $lt_cv_sys_max_cmd_len \/ 4` lt_cv_sys_max_cmd_len=`expr $lt_cv_sys_max_cmd_len \* 3` ;; interix*) # We know the value 262144 and hardcode it with a safety zone (like BSD) lt_cv_sys_max_cmd_len=196608 ;; os2*) # The test takes a long time on OS/2. lt_cv_sys_max_cmd_len=8192 ;; osf*) # Dr. Hans Ekkehard Plesser reports seeing a kernel panic running configure # due to this test when exec_disable_arg_limit is 1 on Tru64. It is not # nice to cause kernel panics so lets avoid the loop below. # First set a reasonable default. lt_cv_sys_max_cmd_len=16384 # if test -x /sbin/sysconfig; then case `/sbin/sysconfig -q proc exec_disable_arg_limit` in *1*) lt_cv_sys_max_cmd_len=-1 ;; esac fi ;; sco3.2v5*) lt_cv_sys_max_cmd_len=102400 ;; sysv5* | sco5v6* | sysv4.2uw2*) kargmax=`grep ARG_MAX /etc/conf/cf.d/stune 2>/dev/null` if test -n "$kargmax"; then lt_cv_sys_max_cmd_len=`echo $kargmax | $SED 's/.*[ ]//'` else lt_cv_sys_max_cmd_len=32768 fi ;; *) lt_cv_sys_max_cmd_len=`(getconf ARG_MAX) 2> /dev/null` if test -n "$lt_cv_sys_max_cmd_len" && \ test undefined != "$lt_cv_sys_max_cmd_len"; then lt_cv_sys_max_cmd_len=`expr $lt_cv_sys_max_cmd_len \/ 4` lt_cv_sys_max_cmd_len=`expr $lt_cv_sys_max_cmd_len \* 3` else # Make teststring a little bigger before we do anything with it. # a 1K string should be a reasonable start. for i in 1 2 3 4 5 6 7 8; do teststring=$teststring$teststring done SHELL=${SHELL-${CONFIG_SHELL-/bin/sh}} # If test is not a shell built-in, we'll probably end up computing a # maximum length that is only half of the actual maximum length, but # we can't tell. while { test X`env echo "$teststring$teststring" 2>/dev/null` \ = "X$teststring$teststring"; } >/dev/null 2>&1 && test 17 != "$i" # 1/2 MB should be enough do i=`expr $i + 1` teststring=$teststring$teststring done # Only check the string length outside the loop. lt_cv_sys_max_cmd_len=`expr "X$teststring" : ".*" 2>&1` teststring= # Add a significant safety factor because C++ compilers can tack on # massive amounts of additional arguments before passing them to the # linker. It appears as though 1/2 is a usable value. lt_cv_sys_max_cmd_len=`expr $lt_cv_sys_max_cmd_len \/ 2` fi ;; esac ;; esac fi if test -n "$lt_cv_sys_max_cmd_len"; then { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $lt_cv_sys_max_cmd_len" >&5 printf "%s\n" "$lt_cv_sys_max_cmd_len" >&6; } else { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: none" >&5 printf "%s\n" "none" >&6; } fi max_cmd_len=$lt_cv_sys_max_cmd_len : ${CP="cp -f"} : ${MV="mv -f"} : ${RM="rm -f"} if ( (MAIL=60; unset MAIL) || exit) >/dev/null 2>&1; then lt_unset=unset else lt_unset=false fi # test EBCDIC or ASCII case `echo X|tr X '\101'` in A) # ASCII based system # \n is not interpreted correctly by Solaris 8 /usr/ucb/tr lt_SP2NL='tr \040 \012' lt_NL2SP='tr \015\012 \040\040' ;; *) # EBCDIC based system lt_SP2NL='tr \100 \n' lt_NL2SP='tr \r\n \100\100' ;; esac { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking how to convert $build file names to $host format" >&5 printf %s "checking how to convert $build file names to $host format... " >&6; } if test ${lt_cv_to_host_file_cmd+y} then : printf %s "(cached) " >&6 else case e in #( e) case $host in *-*-mingw* ) case $build in *-*-mingw* ) # actually msys lt_cv_to_host_file_cmd=func_convert_file_msys_to_w32 ;; *-*-cygwin* ) lt_cv_to_host_file_cmd=func_convert_file_cygwin_to_w32 ;; * ) # otherwise, assume *nix lt_cv_to_host_file_cmd=func_convert_file_nix_to_w32 ;; esac ;; *-*-cygwin* ) case $build in *-*-mingw* ) # actually msys lt_cv_to_host_file_cmd=func_convert_file_msys_to_cygwin ;; *-*-cygwin* ) lt_cv_to_host_file_cmd=func_convert_file_noop ;; * ) # otherwise, assume *nix lt_cv_to_host_file_cmd=func_convert_file_nix_to_cygwin ;; esac ;; * ) # unhandled hosts (and "normal" native builds) lt_cv_to_host_file_cmd=func_convert_file_noop ;; esac ;; esac fi to_host_file_cmd=$lt_cv_to_host_file_cmd { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $lt_cv_to_host_file_cmd" >&5 printf "%s\n" "$lt_cv_to_host_file_cmd" >&6; } { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking how to convert $build file names to toolchain format" >&5 printf %s "checking how to convert $build file names to toolchain format... " >&6; } if test ${lt_cv_to_tool_file_cmd+y} then : printf %s "(cached) " >&6 else case e in #( e) #assume ordinary cross tools, or native build. lt_cv_to_tool_file_cmd=func_convert_file_noop case $host in *-*-mingw* ) case $build in *-*-mingw* ) # actually msys lt_cv_to_tool_file_cmd=func_convert_file_msys_to_w32 ;; esac ;; esac ;; esac fi to_tool_file_cmd=$lt_cv_to_tool_file_cmd { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $lt_cv_to_tool_file_cmd" >&5 printf "%s\n" "$lt_cv_to_tool_file_cmd" >&6; } { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $LD option to reload object files" >&5 printf %s "checking for $LD option to reload object files... " >&6; } if test ${lt_cv_ld_reload_flag+y} then : printf %s "(cached) " >&6 else case e in #( e) lt_cv_ld_reload_flag='-r' ;; esac fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $lt_cv_ld_reload_flag" >&5 printf "%s\n" "$lt_cv_ld_reload_flag" >&6; } reload_flag=$lt_cv_ld_reload_flag case $reload_flag in "" | " "*) ;; *) reload_flag=" $reload_flag" ;; esac reload_cmds='$LD$reload_flag -o $output$reload_objs' case $host_os in cygwin* | mingw* | pw32* | cegcc*) if test yes != "$GCC"; then reload_cmds=false fi ;; darwin*) if test yes = "$GCC"; then reload_cmds='$LTCC $LTCFLAGS -nostdlib $wl-r -o $output$reload_objs' else reload_cmds='$LD$reload_flag -o $output$reload_objs' fi ;; esac if test -n "$ac_tool_prefix"; then # Extract the first word of "${ac_tool_prefix}file", so it can be a program name with args. set dummy ${ac_tool_prefix}file; ac_word=$2 { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 printf %s "checking for $ac_word... " >&6; } if test ${ac_cv_prog_FILECMD+y} then : printf %s "(cached) " >&6 else case e in #( e) if test -n "$FILECMD"; then ac_cv_prog_FILECMD="$FILECMD" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS case $as_dir in #((( '') as_dir=./ ;; */) ;; *) as_dir=$as_dir/ ;; esac for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir$ac_word$ac_exec_ext"; then ac_cv_prog_FILECMD="${ac_tool_prefix}file" printf "%s\n" "$as_me:${as_lineno-$LINENO}: found $as_dir$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi ;; esac fi FILECMD=$ac_cv_prog_FILECMD if test -n "$FILECMD"; then { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $FILECMD" >&5 printf "%s\n" "$FILECMD" >&6; } else { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 printf "%s\n" "no" >&6; } fi fi if test -z "$ac_cv_prog_FILECMD"; then ac_ct_FILECMD=$FILECMD # Extract the first word of "file", so it can be a program name with args. set dummy file; ac_word=$2 { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 printf %s "checking for $ac_word... " >&6; } if test ${ac_cv_prog_ac_ct_FILECMD+y} then : printf %s "(cached) " >&6 else case e in #( e) if test -n "$ac_ct_FILECMD"; then ac_cv_prog_ac_ct_FILECMD="$ac_ct_FILECMD" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS case $as_dir in #((( '') as_dir=./ ;; */) ;; *) as_dir=$as_dir/ ;; esac for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir$ac_word$ac_exec_ext"; then ac_cv_prog_ac_ct_FILECMD="file" printf "%s\n" "$as_me:${as_lineno-$LINENO}: found $as_dir$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi ;; esac fi ac_ct_FILECMD=$ac_cv_prog_ac_ct_FILECMD if test -n "$ac_ct_FILECMD"; then { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_ct_FILECMD" >&5 printf "%s\n" "$ac_ct_FILECMD" >&6; } else { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 printf "%s\n" "no" >&6; } fi if test "x$ac_ct_FILECMD" = x; then FILECMD=":" else case $cross_compiling:$ac_tool_warned in yes:) { printf "%s\n" "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 printf "%s\n" "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} ac_tool_warned=yes ;; esac FILECMD=$ac_ct_FILECMD fi else FILECMD="$ac_cv_prog_FILECMD" fi if test -n "$ac_tool_prefix"; then # Extract the first word of "${ac_tool_prefix}objdump", so it can be a program name with args. set dummy ${ac_tool_prefix}objdump; ac_word=$2 { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 printf %s "checking for $ac_word... " >&6; } if test ${ac_cv_prog_OBJDUMP+y} then : printf %s "(cached) " >&6 else case e in #( e) if test -n "$OBJDUMP"; then ac_cv_prog_OBJDUMP="$OBJDUMP" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS case $as_dir in #((( '') as_dir=./ ;; */) ;; *) as_dir=$as_dir/ ;; esac for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir$ac_word$ac_exec_ext"; then ac_cv_prog_OBJDUMP="${ac_tool_prefix}objdump" printf "%s\n" "$as_me:${as_lineno-$LINENO}: found $as_dir$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi ;; esac fi OBJDUMP=$ac_cv_prog_OBJDUMP if test -n "$OBJDUMP"; then { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $OBJDUMP" >&5 printf "%s\n" "$OBJDUMP" >&6; } else { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 printf "%s\n" "no" >&6; } fi fi if test -z "$ac_cv_prog_OBJDUMP"; then ac_ct_OBJDUMP=$OBJDUMP # Extract the first word of "objdump", so it can be a program name with args. set dummy objdump; ac_word=$2 { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 printf %s "checking for $ac_word... " >&6; } if test ${ac_cv_prog_ac_ct_OBJDUMP+y} then : printf %s "(cached) " >&6 else case e in #( e) if test -n "$ac_ct_OBJDUMP"; then ac_cv_prog_ac_ct_OBJDUMP="$ac_ct_OBJDUMP" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS case $as_dir in #((( '') as_dir=./ ;; */) ;; *) as_dir=$as_dir/ ;; esac for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir$ac_word$ac_exec_ext"; then ac_cv_prog_ac_ct_OBJDUMP="objdump" printf "%s\n" "$as_me:${as_lineno-$LINENO}: found $as_dir$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi ;; esac fi ac_ct_OBJDUMP=$ac_cv_prog_ac_ct_OBJDUMP if test -n "$ac_ct_OBJDUMP"; then { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_ct_OBJDUMP" >&5 printf "%s\n" "$ac_ct_OBJDUMP" >&6; } else { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 printf "%s\n" "no" >&6; } fi if test "x$ac_ct_OBJDUMP" = x; then OBJDUMP="false" else case $cross_compiling:$ac_tool_warned in yes:) { printf "%s\n" "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 printf "%s\n" "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} ac_tool_warned=yes ;; esac OBJDUMP=$ac_ct_OBJDUMP fi else OBJDUMP="$ac_cv_prog_OBJDUMP" fi test -z "$OBJDUMP" && OBJDUMP=objdump { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking how to recognize dependent libraries" >&5 printf %s "checking how to recognize dependent libraries... " >&6; } if test ${lt_cv_deplibs_check_method+y} then : printf %s "(cached) " >&6 else case e in #( e) lt_cv_file_magic_cmd='$MAGIC_CMD' lt_cv_file_magic_test_file= lt_cv_deplibs_check_method='unknown' # Need to set the preceding variable on all platforms that support # interlibrary dependencies. # 'none' -- dependencies not supported. # 'unknown' -- same as none, but documents that we really don't know. # 'pass_all' -- all dependencies passed with no checks. # 'test_compile' -- check by making test program. # 'file_magic [[regex]]' -- check by looking for files in library path # that responds to the $file_magic_cmd with a given extended regex. # If you have 'file' or equivalent on your system and you're not sure # whether 'pass_all' will *always* work, you probably want this one. case $host_os in aix[4-9]*) lt_cv_deplibs_check_method=pass_all ;; beos*) lt_cv_deplibs_check_method=pass_all ;; bsdi[45]*) lt_cv_deplibs_check_method='file_magic ELF [0-9][0-9]*-bit [ML]SB (shared object|dynamic lib)' lt_cv_file_magic_cmd='$FILECMD -L' lt_cv_file_magic_test_file=/shlib/libc.so ;; cygwin*) # func_win32_libid is a shell function defined in ltmain.sh lt_cv_deplibs_check_method='file_magic ^x86 archive import|^x86 DLL' lt_cv_file_magic_cmd='func_win32_libid' ;; mingw* | pw32*) # Base MSYS/MinGW do not provide the 'file' command needed by # func_win32_libid shell function, so use a weaker test based on 'objdump', # unless we find 'file', for example because we are cross-compiling. if ( file / ) >/dev/null 2>&1; then lt_cv_deplibs_check_method='file_magic ^x86 archive import|^x86 DLL' lt_cv_file_magic_cmd='func_win32_libid' else # Keep this pattern in sync with the one in func_win32_libid. lt_cv_deplibs_check_method='file_magic file format (pei*-i386(.*architecture: i386)?|pe-arm-wince|pe-x86-64)' lt_cv_file_magic_cmd='$OBJDUMP -f' fi ;; cegcc*) # use the weaker test based on 'objdump'. See mingw*. lt_cv_deplibs_check_method='file_magic file format pe-arm-.*little(.*architecture: arm)?' lt_cv_file_magic_cmd='$OBJDUMP -f' ;; darwin* | rhapsody*) lt_cv_deplibs_check_method=pass_all ;; freebsd* | dragonfly* | midnightbsd*) if echo __ELF__ | $CC -E - | $GREP __ELF__ > /dev/null; then case $host_cpu in i*86 ) # Not sure whether the presence of OpenBSD here was a mistake. # Let's accept both of them until this is cleared up. lt_cv_deplibs_check_method='file_magic (FreeBSD|OpenBSD|DragonFly)/i[3-9]86 (compact )?demand paged shared library' lt_cv_file_magic_cmd=$FILECMD lt_cv_file_magic_test_file=`echo /usr/lib/libc.so.*` ;; esac else lt_cv_deplibs_check_method=pass_all fi ;; haiku*) lt_cv_deplibs_check_method=pass_all ;; hpux10.20* | hpux11*) lt_cv_file_magic_cmd=$FILECMD case $host_cpu in ia64*) lt_cv_deplibs_check_method='file_magic (s[0-9][0-9][0-9]|ELF-[0-9][0-9]) shared object file - IA64' lt_cv_file_magic_test_file=/usr/lib/hpux32/libc.so ;; hppa*64*) lt_cv_deplibs_check_method='file_magic (s[0-9][0-9][0-9]|ELF[ -][0-9][0-9])(-bit)?( [LM]SB)? shared object( file)?[, -]* PA-RISC [0-9]\.[0-9]' lt_cv_file_magic_test_file=/usr/lib/pa20_64/libc.sl ;; *) lt_cv_deplibs_check_method='file_magic (s[0-9][0-9][0-9]|PA-RISC[0-9]\.[0-9]) shared library' lt_cv_file_magic_test_file=/usr/lib/libc.sl ;; esac ;; interix[3-9]*) # PIC code is broken on Interix 3.x, that's why |\.a not |_pic\.a here lt_cv_deplibs_check_method='match_pattern /lib[^/]+(\.so|\.a)$' ;; irix5* | irix6* | nonstopux*) case $LD in *-32|*"-32 ") libmagic=32-bit;; *-n32|*"-n32 ") libmagic=N32;; *-64|*"-64 ") libmagic=64-bit;; *) libmagic=never-match;; esac lt_cv_deplibs_check_method=pass_all ;; # This must be glibc/ELF. linux* | k*bsd*-gnu | kopensolaris*-gnu | gnu*) lt_cv_deplibs_check_method=pass_all ;; netbsd* | netbsdelf*-gnu) if echo __ELF__ | $CC -E - | $GREP __ELF__ > /dev/null; then lt_cv_deplibs_check_method='match_pattern /lib[^/]+(\.so\.[0-9]+\.[0-9]+|_pic\.a)$' else lt_cv_deplibs_check_method='match_pattern /lib[^/]+(\.so|_pic\.a)$' fi ;; newos6*) lt_cv_deplibs_check_method='file_magic ELF [0-9][0-9]*-bit [ML]SB (executable|dynamic lib)' lt_cv_file_magic_cmd=$FILECMD lt_cv_file_magic_test_file=/usr/lib/libnls.so ;; *nto* | *qnx*) lt_cv_deplibs_check_method=pass_all ;; openbsd* | bitrig*) if test -z "`echo __ELF__ | $CC -E - | $GREP __ELF__`"; then lt_cv_deplibs_check_method='match_pattern /lib[^/]+(\.so\.[0-9]+\.[0-9]+|\.so|_pic\.a)$' else lt_cv_deplibs_check_method='match_pattern /lib[^/]+(\.so\.[0-9]+\.[0-9]+|_pic\.a)$' fi ;; osf3* | osf4* | osf5*) lt_cv_deplibs_check_method=pass_all ;; rdos*) lt_cv_deplibs_check_method=pass_all ;; solaris*) lt_cv_deplibs_check_method=pass_all ;; sysv5* | sco3.2v5* | sco5v6* | unixware* | OpenUNIX* | sysv4*uw2*) lt_cv_deplibs_check_method=pass_all ;; sysv4 | sysv4.3*) case $host_vendor in motorola) lt_cv_deplibs_check_method='file_magic ELF [0-9][0-9]*-bit [ML]SB (shared object|dynamic lib) M[0-9][0-9]* Version [0-9]' lt_cv_file_magic_test_file=`echo /usr/lib/libc.so*` ;; ncr) lt_cv_deplibs_check_method=pass_all ;; sequent) lt_cv_file_magic_cmd='/bin/file' lt_cv_deplibs_check_method='file_magic ELF [0-9][0-9]*-bit [LM]SB (shared object|dynamic lib )' ;; sni) lt_cv_file_magic_cmd='/bin/file' lt_cv_deplibs_check_method="file_magic ELF [0-9][0-9]*-bit [LM]SB dynamic lib" lt_cv_file_magic_test_file=/lib/libc.so ;; siemens) lt_cv_deplibs_check_method=pass_all ;; pc) lt_cv_deplibs_check_method=pass_all ;; esac ;; tpf*) lt_cv_deplibs_check_method=pass_all ;; os2*) lt_cv_deplibs_check_method=pass_all ;; esac ;; esac fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $lt_cv_deplibs_check_method" >&5 printf "%s\n" "$lt_cv_deplibs_check_method" >&6; } file_magic_glob= want_nocaseglob=no if test "$build" = "$host"; then case $host_os in mingw* | pw32*) if ( shopt | grep nocaseglob ) >/dev/null 2>&1; then want_nocaseglob=yes else file_magic_glob=`echo aAbBcCdDeEfFgGhHiIjJkKlLmMnNoOpPqQrRsStTuUvVwWxXyYzZ | $SED -e "s/\(..\)/s\/[\1]\/[\1]\/g;/g"` fi ;; esac fi file_magic_cmd=$lt_cv_file_magic_cmd deplibs_check_method=$lt_cv_deplibs_check_method test -z "$deplibs_check_method" && deplibs_check_method=unknown if test -n "$ac_tool_prefix"; then # Extract the first word of "${ac_tool_prefix}dlltool", so it can be a program name with args. set dummy ${ac_tool_prefix}dlltool; ac_word=$2 { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 printf %s "checking for $ac_word... " >&6; } if test ${ac_cv_prog_DLLTOOL+y} then : printf %s "(cached) " >&6 else case e in #( e) if test -n "$DLLTOOL"; then ac_cv_prog_DLLTOOL="$DLLTOOL" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS case $as_dir in #((( '') as_dir=./ ;; */) ;; *) as_dir=$as_dir/ ;; esac for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir$ac_word$ac_exec_ext"; then ac_cv_prog_DLLTOOL="${ac_tool_prefix}dlltool" printf "%s\n" "$as_me:${as_lineno-$LINENO}: found $as_dir$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi ;; esac fi DLLTOOL=$ac_cv_prog_DLLTOOL if test -n "$DLLTOOL"; then { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $DLLTOOL" >&5 printf "%s\n" "$DLLTOOL" >&6; } else { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 printf "%s\n" "no" >&6; } fi fi if test -z "$ac_cv_prog_DLLTOOL"; then ac_ct_DLLTOOL=$DLLTOOL # Extract the first word of "dlltool", so it can be a program name with args. set dummy dlltool; ac_word=$2 { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 printf %s "checking for $ac_word... " >&6; } if test ${ac_cv_prog_ac_ct_DLLTOOL+y} then : printf %s "(cached) " >&6 else case e in #( e) if test -n "$ac_ct_DLLTOOL"; then ac_cv_prog_ac_ct_DLLTOOL="$ac_ct_DLLTOOL" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS case $as_dir in #((( '') as_dir=./ ;; */) ;; *) as_dir=$as_dir/ ;; esac for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir$ac_word$ac_exec_ext"; then ac_cv_prog_ac_ct_DLLTOOL="dlltool" printf "%s\n" "$as_me:${as_lineno-$LINENO}: found $as_dir$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi ;; esac fi ac_ct_DLLTOOL=$ac_cv_prog_ac_ct_DLLTOOL if test -n "$ac_ct_DLLTOOL"; then { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_ct_DLLTOOL" >&5 printf "%s\n" "$ac_ct_DLLTOOL" >&6; } else { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 printf "%s\n" "no" >&6; } fi if test "x$ac_ct_DLLTOOL" = x; then DLLTOOL="false" else case $cross_compiling:$ac_tool_warned in yes:) { printf "%s\n" "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 printf "%s\n" "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} ac_tool_warned=yes ;; esac DLLTOOL=$ac_ct_DLLTOOL fi else DLLTOOL="$ac_cv_prog_DLLTOOL" fi test -z "$DLLTOOL" && DLLTOOL=dlltool { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking how to associate runtime and link libraries" >&5 printf %s "checking how to associate runtime and link libraries... " >&6; } if test ${lt_cv_sharedlib_from_linklib_cmd+y} then : printf %s "(cached) " >&6 else case e in #( e) lt_cv_sharedlib_from_linklib_cmd='unknown' case $host_os in cygwin* | mingw* | pw32* | cegcc*) # two different shell functions defined in ltmain.sh; # decide which one to use based on capabilities of $DLLTOOL case `$DLLTOOL --help 2>&1` in *--identify-strict*) lt_cv_sharedlib_from_linklib_cmd=func_cygming_dll_for_implib ;; *) lt_cv_sharedlib_from_linklib_cmd=func_cygming_dll_for_implib_fallback ;; esac ;; *) # fallback: assume linklib IS sharedlib lt_cv_sharedlib_from_linklib_cmd=$ECHO ;; esac ;; esac fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $lt_cv_sharedlib_from_linklib_cmd" >&5 printf "%s\n" "$lt_cv_sharedlib_from_linklib_cmd" >&6; } sharedlib_from_linklib_cmd=$lt_cv_sharedlib_from_linklib_cmd test -z "$sharedlib_from_linklib_cmd" && sharedlib_from_linklib_cmd=$ECHO if test -n "$ac_tool_prefix"; then for ac_prog in ar do # Extract the first word of "$ac_tool_prefix$ac_prog", so it can be a program name with args. set dummy $ac_tool_prefix$ac_prog; ac_word=$2 { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 printf %s "checking for $ac_word... " >&6; } if test ${ac_cv_prog_AR+y} then : printf %s "(cached) " >&6 else case e in #( e) if test -n "$AR"; then ac_cv_prog_AR="$AR" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS case $as_dir in #((( '') as_dir=./ ;; */) ;; *) as_dir=$as_dir/ ;; esac for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir$ac_word$ac_exec_ext"; then ac_cv_prog_AR="$ac_tool_prefix$ac_prog" printf "%s\n" "$as_me:${as_lineno-$LINENO}: found $as_dir$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi ;; esac fi AR=$ac_cv_prog_AR if test -n "$AR"; then { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $AR" >&5 printf "%s\n" "$AR" >&6; } else { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 printf "%s\n" "no" >&6; } fi test -n "$AR" && break done fi if test -z "$AR"; then ac_ct_AR=$AR for ac_prog in ar do # Extract the first word of "$ac_prog", so it can be a program name with args. set dummy $ac_prog; ac_word=$2 { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 printf %s "checking for $ac_word... " >&6; } if test ${ac_cv_prog_ac_ct_AR+y} then : printf %s "(cached) " >&6 else case e in #( e) if test -n "$ac_ct_AR"; then ac_cv_prog_ac_ct_AR="$ac_ct_AR" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS case $as_dir in #((( '') as_dir=./ ;; */) ;; *) as_dir=$as_dir/ ;; esac for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir$ac_word$ac_exec_ext"; then ac_cv_prog_ac_ct_AR="$ac_prog" printf "%s\n" "$as_me:${as_lineno-$LINENO}: found $as_dir$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi ;; esac fi ac_ct_AR=$ac_cv_prog_ac_ct_AR if test -n "$ac_ct_AR"; then { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_ct_AR" >&5 printf "%s\n" "$ac_ct_AR" >&6; } else { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 printf "%s\n" "no" >&6; } fi test -n "$ac_ct_AR" && break done if test "x$ac_ct_AR" = x; then AR="false" else case $cross_compiling:$ac_tool_warned in yes:) { printf "%s\n" "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 printf "%s\n" "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} ac_tool_warned=yes ;; esac AR=$ac_ct_AR fi fi : ${AR=ar} # Use ARFLAGS variable as AR's operation code to sync the variable naming with # Automake. If both AR_FLAGS and ARFLAGS are specified, AR_FLAGS should have # higher priority because thats what people were doing historically (setting # ARFLAGS for automake and AR_FLAGS for libtool). FIXME: Make the AR_FLAGS # variable obsoleted/removed. test ${AR_FLAGS+y} || AR_FLAGS=${ARFLAGS-cr} lt_ar_flags=$AR_FLAGS # Make AR_FLAGS overridable by 'make ARFLAGS='. Don't try to run-time override # by AR_FLAGS because that was never working and AR_FLAGS is about to die. { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for archiver @FILE support" >&5 printf %s "checking for archiver @FILE support... " >&6; } if test ${lt_cv_ar_at_file+y} then : printf %s "(cached) " >&6 else case e in #( e) lt_cv_ar_at_file=no cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ int main (void) { ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO" then : echo conftest.$ac_objext > conftest.lst lt_ar_try='$AR $AR_FLAGS libconftest.a @conftest.lst >&5' { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$lt_ar_try\""; } >&5 (eval $lt_ar_try) 2>&5 ac_status=$? printf "%s\n" "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; } if test 0 -eq "$ac_status"; then # Ensure the archiver fails upon bogus file names. rm -f conftest.$ac_objext libconftest.a { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$lt_ar_try\""; } >&5 (eval $lt_ar_try) 2>&5 ac_status=$? printf "%s\n" "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; } if test 0 -ne "$ac_status"; then lt_cv_ar_at_file=@ fi fi rm -f conftest.* libconftest.a fi rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext ;; esac fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $lt_cv_ar_at_file" >&5 printf "%s\n" "$lt_cv_ar_at_file" >&6; } if test no = "$lt_cv_ar_at_file"; then archiver_list_spec= else archiver_list_spec=$lt_cv_ar_at_file fi if test -n "$ac_tool_prefix"; then # Extract the first word of "${ac_tool_prefix}strip", so it can be a program name with args. set dummy ${ac_tool_prefix}strip; ac_word=$2 { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 printf %s "checking for $ac_word... " >&6; } if test ${ac_cv_prog_STRIP+y} then : printf %s "(cached) " >&6 else case e in #( e) if test -n "$STRIP"; then ac_cv_prog_STRIP="$STRIP" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS case $as_dir in #((( '') as_dir=./ ;; */) ;; *) as_dir=$as_dir/ ;; esac for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir$ac_word$ac_exec_ext"; then ac_cv_prog_STRIP="${ac_tool_prefix}strip" printf "%s\n" "$as_me:${as_lineno-$LINENO}: found $as_dir$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi ;; esac fi STRIP=$ac_cv_prog_STRIP if test -n "$STRIP"; then { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $STRIP" >&5 printf "%s\n" "$STRIP" >&6; } else { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 printf "%s\n" "no" >&6; } fi fi if test -z "$ac_cv_prog_STRIP"; then ac_ct_STRIP=$STRIP # Extract the first word of "strip", so it can be a program name with args. set dummy strip; ac_word=$2 { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 printf %s "checking for $ac_word... " >&6; } if test ${ac_cv_prog_ac_ct_STRIP+y} then : printf %s "(cached) " >&6 else case e in #( e) if test -n "$ac_ct_STRIP"; then ac_cv_prog_ac_ct_STRIP="$ac_ct_STRIP" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS case $as_dir in #((( '') as_dir=./ ;; */) ;; *) as_dir=$as_dir/ ;; esac for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir$ac_word$ac_exec_ext"; then ac_cv_prog_ac_ct_STRIP="strip" printf "%s\n" "$as_me:${as_lineno-$LINENO}: found $as_dir$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi ;; esac fi ac_ct_STRIP=$ac_cv_prog_ac_ct_STRIP if test -n "$ac_ct_STRIP"; then { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_ct_STRIP" >&5 printf "%s\n" "$ac_ct_STRIP" >&6; } else { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 printf "%s\n" "no" >&6; } fi if test "x$ac_ct_STRIP" = x; then STRIP=":" else case $cross_compiling:$ac_tool_warned in yes:) { printf "%s\n" "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 printf "%s\n" "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} ac_tool_warned=yes ;; esac STRIP=$ac_ct_STRIP fi else STRIP="$ac_cv_prog_STRIP" fi test -z "$STRIP" && STRIP=: if test -n "$ac_tool_prefix"; then # Extract the first word of "${ac_tool_prefix}ranlib", so it can be a program name with args. set dummy ${ac_tool_prefix}ranlib; ac_word=$2 { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 printf %s "checking for $ac_word... " >&6; } if test ${ac_cv_prog_RANLIB+y} then : printf %s "(cached) " >&6 else case e in #( e) if test -n "$RANLIB"; then ac_cv_prog_RANLIB="$RANLIB" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS case $as_dir in #((( '') as_dir=./ ;; */) ;; *) as_dir=$as_dir/ ;; esac for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir$ac_word$ac_exec_ext"; then ac_cv_prog_RANLIB="${ac_tool_prefix}ranlib" printf "%s\n" "$as_me:${as_lineno-$LINENO}: found $as_dir$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi ;; esac fi RANLIB=$ac_cv_prog_RANLIB if test -n "$RANLIB"; then { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $RANLIB" >&5 printf "%s\n" "$RANLIB" >&6; } else { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 printf "%s\n" "no" >&6; } fi fi if test -z "$ac_cv_prog_RANLIB"; then ac_ct_RANLIB=$RANLIB # Extract the first word of "ranlib", so it can be a program name with args. set dummy ranlib; ac_word=$2 { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 printf %s "checking for $ac_word... " >&6; } if test ${ac_cv_prog_ac_ct_RANLIB+y} then : printf %s "(cached) " >&6 else case e in #( e) if test -n "$ac_ct_RANLIB"; then ac_cv_prog_ac_ct_RANLIB="$ac_ct_RANLIB" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS case $as_dir in #((( '') as_dir=./ ;; */) ;; *) as_dir=$as_dir/ ;; esac for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir$ac_word$ac_exec_ext"; then ac_cv_prog_ac_ct_RANLIB="ranlib" printf "%s\n" "$as_me:${as_lineno-$LINENO}: found $as_dir$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi ;; esac fi ac_ct_RANLIB=$ac_cv_prog_ac_ct_RANLIB if test -n "$ac_ct_RANLIB"; then { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_ct_RANLIB" >&5 printf "%s\n" "$ac_ct_RANLIB" >&6; } else { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 printf "%s\n" "no" >&6; } fi if test "x$ac_ct_RANLIB" = x; then RANLIB=":" else case $cross_compiling:$ac_tool_warned in yes:) { printf "%s\n" "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 printf "%s\n" "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} ac_tool_warned=yes ;; esac RANLIB=$ac_ct_RANLIB fi else RANLIB="$ac_cv_prog_RANLIB" fi test -z "$RANLIB" && RANLIB=: # Determine commands to create old-style static archives. old_archive_cmds='$AR $AR_FLAGS $oldlib$oldobjs' old_postinstall_cmds='chmod 644 $oldlib' old_postuninstall_cmds= if test -n "$RANLIB"; then case $host_os in bitrig* | openbsd*) old_postinstall_cmds="$old_postinstall_cmds~\$RANLIB -t \$tool_oldlib" ;; *) old_postinstall_cmds="$old_postinstall_cmds~\$RANLIB \$tool_oldlib" ;; esac old_archive_cmds="$old_archive_cmds~\$RANLIB \$tool_oldlib" fi case $host_os in darwin*) lock_old_archive_extraction=yes ;; *) lock_old_archive_extraction=no ;; esac # If no C compiler was specified, use CC. LTCC=${LTCC-"$CC"} # If no C compiler flags were specified, use CFLAGS. LTCFLAGS=${LTCFLAGS-"$CFLAGS"} # Allow CC to be a program name with arguments. compiler=$CC # Check for command to grab the raw symbol name followed by C symbol from nm. { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking command to parse $NM output from $compiler object" >&5 printf %s "checking command to parse $NM output from $compiler object... " >&6; } if test ${lt_cv_sys_global_symbol_pipe+y} then : printf %s "(cached) " >&6 else case e in #( e) # These are sane defaults that work on at least a few old systems. # [They come from Ultrix. What could be older than Ultrix?!! ;)] # Character class describing NM global symbol codes. symcode='[BCDEGRST]' # Regexp to match symbols that can be accessed directly from C. sympat='\([_A-Za-z][_A-Za-z0-9]*\)' # Define system-specific variables. case $host_os in aix*) symcode='[BCDT]' ;; cygwin* | mingw* | pw32* | cegcc*) symcode='[ABCDGISTW]' ;; hpux*) if test ia64 = "$host_cpu"; then symcode='[ABCDEGRST]' fi ;; irix* | nonstopux*) symcode='[BCDEGRST]' ;; osf*) symcode='[BCDEGQRST]' ;; solaris*) symcode='[BDRT]' ;; sco3.2v5*) symcode='[DT]' ;; sysv4.2uw2*) symcode='[DT]' ;; sysv5* | sco5v6* | unixware* | OpenUNIX*) symcode='[ABDT]' ;; sysv4) symcode='[DFNSTU]' ;; esac # If we're using GNU nm, then use its standard symbol codes. case `$NM -V 2>&1` in *GNU* | *'with BFD'*) symcode='[ABCDGIRSTW]' ;; esac if test "$lt_cv_nm_interface" = "MS dumpbin"; then # Gets list of data symbols to import. lt_cv_sys_global_symbol_to_import="$SED -n -e 's/^I .* \(.*\)$/\1/p'" # Adjust the below global symbol transforms to fixup imported variables. lt_cdecl_hook=" -e 's/^I .* \(.*\)$/extern __declspec(dllimport) char \1;/p'" lt_c_name_hook=" -e 's/^I .* \(.*\)$/ {\"\1\", (void *) 0},/p'" lt_c_name_lib_hook="\ -e 's/^I .* \(lib.*\)$/ {\"\1\", (void *) 0},/p'\ -e 's/^I .* \(.*\)$/ {\"lib\1\", (void *) 0},/p'" else # Disable hooks by default. lt_cv_sys_global_symbol_to_import= lt_cdecl_hook= lt_c_name_hook= lt_c_name_lib_hook= fi # Transform an extracted symbol line into a proper C declaration. # Some systems (esp. on ia64) link data and code symbols differently, # so use this general approach. lt_cv_sys_global_symbol_to_cdecl="$SED -n"\ $lt_cdecl_hook\ " -e 's/^T .* \(.*\)$/extern int \1();/p'"\ " -e 's/^$symcode$symcode* .* \(.*\)$/extern char \1;/p'" # Transform an extracted symbol line into symbol name and symbol address lt_cv_sys_global_symbol_to_c_name_address="$SED -n"\ $lt_c_name_hook\ " -e 's/^: \(.*\) .*$/ {\"\1\", (void *) 0},/p'"\ " -e 's/^$symcode$symcode* .* \(.*\)$/ {\"\1\", (void *) \&\1},/p'" # Transform an extracted symbol line into symbol name with lib prefix and # symbol address. lt_cv_sys_global_symbol_to_c_name_address_lib_prefix="$SED -n"\ $lt_c_name_lib_hook\ " -e 's/^: \(.*\) .*$/ {\"\1\", (void *) 0},/p'"\ " -e 's/^$symcode$symcode* .* \(lib.*\)$/ {\"\1\", (void *) \&\1},/p'"\ " -e 's/^$symcode$symcode* .* \(.*\)$/ {\"lib\1\", (void *) \&\1},/p'" # Handle CRLF in mingw tool chain opt_cr= case $build_os in mingw*) opt_cr=`$ECHO 'x\{0,1\}' | tr x '\015'` # option cr in regexp ;; esac # Try without a prefix underscore, then with it. for ac_symprfx in "" "_"; do # Transform symcode, sympat, and symprfx into a raw symbol and a C symbol. symxfrm="\\1 $ac_symprfx\\2 \\2" # Write the raw and C identifiers. if test "$lt_cv_nm_interface" = "MS dumpbin"; then # Fake it for dumpbin and say T for any non-static function, # D for any global variable and I for any imported variable. # Also find C++ and __fastcall symbols from MSVC++ or ICC, # which start with @ or ?. lt_cv_sys_global_symbol_pipe="$AWK '"\ " {last_section=section; section=\$ 3};"\ " /^COFF SYMBOL TABLE/{for(i in hide) delete hide[i]};"\ " /Section length .*#relocs.*(pick any)/{hide[last_section]=1};"\ " /^ *Symbol name *: /{split(\$ 0,sn,\":\"); si=substr(sn[2],2)};"\ " /^ *Type *: code/{print \"T\",si,substr(si,length(prfx))};"\ " /^ *Type *: data/{print \"I\",si,substr(si,length(prfx))};"\ " \$ 0!~/External *\|/{next};"\ " / 0+ UNDEF /{next}; / UNDEF \([^|]\)*()/{next};"\ " {if(hide[section]) next};"\ " {f=\"D\"}; \$ 0~/\(\).*\|/{f=\"T\"};"\ " {split(\$ 0,a,/\||\r/); split(a[2],s)};"\ " s[1]~/^[@?]/{print f,s[1],s[1]; next};"\ " s[1]~prfx {split(s[1],t,\"@\"); print f,t[1],substr(t[1],length(prfx))}"\ " ' prfx=^$ac_symprfx" else lt_cv_sys_global_symbol_pipe="$SED -n -e 's/^.*[ ]\($symcode$symcode*\)[ ][ ]*$ac_symprfx$sympat$opt_cr$/$symxfrm/p'" fi lt_cv_sys_global_symbol_pipe="$lt_cv_sys_global_symbol_pipe | $SED '/ __gnu_lto/d'" # Check to see that the pipe works correctly. pipe_works=no rm -f conftest* cat > conftest.$ac_ext <<_LT_EOF #ifdef __cplusplus extern "C" { #endif char nm_test_var; void nm_test_func(void); void nm_test_func(void){} #ifdef __cplusplus } #endif int main(){nm_test_var='a';nm_test_func();return(0);} _LT_EOF if { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$ac_compile\""; } >&5 (eval $ac_compile) 2>&5 ac_status=$? printf "%s\n" "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; }; then # Now try to grab the symbols. nlist=conftest.nm $ECHO "$as_me:$LINENO: $NM conftest.$ac_objext | $lt_cv_sys_global_symbol_pipe > $nlist" >&5 if eval "$NM" conftest.$ac_objext \| "$lt_cv_sys_global_symbol_pipe" \> $nlist 2>&5 && test -s "$nlist"; then # Try sorting and uniquifying the output. if sort "$nlist" | uniq > "$nlist"T; then mv -f "$nlist"T "$nlist" else rm -f "$nlist"T fi # Make sure that we snagged all the symbols we need. if $GREP ' nm_test_var$' "$nlist" >/dev/null; then if $GREP ' nm_test_func$' "$nlist" >/dev/null; then cat <<_LT_EOF > conftest.$ac_ext /* Keep this code in sync between libtool.m4, ltmain, lt_system.h, and tests. */ #if defined _WIN32 || defined __CYGWIN__ || defined _WIN32_WCE /* DATA imports from DLLs on WIN32 can't be const, because runtime relocations are performed -- see ld's documentation on pseudo-relocs. */ # define LT_DLSYM_CONST #elif defined __osf__ /* This system does not cope well with relocations in const data. */ # define LT_DLSYM_CONST #else # define LT_DLSYM_CONST const #endif #ifdef __cplusplus extern "C" { #endif _LT_EOF # Now generate the symbol file. eval "$lt_cv_sys_global_symbol_to_cdecl"' < "$nlist" | $GREP -v main >> conftest.$ac_ext' cat <<_LT_EOF >> conftest.$ac_ext /* The mapping between symbol names and symbols. */ LT_DLSYM_CONST struct { const char *name; void *address; } lt__PROGRAM__LTX_preloaded_symbols[] = { { "@PROGRAM@", (void *) 0 }, _LT_EOF $SED "s/^$symcode$symcode* .* \(.*\)$/ {\"\1\", (void *) \&\1},/" < "$nlist" | $GREP -v main >> conftest.$ac_ext cat <<\_LT_EOF >> conftest.$ac_ext {0, (void *) 0} }; /* This works around a problem in FreeBSD linker */ #ifdef FREEBSD_WORKAROUND static const void *lt_preloaded_setup() { return lt__PROGRAM__LTX_preloaded_symbols; } #endif #ifdef __cplusplus } #endif _LT_EOF # Now try linking the two files. mv conftest.$ac_objext conftstm.$ac_objext lt_globsym_save_LIBS=$LIBS lt_globsym_save_CFLAGS=$CFLAGS LIBS=conftstm.$ac_objext CFLAGS="$CFLAGS$lt_prog_compiler_no_builtin_flag" if { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$ac_link\""; } >&5 (eval $ac_link) 2>&5 ac_status=$? printf "%s\n" "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; } && test -s conftest$ac_exeext; then pipe_works=yes fi LIBS=$lt_globsym_save_LIBS CFLAGS=$lt_globsym_save_CFLAGS else echo "cannot find nm_test_func in $nlist" >&5 fi else echo "cannot find nm_test_var in $nlist" >&5 fi else echo "cannot run $lt_cv_sys_global_symbol_pipe" >&5 fi else echo "$progname: failed program was:" >&5 cat conftest.$ac_ext >&5 fi rm -rf conftest* conftst* # Do not use the global_symbol_pipe unless it works. if test yes = "$pipe_works"; then break else lt_cv_sys_global_symbol_pipe= fi done ;; esac fi if test -z "$lt_cv_sys_global_symbol_pipe"; then lt_cv_sys_global_symbol_to_cdecl= fi if test -z "$lt_cv_sys_global_symbol_pipe$lt_cv_sys_global_symbol_to_cdecl"; then { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: failed" >&5 printf "%s\n" "failed" >&6; } else { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: ok" >&5 printf "%s\n" "ok" >&6; } fi # Response file support. if test "$lt_cv_nm_interface" = "MS dumpbin"; then nm_file_list_spec='@' elif $NM --help 2>/dev/null | grep '[@]FILE' >/dev/null; then nm_file_list_spec='@' fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for sysroot" >&5 printf %s "checking for sysroot... " >&6; } # Check whether --with-sysroot was given. if test ${with_sysroot+y} then : withval=$with_sysroot; else case e in #( e) with_sysroot=no ;; esac fi lt_sysroot= case $with_sysroot in #( yes) if test yes = "$GCC"; then lt_sysroot=`$CC --print-sysroot 2>/dev/null` fi ;; #( /*) lt_sysroot=`echo "$with_sysroot" | $SED -e "$sed_quote_subst"` ;; #( no|'') ;; #( *) { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $with_sysroot" >&5 printf "%s\n" "$with_sysroot" >&6; } as_fn_error $? "The sysroot must be an absolute path." "$LINENO" 5 ;; esac { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: ${lt_sysroot:-no}" >&5 printf "%s\n" "${lt_sysroot:-no}" >&6; } { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for a working dd" >&5 printf %s "checking for a working dd... " >&6; } if test ${ac_cv_path_lt_DD+y} then : printf %s "(cached) " >&6 else case e in #( e) printf 0123456789abcdef0123456789abcdef >conftest.i cat conftest.i conftest.i >conftest2.i : ${lt_DD:=$DD} if test -z "$lt_DD"; then ac_path_lt_DD_found=false # Loop through the user's path and test for each of PROGNAME-LIST as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS case $as_dir in #((( '') as_dir=./ ;; */) ;; *) as_dir=$as_dir/ ;; esac for ac_prog in dd do for ac_exec_ext in '' $ac_executable_extensions; do ac_path_lt_DD="$as_dir$ac_prog$ac_exec_ext" as_fn_executable_p "$ac_path_lt_DD" || continue if "$ac_path_lt_DD" bs=32 count=1 conftest.out 2>/dev/null; then cmp -s conftest.i conftest.out \ && ac_cv_path_lt_DD="$ac_path_lt_DD" ac_path_lt_DD_found=: fi $ac_path_lt_DD_found && break 3 done done done IFS=$as_save_IFS if test -z "$ac_cv_path_lt_DD"; then : fi else ac_cv_path_lt_DD=$lt_DD fi rm -f conftest.i conftest2.i conftest.out ;; esac fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_path_lt_DD" >&5 printf "%s\n" "$ac_cv_path_lt_DD" >&6; } { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking how to truncate binary pipes" >&5 printf %s "checking how to truncate binary pipes... " >&6; } if test ${lt_cv_truncate_bin+y} then : printf %s "(cached) " >&6 else case e in #( e) printf 0123456789abcdef0123456789abcdef >conftest.i cat conftest.i conftest.i >conftest2.i lt_cv_truncate_bin= if "$ac_cv_path_lt_DD" bs=32 count=1 conftest.out 2>/dev/null; then cmp -s conftest.i conftest.out \ && lt_cv_truncate_bin="$ac_cv_path_lt_DD bs=4096 count=1" fi rm -f conftest.i conftest2.i conftest.out test -z "$lt_cv_truncate_bin" && lt_cv_truncate_bin="$SED -e 4q" ;; esac fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $lt_cv_truncate_bin" >&5 printf "%s\n" "$lt_cv_truncate_bin" >&6; } # Calculate cc_basename. Skip known compiler wrappers and cross-prefix. func_cc_basename () { for cc_temp in $*""; do case $cc_temp in compile | *[\\/]compile | ccache | *[\\/]ccache ) ;; distcc | *[\\/]distcc | purify | *[\\/]purify ) ;; \-*) ;; *) break;; esac done func_cc_basename_result=`$ECHO "$cc_temp" | $SED "s%.*/%%; s%^$host_alias-%%"` } # Check whether --enable-libtool-lock was given. if test ${enable_libtool_lock+y} then : enableval=$enable_libtool_lock; fi test no = "$enable_libtool_lock" || enable_libtool_lock=yes # Some flags need to be propagated to the compiler or linker for good # libtool support. case $host in ia64-*-hpux*) # Find out what ABI is being produced by ac_compile, and set mode # options accordingly. echo 'int i;' > conftest.$ac_ext if { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$ac_compile\""; } >&5 (eval $ac_compile) 2>&5 ac_status=$? printf "%s\n" "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; }; then case `$FILECMD conftest.$ac_objext` in *ELF-32*) HPUX_IA64_MODE=32 ;; *ELF-64*) HPUX_IA64_MODE=64 ;; esac fi rm -rf conftest* ;; *-*-irix6*) # Find out what ABI is being produced by ac_compile, and set linker # options accordingly. echo '#line '$LINENO' "configure"' > conftest.$ac_ext if { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$ac_compile\""; } >&5 (eval $ac_compile) 2>&5 ac_status=$? printf "%s\n" "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; }; then if test yes = "$lt_cv_prog_gnu_ld"; then case `$FILECMD conftest.$ac_objext` in *32-bit*) LD="${LD-ld} -melf32bsmip" ;; *N32*) LD="${LD-ld} -melf32bmipn32" ;; *64-bit*) LD="${LD-ld} -melf64bmip" ;; esac else case `$FILECMD conftest.$ac_objext` in *32-bit*) LD="${LD-ld} -32" ;; *N32*) LD="${LD-ld} -n32" ;; *64-bit*) LD="${LD-ld} -64" ;; esac fi fi rm -rf conftest* ;; mips64*-*linux*) # Find out what ABI is being produced by ac_compile, and set linker # options accordingly. echo '#line '$LINENO' "configure"' > conftest.$ac_ext if { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$ac_compile\""; } >&5 (eval $ac_compile) 2>&5 ac_status=$? printf "%s\n" "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; }; then emul=elf case `$FILECMD conftest.$ac_objext` in *32-bit*) emul="${emul}32" ;; *64-bit*) emul="${emul}64" ;; esac case `$FILECMD conftest.$ac_objext` in *MSB*) emul="${emul}btsmip" ;; *LSB*) emul="${emul}ltsmip" ;; esac case `$FILECMD conftest.$ac_objext` in *N32*) emul="${emul}n32" ;; esac LD="${LD-ld} -m $emul" fi rm -rf conftest* ;; x86_64-*kfreebsd*-gnu|x86_64-*linux*|powerpc*-*linux*| \ s390*-*linux*|s390*-*tpf*|sparc*-*linux*) # Find out what ABI is being produced by ac_compile, and set linker # options accordingly. Note that the listed cases only cover the # situations where additional linker options are needed (such as when # doing 32-bit compilation for a host where ld defaults to 64-bit, or # vice versa); the common cases where no linker options are needed do # not appear in the list. echo 'int i;' > conftest.$ac_ext if { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$ac_compile\""; } >&5 (eval $ac_compile) 2>&5 ac_status=$? printf "%s\n" "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; }; then case `$FILECMD conftest.o` in *32-bit*) case $host in x86_64-*kfreebsd*-gnu) LD="${LD-ld} -m elf_i386_fbsd" ;; x86_64-*linux*) case `$FILECMD conftest.o` in *x86-64*) LD="${LD-ld} -m elf32_x86_64" ;; *) LD="${LD-ld} -m elf_i386" ;; esac ;; powerpc64le-*linux*) LD="${LD-ld} -m elf32lppclinux" ;; powerpc64-*linux*) LD="${LD-ld} -m elf32ppclinux" ;; s390x-*linux*) LD="${LD-ld} -m elf_s390" ;; sparc64-*linux*) LD="${LD-ld} -m elf32_sparc" ;; esac ;; *64-bit*) case $host in x86_64-*kfreebsd*-gnu) LD="${LD-ld} -m elf_x86_64_fbsd" ;; x86_64-*linux*) LD="${LD-ld} -m elf_x86_64" ;; powerpcle-*linux*) LD="${LD-ld} -m elf64lppc" ;; powerpc-*linux*) LD="${LD-ld} -m elf64ppc" ;; s390*-*linux*|s390*-*tpf*) LD="${LD-ld} -m elf64_s390" ;; sparc*-*linux*) LD="${LD-ld} -m elf64_sparc" ;; esac ;; esac fi rm -rf conftest* ;; *-*-sco3.2v5*) # On SCO OpenServer 5, we need -belf to get full-featured binaries. SAVE_CFLAGS=$CFLAGS CFLAGS="$CFLAGS -belf" { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking whether the C compiler needs -belf" >&5 printf %s "checking whether the C compiler needs -belf... " >&6; } if test ${lt_cv_cc_needs_belf+y} then : printf %s "(cached) " >&6 else case e in #( e) ac_ext=c ac_cpp='$CPP $CPPFLAGS' ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' ac_compiler_gnu=$ac_cv_c_compiler_gnu cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ int main (void) { ; return 0; } _ACEOF if ac_fn_c_try_link "$LINENO" then : lt_cv_cc_needs_belf=yes else case e in #( e) lt_cv_cc_needs_belf=no ;; esac fi rm -f core conftest.err conftest.$ac_objext conftest.beam \ conftest$ac_exeext conftest.$ac_ext ac_ext=c ac_cpp='$CPP $CPPFLAGS' ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' ac_compiler_gnu=$ac_cv_c_compiler_gnu ;; esac fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $lt_cv_cc_needs_belf" >&5 printf "%s\n" "$lt_cv_cc_needs_belf" >&6; } if test yes != "$lt_cv_cc_needs_belf"; then # this is probably gcc 2.8.0, egcs 1.0 or newer; no need for -belf CFLAGS=$SAVE_CFLAGS fi ;; *-*solaris*) # Find out what ABI is being produced by ac_compile, and set linker # options accordingly. echo 'int i;' > conftest.$ac_ext if { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$ac_compile\""; } >&5 (eval $ac_compile) 2>&5 ac_status=$? printf "%s\n" "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; }; then case `$FILECMD conftest.o` in *64-bit*) case $lt_cv_prog_gnu_ld in yes*) case $host in i?86-*-solaris*|x86_64-*-solaris*) LD="${LD-ld} -m elf_x86_64" ;; sparc*-*-solaris*) LD="${LD-ld} -m elf64_sparc" ;; esac # GNU ld 2.21 introduced _sol2 emulations. Use them if available. if ${LD-ld} -V | grep _sol2 >/dev/null 2>&1; then LD=${LD-ld}_sol2 fi ;; *) if ${LD-ld} -64 -r -o conftest2.o conftest.o >/dev/null 2>&1; then LD="${LD-ld} -64" fi ;; esac ;; esac fi rm -rf conftest* ;; esac need_locks=$enable_libtool_lock if test -n "$ac_tool_prefix"; then # Extract the first word of "${ac_tool_prefix}mt", so it can be a program name with args. set dummy ${ac_tool_prefix}mt; ac_word=$2 { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 printf %s "checking for $ac_word... " >&6; } if test ${ac_cv_prog_MANIFEST_TOOL+y} then : printf %s "(cached) " >&6 else case e in #( e) if test -n "$MANIFEST_TOOL"; then ac_cv_prog_MANIFEST_TOOL="$MANIFEST_TOOL" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS case $as_dir in #((( '') as_dir=./ ;; */) ;; *) as_dir=$as_dir/ ;; esac for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir$ac_word$ac_exec_ext"; then ac_cv_prog_MANIFEST_TOOL="${ac_tool_prefix}mt" printf "%s\n" "$as_me:${as_lineno-$LINENO}: found $as_dir$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi ;; esac fi MANIFEST_TOOL=$ac_cv_prog_MANIFEST_TOOL if test -n "$MANIFEST_TOOL"; then { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $MANIFEST_TOOL" >&5 printf "%s\n" "$MANIFEST_TOOL" >&6; } else { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 printf "%s\n" "no" >&6; } fi fi if test -z "$ac_cv_prog_MANIFEST_TOOL"; then ac_ct_MANIFEST_TOOL=$MANIFEST_TOOL # Extract the first word of "mt", so it can be a program name with args. set dummy mt; ac_word=$2 { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 printf %s "checking for $ac_word... " >&6; } if test ${ac_cv_prog_ac_ct_MANIFEST_TOOL+y} then : printf %s "(cached) " >&6 else case e in #( e) if test -n "$ac_ct_MANIFEST_TOOL"; then ac_cv_prog_ac_ct_MANIFEST_TOOL="$ac_ct_MANIFEST_TOOL" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS case $as_dir in #((( '') as_dir=./ ;; */) ;; *) as_dir=$as_dir/ ;; esac for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir$ac_word$ac_exec_ext"; then ac_cv_prog_ac_ct_MANIFEST_TOOL="mt" printf "%s\n" "$as_me:${as_lineno-$LINENO}: found $as_dir$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi ;; esac fi ac_ct_MANIFEST_TOOL=$ac_cv_prog_ac_ct_MANIFEST_TOOL if test -n "$ac_ct_MANIFEST_TOOL"; then { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_ct_MANIFEST_TOOL" >&5 printf "%s\n" "$ac_ct_MANIFEST_TOOL" >&6; } else { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 printf "%s\n" "no" >&6; } fi if test "x$ac_ct_MANIFEST_TOOL" = x; then MANIFEST_TOOL=":" else case $cross_compiling:$ac_tool_warned in yes:) { printf "%s\n" "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 printf "%s\n" "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} ac_tool_warned=yes ;; esac MANIFEST_TOOL=$ac_ct_MANIFEST_TOOL fi else MANIFEST_TOOL="$ac_cv_prog_MANIFEST_TOOL" fi test -z "$MANIFEST_TOOL" && MANIFEST_TOOL=mt { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking if $MANIFEST_TOOL is a manifest tool" >&5 printf %s "checking if $MANIFEST_TOOL is a manifest tool... " >&6; } if test ${lt_cv_path_mainfest_tool+y} then : printf %s "(cached) " >&6 else case e in #( e) lt_cv_path_mainfest_tool=no echo "$as_me:$LINENO: $MANIFEST_TOOL '-?'" >&5 $MANIFEST_TOOL '-?' 2>conftest.err > conftest.out cat conftest.err >&5 if $GREP 'Manifest Tool' conftest.out > /dev/null; then lt_cv_path_mainfest_tool=yes fi rm -f conftest* ;; esac fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $lt_cv_path_mainfest_tool" >&5 printf "%s\n" "$lt_cv_path_mainfest_tool" >&6; } if test yes != "$lt_cv_path_mainfest_tool"; then MANIFEST_TOOL=: fi case $host_os in rhapsody* | darwin*) if test -n "$ac_tool_prefix"; then # Extract the first word of "${ac_tool_prefix}dsymutil", so it can be a program name with args. set dummy ${ac_tool_prefix}dsymutil; ac_word=$2 { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 printf %s "checking for $ac_word... " >&6; } if test ${ac_cv_prog_DSYMUTIL+y} then : printf %s "(cached) " >&6 else case e in #( e) if test -n "$DSYMUTIL"; then ac_cv_prog_DSYMUTIL="$DSYMUTIL" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS case $as_dir in #((( '') as_dir=./ ;; */) ;; *) as_dir=$as_dir/ ;; esac for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir$ac_word$ac_exec_ext"; then ac_cv_prog_DSYMUTIL="${ac_tool_prefix}dsymutil" printf "%s\n" "$as_me:${as_lineno-$LINENO}: found $as_dir$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi ;; esac fi DSYMUTIL=$ac_cv_prog_DSYMUTIL if test -n "$DSYMUTIL"; then { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $DSYMUTIL" >&5 printf "%s\n" "$DSYMUTIL" >&6; } else { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 printf "%s\n" "no" >&6; } fi fi if test -z "$ac_cv_prog_DSYMUTIL"; then ac_ct_DSYMUTIL=$DSYMUTIL # Extract the first word of "dsymutil", so it can be a program name with args. set dummy dsymutil; ac_word=$2 { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 printf %s "checking for $ac_word... " >&6; } if test ${ac_cv_prog_ac_ct_DSYMUTIL+y} then : printf %s "(cached) " >&6 else case e in #( e) if test -n "$ac_ct_DSYMUTIL"; then ac_cv_prog_ac_ct_DSYMUTIL="$ac_ct_DSYMUTIL" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS case $as_dir in #((( '') as_dir=./ ;; */) ;; *) as_dir=$as_dir/ ;; esac for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir$ac_word$ac_exec_ext"; then ac_cv_prog_ac_ct_DSYMUTIL="dsymutil" printf "%s\n" "$as_me:${as_lineno-$LINENO}: found $as_dir$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi ;; esac fi ac_ct_DSYMUTIL=$ac_cv_prog_ac_ct_DSYMUTIL if test -n "$ac_ct_DSYMUTIL"; then { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_ct_DSYMUTIL" >&5 printf "%s\n" "$ac_ct_DSYMUTIL" >&6; } else { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 printf "%s\n" "no" >&6; } fi if test "x$ac_ct_DSYMUTIL" = x; then DSYMUTIL=":" else case $cross_compiling:$ac_tool_warned in yes:) { printf "%s\n" "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 printf "%s\n" "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} ac_tool_warned=yes ;; esac DSYMUTIL=$ac_ct_DSYMUTIL fi else DSYMUTIL="$ac_cv_prog_DSYMUTIL" fi if test -n "$ac_tool_prefix"; then # Extract the first word of "${ac_tool_prefix}nmedit", so it can be a program name with args. set dummy ${ac_tool_prefix}nmedit; ac_word=$2 { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 printf %s "checking for $ac_word... " >&6; } if test ${ac_cv_prog_NMEDIT+y} then : printf %s "(cached) " >&6 else case e in #( e) if test -n "$NMEDIT"; then ac_cv_prog_NMEDIT="$NMEDIT" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS case $as_dir in #((( '') as_dir=./ ;; */) ;; *) as_dir=$as_dir/ ;; esac for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir$ac_word$ac_exec_ext"; then ac_cv_prog_NMEDIT="${ac_tool_prefix}nmedit" printf "%s\n" "$as_me:${as_lineno-$LINENO}: found $as_dir$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi ;; esac fi NMEDIT=$ac_cv_prog_NMEDIT if test -n "$NMEDIT"; then { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $NMEDIT" >&5 printf "%s\n" "$NMEDIT" >&6; } else { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 printf "%s\n" "no" >&6; } fi fi if test -z "$ac_cv_prog_NMEDIT"; then ac_ct_NMEDIT=$NMEDIT # Extract the first word of "nmedit", so it can be a program name with args. set dummy nmedit; ac_word=$2 { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 printf %s "checking for $ac_word... " >&6; } if test ${ac_cv_prog_ac_ct_NMEDIT+y} then : printf %s "(cached) " >&6 else case e in #( e) if test -n "$ac_ct_NMEDIT"; then ac_cv_prog_ac_ct_NMEDIT="$ac_ct_NMEDIT" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS case $as_dir in #((( '') as_dir=./ ;; */) ;; *) as_dir=$as_dir/ ;; esac for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir$ac_word$ac_exec_ext"; then ac_cv_prog_ac_ct_NMEDIT="nmedit" printf "%s\n" "$as_me:${as_lineno-$LINENO}: found $as_dir$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi ;; esac fi ac_ct_NMEDIT=$ac_cv_prog_ac_ct_NMEDIT if test -n "$ac_ct_NMEDIT"; then { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_ct_NMEDIT" >&5 printf "%s\n" "$ac_ct_NMEDIT" >&6; } else { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 printf "%s\n" "no" >&6; } fi if test "x$ac_ct_NMEDIT" = x; then NMEDIT=":" else case $cross_compiling:$ac_tool_warned in yes:) { printf "%s\n" "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 printf "%s\n" "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} ac_tool_warned=yes ;; esac NMEDIT=$ac_ct_NMEDIT fi else NMEDIT="$ac_cv_prog_NMEDIT" fi if test -n "$ac_tool_prefix"; then # Extract the first word of "${ac_tool_prefix}lipo", so it can be a program name with args. set dummy ${ac_tool_prefix}lipo; ac_word=$2 { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 printf %s "checking for $ac_word... " >&6; } if test ${ac_cv_prog_LIPO+y} then : printf %s "(cached) " >&6 else case e in #( e) if test -n "$LIPO"; then ac_cv_prog_LIPO="$LIPO" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS case $as_dir in #((( '') as_dir=./ ;; */) ;; *) as_dir=$as_dir/ ;; esac for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir$ac_word$ac_exec_ext"; then ac_cv_prog_LIPO="${ac_tool_prefix}lipo" printf "%s\n" "$as_me:${as_lineno-$LINENO}: found $as_dir$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi ;; esac fi LIPO=$ac_cv_prog_LIPO if test -n "$LIPO"; then { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $LIPO" >&5 printf "%s\n" "$LIPO" >&6; } else { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 printf "%s\n" "no" >&6; } fi fi if test -z "$ac_cv_prog_LIPO"; then ac_ct_LIPO=$LIPO # Extract the first word of "lipo", so it can be a program name with args. set dummy lipo; ac_word=$2 { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 printf %s "checking for $ac_word... " >&6; } if test ${ac_cv_prog_ac_ct_LIPO+y} then : printf %s "(cached) " >&6 else case e in #( e) if test -n "$ac_ct_LIPO"; then ac_cv_prog_ac_ct_LIPO="$ac_ct_LIPO" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS case $as_dir in #((( '') as_dir=./ ;; */) ;; *) as_dir=$as_dir/ ;; esac for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir$ac_word$ac_exec_ext"; then ac_cv_prog_ac_ct_LIPO="lipo" printf "%s\n" "$as_me:${as_lineno-$LINENO}: found $as_dir$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi ;; esac fi ac_ct_LIPO=$ac_cv_prog_ac_ct_LIPO if test -n "$ac_ct_LIPO"; then { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_ct_LIPO" >&5 printf "%s\n" "$ac_ct_LIPO" >&6; } else { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 printf "%s\n" "no" >&6; } fi if test "x$ac_ct_LIPO" = x; then LIPO=":" else case $cross_compiling:$ac_tool_warned in yes:) { printf "%s\n" "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 printf "%s\n" "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} ac_tool_warned=yes ;; esac LIPO=$ac_ct_LIPO fi else LIPO="$ac_cv_prog_LIPO" fi if test -n "$ac_tool_prefix"; then # Extract the first word of "${ac_tool_prefix}otool", so it can be a program name with args. set dummy ${ac_tool_prefix}otool; ac_word=$2 { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 printf %s "checking for $ac_word... " >&6; } if test ${ac_cv_prog_OTOOL+y} then : printf %s "(cached) " >&6 else case e in #( e) if test -n "$OTOOL"; then ac_cv_prog_OTOOL="$OTOOL" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS case $as_dir in #((( '') as_dir=./ ;; */) ;; *) as_dir=$as_dir/ ;; esac for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir$ac_word$ac_exec_ext"; then ac_cv_prog_OTOOL="${ac_tool_prefix}otool" printf "%s\n" "$as_me:${as_lineno-$LINENO}: found $as_dir$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi ;; esac fi OTOOL=$ac_cv_prog_OTOOL if test -n "$OTOOL"; then { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $OTOOL" >&5 printf "%s\n" "$OTOOL" >&6; } else { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 printf "%s\n" "no" >&6; } fi fi if test -z "$ac_cv_prog_OTOOL"; then ac_ct_OTOOL=$OTOOL # Extract the first word of "otool", so it can be a program name with args. set dummy otool; ac_word=$2 { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 printf %s "checking for $ac_word... " >&6; } if test ${ac_cv_prog_ac_ct_OTOOL+y} then : printf %s "(cached) " >&6 else case e in #( e) if test -n "$ac_ct_OTOOL"; then ac_cv_prog_ac_ct_OTOOL="$ac_ct_OTOOL" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS case $as_dir in #((( '') as_dir=./ ;; */) ;; *) as_dir=$as_dir/ ;; esac for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir$ac_word$ac_exec_ext"; then ac_cv_prog_ac_ct_OTOOL="otool" printf "%s\n" "$as_me:${as_lineno-$LINENO}: found $as_dir$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi ;; esac fi ac_ct_OTOOL=$ac_cv_prog_ac_ct_OTOOL if test -n "$ac_ct_OTOOL"; then { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_ct_OTOOL" >&5 printf "%s\n" "$ac_ct_OTOOL" >&6; } else { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 printf "%s\n" "no" >&6; } fi if test "x$ac_ct_OTOOL" = x; then OTOOL=":" else case $cross_compiling:$ac_tool_warned in yes:) { printf "%s\n" "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 printf "%s\n" "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} ac_tool_warned=yes ;; esac OTOOL=$ac_ct_OTOOL fi else OTOOL="$ac_cv_prog_OTOOL" fi if test -n "$ac_tool_prefix"; then # Extract the first word of "${ac_tool_prefix}otool64", so it can be a program name with args. set dummy ${ac_tool_prefix}otool64; ac_word=$2 { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 printf %s "checking for $ac_word... " >&6; } if test ${ac_cv_prog_OTOOL64+y} then : printf %s "(cached) " >&6 else case e in #( e) if test -n "$OTOOL64"; then ac_cv_prog_OTOOL64="$OTOOL64" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS case $as_dir in #((( '') as_dir=./ ;; */) ;; *) as_dir=$as_dir/ ;; esac for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir$ac_word$ac_exec_ext"; then ac_cv_prog_OTOOL64="${ac_tool_prefix}otool64" printf "%s\n" "$as_me:${as_lineno-$LINENO}: found $as_dir$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi ;; esac fi OTOOL64=$ac_cv_prog_OTOOL64 if test -n "$OTOOL64"; then { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $OTOOL64" >&5 printf "%s\n" "$OTOOL64" >&6; } else { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 printf "%s\n" "no" >&6; } fi fi if test -z "$ac_cv_prog_OTOOL64"; then ac_ct_OTOOL64=$OTOOL64 # Extract the first word of "otool64", so it can be a program name with args. set dummy otool64; ac_word=$2 { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 printf %s "checking for $ac_word... " >&6; } if test ${ac_cv_prog_ac_ct_OTOOL64+y} then : printf %s "(cached) " >&6 else case e in #( e) if test -n "$ac_ct_OTOOL64"; then ac_cv_prog_ac_ct_OTOOL64="$ac_ct_OTOOL64" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS case $as_dir in #((( '') as_dir=./ ;; */) ;; *) as_dir=$as_dir/ ;; esac for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir$ac_word$ac_exec_ext"; then ac_cv_prog_ac_ct_OTOOL64="otool64" printf "%s\n" "$as_me:${as_lineno-$LINENO}: found $as_dir$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi ;; esac fi ac_ct_OTOOL64=$ac_cv_prog_ac_ct_OTOOL64 if test -n "$ac_ct_OTOOL64"; then { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_ct_OTOOL64" >&5 printf "%s\n" "$ac_ct_OTOOL64" >&6; } else { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 printf "%s\n" "no" >&6; } fi if test "x$ac_ct_OTOOL64" = x; then OTOOL64=":" else case $cross_compiling:$ac_tool_warned in yes:) { printf "%s\n" "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 printf "%s\n" "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} ac_tool_warned=yes ;; esac OTOOL64=$ac_ct_OTOOL64 fi else OTOOL64="$ac_cv_prog_OTOOL64" fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for -single_module linker flag" >&5 printf %s "checking for -single_module linker flag... " >&6; } if test ${lt_cv_apple_cc_single_mod+y} then : printf %s "(cached) " >&6 else case e in #( e) lt_cv_apple_cc_single_mod=no if test -z "$LT_MULTI_MODULE"; then # By default we will add the -single_module flag. You can override # by either setting the environment variable LT_MULTI_MODULE # non-empty at configure time, or by adding -multi_module to the # link flags. rm -rf libconftest.dylib* echo "int foo(void){return 1;}" > conftest.c echo "$LTCC $LTCFLAGS $LDFLAGS -o libconftest.dylib \ -dynamiclib -Wl,-single_module conftest.c" >&5 $LTCC $LTCFLAGS $LDFLAGS -o libconftest.dylib \ -dynamiclib -Wl,-single_module conftest.c 2>conftest.err _lt_result=$? # If there is a non-empty error log, and "single_module" # appears in it, assume the flag caused a linker warning if test -s conftest.err && $GREP single_module conftest.err; then cat conftest.err >&5 # Otherwise, if the output was created with a 0 exit code from # the compiler, it worked. elif test -f libconftest.dylib && test 0 = "$_lt_result"; then lt_cv_apple_cc_single_mod=yes else cat conftest.err >&5 fi rm -rf libconftest.dylib* rm -f conftest.* fi ;; esac fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $lt_cv_apple_cc_single_mod" >&5 printf "%s\n" "$lt_cv_apple_cc_single_mod" >&6; } { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for -exported_symbols_list linker flag" >&5 printf %s "checking for -exported_symbols_list linker flag... " >&6; } if test ${lt_cv_ld_exported_symbols_list+y} then : printf %s "(cached) " >&6 else case e in #( e) lt_cv_ld_exported_symbols_list=no save_LDFLAGS=$LDFLAGS echo "_main" > conftest.sym LDFLAGS="$LDFLAGS -Wl,-exported_symbols_list,conftest.sym" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ int main (void) { ; return 0; } _ACEOF if ac_fn_c_try_link "$LINENO" then : lt_cv_ld_exported_symbols_list=yes else case e in #( e) lt_cv_ld_exported_symbols_list=no ;; esac fi rm -f core conftest.err conftest.$ac_objext conftest.beam \ conftest$ac_exeext conftest.$ac_ext LDFLAGS=$save_LDFLAGS ;; esac fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $lt_cv_ld_exported_symbols_list" >&5 printf "%s\n" "$lt_cv_ld_exported_symbols_list" >&6; } { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for -force_load linker flag" >&5 printf %s "checking for -force_load linker flag... " >&6; } if test ${lt_cv_ld_force_load+y} then : printf %s "(cached) " >&6 else case e in #( e) lt_cv_ld_force_load=no cat > conftest.c << _LT_EOF int forced_loaded() { return 2;} _LT_EOF echo "$LTCC $LTCFLAGS -c -o conftest.o conftest.c" >&5 $LTCC $LTCFLAGS -c -o conftest.o conftest.c 2>&5 echo "$AR $AR_FLAGS libconftest.a conftest.o" >&5 $AR $AR_FLAGS libconftest.a conftest.o 2>&5 echo "$RANLIB libconftest.a" >&5 $RANLIB libconftest.a 2>&5 cat > conftest.c << _LT_EOF int main() { return 0;} _LT_EOF echo "$LTCC $LTCFLAGS $LDFLAGS -o conftest conftest.c -Wl,-force_load,./libconftest.a" >&5 $LTCC $LTCFLAGS $LDFLAGS -o conftest conftest.c -Wl,-force_load,./libconftest.a 2>conftest.err _lt_result=$? if test -s conftest.err && $GREP force_load conftest.err; then cat conftest.err >&5 elif test -f conftest && test 0 = "$_lt_result" && $GREP forced_load conftest >/dev/null 2>&1; then lt_cv_ld_force_load=yes else cat conftest.err >&5 fi rm -f conftest.err libconftest.a conftest conftest.c rm -rf conftest.dSYM ;; esac fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $lt_cv_ld_force_load" >&5 printf "%s\n" "$lt_cv_ld_force_load" >&6; } case $host_os in rhapsody* | darwin1.[012]) _lt_dar_allow_undefined='$wl-undefined ${wl}suppress' ;; darwin1.*) _lt_dar_allow_undefined='$wl-flat_namespace $wl-undefined ${wl}suppress' ;; darwin*) case $MACOSX_DEPLOYMENT_TARGET,$host in 10.[012],*|,*powerpc*-darwin[5-8]*) _lt_dar_allow_undefined='$wl-flat_namespace $wl-undefined ${wl}suppress' ;; *) _lt_dar_allow_undefined='$wl-undefined ${wl}dynamic_lookup' ;; esac ;; esac if test yes = "$lt_cv_apple_cc_single_mod"; then _lt_dar_single_mod='$single_module' fi if test yes = "$lt_cv_ld_exported_symbols_list"; then _lt_dar_export_syms=' $wl-exported_symbols_list,$output_objdir/$libname-symbols.expsym' else _lt_dar_export_syms='~$NMEDIT -s $output_objdir/$libname-symbols.expsym $lib' fi if test : != "$DSYMUTIL" && test no = "$lt_cv_ld_force_load"; then _lt_dsymutil='~$DSYMUTIL $lib || :' else _lt_dsymutil= fi ;; esac # func_munge_path_list VARIABLE PATH # ----------------------------------- # VARIABLE is name of variable containing _space_ separated list of # directories to be munged by the contents of PATH, which is string # having a format: # "DIR[:DIR]:" # string "DIR[ DIR]" will be prepended to VARIABLE # ":DIR[:DIR]" # string "DIR[ DIR]" will be appended to VARIABLE # "DIRP[:DIRP]::[DIRA:]DIRA" # string "DIRP[ DIRP]" will be prepended to VARIABLE and string # "DIRA[ DIRA]" will be appended to VARIABLE # "DIR[:DIR]" # VARIABLE will be replaced by "DIR[ DIR]" func_munge_path_list () { case x$2 in x) ;; *:) eval $1=\"`$ECHO $2 | $SED 's/:/ /g'` \$$1\" ;; x:*) eval $1=\"\$$1 `$ECHO $2 | $SED 's/:/ /g'`\" ;; *::*) eval $1=\"\$$1\ `$ECHO $2 | $SED -e 's/.*:://' -e 's/:/ /g'`\" eval $1=\"`$ECHO $2 | $SED -e 's/::.*//' -e 's/:/ /g'`\ \$$1\" ;; *) eval $1=\"`$ECHO $2 | $SED 's/:/ /g'`\" ;; esac } ac_header= ac_cache= for ac_item in $ac_header_c_list do if test $ac_cache; then ac_fn_c_check_header_compile "$LINENO" $ac_header ac_cv_header_$ac_cache "$ac_includes_default" if eval test \"x\$ac_cv_header_$ac_cache\" = xyes; then printf "%s\n" "#define $ac_item 1" >> confdefs.h fi ac_header= ac_cache= elif test $ac_header; then ac_cache=$ac_item else ac_header=$ac_item fi done if test $ac_cv_header_stdlib_h = yes && test $ac_cv_header_string_h = yes then : printf "%s\n" "#define STDC_HEADERS 1" >>confdefs.h fi ac_fn_c_check_header_compile "$LINENO" "dlfcn.h" "ac_cv_header_dlfcn_h" "$ac_includes_default " if test "x$ac_cv_header_dlfcn_h" = xyes then : printf "%s\n" "#define HAVE_DLFCN_H 1" >>confdefs.h fi # Set options # Check whether --enable-static was given. if test ${enable_static+y} then : enableval=$enable_static; p=${PACKAGE-default} case $enableval in yes) enable_static=yes ;; no) enable_static=no ;; *) enable_static=no # Look at the argument we got. We use all the common list separators. lt_save_ifs=$IFS; IFS=$IFS$PATH_SEPARATOR, for pkg in $enableval; do IFS=$lt_save_ifs if test "X$pkg" = "X$p"; then enable_static=yes fi done IFS=$lt_save_ifs ;; esac else case e in #( e) enable_static=no ;; esac fi enable_dlopen=no enable_win32_dll=no # Check whether --enable-shared was given. if test ${enable_shared+y} then : enableval=$enable_shared; p=${PACKAGE-default} case $enableval in yes) enable_shared=yes ;; no) enable_shared=no ;; *) enable_shared=no # Look at the argument we got. We use all the common list separators. lt_save_ifs=$IFS; IFS=$IFS$PATH_SEPARATOR, for pkg in $enableval; do IFS=$lt_save_ifs if test "X$pkg" = "X$p"; then enable_shared=yes fi done IFS=$lt_save_ifs ;; esac else case e in #( e) enable_shared=yes ;; esac fi # Check whether --with-pic was given. if test ${with_pic+y} then : withval=$with_pic; lt_p=${PACKAGE-default} case $withval in yes|no) pic_mode=$withval ;; *) pic_mode=default # Look at the argument we got. We use all the common list separators. lt_save_ifs=$IFS; IFS=$IFS$PATH_SEPARATOR, for lt_pkg in $withval; do IFS=$lt_save_ifs if test "X$lt_pkg" = "X$lt_p"; then pic_mode=yes fi done IFS=$lt_save_ifs ;; esac else case e in #( e) pic_mode=default ;; esac fi # Check whether --enable-fast-install was given. if test ${enable_fast_install+y} then : enableval=$enable_fast_install; p=${PACKAGE-default} case $enableval in yes) enable_fast_install=yes ;; no) enable_fast_install=no ;; *) enable_fast_install=no # Look at the argument we got. We use all the common list separators. lt_save_ifs=$IFS; IFS=$IFS$PATH_SEPARATOR, for pkg in $enableval; do IFS=$lt_save_ifs if test "X$pkg" = "X$p"; then enable_fast_install=yes fi done IFS=$lt_save_ifs ;; esac else case e in #( e) enable_fast_install=yes ;; esac fi shared_archive_member_spec= case $host,$enable_shared in power*-*-aix[5-9]*,yes) { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking which variant of shared library versioning to provide" >&5 printf %s "checking which variant of shared library versioning to provide... " >&6; } # Check whether --with-aix-soname was given. if test ${with_aix_soname+y} then : withval=$with_aix_soname; case $withval in aix|svr4|both) ;; *) as_fn_error $? "Unknown argument to --with-aix-soname" "$LINENO" 5 ;; esac lt_cv_with_aix_soname=$with_aix_soname else case e in #( e) if test ${lt_cv_with_aix_soname+y} then : printf %s "(cached) " >&6 else case e in #( e) lt_cv_with_aix_soname=aix ;; esac fi with_aix_soname=$lt_cv_with_aix_soname ;; esac fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $with_aix_soname" >&5 printf "%s\n" "$with_aix_soname" >&6; } if test aix != "$with_aix_soname"; then # For the AIX way of multilib, we name the shared archive member # based on the bitwidth used, traditionally 'shr.o' or 'shr_64.o', # and 'shr.imp' or 'shr_64.imp', respectively, for the Import File. # Even when GNU compilers ignore OBJECT_MODE but need '-maix64' flag, # the AIX toolchain works better with OBJECT_MODE set (default 32). if test 64 = "${OBJECT_MODE-32}"; then shared_archive_member_spec=shr_64 else shared_archive_member_spec=shr fi fi ;; *) with_aix_soname=aix ;; esac # This can be used to rebuild libtool when needed LIBTOOL_DEPS=$ltmain # Always use our own libtool. LIBTOOL='$(SHELL) $(top_builddir)/libtool' test -z "$LN_S" && LN_S="ln -s" if test -n "${ZSH_VERSION+set}"; then setopt NO_GLOB_SUBST fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for objdir" >&5 printf %s "checking for objdir... " >&6; } if test ${lt_cv_objdir+y} then : printf %s "(cached) " >&6 else case e in #( e) rm -f .libs 2>/dev/null mkdir .libs 2>/dev/null if test -d .libs; then lt_cv_objdir=.libs else # MS-DOS does not allow filenames that begin with a dot. lt_cv_objdir=_libs fi rmdir .libs 2>/dev/null ;; esac fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $lt_cv_objdir" >&5 printf "%s\n" "$lt_cv_objdir" >&6; } objdir=$lt_cv_objdir printf "%s\n" "#define LT_OBJDIR \"$lt_cv_objdir/\"" >>confdefs.h case $host_os in aix3*) # AIX sometimes has problems with the GCC collect2 program. For some # reason, if we set the COLLECT_NAMES environment variable, the problems # vanish in a puff of smoke. if test set != "${COLLECT_NAMES+set}"; then COLLECT_NAMES= export COLLECT_NAMES fi ;; esac # Global variables: ofile=libtool can_build_shared=yes # All known linkers require a '.a' archive for static linking (except MSVC and # ICC, which need '.lib'). libext=a with_gnu_ld=$lt_cv_prog_gnu_ld old_CC=$CC old_CFLAGS=$CFLAGS # Set sane defaults for various variables test -z "$CC" && CC=cc test -z "$LTCC" && LTCC=$CC test -z "$LTCFLAGS" && LTCFLAGS=$CFLAGS test -z "$LD" && LD=ld test -z "$ac_objext" && ac_objext=o func_cc_basename $compiler cc_basename=$func_cc_basename_result # Only perform the check for file, if the check method requires it test -z "$MAGIC_CMD" && MAGIC_CMD=file case $deplibs_check_method in file_magic*) if test "$file_magic_cmd" = '$MAGIC_CMD'; then { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for ${ac_tool_prefix}file" >&5 printf %s "checking for ${ac_tool_prefix}file... " >&6; } if test ${lt_cv_path_MAGIC_CMD+y} then : printf %s "(cached) " >&6 else case e in #( e) case $MAGIC_CMD in [\\/*] | ?:[\\/]*) lt_cv_path_MAGIC_CMD=$MAGIC_CMD # Let the user override the test with a path. ;; *) lt_save_MAGIC_CMD=$MAGIC_CMD lt_save_ifs=$IFS; IFS=$PATH_SEPARATOR ac_dummy="/usr/bin$PATH_SEPARATOR$PATH" for ac_dir in $ac_dummy; do IFS=$lt_save_ifs test -z "$ac_dir" && ac_dir=. if test -f "$ac_dir/${ac_tool_prefix}file"; then lt_cv_path_MAGIC_CMD=$ac_dir/"${ac_tool_prefix}file" if test -n "$file_magic_test_file"; then case $deplibs_check_method in "file_magic "*) file_magic_regex=`expr "$deplibs_check_method" : "file_magic \(.*\)"` MAGIC_CMD=$lt_cv_path_MAGIC_CMD if eval $file_magic_cmd \$file_magic_test_file 2> /dev/null | $EGREP "$file_magic_regex" > /dev/null; then : else cat <<_LT_EOF 1>&2 *** Warning: the command libtool uses to detect shared libraries, *** $file_magic_cmd, produces output that libtool cannot recognize. *** The result is that libtool may fail to recognize shared libraries *** as such. This will affect the creation of libtool libraries that *** depend on shared libraries, but programs linked with such libtool *** libraries will work regardless of this problem. Nevertheless, you *** may want to report the problem to your system manager and/or to *** bug-libtool@gnu.org _LT_EOF fi ;; esac fi break fi done IFS=$lt_save_ifs MAGIC_CMD=$lt_save_MAGIC_CMD ;; esac ;; esac fi MAGIC_CMD=$lt_cv_path_MAGIC_CMD if test -n "$MAGIC_CMD"; then { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $MAGIC_CMD" >&5 printf "%s\n" "$MAGIC_CMD" >&6; } else { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 printf "%s\n" "no" >&6; } fi if test -z "$lt_cv_path_MAGIC_CMD"; then if test -n "$ac_tool_prefix"; then { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for file" >&5 printf %s "checking for file... " >&6; } if test ${lt_cv_path_MAGIC_CMD+y} then : printf %s "(cached) " >&6 else case e in #( e) case $MAGIC_CMD in [\\/*] | ?:[\\/]*) lt_cv_path_MAGIC_CMD=$MAGIC_CMD # Let the user override the test with a path. ;; *) lt_save_MAGIC_CMD=$MAGIC_CMD lt_save_ifs=$IFS; IFS=$PATH_SEPARATOR ac_dummy="/usr/bin$PATH_SEPARATOR$PATH" for ac_dir in $ac_dummy; do IFS=$lt_save_ifs test -z "$ac_dir" && ac_dir=. if test -f "$ac_dir/file"; then lt_cv_path_MAGIC_CMD=$ac_dir/"file" if test -n "$file_magic_test_file"; then case $deplibs_check_method in "file_magic "*) file_magic_regex=`expr "$deplibs_check_method" : "file_magic \(.*\)"` MAGIC_CMD=$lt_cv_path_MAGIC_CMD if eval $file_magic_cmd \$file_magic_test_file 2> /dev/null | $EGREP "$file_magic_regex" > /dev/null; then : else cat <<_LT_EOF 1>&2 *** Warning: the command libtool uses to detect shared libraries, *** $file_magic_cmd, produces output that libtool cannot recognize. *** The result is that libtool may fail to recognize shared libraries *** as such. This will affect the creation of libtool libraries that *** depend on shared libraries, but programs linked with such libtool *** libraries will work regardless of this problem. Nevertheless, you *** may want to report the problem to your system manager and/or to *** bug-libtool@gnu.org _LT_EOF fi ;; esac fi break fi done IFS=$lt_save_ifs MAGIC_CMD=$lt_save_MAGIC_CMD ;; esac ;; esac fi MAGIC_CMD=$lt_cv_path_MAGIC_CMD if test -n "$MAGIC_CMD"; then { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $MAGIC_CMD" >&5 printf "%s\n" "$MAGIC_CMD" >&6; } else { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 printf "%s\n" "no" >&6; } fi else MAGIC_CMD=: fi fi fi ;; esac # Use C for the default configuration in the libtool script lt_save_CC=$CC ac_ext=c ac_cpp='$CPP $CPPFLAGS' ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' ac_compiler_gnu=$ac_cv_c_compiler_gnu # Source file extension for C test sources. ac_ext=c # Object file extension for compiled C test sources. objext=o objext=$objext # Code to be used in simple compile tests lt_simple_compile_test_code="int some_variable = 0;" # Code to be used in simple link tests lt_simple_link_test_code='int main(){return(0);}' # If no C compiler was specified, use CC. LTCC=${LTCC-"$CC"} # If no C compiler flags were specified, use CFLAGS. LTCFLAGS=${LTCFLAGS-"$CFLAGS"} # Allow CC to be a program name with arguments. compiler=$CC # Save the default compiler, since it gets overwritten when the other # tags are being tested, and _LT_TAGVAR(compiler, []) is a NOP. compiler_DEFAULT=$CC # save warnings/boilerplate of simple test code ac_outfile=conftest.$ac_objext echo "$lt_simple_compile_test_code" >conftest.$ac_ext eval "$ac_compile" 2>&1 >/dev/null | $SED '/^$/d; /^ *+/d' >conftest.err _lt_compiler_boilerplate=`cat conftest.err` $RM conftest* ac_outfile=conftest.$ac_objext echo "$lt_simple_link_test_code" >conftest.$ac_ext eval "$ac_link" 2>&1 >/dev/null | $SED '/^$/d; /^ *+/d' >conftest.err _lt_linker_boilerplate=`cat conftest.err` $RM -r conftest* if test -n "$compiler"; then lt_prog_compiler_no_builtin_flag= if test yes = "$GCC"; then case $cc_basename in nvcc*) lt_prog_compiler_no_builtin_flag=' -Xcompiler -fno-builtin' ;; *) lt_prog_compiler_no_builtin_flag=' -fno-builtin' ;; esac { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking if $compiler supports -fno-rtti -fno-exceptions" >&5 printf %s "checking if $compiler supports -fno-rtti -fno-exceptions... " >&6; } if test ${lt_cv_prog_compiler_rtti_exceptions+y} then : printf %s "(cached) " >&6 else case e in #( e) lt_cv_prog_compiler_rtti_exceptions=no ac_outfile=conftest.$ac_objext echo "$lt_simple_compile_test_code" > conftest.$ac_ext lt_compiler_flag="-fno-rtti -fno-exceptions" ## exclude from sc_useless_quotes_in_assignment # Insert the option either (1) after the last *FLAGS variable, or # (2) before a word containing "conftest.", or (3) at the end. # Note that $ac_compile itself does not contain backslashes and begins # with a dollar sign (not a hyphen), so the echo should work correctly. # The option is referenced via a variable to avoid confusing sed. lt_compile=`echo "$ac_compile" | $SED \ -e 's:.*FLAGS}\{0,1\} :&$lt_compiler_flag :; t' \ -e 's: [^ ]*conftest\.: $lt_compiler_flag&:; t' \ -e 's:$: $lt_compiler_flag:'` (eval echo "\"\$as_me:$LINENO: $lt_compile\"" >&5) (eval "$lt_compile" 2>conftest.err) ac_status=$? cat conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 if (exit $ac_status) && test -s "$ac_outfile"; then # The compiler can only warn and ignore the option if not recognized # So say no if there are warnings other than the usual output. $ECHO "$_lt_compiler_boilerplate" | $SED '/^$/d' >conftest.exp $SED '/^$/d; /^ *+/d' conftest.err >conftest.er2 if test ! -s conftest.er2 || diff conftest.exp conftest.er2 >/dev/null; then lt_cv_prog_compiler_rtti_exceptions=yes fi fi $RM conftest* ;; esac fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $lt_cv_prog_compiler_rtti_exceptions" >&5 printf "%s\n" "$lt_cv_prog_compiler_rtti_exceptions" >&6; } if test yes = "$lt_cv_prog_compiler_rtti_exceptions"; then lt_prog_compiler_no_builtin_flag="$lt_prog_compiler_no_builtin_flag -fno-rtti -fno-exceptions" else : fi fi lt_prog_compiler_wl= lt_prog_compiler_pic= lt_prog_compiler_static= if test yes = "$GCC"; then lt_prog_compiler_wl='-Wl,' lt_prog_compiler_static='-static' case $host_os in aix*) # All AIX code is PIC. if test ia64 = "$host_cpu"; then # AIX 5 now supports IA64 processor lt_prog_compiler_static='-Bstatic' fi lt_prog_compiler_pic='-fPIC' ;; amigaos*) case $host_cpu in powerpc) # see comment about AmigaOS4 .so support lt_prog_compiler_pic='-fPIC' ;; m68k) # FIXME: we need at least 68020 code to build shared libraries, but # adding the '-m68020' flag to GCC prevents building anything better, # like '-m68040'. lt_prog_compiler_pic='-m68020 -resident32 -malways-restore-a4' ;; esac ;; beos* | irix5* | irix6* | nonstopux* | osf3* | osf4* | osf5*) # PIC is the default for these OSes. ;; mingw* | cygwin* | pw32* | os2* | cegcc*) # This hack is so that the source file can tell whether it is being # built for inclusion in a dll (and should export symbols for example). # Although the cygwin gcc ignores -fPIC, still need this for old-style # (--disable-auto-import) libraries lt_prog_compiler_pic='-DDLL_EXPORT' case $host_os in os2*) lt_prog_compiler_static='$wl-static' ;; esac ;; darwin* | rhapsody*) # PIC is the default on this platform # Common symbols not allowed in MH_DYLIB files lt_prog_compiler_pic='-fno-common' ;; haiku*) # PIC is the default for Haiku. # The "-static" flag exists, but is broken. lt_prog_compiler_static= ;; hpux*) # PIC is the default for 64-bit PA HP-UX, but not for 32-bit # PA HP-UX. On IA64 HP-UX, PIC is the default but the pic flag # sets the default TLS model and affects inlining. case $host_cpu in hppa*64*) # +Z the default ;; *) lt_prog_compiler_pic='-fPIC' ;; esac ;; interix[3-9]*) # Interix 3.x gcc -fpic/-fPIC options generate broken code. # Instead, we relocate shared libraries at runtime. ;; msdosdjgpp*) # Just because we use GCC doesn't mean we suddenly get shared libraries # on systems that don't support them. lt_prog_compiler_can_build_shared=no enable_shared=no ;; *nto* | *qnx*) # QNX uses GNU C++, but need to define -shared option too, otherwise # it will coredump. lt_prog_compiler_pic='-fPIC -shared' ;; sysv4*MP*) if test -d /usr/nec; then lt_prog_compiler_pic=-Kconform_pic fi ;; *) lt_prog_compiler_pic='-fPIC' ;; esac case $cc_basename in nvcc*) # Cuda Compiler Driver 2.2 lt_prog_compiler_wl='-Xlinker ' if test -n "$lt_prog_compiler_pic"; then lt_prog_compiler_pic="-Xcompiler $lt_prog_compiler_pic" fi ;; esac else # PORTME Check for flag to pass linker flags through the system compiler. case $host_os in aix*) lt_prog_compiler_wl='-Wl,' if test ia64 = "$host_cpu"; then # AIX 5 now supports IA64 processor lt_prog_compiler_static='-Bstatic' else lt_prog_compiler_static='-bnso -bI:/lib/syscalls.exp' fi ;; darwin* | rhapsody*) # PIC is the default on this platform # Common symbols not allowed in MH_DYLIB files lt_prog_compiler_pic='-fno-common' case $cc_basename in nagfor*) # NAG Fortran compiler lt_prog_compiler_wl='-Wl,-Wl,,' lt_prog_compiler_pic='-PIC' lt_prog_compiler_static='-Bstatic' ;; esac ;; mingw* | cygwin* | pw32* | os2* | cegcc*) # This hack is so that the source file can tell whether it is being # built for inclusion in a dll (and should export symbols for example). lt_prog_compiler_pic='-DDLL_EXPORT' case $host_os in os2*) lt_prog_compiler_static='$wl-static' ;; esac ;; hpux9* | hpux10* | hpux11*) lt_prog_compiler_wl='-Wl,' # PIC is the default for IA64 HP-UX and 64-bit HP-UX, but # not for PA HP-UX. case $host_cpu in hppa*64*|ia64*) # +Z the default ;; *) lt_prog_compiler_pic='+Z' ;; esac # Is there a better lt_prog_compiler_static that works with the bundled CC? lt_prog_compiler_static='$wl-a ${wl}archive' ;; irix5* | irix6* | nonstopux*) lt_prog_compiler_wl='-Wl,' # PIC (with -KPIC) is the default. lt_prog_compiler_static='-non_shared' ;; linux* | k*bsd*-gnu | kopensolaris*-gnu | gnu*) case $cc_basename in # old Intel for x86_64, which still supported -KPIC. ecc*) lt_prog_compiler_wl='-Wl,' lt_prog_compiler_pic='-KPIC' lt_prog_compiler_static='-static' ;; # flang / f18. f95 an alias for gfortran or flang on Debian flang* | f18* | f95*) lt_prog_compiler_wl='-Wl,' lt_prog_compiler_pic='-fPIC' lt_prog_compiler_static='-static' ;; # icc used to be incompatible with GCC. # ICC 10 doesn't accept -KPIC any more. icc* | ifort*) lt_prog_compiler_wl='-Wl,' lt_prog_compiler_pic='-fPIC' lt_prog_compiler_static='-static' ;; # Lahey Fortran 8.1. lf95*) lt_prog_compiler_wl='-Wl,' lt_prog_compiler_pic='--shared' lt_prog_compiler_static='--static' ;; nagfor*) # NAG Fortran compiler lt_prog_compiler_wl='-Wl,-Wl,,' lt_prog_compiler_pic='-PIC' lt_prog_compiler_static='-Bstatic' ;; tcc*) # Fabrice Bellard et al's Tiny C Compiler lt_prog_compiler_wl='-Wl,' lt_prog_compiler_pic='-fPIC' lt_prog_compiler_static='-static' ;; pgcc* | pgf77* | pgf90* | pgf95* | pgfortran*) # Portland Group compilers (*not* the Pentium gcc compiler, # which looks to be a dead project) lt_prog_compiler_wl='-Wl,' lt_prog_compiler_pic='-fpic' lt_prog_compiler_static='-Bstatic' ;; ccc*) lt_prog_compiler_wl='-Wl,' # All Alpha code is PIC. lt_prog_compiler_static='-non_shared' ;; xl* | bgxl* | bgf* | mpixl*) # IBM XL C 8.0/Fortran 10.1, 11.1 on PPC and BlueGene lt_prog_compiler_wl='-Wl,' lt_prog_compiler_pic='-qpic' lt_prog_compiler_static='-qstaticlink' ;; *) case `$CC -V 2>&1 | $SED 5q` in *Sun\ Ceres\ Fortran* | *Sun*Fortran*\ [1-7].* | *Sun*Fortran*\ 8.[0-3]*) # Sun Fortran 8.3 passes all unrecognized flags to the linker lt_prog_compiler_pic='-KPIC' lt_prog_compiler_static='-Bstatic' lt_prog_compiler_wl='' ;; *Sun\ F* | *Sun*Fortran*) lt_prog_compiler_pic='-KPIC' lt_prog_compiler_static='-Bstatic' lt_prog_compiler_wl='-Qoption ld ' ;; *Sun\ C*) # Sun C 5.9 lt_prog_compiler_pic='-KPIC' lt_prog_compiler_static='-Bstatic' lt_prog_compiler_wl='-Wl,' ;; *Intel*\ [CF]*Compiler*) lt_prog_compiler_wl='-Wl,' lt_prog_compiler_pic='-fPIC' lt_prog_compiler_static='-static' ;; *Portland\ Group*) lt_prog_compiler_wl='-Wl,' lt_prog_compiler_pic='-fpic' lt_prog_compiler_static='-Bstatic' ;; esac ;; esac ;; newsos6) lt_prog_compiler_pic='-KPIC' lt_prog_compiler_static='-Bstatic' ;; *nto* | *qnx*) # QNX uses GNU C++, but need to define -shared option too, otherwise # it will coredump. lt_prog_compiler_pic='-fPIC -shared' ;; osf3* | osf4* | osf5*) lt_prog_compiler_wl='-Wl,' # All OSF/1 code is PIC. lt_prog_compiler_static='-non_shared' ;; rdos*) lt_prog_compiler_static='-non_shared' ;; solaris*) lt_prog_compiler_pic='-KPIC' lt_prog_compiler_static='-Bstatic' case $cc_basename in f77* | f90* | f95* | sunf77* | sunf90* | sunf95*) lt_prog_compiler_wl='-Qoption ld ';; *) lt_prog_compiler_wl='-Wl,';; esac ;; sunos4*) lt_prog_compiler_wl='-Qoption ld ' lt_prog_compiler_pic='-PIC' lt_prog_compiler_static='-Bstatic' ;; sysv4 | sysv4.2uw2* | sysv4.3*) lt_prog_compiler_wl='-Wl,' lt_prog_compiler_pic='-KPIC' lt_prog_compiler_static='-Bstatic' ;; sysv4*MP*) if test -d /usr/nec; then lt_prog_compiler_pic='-Kconform_pic' lt_prog_compiler_static='-Bstatic' fi ;; sysv5* | unixware* | sco3.2v5* | sco5v6* | OpenUNIX*) lt_prog_compiler_wl='-Wl,' lt_prog_compiler_pic='-KPIC' lt_prog_compiler_static='-Bstatic' ;; unicos*) lt_prog_compiler_wl='-Wl,' lt_prog_compiler_can_build_shared=no ;; uts4*) lt_prog_compiler_pic='-pic' lt_prog_compiler_static='-Bstatic' ;; *) lt_prog_compiler_can_build_shared=no ;; esac fi case $host_os in # For platforms that do not support PIC, -DPIC is meaningless: *djgpp*) lt_prog_compiler_pic= ;; *) lt_prog_compiler_pic="$lt_prog_compiler_pic -DPIC" ;; esac { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $compiler option to produce PIC" >&5 printf %s "checking for $compiler option to produce PIC... " >&6; } if test ${lt_cv_prog_compiler_pic+y} then : printf %s "(cached) " >&6 else case e in #( e) lt_cv_prog_compiler_pic=$lt_prog_compiler_pic ;; esac fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $lt_cv_prog_compiler_pic" >&5 printf "%s\n" "$lt_cv_prog_compiler_pic" >&6; } lt_prog_compiler_pic=$lt_cv_prog_compiler_pic # # Check to make sure the PIC flag actually works. # if test -n "$lt_prog_compiler_pic"; then { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking if $compiler PIC flag $lt_prog_compiler_pic works" >&5 printf %s "checking if $compiler PIC flag $lt_prog_compiler_pic works... " >&6; } if test ${lt_cv_prog_compiler_pic_works+y} then : printf %s "(cached) " >&6 else case e in #( e) lt_cv_prog_compiler_pic_works=no ac_outfile=conftest.$ac_objext echo "$lt_simple_compile_test_code" > conftest.$ac_ext lt_compiler_flag="$lt_prog_compiler_pic -DPIC" ## exclude from sc_useless_quotes_in_assignment # Insert the option either (1) after the last *FLAGS variable, or # (2) before a word containing "conftest.", or (3) at the end. # Note that $ac_compile itself does not contain backslashes and begins # with a dollar sign (not a hyphen), so the echo should work correctly. # The option is referenced via a variable to avoid confusing sed. lt_compile=`echo "$ac_compile" | $SED \ -e 's:.*FLAGS}\{0,1\} :&$lt_compiler_flag :; t' \ -e 's: [^ ]*conftest\.: $lt_compiler_flag&:; t' \ -e 's:$: $lt_compiler_flag:'` (eval echo "\"\$as_me:$LINENO: $lt_compile\"" >&5) (eval "$lt_compile" 2>conftest.err) ac_status=$? cat conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 if (exit $ac_status) && test -s "$ac_outfile"; then # The compiler can only warn and ignore the option if not recognized # So say no if there are warnings other than the usual output. $ECHO "$_lt_compiler_boilerplate" | $SED '/^$/d' >conftest.exp $SED '/^$/d; /^ *+/d' conftest.err >conftest.er2 if test ! -s conftest.er2 || diff conftest.exp conftest.er2 >/dev/null; then lt_cv_prog_compiler_pic_works=yes fi fi $RM conftest* ;; esac fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $lt_cv_prog_compiler_pic_works" >&5 printf "%s\n" "$lt_cv_prog_compiler_pic_works" >&6; } if test yes = "$lt_cv_prog_compiler_pic_works"; then case $lt_prog_compiler_pic in "" | " "*) ;; *) lt_prog_compiler_pic=" $lt_prog_compiler_pic" ;; esac else lt_prog_compiler_pic= lt_prog_compiler_can_build_shared=no fi fi # # Check to make sure the static flag actually works. # wl=$lt_prog_compiler_wl eval lt_tmp_static_flag=\"$lt_prog_compiler_static\" { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking if $compiler static flag $lt_tmp_static_flag works" >&5 printf %s "checking if $compiler static flag $lt_tmp_static_flag works... " >&6; } if test ${lt_cv_prog_compiler_static_works+y} then : printf %s "(cached) " >&6 else case e in #( e) lt_cv_prog_compiler_static_works=no save_LDFLAGS=$LDFLAGS LDFLAGS="$LDFLAGS $lt_tmp_static_flag" echo "$lt_simple_link_test_code" > conftest.$ac_ext if (eval $ac_link 2>conftest.err) && test -s conftest$ac_exeext; then # The linker can only warn and ignore the option if not recognized # So say no if there are warnings if test -s conftest.err; then # Append any errors to the config.log. cat conftest.err 1>&5 $ECHO "$_lt_linker_boilerplate" | $SED '/^$/d' > conftest.exp $SED '/^$/d; /^ *+/d' conftest.err >conftest.er2 if diff conftest.exp conftest.er2 >/dev/null; then lt_cv_prog_compiler_static_works=yes fi else lt_cv_prog_compiler_static_works=yes fi fi $RM -r conftest* LDFLAGS=$save_LDFLAGS ;; esac fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $lt_cv_prog_compiler_static_works" >&5 printf "%s\n" "$lt_cv_prog_compiler_static_works" >&6; } if test yes = "$lt_cv_prog_compiler_static_works"; then : else lt_prog_compiler_static= fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking if $compiler supports -c -o file.$ac_objext" >&5 printf %s "checking if $compiler supports -c -o file.$ac_objext... " >&6; } if test ${lt_cv_prog_compiler_c_o+y} then : printf %s "(cached) " >&6 else case e in #( e) lt_cv_prog_compiler_c_o=no $RM -r conftest 2>/dev/null mkdir conftest cd conftest mkdir out echo "$lt_simple_compile_test_code" > conftest.$ac_ext lt_compiler_flag="-o out/conftest2.$ac_objext" # Insert the option either (1) after the last *FLAGS variable, or # (2) before a word containing "conftest.", or (3) at the end. # Note that $ac_compile itself does not contain backslashes and begins # with a dollar sign (not a hyphen), so the echo should work correctly. lt_compile=`echo "$ac_compile" | $SED \ -e 's:.*FLAGS}\{0,1\} :&$lt_compiler_flag :; t' \ -e 's: [^ ]*conftest\.: $lt_compiler_flag&:; t' \ -e 's:$: $lt_compiler_flag:'` (eval echo "\"\$as_me:$LINENO: $lt_compile\"" >&5) (eval "$lt_compile" 2>out/conftest.err) ac_status=$? cat out/conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 if (exit $ac_status) && test -s out/conftest2.$ac_objext then # The compiler can only warn and ignore the option if not recognized # So say no if there are warnings $ECHO "$_lt_compiler_boilerplate" | $SED '/^$/d' > out/conftest.exp $SED '/^$/d; /^ *+/d' out/conftest.err >out/conftest.er2 if test ! -s out/conftest.er2 || diff out/conftest.exp out/conftest.er2 >/dev/null; then lt_cv_prog_compiler_c_o=yes fi fi chmod u+w . 2>&5 $RM conftest* # SGI C++ compiler will create directory out/ii_files/ for # template instantiation test -d out/ii_files && $RM out/ii_files/* && rmdir out/ii_files $RM out/* && rmdir out cd .. $RM -r conftest $RM conftest* ;; esac fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $lt_cv_prog_compiler_c_o" >&5 printf "%s\n" "$lt_cv_prog_compiler_c_o" >&6; } { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking if $compiler supports -c -o file.$ac_objext" >&5 printf %s "checking if $compiler supports -c -o file.$ac_objext... " >&6; } if test ${lt_cv_prog_compiler_c_o+y} then : printf %s "(cached) " >&6 else case e in #( e) lt_cv_prog_compiler_c_o=no $RM -r conftest 2>/dev/null mkdir conftest cd conftest mkdir out echo "$lt_simple_compile_test_code" > conftest.$ac_ext lt_compiler_flag="-o out/conftest2.$ac_objext" # Insert the option either (1) after the last *FLAGS variable, or # (2) before a word containing "conftest.", or (3) at the end. # Note that $ac_compile itself does not contain backslashes and begins # with a dollar sign (not a hyphen), so the echo should work correctly. lt_compile=`echo "$ac_compile" | $SED \ -e 's:.*FLAGS}\{0,1\} :&$lt_compiler_flag :; t' \ -e 's: [^ ]*conftest\.: $lt_compiler_flag&:; t' \ -e 's:$: $lt_compiler_flag:'` (eval echo "\"\$as_me:$LINENO: $lt_compile\"" >&5) (eval "$lt_compile" 2>out/conftest.err) ac_status=$? cat out/conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 if (exit $ac_status) && test -s out/conftest2.$ac_objext then # The compiler can only warn and ignore the option if not recognized # So say no if there are warnings $ECHO "$_lt_compiler_boilerplate" | $SED '/^$/d' > out/conftest.exp $SED '/^$/d; /^ *+/d' out/conftest.err >out/conftest.er2 if test ! -s out/conftest.er2 || diff out/conftest.exp out/conftest.er2 >/dev/null; then lt_cv_prog_compiler_c_o=yes fi fi chmod u+w . 2>&5 $RM conftest* # SGI C++ compiler will create directory out/ii_files/ for # template instantiation test -d out/ii_files && $RM out/ii_files/* && rmdir out/ii_files $RM out/* && rmdir out cd .. $RM -r conftest $RM conftest* ;; esac fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $lt_cv_prog_compiler_c_o" >&5 printf "%s\n" "$lt_cv_prog_compiler_c_o" >&6; } hard_links=nottested if test no = "$lt_cv_prog_compiler_c_o" && test no != "$need_locks"; then # do not overwrite the value of need_locks provided by the user { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking if we can lock with hard links" >&5 printf %s "checking if we can lock with hard links... " >&6; } hard_links=yes $RM conftest* ln conftest.a conftest.b 2>/dev/null && hard_links=no touch conftest.a ln conftest.a conftest.b 2>&5 || hard_links=no ln conftest.a conftest.b 2>/dev/null && hard_links=no { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $hard_links" >&5 printf "%s\n" "$hard_links" >&6; } if test no = "$hard_links"; then { printf "%s\n" "$as_me:${as_lineno-$LINENO}: WARNING: '$CC' does not support '-c -o', so 'make -j' may be unsafe" >&5 printf "%s\n" "$as_me: WARNING: '$CC' does not support '-c -o', so 'make -j' may be unsafe" >&2;} need_locks=warn fi else need_locks=no fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking whether the $compiler linker ($LD) supports shared libraries" >&5 printf %s "checking whether the $compiler linker ($LD) supports shared libraries... " >&6; } runpath_var= allow_undefined_flag= always_export_symbols=no archive_cmds= archive_expsym_cmds= compiler_needs_object=no enable_shared_with_static_runtimes=no export_dynamic_flag_spec= export_symbols_cmds='$NM $libobjs $convenience | $global_symbol_pipe | $SED '\''s/.* //'\'' | sort | uniq > $export_symbols' hardcode_automatic=no hardcode_direct=no hardcode_direct_absolute=no hardcode_libdir_flag_spec= hardcode_libdir_separator= hardcode_minus_L=no hardcode_shlibpath_var=unsupported inherit_rpath=no link_all_deplibs=unknown module_cmds= module_expsym_cmds= old_archive_from_new_cmds= old_archive_from_expsyms_cmds= thread_safe_flag_spec= whole_archive_flag_spec= # include_expsyms should be a list of space-separated symbols to be *always* # included in the symbol list include_expsyms= # exclude_expsyms can be an extended regexp of symbols to exclude # it will be wrapped by ' (' and ')$', so one must not match beginning or # end of line. Example: 'a|bc|.*d.*' will exclude the symbols 'a' and 'bc', # as well as any symbol that contains 'd'. exclude_expsyms='_GLOBAL_OFFSET_TABLE_|_GLOBAL__F[ID]_.*' # Although _GLOBAL_OFFSET_TABLE_ is a valid symbol C name, most a.out # platforms (ab)use it in PIC code, but their linkers get confused if # the symbol is explicitly referenced. Since portable code cannot # rely on this symbol name, it's probably fine to never include it in # preloaded symbol tables. # Exclude shared library initialization/finalization symbols. extract_expsyms_cmds= case $host_os in cygwin* | mingw* | pw32* | cegcc*) # FIXME: the MSVC++ and ICC port hasn't been tested in a loooong time # When not using gcc, we currently assume that we are using # Microsoft Visual C++ or Intel C++ Compiler. if test yes != "$GCC"; then with_gnu_ld=no fi ;; interix*) # we just hope/assume this is gcc and not c89 (= MSVC++ or ICC) with_gnu_ld=yes ;; openbsd* | bitrig*) with_gnu_ld=no ;; linux* | k*bsd*-gnu | gnu*) link_all_deplibs=no ;; esac ld_shlibs=yes # On some targets, GNU ld is compatible enough with the native linker # that we're better off using the native interface for both. lt_use_gnu_ld_interface=no if test yes = "$with_gnu_ld"; then case $host_os in aix*) # The AIX port of GNU ld has always aspired to compatibility # with the native linker. However, as the warning in the GNU ld # block says, versions before 2.19.5* couldn't really create working # shared libraries, regardless of the interface used. case `$LD -v 2>&1` in *\ \(GNU\ Binutils\)\ 2.19.5*) ;; *\ \(GNU\ Binutils\)\ 2.[2-9]*) ;; *\ \(GNU\ Binutils\)\ [3-9]*) ;; *) lt_use_gnu_ld_interface=yes ;; esac ;; *) lt_use_gnu_ld_interface=yes ;; esac fi if test yes = "$lt_use_gnu_ld_interface"; then # If archive_cmds runs LD, not CC, wlarc should be empty wlarc='$wl' # Set some defaults for GNU ld with shared library support. These # are reset later if shared libraries are not supported. Putting them # here allows them to be overridden if necessary. runpath_var=LD_RUN_PATH hardcode_libdir_flag_spec='$wl-rpath $wl$libdir' export_dynamic_flag_spec='$wl--export-dynamic' # ancient GNU ld didn't support --whole-archive et. al. if $LD --help 2>&1 | $GREP 'no-whole-archive' > /dev/null; then whole_archive_flag_spec=$wlarc'--whole-archive$convenience '$wlarc'--no-whole-archive' else whole_archive_flag_spec= fi supports_anon_versioning=no case `$LD -v | $SED -e 's/([^)]\+)\s\+//' 2>&1` in *GNU\ gold*) supports_anon_versioning=yes ;; *\ [01].* | *\ 2.[0-9].* | *\ 2.10.*) ;; # catch versions < 2.11 *\ 2.11.93.0.2\ *) supports_anon_versioning=yes ;; # RH7.3 ... *\ 2.11.92.0.12\ *) supports_anon_versioning=yes ;; # Mandrake 8.2 ... *\ 2.11.*) ;; # other 2.11 versions *) supports_anon_versioning=yes ;; esac # See if GNU ld supports shared libraries. case $host_os in aix[3-9]*) # On AIX/PPC, the GNU linker is very broken if test ia64 != "$host_cpu"; then ld_shlibs=no cat <<_LT_EOF 1>&2 *** Warning: the GNU linker, at least up to release 2.19, is reported *** to be unable to reliably create shared libraries on AIX. *** Therefore, libtool is disabling shared libraries support. If you *** really care for shared libraries, you may want to install binutils *** 2.20 or above, or modify your PATH so that a non-GNU linker is found. *** You will then need to restart the configuration process. _LT_EOF fi ;; amigaos*) case $host_cpu in powerpc) # see comment about AmigaOS4 .so support archive_cmds='$CC -shared $libobjs $deplibs $compiler_flags $wl-soname $wl$soname -o $lib' archive_expsym_cmds='' ;; m68k) archive_cmds='$RM $output_objdir/a2ixlibrary.data~$ECHO "#define NAME $libname" > $output_objdir/a2ixlibrary.data~$ECHO "#define LIBRARY_ID 1" >> $output_objdir/a2ixlibrary.data~$ECHO "#define VERSION $major" >> $output_objdir/a2ixlibrary.data~$ECHO "#define REVISION $revision" >> $output_objdir/a2ixlibrary.data~$AR $AR_FLAGS $lib $libobjs~$RANLIB $lib~(cd $output_objdir && a2ixlibrary -32)' hardcode_libdir_flag_spec='-L$libdir' hardcode_minus_L=yes ;; esac ;; beos*) if $LD --help 2>&1 | $GREP ': supported targets:.* elf' > /dev/null; then allow_undefined_flag=unsupported # Joseph Beckenbach says some releases of gcc # support --undefined. This deserves some investigation. FIXME archive_cmds='$CC -nostart $libobjs $deplibs $compiler_flags $wl-soname $wl$soname -o $lib' else ld_shlibs=no fi ;; cygwin* | mingw* | pw32* | cegcc*) # _LT_TAGVAR(hardcode_libdir_flag_spec, ) is actually meaningless, # as there is no search path for DLLs. hardcode_libdir_flag_spec='-L$libdir' export_dynamic_flag_spec='$wl--export-all-symbols' allow_undefined_flag=unsupported always_export_symbols=no enable_shared_with_static_runtimes=yes export_symbols_cmds='$NM $libobjs $convenience | $global_symbol_pipe | $SED -e '\''/^[BCDGRS][ ]/s/.*[ ]\([^ ]*\)/\1 DATA/;s/^.*[ ]__nm__\([^ ]*\)[ ][^ ]*/\1 DATA/;/^I[ ]/d;/^[AITW][ ]/s/.* //'\'' | sort | uniq > $export_symbols' exclude_expsyms='[_]+GLOBAL_OFFSET_TABLE_|[_]+GLOBAL__[FID]_.*|[_]+head_[A-Za-z0-9_]+_dll|[A-Za-z0-9_]+_dll_iname' if $LD --help 2>&1 | $GREP 'auto-import' > /dev/null; then archive_cmds='$CC -shared $libobjs $deplibs $compiler_flags -o $output_objdir/$soname $wl--enable-auto-image-base -Xlinker --out-implib -Xlinker $lib' # If the export-symbols file already is a .def file, use it as # is; otherwise, prepend EXPORTS... archive_expsym_cmds='if test DEF = "`$SED -n -e '\''s/^[ ]*//'\'' -e '\''/^\(;.*\)*$/d'\'' -e '\''s/^\(EXPORTS\|LIBRARY\)\([ ].*\)*$/DEF/p'\'' -e q $export_symbols`" ; then cp $export_symbols $output_objdir/$soname.def; else echo EXPORTS > $output_objdir/$soname.def; cat $export_symbols >> $output_objdir/$soname.def; fi~ $CC -shared $output_objdir/$soname.def $libobjs $deplibs $compiler_flags -o $output_objdir/$soname $wl--enable-auto-image-base -Xlinker --out-implib -Xlinker $lib' else ld_shlibs=no fi ;; haiku*) archive_cmds='$CC -shared $libobjs $deplibs $compiler_flags $wl-soname $wl$soname -o $lib' link_all_deplibs=yes ;; os2*) hardcode_libdir_flag_spec='-L$libdir' hardcode_minus_L=yes allow_undefined_flag=unsupported shrext_cmds=.dll archive_cmds='$ECHO "LIBRARY ${soname%$shared_ext} INITINSTANCE TERMINSTANCE" > $output_objdir/$libname.def~ $ECHO "DESCRIPTION \"$libname\"" >> $output_objdir/$libname.def~ $ECHO "DATA MULTIPLE NONSHARED" >> $output_objdir/$libname.def~ $ECHO EXPORTS >> $output_objdir/$libname.def~ emxexp $libobjs | $SED /"_DLL_InitTerm"/d >> $output_objdir/$libname.def~ $CC -Zdll -Zcrtdll -o $output_objdir/$soname $libobjs $deplibs $compiler_flags $output_objdir/$libname.def~ emximp -o $lib $output_objdir/$libname.def' archive_expsym_cmds='$ECHO "LIBRARY ${soname%$shared_ext} INITINSTANCE TERMINSTANCE" > $output_objdir/$libname.def~ $ECHO "DESCRIPTION \"$libname\"" >> $output_objdir/$libname.def~ $ECHO "DATA MULTIPLE NONSHARED" >> $output_objdir/$libname.def~ $ECHO EXPORTS >> $output_objdir/$libname.def~ prefix_cmds="$SED"~ if test EXPORTS = "`$SED 1q $export_symbols`"; then prefix_cmds="$prefix_cmds -e 1d"; fi~ prefix_cmds="$prefix_cmds -e \"s/^\(.*\)$/_\1/g\""~ cat $export_symbols | $prefix_cmds >> $output_objdir/$libname.def~ $CC -Zdll -Zcrtdll -o $output_objdir/$soname $libobjs $deplibs $compiler_flags $output_objdir/$libname.def~ emximp -o $lib $output_objdir/$libname.def' old_archive_From_new_cmds='emximp -o $output_objdir/${libname}_dll.a $output_objdir/$libname.def' enable_shared_with_static_runtimes=yes file_list_spec='@' ;; interix[3-9]*) hardcode_direct=no hardcode_shlibpath_var=no hardcode_libdir_flag_spec='$wl-rpath,$libdir' export_dynamic_flag_spec='$wl-E' # Hack: On Interix 3.x, we cannot compile PIC because of a broken gcc. # Instead, shared libraries are loaded at an image base (0x10000000 by # default) and relocated if they conflict, which is a slow very memory # consuming and fragmenting process. To avoid this, we pick a random, # 256 KiB-aligned image base between 0x50000000 and 0x6FFC0000 at link # time. Moving up from 0x10000000 also allows more sbrk(2) space. archive_cmds='$CC -shared $pic_flag $libobjs $deplibs $compiler_flags $wl-h,$soname $wl--image-base,`expr ${RANDOM-$$} % 4096 / 2 \* 262144 + 1342177280` -o $lib' archive_expsym_cmds='$SED "s|^|_|" $export_symbols >$output_objdir/$soname.expsym~$CC -shared $pic_flag $libobjs $deplibs $compiler_flags $wl-h,$soname $wl--retain-symbols-file,$output_objdir/$soname.expsym $wl--image-base,`expr ${RANDOM-$$} % 4096 / 2 \* 262144 + 1342177280` -o $lib' ;; gnu* | linux* | tpf* | k*bsd*-gnu | kopensolaris*-gnu) tmp_diet=no if test linux-dietlibc = "$host_os"; then case $cc_basename in diet\ *) tmp_diet=yes;; # linux-dietlibc with static linking (!diet-dyn) esac fi if $LD --help 2>&1 | $EGREP ': supported targets:.* elf' > /dev/null \ && test no = "$tmp_diet" then tmp_addflag=' $pic_flag' tmp_sharedflag='-shared' case $cc_basename,$host_cpu in pgcc*) # Portland Group C compiler whole_archive_flag_spec='$wl--whole-archive`for conv in $convenience\"\"; do test -n \"$conv\" && new_convenience=\"$new_convenience,$conv\"; done; func_echo_all \"$new_convenience\"` $wl--no-whole-archive' tmp_addflag=' $pic_flag' ;; pgf77* | pgf90* | pgf95* | pgfortran*) # Portland Group f77 and f90 compilers whole_archive_flag_spec='$wl--whole-archive`for conv in $convenience\"\"; do test -n \"$conv\" && new_convenience=\"$new_convenience,$conv\"; done; func_echo_all \"$new_convenience\"` $wl--no-whole-archive' tmp_addflag=' $pic_flag -Mnomain' ;; ecc*,ia64* | icc*,ia64*) # Intel C compiler on ia64 tmp_addflag=' -i_dynamic' ;; efc*,ia64* | ifort*,ia64*) # Intel Fortran compiler on ia64 tmp_addflag=' -i_dynamic -nofor_main' ;; ifc* | ifort*) # Intel Fortran compiler tmp_addflag=' -nofor_main' ;; lf95*) # Lahey Fortran 8.1 whole_archive_flag_spec= tmp_sharedflag='--shared' ;; nagfor*) # NAGFOR 5.3 tmp_sharedflag='-Wl,-shared' ;; xl[cC]* | bgxl[cC]* | mpixl[cC]*) # IBM XL C 8.0 on PPC (deal with xlf below) tmp_sharedflag='-qmkshrobj' tmp_addflag= ;; nvcc*) # Cuda Compiler Driver 2.2 whole_archive_flag_spec='$wl--whole-archive`for conv in $convenience\"\"; do test -n \"$conv\" && new_convenience=\"$new_convenience,$conv\"; done; func_echo_all \"$new_convenience\"` $wl--no-whole-archive' compiler_needs_object=yes ;; esac case `$CC -V 2>&1 | $SED 5q` in *Sun\ C*) # Sun C 5.9 whole_archive_flag_spec='$wl--whole-archive`new_convenience=; for conv in $convenience\"\"; do test -z \"$conv\" || new_convenience=\"$new_convenience,$conv\"; done; func_echo_all \"$new_convenience\"` $wl--no-whole-archive' compiler_needs_object=yes tmp_sharedflag='-G' ;; *Sun\ F*) # Sun Fortran 8.3 tmp_sharedflag='-G' ;; esac archive_cmds='$CC '"$tmp_sharedflag""$tmp_addflag"' $libobjs $deplibs $compiler_flags $wl-soname $wl$soname -o $lib' if test yes = "$supports_anon_versioning"; then archive_expsym_cmds='echo "{ global:" > $output_objdir/$libname.ver~ cat $export_symbols | $SED -e "s/\(.*\)/\1;/" >> $output_objdir/$libname.ver~ echo "local: *; };" >> $output_objdir/$libname.ver~ $CC '"$tmp_sharedflag""$tmp_addflag"' $libobjs $deplibs $compiler_flags $wl-soname $wl$soname $wl-version-script $wl$output_objdir/$libname.ver -o $lib' fi case $cc_basename in tcc*) hardcode_libdir_flag_spec='$wl-rpath $wl$libdir' export_dynamic_flag_spec='-rdynamic' ;; xlf* | bgf* | bgxlf* | mpixlf*) # IBM XL Fortran 10.1 on PPC cannot create shared libs itself whole_archive_flag_spec='--whole-archive$convenience --no-whole-archive' hardcode_libdir_flag_spec='$wl-rpath $wl$libdir' archive_cmds='$LD -shared $libobjs $deplibs $linker_flags -soname $soname -o $lib' if test yes = "$supports_anon_versioning"; then archive_expsym_cmds='echo "{ global:" > $output_objdir/$libname.ver~ cat $export_symbols | $SED -e "s/\(.*\)/\1;/" >> $output_objdir/$libname.ver~ echo "local: *; };" >> $output_objdir/$libname.ver~ $LD -shared $libobjs $deplibs $linker_flags -soname $soname -version-script $output_objdir/$libname.ver -o $lib' fi ;; esac else ld_shlibs=no fi ;; netbsd* | netbsdelf*-gnu) if echo __ELF__ | $CC -E - | $GREP __ELF__ >/dev/null; then archive_cmds='$LD -Bshareable $libobjs $deplibs $linker_flags -o $lib' wlarc= else archive_cmds='$CC -shared $pic_flag $libobjs $deplibs $compiler_flags $wl-soname $wl$soname -o $lib' archive_expsym_cmds='$CC -shared $pic_flag $libobjs $deplibs $compiler_flags $wl-soname $wl$soname $wl-retain-symbols-file $wl$export_symbols -o $lib' fi ;; solaris*) if $LD -v 2>&1 | $GREP 'BFD 2\.8' > /dev/null; then ld_shlibs=no cat <<_LT_EOF 1>&2 *** Warning: The releases 2.8.* of the GNU linker cannot reliably *** create shared libraries on Solaris systems. Therefore, libtool *** is disabling shared libraries support. We urge you to upgrade GNU *** binutils to release 2.9.1 or newer. Another option is to modify *** your PATH or compiler configuration so that the native linker is *** used, and then restart. _LT_EOF elif $LD --help 2>&1 | $GREP ': supported targets:.* elf' > /dev/null; then archive_cmds='$CC -shared $pic_flag $libobjs $deplibs $compiler_flags $wl-soname $wl$soname -o $lib' archive_expsym_cmds='$CC -shared $pic_flag $libobjs $deplibs $compiler_flags $wl-soname $wl$soname $wl-retain-symbols-file $wl$export_symbols -o $lib' else ld_shlibs=no fi ;; sysv5* | sco3.2v5* | sco5v6* | unixware* | OpenUNIX*) case `$LD -v 2>&1` in *\ [01].* | *\ 2.[0-9].* | *\ 2.1[0-5].*) ld_shlibs=no cat <<_LT_EOF 1>&2 *** Warning: Releases of the GNU linker prior to 2.16.91.0.3 cannot *** reliably create shared libraries on SCO systems. Therefore, libtool *** is disabling shared libraries support. We urge you to upgrade GNU *** binutils to release 2.16.91.0.3 or newer. Another option is to modify *** your PATH or compiler configuration so that the native linker is *** used, and then restart. _LT_EOF ;; *) # For security reasons, it is highly recommended that you always # use absolute paths for naming shared libraries, and exclude the # DT_RUNPATH tag from executables and libraries. But doing so # requires that you compile everything twice, which is a pain. if $LD --help 2>&1 | $GREP ': supported targets:.* elf' > /dev/null; then hardcode_libdir_flag_spec='$wl-rpath $wl$libdir' archive_cmds='$CC -shared $libobjs $deplibs $compiler_flags $wl-soname $wl$soname -o $lib' archive_expsym_cmds='$CC -shared $libobjs $deplibs $compiler_flags $wl-soname $wl$soname $wl-retain-symbols-file $wl$export_symbols -o $lib' else ld_shlibs=no fi ;; esac ;; sunos4*) archive_cmds='$LD -assert pure-text -Bshareable -o $lib $libobjs $deplibs $linker_flags' wlarc= hardcode_direct=yes hardcode_shlibpath_var=no ;; *) if $LD --help 2>&1 | $GREP ': supported targets:.* elf' > /dev/null; then archive_cmds='$CC -shared $pic_flag $libobjs $deplibs $compiler_flags $wl-soname $wl$soname -o $lib' archive_expsym_cmds='$CC -shared $pic_flag $libobjs $deplibs $compiler_flags $wl-soname $wl$soname $wl-retain-symbols-file $wl$export_symbols -o $lib' else ld_shlibs=no fi ;; esac if test no = "$ld_shlibs"; then runpath_var= hardcode_libdir_flag_spec= export_dynamic_flag_spec= whole_archive_flag_spec= fi else # PORTME fill in a description of your system's linker (not GNU ld) case $host_os in aix3*) allow_undefined_flag=unsupported always_export_symbols=yes archive_expsym_cmds='$LD -o $output_objdir/$soname $libobjs $deplibs $linker_flags -bE:$export_symbols -T512 -H512 -bM:SRE~$AR $AR_FLAGS $lib $output_objdir/$soname' # Note: this linker hardcodes the directories in LIBPATH if there # are no directories specified by -L. hardcode_minus_L=yes if test yes = "$GCC" && test -z "$lt_prog_compiler_static"; then # Neither direct hardcoding nor static linking is supported with a # broken collect2. hardcode_direct=unsupported fi ;; aix[4-9]*) if test ia64 = "$host_cpu"; then # On IA64, the linker does run time linking by default, so we don't # have to do anything special. aix_use_runtimelinking=no exp_sym_flag='-Bexport' no_entry_flag= else # If we're using GNU nm, then we don't want the "-C" option. # -C means demangle to GNU nm, but means don't demangle to AIX nm. # Without the "-l" option, or with the "-B" option, AIX nm treats # weak defined symbols like other global defined symbols, whereas # GNU nm marks them as "W". # While the 'weak' keyword is ignored in the Export File, we need # it in the Import File for the 'aix-soname' feature, so we have # to replace the "-B" option with "-P" for AIX nm. if $NM -V 2>&1 | $GREP 'GNU' > /dev/null; then export_symbols_cmds='$NM -Bpg $libobjs $convenience | awk '\''{ if (((\$ 2 == "T") || (\$ 2 == "D") || (\$ 2 == "B") || (\$ 2 == "W")) && (substr(\$ 3,1,1) != ".")) { if (\$ 2 == "W") { print \$ 3 " weak" } else { print \$ 3 } } }'\'' | sort -u > $export_symbols' else export_symbols_cmds='`func_echo_all $NM | $SED -e '\''s/B\([^B]*\)$/P\1/'\''` -PCpgl $libobjs $convenience | awk '\''{ if (((\$ 2 == "T") || (\$ 2 == "D") || (\$ 2 == "B") || (\$ 2 == "L") || (\$ 2 == "W") || (\$ 2 == "V") || (\$ 2 == "Z")) && (substr(\$ 1,1,1) != ".")) { if ((\$ 2 == "W") || (\$ 2 == "V") || (\$ 2 == "Z")) { print \$ 1 " weak" } else { print \$ 1 } } }'\'' | sort -u > $export_symbols' fi aix_use_runtimelinking=no # Test if we are trying to use run time linking or normal # AIX style linking. If -brtl is somewhere in LDFLAGS, we # have runtime linking enabled, and use it for executables. # For shared libraries, we enable/disable runtime linking # depending on the kind of the shared library created - # when "with_aix_soname,aix_use_runtimelinking" is: # "aix,no" lib.a(lib.so.V) shared, rtl:no, for executables # "aix,yes" lib.so shared, rtl:yes, for executables # lib.a static archive # "both,no" lib.so.V(shr.o) shared, rtl:yes # lib.a(lib.so.V) shared, rtl:no, for executables # "both,yes" lib.so.V(shr.o) shared, rtl:yes, for executables # lib.a(lib.so.V) shared, rtl:no # "svr4,*" lib.so.V(shr.o) shared, rtl:yes, for executables # lib.a static archive case $host_os in aix4.[23]|aix4.[23].*|aix[5-9]*) for ld_flag in $LDFLAGS; do if (test x-brtl = "x$ld_flag" || test x-Wl,-brtl = "x$ld_flag"); then aix_use_runtimelinking=yes break fi done if test svr4,no = "$with_aix_soname,$aix_use_runtimelinking"; then # With aix-soname=svr4, we create the lib.so.V shared archives only, # so we don't have lib.a shared libs to link our executables. # We have to force runtime linking in this case. aix_use_runtimelinking=yes LDFLAGS="$LDFLAGS -Wl,-brtl" fi ;; esac exp_sym_flag='-bexport' no_entry_flag='-bnoentry' fi # When large executables or shared objects are built, AIX ld can # have problems creating the table of contents. If linking a library # or program results in "error TOC overflow" add -mminimal-toc to # CXXFLAGS/CFLAGS for g++/gcc. In the cases where that is not # enough to fix the problem, add -Wl,-bbigtoc to LDFLAGS. archive_cmds='' hardcode_direct=yes hardcode_direct_absolute=yes hardcode_libdir_separator=':' link_all_deplibs=yes file_list_spec='$wl-f,' case $with_aix_soname,$aix_use_runtimelinking in aix,*) ;; # traditional, no import file svr4,* | *,yes) # use import file # The Import File defines what to hardcode. hardcode_direct=no hardcode_direct_absolute=no ;; esac if test yes = "$GCC"; then case $host_os in aix4.[012]|aix4.[012].*) # We only want to do this on AIX 4.2 and lower, the check # below for broken collect2 doesn't work under 4.3+ collect2name=`$CC -print-prog-name=collect2` if test -f "$collect2name" && strings "$collect2name" | $GREP resolve_lib_name >/dev/null then # We have reworked collect2 : else # We have old collect2 hardcode_direct=unsupported # It fails to find uninstalled libraries when the uninstalled # path is not listed in the libpath. Setting hardcode_minus_L # to unsupported forces relinking hardcode_minus_L=yes hardcode_libdir_flag_spec='-L$libdir' hardcode_libdir_separator= fi ;; esac shared_flag='-shared' if test yes = "$aix_use_runtimelinking"; then shared_flag="$shared_flag "'$wl-G' fi # Need to ensure runtime linking is disabled for the traditional # shared library, or the linker may eventually find shared libraries # /with/ Import File - we do not want to mix them. shared_flag_aix='-shared' shared_flag_svr4='-shared $wl-G' else # not using gcc if test ia64 = "$host_cpu"; then # VisualAge C++, Version 5.5 for AIX 5L for IA-64, Beta 3 Release # chokes on -Wl,-G. The following line is correct: shared_flag='-G' else if test yes = "$aix_use_runtimelinking"; then shared_flag='$wl-G' else shared_flag='$wl-bM:SRE' fi shared_flag_aix='$wl-bM:SRE' shared_flag_svr4='$wl-G' fi fi export_dynamic_flag_spec='$wl-bexpall' # It seems that -bexpall does not export symbols beginning with # underscore (_), so it is better to generate a list of symbols to export. always_export_symbols=yes if test aix,yes = "$with_aix_soname,$aix_use_runtimelinking"; then # Warning - without using the other runtime loading flags (-brtl), # -berok will link without error, but may produce a broken library. allow_undefined_flag='-berok' # Determine the default libpath from the value encoded in an # empty executable. if test set = "${lt_cv_aix_libpath+set}"; then aix_libpath=$lt_cv_aix_libpath else if test ${lt_cv_aix_libpath_+y} then : printf %s "(cached) " >&6 else case e in #( e) cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ int main (void) { ; return 0; } _ACEOF if ac_fn_c_try_link "$LINENO" then : lt_aix_libpath_sed=' /Import File Strings/,/^$/ { /^0/ { s/^0 *\([^ ]*\) *$/\1/ p } }' lt_cv_aix_libpath_=`dump -H conftest$ac_exeext 2>/dev/null | $SED -n -e "$lt_aix_libpath_sed"` # Check for a 64-bit object if we didn't find anything. if test -z "$lt_cv_aix_libpath_"; then lt_cv_aix_libpath_=`dump -HX64 conftest$ac_exeext 2>/dev/null | $SED -n -e "$lt_aix_libpath_sed"` fi fi rm -f core conftest.err conftest.$ac_objext conftest.beam \ conftest$ac_exeext conftest.$ac_ext if test -z "$lt_cv_aix_libpath_"; then lt_cv_aix_libpath_=/usr/lib:/lib fi ;; esac fi aix_libpath=$lt_cv_aix_libpath_ fi hardcode_libdir_flag_spec='$wl-blibpath:$libdir:'"$aix_libpath" archive_expsym_cmds='$CC -o $output_objdir/$soname $libobjs $deplibs $wl'$no_entry_flag' $compiler_flags `if test -n "$allow_undefined_flag"; then func_echo_all "$wl$allow_undefined_flag"; else :; fi` $wl'$exp_sym_flag:\$export_symbols' '$shared_flag else if test ia64 = "$host_cpu"; then hardcode_libdir_flag_spec='$wl-R $libdir:/usr/lib:/lib' allow_undefined_flag="-z nodefs" archive_expsym_cmds="\$CC $shared_flag"' -o $output_objdir/$soname $libobjs $deplibs '"\$wl$no_entry_flag"' $compiler_flags $wl$allow_undefined_flag '"\$wl$exp_sym_flag:\$export_symbols" else # Determine the default libpath from the value encoded in an # empty executable. if test set = "${lt_cv_aix_libpath+set}"; then aix_libpath=$lt_cv_aix_libpath else if test ${lt_cv_aix_libpath_+y} then : printf %s "(cached) " >&6 else case e in #( e) cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ int main (void) { ; return 0; } _ACEOF if ac_fn_c_try_link "$LINENO" then : lt_aix_libpath_sed=' /Import File Strings/,/^$/ { /^0/ { s/^0 *\([^ ]*\) *$/\1/ p } }' lt_cv_aix_libpath_=`dump -H conftest$ac_exeext 2>/dev/null | $SED -n -e "$lt_aix_libpath_sed"` # Check for a 64-bit object if we didn't find anything. if test -z "$lt_cv_aix_libpath_"; then lt_cv_aix_libpath_=`dump -HX64 conftest$ac_exeext 2>/dev/null | $SED -n -e "$lt_aix_libpath_sed"` fi fi rm -f core conftest.err conftest.$ac_objext conftest.beam \ conftest$ac_exeext conftest.$ac_ext if test -z "$lt_cv_aix_libpath_"; then lt_cv_aix_libpath_=/usr/lib:/lib fi ;; esac fi aix_libpath=$lt_cv_aix_libpath_ fi hardcode_libdir_flag_spec='$wl-blibpath:$libdir:'"$aix_libpath" # Warning - without using the other run time loading flags, # -berok will link without error, but may produce a broken library. no_undefined_flag=' $wl-bernotok' allow_undefined_flag=' $wl-berok' if test yes = "$with_gnu_ld"; then # We only use this code for GNU lds that support --whole-archive. whole_archive_flag_spec='$wl--whole-archive$convenience $wl--no-whole-archive' else # Exported symbols can be pulled into shared objects from archives whole_archive_flag_spec='$convenience' fi archive_cmds_need_lc=yes archive_expsym_cmds='$RM -r $output_objdir/$realname.d~$MKDIR $output_objdir/$realname.d' # -brtl affects multiple linker settings, -berok does not and is overridden later compiler_flags_filtered='`func_echo_all "$compiler_flags " | $SED -e "s%-brtl\\([, ]\\)%-berok\\1%g"`' if test svr4 != "$with_aix_soname"; then # This is similar to how AIX traditionally builds its shared libraries. archive_expsym_cmds="$archive_expsym_cmds"'~$CC '$shared_flag_aix' -o $output_objdir/$realname.d/$soname $libobjs $deplibs $wl-bnoentry '$compiler_flags_filtered'$wl-bE:$export_symbols$allow_undefined_flag~$AR $AR_FLAGS $output_objdir/$libname$release.a $output_objdir/$realname.d/$soname' fi if test aix != "$with_aix_soname"; then archive_expsym_cmds="$archive_expsym_cmds"'~$CC '$shared_flag_svr4' -o $output_objdir/$realname.d/$shared_archive_member_spec.o $libobjs $deplibs $wl-bnoentry '$compiler_flags_filtered'$wl-bE:$export_symbols$allow_undefined_flag~$STRIP -e $output_objdir/$realname.d/$shared_archive_member_spec.o~( func_echo_all "#! $soname($shared_archive_member_spec.o)"; if test shr_64 = "$shared_archive_member_spec"; then func_echo_all "# 64"; else func_echo_all "# 32"; fi; cat $export_symbols ) > $output_objdir/$realname.d/$shared_archive_member_spec.imp~$AR $AR_FLAGS $output_objdir/$soname $output_objdir/$realname.d/$shared_archive_member_spec.o $output_objdir/$realname.d/$shared_archive_member_spec.imp' else # used by -dlpreopen to get the symbols archive_expsym_cmds="$archive_expsym_cmds"'~$MV $output_objdir/$realname.d/$soname $output_objdir' fi archive_expsym_cmds="$archive_expsym_cmds"'~$RM -r $output_objdir/$realname.d' fi fi ;; amigaos*) case $host_cpu in powerpc) # see comment about AmigaOS4 .so support archive_cmds='$CC -shared $libobjs $deplibs $compiler_flags $wl-soname $wl$soname -o $lib' archive_expsym_cmds='' ;; m68k) archive_cmds='$RM $output_objdir/a2ixlibrary.data~$ECHO "#define NAME $libname" > $output_objdir/a2ixlibrary.data~$ECHO "#define LIBRARY_ID 1" >> $output_objdir/a2ixlibrary.data~$ECHO "#define VERSION $major" >> $output_objdir/a2ixlibrary.data~$ECHO "#define REVISION $revision" >> $output_objdir/a2ixlibrary.data~$AR $AR_FLAGS $lib $libobjs~$RANLIB $lib~(cd $output_objdir && a2ixlibrary -32)' hardcode_libdir_flag_spec='-L$libdir' hardcode_minus_L=yes ;; esac ;; bsdi[45]*) export_dynamic_flag_spec=-rdynamic ;; cygwin* | mingw* | pw32* | cegcc*) # When not using gcc, we currently assume that we are using # Microsoft Visual C++ or Intel C++ Compiler. # hardcode_libdir_flag_spec is actually meaningless, as there is # no search path for DLLs. case $cc_basename in cl* | icl*) # Native MSVC or ICC hardcode_libdir_flag_spec=' ' allow_undefined_flag=unsupported always_export_symbols=yes file_list_spec='@' # Tell ltmain to make .lib files, not .a files. libext=lib # Tell ltmain to make .dll files, not .so files. shrext_cmds=.dll # FIXME: Setting linknames here is a bad hack. archive_cmds='$CC -o $output_objdir/$soname $libobjs $compiler_flags $deplibs -Wl,-DLL,-IMPLIB:"$tool_output_objdir$libname.dll.lib"~linknames=' archive_expsym_cmds='if test DEF = "`$SED -n -e '\''s/^[ ]*//'\'' -e '\''/^\(;.*\)*$/d'\'' -e '\''s/^\(EXPORTS\|LIBRARY\)\([ ].*\)*$/DEF/p'\'' -e q $export_symbols`" ; then cp "$export_symbols" "$output_objdir/$soname.def"; echo "$tool_output_objdir$soname.def" > "$output_objdir/$soname.exp"; else $SED -e '\''s/^/-link -EXPORT:/'\'' < $export_symbols > $output_objdir/$soname.exp; fi~ $CC -o $tool_output_objdir$soname $libobjs $compiler_flags $deplibs "@$tool_output_objdir$soname.exp" -Wl,-DLL,-IMPLIB:"$tool_output_objdir$libname.dll.lib"~ linknames=' # The linker will not automatically build a static lib if we build a DLL. # _LT_TAGVAR(old_archive_from_new_cmds, )='true' enable_shared_with_static_runtimes=yes exclude_expsyms='_NULL_IMPORT_DESCRIPTOR|_IMPORT_DESCRIPTOR_.*' export_symbols_cmds='$NM $libobjs $convenience | $global_symbol_pipe | $SED -e '\''/^[BCDGRS][ ]/s/.*[ ]\([^ ]*\)/\1,DATA/'\'' | $SED -e '\''/^[AITW][ ]/s/.*[ ]//'\'' | sort | uniq > $export_symbols' # Don't use ranlib old_postinstall_cmds='chmod 644 $oldlib' postlink_cmds='lt_outputfile="@OUTPUT@"~ lt_tool_outputfile="@TOOL_OUTPUT@"~ case $lt_outputfile in *.exe|*.EXE) ;; *) lt_outputfile=$lt_outputfile.exe lt_tool_outputfile=$lt_tool_outputfile.exe ;; esac~ if test : != "$MANIFEST_TOOL" && test -f "$lt_outputfile.manifest"; then $MANIFEST_TOOL -manifest "$lt_tool_outputfile.manifest" -outputresource:"$lt_tool_outputfile" || exit 1; $RM "$lt_outputfile.manifest"; fi' ;; *) # Assume MSVC and ICC wrapper hardcode_libdir_flag_spec=' ' allow_undefined_flag=unsupported # Tell ltmain to make .lib files, not .a files. libext=lib # Tell ltmain to make .dll files, not .so files. shrext_cmds=.dll # FIXME: Setting linknames here is a bad hack. archive_cmds='$CC -o $lib $libobjs $compiler_flags `func_echo_all "$deplibs" | $SED '\''s/ -lc$//'\''` -link -dll~linknames=' # The linker will automatically build a .lib file if we build a DLL. old_archive_from_new_cmds='true' # FIXME: Should let the user specify the lib program. old_archive_cmds='lib -OUT:$oldlib$oldobjs$old_deplibs' enable_shared_with_static_runtimes=yes ;; esac ;; darwin* | rhapsody*) archive_cmds_need_lc=no hardcode_direct=no hardcode_automatic=yes hardcode_shlibpath_var=unsupported if test yes = "$lt_cv_ld_force_load"; then whole_archive_flag_spec='`for conv in $convenience\"\"; do test -n \"$conv\" && new_convenience=\"$new_convenience $wl-force_load,$conv\"; done; func_echo_all \"$new_convenience\"`' else whole_archive_flag_spec='' fi link_all_deplibs=yes allow_undefined_flag=$_lt_dar_allow_undefined case $cc_basename in ifort*|nagfor*) _lt_dar_can_shared=yes ;; *) _lt_dar_can_shared=$GCC ;; esac if test yes = "$_lt_dar_can_shared"; then output_verbose_link_cmd=func_echo_all archive_cmds="\$CC -dynamiclib \$allow_undefined_flag -o \$lib \$libobjs \$deplibs \$compiler_flags -install_name \$rpath/\$soname \$verstring $_lt_dar_single_mod$_lt_dsymutil" module_cmds="\$CC \$allow_undefined_flag -o \$lib -bundle \$libobjs \$deplibs \$compiler_flags$_lt_dsymutil" archive_expsym_cmds="$SED 's|^|_|' < \$export_symbols > \$output_objdir/\$libname-symbols.expsym~\$CC -dynamiclib \$allow_undefined_flag -o \$lib \$libobjs \$deplibs \$compiler_flags -install_name \$rpath/\$soname \$verstring $_lt_dar_single_mod$_lt_dar_export_syms$_lt_dsymutil" module_expsym_cmds="$SED -e 's|^|_|' < \$export_symbols > \$output_objdir/\$libname-symbols.expsym~\$CC \$allow_undefined_flag -o \$lib -bundle \$libobjs \$deplibs \$compiler_flags$_lt_dar_export_syms$_lt_dsymutil" else ld_shlibs=no fi ;; dgux*) archive_cmds='$LD -G -h $soname -o $lib $libobjs $deplibs $linker_flags' hardcode_libdir_flag_spec='-L$libdir' hardcode_shlibpath_var=no ;; # FreeBSD 2.2.[012] allows us to include c++rt0.o to get C++ constructor # support. Future versions do this automatically, but an explicit c++rt0.o # does not break anything, and helps significantly (at the cost of a little # extra space). freebsd2.2*) archive_cmds='$LD -Bshareable -o $lib $libobjs $deplibs $linker_flags /usr/lib/c++rt0.o' hardcode_libdir_flag_spec='-R$libdir' hardcode_direct=yes hardcode_shlibpath_var=no ;; # Unfortunately, older versions of FreeBSD 2 do not have this feature. freebsd2.*) archive_cmds='$LD -Bshareable -o $lib $libobjs $deplibs $linker_flags' hardcode_direct=yes hardcode_minus_L=yes hardcode_shlibpath_var=no ;; # FreeBSD 3 and greater uses gcc -shared to do shared libraries. freebsd* | dragonfly* | midnightbsd*) archive_cmds='$CC -shared $pic_flag -o $lib $libobjs $deplibs $compiler_flags' hardcode_libdir_flag_spec='-R$libdir' hardcode_direct=yes hardcode_shlibpath_var=no ;; hpux9*) if test yes = "$GCC"; then archive_cmds='$RM $output_objdir/$soname~$CC -shared $pic_flag $wl+b $wl$install_libdir -o $output_objdir/$soname $libobjs $deplibs $compiler_flags~test "x$output_objdir/$soname" = "x$lib" || mv $output_objdir/$soname $lib' else archive_cmds='$RM $output_objdir/$soname~$LD -b +b $install_libdir -o $output_objdir/$soname $libobjs $deplibs $linker_flags~test "x$output_objdir/$soname" = "x$lib" || mv $output_objdir/$soname $lib' fi hardcode_libdir_flag_spec='$wl+b $wl$libdir' hardcode_libdir_separator=: hardcode_direct=yes # hardcode_minus_L: Not really in the search PATH, # but as the default location of the library. hardcode_minus_L=yes export_dynamic_flag_spec='$wl-E' ;; hpux10*) if test yes,no = "$GCC,$with_gnu_ld"; then archive_cmds='$CC -shared $pic_flag $wl+h $wl$soname $wl+b $wl$install_libdir -o $lib $libobjs $deplibs $compiler_flags' else archive_cmds='$LD -b +h $soname +b $install_libdir -o $lib $libobjs $deplibs $linker_flags' fi if test no = "$with_gnu_ld"; then hardcode_libdir_flag_spec='$wl+b $wl$libdir' hardcode_libdir_separator=: hardcode_direct=yes hardcode_direct_absolute=yes export_dynamic_flag_spec='$wl-E' # hardcode_minus_L: Not really in the search PATH, # but as the default location of the library. hardcode_minus_L=yes fi ;; hpux11*) if test yes,no = "$GCC,$with_gnu_ld"; then case $host_cpu in hppa*64*) archive_cmds='$CC -shared $wl+h $wl$soname -o $lib $libobjs $deplibs $compiler_flags' ;; ia64*) archive_cmds='$CC -shared $pic_flag $wl+h $wl$soname $wl+nodefaultrpath -o $lib $libobjs $deplibs $compiler_flags' ;; *) archive_cmds='$CC -shared $pic_flag $wl+h $wl$soname $wl+b $wl$install_libdir -o $lib $libobjs $deplibs $compiler_flags' ;; esac else case $host_cpu in hppa*64*) archive_cmds='$CC -b $wl+h $wl$soname -o $lib $libobjs $deplibs $compiler_flags' ;; ia64*) archive_cmds='$CC -b $wl+h $wl$soname $wl+nodefaultrpath -o $lib $libobjs $deplibs $compiler_flags' ;; *) # Older versions of the 11.00 compiler do not understand -b yet # (HP92453-01 A.11.01.20 doesn't, HP92453-01 B.11.X.35175-35176.GP does) { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking if $CC understands -b" >&5 printf %s "checking if $CC understands -b... " >&6; } if test ${lt_cv_prog_compiler__b+y} then : printf %s "(cached) " >&6 else case e in #( e) lt_cv_prog_compiler__b=no save_LDFLAGS=$LDFLAGS LDFLAGS="$LDFLAGS -b" echo "$lt_simple_link_test_code" > conftest.$ac_ext if (eval $ac_link 2>conftest.err) && test -s conftest$ac_exeext; then # The linker can only warn and ignore the option if not recognized # So say no if there are warnings if test -s conftest.err; then # Append any errors to the config.log. cat conftest.err 1>&5 $ECHO "$_lt_linker_boilerplate" | $SED '/^$/d' > conftest.exp $SED '/^$/d; /^ *+/d' conftest.err >conftest.er2 if diff conftest.exp conftest.er2 >/dev/null; then lt_cv_prog_compiler__b=yes fi else lt_cv_prog_compiler__b=yes fi fi $RM -r conftest* LDFLAGS=$save_LDFLAGS ;; esac fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $lt_cv_prog_compiler__b" >&5 printf "%s\n" "$lt_cv_prog_compiler__b" >&6; } if test yes = "$lt_cv_prog_compiler__b"; then archive_cmds='$CC -b $wl+h $wl$soname $wl+b $wl$install_libdir -o $lib $libobjs $deplibs $compiler_flags' else archive_cmds='$LD -b +h $soname +b $install_libdir -o $lib $libobjs $deplibs $linker_flags' fi ;; esac fi if test no = "$with_gnu_ld"; then hardcode_libdir_flag_spec='$wl+b $wl$libdir' hardcode_libdir_separator=: case $host_cpu in hppa*64*|ia64*) hardcode_direct=no hardcode_shlibpath_var=no ;; *) hardcode_direct=yes hardcode_direct_absolute=yes export_dynamic_flag_spec='$wl-E' # hardcode_minus_L: Not really in the search PATH, # but as the default location of the library. hardcode_minus_L=yes ;; esac fi ;; irix5* | irix6* | nonstopux*) if test yes = "$GCC"; then archive_cmds='$CC -shared $pic_flag $libobjs $deplibs $compiler_flags $wl-soname $wl$soname `test -n "$verstring" && func_echo_all "$wl-set_version $wl$verstring"` $wl-update_registry $wl$output_objdir/so_locations -o $lib' # Try to use the -exported_symbol ld option, if it does not # work, assume that -exports_file does not work either and # implicitly export all symbols. # This should be the same for all languages, so no per-tag cache variable. { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking whether the $host_os linker accepts -exported_symbol" >&5 printf %s "checking whether the $host_os linker accepts -exported_symbol... " >&6; } if test ${lt_cv_irix_exported_symbol+y} then : printf %s "(cached) " >&6 else case e in #( e) save_LDFLAGS=$LDFLAGS LDFLAGS="$LDFLAGS -shared $wl-exported_symbol ${wl}foo $wl-update_registry $wl/dev/null" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ int foo (void) { return 0; } _ACEOF if ac_fn_c_try_link "$LINENO" then : lt_cv_irix_exported_symbol=yes else case e in #( e) lt_cv_irix_exported_symbol=no ;; esac fi rm -f core conftest.err conftest.$ac_objext conftest.beam \ conftest$ac_exeext conftest.$ac_ext LDFLAGS=$save_LDFLAGS ;; esac fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $lt_cv_irix_exported_symbol" >&5 printf "%s\n" "$lt_cv_irix_exported_symbol" >&6; } if test yes = "$lt_cv_irix_exported_symbol"; then archive_expsym_cmds='$CC -shared $pic_flag $libobjs $deplibs $compiler_flags $wl-soname $wl$soname `test -n "$verstring" && func_echo_all "$wl-set_version $wl$verstring"` $wl-update_registry $wl$output_objdir/so_locations $wl-exports_file $wl$export_symbols -o $lib' fi link_all_deplibs=no else archive_cmds='$CC -shared $libobjs $deplibs $compiler_flags -soname $soname `test -n "$verstring" && func_echo_all "-set_version $verstring"` -update_registry $output_objdir/so_locations -o $lib' archive_expsym_cmds='$CC -shared $libobjs $deplibs $compiler_flags -soname $soname `test -n "$verstring" && func_echo_all "-set_version $verstring"` -update_registry $output_objdir/so_locations -exports_file $export_symbols -o $lib' fi archive_cmds_need_lc='no' hardcode_libdir_flag_spec='$wl-rpath $wl$libdir' hardcode_libdir_separator=: inherit_rpath=yes link_all_deplibs=yes ;; linux*) case $cc_basename in tcc*) # Fabrice Bellard et al's Tiny C Compiler ld_shlibs=yes archive_cmds='$CC -shared $pic_flag -o $lib $libobjs $deplibs $compiler_flags' hardcode_libdir_flag_spec='$wl-rpath $wl$libdir' ;; esac ;; netbsd* | netbsdelf*-gnu) if echo __ELF__ | $CC -E - | $GREP __ELF__ >/dev/null; then archive_cmds='$LD -Bshareable -o $lib $libobjs $deplibs $linker_flags' # a.out else archive_cmds='$LD -shared -o $lib $libobjs $deplibs $linker_flags' # ELF fi hardcode_libdir_flag_spec='-R$libdir' hardcode_direct=yes hardcode_shlibpath_var=no ;; newsos6) archive_cmds='$LD -G -h $soname -o $lib $libobjs $deplibs $linker_flags' hardcode_direct=yes hardcode_libdir_flag_spec='$wl-rpath $wl$libdir' hardcode_libdir_separator=: hardcode_shlibpath_var=no ;; *nto* | *qnx*) ;; openbsd* | bitrig*) if test -f /usr/libexec/ld.so; then hardcode_direct=yes hardcode_shlibpath_var=no hardcode_direct_absolute=yes if test -z "`echo __ELF__ | $CC -E - | $GREP __ELF__`"; then archive_cmds='$CC -shared $pic_flag -o $lib $libobjs $deplibs $compiler_flags' archive_expsym_cmds='$CC -shared $pic_flag -o $lib $libobjs $deplibs $compiler_flags $wl-retain-symbols-file,$export_symbols' hardcode_libdir_flag_spec='$wl-rpath,$libdir' export_dynamic_flag_spec='$wl-E' else archive_cmds='$CC -shared $pic_flag -o $lib $libobjs $deplibs $compiler_flags' hardcode_libdir_flag_spec='$wl-rpath,$libdir' fi else ld_shlibs=no fi ;; os2*) hardcode_libdir_flag_spec='-L$libdir' hardcode_minus_L=yes allow_undefined_flag=unsupported shrext_cmds=.dll archive_cmds='$ECHO "LIBRARY ${soname%$shared_ext} INITINSTANCE TERMINSTANCE" > $output_objdir/$libname.def~ $ECHO "DESCRIPTION \"$libname\"" >> $output_objdir/$libname.def~ $ECHO "DATA MULTIPLE NONSHARED" >> $output_objdir/$libname.def~ $ECHO EXPORTS >> $output_objdir/$libname.def~ emxexp $libobjs | $SED /"_DLL_InitTerm"/d >> $output_objdir/$libname.def~ $CC -Zdll -Zcrtdll -o $output_objdir/$soname $libobjs $deplibs $compiler_flags $output_objdir/$libname.def~ emximp -o $lib $output_objdir/$libname.def' archive_expsym_cmds='$ECHO "LIBRARY ${soname%$shared_ext} INITINSTANCE TERMINSTANCE" > $output_objdir/$libname.def~ $ECHO "DESCRIPTION \"$libname\"" >> $output_objdir/$libname.def~ $ECHO "DATA MULTIPLE NONSHARED" >> $output_objdir/$libname.def~ $ECHO EXPORTS >> $output_objdir/$libname.def~ prefix_cmds="$SED"~ if test EXPORTS = "`$SED 1q $export_symbols`"; then prefix_cmds="$prefix_cmds -e 1d"; fi~ prefix_cmds="$prefix_cmds -e \"s/^\(.*\)$/_\1/g\""~ cat $export_symbols | $prefix_cmds >> $output_objdir/$libname.def~ $CC -Zdll -Zcrtdll -o $output_objdir/$soname $libobjs $deplibs $compiler_flags $output_objdir/$libname.def~ emximp -o $lib $output_objdir/$libname.def' old_archive_From_new_cmds='emximp -o $output_objdir/${libname}_dll.a $output_objdir/$libname.def' enable_shared_with_static_runtimes=yes file_list_spec='@' ;; osf3*) if test yes = "$GCC"; then allow_undefined_flag=' $wl-expect_unresolved $wl\*' archive_cmds='$CC -shared$allow_undefined_flag $libobjs $deplibs $compiler_flags $wl-soname $wl$soname `test -n "$verstring" && func_echo_all "$wl-set_version $wl$verstring"` $wl-update_registry $wl$output_objdir/so_locations -o $lib' else allow_undefined_flag=' -expect_unresolved \*' archive_cmds='$CC -shared$allow_undefined_flag $libobjs $deplibs $compiler_flags -soname $soname `test -n "$verstring" && func_echo_all "-set_version $verstring"` -update_registry $output_objdir/so_locations -o $lib' fi archive_cmds_need_lc='no' hardcode_libdir_flag_spec='$wl-rpath $wl$libdir' hardcode_libdir_separator=: ;; osf4* | osf5*) # as osf3* with the addition of -msym flag if test yes = "$GCC"; then allow_undefined_flag=' $wl-expect_unresolved $wl\*' archive_cmds='$CC -shared$allow_undefined_flag $pic_flag $libobjs $deplibs $compiler_flags $wl-msym $wl-soname $wl$soname `test -n "$verstring" && func_echo_all "$wl-set_version $wl$verstring"` $wl-update_registry $wl$output_objdir/so_locations -o $lib' hardcode_libdir_flag_spec='$wl-rpath $wl$libdir' else allow_undefined_flag=' -expect_unresolved \*' archive_cmds='$CC -shared$allow_undefined_flag $libobjs $deplibs $compiler_flags -msym -soname $soname `test -n "$verstring" && func_echo_all "-set_version $verstring"` -update_registry $output_objdir/so_locations -o $lib' archive_expsym_cmds='for i in `cat $export_symbols`; do printf "%s %s\\n" -exported_symbol "\$i" >> $lib.exp; done; printf "%s\\n" "-hidden">> $lib.exp~ $CC -shared$allow_undefined_flag $wl-input $wl$lib.exp $compiler_flags $libobjs $deplibs -soname $soname `test -n "$verstring" && $ECHO "-set_version $verstring"` -update_registry $output_objdir/so_locations -o $lib~$RM $lib.exp' # Both c and cxx compiler support -rpath directly hardcode_libdir_flag_spec='-rpath $libdir' fi archive_cmds_need_lc='no' hardcode_libdir_separator=: ;; solaris*) no_undefined_flag=' -z defs' if test yes = "$GCC"; then wlarc='$wl' archive_cmds='$CC -shared $pic_flag $wl-z ${wl}text $wl-h $wl$soname -o $lib $libobjs $deplibs $compiler_flags' archive_expsym_cmds='echo "{ global:" > $lib.exp~cat $export_symbols | $SED -e "s/\(.*\)/\1;/" >> $lib.exp~echo "local: *; };" >> $lib.exp~ $CC -shared $pic_flag $wl-z ${wl}text $wl-M $wl$lib.exp $wl-h $wl$soname -o $lib $libobjs $deplibs $compiler_flags~$RM $lib.exp' else case `$CC -V 2>&1` in *"Compilers 5.0"*) wlarc='' archive_cmds='$LD -G$allow_undefined_flag -h $soname -o $lib $libobjs $deplibs $linker_flags' archive_expsym_cmds='echo "{ global:" > $lib.exp~cat $export_symbols | $SED -e "s/\(.*\)/\1;/" >> $lib.exp~echo "local: *; };" >> $lib.exp~ $LD -G$allow_undefined_flag -M $lib.exp -h $soname -o $lib $libobjs $deplibs $linker_flags~$RM $lib.exp' ;; *) wlarc='$wl' archive_cmds='$CC -G$allow_undefined_flag -h $soname -o $lib $libobjs $deplibs $compiler_flags' archive_expsym_cmds='echo "{ global:" > $lib.exp~cat $export_symbols | $SED -e "s/\(.*\)/\1;/" >> $lib.exp~echo "local: *; };" >> $lib.exp~ $CC -G$allow_undefined_flag -M $lib.exp -h $soname -o $lib $libobjs $deplibs $compiler_flags~$RM $lib.exp' ;; esac fi hardcode_libdir_flag_spec='-R$libdir' hardcode_shlibpath_var=no case $host_os in solaris2.[0-5] | solaris2.[0-5].*) ;; *) # The compiler driver will combine and reorder linker options, # but understands '-z linker_flag'. GCC discards it without '$wl', # but is careful enough not to reorder. # Supported since Solaris 2.6 (maybe 2.5.1?) if test yes = "$GCC"; then whole_archive_flag_spec='$wl-z ${wl}allextract$convenience $wl-z ${wl}defaultextract' else whole_archive_flag_spec='-z allextract$convenience -z defaultextract' fi ;; esac link_all_deplibs=yes ;; sunos4*) if test sequent = "$host_vendor"; then # Use $CC to link under sequent, because it throws in some extra .o # files that make .init and .fini sections work. archive_cmds='$CC -G $wl-h $soname -o $lib $libobjs $deplibs $compiler_flags' else archive_cmds='$LD -assert pure-text -Bstatic -o $lib $libobjs $deplibs $linker_flags' fi hardcode_libdir_flag_spec='-L$libdir' hardcode_direct=yes hardcode_minus_L=yes hardcode_shlibpath_var=no ;; sysv4) case $host_vendor in sni) archive_cmds='$LD -G -h $soname -o $lib $libobjs $deplibs $linker_flags' hardcode_direct=yes # is this really true??? ;; siemens) ## LD is ld it makes a PLAMLIB ## CC just makes a GrossModule. archive_cmds='$LD -G -o $lib $libobjs $deplibs $linker_flags' reload_cmds='$CC -r -o $output$reload_objs' hardcode_direct=no ;; motorola) archive_cmds='$LD -G -h $soname -o $lib $libobjs $deplibs $linker_flags' hardcode_direct=no #Motorola manual says yes, but my tests say they lie ;; esac runpath_var='LD_RUN_PATH' hardcode_shlibpath_var=no ;; sysv4.3*) archive_cmds='$LD -G -h $soname -o $lib $libobjs $deplibs $linker_flags' hardcode_shlibpath_var=no export_dynamic_flag_spec='-Bexport' ;; sysv4*MP*) if test -d /usr/nec; then archive_cmds='$LD -G -h $soname -o $lib $libobjs $deplibs $linker_flags' hardcode_shlibpath_var=no runpath_var=LD_RUN_PATH hardcode_runpath_var=yes ld_shlibs=yes fi ;; sysv4*uw2* | sysv5OpenUNIX* | sysv5UnixWare7.[01].[10]* | unixware7* | sco3.2v5.0.[024]*) no_undefined_flag='$wl-z,text' archive_cmds_need_lc=no hardcode_shlibpath_var=no runpath_var='LD_RUN_PATH' if test yes = "$GCC"; then archive_cmds='$CC -shared $wl-h,$soname -o $lib $libobjs $deplibs $compiler_flags' archive_expsym_cmds='$CC -shared $wl-Bexport:$export_symbols $wl-h,$soname -o $lib $libobjs $deplibs $compiler_flags' else archive_cmds='$CC -G $wl-h,$soname -o $lib $libobjs $deplibs $compiler_flags' archive_expsym_cmds='$CC -G $wl-Bexport:$export_symbols $wl-h,$soname -o $lib $libobjs $deplibs $compiler_flags' fi ;; sysv5* | sco3.2v5* | sco5v6*) # Note: We CANNOT use -z defs as we might desire, because we do not # link with -lc, and that would cause any symbols used from libc to # always be unresolved, which means just about no library would # ever link correctly. If we're not using GNU ld we use -z text # though, which does catch some bad symbols but isn't as heavy-handed # as -z defs. no_undefined_flag='$wl-z,text' allow_undefined_flag='$wl-z,nodefs' archive_cmds_need_lc=no hardcode_shlibpath_var=no hardcode_libdir_flag_spec='$wl-R,$libdir' hardcode_libdir_separator=':' link_all_deplibs=yes export_dynamic_flag_spec='$wl-Bexport' runpath_var='LD_RUN_PATH' if test yes = "$GCC"; then archive_cmds='$CC -shared $wl-h,$soname -o $lib $libobjs $deplibs $compiler_flags' archive_expsym_cmds='$CC -shared $wl-Bexport:$export_symbols $wl-h,$soname -o $lib $libobjs $deplibs $compiler_flags' else archive_cmds='$CC -G $wl-h,$soname -o $lib $libobjs $deplibs $compiler_flags' archive_expsym_cmds='$CC -G $wl-Bexport:$export_symbols $wl-h,$soname -o $lib $libobjs $deplibs $compiler_flags' fi ;; uts4*) archive_cmds='$LD -G -h $soname -o $lib $libobjs $deplibs $linker_flags' hardcode_libdir_flag_spec='-L$libdir' hardcode_shlibpath_var=no ;; *) ld_shlibs=no ;; esac if test sni = "$host_vendor"; then case $host in sysv4 | sysv4.2uw2* | sysv4.3* | sysv5*) export_dynamic_flag_spec='$wl-Blargedynsym' ;; esac fi fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ld_shlibs" >&5 printf "%s\n" "$ld_shlibs" >&6; } test no = "$ld_shlibs" && can_build_shared=no with_gnu_ld=$with_gnu_ld # # Do we need to explicitly link libc? # case "x$archive_cmds_need_lc" in x|xyes) # Assume -lc should be added archive_cmds_need_lc=yes if test yes,yes = "$GCC,$enable_shared"; then case $archive_cmds in *'~'*) # FIXME: we may have to deal with multi-command sequences. ;; '$CC '*) # Test whether the compiler implicitly links with -lc since on some # systems, -lgcc has to come before -lc. If gcc already passes -lc # to ld, don't add -lc before -lgcc. { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking whether -lc should be explicitly linked in" >&5 printf %s "checking whether -lc should be explicitly linked in... " >&6; } if test ${lt_cv_archive_cmds_need_lc+y} then : printf %s "(cached) " >&6 else case e in #( e) $RM conftest* echo "$lt_simple_compile_test_code" > conftest.$ac_ext if { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$ac_compile\""; } >&5 (eval $ac_compile) 2>&5 ac_status=$? printf "%s\n" "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; } 2>conftest.err; then soname=conftest lib=conftest libobjs=conftest.$ac_objext deplibs= wl=$lt_prog_compiler_wl pic_flag=$lt_prog_compiler_pic compiler_flags=-v linker_flags=-v verstring= output_objdir=. libname=conftest lt_save_allow_undefined_flag=$allow_undefined_flag allow_undefined_flag= if { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$archive_cmds 2\>\&1 \| $GREP \" -lc \" \>/dev/null 2\>\&1\""; } >&5 (eval $archive_cmds 2\>\&1 \| $GREP \" -lc \" \>/dev/null 2\>\&1) 2>&5 ac_status=$? printf "%s\n" "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; } then lt_cv_archive_cmds_need_lc=no else lt_cv_archive_cmds_need_lc=yes fi allow_undefined_flag=$lt_save_allow_undefined_flag else cat conftest.err 1>&5 fi $RM conftest* ;; esac fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $lt_cv_archive_cmds_need_lc" >&5 printf "%s\n" "$lt_cv_archive_cmds_need_lc" >&6; } archive_cmds_need_lc=$lt_cv_archive_cmds_need_lc ;; esac fi ;; esac { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking dynamic linker characteristics" >&5 printf %s "checking dynamic linker characteristics... " >&6; } if test yes = "$GCC"; then case $host_os in darwin*) lt_awk_arg='/^libraries:/,/LR/' ;; *) lt_awk_arg='/^libraries:/' ;; esac case $host_os in mingw* | cegcc*) lt_sed_strip_eq='s|=\([A-Za-z]:\)|\1|g' ;; *) lt_sed_strip_eq='s|=/|/|g' ;; esac lt_search_path_spec=`$CC -print-search-dirs | awk $lt_awk_arg | $SED -e "s/^libraries://" -e $lt_sed_strip_eq` case $lt_search_path_spec in *\;*) # if the path contains ";" then we assume it to be the separator # otherwise default to the standard path separator (i.e. ":") - it is # assumed that no part of a normal pathname contains ";" but that should # okay in the real world where ";" in dirpaths is itself problematic. lt_search_path_spec=`$ECHO "$lt_search_path_spec" | $SED 's/;/ /g'` ;; *) lt_search_path_spec=`$ECHO "$lt_search_path_spec" | $SED "s/$PATH_SEPARATOR/ /g"` ;; esac # Ok, now we have the path, separated by spaces, we can step through it # and add multilib dir if necessary... lt_tmp_lt_search_path_spec= lt_multi_os_dir=/`$CC $CPPFLAGS $CFLAGS $LDFLAGS -print-multi-os-directory 2>/dev/null` # ...but if some path component already ends with the multilib dir we assume # that all is fine and trust -print-search-dirs as is (GCC 4.2? or newer). case "$lt_multi_os_dir; $lt_search_path_spec " in "/; "* | "/.; "* | "/./; "* | *"$lt_multi_os_dir "* | *"$lt_multi_os_dir/ "*) lt_multi_os_dir= ;; esac for lt_sys_path in $lt_search_path_spec; do if test -d "$lt_sys_path$lt_multi_os_dir"; then lt_tmp_lt_search_path_spec="$lt_tmp_lt_search_path_spec $lt_sys_path$lt_multi_os_dir" elif test -n "$lt_multi_os_dir"; then test -d "$lt_sys_path" && \ lt_tmp_lt_search_path_spec="$lt_tmp_lt_search_path_spec $lt_sys_path" fi done lt_search_path_spec=`$ECHO "$lt_tmp_lt_search_path_spec" | awk ' BEGIN {RS = " "; FS = "/|\n";} { lt_foo = ""; lt_count = 0; for (lt_i = NF; lt_i > 0; lt_i--) { if ($lt_i != "" && $lt_i != ".") { if ($lt_i == "..") { lt_count++; } else { if (lt_count == 0) { lt_foo = "/" $lt_i lt_foo; } else { lt_count--; } } } } if (lt_foo != "") { lt_freq[lt_foo]++; } if (lt_freq[lt_foo] == 1) { print lt_foo; } }'` # AWK program above erroneously prepends '/' to C:/dos/paths # for these hosts. case $host_os in mingw* | cegcc*) lt_search_path_spec=`$ECHO "$lt_search_path_spec" |\ $SED 's|/\([A-Za-z]:\)|\1|g'` ;; esac sys_lib_search_path_spec=`$ECHO "$lt_search_path_spec" | $lt_NL2SP` else sys_lib_search_path_spec="/lib /usr/lib /usr/local/lib" fi library_names_spec= libname_spec='lib$name' soname_spec= shrext_cmds=.so postinstall_cmds= postuninstall_cmds= finish_cmds= finish_eval= shlibpath_var= shlibpath_overrides_runpath=unknown version_type=none dynamic_linker="$host_os ld.so" sys_lib_dlsearch_path_spec="/lib /usr/lib" need_lib_prefix=unknown hardcode_into_libs=no # when you set need_version to no, make sure it does not cause -set_version # flags to be left without arguments need_version=unknown case $host_os in aix3*) version_type=linux # correct to gnu/linux during the next big refactor library_names_spec='$libname$release$shared_ext$versuffix $libname.a' shlibpath_var=LIBPATH # AIX 3 has no versioning support, so we append a major version to the name. soname_spec='$libname$release$shared_ext$major' ;; aix[4-9]*) version_type=linux # correct to gnu/linux during the next big refactor need_lib_prefix=no need_version=no hardcode_into_libs=yes if test ia64 = "$host_cpu"; then # AIX 5 supports IA64 library_names_spec='$libname$release$shared_ext$major $libname$release$shared_ext$versuffix $libname$shared_ext' shlibpath_var=LD_LIBRARY_PATH else # With GCC up to 2.95.x, collect2 would create an import file # for dependence libraries. The import file would start with # the line '#! .'. This would cause the generated library to # depend on '.', always an invalid library. This was fixed in # development snapshots of GCC prior to 3.0. case $host_os in aix4 | aix4.[01] | aix4.[01].*) if { echo '#if __GNUC__ > 2 || (__GNUC__ == 2 && __GNUC_MINOR__ >= 97)' echo ' yes ' echo '#endif'; } | $CC -E - | $GREP yes > /dev/null; then : else can_build_shared=no fi ;; esac # Using Import Files as archive members, it is possible to support # filename-based versioning of shared library archives on AIX. While # this would work for both with and without runtime linking, it will # prevent static linking of such archives. So we do filename-based # shared library versioning with .so extension only, which is used # when both runtime linking and shared linking is enabled. # Unfortunately, runtime linking may impact performance, so we do # not want this to be the default eventually. Also, we use the # versioned .so libs for executables only if there is the -brtl # linker flag in LDFLAGS as well, or --with-aix-soname=svr4 only. # To allow for filename-based versioning support, we need to create # libNAME.so.V as an archive file, containing: # *) an Import File, referring to the versioned filename of the # archive as well as the shared archive member, telling the # bitwidth (32 or 64) of that shared object, and providing the # list of exported symbols of that shared object, eventually # decorated with the 'weak' keyword # *) the shared object with the F_LOADONLY flag set, to really avoid # it being seen by the linker. # At run time we better use the real file rather than another symlink, # but for link time we create the symlink libNAME.so -> libNAME.so.V case $with_aix_soname,$aix_use_runtimelinking in # AIX (on Power*) has no versioning support, so currently we cannot hardcode correct # soname into executable. Probably we can add versioning support to # collect2, so additional links can be useful in future. aix,yes) # traditional libtool dynamic_linker='AIX unversionable lib.so' # If using run time linking (on AIX 4.2 or later) use lib.so # instead of lib.a to let people know that these are not # typical AIX shared libraries. library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext' ;; aix,no) # traditional AIX only dynamic_linker='AIX lib.a(lib.so.V)' # We preserve .a as extension for shared libraries through AIX4.2 # and later when we are not doing run time linking. library_names_spec='$libname$release.a $libname.a' soname_spec='$libname$release$shared_ext$major' ;; svr4,*) # full svr4 only dynamic_linker="AIX lib.so.V($shared_archive_member_spec.o)" library_names_spec='$libname$release$shared_ext$major $libname$shared_ext' # We do not specify a path in Import Files, so LIBPATH fires. shlibpath_overrides_runpath=yes ;; *,yes) # both, prefer svr4 dynamic_linker="AIX lib.so.V($shared_archive_member_spec.o), lib.a(lib.so.V)" library_names_spec='$libname$release$shared_ext$major $libname$shared_ext' # unpreferred sharedlib libNAME.a needs extra handling postinstall_cmds='test -n "$linkname" || linkname="$realname"~func_stripname "" ".so" "$linkname"~$install_shared_prog "$dir/$func_stripname_result.$libext" "$destdir/$func_stripname_result.$libext"~test -z "$tstripme" || test -z "$striplib" || $striplib "$destdir/$func_stripname_result.$libext"' postuninstall_cmds='for n in $library_names $old_library; do :; done~func_stripname "" ".so" "$n"~test "$func_stripname_result" = "$n" || func_append rmfiles " $odir/$func_stripname_result.$libext"' # We do not specify a path in Import Files, so LIBPATH fires. shlibpath_overrides_runpath=yes ;; *,no) # both, prefer aix dynamic_linker="AIX lib.a(lib.so.V), lib.so.V($shared_archive_member_spec.o)" library_names_spec='$libname$release.a $libname.a' soname_spec='$libname$release$shared_ext$major' # unpreferred sharedlib libNAME.so.V and symlink libNAME.so need extra handling postinstall_cmds='test -z "$dlname" || $install_shared_prog $dir/$dlname $destdir/$dlname~test -z "$tstripme" || test -z "$striplib" || $striplib $destdir/$dlname~test -n "$linkname" || linkname=$realname~func_stripname "" ".a" "$linkname"~(cd "$destdir" && $LN_S -f $dlname $func_stripname_result.so)' postuninstall_cmds='test -z "$dlname" || func_append rmfiles " $odir/$dlname"~for n in $old_library $library_names; do :; done~func_stripname "" ".a" "$n"~func_append rmfiles " $odir/$func_stripname_result.so"' ;; esac shlibpath_var=LIBPATH fi ;; amigaos*) case $host_cpu in powerpc) # Since July 2007 AmigaOS4 officially supports .so libraries. # When compiling the executable, add -use-dynld -Lsobjs: to the compileline. library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext' ;; m68k) library_names_spec='$libname.ixlibrary $libname.a' # Create ${libname}_ixlibrary.a entries in /sys/libs. finish_eval='for lib in `ls $libdir/*.ixlibrary 2>/dev/null`; do libname=`func_echo_all "$lib" | $SED '\''s%^.*/\([^/]*\)\.ixlibrary$%\1%'\''`; $RM /sys/libs/${libname}_ixlibrary.a; $show "cd /sys/libs && $LN_S $lib ${libname}_ixlibrary.a"; cd /sys/libs && $LN_S $lib ${libname}_ixlibrary.a || exit 1; done' ;; esac ;; beos*) library_names_spec='$libname$shared_ext' dynamic_linker="$host_os ld.so" shlibpath_var=LIBRARY_PATH ;; bsdi[45]*) version_type=linux # correct to gnu/linux during the next big refactor need_version=no library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext' soname_spec='$libname$release$shared_ext$major' finish_cmds='PATH="\$PATH:/sbin" ldconfig $libdir' shlibpath_var=LD_LIBRARY_PATH sys_lib_search_path_spec="/shlib /usr/lib /usr/X11/lib /usr/contrib/lib /lib /usr/local/lib" sys_lib_dlsearch_path_spec="/shlib /usr/lib /usr/local/lib" # the default ld.so.conf also contains /usr/contrib/lib and # /usr/X11R6/lib (/usr/X11 is a link to /usr/X11R6), but let us allow # libtool to hard-code these into programs ;; cygwin* | mingw* | pw32* | cegcc*) version_type=windows shrext_cmds=.dll need_version=no need_lib_prefix=no case $GCC,$cc_basename in yes,*) # gcc library_names_spec='$libname.dll.a' # DLL is installed to $(libdir)/../bin by postinstall_cmds postinstall_cmds='base_file=`basename \$file`~ dlpath=`$SHELL 2>&1 -c '\''. $dir/'\''\$base_file'\''i; echo \$dlname'\''`~ dldir=$destdir/`dirname \$dlpath`~ test -d \$dldir || mkdir -p \$dldir~ $install_prog $dir/$dlname \$dldir/$dlname~ chmod a+x \$dldir/$dlname~ if test -n '\''$stripme'\'' && test -n '\''$striplib'\''; then eval '\''$striplib \$dldir/$dlname'\'' || exit \$?; fi' postuninstall_cmds='dldll=`$SHELL 2>&1 -c '\''. $file; echo \$dlname'\''`~ dlpath=$dir/\$dldll~ $RM \$dlpath' shlibpath_overrides_runpath=yes case $host_os in cygwin*) # Cygwin DLLs use 'cyg' prefix rather than 'lib' soname_spec='`echo $libname | $SED -e 's/^lib/cyg/'``echo $release | $SED -e 's/[.]/-/g'`$versuffix$shared_ext' sys_lib_search_path_spec="$sys_lib_search_path_spec /usr/lib/w32api" ;; mingw* | cegcc*) # MinGW DLLs use traditional 'lib' prefix soname_spec='$libname`echo $release | $SED -e 's/[.]/-/g'`$versuffix$shared_ext' ;; pw32*) # pw32 DLLs use 'pw' prefix rather than 'lib' library_names_spec='`echo $libname | $SED -e 's/^lib/pw/'``echo $release | $SED -e 's/[.]/-/g'`$versuffix$shared_ext' ;; esac dynamic_linker='Win32 ld.exe' ;; *,cl* | *,icl*) # Native MSVC or ICC libname_spec='$name' soname_spec='$libname`echo $release | $SED -e 's/[.]/-/g'`$versuffix$shared_ext' library_names_spec='$libname.dll.lib' case $build_os in mingw*) sys_lib_search_path_spec= lt_save_ifs=$IFS IFS=';' for lt_path in $LIB do IFS=$lt_save_ifs # Let DOS variable expansion print the short 8.3 style file name. lt_path=`cd "$lt_path" 2>/dev/null && cmd //C "for %i in (".") do @echo %~si"` sys_lib_search_path_spec="$sys_lib_search_path_spec $lt_path" done IFS=$lt_save_ifs # Convert to MSYS style. sys_lib_search_path_spec=`$ECHO "$sys_lib_search_path_spec" | $SED -e 's|\\\\|/|g' -e 's| \\([a-zA-Z]\\):| /\\1|g' -e 's|^ ||'` ;; cygwin*) # Convert to unix form, then to dos form, then back to unix form # but this time dos style (no spaces!) so that the unix form looks # like /cygdrive/c/PROGRA~1:/cygdr... sys_lib_search_path_spec=`cygpath --path --unix "$LIB"` sys_lib_search_path_spec=`cygpath --path --dos "$sys_lib_search_path_spec" 2>/dev/null` sys_lib_search_path_spec=`cygpath --path --unix "$sys_lib_search_path_spec" | $SED -e "s/$PATH_SEPARATOR/ /g"` ;; *) sys_lib_search_path_spec=$LIB if $ECHO "$sys_lib_search_path_spec" | $GREP ';[c-zC-Z]:/' >/dev/null; then # It is most probably a Windows format PATH. sys_lib_search_path_spec=`$ECHO "$sys_lib_search_path_spec" | $SED -e 's/;/ /g'` else sys_lib_search_path_spec=`$ECHO "$sys_lib_search_path_spec" | $SED -e "s/$PATH_SEPARATOR/ /g"` fi # FIXME: find the short name or the path components, as spaces are # common. (e.g. "Program Files" -> "PROGRA~1") ;; esac # DLL is installed to $(libdir)/../bin by postinstall_cmds postinstall_cmds='base_file=`basename \$file`~ dlpath=`$SHELL 2>&1 -c '\''. $dir/'\''\$base_file'\''i; echo \$dlname'\''`~ dldir=$destdir/`dirname \$dlpath`~ test -d \$dldir || mkdir -p \$dldir~ $install_prog $dir/$dlname \$dldir/$dlname' postuninstall_cmds='dldll=`$SHELL 2>&1 -c '\''. $file; echo \$dlname'\''`~ dlpath=$dir/\$dldll~ $RM \$dlpath' shlibpath_overrides_runpath=yes dynamic_linker='Win32 link.exe' ;; *) # Assume MSVC and ICC wrapper library_names_spec='$libname`echo $release | $SED -e 's/[.]/-/g'`$versuffix$shared_ext $libname.lib' dynamic_linker='Win32 ld.exe' ;; esac # FIXME: first we should search . and the directory the executable is in shlibpath_var=PATH ;; darwin* | rhapsody*) dynamic_linker="$host_os dyld" version_type=darwin need_lib_prefix=no need_version=no library_names_spec='$libname$release$major$shared_ext $libname$shared_ext' soname_spec='$libname$release$major$shared_ext' shlibpath_overrides_runpath=yes shlibpath_var=DYLD_LIBRARY_PATH shrext_cmds='`test .$module = .yes && echo .so || echo .dylib`' sys_lib_search_path_spec="$sys_lib_search_path_spec /usr/local/lib" sys_lib_dlsearch_path_spec='/usr/local/lib /lib /usr/lib' ;; dgux*) version_type=linux # correct to gnu/linux during the next big refactor need_lib_prefix=no need_version=no library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext' soname_spec='$libname$release$shared_ext$major' shlibpath_var=LD_LIBRARY_PATH ;; freebsd* | dragonfly* | midnightbsd*) # DragonFly does not have aout. When/if they implement a new # versioning mechanism, adjust this. if test -x /usr/bin/objformat; then objformat=`/usr/bin/objformat` else case $host_os in freebsd[23].*) objformat=aout ;; *) objformat=elf ;; esac fi version_type=freebsd-$objformat case $version_type in freebsd-elf*) library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext' soname_spec='$libname$release$shared_ext$major' need_version=no need_lib_prefix=no ;; freebsd-*) library_names_spec='$libname$release$shared_ext$versuffix $libname$shared_ext$versuffix' need_version=yes ;; esac shlibpath_var=LD_LIBRARY_PATH case $host_os in freebsd2.*) shlibpath_overrides_runpath=yes ;; freebsd3.[01]* | freebsdelf3.[01]*) shlibpath_overrides_runpath=yes hardcode_into_libs=yes ;; freebsd3.[2-9]* | freebsdelf3.[2-9]* | \ freebsd4.[0-5] | freebsdelf4.[0-5] | freebsd4.1.1 | freebsdelf4.1.1) shlibpath_overrides_runpath=no hardcode_into_libs=yes ;; *) # from 4.6 on, and DragonFly shlibpath_overrides_runpath=yes hardcode_into_libs=yes ;; esac ;; haiku*) version_type=linux # correct to gnu/linux during the next big refactor need_lib_prefix=no need_version=no dynamic_linker="$host_os runtime_loader" library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext' soname_spec='$libname$release$shared_ext$major' shlibpath_var=LIBRARY_PATH shlibpath_overrides_runpath=no sys_lib_dlsearch_path_spec='/boot/home/config/lib /boot/common/lib /boot/system/lib' hardcode_into_libs=yes ;; hpux9* | hpux10* | hpux11*) # Give a soname corresponding to the major version so that dld.sl refuses to # link against other versions. version_type=sunos need_lib_prefix=no need_version=no case $host_cpu in ia64*) shrext_cmds='.so' hardcode_into_libs=yes dynamic_linker="$host_os dld.so" shlibpath_var=LD_LIBRARY_PATH shlibpath_overrides_runpath=yes # Unless +noenvvar is specified. library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext' soname_spec='$libname$release$shared_ext$major' if test 32 = "$HPUX_IA64_MODE"; then sys_lib_search_path_spec="/usr/lib/hpux32 /usr/local/lib/hpux32 /usr/local/lib" sys_lib_dlsearch_path_spec=/usr/lib/hpux32 else sys_lib_search_path_spec="/usr/lib/hpux64 /usr/local/lib/hpux64" sys_lib_dlsearch_path_spec=/usr/lib/hpux64 fi ;; hppa*64*) shrext_cmds='.sl' hardcode_into_libs=yes dynamic_linker="$host_os dld.sl" shlibpath_var=LD_LIBRARY_PATH # How should we handle SHLIB_PATH shlibpath_overrides_runpath=yes # Unless +noenvvar is specified. library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext' soname_spec='$libname$release$shared_ext$major' sys_lib_search_path_spec="/usr/lib/pa20_64 /usr/ccs/lib/pa20_64" sys_lib_dlsearch_path_spec=$sys_lib_search_path_spec ;; *) shrext_cmds='.sl' dynamic_linker="$host_os dld.sl" shlibpath_var=SHLIB_PATH shlibpath_overrides_runpath=no # +s is required to enable SHLIB_PATH library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext' soname_spec='$libname$release$shared_ext$major' ;; esac # HP-UX runs *really* slowly unless shared libraries are mode 555, ... postinstall_cmds='chmod 555 $lib' # or fails outright, so override atomically: install_override_mode=555 ;; interix[3-9]*) version_type=linux # correct to gnu/linux during the next big refactor need_lib_prefix=no need_version=no library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext' soname_spec='$libname$release$shared_ext$major' dynamic_linker='Interix 3.x ld.so.1 (PE, like ELF)' shlibpath_var=LD_LIBRARY_PATH shlibpath_overrides_runpath=no hardcode_into_libs=yes ;; irix5* | irix6* | nonstopux*) case $host_os in nonstopux*) version_type=nonstopux ;; *) if test yes = "$lt_cv_prog_gnu_ld"; then version_type=linux # correct to gnu/linux during the next big refactor else version_type=irix fi ;; esac need_lib_prefix=no need_version=no soname_spec='$libname$release$shared_ext$major' library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$release$shared_ext $libname$shared_ext' case $host_os in irix5* | nonstopux*) libsuff= shlibsuff= ;; *) case $LD in # libtool.m4 will add one of these switches to LD *-32|*"-32 "|*-melf32bsmip|*"-melf32bsmip ") libsuff= shlibsuff= libmagic=32-bit;; *-n32|*"-n32 "|*-melf32bmipn32|*"-melf32bmipn32 ") libsuff=32 shlibsuff=N32 libmagic=N32;; *-64|*"-64 "|*-melf64bmip|*"-melf64bmip ") libsuff=64 shlibsuff=64 libmagic=64-bit;; *) libsuff= shlibsuff= libmagic=never-match;; esac ;; esac shlibpath_var=LD_LIBRARY${shlibsuff}_PATH shlibpath_overrides_runpath=no sys_lib_search_path_spec="/usr/lib$libsuff /lib$libsuff /usr/local/lib$libsuff" sys_lib_dlsearch_path_spec="/usr/lib$libsuff /lib$libsuff" hardcode_into_libs=yes ;; # No shared lib support for Linux oldld, aout, or coff. linux*oldld* | linux*aout* | linux*coff*) dynamic_linker=no ;; linux*android*) version_type=none # Android doesn't support versioned libraries. need_lib_prefix=no need_version=no library_names_spec='$libname$release$shared_ext' soname_spec='$libname$release$shared_ext' finish_cmds= shlibpath_var=LD_LIBRARY_PATH shlibpath_overrides_runpath=yes # This implies no fast_install, which is unacceptable. # Some rework will be needed to allow for fast_install # before this can be enabled. hardcode_into_libs=yes dynamic_linker='Android linker' # Don't embed -rpath directories since the linker doesn't support them. hardcode_libdir_flag_spec='-L$libdir' ;; # This must be glibc/ELF. linux* | k*bsd*-gnu | kopensolaris*-gnu | gnu*) version_type=linux # correct to gnu/linux during the next big refactor need_lib_prefix=no need_version=no library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext' soname_spec='$libname$release$shared_ext$major' finish_cmds='PATH="\$PATH:/sbin" ldconfig -n $libdir' shlibpath_var=LD_LIBRARY_PATH shlibpath_overrides_runpath=no # Some binutils ld are patched to set DT_RUNPATH if test ${lt_cv_shlibpath_overrides_runpath+y} then : printf %s "(cached) " >&6 else case e in #( e) lt_cv_shlibpath_overrides_runpath=no save_LDFLAGS=$LDFLAGS save_libdir=$libdir eval "libdir=/foo; wl=\"$lt_prog_compiler_wl\"; \ LDFLAGS=\"\$LDFLAGS $hardcode_libdir_flag_spec\"" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ int main (void) { ; return 0; } _ACEOF if ac_fn_c_try_link "$LINENO" then : if ($OBJDUMP -p conftest$ac_exeext) 2>/dev/null | grep "RUNPATH.*$libdir" >/dev/null then : lt_cv_shlibpath_overrides_runpath=yes fi fi rm -f core conftest.err conftest.$ac_objext conftest.beam \ conftest$ac_exeext conftest.$ac_ext LDFLAGS=$save_LDFLAGS libdir=$save_libdir ;; esac fi shlibpath_overrides_runpath=$lt_cv_shlibpath_overrides_runpath # This implies no fast_install, which is unacceptable. # Some rework will be needed to allow for fast_install # before this can be enabled. hardcode_into_libs=yes # Ideally, we could use ldconfig to report *all* directores which are # searched for libraries, however this is still not possible. Aside from not # being certain /sbin/ldconfig is available, command # 'ldconfig -N -X -v | grep ^/' on 64bit Fedora does not report /usr/lib64, # even though it is searched at run-time. Try to do the best guess by # appending ld.so.conf contents (and includes) to the search path. if test -f /etc/ld.so.conf; then lt_ld_extra=`awk '/^include / { system(sprintf("cd /etc; cat %s 2>/dev/null", \$2)); skip = 1; } { if (!skip) print \$0; skip = 0; }' < /etc/ld.so.conf | $SED -e 's/#.*//;/^[ ]*hwcap[ ]/d;s/[:, ]/ /g;s/=[^=]*$//;s/=[^= ]* / /g;s/"//g;/^$/d' | tr '\n' ' '` sys_lib_dlsearch_path_spec="/lib /usr/lib $lt_ld_extra" fi # We used to test for /lib/ld.so.1 and disable shared libraries on # powerpc, because MkLinux only supported shared libraries with the # GNU dynamic linker. Since this was broken with cross compilers, # most powerpc-linux boxes support dynamic linking these days and # people can always --disable-shared, the test was removed, and we # assume the GNU/Linux dynamic linker is in use. dynamic_linker='GNU/Linux ld.so' ;; netbsdelf*-gnu) version_type=linux need_lib_prefix=no need_version=no library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major ${libname}${shared_ext}' soname_spec='${libname}${release}${shared_ext}$major' shlibpath_var=LD_LIBRARY_PATH shlibpath_overrides_runpath=no hardcode_into_libs=yes dynamic_linker='NetBSD ld.elf_so' ;; netbsd*) version_type=sunos need_lib_prefix=no need_version=no if echo __ELF__ | $CC -E - | $GREP __ELF__ >/dev/null; then library_names_spec='$libname$release$shared_ext$versuffix $libname$shared_ext$versuffix' finish_cmds='PATH="\$PATH:/sbin" ldconfig -m $libdir' dynamic_linker='NetBSD (a.out) ld.so' else library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext' soname_spec='$libname$release$shared_ext$major' dynamic_linker='NetBSD ld.elf_so' fi shlibpath_var=LD_LIBRARY_PATH shlibpath_overrides_runpath=yes hardcode_into_libs=yes ;; newsos6) version_type=linux # correct to gnu/linux during the next big refactor library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext' shlibpath_var=LD_LIBRARY_PATH shlibpath_overrides_runpath=yes ;; *nto* | *qnx*) version_type=qnx need_lib_prefix=no need_version=no library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext' soname_spec='$libname$release$shared_ext$major' shlibpath_var=LD_LIBRARY_PATH shlibpath_overrides_runpath=no hardcode_into_libs=yes dynamic_linker='ldqnx.so' ;; openbsd* | bitrig*) version_type=sunos sys_lib_dlsearch_path_spec=/usr/lib need_lib_prefix=no if test -z "`echo __ELF__ | $CC -E - | $GREP __ELF__`"; then need_version=no else need_version=yes fi library_names_spec='$libname$release$shared_ext$versuffix $libname$shared_ext$versuffix' finish_cmds='PATH="\$PATH:/sbin" ldconfig -m $libdir' shlibpath_var=LD_LIBRARY_PATH shlibpath_overrides_runpath=yes ;; os2*) libname_spec='$name' version_type=windows shrext_cmds=.dll need_version=no need_lib_prefix=no # OS/2 can only load a DLL with a base name of 8 characters or less. soname_spec='`test -n "$os2dllname" && libname="$os2dllname"; v=$($ECHO $release$versuffix | tr -d .-); n=$($ECHO $libname | cut -b -$((8 - ${#v})) | tr . _); $ECHO $n$v`$shared_ext' library_names_spec='${libname}_dll.$libext' dynamic_linker='OS/2 ld.exe' shlibpath_var=BEGINLIBPATH sys_lib_search_path_spec="/lib /usr/lib /usr/local/lib" sys_lib_dlsearch_path_spec=$sys_lib_search_path_spec postinstall_cmds='base_file=`basename \$file`~ dlpath=`$SHELL 2>&1 -c '\''. $dir/'\''\$base_file'\''i; $ECHO \$dlname'\''`~ dldir=$destdir/`dirname \$dlpath`~ test -d \$dldir || mkdir -p \$dldir~ $install_prog $dir/$dlname \$dldir/$dlname~ chmod a+x \$dldir/$dlname~ if test -n '\''$stripme'\'' && test -n '\''$striplib'\''; then eval '\''$striplib \$dldir/$dlname'\'' || exit \$?; fi' postuninstall_cmds='dldll=`$SHELL 2>&1 -c '\''. $file; $ECHO \$dlname'\''`~ dlpath=$dir/\$dldll~ $RM \$dlpath' ;; osf3* | osf4* | osf5*) version_type=osf need_lib_prefix=no need_version=no soname_spec='$libname$release$shared_ext$major' library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext' shlibpath_var=LD_LIBRARY_PATH sys_lib_search_path_spec="/usr/shlib /usr/ccs/lib /usr/lib/cmplrs/cc /usr/lib /usr/local/lib /var/shlib" sys_lib_dlsearch_path_spec=$sys_lib_search_path_spec ;; rdos*) dynamic_linker=no ;; solaris*) version_type=linux # correct to gnu/linux during the next big refactor need_lib_prefix=no need_version=no library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext' soname_spec='$libname$release$shared_ext$major' shlibpath_var=LD_LIBRARY_PATH shlibpath_overrides_runpath=yes hardcode_into_libs=yes # ldd complains unless libraries are executable postinstall_cmds='chmod +x $lib' ;; sunos4*) version_type=sunos library_names_spec='$libname$release$shared_ext$versuffix $libname$shared_ext$versuffix' finish_cmds='PATH="\$PATH:/usr/etc" ldconfig $libdir' shlibpath_var=LD_LIBRARY_PATH shlibpath_overrides_runpath=yes if test yes = "$with_gnu_ld"; then need_lib_prefix=no fi need_version=yes ;; sysv4 | sysv4.3*) version_type=linux # correct to gnu/linux during the next big refactor library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext' soname_spec='$libname$release$shared_ext$major' shlibpath_var=LD_LIBRARY_PATH case $host_vendor in sni) shlibpath_overrides_runpath=no need_lib_prefix=no runpath_var=LD_RUN_PATH ;; siemens) need_lib_prefix=no ;; motorola) need_lib_prefix=no need_version=no shlibpath_overrides_runpath=no sys_lib_search_path_spec='/lib /usr/lib /usr/ccs/lib' ;; esac ;; sysv4*MP*) if test -d /usr/nec; then version_type=linux # correct to gnu/linux during the next big refactor library_names_spec='$libname$shared_ext.$versuffix $libname$shared_ext.$major $libname$shared_ext' soname_spec='$libname$shared_ext.$major' shlibpath_var=LD_LIBRARY_PATH fi ;; sysv5* | sco3.2v5* | sco5v6* | unixware* | OpenUNIX* | sysv4*uw2*) version_type=sco need_lib_prefix=no need_version=no library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext $libname$shared_ext' soname_spec='$libname$release$shared_ext$major' shlibpath_var=LD_LIBRARY_PATH shlibpath_overrides_runpath=yes hardcode_into_libs=yes if test yes = "$with_gnu_ld"; then sys_lib_search_path_spec='/usr/local/lib /usr/gnu/lib /usr/ccs/lib /usr/lib /lib' else sys_lib_search_path_spec='/usr/ccs/lib /usr/lib' case $host_os in sco3.2v5*) sys_lib_search_path_spec="$sys_lib_search_path_spec /lib" ;; esac fi sys_lib_dlsearch_path_spec='/usr/lib' ;; tpf*) # TPF is a cross-target only. Preferred cross-host = GNU/Linux. version_type=linux # correct to gnu/linux during the next big refactor need_lib_prefix=no need_version=no library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext' shlibpath_var=LD_LIBRARY_PATH shlibpath_overrides_runpath=no hardcode_into_libs=yes ;; uts4*) version_type=linux # correct to gnu/linux during the next big refactor library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext' soname_spec='$libname$release$shared_ext$major' shlibpath_var=LD_LIBRARY_PATH ;; *) dynamic_linker=no ;; esac { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $dynamic_linker" >&5 printf "%s\n" "$dynamic_linker" >&6; } test no = "$dynamic_linker" && can_build_shared=no variables_saved_for_relink="PATH $shlibpath_var $runpath_var" if test yes = "$GCC"; then variables_saved_for_relink="$variables_saved_for_relink GCC_EXEC_PREFIX COMPILER_PATH LIBRARY_PATH" fi if test set = "${lt_cv_sys_lib_search_path_spec+set}"; then sys_lib_search_path_spec=$lt_cv_sys_lib_search_path_spec fi if test set = "${lt_cv_sys_lib_dlsearch_path_spec+set}"; then sys_lib_dlsearch_path_spec=$lt_cv_sys_lib_dlsearch_path_spec fi # remember unaugmented sys_lib_dlsearch_path content for libtool script decls... configure_time_dlsearch_path=$sys_lib_dlsearch_path_spec # ... but it needs LT_SYS_LIBRARY_PATH munging for other configure-time code func_munge_path_list sys_lib_dlsearch_path_spec "$LT_SYS_LIBRARY_PATH" # to be used as default LT_SYS_LIBRARY_PATH value in generated libtool configure_time_lt_sys_library_path=$LT_SYS_LIBRARY_PATH { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking how to hardcode library paths into programs" >&5 printf %s "checking how to hardcode library paths into programs... " >&6; } hardcode_action= if test -n "$hardcode_libdir_flag_spec" || test -n "$runpath_var" || test yes = "$hardcode_automatic"; then # We can hardcode non-existent directories. if test no != "$hardcode_direct" && # If the only mechanism to avoid hardcoding is shlibpath_var, we # have to relink, otherwise we might link with an installed library # when we should be linking with a yet-to-be-installed one ## test no != "$_LT_TAGVAR(hardcode_shlibpath_var, )" && test no != "$hardcode_minus_L"; then # Linking always hardcodes the temporary library directory. hardcode_action=relink else # We can link without hardcoding, and we can hardcode nonexisting dirs. hardcode_action=immediate fi else # We cannot hardcode anything, or else we can only hardcode existing # directories. hardcode_action=unsupported fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $hardcode_action" >&5 printf "%s\n" "$hardcode_action" >&6; } if test relink = "$hardcode_action" || test yes = "$inherit_rpath"; then # Fast installation is not supported enable_fast_install=no elif test yes = "$shlibpath_overrides_runpath" || test no = "$enable_shared"; then # Fast installation is not necessary enable_fast_install=needless fi if test yes != "$enable_dlopen"; then enable_dlopen=unknown enable_dlopen_self=unknown enable_dlopen_self_static=unknown else lt_cv_dlopen=no lt_cv_dlopen_libs= case $host_os in beos*) lt_cv_dlopen=load_add_on lt_cv_dlopen_libs= lt_cv_dlopen_self=yes ;; mingw* | pw32* | cegcc*) lt_cv_dlopen=LoadLibrary lt_cv_dlopen_libs= ;; cygwin*) lt_cv_dlopen=dlopen lt_cv_dlopen_libs= ;; darwin*) # if libdl is installed we need to link against it { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for dlopen in -ldl" >&5 printf %s "checking for dlopen in -ldl... " >&6; } if test ${ac_cv_lib_dl_dlopen+y} then : printf %s "(cached) " >&6 else case e in #( e) ac_check_lib_save_LIBS=$LIBS LIBS="-ldl $LIBS" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ /* Override any GCC internal prototype to avoid an error. Use char because int might match the return type of a GCC builtin and then its argument prototype would still apply. The 'extern "C"' is for builds by C++ compilers; although this is not generally supported in C code supporting it here has little cost and some practical benefit (sr 110532). */ #ifdef __cplusplus extern "C" #endif char dlopen (void); int main (void) { return dlopen (); ; return 0; } _ACEOF if ac_fn_c_try_link "$LINENO" then : ac_cv_lib_dl_dlopen=yes else case e in #( e) ac_cv_lib_dl_dlopen=no ;; esac fi rm -f core conftest.err conftest.$ac_objext conftest.beam \ conftest$ac_exeext conftest.$ac_ext LIBS=$ac_check_lib_save_LIBS ;; esac fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_dl_dlopen" >&5 printf "%s\n" "$ac_cv_lib_dl_dlopen" >&6; } if test "x$ac_cv_lib_dl_dlopen" = xyes then : lt_cv_dlopen=dlopen lt_cv_dlopen_libs=-ldl else case e in #( e) lt_cv_dlopen=dyld lt_cv_dlopen_libs= lt_cv_dlopen_self=yes ;; esac fi ;; tpf*) # Don't try to run any link tests for TPF. We know it's impossible # because TPF is a cross-compiler, and we know how we open DSOs. lt_cv_dlopen=dlopen lt_cv_dlopen_libs= lt_cv_dlopen_self=no ;; *) ac_fn_c_check_func "$LINENO" "shl_load" "ac_cv_func_shl_load" if test "x$ac_cv_func_shl_load" = xyes then : lt_cv_dlopen=shl_load else case e in #( e) { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for shl_load in -ldld" >&5 printf %s "checking for shl_load in -ldld... " >&6; } if test ${ac_cv_lib_dld_shl_load+y} then : printf %s "(cached) " >&6 else case e in #( e) ac_check_lib_save_LIBS=$LIBS LIBS="-ldld $LIBS" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ /* Override any GCC internal prototype to avoid an error. Use char because int might match the return type of a GCC builtin and then its argument prototype would still apply. The 'extern "C"' is for builds by C++ compilers; although this is not generally supported in C code supporting it here has little cost and some practical benefit (sr 110532). */ #ifdef __cplusplus extern "C" #endif char shl_load (void); int main (void) { return shl_load (); ; return 0; } _ACEOF if ac_fn_c_try_link "$LINENO" then : ac_cv_lib_dld_shl_load=yes else case e in #( e) ac_cv_lib_dld_shl_load=no ;; esac fi rm -f core conftest.err conftest.$ac_objext conftest.beam \ conftest$ac_exeext conftest.$ac_ext LIBS=$ac_check_lib_save_LIBS ;; esac fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_dld_shl_load" >&5 printf "%s\n" "$ac_cv_lib_dld_shl_load" >&6; } if test "x$ac_cv_lib_dld_shl_load" = xyes then : lt_cv_dlopen=shl_load lt_cv_dlopen_libs=-ldld else case e in #( e) ac_fn_c_check_func "$LINENO" "dlopen" "ac_cv_func_dlopen" if test "x$ac_cv_func_dlopen" = xyes then : lt_cv_dlopen=dlopen else case e in #( e) { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for dlopen in -ldl" >&5 printf %s "checking for dlopen in -ldl... " >&6; } if test ${ac_cv_lib_dl_dlopen+y} then : printf %s "(cached) " >&6 else case e in #( e) ac_check_lib_save_LIBS=$LIBS LIBS="-ldl $LIBS" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ /* Override any GCC internal prototype to avoid an error. Use char because int might match the return type of a GCC builtin and then its argument prototype would still apply. The 'extern "C"' is for builds by C++ compilers; although this is not generally supported in C code supporting it here has little cost and some practical benefit (sr 110532). */ #ifdef __cplusplus extern "C" #endif char dlopen (void); int main (void) { return dlopen (); ; return 0; } _ACEOF if ac_fn_c_try_link "$LINENO" then : ac_cv_lib_dl_dlopen=yes else case e in #( e) ac_cv_lib_dl_dlopen=no ;; esac fi rm -f core conftest.err conftest.$ac_objext conftest.beam \ conftest$ac_exeext conftest.$ac_ext LIBS=$ac_check_lib_save_LIBS ;; esac fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_dl_dlopen" >&5 printf "%s\n" "$ac_cv_lib_dl_dlopen" >&6; } if test "x$ac_cv_lib_dl_dlopen" = xyes then : lt_cv_dlopen=dlopen lt_cv_dlopen_libs=-ldl else case e in #( e) { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for dlopen in -lsvld" >&5 printf %s "checking for dlopen in -lsvld... " >&6; } if test ${ac_cv_lib_svld_dlopen+y} then : printf %s "(cached) " >&6 else case e in #( e) ac_check_lib_save_LIBS=$LIBS LIBS="-lsvld $LIBS" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ /* Override any GCC internal prototype to avoid an error. Use char because int might match the return type of a GCC builtin and then its argument prototype would still apply. The 'extern "C"' is for builds by C++ compilers; although this is not generally supported in C code supporting it here has little cost and some practical benefit (sr 110532). */ #ifdef __cplusplus extern "C" #endif char dlopen (void); int main (void) { return dlopen (); ; return 0; } _ACEOF if ac_fn_c_try_link "$LINENO" then : ac_cv_lib_svld_dlopen=yes else case e in #( e) ac_cv_lib_svld_dlopen=no ;; esac fi rm -f core conftest.err conftest.$ac_objext conftest.beam \ conftest$ac_exeext conftest.$ac_ext LIBS=$ac_check_lib_save_LIBS ;; esac fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_svld_dlopen" >&5 printf "%s\n" "$ac_cv_lib_svld_dlopen" >&6; } if test "x$ac_cv_lib_svld_dlopen" = xyes then : lt_cv_dlopen=dlopen lt_cv_dlopen_libs=-lsvld else case e in #( e) { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for dld_link in -ldld" >&5 printf %s "checking for dld_link in -ldld... " >&6; } if test ${ac_cv_lib_dld_dld_link+y} then : printf %s "(cached) " >&6 else case e in #( e) ac_check_lib_save_LIBS=$LIBS LIBS="-ldld $LIBS" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ /* Override any GCC internal prototype to avoid an error. Use char because int might match the return type of a GCC builtin and then its argument prototype would still apply. The 'extern "C"' is for builds by C++ compilers; although this is not generally supported in C code supporting it here has little cost and some practical benefit (sr 110532). */ #ifdef __cplusplus extern "C" #endif char dld_link (void); int main (void) { return dld_link (); ; return 0; } _ACEOF if ac_fn_c_try_link "$LINENO" then : ac_cv_lib_dld_dld_link=yes else case e in #( e) ac_cv_lib_dld_dld_link=no ;; esac fi rm -f core conftest.err conftest.$ac_objext conftest.beam \ conftest$ac_exeext conftest.$ac_ext LIBS=$ac_check_lib_save_LIBS ;; esac fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_dld_dld_link" >&5 printf "%s\n" "$ac_cv_lib_dld_dld_link" >&6; } if test "x$ac_cv_lib_dld_dld_link" = xyes then : lt_cv_dlopen=dld_link lt_cv_dlopen_libs=-ldld fi ;; esac fi ;; esac fi ;; esac fi ;; esac fi ;; esac fi ;; esac if test no = "$lt_cv_dlopen"; then enable_dlopen=no else enable_dlopen=yes fi case $lt_cv_dlopen in dlopen) save_CPPFLAGS=$CPPFLAGS test yes = "$ac_cv_header_dlfcn_h" && CPPFLAGS="$CPPFLAGS -DHAVE_DLFCN_H" save_LDFLAGS=$LDFLAGS wl=$lt_prog_compiler_wl eval LDFLAGS=\"\$LDFLAGS $export_dynamic_flag_spec\" save_LIBS=$LIBS LIBS="$lt_cv_dlopen_libs $LIBS" { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking whether a program can dlopen itself" >&5 printf %s "checking whether a program can dlopen itself... " >&6; } if test ${lt_cv_dlopen_self+y} then : printf %s "(cached) " >&6 else case e in #( e) if test yes = "$cross_compiling"; then : lt_cv_dlopen_self=cross else lt_dlunknown=0; lt_dlno_uscore=1; lt_dlneed_uscore=2 lt_status=$lt_dlunknown cat > conftest.$ac_ext <<_LT_EOF #line $LINENO "configure" #include "confdefs.h" #if HAVE_DLFCN_H #include #endif #include #ifdef RTLD_GLOBAL # define LT_DLGLOBAL RTLD_GLOBAL #else # ifdef DL_GLOBAL # define LT_DLGLOBAL DL_GLOBAL # else # define LT_DLGLOBAL 0 # endif #endif /* We may have to define LT_DLLAZY_OR_NOW in the command line if we find out it does not work in some platform. */ #ifndef LT_DLLAZY_OR_NOW # ifdef RTLD_LAZY # define LT_DLLAZY_OR_NOW RTLD_LAZY # else # ifdef DL_LAZY # define LT_DLLAZY_OR_NOW DL_LAZY # else # ifdef RTLD_NOW # define LT_DLLAZY_OR_NOW RTLD_NOW # else # ifdef DL_NOW # define LT_DLLAZY_OR_NOW DL_NOW # else # define LT_DLLAZY_OR_NOW 0 # endif # endif # endif # endif #endif /* When -fvisibility=hidden is used, assume the code has been annotated correspondingly for the symbols needed. */ #if defined __GNUC__ && (((__GNUC__ == 3) && (__GNUC_MINOR__ >= 3)) || (__GNUC__ > 3)) int fnord () __attribute__((visibility("default"))); #endif int fnord () { return 42; } int main () { void *self = dlopen (0, LT_DLGLOBAL|LT_DLLAZY_OR_NOW); int status = $lt_dlunknown; if (self) { if (dlsym (self,"fnord")) status = $lt_dlno_uscore; else { if (dlsym( self,"_fnord")) status = $lt_dlneed_uscore; else puts (dlerror ()); } /* dlclose (self); */ } else puts (dlerror ()); return status; } _LT_EOF if { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$ac_link\""; } >&5 (eval $ac_link) 2>&5 ac_status=$? printf "%s\n" "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; } && test -s "conftest$ac_exeext" 2>/dev/null; then (./conftest; exit; ) >&5 2>/dev/null lt_status=$? case x$lt_status in x$lt_dlno_uscore) lt_cv_dlopen_self=yes ;; x$lt_dlneed_uscore) lt_cv_dlopen_self=yes ;; x$lt_dlunknown|x*) lt_cv_dlopen_self=no ;; esac else : # compilation failed lt_cv_dlopen_self=no fi fi rm -fr conftest* ;; esac fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $lt_cv_dlopen_self" >&5 printf "%s\n" "$lt_cv_dlopen_self" >&6; } if test yes = "$lt_cv_dlopen_self"; then wl=$lt_prog_compiler_wl eval LDFLAGS=\"\$LDFLAGS $lt_prog_compiler_static\" { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking whether a statically linked program can dlopen itself" >&5 printf %s "checking whether a statically linked program can dlopen itself... " >&6; } if test ${lt_cv_dlopen_self_static+y} then : printf %s "(cached) " >&6 else case e in #( e) if test yes = "$cross_compiling"; then : lt_cv_dlopen_self_static=cross else lt_dlunknown=0; lt_dlno_uscore=1; lt_dlneed_uscore=2 lt_status=$lt_dlunknown cat > conftest.$ac_ext <<_LT_EOF #line $LINENO "configure" #include "confdefs.h" #if HAVE_DLFCN_H #include #endif #include #ifdef RTLD_GLOBAL # define LT_DLGLOBAL RTLD_GLOBAL #else # ifdef DL_GLOBAL # define LT_DLGLOBAL DL_GLOBAL # else # define LT_DLGLOBAL 0 # endif #endif /* We may have to define LT_DLLAZY_OR_NOW in the command line if we find out it does not work in some platform. */ #ifndef LT_DLLAZY_OR_NOW # ifdef RTLD_LAZY # define LT_DLLAZY_OR_NOW RTLD_LAZY # else # ifdef DL_LAZY # define LT_DLLAZY_OR_NOW DL_LAZY # else # ifdef RTLD_NOW # define LT_DLLAZY_OR_NOW RTLD_NOW # else # ifdef DL_NOW # define LT_DLLAZY_OR_NOW DL_NOW # else # define LT_DLLAZY_OR_NOW 0 # endif # endif # endif # endif #endif /* When -fvisibility=hidden is used, assume the code has been annotated correspondingly for the symbols needed. */ #if defined __GNUC__ && (((__GNUC__ == 3) && (__GNUC_MINOR__ >= 3)) || (__GNUC__ > 3)) int fnord () __attribute__((visibility("default"))); #endif int fnord () { return 42; } int main () { void *self = dlopen (0, LT_DLGLOBAL|LT_DLLAZY_OR_NOW); int status = $lt_dlunknown; if (self) { if (dlsym (self,"fnord")) status = $lt_dlno_uscore; else { if (dlsym( self,"_fnord")) status = $lt_dlneed_uscore; else puts (dlerror ()); } /* dlclose (self); */ } else puts (dlerror ()); return status; } _LT_EOF if { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$ac_link\""; } >&5 (eval $ac_link) 2>&5 ac_status=$? printf "%s\n" "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; } && test -s "conftest$ac_exeext" 2>/dev/null; then (./conftest; exit; ) >&5 2>/dev/null lt_status=$? case x$lt_status in x$lt_dlno_uscore) lt_cv_dlopen_self_static=yes ;; x$lt_dlneed_uscore) lt_cv_dlopen_self_static=yes ;; x$lt_dlunknown|x*) lt_cv_dlopen_self_static=no ;; esac else : # compilation failed lt_cv_dlopen_self_static=no fi fi rm -fr conftest* ;; esac fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $lt_cv_dlopen_self_static" >&5 printf "%s\n" "$lt_cv_dlopen_self_static" >&6; } fi CPPFLAGS=$save_CPPFLAGS LDFLAGS=$save_LDFLAGS LIBS=$save_LIBS ;; esac case $lt_cv_dlopen_self in yes|no) enable_dlopen_self=$lt_cv_dlopen_self ;; *) enable_dlopen_self=unknown ;; esac case $lt_cv_dlopen_self_static in yes|no) enable_dlopen_self_static=$lt_cv_dlopen_self_static ;; *) enable_dlopen_self_static=unknown ;; esac fi striplib= old_striplib= { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking whether stripping libraries is possible" >&5 printf %s "checking whether stripping libraries is possible... " >&6; } if test -z "$STRIP"; then { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 printf "%s\n" "no" >&6; } else if $STRIP -V 2>&1 | $GREP "GNU strip" >/dev/null; then old_striplib="$STRIP --strip-debug" striplib="$STRIP --strip-unneeded" { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: yes" >&5 printf "%s\n" "yes" >&6; } else case $host_os in darwin*) # FIXME - insert some real tests, host_os isn't really good enough striplib="$STRIP -x" old_striplib="$STRIP -S" { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: yes" >&5 printf "%s\n" "yes" >&6; } ;; freebsd*) if $STRIP -V 2>&1 | $GREP "elftoolchain" >/dev/null; then old_striplib="$STRIP --strip-debug" striplib="$STRIP --strip-unneeded" { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: yes" >&5 printf "%s\n" "yes" >&6; } else { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 printf "%s\n" "no" >&6; } fi ;; *) { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 printf "%s\n" "no" >&6; } ;; esac fi fi # Report what library types will actually be built { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking if libtool supports shared libraries" >&5 printf %s "checking if libtool supports shared libraries... " >&6; } { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $can_build_shared" >&5 printf "%s\n" "$can_build_shared" >&6; } { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking whether to build shared libraries" >&5 printf %s "checking whether to build shared libraries... " >&6; } test no = "$can_build_shared" && enable_shared=no # On AIX, shared libraries and static libraries use the same namespace, and # are all built from PIC. case $host_os in aix3*) test yes = "$enable_shared" && enable_static=no if test -n "$RANLIB"; then archive_cmds="$archive_cmds~\$RANLIB \$lib" postinstall_cmds='$RANLIB $lib' fi ;; aix[4-9]*) if test ia64 != "$host_cpu"; then case $enable_shared,$with_aix_soname,$aix_use_runtimelinking in yes,aix,yes) ;; # shared object as lib.so file only yes,svr4,*) ;; # shared object as lib.so archive member only yes,*) enable_static=no ;; # shared object in lib.a archive as well esac fi ;; esac { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $enable_shared" >&5 printf "%s\n" "$enable_shared" >&6; } { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking whether to build static libraries" >&5 printf %s "checking whether to build static libraries... " >&6; } # Make sure either enable_shared or enable_static is yes. test yes = "$enable_shared" || enable_static=yes { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $enable_static" >&5 printf "%s\n" "$enable_static" >&6; } fi ac_ext=c ac_cpp='$CPP $CPPFLAGS' ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' ac_compiler_gnu=$ac_cv_c_compiler_gnu CC=$lt_save_CC ac_config_commands="$ac_config_commands libtool" # Only expand once: if (test "$USE_MAINTAINER_MODE" = "yes"); then # Extract the first word of "lcov", so it can be a program name with args. set dummy lcov; ac_word=$2 { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 printf %s "checking for $ac_word... " >&6; } if test ${ac_cv_prog_enable_coverage+y} then : printf %s "(cached) " >&6 else case e in #( e) if test -n "$enable_coverage"; then ac_cv_prog_enable_coverage="$enable_coverage" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS case $as_dir in #((( '') as_dir=./ ;; */) ;; *) as_dir=$as_dir/ ;; esac for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir$ac_word$ac_exec_ext"; then ac_cv_prog_enable_coverage="yes" printf "%s\n" "$as_me:${as_lineno-$LINENO}: found $as_dir$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS test -z "$ac_cv_prog_enable_coverage" && ac_cv_prog_enable_coverage="no" fi ;; esac fi enable_coverage=$ac_cv_prog_enable_coverage if test -n "$enable_coverage"; then { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $enable_coverage" >&5 printf "%s\n" "$enable_coverage" >&6; } else { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 printf "%s\n" "no" >&6; } fi # Extract the first word of "dbus-run-session", so it can be a program name with args. set dummy dbus-run-session; ac_word=$2 { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 printf %s "checking for $ac_word... " >&6; } if test ${ac_cv_prog_enable_dbus_run_session+y} then : printf %s "(cached) " >&6 else case e in #( e) if test -n "$enable_dbus_run_session"; then ac_cv_prog_enable_dbus_run_session="$enable_dbus_run_session" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS case $as_dir in #((( '') as_dir=./ ;; */) ;; *) as_dir=$as_dir/ ;; esac for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir$ac_word$ac_exec_ext"; then ac_cv_prog_enable_dbus_run_session="yes" printf "%s\n" "$as_me:${as_lineno-$LINENO}: found $as_dir$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi ;; esac fi enable_dbus_run_session=$ac_cv_prog_enable_dbus_run_session if test -n "$enable_dbus_run_session"; then { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $enable_dbus_run_session" >&5 printf "%s\n" "$enable_dbus_run_session" >&6; } else { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 printf "%s\n" "no" >&6; } fi # Extract the first word of "valgrind", so it can be a program name with args. set dummy valgrind; ac_word=$2 { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 printf %s "checking for $ac_word... " >&6; } if test ${ac_cv_prog_enable_valgrind+y} then : printf %s "(cached) " >&6 else case e in #( e) if test -n "$enable_valgrind"; then ac_cv_prog_enable_valgrind="$enable_valgrind" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS case $as_dir in #((( '') as_dir=./ ;; */) ;; *) as_dir=$as_dir/ ;; esac for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir$ac_word$ac_exec_ext"; then ac_cv_prog_enable_valgrind="yes" printf "%s\n" "$as_me:${as_lineno-$LINENO}: found $as_dir$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi ;; esac fi enable_valgrind=$ac_cv_prog_enable_valgrind if test -n "$enable_valgrind"; then { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $enable_valgrind" >&5 printf "%s\n" "$enable_valgrind" >&6; } else { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 printf "%s\n" "no" >&6; } fi ac_fn_c_check_header_compile "$LINENO" "valgrind/memcheck.h" "ac_cv_header_valgrind_memcheck_h" "$ac_includes_default" if test "x$ac_cv_header_valgrind_memcheck_h" = xyes then : printf "%s\n" "#define HAVE_VALGRIND_MEMCHECK_H 1" >>confdefs.h fi fi if test "${enable_coverage}" = "yes"; then COVERAGE_TRUE= COVERAGE_FALSE='#' else COVERAGE_TRUE='#' COVERAGE_FALSE= fi if test "${enable_dbus_run_session}" = "yes"; then DBUS_RUN_SESSION_TRUE= DBUS_RUN_SESSION_FALSE='#' else DBUS_RUN_SESSION_TRUE='#' DBUS_RUN_SESSION_FALSE= fi misc_cflags="" misc_ldflags="" # Check whether --enable-optimization was given. if test ${enable_optimization+y} then : enableval=$enable_optimization; if (test "${enableval}" = "no"); then misc_cflags="$misc_cflags -O0" fi fi # Check whether --enable-asan was given. if test ${enable_asan+y} then : enableval=$enable_asan; save_LIBS=$LIBS { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for _init in -lasan" >&5 printf %s "checking for _init in -lasan... " >&6; } if test ${ac_cv_lib_asan__init+y} then : printf %s "(cached) " >&6 else case e in #( e) ac_check_lib_save_LIBS=$LIBS LIBS="-lasan $LIBS" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ /* Override any GCC internal prototype to avoid an error. Use char because int might match the return type of a GCC builtin and then its argument prototype would still apply. The 'extern "C"' is for builds by C++ compilers; although this is not generally supported in C code supporting it here has little cost and some practical benefit (sr 110532). */ #ifdef __cplusplus extern "C" #endif char _init (void); int main (void) { return _init (); ; return 0; } _ACEOF if ac_fn_c_try_link "$LINENO" then : ac_cv_lib_asan__init=yes else case e in #( e) ac_cv_lib_asan__init=no ;; esac fi rm -f core conftest.err conftest.$ac_objext conftest.beam \ conftest$ac_exeext conftest.$ac_ext LIBS=$ac_check_lib_save_LIBS ;; esac fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_asan__init" >&5 printf "%s\n" "$ac_cv_lib_asan__init" >&6; } if test "x$ac_cv_lib_asan__init" = xyes then : printf "%s\n" "#define HAVE_LIBASAN 1" >>confdefs.h LIBS="-lasan $LIBS" fi LIBS=$save_LIBS if (test "${enableval}" = "yes" && test "${ac_cv_lib_asan__init}" = "yes" && test "${ac_cv_prog_cc_asan}" = "yes"); then misc_cflags="$misc_cflags -fsanitize=address"; misc_ldflags="$misc_ldflags -fsanitize=address" ASAN_LIB=${ac_cv_lib_asan__init} fi fi # Check whether --enable-lsan was given. if test ${enable_lsan+y} then : enableval=$enable_lsan; save_LIBS=$LIBS { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for _init in -llsan" >&5 printf %s "checking for _init in -llsan... " >&6; } if test ${ac_cv_lib_lsan__init+y} then : printf %s "(cached) " >&6 else case e in #( e) ac_check_lib_save_LIBS=$LIBS LIBS="-llsan $LIBS" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ /* Override any GCC internal prototype to avoid an error. Use char because int might match the return type of a GCC builtin and then its argument prototype would still apply. The 'extern "C"' is for builds by C++ compilers; although this is not generally supported in C code supporting it here has little cost and some practical benefit (sr 110532). */ #ifdef __cplusplus extern "C" #endif char _init (void); int main (void) { return _init (); ; return 0; } _ACEOF if ac_fn_c_try_link "$LINENO" then : ac_cv_lib_lsan__init=yes else case e in #( e) ac_cv_lib_lsan__init=no ;; esac fi rm -f core conftest.err conftest.$ac_objext conftest.beam \ conftest$ac_exeext conftest.$ac_ext LIBS=$ac_check_lib_save_LIBS ;; esac fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_lsan__init" >&5 printf "%s\n" "$ac_cv_lib_lsan__init" >&6; } if test "x$ac_cv_lib_lsan__init" = xyes then : printf "%s\n" "#define HAVE_LIBLSAN 1" >>confdefs.h LIBS="-llsan $LIBS" fi LIBS=$save_LIBS if (test "${enableval}" = "yes" && test "${ac_cv_lib_lsan__init}" = "yes" && test "${ac_cv_prog_cc_lsan}" = "yes"); then misc_cflags="$misc_cflags -fsanitize=leak"; misc_ldflags="$misc_ldflags -fsanitize=leak" ASAN_LIB=${ac_cv_lib_lsan__init} fi fi # Check whether --enable-ubsan was given. if test ${enable_ubsan+y} then : enableval=$enable_ubsan; save_LIBS=$LIBS { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for _init in -lubsan" >&5 printf %s "checking for _init in -lubsan... " >&6; } if test ${ac_cv_lib_ubsan__init+y} then : printf %s "(cached) " >&6 else case e in #( e) ac_check_lib_save_LIBS=$LIBS LIBS="-lubsan $LIBS" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ /* Override any GCC internal prototype to avoid an error. Use char because int might match the return type of a GCC builtin and then its argument prototype would still apply. The 'extern "C"' is for builds by C++ compilers; although this is not generally supported in C code supporting it here has little cost and some practical benefit (sr 110532). */ #ifdef __cplusplus extern "C" #endif char _init (void); int main (void) { return _init (); ; return 0; } _ACEOF if ac_fn_c_try_link "$LINENO" then : ac_cv_lib_ubsan__init=yes else case e in #( e) ac_cv_lib_ubsan__init=no ;; esac fi rm -f core conftest.err conftest.$ac_objext conftest.beam \ conftest$ac_exeext conftest.$ac_ext LIBS=$ac_check_lib_save_LIBS ;; esac fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_ubsan__init" >&5 printf "%s\n" "$ac_cv_lib_ubsan__init" >&6; } if test "x$ac_cv_lib_ubsan__init" = xyes then : printf "%s\n" "#define HAVE_LIBUBSAN 1" >>confdefs.h LIBS="-lubsan $LIBS" fi LIBS=$save_LIBS if (test "${enableval}" = "yes" && test "${ac_cv_lib_ubsan__init}" = "yes" && test "${ac_cv_prog_cc_ubsan}" = "yes"); then misc_cflags="$misc_cflags -fsanitize=undefined"; misc_ldflags="$misc_ldflags -fsanitize=undefined"; fi fi # Check whether --enable-debug was given. if test ${enable_debug+y} then : enableval=$enable_debug; if (test "${enableval}" = "yes" && test "${ac_cv_prog_cc_g}" = "yes"); then misc_cflags="$misc_cflags -g" fi fi # Check whether --enable-pie was given. if test ${enable_pie+y} then : enableval=$enable_pie; if (test "${enableval}" = "yes" && test "${ac_cv_prog_cc_pie}" = "yes"); then misc_cflags="$misc_cflags -fPIC" misc_ldflags="$misc_ldflags -pie -Wl,-z,now" fi fi if (test "$enable_coverage" = "yes"); then misc_cflags="$misc_cflags --coverage" misc_ldflags="$misc_ldflags --coverage" fi if (test "$USE_MAINTAINER_MODE" = "yes"); then { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking whether ${CC-cc} accepts -D_FORTIFY_SOURCE=3" >&5 printf %s "checking whether ${CC-cc} accepts -D_FORTIFY_SOURCE=3... " >&6; } if test ${ac_cv_prog_cc_fortify_source_3+y} then : printf %s "(cached) " >&6 else case e in #( e) echo '#include ' > fortify.c if test -z "`${CC-cc} ${CFLAGS} ${misc_cflags} -U_FORTIFY_SOURCE -D_FORTIFY_SOURCE=3 -c fortify.c 2>&1`"; then ac_cv_prog_cc_fortify_source_3=yes else ac_cv_prog_cc_fortify_source_3=no fi rm -f fortify.c fortify.o ;; esac fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_prog_cc_fortify_source_3" >&5 printf "%s\n" "$ac_cv_prog_cc_fortify_source_3" >&6; } if test "${ac_cv_prog_cc_fortify_source_3}" = "yes"; then misc_cflags="$misc_cflags -U_FORTIFY_SOURCE -D_FORTIFY_SOURCE=3" fi fi misc_cflags="$misc_cflags -ffunction-sections -fdata-sections" misc_ldflags="$misc_ldflags -Wl,--gc-sections" MISC_CFLAGS=$misc_cflags MISC_LDFLAGS=$misc_ldflags if test "${enable_valgrind}" = "yes" && test "$ASAN_LIB" != "yes" && test "LSAN_LIB" != "yes"; then VALGRIND_TRUE= VALGRIND_FALSE='#' else VALGRIND_TRUE='#' VALGRIND_FALSE= fi # Check whether --enable-threads was given. if test ${enable_threads+y} then : enableval=$enable_threads; enable_threads=${enableval} fi ac_fn_c_check_func "$LINENO" "explicit_bzero" "ac_cv_func_explicit_bzero" if test "x$ac_cv_func_explicit_bzero" = xyes then : printf "%s\n" "#define HAVE_EXPLICIT_BZERO 1" >>confdefs.h fi ac_fn_c_check_func "$LINENO" "getrandom" "ac_cv_func_getrandom" if test "x$ac_cv_func_getrandom" = xyes then : printf "%s\n" "#define HAVE_GETRANDOM 1" >>confdefs.h fi ac_fn_c_check_func "$LINENO" "rawmemchr" "ac_cv_func_rawmemchr" if test "x$ac_cv_func_rawmemchr" = xyes then : printf "%s\n" "#define HAVE_RAWMEMCHR 1" >>confdefs.h fi ac_fn_c_check_func "$LINENO" "signalfd" "ac_cv_func_signalfd" if test "x$ac_cv_func_signalfd" = xyes then : dummy=yes else case e in #( e) as_fn_error $? "signalfd support is required" "$LINENO" 5 ;; esac fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for clock_gettime in -lrt" >&5 printf %s "checking for clock_gettime in -lrt... " >&6; } if test ${ac_cv_lib_rt_clock_gettime+y} then : printf %s "(cached) " >&6 else case e in #( e) ac_check_lib_save_LIBS=$LIBS LIBS="-lrt $LIBS" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ /* Override any GCC internal prototype to avoid an error. Use char because int might match the return type of a GCC builtin and then its argument prototype would still apply. The 'extern "C"' is for builds by C++ compilers; although this is not generally supported in C code supporting it here has little cost and some practical benefit (sr 110532). */ #ifdef __cplusplus extern "C" #endif char clock_gettime (void); int main (void) { return clock_gettime (); ; return 0; } _ACEOF if ac_fn_c_try_link "$LINENO" then : ac_cv_lib_rt_clock_gettime=yes else case e in #( e) ac_cv_lib_rt_clock_gettime=no ;; esac fi rm -f core conftest.err conftest.$ac_objext conftest.beam \ conftest$ac_exeext conftest.$ac_ext LIBS=$ac_check_lib_save_LIBS ;; esac fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_rt_clock_gettime" >&5 printf "%s\n" "$ac_cv_lib_rt_clock_gettime" >&6; } if test "x$ac_cv_lib_rt_clock_gettime" = xyes then : dummy=yes else case e in #( e) as_fn_error $? "realtime clock support is required" "$LINENO" 5 ;; esac fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for pthread_create in -lpthread" >&5 printf %s "checking for pthread_create in -lpthread... " >&6; } if test ${ac_cv_lib_pthread_pthread_create+y} then : printf %s "(cached) " >&6 else case e in #( e) ac_check_lib_save_LIBS=$LIBS LIBS="-lpthread $LIBS" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ /* Override any GCC internal prototype to avoid an error. Use char because int might match the return type of a GCC builtin and then its argument prototype would still apply. The 'extern "C"' is for builds by C++ compilers; although this is not generally supported in C code supporting it here has little cost and some practical benefit (sr 110532). */ #ifdef __cplusplus extern "C" #endif char pthread_create (void); int main (void) { return pthread_create (); ; return 0; } _ACEOF if ac_fn_c_try_link "$LINENO" then : ac_cv_lib_pthread_pthread_create=yes else case e in #( e) ac_cv_lib_pthread_pthread_create=no ;; esac fi rm -f core conftest.err conftest.$ac_objext conftest.beam \ conftest$ac_exeext conftest.$ac_ext LIBS=$ac_check_lib_save_LIBS ;; esac fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_pthread_pthread_create" >&5 printf "%s\n" "$ac_cv_lib_pthread_pthread_create" >&6; } if test "x$ac_cv_lib_pthread_pthread_create" = xyes then : dummy=yes else case e in #( e) as_fn_error $? "posix thread support is required" "$LINENO" 5 ;; esac fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for dlopen in -ldl" >&5 printf %s "checking for dlopen in -ldl... " >&6; } if test ${ac_cv_lib_dl_dlopen+y} then : printf %s "(cached) " >&6 else case e in #( e) ac_check_lib_save_LIBS=$LIBS LIBS="-ldl $LIBS" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ /* Override any GCC internal prototype to avoid an error. Use char because int might match the return type of a GCC builtin and then its argument prototype would still apply. The 'extern "C"' is for builds by C++ compilers; although this is not generally supported in C code supporting it here has little cost and some practical benefit (sr 110532). */ #ifdef __cplusplus extern "C" #endif char dlopen (void); int main (void) { return dlopen (); ; return 0; } _ACEOF if ac_fn_c_try_link "$LINENO" then : ac_cv_lib_dl_dlopen=yes else case e in #( e) ac_cv_lib_dl_dlopen=no ;; esac fi rm -f core conftest.err conftest.$ac_objext conftest.beam \ conftest$ac_exeext conftest.$ac_ext LIBS=$ac_check_lib_save_LIBS ;; esac fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_dl_dlopen" >&5 printf "%s\n" "$ac_cv_lib_dl_dlopen" >&6; } if test "x$ac_cv_lib_dl_dlopen" = xyes then : dummy=yes else case e in #( e) as_fn_error $? "dynamic linking loader is required" "$LINENO" 5 ;; esac fi ac_fn_c_check_header_compile "$LINENO" "stdio.h" "ac_cv_header_stdio_h" "$ac_includes_default" if test "x$ac_cv_header_stdio_h" = xyes then : printf "%s\n" "#define HAVE_STDIO_H 1" >>confdefs.h fi ac_fn_c_check_header_compile "$LINENO" "string.h" "ac_cv_header_string_h" "$ac_includes_default" if test "x$ac_cv_header_string_h" = xyes then : printf "%s\n" "#define HAVE_STRING_H 1" >>confdefs.h fi ac_fn_c_check_header_compile "$LINENO" "linux/types.h" "ac_cv_header_linux_types_h" "$ac_includes_default" if test "x$ac_cv_header_linux_types_h" = xyes then : printf "%s\n" "#define HAVE_LINUX_TYPES_H 1" >>confdefs.h fi ac_fn_c_check_header_compile "$LINENO" "linux/if_alg.h" "ac_cv_header_linux_if_alg_h" "$ac_includes_default" if test "x$ac_cv_header_linux_if_alg_h" = xyes then : printf "%s\n" "#define HAVE_LINUX_IF_ALG_H 1" >>confdefs.h fi ac_fn_c_check_header_compile "$LINENO" "linux/uinput.h" "ac_cv_header_linux_uinput_h" "$ac_includes_default" if test "x$ac_cv_header_linux_uinput_h" = xyes then : printf "%s\n" "#define HAVE_LINUX_UINPUT_H 1" >>confdefs.h fi ac_fn_c_check_header_compile "$LINENO" "linux/uhid.h" "ac_cv_header_linux_uhid_h" "$ac_includes_default" if test "x$ac_cv_header_linux_uhid_h" = xyes then : printf "%s\n" "#define HAVE_LINUX_UHID_H 1" >>confdefs.h fi ac_fn_c_check_header_compile "$LINENO" "sys/random.h" "ac_cv_header_sys_random_h" "$ac_includes_default" if test "x$ac_cv_header_sys_random_h" = xyes then : printf "%s\n" "#define HAVE_SYS_RANDOM_H 1" >>confdefs.h fi # basename may be only available in libgen.h with the POSIX behavior, # not desired here { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $CC options needed to detect all undeclared functions" >&5 printf %s "checking for $CC options needed to detect all undeclared functions... " >&6; } if test ${ac_cv_c_undeclared_builtin_options+y} then : printf %s "(cached) " >&6 else case e in #( e) ac_save_CFLAGS=$CFLAGS ac_cv_c_undeclared_builtin_options='cannot detect' for ac_arg in '' -fno-builtin; do CFLAGS="$ac_save_CFLAGS $ac_arg" # This test program should *not* compile successfully. cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ int main (void) { (void) strchr; ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO" then : else case e in #( e) # This test program should compile successfully. # No library function is consistently available on # freestanding implementations, so test against a dummy # declaration. Include always-available headers on the # off chance that they somehow elicit warnings. cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include #include #include #include extern void ac_decl (int, char *); int main (void) { (void) ac_decl (0, (char *) 0); (void) ac_decl; ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO" then : if test x"$ac_arg" = x then : ac_cv_c_undeclared_builtin_options='none needed' else case e in #( e) ac_cv_c_undeclared_builtin_options=$ac_arg ;; esac fi break fi rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext ;; esac fi rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext done CFLAGS=$ac_save_CFLAGS ;; esac fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_c_undeclared_builtin_options" >&5 printf "%s\n" "$ac_cv_c_undeclared_builtin_options" >&6; } case $ac_cv_c_undeclared_builtin_options in #( 'cannot detect') : { { printf "%s\n" "$as_me:${as_lineno-$LINENO}: error: in '$ac_pwd':" >&5 printf "%s\n" "$as_me: error: in '$ac_pwd':" >&2;} as_fn_error $? "cannot make $CC report undeclared builtins See 'config.log' for more details" "$LINENO" 5; } ;; #( 'none needed') : ac_c_undeclared_builtin_options='' ;; #( *) : ac_c_undeclared_builtin_options=$ac_cv_c_undeclared_builtin_options ;; esac ac_fn_check_decl "$LINENO" "basename" "ac_cv_have_decl_basename" "#define _GNU_SOURCE 1 #include " "$ac_c_undeclared_builtin_options" "CFLAGS" if test "x$ac_cv_have_decl_basename" = xyes then : ac_have_decl=1 else case e in #( e) ac_have_decl=0 ;; esac fi printf "%s\n" "#define HAVE_DECL_BASENAME $ac_have_decl" >>confdefs.h if test $ac_have_decl = 1 then : else case e in #( e) { printf "%s\n" "$as_me:${as_lineno-$LINENO}: WARNING: GNU basename extension not found" >&5 printf "%s\n" "$as_me: WARNING: GNU basename extension not found" >&2;} ;; esac fi pkg_failed=no { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for glib-2.0 >= 2.28" >&5 printf %s "checking for glib-2.0 >= 2.28... " >&6; } if test -n "$GLIB_CFLAGS"; then pkg_cv_GLIB_CFLAGS="$GLIB_CFLAGS" elif test -n "$PKG_CONFIG"; then if test -n "$PKG_CONFIG" && \ { { printf "%s\n" "$as_me:${as_lineno-$LINENO}: \$PKG_CONFIG --exists --print-errors \"glib-2.0 >= 2.28\""; } >&5 ($PKG_CONFIG --exists --print-errors "glib-2.0 >= 2.28") 2>&5 ac_status=$? printf "%s\n" "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; }; then pkg_cv_GLIB_CFLAGS=`$PKG_CONFIG --cflags "glib-2.0 >= 2.28" 2>/dev/null` test "x$?" != "x0" && pkg_failed=yes else pkg_failed=yes fi else pkg_failed=untried fi if test -n "$GLIB_LIBS"; then pkg_cv_GLIB_LIBS="$GLIB_LIBS" elif test -n "$PKG_CONFIG"; then if test -n "$PKG_CONFIG" && \ { { printf "%s\n" "$as_me:${as_lineno-$LINENO}: \$PKG_CONFIG --exists --print-errors \"glib-2.0 >= 2.28\""; } >&5 ($PKG_CONFIG --exists --print-errors "glib-2.0 >= 2.28") 2>&5 ac_status=$? printf "%s\n" "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; }; then pkg_cv_GLIB_LIBS=`$PKG_CONFIG --libs "glib-2.0 >= 2.28" 2>/dev/null` test "x$?" != "x0" && pkg_failed=yes else pkg_failed=yes fi else pkg_failed=untried fi if test $pkg_failed = yes; then { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 printf "%s\n" "no" >&6; } if $PKG_CONFIG --atleast-pkgconfig-version 0.20; then _pkg_short_errors_supported=yes else _pkg_short_errors_supported=no fi if test $_pkg_short_errors_supported = yes; then GLIB_PKG_ERRORS=`$PKG_CONFIG --short-errors --print-errors --cflags --libs "glib-2.0 >= 2.28" 2>&1` else GLIB_PKG_ERRORS=`$PKG_CONFIG --print-errors --cflags --libs "glib-2.0 >= 2.28" 2>&1` fi # Put the nasty error message in config.log where it belongs echo "$GLIB_PKG_ERRORS" >&5 as_fn_error $? "Package requirements (glib-2.0 >= 2.28) were not met: $GLIB_PKG_ERRORS Consider adjusting the PKG_CONFIG_PATH environment variable if you installed software in a non-standard prefix. Alternatively, you may set the environment variables GLIB_CFLAGS and GLIB_LIBS to avoid the need to call pkg-config. See the pkg-config man page for more details." "$LINENO" 5 elif test $pkg_failed = untried; then { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 printf "%s\n" "no" >&6; } { { printf "%s\n" "$as_me:${as_lineno-$LINENO}: error: in '$ac_pwd':" >&5 printf "%s\n" "$as_me: error: in '$ac_pwd':" >&2;} as_fn_error $? "The pkg-config script could not be found or is too old. Make sure it is in your PATH or set the PKG_CONFIG environment variable to the full path to pkg-config. Alternatively, you may set the environment variables GLIB_CFLAGS and GLIB_LIBS to avoid the need to call pkg-config. See the pkg-config man page for more details. To get pkg-config, see . See 'config.log' for more details" "$LINENO" 5; } else GLIB_CFLAGS=$pkg_cv_GLIB_CFLAGS GLIB_LIBS=$pkg_cv_GLIB_LIBS { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: yes" >&5 printf "%s\n" "yes" >&6; } fi if (test "${enable_threads}" = "yes"); then printf "%s\n" "#define NEED_THREADS 1" >>confdefs.h pkg_failed=no { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for gthread-2.0 >= 2.16" >&5 printf %s "checking for gthread-2.0 >= 2.16... " >&6; } if test -n "$GTHREAD_CFLAGS"; then pkg_cv_GTHREAD_CFLAGS="$GTHREAD_CFLAGS" elif test -n "$PKG_CONFIG"; then if test -n "$PKG_CONFIG" && \ { { printf "%s\n" "$as_me:${as_lineno-$LINENO}: \$PKG_CONFIG --exists --print-errors \"gthread-2.0 >= 2.16\""; } >&5 ($PKG_CONFIG --exists --print-errors "gthread-2.0 >= 2.16") 2>&5 ac_status=$? printf "%s\n" "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; }; then pkg_cv_GTHREAD_CFLAGS=`$PKG_CONFIG --cflags "gthread-2.0 >= 2.16" 2>/dev/null` test "x$?" != "x0" && pkg_failed=yes else pkg_failed=yes fi else pkg_failed=untried fi if test -n "$GTHREAD_LIBS"; then pkg_cv_GTHREAD_LIBS="$GTHREAD_LIBS" elif test -n "$PKG_CONFIG"; then if test -n "$PKG_CONFIG" && \ { { printf "%s\n" "$as_me:${as_lineno-$LINENO}: \$PKG_CONFIG --exists --print-errors \"gthread-2.0 >= 2.16\""; } >&5 ($PKG_CONFIG --exists --print-errors "gthread-2.0 >= 2.16") 2>&5 ac_status=$? printf "%s\n" "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; }; then pkg_cv_GTHREAD_LIBS=`$PKG_CONFIG --libs "gthread-2.0 >= 2.16" 2>/dev/null` test "x$?" != "x0" && pkg_failed=yes else pkg_failed=yes fi else pkg_failed=untried fi if test $pkg_failed = yes; then { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 printf "%s\n" "no" >&6; } if $PKG_CONFIG --atleast-pkgconfig-version 0.20; then _pkg_short_errors_supported=yes else _pkg_short_errors_supported=no fi if test $_pkg_short_errors_supported = yes; then GTHREAD_PKG_ERRORS=`$PKG_CONFIG --short-errors --print-errors --cflags --libs "gthread-2.0 >= 2.16" 2>&1` else GTHREAD_PKG_ERRORS=`$PKG_CONFIG --print-errors --cflags --libs "gthread-2.0 >= 2.16" 2>&1` fi # Put the nasty error message in config.log where it belongs echo "$GTHREAD_PKG_ERRORS" >&5 as_fn_error $? "Package requirements (gthread-2.0 >= 2.16) were not met: $GTHREAD_PKG_ERRORS Consider adjusting the PKG_CONFIG_PATH environment variable if you installed software in a non-standard prefix. Alternatively, you may set the environment variables GTHREAD_CFLAGS and GTHREAD_LIBS to avoid the need to call pkg-config. See the pkg-config man page for more details." "$LINENO" 5 elif test $pkg_failed = untried; then { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 printf "%s\n" "no" >&6; } { { printf "%s\n" "$as_me:${as_lineno-$LINENO}: error: in '$ac_pwd':" >&5 printf "%s\n" "$as_me: error: in '$ac_pwd':" >&2;} as_fn_error $? "The pkg-config script could not be found or is too old. Make sure it is in your PATH or set the PKG_CONFIG environment variable to the full path to pkg-config. Alternatively, you may set the environment variables GTHREAD_CFLAGS and GTHREAD_LIBS to avoid the need to call pkg-config. See the pkg-config man page for more details. To get pkg-config, see . See 'config.log' for more details" "$LINENO" 5; } else GTHREAD_CFLAGS=$pkg_cv_GTHREAD_CFLAGS GTHREAD_LIBS=$pkg_cv_GTHREAD_LIBS { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: yes" >&5 printf "%s\n" "yes" >&6; } fi GLIB_CFLAGS="$GLIB_CFLAGS $GTHREAD_CFLAGS" GLIB_LIBS="$GLIB_LIBS $GTHREAD_LIBS" fi pkg_failed=no { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for dbus-1 >= 1.10" >&5 printf %s "checking for dbus-1 >= 1.10... " >&6; } if test -n "$DBUS_CFLAGS"; then pkg_cv_DBUS_CFLAGS="$DBUS_CFLAGS" elif test -n "$PKG_CONFIG"; then if test -n "$PKG_CONFIG" && \ { { printf "%s\n" "$as_me:${as_lineno-$LINENO}: \$PKG_CONFIG --exists --print-errors \"dbus-1 >= 1.10\""; } >&5 ($PKG_CONFIG --exists --print-errors "dbus-1 >= 1.10") 2>&5 ac_status=$? printf "%s\n" "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; }; then pkg_cv_DBUS_CFLAGS=`$PKG_CONFIG --cflags "dbus-1 >= 1.10" 2>/dev/null` test "x$?" != "x0" && pkg_failed=yes else pkg_failed=yes fi else pkg_failed=untried fi if test -n "$DBUS_LIBS"; then pkg_cv_DBUS_LIBS="$DBUS_LIBS" elif test -n "$PKG_CONFIG"; then if test -n "$PKG_CONFIG" && \ { { printf "%s\n" "$as_me:${as_lineno-$LINENO}: \$PKG_CONFIG --exists --print-errors \"dbus-1 >= 1.10\""; } >&5 ($PKG_CONFIG --exists --print-errors "dbus-1 >= 1.10") 2>&5 ac_status=$? printf "%s\n" "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; }; then pkg_cv_DBUS_LIBS=`$PKG_CONFIG --libs "dbus-1 >= 1.10" 2>/dev/null` test "x$?" != "x0" && pkg_failed=yes else pkg_failed=yes fi else pkg_failed=untried fi if test $pkg_failed = yes; then { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 printf "%s\n" "no" >&6; } if $PKG_CONFIG --atleast-pkgconfig-version 0.20; then _pkg_short_errors_supported=yes else _pkg_short_errors_supported=no fi if test $_pkg_short_errors_supported = yes; then DBUS_PKG_ERRORS=`$PKG_CONFIG --short-errors --print-errors --cflags --libs "dbus-1 >= 1.10" 2>&1` else DBUS_PKG_ERRORS=`$PKG_CONFIG --print-errors --cflags --libs "dbus-1 >= 1.10" 2>&1` fi # Put the nasty error message in config.log where it belongs echo "$DBUS_PKG_ERRORS" >&5 as_fn_error $? "Package requirements (dbus-1 >= 1.10) were not met: $DBUS_PKG_ERRORS Consider adjusting the PKG_CONFIG_PATH environment variable if you installed software in a non-standard prefix. Alternatively, you may set the environment variables DBUS_CFLAGS and DBUS_LIBS to avoid the need to call pkg-config. See the pkg-config man page for more details." "$LINENO" 5 elif test $pkg_failed = untried; then { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 printf "%s\n" "no" >&6; } { { printf "%s\n" "$as_me:${as_lineno-$LINENO}: error: in '$ac_pwd':" >&5 printf "%s\n" "$as_me: error: in '$ac_pwd':" >&2;} as_fn_error $? "The pkg-config script could not be found or is too old. Make sure it is in your PATH or set the PKG_CONFIG environment variable to the full path to pkg-config. Alternatively, you may set the environment variables DBUS_CFLAGS and DBUS_LIBS to avoid the need to call pkg-config. See the pkg-config man page for more details. To get pkg-config, see . See 'config.log' for more details" "$LINENO" 5; } else DBUS_CFLAGS=$pkg_cv_DBUS_CFLAGS DBUS_LIBS=$pkg_cv_DBUS_LIBS { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: yes" >&5 printf "%s\n" "yes" >&6; } fi # Check whether --with-dbusconfdir was given. if test ${with_dbusconfdir+y} then : withval=$with_dbusconfdir; path_dbusconfdir=${withval} fi if (test -z "${path_dbusconfdir}"); then { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking D-Bus configuration directory" >&5 printf %s "checking D-Bus configuration directory... " >&6; } path_dbusconfdir="`$PKG_CONFIG --variable=datadir dbus-1`" if (test -z "${path_dbusconfdir}"); then as_fn_error $? "D-Bus configuration directory is required" "$LINENO" 5 fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: ${path_dbusconfdir}" >&5 printf "%s\n" "${path_dbusconfdir}" >&6; } fi DBUS_CONFDIR=${path_dbusconfdir} # Check whether --with-dbussystembusdir was given. if test ${with_dbussystembusdir+y} then : withval=$with_dbussystembusdir; path_dbussystembusdir=${withval} fi if (test -z "${path_dbussystembusdir}"); then { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking D-Bus system bus services dir" >&5 printf %s "checking D-Bus system bus services dir... " >&6; } path_dbussystembusdir="`$PKG_CONFIG --variable=system_bus_services_dir dbus-1`" if (test -z "${path_dbussystembusdir}"); then as_fn_error $? "D-Bus system bus services directory is required" "$LINENO" 5 fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: ${path_dbussystembusdir}" >&5 printf "%s\n" "${path_dbussystembusdir}" >&6; } fi DBUS_SYSTEMBUSDIR=${path_dbussystembusdir} # Check whether --with-dbussessionbusdir was given. if test ${with_dbussessionbusdir+y} then : withval=$with_dbussessionbusdir; path_dbussessionbusdir=${withval} fi if (test -z "${path_dbussessionbusdir}"); then { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking D-Bus session bus services dir" >&5 printf %s "checking D-Bus session bus services dir... " >&6; } path_dbussessionbusdir="`$PKG_CONFIG --variable=session_bus_services_dir dbus-1`" if (test -z "${path_dbussessionbusdir}"); then as_fn_error $? "D-Bus session bus services directory is required" "$LINENO" 5 fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: ${path_dbussessionbusdir}" >&5 printf "%s\n" "${path_dbussessionbusdir}" >&6; } fi DBUS_SESSIONBUSDIR=${path_dbussessionbusdir} # Check whether --with-zsh-completion-dir was given. if test ${with_zsh_completion_dir+y} then : withval=$with_zsh_completion_dir; path_zshcompletiondir=${withval} else case e in #( e) path_zshcompletiondir="yes" ;; esac fi if (test "${path_zshcompletiondir}" = "yes"); then path_zshcompletiondir="$datarootdir/zsh/site-functions" { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: ${path_zshcompletiondir}" >&5 printf "%s\n" "${path_zshcompletiondir}" >&6; } fi ZSH_COMPLETIONDIR=${path_zshcompletiondir} if test "${path_zshcompletiondir}" != "no"; then ZSH_COMPLETIONS_TRUE= ZSH_COMPLETIONS_FALSE='#' else ZSH_COMPLETIONS_TRUE='#' ZSH_COMPLETIONS_FALSE= fi # Check whether --enable-backtrace was given. if test ${enable_backtrace+y} then : enableval=$enable_backtrace; enable_backtrace=${enableval} fi if (test "${enable_backtrace}" = "yes"); then ac_fn_c_check_header_compile "$LINENO" "elfutils/libdwfl.h" "ac_cv_header_elfutils_libdwfl_h" "$ac_includes_default" if test "x$ac_cv_header_elfutils_libdwfl_h" = xyes then : dummy=yes else case e in #( e) as_fn_error $? "elfutils support is required" "$LINENO" 5 ;; esac fi printf "%s\n" "#define HAVE_BACKTRACE_SUPPORT 1" >>confdefs.h BACKTRACE_CFLAGS="" BACKTRACE_LIBS="-ldw" fi # Check whether --enable-library was given. if test ${enable_library+y} then : enableval=$enable_library; enable_library=${enableval} fi if test "${enable_library}" = "yes"; then LIBRARY_TRUE= LIBRARY_FALSE='#' else LIBRARY_TRUE='#' LIBRARY_FALSE= fi # Check whether --enable-test was given. if test ${enable_test+y} then : enableval=$enable_test; enable_test=${enableval} fi if test "${enable_test}" = "yes"; then TEST_TRUE= TEST_FALSE='#' else TEST_TRUE='#' TEST_FALSE= fi # Check whether --enable-nfc was given. if test ${enable_nfc+y} then : enableval=$enable_nfc; enable_nfc=${enableval} fi if test "${enable_nfc}" = "yes"; then NFC_TRUE= NFC_FALSE='#' else NFC_TRUE='#' NFC_FALSE= fi # Check whether --enable-sap was given. if test ${enable_sap+y} then : enableval=$enable_sap; enable_sap=${enableval} fi if test "${enable_sap}" = "yes"; then SAP_TRUE= SAP_FALSE='#' else SAP_TRUE='#' SAP_FALSE= fi # Check whether --enable-a2dp was given. if test ${enable_a2dp+y} then : enableval=$enable_a2dp; enable_a2dp=${enableval} fi if test "${enable_a2dp}" != "no"; then A2DP_TRUE= A2DP_FALSE='#' else A2DP_TRUE='#' A2DP_FALSE= fi if test "${enable_a2dp}" != "no"; then printf "%s\n" "#define HAVE_A2DP 1" >>confdefs.h fi # Check whether --enable-avrcp was given. if test ${enable_avrcp+y} then : enableval=$enable_avrcp; enable_avrcp=${enableval} fi if test "${enable_avrcp}" != "no"; then AVRCP_TRUE= AVRCP_FALSE='#' else AVRCP_TRUE='#' AVRCP_FALSE= fi if test "${enable_avrcp}" != "no"; then printf "%s\n" "#define HAVE_AVRCP 1" >>confdefs.h fi # Check whether --enable-network was given. if test ${enable_network+y} then : enableval=$enable_network; enable_network=${enableval} fi if test "${enable_network}" != "no"; then NETWORK_TRUE= NETWORK_FALSE='#' else NETWORK_TRUE='#' NETWORK_FALSE= fi # Check whether --enable-hid was given. if test ${enable_hid+y} then : enableval=$enable_hid; enable_hid=${enableval} fi if test "${enable_hid}" != "no"; then HID_TRUE= HID_FALSE='#' else HID_TRUE='#' HID_FALSE= fi # Check whether --enable-hog was given. if test ${enable_hog+y} then : enableval=$enable_hog; enable_hog=${enableval} fi if test "${enable_hog}" != "no"; then HOG_TRUE= HOG_FALSE='#' else HOG_TRUE='#' HOG_FALSE= fi # Check whether --enable-health was given. if test ${enable_health+y} then : enableval=$enable_health; enable_health=${enableval} fi if test "${enable_health}" = "yes"; then HEALTH_TRUE= HEALTH_FALSE='#' else HEALTH_TRUE='#' HEALTH_FALSE= fi # Check whether --enable-bap was given. if test ${enable_bap+y} then : enableval=$enable_bap; enable_bap=${enableval} fi if test "${enable_bap}" != "no"; then BAP_TRUE= BAP_FALSE='#' else BAP_TRUE='#' BAP_FALSE= fi # Check whether --enable-bass was given. if test ${enable_bass+y} then : enableval=$enable_bass; enable_bass=${enableval} fi if test "${enable_bass}" != "no"; then BASS_TRUE= BASS_FALSE='#' else BASS_TRUE='#' BASS_FALSE= fi # Check whether --enable-mcp was given. if test ${enable_mcp+y} then : enableval=$enable_mcp; enable_mcp=${enableval} fi if test "${enable_mcp}" != "no"; then MCP_TRUE= MCP_FALSE='#' else MCP_TRUE='#' MCP_FALSE= fi # Check whether --enable-ccp was given. if test ${enable_ccp+y} then : enableval=$enable_ccp; enable_ccp=${enableval} fi if test "${enable_ccp}" != "no"; then CCP_TRUE= CCP_FALSE='#' else CCP_TRUE='#' CCP_FALSE= fi # Check whether --enable-vcp was given. if test ${enable_vcp+y} then : enableval=$enable_vcp; enable_vcp=${enableval} fi if test "${enable_vcp}" != "no"; then VCP_TRUE= VCP_FALSE='#' else VCP_TRUE='#' VCP_FALSE= fi # Check whether --enable-micp was given. if test ${enable_micp+y} then : enableval=$enable_micp; enable_micp=${enableval} fi if test "${enable_micp}" != "no"; then MICP_TRUE= MICP_FALSE='#' else MICP_TRUE='#' MICP_FALSE= fi # Check whether --enable-csip was given. if test ${enable_csip+y} then : enableval=$enable_csip; enable_csip=${enableval} fi if test "${enable_csip}" != "no"; then CSIP_TRUE= CSIP_FALSE='#' else CSIP_TRUE='#' CSIP_FALSE= fi # Check whether --enable-asha was given. if test ${enable_asha+y} then : enableval=$enable_asha; enable_asha=${enableval} fi if test "${enable_asha}" != "no"; then ASHA_TRUE= ASHA_FALSE='#' else ASHA_TRUE='#' ASHA_FALSE= fi if test "${enable_asha}" != "no"; then printf "%s\n" "#define HAVE_ASHA 1" >>confdefs.h fi # Check whether --enable-tools was given. if test ${enable_tools+y} then : enableval=$enable_tools; enable_tools=${enableval} fi if test "${enable_tools}" != "no"; then TOOLS_TRUE= TOOLS_FALSE='#' else TOOLS_TRUE='#' TOOLS_FALSE= fi # Check whether --enable-monitor was given. if test ${enable_monitor+y} then : enableval=$enable_monitor; enable_monitor=${enableval} fi if test "${enable_monitor}" != "no"; then MONITOR_TRUE= MONITOR_FALSE='#' else MONITOR_TRUE='#' MONITOR_FALSE= fi # Check whether --enable-udev was given. if test ${enable_udev+y} then : enableval=$enable_udev; enable_udev=${enableval} fi if (test "${enable_udev}" != "no"); then pkg_failed=no { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for libudev >= 196" >&5 printf %s "checking for libudev >= 196... " >&6; } if test -n "$UDEV_CFLAGS"; then pkg_cv_UDEV_CFLAGS="$UDEV_CFLAGS" elif test -n "$PKG_CONFIG"; then if test -n "$PKG_CONFIG" && \ { { printf "%s\n" "$as_me:${as_lineno-$LINENO}: \$PKG_CONFIG --exists --print-errors \"libudev >= 196\""; } >&5 ($PKG_CONFIG --exists --print-errors "libudev >= 196") 2>&5 ac_status=$? printf "%s\n" "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; }; then pkg_cv_UDEV_CFLAGS=`$PKG_CONFIG --cflags "libudev >= 196" 2>/dev/null` test "x$?" != "x0" && pkg_failed=yes else pkg_failed=yes fi else pkg_failed=untried fi if test -n "$UDEV_LIBS"; then pkg_cv_UDEV_LIBS="$UDEV_LIBS" elif test -n "$PKG_CONFIG"; then if test -n "$PKG_CONFIG" && \ { { printf "%s\n" "$as_me:${as_lineno-$LINENO}: \$PKG_CONFIG --exists --print-errors \"libudev >= 196\""; } >&5 ($PKG_CONFIG --exists --print-errors "libudev >= 196") 2>&5 ac_status=$? printf "%s\n" "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; }; then pkg_cv_UDEV_LIBS=`$PKG_CONFIG --libs "libudev >= 196" 2>/dev/null` test "x$?" != "x0" && pkg_failed=yes else pkg_failed=yes fi else pkg_failed=untried fi if test $pkg_failed = yes; then { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 printf "%s\n" "no" >&6; } if $PKG_CONFIG --atleast-pkgconfig-version 0.20; then _pkg_short_errors_supported=yes else _pkg_short_errors_supported=no fi if test $_pkg_short_errors_supported = yes; then UDEV_PKG_ERRORS=`$PKG_CONFIG --short-errors --print-errors --cflags --libs "libudev >= 196" 2>&1` else UDEV_PKG_ERRORS=`$PKG_CONFIG --print-errors --cflags --libs "libudev >= 196" 2>&1` fi # Put the nasty error message in config.log where it belongs echo "$UDEV_PKG_ERRORS" >&5 as_fn_error $? "Package requirements (libudev >= 196) were not met: $UDEV_PKG_ERRORS Consider adjusting the PKG_CONFIG_PATH environment variable if you installed software in a non-standard prefix. Alternatively, you may set the environment variables UDEV_CFLAGS and UDEV_LIBS to avoid the need to call pkg-config. See the pkg-config man page for more details." "$LINENO" 5 elif test $pkg_failed = untried; then { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 printf "%s\n" "no" >&6; } { { printf "%s\n" "$as_me:${as_lineno-$LINENO}: error: in '$ac_pwd':" >&5 printf "%s\n" "$as_me: error: in '$ac_pwd':" >&2;} as_fn_error $? "The pkg-config script could not be found or is too old. Make sure it is in your PATH or set the PKG_CONFIG environment variable to the full path to pkg-config. Alternatively, you may set the environment variables UDEV_CFLAGS and UDEV_LIBS to avoid the need to call pkg-config. See the pkg-config man page for more details. To get pkg-config, see . See 'config.log' for more details" "$LINENO" 5; } else UDEV_CFLAGS=$pkg_cv_UDEV_CFLAGS UDEV_LIBS=$pkg_cv_UDEV_LIBS { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: yes" >&5 printf "%s\n" "yes" >&6; } fi printf "%s\n" "#define HAVE_UDEV 1" >>confdefs.h fi # Check whether --with-udevdir was given. if test ${with_udevdir+y} then : withval=$with_udevdir; path_udevdir=${withval} fi if (test "${enable_udev}" != "no" && test -z "${path_udevdir}"); then { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking udev directory" >&5 printf %s "checking udev directory... " >&6; } path_udevdir="`$PKG_CONFIG --variable=udevdir udev`" if (test -z "${path_udevdir}"); then as_fn_error $? "udev directory is required" "$LINENO" 5 fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: ${path_udevdir}" >&5 printf "%s\n" "${path_udevdir}" >&6; } fi UDEV_DIR=${path_udevdir} # Check whether --enable-cups was given. if test ${enable_cups+y} then : enableval=$enable_cups; enable_cups=${enableval} fi if test "${enable_cups}" != "no"; then CUPS_TRUE= CUPS_FALSE='#' else CUPS_TRUE='#' CUPS_FALSE= fi if (test "${enable_cups}" != "no"); then { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking cups directory" >&5 printf %s "checking cups directory... " >&6; } cups_serverbin=`$PKG_CONFIG cups --variable=cups_serverbin` { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: ${cups_serverbin}" >&5 printf "%s\n" "${cups_serverbin}" >&6; } fi if test "${cups_serverbin}" != ""; then CUPS_SERVERBIN_TRUE= CUPS_SERVERBIN_FALSE='#' else CUPS_SERVERBIN_TRUE='#' CUPS_SERVERBIN_FALSE= fi if test "${cups_serverbin}" != "" then : CUPS_SERVERBIN=${cups_serverbin} fi # Check whether --enable-mesh was given. if test ${enable_mesh+y} then : enableval=$enable_mesh; enable_mesh=${enableval} fi if test "${enable_mesh}" = "yes"; then MESH_TRUE= MESH_FALSE='#' else MESH_TRUE='#' MESH_FALSE= fi if (test "${enable_mesh}" = "yes"); then pkg_failed=no { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for json-c >= 0.13" >&5 printf %s "checking for json-c >= 0.13... " >&6; } if test -n "$JSONC_CFLAGS"; then pkg_cv_JSONC_CFLAGS="$JSONC_CFLAGS" elif test -n "$PKG_CONFIG"; then if test -n "$PKG_CONFIG" && \ { { printf "%s\n" "$as_me:${as_lineno-$LINENO}: \$PKG_CONFIG --exists --print-errors \"json-c >= 0.13\""; } >&5 ($PKG_CONFIG --exists --print-errors "json-c >= 0.13") 2>&5 ac_status=$? printf "%s\n" "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; }; then pkg_cv_JSONC_CFLAGS=`$PKG_CONFIG --cflags "json-c >= 0.13" 2>/dev/null` test "x$?" != "x0" && pkg_failed=yes else pkg_failed=yes fi else pkg_failed=untried fi if test -n "$JSONC_LIBS"; then pkg_cv_JSONC_LIBS="$JSONC_LIBS" elif test -n "$PKG_CONFIG"; then if test -n "$PKG_CONFIG" && \ { { printf "%s\n" "$as_me:${as_lineno-$LINENO}: \$PKG_CONFIG --exists --print-errors \"json-c >= 0.13\""; } >&5 ($PKG_CONFIG --exists --print-errors "json-c >= 0.13") 2>&5 ac_status=$? printf "%s\n" "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; }; then pkg_cv_JSONC_LIBS=`$PKG_CONFIG --libs "json-c >= 0.13" 2>/dev/null` test "x$?" != "x0" && pkg_failed=yes else pkg_failed=yes fi else pkg_failed=untried fi if test $pkg_failed = yes; then { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 printf "%s\n" "no" >&6; } if $PKG_CONFIG --atleast-pkgconfig-version 0.20; then _pkg_short_errors_supported=yes else _pkg_short_errors_supported=no fi if test $_pkg_short_errors_supported = yes; then JSONC_PKG_ERRORS=`$PKG_CONFIG --short-errors --print-errors --cflags --libs "json-c >= 0.13" 2>&1` else JSONC_PKG_ERRORS=`$PKG_CONFIG --print-errors --cflags --libs "json-c >= 0.13" 2>&1` fi # Put the nasty error message in config.log where it belongs echo "$JSONC_PKG_ERRORS" >&5 as_fn_error $? "Package requirements (json-c >= 0.13) were not met: $JSONC_PKG_ERRORS Consider adjusting the PKG_CONFIG_PATH environment variable if you installed software in a non-standard prefix. Alternatively, you may set the environment variables JSONC_CFLAGS and JSONC_LIBS to avoid the need to call pkg-config. See the pkg-config man page for more details." "$LINENO" 5 elif test $pkg_failed = untried; then { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 printf "%s\n" "no" >&6; } { { printf "%s\n" "$as_me:${as_lineno-$LINENO}: error: in '$ac_pwd':" >&5 printf "%s\n" "$as_me: error: in '$ac_pwd':" >&2;} as_fn_error $? "The pkg-config script could not be found or is too old. Make sure it is in your PATH or set the PKG_CONFIG environment variable to the full path to pkg-config. Alternatively, you may set the environment variables JSONC_CFLAGS and JSONC_LIBS to avoid the need to call pkg-config. See the pkg-config man page for more details. To get pkg-config, see . See 'config.log' for more details" "$LINENO" 5; } else JSONC_CFLAGS=$pkg_cv_JSONC_CFLAGS JSONC_LIBS=$pkg_cv_JSONC_LIBS { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: yes" >&5 printf "%s\n" "yes" >&6; } fi fi # Check whether --enable-midi was given. if test ${enable_midi+y} then : enableval=$enable_midi; enable_midi=${enableval} fi if test "${enable_midi}" = "yes"; then MIDI_TRUE= MIDI_FALSE='#' else MIDI_TRUE='#' MIDI_FALSE= fi if (test "${enable_midi}" = "yes"); then pkg_failed=no { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for alsa" >&5 printf %s "checking for alsa... " >&6; } if test -n "$ALSA_CFLAGS"; then pkg_cv_ALSA_CFLAGS="$ALSA_CFLAGS" elif test -n "$PKG_CONFIG"; then if test -n "$PKG_CONFIG" && \ { { printf "%s\n" "$as_me:${as_lineno-$LINENO}: \$PKG_CONFIG --exists --print-errors \"alsa\""; } >&5 ($PKG_CONFIG --exists --print-errors "alsa") 2>&5 ac_status=$? printf "%s\n" "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; }; then pkg_cv_ALSA_CFLAGS=`$PKG_CONFIG --cflags "alsa" 2>/dev/null` test "x$?" != "x0" && pkg_failed=yes else pkg_failed=yes fi else pkg_failed=untried fi if test -n "$ALSA_LIBS"; then pkg_cv_ALSA_LIBS="$ALSA_LIBS" elif test -n "$PKG_CONFIG"; then if test -n "$PKG_CONFIG" && \ { { printf "%s\n" "$as_me:${as_lineno-$LINENO}: \$PKG_CONFIG --exists --print-errors \"alsa\""; } >&5 ($PKG_CONFIG --exists --print-errors "alsa") 2>&5 ac_status=$? printf "%s\n" "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; }; then pkg_cv_ALSA_LIBS=`$PKG_CONFIG --libs "alsa" 2>/dev/null` test "x$?" != "x0" && pkg_failed=yes else pkg_failed=yes fi else pkg_failed=untried fi if test $pkg_failed = yes; then { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 printf "%s\n" "no" >&6; } if $PKG_CONFIG --atleast-pkgconfig-version 0.20; then _pkg_short_errors_supported=yes else _pkg_short_errors_supported=no fi if test $_pkg_short_errors_supported = yes; then ALSA_PKG_ERRORS=`$PKG_CONFIG --short-errors --print-errors --cflags --libs "alsa" 2>&1` else ALSA_PKG_ERRORS=`$PKG_CONFIG --print-errors --cflags --libs "alsa" 2>&1` fi # Put the nasty error message in config.log where it belongs echo "$ALSA_PKG_ERRORS" >&5 as_fn_error $? "Package requirements (alsa) were not met: $ALSA_PKG_ERRORS Consider adjusting the PKG_CONFIG_PATH environment variable if you installed software in a non-standard prefix. Alternatively, you may set the environment variables ALSA_CFLAGS and ALSA_LIBS to avoid the need to call pkg-config. See the pkg-config man page for more details." "$LINENO" 5 elif test $pkg_failed = untried; then { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 printf "%s\n" "no" >&6; } { { printf "%s\n" "$as_me:${as_lineno-$LINENO}: error: in '$ac_pwd':" >&5 printf "%s\n" "$as_me: error: in '$ac_pwd':" >&2;} as_fn_error $? "The pkg-config script could not be found or is too old. Make sure it is in your PATH or set the PKG_CONFIG environment variable to the full path to pkg-config. Alternatively, you may set the environment variables ALSA_CFLAGS and ALSA_LIBS to avoid the need to call pkg-config. See the pkg-config man page for more details. To get pkg-config, see . See 'config.log' for more details" "$LINENO" 5; } else ALSA_CFLAGS=$pkg_cv_ALSA_CFLAGS ALSA_LIBS=$pkg_cv_ALSA_LIBS { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: yes" >&5 printf "%s\n" "yes" >&6; } fi fi # Check whether --enable-obex was given. if test ${enable_obex+y} then : enableval=$enable_obex; enable_obex=${enableval} fi if (test "${enable_obex}" != "no"); then pkg_failed=no { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for libical" >&5 printf %s "checking for libical... " >&6; } if test -n "$ICAL_CFLAGS"; then pkg_cv_ICAL_CFLAGS="$ICAL_CFLAGS" elif test -n "$PKG_CONFIG"; then if test -n "$PKG_CONFIG" && \ { { printf "%s\n" "$as_me:${as_lineno-$LINENO}: \$PKG_CONFIG --exists --print-errors \"libical\""; } >&5 ($PKG_CONFIG --exists --print-errors "libical") 2>&5 ac_status=$? printf "%s\n" "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; }; then pkg_cv_ICAL_CFLAGS=`$PKG_CONFIG --cflags "libical" 2>/dev/null` test "x$?" != "x0" && pkg_failed=yes else pkg_failed=yes fi else pkg_failed=untried fi if test -n "$ICAL_LIBS"; then pkg_cv_ICAL_LIBS="$ICAL_LIBS" elif test -n "$PKG_CONFIG"; then if test -n "$PKG_CONFIG" && \ { { printf "%s\n" "$as_me:${as_lineno-$LINENO}: \$PKG_CONFIG --exists --print-errors \"libical\""; } >&5 ($PKG_CONFIG --exists --print-errors "libical") 2>&5 ac_status=$? printf "%s\n" "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; }; then pkg_cv_ICAL_LIBS=`$PKG_CONFIG --libs "libical" 2>/dev/null` test "x$?" != "x0" && pkg_failed=yes else pkg_failed=yes fi else pkg_failed=untried fi if test $pkg_failed = yes; then { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 printf "%s\n" "no" >&6; } if $PKG_CONFIG --atleast-pkgconfig-version 0.20; then _pkg_short_errors_supported=yes else _pkg_short_errors_supported=no fi if test $_pkg_short_errors_supported = yes; then ICAL_PKG_ERRORS=`$PKG_CONFIG --short-errors --print-errors --cflags --libs "libical" 2>&1` else ICAL_PKG_ERRORS=`$PKG_CONFIG --print-errors --cflags --libs "libical" 2>&1` fi # Put the nasty error message in config.log where it belongs echo "$ICAL_PKG_ERRORS" >&5 as_fn_error $? "Package requirements (libical) were not met: $ICAL_PKG_ERRORS Consider adjusting the PKG_CONFIG_PATH environment variable if you installed software in a non-standard prefix. Alternatively, you may set the environment variables ICAL_CFLAGS and ICAL_LIBS to avoid the need to call pkg-config. See the pkg-config man page for more details." "$LINENO" 5 elif test $pkg_failed = untried; then { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 printf "%s\n" "no" >&6; } { { printf "%s\n" "$as_me:${as_lineno-$LINENO}: error: in '$ac_pwd':" >&5 printf "%s\n" "$as_me: error: in '$ac_pwd':" >&2;} as_fn_error $? "The pkg-config script could not be found or is too old. Make sure it is in your PATH or set the PKG_CONFIG environment variable to the full path to pkg-config. Alternatively, you may set the environment variables ICAL_CFLAGS and ICAL_LIBS to avoid the need to call pkg-config. See the pkg-config man page for more details. To get pkg-config, see . See 'config.log' for more details" "$LINENO" 5; } else ICAL_CFLAGS=$pkg_cv_ICAL_CFLAGS ICAL_LIBS=$pkg_cv_ICAL_LIBS { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: yes" >&5 printf "%s\n" "yes" >&6; } fi fi if test "${enable_obex}" != "no"; then OBEX_TRUE= OBEX_FALSE='#' else OBEX_TRUE='#' OBEX_FALSE= fi # Check whether --enable-btpclient was given. if test ${enable_btpclient+y} then : enableval=$enable_btpclient; enable_btpclient=${enableval} fi if test "${enable_btpclient}" = "yes"; then BTPCLIENT_TRUE= BTPCLIENT_FALSE='#' else BTPCLIENT_TRUE='#' BTPCLIENT_FALSE= fi # Check whether --enable-external_ell was given. if test ${enable_external_ell+y} then : enableval=$enable_external_ell; enable_external_ell=${enableval} fi if (test "${enable_external_ell}" = "yes"); then pkg_failed=no { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for ell >= 0.39" >&5 printf %s "checking for ell >= 0.39... " >&6; } if test -n "$ELL_CFLAGS"; then pkg_cv_ELL_CFLAGS="$ELL_CFLAGS" elif test -n "$PKG_CONFIG"; then if test -n "$PKG_CONFIG" && \ { { printf "%s\n" "$as_me:${as_lineno-$LINENO}: \$PKG_CONFIG --exists --print-errors \"ell >= 0.39\""; } >&5 ($PKG_CONFIG --exists --print-errors "ell >= 0.39") 2>&5 ac_status=$? printf "%s\n" "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; }; then pkg_cv_ELL_CFLAGS=`$PKG_CONFIG --cflags "ell >= 0.39" 2>/dev/null` test "x$?" != "x0" && pkg_failed=yes else pkg_failed=yes fi else pkg_failed=untried fi if test -n "$ELL_LIBS"; then pkg_cv_ELL_LIBS="$ELL_LIBS" elif test -n "$PKG_CONFIG"; then if test -n "$PKG_CONFIG" && \ { { printf "%s\n" "$as_me:${as_lineno-$LINENO}: \$PKG_CONFIG --exists --print-errors \"ell >= 0.39\""; } >&5 ($PKG_CONFIG --exists --print-errors "ell >= 0.39") 2>&5 ac_status=$? printf "%s\n" "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; }; then pkg_cv_ELL_LIBS=`$PKG_CONFIG --libs "ell >= 0.39" 2>/dev/null` test "x$?" != "x0" && pkg_failed=yes else pkg_failed=yes fi else pkg_failed=untried fi if test $pkg_failed = yes; then { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 printf "%s\n" "no" >&6; } if $PKG_CONFIG --atleast-pkgconfig-version 0.20; then _pkg_short_errors_supported=yes else _pkg_short_errors_supported=no fi if test $_pkg_short_errors_supported = yes; then ELL_PKG_ERRORS=`$PKG_CONFIG --short-errors --print-errors --cflags --libs "ell >= 0.39" 2>&1` else ELL_PKG_ERRORS=`$PKG_CONFIG --print-errors --cflags --libs "ell >= 0.39" 2>&1` fi # Put the nasty error message in config.log where it belongs echo "$ELL_PKG_ERRORS" >&5 as_fn_error $? "Package requirements (ell >= 0.39) were not met: $ELL_PKG_ERRORS Consider adjusting the PKG_CONFIG_PATH environment variable if you installed software in a non-standard prefix. Alternatively, you may set the environment variables ELL_CFLAGS and ELL_LIBS to avoid the need to call pkg-config. See the pkg-config man page for more details." "$LINENO" 5 elif test $pkg_failed = untried; then { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 printf "%s\n" "no" >&6; } { { printf "%s\n" "$as_me:${as_lineno-$LINENO}: error: in '$ac_pwd':" >&5 printf "%s\n" "$as_me: error: in '$ac_pwd':" >&2;} as_fn_error $? "The pkg-config script could not be found or is too old. Make sure it is in your PATH or set the PKG_CONFIG environment variable to the full path to pkg-config. Alternatively, you may set the environment variables ELL_CFLAGS and ELL_LIBS to avoid the need to call pkg-config. See the pkg-config man page for more details. To get pkg-config, see . See 'config.log' for more details" "$LINENO" 5; } else ELL_CFLAGS=$pkg_cv_ELL_CFLAGS ELL_LIBS=$pkg_cv_ELL_LIBS { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: yes" >&5 printf "%s\n" "yes" >&6; } fi fi if (test "${enable_external_ell}" != "yes" && (test "${enable_btpclient}" = "yes" || test "${enable_mesh}" = "yes")); then if (test ! -f ${srcdir}/ell/ell.h) && (test ! -f ${srcdir}/../ell/ell/ell.h); then as_fn_error $? "ELL source is required or use --enable-external-ell" "$LINENO" 5 fi fi if test "${enable_external_ell}" = "yes" || (test "${enable_btpclient}" != "yes" && test "${enable_mesh}" != "yes"); then EXTERNAL_ELL_TRUE= EXTERNAL_ELL_FALSE='#' else EXTERNAL_ELL_TRUE='#' EXTERNAL_ELL_FALSE= fi if test "${enable_btpclient}" = "yes" || test "${enable_mesh}" = "yes"; then LIBSHARED_ELL_TRUE= LIBSHARED_ELL_FALSE='#' else LIBSHARED_ELL_TRUE='#' LIBSHARED_ELL_FALSE= fi # Check whether --enable-client was given. if test ${enable_client+y} then : enableval=$enable_client; enable_client=${enableval} fi if test "${enable_client}" != "no"; then CLIENT_TRUE= CLIENT_FALSE='#' else CLIENT_TRUE='#' CLIENT_FALSE= fi if (test "${enable_client}" != "no" || test "${enable_mesh}" = "yes"); then for ac_header in readline/readline.h do : ac_fn_c_check_header_compile "$LINENO" "readline/readline.h" "ac_cv_header_readline_readline_h" "$ac_includes_default" if test "x$ac_cv_header_readline_readline_h" = xyes then : printf "%s\n" "#define HAVE_READLINE_READLINE_H 1" >>confdefs.h enable_readline=yes else case e in #( e) as_fn_error $? "readline header files are required" "$LINENO" 5 ;; esac fi done fi if test "${enable_readline}" = "yes"; then READLINE_TRUE= READLINE_FALSE='#' else READLINE_TRUE='#' READLINE_FALSE= fi # Check whether --enable-systemd was given. if test ${enable_systemd+y} then : enableval=$enable_systemd; enable_systemd=${enableval} fi if test "${enable_systemd}" != "no"; then SYSTEMD_TRUE= SYSTEMD_FALSE='#' else SYSTEMD_TRUE='#' SYSTEMD_FALSE= fi # Check whether --with-systemdsystemunitdir was given. if test ${with_systemdsystemunitdir+y} then : withval=$with_systemdsystemunitdir; path_systemunitdir=${withval} fi if (test "${enable_systemd}" != "no" && test -z "${path_systemunitdir}"); then { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking systemd system unit dir" >&5 printf %s "checking systemd system unit dir... " >&6; } path_systemunitdir="`$PKG_CONFIG --variable=systemdsystemunitdir systemd`" if (test -z "${path_systemunitdir}"); then as_fn_error $? "systemd system unit directory is required" "$LINENO" 5 fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: ${path_systemunitdir}" >&5 printf "%s\n" "${path_systemunitdir}" >&6; } fi SYSTEMD_SYSTEMUNITDIR=${path_systemunitdir} # Check whether --with-systemduserunitdir was given. if test ${with_systemduserunitdir+y} then : withval=$with_systemduserunitdir; path_userunitdir=${withval} fi if (test "${enable_systemd}" != "no" && test -z "${path_userunitdir}"); then { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking systemd user unit dir" >&5 printf %s "checking systemd user unit dir... " >&6; } path_userunitdir="`$PKG_CONFIG --variable=systemduserunitdir systemd`" if (test -z "${path_userunitdir}"); then as_fn_error $? "systemd user unit directory is required" "$LINENO" 5 fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: ${path_userunitdir}" >&5 printf "%s\n" "${path_userunitdir}" >&6; } fi SYSTEMD_USERUNITDIR=${path_userunitdir} # Check whether --enable-datafiles was given. if test ${enable_datafiles+y} then : enableval=$enable_datafiles; enable_datafiles=${enableval} fi if test "${enable_datafiles}" != "no"; then DATAFILES_TRUE= DATAFILES_FALSE='#' else DATAFILES_TRUE='#' DATAFILES_FALSE= fi # Check whether --enable-manpages was given. if test ${enable_manpages+y} then : enableval=$enable_manpages; enable_manpages=${enableval} fi if (test "${enable_manpages}" != "no"); then for ac_prog in rst2man rst2man.py do # Extract the first word of "$ac_prog", so it can be a program name with args. set dummy $ac_prog; ac_word=$2 { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 printf %s "checking for $ac_word... " >&6; } if test ${ac_cv_prog_RST2MAN+y} then : printf %s "(cached) " >&6 else case e in #( e) if test -n "$RST2MAN"; then ac_cv_prog_RST2MAN="$RST2MAN" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS case $as_dir in #((( '') as_dir=./ ;; */) ;; *) as_dir=$as_dir/ ;; esac for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir$ac_word$ac_exec_ext"; then ac_cv_prog_RST2MAN="$ac_prog" printf "%s\n" "$as_me:${as_lineno-$LINENO}: found $as_dir$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi ;; esac fi RST2MAN=$ac_cv_prog_RST2MAN if test -n "$RST2MAN"; then { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $RST2MAN" >&5 printf "%s\n" "$RST2MAN" >&6; } else { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 printf "%s\n" "no" >&6; } fi test -n "$RST2MAN" && break done test -n "$RST2MAN" || RST2MAN=""no"" if (test "${RST2MAN}" = "no" ); then as_fn_error $? "rst2man is required" "$LINENO" 5 fi fi if test "${enable_manpages}" != "no"; then MANPAGES_TRUE= MANPAGES_FALSE='#' else MANPAGES_TRUE='#' MANPAGES_FALSE= fi if test "${enable_manpages}" != "no" && test "${RST2MAN}" != "no"; then RUN_RST2MAN_TRUE= RUN_RST2MAN_FALSE='#' else RUN_RST2MAN_TRUE='#' RUN_RST2MAN_FALSE= fi # Check whether --enable-testing was given. if test ${enable_testing+y} then : enableval=$enable_testing; enable_testing=${enableval} fi if test "${enable_testing}" = "yes"; then TESTING_TRUE= TESTING_FALSE='#' else TESTING_TRUE='#' TESTING_FALSE= fi if (test "${enable_testing}" = "yes"); then ac_fn_check_decl "$LINENO" "SOF_TIMESTAMPING_TX_COMPLETION" "ac_cv_have_decl_SOF_TIMESTAMPING_TX_COMPLETION" "#include #include #include " "$ac_c_undeclared_builtin_options" "CFLAGS" if test "x$ac_cv_have_decl_SOF_TIMESTAMPING_TX_COMPLETION" = xyes then : ac_have_decl=1 else case e in #( e) ac_have_decl=0 ;; esac fi printf "%s\n" "#define HAVE_DECL_SOF_TIMESTAMPING_TX_COMPLETION $ac_have_decl" >>confdefs.h ac_fn_check_decl "$LINENO" "SCM_TSTAMP_COMPLETION" "ac_cv_have_decl_SCM_TSTAMP_COMPLETION" "#include #include #include " "$ac_c_undeclared_builtin_options" "CFLAGS" if test "x$ac_cv_have_decl_SCM_TSTAMP_COMPLETION" = xyes then : ac_have_decl=1 else case e in #( e) ac_have_decl=0 ;; esac fi printf "%s\n" "#define HAVE_DECL_SCM_TSTAMP_COMPLETION $ac_have_decl" >>confdefs.h fi # Check whether --enable-experimental was given. if test ${enable_experimental+y} then : enableval=$enable_experimental; enable_experimental=${enableval} fi if test "${enable_experimental}" = "yes"; then EXPERIMENTAL_TRUE= EXPERIMENTAL_FALSE='#' else EXPERIMENTAL_TRUE='#' EXPERIMENTAL_FALSE= fi # Check whether --enable-deprecated was given. if test ${enable_deprecated+y} then : enableval=$enable_deprecated; enable_deprecated=${enableval} fi if test "${enable_deprecated}" = "yes"; then DEPRECATED_TRUE= DEPRECATED_FALSE='#' else DEPRECATED_TRUE='#' DEPRECATED_FALSE= fi # Check whether --enable-external-plugins was given. if test ${enable_external_plugins+y} then : enableval=$enable_external_plugins; enable_external_plugins=${enableval} fi if test "${enable_external_plugins}" = "yes"; then EXTERNAL_PLUGINS_TRUE= EXTERNAL_PLUGINS_FALSE='#' else EXTERNAL_PLUGINS_TRUE='#' EXTERNAL_PLUGINS_FALSE= fi if (test "${enable_external_plugins}" = "yes"); then printf "%s\n" "#define EXTERNAL_PLUGINS 1" >>confdefs.h else printf "%s\n" "#define EXTERNAL_PLUGINS 0" >>confdefs.h fi # Check whether --enable-sixaxis was given. if test ${enable_sixaxis+y} then : enableval=$enable_sixaxis; enable_sixaxis=${enableval} fi if test "${enable_sixaxis}" = "yes" && test "${enable_udev}" != "no"; then SIXAXIS_TRUE= SIXAXIS_FALSE='#' else SIXAXIS_TRUE='#' SIXAXIS_FALSE= fi # Check whether --enable-hid2hci was given. if test ${enable_hid2hci+y} then : enableval=$enable_hid2hci; enable_hid2hci=${enableval} fi if test "${enable_hid2hci}" = "yes" && test "${enable_udev}" != "no"; then HID2HCI_TRUE= HID2HCI_FALSE='#' else HID2HCI_TRUE='#' HID2HCI_FALSE= fi # Check whether --enable-logger was given. if test ${enable_logger+y} then : enableval=$enable_logger; enable_logger=${enableval} fi if test "${enable_logger}" = "yes"; then LOGGER_TRUE= LOGGER_FALSE='#' else LOGGER_TRUE='#' LOGGER_FALSE= fi # Check whether --enable-admin was given. if test ${enable_admin+y} then : enableval=$enable_admin; enable_admin=${enableval} fi if test "${enable_admin}" = "yes"; then ADMIN_TRUE= ADMIN_FALSE='#' else ADMIN_TRUE='#' ADMIN_FALSE= fi if (test "${prefix}" = "NONE"); then if (test "$localstatedir" = '${prefix}/var'); then localstatedir='/var' fi prefix="${ac_default_prefix}" fi if (test "${exec_prefix}" = "NONE"); then # exec_prefix defaults to prefix, although our manual handling of the # latter (above) confuses autoconf. Manually set the exec_prefix. exec_prefix="${prefix}" fi # Expand any variables containing relative references like ${prefix} and co. # # Otherwise we'll end up with literal references in the final binaries or # manuals, which is not something we really want. # pkgbindir="${bindir}" if (test "$bindir" = '${exec_prefix}/bin'); then pkgbindir="${exec_prefix}/bin" else pkgbindir="${bindir}" fi PKGBINDIR="${pkgbindir}" if (test "$libexecdir" = '${exec_prefix}/libexec'); then pkglibexecdir="${exec_prefix}/libexec/bluetooth" else pkglibexecdir="${libexecdir}/bluetooth" fi PKGLIBEXECDIR="${pkglibexecdir}" if (test "$localstatedir" = '${prefix}/var'); then storagedir="${prefix}/var/lib/bluetooth" else storagedir="${localstatedir}/lib/bluetooth" fi printf "%s\n" "#define STORAGEDIR \"${storagedir}\"" >>confdefs.h if (test "$sysconfdir" = '${prefix}/etc'); then configdir="${prefix}/etc/bluetooth" else configdir="${sysconfdir}/bluetooth" fi printf "%s\n" "#define CONFIGDIR \"${configdir}\"" >>confdefs.h CONFIGDIR="${configdir}" printf "%s\n" "#define MESH_STORAGEDIR \"${storagedir}/mesh\"" >>confdefs.h MESH_STORAGEDIR="${storagedir}/mesh" # Check whether --enable-android was given. if test ${enable_android+y} then : enableval=$enable_android; enable_android=${enableval} fi if test "${enable_android}" = "yes"; then ANDROID_TRUE= ANDROID_FALSE='#' else ANDROID_TRUE='#' ANDROID_FALSE= fi if (test "${enable_android}" = "yes"); then pkg_failed=no { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for sbc >= 1.2" >&5 printf %s "checking for sbc >= 1.2... " >&6; } if test -n "$SBC_CFLAGS"; then pkg_cv_SBC_CFLAGS="$SBC_CFLAGS" elif test -n "$PKG_CONFIG"; then if test -n "$PKG_CONFIG" && \ { { printf "%s\n" "$as_me:${as_lineno-$LINENO}: \$PKG_CONFIG --exists --print-errors \"sbc >= 1.2\""; } >&5 ($PKG_CONFIG --exists --print-errors "sbc >= 1.2") 2>&5 ac_status=$? printf "%s\n" "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; }; then pkg_cv_SBC_CFLAGS=`$PKG_CONFIG --cflags "sbc >= 1.2" 2>/dev/null` test "x$?" != "x0" && pkg_failed=yes else pkg_failed=yes fi else pkg_failed=untried fi if test -n "$SBC_LIBS"; then pkg_cv_SBC_LIBS="$SBC_LIBS" elif test -n "$PKG_CONFIG"; then if test -n "$PKG_CONFIG" && \ { { printf "%s\n" "$as_me:${as_lineno-$LINENO}: \$PKG_CONFIG --exists --print-errors \"sbc >= 1.2\""; } >&5 ($PKG_CONFIG --exists --print-errors "sbc >= 1.2") 2>&5 ac_status=$? printf "%s\n" "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; }; then pkg_cv_SBC_LIBS=`$PKG_CONFIG --libs "sbc >= 1.2" 2>/dev/null` test "x$?" != "x0" && pkg_failed=yes else pkg_failed=yes fi else pkg_failed=untried fi if test $pkg_failed = yes; then { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 printf "%s\n" "no" >&6; } if $PKG_CONFIG --atleast-pkgconfig-version 0.20; then _pkg_short_errors_supported=yes else _pkg_short_errors_supported=no fi if test $_pkg_short_errors_supported = yes; then SBC_PKG_ERRORS=`$PKG_CONFIG --short-errors --print-errors --cflags --libs "sbc >= 1.2" 2>&1` else SBC_PKG_ERRORS=`$PKG_CONFIG --print-errors --cflags --libs "sbc >= 1.2" 2>&1` fi # Put the nasty error message in config.log where it belongs echo "$SBC_PKG_ERRORS" >&5 as_fn_error $? "Package requirements (sbc >= 1.2) were not met: $SBC_PKG_ERRORS Consider adjusting the PKG_CONFIG_PATH environment variable if you installed software in a non-standard prefix. Alternatively, you may set the environment variables SBC_CFLAGS and SBC_LIBS to avoid the need to call pkg-config. See the pkg-config man page for more details." "$LINENO" 5 elif test $pkg_failed = untried; then { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 printf "%s\n" "no" >&6; } { { printf "%s\n" "$as_me:${as_lineno-$LINENO}: error: in '$ac_pwd':" >&5 printf "%s\n" "$as_me: error: in '$ac_pwd':" >&2;} as_fn_error $? "The pkg-config script could not be found or is too old. Make sure it is in your PATH or set the PKG_CONFIG environment variable to the full path to pkg-config. Alternatively, you may set the environment variables SBC_CFLAGS and SBC_LIBS to avoid the need to call pkg-config. See the pkg-config man page for more details. To get pkg-config, see . See 'config.log' for more details" "$LINENO" 5; } else SBC_CFLAGS=$pkg_cv_SBC_CFLAGS SBC_LIBS=$pkg_cv_SBC_LIBS { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: yes" >&5 printf "%s\n" "yes" >&6; } fi fi if (test "${enable_android}" = "yes"); then pkg_failed=no { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for speexdsp >= 1.2" >&5 printf %s "checking for speexdsp >= 1.2... " >&6; } if test -n "$SPEEXDSP_CFLAGS"; then pkg_cv_SPEEXDSP_CFLAGS="$SPEEXDSP_CFLAGS" elif test -n "$PKG_CONFIG"; then if test -n "$PKG_CONFIG" && \ { { printf "%s\n" "$as_me:${as_lineno-$LINENO}: \$PKG_CONFIG --exists --print-errors \"speexdsp >= 1.2\""; } >&5 ($PKG_CONFIG --exists --print-errors "speexdsp >= 1.2") 2>&5 ac_status=$? printf "%s\n" "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; }; then pkg_cv_SPEEXDSP_CFLAGS=`$PKG_CONFIG --cflags "speexdsp >= 1.2" 2>/dev/null` test "x$?" != "x0" && pkg_failed=yes else pkg_failed=yes fi else pkg_failed=untried fi if test -n "$SPEEXDSP_LIBS"; then pkg_cv_SPEEXDSP_LIBS="$SPEEXDSP_LIBS" elif test -n "$PKG_CONFIG"; then if test -n "$PKG_CONFIG" && \ { { printf "%s\n" "$as_me:${as_lineno-$LINENO}: \$PKG_CONFIG --exists --print-errors \"speexdsp >= 1.2\""; } >&5 ($PKG_CONFIG --exists --print-errors "speexdsp >= 1.2") 2>&5 ac_status=$? printf "%s\n" "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; }; then pkg_cv_SPEEXDSP_LIBS=`$PKG_CONFIG --libs "speexdsp >= 1.2" 2>/dev/null` test "x$?" != "x0" && pkg_failed=yes else pkg_failed=yes fi else pkg_failed=untried fi if test $pkg_failed = yes; then { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 printf "%s\n" "no" >&6; } if $PKG_CONFIG --atleast-pkgconfig-version 0.20; then _pkg_short_errors_supported=yes else _pkg_short_errors_supported=no fi if test $_pkg_short_errors_supported = yes; then SPEEXDSP_PKG_ERRORS=`$PKG_CONFIG --short-errors --print-errors --cflags --libs "speexdsp >= 1.2" 2>&1` else SPEEXDSP_PKG_ERRORS=`$PKG_CONFIG --print-errors --cflags --libs "speexdsp >= 1.2" 2>&1` fi # Put the nasty error message in config.log where it belongs echo "$SPEEXDSP_PKG_ERRORS" >&5 as_fn_error $? "Package requirements (speexdsp >= 1.2) were not met: $SPEEXDSP_PKG_ERRORS Consider adjusting the PKG_CONFIG_PATH environment variable if you installed software in a non-standard prefix. Alternatively, you may set the environment variables SPEEXDSP_CFLAGS and SPEEXDSP_LIBS to avoid the need to call pkg-config. See the pkg-config man page for more details." "$LINENO" 5 elif test $pkg_failed = untried; then { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 printf "%s\n" "no" >&6; } { { printf "%s\n" "$as_me:${as_lineno-$LINENO}: error: in '$ac_pwd':" >&5 printf "%s\n" "$as_me: error: in '$ac_pwd':" >&2;} as_fn_error $? "The pkg-config script could not be found or is too old. Make sure it is in your PATH or set the PKG_CONFIG environment variable to the full path to pkg-config. Alternatively, you may set the environment variables SPEEXDSP_CFLAGS and SPEEXDSP_LIBS to avoid the need to call pkg-config. See the pkg-config man page for more details. To get pkg-config, see . See 'config.log' for more details" "$LINENO" 5; } else SPEEXDSP_CFLAGS=$pkg_cv_SPEEXDSP_CFLAGS SPEEXDSP_LIBS=$pkg_cv_SPEEXDSP_LIBS { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: yes" >&5 printf "%s\n" "yes" >&6; } fi fi printf "%s\n" "#define ANDROID_STORAGEDIR \"${storagedir}/android\"" >>confdefs.h # Check whether --with-phonebook was given. if test ${with_phonebook+y} then : withval=$with_phonebook; plugin_phonebook=${withval} fi if (test -z "${plugin_phonebook}"); then plugin_phonebook=dummy fi if (test "${plugin_phonebook}" = "ebook"); then pkg_failed=no { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for libebook-1.2 >= 3.3" >&5 printf %s "checking for libebook-1.2 >= 3.3... " >&6; } if test -n "$LIBEBOOK_CFLAGS"; then pkg_cv_LIBEBOOK_CFLAGS="$LIBEBOOK_CFLAGS" elif test -n "$PKG_CONFIG"; then if test -n "$PKG_CONFIG" && \ { { printf "%s\n" "$as_me:${as_lineno-$LINENO}: \$PKG_CONFIG --exists --print-errors \"libebook-1.2 >= 3.3\""; } >&5 ($PKG_CONFIG --exists --print-errors "libebook-1.2 >= 3.3") 2>&5 ac_status=$? printf "%s\n" "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; }; then pkg_cv_LIBEBOOK_CFLAGS=`$PKG_CONFIG --cflags "libebook-1.2 >= 3.3" 2>/dev/null` test "x$?" != "x0" && pkg_failed=yes else pkg_failed=yes fi else pkg_failed=untried fi if test -n "$LIBEBOOK_LIBS"; then pkg_cv_LIBEBOOK_LIBS="$LIBEBOOK_LIBS" elif test -n "$PKG_CONFIG"; then if test -n "$PKG_CONFIG" && \ { { printf "%s\n" "$as_me:${as_lineno-$LINENO}: \$PKG_CONFIG --exists --print-errors \"libebook-1.2 >= 3.3\""; } >&5 ($PKG_CONFIG --exists --print-errors "libebook-1.2 >= 3.3") 2>&5 ac_status=$? printf "%s\n" "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; }; then pkg_cv_LIBEBOOK_LIBS=`$PKG_CONFIG --libs "libebook-1.2 >= 3.3" 2>/dev/null` test "x$?" != "x0" && pkg_failed=yes else pkg_failed=yes fi else pkg_failed=untried fi if test $pkg_failed = yes; then { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 printf "%s\n" "no" >&6; } if $PKG_CONFIG --atleast-pkgconfig-version 0.20; then _pkg_short_errors_supported=yes else _pkg_short_errors_supported=no fi if test $_pkg_short_errors_supported = yes; then LIBEBOOK_PKG_ERRORS=`$PKG_CONFIG --short-errors --print-errors --cflags --libs "libebook-1.2 >= 3.3" 2>&1` else LIBEBOOK_PKG_ERRORS=`$PKG_CONFIG --print-errors --cflags --libs "libebook-1.2 >= 3.3" 2>&1` fi # Put the nasty error message in config.log where it belongs echo "$LIBEBOOK_PKG_ERRORS" >&5 as_fn_error $? "Package requirements (libebook-1.2 >= 3.3) were not met: $LIBEBOOK_PKG_ERRORS Consider adjusting the PKG_CONFIG_PATH environment variable if you installed software in a non-standard prefix. Alternatively, you may set the environment variables LIBEBOOK_CFLAGS and LIBEBOOK_LIBS to avoid the need to call pkg-config. See the pkg-config man page for more details." "$LINENO" 5 elif test $pkg_failed = untried; then { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 printf "%s\n" "no" >&6; } { { printf "%s\n" "$as_me:${as_lineno-$LINENO}: error: in '$ac_pwd':" >&5 printf "%s\n" "$as_me: error: in '$ac_pwd':" >&2;} as_fn_error $? "The pkg-config script could not be found or is too old. Make sure it is in your PATH or set the PKG_CONFIG environment variable to the full path to pkg-config. Alternatively, you may set the environment variables LIBEBOOK_CFLAGS and LIBEBOOK_LIBS to avoid the need to call pkg-config. See the pkg-config man page for more details. To get pkg-config, see . See 'config.log' for more details" "$LINENO" 5; } else LIBEBOOK_CFLAGS=$pkg_cv_LIBEBOOK_CFLAGS LIBEBOOK_LIBS=$pkg_cv_LIBEBOOK_LIBS { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: yes" >&5 printf "%s\n" "yes" >&6; } fi pkg_failed=no { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for libedataserver-1.2 >= 3.3" >&5 printf %s "checking for libedataserver-1.2 >= 3.3... " >&6; } if test -n "$LIBEDATESERVER_CFLAGS"; then pkg_cv_LIBEDATESERVER_CFLAGS="$LIBEDATESERVER_CFLAGS" elif test -n "$PKG_CONFIG"; then if test -n "$PKG_CONFIG" && \ { { printf "%s\n" "$as_me:${as_lineno-$LINENO}: \$PKG_CONFIG --exists --print-errors \"libedataserver-1.2 >= 3.3\""; } >&5 ($PKG_CONFIG --exists --print-errors "libedataserver-1.2 >= 3.3") 2>&5 ac_status=$? printf "%s\n" "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; }; then pkg_cv_LIBEDATESERVER_CFLAGS=`$PKG_CONFIG --cflags "libedataserver-1.2 >= 3.3" 2>/dev/null` test "x$?" != "x0" && pkg_failed=yes else pkg_failed=yes fi else pkg_failed=untried fi if test -n "$LIBEDATESERVER_LIBS"; then pkg_cv_LIBEDATESERVER_LIBS="$LIBEDATESERVER_LIBS" elif test -n "$PKG_CONFIG"; then if test -n "$PKG_CONFIG" && \ { { printf "%s\n" "$as_me:${as_lineno-$LINENO}: \$PKG_CONFIG --exists --print-errors \"libedataserver-1.2 >= 3.3\""; } >&5 ($PKG_CONFIG --exists --print-errors "libedataserver-1.2 >= 3.3") 2>&5 ac_status=$? printf "%s\n" "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; }; then pkg_cv_LIBEDATESERVER_LIBS=`$PKG_CONFIG --libs "libedataserver-1.2 >= 3.3" 2>/dev/null` test "x$?" != "x0" && pkg_failed=yes else pkg_failed=yes fi else pkg_failed=untried fi if test $pkg_failed = yes; then { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 printf "%s\n" "no" >&6; } if $PKG_CONFIG --atleast-pkgconfig-version 0.20; then _pkg_short_errors_supported=yes else _pkg_short_errors_supported=no fi if test $_pkg_short_errors_supported = yes; then LIBEDATESERVER_PKG_ERRORS=`$PKG_CONFIG --short-errors --print-errors --cflags --libs "libedataserver-1.2 >= 3.3" 2>&1` else LIBEDATESERVER_PKG_ERRORS=`$PKG_CONFIG --print-errors --cflags --libs "libedataserver-1.2 >= 3.3" 2>&1` fi # Put the nasty error message in config.log where it belongs echo "$LIBEDATESERVER_PKG_ERRORS" >&5 as_fn_error $? "Package requirements (libedataserver-1.2 >= 3.3) were not met: $LIBEDATESERVER_PKG_ERRORS Consider adjusting the PKG_CONFIG_PATH environment variable if you installed software in a non-standard prefix. Alternatively, you may set the environment variables LIBEDATESERVER_CFLAGS and LIBEDATESERVER_LIBS to avoid the need to call pkg-config. See the pkg-config man page for more details." "$LINENO" 5 elif test $pkg_failed = untried; then { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 printf "%s\n" "no" >&6; } { { printf "%s\n" "$as_me:${as_lineno-$LINENO}: error: in '$ac_pwd':" >&5 printf "%s\n" "$as_me: error: in '$ac_pwd':" >&2;} as_fn_error $? "The pkg-config script could not be found or is too old. Make sure it is in your PATH or set the PKG_CONFIG environment variable to the full path to pkg-config. Alternatively, you may set the environment variables LIBEDATESERVER_CFLAGS and LIBEDATESERVER_LIBS to avoid the need to call pkg-config. See the pkg-config man page for more details. To get pkg-config, see . See 'config.log' for more details" "$LINENO" 5; } else LIBEDATESERVER_CFLAGS=$pkg_cv_LIBEDATESERVER_CFLAGS LIBEDATESERVER_LIBS=$pkg_cv_LIBEDATESERVER_LIBS { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: yes" >&5 printf "%s\n" "yes" >&6; } fi fi PLUGIN_PHONEBOOK=${plugin_phonebook} ac_config_files="$ac_config_files lib/bluez.pc Makefile mesh/bluetooth-meshd.rst mesh/bluetooth-mesh.service obexd/src/obex.service obexd/src/org.bluez.obex.service src/bluetoothd.rst src/bluetooth.service tools/bluetooth-logger.service tools/mpris-proxy.service" cat >confcache <<\_ACEOF # This file is a shell script that caches the results of configure # tests run on this system so they can be shared between configure # scripts and configure runs, see configure's option --config-cache. # It is not useful on other systems. If it contains results you don't # want to keep, you may remove or edit it. # # config.status only pays attention to the cache file if you give it # the --recheck option to rerun configure. # # 'ac_cv_env_foo' variables (set or unset) will be overridden when # loading this file, other *unset* 'ac_cv_foo' will be assigned the # following values. _ACEOF # The following way of writing the cache mishandles newlines in values, # but we know of no workaround that is simple, portable, and efficient. # So, we kill variables containing newlines. # Ultrix sh set writes to stderr and can't be redirected directly, # and sets the high bit in the cache file unless we assign to the vars. ( for ac_var in `(set) 2>&1 | sed -n 's/^\([a-zA-Z_][a-zA-Z0-9_]*\)=.*/\1/p'`; do eval ac_val=\$$ac_var case $ac_val in #( *${as_nl}*) case $ac_var in #( *_cv_*) { printf "%s\n" "$as_me:${as_lineno-$LINENO}: WARNING: cache variable $ac_var contains a newline" >&5 printf "%s\n" "$as_me: WARNING: cache variable $ac_var contains a newline" >&2;} ;; esac case $ac_var in #( _ | IFS | as_nl) ;; #( BASH_ARGV | BASH_SOURCE) eval $ac_var= ;; #( *) { eval $ac_var=; unset $ac_var;} ;; esac ;; esac done (set) 2>&1 | case $as_nl`(ac_space=' '; set) 2>&1` in #( *${as_nl}ac_space=\ *) # 'set' does not quote correctly, so add quotes: double-quote # substitution turns \\\\ into \\, and sed turns \\ into \. sed -n \ "s/'/'\\\\''/g; s/^\\([_$as_cr_alnum]*_cv_[_$as_cr_alnum]*\\)=\\(.*\\)/\\1='\\2'/p" ;; #( *) # 'set' quotes correctly as required by POSIX, so do not add quotes. sed -n "/^[_$as_cr_alnum]*_cv_[_$as_cr_alnum]*=/p" ;; esac | sort ) | sed ' /^ac_cv_env_/b end t clear :clear s/^\([^=]*\)=\(.*[{}].*\)$/test ${\1+y} || &/ t end s/^\([^=]*\)=\(.*\)$/\1=${\1=\2}/ :end' >>confcache if diff "$cache_file" confcache >/dev/null 2>&1; then :; else if test -w "$cache_file"; then if test "x$cache_file" != "x/dev/null"; then { printf "%s\n" "$as_me:${as_lineno-$LINENO}: updating cache $cache_file" >&5 printf "%s\n" "$as_me: updating cache $cache_file" >&6;} if test ! -f "$cache_file" || test -h "$cache_file"; then cat confcache >"$cache_file" else case $cache_file in #( */* | ?:*) mv -f confcache "$cache_file"$$ && mv -f "$cache_file"$$ "$cache_file" ;; #( *) mv -f confcache "$cache_file" ;; esac fi fi else { printf "%s\n" "$as_me:${as_lineno-$LINENO}: not updating unwritable cache $cache_file" >&5 printf "%s\n" "$as_me: not updating unwritable cache $cache_file" >&6;} fi fi rm -f confcache test "x$prefix" = xNONE && prefix=$ac_default_prefix # Let make expand exec_prefix. test "x$exec_prefix" = xNONE && exec_prefix='${prefix}' DEFS=-DHAVE_CONFIG_H ac_libobjs= ac_ltlibobjs= U= for ac_i in : $LIBOBJS; do test "x$ac_i" = x: && continue # 1. Remove the extension, and $U if already installed. ac_script='s/\$U\././;s/\.o$//;s/\.obj$//' ac_i=`printf "%s\n" "$ac_i" | sed "$ac_script"` # 2. Prepend LIBOBJDIR. When used with automake>=1.10 LIBOBJDIR # will be set to the directory where LIBOBJS objects are built. as_fn_append ac_libobjs " \${LIBOBJDIR}$ac_i\$U.$ac_objext" as_fn_append ac_ltlibobjs " \${LIBOBJDIR}$ac_i"'$U.lo' done LIBOBJS=$ac_libobjs LTLIBOBJS=$ac_ltlibobjs { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking that generated files are newer than configure" >&5 printf %s "checking that generated files are newer than configure... " >&6; } if test -n "$am_sleep_pid"; then # Hide warnings about reused PIDs. wait $am_sleep_pid 2>/dev/null fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: done" >&5 printf "%s\n" "done" >&6; } if test -n "$EXEEXT"; then am__EXEEXT_TRUE= am__EXEEXT_FALSE='#' else am__EXEEXT_TRUE='#' am__EXEEXT_FALSE= fi if test -z "${MAINTAINER_MODE_TRUE}" && test -z "${MAINTAINER_MODE_FALSE}"; then as_fn_error $? "conditional \"MAINTAINER_MODE\" was never defined. Usually this means the macro was only invoked conditionally." "$LINENO" 5 fi if test -z "${AMDEP_TRUE}" && test -z "${AMDEP_FALSE}"; then as_fn_error $? "conditional \"AMDEP\" was never defined. Usually this means the macro was only invoked conditionally." "$LINENO" 5 fi if test -z "${am__fastdepCC_TRUE}" && test -z "${am__fastdepCC_FALSE}"; then as_fn_error $? "conditional \"am__fastdepCC\" was never defined. Usually this means the macro was only invoked conditionally." "$LINENO" 5 fi if test -z "${am__fastdepCC_TRUE}" && test -z "${am__fastdepCC_FALSE}"; then as_fn_error $? "conditional \"am__fastdepCC\" was never defined. Usually this means the macro was only invoked conditionally." "$LINENO" 5 fi if test -z "${COVERAGE_TRUE}" && test -z "${COVERAGE_FALSE}"; then as_fn_error $? "conditional \"COVERAGE\" was never defined. Usually this means the macro was only invoked conditionally." "$LINENO" 5 fi if test -z "${DBUS_RUN_SESSION_TRUE}" && test -z "${DBUS_RUN_SESSION_FALSE}"; then as_fn_error $? "conditional \"DBUS_RUN_SESSION\" was never defined. Usually this means the macro was only invoked conditionally." "$LINENO" 5 fi if test -z "${VALGRIND_TRUE}" && test -z "${VALGRIND_FALSE}"; then as_fn_error $? "conditional \"VALGRIND\" was never defined. Usually this means the macro was only invoked conditionally." "$LINENO" 5 fi if test -z "${ZSH_COMPLETIONS_TRUE}" && test -z "${ZSH_COMPLETIONS_FALSE}"; then as_fn_error $? "conditional \"ZSH_COMPLETIONS\" was never defined. Usually this means the macro was only invoked conditionally." "$LINENO" 5 fi if test -z "${LIBRARY_TRUE}" && test -z "${LIBRARY_FALSE}"; then as_fn_error $? "conditional \"LIBRARY\" was never defined. Usually this means the macro was only invoked conditionally." "$LINENO" 5 fi if test -z "${TEST_TRUE}" && test -z "${TEST_FALSE}"; then as_fn_error $? "conditional \"TEST\" was never defined. Usually this means the macro was only invoked conditionally." "$LINENO" 5 fi if test -z "${NFC_TRUE}" && test -z "${NFC_FALSE}"; then as_fn_error $? "conditional \"NFC\" was never defined. Usually this means the macro was only invoked conditionally." "$LINENO" 5 fi if test -z "${SAP_TRUE}" && test -z "${SAP_FALSE}"; then as_fn_error $? "conditional \"SAP\" was never defined. Usually this means the macro was only invoked conditionally." "$LINENO" 5 fi if test -z "${A2DP_TRUE}" && test -z "${A2DP_FALSE}"; then as_fn_error $? "conditional \"A2DP\" was never defined. Usually this means the macro was only invoked conditionally." "$LINENO" 5 fi if test -z "${AVRCP_TRUE}" && test -z "${AVRCP_FALSE}"; then as_fn_error $? "conditional \"AVRCP\" was never defined. Usually this means the macro was only invoked conditionally." "$LINENO" 5 fi if test -z "${NETWORK_TRUE}" && test -z "${NETWORK_FALSE}"; then as_fn_error $? "conditional \"NETWORK\" was never defined. Usually this means the macro was only invoked conditionally." "$LINENO" 5 fi if test -z "${HID_TRUE}" && test -z "${HID_FALSE}"; then as_fn_error $? "conditional \"HID\" was never defined. Usually this means the macro was only invoked conditionally." "$LINENO" 5 fi if test -z "${HOG_TRUE}" && test -z "${HOG_FALSE}"; then as_fn_error $? "conditional \"HOG\" was never defined. Usually this means the macro was only invoked conditionally." "$LINENO" 5 fi if test -z "${HEALTH_TRUE}" && test -z "${HEALTH_FALSE}"; then as_fn_error $? "conditional \"HEALTH\" was never defined. Usually this means the macro was only invoked conditionally." "$LINENO" 5 fi if test -z "${BAP_TRUE}" && test -z "${BAP_FALSE}"; then as_fn_error $? "conditional \"BAP\" was never defined. Usually this means the macro was only invoked conditionally." "$LINENO" 5 fi if test -z "${BASS_TRUE}" && test -z "${BASS_FALSE}"; then as_fn_error $? "conditional \"BASS\" was never defined. Usually this means the macro was only invoked conditionally." "$LINENO" 5 fi if test -z "${MCP_TRUE}" && test -z "${MCP_FALSE}"; then as_fn_error $? "conditional \"MCP\" was never defined. Usually this means the macro was only invoked conditionally." "$LINENO" 5 fi if test -z "${CCP_TRUE}" && test -z "${CCP_FALSE}"; then as_fn_error $? "conditional \"CCP\" was never defined. Usually this means the macro was only invoked conditionally." "$LINENO" 5 fi if test -z "${VCP_TRUE}" && test -z "${VCP_FALSE}"; then as_fn_error $? "conditional \"VCP\" was never defined. Usually this means the macro was only invoked conditionally." "$LINENO" 5 fi if test -z "${MICP_TRUE}" && test -z "${MICP_FALSE}"; then as_fn_error $? "conditional \"MICP\" was never defined. Usually this means the macro was only invoked conditionally." "$LINENO" 5 fi if test -z "${CSIP_TRUE}" && test -z "${CSIP_FALSE}"; then as_fn_error $? "conditional \"CSIP\" was never defined. Usually this means the macro was only invoked conditionally." "$LINENO" 5 fi if test -z "${ASHA_TRUE}" && test -z "${ASHA_FALSE}"; then as_fn_error $? "conditional \"ASHA\" was never defined. Usually this means the macro was only invoked conditionally." "$LINENO" 5 fi if test -z "${TOOLS_TRUE}" && test -z "${TOOLS_FALSE}"; then as_fn_error $? "conditional \"TOOLS\" was never defined. Usually this means the macro was only invoked conditionally." "$LINENO" 5 fi if test -z "${MONITOR_TRUE}" && test -z "${MONITOR_FALSE}"; then as_fn_error $? "conditional \"MONITOR\" was never defined. Usually this means the macro was only invoked conditionally." "$LINENO" 5 fi if test -z "${CUPS_TRUE}" && test -z "${CUPS_FALSE}"; then as_fn_error $? "conditional \"CUPS\" was never defined. Usually this means the macro was only invoked conditionally." "$LINENO" 5 fi if test -z "${CUPS_SERVERBIN_TRUE}" && test -z "${CUPS_SERVERBIN_FALSE}"; then as_fn_error $? "conditional \"CUPS_SERVERBIN\" was never defined. Usually this means the macro was only invoked conditionally." "$LINENO" 5 fi if test -z "${MESH_TRUE}" && test -z "${MESH_FALSE}"; then as_fn_error $? "conditional \"MESH\" was never defined. Usually this means the macro was only invoked conditionally." "$LINENO" 5 fi if test -z "${MIDI_TRUE}" && test -z "${MIDI_FALSE}"; then as_fn_error $? "conditional \"MIDI\" was never defined. Usually this means the macro was only invoked conditionally." "$LINENO" 5 fi if test -z "${OBEX_TRUE}" && test -z "${OBEX_FALSE}"; then as_fn_error $? "conditional \"OBEX\" was never defined. Usually this means the macro was only invoked conditionally." "$LINENO" 5 fi if test -z "${BTPCLIENT_TRUE}" && test -z "${BTPCLIENT_FALSE}"; then as_fn_error $? "conditional \"BTPCLIENT\" was never defined. Usually this means the macro was only invoked conditionally." "$LINENO" 5 fi if test -z "${EXTERNAL_ELL_TRUE}" && test -z "${EXTERNAL_ELL_FALSE}"; then as_fn_error $? "conditional \"EXTERNAL_ELL\" was never defined. Usually this means the macro was only invoked conditionally." "$LINENO" 5 fi if test -z "${LIBSHARED_ELL_TRUE}" && test -z "${LIBSHARED_ELL_FALSE}"; then as_fn_error $? "conditional \"LIBSHARED_ELL\" was never defined. Usually this means the macro was only invoked conditionally." "$LINENO" 5 fi if test -z "${CLIENT_TRUE}" && test -z "${CLIENT_FALSE}"; then as_fn_error $? "conditional \"CLIENT\" was never defined. Usually this means the macro was only invoked conditionally." "$LINENO" 5 fi if test -z "${READLINE_TRUE}" && test -z "${READLINE_FALSE}"; then as_fn_error $? "conditional \"READLINE\" was never defined. Usually this means the macro was only invoked conditionally." "$LINENO" 5 fi if test -z "${SYSTEMD_TRUE}" && test -z "${SYSTEMD_FALSE}"; then as_fn_error $? "conditional \"SYSTEMD\" was never defined. Usually this means the macro was only invoked conditionally." "$LINENO" 5 fi if test -z "${DATAFILES_TRUE}" && test -z "${DATAFILES_FALSE}"; then as_fn_error $? "conditional \"DATAFILES\" was never defined. Usually this means the macro was only invoked conditionally." "$LINENO" 5 fi if test -z "${MANPAGES_TRUE}" && test -z "${MANPAGES_FALSE}"; then as_fn_error $? "conditional \"MANPAGES\" was never defined. Usually this means the macro was only invoked conditionally." "$LINENO" 5 fi if test -z "${RUN_RST2MAN_TRUE}" && test -z "${RUN_RST2MAN_FALSE}"; then as_fn_error $? "conditional \"RUN_RST2MAN\" was never defined. Usually this means the macro was only invoked conditionally." "$LINENO" 5 fi if test -z "${TESTING_TRUE}" && test -z "${TESTING_FALSE}"; then as_fn_error $? "conditional \"TESTING\" was never defined. Usually this means the macro was only invoked conditionally." "$LINENO" 5 fi if test -z "${EXPERIMENTAL_TRUE}" && test -z "${EXPERIMENTAL_FALSE}"; then as_fn_error $? "conditional \"EXPERIMENTAL\" was never defined. Usually this means the macro was only invoked conditionally." "$LINENO" 5 fi if test -z "${DEPRECATED_TRUE}" && test -z "${DEPRECATED_FALSE}"; then as_fn_error $? "conditional \"DEPRECATED\" was never defined. Usually this means the macro was only invoked conditionally." "$LINENO" 5 fi if test -z "${EXTERNAL_PLUGINS_TRUE}" && test -z "${EXTERNAL_PLUGINS_FALSE}"; then as_fn_error $? "conditional \"EXTERNAL_PLUGINS\" was never defined. Usually this means the macro was only invoked conditionally." "$LINENO" 5 fi if test -z "${SIXAXIS_TRUE}" && test -z "${SIXAXIS_FALSE}"; then as_fn_error $? "conditional \"SIXAXIS\" was never defined. Usually this means the macro was only invoked conditionally." "$LINENO" 5 fi if test -z "${HID2HCI_TRUE}" && test -z "${HID2HCI_FALSE}"; then as_fn_error $? "conditional \"HID2HCI\" was never defined. Usually this means the macro was only invoked conditionally." "$LINENO" 5 fi if test -z "${LOGGER_TRUE}" && test -z "${LOGGER_FALSE}"; then as_fn_error $? "conditional \"LOGGER\" was never defined. Usually this means the macro was only invoked conditionally." "$LINENO" 5 fi if test -z "${ADMIN_TRUE}" && test -z "${ADMIN_FALSE}"; then as_fn_error $? "conditional \"ADMIN\" was never defined. Usually this means the macro was only invoked conditionally." "$LINENO" 5 fi if test -z "${ANDROID_TRUE}" && test -z "${ANDROID_FALSE}"; then as_fn_error $? "conditional \"ANDROID\" was never defined. Usually this means the macro was only invoked conditionally." "$LINENO" 5 fi : "${CONFIG_STATUS=./config.status}" ac_write_fail=0 ac_clean_files_save=$ac_clean_files ac_clean_files="$ac_clean_files $CONFIG_STATUS" { printf "%s\n" "$as_me:${as_lineno-$LINENO}: creating $CONFIG_STATUS" >&5 printf "%s\n" "$as_me: creating $CONFIG_STATUS" >&6;} as_write_fail=0 cat >$CONFIG_STATUS <<_ASEOF || as_write_fail=1 #! $SHELL # Generated by $as_me. # Run this file to recreate the current configuration. # Compiler output produced by configure, useful for debugging # configure, is in config.log if it exists. debug=false ac_cs_recheck=false ac_cs_silent=false SHELL=\${CONFIG_SHELL-$SHELL} export SHELL _ASEOF cat >>$CONFIG_STATUS <<\_ASEOF || as_write_fail=1 ## -------------------- ## ## M4sh Initialization. ## ## -------------------- ## # Be more Bourne compatible DUALCASE=1; export DUALCASE # for MKS sh if test ${ZSH_VERSION+y} && (emulate sh) >/dev/null 2>&1 then : emulate sh NULLCMD=: # Pre-4.2 versions of Zsh do word splitting on ${1+"$@"}, which # is contrary to our usage. Disable this feature. alias -g '${1+"$@"}'='"$@"' setopt NO_GLOB_SUBST else case e in #( e) case `(set -o) 2>/dev/null` in #( *posix*) : set -o posix ;; #( *) : ;; esac ;; esac fi # Reset variables that may have inherited troublesome values from # the environment. # IFS needs to be set, to space, tab, and newline, in precisely that order. # (If _AS_PATH_WALK were called with IFS unset, it would have the # side effect of setting IFS to empty, thus disabling word splitting.) # Quoting is to prevent editors from complaining about space-tab. as_nl=' ' export as_nl IFS=" "" $as_nl" PS1='$ ' PS2='> ' PS4='+ ' # Ensure predictable behavior from utilities with locale-dependent output. LC_ALL=C export LC_ALL LANGUAGE=C export LANGUAGE # We cannot yet rely on "unset" to work, but we need these variables # to be unset--not just set to an empty or harmless value--now, to # avoid bugs in old shells (e.g. pre-3.0 UWIN ksh). This construct # also avoids known problems related to "unset" and subshell syntax # in other old shells (e.g. bash 2.01 and pdksh 5.2.14). for as_var in BASH_ENV ENV MAIL MAILPATH CDPATH do eval test \${$as_var+y} \ && ( (unset $as_var) || exit 1) >/dev/null 2>&1 && unset $as_var || : done # Ensure that fds 0, 1, and 2 are open. if (exec 3>&0) 2>/dev/null; then :; else exec 0&1) 2>/dev/null; then :; else exec 1>/dev/null; fi if (exec 3>&2) ; then :; else exec 2>/dev/null; fi # The user is always right. if ${PATH_SEPARATOR+false} :; then PATH_SEPARATOR=: (PATH='/bin;/bin'; FPATH=$PATH; sh -c :) >/dev/null 2>&1 && { (PATH='/bin:/bin'; FPATH=$PATH; sh -c :) >/dev/null 2>&1 || PATH_SEPARATOR=';' } fi # Find who we are. Look in the path if we contain no directory separator. as_myself= case $0 in #(( *[\\/]* ) as_myself=$0 ;; *) as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS case $as_dir in #((( '') as_dir=./ ;; */) ;; *) as_dir=$as_dir/ ;; esac test -r "$as_dir$0" && as_myself=$as_dir$0 && break done IFS=$as_save_IFS ;; esac # We did not find ourselves, most probably we were run as 'sh COMMAND' # in which case we are not to be found in the path. if test "x$as_myself" = x; then as_myself=$0 fi if test ! -f "$as_myself"; then printf "%s\n" "$as_myself: error: cannot find myself; rerun with an absolute file name" >&2 exit 1 fi # as_fn_error STATUS ERROR [LINENO LOG_FD] # ---------------------------------------- # Output "`basename $0`: error: ERROR" to stderr. If LINENO and LOG_FD are # provided, also output the error to LOG_FD, referencing LINENO. Then exit the # script with STATUS, using 1 if that was 0. as_fn_error () { as_status=$1; test $as_status -eq 0 && as_status=1 if test "$4"; then as_lineno=${as_lineno-"$3"} as_lineno_stack=as_lineno_stack=$as_lineno_stack printf "%s\n" "$as_me:${as_lineno-$LINENO}: error: $2" >&$4 fi printf "%s\n" "$as_me: error: $2" >&2 as_fn_exit $as_status } # as_fn_error # as_fn_set_status STATUS # ----------------------- # Set $? to STATUS, without forking. as_fn_set_status () { return $1 } # as_fn_set_status # as_fn_exit STATUS # ----------------- # Exit the shell with STATUS, even in a "trap 0" or "set -e" context. as_fn_exit () { set +e as_fn_set_status $1 exit $1 } # as_fn_exit # as_fn_unset VAR # --------------- # Portably unset VAR. as_fn_unset () { { eval $1=; unset $1;} } as_unset=as_fn_unset # as_fn_append VAR VALUE # ---------------------- # Append the text in VALUE to the end of the definition contained in VAR. Take # advantage of any shell optimizations that allow amortized linear growth over # repeated appends, instead of the typical quadratic growth present in naive # implementations. if (eval "as_var=1; as_var+=2; test x\$as_var = x12") 2>/dev/null then : eval 'as_fn_append () { eval $1+=\$2 }' else case e in #( e) as_fn_append () { eval $1=\$$1\$2 } ;; esac fi # as_fn_append # as_fn_arith ARG... # ------------------ # Perform arithmetic evaluation on the ARGs, and store the result in the # global $as_val. Take advantage of shells that can avoid forks. The arguments # must be portable across $(()) and expr. if (eval "test \$(( 1 + 1 )) = 2") 2>/dev/null then : eval 'as_fn_arith () { as_val=$(( $* )) }' else case e in #( e) as_fn_arith () { as_val=`expr "$@" || test $? -eq 1` } ;; esac fi # as_fn_arith if expr a : '\(a\)' >/dev/null 2>&1 && test "X`expr 00001 : '.*\(...\)'`" = X001; then as_expr=expr else as_expr=false fi if (basename -- /) >/dev/null 2>&1 && test "X`basename -- / 2>&1`" = "X/"; then as_basename=basename else as_basename=false fi if (as_dir=`dirname -- /` && test "X$as_dir" = X/) >/dev/null 2>&1; then as_dirname=dirname else as_dirname=false fi as_me=`$as_basename -- "$0" || $as_expr X/"$0" : '.*/\([^/][^/]*\)/*$' \| \ X"$0" : 'X\(//\)$' \| \ X"$0" : 'X\(/\)' \| . 2>/dev/null || printf "%s\n" X/"$0" | sed '/^.*\/\([^/][^/]*\)\/*$/{ s//\1/ q } /^X\/\(\/\/\)$/{ s//\1/ q } /^X\/\(\/\).*/{ s//\1/ q } s/.*/./; q'` # Avoid depending upon Character Ranges. as_cr_letters='abcdefghijklmnopqrstuvwxyz' as_cr_LETTERS='ABCDEFGHIJKLMNOPQRSTUVWXYZ' as_cr_Letters=$as_cr_letters$as_cr_LETTERS as_cr_digits='0123456789' as_cr_alnum=$as_cr_Letters$as_cr_digits # Determine whether it's possible to make 'echo' print without a newline. # These variables are no longer used directly by Autoconf, but are AC_SUBSTed # for compatibility with existing Makefiles. ECHO_C= ECHO_N= ECHO_T= case `echo -n x` in #((((( -n*) case `echo 'xy\c'` in *c*) ECHO_T=' ';; # ECHO_T is single tab character. xy) ECHO_C='\c';; *) echo `echo ksh88 bug on AIX 6.1` > /dev/null ECHO_T=' ';; esac;; *) ECHO_N='-n';; esac # For backward compatibility with old third-party macros, we provide # the shell variables $as_echo and $as_echo_n. New code should use # AS_ECHO(["message"]) and AS_ECHO_N(["message"]), respectively. as_echo='printf %s\n' as_echo_n='printf %s' rm -f conf$$ conf$$.exe conf$$.file if test -d conf$$.dir; then rm -f conf$$.dir/conf$$.file else rm -f conf$$.dir mkdir conf$$.dir 2>/dev/null fi if (echo >conf$$.file) 2>/dev/null; then if ln -s conf$$.file conf$$ 2>/dev/null; then as_ln_s='ln -s' # ... but there are two gotchas: # 1) On MSYS, both 'ln -s file dir' and 'ln file dir' fail. # 2) DJGPP < 2.04 has no symlinks; 'ln -s' creates a wrapper executable. # In both cases, we have to default to 'cp -pR'. ln -s conf$$.file conf$$.dir 2>/dev/null && test ! -f conf$$.exe || as_ln_s='cp -pR' elif ln conf$$.file conf$$ 2>/dev/null; then as_ln_s=ln else as_ln_s='cp -pR' fi else as_ln_s='cp -pR' fi rm -f conf$$ conf$$.exe conf$$.dir/conf$$.file conf$$.file rmdir conf$$.dir 2>/dev/null # as_fn_mkdir_p # ------------- # Create "$as_dir" as a directory, including parents if necessary. as_fn_mkdir_p () { case $as_dir in #( -*) as_dir=./$as_dir;; esac test -d "$as_dir" || eval $as_mkdir_p || { as_dirs= while :; do case $as_dir in #( *\'*) as_qdir=`printf "%s\n" "$as_dir" | sed "s/'/'\\\\\\\\''/g"`;; #'( *) as_qdir=$as_dir;; esac as_dirs="'$as_qdir' $as_dirs" as_dir=`$as_dirname -- "$as_dir" || $as_expr X"$as_dir" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ X"$as_dir" : 'X\(//\)[^/]' \| \ X"$as_dir" : 'X\(//\)$' \| \ X"$as_dir" : 'X\(/\)' \| . 2>/dev/null || printf "%s\n" X"$as_dir" | sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ s//\1/ q } /^X\(\/\/\)[^/].*/{ s//\1/ q } /^X\(\/\/\)$/{ s//\1/ q } /^X\(\/\).*/{ s//\1/ q } s/.*/./; q'` test -d "$as_dir" && break done test -z "$as_dirs" || eval "mkdir $as_dirs" } || test -d "$as_dir" || as_fn_error $? "cannot create directory $as_dir" } # as_fn_mkdir_p if mkdir -p . 2>/dev/null; then as_mkdir_p='mkdir -p "$as_dir"' else test -d ./-p && rmdir ./-p as_mkdir_p=false fi # as_fn_executable_p FILE # ----------------------- # Test if FILE is an executable regular file. as_fn_executable_p () { test -f "$1" && test -x "$1" } # as_fn_executable_p as_test_x='test -x' as_executable_p=as_fn_executable_p # Sed expression to map a string onto a valid CPP name. as_sed_cpp="y%*$as_cr_letters%P$as_cr_LETTERS%;s%[^_$as_cr_alnum]%_%g" as_tr_cpp="eval sed '$as_sed_cpp'" # deprecated # Sed expression to map a string onto a valid variable name. as_sed_sh="y%*+%pp%;s%[^_$as_cr_alnum]%_%g" as_tr_sh="eval sed '$as_sed_sh'" # deprecated exec 6>&1 ## ----------------------------------- ## ## Main body of $CONFIG_STATUS script. ## ## ----------------------------------- ## _ASEOF test $as_write_fail = 0 && chmod +x $CONFIG_STATUS || ac_write_fail=1 cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 # Save the log message, to keep $0 and so on meaningful, and to # report actual input values of CONFIG_FILES etc. instead of their # values after options handling. ac_log=" This file was extended by bluez $as_me 5.82, which was generated by GNU Autoconf 2.72. Invocation command line was CONFIG_FILES = $CONFIG_FILES CONFIG_HEADERS = $CONFIG_HEADERS CONFIG_LINKS = $CONFIG_LINKS CONFIG_COMMANDS = $CONFIG_COMMANDS $ $0 $@ on `(hostname || uname -n) 2>/dev/null | sed 1q` " _ACEOF case $ac_config_files in *" "*) set x $ac_config_files; shift; ac_config_files=$*;; esac case $ac_config_headers in *" "*) set x $ac_config_headers; shift; ac_config_headers=$*;; esac cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 # Files that config.status was made for. config_files="$ac_config_files" config_headers="$ac_config_headers" config_commands="$ac_config_commands" _ACEOF cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 ac_cs_usage="\ '$as_me' instantiates files and other configuration actions from templates according to the current configuration. Unless the files and actions are specified as TAGs, all are instantiated by default. Usage: $0 [OPTION]... [TAG]... -h, --help print this help, then exit -V, --version print version number and configuration settings, then exit --config print configuration, then exit -q, --quiet, --silent do not print progress messages -d, --debug don't remove temporary files --recheck update $as_me by reconfiguring in the same conditions --file=FILE[:TEMPLATE] instantiate the configuration file FILE --header=FILE[:TEMPLATE] instantiate the configuration header FILE Configuration files: $config_files Configuration headers: $config_headers Configuration commands: $config_commands Report bugs to the package provider." _ACEOF ac_cs_config=`printf "%s\n" "$ac_configure_args" | sed "$ac_safe_unquote"` ac_cs_config_escaped=`printf "%s\n" "$ac_cs_config" | sed "s/^ //; s/'/'\\\\\\\\''/g"` cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 ac_cs_config='$ac_cs_config_escaped' ac_cs_version="\\ bluez config.status 5.82 configured by $0, generated by GNU Autoconf 2.72, with options \\"\$ac_cs_config\\" Copyright (C) 2023 Free Software Foundation, Inc. This config.status script is free software; the Free Software Foundation gives unlimited permission to copy, distribute and modify it." ac_pwd='$ac_pwd' srcdir='$srcdir' INSTALL='$INSTALL' MKDIR_P='$MKDIR_P' AWK='$AWK' test -n "\$AWK" || AWK=awk _ACEOF cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 # The default lists apply if the user does not specify any file. ac_need_defaults=: while test $# != 0 do case $1 in --*=?*) ac_option=`expr "X$1" : 'X\([^=]*\)='` ac_optarg=`expr "X$1" : 'X[^=]*=\(.*\)'` ac_shift=: ;; --*=) ac_option=`expr "X$1" : 'X\([^=]*\)='` ac_optarg= ac_shift=: ;; *) ac_option=$1 ac_optarg=$2 ac_shift=shift ;; esac case $ac_option in # Handling of the options. -recheck | --recheck | --rechec | --reche | --rech | --rec | --re | --r) ac_cs_recheck=: ;; --version | --versio | --versi | --vers | --ver | --ve | --v | -V ) printf "%s\n" "$ac_cs_version"; exit ;; --config | --confi | --conf | --con | --co | --c ) printf "%s\n" "$ac_cs_config"; exit ;; --debug | --debu | --deb | --de | --d | -d ) debug=: ;; --file | --fil | --fi | --f ) $ac_shift case $ac_optarg in *\'*) ac_optarg=`printf "%s\n" "$ac_optarg" | sed "s/'/'\\\\\\\\''/g"` ;; '') as_fn_error $? "missing file argument" ;; esac as_fn_append CONFIG_FILES " '$ac_optarg'" ac_need_defaults=false;; --header | --heade | --head | --hea ) $ac_shift case $ac_optarg in *\'*) ac_optarg=`printf "%s\n" "$ac_optarg" | sed "s/'/'\\\\\\\\''/g"` ;; esac as_fn_append CONFIG_HEADERS " '$ac_optarg'" ac_need_defaults=false;; --he | --h) # Conflict between --help and --header as_fn_error $? "ambiguous option: '$1' Try '$0 --help' for more information.";; --help | --hel | -h ) printf "%s\n" "$ac_cs_usage"; exit ;; -q | -quiet | --quiet | --quie | --qui | --qu | --q \ | -silent | --silent | --silen | --sile | --sil | --si | --s) ac_cs_silent=: ;; # This is an error. -*) as_fn_error $? "unrecognized option: '$1' Try '$0 --help' for more information." ;; *) as_fn_append ac_config_targets " $1" ac_need_defaults=false ;; esac shift done ac_configure_extra_args= if $ac_cs_silent; then exec 6>/dev/null ac_configure_extra_args="$ac_configure_extra_args --silent" fi _ACEOF cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 if \$ac_cs_recheck; then set X $SHELL '$0' $ac_configure_args \$ac_configure_extra_args --no-create --no-recursion shift \printf "%s\n" "running CONFIG_SHELL=$SHELL \$*" >&6 CONFIG_SHELL='$SHELL' export CONFIG_SHELL exec "\$@" fi _ACEOF cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 exec 5>>config.log { echo sed 'h;s/./-/g;s/^.../## /;s/...$/ ##/;p;x;p;x' <<_ASBOX ## Running $as_me. ## _ASBOX printf "%s\n" "$ac_log" } >&5 _ACEOF cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 # # INIT-COMMANDS # AMDEP_TRUE="$AMDEP_TRUE" MAKE="${MAKE-make}" # The HP-UX ksh and POSIX shell print the target directory to stdout # if CDPATH is set. (unset CDPATH) >/dev/null 2>&1 && unset CDPATH sed_quote_subst='$sed_quote_subst' double_quote_subst='$double_quote_subst' delay_variable_subst='$delay_variable_subst' macro_version='`$ECHO "$macro_version" | $SED "$delay_single_quote_subst"`' macro_revision='`$ECHO "$macro_revision" | $SED "$delay_single_quote_subst"`' enable_static='`$ECHO "$enable_static" | $SED "$delay_single_quote_subst"`' enable_shared='`$ECHO "$enable_shared" | $SED "$delay_single_quote_subst"`' pic_mode='`$ECHO "$pic_mode" | $SED "$delay_single_quote_subst"`' enable_fast_install='`$ECHO "$enable_fast_install" | $SED "$delay_single_quote_subst"`' shared_archive_member_spec='`$ECHO "$shared_archive_member_spec" | $SED "$delay_single_quote_subst"`' SHELL='`$ECHO "$SHELL" | $SED "$delay_single_quote_subst"`' ECHO='`$ECHO "$ECHO" | $SED "$delay_single_quote_subst"`' PATH_SEPARATOR='`$ECHO "$PATH_SEPARATOR" | $SED "$delay_single_quote_subst"`' host_alias='`$ECHO "$host_alias" | $SED "$delay_single_quote_subst"`' host='`$ECHO "$host" | $SED "$delay_single_quote_subst"`' host_os='`$ECHO "$host_os" | $SED "$delay_single_quote_subst"`' build_alias='`$ECHO "$build_alias" | $SED "$delay_single_quote_subst"`' build='`$ECHO "$build" | $SED "$delay_single_quote_subst"`' build_os='`$ECHO "$build_os" | $SED "$delay_single_quote_subst"`' SED='`$ECHO "$SED" | $SED "$delay_single_quote_subst"`' Xsed='`$ECHO "$Xsed" | $SED "$delay_single_quote_subst"`' GREP='`$ECHO "$GREP" | $SED "$delay_single_quote_subst"`' EGREP='`$ECHO "$EGREP" | $SED "$delay_single_quote_subst"`' FGREP='`$ECHO "$FGREP" | $SED "$delay_single_quote_subst"`' LD='`$ECHO "$LD" | $SED "$delay_single_quote_subst"`' NM='`$ECHO "$NM" | $SED "$delay_single_quote_subst"`' LN_S='`$ECHO "$LN_S" | $SED "$delay_single_quote_subst"`' max_cmd_len='`$ECHO "$max_cmd_len" | $SED "$delay_single_quote_subst"`' ac_objext='`$ECHO "$ac_objext" | $SED "$delay_single_quote_subst"`' exeext='`$ECHO "$exeext" | $SED "$delay_single_quote_subst"`' lt_unset='`$ECHO "$lt_unset" | $SED "$delay_single_quote_subst"`' lt_SP2NL='`$ECHO "$lt_SP2NL" | $SED "$delay_single_quote_subst"`' lt_NL2SP='`$ECHO "$lt_NL2SP" | $SED "$delay_single_quote_subst"`' lt_cv_to_host_file_cmd='`$ECHO "$lt_cv_to_host_file_cmd" | $SED "$delay_single_quote_subst"`' lt_cv_to_tool_file_cmd='`$ECHO "$lt_cv_to_tool_file_cmd" | $SED "$delay_single_quote_subst"`' reload_flag='`$ECHO "$reload_flag" | $SED "$delay_single_quote_subst"`' reload_cmds='`$ECHO "$reload_cmds" | $SED "$delay_single_quote_subst"`' FILECMD='`$ECHO "$FILECMD" | $SED "$delay_single_quote_subst"`' OBJDUMP='`$ECHO "$OBJDUMP" | $SED "$delay_single_quote_subst"`' deplibs_check_method='`$ECHO "$deplibs_check_method" | $SED "$delay_single_quote_subst"`' file_magic_cmd='`$ECHO "$file_magic_cmd" | $SED "$delay_single_quote_subst"`' file_magic_glob='`$ECHO "$file_magic_glob" | $SED "$delay_single_quote_subst"`' want_nocaseglob='`$ECHO "$want_nocaseglob" | $SED "$delay_single_quote_subst"`' DLLTOOL='`$ECHO "$DLLTOOL" | $SED "$delay_single_quote_subst"`' sharedlib_from_linklib_cmd='`$ECHO "$sharedlib_from_linklib_cmd" | $SED "$delay_single_quote_subst"`' AR='`$ECHO "$AR" | $SED "$delay_single_quote_subst"`' lt_ar_flags='`$ECHO "$lt_ar_flags" | $SED "$delay_single_quote_subst"`' AR_FLAGS='`$ECHO "$AR_FLAGS" | $SED "$delay_single_quote_subst"`' archiver_list_spec='`$ECHO "$archiver_list_spec" | $SED "$delay_single_quote_subst"`' STRIP='`$ECHO "$STRIP" | $SED "$delay_single_quote_subst"`' RANLIB='`$ECHO "$RANLIB" | $SED "$delay_single_quote_subst"`' old_postinstall_cmds='`$ECHO "$old_postinstall_cmds" | $SED "$delay_single_quote_subst"`' old_postuninstall_cmds='`$ECHO "$old_postuninstall_cmds" | $SED "$delay_single_quote_subst"`' old_archive_cmds='`$ECHO "$old_archive_cmds" | $SED "$delay_single_quote_subst"`' lock_old_archive_extraction='`$ECHO "$lock_old_archive_extraction" | $SED "$delay_single_quote_subst"`' CC='`$ECHO "$CC" | $SED "$delay_single_quote_subst"`' CFLAGS='`$ECHO "$CFLAGS" | $SED "$delay_single_quote_subst"`' compiler='`$ECHO "$compiler" | $SED "$delay_single_quote_subst"`' GCC='`$ECHO "$GCC" | $SED "$delay_single_quote_subst"`' lt_cv_sys_global_symbol_pipe='`$ECHO "$lt_cv_sys_global_symbol_pipe" | $SED "$delay_single_quote_subst"`' lt_cv_sys_global_symbol_to_cdecl='`$ECHO "$lt_cv_sys_global_symbol_to_cdecl" | $SED "$delay_single_quote_subst"`' lt_cv_sys_global_symbol_to_import='`$ECHO "$lt_cv_sys_global_symbol_to_import" | $SED "$delay_single_quote_subst"`' lt_cv_sys_global_symbol_to_c_name_address='`$ECHO "$lt_cv_sys_global_symbol_to_c_name_address" | $SED "$delay_single_quote_subst"`' lt_cv_sys_global_symbol_to_c_name_address_lib_prefix='`$ECHO "$lt_cv_sys_global_symbol_to_c_name_address_lib_prefix" | $SED "$delay_single_quote_subst"`' lt_cv_nm_interface='`$ECHO "$lt_cv_nm_interface" | $SED "$delay_single_quote_subst"`' nm_file_list_spec='`$ECHO "$nm_file_list_spec" | $SED "$delay_single_quote_subst"`' lt_sysroot='`$ECHO "$lt_sysroot" | $SED "$delay_single_quote_subst"`' lt_cv_truncate_bin='`$ECHO "$lt_cv_truncate_bin" | $SED "$delay_single_quote_subst"`' objdir='`$ECHO "$objdir" | $SED "$delay_single_quote_subst"`' MAGIC_CMD='`$ECHO "$MAGIC_CMD" | $SED "$delay_single_quote_subst"`' lt_prog_compiler_no_builtin_flag='`$ECHO "$lt_prog_compiler_no_builtin_flag" | $SED "$delay_single_quote_subst"`' lt_prog_compiler_pic='`$ECHO "$lt_prog_compiler_pic" | $SED "$delay_single_quote_subst"`' lt_prog_compiler_wl='`$ECHO "$lt_prog_compiler_wl" | $SED "$delay_single_quote_subst"`' lt_prog_compiler_static='`$ECHO "$lt_prog_compiler_static" | $SED "$delay_single_quote_subst"`' lt_cv_prog_compiler_c_o='`$ECHO "$lt_cv_prog_compiler_c_o" | $SED "$delay_single_quote_subst"`' need_locks='`$ECHO "$need_locks" | $SED "$delay_single_quote_subst"`' MANIFEST_TOOL='`$ECHO "$MANIFEST_TOOL" | $SED "$delay_single_quote_subst"`' DSYMUTIL='`$ECHO "$DSYMUTIL" | $SED "$delay_single_quote_subst"`' NMEDIT='`$ECHO "$NMEDIT" | $SED "$delay_single_quote_subst"`' LIPO='`$ECHO "$LIPO" | $SED "$delay_single_quote_subst"`' OTOOL='`$ECHO "$OTOOL" | $SED "$delay_single_quote_subst"`' OTOOL64='`$ECHO "$OTOOL64" | $SED "$delay_single_quote_subst"`' libext='`$ECHO "$libext" | $SED "$delay_single_quote_subst"`' shrext_cmds='`$ECHO "$shrext_cmds" | $SED "$delay_single_quote_subst"`' extract_expsyms_cmds='`$ECHO "$extract_expsyms_cmds" | $SED "$delay_single_quote_subst"`' archive_cmds_need_lc='`$ECHO "$archive_cmds_need_lc" | $SED "$delay_single_quote_subst"`' enable_shared_with_static_runtimes='`$ECHO "$enable_shared_with_static_runtimes" | $SED "$delay_single_quote_subst"`' export_dynamic_flag_spec='`$ECHO "$export_dynamic_flag_spec" | $SED "$delay_single_quote_subst"`' whole_archive_flag_spec='`$ECHO "$whole_archive_flag_spec" | $SED "$delay_single_quote_subst"`' compiler_needs_object='`$ECHO "$compiler_needs_object" | $SED "$delay_single_quote_subst"`' old_archive_from_new_cmds='`$ECHO "$old_archive_from_new_cmds" | $SED "$delay_single_quote_subst"`' old_archive_from_expsyms_cmds='`$ECHO "$old_archive_from_expsyms_cmds" | $SED "$delay_single_quote_subst"`' archive_cmds='`$ECHO "$archive_cmds" | $SED "$delay_single_quote_subst"`' archive_expsym_cmds='`$ECHO "$archive_expsym_cmds" | $SED "$delay_single_quote_subst"`' module_cmds='`$ECHO "$module_cmds" | $SED "$delay_single_quote_subst"`' module_expsym_cmds='`$ECHO "$module_expsym_cmds" | $SED "$delay_single_quote_subst"`' with_gnu_ld='`$ECHO "$with_gnu_ld" | $SED "$delay_single_quote_subst"`' allow_undefined_flag='`$ECHO "$allow_undefined_flag" | $SED "$delay_single_quote_subst"`' no_undefined_flag='`$ECHO "$no_undefined_flag" | $SED "$delay_single_quote_subst"`' hardcode_libdir_flag_spec='`$ECHO "$hardcode_libdir_flag_spec" | $SED "$delay_single_quote_subst"`' hardcode_libdir_separator='`$ECHO "$hardcode_libdir_separator" | $SED "$delay_single_quote_subst"`' hardcode_direct='`$ECHO "$hardcode_direct" | $SED "$delay_single_quote_subst"`' hardcode_direct_absolute='`$ECHO "$hardcode_direct_absolute" | $SED "$delay_single_quote_subst"`' hardcode_minus_L='`$ECHO "$hardcode_minus_L" | $SED "$delay_single_quote_subst"`' hardcode_shlibpath_var='`$ECHO "$hardcode_shlibpath_var" | $SED "$delay_single_quote_subst"`' hardcode_automatic='`$ECHO "$hardcode_automatic" | $SED "$delay_single_quote_subst"`' inherit_rpath='`$ECHO "$inherit_rpath" | $SED "$delay_single_quote_subst"`' link_all_deplibs='`$ECHO "$link_all_deplibs" | $SED "$delay_single_quote_subst"`' always_export_symbols='`$ECHO "$always_export_symbols" | $SED "$delay_single_quote_subst"`' export_symbols_cmds='`$ECHO "$export_symbols_cmds" | $SED "$delay_single_quote_subst"`' exclude_expsyms='`$ECHO "$exclude_expsyms" | $SED "$delay_single_quote_subst"`' include_expsyms='`$ECHO "$include_expsyms" | $SED "$delay_single_quote_subst"`' prelink_cmds='`$ECHO "$prelink_cmds" | $SED "$delay_single_quote_subst"`' postlink_cmds='`$ECHO "$postlink_cmds" | $SED "$delay_single_quote_subst"`' file_list_spec='`$ECHO "$file_list_spec" | $SED "$delay_single_quote_subst"`' variables_saved_for_relink='`$ECHO "$variables_saved_for_relink" | $SED "$delay_single_quote_subst"`' need_lib_prefix='`$ECHO "$need_lib_prefix" | $SED "$delay_single_quote_subst"`' need_version='`$ECHO "$need_version" | $SED "$delay_single_quote_subst"`' version_type='`$ECHO "$version_type" | $SED "$delay_single_quote_subst"`' runpath_var='`$ECHO "$runpath_var" | $SED "$delay_single_quote_subst"`' shlibpath_var='`$ECHO "$shlibpath_var" | $SED "$delay_single_quote_subst"`' shlibpath_overrides_runpath='`$ECHO "$shlibpath_overrides_runpath" | $SED "$delay_single_quote_subst"`' libname_spec='`$ECHO "$libname_spec" | $SED "$delay_single_quote_subst"`' library_names_spec='`$ECHO "$library_names_spec" | $SED "$delay_single_quote_subst"`' soname_spec='`$ECHO "$soname_spec" | $SED "$delay_single_quote_subst"`' install_override_mode='`$ECHO "$install_override_mode" | $SED "$delay_single_quote_subst"`' postinstall_cmds='`$ECHO "$postinstall_cmds" | $SED "$delay_single_quote_subst"`' postuninstall_cmds='`$ECHO "$postuninstall_cmds" | $SED "$delay_single_quote_subst"`' finish_cmds='`$ECHO "$finish_cmds" | $SED "$delay_single_quote_subst"`' finish_eval='`$ECHO "$finish_eval" | $SED "$delay_single_quote_subst"`' hardcode_into_libs='`$ECHO "$hardcode_into_libs" | $SED "$delay_single_quote_subst"`' sys_lib_search_path_spec='`$ECHO "$sys_lib_search_path_spec" | $SED "$delay_single_quote_subst"`' configure_time_dlsearch_path='`$ECHO "$configure_time_dlsearch_path" | $SED "$delay_single_quote_subst"`' configure_time_lt_sys_library_path='`$ECHO "$configure_time_lt_sys_library_path" | $SED "$delay_single_quote_subst"`' hardcode_action='`$ECHO "$hardcode_action" | $SED "$delay_single_quote_subst"`' enable_dlopen='`$ECHO "$enable_dlopen" | $SED "$delay_single_quote_subst"`' enable_dlopen_self='`$ECHO "$enable_dlopen_self" | $SED "$delay_single_quote_subst"`' enable_dlopen_self_static='`$ECHO "$enable_dlopen_self_static" | $SED "$delay_single_quote_subst"`' old_striplib='`$ECHO "$old_striplib" | $SED "$delay_single_quote_subst"`' striplib='`$ECHO "$striplib" | $SED "$delay_single_quote_subst"`' LTCC='$LTCC' LTCFLAGS='$LTCFLAGS' compiler='$compiler_DEFAULT' # A function that is used when there is no print builtin or printf. func_fallback_echo () { eval 'cat <<_LTECHO_EOF \$1 _LTECHO_EOF' } # Quote evaled strings. for var in SHELL \ ECHO \ PATH_SEPARATOR \ SED \ GREP \ EGREP \ FGREP \ LD \ NM \ LN_S \ lt_SP2NL \ lt_NL2SP \ reload_flag \ FILECMD \ OBJDUMP \ deplibs_check_method \ file_magic_cmd \ file_magic_glob \ want_nocaseglob \ DLLTOOL \ sharedlib_from_linklib_cmd \ AR \ archiver_list_spec \ STRIP \ RANLIB \ CC \ CFLAGS \ compiler \ lt_cv_sys_global_symbol_pipe \ lt_cv_sys_global_symbol_to_cdecl \ lt_cv_sys_global_symbol_to_import \ lt_cv_sys_global_symbol_to_c_name_address \ lt_cv_sys_global_symbol_to_c_name_address_lib_prefix \ lt_cv_nm_interface \ nm_file_list_spec \ lt_cv_truncate_bin \ lt_prog_compiler_no_builtin_flag \ lt_prog_compiler_pic \ lt_prog_compiler_wl \ lt_prog_compiler_static \ lt_cv_prog_compiler_c_o \ need_locks \ MANIFEST_TOOL \ DSYMUTIL \ NMEDIT \ LIPO \ OTOOL \ OTOOL64 \ shrext_cmds \ export_dynamic_flag_spec \ whole_archive_flag_spec \ compiler_needs_object \ with_gnu_ld \ allow_undefined_flag \ no_undefined_flag \ hardcode_libdir_flag_spec \ hardcode_libdir_separator \ exclude_expsyms \ include_expsyms \ file_list_spec \ variables_saved_for_relink \ libname_spec \ library_names_spec \ soname_spec \ install_override_mode \ finish_eval \ old_striplib \ striplib; do case \`eval \\\\\$ECHO \\\\""\\\\\$\$var"\\\\"\` in *[\\\\\\\`\\"\\\$]*) eval "lt_\$var=\\\\\\"\\\`\\\$ECHO \\"\\\$\$var\\" | \\\$SED \\"\\\$sed_quote_subst\\"\\\`\\\\\\"" ## exclude from sc_prohibit_nested_quotes ;; *) eval "lt_\$var=\\\\\\"\\\$\$var\\\\\\"" ;; esac done # Double-quote double-evaled strings. for var in reload_cmds \ old_postinstall_cmds \ old_postuninstall_cmds \ old_archive_cmds \ extract_expsyms_cmds \ old_archive_from_new_cmds \ old_archive_from_expsyms_cmds \ archive_cmds \ archive_expsym_cmds \ module_cmds \ module_expsym_cmds \ export_symbols_cmds \ prelink_cmds \ postlink_cmds \ postinstall_cmds \ postuninstall_cmds \ finish_cmds \ sys_lib_search_path_spec \ configure_time_dlsearch_path \ configure_time_lt_sys_library_path; do case \`eval \\\\\$ECHO \\\\""\\\\\$\$var"\\\\"\` in *[\\\\\\\`\\"\\\$]*) eval "lt_\$var=\\\\\\"\\\`\\\$ECHO \\"\\\$\$var\\" | \\\$SED -e \\"\\\$double_quote_subst\\" -e \\"\\\$sed_quote_subst\\" -e \\"\\\$delay_variable_subst\\"\\\`\\\\\\"" ## exclude from sc_prohibit_nested_quotes ;; *) eval "lt_\$var=\\\\\\"\\\$\$var\\\\\\"" ;; esac done ac_aux_dir='$ac_aux_dir' # See if we are running on zsh, and set the options that allow our # commands through without removal of \ escapes INIT. if test -n "\${ZSH_VERSION+set}"; then setopt NO_GLOB_SUBST fi PACKAGE='$PACKAGE' VERSION='$VERSION' RM='$RM' ofile='$ofile' _ACEOF cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 # Handling of arguments. for ac_config_target in $ac_config_targets do case $ac_config_target in "config.h") CONFIG_HEADERS="$CONFIG_HEADERS config.h" ;; "depfiles") CONFIG_COMMANDS="$CONFIG_COMMANDS depfiles" ;; "libtool") CONFIG_COMMANDS="$CONFIG_COMMANDS libtool" ;; "lib/bluez.pc") CONFIG_FILES="$CONFIG_FILES lib/bluez.pc" ;; "Makefile") CONFIG_FILES="$CONFIG_FILES Makefile" ;; "mesh/bluetooth-meshd.rst") CONFIG_FILES="$CONFIG_FILES mesh/bluetooth-meshd.rst" ;; "mesh/bluetooth-mesh.service") CONFIG_FILES="$CONFIG_FILES mesh/bluetooth-mesh.service" ;; "obexd/src/obex.service") CONFIG_FILES="$CONFIG_FILES obexd/src/obex.service" ;; "obexd/src/org.bluez.obex.service") CONFIG_FILES="$CONFIG_FILES obexd/src/org.bluez.obex.service" ;; "src/bluetoothd.rst") CONFIG_FILES="$CONFIG_FILES src/bluetoothd.rst" ;; "src/bluetooth.service") CONFIG_FILES="$CONFIG_FILES src/bluetooth.service" ;; "tools/bluetooth-logger.service") CONFIG_FILES="$CONFIG_FILES tools/bluetooth-logger.service" ;; "tools/mpris-proxy.service") CONFIG_FILES="$CONFIG_FILES tools/mpris-proxy.service" ;; *) as_fn_error $? "invalid argument: '$ac_config_target'" "$LINENO" 5;; esac done # If the user did not use the arguments to specify the items to instantiate, # then the envvar interface is used. Set only those that are not. # We use the long form for the default assignment because of an extremely # bizarre bug on SunOS 4.1.3. if $ac_need_defaults; then test ${CONFIG_FILES+y} || CONFIG_FILES=$config_files test ${CONFIG_HEADERS+y} || CONFIG_HEADERS=$config_headers test ${CONFIG_COMMANDS+y} || CONFIG_COMMANDS=$config_commands fi # Have a temporary directory for convenience. Make it in the build tree # simply because there is no reason against having it here, and in addition, # creating and moving files from /tmp can sometimes cause problems. # Hook for its removal unless debugging. # Note that there is a small window in which the directory will not be cleaned: # after its creation but before its name has been assigned to '$tmp'. $debug || { tmp= ac_tmp= trap 'exit_status=$? : "${ac_tmp:=$tmp}" { test ! -d "$ac_tmp" || rm -fr "$ac_tmp"; } && exit $exit_status ' 0 trap 'as_fn_exit 1' 1 2 13 15 } # Create a (secure) tmp directory for tmp files. { tmp=`(umask 077 && mktemp -d "./confXXXXXX") 2>/dev/null` && test -d "$tmp" } || { tmp=./conf$$-$RANDOM (umask 077 && mkdir "$tmp") } || as_fn_error $? "cannot create a temporary directory in ." "$LINENO" 5 ac_tmp=$tmp # Set up the scripts for CONFIG_FILES section. # No need to generate them if there are no CONFIG_FILES. # This happens for instance with './config.status config.h'. if test -n "$CONFIG_FILES"; then ac_cr=`echo X | tr X '\015'` # On cygwin, bash can eat \r inside `` if the user requested igncr. # But we know of no other shell where ac_cr would be empty at this # point, so we can use a bashism as a fallback. if test "x$ac_cr" = x; then eval ac_cr=\$\'\\r\' fi ac_cs_awk_cr=`$AWK 'BEGIN { print "a\rb" }' /dev/null` if test "$ac_cs_awk_cr" = "a${ac_cr}b"; then ac_cs_awk_cr='\\r' else ac_cs_awk_cr=$ac_cr fi echo 'BEGIN {' >"$ac_tmp/subs1.awk" && _ACEOF { echo "cat >conf$$subs.awk <<_ACEOF" && echo "$ac_subst_vars" | sed 's/.*/&!$&$ac_delim/' && echo "_ACEOF" } >conf$$subs.sh || as_fn_error $? "could not make $CONFIG_STATUS" "$LINENO" 5 ac_delim_num=`echo "$ac_subst_vars" | grep -c '^'` ac_delim='%!_!# ' for ac_last_try in false false false false false :; do . ./conf$$subs.sh || as_fn_error $? "could not make $CONFIG_STATUS" "$LINENO" 5 ac_delim_n=`sed -n "s/.*$ac_delim\$/X/p" conf$$subs.awk | grep -c X` if test $ac_delim_n = $ac_delim_num; then break elif $ac_last_try; then as_fn_error $? "could not make $CONFIG_STATUS" "$LINENO" 5 else ac_delim="$ac_delim!$ac_delim _$ac_delim!! " fi done rm -f conf$$subs.sh cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 cat >>"\$ac_tmp/subs1.awk" <<\\_ACAWK && _ACEOF sed -n ' h s/^/S["/; s/!.*/"]=/ p g s/^[^!]*!// :repl t repl s/'"$ac_delim"'$// t delim :nl h s/\(.\{148\}\)..*/\1/ t more1 s/["\\]/\\&/g; s/^/"/; s/$/\\n"\\/ p n b repl :more1 s/["\\]/\\&/g; s/^/"/; s/$/"\\/ p g s/.\{148\}// t nl :delim h s/\(.\{148\}\)..*/\1/ t more2 s/["\\]/\\&/g; s/^/"/; s/$/"/ p b :more2 s/["\\]/\\&/g; s/^/"/; s/$/"\\/ p g s/.\{148\}// t delim ' >$CONFIG_STATUS || ac_write_fail=1 rm -f conf$$subs.awk cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 _ACAWK cat >>"\$ac_tmp/subs1.awk" <<_ACAWK && for (key in S) S_is_set[key] = 1 FS = "" } { line = $ 0 nfields = split(line, field, "@") substed = 0 len = length(field[1]) for (i = 2; i < nfields; i++) { key = field[i] keylen = length(key) if (S_is_set[key]) { value = S[key] line = substr(line, 1, len) "" value "" substr(line, len + keylen + 3) len += length(value) + length(field[++i]) substed = 1 } else len += 1 + keylen } print line } _ACAWK _ACEOF cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 if sed "s/$ac_cr//" < /dev/null > /dev/null 2>&1; then sed "s/$ac_cr\$//; s/$ac_cr/$ac_cs_awk_cr/g" else cat fi < "$ac_tmp/subs1.awk" > "$ac_tmp/subs.awk" \ || as_fn_error $? "could not setup config files machinery" "$LINENO" 5 _ACEOF # VPATH may cause trouble with some makes, so we remove sole $(srcdir), # ${srcdir} and @srcdir@ entries from VPATH if srcdir is ".", strip leading and # trailing colons and then remove the whole line if VPATH becomes empty # (actually we leave an empty line to preserve line numbers). if test "x$srcdir" = x.; then ac_vpsub='/^[ ]*VPATH[ ]*=[ ]*/{ h s/// s/^/:/ s/[ ]*$/:/ s/:\$(srcdir):/:/g s/:\${srcdir}:/:/g s/:@srcdir@:/:/g s/^:*// s/:*$// x s/\(=[ ]*\).*/\1/ G s/\n// s/^[^=]*=[ ]*$// }' fi cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 fi # test -n "$CONFIG_FILES" # Set up the scripts for CONFIG_HEADERS section. # No need to generate them if there are no CONFIG_HEADERS. # This happens for instance with './config.status Makefile'. if test -n "$CONFIG_HEADERS"; then cat >"$ac_tmp/defines.awk" <<\_ACAWK || BEGIN { _ACEOF # Transform confdefs.h into an awk script 'defines.awk', embedded as # here-document in config.status, that substitutes the proper values into # config.h.in to produce config.h. # Create a delimiter string that does not exist in confdefs.h, to ease # handling of long lines. ac_delim='%!_!# ' for ac_last_try in false false :; do ac_tt=`sed -n "/$ac_delim/p" confdefs.h` if test -z "$ac_tt"; then break elif $ac_last_try; then as_fn_error $? "could not make $CONFIG_HEADERS" "$LINENO" 5 else ac_delim="$ac_delim!$ac_delim _$ac_delim!! " fi done # For the awk script, D is an array of macro values keyed by name, # likewise P contains macro parameters if any. Preserve backslash # newline sequences. ac_word_re=[_$as_cr_Letters][_$as_cr_alnum]* sed -n ' s/.\{148\}/&'"$ac_delim"'/g t rset :rset s/^[ ]*#[ ]*define[ ][ ]*/ / t def d :def s/\\$// t bsnl s/["\\]/\\&/g s/^ \('"$ac_word_re"'\)\(([^()]*)\)[ ]*\(.*\)/P["\1"]="\2"\ D["\1"]=" \3"/p s/^ \('"$ac_word_re"'\)[ ]*\(.*\)/D["\1"]=" \2"/p d :bsnl s/["\\]/\\&/g s/^ \('"$ac_word_re"'\)\(([^()]*)\)[ ]*\(.*\)/P["\1"]="\2"\ D["\1"]=" \3\\\\\\n"\\/p t cont s/^ \('"$ac_word_re"'\)[ ]*\(.*\)/D["\1"]=" \2\\\\\\n"\\/p t cont d :cont n s/.\{148\}/&'"$ac_delim"'/g t clear :clear s/\\$// t bsnlc s/["\\]/\\&/g; s/^/"/; s/$/"/p d :bsnlc s/["\\]/\\&/g; s/^/"/; s/$/\\\\\\n"\\/p b cont ' >$CONFIG_STATUS || ac_write_fail=1 cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 for (key in D) D_is_set[key] = 1 FS = "" } /^[\t ]*#[\t ]*(define|undef)[\t ]+$ac_word_re([\t (]|\$)/ { line = \$ 0 split(line, arg, " ") if (arg[1] == "#") { defundef = arg[2] mac1 = arg[3] } else { defundef = substr(arg[1], 2) mac1 = arg[2] } split(mac1, mac2, "(") #) macro = mac2[1] prefix = substr(line, 1, index(line, defundef) - 1) if (D_is_set[macro]) { # Preserve the white space surrounding the "#". print prefix "define", macro P[macro] D[macro] next } else { # Replace #undef with comments. This is necessary, for example, # in the case of _POSIX_SOURCE, which is predefined and required # on some systems where configure will not decide to define it. if (defundef == "undef") { print "/*", prefix defundef, macro, "*/" next } } } { print } _ACAWK _ACEOF cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 as_fn_error $? "could not setup config headers machinery" "$LINENO" 5 fi # test -n "$CONFIG_HEADERS" eval set X " :F $CONFIG_FILES :H $CONFIG_HEADERS :C $CONFIG_COMMANDS" shift for ac_tag do case $ac_tag in :[FHLC]) ac_mode=$ac_tag; continue;; esac case $ac_mode$ac_tag in :[FHL]*:*);; :L* | :C*:*) as_fn_error $? "invalid tag '$ac_tag'" "$LINENO" 5;; :[FH]-) ac_tag=-:-;; :[FH]*) ac_tag=$ac_tag:$ac_tag.in;; esac ac_save_IFS=$IFS IFS=: set x $ac_tag IFS=$ac_save_IFS shift ac_file=$1 shift case $ac_mode in :L) ac_source=$1;; :[FH]) ac_file_inputs= for ac_f do case $ac_f in -) ac_f="$ac_tmp/stdin";; *) # Look for the file first in the build tree, then in the source tree # (if the path is not absolute). The absolute path cannot be DOS-style, # because $ac_f cannot contain ':'. test -f "$ac_f" || case $ac_f in [\\/$]*) false;; *) test -f "$srcdir/$ac_f" && ac_f="$srcdir/$ac_f";; esac || as_fn_error 1 "cannot find input file: '$ac_f'" "$LINENO" 5;; esac case $ac_f in *\'*) ac_f=`printf "%s\n" "$ac_f" | sed "s/'/'\\\\\\\\''/g"`;; esac as_fn_append ac_file_inputs " '$ac_f'" done # Let's still pretend it is 'configure' which instantiates (i.e., don't # use $as_me), people would be surprised to read: # /* config.h. Generated by config.status. */ configure_input='Generated from '` printf "%s\n" "$*" | sed 's|^[^:]*/||;s|:[^:]*/|, |g' `' by configure.' if test x"$ac_file" != x-; then configure_input="$ac_file. $configure_input" { printf "%s\n" "$as_me:${as_lineno-$LINENO}: creating $ac_file" >&5 printf "%s\n" "$as_me: creating $ac_file" >&6;} fi # Neutralize special characters interpreted by sed in replacement strings. case $configure_input in #( *\&* | *\|* | *\\* ) ac_sed_conf_input=`printf "%s\n" "$configure_input" | sed 's/[\\\\&|]/\\\\&/g'`;; #( *) ac_sed_conf_input=$configure_input;; esac case $ac_tag in *:-:* | *:-) cat >"$ac_tmp/stdin" \ || as_fn_error $? "could not create $ac_file" "$LINENO" 5 ;; esac ;; esac ac_dir=`$as_dirname -- "$ac_file" || $as_expr X"$ac_file" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ X"$ac_file" : 'X\(//\)[^/]' \| \ X"$ac_file" : 'X\(//\)$' \| \ X"$ac_file" : 'X\(/\)' \| . 2>/dev/null || printf "%s\n" X"$ac_file" | sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ s//\1/ q } /^X\(\/\/\)[^/].*/{ s//\1/ q } /^X\(\/\/\)$/{ s//\1/ q } /^X\(\/\).*/{ s//\1/ q } s/.*/./; q'` as_dir="$ac_dir"; as_fn_mkdir_p ac_builddir=. case "$ac_dir" in .) ac_dir_suffix= ac_top_builddir_sub=. ac_top_build_prefix= ;; *) ac_dir_suffix=/`printf "%s\n" "$ac_dir" | sed 's|^\.[\\/]||'` # A ".." for each directory in $ac_dir_suffix. ac_top_builddir_sub=`printf "%s\n" "$ac_dir_suffix" | sed 's|/[^\\/]*|/..|g;s|/||'` case $ac_top_builddir_sub in "") ac_top_builddir_sub=. ac_top_build_prefix= ;; *) ac_top_build_prefix=$ac_top_builddir_sub/ ;; esac ;; esac ac_abs_top_builddir=$ac_pwd ac_abs_builddir=$ac_pwd$ac_dir_suffix # for backward compatibility: ac_top_builddir=$ac_top_build_prefix case $srcdir in .) # We are building in place. ac_srcdir=. ac_top_srcdir=$ac_top_builddir_sub ac_abs_top_srcdir=$ac_pwd ;; [\\/]* | ?:[\\/]* ) # Absolute name. ac_srcdir=$srcdir$ac_dir_suffix; ac_top_srcdir=$srcdir ac_abs_top_srcdir=$srcdir ;; *) # Relative name. ac_srcdir=$ac_top_build_prefix$srcdir$ac_dir_suffix ac_top_srcdir=$ac_top_build_prefix$srcdir ac_abs_top_srcdir=$ac_pwd/$srcdir ;; esac ac_abs_srcdir=$ac_abs_top_srcdir$ac_dir_suffix case $ac_mode in :F) # # CONFIG_FILE # case $INSTALL in [\\/$]* | ?:[\\/]* ) ac_INSTALL=$INSTALL ;; *) ac_INSTALL=$ac_top_build_prefix$INSTALL ;; esac ac_MKDIR_P=$MKDIR_P case $MKDIR_P in [\\/$]* | ?:[\\/]* ) ;; */*) ac_MKDIR_P=$ac_top_build_prefix$MKDIR_P ;; esac _ACEOF cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 # If the template does not know about datarootdir, expand it. # FIXME: This hack should be removed a few years after 2.60. ac_datarootdir_hack=; ac_datarootdir_seen= ac_sed_dataroot=' /datarootdir/ { p q } /@datadir@/p /@docdir@/p /@infodir@/p /@localedir@/p /@mandir@/p' case `eval "sed -n \"\$ac_sed_dataroot\" $ac_file_inputs"` in *datarootdir*) ac_datarootdir_seen=yes;; *@datadir@*|*@docdir@*|*@infodir@*|*@localedir@*|*@mandir@*) { printf "%s\n" "$as_me:${as_lineno-$LINENO}: WARNING: $ac_file_inputs seems to ignore the --datarootdir setting" >&5 printf "%s\n" "$as_me: WARNING: $ac_file_inputs seems to ignore the --datarootdir setting" >&2;} _ACEOF cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 ac_datarootdir_hack=' s&@datadir@&$datadir&g s&@docdir@&$docdir&g s&@infodir@&$infodir&g s&@localedir@&$localedir&g s&@mandir@&$mandir&g s&\\\${datarootdir}&$datarootdir&g' ;; esac _ACEOF # Neutralize VPATH when '$srcdir' = '.'. # Shell code in configure.ac might set extrasub. # FIXME: do we really want to maintain this feature? cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 ac_sed_extra="$ac_vpsub $extrasub _ACEOF cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 :t /@[a-zA-Z_][a-zA-Z_0-9]*@/!b s|@configure_input@|$ac_sed_conf_input|;t t s&@top_builddir@&$ac_top_builddir_sub&;t t s&@top_build_prefix@&$ac_top_build_prefix&;t t s&@srcdir@&$ac_srcdir&;t t s&@abs_srcdir@&$ac_abs_srcdir&;t t s&@top_srcdir@&$ac_top_srcdir&;t t s&@abs_top_srcdir@&$ac_abs_top_srcdir&;t t s&@builddir@&$ac_builddir&;t t s&@abs_builddir@&$ac_abs_builddir&;t t s&@abs_top_builddir@&$ac_abs_top_builddir&;t t s&@INSTALL@&$ac_INSTALL&;t t s&@MKDIR_P@&$ac_MKDIR_P&;t t $ac_datarootdir_hack " eval sed \"\$ac_sed_extra\" "$ac_file_inputs" | $AWK -f "$ac_tmp/subs.awk" \ >$ac_tmp/out || as_fn_error $? "could not create $ac_file" "$LINENO" 5 test -z "$ac_datarootdir_hack$ac_datarootdir_seen" && { ac_out=`sed -n '/\${datarootdir}/p' "$ac_tmp/out"`; test -n "$ac_out"; } && { ac_out=`sed -n '/^[ ]*datarootdir[ ]*:*=/p' \ "$ac_tmp/out"`; test -z "$ac_out"; } && { printf "%s\n" "$as_me:${as_lineno-$LINENO}: WARNING: $ac_file contains a reference to the variable 'datarootdir' which seems to be undefined. Please make sure it is defined" >&5 printf "%s\n" "$as_me: WARNING: $ac_file contains a reference to the variable 'datarootdir' which seems to be undefined. Please make sure it is defined" >&2;} rm -f "$ac_tmp/stdin" case $ac_file in -) cat "$ac_tmp/out" && rm -f "$ac_tmp/out";; *) rm -f "$ac_file" && mv "$ac_tmp/out" "$ac_file";; esac \ || as_fn_error $? "could not create $ac_file" "$LINENO" 5 ;; :H) # # CONFIG_HEADER # if test x"$ac_file" != x-; then { printf "%s\n" "/* $configure_input */" >&1 \ && eval '$AWK -f "$ac_tmp/defines.awk"' "$ac_file_inputs" } >"$ac_tmp/config.h" \ || as_fn_error $? "could not create $ac_file" "$LINENO" 5 if diff "$ac_file" "$ac_tmp/config.h" >/dev/null 2>&1; then { printf "%s\n" "$as_me:${as_lineno-$LINENO}: $ac_file is unchanged" >&5 printf "%s\n" "$as_me: $ac_file is unchanged" >&6;} else rm -f "$ac_file" mv "$ac_tmp/config.h" "$ac_file" \ || as_fn_error $? "could not create $ac_file" "$LINENO" 5 fi else printf "%s\n" "/* $configure_input */" >&1 \ && eval '$AWK -f "$ac_tmp/defines.awk"' "$ac_file_inputs" \ || as_fn_error $? "could not create -" "$LINENO" 5 fi # Compute "$ac_file"'s index in $config_headers. _am_arg="$ac_file" _am_stamp_count=1 for _am_header in $config_headers :; do case $_am_header in $_am_arg | $_am_arg:* ) break ;; * ) _am_stamp_count=`expr $_am_stamp_count + 1` ;; esac done echo "timestamp for $_am_arg" >`$as_dirname -- "$_am_arg" || $as_expr X"$_am_arg" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ X"$_am_arg" : 'X\(//\)[^/]' \| \ X"$_am_arg" : 'X\(//\)$' \| \ X"$_am_arg" : 'X\(/\)' \| . 2>/dev/null || printf "%s\n" X"$_am_arg" | sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ s//\1/ q } /^X\(\/\/\)[^/].*/{ s//\1/ q } /^X\(\/\/\)$/{ s//\1/ q } /^X\(\/\).*/{ s//\1/ q } s/.*/./; q'`/stamp-h$_am_stamp_count ;; :C) { printf "%s\n" "$as_me:${as_lineno-$LINENO}: executing $ac_file commands" >&5 printf "%s\n" "$as_me: executing $ac_file commands" >&6;} ;; esac case $ac_file$ac_mode in "depfiles":C) test x"$AMDEP_TRUE" != x"" || { # Older Autoconf quotes --file arguments for eval, but not when files # are listed without --file. Let's play safe and only enable the eval # if we detect the quoting. # TODO: see whether this extra hack can be removed once we start # requiring Autoconf 2.70 or later. case $CONFIG_FILES in #( *\'*) : eval set x "$CONFIG_FILES" ;; #( *) : set x $CONFIG_FILES ;; #( *) : ;; esac shift # Used to flag and report bootstrapping failures. am_rc=0 for am_mf do # Strip MF so we end up with the name of the file. am_mf=`printf "%s\n" "$am_mf" | sed -e 's/:.*$//'` # Check whether this is an Automake generated Makefile which includes # dependency-tracking related rules and includes. # Grep'ing the whole file directly is not great: AIX grep has a line # limit of 2048, but all sed's we know have understand at least 4000. sed -n 's,^am--depfiles:.*,X,p' "$am_mf" | grep X >/dev/null 2>&1 \ || continue am_dirpart=`$as_dirname -- "$am_mf" || $as_expr X"$am_mf" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ X"$am_mf" : 'X\(//\)[^/]' \| \ X"$am_mf" : 'X\(//\)$' \| \ X"$am_mf" : 'X\(/\)' \| . 2>/dev/null || printf "%s\n" X"$am_mf" | sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ s//\1/ q } /^X\(\/\/\)[^/].*/{ s//\1/ q } /^X\(\/\/\)$/{ s//\1/ q } /^X\(\/\).*/{ s//\1/ q } s/.*/./; q'` am_filepart=`$as_basename -- "$am_mf" || $as_expr X/"$am_mf" : '.*/\([^/][^/]*\)/*$' \| \ X"$am_mf" : 'X\(//\)$' \| \ X"$am_mf" : 'X\(/\)' \| . 2>/dev/null || printf "%s\n" X/"$am_mf" | sed '/^.*\/\([^/][^/]*\)\/*$/{ s//\1/ q } /^X\/\(\/\/\)$/{ s//\1/ q } /^X\/\(\/\).*/{ s//\1/ q } s/.*/./; q'` { echo "$as_me:$LINENO: cd "$am_dirpart" \ && sed -e '/# am--include-marker/d' "$am_filepart" \ | $MAKE -f - am--depfiles" >&5 (cd "$am_dirpart" \ && sed -e '/# am--include-marker/d' "$am_filepart" \ | $MAKE -f - am--depfiles) >&5 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } || am_rc=$? done if test $am_rc -ne 0; then { { printf "%s\n" "$as_me:${as_lineno-$LINENO}: error: in '$ac_pwd':" >&5 printf "%s\n" "$as_me: error: in '$ac_pwd':" >&2;} as_fn_error $? "Something went wrong bootstrapping makefile fragments for automatic dependency tracking. If GNU make was not used, consider re-running the configure script with MAKE=\"gmake\" (or whatever is necessary). You can also try re-running configure with the '--disable-dependency-tracking' option to at least be able to build the package (albeit without support for automatic dependency tracking). See 'config.log' for more details" "$LINENO" 5; } fi { am_dirpart=; unset am_dirpart;} { am_filepart=; unset am_filepart;} { am_mf=; unset am_mf;} { am_rc=; unset am_rc;} rm -f conftest-deps.mk } ;; "libtool":C) # See if we are running on zsh, and set the options that allow our # commands through without removal of \ escapes. if test -n "${ZSH_VERSION+set}"; then setopt NO_GLOB_SUBST fi cfgfile=${ofile}T trap "$RM \"$cfgfile\"; exit 1" 1 2 15 $RM "$cfgfile" cat <<_LT_EOF >> "$cfgfile" #! $SHELL # Generated automatically by $as_me ($PACKAGE) $VERSION # NOTE: Changes made to this file will be lost: look at ltmain.sh. # Provide generalized library-building support services. # Written by Gordon Matzigkeit, 1996 # Copyright (C) 2014 Free Software Foundation, Inc. # This is free software; see the source for copying conditions. There is NO # warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. # GNU Libtool is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of of the License, or # (at your option) any later version. # # As a special exception to the GNU General Public License, if you # distribute this file as part of a program or library that is built # using GNU Libtool, you may include this file under the same # distribution terms that you use for the rest of that program. # # GNU Libtool is distributed in the hope that it will be useful, but # WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program. If not, see . # The names of the tagged configurations supported by this script. available_tags='' # Configured defaults for sys_lib_dlsearch_path munging. : \${LT_SYS_LIBRARY_PATH="$configure_time_lt_sys_library_path"} # ### BEGIN LIBTOOL CONFIG # Which release of libtool.m4 was used? macro_version=$macro_version macro_revision=$macro_revision # Whether or not to build static libraries. build_old_libs=$enable_static # Whether or not to build shared libraries. build_libtool_libs=$enable_shared # What type of objects to build. pic_mode=$pic_mode # Whether or not to optimize for fast installation. fast_install=$enable_fast_install # Shared archive member basename,for filename based shared library versioning on AIX. shared_archive_member_spec=$shared_archive_member_spec # Shell to use when invoking shell scripts. SHELL=$lt_SHELL # An echo program that protects backslashes. ECHO=$lt_ECHO # The PATH separator for the build system. PATH_SEPARATOR=$lt_PATH_SEPARATOR # The host system. host_alias=$host_alias host=$host host_os=$host_os # The build system. build_alias=$build_alias build=$build build_os=$build_os # A sed program that does not truncate output. SED=$lt_SED # Sed that helps us avoid accidentally triggering echo(1) options like -n. Xsed="\$SED -e 1s/^X//" # A grep program that handles long lines. GREP=$lt_GREP # An ERE matcher. EGREP=$lt_EGREP # A literal string matcher. FGREP=$lt_FGREP # A BSD- or MS-compatible name lister. NM=$lt_NM # Whether we need soft or hard links. LN_S=$lt_LN_S # What is the maximum length of a command? max_cmd_len=$max_cmd_len # Object file suffix (normally "o"). objext=$ac_objext # Executable file suffix (normally ""). exeext=$exeext # whether the shell understands "unset". lt_unset=$lt_unset # turn spaces into newlines. SP2NL=$lt_lt_SP2NL # turn newlines into spaces. NL2SP=$lt_lt_NL2SP # convert \$build file names to \$host format. to_host_file_cmd=$lt_cv_to_host_file_cmd # convert \$build files to toolchain format. to_tool_file_cmd=$lt_cv_to_tool_file_cmd # A file(cmd) program that detects file types. FILECMD=$lt_FILECMD # An object symbol dumper. OBJDUMP=$lt_OBJDUMP # Method to check whether dependent libraries are shared objects. deplibs_check_method=$lt_deplibs_check_method # Command to use when deplibs_check_method = "file_magic". file_magic_cmd=$lt_file_magic_cmd # How to find potential files when deplibs_check_method = "file_magic". file_magic_glob=$lt_file_magic_glob # Find potential files using nocaseglob when deplibs_check_method = "file_magic". want_nocaseglob=$lt_want_nocaseglob # DLL creation program. DLLTOOL=$lt_DLLTOOL # Command to associate shared and link libraries. sharedlib_from_linklib_cmd=$lt_sharedlib_from_linklib_cmd # The archiver. AR=$lt_AR # Flags to create an archive (by configure). lt_ar_flags=$lt_ar_flags # Flags to create an archive. AR_FLAGS=\${ARFLAGS-"\$lt_ar_flags"} # How to feed a file listing to the archiver. archiver_list_spec=$lt_archiver_list_spec # A symbol stripping program. STRIP=$lt_STRIP # Commands used to install an old-style archive. RANLIB=$lt_RANLIB old_postinstall_cmds=$lt_old_postinstall_cmds old_postuninstall_cmds=$lt_old_postuninstall_cmds # Whether to use a lock for old archive extraction. lock_old_archive_extraction=$lock_old_archive_extraction # A C compiler. LTCC=$lt_CC # LTCC compiler flags. LTCFLAGS=$lt_CFLAGS # Take the output of nm and produce a listing of raw symbols and C names. global_symbol_pipe=$lt_lt_cv_sys_global_symbol_pipe # Transform the output of nm in a proper C declaration. global_symbol_to_cdecl=$lt_lt_cv_sys_global_symbol_to_cdecl # Transform the output of nm into a list of symbols to manually relocate. global_symbol_to_import=$lt_lt_cv_sys_global_symbol_to_import # Transform the output of nm in a C name address pair. global_symbol_to_c_name_address=$lt_lt_cv_sys_global_symbol_to_c_name_address # Transform the output of nm in a C name address pair when lib prefix is needed. global_symbol_to_c_name_address_lib_prefix=$lt_lt_cv_sys_global_symbol_to_c_name_address_lib_prefix # The name lister interface. nm_interface=$lt_lt_cv_nm_interface # Specify filename containing input files for \$NM. nm_file_list_spec=$lt_nm_file_list_spec # The root where to search for dependent libraries,and where our libraries should be installed. lt_sysroot=$lt_sysroot # Command to truncate a binary pipe. lt_truncate_bin=$lt_lt_cv_truncate_bin # The name of the directory that contains temporary libtool files. objdir=$objdir # Used to examine libraries when file_magic_cmd begins with "file". MAGIC_CMD=$MAGIC_CMD # Must we lock files when doing compilation? need_locks=$lt_need_locks # Manifest tool. MANIFEST_TOOL=$lt_MANIFEST_TOOL # Tool to manipulate archived DWARF debug symbol files on Mac OS X. DSYMUTIL=$lt_DSYMUTIL # Tool to change global to local symbols on Mac OS X. NMEDIT=$lt_NMEDIT # Tool to manipulate fat objects and archives on Mac OS X. LIPO=$lt_LIPO # ldd/readelf like tool for Mach-O binaries on Mac OS X. OTOOL=$lt_OTOOL # ldd/readelf like tool for 64 bit Mach-O binaries on Mac OS X 10.4. OTOOL64=$lt_OTOOL64 # Old archive suffix (normally "a"). libext=$libext # Shared library suffix (normally ".so"). shrext_cmds=$lt_shrext_cmds # The commands to extract the exported symbol list from a shared archive. extract_expsyms_cmds=$lt_extract_expsyms_cmds # Variables whose values should be saved in libtool wrapper scripts and # restored at link time. variables_saved_for_relink=$lt_variables_saved_for_relink # Do we need the "lib" prefix for modules? need_lib_prefix=$need_lib_prefix # Do we need a version for libraries? need_version=$need_version # Library versioning type. version_type=$version_type # Shared library runtime path variable. runpath_var=$runpath_var # Shared library path variable. shlibpath_var=$shlibpath_var # Is shlibpath searched before the hard-coded library search path? shlibpath_overrides_runpath=$shlibpath_overrides_runpath # Format of library name prefix. libname_spec=$lt_libname_spec # List of archive names. First name is the real one, the rest are links. # The last name is the one that the linker finds with -lNAME library_names_spec=$lt_library_names_spec # The coded name of the library, if different from the real name. soname_spec=$lt_soname_spec # Permission mode override for installation of shared libraries. install_override_mode=$lt_install_override_mode # Command to use after installation of a shared archive. postinstall_cmds=$lt_postinstall_cmds # Command to use after uninstallation of a shared archive. postuninstall_cmds=$lt_postuninstall_cmds # Commands used to finish a libtool library installation in a directory. finish_cmds=$lt_finish_cmds # As "finish_cmds", except a single script fragment to be evaled but # not shown. finish_eval=$lt_finish_eval # Whether we should hardcode library paths into libraries. hardcode_into_libs=$hardcode_into_libs # Compile-time system search path for libraries. sys_lib_search_path_spec=$lt_sys_lib_search_path_spec # Detected run-time system search path for libraries. sys_lib_dlsearch_path_spec=$lt_configure_time_dlsearch_path # Explicit LT_SYS_LIBRARY_PATH set during ./configure time. configure_time_lt_sys_library_path=$lt_configure_time_lt_sys_library_path # Whether dlopen is supported. dlopen_support=$enable_dlopen # Whether dlopen of programs is supported. dlopen_self=$enable_dlopen_self # Whether dlopen of statically linked programs is supported. dlopen_self_static=$enable_dlopen_self_static # Commands to strip libraries. old_striplib=$lt_old_striplib striplib=$lt_striplib # The linker used to build libraries. LD=$lt_LD # How to create reloadable object files. reload_flag=$lt_reload_flag reload_cmds=$lt_reload_cmds # Commands used to build an old-style archive. old_archive_cmds=$lt_old_archive_cmds # A language specific compiler. CC=$lt_compiler # Is the compiler the GNU compiler? with_gcc=$GCC # Compiler flag to turn off builtin functions. no_builtin_flag=$lt_lt_prog_compiler_no_builtin_flag # Additional compiler flags for building library objects. pic_flag=$lt_lt_prog_compiler_pic # How to pass a linker flag through the compiler. wl=$lt_lt_prog_compiler_wl # Compiler flag to prevent dynamic linking. link_static_flag=$lt_lt_prog_compiler_static # Does compiler simultaneously support -c and -o options? compiler_c_o=$lt_lt_cv_prog_compiler_c_o # Whether or not to add -lc for building shared libraries. build_libtool_need_lc=$archive_cmds_need_lc # Whether or not to disallow shared libs when runtime libs are static. allow_libtool_libs_with_static_runtimes=$enable_shared_with_static_runtimes # Compiler flag to allow reflexive dlopens. export_dynamic_flag_spec=$lt_export_dynamic_flag_spec # Compiler flag to generate shared objects directly from archives. whole_archive_flag_spec=$lt_whole_archive_flag_spec # Whether the compiler copes with passing no objects directly. compiler_needs_object=$lt_compiler_needs_object # Create an old-style archive from a shared archive. old_archive_from_new_cmds=$lt_old_archive_from_new_cmds # Create a temporary old-style archive to link instead of a shared archive. old_archive_from_expsyms_cmds=$lt_old_archive_from_expsyms_cmds # Commands used to build a shared archive. archive_cmds=$lt_archive_cmds archive_expsym_cmds=$lt_archive_expsym_cmds # Commands used to build a loadable module if different from building # a shared archive. module_cmds=$lt_module_cmds module_expsym_cmds=$lt_module_expsym_cmds # Whether we are building with GNU ld or not. with_gnu_ld=$lt_with_gnu_ld # Flag that allows shared libraries with undefined symbols to be built. allow_undefined_flag=$lt_allow_undefined_flag # Flag that enforces no undefined symbols. no_undefined_flag=$lt_no_undefined_flag # Flag to hardcode \$libdir into a binary during linking. # This must work even if \$libdir does not exist hardcode_libdir_flag_spec=$lt_hardcode_libdir_flag_spec # Whether we need a single "-rpath" flag with a separated argument. hardcode_libdir_separator=$lt_hardcode_libdir_separator # Set to "yes" if using DIR/libNAME\$shared_ext during linking hardcodes # DIR into the resulting binary. hardcode_direct=$hardcode_direct # Set to "yes" if using DIR/libNAME\$shared_ext during linking hardcodes # DIR into the resulting binary and the resulting library dependency is # "absolute",i.e impossible to change by setting \$shlibpath_var if the # library is relocated. hardcode_direct_absolute=$hardcode_direct_absolute # Set to "yes" if using the -LDIR flag during linking hardcodes DIR # into the resulting binary. hardcode_minus_L=$hardcode_minus_L # Set to "yes" if using SHLIBPATH_VAR=DIR during linking hardcodes DIR # into the resulting binary. hardcode_shlibpath_var=$hardcode_shlibpath_var # Set to "yes" if building a shared library automatically hardcodes DIR # into the library and all subsequent libraries and executables linked # against it. hardcode_automatic=$hardcode_automatic # Set to yes if linker adds runtime paths of dependent libraries # to runtime path list. inherit_rpath=$inherit_rpath # Whether libtool must link a program against all its dependency libraries. link_all_deplibs=$link_all_deplibs # Set to "yes" if exported symbols are required. always_export_symbols=$always_export_symbols # The commands to list exported symbols. export_symbols_cmds=$lt_export_symbols_cmds # Symbols that should not be listed in the preloaded symbols. exclude_expsyms=$lt_exclude_expsyms # Symbols that must always be exported. include_expsyms=$lt_include_expsyms # Commands necessary for linking programs (against libraries) with templates. prelink_cmds=$lt_prelink_cmds # Commands necessary for finishing linking programs. postlink_cmds=$lt_postlink_cmds # Specify filename containing input files. file_list_spec=$lt_file_list_spec # How to hardcode a shared library path into an executable. hardcode_action=$hardcode_action # ### END LIBTOOL CONFIG _LT_EOF cat <<'_LT_EOF' >> "$cfgfile" # ### BEGIN FUNCTIONS SHARED WITH CONFIGURE # func_munge_path_list VARIABLE PATH # ----------------------------------- # VARIABLE is name of variable containing _space_ separated list of # directories to be munged by the contents of PATH, which is string # having a format: # "DIR[:DIR]:" # string "DIR[ DIR]" will be prepended to VARIABLE # ":DIR[:DIR]" # string "DIR[ DIR]" will be appended to VARIABLE # "DIRP[:DIRP]::[DIRA:]DIRA" # string "DIRP[ DIRP]" will be prepended to VARIABLE and string # "DIRA[ DIRA]" will be appended to VARIABLE # "DIR[:DIR]" # VARIABLE will be replaced by "DIR[ DIR]" func_munge_path_list () { case x$2 in x) ;; *:) eval $1=\"`$ECHO $2 | $SED 's/:/ /g'` \$$1\" ;; x:*) eval $1=\"\$$1 `$ECHO $2 | $SED 's/:/ /g'`\" ;; *::*) eval $1=\"\$$1\ `$ECHO $2 | $SED -e 's/.*:://' -e 's/:/ /g'`\" eval $1=\"`$ECHO $2 | $SED -e 's/::.*//' -e 's/:/ /g'`\ \$$1\" ;; *) eval $1=\"`$ECHO $2 | $SED 's/:/ /g'`\" ;; esac } # Calculate cc_basename. Skip known compiler wrappers and cross-prefix. func_cc_basename () { for cc_temp in $*""; do case $cc_temp in compile | *[\\/]compile | ccache | *[\\/]ccache ) ;; distcc | *[\\/]distcc | purify | *[\\/]purify ) ;; \-*) ;; *) break;; esac done func_cc_basename_result=`$ECHO "$cc_temp" | $SED "s%.*/%%; s%^$host_alias-%%"` } # ### END FUNCTIONS SHARED WITH CONFIGURE _LT_EOF case $host_os in aix3*) cat <<\_LT_EOF >> "$cfgfile" # AIX sometimes has problems with the GCC collect2 program. For some # reason, if we set the COLLECT_NAMES environment variable, the problems # vanish in a puff of smoke. if test set != "${COLLECT_NAMES+set}"; then COLLECT_NAMES= export COLLECT_NAMES fi _LT_EOF ;; esac ltmain=$ac_aux_dir/ltmain.sh # We use sed instead of cat because bash on DJGPP gets confused if # if finds mixed CR/LF and LF-only lines. Since sed operates in # text mode, it properly converts lines to CR/LF. This bash problem # is reportedly fixed, but why not run on old versions too? $SED '$q' "$ltmain" >> "$cfgfile" \ || (rm -f "$cfgfile"; exit 1) mv -f "$cfgfile" "$ofile" || (rm -f "$ofile" && cp "$cfgfile" "$ofile" && rm -f "$cfgfile") chmod +x "$ofile" ;; esac done # for ac_tag as_fn_exit 0 _ACEOF ac_clean_files=$ac_clean_files_save test $ac_write_fail = 0 || as_fn_error $? "write failure creating $CONFIG_STATUS" "$LINENO" 5 # configure is writing to config.log, and then calls config.status. # config.status does its own redirection, appending to config.log. # Unfortunately, on DOS this fails, as config.log is still kept open # by configure, so config.status won't be able to write to it; its # output is simply discarded. So we exec the FD to /dev/null, # effectively closing config.log, so it can be properly (re)opened and # appended to by config.status. When coming back to configure, we # need to make the FD available again. if test "$no_create" != yes; then ac_cs_success=: ac_config_status_args= test "$silent" = yes && ac_config_status_args="$ac_config_status_args --quiet" exec 5>/dev/null $SHELL $CONFIG_STATUS $ac_config_status_args || ac_cs_success=false exec 5>>config.log # Use ||, not &&, to avoid exiting from the if with $? = 1, which # would make configure fail if this is the last instruction. $ac_cs_success || as_fn_exit 1 fi if test -n "$ac_unrecognized_opts" && test "$enable_option_checking" != no; then { printf "%s\n" "$as_me:${as_lineno-$LINENO}: WARNING: unrecognized options: $ac_unrecognized_opts" >&5 printf "%s\n" "$as_me: WARNING: unrecognized options: $ac_unrecognized_opts" >&2;} fi bluez-5.82/PaxHeaders/COPYING0000644000000000000000000000005011071665350012705 xustar0020 atime=1743516862 20 ctime=1743591275 bluez-5.82/COPYING0000644000000000000000000004312611071665350012374 0ustar00rootroot GNU GENERAL PUBLIC LICENSE Version 2, June 1991 Copyright (C) 1989, 1991 Free Software Foundation, Inc. 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. Preamble The licenses for most software are designed to take away your freedom to share and change it. By contrast, the GNU General Public License is intended to guarantee your freedom to share and change free software--to make sure the software is free for all its users. This General Public License applies to most of the Free Software Foundation's software and to any other program whose authors commit to using it. (Some other Free Software Foundation software is covered by the GNU Library General Public License instead.) You can apply it to your programs, too. When we speak of free software, we are referring to freedom, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for this service if you wish), that you receive source code or can get it if you want it, that you can change the software or use pieces of it in new free programs; and that you know you can do these things. To protect your rights, we need to make restrictions that forbid anyone to deny you these rights or to ask you to surrender the rights. These restrictions translate to certain responsibilities for you if you distribute copies of the software, or if you modify it. For example, if you distribute copies of such a program, whether gratis or for a fee, you must give the recipients all the rights that you have. You must make sure that they, too, receive or can get the source code. And you must show them these terms so they know their rights. We protect your rights with two steps: (1) copyright the software, and (2) offer you this license which gives you legal permission to copy, distribute and/or modify the software. Also, for each author's protection and ours, we want to make certain that everyone understands that there is no warranty for this free software. If the software is modified by someone else and passed on, we want its recipients to know that what they have is not the original, so that any problems introduced by others will not reflect on the original authors' reputations. Finally, any free program is threatened constantly by software patents. We wish to avoid the danger that redistributors of a free program will individually obtain patent licenses, in effect making the program proprietary. To prevent this, we have made it clear that any patent must be licensed for everyone's free use or not licensed at all. The precise terms and conditions for copying, distribution and modification follow. GNU GENERAL PUBLIC LICENSE TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 0. This License applies to any program or other work which contains a notice placed by the copyright holder saying it may be distributed under the terms of this General Public License. The "Program", below, refers to any such program or work, and a "work based on the Program" means either the Program or any derivative work under copyright law: that is to say, a work containing the Program or a portion of it, either verbatim or with modifications and/or translated into another language. (Hereinafter, translation is included without limitation in the term "modification".) Each licensee is addressed as "you". Activities other than copying, distribution and modification are not covered by this License; they are outside its scope. The act of running the Program is not restricted, and the output from the Program is covered only if its contents constitute a work based on the Program (independent of having been made by running the Program). Whether that is true depends on what the Program does. 1. You may copy and distribute verbatim copies of the Program's source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice and disclaimer of warranty; keep intact all the notices that refer to this License and to the absence of any warranty; and give any other recipients of the Program a copy of this License along with the Program. You may charge a fee for the physical act of transferring a copy, and you may at your option offer warranty protection in exchange for a fee. 2. You may modify your copy or copies of the Program or any portion of it, thus forming a work based on the Program, and copy and distribute such modifications or work under the terms of Section 1 above, provided that you also meet all of these conditions: a) You must cause the modified files to carry prominent notices stating that you changed the files and the date of any change. b) You must cause any work that you distribute or publish, that in whole or in part contains or is derived from the Program or any part thereof, to be licensed as a whole at no charge to all third parties under the terms of this License. c) If the modified program normally reads commands interactively when run, you must cause it, when started running for such interactive use in the most ordinary way, to print or display an announcement including an appropriate copyright notice and a notice that there is no warranty (or else, saying that you provide a warranty) and that users may redistribute the program under these conditions, and telling the user how to view a copy of this License. (Exception: if the Program itself is interactive but does not normally print such an announcement, your work based on the Program is not required to print an announcement.) These requirements apply to the modified work as a whole. If identifiable sections of that work are not derived from the Program, and can be reasonably considered independent and separate works in themselves, then this License, and its terms, do not apply to those sections when you distribute them as separate works. But when you distribute the same sections as part of a whole which is a work based on the Program, the distribution of the whole must be on the terms of this License, whose permissions for other licensees extend to the entire whole, and thus to each and every part regardless of who wrote it. Thus, it is not the intent of this section to claim rights or contest your rights to work written entirely by you; rather, the intent is to exercise the right to control the distribution of derivative or collective works based on the Program. In addition, mere aggregation of another work not based on the Program with the Program (or with a work based on the Program) on a volume of a storage or distribution medium does not bring the other work under the scope of this License. 3. You may copy and distribute the Program (or a work based on it, under Section 2) in object code or executable form under the terms of Sections 1 and 2 above provided that you also do one of the following: a) Accompany it with the complete corresponding machine-readable source code, which must be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, b) Accompany it with a written offer, valid for at least three years, to give any third party, for a charge no more than your cost of physically performing source distribution, a complete machine-readable copy of the corresponding source code, to be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, c) Accompany it with the information you received as to the offer to distribute corresponding source code. (This alternative is allowed only for noncommercial distribution and only if you received the program in object code or executable form with such an offer, in accord with Subsection b above.) The source code for a work means the preferred form of the work for making modifications to it. For an executable work, complete source code means all the source code for all modules it contains, plus any associated interface definition files, plus the scripts used to control compilation and installation of the executable. However, as a special exception, the source code distributed need not include anything that is normally distributed (in either source or binary form) with the major components (compiler, kernel, and so on) of the operating system on which the executable runs, unless that component itself accompanies the executable. If distribution of executable or object code is made by offering access to copy from a designated place, then offering equivalent access to copy the source code from the same place counts as distribution of the source code, even though third parties are not compelled to copy the source along with the object code. 4. You may not copy, modify, sublicense, or distribute the Program except as expressly provided under this License. Any attempt otherwise to copy, modify, sublicense or distribute the Program is void, and will automatically terminate your rights under this License. However, parties who have received copies, or rights, from you under this License will not have their licenses terminated so long as such parties remain in full compliance. 5. You are not required to accept this License, since you have not signed it. However, nothing else grants you permission to modify or distribute the Program or its derivative works. These actions are prohibited by law if you do not accept this License. Therefore, by modifying or distributing the Program (or any work based on the Program), you indicate your acceptance of this License to do so, and all its terms and conditions for copying, distributing or modifying the Program or works based on it. 6. Each time you redistribute the Program (or any work based on the Program), the recipient automatically receives a license from the original licensor to copy, distribute or modify the Program subject to these terms and conditions. You may not impose any further restrictions on the recipients' exercise of the rights granted herein. You are not responsible for enforcing compliance by third parties to this License. 7. If, as a consequence of a court judgment or allegation of patent infringement or for any other reason (not limited to patent issues), conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot distribute so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not distribute the Program at all. For example, if a patent license would not permit royalty-free redistribution of the Program by all those who receive copies directly or indirectly through you, then the only way you could satisfy both it and this License would be to refrain entirely from distribution of the Program. If any portion of this section is held invalid or unenforceable under any particular circumstance, the balance of the section is intended to apply and the section as a whole is intended to apply in other circumstances. It is not the purpose of this section to induce you to infringe any patents or other property right claims or to contest validity of any such claims; this section has the sole purpose of protecting the integrity of the free software distribution system, which is implemented by public license practices. Many people have made generous contributions to the wide range of software distributed through that system in reliance on consistent application of that system; it is up to the author/donor to decide if he or she is willing to distribute software through any other system and a licensee cannot impose that choice. This section is intended to make thoroughly clear what is believed to be a consequence of the rest of this License. 8. If the distribution and/or use of the Program is restricted in certain countries either by patents or by copyrighted interfaces, the original copyright holder who places the Program under this License may add an explicit geographical distribution limitation excluding those countries, so that distribution is permitted only in or among countries not thus excluded. In such case, this License incorporates the limitation as if written in the body of this License. 9. The Free Software Foundation may publish revised and/or new versions of the General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. Each version is given a distinguishing version number. If the Program specifies a version number of this License which applies to it and "any later version", you have the option of following the terms and conditions either of that version or of any later version published by the Free Software Foundation. If the Program does not specify a version number of this License, you may choose any version ever published by the Free Software Foundation. 10. If you wish to incorporate parts of the Program into other free programs whose distribution conditions are different, write to the author to ask for permission. For software which is copyrighted by the Free Software Foundation, write to the Free Software Foundation; we sometimes make exceptions for this. Our decision will be guided by the two goals of preserving the free status of all derivatives of our free software and of promoting the sharing and reuse of software generally. NO WARRANTY 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. END OF TERMS AND CONDITIONS How to Apply These Terms to Your New Programs If you develop a new program, and you want it to be of the greatest possible use to the public, the best way to achieve this is to make it free software which everyone can redistribute and change under these terms. To do so, attach the following notices to the program. It is safest to attach them to the start of each source file to most effectively convey the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found. Copyright (C) This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA Also add information on how to contact you by electronic and paper mail. If the program is interactive, make it output a short notice like this when it starts in an interactive mode: Gnomovision version 69, Copyright (C) year name of author Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. This is free software, and you are welcome to redistribute it under certain conditions; type `show c' for details. The hypothetical commands `show w' and `show c' should show the appropriate parts of the General Public License. Of course, the commands you use may be called something other than `show w' and `show c'; they could even be mouse-clicks or menu items--whatever suits your program. You should also get your employer (if you work as a programmer) or your school, if any, to sign a "copyright disclaimer" for the program, if necessary. Here is a sample; alter the names: Yoyodyne, Inc., hereby disclaims all copyright interest in the program `Gnomovision' (which makes passes at compilers) written by James Hacker. , 1 April 1989 Ty Coon, President of Vice This General Public License does not permit incorporating your program into proprietary programs. If your program is a subroutine library, you may consider it more useful to permit linking proprietary applications with the library. If this is what you want to do, use the GNU Library General Public License instead of this License. bluez-5.82/PaxHeaders/Makefile.am0000644000000000000000000000005014772767672013733 xustar0020 atime=1743515579 20 ctime=1743591275 bluez-5.82/Makefile.am0000644000000000000000000005756514772767672013436 0ustar00rootroot# SPDX-License-Identifier: GPL-2.0 AM_MAKEFLAGS = --no-print-directory AM_CPPFLAGS = lib_LTLIBRARIES = noinst_LIBRARIES = noinst_LTLIBRARIES = bin_PROGRAMS = noinst_PROGRAMS = CLEANFILES = EXTRA_DIST = pkglibexecdir = @PKGLIBEXECDIR@ pkglibexec_PROGRAMS = pkgincludedir = $(includedir)/bluetooth pkginclude_HEADERS = AM_CFLAGS = $(MISC_CFLAGS) $(WARNING_CFLAGS) $(UDEV_CFLAGS) $(LIBEBOOK_CFLAGS) \ $(LIBEDATASERVER_CFLAGS) $(ell_cflags) AM_LDFLAGS = $(MISC_LDFLAGS) confdir = $(sysconfdir)/bluetooth statedir = $(localstatedir)/lib/bluetooth bluetoothd-fix-permissions: install -dm755 $(DESTDIR)$(confdir) install -dm700 $(DESTDIR)$(statedir) if DATAFILES dbusdir = $(DBUS_CONFDIR)/dbus-1/system.d dbus_DATA = src/bluetooth.conf if OBEX dbus_DATA += obexd/src/obex.conf endif conf_DATA = src/main.conf conf_DATA += profiles/input/input.conf conf_DATA += profiles/network/network.conf state_DATA = endif if SYSTEMD systemdsystemunitdir = $(SYSTEMD_SYSTEMUNITDIR) systemdsystemunit_DATA = src/bluetooth.service systemduserunitdir = $(SYSTEMD_USERUNITDIR) systemduserunit_DATA = dbussystembusdir = $(DBUS_SYSTEMBUSDIR) dbussystembus_DATA = src/org.bluez.service endif EXTRA_DIST += src/org.bluez.service plugindir = $(libdir)/bluetooth/plugins build_plugindir = $(plugindir) if MANPAGES man_MANS = endif manual_pages = plugin_LTLIBRARIES = lib_sources = lib/bluetooth.c lib/hci.c lib/sdp.c lib_headers = lib/bluetooth.h lib/hci.h lib/hci_lib.h \ lib/sco.h lib/l2cap.h lib/sdp.h lib/sdp_lib.h \ lib/rfcomm.h lib/bnep.h lib/cmtp.h lib/hidp.h extra_headers = lib/mgmt.h lib/uuid.h lib/iso.h extra_sources = lib/uuid.c local_headers = $(foreach file,$(lib_headers), lib/bluetooth/$(notdir $(file))) BUILT_SOURCES = $(local_headers) $(ell_built_sources) src/builtin.h if LIBRARY pkginclude_HEADERS += $(lib_headers) lib_LTLIBRARIES += lib/libbluetooth.la lib_libbluetooth_la_SOURCES = $(lib_headers) $(lib_sources) lib_libbluetooth_la_LDFLAGS = $(AM_LDFLAGS) -version-info 22:15:19 endif noinst_LTLIBRARIES += lib/libbluetooth-internal.la lib_libbluetooth_internal_la_SOURCES = $(lib_headers) $(lib_sources) \ $(extra_headers) $(extra_sources) noinst_LTLIBRARIES += gdbus/libgdbus-internal.la gdbus_libgdbus_internal_la_SOURCES = gdbus/gdbus.h \ gdbus/mainloop.c gdbus/watch.c \ gdbus/object.c gdbus/client.c gdbus/polkit.c if EXTERNAL_ELL ell_cflags = @ELL_CFLAGS@ ell_ldadd = @ELL_LIBS@ ell_built_sources = ell/shared else ell_cflags = ell_ldadd = ell/libell-internal.la ell_built_sources = ell/shared ell/internal ell/ell.h noinst_LTLIBRARIES += ell/libell-internal.la ell_headers = ell/util.h \ ell/log.h \ ell/queue.h \ ell/hashmap.h \ ell/random.h \ ell/signal.h \ ell/time.h \ ell/time-private.h \ ell/timeout.h \ ell/cipher.h \ ell/checksum.h \ ell/io.h \ ell/idle.h \ ell/main.h \ ell/settings.h \ ell/strv.h \ ell/string.h \ ell/utf8.h \ ell/dbus.h \ ell/dbus-service.h \ ell/dbus-client.h \ ell/key.h \ ell/cert.h \ ell/pem.h \ ell/base64.h \ ell/asn1-private.h \ ell/cert-private.h \ ell/pem-private.h \ ell/uuid.h \ ell/useful.h \ ell/main-private.h \ ell/tester.h \ ell/tls.h \ ell/tls-private.h \ ell/ecc.h \ ell/ecc-private.h \ ell/cleanup.h \ ell/ecdh.h ell_sources = ell/private.h ell/missing.h \ ell/util.c \ ell/log.c \ ell/queue.c \ ell/hashmap.c \ ell/random.c \ ell/signal.c \ ell/time.c \ ell/timeout.c \ ell/io.c \ ell/idle.c \ ell/main.c \ ell/settings.c \ ell/strv.c \ ell/string.c \ ell/cipher.c \ ell/checksum.c \ ell/pem.c \ ell/cert.c \ ell/cert-crypto.c \ ell/key.c \ ell/base64.c \ ell/utf8.c \ ell/dbus-private.h \ ell/dbus.c \ ell/dbus-message.c \ ell/dbus-util.c \ ell/dbus-service.c \ ell/dbus-client.c \ ell/dbus-name-cache.c \ ell/dbus-filter.c \ ell/gvariant-private.h \ ell/gvariant-util.c \ ell/siphash-private.h \ ell/siphash.c \ ell/uuid.c \ ell/tester.c \ ell/tls.c \ ell/tls-extensions.c \ ell/tls-suites.c \ ell/tls-record.c \ ell/ecc.c \ ell/ecc-external.c \ ell/ecdh.c ell_shared = ell/useful.h ell_libell_internal_la_SOURCES = $(ell_headers) $(ell_sources) $(ell_shared) endif CLEANFILES += $(ell_built_sources) noinst_LTLIBRARIES += src/libshared-glib.la src/libshared-mainloop.la if LIBSHARED_ELL noinst_LTLIBRARIES += src/libshared-ell.la endif shared_sources = src/shared/io.h src/shared/timeout.h \ src/shared/queue.h src/shared/queue.c \ src/shared/util.h src/shared/util.c \ src/shared/mgmt.h src/shared/mgmt.c \ src/shared/crypto.h src/shared/crypto.c \ src/shared/ecc.h src/shared/ecc.c \ src/shared/ringbuf.h src/shared/ringbuf.c \ src/shared/tester.h\ src/shared/hci.h src/shared/hci.c \ src/shared/hci-crypto.h src/shared/hci-crypto.c \ src/shared/hfp.h src/shared/hfp.c \ src/shared/uhid.h src/shared/uhid.c \ src/shared/pcap.h src/shared/pcap.c \ src/shared/btsnoop.h src/shared/btsnoop.c \ src/shared/ad.h src/shared/ad.c \ src/shared/att-types.h \ src/shared/att.h src/shared/att.c \ src/shared/gatt-helpers.h src/shared/gatt-helpers.c \ src/shared/gatt-client.h src/shared/gatt-client.c \ src/shared/gatt-server.h src/shared/gatt-server.c \ src/shared/gatt-db.h src/shared/gatt-db.c \ src/shared/gap.h src/shared/gap.c \ src/shared/log.h src/shared/log.c \ src/shared/bap.h src/shared/bap.c src/shared/ascs.h \ src/shared/bap-debug.h src/shared/bap-debug.c \ src/shared/mcs.h src/shared/mcp.h src/shared/mcp.c \ src/shared/vcp.c src/shared/vcp.h \ src/shared/micp.c src/shared/micp.h \ src/shared/csip.c src/shared/csip.h \ src/shared/bass.h src/shared/bass.c \ src/shared/ccp.h src/shared/ccp.c \ src/shared/lc3.h src/shared/tty.h \ src/shared/bap-defs.h \ src/shared/asha.h src/shared/asha.c if READLINE shared_sources += src/shared/shell.c src/shared/shell.h endif src_libshared_glib_la_SOURCES = $(shared_sources) \ src/shared/io-glib.c \ src/shared/timeout-glib.c \ src/shared/mainloop-glib.c \ src/shared/mainloop-notify.h \ src/shared/mainloop-notify.c \ src/shared/tester.c src_libshared_glib_la_LDFLAGS = $(AM_LDFLAGS) src_libshared_glib_la_CFLAGS = $(AM_CFLAGS) src_libshared_mainloop_la_SOURCES = $(shared_sources) \ src/shared/io-mainloop.c \ src/shared/timeout-mainloop.c \ src/shared/mainloop.h src/shared/mainloop.c \ src/shared/mainloop-notify.h \ src/shared/mainloop-notify.c src_libshared_mainloop_la_LDFLAGS = $(AM_LDFLAGS) src_libshared_mainloop_la_CFLAGS = $(AM_CFLAGS) if LIBSHARED_ELL src_libshared_ell_la_SOURCES = $(shared_sources) \ src/shared/io-ell.c \ src/shared/timeout-ell.c \ src/shared/mainloop.h \ src/shared/mainloop-ell.c src_libshared_ell_la_LDFLAGS = $(AM_LDFLAGS) src_libshared_ell_la_CFLAGS = $(AM_CFLAGS) endif attrib_sources = attrib/att.h attrib/att-database.h attrib/att.c \ attrib/gatt.h attrib/gatt.c \ attrib/gattrib.h attrib/gattrib.c btio_sources = btio/btio.h btio/btio.c gobex_sources = gobex/gobex.h gobex/gobex.c \ gobex/gobex-defs.h gobex/gobex-defs.c \ gobex/gobex-packet.c gobex/gobex-packet.h \ gobex/gobex-header.c gobex/gobex-header.h \ gobex/gobex-transfer.c gobex/gobex-debug.h \ gobex/gobex-apparam.c gobex/gobex-apparam.h builtin_modules = builtin_sources = builtin_cppflags = builtin_ldadd = include Makefile.plugins pkglibexec_PROGRAMS += src/bluetoothd src_bluetoothd_SOURCES = $(builtin_sources) \ $(attrib_sources) $(btio_sources) \ src/main.c src/log.h src/log.c \ src/backtrace.h src/backtrace.c \ src/rfkill.c src/btd.h src/sdpd.h \ src/sdpd-server.c src/sdpd-request.c \ src/sdpd-service.c src/sdpd-database.c \ src/gatt-database.h src/gatt-database.c \ src/sdp-xml.h src/sdp-xml.c \ src/sdp-client.h src/sdp-client.c \ src/textfile.h src/textfile.c \ src/uuid-helper.h src/uuid-helper.c \ src/plugin.h src/plugin.c \ src/storage.h src/storage.c \ src/advertising.h src/advertising.c \ src/agent.h src/agent.c \ src/error.h src/error.c \ src/adapter.h src/adapter.c \ src/profile.h src/profile.c \ src/service.h src/service.c \ src/gatt-client.h src/gatt-client.c \ src/device.h src/device.c \ src/dbus-common.c src/dbus-common.h \ src/eir.h src/eir.c \ src/adv_monitor.h src/adv_monitor.c \ src/battery.h src/battery.c \ src/settings.h src/settings.c \ src/set.h src/set.c src_bluetoothd_LDADD = lib/libbluetooth-internal.la \ gdbus/libgdbus-internal.la \ src/libshared-glib.la \ $(BACKTRACE_LIBS) $(GLIB_LIBS) $(DBUS_LIBS) -ldl -lrt \ $(builtin_ldadd) if EXTERNAL_PLUGINS src_bluetoothd_SOURCES += src/bluetooth.ver src_bluetoothd_LDFLAGS = $(AM_LDFLAGS) -Wl,--export-dynamic \ -Wl,--version-script=$(srcdir)/src/bluetooth.ver endif src_bluetoothd_CPPFLAGS = $(AM_CPPFLAGS) -DBLUETOOTH_PLUGIN_BUILTIN \ -DPLUGINDIR=\""$(build_plugindir)"\" \ $(BACKTRACE_CFLAGS) $(builtin_cppflags) src_bluetoothd_SHORTNAME = bluetoothd builtin_files = src/builtin.h nodist_src_bluetoothd_SOURCES = $(builtin_files) CLEANFILES += $(builtin_files) if MANPAGES man_MANS += src/bluetoothd.8 man_MANS += doc/hci.7 doc/l2cap.7 doc/rfcomm.7 doc/sco.7 man_MANS += doc/org.bluez.Adapter.5 doc/org.bluez.Device.5 \ doc/org.bluez.DeviceSet.5 doc/org.bluez.AgentManager.5 \ doc/org.bluez.Agent.5 doc/org.bluez.ProfileManager.5 \ doc/org.bluez.Profile.5 doc/org.bluez.NetworkServer.5 \ doc/org.bluez.Network.5 doc/org.bluez.Input.5 \ doc/org.bluez.BatteryProviderManager.5 \ doc/org.bluez.BatteryProvider.5 doc/org.bluez.Battery.5 \ doc/org.bluez.AdminPolicySet.5 \ doc/org.bluez.AdminPolicyStatus.5 man_MANS += doc/org.bluez.Media.5 doc/org.bluez.MediaControl.5 \ doc/org.bluez.MediaPlayer.5 doc/org.bluez.MediaFolder.5 \ doc/org.bluez.MediaItem.5 doc/org.bluez.MediaEndpoint.5 \ doc/org.bluez.MediaTransport.5 doc/org.bluez.MediaAssistant.5 man_MANS += doc/org.bluez.GattManager.5 doc/org.bluez.GattProfile.5 \ doc/org.bluez.GattService.5 \ doc/org.bluez.GattCharacteristic.5 \ doc/org.bluez.GattDescriptor.5 \ doc/org.bluez.LEAdvertisingManager.5 \ doc/org.bluez.LEAdvertisement.5 \ doc/org.bluez.AdvertisementMonitorManager.5 \ doc/org.bluez.AdvertisementMonitor.5 man_MANS += doc/org.bluez.obex.Client.5 doc/org.bluez.obex.Session.5 \ doc/org.bluez.obex.Transfer.5 \ doc/org.bluez.obex.ObjectPush.5 \ doc/org.bluez.obex.FileTransfer.5 \ doc/org.bluez.obex.Synchronization.5 \ doc/org.bluez.obex.PhonebookAccess.5 \ doc/org.bluez.obex.MessageAccess.5 \ doc/org.bluez.obex.Message.5 \ doc/org.bluez.obex.AgentManager.5 doc/org.bluez.obex.Agent.5 \ doc/org.bluez.obex.Image.5 endif manual_pages += src/bluetoothd.8 manual_pages += doc/hci.7 doc/l2cap.7 doc/rfcomm.7 doc/sco.7 manual_pages += doc/org.bluez.Adapter.5 doc/org.bluez.Device.5 \ doc/org.bluez.DeviceSet.5 doc/org.bluez.AgentManager.5 \ doc/org.bluez.Agent.5 doc/org.bluez.ProfileManager.5 \ doc/org.bluez.Profile.5 doc/org.bluez.NetworkServer.5 \ doc/org.bluez.Network.5 doc/org.bluez.Input.5\ doc/org.bluez.BatteryProviderManager.5 \ doc/org.bluez.BatteryProvider.5 doc/org.bluez.Battery.5 \ doc/org.bluez.AdminPolicySet.5 \ doc/org.bluez.AdminPolicyStatus.5 manual_pages += doc/org.bluez.Media.5 doc/org.bluez.MediaControl.5 \ doc/org.bluez.MediaPlayer.5 doc/org.bluez.MediaFolder.5 \ doc/org.bluez.MediaItem.5 doc/org.bluez.MediaEndpoint.5 \ doc/org.bluez.MediaTransport.5 doc/org.bluez.MediaAssistant.5 manual_pages += doc/org.bluez.GattManager.5 doc/org.bluez.GattProfile.5 \ doc/org.bluez.GattService.5 \ doc/org.bluez.GattCharacteristic.5 \ doc/org.bluez.GattDescriptor.5 \ doc/org.bluez.LEAdvertisingManager.5 \ doc/org.bluez.LEAdvertisement.5 \ doc/org.bluez.AdvertisementMonitorManager.5 \ doc/org.bluez.AdvertisementMonitor.5 manual_pages += doc/org.bluez.obex.Client.5 doc/org.bluez.obex.Session.5 \ doc/org.bluez.obex.Transfer.5 \ doc/org.bluez.obex.ObjectPush.5 \ doc/org.bluez.obex.FileTransfer.5 \ doc/org.bluez.obex.Synchronization.5 \ doc/org.bluez.obex.PhonebookAccess.5 \ doc/org.bluez.obex.MessageAccess.5 \ doc/org.bluez.obex.Message.5 \ doc/org.bluez.obex.AgentManager.5 doc/org.bluez.obex.Agent.5 \ doc/org.bluez.obex.Image.5 EXTRA_DIST += src/genbuiltin src/bluetooth.conf \ src/main.conf profiles/network/network.conf \ profiles/input/input.conf obexd/src/obex.conf test_scripts = unit_tests = include Makefile.tools include Makefile.obexd include android/Makefile.am include Makefile.mesh if SYSTEMD install-data-hook: obexd-add-service-symlink else install-data-hook: bluetoothd-fix-permissions obexd-add-service-symlink endif uninstall-hook: obexd-remove-service-symlink if HID2HCI rulesdir = $(UDEV_DIR)/rules.d rules_DATA = tools/97-hid2hci.rules CLEANFILES += $(rules_DATA) endif EXTRA_DIST += tools/hid2hci.rules if TEST testdir = $(pkglibdir)/test test_SCRIPTS = $(test_scripts) endif EXTRA_DIST += $(test_scripts) EXTRA_DIST += doc/assigned-numbers.txt doc/supported-features.txt \ doc/test-coverage.txt \ doc/test-runner.rst \ doc/settings-storage.txt EXTRA_DIST += doc/mgmt-api.txt \ doc/health-api.txt \ doc/sap-api.txt EXTRA_DIST += doc/hci.rst doc/l2cap.rst doc/rfcomm.rst doc/sco.rst EXTRA_DIST += doc/org.bluez.Adapter.rst doc/org.bluez.Device.rst \ doc/org.bluez.DeviceSet.rst doc/org.bluez.AgentManager.rst \ doc/org.bluez.Agent.rst doc/org.bluez.ProfileManager.rst \ doc/org.bluez.Profile.rst doc/org.bluez.NetworkServer.rst \ doc/org.bluez.Network.rst doc/org.bluez.Input.rst \ doc/org.bluez.BatteryProviderManager.rst \ doc/org.bluez.BatteryProvider.rst doc/org.bluez.Battery.rst \ doc/org.bluez.AdminPolicySet.rst \ doc/org.bluez.AdminPolicyStatus.rst EXTRA_DIST += doc/org.bluez.Media.rst doc/org.bluez.MediaControl.rst \ doc/org.bluez.MediaPlayer.rst doc/org.bluez.MediaFolder.rst \ doc/org.bluez.MediaItem.rst doc/org.bluez.MediaEndpoint.rst \ doc/org.bluez.MediaTransport.rst doc/org.bluez.MediaAssistant.rst EXTRA_DIST += doc/org.bluez.GattManager.rst doc/org.bluez.GattProfile.rst\ doc/org.bluez.GattService.rst \ doc/org.bluez.GattCharacteristic.rst \ doc/org.bluez.GattDescriptor.rst \ doc/org.bluez.LEAdvertisingManager.rst \ doc/org.bluez.LEAdvertisement.rst \ doc/org.bluez.AdvertisementMonitorManager.rst \ doc/org.bluez.AdvertisementMonitor.rst EXTRA_DIST += doc/org.bluez.obex.Client.rst doc/org.bluez.obex.Session.rst \ doc/org.bluez.obex.Transfer.rst \ doc/org.bluez.obex.ObjectPush.rst \ doc/org.bluez.obex.FileTransfer.rst \ doc/org.bluez.obex.Synchronization.rst \ doc/org.bluez.obex.PhonebookAccess.rst \ doc/org.bluez.obex.MessageAccess.rst \ doc/org.bluez.obex.Message.rst \ doc/org.bluez.obex.AgentManager.rst doc/org.bluez.obex.Agent.rst \ doc/org.bluez.obex.Image.rst EXTRA_DIST += doc/pics-opp.txt doc/pixit-opp.txt \ doc/pts-opp.txt EXTRA_DIST += doc/btsnoop.txt EXTRA_DIST += tools/magic.btsnoop AM_CPPFLAGS += $(DBUS_CFLAGS) $(GLIB_CFLAGS) -I$(builddir)/lib unit_tests += unit/test-tester unit_test_tester_SOURCES = unit/test-tester.c unit_test_tester_LDADD = src/libshared-glib.la lib/libbluetooth-internal.la \ $(GLIB_LIBS) unit_tests += unit/test-eir unit_test_eir_SOURCES = unit/test-eir.c src/eir.c src/uuid-helper.c unit_test_eir_LDADD = src/libshared-glib.la lib/libbluetooth-internal.la \ $(GLIB_LIBS) unit_tests += unit/test-uuid unit_test_uuid_SOURCES = unit/test-uuid.c unit_test_uuid_LDADD = src/libshared-glib.la lib/libbluetooth-internal.la \ $(GLIB_LIBS) unit_tests += unit/test-textfile unit_test_textfile_SOURCES = unit/test-textfile.c src/textfile.h src/textfile.c unit_test_textfile_LDADD = src/libshared-glib.la $(GLIB_LIBS) unit_tests += unit/test-crc unit_test_crc_SOURCES = unit/test-crc.c monitor/crc.h monitor/crc.c unit_test_crc_LDADD = src/libshared-glib.la $(GLIB_LIBS) unit_tests += unit/test-crypto unit_test_crypto_SOURCES = unit/test-crypto.c unit_test_crypto_LDADD = src/libshared-glib.la $(GLIB_LIBS) unit_tests += unit/test-ecc unit_test_ecc_SOURCES = unit/test-ecc.c unit_test_ecc_LDADD = src/libshared-glib.la $(GLIB_LIBS) unit_tests += unit/test-ringbuf unit/test-queue unit_test_ringbuf_SOURCES = unit/test-ringbuf.c unit_test_ringbuf_LDADD = src/libshared-glib.la $(GLIB_LIBS) unit_test_queue_SOURCES = unit/test-queue.c unit_test_queue_LDADD = src/libshared-glib.la $(GLIB_LIBS) unit_tests += unit/test-mgmt unit_test_mgmt_SOURCES = unit/test-mgmt.c unit_test_mgmt_LDADD = src/libshared-glib.la $(GLIB_LIBS) unit_tests += unit/test-uhid unit_test_uhid_SOURCES = unit/test-uhid.c unit_test_uhid_LDADD = src/libshared-glib.la $(GLIB_LIBS) unit_tests += unit/test-sdp unit_test_sdp_SOURCES = unit/test-sdp.c \ src/sdpd.h src/sdpd-database.c \ src/log.h src/log.c \ src/sdpd-service.c src/sdpd-request.c unit_test_sdp_LDADD = lib/libbluetooth-internal.la \ src/libshared-glib.la $(GLIB_LIBS) unit_tests += unit/test-avdtp unit_test_avdtp_SOURCES = unit/test-avdtp.c \ src/log.h src/log.c \ android/avdtp.c android/avdtp.h unit_test_avdtp_LDADD = src/libshared-glib.la $(GLIB_LIBS) unit_tests += unit/test-avctp unit_test_avctp_SOURCES = unit/test-avctp.c \ src/log.h src/log.c \ android/avctp.c android/avctp.h unit_test_avctp_LDADD = src/libshared-glib.la $(GLIB_LIBS) unit_tests += unit/test-avrcp unit_test_avrcp_SOURCES = unit/test-avrcp.c \ src/log.h src/log.c \ android/avctp.c android/avctp.h \ android/avrcp-lib.c android/avrcp-lib.h unit_test_avrcp_LDADD = lib/libbluetooth-internal.la \ src/libshared-glib.la $(GLIB_LIBS) unit_tests += unit/test-hfp unit_test_hfp_SOURCES = unit/test-hfp.c unit_test_hfp_LDADD = src/libshared-glib.la $(GLIB_LIBS) unit_tests += unit/test-gdbus-client unit_test_gdbus_client_SOURCES = unit/test-gdbus-client.c unit_test_gdbus_client_LDADD = gdbus/libgdbus-internal.la \ src/libshared-glib.la $(GLIB_LIBS) $(DBUS_LIBS) if OBEX unit_tests += unit/test-gobex-header unit/test-gobex-packet unit/test-gobex \ unit/test-gobex-transfer unit/test-gobex-apparam unit_test_gobex_SOURCES = $(gobex_sources) unit/util.c unit/util.h \ unit/test-gobex.c unit_test_gobex_LDADD = src/libshared-glib.la $(GLIB_LIBS) unit_test_gobex_packet_SOURCES = $(gobex_sources) unit/util.c unit/util.h \ unit/test-gobex-packet.c unit_test_gobex_packet_LDADD = src/libshared-glib.la $(GLIB_LIBS) unit_test_gobex_header_SOURCES = $(gobex_sources) unit/util.c unit/util.h \ unit/test-gobex-header.c unit_test_gobex_header_LDADD = src/libshared-glib.la $(GLIB_LIBS) unit_test_gobex_transfer_SOURCES = $(gobex_sources) unit/util.c unit/util.h \ unit/test-gobex-transfer.c unit_test_gobex_transfer_LDADD = src/libshared-glib.la $(GLIB_LIBS) unit_test_gobex_apparam_SOURCES = $(gobex_sources) unit/util.c unit/util.h \ unit/test-gobex-apparam.c unit_test_gobex_apparam_LDADD = src/libshared-glib.la $(GLIB_LIBS) endif unit_tests += unit/test-lib unit_test_lib_SOURCES = unit/test-lib.c unit_test_lib_LDADD = src/libshared-glib.la \ lib/libbluetooth-internal.la $(GLIB_LIBS) unit_tests += unit/test-gatt unit_test_gatt_SOURCES = unit/test-gatt.c unit_test_gatt_LDADD = src/libshared-glib.la \ lib/libbluetooth-internal.la $(GLIB_LIBS) unit_tests += unit/test-hog unit_test_hog_SOURCES = unit/test-hog.c \ $(btio_sources) \ profiles/input/hog-lib.h profiles/input/hog-lib.c \ profiles/scanparam/scpp.h profiles/scanparam/scpp.c \ profiles/battery/bas.h profiles/battery/bas.c \ profiles/deviceinfo/dis.h profiles/deviceinfo/dis.c \ src/log.h src/log.c \ attrib/att.h attrib/att.c \ attrib/gatt.h attrib/gatt.c \ attrib/gattrib.h attrib/gattrib.c unit_test_hog_LDADD = src/libshared-glib.la \ lib/libbluetooth-internal.la $(GLIB_LIBS) unit_tests += unit/test-gattrib unit_test_gattrib_SOURCES = unit/test-gattrib.c attrib/gattrib.c \ $(btio_sources) src/log.h src/log.c unit_test_gattrib_LDADD = src/libshared-glib.la \ lib/libbluetooth-internal.la \ $(GLIB_LIBS) $(DBUS_LIBS) -ldl -lrt unit_tests += unit/test-bap unit_test_bap_SOURCES = unit/test-bap.c unit_test_bap_LDADD = src/libshared-glib.la \ lib/libbluetooth-internal.la $(GLIB_LIBS) unit_tests += unit/test-micp unit_test_micp_SOURCES = unit/test-micp.c unit_test_micp_LDADD = src/libshared-glib.la \ lib/libbluetooth-internal.la $(GLIB_LIBS) unit_tests += unit/test-bass unit_test_bass_SOURCES = unit/test-bass.c $(btio_sources) unit_test_bass_LDADD = src/libshared-glib.la \ lib/libbluetooth-internal.la $(GLIB_LIBS) unit_tests += unit/test-vcp unit_test_vcp_SOURCES = unit/test-vcp.c $(btio_sources) unit_test_vcp_LDADD = src/libshared-glib.la \ lib/libbluetooth-internal.la $(GLIB_LIBS) if MIDI unit_tests += unit/test-midi unit_test_midi_CPPFLAGS = $(AM_CPPFLAGS) $(ALSA_CFLAGS) -DMIDI_TEST unit_test_midi_SOURCES = unit/test-midi.c \ profiles/midi/libmidi.h \ profiles/midi/libmidi.c unit_test_midi_LDADD = src/libshared-glib.la \ $(GLIB_LIBS) $(ALSA_LIBS) endif if MESH unit_tests += unit/test-mesh-crypto unit_test_mesh_crypto_CPPFLAGS = $(ell_cflags) unit_test_mesh_crypto_SOURCES = unit/test-mesh-crypto.c \ mesh/crypto.h ell/internal ell/ell.h unit_test_mesh_crypto_LDADD = $(ell_ldadd) endif if MAINTAINER_MODE noinst_PROGRAMS += $(unit_tests) endif TESTS = $(unit_tests) AM_TESTS_ENVIRONMENT = MALLOC_CHECK_=3 MALLOC_PERTURB_=69 if DBUS_RUN_SESSION AM_TESTS_ENVIRONMENT += dbus-run-session -- endif if VALGRIND LOG_COMPILER = valgrind --error-exitcode=1 --num-callers=30 LOG_FLAGS = --trace-children=yes --leak-check=full --show-reachable=no \ --suppressions=$(srcdir)/tools/valgrind.supp --quiet endif pkgconfigdir = $(libdir)/pkgconfig if LIBRARY pkgconfig_DATA = lib/bluez.pc endif EXTRA_DIST += $(manual_pages) $(patsubst %.1,%.rst, \ $(patsubst %.8,%.rst,$(manual_pages))) DISTCHECK_CONFIGURE_FLAGS = --disable-datafiles --enable-library \ --enable-health \ --enable-midi \ --enable-manpages \ --enable-android \ --enable-mesh \ --enable-btpclient \ --disable-systemd \ --disable-udev DISTCLEANFILES = $(pkgconfig_DATA) $(unit_tests) $(manual_pages) MAINTAINERCLEANFILES = Makefile.in \ aclocal.m4 configure config.h.in config.sub config.guess \ ltmain.sh depcomp compile missing install-sh mkinstalldirs test-driver if RUN_RST2MAN RST2MAN_PROCESS = $(AM_V_GEN)$(MKDIR_P) $(dir $@) && \ $(RST2MAN) --strict --no-raw \ --no-generator --no-datestamp $< $@ else RST2MAN_PROCESS = $(AM_V_GEN)test -f $@ || \ { echo "Generated manual page $@ does not exist"; false; } endif %.1: %.rst Makefile $(RST2MAN_PROCESS) %.5: %.rst Makefile $(RST2MAN_PROCESS) %.7: %.rst Makefile $(RST2MAN_PROCESS) %.8: %.rst Makefile $(RST2MAN_PROCESS) src/builtin.h: src/genbuiltin $(builtin_sources) $(AM_V_GEN)$(srcdir)/src/genbuiltin $(builtin_modules) > $@ tools/%.rules: $(AM_V_at)$(MKDIR_P) tools $(AM_V_GEN)cp $(srcdir)/$(subst 97-,,$@) $@ $(lib_libbluetooth_la_OBJECTS): $(local_headers) lib/bluetooth/%.h: lib/%.h $(AM_V_at)$(MKDIR_P) lib/bluetooth $(AM_V_GEN)$(LN_S) -f $(abspath $<) $@ ell/shared: Makefile $(AM_V_at)$(MKDIR_P) ell $(AM_V_GEN)for f in $(ell_shared) ; do \ if [ ! -f $$f ] ; then \ $(LN_S) -t ell -f $(abs_srcdir)/../ell/$$f ; \ fi \ done > $@ ell/internal: Makefile $(AM_V_at)$(MKDIR_P) ell $(AM_V_GEN)for f in $(ell_headers) $(ell_sources) ; do \ if [ ! -f $$f ] ; then \ $(LN_S) -t ell -f $(abs_srcdir)/../ell/$$f ; \ fi \ done > $@ ell/ell.h: Makefile $(AM_V_at)echo -n > $@ $(AM_V_GEN)for f in $(ell_headers) ; do \ echo "#include <$$f>" >> $@ ; \ done maintainer-clean-local: -rm -rf ell clean-coverage: if COVERAGE @lcov --directory $(top_builddir) --zerocounters $(RM) -r coverage $(top_builddir)/coverage.info coverage: check @lcov --compat-libtool --directory $(top_builddir) --capture \ --output-file $(top_builddir)/coverage.info $(AM_V_at)$(MKDIR_P) coverage @genhtml -o coverage/ $(top_builddir)/coverage.info endif clean-local: clean-coverage -find $(top_builddir) -name "*.gcno" -delete -find $(top_builddir) -name "*.gcda" -delete $(RM) -r lib/bluetooth bluez-5.82/PaxHeaders/ell0000644000000000000000000000005014773213570012356 xustar0020 atime=1743591291 20 ctime=1743591288 bluez-5.82/ell/0000755000000000000000000000000014773213570012114 5ustar00rootrootbluez-5.82/ell/PaxHeaders/queue.c0000644000000000000000000000005014601374755013726 xustar0020 atime=1743575464 20 ctime=1743591276 bluez-5.82/ell/queue.c0000644000000000000000000002465714601374755013425 0ustar00rootroot/* * Embedded Linux library * Copyright (C) 2011-2014 Intel Corporation * * SPDX-License-Identifier: LGPL-2.1-or-later */ #ifdef HAVE_CONFIG_H #include #endif #include "queue.h" #include "private.h" #include "useful.h" /** * SECTION:queue * @short_description: Queue support * * Queue support */ /** * l_queue: * * Opaque object representing the queue. */ struct l_queue { struct l_queue_entry *head; struct l_queue_entry *tail; unsigned int entries; }; /** * l_queue_new: * * Create a new queue. * * No error handling is needed since. In case of real memory allocation * problems abort() will be called. * * Returns: a newly allocated #l_queue object **/ LIB_EXPORT struct l_queue *l_queue_new(void) { struct l_queue *queue; queue = l_new(struct l_queue, 1); queue->head = NULL; queue->tail = NULL; queue->entries = 0; return queue; } /** * l_queue_destroy: * @queue: queue object * @destroy: destroy function * * Free queue and call @destroy on all remaining entries. **/ LIB_EXPORT void l_queue_destroy(struct l_queue *queue, l_queue_destroy_func_t destroy) { l_queue_clear(queue, destroy); l_free(queue); } /** * l_queue_clear: * @queue: queue object * @destroy: destroy function * * Clear queue and call @destroy on all remaining entries. **/ LIB_EXPORT void l_queue_clear(struct l_queue *queue, l_queue_destroy_func_t destroy) { struct l_queue_entry *entry; if (unlikely(!queue)) return; entry = queue->head; while (entry) { struct l_queue_entry *tmp = entry; if (destroy) destroy(entry->data); entry = entry->next; l_free(tmp); } queue->head = NULL; queue->tail = NULL; queue->entries = 0; } /** * l_queue_push_tail: * @queue: queue object * @data: pointer to data * * Adds @data pointer at the end of the queue. * * Returns: #true when data has been added and #false in case an invalid * @queue object has been provided **/ LIB_EXPORT bool l_queue_push_tail(struct l_queue *queue, void *data) { struct l_queue_entry *entry; if (unlikely(!queue)) return false; entry = l_new(struct l_queue_entry, 1); entry->data = data; entry->next = NULL; if (queue->tail) queue->tail->next = entry; queue->tail = entry; if (!queue->head) queue->head = entry; queue->entries++; return true; } /** * l_queue_push_head: * @queue: queue object * @data: pointer to data * * Adds @data pointer at the start of the queue. * * Returns: #true when data has been added and #false in case an invalid * @queue object has been provided **/ LIB_EXPORT bool l_queue_push_head(struct l_queue *queue, void *data) { struct l_queue_entry *entry; if (unlikely(!queue)) return false; entry = l_new(struct l_queue_entry, 1); entry->data = data; entry->next = queue->head; queue->head = entry; if (!queue->tail) queue->tail = entry; queue->entries++; return true; } /** * l_queue_pop_head: * @queue: queue object * * Removes the first element of the queue an returns it. * * Returns: data pointer to first element or #NULL in case an empty queue **/ LIB_EXPORT void *l_queue_pop_head(struct l_queue *queue) { struct l_queue_entry *entry; void *data; if (unlikely(!queue)) return NULL; if (!queue->head) return NULL; entry = queue->head; if (!queue->head->next) { queue->head = NULL; queue->tail = NULL; } else queue->head = queue->head->next; data = entry->data; l_free(entry); queue->entries--; return data; } /** * l_queue_peek_head: * @queue: queue object * * Peeks at the first element of the queue an returns it. * * Returns: data pointer to first element or #NULL in case an empty queue **/ LIB_EXPORT void *l_queue_peek_head(struct l_queue *queue) { struct l_queue_entry *entry; if (unlikely(!queue)) return NULL; if (!queue->head) return NULL; entry = queue->head; return entry->data; } /** * l_queue_peek_tail: * @queue: queue object * * Peeks at the last element of the queue an returns it. * * Returns: data pointer to first element or #NULL in case an empty queue **/ LIB_EXPORT void *l_queue_peek_tail(struct l_queue *queue) { struct l_queue_entry *entry; if (unlikely(!queue)) return NULL; if (!queue->tail) return NULL; entry = queue->tail; return entry->data; } /** * l_queue_insert: * @queue: queue object * @data: pointer to data * @function: compare function * @user_data: user data given to compare function * * Inserts @data pointer at a position in the queue determined by the * compare @function. @function should return >= 0 if the @data (first * parameter) should be inserted after the current entry (second parameter) * and should return < 0 if before it. * * Returns: #true when data has been added and #false in case of failure **/ LIB_EXPORT bool l_queue_insert(struct l_queue *queue, void *data, l_queue_compare_func_t function, void *user_data) { struct l_queue_entry *entry, *prev, *cur; int cmp; if (unlikely(!queue || !function)) return false; entry = l_new(struct l_queue_entry, 1); entry->data = data; entry->next = NULL; if (!queue->head) { queue->head = entry; queue->tail = entry; goto done; } for (prev = NULL, cur = queue->head; cur; prev = cur, cur = cur->next) { cmp = function(entry->data, cur->data, user_data); if (cmp >= 0) continue; if (prev == NULL) { entry->next = queue->head; queue->head = entry; goto done; } entry->next = cur; prev->next = entry; goto done; } queue->tail->next = entry; queue->tail = entry; done: queue->entries++; return true; } /** * l_queue_find: * @queue: queue object * @function: match function * @user_data: user data given to compare function * * Finds an entry in the queue by running the match @function * * Returns: Matching entry or NULL if no entry can be found **/ LIB_EXPORT void *l_queue_find(struct l_queue *queue, l_queue_match_func_t function, const void *user_data) { struct l_queue_entry *entry; if (unlikely(!queue || !function)) return NULL; for (entry = queue->head; entry; entry = entry->next) if (function(entry->data, user_data)) return entry->data; return NULL; } /** * l_queue_remove: * @queue: queue object * @data: pointer to data * * Remove given @data from the queue. * * Returns: #true when data has been removed and #false when data could not * be found or an invalid @queue object has been provided **/ LIB_EXPORT bool l_queue_remove(struct l_queue *queue, void *data) { struct l_queue_entry *entry, *prev; if (unlikely(!queue)) return false; for (entry = queue->head, prev = NULL; entry; prev = entry, entry = entry->next) { if (entry->data != data) continue; if (prev) prev->next = entry->next; else queue->head = entry->next; if (!entry->next) queue->tail = prev; l_free(entry); queue->entries--; return true; } return false; } /** * l_queue_reverse: * @queue: queue object * * Reverse entries in the queue. * * Returns: #true on success and #false on failure **/ LIB_EXPORT bool l_queue_reverse(struct l_queue *queue) { struct l_queue_entry *entry, *prev = NULL; if (unlikely(!queue)) return false; entry = queue->head; while (entry) { struct l_queue_entry *next = entry->next; entry->next = prev; prev = entry; entry = next; } queue->tail = queue->head; queue->head = prev; return true; } /** * l_queue_foreach: * @queue: queue object * @function: callback function * @user_data: user data given to callback function * * Call @function for every given data in @queue. **/ LIB_EXPORT void l_queue_foreach(struct l_queue *queue, l_queue_foreach_func_t function, void *user_data) { struct l_queue_entry *entry; if (unlikely(!queue || !function)) return; for (entry = queue->head; entry; entry = entry->next) function(entry->data, user_data); } /** * l_queue_foreach_remove: * @queue: queue object * @function: callback function * @user_data: user data given to callback function * * Remove all entries in the @queue where @function returns #true. * * Returns: number of removed entries **/ LIB_EXPORT unsigned int l_queue_foreach_remove(struct l_queue *queue, l_queue_remove_func_t function, void *user_data) { struct l_queue_entry *entry, *prev = NULL; unsigned int count = 0; if (unlikely(!queue || !function)) return 0; entry = queue->head; while (entry) { if (function(entry->data, user_data)) { struct l_queue_entry *tmp = entry; if (prev) prev->next = entry->next; else queue->head = entry->next; if (!entry->next) queue->tail = prev; entry = entry->next; l_free(tmp); count++; } else { prev = entry; entry = entry->next; } } queue->entries -= count; return count; } /** * l_queue_remove_if * @queue: queue object * @function: callback function * @user_data: user data given to callback function * * Remove the first entry in the @queue where the function returns #true. * * Returns: NULL if no entry was found, or the entry data if removal was * successful. **/ LIB_EXPORT void *l_queue_remove_if(struct l_queue *queue, l_queue_match_func_t function, const void *user_data) { struct l_queue_entry *entry, *prev = NULL; if (unlikely(!queue || !function)) return NULL; entry = queue->head; while (entry) { if (function(entry->data, user_data)) { struct l_queue_entry *tmp = entry; void *data; if (prev) prev->next = entry->next; else queue->head = entry->next; if (!entry->next) queue->tail = prev; data = tmp->data; l_free(tmp); queue->entries--; return data; } prev = entry; entry = entry->next; } return NULL; } /** * l_queue_length: * @queue: queue object * * Returns: entries of the queue **/ LIB_EXPORT unsigned int l_queue_length(struct l_queue *queue) { if (unlikely(!queue)) return 0; return queue->entries; } /** * l_queue_isempty: * @queue: queue object * * Returns: #true if @queue is empty and #false is not **/ LIB_EXPORT bool l_queue_isempty(struct l_queue *queue) { if (unlikely(!queue)) return true; return queue->entries == 0; } /** * l_queue_get_entries: * @queue: queue object * * This function gives direct, read-only access to the internal list structure * of the queue. This can be used to efficiently traverse the elements. * * Returns: A pointer to the head of the queue. **/ LIB_EXPORT const struct l_queue_entry *l_queue_get_entries( const struct l_queue *queue) { if (unlikely(!queue)) return NULL; return queue->head; } bluez-5.82/ell/PaxHeaders/tester.h0000644000000000000000000000005014560467756014125 xustar0020 atime=1743575449 20 ctime=1743591276 bluez-5.82/ell/tester.h0000644000000000000000000000466714560467756013623 0ustar00rootroot/* * Embedded Linux library * Copyright (C) 2021 Intel Corporation * * SPDX-License-Identifier: LGPL-2.1-or-later */ #ifndef __ELL_TESTER_H #define __ELL_TESTER_H #include #include #include #ifdef __cplusplus extern "C" { #endif struct l_tester; enum l_tester_stage { L_TESTER_STAGE_INVALID, L_TESTER_STAGE_PRE_SETUP, L_TESTER_STAGE_SETUP, L_TESTER_STAGE_RUN, L_TESTER_STAGE_TEARDOWN, L_TESTER_STAGE_POST_TEARDOWN, }; typedef void (*l_tester_destroy_func_t)(void *user_data); typedef void (*l_tester_data_func_t)(const void *test_data); typedef void (*l_tester_finish_func_t)(struct l_tester *tester); typedef void (*l_tester_wait_func_t)(void *user_data); struct l_tester *l_tester_new(const char *prefix, const char *substring, bool list_cases); void l_tester_destroy(struct l_tester *tester); void l_tester_start(struct l_tester *tester, l_tester_finish_func_t finish_func); bool l_tester_summarize(struct l_tester *tester); void l_tester_add_full(struct l_tester *tester, const char *name, const void *test_data, l_tester_data_func_t pre_setup_func, l_tester_data_func_t setup_func, l_tester_data_func_t test_func, l_tester_data_func_t teardown_func, l_tester_data_func_t post_teardown_func, unsigned int timeout, void *user_data, l_tester_destroy_func_t destroy); void l_tester_add(struct l_tester *tester, const char *name, const void *test_data, l_tester_data_func_t setup_func, l_tester_data_func_t test_func, l_tester_data_func_t teardown_func); void l_tester_pre_setup_complete(struct l_tester *tester); void l_tester_pre_setup_failed(struct l_tester *tester); void l_tester_setup_complete(struct l_tester *tester); void l_tester_setup_failed(struct l_tester *tester); void l_tester_test_passed(struct l_tester *tester); void l_tester_test_failed(struct l_tester *tester); void l_tester_test_abort(struct l_tester *tester); void l_tester_teardown_complete(struct l_tester *tester); void l_tester_teardown_failed(struct l_tester *tester); void l_tester_post_teardown_complete(struct l_tester *tester); void l_tester_post_teardown_failed(struct l_tester *tester); enum l_tester_stage l_tester_get_stage(struct l_tester *tester); void *l_tester_get_data(struct l_tester *tester); void l_tester_wait(struct l_tester *tester, unsigned int seconds, l_tester_wait_func_t func, void *user_data); #ifdef __cplusplus } #endif #endif /* __ELL_TESTER_H */ bluez-5.82/ell/PaxHeaders/cert-private.h0000644000000000000000000000005014504767710015213 xustar0020 atime=1743575449 20 ctime=1743591276 bluez-5.82/ell/cert-private.h0000644000000000000000000000230614504767710014675 0ustar00rootroot/* * Embedded Linux library * Copyright (C) 2018 Intel Corporation * * SPDX-License-Identifier: LGPL-2.1-or-later */ struct asn1_oid; struct l_certchain *certchain_new_from_leaf(struct l_cert *leaf); void certchain_link_issuer(struct l_certchain *chain, struct l_cert *ca); const uint8_t *cert_get_extension(struct l_cert *cert, const struct asn1_oid *ext_id, bool *out_critical, size_t *out_len); struct l_key *cert_key_from_pkcs8_private_key_info(const uint8_t *der, size_t der_len); struct l_key *cert_key_from_pkcs8_encrypted_private_key_info(const uint8_t *der, size_t der_len, const char *passphrase); struct l_key *cert_key_from_pkcs1_rsa_private_key(const uint8_t *der, size_t der_len); struct cert_pkcs12_hash { enum l_checksum_type alg; unsigned int len; unsigned int u; unsigned int v; struct asn1_oid oid; }; uint8_t *cert_pkcs12_pbkdf(const char *password, const struct cert_pkcs12_hash *hash, const uint8_t *salt, size_t salt_len, unsigned int iterations, uint8_t id, size_t key_len); struct l_cipher *cert_cipher_from_pkcs_alg_id(const uint8_t *id_asn1, size_t id_asn1_len, const char *password, bool *out_is_block); bluez-5.82/ell/PaxHeaders/cleanup.h0000644000000000000000000000005014560467756014246 xustar0020 atime=1743575449 20 ctime=1743591276 bluez-5.82/ell/cleanup.h0000644000000000000000000000055714560467756013736 0ustar00rootroot/* * Embedded Linux library * Copyright (C) 2021 Intel Corporation * * SPDX-License-Identifier: LGPL-2.1-or-later */ #pragma once #define __L_AUTODESTRUCT(func) \ __attribute((cleanup(_l_ ## func ## _cleanup))) #define DEFINE_CLEANUP_FUNC(func) \ inline __attribute__((always_inline)) \ void _l_ ## func ## _cleanup(void *p) { func(*(void **) p); } bluez-5.82/ell/PaxHeaders/siphash-private.h0000644000000000000000000000005014504767710015715 xustar0020 atime=1743575521 20 ctime=1743591277 bluez-5.82/ell/siphash-private.h0000644000000000000000000000040714504767710015377 0ustar00rootroot/* * Embedded Linux library * Copyright (C) 2011-2014 Intel Corporation * * SPDX-License-Identifier: LGPL-2.1-or-later */ #include #include void _siphash24(uint8_t out[8], const uint8_t *in, size_t inlen, const uint8_t k[16]); bluez-5.82/ell/PaxHeaders/tls.c0000644000000000000000000000005014560467756013414 xustar0020 atime=1743575524 20 ctime=1743591277 bluez-5.82/ell/tls.c0000644000000000000000000032004714560467756013103 0ustar00rootroot/* * Embedded Linux library * Copyright (C) 2015 Intel Corporation * * SPDX-License-Identifier: LGPL-2.1-or-later */ #ifdef HAVE_CONFIG_H #include #endif #define _GNU_SOURCE #include #include #include #include #include #include "useful.h" #include "private.h" #include "tls.h" #include "checksum.h" #include "cipher.h" #include "random.h" #include "queue.h" #include "pem.h" #include "pem-private.h" #include "asn1-private.h" #include "cert.h" #include "cert-private.h" #include "tls-private.h" #include "key.h" #include "strv.h" #include "missing.h" #include "string.h" #include "settings.h" #include "time.h" #include "time-private.h" bool tls10_prf(const void *secret, size_t secret_len, const char *label, const void *seed, size_t seed_len, uint8_t *out, size_t out_len) { uint8_t p_hash2[out_len]; uint8_t l_s1 = (secret_len + 1) / 2; unsigned int i; /* * RFC2246 section 5: * S1 and S2 are the two halves of the secret, and each is the same * length. S1 is taken from the first half of the secret, S2 from the * second half. Their length is created by rounding up the length of * the overall secret, divided by two; thus, if the original secret is * an odd number of bytes long, the last byte of S1 will be the same as * the first byte of S2. */ if (!tls12_prf(L_CHECKSUM_MD5, secret, l_s1, label, seed, seed_len, out, out_len)) return false; if (secret_len > 0) secret += secret_len - l_s1; if (!tls12_prf(L_CHECKSUM_SHA1, secret, l_s1, label, seed, seed_len, p_hash2, out_len)) return false; for (i = 0; i < out_len; i++) out[i] ^= p_hash2[i]; return true; } bool tls12_prf(enum l_checksum_type type, const void *secret, size_t secret_len, const char *label, const void *seed, size_t seed_len, uint8_t *out, size_t out_len) { struct l_checksum *hmac = l_checksum_new_hmac(type, secret, secret_len); size_t a_len, chunk_len, prfseed_len = strlen(label) + seed_len; uint8_t a[64 + prfseed_len], prfseed[prfseed_len]; if (!hmac) return false; /* Generate the hash seed or A(0) as label + seed */ memcpy(prfseed, label, strlen(label)); memcpy(prfseed + strlen(label), seed, seed_len); memcpy(a, prfseed, prfseed_len); a_len = prfseed_len; while (out_len) { /* Generate A(i) */ l_checksum_reset(hmac); l_checksum_update(hmac, a, a_len); a_len = l_checksum_get_digest(hmac, a, sizeof(a)); /* Append seed & generate output */ memcpy(a + a_len, prfseed, prfseed_len); l_checksum_reset(hmac); l_checksum_update(hmac, a, a_len + prfseed_len); chunk_len = l_checksum_get_digest(hmac, out, out_len); out += chunk_len; out_len -= chunk_len; } l_checksum_free(hmac); return true; } static bool tls_prf_get_bytes(struct l_tls *tls, const void *secret, size_t secret_len, const char *label, const void *seed, size_t seed_len, uint8_t *buf, size_t len) { if (tls->negotiated_version >= L_TLS_V12) return tls12_prf(tls->prf_hmac->l_id, secret, secret_len, label, seed, seed_len, buf, len); return tls10_prf(secret, secret_len, label, seed, seed_len, buf, len); } LIB_EXPORT bool l_tls_prf_get_bytes(struct l_tls *tls, bool use_master_secret, const char *label, uint8_t *buf, size_t len) { uint8_t seed[64]; bool r; if (unlikely(!tls || !tls->prf_hmac)) return false; memcpy(seed + 0, tls->pending.client_random, 32); memcpy(seed + 32, tls->pending.server_random, 32); if (use_master_secret) r = tls_prf_get_bytes(tls, tls->pending.master_secret, 48, label, seed, 64, buf, len); else r = tls_prf_get_bytes(tls, "", 0, label, seed, 64, buf, len); explicit_bzero(seed, 64); return r; } static void tls_write_random(uint8_t *buf) { l_put_be32(time(NULL), buf); l_getrandom(buf + 4, 28); } static void tls_drop_handshake_hash(struct l_tls *tls, enum handshake_hash_type hash) { if (tls->handshake_hash[hash]) { l_checksum_free(tls->handshake_hash[hash]); tls->handshake_hash[hash] = NULL; } } static void tls_reset_handshake(struct l_tls *tls) { enum handshake_hash_type hash; explicit_bzero(tls->pending.key_block, sizeof(tls->pending.key_block)); if (tls->pending.cipher_suite && tls->pending.cipher_suite->key_xchg->free_params) tls->pending.cipher_suite->key_xchg->free_params(tls); l_cert_free(tls->peer_cert); l_key_free(tls->peer_pubkey); tls->peer_cert = NULL; tls->peer_pubkey = NULL; tls->peer_pubkey_size = 0; tls->peer_authenticated = false; tls->negotiated_curve = NULL; tls->negotiated_ff_group = NULL; for (hash = 0; hash < __HANDSHAKE_HASH_COUNT; hash++) tls_drop_handshake_hash(tls, hash); TLS_SET_STATE(TLS_HANDSHAKE_WAIT_START); tls->cert_requested = 0; tls->cert_sent = 0; tls->session_id_size = 0; tls->session_id_size_replaced = 0; tls->session_id_new = false; l_free(l_steal_ptr(tls->session_peer_identity)); tls->session_resumed = false; } static void tls_cleanup_handshake(struct l_tls *tls) { explicit_bzero(tls->pending.client_random, 32); explicit_bzero(tls->pending.server_random, 32); explicit_bzero(tls->pending.master_secret, 48); } static bool tls_change_cipher_spec(struct l_tls *tls, bool txrx, const char **error) { struct tls_bulk_encryption_algorithm *enc; struct tls_mac_algorithm *mac; int key_offset; static char error_buf[200]; if (tls->cipher_type[txrx] == TLS_CIPHER_AEAD) { if (tls->aead_cipher[txrx]) { l_aead_cipher_free(tls->aead_cipher[txrx]); tls->aead_cipher[txrx] = NULL; } } else { if (tls->cipher[txrx]) { l_cipher_free(tls->cipher[txrx]); tls->cipher[txrx] = NULL; } } tls->cipher_type[txrx] = TLS_CIPHER_STREAM; if (tls->mac[txrx]) { l_checksum_free(tls->mac[txrx]); tls->mac[txrx] = NULL; } tls->mac_length[txrx] = 0; tls->block_length[txrx] = 0; tls->record_iv_length[txrx] = 0; if (tls->fixed_iv_length[txrx]) { explicit_bzero(tls->fixed_iv[txrx], tls->fixed_iv_length[txrx]); tls->fixed_iv_length[txrx] = 0; } tls->auth_tag_length[txrx] = 0; tls->seq_num[txrx] = 0; tls->cipher_suite[txrx] = tls->pending.cipher_suite; if (!tls->cipher_suite[txrx]) return true; key_offset = 0; if (tls->cipher_suite[txrx]->mac) { mac = tls->cipher_suite[txrx]->mac; /* Server write / client read is 2nd in the key block */ if ((tls->server && txrx) || (!tls->server && !txrx)) key_offset += mac->mac_length; tls->mac[txrx] = l_checksum_new_hmac(mac->hmac_type, tls->pending.key_block + key_offset, mac->mac_length); /* Wipe out the now unneeded part of the key block */ explicit_bzero(tls->pending.key_block + key_offset, mac->mac_length); if (!tls->mac[txrx]) { if (error) { *error = error_buf; snprintf(error_buf, sizeof(error_buf), "Can't create %s's %s HMAC", tls->cipher_suite[txrx]->name, txrx ? "Tx" : "Rx"); } return false; } tls->mac_length[txrx] = mac->mac_length; key_offset = 2 * mac->mac_length; } if (tls->cipher_suite[txrx]->encryption) { void *cipher; enc = tls->cipher_suite[txrx]->encryption; /* Server write / client read is 4th in the key block */ if ((tls->server && txrx) || (!tls->server && !txrx)) key_offset += enc->key_length; if (enc->cipher_type == TLS_CIPHER_AEAD) { cipher = l_aead_cipher_new(enc->l_aead_id, tls->pending.key_block + key_offset, enc->key_length, enc->auth_tag_length); tls->aead_cipher[txrx] = cipher; } else { cipher = l_cipher_new(enc->l_id, tls->pending.key_block + key_offset, enc->key_length); tls->cipher[txrx] = cipher; } /* Wipe out the now unneeded part of the key block */ explicit_bzero(tls->pending.key_block + key_offset, enc->key_length); if (!cipher) { if (error) { *error = error_buf; snprintf(error_buf, sizeof(error_buf), "Can't create %s's %s cipher", tls->cipher_suite[txrx]->name, txrx ? "Tx" : "Rx"); } return false; } tls->cipher_type[txrx] = enc->cipher_type; tls->record_iv_length[txrx] = enc->iv_length - enc->fixed_iv_length; tls->block_length[txrx] = enc->block_length; tls->auth_tag_length[txrx] = enc->auth_tag_length; if ((tls->server && txrx) || (!tls->server && !txrx)) key_offset += enc->key_length; else key_offset += 2 * enc->key_length; } if (tls->negotiated_version <= L_TLS_V10 && tls->cipher_suite[txrx]->encryption && tls->cipher_suite[txrx]->encryption->cipher_type == TLS_CIPHER_BLOCK) { enc = tls->cipher_suite[txrx]->encryption; /* Server write / client read is 6th in the key block */ if ((tls->server && txrx) || (!tls->server && !txrx)) key_offset += enc->iv_length; l_cipher_set_iv(tls->cipher[txrx], tls->pending.key_block + key_offset, enc->iv_length); /* Wipe out the now unneeded part of the key block */ explicit_bzero(tls->pending.key_block + key_offset, enc->iv_length); } else if (tls->cipher_suite[txrx]->encryption && tls->cipher_suite[txrx]->encryption->fixed_iv_length) { enc = tls->cipher_suite[txrx]->encryption; /* Server write / client read is 6th in the key block */ if ((tls->server && txrx) || (!tls->server && !txrx)) key_offset += enc->fixed_iv_length; tls->fixed_iv_length[txrx] = enc->fixed_iv_length; memcpy(tls->fixed_iv[txrx], tls->pending.key_block + key_offset, enc->fixed_iv_length); /* Wipe out the now unneeded part of the key block */ explicit_bzero(tls->pending.key_block + key_offset, enc->fixed_iv_length); } return true; } static void tls_reset_cipher_spec(struct l_tls *tls, bool txrx) { /* Reset everything to the TLS_NULL_WITH_NULL_NULL state */ tls->pending.cipher_suite = NULL; tls_change_cipher_spec(tls, txrx, NULL); } static bool tls_cipher_suite_is_compatible_no_key_xchg(struct l_tls *tls, const struct tls_cipher_suite *suite, const char **error) { static char error_buf[200]; struct l_cert *leaf; enum l_tls_version min_version = tls->negotiated_version ?: tls->min_version; enum l_tls_version max_version = tls->negotiated_version ?: tls->max_version; if (suite->encryption && suite->encryption->cipher_type == TLS_CIPHER_AEAD) { if (max_version < L_TLS_V12) { if (error) { *error = error_buf; snprintf(error_buf, sizeof(error_buf), "Cipher suite %s uses an AEAD " "cipher (TLS 1.2+) but " TLS_VER_FMT " was negotiated or is the max " "version allowed", suite->name, TLS_VER_ARGS(tls->max_version)); } return false; } if (!l_aead_cipher_is_supported(suite->encryption->l_aead_id)) { if (error) { *error = error_buf; snprintf(error_buf, sizeof(error_buf), "Cipher suite %s's AEAD cipher " "algorithm not supported by " "the kernel", suite->name); } return false; } } else if (suite->encryption) { /* Block or stream cipher */ if (!l_cipher_is_supported(suite->encryption->l_id)) { if (error) { *error = error_buf; snprintf(error_buf, sizeof(error_buf), "Cipher suite %s's block/stream" " cipher algorithm not " "supported by the kernel", suite->name); } return false; } } if (suite->mac && !l_checksum_is_supported(suite->mac->hmac_type, true)) { if (error) { *error = error_buf; snprintf(error_buf, sizeof(error_buf), "Cipher suite %s's HMAC algorithm not " "supported by the kernel", suite->name); } return false; } if ( (max_version < L_TLS_V12 && (!l_checksum_is_supported(L_CHECKSUM_MD5, true) || !l_checksum_is_supported(L_CHECKSUM_SHA1, true))) || (min_version >= L_TLS_V12 && !l_checksum_is_supported( suite->prf_hmac != L_CHECKSUM_NONE ? suite->prf_hmac : L_CHECKSUM_SHA256, true))) { if (error) { *error = error_buf; snprintf(error_buf, sizeof(error_buf), "Cipher suite %s's PRF algorithm not " "supported by the kernel", suite->name); } return false; } /* * If the certificate is compatible with the signature algorithm it * also must be compatible with the key exchange mechanism because * the cipher suites are defined so that the same certificates can * be used by both. */ leaf = l_certchain_get_leaf(tls->cert); if (leaf && suite->signature && !suite->signature->validate_cert_key_type(leaf)) { if (error) { *error = error_buf; snprintf(error_buf, sizeof(error_buf), "Local certificate has key type " "incompatible with cipher suite %s's " "signature algorithm", suite->name); } return false; } return true; } /* * Assumes that Client Hello extensions have been processed and that we * will want to start a new session using this cipher suite, including the * key exchange. This is unlike tls_cipher_suite_is_compatible_no_key_xchg() * which runs fewer checks and must succeed even for a cipher suite loaded * during session resumption. */ bool tls_cipher_suite_is_compatible(struct l_tls *tls, const struct tls_cipher_suite *suite, const char **error) { static char error_buf[200]; if (!tls_cipher_suite_is_compatible_no_key_xchg(tls, suite, error)) return false; if (suite->key_xchg->need_ffdh && !l_key_is_supported(L_KEY_FEATURE_DH)) { if (error) { *error = error_buf; snprintf(error_buf, sizeof(error_buf), "Cipher suite %s's key exchange " "mechanism needs kernel DH support", suite->name); } return false; } /* * On the server we know what elliptic curve we'll be using as soon * as we've processed the ClientHello so for EC-based key exchange * methods require that a curve has been selected. */ if (suite->key_xchg->need_ecc && tls->server && !tls->negotiated_curve) { if (error) { *error = error_buf; snprintf(error_buf, sizeof(error_buf), "No common supported elliptic curves " "with the client, can't use %s", suite->name); } return false; } /* Similarly for FF DH groups */ if (suite->key_xchg->need_ffdh && tls->server && !tls->negotiated_ff_group) { if (error) { *error = error_buf; snprintf(error_buf, sizeof(error_buf), "No common supported finite-field " "groups with the client, can't use %s", suite->name); } return false; } return true; } static struct tls_cipher_suite *tls_find_cipher_suite(const uint8_t *id) { struct tls_cipher_suite **suite; for (suite = tls_cipher_suite_pref; *suite; suite++) if ((*suite)->id[0] == id[0] && (*suite)->id[1] == id[1]) return *suite; return NULL; } static struct tls_compression_method tls_compression_pref[] = { { 0, "CompressionMethod.null", }, }; static struct tls_compression_method *tls_find_compression_method( const uint8_t id) { int i; for (i = 0; i < (int) L_ARRAY_SIZE(tls_compression_pref); i++) if (tls_compression_pref[i].id == id) return &tls_compression_pref[i]; return NULL; } const struct tls_hash_algorithm tls_handshake_hash_data[] = { [HANDSHAKE_HASH_SHA384] = { 5, HANDSHAKE_HASH_SHA384, L_CHECKSUM_SHA384, "SHA384" }, [HANDSHAKE_HASH_SHA256] = { 4, HANDSHAKE_HASH_SHA256, L_CHECKSUM_SHA256, "SHA256" }, [HANDSHAKE_HASH_MD5] = { 1, HANDSHAKE_HASH_MD5, L_CHECKSUM_MD5, "MD5" }, [HANDSHAKE_HASH_SHA1] = { 2, HANDSHAKE_HASH_SHA1, L_CHECKSUM_SHA1, "SHA1" }, }; static bool tls_init_handshake_hash(struct l_tls *tls) { enum handshake_hash_type hash; bool tls10 = tls->max_version < L_TLS_V12; for (hash = 0; hash < __HANDSHAKE_HASH_COUNT; hash++) { /* Skip hash types we already know we won't need */ if (tls10 && hash != HANDSHAKE_HASH_SHA1 && hash != HANDSHAKE_HASH_MD5) continue; if (tls->handshake_hash[hash]) { TLS_DEBUG("Handshake hash %s already exists", tls_handshake_hash_data[hash].name); goto err; } tls->handshake_hash[hash] = l_checksum_new( tls_handshake_hash_data[hash].l_id); if (!tls->handshake_hash[hash]) { TLS_DEBUG("Can't create %s hash", tls_handshake_hash_data[hash].name); goto err; } } return true; err: for (hash = 0; hash < __HANDSHAKE_HASH_COUNT; hash++) tls_drop_handshake_hash(tls, hash); return false; } static const struct tls_hash_algorithm *tls_set_prf_hmac(struct l_tls *tls) { enum handshake_hash_type hash; if (tls->pending.cipher_suite->prf_hmac == L_CHECKSUM_NONE) { tls->prf_hmac = &tls_handshake_hash_data[HANDSHAKE_HASH_SHA256]; return tls->prf_hmac; } for (hash = 0; hash < __HANDSHAKE_HASH_COUNT; hash++) if (tls_handshake_hash_data[hash].l_id == tls->pending.cipher_suite->prf_hmac) { tls->prf_hmac = &tls_handshake_hash_data[hash]; return tls->prf_hmac; } return NULL; } static bool tls_domain_match_mask(const char *name, size_t name_len, const char *mask, size_t mask_len) { bool at_start = true; while (1) { const char *name_seg_end = memchr(name, '.', name_len); const char *mask_seg_end = memchr(mask, '.', mask_len); size_t name_seg_len = name_seg_end ? (size_t) (name_seg_end - name) : name_len; size_t mask_seg_len = mask_seg_end ? (size_t) (mask_seg_end - mask) : mask_len; if (mask_seg_len == 1 && mask[0] == '*') { /* * A * at the beginning of the mask matches any * number of labels. */ if (at_start && name_seg_end && tls_domain_match_mask(name_seg_end + 1, name_len - name_seg_len - 1, mask, mask_len)) return true; goto ok_next; } if (name_seg_len != mask_seg_len || memcmp(name, mask, name_seg_len)) return false; ok_next: /* If either string ends here both must end here */ if (!name_seg_end || !mask_seg_end) return !name_seg_end && !mask_seg_end; at_start = false; name = name_seg_end + 1; name_len -= name_seg_len + 1; mask = mask_seg_end + 1; mask_len -= mask_seg_len + 1; } } static const struct asn1_oid subject_alt_name_oid = { 3, { 0x55, 0x1d, 0x11 } }; static const struct asn1_oid dn_common_name_oid = { 3, { 0x55, 0x04, 0x03 } }; #define SAN_DNS_NAME_ID ASN1_CONTEXT_IMPLICIT(2) static bool tls_cert_domains_match_mask(struct l_cert *cert, char **mask, char **error_msg) { const uint8_t *san, *dn, *end; size_t san_len, dn_len; uint8_t san_tag; const char *cn = NULL; size_t cn_len; char **i; struct l_string *dns_names = NULL; int dns_name_count = 0; /* * Locate SubjectAltName (RFC5280 Section 4.2.1.6) and descend into * the sole SEQUENCE element, check if any DNSName matches. */ san = cert_get_extension(cert, &subject_alt_name_oid, NULL, &san_len); if (san) { san = asn1_der_find_elem(san, san_len, 0, &san_tag, &san_len); if (unlikely(!san || san_tag != ASN1_ID_SEQUENCE)) goto parse_error; end = san + san_len; while (san < end) { const uint8_t *value; uint8_t tag; size_t len; value = asn1_der_find_elem(san, end - san, SAN_DNS_NAME_ID, &tag, &len); if (!value) break; /* Type is implicitly IA5STRING */ for (i = mask; *i; i++) if (tls_domain_match_mask((const char *) value, len, *i, strlen(*i))) { l_string_free(dns_names); return true; } if (!dns_names) { dns_names = l_string_new(128); l_string_append(dns_names, "tried DNSName(s) "); l_string_append_fixed(dns_names, (char *) value, len); } else if (dns_name_count < 20) { l_string_append(dns_names, ", "); l_string_append_fixed(dns_names, (char *) value, len); } san = value + len; dns_name_count++; } } /* * Retrieve the Common Name from the Subject DN and check if it * matches. * * We look at the Common Name only if no DNSNames were present in * the certificate, following Wi-Fi Alliance's Hotspot 2.0 * Specification v3.1 section 7.3.3.2 step 2: * "Verify in the AAA server certificate that the domain name from * the FQDN [...] is a suffix match of the domain name in at least * one of the DNSName SubjectAltName extensions. If a SubjectAltName * of type DNSName is not present, then the domain name from the * FQDN shall be a suffix match to the CommonName portion of the * SubjectName. If neither of these conditions holds, then * verification fails." */ if (dns_name_count) { if (dns_name_count > 20) l_string_append_printf(dns_names, " and %i other", dns_name_count - 20); *error_msg = l_string_unwrap(dns_names); return false; } dn = l_cert_get_dn(cert, &dn_len); if (unlikely(!dn)) goto parse_error; end = dn + dn_len; while (dn < end) { const uint8_t *set, *seq, *oid, *name; uint8_t tag; size_t len, oid_len, name_len; set = asn1_der_find_elem(dn, end - dn, 0, &tag, &len); if (unlikely(!set || tag != ASN1_ID_SET)) goto parse_error; dn = set + len; seq = asn1_der_find_elem(set, len, 0, &tag, &len); if (unlikely(!seq || tag != ASN1_ID_SEQUENCE)) goto parse_error; oid = asn1_der_find_elem(seq, len, 0, &tag, &oid_len); if (unlikely(!oid || tag != ASN1_ID_OID)) goto parse_error; name = asn1_der_find_elem(seq, len, 1, &tag, &name_len); if (unlikely(!name || (tag != ASN1_ID_PRINTABLESTRING && tag != ASN1_ID_UTF8STRING && tag != ASN1_ID_IA5STRING))) continue; if (asn1_oid_eq(&dn_common_name_oid, oid_len, oid)) { cn = (const char *) name; cn_len = name_len; break; } } if (unlikely(!cn)) goto parse_error; for (i = mask; *i; i++) if (tls_domain_match_mask(cn, cn_len, *i, strlen(*i))) return true; *error_msg = l_strdup_printf("tried CommonName %.*s", (int) cn_len, cn); return false; parse_error: *error_msg = l_strdup("couldn't locate DNSName or CommonName"); return false; } static const char *tls_get_cache_group_name(struct l_tls *tls, const uint8_t *session_id, size_t session_id_size) { _auto_(l_free) char *session_id_str = NULL; static char group_name[256]; if (!tls->server) return tls->session_prefix; session_id_str = l_util_hexstring(session_id, session_id_size); snprintf(group_name, sizeof(group_name), "%s-%s", tls->session_prefix, session_id_str); return group_name; } static void tls_forget_cached_session(struct l_tls *tls, const char *group_name, const uint8_t *session_id, size_t session_id_size, bool call_back) { if (!group_name) group_name = tls_get_cache_group_name(tls, session_id, session_id_size); l_settings_remove_group(tls->session_settings, group_name); if (call_back && tls->session_update_cb) { tls->in_callback = true; tls->session_update_cb(tls->session_update_user_data); tls->in_callback = false; } } static bool tls_load_cached_session(struct l_tls *tls, const char *group_name, const uint8_t *session_id, size_t session_id_size, const char *session_id_str) { _auto_(l_free) uint8_t *master_secret = NULL; int version; _auto_(l_free) uint8_t *cipher_suite_id = NULL; struct tls_cipher_suite *cipher_suite; unsigned int compression_method_id; _auto_(l_free) char *peer_identity = NULL; size_t size; const char *error; if (l_settings_has_key(tls->session_settings, group_name, "SessionExpiryTime")) { uint64_t expiry_time; if (unlikely(!l_settings_get_uint64(tls->session_settings, group_name, "SessionExpiryTime", &expiry_time))) goto warn_corrupt; if (time_realtime_now() > expiry_time) { TLS_DEBUG("Cached session %s is expired, removing it, " "will start a new session", session_id_str); goto forget; } } if (unlikely(!l_settings_get_int(tls->session_settings, group_name, "SessionVersion", &version) || version < TLS_MIN_VERSION || version > TLS_MAX_VERSION)) goto warn_corrupt; master_secret = l_settings_get_bytes(tls->session_settings, group_name, "SessionMasterSecret", &size); if (unlikely(!master_secret || size != 48)) goto warn_corrupt; cipher_suite_id = l_settings_get_bytes(tls->session_settings, group_name, "SessionCipherSuite", &size); if (unlikely(!cipher_suite_id || size != 2 || !(cipher_suite = tls_find_cipher_suite(cipher_suite_id)))) goto warn_corrupt; /* * While we could attempt to resume a session even though we're now * configured with, say, a different certificate type than what we * had when we cached that session, that is too questionable of a * scenario to support it. We don't specifically check that all of * the authentication data is the same, e.g. we don't save the * certificate serial number or path, but ensure the cached cipher * suite is compatible with current authentication data. * * We filter the cipher suites in our Client Hello to only offer the * ones compatible with current configuration so if we also include * a Session ID from a session who's cipher suite is not one of those * listed in that same Client Hello, the server is likely to notice * and either start a new session or send a fatal Alert. * * It is up to the user to keep multiple cache instances if it needs * to save multiple sessions. */ if (unlikely(!tls_cipher_suite_is_compatible_no_key_xchg(tls, cipher_suite, &error))) { TLS_DEBUG("Cached session %s cipher suite not compatible: %s", session_id_str, error); goto forget; } if (unlikely(!l_settings_get_uint(tls->session_settings, group_name, "SessionCompressionMethod", &compression_method_id) || !tls_find_compression_method(compression_method_id))) goto warn_corrupt; if (l_settings_has_key(tls->session_settings, group_name, "SessionPeerIdentity")) { peer_identity = l_settings_get_string(tls->session_settings, group_name, "SessionPeerIdentity"); if (unlikely(!peer_identity || !cipher_suite->signature)) goto warn_corrupt; } tls->session_id_size = session_id_size; memcpy(tls->session_id, session_id, session_id_size); tls->session_id_new = false; tls->client_version = version; memcpy(tls->pending.master_secret, master_secret, 48); memcpy(tls->session_cipher_suite_id, cipher_suite_id, 2); tls->session_compression_method_id = compression_method_id; l_free(tls->session_peer_identity); tls->session_peer_identity = l_steal_ptr(peer_identity); return true; warn_corrupt: TLS_DEBUG("Cached session %s data is corrupt or has unsupported " "parameters, removing it, will start a new session", session_id_str); forget: tls_forget_cached_session(tls, group_name, session_id, session_id_size, true); return false; } static bool tls_load_cached_client_session(struct l_tls *tls) { /* * The following settings are required: * SessionID, * SessionMasterSecret, * SessionVersion, * SessionCipherSuite, * SessionCompressionMethod, * and these two are optional: * SessionExpiryTime, * SessionPeerIdentity. */ _auto_(l_free) uint8_t *session_id = NULL; size_t session_id_size; _auto_(l_free) char *session_id_str = NULL; const char *group_name = tls_get_cache_group_name(tls, NULL, 0); tls->session_id_size = 0; tls->session_id_new = false; if (!tls->session_settings || !l_settings_has_key(tls->session_settings, group_name, "SessionID")) /* No session cached, no error */ return false; session_id = l_settings_get_bytes(tls->session_settings, group_name, "SessionID", &session_id_size); if (unlikely(!session_id || session_id_size < 1 || session_id_size > 32)) { TLS_DEBUG("Bad cached session ID format"); tls_forget_cached_session(tls, group_name, NULL, 0, true); return false; } session_id_str = l_util_hexstring(session_id, session_id_size); return tls_load_cached_session(tls, group_name, session_id, session_id_size, session_id_str); } static bool tls_load_cached_server_session(struct l_tls *tls, const uint8_t *session_id, size_t session_id_size) { _auto_(l_free) char *session_id_str = l_util_hexstring(session_id, session_id_size); const char *target_group_name = tls_get_cache_group_name(tls, session_id, session_id_size); _auto_(l_strv_free) char **groups = l_settings_get_groups(tls->session_settings); char **group; unsigned int cnt = 0; size_t prefix_len = strlen(tls->session_prefix); uint64_t now = time_realtime_now(); char *oldest_session_group = NULL; uint64_t oldest_session_expiry = UINT64_MAX; bool found = false; bool changed = false; bool loaded = false; tls->session_id_size = 0; tls->session_id_new = false; /* Clean up expired entries and enforce session count limit */ for (group = groups; *group; group++) { uint64_t expiry_time; if (memcmp(*group, tls->session_prefix, prefix_len) || (*group)[prefix_len] != '-') continue; /* Group seems to be a session cache entry */ if (unlikely(!l_settings_get_uint64(tls->session_settings, *group, "SessionExpiryTime", &expiry_time)) || expiry_time <= now) { TLS_DEBUG("Cached session %s is expired or invalid, " "purging entry", *group + prefix_len + 1); l_settings_remove_group(tls->session_settings, *group); changed = true; continue; } cnt++; if (!strcmp(*group + prefix_len + 1, target_group_name + prefix_len + 1)) { found = true; continue; /* Don't purge this entry */ } if (expiry_time < oldest_session_expiry) { oldest_session_group = *group; oldest_session_expiry = expiry_time; } } /* * Enforce tls->session_count_max by dropping the entry for the * oldest session (more specifically the one closest to its expiry * time) in the cache, that is not the session we're trying to * load. If the target session was not found, do this as soon as * tls->session_count_max is reached rather than when it's exceeded * so as to make room for a new entry. If we end up not saving * a new session due to an error before the handshake finish, we'll * be left with tls->session_count_max - 1 entries and will have * purged that entry unnecessarily but the cache will have room for * a future session. Same when we did find the target session but * later had to forget it because of a fatal alert during or after * the handshake. * * If we did find the target session but we later find that we * can't resume it, we will forget it so the new session can take * the old session's place and the limit is not exceeded, see * discussion in tls_server_resume_error. */ if (tls->session_count_max && oldest_session_group && cnt >= tls->session_count_max + (found ? 1 : 0)) { l_settings_remove_group(tls->session_settings, oldest_session_group); changed = true; } if (!found) { TLS_DEBUG("Requested session %s not found in cache, will " "start a new session", session_id_str); goto call_back; } loaded = tls_load_cached_session(tls, target_group_name, session_id, session_id_size, session_id_str); /* * If tls_load_cached_session() returned false it will have called * session_update_cb for us. */ if (!loaded) changed = false; call_back: if (changed && tls->session_update_cb) { tls->in_callback = true; tls->session_update_cb(tls->session_update_user_data); tls->in_callback = false; } return loaded; } #define SWITCH_ENUM_TO_STR(val) \ case (val): \ return L_STRINGIFY(val); static const char *tls_handshake_type_to_str(enum tls_handshake_type type) { static char buf[100]; switch (type) { SWITCH_ENUM_TO_STR(TLS_HELLO_REQUEST) SWITCH_ENUM_TO_STR(TLS_CLIENT_HELLO) SWITCH_ENUM_TO_STR(TLS_SERVER_HELLO) SWITCH_ENUM_TO_STR(TLS_CERTIFICATE) SWITCH_ENUM_TO_STR(TLS_SERVER_KEY_EXCHANGE) SWITCH_ENUM_TO_STR(TLS_CERTIFICATE_REQUEST) SWITCH_ENUM_TO_STR(TLS_SERVER_HELLO_DONE) SWITCH_ENUM_TO_STR(TLS_CERTIFICATE_VERIFY) SWITCH_ENUM_TO_STR(TLS_CLIENT_KEY_EXCHANGE) SWITCH_ENUM_TO_STR(TLS_FINISHED) } snprintf(buf, sizeof(buf), "tls_handshake_type(%i)", type); return buf; } static void tls_send_alert(struct l_tls *tls, bool fatal, enum l_tls_alert_desc alert_desc) { uint8_t buf[2]; TLS_DEBUG("Sending a %s Alert: %s", fatal ? "Fatal" : "Warning", l_tls_alert_to_str(alert_desc)); buf[0] = fatal ? 2 : 1; buf[1] = alert_desc; tls_tx_record(tls, TLS_CT_ALERT, buf, 2); } /* * Callers make sure this is about the last function before returning * from the stack frames up to the exported library call so that the * user-supplied disconnected callback here is free to use l_tls_free * for example. */ void tls_disconnect(struct l_tls *tls, enum l_tls_alert_desc desc, enum l_tls_alert_desc local_desc) { bool forget_session = false; /* Save session_id_size before tls_reset_handshake() */ size_t session_id_size = tls->session_id_size; if ((desc || local_desc) && tls->session_settings && session_id_size && !tls->session_id_new) /* * RFC5246 Section 7.2: "Alert messages with a level of fatal * result in the immediate termination of the connection. In * this case, other connections corresponding to the session * may continue, but the session identifier MUST be * invalidated, preventing the failed session from being used * to establish new connections." * * and 7.2.1: "Note that as of TLS 1.1, failure to properly * close a connection no longer requires that a session not * be resumed." * * I.e. we need to remove the session from the cache here but * not on l_tls_close(). */ forget_session = true; tls_send_alert(tls, true, desc); tls_reset_handshake(tls); tls_cleanup_handshake(tls); tls_reset_cipher_spec(tls, 0); tls_reset_cipher_spec(tls, 1); tls->negotiated_version = 0; tls->ready = false; tls->renegotiation_info.secure_renegotiation = false; if (forget_session) { tls_forget_cached_session(tls, NULL, tls->session_id, session_id_size, true); if (tls->pending_destroy) return; } tls->disconnected(local_desc ?: desc, local_desc && !desc, tls->user_data); } void tls_tx_handshake(struct l_tls *tls, int type, uint8_t *buf, size_t length) { int i; TLS_DEBUG("Sending a %s of %zi bytes", tls_handshake_type_to_str(type), length - TLS_HANDSHAKE_HEADER_SIZE); /* Fill in the handshake header */ buf[0] = type; buf[1] = (length - TLS_HANDSHAKE_HEADER_SIZE) >> 16; buf[2] = (length - TLS_HANDSHAKE_HEADER_SIZE) >> 8; buf[3] = (length - TLS_HANDSHAKE_HEADER_SIZE) >> 0; for (i = 0; i < __HANDSHAKE_HASH_COUNT; i++) if (tls->handshake_hash[i]) l_checksum_update(tls->handshake_hash[i], buf, length); tls_tx_record(tls, TLS_CT_HANDSHAKE, buf, length); } static ssize_t tls_append_hello_extensions(struct l_tls *tls, struct l_queue *extensions, uint8_t *buf, size_t len) { uint8_t *ptr = buf; uint8_t *extensions_len_ptr = ptr; bool client_hello = !tls->server; unsigned int i = 0; const struct l_queue_entry *entry = l_queue_get_entries(extensions); if (len < 2) return -ENOSPC; ptr += 2; len -= 2; while (1) { const struct tls_hello_extension *extension; ssize_t ext_len; ssize_t (*ext_write)(struct l_tls *tls, uint8_t *buf, size_t len); if (client_hello) { extension = &tls_extensions[i++]; if (!extension->name) break; ext_write = extension->client_write; } else { uint16_t ext_id; if (!entry) break; ext_id = L_PTR_TO_UINT(entry->data); entry = entry->next; for (i = 0; tls_extensions[i].name; i++) if (tls_extensions[i].id == ext_id) break; extension = &tls_extensions[i]; if (!extension->name) continue; ext_write = extension->server_write; } /* * Note: could handle NULL client_write with non-NULL * server_handle or server_handle_absent as "server-oriented" * extension (7.4.1.4) and write empty extension_data and * similarly require empty extension_data in * tls_handle_client_hello if client_handle NULL. */ if (!ext_write) continue; if (len < 4) return -ENOSPC; ext_len = ext_write(tls, ptr + 4, len - 4); if (ext_len == -ENOMSG) continue; if (ext_len < 0) { TLS_DEBUG("%s extension's %s_write: %s", extension->name, client_hello ? "client" : "server", strerror(-ext_len)); return ext_len; } l_put_be16(extension->id, ptr + 0); l_put_be16(ext_len, ptr + 2); ptr += 4 + ext_len; len -= 4 + ext_len; } if (ptr > extensions_len_ptr + 2) l_put_be16(ptr - (extensions_len_ptr + 2), extensions_len_ptr); else /* Skip the length if no extensions */ ptr = extensions_len_ptr; return ptr - buf; } static bool tls_send_client_hello(struct l_tls *tls) { uint8_t buf[1024 + L_ARRAY_SIZE(tls_compression_pref)]; uint8_t *ptr = buf + TLS_HANDSHAKE_HEADER_SIZE; uint8_t *len_ptr; unsigned int i; ssize_t r; struct tls_cipher_suite **suite; /* Fill in the Client Hello body */ *ptr++ = (uint8_t) (tls->client_version >> 8); *ptr++ = (uint8_t) (tls->client_version >> 0); tls_write_random(tls->pending.client_random); memcpy(ptr, tls->pending.client_random, 32); ptr += 32; if (tls->session_id_size) { *ptr++ = tls->session_id_size; memcpy(ptr, tls->session_id, tls->session_id_size); ptr += tls->session_id_size; } else *ptr++ = 0; len_ptr = ptr; ptr += 2; for (suite = tls->cipher_suite_pref_list; *suite; suite++) { const char *error; if (!tls_cipher_suite_is_compatible(tls, *suite, &error)) { TLS_DEBUG("non-fatal: %s", error); continue; } *ptr++ = (*suite)->id[0]; *ptr++ = (*suite)->id[1]; } if (ptr == len_ptr + 2) { TLS_DEBUG("No compatible cipher suites, check kernel config, " "certificate's key type and TLS version range"); return false; } l_put_be16((ptr - len_ptr - 2), len_ptr); *ptr++ = L_ARRAY_SIZE(tls_compression_pref); for (i = 0; i < L_ARRAY_SIZE(tls_compression_pref); i++) *ptr++ = tls_compression_pref[i].id; r = tls_append_hello_extensions(tls, NULL, ptr, buf + sizeof(buf) - ptr); if (r < 0) return false; ptr += r; tls_tx_handshake(tls, TLS_CLIENT_HELLO, buf, ptr - buf); return true; } static bool tls_send_server_hello(struct l_tls *tls, struct l_queue *extensions) { uint8_t buf[1024]; uint8_t *ptr = buf + TLS_HANDSHAKE_HEADER_SIZE; ssize_t r; /* Fill in the Server Hello body */ *ptr++ = tls->negotiated_version >> 8; *ptr++ = tls->negotiated_version >> 0; tls_write_random(tls->pending.server_random); memcpy(ptr, tls->pending.server_random, 32); ptr += 32; if (tls->session_id_size) { *ptr++ = tls->session_id_size; memcpy(ptr, tls->session_id, tls->session_id_size); ptr += tls->session_id_size; } else *ptr++ = 0; *ptr++ = tls->pending.cipher_suite->id[0]; *ptr++ = tls->pending.cipher_suite->id[1]; *ptr++ = tls->pending.compression_method->id; r = tls_append_hello_extensions(tls, extensions, ptr, buf + sizeof(buf) - ptr); if (r < 0) { TLS_DISCONNECT(TLS_ALERT_INTERNAL_ERROR, 0, "Error appending extensions: %s", strerror(-r)); return false; } ptr += r; tls_tx_handshake(tls, TLS_SERVER_HELLO, buf, ptr - buf); return true; } static bool tls_cert_list_add_size(struct l_cert *cert, void *user_data) { size_t *total = user_data; size_t der_len; l_cert_get_der_data(cert, &der_len); *total += 3 + der_len; return false; } static bool tls_cert_list_append(struct l_cert *cert, void *user_data) { uint8_t **ptr = user_data; const uint8_t *der; size_t der_len; der = l_cert_get_der_data(cert, &der_len); *(*ptr)++ = der_len >> 16; *(*ptr)++ = der_len >> 8; *(*ptr)++ = der_len >> 0; memcpy(*ptr, der, der_len); *ptr += der_len; return false; } static bool tls_send_certificate(struct l_tls *tls) { uint8_t *buf, *ptr; size_t total; if (tls->server && !tls->cert) { TLS_DISCONNECT(TLS_ALERT_INTERNAL_ERROR, TLS_ALERT_BAD_CERT, "Certificate needed in server mode"); return false; } /* * TODO: check that the certificate is compatible with hash and * signature algorithms lists supplied to us in the Client Hello * extensions (if we're a 1.2+ server) or in the Certificate Request * (if we act as a 1.2+ client). * * - for the hash and signature_algorithms list, check all * certs in the cert chain. * * - also if !cipher_suite->key_xchg->key_exchange_msg, check that the * end entity certificate's key type matches and is usable with some * hash/signature pair. * * - on client check if any of the supplied DNs (if any) match * anything in our cert chain. */ total = 0; l_certchain_walk_from_leaf(tls->cert, tls_cert_list_add_size, &total); buf = l_malloc(128 + total); ptr = buf + TLS_HANDSHAKE_HEADER_SIZE; /* Fill in the Certificate body */ *ptr++ = total >> 16; *ptr++ = total >> 8; *ptr++ = total >> 0; l_certchain_walk_from_leaf(tls->cert, tls_cert_list_append, &ptr); tls_tx_handshake(tls, TLS_CERTIFICATE, buf, ptr - buf); l_free(buf); if (tls->cert) tls->cert_sent = true; return true; } /* * Note: ClientCertificateType.rsa_sign value coincides with the * SignatureAlgorithm.rsa value but other values in those enum are * different so we don't mix them, can't extract them from * tls->pending.cipher_suite->signature. */ static uint8_t tls_cert_type_pref[] = { 1, /* RSA_sign */ }; static bool tls_send_certificate_request(struct l_tls *tls) { uint8_t *buf, *ptr, *dn_ptr; size_t len; const struct l_queue_entry *entry; unsigned int i; size_t dn_total = 0; for (entry = l_queue_get_entries(tls->ca_certs); entry; entry = entry->next) { struct l_cert *ca_cert = entry->data; size_t dn_size; if (l_cert_get_dn(ca_cert, &dn_size)) dn_total += 10 + dn_size; } len = 256 + L_ARRAY_SIZE(tls_cert_type_pref) + dn_total; buf = l_malloc(len); ptr = buf + TLS_HANDSHAKE_HEADER_SIZE; /* Fill in the Certificate Request body */ *ptr++ = L_ARRAY_SIZE(tls_cert_type_pref); for (i = 0; i < L_ARRAY_SIZE(tls_cert_type_pref); i++) *ptr++ = tls_cert_type_pref[i]; if (tls->negotiated_version >= L_TLS_V12) { ssize_t ret = tls_write_signature_algorithms(tls, ptr, buf + len - ptr); if (ret < 0) { TLS_DISCONNECT(TLS_ALERT_INTERNAL_ERROR, 0, "tls_write_signature_algorithms: %s", strerror(-ret)); l_free(buf); return false; } ptr += ret; } dn_ptr = ptr; ptr += 2; /* Leave space for the total DN size */ for (entry = l_queue_get_entries(tls->ca_certs); entry; entry = entry->next) { struct l_cert *ca_cert = entry->data; size_t dn_size; const uint8_t *dn = l_cert_get_dn(ca_cert, &dn_size); uint8_t *cur_dn_ptr = ptr; if (!dn) continue; ptr += 2; /* Leave space for current DN size */ *ptr++ = ASN1_ID_SEQUENCE; /* DER outer SEQUENCE tag */ asn1_write_definite_length(&ptr, dn_size); /* length */ memcpy(ptr, dn, dn_size); /* value */ ptr += dn_size; l_put_be16(ptr - cur_dn_ptr - 2, cur_dn_ptr); } l_put_be16(ptr - dn_ptr - 2, dn_ptr); /* DistinguishedNames size */ tls_tx_handshake(tls, TLS_CERTIFICATE_REQUEST, buf, ptr - buf); l_free(buf); return true; } static void tls_send_server_hello_done(struct l_tls *tls) { uint8_t buf[32]; /* No body */ tls_tx_handshake(tls, TLS_SERVER_HELLO_DONE, buf, TLS_HANDSHAKE_HEADER_SIZE); } static void tls_update_key_block(struct l_tls *tls) { uint8_t seed[64]; int key_block_size = 0; if (tls->pending.cipher_suite->encryption) key_block_size += 2 * tls->pending.cipher_suite->encryption->key_length; if (tls->pending.cipher_suite->mac) key_block_size += 2 * tls->pending.cipher_suite->mac->mac_length; if (tls->pending.cipher_suite->encryption && tls->negotiated_version <= L_TLS_V10 && tls->pending.cipher_suite->encryption->cipher_type == TLS_CIPHER_BLOCK) key_block_size += 2 * tls->pending.cipher_suite->encryption->iv_length; if (tls->pending.cipher_suite->encryption) key_block_size += 2 * tls->pending.cipher_suite->encryption-> fixed_iv_length; /* Reverse order from the master secret seed */ memcpy(seed + 0, tls->pending.server_random, 32); memcpy(seed + 32, tls->pending.client_random, 32); tls_prf_get_bytes(tls, tls->pending.master_secret, 48, "key expansion", seed, 64, tls->pending.key_block, key_block_size); explicit_bzero(seed, 64); } void tls_generate_master_secret(struct l_tls *tls, const uint8_t *pre_master_secret, int pre_master_secret_len) { uint8_t seed[64]; memcpy(seed + 0, tls->pending.client_random, 32); memcpy(seed + 32, tls->pending.server_random, 32); tls_prf_get_bytes(tls, pre_master_secret, pre_master_secret_len, "master secret", seed, 64, tls->pending.master_secret, 48); explicit_bzero(seed, 64); /* Directly generate the key block while we're at it */ tls_update_key_block(tls); } static void tls_get_handshake_hash(struct l_tls *tls, enum handshake_hash_type type, uint8_t *out) { struct l_checksum *hash = l_checksum_clone(tls->handshake_hash[type]); if (!hash) return; l_checksum_get_digest(hash, out, l_checksum_digest_length( tls_handshake_hash_data[type].l_id)); l_checksum_free(hash); } static bool tls_get_handshake_hash_by_type(struct l_tls *tls, enum handshake_hash_type type, const uint8_t *data, size_t data_len, uint8_t *out, size_t *out_len) { if (!tls->handshake_hash[type]) return false; if (out_len) *out_len = l_checksum_digest_length( tls_handshake_hash_data[type].l_id); tls_get_handshake_hash(tls, type, out); return true; } static bool tls_send_certificate_verify(struct l_tls *tls) { uint8_t buf[2048]; int i; ssize_t sign_len; /* Fill in the Certificate Verify body */ sign_len = tls->pending.cipher_suite->signature->sign(tls, buf + TLS_HANDSHAKE_HEADER_SIZE, 2048 - TLS_HANDSHAKE_HEADER_SIZE, tls_get_handshake_hash_by_type, NULL, 0); if (sign_len < 0) return false; /* Stop maintaining handshake message hashes other than the PRF hash */ if (tls->negotiated_version >= L_TLS_V12) for (i = 0; i < __HANDSHAKE_HASH_COUNT; i++) if (&tls_handshake_hash_data[i] != tls->prf_hmac) tls_drop_handshake_hash(tls, i); tls_tx_handshake(tls, TLS_CERTIFICATE_VERIFY, buf, sign_len + TLS_HANDSHAKE_HEADER_SIZE); return true; } static void tls_send_change_cipher_spec(struct l_tls *tls) { uint8_t buf = 1; tls_tx_record(tls, TLS_CT_CHANGE_CIPHER_SPEC, &buf, 1); } size_t tls_verify_data_length(struct l_tls *tls, unsigned int index) { /* * RFC 5246, Section 7.4.9: * * In previous versions of TLS, the verify_data was always 12 octets * long. In the current version of TLS, it depends on the cipher * suite. Any cipher suite which does not explicitly specify * verify_data_length has a verify_data_length equal to 12. */ return maxsize(tls->cipher_suite[index]->verify_data_length, 12); } static bool tls_save_verify_data(struct l_tls *tls, bool txrx, const uint8_t *vd, size_t vdl) { uint8_t *buf; if (tls->server == txrx) { if (vdl > sizeof(tls->renegotiation_info.server_verify_data)) goto error; buf = tls->renegotiation_info.server_verify_data; } else { if (vdl > sizeof(tls->renegotiation_info.client_verify_data)) goto error; buf = tls->renegotiation_info.client_verify_data; } memcpy(buf, vd, vdl); return true; error: TLS_DISCONNECT(TLS_ALERT_INTERNAL_ERROR, 0, "tls->renegotiation_info.*verify too small for %s, " "report an ell bug", tls->cipher_suite[txrx]->name); return false; } static bool tls_send_finished(struct l_tls *tls) { uint8_t buf[512]; uint8_t *ptr = buf + TLS_HANDSHAKE_HEADER_SIZE; uint8_t seed[HANDSHAKE_HASH_MAX_SIZE * 2]; size_t vdl = tls_verify_data_length(tls, 1); size_t seed_len; if (tls->negotiated_version >= L_TLS_V12) { /* Same hash type as that used for the PRF (usually SHA256) */ tls_get_handshake_hash(tls, tls->prf_hmac->type, seed); seed_len = l_checksum_digest_length(tls->prf_hmac->l_id); } else { tls_get_handshake_hash(tls, HANDSHAKE_HASH_MD5, seed + 0); tls_get_handshake_hash(tls, HANDSHAKE_HASH_SHA1, seed + 16); seed_len = 36; } tls_prf_get_bytes(tls, tls->pending.master_secret, 48, tls->server ? "server finished" : "client finished", seed, seed_len, ptr, vdl); if (!tls_save_verify_data(tls, 1, ptr, vdl)) return false; ptr += vdl; tls_tx_handshake(tls, TLS_FINISHED, buf, ptr - buf); return true; } static bool tls_verify_finished(struct l_tls *tls, const uint8_t *received, size_t len) { size_t vdl = tls_verify_data_length(tls, 0); uint8_t expected[vdl]; uint8_t *seed; size_t seed_len; if (len != vdl) { TLS_DISCONNECT(TLS_ALERT_DECODE_ERROR, 0, "TLS_FINISHED length not %zu", vdl); return false; } if (tls->negotiated_version >= L_TLS_V12) { enum handshake_hash_type hash = tls->prf_hmac->type; seed = tls->prev_digest[hash]; seed_len = l_checksum_digest_length(tls->prf_hmac->l_id); } else { seed = alloca(36); memcpy(seed + 0, tls->prev_digest[HANDSHAKE_HASH_MD5], 16); memcpy(seed + 16, tls->prev_digest[HANDSHAKE_HASH_SHA1], 20); seed_len = 36; } tls_prf_get_bytes(tls, tls->pending.master_secret, 48, tls->server ? "client finished" : "server finished", seed, seed_len, expected, vdl); if (memcmp(received, expected, len)) { TLS_DISCONNECT(TLS_ALERT_DECRYPT_ERROR, 0, "TLS_FINISHED contents don't match"); return false; } if (!tls_save_verify_data(tls, 0, received, vdl)) return false; return true; } static bool tls_ptr_match(const void *a, const void *b) { return a == b; } static bool tls_handle_hello_extensions(struct l_tls *tls, const uint8_t *buf, size_t len, struct l_queue *seen) { unsigned int i; const struct tls_hello_extension *extension; bool client_hello = tls->server; uint16_t extensions_size; if (!len) return true; if (len < 2 || len > 2 + 65535) goto decode_error; extensions_size = l_get_be16(buf); len -= 2; buf += 2; if (len != extensions_size) goto decode_error; while (len) { uint16_t ext_id; size_t ext_len; bool (*handler)(struct l_tls *tls, const uint8_t *buf, size_t len); if (len < 4) goto decode_error; ext_id = l_get_be16(buf + 0); ext_len = l_get_be16(buf + 2); buf += 4; len -= 4; if (ext_len > len) goto decode_error; /* * RFC 5246, Section 7.4.1.4: "There MUST NOT be more than * one extension of the same type." */ if (l_queue_find(seen, tls_ptr_match, L_UINT_TO_PTR(ext_id))) { TLS_DEBUG("Duplicate extension %u", ext_id); goto decode_error; } l_queue_push_tail(seen, L_UINT_TO_PTR(ext_id)); extension = NULL; for (i = 0; tls_extensions[i].name; i++) if (tls_extensions[i].id == ext_id) { extension = &tls_extensions[i]; break; } if (!extension) goto next; handler = client_hello ? extension->client_handle : extension->server_handle; /* * RFC 5246, Section 7.4.1.4: "If a client receives an * extension type in ServerHello that it did not request in * the associated ClientHello, it MUST abort the handshake * with an unsupported_extension fatal alert." * There are however servers that include an unsolicited * Supported Point Format extension where the handshake * still completes fine if the extension is ignored so we * do this instead. */ if (!client_hello && !handler) { TLS_DEBUG("non-fatal: %s extension not expected in " "a ServerHello", extension->name); goto next; } if (!handler(tls, buf, ext_len)) { TLS_DISCONNECT(TLS_ALERT_DECODE_ERROR, 0, "Hello %s extension parse error", extension->name); return false; } next: buf += ext_len; len -= ext_len; } /* * Trigger any actions needed when an extension is missing and its * handler has not been called yet. */ for (i = 0; tls_extensions[i].name; i++) { bool (*handler)(struct l_tls *tls); extension = &tls_extensions[i]; handler = client_hello ? extension->client_handle_absent : extension->server_handle_absent; if (!handler) continue; if (l_queue_find(seen, tls_ptr_match, L_UINT_TO_PTR(extension->id))) continue; if (!handler(tls)) { TLS_DISCONNECT(TLS_ALERT_DECODE_ERROR, 0, "Hello %s extension missing", extension->name); return false; } } return true; decode_error: TLS_DISCONNECT(TLS_ALERT_DECODE_ERROR, 0, "Hello extensions decode error"); return false; } static void tls_server_resume_error(struct l_tls *tls) { /* * When Client Hello parameters don't match the parameters of the * cached session that was requested by the client, we'll probably * start and cache a new session. Even though RFC 5246 doesn't * specifically mandate that the requested session be forgotten * (there's no fatal Alert in that case), we overwrite the old * session's entry in the cache with the new session's data to * avoid keeping many sessions related to one client in the cache. * In theory this allows an attacker to connect as a client and * invalidate a legitimate client's session entry in our cache, * DoSing the session resumption mechanism so that clients have * to go through the full handshake. In practice there are many * ways for an attacker to do that even without this. * * Our client mode only caches one last session anyway, other * implementations may work that way too. */ memcpy(tls->session_id_replaced, tls->session_id, tls->session_id_size); tls->session_id_size_replaced = tls->session_id_size; tls->session_id_size = 0; tls->session_id_new = false; l_free(l_steal_ptr(tls->session_peer_identity)); } /* RFC 5746 */ static const uint8_t tls_empty_renegotiation_info_scsv[2] = { 0x00, 0xff }; static const uint16_t tls_renegotiation_info_id = 0xff01; static void tls_handle_client_hello(struct l_tls *tls, const uint8_t *buf, size_t len) { uint16_t cipher_suites_size; uint8_t session_id_size, compression_methods_size; const uint8_t *cipher_suites; const uint8_t *compression_methods; int i; struct l_queue *extensions_offered = NULL; enum l_tls_alert_desc alert_desc = TLS_ALERT_HANDSHAKE_FAIL; bool resuming = false; _auto_(l_free) char *session_id_str = NULL; struct tls_cipher_suite *backup_suite = NULL; struct tls_compression_method *backup_cm = NULL; /* Do we have enough for ProtocolVersion + Random + SessionID size? */ if (len < 2 + 32 + 1) goto decode_error; memcpy(tls->pending.client_random, buf + 2, 32); session_id_size = buf[34]; len -= 35; if (unlikely(session_id_size > 32)) goto decode_error; /* * Do we have enough to hold the actual session ID + 2 byte field for * cipher_suite len + minimum of a single cipher suite identifier */ if (len < (size_t) session_id_size + 4) goto decode_error; len -= session_id_size + 2; cipher_suites_size = l_get_be16(buf + 35 + session_id_size); cipher_suites = buf + 37 + session_id_size; /* * Check that size is not odd, more than 0 and we have enough * data in the packet for cipher_suites_size + 2 bytes for * compression_methods_size + a single compression method */ if (len < (size_t) cipher_suites_size + 2 || (cipher_suites_size & 1) || cipher_suites_size == 0) goto decode_error; len -= cipher_suites_size + 1; compression_methods_size = cipher_suites[cipher_suites_size]; compression_methods = cipher_suites + cipher_suites_size + 1; if (len < (size_t) compression_methods_size || compression_methods_size == 0) goto decode_error; len -= compression_methods_size; if (session_id_size && tls->session_settings && tls_load_cached_server_session(tls, buf + 35, session_id_size)) { /* * Attempt a session resumption but note later checks may * spoil this. */ resuming = true; session_id_str = l_util_hexstring(tls->session_id, tls->session_id_size); } if (tls->pending_destroy) return; extensions_offered = l_queue_new(); if (!tls_handle_hello_extensions(tls, compression_methods + compression_methods_size, len, extensions_offered)) goto cleanup; /* Save client_version for Premaster Secret verification */ tls->client_version = l_get_be16(buf); if (tls->client_version < tls->min_version) { TLS_DISCONNECT(TLS_ALERT_PROTOCOL_VERSION, 0, "Client version too low: %02x", tls->client_version); goto cleanup; } tls->negotiated_version = tls->client_version > tls->max_version ? tls->max_version : tls->client_version; /* Stop maintaining handshake message hashes other than MD1 and SHA. */ if (tls->negotiated_version < L_TLS_V12) for (i = 0; i < __HANDSHAKE_HASH_COUNT; i++) if (i != HANDSHAKE_HASH_SHA1 && i != HANDSHAKE_HASH_MD5) tls_drop_handshake_hash(tls, i); TLS_DEBUG("Negotiated TLS " TLS_VER_FMT, TLS_VER_ARGS(tls->negotiated_version)); if (!tls->cipher_suite_pref_list) { TLS_DISCONNECT(TLS_ALERT_INTERNAL_ERROR, 0, "No usable cipher suites"); goto cleanup; } if (!tls->renegotiation_info.secure_renegotiation || tls->ready) { for (i = 0; i < cipher_suites_size; i += 2) if (l_get_be16(cipher_suites + i) == l_get_be16( tls_empty_renegotiation_info_scsv)) break; if (i < cipher_suites_size) { if (unlikely(tls->ready)) { TLS_DISCONNECT(TLS_ALERT_ILLEGAL_PARAM, 0, "Empty renegotiation_info in " "renegotiation Client Hello"); goto cleanup; } /* * RFC 5746 Section 3.6, act as if we had received an * empty renegotiation_info extension. */ tls->renegotiation_info.secure_renegotiation = true; l_queue_push_tail(extensions_offered, L_UINT_TO_PTR( tls_renegotiation_info_id)); } } /* Select a cipher suite according to client's preference list */ while (cipher_suites_size) { struct tls_cipher_suite *suite = tls_find_cipher_suite(cipher_suites); struct tls_cipher_suite **iter; const char *error; for (iter = tls->cipher_suite_pref_list; *iter; iter++) if (*iter == suite) break; if (!suite) TLS_DEBUG("non-fatal: Cipher suite %04x unknown", l_get_be16(cipher_suites)); else if (!tls_cipher_suite_is_compatible(tls, suite, &error)) TLS_DEBUG("non-fatal: %s", error); else if (!*iter) { /* * We have at least one matching compatible suite but * it is not allowed in this security profile. If the * handshake ends up failing then we blame the security * profile. */ alert_desc = TLS_ALERT_INSUFFICIENT_SECURITY; TLS_DEBUG("non-fatal: Cipher suite %s disallowed " "by config", suite->name); } else if (resuming && memcmp(tls->session_cipher_suite_id, suite->id, 2)) { /* * For now skip this cipher suite because we're trying * to find the one from the cached session state. But * keep it as a backup in case we end up starting a new * session. */ if (!backup_suite) backup_suite = suite; } else { tls->pending.cipher_suite = suite; break; } cipher_suites += 2; cipher_suites_size -= 2; } if (unlikely(!cipher_suites_size && backup_suite)) { TLS_DEBUG("Cached session %s's cipher suite %04x " "unavailable, will start a new session", session_id_str, l_get_be16(tls->session_cipher_suite_id)); tls->pending.cipher_suite = backup_suite; resuming = false; tls_server_resume_error(tls); } else if (unlikely(!cipher_suites_size)) { TLS_DISCONNECT(alert_desc, 0, "No common cipher suites matching negotiated " "TLS version and our certificate's key type"); goto cleanup; } if (!tls_set_prf_hmac(tls)) { TLS_DISCONNECT(TLS_ALERT_INTERNAL_ERROR, 0, "Error selecting the PRF HMAC"); goto cleanup; } /* Select a compression method */ /* CompressionMethod.null must be present in the vector */ while (compression_methods_size) { struct tls_compression_method *cm = tls_find_compression_method(*compression_methods); if (!cm) TLS_DEBUG("non-fatal: Compression %02x unknown", *compression_methods); else if (resuming && *compression_methods != tls->session_compression_method_id) { /* * For now skip this compression method because we're * trying to find the one from the cached session state. * But keep it as a backup in case we end up starting * a new * session. */ if (!backup_cm) backup_cm = cm; } else { tls->pending.compression_method = cm; break; } compression_methods++; compression_methods_size--; } if (unlikely(!compression_methods_size && backup_cm)) { TLS_DEBUG("Cached session %s's compression method %02x " "unavailable, will start a new session", session_id_str, tls->session_compression_method_id); tls->pending.compression_method = backup_cm; if (backup_suite) tls->pending.cipher_suite = backup_suite; resuming = false; tls_server_resume_error(tls); } else if (unlikely(!compression_methods_size)) { TLS_DISCONNECT(TLS_ALERT_HANDSHAKE_FAIL, 0, "No common compression methods"); goto cleanup; } if (resuming) TLS_DEBUG("Negotiated resumption of cached session %s", session_id_str); TLS_DEBUG("Negotiated %s", tls->pending.cipher_suite->name); TLS_DEBUG("Negotiated %s", tls->pending.compression_method->name); if (!resuming && tls->session_settings) { tls->session_id_new = true; tls->session_id_size = 32; l_getrandom(tls->session_id, 32); } if (!tls_send_server_hello(tls, extensions_offered)) goto cleanup; l_queue_destroy(extensions_offered, NULL); if (resuming) { const char *error; tls_update_key_block(tls); tls_send_change_cipher_spec(tls); if (!tls_change_cipher_spec(tls, 1, &error)) { TLS_DISCONNECT(TLS_ALERT_INTERNAL_ERROR, 0, "change_cipher_spec: %s", error); return; } if (!tls_send_finished(tls)) return; TLS_SET_STATE(TLS_HANDSHAKE_WAIT_CHANGE_CIPHER_SPEC); return; } if (tls->pending.cipher_suite->signature && tls->cert) if (!tls_send_certificate(tls)) return; if (tls->pending.cipher_suite->key_xchg->send_server_key_exchange) if (!tls->pending.cipher_suite->key_xchg-> send_server_key_exchange(tls)) return; /* TODO: don't bother if configured to not authenticate client */ if (tls->pending.cipher_suite->signature && tls->ca_certs) if (!tls_send_certificate_request(tls)) return; tls_send_server_hello_done(tls); if (tls->pending.cipher_suite->signature && tls->ca_certs) TLS_SET_STATE(TLS_HANDSHAKE_WAIT_CERTIFICATE); else TLS_SET_STATE(TLS_HANDSHAKE_WAIT_KEY_EXCHANGE); return; decode_error: TLS_DISCONNECT(TLS_ALERT_DECODE_ERROR, 0, "ClientHello decode error"); cleanup: l_queue_destroy(extensions_offered, NULL); } static void tls_handle_server_hello(struct l_tls *tls, const uint8_t *buf, size_t len) { uint8_t session_id_size, cipher_suite_id[2], compression_method_id; const char *error; struct tls_cipher_suite **iter; int i; struct l_queue *extensions_seen; bool result; uint16_t version; bool resuming = false; /* Do we have enough for ProtocolVersion + Random + SessionID len ? */ if (len < 2 + 32 + 1) goto decode_error; version = l_get_be16(buf); memcpy(tls->pending.server_random, buf + 2, 32); session_id_size = buf[34]; len -= 35; /* Do we have enough for SessionID + CipherSuite ID + Compression ID */ if (len < (size_t) session_id_size + 2 + 1) goto decode_error; cipher_suite_id[0] = buf[35 + session_id_size + 0]; cipher_suite_id[1] = buf[35 + session_id_size + 1]; compression_method_id = buf[35 + session_id_size + 2]; len -= session_id_size + 2 + 1; if (session_id_size > 32) goto decode_error; if (tls->session_id_size) { _auto_(l_free) char *session_id_str = l_util_hexstring(tls->session_id, tls->session_id_size); if (session_id_size == tls->session_id_size && !memcmp(buf + 35, tls->session_id, session_id_size)) { TLS_DEBUG("Negotiated resumption of cached session %s", session_id_str); resuming = true; } else { TLS_DEBUG("Server decided not to resume cached session " "%s, sent %s session ID", session_id_str, session_id_size ? "a new" : "no"); tls->session_id_size = 0; } } if (session_id_size && !resuming && tls->session_settings) { tls->session_id_new = true; tls->session_id_size = session_id_size; memcpy(tls->session_id, buf + 35, session_id_size); } extensions_seen = l_queue_new(); result = tls_handle_hello_extensions(tls, buf + 38 + session_id_size, len, extensions_seen); l_queue_destroy(extensions_seen, NULL); if (!result) return; if (version < tls->min_version || version > tls->max_version) { TLS_DISCONNECT(version < tls->min_version ? TLS_ALERT_PROTOCOL_VERSION : TLS_ALERT_ILLEGAL_PARAM, 0, "Unsupported version %02x", version); return; } tls->negotiated_version = version; /* Stop maintaining handshake message hashes other than MD1 and SHA. */ if (tls->negotiated_version < L_TLS_V12) for (i = 0; i < __HANDSHAKE_HASH_COUNT; i++) if (i != HANDSHAKE_HASH_SHA1 && i != HANDSHAKE_HASH_MD5) tls_drop_handshake_hash(tls, i); TLS_DEBUG("Negotiated TLS " TLS_VER_FMT, TLS_VER_ARGS(tls->negotiated_version)); /* Set the new cipher suite and compression method structs */ tls->pending.cipher_suite = tls_find_cipher_suite(cipher_suite_id); if (!tls->pending.cipher_suite) { TLS_DISCONNECT(TLS_ALERT_HANDSHAKE_FAIL, 0, "Unknown cipher suite %04x", l_get_be16(cipher_suite_id)); return; } for (iter = tls->cipher_suite_pref_list; *iter; iter++) if (*iter == tls->pending.cipher_suite) break; if (!*iter) { TLS_DISCONNECT(TLS_ALERT_INSUFFICIENT_SECURITY, 0, "Selected cipher suite %s disallowed by config", tls->pending.cipher_suite->name); return; } if (!tls_cipher_suite_is_compatible(tls, tls->pending.cipher_suite, &error)) { TLS_DISCONNECT(TLS_ALERT_HANDSHAKE_FAIL, 0, "Selected cipher suite not compatible: %s", error); return; } if (!tls_set_prf_hmac(tls)) { TLS_DISCONNECT(TLS_ALERT_INTERNAL_ERROR, 0, "Error selecting the PRF HMAC"); return; } TLS_DEBUG("Negotiated %s", tls->pending.cipher_suite->name); tls->pending.compression_method = tls_find_compression_method(compression_method_id); if (!tls->pending.compression_method) { TLS_DISCONNECT(TLS_ALERT_HANDSHAKE_FAIL, 0, "Unknown compression method %i", compression_method_id); return; } TLS_DEBUG("Negotiated %s", tls->pending.compression_method->name); if (resuming) { /* * Now that we've validated the Server Hello parameters and * know that they're supported by this version of ell and * consistent with the current configuration, ensure that * they're identical with the ones in the cached session * being resumed. This serves as a sanity check for * rare situations like a corrupt session cache file or * a file written by a newer ell version. */ if (tls->negotiated_version != tls->client_version || memcmp(cipher_suite_id, tls->session_cipher_suite_id, 2) || compression_method_id != tls->session_compression_method_id) { TLS_DISCONNECT(TLS_ALERT_HANDSHAKE_FAIL, 0, "Session parameters don't match"); return; } tls_update_key_block(tls); TLS_SET_STATE(TLS_HANDSHAKE_WAIT_CHANGE_CIPHER_SPEC); } else if (tls->pending.cipher_suite->signature) TLS_SET_STATE(TLS_HANDSHAKE_WAIT_CERTIFICATE); else TLS_SET_STATE(TLS_HANDSHAKE_WAIT_KEY_EXCHANGE); return; decode_error: TLS_DISCONNECT(TLS_ALERT_DECODE_ERROR, 0, "ServerHello decode error"); } static void tls_handle_certificate(struct l_tls *tls, const uint8_t *buf, size_t len) { size_t total; _auto_(l_certchain_free) struct l_certchain *certchain = NULL; struct l_cert *leaf; size_t der_len; const uint8_t *der; bool dummy; const char *error_str; char *subject_str; if (len < 3) goto decode_error; /* Length checks */ total = *buf++ << 16; total |= *buf++ << 8; total |= *buf++ << 0; if (total + 3 != len) goto decode_error; if (tls_parse_certificate_list(buf, total, &certchain) < 0) { TLS_DISCONNECT(TLS_ALERT_DECODE_ERROR, 0, "Error decoding peer certificate chain"); return; } /* * "Note that a client MAY send no certificates if it does not have any * appropriate certificate to send in response to the server's * authentication request." -- for now we unconditionally accept * an empty certificate chain from the client. Later on we need to * make this configurable, if we don't want to authenticate the * client then also don't bother sending a Certificate Request. */ if (!certchain) { if (!tls->server) { TLS_DISCONNECT(TLS_ALERT_HANDSHAKE_FAIL, 0, "Server sent no certificate chain"); return; } TLS_SET_STATE(TLS_HANDSHAKE_WAIT_KEY_EXCHANGE); return; } if (tls->cert_dump_path) { int r = pem_write_certificate_chain(certchain, tls->cert_dump_path); if (r < 0) TLS_DEBUG("Error %i (%s) writing the peer certchain " "to %s", -r, strerror(-r), tls->cert_dump_path); else TLS_DEBUG("Peer certchain written to %s", tls->cert_dump_path); } /* * Validate the certificate chain's consistency and validate it * against our CAs if we have any. */ if (!l_certchain_verify(certchain, tls->ca_certs, &error_str)) { if (tls->ca_certs) { TLS_DISCONNECT(TLS_ALERT_BAD_CERT, 0, "Peer certchain verification failed " "consistency check%s: %s", tls->ca_certs ? " or against local CA certs" : "", error_str); return; } /* * Until the mainstream kernel can handle the occasionally * used certificates without the AKID extension (both root, * which is legal, and non-root, which is iffy but still * happens) don't fail on peer certificate chain verification * failure when CA certificates were not provided. Knowing * that the chain is self-consistent alone doesn't * authenticate the peer in any way. Only warn when it looks * like the chain is bad but parses and we can get the peer * public key from it below. */ TLS_DEBUG("Peer certchain verification failed (%s.) No local " "CA certs provided so proceeding anyway. This " "failure can signal a security issue or a " "known kernel problem with some certificates.", error_str); } /* * RFC5246 7.4.2: * "The end entity certificate's public key (and associated * restrictions) MUST be compatible with the selected key exchange * algorithm." */ leaf = l_certchain_get_leaf(certchain); if (!tls->pending.cipher_suite->signature-> validate_cert_key_type(leaf)) { TLS_DISCONNECT(TLS_ALERT_UNSUPPORTED_CERT, 0, "Peer certificate key type incompatible with " "pending cipher suite %s", tls->pending.cipher_suite->name); return; } if (tls->subject_mask && !tls_cert_domains_match_mask(leaf, tls->subject_mask, &subject_str)) { char *mask = l_strjoinv(tls->subject_mask, '|'); TLS_DISCONNECT(TLS_ALERT_BAD_CERT, 0, "Peer certificate's subject domain " "doesn't match mask %s: %s", mask, subject_str); l_free(mask); l_free(subject_str); return; } /* Save the end-entity certificate and free the chain */ der = l_cert_get_der_data(leaf, &der_len); tls->peer_cert = l_cert_new_from_der(der, der_len); tls->peer_pubkey = l_cert_get_pubkey(tls->peer_cert); if (!tls->peer_pubkey) { TLS_DISCONNECT(TLS_ALERT_UNSUPPORTED_CERT, 0, "Error loading peer public key to kernel"); return; } switch (l_cert_get_pubkey_type(tls->peer_cert)) { case L_CERT_KEY_RSA: if (!l_key_get_info(tls->peer_pubkey, L_KEY_RSA_PKCS1_V1_5, L_CHECKSUM_NONE, &tls->peer_pubkey_size, &dummy)) goto pubkey_unsupported; break; case L_CERT_KEY_ECC: if (!l_key_get_info(tls->peer_pubkey, L_KEY_ECDSA_X962, L_CHECKSUM_SHA1, &tls->peer_pubkey_size, &dummy)) goto pubkey_unsupported; break; case L_CERT_KEY_UNKNOWN: TLS_DISCONNECT(TLS_ALERT_INTERNAL_ERROR, 0, "Unknown public key type"); return; } tls->peer_pubkey_size /= 8; if (tls->server || tls->pending.cipher_suite->key_xchg-> handle_server_key_exchange) TLS_SET_STATE(TLS_HANDSHAKE_WAIT_KEY_EXCHANGE); else TLS_SET_STATE(TLS_HANDSHAKE_WAIT_HELLO_DONE); return; pubkey_unsupported: TLS_DISCONNECT(TLS_ALERT_INTERNAL_ERROR, 0, "Can't l_key_get_info for peer public key"); return; decode_error: TLS_DISCONNECT(TLS_ALERT_DECODE_ERROR, 0, "TLS_CERTIFICATE decode error"); } static void tls_handle_certificate_request(struct l_tls *tls, const uint8_t *buf, size_t len) { unsigned int cert_type_len, dn_len, i; tls->cert_requested = 1; cert_type_len = *buf++; if (len < 1 + cert_type_len + 2) goto decode_error; for (i = 0; i < sizeof(tls_cert_type_pref); i++) if (memchr(buf, tls_cert_type_pref[i], cert_type_len)) break; if (i == sizeof(tls_cert_type_pref)) { TLS_DISCONNECT(TLS_ALERT_UNSUPPORTED_CERT, 0, "Requested certificate types not supported"); return; } buf += cert_type_len; len -= 1 + cert_type_len; /* * TODO: parse and save certificate_types, * supported_signature_algorithms and certificate_authorities * lists for use in tls_send_certificate. */ if (tls->negotiated_version >= L_TLS_V12) { enum handshake_hash_type hash; ssize_t ret = tls_parse_signature_algorithms(tls, buf, len); if (ret == -ENOTSUP) { TLS_DISCONNECT(TLS_ALERT_UNSUPPORTED_CERT, 0, "No supported signature hash type"); return; } if (ret < 0) goto decode_error; len -= ret; buf += ret; /* * We can now safely stop maintaining handshake message * hashes other than the PRF hash and the one selected for * signing. */ for (hash = 0; hash < __HANDSHAKE_HASH_COUNT; hash++) if (&tls_handshake_hash_data[hash] != tls->prf_hmac && hash != tls->signature_hash) tls_drop_handshake_hash(tls, hash); } dn_len = l_get_be16(buf); if (2 + dn_len != len) goto decode_error; return; decode_error: TLS_DISCONNECT(TLS_ALERT_DECODE_ERROR, 0, "CertificateRequest decode error"); } static void tls_handle_server_hello_done(struct l_tls *tls, const uint8_t *buf, size_t len) { const char *error; if (len) { TLS_DISCONNECT(TLS_ALERT_DECODE_ERROR, 0, "ServerHello not empty"); return; } if (tls->cert_requested) if (!tls_send_certificate(tls)) return; if (!tls->pending.cipher_suite->key_xchg->send_client_key_exchange(tls)) return; if (tls->cert_sent) if (!tls_send_certificate_verify(tls)) return; tls_send_change_cipher_spec(tls); if (!tls_change_cipher_spec(tls, 1, &error)) { TLS_DISCONNECT(TLS_ALERT_INTERNAL_ERROR, 0, "change_cipher_spec: %s", error); return; } if (!tls_send_finished(tls)) return; TLS_SET_STATE(TLS_HANDSHAKE_WAIT_CHANGE_CIPHER_SPEC); } static bool tls_get_prev_digest_by_type(struct l_tls *tls, enum handshake_hash_type type, const uint8_t *data, size_t data_len, uint8_t *out, size_t *out_len) { size_t len; if (!tls->handshake_hash[type]) return false; len = l_checksum_digest_length(tls_handshake_hash_data[type].l_id); memcpy(out, tls->prev_digest[type], len); if (out_len) *out_len = len; return 0; } static void tls_handle_certificate_verify(struct l_tls *tls, const uint8_t *buf, size_t len) { int i; if (!tls->pending.cipher_suite->signature->verify(tls, buf, len, tls_get_prev_digest_by_type, NULL, 0)) return; /* Stop maintaining handshake message hashes other than the PRF hash */ if (tls->negotiated_version >= L_TLS_V12) for (i = 0; i < __HANDSHAKE_HASH_COUNT; i++) if (&tls_handshake_hash_data[i] != tls->prf_hmac) tls_drop_handshake_hash(tls, i); /* * The client's certificate is now verified based on the following * logic: * - If we received an (expected) Certificate Verify, we must have * sent a Certificate Request. * - If we sent a Certificate Request that's because * tls->ca_certs is non-NULL. * - If tls->ca_certs is non-NULL then tls_handle_certificate * will have checked the whole certificate chain to be valid and * additionally trusted by our CAs if known. * - Additionally cipher_suite->signature->verify has just confirmed * that the peer owns the end-entity certificate because it was * able to sign the contents of the handshake messages and that * signature could be verified with the public key from that * certificate. */ tls->peer_authenticated = true; TLS_SET_STATE(TLS_HANDSHAKE_WAIT_CHANGE_CIPHER_SPEC); } struct dn_element_info { const char *str; const struct asn1_oid oid; }; static const struct dn_element_info dn_elements[] = { { "CN", { 3, { 0x55, 0x04, 0x03 } } }, { "SN", { 3, { 0x55, 0x04, 0x04 } } }, { "serialNumber", { 3, { 0x55, 0x04, 0x05 } } }, { "C", { 3, { 0x55, 0x04, 0x06 } } }, { "ST", { 3, { 0x55, 0x04, 0x07 } } }, { "L", { 3, { 0x55, 0x04, 0x08 } } }, { "street", { 3, { 0x55, 0x04, 0x09 } } }, { "O", { 3, { 0x55, 0x04, 0x0a } } }, { "OU", { 3, { 0x55, 0x04, 0x0b } } }, { "title", { 3, { 0x55, 0x04, 0x0c } } }, { "telephoneNumber", { 3, { 0x55, 0x04, 0x14 } } }, { "givenName", { 3, { 0x55, 0x04, 0x2a } } }, { "initials", { 3, { 0x55, 0x04, 0x2b } } }, { "emailAddress", { 9, { 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x09, 0x01 } } }, { "domainComponent", { 10, { 0x09, 0x92, 0x26, 0x89, 0x93, 0xf2, 0x2c, 0x64, 0x01, 0x19 } } }, {} }; static void tls_str_escape_append(struct l_string *out, char *str, size_t len) { while (len--) { switch (*str) { case '\\': case '/': case '=': l_string_append_c(out, '\\'); l_string_append_c(out, *str); break; default: l_string_append_c(out, *str); break; } str++; } } static char *tls_get_peer_identity_str(struct l_cert *cert) { const uint8_t *dn, *end; size_t dn_size; struct l_string *id_str; if (!cert) return NULL; dn = l_cert_get_dn(cert, &dn_size); if (!dn) return NULL; id_str = l_string_new(200); end = dn + dn_size; while (dn < end) { const uint8_t *set, *seq, *oid, *name; uint8_t tag; size_t len, oid_len, name_len; const struct dn_element_info *info; set = asn1_der_find_elem(dn, end - dn, 0, &tag, &len); if (!set || tag != ASN1_ID_SET) goto error; dn = set + len; seq = asn1_der_find_elem(set, len, 0, &tag, &len); if (!seq || tag != ASN1_ID_SEQUENCE) goto error; oid = asn1_der_find_elem(seq, len, 0, &tag, &oid_len); if (!oid || tag != ASN1_ID_OID) goto error; name = asn1_der_find_elem(seq, len, 1, &tag, &name_len); if (!name || (tag != ASN1_ID_PRINTABLESTRING && tag != ASN1_ID_UTF8STRING && tag != ASN1_ID_IA5STRING)) continue; for (info = dn_elements; info->str; info++) if (asn1_oid_eq(&info->oid, oid_len, oid)) break; if (!info->str) continue; l_string_append_c(id_str, '/'); l_string_append(id_str, info->str); l_string_append_c(id_str, '='); tls_str_escape_append(id_str, (char *) name, name_len); } return l_string_unwrap(id_str); error: l_string_free(id_str); return NULL; } static void tls_finished(struct l_tls *tls) { _auto_(l_free) char *peer_cert_identity = NULL; char *peer_identity = NULL; uint64_t peer_cert_expiry; bool resuming = tls->session_id_size && !tls->session_id_new; bool session_update = false; bool renegotiation = tls->ready; if (tls->peer_authenticated && !resuming) { peer_cert_identity = tls_get_peer_identity_str(tls->peer_cert); if (!peer_cert_identity) { TLS_DISCONNECT(TLS_ALERT_INTERNAL_ERROR, 0, "tls_get_peer_identity_str failed"); return; } peer_identity = peer_cert_identity; if (tls->session_id_new && !l_cert_get_valid_times(tls->peer_cert, NULL, &peer_cert_expiry)) { TLS_DISCONNECT(TLS_ALERT_INTERNAL_ERROR, 0, "l_cert_get_valid_times failed"); return; } } else if (tls->peer_authenticated && resuming) peer_identity = tls->session_peer_identity; if (tls->session_settings && tls->session_id_new) { _auto_(l_free) char *session_id_str = l_util_hexstring(tls->session_id, tls->session_id_size); uint64_t expiry = tls->session_lifetime ? time_realtime_now() + tls->session_lifetime : 0; const char *group_name = tls_get_cache_group_name(tls, tls->session_id, tls->session_id_size); if (tls->peer_authenticated && (!expiry || peer_cert_expiry < expiry)) expiry = peer_cert_expiry; if (!tls->server) l_settings_set_bytes(tls->session_settings, group_name, "SessionID", tls->session_id, tls->session_id_size); l_settings_set_bytes(tls->session_settings, group_name, "SessionMasterSecret", tls->pending.master_secret, 48); l_settings_set_int(tls->session_settings, group_name, "SessionVersion", tls->negotiated_version); l_settings_set_bytes(tls->session_settings, group_name, "SessionCipherSuite", tls->pending.cipher_suite->id, 2); l_settings_set_uint(tls->session_settings, group_name, "SessionCompressionMethod", tls->pending.compression_method->id); if (expiry) l_settings_set_uint64(tls->session_settings, group_name, "SessionExpiryTime", expiry); else /* We may be overwriting an older session's data */ l_settings_remove_key(tls->session_settings, group_name, "SessionExpiryTime"); if (tls->peer_authenticated) l_settings_set_string(tls->session_settings, group_name, "SessionPeerIdentity", peer_identity); else /* We may be overwriting an older session's data */ l_settings_remove_key(tls->session_settings, group_name, "SessionPeerIdentity"); TLS_DEBUG("Saving new session %s to cache", session_id_str); session_update = true; if (tls->session_id_size_replaced) { tls_forget_cached_session(tls, NULL, tls->session_id_replaced, tls->session_id_size_replaced, false); tls->session_id_size_replaced = 0; } } /* Free up the resources used in the handshake */ tls_reset_handshake(tls); TLS_SET_STATE(TLS_HANDSHAKE_DONE); tls->ready = true; tls->session_resumed = resuming; if (session_update && tls->session_update_cb) { tls->in_callback = true; tls->session_update_cb(tls->session_update_user_data); tls->in_callback = false; if (tls->pending_destroy) return; } if (!renegotiation) { tls->in_callback = true; tls->ready_handle(peer_identity, tls->user_data); tls->in_callback = false; } tls_cleanup_handshake(tls); } static void tls_handle_handshake(struct l_tls *tls, int type, const uint8_t *buf, size_t len) { bool resuming; TLS_DEBUG("Handling a %s of %zi bytes", tls_handshake_type_to_str(type), len); switch (type) { case TLS_HELLO_REQUEST: if (tls->server) { TLS_DISCONNECT(TLS_ALERT_UNEXPECTED_MESSAGE, 0, "Message invalid in server mode"); break; } if (len != 0) { TLS_DISCONNECT(TLS_ALERT_DECODE_ERROR, 0, "HelloRequest not empty"); break; } /* * May be sent by the server at any time but "SHOULD be ignored * by the client if it arrives in the middle of a handshake" * and "MAY be ignored by the client if it does not wish to * renegotiate a session". */ if (tls->state != TLS_HANDSHAKE_DONE) { TLS_DEBUG("Message invalid in state %s", tls_handshake_state_to_str(tls->state)); break; } if (!tls_send_client_hello(tls)) break; TLS_SET_STATE(TLS_HANDSHAKE_WAIT_HELLO); break; case TLS_CLIENT_HELLO: if (!tls->server) { TLS_DISCONNECT(TLS_ALERT_UNEXPECTED_MESSAGE, 0, "Message invalid in client mode"); break; } if (tls->state != TLS_HANDSHAKE_WAIT_HELLO && tls->state != TLS_HANDSHAKE_DONE) { TLS_DISCONNECT(TLS_ALERT_UNEXPECTED_MESSAGE, 0, "Message invalid in state %s", tls_handshake_state_to_str(tls->state)); break; } tls_handle_client_hello(tls, buf, len); break; case TLS_SERVER_HELLO: if (tls->server) { TLS_DISCONNECT(TLS_ALERT_UNEXPECTED_MESSAGE, 0, "Message invalid in server mode"); break; } if (tls->state != TLS_HANDSHAKE_WAIT_HELLO) { TLS_DISCONNECT(TLS_ALERT_UNEXPECTED_MESSAGE, 0, "Message invalid in state %s", tls_handshake_state_to_str(tls->state)); break; } tls_handle_server_hello(tls, buf, len); break; case TLS_CERTIFICATE: if (tls->state != TLS_HANDSHAKE_WAIT_CERTIFICATE) { TLS_DISCONNECT(TLS_ALERT_UNEXPECTED_MESSAGE, 0, "Message invalid in state %s", tls_handshake_state_to_str(tls->state)); break; } tls_handle_certificate(tls, buf, len); break; case TLS_SERVER_KEY_EXCHANGE: if (tls->server) { TLS_DISCONNECT(TLS_ALERT_UNEXPECTED_MESSAGE, 0, "Message invalid in server mode"); break; } if (tls->state != TLS_HANDSHAKE_WAIT_KEY_EXCHANGE) { TLS_DISCONNECT(TLS_ALERT_UNEXPECTED_MESSAGE, 0, "Message invalid in state %s", tls_handshake_state_to_str(tls->state)); break; } TLS_SET_STATE(TLS_HANDSHAKE_WAIT_HELLO_DONE); tls->pending.cipher_suite->key_xchg->handle_server_key_exchange( tls, buf, len); break; case TLS_CERTIFICATE_REQUEST: if (tls->server) { TLS_DISCONNECT(TLS_ALERT_UNEXPECTED_MESSAGE, 0, "Message invalid in server mode"); break; } /* * Server sends this optionally so in the WAIT_HELLO_DONE * state we accept either this or a Server Hello Done (below). */ if (tls->state != TLS_HANDSHAKE_WAIT_HELLO_DONE || tls->cert_requested || !tls->pending.cipher_suite->signature) { TLS_DISCONNECT(TLS_ALERT_UNEXPECTED_MESSAGE, 0, "Message invalid in current state " "or certificate check not supported " "in pending cipher suite"); break; } tls_handle_certificate_request(tls, buf, len); break; case TLS_SERVER_HELLO_DONE: if (tls->state != TLS_HANDSHAKE_WAIT_HELLO_DONE) { TLS_DISCONNECT(TLS_ALERT_UNEXPECTED_MESSAGE, 0, "Message invalid in state %s", tls_handshake_state_to_str(tls->state)); break; } tls_handle_server_hello_done(tls, buf, len); break; case TLS_CERTIFICATE_VERIFY: if (tls->state != TLS_HANDSHAKE_WAIT_CERTIFICATE_VERIFY) { TLS_DISCONNECT(TLS_ALERT_UNEXPECTED_MESSAGE, 0, "Message invalid in state %s", tls_handshake_state_to_str(tls->state)); break; } tls_handle_certificate_verify(tls, buf, len); break; case TLS_CLIENT_KEY_EXCHANGE: if (!tls->server) { TLS_DISCONNECT(TLS_ALERT_UNEXPECTED_MESSAGE, 0, "Message invalid in client mode"); break; } if (tls->state != TLS_HANDSHAKE_WAIT_KEY_EXCHANGE) { TLS_DISCONNECT(TLS_ALERT_UNEXPECTED_MESSAGE, 0, "Message invalid in state %s", tls_handshake_state_to_str(tls->state)); break; } /* * If we accepted a client Certificate message with a * certificate that has signing capability (TODO: check * usage bitmask), Certificate Verify is received next. It * sounds as if this is mandatory for the client although * this isn't 100% clear. */ if (tls->peer_pubkey) TLS_SET_STATE(TLS_HANDSHAKE_WAIT_CERTIFICATE_VERIFY); else TLS_SET_STATE(TLS_HANDSHAKE_WAIT_CHANGE_CIPHER_SPEC); tls->pending.cipher_suite->key_xchg->handle_client_key_exchange( tls, buf, len); break; case TLS_FINISHED: if (tls->state != TLS_HANDSHAKE_WAIT_FINISHED) { TLS_DISCONNECT(TLS_ALERT_UNEXPECTED_MESSAGE, 0, "Message invalid in state %s", tls_handshake_state_to_str(tls->state)); break; } if (!tls_verify_finished(tls, buf, len)) break; resuming = tls->session_id_size && !tls->session_id_new; if ((tls->server && !resuming) || (!tls->server && resuming)) { const char *error; tls_send_change_cipher_spec(tls); if (!tls_change_cipher_spec(tls, 1, &error)) { TLS_DISCONNECT(TLS_ALERT_INTERNAL_ERROR, 0, "change_cipher_spec: %s", error); break; } if (!tls_send_finished(tls)) break; } /* * When starting a new session on the client, the server's * certificate is now verified regardless of the key exchange * method, based on the following logic: * * - tls->ca_certs is non-NULL so tls_handle_certificate * (always called on the client) must have veritifed the * server's certificate chain to be valid and additionally * trusted by our CA. * * - the peer owns the end-entity certificate because: * either: * * * (RSA key exchange algorithm case) the correct * receival of this Finished message confirms the * possession of the master secret, it is verified by * both the successful decryption and the MAC of this * message (either should be enough) because we entered * the TLS_HANDSHAKE_WAIT_FINISHED state only after * encryption and MAC were enabled in ChangeCipherSpec. * To obtain the master secret the server must have been * able to decrypt the pre_master_secret which we had * encrypted with the public key from that certificate. * * * (ECDHE and DHE key exchange algorithms) server was * able to sign the client random together with the * ServerKeyExchange parameters using its certified key * pair. * * If we're resuming a cached session, we have authenticated * this peer before and the successful decryption of this * message confirms their identity hasn't changed. */ if (tls->cipher_suite[0]->signature && ((!tls->server && !resuming && tls->ca_certs) || (resuming && tls->session_peer_identity))) tls->peer_authenticated = true; tls_finished(tls); break; default: TLS_DISCONNECT(TLS_ALERT_UNEXPECTED_MESSAGE, 0, "Invalid message"); } } LIB_EXPORT struct l_tls *l_tls_new(bool server, l_tls_write_cb_t app_data_handler, l_tls_write_cb_t tx_handler, l_tls_ready_cb_t ready_handler, l_tls_disconnect_cb_t disconnect_handler, void *user_data) { struct l_tls *tls; if (!l_key_is_supported(L_KEY_FEATURE_CRYPTO)) return NULL; tls = l_new(struct l_tls, 1); tls->server = server; tls->rx = app_data_handler; tls->tx = tx_handler; tls->ready_handle = ready_handler; tls->disconnected = disconnect_handler; tls->user_data = user_data; tls->cipher_suite_pref_list = tls_cipher_suite_pref; tls->min_version = TLS_MIN_VERSION; tls->max_version = TLS_MAX_VERSION; tls->session_lifetime = 24 * 3600 * L_USEC_PER_SEC; /* If we're the server wait for the Client Hello already */ if (tls->server) TLS_SET_STATE(TLS_HANDSHAKE_WAIT_HELLO); else TLS_SET_STATE(TLS_HANDSHAKE_WAIT_START); return tls; } LIB_EXPORT void l_tls_free(struct l_tls *tls) { enum handshake_hash_type hash; if (unlikely(!tls)) return; if (tls->in_callback) { tls->pending_destroy = true; return; } l_tls_set_cacert(tls, NULL); l_tls_set_auth_data(tls, NULL, NULL); l_tls_set_domain_mask(tls, NULL); l_tls_set_cert_dump_path(tls, NULL); l_tls_set_session_cache(tls, NULL, NULL, 0, 0, NULL, NULL); tls_reset_handshake(tls); tls_cleanup_handshake(tls); tls_reset_cipher_spec(tls, 0); tls_reset_cipher_spec(tls, 1); if (tls->record_buf) l_free(tls->record_buf); if (tls->message_buf) l_free(tls->message_buf); for (hash = 0; hash < __HANDSHAKE_HASH_COUNT; hash++) tls_drop_handshake_hash(tls, hash); if (tls->debug_destroy) tls->debug_destroy(tls->debug_data); if (tls->cipher_suite_pref_list != tls_cipher_suite_pref) l_free(tls->cipher_suite_pref_list); l_free(tls); } LIB_EXPORT void l_tls_write(struct l_tls *tls, const uint8_t *data, size_t len) { if (unlikely(!tls->ready)) { return; } tls_tx_record(tls, TLS_CT_APPLICATION_DATA, data, len); } bool tls_handle_message(struct l_tls *tls, const uint8_t *message, int len, enum tls_content_type type, uint16_t version) { enum handshake_hash_type hash; const char *error; switch (type) { case TLS_CT_CHANGE_CIPHER_SPEC: if (len != 1 || message[0] != 0x01) { TLS_DISCONNECT(TLS_ALERT_DECODE_ERROR, 0, "ChangeCipherSpec msg decode error"); return false; } if (tls->state != TLS_HANDSHAKE_WAIT_CHANGE_CIPHER_SPEC) { TLS_DISCONNECT(TLS_ALERT_UNEXPECTED_MESSAGE, 0, "ChangeCipherSpec invalid in state %s", tls_handshake_state_to_str(tls->state)); return false; } if (!tls_change_cipher_spec(tls, 0, &error)) { TLS_DISCONNECT(TLS_ALERT_INTERNAL_ERROR, 0, "change_cipher_spec: %s", error); return false; } TLS_SET_STATE(TLS_HANDSHAKE_WAIT_FINISHED); return true; case TLS_CT_ALERT: /* Verify AlertLevel */ if (message[0] != 0x01 && message[0] != 0x02) { TLS_DISCONNECT(TLS_ALERT_DECODE_ERROR, 0, "Received bad AlertLevel %i", message[0]); return false; } /* * On a fatal alert we are obligated to respond with a * fatal alert and disconnect but also not complain if * the connection has been torn down by the peer before * we were able to send our alert. However on a non-fatal * alert (warning) we're also allowed to panic and send * a fatal alert, then disconnect, so we do that * regardless of the alert level. */ TLS_DISCONNECT(TLS_ALERT_CLOSE_NOTIFY, message[1], "Peer sent a %s Alert: %s", message[0] == 0x02 ? "Fatal" : "Warning", l_tls_alert_to_str(message[1])); return false; case TLS_CT_HANDSHAKE: /* Start hashing the handshake contents on first message */ if (tls->server && message[0] == TLS_CLIENT_HELLO && (tls->state == TLS_HANDSHAKE_WAIT_HELLO || tls->state == TLS_HANDSHAKE_DONE)) if (!tls_init_handshake_hash(tls)) return false; /* * Corner case: When handling a Certificate Verify or a * Finished message we need access to the messages hash from * before this message was transmitted on the Tx side so we * can verify it matches the hash the sender included in the * message. We save it here for that purpose. Everywhere * else we need to update the hash before handling the new * message because 1. we may need the new hash to build our * own Certificate Verify or Finished messages, and 2. we * update the message hash with newly transmitted messages * inside tls_tx_handshake which may be called as part of * handling incoming message, and if we didn't call * l_checksum_update before, the calls would end up being * out of order. */ if (message[0] == TLS_CERTIFICATE_VERIFY || message[0] == TLS_FINISHED) for (hash = 0; hash < __HANDSHAKE_HASH_COUNT; hash++) { if (!tls->handshake_hash[hash]) continue; tls_get_handshake_hash(tls, hash, tls->prev_digest[hash]); } /* * RFC 5246, Section 7.4.1.1: * This message MUST NOT be included in the message hashes * that are maintained throughout the handshake and used in * the Finished messages and the certificate verify message. */ if (message[0] != TLS_HELLO_REQUEST) for (hash = 0; hash < __HANDSHAKE_HASH_COUNT; hash++) { if (!tls->handshake_hash[hash]) continue; l_checksum_update(tls->handshake_hash[hash], message, len); } tls_handle_handshake(tls, message[0], message + TLS_HANDSHAKE_HEADER_SIZE, len - TLS_HANDSHAKE_HEADER_SIZE); if (tls->pending_destroy) { l_tls_free(tls); return false; } return true; case TLS_CT_APPLICATION_DATA: if (!tls->ready) { TLS_DISCONNECT(TLS_ALERT_UNEXPECTED_MESSAGE, 0, "Application data message before " "handshake finished"); return false; } if (!len) return true; tls->in_callback = true; tls->rx(message, len, tls->user_data); tls->in_callback = false; if (tls->pending_destroy) { l_tls_free(tls); return false; } return true; } return false; } LIB_EXPORT bool l_tls_start(struct l_tls *tls) { if (tls->max_version < tls->min_version) return false; if (!tls->cipher_suite_pref_list) return false; /* This is a nop in server mode */ if (tls->server) return true; if (tls->state != TLS_HANDSHAKE_WAIT_START) { TLS_DEBUG("Call invalid in state %s", tls_handshake_state_to_str(tls->state)); return false; } if (!tls_init_handshake_hash(tls)) return false; /* * If we're going to try resuming a cached session, send the Client * Hello with the version we think is supported. * * RFC5246 Appendix E.1: * "Whenever a client already knows the highest protocol version known * to a server (for example, when resuming a session), it SHOULD * initiate the connection in that native protocol." * * Don't directly set tls->{min,max}_version as that would make the * handshake fail if the server decides to start a new session with * a new version instead of resuming, which it is allowed to do. */ tls->client_version = tls->max_version; tls_load_cached_client_session(tls); if (tls->pending_destroy) { l_tls_free(tls); return false; } if (!tls_send_client_hello(tls)) return false; TLS_SET_STATE(TLS_HANDSHAKE_WAIT_HELLO); return true; } LIB_EXPORT void l_tls_close(struct l_tls *tls) { tls->record_buf_len = 0; tls->message_buf_len = 0; TLS_DISCONNECT(TLS_ALERT_CLOSE_NOTIFY, 0, "Closing session"); } LIB_EXPORT void l_tls_reset(struct l_tls *tls) { /* * Similar to l_tls_close but without sending the alert or a * disconnect callback. */ tls_reset_handshake(tls); tls_cleanup_handshake(tls); tls_reset_cipher_spec(tls, 0); tls_reset_cipher_spec(tls, 1); tls->negotiated_version = 0; tls->ready = false; tls->record_flush = true; tls->record_buf_len = 0; tls->message_buf_len = 0; } LIB_EXPORT bool l_tls_set_cacert(struct l_tls *tls, struct l_queue *ca_certs) { if (tls->ca_certs) { l_queue_destroy(tls->ca_certs, (l_queue_destroy_func_t) l_cert_free); tls->ca_certs = NULL; } if (ca_certs) { if (!l_key_is_supported(L_KEY_FEATURE_RESTRICT)) { TLS_DEBUG("keyctl restrict support missing, " "check kernel configuration"); return false; } tls->ca_certs = ca_certs; } return true; } LIB_EXPORT bool l_tls_set_auth_data(struct l_tls *tls, struct l_certchain *certchain, struct l_key *priv_key) { if (tls->cert) { l_certchain_free(tls->cert); tls->cert = NULL; } if (tls->priv_key) { l_key_free(tls->priv_key); tls->priv_key = NULL; tls->priv_key_size = 0; } if (certchain) tls->cert = certchain; if (priv_key) { bool is_public = true; tls->priv_key = priv_key; if (!l_key_get_info(tls->priv_key, L_KEY_RSA_PKCS1_V1_5, L_CHECKSUM_NONE, &tls->priv_key_size, &is_public) || is_public) { TLS_DEBUG("Not a private key or l_key_get_info failed"); tls->cert = NULL; tls->priv_key = NULL; tls->priv_key_size = 0; return false; } tls->priv_key_size /= 8; } return true; } bool tls_set_cipher_suites(struct l_tls *tls, const char **suite_list) { struct tls_cipher_suite **suite; if (tls->cipher_suite_pref_list != tls_cipher_suite_pref) l_free(tls->cipher_suite_pref_list); if (!suite_list) { /* Use our default cipher suite preference list */ tls->cipher_suite_pref_list = tls_cipher_suite_pref; return true; } tls->cipher_suite_pref_list = l_new(struct tls_cipher_suite *, l_strv_length((char **) suite_list) + 1); suite = tls->cipher_suite_pref_list; for (; *suite_list; suite_list++) { unsigned int i; for (i = 0; tls_cipher_suite_pref[i]; i++) if (!strcmp(tls_cipher_suite_pref[i]->name, *suite_list)) break; if (tls_cipher_suite_pref[i]) *suite++ = tls_cipher_suite_pref[i]; else TLS_DEBUG("Cipher suite %s is not supported", *suite_list); } if (suite > tls->cipher_suite_pref_list) return true; TLS_DEBUG("None of the supplied suite names is supported"); l_free(suite); tls->cipher_suite_pref_list = NULL; return false; } LIB_EXPORT void l_tls_set_version_range(struct l_tls *tls, enum l_tls_version min_version, enum l_tls_version max_version) { tls->min_version = (min_version && min_version > TLS_MIN_VERSION) ? min_version : TLS_MIN_VERSION; tls->max_version = (max_version && max_version < TLS_MAX_VERSION) ? max_version : TLS_MAX_VERSION; } /** * l_tls_set_domain_mask: * @tls: TLS object being configured * @mask: NULL-terminated array of domain masks * * Sets a mask for domain names contained in the peer certificate * (eg. the subject Common Name) to be matched against. If none of the * domains match the any mask, authentication will fail. At least one * domain has to match at least one mask from the list. * * The masks are each split into segments at the dot characters and each * segment must match the corresponding label of the domain name -- * a domain name is a sequence of labels joined by dots. An asterisk * segment in the mask matches any label. An asterisk segment at the * beginning of the mask matches one or more consecutive labels from * the beginning of the domain string. */ LIB_EXPORT void l_tls_set_domain_mask(struct l_tls *tls, char **mask) { l_strv_free(tls->subject_mask); tls->subject_mask = l_strv_copy(mask); } /** * l_tls_set_session_cache: * @tls: TLS object being configured * @settings: l_settings object to read and write session data from/to or * NULL to disable caching session states. The object must remain valid * until this method is called with a different value. * @group_prefix: prefix to build group names inside @settings. Note: * existing settings in groups starting with the prefix may be * overwritten or removed. * @lifetime: a CLOCK_REALTIME-based microsecond resolution lifetime for * cached sessions. The RFC recommends 24 hours. * @max_sessions: limit on the number of sessions in the cache, or 0 for * unlimited. Ignored in client mode. * @update_cb: a callback to be invoked whenever the settings in @settings * have been updated and may need to be written to persistent storage if * desired, or NULL. * @user_data: user data pointer to pass to @update_cb. * * Enables caching and resuming session states as described in RFC 5246 for * faster setup. l_tls will maintain the required session state data in * @settings including removing expired or erroneous sessions. * * A client's cache contains at most one session state since the client * must request one specific Session ID from the server when resuming a * session. The resumption will only work while the state is cached by * both the server and the client so clients should keep separate @settings * objects or use separate groups inside one object for every discrete * server they may want to connect to. * * Multiple l_tls clients connecting to the same server can share one cache * to allow reusing an established session that is still active (actual * concurrency is not supported as there's no locking.) */ LIB_EXPORT void l_tls_set_session_cache(struct l_tls *tls, struct l_settings *settings, const char *group_prefix, uint64_t lifetime, unsigned int max_sessions, l_tls_session_update_cb_t update_cb, void *user_data) { if (unlikely(!tls)) return; tls->session_settings = settings; tls->session_lifetime = lifetime; tls->session_count_max = max_sessions; tls->session_update_cb = update_cb; tls->session_update_user_data = user_data; l_free(tls->session_prefix); tls->session_prefix = l_strdup(group_prefix); } LIB_EXPORT bool l_tls_get_session_resumed(struct l_tls *tls) { if (unlikely(!tls || !tls->ready)) return false; return tls->session_resumed; } LIB_EXPORT const char *l_tls_alert_to_str(enum l_tls_alert_desc desc) { switch (desc) { case TLS_ALERT_CLOSE_NOTIFY: return "close_notify"; case TLS_ALERT_UNEXPECTED_MESSAGE: return "unexpected_message"; case TLS_ALERT_BAD_RECORD_MAC: return "bad_record_mac"; case TLS_ALERT_DECRYPT_FAIL_RESERVED: return "decryption_failure_RESERVED"; case TLS_ALERT_RECORD_OVERFLOW: return "record_overflow"; case TLS_ALERT_DECOMPRESS_FAIL: return "decompression_failure"; case TLS_ALERT_HANDSHAKE_FAIL: return "handshake_failure"; case TLS_ALERT_NO_CERT_RESERVED: return "no_certificate_RESERVED"; case TLS_ALERT_BAD_CERT: return "bad_certificate"; case TLS_ALERT_UNSUPPORTED_CERT: return "unsupported_certificate"; case TLS_ALERT_CERT_REVOKED: return "certificate_revoked"; case TLS_ALERT_CERT_EXPIRED: return "certificate_expired"; case TLS_ALERT_CERT_UNKNOWN: return "certificate_unknown"; case TLS_ALERT_ILLEGAL_PARAM: return "illegal_parameter"; case TLS_ALERT_UNKNOWN_CA: return "unknown_ca"; case TLS_ALERT_ACCESS_DENIED: return "access_denied"; case TLS_ALERT_DECODE_ERROR: return "decode_error"; case TLS_ALERT_DECRYPT_ERROR: return "decrypt_error"; case TLS_ALERT_EXPORT_RES_RESERVED: return "export_restriction_RESERVED"; case TLS_ALERT_PROTOCOL_VERSION: return "protocol_version"; case TLS_ALERT_INSUFFICIENT_SECURITY: return "insufficient_security"; case TLS_ALERT_INTERNAL_ERROR: return "internal_error"; case TLS_ALERT_USER_CANCELED: return "user_canceled"; case TLS_ALERT_NO_RENEGOTIATION: return "no_renegotiation"; case TLS_ALERT_UNSUPPORTED_EXTENSION: return "unsupported_extension"; } return NULL; } const char *tls_handshake_state_to_str(enum tls_handshake_state state) { static char buf[100]; switch (state) { SWITCH_ENUM_TO_STR(TLS_HANDSHAKE_WAIT_START) SWITCH_ENUM_TO_STR(TLS_HANDSHAKE_WAIT_HELLO) SWITCH_ENUM_TO_STR(TLS_HANDSHAKE_WAIT_CERTIFICATE) SWITCH_ENUM_TO_STR(TLS_HANDSHAKE_WAIT_KEY_EXCHANGE) SWITCH_ENUM_TO_STR(TLS_HANDSHAKE_WAIT_HELLO_DONE) SWITCH_ENUM_TO_STR(TLS_HANDSHAKE_WAIT_CERTIFICATE_VERIFY) SWITCH_ENUM_TO_STR(TLS_HANDSHAKE_WAIT_CHANGE_CIPHER_SPEC) SWITCH_ENUM_TO_STR(TLS_HANDSHAKE_WAIT_FINISHED) SWITCH_ENUM_TO_STR(TLS_HANDSHAKE_DONE) } snprintf(buf, sizeof(buf), "tls_handshake_state(%i)", state); return buf; } int tls_parse_certificate_list(const void *data, size_t len, struct l_certchain **out_certchain) { const uint8_t *buf = data; struct l_certchain *chain = NULL; while (len) { struct l_cert *cert; size_t cert_len; if (len < 3) goto decode_error; cert_len = *buf++ << 16; cert_len |= *buf++ << 8; cert_len |= *buf++ << 0; if (cert_len + 3 > len) goto decode_error; cert = l_cert_new_from_der(buf, cert_len); if (!cert) goto decode_error; if (!chain) { chain = certchain_new_from_leaf(cert); if (!chain) goto decode_error; } else certchain_link_issuer(chain, cert); buf += cert_len; len -= cert_len + 3; } if (out_certchain) *out_certchain = chain; else l_certchain_free(chain); return 0; decode_error: l_certchain_free(chain); return -EBADMSG; } LIB_EXPORT bool l_tls_set_debug(struct l_tls *tls, l_tls_debug_cb_t function, void *user_data, l_tls_destroy_cb_t destroy) { if (unlikely(!tls)) return false; if (tls->debug_destroy) tls->debug_destroy(tls->debug_data); tls->debug_handler = function; tls->debug_destroy = destroy; tls->debug_data = user_data; return true; } LIB_EXPORT bool l_tls_set_cert_dump_path(struct l_tls *tls, const char *path) { l_free(tls->cert_dump_path); tls->cert_dump_path = path ? l_strdup(path) : NULL; return true; } bluez-5.82/ell/PaxHeaders/gvariant-private.h0000644000000000000000000000005014504767710016071 xustar0020 atime=1743575504 20 ctime=1743591277 bluez-5.82/ell/gvariant-private.h0000644000000000000000000000442314504767710015555 0ustar00rootroot/* * Embedded Linux library * Copyright (C) 2011-2014 Intel Corporation * * SPDX-License-Identifier: LGPL-2.1-or-later */ struct l_dbus_message_iter; struct dbus_builder; bool _gvariant_iter_init(struct l_dbus_message_iter *iter, struct l_dbus_message *message, const char *sig_start, const char *sig_end, const void *data, size_t len); bool _gvariant_iter_next_entry_basic(struct l_dbus_message_iter *iter, char type, void *out_p); bool _gvariant_iter_enter_struct(struct l_dbus_message_iter *iter, struct l_dbus_message_iter *structure); bool _gvariant_iter_enter_variant(struct l_dbus_message_iter *iter, struct l_dbus_message_iter *variant); bool _gvariant_iter_enter_array(struct l_dbus_message_iter *iter, struct l_dbus_message_iter *array); bool _gvariant_iter_skip_entry(struct l_dbus_message_iter *iter); bool _gvariant_valid_signature(const char *sig); int _gvariant_get_alignment(const char *signature); bool _gvariant_is_fixed_size(const char *signature); int _gvariant_get_fixed_size(const char *signature); int _gvariant_num_children(const char *sig); struct dbus_builder *_gvariant_builder_new(void *body, size_t body_size); void _gvariant_builder_free(struct dbus_builder *builder); bool _gvariant_builder_append_basic(struct dbus_builder *builder, char type, const void *value); bool _gvariant_builder_mark(struct dbus_builder *builder); bool _gvariant_builder_rewind(struct dbus_builder *builder); char *_gvariant_builder_finish(struct dbus_builder *builder, void **body, size_t *body_size); bool _gvariant_builder_enter_struct(struct dbus_builder *builder, const char *signature); bool _gvariant_builder_leave_struct(struct dbus_builder *builder); bool _gvariant_builder_enter_dict(struct dbus_builder *builder, const char *signature); bool _gvariant_builder_leave_dict(struct dbus_builder *builder); bool _gvariant_builder_enter_variant(struct dbus_builder *builder, const char *signature); bool _gvariant_builder_leave_variant(struct dbus_builder *builder); bool _gvariant_builder_enter_array(struct dbus_builder *builder, const char *signature); bool _gvariant_builder_leave_array(struct dbus_builder *builder); size_t _gvariant_message_finalize(size_t header_end, void *body, size_t body_size, const char *signature); bluez-5.82/ell/PaxHeaders/pem-private.h0000644000000000000000000000005014504767710015037 xustar0020 atime=1743575449 20 ctime=1743591276 bluez-5.82/ell/pem-private.h0000644000000000000000000000163214504767710014522 0ustar00rootroot/* * Embedded Linux library * Copyright (C) 2019 Intel Corporation * * SPDX-License-Identifier: LGPL-2.1-or-later */ #define _GNU_SOURCE #include #include struct l_certchain; struct pem_file_info { int fd; struct stat st; uint8_t *data; }; int pem_file_open(struct pem_file_info *info, const char *filename); void pem_file_close(struct pem_file_info *info); const char *pem_next(const void *buf, size_t buf_len, char **type_label, size_t *base64_len, const char **endp, bool strict); uint8_t *pem_load_buffer(const void *buf, size_t buf_len, char **out_type_label, size_t *out_len, char **out_headers, const char **out_endp); struct l_key *pem_load_private_key(uint8_t *content, size_t len, char *label, const char *passphrase, char *headers, bool *encrypted); int pem_write_certificate_chain(const struct l_certchain *cert, const char *filename); bluez-5.82/ell/PaxHeaders/gvariant-util.c0000644000000000000000000000005014601374755015370 xustar0020 atime=1743575518 20 ctime=1743591277 bluez-5.82/ell/gvariant-util.c0000644000000000000000000007170714601374755015065 0ustar00rootroot/* * Embedded Linux library * Copyright (C) 2011-2014 Intel Corporation * * SPDX-License-Identifier: LGPL-2.1-or-later */ #ifdef HAVE_CONFIG_H #include #endif #define _GNU_SOURCE #include #include #include #include #include #include "private.h" #include "useful.h" #include "util.h" #include "queue.h" #include "string.h" #include "log.h" #include "dbus.h" #include "dbus-private.h" #include "gvariant-private.h" static const char *simple_types = "sogybnqiuxtdh"; static const char *variable_types = "sogav"; static const char *fixed_types = "bynqhiuxtd"; /* * The alignment of a container type is equal to the largest alignment of * any potential child of that container. This means that, even if an array * of 32-bit integers is empty, it still must be aligned to the nearest * multiple of 4 bytes. It also means that the variant type (described below) * has an alignment of 8 (since it could potentially contain a value of any * other type and the maximum alignment is 8). */ static int get_basic_alignment(const char type) { switch (type) { case 'b': return 1; case 'y': return 1; case 'n': case 'q': return 2; case 'i': case 'u': return 4; case 'x': case 't': case 'd': return 8; case 's': case 'g': case 'o': return 1; case 'h': return 4; case 'v': return 8; default: return 0; } } static int get_basic_fixed_size(const char type) { switch (type) { case 'b': return 1; case 'y': return 1; case 'n': case 'q': return 2; case 'i': case 'u': return 4; case 'x': case 't': case 'd': return 8; case 'h': return 4; default: return 0; } } static const char *validate_next_type(const char *sig, int *out_alignment) { char s = *sig; int alignment; if (s == '\0') return NULL; if (strchr(simple_types, s) || s == 'v') { *out_alignment = get_basic_alignment(s); return sig + 1; } switch (s) { case 'a': return validate_next_type(++sig, out_alignment); case '{': s = *++sig; /* Dictionary keys can only be simple types */ if (!strchr(simple_types, s)) return NULL; alignment = get_basic_alignment(s); sig = validate_next_type(sig + 1, out_alignment); if (!sig) return NULL; if (*sig != '}') return NULL; if (alignment > *out_alignment) *out_alignment = alignment; return sig + 1; case '(': { int max_alignment = 1, alignment; sig++; while (sig && *sig != ')') { sig = validate_next_type(sig, &alignment); if (alignment > max_alignment) max_alignment = alignment; } if (!sig) return NULL; if (*sig != ')') return NULL; *out_alignment = max_alignment; return sig + 1; } } return NULL; } bool _gvariant_valid_signature(const char *sig) { const char *s = sig; int a; if (strlen(sig) > 255) return false; do { s = validate_next_type(s, &a); if (!s) return false; } while (*s); return true; } int _gvariant_num_children(const char *sig) { const char *s = sig; int a; int num_children = 0; if (strlen(sig) > 255) return false; do { s = validate_next_type(s, &a); if (!s) return -1; num_children += 1; } while (*s); return num_children; } int _gvariant_get_alignment(const char *sig) { int max_alignment = 1, alignment; const char *s = sig; /* 8 is the largest alignment possible, so quit if we reach it */ while (*s && max_alignment != 8) { s = validate_next_type(s, &alignment); if (!s) return 0; if (alignment > max_alignment) max_alignment = alignment; } return max_alignment; } bool _gvariant_is_fixed_size(const char *sig) { while (*sig != 0) { if (strchr(variable_types, sig[0])) return false; sig += 1; } return true; } int _gvariant_get_fixed_size(const char *sig) { const char *s = sig; const char *p; int size = 0; int alignment; int max_alignment = 1; int r; while (*s) { if (strchr(variable_types, *s)) return 0; if (strchr(fixed_types, *s)) { alignment = get_basic_alignment(*s); if (alignment > max_alignment) max_alignment = alignment; size = align_len(size, alignment); size += get_basic_fixed_size(*s); s++; continue; } if (*s == '}' || *s == ')') break; p = validate_next_type(s, &alignment); if (!p) return 0; if (alignment > max_alignment) max_alignment = alignment; size = align_len(size, alignment); /* Handle special case of unit type */ if (s[0] == '(' && s[1] == ')') r = 1; else r = _gvariant_get_fixed_size(s + 1); if (r == 0) return 0; size += r; s = p; } size = align_len(size, max_alignment); return size; } static inline size_t offset_length(size_t size, size_t n_offsets) { if (size + n_offsets <= 0xff) return 1; if (size + n_offsets * 2 <= 0xffff) return 2; if (size + n_offsets * 4 <= 0xffffffff) return 4; return 8; } static inline size_t read_word_le(const void *p, size_t sz) { union { uint16_t u16; uint32_t u32; uint64_t u64; } x; if (sz == 1) return *(uint8_t *) p; memcpy(&x, p, sz); if (sz == 2) return le16toh(x.u16); if (sz == 4) return le32toh(x.u32); return le64toh(x.u64); } static inline void write_word_le(void *p, size_t value, size_t sz) { union { uint16_t u16; uint32_t u32; uint64_t u64; } x; if (sz == 1) { *(uint8_t *) p = value; return; } if (sz == 2) x.u16 = htole16((uint16_t) value); else if (sz == 4) x.u32 = htole32((uint32_t) value); else x.u64 = htole64((uint64_t) value); memcpy(p, &x, sz); } static bool gvariant_iter_init_internal(struct l_dbus_message_iter *iter, struct l_dbus_message *message, enum dbus_container_type type, const char *sig_start, const char *sig_end, const void *data, size_t len) { const char *p; int i; int v; char subsig[256]; unsigned int num_variable = 0; unsigned int offset_len = offset_length(len, 0); size_t last_offset; struct gvariant_type_info { uint8_t sig_start; uint8_t sig_end; bool fixed_size : 1; unsigned int alignment : 4; size_t end; /* Index past the end of the type */ } *children; int n_children; if (sig_end) { size_t len = sig_end - sig_start; memcpy(subsig, sig_start, len); subsig[len] = '\0'; } else strcpy(subsig, sig_start); iter->message = message; iter->sig_start = sig_start; iter->sig_len = strlen(subsig); iter->sig_pos = 0; iter->data = data; iter->len = len; iter->pos = 0; if (subsig[0] != '\0') { n_children = _gvariant_num_children(subsig); if (n_children < 0) return false; children = l_new(struct gvariant_type_info, n_children); } else { n_children = 0; children = NULL; } for (p = sig_start, i = 0; i < n_children; i++) { int alignment; size_t size; size_t len; children[i].sig_start = p - sig_start; p = validate_next_type(p, &alignment); children[i].sig_end = p - sig_start; len = children[i].sig_end - children[i].sig_start; memcpy(subsig, sig_start + children[i].sig_start, len); subsig[len] = '\0'; children[i].alignment = alignment; children[i].fixed_size = _gvariant_is_fixed_size(subsig); if (children[i].fixed_size) { size = _gvariant_get_fixed_size(subsig); children[i].end = size; } else if (i + 1 < n_children) num_variable += 1; } if (len < num_variable * offset_len) goto fail; last_offset = len - num_variable * offset_len; if (num_variable > 0) iter->offsets = iter->data + len - offset_len; else iter->offsets = NULL; for (i = 0, v = 0; i < n_children; i++) { size_t o; if (children[i].fixed_size) { if (i == 0) continue; o = align_len(children[i-1].end, children[i].alignment); children[i].end += o; if (children[i].end > len) goto fail; continue; } if (num_variable == 0) { children[i].end = last_offset; continue; } v += 1; children[i].end = read_word_le(data + len - offset_len * v, offset_len); num_variable -= 1; if (children[i].end > len) goto fail; } iter->container_type = type; if (type == DBUS_CONTAINER_TYPE_ARRAY && !children[0].fixed_size) { size_t offset = read_word_le(iter->data + iter->len - offset_len, offset_len); iter->offsets = iter->data + offset; } l_free(children); return true; fail: l_free(children); return false; } bool _gvariant_iter_init(struct l_dbus_message_iter *iter, struct l_dbus_message *message, const char *sig_start, const char *sig_end, const void *data, size_t len) { return gvariant_iter_init_internal(iter, message, DBUS_CONTAINER_TYPE_STRUCT, sig_start, sig_end, data, len); } static const void *next_item(struct l_dbus_message_iter *iter, size_t *out_item_size) { const void *start; const char *p; char sig[256]; int alignment; bool fixed_size; bool last_member; unsigned int sig_len; unsigned int offset_len; memcpy(sig, iter->sig_start + iter->sig_pos, iter->sig_len - iter->sig_pos); sig[iter->sig_len - iter->sig_pos] = '\0'; /* * Find the next type and make a note whether it is the last in the * structure. Arrays will always have a single complete type, so * last_member will always be true. */ p = validate_next_type(sig, &alignment); if (!p) return NULL; sig_len = p - sig; last_member = *p == '\0'; sig[sig_len] = '\0'; fixed_size = _gvariant_is_fixed_size(sig); if (iter->container_type != DBUS_CONTAINER_TYPE_ARRAY) iter->sig_pos += sig_len; iter->pos = align_len(iter->pos, alignment); if (fixed_size) { *out_item_size = _gvariant_get_fixed_size(sig); goto done; } if (iter->container_type != DBUS_CONTAINER_TYPE_ARRAY && last_member) { unsigned int len = iter->len; offset_len = offset_length(iter->len, 0); if (iter->offsets && iter->offsets + offset_len < iter->data + len) len = iter->offsets + offset_len - iter->data; *out_item_size = len - iter->pos; goto done; } if (iter->offsets >= iter->data + iter->len) return NULL; offset_len = offset_length(iter->len, 0); *out_item_size = read_word_le(iter->offsets, offset_len) - iter->pos; /* In structures the offsets are in reverse order */ if (iter->container_type == DBUS_CONTAINER_TYPE_ARRAY) iter->offsets += offset_len; else iter->offsets -= offset_len; done: start = iter->data + iter->pos; if (start >= iter->data + iter->len) return NULL; iter->pos += *out_item_size; return start; } bool _gvariant_iter_next_entry_basic(struct l_dbus_message_iter *iter, char type, void *out) { size_t item_size = 0; const void *start; uint8_t uint8_val; uint16_t uint16_val; uint32_t uint32_val; uint64_t uint64_val; int16_t int16_val; int32_t int32_val; int64_t int64_val; if (iter->pos >= iter->len) return false; if (iter->sig_start[iter->sig_pos] != type) return false; start = next_item(iter, &item_size); if (!start) return false; switch (type) { case 'o': case 's': case 'g': { const void *end = memchr(start, 0, item_size); if (!end) return false; *(const char**) out = start; break; } case 'b': uint8_val = l_get_u8(start); *(bool *) out = !!uint8_val; break; case 'y': uint8_val = l_get_u8(start); *(uint8_t *) out = uint8_val; break; case 'n': int16_val = l_get_s16(start); *(int16_t *) out = int16_val; break; case 'q': uint16_val = l_get_u16(start); *(uint16_t *) out = uint16_val; break; case 'i': int32_val = l_get_s32(start); *(int32_t *) out = int32_val; break; case 'h': case 'u': uint32_val = l_get_u32(start); *(uint32_t *) out = uint32_val; break; case 'x': int64_val = l_get_s64(start); *(int64_t *) out = int64_val; break; case 't': uint64_val = l_get_u64(start); *(uint64_t *) out = uint64_val; break; case 'd': uint64_val = l_get_u64(start); *(uint64_t *) out = uint64_val; break; } return true; } bool _gvariant_iter_enter_struct(struct l_dbus_message_iter *iter, struct l_dbus_message_iter *structure) { bool is_dict = iter->sig_start[iter->sig_pos] == '{'; bool is_struct = iter->sig_start[iter->sig_pos] == '('; const char *sig_start = iter->sig_start + iter->sig_pos + 1; const char *sig_end; const void *start; size_t item_size; enum dbus_container_type type; if (!is_dict && !is_struct) return false; start = next_item(iter, &item_size); if (!start) return false; /* For ARRAY containers the sig_pos is never incremented */ if (iter->container_type == DBUS_CONTAINER_TYPE_ARRAY) sig_end = iter->sig_start + iter->sig_len - 1; else sig_end = iter->sig_start + iter->sig_pos - 1; type = is_dict ? DBUS_CONTAINER_TYPE_DICT_ENTRY : DBUS_CONTAINER_TYPE_STRUCT; return gvariant_iter_init_internal(structure, iter->message, type, sig_start, sig_end, start, item_size); } bool _gvariant_iter_enter_variant(struct l_dbus_message_iter *iter, struct l_dbus_message_iter *variant) { size_t item_size; const void *start, *end, *nul; char signature[256]; if (iter->sig_start[iter->sig_pos] != 'v') return false; start = next_item(iter, &item_size); if (!start) return false; /* Find the signature */ end = start + item_size; nul = memrchr(start, 0, end - start); if (!nul) return false; if (end - nul - 1 > 255) return false; memcpy(signature, nul + 1, end - nul - 1); signature[end - nul - 1] = '\0'; if (_gvariant_num_children(signature) != 1) return false; return gvariant_iter_init_internal(variant, iter->message, DBUS_CONTAINER_TYPE_VARIANT, nul + 1, end, start, nul - start); } bool _gvariant_iter_enter_array(struct l_dbus_message_iter *iter, struct l_dbus_message_iter *array) { const char *sig_start; const char *sig_end; size_t item_size; const void *start; if (iter->sig_start[iter->sig_pos] != 'a') return false; sig_start = iter->sig_start + iter->sig_pos + 1; start = next_item(iter, &item_size); if (!start) return false; /* For ARRAY containers the sig_pos is never incremented */ if (iter->container_type == DBUS_CONTAINER_TYPE_ARRAY) sig_end = iter->sig_start + iter->sig_len; else sig_end = iter->sig_start + iter->sig_pos; return gvariant_iter_init_internal(array, iter->message, DBUS_CONTAINER_TYPE_ARRAY, sig_start, sig_end, start, item_size); } bool _gvariant_iter_skip_entry(struct l_dbus_message_iter *iter) { size_t size; if (!next_item(iter, &size)) return false; return true; } struct dbus_builder { struct l_string *signature; void *body; size_t body_size; size_t body_pos; struct l_queue *containers; struct { struct container *container; int sig_end; size_t body_pos; size_t offset_index; bool variable_is_last : 1; } mark; }; struct container { size_t *offsets; size_t offsets_size; size_t offset_index; size_t start; bool variable_is_last : 1; enum dbus_container_type type; char signature[256]; uint8_t sigindex; }; static inline size_t grow_body(struct dbus_builder *builder, size_t len, unsigned int alignment) { size_t size = align_len(builder->body_pos, alignment); if (size + len > builder->body_size) { builder->body = l_realloc(builder->body, size + len); builder->body_size = size + len; } if (size - builder->body_pos > 0) memset(builder->body + builder->body_pos, 0, size - builder->body_pos); builder->body_pos = size + len; return size; } static inline bool grow_offsets(struct container *container) { size_t needed; if (container->offset_index < container->offsets_size) return true; needed = container->offsets_size * 2; if (needed > USHRT_MAX) return false; if (needed == 0) needed = 8; container->offsets = l_realloc(container->offsets, needed * sizeof(size_t)); container->offsets_size = needed; return true; } static struct container *container_new(enum dbus_container_type type, const char *signature, size_t start) { struct container *ret; ret = l_new(struct container, 1); ret->type = type; strcpy(ret->signature, signature); ret->start = start; return ret; } static void container_free(struct container *container) { l_free(container->offsets); l_free(container); } static void container_append_struct_offsets(struct container *container, struct dbus_builder *builder) { size_t offset_size; int i; size_t start; if (container->variable_is_last) container->offset_index -= 1; if (container->offset_index == 0) return; offset_size = offset_length(builder->body_pos, container->offset_index); start = grow_body(builder, offset_size * container->offset_index, 1); for (i = container->offset_index - 1; i >= 0; i--) { write_word_le(builder->body + start, container->offsets[i], offset_size); start += offset_size; } } static void container_append_array_offsets(struct container *container, struct dbus_builder *builder) { size_t offset_size; unsigned int i; size_t start; if (container->offset_index == 0) return; offset_size = offset_length(builder->body_pos, container->offset_index); start = grow_body(builder, offset_size * container->offset_index, 1); for (i = 0; i < container->offset_index; i++) { write_word_le(builder->body + start, container->offsets[i], offset_size); start += offset_size; } } struct dbus_builder *_gvariant_builder_new(void *body, size_t body_size) { struct dbus_builder *builder; struct container *root; builder = l_new(struct dbus_builder, 1); builder->signature = l_string_new(63); builder->containers = l_queue_new(); root = container_new(DBUS_CONTAINER_TYPE_STRUCT, "", 0); l_queue_push_head(builder->containers, root); builder->body = body; builder->body_size = body_size; builder->body_pos = body_size; builder->mark.container = root; return builder; } void _gvariant_builder_free(struct dbus_builder *builder) { if (unlikely(!builder)) return; l_string_free(builder->signature); l_queue_destroy(builder->containers, (l_queue_destroy_func_t) container_free); l_free(builder->body); l_free(builder); } static bool enter_struct_dict_common(struct dbus_builder *builder, const char *signature, enum dbus_container_type type, const char open, const char close) { size_t qlen = l_queue_length(builder->containers); struct container *container = l_queue_peek_head(builder->containers); int alignment; size_t start; if (qlen == 1) { if (l_string_length(builder->signature) + strlen(signature) + 2 > 255) return false; } else { /* Verify Signatures Match */ char expect[256]; const char *start; const char *end; start = container->signature + container->sigindex; end = validate_next_type(start, &alignment) - 1; if (*start != open || *end != close) return false; memcpy(expect, start + 1, end - start - 1); expect[end - start - 1] = '\0'; if (strcmp(expect, signature)) return false; } alignment = _gvariant_get_alignment(signature); start = grow_body(builder, 0, alignment); container = container_new(type, signature, start); l_queue_push_head(builder->containers, container); return true; } bool _gvariant_builder_enter_struct(struct dbus_builder *builder, const char *signature) { if (signature[0] && !_gvariant_valid_signature(signature)) return false; return enter_struct_dict_common(builder, signature, DBUS_CONTAINER_TYPE_STRUCT, '(', ')'); } bool _gvariant_builder_enter_dict(struct dbus_builder *builder, const char *signature) { if (_gvariant_num_children(signature) != 2) return false; if (!strchr(simple_types, signature[0])) return false; return enter_struct_dict_common(builder, signature, DBUS_CONTAINER_TYPE_DICT_ENTRY, '{', '}'); } static bool leave_struct_dict_common(struct dbus_builder *builder, enum dbus_container_type type, const char open, const char close) { struct container *container = l_queue_peek_head(builder->containers); size_t qlen = l_queue_length(builder->containers); struct container *parent; if (unlikely(qlen <= 1)) return false; if (unlikely(container->type != type)) return false; l_queue_pop_head(builder->containers); qlen -= 1; parent = l_queue_peek_head(builder->containers); if (_gvariant_is_fixed_size(container->signature)) { int alignment = _gvariant_get_alignment(container->signature); grow_body(builder, 0, alignment); /* Empty struct or "unit type" is encoded as a zero byte */ if (container->signature[0] == '\0') { size_t start = grow_body(builder, 1, 1); memset(builder->body + start, 0, 1); } parent->variable_is_last = false; } else { size_t offset; if (!grow_offsets(parent)) return false; container_append_struct_offsets(container, builder); offset = builder->body_pos - parent->start; parent->offsets[parent->offset_index++] = offset; parent->variable_is_last = true; } if (qlen == 1) l_string_append_printf(builder->signature, "%c%s%c", open, container->signature, close); else if (parent->type != DBUS_CONTAINER_TYPE_ARRAY) parent->sigindex += strlen(container->signature) + 2; container_free(container); return true; } bool _gvariant_builder_leave_struct(struct dbus_builder *builder) { return leave_struct_dict_common(builder, DBUS_CONTAINER_TYPE_STRUCT, '(', ')'); } bool _gvariant_builder_leave_dict(struct dbus_builder *builder) { return leave_struct_dict_common(builder, DBUS_CONTAINER_TYPE_DICT_ENTRY, '{', '}'); } bool _gvariant_builder_enter_variant(struct dbus_builder *builder, const char *signature) { size_t qlen = l_queue_length(builder->containers); struct container *container = l_queue_peek_head(builder->containers); size_t start; if (_gvariant_num_children(signature) != 1) return false; if (qlen == 1) { if (l_string_length(builder->signature) + 1 > 255) return false; } else if (container->signature[container->sigindex] != 'v') return false; start = grow_body(builder, 0, 8); container = container_new(DBUS_CONTAINER_TYPE_VARIANT, signature, start); l_queue_push_head(builder->containers, container); return true; } bool _gvariant_builder_leave_variant(struct dbus_builder *builder) { struct container *container = l_queue_peek_head(builder->containers); size_t qlen = l_queue_length(builder->containers); struct container *parent; size_t start; size_t siglen; size_t offset; if (unlikely(qlen <= 1)) return false; if (unlikely(container->type != DBUS_CONTAINER_TYPE_VARIANT)) return false; l_queue_pop_head(builder->containers); qlen -= 1; parent = l_queue_peek_head(builder->containers); siglen = strlen(container->signature); start = grow_body(builder, siglen + 1, 1); memset(builder->body + start, 0, 1); memcpy(builder->body + start + 1, container->signature, siglen); if (!grow_offsets(parent)) return false; offset = builder->body_pos - parent->start; parent->offsets[parent->offset_index++] = offset; parent->variable_is_last = true; if (qlen == 1) l_string_append_c(builder->signature, 'v'); else if (parent->type != DBUS_CONTAINER_TYPE_ARRAY) parent->sigindex += 1; container_free(container); return true; } bool _gvariant_builder_enter_array(struct dbus_builder *builder, const char *signature) { size_t qlen = l_queue_length(builder->containers); struct container *container = l_queue_peek_head(builder->containers); size_t start; int alignment; if (_gvariant_num_children(signature) != 1) return false; if (qlen == 1) { if (l_string_length(builder->signature) + strlen(signature) + 1 > 255) return false; } else { /* Verify Signatures Match */ char expect[256]; const char *start; const char *end; start = container->signature + container->sigindex; end = validate_next_type(start, &alignment); if (*start != 'a') return false; memcpy(expect, start + 1, end - start - 1); expect[end - start - 1] = '\0'; if (strcmp(expect, signature)) return false; } alignment = _gvariant_get_alignment(signature); start = grow_body(builder, 0, alignment); container = container_new(DBUS_CONTAINER_TYPE_ARRAY, signature, start); l_queue_push_head(builder->containers, container); return true; } bool _gvariant_builder_leave_array(struct dbus_builder *builder) { struct container *container = l_queue_peek_head(builder->containers); size_t qlen = l_queue_length(builder->containers); struct container *parent; size_t offset; if (unlikely(qlen <= 1)) return false; if (unlikely(container->type != DBUS_CONTAINER_TYPE_ARRAY)) return false; l_queue_pop_head(builder->containers); qlen -= 1; parent = l_queue_peek_head(builder->containers); if (!_gvariant_is_fixed_size(container->signature)) container_append_array_offsets(container, builder); if (!grow_offsets(parent)) return false; offset = builder->body_pos - parent->start; parent->offsets[parent->offset_index++] = offset; parent->variable_is_last = true; if (qlen == 1) l_string_append_printf(builder->signature, "a%s", container->signature); else if (parent->type != DBUS_CONTAINER_TYPE_ARRAY) parent->sigindex += strlen(container->signature) + 1; container_free(container); return true; } bool _gvariant_builder_append_basic(struct dbus_builder *builder, char type, const void *value) { struct container *container = l_queue_peek_head(builder->containers); size_t start; unsigned int alignment; size_t len; size_t offset; if (unlikely(!builder)) return false; if (unlikely(!strchr(simple_types, type))) return false; alignment = get_basic_alignment(type); if (!alignment) return false; if (l_queue_length(builder->containers) == 1) l_string_append_c(builder->signature, type); else if (container->signature[container->sigindex] != type) return false; len = get_basic_fixed_size(type); if (len) { start = grow_body(builder, len, alignment); memcpy(builder->body + start, value, len); container->variable_is_last = false; if (container->type != DBUS_CONTAINER_TYPE_ARRAY) container->sigindex += 1; return true; } if (!grow_offsets(container)) return false; len = strlen(value) + 1; start = grow_body(builder, len, alignment); memcpy(builder->body + start, value, len); offset = builder->body_pos - container->start; container->offsets[container->offset_index++] = offset; container->variable_is_last = true; if (container->type != DBUS_CONTAINER_TYPE_ARRAY) container->sigindex += 1; return true; } bool _gvariant_builder_mark(struct dbus_builder *builder) { struct container *container = l_queue_peek_head(builder->containers); builder->mark.container = container; if (l_queue_length(builder->containers) == 1) builder->mark.sig_end = l_string_length(builder->signature); else builder->mark.sig_end = container->sigindex; builder->mark.body_pos = builder->body_pos; builder->mark.offset_index = container->offset_index; builder->mark.variable_is_last = container->variable_is_last; return true; } bool _gvariant_builder_rewind(struct dbus_builder *builder) { struct container *container; while ((container = l_queue_peek_head(builder->containers)) != builder->mark.container) { container_free(container); l_queue_pop_head(builder->containers); } builder->body_pos = builder->mark.body_pos; container->offset_index = builder->mark.offset_index; container->variable_is_last = builder->mark.variable_is_last; if (l_queue_length(builder->containers) == 1) l_string_truncate(builder->signature, builder->mark.sig_end); else container->sigindex = builder->mark.sig_end; return true; } char *_gvariant_builder_finish(struct dbus_builder *builder, void **body, size_t *body_size) { char *signature; struct container *root; uint8_t *variant_buf; size_t size; if (unlikely(!builder)) return NULL; if (unlikely(l_queue_length(builder->containers) != 1)) return NULL; root = l_queue_peek_head(builder->containers); signature = l_string_unwrap(builder->signature); builder->signature = NULL; if (_gvariant_is_fixed_size(signature)) { int alignment = _gvariant_get_alignment(signature); grow_body(builder, 0, alignment); /* Empty struct or "unit type" is encoded as a zero byte */ if (signature[0] == '\0') { size_t start = grow_body(builder, 1, 1); memset(builder->body + start, 0, 1); } } else container_append_struct_offsets(root, builder); /* * Make sure there's enough space after the body for the variant * signature written here but not included in the body size and * one framing offset value to be written in * _gvariant_message_finalize. */ size = 3 + strlen(signature) + 8; if (builder->body_pos + size > builder->body_size) builder->body = l_realloc(builder->body, builder->body_pos + size); variant_buf = builder->body + builder->body_pos; *variant_buf++ = 0; *variant_buf++ = '('; variant_buf = mempcpy(variant_buf, signature, strlen(signature)); *variant_buf++ = ')'; *body = builder->body; *body_size = builder->body_pos; builder->body = NULL; builder->body_size = 0; return signature; } /* * Write the header's framing offset after the body variant which is the * last piece of data in the message after the header, the padding and * the builder has written the message body. */ size_t _gvariant_message_finalize(size_t header_end, void *body, size_t body_size, const char *signature) { size_t offset_start; size_t offset_size; offset_start = body_size + 3 + strlen(signature); offset_size = offset_length(align_len(header_end, 8) + offset_start, 1); write_word_le(body + offset_start, header_end, offset_size); return align_len(header_end, 8) + offset_start + offset_size; } bluez-5.82/ell/PaxHeaders/dbus.c0000644000000000000000000000005014752501231013523 xustar0020 atime=1743575500 20 ctime=1743591276 bluez-5.82/ell/dbus.c0000644000000000000000000013165414752501231013216 0ustar00rootroot/* * Embedded Linux library * Copyright (C) 2011-2014 Intel Corporation * * SPDX-License-Identifier: LGPL-2.1-or-later */ #ifdef HAVE_CONFIG_H #include #endif #define _GNU_SOURCE #include #include #include #include #include #include #include #include #include #include #include "util.h" #include "io.h" #include "idle.h" #include "queue.h" #include "hashmap.h" #include "dbus.h" #include "private.h" #include "useful.h" #include "dbus-private.h" #define DEFAULT_SYSTEM_BUS_ADDRESS "unix:path=/var/run/dbus/system_bus_socket" #define DBUS_SERVICE_DBUS "org.freedesktop.DBus" #define DBUS_PATH_DBUS "/org/freedesktop/DBus" #define DBUS_MAXIMUM_MATCH_RULE_LENGTH 1024 enum auth_state { WAITING_FOR_OK, WAITING_FOR_AGREE_UNIX_FD, SETUP_DONE }; struct l_dbus_ops { char version; bool (*send_message)(struct l_dbus *bus, struct l_dbus_message *message); struct l_dbus_message *(*recv_message)(struct l_dbus *bus); void (*free)(struct l_dbus *bus); struct _dbus_name_ops name_ops; struct _dbus_filter_ops filter_ops; uint32_t (*name_acquire)(struct l_dbus *dbus, const char *name, bool allow_replacement, bool replace_existing, bool queue, l_dbus_name_acquire_func_t callback, void *user_data); }; struct l_dbus { struct l_io *io; char *guid; bool negotiate_unix_fd; bool support_unix_fd; bool is_ready; char *unique_name; unsigned int next_id; uint32_t next_serial; struct l_queue *message_queue; struct l_hashmap *message_list; struct l_hashmap *signal_list; l_dbus_ready_func_t ready_handler; l_dbus_destroy_func_t ready_destroy; void *ready_data; l_dbus_disconnect_func_t disconnect_handler; l_dbus_destroy_func_t disconnect_destroy; void *disconnect_data; l_dbus_debug_func_t debug_handler; l_dbus_destroy_func_t debug_destroy; void *debug_data; struct _dbus_object_tree *tree; struct _dbus_name_cache *name_cache; struct _dbus_filter *filter; bool name_notify_enabled; const struct l_dbus_ops *driver; }; struct l_dbus_classic { struct l_dbus super; void *auth_command; enum auth_state auth_state; bool skip_hello; struct l_hashmap *match_strings; int *fd_buf; unsigned int num_fds; }; struct message_callback { uint32_t serial; struct l_dbus_message *message; l_dbus_message_func_t callback; l_dbus_destroy_func_t destroy; void *user_data; }; struct signal_callback { unsigned int id; l_dbus_message_func_t callback; l_dbus_destroy_func_t destroy; void *user_data; }; static void message_queue_destroy(void *data) { struct message_callback *callback = data; l_dbus_message_unref(callback->message); if (callback->destroy) callback->destroy(callback->user_data); l_free(callback); } static void message_list_destroy(void *value) { message_queue_destroy(value); } static void signal_list_destroy(void *value) { struct signal_callback *callback = value; if (callback->destroy) callback->destroy(callback->user_data); l_free(callback); } static bool message_write_handler(struct l_io *io, void *user_data) { struct l_dbus *dbus = user_data; struct l_dbus_message *message; struct message_callback *callback; const void *header, *body; size_t header_size, body_size; callback = l_queue_pop_head(dbus->message_queue); if (!callback) return false; message = callback->message; if (_dbus_message_get_type(message) == DBUS_MESSAGE_TYPE_METHOD_CALL && callback->callback == NULL) l_dbus_message_set_no_reply(message, true); _dbus_message_set_serial(message, callback->serial); if (!dbus->driver->send_message(dbus, message)) { message_queue_destroy(callback); return false; } header = _dbus_message_get_header(message, &header_size); body = _dbus_message_get_body(message, &body_size); l_util_hexdump_two(false, header, header_size, body, body_size, dbus->debug_handler, dbus->debug_data); if (callback->callback == NULL) { message_queue_destroy(callback); goto done; } l_hashmap_insert(dbus->message_list, L_UINT_TO_PTR(callback->serial), callback); done: if (l_queue_isempty(dbus->message_queue)) return false; /* Only continue sending messges if the connection is ready */ return dbus->is_ready; } static void handle_method_return(struct l_dbus *dbus, struct l_dbus_message *message) { struct message_callback *callback; uint32_t reply_serial; reply_serial = _dbus_message_get_reply_serial(message); if (reply_serial == 0) return; callback = l_hashmap_remove(dbus->message_list, L_UINT_TO_PTR(reply_serial)); if (!callback) return; if (callback->callback) callback->callback(message, callback->user_data); message_queue_destroy(callback); } static void handle_error(struct l_dbus *dbus, struct l_dbus_message *message) { struct message_callback *callback; uint32_t reply_serial; reply_serial = _dbus_message_get_reply_serial(message); if (reply_serial == 0) return; callback = l_hashmap_remove(dbus->message_list, L_UINT_TO_PTR(reply_serial)); if (!callback) return; if (callback->callback) callback->callback(message, callback->user_data); message_queue_destroy(callback); } static void process_signal(const void *key, void *value, void *user_data) { struct signal_callback *callback = value; struct l_dbus_message *message = user_data; if (callback->callback) callback->callback(message, callback->user_data); } static void handle_signal(struct l_dbus *dbus, struct l_dbus_message *message) { l_hashmap_foreach(dbus->signal_list, process_signal, message); } static bool message_read_handler(struct l_io *io, void *user_data) { struct l_dbus *dbus = user_data; struct l_dbus_message *message; const void *header, *body; size_t header_size, body_size; enum dbus_message_type msgtype; message = dbus->driver->recv_message(dbus); if (!message) return true; header = _dbus_message_get_header(message, &header_size); body = _dbus_message_get_body(message, &body_size); l_util_hexdump_two(true, header, header_size, body, body_size, dbus->debug_handler, dbus->debug_data); msgtype = _dbus_message_get_type(message); switch (msgtype) { case DBUS_MESSAGE_TYPE_METHOD_RETURN: handle_method_return(dbus, message); break; case DBUS_MESSAGE_TYPE_ERROR: handle_error(dbus, message); break; case DBUS_MESSAGE_TYPE_SIGNAL: handle_signal(dbus, message); break; case DBUS_MESSAGE_TYPE_METHOD_CALL: if (!_dbus_object_tree_dispatch(dbus->tree, dbus, message)) { struct l_dbus_message *error; error = l_dbus_message_new_error(message, "org.freedesktop.DBus.Error.NotFound", "No matching method found"); l_dbus_send(dbus, error); } break; } l_dbus_message_unref(message); return true; } static uint32_t send_message(struct l_dbus *dbus, bool priority, struct l_dbus_message *message, l_dbus_message_func_t function, void *user_data, l_dbus_destroy_func_t destroy) { struct message_callback *callback; enum dbus_message_type type; const char *path; type = _dbus_message_get_type(message); if ((type == DBUS_MESSAGE_TYPE_METHOD_RETURN || type == DBUS_MESSAGE_TYPE_ERROR) && _dbus_message_get_reply_serial(message) == 0) { l_dbus_message_unref(message); return 0; } /* Default empty signature for method return messages */ if (type == DBUS_MESSAGE_TYPE_METHOD_RETURN && !l_dbus_message_get_signature(message)) l_dbus_message_set_arguments(message, ""); callback = l_new(struct message_callback, 1); callback->serial = dbus->next_serial++; callback->message = message; callback->callback = function; callback->destroy = destroy; callback->user_data = user_data; if (priority) { l_queue_push_head(dbus->message_queue, callback); l_io_set_write_handler(dbus->io, message_write_handler, dbus, NULL); return callback->serial; } path = l_dbus_message_get_path(message); if (path) _dbus_object_tree_signals_flush(dbus, path); l_queue_push_tail(dbus->message_queue, callback); if (dbus->is_ready) l_io_set_write_handler(dbus->io, message_write_handler, dbus, NULL); return callback->serial; } static void bus_ready(struct l_dbus *dbus) { dbus->is_ready = true; if (dbus->ready_handler) dbus->ready_handler(dbus->ready_data); l_io_set_read_handler(dbus->io, message_read_handler, dbus, NULL); /* Check for messages added before the connection was ready */ if (l_queue_isempty(dbus->message_queue)) return; l_io_set_write_handler(dbus->io, message_write_handler, dbus, NULL); } static void hello_callback(struct l_dbus_message *message, void *user_data) { struct l_dbus *dbus = user_data; const char *signature; const char *unique_name; signature = l_dbus_message_get_signature(message); if (!signature || strcmp(signature, "s")) { close(l_io_get_fd(dbus->io)); return; } if (!l_dbus_message_get_arguments(message, "s", &unique_name)) { close(l_io_get_fd(dbus->io)); return; } dbus->unique_name = l_strdup(unique_name); bus_ready(dbus); } static bool auth_write_handler(struct l_io *io, void *user_data) { struct l_dbus_classic *classic = user_data; struct l_dbus *dbus = &classic->super; ssize_t written, len; int fd; fd = l_io_get_fd(io); if (!classic->auth_command) return false; len = strlen(classic->auth_command); if (!len) return false; written = L_TFR(send(fd, classic->auth_command, len, 0)); if (written < 0) return false; l_util_hexdump(false, classic->auth_command, written, dbus->debug_handler, dbus->debug_data); if (written < len) { memmove(classic->auth_command, classic->auth_command + written, len + 1 - written); return true; } l_free(classic->auth_command); classic->auth_command = NULL; if (classic->auth_state == SETUP_DONE) { struct l_dbus_message *message; if (classic->skip_hello) { bus_ready(dbus); return true; } l_io_set_read_handler(dbus->io, message_read_handler, dbus, NULL); message = l_dbus_message_new_method_call(dbus, DBUS_SERVICE_DBUS, DBUS_PATH_DBUS, L_DBUS_INTERFACE_DBUS, "Hello"); l_dbus_message_set_arguments(message, ""); send_message(dbus, true, message, hello_callback, dbus, NULL); return true; } return false; } static bool auth_read_handler(struct l_io *io, void *user_data) { struct l_dbus_classic *classic = user_data; struct l_dbus *dbus = &classic->super; char buffer[64]; char *ptr, *end; ssize_t offset, len; int fd; fd = l_io_get_fd(io); ptr = buffer; offset = 0; while (1) { len = L_TFR(recv(fd, ptr + offset, sizeof(buffer) - offset, MSG_DONTWAIT)); if (len < 0) { if (errno != EAGAIN) return false; break; } offset += len; } ptr = buffer; len = offset; if (!ptr || len < 3) return true; end = strstr(ptr, "\r\n"); if (!end) return true; if (end - ptr + 2 != len) return true; l_util_hexdump(true, ptr, len, dbus->debug_handler, dbus->debug_data); *end = '\0'; switch (classic->auth_state) { case WAITING_FOR_OK: if (!strncmp(ptr, "OK ", 3)) { enum auth_state state; const char *command; if (dbus->negotiate_unix_fd) { command = "NEGOTIATE_UNIX_FD\r\n"; state = WAITING_FOR_AGREE_UNIX_FD; } else { command = "BEGIN\r\n"; state = SETUP_DONE; } l_free(dbus->guid); dbus->guid = l_strdup(ptr + 3); classic->auth_command = l_strdup(command); classic->auth_state = state; break; } else if (!strncmp(ptr, "REJECTED ", 9)) { static const char *command = "AUTH ANONYMOUS\r\n"; dbus->negotiate_unix_fd = true; classic->auth_command = l_strdup(command); classic->auth_state = WAITING_FOR_OK; } break; case WAITING_FOR_AGREE_UNIX_FD: if (!strncmp(ptr, "AGREE_UNIX_FD", 13)) { static const char *command = "BEGIN\r\n"; dbus->support_unix_fd = true; classic->auth_command = l_strdup(command); classic->auth_state = SETUP_DONE; break; } else if (!strncmp(ptr, "ERROR", 5)) { static const char *command = "BEGIN\r\n"; dbus->support_unix_fd = false; classic->auth_command = l_strdup(command); classic->auth_state = SETUP_DONE; break; } break; case SETUP_DONE: break; } l_io_set_write_handler(io, auth_write_handler, dbus, NULL); return true; } static void disconnect_handler(struct l_io *io, void *user_data) { struct l_dbus *dbus = user_data; dbus->is_ready = false; l_util_debug(dbus->debug_handler, dbus->debug_data, "disconnect"); if (dbus->disconnect_handler) dbus->disconnect_handler(dbus->disconnect_data); } static void dbus_init(struct l_dbus *dbus, int fd) { dbus->io = l_io_new(fd); l_io_set_close_on_destroy(dbus->io, true); l_io_set_disconnect_handler(dbus->io, disconnect_handler, dbus, NULL); dbus->is_ready = false; dbus->next_id = 1; dbus->next_serial = 1; dbus->message_queue = l_queue_new(); dbus->message_list = l_hashmap_new(); dbus->signal_list = l_hashmap_new(); dbus->tree = _dbus_object_tree_new(); } static void classic_free(struct l_dbus *dbus) { struct l_dbus_classic *classic = l_container_of(dbus, struct l_dbus_classic, super); unsigned int i; for (i = 0; i < classic->num_fds; i++) close(classic->fd_buf[i]); l_free(classic->fd_buf); l_free(classic->auth_command); l_hashmap_destroy(classic->match_strings, l_free); l_free(classic); } static bool classic_send_message(struct l_dbus *dbus, struct l_dbus_message *message) { int fd = l_io_get_fd(dbus->io); struct msghdr msg; struct iovec iov[2], *iovpos; ssize_t r; int *fds = NULL; uint32_t num_fds = 0; struct cmsghdr *cmsg; int iovlen; iov[0].iov_base = _dbus_message_get_header(message, &iov[0].iov_len); iov[1].iov_base = _dbus_message_get_body(message, &iov[1].iov_len); if (dbus->support_unix_fd) fds = _dbus_message_get_fds(message, &num_fds); iovpos = iov; iovlen = 2; while (1) { memset(&msg, 0, sizeof(msg)); msg.msg_iov = iovpos; msg.msg_iovlen = iovlen; if (num_fds) { msg.msg_control = alloca(CMSG_SPACE(num_fds * sizeof(int))); msg.msg_controllen = CMSG_LEN(num_fds * sizeof(int)); cmsg = CMSG_FIRSTHDR(&msg); cmsg->cmsg_len = msg.msg_controllen; cmsg->cmsg_level = SOL_SOCKET; cmsg->cmsg_type = SCM_RIGHTS; memcpy(CMSG_DATA(cmsg), fds, num_fds * sizeof(int)); } r = L_TFR(sendmsg(fd, &msg, 0)); if (r < 0) return false; while ((size_t) r >= iovpos->iov_len) { r -= iovpos->iov_len; iovpos++; iovlen--; if (!iovlen) break; } if (!iovlen) break; iovpos->iov_base += r; iovpos->iov_len -= r; /* The FDs have been transmitted, don't retransmit */ num_fds = 0; } return true; } static struct l_dbus_message *classic_recv_message(struct l_dbus *dbus) { struct l_dbus_classic *classic = l_container_of(dbus, struct l_dbus_classic, super); int fd = l_io_get_fd(dbus->io); struct dbus_header hdr; struct msghdr msg; struct iovec iov[2], *iovpos; struct cmsghdr *cmsg; ssize_t len, r; void *header, *body; size_t header_size, body_size; union { uint8_t bytes[CMSG_SPACE(16 * sizeof(int))]; struct cmsghdr align; } fd_buf; int *fds = NULL; uint32_t num_fds = 0; int iovlen; struct l_dbus_message *message; unsigned int i; len = recv(fd, &hdr, DBUS_HEADER_SIZE, MSG_PEEK | MSG_DONTWAIT); if (len != DBUS_HEADER_SIZE) return NULL; header_size = align_len(DBUS_HEADER_SIZE + hdr.dbus1.field_length, 8); header = l_malloc(header_size); body_size = hdr.dbus1.body_length; body = l_malloc(body_size); iov[0].iov_base = header; iov[0].iov_len = header_size; iov[1].iov_base = body; iov[1].iov_len = body_size; iovpos = iov; iovlen = 2; while (1) { memset(&msg, 0, sizeof(msg)); msg.msg_iov = iovpos; msg.msg_iovlen = iovlen; msg.msg_control = &fd_buf; msg.msg_controllen = sizeof(fd_buf); r = L_TFR(recvmsg(fd, &msg, MSG_CMSG_CLOEXEC | MSG_WAITALL)); if (r < 0) goto cmsg_fail; for (cmsg = CMSG_FIRSTHDR(&msg); cmsg; cmsg = CMSG_NXTHDR(&msg, cmsg)) { if (cmsg->cmsg_level != SOL_SOCKET || cmsg->cmsg_type != SCM_RIGHTS) continue; num_fds = (cmsg->cmsg_len - CMSG_LEN(0)) / sizeof(int); fds = (void *) CMSG_DATA(cmsg); /* Set FD_CLOEXEC on all file descriptors */ for (i = 0; i < num_fds; i++) { long flags; flags = fcntl(fds[i], F_GETFD, NULL); if (flags < 0) continue; if (!(flags & FD_CLOEXEC)) fcntl(fds[i], F_SETFD, flags | FD_CLOEXEC); } classic->fd_buf = l_realloc(classic->fd_buf, (classic->num_fds + num_fds) * sizeof(int)); memcpy(classic->fd_buf + classic->num_fds, fds, num_fds * sizeof(int)); classic->num_fds += num_fds; } while ((size_t) r >= iovpos->iov_len) { r -= iovpos->iov_len; iovpos++; iovlen--; if (!iovlen) break; } if (!iovlen) break; iovpos->iov_base += r; iovpos->iov_len -= r; } if (hdr.endian != DBUS_NATIVE_ENDIAN) { l_util_debug(dbus->debug_handler, dbus->debug_data, "Endianness incorrect"); goto bad_msg; } if (hdr.version != 1) { l_util_debug(dbus->debug_handler, dbus->debug_data, "Protocol version incorrect"); goto bad_msg; } num_fds = _dbus_message_unix_fds_from_header(header, header_size); if (num_fds > classic->num_fds) goto bad_msg; message = dbus_message_build(header, header_size, body, body_size, classic->fd_buf, num_fds); if (message && num_fds) { if (classic->num_fds > num_fds) { memmove(classic->fd_buf, classic->fd_buf + num_fds, (classic->num_fds - num_fds) * sizeof(int)); classic->num_fds -= num_fds; } else { l_free(classic->fd_buf); classic->fd_buf = NULL; classic->num_fds = 0; } } if (message) return message; bad_msg: cmsg_fail: for (i = 0; i < classic->num_fds; i++) close(classic->fd_buf[i]); l_free(classic->fd_buf); classic->fd_buf = NULL; classic->num_fds = 0; l_free(header); l_free(body); return NULL; } static bool classic_add_match(struct l_dbus *dbus, unsigned int id, const struct _dbus_filter_condition *rule, int rule_len) { struct l_dbus_classic *classic = l_container_of(dbus, struct l_dbus_classic, super); char *match_str; struct l_dbus_message *message; match_str = _dbus_filter_rule_to_str(rule, rule_len); l_hashmap_insert(classic->match_strings, L_UINT_TO_PTR(id), match_str); message = l_dbus_message_new_method_call(dbus, DBUS_SERVICE_DBUS, DBUS_PATH_DBUS, L_DBUS_INTERFACE_DBUS, "AddMatch"); l_dbus_message_set_arguments(message, "s", match_str); send_message(dbus, false, message, NULL, NULL, NULL); return true; } static bool classic_remove_match(struct l_dbus *dbus, unsigned int id) { struct l_dbus_classic *classic = l_container_of(dbus, struct l_dbus_classic, super); char *match_str = l_hashmap_remove(classic->match_strings, L_UINT_TO_PTR(id)); struct l_dbus_message *message; if (!match_str) return false; message = l_dbus_message_new_method_call(dbus, DBUS_SERVICE_DBUS, DBUS_PATH_DBUS, L_DBUS_INTERFACE_DBUS, "RemoveMatch"); l_dbus_message_set_arguments(message, "s", match_str); send_message(dbus, false, message, NULL, NULL, NULL); l_free(match_str); return true; } static void name_owner_changed_cb(struct l_dbus_message *message, void *user_data) { struct l_dbus *dbus = user_data; char *name, *old, *new; if (!l_dbus_message_get_arguments(message, "sss", &name, &old, &new)) return; _dbus_name_cache_notify(dbus->name_cache, name, new); } struct get_name_owner_request { struct l_dbus_message *message; struct l_dbus *dbus; }; static void get_name_owner_reply_cb(struct l_dbus_message *reply, void *user_data) { struct get_name_owner_request *req = user_data; const char *name, *owner; /* No name owner yet */ if (l_dbus_message_is_error(reply)) return; /* Shouldn't happen */ if (!l_dbus_message_get_arguments(reply, "s", &owner)) return; /* Shouldn't happen */ if (!l_dbus_message_get_arguments(req->message, "s", &name)) return; _dbus_name_cache_notify(req->dbus->name_cache, name, owner); } static bool classic_get_name_owner(struct l_dbus *bus, const char *name) { struct get_name_owner_request *req; /* Name resolution is not performed for DBUS_SERVICE_DBUS */ if (!strcmp(name, DBUS_SERVICE_DBUS)) return false; req = l_new(struct get_name_owner_request, 1); req->dbus = bus; req->message = l_dbus_message_new_method_call(bus, DBUS_SERVICE_DBUS, DBUS_PATH_DBUS, L_DBUS_INTERFACE_DBUS, "GetNameOwner"); l_dbus_message_set_arguments(req->message, "s", name); send_message(bus, false, req->message, get_name_owner_reply_cb, req, l_free); if (!bus->name_notify_enabled) { static struct _dbus_filter_condition rule[] = { { L_DBUS_MATCH_TYPE, "signal" }, { L_DBUS_MATCH_SENDER, DBUS_SERVICE_DBUS }, { L_DBUS_MATCH_PATH, DBUS_PATH_DBUS }, { L_DBUS_MATCH_INTERFACE, L_DBUS_INTERFACE_DBUS }, { L_DBUS_MATCH_MEMBER, "NameOwnerChanged" }, }; if (!bus->filter) bus->filter = _dbus_filter_new(bus, &bus->driver->filter_ops, bus->name_cache); _dbus_filter_add_rule(bus->filter, rule, L_ARRAY_SIZE(rule), name_owner_changed_cb, bus); bus->name_notify_enabled = true; } return true; } struct name_request { l_dbus_name_acquire_func_t callback; void *user_data; struct l_dbus *dbus; }; enum dbus_name_flag { DBUS_NAME_FLAG_ALLOW_REPLACEMENT = 0x1, DBUS_NAME_FLAG_REPLACE_EXISTING = 0x2, DBUS_NAME_FLAG_DO_NOT_QUEUE = 0x4, }; enum dbus_name_reply { DBUS_REQUEST_NAME_REPLY_PRIMARY_OWNER = 1, DBUS_REQUEST_NAME_REPLY_IN_QUEUE = 2, DBUS_REQUEST_NAME_REPLY_EXISTS = 3, DBUS_REQUEST_NAME_REPLY_ALREADY_OWNER = 4, }; static void request_name_reply_cb(struct l_dbus_message *reply, void *user_data) { struct name_request *req = user_data; bool success = false, queued = false; uint32_t retval; if (!req->callback) return; /* No name owner yet */ if (l_dbus_message_is_error(reply)) goto call_back; /* Shouldn't happen */ if (!l_dbus_message_get_arguments(reply, "u", &retval)) goto call_back; success = (retval == DBUS_REQUEST_NAME_REPLY_PRIMARY_OWNER) || (retval == DBUS_REQUEST_NAME_REPLY_ALREADY_OWNER) || (retval == DBUS_REQUEST_NAME_REPLY_IN_QUEUE); queued = (retval == DBUS_REQUEST_NAME_REPLY_IN_QUEUE); call_back: req->callback(req->dbus, success, queued, req->user_data); } static uint32_t classic_name_acquire(struct l_dbus *dbus, const char *name, bool allow_replacement, bool replace_existing, bool queue, l_dbus_name_acquire_func_t callback, void *user_data) { struct name_request *req; struct l_dbus_message *message; uint32_t flags = 0; req = l_new(struct name_request, 1); req->dbus = dbus; req->user_data = user_data; req->callback = callback; message = l_dbus_message_new_method_call(dbus, DBUS_SERVICE_DBUS, DBUS_PATH_DBUS, L_DBUS_INTERFACE_DBUS, "RequestName"); if (allow_replacement) flags |= DBUS_NAME_FLAG_ALLOW_REPLACEMENT; if (replace_existing) flags |= DBUS_NAME_FLAG_REPLACE_EXISTING; if (!queue) flags |= DBUS_NAME_FLAG_DO_NOT_QUEUE; l_dbus_message_set_arguments(message, "su", name, flags); return send_message(dbus, false, message, request_name_reply_cb, req, free); } static const struct l_dbus_ops classic_ops = { .version = 1, .send_message = classic_send_message, .recv_message = classic_recv_message, .free = classic_free, .name_ops = { .get_name_owner = classic_get_name_owner, }, .filter_ops = { .add_match = classic_add_match, .remove_match = classic_remove_match, }, .name_acquire = classic_name_acquire, }; static struct l_dbus *setup_dbus1(int fd, const char *guid, bool skip_hello) { static const unsigned char creds = 0x00; char uid[6], hexuid[12], *ptr = hexuid; struct l_dbus *dbus; struct l_dbus_classic *classic; ssize_t written; unsigned int i; if (snprintf(uid, sizeof(uid), "%d", geteuid()) < 1) { close(fd); return NULL; } for (i = 0; i < strlen(uid); i++) ptr += sprintf(ptr, "%02x", uid[i]); /* Send special credentials-passing nul byte */ written = L_TFR(send(fd, &creds, 1, 0)); if (written < 1) { close(fd); return NULL; } classic = l_new(struct l_dbus_classic, 1); dbus = &classic->super; dbus->driver = &classic_ops; classic->match_strings = l_hashmap_new(); dbus_init(dbus, fd); dbus->guid = l_strdup(guid); classic->auth_command = l_strdup_printf("AUTH EXTERNAL %s\r\n", hexuid); classic->auth_state = WAITING_FOR_OK; classic->skip_hello = skip_hello; dbus->negotiate_unix_fd = true; dbus->support_unix_fd = false; l_io_set_read_handler(dbus->io, auth_read_handler, dbus, NULL); l_io_set_write_handler(dbus->io, auth_write_handler, dbus, NULL); return dbus; } static struct l_dbus *setup_unix(char *params) { char *path = NULL, *guid = NULL; bool abstract = false; struct sockaddr_un addr; size_t len; int fd; while (params) { char *key = strsep(¶ms, ","); char *value; if (!key) break; value = strchr(key, '='); if (!value) continue; *value++ = '\0'; if (!strcmp(key, "path")) { path = value; abstract = false; } else if (!strcmp(key, "abstract")) { path = value; abstract = true; } else if (!strcmp(key, "guid")) guid = value; } if (!path) return NULL; fd = socket(PF_UNIX, SOCK_STREAM | SOCK_CLOEXEC, 0); if (fd < 0) return NULL; memset(&addr, 0, sizeof(addr)); addr.sun_family = AF_UNIX; len = strlen(path); if (abstract) { if (len > sizeof(addr.sun_path) - 1) { close(fd); return NULL; } addr.sun_path[0] = '\0'; strncpy(addr.sun_path + 1, path, sizeof(addr.sun_path) - 2); len++; } else { if (len > sizeof(addr.sun_path)) { close(fd); return NULL; } strncpy(addr.sun_path, path, sizeof(addr.sun_path) - 1); } if (connect(fd, (struct sockaddr *) &addr, sizeof(addr.sun_family) + len) < 0) { close(fd); return NULL; } return setup_dbus1(fd, guid, false); } static bool setup_tcp_cb(struct l_io *io, void *user_data) { static const unsigned char creds = 0x00; struct l_dbus *dbus = user_data; struct l_dbus_classic *classic; ssize_t written; int fd = l_io_get_fd(io); /* Send special credentials-passing nul byte */ written = L_TFR(send(fd, &creds, 1, 0)); if (written < 1) { l_util_debug(dbus->debug_handler, dbus->debug_handler, "error writing NUL byte"); close(fd); return false; } dbus->driver = &classic_ops; dbus->negotiate_unix_fd = false; dbus->support_unix_fd = false; classic = l_container_of(dbus, struct l_dbus_classic, super); classic->match_strings = l_hashmap_new(); classic->auth_command = l_strdup("AUTH ANONYMOUS\r\n"); classic->auth_state = WAITING_FOR_OK; l_io_set_read_handler(dbus->io, auth_read_handler, dbus, NULL); l_io_set_write_handler(dbus->io, auth_write_handler, dbus, NULL); return auth_write_handler(dbus->io, dbus); } static struct l_dbus *setup_tcp(char *params) { char *host = NULL; char *port = NULL; char *family = NULL; struct addrinfo hints = { 0 }; struct addrinfo *res; struct addrinfo *iter; struct l_dbus *dbus = NULL; while (params) { char *key = strsep(¶ms, ","); char *value; value = strchr(key, '='); if (!value) continue; *value++ = '\0'; if (!strcmp(key, "host")) host = value; else if (!strcmp(key, "port")) port = value; else if (!strcmp(key, "family")) family = value; } if (!host || !port) return NULL; if (!family) hints.ai_family = AF_UNSPEC; else if (!strcmp(family, "ipv4")) hints.ai_family = AF_INET; else if (!strcmp(family, "ipv6")) hints.ai_family = AF_INET6; else return NULL; hints.ai_socktype = SOCK_STREAM; hints.ai_flags = AI_NUMERICHOST | AI_NUMERICSERV; hints.ai_protocol = IPPROTO_TCP; if (getaddrinfo(host, port, &hints, &res) != 0) return NULL; for (iter = res; iter; iter = iter->ai_next) { int fd; struct l_dbus_classic *classic; fd = socket(iter->ai_family, iter->ai_socktype | SOCK_NONBLOCK, iter->ai_protocol); if (fd < 0) continue; if (connect(fd, iter->ai_addr, iter->ai_addrlen) < 0) { if (errno != EINPROGRESS) { close(fd); continue; } } classic = l_new(struct l_dbus_classic, 1); dbus = &classic->super; dbus_init(dbus, fd); l_io_set_write_handler(dbus->io, setup_tcp_cb, dbus, NULL); break; } freeaddrinfo(res); return dbus; } static struct l_dbus *setup_address(const char *address) { struct l_dbus *dbus = NULL; char *address_copy; address_copy = strdupa(address); while (address_copy) { char *transport = strsep(&address_copy, ";"); char *params; if (!transport) break; params = strchr(transport, ':'); if (params) *params++ = '\0'; if (!strcmp(transport, "unix")) { /* Function will modify params string */ dbus = setup_unix(params); break; } else if (!strcmp(transport, "tcp")) { dbus = setup_tcp(params); break; } } return dbus; } LIB_EXPORT struct l_dbus *l_dbus_new(const char *address) { if (unlikely(!address)) return NULL; return setup_address(address); } LIB_EXPORT struct l_dbus *l_dbus_new_default(enum l_dbus_bus bus) { const char *address; switch (bus) { case L_DBUS_SYSTEM_BUS: address = getenv("DBUS_SYSTEM_BUS_ADDRESS"); if (!address) address = DEFAULT_SYSTEM_BUS_ADDRESS; break; case L_DBUS_SESSION_BUS: address = getenv("DBUS_SESSION_BUS_ADDRESS"); if (!address) return NULL; break; default: return NULL; } return setup_address(address); } LIB_EXPORT struct l_dbus *l_dbus_new_private(int fd) { return setup_dbus1(fd, NULL, true); } LIB_EXPORT void l_dbus_destroy(struct l_dbus *dbus) { if (unlikely(!dbus)) return; if (dbus->ready_destroy) dbus->ready_destroy(dbus->ready_data); _dbus_filter_free(dbus->filter); _dbus_name_cache_free(dbus->name_cache); l_hashmap_destroy(dbus->signal_list, signal_list_destroy); l_hashmap_destroy(dbus->message_list, message_list_destroy); l_queue_destroy(dbus->message_queue, message_queue_destroy); l_io_destroy(dbus->io); if (dbus->disconnect_destroy) dbus->disconnect_destroy(dbus->disconnect_data); if (dbus->debug_destroy) dbus->debug_destroy(dbus->debug_data); l_free(dbus->guid); l_free(dbus->unique_name); _dbus_object_tree_free(dbus->tree); dbus->driver->free(dbus); } LIB_EXPORT bool l_dbus_set_ready_handler(struct l_dbus *dbus, l_dbus_ready_func_t function, void *user_data, l_dbus_destroy_func_t destroy) { if (unlikely(!dbus)) return false; if (dbus->ready_destroy) dbus->ready_destroy(dbus->ready_data); dbus->ready_handler = function; dbus->ready_destroy = destroy; dbus->ready_data = user_data; return true; } LIB_EXPORT bool l_dbus_set_disconnect_handler(struct l_dbus *dbus, l_dbus_disconnect_func_t function, void *user_data, l_dbus_destroy_func_t destroy) { if (unlikely(!dbus)) return false; if (dbus->disconnect_destroy) dbus->disconnect_destroy(dbus->disconnect_data); dbus->disconnect_handler = function; dbus->disconnect_destroy = destroy; dbus->disconnect_data = user_data; return true; } LIB_EXPORT bool l_dbus_set_debug(struct l_dbus *dbus, l_dbus_debug_func_t function, void *user_data, l_dbus_destroy_func_t destroy) { if (unlikely(!dbus)) return false; if (dbus->debug_destroy) dbus->debug_destroy(dbus->debug_data); dbus->debug_handler = function; dbus->debug_destroy = destroy; dbus->debug_data = user_data; /* l_io_set_debug(dbus->io, function, user_data, NULL); */ return true; } LIB_EXPORT uint32_t l_dbus_send_with_reply(struct l_dbus *dbus, struct l_dbus_message *message, l_dbus_message_func_t function, void *user_data, l_dbus_destroy_func_t destroy) { if (unlikely(!dbus || !message)) return 0; return send_message(dbus, false, message, function, user_data, destroy); } LIB_EXPORT uint32_t l_dbus_send(struct l_dbus *dbus, struct l_dbus_message *message) { if (unlikely(!dbus || !message)) return 0; return send_message(dbus, false, message, NULL, NULL, NULL); } static bool remove_entry(void *data, void *user_data) { struct message_callback *callback = data; uint32_t serial = L_PTR_TO_UINT(user_data); if (callback->serial == serial) { message_queue_destroy(callback); return true; } return false; } LIB_EXPORT bool l_dbus_cancel(struct l_dbus *dbus, uint32_t serial) { struct message_callback *callback; unsigned int count; if (unlikely(!dbus || !serial)) return false; callback = l_hashmap_remove(dbus->message_list, L_UINT_TO_PTR(serial)); if (callback) { message_queue_destroy(callback); return true; } count = l_queue_foreach_remove(dbus->message_queue, remove_entry, L_UINT_TO_PTR(serial)); if (!count) return false; return true; } LIB_EXPORT unsigned int l_dbus_register(struct l_dbus *dbus, l_dbus_message_func_t function, void *user_data, l_dbus_destroy_func_t destroy) { struct signal_callback *callback; if (unlikely(!dbus)) return 0; callback = l_new(struct signal_callback, 1); callback->id = dbus->next_id++; callback->callback = function; callback->destroy = destroy; callback->user_data = user_data; l_hashmap_insert(dbus->signal_list, L_UINT_TO_PTR(callback->id), callback); return callback->id; } LIB_EXPORT bool l_dbus_unregister(struct l_dbus *dbus, unsigned int id) { struct signal_callback *callback; if (unlikely(!dbus || !id)) return false; callback = l_hashmap_remove(dbus->signal_list, L_UINT_TO_PTR(id)); if (!callback) return false; signal_list_destroy(callback); return true; } LIB_EXPORT uint32_t l_dbus_method_call(struct l_dbus *dbus, const char *destination, const char *path, const char *interface, const char *method, l_dbus_message_func_t setup, l_dbus_message_func_t function, void *user_data, l_dbus_destroy_func_t destroy) { struct l_dbus_message *message; if (unlikely(!dbus)) return 0; message = l_dbus_message_new_method_call(dbus, destination, path, interface, method); if (setup) setup(message, user_data); else l_dbus_message_set_arguments(message, ""); return send_message(dbus, false, message, function, user_data, destroy); } uint8_t _dbus_get_version(struct l_dbus *dbus) { return dbus->driver->version; } int _dbus_get_fd(struct l_dbus *dbus) { return l_io_get_fd(dbus->io); } struct _dbus_object_tree *_dbus_get_tree(struct l_dbus *dbus) { return dbus->tree; } /** * l_dbus_register_interface: * @dbus: D-Bus connection as returned by @l_dbus_new* * @interface: interface name string * @setup_func: function that sets up the methods, signals and properties by * using the #dbus-service.h API. * @destroy: optional destructor to be called every time an instance of this * interface is being removed from an object on this bus. * @handle_old_style_properties: whether to automatically handle SetProperty and * GetProperties for any properties registered by * @setup_func. * * Registers an interface. If successful the interface can then be added * to any number of objects with @l_dbus_object_add_interface. * * Returns: whether the interface was successfully registered **/ LIB_EXPORT bool l_dbus_register_interface(struct l_dbus *dbus, const char *interface, l_dbus_interface_setup_func_t setup_func, l_dbus_destroy_func_t destroy, bool handle_old_style_properties) { if (unlikely(!dbus)) return false; if (unlikely(!dbus->tree)) return false; return _dbus_object_tree_register_interface(dbus->tree, interface, setup_func, destroy, handle_old_style_properties); } LIB_EXPORT bool l_dbus_unregister_interface(struct l_dbus *dbus, const char *interface) { if (unlikely(!dbus)) return false; if (unlikely(!dbus->tree)) return false; return _dbus_object_tree_unregister_interface(dbus->tree, interface); } /** * l_dbus_register_object: * @dbus: D-Bus connection * @path: new object path * @user_data: user pointer to be passed to @destroy if any * @destroy: optional destructor to be called when object dropped from the tree * @...: NULL-terminated list of 0 or more interfaces to be present on the * object from the moment of creation. For every interface the interface * name string is expected followed by the @user_data pointer same as * would be passed as @l_dbus_object_add_interface's last two parameters. * * Create a new D-Bus object on the tree visible to D-Bus peers. For example: * success = l_dbus_register_object(bus, "/org/example/ExampleManager", * NULL, NULL, * "org.example.Manager", * manager_data, * NULL); * * Returns: whether the object path was successfully registered **/ LIB_EXPORT bool l_dbus_register_object(struct l_dbus *dbus, const char *path, void *user_data, l_dbus_destroy_func_t destroy, ...) { va_list args; const char *interface; void *if_user_data; bool r = true; if (unlikely(!dbus)) return false; if (unlikely(!dbus->tree)) return false; if (!_dbus_object_tree_new_object(dbus->tree, path, user_data, destroy)) return false; va_start(args, destroy); while ((interface = va_arg(args, const char *))) { if_user_data = va_arg(args, void *); if (!_dbus_object_tree_add_interface(dbus->tree, path, interface, if_user_data)) { _dbus_object_tree_object_destroy(dbus->tree, path); r = false; break; } } va_end(args); return r; } LIB_EXPORT bool l_dbus_unregister_object(struct l_dbus *dbus, const char *object) { if (unlikely(!dbus)) return false; if (unlikely(!dbus->tree)) return false; return _dbus_object_tree_object_destroy(dbus->tree, object); } /** * l_dbus_object_add_interface: * @dbus: D-Bus connection * @object: object path as passed to @l_dbus_register_object * @interface: interface name as passed to @l_dbus_register_interface * @user_data: user data pointer to be passed to any method and property * callbacks provided by the @setup_func and to the @destroy * callback as passed to @l_dbus_register_interface * * Creates an instance of given interface at the given path in the * connection's object tree. If no object was registered at this path * before @l_dbus_register_object gets called automatically. * * The addition of an interface to the object may trigger a query of * all the properties on this interface and * #org.freedesktop.DBus.ObjectManager.InterfacesAdded signals. * * Returns: whether the interface was successfully added. **/ LIB_EXPORT bool l_dbus_object_add_interface(struct l_dbus *dbus, const char *object, const char *interface, void *user_data) { if (unlikely(!dbus)) return false; if (unlikely(!dbus->tree)) return false; return _dbus_object_tree_add_interface(dbus->tree, object, interface, user_data); } LIB_EXPORT bool l_dbus_object_remove_interface(struct l_dbus *dbus, const char *object, const char *interface) { if (unlikely(!dbus)) return false; if (unlikely(!dbus->tree)) return false; return _dbus_object_tree_remove_interface(dbus->tree, object, interface); } LIB_EXPORT void *l_dbus_object_get_data(struct l_dbus *dbus, const char *object, const char *interface) { if (unlikely(!dbus)) return NULL; if (unlikely(!dbus->tree)) return NULL; return _dbus_object_tree_get_interface_data(dbus->tree, object, interface); } LIB_EXPORT bool l_dbus_object_set_data(struct l_dbus *dbus, const char *object, const char *interface, void *user_data) { if (unlikely(!dbus)) return false; if (unlikely(!dbus->tree)) return false; return _dbus_object_tree_set_interface_data(dbus->tree, object, interface, user_data); } LIB_EXPORT bool l_dbus_object_manager_enable(struct l_dbus *dbus, const char *root) { if (unlikely(!dbus)) return false; if (unlikely(!dbus->tree)) return false; return _dbus_object_tree_add_interface(dbus->tree, root, L_DBUS_INTERFACE_OBJECT_MANAGER, dbus); } LIB_EXPORT unsigned int l_dbus_add_disconnect_watch(struct l_dbus *dbus, const char *name, l_dbus_watch_func_t disconnect_func, void *user_data, l_dbus_destroy_func_t destroy) { return l_dbus_add_service_watch(dbus, name, NULL, disconnect_func, user_data, destroy); } LIB_EXPORT unsigned int l_dbus_add_service_watch(struct l_dbus *dbus, const char *name, l_dbus_watch_func_t connect_func, l_dbus_watch_func_t disconnect_func, void *user_data, l_dbus_destroy_func_t destroy) { if (!name) return 0; if (!dbus->name_cache) dbus->name_cache = _dbus_name_cache_new(dbus, &dbus->driver->name_ops); return _dbus_name_cache_add_watch(dbus->name_cache, name, connect_func, disconnect_func, user_data, destroy); } LIB_EXPORT bool l_dbus_remove_watch(struct l_dbus *dbus, unsigned int id) { if (!dbus->name_cache) return false; return _dbus_name_cache_remove_watch(dbus->name_cache, id); } /** * l_dbus_add_signal_watch: * @dbus: D-Bus connection * @sender: bus name to match the signal sender against or NULL to * match any sender * @path: object path to match the signal path against or NULL to * match any path * @interface: interface name to match the signal interface against * or NULL to match any interface * @member: name to match the signal name against or NULL to match any * signal * @...: a list of further conditions to be met by the signal followed * by three more mandatory parameters: * enum l_dbus_match_type list_end_marker, * l_dbus_message_func callback, * void *user_data, * The value L_DBUS_MATCH_NONE must be passed as the end of list * marker, followed by the signal match callback and user_data. * In the list, every condition is a pair of parameters: * enum l_dbus_match_type match_type, const char *value. * * Subscribe to a group of signals based on a set of conditions that * compare the signal's header fields and string arguments against given * values. For example: * signal_id = l_dbus_add_signal_watch(bus, "org.example", "/" * "org.example.Manager", * "PropertyChanged", * L_DBUS_MATCH_ARGUMENT(0), * "ExampleProperty", * L_DBUS_MATCH_NONE * manager_property_change_cb, * NULL); * * Returns: a non-zero signal filter identifier that can be passed to * l_dbus_remove_signal_watch to remove this filter rule, or * zero on failure. **/ LIB_EXPORT unsigned int l_dbus_add_signal_watch(struct l_dbus *dbus, const char *sender, const char *path, const char *interface, const char *member, ...) { struct _dbus_filter_condition *rule; int rule_len; va_list args; const char *value; l_dbus_message_func_t signal_func; enum l_dbus_match_type type; void *user_data; unsigned int id; va_start(args, member); rule_len = 0; while (true) { type = va_arg(args, enum l_dbus_match_type); if (type == L_DBUS_MATCH_NONE) break; va_arg(args, const char *); rule_len++; } va_end(args); rule = l_new(struct _dbus_filter_condition, rule_len + 5); rule_len = 0; rule[rule_len].type = L_DBUS_MATCH_TYPE; rule[rule_len++].value = "signal"; if (sender) { rule[rule_len].type = L_DBUS_MATCH_SENDER; rule[rule_len++].value = sender; } if (path) { rule[rule_len].type = L_DBUS_MATCH_PATH; rule[rule_len++].value = path; } if (interface) { rule[rule_len].type = L_DBUS_MATCH_INTERFACE; rule[rule_len++].value = interface; } if (member) { rule[rule_len].type = L_DBUS_MATCH_MEMBER; rule[rule_len++].value = member; } va_start(args, member); while (true) { type = va_arg(args, enum l_dbus_match_type); if (type == L_DBUS_MATCH_NONE) break; value = va_arg(args, const char *); rule[rule_len].type = type; rule[rule_len++].value = value; } signal_func = va_arg(args, l_dbus_message_func_t); user_data = va_arg(args, void *); va_end(args); if (!dbus->filter) { if (!dbus->name_cache) dbus->name_cache = _dbus_name_cache_new(dbus, &dbus->driver->name_ops); dbus->filter = _dbus_filter_new(dbus, &dbus->driver->filter_ops, dbus->name_cache); } id = _dbus_filter_add_rule(dbus->filter, rule, rule_len, signal_func, user_data); l_free(rule); return id; } LIB_EXPORT bool l_dbus_remove_signal_watch(struct l_dbus *dbus, unsigned int id) { if (!dbus->filter) return false; return _dbus_filter_remove_rule(dbus->filter, id); } /** * l_dbus_name_acquire: * @dbus: D-Bus connection * @name: Well-known bus name to be acquired * @allow_replacement: Whether to allow another peer's name request to * take the name ownership away from this connection * @replace_existing: Whether to allow D-Bus to take the name's ownership * away from another peer in case the name is already * owned and allows replacement. Ignored if name is * currently free. * @queue: Whether to allow the name request to be queued by D-Bus in * case it cannot be acquired now, rather than to return a failure. * @callback: Callback to receive the request result when done. * * Acquire a well-known bus name (service name) on the bus. * * Returns: a non-zero request serial that can be passed to l_dbus_cancel * while waiting for the callback or zero if the callback has * has happened while l_dbus_name_acquire was running. **/ LIB_EXPORT uint32_t l_dbus_name_acquire(struct l_dbus *dbus, const char *name, bool allow_replacement, bool replace_existing, bool queue, l_dbus_name_acquire_func_t callback, void *user_data) { return dbus->driver->name_acquire(dbus, name, allow_replacement, replace_existing, queue, callback, user_data); } bluez-5.82/ell/PaxHeaders/dbus.h0000644000000000000000000000005014661631567013547 xustar0020 atime=1743575449 20 ctime=1743591276 bluez-5.82/ell/dbus.h0000644000000000000000000002235114661631567013233 0ustar00rootroot/* * Embedded Linux library * Copyright (C) 2011-2014 Intel Corporation * * SPDX-License-Identifier: LGPL-2.1-or-later */ #ifndef __ELL_DBUS_H #define __ELL_DBUS_H #include #include #include #include #ifdef __cplusplus extern "C" { #endif #define L_DBUS_INTERFACE_DBUS "org.freedesktop.DBus" #define L_DBUS_INTERFACE_INTROSPECTABLE "org.freedesktop.DBus.Introspectable" #define L_DBUS_INTERFACE_PROPERTIES "org.freedesktop.DBus.Properties" #define L_DBUS_INTERFACE_OBJECT_MANAGER "org.freedesktop.DBus.ObjectManager" enum l_dbus_bus { L_DBUS_SYSTEM_BUS, L_DBUS_SESSION_BUS, }; enum l_dbus_match_type { L_DBUS_MATCH_NONE = 0, L_DBUS_MATCH_TYPE, L_DBUS_MATCH_SENDER, L_DBUS_MATCH_PATH, L_DBUS_MATCH_INTERFACE, L_DBUS_MATCH_MEMBER, L_DBUS_MATCH_ARG0, }; #define L_DBUS_MATCH_ARGUMENT(i) (L_DBUS_MATCH_ARG0 + (i)) struct l_dbus; struct l_dbus_interface; struct l_dbus_message_builder; typedef void (*l_dbus_ready_func_t) (void *user_data); typedef void (*l_dbus_disconnect_func_t) (void *user_data); typedef void (*l_dbus_debug_func_t) (const char *str, void *user_data); typedef void (*l_dbus_destroy_func_t) (void *user_data); typedef void (*l_dbus_interface_setup_func_t) (struct l_dbus_interface *); typedef void (*l_dbus_watch_func_t) (struct l_dbus *dbus, void *user_data); typedef void (*l_dbus_name_acquire_func_t) (struct l_dbus *dbus, bool success, bool queued, void *user_data); struct l_dbus *l_dbus_new(const char *address); struct l_dbus *l_dbus_new_default(enum l_dbus_bus bus); struct l_dbus *l_dbus_new_private(int fd); void l_dbus_destroy(struct l_dbus *dbus); bool l_dbus_set_ready_handler(struct l_dbus *dbus, l_dbus_ready_func_t function, void *user_data, l_dbus_destroy_func_t destroy); bool l_dbus_set_disconnect_handler(struct l_dbus *dbus, l_dbus_disconnect_func_t function, void *user_data, l_dbus_destroy_func_t destroy); bool l_dbus_set_debug(struct l_dbus *dbus, l_dbus_debug_func_t function, void *user_data, l_dbus_destroy_func_t destroy); struct l_dbus_message; struct l_dbus_message_iter { struct l_dbus_message *message; const char *sig_start; uint8_t sig_len; uint8_t sig_pos; const void *data; size_t len; size_t pos; char container_type; const void *offsets; }; struct l_dbus_message *l_dbus_message_new_method_call(struct l_dbus *dbus, const char *destination, const char *path, const char *interface, const char *method); struct l_dbus_message *l_dbus_message_new_signal(struct l_dbus *dbus, const char *path, const char *interface, const char *name); struct l_dbus_message *l_dbus_message_new_method_return( struct l_dbus_message *method_call); struct l_dbus_message *l_dbus_message_new_error_valist( struct l_dbus_message *method_call, const char *name, const char *format, va_list args) __attribute__((format(printf, 3, 0))); struct l_dbus_message *l_dbus_message_new_error( struct l_dbus_message *method_call, const char *name, const char *format, ...) __attribute__((format(printf, 3, 4))); struct l_dbus_message *l_dbus_message_ref(struct l_dbus_message *message); void l_dbus_message_unref(struct l_dbus_message *message); const char *l_dbus_message_get_path(struct l_dbus_message *message); const char *l_dbus_message_get_interface(struct l_dbus_message *message); const char *l_dbus_message_get_member(struct l_dbus_message *message); const char *l_dbus_message_get_destination(struct l_dbus_message *message); const char *l_dbus_message_get_sender(struct l_dbus_message *message); const char *l_dbus_message_get_signature(struct l_dbus_message *message); bool l_dbus_message_set_no_reply(struct l_dbus_message *message, bool on); bool l_dbus_message_get_no_reply(struct l_dbus_message *message); bool l_dbus_message_set_no_autostart(struct l_dbus_message *message, bool on); bool l_dbus_message_get_no_autostart(struct l_dbus_message *message); typedef void (*l_dbus_message_func_t) (struct l_dbus_message *message, void *user_data); uint32_t l_dbus_send_with_reply(struct l_dbus *dbus, struct l_dbus_message *message, l_dbus_message_func_t function, void *user_data, l_dbus_destroy_func_t destroy); uint32_t l_dbus_send(struct l_dbus *dbus, struct l_dbus_message *message); bool l_dbus_cancel(struct l_dbus *dbus, uint32_t serial); unsigned int l_dbus_register(struct l_dbus *dbus, l_dbus_message_func_t function, void *user_data, l_dbus_destroy_func_t destroy); bool l_dbus_unregister(struct l_dbus *dbus, unsigned int id); uint32_t l_dbus_method_call(struct l_dbus *dbus, const char *destination, const char *path, const char *interface, const char *method, l_dbus_message_func_t setup, l_dbus_message_func_t function, void *user_data, l_dbus_destroy_func_t destroy); bool l_dbus_message_is_error(struct l_dbus_message *message); bool l_dbus_message_get_error(struct l_dbus_message *message, const char **name, const char **text); bool l_dbus_message_get_arguments(struct l_dbus_message *message, const char *signature, ...); bool l_dbus_message_get_arguments_valist(struct l_dbus_message *message, const char *signature, va_list args); bool l_dbus_message_iter_next_entry(struct l_dbus_message_iter *iter, ...); bool l_dbus_message_iter_get_variant(struct l_dbus_message_iter *iter, const char *signature, ...); bool l_dbus_message_iter_get_fixed_array(struct l_dbus_message_iter *iter, void *out, uint32_t *n_elem); bool l_dbus_message_set_arguments(struct l_dbus_message *message, const char *signature, ...); bool l_dbus_message_set_arguments_valist(struct l_dbus_message *message, const char *signature, va_list args); struct l_dbus_message_builder *l_dbus_message_builder_new( struct l_dbus_message *message); void l_dbus_message_builder_destroy(struct l_dbus_message_builder *builder); bool l_dbus_message_builder_append_basic(struct l_dbus_message_builder *builder, char type, const void *value); bool l_dbus_message_builder_enter_container( struct l_dbus_message_builder *builder, char container_type, const char *signature); bool l_dbus_message_builder_leave_container( struct l_dbus_message_builder *builder, char container_type); bool l_dbus_message_builder_enter_struct(struct l_dbus_message_builder *builder, const char *signature); bool l_dbus_message_builder_leave_struct( struct l_dbus_message_builder *builder); bool l_dbus_message_builder_enter_dict(struct l_dbus_message_builder *builder, const char *signature); bool l_dbus_message_builder_leave_dict(struct l_dbus_message_builder *builder); bool l_dbus_message_builder_enter_array(struct l_dbus_message_builder *builder, const char *signature); bool l_dbus_message_builder_leave_array(struct l_dbus_message_builder *builder); bool l_dbus_message_builder_enter_variant( struct l_dbus_message_builder *builder, const char *signature); bool l_dbus_message_builder_leave_variant( struct l_dbus_message_builder *builder); bool l_dbus_message_builder_append_from_iter( struct l_dbus_message_builder *builder, struct l_dbus_message_iter *from); bool l_dbus_message_builder_append_from_valist( struct l_dbus_message_builder *builder, const char *signature, va_list args); struct l_dbus_message *l_dbus_message_builder_finalize( struct l_dbus_message_builder *builder); bool l_dbus_register_interface(struct l_dbus *dbus, const char *interface, l_dbus_interface_setup_func_t setup_func, l_dbus_destroy_func_t destroy, bool handle_old_style_properties); bool l_dbus_unregister_interface(struct l_dbus *dbus, const char *interface); bool l_dbus_register_object(struct l_dbus *dbus, const char *path, void *user_data, l_dbus_destroy_func_t destroy, ...); bool l_dbus_unregister_object(struct l_dbus *dbus, const char *object); bool l_dbus_object_add_interface(struct l_dbus *dbus, const char *object, const char *interface, void *user_data); bool l_dbus_object_remove_interface(struct l_dbus *dbus, const char *object, const char *interface); void *l_dbus_object_get_data(struct l_dbus *dbus, const char *object, const char *interface); bool l_dbus_object_set_data(struct l_dbus *dbus, const char *object, const char *interface, void *user_data); bool l_dbus_object_manager_enable(struct l_dbus *dbus, const char *root); unsigned int l_dbus_add_service_watch(struct l_dbus *dbus, const char *name, l_dbus_watch_func_t connect_func, l_dbus_watch_func_t disconnect_func, void *user_data, l_dbus_destroy_func_t destroy); unsigned int l_dbus_add_disconnect_watch(struct l_dbus *dbus, const char *name, l_dbus_watch_func_t disconnect_func, void *user_data, l_dbus_destroy_func_t destroy); bool l_dbus_remove_watch(struct l_dbus *dbus, unsigned int id); unsigned int l_dbus_add_signal_watch(struct l_dbus *dbus, const char *sender, const char *path, const char *interface, const char *member, ...); bool l_dbus_remove_signal_watch(struct l_dbus *dbus, unsigned int id); uint32_t l_dbus_name_acquire(struct l_dbus *dbus, const char *name, bool allow_replacement, bool replace_existing, bool queue, l_dbus_name_acquire_func_t callback, void *user_data); #ifdef __cplusplus } #endif #endif /* __ELL_DBUS_H */ bluez-5.82/ell/PaxHeaders/cipher.c0000644000000000000000000000005014526443132014044 xustar0020 atime=1743575478 20 ctime=1743591276 bluez-5.82/ell/cipher.c0000644000000000000000000006100714526443132013531 0ustar00rootroot/* * Embedded Linux library * Copyright (C) 2015 Intel Corporation * * SPDX-License-Identifier: LGPL-2.1-or-later */ #ifdef HAVE_CONFIG_H #include #endif #define _GNU_SOURCE #include #include #include #include #include #include #include "useful.h" #include "cipher.h" #include "private.h" #include "random.h" #include "missing.h" #ifndef HAVE_LINUX_IF_ALG_H #ifndef HAVE_LINUX_TYPES_H typedef uint8_t __u8; typedef uint16_t __u16; typedef uint32_t __u32; #else #include #endif #ifndef AF_ALG #define AF_ALG 38 #define PF_ALG AF_ALG #endif struct sockaddr_alg { __u16 salg_family; __u8 salg_type[14]; __u32 salg_feat; __u32 salg_mask; __u8 salg_name[64]; }; struct af_alg_iv { __u32 ivlen; __u8 iv[0]; }; /* Socket options */ #define ALG_SET_KEY 1 #define ALG_SET_IV 2 #define ALG_SET_OP 3 /* Operations */ #define ALG_OP_DECRYPT 0 #define ALG_OP_ENCRYPT 1 #else #include #endif #ifndef SOL_ALG #define SOL_ALG 279 #endif #ifndef ALG_SET_AEAD_ASSOCLEN #define ALG_SET_AEAD_ASSOCLEN 4 #endif #ifndef ALG_SET_AEAD_AUTHSIZE #define ALG_SET_AEAD_AUTHSIZE 5 #endif #define is_valid_type(type) ((type) <= L_CIPHER_RC2_CBC) static uint32_t supported_ciphers; static uint32_t supported_aead_ciphers; struct l_cipher { int type; const struct local_impl *local; union { int sk; void *local_data; }; }; struct l_aead_cipher { int type; int sk; }; struct local_impl { void *(*cipher_new)(enum l_cipher_type, const void *key, size_t key_length); void (*cipher_free)(void *data); bool (*set_iv)(void *data, const uint8_t *iv, size_t iv_length); ssize_t (*operate)(void *data, __u32 operation, const struct iovec *in, size_t in_cnt, const struct iovec *out, size_t out_cnt); }; static int create_alg(const char *alg_type, const char *alg_name, const void *key, size_t key_length, size_t tag_length) { struct sockaddr_alg salg; int sk; int ret; sk = socket(PF_ALG, SOCK_SEQPACKET | SOCK_CLOEXEC, 0); if (sk < 0) return -errno; memset(&salg, 0, sizeof(salg)); salg.salg_family = AF_ALG; strcpy((char *) salg.salg_type, alg_type); strcpy((char *) salg.salg_name, alg_name); if (bind(sk, (struct sockaddr *) &salg, sizeof(salg)) < 0) { close(sk); return -1; } if (setsockopt(sk, SOL_ALG, ALG_SET_KEY, key, key_length) < 0) { close(sk); return -1; } if (tag_length && setsockopt(sk, SOL_ALG, ALG_SET_AEAD_AUTHSIZE, NULL, tag_length)) { close(sk); return -1; } ret = accept4(sk, NULL, 0, SOCK_CLOEXEC); close(sk); return ret; } static const char *cipher_type_to_name(enum l_cipher_type type) { switch (type) { case L_CIPHER_AES: return "ecb(aes)"; case L_CIPHER_AES_CBC: return "cbc(aes)"; case L_CIPHER_AES_CTR: return "ctr(aes)"; case L_CIPHER_ARC4: return NULL; case L_CIPHER_DES: return "ecb(des)"; case L_CIPHER_DES_CBC: return "cbc(des)"; case L_CIPHER_DES3_EDE_CBC: return "cbc(des3_ede)"; case L_CIPHER_RC2_CBC: return NULL; } return NULL; } static const struct local_impl local_arc4; static const struct local_impl local_rc2_cbc; static const struct local_impl *local_impl_ciphers[] = { [L_CIPHER_ARC4] = &local_arc4, [L_CIPHER_RC2_CBC] = &local_rc2_cbc, }; #define HAVE_LOCAL_IMPLEMENTATION(type) \ ((type) < L_ARRAY_SIZE(local_impl_ciphers) && \ local_impl_ciphers[(type)]) LIB_EXPORT struct l_cipher *l_cipher_new(enum l_cipher_type type, const void *key, size_t key_length) { struct l_cipher *cipher; const char *alg_name; if (unlikely(!key)) return NULL; if (!is_valid_type(type)) return NULL; cipher = l_new(struct l_cipher, 1); cipher->type = type; alg_name = cipher_type_to_name(type); if (HAVE_LOCAL_IMPLEMENTATION(type)) { cipher->local = local_impl_ciphers[type]; cipher->local_data = cipher->local->cipher_new(type, key, key_length); if (!cipher->local_data) goto error_free; return cipher; } cipher->sk = create_alg("skcipher", alg_name, key, key_length, 0); if (cipher->sk < 0) goto error_free; return cipher; error_free: l_free(cipher); return NULL; } static const char *aead_cipher_type_to_name(enum l_aead_cipher_type type) { switch (type) { case L_AEAD_CIPHER_AES_CCM: return "ccm(aes)"; case L_AEAD_CIPHER_AES_GCM: return "gcm(aes)"; } return NULL; } LIB_EXPORT struct l_aead_cipher *l_aead_cipher_new(enum l_aead_cipher_type type, const void *key, size_t key_length, size_t tag_length) { struct l_aead_cipher *cipher; const char *alg_name; if (unlikely(!key)) return NULL; if (type != L_AEAD_CIPHER_AES_CCM && type != L_AEAD_CIPHER_AES_GCM) return NULL; cipher = l_new(struct l_aead_cipher, 1); cipher->type = type; alg_name = aead_cipher_type_to_name(type); cipher->sk = create_alg("aead", alg_name, key, key_length, tag_length); if (cipher->sk >= 0) return cipher; l_free(cipher); return NULL; } LIB_EXPORT void l_cipher_free(struct l_cipher *cipher) { if (unlikely(!cipher)) return; if (cipher->local) cipher->local->cipher_free(cipher->local_data); else close(cipher->sk); l_free(cipher); } LIB_EXPORT void l_aead_cipher_free(struct l_aead_cipher *cipher) { if (unlikely(!cipher)) return; close(cipher->sk); l_free(cipher); } static ssize_t operate_cipher(int sk, __u32 operation, const void *in, size_t in_len, const void *ad, size_t ad_len, const void *iv, size_t iv_len, void *out, size_t out_len) { char *c_msg_buf; size_t c_msg_size; struct msghdr msg; struct cmsghdr *c_msg; struct iovec iov[2]; ssize_t result; c_msg_size = CMSG_SPACE(sizeof(operation)); c_msg_size += ad_len ? CMSG_SPACE(sizeof(uint32_t)) : 0; c_msg_size += iv_len ? CMSG_SPACE(sizeof(struct af_alg_iv) + iv_len) : 0; c_msg_buf = alloca(c_msg_size); memset(c_msg_buf, 0, c_msg_size); memset(&msg, 0, sizeof(msg)); msg.msg_iov = iov; msg.msg_control = c_msg_buf; msg.msg_controllen = c_msg_size; c_msg = CMSG_FIRSTHDR(&msg); c_msg->cmsg_level = SOL_ALG; c_msg->cmsg_type = ALG_SET_OP; c_msg->cmsg_len = CMSG_LEN(sizeof(operation)); memcpy(CMSG_DATA(c_msg), &operation, sizeof(operation)); if (ad_len) { uint32_t *ad_data; c_msg = CMSG_NXTHDR(&msg, c_msg); c_msg->cmsg_level = SOL_ALG; c_msg->cmsg_type = ALG_SET_AEAD_ASSOCLEN; c_msg->cmsg_len = CMSG_LEN(sizeof(*ad_data)); ad_data = (void *) CMSG_DATA(c_msg); *ad_data = ad_len; iov[0].iov_base = (void *) ad; iov[0].iov_len = ad_len; msg.msg_iovlen = 1; if (in) { iov[1].iov_base = (void *) in; iov[1].iov_len = in_len; msg.msg_iovlen = 2; } } else { iov[0].iov_base = (void *) in; iov[0].iov_len = in_len; msg.msg_iovlen = 1; } if (iv_len) { struct af_alg_iv *algiv; c_msg = CMSG_NXTHDR(&msg, c_msg); c_msg->cmsg_level = SOL_ALG; c_msg->cmsg_type = ALG_SET_IV; c_msg->cmsg_len = CMSG_LEN(sizeof(*algiv) + iv_len); algiv = (void *)CMSG_DATA(c_msg); algiv->ivlen = iv_len; memcpy(algiv->iv, iv, iv_len); } result = sendmsg(sk, &msg, 0); if (result < 0) return -errno; if (ad_len) { /* * When AEAD additional data is passed to sendmsg() for * use in computing the tag, those bytes also appear at * the beginning of the encrypt or decrypt results. Rather * than force the caller to pad their result buffer with * the correct number of bytes for the additional data, * the necessary space is allocated here and then the * duplicate AAD is discarded. */ iov[0].iov_base = l_malloc(ad_len); iov[0].iov_len = ad_len; iov[1].iov_base = (void *) out; iov[1].iov_len = out_len; msg.msg_iovlen = 2; msg.msg_control = NULL; msg.msg_controllen = 0; result = recvmsg(sk, &msg, 0); if (result >= (ssize_t) ad_len) result -= ad_len; else if (result > 0) result = 0; l_free(iov[0].iov_base); } else { result = read(sk, out, out_len); } if (result < 0) return -errno; return result; } static ssize_t operate_cipherv(int sk, __u32 operation, const struct iovec *in, size_t in_cnt, const struct iovec *out, size_t out_cnt) { char *c_msg_buf; size_t c_msg_size; struct msghdr msg; struct cmsghdr *c_msg; ssize_t result; c_msg_size = CMSG_SPACE(sizeof(operation)); c_msg_buf = alloca(c_msg_size); memset(c_msg_buf, 0, c_msg_size); memset(&msg, 0, sizeof(msg)); msg.msg_iov = (struct iovec *) in; msg.msg_iovlen = in_cnt; msg.msg_control = c_msg_buf; msg.msg_controllen = c_msg_size; c_msg = CMSG_FIRSTHDR(&msg); c_msg->cmsg_level = SOL_ALG; c_msg->cmsg_type = ALG_SET_OP; c_msg->cmsg_len = CMSG_LEN(sizeof(operation)); memcpy(CMSG_DATA(c_msg), &operation, sizeof(operation)); result = sendmsg(sk, &msg, 0); if (result < 0) return -errno; result = readv(sk, out, out_cnt); if (result < 0) return -errno; return result; } LIB_EXPORT bool l_cipher_encrypt(struct l_cipher *cipher, const void *in, void *out, size_t len) { if (unlikely(!cipher)) return false; if (unlikely(!in) || unlikely(!out)) return false; if (cipher->local) { struct iovec in_iov = { (void *) in, len }; struct iovec out_iov = { out, len }; return cipher->local->operate(cipher->local_data, ALG_OP_ENCRYPT, &in_iov, 1, &out_iov, 1) >= 0; } return operate_cipher(cipher->sk, ALG_OP_ENCRYPT, in, len, NULL, 0, NULL, 0, out, len) >= 0; } LIB_EXPORT bool l_cipher_encryptv(struct l_cipher *cipher, const struct iovec *in, size_t in_cnt, const struct iovec *out, size_t out_cnt) { if (unlikely(!cipher)) return false; if (unlikely(!in) || unlikely(!out)) return false; if (cipher->local) return cipher->local->operate(cipher->local_data, ALG_OP_ENCRYPT, in, in_cnt, out, out_cnt) >= 0; return operate_cipherv(cipher->sk, ALG_OP_ENCRYPT, in, in_cnt, out, out_cnt) >= 0; } LIB_EXPORT bool l_cipher_decrypt(struct l_cipher *cipher, const void *in, void *out, size_t len) { if (unlikely(!cipher)) return false; if (unlikely(!in) || unlikely(!out)) return false; if (cipher->local) { struct iovec in_iov = { (void *) in, len }; struct iovec out_iov = { out, len }; return cipher->local->operate(cipher->local_data, ALG_OP_DECRYPT, &in_iov, 1, &out_iov, 1) >= 0; } return operate_cipher(cipher->sk, ALG_OP_DECRYPT, in, len, NULL, 0, NULL, 0, out, len) >= 0; } LIB_EXPORT bool l_cipher_decryptv(struct l_cipher *cipher, const struct iovec *in, size_t in_cnt, const struct iovec *out, size_t out_cnt) { if (unlikely(!cipher)) return false; if (unlikely(!in) || unlikely(!out)) return false; if (cipher->local) return cipher->local->operate(cipher->local_data, ALG_OP_DECRYPT, in, in_cnt, out, out_cnt) >= 0; return operate_cipherv(cipher->sk, ALG_OP_DECRYPT, in, in_cnt, out, out_cnt) >= 0; } LIB_EXPORT bool l_cipher_set_iv(struct l_cipher *cipher, const uint8_t *iv, size_t iv_length) { char c_msg_buf[CMSG_SPACE(4 + iv_length)]; struct msghdr msg; struct cmsghdr *c_msg; uint32_t len = iv_length; if (unlikely(!cipher)) return false; if (cipher->local) { if (!cipher->local->set_iv) return false; return cipher->local->set_iv(cipher->local_data, iv, iv_length); } memset(&c_msg_buf, 0, sizeof(c_msg_buf)); memset(&msg, 0, sizeof(struct msghdr)); msg.msg_control = c_msg_buf; msg.msg_controllen = sizeof(c_msg_buf); c_msg = CMSG_FIRSTHDR(&msg); c_msg->cmsg_level = SOL_ALG; c_msg->cmsg_type = ALG_SET_IV; c_msg->cmsg_len = CMSG_LEN(4 + iv_length); memcpy(CMSG_DATA(c_msg) + 0, &len, 4); memcpy(CMSG_DATA(c_msg) + 4, iv, iv_length); msg.msg_iov = NULL; msg.msg_iovlen = 0; if (sendmsg(cipher->sk, &msg, MSG_MORE) < 0) return false; return true; } #define CCM_IV_SIZE 16 static size_t l_aead_cipher_get_ivlen(struct l_aead_cipher *cipher) { switch (cipher->type) { case L_AEAD_CIPHER_AES_CCM: return CCM_IV_SIZE; case L_AEAD_CIPHER_AES_GCM: return 12; } return 0; } /* RFC3610 Section 2.3 */ static ssize_t build_ccm_iv(const void *nonce, uint8_t nonce_len, uint8_t (*iv)[CCM_IV_SIZE]) { const size_t iv_overhead = 2; int lprime = 15 - nonce_len - 1; if (unlikely(nonce_len + iv_overhead > CCM_IV_SIZE || lprime > 7)) return -EINVAL; (*iv)[0] = lprime; memcpy(*iv + 1, nonce, nonce_len); memset(*iv + 1 + nonce_len, 0, lprime + 1); return CCM_IV_SIZE; } LIB_EXPORT bool l_aead_cipher_encrypt(struct l_aead_cipher *cipher, const void *in, size_t in_len, const void *ad, size_t ad_len, const void *nonce, size_t nonce_len, void *out, size_t out_len) { uint8_t ccm_iv[CCM_IV_SIZE]; const uint8_t *iv; ssize_t iv_len; if (unlikely(!cipher)) return false; if (unlikely(!in && !ad) || unlikely(!out)) return false; if (unlikely(!in && in_len) || unlikely(!ad && ad_len)) return false; if (cipher->type == L_AEAD_CIPHER_AES_CCM) { iv_len = build_ccm_iv(nonce, nonce_len, &ccm_iv); if (unlikely(iv_len < 0)) return false; iv = ccm_iv; } else { if (unlikely(nonce_len != l_aead_cipher_get_ivlen(cipher))) return false; iv = nonce; iv_len = nonce_len; } return operate_cipher(cipher->sk, ALG_OP_ENCRYPT, in, in_len, ad, ad_len, iv, iv_len, out, out_len) == (ssize_t)out_len; } LIB_EXPORT bool l_aead_cipher_decrypt(struct l_aead_cipher *cipher, const void *in, size_t in_len, const void *ad, size_t ad_len, const void *nonce, size_t nonce_len, void *out, size_t out_len) { uint8_t ccm_iv[CCM_IV_SIZE]; const uint8_t *iv; ssize_t iv_len; if (unlikely(!cipher)) return false; if (unlikely(!in) || unlikely(!out)) return false; if (cipher->type == L_AEAD_CIPHER_AES_CCM) { iv_len = build_ccm_iv(nonce, nonce_len, &ccm_iv); if (unlikely(iv_len < 0)) return false; iv = ccm_iv; } else { if (unlikely(nonce_len != l_aead_cipher_get_ivlen(cipher))) return false; iv = nonce; iv_len = nonce_len; } return operate_cipher(cipher->sk, ALG_OP_DECRYPT, in, in_len, ad, ad_len, iv, iv_len, out, out_len) == (ssize_t)out_len; } static void init_supported() { static bool initialized = false; struct sockaddr_alg salg; int sk; enum l_cipher_type c; enum l_aead_cipher_type a; if (likely(initialized)) return; initialized = true; for (c = 0; c < L_ARRAY_SIZE(local_impl_ciphers); c++) if (HAVE_LOCAL_IMPLEMENTATION(c)) supported_ciphers |= 1 << c; sk = socket(PF_ALG, SOCK_SEQPACKET | SOCK_CLOEXEC, 0); if (sk < 0) return; memset(&salg, 0, sizeof(salg)); salg.salg_family = AF_ALG; strcpy((char *) salg.salg_type, "skcipher"); for (c = L_CIPHER_AES; c <= L_CIPHER_DES3_EDE_CBC; c++) { const char *name = cipher_type_to_name(c); if (!name) continue; strcpy((char *) salg.salg_name, name); if (bind(sk, (struct sockaddr *) &salg, sizeof(salg)) < 0) continue; supported_ciphers |= 1 << c; } strcpy((char *) salg.salg_type, "aead"); for (a = L_AEAD_CIPHER_AES_CCM; a <= L_AEAD_CIPHER_AES_GCM; a++) { strcpy((char *) salg.salg_name, aead_cipher_type_to_name(a)); if (bind(sk, (struct sockaddr *) &salg, sizeof(salg)) < 0) continue; supported_aead_ciphers |= 1 << a; } close(sk); } LIB_EXPORT bool l_cipher_is_supported(enum l_cipher_type type) { if (!is_valid_type(type)) return false; init_supported(); return supported_ciphers & (1 << type); } LIB_EXPORT bool l_aead_cipher_is_supported(enum l_aead_cipher_type type) { if (type != L_AEAD_CIPHER_AES_CCM && type != L_AEAD_CIPHER_AES_GCM) return false; init_supported(); return supported_aead_ciphers & (1 << type); } /* ARC4 implementation copyright (c) 2001 Niels Möller */ static void arc4_set_key(uint8_t *S, const uint8_t *key, size_t key_length) { unsigned int i; uint8_t j; for (i = 0; i < 256; i++) S[i] = i; for (i = j = 0; i < 256; i++) { j += S[i] + key[i % key_length]; SWAP(S[i], S[j]); } } struct arc4_state { struct arc4_state_ctx { uint8_t S[256]; uint8_t i; uint8_t j; } ctx[2]; }; static void *local_arc4_new(enum l_cipher_type type, const void *key, size_t key_length) { struct arc4_state *s; if (unlikely(key_length == 0 || key_length > 256)) return NULL; s = l_new(struct arc4_state, 1); arc4_set_key(s->ctx[0].S, key, key_length); s->ctx[1] = s->ctx[0]; return s; } static void local_arc4_free(void *data) { explicit_bzero(data, sizeof(struct arc4_state)); l_free(data); } static ssize_t local_arc4_operate(void *data, __u32 operation, const struct iovec *in, size_t in_cnt, const struct iovec *out, size_t out_cnt) { struct arc4_state *s = data; struct iovec cur_in; struct iovec cur_out; struct arc4_state_ctx *ctx = &s->ctx[operation == ALG_OP_ENCRYPT ? 1 : 0]; if (!in_cnt || !out_cnt) return 0; cur_in = *in; cur_out = *out; while (1) { while (!cur_in.iov_len) { cur_in = *in++; if (!--in_cnt) return 0; } while (!cur_out.iov_len) { cur_out = *out++; if (!--out_cnt) return 0; } ctx->j += ctx->S[++ctx->i]; SWAP(ctx->S[ctx->i], ctx->S[ctx->j]); *(uint8_t *) cur_out.iov_base++ = *(uint8_t *) cur_in.iov_base++ ^ ctx->S[(ctx->S[ctx->i] + ctx->S[ctx->j]) & 0xff]; cur_in.iov_len--; cur_out.iov_len--; } } static const struct local_impl local_arc4 = { local_arc4_new, local_arc4_free, NULL, local_arc4_operate, }; struct rc2_state { union { uint16_t xkey[64]; uint8_t xkey8[128]; }; struct rc2_state_ctx { union { uint16_t x[4]; uint64_t x64; }; } ctx[2]; }; /* Simplified from the 1996 public-domain implementation */ static void rc2_keyschedule(struct rc2_state *s, const uint8_t *key, size_t key_len, size_t bits) { static const uint8_t permute[256] = { 217,120,249,196, 25,221,181,237, 40,233,253,121, 74,160,216,157, 198,126, 55,131, 43,118, 83,142, 98, 76,100,136, 68,139,251,162, 23,154, 89,245,135,179, 79, 19, 97, 69,109,141, 9,129,125, 50, 189,143, 64,235,134,183,123, 11,240,149, 33, 34, 92,107, 78,130, 84,214,101,147,206, 96,178, 28,115, 86,192, 20,167,140,241,220, 18,117,202, 31, 59,190,228,209, 66, 61,212, 48,163, 60,182, 38, 111,191, 14,218, 70,105, 7, 87, 39,242, 29,155,188,148, 67, 3, 248, 17,199,246,144,239, 62,231, 6,195,213, 47,200,102, 30,215, 8,232,234,222,128, 82,238,247,132,170,114,172, 53, 77,106, 42, 150, 26,210,113, 90, 21, 73,116, 75,159,208, 94, 4, 24,164,236, 194,224, 65,110, 15, 81,203,204, 36,145,175, 80,161,244,112, 57, 153,124, 58,133, 35,184,180,122,252, 2, 54, 91, 37, 85,151, 49, 45, 93,250,152,227,138,146,174, 5,223, 41, 16,103,108,186,201, 211, 0,230,207,225,158,168, 44, 99, 22, 1, 63, 88,226,137,169, 13, 56, 52, 27,171, 51,255,176,187, 72, 12, 95,185,177,205, 46, 197,243,219, 71,229,165,156,119, 10,166, 32,104,254,127,193,173 }; uint8_t x; unsigned int i; memcpy(&s->xkey8, key, key_len); /* Step 1: expand input key to 128 bytes */ x = s->xkey8[key_len - 1]; for (i = 0; key_len < 128; key_len++, i++) s->xkey8[key_len] = x = permute[(x + s->xkey8[i]) & 255]; /* Step 2: reduce effective key size to "bits" */ key_len = (bits + 7) >> 3; i = 128 - key_len; s->xkey8[i] = x = permute[s->xkey8[i] & (255 >> (7 & -bits))]; while (i--) s->xkey8[i] = x = permute[x ^ s->xkey8[i + key_len]]; /* Step 3: copy to xkey in little-endian order */ for (i = 0; i < 64; i++) s->xkey[i] = L_CPU_TO_LE16(s->xkey[i]); } static uint64_t rc2_operate(struct rc2_state *s, uint64_t in, __u32 operation) { int i; union { uint16_t x16[4]; uint64_t x64; } x; x.x64 = in; if (operation == ALG_OP_ENCRYPT) { const uint16_t *xkey = s->xkey; for (i = 0; i < 16; i++) { x.x16[0] += (x.x16[1] & ~x.x16[3]) + (x.x16[2] & x.x16[3]) + *xkey++; x.x16[0] = (x.x16[0] << 1) | (x.x16[0] >> 15); x.x16[1] += (x.x16[2] & ~x.x16[0]) + (x.x16[3] & x.x16[0]) + *xkey++; x.x16[1] = (x.x16[1] << 2) | (x.x16[1] >> 14); x.x16[2] += (x.x16[3] & ~x.x16[1]) + (x.x16[0] & x.x16[1]) + *xkey++; x.x16[2] = (x.x16[2] << 3) | (x.x16[2] >> 13); x.x16[3] += (x.x16[0] & ~x.x16[2]) + (x.x16[1] & x.x16[2]) + *xkey++; x.x16[3] = (x.x16[3] << 5) | (x.x16[3] >> 11); if (i == 4 || i == 10) { x.x16[0] += s->xkey[x.x16[3] & 63]; x.x16[1] += s->xkey[x.x16[0] & 63]; x.x16[2] += s->xkey[x.x16[1] & 63]; x.x16[3] += s->xkey[x.x16[2] & 63]; } } } else { const uint16_t *xkey = s->xkey + 63; for (i = 0; i < 16; i++) { x.x16[3] = (x.x16[3] << 11) | (x.x16[3] >> 5); x.x16[3] -= (x.x16[0] & ~x.x16[2]) + (x.x16[1] & x.x16[2]) + *xkey--; x.x16[2] = (x.x16[2] << 13) | (x.x16[2] >> 3); x.x16[2] -= (x.x16[3] & ~x.x16[1]) + (x.x16[0] & x.x16[1]) + *xkey--; x.x16[1] = (x.x16[1] << 14) | (x.x16[1] >> 2); x.x16[1] -= (x.x16[2] & ~x.x16[0]) + (x.x16[3] & x.x16[0]) + *xkey--; x.x16[0] = (x.x16[0] << 15) | (x.x16[0] >> 1); x.x16[0] -= (x.x16[1] & ~x.x16[3]) + (x.x16[2] & x.x16[3]) + *xkey--; if (i == 4 || i == 10) { x.x16[3] -= s->xkey[x.x16[2] & 63]; x.x16[2] -= s->xkey[x.x16[1] & 63]; x.x16[1] -= s->xkey[x.x16[0] & 63]; x.x16[0] -= s->xkey[x.x16[3] & 63]; } } } return x.x64; } static void *local_rc2_cbc_new(enum l_cipher_type type, const void *key, size_t key_length) { struct rc2_state *s; if (unlikely(key_length == 0 || key_length > 128)) return NULL; /* * The key length and the effective "strength" bits are separate * parameters but they match in our current use cases. */ s = l_new(struct rc2_state, 1); rc2_keyschedule(s, key, key_length, key_length * 8); return s; } static void local_rc2_cbc_free(void *data) { explicit_bzero(data, sizeof(struct rc2_state)); l_free(data); } static bool local_rc2_cbc_set_iv(void *data, const uint8_t *iv, size_t iv_length) { struct rc2_state *s = data; if (unlikely(iv_length != 8)) return false; s->ctx[0].x[0] = l_get_le16(iv + 0); s->ctx[0].x[1] = l_get_le16(iv + 2); s->ctx[0].x[2] = l_get_le16(iv + 4); s->ctx[0].x[3] = l_get_le16(iv + 6); s->ctx[1].x64 = s->ctx[0].x64; return true; } static ssize_t local_rc2_cbc_operate(void *data, __u32 operation, const struct iovec *in, size_t in_cnt, const struct iovec *out, size_t out_cnt) { struct rc2_state *s = data; struct iovec cur_in = {}; struct iovec cur_out = {}; struct rc2_state_ctx *ctx = &s->ctx[operation == ALG_OP_ENCRYPT ? 1 : 0]; #define CONSUME_IN(bytes, eof_ok) \ cur_in.iov_len -= (bytes); \ while (!cur_in.iov_len) { \ if (!in_cnt) { \ if (eof_ok) \ break; \ else \ return -1; \ } \ \ cur_in = *in++; \ in_cnt--; \ } #define CONSUME_OUT(bytes) \ cur_out.iov_len -= (bytes); \ while (!cur_out.iov_len) { \ if (!out_cnt) \ return 0; \ \ cur_out = *out++; \ out_cnt--; \ } CONSUME_IN(0, true) CONSUME_OUT(0) while (cur_in.iov_len) { union { uint16_t x16[4]; uint64_t x64; } inblk; if (cur_in.iov_len >= 8) { #define CUR_IN16 (*(uint16_t **) &cur_in.iov_base) inblk.x16[0] = l_get_le16(CUR_IN16++); inblk.x16[1] = l_get_le16(CUR_IN16++); inblk.x16[2] = l_get_le16(CUR_IN16++); inblk.x16[3] = l_get_le16(CUR_IN16++); CONSUME_IN(8, true) } else { inblk.x16[0] = *(uint8_t *) cur_in.iov_base++; CONSUME_IN(1, false) inblk.x16[0] |= (*(uint8_t *) cur_in.iov_base++) << 8; CONSUME_IN(1, false) inblk.x16[1] = *(uint8_t *) cur_in.iov_base++; CONSUME_IN(1, false) inblk.x16[1] |= (*(uint8_t *) cur_in.iov_base++) << 8; CONSUME_IN(1, false) inblk.x16[2] = *(uint8_t *) cur_in.iov_base++; CONSUME_IN(1, false) inblk.x16[2] |= (*(uint8_t *) cur_in.iov_base++) << 8; CONSUME_IN(1, false) inblk.x16[3] = *(uint8_t *) cur_in.iov_base++; CONSUME_IN(1, false) inblk.x16[3] |= (*(uint8_t *) cur_in.iov_base++) << 8; CONSUME_IN(1, true) } if (operation == ALG_OP_ENCRYPT) ctx->x64 = rc2_operate(s, inblk.x64 ^ ctx->x64, operation); else ctx->x64 ^= rc2_operate(s, inblk.x64, operation); if (cur_out.iov_len >= 8) { #define CUR_OUT16 (*(uint16_t **) &cur_out.iov_base) l_put_le16(ctx->x[0], CUR_OUT16++); l_put_le16(ctx->x[1], CUR_OUT16++); l_put_le16(ctx->x[2], CUR_OUT16++); l_put_le16(ctx->x[3], CUR_OUT16++); CONSUME_OUT(8) } else { *(uint8_t *) cur_out.iov_base++ = ctx->x[0]; CONSUME_OUT(1) *(uint8_t *) cur_out.iov_base++ = ctx->x[0] >> 8; CONSUME_OUT(1) *(uint8_t *) cur_out.iov_base++ = ctx->x[1]; CONSUME_OUT(1) *(uint8_t *) cur_out.iov_base++ = ctx->x[1] >> 8; CONSUME_OUT(1) *(uint8_t *) cur_out.iov_base++ = ctx->x[2]; CONSUME_OUT(1) *(uint8_t *) cur_out.iov_base++ = ctx->x[2] >> 8; CONSUME_OUT(1) *(uint8_t *) cur_out.iov_base++ = ctx->x[3]; CONSUME_OUT(1) *(uint8_t *) cur_out.iov_base++ = ctx->x[3] >> 8; CONSUME_OUT(1) } /* Save ciphertext as IV for next CBC block */ if (operation == ALG_OP_DECRYPT) ctx->x64 = inblk.x64; inblk.x64 = 0; } return 0; } static const struct local_impl local_rc2_cbc = { local_rc2_cbc_new, local_rc2_cbc_free, local_rc2_cbc_set_iv, local_rc2_cbc_operate, }; bluez-5.82/ell/PaxHeaders/ell.h0000644000000000000000000000005014773211450013353 xustar0020 atime=1743590807 20 ctime=1743591288 bluez-5.82/ell/ell.h0000644000000000000000000000164114773211450013036 0ustar00rootroot#include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include bluez-5.82/ell/PaxHeaders/checksum.c0000644000000000000000000000005014770744372014407 xustar0020 atime=1743575481 20 ctime=1743591276 bluez-5.82/ell/checksum.c0000644000000000000000000002364314770744372014100 0ustar00rootroot/* * Embedded Linux library * Copyright (C) 2011-2014 Intel Corporation * * SPDX-License-Identifier: LGPL-2.1-or-later */ #ifdef HAVE_CONFIG_H #include #endif #define _GNU_SOURCE #include #include #include #include #include #include "useful.h" #include "checksum.h" #include "private.h" #ifndef HAVE_LINUX_IF_ALG_H #ifndef HAVE_LINUX_TYPES_H typedef uint8_t __u8; typedef uint16_t __u16; typedef uint32_t __u32; #else #include #endif #ifndef AF_ALG #define AF_ALG 38 #define PF_ALG AF_ALG #endif struct sockaddr_alg { __u16 salg_family; __u8 salg_type[14]; __u32 salg_feat; __u32 salg_mask; __u8 salg_name[64]; }; /* Socket options */ #define ALG_SET_KEY 1 #else #include #endif #ifndef SOL_ALG #define SOL_ALG 279 #endif struct checksum_info { const char *name; uint8_t digest_len; bool supported; }; static struct checksum_info checksum_algs[] = { [L_CHECKSUM_MD4] = { .name = "md4", .digest_len = 16 }, [L_CHECKSUM_MD5] = { .name = "md5", .digest_len = 16 }, [L_CHECKSUM_SHA1] = { .name = "sha1", .digest_len = 20 }, [L_CHECKSUM_SHA224] = { .name = "sha224", .digest_len = 28 }, [L_CHECKSUM_SHA256] = { .name = "sha256", .digest_len = 32 }, [L_CHECKSUM_SHA384] = { .name = "sha384", .digest_len = 48 }, [L_CHECKSUM_SHA512] = { .name = "sha512", .digest_len = 64 }, [L_CHECKSUM_SHA3_224] = { .name = "sha3-224", .digest_len = 28 }, [L_CHECKSUM_SHA3_256] = { .name = "sha3-256", .digest_len = 32 }, [L_CHECKSUM_SHA3_384] = { .name = "sha3-384", .digest_len = 48 }, [L_CHECKSUM_SHA3_512] = { .name = "sha3-512", .digest_len = 64 }, }; static struct checksum_info checksum_cmac_aes_alg = { .name = "cmac(aes)", .digest_len = 16 }; static struct checksum_info checksum_hmac_algs[] = { [L_CHECKSUM_MD4] = { .name = "hmac(md4)", .digest_len = 16 }, [L_CHECKSUM_MD5] = { .name = "hmac(md5)", .digest_len = 16 }, [L_CHECKSUM_SHA1] = { .name = "hmac(sha1)", .digest_len = 20 }, [L_CHECKSUM_SHA224] = { .name = "hmac(sha224)", .digest_len = 28 }, [L_CHECKSUM_SHA256] = { .name = "hmac(sha256)", .digest_len = 32 }, [L_CHECKSUM_SHA384] = { .name = "hmac(sha384)", .digest_len = 48 }, [L_CHECKSUM_SHA512] = { .name = "hmac(sha512)", .digest_len = 64 }, [L_CHECKSUM_SHA3_224] = { .name = "hmac(sha3-224)", .digest_len = 28 }, [L_CHECKSUM_SHA3_256] = { .name = "hmac(sha3-256)", .digest_len = 32 }, [L_CHECKSUM_SHA3_384] = { .name = "hmac(sha3-384)", .digest_len = 48 }, [L_CHECKSUM_SHA3_512] = { .name = "hmac(sha3-512)", .digest_len = 64 }, }; static const struct { struct checksum_info *list; size_t n; } checksum_info_table[] = { { checksum_algs, L_ARRAY_SIZE(checksum_algs) }, { &checksum_cmac_aes_alg, 1 }, { checksum_hmac_algs, L_ARRAY_SIZE(checksum_hmac_algs) }, {} }; /** * SECTION:checksum * @short_description: Checksum handling * * Checksum handling */ #define is_valid_index(array, i) ((i) >= 0 && (i) < L_ARRAY_SIZE(array)) /** * l_checksum: * * Opaque object representing the checksum. */ struct l_checksum { int sk; const struct checksum_info *alg_info; }; static int create_alg(const char *alg) { struct sockaddr_alg salg; int sk; sk = socket(PF_ALG, SOCK_SEQPACKET | SOCK_CLOEXEC, 0); if (sk < 0) return -1; memset(&salg, 0, sizeof(salg)); salg.salg_family = AF_ALG; strcpy((char *) salg.salg_type, "hash"); strcpy((char *) salg.salg_name, alg); if (bind(sk, (struct sockaddr *) &salg, sizeof(salg)) < 0) { close(sk); return -1; } return sk; } static struct l_checksum *checksum_new_common(const char *alg, int sockopt, const void *data, size_t len, struct checksum_info *info) { struct l_checksum *checksum; int fd; fd = create_alg(alg); if (fd < 0) return NULL; if (data) { if (setsockopt(fd, SOL_ALG, sockopt, data, len) < 0) { close(fd); return NULL; } } checksum = l_new(struct l_checksum, 1); checksum->sk = accept4(fd, NULL, 0, SOCK_CLOEXEC); close(fd); if (checksum->sk < 0) { l_free(checksum); return NULL; } checksum->alg_info = info; return checksum; } /** * l_checksum_new: * @type: checksum type * * Creates new #l_checksum, using the checksum algorithm @type. * * Returns: a newly allocated #l_checksum object. **/ LIB_EXPORT struct l_checksum *l_checksum_new(enum l_checksum_type type) { if (!is_valid_index(checksum_algs, type) || !checksum_algs[type].name) return NULL; return checksum_new_common(checksum_algs[type].name, 0, NULL, 0, &checksum_algs[type]); } LIB_EXPORT struct l_checksum *l_checksum_new_cmac_aes(const void *key, size_t key_len) { return checksum_new_common("cmac(aes)", ALG_SET_KEY, key, key_len, &checksum_cmac_aes_alg); } LIB_EXPORT struct l_checksum *l_checksum_new_hmac(enum l_checksum_type type, const void *key, size_t key_len) { if (!is_valid_index(checksum_hmac_algs, type) || !checksum_hmac_algs[type].name) return NULL; return checksum_new_common(checksum_hmac_algs[type].name, ALG_SET_KEY, key, key_len, &checksum_hmac_algs[type]); } /** * l_checksum_clone: * @checksum: parent checksum object * * Creates a new checksum with an independent copy of parent @checksum's * state. l_checksum_get_digest can then be called on the parent or the * clone without affecting the state of the other object. **/ LIB_EXPORT struct l_checksum *l_checksum_clone(struct l_checksum *checksum) { struct l_checksum *clone; if (unlikely(!checksum)) return NULL; clone = l_new(struct l_checksum, 1); clone->sk = accept4(checksum->sk, NULL, 0, SOCK_CLOEXEC); if (clone->sk < 0) { l_free(clone); return NULL; } clone->alg_info = checksum->alg_info; return clone; } /** * l_checksum_free: * @checksum: checksum object * * Frees the memory allocated for @checksum. **/ LIB_EXPORT void l_checksum_free(struct l_checksum *checksum) { if (unlikely(!checksum)) return; close(checksum->sk); l_free(checksum); } /** * l_checksum_reset: * @checksum: checksum object * * Resets the internal state of @checksum. **/ LIB_EXPORT void l_checksum_reset(struct l_checksum *checksum) { if (unlikely(!checksum)) return; send(checksum->sk, NULL, 0, 0); } /** * l_checksum_update: * @checksum: checksum object * @data: data pointer * @len: length of data * * Updates checksum from @data pointer with @len bytes. * * Returns: true if the operation succeeded, false otherwise. **/ LIB_EXPORT bool l_checksum_update(struct l_checksum *checksum, const void *data, size_t len) { ssize_t written; if (unlikely(!checksum)) return false; written = send(checksum->sk, data, len, MSG_MORE); if (written < 0) return false; return true; } /** * l_checksum_updatev: * @checksum: checksum object * @iov: iovec pointer * @iov_len: Number of iovec entries * * This is a iovec based version of l_checksum_update; it updates the checksum * based on contents of @iov and @iov_len. * * Returns: true if the operation succeeded, false otherwise. **/ LIB_EXPORT bool l_checksum_updatev(struct l_checksum *checksum, const struct iovec *iov, size_t iov_len) { struct msghdr msg; ssize_t written; if (unlikely(!checksum)) return false; if (unlikely(!iov) || unlikely(!iov_len)) return false; memset(&msg, 0, sizeof(msg)); msg.msg_iov = (struct iovec *) iov; msg.msg_iovlen = iov_len; written = sendmsg(checksum->sk, &msg, MSG_MORE); if (written < 0) return false; return true; } /** * l_checksum_get_digest: * @checksum: checksum object * @digest: output data buffer * @len: length of output buffer * * Writes the digest from @checksum as raw binary data into the provided * buffer or, if the buffer is shorter, the initial @len bytes of the digest * data. * * Returns: Number of bytes written, or negative value if an error occurred. **/ LIB_EXPORT ssize_t l_checksum_get_digest(struct l_checksum *checksum, void *digest, size_t len) { ssize_t result; if (unlikely(!checksum)) return -EINVAL; if (unlikely(!digest)) return -EFAULT; if (unlikely(!len)) return -EINVAL; result = recv(checksum->sk, digest, len, 0); if (result < 0) return -errno; if ((size_t) result < len && result < checksum->alg_info->digest_len) return -EIO; return result; } /** * l_checksum_get_string: * @checksum: checksum object * * Gets the digest from @checksum as hex encoded string. * * Returns: a newly allocated hex string **/ LIB_EXPORT char *l_checksum_get_string(struct l_checksum *checksum) { unsigned char digest[64]; if (unlikely(!checksum)) return NULL; l_checksum_get_digest(checksum, digest, sizeof(digest)); return l_util_hexstring(digest, checksum->alg_info->digest_len); } static void init_supported() { static bool initialized = false; struct sockaddr_alg salg; int sk; unsigned int i, j; if (likely(initialized)) return; initialized = true; sk = socket(PF_ALG, SOCK_SEQPACKET | SOCK_CLOEXEC, 0); if (sk < 0) return; memset(&salg, 0, sizeof(salg)); salg.salg_family = AF_ALG; strcpy((char *) salg.salg_type, "hash"); for (i = 0; checksum_info_table[i].list; i++) for (j = 0; j < checksum_info_table[i].n; j++) { struct checksum_info *info; info = &checksum_info_table[i].list[j]; if (!info->name) continue; strcpy((char *) salg.salg_name, info->name); if (bind(sk, (struct sockaddr *) &salg, sizeof(salg)) < 0) continue; info->supported = true; } close(sk); } LIB_EXPORT bool l_checksum_is_supported(enum l_checksum_type type, bool check_hmac) { const struct checksum_info *list; init_supported(); if (!check_hmac) { if (!is_valid_index(checksum_algs, type)) return false; list = checksum_algs; } else { if (!is_valid_index(checksum_hmac_algs, type)) return false; list = checksum_hmac_algs; } return list[type].supported; } LIB_EXPORT bool l_checksum_cmac_aes_supported() { init_supported(); return checksum_cmac_aes_alg.supported; } LIB_EXPORT ssize_t l_checksum_digest_length(enum l_checksum_type type) { return is_valid_index(checksum_algs, type) ? checksum_algs[type].digest_len : 0; } bluez-5.82/ell/PaxHeaders/utf8.c0000644000000000000000000000005014601374755013470 xustar0020 atime=1743575499 20 ctime=1743591276 bluez-5.82/ell/utf8.c0000644000000000000000000002433014601374755013153 0ustar00rootroot/* * Embedded Linux library * Copyright (C) 2011-2014 Intel Corporation * Copyright (C) 2024 Cruise, LLC * * SPDX-License-Identifier: LGPL-2.1-or-later */ #ifdef HAVE_CONFIG_H #include #endif #include #include #include "strv.h" #include "utf8.h" #include "private.h" #include "useful.h" /** * SECTION:utf8 * @short_description: UTF-8 utility function * * UTF-8 string handling support */ LIB_EXPORT unsigned char l_ascii_table[256] = { [0x00 ... 0x08] = L_ASCII_CNTRL, [0x09 ... 0x0D] = L_ASCII_CNTRL | L_ASCII_SPACE, [0x0E ... 0x1F] = L_ASCII_CNTRL, [0x20] = L_ASCII_PRINT | L_ASCII_SPACE, [0x21 ... 0x2F] = L_ASCII_PRINT | L_ASCII_PUNCT, [0x30 ... 0x39] = L_ASCII_DIGIT | L_ASCII_XDIGIT | L_ASCII_PRINT, [0x3A ... 0x40] = L_ASCII_PRINT | L_ASCII_PUNCT, [0x41 ... 0x46] = L_ASCII_PRINT | L_ASCII_XDIGIT | L_ASCII_UPPER, [0x47 ... 0x5A] = L_ASCII_PRINT | L_ASCII_UPPER, [0x5B ... 0x60] = L_ASCII_PRINT | L_ASCII_PUNCT, [0x61 ... 0x66] = L_ASCII_PRINT | L_ASCII_XDIGIT | L_ASCII_LOWER, [0x67 ... 0x7A] = L_ASCII_PRINT | L_ASCII_LOWER, [0x7B ... 0x7E] = L_ASCII_PRINT | L_ASCII_PUNCT, [0x7F] = L_ASCII_CNTRL, [0x80 ... 0xFF] = 0, }; /** * l_ascii_strdown * @str: a pointer to an ASCII string * @len: maximum bytes to process or negative if string is null terminated * * Returns: Newly allocated string with all upper case characters converted * to lower case. **/ LIB_EXPORT char *l_ascii_strdown(const char *str, ssize_t len) { size_t slen; size_t i; char *ret; if (!str) return NULL; if (len < 0) slen = strlen(str); else slen = len; ret = l_malloc(slen + 1); for (i = 0; i < slen && str[i]; i++) ret[i] = l_ascii_tolower(str[i]); ret[i] = '\0'; return ret; } /** * l_ascii_strup * @str: a pointer to an ASCII string * @len: maximum bytes to process or negative if string is null terminated * * Returns: Newly allocated string with all lower case characters converted * to upper case. **/ LIB_EXPORT char *l_ascii_strup(const char *str, ssize_t len) { size_t slen; size_t i; char *ret; if (!str) return NULL; if (len < 0) slen = strlen(str); else slen = len; ret = l_malloc(slen + 1); for (i = 0; i < slen && str[i]; i++) ret[i] = l_ascii_toupper(str[i]); ret[i] = '\0'; return ret; } static inline bool __attribute__ ((always_inline)) valid_unicode(wchar_t c) { if (c <= 0xd7ff) return true; if (c < 0xe000 || c > 0x10ffff) return false; if (c >= 0xfdd0 && c <= 0xfdef) return false; if ((c & 0xfffe) == 0xfffe) return false; return true; } /** * l_utf8_get_codepoint * @str: a pointer to codepoint data * @len: maximum bytes to read * @cp: destination for codepoint * * Returns: number of bytes read, or -1 for invalid coddepoint **/ LIB_EXPORT int l_utf8_get_codepoint(const char *str, size_t len, wchar_t *cp) { static const wchar_t mins[3] = { 1 << 7, 1 << 11, 1 << 16 }; unsigned int expect_bytes; wchar_t val; size_t i; if (len == 0) return 0; if ((signed char) str[0] > 0) { *cp = str[0]; return 1; } expect_bytes = __builtin_clz(~((unsigned int)str[0] << 24)); if (expect_bytes < 2 || expect_bytes > 4) goto error; if (expect_bytes > len) goto error; val = str[0] & (0xff >> (expect_bytes + 1)); for (i = 1; i < expect_bytes; i++) { if ((str[i] & 0xc0) != 0x80) goto error; val <<= 6; val |= str[i] & 0x3f; } if (val < mins[expect_bytes - 2]) goto error; if (valid_unicode(val) == false) goto error; *cp = val; return expect_bytes; error: return -1; } /** * l_utf8_validate: * @str: a pointer to character data * @len: max bytes to validate * @end: return location for end of valid data * * Validates UTF-8 encoded text. If @end is non-NULL, then the end of * the valid range will be stored there (i.e. the start of the first * invalid character if some bytes were invalid, or the end of the text * being validated otherwise). * * Returns: Whether the text was valid UTF-8 **/ LIB_EXPORT bool l_utf8_validate(const char *str, size_t len, const char **end) { size_t pos = 0; int ret; wchar_t val; while (pos < len && str[pos]) { ret = l_utf8_get_codepoint(str + pos, len - pos, &val); if (ret < 0) goto error; pos += ret; } error: if (end) *end = str + pos; if (pos != len) return false; return true; } /** * l_utf8_strlen: * @str: a pointer to character data * * Computes the number of UTF-8 characters (not bytes) in the string given * by @str. * * Returns: The number of UTF-8 characters in the string **/ LIB_EXPORT size_t l_utf8_strlen(const char *str) { size_t l = 0; size_t i; unsigned char b; for (i = 0; str[i]; i++) { b = str[i]; if ((b >> 6) == 2) l += 1; } return i - l; } static inline int __attribute__ ((always_inline)) utf8_length(wchar_t c) { if (c <= 0x7f) return 1; if (c <= 0x7ff) return 2; if (c <= 0xffff) return 3; return 4; } static inline uint16_t __attribute__ ((always_inline)) surrogate_value(uint16_t h, uint16_t l) { return 0x10000 + (h - 0xd800) * 0x400 + l - 0xdc00; } /* * l_utf8_from_wchar: * @c: a wide-character to convert * @out_buf: Buffer to write out to * * Assumes c is valid unicode and out_buf contains enough space for a single * utf8 character (maximum 4 bytes) * Returns: number of characters written */ LIB_EXPORT size_t l_utf8_from_wchar(wchar_t c, char *out_buf) { int len = utf8_length(c); int i; if (len == 1) { out_buf[0] = c; return 1; } for (i = len - 1; i; i--) { out_buf[i] = (c & 0x3f) | 0x80; c >>= 6; } out_buf[0] = (0xff << (8 - len)) | c; return len; } /** * l_utf8_from_utf16: * @utf16: Array of UTF16 characters * @utf16_size: The size of the @utf16 array in bytes. Must be a multiple of 2. * * Returns: A newly-allocated buffer containing UTF16 encoded string converted * to UTF8. The UTF8 string will always be null terminated, even if the * original UTF16 string was not. **/ LIB_EXPORT char *l_utf8_from_utf16(const void *utf16, ssize_t utf16_size) { char *utf8; size_t utf8_len = 0; wchar_t high_surrogate = 0; ssize_t i = 0; uint16_t in; wchar_t c; if (unlikely(utf16_size % 2)) return NULL; while (utf16_size < 0 || i < utf16_size) { in = l_get_u16(utf16 + i); if (!in) break; if (in >= 0xdc00 && in < 0xe000) { if (high_surrogate) c = surrogate_value(high_surrogate, in); else return NULL; high_surrogate = 0; } else { if (high_surrogate) return NULL; if (in >= 0xd800 && in < 0xdc00) { high_surrogate = in; goto next; } c = in; } if (!valid_unicode(c)) return NULL; utf8_len += utf8_length(c); next: i += 2; } if (high_surrogate) return NULL; utf8 = l_malloc(utf8_len + 1); utf8_len = 0; i = 0; while (utf16_size < 0 || i < utf16_size) { in = l_get_u16(utf16 + i); if (!in) break; if (in >= 0xd800 && in < 0xdc00) { high_surrogate = in; i += 2; in = l_get_u16(utf16 + i); c = surrogate_value(high_surrogate, in); } else c = in; utf8_len += l_utf8_from_wchar(c, utf8 + utf8_len); i += 2; } utf8[utf8_len] = '\0'; return utf8; } /** * l_utf8_to_utf16: * @utf8: UTF8 formatted string * @out_size: The size in bytes of the converted utf16 string * * Converts a UTF8 formatted string to UTF16. It is assumed that the string * is valid UTF8 and no sanity checking is performed. * * Returns: A newly-allocated buffer containing UTF8 encoded string converted * to UTF16. The UTF16 string will always be null terminated. **/ LIB_EXPORT void *l_utf8_to_utf16(const char *utf8, size_t *out_size) { const char *c; wchar_t wc; int len; uint16_t *utf16; size_t n_utf16; if (unlikely(!utf8)) return NULL; c = utf8; n_utf16 = 0; while (*c) { len = l_utf8_get_codepoint(c, 4, &wc); if (len < 0) return NULL; if (wc < 0x10000) n_utf16 += 1; else n_utf16 += 2; c += len; } utf16 = l_malloc((n_utf16 + 1) * 2); c = utf8; n_utf16 = 0; while (*c) { len = l_utf8_get_codepoint(c, 4, &wc); if (wc >= 0x10000) { utf16[n_utf16++] = (wc - 0x1000) / 0x400 + 0xd800; utf16[n_utf16++] = (wc - 0x1000) % 0x400 + 0xdc00; } else utf16[n_utf16++] = wc; c += len; } utf16[n_utf16] = 0; if (out_size) *out_size = (n_utf16 + 1) * 2; return utf16; } /** * l_utf8_from_ucs2be: * @ucs2be: Array of UCS2 characters in big-endian format * @ucs2be_size: The size of the @ucs2 array in bytes. Must be a multiple of 2. * * Returns: A newly-allocated buffer containing UCS2BE encoded string converted * to UTF8. The UTF8 string will always be null terminated, even if the * original UCS2BE string was not. **/ LIB_EXPORT char *l_utf8_from_ucs2be(const void *ucs2be, ssize_t ucs2be_size) { char *utf8; size_t utf8_len = 0; ssize_t i = 0; uint16_t in; if (unlikely(ucs2be_size % 2)) return NULL; while (ucs2be_size < 0 || i < ucs2be_size) { in = l_get_be16(ucs2be + i); if (!in) break; if (in >= 0xd800 && in < 0xe000) return NULL; if (!valid_unicode(in)) return NULL; utf8_len += utf8_length(in); i += 2; } utf8 = l_malloc(utf8_len + 1); utf8_len = 0; i = 0; while (ucs2be_size < 0 || i < ucs2be_size) { in = l_get_be16(ucs2be + i); if (!in) break; utf8_len += l_utf8_from_wchar(in, utf8 + utf8_len); i += 2; } utf8[utf8_len] = '\0'; return utf8; } /** * l_utf8_to_ucs2be: * @utf8: UTF8 formatted string * @out_size: The size in bytes of the converted ucs2be string * * Converts a UTF8 formatted string to UCS2BE. It is assumed that the string * is valid UTF8 and no sanity checking is performed. * * Returns: A newly-allocated buffer containing UTF8 encoded string converted * to UCS2BE. The UCS2BE string will always be null terminated. **/ LIB_EXPORT void *l_utf8_to_ucs2be(const char *utf8, size_t *out_size) { const char *c; wchar_t wc; int len; uint16_t *ucs2be; size_t n_ucs2be; if (unlikely(!utf8)) return NULL; c = utf8; n_ucs2be = 0; while (*c) { len = l_utf8_get_codepoint(c, 4, &wc); if (len < 0) return NULL; if (wc >= 0x10000) return NULL; n_ucs2be += 1; c += len; } ucs2be = l_malloc((n_ucs2be + 1) * 2); c = utf8; n_ucs2be = 0; while (*c) { len = l_utf8_get_codepoint(c, 4, &wc); ucs2be[n_ucs2be++] = L_CPU_TO_BE16(wc); c += len; } ucs2be[n_ucs2be] = 0; if (out_size) *out_size = (n_ucs2be + 1) * 2; return ucs2be; } bluez-5.82/ell/PaxHeaders/timeout.h0000644000000000000000000000005014667515664014305 xustar0020 atime=1743575449 20 ctime=1743591276 bluez-5.82/ell/timeout.h0000644000000000000000000000224414667515664013770 0ustar00rootroot/* * Embedded Linux library * Copyright (C) 2011-2014 Intel Corporation * * SPDX-License-Identifier: LGPL-2.1-or-later */ #ifndef __ELL_TIMEOUT_H #define __ELL_TIMEOUT_H #include #ifdef __cplusplus extern "C" { #endif struct l_timeout; typedef void (*l_timeout_notify_cb_t) (struct l_timeout *timeout, void *user_data); typedef void (*l_timeout_destroy_cb_t) (void *user_data); struct l_timeout *l_timeout_create(unsigned int seconds, l_timeout_notify_cb_t callback, void *user_data, l_timeout_destroy_cb_t destroy); struct l_timeout *l_timeout_create_ms(uint64_t milliseconds, l_timeout_notify_cb_t callback, void *user_data, l_timeout_destroy_cb_t destroy); void l_timeout_modify(struct l_timeout *timeout, unsigned int seconds); void l_timeout_modify_ms(struct l_timeout *timeout, uint64_t milliseconds); void l_timeout_remove(struct l_timeout *timeout); void l_timeout_set_callback(struct l_timeout *timeout, l_timeout_notify_cb_t callback, void *user_data, l_timeout_destroy_cb_t destroy); bool l_timeout_remaining(struct l_timeout *timeout, uint64_t *remaining); #ifdef __cplusplus } #endif #endif /* __ELL_TIMEOUT_H */ bluez-5.82/ell/PaxHeaders/timeout.c0000644000000000000000000000005014667515664014300 xustar0020 atime=1743575468 20 ctime=1743591276 bluez-5.82/ell/timeout.c0000644000000000000000000001644414667515664013772 0ustar00rootroot/* * Embedded Linux library * Copyright (C) 2011-2014 Intel Corporation * * SPDX-License-Identifier: LGPL-2.1-or-later */ #ifdef HAVE_CONFIG_H #include #endif #define _GNU_SOURCE #include #include #include #include #include #include #include #include #include "useful.h" #include "timeout.h" #include "main-private.h" #include "private.h" #include "time-private.h" /** * SECTION:timeout * @short_description: Timeout support * * Timeout support */ /** * l_timeout: * * Opaque object representing the timeout. */ struct l_timeout { int fd; l_timeout_notify_cb_t callback; l_timeout_destroy_cb_t destroy; void *user_data; }; static void timeout_destroy(void *user_data) { struct l_timeout *timeout = user_data; close(timeout->fd); timeout->fd = -1; if (timeout->destroy) timeout->destroy(timeout->user_data); } static void timeout_callback(int fd, uint32_t events, void *user_data) { struct l_timeout *timeout = user_data; uint64_t expired; ssize_t result; result = read(timeout->fd, &expired, sizeof(expired)); if (result != sizeof(expired)) return; if (timeout->callback) timeout->callback(timeout, timeout->user_data); } static inline int timeout_set(int fd, unsigned int seconds, long nanoseconds) { struct itimerspec itimer; memset(&itimer, 0, sizeof(itimer)); itimer.it_interval.tv_sec = 0; itimer.it_interval.tv_nsec = 0; itimer.it_value.tv_sec = seconds; itimer.it_value.tv_nsec = nanoseconds; return timerfd_settime(fd, 0, &itimer, NULL); } static bool convert_ms(uint64_t milliseconds, unsigned int *seconds, long *nanoseconds) { uint64_t big_seconds = milliseconds / 1000; if (big_seconds > UINT_MAX) return false; *seconds = big_seconds; *nanoseconds = (milliseconds % 1000) * 1000000L; return true; } /** * timeout_create_with_nanoseconds: * @seconds: number of seconds * @nanoseconds: number of nanoseconds * @callback: timeout callback function * @user_data: user data provided to timeout callback function * @destroy: destroy function for user data * * Create new timeout callback handling. * * The timeout will only fire once. The timeout handling needs to be rearmed * with one of the l_timeout_modify functions to trigger again. * * Returns: a newly allocated #l_timeout object. On failure, the function * returns NULL. **/ static struct l_timeout *timeout_create_with_nanoseconds(unsigned int seconds, long nanoseconds, l_timeout_notify_cb_t callback, void *user_data, l_timeout_destroy_cb_t destroy) { struct l_timeout *timeout; int err; if (unlikely(!callback)) return NULL; timeout = l_new(struct l_timeout, 1); timeout->callback = callback; timeout->destroy = destroy; timeout->user_data = user_data; timeout->fd = timerfd_create(CLOCK_MONOTONIC, TFD_NONBLOCK | TFD_CLOEXEC); if (timeout->fd < 0) { l_free(timeout); return NULL; } if (seconds > 0 || nanoseconds > 0) { if (timeout_set(timeout->fd, seconds, nanoseconds) < 0) { close(timeout->fd); l_free(timeout); return NULL; } } err = watch_add(timeout->fd, EPOLLIN | EPOLLONESHOT, timeout_callback, timeout, timeout_destroy); if (err < 0) { l_free(timeout); return NULL; } return timeout; } /** * l_timeout_create: * @seconds: timeout in seconds * @callback: timeout callback function * @user_data: user data provided to timeout callback function * @destroy: destroy function for user data * * Create new timeout callback handling. * * The timeout will only fire once. The timeout handling needs to be rearmed * with one of the l_timeout_modify functions to trigger again. * * Returns: a newly allocated #l_timeout object. On failure, the function * returns NULL. **/ LIB_EXPORT struct l_timeout *l_timeout_create(unsigned int seconds, l_timeout_notify_cb_t callback, void *user_data, l_timeout_destroy_cb_t destroy) { return timeout_create_with_nanoseconds(seconds, 0, callback, user_data, destroy); } /** * l_timeout_create_ms: * @milliseconds: timeout in milliseconds * @callback: timeout callback function * @user_data: user data provided to timeout callback function * @destroy: destroy function for user data * * Create new timeout callback handling. * * The timeout will only fire once. The timeout handling needs to be rearmed * with one of the l_timeout_modify functions to trigger again. * * Returns: a newly allocated #l_timeout object. On failure, the function * returns NULL. **/ LIB_EXPORT struct l_timeout *l_timeout_create_ms(uint64_t milliseconds, l_timeout_notify_cb_t callback, void *user_data, l_timeout_destroy_cb_t destroy) { unsigned int seconds; long nanoseconds; if (!convert_ms(milliseconds, &seconds, &nanoseconds)) return NULL; return timeout_create_with_nanoseconds(seconds, nanoseconds, callback, user_data, destroy); } /** * l_timeout_modify: * @timeout: timeout object * @seconds: timeout in seconds * * Modify an existing @timeout and rearm it. **/ LIB_EXPORT void l_timeout_modify(struct l_timeout *timeout, unsigned int seconds) { if (unlikely(!timeout)) return; if (unlikely(timeout->fd < 0)) return; if (seconds > 0) { if (timeout_set(timeout->fd, seconds, 0) < 0) return; } watch_modify(timeout->fd, EPOLLIN | EPOLLONESHOT, true); } /** * l_timeout_modify_ms: * @timeout: timeout object * @milliseconds: number of milliseconds * * Modify an existing @timeout and rearm it. **/ LIB_EXPORT void l_timeout_modify_ms(struct l_timeout *timeout, uint64_t milliseconds) { if (unlikely(!timeout)) return; if (unlikely(timeout->fd < 0)) return; if (milliseconds > 0) { unsigned int sec; long nanosec; if (!convert_ms(milliseconds, &sec, &nanosec) || timeout_set(timeout->fd, sec, nanosec) < 0) return; } watch_modify(timeout->fd, EPOLLIN | EPOLLONESHOT, true); } /** * l_timeout_remove: * @timeout: timeout object * * Remove timeout handling. **/ LIB_EXPORT void l_timeout_remove(struct l_timeout *timeout) { if (unlikely(!timeout)) return; watch_remove(timeout->fd, false); l_free(timeout); } /** * l_timeout_set_callback: * @timeout: timeout object * @callback: The new callback * @user_data: The new user_data * @destroy: The new destroy function * * Sets the new notify callback for @timeout. If the old user_data object had * a destroy function set, then that function will be called. */ LIB_EXPORT void l_timeout_set_callback(struct l_timeout *timeout, l_timeout_notify_cb_t callback, void *user_data, l_timeout_destroy_cb_t destroy) { if (unlikely(!timeout)) return; if (timeout->destroy) timeout->destroy(timeout->user_data); timeout->callback = callback; timeout->user_data = user_data; timeout->destroy = destroy; } /** * l_timeout_get_remaining: * * Get the remaining time for a timeout in microseconds * * @timeout: timeout object * @remaining: microseconds remaining on timer * * Returns: True if successfully got remaining time * False if failure to get remaining time **/ LIB_EXPORT bool l_timeout_remaining(struct l_timeout *timeout, uint64_t *remaining) { struct itimerspec current; if (unlikely(!timeout)) return false; if (timerfd_gettime(timeout->fd, ¤t) < 0) return false; if (remaining) *remaining = _time_from_timespec(¤t.it_value); return true; } bluez-5.82/ell/PaxHeaders/cert.c0000644000000000000000000000005014721132752013527 xustar0020 atime=1743575484 20 ctime=1743591276 bluez-5.82/ell/cert.c0000644000000000000000000013716714721132752013227 0ustar00rootroot/* * Embedded Linux library * Copyright (C) 2018 Intel Corporation * * SPDX-License-Identifier: LGPL-2.1-or-later */ #ifdef HAVE_CONFIG_H #include #endif #include #include #include #include #include "private.h" #include "useful.h" #include "key.h" #include "queue.h" #include "asn1-private.h" #include "cipher.h" #include "pem-private.h" #include "time.h" #include "time-private.h" #include "utf8.h" #include "cert.h" #include "cert-private.h" #include "tls.h" #include "tls-private.h" #include "missing.h" #define X509_CERTIFICATE_POS 0 #define X509_TBSCERTIFICATE_POS 0 #define X509_TBSCERT_VERSION_POS ASN1_CONTEXT_EXPLICIT(0) #define X509_TBSCERT_SERIAL_POS 0 #define X509_TBSCERT_SIGNATURE_POS 1 #define X509_ALGORITHM_ID_ALGORITHM_POS 0 #define X509_ALGORITHM_ID_PARAMS_POS 1 #define X509_TBSCERT_ISSUER_DN_POS 2 #define X509_TBSCERT_VALIDITY_POS 3 #define X509_TBSCERT_SUBJECT_DN_POS 4 #define X509_TBSCERT_SUBJECT_KEY_POS 5 #define X509_SUBJECT_KEY_ALGORITHM_POS 0 #define X509_SUBJECT_KEY_VALUE_POS 1 #define X509_TBSCERT_ISSUER_UID_POS ASN1_CONTEXT_IMPLICIT(1) #define X509_TBSCERT_SUBJECT_UID_POS ASN1_CONTEXT_IMPLICIT(2) #define X509_TBSCERT_EXTENSIONS_POS ASN1_CONTEXT_EXPLICIT(3) #define X509_SIGNATURE_ALGORITHM_POS 1 #define X509_SIGNATURE_VALUE_POS 2 struct l_cert { enum l_cert_key_type pubkey_type; struct l_cert *issuer; struct l_cert *issued; size_t asn1_len; uint8_t asn1[]; }; struct l_certchain { struct l_cert *leaf; /* Bottom of the doubly-linked list */ struct l_cert *ca; /* Top of the doubly-linked list */ }; static const struct pkcs1_encryption_oid { enum l_cert_key_type key_type; struct asn1_oid oid; } pkcs1_encryption_oids[] = { { /* rsaEncryption */ L_CERT_KEY_RSA, { .asn1_len = 9, .asn1 = { 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x01 } }, }, { /* ecPublicKey */ L_CERT_KEY_ECC, { .asn1_len = 7, .asn1 = { 0x2a, 0x86, 0x48, 0xce, 0x3d, 0x02, 0x01 } }, }, }; static bool cert_set_pubkey_type(struct l_cert *cert) { const uint8_t *key_type; size_t key_type_len; int i; key_type = asn1_der_find_elem_by_path(cert->asn1, cert->asn1_len, ASN1_ID_OID, &key_type_len, X509_CERTIFICATE_POS, X509_TBSCERTIFICATE_POS, X509_TBSCERT_SUBJECT_KEY_POS, X509_SUBJECT_KEY_ALGORITHM_POS, X509_ALGORITHM_ID_ALGORITHM_POS, -1); if (!key_type) return false; for (i = 0; i < (int) L_ARRAY_SIZE(pkcs1_encryption_oids); i++) if (asn1_oid_eq(&pkcs1_encryption_oids[i].oid, key_type_len, key_type)) break; if (i == L_ARRAY_SIZE(pkcs1_encryption_oids)) cert->pubkey_type = L_CERT_KEY_UNKNOWN; else cert->pubkey_type = pkcs1_encryption_oids[i].key_type; return true; } LIB_EXPORT struct l_cert *l_cert_new_from_der(const uint8_t *buf, size_t buf_len) { const uint8_t *seq = buf; size_t seq_len = buf_len; size_t content_len; struct l_cert *cert; /* Sanity check: outer element is a SEQUENCE */ if (seq_len-- < 1 || *seq++ != ASN1_ID_SEQUENCE) return NULL; /* Sanity check: the SEQUENCE spans the whole buffer */ content_len = asn1_parse_definite_length(&seq, &seq_len); if (content_len < 64 || content_len != seq_len) return NULL; /* * We could require the signature algorithm and the key algorithm * to be one of our supported types here but instead we only * require that when the user wants to verify this certificate or * get the public key respectively. */ cert = l_malloc(sizeof(struct l_cert) + buf_len); cert->issuer = NULL; cert->issued = NULL; cert->asn1_len = buf_len; memcpy(cert->asn1, buf, buf_len); /* Sanity check: structure is correct up to the Public Key Algorithm */ if (!cert_set_pubkey_type(cert)) { l_free(cert); return NULL; } return cert; } LIB_EXPORT void l_cert_free(struct l_cert *cert) { l_free(cert); } LIB_EXPORT const uint8_t *l_cert_get_der_data(struct l_cert *cert, size_t *out_len) { if (unlikely(!cert)) return NULL; *out_len = cert->asn1_len; return cert->asn1; } LIB_EXPORT const uint8_t *l_cert_get_dn(struct l_cert *cert, size_t *out_len) { if (unlikely(!cert)) return NULL; return asn1_der_find_elem_by_path(cert->asn1, cert->asn1_len, ASN1_ID_SEQUENCE, out_len, X509_CERTIFICATE_POS, X509_TBSCERTIFICATE_POS, X509_TBSCERT_SUBJECT_DN_POS, -1); } static uint64_t cert_parse_asn1_time(const uint8_t *data, size_t len, uint8_t tag) { struct tm tm = {}; int tz_hours; int tz_mins; int century; int msecs = 0; time_t tt; unsigned int i; for (i = 0; i < len && i < 15; i++) if (unlikely(!l_ascii_isdigit(data[i]))) break; if (tag == ASN1_ID_UTCTIME) { if (unlikely(!L_IN_SET(i, 10, 12))) return L_TIME_INVALID; century = 19; } else if (tag == ASN1_ID_GENERALIZEDTIME) { if (unlikely(!L_IN_SET(i, 10, 12, 14))) return L_TIME_INVALID; century = (data[0] - '0') * 10 + (data[1] - '0'); if (century < 19) return L_TIME_INVALID; if (len >= i + 4 && data[i] == '.') { if (unlikely(!l_ascii_isdigit(data[i + 1]) || !l_ascii_isdigit(data[i + 2]) || !l_ascii_isdigit(data[i + 3]))) return L_TIME_INVALID; i++; msecs += (data[i++] - '0') * 100; msecs += (data[i++] - '0') * 10; msecs += (data[i++] - '0'); } data += 2; len -= 2; i -= 2; } else return L_TIME_INVALID; if (unlikely((len != i + 1 || data[i] != 'Z') && (len != i + 5 || (data[i] != '+' && data[i] != '-')))) return L_TIME_INVALID; tm.tm_year = (data[0] - '0') * 10 + (data[1] - '0'); tm.tm_mon = (data[2] - '0') * 10 + (data[3] - '0'); tm.tm_mday = (data[4] - '0') * 10 + (data[5] - '0'); tm.tm_hour = (data[6] - '0') * 10 + (data[7] - '0'); if (unlikely(tm.tm_mon < 1 || tm.tm_mon > 12 || tm.tm_mday < 1 || tm.tm_mday > 31 || tm.tm_hour > 23)) return L_TIME_INVALID; if (i >= 10) { tm.tm_min = (data[8] - '0') * 10 + (data[9] - '0'); if (unlikely(tm.tm_min > 59)) return L_TIME_INVALID; } if (i >= 12) { tm.tm_sec = (data[10] - '0') * 10 + (data[11] - '0'); if (unlikely(tm.tm_sec > 59)) return L_TIME_INVALID; } /* RFC5280 Section 4.1.2.5.1 */ if (tag == ASN1_ID_UTCTIME && tm.tm_year < 50) century = 20; tm.tm_year += (century - 19) * 100; /* Month number is 1-based in UTCTime and 0-based in struct tm */ tm.tm_mon -= 1; tt = timegm(&tm); if (unlikely(tt == (time_t) -1)) return L_TIME_INVALID; if (len == i + 5) { data += i; for (i = 1; i < 5; i++) if (unlikely(!l_ascii_isdigit(data[i]))) return L_TIME_INVALID; tz_hours = (data[1] - '0') * 10 + (data[2] - '0'); tz_mins = (data[3] - '0') * 10 + (data[4] - '0'); if (unlikely(tz_hours > 14 || tz_mins > 59)) return L_TIME_INVALID; /* The sign converts UTC to local so invert it */ if (data[0] == '+') tt -= tz_hours * 3600 + tz_mins * 60; else tt += tz_hours * 3600 + tz_mins * 60; } return (uint64_t) tt * L_USEC_PER_SEC + msecs * L_USEC_PER_MSEC; } LIB_EXPORT bool l_cert_get_valid_times(struct l_cert *cert, uint64_t *out_not_before_time, uint64_t *out_not_after_time) { const uint8_t *validity; const uint8_t *not_before; const uint8_t *not_after; size_t seq_size; size_t not_before_size; size_t not_after_size; uint8_t not_before_tag; uint8_t not_after_tag; uint64_t not_before_time = 0; uint64_t not_after_time = 0; if (unlikely(!cert)) return false; validity = asn1_der_find_elem_by_path(cert->asn1, cert->asn1_len, ASN1_ID_SEQUENCE, &seq_size, X509_CERTIFICATE_POS, X509_TBSCERTIFICATE_POS, X509_TBSCERT_VALIDITY_POS, -1); if (unlikely(!validity)) return false; not_before = asn1_der_find_elem(validity, seq_size, 0, ¬_before_tag, ¬_before_size); if (!not_before) return false; seq_size -= not_before_size + (not_before - validity); validity = not_before + not_before_size; not_after = asn1_der_find_elem(validity, seq_size, 0, ¬_after_tag, ¬_after_size); if (!not_after) return false; if (out_not_before_time) { not_before_time = cert_parse_asn1_time(not_before, not_before_size, not_before_tag); if (not_before_time == L_TIME_INVALID) return false; } if (out_not_after_time) { /* * RFC5280 Section 4.1.2.5: "To indicate that a certificate * has no well-defined expiration date, the notAfter SHOULD * be assigned the GeneralizedTime value of 99991231235959Z." */ if (not_after_size == 15 && !memcmp(not_after, "99991231235959Z", 15)) not_after_time = 0; else { not_after_time = cert_parse_asn1_time(not_after, not_after_size, not_after_tag); if (not_after_time == L_TIME_INVALID) return false; } } if (out_not_before_time) *out_not_before_time = not_before_time; if (out_not_after_time) *out_not_after_time = not_after_time; return true; } const uint8_t *cert_get_extension(struct l_cert *cert, const struct asn1_oid *ext_id, bool *out_critical, size_t *out_len) { const uint8_t *ext, *end; size_t ext_len; if (unlikely(!cert)) return NULL; ext = asn1_der_find_elem_by_path(cert->asn1, cert->asn1_len, ASN1_ID_SEQUENCE, &ext_len, X509_CERTIFICATE_POS, X509_TBSCERTIFICATE_POS, X509_TBSCERT_EXTENSIONS_POS, -1); if (unlikely(!ext)) return NULL; end = ext + ext_len; while (ext < end) { const uint8_t *seq, *oid, *data; uint8_t tag; size_t len, oid_len, data_len; bool critical; seq = asn1_der_find_elem(ext, end - ext, 0, &tag, &len); if (unlikely(!seq || tag != ASN1_ID_SEQUENCE)) return NULL; ext = seq + len; oid = asn1_der_find_elem(seq, len, 0, &tag, &oid_len); if (unlikely(!oid || tag != ASN1_ID_OID)) return NULL; if (!asn1_oid_eq(ext_id, oid_len, oid)) continue; data = asn1_der_find_elem(seq, len, 1, &tag, &data_len); critical = false; if (data && tag == ASN1_ID_BOOLEAN) { if (data_len != 1) return NULL; critical = *data != 0; /* Tolerate BER booleans */ data = asn1_der_find_elem(seq, len, 2, &tag, &data_len); } if (unlikely(!data || tag != ASN1_ID_OCTET_STRING)) return NULL; if (out_critical) *out_critical = critical; if (out_len) *out_len = data_len; return data; } return NULL; } LIB_EXPORT enum l_cert_key_type l_cert_get_pubkey_type(struct l_cert *cert) { if (unlikely(!cert)) return L_CERT_KEY_UNKNOWN; return cert->pubkey_type; } /* * Note: Returns a new l_key object to be freed by the caller. */ LIB_EXPORT struct l_key *l_cert_get_pubkey(struct l_cert *cert) { if (unlikely(!cert)) return NULL; /* Use kernel's ASN.1 certificate parser to find the key data for us */ switch (cert->pubkey_type) { case L_CERT_KEY_RSA: return l_key_new(L_KEY_RSA, cert->asn1, cert->asn1_len); case L_CERT_KEY_ECC: return l_key_new(L_KEY_ECC, cert->asn1, cert->asn1_len); case L_CERT_KEY_UNKNOWN: break; } return NULL; } /* * Note: takes ownership of the certificate. The certificate is * assumed to be new and not linked into any certchain object. */ struct l_certchain *certchain_new_from_leaf(struct l_cert *leaf) { struct l_certchain *chain; chain = l_new(struct l_certchain, 1); chain->leaf = leaf; chain->ca = leaf; return chain; } /* * Note: takes ownership of the certificate. The certificate is * assumed to be new and not linked into any certchain object. */ void certchain_link_issuer(struct l_certchain *chain, struct l_cert *ca) { ca->issued = chain->ca; chain->ca->issuer = ca; chain->ca = ca; } static struct l_cert *certchain_pop_ca(struct l_certchain *chain) { struct l_cert *ca = chain->ca; if (!ca) return NULL; if (ca->issued) { chain->ca = ca->issued; ca->issued->issuer = NULL; ca->issued = NULL; } else { chain->ca = NULL; chain->leaf = NULL; } return ca; } LIB_EXPORT void l_certchain_free(struct l_certchain *chain) { while (chain && chain->ca) l_cert_free(certchain_pop_ca(chain)); l_free(chain); } LIB_EXPORT struct l_cert *l_certchain_get_leaf(struct l_certchain *chain) { if (unlikely(!chain)) return NULL; return chain->leaf; } /* * Call @cb for each certificate in the chain starting from the leaf * certificate. Stop if a call returns @true. */ LIB_EXPORT void l_certchain_walk_from_leaf(struct l_certchain *chain, l_cert_walk_cb_t cb, void *user_data) { struct l_cert *cert; if (unlikely(!chain)) return; for (cert = chain->leaf; cert; cert = cert->issuer) if (cb(cert, user_data)) break; } /* * Call @cb for each certificate in the chain starting from the root * certificate. Stop if a call returns @true. */ LIB_EXPORT void l_certchain_walk_from_ca(struct l_certchain *chain, l_cert_walk_cb_t cb, void *user_data) { struct l_cert *cert; if (unlikely(!chain)) return; for (cert = chain->ca; cert; cert = cert->issued) if (cb(cert, user_data)) break; } static struct l_keyring *cert_set_to_keyring(struct l_cert **certs, char *error) { struct l_keyring *ring; int i = 1; int count; ring = l_keyring_new(); if (!ring) return NULL; for (count = 0; certs[count]; count++); for (; *certs; certs++) { struct l_cert *cert = *certs; struct l_key *key = l_cert_get_pubkey(cert); if (!key) { sprintf(error, "Can't get public key from certificate " "%i / %i in certificate set", i, count); goto cleanup; } if (!l_keyring_link(ring, key)) { l_key_free(key); sprintf(error, "Can't link the public key from " "certificate %i / %i to target keyring", i, count); goto cleanup; } l_key_free_norevoke(key); i++; } return ring; cleanup: l_keyring_free(ring); return NULL; } static bool cert_is_in_set(struct l_cert *cert, struct l_cert **set) { for (; *set; set++) { struct l_cert *cert2 = *set; if (cert == cert2) return true; if (cert->asn1_len == cert2->asn1_len && !memcmp(cert->asn1, cert2->asn1, cert->asn1_len)) return true; } return false; } static struct l_cert **cert_set_filter_by_validity(struct l_queue *set, uint64_t now, int *out_total, int *out_valid) { const struct l_queue_entry *entry; _auto_(l_free) struct l_cert **valid; *out_total = l_queue_length(set); *out_valid = 0; valid = l_new(struct l_cert *, *out_total + 1); for (entry = l_queue_get_entries(set); entry; entry = entry->next) { struct l_cert *cert = entry->data; uint64_t not_before; uint64_t not_after; if (!l_cert_get_valid_times(cert, ¬_before, ¬_after)) return NULL; if (now < not_before || (not_after && now > not_after)) continue; valid[(*out_valid)++] = cert; } return l_steal_ptr(valid); } static struct l_key *cert_try_link(struct l_cert *cert, struct l_keyring *ring) { struct l_key *key; key = l_key_new(L_KEY_RSA, cert->asn1, cert->asn1_len); if (!key) return NULL; if (l_keyring_link(ring, key)) return key; l_key_free(key); return NULL; } #define RETURN_ERROR(msg, args...) \ do { \ if (error) { \ *error = error_buf; \ snprintf(error_buf, sizeof(error_buf), msg, ## args); \ } \ return false; \ } while (0) LIB_EXPORT bool l_certchain_verify(struct l_certchain *chain, struct l_queue *ca_certs, const char **error) { struct l_keyring *ca_ring = NULL; _auto_(l_keyring_free) struct l_keyring *verify_ring = NULL; struct l_cert *cert; struct l_key *prev_key = NULL; int verified = 0; int ca_match = 0; int i; static char error_buf[1024]; int total = 0; uint64_t now; _auto_(l_free) struct l_cert **ca_certs_valid = NULL; int ca_certs_total_count = 0; int ca_certs_valid_count = 0; if (unlikely(!chain || !chain->leaf)) RETURN_ERROR("Chain empty"); for (cert = chain->ca; cert; cert = cert->issued, total++); now = time_realtime_now(); for (cert = chain->ca, i = 0; cert; cert = cert->issued, i++) { uint64_t not_before; uint64_t not_after; char time_str[100]; if (unlikely(!l_cert_get_valid_times(cert, ¬_before, ¬_after))) RETURN_ERROR("Can't parse validity in certificate " "%i / %i", i + 1, total); if (unlikely(now < not_before)) { time_t t = not_before / L_USEC_PER_SEC; struct tm *tm = gmtime(&t); if (!tm || !strftime(time_str, sizeof(time_str), "%a %F %T UTC", tm)) strcpy(time_str, ""); RETURN_ERROR("Certificate %i / %i not valid before %s", i + 1, total, time_str); } if (unlikely(not_after && now > not_after)) { time_t t = not_after / L_USEC_PER_SEC; struct tm *tm = gmtime(&t); if (!tm || !strftime(time_str, sizeof(time_str), "%a %F %T UTC", tm)) strcpy(time_str, ""); RETURN_ERROR("Certificate %i / %i expired on %s", i + 1, total, time_str); } } if (ca_certs) { if (unlikely(l_queue_isempty(ca_certs))) RETURN_ERROR("No trusted CA certificates"); ca_certs_valid = cert_set_filter_by_validity(ca_certs, now, &ca_certs_total_count, &ca_certs_valid_count); if (unlikely(!ca_certs_valid)) RETURN_ERROR("Can't parse validity in CA cert(s)"); if (unlikely(!ca_certs_valid_count)) RETURN_ERROR("All trusted CA certs are expired or " "not-yet-valid"); for (cert = chain->ca, i = 0; cert; cert = cert->issued, i++) if (cert_is_in_set(cert, ca_certs_valid)) { ca_match = i + 1; break; } } verify_ring = l_keyring_new(); if (!verify_ring) RETURN_ERROR("Can't create verify keyring"); cert = chain->ca; /* * For TLS compatibility the trusted root CA certificate is * optionally present in the chain. * * RFC5246 7.4.2: * "Because certificate validation requires that root keys be * distributed independently, the self-signed certificate that * specifies the root certificate authority MAY be omitted from * the chain, under the assumption that the remote end must * already possess it in order to validate it in any case." * * The following is an optimization to skip verifying the root * cert in the chain if it is bitwise-identical to one of the * trusted CA certificates. In that case we don't have to load * all of the trusted certificates into the kernel, link them * to @ca_ring or link @ca_ring to @verify_ring, instead we * load the first certificate into @verify_ring before we set * the restrict mode on it, same as when no trusted CAs are * provided. * * Note this happens to work around a kernel issue preventing * self-signed certificates missing the optional AKID extension * from being linked to a restricted keyring. That issue would * have affected us if the trusted CA set included such * certificate and the same certificate was at the root of * the chain. */ if (ca_certs && !ca_match) { ca_ring = cert_set_to_keyring(ca_certs_valid, error_buf); if (!ca_ring) { if (error) *error = error_buf; return false; } if (!l_keyring_link_nested(verify_ring, ca_ring)) { l_keyring_free(ca_ring); RETURN_ERROR("Can't link CA ring to verify ring"); } } else prev_key = cert_try_link(cert, verify_ring); /* * The top, unverified certificate(s) are linked to the keyring and * we can now force verification of any new certificates linked. */ if (!l_keyring_restrict(verify_ring, L_KEYRING_RESTRICT_ASYM_CHAIN, NULL)) { l_key_free(prev_key); l_keyring_free(ca_ring); RETURN_ERROR("Can't restrict verify keyring"); } if (ca_ring) { /* * Verify the first certificate outside of the loop, then * revoke the trusted CAs' keys so that only the newly * verified cert's public key remains in the ring. */ prev_key = cert_try_link(cert, verify_ring); l_keyring_free(ca_ring); } cert = cert->issued; /* Verify the rest of the chain */ while (prev_key && cert) { struct l_key *new_key = cert_try_link(cert, verify_ring); /* * Free and revoke the issuer's public key again leaving only * new_key in verify_ring to ensure the next certificate linked * is signed by the owner of this key. */ l_key_free(prev_key); prev_key = new_key; cert = cert->issued; verified++; } if (!prev_key) { char str1[100]; char str2[100] = ""; if (ca_match) snprintf(str1, sizeof(str1), "%i / %i matched a trusted" " certificate, root not verified", ca_match, total); else snprintf(str1, sizeof(str1), "root %sverified against " "trusted CA(s)", ca_certs && verified ? "" : "not "); if (ca_certs && !ca_match && !verified && ca_certs_valid_count < ca_certs_total_count) snprintf(str2, sizeof(str2), ", %i out of %i trused " "CA(s) were expired or not-yet-valid", ca_certs_total_count - ca_certs_valid_count, ca_certs_total_count); RETURN_ERROR("Linking certificate %i / %i failed, %s%s", verified + 1, total, str1, str2); } l_key_free(prev_key); return true; } struct l_key *cert_key_from_pkcs8_private_key_info(const uint8_t *der, size_t der_len) { return l_key_new(L_KEY_RSA, der, der_len); } /* * The passphrase, if given, must have been validated as UTF-8 unless the * caller knows that PKCS#12 encryption algorithms are not used. * Use l_utf8_validate. */ struct l_key *cert_key_from_pkcs8_encrypted_private_key_info(const uint8_t *der, size_t der_len, const char *passphrase) { const uint8_t *key_info, *alg_id, *data; uint8_t tag; size_t key_info_len, alg_id_len, data_len, tmp_len; struct l_cipher *alg; uint8_t *decrypted; struct l_key *pkey; bool r; bool is_block; size_t decrypted_len; /* Technically this is BER, not limited to DER */ key_info = asn1_der_find_elem(der, der_len, 0, &tag, &key_info_len); if (!key_info || tag != ASN1_ID_SEQUENCE) return NULL; alg_id = asn1_der_find_elem(key_info, key_info_len, 0, &tag, &alg_id_len); if (!alg_id || tag != ASN1_ID_SEQUENCE) return NULL; data = asn1_der_find_elem(key_info, key_info_len, 1, &tag, &data_len); if (!data || tag != ASN1_ID_OCTET_STRING || data_len < 8 || (data_len & 7) != 0) return NULL; if (asn1_der_find_elem(der, der_len, 2, &tag, &tmp_len)) return NULL; alg = cert_cipher_from_pkcs_alg_id(alg_id, alg_id_len, passphrase, &is_block); if (!alg) return NULL; decrypted = l_malloc(data_len); r = l_cipher_decrypt(alg, data, decrypted, data_len); l_cipher_free(alg); if (!r) { l_free(decrypted); return NULL; } decrypted_len = data_len; /* * For block ciphers strip padding as defined in RFC8018 * (for PKCS#5 v1) or RFC1423 / RFC5652 (for v2). */ if (is_block) { uint8_t pad = decrypted[data_len - 1]; pkey = NULL; if (pad > data_len || pad > 16 || pad == 0) goto cleanup; if (!l_secure_memeq(decrypted + data_len - pad, pad - 1U, pad)) goto cleanup; decrypted_len -= pad; } pkey = cert_key_from_pkcs8_private_key_info(decrypted, decrypted_len); cleanup: explicit_bzero(decrypted, data_len); l_free(decrypted); return pkey; } struct l_key *cert_key_from_pkcs1_rsa_private_key(const uint8_t *der, size_t der_len) { const uint8_t *data; uint8_t tag; size_t data_len; const uint8_t *key_data; size_t key_data_len; int i; uint8_t *private_key; size_t private_key_len; uint8_t *one_asymmetric_key; uint8_t *ptr; struct l_key *pkey; static const uint8_t version0[] = { ASN1_ID_INTEGER, 0x01, 0x00 }; static const uint8_t pkcs1_rsa_encryption[] = { ASN1_ID_SEQUENCE, 0x0d, ASN1_ID_OID, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x01, ASN1_ID_NULL, 0x00, }; /* * Sanity check that it's a version 0 or 1 RSAPrivateKey structure * with the 8 integers. */ key_data = asn1_der_find_elem(der, der_len, 0, &tag, &key_data_len); if (!key_data || tag != ASN1_ID_SEQUENCE) return NULL; data = asn1_der_find_elem(key_data, key_data_len, 0, &tag, &data_len); if (!data || tag != ASN1_ID_INTEGER || data_len != 1 || (data[0] != 0x00 && data[0] != 0x01)) return NULL; for (i = 1; i < 9; i++) { data = asn1_der_find_elem(key_data, key_data_len, i, &tag, &data_len); if (!data || tag != ASN1_ID_INTEGER || data_len < 1) return NULL; } private_key = l_malloc(10 + der_len); ptr = private_key; *ptr++ = ASN1_ID_OCTET_STRING; asn1_write_definite_length(&ptr, der_len); memcpy(ptr, der, der_len); ptr += der_len; private_key_len = ptr - private_key; one_asymmetric_key = l_malloc(32 + private_key_len); ptr = one_asymmetric_key; *ptr++ = ASN1_ID_SEQUENCE; asn1_write_definite_length(&ptr, sizeof(version0) + sizeof(pkcs1_rsa_encryption) + private_key_len); memcpy(ptr, version0, sizeof(version0)); ptr += sizeof(version0); memcpy(ptr, pkcs1_rsa_encryption, sizeof(pkcs1_rsa_encryption)); ptr += sizeof(pkcs1_rsa_encryption); memcpy(ptr, private_key, private_key_len); ptr += private_key_len; explicit_bzero(private_key, private_key_len); l_free(private_key); pkey = cert_key_from_pkcs8_private_key_info(one_asymmetric_key, ptr - one_asymmetric_key); explicit_bzero(one_asymmetric_key, ptr - one_asymmetric_key); l_free(one_asymmetric_key); return pkey; } static const uint8_t *cert_unpack_pkcs7_content_info(const uint8_t *container, size_t container_len, int pos, const struct asn1_oid *expected_oid, struct asn1_oid *out_oid, uint8_t *out_tag, size_t *out_len) { const uint8_t *content_info; size_t content_info_len; const uint8_t *type; size_t type_len; const uint8_t *ret; uint8_t tag; if (!(content_info = asn1_der_find_elem(container, container_len, pos, &tag, &content_info_len)) || tag != ASN1_ID_SEQUENCE) return NULL; if (!(type = asn1_der_find_elem(content_info, content_info_len, 0, &tag, &type_len)) || tag != ASN1_ID_OID || type_len > sizeof(out_oid->asn1)) return NULL; if (expected_oid && !asn1_oid_eq(expected_oid, type_len, type)) return NULL; if (!(ret = asn1_der_find_elem(content_info, content_info_len, ASN1_CONTEXT_EXPLICIT(0), out_tag, out_len)) || ret + *out_len != content_info + content_info_len) return NULL; if (out_oid) { out_oid->asn1_len = type_len; memcpy(out_oid->asn1, type, type_len); } return ret; } /* RFC5652 Section 8 */ static uint8_t *cert_decrypt_pkcs7_encrypted_data(const uint8_t *data, size_t data_len, const char *password, struct asn1_oid *out_oid, size_t *out_len) { const uint8_t *version; size_t version_len; const uint8_t *encrypted_info; size_t encrypted_info_len; const uint8_t *type; size_t type_len; const uint8_t *alg_id; size_t alg_id_len; const uint8_t *encrypted; size_t encrypted_len; uint8_t tag; struct l_cipher *alg; uint8_t *plaintext; int i; bool ok; bool is_block; if (!(version = asn1_der_find_elem(data, data_len, 0, &tag, &version_len)) || tag != ASN1_ID_INTEGER || version_len != 1 || !L_IN_SET(version[0], 0, 2)) return NULL; if (!(encrypted_info = asn1_der_find_elem(data, data_len, 1, &tag, &encrypted_info_len)) || tag != ASN1_ID_SEQUENCE) return NULL; if (!(type = asn1_der_find_elem(encrypted_info, encrypted_info_len, 0, &tag, &type_len)) || tag != ASN1_ID_OID || type_len > sizeof(out_oid->asn1)) return NULL; if (!(alg_id = asn1_der_find_elem(encrypted_info, encrypted_info_len, 1, &tag, &alg_id_len)) || tag != ASN1_ID_SEQUENCE) return NULL; /* Not optional in our case, defined [0] IMPLICIT OCTET STRING */ if (!(encrypted = asn1_der_find_elem(encrypted_info, encrypted_info_len, ASN1_CONTEXT_IMPLICIT(0), &tag, &encrypted_len)) || tag != ASN1_ID(ASN1_CLASS_CONTEXT, 0, 0) || encrypted_len < 8) return NULL; if (!(alg = cert_cipher_from_pkcs_alg_id(alg_id, alg_id_len, password, &is_block))) return NULL; plaintext = l_malloc(encrypted_len); ok = l_cipher_decrypt(alg, encrypted, plaintext, encrypted_len); l_cipher_free(alg); if (!ok) { l_free(plaintext); return NULL; } if (is_block) { bool ok = true; /* Also validate the padding */ if (encrypted_len < plaintext[encrypted_len - 1] || plaintext[encrypted_len - 1] > 16) { plaintext[encrypted_len - 1] = 1; ok = false; } for (i = 1; i < plaintext[encrypted_len - 1]; i++) if (plaintext[encrypted_len - 1 - i] != plaintext[encrypted_len - 1]) ok = false; if (!ok) { explicit_bzero(plaintext, encrypted_len); l_free(plaintext); return NULL; } encrypted_len -= plaintext[encrypted_len - 1]; } if (out_oid) { out_oid->asn1_len = type_len; memcpy(out_oid->asn1, type, type_len); } *out_len = encrypted_len; return plaintext; } /* RFC7292 Appendix A. */ static const struct cert_pkcs12_hash pkcs12_mac_algs[] = { { L_CHECKSUM_MD5, 16, 16, 64, { 8, { 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0f, 0x02, 0x05 } } }, { L_CHECKSUM_SHA1, 20, 20, 64, { 5, { 0x2b, 0x0e, 0x03, 0x02, 0x1a } } }, { L_CHECKSUM_SHA224, 28, 28, 64, { 9, { 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x04 } } }, { L_CHECKSUM_SHA256, 32, 32, 64, { 9, { 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x01 } } }, { L_CHECKSUM_SHA384, 48, 48, 128, { 9, { 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x02 } } }, { L_CHECKSUM_SHA512, 64, 64, 128, { 9, { 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x03 } } }, { L_CHECKSUM_SHA512, 64, 28, 128, { 9, { 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x05 } } }, { L_CHECKSUM_SHA512, 64, 32, 128, { 9, { 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x06 } } }, }; static const struct asn1_oid pkcs12_key_bag_oid = { 11, { 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x0c, 0x0a, 0x01, 0x01 } }; static const struct asn1_oid pkcs12_pkcs8_shrouded_key_bag_oid = { 11, { 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x0c, 0x0a, 0x01, 0x02 } }; static const struct asn1_oid pkcs12_cert_bag_oid = { 11, { 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x0c, 0x0a, 0x01, 0x03 } }; static const struct asn1_oid pkcs12_safe_contents_bag_oid = { 11, { 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x0c, 0x0a, 0x01, 0x06 } }; static const struct asn1_oid pkcs9_x509_certificate_oid = { 10, { 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x09, 0x16, 0x01 } }; /* RFC7292 Section 4.2.3 */ static bool cert_parse_pkcs12_cert_bag(const uint8_t *data, size_t data_len, struct l_certchain **out_certchain) { const uint8_t *cert_bag; size_t cert_bag_len; const uint8_t *cert_id; size_t cert_id_len; const uint8_t *cert_value; size_t cert_value_len; uint8_t tag; struct l_cert *cert; if (!(cert_bag = asn1_der_find_elem(data, data_len, 0, &tag, &cert_bag_len)) || tag != ASN1_ID_SEQUENCE) return false; if (!(cert_id = asn1_der_find_elem(cert_bag, cert_bag_len, 0, &tag, &cert_id_len)) || tag != ASN1_ID_OID) return false; if (!(cert_value = asn1_der_find_elem(cert_bag, cert_bag_len, ASN1_CONTEXT_EXPLICIT(0), &tag, &cert_value_len)) || tag != ASN1_ID_OCTET_STRING || cert_value + cert_value_len != data + data_len) return false; /* Skip unsupported certificate types */ if (!asn1_oid_eq(&pkcs9_x509_certificate_oid, cert_id_len, cert_id)) return true; if (!(cert = l_cert_new_from_der(cert_value, cert_value_len))) return false; if (!*out_certchain) *out_certchain = certchain_new_from_leaf(cert); else certchain_link_issuer(*out_certchain, cert); return true; } static bool cert_parse_pkcs12_safe_contents(const uint8_t *data, size_t data_len, const char *password, struct l_certchain **out_certchain, struct l_key **out_privkey) { const uint8_t *safe_contents; size_t safe_contents_len; uint8_t tag; if (!(safe_contents = asn1_der_find_elem(data, data_len, 0, &tag, &safe_contents_len)) || tag != ASN1_ID_SEQUENCE || data + data_len != safe_contents + safe_contents_len) return false; /* RFC7292 Section 4.2 */ while (safe_contents_len) { const uint8_t *safe_bag; size_t safe_bag_len; const uint8_t *bag_id; size_t bag_id_len; const uint8_t *bag_value; int bag_value_len; /* RFC7292 Section 4.2 */ if (!(safe_bag = asn1_der_find_elem(safe_contents, safe_contents_len, 0, &tag, &safe_bag_len)) || tag != ASN1_ID_SEQUENCE) return false; if (!(bag_id = asn1_der_find_elem(safe_bag, safe_bag_len, 0, &tag, &bag_id_len)) || tag != ASN1_ID_OID) return false; /* * The bagValue is EXPLICITly tagged but we don't want to * unpack the inner TLV yet so don't use asn1_der_find_elem. */ safe_bag_len -= bag_id + bag_id_len - safe_bag; safe_bag = bag_id + bag_id_len; if (safe_bag_len < 4) return false; tag = *safe_bag++; safe_bag_len--; bag_value_len = asn1_parse_definite_length(&safe_bag, &safe_bag_len); bag_value = safe_bag; if (bag_value_len < 0 || bag_value_len > (int) safe_bag_len || tag != ASN1_ID(ASN1_CLASS_CONTEXT, 1, 0)) return false; /* PKCS#9 attributes ignored */ safe_contents_len -= (safe_bag + safe_bag_len - safe_contents); safe_contents = safe_bag + safe_bag_len; if (asn1_oid_eq(&pkcs12_key_bag_oid, bag_id_len, bag_id)) { if (!out_privkey || *out_privkey) continue; *out_privkey = cert_key_from_pkcs8_private_key_info(bag_value, bag_value_len); if (!*out_privkey) return false; } else if (asn1_oid_eq(&pkcs12_pkcs8_shrouded_key_bag_oid, bag_id_len, bag_id)) { if (!out_privkey || *out_privkey) continue; *out_privkey = cert_key_from_pkcs8_encrypted_private_key_info( bag_value, bag_value_len, password); if (!*out_privkey) return false; } else if (asn1_oid_eq(&pkcs12_cert_bag_oid, bag_id_len, bag_id)) { if (!out_certchain) continue; if (!cert_parse_pkcs12_cert_bag(bag_value, bag_value_len, out_certchain)) return false; } else if (asn1_oid_eq(&pkcs12_safe_contents_bag_oid, bag_id_len, bag_id)) { /* TODO: depth check */ if (!(cert_parse_pkcs12_safe_contents(bag_value, bag_value_len, password, out_certchain, out_privkey))) return false; } } return true; } static bool cert_check_pkcs12_integrity(const uint8_t *mac_data, size_t mac_data_len, const uint8_t *auth_safe, size_t auth_safe_len, const char *password) { const uint8_t *mac; size_t mac_len; const uint8_t *mac_salt; size_t mac_salt_len; const uint8_t *iterations_data; size_t iterations_len; unsigned int iterations; const uint8_t *digest_alg; size_t digest_alg_len; const uint8_t *digest; size_t digest_len; const uint8_t *alg_id; size_t alg_id_len; const struct cert_pkcs12_hash *mac_hash; L_AUTO_FREE_VAR(uint8_t *, key) = NULL; struct l_checksum *hmac; uint8_t hmac_val[64]; uint8_t tag; bool ok; unsigned int i; if (!(mac = asn1_der_find_elem(mac_data, mac_data_len, 0, &tag, &mac_len)) || tag != ASN1_ID_SEQUENCE) return false; if (!(mac_salt = asn1_der_find_elem(mac_data, mac_data_len, 1, &tag, &mac_salt_len)) || tag != ASN1_ID_OCTET_STRING || mac_salt_len > 1024) return false; if (!(iterations_data = asn1_der_find_elem(mac_data, mac_data_len, 2, &tag, &iterations_len)) || tag != ASN1_ID_INTEGER || iterations_len > 4) return false; for (iterations = 0; iterations_len; iterations_len--) iterations = (iterations << 8) | *iterations_data++; if (iterations < 1 || iterations > 8192) return false; /* RFC2315 Section 9.4 */ if (!(digest_alg = asn1_der_find_elem(mac, mac_len, 0, &tag, &digest_alg_len)) || tag != ASN1_ID_SEQUENCE) return false; if (!(digest = asn1_der_find_elem(mac, mac_len, 1, &tag, &digest_len)) || tag != ASN1_ID_OCTET_STRING) return false; if (!(alg_id = asn1_der_find_elem(digest_alg, digest_alg_len, 0, &tag, &alg_id_len)) || tag != ASN1_ID_OID) return false; /* This is going to be used for both the MAC and its key derivation */ for (i = 0; i < L_ARRAY_SIZE(pkcs12_mac_algs); i++) if (asn1_oid_eq(&pkcs12_mac_algs[i].oid, alg_id_len, alg_id)) { mac_hash = &pkcs12_mac_algs[i]; break; } if (i == L_ARRAY_SIZE(pkcs12_mac_algs) || digest_len != mac_hash->u) return false; if (!(key = cert_pkcs12_pbkdf(password, mac_hash, mac_salt, mac_salt_len, iterations, 3, mac_hash->u))) return false; hmac = l_checksum_new_hmac(mac_hash->alg, key, mac_hash->u); explicit_bzero(key, mac_hash->u); if (!hmac) return false; ok = l_checksum_update(hmac, auth_safe, auth_safe_len) && l_checksum_get_digest(hmac, hmac_val, mac_hash->len) > 0; l_checksum_free(hmac); if (!ok) return false; /* * SHA-512/224 and SHA-512/256 are not supported. We can truncate the * output for key derivation but we can't do this inside the HMAC * algorithms based on these hashes. We skip the MAC verification * if one of these hashes is used (identified by .u != .len) */ if (mac_hash->u != mac_hash->len) return true; return l_secure_memcmp(hmac_val, digest, digest_len) == 0; } /* RFC5652 Section 4 */ static const struct asn1_oid pkcs7_data_oid = { 9, { 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x07, 0x01 } }; /* RFC5652 Section 8 */ static const struct asn1_oid pkcs7_encrypted_data_oid = { 9, { 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x07, 0x06 } }; static bool cert_parse_auth_safe_content(const uint8_t *data, size_t data_len, uint8_t tag, const struct asn1_oid *data_oid, const char *password, struct l_certchain **out_certchain, struct l_key **out_privkey) { if (asn1_oid_eq(&pkcs7_encrypted_data_oid, data_oid->asn1_len, data_oid->asn1)) { uint8_t *plaintext; size_t plaintext_len; struct asn1_oid oid; bool ok; if (tag != ASN1_ID_SEQUENCE) return false; /* * This is same as PKCS#7 encryptedData but the ciphers * used are from PKCS#12 (broken but still the default * everywhere) and PKCS#5 (recommended). */ plaintext = cert_decrypt_pkcs7_encrypted_data(data, data_len, password, &oid, &plaintext_len); if (!plaintext) return false; /* * Since we only support PKCS#7 data and encryptedData * types, and there's no point re-encrypting * encryptedData, the plaintext must be a PKCS#7 * "data". */ ok = asn1_oid_eq(&pkcs7_data_oid, oid.asn1_len, oid.asn1) && cert_parse_pkcs12_safe_contents(plaintext, plaintext_len, password, out_certchain, out_privkey); explicit_bzero(plaintext, plaintext_len); l_free(plaintext); if (!ok) return false; } else if (asn1_oid_eq(&pkcs7_data_oid, data_oid->asn1_len, data_oid->asn1)) { if (tag != ASN1_ID_OCTET_STRING) return false; if (!cert_parse_pkcs12_safe_contents(data, data_len, password, out_certchain, out_privkey)) return false; } /* envelopedData support not needed */ return true; } static bool cert_parse_pkcs12_pfx(const uint8_t *ptr, size_t len, const char *password, struct l_certchain **out_certchain, struct l_key **out_privkey) { const uint8_t *version; size_t version_len; const uint8_t *auth_safe; size_t auth_safe_len; const uint8_t *mac_data; size_t mac_data_len; const uint8_t *auth_safe_seq; size_t auth_safe_seq_len; uint8_t tag; unsigned int i; struct l_certchain *certchain = NULL; struct l_key *privkey = NULL; /* RFC7292 Section 4 */ if (!(version = asn1_der_find_elem(ptr, len, 0, &tag, &version_len)) || tag != ASN1_ID_INTEGER) return false; if (version_len != 1 || version[0] != 3) return false; /* * Since we only support the password-based integrity mode, the * authSafe must be of PKCS#7 type "data" and not "signedData". */ if (!(auth_safe = cert_unpack_pkcs7_content_info(ptr, len, 1, &pkcs7_data_oid, NULL, &tag, &auth_safe_len)) || tag != ASN1_ID_OCTET_STRING) return false; /* * openssl can generate PFX structures without macData not signed * with a public key so handle this case, otherwise the macData * would not be optional. */ if (auth_safe + auth_safe_len == ptr + len) goto integrity_check_done; if (!(mac_data = asn1_der_find_elem(ptr, len, 2, &tag, &mac_data_len)) || tag != ASN1_ID_SEQUENCE) return false; if (!cert_check_pkcs12_integrity(mac_data, mac_data_len, auth_safe, auth_safe_len, password)) return false; integrity_check_done: if (!(auth_safe_seq = asn1_der_find_elem(auth_safe, auth_safe_len, 0, &tag, &auth_safe_seq_len)) || tag != ASN1_ID_SEQUENCE || auth_safe + auth_safe_len != auth_safe_seq + auth_safe_seq_len) return false; i = 0; while (1) { struct asn1_oid data_oid; const uint8_t *data; size_t data_len; if (!(data = cert_unpack_pkcs7_content_info(auth_safe_seq, auth_safe_seq_len, i++, NULL, &data_oid, &tag, &data_len))) goto error; if (!cert_parse_auth_safe_content(data, data_len, tag, &data_oid, password, out_certchain ? &certchain : NULL, out_privkey ? &privkey : NULL)) goto error; if (data + data_len == auth_safe_seq + auth_safe_seq_len) break; } if (out_certchain) *out_certchain = certchain; if (out_privkey) *out_privkey = privkey; return true; error: if (certchain) l_certchain_free(certchain); if (privkey) l_key_free(privkey); return false; } static int cert_try_load_der_format(const uint8_t *content, size_t content_len, const char *password, struct l_certchain **out_certchain, struct l_key **out_privkey, bool *out_encrypted) { const uint8_t *seq; size_t seq_len; size_t elem_len; uint8_t tag; if (!(seq = asn1_der_find_elem(content, content_len, 0, &tag, &seq_len))) /* May not have been a DER file after all */ return -ENOMSG; /* * See if the first sub-element is another sequence, then, out of * the formats that we currently support this can only be a raw * certificate. If integer, it's going to be PKCS#12. If we wish * to add any more formats we'll probably need to start guessing * from the filename suffix. */ if (!asn1_der_find_elem(seq, seq_len,0, &tag, &elem_len)) return -ENOMSG; if (tag == ASN1_ID_SEQUENCE) { if (out_certchain) { struct l_cert *cert; if (!(cert = l_cert_new_from_der(content, content_len))) return -EINVAL; *out_certchain = certchain_new_from_leaf(cert); if (out_privkey) *out_privkey = NULL; if (out_encrypted) *out_encrypted = false; return 0; } return -EINVAL; } if (tag == ASN1_ID_INTEGER) { /* * Since we don't support public key-protected PKCS#12 * modes, we always require the password at least for the * integrity check. Strictly speaking encryption may not * actually be in use. We also don't support files with * different integrity and privacy passwords, they must * be identical if privacy is enabled. */ if (out_encrypted) *out_encrypted = true; if (!password) { if (!out_encrypted) return -EINVAL; if (out_certchain) *out_certchain = NULL; if (out_privkey) *out_privkey = NULL; return 0; } if (cert_parse_pkcs12_pfx(seq, seq_len, password, out_certchain, out_privkey)) return 0; else return -EINVAL; } return -ENOMSG; } static bool cert_try_load_pem_format(const char *content, size_t content_len, const char *password, struct l_certchain **out_certchain, struct l_key **out_privkey, bool *out_encrypted) { bool error = false; bool done = false; struct l_certchain *certchain = NULL; struct l_key *privkey = NULL; bool encrypted = false; while (!done && !error && content_len) { uint8_t *der; size_t der_len; char *type_label; char *headers; const char *endp; if (!(der = pem_load_buffer(content, content_len, &type_label, &der_len, &headers, &endp))) break; content_len -= endp - content; content = endp; if (out_certchain && L_IN_STRSET(type_label, "CERTIFICATE")) { struct l_cert *cert; if (!(cert = l_cert_new_from_der(der, der_len))) { error = true; goto next; } if (!certchain) certchain = certchain_new_from_leaf(cert); else certchain_link_issuer(certchain, cert); goto next; } /* Only use the first private key found */ if (out_privkey && !privkey && L_IN_STRSET(type_label, "PRIVATE KEY", "ENCRYPTED PRIVATE KEY", "RSA PRIVATE KEY")) { privkey = pem_load_private_key(der, der_len, type_label, password, headers, &encrypted); if (!privkey) { if (certchain) { l_certchain_free(certchain); certchain = NULL; } if (password) error = true; else error = !encrypted || !out_encrypted; done = true; } continue; } /* Cisco/gnutls-type PEM-encoded PKCS#12, probably rare */ if (L_IN_STRSET(type_label, "PKCS12")) { encrypted = true; if (!password) { if (certchain && out_privkey) { l_certchain_free(certchain); certchain = NULL; } error = !out_encrypted; done = true; goto next; } error = !cert_parse_pkcs12_pfx(der, der_len, password, out_certchain ? &certchain : NULL, out_privkey ? &privkey : NULL); goto next; } next: explicit_bzero(der, der_len); l_free(der); l_free(type_label); l_free(headers); } if (error) { if (certchain) l_certchain_free(certchain); if (privkey) l_key_free(privkey); return false; } if (out_certchain) *out_certchain = certchain; if (out_privkey) *out_privkey = privkey; if (out_encrypted) *out_encrypted = encrypted; return true; } /* * Look at a file, try to detect which of the few X.509 certificate and/or * private key container formats it uses and load any certificates in it as * a certificate chain object, and load the first private key as an l_key * object. * * Currently supported are: * PEM X.509 certificates * PEM PKCS#8 encrypted and unencrypted private keys * PEM legacy PKCS#1 encrypted and unencrypted private keys * Raw X.509 certificates (.cer, .der, .crt) * PKCS#12 certificates * PKCS#12 encrypted private keys * * The raw format contains exactly one certificate, PEM and PKCS#12 files * can contain any combination of certificates and private keys. * * The password must have been validated as UTF-8 (use l_utf8_validate) * unless the caller knows that no PKCS#12-defined encryption algorithm * or MAC is used. * * Returns false on "unrecoverable" errors, and *out_certchain, * *out_privkey and *out_encrypted (if provided) are not modified. However * when true is returned, *out_certchain and *out_privkey (if provided) may * be set to NULL when nothing could be loaded only due to missing password, * and *out_encrypted (if provided) will be set accordingly. It will also * be set on success to indicate whether the password was used. * *out_certchain and/or *out_privkey will also be NULL if the container * was loaded but there were no certificates or private keys in it. */ LIB_EXPORT bool l_cert_load_container_file(const char *filename, const char *password, struct l_certchain **out_certchain, struct l_key **out_privkey, bool *out_encrypted) { struct pem_file_info file; bool error = true; if (unlikely(!filename)) return false; if (pem_file_open(&file, filename) < 0) return false; if (file.st.st_size < 1) goto close; /* See if we have a DER sequence tag at the start */ if (file.data[0] == ASN1_ID_SEQUENCE) { int err; err = cert_try_load_der_format(file.data, file.st.st_size, password, out_certchain, out_privkey, out_encrypted); if (!err) { error = false; goto close; } if (err != -ENOMSG) goto close; /* Try other formats */ } /* * For backwards compatibility try the TLS internal struct Certificate * format as may be captured by PCAP (no future support guaranteed). */ if (out_certchain && !password && file.st.st_size && tls_parse_certificate_list(file.data, file.st.st_size, out_certchain) == 0) { error = false; if (out_privkey) *out_privkey = NULL; if (out_encrypted) *out_encrypted = false; goto close; } /* * RFC 7486 allows whitespace and possibly other data before the * PEM "encapsulation boundary" so rather than check if the start * of the data looks like PEM, we fall back to this format if the * data didn't look like anything else we knew about. Note this * succeeds for empty files and files without any PEM markers, * returning NULL chain and privkey. */ if (cert_try_load_pem_format((const char *) file.data, file.st.st_size, password, out_certchain, out_privkey, out_encrypted)) error = false; close: pem_file_close(&file); return !error; } bluez-5.82/ell/PaxHeaders/log.h0000644000000000000000000000005014567163422013366 xustar0020 atime=1743575449 20 ctime=1743591276 bluez-5.82/ell/log.h0000644000000000000000000000554214567163422013055 0ustar00rootroot/* * Embedded Linux library * Copyright (C) 2011-2014 Intel Corporation * * SPDX-License-Identifier: LGPL-2.1-or-later */ #ifndef __ELL_LOG_H #define __ELL_LOG_H #include #include #ifdef __cplusplus extern "C" { #endif #define L_LOG_ERR 3 #define L_LOG_WARNING 4 #define L_LOG_NOTICE 5 #define L_LOG_INFO 6 #define L_LOG_DEBUG 7 typedef void (*l_log_func_t) (int priority, const char *file, const char *line, const char *func, const char *format, va_list ap); void l_log_set_ident(const char *ident); void l_log_set_handler(l_log_func_t function); void l_log_set_null(void); void l_log_set_stderr(void); void l_log_set_syslog(void); void l_log_set_journal(void); void l_log_with_location(int priority, const char *file, const char *line, const char *func, const char *format, ...) __attribute__((format(printf, 5, 6))); #define l_log(priority, format, ...) l_log_with_location(priority, \ __FILE__, L_STRINGIFY(__LINE__), \ __func__, format "\n", ##__VA_ARGS__) struct l_debug_desc { const char *file; const char *func; #define L_DEBUG_FLAG_DEFAULT (0) #define L_DEBUG_FLAG_PRINT (1 << 0) unsigned int flags; } __attribute__((aligned(8))); /* * Set the retain attribute so that the section cannot be discarded by ld * --gc-sections -z start-stop-gc. Older compilers would warn for the unknown * attribute, so just disable -Wattributes. */ #define L_DEBUG_SYMBOL(symbol, format, ...) do { \ _Pragma("GCC diagnostic push") \ _Pragma("GCC diagnostic ignored \"-Wattributes\"") \ static struct l_debug_desc symbol \ __attribute__((used, retain, section("__ell_debug"), aligned(8))) = { \ .file = __FILE__, .func = __func__, \ .flags = L_DEBUG_FLAG_DEFAULT, \ }; \ _Pragma("GCC diagnostic pop") \ if (symbol.flags & L_DEBUG_FLAG_PRINT) \ l_log(L_LOG_DEBUG, "%s:%s() " format, __FILE__, \ __func__ , ##__VA_ARGS__); \ } while (0) void l_debug_enable_full(const char *pattern, struct l_debug_desc *start, struct l_debug_desc *end); void l_debug_add_section(struct l_debug_desc *start, struct l_debug_desc *end); #define l_debug_enable(pattern) do { \ _Pragma("GCC diagnostic push") \ _Pragma("GCC diagnostic ignored \"-Wredundant-decls\"") \ extern struct l_debug_desc __start___ell_debug[]; \ extern struct l_debug_desc __stop___ell_debug[]; \ l_debug_enable_full(pattern, __start___ell_debug, __stop___ell_debug); \ _Pragma("GCC diagnostic pop") \ } while (0) void l_debug_disable(void); #define l_error(format, ...) l_log(L_LOG_ERR, format, ##__VA_ARGS__) #define l_warn(format, ...) l_log(L_LOG_WARNING, format, ##__VA_ARGS__) #define l_notice(format, ...) l_log(L_LOG_NOTICE, format, ##__VA_ARGS__) #define l_info(format, ...) l_log(L_LOG_INFO, format, ##__VA_ARGS__) #define l_debug(format, ...) L_DEBUG_SYMBOL(__debug_desc, format, ##__VA_ARGS__) #ifdef __cplusplus } #endif #endif /* __ELL_LOG_H */ bluez-5.82/ell/PaxHeaders/main.h0000644000000000000000000000005014504767710013532 xustar0020 atime=1743575449 20 ctime=1743591276 bluez-5.82/ell/main.h0000644000000000000000000000120314504767710013207 0ustar00rootroot/* * Embedded Linux library * Copyright (C) 2011-2014 Intel Corporation * * SPDX-License-Identifier: LGPL-2.1-or-later */ #ifndef __ELL_MAIN_H #define __ELL_MAIN_H #include #include #ifdef __cplusplus extern "C" { #endif bool l_main_init(void); int l_main_prepare(void); void l_main_iterate(int timeout); int l_main_run(void); bool l_main_exit(void); bool l_main_quit(void); typedef void (*l_main_signal_cb_t) (uint32_t signo, void *user_data); int l_main_run_with_signal(l_main_signal_cb_t callback, void *user_data); int l_main_get_epoll_fd(void); #ifdef __cplusplus } #endif #endif /* __ELL_MAIN_H */ bluez-5.82/ell/PaxHeaders/pem.h0000644000000000000000000000005014504767710013367 xustar0020 atime=1743575449 20 ctime=1743591276 bluez-5.82/ell/pem.h0000644000000000000000000000206514504767710013053 0ustar00rootroot/* * Embedded Linux library * Copyright (C) 2015 Intel Corporation * * SPDX-License-Identifier: LGPL-2.1-or-later */ #ifndef __ELL_PEM_H #define __ELL_PEM_H #ifdef __cplusplus extern "C" { #endif struct l_queue; struct l_key; struct l_cert; struct l_certchain; uint8_t *l_pem_load_buffer(const void *buf, size_t buf_len, char **type_label, size_t *out_len); uint8_t *l_pem_load_file(const char *filename, char **type_label, size_t *len); struct l_certchain *l_pem_load_certificate_chain(const char *filename); struct l_certchain *l_pem_load_certificate_chain_from_data(const void *buf, size_t len); struct l_queue *l_pem_load_certificate_list(const char *filename); struct l_queue *l_pem_load_certificate_list_from_data(const void *buf, size_t len); struct l_key *l_pem_load_private_key(const char *filename, const char *passphrase, bool *encrypted); struct l_key *l_pem_load_private_key_from_data(const void *buf, size_t len, const char *passphrase, bool *encrypted); #ifdef __cplusplus } #endif #endif /* __ELL_PEM_H */ bluez-5.82/ell/PaxHeaders/util.h0000644000000000000000000000005014770744372013567 xustar0020 atime=1743575449 20 ctime=1743591276 bluez-5.82/ell/util.h0000644000000000000000000003662214770744372013261 0ustar00rootroot/* * Embedded Linux library * Copyright (C) 2011-2014 Intel Corporation * * SPDX-License-Identifier: LGPL-2.1-or-later */ #ifndef __ELL_UTIL_H #define __ELL_UTIL_H #include #include #include #include #include #include #include #include #include #include #ifdef __cplusplus extern "C" { #endif #define l_container_of(ptr, type, member) ({ \ _Pragma("GCC diagnostic push") \ _Pragma("GCC diagnostic ignored \"-Wcast-align\"") \ const __typeof__(((type *) 0)->member) *__mptr = (ptr); \ (type *)((char *) __mptr - offsetof(type, member)); \ _Pragma("GCC diagnostic pop") \ }) #define L_STRINGIFY(val) L_STRINGIFY_ARG(val) #define L_STRINGIFY_ARG(contents) #contents #define L_WARN_ON(condition) __extension__ ({ \ bool r = !!(condition); \ if (__builtin_expect(r, 0)) \ l_warn("WARNING: %s:%s() condition %s failed", \ __FILE__, __func__, \ #condition); \ r; \ }) /* * If ELL headers and iterfaces end up getting compiled in a C++ * environment, even though ELL itself is a C source based and is * compiled as such, certain assignments may be flagged by the C++ * compiler as errors or warnings. The following portable casts should * be used in such cases, with a preference towards L_PERMISSIVE_CAST * where possible since it is not a cast in C and, therefore, will not * mask otherwise-legitimate warnings in that environment. */ #ifdef __cplusplus #define L_CONST_CAST(t, v) const_cast(v) #define L_REINTERPRET_CAST(t, v) reinterpret_cast(v) #define L_STATIC_CAST(t, v) static_cast(v) #define L_PERMISSIVE_CAST(t, v) L_STATIC_CAST(t, v) #else #define L_CONST_CAST(t, v) ((t)(v)) #define L_REINTERPRET_CAST(t, v) ((t)(v)) #define L_STATIC_CAST(t, v) ((t)(v)) #define L_PERMISSIVE_CAST(t, v) (v) #endif #define L_PTR_TO_UINT(p) ((unsigned int) ((uintptr_t) (p))) #define L_UINT_TO_PTR(u) ((void *) ((uintptr_t) (u))) #define L_PTR_TO_INT(p) ((int) ((intptr_t) (p))) #define L_INT_TO_PTR(u) ((void *) ((intptr_t) (u))) #define L_GET_UNALIGNED(ptr) __extension__ \ ({ \ struct __attribute__((packed)) { \ __typeof__(*(ptr)) __v; \ } *__p = (__typeof__(__p)) (ptr); \ __p->__v; \ }) #define L_PUT_UNALIGNED(val, ptr) \ do { \ struct __attribute__((packed)) { \ __typeof__(*(ptr)) __v; \ } *__p = (__typeof__(__p)) (ptr); \ __p->__v = (val); \ } while(0) #if __BYTE_ORDER == __LITTLE_ENDIAN #define L_LE16_TO_CPU(val) (val) #define L_LE32_TO_CPU(val) (val) #define L_LE64_TO_CPU(val) (val) #define L_CPU_TO_LE16(val) (val) #define L_CPU_TO_LE32(val) (val) #define L_CPU_TO_LE64(val) (val) #define L_BE16_TO_CPU(val) bswap_16(val) #define L_BE32_TO_CPU(val) bswap_32(val) #define L_BE64_TO_CPU(val) bswap_64(val) #define L_CPU_TO_BE16(val) bswap_16(val) #define L_CPU_TO_BE32(val) bswap_32(val) #define L_CPU_TO_BE64(val) bswap_64(val) #elif __BYTE_ORDER == __BIG_ENDIAN #define L_LE16_TO_CPU(val) bswap_16(val) #define L_LE32_TO_CPU(val) bswap_32(val) #define L_LE64_TO_CPU(val) bswap_64(val) #define L_CPU_TO_LE16(val) bswap_16(val) #define L_CPU_TO_LE32(val) bswap_32(val) #define L_CPU_TO_LE64(val) bswap_64(val) #define L_BE16_TO_CPU(val) (val) #define L_BE32_TO_CPU(val) (val) #define L_BE64_TO_CPU(val) (val) #define L_CPU_TO_BE16(val) (val) #define L_CPU_TO_BE32(val) (val) #define L_CPU_TO_BE64(val) (val) #else #error "Unknown byte order" #endif #if __STDC_VERSION__ <= 199409L #define inline __inline__ #endif static inline uint8_t l_get_u8(const void *ptr) { return *((const uint8_t *) ptr); } static inline void l_put_u8(uint8_t val, void *ptr) { *((uint8_t *) ptr) = val; } static inline uint16_t l_get_u16(const void *ptr) { return L_GET_UNALIGNED((const uint16_t *) ptr); } static inline void l_put_u16(uint16_t val, void *ptr) { L_PUT_UNALIGNED(val, (uint16_t *) ptr); } static inline uint32_t l_get_u32(const void *ptr) { return L_GET_UNALIGNED((const uint32_t *) ptr); } static inline void l_put_u32(uint32_t val, void *ptr) { L_PUT_UNALIGNED(val, (uint32_t *) ptr); } static inline uint64_t l_get_u64(const void *ptr) { return L_GET_UNALIGNED((const uint64_t *) ptr); } static inline void l_put_u64(uint64_t val, void *ptr) { L_PUT_UNALIGNED(val, (uint64_t *) ptr); } static inline int16_t l_get_s16(const void *ptr) { return L_GET_UNALIGNED((const int16_t *) ptr); } static inline int32_t l_get_s32(const void *ptr) { return L_GET_UNALIGNED((const int32_t *) ptr); } static inline int64_t l_get_s64(const void *ptr) { return L_GET_UNALIGNED((const int64_t *) ptr); } static inline uint16_t l_get_le16(const void *ptr) { return L_LE16_TO_CPU(L_GET_UNALIGNED((const uint16_t *) ptr)); } static inline uint16_t l_get_be16(const void *ptr) { return L_BE16_TO_CPU(L_GET_UNALIGNED((const uint16_t *) ptr)); } static inline uint32_t l_get_le32(const void *ptr) { return L_LE32_TO_CPU(L_GET_UNALIGNED((const uint32_t *) ptr)); } static inline uint32_t l_get_be32(const void *ptr) { return L_BE32_TO_CPU(L_GET_UNALIGNED((const uint32_t *) ptr)); } static inline uint64_t l_get_le64(const void *ptr) { return L_LE64_TO_CPU(L_GET_UNALIGNED((const uint64_t *) ptr)); } static inline uint64_t l_get_be64(const void *ptr) { return L_BE64_TO_CPU(L_GET_UNALIGNED((const uint64_t *) ptr)); } static inline void l_put_le16(uint16_t val, void *ptr) { L_PUT_UNALIGNED(L_CPU_TO_LE16(val), (uint16_t *) ptr); } static inline void l_put_be16(uint16_t val, const void *ptr) { L_PUT_UNALIGNED(L_CPU_TO_BE16(val), (uint16_t *) ptr); } static inline void l_put_le32(uint32_t val, void *ptr) { L_PUT_UNALIGNED(L_CPU_TO_LE32(val), (uint32_t *) ptr); } static inline void l_put_be32(uint32_t val, void *ptr) { L_PUT_UNALIGNED(L_CPU_TO_BE32(val), (uint32_t *) ptr); } static inline void l_put_le64(uint64_t val, void *ptr) { L_PUT_UNALIGNED(L_CPU_TO_LE64(val), (uint64_t *) ptr); } static inline void l_put_be64(uint64_t val, void *ptr) { L_PUT_UNALIGNED(L_CPU_TO_BE64(val), (uint64_t *) ptr); } #define L_AUTO_FREE_VAR(vartype,varname) \ vartype varname __attribute__((cleanup(auto_free))) #define L_ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0])) void *l_malloc(size_t size) __attribute__ ((warn_unused_result, malloc)); void *l_memdup(const void *mem, size_t size) __attribute__ ((warn_unused_result, malloc)); static inline void * __attribute__((nonnull(1))) l_memcpy(void *dest, const void *src, size_t n) { if (!n) return dest; return __builtin_memcpy(dest, src, n); } void l_free(void *ptr); DEFINE_CLEANUP_FUNC(l_free); void *l_realloc(void *mem, size_t size) __attribute__ ((warn_unused_result, malloc)); static inline void auto_free(void *a) { void **p = (void **)a; l_free(*p); } #define l_steal_ptr(ptr) \ (__extension__ ({ typeof(ptr) _tmp = (ptr); (ptr) = NULL; _tmp; })) /** * l_new: * @type: type of structure * @count: amount of structures * * Returns: pointer to allocated memory **/ #define l_new(type, count) \ (type *) (__extension__ ({ \ size_t __n = (size_t) (count); \ size_t __s = sizeof(type); \ void *__p; \ __p = l_malloc(__n * __s); \ memset(__p, 0, __n * __s); \ __p; \ })) /** * l_newa: * @type: type of structure * @count: amount of structures * * Allocates stack space for @count structures of @type. Memory is allocated * using alloca and initialized to 0. * * Returns: Pointer to memory allocated on the stack. */ #define l_newa(type, count) \ (type *) (__extension__ ({ \ size_t __n = (size_t) (count); \ size_t __s = sizeof(type); \ void *__p; \ __p = alloca(__n * __s); \ memset(__p, 0, __n * __s); \ __p; \ })) char *l_strdup(const char *str); char *l_strndup(const char *str, size_t max); char *l_strdup_printf(const char *format, ...) __attribute__((format(printf, 1, 2))); char *l_strdup_vprintf(const char *format, va_list args) __attribute__((format(printf, 1, 0))); size_t l_strlcpy(char* dst, const char *src, size_t len); bool l_str_has_prefix(const char *str, const char *prefix); bool l_str_has_suffix(const char *str, const char *suffix); bool l_streq0(const char *a, const char *b); char *l_util_oidstring(const void *buf, size_t len); char *l_util_hexstring(const void *buf, size_t len); char *l_util_hexstring_upper(const void *buf, size_t len); char *l_util_hexstringv(const struct iovec *iov, size_t n_iov); char *l_util_hexstringv_upper(const struct iovec *iov, size_t n_iov); unsigned char *l_util_from_hexstring(const char *str, size_t *out_len); typedef void (*l_util_hexdump_func_t) (const char *str, void *user_data); void l_util_hexdump(bool in, const void *buf, size_t len, l_util_hexdump_func_t function, void *user_data); void l_util_hexdump_two(bool in, const void *buf1, size_t len1, const void *buf2, size_t len2, l_util_hexdump_func_t function, void *user_data); void l_util_hexdumpv(bool in, const struct iovec *iov, size_t n_iov, l_util_hexdump_func_t function, void *user_data); void l_util_debug(l_util_hexdump_func_t function, void *user_data, const char *format, ...) __attribute__((format(printf, 3, 4))); const char *l_util_get_debugfs_path(void); #define L_TFR(expression) \ (__extension__ \ ({ long int __result; \ do __result = (long int) (expression); \ while (__result == -1L && errno == EINTR); \ __result; })) /* Enables declaring _auto_(close) int fd = <-1 or L_TFR(open(...))>; */ inline __attribute__((always_inline)) void _l_close_cleanup(void *p) { int fd = *(int *) p; if (fd >= 0) L_TFR(close(fd)); } #define _L_IN_SET_CMP(val, type, cmp, ...) __extension__ ({ \ const type __v = (val); \ const typeof(__v) __elems[] = {__VA_ARGS__}; \ size_t __i; \ const size_t __n = L_ARRAY_SIZE(__elems); \ bool __r = false; \ for (__i = 0; __i < __n && !__r; __i++) \ __r = (cmp); \ __r; \ }) /* Warning: evaluates all set elements even after @val has matched one */ #define L_IN_SET(val, ...) \ _L_IN_SET_CMP((val), __auto_type, __v == __elems[__i], ##__VA_ARGS__) #define L_IN_STRSET(val, ...) \ _L_IN_SET_CMP((val), char *, __v == __elems[__i] || \ (__v && __elems[__i] && \ !strcmp(__v, __elems[__i])), ##__VA_ARGS__) #define _L_BIT_TO_MASK(bits, nr) __extension__ ({ \ typeof(*(bits)) _one = 1U; \ const unsigned int _shift = (nr) % (sizeof(_one) * 8); \ _one << _shift; \ }) #define _L_BIT_TO_OFFSET(bits, nr) __extension__ ({ \ __auto_type _bits = (bits); \ const size_t _offset = (nr) / (sizeof(*_bits) * 8); \ _bits + _offset; \ }) #define L_BIT_SET(bits, nr) __extension__ ({ \ size_t _nr = (nr); \ __auto_type _offset = _L_BIT_TO_OFFSET(bits, _nr); \ *_offset |= _L_BIT_TO_MASK(_offset, _nr); \ }) #define L_BIT_CLEAR(bits, nr) __extension__ ({ \ size_t _nr = (nr); \ __auto_type _offset = _L_BIT_TO_OFFSET(bits, _nr); \ *_offset &= ~_L_BIT_TO_MASK(_offset, _nr); \ }) #define L_BIT_TEST(bits, nr) __extension__ ({ \ size_t _nr = (nr); \ __auto_type _offset = _L_BIT_TO_OFFSET(bits, _nr); \ (*_offset & _L_BIT_TO_MASK(_offset, _nr)) != 0; \ }) #define L_BITS_SET(bits, ...) __extension__ ({ \ const unsigned int __elems[] = {__VA_ARGS__}; \ size_t __i; \ for (__i = 0; __i < L_ARRAY_SIZE(__elems); __i++) \ L_BIT_SET(bits, __elems[__i]); \ }) #define L_BITS_CLEAR(bits, ...) __extension__ ({ \ const unsigned int __elems[] = {__VA_ARGS__}; \ size_t __i; \ for (__i = 0; __i < L_ARRAY_SIZE(__elems); __i++) \ L_BIT_CLEAR(bits, __elems[__i]); \ }) /* * Taken from https://github.com/chmike/cst_time_memcmp, adding a volatile to * ensure the compiler does not try to optimize the constant time behavior. * The code has been modified to add comments and project specific code * styling. * This specific piece of code is subject to the following copyright: * * The MIT License (MIT) * * Copyright (c) 2015 Christophe Meessen * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to * deal in the Software without restriction, including without limitation the * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or * sell copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS * IN THE SOFTWARE. * * This function performs a secure memory comparison of two buffers of size * bytes, representing an integer (byte order is big endian). It returns * a negative, zero or positif value if a < b, a == b or a > b respectively. */ static inline int l_secure_memcmp(const void *a, const void *b, size_t size) { const volatile uint8_t *aa = L_PERMISSIVE_CAST(const volatile uint8_t *, a); const volatile uint8_t *bb = L_PERMISSIVE_CAST(const volatile uint8_t *, b); int res = 0, diff, mask; /* * We will compare all bytes, starting with the less significant. When * we find a non-zero difference, we update the result accordingly. */ if (size > 0) { /* * The following couple of lines can be summarized as a * constant time/memory access version of: * if (diff != 0) res = diff; * * From the previous operation, we know that diff is in * [-255, 255] * * The following figure show the possible value of mask, based * on different cases of diff: * * diff | diff-1 | ~diff | ((diff-1) & ~diff) | mask * ------|------------|------------|--------------------|------ * < 0 | 0xFFFFFFXX | 0x000000YY | 0x000000ZZ | 0 * == 0 | 0xFFFFFFFF | 0xFFFFFFFF | 0xFFFFFFFF | 0xF..F * > 0 | 0x000000XX | 0xFFFFFFYY | 0x000000ZZ | 0 * * Hence, the mask allows to keep res when diff == 0, and to * set res to diff otherwise. */ do { --size; diff = aa[size] - bb[size]; mask = (((diff - 1) & ~diff) >> 8); res = (res & mask) | diff; } while (size != 0); } return res; } bool l_memeq(const void *field, size_t size, uint8_t byte); bool l_secure_memeq(const void *field, size_t size, uint8_t byte); static inline bool l_memeqzero(const void *field, size_t size) { return l_memeq(field, size, 0); } static inline void l_secure_select(bool select_left, const void *left, const void *right, void *out, size_t len) { const uint8_t *l = L_PERMISSIVE_CAST(const uint8_t *, left); const uint8_t *r = L_PERMISSIVE_CAST(const uint8_t *, right); uint8_t *o = L_PERMISSIVE_CAST(uint8_t *, out); uint8_t mask = -(!!select_left); size_t i; for (i = 0; i < len; i++) o[i] = r[i] ^ ((l[i] ^ r[i]) & mask); } int l_safe_atou32(const char *s, uint32_t *out_u); int l_safe_atox32(const char *s, uint32_t *out_u); int l_safe_atox16(const char *s, uint16_t *out_u); int l_safe_atox8(const char *s, uint8_t *out_u); size_t l_util_pagesize(void); #ifdef __cplusplus } #endif #endif /* __ELL_UTIL_H */ bluez-5.82/ell/PaxHeaders/settings.h0000644000000000000000000000005014504767710014446 xustar0020 atime=1743575449 20 ctime=1743591276 bluez-5.82/ell/settings.h0000644000000000000000000001127214504767710014132 0ustar00rootroot/* * Embedded Linux library * Copyright (C) 2011-2014 Intel Corporation * * SPDX-License-Identifier: LGPL-2.1-or-later */ #ifndef __ELL_SETTINGS_H #define __ELL_SETTINGS_H #include #include #include #ifdef __cplusplus extern "C" { #endif struct l_settings; typedef void (*l_settings_debug_cb_t) (const char *str, void *user_data); typedef void (*l_settings_destroy_cb_t) (void *user_data); struct l_settings *l_settings_new(void); struct l_settings *l_settings_clone(const struct l_settings *settings); void l_settings_free(struct l_settings *settings); DEFINE_CLEANUP_FUNC(l_settings_free); bool l_settings_load_from_data(struct l_settings *settings, const char *data, size_t len); char *l_settings_to_data(const struct l_settings *settings, size_t *len); bool l_settings_load_from_file(struct l_settings *settings, const char *filename); bool l_settings_set_debug(struct l_settings *settings, l_settings_debug_cb_t callback, void *user_data, l_settings_destroy_cb_t destroy); char **l_settings_get_groups(const struct l_settings *settings); char **l_settings_get_keys(const struct l_settings *settings, const char *group_name); bool l_settings_add_group(struct l_settings *settings, const char *group_name); bool l_settings_has_group(const struct l_settings *settings, const char *group_name); bool l_settings_has_key(const struct l_settings *settings, const char *group_name, const char *key); const char *l_settings_get_value(const struct l_settings *settings, const char *group_name, const char *key); bool l_settings_set_value(struct l_settings *settings, const char *group_name, const char *key, const char *value); bool l_settings_get_bool(const struct l_settings *settings, const char *group_name, const char *key, bool *out); bool l_settings_set_bool(struct l_settings *settings, const char *group_name, const char *key, bool in); bool l_settings_get_int(const struct l_settings *settings, const char *group_name, const char *key, int *out); bool l_settings_set_int(struct l_settings *settings, const char *group_name, const char *key, int in); bool l_settings_get_uint(const struct l_settings *settings, const char *group_name, const char *key, unsigned int *out); bool l_settings_set_uint(struct l_settings *settings, const char *group_name, const char *key, unsigned int in); bool l_settings_get_int64(const struct l_settings *settings, const char *group_name, const char *key, int64_t *out); bool l_settings_set_int64(struct l_settings *settings, const char *group_name, const char *key, int64_t in); bool l_settings_get_uint64(const struct l_settings *settings, const char *group_name, const char *key, uint64_t *out); bool l_settings_set_uint64(struct l_settings *settings, const char *group_name, const char *key, uint64_t in); char *l_settings_get_string(const struct l_settings *settings, const char *group_name, const char *key); bool l_settings_set_string(struct l_settings *settings, const char *group_name, const char *key, const char *value); char **l_settings_get_string_list(const struct l_settings *settings, const char *group_name, const char *key, char delimiter); bool l_settings_set_string_list(struct l_settings *settings, const char *group_name, const char *key, char **list, char delimiter); bool l_settings_get_double(const struct l_settings *settings, const char *group_name, const char *key, double *out); bool l_settings_set_double(struct l_settings *settings, const char *group_name, const char *key, double in); bool l_settings_get_float(const struct l_settings *settings, const char *group_name, const char *key, float *out); bool l_settings_set_float(struct l_settings *settings, const char *group_name, const char *key, float in); uint8_t *l_settings_get_bytes(const struct l_settings *settings, const char *group_name, const char *key, size_t *out_len); bool l_settings_set_bytes(struct l_settings *settings, const char *group_name, const char *key, const uint8_t *value, size_t value_len); bool l_settings_remove_key(struct l_settings *settings, const char *group_name, const char *key); bool l_settings_remove_group(struct l_settings *settings, const char *group_name); bool l_settings_remove_embedded_groups(struct l_settings *settings); char **l_settings_get_embedded_groups(struct l_settings *settings); bool l_settings_has_embedded_group(struct l_settings *settings, const char *group); const char *l_settings_get_embedded_value(struct l_settings *settings, const char *group_name, const char **out_type); #ifdef __cplusplus } #endif #endif /* __ELL_SETTINGS_H */ bluez-5.82/ell/PaxHeaders/private.h0000644000000000000000000000005014504767710014260 xustar0020 atime=1743575461 20 ctime=1743591276 bluez-5.82/ell/private.h0000644000000000000000000000032314504767710013737 0ustar00rootroot/* * Embedded Linux library * Copyright (C) 2011-2014 Intel Corporation * * SPDX-License-Identifier: LGPL-2.1-or-later */ #include #define LIB_EXPORT __attribute__ ((visibility("default"))) bluez-5.82/ell/PaxHeaders/siphash.c0000644000000000000000000000005014504767710014240 xustar0020 atime=1743575521 20 ctime=1743591277 bluez-5.82/ell/siphash.c0000644000000000000000000000476014504767710013730 0ustar00rootroot/* * Embedded Linux library * Copyright (C) 2011-2014 Intel Corporation * * SPDX-License-Identifier: LGPL-2.1-or-later */ #ifdef HAVE_CONFIG_H #include #endif #include "siphash-private.h" /* * Based on public domain SipHash reference C implementation * * Written in 2012 by * Jean-Philippe Aumasson * Daniel J. Bernstein * */ #define ROTL(x,b) (uint64_t) (((x) << (b)) | ((x) >> (64 - (b)))) #define U32TO8_LE(p, v) \ (p)[0] = (uint8_t) ((v)); \ (p)[1] = (uint8_t) ((v) >> 8); \ (p)[2] = (uint8_t) ((v) >> 16); \ (p)[3] = (uint8_t) ((v) >> 24); #define U64TO8_LE(p, v) \ U32TO8_LE((p), (uint32_t) ((v))); \ U32TO8_LE((p) + 4, (uint32_t) ((v) >> 32)); #define U8TO64_LE(p) \ (((uint64_t) ((p)[0])) | \ ((uint64_t) ((p)[1]) << 8) | \ ((uint64_t) ((p)[2]) << 16) | \ ((uint64_t) ((p)[3]) << 24) | \ ((uint64_t) ((p)[4]) << 32) | \ ((uint64_t) ((p)[5]) << 40) | \ ((uint64_t) ((p)[6]) << 48) | \ ((uint64_t) ((p)[7]) << 56)) #define SIPROUND \ do { \ v0 += v1; v1=ROTL(v1, 13); \ v1 ^= v0; v0=ROTL(v0, 32); \ v2 += v3; v3=ROTL(v3, 16); \ v3 ^= v2; \ v0 += v3; v3=ROTL(v3, 21); \ v3 ^= v0; \ v2 += v1; v1=ROTL(v1, 17); \ v1 ^= v2; v2=ROTL(v2, 32); \ } while(0) void _siphash24(uint8_t out[8], const uint8_t *in, size_t inlen, const uint8_t k[16]) { /* "somepseudorandomlygeneratedbytes" */ uint64_t v0 = 0x736f6d6570736575ULL; uint64_t v1 = 0x646f72616e646f6dULL; uint64_t v2 = 0x6c7967656e657261ULL; uint64_t v3 = 0x7465646279746573ULL; uint64_t b; uint64_t k0 = U8TO64_LE(k); uint64_t k1 = U8TO64_LE(k + 8); uint64_t m; const uint8_t *end = in + inlen - (inlen % sizeof(uint64_t)); const int left = inlen & 7; b = ((uint64_t) inlen) << 56; v3 ^= k1; v2 ^= k0; v1 ^= k1; v0 ^= k0; for (; in != end; in += 8) { m = U8TO64_LE(in); v3 ^= m; SIPROUND; SIPROUND; v0 ^= m; } switch (left) { case 7: b |= ((uint64_t) in[6]) << 48; /* fall through */ case 6: b |= ((uint64_t) in[5]) << 40; /* fall through */ case 5: b |= ((uint64_t) in[4]) << 32; /* fall through */ case 4: b |= ((uint64_t) in[3]) << 24; /* fall through */ case 3: b |= ((uint64_t) in[2]) << 16; /* fall through */ case 2: b |= ((uint64_t) in[1]) << 8; /* fall through */ case 1: b |= ((uint64_t) in[0]); break; case 0: break; } v3 ^= b; SIPROUND; SIPROUND; v0 ^= b; v2 ^= 0xff; SIPROUND; SIPROUND; SIPROUND; SIPROUND; b = v0 ^ v1 ^ v2 ^ v3; U64TO8_LE(out, b) } bluez-5.82/ell/PaxHeaders/strv.c0000644000000000000000000000005014560467756013610 xustar0020 atime=1743575475 20 ctime=1743591276 bluez-5.82/ell/strv.c0000644000000000000000000001466514560467756013305 0ustar00rootroot/* * Embedded Linux library * Copyright (C) 2011-2014 Intel Corporation * * SPDX-License-Identifier: LGPL-2.1-or-later */ #ifdef HAVE_CONFIG_H #include #endif #define _GNU_SOURCE #include #include "strv.h" #include "private.h" #include "useful.h" /** * SECTION:strv * @short_description: String array functions * * String array functions */ /** * l_strfreev: * @strlist: String list to free * * Frees a list of strings **/ LIB_EXPORT void l_strfreev(char **strlist) { l_strv_free(strlist); } /** * l_strsplit: * @str: String to split * @sep: The delimiter character * * Splits a string into pieces which do not contain the delimiter character. * As a special case, an empty string is returned as an empty array, e.g. * an array with just the NULL element. * * Note that this function only works with ASCII delimiters. * * Returns: A newly allocated %NULL terminated string array. This array * should be freed using l_strfreev(). **/ LIB_EXPORT char **l_strsplit(const char *str, const char sep) { int len; int i; const char *p; char **ret; if (unlikely(!str)) return NULL; if (str[0] == '\0') return l_new(char *, 1); for (p = str, len = 1; *p; p++) if (*p == sep) len += 1; ret = l_new(char *, len + 1); i = 0; p = str; len = 0; while (p[len]) { if (p[len] != sep) { len += 1; continue; } ret[i++] = l_strndup(p, len); p += len + 1; len = 0; } ret[i++] = l_strndup(p, len); return ret; } /** * l_strsplit_set: * @str: String to split * @separators: A set of delimiters * * Splits a string into pieces which do not contain the delimiter characters * that can be found in @separators. * As a special case, an empty string is returned as an empty array, e.g. * an array with just the NULL element. * * Note that this function only works with ASCII delimiters. * * Returns: A newly allocated %NULL terminated string array. This array * should be freed using l_strfreev(). **/ LIB_EXPORT char **l_strsplit_set(const char *str, const char *separators) { int len; int i; const char *p; char **ret; bool sep_table[256]; if (unlikely(!str)) return NULL; if (str[0] == '\0') return l_new(char *, 1); memset(sep_table, 0, sizeof(sep_table)); for (p = separators; *p; p++) sep_table[(unsigned char) *p] = true; for (p = str, len = 1; *p; p++) if (sep_table[(unsigned char) *p] == true) len += 1; ret = l_new(char *, len + 1); i = 0; p = str; len = 0; while (p[len]) { if (sep_table[(unsigned char) p[len]] != true) { len += 1; continue; } ret[i++] = l_strndup(p, len); p += len + 1; len = 0; } ret[i++] = l_strndup(p, len); return ret; } /** * l_strjoinv: * @str_array: a %NULL terminated array of strings to join * @delim: Delimiting character * * Joins strings contanied in the @str_array into one long string delimited * by @delim. * * Returns: A newly allocated string that should be freed using l_free() */ LIB_EXPORT char *l_strjoinv(char **str_array, const char delim) { size_t len = 0; unsigned int i; char *ret; char *p; if (unlikely(!str_array)) return NULL; if (!str_array[0]) return l_strdup(""); for (i = 0; str_array[i]; i++) len += strlen(str_array[i]); len += 1 + i - 1; ret = l_malloc(len); p = stpcpy(ret, str_array[0]); for (i = 1; str_array[i]; i++) { *p++ = delim; p = stpcpy(p, str_array[i]); } return ret; } /** * l_strv_new: * * Returns: new empty string array **/ LIB_EXPORT char **l_strv_new(void) { return l_new(char *, 1); } /** * l_strv_free: * @str_array: a %NULL terminated array of strings * * Frees strings in @str_array and @str_array itself **/ LIB_EXPORT void l_strv_free(char **str_array) { if (likely(str_array)) { int i; for (i = 0; str_array[i]; i++) l_free(str_array[i]); l_free(str_array); } } /** * l_strv_length: * @str_array: a %NULL terminated array of strings * * Returns: the number of strings in @str_array */ LIB_EXPORT unsigned int l_strv_length(char **str_array) { unsigned int i = 0; if (unlikely(!str_array)) return 0; while (str_array[i]) i += 1; return i; } /** * l_strv_contains: * @str_array: a %NULL terminated array of strings * @item: An item to search for, must be not %NULL * * Returns: #true if @str_array contains item */ LIB_EXPORT bool l_strv_contains(char **str_array, const char *item) { unsigned int i = 0; if (unlikely(!str_array || !item)) return false; while (str_array[i]) { if (!strcmp(str_array[i], item)) return true; i += 1; } return false; } /** * l_strv_append: * @str_array: a %NULL terminated array of strings or %NULL * @str: A string to be appened at the end of @str_array * * Returns: New %NULL terminated array of strings with @str added */ LIB_EXPORT char **l_strv_append(char **str_array, const char *str) { char **ret; unsigned int i, len; if (unlikely(!str)) return str_array; len = l_strv_length(str_array); ret = l_new(char *, len + 2); for (i = 0; i < len; i++) ret[i] = str_array[i]; ret[i] = l_strdup(str); l_free(str_array); return ret; } LIB_EXPORT char **l_strv_append_printf(char **str_array, const char *format, ...) { va_list args; char **ret; va_start(args, format); ret = l_strv_append_vprintf(str_array, format, args); va_end(args); return ret; } LIB_EXPORT char **l_strv_append_vprintf(char **str_array, const char *format, va_list args) { char **ret; unsigned int i, len; if (unlikely(!format)) return str_array; len = l_strv_length(str_array); ret = l_new(char *, len + 2); for (i = 0; i < len; i++) ret[i] = str_array[i]; ret[i] = l_strdup_vprintf(format, args); l_free(str_array); return ret; } /** * l_strv_copy: * @str_array: a %NULL terminated array of strings or %NULL * * Returns: An independent copy of @str_array. */ LIB_EXPORT char **l_strv_copy(char **str_array) { int i, len; char **copy; if (unlikely(!str_array)) return NULL; for (len = 0; str_array[len]; len++); copy = l_malloc(sizeof(char *) * (len + 1)); for (i = len; i >= 0; i--) copy[i] = l_strdup(str_array[i]); return copy; } /** * l_strv_eq: * @a: a %NULL terminated array of strings or %NULL * @b: another %NULL terminated array of strings or %NULL * * Returns: Whether @a and @b's contents are identical, including the * order, or @a and @b are both %NULL. */ LIB_EXPORT bool l_strv_eq(char **a, char **b) { if (!a || !b) return a == b; for (; *a; a++, b++) if (!*b || strcmp(*a, *b)) return false; return !*b; } bluez-5.82/ell/PaxHeaders/dbus-service.h0000644000000000000000000000005014504767710015201 xustar0020 atime=1743575449 20 ctime=1743591276 bluez-5.82/ell/dbus-service.h0000644000000000000000000000375114504767710014670 0ustar00rootroot/* * Embedded Linux library * Copyright (C) 2011-2014 Intel Corporation * * SPDX-License-Identifier: LGPL-2.1-or-later */ #ifndef __ELL_SERVICE_H #define __ELL_SERVICE_H #include #include #ifdef __cplusplus extern "C" { #endif struct l_dbus; struct l_dbus_interface; struct l_dbus_message; enum l_dbus_method_flag { L_DBUS_METHOD_FLAG_DEPRECATED = 1, L_DBUS_METHOD_FLAG_NOREPLY = 2, L_DBUS_METHOD_FLAG_ASYNC = 4, }; enum l_dbus_signal_flag { L_DBUS_SIGNAL_FLAG_DEPRECATED = 1, }; enum l_dbus_property_flag { L_DBUS_PROPERTY_FLAG_DEPRECATED = 1, L_DBUS_PROPERTY_FLAG_AUTO_EMIT = 2, }; typedef struct l_dbus_message *(*l_dbus_interface_method_cb_t) (struct l_dbus *, struct l_dbus_message *message, void *user_data); typedef void (*l_dbus_property_complete_cb_t) (struct l_dbus *, struct l_dbus_message *, struct l_dbus_message *error); typedef struct l_dbus_message *(*l_dbus_property_set_cb_t) (struct l_dbus *, struct l_dbus_message *message, struct l_dbus_message_iter *new_value, l_dbus_property_complete_cb_t complete, void *user_data); typedef bool (*l_dbus_property_get_cb_t) (struct l_dbus *, struct l_dbus_message *message, struct l_dbus_message_builder *builder, void *user_data); bool l_dbus_interface_method(struct l_dbus_interface *interface, const char *name, uint32_t flags, l_dbus_interface_method_cb_t cb, const char *return_sig, const char *param_sig, ...); bool l_dbus_interface_signal(struct l_dbus_interface *interface, const char *name, uint32_t flags, const char *signature, ...); bool l_dbus_interface_property(struct l_dbus_interface *interface, const char *name, uint32_t flags, const char *signature, l_dbus_property_get_cb_t getter, l_dbus_property_set_cb_t setter); bool l_dbus_property_changed(struct l_dbus *dbus, const char *path, const char *interface, const char *property); #ifdef __cplusplus } #endif #endif /* __ELL_DBUS_SERVICE_H */ bluez-5.82/ell/PaxHeaders/ecdh.h0000644000000000000000000000005014504767710013511 xustar0020 atime=1743575449 20 ctime=1743591276 bluez-5.82/ell/ecdh.h0000644000000000000000000000150314504767710013171 0ustar00rootroot/* * Embedded Linux library * Copyright (C) 2018 Intel Corporation * * SPDX-License-Identifier: LGPL-2.1-or-later */ #ifndef __ELL_ECDH_H #define __ELL_ECDH_H #ifdef __cplusplus extern "C" { #endif struct l_ecc_curve; struct l_ecc_point; struct l_ecc_scalar; /* * Generate a private/public key pair. private/public are out parameters and * must be freed. */ bool l_ecdh_generate_key_pair(const struct l_ecc_curve *curve, struct l_ecc_scalar **out_private, struct l_ecc_point **out_public); /* * Generate a shared secret from a private/public key. secret is an out * parameters and must be freed. */ bool l_ecdh_generate_shared_secret(const struct l_ecc_scalar *private_key, const struct l_ecc_point *other_public, struct l_ecc_scalar **secret); #ifdef __cplusplus } #endif #endif /* __ELL_ECDH_H */ bluez-5.82/ell/PaxHeaders/idle.c0000644000000000000000000000005014560467756013527 xustar0020 atime=1743575470 20 ctime=1743591276 bluez-5.82/ell/idle.c0000644000000000000000000000557714560467756013226 0ustar00rootroot/* * Embedded Linux library * Copyright (C) 2011-2014 Intel Corporation * * SPDX-License-Identifier: LGPL-2.1-or-later */ #ifdef HAVE_CONFIG_H #include #endif #include #include #include #include "useful.h" #include "idle.h" #include "main-private.h" #include "private.h" /** * SECTION:idle * @short_description: Idle processing support * * Idle processing support */ /** * l_idle: * * Opaque object representing the idle time event. */ struct l_idle { union { l_idle_notify_cb_t callback; l_idle_oneshot_cb_t oneshot; }; l_idle_destroy_cb_t destroy; void *user_data; int id; }; static void idle_destroy(void *user_data) { struct l_idle *idle = user_data; if (idle->destroy) idle->destroy(idle->user_data); l_free(idle); } static void idle_callback(void *user_data) { struct l_idle *idle = user_data; if (idle->callback) idle->callback(idle, idle->user_data); } static void oneshot_callback(void *user_data) { struct l_idle *idle = user_data; if (idle->oneshot) idle->oneshot(idle->user_data); idle_remove(idle->id); } /** * l_idle_create: * @callback: idle callback function * @user_data: user data provided to idle callback function * @destroy: destroy function for user data * * Create a new idle event processing object. * * The idle callback will be called until canceled using l_idle_remove(). * * Returns: a newly allocated #l_idle object **/ LIB_EXPORT struct l_idle *l_idle_create(l_idle_notify_cb_t callback, void *user_data, l_idle_destroy_cb_t destroy) { struct l_idle *idle; if (unlikely(!callback)) return NULL; idle = l_new(struct l_idle, 1); idle->callback = callback; idle->destroy = destroy; idle->user_data = user_data; idle->id = idle_add(idle_callback, idle, 0, idle_destroy); if (idle->id < 0) { l_free(idle); return NULL; } return idle; } /** * l_idle_oneshot: * @callback: idle callback function * @user_data: user data provided to idle callback function * @destroy: destroy function for user data * * Create a new idle event processing object. The callback will be called * only once at which point the object will be destroyed. * * Returns: true if the oneshot idle object could be created successfully. **/ LIB_EXPORT bool l_idle_oneshot(l_idle_oneshot_cb_t callback, void *user_data, l_idle_destroy_cb_t destroy) { struct l_idle *idle; if (unlikely(!callback)) return NULL; idle = l_new(struct l_idle, 1); idle->oneshot = callback; idle->destroy = destroy; idle->user_data = user_data; idle->id = idle_add(oneshot_callback, idle, IDLE_FLAG_NO_WARN_DANGLING, idle_destroy); if (idle->id < 0) { l_free(idle); return false; } return true; } /** * l_idle_remove: * @idle: idle object * * Remove idle event processing object. **/ LIB_EXPORT void l_idle_remove(struct l_idle *idle) { if (unlikely(!idle)) return; idle_remove(idle->id); } bluez-5.82/ell/PaxHeaders/cert.h0000644000000000000000000000005014504767710013543 xustar0020 atime=1743575449 20 ctime=1743591276 bluez-5.82/ell/cert.h0000644000000000000000000000376614504767710013240 0ustar00rootroot/* * Embedded Linux library * Copyright (C) 2018 Intel Corporation * * SPDX-License-Identifier: LGPL-2.1-or-later */ #ifndef __ELL_CERT_H #define __ELL_CERT_H #include #include #ifdef __cplusplus extern "C" { #endif struct l_queue; struct l_cert; struct l_certchain; enum l_cert_key_type { L_CERT_KEY_RSA, L_CERT_KEY_ECC, L_CERT_KEY_UNKNOWN, }; typedef bool (*l_cert_walk_cb_t)(struct l_cert *cert, void *user_data); struct l_cert *l_cert_new_from_der(const uint8_t *buf, size_t buf_len); void l_cert_free(struct l_cert *cert); DEFINE_CLEANUP_FUNC(l_cert_free); const uint8_t *l_cert_get_der_data(struct l_cert *cert, size_t *out_len); const uint8_t *l_cert_get_dn(struct l_cert *cert, size_t *out_len); bool l_cert_get_valid_times(struct l_cert *cert, uint64_t *out_not_before_time, uint64_t *out_not_after_time); enum l_cert_key_type l_cert_get_pubkey_type(struct l_cert *cert); struct l_key *l_cert_get_pubkey(struct l_cert *cert); void l_certchain_free(struct l_certchain *chain); DEFINE_CLEANUP_FUNC(l_certchain_free); struct l_cert *l_certchain_get_leaf(struct l_certchain *chain); void l_certchain_walk_from_leaf(struct l_certchain *chain, l_cert_walk_cb_t cb, void *user_data); void l_certchain_walk_from_ca(struct l_certchain *chain, l_cert_walk_cb_t cb, void *user_data); bool l_certchain_verify(struct l_certchain *chain, struct l_queue *ca_certs, const char **error); bool l_cert_load_container_file(const char *filename, const char *password, struct l_certchain **out_certchain, struct l_key **out_privkey, bool *out_encrypted); bool l_cert_pkcs5_pbkdf1(enum l_checksum_type type, const char *password, const uint8_t *salt, size_t salt_len, unsigned int iter_count, uint8_t *out_dk, size_t dk_len); bool l_cert_pkcs5_pbkdf2(enum l_checksum_type type, const char *password, const uint8_t *salt, size_t salt_len, unsigned int iter_count, uint8_t *out_dk, size_t dk_len); #ifdef __cplusplus } #endif #endif /* __ELL_CERT_H */ bluez-5.82/ell/PaxHeaders/ecc.h0000644000000000000000000000005014770744372013344 xustar0020 atime=1743575449 20 ctime=1743591276 bluez-5.82/ell/ecc.h0000644000000000000000000001012314770744372013022 0ustar00rootroot/* * Embedded Linux library * Copyright (C) 2018 Intel Corporation * * SPDX-License-Identifier: LGPL-2.1-or-later */ #ifndef __ELL_ECC_H #define __ELL_ECC_H #include #include #ifdef __cplusplus extern "C" { #endif #define L_ECC_MAX_DIGITS 9 #define L_ECC_SCALAR_MAX_BYTES L_ECC_MAX_DIGITS * 8 #define L_ECC_POINT_MAX_BYTES L_ECC_SCALAR_MAX_BYTES * 2 struct l_ecc_curve; struct l_ecc_point; struct l_ecc_scalar; enum l_ecc_point_type { L_ECC_POINT_TYPE_COMPLIANT = 0x01, L_ECC_POINT_TYPE_COMPRESSED_BIT0 = 0x02, L_ECC_POINT_TYPE_COMPRESSED_BIT1 = 0x03, L_ECC_POINT_TYPE_FULL = 0x04, }; const unsigned int *l_ecc_supported_ike_groups(void); const unsigned int *l_ecc_supported_tls_groups(void); const struct l_ecc_curve *l_ecc_curve_from_name(const char *name); const struct l_ecc_curve *l_ecc_curve_from_ike_group(unsigned int group); const struct l_ecc_curve *l_ecc_curve_from_tls_group(unsigned int group); const char *l_ecc_curve_get_name(const struct l_ecc_curve *curve); unsigned int l_ecc_curve_get_ike_group(const struct l_ecc_curve *curve); unsigned int l_ecc_curve_get_tls_group(const struct l_ecc_curve *curve); struct l_ecc_scalar *l_ecc_curve_get_order(const struct l_ecc_curve *curve); struct l_ecc_scalar *l_ecc_curve_get_prime(const struct l_ecc_curve *curve); size_t l_ecc_curve_get_scalar_bytes(const struct l_ecc_curve *curve); struct l_ecc_point *l_ecc_point_new(const struct l_ecc_curve *curve); struct l_ecc_point *l_ecc_point_from_data(const struct l_ecc_curve *curve, enum l_ecc_point_type type, const void *data, size_t len); struct l_ecc_point *l_ecc_point_from_sswu(const struct l_ecc_scalar *u); struct l_ecc_point *l_ecc_point_clone(const struct l_ecc_point *p); const struct l_ecc_curve *l_ecc_point_get_curve(const struct l_ecc_point *p); ssize_t l_ecc_point_get_x(const struct l_ecc_point *p, void *x, size_t xlen); ssize_t l_ecc_point_get_y(const struct l_ecc_point *p, void *y, size_t ylen); bool l_ecc_point_y_isodd(const struct l_ecc_point *p); ssize_t l_ecc_point_get_data(const struct l_ecc_point *p, void *buf, size_t len); void l_ecc_point_free(struct l_ecc_point *p); DEFINE_CLEANUP_FUNC(l_ecc_point_free); struct l_ecc_scalar *l_ecc_scalar_new(const struct l_ecc_curve *curve, const void *buf, size_t len); struct l_ecc_scalar *l_ecc_scalar_clone(const struct l_ecc_scalar *s); struct l_ecc_scalar *l_ecc_scalar_new_random( const struct l_ecc_curve *curve); struct l_ecc_scalar *l_ecc_scalar_new_modp(const struct l_ecc_curve *curve, const void *buf, size_t len); struct l_ecc_scalar *l_ecc_scalar_new_modn(const struct l_ecc_curve *curve, const void *buf, size_t len); struct l_ecc_scalar *l_ecc_scalar_new_reduced_1_to_n( const struct l_ecc_curve *curve, const void *buf, size_t len); ssize_t l_ecc_scalar_get_data(const struct l_ecc_scalar *c, void *buf, size_t len); void l_ecc_scalar_free(struct l_ecc_scalar *c); DEFINE_CLEANUP_FUNC(l_ecc_scalar_free); /* Constant operations */ bool l_ecc_scalar_add(struct l_ecc_scalar *ret, const struct l_ecc_scalar *a, const struct l_ecc_scalar *b, const struct l_ecc_scalar *mod); /* Point operations */ bool l_ecc_point_multiply(struct l_ecc_point *ret, const struct l_ecc_scalar *scalar, const struct l_ecc_point *point); bool l_ecc_point_multiply_g(struct l_ecc_point *ret, const struct l_ecc_scalar *scalar); bool l_ecc_point_add(struct l_ecc_point *ret, const struct l_ecc_point *a, const struct l_ecc_point *b); bool l_ecc_point_inverse(struct l_ecc_point *p); /* extra operations needed for SAE */ bool l_ecc_scalar_multiply(struct l_ecc_scalar *ret, const struct l_ecc_scalar *a, const struct l_ecc_scalar *b); int l_ecc_scalar_legendre(struct l_ecc_scalar *value); bool l_ecc_scalar_sum_x(struct l_ecc_scalar *ret, const struct l_ecc_scalar *x); bool l_ecc_scalars_are_equal(const struct l_ecc_scalar *a, const struct l_ecc_scalar *b); bool l_ecc_points_are_equal(const struct l_ecc_point *a, const struct l_ecc_point *b); bool l_ecc_point_is_infinity(const struct l_ecc_point *p); #ifdef __cplusplus } #endif #endif /* __ELL_ECC_H */ bluez-5.82/ell/PaxHeaders/tester.c0000644000000000000000000000005014504767710014107 xustar0020 atime=1743575522 20 ctime=1743591277 bluez-5.82/ell/tester.c0000644000000000000000000003732214504767710013577 0ustar00rootroot/* * Embedded Linux library * Copyright (C) 2021 Intel Corporation * * SPDX-License-Identifier: LGPL-2.1-or-later */ #ifdef HAVE_CONFIG_H #include #endif #define _GNU_SOURCE #include #include #include #include #include #include "idle.h" #include "log.h" #include "private.h" #include "queue.h" #include "time.h" #include "timeout.h" #include "useful.h" #include "tester.h" /** * SECTION:tester * @short_description: Non-interactive test framework * * Non-interactive test framework */ #define COLOR_OFF "\x1B[0m" #define COLOR_BLACK "\x1B[0;30m" #define COLOR_RED "\x1B[0;31m" #define COLOR_GREEN "\x1B[0;32m" #define COLOR_YELLOW "\x1B[0;33m" #define COLOR_BLUE "\x1B[0;34m" #define COLOR_MAGENTA "\x1B[0;35m" #define COLOR_HIGHLIGHT "\x1B[1;39m" #define print_text(color, fmt, args...) \ l_info(color fmt COLOR_OFF, ## args) #define print_summary(label, color, value, fmt, args...) \ l_info("%-52s " color "%-10s" COLOR_OFF fmt, \ label, value, ## args) #define print_progress(name, color, fmt, args...) \ l_info(COLOR_HIGHLIGHT "%s" COLOR_OFF " - " \ color fmt COLOR_OFF, name, ## args) enum test_result { TEST_RESULT_NOT_RUN, TEST_RESULT_PASSED, TEST_RESULT_FAILED, TEST_RESULT_TIMED_OUT, }; struct l_tester { uint64_t start_time; struct l_queue *tests; const struct l_queue_entry *test_entry; bool list_cases; const char *prefix; const char *substring; l_tester_finish_func_t finish_callback; }; struct test_case { uint64_t start_time; uint64_t end_time; char *name; enum test_result result; enum l_tester_stage stage; const void *test_data; l_tester_data_func_t pre_setup_func; l_tester_data_func_t setup_func; l_tester_data_func_t test_func; l_tester_data_func_t teardown_func; l_tester_data_func_t post_teardown_func; unsigned int timeout; struct l_timeout *run_timer; l_tester_destroy_func_t destroy; void *user_data; bool teardown; }; static void destroy_test(void *data) { struct test_case *test = data; l_timeout_remove(test->run_timer); if (test->destroy) test->destroy(test->user_data); l_free(test->name); l_free(test); } static uint64_t get_elapsed_time(uint64_t base) { uint64_t now; now = l_time_now(); return l_time_diff(base, now); } static void teardown_callback(void *user_data) { struct l_tester *tester = user_data; struct test_case *test; test = tester->test_entry->data; test->stage = L_TESTER_STAGE_TEARDOWN; test->teardown = false; print_progress(test->name, COLOR_MAGENTA, "teardown"); if (test->teardown_func) test->teardown_func(test->test_data); else l_tester_teardown_complete(tester); } static void test_timeout(struct l_timeout *timer, void *user_data) { struct l_tester *tester = user_data; struct test_case *test; test = tester->test_entry->data; l_timeout_remove(timer); test->run_timer = NULL; test->result = TEST_RESULT_TIMED_OUT; print_progress(test->name, COLOR_RED, "test timed out"); l_idle_oneshot(teardown_callback, tester, NULL); } static void next_test_case(struct l_tester *tester) { struct test_case *test; if (tester->test_entry) tester->test_entry = tester->test_entry->next; else tester->test_entry = l_queue_get_entries(tester->tests); if (!tester->test_entry) { if (tester->finish_callback) tester->finish_callback(tester); return; } test = tester->test_entry->data; print_progress(test->name, COLOR_BLACK, "init"); test->start_time = get_elapsed_time(tester->start_time); if (test->timeout > 0) test->run_timer = l_timeout_create(test->timeout, test_timeout, tester, NULL); test->stage = L_TESTER_STAGE_PRE_SETUP; if (test->pre_setup_func) test->pre_setup_func(test->test_data); else l_tester_pre_setup_complete(tester); } static void setup_callback(void *user_data) { struct l_tester *tester = user_data; struct test_case *test = tester->test_entry->data; test->stage = L_TESTER_STAGE_SETUP; print_progress(test->name, COLOR_BLUE, "setup"); if (test->setup_func) test->setup_func(test->test_data); else l_tester_setup_complete(tester); } static void run_callback(void *user_data) { struct l_tester *tester = user_data; struct test_case *test = tester->test_entry->data; test->stage = L_TESTER_STAGE_RUN; print_progress(test->name, COLOR_BLACK, "run"); test->test_func(test->test_data); } static void done_callback(void *user_data) { struct l_tester *tester = user_data; struct test_case *test = tester->test_entry->data; test->end_time = get_elapsed_time(tester->start_time); print_progress(test->name, COLOR_BLACK, "done"); next_test_case(tester); } LIB_EXPORT void *l_tester_get_data(struct l_tester *tester) { struct test_case *test; if (unlikely(!tester)) return NULL; if (!tester->test_entry) return NULL; test = tester->test_entry->data; return test->user_data; } LIB_EXPORT void l_tester_pre_setup_complete(struct l_tester *tester) { struct test_case *test; if (unlikely(!tester)) return; if (!tester->test_entry) return; test = tester->test_entry->data; if (test->stage != L_TESTER_STAGE_PRE_SETUP) return; l_idle_oneshot(setup_callback, tester, NULL); } LIB_EXPORT void l_tester_pre_setup_failed(struct l_tester *tester) { struct test_case *test; if (unlikely(!tester)) return; if (!tester->test_entry) return; test = tester->test_entry->data; if (test->stage != L_TESTER_STAGE_PRE_SETUP) return; print_progress(test->name, COLOR_RED, "pre setup failed"); l_timeout_remove(test->run_timer); test->run_timer = NULL; l_idle_oneshot(done_callback, tester, NULL); } LIB_EXPORT void l_tester_setup_complete(struct l_tester *tester) { struct test_case *test; if (unlikely(!tester)) return; if (!tester->test_entry) return; test = tester->test_entry->data; if (test->stage != L_TESTER_STAGE_SETUP) return; print_progress(test->name, COLOR_BLUE, "setup complete"); l_idle_oneshot(run_callback, tester, NULL); } LIB_EXPORT void l_tester_setup_failed(struct l_tester *tester) { struct test_case *test; if (unlikely(!tester)) return; if (!tester->test_entry) return; test = tester->test_entry->data; if (test->stage != L_TESTER_STAGE_SETUP) return; test->stage = L_TESTER_STAGE_POST_TEARDOWN; l_timeout_remove(test->run_timer); test->run_timer = NULL; print_progress(test->name, COLOR_RED, "setup failed"); print_progress(test->name, COLOR_MAGENTA, "teardown"); test->post_teardown_func(test->test_data); } static void test_result(struct l_tester *tester, enum test_result result) { struct test_case *test; if (unlikely(!tester)) return; if (!tester->test_entry) return; test = tester->test_entry->data; if (test->stage != L_TESTER_STAGE_RUN) return; l_timeout_remove(test->run_timer); test->run_timer = NULL; test->result = result; switch (result) { case TEST_RESULT_PASSED: print_progress(test->name, COLOR_GREEN, "test passed"); break; case TEST_RESULT_FAILED: print_progress(test->name, COLOR_RED, "test failed"); break; case TEST_RESULT_NOT_RUN: print_progress(test->name, COLOR_YELLOW, "test not run"); break; case TEST_RESULT_TIMED_OUT: print_progress(test->name, COLOR_RED, "test timed out"); break; } if (test->teardown) return; test->teardown = true; l_idle_oneshot(teardown_callback, tester, NULL); } LIB_EXPORT void l_tester_test_passed(struct l_tester *tester) { if (unlikely(!tester)) return; test_result(tester, TEST_RESULT_PASSED); } LIB_EXPORT void l_tester_test_failed(struct l_tester *tester) { if (unlikely(!tester)) return; test_result(tester, TEST_RESULT_FAILED); } LIB_EXPORT void l_tester_test_abort(struct l_tester *tester) { if (unlikely(!tester)) return; test_result(tester, TEST_RESULT_NOT_RUN); } LIB_EXPORT void l_tester_teardown_complete(struct l_tester *tester) { struct test_case *test; if (unlikely(!tester)) return; if (!tester->test_entry) return; test = tester->test_entry->data; if (test->stage != L_TESTER_STAGE_TEARDOWN) return; test->stage = L_TESTER_STAGE_POST_TEARDOWN; if (test->post_teardown_func) test->post_teardown_func(test->test_data); else l_tester_post_teardown_complete(tester); } LIB_EXPORT void l_tester_teardown_failed(struct l_tester *tester) { struct test_case *test; if (unlikely(!tester)) return; if (!tester->test_entry) return; test = tester->test_entry->data; if (test->stage != L_TESTER_STAGE_TEARDOWN) return; test->stage = L_TESTER_STAGE_POST_TEARDOWN; l_tester_post_teardown_failed(tester); } LIB_EXPORT void l_tester_post_teardown_complete(struct l_tester *tester) { struct test_case *test; if (unlikely(!tester)) return; if (!tester->test_entry) return; test = tester->test_entry->data; if (test->stage != L_TESTER_STAGE_POST_TEARDOWN) return; print_progress(test->name, COLOR_MAGENTA, "teardown complete"); l_idle_oneshot(done_callback, tester, NULL); } LIB_EXPORT void l_tester_post_teardown_failed(struct l_tester *tester) { struct test_case *test; if (unlikely(!tester)) return; if (!tester->test_entry) return; test = tester->test_entry->data; if (test->stage != L_TESTER_STAGE_POST_TEARDOWN) return; print_progress(test->name, COLOR_RED, "teardown failed"); l_idle_oneshot(done_callback, tester, NULL); } struct wait_data { unsigned int seconds; struct test_case *test; l_tester_wait_func_t func; void *user_data; }; static void wait_callback(struct l_timeout *timer, void *user_data) { struct wait_data *wait = user_data; struct test_case *test = wait->test; wait->seconds--; if (wait->seconds > 0) { print_progress(test->name, COLOR_BLACK, "%u seconds left", wait->seconds); return; } print_progress(test->name, COLOR_BLACK, "waiting done"); wait->func(wait->user_data); l_free(wait); l_timeout_remove(timer); } LIB_EXPORT void l_tester_wait(struct l_tester *tester, unsigned int seconds, l_tester_wait_func_t func, void *user_data) { struct test_case *test; struct wait_data *wait; if (unlikely(!tester)) return; if (!func || seconds < 1) return; if (!tester->test_entry) return; test = tester->test_entry->data; wait = l_new(struct wait_data, 1); wait->seconds = seconds; wait->test = test; wait->func = func; wait->user_data = user_data; l_timeout_create(seconds, wait_callback, wait, NULL); print_progress(test->name, COLOR_BLACK, "waiting %u seconds", seconds); } /** * l_tester_add_full: * @tester: tester instance * @name: test case name * @test_data: test data * @pre_setup_func: test pre-setup function * @setup_func: test setup function * @test_func: test function * @teardown_func: test teardown function * @teardown_func: test post-teardown function * @timeout: test teardown function * @user_data: user data * @destroy: user data destroy function * * Add a new test case. **/ LIB_EXPORT void l_tester_add_full(struct l_tester *tester, const char *name, const void *test_data, l_tester_data_func_t pre_setup_func, l_tester_data_func_t setup_func, l_tester_data_func_t test_func, l_tester_data_func_t teardown_func, l_tester_data_func_t post_teardown_func, unsigned int timeout, void *user_data, l_tester_destroy_func_t destroy) { struct test_case *test; if (unlikely(!tester || !test_func)) return; if (tester->prefix && !l_str_has_prefix(name, tester->prefix)) { if (destroy) destroy(user_data); return; } if (tester->substring && !strstr(name, tester->substring)) { if (destroy) destroy(user_data); return; } if (tester->list_cases) { l_info("%s", name); if (destroy) destroy(user_data); return; } test = l_new(struct test_case, 1); test->name = l_strdup(name); test->result = TEST_RESULT_NOT_RUN; test->stage = L_TESTER_STAGE_INVALID; test->test_data = test_data; test->pre_setup_func = pre_setup_func; test->setup_func = setup_func; test->test_func = test_func; test->teardown_func = teardown_func; test->post_teardown_func = post_teardown_func; test->timeout = timeout; test->destroy = destroy; test->user_data = user_data; l_queue_push_tail(tester->tests, test); } /** * l_tester_add: * @tester: tester instance * @name: test case name * @test_data: test data * @setup_func: test setup function * @test_func: test function * @teardown_func: test teardown function * * Add a new test with default settings for timeout and no pre-setup procedure. **/ LIB_EXPORT void l_tester_add(struct l_tester *tester, const char *name, const void *test_data, l_tester_data_func_t setup_func, l_tester_data_func_t test_func, l_tester_data_func_t teardown_func) { l_tester_add_full(tester, name, test_data, NULL, setup_func, test_func, teardown_func, NULL, 0, NULL, NULL); } /** * l_tester_new: * * Initialize tester framework. * * Returns: new tester instance **/ LIB_EXPORT struct l_tester *l_tester_new(const char *prefix, const char *substring, bool list_cases) { struct l_tester *tester = l_new(struct l_tester, 1); tester->prefix = prefix; tester->substring = substring; tester->list_cases = list_cases; tester->tests = l_queue_new(); return tester; } /** * l_tester_start: * @tester: tester instance * * Kick off execution of the test queue * **/ LIB_EXPORT void l_tester_start(struct l_tester *tester, l_tester_finish_func_t finish_func) { if (unlikely(!tester)) return; if (!tester->tests) return; tester->finish_callback = finish_func; tester->start_time = l_time_now(); next_test_case(tester); } /** * l_tester_summarize: * @tester: tester instance * * Print summary of all added test cases. * * Returns: true, if all the tests passed * false, if any of the tests failed **/ LIB_EXPORT bool l_tester_summarize(struct l_tester *tester) { unsigned int not_run = 0, passed = 0, failed = 0; double execution_time; const struct l_queue_entry *entry; if (unlikely(!tester)) return false; l_info(COLOR_HIGHLIGHT "%s" COLOR_OFF, "\n\nTest Summary\n------------"); entry = l_queue_get_entries(tester->tests); for (; entry; entry = entry->next) { struct test_case *test = entry->data; double exec_time; exec_time = (test->end_time - test->start_time) / (double)L_USEC_PER_SEC; switch (test->result) { case TEST_RESULT_NOT_RUN: print_summary(test->name, COLOR_YELLOW, "Not Run", ""); not_run++; break; case TEST_RESULT_PASSED: print_summary(test->name, COLOR_GREEN, "Passed", "%8.3f seconds", exec_time); passed++; break; case TEST_RESULT_FAILED: print_summary(test->name, COLOR_RED, "Failed", "%8.3f seconds", exec_time); failed++; break; case TEST_RESULT_TIMED_OUT: print_summary(test->name, COLOR_RED, "Timed out", "%8.3f seconds", exec_time); failed++; break; } } l_info("Total: %d, " COLOR_GREEN "Passed: %d (%.1f%%)" COLOR_OFF ", " COLOR_RED "Failed: %d" COLOR_OFF ", " COLOR_YELLOW "Not Run: %d" COLOR_OFF, not_run + passed + failed, passed, (not_run + passed + failed) ? (float) passed * 100 / (not_run + passed + failed) : 0, failed, not_run); execution_time = get_elapsed_time(tester->start_time); l_info("Overall execution time: %8.3f seconds", execution_time / (double)L_USEC_PER_SEC); return failed; } /** * l_tester_destroy: * @tester: tester instance * * Free up the teter framework resources * **/ LIB_EXPORT void l_tester_destroy(struct l_tester *tester) { if (unlikely(!tester)) return; l_queue_destroy(tester->tests, destroy_test); l_free(tester); } /** * l_tester_get_stage: * @tester: tester instance * * Get the current test stage * * Returns: the stage of the current test that is being processing. * **/ LIB_EXPORT enum l_tester_stage l_tester_get_stage(struct l_tester *tester) { struct test_case *test; if (unlikely(!tester)) return L_TESTER_STAGE_INVALID; if (!tester->test_entry) return L_TESTER_STAGE_INVALID; test = tester->test_entry->data; return test->stage; } bluez-5.82/ell/PaxHeaders/idle.h0000644000000000000000000000005014504767710013523 xustar0020 atime=1743575449 20 ctime=1743591276 bluez-5.82/ell/idle.h0000644000000000000000000000134614504767710013210 0ustar00rootroot/* * Embedded Linux library * Copyright (C) 2011-2014 Intel Corporation * * SPDX-License-Identifier: LGPL-2.1-or-later */ #ifndef __ELL_IDLE_H #define __ELL_IDLE_H #include #ifdef __cplusplus extern "C" { #endif struct l_idle; typedef void (*l_idle_notify_cb_t) (struct l_idle *idle, void *user_data); typedef void (*l_idle_oneshot_cb_t) (void *user_data); typedef void (*l_idle_destroy_cb_t) (void *user_data); struct l_idle *l_idle_create(l_idle_notify_cb_t callback, void *user_data, l_idle_destroy_cb_t destroy); void l_idle_remove(struct l_idle *idle); bool l_idle_oneshot(l_idle_oneshot_cb_t callback, void *user_data, l_idle_destroy_cb_t destroy); #ifdef __cplusplus } #endif #endif /* __ELL_IDLE_H */ bluez-5.82/ell/PaxHeaders/time.c0000644000000000000000000000005014667515664013550 xustar0020 atime=1743575468 20 ctime=1743591276 bluez-5.82/ell/time.c0000644000000000000000000000620014667515664013227 0ustar00rootroot/* * Embedded Linux library * Copyright (C) 2019 Intel Corporation * * SPDX-License-Identifier: LGPL-2.1-or-later */ #ifdef HAVE_CONFIG_H #include #endif #define _GNU_SOURCE #include #include #include "time.h" #include "time-private.h" #include "random.h" #include "private.h" uint64_t _time_from_timespec(const struct timespec *ts) { return ts->tv_sec * L_USEC_PER_SEC + ts->tv_nsec / L_NSEC_PER_USEC; } static uint64_t _time_from_timeval(const struct timeval *tv) { return tv->tv_sec * L_USEC_PER_SEC + tv->tv_usec; } /** * l_time_now: * * Get the running clocktime in microseconds * * Returns: Current clock time in microseconds **/ LIB_EXPORT uint64_t l_time_now(void) { struct timespec now; clock_gettime(CLOCK_BOOTTIME, &now); return _time_from_timespec(&now); } uint64_t time_realtime_now(void) { struct timespec now; clock_gettime(CLOCK_REALTIME, &now); return _time_from_timespec(&now); } /** * l_time_after * * Returns: True if time a is after time b **/ /** * l_time_before * * Returns: True if time a is before time b **/ /** * l_time_offset * * @time: Start time to calculate offset * @offset: Amount of time to add to 'time' * * Adds an offset to a time value. This checks for overflow, and if detected * returns UINT64_MAX. * * Returns: A time value 'time' + 'offset'. Or UINT64_MAX if time + offset * exceeds UINT64_MAX. **/ /* Compute ms + RAND*ms where RAND is in range -0.1 .. 0.1 */ uint64_t _time_fuzz_msecs(uint64_t ms) { /* We do this by subtracting 0.1ms and adding 0.1ms * rand[0 .. 2] */ return ms - ms / 10 + (l_getrandom_uint32() % (2 * L_MSEC_PER_SEC)) * ms / 10 / L_MSEC_PER_SEC; } uint64_t _time_pick_interval_secs(uint32_t min_secs, uint32_t max_secs) { uint64_t min_ms = min_secs * L_MSEC_PER_SEC; uint64_t max_ms = max_secs * L_MSEC_PER_SEC; return l_getrandom_uint32() % (max_ms + 1 - min_ms) + min_ms; } /* Compute a time in ms based on seconds + max_offset * [-1.0 .. 1.0] */ uint64_t _time_fuzz_secs(uint32_t secs, uint32_t max_offset) { uint64_t ms = secs * L_MSEC_PER_SEC; uint64_t r = l_getrandom_uint32(); max_offset *= L_MSEC_PER_SEC; if (r & 0x80000000) ms += (r & 0x7fffffff) % max_offset; else ms -= (r & 0x7fffffff) % max_offset; return ms; } /* * Convert a *recent* CLOCK_REALTIME-based timestamp to a * CLOCK_BOOTTIME-based usec count consistent with l_time functions. * The longer the time since the input timestamp the higher the * probability of the two clocks having diverged and the higher the * expected error magnitude. */ uint64_t _time_realtime_to_boottime(const struct timeval *ts) { uint64_t now_realtime; uint64_t now_boottime = l_time_now(); struct timespec timespec; uint64_t ts_realtime; uint64_t offset; clock_gettime(CLOCK_REALTIME, ×pec); now_realtime = _time_from_timespec(×pec); ts_realtime = _time_from_timeval(ts); offset = l_time_diff(ts_realtime, now_realtime); /* Most likely case, timestamp in the past */ if (l_time_before(ts_realtime, now_realtime)) { if (offset > now_boottime) return 0; return now_boottime - offset; } return l_time_offset(now_boottime, offset); } bluez-5.82/ell/PaxHeaders/time.h0000644000000000000000000000005014560467756013555 xustar0020 atime=1743575449 20 ctime=1743591276 bluez-5.82/ell/time.h0000644000000000000000000000224714560467756013243 0ustar00rootroot/* * Embedded Linux library * Copyright (C) 2019 Intel Corporation * * SPDX-License-Identifier: LGPL-2.1-or-later */ #ifndef __ELL_TIME_H #define __ELL_TIME_H #include #include #ifdef __cplusplus extern "C" { #endif #define L_USEC_PER_SEC 1000000ULL #define L_MSEC_PER_SEC 1000ULL #define L_USEC_PER_MSEC 1000ULL #define L_NSEC_PER_SEC 1000000000ULL #define L_NSEC_PER_MSEC 1000000ULL #define L_NSEC_PER_USEC 1000ULL #define L_TIME_INVALID ((uint64_t) -1) uint64_t l_time_now(void); static inline bool l_time_after(uint64_t a, uint64_t b) { return a > b; } static inline bool l_time_before(uint64_t a, uint64_t b) { return l_time_after(b, a); } static inline uint64_t l_time_offset(uint64_t time, uint64_t offset) { /* check overflow */ if (offset > UINT64_MAX - time) return UINT64_MAX; return time + offset; } static inline uint64_t l_time_diff(uint64_t a, uint64_t b) { return (a < b) ? b - a : a - b; } static inline uint64_t l_time_to_secs(uint64_t time) { return time / L_USEC_PER_SEC; } static inline uint64_t l_time_to_msecs(uint64_t time) { return time / L_USEC_PER_MSEC; } #ifdef __cplusplus } #endif #endif /* __ELL_TIME_H */ bluez-5.82/ell/PaxHeaders/checksum.h0000644000000000000000000000005014770744372014414 xustar0020 atime=1743575449 20 ctime=1743591276 bluez-5.82/ell/checksum.h0000644000000000000000000000303514770744372014076 0ustar00rootroot/* * Embedded Linux library * Copyright (C) 2011-2014 Intel Corporation * * SPDX-License-Identifier: LGPL-2.1-or-later */ #ifndef __ELL_CHECKSUM_H #define __ELL_CHECKSUM_H #include #include #include #ifdef __cplusplus extern "C" { #endif struct l_checksum; enum l_checksum_type { L_CHECKSUM_NONE, L_CHECKSUM_MD4, L_CHECKSUM_MD5, L_CHECKSUM_SHA1, L_CHECKSUM_SHA224, L_CHECKSUM_SHA256, L_CHECKSUM_SHA384, L_CHECKSUM_SHA512, L_CHECKSUM_SHA3_224, L_CHECKSUM_SHA3_256, L_CHECKSUM_SHA3_384, L_CHECKSUM_SHA3_512, }; struct l_checksum *l_checksum_new(enum l_checksum_type type); struct l_checksum *l_checksum_new_cmac_aes(const void *key, size_t key_len); struct l_checksum *l_checksum_new_hmac(enum l_checksum_type type, const void *key, size_t key_len); struct l_checksum *l_checksum_clone(struct l_checksum *checksum); void l_checksum_free(struct l_checksum *checksum); void l_checksum_reset(struct l_checksum *checksum); bool l_checksum_update(struct l_checksum *checksum, const void *data, size_t len); bool l_checksum_updatev(struct l_checksum *checksum, const struct iovec *iov, size_t iov_len); ssize_t l_checksum_get_digest(struct l_checksum *checksum, void *digest, size_t len); char *l_checksum_get_string(struct l_checksum *checksum); bool l_checksum_is_supported(enum l_checksum_type type, bool check_hmac); bool l_checksum_cmac_aes_supported(void); ssize_t l_checksum_digest_length(enum l_checksum_type type); #ifdef __cplusplus } #endif #endif /* __ELL_CHECKSUM_H */ bluez-5.82/ell/PaxHeaders/io.c0000644000000000000000000000005014560467756013221 xustar0020 atime=1743575469 20 ctime=1743591276 bluez-5.82/ell/io.c0000644000000000000000000001775214560467756012716 0ustar00rootroot/* * Embedded Linux library * Copyright (C) 2011-2014 Intel Corporation * * SPDX-License-Identifier: LGPL-2.1-or-later */ #ifdef HAVE_CONFIG_H #include #endif #include #include #include #include #include "useful.h" #include "main-private.h" #include "io.h" #include "private.h" /** * SECTION:io * @short_description: IO support * * IO support */ /** * l_io: * * Opaque object representing the IO. */ struct l_io { int fd; uint32_t events; bool close_on_destroy; l_io_read_cb_t read_handler; l_io_destroy_cb_t read_destroy; void *read_data; l_io_write_cb_t write_handler; l_io_destroy_cb_t write_destroy; void *write_data; l_io_disconnect_cb_t disconnect_handler; l_io_destroy_cb_t disconnect_destroy; void *disconnect_data; l_io_debug_cb_t debug_handler; l_io_destroy_cb_t debug_destroy; void *debug_data; }; static void io_cleanup(void *user_data) { struct l_io *io = user_data; l_util_debug(io->debug_handler, io->debug_data, "cleanup <%p>", io); if (io->write_destroy) io->write_destroy(io->write_data); io->write_handler = NULL; io->write_data = NULL; if (io->read_destroy) io->read_destroy(io->read_data); io->read_handler = NULL; io->read_data = NULL; if (io->close_on_destroy) close(io->fd); io->fd = -1; } static void io_closed(struct l_io *io) { /* * Save off copies of disconnect_handler, disconnect_destroy * and disconnect_data in case the handler calls io_destroy */ l_io_disconnect_cb_t handler = io->disconnect_handler; l_io_destroy_cb_t destroy = io->disconnect_destroy; void *disconnect_data = io->disconnect_data; io->disconnect_handler = NULL; io->disconnect_destroy = NULL; io->disconnect_data = NULL; if (handler) handler(io, disconnect_data); if (destroy) destroy(disconnect_data); } static void io_callback(int fd, uint32_t events, void *user_data) { struct l_io *io = user_data; if ((events & EPOLLIN) && io->read_handler) { l_util_debug(io->debug_handler, io->debug_data, "read event <%p>", io); if (!io->read_handler(io, io->read_data)) { if (io->read_destroy) io->read_destroy(io->read_data); io->read_handler = NULL; io->read_destroy = NULL; io->read_data = NULL; io->events &= ~EPOLLIN; if (watch_modify(io->fd, io->events, false) == -EBADF) { io->close_on_destroy = false; watch_clear(io->fd); io_closed(io); return; } } } if (unlikely(events & (EPOLLERR | EPOLLHUP))) { bool close_on_destroy = io->close_on_destroy; int fd = io->fd; l_util_debug(io->debug_handler, io->debug_data, "disconnect event <%p>", io); io_closed(io); watch_remove(fd, !close_on_destroy); return; } if ((events & EPOLLOUT) && io->write_handler) { l_util_debug(io->debug_handler, io->debug_data, "write event <%p>", io); if (!io->write_handler(io, io->write_data)) { if (io->write_destroy) io->write_destroy(io->write_data); io->write_handler = NULL; io->write_destroy = NULL; io->write_data = NULL; io->events &= ~EPOLLOUT; if (watch_modify(io->fd, io->events, false) == -EBADF) { io->close_on_destroy = false; watch_clear(io->fd); io_closed(io); return; } } } } /** * l_io_new: * @fd: file descriptor * * Create new IO handling for a given file descriptor. * * Returns: a newly allocated #l_io object **/ LIB_EXPORT struct l_io *l_io_new(int fd) { struct l_io *io; int err; if (unlikely(fd < 0)) return NULL; io = l_new(struct l_io, 1); io->fd = fd; io->events = EPOLLHUP | EPOLLERR; io->close_on_destroy = false; err = watch_add(io->fd, io->events, io_callback, io, io_cleanup); if (err) { l_free(io); return NULL; } return io; } /** * l_io_destroy: * @io: IO object * * Free IO object and close file descriptor (if enabled). **/ LIB_EXPORT void l_io_destroy(struct l_io *io) { if (unlikely(!io)) return; if (io->fd != -1) watch_remove(io->fd, !io->close_on_destroy); io_closed(io); if (io->debug_destroy) io->debug_destroy(io->debug_data); l_free(io); } /** * l_io_get_fd: * @io: IO object * * Returns: file descriptor associated with @io **/ LIB_EXPORT int l_io_get_fd(struct l_io *io) { if (unlikely(!io)) return -1; return io->fd; } /** * l_io_set_close_on_destroy: * @io: IO object * @do_close: setting for destroy handling * * Set the automatic closing of the file descriptor when destroying @io. * * Returns: #true on success and #false on failure **/ LIB_EXPORT bool l_io_set_close_on_destroy(struct l_io *io, bool do_close) { if (unlikely(!io)) return false; io->close_on_destroy = do_close; return true; } /** * l_io_set_read_handler: * @io: IO object * @callback: read handler callback function * @user_data: user data provided to read handler callback function * @destroy: destroy function for user data * * Set read function. * * Returns: #true on success and #false on failure **/ LIB_EXPORT bool l_io_set_read_handler(struct l_io *io, l_io_read_cb_t callback, void *user_data, l_io_destroy_cb_t destroy) { uint32_t events; int err; if (unlikely(!io || io->fd < 0)) return false; l_util_debug(io->debug_handler, io->debug_data, "set read handler <%p>", io); if (io->read_destroy) io->read_destroy(io->read_data); if (callback) events = io->events | EPOLLIN; else events = io->events & ~EPOLLIN; io->read_handler = callback; io->read_destroy = destroy; io->read_data = user_data; if (events == io->events) return true; err = watch_modify(io->fd, events, false); if (err) return false; io->events = events; return true; } /** * l_io_set_write_handler: * @io: IO object * @callback: write handler callback function * @user_data: user data provided to write handler callback function * @destroy: destroy function for user data * * Set write function. * * Returns: #true on success and #false on failure **/ LIB_EXPORT bool l_io_set_write_handler(struct l_io *io, l_io_write_cb_t callback, void *user_data, l_io_destroy_cb_t destroy) { uint32_t events; int err; if (unlikely(!io || io->fd < 0)) return false; l_util_debug(io->debug_handler, io->debug_data, "set write handler <%p>", io); if (io->write_handler == callback && io->write_destroy == destroy && io->write_data == user_data) return true; if (io->write_destroy) io->write_destroy(io->write_data); if (callback) events = io->events | EPOLLOUT; else events = io->events & ~EPOLLOUT; io->write_handler = callback; io->write_destroy = destroy; io->write_data = user_data; if (events == io->events) return true; err = watch_modify(io->fd, events, false); if (err) return false; io->events = events; return true; } /** * l_io_set_disconnect_handler: * @io: IO object * @callback: disconnect handler callback function * @user_data: user data provided to disconnect handler callback function * @destroy: destroy function for user data * * Set disconnect function. * * Returns: #true on success and #false on failure **/ LIB_EXPORT bool l_io_set_disconnect_handler(struct l_io *io, l_io_disconnect_cb_t callback, void *user_data, l_io_destroy_cb_t destroy) { if (unlikely(!io || io->fd < 0)) return false; l_util_debug(io->debug_handler, io->debug_data, "set disconnect handler <%p>", io); if (io->disconnect_destroy) io->disconnect_destroy(io->disconnect_data); io->disconnect_handler = callback; io->disconnect_destroy = destroy; io->disconnect_data = user_data; return true; } /** * l_io_set_debug: * @io: IO object * @callback: debug callback function * @user_data: user data provided to debug callback function * @destroy: destroy function for user data * * Set debug function. * * Returns: #true on success and #false on failure **/ LIB_EXPORT bool l_io_set_debug(struct l_io *io, l_io_debug_cb_t callback, void *user_data, l_io_destroy_cb_t destroy) { if (unlikely(!io)) return false; if (io->debug_destroy) io->debug_destroy(io->debug_data); io->debug_handler = callback; io->debug_destroy = destroy; io->debug_data = user_data; return true; } bluez-5.82/ell/PaxHeaders/uuid.c0000644000000000000000000000005014504767710013547 xustar0020 atime=1743575522 20 ctime=1743591277 bluez-5.82/ell/uuid.c0000644000000000000000000001305614504767710013235 0ustar00rootroot/* * Embedded Linux library * Copyright (C) 2011-2014 Intel Corporation * * SPDX-License-Identifier: LGPL-2.1-or-later */ #ifdef HAVE_CONFIG_H #include #endif #define _GNU_SOURCE #include #include "checksum.h" #include "random.h" #include "private.h" #include "useful.h" #include "uuid.h" const uint8_t L_UUID_NAMESPACE_DNS[16] = { 0x6b, 0xa7, 0xb8, 0x10, 0x9d, 0xad, 0x11, 0xd1, 0x80, 0xb4, 0x00, 0xc0, 0x4f, 0xd4, 0x30, 0xc8, }; const uint8_t L_UUID_NAMESPACE_URL[16] = { 0x6b, 0xa7, 0xb8, 0x11, 0x9d, 0xad, 0x11, 0xd1, 0x80, 0xb4, 0x00, 0xc0, 0x4f, 0xd4, 0x30, 0xc8, }; const uint8_t L_UUID_NAMESPACE_OID[16] = { 0x6b, 0xa7, 0xb8, 0x12, 0x9d, 0xad, 0x11, 0xd1, 0x80, 0xb4, 0x00, 0xc0, 0x4f, 0xd4, 0x30, 0xc8, }; const uint8_t L_UUID_NAMESPACE_X500[16] = { 0x6b, 0xa7, 0xb8, 0x14, 0x9d, 0xad, 0x11, 0xd1, 0x80, 0xb4, 0x00, 0xc0, 0x4f, 0xd4, 0x30, 0xc8, }; /* RFC 4122, Section 4.3 */ static bool name_from_namespace(int version, const uint8_t nsid[16], const void *name, size_t name_size, uint8_t out_uuid[16]) { enum l_checksum_type type; struct l_checksum *hash; struct iovec iov[2]; if (unlikely(!out_uuid)) return false; switch (version) { case 3: type = L_CHECKSUM_MD5; break; case 5: type = L_CHECKSUM_SHA1; break; default: return false; } hash = l_checksum_new(type); if (!hash) return false; iov[0].iov_base = (void *) nsid; iov[0].iov_len = 16; iov[1].iov_base = (void *) name; iov[1].iov_len = name_size; /* Compute the hash of the name space ID concatenated with the name. */ l_checksum_updatev(hash, iov, 2); /* o Set octets zero through 3 of the time_low field to octets zero * through 3 of the hash. * * o Set octets zero and one of the time_mid field to octets 4 and 5 of * the hash. * * o Set octets zero and one of the time_hi_and_version field to octets * 6 and 7 of the hash. * o Set the four most significant bits (bits 12 through 15) of the * time_hi_and_version field to the appropriate 4-bit version number * from Section 4.1.3. * * o Set the clock_seq_hi_and_reserved field to octet 8 of the hash. * o Set the two most significant bits (bits 6 and 7) of the * clock_seq_hi_and_reserved to zero and one, respectively. * * o Set the clock_seq_low field to octet 9 of the hash. * * o Set octets zero through five of the node field to octets 10 * through 15 of the hash. */ l_checksum_get_digest(hash, out_uuid, 16); /* Set 4 MSB bits of time_hi_and_version field */ out_uuid[6] &= 0x0f; out_uuid[6] |= version << 4; /* Set 2 MSB of clock_seq_hi_and_reserved to 10 */ out_uuid[8] &= 0x3f; out_uuid[8] |= 0x80; l_checksum_free(hash); return true; } LIB_EXPORT bool l_uuid_v3(const uint8_t nsid[16], const void *name, size_t name_size, uint8_t out_uuid[16]) { return name_from_namespace(3, nsid, name, name_size, out_uuid); } LIB_EXPORT bool l_uuid_v5(const uint8_t nsid[16], const void *name, size_t name_size, uint8_t out_uuid[16]) { return name_from_namespace(5, nsid, name, name_size, out_uuid); } /* RFC 4122, Section 4.4 */ LIB_EXPORT bool l_uuid_v4(uint8_t out_uuid[16]) { if (unlikely(!out_uuid)) return false; if (!l_getrandom(out_uuid, 16)) return false; /* * o Set the two most significant bits (bits 6 and 7) of the * clock_seq_hi_and_reserved to zero and one, respectively. * * o Set the four most significant bits (bits 12 through 15) of the * time_hi_and_version field to the 4-bit version number from * Section 4.1.3. * * o Set all the other bits to randomly (or pseudo-randomly) chosen * values. */ /* Set 4 MSB bits of time_hi_and_version field */ out_uuid[6] &= 0x0f; out_uuid[6] |= 4 << 4; /* Set 2 MSB of clock_seq_hi_and_reserved to 10 */ out_uuid[8] &= 0x3f; out_uuid[8] |= 0x80; return true; } /** * l_uuid_is_valid: * @uuid: UUID to check. * * Checks whether the given UUID is valid according to RFC 4122. This function * checks that the version field is set properly and the variant of the UUID * is set to RFC 4122. * * Returns: Whether the UUID is valid **/ LIB_EXPORT bool l_uuid_is_valid(const uint8_t uuid[16]) { unsigned int version; unsigned int variant; if (!uuid) return false; variant = uuid[8] >> 6; if (variant != 2) return false; version = uuid[6] >> 4; if (version < 1 || version > 5) return false; return true; } LIB_EXPORT enum l_uuid_version l_uuid_get_version(const uint8_t uuid[16]) { unsigned int version; version = uuid[6] >> 4; return version; } LIB_EXPORT bool l_uuid_to_string(const uint8_t uuid[16], char *dest, size_t dest_size) { int n; n = snprintf(dest, dest_size, "%02x%02x%02x%02x-%02x%02x-%02x%02x-" "%02x%02x-%02x%02x%02x%02x%02x%02x", uuid[0], uuid[1], uuid[2], uuid[3], uuid[4], uuid[5], uuid[6], uuid[7], uuid[8], uuid[9], uuid[10], uuid[11], uuid[12], uuid[13], uuid[14], uuid[15]); if (n < 0 || (size_t) n >= dest_size) return false; return true; } LIB_EXPORT bool l_uuid_from_string(const char *src, uint8_t uuid[16]) { uint8_t buf[16]; int n; /* * textual representation: 32 hex digits + 4 group separators */ if (strlen(src) < 16 * 2 + 4) return false; n = sscanf(src, "%02hhx%02hhx%02hhx%02hhx-" "%02hhx%02hhx-" "%02hhx%02hhx-" "%02hhx%02hhx-" "%02hhx%02hhx%02hhx%02hhx%02hhx%02hhx", &buf[0], &buf[1], &buf[2], &buf[3], &buf[4], &buf[5], &buf[6], &buf[7], &buf[8], &buf[9], &buf[10], &buf[11], &buf[12], &buf[13], &buf[14], &buf[15]); if (n != 16) return false; if (!l_uuid_is_valid(buf)) return false; memcpy(uuid, buf, sizeof(buf)); return true; } bluez-5.82/ell/PaxHeaders/base64.h0000644000000000000000000000005014504767710013672 xustar0020 atime=1743575449 20 ctime=1743591276 bluez-5.82/ell/base64.h0000644000000000000000000000064014504767710013353 0ustar00rootroot/* * Embedded Linux library * Copyright (C) 2015 Intel Corporation * * SPDX-License-Identifier: LGPL-2.1-or-later */ #ifndef __ELL_BASE64_H #define __ELL_BASE64_H #ifdef __cplusplus extern "C" { #endif uint8_t *l_base64_decode(const char *in, size_t in_len, size_t *n_written); char *l_base64_encode(const uint8_t *in, size_t in_len, int columns); #ifdef __cplusplus } #endif #endif /* __ELL_BASE64_H */ bluez-5.82/ell/PaxHeaders/ecdh.c0000644000000000000000000000005014770744372013510 xustar0020 atime=1743575546 20 ctime=1743591277 bluez-5.82/ell/ecdh.c0000644000000000000000000000443714770744372013201 0ustar00rootroot/* * Embedded Linux library * Copyright (C) 2018 Intel Corporation * * SPDX-License-Identifier: LGPL-2.1-or-later */ #ifdef HAVE_CONFIG_H #include #endif #include #include "private.h" #include "ecc-private.h" #include "ecc.h" #include "ecdh.h" #include "random.h" #include "useful.h" /* * Some sane maximum for calculating the public key. */ #define ECDH_MAX_ITERATIONS 20 /* * IETF draft-jivsov-ecc-compact-00 Section 4.2.1 * * The following algorithm calculates a key pair {k, Q=k*G=(x,y)}, where k is * the private key and Q=(x,y) is the public key. * * Black box generation: * 1. Generate a key pair {k, Q=k*G=(x,y)} with KG * 2. if( y != min(y,p-y) ) goto step 1 * 3. output {k, Q=(x,y)} as a key pair */ LIB_EXPORT bool l_ecdh_generate_key_pair(const struct l_ecc_curve *curve, struct l_ecc_scalar **out_private, struct l_ecc_point **out_public) { bool compliant = false; int iter = 0; uint64_t p2[L_ECC_MAX_DIGITS]; if (unlikely(!curve || !out_private || !out_public)) return false; _ecc_calculate_p2(curve, p2); *out_public = l_ecc_point_new(curve); while (!compliant && iter++ < ECDH_MAX_ITERATIONS) { *out_private = l_ecc_scalar_new_random(curve); if (!*out_private) continue; _ecc_point_mult(*out_public, &curve->g, (*out_private)->c, NULL, curve->p); /* ensure public key is compliant */ if (_vli_cmp((*out_public)->y, p2, curve->ndigits) >= 0) { compliant = true; break; } l_ecc_scalar_free(*out_private); *out_private = NULL; } if (!compliant) { l_ecc_point_free(*out_public); *out_public = NULL; return false; } return true; } LIB_EXPORT bool l_ecdh_generate_shared_secret( const struct l_ecc_scalar *private_key, const struct l_ecc_point *other_public, struct l_ecc_scalar **secret) { const struct l_ecc_curve *curve = private_key->curve; struct l_ecc_scalar *z; struct l_ecc_point *product; if (unlikely(!private_key || !other_public || !secret)) return false; z = l_ecc_scalar_new_random(curve); if (!z) return false; product = l_ecc_point_new(curve); _ecc_point_mult(product, other_public, private_key->c, z->c, curve->p); *secret = _ecc_constant_new(curve, product->x, curve->ndigits * 8); l_ecc_point_free(product); l_ecc_scalar_free(z); return true; } bluez-5.82/ell/PaxHeaders/dbus-service.c0000644000000000000000000000005014661631567015200 xustar0020 atime=1743575510 20 ctime=1743591277 bluez-5.82/ell/dbus-service.c0000644000000000000000000015241414661631567014670 0ustar00rootroot/* * Embedded Linux library * Copyright (C) 2011-2014 Intel Corporation * * SPDX-License-Identifier: LGPL-2.1-or-later */ #ifdef HAVE_CONFIG_H #include #endif #define _GNU_SOURCE #include #include #include #include "useful.h" #include "queue.h" #include "string.h" #include "hashmap.h" #include "dbus.h" #include "dbus-service.h" #include "dbus-private.h" #include "private.h" #include "idle.h" #define XML_ID "-//freedesktop//DTD D-BUS Object Introspection 1.0//EN" #define XML_DTD "http://www.freedesktop.org/standards/dbus/1.0/introspect.dtd" #define XML_HEAD "\n" static const char *static_introspectable = "\t\n" "\t\t\n" "\t\t\t\n" "\t\t\n\t\n"; struct _dbus_method { l_dbus_interface_method_cb_t cb; uint32_t flags; unsigned char name_len; char metainfo[]; }; struct _dbus_signal { uint32_t flags; unsigned char name_len; char metainfo[]; }; struct _dbus_property { l_dbus_property_get_cb_t getter; l_dbus_property_set_cb_t setter; uint32_t flags; unsigned char name_len; char metainfo[]; }; struct l_dbus_interface { struct l_queue *methods; struct l_queue *signals; struct l_queue *properties; bool handle_old_style_properties; void (*instance_destroy)(void *); char name[]; }; struct child_node { struct object_node *node; struct child_node *next; char subpath[]; }; struct interface_instance { struct l_dbus_interface *interface; void *user_data; }; struct object_node { struct object_node *parent; struct l_queue *instances; struct child_node *children; void *user_data; void (*destroy) (void *); }; struct object_manager { char *path; struct l_dbus *dbus; struct l_queue *announce_added; struct l_queue *announce_removed; }; struct interface_add_record { char *path; struct object_node *object; struct l_queue *instances; }; struct interface_remove_record { char *path; struct object_node *object; struct l_queue *interface_names; }; struct property_change_record { char *path; struct object_node *object; struct interface_instance *instance; struct l_queue *properties; }; struct _dbus_object_tree { struct l_hashmap *interfaces; struct l_hashmap *objects; struct object_node *root; struct l_queue *object_managers; struct l_queue *property_changes; struct l_idle *emit_signals_work; bool flushing; }; void _dbus_method_introspection(struct _dbus_method *info, struct l_string *buf) { const char *sig; const char *end; const char *pname; unsigned int offset = info->name_len + 1; l_string_append_printf(buf, "\t\t\n", info->metainfo); sig = info->metainfo + offset; offset += strlen(sig) + 1; for (; *sig; sig++) { end = _dbus_signature_end(sig); pname = info->metainfo + offset; l_string_append_printf(buf, "\t\t\t\n", pname, (int) (end - sig + 1), sig); sig = end; offset += strlen(pname) + 1; } sig = info->metainfo + offset; offset += strlen(sig) + 1; for (; *sig; sig++) { end = _dbus_signature_end(sig); pname = info->metainfo + offset; l_string_append_printf(buf, "\t\t\t\n", pname, (int) (end - sig + 1), sig); sig = end; offset += strlen(pname) + 1; } if (info->flags & L_DBUS_METHOD_FLAG_DEPRECATED) l_string_append(buf, "\t\t\t\n"); if (info->flags & L_DBUS_METHOD_FLAG_NOREPLY) l_string_append(buf, "\t\t\t\n"); l_string_append(buf, "\t\t\n"); } void _dbus_signal_introspection(struct _dbus_signal *info, struct l_string *buf) { const char *sig; const char *end; const char *pname; unsigned int offset = info->name_len + 1; l_string_append_printf(buf, "\t\t\n", info->metainfo); sig = info->metainfo + offset; offset += strlen(sig) + 1; for (; *sig; sig++) { end = _dbus_signature_end(sig); pname = info->metainfo + offset; l_string_append_printf(buf, "\t\t\t\n", pname, (int) (end - sig + 1), sig); sig = end; offset += strlen(pname) + 1; } if (info->flags & L_DBUS_SIGNAL_FLAG_DEPRECATED) l_string_append(buf, "\t\t\t\n"); l_string_append(buf, "\t\t\n"); } void _dbus_property_introspection(struct _dbus_property *info, struct l_string *buf) { unsigned int offset = info->name_len + 1; const char *signature = info->metainfo + offset; l_string_append_printf(buf, "\t\tmetainfo, signature); if (info->setter) l_string_append(buf, "access=\"readwrite\""); else l_string_append(buf, "access=\"read\""); if (info->flags & L_DBUS_METHOD_FLAG_DEPRECATED) { l_string_append(buf, ">\n"); l_string_append(buf, "\t\t\t\n"); l_string_append(buf, "\t\t\n"); } else l_string_append(buf, "/>\n"); } void _dbus_interface_introspection(struct l_dbus_interface *interface, struct l_string *buf) { l_string_append_printf(buf, "\t\n", interface->name); l_queue_foreach(interface->methods, (l_queue_foreach_func_t) _dbus_method_introspection, buf); l_queue_foreach(interface->signals, (l_queue_foreach_func_t) _dbus_signal_introspection, buf); l_queue_foreach(interface->properties, (l_queue_foreach_func_t) _dbus_property_introspection, buf); l_string_append(buf, "\t\n"); } #define COPY_PARAMS(dest, signature, args) \ do { \ const char *pname; \ const char *sig; \ dest = stpcpy(dest, signature) + 1; \ for (sig = signature; *sig; sig++) { \ sig = _dbus_signature_end(sig); \ pname = va_arg(args, const char *); \ dest = stpcpy(dest, pname) + 1; \ } \ } while(0) #define SIZE_PARAMS(signature, args) \ ({ \ unsigned int len = strlen(signature) + 1; \ const char *pname; \ const char *sig; \ for (sig = signature; *sig; sig++) { \ sig = _dbus_signature_end(sig); \ if (!sig) { \ len = 0; \ break; \ } \ pname = va_arg(args, const char *); \ len += strlen(pname) + 1; \ } \ len; \ }) LIB_EXPORT bool l_dbus_interface_method(struct l_dbus_interface *interface, const char *name, uint32_t flags, l_dbus_interface_method_cb_t cb, const char *return_sig, const char *param_sig, ...) { va_list args; unsigned int return_info_len; unsigned int param_info_len; struct _dbus_method *info; char *p; if (!_dbus_valid_method(name)) return false; if (unlikely(!return_sig || !param_sig)) return false; if (return_sig[0] && !_dbus_valid_signature(return_sig)) return false; if (param_sig[0] && !_dbus_valid_signature(param_sig)) return false; /* Pre-calculate the needed meta-info length */ va_start(args, param_sig); return_info_len = SIZE_PARAMS(return_sig, args); param_info_len = SIZE_PARAMS(param_sig, args); va_end(args); if (!return_info_len || !param_info_len) return false; info = l_malloc(sizeof(*info) + return_info_len + param_info_len + strlen(name) + 1); info->cb = cb; info->flags = flags; info->name_len = strlen(name); strcpy(info->metainfo, name); va_start(args, param_sig); /* * We store param signature + parameter names first, to speed up * lookups during the message dispatch procedures. */ p = info->metainfo + info->name_len + param_info_len + 1; COPY_PARAMS(p, return_sig, args); p = info->metainfo + info->name_len + 1; COPY_PARAMS(p, param_sig, args); va_end(args); l_queue_push_tail(interface->methods, info); return true; } LIB_EXPORT bool l_dbus_interface_signal(struct l_dbus_interface *interface, const char *name, uint32_t flags, const char *signature, ...) { va_list args; unsigned int metainfo_len; struct _dbus_signal *info; char *p; if (!_dbus_valid_method(name)) return false; if (unlikely(!signature)) return false; if (signature[0] && !_dbus_valid_signature(signature)) return false; /* Pre-calculate the needed meta-info length */ va_start(args, signature); metainfo_len = SIZE_PARAMS(signature, args); va_end(args); if (!metainfo_len) return false; metainfo_len += strlen(name) + 1; info = l_malloc(sizeof(*info) + metainfo_len); info->flags = flags; info->name_len = strlen(name); p = stpcpy(info->metainfo, name) + 1; va_start(args, signature); COPY_PARAMS(p, signature, args); va_end(args); l_queue_push_tail(interface->signals, info); return true; } LIB_EXPORT bool l_dbus_interface_property(struct l_dbus_interface *interface, const char *name, uint32_t flags, const char *signature, l_dbus_property_get_cb_t getter, l_dbus_property_set_cb_t setter) { unsigned int metainfo_len; struct _dbus_property *info; char *p; if (!_dbus_valid_method(name)) return false; if (unlikely(!signature || !getter)) return false; if (_dbus_num_children(signature) != 1) return false; /* Pre-calculate the needed meta-info length */ metainfo_len = strlen(name) + 1; metainfo_len += strlen(signature) + 1; info = l_malloc(sizeof(*info) + metainfo_len); info->flags = flags; info->name_len = strlen(name); info->getter = getter; info->setter = setter; p = stpcpy(info->metainfo, name) + 1; strcpy(p, signature); l_queue_push_tail(interface->properties, info); return true; } struct l_dbus_interface *_dbus_interface_new(const char *name) { struct l_dbus_interface *interface; interface = l_malloc(sizeof(*interface) + strlen(name) + 1); interface->methods = l_queue_new(); interface->signals = l_queue_new(); interface->properties = l_queue_new(); strcpy(interface->name, name); return interface; } void _dbus_interface_free(struct l_dbus_interface *interface) { l_queue_destroy(interface->methods, l_free); l_queue_destroy(interface->signals, l_free); l_queue_destroy(interface->properties, l_free); l_free(interface); } static bool match_method(const void *a, const void *b) { const struct _dbus_method *method = a; const char *name = b; if (!strcmp(method->metainfo, name)) return true; return false; } struct _dbus_method *_dbus_interface_find_method(struct l_dbus_interface *i, const char *method) { return l_queue_find(i->methods, match_method, (char *) method); } static bool match_signal(const void *a, const void *b) { const struct _dbus_signal *signal = a; const char *name = b; if (!strcmp(signal->metainfo, name)) return true; return false; } struct _dbus_signal *_dbus_interface_find_signal(struct l_dbus_interface *i, const char *signal) { return l_queue_find(i->signals, match_signal, (char *) signal); } static bool match_property(const void *a, const void *b) { const struct _dbus_property *property = a; const char *name = b; if (!strcmp(property->metainfo, name)) return true; return false; } struct _dbus_property *_dbus_interface_find_property(struct l_dbus_interface *i, const char *property) { return l_queue_find(i->properties, match_property, (char *) property); } static void interface_instance_free(struct interface_instance *instance) { if (instance->interface->instance_destroy) instance->interface->instance_destroy(instance->user_data); l_free(instance); } static bool match_interface_instance(const void *a, const void *b) { const struct interface_instance *instance = a; const char *name = b; if (!strcmp(instance->interface->name, name)) return true; return false; } static bool match_interface_instance_ptr(const void *a, const void *b) { const struct interface_instance *instance = a; return instance->interface == b; } static void interface_add_record_free(void *data) { struct interface_add_record *rec = data; l_free(rec->path); l_queue_destroy(rec->instances, NULL); l_free(rec); } static void interface_removed_record_free(void *data) { struct interface_remove_record *rec = data; l_free(rec->path); l_queue_destroy(rec->interface_names, l_free); l_free(rec); } static void property_change_record_free(void *data) { struct property_change_record *rec = data; l_free(rec->path); l_queue_destroy(rec->properties, NULL); l_free(rec); } static void properties_setup_func(struct l_dbus_interface *); static void object_manager_setup_func(struct l_dbus_interface *); struct _dbus_object_tree *_dbus_object_tree_new() { struct _dbus_object_tree *tree; tree = l_new(struct _dbus_object_tree, 1); tree->interfaces = l_hashmap_new(); l_hashmap_set_hash_function(tree->interfaces, l_str_hash); l_hashmap_set_compare_function(tree->interfaces, (l_hashmap_compare_func_t)strcmp); tree->objects = l_hashmap_string_new(); tree->root = l_new(struct object_node, 1); tree->property_changes = l_queue_new(); _dbus_object_tree_register_interface(tree, L_DBUS_INTERFACE_PROPERTIES, properties_setup_func, NULL, false); tree->object_managers = l_queue_new(); _dbus_object_tree_register_interface(tree, L_DBUS_INTERFACE_OBJECT_MANAGER, object_manager_setup_func, NULL, false); return tree; } static void subtree_free(struct object_node *node) { struct child_node *child; while (node->children) { child = node->children; node->children = child->next; subtree_free(child->node); l_free(child); } l_queue_destroy(node->instances, (l_queue_destroy_func_t) interface_instance_free); if (node->destroy) node->destroy(node->user_data); l_free(node); } static void object_manager_free(void *data) { struct object_manager *manager = data; l_free(manager->path); l_queue_destroy(manager->announce_added, interface_add_record_free); l_queue_destroy(manager->announce_removed, interface_removed_record_free); l_free(manager); } void _dbus_object_tree_free(struct _dbus_object_tree *tree) { subtree_free(tree->root); l_hashmap_destroy(tree->interfaces, (l_hashmap_destroy_func_t) _dbus_interface_free); l_hashmap_destroy(tree->objects, NULL); l_queue_destroy(tree->object_managers, object_manager_free); l_queue_destroy(tree->property_changes, property_change_record_free); if (tree->emit_signals_work) l_idle_remove(tree->emit_signals_work); l_free(tree); } static struct object_node *makepath_recurse(struct object_node *node, const char *path) { const char *end; struct child_node *child; if (*path == '\0') return node; path += 1; end = strchrnul(path, '/'); child = node->children; while (child) { if (!strncmp(child->subpath, path, end - path) && child->subpath[end - path] == '\0') goto done; child = child->next; } child = l_malloc(sizeof(*child) + end - path + 1); child->node = l_new(struct object_node, 1); child->node->parent = node; memcpy(child->subpath, path, end - path); child->subpath[end-path] = '\0'; child->next = node->children; node->children = child; done: return makepath_recurse(child->node, end); } struct object_node *_dbus_object_tree_makepath(struct _dbus_object_tree *tree, const char *path) { if (path[0] == '/' && path[1] == '\0') return tree->root; return makepath_recurse(tree->root, path); } static struct object_node *lookup_recurse(struct object_node *node, const char *path) { const char *end; struct child_node *child; if (*path == '\0') return node; path += 1; end = strchrnul(path, '/'); child = node->children; while (child) { if (!strncmp(child->subpath, path, end - path) && child->subpath[end - path] == '\0') return lookup_recurse(child->node, end); child = child->next; } return NULL; } struct object_node *_dbus_object_tree_lookup(struct _dbus_object_tree *tree, const char *path) { if (path[0] == '/' && path[1] == '\0') return tree->root; return lookup_recurse(tree->root, path); } void _dbus_object_tree_prune_node(struct object_node *node) { struct object_node *parent = node->parent; struct child_node *p = NULL, *c; while (parent) { for (c = parent->children, p = NULL; c; p = c, c = c->next) { if (c->node != node) continue; if (p) p->next = c->next; else parent->children = c->next; subtree_free(c->node); l_free(c); break; } if (parent->children != NULL) return; if (parent->instances) return; node = parent; parent = node->parent; } } struct object_node *_dbus_object_tree_new_object(struct _dbus_object_tree *tree, const char *path, void *user_data, void (*destroy) (void *)) { struct object_node *node; if (!_dbus_valid_object_path(path)) return NULL; if (l_hashmap_lookup(tree->objects, path)) return NULL; node = _dbus_object_tree_makepath(tree, path); node->user_data = user_data; node->destroy = destroy; /* * Registered objects in the tree are marked by being present in the * tree->objects hash and having non-null node->instances. Remaining * nodes are intermediate path elements added and removed * automatically. */ node->instances = l_queue_new(); l_hashmap_insert(tree->objects, path, node); return node; } bool _dbus_object_tree_object_destroy(struct _dbus_object_tree *tree, const char *path) { struct object_node *node; const struct l_queue_entry *entry; const struct interface_instance *instance; node = l_hashmap_lookup(tree->objects, path); if (!node) return false; while ((entry = l_queue_get_entries(node->instances))) { instance = entry->data; if (!_dbus_object_tree_remove_interface(tree, path, instance->interface->name)) return false; } l_hashmap_remove(tree->objects, path); l_queue_destroy(node->instances, NULL); node->instances = NULL; if (node->destroy) { node->destroy(node->user_data); node->destroy = NULL; } if (!node->children) _dbus_object_tree_prune_node(node); return true; } static bool get_properties_dict(struct l_dbus *dbus, struct l_dbus_message *message, struct l_dbus_message_builder *builder, const struct l_dbus_interface *interface, void *user_data) { const struct l_queue_entry *entry; const struct _dbus_property *property; const char *signature; l_dbus_message_builder_enter_array(builder, "{sv}"); _dbus_message_builder_mark(builder); for (entry = l_queue_get_entries(interface->properties); entry; entry = entry->next) { property = entry->data; signature = property->metainfo + strlen(property->metainfo) + 1; l_dbus_message_builder_enter_dict(builder, "sv"); l_dbus_message_builder_append_basic(builder, 's', property->metainfo); l_dbus_message_builder_enter_variant(builder, signature); if (!property->getter(dbus, message, builder, user_data)) { if (!_dbus_message_builder_rewind(builder)) return false; continue; } l_dbus_message_builder_leave_variant(builder); l_dbus_message_builder_leave_dict(builder); _dbus_message_builder_mark(builder); } l_dbus_message_builder_leave_array(builder); return true; } static struct l_dbus_message *build_interfaces_removed_signal( const struct object_manager *manager, const struct interface_remove_record *rec) { struct l_dbus_message *signal; struct l_dbus_message_builder *builder; const struct l_queue_entry *entry; signal = l_dbus_message_new_signal(manager->dbus, manager->path, L_DBUS_INTERFACE_OBJECT_MANAGER, "InterfacesRemoved"); builder = l_dbus_message_builder_new(signal); l_dbus_message_builder_append_basic(builder, 'o', rec->path); l_dbus_message_builder_enter_array(builder, "s"); for (entry = l_queue_get_entries(rec->interface_names); entry; entry = entry->next) l_dbus_message_builder_append_basic(builder, 's', entry->data); l_dbus_message_builder_leave_array(builder); l_dbus_message_builder_finalize(builder); l_dbus_message_builder_destroy(builder); return signal; } static struct l_dbus_message *build_interfaces_added_signal( const struct object_manager *manager, const struct interface_add_record *rec) { struct l_dbus_message *signal; struct l_dbus_message_builder *builder; const struct l_queue_entry *entry; const struct interface_instance *instance; signal = l_dbus_message_new_signal(manager->dbus, manager->path, L_DBUS_INTERFACE_OBJECT_MANAGER, "InterfacesAdded"); builder = l_dbus_message_builder_new(signal); l_dbus_message_builder_append_basic(builder, 'o', rec->path); l_dbus_message_builder_enter_array(builder, "{sa{sv}}"); for (entry = l_queue_get_entries(rec->instances); entry; entry = entry->next) { instance = entry->data; l_dbus_message_builder_enter_dict(builder, "sa{sv}"); l_dbus_message_builder_append_basic(builder, 's', instance->interface->name); if (!get_properties_dict(manager->dbus, signal, builder, instance->interface, instance->user_data)) { l_dbus_message_builder_destroy(builder); l_dbus_message_unref(signal); return NULL; } l_dbus_message_builder_leave_dict(builder); } l_dbus_message_builder_leave_array(builder); l_dbus_message_builder_finalize(builder); l_dbus_message_builder_destroy(builder); return signal; } static struct l_dbus_message *build_old_property_changed_signal( struct l_dbus *dbus, const struct property_change_record *rec, const struct _dbus_property *property) { struct l_dbus_message *signal; struct l_dbus_message_builder *builder; const char *signature; signature = property->metainfo + strlen(property->metainfo) + 1; signal = l_dbus_message_new_signal(dbus, rec->path, rec->instance->interface->name, "PropertyChanged"); builder = l_dbus_message_builder_new(signal); l_dbus_message_builder_append_basic(builder, 's', property->metainfo); l_dbus_message_builder_enter_variant(builder, signature); if (!property->getter(dbus, signal, builder, rec->instance->user_data)) { l_dbus_message_builder_destroy(builder); l_dbus_message_unref(signal); return NULL; } l_dbus_message_builder_leave_variant(builder); l_dbus_message_builder_finalize(builder); l_dbus_message_builder_destroy(builder); return signal; } static struct l_dbus_message *build_properties_changed_signal( struct l_dbus *dbus, const struct property_change_record *rec) { struct l_dbus_message *signal; struct l_dbus_message_builder *builder; const struct l_queue_entry *entry; const struct _dbus_property *property; const char *signature; struct l_queue *invalidated; signal = l_dbus_message_new_signal(dbus, rec->path, L_DBUS_INTERFACE_PROPERTIES, "PropertiesChanged"); builder = l_dbus_message_builder_new(signal); invalidated = l_queue_new(); l_dbus_message_builder_append_basic(builder, 's', rec->instance->interface->name); l_dbus_message_builder_enter_array(builder, "{sv}"); for (entry = l_queue_get_entries(rec->properties); entry; entry = entry->next) { property = entry->data; signature = property->metainfo + strlen(property->metainfo) + 1; _dbus_message_builder_mark(builder); l_dbus_message_builder_enter_dict(builder, "sv"); l_dbus_message_builder_append_basic(builder, 's', property->metainfo); l_dbus_message_builder_enter_variant(builder, signature); if (!property->getter(dbus, signal, builder, rec->instance->user_data)) { if (!_dbus_message_builder_rewind(builder)) { l_dbus_message_unref(signal); signal = NULL; goto done; } l_queue_push_tail(invalidated, (void *) property); continue; } l_dbus_message_builder_leave_variant(builder); l_dbus_message_builder_leave_dict(builder); } l_dbus_message_builder_leave_array(builder); l_dbus_message_builder_enter_array(builder, "s"); while ((property = l_queue_pop_head(invalidated))) l_dbus_message_builder_append_basic(builder, 's', property->metainfo); l_dbus_message_builder_leave_array(builder); l_dbus_message_builder_finalize(builder); done: l_dbus_message_builder_destroy(builder); l_queue_destroy(invalidated, NULL); return signal; } struct emit_signals_data { struct l_dbus *dbus; struct object_manager *manager; struct object_node *node; }; static bool emit_interfaces_removed(void *data, void *user_data) { struct interface_remove_record *rec = data; struct emit_signals_data *es = user_data; struct l_dbus_message *signal; if (es->node && rec->object != es->node) return false; signal = build_interfaces_removed_signal(es->manager, rec); interface_removed_record_free(rec); if (signal) l_dbus_send(es->manager->dbus, signal); return true; } static bool emit_interfaces_added(void *data, void *user_data) { struct interface_add_record *rec = data; struct emit_signals_data *es = user_data; struct l_dbus_message *signal; if (es->node && rec->object != es->node) return false; signal = build_interfaces_added_signal(es->manager, rec); interface_add_record_free(rec); if (signal) l_dbus_send(es->manager->dbus, signal); return true; } static bool emit_properties_changed(void *data, void *user_data) { struct property_change_record *rec = data; struct emit_signals_data *es = user_data; struct l_dbus_message *signal; const struct l_queue_entry *entry; if (es->node && rec->object != es->node) return false; if (rec->instance->interface->handle_old_style_properties) for (entry = l_queue_get_entries(rec->properties); entry; entry = entry->next) { signal = build_old_property_changed_signal(es->dbus, rec, entry->data); if (signal) l_dbus_send(es->dbus, signal); } if (l_queue_find(rec->object->instances, match_interface_instance, L_DBUS_INTERFACE_PROPERTIES)) { signal = build_properties_changed_signal(es->dbus, rec); if (signal) l_dbus_send(es->dbus, signal); } property_change_record_free(rec); return true; } void _dbus_object_tree_signals_flush(struct l_dbus *dbus, const char *path) { struct _dbus_object_tree *tree = _dbus_get_tree(dbus); const struct l_queue_entry *entry; struct emit_signals_data data; bool all_done = true; if (!tree->emit_signals_work || tree->flushing) return; tree->flushing = true; data.dbus = dbus; data.node = path ? _dbus_object_tree_lookup(tree, path) : NULL; for (entry = l_queue_get_entries(tree->object_managers); entry; entry = entry->next) { data.manager = entry->data; l_queue_foreach_remove(data.manager->announce_removed, emit_interfaces_removed, &data); if (!l_queue_isempty(data.manager->announce_removed)) all_done = false; l_queue_foreach_remove(data.manager->announce_added, emit_interfaces_added, &data); if (!l_queue_isempty(data.manager->announce_added)) all_done = false; } l_queue_foreach_remove(tree->property_changes, emit_properties_changed, &data); if (!l_queue_isempty(tree->property_changes)) all_done = false; if (all_done) { l_idle_remove(tree->emit_signals_work); tree->emit_signals_work = NULL; } tree->flushing = false; } static void emit_signals(struct l_idle *idle, void *user_data) { struct l_dbus *dbus = user_data; _dbus_object_tree_signals_flush(dbus, NULL); } static void schedule_emit_signals(struct l_dbus *dbus) { struct _dbus_object_tree *tree = _dbus_get_tree(dbus); if (tree->emit_signals_work) return; tree->emit_signals_work = l_idle_create(emit_signals, dbus, NULL); } static bool match_property_changes_instance(const void *a, const void *b) { const struct property_change_record *rec = a; return rec->instance == b; } static bool match_pointer(const void *a, const void *b) { return a == b; } bool _dbus_object_tree_property_changed(struct l_dbus *dbus, const char *path, const char *interface_name, const char *property_name) { struct property_change_record *rec; struct object_node *object; struct interface_instance *instance; struct _dbus_property *property; struct _dbus_object_tree *tree = _dbus_get_tree(dbus); object = l_hashmap_lookup(tree->objects, path); if (!object) return false; instance = l_queue_find(object->instances, match_interface_instance, interface_name); if (!instance) return false; property = _dbus_interface_find_property(instance->interface, property_name); if (!property) return false; rec = l_queue_find(tree->property_changes, match_property_changes_instance, instance); if (rec) { if (l_queue_find(rec->properties, match_pointer, property)) return true; } else { rec = l_new(struct property_change_record, 1); rec->path = l_strdup(path); rec->object = object; rec->instance = instance; rec->properties = l_queue_new(); l_queue_push_tail(tree->property_changes, rec); } l_queue_push_tail(rec->properties, property); schedule_emit_signals(dbus); return true; } static void pending_property_set_done_common(struct l_dbus *dbus, struct l_dbus_message *message, struct l_dbus_message *reply, bool auto_emit) { const char *member; const char *interface_name; const char *property_name; struct l_dbus_message_iter variant; if (!reply) { reply = l_dbus_message_new_method_return(message); l_dbus_message_set_arguments(reply, ""); } l_dbus_send(dbus, l_dbus_message_ref(reply)); member = l_dbus_message_get_member(message); if (!strcmp(member, "SetProperty")) { if (!l_dbus_message_get_arguments(message, "sv", &property_name, &variant)) goto done; interface_name = l_dbus_message_get_interface(message); } else if (strcmp(member, "Set") || !l_dbus_message_get_arguments(message, "ssv", &interface_name, &property_name, &variant)) goto done; if (auto_emit) _dbus_object_tree_property_changed(dbus, l_dbus_message_get_path(message), interface_name, property_name); done: l_dbus_message_unref(message); l_dbus_message_unref(reply); } static void pending_property_set_done_emit(struct l_dbus *dbus, struct l_dbus_message *message, struct l_dbus_message *reply) { pending_property_set_done_common(dbus, message, reply, true); } static void pending_property_set_done(struct l_dbus *dbus, struct l_dbus_message *message, struct l_dbus_message *reply) { pending_property_set_done_common(dbus, message, reply, false); } static struct l_dbus_message *old_set_property(struct l_dbus *dbus, struct l_dbus_message *message, void *user_data) { struct l_dbus_interface *interface; const char *property_name; const struct _dbus_property *property; struct l_dbus_message_iter variant; struct _dbus_object_tree *tree = _dbus_get_tree(dbus); struct l_dbus_message *reply; l_dbus_property_complete_cb_t complete_cb; interface = l_hashmap_lookup(tree->interfaces, l_dbus_message_get_interface(message)); /* If we got here the interface must exist */ if (!l_dbus_message_get_arguments(message, "sv", &property_name, &variant)) return l_dbus_message_new_error(message, "org.freedesktop.DBus.Error." "InvalidArgs", "Invalid arguments"); property = _dbus_interface_find_property(interface, property_name); if (!property) return l_dbus_message_new_error(message, "org.freedesktop.DBus.Error." "InvalidArgs", "Unknown Property %s", property_name); if (!property->setter) return l_dbus_message_new_error(message, "org.freedesktop.DBus.Error." "InvalidArgs", "Property %s is read-only", property_name); if (property->flags & L_DBUS_PROPERTY_FLAG_AUTO_EMIT) complete_cb = pending_property_set_done_emit; else complete_cb = pending_property_set_done; reply = property->setter(dbus, l_dbus_message_ref(message), &variant, complete_cb, user_data); if (reply) complete_cb(dbus, message, reply); return NULL; } static struct l_dbus_message *old_get_properties(struct l_dbus *dbus, struct l_dbus_message *message, void *user_data) { const struct l_dbus_interface *interface; struct l_dbus_message *reply; struct l_dbus_message_builder *builder; struct _dbus_object_tree *tree = _dbus_get_tree(dbus); interface = l_hashmap_lookup(tree->interfaces, l_dbus_message_get_interface(message)); /* If we got here the interface must exist */ reply = l_dbus_message_new_method_return(message); builder = l_dbus_message_builder_new(reply); if (!get_properties_dict(dbus, message, builder, interface, user_data)) { l_dbus_message_unref(reply); reply = l_dbus_message_new_error(message, "org.freedesktop.DBus.Error." "Failed", "Getting properties failed"); } else l_dbus_message_builder_finalize(builder); l_dbus_message_builder_destroy(builder); return reply; } bool _dbus_object_tree_register_interface(struct _dbus_object_tree *tree, const char *interface, void (*setup_func)(struct l_dbus_interface *), void (*destroy) (void *), bool old_style_properties) { struct l_dbus_interface *dbi; if (!_dbus_valid_interface(interface)) return false; /* * Check to make sure we do not have this interface already * registered */ dbi = l_hashmap_lookup(tree->interfaces, interface); if (dbi) return false; dbi = _dbus_interface_new(interface); dbi->instance_destroy = destroy; dbi->handle_old_style_properties = old_style_properties; /* Add our methods first so we don't have to check for conflicts. */ if (old_style_properties) { l_dbus_interface_method(dbi, "SetProperty", 0, old_set_property, "", "sv", "name", "value"); l_dbus_interface_method(dbi, "GetProperties", 0, old_get_properties, "a{sv}", "", "properties"); l_dbus_interface_signal(dbi, "PropertyChanged", 0, "sv", "name", "value"); } setup_func(dbi); l_hashmap_insert(tree->interfaces, dbi->name, dbi); return true; } struct interface_check { struct _dbus_object_tree *tree; const char *interface; }; static void check_interface_used(const void *key, void *value, void *user_data) { const char *path = key; struct object_node *node = value; struct interface_check *state = user_data; if (!l_queue_find(node->instances, match_interface_instance, (char *) state->interface)) return; _dbus_object_tree_remove_interface(state->tree, path, state->interface); } bool _dbus_object_tree_unregister_interface(struct _dbus_object_tree *tree, const char *interface_name) { struct l_dbus_interface *interface; struct interface_check state = { tree, interface_name }; interface = l_hashmap_lookup(tree->interfaces, interface_name); if (!interface) return false; /* Check that the interface is not in use */ l_hashmap_foreach(tree->objects, check_interface_used, &state); l_hashmap_remove(tree->interfaces, interface_name); _dbus_interface_free(interface); return true; } static void collect_instances(struct object_node *node, const char *path, struct l_queue *announce) { const struct l_queue_entry *entry; struct interface_add_record *change_rec; const struct child_node *child; if (!node->instances) goto recurse; change_rec = l_new(struct interface_add_record, 1); change_rec->path = l_strdup(path); change_rec->object = node; change_rec->instances = l_queue_new(); for (entry = l_queue_get_entries(node->instances); entry; entry = entry->next) l_queue_push_tail(change_rec->instances, entry->data); l_queue_push_tail(announce, change_rec); recurse: if (!strcmp(path, "/")) path = ""; for (child = node->children; child; child = child->next) { char *child_path; child_path = l_strdup_printf("%s/%s", path, child->subpath); collect_instances(child->node, child_path, announce); l_free(child_path); } } static bool match_interfaces_added_object(const void *a, const void *b) { const struct interface_add_record *rec = a; return rec->object == b; } static bool match_interfaces_removed_object(const void *a, const void *b) { const struct interface_remove_record *rec = a; return rec->object == b; } bool _dbus_object_tree_add_interface(struct _dbus_object_tree *tree, const char *path, const char *interface, void *user_data) { struct object_node *object; struct l_dbus_interface *dbi; struct interface_instance *instance; const struct l_queue_entry *entry; struct object_manager *manager; size_t path_len; struct interface_add_record *change_rec; dbi = l_hashmap_lookup(tree->interfaces, interface); if (!dbi) return false; object = l_hashmap_lookup(tree->objects, path); if (!object) { object = _dbus_object_tree_new_object(tree, path, NULL, NULL); if (!object) return false; } /* * Check to make sure we do not have this interface already * registered for this object */ if (l_queue_find(object->instances, match_interface_instance_ptr, dbi)) return false; instance = l_new(struct interface_instance, 1); instance->interface = dbi; instance->user_data = user_data; l_queue_push_tail(object->instances, instance); for (entry = l_queue_get_entries(tree->object_managers); entry; entry = entry->next) { manager = entry->data; path_len = strlen(manager->path); if (strncmp(path, manager->path, path_len) || (path[path_len] != '\0' && path[path_len] != '/' && path_len > 1)) continue; change_rec = l_queue_find(manager->announce_added, match_interfaces_added_object, object); if (!change_rec) { change_rec = l_new(struct interface_add_record, 1); change_rec->path = l_strdup(path); change_rec->object = object; change_rec->instances = l_queue_new(); l_queue_push_tail(manager->announce_added, change_rec); } /* No need to check for duplicates here */ l_queue_push_tail(change_rec->instances, instance); schedule_emit_signals(manager->dbus); } if (!strcmp(interface, L_DBUS_INTERFACE_OBJECT_MANAGER)) { manager = l_new(struct object_manager, 1); manager->path = l_strdup(path); manager->dbus = instance->user_data; manager->announce_added = l_queue_new(); manager->announce_removed = l_queue_new(); l_queue_push_tail(tree->object_managers, manager); /* Emit InterfacesAdded for interfaces added before OM */ collect_instances(object, path, manager->announce_added); if (manager->dbus && !l_queue_isempty(manager->announce_added)) schedule_emit_signals(manager->dbus); } return true; } static struct interface_instance *_dbus_object_tree_find_interface( struct _dbus_object_tree *tree, const char *path, const char *interface) { struct object_node *object; object = l_hashmap_lookup(tree->objects, path); if (!object) return NULL; return l_queue_find(object->instances, match_interface_instance, interface); } void *_dbus_object_tree_get_interface_data(struct _dbus_object_tree *tree, const char *path, const char *interface) { struct interface_instance *instance; instance = _dbus_object_tree_find_interface(tree, path, interface); if (!instance) return NULL; return instance->user_data; } bool _dbus_object_tree_set_interface_data(struct _dbus_object_tree *tree, const char *path, const char *interface, void *user_data) { struct interface_instance *instance; instance = _dbus_object_tree_find_interface(tree, path, interface); if (!instance) return false; instance->user_data = user_data; return true; } static bool match_object_manager_path(const void *a, const void *b) { const struct object_manager *manager = a; return !strcmp(manager->path, b); } bool _dbus_object_tree_remove_interface(struct _dbus_object_tree *tree, const char *path, const char *interface) { struct object_node *node; struct interface_instance *instance; const struct l_queue_entry *entry; struct object_manager *manager; size_t path_len; struct interface_add_record *interfaces_added_rec; struct interface_remove_record *interfaces_removed_rec; struct property_change_record *property_change_rec; node = l_hashmap_lookup(tree->objects, path); if (!node) return false; instance = l_queue_remove_if(node->instances, match_interface_instance, (char *) interface); if (!instance) return false; if (!strcmp(interface, L_DBUS_INTERFACE_OBJECT_MANAGER)) { manager = l_queue_remove_if(tree->object_managers, match_object_manager_path, (char *) path); if (manager) object_manager_free(manager); } for (entry = l_queue_get_entries(tree->object_managers); entry; entry = entry->next) { manager = entry->data; path_len = strlen(manager->path); if (strncmp(path, manager->path, path_len) || (path[path_len] != '\0' && path[path_len] != '/' && path_len > 1)) continue; interfaces_added_rec = l_queue_find(manager->announce_added, match_interfaces_added_object, node); if (interfaces_added_rec && l_queue_remove( interfaces_added_rec->instances, instance)) { if (l_queue_isempty(interfaces_added_rec->instances)) { l_queue_remove(manager->announce_added, interfaces_added_rec); interface_add_record_free(interfaces_added_rec); } continue; } interfaces_removed_rec = l_queue_find(manager->announce_removed, match_interfaces_removed_object, node); if (!interfaces_removed_rec) { interfaces_removed_rec = l_new(struct interface_remove_record, 1); interfaces_removed_rec->path = l_strdup(path); interfaces_removed_rec->object = node; interfaces_removed_rec->interface_names = l_queue_new(); l_queue_push_tail(manager->announce_removed, interfaces_removed_rec); } /* No need to check for duplicates here */ l_queue_push_tail(interfaces_removed_rec->interface_names, l_strdup(interface)); schedule_emit_signals(manager->dbus); } property_change_rec = l_queue_remove_if(tree->property_changes, match_property_changes_instance, instance); if (property_change_rec) property_change_record_free(property_change_rec); interface_instance_free(instance); return true; } static void generate_interface_instance(void *data, void *user) { struct interface_instance *instance = data; struct l_string *buf = user; _dbus_interface_introspection(instance->interface, buf); } void _dbus_object_tree_introspect(struct _dbus_object_tree *tree, const char *path, struct l_string *buf) { struct object_node *node; struct child_node *child; bool path_is_object = true; node = l_hashmap_lookup(tree->objects, path); if (!node) { path_is_object = false; node = _dbus_object_tree_lookup(tree, path); } l_string_append(buf, XML_HEAD); l_string_append(buf, "\n"); if (node) { /* * We emit org.freedesktop.DBus.Introspectable only in case the * object node corresponds to a registered object, i.e. * exposes anything other than: * - org.freedesktop.DBus.Introspectable * - org.freedesktop.DBus.Peer * - org.freedesktop.DBus.Properties */ if (path_is_object) l_string_append(buf, static_introspectable); l_queue_foreach(node->instances, generate_interface_instance, buf); for (child = node->children; child; child = child->next) l_string_append_printf(buf, "\t\n", child->subpath); } l_string_append(buf, "\n"); } bool _dbus_object_tree_dispatch(struct _dbus_object_tree *tree, struct l_dbus *dbus, struct l_dbus_message *message) { const char *path; const char *interface; const char *member; const char *msg_sig; const char *sig; struct object_node *node; struct interface_instance *instance; struct _dbus_method *method; struct l_dbus_message *reply; path = l_dbus_message_get_path(message); interface = l_dbus_message_get_interface(message); member = l_dbus_message_get_member(message); msg_sig = l_dbus_message_get_signature(message); /* * Nothing in the spec explicitly forbids this, but handling of such * messages is left up to the implementation. * * TODO: Another route is to go looking for a matching method under this * object and call it. */ if (!interface) return false; if (!msg_sig) msg_sig = ""; if (!strcmp(interface, "org.freedesktop.DBus.Introspectable") && !strcmp(member, "Introspect") && !strcmp(msg_sig, "")) { struct l_string *buf; char *xml; buf = l_string_new(0); _dbus_object_tree_introspect(tree, path, buf); xml = l_string_unwrap(buf); reply = l_dbus_message_new_method_return(message); l_dbus_message_set_arguments(reply, "s", xml); l_dbus_send(dbus, reply); l_free(xml); return true; } node = l_hashmap_lookup(tree->objects, path); if (!node) return false; instance = l_queue_find(node->instances, match_interface_instance, (char *) interface); if (!instance) return false; method = _dbus_interface_find_method(instance->interface, member); if (!method) return false; sig = method->metainfo + method->name_len + 1; if (strcmp(msg_sig, sig)) return false; reply = method->cb(dbus, message, instance->user_data); if (reply) l_dbus_send(dbus, reply); return true; } LIB_EXPORT bool l_dbus_property_changed(struct l_dbus *dbus, const char *path, const char *interface, const char *property) { return _dbus_object_tree_property_changed(dbus, path, interface, property); } static struct l_dbus_message *properties_get(struct l_dbus *dbus, struct l_dbus_message *message, void *user_data) { const struct interface_instance *instance; const char *interface_name, *property_name, *signature; const struct _dbus_property *property; struct _dbus_object_tree *tree = _dbus_get_tree(dbus); const struct object_node *object; struct l_dbus_message *reply; struct l_dbus_message_builder *builder; if (!l_dbus_message_get_arguments(message, "ss", &interface_name, &property_name)) return l_dbus_message_new_error(message, "org.freedesktop.DBus.Error." "InvalidArgs", "Invalid arguments"); object = l_hashmap_lookup(tree->objects, l_dbus_message_get_path(message)); /* If we got here the object must exist */ instance = l_queue_find(object->instances, match_interface_instance, (char *) interface_name); if (!instance) return l_dbus_message_new_error(message, "org.freedesktop.DBus.Error." "InvalidArgs", "Object has no interface %s", interface_name); property = _dbus_interface_find_property(instance->interface, property_name); if (!property) return l_dbus_message_new_error(message, "org.freedesktop.DBus.Error." "InvalidArgs", "Unknown Property %s", property_name); reply = l_dbus_message_new_method_return(message); builder = l_dbus_message_builder_new(reply); signature = property->metainfo + strlen(property->metainfo) + 1; l_dbus_message_builder_enter_variant(builder, signature); if (property->getter(dbus, message, builder, instance->user_data)) { l_dbus_message_builder_leave_variant(builder); l_dbus_message_builder_finalize(builder); } else { l_dbus_message_unref(reply); reply = l_dbus_message_new_error(message, "org.freedesktop.DBus.Error." "Failed", "Getting property value " "failed"); } l_dbus_message_builder_destroy(builder); return reply; } static struct l_dbus_message *properties_set(struct l_dbus *dbus, struct l_dbus_message *message, void *user_data) { struct l_dbus_interface *interface; const struct interface_instance *instance; const char *interface_name, *property_name; const struct _dbus_property *property; struct l_dbus_message_iter variant; struct _dbus_object_tree *tree = _dbus_get_tree(dbus); const struct object_node *object; struct l_dbus_message *reply; l_dbus_property_complete_cb_t complete_cb; if (!l_dbus_message_get_arguments(message, "ssv", &interface_name, &property_name, &variant)) return l_dbus_message_new_error(message, "org.freedesktop.DBus.Error." "InvalidArgs", "Invalid arguments"); interface = l_hashmap_lookup(tree->interfaces, interface_name); if (!interface) return l_dbus_message_new_error(message, "org.freedesktop.DBus.Error." "InvalidArgs", "Unknown Interface %s", interface_name); property = _dbus_interface_find_property(interface, property_name); if (!property) return l_dbus_message_new_error(message, "org.freedesktop.DBus.Error." "InvalidArgs", "Unknown Property %s", property_name); if (!property->setter) return l_dbus_message_new_error(message, "org.freedesktop.DBus.Error." "InvalidArgs", "Property %s is read-only", property_name); object = l_hashmap_lookup(tree->objects, l_dbus_message_get_path(message)); /* If we got here the object must exist */ instance = l_queue_find(object->instances, match_interface_instance_ptr, interface); if (!instance) return l_dbus_message_new_error(message, "org.freedesktop.DBus.Error." "InvalidArgs", "Object has no interface %s", interface_name); if (property->flags & L_DBUS_PROPERTY_FLAG_AUTO_EMIT) complete_cb = pending_property_set_done_emit; else complete_cb = pending_property_set_done; reply = property->setter(dbus, l_dbus_message_ref(message), &variant, complete_cb, instance->user_data); if (reply) complete_cb(dbus, message, reply); return NULL; } static struct l_dbus_message *properties_get_all(struct l_dbus *dbus, struct l_dbus_message *message, void *user_data) { const struct interface_instance *instance; const char *interface_name; struct _dbus_object_tree *tree = _dbus_get_tree(dbus); const struct object_node *object; struct l_dbus_message *reply; struct l_dbus_message_builder *builder; if (!l_dbus_message_get_arguments(message, "s", &interface_name)) return l_dbus_message_new_error(message, "org.freedesktop.DBus.Error." "InvalidArgs", "Invalid arguments"); object = l_hashmap_lookup(tree->objects, l_dbus_message_get_path(message)); /* If we got here the object must exist */ instance = l_queue_find(object->instances, match_interface_instance, (char *) interface_name); if (!instance) return l_dbus_message_new_error(message, "org.freedesktop.DBus.Error." "InvalidArgs", "Object has no interface %s", interface_name); reply = l_dbus_message_new_method_return(message); builder = l_dbus_message_builder_new(reply); if (!get_properties_dict(dbus, message, builder, instance->interface, instance->user_data)) { l_dbus_message_unref(reply); reply = l_dbus_message_new_error(message, "org.freedesktop.DBus.Error." "Failed", "Getting property values " "failed"); } else l_dbus_message_builder_finalize(builder); l_dbus_message_builder_destroy(builder); return reply; } static void properties_setup_func(struct l_dbus_interface *interface) { l_dbus_interface_method(interface, "Get", 0, properties_get, "v", "ss", "value", "interface_name", "property_name"); l_dbus_interface_method(interface, "Set", 0, properties_set, "", "ssv", "interface_name", "property_name", "value"); l_dbus_interface_method(interface, "GetAll", 0, properties_get_all, "a{sv}", "s", "props", "interface_name"); l_dbus_interface_signal(interface, "PropertiesChanged", 0, "sa{sv}as", "interface_name", "changed_properties", "invalidated_properties"); } static bool collect_objects(struct l_dbus *dbus, struct l_dbus_message *message, struct l_dbus_message_builder *builder, const struct object_node *node, const char *path) { const struct l_queue_entry *entry; const struct child_node *child; char *child_path; const struct interface_instance *instance; bool r; if (!node->instances) goto recurse; l_dbus_message_builder_enter_dict(builder, "oa{sa{sv}}"); l_dbus_message_builder_append_basic(builder, 'o', path); l_dbus_message_builder_enter_array(builder, "{sa{sv}}"); for (entry = l_queue_get_entries(node->instances); entry; entry = entry->next) { instance = entry->data; l_dbus_message_builder_enter_dict(builder, "sa{sv}"); l_dbus_message_builder_append_basic(builder, 's', instance->interface->name); if (!get_properties_dict(dbus, message, builder, instance->interface, instance->user_data)) return false; l_dbus_message_builder_leave_dict(builder); } l_dbus_message_builder_leave_array(builder); l_dbus_message_builder_leave_dict(builder); recurse: if (!strcmp(path, "/")) path = ""; for (child = node->children; child; child = child->next) { child_path = l_strdup_printf("%s/%s", path, child->subpath); r = collect_objects(dbus, message, builder, child->node, child_path); l_free(child_path); if (!r) return false; } return true; } struct l_dbus_message *_dbus_object_tree_get_objects( struct _dbus_object_tree *tree, struct l_dbus *dbus, const char *path, struct l_dbus_message *message) { const struct object_node *node; struct l_dbus_message *reply; struct l_dbus_message_builder *builder; node = l_hashmap_lookup(tree->objects, path); reply = l_dbus_message_new_method_return(message); builder = l_dbus_message_builder_new(reply); l_dbus_message_builder_enter_array(builder, "{oa{sa{sv}}}"); if (!collect_objects(dbus, message, builder, node, path)) { l_dbus_message_builder_destroy(builder); l_dbus_message_unref(reply); return l_dbus_message_new_error(message, "org.freedesktop.DBus.Error." "Failed", "Getting property values " "failed"); } l_dbus_message_builder_leave_array(builder); l_dbus_message_builder_finalize(builder); l_dbus_message_builder_destroy(builder); return reply; } static struct l_dbus_message *get_managed_objects(struct l_dbus *dbus, struct l_dbus_message *message, void *user_data) { struct _dbus_object_tree *tree = _dbus_get_tree(dbus); const char *path = l_dbus_message_get_path(message); return _dbus_object_tree_get_objects(tree, dbus, path, message); } static void object_manager_setup_func(struct l_dbus_interface *interface) { l_dbus_interface_method(interface, "GetManagedObjects", 0, get_managed_objects, "a{oa{sa{sv}}}", "", "objpath_interfaces_and_properties"); l_dbus_interface_signal(interface, "InterfacesAdded", 0, "oa{sa{sv}}", "object_path", "interfaces_and_properties"); l_dbus_interface_signal(interface, "InterfacesRemoved", 0, "oas", "object_path", "interfaces"); } bluez-5.82/ell/PaxHeaders/time-private.h0000644000000000000000000000005014667515664015225 xustar0020 atime=1743575449 20 ctime=1743591276 bluez-5.82/ell/time-private.h0000644000000000000000000000072614667515664014713 0ustar00rootroot/* * Embedded Linux library * Copyright (C) 2020 Intel Corporation * * SPDX-License-Identifier: LGPL-2.1-or-later */ struct timeval; uint64_t _time_pick_interval_secs(uint32_t min_secs, uint32_t max_secs); uint64_t _time_fuzz_msecs(uint64_t ms); uint64_t _time_fuzz_secs(uint32_t secs, uint32_t max_offset); uint64_t _time_realtime_to_boottime(const struct timeval *ts); uint64_t time_realtime_now(void); uint64_t _time_from_timespec(const struct timespec *ts); bluez-5.82/ell/PaxHeaders/key.c0000644000000000000000000000005014770744372013375 xustar0020 atime=1743575496 20 ctime=1743591276 bluez-5.82/ell/key.c0000644000000000000000000004263014770744372013063 0ustar00rootroot/* * Embedded Linux library * Copyright (C) 2016 Intel Corporation * * SPDX-License-Identifier: LGPL-2.1-or-later */ #ifdef HAVE_CONFIG_H #include #endif #define _GNU_SOURCE #include #include #include #include #include #include "private.h" #include "useful.h" #include "key.h" #include "string.h" #include "random.h" #include "missing.h" #ifndef KEYCTL_DH_COMPUTE #define KEYCTL_DH_COMPUTE 23 #endif #ifndef KEYCTL_PKEY_QUERY #define KEYCTL_PKEY_QUERY 24 #define KEYCTL_PKEY_ENCRYPT 25 #define KEYCTL_PKEY_DECRYPT 26 #define KEYCTL_PKEY_SIGN 27 #define KEYCTL_PKEY_VERIFY 28 #define KEYCTL_SUPPORTS_ENCRYPT 0x01 #define KEYCTL_SUPPORTS_DECRYPT 0x02 #define KEYCTL_SUPPORTS_SIGN 0x04 #define KEYCTL_SUPPORTS_VERIFY 0x08 struct keyctl_pkey_query { uint32_t supported_ops; uint32_t key_size; uint16_t max_data_size; uint16_t max_sig_size; uint16_t max_enc_size; uint16_t max_dec_size; uint32_t __spare[10]; }; struct keyctl_pkey_params { int32_t key_id; uint32_t in_len; union { uint32_t out_len; uint32_t in2_len; }; uint32_t __spare[7]; }; /* Work around the missing (pre-4.7) or broken (4.14.{70,71,72} and * 4.18.{8,9,10}) kernel declaration of struct keyctl_dh_params */ struct dh_params { int32_t private; int32_t prime; int32_t base; }; #else /* When KEYCTL_PKEY_QUERY is defined by the kernel, the * struct keyctl_dh_params declaration is valid. */ #define dh_params keyctl_dh_params #endif #ifndef KEYCTL_RESTRICT_KEYRING #define KEYCTL_RESTRICT_KEYRING 29 #endif static int32_t internal_keyring; struct l_key { int type; int32_t serial; }; struct l_keyring { int32_t serial; }; static const char * const key_type_names[] = { [L_KEY_RAW] = "user", [L_KEY_RSA] = "asymmetric", [L_KEY_ECC] = "asymmetric", }; static long kernel_add_key(const char *type, const char *description, const void *payload, size_t len, int32_t keyring) { long result; result = syscall(__NR_add_key, type, description, payload, len, keyring); return result >= 0 ? result : -errno; } static long kernel_read_key(int32_t serial, const void *payload, size_t len) { long result; result = syscall(__NR_keyctl, KEYCTL_READ, serial, payload, len); return result >= 0 ? result : -errno; } static long kernel_update_key(int32_t serial, const void *payload, size_t len) { long result; result = syscall(__NR_keyctl, KEYCTL_UPDATE, serial, payload, len); return result >= 0 ? result : -errno; } static long kernel_invalidate_key(int32_t serial) { long result; result = syscall(__NR_keyctl, KEYCTL_INVALIDATE, serial); return result >= 0 ? result : -errno; } static long kernel_link_key(int32_t key_serial, int32_t ring_serial) { long result; result = syscall(__NR_keyctl, KEYCTL_LINK, key_serial, ring_serial); return result >= 0 ? result : -errno; } static long kernel_unlink_key(int32_t key_serial, int32_t ring_serial) { long result; result = syscall(__NR_keyctl, KEYCTL_UNLINK, key_serial, ring_serial); return result >= 0 ? result : -errno; } static char *format_key_info(const char *encoding, const char *hash) { struct l_string *info; if (!encoding && !hash) return NULL; info = l_string_new(0); if (encoding) l_string_append_printf(info, "enc=%s ", encoding); if (hash) l_string_append_printf(info, "hash=%s", hash); return l_string_unwrap(info); } static long kernel_query_key(int32_t key_serial, const char *encoding, const char *hash, size_t *size, bool *public) { long result; struct keyctl_pkey_query query; char *info = format_key_info(encoding, hash); memset(&query, 0, sizeof(query)); result = syscall(__NR_keyctl, KEYCTL_PKEY_QUERY, key_serial, 0, info ?: "", &query); if (result == 0) { *size = query.key_size; *public = ((query.supported_ops & KEYCTL_SUPPORTS_ENCRYPT) && !(query.supported_ops & KEYCTL_SUPPORTS_DECRYPT)); } l_free(info); return result >= 0 ? result : -errno; } static long kernel_dh_compute(int32_t private, int32_t prime, int32_t base, void *payload, size_t len) { long result; struct dh_params params = { .private = private, .prime = prime, .base = base }; result = syscall(__NR_keyctl, KEYCTL_DH_COMPUTE, ¶ms, payload, len, NULL); return result >= 0 ? result : -errno; } static long kernel_restrict_keyring(int32_t serial, const char *keytype, const char *restriction) { long result; result = syscall(__NR_keyctl, KEYCTL_RESTRICT_KEYRING, serial, keytype, restriction); return result >= 0 ? result : -errno; } static long kernel_key_eds(int op, int32_t serial, const char *encoding, const char *hash, const void *in, void *out, size_t len_in, size_t len_out) { long result; struct keyctl_pkey_params params = { .key_id = serial, .in_len = len_in, .out_len = len_out }; char *info = format_key_info(encoding, hash); memset(out, 0, len_out); result = syscall(__NR_keyctl, op, ¶ms, info ?: "", in, out); l_free(info); return result >= 0 ? result : -errno; } static long kernel_key_verify(int32_t serial, const char *encoding, const char *hash, const void *data, size_t data_len, const void *sig, size_t sig_len) { struct keyctl_pkey_params params = { .key_id = serial, .in_len = data_len, .in2_len = sig_len, }; char *info = format_key_info(encoding, hash); long result; result = syscall(__NR_keyctl, KEYCTL_PKEY_VERIFY, ¶ms, info ?: "", data, sig); l_free(info); return result >= 0 ? result : -errno; } static bool setup_internal_keyring(void) { internal_keyring = kernel_add_key("keyring", "ell-internal", NULL, 0, KEY_SPEC_THREAD_KEYRING); if (internal_keyring <= 0) { internal_keyring = 0; return false; } return true; } LIB_EXPORT struct l_key *l_key_new(enum l_key_type type, const void *payload, size_t payload_length) { struct l_key *key; char *description; static unsigned long key_idx; if (unlikely(!payload)) return NULL; if (unlikely((size_t)type >= L_ARRAY_SIZE(key_type_names))) return NULL; if (!internal_keyring && !setup_internal_keyring()) return NULL; key = l_new(struct l_key, 1); key->type = type; description = l_strdup_printf("ell-key-%lu", key_idx++); key->serial = kernel_add_key(key_type_names[type], description, payload, payload_length, internal_keyring); l_free(description); if (key->serial < 0) { l_free(key); key = NULL; } /* * TODO: Query asymmetric key algorithm from the kernel and * ensure that it matches the expected l_key_type. This can * currently be found by digging through /proc/keys, but a * keyctl() op makes more sense. */ return key; } LIB_EXPORT void l_key_free(struct l_key *key) { if (unlikely(!key)) return; /* * Use invalidate as, unlike revoke, this doesn't delay the * key garbage collection and causes the quota used by the * key to be released sooner and more predictably. */ kernel_invalidate_key(key->serial); l_free(key); } LIB_EXPORT void l_key_free_norevoke(struct l_key *key) { if (unlikely(!key)) return; kernel_unlink_key(key->serial, internal_keyring); l_free(key); } LIB_EXPORT bool l_key_update(struct l_key *key, const void *payload, size_t len) { long error; if (unlikely(!key)) return false; error = kernel_update_key(key->serial, payload, len); return error == 0; } LIB_EXPORT bool l_key_extract(struct l_key *key, void *payload, size_t *len) { long keylen; if (unlikely(!key)) return false; keylen = kernel_read_key(key->serial, payload, *len); if (keylen < 0 || (size_t)keylen > *len) { explicit_bzero(payload, *len); return false; } *len = keylen; return true; } LIB_EXPORT ssize_t l_key_get_payload_size(struct l_key *key) { return kernel_read_key(key->serial, NULL, 0); } static const char *lookup_cipher(enum l_key_cipher_type cipher) { switch (cipher) { case L_KEY_RSA_PKCS1_V1_5: return "pkcs1"; case L_KEY_RSA_RAW: return "raw"; case L_KEY_ECDSA_X962: return "x962"; } return NULL; } static const char *lookup_checksum(enum l_checksum_type checksum) { const char* ret = NULL; switch (checksum) { case L_CHECKSUM_NONE: break; case L_CHECKSUM_MD4: ret = "md4"; break; case L_CHECKSUM_MD5: ret = "md5"; break; case L_CHECKSUM_SHA1: ret = "sha1"; break; case L_CHECKSUM_SHA224: ret = "sha224"; break; case L_CHECKSUM_SHA256: ret = "sha256"; break; case L_CHECKSUM_SHA384: ret = "sha384"; break; case L_CHECKSUM_SHA512: ret = "sha512"; break; case L_CHECKSUM_SHA3_224: ret = "sha3-224"; break; case L_CHECKSUM_SHA3_256: ret = "sha3-256"; break; case L_CHECKSUM_SHA3_384: ret = "sha3-384"; break; case L_CHECKSUM_SHA3_512: ret = "sha3-512"; break; } return ret; } LIB_EXPORT bool l_key_get_info(struct l_key *key, enum l_key_cipher_type cipher, enum l_checksum_type checksum, size_t *bits, bool *public) { if (unlikely(!key)) return false; return !kernel_query_key(key->serial, lookup_cipher(cipher), lookup_checksum(checksum), bits, public); } LIB_EXPORT struct l_key *l_key_generate_dh_private(const void *prime_buf, size_t prime_len) { uint8_t *buf; const uint8_t *prime = prime_buf; size_t prime_bits; unsigned int i; size_t private_bytes; size_t random_bytes; struct l_key *private; /* Find the prime's bit length excluding leading 0s */ for (i = 0; i < prime_len && !prime[i]; i++); if (i == prime_len || (i == prime_len - 1 && prime[i] < 5)) return NULL; prime_bits = (prime_len - i) * 8 - __builtin_clz(prime[i]); /* * Generate a random DH private value conforming to 1 < x < p - 1. * To do this covering all possible values in this range with the * same probability of generating each value generally requires * looping. Instead we generate a value in the range * [2 ^ (prime_bits - 2), 2 ^ (prime_bits - 1) - 1] by forcing bit * prime_bits - 2 to 1, i.e. the range in PKCS #3 Section 7.1 for * l equal to prime_bits - 1. This means we're using between * one half and one quarter of the full [2, p - 2] range, i.e. * between 1 and 2 bits fewer. Note that since p is odd * p - 1 has the same bit length as p and so our maximum value * 2 ^ (prime_bits - 1) - 1 is still less than p - 1. */ private_bytes = ((prime_bits - 1) + 7) / 8; random_bytes = ((prime_bits - 2) + 7) / 8; buf = l_malloc(private_bytes); l_getrandom(buf + private_bytes - random_bytes, random_bytes); buf[0] &= (1 << ((prime_bits - 2) % 8)) - 1; buf[0] |= 1 << ((prime_bits - 2) % 8); private = l_key_new(L_KEY_RAW, buf, private_bytes); explicit_bzero(buf, private_bytes); l_free(buf); return private; } static bool compute_common(struct l_key *base, struct l_key *private, struct l_key *prime, void *payload, size_t *len) { long result_len; bool usable_payload = *len != 0; result_len = kernel_dh_compute(private->serial, prime->serial, base->serial, payload, *len); if (result_len > 0) { *len = result_len; return usable_payload; } return false; } LIB_EXPORT bool l_key_compute_dh_public(struct l_key *generator, struct l_key *private, struct l_key *prime, void *payload, size_t *len) { return compute_common(generator, private, prime, payload, len); } LIB_EXPORT bool l_key_compute_dh_secret(struct l_key *other_public, struct l_key *private, struct l_key *prime, void *payload, size_t *len) { return compute_common(other_public, private, prime, payload, len); } static int be_bignum_compare(const uint8_t *a, size_t a_len, const uint8_t *b, size_t b_len) { unsigned int i; if (a_len >= b_len) { for (i = 0; i < a_len - b_len; i++) if (a[i]) return 1; return memcmp(a + i, b, b_len); } for (i = 0; i < b_len - a_len; i++) if (b[i]) return -1; return memcmp(a, b + i, a_len); } /* * Validate that @payload is within range for a private and public key for * a DH computation in the finite field group defined by modulus @prime_buf, * both numbers stored as big-endian integers. We require a key in the * [2, prime - 2] (inclusive) interval. PKCS #3 does not exclude 1 as a * private key but other specs do. */ LIB_EXPORT bool l_key_validate_dh_payload(const void *payload, size_t len, const void *prime_buf, size_t prime_len) { static const uint8_t one[] = { 1 }; uint8_t prime_1[prime_len]; /* * Produce prime - 1 for the payload < prime - 1 check. * prime is odd so just zero the LSB. */ memcpy(prime_1, prime_buf, prime_len); if (prime_len < 1 || !(prime_1[prime_len - 1] & 1)) return false; prime_1[prime_len - 1] &= ~1; if (be_bignum_compare(payload, len, one, 1) <= 0) return false; if (be_bignum_compare(payload, len, prime_1, prime_len) >= 0) return false; return true; } /* Common code for encrypt/decrypt/sign */ static ssize_t eds_common(struct l_key *key, enum l_key_cipher_type cipher, enum l_checksum_type checksum, const void *in, void *out, size_t len_in, size_t len_out, int op) { if (unlikely(!key)) return -EINVAL; return kernel_key_eds(op, key->serial, lookup_cipher(cipher), lookup_checksum(checksum), in, out, len_in, len_out); } LIB_EXPORT ssize_t l_key_encrypt(struct l_key *key, enum l_key_cipher_type cipher, enum l_checksum_type checksum, const void *in, void *out, size_t len_in, size_t len_out) { ssize_t ret_len; ret_len = eds_common(key, cipher, checksum, in, out, len_in, len_out, KEYCTL_PKEY_ENCRYPT); return ret_len; } LIB_EXPORT ssize_t l_key_decrypt(struct l_key *key, enum l_key_cipher_type cipher, enum l_checksum_type checksum, const void *in, void *out, size_t len_in, size_t len_out) { ssize_t ret_len; ret_len = eds_common(key, cipher, checksum, in, out, len_in, len_out, KEYCTL_PKEY_DECRYPT); if (ret_len < 0) goto done; done: return ret_len; } LIB_EXPORT ssize_t l_key_sign(struct l_key *key, enum l_key_cipher_type cipher, enum l_checksum_type checksum, const void *in, void *out, size_t len_in, size_t len_out) { ssize_t ret_len; ret_len = eds_common(key, cipher, checksum, in, out, len_in, len_out, KEYCTL_PKEY_SIGN); return ret_len; } LIB_EXPORT bool l_key_verify(struct l_key *key, enum l_key_cipher_type cipher, enum l_checksum_type checksum, const void *data, const void *sig, size_t len_data, size_t len_sig) { long result; if (unlikely(!key)) return false; result = kernel_key_verify(key->serial, lookup_cipher(cipher), lookup_checksum(checksum), data, len_data, sig, len_sig); return result >= 0; } LIB_EXPORT struct l_keyring *l_keyring_new(void) { struct l_keyring *keyring; char *description; static unsigned long keyring_idx; if (!internal_keyring && !setup_internal_keyring()) return NULL; keyring = l_new(struct l_keyring, 1); description = l_strdup_printf("ell-keyring-%lu", keyring_idx++); keyring->serial = kernel_add_key("keyring", description, NULL, 0, internal_keyring); l_free(description); if (keyring->serial < 0) { l_free(keyring); return NULL; } return keyring; } LIB_EXPORT bool l_keyring_restrict(struct l_keyring *keyring, enum l_keyring_restriction res, const struct l_keyring *trusted) { char *restriction = NULL; long result; switch (res) { case L_KEYRING_RESTRICT_ASYM: case L_KEYRING_RESTRICT_ASYM_CHAIN: { char *option = ""; if (res == L_KEYRING_RESTRICT_ASYM_CHAIN) option = ":chain"; restriction = l_strdup_printf("key_or_keyring:%d%s", trusted ? trusted->serial : 0, option); break; } default: /* Unsupported type */ return NULL; } result = kernel_restrict_keyring(keyring->serial, "asymmetric", restriction); l_free(restriction); return result == 0; } LIB_EXPORT void l_keyring_free(struct l_keyring *keyring) { if (unlikely(!keyring)) return; kernel_invalidate_key(keyring->serial); l_free(keyring); } LIB_EXPORT void l_keyring_free_norevoke(struct l_keyring *keyring) { if (unlikely(!keyring)) return; kernel_unlink_key(keyring->serial, internal_keyring); l_free(keyring); } LIB_EXPORT bool l_keyring_link(struct l_keyring *keyring, const struct l_key *key) { long error; if (unlikely(!keyring) || unlikely(!key)) return false; error = kernel_link_key(key->serial, keyring->serial); return error == 0; } LIB_EXPORT bool l_keyring_unlink(struct l_keyring *keyring, const struct l_key *key) { long error; if (unlikely(!keyring) || unlikely(!key)) return false; error = kernel_unlink_key(key->serial, keyring->serial); return error == 0; } LIB_EXPORT bool l_keyring_link_nested(struct l_keyring *keyring, const struct l_keyring *nested) { long error; if (unlikely(!keyring) || unlikely(!nested)) return false; error = kernel_link_key(nested->serial, keyring->serial); return error == 0; } LIB_EXPORT bool l_keyring_unlink_nested(struct l_keyring *keyring, const struct l_keyring *nested) { long error; if (unlikely(!keyring) || unlikely(!nested)) return false; error = kernel_unlink_key(nested->serial, keyring->serial); return error == 0; } LIB_EXPORT bool l_key_is_supported(uint32_t features) { long result; if (features & L_KEY_FEATURE_DH) { result = syscall(__NR_keyctl, KEYCTL_DH_COMPUTE, NULL, "x", 1, NULL); if (result == -1 && errno == EOPNOTSUPP) return false; } if (features & L_KEY_FEATURE_RESTRICT) { result = syscall(__NR_keyctl, KEYCTL_RESTRICT_KEYRING, 0, "asymmetric", ""); if (result == -1 && errno == EOPNOTSUPP) return false; } if (features & L_KEY_FEATURE_CRYPTO) { result = syscall(__NR_keyctl, KEYCTL_PKEY_QUERY, 0, 0, "", 0); if (result == -1 && errno == EOPNOTSUPP) return false; } return true; } bluez-5.82/ell/PaxHeaders/key.h0000644000000000000000000000005014504767710013376 xustar0020 atime=1743575449 20 ctime=1743591276 bluez-5.82/ell/key.h0000644000000000000000000000611414504767710013061 0ustar00rootroot/* * Embedded Linux library * Copyright (C) 2016 Intel Corporation * * SPDX-License-Identifier: LGPL-2.1-or-later */ #ifndef __ELL_KEY_H #define __ELL_KEY_H #include #include #include #include #ifdef __cplusplus extern "C" { #endif struct l_key; struct l_keyring; enum l_key_feature { L_KEY_FEATURE_DH = 1 << 0, L_KEY_FEATURE_RESTRICT = 1 << 1, L_KEY_FEATURE_CRYPTO = 1 << 2, }; enum l_key_type { L_KEY_RAW = 0, L_KEY_RSA, L_KEY_ECC, }; enum l_keyring_restriction { L_KEYRING_RESTRICT_ASYM = 0, L_KEYRING_RESTRICT_ASYM_CHAIN, }; enum l_key_cipher_type { L_KEY_RSA_PKCS1_V1_5, L_KEY_RSA_RAW, L_KEY_ECDSA_X962, }; struct l_key *l_key_new(enum l_key_type type, const void *payload, size_t payload_length); void l_key_free(struct l_key *key); void l_key_free_norevoke(struct l_key *key); bool l_key_update(struct l_key *key, const void *payload, size_t len); bool l_key_extract(struct l_key *key, void *payload, size_t *len); ssize_t l_key_get_payload_size(struct l_key *key); bool l_key_get_info(struct l_key *key, enum l_key_cipher_type cipher, enum l_checksum_type checksum, size_t *bits, bool *out_public); struct l_key *l_key_generate_dh_private(const void *prime_buf, size_t prime_len); bool l_key_compute_dh_public(struct l_key *generator, struct l_key *private_key, struct l_key *prime, void *payload, size_t *len); bool l_key_compute_dh_secret(struct l_key *other_public, struct l_key *private_key, struct l_key *prime, void *payload, size_t *len); bool l_key_validate_dh_payload(const void *payload, size_t len, const void *prime_buf, size_t prime_len); ssize_t l_key_encrypt(struct l_key *key, enum l_key_cipher_type cipher, enum l_checksum_type checksum, const void *in, void *out, size_t len_in, size_t len_out); ssize_t l_key_decrypt(struct l_key *key, enum l_key_cipher_type cipher, enum l_checksum_type checksum, const void *in, void *out, size_t len_in, size_t len_out); ssize_t l_key_sign(struct l_key *key, enum l_key_cipher_type cipher, enum l_checksum_type checksum, const void *in, void *out, size_t len_in, size_t len_out); bool l_key_verify(struct l_key *key, enum l_key_cipher_type cipher, enum l_checksum_type checksum, const void *data, const void *sig, size_t len_data, size_t len_sig); struct l_keyring *l_keyring_new(void); bool l_keyring_restrict(struct l_keyring *keyring, enum l_keyring_restriction res, const struct l_keyring *trust); void l_keyring_free(struct l_keyring *keyring); DEFINE_CLEANUP_FUNC(l_keyring_free); void l_keyring_free_norevoke(struct l_keyring *keyring); DEFINE_CLEANUP_FUNC(l_keyring_free_norevoke); bool l_keyring_link(struct l_keyring *keyring, const struct l_key *key); bool l_keyring_unlink(struct l_keyring *keyring, const struct l_key *key); bool l_keyring_link_nested(struct l_keyring *keyring, const struct l_keyring *nested); bool l_keyring_unlink_nested(struct l_keyring *keyring, const struct l_keyring *nested); bool l_key_is_supported(uint32_t features); #ifdef __cplusplus } #endif #endif /* __ELL_KEY_H */ bluez-5.82/ell/PaxHeaders/ecc-external.c0000644000000000000000000000005014770744372015157 xustar0020 atime=1743575542 20 ctime=1743591277 bluez-5.82/ell/ecc-external.c0000644000000000000000000007125114770744372014646 0ustar00rootroot/* * Copyright (c) 2013, Kenneth MacKay * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are * met: * * Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #ifdef HAVE_CONFIG_H #include #endif #include #include #include "private.h" #include "ecc.h" #include "ecc-private.h" #include "random.h" typedef struct { uint64_t m_low; uint64_t m_high; } uint128_t; static void vli_clear(uint64_t *vli, unsigned int ndigits) { unsigned int i; for (i = 0; i < ndigits; i++) vli[i] = 0; } /* Returns true if vli == 0, false otherwise. */ static bool vli_is_zero(const uint64_t *vli, unsigned int ndigits) { unsigned int i; for (i = 0; i < ndigits; i++) { if (vli[i]) return false; } return true; } /* Returns nonzero if bit bit of vli is set. */ static uint64_t vli_test_bit(const uint64_t *vli, unsigned int bit) { return (vli[bit / 64] & ((uint64_t) 1 << (bit % 64))); } /* Sets dest = src. */ static void vli_set(uint64_t *dest, const uint64_t *src, unsigned int ndigits) { unsigned int i; for (i = 0; i < ndigits; i++) dest[i] = src[i]; } /* Returns sign of left - right. */ int _vli_cmp(const uint64_t *left, const uint64_t *right, unsigned int ndigits) { int i; for (i = ndigits - 1; i >= 0; i--) { if (left[i] > right[i]) return 1; if (left[i] < right[i]) return -1; } return 0; } /* Computes result = in << c, returning carry. Can modify in place * (if result == in). 0 < shift < 64. */ static uint64_t vli_lshift(uint64_t *result, const uint64_t *in, unsigned int shift, unsigned int ndigits) { uint64_t carry = 0; unsigned int i; for (i = 0; i < ndigits; i++) { uint64_t temp = in[i]; result[i] = (temp << shift) | carry; carry = temp >> (64 - shift); } return carry; } /* Computes vli = vli >> 1. */ void _vli_rshift1(uint64_t *vli, unsigned int ndigits) { uint64_t *end = vli; uint64_t carry = 0; vli += ndigits; while (vli-- > end) { uint64_t temp = *vli; *vli = (temp >> 1) | carry; carry = temp << 63; } } /* Computes result = left + right, returning carry. Can modify in place. */ uint64_t _vli_add(uint64_t *result, const uint64_t *left, const uint64_t *right, unsigned int ndigits) { uint64_t carry = 0; unsigned int i; for (i = 0; i < ndigits; i++) { uint64_t sum; sum = left[i] + right[i] + carry; if (sum != left[i]) carry = (sum < left[i]); result[i] = sum; } return carry; } /* Computes result = left - right, returning borrow. Can modify in place. */ uint64_t _vli_sub(uint64_t *result, const uint64_t *left, const uint64_t *right, unsigned int ndigits) { uint64_t borrow = 0; unsigned int i; for (i = 0; i < ndigits; i++) { uint64_t diff; diff = left[i] - right[i] - borrow; if (diff != left[i]) borrow = (diff > left[i]); result[i] = diff; } return borrow; } static uint128_t mul_64_64(uint64_t left, uint64_t right) { uint64_t a0 = left & 0xffffffffull; uint64_t a1 = left >> 32; uint64_t b0 = right & 0xffffffffull; uint64_t b1 = right >> 32; uint64_t m0 = a0 * b0; uint64_t m1 = a0 * b1; uint64_t m2 = a1 * b0; uint64_t m3 = a1 * b1; uint128_t result; m2 += (m0 >> 32); m2 += m1; /* Overflow */ if (m2 < m1) m3 += 0x100000000ull; result.m_low = (m0 & 0xffffffffull) | (m2 << 32); result.m_high = m3 + (m2 >> 32); return result; } static uint128_t add_128_128(uint128_t a, uint128_t b) { uint128_t result; result.m_low = a.m_low + b.m_low; result.m_high = a.m_high + b.m_high + (result.m_low < a.m_low); return result; } static void vli_mult(uint64_t *result, const uint64_t *left, const uint64_t *right, unsigned int ndigits) { uint128_t r01 = { 0, 0 }; uint64_t r2 = 0; unsigned int i, k; /* Compute each digit of result in sequence, maintaining the * carries. */ for (k = 0; k < ndigits * 2 - 1; k++) { unsigned int min; if (k < ndigits) min = 0; else min = (k + 1) - ndigits; for (i = min; i <= k && i < ndigits; i++) { uint128_t product; product = mul_64_64(left[i], right[k - i]); r01 = add_128_128(r01, product); r2 += (r01.m_high < product.m_high); } result[k] = r01.m_low; r01.m_low = r01.m_high; r01.m_high = r2; r2 = 0; } result[ndigits * 2 - 1] = r01.m_low; } static void vli_square(uint64_t *result, const uint64_t *left, unsigned int ndigits) { uint128_t r01 = { 0, 0 }; uint64_t r2 = 0; unsigned int i, k; for (k = 0; k < ndigits * 2 - 1; k++) { unsigned int min; if (k < ndigits) min = 0; else min = (k + 1) - ndigits; for (i = min; i <= k && i <= k - i; i++) { uint128_t product; product = mul_64_64(left[i], left[k - i]); if (i < k - i) { r2 += product.m_high >> 63; product.m_high = (product.m_high << 1) | (product.m_low >> 63); product.m_low <<= 1; } r01 = add_128_128(r01, product); r2 += (r01.m_high < product.m_high); } result[k] = r01.m_low; r01.m_low = r01.m_high; r01.m_high = r2; r2 = 0; } result[ndigits * 2 - 1] = r01.m_low; } /* Computes result = (left + right) % mod. * Assumes that left < mod and right < mod, result != mod. */ void _vli_mod_add(uint64_t *result, const uint64_t *left, const uint64_t *right, const uint64_t *mod, unsigned int ndigits) { uint64_t carry; carry = _vli_add(result, left, right, ndigits); /* result > mod (result = mod + remainder), so subtract mod to * get remainder. */ if (carry || _vli_cmp(result, mod, ndigits) >= 0) _vli_sub(result, result, mod, ndigits); } /* Computes result = (left - right) % mod. * Assumes that left < mod and right < mod, result != mod. */ void _vli_mod_sub(uint64_t *result, const uint64_t *left, const uint64_t *right, const uint64_t *mod, unsigned int ndigits) { uint64_t borrow = _vli_sub(result, left, right, ndigits); /* In this case, p_result == -diff == (max int) - diff. * Since -x % d == d - x, we can get the correct result from * result + mod (with overflow). */ if (borrow) _vli_add(result, result, mod, ndigits); } /* Counts the number of 64-bit "digits" in vli. */ static unsigned int _vli_num_digits(const uint64_t *vli, unsigned int ndigits) { int i; /* Search from the end until we find a non-zero digit. * We do it in reverse because we expect that most digits will * be nonzero. */ for (i = ndigits - 1; i >= 0 && vli[i] == 0; i--); return (i + 1); } /* Counts the number of bits required for vli. */ static unsigned int _vli_num_bits(const uint64_t *vli, unsigned int ndigits) { unsigned int i, num_digits; uint64_t digit; num_digits = _vli_num_digits(vli, ndigits); if (num_digits == 0) return 0; digit = vli[num_digits - 1]; for (i = 0; digit; i++) digit >>= 1; return ((num_digits - 1) * 64 + i); } /* Computes result = product % mod, where product is 2N words long. * Currently only designed to work for curve_p or curve_n. */ void _vli_mmod_slow(uint64_t *result, const uint64_t *product, const uint64_t *mod, unsigned int ndigits) { uint64_t mod_m[2 * L_ECC_MAX_DIGITS]; uint64_t tmp[2 * L_ECC_MAX_DIGITS]; uint64_t *v[2] = { tmp, (uint64_t *) product }; uint64_t carry = 0; unsigned int i; /* Shift mod so its highest set bit is at the maximum position. */ int shift = (ndigits * 2 * 64) - _vli_num_bits(mod, ndigits); int word_shift = shift / 64; int bit_shift = shift % 64; vli_clear(mod_m, word_shift); if (bit_shift > 0) { for (i = 0; i < ndigits; ++i) { mod_m[word_shift + i] = (mod[i] << bit_shift) | carry; carry = mod[i] >> (64 - bit_shift); } } else vli_set(mod_m + word_shift, mod, ndigits); for (i = 1; shift >= 0; --shift) { uint64_t borrow = 0; unsigned int j; for (j = 0; j < ndigits * 2; ++j) { uint64_t diff = v[i][j] - mod_m[j] - borrow; if (diff != v[i][j]) borrow = (diff > v[i][j]); v[1 - i][j] = diff; } i = !(i ^ borrow); /* Swap the index if there was no borrow */ _vli_rshift1(mod_m, ndigits); mod_m[ndigits - 1] |= mod_m[ndigits] << (64 - 1); _vli_rshift1(mod_m + ndigits, ndigits); } vli_set(result, v[i], ndigits); } /* Computes p_result = p_product % curve_p. * See algorithm 5 and 6 from * http://www.isys.uni-klu.ac.at/PDF/2001-0126-MT.pdf */ static void vli_mmod_fast_192(uint64_t *result, const uint64_t *product, const uint64_t *curve_prime, uint64_t *tmp) { const unsigned int ndigits = 3; int carry; vli_set(result, product, ndigits); vli_set(tmp, &product[3], ndigits); carry = _vli_add(result, result, tmp, ndigits); tmp[0] = 0; tmp[1] = product[3]; tmp[2] = product[4]; carry += _vli_add(result, result, tmp, ndigits); tmp[0] = tmp[1] = product[5]; tmp[2] = 0; carry += _vli_add(result, result, tmp, ndigits); while (carry || _vli_cmp(curve_prime, result, ndigits) != 1) carry -= _vli_sub(result, result, curve_prime, ndigits); } /* Computes result = product % curve_prime * from http://www.nsa.gov/ia/_files/nist-routines.pdf */ static void vli_mmod_fast_224(uint64_t *result, const uint64_t *product, const uint64_t *curve_prime, uint64_t *tmp) { int carry = 0; const unsigned int ndigits = 4; /* t */ vli_set(result, product, ndigits); result[3] &= 0xffffffff; /* s1 */ tmp[0] = 0; tmp[1] = product[3] & 0xffffffff00000000ull; tmp[2] = product[4]; tmp[3] = product[5] & 0xffffffff; _vli_add(result, result, tmp, ndigits); /* s2 */ tmp[1] = product[5] & 0xffffffff00000000ull; tmp[2] = product[6]; tmp[3] = 0; _vli_add(result, result, tmp, ndigits); /* d1 */ tmp[0] = (product[3] >> 32) | (product[4] << 32); tmp[1] = (product[4] >> 32) | (product[5] << 32); tmp[2] = (product[5] >> 32) | (product[6] << 32); tmp[3] = product[6] >> 32; carry -= _vli_sub(result, result, tmp, ndigits); /* d2 */ tmp[0] = (product[5] >> 32) | (product[6] << 32); tmp[1] = product[6] >> 32; tmp[2] = tmp[3] = 0; carry -= _vli_sub(result, result, tmp, ndigits); if (carry < 0) { do { carry += _vli_add(result, result, curve_prime, ndigits); } while (carry < 0); } else { while (carry || _vli_cmp(curve_prime, result, ndigits) != 1) carry -= _vli_sub(result, result, curve_prime, ndigits); } } /* Computes result = product % curve_prime * from http://www.nsa.gov/ia/_files/nist-routines.pdf */ static void vli_mmod_fast_256(uint64_t *result, const uint64_t *product, const uint64_t *curve_prime, uint64_t *tmp) { int carry; const unsigned int ndigits = 4; /* t */ vli_set(result, product, ndigits); /* s1 */ tmp[0] = 0; tmp[1] = product[5] & 0xffffffff00000000ull; tmp[2] = product[6]; tmp[3] = product[7]; carry = vli_lshift(tmp, tmp, 1, ndigits); carry += _vli_add(result, result, tmp, ndigits); /* s2 */ tmp[1] = product[6] << 32; tmp[2] = (product[6] >> 32) | (product[7] << 32); tmp[3] = product[7] >> 32; carry += vli_lshift(tmp, tmp, 1, ndigits); carry += _vli_add(result, result, tmp, ndigits); /* s3 */ tmp[0] = product[4]; tmp[1] = product[5] & 0xffffffff; tmp[2] = 0; tmp[3] = product[7]; carry += _vli_add(result, result, tmp, ndigits); /* s4 */ tmp[0] = (product[4] >> 32) | (product[5] << 32); tmp[1] = (product[5] >> 32) | (product[6] & 0xffffffff00000000ull); tmp[2] = product[7]; tmp[3] = (product[6] >> 32) | (product[4] << 32); carry += _vli_add(result, result, tmp, ndigits); /* d1 */ tmp[0] = (product[5] >> 32) | (product[6] << 32); tmp[1] = (product[6] >> 32); tmp[2] = 0; tmp[3] = (product[4] & 0xffffffff) | (product[5] << 32); carry -= _vli_sub(result, result, tmp, ndigits); /* d2 */ tmp[0] = product[6]; tmp[1] = product[7]; tmp[2] = 0; tmp[3] = (product[4] >> 32) | (product[5] & 0xffffffff00000000ull); carry -= _vli_sub(result, result, tmp, ndigits); /* d3 */ tmp[0] = (product[6] >> 32) | (product[7] << 32); tmp[1] = (product[7] >> 32) | (product[4] << 32); tmp[2] = (product[4] >> 32) | (product[5] << 32); tmp[3] = (product[6] << 32); carry -= _vli_sub(result, result, tmp, ndigits); /* d4 */ tmp[0] = product[7]; tmp[1] = product[4] & 0xffffffff00000000ull; tmp[2] = product[5]; tmp[3] = product[6] & 0xffffffff00000000ull; carry -= _vli_sub(result, result, tmp, ndigits); if (carry < 0) { do { carry += _vli_add(result, result, curve_prime, ndigits); } while (carry < 0); } else { while (carry || _vli_cmp(curve_prime, result, ndigits) != 1) carry -= _vli_sub(result, result, curve_prime, ndigits); } } /* * The NIST algorithms define S values, which are comprised of 32 bit C values * of the original product we are trying to reduce. Since we are working with * 64 bit 'digits', we need to convert these C values into 64 bit chunks. This * macro mainly makes code readability easier since we can directly pass the * two C indexes (h and l). Some of these C values are zero, which is also a * value C index. In this case -1 should be passed to indicate zero. */ #define ECC_SET_S(prod, h, l) ({ \ uint64_t r = 0; \ if (h == -1) { \ /* zero, don't do anything */ \ } else if (h & 1) \ r |= (prod[h / 2] & 0xffffffff00000000ull); \ else \ r |= (prod[h / 2] << 32); \ if (l == -1) { \ /* zero, don't do anything */ \ } else if (l & 1) \ r |= (prod[l / 2] >> 32); \ else \ r |= (prod[l / 2] & 0xffffffff); \ r; \ }) static void vli_mmod_fast_384(uint64_t *result, const uint64_t *product, const uint64_t *curve_prime, uint64_t *tmp) { int carry; const unsigned int ndigits = 6; /* t */ vli_set(result, product, ndigits); /* s1 */ tmp[0] = 0; tmp[1] = 0; tmp[2] = ECC_SET_S(product, 22, 21); tmp[3] = ECC_SET_S(product, -1, 23); tmp[4] = 0; tmp[5] = 0; carry = vli_lshift(tmp, tmp, 1, ndigits); carry += _vli_add(result, result, tmp, ndigits); /* s2 */ tmp[0] = product[6]; tmp[1] = product[7]; tmp[2] = product[8]; tmp[3] = product[9]; tmp[4] = product[10]; tmp[5] = product[11]; carry += _vli_add(result, result, tmp, ndigits); /* s3 */ tmp[0] = ECC_SET_S(product, 22, 21); tmp[1] = ECC_SET_S(product, 12, 23); tmp[2] = ECC_SET_S(product, 14, 13); tmp[3] = ECC_SET_S(product, 16, 15); tmp[4] = ECC_SET_S(product, 18, 17); tmp[5] = ECC_SET_S(product, 20, 19); carry += _vli_add(result, result, tmp, ndigits); /* s4 */ tmp[0] = ECC_SET_S(product, 23, -1); tmp[1] = ECC_SET_S(product, 20, -1); tmp[2] = ECC_SET_S(product, 13, 12); tmp[3] = ECC_SET_S(product, 15, 14); tmp[4] = ECC_SET_S(product, 17, 16); tmp[5] = ECC_SET_S(product, 19, 18); carry += _vli_add(result, result, tmp, ndigits); /* s5 */ tmp[0] = 0; tmp[1] = 0; tmp[2] = ECC_SET_S(product, 21, 20); tmp[3] = ECC_SET_S(product, 23, 22); tmp[4] = 0; tmp[5] = 0; carry += _vli_add(result, result, tmp, ndigits); /* s6 */ tmp[0] = ECC_SET_S(product, -1, 20); tmp[1] = ECC_SET_S(product, 21, -1); tmp[2] = ECC_SET_S(product, 23, 22); tmp[3] = 0; tmp[4] = 0; tmp[5] = 0; carry += _vli_add(result, result, tmp, ndigits); /* s7 */ tmp[0] = ECC_SET_S(product, 12, 23); tmp[1] = ECC_SET_S(product, 14, 13); tmp[2] = ECC_SET_S(product, 16, 15); tmp[3] = ECC_SET_S(product, 18, 17); tmp[4] = ECC_SET_S(product, 20, 19); tmp[5] = ECC_SET_S(product, 22, 21); carry -= _vli_sub(result, result, tmp, ndigits); /* s8 */ tmp[0] = ECC_SET_S(product, 20, -1); tmp[1] = ECC_SET_S(product, 22, 21); tmp[2] = ECC_SET_S(product, -1, 23); tmp[3] = 0; tmp[4] = 0; tmp[5] = 0; carry -= _vli_sub(result, result, tmp, ndigits); /* s9 */ tmp[0] = 0; tmp[1] = ECC_SET_S(product, 23, -1); tmp[2] = ECC_SET_S(product, -1, 23); tmp[3] = 0; tmp[4] = 0; tmp[5] = 0; carry -= _vli_sub(result, result, tmp, ndigits); if (carry < 0) { do { carry += _vli_add(result, result, curve_prime, ndigits); } while (carry < 0); } else { while (carry || _vli_cmp(curve_prime, result, ndigits) != 1) carry -= _vli_sub(result, result, curve_prime, ndigits); } } /* Computes result = product % curve_prime * from http://www.nsa.gov/ia/_files/nist-routines.pdf */ static void vli_mmod_fast_521(uint64_t *result, const uint64_t *product, const uint64_t *curve_prime, uint64_t *tmp) { const unsigned int ndigits = 9; size_t i; /* Initialize result with lowest 521 bits from product */ vli_set(result, product, ndigits); result[8] &= 0x1ff; for (i = 0; i < ndigits; i++) tmp[i] = (product[8 + i] >> 9) | (product[9 + i] << 55); tmp[8] &= 0x1ff; _vli_mod_add(result, result, tmp, curve_prime, ndigits); } /* Computes result = product % curve_prime * from http://www.nsa.gov/ia/_files/nist-routines.pdf */ bool _vli_mmod_fast(uint64_t *result, const uint64_t *product, const uint64_t *curve_prime, unsigned int ndigits) { uint64_t tmp[2 * L_ECC_MAX_DIGITS]; unsigned int nbits = _vli_num_bits(curve_prime, ndigits); switch (nbits) { case 192: vli_mmod_fast_192(result, product, curve_prime, tmp); break; case 224: vli_mmod_fast_224(result, product, curve_prime, tmp); break; case 256: vli_mmod_fast_256(result, product, curve_prime, tmp); break; case 384: vli_mmod_fast_384(result, product, curve_prime, tmp); break; case 521: vli_mmod_fast_521(result, product, curve_prime, tmp); break; default: return false; } return true; } /* Computes result = (left * right) % curve_p. */ void _vli_mod_mult_fast(uint64_t *result, const uint64_t *left, const uint64_t *right, const uint64_t *curve_prime, unsigned int ndigits) { uint64_t product[2 * L_ECC_MAX_DIGITS]; vli_mult(product, left, right, ndigits); _vli_mmod_fast(result, product, curve_prime, ndigits); } /* Computes result = left^2 % curve_p. */ void _vli_mod_square_fast(uint64_t *result, const uint64_t *left, const uint64_t *curve_prime, unsigned int ndigits) { uint64_t product[2 * L_ECC_MAX_DIGITS]; vli_square(product, left, ndigits); _vli_mmod_fast(result, product, curve_prime, ndigits); } #define EVEN(vli) (!(vli[0] & 1)) /* Computes result = (1 / p_input) % mod. All VLIs are the same size. * See "From Euclid's GCD to Montgomery Multiplication to the Great Divide" * https://labs.oracle.com/techrep/2001/smli_tr-2001-95.pdf */ void _vli_mod_inv(uint64_t *result, const uint64_t *input, const uint64_t *mod, unsigned int ndigits) { uint64_t a[L_ECC_MAX_DIGITS], b[L_ECC_MAX_DIGITS]; uint64_t u[L_ECC_MAX_DIGITS], v[L_ECC_MAX_DIGITS]; uint64_t carry; int cmp_result; if (vli_is_zero(input, ndigits)) { vli_clear(result, ndigits); return; } vli_set(a, input, ndigits); vli_set(b, mod, ndigits); vli_clear(u, ndigits); u[0] = 1; vli_clear(v, ndigits); while ((cmp_result = _vli_cmp(a, b, ndigits)) != 0) { carry = 0; if (EVEN(a)) { _vli_rshift1(a, ndigits); if (!EVEN(u)) carry = _vli_add(u, u, mod, ndigits); _vli_rshift1(u, ndigits); if (carry) u[ndigits - 1] |= 0x8000000000000000ull; } else if (EVEN(b)) { _vli_rshift1(b, ndigits); if (!EVEN(v)) carry = _vli_add(v, v, mod, ndigits); _vli_rshift1(v, ndigits); if (carry) v[ndigits - 1] |= 0x8000000000000000ull; } else if (cmp_result > 0) { _vli_sub(a, a, b, ndigits); _vli_rshift1(a, ndigits); if (_vli_cmp(u, v, ndigits) < 0) _vli_add(u, u, mod, ndigits); _vli_sub(u, u, v, ndigits); if (!EVEN(u)) carry = _vli_add(u, u, mod, ndigits); _vli_rshift1(u, ndigits); if (carry) u[ndigits - 1] |= 0x8000000000000000ull; } else { _vli_sub(b, b, a, ndigits); _vli_rshift1(b, ndigits); if (_vli_cmp(v, u, ndigits) < 0) _vli_add(v, v, mod, ndigits); _vli_sub(v, v, u, ndigits); if (!EVEN(v)) carry = _vli_add(v, v, mod, ndigits); _vli_rshift1(v, ndigits); if (carry) v[ndigits - 1] |= 0x8000000000000000ull; } } vli_set(result, u, ndigits); } /* ------ Point operations ------ */ /* Point multiplication algorithm using Montgomery's ladder with co-Z * coordinates. From http://eprint.iacr.org/2011/338.pdf */ /* Double in place */ static void ecc_point_double_jacobian(uint64_t *x1, uint64_t *y1, uint64_t *z1, const uint64_t *curve_prime, unsigned int ndigits) { /* t1 = x, t2 = y, t3 = z */ uint64_t t4[L_ECC_MAX_DIGITS]; uint64_t t5[L_ECC_MAX_DIGITS]; if (vli_is_zero(z1, ndigits)) return; /* t4 = y1^2 */ _vli_mod_square_fast(t4, y1, curve_prime, ndigits); /* t5 = x1*y1^2 = A */ _vli_mod_mult_fast(t5, x1, t4, curve_prime, ndigits); /* t4 = y1^4 */ _vli_mod_square_fast(t4, t4, curve_prime, ndigits); /* t2 = y1*z1 = z3 */ _vli_mod_mult_fast(y1, y1, z1, curve_prime, ndigits); /* t3 = z1^2 */ _vli_mod_square_fast(z1, z1, curve_prime, ndigits); /* t1 = x1 + z1^2 */ _vli_mod_add(x1, x1, z1, curve_prime, ndigits); /* t3 = 2*z1^2 */ _vli_mod_add(z1, z1, z1, curve_prime, ndigits); /* t3 = x1 - z1^2 */ _vli_mod_sub(z1, x1, z1, curve_prime, ndigits); /* t1 = x1^2 - z1^4 */ _vli_mod_mult_fast(x1, x1, z1, curve_prime, ndigits); /* t3 = 2*(x1^2 - z1^4) */ _vli_mod_add(z1, x1, x1, curve_prime, ndigits); /* t1 = 3*(x1^2 - z1^4) */ _vli_mod_add(x1, x1, z1, curve_prime, ndigits); if (vli_test_bit(x1, 0)) { uint64_t carry = _vli_add(x1, x1, curve_prime, ndigits); _vli_rshift1(x1, ndigits); x1[ndigits - 1] |= carry << 63; } else { _vli_rshift1(x1, ndigits); } /* t1 = 3/2*(x1^2 - z1^4) = B */ /* t3 = B^2 */ _vli_mod_square_fast(z1, x1, curve_prime, ndigits); /* t3 = B^2 - A */ _vli_mod_sub(z1, z1, t5, curve_prime, ndigits); /* t3 = B^2 - 2A = x3 */ _vli_mod_sub(z1, z1, t5, curve_prime, ndigits); /* t5 = A - x3 */ _vli_mod_sub(t5, t5, z1, curve_prime, ndigits); /* t1 = B * (A - x3) */ _vli_mod_mult_fast(x1, x1, t5, curve_prime, ndigits); /* t4 = B * (A - x3) - y1^4 = y3 */ _vli_mod_sub(t4, x1, t4, curve_prime, ndigits); vli_set(x1, z1, ndigits); vli_set(z1, y1, ndigits); vli_set(y1, t4, ndigits); } /* Modify (x1, y1) => (x1 * z^2, y1 * z^3) */ static void apply_z(uint64_t *x1, uint64_t *y1, uint64_t *z, const uint64_t *curve_prime, unsigned int ndigits) { uint64_t t1[L_ECC_MAX_DIGITS]; _vli_mod_square_fast(t1, z, curve_prime, ndigits); /* z^2 */ _vli_mod_mult_fast(x1, x1, t1, curve_prime, ndigits); /* x1 * z^2 */ _vli_mod_mult_fast(t1, t1, z, curve_prime, ndigits); /* z^3 */ _vli_mod_mult_fast(y1, y1, t1, curve_prime, ndigits); /* y1 * z^3 */ } /* P = (x1, y1) => 2P, (x2, y2) => P' */ static void xycz_initial_double(uint64_t *x1, uint64_t *y1, uint64_t *x2, uint64_t *y2, uint64_t *p_initial_z, const uint64_t *curve_prime, unsigned int ndigits) { uint64_t z[L_ECC_MAX_DIGITS]; vli_set(x2, x1, ndigits); vli_set(y2, y1, ndigits); vli_clear(z, ndigits); z[0] = 1; if (p_initial_z) vli_set(z, p_initial_z, ndigits); apply_z(x1, y1, z, curve_prime, ndigits); ecc_point_double_jacobian(x1, y1, z, curve_prime, ndigits); apply_z(x2, y2, z, curve_prime, ndigits); } /* Input P = (x1, y1, Z), Q = (x2, y2, Z) * Output P' = (x1', y1', Z3), P + Q = (x3, y3, Z3) * or P => P', Q => P + Q */ static void xycz_add(uint64_t *x1, uint64_t *y1, uint64_t *x2, uint64_t *y2, const uint64_t *curve_prime, unsigned int ndigits) { /* t1 = X1, t2 = Y1, t3 = X2, t4 = Y2 */ uint64_t t5[L_ECC_MAX_DIGITS]; /* t5 = x2 - x1 */ _vli_mod_sub(t5, x2, x1, curve_prime, ndigits); /* t5 = (x2 - x1)^2 = A */ _vli_mod_square_fast(t5, t5, curve_prime, ndigits); /* t1 = x1*A = B */ _vli_mod_mult_fast(x1, x1, t5, curve_prime, ndigits); /* t3 = x2*A = C */ _vli_mod_mult_fast(x2, x2, t5, curve_prime, ndigits); /* t4 = y2 - y1 */ _vli_mod_sub(y2, y2, y1, curve_prime, ndigits); /* t5 = (y2 - y1)^2 = D */ _vli_mod_square_fast(t5, y2, curve_prime, ndigits); /* t5 = D - B */ _vli_mod_sub(t5, t5, x1, curve_prime, ndigits); /* t5 = D - B - C = x3 */ _vli_mod_sub(t5, t5, x2, curve_prime, ndigits); /* t3 = C - B */ _vli_mod_sub(x2, x2, x1, curve_prime, ndigits); /* t2 = y1*(C - B) */ _vli_mod_mult_fast(y1, y1, x2, curve_prime, ndigits); /* t3 = B - x3 */ _vli_mod_sub(x2, x1, t5, curve_prime, ndigits); /* t4 = (y2 - y1)*(B - x3) */ _vli_mod_mult_fast(y2, y2, x2, curve_prime, ndigits); /* t4 = y3 */ _vli_mod_sub(y2, y2, y1, curve_prime, ndigits); vli_set(x2, t5, ndigits); } /* Input P = (x1, y1, Z), Q = (x2, y2, Z) * Output P + Q = (x3, y3, Z3), P - Q = (x3', y3', Z3) * or P => P - Q, Q => P + Q */ static void xycz_add_c(uint64_t *x1, uint64_t *y1, uint64_t *x2, uint64_t *y2, const uint64_t *curve_prime, unsigned int ndigits) { /* t1 = X1, t2 = Y1, t3 = X2, t4 = Y2 */ uint64_t t5[L_ECC_MAX_DIGITS]; uint64_t t6[L_ECC_MAX_DIGITS]; uint64_t t7[L_ECC_MAX_DIGITS]; /* t5 = x2 - x1 */ _vli_mod_sub(t5, x2, x1, curve_prime, ndigits); /* t5 = (x2 - x1)^2 = A */ _vli_mod_square_fast(t5, t5, curve_prime, ndigits); /* t1 = x1*A = B */ _vli_mod_mult_fast(x1, x1, t5, curve_prime, ndigits); /* t3 = x2*A = C */ _vli_mod_mult_fast(x2, x2, t5, curve_prime, ndigits); /* t4 = y2 + y1 */ _vli_mod_add(t5, y2, y1, curve_prime, ndigits); /* t4 = y2 - y1 */ _vli_mod_sub(y2, y2, y1, curve_prime, ndigits); /* t6 = C - B */ _vli_mod_sub(t6, x2, x1, curve_prime, ndigits); /* t2 = y1 * (C - B) */ _vli_mod_mult_fast(y1, y1, t6, curve_prime, ndigits); /* t6 = B + C */ _vli_mod_add(t6, x1, x2, curve_prime, ndigits); /* t3 = (y2 - y1)^2 */ _vli_mod_square_fast(x2, y2, curve_prime, ndigits); /* t3 = x3 */ _vli_mod_sub(x2, x2, t6, curve_prime, ndigits); /* t7 = B - x3 */ _vli_mod_sub(t7, x1, x2, curve_prime, ndigits); /* t4 = (y2 - y1)*(B - x3) */ _vli_mod_mult_fast(y2, y2, t7, curve_prime, ndigits); /* t4 = y3 */ _vli_mod_sub(y2, y2, y1, curve_prime, ndigits); /* t7 = (y2 + y1)^2 = F */ _vli_mod_square_fast(t7, t5, curve_prime, ndigits); /* t7 = x3' */ _vli_mod_sub(t7, t7, t6, curve_prime, ndigits); /* t6 = x3' - B */ _vli_mod_sub(t6, t7, x1, curve_prime, ndigits); /* t6 = (y2 + y1)*(x3' - B) */ _vli_mod_mult_fast(t6, t6, t5, curve_prime, ndigits); /* t2 = y3' */ _vli_mod_sub(y1, t6, y1, curve_prime, ndigits); vli_set(x1, t7, ndigits); } void _ecc_point_mult(struct l_ecc_point *result, const struct l_ecc_point *point, const uint64_t *scalar, uint64_t *initial_z, const uint64_t *curve_prime) { /* R0 and R1 */ const struct l_ecc_curve *curve = point->curve; uint64_t rx[2][L_ECC_MAX_DIGITS]; uint64_t ry[2][L_ECC_MAX_DIGITS]; uint64_t z[L_ECC_MAX_DIGITS]; uint64_t sk[2][L_ECC_MAX_DIGITS]; int i, nb; unsigned int ndigits = curve->ndigits; unsigned int nbits = _vli_num_bits(curve->n, ndigits); int num_bits; int carry; carry = _vli_add(sk[0], scalar, curve->n, ndigits); _vli_add(sk[1], sk[0], curve->n, ndigits); scalar = sk[!carry]; /* secp224r1 and secp521r1 curves */ if (nbits == 224 || nbits == 521) num_bits = nbits + 2; else num_bits = sizeof(uint64_t) * ndigits * 8 + 1; vli_set(rx[1], point->x, ndigits); vli_set(ry[1], point->y, ndigits); xycz_initial_double(rx[1], ry[1], rx[0], ry[0], initial_z, curve_prime, ndigits); for (i = num_bits - 2; i > 0; i--) { nb = !vli_test_bit(scalar, i); xycz_add_c(rx[1 - nb], ry[1 - nb], rx[nb], ry[nb], curve_prime, ndigits); xycz_add(rx[nb], ry[nb], rx[1 - nb], ry[1 - nb], curve_prime, ndigits); } nb = !vli_test_bit(scalar, 0); xycz_add_c(rx[1 - nb], ry[1 - nb], rx[nb], ry[nb], curve_prime, ndigits); /* Find final 1/Z value. */ /* X1 - X0 */ _vli_mod_sub(z, rx[1], rx[0], curve_prime, ndigits); /* Yb * (X1 - X0) */ _vli_mod_mult_fast(z, z, ry[1 - nb], curve_prime, ndigits); /* xP * Yb * (X1 - X0) */ _vli_mod_mult_fast(z, z, point->x, curve_prime, ndigits); /* 1 / (xP * Yb * (X1 - X0)) */ _vli_mod_inv(z, z, curve_prime, ndigits); /* yP / (xP * Yb * (X1 - X0)) */ _vli_mod_mult_fast(z, z, point->y, curve_prime, ndigits); /* Xb * yP / (xP * Yb * (X1 - X0)) */ _vli_mod_mult_fast(z, z, rx[1 - nb], curve_prime, ndigits); /* End 1/Z calculation */ xycz_add(rx[nb], ry[nb], rx[1 - nb], ry[1 - nb], curve_prime, ndigits); apply_z(rx[0], ry[0], z, curve_prime, ndigits); vli_set(result->x, rx[0], ndigits); vli_set(result->y, ry[0], ndigits); } /* Returns true if p_point is the point at infinity, false otherwise. */ bool _ecc_point_is_zero(const struct l_ecc_point *point) { return (vli_is_zero(point->x, point->curve->ndigits) && vli_is_zero(point->y, point->curve->ndigits)); } bluez-5.82/ell/PaxHeaders/pem.c0000644000000000000000000000005014567163422013361 xustar0020 atime=1743575482 20 ctime=1743591276 bluez-5.82/ell/pem.c0000644000000000000000000004363014567163422013050 0ustar00rootroot/* * Embedded Linux library * Copyright (C) 2015 Intel Corporation * * SPDX-License-Identifier: LGPL-2.1-or-later */ #ifdef HAVE_CONFIG_H #include #endif #define _GNU_SOURCE #include #include #include #include #include #include "useful.h" #include "private.h" #include "key.h" #include "cert.h" #include "queue.h" #include "pem.h" #include "base64.h" #include "utf8.h" #include "asn1-private.h" #include "cipher.h" #include "cert-private.h" #include "missing.h" #include "pem-private.h" #define PEM_START_BOUNDARY "-----BEGIN " #define PEM_END_BOUNDARY "-----END " static const char *is_start_boundary(const void *buf, size_t buf_len, size_t *label_len) { const char *start, *end, *ptr; int prev_special, special; const char *buf_ptr = buf; if (buf_len < strlen(PEM_START_BOUNDARY)) return NULL; /* Check we have a "-----BEGIN " (RFC7468 section 2) */ if (memcmp(buf, PEM_START_BOUNDARY, strlen(PEM_START_BOUNDARY))) return NULL; /* * Check we have a string of printable characters in which no * two consecutive characters are "special" nor is the first or the * final character "special". These special characters are space * and hyphen. (RFC7468 section 3) * The loop will end on the second hyphen of the final "-----" if * no error found earlier. */ start = buf + strlen(PEM_START_BOUNDARY); end = start; prev_special = 1; while (end < buf_ptr + buf_len && l_ascii_isprint(*end)) { special = *end == ' ' || *end == '-'; if (prev_special && special) break; end++; prev_special = special; } /* Rewind to the first '-', but handle empty labels */ if (end != start) end--; /* Check we have a "-----" (RFC7468 section 2) */ if (end + 5 > buf_ptr + buf_len || memcmp(end, "-----", 5)) return NULL; /* Check all remaining characters are horizontal whitespace (WSP) */ for (ptr = end + 5; ptr < buf_ptr + buf_len; ptr++) if (*ptr != ' ' && *ptr != '\t') return NULL; *label_len = end - start; return start; } static bool is_end_boundary(const void *buf, size_t buf_len, const char *label, size_t label_len) { const char *buf_ptr = buf; size_t len = strlen(PEM_END_BOUNDARY) + label_len + 5; if (buf_len < len) return false; if (memcmp(buf_ptr, PEM_END_BOUNDARY, strlen(PEM_END_BOUNDARY)) || memcmp(buf_ptr + strlen(PEM_END_BOUNDARY), label, label_len) || memcmp(buf_ptr + (len - 5), "-----", 5)) return false; /* Check all remaining characters are horizontal whitespace (WSP) */ for (; len < buf_len; len++) if (buf_ptr[len] != ' ' && buf_ptr[len] != '\t') return false; return true; } const char *pem_next(const void *buf, size_t buf_len, char **type_label, size_t *base64_len, const char **endp, bool strict) { const char *buf_ptr = buf; const char *base64_data = NULL, *eol; const char *label = NULL; size_t label_len = 0; const char *start = NULL; /* * The base64 parser uses the RFC7468 laxbase64text grammar but we * do full checks on the encapsulation boundary lines, i.e. no * leading spaces allowed, making sure quoted text and similar * are not confused for actual PEM "textual encoding". */ while (buf_len) { for (eol = buf_ptr; eol < buf_ptr + buf_len; eol++) if (*eol == '\r' || *eol == '\n') break; if (!base64_data) { label = is_start_boundary(buf_ptr, eol - buf_ptr, &label_len); if (label) { start = label - strlen("-----BEGIN "); base64_data = eol; } else if (strict) break; } else if (start && is_end_boundary(buf_ptr, eol - buf_ptr, label, label_len)) { if (type_label) *type_label = l_strndup(label, label_len); if (base64_len) *base64_len = buf_ptr - base64_data; if (endp) { if (eol == buf_ptr + buf_len) *endp = eol; else *endp = eol + 1; } return base64_data; } if (eol == buf_ptr + buf_len) break; buf_len -= eol + 1 - buf_ptr; buf_ptr = eol + 1; if (buf_len && *eol == '\r' && *buf_ptr == '\n') { buf_ptr++; buf_len--; } } /* If we found no label signal EOF rather than parse error */ if (!base64_data && endp) *endp = NULL; return NULL; } uint8_t *pem_load_buffer(const void *buf, size_t buf_len, char **out_type_label, size_t *out_len, char **out_headers, const char **out_endp) { size_t base64_len; const char *base64; char *label; const char *headers = NULL; size_t headers_len; uint8_t *ret; base64 = pem_next(buf, buf_len, &label, &base64_len, out_endp, false); if (!base64) return NULL; if (memchr(base64, ':', base64_len)) { const char *start; const char *end; while (base64_len && l_ascii_isspace(*base64)) { base64++; base64_len--; } start = base64; if (!(end = memmem(start, base64_len, "\n\n", 2)) && !(end = memmem(start, base64_len, "\n\r\n", 3))) goto err; /* Check that each header line has a key and a colon */ while (start < end) { const char *lf = rawmemchr(start, '\n'); const char *colon = memchr(start, ':', lf - start); if (!colon) goto err; for (; start < colon; start++) if (l_ascii_isalnum(*start)) break; if (start == colon) goto err; start = lf + 1; } headers = base64; headers_len = end - base64; base64_len -= headers_len + 2; base64 = end + 2; } ret = l_base64_decode(base64, base64_len, out_len); if (ret) { *out_type_label = label; if (out_headers) { if (headers) *out_headers = l_strndup(headers, headers_len); else *out_headers = NULL; } return ret; } err: l_free(label); return NULL; } LIB_EXPORT uint8_t *l_pem_load_buffer(const void *buf, size_t buf_len, char **type_label, size_t *out_len) { return pem_load_buffer(buf, buf_len, type_label, out_len, NULL, NULL); } int pem_file_open(struct pem_file_info *info, const char *filename) { info->fd = open(filename, O_RDONLY); if (info->fd < 0) return -errno; if (fstat(info->fd, &info->st) < 0) { int r = -errno; close(info->fd); return r; } info->data = mmap(NULL, info->st.st_size, PROT_READ, MAP_SHARED, info->fd, 0); if (info->data == MAP_FAILED) { int r = -errno; close(info->fd); return r; } return 0; } void pem_file_close(struct pem_file_info *info) { munmap(info->data, info->st.st_size); close(info->fd); } static uint8_t *pem_load_file(const char *filename, char **out_type_label, size_t *out_len, char **out_headers) { struct pem_file_info file; uint8_t *result; if (unlikely(!filename)) return NULL; if (pem_file_open(&file, filename) < 0) return NULL; result = pem_load_buffer(file.data, file.st.st_size, out_type_label, out_len, out_headers, NULL); pem_file_close(&file); return result; } LIB_EXPORT uint8_t *l_pem_load_file(const char *filename, char **out_type_label, size_t *out_len) { return pem_load_file(filename, out_type_label, out_len, NULL); } static struct l_certchain *pem_list_to_chain(struct l_queue *list) { struct l_certchain *chain; if (!list) return NULL; chain = certchain_new_from_leaf(l_queue_pop_head(list)); while (!l_queue_isempty(list)) certchain_link_issuer(chain, l_queue_pop_head(list)); l_queue_destroy(list, NULL); return chain; } LIB_EXPORT struct l_certchain *l_pem_load_certificate_chain_from_data( const void *buf, size_t len) { struct l_queue *list = l_pem_load_certificate_list_from_data(buf, len); if (!list) return NULL; return pem_list_to_chain(list); } LIB_EXPORT struct l_certchain *l_pem_load_certificate_chain( const char *filename) { struct l_queue *list = l_pem_load_certificate_list(filename); if (!list) return NULL; return pem_list_to_chain(list); } static bool pem_write_one_cert(struct l_cert *cert, void *user_data) { int *fd = user_data; const uint8_t *der; size_t der_len; struct iovec iov[3]; ssize_t r; der = l_cert_get_der_data(cert, &der_len); iov[0].iov_base = "-----BEGIN CERTIFICATE-----\n"; iov[0].iov_len = strlen(iov[0].iov_base); iov[1].iov_base = l_base64_encode(der, der_len, 64); iov[1].iov_len = strlen(iov[1].iov_base); iov[2].iov_base = "\n-----END CERTIFICATE-----\n"; iov[2].iov_len = strlen(iov[2].iov_base); r = L_TFR(writev(*fd, iov, 3)); l_free(iov[1].iov_base); if (r == (ssize_t) (iov[0].iov_len + iov[1].iov_len + iov[2].iov_len)) return false; if (r < 0) *fd = -errno; else *fd = -EIO; return true; } int pem_write_certificate_chain(const struct l_certchain *chain, const char *filename) { int fd = L_TFR(open(filename, O_CREAT | O_WRONLY | O_CLOEXEC, 0600)); int err = fd; if (err < 0) return -errno; l_certchain_walk_from_leaf((struct l_certchain *) chain, pem_write_one_cert, &err); close(fd); return err < 0 ? err : 0; } LIB_EXPORT struct l_queue *l_pem_load_certificate_list_from_data( const void *buf, size_t len) { const char *ptr, *end; struct l_queue *list = NULL; ptr = buf; end = buf + len; while (ptr && ptr < end) { uint8_t *der; size_t der_len; char *label = NULL; struct l_cert *cert; const char *base64; size_t base64_len; bool is_certificate; base64 = pem_next(ptr, end - ptr, &label, &base64_len, &ptr, false); if (!base64) { if (!ptr) break; /* if ptr was not reset to NULL; parse error */ goto error; } is_certificate = !strcmp(label, "CERTIFICATE"); l_free(label); if (!is_certificate) goto error; der = l_base64_decode(base64, base64_len, &der_len); if (!der) goto error; cert = l_cert_new_from_der(der, der_len); l_free(der); if (!cert) goto error; if (!list) list = l_queue_new(); l_queue_push_tail(list, cert); } return list; error: l_queue_destroy(list, (l_queue_destroy_func_t) l_cert_free); return NULL; } LIB_EXPORT struct l_queue *l_pem_load_certificate_list(const char *filename) { struct pem_file_info file; struct l_queue *list = NULL; if (unlikely(!filename)) return NULL; if (pem_file_open(&file, filename) < 0) return NULL; list = l_pem_load_certificate_list_from_data(file.data, file.st.st_size); pem_file_close(&file); return list; } #define SKIP_WHITESPACE(str) \ while (l_ascii_isspace(*(str))) \ (str)++; static const char *parse_rfc1421_dek_info(char *headers, const char **out_params) { const char *proc_type = NULL; char *dek_info = NULL; char *comma; while (headers) { char *lf = strchrnul(headers, '\n'); char *key; key = headers; SKIP_WHITESPACE(key); headers = (*lf == '\n') ? lf + 1 : NULL; if (!memcmp(key, "X-", 2)) key += 2; if (!memcmp(key, "Proc-Type:", 10)) { if (proc_type) return NULL; proc_type = key + 10; SKIP_WHITESPACE(proc_type); } else if (!memcmp(key, "DEK-Info:", 9)) { if (dek_info) return NULL; dek_info = key + 9; SKIP_WHITESPACE(dek_info); } else continue; while (l_ascii_isspace(lf[-1])) lf--; *lf = '\0'; } if (!proc_type || !dek_info) return NULL; /* Skip the version field (should be 3 or 4) */ proc_type = strchr(proc_type, ','); if (!proc_type) return NULL; proc_type++; SKIP_WHITESPACE(proc_type); /* Section 4.6.1.1 */ if (strcmp(proc_type, "ENCRYPTED")) return NULL; comma = strchr(dek_info, ','); if (comma) { *out_params = comma + 1; SKIP_WHITESPACE(*out_params); while (comma > dek_info && l_ascii_isspace(comma[-1])) comma--; *comma = '\0'; } else *out_params = NULL; return dek_info; } static struct l_cipher *cipher_from_dek_info(const char *algid, const char *params, const char *passphrase, size_t *block_len) { enum l_cipher_type type; struct l_cipher *cipher; struct l_checksum *md5; uint8_t key[32]; size_t key_len; bool ok; L_AUTO_FREE_VAR(uint8_t *, iv) = NULL; size_t iv_len; if (!strcmp(algid, "DES-CBC")) { type = L_CIPHER_DES_CBC; key_len = 8; iv_len = 8; } else if (!strcmp(algid, "DES-EDE3-CBC")) { type = L_CIPHER_DES3_EDE_CBC; key_len = 24; iv_len = 8; } else if (!strcmp(algid, "AES-128-CBC")) { type = L_CIPHER_AES_CBC; key_len = 16; iv_len = 16; } else if (!strcmp(algid, "AES-192-CBC")) { type = L_CIPHER_AES_CBC; key_len = 24; iv_len = 16; } else if (!strcmp(algid, "AES-256-CBC")) { type = L_CIPHER_AES_CBC; key_len = 32; iv_len = 16; } else return NULL; if (!params || strlen(params) != 2 * iv_len) return NULL; *block_len = iv_len; iv = l_util_from_hexstring(params, &iv_len); if (!iv) return NULL; /* * The encryption key is the MD5(password | IV[:8]), this comes from * opessl's crypto/evp/evp_key.c:EVP_BytesToKey() and doesn't seem to * be backed by any standard: * https://web.archive.org/web/20190528100132/https://latacora.singles/2018/08/03/the-default-openssh.html */ md5 = l_checksum_new(L_CHECKSUM_MD5); if (!md5) return NULL; ok = l_checksum_update(md5, passphrase, strlen(passphrase)) && l_checksum_update(md5, iv, 8) && l_checksum_get_digest(md5, key, 16) == 16; if (ok && key_len > 16) { l_checksum_reset(md5); ok = l_checksum_update(md5, key, 16) && l_checksum_update(md5, passphrase, strlen(passphrase)) && l_checksum_update(md5, iv, 8) && l_checksum_get_digest(md5, key + 16, 16) == 16; } l_checksum_free(md5); if (!ok) { cipher = NULL; goto cleanup; } cipher = l_cipher_new(type, key, key_len); if (!cipher) goto cleanup; if (l_cipher_set_iv(cipher, iv, iv_len)) goto cleanup; l_cipher_free(cipher); cipher = NULL; cleanup: explicit_bzero(key, sizeof(key)); return cipher; } struct l_key *pem_load_private_key(uint8_t *content, size_t len, char *label, const char *passphrase, char *headers, bool *encrypted) { struct l_key *pkey; /* * RFC7468 Section 10-compatible unencrypted private key label * (also mentioned in PKCS#8/RFC5958 Section 5), encodes * the PKCS#8/RFC5958 PrivateKeyInfo structure -- supported * directly by the pkcs8-key-parser kernel module. */ if (!strcmp(label, "PRIVATE KEY")) { /* RFC822 Headers explicitly disallowed in RFC7468 */ if (headers) goto err; pkey = cert_key_from_pkcs8_private_key_info(content, len); goto done; } /* * RFC7468 Section 11-compatible encrypted private key label * (also mentioned in PKCS#8/RFC5958 Section 5), encodes * the PKCS#8/RFC5958 EncryptedPrivateKeyInfo structure. We * decrypt it into a plain PrivateKeyInfo for the * pkcs8-key-parser module. */ if (!strcmp(label, "ENCRYPTED PRIVATE KEY")) { if (encrypted) *encrypted = true; if (!passphrase) goto err; /* RFC822 Headers explicitly disallowed in RFC7468 */ if (headers) goto err; pkey = cert_key_from_pkcs8_encrypted_private_key_info(content, len, passphrase); goto done; } /* * Legacy RSA private key label aka. SSLeay format, understood by * most software but not documented in an RFC. Encodes the * PKCS#1/RFC8017 RSAPrivateKey structure. We wrap it in a PKCS#8 * PrivateKeyInfo for the pkcs8-key-parser module. */ if (!strcmp(label, "RSA PRIVATE KEY")) { const char *dekalgid; const char *dekparameters; /* * "openssl rsa ..." can produce encrypted PKCS#1-formatted * keys. These are incompatible with RFC7468 parsing because * of the RFC822 headers present but the format is the same * as documented in RFC1421. The encryption algorithms are * supposed to be the ones defined in RFC1423 but that would * be only DES-CBC while openssl allows other algorithms. * When decrypted we get the RSAPrivateKey struct and proceed * like with the unencrypted format. */ dekalgid = parse_rfc1421_dek_info(headers, &dekparameters); if (dekalgid) { struct l_cipher *alg; bool r; size_t block_len; uint8_t pad; if (encrypted) *encrypted = true; if (!passphrase) goto err; alg = cipher_from_dek_info(dekalgid, dekparameters, passphrase, &block_len); if (!alg) goto err; if (len % block_len || !len) { l_cipher_free(alg); goto err; } r = l_cipher_decrypt(alg, content, content, len); l_cipher_free(alg); if (!r) goto err; /* Remove padding like in RFC1423 Section 1.1 */ pad = content[len - 1]; if (pad > block_len || pad == 0) goto err; if (!l_secure_memeq(content + len - pad, pad - 1U, pad)) goto err; len -= pad; } pkey = cert_key_from_pkcs1_rsa_private_key(content, len); goto done; } /* Label not known */ err: pkey = NULL; done: explicit_bzero(content, len); l_free(content); l_free(label); l_free(headers); return pkey; } LIB_EXPORT struct l_key *l_pem_load_private_key_from_data(const void *buf, size_t buf_len, const char *passphrase, bool *encrypted) { uint8_t *content; char *label; size_t len; char *headers; if (encrypted) *encrypted = false; content = pem_load_buffer(buf, buf_len, &label, &len, &headers, NULL); if (!content) return NULL; return pem_load_private_key(content, len, label, passphrase, headers, encrypted); } /** * l_pem_load_private_key * @filename: path string to the PEM file to load * @passphrase: private key encryption passphrase or NULL for unencrypted * @encrypted: receives indication whether the file was encrypted if non-NULL * * Load the PEM encoded RSA Private Key file at @filename. If it is an * encrypted private key and @passphrase was non-NULL, the file is * decrypted. If it's unencrypted @passphrase is ignored. @encrypted * stores information of whether the file was encrypted, both in a * success case and on error when NULL is returned. This can be used to * check if a passphrase is required without prior information. * * The passphrase, if given, must have been validated as UTF-8 unless the * caller knows that PKCS#12 encryption algorithms are not used. * Use l_utf8_validate. * * Returns: An l_key object to be freed with an l_key_free* function, * or NULL. **/ LIB_EXPORT struct l_key *l_pem_load_private_key(const char *filename, const char *passphrase, bool *encrypted) { uint8_t *content; char *label; size_t len; char *headers; if (encrypted) *encrypted = false; content = pem_load_file(filename, &label, &len, &headers); if (!content) return NULL; return pem_load_private_key(content, len, label, passphrase, headers, encrypted); } bluez-5.82/ell/PaxHeaders/string.c0000644000000000000000000000005014661631567014113 xustar0020 atime=1743575477 20 ctime=1743591276 bluez-5.82/ell/string.c0000644000000000000000000002224014661631567013574 0ustar00rootroot/* * Embedded Linux library * Copyright (C) 2011-2014 Intel Corporation * * SPDX-License-Identifier: LGPL-2.1-or-later */ #ifdef HAVE_CONFIG_H #include #endif #include #include "strv.h" #include "string.h" #include "private.h" #include "useful.h" /** * SECTION:string * @short_description: Growable string buffer * * Growable string buffer support */ /** * l_string: * * Opaque object representing the string buffer. */ struct l_string { size_t max; size_t len; char *str; }; static void grow_string(struct l_string *str, size_t extra) { if (str->len + extra < str->max) return; str->max = str->len + extra + 1; if (str->max < l_util_pagesize()) str->max = roundup_pow_of_two(str->max); else str->max = align_len(str->max, l_util_pagesize()); str->str = l_realloc(str->str, str->max); } /** * l_string_new: * @initial_length: Initial length of the groable string * * Create new growable string. If the @initial_length is 0, then a safe * default is chosen. * * Returns: a newly allocated #l_string object. **/ LIB_EXPORT struct l_string *l_string_new(size_t initial_length) { static const size_t DEFAULT_INITIAL_LENGTH = 127; struct l_string *ret; ret = l_new(struct l_string, 1); if (initial_length == 0) initial_length = DEFAULT_INITIAL_LENGTH; grow_string(ret, initial_length); ret->str[0] = '\0'; return ret; } /** * l_string_free: * @string: growable string object * * Free the growable string object and all associated data **/ LIB_EXPORT void l_string_free(struct l_string *string) { if (unlikely(!string)) return; l_free(string->str); l_free(string); } /** * l_string_unwrap: * @string: growable string object * * Free the growable string object and return the internal string data. * The caller is responsible for freeing the string data using l_free(), * and the string object is no longer usable. * * Returns: @string's internal buffer **/ LIB_EXPORT char *l_string_unwrap(struct l_string *string) { char *result; if (unlikely(!string)) return NULL; result = string->str; l_free(string); return result; } /** * l_string_append: * @dest: growable string object * @src: C-style string to copy * * Appends the contents of @src to @dest. The internal buffer of @dest is * grown if necessary. * * Returns: @dest **/ LIB_EXPORT struct l_string *l_string_append(struct l_string *dest, const char *src) { size_t size; if (unlikely(!dest || !src)) return NULL; size = strlen(src); grow_string(dest, size); memcpy(dest->str + dest->len, src, size); dest->len += size; dest->str[dest->len] = '\0'; return dest; } /** * l_string_append_c: * @dest: growable string object * @c: Character * * Appends character given by @c to @dest. The internal buffer of @dest is * grown if necessary. * * Returns: @dest **/ LIB_EXPORT struct l_string *l_string_append_c(struct l_string *dest, const char c) { if (unlikely(!dest)) return NULL; grow_string(dest, 1); dest->str[dest->len++] = c; dest->str[dest->len] = '\0'; return dest; } /** * l_string_append_fixed: * @dest: growable string object * @src: Character array to copy from * @max: Maximum number of characters to copy * * Appends the contents of a fixed size string array @src to @dest. * The internal buffer of @dest is grown if necessary. Up to a maximum of * @max characters are copied. If a null is encountered in the first @max * characters, the string is copied only up to the NULL character. * * Returns: @dest **/ LIB_EXPORT struct l_string *l_string_append_fixed(struct l_string *dest, const char *src, size_t max) { const char *nul; if (unlikely(!dest || !src || !max)) return NULL; nul = memchr(src, 0, max); if (nul) max = nul - src; grow_string(dest, max); memcpy(dest->str + dest->len, src, max); dest->len += max; dest->str[dest->len] = '\0'; return dest; } /** * l_string_append_vprintf: * @dest: growable string object * @format: the string format. See the sprintf() documentation * @args: the parameters to insert * * Appends a formatted string to the growable string buffer. This function * is equivalent to l_string_append_printf except that the arguments are * passed as a va_list. **/ LIB_EXPORT void l_string_append_vprintf(struct l_string *dest, const char *format, va_list args) { size_t len; size_t have_space; va_list args_copy; if (unlikely(!dest)) return; #if __STDC_VERSION__ > 199409L va_copy(args_copy, args); #else __va_copy(args_copy, args); #endif have_space = dest->max - dest->len; len = vsnprintf(dest->str + dest->len, have_space, format, args); if (len >= have_space) { grow_string(dest, len); len = vsprintf(dest->str + dest->len, format, args_copy); } dest->len += len; va_end(args_copy); } /** * l_string_append_printf: * @dest: growable string object * @format: the string format. See the sprintf() documentation * @...: the parameters to insert * * Appends a formatted string to the growable string buffer, growing it as * necessary. **/ LIB_EXPORT void l_string_append_printf(struct l_string *dest, const char *format, ...) { va_list args; if (unlikely(!dest)) return; va_start(args, format); l_string_append_vprintf(dest, format, args); va_end(args); } /** * l_string_length: * @string: growable string object * * Returns: bytes used in the string. **/ LIB_EXPORT unsigned int l_string_length(struct l_string *string) { if (unlikely(!string)) return 0; return string->len; } LIB_EXPORT struct l_string *l_string_truncate(struct l_string *string, size_t new_size) { if (unlikely(!string)) return NULL; if (new_size >= string->len) return string; string->len = new_size; string->str[new_size] = '\0'; return string; } struct arg { size_t max_len; size_t cur_len; char *chars; }; static inline void arg_init(struct arg *arg) { arg->max_len = 0; arg->cur_len = 0; arg->chars = NULL; } static void arg_putchar(struct arg *arg, char ch) { if (arg->cur_len == arg->max_len) { arg->max_len += 32; /* Grow by at least 32 bytes */ arg->chars = l_realloc(arg->chars, 1 + arg->max_len); } arg->chars[arg->cur_len++] = ch; arg->chars[arg->cur_len] = '\0'; } static void arg_putmem(struct arg *arg, const void *mem, size_t len) { if (len == 0) return; if (arg->cur_len + len > arg->max_len) { size_t growby = len * 2; if (growby < 32) growby = 32; arg->max_len += growby; arg->chars = l_realloc(arg->chars, 1 + arg->max_len); } memcpy(arg->chars + arg->cur_len, mem, len); arg->cur_len += len; arg->chars[arg->cur_len] = '\0'; } static bool parse_backslash(struct arg *arg, const char *args, size_t *pos) { /* We're at the backslash, not within double quotes */ char c = args[*pos + 1]; switch (c) { case 0: return false; case '\n': break; default: arg_putchar(arg, c); break; } *pos += 1; return true; } static bool parse_quoted_backslash(struct arg *arg, const char *args, size_t *pos) { /* We're at the backslash, within double quotes */ char c = args[*pos + 1]; switch (c) { case 0: return false; case '\n': break; case '"': case '\\': arg_putchar(arg, c); break; default: arg_putchar(arg, '\\'); arg_putchar(arg, c); break; } *pos += 1; return true; } static bool parse_single_quote(struct arg *arg, const char *args, size_t *pos) { /* We're just past the single quote */ size_t start = *pos; for (; args[*pos]; *pos += 1) { if (args[*pos] != '\'') continue; arg_putmem(arg, args + start, *pos - start); return true; } /* Unterminated ' */ return false; } static bool parse_double_quote(struct arg *arg, const char *args, size_t *pos) { /* We're just past the double quote */ for (; args[*pos]; *pos += 1) { char c = args[*pos]; switch (c) { case '"': return true; case '\\': if (!parse_quoted_backslash(arg, args, pos)) return false; break; default: arg_putchar(arg, c); break; } } /* Unterminated */ return false; } static void add_arg(char ***args, char *arg, int *n_args) { *args = l_realloc(*args, sizeof(char *) * (2 + *n_args)); (*args)[*n_args] = arg; (*args)[*n_args + 1] = NULL; *n_args += 1; } LIB_EXPORT char **l_parse_args(const char *args, int *out_n_args) { size_t i; struct arg arg; char **ret = l_realloc(NULL, sizeof(char *)); int n_args = 0; ret[0] = NULL; arg_init(&arg); for (i = 0; args[i]; i++) { switch (args[i]) { case '\\': if (!parse_backslash(&arg, args, &i)) goto error; break; case '"': i += 1; if (!parse_double_quote(&arg, args, &i)) goto error; /* Add an empty string */ if (!arg.cur_len) add_arg(&ret, l_strdup(""), &n_args); break; case '\'': i += 1; if (!parse_single_quote(&arg, args, &i)) goto error; /* Add an empty string */ if (!arg.cur_len) add_arg(&ret, l_strdup(""), &n_args); break; default: if (!strchr(" \t", args[i])) { if (args[i] == '\n') goto error; arg_putchar(&arg, args[i]); continue; } if (arg.cur_len) add_arg(&ret, arg.chars, &n_args); arg_init(&arg); break; } } if (arg.cur_len) add_arg(&ret, arg.chars, &n_args); if (out_n_args) *out_n_args = n_args; return ret; error: l_free(arg.chars); l_strfreev(ret); return NULL; } bluez-5.82/ell/PaxHeaders/internal0000644000000000000000000000005014773211447014173 xustar0020 atime=1743590183 20 ctime=1743591288 bluez-5.82/ell/internal0000644000000000000000000000000014773211447013642 0ustar00rootrootbluez-5.82/ell/PaxHeaders/tls-private.h0000644000000000000000000000005014504767710015060 xustar0020 atime=1743575449 20 ctime=1743591276 bluez-5.82/ell/tls-private.h0000644000000000000000000002376114504767710014552 0ustar00rootroot/* * Embedded Linux library * Copyright (C) 2011-2014 Intel Corporation * * SPDX-License-Identifier: LGPL-2.1-or-later */ #define TLS_MAX_VERSION L_TLS_V12 #define TLS_MIN_VERSION L_TLS_V10 enum tls_cipher_type { TLS_CIPHER_STREAM, TLS_CIPHER_BLOCK, TLS_CIPHER_AEAD, }; struct tls_bulk_encryption_algorithm { enum tls_cipher_type cipher_type; union { enum l_cipher_type l_id; enum l_aead_cipher_type l_aead_id; }; size_t key_length; size_t iv_length; size_t fixed_iv_length; size_t block_length; size_t auth_tag_length; }; /* * Support the minimum required set of handshake hash types for the * Certificate Verify digital signature and the Finished PRF seed so we * don't have to accumulate all of messages full contents until the * Finished message. If we're sent a hash of a different type (in TLS 1.2+) * and need to verify we'll give up. * SHA1 and MD5 are explicitly required by versions < 1.2 and 1.2 requires * that the Finished hash is the same as used for the PRF so we need to * keep at least the hashes our supported cipher suites specify for the PRF. */ enum handshake_hash_type { HANDSHAKE_HASH_SHA384, HANDSHAKE_HASH_SHA256, HANDSHAKE_HASH_MD5, HANDSHAKE_HASH_SHA1, __HANDSHAKE_HASH_COUNT, }; #define HANDSHAKE_HASH_MAX_SIZE 48 struct tls_hash_algorithm { uint8_t tls_id; enum handshake_hash_type type; enum l_checksum_type l_id; const char *name; }; extern const struct tls_hash_algorithm tls_handshake_hash_data[]; typedef bool (*tls_get_hash_t)(struct l_tls *tls, enum handshake_hash_type type, const uint8_t *data, size_t data_len, uint8_t *out, size_t *out_len); struct tls_signature_algorithm { uint8_t id; bool (*validate_cert_key_type)(struct l_cert *cert); ssize_t (*sign)(struct l_tls *tls, uint8_t *out, size_t out_len, tls_get_hash_t get_hash, const uint8_t *data, size_t data_len); bool (*verify)(struct l_tls *tls, const uint8_t *in, size_t in_len, tls_get_hash_t get_hash, const uint8_t *data, size_t data_len); }; struct tls_key_exchange_algorithm { bool need_ecc; bool need_ffdh; bool (*send_server_key_exchange)(struct l_tls *tls); void (*handle_server_key_exchange)(struct l_tls *tls, const uint8_t *buf, size_t len); bool (*send_client_key_exchange)(struct l_tls *tls); void (*handle_client_key_exchange)(struct l_tls *tls, const uint8_t *buf, size_t len); void (*free_params)(struct l_tls *tls); }; struct tls_mac_algorithm { uint8_t id; enum l_checksum_type hmac_type; size_t mac_length; }; struct tls_cipher_suite { uint8_t id[2]; const char *name; size_t verify_data_length; struct tls_bulk_encryption_algorithm *encryption; struct tls_signature_algorithm *signature; struct tls_key_exchange_algorithm *key_xchg; struct tls_mac_algorithm *mac; enum l_checksum_type prf_hmac; }; extern struct tls_cipher_suite *tls_cipher_suite_pref[]; struct tls_compression_method { int id; const char *name; }; struct tls_hello_extension { const char *name; const char *short_name; uint16_t id; ssize_t (*client_write)(struct l_tls *tls, uint8_t *buf, size_t len); /* Handle a Client Hello extension (on server), can't be NULL */ bool (*client_handle)(struct l_tls *tls, const uint8_t *buf, size_t len); /* Handle a Client Hello extension's absence (on server) */ bool (*client_handle_absent)(struct l_tls *tls); ssize_t (*server_write)(struct l_tls *tls, uint8_t *buf, size_t len); /* Handle a Server Hello extension (on client) */ bool (*server_handle)(struct l_tls *tls, const uint8_t *buf, size_t len); /* Handle a Server Hello extension's absence (on client) */ bool (*server_handle_absent)(struct l_tls *tls); }; extern const struct tls_hello_extension tls_extensions[]; struct tls_named_group { const char *name; uint16_t id; enum { TLS_GROUP_TYPE_EC, TLS_GROUP_TYPE_FF, } type; union { struct { const uint8_t *prime; size_t prime_len; unsigned int generator; } ff; }; }; enum tls_handshake_state { TLS_HANDSHAKE_WAIT_START, TLS_HANDSHAKE_WAIT_HELLO, TLS_HANDSHAKE_WAIT_CERTIFICATE, TLS_HANDSHAKE_WAIT_KEY_EXCHANGE, TLS_HANDSHAKE_WAIT_HELLO_DONE, TLS_HANDSHAKE_WAIT_CERTIFICATE_VERIFY, TLS_HANDSHAKE_WAIT_CHANGE_CIPHER_SPEC, TLS_HANDSHAKE_WAIT_FINISHED, TLS_HANDSHAKE_DONE, }; enum tls_content_type { TLS_CT_CHANGE_CIPHER_SPEC = 20, TLS_CT_ALERT = 21, TLS_CT_HANDSHAKE = 22, TLS_CT_APPLICATION_DATA = 23, }; enum tls_handshake_type { TLS_HELLO_REQUEST = 0, TLS_CLIENT_HELLO = 1, TLS_SERVER_HELLO = 2, TLS_CERTIFICATE = 11, TLS_SERVER_KEY_EXCHANGE = 12, TLS_CERTIFICATE_REQUEST = 13, TLS_SERVER_HELLO_DONE = 14, TLS_CERTIFICATE_VERIFY = 15, TLS_CLIENT_KEY_EXCHANGE = 16, TLS_FINISHED = 20, }; struct l_tls { bool server; l_tls_write_cb_t tx, rx; l_tls_ready_cb_t ready_handle; l_tls_disconnect_cb_t disconnected; void *user_data; l_tls_debug_cb_t debug_handler; l_tls_destroy_cb_t debug_destroy; void *debug_data; char *cert_dump_path; enum l_tls_version min_version; enum l_tls_version max_version; struct l_queue *ca_certs; struct l_certchain *cert; struct l_key *priv_key; size_t priv_key_size; char **subject_mask; struct tls_cipher_suite **cipher_suite_pref_list; struct l_settings *session_settings; char *session_prefix; uint64_t session_lifetime; unsigned int session_count_max; l_tls_session_update_cb_t session_update_cb; void *session_update_user_data; bool in_callback; bool pending_destroy; /* Record layer */ uint8_t *record_buf; int record_buf_len; int record_buf_max_len; bool record_flush; uint8_t *message_buf; int message_buf_len; int message_buf_max_len; enum tls_content_type message_content_type; /* Handshake protocol layer */ enum tls_handshake_state state; struct l_checksum *handshake_hash[__HANDSHAKE_HASH_COUNT]; uint8_t prev_digest[__HANDSHAKE_HASH_COUNT][HANDSHAKE_HASH_MAX_SIZE]; enum l_tls_version client_version; enum l_tls_version negotiated_version; bool cert_requested, cert_sent; bool peer_authenticated; struct l_cert *peer_cert; struct l_key *peer_pubkey; size_t peer_pubkey_size; enum handshake_hash_type signature_hash; const struct tls_hash_algorithm *prf_hmac; const struct tls_named_group *negotiated_curve; const struct tls_named_group *negotiated_ff_group; uint8_t session_id[32]; size_t session_id_size; uint8_t session_id_replaced[32]; size_t session_id_size_replaced; bool session_id_new; uint8_t session_cipher_suite_id[2]; uint8_t session_compression_method_id; char *session_peer_identity; bool session_resumed; struct { bool secure_renegotiation; /* Max .verify_data_length over supported cipher suites */ uint8_t client_verify_data[12]; uint8_t server_verify_data[12]; } renegotiation_info; /* SecurityParameters current and pending */ struct { struct tls_cipher_suite *cipher_suite; struct tls_compression_method *compression_method; uint8_t master_secret[48]; uint8_t client_random[32]; uint8_t server_random[32]; /* * Max key block size per 6.3 v1.1 is 136 bytes but if we * allow AES_256_CBC_SHA256 with v1.0 we get 128 per section * 6.3 v1.2 + two IVs of 32 bytes. */ uint8_t key_block[192]; void *key_xchg_params; } pending; enum tls_cipher_type cipher_type[2]; struct tls_cipher_suite *cipher_suite[2]; union { struct l_cipher *cipher[2]; struct l_aead_cipher *aead_cipher[2]; }; struct l_checksum *mac[2]; size_t mac_length[2]; size_t block_length[2]; size_t record_iv_length[2]; size_t fixed_iv_length[2]; uint8_t fixed_iv[2][32]; size_t auth_tag_length[2]; uint64_t seq_num[2]; /* * Some of the key and IV parts of the "current" state are kept * inside the cipher and mac states in the kernel so we don't * duplicate them here. */ bool ready; }; bool tls10_prf(const void *secret, size_t secret_len, const char *label, const void *seed, size_t seed_len, uint8_t *out, size_t out_len); bool tls12_prf(enum l_checksum_type type, const void *secret, size_t secret_len, const char *label, const void *seed, size_t seed_len, uint8_t *out, size_t out_len); void tls_disconnect(struct l_tls *tls, enum l_tls_alert_desc desc, enum l_tls_alert_desc local_desc); void tls_tx_record(struct l_tls *tls, enum tls_content_type type, const uint8_t *data, size_t len); bool tls_handle_message(struct l_tls *tls, const uint8_t *message, int len, enum tls_content_type type, uint16_t version); #define TLS_HANDSHAKE_HEADER_SIZE 4 void tls_tx_handshake(struct l_tls *tls, int type, uint8_t *buf, size_t length); bool tls_cipher_suite_is_compatible(struct l_tls *tls, const struct tls_cipher_suite *suite, const char **error); /* Optionally limit allowed cipher suites to a custom set */ bool tls_set_cipher_suites(struct l_tls *tls, const char **suite_list); void tls_generate_master_secret(struct l_tls *tls, const uint8_t *pre_master_secret, int pre_master_secret_len); size_t tls_verify_data_length(struct l_tls *tls, unsigned int index); const struct tls_named_group *tls_find_group(uint16_t id); const struct tls_named_group *tls_find_ff_group(const uint8_t *prime, size_t prime_len, const uint8_t *generator, size_t generator_len); ssize_t tls_write_signature_algorithms(struct l_tls *tls, uint8_t *buf, size_t len); ssize_t tls_parse_signature_algorithms(struct l_tls *tls, const uint8_t *buf, size_t len); int tls_parse_certificate_list(const void *data, size_t len, struct l_certchain **out_certchain); #define TLS_DEBUG(fmt, args...) \ l_util_debug(tls->debug_handler, tls->debug_data, "%s:%i " fmt, \ __func__, __LINE__, ## args) #define TLS_SET_STATE(new_state) \ do { \ TLS_DEBUG("New state %s", \ tls_handshake_state_to_str(new_state)); \ tls->state = new_state; \ } while (0) #define TLS_DISCONNECT(desc, local_desc, fmt, args...) \ do { \ TLS_DEBUG("Disconnect desc=%s local-desc=%s reason=" fmt,\ l_tls_alert_to_str(desc), \ l_tls_alert_to_str(local_desc), ## args);\ tls_disconnect(tls, desc, local_desc); \ } while (0) #define TLS_VER_FMT "1.%i" #define TLS_VER_ARGS(version) (((version) & 0xff) - 1) const char *tls_handshake_state_to_str(enum tls_handshake_state state); bluez-5.82/ell/PaxHeaders/ecc-private.h0000644000000000000000000000005014526443132015001 xustar0020 atime=1743575449 20 ctime=1743591276 bluez-5.82/ell/ecc-private.h0000644000000000000000000000735514526443132014474 0ustar00rootroot/* * Embedded Linux library * Copyright (C) 2018 Intel Corporation * * SPDX-License-Identifier: LGPL-2.1-or-later */ #include #include #include "ecc.h" #include "util.h" struct l_ecc_curve; struct l_ecc_point { uint64_t x[L_ECC_MAX_DIGITS]; uint64_t y[L_ECC_MAX_DIGITS]; const struct l_ecc_curve *curve; }; struct l_ecc_curve { unsigned int ndigits; unsigned int ike_group; unsigned int tls_group; const char *name; struct l_ecc_point g; uint64_t p[L_ECC_MAX_DIGITS]; uint64_t n[L_ECC_MAX_DIGITS]; uint64_t b[L_ECC_MAX_DIGITS]; int z; }; struct l_ecc_scalar { uint64_t c[L_ECC_MAX_DIGITS]; const struct l_ecc_curve *curve; }; /* * Performs a secure memory comparison of two uint64_t buffers of size bytes * representing an integer. Blobs are ordered in little endian. It returns * a negative, zero or positif value if a < b, a == b or a > b respectively. */ static inline int secure_memcmp_64(const uint64_t *a, const uint64_t *b, size_t size) { uint64_t aa_64, bb_64; int res = 0, mask; size_t i = 0; if (size) { /* * Arrays store blobs in LE, we will process each blob as a * byte array of size 8 using l_secure_memcmp. We need to make * sure to feed a BE byte array to avoid unexpected behavior * on different architectures. */ do { aa_64 = L_CPU_TO_BE64(a[i]); bb_64 = L_CPU_TO_BE64(b[i]); mask = l_secure_memcmp(&aa_64, &bb_64, 8); res = (mask & res) | mask; i++; } while (i != size); } return res; } void _ecc_be2native(uint64_t *dest, const uint64_t *bytes, unsigned int ndigits); void _ecc_native2be(uint64_t *dest, const uint64_t *native, unsigned int ndigits); void _vli_mod_inv(uint64_t *result, const uint64_t *input, const uint64_t *mod, unsigned int ndigits); void _vli_mod_sub(uint64_t *result, const uint64_t *left, const uint64_t *right, const uint64_t *mod, unsigned int ndigits); void _vli_mod_add(uint64_t *result, const uint64_t *left, const uint64_t *right, const uint64_t *mod, unsigned int ndigits); void _vli_rshift1(uint64_t *vli, unsigned int ndigits); void _vli_mmod_slow(uint64_t *result, const uint64_t *product, const uint64_t *mod, unsigned int ndigits); bool _vli_mmod_fast(uint64_t *result, const uint64_t *product, const uint64_t *curve_prime, unsigned int ndigits); void _vli_mod_mult_fast(uint64_t *result, const uint64_t *left, const uint64_t *right, const uint64_t *curve_prime, unsigned int ndigits); void _vli_mod_square_fast(uint64_t *result, const uint64_t *left, const uint64_t *curve_prime, unsigned int ndigits); void _vli_mod_exp(uint64_t *result, const uint64_t *base, const uint64_t *exp, const uint64_t *mod, unsigned int ndigits); int _vli_cmp(const uint64_t *left, const uint64_t *right, unsigned int ndigits); bool _vli_is_zero_or_one(const uint64_t *vli, unsigned int ndigits); uint64_t _vli_add(uint64_t *result, const uint64_t *left, const uint64_t *right, unsigned int ndigits); uint64_t _vli_sub(uint64_t *result, const uint64_t *left, const uint64_t *right, unsigned int ndigits); int _vli_legendre(uint64_t *val, const uint64_t *p, unsigned int ndigits); bool _ecc_point_is_zero(const struct l_ecc_point *point); void _ecc_calculate_p2(const struct l_ecc_curve *curve, uint64_t *p2); bool _ecc_compute_y(const struct l_ecc_curve *curve, uint64_t *y, const uint64_t *x); void _ecc_point_mult(struct l_ecc_point *result, const struct l_ecc_point *point, const uint64_t *scalar, uint64_t *initial_z, const uint64_t *curve_prime); void _ecc_point_add(struct l_ecc_point *ret, const struct l_ecc_point *p, const struct l_ecc_point *q, const uint64_t *curve_prime); struct l_ecc_scalar *_ecc_constant_new(const struct l_ecc_curve *curve, const void *buf, size_t len); bluez-5.82/ell/PaxHeaders/main-private.h0000644000000000000000000000005014504767710015202 xustar0020 atime=1743575449 20 ctime=1743591276 bluez-5.82/ell/main-private.h0000644000000000000000000000140614504767710014664 0ustar00rootroot/* * Embedded Linux library * Copyright (C) 2021 Intel Corporation * * SPDX-License-Identifier: LGPL-2.1-or-later */ typedef void (*watch_event_cb_t) (int fd, uint32_t events, void *user_data); typedef void (*watch_destroy_cb_t) (void *user_data); typedef void (*idle_event_cb_t) (void *user_data); typedef void (*idle_destroy_cb_t) (void *user_data); int watch_add(int fd, uint32_t events, watch_event_cb_t callback, void *user_data, watch_destroy_cb_t destroy); int watch_modify(int fd, uint32_t events, bool force); int watch_remove(int fd, bool epoll_del); int watch_clear(int fd); #define IDLE_FLAG_NO_WARN_DANGLING 0x10000000 int idle_add(idle_event_cb_t callback, void *user_data, uint32_t flags, idle_destroy_cb_t destroy); void idle_remove(int id); bluez-5.82/ell/PaxHeaders/main.c0000644000000000000000000000005014560467756013536 xustar0020 atime=1743575471 20 ctime=1743591276 bluez-5.82/ell/main.c0000644000000000000000000002671714560467756013234 0ustar00rootroot/* * Embedded Linux library * Copyright (C) 2011-2014 Intel Corporation * * SPDX-License-Identifier: LGPL-2.1-or-later */ #ifdef HAVE_CONFIG_H #include #endif #define _GNU_SOURCE #include #include #include #include #include #include #include #include #include #include "signal.h" #include "queue.h" #include "log.h" #include "useful.h" #include "main.h" #include "main-private.h" #include "private.h" #include "timeout.h" /** * SECTION:main * @short_description: Main loop handling * * Main loop handling */ #define MAX_EPOLL_EVENTS 10 #define IDLE_FLAG_DISPATCHING 1 #define IDLE_FLAG_DESTROYED 2 #define WATCH_FLAG_DISPATCHING 1 #define WATCH_FLAG_DESTROYED 2 #define WATCHDOG_TRIGGER_FREQ 2 static int epoll_fd = -1; static bool epoll_running; static bool epoll_terminate; static int idle_id; static int notify_fd; static struct l_timeout *watchdog; static struct l_queue *idle_list; struct watch_data { int fd; uint32_t events; uint32_t flags; watch_event_cb_t callback; watch_destroy_cb_t destroy; void *user_data; }; #define DEFAULT_WATCH_ENTRIES 128 static unsigned int watch_entries; static struct watch_data **watch_list; struct idle_data { idle_event_cb_t callback; idle_destroy_cb_t destroy; void *user_data; uint32_t flags; int id; }; static inline bool __attribute__ ((always_inline)) create_epoll(void) { unsigned int i; epoll_fd = epoll_create1(EPOLL_CLOEXEC); if (epoll_fd < 0) return false; watch_list = malloc(DEFAULT_WATCH_ENTRIES * sizeof(void *)); if (!watch_list) goto close_epoll; idle_list = l_queue_new(); idle_id = 0; watch_entries = DEFAULT_WATCH_ENTRIES; for (i = 0; i < watch_entries; i++) watch_list[i] = NULL; return true; close_epoll: close(epoll_fd); epoll_fd = -1; return false; } int watch_add(int fd, uint32_t events, watch_event_cb_t callback, void *user_data, watch_destroy_cb_t destroy) { struct watch_data *data; struct epoll_event ev; int err; if (unlikely(fd < 0 || !callback)) return -EINVAL; if (epoll_fd < 0) return -EIO; if (L_WARN_ON((unsigned int) fd > watch_entries - 1)) return -ERANGE; data = l_new(struct watch_data, 1); data->fd = fd; data->events = events; data->flags = 0; data->callback = callback; data->destroy = destroy; data->user_data = user_data; memset(&ev, 0, sizeof(ev)); ev.events = events; ev.data.ptr = data; err = epoll_ctl(epoll_fd, EPOLL_CTL_ADD, data->fd, &ev); if (err < 0) { l_free(data); return -errno; } watch_list[fd] = data; return 0; } int watch_modify(int fd, uint32_t events, bool force) { struct watch_data *data; struct epoll_event ev; int err; if (unlikely(fd < 0)) return -EINVAL; if ((unsigned int) fd > watch_entries - 1) return -ERANGE; data = watch_list[fd]; if (!data) return -ENXIO; if (data->events == events && !force) return 0; memset(&ev, 0, sizeof(ev)); ev.events = events; ev.data.ptr = data; err = epoll_ctl(epoll_fd, EPOLL_CTL_MOD, data->fd, &ev); if (err < 0) return -errno; data->events = events; return 0; } int watch_clear(int fd) { struct watch_data *data; if (unlikely(fd < 0)) return -EINVAL; if ((unsigned int) fd > watch_entries - 1) return -ERANGE; data = watch_list[fd]; if (!data) return -ENXIO; watch_list[fd] = NULL; if (data->destroy) data->destroy(data->user_data); if (data->flags & WATCH_FLAG_DISPATCHING) data->flags |= WATCH_FLAG_DESTROYED; else l_free(data); return 0; } int watch_remove(int fd, bool epoll_del) { int err = watch_clear(fd); if (err < 0) return err; if (!epoll_del) goto done; err = epoll_ctl(epoll_fd, EPOLL_CTL_DEL, fd, NULL); if (err < 0) return -errno; done: return err; } static bool idle_remove_by_id(void *data, void *user_data) { struct idle_data *idle = data; int id = L_PTR_TO_INT(user_data); if (idle->id != id) return false; if (idle->destroy) idle->destroy(idle->user_data); if (idle->flags & IDLE_FLAG_DISPATCHING) { idle->flags |= IDLE_FLAG_DESTROYED; return false; } l_free(idle); return true; } static bool idle_prune(void *data, void *user_data) { struct idle_data *idle = data; if ((idle->flags & IDLE_FLAG_DESTROYED) == 0) return false; l_free(idle); return true; } int idle_add(idle_event_cb_t callback, void *user_data, uint32_t flags, idle_destroy_cb_t destroy) { struct idle_data *data; if (unlikely(!callback)) return -EINVAL; if (epoll_fd < 0) return -EIO; data = l_new(struct idle_data, 1); data->callback = callback; data->destroy = destroy; data->user_data = user_data; data->flags = flags; if (!l_queue_push_tail(idle_list, data)) { l_free(data); return -ENOMEM; } data->id = idle_id++; if (idle_id == INT_MAX) idle_id = 0; return data->id; } void idle_remove(int id) { l_queue_foreach_remove(idle_list, idle_remove_by_id, L_INT_TO_PTR(id)); } static void idle_destroy(void *data) { struct idle_data *idle = data; if (!(idle->flags & IDLE_FLAG_NO_WARN_DANGLING)) l_error("Dangling idle descriptor %p, %d found", data, idle->id); if (idle->destroy) idle->destroy(idle->user_data); l_free(idle); } static void idle_dispatch(void *data, void *user_data) { struct idle_data *idle = data; if (!idle->callback) return; idle->flags |= IDLE_FLAG_DISPATCHING; idle->callback(idle->user_data); idle->flags &= ~IDLE_FLAG_DISPATCHING; } static int sd_notify(const char *state) { int err; if (notify_fd <= 0) return -ENOTCONN; err = send(notify_fd, state, strlen(state), MSG_NOSIGNAL); if (err < 0) return -errno; return 0; } static void watchdog_callback(struct l_timeout *timeout, void *user_data) { int msec = L_PTR_TO_INT(user_data); sd_notify("WATCHDOG=1"); l_timeout_modify_ms(timeout, msec); } static void create_sd_notify_socket(void) { const char *sock; struct sockaddr_un addr; const char *watchdog_usec; int msec; /* check if NOTIFY_SOCKET has been set */ sock = getenv("NOTIFY_SOCKET"); if (!sock) return; /* check for abstract socket or absolute path */ if (sock[0] != '@' && sock[0] != '/') return; notify_fd = socket(AF_UNIX, SOCK_DGRAM | SOCK_CLOEXEC, 0); if (notify_fd < 0) { notify_fd = 0; return; } memset(&addr, 0, sizeof(addr)); addr.sun_family = AF_UNIX; strncpy(addr.sun_path, sock, sizeof(addr.sun_path) - 1); if (addr.sun_path[0] == '@') addr.sun_path[0] = '\0'; if (bind(notify_fd, (struct sockaddr *) &addr, sizeof(addr)) < 0) { close(notify_fd); notify_fd = 0; return; } watchdog_usec = getenv("WATCHDOG_USEC"); if (!watchdog_usec) return; msec = atoi(watchdog_usec) / 1000; if (msec < WATCHDOG_TRIGGER_FREQ) return; msec /= WATCHDOG_TRIGGER_FREQ; watchdog = l_timeout_create_ms(msec, watchdog_callback, L_INT_TO_PTR(msec), NULL); } /** * l_main_init: * * Initialize the main loop. This must be called before l_main_run() * and any other function that directly or indirectly sets up an idle * or watch. A safe rule-of-thumb is to call it before any function * prefixed with "l_". * * Returns: true if initialization was successful, false otherwise. **/ LIB_EXPORT bool l_main_init(void) { if (unlikely(epoll_running)) return false; if (!create_epoll()) return false; create_sd_notify_socket(); epoll_terminate = false; return true; } /** * l_main_prepare: * * Prepare the iteration of the main loop * * Returns: The timeout to use. This will be 0 if idle-event processing is * currently pending, or -1 otherwise. This value can be used to pass to * l_main_iterate. */ LIB_EXPORT int l_main_prepare(void) { return l_queue_isempty(idle_list) ? -1 : 0; } /** * l_main_iterate: * * Run one iteration of the main event loop */ LIB_EXPORT void l_main_iterate(int timeout) { struct epoll_event events[MAX_EPOLL_EVENTS]; struct watch_data *data; int n, nfds; nfds = epoll_wait(epoll_fd, events, MAX_EPOLL_EVENTS, timeout); for (n = 0; n < nfds; n++) { data = events[n].data.ptr; data->flags |= WATCH_FLAG_DISPATCHING; } for (n = 0; n < nfds; n++) { data = events[n].data.ptr; if (data->flags & WATCH_FLAG_DESTROYED) continue; data->callback(data->fd, events[n].events, data->user_data); } for (n = 0; n < nfds; n++) { data = events[n].data.ptr; if (data->flags & WATCH_FLAG_DESTROYED) l_free(data); else data->flags = 0; } l_queue_foreach(idle_list, idle_dispatch, NULL); l_queue_foreach_remove(idle_list, idle_prune, NULL); } /** * l_main_run: * * Run the main loop * * The loop may be restarted by invoking this function after a * previous invocation returns, provided that l_main_exit() has not * been called first. * * Returns: #EXIT_SUCCESS after successful execution or #EXIT_FAILURE in * case of failure **/ LIB_EXPORT int l_main_run(void) { int timeout; /* Has l_main_init() been called? */ if (unlikely(epoll_fd < 0)) return EXIT_FAILURE; if (unlikely(epoll_running)) return EXIT_FAILURE; epoll_running = true; for (;;) { if (epoll_terminate) break; timeout = l_main_prepare(); l_main_iterate(timeout); } epoll_running = false; if (notify_fd) { close(notify_fd); notify_fd = 0; l_timeout_remove(watchdog); watchdog = NULL; } return EXIT_SUCCESS; } /** * l_main_exit: * * Clean up after main loop completes. * **/ LIB_EXPORT bool l_main_exit(void) { unsigned int i; if (epoll_running) { l_error("Cleanup attempted on running main loop"); return false; } for (i = 0; i < watch_entries; i++) { struct watch_data *data = watch_list[i]; if (!data) continue; epoll_ctl(epoll_fd, EPOLL_CTL_DEL, data->fd, NULL); if (data->destroy) data->destroy(data->user_data); else l_error("Dangling file descriptor %d found", data->fd); l_free(data); } watch_entries = 0; free(watch_list); watch_list = NULL; l_queue_destroy(idle_list, idle_destroy); idle_list = NULL; close(epoll_fd); epoll_fd = -1; return true; } /** * l_main_quit: * * Terminate the running main loop * * Returns: #true when terminating the main loop or #false in case of failure **/ LIB_EXPORT bool l_main_quit(void) { if (unlikely(!epoll_running)) return false; epoll_terminate = true; return true; } struct signal_data { l_main_signal_cb_t callback; void *user_data; }; static void sigint_handler(void *user_data) { struct signal_data *data = user_data; if (data->callback) data->callback(SIGINT, data->user_data); } static void sigterm_handler(void *user_data) { struct signal_data *data = user_data; if (data->callback) data->callback(SIGTERM, data->user_data); } /** * l_main_run_with_signal: * * Run the main loop with signal handling for SIGINT and SIGTERM * * Returns: #EXIT_SUCCESS after successful execution or #EXIT_FAILURE in * case of failure **/ LIB_EXPORT int l_main_run_with_signal(l_main_signal_cb_t callback, void *user_data) { struct signal_data *data; struct l_signal *sigint; struct l_signal *sigterm; int result; data = l_new(struct signal_data, 1); data->callback = callback; data->user_data = user_data; sigint = l_signal_create(SIGINT, sigint_handler, data, NULL); sigterm = l_signal_create(SIGTERM, sigterm_handler, data, NULL); result = l_main_run(); l_signal_remove(sigint); l_signal_remove(sigterm); l_free(data); return result; } /** * l_main_get_epoll_fd: * * Can be used to obtain the epoll file descriptor in order to integrate * the ell main event loop with other event loops. * * Returns: epoll file descriptor **/ LIB_EXPORT int l_main_get_epoll_fd(void) { return epoll_fd; } bluez-5.82/ell/PaxHeaders/util.c0000644000000000000000000000005014770744372013562 xustar0020 atime=1743575460 20 ctime=1743591276 bluez-5.82/ell/util.c0000644000000000000000000004164214770744372013252 0ustar00rootroot/* * Embedded Linux library * Copyright (C) 2011-2014 Intel Corporation * * SPDX-License-Identifier: LGPL-2.1-or-later */ #ifdef HAVE_CONFIG_H #include #endif #define _GNU_SOURCE #include #include #include #include #include #include #include "utf8.h" #include "util.h" #include "useful.h" #include "private.h" /** * SECTION:util * @short_description: Utility functions * * Utility functions */ #define STRLOC __FILE__ ":" L_STRINGIFY(__LINE__) /** * l_malloc: * @size: memory size to allocate * * If for any reason the memory allocation fails, then execution will be * halted via abort(). * * In case @size is 0 then #NULL will be returned. * * Returns: pointer to allocated memory **/ LIB_EXPORT void *l_malloc(size_t size) { if (likely(size)) { void *ptr; ptr = malloc(size); if (ptr) return ptr; fprintf(stderr, "%s:%s(): failed to allocate %zd bytes\n", STRLOC, __func__, size); abort(); } return NULL; } /** * l_realloc: * @mem: previously allocated memory, or NULL * @size: memory size to allocate * * If for any reason the memory allocation fails, then execution will be * halted via abort(). * * In case @mem is NULL, this function acts like l_malloc. * In case @size is 0 then #NULL will be returned. * * Returns: pointer to allocated memory **/ LIB_EXPORT void *l_realloc(void *mem, size_t size) { if (likely(size)) { void *ptr; ptr = realloc(mem, size); if (ptr) return ptr; fprintf(stderr, "%s:%s(): failed to re-allocate %zd bytes\n", STRLOC, __func__, size); abort(); } else l_free(mem); return NULL; } /** * l_memdup: * @mem: pointer to memory you want to duplicate * @size: memory size * * If for any reason the memory allocation fails, then execution will be * halted via abort(). * * In case @size is 0 then #NULL will be returned. * * Returns: pointer to duplicated memory buffer **/ LIB_EXPORT void *l_memdup(const void *mem, size_t size) { void *ptr; ptr = l_malloc(size); memcpy(ptr, mem, size); return ptr; } /** * l_free: * @ptr: memory pointer * * Free the allocated memory area. **/ LIB_EXPORT void l_free(void *ptr) { free(ptr); } /** * l_strdup: * @str: string pointer * * Allocates and duplicates string * * Returns: a newly allocated string **/ LIB_EXPORT char *l_strdup(const char *str) { if (likely(str)) { char *tmp; tmp = strdup(str); if (tmp) return tmp; fprintf(stderr, "%s:%s(): failed to allocate string\n", STRLOC, __func__); abort(); } return NULL; } /** * l_strndup: * @str: string pointer * @max: Maximum number of characters to copy * * Allocates and duplicates string. If the string is longer than @max * characters, only @max are copied and a null terminating character * is added. * * Returns: a newly allocated string **/ LIB_EXPORT char *l_strndup(const char *str, size_t max) { if (likely(str)) { char *tmp; tmp = strndup(str, max); if (tmp) return tmp; fprintf(stderr, "%s:%s(): failed to allocate string\n", STRLOC, __func__); abort(); } return NULL; } /** * l_strdup_printf: * @format: string format * @...: parameters to insert into format string * * Returns: a newly allocated string **/ LIB_EXPORT char *l_strdup_printf(const char *format, ...) { va_list args; char *str; int len; va_start(args, format); len = vasprintf(&str, format, args); va_end(args); if (len < 0) { fprintf(stderr, "%s:%s(): failed to allocate string\n", STRLOC, __func__); abort(); return NULL; } return str; } /** * l_strdup_vprintf: * @format: string format * @args: parameters to insert into format string * * Returns: a newly allocated string **/ LIB_EXPORT char *l_strdup_vprintf(const char *format, va_list args) { char *str; int len; len = vasprintf(&str, format, args); if (len < 0) { fprintf(stderr, "%s:%s(): failed to allocate string\n", STRLOC, __func__); abort(); return NULL; } return str; } /** * l_strlcpy: * @dst: Destination buffer for string * @src: Source buffer containing null-terminated string to copy * @len: Maximum destination buffer space to use * * Copies a string from the @src buffer to the @dst buffer, using no * more than @len bytes in @dst. @dst is guaranteed to be * null-terminated. The caller can determine if the copy was truncated by * checking if the return value is greater than or equal to @len. * * NOTE: Passing in a NULL string results in a no-op * * Returns: The length of the @src string, not including the null * terminator. */ LIB_EXPORT size_t l_strlcpy(char *dst, const char *src, size_t len) { size_t src_len = src ? strlen(src) : 0; if (!src) goto done; if (len) { if (src_len < len) { len = src_len + 1; } else { len -= 1; dst[len] = '\0'; } memcpy(dst, src, len); } done: return src_len; } /** * l_str_has_prefix: * @str: A string to be examined * @delim: Prefix string * * Determines if the string given by @str is prefixed by string given by * @prefix. * * Returns: True if @str was prefixed by @prefix. False otherwise. */ LIB_EXPORT bool l_str_has_prefix(const char *str, const char *prefix) { size_t str_len; size_t prefix_len; if (unlikely(!str)) return false; if (unlikely(!prefix)) return false; str_len = strlen(str); prefix_len = strlen(prefix); if (str_len < prefix_len) return false; return !strncmp(str, prefix, prefix_len); } /** * l_str_has_suffix: * @str: A string to be examined * @suffix: Suffix string * * Determines if the string given by @str ends with the specified @suffix. * * Returns: True if @str ends with the specified @suffix. False otherwise. */ LIB_EXPORT bool l_str_has_suffix(const char *str, const char *suffix) { size_t str_len; size_t suffix_len; size_t len_diff; if (unlikely(!str)) return false; if (unlikely(!suffix)) return false; str_len = strlen(str); suffix_len = strlen(suffix); if (str_len < suffix_len) return false; len_diff = str_len - suffix_len; return !strcmp(&str[len_diff], suffix); } /** * l_streq0: * @a: First operand * @b: Second operand * * Returns: True if @a and @b are both NULL or both non-NULL and identical * according to strcmp. False otherwise. */ LIB_EXPORT bool l_streq0(const char *a, const char *b) { return a == b || (a && b && !strcmp(a, b)); } /** * l_util_oidstring: * @buf: buffer pointer * @len: length of buffer * * Returns: a newly allocated string for OID in numeric representation. * In case of error, Null is returned. **/ LIB_EXPORT char *l_util_oidstring(const void *buf, size_t len) { const unsigned char *ptr = buf; char *str; size_t str_len; size_t str_max; size_t i; if (unlikely(!buf) || unlikely(len < 2)) return NULL; /* A very simple initial estimation */ str_max = 4 + (len - 1) * 2; str = l_malloc(str_max); str_len = snprintf(str, str_max, "%u.%u", ptr[0] / 40, ptr[0] % 40); /* In the unlikely case the initial buffer is too small */ if (unlikely(str_len >= str_max)) { str_max = str_len + 1; str = l_realloc(str, str_max); str_len = sprintf(str, "%u.%u", ptr[0] / 40, ptr[0] % 40); } i = 1; while (i < len) { unsigned long value = 0; unsigned char byte; size_t have_space; size_t l; do { /* * Shift the current value by 7 bits to make room * for the lower 7 bits of the byte in the stream. */ byte = ptr[i++]; value = (value << 7) | (byte & 0x7F); /* This is a malformed OID */ if (i > len) { l_free(str); return NULL; } } while (byte & 0x80); have_space = str_max - str_len; l = snprintf(str + str_len, have_space, ".%lu", value); if (l >= have_space) { str_max = str_len + l + 1; str = l_realloc(str, str_max); l = sprintf(str + str_len, ".%lu", value); } str_len += l; } return str; } static char *hexstring_common(const unsigned char *buf, size_t len, const char hexdigits[static 16]) { char *str; size_t i; if (unlikely(!buf) || unlikely(!len)) return NULL; str = l_malloc(len * 2 + 1); for (i = 0; i < len; i++) { str[(i * 2) + 0] = hexdigits[buf[i] >> 4]; str[(i * 2) + 1] = hexdigits[buf[i] & 0xf]; } str[len * 2] = '\0'; return str; } static char *hexstringv_common(const struct iovec *iov, size_t n_iov, const char hexdigits[static 16]) { char *str; size_t i, j, c; size_t len; if (unlikely(!iov || !n_iov)) return NULL; for (i = 0, len = 0; i < n_iov; i++) len += iov[i].iov_len; str = l_malloc(len * 2 + 1); c = 0; for (i = 0; i < n_iov; i++) { const uint8_t *buf = iov[i].iov_base; for (j = 0; j < iov[i].iov_len; j++) { str[c++] = hexdigits[buf[j] >> 4]; str[c++] = hexdigits[buf[j] & 0xf]; } } str[len * 2] = '\0'; return str; } /** * l_util_hexstring: * @buf: buffer pointer * @len: length of buffer * * Returns: a newly allocated hex string. Note that the string will contain * lower case hex digits a-f. If you require upper case hex digits, use * @l_util_hexstring_upper **/ LIB_EXPORT char *l_util_hexstring(const void *buf, size_t len) { static const char hexdigits[] = "0123456789abcdef"; return hexstring_common(buf, len, hexdigits); } /** * l_util_hexstring_upper: * @buf: buffer pointer * @len: length of buffer * * Returns: a newly allocated hex string. Note that the string will contain * upper case hex digits a-f. If you require lower case hex digits, use * @l_util_hexstring **/ LIB_EXPORT char *l_util_hexstring_upper(const void *buf, size_t len) { static const char hexdigits[] = "0123456789ABCDEF"; return hexstring_common(buf, len, hexdigits); } /** * l_util_hexstringv: * @iov: iovec * @n_iov: length of the iovec * * Returns: a newly allocated hex string. Note that the string will contain * lower case hex digits a-f. If you require upper case hex digits, use * @l_util_hexstringv_upper **/ LIB_EXPORT char *l_util_hexstringv(const struct iovec *iov, size_t n_iov) { static const char hexdigits[] = "0123456789abcdef"; return hexstringv_common(iov, n_iov, hexdigits); } /** * l_util_hexstringv_upper: * @iov: iovec * @n_iov: length of the iovec * * Returns: a newly allocated hex string. Note that the string will contain * upper case hex digits a-f. If you require lower case hex digits, use * @l_util_hexstringv **/ LIB_EXPORT char *l_util_hexstringv_upper(const struct iovec *iov, size_t n_iov) { static const char hexdigits[] = "0123456789ABCDEF"; return hexstringv_common(iov, n_iov, hexdigits); } /** * l_util_from_hexstring: * @str: Null-terminated string containing the hex-encoded bytes * @out_len: Number of bytes decoded * * Returns: a newly allocated byte array. Empty strings are treated as * an error condition. **/ LIB_EXPORT unsigned char *l_util_from_hexstring(const char *str, size_t *out_len) { size_t i, j; size_t len; char c; unsigned char *buf; if (unlikely(!str)) return NULL; for (i = 0; str[i]; i++) { c = str[i]; if ((c >= '0' && c <= '9') || (c >= 'A' && c <= 'F') || (c >= 'a' && c <= 'f')) continue; return NULL; } if (!i) return NULL; if ((i % 2) != 0) return NULL; len = i; buf = l_malloc(i >> 1); for (i = 0, j = 0; i < len; i++, j++) { c = str[i]; if (c >= '0' && c <= '9') buf[j] = c - '0'; else if (c >= 'A' && c <= 'F') buf[j] = 10 + c - 'A'; else if (c >= 'a' && c <= 'f') buf[j] = 10 + c - 'a'; i += 1; c = str[i]; if (c >= '0' && c <= '9') buf[j] = buf[j] * 16 + c - '0'; else if (c >= 'A' && c <= 'F') buf[j] = buf[j] * 16 + 10 + c - 'A'; else if (c >= 'a' && c <= 'f') buf[j] = buf[j] * 16 + 10 + c - 'a'; } if (out_len) *out_len = j; return buf; } static void hexdump(const char dir, const unsigned char *buf, size_t len, l_util_hexdump_func_t function, void *user_data) { static const char hexdigits[] = "0123456789abcdef"; char str[68]; size_t i; if (unlikely(!len)) return; str[0] = dir; for (i = 0; i < len; i++) { str[((i % 16) * 3) + 1] = ' '; str[((i % 16) * 3) + 2] = hexdigits[buf[i] >> 4]; str[((i % 16) * 3) + 3] = hexdigits[buf[i] & 0xf]; str[(i % 16) + 51] = l_ascii_isprint(buf[i]) ? buf[i] : '.'; if ((i + 1) % 16 == 0) { str[49] = ' '; str[50] = ' '; str[67] = '\0'; function(str, user_data); str[0] = ' '; } } if (i % 16 > 0) { size_t j; for (j = (i % 16); j < 16; j++) { str[(j * 3) + 1] = ' '; str[(j * 3) + 2] = ' '; str[(j * 3) + 3] = ' '; str[j + 51] = ' '; } str[49] = ' '; str[50] = ' '; str[67] = '\0'; function(str, user_data); } } LIB_EXPORT void l_util_hexdump(bool in, const void *buf, size_t len, l_util_hexdump_func_t function, void *user_data) { if (likely(!function)) return; hexdump(in ? '<' : '>', buf, len, function, user_data); } LIB_EXPORT void l_util_hexdump_two(bool in, const void *buf1, size_t len1, const void *buf2, size_t len2, l_util_hexdump_func_t function, void *user_data) { if (likely(!function)) return; hexdump(in ? '<' : '>', buf1, len1, function, user_data); hexdump(' ', buf2, len2, function, user_data); } LIB_EXPORT void l_util_hexdumpv(bool in, const struct iovec *iov, size_t n_iov, l_util_hexdump_func_t function, void *user_data) { static const char hexdigits[] = "0123456789abcdef"; char str[68]; size_t i; size_t len; size_t c; const uint8_t *buf; if (likely(!function)) return; if (unlikely(!iov || !n_iov)) return; str[0] = in ? '<' : '>'; for (i = 0, len = 0; i < n_iov; i++) len += iov[i].iov_len; c = 0; buf = iov[0].iov_base; for (i = 0; i < len; i++, c++) { if (c == iov[0].iov_len) { c = 0; iov += 1; buf = iov[0].iov_base; } str[((i % 16) * 3) + 1] = ' '; str[((i % 16) * 3) + 2] = hexdigits[buf[c] >> 4]; str[((i % 16) * 3) + 3] = hexdigits[buf[c] & 0xf]; str[(i % 16) + 51] = l_ascii_isprint(buf[c]) ? buf[c] : '.'; if ((i + 1) % 16 == 0) { str[49] = ' '; str[50] = ' '; str[67] = '\0'; function(str, user_data); str[0] = ' '; } } if (i % 16 > 0) { size_t j; for (j = (i % 16); j < 16; j++) { str[(j * 3) + 1] = ' '; str[(j * 3) + 2] = ' '; str[(j * 3) + 3] = ' '; str[j + 51] = ' '; } str[49] = ' '; str[50] = ' '; str[67] = '\0'; function(str, user_data); } } LIB_EXPORT void l_util_debug(l_util_hexdump_func_t function, void *user_data, const char *format, ...) { va_list args; char *str; int len; if (likely(!function)) return; if (unlikely(!format)) return; va_start(args, format); len = vasprintf(&str, format, args); va_end(args); if (unlikely(len < 0)) return; function(str, user_data); free(str); } /** * l_util_get_debugfs_path: * * Returns: a pointer to mount point of debugfs **/ LIB_EXPORT const char *l_util_get_debugfs_path(void) { static char path[PATH_MAX + 1]; static bool found = false; char type[100]; FILE *fp; if (found) return path; fp = fopen("/proc/mounts", "r"); if (!fp) return NULL; while (fscanf(fp, "%*s %" L_STRINGIFY(PATH_MAX) "s %99s %*s %*d %*d\n", path, type) == 2) { if (!strcmp(type, "debugfs")) { found = true; break; } } fclose(fp); if (!found) return NULL; return path; } LIB_EXPORT bool l_memeq(const void *field, size_t size, uint8_t byte) { const uint8_t *mem = field; size_t i; for (i = 0; i < size; i++) if (mem[i] != byte) return false; return true; } __attribute__((noinline)) static int __secure_memeq(const void *field, size_t size, uint8_t byte) { unsigned int diff = 0; size_t i; for (i = 0; i < size; i++) { diff |= ((uint8_t *) field)[i] ^ byte; DO_NOT_OPTIMIZE(diff); } return diff; } LIB_EXPORT bool l_secure_memeq(const void *field, size_t size, uint8_t byte) { return __secure_memeq(field, size, byte) == 0 ? true : false; } static int safe_atou(const char *s, int base, unsigned int *out_u) { unsigned long int r; unsigned int t; char *endp; errno = 0; t = r = strtoul(s, &endp, base); if (unlikely(errno > 0)) return -errno; if (endp == s || *endp != '\0') return -EINVAL; if (unlikely(r != t)) return -ERANGE; if (out_u) *out_u = t; return 0; } LIB_EXPORT int l_safe_atou32(const char *s, uint32_t *out_u) { if (unlikely(!s)) return -EINVAL; if (!l_ascii_isdigit(s[0])) return -EINVAL; /* Don't allow leading zeros */ if (s[0] == '0' && s[1] != '\0') return -EINVAL; return safe_atou(s, 10, out_u); } LIB_EXPORT int l_safe_atox8(const char *s, uint8_t *out_x) { uint32_t x; int r; r = l_safe_atox32(s, &x); if (r < 0) return r; if (x > UINT8_MAX) return -ERANGE; if (out_x) *out_x = x; return 0; } LIB_EXPORT int l_safe_atox16(const char *s, uint16_t *out_x) { uint32_t x; int r; r = l_safe_atox32(s, &x); if (r < 0) return r; if (x > UINT16_MAX) return -ERANGE; if (out_x) *out_x = x; return 0; } LIB_EXPORT int l_safe_atox32(const char *s, uint32_t *out_x) { if (unlikely(!s)) return -EINVAL; if (!l_ascii_isxdigit(s[0])) return -EINVAL; return safe_atou(s, 16, out_x); } LIB_EXPORT size_t l_util_pagesize(void) { static size_t page_size = 0; if (likely(page_size > 0)) return page_size; page_size = sysconf(_SC_PAGESIZE); return page_size; } bluez-5.82/ell/PaxHeaders/ecc.c0000644000000000000000000000005014770744372013337 xustar0020 atime=1743575540 20 ctime=1743591277 bluez-5.82/ell/ecc.c0000644000000000000000000007164614770744372013036 0ustar00rootroot/* * Embedded Linux library * Copyright (C) 2018 Intel Corporation * * SPDX-License-Identifier: LGPL-2.1-or-later */ #ifdef HAVE_CONFIG_H #include #endif #define _GNU_SOURCE #include #include #include #include #include #include #include "ecc.h" #include "ecc-private.h" #include "random.h" #include "useful.h" #include "private.h" #include "missing.h" /* * RFC 5114 - Section 2.4 192-bit Random ECP Group */ #define P192_CURVE_P { 0xFFFFFFFFFFFFFFFFull, 0xFFFFFFFFFFFFFFFEull, \ 0xFFFFFFFFFFFFFFFFull } #define P192_CURVE_GX { 0xF4FF0AFD82FF1012ull, 0x7CBF20EB43A18800ull, \ 0x188DA80EB03090F6ull } #define P192_CURVE_GY { 0x73F977A11E794811ull, 0x631011ED6B24CDD5ull, \ 0x07192B95FFC8DA78ull } #define P192_CURVE_N { 0x146BC9B1B4D22831ull, 0xFFFFFFFF99DEF836ull, \ 0xFFFFFFFFFFFFFFFFull } #define P192_CURVE_B { 0xFEB8DEECC146B9B1ull, 0x0FA7E9AB72243049ull, \ 0x64210519E59C80E7ull } static const struct l_ecc_curve p192 = { .name = "secp192r1", .ike_group = 25, .tls_group = 19, .ndigits = 3, .g = { .x = P192_CURVE_GX, .y = P192_CURVE_GY, .curve = &p192 }, .p = P192_CURVE_P, .n = P192_CURVE_N, .b = P192_CURVE_B, }; /* * RFC 5114 - Section 2.5 224-bit Random ECP Group */ #define P224_CURVE_P { 0x0000000000000001ull, 0xFFFFFFFF00000000ull, \ 0xFFFFFFFFFFFFFFFFull, 0xFFFFFFFFull } #define P224_CURVE_GX { 0x343280D6115C1D21ull, 0x4A03C1D356C21122ull, \ 0x6BB4BF7F321390B9ull, 0xB70E0CBDull } #define P224_CURVE_GY { 0x44D5819985007E34ull, 0xCD4375A05A074764ull, \ 0xB5F723FB4C22DFE6ull, 0xBD376388ull } #define P224_CURVE_N { 0x13DD29455C5C2A3Dull, 0xFFFF16A2E0B8F03Eull, \ 0xFFFFFFFFFFFFFFFFull, 0xFFFFFFFFull } #define P224_CURVE_B { 0x270B39432355FFB4ull, 0x5044B0B7D7BFD8BAull, \ 0x0C04B3ABF5413256ull, 0xB4050A85ull } static const struct l_ecc_curve p224 = { .name = "secp224r1", .ike_group = 26, .tls_group = 21, .ndigits = 4, .g = { .x = P224_CURVE_GX, .y = P224_CURVE_GY, .curve = &p224 }, .p = P224_CURVE_P, .n = P224_CURVE_N, .b = P224_CURVE_B, }; /* * RFC 5114 - Section 2.6 256-bit Random ECP Group */ #define P256_CURVE_P { 0xFFFFFFFFFFFFFFFFull, 0x00000000FFFFFFFFull, \ 0x0000000000000000ull, 0xFFFFFFFF00000001ull } #define P256_CURVE_GX { 0xF4A13945D898C296ull, 0x77037D812DEB33A0ull, \ 0xF8BCE6E563A440F2ull, 0x6B17D1F2E12C4247ull } #define P256_CURVE_GY { 0xCBB6406837BF51F5ull, 0x2BCE33576B315ECEull, \ 0x8EE7EB4A7C0F9E16ull, 0x4FE342E2FE1A7F9Bull } #define P256_CURVE_N { 0xF3B9CAC2FC632551ull, 0xBCE6FAADA7179E84ull, \ 0xFFFFFFFFFFFFFFFFull, 0xFFFFFFFF00000000ull } #define P256_CURVE_B { 0x3BCE3C3E27D2604Bull, 0x651D06B0CC53B0F6ull, \ 0xB3EBBD55769886BCull, 0x5AC635D8AA3A93E7ull } static const struct l_ecc_curve p256 = { .name = "secp256r1", .ike_group = 19, .tls_group = 23, .ndigits = 4, .g = { .x = P256_CURVE_GX, .y = P256_CURVE_GY, .curve = &p256 }, .p = P256_CURVE_P, .n = P256_CURVE_N, .b = P256_CURVE_B, .z = -10, }; /* * RFC 5114 - Section 2.7 384-bit Random ECP Group */ #define P384_CURVE_P { 0x00000000FFFFFFFFull, 0xFFFFFFFF00000000ull, \ 0xFFFFFFFFFFFFFFFEull, 0xFFFFFFFFFFFFFFFFull, \ 0xFFFFFFFFFFFFFFFFull, 0xFFFFFFFFFFFFFFFFull } #define P384_CURVE_GX { 0x3A545E3872760AB7ull, 0x5502F25DBF55296Cull, \ 0x59F741E082542A38ull, 0x6E1D3B628BA79B98ull, \ 0x8EB1C71EF320AD74ull, 0xAA87CA22BE8B0537ull } #define P384_CURVE_GY { 0x7A431D7C90EA0E5Full, 0x0A60B1CE1D7E819Dull, \ 0xE9DA3113B5F0B8C0ull, 0xF8F41DBD289A147Cull, \ 0x5D9E98BF9292DC29ull, 0x3617DE4A96262C6Full } #define P384_CURVE_N { 0xECEC196ACCC52973ull, 0x581A0DB248B0A77Aull, \ 0xC7634D81F4372DDFull, 0xFFFFFFFFFFFFFFFFull, \ 0xFFFFFFFFFFFFFFFFull, 0xFFFFFFFFFFFFFFFFull } #define P384_CURVE_B { 0x2A85C8EDD3EC2AEFull, 0xC656398D8A2ED19Dull, \ 0x0314088F5013875Aull, 0x181D9C6EFE814112ull, \ 0x988E056BE3F82D19ull, 0xB3312FA7E23EE7E4ull } static const struct l_ecc_curve p384 = { .name = "secp384r1", .ike_group = 20, .tls_group = 24, .ndigits = 6, .g = { .x = P384_CURVE_GX, .y = P384_CURVE_GY, .curve = &p384 }, .p = P384_CURVE_P, .n = P384_CURVE_N, .b = P384_CURVE_B, .z = -12, }; /* * RFC 5114 - Section 2.8 521-bit Random ECP Group */ #define P521_CURVE_P { 0xFFFFFFFFFFFFFFFFull, 0xFFFFFFFFFFFFFFFFull, \ 0xFFFFFFFFFFFFFFFFull, 0xFFFFFFFFFFFFFFFFull, \ 0xFFFFFFFFFFFFFFFFull, 0xFFFFFFFFFFFFFFFFull, \ 0xFFFFFFFFFFFFFFFFull, 0xFFFFFFFFFFFFFFFFull, \ 0x000001FFull } #define P521_CURVE_GX { 0xF97E7E31C2E5BD66ull, 0x3348B3C1856A429Bull, \ 0xFE1DC127A2FFA8DEull, 0xA14B5E77EFE75928ull, \ 0xF828AF606B4D3DBAull, 0x9C648139053FB521ull, \ 0x9E3ECB662395B442ull, 0x858E06B70404E9CDull, \ 0x000000C6ull } #define P521_CURVE_GY { 0x88BE94769FD16650ull, 0x353C7086A272C240ull, \ 0xC550B9013FAD0761ull, 0x97EE72995EF42640ull, \ 0x17AFBD17273E662Cull, 0x98F54449579B4468ull, \ 0x5C8A5FB42C7D1BD9ull, 0x39296A789A3BC004ull, \ 0x00000118ull } #define P521_CURVE_N { 0xBB6FB71E91386409ull, 0x3BB5C9B8899C47AEull, \ 0x7FCC0148F709A5D0ull, 0x51868783BF2F966Bull, \ 0xFFFFFFFFFFFFFFFAull, 0xFFFFFFFFFFFFFFFFull, \ 0xFFFFFFFFFFFFFFFFull, 0xFFFFFFFFFFFFFFFFull, \ 0x000001FFull } #define P521_CURVE_B { 0xEF451FD46B503F00ull, 0x3573DF883D2C34F1ull, \ 0x1652C0BD3BB1BF07ull, 0x56193951EC7E937Bull, \ 0xB8B489918EF109E1ull, 0xA2DA725B99B315F3ull, \ 0x929A21A0B68540EEull, 0x953EB9618E1C9A1Full, \ 0x00000051ull } static const struct l_ecc_curve p521 = { .name = "secp521r1", .ike_group = 21, .tls_group = 25, .ndigits = 9, .g = { .x = P521_CURVE_GX, .y = P521_CURVE_GY, .curve = &p521 }, .p = P521_CURVE_P, .n = P521_CURVE_N, .b = P521_CURVE_B, }; static const struct l_ecc_curve *curves[] = { &p384, &p256, &p521, &p224, &p192, }; /* Returns supported IKE groups, sorted by the highest effective key size */ LIB_EXPORT const unsigned int *l_ecc_supported_ike_groups(void) { static unsigned int supported_ike_groups[L_ARRAY_SIZE(curves) + 1]; static bool ike_first = true; if (ike_first) { unsigned int i; for (i = 0; i < L_ARRAY_SIZE(curves); i++) supported_ike_groups[i] = curves[i]->ike_group; supported_ike_groups[i] = 0; ike_first = false; } return supported_ike_groups; } /* Returns supported TLS groups, sorted by the highest effective key size */ LIB_EXPORT const unsigned int *l_ecc_supported_tls_groups(void) { static unsigned int supported_tls_groups[L_ARRAY_SIZE(curves) + 1]; static bool tls_first = true; if (tls_first) { unsigned int i; for (i = 0; i < L_ARRAY_SIZE(curves); i++) supported_tls_groups[i] = curves[i]->tls_group; supported_tls_groups[i] = 0; tls_first = false; } return supported_tls_groups; } LIB_EXPORT const struct l_ecc_curve *l_ecc_curve_from_name(const char *name) { int i; if (unlikely(!name)) return NULL; for (i = 0; curves[i]; i++) { if (!strcmp(curves[i]->name, name)) return curves[i]; } return NULL; } LIB_EXPORT const struct l_ecc_curve *l_ecc_curve_from_ike_group( unsigned int group) { unsigned int i; for (i = 0; i < L_ARRAY_SIZE(curves); i++) { if (curves[i]->ike_group == group) return curves[i]; } return NULL; } LIB_EXPORT const struct l_ecc_curve *l_ecc_curve_from_tls_group( unsigned int group) { unsigned int i; for (i = 0; i < L_ARRAY_SIZE(curves); i++) { if (curves[i]->tls_group == group) return curves[i]; } return NULL; } LIB_EXPORT const char *l_ecc_curve_get_name(const struct l_ecc_curve *curve) { if (unlikely(!curve)) return NULL; return curve->name; } LIB_EXPORT unsigned int l_ecc_curve_get_ike_group( const struct l_ecc_curve *curve) { if (unlikely(!curve)) return 0; return curve->ike_group; } LIB_EXPORT unsigned int l_ecc_curve_get_tls_group( const struct l_ecc_curve *curve) { if (unlikely(!curve)) return 0; return curve->tls_group; } LIB_EXPORT struct l_ecc_scalar *l_ecc_curve_get_order( const struct l_ecc_curve *curve) { return _ecc_constant_new(curve, curve->n, curve->ndigits * 8); } LIB_EXPORT struct l_ecc_scalar *l_ecc_curve_get_prime( const struct l_ecc_curve *curve) { if (unlikely(!curve)) return NULL; return _ecc_constant_new(curve, curve->p, curve->ndigits * 8); } LIB_EXPORT size_t l_ecc_curve_get_scalar_bytes(const struct l_ecc_curve *curve) { if (unlikely(!curve)) return 0; return curve->ndigits * 8; } static bool ecc_valid_point(struct l_ecc_point *point) { const struct l_ecc_curve *curve = point->curve; uint64_t tmp1[L_ECC_MAX_DIGITS]; uint64_t tmp2[L_ECC_MAX_DIGITS]; uint64_t _3[L_ECC_MAX_DIGITS] = { 3 }; /* -a = 3 */ unsigned int ndigits = curve->ndigits; /* The point at infinity is invalid. */ if (_ecc_point_is_zero(point)) return false; /* x and y must be smaller than p. */ if (_vli_cmp(curve->p, point->x, ndigits) != 1 || _vli_cmp(curve->p, point->y, ndigits) != 1) return false; /* Computes result = y^2. */ _vli_mod_square_fast(tmp1, point->y, curve->p, ndigits); /* Computes result = x^3 + ax + b. result must not overlap x. */ /* r = x^2 */ _vli_mod_square_fast(tmp2, point->x, curve->p, ndigits); /* r = x^2 - 3 */ _vli_mod_sub(tmp2, tmp2, _3, curve->p, ndigits); /* r = x^3 - 3x */ _vli_mod_mult_fast(tmp2, tmp2, point->x, curve->p, ndigits); /* r = x^3 - 3x + b */ _vli_mod_add(tmp2, tmp2, curve->b, curve->p, ndigits); /* Make sure that y^2 == x^3 + ax + b */ return (_vli_cmp(tmp1, tmp2, ndigits) == 0); } void _ecc_be2native(uint64_t *dest, const uint64_t *bytes, unsigned int ndigits) { unsigned int i; uint64_t tmp[2 * L_ECC_MAX_DIGITS]; for (i = 0; i < ndigits; i++) tmp[ndigits - 1 - i] = l_get_be64(&bytes[i]); memcpy(dest, tmp, ndigits * 8); } void _ecc_native2be(uint64_t *dest, const uint64_t *native, unsigned int ndigits) { unsigned int i; uint64_t tmp[L_ECC_MAX_DIGITS]; for (i = 0; i < ndigits; i++) l_put_be64(native[ndigits - 1 - i], &tmp[i]); memcpy(dest, tmp, ndigits * 8); } static void ecc_compute_y_sqr(const struct l_ecc_curve *curve, uint64_t *y_sqr, const uint64_t *x) { uint64_t sum[L_ECC_MAX_DIGITS] = { 0 }; uint64_t tmp[L_ECC_MAX_DIGITS] = { 0 }; uint64_t _3[L_ECC_MAX_DIGITS] = { 3ull }; /* -a = 3 */ /* x^3 */ _vli_mod_square_fast(sum, x, curve->p, curve->ndigits); _vli_mod_mult_fast(sum, sum, x, curve->p, curve->ndigits); /* x^3 - ax */ _vli_mod_mult_fast(tmp, _3, x, curve->p, curve->ndigits); _vli_mod_sub(sum, sum, tmp, curve->p, curve->ndigits); /* x^3 - ax + b */ _vli_mod_add(sum, sum, curve->b, curve->p, curve->ndigits); memcpy(y_sqr, sum, curve->ndigits * 8); } /* * Compute sqrt(y^2) * Since our prime p satisfies p = 3 (mod 4), we can say: * * y = (y^2)^((p + 1) / 4) * * This avoids the need for a square root function. */ static void ecc_compute_sqrt(const struct l_ecc_curve *curve, uint64_t *y, const uint64_t *y_sqr) { uint64_t expo[L_ECC_MAX_DIGITS]; uint64_t one[L_ECC_MAX_DIGITS] = { 1ull }; memcpy(expo, curve->p, curve->ndigits * 8); /* (p + 1) / 4 == (p >> 2) + 1 */ _vli_rshift1(expo, curve->ndigits); _vli_rshift1(expo, curve->ndigits); _vli_mod_add(expo, expo, one, curve->p, curve->ndigits); /* sum ^ ((p + 1) / 4) */ _vli_mod_exp(y, y_sqr, expo, curve->p, curve->ndigits); } bool _ecc_compute_y(const struct l_ecc_curve *curve, uint64_t *y, const uint64_t *x) { uint64_t sum[L_ECC_MAX_DIGITS] = { 0 }; uint64_t check[L_ECC_MAX_DIGITS] = { 0 }; /* y = sqrt(x^3 + ax + b) (mod p) */ ecc_compute_y_sqr(curve, sum, x); ecc_compute_sqrt(curve, y, sum); /* square y to ensure we have a correct value */ _vli_mod_mult_fast(check, y, y, curve->p, curve->ndigits); if (_vli_cmp(check, sum, curve->ndigits) != 0) return false; return true; } /* * IETF - Compact representation of an elliptic curve point: * https://tools.ietf.org/id/draft-jivsov-ecc-compact-00.xml * * "min(y,p-y) can be calculated with the help of the pre-calculated value * p2=(p-1)/2. min(y,p-y) is y if yp, one, curve->p, curve->ndigits); _vli_rshift1(p2, curve->ndigits); } /* * IETF draft-jivsov-ecc-compact-00 Section 4.1 * Encoding and decoding of an elliptic curve point * ... * Decoding: * Given the compact representation of Q, return canonical representation * of Q=(x,y) as follows: * 1. y' = sqrt( x^3 + a*x + b ), where y'>0 * 2. y = min(y',p-y') * 3. Q=(x,y) is the canonical representation of the point */ static bool decode_point(const struct l_ecc_curve *curve, uint64_t *x, struct l_ecc_point *point) { uint64_t y_min[L_ECC_MAX_DIGITS]; uint64_t p2[L_ECC_MAX_DIGITS]; if (!_ecc_compute_y(curve, y_min, (uint64_t *)x)) return false; _ecc_calculate_p2(curve, p2); if (_vli_cmp(y_min, p2, curve->ndigits) >= 0) _vli_mod_sub(point->y, curve->p, y_min, curve->p, curve->ndigits); else memcpy(point->y, y_min, curve->ndigits * 8); memcpy(point->x, x, curve->ndigits * 8); return true; } /* (rx, ry) = (px, py) + (qx, qy) */ void _ecc_point_add(struct l_ecc_point *ret, const struct l_ecc_point *p, const struct l_ecc_point *q, const uint64_t *curve_prime) { /* * s = (py - qy)/(px - qx) * * rx = s^2 - px - qx * ry = s(px - rx) - py */ uint64_t s[L_ECC_MAX_DIGITS]; uint64_t kp1[L_ECC_MAX_DIGITS]; uint64_t kp2[L_ECC_MAX_DIGITS]; uint64_t resx[L_ECC_MAX_DIGITS]; uint64_t resy[L_ECC_MAX_DIGITS]; unsigned int ndigits = p->curve->ndigits; memset(s, 0, ndigits * 8); /* kp1 = py - qy */ _vli_mod_sub(kp1, q->y, p->y, curve_prime, ndigits); /* kp2 = px - qx */ _vli_mod_sub(kp2, q->x, p->x, curve_prime, ndigits); /* s = kp1/kp2 */ _vli_mod_inv(kp2, kp2, curve_prime, ndigits); _vli_mod_mult_fast(s, kp1, kp2, curve_prime, ndigits); /* rx = s^2 - px - qx */ _vli_mod_mult_fast(kp1, s, s, curve_prime, ndigits); _vli_mod_sub(kp1, kp1, p->x, curve_prime, ndigits); _vli_mod_sub(resx, kp1, q->x, curve_prime, ndigits); /* ry = s(px - rx) - py */ _vli_mod_sub(kp1, p->x, resx, curve_prime, ndigits); _vli_mod_mult_fast(kp1, s, kp1, curve_prime, ndigits); _vli_mod_sub(resy, kp1, p->y, curve_prime, ndigits); memcpy(ret->x, resx, ndigits * 8); memcpy(ret->y, resy, ndigits * 8); } /* result = (base ^ exp) % p */ void _vli_mod_exp(uint64_t *result, const uint64_t *base, const uint64_t *exp, const uint64_t *mod, unsigned int ndigits) { unsigned int i; int bit; uint64_t n[L_ECC_MAX_DIGITS]; uint64_t r[L_ECC_MAX_DIGITS] = { 1 }; memcpy(n, base, ndigits * 8); for (i = 0; i < ndigits; i++) { for (bit = 0; bit < 64; bit++) { uint64_t tmp[L_ECC_MAX_DIGITS]; if (exp[i] & (1ull << bit)) { _vli_mod_mult_fast(tmp, r, n, mod, ndigits); memcpy(r, tmp, ndigits * 8); } _vli_mod_mult_fast(tmp, n, n, mod, ndigits); memcpy(n, tmp, ndigits * 8); } } memcpy(result, r, ndigits * 8); } __attribute__((noinline)) static int vli_equal(const uint64_t *a, const uint64_t *b, unsigned int ndigits) { uint64_t diff = 0; unsigned int i; for (i = 0; i < ndigits; i++) { diff |= a[i] ^ b[i]; __asm__ ("" : "=r" (diff) : "0" (diff)); } return (~diff & (diff - 1)) >> 63; } int _vli_legendre(uint64_t *val, const uint64_t *p, unsigned int ndigits) { uint64_t tmp[L_ECC_MAX_DIGITS]; uint64_t exp[L_ECC_MAX_DIGITS]; uint64_t _1[L_ECC_MAX_DIGITS] = { 1ull }; uint64_t _0[L_ECC_MAX_DIGITS] = { 0 }; /* check that val ^ ((p - 1) / 2) == [1, 0 or -1] */ _vli_sub(exp, p, _1, ndigits); _vli_rshift1(exp, ndigits); _vli_mod_exp(tmp, val, exp, p, ndigits); if (_vli_cmp(tmp, _1, ndigits) == 0) return 1; if (_vli_cmp(tmp, _0, ndigits) == 0) return 0; return -1; } bool _vli_is_zero_or_one(const uint64_t *vli, unsigned int ndigits) { uint64_t _1[L_ECC_MAX_DIGITS] = { 1ull }; int ret; ret = secure_select(vli_equal(vli, _1, ndigits), true, false); ret = secure_select(l_secure_memeq(vli, ndigits * 8, 0), true, ret); return ret; } LIB_EXPORT struct l_ecc_point *l_ecc_point_new(const struct l_ecc_curve *curve) { struct l_ecc_point *p = l_new(struct l_ecc_point, 1); p->curve = curve; return p; } LIB_EXPORT struct l_ecc_point *l_ecc_point_from_data( const struct l_ecc_curve *curve, enum l_ecc_point_type type, const void *data, size_t len) { struct l_ecc_point *p; size_t bytes = curve->ndigits * 8; uint64_t tmp[L_ECC_MAX_DIGITS]; bool sub; if (!data) return NULL; /* Verify the data length matches a full point or X coordinate */ if (type == L_ECC_POINT_TYPE_FULL) { if (len != bytes * 2) return NULL; } else if (len != bytes) return NULL; p = l_ecc_point_new(curve); _ecc_be2native(p->x, (void *) data, curve->ndigits); switch (type) { case L_ECC_POINT_TYPE_COMPLIANT: if (!decode_point(curve, p->x, p)) goto failed; break; case L_ECC_POINT_TYPE_COMPRESSED_BIT0: case L_ECC_POINT_TYPE_COMPRESSED_BIT1: if (!_ecc_compute_y(curve, p->y, p->x)) goto failed; /* * This is determining whether or not to subtract the Y * coordinate from P. According to ANSI X9.62 an even Y should * be prefixed with 02 (BIT0) and an odd Y should be prefixed * with 03 (BIT1). If this is not the case, subtract Y from P. * * ANSI X9.62 * 4.3.6 Point-to-Octet-String Conversion * * 2. If the compressed form is used, then do the following: * 2.1. Compute the bit ~Yp . (See Section 4.2.) * 2.2. Assign the value 02 to the single octet PC if ~Yp * is 0, or the value 03 if ~Yp is 1. * 2.3. The result is the octet string PO = PC || X */ sub = secure_select(type == L_ECC_POINT_TYPE_COMPRESSED_BIT0, p->y[0] & 1, !(p->y[0] & 1)); _vli_mod_sub(tmp, curve->p, p->y, curve->p, curve->ndigits); l_secure_select(sub, tmp, p->y, p->y, curve->ndigits * 8); break; case L_ECC_POINT_TYPE_FULL: _ecc_be2native(p->y, (void *) data + bytes, curve->ndigits); if (!ecc_valid_point(p)) goto failed; break; } return p; failed: l_free(p); return NULL; } LIB_EXPORT struct l_ecc_point *l_ecc_point_from_sswu( const struct l_ecc_scalar *u) { const struct l_ecc_curve *curve = u->curve; unsigned int ndigits = curve->ndigits; uint64_t z[L_ECC_MAX_DIGITS] = { abs(curve->z) }; uint64_t _3[L_ECC_MAX_DIGITS] = { 3ull }; /* -a = 3 */ uint64_t u2z[L_ECC_MAX_DIGITS]; uint64_t t1[L_ECC_MAX_DIGITS]; uint64_t t2[L_ECC_MAX_DIGITS]; uint64_t m[L_ECC_MAX_DIGITS]; uint64_t t[L_ECC_MAX_DIGITS]; uint64_t x1l[L_ECC_MAX_DIGITS]; uint64_t x1r[L_ECC_MAX_DIGITS]; uint64_t x1[L_ECC_MAX_DIGITS]; uint64_t gx1[L_ECC_MAX_DIGITS]; uint64_t x2[L_ECC_MAX_DIGITS]; uint64_t gx2[L_ECC_MAX_DIGITS]; /* reuse m/t/x1l,x1r, they are unused by the time x/v/y/p-y is needed */ uint64_t *x = m; uint64_t *v = t; uint64_t *yl = x1l; uint64_t *yr = x1r; bool l; struct l_ecc_point *P; /* * m = (z^2 * u^4 + z * u^2) modulo p * u2z = u^2 * z * t2 = u2z^2 * m = t2 - u2z since for all our curves z is negative */ _vli_mod_square_fast(u2z, u->c, curve->p, ndigits); _vli_mod_mult_fast(u2z, u2z, z, curve->p, ndigits); _vli_mod_square_fast(t2, u2z, curve->p, ndigits); _vli_mod_sub(m, t2, u2z, curve->p, ndigits); /* * l = CEQ(m, 0) * t = inv0(m) where inv0(x) is calculated as x^(p-2) modulo p */ l = l_secure_memeq(m, sizeof(m), 0); memset(t2, 0, sizeof(t2)); t2[0] = 2ull; _vli_mod_sub(t1, curve->p, t2, curve->p, ndigits); _vli_mod_exp(t, m, t1, curve->p, ndigits); /* Calculate: b / z*a, both z and a are negative */ _vli_mod_mult_fast(t1, z, _3, curve->p, ndigits); _vli_mod_inv(t1, t1, curve->p, ndigits); _vli_mod_mult_fast(x1l, curve->b, t1, curve->p, ndigits); /* t = 1 + t */ memset(t2, 0, sizeof(t2)); t2[0] = 1ull; _vli_mod_add(t, t, t2, curve->p, ndigits); /* t1 = 1 / a */ _vli_mod_inv(t1, _3, curve->p, ndigits); /* x1r = b * t1 * t */ _vli_mod_mult_fast(x1r, curve->b, t1, curve->p, ndigits); _vli_mod_mult_fast(x1r, x1r, t, curve->p, ndigits); /* x1 = CSEL(l, (b / (z*a) modulo p), ((-b/a) * (1 + t)) modulo p) */ l_secure_select(l, x1l, x1r, x1, ndigits * 8); /* gx1 = (x1^3 + a*x1 + b) modulo p */ ecc_compute_y_sqr(curve, gx1, x1); /* x2 = (z*u^2*x1) modulo p, z is negative, hence the second op */ _vli_mod_mult_fast(x2, u2z, x1, curve->p, ndigits); _vli_mod_sub(x2, curve->p, x2, curve->p, ndigits); /* gx2 = (x2^3 + a*x2 + b) modulo p */ ecc_compute_y_sqr(curve, gx2, x2); /* * l = gx1 is a quadratic residue modulo p * x is a quadratic residue if x^((p-1)/2) modulo p is zero or one */ _vli_mod_sub(t1, curve->p, t2, curve->p, ndigits); _vli_rshift1(t1, ndigits); _vli_mod_exp(t2, gx1, t1, curve->p, ndigits); l = _vli_is_zero_or_one(t2, ndigits); /* v = CSEL(l, gx1, gx2) */ l_secure_select(l, gx1, gx2, v, ndigits * 8); /* x = CSEL(l, x1, x2) */ l_secure_select(l, x1, x2, x, ndigits * 8); /* y = sqrt(v) */ ecc_compute_sqrt(curve, yl, v); /* l = CEQ(LSB(u), LSB(y)) */ l = !((u->c[0] & 1ull) ^ (yl[0] & 1ull)); /* p - y */ _vli_mod_sub(yr, curve->p, yl, curve->p, ndigits); /* P = CSEL(l, (x,y), (x, p-y)) */ P = l_ecc_point_new(curve); memcpy(P->x, x, ndigits * 8); l_secure_select(l, yl, yr, P->y, ndigits * 8); return P; } LIB_EXPORT struct l_ecc_point *l_ecc_point_clone(const struct l_ecc_point *p) { if (!p) return NULL; return l_memdup(p, sizeof(*p)); } LIB_EXPORT const struct l_ecc_curve *l_ecc_point_get_curve( const struct l_ecc_point *p) { if (!p) return NULL; return p->curve; } LIB_EXPORT ssize_t l_ecc_point_get_x(const struct l_ecc_point *p, void *x, size_t xlen) { if (xlen < p->curve->ndigits * 8) return -EMSGSIZE; _ecc_native2be(x, p->x, p->curve->ndigits); return p->curve->ndigits * 8; } LIB_EXPORT ssize_t l_ecc_point_get_y(const struct l_ecc_point *p, void *y, size_t ylen) { if (ylen < p->curve->ndigits * 8) return -EMSGSIZE; _ecc_native2be(y, p->y, p->curve->ndigits); return p->curve->ndigits * 8; } LIB_EXPORT bool l_ecc_point_y_isodd(const struct l_ecc_point *p) { return p->y[0] & 1; } LIB_EXPORT ssize_t l_ecc_point_get_data(const struct l_ecc_point *p, void *buf, size_t len) { if (len < (p->curve->ndigits * 8) * 2) return -EMSGSIZE; _ecc_native2be(buf, (uint64_t *) p->x, p->curve->ndigits); _ecc_native2be(buf + (p->curve->ndigits * 8), (uint64_t *) p->y, p->curve->ndigits); return (p->curve->ndigits * 8) * 2; } LIB_EXPORT void l_ecc_point_free(struct l_ecc_point *p) { if (unlikely(!p)) return; explicit_bzero(p->x, p->curve->ndigits * 8); explicit_bzero(p->y, p->curve->ndigits * 8); l_free(p); } struct l_ecc_scalar *_ecc_constant_new(const struct l_ecc_curve *curve, const void *buf, size_t len) { struct l_ecc_scalar *c; if (unlikely(!curve)) return NULL; if (buf && len != curve->ndigits * 8) return NULL; c = l_new(struct l_ecc_scalar, 1); c->curve = curve; if (buf) memcpy(c->c, buf, len); return c; } LIB_EXPORT struct l_ecc_scalar *l_ecc_scalar_new( const struct l_ecc_curve *curve, const void *buf, size_t len) { struct l_ecc_scalar *c; c = _ecc_constant_new(curve, NULL, 0); if (!c) return NULL; if (!buf) return c; _ecc_be2native(c->c, buf, curve->ndigits); if (!_vli_is_zero_or_one(c->c, curve->ndigits) && secure_memcmp_64(curve->n, c->c, curve->ndigits) > 0) return c; l_ecc_scalar_free(c); return NULL; } LIB_EXPORT struct l_ecc_scalar *l_ecc_scalar_clone(const struct l_ecc_scalar *s) { if (!s) return NULL; return l_memdup(s, sizeof(*s)); } /* * Build a scalar = value modulo p where p is the prime number for a given * curve. bytes can contain a number with up to 2x number of digits as the * curve. This is used in Hash to Curve calculations. */ LIB_EXPORT struct l_ecc_scalar *l_ecc_scalar_new_modp( const struct l_ecc_curve *curve, const void *bytes, size_t len) { struct l_ecc_scalar *c; uint64_t tmp[2 * L_ECC_MAX_DIGITS]; unsigned int ndigits = len / 8; if (!bytes) return NULL; if (len % 8) return NULL; if (ndigits > curve->ndigits * 2) return NULL; c = _ecc_constant_new(curve, NULL, 0); if (!c) return NULL; memset(tmp, 0, sizeof(tmp)); _ecc_be2native(tmp, bytes, ndigits); _vli_mmod_fast(c->c, tmp, curve->p, curve->ndigits); if (!_vli_is_zero_or_one(c->c, curve->ndigits) && secure_memcmp_64(curve->n, c->c, curve->ndigits) > 0) return c; l_ecc_scalar_free(c); return NULL; } LIB_EXPORT struct l_ecc_scalar *l_ecc_scalar_new_modn( const struct l_ecc_curve *curve, const void *bytes, size_t len) { struct l_ecc_scalar *c; uint64_t tmp[2 * L_ECC_MAX_DIGITS]; unsigned int ndigits = len / 8; if (!bytes) return NULL; if (len % 8) return NULL; if (ndigits > curve->ndigits * 2) return NULL; c = _ecc_constant_new(curve, NULL, 0); if (!c) return NULL; memset(tmp, 0, sizeof(tmp)); _ecc_be2native(tmp, bytes, ndigits); _vli_mmod_slow(c->c, tmp, curve->n, curve->ndigits); if (!_vli_is_zero_or_one(c->c, curve->ndigits) && secure_memcmp_64(curve->n, c->c, curve->ndigits) > 0) return c; l_ecc_scalar_free(c); return NULL; } /* * Takes a buffer of the same size as the curve and scales it to a range * 1..n using value = (value mod (n - 1)) + 1. For the curves we support * this can be done using a subtraction operation due to the size of n */ LIB_EXPORT struct l_ecc_scalar *l_ecc_scalar_new_reduced_1_to_n( const struct l_ecc_curve *curve, const void *buf, size_t len) { uint64_t _1[L_ECC_MAX_DIGITS] = { 1ull }; uint64_t tmp[L_ECC_MAX_DIGITS]; struct l_ecc_scalar *c; if (!buf) return NULL; if (len != curve->ndigits * 8) return NULL; c = _ecc_constant_new(curve, NULL, 0); if (!c) return NULL; _vli_sub(tmp, curve->n, _1, curve->ndigits); _ecc_be2native(c->c, buf, curve->ndigits); if (_vli_cmp(c->c, tmp, curve->ndigits) >= 0) _vli_sub(c->c, c->c, tmp, curve->ndigits); _vli_add(c->c, c->c, _1, curve->ndigits); return c; } #define ECC_RANDOM_MAX_ITERATIONS 20 LIB_EXPORT struct l_ecc_scalar *l_ecc_scalar_new_random( const struct l_ecc_curve *curve) { int iter = 0; uint64_t r[L_ECC_MAX_DIGITS]; while (iter++ < ECC_RANDOM_MAX_ITERATIONS) { l_getrandom(r, curve->ndigits * 8); if (curve == &p521) r[8] &= 0x1ff; else if (curve == &p224) r[3] &= 0xffffffff; if (_vli_cmp(r, curve->p, curve->ndigits) > 0 || _vli_cmp(r, curve->n, curve->ndigits) > 0 || _vli_is_zero_or_one(r, curve->ndigits)) continue; return _ecc_constant_new(curve, r, curve->ndigits * 8); } return NULL; } LIB_EXPORT ssize_t l_ecc_scalar_get_data(const struct l_ecc_scalar *c, void *buf, size_t len) { if (len < c->curve->ndigits * 8) return -EMSGSIZE; _ecc_native2be(buf, (uint64_t *) c->c, c->curve->ndigits); return c->curve->ndigits * 8; } LIB_EXPORT void l_ecc_scalar_free(struct l_ecc_scalar *c) { if (unlikely(!c)) return; explicit_bzero(c->c, c->curve->ndigits * 8); l_free(c); } LIB_EXPORT bool l_ecc_scalar_add(struct l_ecc_scalar *ret, const struct l_ecc_scalar *a, const struct l_ecc_scalar *b, const struct l_ecc_scalar *mod) { if (unlikely(!ret || !a || !b || !mod)) return false; _vli_mod_add(ret->c, a->c, b->c, mod->c, a->curve->ndigits); return true; } LIB_EXPORT bool l_ecc_point_multiply(struct l_ecc_point *ret, const struct l_ecc_scalar *scalar, const struct l_ecc_point *point) { if (unlikely(!ret || !scalar || !point)) return false; _ecc_point_mult(ret, point, scalar->c, NULL, scalar->curve->p); return true; } LIB_EXPORT bool l_ecc_point_multiply_g(struct l_ecc_point *ret, const struct l_ecc_scalar *scalar) { if (unlikely(!ret || !scalar)) return false; _ecc_point_mult(ret, &scalar->curve->g, scalar->c, NULL, scalar->curve->p); return true; } LIB_EXPORT bool l_ecc_point_add(struct l_ecc_point *ret, const struct l_ecc_point *a, const struct l_ecc_point *b) { if (unlikely(!ret || !a || !b)) return false; _ecc_point_add(ret, a, b, a->curve->p); return true; } LIB_EXPORT bool l_ecc_point_inverse(struct l_ecc_point *p) { if (unlikely(!p)) return false; _vli_mod_sub(p->y, p->curve->p, p->y, p->curve->p, p->curve->ndigits); return true; } LIB_EXPORT bool l_ecc_scalar_multiply(struct l_ecc_scalar *ret, const struct l_ecc_scalar *a, const struct l_ecc_scalar *b) { if (unlikely(!ret || !a || !b)) return false; _vli_mod_mult_fast(ret->c, a->c, b->c, a->curve->p, a->curve->ndigits); return true; } LIB_EXPORT int l_ecc_scalar_legendre(struct l_ecc_scalar *value) { if (unlikely(!value)) return -1; return _vli_legendre(value->c, value->curve->p, value->curve->ndigits); } LIB_EXPORT bool l_ecc_scalar_sum_x(struct l_ecc_scalar *ret, const struct l_ecc_scalar *x) { if (unlikely(!ret || !x)) return false; ecc_compute_y_sqr(x->curve, ret->c, x->c); return true; } LIB_EXPORT bool l_ecc_scalars_are_equal(const struct l_ecc_scalar *a, const struct l_ecc_scalar *b) { if (unlikely(!a || !b)) return false; return (memcmp(a->c, b->c, a->curve->ndigits * 8) == 0); } LIB_EXPORT bool l_ecc_points_are_equal(const struct l_ecc_point *a, const struct l_ecc_point *b) { if (unlikely(!a || !b)) return false; return ((memcmp(a->x, b->x, a->curve->ndigits * 8) == 0) && (memcmp(a->y, b->y, a->curve->ndigits * 8) == 0)); } LIB_EXPORT bool l_ecc_point_is_infinity(const struct l_ecc_point *p) { return _ecc_point_is_zero(p); } bluez-5.82/ell/PaxHeaders/tls-suites.c0000644000000000000000000000005014504767710014715 xustar0020 atime=1743575536 20 ctime=1743591277 bluez-5.82/ell/tls-suites.c0000644000000000000000000011726314504767710014410 0ustar00rootroot/* * Embedded Linux library * Copyright (C) 2018 Intel Corporation * * SPDX-License-Identifier: LGPL-2.1-or-later */ #ifdef HAVE_CONFIG_H #include #endif #define _GNU_SOURCE #include #include #include #include #include "util.h" #include "tls.h" #include "cipher.h" #include "checksum.h" #include "cert.h" #include "tls-private.h" #include "key.h" #include "random.h" #include "ecc.h" #include "ecdh.h" #include "missing.h" enum signature_algorithm { SIGNATURE_ALGORITHM_ANONYMOUS = 0, SIGNATURE_ALGORITHM_RSA = 1, SIGNATURE_ALGORITHM_DSA = 2, SIGNATURE_ALGORITHM_ECDSA = 3, }; static enum handshake_hash_type find_hash_by_id(uint8_t id) { enum handshake_hash_type hash; for (hash = 0; hash < __HANDSHAKE_HASH_COUNT; hash++) if (tls_handshake_hash_data[hash].tls_id == id) break; return hash; } /* * Sanitize DigitallySigned struct input, making sure the lengths * are valid and correspond to what we expect. * * Returns: start of the opaque portion */ static const uint8_t *validate_digitally_signed(struct l_tls *tls, const uint8_t *in, size_t in_len, enum signature_algorithm expected_alg, uint16_t *opaque_len) { size_t offset = 2; uint16_t len; if (tls->negotiated_version < L_TLS_V12) offset = 0; if (in_len < offset + 2) goto size_error; len = l_get_be16(in + offset); if (len != in_len - offset - 2) goto size_error; if (tls->negotiated_version >= L_TLS_V12) { if (in[1] != expected_alg) { TLS_DISCONNECT(TLS_ALERT_DECRYPT_ERROR, 0, "Unknown signature algorithm %i", in[1]); return NULL; } } *opaque_len = len; return in + offset + 2; size_error: TLS_DISCONNECT(TLS_ALERT_DECODE_ERROR, 0, "Signature msg too " "short (%zi) or signature length doesn't match", in_len); return NULL; } static bool tls_rsa_validate_cert_key(struct l_cert *cert) { return l_cert_get_pubkey_type(cert) == L_CERT_KEY_RSA; } static ssize_t tls_rsa_sign(struct l_tls *tls, uint8_t *out, size_t out_len, tls_get_hash_t get_hash, const uint8_t *data, size_t data_len) { ssize_t result = -EMSGSIZE; enum l_checksum_type sign_checksum_type; uint8_t sign_input[HANDSHAKE_HASH_MAX_SIZE + 36]; size_t sign_input_len; uint8_t *ptr = out; if (!tls->priv_key || !tls->priv_key_size) { TLS_DISCONNECT(TLS_ALERT_INTERNAL_ERROR, TLS_ALERT_BAD_CERT, "No private key loaded"); return -ENOKEY; } if (tls->negotiated_version >= L_TLS_V12) { const struct tls_hash_algorithm *hash_type = &tls_handshake_hash_data[tls->signature_hash]; /* Build the DigitallySigned struct */ if (out_len < 2) /* Is there space for the algorithm IDs */ goto error; get_hash(tls, tls->signature_hash, data, data_len, sign_input, &sign_input_len); sign_checksum_type = hash_type->l_id; *ptr++ = hash_type->tls_id; *ptr++ = 1; /* RSA_sign */ out_len -= 2; } else { get_hash(tls, HANDSHAKE_HASH_MD5, data, data_len, sign_input + 0, NULL); get_hash(tls, HANDSHAKE_HASH_SHA1, data, data_len, sign_input + 16, NULL); sign_checksum_type = L_CHECKSUM_NONE; sign_input_len = 36; } if (out_len < tls->priv_key_size + 2) goto error; l_put_be16(tls->priv_key_size, ptr); result = l_key_sign(tls->priv_key, L_KEY_RSA_PKCS1_V1_5, sign_checksum_type, sign_input, ptr + 2, sign_input_len, tls->priv_key_size); ptr += tls->priv_key_size + 2; if (result == (ssize_t) tls->priv_key_size) return ptr - out; /* Success */ error: TLS_DISCONNECT(TLS_ALERT_INTERNAL_ERROR, 0, "Signing the hash failed: %s", strerror(-result)); return result; } static bool tls_rsa_verify(struct l_tls *tls, const uint8_t *in, size_t in_len, tls_get_hash_t get_hash, const uint8_t *data, size_t data_len) { enum l_checksum_type sign_checksum_type; uint8_t expected[HANDSHAKE_HASH_MAX_SIZE + 36]; size_t expected_len; const uint8_t *opaque; uint16_t opaque_len; bool success; opaque = validate_digitally_signed(tls, in, in_len, SIGNATURE_ALGORITHM_RSA, &opaque_len); if (!opaque) return false; /* Only the default hash type supported */ if (opaque_len != tls->peer_pubkey_size) { TLS_DISCONNECT(TLS_ALERT_DECODE_ERROR, 0, "Signature length %hu not equal %zi", opaque_len, tls->peer_pubkey_size); return false; } if (tls->negotiated_version >= L_TLS_V12) { enum handshake_hash_type hash = find_hash_by_id(in[0]); if (hash == __HANDSHAKE_HASH_COUNT) { TLS_DISCONNECT(TLS_ALERT_DECRYPT_ERROR, 0, "Unknown hash type %i", in[0]); return false; } get_hash(tls, hash, data, data_len, expected, &expected_len); sign_checksum_type = tls_handshake_hash_data[hash].l_id; /* * Note: Next we let the l_key_verify's underlying kernel * operation prepend the OID to the hash to build the * DigestInfo struct. However according to 4.7 we need to * support at least two forms of the signed content in the * verification: * - DigestInfo with NULL AlgorithmIdentifier.parameters, * - DigestInfo with empty AlgorithmIdentifier.parameters, * * while the kernel only understands the former encoding. * Note PKCS#1 versions 2.0 and later section A.2.4 do * mandate NULL AlgorithmIdentifier.parameters. * * Additionally PKCS#1 v1.5 said BER is used in place of DER * for DigestInfo encoding which adds more ambiguity in the * encoding. */ } else { get_hash(tls, HANDSHAKE_HASH_MD5, data, data_len, expected + 0, NULL); get_hash(tls, HANDSHAKE_HASH_SHA1, data, data_len, expected + 16, NULL); expected_len = 36; sign_checksum_type = L_CHECKSUM_NONE; /* * Note: Within the RSA padding for signatures PKCS#1 1.5 * allows the block format to be either 0 or 1, while PKCS#1 * v2.0+ mandates block type 1 making the signatures * unambiguous. TLS 1.0 doesn't additionally specify which * block type is to be used (TLS 1.2 does) meaning that both * PKCS#1 v1.5 types are allowed. The l_key_verify's * underlying kernel implementation only accepts block type * 1. If this ever becomes an issue we'd need to go back to * using L_KEY_RSA_RAW and our own PKCS#1 v1.5 verify logic. */ } success = l_key_verify(tls->peer_pubkey, L_KEY_RSA_PKCS1_V1_5, sign_checksum_type, expected, opaque, expected_len, tls->peer_pubkey_size); if (!success) TLS_DISCONNECT(TLS_ALERT_DECRYPT_ERROR, 0, "Peer signature verification failed"); else TLS_DEBUG("Peer signature verified"); return success; } static struct tls_signature_algorithm tls_rsa_signature = { .id = 1, /* SignatureAlgorithm.rsa */ .validate_cert_key_type = tls_rsa_validate_cert_key, .sign = tls_rsa_sign, .verify = tls_rsa_verify, }; static bool tls_ecdsa_validate_cert_key(struct l_cert *cert) { return l_cert_get_pubkey_type(cert) == L_CERT_KEY_ECC; } static bool tls_ecdsa_verify(struct l_tls *tls, const uint8_t *in, size_t in_len, tls_get_hash_t get_hash, const uint8_t *data, size_t data_len) { /* RFC 8422, Section 5.10: "SHA-1 is used in TLS 1.1 and earlier" */ enum handshake_hash_type hash = HANDSHAKE_HASH_SHA1; enum l_checksum_type sign_checksum_type; const uint8_t *opaque; uint16_t opaque_len; uint8_t expected[HANDSHAKE_HASH_MAX_SIZE]; size_t expected_len; bool success; opaque = validate_digitally_signed(tls, in, in_len, SIGNATURE_ALGORITHM_ECDSA, &opaque_len); if (!opaque) return false; if (tls->negotiated_version >= L_TLS_V12) { hash = find_hash_by_id(in[0]); if (hash == __HANDSHAKE_HASH_COUNT) { TLS_DISCONNECT(TLS_ALERT_DECRYPT_ERROR, 0, "Unknown hash type %i", in[0]); return false; } /* Hash should match the curve, refer to RFC 5480, Section 4 */ switch (tls->peer_pubkey_size) { case 32: if (hash != HANDSHAKE_HASH_SHA256 && hash != HANDSHAKE_HASH_SHA384) goto bad_hash; break; case 48: if (hash != HANDSHAKE_HASH_SHA384) goto bad_hash; break; bad_hash: default: TLS_DISCONNECT(TLS_ALERT_DECRYPT_ERROR, 0, "Invalid hash %i", in[0]); } } get_hash(tls, hash, data, data_len, expected, &expected_len); sign_checksum_type = tls_handshake_hash_data[hash].l_id; success = l_key_verify(tls->peer_pubkey, L_KEY_ECDSA_X962, sign_checksum_type, expected, opaque, expected_len, opaque_len); if (!success) TLS_DISCONNECT(TLS_ALERT_DECRYPT_ERROR, 0, "Peer signature verification failed"); else TLS_DEBUG("Peer signature verified"); return success; } static struct tls_signature_algorithm tls_ecdsa_signature = { .id = 3, /* SignatureAlgorithm.ecdsa */ .validate_cert_key_type = tls_ecdsa_validate_cert_key, .verify = tls_ecdsa_verify, }; static bool tls_send_rsa_client_key_xchg(struct l_tls *tls) { uint8_t buf[1024 + 32]; uint8_t *ptr = buf + TLS_HANDSHAKE_HEADER_SIZE; uint8_t pre_master_secret[48]; ssize_t bytes_encrypted; if (!tls->peer_pubkey) { TLS_DISCONNECT(TLS_ALERT_INTERNAL_ERROR, 0, "Peer public key not received"); return false; } /* Must match the version in tls_send_client_hello */ pre_master_secret[0] = (uint8_t) (tls->client_version >> 8); pre_master_secret[1] = (uint8_t) (tls->client_version >> 0); l_getrandom(pre_master_secret + 2, 46); if (tls->peer_pubkey_size + 32 > (int) sizeof(buf)) { TLS_DISCONNECT(TLS_ALERT_INTERNAL_ERROR, 0, "Peer public key too big: %zi", tls->peer_pubkey_size); return false; } l_put_be16(tls->peer_pubkey_size, ptr); bytes_encrypted = l_key_encrypt(tls->peer_pubkey, L_KEY_RSA_PKCS1_V1_5, L_CHECKSUM_NONE, pre_master_secret, ptr + 2, 48, tls->peer_pubkey_size); ptr += tls->peer_pubkey_size + 2; if (bytes_encrypted != (ssize_t) tls->peer_pubkey_size) { TLS_DISCONNECT(TLS_ALERT_INTERNAL_ERROR, 0, "Encrypting PreMasterSecret failed: %s", strerror(-bytes_encrypted)); return false; } tls_tx_handshake(tls, TLS_CLIENT_KEY_EXCHANGE, buf, ptr - buf); tls_generate_master_secret(tls, pre_master_secret, 48); explicit_bzero(pre_master_secret, 48); return true; } static void tls_handle_rsa_client_key_xchg(struct l_tls *tls, const uint8_t *buf, size_t len) { uint8_t pre_master_secret[48], random_secret[46]; ssize_t bytes_decrypted; if (!tls->priv_key || !tls->priv_key_size) { TLS_DISCONNECT(TLS_ALERT_INTERNAL_ERROR, TLS_ALERT_BAD_CERT, "No private key"); return; } if (len != tls->priv_key_size + 2) { TLS_DISCONNECT(TLS_ALERT_DECODE_ERROR, 0, "ClientKeyExchange len %zi not %zi", len, tls->priv_key_size + 2); return; } len = l_get_be16(buf); if (len != tls->priv_key_size) { TLS_DISCONNECT(TLS_ALERT_DECODE_ERROR, 0, "EncryptedPreMasterSecret len %zi not %zi", len, tls->priv_key_size); return; } bytes_decrypted = l_key_decrypt(tls->priv_key, L_KEY_RSA_PKCS1_V1_5, L_CHECKSUM_NONE, buf + 2, pre_master_secret, tls->priv_key_size, 48); /* * Assume correct premaster secret client version which according * to the TLS1.2 spec is unlikely in client implementations SSLv3 * and prior. Spec suggests either not supporting them or adding * a configurable override for <= SSLv3 clients. For now we have * no need to support them. * * On any decode error randomise the Pre Master Secret as per the * countermeasures in 7.4.7.1 and don't generate any alerts. */ l_getrandom(random_secret, 46); pre_master_secret[0] = tls->client_version >> 8; pre_master_secret[1] = tls->client_version >> 0; if (bytes_decrypted != 48) { memcpy(pre_master_secret + 2, random_secret, 46); TLS_DEBUG("Error decrypting PreMasterSecret: %s", strerror(-bytes_decrypted)); } tls_generate_master_secret(tls, pre_master_secret, 48); explicit_bzero(pre_master_secret, 48); explicit_bzero(random_secret, 46); } static struct tls_key_exchange_algorithm tls_rsa_key_xchg = { .send_client_key_exchange = tls_send_rsa_client_key_xchg, .handle_client_key_exchange = tls_handle_rsa_client_key_xchg, }; /* Used by both DHE and ECDHE */ static bool tls_get_dh_params_hash(struct l_tls *tls, enum handshake_hash_type type, const uint8_t *data, size_t data_len, uint8_t *out, size_t *out_len) { struct l_checksum *checksum; ssize_t ret; /* * The ServerKeyExchange signature hash input format for RSA_sign is * not really specified in either RFC 8422 or RFC 5246 explicitly * but we use this format by analogy to DHE_RSA which uses RSA_sign * as well. Also matches ecdsa, ed25519 and ed448 formats. */ struct iovec iov[] = { { .iov_base = tls->pending.client_random, .iov_len = 32 }, { .iov_base = tls->pending.server_random, .iov_len = 32 }, { .iov_base = (void *) data, .iov_len = data_len }, }; checksum = l_checksum_new(tls_handshake_hash_data[type].l_id); if (!checksum) return false; l_checksum_updatev(checksum, iov, L_ARRAY_SIZE(iov)); ret = l_checksum_get_digest(checksum, out, HANDSHAKE_HASH_MAX_SIZE); l_checksum_free(checksum); if (ret < 0) return false; if (out_len) *out_len = ret; return true; } struct tls_ecdhe_params { const struct l_ecc_curve *curve; struct l_ecc_scalar *private; struct l_ecc_point *public; }; static void tls_free_ecdhe_params(struct l_tls *tls) { struct tls_ecdhe_params *params = tls->pending.key_xchg_params; if (!params) return; tls->pending.key_xchg_params = NULL; l_ecc_scalar_free(params->private); l_ecc_point_free(params->public); l_free(params); } static size_t tls_write_ecpoint(uint8_t *buf, size_t len, const struct tls_named_group *curve, const struct l_ecc_point *point) { size_t point_bytes; /* RFC 8422, Section 5.4.1 */ point_bytes = l_ecc_point_get_data(point, buf + 2, len - 2); buf[0] = 1 + point_bytes; /* length */ buf[1] = 4; /* form: uncompressed */ return 2 + point_bytes; } static size_t tls_write_server_ecdh_params(struct l_tls *tls, uint8_t *buf, size_t len) { struct tls_ecdhe_params *params = tls->pending.key_xchg_params; /* RFC 8422, Section 5.4 */ buf[0] = 3; /* curve_type: named_curve */ l_put_be16(tls->negotiated_curve->id, buf + 1); return 3 + tls_write_ecpoint(buf + 3, len - 3, tls->negotiated_curve, params->public); } static bool tls_send_ecdhe_server_key_xchg(struct l_tls *tls) { uint8_t buf[1024]; uint8_t *ptr = buf + TLS_HANDSHAKE_HEADER_SIZE; struct tls_ecdhe_params *params; ssize_t sign_len; const uint8_t *server_ecdh_params_ptr; /* * RFC 8422, Section 5.4 * * If we're getting here we can assume that tls->pending.key_xchg_params * is NULL, tls->priv_key is our signing key and tls->negotiated_curve * is non-NULL. */ params = l_new(struct tls_ecdhe_params, 1); params->curve = l_ecc_curve_from_tls_group(tls->negotiated_curve->id); tls->pending.key_xchg_params = params; if (!l_ecdh_generate_key_pair(params->curve, ¶ms->private, ¶ms->public)) { TLS_DISCONNECT(TLS_ALERT_INTERNAL_ERROR, 0, "Generating ECDH key pair failed"); return false; } server_ecdh_params_ptr = ptr; ptr += tls_write_server_ecdh_params(tls, ptr, buf + sizeof(buf) - ptr); if (tls->pending.cipher_suite->signature) { sign_len = tls->pending.cipher_suite->signature->sign(tls, ptr, buf + sizeof(buf) - ptr, tls_get_dh_params_hash, server_ecdh_params_ptr, ptr - server_ecdh_params_ptr); if (sign_len < 0) return false; ptr += sign_len; } tls_tx_handshake(tls, TLS_SERVER_KEY_EXCHANGE, buf, ptr - buf); return true; } static void tls_handle_ecdhe_server_key_xchg(struct l_tls *tls, const uint8_t *buf, size_t len) { struct tls_ecdhe_params *params; uint16_t namedcurve; const uint8_t *server_ecdh_params_ptr = buf; size_t point_bytes; /* RFC 8422, Section 5.4 */ if (len < 5) goto decode_error; if (*buf != 3) { TLS_DISCONNECT(TLS_ALERT_ILLEGAL_PARAM, 0, "Unsupported (deprecated?) ECCurveType %u", *buf); return; } namedcurve = l_get_be16(buf + 1); buf += 3; len -= 3; tls->negotiated_curve = tls_find_group(namedcurve); if (!tls->negotiated_curve || tls->negotiated_curve->type != TLS_GROUP_TYPE_EC) { TLS_DISCONNECT(TLS_ALERT_ILLEGAL_PARAM, 0, "Unsupported NamedCurve %u", namedcurve); return; } TLS_DEBUG("Negotiated %s", tls->negotiated_curve->name); if (*buf < 1) goto decode_error; point_bytes = *buf++ - 1; if (*buf != 4) { /* uncompressed */ TLS_DISCONNECT(TLS_ALERT_ILLEGAL_PARAM, 0, "Unsupported (deprecated?) PointConversionForm " "%u", *buf); return; } buf++; len -= 2; if (len < point_bytes) goto decode_error; /* * RFC 8422, Section 5.11: "A receiving party MUST check that the * x and y parameters from the peer's public value satisfy the * curve equation, y^2 = x^3 + ax + b mod p." * This happens in l_ecc_point_from_data when the L_ECC_POINT_TYPE_FULL * format is used. */ params = l_new(struct tls_ecdhe_params, 1); params->curve = l_ecc_curve_from_tls_group(tls->negotiated_curve->id); params->public = l_ecc_point_from_data(params->curve, L_ECC_POINT_TYPE_FULL, buf, point_bytes); tls->pending.key_xchg_params = params; buf += point_bytes; len -= point_bytes; if (!params->public || point_bytes != 2 * l_ecc_curve_get_scalar_bytes(params->curve)) { TLS_DISCONNECT(TLS_ALERT_DECODE_ERROR, 0, "ServerKeyExchange.params.public decode error"); return; } if (tls->pending.cipher_suite->signature) { if (!tls->pending.cipher_suite->signature->verify(tls, buf, len, tls_get_dh_params_hash, server_ecdh_params_ptr, buf - server_ecdh_params_ptr)) return; } else { if (len) goto decode_error; } TLS_SET_STATE(TLS_HANDSHAKE_WAIT_HELLO_DONE); return; decode_error: TLS_DISCONNECT(TLS_ALERT_DECODE_ERROR, 0, "ServerKeyExchange decode error"); } static bool tls_send_ecdhe_client_key_xchg(struct l_tls *tls) { uint8_t buf[1024]; uint8_t *ptr = buf + TLS_HANDSHAKE_HEADER_SIZE; uint8_t pre_master_secret[128]; ssize_t pre_master_secret_len; struct tls_ecdhe_params *params = tls->pending.key_xchg_params; struct l_ecc_point *our_public; struct l_ecc_scalar *secret; /* RFC 8422, Section 5.7 */ if (!l_ecdh_generate_key_pair(params->curve, ¶ms->private, &our_public)) { TLS_DISCONNECT(TLS_ALERT_INTERNAL_ERROR, 0, "Generating ECDH key pair failed"); return false; } ptr += tls_write_ecpoint(ptr, buf + sizeof(buf) - ptr, tls->negotiated_curve, our_public); l_ecc_point_free(our_public); /* * Neither 5.4 or 5.7 "Actions" paragraphs say when the ECDH shared * secret is calculated but we can either do this in * tls_handle_ecdhe_server_key_xchg or here. In both cases we only * need to store the public key in the client's key_xchg_params and * can free all of the params after sending the ClientKeyExchange. * By doing this calculation here we're aligned with RSA and also * with the server mode where the shared secret can only be * calculated after the ClientKeyExchange is received. */ if (!l_ecdh_generate_shared_secret(params->private, params->public, &secret)) { TLS_DISCONNECT(TLS_ALERT_INTERNAL_ERROR, 0, "Generating ECDH shared-secret failed"); return false; } tls_free_ecdhe_params(tls); pre_master_secret_len = l_ecc_scalar_get_data(secret, pre_master_secret, sizeof(pre_master_secret)); l_ecc_scalar_free(secret); if (pre_master_secret_len < 0) { TLS_DISCONNECT(TLS_ALERT_INTERNAL_ERROR, 0, "l_ecc_scalar_get_data(secret) failed"); return false; } tls_tx_handshake(tls, TLS_CLIENT_KEY_EXCHANGE, buf, ptr - buf); tls_generate_master_secret(tls, pre_master_secret, pre_master_secret_len); explicit_bzero(pre_master_secret, pre_master_secret_len); return true; } static void tls_handle_ecdhe_client_key_xchg(struct l_tls *tls, const uint8_t *buf, size_t len) { struct tls_ecdhe_params *params = tls->pending.key_xchg_params; uint8_t pre_master_secret[128]; ssize_t pre_master_secret_len; struct l_ecc_point *other_public; struct l_ecc_scalar *secret; size_t point_bytes = 2 * l_ecc_curve_get_scalar_bytes(params->curve); /* RFC 8422, Section 5.7 */ if (len < 2) goto decode_error; if (*buf++ != 1 + point_bytes) goto decode_error; if (*buf != 4) { /* uncompressed */ TLS_DISCONNECT(TLS_ALERT_ILLEGAL_PARAM, 0, "Unsupported (deprecated?) PointConversionForm " "%u", *buf); return; } buf++; len -= 2; if (len != point_bytes) goto decode_error; /* * RFC 8422, Section 5.11: "A receiving party MUST check that the * x and y parameters from the peer's public value satisfy the * curve equation, y^2 = x^3 + ax + b mod p." * This happens in l_ecc_point_from_data when the L_ECC_POINT_TYPE_FULL * format is used. */ other_public = l_ecc_point_from_data(params->curve, L_ECC_POINT_TYPE_FULL, buf, len); if (!other_public) { TLS_DISCONNECT(TLS_ALERT_DECODE_ERROR, 0, "ClientKeyExchange.exchange_keys.ecdh_Yc " "decode error"); return; } if (!l_ecdh_generate_shared_secret(params->private, other_public, &secret)) { TLS_DISCONNECT(TLS_ALERT_INTERNAL_ERROR, 0, "Generating ECDH shared-secret failed"); return; } tls_free_ecdhe_params(tls); l_ecc_point_free(other_public); pre_master_secret_len = l_ecc_scalar_get_data(secret, pre_master_secret, sizeof(pre_master_secret)); l_ecc_scalar_free(secret); if (pre_master_secret_len < 0) { TLS_DISCONNECT(TLS_ALERT_INTERNAL_ERROR, 0, "l_ecc_scalar_get_data(secret) failed"); return; } tls_generate_master_secret(tls, pre_master_secret, pre_master_secret_len); explicit_bzero(pre_master_secret, pre_master_secret_len); return; decode_error: TLS_DISCONNECT(TLS_ALERT_DECODE_ERROR, 0, "ClientKeyExchange decode error"); } static struct tls_key_exchange_algorithm tls_ecdhe = { .need_ecc = true, .send_server_key_exchange = tls_send_ecdhe_server_key_xchg, .handle_server_key_exchange = tls_handle_ecdhe_server_key_xchg, .send_client_key_exchange = tls_send_ecdhe_client_key_xchg, .handle_client_key_exchange = tls_handle_ecdhe_client_key_xchg, .free_params = tls_free_ecdhe_params, }; /* Maximum FF DH prime modulus size in bytes */ #define TLS_DHE_MAX_SIZE 1024 struct tls_dhe_params { size_t prime_len; struct l_key *prime; struct l_key *generator; struct l_key *private; struct l_key *public; }; static void tls_free_dhe_params(struct l_tls *tls) { struct tls_dhe_params *params = tls->pending.key_xchg_params; if (!params) return; tls->pending.key_xchg_params = NULL; l_key_free(params->prime); l_key_free(params->generator); l_key_free(params->private); l_key_free(params->public); l_free(params); } static bool tls_send_dhe_server_key_xchg(struct l_tls *tls) { uint8_t buf[1024 + TLS_DHE_MAX_SIZE * 3]; uint8_t *ptr = buf + TLS_HANDSHAKE_HEADER_SIZE; struct tls_dhe_params *params; const uint8_t *prime_buf; uint8_t generator_buf = tls->negotiated_ff_group->ff.generator; uint8_t public_buf[TLS_DHE_MAX_SIZE]; size_t public_len; unsigned int zeros = 0; ssize_t sign_len; const uint8_t *server_dh_params_ptr; params = l_new(struct tls_dhe_params, 1); prime_buf = tls->negotiated_ff_group->ff.prime; params->prime_len = tls->negotiated_ff_group->ff.prime_len; params->prime = l_key_new(L_KEY_RAW, prime_buf, params->prime_len); params->generator = l_key_new(L_KEY_RAW, &generator_buf, 1); if (!params->prime || !params->generator) { TLS_DISCONNECT(TLS_ALERT_INTERNAL_ERROR, 0, "l_key_new failed"); goto free_params; } params->private = l_key_generate_dh_private(prime_buf, params->prime_len); if (!params->private) { TLS_DISCONNECT(TLS_ALERT_INTERNAL_ERROR, 0, "l_key_generate_dh_private failed"); goto free_params; } memset(public_buf, 0, sizeof(public_buf)); public_len = params->prime_len; if (!l_key_compute_dh_public(params->generator, params->private, params->prime, public_buf, &public_len)) { TLS_DISCONNECT(TLS_ALERT_INTERNAL_ERROR, 0, "l_key_compute_dh_public failed"); goto free_params; } while (zeros < public_len && public_buf[zeros] == 0x00) zeros++; server_dh_params_ptr = ptr; /* RFC 5246, Section 7.4.3 */ l_put_be16(params->prime_len, ptr); memcpy(ptr + 2, prime_buf, params->prime_len); ptr += 2 + params->prime_len; l_put_be16(1, ptr); memcpy(ptr + 2, &generator_buf, 1); ptr += 2 + 1; l_put_be16(public_len - zeros, ptr); memcpy(ptr + 2, public_buf + zeros, public_len - zeros); ptr += 2 + public_len - zeros; if (tls->pending.cipher_suite->signature) { sign_len = tls->pending.cipher_suite->signature->sign(tls, ptr, buf + sizeof(buf) - ptr, tls_get_dh_params_hash, server_dh_params_ptr, ptr - server_dh_params_ptr); if (sign_len < 0) goto free_params; ptr += sign_len; } tls->pending.key_xchg_params = params; tls_tx_handshake(tls, TLS_SERVER_KEY_EXCHANGE, buf, ptr - buf); return true; free_params: l_key_free(params->prime); l_key_free(params->generator); l_key_free(params->private); l_free(params); return false; } static void tls_handle_dhe_server_key_xchg(struct l_tls *tls, const uint8_t *buf, size_t len) { struct tls_dhe_params *params = NULL; const uint8_t *prime_buf; const uint8_t *generator_buf; size_t generator_len; const uint8_t *public_buf; size_t public_len; const uint8_t *server_dh_params_ptr = buf; if (len < 2) goto decode_error; params = l_new(struct tls_dhe_params, 1); params->prime_len = l_get_be16(buf); if (len < 2 + params->prime_len + 2) goto decode_error; prime_buf = buf + 2; buf += 2 + params->prime_len; len -= 2 + params->prime_len; /* Strip leading zeros for the length checks later */ while (params->prime_len && prime_buf[0] == 0x00) { prime_buf++; params->prime_len--; } generator_len = l_get_be16(buf); if (len < 2 + generator_len + 2) goto decode_error; generator_buf = buf + 2; buf += 2 + generator_len; len -= 2 + generator_len; public_len = l_get_be16(buf); if (len < 2 + public_len) goto decode_error; public_buf = buf + 2; buf += 2 + public_len; len -= 2 + public_len; /* * Validate the values received. Without requiring RFC 7919 from * the server, and there are many servers that don't implement it * yet, we basically have to blindly accept the provided prime value. * We have no way to confirm that it's actually prime or that it's a * "safe prime" or that it forms a group without small sub-groups. * There's also no way to whitelist all valid values. But we do a * basic sanity check and require it to be 1536-bit or longer, the * minimum length required by the Linux kernel for keyctl_dh_compute(). * The generator must also be at least within the min & max interval * for the private/public values. */ if (params->prime_len > TLS_DHE_MAX_SIZE || params->prime_len < 192 || !(prime_buf[params->prime_len - 1] & 1)) { TLS_DISCONNECT(TLS_ALERT_HANDSHAKE_FAIL, 0, "Server DH prime modulus invalid"); goto free_params; } if (!l_key_validate_dh_payload(generator_buf, generator_len, prime_buf, params->prime_len)) { TLS_DISCONNECT(TLS_ALERT_HANDSHAKE_FAIL, 0, "Server DH generator value invalid"); goto free_params; } /* * Just output a warning if the server sent group parameters not * offered in our RFC 7919 Supported Groups extension. */ if (!tls_find_ff_group(prime_buf, params->prime_len, generator_buf, generator_len)) TLS_DEBUG("Warning: using server's custom %i-bit FF DH group", (int) (params->prime_len * 8)); /* * RFC 7919 Section 3.0: * "the client MUST verify that dh_Ys is in the range * 1 < dh_Ys < dh_p - 1. If dh_Ys is not in this range, the client * MUST terminate the connection with a fatal handshake_failure(40) * alert." */ if (!l_key_validate_dh_payload(public_buf, public_len, prime_buf, params->prime_len)) { TLS_DISCONNECT(TLS_ALERT_HANDSHAKE_FAIL, 0, "Server DH public value invalid"); goto free_params; } params->prime = l_key_new(L_KEY_RAW, prime_buf, params->prime_len); params->generator = l_key_new(L_KEY_RAW, generator_buf, generator_len); params->public = l_key_new(L_KEY_RAW, public_buf, public_len); if (!params->prime || !params->generator || !params->public) { TLS_DISCONNECT(TLS_ALERT_INTERNAL_ERROR, 0, "l_key_new failed"); goto free_params; } /* Do this now so we don't need prime_buf in send_client_key_xchg */ params->private = l_key_generate_dh_private(prime_buf, params->prime_len); if (!params->private) { TLS_DISCONNECT(TLS_ALERT_INTERNAL_ERROR, 0, "l_key_generate_dh_private failed"); goto free_params; } tls->pending.key_xchg_params = params; if (tls->pending.cipher_suite->signature) { if (!tls->pending.cipher_suite->signature->verify(tls, buf, len, tls_get_dh_params_hash, server_dh_params_ptr, buf - server_dh_params_ptr)) return; } else { if (len) goto decode_error; } TLS_SET_STATE(TLS_HANDSHAKE_WAIT_HELLO_DONE); return; decode_error: TLS_DISCONNECT(TLS_ALERT_DECODE_ERROR, 0, "ServerKeyExchange decode error"); free_params: if (params) { l_key_free(params->prime); l_key_free(params->generator); l_key_free(params->public); l_free(params); } } static bool tls_send_dhe_client_key_xchg(struct l_tls *tls) { struct tls_dhe_params *params = tls->pending.key_xchg_params; uint8_t buf[128 + params->prime_len]; uint8_t *ptr = buf + TLS_HANDSHAKE_HEADER_SIZE; uint8_t public_buf[params->prime_len]; size_t public_len; unsigned int zeros = 0; uint8_t pre_master_secret[params->prime_len]; size_t pre_master_secret_len; public_len = params->prime_len; memset(public_buf, 0, sizeof(public_buf)); if (!l_key_compute_dh_public(params->generator, params->private, params->prime, public_buf, &public_len)) { TLS_DISCONNECT(TLS_ALERT_INTERNAL_ERROR, 0, "l_key_compute_dh_public failed"); return false; } while (zeros < public_len && public_buf[zeros] == 0x00) zeros++; l_put_be16(public_len - zeros, ptr); memcpy(ptr + 2, public_buf + zeros, public_len - zeros); ptr += 2 + public_len - zeros; pre_master_secret_len = params->prime_len; zeros = 0; if (!l_key_compute_dh_secret(params->public, params->private, params->prime, pre_master_secret, &pre_master_secret_len)) { TLS_DISCONNECT(TLS_ALERT_INTERNAL_ERROR, 0, "Generating DH shared-secret failed"); return false; } while (zeros < pre_master_secret_len && pre_master_secret[zeros] == 0x00) zeros++; tls_tx_handshake(tls, TLS_CLIENT_KEY_EXCHANGE, buf, ptr - buf); tls_free_dhe_params(tls); tls_generate_master_secret(tls, pre_master_secret + zeros, pre_master_secret_len - zeros); explicit_bzero(pre_master_secret, pre_master_secret_len); return true; } static void tls_handle_dhe_client_key_xchg(struct l_tls *tls, const uint8_t *buf, size_t len) { struct tls_dhe_params *params = tls->pending.key_xchg_params; uint8_t pre_master_secret[params->prime_len]; size_t pre_master_secret_len; size_t public_len; unsigned int zeros = 0; if (len < 2) goto decode_error; public_len = l_get_be16(buf); buf += 2; len -= 2; if (public_len != len) goto decode_error; /* * RFC 7919 Section 4: * "the server MUST verify that 1 < dh_Yc < dh_p - 1. If dh_Yc is * out of range, the server MUST terminate the connection with * a fatal handshake_failure(40) alert." */ if (!l_key_validate_dh_payload(buf, public_len, tls->negotiated_ff_group->ff.prime, params->prime_len)) { TLS_DISCONNECT(TLS_ALERT_HANDSHAKE_FAIL, 0, "Client DH public value invalid"); return; } params->public = l_key_new(L_KEY_RAW, buf, public_len); if (!params->public) { TLS_DISCONNECT(TLS_ALERT_INTERNAL_ERROR, 0, "l_key_new failed"); return; } pre_master_secret_len = params->prime_len; if (!l_key_compute_dh_secret(params->public, params->private, params->prime, pre_master_secret, &pre_master_secret_len)) { TLS_DISCONNECT(TLS_ALERT_INTERNAL_ERROR, 0, "Generating DH shared-secret failed"); return; } while (zeros < pre_master_secret_len && pre_master_secret[zeros] == 0x00) zeros++; tls_free_dhe_params(tls); tls_generate_master_secret(tls, pre_master_secret + zeros, pre_master_secret_len - zeros); explicit_bzero(pre_master_secret, pre_master_secret_len); return; decode_error: TLS_DISCONNECT(TLS_ALERT_DECODE_ERROR, 0, "ClientKeyExchange decode error"); } static struct tls_key_exchange_algorithm tls_dhe = { .need_ecc = true, .send_server_key_exchange = tls_send_dhe_server_key_xchg, .handle_server_key_exchange = tls_handle_dhe_server_key_xchg, .send_client_key_exchange = tls_send_dhe_client_key_xchg, .handle_client_key_exchange = tls_handle_dhe_client_key_xchg, .free_params = tls_free_dhe_params, }; static struct tls_bulk_encryption_algorithm tls_aes128 = { .cipher_type = TLS_CIPHER_BLOCK, .l_id = L_CIPHER_AES_CBC, .key_length = 16, .iv_length = 16, .block_length = 16, }, tls_aes256 = { .cipher_type = TLS_CIPHER_BLOCK, .l_id = L_CIPHER_AES_CBC, .key_length = 32, .iv_length = 16, .block_length = 16, }, tls_3des_ede = { .cipher_type = TLS_CIPHER_BLOCK, .l_id = L_CIPHER_DES3_EDE_CBC, .key_length = 24, .iv_length = 8, .block_length = 8, }, tls_aes128_gcm = { .cipher_type = TLS_CIPHER_AEAD, .l_aead_id = L_AEAD_CIPHER_AES_GCM, .key_length = 16, .iv_length = 12, .fixed_iv_length = 4, .auth_tag_length = 16, }, tls_aes256_gcm = { .cipher_type = TLS_CIPHER_AEAD, .l_aead_id = L_AEAD_CIPHER_AES_GCM, .key_length = 32, .iv_length = 12, .fixed_iv_length = 4, .auth_tag_length = 16, }; static struct tls_mac_algorithm tls_sha = { .id = 2, .hmac_type = L_CHECKSUM_SHA1, .mac_length = 20, }, tls_sha256 = { .id = 4, .hmac_type = L_CHECKSUM_SHA256, .mac_length = 32, }, tls_sha384 = { .id = 5, .hmac_type = L_CHECKSUM_SHA384, .mac_length = 48, }; static struct tls_cipher_suite tls_rsa_with_3des_ede_cbc_sha = { .id = { 0x00, 0x0a }, .name = "TLS_RSA_WITH_3DES_EDE_CBC_SHA", .encryption = &tls_3des_ede, .mac = &tls_sha, .signature = &tls_rsa_signature, .key_xchg = &tls_rsa_key_xchg, }, tls_dhe_rsa_with_3des_ede_cbc_sha = { .id = { 0x00, 0x16 }, .name = "TLS_DHE_RSA_WITH_3DES_EDE_CBC_SHA", .encryption = &tls_3des_ede, .mac = &tls_sha, .signature = &tls_rsa_signature, .key_xchg = &tls_dhe, }, tls_rsa_with_aes_128_cbc_sha = { .id = { 0x00, 0x2f }, .name = "TLS_RSA_WITH_AES_128_CBC_SHA", .encryption = &tls_aes128, .mac = &tls_sha, .signature = &tls_rsa_signature, .key_xchg = &tls_rsa_key_xchg, }, tls_dhe_rsa_with_aes_128_cbc_sha = { .id = { 0x00, 0x33 }, .name = "TLS_DHE_RSA_WITH_AES_128_CBC_SHA", .encryption = &tls_aes128, .mac = &tls_sha, .signature = &tls_rsa_signature, .key_xchg = &tls_dhe, }, tls_rsa_with_aes_256_cbc_sha = { .id = { 0x00, 0x35 }, .name = "TLS_RSA_WITH_AES_256_CBC_SHA", .encryption = &tls_aes256, .mac = &tls_sha, .signature = &tls_rsa_signature, .key_xchg = &tls_rsa_key_xchg, }, tls_dhe_rsa_with_aes_256_cbc_sha = { .id = { 0x00, 0x39 }, .name = "TLS_DHE_RSA_WITH_AES_256_CBC_SHA", .encryption = &tls_aes256, .mac = &tls_sha, .signature = &tls_rsa_signature, .key_xchg = &tls_dhe, }, tls_rsa_with_aes_128_cbc_sha256 = { .id = { 0x00, 0x3c }, .name = "TLS_RSA_WITH_AES_128_CBC_SHA256", .encryption = &tls_aes128, .mac = &tls_sha256, .signature = &tls_rsa_signature, .key_xchg = &tls_rsa_key_xchg, }, tls_rsa_with_aes_256_cbc_sha256 = { .id = { 0x00, 0x3d }, .name = "TLS_RSA_WITH_AES_256_CBC_SHA256", .encryption = &tls_aes256, .mac = &tls_sha256, .signature = &tls_rsa_signature, .key_xchg = &tls_rsa_key_xchg, }, tls_dhe_rsa_with_aes_128_cbc_sha256 = { .id = { 0x00, 0x67 }, .name = "TLS_DHE_RSA_WITH_AES_128_CBC_SHA256", .encryption = &tls_aes128, .mac = &tls_sha256, .signature = &tls_rsa_signature, .key_xchg = &tls_dhe, }, tls_dhe_rsa_with_aes_256_cbc_sha256 = { .id = { 0x00, 0x6b }, .name = "TLS_DHE_RSA_WITH_AES_256_CBC_SHA256", .encryption = &tls_aes256, .mac = &tls_sha256, .signature = &tls_rsa_signature, .key_xchg = &tls_dhe, }, tls_rsa_with_aes_128_gcm_sha256 = { .id = { 0x00, 0x9c }, .name = "TLS_RSA_WITH_AES_128_GCM_SHA256", .encryption = &tls_aes128_gcm, .signature = &tls_rsa_signature, .key_xchg = &tls_rsa_key_xchg, }, tls_rsa_with_aes_256_gcm_sha384 = { .id = { 0x00, 0x9d }, .name = "TLS_RSA_WITH_AES_256_GCM_SHA384", .encryption = &tls_aes256_gcm, .prf_hmac = L_CHECKSUM_SHA384, .signature = &tls_rsa_signature, .key_xchg = &tls_rsa_key_xchg, }, tls_dhe_rsa_with_aes_128_gcm_sha256 = { .id = { 0x00, 0x9e }, .name = "TLS_DHE_RSA_WITH_AES_128_GCM_SHA256", .encryption = &tls_aes128_gcm, .signature = &tls_rsa_signature, .key_xchg = &tls_dhe, }, tls_dhe_rsa_with_aes_256_gcm_sha384 = { .id = { 0x00, 0x9f }, .name = "TLS_DHE_RSA_WITH_AES_256_GCM_SHA384", .encryption = &tls_aes256_gcm, .prf_hmac = L_CHECKSUM_SHA384, .signature = &tls_rsa_signature, .key_xchg = &tls_dhe, }, tls_ecdhe_rsa_with_3des_ede_cbc_sha = { .id = { 0xc0, 0x12 }, .name = "TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA", .encryption = &tls_3des_ede, .mac = &tls_sha, .signature = &tls_rsa_signature, .key_xchg = &tls_ecdhe, }, tls_ecdhe_rsa_with_aes_128_cbc_sha = { .id = { 0xc0, 0x13 }, .name = "TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA", .encryption = &tls_aes128, .mac = &tls_sha, .signature = &tls_rsa_signature, .key_xchg = &tls_ecdhe, }, tls_ecdhe_rsa_with_aes_256_cbc_sha = { .id = { 0xc0, 0x14 }, .name = "TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA", .encryption = &tls_aes256, .mac = &tls_sha, .signature = &tls_rsa_signature, .key_xchg = &tls_ecdhe, }, tls_ecdhe_rsa_with_aes_128_cbc_sha256 = { .id = { 0xc0, 0x27 }, .name = "TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256", .encryption = &tls_aes128, .mac = &tls_sha256, .signature = &tls_rsa_signature, .key_xchg = &tls_ecdhe, }, tls_ecdhe_rsa_with_aes_256_cbc_sha384 = { .id = { 0xc0, 0x28 }, .name = "TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384", .encryption = &tls_aes256, .mac = &tls_sha384, .prf_hmac = L_CHECKSUM_SHA384, .signature = &tls_rsa_signature, .key_xchg = &tls_ecdhe, }, tls_ecdhe_rsa_with_aes_128_gcm_sha256 = { .id = { 0xc0, 0x2f }, .name = "TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256", .encryption = &tls_aes128_gcm, .signature = &tls_rsa_signature, .key_xchg = &tls_ecdhe, }, tls_ecdhe_rsa_with_aes_256_gcm_sha384 = { .id = { 0xc0, 0x30 }, .name = "TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384", .encryption = &tls_aes256_gcm, .prf_hmac = L_CHECKSUM_SHA384, .signature = &tls_rsa_signature, .key_xchg = &tls_ecdhe, }, tls_ecdhe_ecdsa_with_3des_ede_cbc_sha = { .id = { 0xc0, 0x08 }, .name = "TLS_ECDHE_ECDSA_WITH_3DES_EDE_CBC_SHA", .encryption = &tls_3des_ede, .mac = &tls_sha, .signature = &tls_ecdsa_signature, .key_xchg = &tls_ecdhe, }, tls_ecdhe_ecdsa_with_aes_128_cbc_sha = { .id = { 0xc0, 0x09 }, .name = "TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA", .encryption = &tls_aes128, .mac = &tls_sha, .signature = &tls_ecdsa_signature, .key_xchg = &tls_ecdhe, }, tls_ecdhe_ecdsa_with_aes_256_cbc_sha = { .id = { 0xc0, 0x0a }, .name = "TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA", .encryption = &tls_aes256, .mac = &tls_sha, .signature = &tls_ecdsa_signature, .key_xchg = &tls_ecdhe, }, tls_ecdhe_ecdsa_with_aes_128_gcm_sha256 = { .id = { 0xc0, 0x2b }, .name = "TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256", .encryption = &tls_aes128_gcm, .signature = &tls_ecdsa_signature, .key_xchg = &tls_ecdhe, }, tls_ecdhe_ecdsa_with_aes_256_gcm_sha384 = { .id = { 0xc0, 0x2c }, .name = "TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384", .encryption = &tls_aes256_gcm, .prf_hmac = L_CHECKSUM_SHA384, .signature = &tls_ecdsa_signature, .key_xchg = &tls_ecdhe, }; struct tls_cipher_suite *tls_cipher_suite_pref[] = { &tls_ecdhe_rsa_with_aes_256_cbc_sha, &tls_ecdhe_ecdsa_with_aes_256_cbc_sha, &tls_ecdhe_rsa_with_aes_128_cbc_sha, &tls_ecdhe_ecdsa_with_aes_128_cbc_sha, &tls_dhe_rsa_with_aes_256_cbc_sha, &tls_dhe_rsa_with_aes_128_cbc_sha, &tls_rsa_with_aes_256_cbc_sha, &tls_rsa_with_aes_128_cbc_sha, &tls_ecdhe_rsa_with_aes_256_cbc_sha384, &tls_ecdhe_rsa_with_aes_128_cbc_sha256, &tls_dhe_rsa_with_aes_256_cbc_sha256, &tls_dhe_rsa_with_aes_128_cbc_sha256, &tls_rsa_with_aes_256_cbc_sha256, &tls_rsa_with_aes_128_cbc_sha256, &tls_ecdhe_rsa_with_aes_256_gcm_sha384, &tls_ecdhe_rsa_with_aes_128_gcm_sha256, &tls_ecdhe_ecdsa_with_aes_256_gcm_sha384, &tls_ecdhe_ecdsa_with_aes_128_gcm_sha256, &tls_dhe_rsa_with_aes_256_gcm_sha384, &tls_dhe_rsa_with_aes_128_gcm_sha256, &tls_rsa_with_aes_256_gcm_sha384, &tls_rsa_with_aes_128_gcm_sha256, &tls_ecdhe_rsa_with_3des_ede_cbc_sha, &tls_ecdhe_ecdsa_with_3des_ede_cbc_sha, &tls_dhe_rsa_with_3des_ede_cbc_sha, &tls_rsa_with_3des_ede_cbc_sha, NULL, }; bluez-5.82/ell/PaxHeaders/random.c0000644000000000000000000000005014504767710014061 xustar0020 atime=1743575466 20 ctime=1743591276 bluez-5.82/ell/random.c0000644000000000000000000000311114504767710013536 0ustar00rootroot/* * Embedded Linux library * Copyright (C) 2015 Intel Corporation * * SPDX-License-Identifier: LGPL-2.1-or-later */ #ifdef HAVE_CONFIG_H #include #endif #define _GNU_SOURCE #include #include #include #include #include "random.h" #include "private.h" #include "missing.h" #ifndef GRND_NONBLOCK #define GRND_NONBLOCK 0x0001 #endif #ifndef GRND_RANDOM #define GRND_RANDOM 0x0002 #endif static inline int getrandom(void *buffer, size_t count, unsigned flags) { return syscall(__NR_getrandom, buffer, count, flags); } /** * l_getrandom: * @buf: buffer to fill with random data * @len: length of random data requested * * Request a number of randomly generated bytes given by @len and put them * into buffer @buf. * * Returns: true if the random data could be generated, false otherwise. **/ LIB_EXPORT bool l_getrandom(void *buf, size_t len) { while (len) { int ret; ret = L_TFR(getrandom(buf, len, 0)); if (ret < 0) return false; buf += ret; len -= ret; } return true; } LIB_EXPORT bool l_getrandom_is_supported() { static bool initialized = false; static bool supported = true; uint8_t buf[4]; int ret; if (initialized) return supported; ret = getrandom(buf, sizeof(buf), GRND_NONBLOCK); if (ret < 0 && errno == ENOSYS) supported = false; initialized = true; return supported; } LIB_EXPORT uint32_t l_getrandom_uint32(void) { int ret; uint32_t u; ret = getrandom(&u, sizeof(u), GRND_NONBLOCK); if (ret == sizeof(u)) return u; return random() * RAND_MAX + random(); } bluez-5.82/ell/PaxHeaders/utf8.h0000644000000000000000000000005014560467756013505 xustar0020 atime=1743575449 20 ctime=1743591276 bluez-5.82/ell/utf8.h0000644000000000000000000000561514560467756013175 0ustar00rootroot/* * Embedded Linux library * Copyright (C) 2011-2014 Intel Corporation * Copyright (C) 2024 Cruise, LLC * * SPDX-License-Identifier: LGPL-2.1-or-later */ #ifndef __ELL_UTF8_H #define __ELL_UTF8_H #include #include #include #ifdef __cplusplus extern "C" { #endif extern unsigned char l_ascii_table[]; enum l_ascii { L_ASCII_CNTRL = 0x80, L_ASCII_PRINT = 0x40, L_ASCII_PUNCT = 0x20, L_ASCII_SPACE = 0x10, L_ASCII_XDIGIT = 0x08, L_ASCII_UPPER = 0x04, L_ASCII_LOWER = 0x02, L_ASCII_DIGIT = 0x01, L_ASCII_ALPHA = L_ASCII_LOWER | L_ASCII_UPPER, L_ASCII_ALNUM = L_ASCII_ALPHA | L_ASCII_DIGIT, L_ASCII_GRAPH = L_ASCII_ALNUM | L_ASCII_PUNCT, }; #define l_ascii_isalnum(c) \ ((l_ascii_table[(unsigned char) (c)] & L_ASCII_ALNUM) != 0) #define l_ascii_isalpha(c) \ ((l_ascii_table[(unsigned char) (c)] & L_ASCII_ALPHA) != 0) #define l_ascii_iscntrl(c) \ ((l_ascii_table[(unsigned char) (c)] & L_ASCII_CNTRL) != 0) #define l_ascii_isdigit(c) \ ((l_ascii_table[(unsigned char) (c)] & L_ASCII_DIGIT) != 0) #define l_ascii_isgraph(c) \ ((l_ascii_table[(unsigned char) (c)] & L_ASCII_GRAPH) != 0) #define l_ascii_islower(c) \ ((l_ascii_table[(unsigned char) (c)] & L_ASCII_LOWER) != 0) #define l_ascii_isprint(c) \ ((l_ascii_table[(unsigned char) (c)] & L_ASCII_PRINT) != 0) #define l_ascii_ispunct(c) \ ((l_ascii_table[(unsigned char) (c)] & L_ASCII_PUNCT) != 0) #define l_ascii_isspace(c) \ ((l_ascii_table[(unsigned char) (c)] & L_ASCII_SPACE) != 0) #define l_ascii_isupper(c) \ ((l_ascii_table[(unsigned char) (c)] & L_ASCII_UPPER) != 0) #define l_ascii_isxdigit(c) \ ((l_ascii_table[(unsigned char) (c)] & L_ASCII_XDIGIT) != 0) #if __STDC_VERSION__ <= 199409L #define inline __inline__ #endif static inline __attribute__ ((always_inline)) bool l_ascii_isblank(unsigned char c) { if (c == ' ' || c == '\t') return true; return false; } static inline __attribute__ ((always_inline)) bool l_ascii_isascii(int c) { if (c <= 127) return true; return false; } static inline __attribute__ ((always_inline)) char l_ascii_toupper(char c) { if (!l_ascii_islower(c)) return c; return c - 32; } static inline __attribute__ ((always_inline)) char l_ascii_tolower(char c) { if (!l_ascii_isupper(c)) return c; return c + 32; } char *l_ascii_strdown(const char *str, ssize_t len); char *l_ascii_strup(const char *str, ssize_t len); bool l_utf8_validate(const char *src, size_t len, const char **end); size_t l_utf8_strlen(const char *str); int l_utf8_get_codepoint(const char *str, size_t len, wchar_t *cp); size_t l_utf8_from_wchar(wchar_t c, char *out_buf); char *l_utf8_from_utf16(const void *utf16, ssize_t utf16_size); void *l_utf8_to_utf16(const char *utf8, size_t *out_size); char *l_utf8_from_ucs2be(const void *ucs2be, ssize_t ucs2be_size); void *l_utf8_to_ucs2be(const char *utf8, size_t *out_size); #ifdef __cplusplus } #endif #endif /* __ELL_UTF8_H */ bluez-5.82/ell/PaxHeaders/dbus-util.c0000644000000000000000000000005014504767710014511 xustar0020 atime=1743575508 20 ctime=1743591277 bluez-5.82/ell/dbus-util.c0000644000000000000000000006213514504767710014201 0ustar00rootroot/* * Embedded Linux library * Copyright (C) 2011-2014 Intel Corporation * * SPDX-License-Identifier: LGPL-2.1-or-later */ #ifdef HAVE_CONFIG_H #include #endif #define _GNU_SOURCE #include #include #include #include #include #include #include #include "dbus.h" #include "private.h" #include "useful.h" #include "dbus-private.h" #include "string.h" #include "queue.h" #include "utf8.h" #define DBUS_MAX_INTERFACE_LEN 255 #define DBUS_MAX_METHOD_LEN 255 static const char *simple_types = "sogybnqiuxtdh"; static int get_alignment(const char type) { switch (type) { case 'b': return 4; case 'y': return 1; case 'n': case 'q': return 2; case 'u': case 'i': return 4; case 'x': case 't': case 'd': return 8; case 's': case 'o': return 4; case 'g': return 1; case 'a': return 4; case '(': case '{': return 8; case 'v': return 1; case 'h': return 4; default: return 0; } } static int get_basic_size(const char type) { switch (type) { case 'b': return 4; case 'y': return 1; case 'n': case 'q': return 2; case 'i': case 'u': return 4; case 'x': case 't': return 8; case 'd': return 8; case 'h': return 4; default: return 0; } } static inline bool is_valid_character(const char c, bool bus_name) { if (c >= 'a' && c <= 'z') return true; if (c >= 'A' && c <= 'Z') return true; if (c >= '0' && c <= '9') return true; if (c == '_') return true; if (c == '-' && bus_name) return true; return false; } bool _dbus_valid_object_path(const char *path) { unsigned int i; char c = '\0'; if (path == NULL) return false; if (path[0] == '\0') return false; if (path[0] && !path[1] && path[0] == '/') return true; if (path[0] != '/') return false; for (i = 0; path[i]; i++) { if (path[i] == '/' && c == '/') return false; c = path[i]; if (is_valid_character(path[i], false) || path[i] == '/') continue; return false; } if (path[i-1] == '/') return false; return true; } static const char *validate_next_type(const char *sig) { char s = *sig; if (s == '\0') return NULL; if (strchr(simple_types, s) || s == 'v') return sig + 1; switch (s) { case 'a': s = *++sig; if (s == '{') { s = *++sig; /* Dictionary keys can only be simple types */ if (!strchr(simple_types, s)) return NULL; sig = validate_next_type(sig + 1); if (!sig) return NULL; if (*sig != '}') return NULL; return sig + 1; } return validate_next_type(sig); case '(': sig++; do sig = validate_next_type(sig); while (sig && *sig != ')'); if (!sig) return NULL; return sig + 1; } return NULL; } static bool valid_dict_signature(const char *sig) { char s = *sig; if (s != '{') return false; s = *++sig; if (!strchr(simple_types, s)) return false; sig = validate_next_type(sig + 1); if (!sig) return false; if (sig[0] != '}') return false; if (sig[1] != '\0') return false; return true; } bool _dbus_valid_signature(const char *sig) { const char *s = sig; do { s = validate_next_type(s); if (!s) return false; } while (*s); return true; } int _dbus_num_children(const char *sig) { const char *s = sig; int num_children = 0; do { s = validate_next_type(s); if (!s) return -1; num_children += 1; } while (*s); return num_children; } static bool valid_member_name(const char *start, const char *end, bool bus_name) { const char *p; if ((end - start) < 1) return false; if (*start >= '0' && *start <= '9') return false; for (p = start; p < end; p++) if (!is_valid_character(*p, bus_name)) return false; return true; } bool _dbus_valid_method(const char *method) { unsigned int i; if (!method) return false; if (method[0] == '\0' || strlen(method) > DBUS_MAX_METHOD_LEN) return false; if (method[0] >= '0' && method[0] <= '9') return false; for (i = 0; method[i]; i++) if (!is_valid_character(method[i], false)) return false; return true; } bool _dbus_valid_interface(const char *interface) { const char *sep; if (!interface) return false; if (interface[0] == '\0' || strlen(interface) > DBUS_MAX_INTERFACE_LEN) return false; sep = strchrnul(interface, '.'); if (*sep == '\0') return false; while (true) { if (!valid_member_name(interface, sep, false)) return false; if (*sep == '\0') break; interface = sep + 1; sep = strchrnul(interface, '.'); } return true; } bool _dbus_parse_unique_name(const char *name, uint64_t *out_id) { char *endp = NULL; uint64_t r; if (!l_str_has_prefix(name, ":1.")) return false; name += 3; /* * Disallow '+' or '-' at the beginning of the string * must have at least one digit */ if (!l_ascii_isdigit(*name)) return false; errno = 0; r = strtoull(name, &endp, 10); if (!endp || *endp || errno) return false; if (out_id) *out_id = r; return true; } bool _dbus_valid_bus_name(const char *bus_name) { const char *sep; if (!bus_name) return false; if (bus_name[0] == '\0' || strlen(bus_name) > DBUS_MAX_INTERFACE_LEN) return false; if (_dbus_parse_unique_name(bus_name, NULL)) return true; sep = strchrnul(bus_name, '.'); if (*sep == '\0') return false; while (true) { if (!valid_member_name(bus_name, sep, true)) return false; if (*sep == '\0') break; bus_name = sep + 1; sep = strchrnul(bus_name, '.'); } return true; } const char *_dbus_signature_end(const char *signature) { const char *ptr = signature; unsigned int indent = 0; char expect; switch (*signature) { case '(': expect = ')'; break; case '{': expect = '}'; break; case 'a': return _dbus_signature_end(signature + 1); default: return signature; } for (ptr = signature; *ptr != '\0'; ptr++) { if (*ptr == *signature) indent++; else if (*ptr == expect) if (!--indent) return ptr; } return NULL; } bool _dbus1_header_is_valid(void *data, size_t size) { struct dbus_header *hdr; size_t header_len; if (size < sizeof(struct dbus_header)) return false; hdr = data; if (hdr->endian != DBUS_NATIVE_ENDIAN) header_len = bswap_32(hdr->dbus1.field_length); else header_len = hdr->dbus1.field_length; header_len += sizeof(struct dbus_header); return size >= header_len; } static inline void dbus1_iter_init_internal(struct l_dbus_message_iter *iter, struct l_dbus_message *message, enum dbus_container_type type, const char *sig_start, const char *sig_end, const void *data, size_t len, size_t pos) { size_t sig_len; iter->message = message; if (sig_end) sig_len = sig_end - sig_start; else sig_len = strlen(sig_start); iter->sig_start = sig_start; iter->sig_len = sig_len; iter->sig_pos = 0; iter->data = data; iter->len = pos + len; iter->pos = pos; iter->container_type = type; } void _dbus1_iter_init(struct l_dbus_message_iter *iter, struct l_dbus_message *message, const char *sig_start, const char *sig_end, const void *data, size_t len) { dbus1_iter_init_internal(iter, message, DBUS_CONTAINER_TYPE_STRUCT, sig_start, sig_end, data, len, 0); } static const char *calc_len_next_item(const char *signature, const void *data, size_t data_pos, size_t data_len, size_t *out_len) { unsigned int alignment; size_t pos; size_t len; const char *sig_end; const char *var_sig; alignment = get_alignment(*signature); if (alignment == 0) return NULL; pos = align_len(data_pos, alignment); if (pos > data_len) return NULL; switch (*signature) { case 'o': case 's': if (pos + 5 > data_len) return NULL; pos += l_get_u32(data + pos) + 5; break; case 'g': if (pos + 2 > data_len) return NULL; pos += l_get_u8(data + pos) + 2; break; case 'y': pos += 1; break; case 'n': case 'q': pos += 2; break; case 'b': case 'i': case 'u': case 'h': pos += 4; break; case 'x': case 't': case 'd': pos += 8; break; case 'a': if (pos + 4 > data_len) return NULL; len = l_get_u32(data + pos); pos += 4; alignment = get_alignment(signature[1]); pos = align_len(pos, alignment); pos += len; sig_end = _dbus_signature_end(signature) + 1; goto done; case '(': sig_end = signature + 1; while (*sig_end != ')') { sig_end = calc_len_next_item(sig_end, data, pos, data_len, &len); if (!sig_end) return NULL; pos += len; } sig_end += 1; goto done; case '{': sig_end = calc_len_next_item(signature + 1, data, pos, data_len, &len); if (!sig_end) return NULL; pos += len; sig_end = calc_len_next_item(sig_end, data, pos, data_len, &len); if (!sig_end) return NULL; pos += len; sig_end += 1; goto done; case 'v': if (!calc_len_next_item("g", data, pos, data_len, &len)) return NULL; var_sig = data + pos + 1; pos += len; if (!calc_len_next_item(var_sig, data, pos, data_len, &len)) return NULL; pos += len; break; default: return NULL; } sig_end = signature + 1; done: if (pos > data_len) return NULL; *out_len = pos - data_pos; return sig_end; } bool _dbus1_iter_next_entry_basic(struct l_dbus_message_iter *iter, char type, void *out) { const char *str_val; uint8_t uint8_val; uint16_t uint16_val; uint32_t uint32_val; uint64_t uint64_val; int16_t int16_val; int32_t int32_val; int64_t int64_val; size_t pos; if (iter->pos >= iter->len) return false; pos = align_len(iter->pos, get_alignment(type)); switch (type) { case 'o': case 's': if (pos + 5 > iter->len) return false; uint32_val = l_get_u32(iter->data + pos); str_val = iter->data + pos + 4; *(const void **) out = str_val; iter->pos = pos + uint32_val + 5; break; case 'g': if (pos + 2 > iter->len) return false; uint8_val = l_get_u8(iter->data + pos); str_val = iter->data + pos + 1; *(const void **) out = str_val; iter->pos = pos + uint8_val + 2; break; case 'b': if (pos + 4 > iter->len) return false; uint32_val = l_get_u32(iter->data + pos); *(bool *) out = !!uint32_val; iter->pos = pos + 4; break; case 'y': if (pos + 1 > iter->len) return false; uint8_val = l_get_u8(iter->data + pos); *(uint8_t *) out = uint8_val; iter->pos = pos + 1; break; case 'n': if (pos + 2 > iter->len) return false; int16_val = l_get_s16(iter->data + pos); *(int16_t *) out = int16_val; iter->pos = pos + 2; break; case 'q': if (pos + 2 > iter->len) return false; uint16_val = l_get_u16(iter->data + pos); *(uint16_t *) out = uint16_val; iter->pos = pos + 2; break; case 'i': if (pos + 4 > iter->len) return false; int32_val = l_get_s32(iter->data + pos); *(int32_t *) out = int32_val; iter->pos = pos + 4; break; case 'u': case 'h': if (pos + 4 > iter->len) return false; uint32_val = l_get_u32(iter->data + pos); *(uint32_t *) out = uint32_val; iter->pos = pos + 4; break; case 'x': if (pos + 8 > iter->len) return false; int64_val = l_get_s64(iter->data + pos); *(int64_t *) out= int64_val; iter->pos = pos + 8; break; case 't': case 'd': if (pos + 8 > iter->len) return false; uint64_val = l_get_u64(iter->data + pos); *(uint64_t *) out = uint64_val; iter->pos = pos + 8; break; default: return false; } if (iter->container_type != DBUS_CONTAINER_TYPE_ARRAY) iter->sig_pos += 1; return true; } bool _dbus1_iter_get_fixed_array(struct l_dbus_message_iter *iter, void *out, uint32_t *n_elem) { char type; uint32_t size; if (iter->container_type != DBUS_CONTAINER_TYPE_ARRAY) return false; type = iter->sig_start[iter->sig_pos]; size = get_basic_size(type); /* Fail if the array is not a fixed size or contains file descriptors */ if (!size || type == 'h') return false; /* * enter_array should already align us to our container type, so * there is no need to align pos here */ *(const void **) out = iter->data + iter->pos; *n_elem = (iter->len - iter->pos) / size; return true; } bool _dbus1_iter_enter_struct(struct l_dbus_message_iter *iter, struct l_dbus_message_iter *structure) { size_t len; size_t pos; const char *sig_start; const char *sig_end; bool is_dict = iter->sig_start[iter->sig_pos] == '{'; bool is_struct = iter->sig_start[iter->sig_pos] == '('; if (!is_dict && !is_struct) return false; pos = align_len(iter->pos, 8); if (pos >= iter->len) return false; sig_start = iter->sig_start + iter->sig_pos + 1; sig_end = _dbus_signature_end(iter->sig_start + iter->sig_pos); if (!calc_len_next_item(iter->sig_start + iter->sig_pos, iter->data, pos, iter->len, &len)) return false; dbus1_iter_init_internal(structure, iter->message, DBUS_CONTAINER_TYPE_STRUCT, sig_start, sig_end, iter->data, len, pos); if (iter->container_type != DBUS_CONTAINER_TYPE_ARRAY) iter->sig_pos += sig_end - sig_start + 2; iter->pos = pos + len; return true; } bool _dbus1_iter_enter_variant(struct l_dbus_message_iter *iter, struct l_dbus_message_iter *variant) { size_t pos; uint8_t sig_len; size_t len; const char *sig_start; if (iter->sig_start[iter->sig_pos] != 'v') return false; pos = align_len(iter->pos, 1); if (pos + 2 > iter->len) return false; sig_len = l_get_u8(iter->data + pos); sig_start = iter->data + pos + 1; if (!calc_len_next_item(sig_start, iter->data, pos + sig_len + 2, iter->len, &len)) return false; dbus1_iter_init_internal(variant, iter->message, DBUS_CONTAINER_TYPE_VARIANT, sig_start, NULL, iter->data, len, pos + sig_len + 2); if (iter->container_type != DBUS_CONTAINER_TYPE_ARRAY) iter->sig_pos += 1; iter->pos = pos + sig_len + 2 + len; return true; } bool _dbus1_iter_enter_array(struct l_dbus_message_iter *iter, struct l_dbus_message_iter *array) { size_t pos; size_t len; const char *sig_start; const char *sig_end; if (iter->sig_start[iter->sig_pos] != 'a') return false; sig_start = iter->sig_start + iter->sig_pos + 1; sig_end = _dbus_signature_end(sig_start) + 1; pos = align_len(iter->pos, 4); if (pos + 4 > iter->len) return false; len = l_get_u32(iter->data + pos); pos += 4; pos = align_len(pos, get_alignment(*sig_start)); dbus1_iter_init_internal(array, iter->message, DBUS_CONTAINER_TYPE_ARRAY, sig_start, sig_end, iter->data, len, pos); if (iter->container_type != DBUS_CONTAINER_TYPE_ARRAY) iter->sig_pos += sig_end - sig_start + 1; iter->pos = pos + len; return true; } bool _dbus1_iter_skip_entry(struct l_dbus_message_iter *iter) { size_t len; const char *sig_end; sig_end = calc_len_next_item(iter->sig_start + iter->sig_pos, iter->data, iter->pos, iter->len, &len); if (!sig_end) return false; iter->pos += len; iter->sig_pos = sig_end - iter->sig_start; return true; } struct dbus_builder { struct l_string *signature; void *body; size_t body_size; size_t body_pos; struct l_queue *containers; struct { struct container *container; int sig_end; size_t body_pos; } mark; }; struct container { size_t start; enum dbus_container_type type; char signature[256]; uint8_t sigindex; }; static struct container *container_new(enum dbus_container_type type, const char *signature, size_t start) { struct container *ret; ret = l_new(struct container, 1); ret->type = type; strcpy(ret->signature, signature); ret->start = start; return ret; } static void container_free(struct container *container) { l_free(container); } static inline size_t grow_body(struct dbus_builder *builder, size_t len, unsigned int alignment) { size_t size = align_len(builder->body_pos, alignment); if (size + len > builder->body_size) { builder->body = l_realloc(builder->body, size + len); builder->body_size = size + len; } if (size - builder->body_pos > 0) memset(builder->body + builder->body_pos, 0, size - builder->body_pos); builder->body_pos = size + len; return size; } struct dbus_builder *_dbus1_builder_new(void *body, size_t body_size) { struct dbus_builder *builder; struct container *root; builder = l_new(struct dbus_builder, 1); builder->signature = l_string_new(63); builder->containers = l_queue_new(); root = container_new(DBUS_CONTAINER_TYPE_STRUCT, "", 0); l_queue_push_head(builder->containers, root); builder->body = body; builder->body_size = body_size; builder->body_pos = body_size; builder->mark.container = root; builder->mark.sig_end = 0; builder->mark.body_pos = 0; return builder; } void _dbus1_builder_free(struct dbus_builder *builder) { if (unlikely(!builder)) return; l_string_free(builder->signature); l_queue_destroy(builder->containers, (l_queue_destroy_func_t) container_free); l_free(builder->body); l_free(builder); } bool _dbus1_builder_append_basic(struct dbus_builder *builder, char type, const void *value) { struct container *container = l_queue_peek_head(builder->containers); size_t start; unsigned int alignment; size_t len; if (unlikely(!builder)) return false; if (unlikely(!strchr(simple_types, type))) return false; alignment = get_alignment(type); if (!alignment) return false; if (l_queue_length(builder->containers) == 1) l_string_append_c(builder->signature, type); else if (container->signature[container->sigindex] != type) return false; len = get_basic_size(type); if (len) { uint32_t b; start = grow_body(builder, len, alignment); if (type == 'b') { b = *(bool *)value; memcpy(builder->body + start, &b, len); } else memcpy(builder->body + start, value, len); if (container->type != DBUS_CONTAINER_TYPE_ARRAY) container->sigindex += 1; return true; } len = strlen(value); if (type == 'g') { start = grow_body(builder, len + 2, 1); l_put_u8(len, builder->body + start); strcpy(builder->body + start + 1, value); } else { start = grow_body(builder, len + 5, 4); l_put_u32(len, builder->body + start); strcpy(builder->body + start + 4, value); } if (container->type != DBUS_CONTAINER_TYPE_ARRAY) container->sigindex += 1; return true; } static bool enter_struct_dict_common(struct dbus_builder *builder, const char *signature, enum dbus_container_type type, const char open, const char close) { size_t qlen = l_queue_length(builder->containers); struct container *container = l_queue_peek_head(builder->containers); size_t start; if (qlen == 1) { if (l_string_length(builder->signature) + strlen(signature) + 2 > 255) return false; } else { /* Verify Signatures Match */ char expect[256]; const char *start; const char *end; start = container->signature + container->sigindex; end = _dbus_signature_end(start); if (*start != open || *end != close) return false; memcpy(expect, start + 1, end - start - 1); expect[end - start - 1] = '\0'; if (strcmp(expect, signature)) return false; } start = grow_body(builder, 0, 8); container = container_new(type, signature, start); l_queue_push_head(builder->containers, container); return true; } bool _dbus1_builder_enter_struct(struct dbus_builder *builder, const char *signature) { if (!_dbus_valid_signature(signature)) return false; return enter_struct_dict_common(builder, signature, DBUS_CONTAINER_TYPE_STRUCT, '(', ')'); } bool _dbus1_builder_enter_dict(struct dbus_builder *builder, const char *signature) { if (_dbus_num_children(signature) != 2) return false; if (!strchr(simple_types, signature[0])) return false; return enter_struct_dict_common(builder, signature, DBUS_CONTAINER_TYPE_DICT_ENTRY, '{', '}'); } static bool leave_struct_dict_common(struct dbus_builder *builder, enum dbus_container_type type, const char open, const char close) { struct container *container = l_queue_peek_head(builder->containers); size_t qlen = l_queue_length(builder->containers); struct container *parent; if (unlikely(qlen <= 1)) return false; if (unlikely(container->type != type)) return false; l_queue_pop_head(builder->containers); qlen -= 1; parent = l_queue_peek_head(builder->containers); if (qlen == 1) l_string_append_printf(builder->signature, "%c%s%c", open, container->signature, close); else if (parent->type != DBUS_CONTAINER_TYPE_ARRAY) parent->sigindex += strlen(container->signature) + 2; container_free(container); return true; } bool _dbus1_builder_leave_struct(struct dbus_builder *builder) { return leave_struct_dict_common(builder, DBUS_CONTAINER_TYPE_STRUCT, '(', ')'); } bool _dbus1_builder_leave_dict(struct dbus_builder *builder) { return leave_struct_dict_common(builder, DBUS_CONTAINER_TYPE_DICT_ENTRY, '{', '}'); } bool _dbus1_builder_enter_variant(struct dbus_builder *builder, const char *signature) { size_t qlen = l_queue_length(builder->containers); struct container *container = l_queue_peek_head(builder->containers); size_t start; size_t siglen; if (_dbus_num_children(signature) != 1) return false; if (qlen == 1) { if (l_string_length(builder->signature) + 1 > 255) return false; } else if (container->signature[container->sigindex] != 'v') return false; siglen = strlen(signature); start = grow_body(builder, siglen + 2, 1); l_put_u8(siglen, builder->body + start); strcpy(builder->body + start + 1, signature); container = container_new(DBUS_CONTAINER_TYPE_VARIANT, signature, start); l_queue_push_head(builder->containers, container); return true; } bool _dbus1_builder_leave_variant(struct dbus_builder *builder) { struct container *container = l_queue_peek_head(builder->containers); size_t qlen = l_queue_length(builder->containers); struct container *parent; if (unlikely(qlen <= 1)) return false; if (unlikely(container->type != DBUS_CONTAINER_TYPE_VARIANT)) return false; l_queue_pop_head(builder->containers); qlen -= 1; parent = l_queue_peek_head(builder->containers); if (qlen == 1) l_string_append_c(builder->signature, 'v'); else if (parent->type != DBUS_CONTAINER_TYPE_ARRAY) parent->sigindex += 1; container_free(container); return true; } bool _dbus1_builder_enter_array(struct dbus_builder *builder, const char *signature) { size_t qlen = l_queue_length(builder->containers); struct container *container = l_queue_peek_head(builder->containers); size_t start; int alignment; if (_dbus_num_children(signature) != 1 && !valid_dict_signature(signature)) return false; if (qlen == 1) { if (l_string_length(builder->signature) + strlen(signature) + 1 > 255) return false; } else { /* Verify Signatures Match */ char expect[256]; const char *start; const char *end; start = container->signature + container->sigindex; end = validate_next_type(start); if (*start != 'a') return false; memcpy(expect, start + 1, end - start - 1); expect[end - start - 1] = '\0'; if (strcmp(expect, signature)) return false; } /* First grow the body enough to cover preceding length */ start = grow_body(builder, 4, 4); /* Now align to element alignment */ alignment = get_alignment(*signature); grow_body(builder, 0, alignment); container = container_new(DBUS_CONTAINER_TYPE_ARRAY, signature, start); l_queue_push_head(builder->containers, container); return true; } bool _dbus1_builder_leave_array(struct dbus_builder *builder) { struct container *container = l_queue_peek_head(builder->containers); size_t qlen = l_queue_length(builder->containers); struct container *parent; size_t alignment; size_t array_start; if (unlikely(qlen <= 1)) return false; if (unlikely(container->type != DBUS_CONTAINER_TYPE_ARRAY)) return false; l_queue_pop_head(builder->containers); qlen -= 1; parent = l_queue_peek_head(builder->containers); if (qlen == 1) l_string_append_printf(builder->signature, "a%s", container->signature); else if (parent->type != DBUS_CONTAINER_TYPE_ARRAY) parent->sigindex += strlen(container->signature) + 1; /* Update array length */ alignment = get_alignment(container->signature[0]); array_start = align_len(container->start + 4, alignment); l_put_u32(builder->body_pos - array_start, builder->body + container->start); container_free(container); return true; } bool _dbus1_builder_mark(struct dbus_builder *builder) { struct container *container = l_queue_peek_head(builder->containers); builder->mark.container = container; if (l_queue_length(builder->containers) == 1) builder->mark.sig_end = l_string_length(builder->signature); else builder->mark.sig_end = container->sigindex; builder->mark.body_pos = builder->body_pos; return true; } bool _dbus1_builder_rewind(struct dbus_builder *builder) { struct container *container; while ((container = l_queue_peek_head(builder->containers)) != builder->mark.container) { container_free(container); l_queue_pop_head(builder->containers); } builder->body_pos = builder->mark.body_pos; if (l_queue_length(builder->containers) == 1) l_string_truncate(builder->signature, builder->mark.sig_end); else container->sigindex = builder->mark.sig_end; return true; } char *_dbus1_builder_finish(struct dbus_builder *builder, void **body, size_t *body_size) { char *signature; if (unlikely(!builder)) return NULL; if (unlikely(l_queue_length(builder->containers) != 1)) return NULL; signature = l_string_unwrap(builder->signature); builder->signature = NULL; *body = builder->body; *body_size = builder->body_pos; builder->body = NULL; builder->body_size = 0; return signature; } bluez-5.82/ell/PaxHeaders/tls.h0000644000000000000000000000005014504767710013410 xustar0020 atime=1743575449 20 ctime=1743591276 bluez-5.82/ell/tls.h0000644000000000000000000001065414504767710013077 0ustar00rootroot/* * Embedded Linux library * Copyright (C) 2015 Intel Corporation * * SPDX-License-Identifier: LGPL-2.1-or-later */ #ifndef __ELL_TLS_H #define __ELL_TLS_H #ifdef __cplusplus extern "C" { #endif enum l_tls_version { L_TLS_V10 = ((3 << 8) | 1), L_TLS_V11 = ((3 << 8) | 2), L_TLS_V12 = ((3 << 8) | 3), L_TLS_V13 = ((3 << 8) | 4), /* Not supported */ }; struct l_tls; struct l_key; struct l_certchain; struct l_queue; struct l_settings; enum l_tls_alert_desc { TLS_ALERT_CLOSE_NOTIFY = 0, TLS_ALERT_UNEXPECTED_MESSAGE = 10, TLS_ALERT_BAD_RECORD_MAC = 20, TLS_ALERT_DECRYPT_FAIL_RESERVED = 21, TLS_ALERT_RECORD_OVERFLOW = 22, TLS_ALERT_DECOMPRESS_FAIL = 30, TLS_ALERT_HANDSHAKE_FAIL = 40, TLS_ALERT_NO_CERT_RESERVED = 41, TLS_ALERT_BAD_CERT = 42, TLS_ALERT_UNSUPPORTED_CERT = 43, TLS_ALERT_CERT_REVOKED = 44, TLS_ALERT_CERT_EXPIRED = 45, TLS_ALERT_CERT_UNKNOWN = 46, TLS_ALERT_ILLEGAL_PARAM = 47, TLS_ALERT_UNKNOWN_CA = 48, TLS_ALERT_ACCESS_DENIED = 49, TLS_ALERT_DECODE_ERROR = 50, TLS_ALERT_DECRYPT_ERROR = 51, TLS_ALERT_EXPORT_RES_RESERVED = 60, TLS_ALERT_PROTOCOL_VERSION = 70, TLS_ALERT_INSUFFICIENT_SECURITY = 71, TLS_ALERT_INTERNAL_ERROR = 80, TLS_ALERT_USER_CANCELED = 90, TLS_ALERT_NO_RENEGOTIATION = 100, TLS_ALERT_UNSUPPORTED_EXTENSION = 110, }; typedef void (*l_tls_write_cb_t)(const uint8_t *data, size_t len, void *user_data); typedef void (*l_tls_ready_cb_t)(const char *peer_identity, void *user_data); typedef void (*l_tls_disconnect_cb_t)(enum l_tls_alert_desc reason, bool remote, void *user_data); typedef void (*l_tls_debug_cb_t)(const char *str, void *user_data); typedef void (*l_tls_destroy_cb_t)(void *user_data); typedef void (*l_tls_session_update_cb_t)(void *user_data); /* * app_data_handler gets called with newly received decrypted data. * tx_handler gets called to send TLS payloads off to remote end. * ready_handler gets called when l_tls_write calls are first accepted. */ struct l_tls *l_tls_new(bool server, l_tls_write_cb_t app_data_handler, l_tls_write_cb_t tx_handler, l_tls_ready_cb_t ready_handler, l_tls_disconnect_cb_t disconnect_handler, void *user_data); void l_tls_free(struct l_tls *tls); /* Begin sending connection setup messages to the server */ bool l_tls_start(struct l_tls *tls); /* Properly disconnect a connected session */ void l_tls_close(struct l_tls *tls); /* Reset to initial state without a graceful disconnect or callback */ void l_tls_reset(struct l_tls *tls); /* Submit plaintext data to be encrypted and transmitted */ void l_tls_write(struct l_tls *tls, const uint8_t *data, size_t len); /* Submit TLS payload from underlying transport to be decrypted */ void l_tls_handle_rx(struct l_tls *tls, const uint8_t *data, size_t len); /* * If peer is to be authenticated, supply the CA certificates. On success * the l_tls object takes ownership of the queue and the individual l_cert * objects and they should not be freed by the caller afterwards. */ bool l_tls_set_cacert(struct l_tls *tls, struct l_queue *ca_certs); /* * If we are to be authenticated, supply our certificate and private key. * On the client this is optional. On success, the l_tls object takes * ownership of the certchain and the key objects and they should not be * freed by the caller afterwards. * TODO: it may also be useful for the caller to be able to supply one * certificate of each type so they can be used depending on which is compatible * with the negotiated parameters. */ bool l_tls_set_auth_data(struct l_tls *tls, struct l_certchain *certchain, struct l_key *priv_key); void l_tls_set_version_range(struct l_tls *tls, enum l_tls_version min_version, enum l_tls_version max_version); void l_tls_set_domain_mask(struct l_tls *tls, char **mask); void l_tls_set_session_cache(struct l_tls *tls, struct l_settings *settings, const char *group_prefix, uint64_t lifetime, unsigned int max_sessions, l_tls_session_update_cb_t update_cb, void *user_data); bool l_tls_get_session_resumed(struct l_tls *tls); const char *l_tls_alert_to_str(enum l_tls_alert_desc desc); enum l_checksum_type; bool l_tls_prf_get_bytes(struct l_tls *tls, bool use_master_secret, const char *label, uint8_t *buf, size_t len); bool l_tls_set_debug(struct l_tls *tls, l_tls_debug_cb_t function, void *user_data, l_tls_destroy_cb_t destroy); bool l_tls_set_cert_dump_path(struct l_tls *tls, const char *path); #ifdef __cplusplus } #endif #endif /* __ELL_TLS_H */ bluez-5.82/ell/PaxHeaders/base64.c0000644000000000000000000000005014504767710013665 xustar0020 atime=1743575498 20 ctime=1743591276 bluez-5.82/ell/base64.c0000644000000000000000000000552114504767710013351 0ustar00rootroot/* * Embedded Linux library * Copyright (C) 2015 Intel Corporation * * SPDX-License-Identifier: LGPL-2.1-or-later */ #ifdef HAVE_CONFIG_H #include #endif #include #include "utf8.h" #include "base64.h" #include "private.h" #include "useful.h" #include LIB_EXPORT uint8_t *l_base64_decode(const char *in, size_t in_len, size_t *n_written) { const char *ptr, *in_end = in + in_len; const char *base64_end = NULL; uint8_t *out_buf, *out; int base64_len = 0, pad_len = 0; uint16_t reg = 0; for (ptr = in; ptr < in_end; ptr++) { if (l_ascii_isspace(*ptr)) /* Whitespace */ continue; else if (*ptr == '=') { /* Final padding */ if (!pad_len) base64_end = ptr; pad_len++; } else if (!pad_len && (l_ascii_isalnum(*ptr) || *ptr == '+' || *ptr == '/')) /* Base64 character */ base64_len++; else /* Bad character */ return NULL; } if (ptr != in_end) return NULL; if ((base64_len & 3) == 1) /* Invalid length */ return NULL; if (pad_len != align_len(base64_len, 4) - base64_len) return NULL; /* No padding */ if (!base64_end) base64_end = ptr; *n_written = base64_len * 3 / 4; out_buf = l_malloc(*n_written); out = out_buf; base64_len = 0; for (ptr = in; ptr < base64_end; ptr++) { if (l_ascii_isspace(*ptr)) /* Whitespace */ continue; /* Base64 character */ reg <<= 6; if (l_ascii_isupper(*ptr)) reg |= *ptr - 'A' + 0; else if (l_ascii_islower(*ptr)) reg |= *ptr - 'a' + 26; else if (l_ascii_isdigit(*ptr)) reg |= *ptr - '0' + 52; else if (*ptr == '+') reg |= 62; else if (*ptr == '/') reg |= 63; if ((base64_len & 3) == 1) *out++ = reg >> 4; else if ((base64_len & 3) == 2) *out++ = reg >> 2; else if ((base64_len & 3) == 3) *out++ = reg >> 0; base64_len++; } return out_buf; } LIB_EXPORT char *l_base64_encode(const uint8_t *in, size_t in_len, int columns) { const uint8_t *in_end = in + in_len; char *out_buf, *out; size_t out_len; uint32_t reg; uint8_t idx; int i, pad = 4; int col = 0; /* For simplicity allow multiples of 4 only */ if (columns & 3) return NULL; out_len = (in_len + 2) / 3 * 4; if (columns && out_len) out_len += (out_len - 4) / columns; out_buf = l_malloc(out_len + 1); out = out_buf; while (in < in_end) { reg = *in++ << 16; if (in < in_end) reg |= *in++ << 8; else pad--; if (in < in_end) reg |= *in++ << 0; else pad--; if (columns && col == columns) { *out++ = '\n'; col = 0; } col += 4; for (i = 0; i < pad; i++) { idx = (reg >> 18) & 63; reg <<= 6; if (idx < 26) *out++ = idx + 'A'; else if (idx < 52) *out++ = idx - 26 + 'a'; else if (idx < 62) *out++ = idx - 52 + '0'; else *out++ = (idx == 62) ? '+' : '/'; } } for (; pad < 4; pad++) *out++ = '='; *out = '\0'; return out_buf; } bluez-5.82/ell/PaxHeaders/strv.h0000644000000000000000000000005014504767710013604 xustar0020 atime=1743575449 20 ctime=1743591276 bluez-5.82/ell/strv.h0000644000000000000000000000213414504767710013265 0ustar00rootroot/* * Embedded Linux library * Copyright (C) 2011-2014 Intel Corporation * * SPDX-License-Identifier: LGPL-2.1-or-later */ #ifndef __ELL_STRV_H #define __ELL_STRV_H #include #include #include #ifdef __cplusplus extern "C" { #endif void l_strfreev(char **strlist); char **l_strsplit(const char *str, const char sep); char **l_strsplit_set(const char *str, const char *separators); char *l_strjoinv(char **str_array, const char delim); char **l_strv_new(void); void l_strv_free(char **str_array); DEFINE_CLEANUP_FUNC(l_strv_free); unsigned int l_strv_length(char **str_array); bool l_strv_contains(char **str_array, const char *item); char **l_strv_append(char **str_array, const char *str); char **l_strv_append_printf(char **str_array, const char *format, ...) __attribute__((format(printf, 2, 3))); char **l_strv_append_vprintf(char **str_array, const char *format, va_list args) __attribute__((format(printf, 2, 0))); char **l_strv_copy(char **str_array); bool l_strv_eq(char **a, char **b); #ifdef __cplusplus } #endif #endif /* __ELL_STRV_H */ bluez-5.82/ell/PaxHeaders/io.h0000644000000000000000000000005014504767710013215 xustar0020 atime=1743575449 20 ctime=1743591276 bluez-5.82/ell/io.h0000644000000000000000000000240114504767710012673 0ustar00rootroot/* * Embedded Linux library * Copyright (C) 2011-2014 Intel Corporation * * SPDX-License-Identifier: LGPL-2.1-or-later */ #ifndef __ELL_IO_H #define __ELL_IO_H #include #ifdef __cplusplus extern "C" { #endif struct l_io; typedef void (*l_io_debug_cb_t) (const char *str, void *user_data); typedef bool (*l_io_read_cb_t) (struct l_io *io, void *user_data); typedef bool (*l_io_write_cb_t) (struct l_io *io, void *user_data); typedef void (*l_io_disconnect_cb_t) (struct l_io *io, void *user_data); typedef void (*l_io_destroy_cb_t) (void *user_data); struct l_io *l_io_new(int fd); void l_io_destroy(struct l_io *io); int l_io_get_fd(struct l_io *io); bool l_io_set_close_on_destroy(struct l_io *io, bool do_close); bool l_io_set_read_handler(struct l_io *io, l_io_read_cb_t callback, void *user_data, l_io_destroy_cb_t destroy); bool l_io_set_write_handler(struct l_io *io, l_io_write_cb_t callback, void *user_data, l_io_destroy_cb_t destroy); bool l_io_set_disconnect_handler(struct l_io *io, l_io_disconnect_cb_t callback, void *user_data, l_io_destroy_cb_t destroy); bool l_io_set_debug(struct l_io *io, l_io_debug_cb_t callback, void *user_data, l_io_destroy_cb_t destroy); #ifdef __cplusplus } #endif #endif /* __ELL_IO_H */ bluez-5.82/ell/PaxHeaders/tls-extensions.c0000644000000000000000000000005014560467756015611 xustar0020 atime=1743575534 20 ctime=1743591277 bluez-5.82/ell/tls-extensions.c0000644000000000000000000011563614560467756015306 0ustar00rootroot/* * Embedded Linux library * Copyright (C) 2018 Intel Corporation * * SPDX-License-Identifier: LGPL-2.1-or-later */ #ifdef HAVE_CONFIG_H #include #endif #include #include "util.h" #include "tls.h" #include "cipher.h" #include "checksum.h" #include "cert.h" #include "tls-private.h" /* Most extensions are not used when resuming a cached session */ #define SKIP_ON_RESUMPTION() \ do { \ if (tls->session_id_size && !tls->session_id_new) \ return -ENOMSG; \ } while (0); /* RFC 7919, Section A.1 */ static const uint8_t tls_ffdhe2048_prime[] = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xad, 0xf8, 0x54, 0x58, 0xa2, 0xbb, 0x4a, 0x9a, 0xaf, 0xdc, 0x56, 0x20, 0x27, 0x3d, 0x3c, 0xf1, 0xd8, 0xb9, 0xc5, 0x83, 0xce, 0x2d, 0x36, 0x95, 0xa9, 0xe1, 0x36, 0x41, 0x14, 0x64, 0x33, 0xfb, 0xcc, 0x93, 0x9d, 0xce, 0x24, 0x9b, 0x3e, 0xf9, 0x7d, 0x2f, 0xe3, 0x63, 0x63, 0x0c, 0x75, 0xd8, 0xf6, 0x81, 0xb2, 0x02, 0xae, 0xc4, 0x61, 0x7a, 0xd3, 0xdf, 0x1e, 0xd5, 0xd5, 0xfd, 0x65, 0x61, 0x24, 0x33, 0xf5, 0x1f, 0x5f, 0x06, 0x6e, 0xd0, 0x85, 0x63, 0x65, 0x55, 0x3d, 0xed, 0x1a, 0xf3, 0xb5, 0x57, 0x13, 0x5e, 0x7f, 0x57, 0xc9, 0x35, 0x98, 0x4f, 0x0c, 0x70, 0xe0, 0xe6, 0x8b, 0x77, 0xe2, 0xa6, 0x89, 0xda, 0xf3, 0xef, 0xe8, 0x72, 0x1d, 0xf1, 0x58, 0xa1, 0x36, 0xad, 0xe7, 0x35, 0x30, 0xac, 0xca, 0x4f, 0x48, 0x3a, 0x79, 0x7a, 0xbc, 0x0a, 0xb1, 0x82, 0xb3, 0x24, 0xfb, 0x61, 0xd1, 0x08, 0xa9, 0x4b, 0xb2, 0xc8, 0xe3, 0xfb, 0xb9, 0x6a, 0xda, 0xb7, 0x60, 0xd7, 0xf4, 0x68, 0x1d, 0x4f, 0x42, 0xa3, 0xde, 0x39, 0x4d, 0xf4, 0xae, 0x56, 0xed, 0xe7, 0x63, 0x72, 0xbb, 0x19, 0x0b, 0x07, 0xa7, 0xc8, 0xee, 0x0a, 0x6d, 0x70, 0x9e, 0x02, 0xfc, 0xe1, 0xcd, 0xf7, 0xe2, 0xec, 0xc0, 0x34, 0x04, 0xcd, 0x28, 0x34, 0x2f, 0x61, 0x91, 0x72, 0xfe, 0x9c, 0xe9, 0x85, 0x83, 0xff, 0x8e, 0x4f, 0x12, 0x32, 0xee, 0xf2, 0x81, 0x83, 0xc3, 0xfe, 0x3b, 0x1b, 0x4c, 0x6f, 0xad, 0x73, 0x3b, 0xb5, 0xfc, 0xbc, 0x2e, 0xc2, 0x20, 0x05, 0xc5, 0x8e, 0xf1, 0x83, 0x7d, 0x16, 0x83, 0xb2, 0xc6, 0xf3, 0x4a, 0x26, 0xc1, 0xb2, 0xef, 0xfa, 0x88, 0x6b, 0x42, 0x38, 0x61, 0x28, 0x5c, 0x97, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, }; /* RFC 7919, Section A.2 */ static const uint8_t tls_ffdhe3072_prime[] = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xad, 0xf8, 0x54, 0x58, 0xa2, 0xbb, 0x4a, 0x9a, 0xaf, 0xdc, 0x56, 0x20, 0x27, 0x3d, 0x3c, 0xf1, 0xd8, 0xb9, 0xc5, 0x83, 0xce, 0x2d, 0x36, 0x95, 0xa9, 0xe1, 0x36, 0x41, 0x14, 0x64, 0x33, 0xfb, 0xcc, 0x93, 0x9d, 0xce, 0x24, 0x9b, 0x3e, 0xf9, 0x7d, 0x2f, 0xe3, 0x63, 0x63, 0x0c, 0x75, 0xd8, 0xf6, 0x81, 0xb2, 0x02, 0xae, 0xc4, 0x61, 0x7a, 0xd3, 0xdf, 0x1e, 0xd5, 0xd5, 0xfd, 0x65, 0x61, 0x24, 0x33, 0xf5, 0x1f, 0x5f, 0x06, 0x6e, 0xd0, 0x85, 0x63, 0x65, 0x55, 0x3d, 0xed, 0x1a, 0xf3, 0xb5, 0x57, 0x13, 0x5e, 0x7f, 0x57, 0xc9, 0x35, 0x98, 0x4f, 0x0c, 0x70, 0xe0, 0xe6, 0x8b, 0x77, 0xe2, 0xa6, 0x89, 0xda, 0xf3, 0xef, 0xe8, 0x72, 0x1d, 0xf1, 0x58, 0xa1, 0x36, 0xad, 0xe7, 0x35, 0x30, 0xac, 0xca, 0x4f, 0x48, 0x3a, 0x79, 0x7a, 0xbc, 0x0a, 0xb1, 0x82, 0xb3, 0x24, 0xfb, 0x61, 0xd1, 0x08, 0xa9, 0x4b, 0xb2, 0xc8, 0xe3, 0xfb, 0xb9, 0x6a, 0xda, 0xb7, 0x60, 0xd7, 0xf4, 0x68, 0x1d, 0x4f, 0x42, 0xa3, 0xde, 0x39, 0x4d, 0xf4, 0xae, 0x56, 0xed, 0xe7, 0x63, 0x72, 0xbb, 0x19, 0x0b, 0x07, 0xa7, 0xc8, 0xee, 0x0a, 0x6d, 0x70, 0x9e, 0x02, 0xfc, 0xe1, 0xcd, 0xf7, 0xe2, 0xec, 0xc0, 0x34, 0x04, 0xcd, 0x28, 0x34, 0x2f, 0x61, 0x91, 0x72, 0xfe, 0x9c, 0xe9, 0x85, 0x83, 0xff, 0x8e, 0x4f, 0x12, 0x32, 0xee, 0xf2, 0x81, 0x83, 0xc3, 0xfe, 0x3b, 0x1b, 0x4c, 0x6f, 0xad, 0x73, 0x3b, 0xb5, 0xfc, 0xbc, 0x2e, 0xc2, 0x20, 0x05, 0xc5, 0x8e, 0xf1, 0x83, 0x7d, 0x16, 0x83, 0xb2, 0xc6, 0xf3, 0x4a, 0x26, 0xc1, 0xb2, 0xef, 0xfa, 0x88, 0x6b, 0x42, 0x38, 0x61, 0x1f, 0xcf, 0xdc, 0xde, 0x35, 0x5b, 0x3b, 0x65, 0x19, 0x03, 0x5b, 0xbc, 0x34, 0xf4, 0xde, 0xf9, 0x9c, 0x02, 0x38, 0x61, 0xb4, 0x6f, 0xc9, 0xd6, 0xe6, 0xc9, 0x07, 0x7a, 0xd9, 0x1d, 0x26, 0x91, 0xf7, 0xf7, 0xee, 0x59, 0x8c, 0xb0, 0xfa, 0xc1, 0x86, 0xd9, 0x1c, 0xae, 0xfe, 0x13, 0x09, 0x85, 0x13, 0x92, 0x70, 0xb4, 0x13, 0x0c, 0x93, 0xbc, 0x43, 0x79, 0x44, 0xf4, 0xfd, 0x44, 0x52, 0xe2, 0xd7, 0x4d, 0xd3, 0x64, 0xf2, 0xe2, 0x1e, 0x71, 0xf5, 0x4b, 0xff, 0x5c, 0xae, 0x82, 0xab, 0x9c, 0x9d, 0xf6, 0x9e, 0xe8, 0x6d, 0x2b, 0xc5, 0x22, 0x36, 0x3a, 0x0d, 0xab, 0xc5, 0x21, 0x97, 0x9b, 0x0d, 0xea, 0xda, 0x1d, 0xbf, 0x9a, 0x42, 0xd5, 0xc4, 0x48, 0x4e, 0x0a, 0xbc, 0xd0, 0x6b, 0xfa, 0x53, 0xdd, 0xef, 0x3c, 0x1b, 0x20, 0xee, 0x3f, 0xd5, 0x9d, 0x7c, 0x25, 0xe4, 0x1d, 0x2b, 0x66, 0xc6, 0x2e, 0x37, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, }; /* RFC 7919, Section A.3 */ static const uint8_t tls_ffdhe4096_prime[] = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xad, 0xf8, 0x54, 0x58, 0xa2, 0xbb, 0x4a, 0x9a, 0xaf, 0xdc, 0x56, 0x20, 0x27, 0x3d, 0x3c, 0xf1, 0xd8, 0xb9, 0xc5, 0x83, 0xce, 0x2d, 0x36, 0x95, 0xa9, 0xe1, 0x36, 0x41, 0x14, 0x64, 0x33, 0xfb, 0xcc, 0x93, 0x9d, 0xce, 0x24, 0x9b, 0x3e, 0xf9, 0x7d, 0x2f, 0xe3, 0x63, 0x63, 0x0c, 0x75, 0xd8, 0xf6, 0x81, 0xb2, 0x02, 0xae, 0xc4, 0x61, 0x7a, 0xd3, 0xdf, 0x1e, 0xd5, 0xd5, 0xfd, 0x65, 0x61, 0x24, 0x33, 0xf5, 0x1f, 0x5f, 0x06, 0x6e, 0xd0, 0x85, 0x63, 0x65, 0x55, 0x3d, 0xed, 0x1a, 0xf3, 0xb5, 0x57, 0x13, 0x5e, 0x7f, 0x57, 0xc9, 0x35, 0x98, 0x4f, 0x0c, 0x70, 0xe0, 0xe6, 0x8b, 0x77, 0xe2, 0xa6, 0x89, 0xda, 0xf3, 0xef, 0xe8, 0x72, 0x1d, 0xf1, 0x58, 0xa1, 0x36, 0xad, 0xe7, 0x35, 0x30, 0xac, 0xca, 0x4f, 0x48, 0x3a, 0x79, 0x7a, 0xbc, 0x0a, 0xb1, 0x82, 0xb3, 0x24, 0xfb, 0x61, 0xd1, 0x08, 0xa9, 0x4b, 0xb2, 0xc8, 0xe3, 0xfb, 0xb9, 0x6a, 0xda, 0xb7, 0x60, 0xd7, 0xf4, 0x68, 0x1d, 0x4f, 0x42, 0xa3, 0xde, 0x39, 0x4d, 0xf4, 0xae, 0x56, 0xed, 0xe7, 0x63, 0x72, 0xbb, 0x19, 0x0b, 0x07, 0xa7, 0xc8, 0xee, 0x0a, 0x6d, 0x70, 0x9e, 0x02, 0xfc, 0xe1, 0xcd, 0xf7, 0xe2, 0xec, 0xc0, 0x34, 0x04, 0xcd, 0x28, 0x34, 0x2f, 0x61, 0x91, 0x72, 0xfe, 0x9c, 0xe9, 0x85, 0x83, 0xff, 0x8e, 0x4f, 0x12, 0x32, 0xee, 0xf2, 0x81, 0x83, 0xc3, 0xfe, 0x3b, 0x1b, 0x4c, 0x6f, 0xad, 0x73, 0x3b, 0xb5, 0xfc, 0xbc, 0x2e, 0xc2, 0x20, 0x05, 0xc5, 0x8e, 0xf1, 0x83, 0x7d, 0x16, 0x83, 0xb2, 0xc6, 0xf3, 0x4a, 0x26, 0xc1, 0xb2, 0xef, 0xfa, 0x88, 0x6b, 0x42, 0x38, 0x61, 0x1f, 0xcf, 0xdc, 0xde, 0x35, 0x5b, 0x3b, 0x65, 0x19, 0x03, 0x5b, 0xbc, 0x34, 0xf4, 0xde, 0xf9, 0x9c, 0x02, 0x38, 0x61, 0xb4, 0x6f, 0xc9, 0xd6, 0xe6, 0xc9, 0x07, 0x7a, 0xd9, 0x1d, 0x26, 0x91, 0xf7, 0xf7, 0xee, 0x59, 0x8c, 0xb0, 0xfa, 0xc1, 0x86, 0xd9, 0x1c, 0xae, 0xfe, 0x13, 0x09, 0x85, 0x13, 0x92, 0x70, 0xb4, 0x13, 0x0c, 0x93, 0xbc, 0x43, 0x79, 0x44, 0xf4, 0xfd, 0x44, 0x52, 0xe2, 0xd7, 0x4d, 0xd3, 0x64, 0xf2, 0xe2, 0x1e, 0x71, 0xf5, 0x4b, 0xff, 0x5c, 0xae, 0x82, 0xab, 0x9c, 0x9d, 0xf6, 0x9e, 0xe8, 0x6d, 0x2b, 0xc5, 0x22, 0x36, 0x3a, 0x0d, 0xab, 0xc5, 0x21, 0x97, 0x9b, 0x0d, 0xea, 0xda, 0x1d, 0xbf, 0x9a, 0x42, 0xd5, 0xc4, 0x48, 0x4e, 0x0a, 0xbc, 0xd0, 0x6b, 0xfa, 0x53, 0xdd, 0xef, 0x3c, 0x1b, 0x20, 0xee, 0x3f, 0xd5, 0x9d, 0x7c, 0x25, 0xe4, 0x1d, 0x2b, 0x66, 0x9e, 0x1e, 0xf1, 0x6e, 0x6f, 0x52, 0xc3, 0x16, 0x4d, 0xf4, 0xfb, 0x79, 0x30, 0xe9, 0xe4, 0xe5, 0x88, 0x57, 0xb6, 0xac, 0x7d, 0x5f, 0x42, 0xd6, 0x9f, 0x6d, 0x18, 0x77, 0x63, 0xcf, 0x1d, 0x55, 0x03, 0x40, 0x04, 0x87, 0xf5, 0x5b, 0xa5, 0x7e, 0x31, 0xcc, 0x7a, 0x71, 0x35, 0xc8, 0x86, 0xef, 0xb4, 0x31, 0x8a, 0xed, 0x6a, 0x1e, 0x01, 0x2d, 0x9e, 0x68, 0x32, 0xa9, 0x07, 0x60, 0x0a, 0x91, 0x81, 0x30, 0xc4, 0x6d, 0xc7, 0x78, 0xf9, 0x71, 0xad, 0x00, 0x38, 0x09, 0x29, 0x99, 0xa3, 0x33, 0xcb, 0x8b, 0x7a, 0x1a, 0x1d, 0xb9, 0x3d, 0x71, 0x40, 0x00, 0x3c, 0x2a, 0x4e, 0xce, 0xa9, 0xf9, 0x8d, 0x0a, 0xcc, 0x0a, 0x82, 0x91, 0xcd, 0xce, 0xc9, 0x7d, 0xcf, 0x8e, 0xc9, 0xb5, 0x5a, 0x7f, 0x88, 0xa4, 0x6b, 0x4d, 0xb5, 0xa8, 0x51, 0xf4, 0x41, 0x82, 0xe1, 0xc6, 0x8a, 0x00, 0x7e, 0x5e, 0x65, 0x5f, 0x6a, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, }; /* RFC 7919, Section A.4 */ static const uint8_t tls_ffdhe6144_prime[] = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xad, 0xf8, 0x54, 0x58, 0xa2, 0xbb, 0x4a, 0x9a, 0xaf, 0xdc, 0x56, 0x20, 0x27, 0x3d, 0x3c, 0xf1, 0xd8, 0xb9, 0xc5, 0x83, 0xce, 0x2d, 0x36, 0x95, 0xa9, 0xe1, 0x36, 0x41, 0x14, 0x64, 0x33, 0xfb, 0xcc, 0x93, 0x9d, 0xce, 0x24, 0x9b, 0x3e, 0xf9, 0x7d, 0x2f, 0xe3, 0x63, 0x63, 0x0c, 0x75, 0xd8, 0xf6, 0x81, 0xb2, 0x02, 0xae, 0xc4, 0x61, 0x7a, 0xd3, 0xdf, 0x1e, 0xd5, 0xd5, 0xfd, 0x65, 0x61, 0x24, 0x33, 0xf5, 0x1f, 0x5f, 0x06, 0x6e, 0xd0, 0x85, 0x63, 0x65, 0x55, 0x3d, 0xed, 0x1a, 0xf3, 0xb5, 0x57, 0x13, 0x5e, 0x7f, 0x57, 0xc9, 0x35, 0x98, 0x4f, 0x0c, 0x70, 0xe0, 0xe6, 0x8b, 0x77, 0xe2, 0xa6, 0x89, 0xda, 0xf3, 0xef, 0xe8, 0x72, 0x1d, 0xf1, 0x58, 0xa1, 0x36, 0xad, 0xe7, 0x35, 0x30, 0xac, 0xca, 0x4f, 0x48, 0x3a, 0x79, 0x7a, 0xbc, 0x0a, 0xb1, 0x82, 0xb3, 0x24, 0xfb, 0x61, 0xd1, 0x08, 0xa9, 0x4b, 0xb2, 0xc8, 0xe3, 0xfb, 0xb9, 0x6a, 0xda, 0xb7, 0x60, 0xd7, 0xf4, 0x68, 0x1d, 0x4f, 0x42, 0xa3, 0xde, 0x39, 0x4d, 0xf4, 0xae, 0x56, 0xed, 0xe7, 0x63, 0x72, 0xbb, 0x19, 0x0b, 0x07, 0xa7, 0xc8, 0xee, 0x0a, 0x6d, 0x70, 0x9e, 0x02, 0xfc, 0xe1, 0xcd, 0xf7, 0xe2, 0xec, 0xc0, 0x34, 0x04, 0xcd, 0x28, 0x34, 0x2f, 0x61, 0x91, 0x72, 0xfe, 0x9c, 0xe9, 0x85, 0x83, 0xff, 0x8e, 0x4f, 0x12, 0x32, 0xee, 0xf2, 0x81, 0x83, 0xc3, 0xfe, 0x3b, 0x1b, 0x4c, 0x6f, 0xad, 0x73, 0x3b, 0xb5, 0xfc, 0xbc, 0x2e, 0xc2, 0x20, 0x05, 0xc5, 0x8e, 0xf1, 0x83, 0x7d, 0x16, 0x83, 0xb2, 0xc6, 0xf3, 0x4a, 0x26, 0xc1, 0xb2, 0xef, 0xfa, 0x88, 0x6b, 0x42, 0x38, 0x61, 0x1f, 0xcf, 0xdc, 0xde, 0x35, 0x5b, 0x3b, 0x65, 0x19, 0x03, 0x5b, 0xbc, 0x34, 0xf4, 0xde, 0xf9, 0x9c, 0x02, 0x38, 0x61, 0xb4, 0x6f, 0xc9, 0xd6, 0xe6, 0xc9, 0x07, 0x7a, 0xd9, 0x1d, 0x26, 0x91, 0xf7, 0xf7, 0xee, 0x59, 0x8c, 0xb0, 0xfa, 0xc1, 0x86, 0xd9, 0x1c, 0xae, 0xfe, 0x13, 0x09, 0x85, 0x13, 0x92, 0x70, 0xb4, 0x13, 0x0c, 0x93, 0xbc, 0x43, 0x79, 0x44, 0xf4, 0xfd, 0x44, 0x52, 0xe2, 0xd7, 0x4d, 0xd3, 0x64, 0xf2, 0xe2, 0x1e, 0x71, 0xf5, 0x4b, 0xff, 0x5c, 0xae, 0x82, 0xab, 0x9c, 0x9d, 0xf6, 0x9e, 0xe8, 0x6d, 0x2b, 0xc5, 0x22, 0x36, 0x3a, 0x0d, 0xab, 0xc5, 0x21, 0x97, 0x9b, 0x0d, 0xea, 0xda, 0x1d, 0xbf, 0x9a, 0x42, 0xd5, 0xc4, 0x48, 0x4e, 0x0a, 0xbc, 0xd0, 0x6b, 0xfa, 0x53, 0xdd, 0xef, 0x3c, 0x1b, 0x20, 0xee, 0x3f, 0xd5, 0x9d, 0x7c, 0x25, 0xe4, 0x1d, 0x2b, 0x66, 0x9e, 0x1e, 0xf1, 0x6e, 0x6f, 0x52, 0xc3, 0x16, 0x4d, 0xf4, 0xfb, 0x79, 0x30, 0xe9, 0xe4, 0xe5, 0x88, 0x57, 0xb6, 0xac, 0x7d, 0x5f, 0x42, 0xd6, 0x9f, 0x6d, 0x18, 0x77, 0x63, 0xcf, 0x1d, 0x55, 0x03, 0x40, 0x04, 0x87, 0xf5, 0x5b, 0xa5, 0x7e, 0x31, 0xcc, 0x7a, 0x71, 0x35, 0xc8, 0x86, 0xef, 0xb4, 0x31, 0x8a, 0xed, 0x6a, 0x1e, 0x01, 0x2d, 0x9e, 0x68, 0x32, 0xa9, 0x07, 0x60, 0x0a, 0x91, 0x81, 0x30, 0xc4, 0x6d, 0xc7, 0x78, 0xf9, 0x71, 0xad, 0x00, 0x38, 0x09, 0x29, 0x99, 0xa3, 0x33, 0xcb, 0x8b, 0x7a, 0x1a, 0x1d, 0xb9, 0x3d, 0x71, 0x40, 0x00, 0x3c, 0x2a, 0x4e, 0xce, 0xa9, 0xf9, 0x8d, 0x0a, 0xcc, 0x0a, 0x82, 0x91, 0xcd, 0xce, 0xc9, 0x7d, 0xcf, 0x8e, 0xc9, 0xb5, 0x5a, 0x7f, 0x88, 0xa4, 0x6b, 0x4d, 0xb5, 0xa8, 0x51, 0xf4, 0x41, 0x82, 0xe1, 0xc6, 0x8a, 0x00, 0x7e, 0x5e, 0x0d, 0xd9, 0x02, 0x0b, 0xfd, 0x64, 0xb6, 0x45, 0x03, 0x6c, 0x7a, 0x4e, 0x67, 0x7d, 0x2c, 0x38, 0x53, 0x2a, 0x3a, 0x23, 0xba, 0x44, 0x42, 0xca, 0xf5, 0x3e, 0xa6, 0x3b, 0xb4, 0x54, 0x32, 0x9b, 0x76, 0x24, 0xc8, 0x91, 0x7b, 0xdd, 0x64, 0xb1, 0xc0, 0xfd, 0x4c, 0xb3, 0x8e, 0x8c, 0x33, 0x4c, 0x70, 0x1c, 0x3a, 0xcd, 0xad, 0x06, 0x57, 0xfc, 0xcf, 0xec, 0x71, 0x9b, 0x1f, 0x5c, 0x3e, 0x4e, 0x46, 0x04, 0x1f, 0x38, 0x81, 0x47, 0xfb, 0x4c, 0xfd, 0xb4, 0x77, 0xa5, 0x24, 0x71, 0xf7, 0xa9, 0xa9, 0x69, 0x10, 0xb8, 0x55, 0x32, 0x2e, 0xdb, 0x63, 0x40, 0xd8, 0xa0, 0x0e, 0xf0, 0x92, 0x35, 0x05, 0x11, 0xe3, 0x0a, 0xbe, 0xc1, 0xff, 0xf9, 0xe3, 0xa2, 0x6e, 0x7f, 0xb2, 0x9f, 0x8c, 0x18, 0x30, 0x23, 0xc3, 0x58, 0x7e, 0x38, 0xda, 0x00, 0x77, 0xd9, 0xb4, 0x76, 0x3e, 0x4e, 0x4b, 0x94, 0xb2, 0xbb, 0xc1, 0x94, 0xc6, 0x65, 0x1e, 0x77, 0xca, 0xf9, 0x92, 0xee, 0xaa, 0xc0, 0x23, 0x2a, 0x28, 0x1b, 0xf6, 0xb3, 0xa7, 0x39, 0xc1, 0x22, 0x61, 0x16, 0x82, 0x0a, 0xe8, 0xdb, 0x58, 0x47, 0xa6, 0x7c, 0xbe, 0xf9, 0xc9, 0x09, 0x1b, 0x46, 0x2d, 0x53, 0x8c, 0xd7, 0x2b, 0x03, 0x74, 0x6a, 0xe7, 0x7f, 0x5e, 0x62, 0x29, 0x2c, 0x31, 0x15, 0x62, 0xa8, 0x46, 0x50, 0x5d, 0xc8, 0x2d, 0xb8, 0x54, 0x33, 0x8a, 0xe4, 0x9f, 0x52, 0x35, 0xc9, 0x5b, 0x91, 0x17, 0x8c, 0xcf, 0x2d, 0xd5, 0xca, 0xce, 0xf4, 0x03, 0xec, 0x9d, 0x18, 0x10, 0xc6, 0x27, 0x2b, 0x04, 0x5b, 0x3b, 0x71, 0xf9, 0xdc, 0x6b, 0x80, 0xd6, 0x3f, 0xdd, 0x4a, 0x8e, 0x9a, 0xdb, 0x1e, 0x69, 0x62, 0xa6, 0x95, 0x26, 0xd4, 0x31, 0x61, 0xc1, 0xa4, 0x1d, 0x57, 0x0d, 0x79, 0x38, 0xda, 0xd4, 0xa4, 0x0e, 0x32, 0x9c, 0xd0, 0xe4, 0x0e, 0x65, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, }; /* RFC 7919, Section A.5 */ static const uint8_t tls_ffdhe8192_prime[] = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xad, 0xf8, 0x54, 0x58, 0xa2, 0xbb, 0x4a, 0x9a, 0xaf, 0xdc, 0x56, 0x20, 0x27, 0x3d, 0x3c, 0xf1, 0xd8, 0xb9, 0xc5, 0x83, 0xce, 0x2d, 0x36, 0x95, 0xa9, 0xe1, 0x36, 0x41, 0x14, 0x64, 0x33, 0xfb, 0xcc, 0x93, 0x9d, 0xce, 0x24, 0x9b, 0x3e, 0xf9, 0x7d, 0x2f, 0xe3, 0x63, 0x63, 0x0c, 0x75, 0xd8, 0xf6, 0x81, 0xb2, 0x02, 0xae, 0xc4, 0x61, 0x7a, 0xd3, 0xdf, 0x1e, 0xd5, 0xd5, 0xfd, 0x65, 0x61, 0x24, 0x33, 0xf5, 0x1f, 0x5f, 0x06, 0x6e, 0xd0, 0x85, 0x63, 0x65, 0x55, 0x3d, 0xed, 0x1a, 0xf3, 0xb5, 0x57, 0x13, 0x5e, 0x7f, 0x57, 0xc9, 0x35, 0x98, 0x4f, 0x0c, 0x70, 0xe0, 0xe6, 0x8b, 0x77, 0xe2, 0xa6, 0x89, 0xda, 0xf3, 0xef, 0xe8, 0x72, 0x1d, 0xf1, 0x58, 0xa1, 0x36, 0xad, 0xe7, 0x35, 0x30, 0xac, 0xca, 0x4f, 0x48, 0x3a, 0x79, 0x7a, 0xbc, 0x0a, 0xb1, 0x82, 0xb3, 0x24, 0xfb, 0x61, 0xd1, 0x08, 0xa9, 0x4b, 0xb2, 0xc8, 0xe3, 0xfb, 0xb9, 0x6a, 0xda, 0xb7, 0x60, 0xd7, 0xf4, 0x68, 0x1d, 0x4f, 0x42, 0xa3, 0xde, 0x39, 0x4d, 0xf4, 0xae, 0x56, 0xed, 0xe7, 0x63, 0x72, 0xbb, 0x19, 0x0b, 0x07, 0xa7, 0xc8, 0xee, 0x0a, 0x6d, 0x70, 0x9e, 0x02, 0xfc, 0xe1, 0xcd, 0xf7, 0xe2, 0xec, 0xc0, 0x34, 0x04, 0xcd, 0x28, 0x34, 0x2f, 0x61, 0x91, 0x72, 0xfe, 0x9c, 0xe9, 0x85, 0x83, 0xff, 0x8e, 0x4f, 0x12, 0x32, 0xee, 0xf2, 0x81, 0x83, 0xc3, 0xfe, 0x3b, 0x1b, 0x4c, 0x6f, 0xad, 0x73, 0x3b, 0xb5, 0xfc, 0xbc, 0x2e, 0xc2, 0x20, 0x05, 0xc5, 0x8e, 0xf1, 0x83, 0x7d, 0x16, 0x83, 0xb2, 0xc6, 0xf3, 0x4a, 0x26, 0xc1, 0xb2, 0xef, 0xfa, 0x88, 0x6b, 0x42, 0x38, 0x61, 0x1f, 0xcf, 0xdc, 0xde, 0x35, 0x5b, 0x3b, 0x65, 0x19, 0x03, 0x5b, 0xbc, 0x34, 0xf4, 0xde, 0xf9, 0x9c, 0x02, 0x38, 0x61, 0xb4, 0x6f, 0xc9, 0xd6, 0xe6, 0xc9, 0x07, 0x7a, 0xd9, 0x1d, 0x26, 0x91, 0xf7, 0xf7, 0xee, 0x59, 0x8c, 0xb0, 0xfa, 0xc1, 0x86, 0xd9, 0x1c, 0xae, 0xfe, 0x13, 0x09, 0x85, 0x13, 0x92, 0x70, 0xb4, 0x13, 0x0c, 0x93, 0xbc, 0x43, 0x79, 0x44, 0xf4, 0xfd, 0x44, 0x52, 0xe2, 0xd7, 0x4d, 0xd3, 0x64, 0xf2, 0xe2, 0x1e, 0x71, 0xf5, 0x4b, 0xff, 0x5c, 0xae, 0x82, 0xab, 0x9c, 0x9d, 0xf6, 0x9e, 0xe8, 0x6d, 0x2b, 0xc5, 0x22, 0x36, 0x3a, 0x0d, 0xab, 0xc5, 0x21, 0x97, 0x9b, 0x0d, 0xea, 0xda, 0x1d, 0xbf, 0x9a, 0x42, 0xd5, 0xc4, 0x48, 0x4e, 0x0a, 0xbc, 0xd0, 0x6b, 0xfa, 0x53, 0xdd, 0xef, 0x3c, 0x1b, 0x20, 0xee, 0x3f, 0xd5, 0x9d, 0x7c, 0x25, 0xe4, 0x1d, 0x2b, 0x66, 0x9e, 0x1e, 0xf1, 0x6e, 0x6f, 0x52, 0xc3, 0x16, 0x4d, 0xf4, 0xfb, 0x79, 0x30, 0xe9, 0xe4, 0xe5, 0x88, 0x57, 0xb6, 0xac, 0x7d, 0x5f, 0x42, 0xd6, 0x9f, 0x6d, 0x18, 0x77, 0x63, 0xcf, 0x1d, 0x55, 0x03, 0x40, 0x04, 0x87, 0xf5, 0x5b, 0xa5, 0x7e, 0x31, 0xcc, 0x7a, 0x71, 0x35, 0xc8, 0x86, 0xef, 0xb4, 0x31, 0x8a, 0xed, 0x6a, 0x1e, 0x01, 0x2d, 0x9e, 0x68, 0x32, 0xa9, 0x07, 0x60, 0x0a, 0x91, 0x81, 0x30, 0xc4, 0x6d, 0xc7, 0x78, 0xf9, 0x71, 0xad, 0x00, 0x38, 0x09, 0x29, 0x99, 0xa3, 0x33, 0xcb, 0x8b, 0x7a, 0x1a, 0x1d, 0xb9, 0x3d, 0x71, 0x40, 0x00, 0x3c, 0x2a, 0x4e, 0xce, 0xa9, 0xf9, 0x8d, 0x0a, 0xcc, 0x0a, 0x82, 0x91, 0xcd, 0xce, 0xc9, 0x7d, 0xcf, 0x8e, 0xc9, 0xb5, 0x5a, 0x7f, 0x88, 0xa4, 0x6b, 0x4d, 0xb5, 0xa8, 0x51, 0xf4, 0x41, 0x82, 0xe1, 0xc6, 0x8a, 0x00, 0x7e, 0x5e, 0x0d, 0xd9, 0x02, 0x0b, 0xfd, 0x64, 0xb6, 0x45, 0x03, 0x6c, 0x7a, 0x4e, 0x67, 0x7d, 0x2c, 0x38, 0x53, 0x2a, 0x3a, 0x23, 0xba, 0x44, 0x42, 0xca, 0xf5, 0x3e, 0xa6, 0x3b, 0xb4, 0x54, 0x32, 0x9b, 0x76, 0x24, 0xc8, 0x91, 0x7b, 0xdd, 0x64, 0xb1, 0xc0, 0xfd, 0x4c, 0xb3, 0x8e, 0x8c, 0x33, 0x4c, 0x70, 0x1c, 0x3a, 0xcd, 0xad, 0x06, 0x57, 0xfc, 0xcf, 0xec, 0x71, 0x9b, 0x1f, 0x5c, 0x3e, 0x4e, 0x46, 0x04, 0x1f, 0x38, 0x81, 0x47, 0xfb, 0x4c, 0xfd, 0xb4, 0x77, 0xa5, 0x24, 0x71, 0xf7, 0xa9, 0xa9, 0x69, 0x10, 0xb8, 0x55, 0x32, 0x2e, 0xdb, 0x63, 0x40, 0xd8, 0xa0, 0x0e, 0xf0, 0x92, 0x35, 0x05, 0x11, 0xe3, 0x0a, 0xbe, 0xc1, 0xff, 0xf9, 0xe3, 0xa2, 0x6e, 0x7f, 0xb2, 0x9f, 0x8c, 0x18, 0x30, 0x23, 0xc3, 0x58, 0x7e, 0x38, 0xda, 0x00, 0x77, 0xd9, 0xb4, 0x76, 0x3e, 0x4e, 0x4b, 0x94, 0xb2, 0xbb, 0xc1, 0x94, 0xc6, 0x65, 0x1e, 0x77, 0xca, 0xf9, 0x92, 0xee, 0xaa, 0xc0, 0x23, 0x2a, 0x28, 0x1b, 0xf6, 0xb3, 0xa7, 0x39, 0xc1, 0x22, 0x61, 0x16, 0x82, 0x0a, 0xe8, 0xdb, 0x58, 0x47, 0xa6, 0x7c, 0xbe, 0xf9, 0xc9, 0x09, 0x1b, 0x46, 0x2d, 0x53, 0x8c, 0xd7, 0x2b, 0x03, 0x74, 0x6a, 0xe7, 0x7f, 0x5e, 0x62, 0x29, 0x2c, 0x31, 0x15, 0x62, 0xa8, 0x46, 0x50, 0x5d, 0xc8, 0x2d, 0xb8, 0x54, 0x33, 0x8a, 0xe4, 0x9f, 0x52, 0x35, 0xc9, 0x5b, 0x91, 0x17, 0x8c, 0xcf, 0x2d, 0xd5, 0xca, 0xce, 0xf4, 0x03, 0xec, 0x9d, 0x18, 0x10, 0xc6, 0x27, 0x2b, 0x04, 0x5b, 0x3b, 0x71, 0xf9, 0xdc, 0x6b, 0x80, 0xd6, 0x3f, 0xdd, 0x4a, 0x8e, 0x9a, 0xdb, 0x1e, 0x69, 0x62, 0xa6, 0x95, 0x26, 0xd4, 0x31, 0x61, 0xc1, 0xa4, 0x1d, 0x57, 0x0d, 0x79, 0x38, 0xda, 0xd4, 0xa4, 0x0e, 0x32, 0x9c, 0xcf, 0xf4, 0x6a, 0xaa, 0x36, 0xad, 0x00, 0x4c, 0xf6, 0x00, 0xc8, 0x38, 0x1e, 0x42, 0x5a, 0x31, 0xd9, 0x51, 0xae, 0x64, 0xfd, 0xb2, 0x3f, 0xce, 0xc9, 0x50, 0x9d, 0x43, 0x68, 0x7f, 0xeb, 0x69, 0xed, 0xd1, 0xcc, 0x5e, 0x0b, 0x8c, 0xc3, 0xbd, 0xf6, 0x4b, 0x10, 0xef, 0x86, 0xb6, 0x31, 0x42, 0xa3, 0xab, 0x88, 0x29, 0x55, 0x5b, 0x2f, 0x74, 0x7c, 0x93, 0x26, 0x65, 0xcb, 0x2c, 0x0f, 0x1c, 0xc0, 0x1b, 0xd7, 0x02, 0x29, 0x38, 0x88, 0x39, 0xd2, 0xaf, 0x05, 0xe4, 0x54, 0x50, 0x4a, 0xc7, 0x8b, 0x75, 0x82, 0x82, 0x28, 0x46, 0xc0, 0xba, 0x35, 0xc3, 0x5f, 0x5c, 0x59, 0x16, 0x0c, 0xc0, 0x46, 0xfd, 0x82, 0x51, 0x54, 0x1f, 0xc6, 0x8c, 0x9c, 0x86, 0xb0, 0x22, 0xbb, 0x70, 0x99, 0x87, 0x6a, 0x46, 0x0e, 0x74, 0x51, 0xa8, 0xa9, 0x31, 0x09, 0x70, 0x3f, 0xee, 0x1c, 0x21, 0x7e, 0x6c, 0x38, 0x26, 0xe5, 0x2c, 0x51, 0xaa, 0x69, 0x1e, 0x0e, 0x42, 0x3c, 0xfc, 0x99, 0xe9, 0xe3, 0x16, 0x50, 0xc1, 0x21, 0x7b, 0x62, 0x48, 0x16, 0xcd, 0xad, 0x9a, 0x95, 0xf9, 0xd5, 0xb8, 0x01, 0x94, 0x88, 0xd9, 0xc0, 0xa0, 0xa1, 0xfe, 0x30, 0x75, 0xa5, 0x77, 0xe2, 0x31, 0x83, 0xf8, 0x1d, 0x4a, 0x3f, 0x2f, 0xa4, 0x57, 0x1e, 0xfc, 0x8c, 0xe0, 0xba, 0x8a, 0x4f, 0xe8, 0xb6, 0x85, 0x5d, 0xfe, 0x72, 0xb0, 0xa6, 0x6e, 0xde, 0xd2, 0xfb, 0xab, 0xfb, 0xe5, 0x8a, 0x30, 0xfa, 0xfa, 0xbe, 0x1c, 0x5d, 0x71, 0xa8, 0x7e, 0x2f, 0x74, 0x1e, 0xf8, 0xc1, 0xfe, 0x86, 0xfe, 0xa6, 0xbb, 0xfd, 0xe5, 0x30, 0x67, 0x7f, 0x0d, 0x97, 0xd1, 0x1d, 0x49, 0xf7, 0xa8, 0x44, 0x3d, 0x08, 0x22, 0xe5, 0x06, 0xa9, 0xf4, 0x61, 0x4e, 0x01, 0x1e, 0x2a, 0x94, 0x83, 0x8f, 0xf8, 0x8c, 0xd6, 0x8c, 0x8b, 0xb7, 0xc5, 0xc6, 0x42, 0x4c, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, }; /* RFC 3526, Section 3 */ static const uint8_t tls_dh14_prime[] = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc9, 0x0f, 0xda, 0xa2, 0x21, 0x68, 0xc2, 0x34, 0xc4, 0xc6, 0x62, 0x8b, 0x80, 0xdc, 0x1c, 0xd1, 0x29, 0x02, 0x4e, 0x08, 0x8a, 0x67, 0xcc, 0x74, 0x02, 0x0b, 0xbe, 0xa6, 0x3b, 0x13, 0x9b, 0x22, 0x51, 0x4a, 0x08, 0x79, 0x8e, 0x34, 0x04, 0xdd, 0xef, 0x95, 0x19, 0xb3, 0xcd, 0x3a, 0x43, 0x1b, 0x30, 0x2b, 0x0a, 0x6d, 0xf2, 0x5f, 0x14, 0x37, 0x4f, 0xe1, 0x35, 0x6d, 0x6d, 0x51, 0xc2, 0x45, 0xe4, 0x85, 0xb5, 0x76, 0x62, 0x5e, 0x7e, 0xc6, 0xf4, 0x4c, 0x42, 0xe9, 0xa6, 0x37, 0xed, 0x6b, 0x0b, 0xff, 0x5c, 0xb6, 0xf4, 0x06, 0xb7, 0xed, 0xee, 0x38, 0x6b, 0xfb, 0x5a, 0x89, 0x9f, 0xa5, 0xae, 0x9f, 0x24, 0x11, 0x7c, 0x4b, 0x1f, 0xe6, 0x49, 0x28, 0x66, 0x51, 0xec, 0xe4, 0x5b, 0x3d, 0xc2, 0x00, 0x7c, 0xb8, 0xa1, 0x63, 0xbf, 0x05, 0x98, 0xda, 0x48, 0x36, 0x1c, 0x55, 0xd3, 0x9a, 0x69, 0x16, 0x3f, 0xa8, 0xfd, 0x24, 0xcf, 0x5f, 0x83, 0x65, 0x5d, 0x23, 0xdc, 0xa3, 0xad, 0x96, 0x1c, 0x62, 0xf3, 0x56, 0x20, 0x85, 0x52, 0xbb, 0x9e, 0xd5, 0x29, 0x07, 0x70, 0x96, 0x96, 0x6d, 0x67, 0x0c, 0x35, 0x4e, 0x4a, 0xbc, 0x98, 0x04, 0xf1, 0x74, 0x6c, 0x08, 0xca, 0x18, 0x21, 0x7c, 0x32, 0x90, 0x5e, 0x46, 0x2e, 0x36, 0xce, 0x3b, 0xe3, 0x9e, 0x77, 0x2c, 0x18, 0x0e, 0x86, 0x03, 0x9b, 0x27, 0x83, 0xa2, 0xec, 0x07, 0xa2, 0x8f, 0xb5, 0xc5, 0x5d, 0xf0, 0x6f, 0x4c, 0x52, 0xc9, 0xde, 0x2b, 0xcb, 0xf6, 0x95, 0x58, 0x17, 0x18, 0x39, 0x95, 0x49, 0x7c, 0xea, 0x95, 0x6a, 0xe5, 0x15, 0xd2, 0x26, 0x18, 0x98, 0xfa, 0x05, 0x10, 0x15, 0x72, 0x8e, 0x5a, 0x8a, 0xac, 0xaa, 0x68, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, }; static const struct tls_named_group tls_group_pref[] = { { "secp256r1", 23, TLS_GROUP_TYPE_EC }, { "secp384r1", 24, TLS_GROUP_TYPE_EC }, { "ffdhe2048", 256, TLS_GROUP_TYPE_FF, .ff = { .prime = tls_ffdhe2048_prime, .prime_len = sizeof(tls_ffdhe2048_prime), .generator = 2, }, }, { "ffdhe3072", 257, TLS_GROUP_TYPE_FF, .ff = { .prime = tls_ffdhe3072_prime, .prime_len = sizeof(tls_ffdhe3072_prime), .generator = 2, }, }, { "ffdhe4096", 258, TLS_GROUP_TYPE_FF, .ff = { .prime = tls_ffdhe4096_prime, .prime_len = sizeof(tls_ffdhe4096_prime), .generator = 2, }, }, { "ffdhe6144", 259, TLS_GROUP_TYPE_FF, .ff = { .prime = tls_ffdhe6144_prime, .prime_len = sizeof(tls_ffdhe6144_prime), .generator = 2, }, }, { "ffdhe8192", 260, TLS_GROUP_TYPE_FF, .ff = { .prime = tls_ffdhe8192_prime, .prime_len = sizeof(tls_ffdhe8192_prime), .generator = 2, }, }, }; /* * For now hardcode a default group for non-RFC7919 clients - same group * as some other TLS servers use, which is actually a downside because the * more common the group parameters are the less secure they are assumed * to be, but it is also a test that the group is sufficiently good. * * Eventually we need to make this configurable so that a unique * likely-prime number generated by either 'openssl dhparam' or * 'ssh-keygen -G' can be set, or parse /etc/ssh/moduli to select * a random pre-generated FFDH group each time. */ static const struct tls_named_group tls_default_ffdh_group = { "RFC3526/Oakley Group 14", 0, TLS_GROUP_TYPE_FF, .ff = { .prime = tls_dh14_prime, .prime_len = sizeof(tls_dh14_prime), .generator = 2, }, }; /* RFC 8422, Section 5.1 + RFC 7919 */ static ssize_t tls_elliptic_curves_client_write(struct l_tls *tls, uint8_t *buf, size_t len) { uint8_t *ptr = buf; unsigned int i; if (len < 2 + L_ARRAY_SIZE(tls_group_pref) * 2) return -ENOMEM; l_put_be16(L_ARRAY_SIZE(tls_group_pref) * 2, ptr); ptr += 2; for (i = 0; i < L_ARRAY_SIZE(tls_group_pref); i++) { l_put_be16(tls_group_pref[i].id, ptr); ptr += 2; } return ptr - buf; } static bool tls_elliptic_curves_client_handle(struct l_tls *tls, const uint8_t *buf, size_t len) { bool ffdh_offered = false; if (len < 2) return false; if (l_get_be16(buf) != len - 2 || (len & 1)) return false; buf += 2; len -= 2; /* * We select one group for DH and one group for ECDH and we'll * let the cipher suite selection logic decide which one is actually * used. It will take into account the client's cipher suite * preference but it could just as well look at the strengths of * the groups chosen. This is not done for simplicity but RFC 7919 * suggests the Supported Groups should actually overrule the * cipher suite preference list in case of a conflict: * "A server that encounters such a contradiction when selecting * between an ECDHE or FFDHE key exchange mechanism while trying * to respect client preferences SHOULD give priority to the * Supported Groups extension (...) but MAY resolve the * contradiction any way it sees fit." * * Not implemented: "If a non-anonymous FFDHE cipher suite is * selected and the TLS client has used this extension to offer * an FFDHE group of comparable or greater strength than the server's * public key, the server SHOULD select an FFDHE group at least * as strong as the server's public key." */ while (len) { unsigned int i; uint16_t id; const struct tls_named_group *group = NULL; id = l_get_be16(buf); buf += 2; len -= 2; if (id >> 8 == 1) /* RFC 7919 ids */ ffdh_offered = true; for (i = 0; i < L_ARRAY_SIZE(tls_group_pref); i++) if (tls_group_pref[i].id == id) { group = &tls_group_pref[i]; break; } if (!group) continue; switch (group->type) { case TLS_GROUP_TYPE_EC: if (!tls->negotiated_curve) tls->negotiated_curve = group; break; case TLS_GROUP_TYPE_FF: if (!tls->negotiated_ff_group) tls->negotiated_ff_group = group; break; } } /* * Note we need to treat DH slightly differently from ECDH groups * here because the extension is defined in RFC 8422 and if the * client offers no elliptic curves we can't use ECDH at all: * "If a server (...) is unable to complete the ECC handshake while * restricting itself to the enumerated curves (...), it MUST NOT * negotiate the use of an ECC cipher suite. Depending on what * other cipher suites are proposed by the client and supported by * the server, this may result in a fatal handshake failure alert * due to the lack of common cipher suites." * * On the other hand if the client offers no FFDH groups we can * only assume the client is okay with us picking a group. Note * the "includes any FFDHE group" part in RFC 7919 Section 4: * "If a compatible TLS server receives a Supported Groups * extension from a client that includes any FFDHE group (i.e., * any codepoint between 256 and 511, inclusive, even if unknown * to the server), and if none of the client-proposed FFDHE groups * are known and acceptable to the server, then the server MUST * NOT select an FFDHE cipher suite." */ if (tls->negotiated_curve) TLS_DEBUG("Negotiated %s", tls->negotiated_curve->name); else TLS_DEBUG("non-fatal: No common supported elliptic curves " "for ECDHE"); if (tls->negotiated_ff_group) TLS_DEBUG("Negotiated %s", tls->negotiated_ff_group->name); else if (ffdh_offered) TLS_DEBUG("non-fatal: No common supported finite-field groups " "for DHE"); else tls->negotiated_ff_group = &tls_default_ffdh_group; return true; } static bool tls_elliptic_curves_client_absent(struct l_tls *tls) { unsigned int i; for (i = 0; i < L_ARRAY_SIZE(tls_group_pref); i++) if (tls_group_pref[i].type == TLS_GROUP_TYPE_EC) { tls->negotiated_curve = &tls_group_pref[i]; break; } tls->negotiated_ff_group = &tls_default_ffdh_group; return true; } static bool tls_ec_point_formats_client_handle(struct l_tls *tls, const uint8_t *buf, size_t len) { if (len < 2) return false; if (buf[0] != len - 1) return false; if (!memchr(buf + 1, 0, len - 1)) { TLS_DEBUG("Uncompressed point format missing"); return false; } return true; } /* * For compatibility with clients respond to a valid Client Hello Supported * Point Formats extension with the hardcoded confirmation that we do * support the single valid point format. As a client we never send this * extension so we never have to handle a server response to it either. */ static ssize_t tls_ec_point_formats_server_write(struct l_tls *tls, uint8_t *buf, size_t len) { SKIP_ON_RESUMPTION(); /* RFC 4492 Section 4 */ if (len < 2) return -ENOMEM; buf[0] = 0x01; /* ec_point_format_list length */ buf[1] = 0x00; /* uncompressed */ return 2; } /* * This is used to append the list of signature algorithm and hash type * combinations we support to the Signature Algorithms client hello * extension (on the client) and the Certificate Request message (on the * server). In both cases we need to list the algorithms we support for * two use cases: certificate chain verification and signing/verifying * Server Key Exchange params (server->client) or Certificate Verify * data (client->server). * * For the server side RFC 5462, Section 7.4.1.4.1 says: * "If the client [...] is willing to use them for verifying * messages sent by the server, i.e., server certificates and * server key exchange [...] it MUST send the * signature_algorithms extension, listing the algorithms it * is willing to accept." * * As for the certificate chains we mostly rely on the kernel to do * this so when we receive the list we do not currently verify the * that the whole chain uses only algorithms from the list on either * side (TODO). But we know that the chain verification in the kernel * can use a superset of the hash algorithms l_checksum supports. * For the Server Key Exchange and Certificate Verify signatures we * use l_checksum but we need to map the TLS-specific hash IDs to * enum l_checksum_type using the tls_handshake_hash_data list in * signature->sign() and signature->verify(), so we use * tls_handshake_hash_data as the definitive list of allowed hash * algorithms. * * Our supported signature algorithms can work with any hash type so we * basically have to send all possible combinations of the signature * algorithm IDs from the supported cipher suites (except anonymous) * with the hash algorithms we can use for signature verification, * i.e. those in the tls_handshake_hash_data table. */ ssize_t tls_write_signature_algorithms(struct l_tls *tls, uint8_t *buf, size_t len) { uint8_t *ptr = buf; unsigned int i, j; struct tls_cipher_suite **suite; uint8_t sig_alg_ids[16]; uint8_t hash_ids[16]; unsigned int sig_alg_cnt = 0; unsigned int hash_cnt = 0; for (suite = tls->cipher_suite_pref_list; *suite; suite++) { uint8_t id; if (!(*suite)->signature) continue; id = (*suite)->signature->id; if (memchr(sig_alg_ids, id, sig_alg_cnt)) continue; if (!tls_cipher_suite_is_compatible(tls, *suite, NULL)) continue; if (sig_alg_cnt >= sizeof(sig_alg_ids)) return -ENOMEM; sig_alg_ids[sig_alg_cnt++] = id; } for (i = 0; i < __HANDSHAKE_HASH_COUNT; i++) { const struct tls_hash_algorithm *hash = &tls_handshake_hash_data[i]; bool supported; /* * The hash types in the Signature Algorithms extension are * all supported hashes but the ones in the Certificate * Request (server->client) must be in the set for which we * maintain handshake message hashes because that is going * to be used in Certificate Verify. */ if (tls->server) supported = !!tls->handshake_hash[i]; else supported = l_checksum_is_supported(hash->l_id, false); if (supported) hash_ids[hash_cnt++] = hash->tls_id; } if (len < 2 + sig_alg_cnt * hash_cnt * 2) return -ENOMEM; l_put_be16(sig_alg_cnt * hash_cnt * 2, ptr); ptr += 2; for (i = 0; i < sig_alg_cnt; i++) for (j = 0; j < hash_cnt; j++) { *ptr++ = hash_ids[j]; *ptr++ = sig_alg_ids[i]; } return ptr - buf; } ssize_t tls_parse_signature_algorithms(struct l_tls *tls, const uint8_t *buf, size_t len) { const uint8_t *ptr = buf; enum handshake_hash_type first_supported, hash; const struct tls_hash_algorithm *preferred; struct tls_cipher_suite **suite; uint8_t sig_alg_ids[16]; unsigned int sig_alg_cnt = 0; /* * This only makes sense as a variable-length field, assume * there's a typo in RFC5246 7.4.4 here. */ if (len < 4) return -EINVAL; if (l_get_be16(ptr) > len - 2) return -EINVAL; len = l_get_be16(ptr); ptr += 2; if (len & 1) return -EINVAL; for (suite = tls->cipher_suite_pref_list; *suite; suite++) { uint8_t id; if (!(*suite)->signature) continue; id = (*suite)->signature->id; if (memchr(sig_alg_ids, id, sig_alg_cnt)) continue; if (!tls_cipher_suite_is_compatible(tls, *suite, NULL)) continue; if (sig_alg_cnt >= sizeof(sig_alg_ids)) return -ENOMEM; sig_alg_ids[sig_alg_cnt++] = id; } /* * In 1.2 we force our preference for SHA256/SHA384 (depending on * cipher suite's PRF hmac) if it is supported by the peer because * that must be supported anyway for the PRF and the Finished hash * meaning that we only need to keep one hash instead of two. * If not available fall back to the first common hash algorithm. */ first_supported = -1; if (tls->prf_hmac) preferred = tls->prf_hmac; else preferred = &tls_handshake_hash_data[HANDSHAKE_HASH_SHA256]; while (len) { uint8_t hash_id = *ptr++; uint8_t sig_alg_id = *ptr++; bool supported; len -= 2; /* Ignore hash types for signatures other than ours */ if (tls->pending.cipher_suite && (!tls->pending.cipher_suite->signature || tls->pending.cipher_suite->signature->id != sig_alg_id)) continue; if (!tls->pending.cipher_suite && !memchr(sig_alg_ids, sig_alg_id, sig_alg_cnt)) continue; if (hash_id == preferred->tls_id) { for (hash = 0; hash < __HANDSHAKE_HASH_COUNT; hash++) if (&tls_handshake_hash_data[hash] == preferred) break; break; } if ((int) first_supported != -1) continue; for (hash = 0; hash < __HANDSHAKE_HASH_COUNT; hash++) if (hash_id == tls_handshake_hash_data[hash].tls_id) break; if (hash == __HANDSHAKE_HASH_COUNT) continue; if (tls->server) supported = l_checksum_is_supported( tls_handshake_hash_data[hash].l_id, false); else supported = !!tls->handshake_hash[hash]; if (supported) first_supported = hash; } if (len) tls->signature_hash = hash; else if ((int) first_supported != -1) tls->signature_hash = first_supported; else return -ENOTSUP; return ptr + len - buf; } /* RFC 5246, Section 7.4.1.4.1 */ static ssize_t tls_signature_algorithms_client_write(struct l_tls *tls, uint8_t *buf, size_t len) { /* * "Note: this extension is not meaningful for TLS versions * prior to 1.2. Clients MUST NOT offer it if they are offering * prior versions." */ if (tls->max_version < L_TLS_V12) return -ENOMSG; return tls_write_signature_algorithms(tls, buf, len); } static bool tls_signature_algorithms_client_handle(struct l_tls *tls, const uint8_t *buf, size_t len) { ssize_t ret; /* * "However, even if clients do offer it, the rules specified in * [TLSEXT] require servers to ignore extensions they do not * understand." */ if (tls->max_version < L_TLS_V12) return true; ret = tls_parse_signature_algorithms(tls, buf, len); if (ret == -ENOTSUP) TLS_DEBUG("No common signature algorithms"); /* * TODO: also check our certificate chain against the parsed * signature algorithms. */ return ret == (ssize_t) len; } static bool tls_signature_algorithms_client_absent(struct l_tls *tls) { /* * "If the client does not send the signature_algorithms extension, * the server MUST do the following: * - [...] behave as if client had sent the value {sha1,rsa}. * - [...] behave as if client had sent the value {sha1,dsa}. * - [...] behave as if client had sent the value {sha1,ecdsa}. */ if (tls->max_version >= L_TLS_V12) tls->signature_hash = HANDSHAKE_HASH_SHA1; return true; } /* RFC 5746, Section 3.2 */ static ssize_t tls_renegotiation_info_client_write(struct l_tls *tls, uint8_t *buf, size_t len) { /* * Section 4.2 implies we should send the client_verify_data on * renegotiation even if .secure_renegotiation is false, if we want * to go through with the renegotiation in the first place. */ if (tls->ready) { size_t vdl = tls_verify_data_length(tls, 1); if (len < vdl + 1) return -ENOMEM; buf[0] = vdl; memcpy(buf + 1, tls->renegotiation_info.client_verify_data, vdl); return 1 + vdl; } else { if (len < 1) return -ENOMEM; buf[0] = 0x00; /* Empty "renegotiated_connection" */ return 1; } } static ssize_t tls_renegotiation_info_server_write(struct l_tls *tls, uint8_t *buf, size_t len) { if (tls->ready) { size_t rx_vdl = tls_verify_data_length(tls, 0); size_t tx_vdl = tls_verify_data_length(tls, 1); if (len < rx_vdl + tx_vdl + 1) return -ENOMEM; buf[0] = rx_vdl + tx_vdl; memcpy(buf + 1, tls->renegotiation_info.client_verify_data, rx_vdl); memcpy(buf + 1 + rx_vdl, tls->renegotiation_info.server_verify_data, tx_vdl); return 1 + rx_vdl + tx_vdl; } else { if (len < 1) return -ENOMEM; buf[0] = 0x00; /* Empty "renegotiated_connection" */ return 1; } } static bool tls_renegotiation_info_client_handle(struct l_tls *tls, const uint8_t *buf, size_t len) { if (tls->ready) { size_t vdl = tls_verify_data_length(tls, 0); return len >= 1 + vdl && tls->renegotiation_info.secure_renegotiation && !memcmp(tls->renegotiation_info.client_verify_data, buf + 1, vdl); } /* * RFC 5746 Section 3.6: "The server MUST then verify that the length * of the "renegotiated_connection" field is zero, ..." */ if (len < 1 || buf[0] != 0x00) return false; tls->renegotiation_info.secure_renegotiation = true; return true; } static bool tls_renegotiation_info_server_handle(struct l_tls *tls, const uint8_t *buf, size_t len) { if (tls->ready) { size_t rx_vdl = tls_verify_data_length(tls, 0); size_t tx_vdl = tls_verify_data_length(tls, 1); return len >= 1 + rx_vdl + tx_vdl && tls->renegotiation_info.secure_renegotiation && !memcmp(tls->renegotiation_info.client_verify_data, buf + 1, tx_vdl) && !memcmp(tls->renegotiation_info.server_verify_data, buf + 1 + tx_vdl, rx_vdl); } /* * RFC 5746 Section 3.4: "The client MUST then verify that the length * of the "renegotiated_connection" field is zero, ..." */ if (len < 1 || buf[0] != 0x00) return false; tls->renegotiation_info.secure_renegotiation = true; return true; } static bool tls_renegotiation_info_absent(struct l_tls *tls) { /* * RFC 5746 Section 4.2: "It is possible that un-upgraded servers * will request that the client renegotiate. It is RECOMMENDED * that clients refuse this renegotiation request." and Section 4.4: * "It is RECOMMENDED that servers not permit legacy renegotiation." * * This may need to be made configurable, for now follow the * recommendation and don't renegotiate. */ if (tls->ready) return false; /* * The normal policy otherwise is that the extension must be * present in renegotiation if the previous Client or Server Hello * did include this extension, or the SCSV in the Client Hello case. */ return !tls->ready || !tls->renegotiation_info.secure_renegotiation; } const struct tls_hello_extension tls_extensions[] = { { "Supported Groups", "elliptic_curves", 10, tls_elliptic_curves_client_write, tls_elliptic_curves_client_handle, tls_elliptic_curves_client_absent, NULL, NULL, NULL, }, { "Supported Point Formats", "ec_point_formats", 11, NULL, tls_ec_point_formats_client_handle, NULL, tls_ec_point_formats_server_write, NULL, NULL, }, { "Signature Algorithms", "signature_algoritms", 13, tls_signature_algorithms_client_write, tls_signature_algorithms_client_handle, tls_signature_algorithms_client_absent, NULL, NULL, NULL, }, { "Secure Renegotiation", "renegotiation_info", 0xff01, tls_renegotiation_info_client_write, tls_renegotiation_info_client_handle, tls_renegotiation_info_absent, tls_renegotiation_info_server_write, tls_renegotiation_info_server_handle, tls_renegotiation_info_absent, }, {} }; const struct tls_named_group *tls_find_group(uint16_t id) { unsigned int i; for (i = 0; i < L_ARRAY_SIZE(tls_group_pref); i++) if (tls_group_pref[i].id == id) return &tls_group_pref[i]; return NULL; } const struct tls_named_group *tls_find_ff_group(const uint8_t *prime, size_t prime_len, const uint8_t *generator, size_t generator_len) { unsigned int i; if (generator_len != 1) return NULL; for (i = 0; i < L_ARRAY_SIZE(tls_group_pref); i++) { const struct tls_named_group *g = &tls_group_pref[i]; if (g->type != TLS_GROUP_TYPE_FF) continue; if (g->ff.prime_len != prime_len || memcmp(prime, g->ff.prime, prime_len)) continue; if (g->ff.generator != *generator) continue; return g; } return NULL; } bluez-5.82/ell/PaxHeaders/uuid.h0000644000000000000000000000005014504767710013554 xustar0020 atime=1743575449 20 ctime=1743591276 bluez-5.82/ell/uuid.h0000644000000000000000000000217514504767710013242 0ustar00rootroot/* * Embedded Linux library * Copyright (C) 2011-2015 Intel Corporation * * SPDX-License-Identifier: LGPL-2.1-or-later */ #ifndef __ELL_UUID_H #define __ELL_UUID_H #include #include #ifdef __cplusplus extern "C" { #endif enum l_uuid_version { L_UUID_VERSION_1_TIME = 1, L_UUID_VERSION_2_DCE = 2, L_UUID_VERSION_3_MD5 = 3, L_UUID_VERSION_4_RANDOM = 4, L_UUID_VERSION_5_SHA1 = 5, }; extern const uint8_t L_UUID_NAMESPACE_DNS[]; extern const uint8_t L_UUID_NAMESPACE_URL[]; extern const uint8_t L_UUID_NAMESPACE_OID[]; extern const uint8_t L_UUID_NAMESPACE_X500[]; bool l_uuid_v3(const uint8_t nsid[16], const void *name, size_t name_size, uint8_t out_uuid[16]); bool l_uuid_v4(uint8_t out_uuid[16]); bool l_uuid_v5(const uint8_t nsid[16], const void *name, size_t name_size, uint8_t out_uuid[16]); bool l_uuid_is_valid(const uint8_t uuid[16]); enum l_uuid_version l_uuid_get_version(const uint8_t uuid[16]); bool l_uuid_to_string(const uint8_t uuid[16], char *dest, size_t dest_size); bool l_uuid_from_string(const char *src, uint8_t uuid[16]); #ifdef __cplusplus } #endif #endif /* __ELL_UTIL_H */ bluez-5.82/ell/PaxHeaders/dbus-client.h0000644000000000000000000000005014504767710015017 xustar0020 atime=1743575449 20 ctime=1743591276 bluez-5.82/ell/dbus-client.h0000644000000000000000000000475214504767710014510 0ustar00rootroot/* * Embedded Linux library * Copyright (C) 2011-2014 Intel Corporation * Copyright (C) 2017 Codecoup * * SPDX-License-Identifier: LGPL-2.1-or-later */ #ifndef __ELL_DBUS_CLIENT_H #define __ELL_DBUS_CLIENT_H #include #ifdef __cplusplus extern "C" { #endif struct l_dbus; struct l_dbus_message; struct l_dbus_client; struct l_dbus_proxy; typedef void (*l_dbus_client_ready_func_t)(struct l_dbus_client *client, void *user_data); typedef void (*l_dbus_client_proxy_func_t) (struct l_dbus_proxy *proxy, void *user_data); typedef void (*l_dbus_client_proxy_result_func_t) (struct l_dbus_proxy *proxy, struct l_dbus_message *result, void *user_data); typedef void (*l_dbus_client_property_function_t) (struct l_dbus_proxy *proxy, const char *name, struct l_dbus_message *msg, void *user_data); struct l_dbus_client *l_dbus_client_new(struct l_dbus *dbus, const char *service, const char *path); void l_dbus_client_destroy(struct l_dbus_client *client); bool l_dbus_client_set_connect_handler(struct l_dbus_client *client, l_dbus_watch_func_t function, void *user_data, l_dbus_destroy_func_t destroy); bool l_dbus_client_set_disconnect_handler(struct l_dbus_client *client, l_dbus_watch_func_t function, void *user_data, l_dbus_destroy_func_t destroy); bool l_dbus_client_set_ready_handler(struct l_dbus_client *client, l_dbus_client_ready_func_t function, void *user_data, l_dbus_destroy_func_t destroy); bool l_dbus_client_set_proxy_handlers(struct l_dbus_client *client, l_dbus_client_proxy_func_t proxy_added, l_dbus_client_proxy_func_t proxy_removed, l_dbus_client_property_function_t property_changed, void *user_data, l_dbus_destroy_func_t destroy); const char *l_dbus_proxy_get_path(struct l_dbus_proxy *proxy); const char *l_dbus_proxy_get_interface(struct l_dbus_proxy *proxy); bool l_dbus_proxy_get_property(struct l_dbus_proxy *proxy, const char *name, const char *signature, ...); bool l_dbus_proxy_set_property(struct l_dbus_proxy *proxy, l_dbus_client_proxy_result_func_t result, void *user_data, l_dbus_destroy_func_t destroy, const char *name, const char *signature, ...); uint32_t l_dbus_proxy_method_call(struct l_dbus_proxy *proxy, const char *method, l_dbus_message_func_t setup, l_dbus_client_proxy_result_func_t reply, void *user_data, l_dbus_destroy_func_t destroy); #ifdef __cplusplus } #endif #endif /* __ELL_DBUS_CLIENT_H */ bluez-5.82/ell/PaxHeaders/queue.h0000644000000000000000000000005014567163422013731 xustar0020 atime=1743575449 20 ctime=1743591276 bluez-5.82/ell/queue.h0000644000000000000000000000372614567163422013422 0ustar00rootroot/* * Embedded Linux library * Copyright (C) 2011-2014 Intel Corporation * * SPDX-License-Identifier: LGPL-2.1-or-later */ #ifndef __ELL_QUEUE_H #define __ELL_QUEUE_H #include #ifdef __cplusplus extern "C" { #endif typedef void (*l_queue_foreach_func_t) (void *data, void *user_data); typedef void (*l_queue_destroy_func_t) (void *data); typedef int (*l_queue_compare_func_t) (const void *a, const void *b, void *user_data); typedef bool (*l_queue_match_func_t) (const void *data, const void *user_data); typedef bool (*l_queue_remove_func_t) (void *data, void *user_data); struct l_queue; struct l_queue_entry { void *data; struct l_queue_entry *next; }; struct l_queue *l_queue_new(void); void l_queue_destroy(struct l_queue *queue, l_queue_destroy_func_t destroy); void l_queue_clear(struct l_queue *queue, l_queue_destroy_func_t destroy); bool l_queue_push_tail(struct l_queue *queue, void *data); bool l_queue_push_head(struct l_queue *queue, void *data); void *l_queue_pop_head(struct l_queue *queue); void *l_queue_peek_head(struct l_queue *queue); void *l_queue_peek_tail(struct l_queue *queue); bool l_queue_insert(struct l_queue *queue, void *data, l_queue_compare_func_t function, void *user_data); void *l_queue_find(struct l_queue *queue, l_queue_match_func_t function, const void *user_data); bool l_queue_remove(struct l_queue *queue, void *data); void *l_queue_remove_if(struct l_queue *queue, l_queue_match_func_t function, const void *user_data); bool l_queue_reverse(struct l_queue *queue); void l_queue_foreach(struct l_queue *queue, l_queue_foreach_func_t function, void *user_data); unsigned int l_queue_foreach_remove(struct l_queue *queue, l_queue_remove_func_t function, void *user_data); unsigned int l_queue_length(struct l_queue *queue); bool l_queue_isempty(struct l_queue *queue); const struct l_queue_entry *l_queue_get_entries(const struct l_queue *queue); #ifdef __cplusplus } #endif #endif /* __ELL_QUEUE_H */ bluez-5.82/ell/PaxHeaders/dbus-client.c0000644000000000000000000000005014504767710015012 xustar0020 atime=1743575514 20 ctime=1743591277 bluez-5.82/ell/dbus-client.c0000644000000000000000000004221714504767710014501 0ustar00rootroot/* * Embedded Linux library * Copyright (C) 2011-2014 Intel Corporation * Copyright (C) 2017 Codecoup * * SPDX-License-Identifier: LGPL-2.1-or-later */ #ifdef HAVE_CONFIG_H #include #endif #include "dbus.h" #include "dbus-client.h" #include "queue.h" #include "useful.h" #include "private.h" struct l_dbus_client { struct l_dbus *dbus; unsigned int watch; unsigned int added_watch; unsigned int removed_watch; char *service; uint32_t objects_call; l_dbus_watch_func_t connect_cb; void *connect_cb_data; l_dbus_destroy_func_t connect_cb_data_destroy; l_dbus_watch_func_t disconnect_cb; void *disconnect_cb_data; l_dbus_destroy_func_t disconnect_cb_data_destroy; l_dbus_client_ready_func_t ready_cb; void *ready_cb_data; l_dbus_destroy_func_t ready_cb_data_destroy; l_dbus_client_proxy_func_t proxy_added_cb; l_dbus_client_proxy_func_t proxy_removed_cb; l_dbus_client_property_function_t properties_changed_cb; void *proxy_cb_data; l_dbus_destroy_func_t proxy_cb_data_destroy; struct l_queue *proxies; }; struct proxy_property { char *name; struct l_dbus_message *msg; }; struct l_dbus_proxy { struct l_dbus_client *client; char *interface; char *path; uint32_t properties_watch; bool ready; struct l_queue *properties; struct l_queue *pending_calls; }; LIB_EXPORT const char *l_dbus_proxy_get_path(struct l_dbus_proxy *proxy) { if (unlikely(!proxy)) return NULL; return proxy->path; } LIB_EXPORT const char *l_dbus_proxy_get_interface(struct l_dbus_proxy *proxy) { if (unlikely(!proxy)) return NULL; return proxy->interface; } static bool property_match_by_name(const void *a, const void *b) { const struct proxy_property *prop = a; const char *name = b; return !strcmp(prop->name, name); } static struct proxy_property *find_property(struct l_dbus_proxy *proxy, const char *name) { return l_queue_find(proxy->properties, property_match_by_name, name); } static struct proxy_property *get_property(struct l_dbus_proxy *proxy, const char *name) { struct proxy_property *prop; prop = find_property(proxy, name); if (prop) return prop; prop = l_new(struct proxy_property, 1); prop->name = l_strdup(name); l_queue_push_tail(proxy->properties, prop); return prop; } LIB_EXPORT bool l_dbus_proxy_get_property(struct l_dbus_proxy *proxy, const char *name, const char *signature, ...) { struct proxy_property *prop; va_list args; bool res; if (unlikely(!proxy)) return false; prop = find_property(proxy, name); if (!prop) return false; va_start(args, signature); res = l_dbus_message_get_arguments_valist(prop->msg, signature, args); va_end(args); return res; } static void property_free(void *data) { struct proxy_property *prop = data; if (prop->msg) l_dbus_message_unref(prop->msg); l_free(prop->name); l_free(prop); } static void cancel_pending_calls(struct l_dbus_proxy *proxy) { const struct l_queue_entry *entry; for (entry = l_queue_get_entries(proxy->pending_calls); entry; entry = entry->next) { uint32_t call_id = L_PTR_TO_UINT(entry->data); l_dbus_cancel(proxy->client->dbus, call_id); } } static void dbus_proxy_destroy(struct l_dbus_proxy *proxy) { if (unlikely(!proxy)) return; if (proxy->properties_watch) l_dbus_remove_signal_watch(proxy->client->dbus, proxy->properties_watch); cancel_pending_calls(proxy); l_queue_destroy(proxy->pending_calls, NULL); l_queue_destroy(proxy->properties, property_free); l_free(proxy->interface); l_free(proxy->path); l_free(proxy); } struct method_call_request { struct l_dbus_proxy *proxy; uint32_t call_id; l_dbus_message_func_t setup; l_dbus_client_proxy_result_func_t result; void *user_data; l_dbus_destroy_func_t destroy; }; static void method_call_request_free(void *user_data) { struct method_call_request *req = user_data; l_queue_remove(req->proxy->pending_calls, L_UINT_TO_PTR(req->call_id)); if (req->destroy) req->destroy(req->user_data); l_free(req); } static void method_call_setup(struct l_dbus_message *message, void *user_data) { struct method_call_request *req = user_data; if (req->setup) req->setup(message, req->user_data); else l_dbus_message_set_arguments(message, ""); } static void method_call_reply(struct l_dbus_message *message, void *user_data) { struct method_call_request *req = user_data; if (req->result) req->result(req->proxy, message, req->user_data); } LIB_EXPORT bool l_dbus_proxy_set_property(struct l_dbus_proxy *proxy, l_dbus_client_proxy_result_func_t result, void *user_data, l_dbus_destroy_func_t destroy, const char *name, const char *signature, ...) { struct l_dbus_client *client = proxy->client; struct l_dbus_message_builder *builder; struct method_call_request *req; struct l_dbus_message *message; struct proxy_property *prop; va_list args; if (unlikely(!proxy)) return false; prop = find_property(proxy, name); if (!prop) return false; if (strcmp(l_dbus_message_get_signature(prop->msg), signature)) return false; message = l_dbus_message_new_method_call(client->dbus, client->service, proxy->path, L_DBUS_INTERFACE_PROPERTIES, "Set"); if (!message) return false; builder = l_dbus_message_builder_new(message); if (!builder) { l_dbus_message_unref(message); return false; } l_dbus_message_builder_append_basic(builder, 's', proxy->interface); l_dbus_message_builder_append_basic(builder, 's', name); l_dbus_message_builder_enter_variant(builder, signature); va_start(args, signature); l_dbus_message_builder_append_from_valist(builder, signature, args); va_end(args); l_dbus_message_builder_leave_variant(builder); l_dbus_message_builder_finalize(builder); l_dbus_message_builder_destroy(builder); req = l_new(struct method_call_request, 1); req->proxy = proxy; req->result = result; req->user_data = user_data; req->destroy = destroy; req->call_id = l_dbus_send_with_reply(client->dbus, message, method_call_reply, req, method_call_request_free); if (!req->call_id) { l_free(req); return false; } l_queue_push_tail(proxy->pending_calls, L_UINT_TO_PTR(req->call_id)); return true; } LIB_EXPORT uint32_t l_dbus_proxy_method_call(struct l_dbus_proxy *proxy, const char *method, l_dbus_message_func_t setup, l_dbus_client_proxy_result_func_t reply, void *user_data, l_dbus_destroy_func_t destroy) { struct method_call_request *req; if (unlikely(!proxy)) return 0; req = l_new(struct method_call_request, 1); req->proxy = proxy; req->setup = setup; req->result = reply; req->user_data = user_data; req->destroy = destroy; req->call_id = l_dbus_method_call(proxy->client->dbus, proxy->client->service, proxy->path, proxy->interface, method, method_call_setup, method_call_reply, req, method_call_request_free); if (!req->call_id) { l_free(req); return 0; } l_queue_push_tail(proxy->pending_calls, L_UINT_TO_PTR(req->call_id)); return req->call_id; } static void proxy_update_property(struct l_dbus_proxy *proxy, const char *name, struct l_dbus_message_iter *property) { struct l_dbus_message_builder *builder; struct proxy_property *prop = get_property(proxy, name); l_dbus_message_unref(prop->msg); if (!property) { prop->msg = NULL; goto done; } prop->msg = l_dbus_message_new_signal(proxy->client->dbus, proxy->path, proxy->interface, name); if (!prop->msg) return; builder = l_dbus_message_builder_new(prop->msg); l_dbus_message_builder_append_from_iter(builder, property); l_dbus_message_builder_finalize(builder); l_dbus_message_builder_destroy(builder); done: if (proxy->client->properties_changed_cb && proxy->ready) proxy->client->properties_changed_cb(proxy, name, prop->msg, proxy->client->proxy_cb_data); } static void proxy_invalidate_properties(struct l_dbus_proxy *proxy, struct l_dbus_message_iter* props) { const char *name; while (l_dbus_message_iter_next_entry(props, &name)) proxy_update_property(proxy, name, NULL); } static void proxy_update_properties(struct l_dbus_proxy *proxy, struct l_dbus_message_iter* props) { struct l_dbus_message_iter variant; const char *name; while (l_dbus_message_iter_next_entry(props, &name, &variant)) proxy_update_property(proxy, name, &variant); } static void properties_changed_callback(struct l_dbus_message *message, void *user_data) { struct l_dbus_proxy *proxy = user_data; const char *interface; struct l_dbus_message_iter changed; struct l_dbus_message_iter invalidated; if (!l_dbus_message_get_arguments(message, "sa{sv}as", &interface, &changed, &invalidated)) return; proxy_update_properties(proxy, &changed); proxy_invalidate_properties(proxy, &invalidated); } static struct l_dbus_proxy *dbus_proxy_new(struct l_dbus_client *client, const char *path, const char *interface) { struct l_dbus_proxy *proxy = l_new(struct l_dbus_proxy, 1); proxy->properties_watch = l_dbus_add_signal_watch(client->dbus, client->service, path, L_DBUS_INTERFACE_PROPERTIES, "PropertiesChanged", L_DBUS_MATCH_ARGUMENT(0), interface, L_DBUS_MATCH_NONE, properties_changed_callback, proxy); if (!proxy->properties_watch) { l_free(proxy); return NULL; } proxy->client = client; proxy->interface = l_strdup(interface); proxy->path = l_strdup(path); proxy->properties = l_queue_new(); proxy->pending_calls = l_queue_new(); l_queue_push_tail(client->proxies, proxy); return proxy; } static bool is_ignorable(const char *interface) { static const struct { const char *interface; } interfaces_to_ignore[] = { { L_DBUS_INTERFACE_OBJECT_MANAGER }, { L_DBUS_INTERFACE_INTROSPECTABLE }, { L_DBUS_INTERFACE_PROPERTIES }, }; size_t i; for (i = 0; i < L_ARRAY_SIZE(interfaces_to_ignore); i++) if (!strcmp(interfaces_to_ignore[i].interface, interface)) return true; return false; } static struct l_dbus_proxy *find_proxy(struct l_dbus_client *client, const char *path, const char *interface) { const struct l_queue_entry *entry; for (entry = l_queue_get_entries(client->proxies); entry; entry = entry->next) { struct l_dbus_proxy *proxy = entry->data; if (!strcmp(proxy->interface, interface) && !strcmp(proxy->path, path)) return proxy; } return NULL; } static void parse_interface(struct l_dbus_client *client, const char *path, const char *interface, struct l_dbus_message_iter *properties) { struct l_dbus_proxy *proxy; if (is_ignorable(interface)) return; proxy = find_proxy(client, path, interface); if (!proxy) proxy = dbus_proxy_new(client, path, interface); if (!proxy) return; proxy_update_properties(proxy, properties); if (!proxy->ready) { proxy->ready = true; if (client->proxy_added_cb) client->proxy_added_cb(proxy, client->proxy_cb_data); } } static void parse_object(struct l_dbus_client *client, const char *path, struct l_dbus_message_iter *object) { const char *interface; struct l_dbus_message_iter properties; if (!path) return; while (l_dbus_message_iter_next_entry(object, &interface, &properties)) parse_interface(client, path, interface, &properties); } static void interfaces_added_callback(struct l_dbus_message *message, void *user_data) { struct l_dbus_client *client = user_data; struct l_dbus_message_iter object; const char *path; if (!l_dbus_message_get_arguments(message, "oa{sa{sv}}", &path, &object)) return; parse_object(client, path, &object); } static void interfaces_removed_callback(struct l_dbus_message *message, void *user_data) { struct l_dbus_client *client = user_data; struct l_dbus_message_iter interfaces; const char *interface; const char *path; if (!l_dbus_message_get_arguments(message, "oas", &path, &interfaces)) return; while (l_dbus_message_iter_next_entry(&interfaces, &interface)) { struct l_dbus_proxy *proxy; proxy = find_proxy(client, path, interface); if (!proxy) continue; l_queue_remove(proxy->client->proxies, proxy); if (client->proxy_removed_cb) client->proxy_removed_cb(proxy, client->proxy_cb_data); dbus_proxy_destroy(proxy); } } static void get_managed_objects_reply(struct l_dbus_message *message, void *user_data) { struct l_dbus_client *client = user_data; struct l_dbus_message_iter objects; struct l_dbus_message_iter object; const char *path; client->objects_call = 0; if (l_dbus_message_is_error(message)) return; if (!l_dbus_message_get_arguments(message, "a{oa{sa{sv}}}", &objects)) return; while (l_dbus_message_iter_next_entry(&objects, &path, &object)) parse_object(client, path, &object); client->added_watch = l_dbus_add_signal_watch(client->dbus, client->service, "/", L_DBUS_INTERFACE_OBJECT_MANAGER, "InterfacesAdded", L_DBUS_MATCH_NONE, interfaces_added_callback, client); client->removed_watch = l_dbus_add_signal_watch(client->dbus, client->service, "/", L_DBUS_INTERFACE_OBJECT_MANAGER, "InterfacesRemoved", L_DBUS_MATCH_NONE, interfaces_removed_callback, client); if (client->ready_cb) client->ready_cb(client, client->ready_cb_data); } static void service_appeared_callback(struct l_dbus *dbus, void *user_data) { struct l_dbus_client *client = user_data; /* TODO should we allow to set different root? */ client->objects_call = l_dbus_method_call(dbus, client->service, "/", L_DBUS_INTERFACE_OBJECT_MANAGER, "GetManagedObjects", NULL, get_managed_objects_reply, client, NULL); if (client->connect_cb) client->connect_cb(client->dbus, client->connect_cb_data); } static void service_disappeared_callback(struct l_dbus *dbus, void *user_data) { struct l_dbus_client *client = user_data; if (client->disconnect_cb) client->disconnect_cb(client->dbus, client->disconnect_cb_data); l_queue_clear(client->proxies, (l_queue_destroy_func_t)dbus_proxy_destroy); } LIB_EXPORT struct l_dbus_client *l_dbus_client_new(struct l_dbus *dbus, const char *service, const char *path) { struct l_dbus_client *client = l_new(struct l_dbus_client, 1); client->dbus = dbus; client->watch = l_dbus_add_service_watch(dbus, service, service_appeared_callback, service_disappeared_callback, client, NULL); if (!client->watch) { l_free(client); return NULL; } client->service = l_strdup(service); client->proxies = l_queue_new(); return client; } LIB_EXPORT void l_dbus_client_destroy(struct l_dbus_client *client) { if (unlikely(!client)) return; if (client->watch) l_dbus_remove_signal_watch(client->dbus, client->watch); if (client->added_watch) l_dbus_remove_signal_watch(client->dbus, client->added_watch); if (client->removed_watch) l_dbus_remove_signal_watch(client->dbus, client->removed_watch); if (client->connect_cb_data_destroy) client->connect_cb_data_destroy(client->connect_cb_data); if (client->disconnect_cb_data_destroy) client->disconnect_cb_data_destroy(client->disconnect_cb_data); if (client->ready_cb_data_destroy) client->ready_cb_data_destroy(client->ready_cb_data); if (client->proxy_cb_data_destroy) client->proxy_cb_data_destroy(client->proxy_cb_data); if (client->objects_call) l_dbus_cancel(client->dbus, client->objects_call); l_queue_destroy(client->proxies, (l_queue_destroy_func_t)dbus_proxy_destroy); l_free(client->service); l_free(client); } LIB_EXPORT bool l_dbus_client_set_connect_handler(struct l_dbus_client *client, l_dbus_watch_func_t function, void *user_data, l_dbus_destroy_func_t destroy) { if (unlikely(!client)) return false; if (client->connect_cb_data_destroy) client->connect_cb_data_destroy(client->connect_cb_data); client->connect_cb = function; client->connect_cb_data = user_data; client->connect_cb_data_destroy = destroy; return true; } LIB_EXPORT bool l_dbus_client_set_disconnect_handler(struct l_dbus_client *client, l_dbus_watch_func_t function, void *user_data, l_dbus_destroy_func_t destroy) { if (unlikely(!client)) return false; if(client->disconnect_cb_data_destroy) client->disconnect_cb_data_destroy(client->disconnect_cb_data); client->disconnect_cb = function; client->disconnect_cb_data = user_data; client->disconnect_cb_data_destroy = destroy; return true; } LIB_EXPORT bool l_dbus_client_set_ready_handler(struct l_dbus_client *client, l_dbus_client_ready_func_t function, void *user_data, l_dbus_destroy_func_t destroy) { if (unlikely(!client)) return false; if (client->ready_cb_data_destroy) client->ready_cb_data_destroy(client->ready_cb_data); client->ready_cb = function; client->ready_cb_data = user_data; client->ready_cb_data_destroy = destroy; return true; } LIB_EXPORT bool l_dbus_client_set_proxy_handlers(struct l_dbus_client *client, l_dbus_client_proxy_func_t proxy_added, l_dbus_client_proxy_func_t proxy_removed, l_dbus_client_property_function_t property_changed, void *user_data, l_dbus_destroy_func_t destroy) { if (unlikely(!client)) return false; if (client->proxy_cb_data_destroy) client->proxy_cb_data_destroy(client->proxy_cb_data); client->proxy_added_cb = proxy_added; client->proxy_removed_cb = proxy_removed; client->properties_changed_cb = property_changed; client->proxy_cb_data = user_data; client->proxy_cb_data_destroy = destroy; return true; } bluez-5.82/ell/PaxHeaders/dbus-message.c0000644000000000000000000000005014504767710015160 xustar0020 atime=1743575504 20 ctime=1743591276 bluez-5.82/ell/dbus-message.c0000644000000000000000000013216314504767710014647 0ustar00rootroot/* * Embedded Linux library * Copyright (C) 2011-2014 Intel Corporation * * SPDX-License-Identifier: LGPL-2.1-or-later */ #ifdef HAVE_CONFIG_H #include #endif #define _GNU_SOURCE #include #include #include #include "util.h" #include "private.h" #include "useful.h" #include "dbus.h" #include "dbus-private.h" #include "gvariant-private.h" #define DBUS_MESSAGE_LITTLE_ENDIAN ('l') #define DBUS_MESSAGE_BIG_ENDIAN ('B') #define DBUS_MESSAGE_PROTOCOL_VERSION 1 #define DBUS_MESSAGE_FLAG_NO_REPLY_EXPECTED 0x01 #define DBUS_MESSAGE_FLAG_NO_AUTO_START 0x02 #define DBUS_MESSAGE_FIELD_PATH 1 #define DBUS_MESSAGE_FIELD_INTERFACE 2 #define DBUS_MESSAGE_FIELD_MEMBER 3 #define DBUS_MESSAGE_FIELD_ERROR_NAME 4 #define DBUS_MESSAGE_FIELD_REPLY_SERIAL 5 #define DBUS_MESSAGE_FIELD_DESTINATION 6 #define DBUS_MESSAGE_FIELD_SENDER 7 #define DBUS_MESSAGE_FIELD_SIGNATURE 8 #define DBUS_MESSAGE_FIELD_UNIX_FDS 9 #define DBUS_MAX_NESTING 32 struct l_dbus_message { int refcount; void *header; size_t header_size; size_t header_end; char *signature; void *body; size_t body_size; char *path; char *interface; char *member; char *error_name; uint32_t reply_serial; char *destination; char *sender; int fds[16]; uint32_t num_fds; bool sealed : 1; bool signature_free : 1; }; struct l_dbus_message_builder { struct l_dbus_message *message; struct dbus_builder *builder; struct builder_driver *driver; }; static inline bool _dbus_message_is_gvariant(struct l_dbus_message *msg) { struct dbus_header *hdr = msg->header; return hdr->version == 2; } void *_dbus_message_get_header(struct l_dbus_message *msg, size_t *out_size) { if (out_size) *out_size = msg->header_size; return msg->header; } void *_dbus_message_get_body(struct l_dbus_message *msg, size_t *out_size) { if (out_size) *out_size = msg->body_size; return msg->body; } /* Get a buffer containing the final message contents except the header */ void *_dbus_message_get_footer(struct l_dbus_message *msg, size_t *out_size) { size_t size; if (_dbus_message_is_gvariant(msg)) { size = _gvariant_message_finalize(msg->header_end, msg->body, msg->body_size, msg->signature); size -= msg->header_size; } else size = msg->body_size; if (out_size) *out_size = size; return msg->body; } int *_dbus_message_get_fds(struct l_dbus_message *msg, uint32_t *num_fds) { *num_fds = msg->num_fds; return msg->fds; } void _dbus_message_set_serial(struct l_dbus_message *msg, uint32_t serial) { struct dbus_header *hdr = msg->header; hdr->dbus1.serial = serial; } uint32_t _dbus_message_get_serial(struct l_dbus_message *msg) { struct dbus_header *hdr = msg->header; return hdr->dbus1.serial; } LIB_EXPORT bool l_dbus_message_set_no_reply(struct l_dbus_message *msg, bool on) { struct dbus_header *hdr; if (unlikely(!msg)) return false; hdr = msg->header; if (on) hdr->flags |= DBUS_MESSAGE_FLAG_NO_REPLY_EXPECTED; else hdr->flags &= ~DBUS_MESSAGE_FLAG_NO_REPLY_EXPECTED; return true; } LIB_EXPORT bool l_dbus_message_get_no_reply(struct l_dbus_message *msg) { struct dbus_header *hdr; if (unlikely(!msg)) return false; hdr = msg->header; if (hdr->flags & DBUS_MESSAGE_FLAG_NO_REPLY_EXPECTED) return true; return false; } LIB_EXPORT bool l_dbus_message_set_no_autostart(struct l_dbus_message *msg, bool on) { struct dbus_header *hdr; if (unlikely(!msg)) return false; hdr = msg->header; if (on) hdr->flags |= DBUS_MESSAGE_FLAG_NO_AUTO_START; else hdr->flags &= ~DBUS_MESSAGE_FLAG_NO_AUTO_START; return true; } LIB_EXPORT bool l_dbus_message_get_no_autostart(struct l_dbus_message *msg) { struct dbus_header *hdr; if (unlikely(!msg)) return false; hdr = msg->header; if (hdr->flags & DBUS_MESSAGE_FLAG_NO_AUTO_START) return true; return false; } static struct l_dbus_message *message_new_common(uint8_t type, uint8_t flags, uint8_t version) { struct l_dbus_message *message; struct dbus_header *hdr; message = l_new(struct l_dbus_message, 1); message->refcount = 1; /* * We allocate the header with the initial 12 bytes (up to the field * length) so that we can store the basic information here. For * GVariant we need 16 bytes. */ message->header_size = version == 1 ? 12 : 16; message->header_end = message->header_size; message->header = l_realloc(NULL, message->header_size); hdr = message->header; hdr->endian = DBUS_NATIVE_ENDIAN; hdr->message_type = type; hdr->flags = flags; hdr->version = version; return message; } struct l_dbus_message *_dbus_message_new_method_call(uint8_t version, const char *destination, const char *path, const char *interface, const char *method) { struct l_dbus_message *message; message = message_new_common(DBUS_MESSAGE_TYPE_METHOD_CALL, 0, version); message->destination = l_strdup(destination); message->path = l_strdup(path); message->interface = l_strdup(interface); message->member = l_strdup(method); return message; } LIB_EXPORT struct l_dbus_message *l_dbus_message_new_method_call( struct l_dbus *dbus, const char *destination, const char *path, const char *interface, const char *method) { uint8_t version; if (unlikely(!dbus)) return NULL; version = _dbus_get_version(dbus); return _dbus_message_new_method_call(version, destination, path, interface, method); } struct l_dbus_message *_dbus_message_new_signal(uint8_t version, const char *path, const char *interface, const char *name) { struct l_dbus_message *message; message = message_new_common(DBUS_MESSAGE_TYPE_SIGNAL, DBUS_MESSAGE_FLAG_NO_REPLY_EXPECTED, version); message->path = l_strdup(path); message->interface = l_strdup(interface); message->member = l_strdup(name); return message; } LIB_EXPORT struct l_dbus_message *l_dbus_message_new_signal(struct l_dbus *dbus, const char *path, const char *interface, const char *name) { uint8_t version; if (unlikely(!dbus)) return NULL; version = _dbus_get_version(dbus); return _dbus_message_new_signal(version, path, interface, name); } LIB_EXPORT struct l_dbus_message *l_dbus_message_new_method_return( struct l_dbus_message *method_call) { struct l_dbus_message *message; struct dbus_header *hdr = method_call->header; const char *sender; message = message_new_common(DBUS_MESSAGE_TYPE_METHOD_RETURN, DBUS_MESSAGE_FLAG_NO_REPLY_EXPECTED, hdr->version); if (!l_dbus_message_get_no_reply(method_call)) message->reply_serial = _dbus_message_get_serial(method_call); sender = l_dbus_message_get_sender(method_call); if (sender) message->destination = l_strdup(sender); return message; } struct l_dbus_message *_dbus_message_new_error(uint8_t version, uint32_t reply_serial, const char *destination, const char *name, const char *error) { struct l_dbus_message *reply; if (!_dbus_valid_interface(name)) return NULL; reply = message_new_common(DBUS_MESSAGE_TYPE_ERROR, DBUS_MESSAGE_FLAG_NO_REPLY_EXPECTED, version); reply->error_name = l_strdup(name); reply->destination = l_strdup(destination); reply->reply_serial = reply_serial; if (!l_dbus_message_set_arguments(reply, "s", error)) { l_dbus_message_unref(reply); return NULL; } return reply; } LIB_EXPORT struct l_dbus_message *l_dbus_message_new_error_valist( struct l_dbus_message *method_call, const char *name, const char *format, va_list args) { char str[1024]; struct dbus_header *hdr = method_call->header; uint32_t reply_serial = 0; vsnprintf(str, sizeof(str), format, args); if (!l_dbus_message_get_no_reply(method_call)) reply_serial = _dbus_message_get_serial(method_call); return _dbus_message_new_error(hdr->version, reply_serial, l_dbus_message_get_sender(method_call), name, str); } LIB_EXPORT struct l_dbus_message *l_dbus_message_new_error( struct l_dbus_message *method_call, const char *name, const char *format, ...) { va_list args; struct l_dbus_message *reply; va_start(args, format); reply = l_dbus_message_new_error_valist(method_call, name, format, args); va_end(args); return reply; } LIB_EXPORT struct l_dbus_message *l_dbus_message_ref(struct l_dbus_message *message) { if (unlikely(!message)) return NULL; __atomic_fetch_add(&message->refcount, 1, __ATOMIC_SEQ_CST); return message; } LIB_EXPORT void l_dbus_message_unref(struct l_dbus_message *message) { unsigned int i; if (unlikely(!message)) return; if (__atomic_sub_fetch(&message->refcount, 1, __ATOMIC_SEQ_CST)) return; for (i = 0; i < message->num_fds; i++) close(message->fds[i]); if (!message->sealed) { l_free(message->destination); l_free(message->path); l_free(message->interface); l_free(message->member); l_free(message->error_name); l_free(message->sender); } if (message->signature_free) l_free(message->signature); l_free(message->header); l_free(message->body); l_free(message); } const char *_dbus_message_get_nth_string_argument( struct l_dbus_message *message, int n) { struct l_dbus_message_iter iter; const char *signature, *value; void *body; size_t size; char type; bool (*skip_entry)(struct l_dbus_message_iter *); bool (*get_basic)(struct l_dbus_message_iter *, char, void *); signature = l_dbus_message_get_signature(message); body = _dbus_message_get_body(message, &size); if (!signature) return NULL; if (_dbus_message_is_gvariant(message)) { if (!_gvariant_iter_init(&iter, message, signature, NULL, body, size)) return NULL; skip_entry = _gvariant_iter_skip_entry; get_basic = _gvariant_iter_next_entry_basic; } else { _dbus1_iter_init(&iter, message, signature, NULL, body, size); skip_entry = _dbus1_iter_skip_entry; get_basic = _dbus1_iter_next_entry_basic; } while (n--) if (!skip_entry(&iter)) return NULL; if (!iter.sig_start) return NULL; type = iter.sig_start[iter.sig_pos]; if (!strchr("sog", type)) return NULL; if (!get_basic(&iter, type, &value)) return NULL; return value; } static bool message_iter_next_entry_valist(struct l_dbus_message_iter *orig, va_list args) { static const char *simple_types = "sogybnqiuxtd"; struct l_dbus_message_iter *iter = orig; const char *signature = orig->sig_start + orig->sig_pos; const char *end; struct l_dbus_message_iter *sub_iter; struct l_dbus_message_iter stack[DBUS_MAX_NESTING]; unsigned int indent = 0; uint32_t uint32_val; int fd; void *arg; bool (*get_basic)(struct l_dbus_message_iter *, char ,void *); bool (*enter_struct)(struct l_dbus_message_iter *, struct l_dbus_message_iter *); bool (*enter_array)(struct l_dbus_message_iter *, struct l_dbus_message_iter *); bool (*enter_variant)(struct l_dbus_message_iter *, struct l_dbus_message_iter *); if (_dbus_message_is_gvariant(orig->message)) { get_basic = _gvariant_iter_next_entry_basic; enter_struct = _gvariant_iter_enter_struct; enter_array = _gvariant_iter_enter_array; enter_variant = _gvariant_iter_enter_variant; } else { get_basic = _dbus1_iter_next_entry_basic; enter_struct = _dbus1_iter_enter_struct; enter_array = _dbus1_iter_enter_array; enter_variant = _dbus1_iter_enter_variant; } while (signature < orig->sig_start + orig->sig_len) { if (strchr(simple_types, *signature)) { arg = va_arg(args, void *); if (!get_basic(iter, *signature, arg)) return false; signature += 1; continue; } switch (*signature) { case 'h': if (!get_basic(iter, 'h', &uint32_val)) return false; if (uint32_val < iter->message->num_fds) fd = fcntl(iter->message->fds[uint32_val], F_DUPFD_CLOEXEC, 3); else fd = -1; *va_arg(args, int *) = fd; signature += 1; break; case '(': case '{': signature += 1; indent += 1; if (indent > DBUS_MAX_NESTING) return false; if (!enter_struct(iter, &stack[indent - 1])) return false; iter = &stack[indent - 1]; break; case ')': case '}': /* * Sanity check in case of an unmatched paren/brace * that isn't caught elsewhere. */ if (unlikely(indent == 0)) return false; signature += 1; indent -= 1; if (indent == 0) iter = orig; else iter = &stack[indent - 1]; break; case 'a': sub_iter = va_arg(args, void *); if (!enter_array(iter, sub_iter)) return false; end = _dbus_signature_end(signature + 1); signature = end + 1; break; case 'v': sub_iter = va_arg(args, void *); if (!enter_variant(iter, sub_iter)) return false; signature += 1; break; default: return false; } } return true; } static inline bool message_iter_next_entry(struct l_dbus_message_iter *iter, ...) { va_list args; bool result; va_start(args, iter); result = message_iter_next_entry_valist(iter, args); va_end(args); return result; } static bool get_header_field_from_iter_valist(struct l_dbus_message *message, uint8_t type, char data_type, va_list args) { struct l_dbus_message_iter header; struct l_dbus_message_iter array, iter; uint8_t endian, message_type, flags, version; uint32_t body_length, serial; bool found; if (!message->sealed) return false; if (_dbus_message_is_gvariant(message)) { uint64_t field_type; if (!_gvariant_iter_init(&header, message, "a(tv)", NULL, message->header + 16, message->header_end - 16)) return false; if (!_gvariant_iter_enter_array(&header, &array)) return false; while ((found = message_iter_next_entry(&array, &field_type, &iter))) if (field_type == type) break; } else { uint8_t field_type; _dbus1_iter_init(&header, message, "yyyyuua(yv)", NULL, message->header, message->header_size); if (!message_iter_next_entry(&header, &endian, &message_type, &flags, &version, &body_length, &serial, &array)) return false; while ((found = message_iter_next_entry(&array, &field_type, &iter))) if (field_type == type) break; } if (!found) return false; if (iter.sig_start[iter.sig_pos] != data_type) return false; return message_iter_next_entry_valist(&iter, args); } static inline bool get_header_field(struct l_dbus_message *message, uint8_t type, int data_type, ...) { va_list args; bool result; va_start(args, data_type); result = get_header_field_from_iter_valist(message, type, data_type, args); va_end(args); return result; } static bool valid_header(const struct dbus_header *hdr) { if (hdr->endian != DBUS_MESSAGE_LITTLE_ENDIAN && hdr->endian != DBUS_MESSAGE_BIG_ENDIAN) return false; if (hdr->message_type < DBUS_MESSAGE_TYPE_METHOD_CALL || hdr->message_type > DBUS_MESSAGE_TYPE_SIGNAL) return false; if (hdr->version != 1 && hdr->version != 2) return false; if (hdr->version == 1) { if (hdr->dbus1.serial == 0) return false; } return true; } unsigned int _dbus_message_unix_fds_from_header(const void *data, size_t size) { struct l_dbus_message message; uint32_t unix_fds; message.header = (uint8_t *) data; message.header_size = size; message.body_size = 0; message.sealed = true; if (!get_header_field(&message, DBUS_MESSAGE_FIELD_UNIX_FDS, 'u', &unix_fds)) return 0; return unix_fds; } struct l_dbus_message *dbus_message_from_blob(const void *data, size_t size, int fds[], uint32_t num_fds) { const struct dbus_header *hdr = data; struct l_dbus_message *message; size_t body_pos; unsigned int i; if (unlikely(size < DBUS_HEADER_SIZE)) return NULL; message = l_new(struct l_dbus_message, 1); message->refcount = 1; if (hdr->version == 1) { message->header_size = align_len(DBUS_HEADER_SIZE + hdr->dbus1.field_length, 8); message->body_size = hdr->dbus1.body_length; if (message->header_size + message->body_size < size) goto free; body_pos = message->header_size; } else { struct l_dbus_message_iter iter; struct l_dbus_message_iter header, variant, body; /* * GVariant message structure as per * https://wiki.gnome.org/Projects/GLib/GDBus/Version2 * is "(yyyyuta{tv}v)". As noted this is equivalent to * some other types, this one lets us get iterators for * the header and the body in the fewest steps. */ if (!_gvariant_iter_init(&iter, message, "(yyyyuta{tv})v", NULL, data, size)) goto free; if (!_gvariant_iter_enter_struct(&iter, &header)) goto free; if (!_gvariant_iter_enter_variant(&iter, &variant)) goto free; if (!_gvariant_iter_enter_struct(&variant, &body)) goto free; message->header_size = align_len(header.len - header.pos, 8); message->body_size = body.len - body.pos; message->signature = l_strndup(body.sig_start + body.sig_pos, body.sig_len - body.sig_pos); message->signature_free = true; message->header_end = header.len; body_pos = body.data + body.pos - data; } message->header = l_malloc(message->header_size); message->body = l_malloc(message->body_size); memcpy(message->header, data, message->header_size); memcpy(message->body, data + body_pos, message->body_size); message->sealed = true; /* If the field is absent message->signature will remain NULL */ if (hdr->version == 1) get_header_field(message, DBUS_MESSAGE_FIELD_SIGNATURE, 'g', &message->signature); if (num_fds) { uint32_t unix_fds, orig_fds = num_fds; if (!get_header_field(message, DBUS_MESSAGE_FIELD_UNIX_FDS, 'u', &unix_fds)) goto free; if (num_fds > unix_fds) num_fds = unix_fds; if (num_fds > L_ARRAY_SIZE(message->fds)) num_fds = L_ARRAY_SIZE(message->fds); for (i = num_fds; i < orig_fds; i++) close(fds[i]); message->num_fds = num_fds; memcpy(message->fds, fds, num_fds * sizeof(int)); } return message; free: l_dbus_message_unref(message); return NULL; } struct l_dbus_message *dbus_message_build(void *header, size_t header_size, void *body, size_t body_size, int fds[], uint32_t num_fds) { const struct dbus_header *hdr = header; struct l_dbus_message *message; unsigned int i; if (unlikely(header_size < DBUS_HEADER_SIZE)) return NULL; if (unlikely(!valid_header(hdr))) return NULL; /* * With GVariant we need to know the signature, use * dbus_message_from_blob instead. */ if (unlikely(hdr->version != 1)) return NULL; message = l_new(struct l_dbus_message, 1); message->refcount = 1; message->header_size = header_size; message->header = header; message->body_size = body_size; message->body = body; message->sealed = true; if (num_fds) { uint32_t unix_fds, orig_fds = num_fds; if (!get_header_field(message, DBUS_MESSAGE_FIELD_UNIX_FDS, 'u', &unix_fds)) { l_free(message); return NULL; } if (num_fds > unix_fds) num_fds = unix_fds; if (num_fds > L_ARRAY_SIZE(message->fds)) num_fds = L_ARRAY_SIZE(message->fds); for (i = num_fds; i < orig_fds; i++) close(fds[i]); message->num_fds = num_fds; memcpy(message->fds, fds, num_fds * sizeof(int)); } /* If the field is absent message->signature will remain NULL */ get_header_field(message, DBUS_MESSAGE_FIELD_SIGNATURE, 'g', &message->signature); return message; } bool dbus_message_compare(struct l_dbus_message *message, const void *data, size_t size) { struct l_dbus_message *other; bool ret = false; other = dbus_message_from_blob(data, size, NULL, 0); if (message->signature) { if (!other->signature) goto done; if (strcmp(message->signature, other->signature)) goto done; } else { if (other->signature) goto done; } if (message->body_size != other->body_size) goto done; if (message->header_size != other->header_size) goto done; ret = !memcmp(message->body, other->body, message->body_size); done: l_dbus_message_unref(other); return ret; } struct builder_driver { bool (*append_basic)(struct dbus_builder *, char, const void *); bool (*enter_struct)(struct dbus_builder *, const char *); bool (*leave_struct)(struct dbus_builder *); bool (*enter_dict)(struct dbus_builder *, const char *); bool (*leave_dict)(struct dbus_builder *); bool (*enter_array)(struct dbus_builder *, const char *); bool (*leave_array)(struct dbus_builder *); bool (*enter_variant)(struct dbus_builder *, const char *); bool (*leave_variant)(struct dbus_builder *); char *(*finish)(struct dbus_builder *, void **, size_t *); bool (*mark)(struct dbus_builder *); bool (*rewind)(struct dbus_builder *); struct dbus_builder *(*new)(void *, size_t); void (*free)(struct dbus_builder *); }; static struct builder_driver dbus1_driver = { .append_basic = _dbus1_builder_append_basic, .enter_struct = _dbus1_builder_enter_struct, .leave_struct = _dbus1_builder_leave_struct, .enter_dict = _dbus1_builder_enter_dict, .leave_dict = _dbus1_builder_leave_dict, .enter_variant = _dbus1_builder_enter_variant, .leave_variant = _dbus1_builder_leave_variant, .enter_array = _dbus1_builder_enter_array, .leave_array = _dbus1_builder_leave_array, .finish = _dbus1_builder_finish, .mark = _dbus1_builder_mark, .rewind = _dbus1_builder_rewind, .new = _dbus1_builder_new, .free = _dbus1_builder_free, }; static struct builder_driver gvariant_driver = { .append_basic = _gvariant_builder_append_basic, .enter_struct = _gvariant_builder_enter_struct, .leave_struct = _gvariant_builder_leave_struct, .enter_dict = _gvariant_builder_enter_dict, .leave_dict = _gvariant_builder_leave_dict, .enter_variant = _gvariant_builder_enter_variant, .leave_variant = _gvariant_builder_leave_variant, .enter_array = _gvariant_builder_enter_array, .leave_array = _gvariant_builder_leave_array, .finish = _gvariant_builder_finish, .mark = _gvariant_builder_mark, .rewind = _gvariant_builder_rewind, .new = _gvariant_builder_new, .free = _gvariant_builder_free, }; static void add_field(struct dbus_builder *builder, struct builder_driver *driver, uint8_t field, const char *type, const void *value) { if (driver == &gvariant_driver) { uint64_t long_field = field; driver->enter_struct(builder, "tv"); driver->append_basic(builder, 't', &long_field); } else { driver->enter_struct(builder, "yv"); driver->append_basic(builder, 'y', &field); } driver->enter_variant(builder, type); driver->append_basic(builder, type[0], value); driver->leave_variant(builder); driver->leave_struct(builder); } static void build_header(struct l_dbus_message *message, const char *signature) { struct dbus_builder *builder; struct builder_driver *driver; char *generated_signature; size_t header_size; bool gvariant; gvariant = _dbus_message_is_gvariant(message); if (gvariant) driver = &gvariant_driver; else driver = &dbus1_driver; builder = driver->new(message->header, message->header_size); driver->enter_array(builder, gvariant ? "(tv)" : "(yv)"); if (message->path) { add_field(builder, driver, DBUS_MESSAGE_FIELD_PATH, "o", message->path); l_free(message->path); message->path = NULL; } if (message->member) { add_field(builder, driver, DBUS_MESSAGE_FIELD_MEMBER, "s", message->member); l_free(message->member); message->member = NULL; } if (message->interface) { add_field(builder, driver, DBUS_MESSAGE_FIELD_INTERFACE, "s", message->interface); l_free(message->interface); message->interface = NULL; } if (message->destination) { add_field(builder, driver, DBUS_MESSAGE_FIELD_DESTINATION, "s", message->destination); l_free(message->destination); message->destination = NULL; } if (message->error_name != 0) { add_field(builder, driver, DBUS_MESSAGE_FIELD_ERROR_NAME, "s", message->error_name); l_free(message->error_name); message->error_name = NULL; } if (message->reply_serial != 0) { if (gvariant) { uint64_t reply_serial = message->reply_serial; add_field(builder, driver, DBUS_MESSAGE_FIELD_REPLY_SERIAL, "t", &reply_serial); } else { add_field(builder, driver, DBUS_MESSAGE_FIELD_REPLY_SERIAL, "u", &message->reply_serial); } message->reply_serial = 0; } if (message->sender) { add_field(builder, driver, DBUS_MESSAGE_FIELD_SENDER, "s", message->sender); l_free(message->sender); message->sender = NULL; } if (signature[0] != '\0' && !gvariant) add_field(builder, driver, DBUS_MESSAGE_FIELD_SIGNATURE, "g", signature); if (message->num_fds) add_field(builder, driver, DBUS_MESSAGE_FIELD_UNIX_FDS, "u", &message->num_fds); driver->leave_array(builder); generated_signature = driver->finish(builder, &message->header, &header_size); l_free(generated_signature); driver->free(builder); if (!_dbus_message_is_gvariant(message)) { struct dbus_header *hdr = message->header; hdr->dbus1.body_length = message->body_size; } /* We must align the end of the header to an 8-byte boundary */ message->header_size = align_len(header_size, 8); message->header = l_realloc(message->header, message->header_size); memset(message->header + header_size, 0, message->header_size - header_size); message->header_end = header_size; } struct container { char type; const char *sig_start; const char *sig_end; unsigned int n_items; }; static bool append_arguments(struct l_dbus_message *message, const char *signature, va_list args) { struct l_dbus_message_builder *builder; bool ret; builder = l_dbus_message_builder_new(message); if (!builder) return false; if (!l_dbus_message_builder_append_from_valist(builder, signature, args)) { l_dbus_message_builder_destroy(builder); return false; } l_dbus_message_builder_finalize(builder); ret = strcmp(signature, builder->message->signature) == 0; l_dbus_message_builder_destroy(builder); return ret; } LIB_EXPORT bool l_dbus_message_get_error(struct l_dbus_message *message, const char **name, const char **text) { struct dbus_header *hdr; const char *str; if (unlikely(!message)) return false; hdr = message->header; if (hdr->message_type != DBUS_MESSAGE_TYPE_ERROR) return false; if (!message->signature) return false; if (message->signature[0] != 's') return false; str = _dbus_message_get_nth_string_argument(message, 0); if (!str) return false; if (!message->error_name) get_header_field(message, DBUS_MESSAGE_FIELD_ERROR_NAME, 's', &message->error_name); if (name) *name = message->error_name; if (text) *text = str; return true; } LIB_EXPORT bool l_dbus_message_is_error(struct l_dbus_message *message) { struct dbus_header *hdr; if (unlikely(!message)) return false; hdr = message->header; return hdr->message_type == DBUS_MESSAGE_TYPE_ERROR; } LIB_EXPORT bool l_dbus_message_get_arguments_valist( struct l_dbus_message *message, const char *signature, va_list args) { struct l_dbus_message_iter iter; if (unlikely(!message)) return false; if (!message->signature) { /* An empty signature is valid */ if (!signature || *signature == '\0') return true; return false; } if (!signature || strcmp(message->signature, signature)) return false; if (_dbus_message_is_gvariant(message)) { if (!_gvariant_iter_init(&iter, message, message->signature, NULL, message->body, message->body_size)) return false; } else _dbus1_iter_init(&iter, message, message->signature, NULL, message->body, message->body_size); return message_iter_next_entry_valist(&iter, args); } LIB_EXPORT bool l_dbus_message_get_arguments(struct l_dbus_message *message, const char *signature, ...) { va_list args; bool result; va_start(args, signature); result = l_dbus_message_get_arguments_valist(message, signature, args); va_end(args); return result; } LIB_EXPORT bool l_dbus_message_set_arguments(struct l_dbus_message *message, const char *signature, ...) { va_list args; bool result; if (unlikely(!message)) return false; if (unlikely(message->sealed)) return false; if (!signature) return true; va_start(args, signature); result = append_arguments(message, signature, args); va_end(args); return result; } LIB_EXPORT bool l_dbus_message_set_arguments_valist( struct l_dbus_message *message, const char *signature, va_list args) { bool result; if (unlikely(!message)) return false; if (!signature) return true; result = append_arguments(message, signature, args); return result; } LIB_EXPORT const char *l_dbus_message_get_path(struct l_dbus_message *message) { if (unlikely(!message)) return NULL; if (!message->path && message->sealed) get_header_field(message, DBUS_MESSAGE_FIELD_PATH, 'o', &message->path); return message->path; } LIB_EXPORT const char *l_dbus_message_get_interface(struct l_dbus_message *message) { if (unlikely(!message)) return NULL; if (!message->interface && message->sealed) get_header_field(message, DBUS_MESSAGE_FIELD_INTERFACE, 's', &message->interface); return message->interface; } LIB_EXPORT const char *l_dbus_message_get_member(struct l_dbus_message *message) { if (unlikely(!message)) return NULL; if (!message->member && message->sealed) get_header_field(message, DBUS_MESSAGE_FIELD_MEMBER, 's', &message->member); return message->member; } LIB_EXPORT const char *l_dbus_message_get_destination(struct l_dbus_message *message) { if (unlikely(!message)) return NULL; if (!message->destination && message->sealed) get_header_field(message, DBUS_MESSAGE_FIELD_DESTINATION, 's', &message->destination); return message->destination; } LIB_EXPORT const char *l_dbus_message_get_sender(struct l_dbus_message *message) { if (unlikely(!message)) return NULL; if (!message->sender && message->sealed) get_header_field(message, DBUS_MESSAGE_FIELD_SENDER, 's', &message->sender); return message->sender; } LIB_EXPORT const char *l_dbus_message_get_signature( struct l_dbus_message *message) { if (unlikely(!message)) return NULL; return message->signature; } uint32_t _dbus_message_get_reply_serial(struct l_dbus_message *message) { if (unlikely(!message)) return 0; if (message->reply_serial == 0 && message->sealed) { if (_dbus_message_is_gvariant(message)) { uint64_t reply_serial = 0; get_header_field(message, DBUS_MESSAGE_FIELD_REPLY_SERIAL, 't', &reply_serial); message->reply_serial = reply_serial; } else get_header_field(message, DBUS_MESSAGE_FIELD_REPLY_SERIAL, 'u', &message->reply_serial); } return message->reply_serial; } enum dbus_message_type _dbus_message_get_type(struct l_dbus_message *message) { struct dbus_header *header; header = message->header; return header->message_type; } const char * _dbus_message_get_type_as_string(struct l_dbus_message *message) { struct dbus_header *header; header = message->header; switch (header->message_type) { case DBUS_MESSAGE_TYPE_METHOD_CALL: return "method_call"; case DBUS_MESSAGE_TYPE_METHOD_RETURN: return "method_return"; case DBUS_MESSAGE_TYPE_ERROR: return "error"; case DBUS_MESSAGE_TYPE_SIGNAL: return "signal"; } return NULL; } uint8_t _dbus_message_get_endian(struct l_dbus_message *message) { struct dbus_header *header = message->header; return header->endian; } uint8_t _dbus_message_get_version(struct l_dbus_message *message) { struct dbus_header *header = message->header; return header->version; } LIB_EXPORT bool l_dbus_message_iter_next_entry(struct l_dbus_message_iter *iter, ...) { va_list args; bool result; if (unlikely(!iter)) return false; va_start(args, iter); result = message_iter_next_entry_valist(iter, args); va_end(args); return result; } LIB_EXPORT bool l_dbus_message_iter_get_variant( struct l_dbus_message_iter *iter, const char *signature, ...) { va_list args; bool result; if (unlikely(!iter)) return false; if (!iter->sig_start || strlen(signature) != iter->sig_len || memcmp(iter->sig_start, signature, iter->sig_len)) return false; va_start(args, signature); result = message_iter_next_entry_valist(iter, args); va_end(args); return result; } LIB_EXPORT bool l_dbus_message_iter_get_fixed_array( struct l_dbus_message_iter *iter, void *out, uint32_t *n_elem) { if (unlikely(!iter)) return false; if (_dbus_message_is_gvariant(iter->message)) return false; return _dbus1_iter_get_fixed_array(iter, out, n_elem); } void _dbus_message_set_sender(struct l_dbus_message *message, const char *sender) { if (!_dbus_message_is_gvariant(message)) return; l_free(message->sender); message->sender = l_strdup(sender); } void _dbus_message_set_destination(struct l_dbus_message *message, const char *destination) { if (!_dbus_message_is_gvariant(message)) return; l_free(message->destination); message->destination = l_strdup(destination); } LIB_EXPORT struct l_dbus_message_builder *l_dbus_message_builder_new( struct l_dbus_message *message) { struct l_dbus_message_builder *ret; if (unlikely(!message)) return NULL; if (message->sealed) return NULL; ret = l_new(struct l_dbus_message_builder, 1); ret->message = l_dbus_message_ref(message); if (_dbus_message_is_gvariant(message)) ret->driver = &gvariant_driver; else ret->driver = &dbus1_driver; ret->builder = ret->driver->new(NULL, 0); return ret; } LIB_EXPORT void l_dbus_message_builder_destroy( struct l_dbus_message_builder *builder) { if (unlikely(!builder)) return; builder->driver->free(builder->builder); l_dbus_message_unref(builder->message); l_free(builder); } LIB_EXPORT bool l_dbus_message_builder_append_basic( struct l_dbus_message_builder *builder, char type, const void *value) { if (unlikely(!builder)) return false; return builder->driver->append_basic(builder->builder, type, value); } LIB_EXPORT bool l_dbus_message_builder_enter_container( struct l_dbus_message_builder *builder, char container_type, const char *signature) { if (unlikely(!builder)) return false; switch (container_type) { case DBUS_CONTAINER_TYPE_ARRAY: return builder->driver->enter_array(builder->builder, signature); case DBUS_CONTAINER_TYPE_DICT_ENTRY: return builder->driver->enter_dict(builder->builder, signature); case DBUS_CONTAINER_TYPE_STRUCT: return builder->driver->enter_struct(builder->builder, signature); case DBUS_CONTAINER_TYPE_VARIANT: return builder->driver->enter_variant(builder->builder, signature); default: break; } return false; } LIB_EXPORT bool l_dbus_message_builder_leave_container( struct l_dbus_message_builder *builder, char container_type) { if (unlikely(!builder)) return false; switch (container_type) { case DBUS_CONTAINER_TYPE_ARRAY: return builder->driver->leave_array(builder->builder); case DBUS_CONTAINER_TYPE_DICT_ENTRY: return builder->driver->leave_dict(builder->builder); case DBUS_CONTAINER_TYPE_STRUCT: return builder->driver->leave_struct(builder->builder); case DBUS_CONTAINER_TYPE_VARIANT: return builder->driver->leave_variant(builder->builder); default: break; } return false; } LIB_EXPORT bool l_dbus_message_builder_enter_struct( struct l_dbus_message_builder *builder, const char *signature) { return l_dbus_message_builder_enter_container(builder, 'r', signature); } LIB_EXPORT bool l_dbus_message_builder_leave_struct( struct l_dbus_message_builder *builder) { return l_dbus_message_builder_leave_container(builder, 'r'); } LIB_EXPORT bool l_dbus_message_builder_enter_array( struct l_dbus_message_builder *builder, const char *signature) { return l_dbus_message_builder_enter_container(builder, 'a', signature); } LIB_EXPORT bool l_dbus_message_builder_leave_array( struct l_dbus_message_builder *builder) { return l_dbus_message_builder_leave_container(builder, 'a'); } LIB_EXPORT bool l_dbus_message_builder_enter_dict( struct l_dbus_message_builder *builder, const char *signature) { return l_dbus_message_builder_enter_container(builder, 'e', signature); } LIB_EXPORT bool l_dbus_message_builder_leave_dict( struct l_dbus_message_builder *builder) { return l_dbus_message_builder_leave_container(builder, 'e'); } LIB_EXPORT bool l_dbus_message_builder_enter_variant( struct l_dbus_message_builder *builder, const char *signature) { return l_dbus_message_builder_enter_container(builder, 'v', signature); } LIB_EXPORT bool l_dbus_message_builder_leave_variant( struct l_dbus_message_builder *builder) { return l_dbus_message_builder_leave_container(builder, 'v'); } /** * l_dbus_message_builder_append_from_iter: * @builder: message builder to receive a new value * @from: message iterator to have its position moved by one value * * Copy one value from a message iterator onto a message builder. The * value's signature is also copied. * * Returns: whether the value was correctly copied. On failure both * the @from iterator and the @builder may have their positions * moved to somewhere within the new value if it's of a * container type. **/ LIB_EXPORT bool l_dbus_message_builder_append_from_iter( struct l_dbus_message_builder *builder, struct l_dbus_message_iter *from) { static const char *simple_types = "sogybnqiuxtd"; char type = from->sig_start[from->sig_pos]; char container_type; char signature[256]; struct l_dbus_message_iter iter; void *basic_ptr; uint64_t basic; uint32_t uint32_val; bool (*get_basic)(struct l_dbus_message_iter *, char, void *); bool (*enter_func)(struct l_dbus_message_iter *, struct l_dbus_message_iter *); bool (*enter_struct)(struct l_dbus_message_iter *, struct l_dbus_message_iter *); bool (*enter_array)(struct l_dbus_message_iter *, struct l_dbus_message_iter *); bool (*enter_variant)(struct l_dbus_message_iter *, struct l_dbus_message_iter *); if (_dbus_message_is_gvariant(from->message)) { get_basic = _gvariant_iter_next_entry_basic; enter_struct = _gvariant_iter_enter_struct; enter_array = _gvariant_iter_enter_array; enter_variant = _gvariant_iter_enter_variant; } else { get_basic = _dbus1_iter_next_entry_basic; enter_struct = _dbus1_iter_enter_struct; enter_array = _dbus1_iter_enter_array; enter_variant = _dbus1_iter_enter_variant; } if (strchr(simple_types, type)) { if (strchr("sog", type)) { if (!get_basic(from, type, &basic_ptr)) return false; } else { basic_ptr = &basic; if (!get_basic(from, type, basic_ptr)) return false; } if (!l_dbus_message_builder_append_basic(builder, type, basic_ptr)) return false; return true; } switch (type) { case 'h': if (!get_basic(from, type, &uint32_val)) return false; if (!l_dbus_message_builder_append_basic(builder, type, &builder->message->num_fds)) return false; if (builder->message->num_fds < L_ARRAY_SIZE(builder->message->fds)) { int fd; if (uint32_val < from->message->num_fds) fd = fcntl(from->message->fds[uint32_val], F_DUPFD_CLOEXEC, 3); else fd = -1; builder->message->fds[builder->message->num_fds++] = fd; } return true; case '(': enter_func = enter_struct; container_type = DBUS_CONTAINER_TYPE_STRUCT; break; case '{': enter_func = enter_struct; container_type = DBUS_CONTAINER_TYPE_DICT_ENTRY; break; case 'a': enter_func = enter_array; container_type = DBUS_CONTAINER_TYPE_ARRAY; break; case 'v': enter_func = enter_variant; container_type = DBUS_CONTAINER_TYPE_VARIANT; break; default: return false; } if (!enter_func(from, &iter)) return false; memcpy(signature, iter.sig_start, iter.sig_len); signature[iter.sig_len] = '\0'; if (!l_dbus_message_builder_enter_container(builder, container_type, signature)) return false; if (container_type == DBUS_CONTAINER_TYPE_ARRAY) while(l_dbus_message_builder_append_from_iter(builder, &iter)); else while (iter.sig_pos < iter.sig_len) if (!l_dbus_message_builder_append_from_iter(builder, &iter)) return false; if (!l_dbus_message_builder_leave_container(builder, container_type)) return false; return true; } LIB_EXPORT bool l_dbus_message_builder_append_from_valist( struct l_dbus_message_builder *builder, const char *signature, va_list args) { struct builder_driver *driver; char subsig[256]; const char *sigend; /* Nesting requires an extra stack entry for the base level */ struct container stack[DBUS_MAX_NESTING + 1]; unsigned int stack_index = 0; if (unlikely(!builder)) return false; driver = builder->driver; stack[stack_index].type = DBUS_CONTAINER_TYPE_STRUCT; stack[stack_index].sig_start = signature; stack[stack_index].sig_end = signature + strlen(signature); stack[stack_index].n_items = 0; while (stack_index != 0 || stack[0].sig_start != stack[0].sig_end) { const char *s; const char *str; if (stack[stack_index].type == DBUS_CONTAINER_TYPE_ARRAY && stack[stack_index].n_items == 0) stack[stack_index].sig_start = stack[stack_index].sig_end; if (stack[stack_index].sig_start == stack[stack_index].sig_end) { bool ret; /* * Sanity check in case of an invalid signature that * isn't caught elsewhere. */ if (unlikely(stack_index == 0)) return false; switch (stack[stack_index].type) { case DBUS_CONTAINER_TYPE_STRUCT: ret = driver->leave_struct(builder->builder); break; case DBUS_CONTAINER_TYPE_DICT_ENTRY: ret = driver->leave_dict(builder->builder); break; case DBUS_CONTAINER_TYPE_VARIANT: ret = driver->leave_variant(builder->builder); break; case DBUS_CONTAINER_TYPE_ARRAY: ret = driver->leave_array(builder->builder); break; default: ret = false; } if (!ret) return false; stack_index -= 1; continue; } s = stack[stack_index].sig_start; if (stack[stack_index].type != DBUS_CONTAINER_TYPE_ARRAY) stack[stack_index].sig_start += 1; else stack[stack_index].n_items -= 1; switch (*s) { case 'o': case 's': case 'g': str = va_arg(args, const char *); if (!driver->append_basic(builder->builder, *s, str)) return false; break; case 'b': case 'y': { uint8_t y = (uint8_t) va_arg(args, int); if (!driver->append_basic(builder->builder, *s, &y)) return false; break; } case 'n': case 'q': { uint16_t n = (uint16_t) va_arg(args, int); if (!driver->append_basic(builder->builder, *s, &n)) return false; break; } case 'i': case 'u': { uint32_t u = va_arg(args, uint32_t); if (!driver->append_basic(builder->builder, *s, &u)) return false; break; } case 'h': { int fd = va_arg(args, int); struct l_dbus_message *message = builder->message; if (!driver->append_basic(builder->builder, *s, &message->num_fds)) return false; if (message->num_fds < L_ARRAY_SIZE(message->fds)) message->fds[message->num_fds++] = fcntl(fd, F_DUPFD_CLOEXEC, 3); break; } case 'x': case 't': { uint64_t x = va_arg(args, uint64_t); if (!driver->append_basic(builder->builder, *s, &x)) return false; break; } case 'd': { double d = va_arg(args, double); if (!driver->append_basic(builder->builder, *s, &d)) return false; break; } case '(': case '{': if (stack_index == DBUS_MAX_NESTING) return false; sigend = _dbus_signature_end(s); memcpy(subsig, s + 1, sigend - s - 1); subsig[sigend - s - 1] = '\0'; if (*s == '(' && !driver->enter_struct(builder->builder, subsig)) return false; if (*s == '{' && !driver->enter_dict(builder->builder, subsig)) return false; if (stack[stack_index].type != DBUS_CONTAINER_TYPE_ARRAY) stack[stack_index].sig_start = sigend + 1; stack_index += 1; stack[stack_index].sig_start = s + 1; stack[stack_index].sig_end = sigend; stack[stack_index].n_items = 0; stack[stack_index].type = *s == '(' ? DBUS_CONTAINER_TYPE_STRUCT : DBUS_CONTAINER_TYPE_DICT_ENTRY; break; case 'v': if (stack_index == DBUS_MAX_NESTING) return false; str = va_arg(args, const char *); if (!str) return false; if (!driver->enter_variant(builder->builder, str)) return false; stack_index += 1; stack[stack_index].type = DBUS_CONTAINER_TYPE_VARIANT; stack[stack_index].sig_start = str; stack[stack_index].sig_end = str + strlen(str); stack[stack_index].n_items = 0; break; case 'a': if (stack_index == DBUS_MAX_NESTING) return false; sigend = _dbus_signature_end(s + 1) + 1; memcpy(subsig, s + 1, sigend - s - 1); subsig[sigend - s - 1] = '\0'; if (!driver->enter_array(builder->builder, subsig)) return false; if (stack[stack_index].type != DBUS_CONTAINER_TYPE_ARRAY) stack[stack_index].sig_start = sigend; stack_index += 1; stack[stack_index].sig_start = s + 1; stack[stack_index].sig_end = sigend; stack[stack_index].n_items = va_arg(args, unsigned int); stack[stack_index].type = DBUS_CONTAINER_TYPE_ARRAY; break; default: return false; } } return true; } LIB_EXPORT struct l_dbus_message *l_dbus_message_builder_finalize( struct l_dbus_message_builder *builder) { char *generated_signature; if (unlikely(!builder)) return NULL; generated_signature = builder->driver->finish(builder->builder, &builder->message->body, &builder->message->body_size); build_header(builder->message, generated_signature); builder->message->sealed = true; builder->message->signature = generated_signature; builder->message->signature_free = true; return builder->message; } bool _dbus_message_builder_mark(struct l_dbus_message_builder *builder) { if (unlikely(!builder)) return false; return builder->driver->mark(builder->builder); } bool _dbus_message_builder_rewind(struct l_dbus_message_builder *builder) { if (unlikely(!builder)) return false; return builder->driver->rewind(builder->builder); } bluez-5.82/ell/PaxHeaders/signal.c0000644000000000000000000000005014560467756014067 xustar0020 atime=1743575467 20 ctime=1743591276 bluez-5.82/ell/signal.c0000644000000000000000000001147314560467756013556 0ustar00rootroot/* * Embedded Linux library * Copyright (C) 2011-2014 Intel Corporation * * SPDX-License-Identifier: LGPL-2.1-or-later */ #ifdef HAVE_CONFIG_H #include #endif #define _GNU_SOURCE #include #include #include #include #include #include #include "util.h" #include "io.h" #include "queue.h" #include "signal.h" #include "private.h" /** * SECTION:signal * @short_description: Unix signal support * * Unix signal support */ /** * l_signal: * * Opaque object representing the signal. */ struct l_signal { struct signal_desc *desc; l_signal_notify_cb_t callback; void *user_data; l_signal_destroy_cb_t destroy; }; struct signal_desc { uint32_t signo; struct l_queue *callbacks; }; static struct l_io *signalfd_io = NULL; static struct l_queue *signal_list = NULL; static sigset_t signal_mask; static void handle_callback(struct signal_desc *desc) { const struct l_queue_entry *entry; for (entry = l_queue_get_entries(desc->callbacks); entry; entry = entry->next) { struct l_signal *signal = entry->data; if (signal->callback) signal->callback(signal->user_data); } } static bool desc_match_signo(const void *a, const void *b) { const struct signal_desc *desc = a; uint32_t signo = L_PTR_TO_UINT(b); return (desc->signo == signo); } static bool signalfd_read_cb(struct l_io *io, void *user_data) { int fd = l_io_get_fd(io); struct signal_desc *desc; struct signalfd_siginfo si; ssize_t result; result = read(fd, &si, sizeof(si)); if (result != sizeof(si)) return true; desc = l_queue_find(signal_list, desc_match_signo, L_UINT_TO_PTR(si.ssi_signo)); if (desc) handle_callback(desc); return true; } static bool signalfd_add(int signo) { int fd; if (!signalfd_io) { fd = -1; sigemptyset(&signal_mask); } else fd = l_io_get_fd(signalfd_io); sigaddset(&signal_mask, signo); fd = signalfd(fd, &signal_mask, SFD_CLOEXEC); if (fd < 0) return false; if (signalfd_io) return true; signalfd_io = l_io_new(fd); if (!signalfd_io) { close(fd); return false; } l_io_set_close_on_destroy(signalfd_io, true); if (!l_io_set_read_handler(signalfd_io, signalfd_read_cb, NULL, NULL)) { l_io_destroy(signalfd_io); return false; } signal_list = l_queue_new(); return true; } static void signalfd_remove(int signo) { if (!signalfd_io) return; sigdelset(&signal_mask, signo); if (!sigisemptyset(&signal_mask)) { signalfd(l_io_get_fd(signalfd_io), &signal_mask, SFD_CLOEXEC); return; } l_io_destroy(signalfd_io); signalfd_io = NULL; l_queue_destroy(signal_list, NULL); signal_list = NULL; } /** * l_signal_create: * @callback: signal callback function * @user_data: user data provided to signal callback function * @destroy: destroy function for user data * * Create new signal callback handling for a given set of signals. * * Returns: a newly allocated #l_signal object **/ LIB_EXPORT struct l_signal *l_signal_create(uint32_t signo, l_signal_notify_cb_t callback, void *user_data, l_signal_destroy_cb_t destroy) { struct l_signal *signal; struct signal_desc *desc; sigset_t mask, oldmask; if (signo <= 1 || signo >= _NSIG) return NULL; signal = l_new(struct l_signal, 1); signal->callback = callback; signal->destroy = destroy; signal->user_data = user_data; desc = l_queue_find(signal_list, desc_match_signo, L_UINT_TO_PTR(signo)); if (desc) goto done; sigemptyset(&mask); sigaddset(&mask, signo); if (sigprocmask(SIG_BLOCK, &mask, &oldmask) < 0) { l_free(signal); return NULL; } if (!signalfd_add(signo)) { sigprocmask(SIG_SETMASK, &oldmask, NULL); l_free(signal); return NULL; } desc = l_new(struct signal_desc, 1); desc->signo = signo; desc->callbacks = l_queue_new(); l_queue_push_tail(signal_list, desc); done: l_queue_push_tail(desc->callbacks, signal); signal->desc = desc; return signal; } /** * l_signal_remove: * @signal: signal object * * Remove signal handling. **/ LIB_EXPORT void l_signal_remove(struct l_signal *signal) { struct signal_desc *desc; sigset_t mask; if (!signal) return; desc = signal->desc; l_queue_remove(desc->callbacks, signal); /* * As long as the signal descriptor has callbacks registered, it is * still needed to be active. */ if (!l_queue_isempty(desc->callbacks)) goto done; if (!l_queue_remove(signal_list, desc)) goto done; sigemptyset(&mask); sigaddset(&mask, desc->signo); /* * When the number of signals goes to zero, then this will close * the signalfd file descriptor, otherwise it will only adjust the * signal mask to account for the removed signal. * */ signalfd_remove(desc->signo); sigprocmask(SIG_UNBLOCK, &mask, NULL); l_queue_destroy(desc->callbacks, NULL); l_free(desc); done: if (signal->destroy) signal->destroy(signal->user_data); l_free(signal); } bluez-5.82/ell/PaxHeaders/cipher.h0000644000000000000000000000005014504767710014060 xustar0020 atime=1743575449 20 ctime=1743591276 bluez-5.82/ell/cipher.h0000644000000000000000000000365514504767710013552 0ustar00rootroot/* * Embedded Linux library * Copyright (C) 2015 Intel Corporation * * SPDX-License-Identifier: LGPL-2.1-or-later */ #ifndef __ELL_CIPHER_H #define __ELL_CIPHER_H #ifdef __cplusplus extern "C" { #endif struct l_cipher; enum l_cipher_type { L_CIPHER_AES = 0, L_CIPHER_AES_CBC, L_CIPHER_AES_CTR, L_CIPHER_ARC4, L_CIPHER_DES, L_CIPHER_DES_CBC, L_CIPHER_DES3_EDE_CBC, L_CIPHER_RC2_CBC, }; struct l_cipher *l_cipher_new(enum l_cipher_type type, const void *key, size_t key_length); void l_cipher_free(struct l_cipher *cipher); bool l_cipher_encrypt(struct l_cipher *cipher, const void *in, void *out, size_t len); bool l_cipher_encryptv(struct l_cipher *cipher, const struct iovec *in, size_t in_cnt, const struct iovec *out, size_t out_cnt); bool l_cipher_decrypt(struct l_cipher *cipher, const void *in, void *out, size_t len); bool l_cipher_decryptv(struct l_cipher *cipher, const struct iovec *in, size_t in_cnt, const struct iovec *out, size_t out_cnt); bool l_cipher_set_iv(struct l_cipher *cipher, const uint8_t *iv, size_t iv_length); struct l_aead_cipher; enum l_aead_cipher_type { L_AEAD_CIPHER_AES_CCM = 0, L_AEAD_CIPHER_AES_GCM, }; struct l_aead_cipher *l_aead_cipher_new(enum l_aead_cipher_type type, const void *key, size_t key_length, size_t tag_length); void l_aead_cipher_free(struct l_aead_cipher *cipher); bool l_aead_cipher_encrypt(struct l_aead_cipher *cipher, const void *in, size_t in_len, const void *ad, size_t ad_len, const void *nonce, size_t nonce_len, void *out, size_t out_len); bool l_aead_cipher_decrypt(struct l_aead_cipher *cipher, const void *in, size_t in_len, const void *ad, size_t ad_len, const void *nonce, size_t nonce_len, void *out, size_t out_len); bool l_cipher_is_supported(enum l_cipher_type type); bool l_aead_cipher_is_supported(enum l_aead_cipher_type type); #ifdef __cplusplus } #endif #endif /* __ELL_CIPHER_H */ bluez-5.82/ell/PaxHeaders/string.h0000644000000000000000000000005014504767710014114 xustar0020 atime=1743575449 20 ctime=1743591276 bluez-5.82/ell/string.h0000644000000000000000000000227614504767710013604 0ustar00rootroot/* * Embedded Linux library * Copyright (C) 2011-2014 Intel Corporation * * SPDX-License-Identifier: LGPL-2.1-or-later */ #ifndef __ELL_STRING_H #define __ELL_STRING_H #include #include #ifdef __cplusplus extern "C" { #endif struct l_string; struct l_string *l_string_new(size_t initial_length); void l_string_free(struct l_string *string); DEFINE_CLEANUP_FUNC(l_string_free); char *l_string_unwrap(struct l_string *string); struct l_string *l_string_append(struct l_string *dest, const char *src); struct l_string *l_string_append_c(struct l_string *dest, const char c); struct l_string *l_string_append_fixed(struct l_string *dest, const char *src, size_t max); void l_string_append_vprintf(struct l_string *dest, const char *format, va_list args) __attribute__((format(printf, 2, 0))); void l_string_append_printf(struct l_string *dest, const char *format, ...) __attribute__((format(printf, 2, 3))); struct l_string *l_string_truncate(struct l_string *string, size_t new_size); unsigned int l_string_length(struct l_string *string); char **l_parse_args(const char *args, int *out_n_args); #ifdef __cplusplus } #endif #endif /* __ELL_STRING_H */ bluez-5.82/ell/PaxHeaders/dbus-private.h0000644000000000000000000000005014661631567015217 xustar0020 atime=1743575501 20 ctime=1743591276 bluez-5.82/ell/dbus-private.h0000644000000000000000000002516314661631567014707 0ustar00rootroot/* * Embedded Linux library * Copyright (C) 2011-2014 Intel Corporation * * SPDX-License-Identifier: LGPL-2.1-or-later */ #include enum dbus_message_type { DBUS_MESSAGE_TYPE_METHOD_CALL = 1, DBUS_MESSAGE_TYPE_METHOD_RETURN = 2, DBUS_MESSAGE_TYPE_ERROR = 3, DBUS_MESSAGE_TYPE_SIGNAL = 4, }; enum dbus_container_type { DBUS_CONTAINER_TYPE_ARRAY = 'a', DBUS_CONTAINER_TYPE_STRUCT = 'r', DBUS_CONTAINER_TYPE_VARIANT = 'v', DBUS_CONTAINER_TYPE_DICT_ENTRY = 'e', }; #if __BYTE_ORDER == __LITTLE_ENDIAN #define DBUS_NATIVE_ENDIAN 'l' #elif __BYTE_ORDER == __BIG_ENDIAN #define DBUS_NATIVE_ENDIAN 'B' #else #error "Unknown byte order" #endif struct dbus_header { uint8_t endian; uint8_t message_type; uint8_t flags; uint8_t version; union { struct { uint32_t body_length; uint32_t serial; uint32_t field_length; } __attribute__ ((packed)) dbus1; }; } __attribute__ ((packed)); #define DBUS_HEADER_SIZE 16 struct dbus_builder; struct l_string; struct l_dbus_interface; struct _dbus_method; struct _dbus_signal; struct _dbus_property; struct l_dbus_message_iter; struct l_dbus_message; struct l_dbus; struct _dbus_filter; struct _dbus_filter_condition; struct _dbus_filter_ops; void _dbus1_iter_init(struct l_dbus_message_iter *iter, struct l_dbus_message *message, const char *sig_start, const char *sig_end, const void *data, size_t len); bool _dbus1_iter_next_entry_basic(struct l_dbus_message_iter *iter, char type, void *out); bool _dbus1_iter_get_fixed_array(struct l_dbus_message_iter *iter, void *out, uint32_t *n_elem); bool _dbus1_iter_enter_struct(struct l_dbus_message_iter *iter, struct l_dbus_message_iter *structure); bool _dbus1_iter_enter_variant(struct l_dbus_message_iter *iter, struct l_dbus_message_iter *variant); bool _dbus1_iter_enter_array(struct l_dbus_message_iter *iter, struct l_dbus_message_iter *array); bool _dbus1_iter_skip_entry(struct l_dbus_message_iter *iter); struct dbus_builder *_dbus1_builder_new(void *body, size_t body_size); void _dbus1_builder_free(struct dbus_builder *builder); bool _dbus1_builder_append_basic(struct dbus_builder *builder, char type, const void *value); bool _dbus1_builder_enter_struct(struct dbus_builder *builder, const char *signature); bool _dbus1_builder_leave_struct(struct dbus_builder *builder); bool _dbus1_builder_enter_dict(struct dbus_builder *builder, const char *signature); bool _dbus1_builder_leave_dict(struct dbus_builder *builder); bool _dbus1_builder_enter_variant(struct dbus_builder *builder, const char *signature); bool _dbus1_builder_leave_variant(struct dbus_builder *builder); bool _dbus1_builder_enter_array(struct dbus_builder *builder, const char *signature); bool _dbus1_builder_leave_array(struct dbus_builder *builder); char *_dbus1_builder_finish(struct dbus_builder *builder, void **body, size_t *body_size); bool _dbus1_builder_mark(struct dbus_builder *builder); bool _dbus1_builder_rewind(struct dbus_builder *builder); void *_dbus_message_get_body(struct l_dbus_message *msg, size_t *out_size); void *_dbus_message_get_header(struct l_dbus_message *msg, size_t *out_size); void *_dbus_message_get_footer(struct l_dbus_message *msg, size_t *out_size); int *_dbus_message_get_fds(struct l_dbus_message *msg, uint32_t *num_fds); void _dbus_message_set_serial(struct l_dbus_message *msg, uint32_t serial); uint32_t _dbus_message_get_serial(struct l_dbus_message *msg); uint32_t _dbus_message_get_reply_serial(struct l_dbus_message *message); void _dbus_message_set_sender(struct l_dbus_message *message, const char *sender); void _dbus_message_set_destination(struct l_dbus_message *message, const char *destination); enum dbus_message_type _dbus_message_get_type(struct l_dbus_message *message); const char * _dbus_message_get_type_as_string(struct l_dbus_message *message); uint8_t _dbus_message_get_version(struct l_dbus_message *message); uint8_t _dbus_message_get_endian(struct l_dbus_message *message); const char *_dbus_message_get_nth_string_argument( struct l_dbus_message *message, int n); struct l_dbus_message *_dbus_message_new_method_call(uint8_t version, const char *destination, const char *path, const char *interface, const char *method); struct l_dbus_message *_dbus_message_new_signal(uint8_t version, const char *path, const char *interface, const char *name); struct l_dbus_message *_dbus_message_new_error(uint8_t version, uint32_t reply_serial, const char *destination, const char *name, const char *error); struct l_dbus_message *dbus_message_from_blob(const void *data, size_t size, int fds[], uint32_t num_fds); struct l_dbus_message *dbus_message_build(void *header, size_t header_size, void *body, size_t body_size, int fds[], uint32_t num_fds); bool dbus_message_compare(struct l_dbus_message *message, const void *data, size_t size); bool _dbus_message_builder_mark(struct l_dbus_message_builder *builder); bool _dbus_message_builder_rewind(struct l_dbus_message_builder *builder); unsigned int _dbus_message_unix_fds_from_header(const void *data, size_t size); const char *_dbus_signature_end(const char *signature); bool _dbus_valid_object_path(const char *path); bool _dbus_valid_signature(const char *sig); int _dbus_num_children(const char *sig); bool _dbus_valid_interface(const char *interface); bool _dbus_valid_method(const char *method); bool _dbus_parse_unique_name(const char *name, uint64_t *out_id); bool _dbus_valid_bus_name(const char *bus_name); bool _dbus1_header_is_valid(void *data, size_t size); void _dbus_method_introspection(struct _dbus_method *info, struct l_string *buf); void _dbus_signal_introspection(struct _dbus_signal *info, struct l_string *buf); void _dbus_property_introspection(struct _dbus_property *info, struct l_string *buf); void _dbus_interface_introspection(struct l_dbus_interface *interface, struct l_string *buf); struct l_dbus_interface *_dbus_interface_new(const char *name); void _dbus_interface_free(struct l_dbus_interface *interface); struct _dbus_method *_dbus_interface_find_method(struct l_dbus_interface *i, const char *method); struct _dbus_signal *_dbus_interface_find_signal(struct l_dbus_interface *i, const char *signal); struct _dbus_property *_dbus_interface_find_property(struct l_dbus_interface *i, const char *property); struct _dbus_object_tree *_dbus_object_tree_new(void); void _dbus_object_tree_free(struct _dbus_object_tree *tree); struct object_node *_dbus_object_tree_makepath(struct _dbus_object_tree *tree, const char *path); struct object_node *_dbus_object_tree_lookup(struct _dbus_object_tree *tree, const char *path); void _dbus_object_tree_prune_node(struct object_node *node); struct object_node *_dbus_object_tree_new_object(struct _dbus_object_tree *tree, const char *path, void *user_data, void (*destroy) (void *)); bool _dbus_object_tree_object_destroy(struct _dbus_object_tree *tree, const char *path); bool _dbus_object_tree_register_interface(struct _dbus_object_tree *tree, const char *interface, void (*setup_func)(struct l_dbus_interface *), void (*destroy) (void *), bool old_style_properties); bool _dbus_object_tree_unregister_interface(struct _dbus_object_tree *tree, const char *interface); bool _dbus_object_tree_add_interface(struct _dbus_object_tree *tree, const char *path, const char *interface, void *user_data); bool _dbus_object_tree_remove_interface(struct _dbus_object_tree *tree, const char *path, const char *interface); void *_dbus_object_tree_get_interface_data(struct _dbus_object_tree *tree, const char *path, const char *interface); bool _dbus_object_tree_set_interface_data(struct _dbus_object_tree *tree, const char *path, const char *interface, void *user_data); void _dbus_object_tree_introspect(struct _dbus_object_tree *tree, const char *path, struct l_string *buf); bool _dbus_object_tree_dispatch(struct _dbus_object_tree *tree, struct l_dbus *dbus, struct l_dbus_message *message); struct l_dbus_message *_dbus_object_tree_get_objects( struct _dbus_object_tree *tree, struct l_dbus *dbus, const char *path, struct l_dbus_message *message); bool _dbus_object_tree_property_changed(struct l_dbus *dbus, const char *path, const char *interface_name, const char *property_name); void _dbus_object_tree_signals_flush(struct l_dbus *dbus, const char *path); typedef void (*_dbus_name_owner_change_func_t)(const char *name, uint64_t old_owner, uint64_t new_owner, void *user_data); uint8_t _dbus_get_version(struct l_dbus *dbus); int _dbus_get_fd(struct l_dbus *dbus); struct _dbus_object_tree *_dbus_get_tree(struct l_dbus *dbus); struct _dbus_name_ops { bool (*get_name_owner)(struct l_dbus *bus, const char *name); }; struct _dbus_name_cache; struct _dbus_name_cache *_dbus_name_cache_new(struct l_dbus *bus, const struct _dbus_name_ops *driver); void _dbus_name_cache_free(struct _dbus_name_cache *cache); bool _dbus_name_cache_add(struct _dbus_name_cache *cache, const char *name); bool _dbus_name_cache_remove(struct _dbus_name_cache *cache, const char *name); const char *_dbus_name_cache_lookup(struct _dbus_name_cache *cache, const char *name); void _dbus_name_cache_notify(struct _dbus_name_cache *cache, const char *name, const char *owner); unsigned int _dbus_name_cache_add_watch(struct _dbus_name_cache *cache, const char *name, l_dbus_watch_func_t connect_func, l_dbus_watch_func_t disconnect_func, void *user_data, l_dbus_destroy_func_t destroy); bool _dbus_name_cache_remove_watch(struct _dbus_name_cache *cache, unsigned int id); struct _dbus_filter_condition { enum l_dbus_match_type type; const char *value; }; struct _dbus_filter_ops { bool skip_register; bool (*add_match)(struct l_dbus *bus, unsigned int id, const struct _dbus_filter_condition *rule, int rule_len); bool (*remove_match)(struct l_dbus *bus, unsigned int id); }; struct _dbus_filter *_dbus_filter_new(struct l_dbus *dbus, const struct _dbus_filter_ops *driver, struct _dbus_name_cache *name_cache); void _dbus_filter_free(struct _dbus_filter *filter); unsigned int _dbus_filter_add_rule(struct _dbus_filter *filter, const struct _dbus_filter_condition *rule, int rule_len, l_dbus_message_func_t signal_func, void *user_data); bool _dbus_filter_remove_rule(struct _dbus_filter *filter, unsigned int id); char *_dbus_filter_rule_to_str(const struct _dbus_filter_condition *rule, int rule_len); void _dbus_filter_dispatch(struct l_dbus_message *message, void *user_data); bluez-5.82/ell/PaxHeaders/settings.c0000644000000000000000000000005014721132752014432 xustar0020 atime=1743575472 20 ctime=1743591276 bluez-5.82/ell/settings.c0000644000000000000000000007422114721132752014121 0ustar00rootroot/* * Embedded Linux library * Copyright (C) 2011-2014 Intel Corporation * * SPDX-License-Identifier: LGPL-2.1-or-later */ #ifdef HAVE_CONFIG_H #include #endif #if __STDC_VERSION__ <= 199409L #define _DEFAULT_SOURCE /* for strto{u}ll() */ #endif #define _GNU_SOURCE #include #include #include #include #include #include #include #include #include #include #include #include #include "useful.h" #include "strv.h" #include "utf8.h" #include "string.h" #include "queue.h" #include "settings.h" #include "private.h" #include "missing.h" #include "pem-private.h" struct setting_data { char *key; char *value; }; struct embedded_group_data { char *name; char type[32]; size_t len; char data[]; }; struct group_data { char *name; struct l_queue *settings; }; struct l_settings { l_settings_debug_cb_t debug_handler; l_settings_destroy_cb_t debug_destroy; void *debug_data; struct l_queue *groups; struct l_queue *embedded_groups; }; static void setting_destroy(void *data) { struct setting_data *pair = data; l_free(pair->key); explicit_bzero(pair->value, strlen(pair->value)); l_free(pair->value); l_free(pair); } static void group_destroy(void *data) { struct group_data *group = data; l_free(group->name); l_queue_destroy(group->settings, setting_destroy); l_free(group); } static void embedded_group_destroy(void *data) { struct embedded_group_data *group = data; l_free(group->name); l_free(group); } LIB_EXPORT struct l_settings *l_settings_new(void) { struct l_settings *settings; settings = l_new(struct l_settings, 1); settings->groups = l_queue_new(); settings->embedded_groups = l_queue_new(); return settings; } static void copy_key_value_foreach(void *data, void *user_data) { struct setting_data *s = data; struct l_queue *settings = user_data; struct setting_data *copy = l_new(struct setting_data, 1); copy->key = l_strdup(s->key); copy->value = l_strdup(s->value); l_queue_push_head(settings, copy); } static void copy_group_foreach(void *data, void *user_data) { struct group_data *group = data; struct l_queue *groups = user_data; struct group_data *copy = l_new(struct group_data, 1); copy->name = l_strdup(group->name); copy->settings = l_queue_new(); l_queue_push_head(groups, copy); l_queue_foreach(group->settings, copy_key_value_foreach, copy->settings); } static void copy_embedded_foreach(void *data, void *user_data) { struct embedded_group_data *embedded = data; struct l_queue *groups = user_data; struct embedded_group_data *copy; copy = l_memdup(embedded, sizeof(struct embedded_group_data) + embedded->len + 1); copy->name = l_strdup(embedded->name); l_queue_push_tail(groups, copy); } LIB_EXPORT struct l_settings *l_settings_clone( const struct l_settings *settings) { struct l_settings *copy; if (unlikely(!settings)) return NULL; copy = l_settings_new(); l_queue_foreach(settings->groups, copy_group_foreach, copy->groups); l_queue_foreach(settings->embedded_groups, copy_embedded_foreach, copy->embedded_groups); return copy; } LIB_EXPORT void l_settings_free(struct l_settings *settings) { if (unlikely(!settings)) return; if (settings->debug_destroy) settings->debug_destroy(settings->debug_data); l_queue_destroy(settings->groups, group_destroy); l_queue_destroy(settings->embedded_groups, embedded_group_destroy); l_free(settings); } static char *unescape_value(const char *value) { char *ret; char *n; const char *o; ret = l_new(char, strlen(value) + 1); for (n = ret, o = value; *o; o++, n++) { if (*o != '\\') { *n = *o; continue; } o += 1; switch (*o) { case 's': *n = ' '; break; case 'n': *n = '\n'; break; case 't': *n = '\t'; break; case 'r': *n = '\r'; break; case '\\': *n = '\\'; break; default: explicit_bzero(ret, n - ret); l_free(ret); return NULL; } } return ret; } static char *escape_value(const char *value) { size_t i; size_t j; char *ret; bool lead_whitespace; for (i = 0, j = 0, lead_whitespace = true; value[i]; i++) { switch (value[i]) { case ' ': case '\t': if (lead_whitespace) j += 1; break; case '\n': case '\r': case '\\': j += 1; /* fall through */ default: lead_whitespace = false; } } ret = l_malloc(i + j + 1); for (i = 0, j = 0, lead_whitespace = true; value[i]; i++) { switch (value[i]) { case ' ': if (lead_whitespace) { ret[j++] = '\\'; ret[j++] = 's'; } else ret[j++] = value[i]; break; case '\t': if (lead_whitespace) { ret[j++] = '\\'; ret[j++] = 't'; } else ret[j++] = value[i]; break; case '\n': ret[j++] = '\\'; ret[j++] = 'n'; lead_whitespace = false; break; case '\r': ret[j++] = '\\'; ret[j++] = 'r'; lead_whitespace = false; break; case '\\': ret[j++] = '\\'; ret[j++] = '\\'; lead_whitespace = false; break; default: ret[j++] = value[i]; lead_whitespace = false; } } ret[j] = '\0'; return ret; } static ssize_t parse_pem(const char *data, size_t len) { const char *ptr; const char *end; size_t count = 0; ptr = data; end = data + len; while (ptr && ptr < end) { const char *pem_start = ptr; if (!pem_next(ptr, len, NULL, NULL, &ptr, true)) { if (ptr) return -EINVAL; break; } len -= ptr - pem_start; count += ptr - pem_start; } return count; } struct group_extension { char *name; ssize_t (*parse)(const char *data, size_t len); }; static const struct group_extension pem_extension = { .name = "pem", .parse = parse_pem, }; static const struct group_extension *extensions[] = { &pem_extension, NULL }; static const struct group_extension *find_group_extension(const char *type, size_t len) { unsigned int i; for (i = 0; extensions[i]; i++) { if (!strncmp(type, extensions[i]->name, len)) return extensions[i]; } return NULL; } static ssize_t parse_embedded_group(struct l_settings *setting, const char *data, size_t line_len, size_t len, size_t line) { struct embedded_group_data *group; const struct group_extension *ext; const char *ptr; const char *type; size_t type_len; const char *name; size_t name_len; ssize_t bytes; /* Must be at least [@a@b] */ if (line_len < 6) goto invalid_group; /* caller checked data[1] == '@', next char is type */ type = data + 2; ptr = memchr(type, '@', line_len - 2); type_len = ptr - type; if (!ptr || type_len > 31 || type_len < 1) goto invalid_group; if (ptr + 1 > data + line_len) goto invalid_group; name = ptr + 1; /* subtract [@@ + type */ ptr = memchr(name, ']', line_len - 3 - type_len); name_len = ptr - name; if (!ptr || name_len < 1) goto invalid_group; ext = find_group_extension(type, type_len); if (!ext) goto invalid_group; if (ptr + 2 > data + len) { l_util_debug(setting->debug_handler, setting->debug_data, "Embedded group had no payload"); return -EINVAL; } bytes = ext->parse(ptr + 2, len - line_len); if (bytes < 0) { l_util_debug(setting->debug_handler, setting->debug_data, "Failed to parse embedded group data"); return -EINVAL; } group = l_malloc(sizeof(struct embedded_group_data) + bytes + 1); group->name = l_strndup(name, name_len); memcpy(group->type, type, type_len); group->type[type_len] = '\0'; group->len = bytes; memcpy(group->data, ptr + 2, bytes); group->data[bytes] = '\0'; l_queue_push_tail(setting->embedded_groups, group); return bytes; invalid_group: l_util_debug(setting->debug_handler, setting->debug_data, "Invalid embedded group at line %zd", line); return -EINVAL; } static bool parse_group(struct l_settings *settings, const char *data, size_t len, size_t line) { size_t i = 1; size_t end; struct group_data *group; while (i < len && data[i] != ']') { if (l_ascii_isprint(data[i]) == false || data[i] == '[') { l_util_debug(settings->debug_handler, settings->debug_data, "Invalid group name at line %zd", line); return false; } i += 1; } if (i >= len) { l_util_debug(settings->debug_handler, settings->debug_data, "Unterminated group name at line %zd", line); return false; } end = i; i += 1; while (i < len && l_ascii_isblank(data[i])) i += 1; if (i != len) { l_util_debug(settings->debug_handler, settings->debug_data, "Junk characters at the end of line %zd", line); return false; } group = l_new(struct group_data, 1); group->name = l_strndup(data + 1, end - 1); group->settings = l_queue_new(); l_queue_push_tail(settings->groups, group); return true; } static bool validate_key_character(char c) { if (l_ascii_isalnum(c)) return true; if (c == '_' || c == '-' || c == '.') return true; return false; } static unsigned int parse_key(struct l_settings *settings, const char *data, size_t len, size_t line) { unsigned int i; unsigned int end; struct group_data *group; struct setting_data *pair; for (i = 0; i < len; i++) { if (validate_key_character(data[i])) continue; if (l_ascii_isblank(data[i])) break; l_util_debug(settings->debug_handler, settings->debug_data, "Invalid character in Key on line %zd", line); return 0; } end = i; /* Make sure the rest of the characters are blanks */ while (i < len) { if (l_ascii_isblank(data[i++])) continue; l_util_debug(settings->debug_handler, settings->debug_data, "Garbage after Key on line %zd", line); return 0; } group = l_queue_peek_tail(settings->groups); pair = l_new(struct setting_data, 1); pair->key = l_strndup(data, end); l_queue_push_head(group->settings, pair); return end; } static bool parse_value(struct l_settings *settings, const char *data, size_t len, size_t line) { unsigned int end = len; struct group_data *group; struct setting_data *pair; group = l_queue_peek_tail(settings->groups); pair = l_queue_pop_head(group->settings); if (!l_utf8_validate(data, len, NULL)) { l_util_debug(settings->debug_handler, settings->debug_data, "Invalid UTF8 in value on line: %zd", line); l_free(pair->key); l_free(pair); return false; } pair->value = l_strndup(data, end); l_queue_push_tail(group->settings, pair); return true; } static bool parse_keyvalue(struct l_settings *settings, const char *data, size_t len, size_t line) { const char *equal = memchr(data, '=', len); if (!equal) { l_util_debug(settings->debug_handler, settings->debug_data, "Delimiter '=' not found on line: %zd", line); return false; } if (equal == data) { l_util_debug(settings->debug_handler, settings->debug_data, "Empty key on line: %zd", line); return false; } if (!parse_key(settings, data, equal - data, line)) return false; equal += 1; while (equal < data + len && l_ascii_isblank(*equal)) equal += 1; return parse_value(settings, equal, len - (equal - data), line); } LIB_EXPORT bool l_settings_load_from_data(struct l_settings *settings, const char *data, size_t len) { size_t pos = 0; bool r = true; bool has_group = false; const char *eol; size_t line = 1; size_t line_len; if (unlikely(!settings || !data || !len)) return false; while (pos < len && r) { if (l_ascii_isblank(data[pos])) { pos += 1; continue; } if (data[pos] == '\n') { line += 1; pos += 1; continue; } eol = memchr(data + pos, '\n', len - pos); if (!eol) eol = data + len; line_len = eol - data - pos; if (line_len > 1 && data[pos] == '[' && data[pos + 1] == '@') { ssize_t ret; ret = parse_embedded_group(settings, data + pos, line_len, len - pos, line); if (ret < 0) return false; /* * This is the offset for the actual raw data, the * group line will be offset below */ pos += ret; } else if (data[pos] == '[') { r = parse_group(settings, data + pos, line_len, line); if (r) has_group = true; } else if (data[pos] != '#') { if (!has_group) return false; r = parse_keyvalue(settings, data + pos, line_len, line); } pos += line_len; } return r; } LIB_EXPORT char *l_settings_to_data(const struct l_settings *settings, size_t *len) { struct l_string *buf; char *ret; const struct l_queue_entry *group_entry; if (unlikely(!settings)) return NULL; buf = l_string_new(255); group_entry = l_queue_get_entries(settings->groups); while (group_entry) { struct group_data *group = group_entry->data; const struct l_queue_entry *setting_entry; l_string_append_printf(buf, "[%s]\n", group->name); setting_entry = l_queue_get_entries(group->settings); while (setting_entry) { struct setting_data *setting = setting_entry->data; l_string_append_printf(buf, "%s=%s\n", setting->key, setting->value); setting_entry = setting_entry->next; } if (group_entry->next) l_string_append_c(buf, '\n'); group_entry = group_entry->next; } group_entry = l_queue_get_entries(settings->embedded_groups); if (group_entry && l_queue_length(settings->groups) > 0) l_string_append_c(buf, '\n'); while (group_entry) { struct embedded_group_data *group = group_entry->data; l_string_append_printf(buf, "[@%s@%s]\n%s", group->type, group->name, group->data); if (group_entry->next) l_string_append_c(buf, '\n'); group_entry = group_entry->next; } ret = l_string_unwrap(buf); if (len) *len = strlen(ret); return ret; } LIB_EXPORT bool l_settings_load_from_file(struct l_settings *settings, const char *filename) { int fd; struct stat st; char *data; bool r; if (unlikely(!settings || !filename)) return false; fd = open(filename, O_RDONLY); if (fd < 0) { l_util_debug(settings->debug_handler, settings->debug_data, "Could not open %s (%s)", filename, strerror(errno)); return false; } if (fstat(fd, &st) < 0) { l_util_debug(settings->debug_handler, settings->debug_data, "Could not stat %s (%s)", filename, strerror(errno)); close(fd); return false; } /* Nothing to do, assume success */ if (st.st_size == 0) { close(fd); return true; } data = mmap(NULL, st.st_size, PROT_READ, MAP_SHARED, fd, 0); if (data == MAP_FAILED) { l_util_debug(settings->debug_handler, settings->debug_data, "Could not mmap %s (%s)", filename, strerror(errno)); close(fd); return false; } r = l_settings_load_from_data(settings, data, st.st_size); munmap(data, st.st_size); close(fd); return r; } LIB_EXPORT bool l_settings_set_debug(struct l_settings *settings, l_settings_debug_cb_t callback, void *user_data, l_settings_destroy_cb_t destroy) { if (unlikely(!settings)) return false; if (settings->debug_destroy) settings->debug_destroy(settings->debug_data); settings->debug_handler = callback; settings->debug_destroy = destroy; settings->debug_data = user_data; return true; } static bool group_match(const void *a, const void *b) { const struct group_data *group = a; const char *name = b; return !strcmp(group->name, name); } struct gather_data { int cur; char **v; }; static void gather_groups(void *data, void *user_data) { struct group_data *group_data = data; struct gather_data *gather = user_data; gather->v[gather->cur++] = l_strdup(group_data->name); } LIB_EXPORT char **l_settings_get_groups(const struct l_settings *settings) { char **ret; struct gather_data gather; if (unlikely(!settings)) return NULL; ret = l_new(char *, l_queue_length(settings->groups) + 1); gather.v = ret; gather.cur = 0; l_queue_foreach(settings->groups, gather_groups, &gather); return ret; } LIB_EXPORT bool l_settings_has_group(const struct l_settings *settings, const char *group_name) { struct group_data *group; if (unlikely(!settings)) return false; group = l_queue_find(settings->groups, group_match, group_name); return !!group; } static bool key_match(const void *a, const void *b) { const struct setting_data *setting = a; const char *key = b; return !strcmp(setting->key, key); } static void gather_keys(void *data, void *user_data) { struct setting_data *setting_data = data; struct gather_data *gather = user_data; gather->v[gather->cur++] = l_strdup(setting_data->key); } LIB_EXPORT char **l_settings_get_keys(const struct l_settings *settings, const char *group_name) { char **ret; struct group_data *group_data; struct gather_data gather; if (unlikely(!settings)) return NULL; group_data = l_queue_find(settings->groups, group_match, group_name); if (!group_data) return NULL; ret = l_new(char *, l_queue_length(group_data->settings) + 1); gather.v = ret; gather.cur = 0; l_queue_foreach(group_data->settings, gather_keys, &gather); return ret; } LIB_EXPORT bool l_settings_has_key(const struct l_settings *settings, const char *group_name, const char *key) { struct group_data *group; struct setting_data *setting; if (unlikely(!settings)) return false; group = l_queue_find(settings->groups, group_match, group_name); if (!group) return false; setting = l_queue_find(group->settings, key_match, key); return !!setting; } LIB_EXPORT const char *l_settings_get_value(const struct l_settings *settings, const char *group_name, const char *key) { struct group_data *group; struct setting_data *setting; if (unlikely(!settings)) return NULL; group = l_queue_find(settings->groups, group_match, group_name); if (!group) return NULL; setting = l_queue_find(group->settings, key_match, key); if (!setting) return NULL; return setting->value; } static bool validate_group_name(const char *group_name) { int i; for (i = 0; group_name[i]; i++) { if (!l_ascii_isprint(group_name[i])) return false; if (group_name[i] == ']' || group_name[i] == '[') return false; } return true; } LIB_EXPORT bool l_settings_add_group(struct l_settings *settings, const char *group_name) { struct group_data *group; if (unlikely(!settings || !group_name)) return false; if (!validate_group_name(group_name)) { l_util_debug(settings->debug_handler, settings->debug_data, "Invalid group name %s", group_name); return false; } group = l_queue_find(settings->groups, group_match, group_name); if (group) { l_util_debug(settings->debug_handler, settings->debug_data, "Group %s exists", group_name); return true; } group = l_new(struct group_data, 1); group->name = l_strdup(group_name); group->settings = l_queue_new(); l_queue_push_tail(settings->groups, group); return true; } static bool validate_key(const char *key) { int i; for (i = 0; key[i]; i++) { if (!validate_key_character(key[i])) return false; } return true; } static bool set_value(struct l_settings *settings, const char *group_name, const char *key, char *value) { struct group_data *group; struct setting_data *pair; if (!validate_group_name(group_name)) { l_util_debug(settings->debug_handler, settings->debug_data, "Invalid group name %s", group_name); goto error; } if (!validate_key(key)) { l_util_debug(settings->debug_handler, settings->debug_data, "Invalid key %s", key); goto error; } group = l_queue_find(settings->groups, group_match, group_name); if (!group) { group = l_new(struct group_data, 1); group->name = l_strdup(group_name); group->settings = l_queue_new(); l_queue_push_tail(settings->groups, group); goto add_pair; } pair = l_queue_find(group->settings, key_match, key); if (!pair) { add_pair: pair = l_new(struct setting_data, 1); pair->key = l_strdup(key); pair->value = value; l_queue_push_tail(group->settings, pair); return true; } explicit_bzero(pair->value, strlen(pair->value)); l_free(pair->value); pair->value = value; return true; error: explicit_bzero(value, strlen(value)); l_free(value); return false; } LIB_EXPORT bool l_settings_set_value(struct l_settings *settings, const char *group_name, const char *key, const char *value) { if (unlikely(!settings || !value)) return false; return set_value(settings, group_name, key, l_strdup(value)); } LIB_EXPORT bool l_settings_get_bool(const struct l_settings *settings, const char *group_name, const char *key, bool *out) { const char *value; value = l_settings_get_value(settings, group_name, key); if (!value) return false; if (!strcasecmp(value, "true") || !strcmp(value, "1")) { if (out) *out = true; return true; } if (!strcasecmp(value, "false") || !strcmp(value, "0")) { if (out) *out = false; return true; } l_util_debug(settings->debug_handler, settings->debug_data, "Could not interpret %s as a bool", value); return false; } LIB_EXPORT bool l_settings_set_bool(struct l_settings *settings, const char *group_name, const char *key, bool in) { static const char *true_str = "true"; static const char *false_str = "false"; const char *v; if (in == false) v = false_str; else v = true_str; return l_settings_set_value(settings, group_name, key, v); } LIB_EXPORT bool l_settings_get_int(const struct l_settings *settings, const char *group_name, const char *key, int *out) { const char *value = l_settings_get_value(settings, group_name, key); long int r; int t; char *endp; if (!value) return false; if (*value == '\0') goto error; errno = 0; t = r = strtol(value, &endp, 0); if (*endp != '\0') goto error; if (unlikely(errno == ERANGE || r != t)) goto error; if (out) *out = r; return true; error: l_util_debug(settings->debug_handler, settings->debug_data, "Could not interpret %s as an int", value); return false; } LIB_EXPORT bool l_settings_set_int(struct l_settings *settings, const char *group_name, const char *key, int in) { char buf[64]; snprintf(buf, sizeof(buf), "%d", in); return l_settings_set_value(settings, group_name, key, buf); } LIB_EXPORT bool l_settings_get_uint(const struct l_settings *settings, const char *group_name, const char *key, unsigned int *out) { const char *value = l_settings_get_value(settings, group_name, key); if (!value) return false; if (l_safe_atou32(value, out) < 0) goto error; return true; error: l_util_debug(settings->debug_handler, settings->debug_data, "Could not interpret %s as a uint", value); return false; } LIB_EXPORT bool l_settings_set_uint(struct l_settings *settings, const char *group_name, const char *key, unsigned int in) { char buf[64]; snprintf(buf, sizeof(buf), "%u", in); return l_settings_set_value(settings, group_name, key, buf); } LIB_EXPORT bool l_settings_get_int64(const struct l_settings *settings, const char *group_name, const char *key, int64_t *out) { const char *value = l_settings_get_value(settings, group_name, key); int64_t r; char *endp; if (!value) return false; if (*value == '\0') goto error; errno = 0; r = strtoll(value, &endp, 0); if (*endp != '\0') goto error; if (unlikely(errno == ERANGE)) goto error; if (out) *out = r; return true; error: l_util_debug(settings->debug_handler, settings->debug_data, "Could not interpret %s as an int64", value); return false; } LIB_EXPORT bool l_settings_set_int64(struct l_settings *settings, const char *group_name, const char *key, int64_t in) { char buf[64]; snprintf(buf, sizeof(buf), "%" PRId64, in); return l_settings_set_value(settings, group_name, key, buf); } LIB_EXPORT bool l_settings_get_uint64(const struct l_settings *settings, const char *group_name, const char *key, uint64_t *out) { const char *value = l_settings_get_value(settings, group_name, key); uint64_t r; char *endp; if (!value) return false; /* Do not allow '+' or '-' or empty string */ if (!l_ascii_isdigit(*value)) goto error; errno = 0; r = strtoull(value, &endp, 0); if (*endp != '\0') goto error; if (unlikely(errno == ERANGE)) goto error; if (out) *out = r; return true; error: l_util_debug(settings->debug_handler, settings->debug_data, "Could not interpret %s as a uint64", value); return false; } LIB_EXPORT bool l_settings_set_uint64(struct l_settings *settings, const char *group_name, const char *key, uint64_t in) { char buf[64]; snprintf(buf, sizeof(buf), "%" PRIu64, in); return l_settings_set_value(settings, group_name, key, buf); } LIB_EXPORT char *l_settings_get_string(const struct l_settings *settings, const char *group_name, const char *key) { const char *value = l_settings_get_value(settings, group_name, key); if (!value) return NULL; return unescape_value(value); } LIB_EXPORT bool l_settings_set_string(struct l_settings *settings, const char *group_name, const char *key, const char *value) { char *buf; if (unlikely(!settings || !value)) return false; buf = escape_value(value); return set_value(settings, group_name, key, buf); } LIB_EXPORT char **l_settings_get_string_list(const struct l_settings *settings, const char *group_name, const char *key, const char delimiter) { const char *value = l_settings_get_value(settings, group_name, key); char *str; char **ret; if (!value) return NULL; str = unescape_value(value); if (str == NULL) return NULL; ret = l_strsplit(str, delimiter); l_free(str); return ret; } LIB_EXPORT bool l_settings_set_string_list(struct l_settings *settings, const char *group_name, const char *key, char **value, char delimiter) { char *buf; char *tmp; if (unlikely(!settings || !value)) return false; tmp = l_strjoinv(value, delimiter); buf = escape_value(tmp); l_free(tmp); return set_value(settings, group_name, key, buf); } LIB_EXPORT bool l_settings_get_double(const struct l_settings *settings, const char *group_name, const char *key, double *out) { const char *value = l_settings_get_value(settings, group_name, key); char *endp; double r; if (!value) return NULL; if (*value == '\0') goto error; errno = 0; r = strtod(value, &endp); if (*endp != '\0') goto error; if (unlikely(errno == ERANGE)) goto error; if (out) *out = r; return true; error: l_util_debug(settings->debug_handler, settings->debug_data, "Could not interpret %s as a double", value); return false; } LIB_EXPORT bool l_settings_set_double(struct l_settings *settings, const char *group_name, const char *key, double in) { L_AUTO_FREE_VAR(char *, buf); buf = l_strdup_printf("%f", in); return l_settings_set_value(settings, group_name, key, buf); } LIB_EXPORT bool l_settings_get_float(const struct l_settings *settings, const char *group_name, const char *key, float *out) { const char *value = l_settings_get_value(settings, group_name, key); char *endp; float r; if (!value) return NULL; if (*value == '\0') goto error; errno = 0; r = strtof(value, &endp); if (*endp != '\0') goto error; if (unlikely(errno == ERANGE)) goto error; if (out) *out = r; return true; error: l_util_debug(settings->debug_handler, settings->debug_data, "Could not interpret %s as a float", value); return false; } LIB_EXPORT bool l_settings_set_float(struct l_settings *settings, const char *group_name, const char *key, float in) { L_AUTO_FREE_VAR(char *, buf); buf = l_strdup_printf("%f", (double)in); return l_settings_set_value(settings, group_name, key, buf); } LIB_EXPORT uint8_t *l_settings_get_bytes(const struct l_settings *settings, const char *group_name, const char *key, size_t *out_len) { const char *value = l_settings_get_value(settings, group_name, key); if (!value) return NULL; if (value[0] == '\0') { *out_len = 0; /* Return something that can be l_freed but is not a NULL */ return l_memdup("", 1); } return l_util_from_hexstring(value, out_len); } LIB_EXPORT bool l_settings_set_bytes(struct l_settings *settings, const char *group_name, const char *key, const uint8_t *value, size_t value_len) { char *buf; if (unlikely(!settings || !value)) return false; if (value_len) buf = l_util_hexstring(value, value_len); else buf = l_strdup(""); return set_value(settings, group_name, key, buf); } LIB_EXPORT bool l_settings_remove_group(struct l_settings *settings, const char *group_name) { struct group_data *group; if (unlikely(!settings)) return false; group = l_queue_remove_if(settings->groups, group_match, group_name); if (!group) return false; group_destroy(group); return true; } LIB_EXPORT bool l_settings_remove_key(struct l_settings *settings, const char *group_name, const char *key) { struct group_data *group; struct setting_data *setting; if (unlikely(!settings)) return false; group = l_queue_find(settings->groups, group_match, group_name); if (!group) return false; setting = l_queue_remove_if(group->settings, key_match, key); if (!setting) return false; setting_destroy(setting); return true; } static void gather_embedded_groups(void *data, void *user_data) { struct embedded_group_data *group_data = data; struct gather_data *gather = user_data; gather->v[gather->cur++] = l_strdup(group_data->name); } LIB_EXPORT char **l_settings_get_embedded_groups(struct l_settings *settings) { char **ret; struct gather_data gather; if (unlikely(!settings)) return NULL; ret = l_new(char *, l_queue_length(settings->groups) + 1); gather.v = ret; gather.cur = 0; l_queue_foreach(settings->embedded_groups, gather_embedded_groups, &gather); return ret; } static bool embedded_group_match(const void *a, const void *b) { const struct embedded_group_data *group = a; const char *name = b; return !strcmp(group->name, name); } LIB_EXPORT bool l_settings_has_embedded_group(struct l_settings *settings, const char *group) { struct embedded_group_data *group_data; if (unlikely(!settings)) return false; group_data = l_queue_find(settings->embedded_groups, embedded_group_match, group); return group_data != NULL; } LIB_EXPORT const char *l_settings_get_embedded_value( struct l_settings *settings, const char *group_name, const char **out_type) { struct embedded_group_data *group; if (unlikely(!settings)) return NULL; group = l_queue_find(settings->embedded_groups, embedded_group_match, group_name); if (!group) return NULL; if (out_type) *out_type = group->type; return group->data; } LIB_EXPORT bool l_settings_remove_embedded_groups(struct l_settings *settings) { if (unlikely(!settings)) return false; l_queue_clear(settings->embedded_groups, embedded_group_destroy); return true; } bluez-5.82/ell/PaxHeaders/useful.h0000644000000000000000000000005014661631567014115 xustar0020 atime=1743575449 20 ctime=1743591276 bluez-5.82/ell/useful.h0000644000000000000000000000367314661631567013607 0ustar00rootroot/* * Embedded Linux library * Copyright (C) 2021 Intel Corporation * * SPDX-License-Identifier: LGPL-2.1-or-later */ #define align_len(len, boundary) (((len)+(boundary)-1) & ~((boundary)-1)) #define likely(x) __builtin_expect(!!(x), 1) #define unlikely(x) __builtin_expect(!!(x), 0) #define SWAP(l, r) \ do { typeof(l) __tmp = (l); (l) = (r); (r) = __tmp; } while (0) #ifndef __always_inline #define __always_inline inline __attribute__((always_inline)) #endif static inline size_t minsize(size_t a, size_t b) { if (a <= b) return a; return b; } static inline size_t maxsize(size_t a, size_t b) { if (a >= b) return a; return b; } static inline void set_bit(void *addr, unsigned int bit) { unsigned char *field = addr; field[bit / 8] |= 1U << (bit % 8); } static inline int test_bit(const void *addr, unsigned int bit) { const unsigned char *field = addr; return (field[bit / 8] & (1U << (bit % 8))) != 0; } static inline unsigned char bit_field(const unsigned char oct, unsigned int start, unsigned int n_bits) { unsigned char mask = (1U << n_bits) - 1U; return (oct >> start) & mask; } /* Must be called with n >= 2 and n <= ULONG_MAX / 2 + 1 */ static inline unsigned long roundup_pow_of_two(unsigned long n) { return 1UL << (sizeof(unsigned long) * 8 - __builtin_clzl(n - 1)); } #define DIV_ROUND_CLOSEST(x, divisor) \ ({ \ typeof(divisor) _d = (divisor); \ typeof(x) _x = (x) + _d / 2; \ _x / _d; \ }) #ifndef _auto_ #define _auto_(func) \ __L_AUTODESTRUCT(func) #endif /* * Trick the compiler into thinking that var might be changed somehow by * the asm */ #define DO_NOT_OPTIMIZE(var) \ __asm__ ("" : "=r" (var) : "0" (var)); static inline int secure_select(int select_left, int l, int r) { int mask = -(!!select_left); return r ^ ((l ^ r) & mask); } #define struct_alloc(structname, ...) \ (struct structname *) l_memdup(&(struct structname) { __VA_ARGS__ }, \ sizeof(struct structname)) bluez-5.82/ell/PaxHeaders/hashmap.h0000644000000000000000000000005014504767710014227 xustar0020 atime=1743575449 20 ctime=1743591276 bluez-5.82/ell/hashmap.h0000644000000000000000000000405514504767710013714 0ustar00rootroot/* * Embedded Linux library * Copyright (C) 2011-2014 Intel Corporation * * SPDX-License-Identifier: LGPL-2.1-or-later */ #ifndef __ELL_HASHMAP_H #define __ELL_HASHMAP_H #include #ifdef __cplusplus extern "C" { #endif typedef void (*l_hashmap_foreach_func_t) (const void *key, void *value, void *user_data); typedef void (*l_hashmap_destroy_func_t) (void *value); typedef unsigned int (*l_hashmap_hash_func_t) (const void *p); typedef int (*l_hashmap_compare_func_t) (const void *a, const void *b); typedef void *(*l_hashmap_key_new_func_t) (const void *p); typedef void (*l_hashmap_key_free_func_t) (void *p); typedef bool (*l_hashmap_remove_func_t)(const void *key, void *value, void *user_data); struct l_hashmap; unsigned int l_str_hash(const void *p); struct l_hashmap *l_hashmap_new(void); struct l_hashmap *l_hashmap_string_new(void); bool l_hashmap_set_hash_function(struct l_hashmap *hashmap, l_hashmap_hash_func_t func); bool l_hashmap_set_compare_function(struct l_hashmap *hashmap, l_hashmap_compare_func_t func); bool l_hashmap_set_key_copy_function(struct l_hashmap *hashmap, l_hashmap_key_new_func_t func); bool l_hashmap_set_key_free_function(struct l_hashmap *hashmap, l_hashmap_key_free_func_t func); void l_hashmap_destroy(struct l_hashmap *hashmap, l_hashmap_destroy_func_t destroy); bool l_hashmap_insert(struct l_hashmap *hashmap, const void *key, void *value); bool l_hashmap_replace(struct l_hashmap *hashmap, const void *key, void *value, void **old_value); void *l_hashmap_remove(struct l_hashmap *hashmap, const void *key); void *l_hashmap_lookup(struct l_hashmap *hashmap, const void *key); void l_hashmap_foreach(struct l_hashmap *hashmap, l_hashmap_foreach_func_t function, void *user_data); unsigned int l_hashmap_foreach_remove(struct l_hashmap *hashmap, l_hashmap_remove_func_t function, void *user_data); unsigned int l_hashmap_size(struct l_hashmap *hashmap); bool l_hashmap_isempty(struct l_hashmap *hashmap); #ifdef __cplusplus } #endif #endif /* __ELL_HASHMAP_H */ bluez-5.82/ell/PaxHeaders/tls-record.c0000644000000000000000000000005014504767710014657 xustar0020 atime=1743575538 20 ctime=1743591277 bluez-5.82/ell/tls-record.c0000644000000000000000000004201314504767710014340 0ustar00rootroot/* * Embedded Linux library * Copyright (C) 2015 Intel Corporation * * SPDX-License-Identifier: LGPL-2.1-or-later */ #ifdef HAVE_CONFIG_H #include #endif #define _GNU_SOURCE #include #include "private.h" #include "tls.h" #include "checksum.h" #include "cipher.h" #include "cert.h" #include "tls-private.h" #include "random.h" /* Implementation-specific max Record Layer fragment size (must be < 16kB) */ #define TX_RECORD_MAX_LEN 4096 /* TLSPlaintext + TLSCompressed + TLSCiphertext headers + seq_num sizes */ #define TX_RECORD_MAX_HEADERS (5 + 5 + 8 + 5) #define TX_RECORD_MAX_MAC 64 /* Head room and tail room for the buffer passed to the cipher */ #define TX_RECORD_HEADROOM TX_RECORD_MAX_HEADERS #define TX_RECORD_TAILROOM TX_RECORD_MAX_MAC static void tls_write_mac(struct l_tls *tls, uint8_t *compressed, uint16_t compressed_len, uint8_t *out_buf, bool txrx) { uint8_t *in_buf; /* Prepend the sequence number to the TLSCompressed buffer */ in_buf = compressed - 8; l_put_be64(tls->seq_num[txrx]++, in_buf); if (tls->mac[txrx]) { l_checksum_reset(tls->mac[txrx]); l_checksum_update(tls->mac[txrx], in_buf, compressed_len + 8); l_checksum_get_digest(tls->mac[txrx], out_buf, tls->mac_length[txrx]); } } static void tls_tx_record_plaintext(struct l_tls *tls, uint8_t *plaintext, uint16_t plaintext_len) { uint8_t *compressed; uint16_t compressed_len; uint8_t *cipher_input; uint16_t cipher_input_len; uint8_t *ciphertext; uint16_t ciphertext_len; uint8_t padding_length; uint8_t buf[TX_RECORD_HEADROOM + TX_RECORD_MAX_LEN + TX_RECORD_TAILROOM]; uint8_t iv[32]; uint8_t *assocdata; int offset; /* * TODO: if DEFLATE is selected in current state, use a new buffer * on stack to write a TLSCompressed structure there, otherwise use * the provided buffer. Since only null compression is supported * today we always use the provided buffer. */ compressed_len = plaintext_len - 5; compressed = plaintext; /* Build a TLSCompressed struct */ compressed[0] = plaintext[0]; /* Copy type and version fields */ compressed[1] = plaintext[1]; compressed[2] = plaintext[2]; compressed[3] = compressed_len >> 8; compressed[4] = compressed_len >> 0; switch (tls->cipher_type[1]) { case TLS_CIPHER_STREAM: /* Append the MAC after TLSCompressed.fragment, if needed */ tls_write_mac(tls, compressed, compressed_len + 5, compressed + compressed_len + 5, true); cipher_input = compressed + 5; cipher_input_len = compressed_len + tls->mac_length[1]; if (!tls->cipher[1]) { ciphertext = cipher_input; ciphertext_len = cipher_input_len; } else { ciphertext = buf + TX_RECORD_HEADROOM; ciphertext_len = cipher_input_len; l_cipher_encrypt(tls->cipher[1], cipher_input, ciphertext, cipher_input_len); } break; case TLS_CIPHER_BLOCK: /* Append the MAC after TLSCompressed.fragment, if needed */ cipher_input = compressed + 5; tls_write_mac(tls, compressed, compressed_len + 5, cipher_input + compressed_len, true); cipher_input_len = compressed_len + tls->mac_length[1]; /* Add minimum padding */ padding_length = (~cipher_input_len) & (tls->block_length[1] - 1); memset(cipher_input + cipher_input_len, padding_length, padding_length + 1); cipher_input_len += padding_length + 1; /* Generate an IV */ ciphertext = buf + TX_RECORD_HEADROOM; offset = 0; if (tls->negotiated_version >= L_TLS_V12) { l_getrandom(ciphertext, tls->record_iv_length[1]); l_cipher_set_iv(tls->cipher[1], ciphertext, tls->record_iv_length[1]); offset = tls->record_iv_length[1]; } else if (tls->negotiated_version >= L_TLS_V11) { l_getrandom(iv, tls->record_iv_length[1]); l_cipher_encrypt(tls->cipher[1], iv, ciphertext, tls->record_iv_length[1]); offset = tls->record_iv_length[1]; } l_cipher_encrypt(tls->cipher[1], cipher_input, ciphertext + offset, cipher_input_len); ciphertext_len = offset + cipher_input_len; break; case TLS_CIPHER_AEAD: /* Prepend seq_num to TLSCompressed.type + .version + .length */ assocdata = compressed - 8; l_put_be64(tls->seq_num[1]++, assocdata); cipher_input = compressed + 5; cipher_input_len = compressed_len; /* * Build the IV. The explicit part generation method is * actually cipher suite-specific but our only AEAD cipher * suites only require this part to be unique for each * record. For future suites there may need to be a callback * that generates the per-record IV or an enum for the suite * to select one of a few IV types. * * Note kernel's rfc4106(gcm(...)) algorithm could potentially * be used to build the IV. */ memcpy(iv, tls->fixed_iv[1], tls->fixed_iv_length[1]); l_put_le64(tls->seq_num[1], iv + tls->fixed_iv_length[1]); if (tls->record_iv_length[1] > 8) memset(iv + tls->fixed_iv_length[1] + 8, 42, tls->record_iv_length[1] - 8); /* Build the GenericAEADCipher struct */ ciphertext = buf + TX_RECORD_HEADROOM; memcpy(ciphertext, iv + tls->fixed_iv_length[1], tls->record_iv_length[1]); l_aead_cipher_encrypt(tls->aead_cipher[1], cipher_input, cipher_input_len, assocdata, 13, iv, tls->fixed_iv_length[1] + tls->record_iv_length[1], ciphertext + tls->record_iv_length[1], cipher_input_len + tls->auth_tag_length[1]); ciphertext_len = tls->record_iv_length[1] + cipher_input_len + tls->auth_tag_length[1]; break; default: return; } /* Build a TLSCiphertext struct */ ciphertext -= 5; ciphertext[0] = plaintext[0]; /* Copy type and version fields */ ciphertext[1] = plaintext[1]; ciphertext[2] = plaintext[2]; ciphertext[3] = ciphertext_len >> 8; ciphertext[4] = ciphertext_len >> 0; tls->tx(ciphertext, ciphertext_len + 5, tls->user_data); } void tls_tx_record(struct l_tls *tls, enum tls_content_type type, const uint8_t *data, size_t len) { uint8_t buf[TX_RECORD_HEADROOM + TX_RECORD_MAX_LEN + TX_RECORD_TAILROOM]; uint8_t *fragment, *plaintext; uint16_t fragment_len; uint16_t version = tls->negotiated_version ?: tls->min_version; if (type == TLS_CT_ALERT) tls->record_flush = true; while (len) { fragment = buf + TX_RECORD_HEADROOM; fragment_len = len < TX_RECORD_MAX_LEN ? len : TX_RECORD_MAX_LEN; /* Build a TLSPlaintext struct */ plaintext = fragment - 5; plaintext[0] = type; plaintext[1] = (uint8_t) (version >> 8); plaintext[2] = (uint8_t) (version >> 0); plaintext[3] = fragment_len >> 8; plaintext[4] = fragment_len >> 0; memcpy(plaintext + 5, data, fragment_len); tls_tx_record_plaintext(tls, plaintext, fragment_len + 5); data += fragment_len; len -= fragment_len; } } static bool tls_handle_plaintext(struct l_tls *tls, const uint8_t *plaintext, int len, uint8_t type, uint16_t version) { if (len > (1 << 14)) { TLS_DISCONNECT(TLS_ALERT_DECODE_ERROR, 0, "Plaintext message too long: %i", len); return false; } switch (type) { case TLS_CT_CHANGE_CIPHER_SPEC: case TLS_CT_APPLICATION_DATA: return tls_handle_message(tls, plaintext, len, type, version); /* * We need to perform input reassembly twice at different levels: * once to make sure we're handling complete TLSCiphertext messages, * in l_tls_handle_rx(), and again here so that the Alert and * Handshake message type handlers deal with complete messages. * This does not affect ChangeCipherSpec messages because they're * just a single byte and there are never more than one such message * in a row. Similarly it doesn't affect application data because * the application is not guaranteed that message boundaries are * preserved in any way and we don't know its message lengths anyway. * It does affect Alert because these messages are 2 byte long and * could potentially be split over two TLSPlaintext messages but * there are never more than one Alert in a TLSPlaintext for the same * reason as with ChangeCipherSpec. Handshake messages are the * most affected although the need to do the reassembly twice still * seems wasteful considering most of these messages are sent in * plaintext and TLSCiphertext maps to TLSPlaintext records. */ case TLS_CT_ALERT: case TLS_CT_HANDSHAKE: break; default: TLS_DISCONNECT(TLS_ALERT_DECODE_ERROR, 0, "Unknown content type %i", type); return false; } if (tls->message_buf_len && type != tls->message_content_type) { TLS_DISCONNECT(TLS_ALERT_DECODE_ERROR, 0, "Message fragment type %i doesn't match " "previous type %i", type, tls->message_content_type); return false; } tls->message_content_type = type; while (1) { int header_len, need_len; int chunk_len; /* Do we have a full header in tls->message_buf? */ header_len = (type == TLS_CT_ALERT) ? 2 : 4; need_len = header_len; if (tls->message_buf_len >= header_len) { if (type == TLS_CT_HANDSHAKE) { uint32_t hs_len = (tls->message_buf[1] << 16) | (tls->message_buf[2] << 8) | (tls->message_buf[3] << 0); if (hs_len > (1 << 14)) { TLS_DISCONNECT(TLS_ALERT_DECODE_ERROR, 0, "Handshake message " "too long: %i", (int) hs_len); return false; } need_len += hs_len; } /* Do we have a full structure? */ if (tls->message_buf_len == need_len) { if (!tls_handle_message(tls, tls->message_buf, need_len, type, version)) return false; tls->message_buf_len = 0; if (tls->record_flush) break; continue; } if (!len) break; } /* Try to fill up tls->message_buf up to need_len */ if (tls->message_buf_max_len < need_len) { tls->message_buf_max_len = need_len; tls->message_buf = l_realloc(tls->message_buf, need_len); } need_len -= tls->message_buf_len; chunk_len = need_len; if (len < chunk_len) chunk_len = len; memcpy(tls->message_buf + tls->message_buf_len, plaintext, chunk_len); tls->message_buf_len += chunk_len; plaintext += chunk_len; len -= chunk_len; if (chunk_len < need_len) break; } return true; } static bool tls_handle_ciphertext(struct l_tls *tls) { uint8_t type; uint16_t version; uint16_t fragment_len; uint8_t mac_buf[TX_RECORD_MAX_MAC], i, padding_len; int cipher_output_len, error; uint8_t *compressed; int compressed_len; uint8_t iv[32]; uint8_t *assocdata; type = tls->record_buf[0]; version = l_get_be16(tls->record_buf + 1); fragment_len = l_get_be16(tls->record_buf + 3); if (fragment_len > (1 << 14) + 2048) { TLS_DISCONNECT(TLS_ALERT_RECORD_OVERFLOW, 0, "Record fragment too long: %u", fragment_len); return false; } if ((tls->negotiated_version && tls->negotiated_version != version) || (!tls->negotiated_version && tls->record_buf[1] != 0x03 /* Appending E.1 */)) { TLS_DISCONNECT(TLS_ALERT_PROTOCOL_VERSION, 0, "Record version mismatch: %02x", version); return false; } if (fragment_len < tls->mac_length[0]) { TLS_DISCONNECT(TLS_ALERT_DECODE_ERROR, 0, "Record fragment too short: %u", fragment_len); return false; } compressed = alloca(8 + 5 + fragment_len); /* Copy the type and version fields */ compressed[8] = type; l_put_be16(version, compressed + 9); switch (tls->cipher_type[0]) { case TLS_CIPHER_STREAM: cipher_output_len = fragment_len; compressed_len = cipher_output_len - tls->mac_length[0]; l_put_be16(compressed_len, compressed + 11); if (!tls->cipher[0]) memcpy(compressed + 13, tls->record_buf + 5, cipher_output_len); else if (!l_cipher_decrypt(tls->cipher[0], tls->record_buf + 5, compressed + 13, cipher_output_len)) { TLS_DISCONNECT(TLS_ALERT_INTERNAL_ERROR, 0, "Decrypting record fragment failed"); return false; } /* Calculate the MAC if needed */ tls_write_mac(tls, compressed + 8, 5 + compressed_len, mac_buf, false); if (memcmp(mac_buf, compressed + 13 + compressed_len, tls->mac_length[0])) { TLS_DISCONNECT(TLS_ALERT_BAD_RECORD_MAC, 0, "Record fragment MAC mismatch"); return false; } compressed += 13; break; case TLS_CIPHER_BLOCK: i = 0; if (tls->negotiated_version >= L_TLS_V11) i = tls->record_iv_length[0]; if (fragment_len <= tls->mac_length[0] + i) { TLS_DISCONNECT(TLS_ALERT_DECODE_ERROR, 0, "Record fragment too short: %u", fragment_len); return false; } cipher_output_len = fragment_len - i; if (cipher_output_len % tls->block_length[0] != 0) { /* * In strict TLS 1.0 TLS_ALERT_DECRYPT_FAIL_RESERVED * should be returned here but that was declared * unsafe in the TLS 1.1 spec. */ TLS_DISCONNECT(TLS_ALERT_BAD_RECORD_MAC, 0, "Fragment data len %i not a multiple " "of block length %zi", cipher_output_len, tls->block_length[0]); return false; } if (tls->negotiated_version >= L_TLS_V12) { if (!l_cipher_set_iv(tls->cipher[0], tls->record_buf + 5, tls->record_iv_length[0])) { TLS_DISCONNECT(TLS_ALERT_INTERNAL_ERROR, 0, "Setting fragment IV failed"); return false; } } else if (tls->negotiated_version >= L_TLS_V11) if (!l_cipher_decrypt(tls->cipher[0], tls->record_buf + 5, iv, tls->record_iv_length[0])) { TLS_DISCONNECT(TLS_ALERT_INTERNAL_ERROR, 0, "Setting fragment IV failed"); return false; } if (!l_cipher_decrypt(tls->cipher[0], tls->record_buf + 5 + i, compressed + 13, cipher_output_len)) { TLS_DISCONNECT(TLS_ALERT_INTERNAL_ERROR, 0, "Fragment decryption failed"); return false; } /* * RFC 5246, page 24: * In order to defend against this attack, implementations * MUST ensure that record processing time is essentially the * same whether or not the padding is correct. In general, * the best way to do this is to compute the MAC even if the * padding is incorrect, and only then reject the packet. For * instance, if the pad appears to be incorrect, the * implementation might assume a zero-length pad and then * compute the MAC. */ padding_len = compressed[13 + cipher_output_len - 1]; error = 0; if (padding_len + tls->mac_length[0] + 1 > (size_t) cipher_output_len) { /* * In strict TLS 1.0 TLS_ALERT_DECRYPT_FAIL_RESERVED * should be returned here but that was declared * unsafe in the TLS 1.1 spec. */ padding_len = 0; error = 1; } compressed_len = cipher_output_len - 1 - padding_len - tls->mac_length[0]; l_put_be16(compressed_len, compressed + 11); error |= !l_secure_memeq(compressed + 13 + cipher_output_len - 1 - padding_len, padding_len, padding_len); /* Calculate the MAC if needed */ tls_write_mac(tls, compressed + 8, 5 + compressed_len, mac_buf, false); if ((tls->mac_length[0] && memcmp(mac_buf, compressed + 13 + compressed_len, tls->mac_length[0])) || error) { TLS_DISCONNECT(TLS_ALERT_BAD_RECORD_MAC, 0, "Record fragment MAC mismatch"); return false; } compressed += 13; break; case TLS_CIPHER_AEAD: if (fragment_len <= tls->record_iv_length[0] + tls->auth_tag_length[0]) { TLS_DISCONNECT(TLS_ALERT_DECODE_ERROR, 0, "Record fragment too short: %u", fragment_len); return false; } compressed_len = fragment_len - tls->record_iv_length[0] - tls->auth_tag_length[0]; l_put_be16(compressed_len, compressed + 11); /* Prepend seq_num to TLSCompressed.type + .version + .length */ assocdata = compressed; l_put_be64(tls->seq_num[0]++, assocdata); compressed += 13; /* Build the IV */ memcpy(iv, tls->fixed_iv[0], tls->fixed_iv_length[0]); memcpy(iv + tls->fixed_iv_length[0], tls->record_buf + 5, tls->record_iv_length[0]); if (!l_aead_cipher_decrypt(tls->aead_cipher[0], tls->record_buf + 5 + tls->record_iv_length[0], fragment_len - tls->record_iv_length[0], assocdata, 13, iv, tls->fixed_iv_length[0] + tls->record_iv_length[0], compressed, compressed_len)) { TLS_DISCONNECT(TLS_ALERT_INTERNAL_ERROR, 0, "Decrypting record fragment failed"); return false; } break; default: return false; } /* DEFLATE not supported so just pass on compressed / compressed_len */ return tls_handle_plaintext(tls, compressed, compressed_len, type, version); } LIB_EXPORT void l_tls_handle_rx(struct l_tls *tls, const uint8_t *data, size_t len) { int need_len; int chunk_len; tls->record_flush = false; /* Reassemble TLSCiphertext structures from the received chunks */ while (1) { /* Do we have a full header in tls->record_buf? */ if (tls->record_buf_len >= 5) { need_len = 5 + l_get_be16(tls->record_buf + 3); /* Do we have a full structure? */ if (tls->record_buf_len == need_len) { if (!tls_handle_ciphertext(tls)) return; tls->record_buf_len = 0; need_len = 5; if (tls->record_flush) break; } if (!len) break; } else need_len = 5; /* Try to fill up tls->record_buf up to need_len */ if (tls->record_buf_max_len < need_len) { tls->record_buf_max_len = need_len; tls->record_buf = l_realloc(tls->record_buf, need_len); } need_len -= tls->record_buf_len; chunk_len = need_len; if (len < (size_t) chunk_len) chunk_len = len; memcpy(tls->record_buf + tls->record_buf_len, data, chunk_len); tls->record_buf_len += chunk_len; data += chunk_len; len -= chunk_len; if (chunk_len < need_len) break; } } bluez-5.82/ell/PaxHeaders/signal.h0000644000000000000000000000005014504767710014063 xustar0020 atime=1743575449 20 ctime=1743591276 bluez-5.82/ell/signal.h0000644000000000000000000000113714504767710013546 0ustar00rootroot/* * Embedded Linux library * Copyright (C) 2011-2014 Intel Corporation * * SPDX-License-Identifier: LGPL-2.1-or-later */ #ifndef __ELL_SIGNAL_H #define __ELL_SIGNAL_H #include #ifdef __cplusplus extern "C" { #endif struct l_signal; typedef void (*l_signal_notify_cb_t) (void *user_data); typedef void (*l_signal_destroy_cb_t) (void *user_data); struct l_signal *l_signal_create(uint32_t signo, l_signal_notify_cb_t callback, void *user_data, l_signal_destroy_cb_t destroy); void l_signal_remove(struct l_signal *signal); #ifdef __cplusplus } #endif #endif /* __ELL_SIGNAL_H */ bluez-5.82/ell/PaxHeaders/random.h0000644000000000000000000000005014560467756014077 xustar0020 atime=1743575449 20 ctime=1743591276 bluez-5.82/ell/random.h0000644000000000000000000000067514560467756013570 0ustar00rootroot/* * Embedded Linux library * Copyright (C) 2015 Intel Corporation * * SPDX-License-Identifier: LGPL-2.1-or-later */ #ifndef __ELL_RANDOM_H #define __ELL_RANDOM_H #include #include #include #ifdef __cplusplus extern "C" { #endif bool l_getrandom(void *buf, size_t len); bool l_getrandom_is_supported(void); uint32_t l_getrandom_uint32(void); #ifdef __cplusplus } #endif #endif /* __ELL_RANDOM_H */ bluez-5.82/ell/PaxHeaders/asn1-private.h0000644000000000000000000000005014504767710015120 xustar0020 atime=1743575449 20 ctime=1743591276 bluez-5.82/ell/asn1-private.h0000644000000000000000000001046614504767710014610 0ustar00rootroot/* * Embedded Linux library * Copyright (C) 2017 Intel Corporation * * SPDX-License-Identifier: LGPL-2.1-or-later */ #define ASN1_ID(class, pc, tag) (((class) << 6) | ((pc) << 5) | (tag)) #define ASN1_CLASS_UNIVERSAL 0 #define ASN1_CLASS_CONTEXT 2 #define ASN1_ID_SEQUENCE ASN1_ID(ASN1_CLASS_UNIVERSAL, 1, 0x10) #define ASN1_ID_SET ASN1_ID(ASN1_CLASS_UNIVERSAL, 1, 0x11) #define ASN1_ID_BOOLEAN ASN1_ID(ASN1_CLASS_UNIVERSAL, 0, 0x01) #define ASN1_ID_INTEGER ASN1_ID(ASN1_CLASS_UNIVERSAL, 0, 0x02) #define ASN1_ID_BIT_STRING ASN1_ID(ASN1_CLASS_UNIVERSAL, 0, 0x03) #define ASN1_ID_OCTET_STRING ASN1_ID(ASN1_CLASS_UNIVERSAL, 0, 0x04) #define ASN1_ID_NULL ASN1_ID(ASN1_CLASS_UNIVERSAL, 0, 0x05) #define ASN1_ID_OID ASN1_ID(ASN1_CLASS_UNIVERSAL, 0, 0x06) #define ASN1_ID_UTF8STRING ASN1_ID(ASN1_CLASS_UNIVERSAL, 0, 0x0c) #define ASN1_ID_PRINTABLESTRING ASN1_ID(ASN1_CLASS_UNIVERSAL, 0, 0x13) #define ASN1_ID_IA5STRING ASN1_ID(ASN1_CLASS_UNIVERSAL, 0, 0x16) #define ASN1_ID_UTCTIME ASN1_ID(ASN1_CLASS_UNIVERSAL, 0, 0x17) #define ASN1_ID_GENERALIZEDTIME ASN1_ID(ASN1_CLASS_UNIVERSAL, 0, 0x18) struct asn1_oid { uint8_t asn1_len; uint8_t asn1[11]; }; #define asn1_oid_eq(oid1, oid2_len, oid2_string) \ ((oid1)->asn1_len == (oid2_len) && \ !memcmp((oid1)->asn1, (oid2_string), (oid2_len))) #if __STDC_VERSION__ <= 199409L #define inline __inline__ #endif static inline int asn1_parse_definite_length(const uint8_t **buf, size_t *len) { int n; size_t result = 0; /* Decrease the buffer length left */ if ((*len)-- < 1) return -1; /* * If short form length, move the pointer to start of data and * return the data length. */ if (!(**buf & 0x80)) return *(*buf)++; n = *(*buf)++ & 0x7f; if ((size_t) n > *len) return -1; *len -= n; while (n--) result = (result << 8) | *(*buf)++; return result; } static inline void asn1_write_definite_length(uint8_t **buf, size_t len) { int n; if (len < 0x80) { *(*buf)++ = len; return; } for (n = 1; len >> (n * 8); n++); *(*buf)++ = 0x80 | n; while (n--) *(*buf)++ = len >> (n * 8); } #define ASN1_CONTEXT_IMPLICIT(tag) (0x1000 | (tag)) #define ASN1_CONTEXT_EXPLICIT(tag) (0x2000 | (tag)) /* * Return the tag, length and value of the @index'th * non-context-specific-tagged element in a DER SEQUENCE or one who's * ASN1_CONTEXT_IMPLICIT(tag) matches @index or the inner element of * the one who's ASN1_CONTEXT_EXPLICIT(tag) matches @index. */ static inline const uint8_t *asn1_der_find_elem(const uint8_t *buf, size_t len_in, int index, uint8_t *tag, size_t *len_out) { int n = 0; while (1) { int tlv_len; if (len_in < 2) return NULL; *tag = *buf++; len_in--; tlv_len = asn1_parse_definite_length((void *) &buf, &len_in); if (tlv_len < 0 || (size_t) tlv_len > len_in) return NULL; if (*tag >> 6 != ASN1_CLASS_CONTEXT) { if (n++ == index) { *len_out = tlv_len; return buf; } } else if ((*tag & 0x1f) == (index & 0xfff)) { /* Context-specific tag */ if (index & 0x1000) { /* Implicit */ *len_out = tlv_len; return buf; } else if (index & 0x2000) { /* Explicit */ const uint8_t *outer = buf; int inner_len; if (!(*tag & 0x20)) /* Primitive */ return NULL; if (tlv_len < 2) return NULL; *tag = *buf++; inner_len = asn1_parse_definite_length( (void *) &buf, &len_in); if (outer + tlv_len != buf + inner_len) return NULL; *len_out = inner_len; return buf; } } buf += tlv_len; len_in -= tlv_len; } } /* Return an element in a DER SEQUENCE structure by path */ static inline const uint8_t *asn1_der_find_elem_by_path(const uint8_t *buf, size_t len_in, uint8_t tag, size_t *len_out, ...) { int index; va_list vl; va_start(vl, len_out); index = va_arg(vl, int); while (index != -1) { uint8_t elem_tag; uint8_t expect_tag; int prev_index = index; buf = asn1_der_find_elem(buf, len_in, index, &elem_tag, &len_in); if (!buf) { va_end(vl); return NULL; } index = va_arg(vl, int); if (prev_index & 0x1000) expect_tag = ASN1_ID(ASN1_CLASS_CONTEXT, index != -1 ? 1 : ((elem_tag >> 5) & 1), prev_index & 0xfff); else expect_tag = (index == -1) ? tag : ASN1_ID_SEQUENCE; if (elem_tag != expect_tag) { va_end(vl); return NULL; } } va_end(vl); *len_out = len_in; return buf; } bluez-5.82/ell/PaxHeaders/hashmap.c0000644000000000000000000000005014560467756014233 xustar0020 atime=1743575465 20 ctime=1743591276 bluez-5.82/ell/hashmap.c0000644000000000000000000003521214560467756013717 0ustar00rootroot/* * Embedded Linux library * Copyright (C) 2011-2014 Intel Corporation * * SPDX-License-Identifier: LGPL-2.1-or-later */ #ifdef HAVE_CONFIG_H #include #endif #include "hashmap.h" #include "private.h" #include "useful.h" /** * SECTION:hashmap * @short_description: Hash table support * * Hash table support */ #define NBUCKETS 127 struct entry { void *key; void *value; struct entry *next; unsigned int hash; }; /** * l_hashmap: * * Opaque object representing the hash table. */ struct l_hashmap { l_hashmap_hash_func_t hash_func; l_hashmap_compare_func_t compare_func; l_hashmap_key_new_func_t key_new_func; l_hashmap_key_free_func_t key_free_func; unsigned int entries; struct entry buckets[NBUCKETS]; }; static inline void *get_key_new(const struct l_hashmap *hashmap, const void *key) { if (hashmap->key_new_func) return hashmap->key_new_func(key); return (void *)key; } static inline void free_key(const struct l_hashmap *hashmap, void *key) { if (hashmap->key_free_func) hashmap->key_free_func(key); } static inline unsigned int hash_superfast(const uint8_t *key, unsigned int len) { /* * Paul Hsieh (http://www.azillionmonkeys.com/qed/hash.html) * used by WebCore (http://webkit.org/blog/8/hashtables-part-2/), * EFL's eina, kmod and possible others. */ unsigned int tmp, hash = len, rem = len & 3; len /= 4; /* Main loop */ for (; len > 0; len--) { hash += l_get_u16(key); tmp = (l_get_u16(key + 2) << 11) ^ hash; hash = (hash << 16) ^ tmp; key += 4; hash += hash >> 11; } /* Handle end cases */ switch (rem) { case 3: hash += l_get_u16(key); hash ^= hash << 16; hash ^= key[2] << 18; hash += hash >> 11; break; case 2: hash += l_get_u16(key); hash ^= hash << 11; hash += hash >> 17; break; case 1: hash += *key; hash ^= hash << 10; hash += hash >> 1; break; } /* Force "avalanching" of final 127 bits */ hash ^= hash << 3; hash += hash >> 5; hash ^= hash << 4; hash += hash >> 17; hash ^= hash << 25; hash += hash >> 6; return hash; } static unsigned int direct_hash_func(const void *p) { return L_PTR_TO_UINT(p); } static int direct_compare_func(const void *a, const void *b) { return a < b ? -1 : (a > b ? 1 : 0); } /** * l_hashmap_new: * * Create a new hash table. The keys are managed as pointers, that is, * the pointer value is hashed and looked up. * * No error handling is needed since. In case of real memory allocation * problems abort() will be called. * * See also l_hashmap_string_new(). * * Returns: a newly allocated #l_hashmap object **/ LIB_EXPORT struct l_hashmap *l_hashmap_new(void) { struct l_hashmap *hashmap; hashmap = l_new(struct l_hashmap, 1); hashmap->hash_func = direct_hash_func; hashmap->compare_func = direct_compare_func; hashmap->entries = 0; return hashmap; } LIB_EXPORT unsigned int l_str_hash(const void *p) { const char *s = p; size_t len = strlen(s); return hash_superfast((const uint8_t *)s, len); } /** * l_hashmap_string_new: * * Create a new hash table. The keys are considered strings and are * copied. * * No error handling is needed since. In case of real memory allocation * problems abort() will be called. * * See also l_hashmap_new(). * * Returns: a newly allocated #l_hashmap object **/ LIB_EXPORT struct l_hashmap *l_hashmap_string_new(void) { struct l_hashmap *hashmap; hashmap = l_new(struct l_hashmap, 1); hashmap->hash_func = l_str_hash; hashmap->compare_func = (l_hashmap_compare_func_t) strcmp; hashmap->key_new_func = (l_hashmap_key_new_func_t) l_strdup; hashmap->key_free_func = l_free; hashmap->entries = 0; return hashmap; } /** * l_hashmap_set_hash_function: * @hashmap: hash table object * @func: Key hashing function * * Sets the hashing function to be used by this object. * * This function can only be called when the @hashmap is empty. * * Returns: #true when the hashing function could be updated successfully, * and #false otherwise. **/ LIB_EXPORT bool l_hashmap_set_hash_function(struct l_hashmap *hashmap, l_hashmap_hash_func_t func) { if (unlikely(!hashmap)) return false; if (hashmap->entries != 0) return false; hashmap->hash_func = func; return true; } /** * l_hashmap_set_compare_function: * @hashmap: hash table object * @func: Key compare function * * Sets the key comparison function to be used by this object. * * This function can only be called when the @hashmap is empty. * * Returns: #true when the comparison function could be updated successfully, * and #false otherwise. **/ LIB_EXPORT bool l_hashmap_set_compare_function(struct l_hashmap *hashmap, l_hashmap_compare_func_t func) { if (unlikely(!hashmap)) return false; if (hashmap->entries != 0) return false; hashmap->compare_func = func; return true; } /** * l_hashmap_set_key_copy_function: * @hashmap: hash table object * @func: Key duplication function * * Sets the key duplication function to be used by this object. If the * function is NULL, then the keys are assigned directly. * * This function can only be called when the @hashmap is empty. * * Returns: #true when the key copy function could be updated successfully, * and #false otherwise. **/ LIB_EXPORT bool l_hashmap_set_key_copy_function(struct l_hashmap *hashmap, l_hashmap_key_new_func_t func) { if (unlikely(!hashmap)) return false; if (hashmap->entries != 0) return false; hashmap->key_new_func = func; return true; } /** * l_hashmap_set_key_free_function: * @hashmap: hash table object * @func: Key destructor function * * Sets the key destructor function to be used by this object. This function * should undo the result of the function specified in * l_hashmap_set_key_copy_function(). This function can be NULL, in which * case no destructor is called. * * This function can only be called when the @hashmap is empty. * * Returns: #true when the key free function could be updated successfully, * and #false otherwise. **/ LIB_EXPORT bool l_hashmap_set_key_free_function(struct l_hashmap *hashmap, l_hashmap_key_free_func_t func) { if (unlikely(!hashmap)) return false; if (hashmap->entries != 0) return false; hashmap->key_free_func = func; return true; } /** * l_hashmap_destroy: * @hashmap: hash table object * @destroy: destroy function * * Free hash table and call @destroy on all remaining entries. * * NOTE: While the destroy is in progress, the hashmap is assumed to be * invariant. The behavior of adding or removing entries while a destroy * operation is in progress is undefined. **/ LIB_EXPORT void l_hashmap_destroy(struct l_hashmap *hashmap, l_hashmap_destroy_func_t destroy) { unsigned int i; if (unlikely(!hashmap)) return; for (i = 0; i < NBUCKETS; i++) { struct entry *entry, *next, *head = &hashmap->buckets[i]; if (!head->next) continue; for (entry = head;; entry = next) { if (destroy) destroy(entry->value); free_key(hashmap, entry->key); next = entry->next; if (entry != head) l_free(entry); if (next == head) break; } } l_free(hashmap); } /** * l_hashmap_insert: * @hashmap: hash table object * @key: key pointer * @value: value pointer * * Insert new @value entry with @key. Note that entries with a duplicate key * are allowed. If a duplicate entry in inserted, it will be added in order * of insertion. @l_hashmap_lookup and @l_hashmap_remove will remove the * first matching entry. * * Returns: #true when value has been added and #false in case of failure **/ LIB_EXPORT bool l_hashmap_insert(struct l_hashmap *hashmap, const void *key, void *value) { struct entry *entry, *head; unsigned int hash; void *key_new; if (unlikely(!hashmap)) return false; key_new = get_key_new(hashmap, key); hash = hashmap->hash_func(key_new); head = &hashmap->buckets[hash % NBUCKETS]; if (!head->next) { head->key = key_new; head->value = value; head->hash = hash; head->next = head; goto done; } entry = l_new(struct entry, 1); entry->key = key_new; entry->value = value; entry->hash = hash; entry->next = head; while (head->next != entry->next) head = head->next; head->next = entry; done: hashmap->entries++; return true; } /** * l_hashmap_replace: * @hashmap: hash table object * @key: key pointer * @value: value pointer * @old_value: old value that has been replaced. * * Replace the first entry with @key by @value or insert a new @value entry * with @key. If the entry was replaced, then the old value is returned * in @old_value, otherwise @old_value is assigned a #NULL. * * Returns: #true when value has been added and #false in case of failure **/ LIB_EXPORT bool l_hashmap_replace(struct l_hashmap *hashmap, const void *key, void *value, void **old_value) { struct entry *entry; struct entry *head; unsigned int hash; void *key_new; if (unlikely(!hashmap)) return false; key_new = get_key_new(hashmap, key); hash = hashmap->hash_func(key_new); head = &hashmap->buckets[hash % NBUCKETS]; if (!head->next) { head->key = key_new; head->value = value; head->hash = hash; head->next = head; goto done; } for (entry = head;; entry = entry->next) { if (entry->hash != hash) goto next; if (hashmap->compare_func(key, entry->key)) goto next; if (old_value) *old_value = entry->value; entry->value = value; free_key(hashmap, key_new); return true; next: if (entry->next == head) break; } entry = l_new(struct entry, 1); entry->key = key_new; entry->value = value; entry->hash = hash; entry->next = head; while (head->next != entry->next) head = head->next; head->next = entry; done: if (old_value) *old_value = NULL; hashmap->entries++; return true; } /** * l_hashmap_remove: * @hashmap: hash table object * @key: key pointer * * Remove entry for @key. * * Returns: value pointer of the removed entry or #NULL in case of failure **/ LIB_EXPORT void *l_hashmap_remove(struct l_hashmap *hashmap, const void *key) { struct entry *entry, *head, *prev; unsigned int hash; if (unlikely(!hashmap)) return NULL; hash = hashmap->hash_func(key); head = &hashmap->buckets[hash % NBUCKETS]; if (!head->next) return NULL; for (entry = head, prev = NULL;; prev = entry, entry = entry->next) { void *value; if (entry->hash != hash) goto next; if (hashmap->compare_func(key, entry->key)) goto next; value = entry->value; if (entry == head) { if (entry->next == head) { free_key(hashmap, entry->key); head->key = NULL; head->value = NULL; head->hash = 0; head->next = NULL; } else { entry = entry->next; free_key(hashmap, head->key); head->key = entry->key; head->value = entry->value; head->hash = entry->hash; head->next = entry->next; l_free(entry); } } else { prev->next = entry->next; free_key(hashmap, entry->key); l_free(entry); } hashmap->entries--; return value; next: if (entry->next == head) break; } return NULL; } /** * l_hashmap_lookup: * @hashmap: hash table object * @key: key pointer * * Lookup entry for @key. * * Returns: value pointer for @key or #NULL in case of failure **/ LIB_EXPORT void *l_hashmap_lookup(struct l_hashmap *hashmap, const void *key) { struct entry *entry, *head; unsigned int hash; if (unlikely(!hashmap)) return NULL; hash = hashmap->hash_func(key); head = &hashmap->buckets[hash % NBUCKETS]; if (!head->next) return NULL; for (entry = head;; entry = entry->next) { if (entry->hash == hash && !hashmap->compare_func(key, entry->key)) return entry->value; if (entry->next == head) break; } return NULL; } /** * l_hashmap_foreach: * @hashmap: hash table object * @function: callback function * @user_data: user data given to callback function * * Call @function for every entry in @hashmap. * * NOTE: While the foreach is in progress, the hashmap is assumed to be * invariant. The behavior of adding or removing entries while a foreach * operation is in progress is undefined. **/ LIB_EXPORT void l_hashmap_foreach(struct l_hashmap *hashmap, l_hashmap_foreach_func_t function, void *user_data) { unsigned int i; if (unlikely(!hashmap || !function)) return; for (i = 0; i < NBUCKETS; i++) { struct entry *entry, *head = &hashmap->buckets[i]; if (!head->next) continue; for (entry = head;; entry = entry->next) { function(entry->key, entry->value, user_data); if (entry->next == head) break; } } } /** * l_hashmap_foreach_remove: * @hashmap: hash table object * @function: callback function * @user_data: user data given to callback function * * Call @function for every entry in @hashmap. If the @function returns * true, then the object will be removed from the hashmap. * * NOTE: While the foreach is in progress, the hashmap is assumed to be * invariant. The behavior of adding or removing entries while a foreach * operation is in progress is undefined. * * Returns: Number of entries removed. **/ LIB_EXPORT unsigned int l_hashmap_foreach_remove(struct l_hashmap *hashmap, l_hashmap_remove_func_t function, void *user_data) { unsigned int i; unsigned int nremoved = 0; if (unlikely(!hashmap || !function)) return 0; for (i = 0; i < NBUCKETS; i++) { struct entry *head = &hashmap->buckets[i]; struct entry *entry; struct entry *prev; bool remove; if (head->next == NULL) continue; entry = head; prev = NULL; while (true) { remove = function(entry->key, entry->value, user_data); if (!remove) goto next; nremoved += 1; hashmap->entries -= 1; if (entry == head) { if (entry->next == head) { free_key(hashmap, entry->key); head->key = NULL; head->value = NULL; head->hash = 0; head->next = NULL; break; } else { entry = entry->next; free_key(hashmap, head->key); head->key = entry->key; head->value = entry->value; head->hash = entry->hash; head->next = entry->next; l_free(entry); entry = head; continue; } } else { prev->next = entry->next; free_key(hashmap, entry->key); l_free(entry); entry = prev->next; if (entry == head) break; continue; } next: if (entry->next == head) break; prev = entry; entry = entry->next; } } return nremoved; } /** * l_hashmap_size: * @hashmap: hash table object * * Returns: entries in the hash table **/ LIB_EXPORT unsigned int l_hashmap_size(struct l_hashmap *hashmap) { if (unlikely(!hashmap)) return 0; return hashmap->entries; } /** * l_hashmap_isempty: * @hashmap: hash table object * * Returns: #true if hash table is empty and #false if not **/ LIB_EXPORT bool l_hashmap_isempty(struct l_hashmap *hashmap) { if (unlikely(!hashmap)) return true; return hashmap->entries == 0; } bluez-5.82/ell/PaxHeaders/dbus-filter.c0000644000000000000000000000005014504767710015021 xustar0020 atime=1743575517 20 ctime=1743591277 bluez-5.82/ell/dbus-filter.c0000644000000000000000000002216714504767710014512 0ustar00rootroot/* * Embedded Linux library * Copyright (C) 2016 Intel Corporation * * SPDX-License-Identifier: LGPL-2.1-or-later */ #ifdef HAVE_CONFIG_H #include #endif #define _GNU_SOURCE #include #include #include "util.h" #include "queue.h" #include "hashmap.h" #include "string.h" #include "dbus.h" #include "dbus-private.h" #include "gvariant-private.h" #include "private.h" #include "useful.h" #define NODE_TYPE_CALLBACK L_DBUS_MATCH_NONE struct filter_node { enum l_dbus_match_type type; union { struct { char *value; struct filter_node *children; bool remote_rule; } match; struct { l_dbus_message_func_t func; void *user_data; } callback; }; unsigned int id; struct filter_node *next; }; struct _dbus_filter { struct l_dbus *dbus; struct filter_node *root; unsigned int signal_id; unsigned int last_id; const struct _dbus_filter_ops *driver; struct _dbus_name_cache *name_cache; }; static void filter_subtree_free(struct filter_node *node) { struct filter_node *child, *next; if (node->type == NODE_TYPE_CALLBACK) { l_free(node); return; } next = node->match.children; l_free(node->match.value); l_free(node); while (next) { child = next; next = child->next; filter_subtree_free(child); } } static void dbus_filter_destroy(void *data) { struct _dbus_filter *filter = data; if (filter->root) filter_subtree_free(filter->root); l_free(filter); } static void filter_dispatch_match_recurse(struct _dbus_filter *filter, struct filter_node *node, struct l_dbus_message *message) { const char *value = NULL; const char *alt_value = NULL; struct filter_node *child; switch ((int) node->type) { case NODE_TYPE_CALLBACK: node->callback.func(message, node->callback.user_data); return; case L_DBUS_MATCH_SENDER: value = l_dbus_message_get_sender(message); break; case L_DBUS_MATCH_TYPE: value = _dbus_message_get_type_as_string(message); break; case L_DBUS_MATCH_PATH: value = l_dbus_message_get_path(message); break; case L_DBUS_MATCH_INTERFACE: value = l_dbus_message_get_interface(message); break; case L_DBUS_MATCH_MEMBER: value = l_dbus_message_get_member(message); break; case L_DBUS_MATCH_ARG0...(L_DBUS_MATCH_ARG0 + 63): value = _dbus_message_get_nth_string_argument(message, node->type - L_DBUS_MATCH_ARG0); break; } if (!value) return; if (node->type == L_DBUS_MATCH_SENDER && filter->name_cache) alt_value = _dbus_name_cache_lookup(filter->name_cache, node->match.value); if (strcmp(value, node->match.value) && (!alt_value || strcmp(value, alt_value))) return; for (child = node->match.children; child; child = child->next) filter_dispatch_match_recurse(filter, child, message); } void _dbus_filter_dispatch(struct l_dbus_message *message, void *user_data) { struct _dbus_filter *filter = user_data; filter_dispatch_match_recurse(filter, filter->root, message); } struct _dbus_filter *_dbus_filter_new(struct l_dbus *dbus, const struct _dbus_filter_ops *driver, struct _dbus_name_cache *name_cache) { struct _dbus_filter *filter; filter = l_new(struct _dbus_filter, 1); filter->dbus = dbus; filter->driver = driver; filter->name_cache = name_cache; if (!filter->driver->skip_register) filter->signal_id = l_dbus_register(dbus, _dbus_filter_dispatch, filter, dbus_filter_destroy); return filter; } void _dbus_filter_free(struct _dbus_filter *filter) { if (!filter) return; if (!filter->driver->skip_register) l_dbus_unregister(filter->dbus, filter->signal_id); else dbus_filter_destroy(filter); } static int condition_compare(const void *a, const void *b) { const struct _dbus_filter_condition *condition_a = a, *condition_b = b; return condition_a->type - condition_b->type; } static bool remove_recurse(struct _dbus_filter *filter, struct filter_node **node, unsigned int id) { struct filter_node *tmp; for (; *node; node = &(*node)->next) { if ((*node)->type == NODE_TYPE_CALLBACK && (*node)->id == id) break; if ((*node)->type != NODE_TYPE_CALLBACK && remove_recurse(filter, &(*node)->match.children, id)) break; } if (!*node) return false; if ((*node)->type == NODE_TYPE_CALLBACK || !(*node)->match.children) { tmp = *node; *node = tmp->next; if (tmp->match.remote_rule) filter->driver->remove_match(filter->dbus, tmp->id); if (tmp->type == L_DBUS_MATCH_SENDER && filter->name_cache && !_dbus_parse_unique_name(tmp->match.value, NULL)) _dbus_name_cache_remove(filter->name_cache, tmp->match.value); filter_subtree_free(tmp); } return true; } unsigned int _dbus_filter_add_rule(struct _dbus_filter *filter, const struct _dbus_filter_condition *rule, int rule_len, l_dbus_message_func_t signal_func, void *user_data) { struct filter_node **node_ptr = &filter->root; struct filter_node *node; struct filter_node *parent = filter->root; bool remote_rule = false; struct _dbus_filter_condition sorted[rule_len]; struct _dbus_filter_condition *unused; struct _dbus_filter_condition *condition; struct _dbus_filter_condition *end = sorted + rule_len; memcpy(sorted, rule, sizeof(sorted)); qsort(sorted, rule_len, sizeof(*condition), condition_compare); /* * Find or create a path in the tree with a node for each * condition in the rule, loop until all conditions have been * used. */ unused = sorted; while (unused < end) { /* * Find a child of the node that matches any unused * condition. Note there could be multiple matches, we're * happy with the first we can find. */ while (*node_ptr) { node = *node_ptr; for (condition = unused; condition < end; condition++) { if (condition->type > node->type) { condition = end; break; } if (condition->type < node->type || condition->type == L_DBUS_MATCH_NONE) continue; if (!strcmp(node->match.value, condition->value)) break; } if (condition < end) break; node_ptr = &node->next; } /* Add a node */ if (!*node_ptr) { condition = unused; node = l_new(struct filter_node, 1); node->type = condition->type; node->match.value = l_strdup(condition->value); *node_ptr = node; if (node->type == L_DBUS_MATCH_SENDER && filter->name_cache && !_dbus_parse_unique_name( node->match.value, NULL)) _dbus_name_cache_add(filter->name_cache, node->match.value); } _Pragma("GCC diagnostic push") _Pragma("GCC diagnostic ignored \"-Wmaybe-uninitialized\"") /* * Mark the condition used. We do this by setting * condition->type to an invalid value unless it is the * first condition left in which case we can push the * rule start. Another option is to always push the rule * start and memmove the still unused conditions by one * if necessary. */ condition->type = L_DBUS_MATCH_NONE; while (unused < end && unused[0].type == L_DBUS_MATCH_NONE) unused++; node_ptr = &node->match.children; parent = node; /* * Only have to call AddMatch if none of the parent nodes * have yet created an AddMatch rule on the server. */ remote_rule |= node->match.remote_rule; _Pragma("GCC diagnostic pop") } node = l_new(struct filter_node, 1); node->type = NODE_TYPE_CALLBACK; node->callback.func = signal_func; node->callback.user_data = user_data; node->id = ++filter->last_id; node->next = *node_ptr; *node_ptr = node; if (!remote_rule) { if (!filter->driver->add_match(filter->dbus, node->id, rule, rule_len)) goto err; _Pragma("GCC diagnostic push") _Pragma("GCC diagnostic ignored \"-Wmaybe-uninitialized\"") parent->id = node->id; parent->match.remote_rule = true; _Pragma("GCC diagnostic pop") } return node->id; err: /* Remove all the nodes we may have added */ node->id = (unsigned int) -1; remove_recurse(filter, &filter->root, node->id); return 0; } bool _dbus_filter_remove_rule(struct _dbus_filter *filter, unsigned int id) { return remove_recurse(filter, &filter->root, id); } char *_dbus_filter_rule_to_str(const struct _dbus_filter_condition *rule, int rule_len) { struct l_string *str = l_string_new(63); char *key, arg_buf[6]; const char *value, *endp; for (; rule_len; rule++, rule_len--) { switch ((int) rule->type) { case L_DBUS_MATCH_SENDER: key = "sender"; break; case L_DBUS_MATCH_TYPE: key = "type"; break; case L_DBUS_MATCH_PATH: key = "path"; break; case L_DBUS_MATCH_INTERFACE: key = "interface"; break; case L_DBUS_MATCH_MEMBER: key = "member"; break; case L_DBUS_MATCH_ARG0...(L_DBUS_MATCH_ARG0 + 63): key = arg_buf; snprintf(arg_buf, sizeof(arg_buf), "arg%i", rule->type - L_DBUS_MATCH_ARG0); break; default: l_string_free(str); return NULL; } l_string_append(str, key); l_string_append(str, "='"); /* We only need to escape single-quotes in the values */ value = rule->value; while ((endp = strchr(value, '\''))) { l_string_append_fixed(str, value, endp - value); l_string_append(str, "'\\''"); value = endp + 1; } l_string_append(str, value); l_string_append_c(str, '\''); if (rule_len > 1) l_string_append_c(str, ','); } return l_string_unwrap(str); } bluez-5.82/ell/PaxHeaders/cert-crypto.c0000644000000000000000000000005014770744372015060 xustar0020 atime=1743575492 20 ctime=1743591276 bluez-5.82/ell/cert-crypto.c0000644000000000000000000004547214770744372014555 0ustar00rootroot/* * Embedded Linux library * Copyright (C) 2020 Intel Corporation * * SPDX-License-Identifier: LGPL-2.1-or-later */ #ifdef HAVE_CONFIG_H #include #endif #define _GNU_SOURCE #include #include #include #include #include "checksum.h" #include "cipher.h" #include "useful.h" #include "utf8.h" #include "asn1-private.h" #include "private.h" #include "missing.h" #include "cert.h" #include "cert-private.h" /* RFC8018 section 5.1 */ LIB_EXPORT bool l_cert_pkcs5_pbkdf1(enum l_checksum_type type, const char *password, const uint8_t *salt, size_t salt_len, unsigned int iter_count, uint8_t *out_dk, size_t dk_len) { size_t hash_len, t_len; uint8_t t[20 + salt_len + strlen(password)]; struct l_checksum *checksum; switch (type) { case L_CHECKSUM_NONE: case L_CHECKSUM_MD4: return false; case L_CHECKSUM_MD5: hash_len = 16; break; case L_CHECKSUM_SHA1: hash_len = 20; break; case L_CHECKSUM_SHA224: case L_CHECKSUM_SHA256: case L_CHECKSUM_SHA384: case L_CHECKSUM_SHA512: case L_CHECKSUM_SHA3_224: case L_CHECKSUM_SHA3_256: case L_CHECKSUM_SHA3_384: case L_CHECKSUM_SHA3_512: return false; default: return false; } if (dk_len > hash_len) return false; checksum = l_checksum_new(type); if (!checksum) return false; memcpy(t, password, strlen(password)); memcpy(t + strlen(password), salt, salt_len); t_len = strlen(password) + salt_len; while (iter_count) { l_checksum_reset(checksum); if (!l_checksum_update(checksum, t, t_len)) break; if (l_checksum_get_digest(checksum, t, hash_len) != (ssize_t) hash_len) break; t_len = hash_len; iter_count--; } l_checksum_free(checksum); if (!iter_count) memcpy(out_dk, t, dk_len); explicit_bzero(t, sizeof(t)); return !iter_count; } /* RFC8018 section 5.2 */ LIB_EXPORT bool l_cert_pkcs5_pbkdf2(enum l_checksum_type type, const char *password, const uint8_t *salt, size_t salt_len, unsigned int iter_count, uint8_t *out_dk, size_t dk_len) { size_t h_len; struct l_checksum *checksum; unsigned int i; switch (type) { case L_CHECKSUM_NONE: case L_CHECKSUM_MD4: case L_CHECKSUM_MD5: return false; case L_CHECKSUM_SHA1: h_len = 20; break; case L_CHECKSUM_SHA224: h_len = 28; break; case L_CHECKSUM_SHA256: h_len = 32; break; case L_CHECKSUM_SHA384: h_len = 48; break; case L_CHECKSUM_SHA512: h_len = 64; break; case L_CHECKSUM_SHA3_224: case L_CHECKSUM_SHA3_256: case L_CHECKSUM_SHA3_384: case L_CHECKSUM_SHA3_512: return false; default: return false; } checksum = l_checksum_new_hmac(type, password, strlen(password)); if (!checksum) return false; for (i = 1; dk_len; i++) { unsigned int j, k; uint8_t u[salt_len + 64]; size_t u_len; size_t block_len = h_len; if (block_len > dk_len) block_len = dk_len; memset(out_dk, 0, block_len); memcpy(u, salt, salt_len); l_put_be32(i, u + salt_len); u_len = salt_len + 4; for (j = 0; j < iter_count; j++) { l_checksum_reset(checksum); if (!l_checksum_update(checksum, u, u_len)) break; if (l_checksum_get_digest(checksum, u, h_len) != (ssize_t) h_len) break; u_len = h_len; for (k = 0; k < block_len; k++) out_dk[k] ^= u[k]; } if (j < iter_count) break; out_dk += block_len; dk_len -= block_len; } l_checksum_free(checksum); return !dk_len; } /* RFC7292 Appendix B */ uint8_t *cert_pkcs12_pbkdf(const char *password, const struct cert_pkcs12_hash *hash, const uint8_t *salt, size_t salt_len, unsigned int iterations, uint8_t id, size_t key_len) { /* All lengths in bytes instead of bits */ size_t passwd_len = password ? 2 * strlen(password) + 2 : 0; uint8_t *bmpstring; /* Documented as v(ceiling(s/v)), usually will just equal v */ unsigned int s_len = (salt_len + hash->v - 1) & ~(hash->v - 1); /* Documented as p(ceiling(s/p)), usually will just equal v */ unsigned int p_len = password ? (passwd_len + hash->v - 1) & ~(hash->v - 1) : 0; uint8_t di[hash->v + s_len + p_len]; uint8_t *ptr; unsigned int j; uint8_t *key; unsigned int bytes; struct l_checksum *h = l_checksum_new(hash->alg); if (!h) return NULL; /* * The BMPString encoding, in practice same as UCS-2, can end up * at 2 * strlen(password) + 2 bytes or shorter depending on the * characters used. Recalculate p_len after we know it. * Important: The password must be valid UTF-8 here. */ if (p_len) { if (!(bmpstring = l_utf8_to_ucs2be(password, &passwd_len))) { l_checksum_free(h); return NULL; } p_len = (passwd_len + hash->v - 1) & ~(hash->v - 1); } memset(di, id, hash->v); ptr = di + hash->v; for (j = salt_len; j < s_len; j += salt_len, ptr += salt_len) memcpy(ptr, salt, salt_len); if (s_len) { memcpy(ptr, salt, s_len + salt_len - j); ptr += s_len + salt_len - j; } _Pragma("GCC diagnostic push") _Pragma("GCC diagnostic ignored \"-Wmaybe-uninitialized\"") if (p_len) { for (j = passwd_len; j < p_len; j += passwd_len, ptr += passwd_len) memcpy(ptr, bmpstring, passwd_len); memcpy(ptr, bmpstring, p_len + passwd_len - j); explicit_bzero(bmpstring, passwd_len); l_free(bmpstring); } _Pragma("GCC diagnostic pop") key = l_malloc(key_len + hash->len); for (bytes = 0; bytes < key_len; bytes += hash->u) { uint8_t b[hash->v]; uint8_t *input = di; unsigned int input_len = hash->v + s_len + p_len; for (j = 0; j < iterations; j++) { if (!l_checksum_update(h, input, input_len) || l_checksum_get_digest(h, key + bytes, hash->len) <= 0) { l_checksum_free(h); l_free(key); return NULL; } input = key + bytes; input_len = hash->u; l_checksum_reset(h); } if (bytes + hash->u >= key_len) break; for (j = 0; j < hash->v - hash->u; j += hash->u) memcpy(b + j, input, hash->u); memcpy(b + j, input, hash->v - j); ptr = di + hash->v; for (j = 0; j < s_len + p_len; j += hash->v, ptr += hash->v) { unsigned int k; uint16_t carry = 1; /* * Not specified in the RFC7292 but implementations * sum these octet strings as big-endian integers. * We could use 64-bit additions here but the benefit * may not compensate the cost of the byteswapping. */ for (k = hash->v - 1; k > 0; k--) { carry = ptr[k] + b[k] + carry; ptr[k] = carry; carry >>= 8; } ptr[k] += b[k] + carry; explicit_bzero(&carry, sizeof(carry)); } explicit_bzero(b, sizeof(b)); } explicit_bzero(di, sizeof(di)); l_checksum_free(h); return key; } /* RFC7292 Appendix A */ static const struct cert_pkcs12_hash pkcs12_sha1_hash = { .alg = L_CHECKSUM_SHA1, .len = 20, .u = 20, .v = 64, .oid = { 5, { 0x2b, 0x0e, 0x03, 0x02, 0x1a } }, }; /* RFC8018 Section A.2 */ static struct asn1_oid pkcs5_pbkdf2_oid = { 9, { 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x05, 0x0c } }; /* RFC8018 Section A.4 */ static struct asn1_oid pkcs5_pbes2_oid = { 9, { 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x05, 0x0d } }; /* RFC8018 Section A.3 */ static const struct pkcs5_pbes1_encryption_oid { enum l_checksum_type checksum_type; enum l_cipher_type cipher_type; struct asn1_oid oid; } pkcs5_pbes1_encryption_oids[] = { { /* pbeWithMD5AndDES-CBC */ L_CHECKSUM_MD5, L_CIPHER_DES_CBC, { 9, { 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x05, 0x03 } }, }, { /* pbeWithSHA1AndDES-CBC */ L_CHECKSUM_SHA1, L_CIPHER_DES_CBC, { 9, { 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x05, 0x0a } }, }, /* MD2- and RC2-based schemes 1, 4, 6 and 11 not supported */ }; /* RFC7292 Appendix C */ static const struct pkcs12_encryption_oid { enum l_cipher_type cipher_type; unsigned int key_length; unsigned int iv_length; bool copy_k1; /* Expand the 2-Key 3DES key for 3-Key 3DES */ bool is_block; struct asn1_oid oid; } pkcs12_encryption_oids[] = { { /* pbeWithSHAAnd128BitRC4 */ .cipher_type = L_CIPHER_ARC4, .key_length = 16, .oid = { 10, { 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x0c, 0x01, 0x01, } } }, { /* pbeWithSHAAnd40BitRC4 */ .cipher_type = L_CIPHER_ARC4, .key_length = 5, .oid = { 10, { 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x0c, 0x01, 0x02, } } }, { /* pbeWithSHAAnd3-KeyTripleDES-CBC */ .cipher_type = L_CIPHER_DES3_EDE_CBC, .key_length = 24, .iv_length = 8, .is_block = true, .oid = { 10, { 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x0c, 0x01, 0x03, } } }, { /* pbeWithSHAAnd2-KeyTripleDES-CBC */ .cipher_type = L_CIPHER_DES3_EDE_CBC, .key_length = 16, .iv_length = 8, .copy_k1 = true, .is_block = true, .oid = { 10, { 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x0c, 0x01, 0x04, } } }, { /* pbeWithSHAAnd128BitRC2-CBC */ .cipher_type = L_CIPHER_RC2_CBC, .key_length = 16, .iv_length = 8, .is_block = true, .oid = { 10, { 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x0c, 0x01, 0x05, } } }, { /* pbeWithSHAAnd40BitRC2-CBC */ .cipher_type = L_CIPHER_RC2_CBC, .key_length = 5, .iv_length = 8, .is_block = true, .oid = { 10, { 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x0c, 0x01, 0x06, } } }, }; static const struct pkcs5_digest_alg_oid { enum l_checksum_type type; struct asn1_oid oid; } pkcs5_digest_alg_oids[] = { { /* hmacWithSHA1 */ L_CHECKSUM_SHA1, { 8, { 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x02, 0x07 } }, }, { /* hmacWithSHA224 */ L_CHECKSUM_SHA224, { 8, { 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x02, 0x08 } }, }, { /* hmacWithSHA256 */ L_CHECKSUM_SHA256, { 8, { 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x02, 0x09 } }, }, { /* hmacWithSHA384 */ L_CHECKSUM_SHA384, { 8, { 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x02, 0x0a } }, }, { /* hmacWithSHA512 */ L_CHECKSUM_SHA512, { 8, { 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x02, 0x0b } }, }, /* hmacWithSHA512-224 and hmacWithSHA512-256 not supported */ }; static const struct pkcs5_enc_alg_oid { enum l_cipher_type cipher_type; uint8_t key_size, iv_size; struct asn1_oid oid; } pkcs5_enc_alg_oids[] = { { /* desCBC */ L_CIPHER_DES_CBC, 8, 8, { 5, { 0x2b, 0x0e, 0x03, 0x02, 0x07 } }, }, { /* des-EDE3-CBC */ L_CIPHER_DES3_EDE_CBC, 24, 8, { 8, { 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x03, 0x07 } }, }, /* RC2/RC5-based schemes 2 and 9 not supported */ { /* aes128-CBC-PAD */ L_CIPHER_AES_CBC, 16, 16, { 9, { 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x01, 0x02 } }, }, { /* aes192-CBC-PAD */ L_CIPHER_AES_CBC, 24, 16, { 9, { 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x01, 0x16 } }, }, { /* aes256-CBC-PAD */ L_CIPHER_AES_CBC, 32, 16, { 9, { 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x01, 0x2a } }, }, }; static struct l_cipher *cipher_from_pkcs5_pbes2_params( const uint8_t *pbes2_params, size_t pbes2_params_len, const char *password) { uint8_t tag; const uint8_t *kdf_sequence, *enc_sequence, *oid, *params, *salt, *iter_count_buf, *key_len_buf, *prf_sequence; size_t kdf_len, enc_len, params_len, salt_len, key_len, tmp_len; unsigned int i, iter_count, pos; enum l_checksum_type prf_alg = L_CHECKSUM_NONE; const struct pkcs5_enc_alg_oid *enc_scheme = NULL; uint8_t derived_key[64]; struct l_cipher *cipher; /* RFC8018 section A.4 */ kdf_sequence = asn1_der_find_elem(pbes2_params, pbes2_params_len, 0, &tag, &kdf_len); if (!kdf_sequence || tag != ASN1_ID_SEQUENCE) return NULL; enc_sequence = asn1_der_find_elem(pbes2_params, pbes2_params_len, 1, &tag, &enc_len); if (!enc_sequence || tag != ASN1_ID_SEQUENCE) return NULL; if (asn1_der_find_elem(pbes2_params, pbes2_params_len, 2, &tag, &tmp_len)) return NULL; /* RFC8018 section A.2 */ oid = asn1_der_find_elem(kdf_sequence, kdf_len, 0, &tag, &tmp_len); if (!oid || tag != ASN1_ID_OID) return NULL; if (!asn1_oid_eq(&pkcs5_pbkdf2_oid, tmp_len, oid)) return NULL; params = asn1_der_find_elem(kdf_sequence, kdf_len, 1, &tag, ¶ms_len); if (!params || tag != ASN1_ID_SEQUENCE) return NULL; if (asn1_der_find_elem(kdf_sequence, kdf_len, 2, &tag, &tmp_len)) return NULL; salt = asn1_der_find_elem(params, params_len, 0, &tag, &salt_len); if (!salt || tag != ASN1_ID_OCTET_STRING || salt_len < 1 || salt_len > 512) return NULL; iter_count_buf = asn1_der_find_elem(params, params_len, 1, &tag, &tmp_len); if (!iter_count_buf || tag != ASN1_ID_INTEGER || tmp_len < 1 || tmp_len > 4) return NULL; iter_count = 0; while (tmp_len--) iter_count = (iter_count << 8) | *iter_count_buf++; pos = 2; key_len_buf = asn1_der_find_elem(params, params_len, pos, &tag, &tmp_len); if (key_len_buf && tag == ASN1_ID_INTEGER) { if (tmp_len != 1) return NULL; pos++; key_len = 0; while (tmp_len--) key_len = (key_len << 8) | *key_len_buf++; } else key_len = 0; prf_sequence = asn1_der_find_elem(params, params_len, pos, &tag, &tmp_len); if (prf_sequence && tag == ASN1_ID_SEQUENCE) { pos++; oid = asn1_der_find_elem(prf_sequence, tmp_len, 0, &tag, &tmp_len); if (!oid || tag != ASN1_ID_OID) return NULL; for (i = 0; i < L_ARRAY_SIZE(pkcs5_digest_alg_oids); i++) if (asn1_oid_eq(&pkcs5_digest_alg_oids[i].oid, tmp_len, oid)) prf_alg = pkcs5_digest_alg_oids[i].type; if (prf_alg == L_CHECKSUM_NONE) return NULL; } else prf_alg = L_CHECKSUM_SHA1; oid = asn1_der_find_elem(enc_sequence, enc_len, 0, &tag, &tmp_len); if (!oid || tag != ASN1_ID_OID) return NULL; for (i = 0; i < L_ARRAY_SIZE(pkcs5_enc_alg_oids); i++) { if (asn1_oid_eq(&pkcs5_enc_alg_oids[i].oid, tmp_len, oid)) { enc_scheme = &pkcs5_enc_alg_oids[i]; break; } } if (!enc_scheme) return NULL; params = asn1_der_find_elem(enc_sequence, enc_len, 1, &tag, ¶ms_len); if (!params) return NULL; /* RFC8018 section B.2 */ /* * Since we don't support the RC2/RC5 PBES2 ciphers, our parameters * only have an obligatory OCTET STRING IV parameter and a fixed key * length. */ if (tag != ASN1_ID_OCTET_STRING || params_len != enc_scheme->iv_size) return NULL; if (key_len && enc_scheme->key_size != key_len) return NULL; key_len = enc_scheme->key_size; if (asn1_der_find_elem(enc_sequence, enc_len, 2, &tag, &tmp_len)) return NULL; /* RFC8018 section 6.2 */ if (!l_cert_pkcs5_pbkdf2(prf_alg, password, salt, salt_len, iter_count, derived_key, key_len)) return NULL; cipher = l_cipher_new(enc_scheme->cipher_type, derived_key, key_len); if (cipher && !l_cipher_set_iv(cipher, params, enc_scheme->iv_size)) { l_cipher_free(cipher); cipher = NULL; } explicit_bzero(derived_key, 16); return cipher; } static struct l_cipher *cipher_from_pkcs12_alg_id( const struct pkcs12_encryption_oid *scheme, const uint8_t *params, size_t params_len, const char *password, bool *out_is_block) { uint8_t tag; const uint8_t *salt; const uint8_t *iterations_data; size_t salt_len; size_t iterations_len; unsigned int iterations; uint8_t *key; size_t key_len; struct l_cipher *cipher; /* Same parameters as in PKCS#5 */ salt = asn1_der_find_elem(params, params_len, 0, &tag, &salt_len); if (!salt || tag != ASN1_ID_OCTET_STRING) return NULL; iterations_data = asn1_der_find_elem(params, params_len, 1, &tag, &iterations_len); if (!iterations_data || tag != ASN1_ID_INTEGER || iterations_len < 1 || iterations_len > 4) return NULL; for (iterations = 0; iterations_len; iterations_len--) iterations = (iterations << 8) | *iterations_data++; if (iterations < 1 || iterations > 8192) return NULL; if (iterations_data != params + params_len) return NULL; key_len = scheme->key_length; key = cert_pkcs12_pbkdf(password, &pkcs12_sha1_hash, salt, salt_len, iterations, 1, key_len); if (!key) return NULL; if (scheme->copy_k1) { /* * 2-Key 3DES is like L_CIPHER_DES3_EDE_CBC except the last * of the 3 8-byte keys is not generated using a KDF and * instead is a copy of the first key. In other words * the first half of the 16-byte key material is appended * at the end to produce the 24 bytes for DES3_EDE_CBC. */ uint8_t *key2 = l_malloc(24); memcpy(key2, key, 16); memcpy(key2 + 16, key, 8); explicit_bzero(key, key_len); l_free(key); key = key2; key_len = 24; } cipher = l_cipher_new(scheme->cipher_type, key, key_len); explicit_bzero(key, key_len); l_free(key); if (!cipher) return NULL; if (scheme->iv_length) { uint8_t *iv = cert_pkcs12_pbkdf(password, &pkcs12_sha1_hash, salt, salt_len, iterations, 2, scheme->iv_length); if (!iv || !l_cipher_set_iv(cipher, iv, scheme->iv_length)) { l_cipher_free(cipher); cipher = NULL; } if (iv) explicit_bzero(iv, scheme->iv_length); l_free(iv); } if (out_is_block) *out_is_block = scheme->is_block; return cipher; } struct l_cipher *cert_cipher_from_pkcs_alg_id(const uint8_t *id_asn1, size_t id_asn1_len, const char *password, bool *out_is_block) { uint8_t tag; const uint8_t *oid, *params, *salt, *iter_count_buf; size_t oid_len, params_len, tmp_len; unsigned int i, iter_count; const struct pkcs5_pbes1_encryption_oid *pbes1_scheme = NULL; uint8_t derived_key[16]; struct l_cipher *cipher; oid = asn1_der_find_elem(id_asn1, id_asn1_len, 0, &tag, &oid_len); if (!oid || tag != ASN1_ID_OID) return NULL; params = asn1_der_find_elem(id_asn1, id_asn1_len, 1, &tag, ¶ms_len); if (!params || tag != ASN1_ID_SEQUENCE) return NULL; if (asn1_der_find_elem(id_asn1, id_asn1_len, 2, &tag, &tmp_len)) return NULL; if (asn1_oid_eq(&pkcs5_pbes2_oid, oid_len, oid)) { if (out_is_block) *out_is_block = true; return cipher_from_pkcs5_pbes2_params(params, params_len, password); } /* RFC8018 section A.3 */ for (i = 0; i < L_ARRAY_SIZE(pkcs5_pbes1_encryption_oids); i++) { if (asn1_oid_eq(&pkcs5_pbes1_encryption_oids[i].oid, oid_len, oid)) { pbes1_scheme = &pkcs5_pbes1_encryption_oids[i]; break; } } /* Check if this is a PKCS#12 OID */ if (!pbes1_scheme) { for (i = 0; i < L_ARRAY_SIZE(pkcs12_encryption_oids); i++) if (asn1_oid_eq(&pkcs12_encryption_oids[i].oid, oid_len, oid)) return cipher_from_pkcs12_alg_id( &pkcs12_encryption_oids[i], params, params_len, password, out_is_block); return NULL; } salt = asn1_der_find_elem(params, params_len, 0, &tag, &tmp_len); if (!salt || tag != ASN1_ID_OCTET_STRING || tmp_len != 8) return NULL; iter_count_buf = asn1_der_find_elem(params, params_len, 1, &tag, &tmp_len); if (!iter_count_buf || tag != ASN1_ID_INTEGER || tmp_len < 1 || tmp_len > 4) return NULL; iter_count = 0; while (tmp_len--) iter_count = (iter_count << 8) | *iter_count_buf++; if (asn1_der_find_elem(params, params_len, 2, &tag, &tmp_len)) return NULL; /* RFC8018 section 6.1 */ if (!l_cert_pkcs5_pbkdf1(pbes1_scheme->checksum_type, password, salt, 8, iter_count, derived_key, 16)) return NULL; cipher = l_cipher_new(pbes1_scheme->cipher_type, derived_key + 0, 8); if (cipher && !l_cipher_set_iv(cipher, derived_key + 8, 8)) { l_cipher_free(cipher); cipher = NULL; } explicit_bzero(derived_key, 16); if (out_is_block) *out_is_block = true; return cipher; } bluez-5.82/ell/PaxHeaders/missing.h0000644000000000000000000000005014504767710014257 xustar0020 atime=1743575466 20 ctime=1743591276 bluez-5.82/ell/missing.h0000644000000000000000000000276214504767710013747 0ustar00rootroot/* * Embedded Linux library * Copyright (C) 2011-2014 Intel Corporation * * SPDX-License-Identifier: LGPL-2.1-or-later */ #include #include #ifndef __NR_getrandom # if defined __x86_64__ # define __NR_getrandom 318 # elif defined(__i386__) # define __NR_getrandom 355 # elif defined(__arm__) # define __NR_getrandom 384 # elif defined(__aarch64__) # define __NR_getrandom 278 # elif defined(__ia64__) # define __NR_getrandom 1339 # elif defined(__m68k__) # define __NR_getrandom 352 # elif defined(__s390x__) # define __NR_getrandom 349 # elif defined(__powerpc__) # define __NR_getrandom 359 # elif defined _MIPS_SIM # if _MIPS_SIM == _MIPS_SIM_ABI32 # define __NR_getrandom 4353 # endif # if _MIPS_SIM == _MIPS_SIM_NABI32 # define __NR_getrandom 6317 # endif # if _MIPS_SIM == _MIPS_SIM_ABI64 # define __NR_getrandom 5313 # endif # else # warning "__NR_getrandom unknown for your architecture" # define __NR_getrandom 0xffffffff # endif #endif #ifndef HAVE_EXPLICIT_BZERO static inline void explicit_bzero(void *s, size_t n) { memset(s, 0, n); __asm__ __volatile__ ("" : : "r"(s) : "memory"); } #endif #ifndef SO_BINDTOIFINDEX #define SO_BINDTOIFINDEX 62 #endif #ifndef HAVE_RAWMEMCHR static inline void *rawmemchr(const void *s, int c) { _Pragma("GCC diagnostic push") _Pragma("GCC diagnostic ignored \"-Wstringop-overflow=\"") return memchr(s, c, PTRDIFF_MAX); _Pragma("GCC diagnostic pop") } #endif bluez-5.82/ell/PaxHeaders/log.c0000644000000000000000000000005014504767710013362 xustar0020 atime=1743575463 20 ctime=1743591276 bluez-5.82/ell/log.c0000644000000000000000000002045414504767710013050 0ustar00rootroot/* * Embedded Linux library * Copyright (C) 2011-2014 Intel Corporation * * SPDX-License-Identifier: LGPL-2.1-or-later */ #ifdef HAVE_CONFIG_H #include #endif #define _GNU_SOURCE #include #include #include #include #include #include #include #include #include "queue.h" #include "log.h" #include "private.h" struct debug_section { struct l_debug_desc *start; struct l_debug_desc *end; }; struct l_queue *debug_sections; /** * SECTION:log * @short_description: Logging framework * * Logging framework */ /** * l_debug_desc: * * Debug descriptor. */ static void log_null(int priority, const char *file, const char *line, const char *func, const char *format, va_list ap) { } static l_log_func_t log_func = log_null; static const char *log_ident = ""; static int log_fd = -1; static unsigned long log_pid; static inline void close_log(void) { if (log_fd > 0) { close(log_fd); log_fd = -1; } } static int open_log(const char *path) { struct sockaddr_un addr; log_fd = socket(PF_UNIX, SOCK_DGRAM | SOCK_CLOEXEC, 0); if (log_fd < 0) return -1; memset(&addr, 0, sizeof(addr)); addr.sun_family = AF_UNIX; strncpy(addr.sun_path, path, sizeof(addr.sun_path) - 1); if (connect(log_fd, (struct sockaddr *) &addr, sizeof(addr)) < 0) { close_log(); return -1; } return 0; } /** * l_log_set_ident: * @ident: string identifier * * Sets the log identifier string. **/ LIB_EXPORT void l_log_set_ident(const char *ident) { log_ident = ident; } /** * l_log_set_handler: * @function: log handler function * * Sets the log handler function. **/ LIB_EXPORT void l_log_set_handler(l_log_func_t function) { L_DEBUG_SYMBOL(__debug_intern, ""); close_log(); if (!function) { log_func = log_null; return; } log_func = function; } /** * l_log_set_null: * * Disable logging. **/ LIB_EXPORT void l_log_set_null(void) { close_log(); log_func = log_null; } __attribute__((format(printf, 5, 0))) static void log_stderr(int priority, const char *file, const char *line, const char *func, const char *format, va_list ap) { vfprintf(stderr, format, ap); } /** * l_log_set_stderr: * * Enable logging to stderr. **/ LIB_EXPORT void l_log_set_stderr(void) { close_log(); log_func = log_stderr; } __attribute__((format(printf, 5, 0))) static void log_syslog(int priority, const char *file, const char *line, const char *func, const char *format, va_list ap) { struct msghdr msg; struct iovec iov[2]; char hdr[64], *str; int hdr_len, str_len; str_len = vasprintf(&str, format, ap); if (str_len < 0) return; hdr_len = snprintf(hdr, sizeof(hdr), "<%i>%s[%lu]: ", priority, log_ident, (unsigned long) log_pid); iov[0].iov_base = hdr; iov[0].iov_len = hdr_len; iov[1].iov_base = str; iov[1].iov_len = str_len; memset(&msg, 0, sizeof(msg)); msg.msg_iov = iov; msg.msg_iovlen = 2; sendmsg(log_fd, &msg, 0); free(str); } /** * l_log_set_syslog: * * Enable logging to syslog. **/ LIB_EXPORT void l_log_set_syslog(void) { close_log(); if (open_log("/dev/log") < 0) { log_func = log_null; return; } log_pid = getpid(); log_func = log_syslog; } __attribute__((format(printf, 5, 0))) static void log_journal(int priority, const char *file, const char *line, const char *func, const char *format, va_list ap) { struct msghdr msg; struct iovec iov[12]; char prio[16], *str; int prio_len, str_len; str_len = vasprintf(&str, format, ap); if (str_len < 0) return; prio_len = snprintf(prio, sizeof(prio), "PRIORITY=%u\n", priority); iov[0].iov_base = "MESSAGE="; iov[0].iov_len = 8; iov[1].iov_base = str; iov[1].iov_len = str_len; iov[2].iov_base = prio; iov[2].iov_len = prio_len; iov[3].iov_base = "CODE_FILE="; iov[3].iov_len = 10; iov[4].iov_base = (char *) file; iov[4].iov_len = strlen(file); iov[5].iov_base = "\n"; iov[5].iov_len = 1; iov[6].iov_base = "CODE_LINE="; iov[6].iov_len = 10; iov[7].iov_base = (char *) line; iov[7].iov_len = strlen(line); iov[8].iov_base = "\n"; iov[8].iov_len = 1; iov[9].iov_base = "CODE_FUNC="; iov[9].iov_len = 10; iov[10].iov_base = (char *) func; iov[10].iov_len = strlen(func); iov[11].iov_base = "\n"; iov[11].iov_len = 1; memset(&msg, 0, sizeof(msg)); msg.msg_iov = iov; msg.msg_iovlen = 12; sendmsg(log_fd, &msg, 0); free(str); } /** * l_log_set_journal: * * Enable logging to journal. **/ LIB_EXPORT void l_log_set_journal(void) { close_log(); if (open_log("/run/systemd/journal/socket") < 0) { log_func = log_null; return; } log_pid = getpid(); log_func = log_journal; } /** * l_log_with_location: * @priority: priority level * @file: source file * @line: source line * @func: source function * @format: format string * @...: format arguments * * Log information. **/ LIB_EXPORT void l_log_with_location(int priority, const char *file, const char *line, const char *func, const char *format, ...) { va_list ap; va_start(ap, format); log_func(priority, file, line, func, format, ap); va_end(ap); } /** * l_error: * @format: format string * @...: format arguments * **/ /** * l_warn: * @format: format string * @...: format arguments * **/ /** * l_info: * @format: format string * @...: format arguments * **/ /** * l_debug: * @format: format string * @...: format arguments **/ static const char *debug_pattern; static void debug_enable(struct l_debug_desc *start, struct l_debug_desc *stop) { struct l_debug_desc *desc; char *pattern_copy; if (!debug_pattern) return; pattern_copy = strdupa(debug_pattern); while (pattern_copy) { char *str = strsep(&pattern_copy, ":,"); if (!str) break; for (desc = start; desc < stop; desc++) { if (!fnmatch(str, desc->file, 0)) desc->flags |= L_DEBUG_FLAG_PRINT; if (!fnmatch(str, desc->func, 0)) desc->flags |= L_DEBUG_FLAG_PRINT; } } } static void debug_disable(struct l_debug_desc *start, struct l_debug_desc *stop) { struct l_debug_desc *desc; for (desc = start; desc < stop; desc++) desc->flags &= ~L_DEBUG_FLAG_PRINT; } /** * l_debug_add_section: * @start: start of the debug section * @stop: stop of the debug section * * Add information about a debug section. This is used by shared libraries * to tell ell about their debug section start & stopping points. This is used * to make l_debug statements work across all shared libraries that might be * linked into the executable */ LIB_EXPORT void l_debug_add_section(struct l_debug_desc *start, struct l_debug_desc *end) { const struct l_queue_entry *entry; struct debug_section *new_section; if (!debug_sections) { debug_sections = l_queue_new(); goto add; } for (entry = l_queue_get_entries(debug_sections); entry; entry = entry->next) { const struct debug_section *section = entry->data; if (section->start == start && section->end == end) return; } add: new_section = l_new(struct debug_section, 1); new_section->start = start; new_section->end = end; l_queue_push_head(debug_sections, new_section); } /** * l_debug_enable_full: * @pattern: debug pattern * @start: start of the debug section * @stop: end of the debug section * * Enable debug sections based on @pattern. **/ LIB_EXPORT void l_debug_enable_full(const char *pattern, struct l_debug_desc *start, struct l_debug_desc *end) { const struct l_queue_entry *entry; if (!pattern) return; debug_pattern = pattern; l_debug_add_section(start, end); for (entry = l_queue_get_entries(debug_sections); entry; entry = entry->next) { const struct debug_section *section = entry->data; debug_enable(section->start, section->end); } } /** * l_debug_disable: * * Disable all debug sections. **/ LIB_EXPORT void l_debug_disable(void) { const struct l_queue_entry *entry; for (entry = l_queue_get_entries(debug_sections); entry; entry = entry->next) { const struct debug_section *section = entry->data; debug_disable(section->start, section->end); } debug_pattern = NULL; } __attribute__((constructor)) static void register_debug_section() { extern struct l_debug_desc __start___ell_debug[]; extern struct l_debug_desc __stop___ell_debug[]; l_debug_add_section(__start___ell_debug, __stop___ell_debug); } __attribute__((destructor(65535))) static void free_debug_sections() { l_queue_destroy(debug_sections, l_free); } bluez-5.82/ell/PaxHeaders/dbus-name-cache.c0000644000000000000000000000005014504767710015515 xustar0020 atime=1743575516 20 ctime=1743591277 bluez-5.82/ell/dbus-name-cache.c0000644000000000000000000001365414504767710015207 0ustar00rootroot/* * Embedded Linux library * Copyright (C) 2016 Intel Corporation * * SPDX-License-Identifier: LGPL-2.1-or-later */ #ifdef HAVE_CONFIG_H #include #endif #define _GNU_SOURCE #include #include #include "util.h" #include "hashmap.h" #include "idle.h" #include "dbus.h" #include "dbus-private.h" struct _dbus_name_cache { struct l_dbus *bus; struct l_hashmap *names; const struct _dbus_name_ops *driver; unsigned int last_watch_id; struct l_idle *watch_remove_work; }; struct service_watch { l_dbus_watch_func_t connect_func; l_dbus_watch_func_t disconnect_func; l_dbus_destroy_func_t destroy; void *user_data; unsigned int id; struct service_watch *next; }; struct name_cache_entry { int ref_count; char *unique_name; struct service_watch *watches; }; struct _dbus_name_cache *_dbus_name_cache_new(struct l_dbus *bus, const struct _dbus_name_ops *driver) { struct _dbus_name_cache *cache; cache = l_new(struct _dbus_name_cache, 1); cache->bus = bus; cache->driver = driver; return cache; } static void service_watch_destroy(void *data) { struct service_watch *watch = data; if (watch->destroy) watch->destroy(watch->user_data); l_free(watch); } static void name_cache_entry_destroy(void *data) { struct name_cache_entry *entry = data; struct service_watch *watch; while (entry->watches) { watch = entry->watches; entry->watches = watch->next; service_watch_destroy(watch); } l_free(entry->unique_name); l_free(entry); } void _dbus_name_cache_free(struct _dbus_name_cache *cache) { if (!cache) return; if (cache->watch_remove_work) l_idle_remove(cache->watch_remove_work); l_hashmap_destroy(cache->names, name_cache_entry_destroy); l_free(cache); } bool _dbus_name_cache_add(struct _dbus_name_cache *cache, const char *name) { struct name_cache_entry *entry; if (!_dbus_valid_bus_name(name)) return false; if (!cache->names) cache->names = l_hashmap_string_new(); entry = l_hashmap_lookup(cache->names, name); if (!entry) { entry = l_new(struct name_cache_entry, 1); l_hashmap_insert(cache->names, name, entry); cache->driver->get_name_owner(cache->bus, name); } entry->ref_count++; return true; } bool _dbus_name_cache_remove(struct _dbus_name_cache *cache, const char *name) { struct name_cache_entry *entry; entry = l_hashmap_lookup(cache->names, name); if (!entry) return false; if (--entry->ref_count) return true; l_hashmap_remove(cache->names, name); name_cache_entry_destroy(entry); return true; } const char *_dbus_name_cache_lookup(struct _dbus_name_cache *cache, const char *name) { struct name_cache_entry *entry; entry = l_hashmap_lookup(cache->names, name); if (!entry) return NULL; return entry->unique_name; } void _dbus_name_cache_notify(struct _dbus_name_cache *cache, const char *name, const char *owner) { struct name_cache_entry *entry; struct service_watch *watch; bool prev_connected, connected; if (!cache) return; entry = l_hashmap_lookup(cache->names, name); if (!entry) return; prev_connected = !!entry->unique_name; connected = owner && *owner != '\0'; l_free(entry->unique_name); entry->unique_name = connected ? l_strdup(owner) : NULL; /* * This check also means we notify all watchers who have a connected * callback when we first learn that the service is in fact connected. */ if (connected == prev_connected) return; for (watch = entry->watches; watch; watch = watch->next) if (connected && watch->connect_func) watch->connect_func(cache->bus, watch->user_data); else if (!connected && watch->disconnect_func) watch->disconnect_func(cache->bus, watch->user_data); } unsigned int _dbus_name_cache_add_watch(struct _dbus_name_cache *cache, const char *name, l_dbus_watch_func_t connect_func, l_dbus_watch_func_t disconnect_func, void *user_data, l_dbus_destroy_func_t destroy) { struct name_cache_entry *entry; struct service_watch *watch; if (!_dbus_name_cache_add(cache, name)) return 0; watch = l_new(struct service_watch, 1); watch->id = ++cache->last_watch_id; watch->connect_func = connect_func; watch->disconnect_func = disconnect_func; watch->user_data = user_data; watch->destroy = destroy; entry = l_hashmap_lookup(cache->names, name); watch->next = entry->watches; entry->watches = watch; if (entry->unique_name && connect_func) watch->connect_func(cache->bus, watch->user_data); return watch->id; } static bool service_watch_remove(const void *key, void *value, void *user_data) { struct name_cache_entry *entry = value; struct service_watch **watch, *tmp; for (watch = &entry->watches; *watch;) { if ((*watch)->id) { watch = &(*watch)->next; continue; } tmp = *watch; *watch = tmp->next; service_watch_destroy(tmp); entry->ref_count--; } if (entry->ref_count) return false; name_cache_entry_destroy(entry); return true; } static void service_watch_remove_all(struct l_idle *idle, void *user_data) { struct _dbus_name_cache *cache = user_data; l_idle_remove(cache->watch_remove_work); cache->watch_remove_work = NULL; l_hashmap_foreach_remove(cache->names, service_watch_remove, cache); } static void service_watch_mark(const void *key, void *value, void *user_data) { struct name_cache_entry *entry = value; struct service_watch *watch; unsigned int *id = user_data; if (!*id) return; for (watch = entry->watches; watch; watch = watch->next) if (watch->id == *id) { watch->id = 0; watch->connect_func = NULL; watch->disconnect_func = NULL; if (watch->destroy) { watch->destroy(watch->user_data); watch->destroy = NULL; } *id = 0; break; } } bool _dbus_name_cache_remove_watch(struct _dbus_name_cache *cache, unsigned int id) { l_hashmap_foreach(cache->names, service_watch_mark, &id); if (id) return false; if (!cache->watch_remove_work) cache->watch_remove_work = l_idle_create( service_watch_remove_all, cache, NULL); return true; } bluez-5.82/PaxHeaders/doc0000644000000000000000000000005014773213573012352 xustar0020 atime=1743591291 20 ctime=1743591291 bluez-5.82/doc/0000755000000000000000000000000014773213573012110 5ustar00rootrootbluez-5.82/doc/PaxHeaders/org.bluez.GattManager.50000644000000000000000000000005014773213460016610 xustar0020 atime=1743591216 20 ctime=1743591291 bluez-5.82/doc/org.bluez.GattManager.50000644000000000000000000001115414773213460016273 0ustar00rootroot.\" Man page generated from reStructuredText. . . .nr rst2man-indent-level 0 . .de1 rstReportMargin \\$1 \\n[an-margin] level \\n[rst2man-indent-level] level margin: \\n[rst2man-indent\\n[rst2man-indent-level]] - \\n[rst2man-indent0] \\n[rst2man-indent1] \\n[rst2man-indent2] .. .de1 INDENT .\" .rstReportMargin pre: . RS \\$1 . nr rst2man-indent\\n[rst2man-indent-level] \\n[an-margin] . nr rst2man-indent-level +1 .\" .rstReportMargin post: .. .de UNINDENT . RE .\" indent \\n[an-margin] .\" old: \\n[rst2man-indent\\n[rst2man-indent-level]] .nr rst2man-indent-level -1 .\" new: \\n[rst2man-indent\\n[rst2man-indent-level]] .in \\n[rst2man-indent\\n[rst2man-indent-level]]u .. .TH "ORG.BLUEZ.GATTMANAGER" "5" "October 2023" "BlueZ" "Linux System Administration" .SH NAME org.bluez.GattManager \- BlueZ D-Bus GattManager API documentation .SH DESCRIPTION .sp GATT Manager allows external applications to register GATT services and profiles. .sp Registering a profile allows applications to subscribe to \fIremote/client\fP services. .sp Registering a service allows applications to publish a \fIlocal/server\fP GATT service, which then becomes available to remote devices. A GATT service is represented by a D\-Bus object hierarchy where the root node corresponds to a service and the child nodes represent characteristics and descriptors that belong to that service. Each node must implement one of \fBorg.bluez.GattService(5)\fP, \fBorg.bluez.GattCharacteristic(5)\fP or \fBorg.bluez.GattDescriptor(5)\fP interfaces, based on the attribute it represents. Each node must also implement the standard D\-Bus Properties interface to expose their properties. These objects collectively represent a GATT service definition. .sp To make service registration simple, \fBbluetoothd(8)\fP requires that all objects that belong to a GATT service be grouped under a D\-Bus Object Manager that solely manages the objects of that service. Hence, the standard DBus.ObjectManager interface must be available on the root service path. An example application hierarchy containing two separate GATT services may look like this: .INDENT 0.0 .INDENT 3.5 .sp .EX \-> /com/example | \- org.freedesktop.DBus.ObjectManager | \-> /com/example/service0 | | \- org.freedesktop.DBus.Properties | | \- org.bluez.GattService1 | | | \-> /com/example/service0/char0 | | \- org.freedesktop.DBus.Properties | | \- org.bluez.GattCharacteristic1 | | | \-> /com/example/service0/char1 | | \- org.freedesktop.DBus.Properties | | \- org.bluez.GattCharacteristic1 | | | \-> /com/example/service0/char1/desc0 | \- org.freedesktop.DBus.Properties | \- org.bluez.GattDescriptor1 | \-> /com/example/service1 | \- org.freedesktop.DBus.Properties | \- org.bluez.GattService1 | \-> /com/example/service1/char0 \- org.freedesktop.DBus.Properties \- org.bluez.GattCharacteristic1 .EE .UNINDENT .UNINDENT .sp When a service is registered, \fBbluetoothd(8)\fP will automatically obtain information about all objects using the service\(aqs Object Manager. Once a service has been registered, the objects of a service should not be removed. If \fBbluetoothd(8)\fP receives an InterfacesRemoved signal from a service\(aqs Object Manager, it will immediately unregister the service. Similarly, if the application disconnects from the bus, all of its registered services will be automatically unregistered. InterfacesAdded signals will be ignored. .SH INTERFACE .INDENT 0.0 .TP .B Service org.bluez .TP .B Interface org.bluez.GattManager1 .TP .B Object path [variable prefix]/{hci0,hci1,...} .UNINDENT .SS Methods .SS void RegisterApplication(object application, dict options) .INDENT 0.0 .INDENT 3.5 Registers a local GATT services hierarchy as described above (GATT Server) and/or GATT profiles (GATT Client). .sp The application object path together with the D\-Bus system bus connection ID define the identification of the application registering a GATT based service (\fBorg.bluez.GattService(5)\fP) and/or profile (\fBorg.bluez.GattProfile(5)\fP). .sp Possible errors: .INDENT 0.0 .TP .B org.bluez.Error.InvalidArguments .TP .B org.bluez.Error.AlreadyExists .UNINDENT .UNINDENT .UNINDENT .SS void UnregisterApplication(object application) .INDENT 0.0 .INDENT 3.5 This unregisters the services and/or profiles that has been previously registered using \fBRegisterApplication()\fP\&. The object path parameter must match the same value that has been used on registration. .sp Possible errors: .INDENT 0.0 .TP .B org.bluez.Error.InvalidArguments .TP .B org.bluez.Error.DoesNotExist .UNINDENT .UNINDENT .UNINDENT .\" Generated by docutils manpage writer. . bluez-5.82/doc/PaxHeaders/pts-opp.txt0000644000000000000000000000005012572170650014563 xustar0020 atime=1743516877 20 ctime=1743591290 bluez-5.82/doc/pts-opp.txt0000644000000000000000000000631712572170650014253 0ustar00rootrootPTS test results for OPP PTS version: 6.2.0 Tested: 26-Aug-2015 Results: PASS test passed FAIL test failed INC test is inconclusive N/A test is disabled due to PICS setup NONE test result is none ------------------------------------------------------------------------------- Test Name Result Notes ------------------------------------------------------------------------------- TC_CLIENT_BC_BV_02_I PASS TC_CLIENT_BC_BV_04_I PASS TC_CLIENT_BCE_BV_01_I PASS TC_CLIENT_BCE_BV_03_I N/A TC_CLIENT_BCE_BV_04_I PASS TC_CLIENT_BCE_BV_05_I PASS TC_CLIENT_BCE_BV_06_I PASS TC_CLIENT_BCE_BV_07_I PASS TC_CLIENT_BCP_BV_01_I PASS TC_CLIENT_BCP_BV_02_I PASS TC_CLIENT_BCP_BV_03_I N/A TC_CLIENT_BCP_BV_04_I PASS TC_CLIENT_BCP_BV_05_I PASS TC_CLIENT_CON_BV_01_C PASS TC_CLIENT_OPH_BI_01_C PASS TC_CLIENT_OPH_BV_01_I PASS TC_CLIENT_OPH_BV_02_I N/A TC_CLIENT_OPH_BV_03_I PASS TC_CLIENT_OPH_BV_04_I PASS TC_CLIENT_OPH_BV_05_I PASS TC_CLIENT_OPH_BV_07_I N/A TC_CLIENT_OPH_BV_08_I PASS TC_CLIENT_OPH_BV_09_I N/A TC_CLIENT_OPH_BV_10_I N/A TC_CLIENT_OPH_BV_11_I N/A TC_CLIENT_OPH_BV_12_I PASS TC_CLIENT_OPH_BV_13_I N/A TC_CLIENT_OPH_BV_14_I N/A TC_CLIENT_OPH_BV_15_I N/A TC_CLIENT_OPH_BV_16_I PASS TC_CLIENT_OPH_BV_17_I N/A TC_CLIENT_OPH_BV_18_I N/A TC_CLIENT_OPH_BV_19_I PASS Send file other than vCard TC_CLIENT_OPH_BV_20_I PASS TC_CLIENT_OPH_BV_22_I PASS Send file greater than 2 MB TC_CLIENT_OPH_BV_23_I N/A TC_CLIENT_OPH_BV_24_I N/A TC_CLIENT_OPH_BV_25_I N/A TC_CLIENT_OPH_BV_26_I N/A TC_CLIENT_SRM_BV_01_C N/A TC_CLIENT_SRM_BV_03_C N/A TC_CLIENT_SRM_BV_05_C N/A TC_CLIENT_SRM_BV_07_C N/A TC_CLIENT_SRMP_BI_01_C N/A TC_CLIENT_SRMP_BV_01_C N/A TC_CLIENT_SRMP_BV_04_C N/A TC_CLIENT_SRMP_BV_05_C N/A TC_CLIENT_SRMP_BV_06_C N/A TC_SERVER_BC_BV_01_I PASS TC_SERVER_BC_BV_03_I PASS TC_SERVER_BCE_BV_01_I PASS TC_SERVER_BCE_BV_03_I PASS TC_SERVER_BCE_BV_04_I PASS TC_SERVER_BCE_BV_05_I PASS TC_SERVER_BCE_BV_06_I PASS TC_SERVER_BCE_BV_07_I PASS TC_SERVER_BCP_BV_01_I PASS TC_SERVER_BCP_BV_02_I N/A TC_SERVER_BCP_BV_03_I PASS TC_SERVER_BCP_BV_04_I PASS TC_SERVER_BCP_BV_05_I PASS TC_SERVER_CON_BV_02_C PASS TC_SERVER_OPH_BV_01_I PASS TC_SERVER_OPH_BV_02_I PASS TC_SERVER_OPH_BV_03_I PASS TC_SERVER_OPH_BV_04_I PASS TC_SERVER_OPH_BV_05_I PASS TC_SERVER_OPH_BV_07_I N/A TC_SERVER_OPH_BV_08_I N/A TC_SERVER_OPH_BV_09_I PASS TC_SERVER_OPH_BV_10_I PASS TC_SERVER_OPH_BV_11_I N/A TC_SERVER_OPH_BV_12_I N/A TC_SERVER_OPH_BV_13_I PASS TC_SERVER_OPH_BV_14_I PASS TC_SERVER_OPH_BV_15_I N/A TC_SERVER_OPH_BV_16_I N/A TC_SERVER_OPH_BV_17_I PASS TC_SERVER_OPH_BV_18_I PASS TC_SERVER_OPH_BV_19_I PASS TC_SERVER_OPH_BV_21_I N/A TC_SERVER_OPH_BV_22_I PASS TC_SERVER_OPH_BV_23_I PASS TC_SERVER_OPH_BV_24_I N/A TC_SERVER_OPH_BV_25_I N/A TC_SERVER_OPH_BV_26_I N/A TC_SERVER_ROB_BV_01_C PASS TC_SERVER_ROB_BV_02_C PASS TC_SERVER_SRM_BI_02_C N/A TC_SERVER_SRM_BI_03_C PASS TC_SERVER_SRM_BI_05_C N/A TC_SERVER_SRM_BV_04_C N/A TC_SERVER_SRM_BV_08_C N/A TC_SERVER_SRMP_BV_02_C N/A TC_SERVER_SRMP_BV_03_C N/A TC_CLIENT_OPH_BV_27_I N/A TC_CLIENT_OPH_BV_34_I PASS TC_SERVER_OPH_BV_27_I N/A TC_SERVER_OPH_BV_30_I PASS TC_SERVER_OPH_BV_31_I PASS TC_SERVER_OPH_BV_32_I PASS TC_SERVER_OPH_BV_33_I N/A TC_SERVER_OPH_BV_34_I PASS ------------------------------------------------------------------------------- bluez-5.82/doc/PaxHeaders/org.bluez.obex.Transfer.rst0000644000000000000000000000005014536422313017576 xustar0020 atime=1743516814 20 ctime=1743591290 bluez-5.82/doc/org.bluez.obex.Transfer.rst0000644000000000000000000000506014536422313017260 0ustar00rootroot======================= org.bluez.obex.Transfer ======================= ------------------------------------------- BlueZ D-Bus OBEX Transfer API documentation ------------------------------------------- :Version: BlueZ :Date: October 2023 :Manual section: 5 :Manual group: Linux System Administration Interface ========= :Service: org.bluez.obex :Interface: org.bluez.obex.Transfer1 :Object path: [Session object path]/transfer{#} Methods ------- void Cancel() ````````````` Cancels the current transference. Possible errors: :org.bluez.obex.Error.NotAuthorized: :org.bluez.obex.Error.InProgress: :org.bluez.obex.Error.Failed: void Suspend() `````````````` Suspends transference. Possible errors: :org.bluez.obex.Error.NotAuthorized: :org.bluez.obex.Error.NotInProgress: If transfer is still in with **Status** **"queued"**. void Resume() ````````````` Resumes transference previously suspended with use of **Suspend()** method. Possible errors: :org.bluez.obex.Error.NotAuthorized: :org.bluez.obex.Error.NotInProgress: If transfer is still in with **Status** **"queued"**. Properties ---------- string Status [readonly] ```````````````````````` Indicates the current status of the transfer. Possible values: :"queued": :"active": :"suspended": :"complete": :"error": object Session [readonly] ````````````````````````` The object path of the session the transfer belongs to. string Name [readonly, optional] ```````````````````````````````` Name of the object being transferred. Either Name or Type or both will be present. string Type [readonly, optional] ```````````````````````````````` Type of the object transferred being transferred. Either Name or Type or both will be present. uint64 Time [readonly, optional] ```````````````````````````````` Time of the object being transferred if this is provided by the remote party. uint64 Size [readonly, optional] ```````````````````````````````` Size of the object being transferred. If the size is unknown, then this property will not be present. uint64 Transferred [readonly, optional] ``````````````````````````````````````` Number of bytes transferred. For transfers with **Status** set to **"queued"**, this value will not be present. string Filename [readonly, optional] ```````````````````````````````````` Complete name of the file being received or sent. For incoming object push transaction, this will be the proposed default location and name. It can be overwritten by the **AuthorizePush()** in **org.bluez.obex.Agent(5)** and will be then updated accordingly. bluez-5.82/doc/PaxHeaders/org.bluez.GattDescriptor.50000644000000000000000000000005014773213464017360 xustar0020 atime=1743591220 20 ctime=1743591291 bluez-5.82/doc/org.bluez.GattDescriptor.50000644000000000000000000001062114773213464017041 0ustar00rootroot.\" Man page generated from reStructuredText. . . .nr rst2man-indent-level 0 . .de1 rstReportMargin \\$1 \\n[an-margin] level \\n[rst2man-indent-level] level margin: \\n[rst2man-indent\\n[rst2man-indent-level]] - \\n[rst2man-indent0] \\n[rst2man-indent1] \\n[rst2man-indent2] .. .de1 INDENT .\" .rstReportMargin pre: . RS \\$1 . nr rst2man-indent\\n[rst2man-indent-level] \\n[an-margin] . nr rst2man-indent-level +1 .\" .rstReportMargin post: .. .de UNINDENT . RE .\" indent \\n[an-margin] .\" old: \\n[rst2man-indent\\n[rst2man-indent-level]] .nr rst2man-indent-level -1 .\" new: \\n[rst2man-indent\\n[rst2man-indent-level]] .in \\n[rst2man-indent\\n[rst2man-indent-level]]u .. .TH "ORG.BLUEZ.GATTDESCRIPTOR" "5" "October 2023" "BlueZ" "Linux System Administration" .SH NAME org.bluez.GattDescriptor \- BlueZ D-Bus GattDescriptor API documentation .SH DESCRIPTION .sp GATT local/server and remote/client descriptor attribute representation share the same high\-level D\-Bus API. .sp Local/Server refers to GATT based descriptors exported by a plugin or an external application. .sp Remote/Client refers to GATT descriptors exported by the peer. .SH INTERFACE .SS Client .INDENT 0.0 .TP .B Service org.bluez .TP .B Interface org.bluez.GattDescriptor1 .TP .B Object path [variable prefix]/{hci0,hci1,...}/dev_XX_XX_XX_XX_XX_XX/serviceXX/charYYYY/descriptorZZZ .UNINDENT .SS Server .INDENT 0.0 .TP .B Service unique name .TP .B Interface org.bluez.GattDescriptor1 .TP .B Object path freely definable .UNINDENT .SS Methods .SS array{byte} ReadValue(dict flags) .INDENT 0.0 .INDENT 3.5 Issues a request to read the value of the descriptor and returns the value if the operation was successful. .sp Possible options: .INDENT 0.0 .TP .B uint16_t offset Read start offset in bytes. .TP .B object device (server only) Device object. .TP .B string link Link type (Server only). .sp Possible values: .INDENT 7.0 .TP .B \(dqBR/EDR\(dq .TP .B \(dqLE\(dq .UNINDENT .UNINDENT .sp Possible Errors: .INDENT 0.0 .TP .B org.bluez.Error.Failed .TP .B org.bluez.Error.InProgress .TP .B org.bluez.Error.NotPermitted .TP .B org.bluez.Error.NotAuthorized .TP .B org.bluez.Error.NotSupported .UNINDENT .UNINDENT .UNINDENT .SS void WriteValue(array{byte} value, dict flags) .INDENT 0.0 .INDENT 3.5 Issues a request to write the value of the descriptor. .sp Possible flags: .INDENT 0.0 .TP .B uint16 offset Write start offset in bytes. .TP .B uint16 mtu Exchanged MTU (Server only). .TP .B object device Device path (Server only). .TP .B string link Link type (Server only). .sp Possible values: .INDENT 7.0 .TP .B \(dqBR/EDR\(dq .TP .B \(dqLE\(dq .UNINDENT .TP .B boolean prepare\-authorize True if prepare authorization request. .UNINDENT .sp Possible Errors: .INDENT 0.0 .TP .B org.bluez.Error.Failed .TP .B org.bluez.Error.InProgress .TP .B org.bluez.Error.NotPermitted .TP .B org.bluez.Error.InvalidValueLength .TP .B org.bluez.Error.NotAuthorized .TP .B org.bluez.Error.NotSupported .TP .B org.bluez.Error.ImproperlyConfigured .UNINDENT .UNINDENT .UNINDENT .SS Properties .SS string UUID [read\-only] .INDENT 0.0 .INDENT 3.5 128\-bit descriptor UUID. .UNINDENT .UNINDENT .SS object Characteristic [read\-only] .INDENT 0.0 .INDENT 3.5 Object path of the GATT characteristic the descriptor belongs to. .UNINDENT .UNINDENT .SS array{byte} Value [read\-only, optional] .INDENT 0.0 .INDENT 3.5 The cached value of the descriptor. This property gets updated only after a successful read request, upon which a PropertiesChanged signal will be emitted. .UNINDENT .UNINDENT .SS array{string} Flags [read\-only] .INDENT 0.0 .INDENT 3.5 Defines how the descriptor value can be used. .sp Possible values: .INDENT 0.0 .TP .B \(dqread\(dq .TP .B \(dqwrite\(dq .TP .B \(dqencrypt\-read\(dq .TP .B \(dqencrypt\-write\(dq .TP .B \(dqencrypt\-authenticated\-read\(dq .TP .B \(dqencrypt\-authenticated\-write\(dq .TP .B \(dqsecure\-read\(dq (Server Only) .TP .B \(dqsecure\-write\(dq (Server Only) .TP .B \(dqauthorize\(dq .UNINDENT .UNINDENT .UNINDENT .SS uint16 Handle [read\-only] (Client Only) .INDENT 0.0 .INDENT 3.5 Descriptor handle. .UNINDENT .UNINDENT .SS uint16 Handle [read\-write, optional] (Server Only) .INDENT 0.0 .INDENT 3.5 Descriptor handle. When available in the server it would attempt to use to allocate into the database which may fail, to auto allocate the value 0x0000 shall be used which will cause the allocated handle to be set once registered. .UNINDENT .UNINDENT .\" Generated by docutils manpage writer. . bluez-5.82/doc/PaxHeaders/org.bluez.obex.PhonebookAccess.50000644000000000000000000000005014773213501020414 xustar0020 atime=1743591233 20 ctime=1743591291 bluez-5.82/doc/org.bluez.obex.PhonebookAccess.50000644000000000000000000002242014773213501020075 0ustar00rootroot.\" Man page generated from reStructuredText. . . .nr rst2man-indent-level 0 . .de1 rstReportMargin \\$1 \\n[an-margin] level \\n[rst2man-indent-level] level margin: \\n[rst2man-indent\\n[rst2man-indent-level]] - \\n[rst2man-indent0] \\n[rst2man-indent1] \\n[rst2man-indent2] .. .de1 INDENT .\" .rstReportMargin pre: . RS \\$1 . nr rst2man-indent\\n[rst2man-indent-level] \\n[an-margin] . nr rst2man-indent-level +1 .\" .rstReportMargin post: .. .de UNINDENT . RE .\" indent \\n[an-margin] .\" old: \\n[rst2man-indent\\n[rst2man-indent-level]] .nr rst2man-indent-level -1 .\" new: \\n[rst2man-indent\\n[rst2man-indent-level]] .in \\n[rst2man-indent\\n[rst2man-indent-level]]u .. .TH "ORG.BLUEZ.OBEX.PHONEBOOKACCESS" "5" "October 2023" "BlueZ" "Linux System Administration" .SH NAME org.bluez.obex.PhonebookAccess \- BlueZ D-Bus OBEX PhonebookAccess API documentation .SH INTERFACE .INDENT 0.0 .TP .B Service org.bluez.obex .TP .B Interface org.bluez.obex.PhonebookAccess1 .TP .B Object path [Session object path] .UNINDENT .SS Methods .SS void Select(string location, string phonebook) .INDENT 0.0 .INDENT 3.5 Selects the phonebook object for other operations. Should be call before all the other operations. .sp Possible location values: .INDENT 0.0 .TP .B \(dqint\(dq, \(dqinternal\(dq (default) Store in the Internal memory. .TP .B \(dqsim{#}\(dq Store in the sim number. .UNINDENT .sp Possible phonebook values: .INDENT 0.0 .TP .B \(dqpb\(dq Store as contact. .TP .B \(dqich\(dq Store as incoming call. .TP .B \(dqoch\(dq Store as outgoing call. .TP .B \(dqmch\(dq Store as missing call. .TP .B \(dqcch\(dq Store as a combination of incoming, outgoing and missing call. .UNINDENT .sp \(dqspd\(dq: .INDENT 0.0 .INDENT 3.5 Store as speed dials entry ( only for \(dqinternal\(dq ) .UNINDENT .UNINDENT .sp \(dqfav\(dq: .INDENT 0.0 .INDENT 3.5 Store as favorites entry ( only for \(dqinternal\(dq ) .UNINDENT .UNINDENT .sp Possible errors: .INDENT 0.0 .TP .B org.bluez.obex.Error.InvalidArguments .TP .B org.bluez.obex.Error.Failed .UNINDENT .UNINDENT .UNINDENT .SS object, dict PullAll(string targetfile, dict filters) .INDENT 0.0 .INDENT 3.5 Returns the entire phonebook object from the PSE server in plain string with vcard format, and store it in a local file. .sp If an empty target file is given, a name will be automatically generated for the temporary file. .sp The returned path represents the newly created transfer, which should be used to find out if the content has been successfully transferred or if the operation fails. .sp The properties of this transfer are also returned along with the object path, to avoid a call to GetProperties, see \fBorg.bluez.obex.Transfer(5)\fP for the possible list of properties. .sp Possible filters: .INDENT 0.0 .TP .B string Format Items vcard format. .sp Possible values: .INDENT 7.0 .TP .B \(dqvcard21\(dq (default) .TP .B \(dqvcard30\(dq .UNINDENT .TP .B string Order Items order. .sp Possible values: .INDENT 7.0 .TP .B \(dq\(dq .TP .B \(dqindexed\(dq .TP .B \(dqalphanumeric\(dq .TP .B \(dqphonetic\(dq .UNINDENT .TP .B uint16 Offset (default 0) Offset of the first item. .TP .B uint16 MaxCount (default 65535) Maximum number of items. .TP .B array{string} Fields (default all fields) Item vcard fields. .sp See \fBListFilterFields()\fP for possible values. .TP .B array{string} FilterAll Filter items by fields using AND logic, cannot be used together with \fBFilterAny\fP\&. .sp See \fBListFilterFields()\fP for possible values. .TP .B array{string} FilterAny Filter items by fields using OR logic, cannot be used together with \fBFilterAll\fP\&. .sp See \fBListFilterFields()\fP for possible values. .TP .B bool ResetNewMissedCalls Reset new the missed calls items, shall only be used for folders mch and cch. .UNINDENT .sp Possible errors: .INDENT 0.0 .TP .B org.bluez.obex.Error.InvalidArguments .TP .B org.bluez.obex.Forbidden .UNINDENT .UNINDENT .UNINDENT .SS array{string vcard, string name} List(dict filters) .INDENT 0.0 .INDENT 3.5 Returns array of vcard\-listing data where every entry consists of a pair of strings containing the vcard handle and the contact name. For example: .INDENT 0.0 .TP .B \(dq1.vcf\(dq \(dqJohn\(dq .UNINDENT .sp Possible filters: .INDENT 0.0 .TP .B string Order Contact order. .sp Possible values: .INDENT 7.0 .TP .B \(dq\(dq .TP .B \(dqindexed\(dq .TP .B \(dqalphanumeric\(dq .TP .B \(dqphonetic\(dq .UNINDENT .TP .B uint16 Offset Start offset. .TP .B uint16 MaxCount Maximum number of contacts. .UNINDENT .sp Possible errors: .INDENT 0.0 .TP .B org.bluez.obex.Error.InvalidArguments .TP .B org.bluez.obex.Forbidden .UNINDENT .UNINDENT .UNINDENT .SS object, dict Pull(string vcard, string targetfile, dict filters) .INDENT 0.0 .INDENT 3.5 Retrieves the vcard in the current phonebook object and store it in a local file. .sp If an empty target file is given, a name will be automatically generated for the temporary file. .sp The returned path represents the newly created transfer, which should be used to find out if the content has been successfully transferred or if the operation fails. .sp The properties of this transfer are also returned along with the object path, to avoid a call to GetProperties, see \fBorg.bluez.obex.Transfer(5)\fP for the possible list of properties. .sp Possible filters: .INDENT 0.0 .TP .B string Format Contact data format. .sp Possible values: .INDENT 7.0 .TP .B \(dq\(dq .TP .B \(dqvcard21\(dq .TP .B \(dqvcard30\(dq .UNINDENT .TP .B array{string} Fields See \fBListFilterFields()\fP for possible values. .UNINDENT .sp Possible errors: .INDENT 0.0 .TP .B org.bluez.obex.Error.InvalidArguments .TP .B org.bluez.obex.Error.Forbidden .TP .B org.bluez.obex.Error.Failed .UNINDENT .UNINDENT .UNINDENT .SS array{string vcard, string name} Search(string field, string value, dict filters) .INDENT 0.0 .INDENT 3.5 Searches for entries matching the given condition and return an array of vcard\-listing data where every entry consists of a pair of strings containing the vcard handle and the contact name. .sp Possible field values: .INDENT 0.0 .INDENT 3.5 .INDENT 0.0 .TP .B \(dqname\(dq (default) Search by name. .TP .B \(dqnumber\(dq Search by number. .TP .B \(dqsound\(dq Search by sound. .UNINDENT .UNINDENT .UNINDENT .sp value: the string value to search for .sp Possible filters: .INDENT 0.0 .TP .B string Order Contact order. .sp Possible values: .INDENT 7.0 .TP .B \(dq\(dq .TP .B \(dqindexed\(dq .TP .B \(dqalphanumeric\(dq .TP .B \(dqphonetic\(dq .UNINDENT .TP .B uint16 Offset Start offset. .TP .B uint16 MaxCount Maximum number of contacts. .UNINDENT .sp Possible errors: .INDENT 0.0 .TP .B org.bluez.obex.Error.InvalidArguments .TP .B org.bluez.obex.Error.Forbidden .TP .B org.bluez.obex.Error.Failed .UNINDENT .UNINDENT .UNINDENT .SS uint16 GetSize() .INDENT 0.0 .INDENT 3.5 Returns the number of entries in the selected phonebook object that are actually used (i.e. indexes that correspond to non\-NULL entries). .sp Possible errors: .INDENT 0.0 .TP .B org.bluez.obex.Error.Forbidden .TP .B org.bluez.obex.Error.Failed .UNINDENT .UNINDENT .UNINDENT .SS void UpdateVersion() .INDENT 0.0 .INDENT 3.5 Attempts to update PrimaryCounter and SecondaryCounter. .sp Possible errors: .INDENT 0.0 .TP .B org.bluez.obex.Error.NotSupported .TP .B org.bluez.obex.Error.Forbidden .TP .B org.bluez.obex.Error.Failed .UNINDENT .UNINDENT .UNINDENT .SS array{string} ListFilterFields() .INDENT 0.0 .INDENT 3.5 Returns all Available fields that can be used in Fields filter. .sp Possible return: .INDENT 0.0 .TP .B \(dqVERSION\(dq .TP .B \(dqFN\(dq .TP .B \(dqN\(dq .TP .B \(dqPHOTO\(dq .TP .B \(dqBDAY\(dq .TP .B \(dqADR\(dq .TP .B \(dqLABEL\(dq .TP .B \(dqTEL\(dq .TP .B \(dqEMAIL\(dq .TP .B \(dqMAILER\(dq .TP .B \(dqTZ\(dq .TP .B \(dqGEO\(dq .TP .B \(dqTITLE\(dq .TP .B \(dqROLE\(dq .TP .B \(dqLOGO\(dq .TP .B \(dqAGENT\(dq .TP .B \(dqORG\(dq .TP .B \(dqNOTE\(dq .TP .B \(dqREV\(dq .TP .B \(dqSOUND\(dq .TP .B \(dqURL\(dq .TP .B \(dqUID\(dq .TP .B \(dqKEY\(dq .TP .B \(dqNICKNAME\(dq .TP .B \(dqCATEGORIES\(dq .TP .B \(dqPROID\(dq .TP .B \(dqCLASS\(dq .TP .B \(dqSORT\-STRING\(dq .TP .B \(dqX\-IRMC\-CALL\-DATETIME\(dq .TP .B \(dqX\-BT\-SPEEDDIALKEY\(dq .TP .B \(dqX\-BT\-UCI\(dq .TP .B \(dqX\-BT\-UID\(dq .TP .B \(dqBIT\-{#}\(dq .UNINDENT .sp Possible errors: None .UNINDENT .UNINDENT .SS Properties .SS string Folder [readonly] .INDENT 0.0 .INDENT 3.5 Current folder. .UNINDENT .UNINDENT .SS string DatabaseIdentifier [readonly, optional] .INDENT 0.0 .INDENT 3.5 128 bits persistent database identifier. .sp Possible values: .INDENT 0.0 .INDENT 3.5 32\-character hexadecimal such as A1A2A3A4B1B2C1C2D1D2E1E2E3E4E5E6 .UNINDENT .UNINDENT .UNINDENT .UNINDENT .SS string PrimaryCounter [readonly, optional] .INDENT 0.0 .INDENT 3.5 128 bits primary version counter. .sp Possible values: .INDENT 0.0 .INDENT 3.5 32\-character hexadecimal such as A1A2A3A4B1B2C1C2D1D2E1E2E3E4E5E6 .UNINDENT .UNINDENT .UNINDENT .UNINDENT .SS string SecondaryCounter [readonly, optional] .INDENT 0.0 .INDENT 3.5 128 bits secondary version counter. .sp Possible values: .INDENT 0.0 .INDENT 3.5 32\-character hexadecimal such as A1A2A3A4B1B2C1C2D1D2E1E2E3E4E5E6 .UNINDENT .UNINDENT .UNINDENT .UNINDENT .SS bool FixedImageSize [readonly, optional] .INDENT 0.0 .INDENT 3.5 Indicate support for fixed image size. .sp Possible values: .INDENT 0.0 .INDENT 3.5 True if image is JPEG 300x300 pixels otherwise False. .UNINDENT .UNINDENT .UNINDENT .UNINDENT .\" Generated by docutils manpage writer. . bluez-5.82/doc/PaxHeaders/pixit-opp.txt0000644000000000000000000000005012572170650015112 xustar0020 atime=1743516877 20 ctime=1743591290 bluez-5.82/doc/pixit-opp.txt0000644000000000000000000000156212572170650014577 0ustar00rootrootOPP PIXIT for the PTS tool. PTS version: 6.2.0 * - different than PTS defaults & - should be set to IUT Bluetooth address Required PIXIT settings ------------------------------------------------------------------------------- Parameter Name Value ------------------------------------------------------------------------------- TSPX_supported_extension bmp TSPX_unsupported_extension pts TSPX_client_class_of_device 100104 TSPX_server_class_of_device 100104 TSPX_auth_password 0000 TSPX_auth_user_id PTS TSPX_l2cap_psm 1003 TSPX_rfcomm_channel 8 TSPX_no_confirmations FALSE TSPX_bd_addr_iut 112233445566 (*&) TSPX_delete_link_key FALSE TSPX_pin_code 0000 TSPX_security_enabled FALSE TSPX_time_guard 300000 TSPX_use_implicit_send TRUE ------------------------------------------------------------------------------- bluez-5.82/doc/PaxHeaders/org.bluez.obex.AgentManager.50000644000000000000000000000005014773213504017702 xustar0020 atime=1743591236 20 ctime=1743591291 bluez-5.82/doc/org.bluez.obex.AgentManager.50000644000000000000000000000335314773213504017367 0ustar00rootroot.\" Man page generated from reStructuredText. . . .nr rst2man-indent-level 0 . .de1 rstReportMargin \\$1 \\n[an-margin] level \\n[rst2man-indent-level] level margin: \\n[rst2man-indent\\n[rst2man-indent-level]] - \\n[rst2man-indent0] \\n[rst2man-indent1] \\n[rst2man-indent2] .. .de1 INDENT .\" .rstReportMargin pre: . RS \\$1 . nr rst2man-indent\\n[rst2man-indent-level] \\n[an-margin] . nr rst2man-indent-level +1 .\" .rstReportMargin post: .. .de UNINDENT . RE .\" indent \\n[an-margin] .\" old: \\n[rst2man-indent\\n[rst2man-indent-level]] .nr rst2man-indent-level -1 .\" new: \\n[rst2man-indent\\n[rst2man-indent-level]] .in \\n[rst2man-indent\\n[rst2man-indent-level]]u .. .TH "ORG.BLUEZ.OBEX.AGENTMANAGER" "5" "October 2023" "BlueZ" "Linux System Administration" .SH NAME org.bluez.obex.AgentManager \- BlueZ D-Bus OBEX AgentManager API documentation .SH INTERFACE .INDENT 0.0 .TP .B Service org.bluez.obex .TP .B Interface org.bluez.obex.AgentManager1 .TP .B Object path /org/bluez/obex .UNINDENT .SS Methods .SS void RegisterAgent(object agent) .INDENT 0.0 .INDENT 3.5 Registers an agent, which must implement \fBorg.bluez.obex.Agent(5)\fP, to request authorization of the user to accept/reject objects. .sp Object push service needs to authorize each received object. .sp Possible errors: .INDENT 0.0 .TP .B org.bluez.obex.Error.AlreadyExists .UNINDENT .UNINDENT .UNINDENT .SS void UnregisterAgent(object agent) .INDENT 0.0 .INDENT 3.5 Unregisters the agent that has been previously registered using \fBRegisterAgent()\fP\&. The object path parameter must match the same value that has been used on registration. .sp Possible errors: .INDENT 0.0 .TP .B org.bluez.obex.Error.DoesNotExist .UNINDENT .UNINDENT .UNINDENT .\" Generated by docutils manpage writer. . bluez-5.82/doc/PaxHeaders/sap-api.txt0000644000000000000000000000005012066112416014505 xustar0020 atime=1743516877 20 ctime=1743591290 bluez-5.82/doc/sap-api.txt0000644000000000000000000000070012066112416014163 0ustar00rootrootBlueZ D-Bus Sim Access API description ************************************** Sim Access Profile hierarchy ============================ Service org.bluez Interface org.bluez.SimAccess1 Object path [variable prefix]/{hci0,hci1,...} Methods void Disconnect() Disconnects SAP client from the server. Possible errors: org.bluez.Error.Failed Properties boolean Connected [readonly] Indicates if SAP client is connected to the server. bluez-5.82/doc/PaxHeaders/org.bluez.Input.rst0000644000000000000000000000005014536422313016155 xustar0020 atime=1743516786 20 ctime=1743591290 bluez-5.82/doc/org.bluez.Input.rst0000644000000000000000000000215114536422313015635 0ustar00rootroot=============== org.bluez.Input =============== ----------------------------------- BlueZ D-Bus Input API documentation ----------------------------------- :Version: BlueZ :Date: October 2023 :Manual section: 5 :Manual group: Linux System Administration Interface ========= :Service: org.bluez :Interface: org.bluez.Input1 :Object path: [variable prefix]/{hci0,hci1,...}/dev_XX_XX_XX_XX_XX_XX Properties ---------- string ReconnectMode [readonly] ``````````````````````````````` Indicates the Connectability mode of the HID device as defined by the HID Profile specification, Section 5.4.2. This mode is based in the two properties HIDReconnectInitiate (see Section 5.3.4.6) and HIDNormallyConnectable (see Section 5.3.4.14) which define the following four possible values: :"none": Device and host are not required to automatically restore the connection. :"host": Bluetooth HID host restores connection. :"device": Bluetooth HID device restores connection. :"any": Bluetooth HID device shall attempt to restore the lost connection, but Bluetooth HID Host may also restore the connection. bluez-5.82/doc/PaxHeaders/org.bluez.Battery.rst0000644000000000000000000000005014536422313016470 xustar0020 atime=1743516789 20 ctime=1743591290 bluez-5.82/doc/org.bluez.Battery.rst0000644000000000000000000000164614536422313016160 0ustar00rootroot================= org.bluez.Battery ================= ------------------------------------- BlueZ D-Bus Battery API documentation ------------------------------------- :Version: BlueZ :Date: October 2023 :Manual section: 5 :Manual group: Linux System Administration Interface ========= :Service: org.bluez :Interface: org.bluez.Battery1 :Object path: [variable prefix]/{hci0,hci1,...}/dev_XX_XX_XX_XX_XX_XX Properties ---------- byte Percentage [readonly] `````````````````````````` The percentage of battery left as an unsigned 8-bit integer. string Source [readonly, optional] `````````````````````````````````` Describes where the battery information comes from. This property is informational only and may be useful for debugging purposes. Providers from **org.bluez.BatteryProvider(5)** may make use of this property to indicate where the battery report comes from (e.g. "HFP 1.7", "HID", or the profile UUID). bluez-5.82/doc/PaxHeaders/org.bluez.MediaEndpoint.50000644000000000000000000000005014773213454017141 xustar0020 atime=1743591212 20 ctime=1743591291 bluez-5.82/doc/org.bluez.MediaEndpoint.50000644000000000000000000001435314773213454016630 0ustar00rootroot.\" Man page generated from reStructuredText. . . .nr rst2man-indent-level 0 . .de1 rstReportMargin \\$1 \\n[an-margin] level \\n[rst2man-indent-level] level margin: \\n[rst2man-indent\\n[rst2man-indent-level]] - \\n[rst2man-indent0] \\n[rst2man-indent1] \\n[rst2man-indent2] .. .de1 INDENT .\" .rstReportMargin pre: . RS \\$1 . nr rst2man-indent\\n[rst2man-indent-level] \\n[an-margin] . nr rst2man-indent-level +1 .\" .rstReportMargin post: .. .de UNINDENT . RE .\" indent \\n[an-margin] .\" old: \\n[rst2man-indent\\n[rst2man-indent-level]] .nr rst2man-indent-level -1 .\" new: \\n[rst2man-indent\\n[rst2man-indent-level]] .in \\n[rst2man-indent\\n[rst2man-indent-level]]u .. .TH "ORG.BLUEZ.MEDIAENDPOINT" "5" "September 2023" "BlueZ" "Linux System Administration" .SH NAME org.bluez.MediaEndpoint \- BlueZ D-Bus MediaEndpoint API documentation .SH INTERFACE .INDENT 0.0 .TP .B Service unique name (Server role) org.bluez (Client role) .TP .B Interface org.bluez.MediaEndpoint1 .TP .B Object path freely definable (Server role) [variable prefix]/{hci0,hci1,...}/dev_XX_XX_XX_XX_XX_XX/sepX (Client role) .UNINDENT .SS Methods .SS void SetConfiguration(object transport, dict properties) .INDENT 0.0 .INDENT 3.5 Set configuration for the transport. .INDENT 0.0 .TP .B object transport Configured transport object. .TP .B dict properties Configured \fBorg.bluez.MediaTransport(5)\fP properties. .UNINDENT .sp For client role transport must be set with a server endpoint object which will be configured and the properties must contain the following properties: .INDENT 0.0 .TP .B array{byte} Capabilities [Mandatory] See Capabilities property. .TP .B array{byte} Metadata [ISO only] See Metadata property. .TP .B dict QoS [ISO only] See \fBorg.bluez.MediaTransport(5)\fP QoS property. .UNINDENT .UNINDENT .UNINDENT .SS array{byte} SelectConfiguration(array{byte} capabilities) .INDENT 0.0 .INDENT 3.5 Select preferable configuration from the supported capabilities. .sp Returns a configuration which can be used to setup a transport, see \fBorg.bluez.MediaTransport(5)\fP for possible values. .sp Note: There is no need to cache the selected configuration since on success the configuration is send back as parameter of SetConfiguration. .UNINDENT .UNINDENT .SS dict SelectProperties(dict capabilities) .INDENT 0.0 .INDENT 3.5 Select BAP unicast configuration from the supported capabilities: .INDENT 0.0 .TP .B object Endpoint .TP .B array{byte} Capabilities .TP .B array{byte} Metadata .TP .B uint32 Locations .TP .B uint32_t ChannelAllocation .TP .B dict QoS .INDENT 7.0 .TP .B byte Framing .TP .B byte PHY .TP .B uint16 MaximumLatency .TP .B uint32 MinimumDelay .TP .B uint32 MaximumDelay .TP .B uint32 PreferredMinimumDelay .TP .B uint32 PreferredMaximumDelay .UNINDENT .UNINDENT .sp See MediaEndpoint Properties for their possible values. .sp Returns a configuration which can be used to setup a transport: .INDENT 0.0 .TP .B array{byte} Capabilities .TP .B array{byte} Metadata [optional] .TP .B dict QoS .UNINDENT .sp See SetConfiguration for their possible values. .sp Note: There is no need to cache the selected properties since on success the configuration is send back as parameter of SetConfiguration. .UNINDENT .UNINDENT .SS void ClearConfiguration(object transport) .INDENT 0.0 .INDENT 3.5 Clear transport configuration. .UNINDENT .UNINDENT .SS void Release() .INDENT 0.0 .INDENT 3.5 This method gets called when the service daemon unregisters the endpoint. An endpoint can use it to do cleanup tasks. There is no need to unregister the endpoint, because when this method gets called it has already been unregistered. .UNINDENT .UNINDENT .SS MediaEndpoint Properties .SS string UUID [readonly, optional] .INDENT 0.0 .INDENT 3.5 UUID of the profile which the endpoint is for. .UNINDENT .UNINDENT .SS byte Codec [readonly, optional] .INDENT 0.0 .INDENT 3.5 Assigned number of codec that the endpoint implements. The values should match the profile specification which is indicated by the UUID. .UNINDENT .UNINDENT .SS uint32_t Vendor [readonly, Optional] .INDENT 0.0 .INDENT 3.5 Vendor\-specific Company ID, Codec ID tuple that the endpoint implements. .sp It shall be set to appropriate value when Vendor Specific Codec (0xff) is used. .UNINDENT .UNINDENT .SS array{byte} Capabilities [readonly, optional] .INDENT 0.0 .INDENT 3.5 Capabilities blob, it is used as it is so the size and byte order must match. .UNINDENT .UNINDENT .SS array{byte} Metadata [readonly, Optional] .INDENT 0.0 .INDENT 3.5 Metadata blob, it is used as it is so the size and byte order must match. .UNINDENT .UNINDENT .SS object Device [readonly, optional] .INDENT 0.0 .INDENT 3.5 Device object which the endpoint is belongs to. .UNINDENT .UNINDENT .SS bool DelayReporting [readonly, optional] .INDENT 0.0 .INDENT 3.5 Indicates if endpoint supports Delay Reporting. .UNINDENT .UNINDENT .SS uint32 Locations [readonly, optional, ISO only, experimental] .INDENT 0.0 .INDENT 3.5 Indicates endpoint supported locations. .UNINDENT .UNINDENT .SS uint16 SupportedContext [readonly, optional, ISO only, experimental] .INDENT 0.0 .INDENT 3.5 Indicates endpoint supported audio context. .UNINDENT .UNINDENT .SS uint16 Context [readonly, optional, ISO only, experimental] .INDENT 0.0 .INDENT 3.5 Indicates endpoint available audio context. .UNINDENT .UNINDENT .SS dict QoS [readonly, optional, ISO only, experimental] .INDENT 0.0 .INDENT 3.5 Indicates QoS capabilities. .INDENT 0.0 .TP .B byte Framing Indicates endpoint support framing. .sp Possible Values: .INDENT 7.0 .TP .B 0x00 Unframed PDUs supported. .TP .B 0x01 Unframed PDUs not supported. .UNINDENT .TP .B byte PHY Indicates endpoint preferred PHY. .sp Possible values: .INDENT 7.0 .TP .B bit 0 LE 1M preferred. .TP .B bit 1 LE 2M preferred. .TP .B bit 2 LE Coded preferred. .UNINDENT .TP .B byte Retransmissions Indicates endpoint preferred number of retransmissions. .TP .B uint16 MaximumLatency Indicates endpoint maximum latency. .TP .B uint32 MinimumDelay Indicates endpoint minimum presentation delay. .TP .B uint32 MaximumDelay Indicates endpoint maximum presentation delay. .TP .B uint32 PreferredMinimumDelay Indicates endpoint preferred minimum presentation delay. .TP .B uint32 PreferredMaximumDelay Indicates endpoint preferred maximum presentation delay. .UNINDENT .UNINDENT .UNINDENT .\" Generated by docutils manpage writer. . bluez-5.82/doc/PaxHeaders/org.bluez.MediaControl.rst0000644000000000000000000000005014536422313017436 xustar0020 atime=1743516793 20 ctime=1743591290 bluez-5.82/doc/org.bluez.MediaControl.rst0000644000000000000000000000275114536422313017124 0ustar00rootroot====================== org.bluez.MediaControl ====================== ------------------------------------------ BlueZ D-Bus MediaControl API documentation ------------------------------------------ :Version: BlueZ :Date: September 2023 :Manual section: 5 :Manual group: Linux System Administration Interface ========= :Service: org.bluez :Interface: org.bluez.MediaControl1 :Object path: [variable prefix]/{hci0,hci1,...}/dev_XX_XX_XX_XX_XX_XX Methods ------- void Play() [Deprecated] ```````````````````````` Resume playback. void Pause() [Deprecated] ````````````````````````` Pause playback. void Stop() [Deprecated] ```````````````````````` Stop playback. void Next() [Deprecated] ```````````````````````` Next item. void Previous() [Deprecated] ```````````````````````````` Previous item. void VolumeUp() [Deprecated] ```````````````````````````` Adjust remote volume one step up void VolumeDown() [Deprecated] `````````````````````````````` Adjust remote volume one step down void FastForward() [Deprecated] ``````````````````````````````` Fast forward playback, this action is only stopped when another method in this interface is called. void Rewind() [Deprecated] `````````````````````````` Rewind playback, this action is only stopped when another method in this interface is called. Properties ---------- boolean Connected [readonly] ```````````````````````````` object Player [readonly, optional] `````````````````````````````````` Addressed Player object path. bluez-5.82/doc/PaxHeaders/org.bluez.GattDescriptor.rst0000644000000000000000000000005014621503015020006 xustar0020 atime=1743516806 20 ctime=1743591290 bluez-5.82/doc/org.bluez.GattDescriptor.rst0000644000000000000000000000661114621503015017473 0ustar00rootroot======================== org.bluez.GattDescriptor ======================== -------------------------------------------- BlueZ D-Bus GattDescriptor API documentation -------------------------------------------- :Version: BlueZ :Date: October 2023 :Manual section: 5 :Manual group: Linux System Administration Description =========== GATT local/server and remote/client descriptor attribute representation share the same high-level D-Bus API. Local/Server refers to GATT based descriptors exported by a plugin or an external application. Remote/Client refers to GATT descriptors exported by the peer. Interface ========= Client ------ :Service: org.bluez :Interface: org.bluez.GattDescriptor1 :Object path: [variable prefix]/{hci0,hci1,...}/dev_XX_XX_XX_XX_XX_XX/serviceXX/charYYYY/descriptorZZZ Server ------ :Service: unique name :Interface: org.bluez.GattDescriptor1 :Object path: freely definable Methods ------- array{byte} ReadValue(dict flags) ````````````````````````````````` Issues a request to read the value of the descriptor and returns the value if the operation was successful. Possible options: :uint16_t offset: Read start offset in bytes. :object device (server only): Device object. :string link: Link type (Server only). Possible values: :"BR/EDR": :"LE": Possible Errors: :org.bluez.Error.Failed: :org.bluez.Error.InProgress: :org.bluez.Error.NotPermitted: :org.bluez.Error.NotAuthorized: :org.bluez.Error.NotSupported: void WriteValue(array{byte} value, dict flags) `````````````````````````````````````````````` Issues a request to write the value of the descriptor. Possible flags: :uint16 offset: Write start offset in bytes. :uint16 mtu: Exchanged MTU (Server only). :object device: Device path (Server only). :string link: Link type (Server only). Possible values: :"BR/EDR": :"LE": :boolean prepare-authorize: True if prepare authorization request. Possible Errors: :org.bluez.Error.Failed: :org.bluez.Error.InProgress: :org.bluez.Error.NotPermitted: :org.bluez.Error.InvalidValueLength: :org.bluez.Error.NotAuthorized: :org.bluez.Error.NotSupported: :org.bluez.Error.ImproperlyConfigured: Properties ---------- string UUID [read-only] ``````````````````````` 128-bit descriptor UUID. object Characteristic [read-only] ````````````````````````````````` Object path of the GATT characteristic the descriptor belongs to. array{byte} Value [read-only, optional] ``````````````````````````````````````` The cached value of the descriptor. This property gets updated only after a successful read request, upon which a PropertiesChanged signal will be emitted. array{string} Flags [read-only] ``````````````````````````````` Defines how the descriptor value can be used. Possible values: :"read": :"write": :"encrypt-read": :"encrypt-write": :"encrypt-authenticated-read": :"encrypt-authenticated-write": :"secure-read" (Server Only): :"secure-write" (Server Only): :"authorize": uint16 Handle [read-only] (Client Only) ``````````````````````````````````````` Descriptor handle. uint16 Handle [read-write, optional] (Server Only) `````````````````````````````````````````````````` Descriptor handle. When available in the server it would attempt to use to allocate into the database which may fail, to auto allocate the value 0x0000 shall be used which will cause the allocated handle to be set once registered. bluez-5.82/doc/PaxHeaders/org.bluez.GattManager.rst0000644000000000000000000000005014536422313017250 xustar0020 atime=1743516802 20 ctime=1743591290 bluez-5.82/doc/org.bluez.GattManager.rst0000644000000000000000000000773414536422313016744 0ustar00rootroot===================== org.bluez.GattManager ===================== ----------------------------------------- BlueZ D-Bus GattManager API documentation ----------------------------------------- :Version: BlueZ :Date: October 2023 :Manual section: 5 :Manual group: Linux System Administration Description =========== GATT Manager allows external applications to register GATT services and profiles. Registering a profile allows applications to subscribe to *remote/client* services. Registering a service allows applications to publish a *local/server* GATT service, which then becomes available to remote devices. A GATT service is represented by a D-Bus object hierarchy where the root node corresponds to a service and the child nodes represent characteristics and descriptors that belong to that service. Each node must implement one of **org.bluez.GattService(5)**, **org.bluez.GattCharacteristic(5)** or **org.bluez.GattDescriptor(5)** interfaces, based on the attribute it represents. Each node must also implement the standard D-Bus Properties interface to expose their properties. These objects collectively represent a GATT service definition. To make service registration simple, **bluetoothd(8)** requires that all objects that belong to a GATT service be grouped under a D-Bus Object Manager that solely manages the objects of that service. Hence, the standard DBus.ObjectManager interface must be available on the root service path. An example application hierarchy containing two separate GATT services may look like this: .. code-block:: -> /com/example | - org.freedesktop.DBus.ObjectManager | -> /com/example/service0 | | - org.freedesktop.DBus.Properties | | - org.bluez.GattService1 | | | -> /com/example/service0/char0 | | - org.freedesktop.DBus.Properties | | - org.bluez.GattCharacteristic1 | | | -> /com/example/service0/char1 | | - org.freedesktop.DBus.Properties | | - org.bluez.GattCharacteristic1 | | | -> /com/example/service0/char1/desc0 | - org.freedesktop.DBus.Properties | - org.bluez.GattDescriptor1 | -> /com/example/service1 | - org.freedesktop.DBus.Properties | - org.bluez.GattService1 | -> /com/example/service1/char0 - org.freedesktop.DBus.Properties - org.bluez.GattCharacteristic1 When a service is registered, **bluetoothd(8)** will automatically obtain information about all objects using the service's Object Manager. Once a service has been registered, the objects of a service should not be removed. If **bluetoothd(8)** receives an InterfacesRemoved signal from a service's Object Manager, it will immediately unregister the service. Similarly, if the application disconnects from the bus, all of its registered services will be automatically unregistered. InterfacesAdded signals will be ignored. Interface ========= :Service: org.bluez :Interface: org.bluez.GattManager1 :Object path: [variable prefix]/{hci0,hci1,...} Methods ------- void RegisterApplication(object application, dict options) `````````````````````````````````````````````````````````` Registers a local GATT services hierarchy as described above (GATT Server) and/or GATT profiles (GATT Client). The application object path together with the D-Bus system bus connection ID define the identification of the application registering a GATT based service (**org.bluez.GattService(5)**) and/or profile (**org.bluez.GattProfile(5)**). Possible errors: :org.bluez.Error.InvalidArguments: :org.bluez.Error.AlreadyExists: void UnregisterApplication(object application) `````````````````````````````````````````````` This unregisters the services and/or profiles that has been previously registered using **RegisterApplication()**. The object path parameter must match the same value that has been used on registration. Possible errors: :org.bluez.Error.InvalidArguments: :org.bluez.Error.DoesNotExist: bluez-5.82/doc/PaxHeaders/org.bluez.DeviceSet.rst0000644000000000000000000000005014536422313016731 xustar0020 atime=1743516778 20 ctime=1743591290 bluez-5.82/doc/org.bluez.DeviceSet.rst0000644000000000000000000000330414536422313016412 0ustar00rootroot=================== org.bluez.DeviceSet =================== --------------------------------------- BlueZ D-Bus DeviceSet API documentation --------------------------------------- :Version: BlueZ :Date: September 2023 :Manual section: 5 :Manual group: Linux System Administration Interface ========= :Service: org.bluez :Interface: org.bluez.DeviceSet1 :Object path: [variable prefix]/{hci0,hci1,...}/set_{sirk} Methods ------- void Connect() [experimental] ````````````````````````````` Connects all **devices** members of the set, each member is connected in sequence as they were added/loaded following the same proceedure as described in **Device1.Connect**. Possible errors: :org.bluez.Error.NotReady: :org.bluez.Error.Failed: :org.bluez.Error.InProgress: :org.bluez.Error.AlreadyConnected: void Disconnect() [experimental] ```````````````````````````````` Disconnects all **devices** members of the set, each member is disconnected in sequence as they were connected following the same proceedure as described in **Device1.Disconnect**. Possible errors: :org.bluez.Error.NotConnected: Properties ---------- object Adapter [readonly, experimental] ``````````````````````````````````````` The object path of the adapter the set belongs to. bool AutoConnect [read-write, experimental] ``````````````````````````````````````````` Indicates if the **devices** members of the set shall be automatically connected once any of its members is connected. array(object) Devices [ready-only, experimental] ```````````````````````````````````````````````` List of devices objects that are members of the set. byte Size [read-only, experimental] ``````````````````````````````````` Set members size. bluez-5.82/doc/PaxHeaders/org.bluez.ProfileManager.rst0000644000000000000000000000005014536422313017751 xustar0020 atime=1743516781 20 ctime=1743591290 bluez-5.82/doc/org.bluez.ProfileManager.rst0000644000000000000000000000646014536422313017440 0ustar00rootroot======================== org.bluez.ProfileManager ======================== -------------------------------------------- BlueZ D-Bus ProfileManager API documentation -------------------------------------------- :Version: BlueZ :Date: October 2023 :Manual section: 5 :Manual group: Linux System Administration Interface ========= :Service: org.bluez :Interface: org.bluez.ProfileManager1 :Object path: /org/bluez Methods ------- void RegisterProfile(object profile, string uuid, dict options) ``````````````````````````````````````````````````````````````` Registers profile agent. The object path defines the path of the profile that will be called when there is a connection and must implement **org.bluez.Profile(5)** interface. If an application disconnects from the bus all its registered profiles will be removed. Possible uuid values: :"0000111f-0000-1000-8000-00805f9b34fb": HFP AG, default profile Version is 1.7, profile Features is 0b001001 and RFCOMM channel is 13. Authentication is required. :"0000111e-0000-1000-8000-00805f9b34fb": HFP HS, default profile Version is 1.7, profile Features is 0b000000 and RFCOMM channel is 7. Authentication is required. :"00001112-0000-1000-8000-00805f9b34fb": HSP AG, default profile Version is 1.2, RFCOMM channel is 12 and Authentication is required. Does not support any Features, option is ignored. :"00001108-0000-1000-8000-00805f9b34fb": HSP HS, default profile Version is 1.2, profile Features is 0b0 and RFCOMM channel is 6. Authentication is required. Features is one bit value, specify capability of Remote Audio Volume Control (by default turned off). :"": Vendor defined UUID, no defaults, must set options. Possible options values: :string Name: Human readable name for the profile :string Service: The primary service class UUID (if different from the actual profile UUID). :string Role: For asymmetric profiles that do not have UUIDs available to uniquely identify each side this parameter allows specifying the precise local role. Possible values: :"client": :"server": :uint16 Channel: RFCOMM channel number that is used for client and server UUIDs. If applicable it will be used in the SDP record as well. :uint16 PSM: PSM number that is used for client and server UUIDs. If applicable it will be used in the SDP record as well. :boolean RequireAuthentication: Pairing is required before connections will be established. No devices will be connected if not paired. :boolean RequireAuthorization: Request authorization before any connection will be established. :boolean AutoConnect: In case of a client UUID this will force connection of the RFCOMM or L2CAP channels when a remote device is connected. :string ServiceRecord: Provide a manual SDP record. :uint16 Version: Profile version (for SDP record) :uint16 Features: Profile features (for SDP record) Possible errors: :org.bluez.Error.InvalidArguments: :org.bluez.Error.AlreadyExists: void UnregisterProfile(object profile) `````````````````````````````````````` Unregisters profile object that has been previously registered using **RegisterProfile**. The object path parameter must match the same value that has been used on registration. Possible errors: :org.bluez.Error.DoesNotExist: bluez-5.82/doc/PaxHeaders/org.bluez.Agent.rst0000644000000000000000000000005014536422313016114 xustar0020 atime=1743516780 20 ctime=1743591290 bluez-5.82/doc/org.bluez.Agent.rst0000644000000000000000000001023314536422313015574 0ustar00rootroot=============== org.bluez.Agent =============== ----------------------------------- BlueZ D-Bus Agent API documentation ----------------------------------- :Version: BlueZ :Date: October 2023 :Manual section: 5 :Manual group: Linux System Administration Interface ========= :Service: unique name :Interface: org.bluez.Agent1 :Object path: freely definable Methods ------- void Release() `````````````` This method gets called when the service daemon unregisters the agent. An agent can use it to do cleanup tasks. There is no need to unregister the agent, because when this method gets called it has already been unregistered. string RequestPinCode(object device) ```````````````````````````````````` This method gets called when the service daemon needs to get the passkey for an authentication. The return value should be a string of 1-16 characters length. The string can be alphanumeric. Possible errors: :org.bluez.Error.Rejected: :org.bluez.Error.Canceled: void DisplayPinCode(object device, string pincode) `````````````````````````````````````````````````` This method gets called when the service daemon needs to display a pincode for an authentication. An empty reply should be returned. When the pincode needs no longer to be displayed, the Cancel method of the agent will be called. This is used during the pairing process of keyboards that don't support Bluetooth 2.1 Secure Simple Pairing, in contrast to DisplayPasskey which is used for those that do. This method will only ever be called once since older keyboards do not support typing notification. Note that the PIN will always be a 6-digit number, zero-padded to 6 digits. This is for harmony with the later specification. Possible errors: :org.bluez.Error.Rejected: :org.bluez.Error.Canceled: uint32 RequestPasskey(object device) ```````````````````````````````````` This method gets called when the service daemon needs to get the passkey for an authentication. The return value should be a numeric value between 0-999999. Possible errors: :org.bluez.Error.Rejected: :org.bluez.Error.Canceled: void DisplayPasskey(object device, uint32 passkey, uint16 entered) `````````````````````````````````````````````````````````````````` This method gets called when the service daemon needs to display a passkey for an authentication. The entered parameter indicates the number of already typed keys on the remote side. An empty reply should be returned. When the passkey needs no longer to be displayed, the Cancel method of the agent will be called. During the pairing process this method might be called multiple times to update the entered value. Note that the passkey will always be a 6-digit number, so the display should be zero-padded at the start if the value contains less than 6 digits. void RequestConfirmation(object device, uint32 passkey) ``````````````````````````````````````````````````````` This method gets called when the service daemon needs to confirm a passkey for an authentication. To confirm the value it should return an empty reply or an error in case the passkey is invalid. Note that the passkey will always be a 6-digit number, so the display should be zero-padded at the start if the value contains less than 6 digits. Possible errors: :org.bluez.Error.Rejected: :org.bluez.Error.Canceled: void RequestAuthorization(object device) ```````````````````````````````````````` This method gets called to request the user to authorize an incoming pairing attempt which would in other circumstances trigger the just-works model, or when the user plugged in a device that implements cable pairing. In the latter case, the device would not be connected to the adapter via Bluetooth yet. Possible errors: :org.bluez.Error.Rejected: :org.bluez.Error.Canceled: void AuthorizeService(object device, string uuid) ````````````````````````````````````````````````` This method gets called when the service daemon needs to authorize a connection/service request. Possible errors: :org.bluez.Error.Rejected: :org.bluez.Error.Canceled: void Cancel() ````````````` This method gets called to indicate that the agent request failed before a reply was returned. bluez-5.82/doc/PaxHeaders/org.bluez.Adapter.rst0000644000000000000000000000005014766002272016441 xustar0020 atime=1743515578 20 ctime=1743591290 bluez-5.82/doc/org.bluez.Adapter.rst0000644000000000000000000002650514766002272016132 0ustar00rootroot================= org.bluez.Adapter ================= ------------------------------------- BlueZ D-Bus Adapter API documentation ------------------------------------- :Version: BlueZ :Date: October 2023 :Manual section: 5 :Manual group: Linux System Administration Interface ========= :Service: org.bluez :Interface: org.bluez.Adapter1 :Object path: [variable prefix]/{hci0,hci1,...} Methods ------- void StartDiscovery() ````````````````````` Starts device discovery session which may include starting an inquiry and/or scanning procedures and remote device name resolving. Use **StopDiscovery** to release the sessions acquired. This process will start creating Device objects as new devices are discovered. During discovery RSSI delta-threshold is imposed. Each client can request a single device discovery session per adapter. Possible errors: :org.bluez.Error.NotReady: :org.bluez.Error.Failed: :org.bluez.Error.InProgress: void StopDiscovery() ```````````````````` Stops device discovery session started by **StartDiscovery**. Note that a discovery procedure is shared between all discovery sessions thus calling StopDiscovery will only release a single session and discovery will stop when all sessions from all clients have finished. Possible errors: :org.bluez.Error.NotReady: :org.bluez.Error.Failed: :org.bluez.Error.NotAuthorized: void RemoveDevice(object device) ```````````````````````````````` Removes the remote device object at the given path including cahed information such as bonding information. Possible errors: :org.bluez.Error.InvalidArguments: :org.bluez.Error.Failed: void SetDiscoveryFilter(dict filter) ```````````````````````````````````` Sets the device discovery filter for the caller. When this method is called with no filter parameter, filter is removed. Possible filter values: :array{string} UUIDs: Filter by service UUIDs, empty means match *any* UUID. When a remote device is found that advertises any UUID from UUIDs, it will be reported if: - **Pathloss** and **RSSI** are both empty. - only **Pathloss** param is set, device advertise TX power, and computed pathloss is less than Pathloss param. - only **RSSI** param is set, and received RSSI is higher than RSSI param. :int16 RSSI: RSSI threshold value. PropertiesChanged signals will be emitted for already existing Device objects, with updated RSSI value. If one or more discovery filters have been set, the RSSI delta-threshold, that is imposed by StartDiscovery by default, will not be applied. :uint16 Pathloss: Pathloss threshold value. PropertiesChanged signals will be emitted for already existing Device objects, with updated Pathloss value. :string Transport (Default "auto"): Transport parameter determines the type of scan. Possible values: :"auto": Interleaved scan, use LE, BREDR, or both, depending on what's currently enabled. :"bredr": BR/EDR inquiry only. :"le": LE scan only. :bool DuplicateData (Default false): Disables duplicate detection of advertisement data. When enabled PropertiesChanged signals will be generated for either ManufacturerData and ServiceData everytime they are discovered. :bool Discoverable (Default false): Make adapter discoverable while discovering, if the adapter is already discoverable setting this filter won't do anything. :string Pattern (Default none): Discover devices where the pattern matches either the prefix of the address or device name which is convenient way to limited the number of device objects created during a discovery. When set disregards device discoverable flags. Note: The pattern matching is ignored if there are other client that don't set any pattern as it work as a logical OR, also setting empty string "" pattern will match any device found. When discovery filter is set, Device objects will be created as new devices with matching criteria are discovered regardless of they are connectable or discoverable which enables listening to non-connectable and non-discoverable devices. When multiple clients call SetDiscoveryFilter, their filters are internally merged, and notifications about new devices are sent to all clients. Therefore, each client must check that device updates actually match its filter. When SetDiscoveryFilter is called multiple times by the same client, last filter passed will be active for given client. SetDiscoveryFilter can be called before StartDiscovery. It is useful when client will create first discovery session, to ensure that proper scan will be started right after call to StartDiscovery. Possible errors: :org.bluez.Error.NotReady: :org.bluez.Error.NotSupported: :org.bluez.Error.Failed: array{string} GetDiscoveryFilters() ``````````````````````````````````` Returns available filters that can be given to **SetDiscoveryFilter**. Possible errors: None object ConnectDevice(dict properties) [experimental] ```````````````````````````````````````````````````` connects to device without need of performing General Discovery. Connection mechanism is similar to Connect method on **org.bluez.Device1(5)** interface with exception that this method returns success when physical connection is established and you can specify bearer to connect with parameter. After this method returns, services discovery will continue and any supported profile will be connected. There is no need for calling Connect on Device1 after this call. If connection was successful this method returns object path to created device object or device that already exist. Possible properties values: :string Address (Mandatory): The Bluetooth device address of the remote device. :string AddressType (Default "BR/EDR"): The Bluetooth device Address Type. This is address type that should be used for initial connection. Possible values: :"public": Public address :"random": Random address Possible errors: :org.bluez.Error.InvalidArguments: :org.bluez.Error.AlreadyExists: :org.bluez.Error.NotSupported: :org.bluez.Error.NotReady: :org.bluez.Error.Failed: Properties ---------- string Address [readonly] ````````````````````````` The Bluetooth device address. string AddressType [readonly] ````````````````````````````` The Bluetooth Address Type. For dual-mode and BR/EDR only adapter this defaults to "public". Single mode LE adapters may have either value. With privacy enabled this contains type of Identity Address and not type of address used for connection. Possible values: :"public": Public address. :"random: Random address. string Name [readonly] `````````````````````` The Bluetooth system name (pretty hostname). This property is either a static system default or controlled by an external daemon providing access to the pretty hostname configuration. string Alias [readwrite] ```````````````````````` The Bluetooth friendly name. This value can be changed. In case no alias is set, it will return the system provided name. Setting an empty string as alias will convert it back to the system provided name. When resetting the alias with an empty string, the property will default back to system name. On a well configured system, this property never needs to be changed since it defaults to the system name and provides the pretty hostname. Only if the local name needs to be different from the pretty hostname, this property should be used as last resort. uint32 Class [readonly] ``````````````````````` The Bluetooth class of device. This property represents the value that is either automatically configured by DMI/ACPI information or provided as static configuration. boolean Connectable [readwrite] ``````````````````````````````` Set an adapter to connectable or non-connectable. This is a global setting and should only be used by the settings application. Setting this property to false will set the Discoverable property of the adapter to false as well, which will not be reverted if if Connectable is set back to true. If required, the application will need to manually set Discoverable to true. Note that this property only affects incoming connections. boolean Powered [readwrite] ``````````````````````````` Switch an adapter on or off. This will also set the appropriate connectable state of the controller. The value of this property is not persistent. After restart or unplugging of the adapter it will reset back to false. string PowerState [readonly, experimental] `````````````````````````````````````````` The power state of an adapter. The power state will show whether the adapter is turning off, or turning on, as well as being on or off. Possible values: :"on": Powered on. :"off": Powered off :"off-enabling": Transitioning from "off" to "on". :"on-disabling": Transitioning from "on" to "off". :"off-blocked": Blocked by rfkill. boolean Discoverable [readwrite] (Default: false) ````````````````````````````````````````````````` Switch an adapter to discoverable or non-discoverable to either make it visible or hide it. This is a global setting and should only be used by the settings application. If the DiscoverableTimeout is set to a non-zero value then the system will set this value back to false after the timer expired. In case the adapter is switched off, setting this value will fail. When changing the Powered property the new state of this property will be updated via a PropertiesChanged signal. boolean Pairable [readwrite] (Default: true) ```````````````````````````````````````````` Switch an adapter to pairable or non-pairable. This is a global setting and should only be used by the settings application. Note that this property only affects incoming pairing requests. uint32 PairableTimeout [readwrite] (Default: 0) ``````````````````````````````````````````````` The pairable timeout in seconds. A value of zero means that the timeout is disabled and it will stay in pairable mode forever. uint32 DiscoverableTimeout [readwrite] (Default: 180) ````````````````````````````````````````````````````` The discoverable timeout in seconds. A value of zero means that the timeout is disabled and it will stay in discoverable/limited mode forever. boolean Discovering [readonly] `````````````````````````````` Indicates that a device discovery procedure is active. array{string} UUIDs [readonly] `````````````````````````````` List of 128-bit UUIDs that represents the available local services. string Modalias [readonly, optional] ```````````````````````````````````` Local Device ID information in modalias format used by the kernel and udev. array{string} Roles [readonly] `````````````````````````````` List of supported roles. Possible values: :"central": Supports the central role. :"peripheral": Supports the peripheral role. :"central-peripheral": Supports both roles concurrently. array{string} ExperimentalFeatures [readonly, optional] ``````````````````````````````````````````````````````` List of 128-bit UUIDs that represents the experimental features currently enabled. uint16 Manufacturer [readonly] `````````````````````````````` The manufacturer of the device, as a uint16 company identifier defined by the Core Bluetooth Specification. byte Version [readonly] ``````````````````````` The Bluetooth version supported by the device, as a core version code defined by the Core Bluetooth Specification. bluez-5.82/doc/PaxHeaders/org.bluez.obex.FileTransfer.rst0000644000000000000000000000005014536422313020376 xustar0020 atime=1743516816 20 ctime=1743591290 bluez-5.82/doc/org.bluez.obex.FileTransfer.rst0000644000000000000000000000701114536422313020056 0ustar00rootroot=========================== org.bluez.obex.FileTransfer =========================== ----------------------------------------------- BlueZ D-Bus OBEX FileTransfer API documentation ----------------------------------------------- :Version: BlueZ :Date: October 2023 :Manual section: 5 :Manual group: Linux System Administration Interface ========= :Service: org.bluez.obex :Interface: org.bluez.obex.FileTransfer1 :Object path: [Session object path] Methods ------- void ChangeFolder(string folder) ```````````````````````````````` Changes the current folder of the remote device. Possible errors: :org.bluez.obex.Error.InvalidArguments: :org.bluez.obex.Error.Failed: void CreateFolder(string folder) ```````````````````````````````` Creates a new folder in the remote device. Possible errors: :org.bluez.obex.Error.InvalidArguments: :org.bluez.obex.Error.Failed: array{dict} ListFolder() ```````````````````````` Returns a dictionary containing information about the current folder content. Possible return values: :string Name: Object name in UTF-8 format. :string Type: Either "folder" or "file". :uint64 Size: Object size or number of items in folder. :string Permission: Group, owner and other permission. :uint64 Modified: Last change. :uint64 Accessed: Last access. :uint64 Created: Creation date. Possible errors: :org.bluez.obex.Error.Failed: object, dict GetFile(string targetfile, string sourcefile) `````````````````````````````````````````````````````````` Copies the contents of the source file (from remote device) to the target file (on local filesystem). If an empty target file is given, a name will be automatically generated for the temporary file. The returned path represents the newly created transfer, which should be used to find out if the content has been successfully transferred or if the operation fails. The properties of this transfer are also returned along with the object path, to avoid a call to GetProperties, see **org.bluez.obex.Transfer(5)** for the possible list of properties. Possible errors: :org.bluez.obex.Error.InvalidArguments: :org.bluez.obex.Error.Failed: object, dict PutFile(string sourcefile, string targetfile) `````````````````````````````````````````````````````````` Copies the contents of the source file (from local filesystem) to the target file (on remote device). The returned path represents the newly created transfer, which should be used to find out if the content has been successfully transferred or if the operation fails. The properties of this transfer are also returned along with the object path, to avoid a call to GetProperties, see **org.bluez.obex.Transfer(5)** for the possible list of properties. Possible errors: :org.bluez.obex.Error.InvalidArguments: :org.bluez.obex.Error.Failed: void CopyFile(string sourcefile, string targetfile) ``````````````````````````````````````````````````` Copies the contents from source file to target file on the remote device. Possible errors: :org.bluez.obex.Error.InvalidArguments: :org.bluez.obex.Error.Failed: void MoveFile(string sourcefile, string targetfile) ``````````````````````````````````````````````````` Moves a file within the remote device from source file to the target file. Possible errors: ;org.bluez.obex.Error.InvalidArguments: :org.bluez.obex.Error.Failed: void Delete(string file) ```````````````````````` Deletes the specified file/folder. Possible errors: :org.bluez.obex.Error.InvalidArguments: :org.bluez.obex.Error.Failed: bluez-5.82/doc/PaxHeaders/org.bluez.NetworkServer.rst0000644000000000000000000000005014536422313017676 xustar0020 atime=1743516784 20 ctime=1743591290 bluez-5.82/doc/org.bluez.NetworkServer.rst0000644000000000000000000000275314536422313017366 0ustar00rootroot======================= org.bluez.NetworkServer ======================= ------------------------------------------- BlueZ D-Bus NetworkServer API documentation ------------------------------------------- :Version: BlueZ :Date: October 2023 :Manual section: 5 :Manual group: Linux System Administration Interface ========= :Service: org.bluez :Interface: org.bluez.NetworkServer1 :Object path: /org/bluez/{hci0,hci1,...} Methods ------- void Register(string uuid, string bridge) ````````````````````````````````````````` Registers server for the provided UUID. Every new connection to this server will be added the bridge interface. Possible uuid values: :"panu", "00001115-0000-1000-8000-00805f9b34fb": Personal Network User role. :"nap", "00001116-0000-1000-8000-00805f9b34fb": Network Access Point role. :"gn", "00001117-0000-1000-8000-00805f9b34fb": Group Network role. Initially no network server SDP is provided. Only after this method a SDP record will be available and the BNEP server will be ready for incoming connections. Possible errors: :org.bluez.Error.InvalidArguments: :org.bluez.Error.AlreadyExists: :org.bluez.Error.Failed: void Unregister(string uuid) ```````````````````````````` Unregisters the server for provided UUID which was previously registered with **Register()** method. All servers will be automatically unregistered when the calling application terminates. Possible errors: :org.bluez.Error.InvalidArguments: :org.bluez.Error.Failed: bluez-5.82/doc/PaxHeaders/org.bluez.Media.50000644000000000000000000000005014773213447015442 xustar0020 atime=1743591207 20 ctime=1743591291 bluez-5.82/doc/org.bluez.Media.50000644000000000000000000000767714773213447015144 0ustar00rootroot.\" Man page generated from reStructuredText. . . .nr rst2man-indent-level 0 . .de1 rstReportMargin \\$1 \\n[an-margin] level \\n[rst2man-indent-level] level margin: \\n[rst2man-indent\\n[rst2man-indent-level]] - \\n[rst2man-indent0] \\n[rst2man-indent1] \\n[rst2man-indent2] .. .de1 INDENT .\" .rstReportMargin pre: . RS \\$1 . nr rst2man-indent\\n[rst2man-indent-level] \\n[an-margin] . nr rst2man-indent-level +1 .\" .rstReportMargin post: .. .de UNINDENT . RE .\" indent \\n[an-margin] .\" old: \\n[rst2man-indent\\n[rst2man-indent-level]] .nr rst2man-indent-level -1 .\" new: \\n[rst2man-indent\\n[rst2man-indent-level]] .in \\n[rst2man-indent\\n[rst2man-indent-level]]u .. .TH "ORG.BLUEZ.MEDIA" "5" "September 2023" "BlueZ" "Linux System Administration" .SH NAME org.bluez.Media \- BlueZ D-Bus Media API documentation .SH INTERFACE .INDENT 0.0 .TP .B Service org.bluez .TP .B Interface org.bluez.Media1 .TP .B Object path [variable prefix]/{hci0,hci1,...} .UNINDENT .SS Methods .SS void RegisterEndpoint(object endpoint, dict properties) .INDENT 0.0 .INDENT 3.5 Register a local end point to sender, the sender can register as many end points as it likes. .sp Note: If the sender disconnects the end points are automatically unregistered. .sp possible properties: .INDENT 0.0 .TP .B string UUID UUID of the profile which the endpoint is for. .sp UUID must be in the list of SupportedUUIDS. .TP .B byte Codec Assigned number of codec that the endpoint implements. The values should match the profile specification which is indicated by the UUID. .TP .B uint32_t Vendor [Optional] Vendor\-specific Company ID, Codec ID tuple that the endpoint implements. .sp It shall be set to appropriate value when Vendor Specific Codec (0xff) is used. .TP .B array{byte} Capabilities Capabilities blob, it is used as it is so the size and byte order must match. .TP .B array{byte} Metadata [Optional] Metadata blob, it is used as it is so the size and byte order must match. .UNINDENT .sp Possible Errors: .INDENT 0.0 .TP .B org.bluez.Error.InvalidArguments .TP .B org.bluez.Error.NotSupported emitted when interface for the end\-point is disabled .UNINDENT .UNINDENT .UNINDENT .SS void UnregisterEndpoint(object endpoint) .INDENT 0.0 .INDENT 3.5 Unregister sender end point. .UNINDENT .UNINDENT .SS void RegisterPlayer(object player, dict properties) .INDENT 0.0 .INDENT 3.5 Register a media player object to sender, the sender can register as many objects as it likes. .sp Object must implement at least org.mpris.MediaPlayer2.Player as defined in MPRIS 2.2 spec: .INDENT 0.0 .INDENT 3.5 .UNINDENT .UNINDENT .sp Note: If the sender disconnects its objects are automatically unregistered. .sp Possible Errors: .INDENT 0.0 .TP .B org.bluez.Error.InvalidArguments .TP .B org.bluez.Error.NotSupported .UNINDENT .UNINDENT .UNINDENT .SS void UnregisterPlayer(object player) .INDENT 0.0 .INDENT 3.5 Unregister sender media player. .UNINDENT .UNINDENT .SS void RegisterApplication(object root, dict options) .INDENT 0.0 .INDENT 3.5 Register endpoints an player objects within root object which must implement ObjectManager. .sp The application object path together with the D\-Bus system bus connection ID define the identification of the application. .sp Possible errors: .INDENT 0.0 .TP .B org.bluez.Error.InvalidArguments .TP .B org.bluez.Error.AlreadyExists .UNINDENT .UNINDENT .UNINDENT .SS void UnregisterApplication(object application) .INDENT 0.0 .INDENT 3.5 This unregisters the services that has been previously registered. The object path parameter must match the same value that has been used on registration. .sp Possible errors: .INDENT 0.0 .TP .B org.bluez.Error.InvalidArguments .TP .B org.bluez.Error.DoesNotExist .UNINDENT .UNINDENT .UNINDENT .SS Properties .SS array{string} SupportedUUIDs [readonly] .INDENT 0.0 .INDENT 3.5 List of 128\-bit UUIDs that represents the supported Endpoint registration. .UNINDENT .UNINDENT .\" Generated by docutils manpage writer. . bluez-5.82/doc/PaxHeaders/org.bluez.BatteryProviderManager.rst0000644000000000000000000000005014536422313021476 xustar0020 atime=1743516787 20 ctime=1743591290 bluez-5.82/doc/org.bluez.BatteryProviderManager.rst0000644000000000000000000000316614536422313021165 0ustar00rootroot================================ org.bluez.BatteryProviderManager ================================ ---------------------------------------------------- BlueZ D-Bus BatteryProviderManager API documentation ---------------------------------------------------- :Version: BlueZ :Date: October 2023 :Manual section: 5 :Manual group: Linux System Administration Description ============ A battery provider starts by registering itself as a battery provider with the **RegisterBatteryProvider()** method passing an object path as the provider ID. Then, it can start exposing **org.bluez.BatteryProvider(5)** objects having the path starting with the given provider ID. It can also remove objects at any time. The objects and their properties exposed by battery providers will be reflected on **org.bluez.Battery(5)** interface. **bluetoothd(8)** will stop monitoring these exposed and removed objects after UnregisterBatteryProvider is called for that provider ID. Interface ========= :Service: org.bluez :Interface: org.bluez.BatteryProviderManager1 :Object path: /org/bluez/{hci0,hci1,...} Methods ------- void RegisterBatteryProvider(object provider) ````````````````````````````````````````````` Registers a battery provider. A registered battery provider can then expose objects with **org.bluez.BatteryProvider(5)** interface. void UnregisterBatteryProvider(object provider) ``````````````````````````````````````````````` Unregisters a battery provider previously registered with **RegisterBatteryProvider()**. After unregistration, the **org.bluez.BatteryProvider(5)** objects provided by this client are ignored by **bluetoothd(8)**. bluez-5.82/doc/PaxHeaders/health-api.txt0000644000000000000000000000005012066112416015167 xustar0020 atime=1743516877 20 ctime=1743591290 bluez-5.82/doc/health-api.txt0000644000000000000000000000764512066112416014664 0ustar00rootrootBlueZ D-Bus Health API description ********************************** HealthManager hierarchy ======================= Service org.bluez Interface org.bluez.HealthManager1 Object path /org/bluez/ Methods object CreateApplication(dict config) Returns the path of the new registered application. Application will be closed by the call or implicitly when the programs leaves the bus. config: uint16 DataType: Mandatory string Role: Mandatory. Possible values: "source", "sink" string Description: Optional ChannelType: Optional, just for sources. Possible values: "reliable", "streaming" Possible Errors: org.bluez.Error.InvalidArguments void DestroyApplication(object application) Closes the HDP application identified by the object path. Also application will be closed if the process that started it leaves the bus. Only the creator of the application will be able to destroy it. Possible errors: org.bluez.Error.InvalidArguments org.bluez.Error.NotFound org.bluez.Error.NotAllowed HealthDevice hierarchy ====================== Service org.bluez Interface org.bluez.HealthDevice1 Object path [variable prefix]/{hci0,hci1,...}/dev_XX_XX_XX_XX_XX_XX Methods boolean Echo() Sends an echo petition to the remote service. Returns True if response matches with the buffer sent. If some error is detected False value is returned. Possible errors: org.bluez.Error.InvalidArguments org.bluez.Error.OutOfRange object CreateChannel(object application, string configuration) Creates a new data channel. The configuration should indicate the channel quality of service using one of this values "reliable", "streaming", "any". Returns the object path that identifies the data channel that is already connected. Possible errors: org.bluez.Error.InvalidArguments org.bluez.Error.HealthError void DestroyChannel(object channel) Destroys the data channel object. Only the creator of the channel or the creator of the HealthApplication that received the data channel will be able to destroy it. Possible errors: org.bluez.Error.InvalidArguments org.bluez.Error.NotFound org.bluez.Error.NotAllowed Signals void ChannelConnected(object channel) This signal is launched when a new data channel is created or when a known data channel is reconnected. void ChannelDeleted(object channel) This signal is launched when a data channel is deleted. After this signal the data channel path will not be valid and its path can be reused for future data channels. Properties object MainChannel [readonly] The first reliable channel opened. It is needed by upper applications in order to send specific protocol data units. The first reliable can change after a reconnection. HealthChannel hierarchy ======================= Service org.bluez Interface org.bluez.HealthChannel1 Object path [variable prefix]/{hci0,hci1,...}/dev_XX_XX_XX_XX_XX_XX/chanZZZ Only the process that created the data channel or the creator of the HealthApplication that received it will be able to call these methods. Methods fd Acquire() Returns the file descriptor for this data channel. If the data channel is not connected it will also reconnect. Possible Errors: org.bluez.Error.NotConnected org.bluez.Error.NotAllowed void Release() Releases the fd. Application should also need to close() it. Possible Errors: org.bluez.Error.NotAcquired org.bluez.Error.NotAllowed Properties string Type [readonly] The quality of service of the data channel. ("reliable" or "streaming") object Device [readonly] Identifies the Remote Device that is connected with. Maps with a HealthDevice object. object Application [readonly] Identifies the HealthApplication to which this channel is related to (which indirectly defines its role and data type). bluez-5.82/doc/PaxHeaders/l2cap.rst0000644000000000000000000000005014766002272014154 xustar0020 atime=1743515577 20 ctime=1743591290 bluez-5.82/doc/l2cap.rst0000644000000000000000000001535714766002272013650 0ustar00rootroot===== l2cap ===== -------------- L2CAP protocol -------------- :Version: BlueZ :Copyright: Free use of this software is granted under the terms of the GNU Lesser General Public Licenses (LGPL). :Date: May 2024 :Manual section: 7 :Manual group: Linux System Administration SYNOPSIS ======== .. code-block:: #include #include #include l2cap_socket = socket(PF_BLUETOOTH, SOCK_SEQPACKET, BTPROTO_L2CAP); DESCRIPTION =========== L2CAP is a protocol that provides an interface for higher-level protocols to send and receive data over a Bluetooth connection. L2CAP sits on top of the Bluetooth Host Controller Interface (HCI) and provides a set of channels that can be used by higher-level protocols to transmit data. L2CAP provides a number of services to higher-level protocols, including segmentation and reassembly of large data packets and flow control to prevent overloading of the receiver. L2CAP also supports multiple channels per connection, allowing for concurrent data transmission using different protocols. SOCKET ADDRESS ============== .. code-block:: struct sockaddr_l2 { sa_family_t l2_family; unsigned short l2_psm; bdaddr_t l2_bdaddr; unsigned short l2_cid; uint8_t l2_bdaddr_type; }; Example: .. code-block:: struct sockaddr_l2 addr; memset(&addr, 0, sizeof(addr)); addr.l2_family = AF_BLUETOOTH; bacpy(&addr.l2_bdaddr, bdaddr); if (cid) addr.l2_cid = htobs(cid); else addr.l2_psm = htobs(psm); addr.l2_bdaddr_type = bdaddr_type; SOCKET OPTIONS ============== The socket options listed below can be set by using **setsockopt(2)** and read with **getsockopt(2)** with the socket level set to SOL_BLUETOOTH. BT_SECURITY (since Linux 2.6.30) -------------------------------- Channel security level, possible values: .. csv-table:: :header: "Value", "Security Level", "Link Key Type", "Encryption" :widths: auto **BT_SECURITY_SDP**, 0 (SDP Only), None, Not required **BT_SECURITY_LOW**, 1 (Low), Unauthenticated, Not required **BT_SECURITY_MEDIUM**, 2 (Medium - default), Unauthenticated, Desired **BT_SECURITY_HIGH**, 3 (High), Authenticated, Required **BT_SECURITY_FIPS** (since Linux 3.15), 4 (Secure Only), Authenticated (P-256 based Secure Simple Pairing and Secure Authentication), Required Example: .. code-block:: int level = BT_SECURITY_HIGH; int err = setsockopt(l2cap_socket, SOL_BLUETOOTH, BT_SECURITY, &level, sizeof(level)); if (err == -1) { perror("setsockopt"); return 1; } BT_DEFER_SETUP (since Linux 2.6.30) ----------------------------------- Channel defer connection setup, this control if the connection procedure needs to be authorized by userspace before responding which allows authorization at profile level, possible values: .. csv-table:: :header: "Value", "Description", "Authorization" :widths: auto **0**, Disable (default), Not required **1**, Enable, Required Example: .. code-block:: int defer_setup = 1; int err = setsockopt(l2cap_socket, SOL_BLUETOOTH, BT_DEFER_SETUP, &defer_setup, sizeof(defer_setup)); if (err == -1) { perror("setsockopt"); return err; } err = listen(l2cap_socket, 5); if (err) { perror("listen"); return err; } struct sockaddr_l2 remote_addr = {0}; socklen_t addr_len = sizeof(remote_addr); int new_socket = accept(l2cap_socket, (struct sockaddr*)&remote_addr, &addr_len); if (new_socket < 0) { perror("accept"); return new_socket; } /* To complete the connection setup of new_socket read 1 byte */ char c; struct pollfd pfd; memset(&pfd, 0, sizeof(pfd)); pfd.fd = new_socket; pfd.events = POLLOUT; err = poll(&pfd, 1, 0); if (err) { perror("poll"); return err; } if (!(pfd.revents & POLLOUT)) { err = read(sk, &c, 1); if (err < 0) { perror("read"); return err; } } BT_FLUSHABLE (since Linux 2.6.39) --------------------------------- Channel flushable flag, this control if the channel data can be flushed or not, possible values: .. csv-table:: :header: "Define", "Value", "Description" :widths: auto **BT_FLUSHABLE_OFF**, 0x00 (default), Do not flush data **BT_FLUSHABLE_ON**, 0x01, Flush data BT_POWER (since Linux 3.1) -------------------------- Channel power policy, this control if the channel shall force exit of sniff mode or not, possible values: .. csv-table:: :header: "Define", "Value", "Description" :widths: auto **BT_POWER_FORCE_ACTIVE_OFF**, 0x00 (default), Don't force exit of sniff mode **BT_POWER_FORCE_ACTIVE_ON**, 0x01, Force exit of sniff mode BT_CHANNEL_POLICY (since Linux 3.10) ------------------------------------ High-speed (AMP) channel policy, possible values: .. csv-table:: :header: "Define", "Value", "Description" :widths: auto **BT_CHANNEL_POLICY_BREDR_ONLY**, 0 (default), BR/EDR only **BT_CHANNEL_POLICY_BREDR_PREFERRED**, 1, BR/EDR Preferred **BT_CHANNEL_POLICY_BREDR_PREFERRED**, 2, AMP Preferred BT_PHY (since Linux 5.10) ------------------------- Channel supported PHY(s), possible values: .. csv-table:: :header: "Define", "Value", "Description" :widths: auto **BT_PHY_BR_1M_1SLOT**, BIT 0, BR 1Mbps 1SLOT **BT_PHY_BR_1M_3SLOT**, BIT 1, BR 1Mbps 3SLOT **BT_PHY_BR_1M_5SLOT**, BIT 2, BR 1Mbps 5SLOT **BT_PHY_BR_2M_1SLOT**, BIT 3, EDR 2Mbps 1SLOT **BT_PHY_BR_2M_3SLOT**, BIT 4, EDR 2Mbps 3SLOT **BT_PHY_BR_2M_5SLOT**, BIT 5, EDR 2Mbps 5SLOT **BT_PHY_BR_3M_1SLOT**, BIT 6, EDR 3Mbps 1SLOT **BT_PHY_BR_3M_3SLOT**, BIT 7, EDR 3Mbps 3SLOT **BT_PHY_BR_3M_5SLOT**, BIT 8, EDR 3Mbps 5SLOT **BT_PHY_LE_1M_TX**, BIT 9, LE 1Mbps TX **BT_PHY_LE_1M_RX**, BIT 10, LE 1Mbps RX **BT_PHY_LE_2M_TX**, BIT 11, LE 2Mbps TX **BT_PHY_LE_2M_RX**, BIT 12, LE 2Mbps RX **BT_PHY_LE_CODED_TX**, BIT 13, LE Coded TX **BT_PHY_LE_CODED_RX**, BIT 14, LE Coded RX BT_MODE (since Linux 5.10) -------------------------- Channel Mode, possible values: .. csv-table:: :header: "Define", "Value", "Description", "Link" :widths: auto **BT_MODE_BASIC**, 0x00 (default), Basic mode, Any **BT_MODE_ERTM**, 0x01, Enhanced Retransmission mode, BR/EDR **BT_MODE_STREAM**, 0x02, Stream mode, BR/EDR **BT_MODE_LE_FLOWCTL**, 0x03, Credit based flow control mode, LE **BT_MODE_EXT_FLOWCTL**, 0x04, Extended Credit based flow control mode, Any RESOURCES ========= http://www.bluez.org REPORTING BUGS ============== linux-bluetooth@vger.kernel.org SEE ALSO ======== socket(7), l2test(1) bluez-5.82/doc/PaxHeaders/btsnoop.txt0000644000000000000000000000005012721624577014655 xustar0020 atime=1743516877 20 ctime=1743591290 bluez-5.82/doc/btsnoop.txt0000644000000000000000000000703412721624577014342 0ustar00rootrootBTSnoop/Monitor protocol formats ******************************** Opcode definitions ================== New Index --------- Code: 0x0000 Parameters: Type (1 Octet Bus (1 Octet) BD_Addr (6 Octets) Name (8 Octets) This opcode indicates that a new controller instance with a given index was added. With some protocols, like the TTY-based one there is only a single supported controller, meaning the index is implicitly 0. Deleted Index ------------- Code: 0x0001 This opcode indicates that the controller with a specific index was removed. Command Packet -------------- Code: 0x0002 HCI command packet. Event Packet ------------ Code: 0x0003 HCI event packet. ACL TX Packet ------------- Code: 0x0004 Outgoing ACL packet. ACL RX Packet ------------- Code: 0x0005 Incoming ACL packet. SCO TX Packet -------------- Code: 0x0006 Outgoing SCO packet. SCO RX Packet ------------- Code: 0x0007 Incomnig SCO packet. Open Index ---------- Code: 0x0008 The HCI transport for the specified controller has been opened. Close Index ----------- Code: 0x0009 The HCI transport for the specified controller has been closed. Index Information ----------------- Code: 0x000a Parameters: BD_Addr (6 Octets) Manufacturer (2 Octets) Information about a specific controller. Vendor Diagnostics ------------------ Code: 0x000b Vendor diagnostic information. System Note ----------- Code: 0x000c System note. User Logging ------------ Code: 0x000d Parameters: Priority (1 Octet) Ident_Length (1 Octet) Ident (Ident_Length Octets) User logging information. TTY-based protocol ================== This section covers the protocol that can be parsed by btmon when passing it the --tty parameter. The protocol is little endian, packet based, and has the following header for each packet: struct tty_hdr { uint16_t data_len; uint16_t opcode; uint8_t flags; uint8_t hdr_len; uint8_t ext_hdr[0]; } __attribute__ ((packed)); The actual payload starts at ext_hdr + hdr_len and has the length of data_len - 4 - hdr_len. Each field of the header is defined as follows: data_len: This is the total length of the entire packet, excuding the data_len field itself. opcode: The BTSnoop opcode flags: Special flags for the packet. Currently no flags are defined. hdr_len: Length of the extended header. ext_hdr: This is a sequence of header extension fields formatted as: struct { uint8_t type; uint8_t value[length]; } The length of the value is dependent on the type. Currently the following types are defined: Type Length Meaning ---------------------------------------------------------------- 1 Command drops 1 byte Dropped HCI command packets 2 Event drops 1 byte Dropped HCI event packets 3 ACL TX drops 1 byte Dropped ACL TX packets 4 ACL RX drops 1 byte Dropped ACL RX packets 5 SCO TX drops 1 byte Dropped SCO TX packets 6 SCO RX drops 1 byte Dropped SCO RX packets 7 Other drops 1 byte Dropped other packets 8 32-bit timestamp 4 bytes Timestamp in 1/10th ms The drops fields indicate the number of packets that the implementation had to drop (e.g. due to lack of buffers) since the last reported drop count. The fields of the extended header must be sorted by increasing type. This is essential so that unknown types can be ignored and the parser can jump to processing the payload. bluez-5.82/doc/PaxHeaders/org.bluez.MediaAssistant.rst0000644000000000000000000000005014667536076020010 xustar0020 atime=1743516800 20 ctime=1743591290 bluez-5.82/doc/org.bluez.MediaAssistant.rst0000644000000000000000000000314414667536076017473 0ustar00rootroot======================== org.bluez.MediaAssistant ======================== -------------------------------------------- BlueZ D-Bus MediaAssistant API documentation -------------------------------------------- :Version: BlueZ :Date: June 2024 :Manual section: 5 :Manual group: Linux System Administration Interface ========= :Service: org.bluez :Interface: org.bluez.MediaAssistant1 :Object path: /org/bluez/{hci0,hci1,...}/src_XX_XX_XX_XX_XX_XX/dev_YY_YY_YY_YY_YY_YY/bisZ Methods ------- void Push(dict properties) ```````````````````````````````````````````````````````` Send stream information to the remote device. :dict properties: Indicate stream properties that will be sent to the peer. Values: :array{byte} Metadata [ISO only]: See Metadata property. :dict QoS [ISO only]: See QoS property. Properties ---------- string State [readonly] ``````````````````````` Indicates the state of the assistant object. Possible values are: :"idle": assistant object was created for the stream :"pending": assistant object was pushed (stream information was sent to the peer) :"requesting": remote device requires Broadcast_Code :"active": remote device started receiving stream array{byte} Metadata [readwrite, ISO Only, experimental] ```````````````````````````````````````````````````````` Indicates stream Metadata. dict QoS [readwrite, ISO only, experimental] ````````````````````````````````````````````````````` Indicates stream QoS capabilities. Values: :byte Encryption: Indicates whether the stream is encrypted. :array{byte} BCode Indicates Broadcast_Code to decrypt stream. bluez-5.82/doc/PaxHeaders/test-coverage.txt0000644000000000000000000000005013004645467015735 xustar0020 atime=1743516877 20 ctime=1743591290 bluez-5.82/doc/test-coverage.txt0000644000000000000000000000435413004645467015424 0ustar00rootrootBlueZ test coverage ******************* Automated unit testing ====================== Application Count Description ------------------------------------------- test-crc 9 Link Layer CRC-24 checksum test-eir 14 EIR and AD parsing test-lib 14 SDP library functions test-sdp 133 SDP qualification test cases test-uuid 30 UUID conversion handling test-mgmt 9 Management interface handling test-crypto 5 Cryptographic toolbox helpers test-textfile 4 Old textfile storage format test-ringbuf 3 Ring buffer functionality test-queue 6 Queue handling functionality test-uhid 6 Userspace HID functionality test-hfp 29 HFP Audio Gateway functionality test-avdtp 60 AVDTP qualification test cases test-avctp 9 AVCTP qualification test cases test-avrcp 110 AVRCP qualification test cases test-gobex 31 Generic OBEX functionality test-gobex-packet 9 OBEX packet handling test-gobex-header 28 OBEX header handling test-gobex-apparam 18 OBEX apparam handling test-gobex-transfer 36 OBEX transfer handling test-gdbus-client 13 D-Bus client handling test-gatt 180 GATT qualification test cases test-hog 6 HID Over GATT qualification test cases ----- 761 Automated end-to-end testing ============================ Application Count Description ------------------------------------------- mgmt-tester 331 Kernel management interface testing l2cap-tester 33 Kernel L2CAP implementation testing rfcomm-tester 9 Kernel RFCOMM implementation testing bnep-tester 1 Kernel BNEP implementation testing smp-tester 8 Kernel SMP implementation testing sco-tester 8 Kernel SCO implementation testing gap-tester 1 Daemon D-Bus API testing hci-tester 14 Controller hardware testing userchan-tester 3 Kernel HCI User Channel testting ----- 408 Android end-to-end testing ========================== Application Count Description ------------------------------------------- android-tester 194 Android HAL interface testing ipc-tester 132 Android IPC resistance testing ----- 326 Android automated unit testing ============================== Application Count Description ------------------------------------------- test-ipc 14 Android IPC library functions ----- 14 bluez-5.82/doc/PaxHeaders/org.bluez.ProfileManager.50000644000000000000000000000005014773213434017312 xustar0020 atime=1743591196 20 ctime=1743591290 bluez-5.82/doc/org.bluez.ProfileManager.50000644000000000000000000001006314773213434016773 0ustar00rootroot.\" Man page generated from reStructuredText. . . .nr rst2man-indent-level 0 . .de1 rstReportMargin \\$1 \\n[an-margin] level \\n[rst2man-indent-level] level margin: \\n[rst2man-indent\\n[rst2man-indent-level]] - \\n[rst2man-indent0] \\n[rst2man-indent1] \\n[rst2man-indent2] .. .de1 INDENT .\" .rstReportMargin pre: . RS \\$1 . nr rst2man-indent\\n[rst2man-indent-level] \\n[an-margin] . nr rst2man-indent-level +1 .\" .rstReportMargin post: .. .de UNINDENT . RE .\" indent \\n[an-margin] .\" old: \\n[rst2man-indent\\n[rst2man-indent-level]] .nr rst2man-indent-level -1 .\" new: \\n[rst2man-indent\\n[rst2man-indent-level]] .in \\n[rst2man-indent\\n[rst2man-indent-level]]u .. .TH "ORG.BLUEZ.PROFILEMANAGER" "5" "October 2023" "BlueZ" "Linux System Administration" .SH NAME org.bluez.ProfileManager \- BlueZ D-Bus ProfileManager API documentation .SH INTERFACE .INDENT 0.0 .TP .B Service org.bluez .TP .B Interface org.bluez.ProfileManager1 .TP .B Object path /org/bluez .UNINDENT .SS Methods .SS void RegisterProfile(object profile, string uuid, dict options) .INDENT 0.0 .INDENT 3.5 Registers profile agent. .sp The object path defines the path of the profile that will be called when there is a connection and must implement \fBorg.bluez.Profile(5)\fP interface. .sp If an application disconnects from the bus all its registered profiles will be removed. .sp Possible uuid values: .INDENT 0.0 .TP .B \(dq0000111f\-0000\-1000\-8000\-00805f9b34fb\(dq HFP AG, default profile Version is 1.7, profile Features is 0b001001 and RFCOMM channel is 13. Authentication is required. .TP .B \(dq0000111e\-0000\-1000\-8000\-00805f9b34fb\(dq HFP HS, default profile Version is 1.7, profile Features is 0b000000 and RFCOMM channel is 7. Authentication is required. .TP .B \(dq00001112\-0000\-1000\-8000\-00805f9b34fb\(dq HSP AG, default profile Version is 1.2, RFCOMM channel is 12 and Authentication is required. Does not support any Features, option is ignored. .TP .B \(dq00001108\-0000\-1000\-8000\-00805f9b34fb\(dq HSP HS, default profile Version is 1.2, profile Features is 0b0 and RFCOMM channel is 6. Authentication is required. Features is one bit value, specify capability of Remote Audio Volume Control (by default turned off). .TP .B \(dq\(dq Vendor defined UUID, no defaults, must set options. .UNINDENT .sp Possible options values: .INDENT 0.0 .TP .B string Name Human readable name for the profile .TP .B string Service The primary service class UUID (if different from the actual profile UUID). .TP .B string Role For asymmetric profiles that do not have UUIDs available to uniquely identify each side this parameter allows specifying the precise local role. .sp Possible values: .INDENT 7.0 .TP .B \(dqclient\(dq .TP .B \(dqserver\(dq .UNINDENT .TP .B uint16 Channel RFCOMM channel number that is used for client and server UUIDs. .sp If applicable it will be used in the SDP record as well. .TP .B uint16 PSM PSM number that is used for client and server UUIDs. .sp If applicable it will be used in the SDP record as well. .TP .B boolean RequireAuthentication Pairing is required before connections will be established. No devices will be connected if not paired. .TP .B boolean RequireAuthorization Request authorization before any connection will be established. .TP .B boolean AutoConnect In case of a client UUID this will force connection of the RFCOMM or L2CAP channels when a remote device is connected. .TP .B string ServiceRecord Provide a manual SDP record. .TP .B uint16 Version Profile version (for SDP record) .TP .B uint16 Features Profile features (for SDP record) .UNINDENT .sp Possible errors: .INDENT 0.0 .TP .B org.bluez.Error.InvalidArguments .TP .B org.bluez.Error.AlreadyExists .UNINDENT .UNINDENT .UNINDENT .SS void UnregisterProfile(object profile) .INDENT 0.0 .INDENT 3.5 Unregisters profile object that has been previously registered using \fBRegisterProfile\fP\&. .sp The object path parameter must match the same value that has been used on registration. .sp Possible errors: .INDENT 0.0 .TP .B org.bluez.Error.DoesNotExist .UNINDENT .UNINDENT .UNINDENT .\" Generated by docutils manpage writer. . bluez-5.82/doc/PaxHeaders/org.bluez.MediaPlayer.rst0000644000000000000000000000005014711225433017251 xustar0020 atime=1743516795 20 ctime=1743591290 bluez-5.82/doc/org.bluez.MediaPlayer.rst0000644000000000000000000001267214711225433016742 0ustar00rootroot===================== org.bluez.MediaPlayer ===================== ----------------------------------------- BlueZ D-Bus MediaPlayer API documentation ----------------------------------------- :Version: BlueZ :Date: September 2023 :Manual section: 5 :Manual group: Linux System Administration Interface ========= :Service: org.bluez (Controller role) :Interface: org.bluez.MediaPlayer1 :Object path: [variable prefix]/{hci0,hci1,...}/dev_XX_XX_XX_XX_XX_XX/playerX Methods ------- void Play() ``````````` Resume playback. Possible Errors: :org.bluez.Error.NotSupported: :org.bluez.Error.Failed: void Pause() ```````````` Pause playback. Possible Errors: :org.bluez.Error.NotSupported: :org.bluez.Error.Failed: void Stop() ``````````` Stop playback. Possible Errors: :org.bluez.Error.NotSupported: :org.bluez.Error.Failed: void Next() ``````````` Next item. Possible Errors: :org.bluez.Error.NotSupported: :org.bluez.Error.Failed: void Previous() ``````````````` Previous item. Possible Errors: :org.bluez.Error.NotSupported: :org.bluez.Error.Failed: void FastForward() `````````````````` Fast forward playback, this action is only stopped when another method in this interface is called. Possible Errors: :org.bluez.Error.NotSupported: :org.bluez.Error.Failed: void Rewind() ````````````` Rewind playback, this action is only stopped when another method in this interface is called. Possible Errors: :org.bluez.Error.NotSupported: :org.bluez.Error.Failed: void Press(byte avc_key) ```````````````````````` Press a specific key to send as passthrough command. The key will be released automatically. Use Hold() instead if the intention is to hold down the key. Possible Errors: :org.bluez.Error.InvalidArguments: :org.bluez.Error.NotSupported: :org.bluez.Error.Failed: void Hold(byte avc_key) ``````````````````````` Press and hold a specific key to send as passthrough command. It is your responsibility to make sure that Release() is called after calling this method. The held key will also be released when any other method in this interface is called. Possible Errors: :org.bluez.Error.InvalidArguments: :org.bluez.Error.NotSupported: :org.bluez.Error.Failed: void Release() `````````````` Release the previously held key invoked using Hold(). Possible Errors: :org.bluez.Error.NotSupported: :org.bluez.Error.Failed: Properties ---------- string Equalizer [readwrite] ```````````````````````````` Indicates Player Equalizer setting. Possible values: :"off": :"on": string Repeat [readwrite] ````````````````````````` Indicates Player Repeat setting. Possible values: :"off": :"singletrack": :"alltracks": :"group": string Shuffle [readwrite] `````````````````````````` Indicates Player Suffle setting. Possible values: :"off": :"alltracks": :"group": string Scan [readwrite] ``````````````````````` Indicates Player Scan setting. Possible values: :"off": :"alltracks": :"group": string Status [readonly] ```````````````````````` Indicates Player Status setting. Possible status: :"playing": :"stopped": :"paused": :"forward-seek": :"reverse-seek": :"error": uint32 Position [readonly] `````````````````````````` Playback position in milliseconds. Changing the position may generate additional events that will be sent to the remote device. When position is 0 it means the track is starting and when it's greater than or equal to track's duration the track has ended. Note that even if duration is not available in metadata it's possible to signal its end by setting position to the maximum uint32 value. dict Track [readonly] ````````````````````` Track metadata. Possible values: :string Title: Track title name :string Artist: Track artist name :string Album: Track album name :string Genre: Track genre name :uint32 NumberOfTracks: Number of tracks in total :uint32 TrackNumber: Track number :uint32 Duration: Track duration in milliseconds :string ImgHandle: [experimental] Track image handle, available and valid only during the lifetime of an OBEX BIP connection to the ObexPort. object Device [readonly] ```````````````````````` Device object path. string Name [readonly] `````````````````````` Player name string Type [readonly] `````````````````````` Player type Possible values: "Audio" "Video" "Audio Broadcasting" "Video Broadcasting" string Subtype [readonly] ````````````````````````` Player subtype Possible values: "Audio Book" "Podcast" boolean Browsable [readonly] ```````````````````````````` If present indicates the player can be browsed using MediaFolder interface. Possible values: :True: Supported and active :False: Supported but inactive Note: If supported but inactive clients can enable it by using MediaFolder interface but it might interfere in the playback of other players. boolean Searchable [readonly] ````````````````````````````` If present indicates the player can be searched using MediaFolder interface. Possible values: :True: Supported and active :False: Supported but inactive Note: If supported but inactive clients can enable it by using MediaFolder interface but it might interfere in the playback of other players. object Playlist ``````````````` Playlist object path. uint16 ObexPort [readonly, experimental] ```````````````````````````````````````` If present indicates the player can get cover art using BIP over OBEX on this PSM port. bluez-5.82/doc/PaxHeaders/org.bluez.LEAdvertisingManager.rst0000644000000000000000000000005014643061455021056 xustar0020 atime=1743516807 20 ctime=1743591290 bluez-5.82/doc/org.bluez.LEAdvertisingManager.rst0000644000000000000000000000644514643061455020550 0ustar00rootroot============================== org.bluez.LEAdvertisingManager ============================== ------------------------------------------------- BlueZ D-Bus LEAvertisingManager API documentation ------------------------------------------------- :Version: BlueZ :Date: October 2023 :Manual section: 5 :Manual group: Linux System Administration Interface ========= The Advertising Manager allows external applications to register Advertisement Data which should be broadcast to devices. Advertisement Data elements must follow the API for LE Advertisement Data described above. :Service: org.bluez :Interface: org.bluez.LEAdvertisingManager1 :Object path: /org/bluez/{hci0,hci1,...} Methods ------- void RegisterAdvertisement(object advertisement, dict options) `````````````````````````````````````````````````````````````` Registers an advertisement object to be sent over the LE Advertising channel. The service must implement **org.bluez.LEAdvertisement(5)** interface. Possible errors: :org.bluez.Error.InvalidArguments: Indicates that the object has invalid or conflicting properties. :org.bluez.Error.AlreadyExists: Indicates the object is already registered. :org.bluez.Error.InvalidLength: Indicates that the data provided generates a data packet which is too long :org.bluez.Error.NotPermitted: Indicates the maximum number of advertisement instances has been reached. void UnregisterAdvertisement(object advertisement) `````````````````````````````````````````````````` Unregisters an advertisement that has been previously registered using **RegisterAdvertisement()**. The object path parameter must match the same value that has been used on registration. Possible errors: :org.bluez.Error.InvalidArguments: :org.bluez.Error.DoesNotExist: Properties ---------- byte ActiveInstances [readonly] ``````````````````````````````` Number of active advertising instances. byte SupportedInstances [readonly] `````````````````````````````````` Number of available advertising instances. array{string} SupportedIncludes [readonly] `````````````````````````````````````````` List of supported system includes. Possible values: :"tx-power": :"appearance": :"local-name": :"rsi": array{string} SupportedSecondaryChannels [readonly] ``````````````````````````````````````````````````` List of supported Secondary channels. Secondary channels can be used to advertise with the corresponding PHY. Possible values: :"1M": :"2M": :"Coded": dict SupportedCapabilities [readonly] ````````````````````````````````````` Enumerates Advertising-related controller capabilities useful to the client. Possible Values: :byte MaxAdvLen: Max advertising data length :byte MaxScnRspLen: Max advertising scan response length ;int16 MinTxPower: Min advertising tx power (dBm) :int16 MaxTxPower: Max advertising tx power (dBm) array{string} SupportedFeatures [readonly,optional] ``````````````````````````````````````````````````` List of supported platform features. If no features are available on the platform, the SupportedFeatures array will be empty. Possible values: :"CanSetTxPower": Indicates whether platform can specify tx power on each advertising instance. :"HardwareOffload": Indicates whether multiple advertising will be offloaded to the controller. bluez-5.82/doc/PaxHeaders/org.bluez.MediaTransport.rst0000644000000000000000000000005014766002272020015 xustar0020 atime=1743515578 20 ctime=1743591290 bluez-5.82/doc/org.bluez.MediaTransport.rst0000644000000000000000000001533714766002272017507 0ustar00rootroot======================== org.bluez.MediaTransport ======================== -------------------------------------------- BlueZ D-Bus MediaTransport API documentation -------------------------------------------- :Version: BlueZ :Date: July 2024 :Manual section: 5 :Manual group: Linux System Administration Interface ========= :Service: org.bluez :Interface: org.bluez.MediaTransport1 :Object path: [variable prefix]/{hci0,hci1,...}/dev_XX_XX_XX_XX_XX_XX/fdX Methods ------- fd, uint16, uint16 Acquire() ```````````````````````````` Acquire transport file descriptor and the MTU for read and write respectively. Possible Errors: :org.bluez.Error.NotAuthorized: :org.bluez.Error.Failed: fd, uint16, uint16 TryAcquire() ``````````````````````````````` Acquire transport file descriptor only if the transport is in "pending" state at the time the message is received by BlueZ. Otherwise no request will be sent to the remote device and the function will just fail with org.bluez.Error.NotAvailable. Possible Errors: :org.bluez.Error.NotAuthorized: :org.bluez.Error.Failed: :org.bluez.Error.NotAvailable: void Release() `````````````` Releases file descriptor. void Select() ````````````` Applicable only for transports created by a broadcast sink. This moves the transport from 'idle' to 'broadcasting'. This allows the user to select which BISes he wishes to sync to via a 2 step process: 1) the user calls the method, changing the transport's state to broadcasting 2) the audio server detects that the transport is in the 'broadcasting' state and automatically acquires it Possible Errors: :org.bluez.Error.NotAuthorized: void Unselect() ``````````````` Applicable only for transports created by a broadcast sink. This moves the transport from 'broadcasting' or 'active' to 'idle'. This allows the user to terminate the sync to a BIS to via a 2 step process: 1) the user calls this method, changing the transport's state to idle 2) the audio server detects this event and releases the transport Possible Errors: :org.bluez.Error.NotAuthorized: Properties ---------- object Device [readonly] ```````````````````````` Device object which the transport is connected to. string UUID [readonly] `````````````````````` UUID of the profile which the transport is for. byte Codec [readonly] ````````````````````` Assigned number of codec that the transport support. The values should match the profile specification which is indicated by the UUID. array{byte} Configuration [readonly] ```````````````````````````````````` Configuration blob, it is used as it is so the size and byte order must match. string State [readonly] ``````````````````````` Indicates the state of the transport. Possible values are: :"idle": not streaming :"pending": streaming but not acquired :"broadcasting": streaming but not acquired, applicable only for transports created by a broadcast sink :"active": streaming and acquired uint16 Delay [readwrite, optional] `````````````````````````````````` Transport delay in 1/10 of millisecond, this property is only writeable when the transport corresponds to a sink endpoint and it was acquired by the sender. uint16 Volume [readwrite, optional] ``````````````````````````````````` Indicates volume level of the transport, this property is only writeable when the transport was acquired by the sender. Possible Values: 0-127 (A2DP) 0-255 (BAP) object Endpoint [readonly, optional, experimental] `````````````````````````````````````````````````` Endpoint object which the transport is associated with. uint32 Location [readonly, ISO only, experimental] `````````````````````````````````````````````````` Indicates transport Audio Location. array{byte} Metadata [readwrite, ISO Only, experimental] ```````````````````````````````````````````````````````` Indicates transport Metadata. array{object} Links [readonly, optional, CIS only, experimental] ```````````````````````````````````````````````````````````````` Linked transport objects which the transport is associated with. array{object} Links [readwrite, BIS only, experimental] ``````````````````````````````````````````````````````` For a Broadcast Sink, the BIG sync procedure requires all desired streams to be enumerated from the start and it cannot be later reconfigured by adding or removing BISes. To avoid terminating and recreating the BIG sync everytime a new transport is selected for acquire, all transports selected via Transport.Select need to be linked together. When the first transport is acquired via Transport.Acquire, all links are included in the BIG sync command. An acquired transport will create and set fds for all its links. Then, each link needs to be acquired separately, to get the fd and start receiving audio. dict QoS [readwrite, optional, ISO only, experimental] `````````````````````````````````````````````````````` Only present when QoS is configured. Possible values for Unicast: :byte CIG: Indicates configured CIG. Possible values: :0x00 - 0xef: Valid ID range. :0xff: Auto allocate. :byte CIS: Indicates configured CIS. Possible values: :0x00 - 0xef: Valid ID range. :0xff: Auto allocate. :byte Framing: Indicates configured framing. Possible values: :0x00: Unframed. :0x01: Framed. :uint32 PresentationDelay: Indicates configured transport presentation delay (us). :byte TargetLatency: Indicates the requested target latency. Possible values: :0x01: Low Latency. :0x02: Balanced Latency/Reliability. :0x03: High Reliability. Possible values for Broadcast: :byte BIG: Indicates configured QoS BIG. :byte BIS: Indicates configured BIS. :byte SyncFactor: Indicates configured broadcast sync factor. :byte Packing: Indicates configured packing. :byte Framing: Indicates configured framing. :array{byte} BCode: Indicates the string used for encryption/decryption. :byte encryption: Indicates if the stream is encrypted. :byte Options: Indicates configured broadcast options. :uint16 Skip: Indicates configured broadcast skip. :byte SyncTimeout: Indicates configured broadcast sync timeout. :byte SyncType: Indicates configured broadcast sync CTE type. :byte MSE: Indicates configured broadcast MSE. :uint16 Timeout: Indicates configured broadcast timeout. Possible values for both Unicast and Broadcast: :uint32 Interval: Indicates configured ISO interval (us). :uint16 Latency: Indicates configured transport latency (ms). :uint16 SDU: Indicates configured maximum SDU. :byte PHY: Indicates configured PHY. Possible values: :bit 0: LE 1M :bit 1: LE 2M :bit 2: LE Coded :byte Retransmissions: Indicates configured retransmissions. bluez-5.82/doc/PaxHeaders/org.bluez.GattCharacteristic.rst0000644000000000000000000000005014766002272020631 xustar0020 atime=1743515578 20 ctime=1743591290 bluez-5.82/doc/org.bluez.GattCharacteristic.rst0000644000000000000000000002216114766002272020314 0ustar00rootroot============================ org.bluez.GattCharacteristic ============================ ------------------------------------------------ BlueZ D-Bus GattCharacteristic API documentation ------------------------------------------------ :Version: BlueZ :Date: October 2023 :Manual section: 5 :Manual group: Linux System Administration Description =========== GATT local/server and remote/client characteristic attribute representation share the same high-level D-Bus API. Local/Server refers to GATT based characteristics exported by a plugin or an external application. Remote/Client refers to GATT characteristics exported by the peer. Interface ========= Client ------ :Service: org.bluez :Interface: org.bluez.GattCharacteristic1 :Object path: [variable prefix]/{hci0,hci1,...}/dev_XX_XX_XX_XX_XX_XX/serviceXX/charYYYY Server ------ :Service: unique name :Interface: org.bluez.GattCharacteristic1 :Object path: freely definable Methods ------- array{byte} ReadValue(dict options) ``````````````````````````````````` Issues a request to read the value of the characteristic and returns the value if the operation was successful. Possible options: :uint16_t offset: Read start offset in bytes. :uint16_t mtu (server only): Exchange MTU in bytes. :object device (server only): Device object. :string link (server only): Link type. Possible values: :"BR/EDR": :"LE": Possible Errors: :org.bluez.Error.Failed: Possible values: string 0x80 - 0x9f :org.bluez.Error.InProgress: :org.bluez.Error.NotPermitted: :org.bluez.Error.NotAuthorized: :org.bluez.Error.InvalidOffset: :org.bluez.Error.NotSupported: void WriteValue(array{byte} value, dict options) ```````````````````````````````````````````````` Issues a request to write the value of the characteristic. Possible options: :uint16 offset: Write start offset in bytes. :string type: Possible values: :"command": Use Write without response procedure. :"request": Use Write with response procedure. :"reliable": Use Reliable Write procedure. :uint16 mtu: Exchanged MTU (Server only). :object device: Device path (Server only). :string link: Link type (Server only). Possible values: :"BR/EDR": :"LE": :boolean prepare-authorize: True if prepare authorization request. Possible Errors: :org.bluez.Error.Failed: Possible values: string 0x80 - 0x9f :org.bluez.Error.InProgress: :org.bluez.Error.NotPermitted: :org.bluez.Error.InvalidValueLength: :org.bluez.Error.NotAuthorized: :org.bluez.Error.NotSupported: :org.bluez.Error.ImproperlyConfigured: fd, uint16 AcquireWrite(dict options) [optional] ```````````````````````````````````````````````` Acquire file descriptor and MTU for writing. Only sockets are supported. Usage of WriteValue will be locked causing it to return NotPermitted error. For server the MTU returned shall be equal or smaller than the negotiated MTU. For client it only works with characteristic that has **WriteAcquired** property which relies on write-without-response **Flag**. To release the lock the client shall close the file descriptor, a HUP is generated in case the device is disconnected. Note: the MTU can only be negotiated once and is symmetric therefore this method may be delayed in order to have the exchange MTU completed, because of that the file descriptor is closed during reconnections as the MTU has to be renegotiated. Possible options: :object device: Object Device (Server only). :uint16 mtu: Exchanged MTU (Server only). :string link: Link type (Server only). Possible values: :"BR/EDR": :"LE": Possible Errors: :org.bluez.Error.Failed: :org.bluez.Error.NotSupported: fd, uint16 AcquireNotify(dict options) [optional] ````````````````````````````````````````````````` Acquire file descriptor and MTU for notify. Only sockets are support. Usage of StartNotify will be locked causing it to return **org.bluez.Error.NotPermitted**. For server the MTU returned shall be equal or smaller than the negotiated MTU. Only works with characteristic that has **NotifyAcquired** property which relies on presence of **"notify" or "indicate"** **Flag** and no other client have called **StartNotify()**. Notification are enabled during this procedure so **StartNotify()** shall not be called, any notification will be dispatched via file descriptor therefore the Value property is not affected during the time where notify has been acquired. To release the lock the client shall close the file descriptor, a HUP is generated in case the device is disconnected. As a client if indication procedure is used the confirmation is generated automatically once received, for a server if the file descriptor is writable (POLLOUT) then upon receiving a confirmation from the client one byte (0x01) is written to the file descriptor. Note: the MTU can only be negotiated once and is symmetric therefore this method may be delayed in order to have the exchange MTU completed, because of that the file descriptor is closed during reconnections as the MTU has to be renegotiated. Possible options: :object device: Object Device (Server only). :uint16 mtu: Exchanged MTU (Server only). :string link: Link type (Server only). Possible values: :"BR/EDR": :"LE": Possible Errors: :org.bluez.Error.Failed: :org.bluez.Error.NotSupported: :org.bluez.Error.NotPermitted: void StartNotify() `````````````````` Starts a notification session from this characteristic if it supports value notifications or indications. Possible Errors: :org.bluez.Error.Failed: :org.bluez.Error.NotPermitted: :org.bluez.Error.InProgress: :org.bluez.Error.NotConnected: :org.bluez.Error.NotSupported: void StopNotify() ````````````````` Stops or cancel session previously created by **StartNotify()**. Note that notifications from a characteristic are shared between sessions thus calling StopNotify will release a single session. Possible Errors: :org.bluez.Error.Failed: void Confirm() [noreply, optional] (Server only) ```````````````````````````````````````````````` Confirms value was received. Possible Errors: org.bluez.Error.Failed Properties ---------- string UUID [read-only] ``````````````````````` 128-bit characteristic UUID. object Service [read-only] `````````````````````````` Object path of the GATT service the characteristic belongs to. array{byte} Value [read-only, optional] ``````````````````````````````````````` The cached value of the characteristic. This property gets updated only after a successful read request and when a notification or indication is received, upon which a PropertiesChanged signal will be emitted. boolean WriteAcquired [read-only, optional] ``````````````````````````````````````````` True, if this characteristic has been acquired by any client using AcquireWrite. For client properties is ommited in case 'write-without-response' flag is not set. For server the presence of this property indicates that AcquireWrite is supported. boolean NotifyAcquired [read-only, optional] ```````````````````````````````````````````` True, if this characteristic has been acquired by any client using AcquireNotify. For client this properties is ommited in case 'notify' flag is not set. For server the presence of this property indicates that AcquireNotify is supported. boolean Notifying [read-only, optional] ``````````````````````````````````````` True, if notifications or indications on this characteristic are currently enabled. array{string} Flags [read-only] ``````````````````````````````` Defines how the characteristic value can be used. See Core spec "Table 3.5: Characteristic Properties bit field", and "Table 3.8: Characteristic Extended Properties bit field". The "x-notify" and "x-indicate" flags restrict access to notifications and indications by imposing write restrictions on a characteristic's client characteristic configuration descriptor. Possible values: :"broadcast": :"read": :"write-without-response": :"write": :"notify": :"indicate": :"authenticated-signed-writes": :"extended-properties": :"reliable-write": :"writable-auxiliaries": :"encrypt-read": :"encrypt-write": :"encrypt-notify" (Server only): :"encrypt-indicate" (Server only): :"encrypt-authenticated-read": :"encrypt-authenticated-write": :"encrypt-authenticated-notify" (Server only): :"encrypt-authenticated-indicate" (Server only): :"secure-read" (Server only): :"secure-write" (Server only): :"secure-notify" (Server only): :"secure-indicate" (Server only): :"authorize": uint16 Handle [read-only] (Client Only) ``````````````````````````````````````` Characteristic handle. uint16 Handle [read-write, optional] (Server Only) `````````````````````````````````````````````````` Characteristic handle. When available in the server it would attempt to use to allocate into the database which may fail, to auto allocate the value 0x0000 shall be used which will cause the allocated handle to be set once registered. uint16 MTU [read-only] `````````````````````` Characteristic MTU, this is valid both for **ReadValue()** and **WriteValue()** but either method can use long procedures when supported. bluez-5.82/doc/PaxHeaders/org.bluez.LEAdvertisement.rst0000644000000000000000000000005014766002272020114 xustar0020 atime=1743515578 20 ctime=1743591290 bluez-5.82/doc/org.bluez.LEAdvertisement.rst0000644000000000000000000001552114766002272017601 0ustar00rootroot========================= org.bluez.LEAdvertisement ========================= --------------------------------------------- BlueZ D-Bus LEAdvertisement API documentation --------------------------------------------- :Version: BlueZ :Date: October 2023 :Manual section: 5 :Manual group: Linux System Administration Description =========== Advertising packets are structured data which is broadcast on the LE Advertising channels and available for all devices in range. Because of the limited space available in LE Advertising packets, each packet's contents must be carefully controlled. The service daemon acts as a store for the Advertisement Data which is meant to be sent. It constructs the correct Advertisement Data from the structured data and configured the kernel to send the correct advertisement. Interface ========= Specifies the Advertisement Data to be broadcast and some advertising parameters. Properties which are not present will not be included in the data. Required advertisement data types will always be included. All UUIDs are 128-bit versions in the API, and 16 or 32-bit versions of the same UUID will be used in the advertising data as appropriate. :Service: org.bluez :Interface: org.bluez.LEAdvertisement1 :Object path: freely definable Methods ------- void Release() [noreply] ```````````````````````` This method gets called when the service daemon removes the Advertisement. A client can use it to do cleanup tasks. There is no need to call **UnregisterAdvertisement()** because when this method gets called it has already been unregistered. Properties ---------- string Type [readonly] `````````````````````` Determines the type of advertising packet requested. Possible values: :"broadcast": :"peripheral": array{string} ServiceUUIDs [readonly, optional] ``````````````````````````````````````````````` List of UUIDs to include in the "Service UUID" field of the Advertising Data. dict ManufacturerData [readonly, optional] `````````````````````````````````````````` Manufacturer Data fields to include in the Advertising Data. Keys are the Manufacturer ID to associate with the data. array{string} SolicitUUIDs [readonly, optional] ``````````````````````````````````````````````` List of UUIDs to include in the "Service Solicitation" field of the Advertising Data. dict ServiceData [readonly, optional] ````````````````````````````````````` Service Data elements to include in the Advertising Data. The keys are the UUID to associate with the data. dict Data [readonly, optional] `````````````````````````````` Advertising Data to include. Key is the advertising type and value is the data as byte array. Note: Types already handled by other properties shall not be used. Possible values: :: Example: 0x26 0x01 0x01... array{string} ScanResponseServiceUUIDs [readonly, optional, experimental] ````````````````````````````````````````````````````````````````````````` List of UUIDs to include in the "Service UUID" field of the Scan Response Data. dict ScanResponseManufacturerData [readonly, optional, experimental] ```````````````````````````````````````````````````````````````````` Manufacturer Data fields to include in the Scan Response Data. Keys are the Manufacturer ID to associate with the data. array{string} ScanResponseSolicitUUIDs [readonly, optional, experimental] ````````````````````````````````````````````````````````````````````````` List of UUIDs to include in the "Service Solicitation" field of the Scan Response Data. dict ScanResponseServiceData [readonly, optional, experimental] ``````````````````````````````````````````````````````````````` Service Data elements to include in the Scan Response Data. The keys are the UUID to associate with the data. dict ScanResponseData [readonly, optional, experimental] ```````````````````````````````````````````````````````` Scan Response Data to include. Key is the advertising type and value is the data as byte array. bool Discoverable [readonly, optional] `````````````````````````````````````` Advertise as general discoverable. When present this will override adapter Discoverable property. Note: This property shall not be set when **Type** is set to "broadcast". uint16 DiscoverableTimeout [readonly, optional] ``````````````````````````````````````````````` The discoverable timeout in seconds. A value of zero means that the timeout is disabled and it will stay in discoverable/limited mode forever. Note: This property shall not be set when **Type** is set to "broadcast". array{string} Includes [readonly, optional] ``````````````````````````````````````````` List of features to be included in the advertising packet. Possible values: See **org.bluez.LEAdvertisingManager(5)** **SupportedIncludes** property. string LocalName [readonly, optional] ````````````````````````````````````` Local name to be used in the advertising report. If the string is too big to fit into the packet it will be truncated. If this property is available 'local-name' cannot be present in the **Includes**. uint16 Appearance [readonly, optional] `````````````````````````````````````` Appearance to be used in the advertising report. Possible values: as found on GAP Service. uint16_t Duration [readonly, optional] `````````````````````````````````````` Rotation duration of the advertisement in seconds. If there are other applications advertising no duration is set the default is 2 seconds. uint16_t Timeout [readonly, optional] ````````````````````````````````````` Timeout of the advertisement in seconds. This defines the lifetime of the advertisement. string SecondaryChannel [readonly, optional] ```````````````````````````````````````````` Secondary channel to be used. Primary channel is always set to "1M" except when "Coded" is set. Possible value: :"1M" (default): :"2M": :"Coded": uint32 MinInterval [readonly, optional] ``````````````````````````````````````` Minimum advertising interval to be used by the advertising set, in milliseconds. Acceptable values are in the range [20ms, 10,485s]. If the provided MinInterval is larger than the provided MaxInterval, the registration will return failure. uint32 MaxInterval [readonly, optional] ``````````````````````````````````````` Maximum advertising interval to be used by the advertising set, in milliseconds. Acceptable values are in the range [20ms, 10,485s]. If the provided MinInterval is larger than the provided MaxInterval, the registration will return failure. int16 TxPower [readonly, optional] `````````````````````````````````` Requested transmission power of this advertising set. The provided value is used only if the "CanSetTxPower" feature is enabled on the **org.bluez.LEAdvertisingManager(5)**. The provided value must be in range [-127 to +20], where units are in dBm. bluez-5.82/doc/PaxHeaders/org.bluez.obex.AgentManager.rst0000644000000000000000000000005014536422313020343 xustar0020 atime=1743516822 20 ctime=1743591290 bluez-5.82/doc/org.bluez.obex.AgentManager.rst0000644000000000000000000000210414536422313020021 0ustar00rootroot=========================== org.bluez.obex.AgentManager =========================== ----------------------------------------------- BlueZ D-Bus OBEX AgentManager API documentation ----------------------------------------------- :Version: BlueZ :Date: October 2023 :Manual section: 5 :Manual group: Linux System Administration Interface ========= :Service: org.bluez.obex :Interface: org.bluez.obex.AgentManager1 :Object path: /org/bluez/obex Methods ``````` void RegisterAgent(object agent) ```````````````````````````````` Registers an agent, which must implement **org.bluez.obex.Agent(5)**, to request authorization of the user to accept/reject objects. Object push service needs to authorize each received object. Possible errors: :org.bluez.obex.Error.AlreadyExists: void UnregisterAgent(object agent) `````````````````````````````````` Unregisters the agent that has been previously registered using **RegisterAgent()**. The object path parameter must match the same value that has been used on registration. Possible errors: :org.bluez.obex.Error.DoesNotExist: bluez-5.82/doc/PaxHeaders/org.bluez.obex.Agent.rst0000644000000000000000000000005014536422313017050 xustar0020 atime=1743516823 20 ctime=1743591290 bluez-5.82/doc/org.bluez.obex.Agent.rst0000644000000000000000000000254614536422313016540 0ustar00rootroot==================== org.bluez.obex.Agent ==================== ---------------------------------------- BlueZ D-Bus OBEX Agent API documentation ---------------------------------------- :Version: BlueZ :Date: October 2023 :Manual section: 5 :Manual group: Linux System Administration Interface ========= ;Service: unique name :Interface: org.bluez.obex.Agent1 :Object path: freely definable Methods ------- void Release() `````````````` This method gets called when **obexd(8)** daemon unregisters the agent. An agent can use it to do cleanup tasks. There is no need to unregister the agent, because when this method gets called it has already been unregistered. string AuthorizePush(object transfer) ````````````````````````````````````` This method gets called when the **obexd(8)** needs to accept/reject a Bluetooth object push request. Returns the full path (including the filename) or the folder name suffixed with '/' where the object shall be stored. The transfer object, see **org.bluez.obex.Transfer(5)** will contain a Filename property that contains the default location and name that can be returned. Possible errors: :org.bluez.obex.Error.Rejected: :org.bluez.obex.Error.Canceled: void Cancel() ````````````` This method gets called to indicate that the agent request failed before a reply was returned. It cancels the previous request. bluez-5.82/doc/PaxHeaders/org.bluez.BatteryProvider.50000644000000000000000000000005014773213442017543 xustar0020 atime=1743591202 20 ctime=1743591291 bluez-5.82/doc/org.bluez.BatteryProvider.50000644000000000000000000000265714773213442017236 0ustar00rootroot.\" Man page generated from reStructuredText. . . .nr rst2man-indent-level 0 . .de1 rstReportMargin \\$1 \\n[an-margin] level \\n[rst2man-indent-level] level margin: \\n[rst2man-indent\\n[rst2man-indent-level]] - \\n[rst2man-indent0] \\n[rst2man-indent1] \\n[rst2man-indent2] .. .de1 INDENT .\" .rstReportMargin pre: . RS \\$1 . nr rst2man-indent\\n[rst2man-indent-level] \\n[an-margin] . nr rst2man-indent-level +1 .\" .rstReportMargin post: .. .de UNINDENT . RE .\" indent \\n[an-margin] .\" old: \\n[rst2man-indent\\n[rst2man-indent-level]] .nr rst2man-indent-level -1 .\" new: \\n[rst2man-indent\\n[rst2man-indent-level]] .in \\n[rst2man-indent\\n[rst2man-indent-level]]u .. .TH "ORG.BLUEZ.BATTERYPROVIDER" "5" "October 2023" "BlueZ" "Linux System Administration" .SH NAME org.bluez.BatteryProvider \- BlueZ D-Bus BatteryProvider API documentation .SH INTERFACE .INDENT 0.0 .TP .B Service .TP .B Interface org.bluez.BatteryProvider1 .TP .B Object path {provider_root}/{unique battery object path} .UNINDENT .SS Properties .sp Objects provided on this interface contain the same properties as \fBorg.bluez.Battery(5)\fP interface. Additionally, this interface needs to have the Device property indicating the object path of the device this battery provides. .SS object Device [readonly] .INDENT 0.0 .INDENT 3.5 The object path of the device that has this battery. .UNINDENT .UNINDENT .\" Generated by docutils manpage writer. . bluez-5.82/doc/PaxHeaders/rfcomm.70000644000000000000000000000005014773213424013775 xustar0020 atime=1743591188 20 ctime=1743591290 bluez-5.82/doc/rfcomm.70000644000000000000000000001450314773213424013461 0ustar00rootroot'\" t .\" Man page generated from reStructuredText. . . .nr rst2man-indent-level 0 . .de1 rstReportMargin \\$1 \\n[an-margin] level \\n[rst2man-indent-level] level margin: \\n[rst2man-indent\\n[rst2man-indent-level]] - \\n[rst2man-indent0] \\n[rst2man-indent1] \\n[rst2man-indent2] .. .de1 INDENT .\" .rstReportMargin pre: . RS \\$1 . nr rst2man-indent\\n[rst2man-indent-level] \\n[an-margin] . nr rst2man-indent-level +1 .\" .rstReportMargin post: .. .de UNINDENT . RE .\" indent \\n[an-margin] .\" old: \\n[rst2man-indent\\n[rst2man-indent-level]] .nr rst2man-indent-level -1 .\" new: \\n[rst2man-indent\\n[rst2man-indent-level]] .in \\n[rst2man-indent\\n[rst2man-indent-level]]u .. .TH "RFCOMM" "7" "May 2024" "BlueZ" "Linux System Administration" .SH NAME rfcomm \- RFCOMM protocol .SH SYNOPSIS .INDENT 0.0 .INDENT 3.5 .sp .EX #include #include #include rfcomm_socket = socket(PF_BLUETOOTH, SOCK_STREAM, BTPROTO_RFCOMM); .EE .UNINDENT .UNINDENT .SH DESCRIPTION .sp The RFCOMM protocol provides emulation of serial ports over the L2CAP(7) protocol. The protocol is based on the ETSI standard TS 07.10. .sp RFCOMM is a simple transport protocol, with additional provisions for emulating the 9 circuits of RS\-232 (EIATIA\-232\-E) serial ports. .SH SOCKET ADDRESS .INDENT 0.0 .INDENT 3.5 .sp .EX struct sockaddr_rc { sa_family_t rc_family; unsigned short rc_bdaddr; unsigned char rc_channel; }; .EE .UNINDENT .UNINDENT .sp Example: .INDENT 0.0 .INDENT 3.5 .sp .EX struct sockaddr_rc addr; memset(&addr, 0, sizeof(addr)); addr.rc_family = AF_BLUETOOTH; bacpy(&addr.rc_bdaddr, bdaddr); addr.rc_channel = channel; .EE .UNINDENT .UNINDENT .SH SOCKET OPTIONS .sp The socket options listed below can be set by using \fBsetsockopt(2)\fP and read with \fBgetsockopt(2)\fP with the socket level set to SOL_BLUETOOTH. .SS BT_SECURITY (since Linux 2.6.30) .sp Channel security level, possible values: .TS box center; l|l|l|l. T{ Value T} T{ Security Level T} T{ Link Key Type T} T{ Encryption T} _ T{ \fBBT_SECURITY_SDP\fP T} T{ 0 (SDP Only) T} T{ None T} T{ Not required T} _ T{ \fBBT_SECURITY_LOW\fP T} T{ 1 (Low) T} T{ Unauthenticated T} T{ Not required T} _ T{ \fBBT_SECURITY_MEDIUM\fP T} T{ 2 (Medium \- default) T} T{ Unauthenticated T} T{ Desired T} _ T{ \fBBT_SECURITY_HIGH\fP T} T{ 3 (High) T} T{ Authenticated T} T{ Required T} _ T{ \fBBT_SECURITY_FIPS\fP (since Linux 3.15) T} T{ 4 (Secure Only) T} T{ Authenticated (P\-256 based Secure Simple Pairing and Secure Authentication) T} T{ Required T} .TE .sp Example: .INDENT 0.0 .INDENT 3.5 .sp .EX int level = BT_SECURITY_HIGH; int err = setsockopt(rfcomm_socket, SOL_BLUETOOTH, BT_SECURITY, &level, sizeof(level)); if (err == \-1) { perror(\(dqsetsockopt\(dq); return 1; } .EE .UNINDENT .UNINDENT .SS BT_DEFER_SETUP (since Linux 2.6.30) .sp Channel defer connection setup, this control if the connection procedure needs to be authorized by userspace before responding which allows authorization at profile level, possible values: .TS box center; l|l|l. T{ Value T} T{ Description T} T{ Authorization T} _ T{ \fB0\fP T} T{ Disable (default) T} T{ Not required T} _ T{ \fB1\fP T} T{ Enable T} T{ Required T} .TE .sp Example: .INDENT 0.0 .INDENT 3.5 .sp .EX int defer_setup = 1; int err = setsockopt(rfcomm_socket, SOL_BLUETOOTH, BT_DEFER_SETUP, &defer_setup, sizeof(defer_setup)); if (err == \-1) { perror(\(dqsetsockopt\(dq); return err; } err = listen(rfcomm_socket, 5); if (err) { perror(\(dqlisten\(dq); return err; } struct sockaddr_rc remote_addr = {0}; socklen_t addr_len = sizeof(remote_addr); int new_socket = accept(rfcomm_socket, (struct sockaddr*)&remote_addr, &addr_len); if (new_socket < 0) { perror(\(dqaccept\(dq); return new_socket; } /* To complete the connection setup of new_socket read 1 byte */ char c; struct pollfd pfd; memset(&pfd, 0, sizeof(pfd)); pfd.fd = new_socket; pfd.events = POLLOUT; err = poll(&pfd, 1, 0); if (err) { perror(\(dqpoll\(dq); return err; } if (!(pfd.revents & POLLOUT)) { err = read(sk, &c, 1); if (err < 0) { perror(\(dqread\(dq); return err; } } .EE .UNINDENT .UNINDENT .SS BT_FLUSHABLE (since Linux 2.6.39) .sp Channel flushable flag, this control if the channel data can be flushed or not, possible values: .TS box center; l|l|l. T{ Define T} T{ Value T} T{ Description T} _ T{ \fBBT_FLUSHABLE_OFF\fP T} T{ 0x00 (default) T} T{ Do not flush data T} _ T{ \fBBT_FLUSHABLE_ON\fP T} T{ 0x01 T} T{ Flush data T} .TE .SS BT_POWER (since Linux 3.1) .sp Channel power policy, this control if the channel shall force exit of sniff mode or not, possible values: .TS box center; l|l|l. T{ Define T} T{ Value T} T{ Description T} _ T{ \fBBT_POWER_FORCE_ACTIVE_OFF\fP T} T{ 0x00 (default) T} T{ Don\(aqt force exit of sniff mode T} _ T{ \fBBT_POWER_FORCE_ACTIVE_ON\fP T} T{ 0x01 T} T{ Force exit of sniff mode T} .TE .SS BT_CHANNEL_POLICY (since Linux 3.10) .sp High\-speed (AMP) channel policy, possible values: .TS box center; l|l|l. T{ Define T} T{ Value T} T{ Description T} _ T{ \fBBT_CHANNEL_POLICY_BREDR_ONLY\fP T} T{ 0 (default) T} T{ BR/EDR only T} _ T{ \fBBT_CHANNEL_POLICY_BREDR_PREFERRED\fP T} T{ 1 T} T{ BR/EDR Preferred T} _ T{ \fBBT_CHANNEL_POLICY_BREDR_PREFERRED\fP T} T{ 2 T} T{ AMP Preferred T} .TE .SS BT_PHY (since Linux 5.10) .sp Channel supported PHY(s), possible values: .TS box center; l|l|l. T{ Define T} T{ Value T} T{ Description T} _ T{ \fBBT_PHY_BR_1M_1SLOT\fP T} T{ BIT 0 T} T{ BR 1Mbps 1SLOT T} _ T{ \fBBT_PHY_BR_1M_3SLOT\fP T} T{ BIT 1 T} T{ BR 1Mbps 3SLOT T} _ T{ \fBBT_PHY_BR_1M_5SLOT\fP T} T{ BIT 2 T} T{ BR 1Mbps 5SLOT T} _ T{ \fBBT_PHY_BR_2M_1SLOT\fP T} T{ BIT 3 T} T{ EDR 2Mbps 1SLOT T} _ T{ \fBBT_PHY_BR_2M_3SLOT\fP T} T{ BIT 4 T} T{ EDR 2Mbps 3SLOT T} _ T{ \fBBT_PHY_BR_2M_5SLOT\fP T} T{ BIT 5 T} T{ EDR 2Mbps 5SLOT T} _ T{ \fBBT_PHY_BR_3M_1SLOT\fP T} T{ BIT 6 T} T{ EDR 3Mbps 1SLOT T} _ T{ \fBBT_PHY_BR_3M_3SLOT\fP T} T{ BIT 7 T} T{ EDR 3Mbps 3SLOT T} _ T{ \fBBT_PHY_BR_3M_5SLOT\fP T} T{ BIT 8 T} T{ EDR 3Mbps 5SLOT T} .TE .SH RESOURCES .sp .SH REPORTING BUGS .sp .SH SEE ALSO .sp socket(7), rctest(1) .SH COPYRIGHT Free use of this software is granted under the terms of the GNU Lesser General Public Licenses (LGPL). .\" Generated by docutils manpage writer. . bluez-5.82/doc/PaxHeaders/org.bluez.AdvertisementMonitor.rst0000644000000000000000000000005014536422313021240 xustar0020 atime=1743516811 20 ctime=1743591290 bluez-5.82/doc/org.bluez.AdvertisementMonitor.rst0000644000000000000000000001154314536422313020725 0ustar00rootroot============================== org.bluez.AdvertisementMonitor ============================== -------------------------------------------------- BlueZ D-Bus AdvertisementMonitor API documentation -------------------------------------------------- :Version: BlueZ :Date: October 2023 :Manual section: 5 :Manual group: Linux System Administration Description =========== This API allows an client to specify a job of monitoring advertisements by registering the root of hierarchy and then exposing advertisement monitors under the root with filtering conditions, thresholds of RSSI and timers of RSSI thresholds. Once a monitoring job is activated by **bluetoothd(8)**, the client can expect to get notified on the targeted advertisements no matter if there is an ongoing discovery session (see **StartDiscovery()** in **org.bluez.Adapter(5)**). Interface ========= :Service: org.bluez :Interface: org.bluez.AdvertisementMonitor1 [experimental] :Object path: freely definable Methods ------- void Release() [noreply] ```````````````````````` This gets called as a signal for a client to perform clean-up when: - Monitor cannot be activated after it was exposed - Monitor has been deactivated. void Activate() [noreply] ````````````````````````` After a monitor was exposed, this gets called as a signal for client to get acknowledged when a monitor has been activated, so the client can expect to receive calls on **DeviceFound()** or **DeviceLost()**. void DeviceFound(object device) [noreply] ````````````````````````````````````````` This gets called to notify the client of finding the targeted device. Once receiving the call, the client should start to monitor the corresponding device to retrieve the changes on RSSI and advertisement content. void DeviceLost(object device) [noreply] ```````````````````````````````````````` This gets called to notify the client of losing the targeted device. Once receiving this call, the client should stop monitoring the corresponding device. Properties ---------- string Type [read-only] ``````````````````````` The type of the monitor. See **SupportedMonitorTypes** in **org.bluez.AdvertisementMonitorManager(5)** for the available options. int16 RSSILowThreshold [read-only, optional] ```````````````````````````````````````````` Used in conjunction with **RSSILowTimeout** to determine whether a device becomes out-of-range. Valid range is -127 to 20 (dBm), while 127 indicates unset. int16 RSSIHighThreshold [read-only, optional] ````````````````````````````````````````````` Used in conjunction with RSSIHighTimeout to determine whether a device becomes in-range. Valid range is -127 to 20 (dBm), while 127 indicates unset. uint16 RSSILowTimeout [read-only, optional] ``````````````````````````````````````````` The time it takes to consider a device as out-of-range. If this many seconds elapses without receiving any signal at least as strong as **RSSILowThreshold**, a currently in-range device will be considered as out-of-range (lost). Valid range is 1 to 300 (seconds), while 0 indicates unset. uint16 RSSIHighTimeout [read-only, optional] ```````````````````````````````````````````` The time it takes to consider a device as in-range. If this many seconds elapses while we continuouslyreceive signals at least as strong as **RSSIHighThreshold**, a currently out-of-range device will be considered as in-range (found). Valid range is 1 to 300 (seconds), while 0 indicates unset. uint16 RSSISamplingPeriod [read-only, optional] ``````````````````````````````````````````````` Grouping rules on how to propagate the received advertisement packets to the client. Possible values: :0: All advertisement packets from in-range devices would be propagated. :255: Only the first advertisement packet of in-range devices would be propagated. If the device becomes lost, then the first packet when it is found again will also be propagated. :1 to 254: Advertisement packets would be grouped into 100ms * N time period. Packets in the same group will only be reported once, with the RSSI value being averaged out. Currently this is unimplemented in user space, so the value is only used to be forwarded to the kernel. array{(uint8, uint8, array{byte})} Patterns [read-only, optional] ````````````````````````````````````````````````````````````````` If the **Type** property is set to **"or_patterns"**, then this property must exist and have at least one entry in the array. The structure of a pattern contains the following: :uint8 start_position: The index in an AD data field where the search hould start. The beginning of an AD data field is index 0. :uint8 AD_data_type: See https://www.bluetooth.com/specifications/assigned-numbers/ generic-access-profile/ for the possible allowed value. :array{byte} content_of_pattern: This is the value of the pattern. The maximum length of the bytes is 31. bluez-5.82/doc/PaxHeaders/org.bluez.Network.rst0000644000000000000000000000005014536422313016507 xustar0020 atime=1743516785 20 ctime=1743591290 bluez-5.82/doc/org.bluez.Network.rst0000644000000000000000000000331614536422313016173 0ustar00rootroot================= org.bluez.Network ================= ------------------------------------- BlueZ D-Bus Network API documentation ------------------------------------- :Version: BlueZ :Date: October 2023 :Manual section: 5 :Manual group: Linux System Administration Interface ========= :Service: org.bluez :Interface: org.bluez.Network1 :Object path: [variable prefix]/{hci0,hci1,...}/dev_XX_XX_XX_XX_XX_XX Methods ------- string Connect(string uuid) ``````````````````````````` Connects to the network device and return the network interface name. Possible uuid values: :"panu", "00001115-0000-1000-8000-00805f9b34fb": Personal Network User role. :"nap", "00001116-0000-1000-8000-00805f9b34fb": Network Access Point role. :"gn", "00001117-0000-1000-8000-00805f9b34fb": Group Network role. The connection will be closed and network device released either upon calling **Disconnect()** or when the client disappears from the message bus. Possible errors: :org.bluez.Error.InvalidArguments: :org.bluez.Error.NotSupported: :org.bluez.Error.InProgress: :org.bluez.Error.Failed: void Disconnect() ````````````````` Disconnects from the network device. To abort a connection attempt in case of errors or timeouts in the client it is fine to call this method. Possible errors: :org.bluez.Error.Failed: :org.bluez.Error.NotConnected: Properties ---------- boolean Connected [readonly] ```````````````````````````` Indicates if the device is connected. string Interface [readonly, optional] ````````````````````````````````````` Indicates the network interface name when available. string UUID [readonly, optional] ```````````````````````````````` Indicates the connection role when available. bluez-5.82/doc/PaxHeaders/settings-storage.txt0000644000000000000000000000005014766002272016464 xustar0020 atime=1743515578 20 ctime=1743591290 bluez-5.82/doc/settings-storage.txt0000644000000000000000000002232514766002272016151 0ustar00rootrootBlueZ settings storage ********************** Purpose ======= The purpose of this document is to describe the directory structure of BlueZ settings storage. In effect, this document will serve as the primary, up to date source of BlueZ storage information. It is intended as reference for developers. Direct access to the storage outside from bluetoothd is highly discouraged. Adapter and remote device info are read form the storage during object initialization. Write to storage is performed immediately on every value change. Default storage directory is /var/lib/bluetooth. This can be adjusted by the --localstatedir configure switch. Default is --localstatedir=/var. When loading as a service the storage directory can be set via StateDirectory environment variable: https://www.freedesktop.org/software/systemd/man/systemd.exec.html All files are in ini-file format. Storage directory structure =========================== The storage root directory contains an optional addresses file that's used for managing adapters that come without a pre-allocated address. The format of the addresses file is: [Static] = Each adapter with an assigned address has its own subdirectory under the root, named based on the address, which contains: - a settings file for the local adapter - an attributes file containing attributes of supported LE services - an admin policy file containing current values of admin policies - a cache directory containing: - one file per device, named by remote device address, which contains device name - one directory per remote device, named by remote device address, which contains: - an info file - an attributes file containing attributes of remote LE services - a ccc file containing persistent Client Characteristic Configuration (CCC) descriptor information for GATT characteristics So the directory structure is: /var/lib/bluetooth// ./settings ./attributes ./admin_policy_settings ./cache/ ./ ./ ... .// ./info ./attributes ./ccc .// ./info ./attributes ... Settings file format ==================== Settings file contains one [General] group with adapter info like: Alias String Friendly user provided name advertised for this adapter This value overwrites the system name (pretty hostname) Discoverable Boolean Discoverability of the adapter PairableTimeout Integer How long to stay in pairable mode before going back to non-pairable. The value is in seconds. 0 = disable timer, i.e. stay pairable forever DiscoverableTimeout Integer How long to stay in discoverable mode before going back to non-discoverable. The value is in seconds. 0 = disable timer, i.e. stay discoverable forever Sample: [General] Name=My PC Discoverable=false Pairable=true DiscoverableTimeout=0 Identity file format ==================== Identity file contains one [General] group that holds identity information such as keys and adresses: IdentityResolvingKey String 128-bit value of the IRK Sample: [General] IdentityResolvingKey=00112233445566778899aabbccddeeff Attributes file format ====================== The attributes file lists all attributes supported by the local adapter or remote device. Attributes are stored using their handle as group name (decimal format). Each group contains: UUID String 128-bit UUID of the attribute Value String Value of the attribute as hexadecimal encoded string EndGroupHandle Integer End group handle in decimal format Sample: [1] UUID=00002800-0000-1000-8000-00805f9b34fb Value=0018 [4] UUID=00002803-0000-1000-8000-00805f9b34fb Value=020600002A [6] UUID=00002a00-0000-1000-8000-00805f9b34fb Value=4578616D706C6520446576696365 Admin Policy file format ====================== The admin policy file stores the current value of each admin policy. [General] group contains: ServiceAllowlist List of List of service UUID allowed by strings adapter in 128-bits format, separated by ','. Default is empty. Sample: [General] ServiceAllowlist= CCC file format ====================== The ccc file stores the current CCC descriptor values for GATT characteristics which have notification/indication enabled by the remote device. Information is stored using CCC attribute handle as group name (in decimal format). Each group contains: Value String CCC descriptor value encoded in hexadecimal Cache directory file format ============================ Each file, named by remote device address, may includes multiple groups (General, ServiceRecords, Attributes, Endpoints, NameResolving). In ServiceRecords, SDP records are stored using their handle as key (hexadecimal format). In "Attributes" group GATT database is stored using attribute handle as key (hexadecimal format). Value associated with this handle is serialized form of all data required to re-create given attribute. ":" is used to separate fields. In "Endpoints" group A2DP remote endpoints are stored using the seid as key (hexadecimal format) and ":" is used to separate fields. It may also contain an entry which key is set to "LastUsed" which represented the last endpoint used. In "NameResolving", information regarding remote name resolving are stored to prevent wasting time resolving name for unresponsive devices. [General] group contains: Name String Remote device friendly name ShortName String Remote device shortened name [ServiceRecords] group contains <0x...> String SDP record as hexadecimal encoded string In [Attributes] group value always starts with attribute type, that determines how to interpret rest of value: Primary service: 2800:end_handle:uuid Secondary service: 2801:end_handle:uuid Included service: 2802:start_handle:end_handle:uuid Characteristic: 2803:value_handle:properties:uuid Descriptor: value:uuid uuid Sample Attributes section: [Attributes] 0001=2800:0005:1801 0002=2803:0003:20:2a05 0014=2800:001c:1800 0015=2803:0016:02:2a00 0017=2803:0018:02:2a01 0019=2803:001a:02:2aa6 0028=2800:ffff:0000180d-0000-1000-8000-00805f9b34fb 0029=2803:002a:10:00002a37-0000-1000-8000-00805f9b34fb 002b=2803:002c:02:00002a38-0000-1000-8000-00805f9b34fb 002d=2803:002e:08:00002a39-0000-1000-8000-00805f9b34fb [Endpoints] group contains: :::: String First field is the endpoint type, followed by codec type and delay reporting and its capabilities as hexadecimal encoded string. LastUsed:: String LastUsed has two fields which are the local and remote seids as hexadecimal encoded string. [NameResolving] group contains: FailedTime Integer The last time we failed to complete name resolving procedure, measured from an arbitrary, fixed point in the past. Info file format ================ Info file may includes multiple groups (General, Device ID, Link key and Long term key) related to a remote device. [General] group contains: Name String Remote device friendly name Alias String Alias name Class String Device class in hexadecimal, i.e. 0x000000 Appearance String Device appearance in hexadecimal, i.e. 0x0000 SupportedTechnologies List of List of technologies supported by strings device, separated by ";" Technologies can be BR/EDR or LE AddressType String An address can be "static" or "public" Trusted Boolean True if the remote device is trusted Blocked Boolean True if the remote device is blocked Services List of List of service UUIDs advertised by strings remote in 128-bits UUID format, separated by ";" PreferredBearer String Preferred bearer for remote device [DeviceID] group contains: Source Integer Assigner of Device ID Vendor Integer Device vendor Product Integer Device product Version Integer Device version [LinkKey] group contains: Key String Key in hexadecimal format Type Integer Type of link key PINLength Integer Length of PIN [LongTermKey] group contains: Key String Long term key in hexadecimal format Authenticated Boolean True if remote device has been authenticated EncSize Integer Encrypted size EDiv Integer Encrypted diversifier Rand Integer Randomizer [PeripheralLongTermKey] group contains: Same as the [LongTermKey] group, except for peripheral keys. [ConnectionParameters] group contains: MinInterval Integer Minimum Connection Interval MaxInterval Integer Maximum Connection Interval Latency Integer Connection Latency Timeout Integer Supervision Timeout [LocalSignatureKey] and [RemoteSignatureKey] groups contain: Key String Key in hexadecimal format Counter Integer Signing counter Authenticated Boolean True if the key is authenticated [ServiceChanged] This section holds information related to Service Changed characteristic of GATT core service. CCC_LE Integer CCC value for LE transport CCC_BR/EDR Integer CCC value for BR/EDR transport bluez-5.82/doc/PaxHeaders/org.bluez.obex.ObjectPush.rst0000644000000000000000000000005014536422313020060 xustar0020 atime=1743516815 20 ctime=1743591290 bluez-5.82/doc/org.bluez.obex.ObjectPush.rst0000644000000000000000000000503214536422313017541 0ustar00rootroot========================= org.bluez.obex.ObjectPush ========================= --------------------------------------------- BlueZ D-Bus OBEX ObjectPush API documentation --------------------------------------------- :Version: BlueZ :Date: October 2023 :Manual section: 5 :Manual group: Linux System Administration Interface ========= :Service: org.bluez.obex :Interface: org.bluez.obex.ObjectPush1 :Object path: [Session object path] Methods ------- object, dict SendFile(string sourcefile) ```````````````````````````````````````` Sends local file to the remote device. The returned path represents the newly created transfer, which should be used to find out if the content has been successfully transferred or if the operation fails. The properties of this transfer are also returned along with the object path, to avoid a call to GetProperties, see **org.bluez.obex.Transfer(5)** for the possible list of properties. Possible errors: :org.bluez.obex.Error.InvalidArguments: :org.bluez.obex.Error.Failed: object, dict PullBusinessCard(string targetfile) ```````````````````````````````````````````````` Request the business card from a remote device and store it in the local file. If an empty target file is given, a name will be automatically generated for the temporary file. The returned path represents the newly created transfer, which should be used to find out if the content has been successfully transferred or if the operation fails. The properties of this transfer are also returned along with the object path, to avoid a call to GetProperties, see **org.bluez.obex.Transfer(5)** for the possible list of properties. Possible errors: :org.bluez.obex.Error.InvalidArguments: :org.bluez.obex.Error.Failed: object, dict ExchangeBusinessCards(string clientfile, string targetfile) ```````````````````````````````````````````````````````````````````````` Push the client's business card to the remote device and then retrieve the remote business card and store it in a local file. If an empty target file is given, a name will be automatically generated for the temporary file. The returned path represents the newly created transfer, which should be used to find out if the content has been successfully transferred or if the operation fails. The properties of this transfer are also returned along with the object path, to avoid a call to GetProperties, see **org.bluez.obex.Transfer(5)** for the possible list of properties. Possible errors: :org.bluez.obex.Error.InvalidArguments: :org.bluez.obex.Error.Failed: bluez-5.82/doc/PaxHeaders/org.bluez.Profile.50000644000000000000000000000005014773213435016020 xustar0020 atime=1743591197 20 ctime=1743591290 bluez-5.82/doc/org.bluez.Profile.50000644000000000000000000000337014773213435015504 0ustar00rootroot.\" Man page generated from reStructuredText. . . .nr rst2man-indent-level 0 . .de1 rstReportMargin \\$1 \\n[an-margin] level \\n[rst2man-indent-level] level margin: \\n[rst2man-indent\\n[rst2man-indent-level]] - \\n[rst2man-indent0] \\n[rst2man-indent1] \\n[rst2man-indent2] .. .de1 INDENT .\" .rstReportMargin pre: . RS \\$1 . nr rst2man-indent\\n[rst2man-indent-level] \\n[an-margin] . nr rst2man-indent-level +1 .\" .rstReportMargin post: .. .de UNINDENT . RE .\" indent \\n[an-margin] .\" old: \\n[rst2man-indent\\n[rst2man-indent-level]] .nr rst2man-indent-level -1 .\" new: \\n[rst2man-indent\\n[rst2man-indent-level]] .in \\n[rst2man-indent\\n[rst2man-indent-level]]u .. .TH "ORG.BLUEZ.PROFILE" "5" "October 2023" "BlueZ" "Linux System Administration" .SH NAME org.bluez.Profile \- BlueZ D-Bus Profile API documentation .SH INTERFACE .INDENT 0.0 .TP .B Service unique name .TP .B Interface org.bluez.Profile1 .TP .B Object path freely definable .UNINDENT .SS Methods .SS void Release() [noreply] .INDENT 0.0 .INDENT 3.5 This method gets called when the service daemon unregisters the profile. A profile can use it to do cleanup tasks. There is no need to unregister the profile, because when this method gets called it has already been unregistered. .UNINDENT .UNINDENT .SS void NewConnection(object device, fd, dict fd_properties) .INDENT 0.0 .INDENT 3.5 This method gets called when a new service level connection has been made and authorized. .sp Possible fd_properties values: .INDENT 0.0 .TP .B uint16 Version [optional] Profile version. .TP .B uint16 Features [optional] Profile features. .UNINDENT .sp Possible errors: .INDENT 0.0 .TP .B org.bluez.Error.Rejected .TP .B org.bluez.Error.Canceled .UNINDENT .UNINDENT .UNINDENT .\" Generated by docutils manpage writer. . bluez-5.82/doc/PaxHeaders/l2cap.70000644000000000000000000000005014773213422013511 xustar0020 atime=1743591186 20 ctime=1743591290 bluez-5.82/doc/l2cap.70000644000000000000000000001730314773213422013176 0ustar00rootroot'\" t .\" Man page generated from reStructuredText. . . .nr rst2man-indent-level 0 . .de1 rstReportMargin \\$1 \\n[an-margin] level \\n[rst2man-indent-level] level margin: \\n[rst2man-indent\\n[rst2man-indent-level]] - \\n[rst2man-indent0] \\n[rst2man-indent1] \\n[rst2man-indent2] .. .de1 INDENT .\" .rstReportMargin pre: . RS \\$1 . nr rst2man-indent\\n[rst2man-indent-level] \\n[an-margin] . nr rst2man-indent-level +1 .\" .rstReportMargin post: .. .de UNINDENT . RE .\" indent \\n[an-margin] .\" old: \\n[rst2man-indent\\n[rst2man-indent-level]] .nr rst2man-indent-level -1 .\" new: \\n[rst2man-indent\\n[rst2man-indent-level]] .in \\n[rst2man-indent\\n[rst2man-indent-level]]u .. .TH "L2CAP" "7" "May 2024" "BlueZ" "Linux System Administration" .SH NAME l2cap \- L2CAP protocol .SH SYNOPSIS .INDENT 0.0 .INDENT 3.5 .sp .EX #include #include #include l2cap_socket = socket(PF_BLUETOOTH, SOCK_SEQPACKET, BTPROTO_L2CAP); .EE .UNINDENT .UNINDENT .SH DESCRIPTION .sp L2CAP is a protocol that provides an interface for higher\-level protocols to send and receive data over a Bluetooth connection. L2CAP sits on top of the Bluetooth Host Controller Interface (HCI) and provides a set of channels that can be used by higher\-level protocols to transmit data. .sp L2CAP provides a number of services to higher\-level protocols, including segmentation and reassembly of large data packets and flow control to prevent overloading of the receiver. L2CAP also supports multiple channels per connection, allowing for concurrent data transmission using different protocols. .SH SOCKET ADDRESS .INDENT 0.0 .INDENT 3.5 .sp .EX struct sockaddr_l2 { sa_family_t l2_family; unsigned short l2_psm; bdaddr_t l2_bdaddr; unsigned short l2_cid; uint8_t l2_bdaddr_type; }; .EE .UNINDENT .UNINDENT .sp Example: .INDENT 0.0 .INDENT 3.5 .sp .EX struct sockaddr_l2 addr; memset(&addr, 0, sizeof(addr)); addr.l2_family = AF_BLUETOOTH; bacpy(&addr.l2_bdaddr, bdaddr); if (cid) addr.l2_cid = htobs(cid); else addr.l2_psm = htobs(psm); addr.l2_bdaddr_type = bdaddr_type; .EE .UNINDENT .UNINDENT .SH SOCKET OPTIONS .sp The socket options listed below can be set by using \fBsetsockopt(2)\fP and read with \fBgetsockopt(2)\fP with the socket level set to SOL_BLUETOOTH. .SS BT_SECURITY (since Linux 2.6.30) .sp Channel security level, possible values: .TS box center; l|l|l|l. T{ Value T} T{ Security Level T} T{ Link Key Type T} T{ Encryption T} _ T{ \fBBT_SECURITY_SDP\fP T} T{ 0 (SDP Only) T} T{ None T} T{ Not required T} _ T{ \fBBT_SECURITY_LOW\fP T} T{ 1 (Low) T} T{ Unauthenticated T} T{ Not required T} _ T{ \fBBT_SECURITY_MEDIUM\fP T} T{ 2 (Medium \- default) T} T{ Unauthenticated T} T{ Desired T} _ T{ \fBBT_SECURITY_HIGH\fP T} T{ 3 (High) T} T{ Authenticated T} T{ Required T} _ T{ \fBBT_SECURITY_FIPS\fP (since Linux 3.15) T} T{ 4 (Secure Only) T} T{ Authenticated (P\-256 based Secure Simple Pairing and Secure Authentication) T} T{ Required T} .TE .sp Example: .INDENT 0.0 .INDENT 3.5 .sp .EX int level = BT_SECURITY_HIGH; int err = setsockopt(l2cap_socket, SOL_BLUETOOTH, BT_SECURITY, &level, sizeof(level)); if (err == \-1) { perror(\(dqsetsockopt\(dq); return 1; } .EE .UNINDENT .UNINDENT .SS BT_DEFER_SETUP (since Linux 2.6.30) .sp Channel defer connection setup, this control if the connection procedure needs to be authorized by userspace before responding which allows authorization at profile level, possible values: .TS box center; l|l|l. T{ Value T} T{ Description T} T{ Authorization T} _ T{ \fB0\fP T} T{ Disable (default) T} T{ Not required T} _ T{ \fB1\fP T} T{ Enable T} T{ Required T} .TE .sp Example: .INDENT 0.0 .INDENT 3.5 .sp .EX int defer_setup = 1; int err = setsockopt(l2cap_socket, SOL_BLUETOOTH, BT_DEFER_SETUP, &defer_setup, sizeof(defer_setup)); if (err == \-1) { perror(\(dqsetsockopt\(dq); return err; } err = listen(l2cap_socket, 5); if (err) { perror(\(dqlisten\(dq); return err; } struct sockaddr_l2 remote_addr = {0}; socklen_t addr_len = sizeof(remote_addr); int new_socket = accept(l2cap_socket, (struct sockaddr*)&remote_addr, &addr_len); if (new_socket < 0) { perror(\(dqaccept\(dq); return new_socket; } /* To complete the connection setup of new_socket read 1 byte */ char c; struct pollfd pfd; memset(&pfd, 0, sizeof(pfd)); pfd.fd = new_socket; pfd.events = POLLOUT; err = poll(&pfd, 1, 0); if (err) { perror(\(dqpoll\(dq); return err; } if (!(pfd.revents & POLLOUT)) { err = read(sk, &c, 1); if (err < 0) { perror(\(dqread\(dq); return err; } } .EE .UNINDENT .UNINDENT .SS BT_FLUSHABLE (since Linux 2.6.39) .sp Channel flushable flag, this control if the channel data can be flushed or not, possible values: .TS box center; l|l|l. T{ Define T} T{ Value T} T{ Description T} _ T{ \fBBT_FLUSHABLE_OFF\fP T} T{ 0x00 (default) T} T{ Do not flush data T} _ T{ \fBBT_FLUSHABLE_ON\fP T} T{ 0x01 T} T{ Flush data T} .TE .SS BT_POWER (since Linux 3.1) .sp Channel power policy, this control if the channel shall force exit of sniff mode or not, possible values: .TS box center; l|l|l. T{ Define T} T{ Value T} T{ Description T} _ T{ \fBBT_POWER_FORCE_ACTIVE_OFF\fP T} T{ 0x00 (default) T} T{ Don\(aqt force exit of sniff mode T} _ T{ \fBBT_POWER_FORCE_ACTIVE_ON\fP T} T{ 0x01 T} T{ Force exit of sniff mode T} .TE .SS BT_CHANNEL_POLICY (since Linux 3.10) .sp High\-speed (AMP) channel policy, possible values: .TS box center; l|l|l. T{ Define T} T{ Value T} T{ Description T} _ T{ \fBBT_CHANNEL_POLICY_BREDR_ONLY\fP T} T{ 0 (default) T} T{ BR/EDR only T} _ T{ \fBBT_CHANNEL_POLICY_BREDR_PREFERRED\fP T} T{ 1 T} T{ BR/EDR Preferred T} _ T{ \fBBT_CHANNEL_POLICY_BREDR_PREFERRED\fP T} T{ 2 T} T{ AMP Preferred T} .TE .SS BT_PHY (since Linux 5.10) .sp Channel supported PHY(s), possible values: .TS box center; l|l|l. T{ Define T} T{ Value T} T{ Description T} _ T{ \fBBT_PHY_BR_1M_1SLOT\fP T} T{ BIT 0 T} T{ BR 1Mbps 1SLOT T} _ T{ \fBBT_PHY_BR_1M_3SLOT\fP T} T{ BIT 1 T} T{ BR 1Mbps 3SLOT T} _ T{ \fBBT_PHY_BR_1M_5SLOT\fP T} T{ BIT 2 T} T{ BR 1Mbps 5SLOT T} _ T{ \fBBT_PHY_BR_2M_1SLOT\fP T} T{ BIT 3 T} T{ EDR 2Mbps 1SLOT T} _ T{ \fBBT_PHY_BR_2M_3SLOT\fP T} T{ BIT 4 T} T{ EDR 2Mbps 3SLOT T} _ T{ \fBBT_PHY_BR_2M_5SLOT\fP T} T{ BIT 5 T} T{ EDR 2Mbps 5SLOT T} _ T{ \fBBT_PHY_BR_3M_1SLOT\fP T} T{ BIT 6 T} T{ EDR 3Mbps 1SLOT T} _ T{ \fBBT_PHY_BR_3M_3SLOT\fP T} T{ BIT 7 T} T{ EDR 3Mbps 3SLOT T} _ T{ \fBBT_PHY_BR_3M_5SLOT\fP T} T{ BIT 8 T} T{ EDR 3Mbps 5SLOT T} _ T{ \fBBT_PHY_LE_1M_TX\fP T} T{ BIT 9 T} T{ LE 1Mbps TX T} _ T{ \fBBT_PHY_LE_1M_RX\fP T} T{ BIT 10 T} T{ LE 1Mbps RX T} _ T{ \fBBT_PHY_LE_2M_TX\fP T} T{ BIT 11 T} T{ LE 2Mbps TX T} _ T{ \fBBT_PHY_LE_2M_RX\fP T} T{ BIT 12 T} T{ LE 2Mbps RX T} _ T{ \fBBT_PHY_LE_CODED_TX\fP T} T{ BIT 13 T} T{ LE Coded TX T} _ T{ \fBBT_PHY_LE_CODED_RX\fP T} T{ BIT 14 T} T{ LE Coded RX T} .TE .SS BT_MODE (since Linux 5.10) .sp Channel Mode, possible values: .TS box center; l|l|l|l. T{ Define T} T{ Value T} T{ Description T} T{ Link T} _ T{ \fBBT_MODE_BASIC\fP T} T{ 0x00 (default) T} T{ Basic mode T} T{ Any T} _ T{ \fBBT_MODE_ERTM\fP T} T{ 0x01 T} T{ Enhanced Retransmission mode T} T{ BR/EDR T} _ T{ \fBBT_MODE_STREAM\fP T} T{ 0x02 T} T{ Stream mode T} T{ BR/EDR T} _ T{ \fBBT_MODE_LE_FLOWCTL\fP T} T{ 0x03 T} T{ Credit based flow control mode T} T{ LE T} _ T{ \fBBT_MODE_EXT_FLOWCTL\fP T} T{ 0x04 T} T{ Extended Credit based flow control mode T} T{ Any T} .TE .SH RESOURCES .sp .SH REPORTING BUGS .sp .SH SEE ALSO .sp socket(7), l2test(1) .SH COPYRIGHT Free use of this software is granted under the terms of the GNU Lesser General Public Licenses (LGPL). .\" Generated by docutils manpage writer. . bluez-5.82/doc/PaxHeaders/org.bluez.Agent.50000644000000000000000000000005014773213433015454 xustar0020 atime=1743591195 20 ctime=1743591290 bluez-5.82/doc/org.bluez.Agent.50000644000000000000000000001177414773213433015147 0ustar00rootroot.\" Man page generated from reStructuredText. . . .nr rst2man-indent-level 0 . .de1 rstReportMargin \\$1 \\n[an-margin] level \\n[rst2man-indent-level] level margin: \\n[rst2man-indent\\n[rst2man-indent-level]] - \\n[rst2man-indent0] \\n[rst2man-indent1] \\n[rst2man-indent2] .. .de1 INDENT .\" .rstReportMargin pre: . RS \\$1 . nr rst2man-indent\\n[rst2man-indent-level] \\n[an-margin] . nr rst2man-indent-level +1 .\" .rstReportMargin post: .. .de UNINDENT . RE .\" indent \\n[an-margin] .\" old: \\n[rst2man-indent\\n[rst2man-indent-level]] .nr rst2man-indent-level -1 .\" new: \\n[rst2man-indent\\n[rst2man-indent-level]] .in \\n[rst2man-indent\\n[rst2man-indent-level]]u .. .TH "ORG.BLUEZ.AGENT" "5" "October 2023" "BlueZ" "Linux System Administration" .SH NAME org.bluez.Agent \- BlueZ D-Bus Agent API documentation .SH INTERFACE .INDENT 0.0 .TP .B Service unique name .TP .B Interface org.bluez.Agent1 .TP .B Object path freely definable .UNINDENT .SS Methods .SS void Release() .INDENT 0.0 .INDENT 3.5 This method gets called when the service daemon unregisters the agent. An agent can use it to do cleanup tasks. There is no need to unregister the agent, because when this method gets called it has already been unregistered. .UNINDENT .UNINDENT .SS string RequestPinCode(object device) .INDENT 0.0 .INDENT 3.5 This method gets called when the service daemon needs to get the passkey for an authentication. .sp The return value should be a string of 1\-16 characters length. The string can be alphanumeric. .sp Possible errors: .INDENT 0.0 .TP .B org.bluez.Error.Rejected .TP .B org.bluez.Error.Canceled .UNINDENT .UNINDENT .UNINDENT .SS void DisplayPinCode(object device, string pincode) .INDENT 0.0 .INDENT 3.5 This method gets called when the service daemon needs to display a pincode for an authentication. .sp An empty reply should be returned. When the pincode needs no longer to be displayed, the Cancel method of the agent will be called. .sp This is used during the pairing process of keyboards that don\(aqt support Bluetooth 2.1 Secure Simple Pairing, in contrast to DisplayPasskey which is used for those that do. .sp This method will only ever be called once since older keyboards do not support typing notification. .sp Note that the PIN will always be a 6\-digit number, zero\-padded to 6 digits. This is for harmony with the later specification. .sp Possible errors: .INDENT 0.0 .TP .B org.bluez.Error.Rejected .TP .B org.bluez.Error.Canceled .UNINDENT .UNINDENT .UNINDENT .SS uint32 RequestPasskey(object device) .INDENT 0.0 .INDENT 3.5 This method gets called when the service daemon needs to get the passkey for an authentication. .sp The return value should be a numeric value between 0\-999999. .sp Possible errors: .INDENT 0.0 .TP .B org.bluez.Error.Rejected .TP .B org.bluez.Error.Canceled .UNINDENT .UNINDENT .UNINDENT .SS void DisplayPasskey(object device, uint32 passkey, uint16 entered) .INDENT 0.0 .INDENT 3.5 This method gets called when the service daemon needs to display a passkey for an authentication. .sp The entered parameter indicates the number of already typed keys on the remote side. .sp An empty reply should be returned. When the passkey needs no longer to be displayed, the Cancel method of the agent will be called. .sp During the pairing process this method might be called multiple times to update the entered value. .sp Note that the passkey will always be a 6\-digit number, so the display should be zero\-padded at the start if the value contains less than 6 digits. .UNINDENT .UNINDENT .SS void RequestConfirmation(object device, uint32 passkey) .INDENT 0.0 .INDENT 3.5 This method gets called when the service daemon needs to confirm a passkey for an authentication. .sp To confirm the value it should return an empty reply or an error in case the passkey is invalid. .sp Note that the passkey will always be a 6\-digit number, so the display should be zero\-padded at the start if the value contains less than 6 digits. .sp Possible errors: .INDENT 0.0 .TP .B org.bluez.Error.Rejected .TP .B org.bluez.Error.Canceled .UNINDENT .UNINDENT .UNINDENT .SS void RequestAuthorization(object device) .INDENT 0.0 .INDENT 3.5 This method gets called to request the user to authorize an incoming pairing attempt which would in other circumstances trigger the just\-works model, or when the user plugged in a device that implements cable pairing. In the latter case, the device would not be connected to the adapter via Bluetooth yet. .sp Possible errors: .INDENT 0.0 .TP .B org.bluez.Error.Rejected .TP .B org.bluez.Error.Canceled .UNINDENT .UNINDENT .UNINDENT .SS void AuthorizeService(object device, string uuid) .INDENT 0.0 .INDENT 3.5 This method gets called when the service daemon needs to authorize a connection/service request. .sp Possible errors: .INDENT 0.0 .TP .B org.bluez.Error.Rejected .TP .B org.bluez.Error.Canceled .UNINDENT .UNINDENT .UNINDENT .SS void Cancel() .INDENT 0.0 .INDENT 3.5 This method gets called to indicate that the agent request failed before a reply was returned. .UNINDENT .UNINDENT .\" Generated by docutils manpage writer. . bluez-5.82/doc/PaxHeaders/org.bluez.MediaPlayer.50000644000000000000000000000005014773213451016612 xustar0020 atime=1743591209 20 ctime=1743591291 bluez-5.82/doc/org.bluez.MediaPlayer.50000644000000000000000000001656414773213451016307 0ustar00rootroot.\" Man page generated from reStructuredText. . . .nr rst2man-indent-level 0 . .de1 rstReportMargin \\$1 \\n[an-margin] level \\n[rst2man-indent-level] level margin: \\n[rst2man-indent\\n[rst2man-indent-level]] - \\n[rst2man-indent0] \\n[rst2man-indent1] \\n[rst2man-indent2] .. .de1 INDENT .\" .rstReportMargin pre: . RS \\$1 . nr rst2man-indent\\n[rst2man-indent-level] \\n[an-margin] . nr rst2man-indent-level +1 .\" .rstReportMargin post: .. .de UNINDENT . RE .\" indent \\n[an-margin] .\" old: \\n[rst2man-indent\\n[rst2man-indent-level]] .nr rst2man-indent-level -1 .\" new: \\n[rst2man-indent\\n[rst2man-indent-level]] .in \\n[rst2man-indent\\n[rst2man-indent-level]]u .. .TH "ORG.BLUEZ.MEDIAPLAYER" "5" "September 2023" "BlueZ" "Linux System Administration" .SH NAME org.bluez.MediaPlayer \- BlueZ D-Bus MediaPlayer API documentation .SH INTERFACE .INDENT 0.0 .TP .B Service org.bluez (Controller role) .TP .B Interface org.bluez.MediaPlayer1 .TP .B Object path [variable prefix]/{hci0,hci1,...}/dev_XX_XX_XX_XX_XX_XX/playerX .UNINDENT .SS Methods .SS void Play() .INDENT 0.0 .INDENT 3.5 Resume playback. .sp Possible Errors: .INDENT 0.0 .TP .B org.bluez.Error.NotSupported .TP .B org.bluez.Error.Failed .UNINDENT .UNINDENT .UNINDENT .SS void Pause() .INDENT 0.0 .INDENT 3.5 Pause playback. .sp Possible Errors: .INDENT 0.0 .TP .B org.bluez.Error.NotSupported .TP .B org.bluez.Error.Failed .UNINDENT .UNINDENT .UNINDENT .SS void Stop() .INDENT 0.0 .INDENT 3.5 Stop playback. .sp Possible Errors: .INDENT 0.0 .TP .B org.bluez.Error.NotSupported .TP .B org.bluez.Error.Failed .UNINDENT .UNINDENT .UNINDENT .SS void Next() .INDENT 0.0 .INDENT 3.5 Next item. .sp Possible Errors: .INDENT 0.0 .TP .B org.bluez.Error.NotSupported .TP .B org.bluez.Error.Failed .UNINDENT .UNINDENT .UNINDENT .SS void Previous() .INDENT 0.0 .INDENT 3.5 Previous item. .sp Possible Errors: .INDENT 0.0 .TP .B org.bluez.Error.NotSupported .TP .B org.bluez.Error.Failed .UNINDENT .UNINDENT .UNINDENT .SS void FastForward() .INDENT 0.0 .INDENT 3.5 Fast forward playback, this action is only stopped when another method in this interface is called. .sp Possible Errors: .INDENT 0.0 .TP .B org.bluez.Error.NotSupported .TP .B org.bluez.Error.Failed .UNINDENT .UNINDENT .UNINDENT .SS void Rewind() .INDENT 0.0 .INDENT 3.5 Rewind playback, this action is only stopped when another method in this interface is called. .sp Possible Errors: .INDENT 0.0 .TP .B org.bluez.Error.NotSupported .TP .B org.bluez.Error.Failed .UNINDENT .UNINDENT .UNINDENT .SS void Press(byte avc_key) .INDENT 0.0 .INDENT 3.5 Press a specific key to send as passthrough command. The key will be released automatically. Use Hold() instead if the intention is to hold down the key. .sp Possible Errors: .INDENT 0.0 .TP .B org.bluez.Error.InvalidArguments .TP .B org.bluez.Error.NotSupported .TP .B org.bluez.Error.Failed .UNINDENT .UNINDENT .UNINDENT .SS void Hold(byte avc_key) .INDENT 0.0 .INDENT 3.5 Press and hold a specific key to send as passthrough command. It is your responsibility to make sure that Release() is called after calling this method. The held key will also be released when any other method in this interface is called. .sp Possible Errors: .INDENT 0.0 .TP .B org.bluez.Error.InvalidArguments .TP .B org.bluez.Error.NotSupported .TP .B org.bluez.Error.Failed .UNINDENT .UNINDENT .UNINDENT .SS void Release() .INDENT 0.0 .INDENT 3.5 Release the previously held key invoked using Hold(). .sp Possible Errors: .INDENT 0.0 .TP .B org.bluez.Error.NotSupported .TP .B org.bluez.Error.Failed .UNINDENT .UNINDENT .UNINDENT .SS Properties .SS string Equalizer [readwrite] .INDENT 0.0 .INDENT 3.5 Indicates Player Equalizer setting. .sp Possible values: .INDENT 0.0 .TP .B \(dqoff\(dq .TP .B \(dqon\(dq .UNINDENT .UNINDENT .UNINDENT .SS string Repeat [readwrite] .INDENT 0.0 .INDENT 3.5 Indicates Player Repeat setting. .sp Possible values: .INDENT 0.0 .TP .B \(dqoff\(dq .TP .B \(dqsingletrack\(dq .TP .B \(dqalltracks\(dq .TP .B \(dqgroup\(dq .UNINDENT .UNINDENT .UNINDENT .SS string Shuffle [readwrite] .INDENT 0.0 .INDENT 3.5 Indicates Player Suffle setting. .sp Possible values: .INDENT 0.0 .TP .B \(dqoff\(dq .TP .B \(dqalltracks\(dq .TP .B \(dqgroup\(dq .UNINDENT .UNINDENT .UNINDENT .SS string Scan [readwrite] .INDENT 0.0 .INDENT 3.5 Indicates Player Scan setting. .sp Possible values: .INDENT 0.0 .TP .B \(dqoff\(dq .TP .B \(dqalltracks\(dq .TP .B \(dqgroup\(dq .UNINDENT .UNINDENT .UNINDENT .SS string Status [readonly] .INDENT 0.0 .INDENT 3.5 Indicates Player Status setting. .sp Possible status: .INDENT 0.0 .TP .B \(dqplaying\(dq .TP .B \(dqstopped\(dq .TP .B \(dqpaused\(dq .TP .B \(dqforward\-seek\(dq .TP .B \(dqreverse\-seek\(dq .TP .B \(dqerror\(dq .UNINDENT .UNINDENT .UNINDENT .SS uint32 Position [readonly] .INDENT 0.0 .INDENT 3.5 Playback position in milliseconds. Changing the position may generate additional events that will be sent to the remote device. When position is 0 it means the track is starting and when it\(aqs greater than or equal to track\(aqs duration the track has ended. .sp Note that even if duration is not available in metadata it\(aqs possible to signal its end by setting position to the maximum uint32 value. .UNINDENT .UNINDENT .SS dict Track [readonly] .INDENT 0.0 .INDENT 3.5 Track metadata. .sp Possible values: .INDENT 0.0 .TP .B string Title Track title name .TP .B string Artist Track artist name .TP .B string Album Track album name .TP .B string Genre Track genre name .TP .B uint32 NumberOfTracks Number of tracks in total .TP .B uint32 TrackNumber Track number .TP .B uint32 Duration Track duration in milliseconds .TP .B string ImgHandle [experimental] .sp Track image handle, available and valid only during the lifetime of an OBEX BIP connection to the ObexPort. .UNINDENT .UNINDENT .UNINDENT .SS object Device [readonly] .INDENT 0.0 .INDENT 3.5 Device object path. .UNINDENT .UNINDENT .SS string Name [readonly] .INDENT 0.0 .INDENT 3.5 Player name .UNINDENT .UNINDENT .SS string Type [readonly] .INDENT 0.0 .INDENT 3.5 Player type .sp Possible values: .INDENT 0.0 .INDENT 3.5 \(dqAudio\(dq \(dqVideo\(dq \(dqAudio Broadcasting\(dq \(dqVideo Broadcasting\(dq .UNINDENT .UNINDENT .UNINDENT .UNINDENT .SS string Subtype [readonly] .INDENT 0.0 .INDENT 3.5 Player subtype .sp Possible values: .INDENT 0.0 .INDENT 3.5 \(dqAudio Book\(dq \(dqPodcast\(dq .UNINDENT .UNINDENT .UNINDENT .UNINDENT .SS boolean Browsable [readonly] .INDENT 0.0 .INDENT 3.5 If present indicates the player can be browsed using MediaFolder interface. .sp Possible values: .INDENT 0.0 .TP .B True Supported and active .TP .B False Supported but inactive .UNINDENT .sp Note: If supported but inactive clients can enable it by using MediaFolder interface but it might interfere in the playback of other players. .UNINDENT .UNINDENT .SS boolean Searchable [readonly] .INDENT 0.0 .INDENT 3.5 If present indicates the player can be searched using MediaFolder interface. .sp Possible values: .INDENT 0.0 .TP .B True Supported and active .TP .B False Supported but inactive .UNINDENT .sp Note: If supported but inactive clients can enable it by using MediaFolder interface but it might interfere in the playback of other players. .UNINDENT .UNINDENT .SS object Playlist .INDENT 0.0 .INDENT 3.5 Playlist object path. .UNINDENT .UNINDENT .SS uint16 ObexPort [readonly, experimental] .INDENT 0.0 .INDENT 3.5 If present indicates the player can get cover art using BIP over OBEX on this PSM port. .UNINDENT .UNINDENT .\" Generated by docutils manpage writer. . bluez-5.82/doc/PaxHeaders/supported-features.txt0000644000000000000000000000005014766002272017023 xustar0020 atime=1743515578 20 ctime=1743591290 bluez-5.82/doc/supported-features.txt0000644000000000000000000000203714766002272016506 0ustar00rootrootSupported features in BlueZ =========================== Note that some profiles/roles will depend on external components such as oFono or ConnMan. Profile/protocol Version Role(s) --------------------------------------------------------------------------- GAP 4.2 (LE) Central, Peripheral, Observer, Broadcaster L2CAP 4.2 Server, Client SDP 4.2 Server, Client GATT 4.2 Server, Client SDAP 1.1 Server, Client RFCOMM 1.1 Server, Client SPP 1.2 Server, Client PXP 1.0 Reporter, Monitor HOGP 1.0 Host HTP 1.0 TIP 1.0 CSCP 1.0 Collector SAP 1.1 Server DUN 1.1 Server, Client DID 1.3 Server, Client HFP 1.6 AG, HF HSP 1.2 AG, HS GAVDTP 1.2 Source, Sink AVDTP 1.3 Source, Sink A2DP 1.3 Source, Sink AVCTP 1.3 CT, TG AVRCP 1.5 CT, TG GOEP 2.0 Client, Server FTP 1.3 Client, Server OPP 1.2 Client, Server SYNCH 1.1 Client PBAP 1.1 Client, Server MAP 1.0 Server MAP 1.4 Client HID 1.1 Host BNEP 1.0 PAN 1.0 PANU, NAP, GN HCRP 1.2 MCAP 1.0 HDP 1.0 bluez-5.82/doc/PaxHeaders/org.bluez.Device.rst0000644000000000000000000000005014766002272016260 xustar0020 atime=1743515578 20 ctime=1743591290 bluez-5.82/doc/org.bluez.Device.rst0000644000000000000000000002410014766002272015736 0ustar00rootroot================ org.bluez.Device ================ ------------------------------------ BlueZ D-Bus Device API documentation ------------------------------------ :Version: BlueZ :Date: October 2023 :Manual section: 5 :Manual group: Linux System Administration Interface ========= :Service: org.bluez :Interface: org.bluez.Device1 :Object path: [variable prefix]/{hci0,hci1,...}/dev_XX_XX_XX_XX_XX_XX Methods ------- void Connect() `````````````` Connects all profiles the remote device supports that can be connected to and have been flagged as auto-connectable. If only subset of profiles is already connected it will try to connect currently disconnected ones. If at least one profile was connected successfully this method will indicate success. For dual-mode devices only one bearer is connected at time, the conditions are in the following order: 1. Connect the disconnected bearer if already connected. 2. Connect first the bonded bearer. If no bearers are bonded or both are skip and check latest seen bearer. 3. Connect last seen bearer, in case the timestamps are the same BR/EDR takes precedence, or in case **PreferredBearer** has been set to a specific bearer then that is used instead. Possible errors: :org.bluez.Error.NotReady: :org.bluez.Error.Failed: :org.bluez.Error.InProgress: :org.bluez.Error.AlreadyConnected: void Disconnect() ````````````````` Disconnects all connected profiles and then terminates low-level ACL connection. ACL connection will be terminated even if some profiles were not disconnected properly e.g. due to misbehaving device. This method can be also used to cancel a preceding Connect call before a reply to it has been received. For non-trusted devices connected over LE bearer calling this method will disable incoming connections until Connect method is called again. Possible errors: :org.bluez.Error.NotConnected: void ConnectProfile(string uuid) ```````````````````````````````` Connects a specific profile of this device. The UUID provided is the remote service UUID for the profile. Possible errors: :org.bluez.Error.Failed: :org.bluez.Error.InProgress: :org.bluez.Error.InvalidArguments: :org.bluez.Error.NotAvailable: :org.bluez.Error.NotReady: void DisconnectProfile(string uuid) ``````````````````````````````````` Disconnects a specific profile of this device. The profile needs to be registered client profile. There is no connection tracking for a profile, so as long as the profile is registered this will always succeed. Possible errors: :org.bluez.Error.Failed: :org.bluez.Error.InProgress: :org.bluez.Error.InvalidArguments: :org.bluez.Error.NotSupported: void Pair() ``````````` Connects to the remote device and initiate pairing procedure then proceed with service discovery. If the application has registered its own agent, then that specific agent will be used. Otherwise it will use the default agent. Only for applications like a pairing wizard it would make sense to have its own agent. In almost all other cases the default agent will handle this just fine. In case there is no application agent and also no default agent present, this method will fail. Possible errors: :org.bluez.Error.InvalidArguments: :org.bluez.Error.Failed: :org.bluez.Error.AlreadyExists: :org.bluez.Error.AuthenticationCanceled: :org.bluez.Error.AuthenticationFailed: :org.bluez.Error.AuthenticationRejected: :org.bluez.Error.AuthenticationTimeout: :org.bluez.Error.ConnectionAttemptFailed: void CancelPairing() ```````````````````` Cancels a pairing operation initiated by the **Pair** method. Possible errors: :org.bluez.Error.DoesNotExist: :org.bluez.Error.Failed: array{array{byte}} GetServiceRecords() [experimental] ````````````````````````````````````````````````````` Returns all currently known BR/EDR service records for the device. Each individual byte array represents a raw SDP record, as defined by the Bluetooth Service Discovery Protocol specification. This method is intended to be only used by compatibility layers like Wine, that need to provide access to raw SDP records to support foreign Bluetooth APIs. General applications should instead use the Profile API for services-related functionality. Possible errors: :org.bluez.Error.Failed: :org.bluez.Error.NotReady: :org.bluez.Error.NotConnected: :org.bluez.Error.DoesNotExist: Properties ---------- string Address [readonly] ````````````````````````` The Bluetooth device address of the remote device. string AddressType [readonly] ````````````````````````````` The Bluetooth device Address Type. For dual-mode and BR/EDR only devices this defaults to "public". Single mode LE devices may have either value. If remote device uses privacy than before pairing this represents address type used for connection and Identity Address after pairing. Possible values: :"public": Public address :"random": Random address string Name [readonly, optional] ```````````````````````````````` The Bluetooth remote name. This value is only present for completeness. It is better to always use the **Alias** property when displaying the devices name. If the **Alias** property is unset, it will reflect this value which makes it more convenient. string Icon [readonly, optional] ```````````````````````````````` Proposed icon name according to the freedesktop.org icon naming specification. uint32 Class [readonly, optional] ````````````````````````````````` The Bluetooth class of device of the remote device. uint16 Appearance [readonly, optional] `````````````````````````````````````` External appearance of device, as found on GAP service. array{string} UUIDs [readonly, optional] ```````````````````````````````````````` List of 128-bit UUIDs that represents the available remote services. boolean Paired [readonly] ````````````````````````` Indicates if the remote device is paired. Paired means the pairing process where devices exchange the information to establish an encrypted connection has been completed. boolean Bonded [readonly] ````````````````````````` Indicates if the remote device is bonded. Bonded means the information exchanged on pairing process has been stored and will be persisted. boolean Connected [readonly] ```````````````````````````` Indicates if the remote device is currently connected. A PropertiesChanged signal indicate changes to this status. boolean Trusted [readwrite] ``````````````````````````` Indicates if the remote is seen as trusted. This setting can be changed by the application. boolean Blocked [readwrite] ``````````````````````````` If set to true any incoming connections from the device will be immediately rejected. Any device drivers will also be removed and no new ones will be probed as long as the device is blocked. boolean WakeAllowed [readwrite] ``````````````````````````````` If set to true this device will be allowed to wake the host from system suspend. string Alias [readwrite] ```````````````````````` The name alias for the remote device. The alias can be used to have a different friendly name for the remote device. In case no alias is set, it will return the remote device name. Setting an empty string as alias will convert it back to the remote device name. When resetting the alias with an empty string, the property will default back to the remote name. object Adapter [readonly] ````````````````````````` The object path of the adapter the device belongs to. boolean LegacyPairing [readonly] ```````````````````````````````` Set to true if the device only supports the pre-2.1 pairing mechanism. This property is useful during device discovery to anticipate whether legacy or simple pairing will occur if pairing is initiated. Note that this property can exhibit false-positives in the case of Bluetooth 2.1 (or newer) devices that have disabled Extended Inquiry Response support. string Modalias [readonly, optional] ```````````````````````````````````` Remote Device ID information in modalias format used by the kernel and udev. int16 RSSI [readonly, optional] ``````````````````````````````` Received Signal Strength Indicator of the remote device (inquiry or advertising). int16 TxPower [readonly, optional] `````````````````````````````````` Advertised transmitted power level (inquiry or advertising). dict ManufacturerData [readonly, optional] `````````````````````````````````````````` Manufacturer specific advertisement data. Keys are 16 bits Manufacturer ID followed by its byte array value. dict ServiceData [readonly, optional] ````````````````````````````````````` Service advertisement data. Keys are the UUIDs in string format followed by its byte array value. bool ServicesResolved [readonly] ```````````````````````````````` Indicate whether or not service discovery has been resolved. array{byte} AdvertisingFlags [readonly] ``````````````````````````````````````` The Advertising Data Flags of the remote device. dict AdvertisingData [readonly] ``````````````````````````````` The Advertising Data of the remote device. Keys are 1 byte AD Type followed by data as byte array. Note: Only types considered safe to be handled by application are exposed. Possible values: :: Example: 0x26 0x01 0x01... array{object, dict} Sets [readonly, experimental] ````````````````````````````````````````````````` The object paths of the sets the device belongs to followed by a dictionary which can contain the following: :byte Rank: Rank of the device in the Set. string PreferredBearer [readwrite, optional, experimental] `````````````````````````````````````````````````````````` Indicate the preferred bearer when initiating a connection, only available for dual-mode devices. When changing from "bredr" to "le" the device will be removed from the 'auto-connect' list so it won't automatically be connected when adverting. Note: Changes only take effect when the device is disconnected. Possible values: :"last-seen": Connect to last seen bearer first. Default. :"bredr": Connect to BR/EDR first. :"le": Connect to LE first. bluez-5.82/doc/PaxHeaders/org.bluez.obex.Message.50000644000000000000000000000005014773213503016734 xustar0020 atime=1743591235 20 ctime=1743591291 bluez-5.82/doc/org.bluez.obex.Message.50000644000000000000000000001066714773213503016427 0ustar00rootroot.\" Man page generated from reStructuredText. . . .nr rst2man-indent-level 0 . .de1 rstReportMargin \\$1 \\n[an-margin] level \\n[rst2man-indent-level] level margin: \\n[rst2man-indent\\n[rst2man-indent-level]] - \\n[rst2man-indent0] \\n[rst2man-indent1] \\n[rst2man-indent2] .. .de1 INDENT .\" .rstReportMargin pre: . RS \\$1 . nr rst2man-indent\\n[rst2man-indent-level] \\n[an-margin] . nr rst2man-indent-level +1 .\" .rstReportMargin post: .. .de UNINDENT . RE .\" indent \\n[an-margin] .\" old: \\n[rst2man-indent\\n[rst2man-indent-level]] .nr rst2man-indent-level -1 .\" new: \\n[rst2man-indent\\n[rst2man-indent-level]] .in \\n[rst2man-indent\\n[rst2man-indent-level]]u .. .TH "ORG.BLUEZ.OBEX.MESSAGE" "5" "October 2023" "BlueZ" "Linux System Administration" .SH NAME org.bluez.obex.Message \- BlueZ D-Bus OBEX Message API documentation .SH INTERFACE .INDENT 0.0 .TP .B Service org.bluez.obex .TP .B Interface org.bluez.obex.Message1 .TP .B Object path [Session object path]/message{#} .UNINDENT .SS Methods .SS object, dict Get(string targetfile, boolean attachment) .INDENT 0.0 .INDENT 3.5 Download message and store it in the target file. .sp If an empty target file is given, a temporary file will be automatically generated. .sp The returned path represents the newly created transfer, which should be used to find out if the content has been successfully transferred or if the operation fails. .sp The properties of this transfer are also returned along with the object path, to avoid a call to GetProperties, see \fBorg.bluez.obex.Transfer(5)\fP for the possible list of properties. .sp Possible errors: .INDENT 0.0 .TP .B org.bluez.obex.Error.InvalidArguments .TP .B org.bluez.obex.Error.Failed .UNINDENT .UNINDENT .UNINDENT .SS Properties .SS string Folder [readonly] .INDENT 0.0 .INDENT 3.5 Folder which the message belongs to .UNINDENT .UNINDENT .SS string Subject [readonly] .INDENT 0.0 .INDENT 3.5 Message subject .UNINDENT .UNINDENT .SS string Timestamp [readonly] .INDENT 0.0 .INDENT 3.5 Message timestamp .UNINDENT .UNINDENT .SS string Sender [readonly] .INDENT 0.0 .INDENT 3.5 Message sender name .UNINDENT .UNINDENT .SS string SenderAddress [readonly] .INDENT 0.0 .INDENT 3.5 Message sender address .UNINDENT .UNINDENT .SS string ReplyTo [readonly] .INDENT 0.0 .INDENT 3.5 Message Reply\-To address .UNINDENT .UNINDENT .SS string Recipient [readonly] .INDENT 0.0 .INDENT 3.5 Message recipient name .UNINDENT .UNINDENT .SS string RecipientAddress [readonly] .INDENT 0.0 .INDENT 3.5 Message recipient address .UNINDENT .UNINDENT .SS string Type [readonly] .INDENT 0.0 .INDENT 3.5 Message type .sp Possible values: .INDENT 0.0 .TP .B \(dqemail\(dq .TP .B \(dqsms\-gsm\(dq .TP .B \(dqsms\-cdma\(dq .TP .B \(dqmms\(dq .UNINDENT .UNINDENT .UNINDENT .SS uint64 Size [readonly] .INDENT 0.0 .INDENT 3.5 Message size in bytes .UNINDENT .UNINDENT .SS string Status [readonly] .INDENT 0.0 .INDENT 3.5 Message reception status .sp Possible values: .INDENT 0.0 .TP .B \(dqcomplete\(dq .TP .B \(dqfractioned\(dq .TP .B \(dqnotification\(dq .UNINDENT .UNINDENT .UNINDENT .SS boolean Priority [readonly] .INDENT 0.0 .INDENT 3.5 Message priority flag .UNINDENT .UNINDENT .SS boolean Read [read/write] .INDENT 0.0 .INDENT 3.5 Message read flag .UNINDENT .UNINDENT .SS boolean Deleted [writeonly] .INDENT 0.0 .INDENT 3.5 Message deleted flag .UNINDENT .UNINDENT .SS boolean Sent [readonly] .INDENT 0.0 .INDENT 3.5 Message sent flag .UNINDENT .UNINDENT .SS boolean Protected [readonly] .INDENT 0.0 .INDENT 3.5 Message protected flag .UNINDENT .UNINDENT .SS string DeliveryStatus [readonly] [optional] .INDENT 0.0 .INDENT 3.5 Message delivery status .sp Possible values: .INDENT 0.0 .TP .B \(dqdelivered\(dq .TP .B \(dqsent\(dq .TP .B \(dqunknown\(dq .UNINDENT .UNINDENT .UNINDENT .SS uint64 ConversationId [readonly] [required] .INDENT 0.0 .INDENT 3.5 Message conversation id sent by Server Unique identification of the conversation .UNINDENT .UNINDENT .SS string ConversationName [readonly] [optional] .INDENT 0.0 .INDENT 3.5 Human readable name of the conversation .UNINDENT .UNINDENT .SS string Direction [readonly] [required] .INDENT 0.0 .INDENT 3.5 Indicate the direction of the message .sp Possible values: .INDENT 0.0 .TP .B \(dqincoming\(dq .TP .B \(dqoutgoing\(dq .TP .B \(dqoutgoingdraft\(dq .TP .B \(dqoutgoingpending\(dq .UNINDENT .UNINDENT .UNINDENT .SS string AttachmentMimeTypes [readonly] [optional] .INDENT 0.0 .INDENT 3.5 MIME type of the attachment .UNINDENT .UNINDENT .\" Generated by docutils manpage writer. . bluez-5.82/doc/PaxHeaders/pics-opp.txt0000644000000000000000000000005012572170650014713 xustar0020 atime=1743516877 20 ctime=1743591290 bluez-5.82/doc/pics-opp.txt0000644000000000000000000002240512572170650014377 0ustar00rootrootOPP PICS for the PTS tool. PTS version: 6.2.0 * - different than PTS defaults # - not yet implemented/supported M - mandatory O - optional Roles ------------------------------------------------------------------------------- Parameter Name Selected Description ------------------------------------------------------------------------------- TSPC_OPP_1_1 True (*) Role: Object Push Client (C.1) TSPC_OPP_1_2 True (*) Role: Object Push Server (C.1) ------------------------------------------------------------------------------- C.1: Mandatory to support at least one of the defined roles. ------------------------------------------------------------------------------- Client Profile Version ------------------------------------------------------------------------------- Parameter Name Selected Description ------------------------------------------------------------------------------- TSPC_OPP_1b_1 False Client supports OPP version 1.1. (C.1) TSPC_OPP_1b_2 True (*) Client supports OPP version 1.2. (C.1) ------------------------------------------------------------------------------- C.1: It is mandatory to support at least one of the profile versions. ------------------------------------------------------------------------------- Client Application Features ------------------------------------------------------------------------------- Parameter Name Selected Description ------------------------------------------------------------------------------- TSPC_OPP_2_1 True Client: Perform Service Discovery request (M) TSPC_OPP_2_2 True Client: Authentication/PIN exchange supported. (M) TSPC_OPP_2_2a True (*) Client: Require Authentication/PIN by default. (O) TSPC_OPP_2_3 True Client: Object Push (M) TSPC_OPP_2_4 True (*) Client: vCard 2.1 (C.3) TSPC_OPP_2_5 False Client: vCalender 1.0 (O) TSPC_OPP_2_6 False Client: vMsg as defined in IrMC 1.1 (O) TSPC_OPP_2_7 False Client: vNote as defined in IrMC 1.1 (O) TSPC_OPP_2_8 True (*) Client: Support content formats other than those declared in TSPC_OPP_2_4 through TSPC_OPP_2_7. (O) TSPC_OPP_2_8a False Client: Support specific set of other content formats. (C.4) TSPC_OPP_2_8b True (*) Client: Support all content formats. (C.4) TSPC_OPP_2_9 True (*) Client: Push multiple vCard objects. (O) TSPC_OPP_2_9a True (*) Client: Push multiple vCard objects using different PUT operations. (C.5) TSPC_OPP_2_9b False Client: Push multiple vCard objects using the same PUT operation. (C.5) TSPC_OPP_2_10 True (*) Client: Push multiple vCalender objects. (O) TSPC_OPP_2_10a True Client: Push multiple vCalendar objects using different PUT operations. (C.6) TSPC_OPP_2_10b False Client: Push multiple vCalendar objects using the same PUT operation. (C.6) TSPC_OPP_2_11 True (*) Client: Push multiple vMsg objects. (O) TSPC_OPP_2_11a True (*) Client: Push multiple vMsg objects using different PUT operations. (C.7) TSPC_OPP_2_11b False Client: Push multiple vMsg objects using the same PUT operation. (C.7) TSPC_OPP_2_12 True (*) Client: Push multiple vNote objects. (O) TSPC_OPP_2_12a True (*) Client: Push multiple vNote objects using different PUT operations. (C.8) TSPC_OPP_2_12b False Client: Push multiple vNote objects using the same PUT operation. (C.8) TSPC_OPP_2_13 True (*) Client: Pull business card (O) TSPC_OPP_2_14 True (*) Client: vCard 2.1 (C.1) TSPC_OPP_2_15 True (*) Client: Exchange business card (O) TSPC_OPP_2_16 False Client: vCard 2.1 (C.2) TSPC_OPP_2_17 True (*) GOEP v2 (C.9) TSPC_OPP_2_18 True (*) GOEP v2 Backward Compatibility (C.9) TSPC_OPP_2_19 True (*) OBEX over L2CAP (C.9) TSPC_OPP_2_20 False OBEX Reliable Session (C.10) TSPC_OPP_2_21 False OBEX SRM (C.10) TSPC_OPP_2_22 False Send OBEX SRMP header (C.10) TSPC_OPP_2_23 False Receive OBEX SRMP header (C.11) ------------------------------------------------------------------------------- C.1: Mandatory to Support IF (TSPC_OPP_2_13) Business Card Pull is supported. C.2: Mandatory to Support IF (TSPC_OPP_2_15) Business Card Exchange is supported. C.3: vCard 2.1 support is required for devices containing phonebook applications. vCard 2.1 support optional for other devices. C.4: Mandatory to support one of TSPC_OPP_2_8a or TSPC_OPP_2_8b if TSPC_OPP_2_8 is supported. Otherwise, both items are excluded. C.5: Mandatory to support at least one of TSPC_OPP_2_9a and TSPC_OPP_2_9b if TSPC_OPP_2_9 is supported. Otherwise, both items are excluded. C.6: Mandatory to support at least one of TSPC_OPP_2_10a and TSPC_OPP_2_10b if TSPC_OPP_2_10 is supported. Otherwise, both items are excluded. C.7: Mandatory to support at least one of TSPC_OPP_2_11a and TSPC_OPP_2_11b if TSPC_OPP_2_11 is supported. Otherwise, both items are excluded. C.8: Mandatory to support at least one of TSPC_OPP_2_12a and TSPC_OPP_2_12b if TSPC_OPP_2_12 is supported. Otherwise, both items are excluded. C.9: Mandatory if TSPC_OPP_1b_2 supported. C.10: Optional to support if TSPC_OPP_1b_2 supported else excluded. C.11: Mandatory if TSPC_OPP_17 and TSPC_OPP_21 supported else excluded. ------------------------------------------------------------------------------- Server Profile Version ------------------------------------------------------------------------------- Parameter Name Selected Description ------------------------------------------------------------------------------- TSPC_OPP_2b_1 False Server supports OPP version 1.1. TSPC_OPP_2b_2 True (*) Server supports OPP version 1.2. ------------------------------------------------------------------------------- C.1: It is mandatory to support at least one of the profile versions. ------------------------------------------------------------------------------- Server Application Features ------------------------------------------------------------------------------- Parameter Name Selected Description ------------------------------------------------------------------------------- TSPC_OPP_3_1 True Server: Provide information on supported contents type on service discovery request. (M) TSPC_OPP_3_2 True Server: Authentication/PIN exchange supported. (M) TSPC_OPP_3_3 True Server: Object Push (M) TSPC_OPP_3_3a True (*) Server: Receive multiple objects in the same PUT operation. (O) TSPC_OPP_3_4 True (*) Server: vCard 2.1 (C.3) TSPC_OPP_3_5 False Server: vCalender 1.0 format (O) TSPC_OPP_3_6 False Server: vMsg as defined in IrMC 1.1 (O) TSPC_OPP_3_7 False Server: vNote as defined in IrMC 1.1 (O) TSPC_OPP_3_8 True (*) Server: Support content formats other than those declared in TSPC_OPP_3_4 through TSPC_OPP_3_7. (O) TSPC_OPP_3_8a False Server: Support specific set of other content formats. (C.4) TSPC_OPP_3_8b True (*) Server: Support all content formats. (C.4) TSPC_OPP_3_9 True (*) Server: Object Push vCard reject. (O) TSPC_OPP_3_10 True (*) Server: Object Push vCal reject. (O) TSPC_OPP_3_11 True (*) Server: Object Push vMsg reject. (O) TSPC_OPP_3_12 True (*) Server: Object Push vNote reject. (O) TSPC_OPP_3_13 True (*) Server: Business card pull (O.1) TSPC_OPP_3_14 True (*) Server: vCard 2.1 (C.1) TSPC_OPP_3_15 True (*) Server: Business card pull reject. (O) TSPC_OPP_3_16 True (*) Server: Business card exchange (O.2) TSPC_OPP_3_17 True (*) Server: vCard 2.1 (C.2) TSPC_OPP_3_18 True (*) Server: Business card exchange reject. (O) TSPC_OPP_3_19 True (*) GOEP v2 (C.5) TSPC_OPP_3_20 True (*) GOEP v2 Backward Compatibility (C.5) TSPC_OPP_3_21 True (*) OBEX over L2CAP (C.5) TSPC_OPP_3_22 False OBEX Reliable Session (C.16) TSPC_OPP_3_23 False OBEX SRM (C.6) TSPC_OPP_3_24 False Send OBEX SRMP header (C.6) TSPC_OPP_3_25 False Receive OBEX SRMP header (C.7) ------------------------------------------------------------------------------- O.1: IF NOT Supported, an error message must be sent on request for Business Card Pull. O.2: IF NOT Supported, an error message must be sent on request for Business Card Exchange. C.1: Mandatory to Support IF (TSPC_OPP_3_13) Business Card Pull is supported. C.2: Mandatory to Support IF (TSPC_OPP_3_16) Business Card Exchange is supported. C.3: vCard 2.1 support is required for devices containing phonebook applications. vCard 2.1 support optional for other devices. C.4: Mandatory to support one of TSPC_OPP_3_8a or TSPC_OPP_3_8b if TSPC_OPP_3_8 is supported. Otherwise, both items are excluded. C.5: Mandatory if TSPC_OPP_2b_2 supported. C.6: Optional to support if TSPC_OPP_2b_2 supported, else excluded. C.7: Mandatory if TSPC_OPP_3_19 and TSPC_OPP_3_23 supported else excluded. ------------------------------------------------------------------------------- Additional OPP Capabilities ------------------------------------------------------------------------------- Parameter Name Selected Description ------------------------------------------------------------------------------- TSPC_OPP_4_1 False Abort-Push Operation (O) TSPC_OPP_4_2 False Intentionally Left Blank (N/A) TSPC_OPP_4_3 True (*) Multiple vCards transferred as a single vObject (C.1) TSPC_OPP_4_4 True (*) Multiple vCards transfer (C.1) TSPC_OPP_4_5 True (*) vCards with multiple Phone Number Fields (C.1) TSPC_OPP_4_6 True (*) Push vCal to Different Time Zone Server (C.1) ------------------------------------------------------------------------------- C.1: Optional if TSPC_OPP_1_2 is supported, otherwise excluded. ------------------------------------------------------------------------------- bluez-5.82/doc/PaxHeaders/org.bluez.Battery.50000644000000000000000000000005014773213443016031 xustar0020 atime=1743591203 20 ctime=1743591291 bluez-5.82/doc/org.bluez.Battery.50000644000000000000000000000310314773213443015507 0ustar00rootroot.\" Man page generated from reStructuredText. . . .nr rst2man-indent-level 0 . .de1 rstReportMargin \\$1 \\n[an-margin] level \\n[rst2man-indent-level] level margin: \\n[rst2man-indent\\n[rst2man-indent-level]] - \\n[rst2man-indent0] \\n[rst2man-indent1] \\n[rst2man-indent2] .. .de1 INDENT .\" .rstReportMargin pre: . RS \\$1 . nr rst2man-indent\\n[rst2man-indent-level] \\n[an-margin] . nr rst2man-indent-level +1 .\" .rstReportMargin post: .. .de UNINDENT . RE .\" indent \\n[an-margin] .\" old: \\n[rst2man-indent\\n[rst2man-indent-level]] .nr rst2man-indent-level -1 .\" new: \\n[rst2man-indent\\n[rst2man-indent-level]] .in \\n[rst2man-indent\\n[rst2man-indent-level]]u .. .TH "ORG.BLUEZ.BATTERY" "5" "October 2023" "BlueZ" "Linux System Administration" .SH NAME org.bluez.Battery \- BlueZ D-Bus Battery API documentation .SH INTERFACE .INDENT 0.0 .TP .B Service org.bluez .TP .B Interface org.bluez.Battery1 .TP .B Object path [variable prefix]/{hci0,hci1,...}/dev_XX_XX_XX_XX_XX_XX .UNINDENT .SS Properties .SS byte Percentage [readonly] .INDENT 0.0 .INDENT 3.5 The percentage of battery left as an unsigned 8\-bit integer. .UNINDENT .UNINDENT .SS string Source [readonly, optional] .INDENT 0.0 .INDENT 3.5 Describes where the battery information comes from. .sp This property is informational only and may be useful for debugging purposes. .sp Providers from \fBorg.bluez.BatteryProvider(5)\fP may make use of this property to indicate where the battery report comes from (e.g. \(dqHFP 1.7\(dq, \(dqHID\(dq, or the profile UUID). .UNINDENT .UNINDENT .\" Generated by docutils manpage writer. . bluez-5.82/doc/PaxHeaders/org.bluez.GattCharacteristic.50000644000000000000000000000005014773213463020171 xustar0020 atime=1743591219 20 ctime=1743591291 bluez-5.82/doc/org.bluez.GattCharacteristic.50000644000000000000000000002532314773213463017657 0ustar00rootroot.\" Man page generated from reStructuredText. . . .nr rst2man-indent-level 0 . .de1 rstReportMargin \\$1 \\n[an-margin] level \\n[rst2man-indent-level] level margin: \\n[rst2man-indent\\n[rst2man-indent-level]] - \\n[rst2man-indent0] \\n[rst2man-indent1] \\n[rst2man-indent2] .. .de1 INDENT .\" .rstReportMargin pre: . RS \\$1 . nr rst2man-indent\\n[rst2man-indent-level] \\n[an-margin] . nr rst2man-indent-level +1 .\" .rstReportMargin post: .. .de UNINDENT . RE .\" indent \\n[an-margin] .\" old: \\n[rst2man-indent\\n[rst2man-indent-level]] .nr rst2man-indent-level -1 .\" new: \\n[rst2man-indent\\n[rst2man-indent-level]] .in \\n[rst2man-indent\\n[rst2man-indent-level]]u .. .TH "ORG.BLUEZ.GATTCHARACTERISTIC" "5" "October 2023" "BlueZ" "Linux System Administration" .SH NAME org.bluez.GattCharacteristic \- BlueZ D-Bus GattCharacteristic API documentation .SH DESCRIPTION .sp GATT local/server and remote/client characteristic attribute representation share the same high\-level D\-Bus API. .sp Local/Server refers to GATT based characteristics exported by a plugin or an external application. .sp Remote/Client refers to GATT characteristics exported by the peer. .SH INTERFACE .SS Client .INDENT 0.0 .TP .B Service org.bluez .TP .B Interface org.bluez.GattCharacteristic1 .TP .B Object path [variable prefix]/{hci0,hci1,...}/dev_XX_XX_XX_XX_XX_XX/serviceXX/charYYYY .UNINDENT .SS Server .INDENT 0.0 .TP .B Service unique name .TP .B Interface org.bluez.GattCharacteristic1 .TP .B Object path freely definable .UNINDENT .SS Methods .SS array{byte} ReadValue(dict options) .INDENT 0.0 .INDENT 3.5 Issues a request to read the value of the characteristic and returns the value if the operation was successful. .sp Possible options: .INDENT 0.0 .TP .B uint16_t offset Read start offset in bytes. .TP .B uint16_t mtu (server only) Exchange MTU in bytes. .TP .B object device (server only) Device object. .TP .B string link (server only) Link type. .sp Possible values: .INDENT 7.0 .TP .B \(dqBR/EDR\(dq .TP .B \(dqLE\(dq .UNINDENT .UNINDENT .sp Possible Errors: .INDENT 0.0 .TP .B org.bluez.Error.Failed Possible values: string 0x80 \- 0x9f .TP .B org.bluez.Error.InProgress .TP .B org.bluez.Error.NotPermitted .TP .B org.bluez.Error.NotAuthorized .TP .B org.bluez.Error.InvalidOffset .TP .B org.bluez.Error.NotSupported .UNINDENT .UNINDENT .UNINDENT .SS void WriteValue(array{byte} value, dict options) .INDENT 0.0 .INDENT 3.5 Issues a request to write the value of the characteristic. .sp Possible options: .INDENT 0.0 .TP .B uint16 offset Write start offset in bytes. .TP .B string type Possible values: .INDENT 7.0 .TP .B \(dqcommand\(dq Use Write without response procedure. .TP .B \(dqrequest\(dq Use Write with response procedure. .TP .B \(dqreliable\(dq Use Reliable Write procedure. .UNINDENT .TP .B uint16 mtu Exchanged MTU (Server only). .TP .B object device Device path (Server only). .TP .B string link Link type (Server only). .sp Possible values: .INDENT 7.0 .TP .B \(dqBR/EDR\(dq .TP .B \(dqLE\(dq .UNINDENT .TP .B boolean prepare\-authorize True if prepare authorization request. .UNINDENT .sp Possible Errors: .INDENT 0.0 .TP .B org.bluez.Error.Failed Possible values: string 0x80 \- 0x9f .TP .B org.bluez.Error.InProgress .TP .B org.bluez.Error.NotPermitted .TP .B org.bluez.Error.InvalidValueLength .TP .B org.bluez.Error.NotAuthorized .TP .B org.bluez.Error.NotSupported .TP .B org.bluez.Error.ImproperlyConfigured .UNINDENT .UNINDENT .UNINDENT .SS fd, uint16 AcquireWrite(dict options) [optional] .INDENT 0.0 .INDENT 3.5 Acquire file descriptor and MTU for writing. Only sockets are supported. Usage of WriteValue will be locked causing it to return NotPermitted error. .sp For server the MTU returned shall be equal or smaller than the negotiated MTU. .sp For client it only works with characteristic that has \fBWriteAcquired\fP property which relies on write\-without\-response \fBFlag\fP\&. .sp To release the lock the client shall close the file descriptor, a HUP is generated in case the device is disconnected. .sp Note: the MTU can only be negotiated once and is symmetric therefore this method may be delayed in order to have the exchange MTU completed, because of that the file descriptor is closed during reconnections as the MTU has to be renegotiated. .sp Possible options: .INDENT 0.0 .TP .B object device Object Device (Server only). .TP .B uint16 mtu Exchanged MTU (Server only). .TP .B string link Link type (Server only). .sp Possible values: .INDENT 7.0 .TP .B \(dqBR/EDR\(dq .TP .B \(dqLE\(dq .UNINDENT .UNINDENT .sp Possible Errors: .INDENT 0.0 .TP .B org.bluez.Error.Failed .TP .B org.bluez.Error.NotSupported .UNINDENT .UNINDENT .UNINDENT .SS fd, uint16 AcquireNotify(dict options) [optional] .INDENT 0.0 .INDENT 3.5 Acquire file descriptor and MTU for notify. Only sockets are support. .sp Usage of StartNotify will be locked causing it to return \fBorg.bluez.Error.NotPermitted\fP\&. .sp For server the MTU returned shall be equal or smaller than the negotiated MTU. .sp Only works with characteristic that has \fBNotifyAcquired\fP property which relies on presence of \fB\(dqnotify\(dq or \(dqindicate\(dq\fP \fBFlag\fP and no other client have called \fBStartNotify()\fP\&. .sp Notification are enabled during this procedure so \fBStartNotify()\fP shall not be called, any notification will be dispatched via file descriptor therefore the Value property is not affected during the time where notify has been acquired. .sp To release the lock the client shall close the file descriptor, a HUP is generated in case the device is disconnected. .sp As a client if indication procedure is used the confirmation is generated automatically once received, for a server if the file descriptor is writable (POLLOUT) then upon receiving a confirmation from the client one byte (0x01) is written to the file descriptor. .sp Note: the MTU can only be negotiated once and is symmetric therefore this method may be delayed in order to have the exchange MTU completed, because of that the file descriptor is closed during reconnections as the MTU has to be renegotiated. .sp Possible options: .INDENT 0.0 .TP .B object device Object Device (Server only). .TP .B uint16 mtu Exchanged MTU (Server only). .TP .B string link Link type (Server only). .sp Possible values: .INDENT 7.0 .TP .B \(dqBR/EDR\(dq .TP .B \(dqLE\(dq .UNINDENT .UNINDENT .sp Possible Errors: .INDENT 0.0 .TP .B org.bluez.Error.Failed .TP .B org.bluez.Error.NotSupported .TP .B org.bluez.Error.NotPermitted .UNINDENT .UNINDENT .UNINDENT .SS void StartNotify() .INDENT 0.0 .INDENT 3.5 Starts a notification session from this characteristic if it supports value notifications or indications. .sp Possible Errors: .INDENT 0.0 .TP .B org.bluez.Error.Failed .TP .B org.bluez.Error.NotPermitted .TP .B org.bluez.Error.InProgress .TP .B org.bluez.Error.NotConnected .TP .B org.bluez.Error.NotSupported .UNINDENT .UNINDENT .UNINDENT .SS void StopNotify() .INDENT 0.0 .INDENT 3.5 Stops or cancel session previously created by \fBStartNotify()\fP\&. .sp Note that notifications from a characteristic are shared between sessions thus calling StopNotify will release a single session. .sp Possible Errors: .INDENT 0.0 .TP .B org.bluez.Error.Failed .UNINDENT .UNINDENT .UNINDENT .SS void Confirm() [noreply, optional] (Server only) .INDENT 0.0 .INDENT 3.5 Confirms value was received. .sp Possible Errors: .sp org.bluez.Error.Failed .UNINDENT .UNINDENT .SS Properties .SS string UUID [read\-only] .INDENT 0.0 .INDENT 3.5 128\-bit characteristic UUID. .UNINDENT .UNINDENT .SS object Service [read\-only] .INDENT 0.0 .INDENT 3.5 Object path of the GATT service the characteristic belongs to. .UNINDENT .UNINDENT .SS array{byte} Value [read\-only, optional] .INDENT 0.0 .INDENT 3.5 The cached value of the characteristic. This property gets updated only after a successful read request and when a notification or indication is received, upon which a PropertiesChanged signal will be emitted. .UNINDENT .UNINDENT .SS boolean WriteAcquired [read\-only, optional] .INDENT 0.0 .INDENT 3.5 True, if this characteristic has been acquired by any client using AcquireWrite. .sp For client properties is ommited in case \(aqwrite\-without\-response\(aq flag is not set. .sp For server the presence of this property indicates that AcquireWrite is supported. .UNINDENT .UNINDENT .SS boolean NotifyAcquired [read\-only, optional] .INDENT 0.0 .INDENT 3.5 True, if this characteristic has been acquired by any client using AcquireNotify. .sp For client this properties is ommited in case \(aqnotify\(aq flag is not set. .sp For server the presence of this property indicates that AcquireNotify is supported. .UNINDENT .UNINDENT .SS boolean Notifying [read\-only, optional] .INDENT 0.0 .INDENT 3.5 True, if notifications or indications on this characteristic are currently enabled. .UNINDENT .UNINDENT .SS array{string} Flags [read\-only] .INDENT 0.0 .INDENT 3.5 Defines how the characteristic value can be used. See Core spec \(dqTable 3.5: Characteristic Properties bit field\(dq, and \(dqTable 3.8: Characteristic Extended Properties bit field\(dq. .sp The \(dqx\-notify\(dq and \(dqx\-indicate\(dq flags restrict access to notifications and indications by imposing write restrictions on a characteristic\(aqs client characteristic configuration descriptor. .sp Possible values: .INDENT 0.0 .TP .B \(dqbroadcast\(dq .TP .B \(dqread\(dq .TP .B \(dqwrite\-without\-response\(dq .TP .B \(dqwrite\(dq .TP .B \(dqnotify\(dq .TP .B \(dqindicate\(dq .TP .B \(dqauthenticated\-signed\-writes\(dq .TP .B \(dqextended\-properties\(dq .TP .B \(dqreliable\-write\(dq .TP .B \(dqwritable\-auxiliaries\(dq .TP .B \(dqencrypt\-read\(dq .TP .B \(dqencrypt\-write\(dq .TP .B \(dqencrypt\-notify\(dq (Server only) .TP .B \(dqencrypt\-indicate\(dq (Server only) .TP .B \(dqencrypt\-authenticated\-read\(dq .TP .B \(dqencrypt\-authenticated\-write\(dq .TP .B \(dqencrypt\-authenticated\-notify\(dq (Server only) .TP .B \(dqencrypt\-authenticated\-indicate\(dq (Server only) .TP .B \(dqsecure\-read\(dq (Server only) .TP .B \(dqsecure\-write\(dq (Server only) .TP .B \(dqsecure\-notify\(dq (Server only) .TP .B \(dqsecure\-indicate\(dq (Server only) .TP .B \(dqauthorize\(dq .UNINDENT .UNINDENT .UNINDENT .SS uint16 Handle [read\-only] (Client Only) .INDENT 0.0 .INDENT 3.5 Characteristic handle. .UNINDENT .UNINDENT .SS uint16 Handle [read\-write, optional] (Server Only) .INDENT 0.0 .INDENT 3.5 Characteristic handle. When available in the server it would attempt to use to allocate into the database which may fail, to auto allocate the value 0x0000 shall be used which will cause the allocated handle to be set once registered. .UNINDENT .UNINDENT .SS uint16 MTU [read\-only] .INDENT 0.0 .INDENT 3.5 Characteristic MTU, this is valid both for \fBReadValue()\fP and \fBWriteValue()\fP but either method can use long procedures when supported. .UNINDENT .UNINDENT .\" Generated by docutils manpage writer. . bluez-5.82/doc/PaxHeaders/org.bluez.MediaItem.50000644000000000000000000000005014773213453016256 xustar0020 atime=1743591211 20 ctime=1743591291 bluez-5.82/doc/org.bluez.MediaItem.50000644000000000000000000000622614773213453015745 0ustar00rootroot.\" Man page generated from reStructuredText. . . .nr rst2man-indent-level 0 . .de1 rstReportMargin \\$1 \\n[an-margin] level \\n[rst2man-indent-level] level margin: \\n[rst2man-indent\\n[rst2man-indent-level]] - \\n[rst2man-indent0] \\n[rst2man-indent1] \\n[rst2man-indent2] .. .de1 INDENT .\" .rstReportMargin pre: . RS \\$1 . nr rst2man-indent\\n[rst2man-indent-level] \\n[an-margin] . nr rst2man-indent-level +1 .\" .rstReportMargin post: .. .de UNINDENT . RE .\" indent \\n[an-margin] .\" old: \\n[rst2man-indent\\n[rst2man-indent-level]] .nr rst2man-indent-level -1 .\" new: \\n[rst2man-indent\\n[rst2man-indent-level]] .in \\n[rst2man-indent\\n[rst2man-indent-level]]u .. .TH "ORG.BLUEZ.MEDIAITEM" "5" "September 2023" "BlueZ" "Linux System Administration" .SH NAME org.bluez.MediaItem \- BlueZ D-Bus MediaItem API documentation .SH INTERFACE .INDENT 0.0 .TP .B Service unique name (Target role) org.bluez (Controller role) .TP .B Interface org.bluez.MediaItem1 .TP .B Object path freely definable (Target role) [variable prefix]/{hci0,hci1,...}/dev_XX_XX_XX_XX_XX_XX/playerX/itemX (Controller role) .UNINDENT .SS Methods .SS void Play() .INDENT 0.0 .INDENT 3.5 Play item .sp Possible Errors: .INDENT 0.0 .TP .B org.bluez.Error.NotSupported .TP .B org.bluez.Error.Failed .UNINDENT .UNINDENT .UNINDENT .SS void AddtoNowPlaying() .INDENT 0.0 .INDENT 3.5 Add item to now playing list .sp Possible Errors: .INDENT 0.0 .TP .B org.bluez.Error.NotSupported .TP .B org.bluez.Error.Failed .UNINDENT .UNINDENT .UNINDENT .SS Properties .SS object Player [readonly] .INDENT 0.0 .INDENT 3.5 Player object path the item belongs to .UNINDENT .UNINDENT .SS string Name [readonly] .INDENT 0.0 .INDENT 3.5 Item displayable name .UNINDENT .UNINDENT .SS string Type [readonly] .INDENT 0.0 .INDENT 3.5 Item type .sp Possible values: \(dqvideo\(dq, \(dqaudio\(dq, \(dqfolder\(dq .UNINDENT .UNINDENT .SS string FolderType [readonly, optional] .INDENT 0.0 .INDENT 3.5 Folder type. .sp Possible values: \(dqmixed\(dq, \(dqtitles\(dq, \(dqalbums\(dq, \(dqartists\(dq .sp Available if property Type is \(dqFolder\(dq .UNINDENT .UNINDENT .SS boolean Playable [readonly, optional] .INDENT 0.0 .INDENT 3.5 Indicates if the item can be played .sp Available if property Type is \(dqfolder\(dq .UNINDENT .UNINDENT .SS dict Metadata [readonly] .INDENT 0.0 .INDENT 3.5 Item metadata. .sp Possible values: .INDENT 0.0 .TP .B string Title Item title name .sp Available if property Type is \(dqaudio\(dq or \(dqvideo\(dq .TP .B string Artist Item artist name .sp Available if property Type is \(dqaudio\(dq or \(dqvideo\(dq .TP .B string Album Item album name .sp Available if property Type is \(dqaudio\(dq or \(dqvideo\(dq .TP .B string Genre Item genre name .sp Available if property Type is \(dqaudio\(dq or \(dqvideo\(dq .TP .B uint32 NumberOfTracks Item album number of tracks in total .sp Available if property Type is \(dqaudio\(dq or \(dqvideo\(dq .TP .B uint32 Number Item album number .sp Available if property Type is \(dqaudio\(dq or \(dqvideo\(dq .TP .B uint32 Duration Item duration in milliseconds .sp Available if property Type is \(dqaudio\(dq or \(dqvideo\(dq .UNINDENT .UNINDENT .UNINDENT .\" Generated by docutils manpage writer. . bluez-5.82/doc/PaxHeaders/org.bluez.GattService.rst0000644000000000000000000000005014536422313017276 xustar0020 atime=1743516804 20 ctime=1743591290 bluez-5.82/doc/org.bluez.GattService.rst0000644000000000000000000000353714536422313016767 0ustar00rootroot===================== org.bluez.GattService ===================== ------------------------------------------------- BlueZ D-Bus GattService API documentation ------------------------------------------------- :Version: BlueZ :Date: October 2023 :Manual section: 5 :Manual group: Linux System Administration Description =========== GATT local/server and remote/client services share the same high-level D-Bus API. Local/Server refers to GATT based service exported by a plugin or an external application. Remote/Client refers to GATT services exported by the peer. Interface ========= Client ------ :Service: org.bluez :Interface: org.bluez.GattService1 :Object path: [variable prefix]/{hci0,hci1,...}/dev_XX_XX_XX_XX_XX_XX/serviceXX Server ------ :Service: unique name :Interface: org.bluez.GattService1 :Object path: freely definable Properties ---------- string UUID [read-only] ``````````````````````` 128-bit service UUID. boolean Primary [read-only] ``````````````````````````` Indicates whether or not this GATT service is a primary service. If false, the service is secondary. object Device [read-only, optional] ``````````````````````````````````` Object path of the Bluetooth device the service belongs to. Only present on services from remote devices. array{object} Includes [read-only, optional] ```````````````````````````````````````````` Array of object paths representing the included services of this service. uint16 Handle [read-only] (client only) ``````````````````````````````````````` Service handle. uint16 Handle [read-write, optional] (Server Only) `````````````````````````````````````````````````` Service handle. When available in the server it would attempt to use to allocate into the database which may fail, to auto allocate the value 0x0000 shall be used which will cause the allocated handle to be set once registered. bluez-5.82/doc/PaxHeaders/org.bluez.MediaFolder.50000644000000000000000000000005014773213452016572 xustar0020 atime=1743591210 20 ctime=1743591291 bluez-5.82/doc/org.bluez.MediaFolder.50000644000000000000000000000631314773213452016256 0ustar00rootroot.\" Man page generated from reStructuredText. . . .nr rst2man-indent-level 0 . .de1 rstReportMargin \\$1 \\n[an-margin] level \\n[rst2man-indent-level] level margin: \\n[rst2man-indent\\n[rst2man-indent-level]] - \\n[rst2man-indent0] \\n[rst2man-indent1] \\n[rst2man-indent2] .. .de1 INDENT .\" .rstReportMargin pre: . RS \\$1 . nr rst2man-indent\\n[rst2man-indent-level] \\n[an-margin] . nr rst2man-indent-level +1 .\" .rstReportMargin post: .. .de UNINDENT . RE .\" indent \\n[an-margin] .\" old: \\n[rst2man-indent\\n[rst2man-indent-level]] .nr rst2man-indent-level -1 .\" new: \\n[rst2man-indent\\n[rst2man-indent-level]] .in \\n[rst2man-indent\\n[rst2man-indent-level]]u .. .TH "ORG.BLUEZ.MEDIAFOLDER" "5" "September 2023" "BlueZ" "Linux System Administration" .SH NAME org.bluez.MediaFolder \- BlueZ D-Bus MediaFolder API documentation .SH INTERFACE .INDENT 0.0 .TP .B Service unique name (Target role) org.bluez (Controller role) .TP .B Interface org.bluez.MediaFolder1 .TP .B Object path freely definable (Target role) [variable prefix]/{hci0,hci1,...}/dev_XX_XX_XX_XX_XX_XX/playerX (Controller role) .UNINDENT .SS Methods .SS object Search(string value, dict filter) .INDENT 0.0 .INDENT 3.5 Return a folder object containing the search result. .sp To list the items found use the folder object returned and pass to ChangeFolder. .sp Possible Errors: .INDENT 0.0 .TP .B org.bluez.Error.NotSupported .TP .B org.bluez.Error.Failed .UNINDENT .UNINDENT .UNINDENT .SS array{objects, properties} ListItems(dict filter) .INDENT 0.0 .INDENT 3.5 Return a list of items found .sp Possible Errors: .INDENT 0.0 .TP .B org.bluez.Error.InvalidArguments .TP .B org.bluez.Error.NotSupported .TP .B org.bluez.Error.Failed .UNINDENT .UNINDENT .UNINDENT .SS void ChangeFolder(object folder) .INDENT 0.0 .INDENT 3.5 Change current folder. .sp Note: By changing folder the items of previous folder might be destroyed and have to be listed again, the exception is NowPlaying folder which should be always present while the player is active. .sp Possible Errors: .INDENT 0.0 .TP .B org.bluez.Error.InvalidArguments .TP .B org.bluez.Error.NotSupported .TP .B org.bluez.Error.Failed .UNINDENT .UNINDENT .UNINDENT .SS Properties .SS uint32 NumberOfItems [readonly] .INDENT 0.0 .INDENT 3.5 Number of items in the folder .UNINDENT .UNINDENT .SS string Name [readonly] .INDENT 0.0 .INDENT 3.5 Folder name: .sp Possible values: .INDENT 0.0 .TP .B \(dq/Filesystem/...\(dq Filesystem scope .TP .B \(dq/NowPlaying/...\(dq NowPlaying scope .UNINDENT .sp Note: /NowPlaying folder might not be listed if player is stopped, folders created by Search are virtual so once another Search is perform or the folder is changed using ChangeFolder it will no longer be listed. .UNINDENT .UNINDENT .SS Filters .INDENT 0.0 .TP .B uint32 Start Offset of the first item. .sp Default value: 0 .TP .B uint32 End Offset of the last item. .sp Default value: NumbeOfItems .TP .B array{string} Attributes Item properties that should be included in the list. .sp Possible Values: .INDENT 7.0 .INDENT 3.5 \(dqtitle\(dq, \(dqartist\(dq, \(dqalbum\(dq, \(dqgenre\(dq, \(dqnumber\-of\-tracks\(dq, \(dqnumber\(dq, \(dqduration\(dq .sp Default Value: All .UNINDENT .UNINDENT .UNINDENT .\" Generated by docutils manpage writer. . bluez-5.82/doc/PaxHeaders/org.bluez.GattProfile.rst0000644000000000000000000000005014536422313017276 xustar0020 atime=1743516803 20 ctime=1743591290 bluez-5.82/doc/org.bluez.GattProfile.rst0000644000000000000000000000210314536422313016753 0ustar00rootroot===================== org.bluez.GattProfile ===================== ----------------------------------------- BlueZ D-Bus GattProfile API documentation ----------------------------------------- :Version: BlueZ :Date: October 2023 :Manual section: 5 :Manual group: Linux System Administration Description =========== Local profile (GATT client) instance. By registering this type of object an application effectively indicates support for a specific GATT profile and requests automatic connections to be established to devices supporting it. Interface ========= :Service: :Interface: org.bluez.GattProfile1 :Object path: Methods ------- void Release() `````````````` This method gets called when the service daemon unregisters the profile. The profile can use it to do cleanup tasks. There is no need to unregister the profile, because when this method gets called it has already been unregistered. Properties ---------- array{string} UUIDs [read-only] ``````````````````````````````` 128-bit GATT service UUIDs to auto connect. bluez-5.82/doc/PaxHeaders/mgmt-api.txt0000644000000000000000000000005014772767672014720 xustar0020 atime=1743515579 20 ctime=1743591290 bluez-5.82/doc/mgmt-api.txt0000644000000000000000000047201314772767672014410 0ustar00rootrootBluetooth Management API ************************* Copyright (C) 2008-2009 Marcel Holtmann Overview ======== This document describes the format of data used for communicating with the kernel using a so-called Bluetooth Management sockets. These sockets are available starting with Linux kernel version 3.4 The following kernel versions introduced new commands, new events or important fixes to the Bluetooth Management API: Linux kernel v3.4 Version 1.0 Linux kernel v3.5 Version 1.1 Linux kernel v3.7 Version 1.2 Linux kernel v3.9 Version 1.3 Linux kernel v3.13 Version 1.4 Linux kernel v3.15 Version 1.5 Linux kernel v3.16 Version 1.6 Linux kernel v3.17 Version 1.7 Linux kernel v3.19 Version 1.8 Linux kernel v4.1 Version 1.9 Linux kernel v4.2 Version 1.10 Linux kernel v4.5 Version 1.11 Linux kernel v4.6 Version 1.12 Linux kernel v4.8 Version 1.13 Linux kernel v4.9 Version 1.14 Linux kernel v5.5 Version 1.15 Linux kernel v5.6 Version 1.16 Linux kernel v5.7 Version 1.17 Linux kernel v5.8 Version 1.18 (not yet released) Version 1.1 introduces Set Device ID command. Version 1.2 introduces Passkey Notify event. Version 1.3 does not introduce any new command or event. Version 1.4 introduces Set Advertising, Set BR/EDR, Set Static Address and Set Scan Parameters commands. The existing Set Discoverable command gained an extra setting for limited discoverable mode. The device name is now provided in the scan response data for Low Energy. Version 1.5 introduces Set Secure Connections, Set Debug Keys, Set Privacy and Load Identity Resolving Keys commands. It also introduces New Identity Resolving Key and New Signature Resolving Key events. Version 1.6 introduces Get Connection Information command. It also updates the Device Found event to combine advertising data and scan response data into a single event. Version 1.7 introduces Get Clock Information, Add Device, Remove Device, Load Connection Parameters, Read Unconfigured Index List, Read Controller Configuration Information, Set External Configuration and Set Public Address commands. It also introduces Device Added, Device Removed, New Connection Parameter, Unconfigured Index Added, Unconfigured Index Removed and New Configuration Options events. The existing Set Debug Keys command gained an extra setting for enabling SSP debug mode. Version 1.8 introduces Start Service Discovery command. It also adds new Long Term Key types for LE Secure Connection feature. Version 1.9 introduces Read Local Out Of Band Extended, Data, Read Extended Controller Index List, Read Advertising Features, Add Advertising and Remove Advertising commands. It also introduces Extended Index Added, Extended Index Removed, Local Out Of Band Extended Data Updated, Advertising Added and Advertising Removed events. The existing Set Advertising command gained an extra setting for enabling undirected connectable advertising. It provides support for a new static address setting and allows the usage of Set Fast Connectable when controller is powered off. Version 1.10 does not introduce any new command or event. It extends the advertising feature to support 5 parallel advertising instances. Version 1.11 introduces Get Advertising Size Information and Start Limited Discovery commands. Version 1.12 introduces a new limited privacy mode (value 0x02 passed to the Set Privacy command). Version 1.13 introduces a new authentication failure reason code for the Device Disconnected event. Version 1.14 introduces Read Extended Controller Information command and Extended Controller Information Changed event. It also adds Set Appearance command. The advertising flags Appearance and Local Name for adding scan response information are now supported. Version 1.15 introduces Get PHY Configuration, Set PHY Configuration and Load Blocked Keys commands. Version 1.16 introduces Wideband Speech setting and its corresponding Set Wideband Speech command. Version 1.17 introduces Read Security Information command, Read Experimental Features Information command, Set Experimental Feature command and the Experimental Feature Changed event. Version 1.18 introduces Read Default System Configuration command, Set Default System Configuration command, Default System Configuration Changed event, Read Default Runtime Configuration command, Set Default Runtime Configuration command, Default Runtime Configuration Changed event, Get Device Flags command, Set Device Flags command, Device Flags Changed event, Read Advertisement Monitor Features command, Add Advertisement Patterns Monitor command, Remove Advertisement Monitor command, Advertisement Monitor Added event and Advertisement Monitor Removed event. Example ======= The Bluetooth management sockets can be created by setting the hci_channel member of struct sockaddr_hci to HCI_CHANNEL_CONTROL (3) when creating a raw HCI socket. In C the needed code would look something like the following: int mgmt_create(void) { struct sockaddr_hci addr; int fd; fd = socket(PF_BLUETOOTH, SOCK_RAW | SOCK_CLOEXEC | SOCK_NONBLOCK, BTPROTO_HCI); if (fd < 0) return -errno; memset(&addr, 0, sizeof(addr)); addr.hci_family = AF_BLUETOOTH; addr.hci_dev = HCI_DEV_NONE; addr.hci_channel = HCI_CHANNEL_CONTROL; if (bind(fd, (struct sockaddr *) &addr, sizeof(addr)) < 0) { int err = -errno; close(fd); return err; } return fd; } The process creating the mgmt socket is required to have the CAP_NET_ADMIN capability (e.g. root would have this). Packet Structures ================= Commands: 0 4 8 12 16 22 24 28 31 35 39 43 47 +-------------------+-------------------+-------------------+ | Command Code | Controller Index | Parameter Length | +-------------------+-------------------+-------------------+ | | Events: 0 4 8 12 16 22 24 28 31 35 39 43 47 +-------------------+-------------------+-------------------+ | Event Code | Controller Index | Parameter Length | +-------------------+-------------------+-------------------+ | | All fields are in little-endian byte order (least significant byte first). Controller Index can have a special value to indicate that command or event is not related to any controller. Possible values: 0x0000 to 0xFFFE 0xFFFF Error Codes =========== The following values have been defined for use with the Command Status and Command Complete events: 0x00 Success 0x01 Unknown Command 0x02 Not Connected 0x03 Failed 0x04 Connect Failed 0x05 Authentication Failed 0x06 Not Paired 0x07 No Resources 0x08 Timeout 0x09 Already Connected 0x0A Busy 0x0B Rejected 0x0C Not Supported 0x0D Invalid Parameters 0x0E Disconnected 0x0F Not Powered 0x10 Cancelled 0x11 Invalid Index 0x12 RFKilled 0x13 Already Paired 0x14 Permission Denied As a general rule all commands generate the events as specified below, however invalid lengths or unknown commands will always generate a Command Status response (with Unknown Command or Invalid Parameters status). Sending a command with an invalid Controller Index value will also always generate a Command Status event with the Invalid Index status code. Read Management Version Information Command =========================================== Command Code: 0x0001 Controller Index: Command Parameters: Return Parameters: Version (1 Octets) Revision (2 Octets) This command returns the Management version and revision. Besides, being informational the information can be used to determine whether certain behavior has changed or bugs fixed when interacting with the kernel. This command generates a Command Complete event on success or a Command Status event on failure. Read Management Supported Commands Command ========================================== Command Code: 0x0002 Controller Index: Command Parameters: Return Parameters: Num_Of_Commands (2 Octets) Num_Of_Events (2 Octets) Command1 (2 Octets) Command2 (2 Octets) ... Event1 (2 Octets) Event2 (2 Octets) ... This command returns the list of supported Management commands and events. The commands Read Management Version Information and Read management Supported Commands are not included in this list. Both commands are always supported and mandatory. The events Command Status and Command Complete are not included in this list. Both are implicit and mandatory. This command generates a Command Complete event on success or a Command Status event on failure. Read Controller Index List Command ================================== Command Code: 0x0003 Controller Index: Command Parameters: Return Parameters: Num_Controllers (2 Octets) Controller_Index[i] (2 Octets) This command returns the list of currently known controllers. Controllers added or removed after calling this command can be monitored using the Index Added and Index Removed events. This command generates a Command Complete event on success or a Command Status event on failure. Read Controller Information Command =================================== Command Code: 0x0004 Controller Index: Command Parameters: Return Parameters: Address (6 Octets) Bluetooth_Version (1 Octet) Manufacturer (2 Octets) Supported_Settings (4 Octets) Current_Settings (4 Octets) Class_Of_Device (3 Octets) Name (249 Octets) Short_Name (11 Octets) This command is used to retrieve the current state and basic information of a controller. It is typically used right after getting the response to the Read Controller Index List command or an Index Added event. The Address parameter describes the controllers public address and it can be expected that it is set. However in case of single mode Low Energy only controllers it can be 00:00:00:00:00:00. To power on the controller in this case, it is required to configure a static address using Set Static Address command first. If the public address is set, then it will be used as identity address for the controller. If no public address is available, then the configured static address will be used as identity address. In the case of a dual-mode controller with public address that is configured as Low Energy only device (BR/EDR switched off), the static address is used when set and public address otherwise. If no short name is set the Short_Name parameter will be empty (begin with a nul byte). Current_Settings and Supported_Settings is a bitmask with currently the following available bits: 0 Powered 1 Connectable 2 Fast Connectable 3 Discoverable 4 Bondable 5 Link Level Security (Sec. mode 3) 6 Secure Simple Pairing 7 Basic Rate/Enhanced Data Rate 8 High Speed 9 Low Energy 10 Advertising 11 Secure Connections 12 Debug Keys 13 Privacy 14 Controller Configuration 15 Static Address 16 PHY Configuration 17 Wideband Speech 18 Connected Isochronous Stream - Central 19 Connected Isochronous Stream - Peripheral 20 Isochronous Broadcaster 21 Synchronized Receiver 22 LL Privacy This command generates a Command Complete event on success or a Command Status event on failure. Possible errors: Invalid Parameters Invalid Index Set Powered Command =================== Command Code: 0x0005 Controller Index: Command Parameters: Powered (1 Octet) Return Parameters: Current_Settings (4 Octets) This command is used to power on or off a controller. The allowed Powered command parameter values are 0x00 and 0x01. All other values will return Invalid Parameters. If discoverable setting is activated with a timeout, then switching the controller off will expire this timeout and disable discoverable. Settings programmed via Set Advertising and Add/Remove Advertising while the controller was powered off will be activated when powering the controller on. Switching the controller off will permanently cancel and remove all advertising instances with a timeout set, i.e. time limited advertising instances are not being remembered across power cycles. Advertising Removed events will be issued accordingly. This command generates a Command Complete event on success or a Command Status event on failure. Possible errors: Busy Invalid Parameters Invalid Index Set Discoverable Command ======================== Command Code: 0x0006 Controller Index: Command Parameters: Discoverable (1 Octet) Timeout (2 Octets) Return Parameters: Current_Settings (4 Octets) This command is used to set the discoverable property of a controller. The allowed Discoverable command parameter values are 0x00, 0x01 and 0x02. All other values will return Invalid Parameters. Timeout is the time in seconds and is only meaningful when Discoverable is set to 0x01 or 0x02. Providing a timeout with 0x00 return Invalid Parameters. For 0x02, the timeout value is required. The value 0x00 disables discoverable, the value 0x01 enables general discoverable and the value 0x02 enables limited discoverable. This command is only available for BR/EDR capable controllers (e.g. not for single-mode LE ones). It will return Not Supported otherwise. This command can be used when the controller is not powered and all settings will be programmed once powered. However using a timeout when the controller is not powered will return Not Powered error. When switching discoverable on and the connectable setting is off it will return Rejected error. This command generates a Command Complete event on success or a Command Status event on failure. Possible errors: Busy Rejected Not Supported Invalid Parameters Not Powered Invalid Index Set Connectable Command ======================= Command Code: 0x0007 Controller Index: Command Parameters: Connectable (1 Octet) Return Parameters: Current_Settings (4 Octets) This command is used to set the connectable property of a controller. The allowed Connectable command parameter values are 0x00 and 0x01. All other values will return Invalid Parameters. This command is available for BR/EDR, LE-only and also dual mode controllers. For BR/EDR is changes the page scan setting and for LE controllers it changes the advertising type. For dual mode controllers it affects both settings. For LE capable controllers the connectable setting takes effect when advertising is enabled (peripheral) or when directed advertising events are received (central). This command can be used when the controller is not powered and all settings will be programmed once powered. When switching connectable off, it will also switch off the discoverable setting. Switching connectable back on will not restore a previous discoverable. It will stay off and needs to be manually switched back on. When switching connectable off, it will expire a discoverable setting with a timeout. This setting does not affect known devices from Add Device command. These devices are always allowed to connect. This command generates a Command Complete event on success or a Command Status event on failure. Possible errors: Busy Not Supported Invalid Parameters Invalid Index Set Fast Connectable Command ============================ Command Code: 0x0008 Controller Index: Command Parameters: Enable (1 Octet) Return Parameters: Current_Settings (4 Octets) This command is used to set the controller into a connectable state where the page scan parameters have been set in a way to favor faster connect times with the expense of higher power consumption. The allowed values of the Enable command parameter are 0x00 and 0x01. All other values will return Invalid Parameters. This command is only available for BR/EDR capable controllers (e.g. not for single-mode LE ones). It will return Not Supported otherwise. This command can be used when the controller is not powered and all settings will be programmed once powered. The setting will be remembered during power down/up toggles. This command generates a Command Complete event on success or a Command Status event on failure. Possible errors: Failed Busy Not Supported Invalid Parameters Invalid Index Set Bondable Command ==================== Command Code: 0x0009 Controller Index: Command Parameters: Bondable (1 Octet) Return Parameters: Current_Settings (4 Octets) This command is used to set the bondable property of an controller. The allowed values for the Bondable command parameter are 0x00 and 0x01. All other values will return Invalid Parameters. This command can be used when the controller is not powered and all settings will be programmed once powered. Turning bondable on will not automatically switch the controller into connectable mode. That needs to be done separately. The setting will be remembered during power down/up toggles. This command generates a Command Complete event on success or a Command Status event on failure. Possible errors: Invalid Parameters Invalid Index Set Link Security Command ========================= Command Code: 0x000A Controller Index: Command Parameters: Link_Security (1 Octet) Return Parameters: Current_Settings (4 Octets) This command is used to either enable or disable link level security for an controller (also known as Security Mode 3). The allowed values for the Link_Security command parameter are 0x00 and 0x01. All other values will return Invalid Parameters. This command is only available for BR/EDR capable controllers (e.g. not for single-mode LE ones). It will return Not Supported otherwise. This command can be used when the controller is not powered and all settings will be programmed once powered. This command generates a Command Complete event on success or a Command Status event on failure. Possible errors: Busy Not Supported Invalid Parameters Invalid Index Set Secure Simple Pairing Command ================================= Command Code: 0x000B Controller Index: Command Parameters: Secure_Simple_Pairing (1 Octet) Return Parameters: Current_Settings (4 Octets) This command is used to enable/disable Secure Simple Pairing support for a controller. The allowed values for the Secure_Simple_Pairing command parameter are 0x00 and 0x01. All other values will return Invalid Parameters. This command is only available for BR/EDR capable controllers supporting the core specification version 2.1 or greater (e.g. not for single-mode LE controllers or pre-2.1 ones). This command can be used when the controller is not powered and all settings will be programmed once powered. In case the controller does not support Secure Simple Pairing, the command will fail regardless with Not Supported error. This command generates a Command Complete event on success or a Command Status event on failure. Possible errors: Busy Not Supported Invalid Parameters Invalid Index Set High Speed Command ====================== Command Code: 0x000C Controller Index: Command Parameters: High_Speed (1 Octet) Return Parameters: Current_Settings (4 Octets) This command is used to enable/disable Bluetooth High Speed support for a controller. The allowed values for the High_Speed command parameter are 0x00 and 0x01. All other values will return Invalid Parameters. This command is only available for BR/EDR capable controllers (e.g. not for single-mode LE ones). This command can be used when the controller is not powered and all settings will be programmed once powered. To enable High Speed support, it is required that Secure Simple Pairing support is enabled first. High Speed support is not possible for connections without Secure Simple Pairing. When switching Secure Simple Pairing off, the support for High Speed will be switched off as well. Switching Secure Simple Pairing back on, will not re-enable High Speed support. That needs to be done manually. This command generates a Command Complete event on success or a Command Status event on failure. Possible errors: Not Supported Invalid Parameters Invalid Index Set Low Energy Command ====================== Command Code: 0x000D Controller Index: Command Parameters: Low_Energy (1 Octet) Return Parameters: Current_Settings (4 Octets) This command is used to enable/disable Low Energy support for a controller. The allowed values of the Low_Energy command parameter are 0x00 and 0x01. All other values will return Invalid Parameters. This command is only available for LE capable controllers and will yield in a Not Supported error otherwise. This command can be used when the controller is not powered and all settings will be programmed once powered. In case the kernel subsystem does not support Low Energy or the controller does not either, the command will fail regardless. Disabling LE support will permanently disable and remove all advertising instances configured with the Add Advertising command. Advertising Removed events will be issued accordingly. This command generates a Command Complete event on success or a Command Status event on failure. Possible errors: Busy Not Supported Invalid Parameters Invalid Index Set Device Class Command ======================== Command Code: 0x000E Controller Index: Command Parameters: Major_Class (1 Octet) Minor_Class (1 Octet) Return Parameters: Class_Of_Device (3 Octets) This command is used to set the major and minor device class for BR/EDR capable controllers. This command will also implicitly disable caching of pending CoD and EIR updates. This command is only available for BR/EDR capable controllers (e.g. not for single-mode LE ones). This command can be used when the controller is not powered and all settings will be programmed once powered. In case the controller is powered off, 0x000000 will be returned for the class of device parameter. And after power on the new value will be announced via class of device changed event. This command generates a Command Complete event on success or a Command Status event on failure. Possible errors: Busy Not Supported Invalid Parameters Invalid Index Set Local Name Command ====================== Command Code: 0x000F Controller Index: Command Parameters: Name (249 Octets) Short_Name (11 Octets) Return Parameters: Name (249 Octets) Short_Name (11 Octets) This command is used to set the local name of a controller. The command parameters also include a short name which will be used in case the full name doesn't fit within EIR/AD data. The name parameters need to always end with a null byte (failure to do so will cause the command to fail). This command can be used when the controller is not powered and all settings will be programmed once powered. The values of name and short name will be remembered when switching the controller off and back on again. So the name and short name only have to be set once when a new controller is found and will stay until removed. This command generates a Command Complete event on success or a Command Status event on failure. Possible errors: Invalid Parameters Invalid Index Add UUID Command ================ Command Code: 0x0010 Controller Index: Command Parameters: UUID (16 Octets) SVC_Hint (1 Octet) Return Parameters: Class_Of_Device (3 Octets) This command is used to add a UUID to be published in EIR data. The accompanied SVC_Hint parameter is used to tell the kernel whether the service class bits of the Class of Device value need modifying due to this UUID. This command can be used when the controller is not powered and all settings will be programmed once powered. In case the controller is powered off, 0x000000 will be returned for the class of device parameter. And after power on the new value will be announced via class of device changed event. This command generates a Command Complete event on success or a Command Status event on failure. Possible errors: Busy Invalid Parameters Invalid Index Remove UUID Command =================== Command Code: 0x0011 Controller Index: Command Parameters: UUID (16 Octets) Return Parameters: Class_Of_Device (3 Octets) This command is used to remove a UUID previously added using the Add UUID command. When the UUID parameter is an empty UUID (16 x 0x00), then all previously loaded UUIDs will be removed. This command can be used when the controller is not powered and all settings will be programmed once powered. In case the controller is powered off, 0x000000 will be returned for the class of device parameter. And after power on the new value will be announced via class of device changed event. This command generates a Command Complete event on success or a Command Status event on failure. Possible errors: Busy Invalid Parameters Invalid Index Load Link Keys Command ====================== Command Code: 0x0012 Controller Index: Command Parameters: Debug_Keys (1 Octet) Key_Count (2 Octets) Key1 { Address (6 Octets) Address_Type (1 Octet) Key_Type (1 Octet) Value (16 Octets) PIN_Length (1 Octet) } Key2 { } ... Return Parameters: This command is used to feed the kernel with currently known link keys. The command does not need to be called again upon the receipt of New Link Key events since the kernel updates its list automatically. The Debug_Keys parameter is used to tell the kernel whether to accept the usage of debug keys or not. The allowed values for this parameter are 0x00 and 0x01. All other values will return an Invalid Parameters response. Usage of the Debug_Keys parameter is deprecated and has been replaced with the Set Debug Keys command. When setting the Debug_Keys option via Load Link Keys command it has the same affect as setting it via Set Debug Keys and applies to all keys in the system. Possible values for the Address_Type parameter: 0 BR/EDR 1 Reserved (not in use) 2 Reserved (not in use) Public and random LE addresses are not valid and will be rejected. Currently defined Key_Type values are: 0x00 Combination key 0x01 Local Unit key 0x02 Remote Unit key 0x03 Debug Combination key 0x04 Unauthenticated Combination key from P-192 0x05 Authenticated Combination key from P-192 0x06 Changed Combination key 0x07 Unauthenticated Combination key from P-256 0x08 Authenticated Combination key from P-256 This command can be used when the controller is not powered. This command generates a Command Complete event on success or a Command Status event on failure. Possible errors: Invalid Parameters Invalid Index Load Long Term Keys Command =========================== Command Code: 0x0013 Controller Index: Command Parameters: Key_Count (2 Octets) Key1 { Address (6 Octets) Address_Type (1 Octet) Key_Type (1 Octet) Central (1 Octet) Encryption_Size (1 Octet) Encryption_Diversifier (2 Octets) Random_Number (8 Octets) Value (16 Octets) } Key2 { } ... Return Parameters: This command is used to feed the kernel with currently known (SMP) Long Term Keys. The command does not need to be called again upon the receipt of New Long Term Key events since the kernel updates its list automatically. Possible values for the Address_Type parameter: 0 Reserved (not in use) 1 LE Public 2 LE Random The provided Address and Address_Type are the identity of a device. So either its public address or static random address. Unresolvable random addresses and resolvable random addresses are not valid and will be rejected. Currently defined Key_Type values are: 0x00 Unauthenticated key 0x01 Authenticated key This command can be used when the controller is not powered. This command generates a Command Complete event on success or a Command Status event on failure. Possible errors: Invalid Parameters Invalid Index Disconnect Command ================== Command Code: 0x0014 Controller Index: Command Parameters: Address (6 Octets) Address_Type (1 Octet) Return Parameters: Address (6 Octets) Address_Type (1 Octet) This command is used to force the disconnection of a currently connected device. Possible values for the Address_Type parameter: 0 BR/EDR 1 LE Public 2 LE Random This command can only be used when the controller is powered. This command generates a Command Complete event on success or failure. Possible errors: Not Connected Busy Invalid Parameters Not Powered Invalid Index Get Connections Command ======================= Command Code: 0x0015 Controller Index: Command Parameters: Return Parameters: Connection_Count (2 Octets) Address1 { Address (6 Octets) Address_Type (1 Octet) } Address2 { } ... This command is used to retrieve a list of currently connected devices. Possible values for the Address_Type parameter: 0 BR/EDR 1 LE Public 2 LE Random For devices using resolvable random addresses with a known identity resolving key, the Address and Address_Type will contain the identity information. This command can only be used when the controller is powered. This command generates a Command Complete event on success or a Command Status event on failure. Possible errors: Invalid Parameters Not Powered Invalid Index PIN Code Reply Command ======================= Command Code: 0x0016 Controller Index: Command Parameters: Address (6 Octets) Address_Type (1 Octet) PIN_Length (1 Octet) PIN_Code (16 Octets) Return Parameters: Address (6 Octets) Address_Type (1 Octet) This command is used to respond to a PIN Code request event. Possible values for the Address_Type parameter: 0 BR/EDR 1 LE Public 2 LE Random This command can only be used when the controller is powered. This command generates a Command Complete event on success or failure. Possible errors: Not Connected Invalid Parameters Not Powered Invalid Index PIN Code Negative Reply Command =============================== Command Code: 0x0017 Controller Index: Command Parameters: Address (6 Octets) Address_Type (1 Octet) Return Parameters: Address (6 Octets) Address_Type (1 Octet) This command is used to return a negative response to a PIN Code Request event. Possible values for the Address_Type parameter: 0 BR/EDR 1 LE Public 2 LE Random This command can only be used when the controller is powered. This command generates a Command Complete event on success or failure. Possible errors: Not Connected Invalid Parameters Not Powered Invalid Index Set IO Capability Command ========================= Command Code: 0x0018 Controller Index: Command Parameters: IO_Capability (1 Octet) Return Parameters: This command is used to set the IO Capability used for pairing. The command accepts both SSP and SMP values. Possible values for the IO_Capability parameter: 0 DisplayOnly 1 DisplayYesNo 2 KeyboardOnly 3 NoInputNoOutput 4 KeyboardDisplay Passing a value 4 (KeyboardDisplay) will cause the kernel to convert it to 1 (DisplayYesNo) in the case of a BR/EDR connection (as KeyboardDisplay is specific to SMP). This command can be used when the controller is not powered. This command generates a Command Complete event on success or a Command Status event on failure. Possible errors: Invalid Parameters Invalid Index Pair Device Command =================== Command Code: 0x0019 Controller Index: Command Parameters: Address (6 Octets) Address_Type (1 Octet) IO_Capability (1 Octet) Return Parameters: Address (6 Octets) Address_Type (1 Octet) This command is used to trigger pairing with a remote device. The IO_Capability command parameter is used to temporarily (for this pairing event only) override the global IO Capability (set using the Set IO Capability command). Possible values for the Address_Type parameter: 0 BR/EDR 1 LE Public 2 LE Random Possible values for the IO_Capability parameter: 0 DisplayOnly 1 DisplayYesNo 2 KeyboardOnly 3 NoInputNoOutput 4 KeyboardDisplay Passing a value 4 (KeyboardDisplay) will cause the kernel to convert it to 1 (DisplayYesNo) in the case of a BR/EDR connection (as KeyboardDisplay is specific to SMP). The Address and Address_Type of the return parameters will return the identity address if known. In case of resolvable random address given as command parameters and the remote provides an identity resolving key, the return parameters will provide the resolved address. To allow tracking of which resolvable random address changed into which identity address, the New Identity Resolving Key event will be sent before receiving Command Complete event for this command. This command can only be used when the controller is powered. This command generates a Command Complete event on success or failure. Reject status is used when requested transport is not enabled. Not Supported status is used if controller is not capable with requested transport. Possible errors: Rejected Not Supported Connect Failed Busy Invalid Parameters Not Powered Invalid Index Already Paired Cancel Pair Device Command ========================== Command Code: 0x001A Controller Index: Command Parameters: Address (6 Octets) Address_Type (1 Octet) Return Parameters: Address (6 Octets) Address_Type (1 Octet) The Address and Address_Type parameters should match what was given to a preceding Pair Device command. Possible values for the Address_Type parameter: 0 BR/EDR 1 LE Public 2 LE Random This command can only be used when the controller is powered. This command generates a Command Complete event on success or failure. Possible errors: Invalid Parameters Not Powered Invalid Index Unpair Device Command ===================== Command Code: 0x001B Controller Index: Command Parameters: Address (6 Octets) Address_Type (1 Octet) Disconnect (1 Octet) Return Parameters: Address (6 Octets) Address_Type (1 Octet) Removes all keys associated with the remote device. Possible values for the Address_Type parameter: 0 BR/EDR 1 LE Public 2 LE Random The Disconnect parameter tells the kernel whether to forcefully disconnect any existing connections to the device. It should in practice always be 1 except for some special GAP qualification test-cases where a key removal without disconnecting is needed. When unpairing a device its link key, long term key and if provided identity resolving key will be purged. For devices using resolvable random addresses where the identity resolving key was available, after this command they will now no longer be resolved. The device will essentially become private again. This command can only be used when the controller is powered. This command generates a Command Complete event on success or failure. Possible errors: Not Paired Invalid Parameters Not Powered Invalid Index User Confirmation Reply Command =============================== Command Code: 0x001C Controller Index: Command Parameters: Address (6 Octets) Address_Type (1 Octet) Return Parameters: Address (6 Octets) Address_Type (1 Octet) This command is used to respond to a User Confirmation Request event. Possible values for the Address_Type parameter: 0 BR/EDR 1 LE Public 2 LE Random This command can only be used when the controller is powered. This command generates a Command Complete event on success or failure. Possible errors: Not Connected Invalid Parameters Not Powered Invalid Index User Confirmation Negative Reply Command ======================================== Command Code: 0x001D Controller Index: Command Parameters: Address (6 Octets) Address_Type (1 Octet) Return Parameters: Address (6 Octets) Address_Type (1 Octet) This command is used to return a negative response to a User Confirmation Request event. Possible values for the Address_Type parameter: 0 BR/EDR 1 LE Public 2 LE Random This command can only be used when the controller is powered. This command generates a Command Complete event on success or failure. Possible errors: Not Connected Invalid Parameters Not Powered Invalid Index User Passkey Reply Command ========================== Command Code: 0x001E Controller Index: Command Parameters: Address (6 Octets) Address_Type (1 Octet) Passkey (4 Octets) Return Parameters: Address (6 Octets) Address_Type (1 Octet) This command is used to respond to a User Confirmation Passkey Request event. Possible values for the Address_Type parameter: 0 BR/EDR 1 LE Public 2 LE Random This command can only be used when the controller is powered. This command generates a Command Complete event on success or failure. Possible errors: Not Connected Invalid Parameters Not Powered Invalid Index User Passkey Negative Reply Command =================================== Command Code: 0x001F Controller Index: Command Parameters: Address (6 Octets) Address_Type (1 Octet) Return Parameters: Address (6 Octets) Address_Type (1 Octet) This command is used to return a negative response to a User Passkey Request event. Possible values for the Address_Type parameter: 0 BR/EDR 1 LE Public 2 LE Random This command can only be used when the controller is powered. This command generates a Command Complete event on success or failure. Possible errors: Not Connected Invalid Parameters Not Powered Invalid Index Read Local Out Of Band Data Command =================================== Command Code: 0x0020 Controller Index: Command Parameters: Return Parameters: Hash_192 (16 Octets) Randomizer_192 (16 Octets) Hash_256 (16 Octets, Optional) Randomizer_256 (16 Octets, Optional) This command is used to read the local Out of Band data. This command can only be used when the controller is powered. If Secure Connections support is enabled, then this command will return P-192 versions of hash and randomizer as well as P-256 versions of both. Values returned by this command become invalid when the controller is powered down. After each power-cycle it is required to call this command again to get updated values. This command generates a Command Complete event on success or a Command Status event on failure. Possible errors: Not Supported Busy Invalid Parameters Not Powered Invalid Index Add Remote Out Of Band Data Command =================================== Command Code: 0x0021 Controller Index: Command Parameters: Address (6 Octets) Address_Type (1 Octet) Hash_192 (16 Octets) Randomizer_192 (16 Octets) Hash_256 (16 Octets, Optional) Randomizer_256 (16 Octets, Optional) Return Parameters: Address (6 Octets) Address_Type (1 Octet) This command is used to provide Out of Band data for a remote device. Possible values for the Address_Type parameter: 0 BR/EDR 1 LE Public 2 LE Random Provided Out Of Band data is persistent over power down/up toggles. This command also accept optional P-256 versions of hash and randomizer. If they are not provided, then they are set to zero value. The P-256 versions of both can also be provided when the support for Secure Connections is not enabled. However in that case they will never be used. To only provide the P-256 versions of hash and randomizer, it is valid to leave both P-192 fields as zero values. If Secure Connections is disabled, then of course this is the same as not providing any data at all. When providing data for remote LE devices, then the Hash_192 and and Randomizer_192 fields are not used and shell be set to zero. The Hash_256 and Randomizer_256 fields can be used for LE secure connections Out Of Band data. If only LE secure connections data is provided the Hash_P192 and Randomizer_P192 fields can be set to zero. Currently there is no support for providing the Security Manager TK Value for LE legacy pairing. If Secure Connections Only mode has been enabled, then providing Hash_P192 and Randomizer_P192 is not allowed. They are required to be set to zero values. This command can be used when the controller is not powered and all settings will be programmed once powered. This command generates a Command Complete event on success or failure. Possible errors: Failed Invalid Parameters Not Powered Invalid Index Remove Remote Out Of Band Data Command ====================================== Command Code: 0x0022 Controller Index: Command Parameters: Address (6 Octets) Address_Type (1 Octet) Return Parameters: Address (6 Octets) Address_Type (1 Octet) This command is used to remove data added using the Add Remote Out Of Band Data command. Possible values for the Address_Type parameter: 0 BR/EDR 1 LE Public 2 LE Random When the Address parameter is 00:00:00:00:00:00, then all previously added data will be removed. This command can be used when the controller is not powered and all settings will be programmed once powered. This command generates a Command Complete event on success or failure. Possible errors: Invalid Parameters Not Powered Invalid Index Start Discovery Command ======================= Command Code: 0x0023 Controller Index: Command Parameters: Address_Type (1 Octet) Return Parameters: Address_Type (1 Octet) This command is used to start the process of discovering remote devices. A Device Found event will be sent for each discovered device. Possible values for the Address_Type parameter are a bit-wise or of the following bits: 0 BR/EDR 1 LE Public 2 LE Random By combining these e.g. the following values are possible: 1 BR/EDR 6 LE (public & random) 7 BR/EDR/LE (interleaved discovery) This command can only be used when the controller is powered. This command generates a Command Complete event on success or failure. Possible errors: Busy Not Supported Invalid Parameters Not Powered Invalid Index Stop Discovery Command ====================== Command Code: 0x0024 Controller Index: Command Parameters: Address_Type (1 Octet) Return Parameters: Address_Type (1 Octet) This command is used to stop the discovery process started using the Start Discovery command. This command can only be used when the controller is powered. This command generates a Command Complete event on success or failure. Possible errors: Rejected Invalid Parameters Invalid Index Confirm Name Command ==================== Command Code: 0x0025 Controller Index: Command Parameters: Address (6 Octets) Address_Type (1 Octet) Name_Known (1 Octet) Return Parameters: Address (6 Octets) Address_Type (1 Octet) This command is only valid during device discovery and is expected for each Device Found event with the Confirm Name flag set. Possible values for the Address_Type parameter: 0 BR/EDR 1 LE Public 2 LE Random The Name_Known parameter should be set to 0x01 if user space knows the name for the device and 0x00 if it doesn't. If set to 0x00 the kernel will perform a name resolving procedure for the device in question. This command can only be used when the controller is powered. This command generates a Command Complete event on success or failure. Possible errors: Failed Invalid Parameters Invalid Index Block Device Command ==================== Command Code: 0x0026 Controller Index: Command Parameters: Address (6 Octets) Address_Type (1 Octet) Return Parameters: Address (6 Octets) Address_Type (1 Octet) This command is used to add a device to the list of devices which should be blocked from being connected to the local controller. Possible values for the Address_Type parameter: 0 BR/EDR 1 LE Public 2 LE Random For Low Energy devices, the blocking of a device takes precedence over auto-connection actions provided by Add Device. Blocked devices will not be auto-connected or even reported when found during background scanning. If the controller is connectable direct advertising from blocked devices will also be ignored. Connections created from advertising of the controller will be dropped if the device is blocked. This command can be used when the controller is not powered. This command generates a Command Complete event on success or failure. Possible errors: Failed Invalid Parameters Invalid Index Unblock Device Command ====================== Command Code: 0x0027 Controller Index: Command Parameters: Address (6 Octets) Address_Type (1 Octet) Return Parameters: Address (6 Octets) Address_Type (1 Octet) This command is used to remove a device from the list of blocked devices (where it was added to using the Block Device command). Possible values for the Address_Type parameter: 0 BR/EDR 1 LE Public 2 LE Random When the Address parameter is 00:00:00:00:00:00, then all previously blocked devices will be unblocked. This command can be used when the controller is not powered. This command generates a Command Complete event on success or failure. Possible errors: Invalid Parameters Invalid Index Set Device ID Command ===================== Command Code: 0x0028 Controller Index: Command Parameters: Source (2 Octets) Vendor (2 Octets) Product (2 Octets) Version (2 Octets) Return Parameters: This command can be used when the controller is not powered and all settings will be programmed once powered. The Source parameter selects the organization that assigned the Vendor parameter: 0x0000 Disable Device ID 0x0001 Bluetooth SIG 0x0002 USB Implementer's Forum The information is put into the EIR data. If the controller does not support EIR or if SSP is disabled, this command will still succeed. The information is stored for later use and will survive toggling SSP on and off. This command generates a Command Complete event on success or a Command Status event on failure. Possible errors: Invalid Parameters Invalid Index Set Advertising Command ======================= Command Code: 0x0029 Controller Index: Command Parameters: Advertising (1 Octet) Return Parameters: Current_Settings (4 Octets) This command is used to enable LE advertising on a controller that supports it. The allowed values for the Advertising command parameter are 0x00, 0x01 and 0x02. All other values will return Invalid Parameters. The value 0x00 disables advertising, the value 0x01 enables advertising with considering of connectable setting and the value 0x02 enables advertising in connectable mode. Using value 0x01 means that when connectable setting is disabled, the advertising happens with undirected non-connectable advertising packets and a non-resolvable random address is used. If connectable setting is enabled, then undirected connectable advertising packets and the identity address or resolvable private address are used. LE Devices configured via Add Device command with Action 0x01 have no effect when using Advertising value 0x01 since only the connectable setting is taken into account. To utilize undirected connectable advertising without changing the connectable setting, the value 0x02 can be utilized. It makes the device connectable via LE without the requirement for being connectable on BR/EDR (and/or LE). The value 0x02 should be the preferred mode of operation when implementing peripheral mode. Using this command will temporarily deactivate any configuration made by the Add Advertising command. This command takes precedence. Once a Set Advertising command with value 0x00 is issued any previously made configurations via Add/Remove Advertising, including such changes made while Set Advertising was active, will be re- enabled. A pre-requisite is that LE is already enabled, otherwise this command will return a "rejected" response. This command generates a Command Complete event on success or a Command Status event on failure. Possible errors: Busy Rejected Not Supported Invalid Parameters Invalid Index Set BR/EDR Command ================== Command Code: 0x002A Controller Index: Command Parameters: BR/EDR (1 Octet) Return Parameters: Current_Settings (4 Octets) This command is used to enable or disable BR/EDR support on a dual-mode controller. The allowed values for the Advertising command parameter are 0x00 and 0x01. All other values will return Invalid Parameters. A pre-requisite is that LE is already enabled, otherwise this command will return a "rejected" response. Enabling BR/EDR can be done both when powered on and powered off, however disabling it can only be done when powered off (otherwise the command will again return "rejected"). Disabling BR/EDR will automatically disable all other BR/EDR related settings. This command generates a Command Complete event on success or a Command Status event on failure. Possible errors: Busy Rejected Not Supported Invalid Parameters Invalid Index Set Static Address Command ========================== Command Code: 0x002B Controller Index: Command Parameters: Address (6 Octets) Return Parameters: Current_Settings (4 Octets) This command allows for setting the static random address. It is only supported on controllers with LE support. The static random address is suppose to be valid for the lifetime of the controller or at least until the next power cycle. To ensure such behavior, setting of the address is limited to when the controller is powered off. The special BDADDR_ANY address (00:00:00:00:00:00) can be used to disable the static address. When a controller has a public address (which is required for all dual-mode controllers), this address is not used. If a dual-mode controller is configured as Low Energy only devices (BR/EDR has been switched off), then the static address is used. Only when the controller information reports BDADDR_ANY (00:00:00:00:00:00), it is required to configure a static address first. If privacy mode is enabled and the controller is single mode LE only without a public address, the static random address is used as identity address. The Static Address flag from the current settings can also be used to determine if the configured static address is in use or not. This command generates a Command Complete event on success or a Command Status event on failure. Possible errors: Rejected Not Supported Invalid Parameters Invalid Index Set Scan Parameters Command =========================== Command Code: 0x002C Controller Index: Command Parameters: Interval (2 Octets) Window (2 Octets) Return Parameters: This command allows for setting the Low Energy scan parameters used for connection establishment and passive scanning. It is only supported on controllers with LE support. This command generates a Command Complete event on success or a Command Status event on failure. Possible errors: Rejected Not Supported Invalid Parameters Invalid Index Set Secure Connections Command ============================== Command Code: 0x002D Controller Index: Command Parameters: Secure_Connections (1 Octet) Return Parameters: Current_Settings (4 Octets) This command is used to enable/disable Secure Connections support for a controller. The allowed values for the Secure_Connections command parameter are 0x00, 0x01 and 0x02. All other values will return Invalid Parameters. The value 0x00 disables Secure Connections, the value 0x01 enables Secure Connections and the value 0x02 enables Secure Connections Only mode. This command is only available for LE capable controllers as well as controllers supporting the core specification version 4.1 or greater. This command can be used when the controller is not powered and all settings will be programmed once powered. In case the controller does not support Secure Connections the command will fail regardless with Not Supported error. This command generates a Command Complete event on success or a Command Status event on failure. Possible errors: Busy Not Supported Invalid Parameters Invalid Index Set Debug Keys Command ====================== Command Code: 0x002E Controller Index: Command Parameters: Debug_Keys (1 Octet) Return Parameters: Current_Settings (4 Octets) This command is used to tell the kernel whether to accept the usage of debug keys or not. The allowed values for this parameter are 0x00, 0x01 and 0x02. All other values will return an Invalid Parameters response. With a value of 0x00 any generated debug key will be discarded as soon as the connection terminates. With a value of 0x01 generated debug keys will be kept and can be used for future connections. However debug keys are always marked as non persistent and should not be stored. This means a reboot or changing the value back to 0x00 will delete them. With a value of 0x02 generated debug keys will be kept and can be used for future connections. This has the same affect as with value 0x01. However in addition this value will also enter the controller mode to generate debug keys for each new pairing. Changing the value back to 0x01 or 0x00 will disable the controller mode for generating debug keys. This command generates a Command Complete event on success or a Command Status event on failure. Possible errors: Busy Not Supported Invalid Parameters Invalid Index Set Privacy Command =================== Command Code: 0x002F Controller Index: Command Parameters: Privacy (1 Octet) Identity_Resolving_Key (16 Octets) Return Parameters: Current_Settings (4 Octets) This command is used to enable Low Energy Privacy feature using resolvable private addresses. The value 0x00 disables privacy mode, the values 0x01 and 0x02 enable privacy mode. With value 0x01 the kernel will always use the privacy mode. This means resolvable private address is used when the controller is discoverable and also when pairing is initiated. With value 0x02 the kernel will use a limited privacy mode with a resolvable private address except when the controller is bondable and discoverable, in which case the identity address is used. Exposing the identity address when bondable and discoverable or during initiated pairing can be a privacy issue. For dual-mode controllers this can be neglected since its public address will be exposed over BR/EDR anyway. The benefit of exposing the identity address for pairing purposes is that it makes matching up devices with dual-mode topology during device discovery now possible. If the privacy value 0x02 is used, then also the GATT database should expose the Privacy Characteristic so that remote devices can determine if the privacy feature is in use or not. When the controller has a public address (mandatory for dual-mode controllers) it is used as identity address. In case the controller is single mode LE only without a public address, it is required to configure a static random address first. The privacy mode can only be enabled when an identity address is available. The Identity_Resolving_Key is the local key assigned for the local resolvable private address. Possible errors: Busy Not Supported Invalid Parameters Invalid Index Load Identity Resolving Keys Command ==================================== Command Code: 0x0030 Controller Index: Command Parameters: Key_Count (2 Octets) Key1 { Address (6 Octets) Address_Type (1 Octet) Value (16 Octets) } Key2 { } ... Return Parameters: This command is used to feed the kernel with currently known identity resolving keys. The command does not need to be called again upon the receipt of New Identity Resolving Key events since the kernel updates its list automatically. Possible values for the Address_Type parameter: 0 Reserved (not in use) 1 LE Public 2 LE Random The provided Address and Address_Type are the identity of a device. So either its public address or static random address. Unresolvable random addresses and resolvable random addresses are not valid and will be rejected. This command can be used when the controller is not powered. This command generates a Command Complete event on success or a Command Status event on failure. Possible errors: Invalid Parameters Invalid Index Get Connection Information Command ================================== Command Code: 0x0031 Controller Index: Command Parameters: Address (6 Octets) Address_Type (1 Octet) Return Parameters: Address (6 Octets) Address_Type (1 Octet) RSSI (1 Octet) TX_Power (1 Octet) Max_TX_Power (1 Octet) This command is used to get connection information. Possible values for the Address_Type parameter: 0 BR/EDR 1 LE Public 2 LE Random TX_Power and Max_TX_Power can be set to 127 if values are invalid or unknown. A value of 127 for RSSI indicates that it is not available. This command generates a Command Complete event on success and on failure. In case of failure only Address and Address_Type fields are valid and values of remaining parameters are considered invalid and shall be ignored. Possible errors: Not Connected Not Powered Invalid Parameters Invalid Index Get Clock Information Command ============================= Command Code: 0x0032 Controller Index: Command Parameters: Address (6 Octets) Address_Type (1 Octet) Return Parameters: Address (6 Octets) Address_Type (1 Octet) Local_Clock (4 Octets) Piconet_Clock (4 Octets) Accuracy (2 Octets) This command is used to get local and piconet clock information. Possible values for the Address_Type parameter: 0 BR/EDR 1 Reserved (not in use) 2 Reserved (not in use) The Accuracy can be set to 0xffff which means the value is unknown. If the Address is set to 00:00:00:00:00:00, then only the Local_Clock field has a valid value. The Piconet_Clock and Accuracy fields are invalid and shall be ignored. This command generates a Command Complete event on success and on failure. In case of failure only Address and Address_Type fields are valid and values of remaining parameters are considered invalid and shall be ignored. Possible errors: Not Connected Not Powered Invalid Parameters Invalid Index Add Device Command ================== Command Code: 0x0033 Controller Index: Command Parameters: Address (6 Octets) Address_Type (1 Octet) Action (1 Octet) Return Parameters: Address (6 Octets) Address_Type (1 Octet) This command is used to add a device to the action list. The action list allows scanning for devices and enables incoming connections from known devices. Possible values for the Address_Type parameter: 0 BR/EDR 1 LE Public 2 LE Random Possible values for the Action parameter: 0 Background scan for device 1 Allow incoming connection 2 Auto-connect remote device With the Action 0, when the device is found, a new Device Found event will be sent indicating this device is available. This action is only valid for LE Public and LE Random address types. With the Action 1, the device is allowed to connect. For BR/EDR address type this means an incoming connection. For LE Public and LE Random address types, a connection will be established to devices using directed advertising. If successful a Device Connected event will be sent. With the Action 2, when the device is found, it will be connected and if successful a Device Connected event will be sent. This action is only valid for LE Public and LE Random address types. When a device is blocked using Block Device command, then it is valid to add the device here, but all actions will be ignored until the device is unblocked. Devices added with Action 1 are allowed to connect even if the connectable setting is off. This acts as list of known trusted devices. This command can be used when the controller is not powered and all settings will be programmed once powered. This command generates a Command Complete event on success or failure. Possible errors: Failed Invalid Parameters Invalid Index Remove Device Command ===================== Command Code: 0x0034 Controller Index: Command Parameters: Address (6 Octets) Address_Type (1 Octet) Return Parameters: Address (6 Octets) Address_Type (1 Octet) This command is used to remove a device from the action list previously added by using the Add Device command. Possible values for the Address_Type parameter: 0 BR/EDR 1 LE Public 2 LE Random When the Address parameter is 00:00:00:00:00:00, then all previously added devices will be removed. This command can be used when the controller is not powered and all settings will be programmed once powered. This command generates a Command Complete event on success or failure. Possible errors: Invalid Parameters Invalid Index Load Connection Parameters Command ================================== Command Code: 0x0035 Controller Index: Command Parameters: Param_Count (2 Octets) Param1 { Address (6 Octets) Address_Type (1 Octet) Min_Connection_Interval (2 Octets) Max_Connection_Interval (2 Octets) Connection_Latency (2 Octets) Supervision_Timeout (2 Octets) } Param2 { } ... Return Parameters: This command is used to load connection parameters from several devices into kernel. Currently this is only supported on controllers with Low Energy support. Possible values for the Address_Type parameter: 0 Reserved (not in use) 1 LE Public 2 LE Random The provided Address and Address_Type are the identity of a device. So either its public address or static random address. The Min_Connection_Interval, Max_Connection_Interval, Connection_Latency and Supervision_Timeout parameters should be configured as described in Core 4.1 spec, Vol 2, 7.8.12. This command can be used when the controller is not powered. This command generates a Command Complete event on success or a Command Status event on failure. Possible errors: Invalid Parameters Invalid Index Not Supported Read Unconfigured Controller Index List Command =============================================== Command Code: 0x0036 Controller Index: Command Parameters: Return Parameters: Num_Controllers (2 Octets) Controller_Index[i] (2 Octets) This command returns the list of currently unconfigured controllers. Unconfigured controllers added after calling this command can be monitored using the Unconfigured Index Added event. An unconfigured controller can either move to a configured state by indicating Unconfigured Index Removed event followed by an Index Added event; or it can be removed from the system which would be indicated by the Unconfigured Index Removed event. Only controllers that require configuration will be listed with this command. A controller that is fully configured will not be listed even if it supports configuration changes. This command generates a Command Complete event on success or a Command Status event on failure. Read Controller Configuration Information Command ================================================= Command Code: 0x0037 Controller Index: Command Parameters: Return Parameters: Manufacturer (2 Octets) Supported_Options (4 Octets) Missing_Options (4 Octets) This command is used to retrieve the supported configuration options of a controller and the missing configuration options. The missing options are required to be configured before the controller is considered fully configured and ready for standard operation. The command is typically used right after getting the response to Read Unconfigured Controller Index List command or Unconfigured Index Added event. Supported_Options and Missing_Options is a bitmask with currently the following available bits: 0 External configuration 1 Bluetooth public address configuration It is valid to call this command on controllers that do not require any configuration. It is possible that a fully configured controller offers additional support for configuration. For example a controller may contain a valid Bluetooth public device address, but also allows to configure it from the host stack. In this case the general support for configurations will be indicated by the Controller Configuration settings. For controllers where no configuration options are available that setting option will not be present. When all configurations have been completed and as a result the Missing_Options mask would become empty, then the now ready controller will be announced via Index Added event. This command generates a Command Complete event on success or a Command Status event on failure. Possible errors: Invalid Parameters Invalid Index Set External Configuration Command ================================== Command Code: 0x0038 Controller Index: Command Parameters: Configuration (1 Octet) Return Parameters: Missing_Options (4 Octets) This command allows to change external configuration option to indicate that a controller is now configured or unconfigured. The value 0x00 sets unconfigured state and the value 0x01 sets configured state of the controller. It is not mandatory that this configuration option is provided by a controller. If it is provided, the configuration has to happen externally using user channel operation or via vendor specific methods. Setting this option and when Missing_Options returns zero, this means that the controller will switch to configured state and it can be expected that it will be announced via Index Added event. Wrongly configured controllers might still cause an error when trying to power them via Set Powered command. This command generates a Command Complete event on success or a Command Status event on failure. Possible errors: Rejected Not Supported Invalid Parameters Invalid Index Set Public Address Command ========================== Command Code: 0x0039 Controller Index: Command Parameters: Address (6 Octets) Return Parameters: Missing_Options (4 Octets) This command allows configuration of public address. Since a vendor specific procedure is required, this command might not be supported by all controllers. Actually most likely only a handful embedded controllers will offer support for this command. When the support for Bluetooth public address configuration is indicated in the supported options mask, then this command can be used to configure the public address. It is only possible to configure the public address when the controller is powered off. For an unconfigured controller and when Missing_Options returns an empty mask, this means that a Index Added event for the now fully configured controller can be expected. For a fully configured controller, the current controller index will become invalid and an Unconfigured Index Removed event will be sent. Once the address has been successfully changed an Index Added event will be sent. There is no guarantee that the controller index stays the same. All previous configured parameters and settings are lost when this command succeeds. The controller has to be treated as new one. Use this command for a fully configured controller only when you really know what you are doing. This command generates a Command Complete event on success or a Command Status event on failure. Possible errors: Rejected Not Supported Invalid Parameters Invalid Index Start Service Discovery Command =============================== Command Code: 0x003a Controller Index: Command Parameters: Address_Type (1 Octet) RSSI_Threshold (1 Octet) UUID_Count (2 Octets) UUID[i] (16 Octets) Return Parameters: Address_Type (1 Octet) This command is used to start the process of discovering remote devices with a specific UUID. A Device Found event will be sent for each discovered device. Possible values for the Address_Type parameter are a bit-wise or of the following bits: 0 BR/EDR 1 LE Public 2 LE Random By combining these e.g. the following values are possible: 1 BR/EDR 6 LE (public & random) 7 BR/EDR/LE (interleaved discovery) The service discovery uses active scanning for Low Energy scanning and will search for UUID in both advertising data and scan response data. Found devices that have a RSSI value smaller than RSSI_Threshold are not reported via DeviceFound event. Setting a value of 127 will cause all devices to be reported. The list of UUIDs identifies a logical OR. Only one of the UUIDs have to match to cause a DeviceFound event. Providing an empty list of UUIDs with Num_UUID set to 0 which means that DeviceFound events are send out for all devices above the RSSI_Threshold. In case RSSI_Threshold is set to 127 and UUID_Count is 0, then this command behaves exactly the same as Start Discovery. When the discovery procedure starts the Discovery event will notify this similar to Start Discovery. This command can only be used when the controller is powered. This command generates a Command Complete event on success or failure. Possible errors: Busy Not Supported Invalid Parameters Not Powered Invalid Index Read Local Out Of Band Extended Data Command ============================================ Command Code: 0x003b Controller Index: Command Parameters: Address_Type (1 Octet) Return Parameters: Address_Type (1 Octet) EIR_Data_Length (2 Octets) EIR_Data (0-65535 Octets) This command is used to read the local Out of Band data information and provide them encoded as extended inquiry response information or advertising data. Possible values for the Address_Type parameter are a bit-wise or of the following bits: 0 BR/EDR 1 LE Public 2 LE Random By combining these e.g. the following values are possible: 1 BR/EDR 6 LE (public & random) 7 Reserved (not in use) For BR/EDR controller (Address_Type 1) the returned information will contain the following information: Class of Device Simple Pairing Hash C-192 (optional) Simple Pairing Randomizer R-192 (optional) Simple Pairing Hash C-256 (optional) Simple Pairing Randomizer R-256 (optional) Service Class UUID (optional) Bluetooth Local Name (optional) The Simple Pairing Hash C-256 and Simple Pairing Randomizer R-256 fields are only included when secure connections has been enabled. The Device Address (BD_ADDR) is not included in the EIR_Data and needs to be taken from controller information. For LE controller (Address_Type 6) the returned information will contain the following information: LE Bluetooth Device Address LE Role LE Secure Connections Confirmation Value (optional) LE Secure Connections Random Value (optional) Appearance (optional) Local Name (optional) Flags The LE Secure Connections Confirmation Value and LE Secure Connections Random Value fields are only included when secure connections has been enabled. The Security Manager TK Value from the Bluetooth specification can not be provided by this command. The Out Of Band information here are for asymmetric exchanges based on Diffie-Hellman key exchange. The Security Manager TK Value is a symmetric random number that has to be acquired and agreed upon differently. The returned information from BR/EDR controller and LE controller types are not related to each other. Once they have been used over an Out Of Band link, a new set of information shall be requested. When Secure Connections Only mode has been enabled, then the fields for Simple Pairing Hash C-192 and Simple Pairing Randomizer R-192 are not returned. Only the fields for the strong secure connections pairing are included. This command can only be used when the controller is powered. Values returned by this command become invalid when the controller is powered down. After each power-cycle it is required to call this command again to get updated information. This command generates a Command Complete event on success or a Command Status event on failure. Possible errors: Not Supported Busy Invalid Parameters Not Powered Invalid Index Read Extended Controller Index List Command =========================================== Command Code: 0x003c Controller Index: Command Parameters: Return Parameters: Num_Controllers (2 Octets) Controller_Index[i] (2 Octets) Controller_Type[i] (1 Octet) Controller_Bus[i] (1 Octet) This command returns the list of currently known controllers. It includes configured, unconfigured and alternate controllers. Controllers added or removed after calling this command can be be monitored using the Extended Index Added and Extended Index Removed events. The existing Index Added, Index Removed, Unconfigured Index Added and Unconfigured Index Removed are no longer sent after this command has been used at least once. Instead of calling Read Controller Index List and Read Unconfigured Controller Index List, this command combines all the information and can be used to retrieve the controller list. The Controller_Type parameter has these values: 0x00 Primary Controller (BR/EDR and/or LE) 0x01 Unconfigured Controller (BR/EDR and/or LE) 0x02 Alternate MAC/PHY Controller (AMP) The 0x00 and 0x01 types indicate a primary BR/EDR and/or LE controller. The difference is just if they need extra configuration or if they are fully configured. Controllers in configured state will be listed as 0x00 and controllers in unconfigured state will be listed as 0x01. A controller that is fully configured and supports configuration changes will be listed as 0x00. Alternate MAC/PHY controllers will be listed as 0x02. They do not support the difference between configured and unconfigured state. The Controller_Bus parameter has these values: 0x00 Virtual 0x01 USB 0x02 PCMCIA 0x03 UART 0x04 RS232 0x05 PCI 0x06 SDIO 0x07 SPI 0x08 I2C 0x09 SMD 0x0A VIRTIO 0x0B IPC Controllers marked as RAW only operation are currently not listed by this command. This command generates a Command Complete event on success or a Command Status event on failure. Read Advertising Features Command ================================= Command Code: 0x003d Controller Index: Command Parameters: Return Parameters: Supported_Flags (4 Octets) Max_Adv_Data_Len (1 Octet) Max_Scan_Rsp_Len (1 Octet) Max_Instances (1 Octet) Num_Instances (1 Octet) Instance[i] (1 Octet) This command is used to read the advertising features supported by the controller and stack. With the Supported_Flags field the possible values for the Flags field in Add Advertising command provided: 0 Switch into Connectable mode 1 Advertise as Discoverable 2 Advertise as Limited Discoverable 3 Add Flags field to Adv_Data 4 Add TX Power field to Adv_Data 5 Add Appearance field to Scan_Rsp 6 Add Local Name in Scan_Rsp 7 Secondary Channel with LE 1M 8 Secondary Channel with LE 2M 9 Secondary Channel with LE Coded The Flags bit 0 indicates support for connectable advertising and for switching to connectable advertising independent of the connectable global setting. When this flag is not supported, then the global connectable setting determines if undirected connectable, undirected scannable or undirected non-connectable advertising is used. It also determines the use of non-resolvable random address versus identity address or resolvable private address. The Flags bit 1 indicates support for advertising with discoverable mode enabled. Users of this flag will decrease the Max_Adv_Data_Len by 3 octets. In this case the advertising data flags are managed and added in front of the provided advertising data. The Flags bit 2 indicates support for advertising with limited discoverable mode enabled. Users of this flag will decrease the Max_Adv_Data_Len by 3 octets. In this case the advertising data flags are managed and added in front of the provided advertising data. The Flags bit 3 indicates support for automatically keeping the Flags field of the advertising data updated. Users of this flag will decrease the Max_Adv_Data_Len by 3 octets and need to keep that in mind. The Flags field will be added in front of the advertising data provided by the user. Note that with Flags bit 1 and Flags bit 2, this one will be implicitly used even if it is not marked as supported. The Flags bit 4 indicates support for automatically adding the TX Power value to the advertising data. Users of this flag will decrease the Max_Adv_Data_Len by 3 octets. The TX Power field will be added at the end of the user provided advertising data. If the controller does not support TX Power information, then this bit will not be set. The Flags bit 5 indicates support for automatically adding the Appearance value to the scan response data. Users of this flag will decrease the Max_Scan_Rsp_len by 4 octets. The Appearance field will be added in front of the scan response data provided by the user. If the appearance value is not supported, then this bit will not be set. The Flags bit 6 indicates support for automatically adding the Local Name value to the scan response data. This flag indicates an opportunistic approach for the Local Name. If enough space in the scan response data is available, it will be added. If the space is limited a short version or no name information. The Local Name will be added at the end of the scan response data. The Flags bit 7 indicates support for advertising in secondary channel in LE 1M PHY. The Flags bit 8 indicates support for advertising in secondary channel in LE 2M PHY. Primary channel would be on 1M. The Flags bit 9 indicates support for advertising in secondary channel in LE CODED PHY. The valid range for Instance identifiers is 1-254. The value 0 is reserved for internal use and the value 255 is reserved for future extensions. However the Max_Instances value for indicating the number of supported Instances can be also 0 if the controller does not support any advertising. The Max_Adv_Data_Len and Max_Scan_Rsp_Len provides extra information about the maximum length of the data fields. For now this will always return the value 31. Different flags however might decrease the actual available length in these data fields. With Num_Instances and Instance array the currently occupied Instance identifiers can be retrieved. This command generates a Command Complete event on success or a Command Status event on failure. Possible errors: Invalid Parameters Invalid Index Add Advertising Command ======================= Command Code: 0x003e Controller Index: Command Parameters: Instance (1 Octet) Flags (4 Octets) Duration (2 Octets) Timeout (2 Octets) Adv_Data_Len (1 Octet) Scan_Rsp_Len (1 Octet) Adv_Data (0-255 Octets) Scan_Rsp (0-255 Octets) Return Parameters: Instance (1 Octet) This command is used to configure an advertising instance that can be used to switch a Bluetooth Low Energy controller into advertising mode. Added advertising information with this command will not be visible immediately if advertising is enabled via the Set Advertising command. The usage of the Set Advertising command takes precedence over this command. Instance information is stored and will be advertised once advertising via Set Advertising has been disabled. The Instance identifier is a value between 1 and the number of supported instances. The value 0 is reserved. With the Flags value the type of advertising is controlled and the following flags are defined: 0 Switch into Connectable mode 1 Advertise as Discoverable 2 Advertise as Limited Discoverable 3 Add Flags field to Adv_Data 4 Add TX Power field to Adv_Data 5 Add Appearance field to Scan_Rsp 6 Add Local Name in Scan_Rsp 7 Secondary Channel with LE 1M 8 Secondary Channel with LE 2M 9 Secondary Channel with LE Coded When the connectable flag is set, then the controller will use undirected connectable advertising. The value of the connectable setting can be overwritten this way. This is useful to switch a controller into connectable mode only for LE operation. This is similar to the mode 0x02 from the Set Advertising command. When the connectable flag is not set, then the controller will use advertising based on the connectable setting. When using non-connectable or scannable advertising, the controller will be programmed with a non-resolvable random address. When the system is connectable, then the identity address or resolvable private address will be used. Using the connectable flag is useful for peripheral mode support where BR/EDR (and/or LE) is controlled by Add Device. This allows making the peripheral connectable without having to interfere with the global connectable setting. If Scan_Rsp_Len is zero and connectable flag is not set and the global connectable setting is off, then non-connectable advertising is used. If Scan_Rsp_Len is larger than zero and connectable flag is not set and the global advertising is off, then scannable advertising is used. This small difference is supported to provide less air traffic for devices implementing broadcaster role. Secondary channel flags can be used to advertise in secondary channel with the corresponding PHYs. These flag bits are mutually exclusive and setting multiple will result in Invalid Parameter error. Choosing either LE 1M or LE 2M will result in using extended advertising on the primary channel with LE 1M and the respectively LE 1M or LE 2M on the secondary channel. Choosing LE Coded will result in using extended advertising on the primary and secondary channels with LE Coded. Choosing none of these flags will result in legacy advertising. The Duration parameter configures the length of an Instance. The value is in seconds. A value of 0 indicates a default value is chosen for the Duration. The default is 2 seconds. If only one advertising Instance has been added, then the Duration value will be ignored. It only applies for the case where multiple Instances are configured. In that case every Instance will be available for the Duration time and after that it switches to the next one. This is a simple round-robin based approach. The Timeout parameter configures the life-time of an Instance. In case the value 0 is used it indicates no expiration time. If a timeout value is provided, then the advertising Instance will be automatically removed when the timeout passes. The value for the timeout is in seconds. Powering down a controller will invalidate all advertising Instances and it is not possible to add a new Instance with a timeout when the controller is powered down. When a Timeout is provided, then the Duration subtracts from the actual Timeout value of that Instance. For example an Instance with Timeout of 5 and Duration of 2 will be scheduled exactly 3 times, twice with 2 seconds and once with one second. Other Instances have no influence on the Timeout. Re-adding an already existing instance (i.e. issuing the Add Advertising command with an Instance identifier of an existing instance) will update that instance's configuration. An instance being added or changed while another instance is being advertised will not be visible immediately but only when the new/changed instance is being scheduled by the round robin advertising algorithm. Changes to an instance that is currently being advertised will cancel that instance and switch to the next instance. The changes will be visible the next time the instance is scheduled for advertising. In case a single instance is active, this means that changes will be visible right away. A pre-requisite is that LE is already enabled, otherwise this command will return a "rejected" response. This command can be used when the controller is not powered and all settings will be programmed once powered. This command generates a Command Complete event on success or a Command Status event on failure. Possible errors: Failed Rejected Not Supported Invalid Parameters Invalid Index Remove Advertising Command ========================== Command Code: 0x003f Controller Index: Command Parameters: Instance (1 Octet) Return Parameters: Instance (1 Octet) This command is used to remove an advertising instance that can be used to switch a Bluetooth Low Energy controller into advertising mode. When the Instance parameter is zero, then all previously added advertising Instances will be removed. Removing advertising information with this command will not be visible as long as advertising is enabled via the Set Advertising command. The usage of the Set Advertising command takes precedence over this command. Changes to Instance information are stored and will be advertised once advertising via Set Advertising has been disabled. Removing an instance while it is being advertised will immediately cancel the instance, even when it has been advertised less then its configured Timeout or Duration. This command can be used when the controller is not powered and all settings will be programmed once powered. This command generates a Command Complete event on success or a Command Status event on failure. Possible errors: Invalid Parameters Invalid Index Get Advertising Size Information Command ======================================== Command Code: 0x0040 Controller Index: Command Parameters: Instance (1 Octet) Flags (4 Octets) Return Parameters: Instance (1 Octet) Flags (4 Octets) Max_Adv_Data_Len (1 Octet) Max_Scan_Rsp_Len (1 Octet) The Read Advertising Features command returns the overall maximum size of advertising data and scan response data fields. That size is valid when no Flags are used. However when certain Flags are used, then the size might decrease. This command can be used to request detailed information about the maximum available size. The following Flags values are defined: 0 Switch into Connectable mode 1 Advertise as Discoverable 2 Advertise as Limited Discoverable 3 Add Flags field to Adv_Data 4 Add TX Power field to Adv_Data 5 Add Appearance field to Scan_Rsp 6 Add Local Name in Scan_Rsp To get accurate information about the available size, the same Flags values should be used with the Add Advertising command. The Max_Adv_Data_Len and Max_Scan_Rsp_Len fields provide information about the maximum length of the data fields for the given Flags values. When the Flags field is zero, then these fields would contain the same values as Read Advertising Features. Possible errors: Invalid Parameters Invalid Index Start Limited Discovery Command =============================== Command Code: 0x0041 Controller Index: Command Parameters: Address_Type (1 Octet) Return Parameters: Address_Type (1 Octet) This command is used to start the process of discovering remote devices using the limited discovery procedure. A Device Found event will be sent for each discovered device. Possible values for the Address_Type parameter are a bit-wise or of the following bits: 0 BR/EDR 1 LE Public 2 LE Random By combining these e.g. the following values are possible: 1 BR/EDR 6 LE (public & random) 7 BR/EDR/LE (interleaved discovery) The limited discovery uses active scanning for Low Energy scanning and will search for devices with the limited discoverability flag configured. On BR/EDR it uses LIAC and filters on the limited discoverability flag of the class of device. When the discovery procedure starts the Discovery event will notify this similar to Start Discovery. This command can only be used when the controller is powered. This command generates a Command Complete event on success or failure. Possible errors: Busy Not Supported Invalid Parameters Not Powered Invalid Index Read Extended Controller Information Command ============================================ Command Code: 0x0042 Controller Index: Command Parameters: Return Parameters: Address (6 Octets) Bluetooth_Version (1 Octet) Manufacturer (2 Octets) Supported_Settings (4 Octets) Current_Settings (4 Octets) EIR_Data_Length (2 Octets) EIR_Data (0-65535 Octets) This command is used to retrieve the current state and basic information of a controller. It is typically used right after getting the response to the Read Controller Index List command or an Index Added event (or its extended counterparts). The Address parameter describes the controllers public address and it can be expected that it is set. However in case of single mode Low Energy only controllers it can be 00:00:00:00:00:00. To power on the controller in this case, it is required to configure a static address using Set Static Address command first. If the public address is set, then it will be used as identity address for the controller. If no public address is available, then the configured static address will be used as identity address. In the case of a dual-mode controller with public address that is configured as Low Energy only device (BR/EDR switched off), the static address is used when set and public address otherwise. Current_Settings and Supported_Settings is a bitmask with currently the following available bits: 0 Powered 1 Connectable 2 Fast Connectable 3 Discoverable 4 Bondable 5 Link Level Security (Sec. mode 3) 6 Secure Simple Pairing 7 Basic Rate/Enhanced Data Rate 8 High Speed 9 Low Energy 10 Advertising 11 Secure Connections 12 Debug Keys 13 Privacy 14 Controller Configuration 15 Static Address 16 PHY Configuration 17 Wideband Speech 18 Connected Isochronous Stream - Central 19 Connected Isochronous Stream - Peripheral The EIR_Data field contains information about class of device, local name and other values. Not all of them might be present. For example a Low Energy only device does not contain class of device information. When any of the values in the EIR_Data field changes, the event Extended Controller Information Changed will be used to inform clients about the updated information. This command generates a Command Complete event on success or a Command Status event on failure. Possible errors: Invalid Parameters Invalid Index Set Appearance Command ====================== Command Code: 0x0043 Controller Index: Command Parameters: Appearance (2 Octets) Return Parameters: This command is used to set the appearance value of a controller. This command can be used when the controller is not powered and all settings will be programmed once powered. The value of appearance will be remembered when switching the controller off and back on again. So the appearance only have to be set once when a new controller is found and will stay until removed. This command generates a Command Complete event on success or a Command Status event on failure. This command is only available for LE capable controllers. It will return Not Supported otherwise. Possible errors: Not Supported Invalid Parameters Invalid Index Get PHY Configuration Command ============================= Command Code: 0x0044 Controller Index: Command Parameters: Return Parameters: Supported_PHYs (4 Octet) Configurable_PHYs (4 Octets) Selected_PHYs (4 Octet) The PHYs parameters are a bitmask with currently the following available bits: 0 BR 1M 1-Slot 1 BR 1M 3-Slot 2 BR 1M 5-Slot 3 EDR 2M 1-Slot 4 EDR 2M 3-Slot 5 EDR 2M 5-Slot 6 EDR 3M 1-Slot 7 EDR 3M 3-Slot 8 EDR 3M 5-Slot 9 LE 1M TX 10 LE 1M RX 11 LE 2M TX 12 LE 2M RX 13 LE Coded TX 14 LE Coded RX If BR/EDR is supported, then BR 1M 1-Slot is supported by default and can also not be deselected. If LE is supported, then LE 1M TX and LE 1M RX are supported by default. Disabling BR/EDR completely or respectively LE has no impact on the PHY configuration. It is remembered over power cycles. This command generates a Command Complete event on success or a Command Status event on failure. Possible errors: Invalid Parameters Invalid Index Set PHY Configuration Command ============================= Command Code: 0x0045 Controller Index: Command Parameters: Selected_PHYs (4 Octet) Return Parameters: This command is used to set the default PHY to the controller. This will be stored and used for all the subsequent scanning and connection initiation. The list of supported PHYs can be retrieved via the Get PHY Configuration command. Selecting unsupported or deselecting default PHYs will result in an Invalid Parameter error. This can be called at any point to change the Selected PHYs. Refer Get PHY Configuration command for PHYs parameter. This command generates a Command Complete event on success or a Command Status event on failure. Possible errors: Invalid Parameters Invalid Index Load Blocked Keys Command =========================== Command Code: 0x0046 Controller Index: Command Parameters: Key_Count (2 Octets) Key1 { Key_Type (1 Octet) Value (16 Octets) } Key2 { } ... Return Parameters: This command is used to feed the kernel a list of keys that are known to be vulnerable. If the pairing procedure produces any of these keys, they will be silently dropped and any attempt to enable encryption rejected. Currently defined Key_Type values are: 0x00 Link Key (BR/EDR) 0x01 Long Term Key (LE) 0x02 Identity Resolving Key (LE) This command can be used when the controller is not powered. This command generates a Command Complete event on success or a Command Status event on failure. Possible errors: Invalid Parameters Invalid Index Set Wideband Speech Command =========================== Command Code: 0x0047 Controller Index: Command Parameters: Wideband_Speech (1 Octet) Return Parameters: Current_Settings (4 Octets) This command is used to enable/disable Wideband Speech support for a controller. The allowed values for the Wideband_Speech command parameter are 0x00 and 0x01. All other values will return Invalid Parameters. The value 0x00 disables Wideband Speech, the value 0x01 enables Wideband Speech. This command is only available for BR/EDR capable controllers and require controller specific support. This command can be used when the controller is not powered and all settings will be programmed once powered. In case the controller does not support Wideband Speech the command will fail regardless with Not Supported error. This command generates a Command Complete event on success or a Command Status event on failure. Possible errors: Busy Not Supported Invalid Parameters Invalid Index Read Controller Capabilities Command ==================================== Command Code: 0x0048 Controller Index: Command Parameters: Return Parameters: Capabilities_Data_Length (2 Octets) Capabilities_Data (0-65535 Octets) This command is used to retrieve the supported capabilities by the controller or the host stack. The Capabilities_Data_Length and Capabilities_Data parameters provide a list of security settings, features and information. It uses the same format as EIR_Data, but with the namespace defined here. Data Type Name -------------------- 0x01 Flags 0x02 Max Encryption Key Size (BR/EDR) 0x03 Max Encryption Key Size (LE) 0x04 Supported Tx Power (LE) Flags (data type 0x01) 0 Remote public key validation (BR/EDR) 1 Remote public key validation (LE) 2 Encryption key size enforcement (BR/EDR) 3 Encryption key size enforcement (LE) Max Encryption Key Size (data types 0x02 and 0x03) When the field is present, then it provides 1 Octet value indicating the max encryption key size. If the field is not present, then it is unknown what the max encryption key size of the controller or host is in use. Supported LE Tx Power (data type 0x04) When present, this 2-octet field provides the min and max LE Tx power supported by the controller, respectively, as reported by the LE Read Transmit Power HCI command. If this field is not available, it indicates that the LE Read Transmit Power HCI command was not available. This command generates a Command Complete event on success or a Command Status event on failure. Possible errors: Invalid Parameters Invalid Index Read Experimental Features Information Command ============================================== Command Code: 0x0049 Controller Index: or Command Parameters: Return Parameters: Feature_Count (2 Octets) Feature1 { UUID (16 Octets) Flags (4 Octets) } Feature2 { } ... This command is used to retrieve the supported experimental features by the host stack. The UUID values are not defined here. They can change over time and are on purpose not stable. Features that mature will be removed at some point. The mapping of feature UUID to the actual functionality of a given feature is out of scope here. The following bits are defined for the Flags parameter: 0 Feature active 1 Causes change in supported settings This command generates a Command Complete event on success or a Command Status event on failure. Possible errors: Invalid Parameters Invalid Index Set Experimental Feature Command ================================ Command Code: 0x004a Controller Index: or Command Parameters: UUID (16 Octets) Action (1 Octet) Return Parameters: UUID (16 Octets) Flags (4 Octets) This command is used to change the setting of an experimental feature of the host stack. The UUID value must be a supported value returned from the Read Experimental Features Information command. The Action parameter is UUID specific, but in most cases it will be just a simple on/off toggle with these values: 0x00 Disable feature 0x01 Enable feature It depends on the feature if the command can be used when the controller is powered up. See Flags parameter of Read Experimental Features Information command for details if the controller has to be powered down first. The following bits are defined for the Flags return parameter: 0 Feature active 1 Supported settings changed When a feature causes the change of supported settings, then it is a good idea to re-read the controller information. When the UUID parameter is an empty UUID (16 x 0x00), then all experimental features will be deactivated. This command generates a Command Complete event on success or a Command Status event on failure. Possible errors: Invalid Parameters Not Powered Invalid Index Read Default System Configuration Command ========================================= Command Code: 0x004b Controller Index: Command Parameters: Return Parameters: Parameter1 { Parameter_Type (2 Octet) Value_Length (1 Octet) Value (0-255 Octets) } Parameter2 { } ... This command is used to read a list of default controller parameters. Currently defined Parameter_Type values are: 0x0000 BR/EDR Page Scan Type 0x0001 BR/EDR Page Scan Interval 0x0002 BR/EDR Page Scan Window 0x0003 BR/EDR Inquiry Scan Type 0x0004 BR/EDR Inquiry Scan Interval 0x0005 BR/EDR Inquiry Scan Window 0x0006 BR/EDR Link Supervision Timeout 0x0007 BR/EDR Page Timeout 0x0008 BR/EDR Min Sniff Interval 0x0009 BR/EDR Max Sniff Interval 0x000a LE Advertisement Min Interval 0x000b LE Advertisement Max Interval 0x000c LE Multi Advertisement Rotation Interval 0x000d LE Scanning Interval for auto connect 0x000e LE Scanning Window for auto connect 0x000f LE Scanning Interval for wake scenarios 0x0010 LE Scanning Window for wake scenarios 0x0011 LE Scanning Interval for discovery 0x0012 LE Scanning Window for discovery 0x0013 LE Scanning Interval for adv monitoring 0x0014 LE Scanning Window for adv monitoring 0x0015 LE Scanning Interval for connect 0x0016 LE Scanning Window for connect 0x0017 LE Min Connection Interval 0x0018 LE Max Connection Interval 0x0019 LE Connection Latency 0x001a LE Connection Supervision Timeout 0x001b LE Autoconnect Timeout This command can be used at any time and will return a list of supported default parameters as well as their current value. This command generates a Command Complete event on success or a Command Status event on failure. Possible errors: Invalid Parameters Invalid Index Set Default System Configuration Command ======================================== Command Code: 0x004c Controller Index: Command Parameters: Parameter1 { Parameter_Type (2 Octet) Value_Length (1 Octet) Value (0-255 Octets) } Parameter2 { } ... Return Parameters: This command is used to set a list of default controller parameters. See Read Default System Configuration command for list of supported Parameter_Type values. This command can be used when the controller is not powered and all supported parameters will be programmed once powered. When providing unsupported values or invalid values, no parameter value will be changed and all values discarded. This command generates a Command Complete event on success or a Command Status event on failure. Possible errors: Rejected Not Supported Invalid Parameters Invalid Index Read Default Runtime Configuration Command ========================================== Command Code: 0x004d Controller Index: Command Parameters: Return Parameters: Parameter1 { Parameter_Type (2 Octet) Value_Length (1 Octet) Value (0-255 Octets) } Parameter2 { } ... This command is used to read a list of default runtime parameters. Currently no Parameter_Type values are defined and an empty list will be returned. This command can be used at any time and will return a list of supported default parameters as well as their current value. This command generates a Command Complete event on success or a Command Status event on failure. Possible errors: Invalid Parameters Invalid Index Set Default Runtime Configuration Command ========================================= Command Code: 0x004e Controller Index: Command Parameters: Parameter1 { Parameter_Type (2 Octet) Value_Length (1 Octet) Value (0-255 Octets) } Parameter2 { } ... Return Parameters: This command is used to set a list of default runtime parameters. See Read Default Runtime Configuration command for list of supported Parameter_Type values. This command can be used at any time and will change the runtime default. Changes however will not apply to existing connections or currently active operations. When providing unsupported values or invalid values, no parameter value will be changed and all values discarded. This command generates a Command Complete event on success or a Command Status event on failure. Possible errors: Rejected Not Supported Invalid Parameters Invalid Index Get Device Flags Command ======================== Command Code: 0x004f Controller Index: Command Parameters: Address (6 Octets) Address_Type (1 Octet) Return Parameters: Address (6 Octets) Address_Type (1 Octet) Supported_Flags (4 Octets) Current_Flags (4 Octets) This command is used to retrieve additional flags and settings for devices that are added via Add Device command. Possible values for the Address_Type parameter: 0 BR/EDR 1 LE Public 2 LE Random The Flags parameters are a bitmask with currently the following available bits: 0 Remote Wakeup enabled 1 Device Privacy Mode enabled 2 Address Resolution enabled This command generates a Command Complete event on success or a Command Status event on failure. Possible errors: Invalid Parameters Invalid Index Set Device Flags Command ======================== Command Code: 0x0050 Controller Index: Command Parameters: Address (6 Octets) Address_Type (1 Octet) Current_Flags (4 Octets) Return Parameters: Address (6 Octets) Address_Type (1 Octet) This command is used to configure additional flags and settings for devices that are added via Add Device command. Possible values for the Address_Type parameter: 0 BR/EDR 1 LE Public 2 LE Random The list of supported Flags can be retrieved via the Get Device Flags or Device Flags Changed command. Selecting unsupported flags will result in an Invalid Parameter error; Refer to the Get Device Flags command for a detailed description of the Flags parameters. This command can be used when the controller is not powered and all settings will be programmed once powered. This command generates a Command Complete event on success or a Command Status event on failure. Possible errors: Invalid Parameters Invalid Index Read Advertisement Monitor Features Command =========================================== Command Code: 0x0051 Controller Index: Command Parameters: Return Parameters: Supported_Features (4 Octets) Enabled_Features (4 Octets) Max_Num_Handles (2 Octets) Max_Num_Patterns (1 Octet) Num_Handles (2 Octets) Handle1 (2 Octets) Handle2 (2 Octets) ... This command is used to read the advertisement monitor features supported by the controller and stack. Supported_Features lists all related features supported by the controller while Enabled_Features lists the ones currently used by the kernel. Supported_Features and Enabled_Features are bitmasks with currently the following available bits: 0 Advertisement content monitoring based on patterns with logic OR. Max_Num_Handles indicates the maximum number of supported advertisement monitors. Note that the actual number of supported ones might be less depending on the limitation of the controller. Max_Num_Pattern indicates the maximum number of supported patterns in an advertisement patterns monitor. Note that the actual number of supported ones might be less depending on the limitation of the controller. Num_Handles indicates the number of added advertisement monitors, and it is followed by a list of handles. This command can be used when the controller is not powered. Add Advertisement Patterns Monitor Command ========================================== Command Code: 0x0052 Controller Index: Command Parameters: Pattern_Count (1 Octet) Pattern1 { AD_Type (1 Octet) Offset (1 Octet) Length (1 Octet) Value (31 Octets) } Pattern2 { } ... Return Parameters: Monitor_Handle (2 Octets) This command is used to add an advertisement monitor whose filtering conditions are patterns. The kernel will trigger scanning if there is at least one monitor added. If the controller supports advertisement filtering, the kernel would offload the content filtering to the controller in order to reduce power consumption; otherwise the kernel ignores the content of the monitor. Note that if the there are more than one patterns, OR logic would applied among patterns during filtering. In other words, any advertisement matching at least one pattern in a given monitor would be considered as a match. A pattern contains the following fields. AD_Data_Type Advertising Data Type. The possible values are defined in Core Specification Supplement. Offset The start index where pattern matching shall be performed with in the AD data. Length The length of the pattern value in bytes. Value The value of the pattern in bytes. Here is an example of a pattern. { 0x16, // Service Data - 16-bit UUID 0x02, // Skip the UUID part. 0x04, {0x11, 0x22, 0x33, 0x44}, } This command can be used when the controller is not powered and all settings will be programmed once powered. Possible errors: Failed Busy No Resources Invalid Parameters Remove Advertisement Monitor Command ==================================== Command Code: 0x0053 Controller Index: Command Parameters: Monitor_Handle (2 Octets) Return Parameters: Monitor_Handle (2 Octets) This command is used to remove advertisement monitor(s). The kernel would remove the monitor(s) with Monitor_Handle and update the LE scanning. When the Monitor_Handle is set to zero, then all previously added handles will be removed. Removing a monitor while it is being added will be ignored. This command can be used when the controller is not powered and all settings will be programmed once powered. Possible errors: Failed Busy Add Extended Advertising Parameters Command =========================================== Command Code: 0x0054 Controller Index: Command Parameters: Instance (1 Octet) Flags (4 Octets) Duration (2 Octets) Timeout (2 Octets) MinInterval (4 Octets) MaxInterval (4 Octets) TxPower (1 Octet) Return Parameters: Instance (1 Octet) TxPower (1 Octet) MaxAdvDataLen (1 Octet) MaxScanRspLen (1 Octet) This command is used to configure the parameters for Bluetooth Low Energy advertising instance. This command is expected to be followed by an Add Extended Advertising Data command to complete and enable the advertising instance. Added advertising information with this command will not be visible immediately if advertising is enabled via the Set Advertising command. The usage of the Set Advertising command takes precedence over this command. Instance information is stored and will be advertised once advertising via Set Advertising has been disabled. The Instance identifier is a value between 1 and the number of supported instances. The value 0 is reserved. With the Flags value the type of advertising is controlled and the following flags are defined: 0 Switch into Connectable mode 1 Advertise as Discoverable 2 Advertise as Limited Discoverable 3 Add Flags field to Adv_Data 4 Add TX Power field to Adv_Data 5 Add Appearance field to Scan_Rsp 6 Add Local Name in Scan_Rsp 7 Secondary Channel with LE 1M 8 Secondary Channel with LE 2M 9 Secondary Channel with LE Coded 10 Indicate tx power can be specified 11 Indicate HW supports the advertising offload 12 The Duration parameter should be used 13 The Timeout parameter should be used 14 The Interval parameters should be used 15 The Tx Power parameter should be used 16 The advertisement will contain a scan response When the connectable flag is set, then the controller will use undirected connectable advertising. The value of the connectable setting can be overwritten this way. This is useful to switch a controller into connectable mode only for LE operation. This is similar to the mode 0x02 from the Set Advertising command. When the connectable flag is not set, then the controller will use advertising based on the connectable setting. When using non-connectable or scannable advertising, the controller will be programmed with a non-resolvable random address. When the system is connectable, then the identity address or resolvable private address will be used. Using the connectable flag is useful for peripheral mode support where BR/EDR (and/or LE) is controlled by Add Device. This allows making the peripheral connectable without having to interfere with the global connectable setting. Secondary channel flags can be used to advertise in secondary channel with the corresponding PHYs. These flag bits are mutually exclusive and setting multiple will result in Invalid Parameter error. Choosing either LE 1M or LE 2M will result in using extended advertising on the primary channel with LE 1M and the respectively LE 1M or LE 2M on the secondary channel. Choosing LE Coded will result in using extended advertising on the primary and secondary channels with LE Coded. Choosing none of these flags will result in legacy advertising. To allow future parameters to be optionally extended in this structure, the flags member has been used to specify which of the structure fields were purposefully set by the caller. Unspecified parameters will be given sensible defaults by the kernel before the advertisement is registered. The Duration parameter configures the length of an Instance. The value is in seconds. The default is 2 seconds. If only one advertising Instance has been added, then the Duration value will be ignored. It only applies for the case where multiple Instances are configured. In that case every Instance will be available for the Duration time and after that it switches to the next one. This is a simple round-robin based approach. The Timeout parameter configures the life-time of an Instance. In case the value 0 is used it indicates no expiration time. If a timeout value is provided, then the advertising Instance will be automatically removed when the timeout passes. The value for the timeout is in seconds. Powering down a controller will invalidate all advertising Instances and it is not possible to add a new Instance with a timeout when the controller is powered down. When a Timeout is provided, then the Duration subtracts from the actual Timeout value of that Instance. For example an Instance with Timeout of 5 and Duration of 2 will be scheduled exactly 3 times, twice with 2 seconds and once with one second. Other Instances have no influence on the Timeout. MinInterval and MaxInterval define the minimum and maximum advertising intervals, with units as number of .625ms advertising slots. The Max interval is expected to be greater than or equal to the Min interval, and both must have values in the range [0x000020, 0xFFFFFF]. If either condition is not met, the registration will fail. The provided Tx Power parameter will only be used if the controller supports it, which can be determined by the presence of the CanSetTxPower member of the Read Advertising Features command. The acceptable range for requested Tx Power is defined in the spec (Version 5.2 | Vol 4, Part E, page 2585) to be [-127, +20] dBm, and the controller will select a power value up to the requested one. The transmission power selected by the controller is not guaranteed to match the requested one, so the reply will contain the power chosen by the controller. If the requested Tx Power is outside the valid range, the registration will fail. When flag bit 16 is enabled, it indicates that the subsequent request to set advertising data will contain a scan response, and that the parameters should set a PDU type that is scannable. Re-adding an already existing instance (i.e. issuing the Add Extended Advertising Parameters command with an Instance identifier of an existing instance) will update that instance's configuration. In this case where no new instance is added, no Advertising Added event will be generated. However, if the update of the instance fails, the instance will be removed, and an Advertising Removed event will be generated. An instance being added or changed while another instance is being advertised will not be visible immediately but only when the new/changed instance is being scheduled by the round robin advertising algorithm. Changes to an instance that is currently being advertised will cancel that instance and switch to the next instance. The changes will be visible the next time the instance is scheduled for advertising. In case a single instance is active, this means that changes will be visible right away. The MaxAdvDataLen return parameter indicates how large the data payload can be in the subsequent Add Extended Advertising Data Command, as it accounts for the data required for the selected flags. Similarly, the MaxScanRspLen return parameter indicates how large the scan response can be. LE must already be enabled, and the controller must be powered, otherwise a "rejected" status will be returned. This command generates a Command Complete event on success or a Command Status event on failure. Possible errors: Failed Rejected Not Supported Invalid Parameters Busy Add Extended Advertising Data Command ===================================== Command Code: 0x0055 Controller Index: Command Parameters: Instance (1 Octet) Advertising Data Length (1 Octet) Scan Response Length (1 Octet) Advertising Data (0-255 Octets) Scan Response (0-255 Octets) Return Parameters: Instance (1 Octet) The Add Extended Advertising Data command is used to update the advertising data of an existing advertising instance known to the kernel. It is expected to be called after an Add Extended Advertising Parameters command, as part of the advertisement registration process. If extended advertising is available, this call will initiate HCI commands to set the instance's advertising data, set scan response data, and then enable the instance. If extended advertising is unavailable, the advertising instance structure maintained in kernel will have its advertising data and scan response updated, and the instance will either be scheduled immediately or left in the queue for later advertisement as part of round-robin advertisement rotation in software. If Scan_Rsp_Len is zero and the flags defined in Add Extended Advertising Parameters command do not have connectable flag set and the global connectable setting is off, then non-connectable advertising is used. If Scan_Rsp_Len is larger than zero and connectable flag is not set and the global advertising is off, then scannable advertising is used. This small difference is supported to provide less air traffic for devices implementing broadcaster role. If the Instance provided does not match a known instance, or if the provided advertising data or scan response are in an unrecognized format, an "Invalid Parameters" status will be returned. If a "Set LE" or Advertising command is still in progress, a "Busy" status will be returned. If the controller is not powered, a "rejected" status will be returned. This command generates a Command Complete event on success or a Command Status event on failure. Possible errors: Failed Rejected Invalid Parameters Busy Add Advertisement Patterns Monitor With RSSI Threshold Command ============================================================== Command Code: 0x0056 Controller Index: Command Parameters: RSSI_Data { High_Threshold (1 Octet) High_Threshold_Timer (2 Octets) Low_Threshold (1 Octet) Low_Threshold_Timer (2 Octets) Sampling_Period (1 Octet) } Pattern_Count (1 Octet) Pattern1 { AD_Type (1 Octet) Offset (1 Octet) Length (1 Octet) Value (31 Octets) } Pattern2 { } ... Return Parameters: Monitor_Handle (2 Octets) This command is essentially the same as Add Advertisement Patterns Monitor Command (0x0052), but with an additional RSSI parameters. As such, if the kernel supports advertisement filtering, then the advertisement data will be filtered in accordance with the set RSSI parameters. Otherwise, it would behave exactly the same as the Add Advertisement Patterns Monitor Command. Devices would be considered "in-range" if the RSSI of the received adv packets are greater than High_Threshold dBm for High_Threshold_Timer seconds. Similarly, devices would be considered lost if no received adv have RSSI greater than Low_Threshold dBm for Low_Threshold_Timer seconds. Only adv packets of "in-range" device would be propagated. The meaning of Sampling_Period is as follows: 0x00 All adv packets from "in-range" devices would be propagated. 0xFF Only the first adv data of "in-range" devices would be propagated. If the device becomes lost, then the first data when it is found again will also be propagated. other Advertisement data would be grouped into 100ms * N time period. Data in the same group will only be reported once, with the RSSI value being averaged out. Possible errors: Failed Busy No Resources Invalid Parameters Set Mesh Receiver Command ========================= Command Code: 0x0057 Controller Index: Command Parameters: Enable (1 Octets) Window (2 Octets) Period (2 Octets) Num AD Types (1 Octets) AD Types { } This command Enables or Disables Mesh Receiving. When enabled passive scanning remains enabled for this controller. The Window/Period values are used to set the Scan Parameters when no other scanning is being done. Num AD Types and AD Types parameter, filter Advertising and Scan responses by AD type. Reponses that do not contain at least one of the requested AD types will be ignored. Otherwise they will be delivered with the Mesh Device Found event. Possible errors: Failed No Resources Invalid Parameters Read Mesh Features Command ========================== Command Code: 0x0058 Controller Index: Command Parameters: Return Parameters: Index (2 Octets) Max Handles (1 Octets) Used Handles (1 Octets) Handle { } This command is used to both verify that Outbound Mesh packet support is enabled, and to indicate the number of packets that can and are simultaneously queued. Index identifies the HCI Controller that this information is valid for. Max Handles indicates the maximum number of packets that may be queued. Used Handles indicates the number of packets awaiting transmission. Handle is an array of the currently outstanding packets. Possible errors: Failed No Resources Invalid Parameters Transmit Mesh Packet Command ============================ Command Code: 0x0059 Controller Index: Command Parameters: Addr (6 octets) Addr Type (1 Octets) Instant (8 Octets) Delay (2 Octets) Count (1 Octets) Data Length (1 Octets) Data (variable) Return Parameters: Handle (1 Octets) This command sends a Mesh Packet as a NONCONN LE Advertisement. The Addr + Addr Type parameters specifify the address to use in the outbound advertising packet. If BD_ADDR_ANY and LE_RANDOM is set, the kernel will create a single use non-resolvable address. The Instant parameter is used in combination with the Delay parameter, to finely time the sending of the Advertising packet. It should be set to the Instant value tag of a received incoming Mesh Device Found Event. It is only useful in POLL-RESPONSE situations where a response must be sent within a negotiated time window. The value of the Instant parameter should not be interpreted by the host, and only has meaning to the controller. The Delay parameter, if 0x0000, will cause the packet to be sent at the earliest opportunity. If non-Zero, and the controller supports delayed delivery, the Instant and Delay parameters will be used to delay the outbound packet. While the Instant is not defined, the Delay is specified in milliseconds. The Count parameter must be sent to a non-Zero value indicating the number of times this packet will be sent before transmission completes. If the Delay parameter is non-Zero, then Count must be 1 only. The Data parameter is an octet array of the AD Type and Mesh Packet. This command will return immediately, and if it succeeds, will generate a Mesh Packet Transmission Complete event when after the packet has been sent. Possible errors: Failed Busy No Resources Invalid Parameters Cancel Transmit Mesh Packet Command =================================== Command Code: 0x005A Controller Index: Command Parameters: Handle (1 Octets) This command may be used to cancel an outbound transmission request. The Handle parameter is the returned handle from a successful Transmit Mesh Packet request. If Zero is specified as the handle, all outstanding send requests are canceled. For each mesh packet canceled, the Mesh Packet Transmission Complete event will be generated, regardless of whether the packet was sent successfully. Possible errors: Failed Invalid Parameters Send HCI command and wait for event Command =========================================== Command Code: 0x005B Controller Index: Command Parameters: Opcode (2 Octets) Event (1 Octet) Timeout (1 Octet) Parameter Length (2 Octets) Parameter (variable) Return Parameters: Response (1-variable Octets) This command may be used to send a HCI command and wait for an (optional) event. The HCI command is specified by the Opcode, any arbitrary is supported including vendor commands, but contrary to the like of Raw/User channel it is run as an HCI command send by the kernel since it uses its command synchronization thus it is possible to wait for a specific event as a response. Setting event to 0x00 will cause the command to wait for either HCI Command Status or HCI Command Complete. Timeout is specified in seconds, setting it to 0 will cause the default timeout to be used. Possible errors: Failed Invalid Parameters Command Complete Event ====================== Event Code: 0x0001 Controller Index: or Event Parameters: Command_Opcode (2 Octets) Status (1 Octet) Return_Parameters This event is an indication that a command has completed. The fixed set of parameters includes the opcode to identify the command that completed as well as a status value to indicate success or failure. The rest of the parameters are command specific and documented in the section for each command separately. Command Status Event ==================== Event Code: 0x0002 Controller Index: or Event Parameters: Command_Opcode (2 Octets) Status (1 Octet) The command status event is used to indicate an early status for a pending command. In the case that the status indicates failure (anything else except success status) this also means that the command has finished executing. Controller Error Event ====================== Event Code: 0x0003 Controller Index: Event Parameters: Error_Code (1 Octet) This event maps straight to the HCI Hardware Error event and is used to indicate something wrong with the controller hardware. Index Added Event ================= Event Code: 0x0004 Controller Index: Event Parameters: This event indicates that a new controller has been added to the system. It is usually followed by a Read Controller Information command. Once the Read Extended Controller Index List command has been used at least once, the Extended Index Added event will be send instead of this one. Index Removed Event =================== Event Code: 0x0005 Controller Index: Event Parameters: This event indicates that a controller has been removed from the system. Once the Read Extended Controller Index List command has been used at least once, the Extended Index Removed event will be send instead of this one. New Settings Event ================== Event Code: 0x0006 Controller Index: Event Parameters: Current_Settings (4 Octets) This event indicates that one or more of the settings for a controller has changed. Class Of Device Changed Event ============================= Event Code: 0x0007 Controller Index: Event Parameters: Class_Of_Device (3 Octets) This event indicates that the Class of Device value for the controller has changed. When the controller is powered off the Class of Device value will always be reported as zero. Local Name Changed Event ======================== Event Code: 0x0008 Controller Index: Event Parameters: Name (249 Octets) Short_Name (11 Octets) This event indicates that the local name of the controller has changed. New Link Key Event ================== Event Code: 0x0009 Controller Index: Event Parameters: Store_Hint (1 Octet) Key { Address (6 Octets) Address_Type (1 Octet) Key_Type (1 Octet) Value (16 Octets) PIN_Length (1 Octet) } This event indicates that a new link key has been generated for a remote device. The Store_Hint parameter indicates whether the host is expected to store the key persistently or not (e.g. this would not be set if the authentication requirement was "No Bonding"). Possible values for the Address_Type parameter: 0 BR/EDR 1 Reserved (not in use) 2 Reserved (not in use) Public and random LE addresses are not valid and will be rejected. Currently defined Key_Type values are: 0x00 Combination key 0x01 Local Unit key 0x02 Remote Unit key 0x03 Debug Combination key 0x04 Unauthenticated Combination key from P-192 0x05 Authenticated Combination key from P-192 0x06 Changed Combination key 0x07 Unauthenticated Combination key from P-256 0x08 Authenticated Combination key from P-256 Receiving this event indicates that a pairing procedure has been completed. New Long Term Key Event ======================= Event Code: 0x000A Controller Index: Event Parameters: Store_Hint (1 Octet) Key { Address (6 Octets) Address_Type (1 Octet) Key_Type (1 Octet) Central (1 Octet) Encryption Size (1 Octet) Enc. Diversifier (2 Octets) Random Number (8 Octets) Value (16 Octets) } This event indicates that a new long term key has been generated for a remote device. The Store_Hint parameter indicates whether the host is expected to store the key persistently or not (e.g. this would not be set if the authentication requirement was "No Bonding"). Possible values for the Address_Type parameter: 0 Reserved (not in use) 1 LE Public 2 LE Random The provided Address and Address_Type are the identity of a device. So either its public address or static random address. For unresolvable random addresses and resolvable random addresses without identity information and identity resolving key, the Store_Hint will be set to not store the long term key. Currently defined Key_Type values are: 0x00 Unauthenticated legacy key 0x01 Authenticated legacy key 0x02 Unauthenticated key from P-256 0x03 Authenticated key from P-256 0x04 Debug key from P-256 Receiving this event indicates that a pairing procedure has been completed. Device Connected Event ====================== Event Code: 0x000B Controller Index: Event Parameters: Address (6 Octets) Address_Type (1 Octet) Flags (4 Octets) EIR_Data_Length (2 Octets) EIR_Data (0-65535 Octets) This event indicates that a successful baseband connection has been created to the remote device. Possible values for the Address_Type parameter: 0 BR/EDR 1 LE Public 2 LE Random For devices using resolvable random addresses with a known identity resolving key, the Address and Address_Type will contain the identity information. It is possible that devices get connected via its resolvable random address and after New Identity Resolving Key event start using its identity. The following bits are defined for the Flags parameter: 0 Reserved (not in use) 1 Legacy Pairing 2 Reserved (not in use) 3 Initiated Connection 4 Reserved (not in use) Device Disconnected Event ========================= Event Code: 0x000C Controller Index: Event Parameters: Address (6 Octets) Address_Type (1 Octet) Reason (1 Octet) This event indicates that the baseband connection was lost to a remote device. Possible values for the Address_Type parameter: 0 BR/EDR 1 LE Public 2 LE Random For devices using resolvable random addresses with a known identity resolving key, the Address and Address_Type will contain the identity information. Possible values for the Reason parameter: 0 Unspecified 1 Connection timeout 2 Connection terminated by local host 3 Connection terminated by remote host 4 Connection terminated due to authentication failure 5 Connection terminated by local host for suspend Note that the local/remote distinction just determines which side terminated the low-level connection, regardless of the disconnection of the higher-level profiles. This can sometimes be misleading and thus must be used with care. For example, some hardware combinations would report a locally initiated disconnection even if the user turned Bluetooth off in the remote side. Connect Failed Event ==================== Event Code: 0x000D Controller Index: Event Parameters: Address (6 Octets) Address_Type (1 Octet) Status (1 Octet) This event indicates that a connection attempt failed to a remote device. Possible values for the Address_Type parameter: 0 BR/EDR 1 LE Public 2 LE Random For devices using resolvable random addresses with a known identity resolving key, the Address and Address_Type will contain the identity information. PIN Code Request Event ====================== Event Code: 0x000E Controller Index: Event Parameters: Address (6 Octets) Address_Type (1 Octet) Secure (1 Octet) This event is used to request a PIN Code reply from user space. The reply should either be returned using the PIN Code Reply or the PIN Code Negative Reply command. Possible values for the Address_Type parameter: 0 BR/EDR 1 LE Public 2 LE Random Secure: 0x01 secure PIN code required 0x00 secure PIN code not required User Confirmation Request Event =============================== Event Code: 0x000F Controller Index: Event Parameters: Address (6 Octets) Address_Type (1 Octet) Confirm_Hint (1 Octet) Value (4 Octets) This event is used to request a user confirmation request from user space. Possible values for the Address_Type parameter: 0 BR/EDR 1 LE Public 2 LE Random If the Confirm_Hint parameter value is 0x01 this means that a simple "Yes/No" confirmation should be presented to the user instead of a full numerical confirmation (in which case the parameter value will be 0x00). User space should respond to this command either using the User Confirmation Reply or the User Confirmation Negative Reply command. User Passkey Request Event ========================== Event Code: 0x0010 Controller Index: Event Parameters: Address (6 Octets) Address_Type (1 Octet) This event is used to request a passkey from user space. The response to this event should either be the User Passkey Reply command or the User Passkey Negative Reply command. Possible values for the Address_Type parameter: 0 BR/EDR 1 LE Public 2 LE Random Authentication Failed Event =========================== Event Code: 0x0011 Controller Index: Event Parameters: Address (6 Octets) Address_Type (1 Octet) Status (1 Octet) This event indicates that there was an authentication failure with a remote device. Possible values for the Address_Type parameter: 0 BR/EDR 1 LE Public 2 LE Random Device Found Event ================== Event Code: 0x0012 Controller Index: Event Parameters: Address (6 Octets) Address_Type (1 Octet) RSSI (1 Octet) Flags (4 Octets) EIR_Data_Length (2 Octets) EIR_Data (0-65535 Octets) This event indicates that a device was found during device discovery. Possible values for the Address_Type parameter: 0 BR/EDR 1 LE Public 2 LE Random The following bits are defined for the Flags parameter: 0 Confirm name 1 Legacy Pairing 2 Not Connectable 3 Reserved (not in use) 4 Name Request Failed 5 Scan Response For the RSSI field a value of 127 indicates that the RSSI is not available. That can happen with Bluetooth 1.1 and earlier controllers or with bad radio conditions. The Confirm name flag indicates that the kernel wants to know whether user space knows the name for this device or not. If this flag is set user space should respond to it using the Confirm Name command. The Legacy Pairing flag indicates that Legacy Pairing is likely to occur when pairing with this device. An application could use this information to optimize the pairing process by locally pre-generating a PIN code and thereby eliminate the risk of local input timeout when pairing. Note that there is a risk of false-positives for this flag so user space should be able to handle getting something else as a PIN Request when pairing. The Not Connectable flag indicates that the device will not accept any connections. This can be indicated by Low Energy devices that are in broadcaster role. The Name Request Failed flag indicates that name resolving procedure has ended with failure for this device. The user space should use this information to determine when is a good time to retry the name resolving procedure. Discovering Event ================= Event Code: 0x0013 Controller Index: Event Parameters: Address_Type (1 Octet) Discovering (1 Octet) This event indicates that the controller has started discovering devices. This discovering state can come and go multiple times between a Start Discovery and a Stop Discovery commands. The Start Service Discovery command will also trigger this event. The valid values for the Discovering parameter are 0x01 (enabled) and 0x00 (disabled). Device Blocked Event ==================== Event Code: 0x0014 Controller Index: Event Parameters: Address (6 Octets) Address_Type (1 Octet) This event indicates that a device has been blocked using the Block Device command. Possible values for the Address_Type parameter: 0 BR/EDR 1 LE Public 2 LE Random The event will only be sent to Management sockets other than the one through which the command was sent. Device Unblocked Event ====================== Event Code: 0x0015 Controller Index: Event Parameters: Address (6 Octets) Address_Type (1 Octet) This event indicates that a device has been unblocked using the Unblock Device command. Possible values for the Address_Type parameter: 0 BR/EDR 1 LE Public 2 LE Random The event will only be sent to Management sockets other than the one through which the command was sent. Device Unpaired Event ===================== Event Code: 0x0016 Controller Index: Event Parameters: Address (6 Octets) Address_Type (1 Octet) This event indicates that a device has been unpaired (i.e. all its keys have been removed from the kernel) using the Unpair Device command. Possible values for the Address_Type parameter: 0 BR/EDR 1 LE Public 2 LE Random For devices using resolvable random addresses with a known identity resolving key, the event parameters will contain the identity. After receiving this event, the device will become essentially private again. The event will only be sent to Management sockets other than the one through which the Unpair Device command was sent. Passkey Notify Event ==================== Event Code: 0x0017 Controller Index: Event Parameters: Address (6 Octets) Address_Type (1 Octet) Passkey (4 Octets) Entered (1 Octet) This event is used to request passkey notification to the user. Unlike the other authentication events it does not need responding to using any Management command. Possible values for the Address_Type parameter: 0 BR/EDR 1 LE Public 2 LE Random The Passkey parameter indicates the passkey to be shown to the user whereas the Entered parameter indicates how many characters the user has entered on the remote side. New Identity Resolving Key Event ================================ Event Code: 0x0018 Controller Index: Event Parameters: Store_Hint (1 Octet) Random_Address (6 Octets) Key { Address (6 Octets) Address_Type (1 Octet) Value (16 Octets) } This event indicates that a new identity resolving key has been generated for a remote device. The Store_Hint parameter indicates whether the host is expected to store the key persistently or not. The Random_Address provides the resolvable random address that was resolved into an identity. A value of 00:00:00:00:00:00 indicates that the identity resolving key was provided for a public address or static random address. Once this event has been send for a resolvable random address, all further events mapping this device will send out using the identity address information. This event also indicates that now the identity address should be used for commands instead of the resolvable random address. It is possible that some devices allow discovering via its identity address, but after pairing using resolvable private address only. In such a case Store_Hint will be 0x00 and the Random_Address will indicate 00:00:00:00:00:00. For these devices, the Privacy Characteristic of the remote GATT database should be consulted to decide if the identity resolving key must be stored persistently or not. Devices using Set Privacy command with the option 0x02 would be such type of device. Possible values for the Address_Type parameter: 0 Reserved (not in use) 1 LE Public 2 LE Random The provided Address and Address_Type are the identity of a device. So either its public address or static random address. New Signature Resolving Key Event ================================= Event Code: 0x0019 Controller Index: Event Parameters: Store_Hint (1 Octet) Key { Address (6 Octets) Address_Type (1 Octet) Type (1 Octet) Value (16 Octets) } This event indicates that a new signature resolving key has been generated for either the central or peripheral device. The Store_Hint parameter indicates whether the host is expected to store the key persistently or not. The Type parameter has the following possible values: 0x00 Unauthenticated local CSRK 0x01 Unauthenticated remote CSRK 0x02 Authenticated local CSRK 0x03 Authenticated remote CSRK The local keys are used for signing data to be sent to the remote device, whereas the remote keys are used to verify signatures received from the remote device. The local signature resolving key will be generated with each pairing request. Only after receiving this event with the Type indicating a local key is it possible to use ATT Signed Write procedures. Possible values for the Address_Type parameter: 0 Reserved (not in use) 1 LE Public 2 LE Random The provided Address and Address_Type are the identity of a device. So either its public address or static random address. Device Added Event ================== Event Code: 0x001a Controller Index: Event Parameters: Address (6 Octets) Address_Type (1 Octet) Action (1 Octet) This event indicates that a device has been added using the Add Device command. Possible values for the Address_Type parameter: 0 BR/EDR 1 LE Public 2 LE Random The event will only be sent to management sockets other than the one through which the command was sent. Device Removed Event ==================== Event Code: 0x001b Controller Index: Event Parameters: Address (6 Octets) Address_Type (1 Octet) This event indicates that a device has been removed using the Remove Device command. Possible values for the Address_Type parameter: 0 BR/EDR 1 LE Public 2 LE Random The event will only be sent to management sockets other than the one through which the command was sent. New Connection Parameter Event ============================== Event Code: 0x001c Controller Index: Event Parameters: Store_Hint (1 Octet) Param { Address (6 Octets) Address_Type (1 Octet) Min_Connection_Interval (2 Octets) Max_Connection_Interval (2 Octets) Connection_Latency (2 Octets) Supervision_Timeout (2 Octets) } This event indicates a new set of connection parameters from a peripheral device. The Store_Hint parameter indicates whether the host is expected to store this information persistently or not. Possible values for the Address_Type parameter: 0 Reserved (not in use) 1 LE Public 2 LE Random The Min_Connection_Interval, Max_Connection_Interval, Connection_Latency and Supervision_Timeout parameters are encoded as described in Core 4.1 spec, Vol 2, 7.7.65.3. Unconfigured Index Added Event ============================== Event Code: 0x001d Controller Index: Event Parameters: This event indicates that a new unconfigured controller has been added to the system. It is usually followed by a Read Controller Configuration Information command. Only when a controller requires further configuration, it will be announced with this event. If it supports configuration, but does not require it, then an Index Added event will be used. Once the Read Extended Controller Index List command has been used at least once, the Extended Index Added event will be send instead of this one. Unconfigured Index Removed Event ================================ Event Code: 0x001e Controller Index: Event Parameters: This event indicates that an unconfigured controller has been removed from the system. Once the Read Extended Controller Index List command has been used at least once, the Extended Index Removed event will be send instead of this one. New Configuration Options Event =============================== Event Code: 0x001f Controller Index: Event Parameters: Missing_Options (4 Octets) This event indicates that one or more of the options for the controller configuration has changed. Extended Index Added Event ========================== Event Code: 0x0020 Controller Index: Event Parameters: Controller_Type (1 Octet) Controller_Bus (1 Octet) This event indicates that a new controller index has been added to the system. This event will only be used after Read Extended Controller Index List has been used at least once. If it has not been used, then Index Added and Unconfigured Index Added are sent instead. Extended Index Removed Event ============================ Event Code: 0x0021 Controller Index: Event Parameters: Controller_Type (1 Octet) Controller_Bus (1 Octet) This event indicates that an existing controller index has been removed from the system. This event will only be used after Read Extended Controller Index List has been used at least once. If it has not been used, then Index Removed and Unconfigured Index Removed are sent instead. Local Out Of Band Extended Data Updated Event ============================================= Event Code: 0x0022 Controller Index: Event Parameters: Address_Type (1 Octet) EIR_Data_Length (2 Octets) EIR_Data (0-65535 Octets) This event is used when the Read Local Out Of Band Extended Data command has been used and some other user requested a new set of local out-of-band data. This allows for the original caller to adjust the data. Possible values for the Address_Type parameter are a bit-wise or of the following bits: 0 BR/EDR 1 LE Public 2 LE Random By combining these e.g. the following values are possible: 1 BR/EDR 6 LE (public & random) 7 Reserved (not in use) The value for EIR_Data_Length and content for EIR_Data is the same as described in Read Local Out Of Band Extended Data command. When LE Privacy is used and LE Secure Connections out-of-band data has been requested, then this event will be emitted every time the Resolvable Private Address (RPA) gets changed. The new RPA will be included in the EIR_Data. The event will only be sent to management sockets other than the one through which the command was sent. It will additionally also only be sent to sockets that have used the command at least once. Advertising Added Event ======================= Event Code: 0x0023 Controller Index: Event Parameters: Instance (1 Octet) This event indicates that an advertising instance has been added using the Add Advertising command. The event will only be sent to management sockets other than the one through which the command was sent. Advertising Removed Event ========================= Event Code: 0x0024 Controller Index: Event Parameters: Instance (1 Octet) This event indicates that an advertising instance has been removed using the Remove Advertising command. The event will only be sent to management sockets other than the one through which the command was sent. Extended Controller Information Changed Event ============================================= Event Code: 0x0025 Controller Index: Event Parameters: EIR_Data_Length (2 Octets) EIR_Data (0-65535 Octets) This event indicates that controller information has been updated and new values are used. This includes the local name, class of device, device id and LE address information. This event will only be used after Read Extended Controller Information command has been used at least once. If it has not been used the legacy events are used. The event will only be sent to management sockets other than the one through which the change was triggered. PHY Configuration Changed Event =============================== Event Code: 0x0026 Controller Index: Event Parameters: Selected_PHYs (4 Octets) This event indicates that default PHYs have been updated. This event will only be used after Set PHY Configuration command has been used at least once. The event will only be sent to management sockets other than the one through which the change was triggered. Refer Get PHY Configuration command for PHYs parameter. Experimental Feature Changed Event ================================== Event Code: 0x0027 Controller Index: Event Parameters: UUID (16 Octets) Flags (4 Octets) This event indicates that the status of an experimental feature has been changed. The event will only be sent to management sockets other than the one through which the change was triggered. Refer to Set Experimental Feature command for the Flags parameter. Default System Configuration Changed Event ========================================== Event Code: 0x0028 Controller Index: Event Parameters: Parameter1 { Parameter_Type (2 Octet) Value_Length (1 Octet) Value (0-255 Octets) } Parameter2 { } ... This event indicates the change of default system parameter values. The event will only be sent to management sockets other than the one through which the change was trigged. In addition it will only be sent to sockets that have issues the Read Default System Configuration command. Refer to Read Default System configuration command for the supported Parameter_Type values. Default Runtime Configuration Changed Event =========================================== Event Code: 0x0029 Controller Index: Event Parameters: Parameter1 { Parameter_Type (2 Octet) Value_Length (1 Octet) Value (0-255 Octets) } Parameter2 { } ... This event indicates the change of default runtime parameter values. The event will only be sent to management sockets other than the one through which the change was trigged. In addition it will only be sent to sockets that have issues the Read Default Runtime Configuration command. Refer to Read Default Runtime configuration command for the supported Parameter_Type values. Device Flags Changed Event ========================== Event Code: 0x002a Controller Index: Event Parameters: Address (6 Octets) Address_Type (1 Octet) Supported_Flags (4 Octets) Current_Flags (4 Octets) This event indicates that the device flags have been changed via the Set Device Flags command or that a new device has been added via the Add Device command. In the latter case it is send right after the Device Added event. Possible values for the Address_Type parameter: 0 BR/EDR 1 LE Public 2 LE Random The event will only be sent to management sockets other than the one through which the command was sent. In case this event is triggered by Add Device then it is sent to all management sockets. Advertisement Monitor Added Event ================================= Event Code: 0x002b Controller Index: Event Parameters: Monitor_Handle (2 Octets) This event indicates that an advertisement monitor has been added using the Add Advertisement Patterns Monitor command. The event will only be sent to management sockets other than the one through which the command was sent. Advertisement Monitor Removed Event =================================== Event Code: 0x002c Controller Index: Event Parameters: Monitor_Handle (2 Octets) This event indicates that an advertisement monitor has been removed using the Remove Advertisement Monitor command. The event will only be sent to management sockets other than the one through which the command was sent. Controller Suspend Event ======================== Event code: 0x002d Controller Index: Event Parameters: Suspend_State (1 octet) This event indicates that the controller is suspended for host suspend. Possible values for the Suspend_State parameter: 0 Running (not disconnected) 1 Disconnected and not scanning 2 Page scanning and/or passive scanning. The value 0 is used for the running state and may be sent if the controller could not be configured to suspend properly. This event will be sent to all management sockets. Controller Resume Event ======================= Event code: 0x002e Controller Index: Event Parameters: Wake_Reason (1 octet) Address (6 octets) Address_Type (1 octet) This event indicates that the controller has resumed from suspend. Possible values for the Wake_Reason parameter: 0 Resume from non-Bluetooth wake source 1 Wake due to unexpected event 2 Remote wake due to peer device connection Currently, we expect that only peer reconnections should wake us from the suspended state. Any other events that occurred while the system should have been suspended results in wake due to unexpected event. If the Wake_Reason is Remote wake due to connection, the address of the peer device that caused the event will be shared in Address and Address_Type. Otherwise, Address and Address_Type will both be zero. This event will be sent to all management sockets. Advertisement Monitor Device Found Event ======================================== Event code: 0x002f Controller Index: Event Parameters: Monitor_Handle (2 Octets) Address (6 Octets) Address_Type (1 Octet) RSSI (1 Octet) Flags (4 Octets) AD_Data_Length (2 Octets) AD_Data (0-65535 Octets) This event indicates that the controller has started tracking a device matching an Advertisement Monitor with handle Monitor_Handle. Monitor_Handle 0 indicates that we are not active scanning and this is a subsequent advertisement report for already matched Advertisement Monitor or the controller offloading support is not available so need to report all advertisements for software based filtering. The address of the device being tracked will be shared in Address and Address_Type. Possible values for the Address_Type parameter: 0 Reserved (not in use) 1 LE Public 2 LE Random For the RSSI field a value of 127 indicates that the RSSI is not available. That can happen with Bluetooth 1.1 and earlier controllers or with bad radio conditions. This event will be sent to all management sockets. Advertisement Monitor Device Lost Event ======================================= Event code: 0x0030 Controller Index: Event Parameters: Monitor_Handle (2 Octets) Address (6 Octets) Address_Type (1 Octet) This event indicates that the controller has stopped tracking a device that was being tracked by an Advertisement Monitor with the handle Monitor_Handle. The address of the device being tracked will be shared in Address and Address_Type. Possible values for the Address_Type parameter: 0 Reserved (not in use) 1 LE Public 2 LE Random This event will be sent to all management sockets. Mesh Device Found Event ======================= Event code: 0x0031 Controller Index: Event Parameters: Address (6 Octets) Address_Type (1 Octet) RSSI (1 Octet) Instant (8 Octets) Flags (4 Octets) AD_Data_Length (2 Octets) AD_Data (0-65535 Octets) This event indicates that the controller has received an Advertisement or Scan Result containing an AD Type matching the Mesh scan set. The address of the sending device is returned, and must be a valid LE Address_Type. Possible values for the Address_Type parameter: 0 Reserved (not in use) 1 LE Public 2 LE Random The RSSI field is a signed octet, and is the RSSI reported by the receiving controller. The Instant field is 64 bit value that represents the instant in time the packet was received. It's value is not intended to be interpretted by the host, and is only useful if the host wants to make a timed response to the received packet. (i.e. a Poll/Response) AD_Length and AD_Data contains the Info structure of Advertising and Scan rsults. To receive this event, AD filters must be requested with the Set Mesh Receiver command command, specifying which AD Types to return. All AD structures will be received in this event if any of the filtered AD Types are present. This event will be sent to all management sockets. Mesh Packet Transmit Complete Event =================================== Event code: 0x0032 Controller Index: Event Parameters: Handle (1 Octets) This event indicates that a requested outbound Mesh packet has completed and no longer occupies a transmit slot. This event will be sent to all management sockets. bluez-5.82/doc/PaxHeaders/org.bluez.obex.ObjectPush.50000644000000000000000000000005014773213475017426 xustar0020 atime=1743591229 20 ctime=1743591291 bluez-5.82/doc/org.bluez.obex.ObjectPush.50000644000000000000000000000630014773213475017106 0ustar00rootroot.\" Man page generated from reStructuredText. . . .nr rst2man-indent-level 0 . .de1 rstReportMargin \\$1 \\n[an-margin] level \\n[rst2man-indent-level] level margin: \\n[rst2man-indent\\n[rst2man-indent-level]] - \\n[rst2man-indent0] \\n[rst2man-indent1] \\n[rst2man-indent2] .. .de1 INDENT .\" .rstReportMargin pre: . RS \\$1 . nr rst2man-indent\\n[rst2man-indent-level] \\n[an-margin] . nr rst2man-indent-level +1 .\" .rstReportMargin post: .. .de UNINDENT . RE .\" indent \\n[an-margin] .\" old: \\n[rst2man-indent\\n[rst2man-indent-level]] .nr rst2man-indent-level -1 .\" new: \\n[rst2man-indent\\n[rst2man-indent-level]] .in \\n[rst2man-indent\\n[rst2man-indent-level]]u .. .TH "ORG.BLUEZ.OBEX.OBJECTPUSH" "5" "October 2023" "BlueZ" "Linux System Administration" .SH NAME org.bluez.obex.ObjectPush \- BlueZ D-Bus OBEX ObjectPush API documentation .SH INTERFACE .INDENT 0.0 .TP .B Service org.bluez.obex .TP .B Interface org.bluez.obex.ObjectPush1 .TP .B Object path [Session object path] .UNINDENT .SS Methods .SS object, dict SendFile(string sourcefile) .INDENT 0.0 .INDENT 3.5 Sends local file to the remote device. .sp The returned path represents the newly created transfer, which should be used to find out if the content has been successfully transferred or if the operation fails. .sp The properties of this transfer are also returned along with the object path, to avoid a call to GetProperties, see \fBorg.bluez.obex.Transfer(5)\fP for the possible list of properties. .sp Possible errors: .INDENT 0.0 .TP .B org.bluez.obex.Error.InvalidArguments .TP .B org.bluez.obex.Error.Failed .UNINDENT .UNINDENT .UNINDENT .SS object, dict PullBusinessCard(string targetfile) .INDENT 0.0 .INDENT 3.5 Request the business card from a remote device and store it in the local file. .sp If an empty target file is given, a name will be automatically generated for the temporary file. .sp The returned path represents the newly created transfer, which should be used to find out if the content has been successfully transferred or if the operation fails. .sp The properties of this transfer are also returned along with the object path, to avoid a call to GetProperties, see \fBorg.bluez.obex.Transfer(5)\fP for the possible list of properties. .sp Possible errors: .INDENT 0.0 .TP .B org.bluez.obex.Error.InvalidArguments .TP .B org.bluez.obex.Error.Failed .UNINDENT .UNINDENT .UNINDENT .SS object, dict ExchangeBusinessCards(string clientfile, string targetfile) .INDENT 0.0 .INDENT 3.5 Push the client\(aqs business card to the remote device and then retrieve the remote business card and store it in a local file. .sp If an empty target file is given, a name will be automatically generated for the temporary file. .sp The returned path represents the newly created transfer, which should be used to find out if the content has been successfully transferred or if the operation fails. .sp The properties of this transfer are also returned along with the object path, to avoid a call to GetProperties, see \fBorg.bluez.obex.Transfer(5)\fP for the possible list of properties. .sp Possible errors: .INDENT 0.0 .TP .B org.bluez.obex.Error.InvalidArguments .TP .B org.bluez.obex.Error.Failed .UNINDENT .UNINDENT .UNINDENT .\" Generated by docutils manpage writer. . bluez-5.82/doc/PaxHeaders/org.bluez.obex.PhonebookAccess.rst0000644000000000000000000000005014536422313021060 xustar0020 atime=1743516819 20 ctime=1743591290 bluez-5.82/doc/org.bluez.obex.PhonebookAccess.rst0000644000000000000000000001641014536422313020543 0ustar00rootroot============================== org.bluez.obex.PhonebookAccess ============================== -------------------------------------------------- BlueZ D-Bus OBEX PhonebookAccess API documentation -------------------------------------------------- :Version: BlueZ :Date: October 2023 :Manual section: 5 :Manual group: Linux System Administration Interface ========= :Service: org.bluez.obex :Interface: org.bluez.obex.PhonebookAccess1 :Object path: [Session object path] Methods ------- void Select(string location, string phonebook) `````````````````````````````````````````````` Selects the phonebook object for other operations. Should be call before all the other operations. Possible location values: :"int", "internal" (default): Store in the Internal memory. :"sim{#}": Store in the sim number. Possible phonebook values: :"pb": Store as contact. :"ich": Store as incoming call. :"och": Store as outgoing call. :"mch": Store as missing call. :"cch": Store as a combination of incoming, outgoing and missing call. "spd": Store as speed dials entry ( only for "internal" ) "fav": Store as favorites entry ( only for "internal" ) Possible errors: :org.bluez.obex.Error.InvalidArguments: :org.bluez.obex.Error.Failed: object, dict PullAll(string targetfile, dict filters) ````````````````````````````````````````````````````` Returns the entire phonebook object from the PSE server in plain string with vcard format, and store it in a local file. If an empty target file is given, a name will be automatically generated for the temporary file. The returned path represents the newly created transfer, which should be used to find out if the content has been successfully transferred or if the operation fails. The properties of this transfer are also returned along with the object path, to avoid a call to GetProperties, see **org.bluez.obex.Transfer(5)** for the possible list of properties. Possible filters: :string Format: Items vcard format. Possible values: :"vcard21" (default): :"vcard30": :string Order: Items order. Possible values: :"": :"indexed": :"alphanumeric": :"phonetic": :uint16 Offset (default 0): Offset of the first item. :uint16 MaxCount (default 65535): Maximum number of items. :array{string} Fields (default all fields): Item vcard fields. See **ListFilterFields()** for possible values. :array{string} FilterAll: Filter items by fields using AND logic, cannot be used together with **FilterAny**. See **ListFilterFields()** for possible values. :array{string} FilterAny: Filter items by fields using OR logic, cannot be used together with **FilterAll**. See **ListFilterFields()** for possible values. :bool ResetNewMissedCalls: Reset new the missed calls items, shall only be used for folders mch and cch. Possible errors: :org.bluez.obex.Error.InvalidArguments: :org.bluez.obex.Forbidden: array{string vcard, string name} List(dict filters) ``````````````````````````````````````````````````` Returns array of vcard-listing data where every entry consists of a pair of strings containing the vcard handle and the contact name. For example: :"1.vcf": "John" Possible filters: :string Order: Contact order. Possible values: :"": :"indexed": :"alphanumeric": :"phonetic": :uint16 Offset: Start offset. :uint16 MaxCount: Maximum number of contacts. Possible errors: :org.bluez.obex.Error.InvalidArguments: :org.bluez.obex.Forbidden: object, dict Pull(string vcard, string targetfile, dict filters) ```````````````````````````````````````````````````````````````` Retrieves the vcard in the current phonebook object and store it in a local file. If an empty target file is given, a name will be automatically generated for the temporary file. The returned path represents the newly created transfer, which should be used to find out if the content has been successfully transferred or if the operation fails. The properties of this transfer are also returned along with the object path, to avoid a call to GetProperties, see **org.bluez.obex.Transfer(5)** for the possible list of properties. Possible filters: :string Format: Contact data format. Possible values: :"": :"vcard21": :"vcard30": :array{string} Fields: See **ListFilterFields()** for possible values. Possible errors: :org.bluez.obex.Error.InvalidArguments: :org.bluez.obex.Error.Forbidden: :org.bluez.obex.Error.Failed: array{string vcard, string name} Search(string field, string value, dict filters) ````````````````````````````````````````````````````````````````````````````````` Searches for entries matching the given condition and return an array of vcard-listing data where every entry consists of a pair of strings containing the vcard handle and the contact name. Possible field values: :"name" (default): Search by name. :"number": Search by number. :"sound": Search by sound. value: the string value to search for Possible filters: :string Order: Contact order. Possible values: :"": :"indexed": :"alphanumeric": :"phonetic": :uint16 Offset: Start offset. :uint16 MaxCount: Maximum number of contacts. Possible errors: :org.bluez.obex.Error.InvalidArguments: :org.bluez.obex.Error.Forbidden: :org.bluez.obex.Error.Failed: uint16 GetSize() ```````````````` Returns the number of entries in the selected phonebook object that are actually used (i.e. indexes that correspond to non-NULL entries). Possible errors: :org.bluez.obex.Error.Forbidden: :org.bluez.obex.Error.Failed: void UpdateVersion() ```````````````````` Attempts to update PrimaryCounter and SecondaryCounter. Possible errors: :org.bluez.obex.Error.NotSupported: :org.bluez.obex.Error.Forbidden: :org.bluez.obex.Error.Failed: array{string} ListFilterFields() ```````````````````````````````` Returns all Available fields that can be used in Fields filter. Possible return: :"VERSION": :"FN": :"N": :"PHOTO": :"BDAY": :"ADR": :"LABEL": :"TEL": :"EMAIL": :"MAILER": :"TZ": :"GEO": :"TITLE": :"ROLE": :"LOGO": :"AGENT": :"ORG": :"NOTE": :"REV": :"SOUND": :"URL": :"UID": :"KEY": :"NICKNAME": :"CATEGORIES": :"PROID": :"CLASS": :"SORT-STRING": :"X-IRMC-CALL-DATETIME": :"X-BT-SPEEDDIALKEY": :"X-BT-UCI": :"X-BT-UID": :"BIT-{#}": Possible errors: None Properties ---------- string Folder [readonly] ```````````````````````` Current folder. string DatabaseIdentifier [readonly, optional] `````````````````````````````````````````````` 128 bits persistent database identifier. Possible values: 32-character hexadecimal such as A1A2A3A4B1B2C1C2D1D2E1E2E3E4E5E6 string PrimaryCounter [readonly, optional] `````````````````````````````````````````` 128 bits primary version counter. Possible values: 32-character hexadecimal such as A1A2A3A4B1B2C1C2D1D2E1E2E3E4E5E6 string SecondaryCounter [readonly, optional] ```````````````````````````````````````````` 128 bits secondary version counter. Possible values: 32-character hexadecimal such as A1A2A3A4B1B2C1C2D1D2E1E2E3E4E5E6 bool FixedImageSize [readonly, optional] ```````````````````````````````````````` Indicate support for fixed image size. Possible values: True if image is JPEG 300x300 pixels otherwise False. bluez-5.82/doc/PaxHeaders/org.bluez.BatteryProviderManager.50000644000000000000000000000005014773213441021035 xustar0020 atime=1743591201 20 ctime=1743591291 bluez-5.82/doc/org.bluez.BatteryProviderManager.50000644000000000000000000000430514773213441020520 0ustar00rootroot.\" Man page generated from reStructuredText. . . .nr rst2man-indent-level 0 . .de1 rstReportMargin \\$1 \\n[an-margin] level \\n[rst2man-indent-level] level margin: \\n[rst2man-indent\\n[rst2man-indent-level]] - \\n[rst2man-indent0] \\n[rst2man-indent1] \\n[rst2man-indent2] .. .de1 INDENT .\" .rstReportMargin pre: . RS \\$1 . nr rst2man-indent\\n[rst2man-indent-level] \\n[an-margin] . nr rst2man-indent-level +1 .\" .rstReportMargin post: .. .de UNINDENT . RE .\" indent \\n[an-margin] .\" old: \\n[rst2man-indent\\n[rst2man-indent-level]] .nr rst2man-indent-level -1 .\" new: \\n[rst2man-indent\\n[rst2man-indent-level]] .in \\n[rst2man-indent\\n[rst2man-indent-level]]u .. .TH "ORG.BLUEZ.BATTERYPROVIDERMANAGER" "5" "October 2023" "BlueZ" "Linux System Administration" .SH NAME org.bluez.BatteryProviderManager \- BlueZ D-Bus BatteryProviderManager API documentation .SH DESCRIPTION .sp A battery provider starts by registering itself as a battery provider with the \fBRegisterBatteryProvider()\fP method passing an object path as the provider ID. Then, it can start exposing \fBorg.bluez.BatteryProvider(5)\fP objects having the path starting with the given provider ID. It can also remove objects at any time. The objects and their properties exposed by battery providers will be reflected on \fBorg.bluez.Battery(5)\fP interface. .sp \fBbluetoothd(8)\fP will stop monitoring these exposed and removed objects after UnregisterBatteryProvider is called for that provider ID. .SH INTERFACE .INDENT 0.0 .TP .B Service org.bluez .TP .B Interface org.bluez.BatteryProviderManager1 .TP .B Object path /org/bluez/{hci0,hci1,...} .UNINDENT .SS Methods .SS void RegisterBatteryProvider(object provider) .INDENT 0.0 .INDENT 3.5 Registers a battery provider. A registered battery provider can then expose objects with \fBorg.bluez.BatteryProvider(5)\fP interface. .UNINDENT .UNINDENT .SS void UnregisterBatteryProvider(object provider) .INDENT 0.0 .INDENT 3.5 Unregisters a battery provider previously registered with \fBRegisterBatteryProvider()\fP\&. After unregistration, the \fBorg.bluez.BatteryProvider(5)\fP objects provided by this client are ignored by \fBbluetoothd(8)\fP\&. .UNINDENT .UNINDENT .\" Generated by docutils manpage writer. . bluez-5.82/doc/PaxHeaders/org.bluez.MediaItem.rst0000644000000000000000000000005014536422313016714 xustar0020 atime=1743516797 20 ctime=1743591290 bluez-5.82/doc/org.bluez.MediaItem.rst0000644000000000000000000000426114536422313016400 0ustar00rootroot=================== org.bluez.MediaItem =================== --------------------------------------- BlueZ D-Bus MediaItem API documentation --------------------------------------- :Version: BlueZ :Date: September 2023 :Manual section: 5 :Manual group: Linux System Administration Interface ========= :Service: unique name (Target role) org.bluez (Controller role) :Interface: org.bluez.MediaItem1 :Object path: freely definable (Target role) [variable prefix]/{hci0,hci1,...}/dev_XX_XX_XX_XX_XX_XX/playerX/itemX (Controller role) Methods ------- void Play() ``````````` Play item Possible Errors: :org.bluez.Error.NotSupported: :org.bluez.Error.Failed: void AddtoNowPlaying() `````````````````````` Add item to now playing list Possible Errors: :org.bluez.Error.NotSupported: :org.bluez.Error.Failed: Properties ---------- object Player [readonly] ```````````````````````` Player object path the item belongs to string Name [readonly] `````````````````````` Item displayable name string Type [readonly] `````````````````````` Item type Possible values: "video", "audio", "folder" string FolderType [readonly, optional] `````````````````````````````````````` Folder type. Possible values: "mixed", "titles", "albums", "artists" Available if property Type is "Folder" boolean Playable [readonly, optional] ````````````````````````````````````` Indicates if the item can be played Available if property Type is "folder" dict Metadata [readonly] ```````````````````````` Item metadata. Possible values: :string Title: Item title name Available if property Type is "audio" or "video" :string Artist: Item artist name Available if property Type is "audio" or "video" :string Album: Item album name Available if property Type is "audio" or "video" :string Genre: Item genre name Available if property Type is "audio" or "video" :uint32 NumberOfTracks: Item album number of tracks in total Available if property Type is "audio" or "video" :uint32 Number: Item album number Available if property Type is "audio" or "video" :uint32 Duration: Item duration in milliseconds Available if property Type is "audio" or "video" bluez-5.82/doc/PaxHeaders/hci.70000644000000000000000000000005014773213421013252 xustar0020 atime=1743591185 20 ctime=1743591290 bluez-5.82/doc/hci.70000644000000000000000000001006714773213421012737 0ustar00rootroot'\" t .\" Man page generated from reStructuredText. . . .nr rst2man-indent-level 0 . .de1 rstReportMargin \\$1 \\n[an-margin] level \\n[rst2man-indent-level] level margin: \\n[rst2man-indent\\n[rst2man-indent-level]] - \\n[rst2man-indent0] \\n[rst2man-indent1] \\n[rst2man-indent2] .. .de1 INDENT .\" .rstReportMargin pre: . RS \\$1 . nr rst2man-indent\\n[rst2man-indent-level] \\n[an-margin] . nr rst2man-indent-level +1 .\" .rstReportMargin post: .. .de UNINDENT . RE .\" indent \\n[an-margin] .\" old: \\n[rst2man-indent\\n[rst2man-indent-level]] .nr rst2man-indent-level -1 .\" new: \\n[rst2man-indent\\n[rst2man-indent-level]] .in \\n[rst2man-indent\\n[rst2man-indent-level]]u .. .TH "HCI" "7" "October 2024" "BlueZ" "Linux System Administration" .SH NAME hci \- Bluetooth HCI protocol .SH SYNOPSIS .INDENT 0.0 .INDENT 3.5 .sp .EX #include #include #include hci_socket = socket(PF_BLUETOOTH, SOCK_RAW, BTPROTO_HCI); .EE .UNINDENT .UNINDENT .SH DESCRIPTION .sp Bluetooth Host Controller Interface (HCI) is the standard protocol to communicate with Bluetooth adapters. HCI protocol provides a uniform command method for the Host to access Controller capabilities and to control connections to other Controllers. .SH SOCKET ADDRESS .INDENT 0.0 .INDENT 3.5 .sp .EX struct sockaddr_hci { sa_family_t hci_family; unsigned short hci_dev; unsigned short hci_channel; }; .EE .UNINDENT .UNINDENT .sp Possible values for hci_channel: .TS box center; l|l|l. T{ Define T} T{ Value T} T{ Description T} _ T{ \fBHCI_CHANNEL_RAW\fP T} T{ 0x00 T} T{ Raw channel \- Used for raw HCI communication T} _ T{ \fBHCI_CHANNEL_USER\fP T} T{ 0x01 T} T{ User channel \- Used for userspace HCI communication (disables kernel processing) T} _ T{ \fBHCI_CHANNEL_MONITOR\fP T} T{ 0x02 T} T{ Monitor channel \- Used for monitoring HCI traffic (btmon(1)) T} _ T{ \fBHCI_CHANNEL_CONTROL\fP T} T{ 0x03 T} T{ Control channel \- Used to manage local adapters (bluetoothd(7)) T} _ T{ \fBHCI_CHANNEL_LOGGING\fP T} T{ 0x04 T} T{ Logging channel \- Used to inject logging messages (bluetoothd(7)) T} .TE .sp Example: .INDENT 0.0 .INDENT 3.5 .sp .EX struct sockaddr_hci addr; memset(&addr, 0, sizeof(addr)); addr.hci_family = AF_BLUETOOTH; addr.hci_dev = HCI_DEV_NONE; addr.hci_channel = HCI_CHANNEL_CONTROL; .EE .UNINDENT .UNINDENT .SH SOCKET OPTIONS .sp The socket options listed below can be set by using \fBsetsockopt(2)\fP and read with \fBgetsockopt(2)\fP with the socket level set to SOL_BLUETOOTH or SOL_HCI (HCI_FILTER). .SS HCI_FILTER (since Linux 2.6) .sp Filter by HCI events, requires hci_channel to be set to HCI_CHANNEL_RAW, possible values: .INDENT 0.0 .INDENT 3.5 .sp .EX struct hci_filter { uint32_t type_mask; uint32_t event_mask[2]; uint16_t opcode; }; .EE .UNINDENT .UNINDENT .sp Example: .INDENT 0.0 .INDENT 3.5 .sp .EX struct hci_filter flt; memset(&flt, 0, sizeof(flt)); flt.type_mask = 1 << BT_H4_EVT_PKT; flt.event_mask[0] = 0xffffffff; flt.event_mask[1] = 0xffffffff; setsockopt(fd, SOL_HCI, HCI_FILTER, &flt, sizeof(flt)); .EE .UNINDENT .UNINDENT .SS BT_SNDBUF (since Linux 5.16) .sp Set send buffer size, requires hci_channel to be set to HCI_CHANNEL_MONITOR, HCI_CHANNEL_CONTROL or HCI_CHANNEL_LOGGING. .sp Default value is 1028. .sp Example: .INDENT 0.0 .INDENT 3.5 .sp .EX uint16_t mtu = UINT16_MAX; int err; err = setsockopt(fd, SOL_BLUETOOTH, BT_SNDMTU, &mtu, sizeof(mtu)); .EE .UNINDENT .UNINDENT .SS BT_RCVBUF (since Linux 5.16) .sp Set receive buffer size, requires hci_channel to be set to HCI_CHANNEL_MONITOR, HCI_CHANNEL_CONTROL or HCI_CHANNEL_LOGGING. .sp Default value is 1028. .sp Example: .INDENT 0.0 .INDENT 3.5 .sp .EX uint16_t mtu; socklen_t len; int err; len = sizeof(mtu); err = getsockopt(sock, SOL_BLUETOOTH, BT_RCVMTU, mtu, &len); .EE .UNINDENT .UNINDENT .SH RESOURCES .sp .SH REPORTING BUGS .sp .SH SEE ALSO .sp socket(7) .SH COPYRIGHT Free use of this software is granted under the terms of the GNU Lesser General Public Licenses (LGPL). .\" Generated by docutils manpage writer. . bluez-5.82/doc/PaxHeaders/org.bluez.obex.MessageAccess.50000644000000000000000000000005014773213502020055 xustar0020 atime=1743591234 20 ctime=1743591291 bluez-5.82/doc/org.bluez.obex.MessageAccess.50000644000000000000000000001313214773213502017536 0ustar00rootroot.\" Man page generated from reStructuredText. . . .nr rst2man-indent-level 0 . .de1 rstReportMargin \\$1 \\n[an-margin] level \\n[rst2man-indent-level] level margin: \\n[rst2man-indent\\n[rst2man-indent-level]] - \\n[rst2man-indent0] \\n[rst2man-indent1] \\n[rst2man-indent2] .. .de1 INDENT .\" .rstReportMargin pre: . RS \\$1 . nr rst2man-indent\\n[rst2man-indent-level] \\n[an-margin] . nr rst2man-indent-level +1 .\" .rstReportMargin post: .. .de UNINDENT . RE .\" indent \\n[an-margin] .\" old: \\n[rst2man-indent\\n[rst2man-indent-level]] .nr rst2man-indent-level -1 .\" new: \\n[rst2man-indent\\n[rst2man-indent-level]] .in \\n[rst2man-indent\\n[rst2man-indent-level]]u .. .TH "ORG.BLUEZ.OBEX.MESSAGEACCESS" "5" "October 2023" "BlueZ" "Linux System Administration" .SH NAME org.bluez.obex.MessageAccess \- BlueZ D-Bus OBEX MessageAccess API documentation .SH INTERFACE .INDENT 0.0 .TP .B Service org.bluez.obex .TP .B Interface org.bluez.obex.MessageAccess1 .TP .B Object path [Session object path] .UNINDENT .SS Methods .SS void SetFolder(string name) .INDENT 0.0 .INDENT 3.5 Set working directory for current session. .sp Possible name: .INDENT 0.0 .INDENT 3.5 Directory name or \(aq..[/dir]\(aq. .UNINDENT .UNINDENT .sp Possible errors: .INDENT 0.0 .TP .B org.bluez.obex.Error.InvalidArguments .TP .B org.bluez.obex.Error.Failed .UNINDENT .UNINDENT .UNINDENT .SS array{dict} ListFolders(dict filter) .INDENT 0.0 .INDENT 3.5 Returns a dictionary containing information about the current folder content. .sp Possible filter: .INDENT 0.0 .TP .B uint16 Offset (default 0) Offset of the first item. .TP .B uint16 MaxCount (default 1024) Maximum number of items. .UNINDENT .sp Possible return: .INDENT 0.0 .TP .B string Name Folder name .UNINDENT .sp Possible errors: .INDENT 0.0 .TP .B org.bluez.obex.Error.InvalidArguments .TP .B org.bluez.obex.Error.Failed .UNINDENT .UNINDENT .UNINDENT .SS array{string} ListFilterFields() .INDENT 0.0 .INDENT 3.5 Return all available fields that can be used in \fBFields\fP filter. .sp Possible values: .INDENT 0.0 .TP .B \(dqsubject\(dq .TP .B \(dqtimestamp\(dq .TP .B \(dqsender\(dq .TP .B \(dqsender\-address\(dq .TP .B \(dqrecipient\(dq .TP .B \(dqrecipient\-address\(dq .TP .B \(dqtype\(dq .TP .B \(dqsize\(dq .TP .B \(dqstatus\(dq .TP .B \(dqtext\(dq .TP .B \(dqattachment\(dq .TP .B \(dqpriority\(dq .TP .B \(dqread\(dq .TP .B \(dqsent\(dq .TP .B \(dqprotected\(dq .TP .B \(dqreplyto\(dq .UNINDENT .sp Possible errors: None .UNINDENT .UNINDENT .SS array{object, dict} ListMessages(string folder, dict filter) .INDENT 0.0 .INDENT 3.5 Returns an array containing the messages objects found in the given subfolder of the current folder, or in the current folder if folder is empty. .sp Possible Filters: .INDENT 0.0 .TP .B uint16 Offset (default 0) Offset of the first item. .UNINDENT .sp uint16 MaxCount (default 1024): .INDENT 0.0 .INDENT 3.5 Maximum number of items. .UNINDENT .UNINDENT .INDENT 0.0 .TP .B byte SubjectLength (default 256) Maximum length of the Subject property in the message. .TP .B array{string} Fields Message fields, default is all values. .sp See \fBListFilterFields()\fP for possible values. .TP .B array{string} Types Filter messages by type. .sp Possible values: .INDENT 7.0 .TP .B \(dqsms\(dq .TP .B \(dqemail\(dq .TP .B \(dqmms\(dq .UNINDENT .TP .B string PeriodBegin Filter messages by starting period. .sp Possible values: .INDENT 7.0 .INDENT 3.5 Date in \(dqYYYYMMDDTHHMMSS\(dq format. .UNINDENT .UNINDENT .TP .B string PeriodEnd Filter messages by ending period. .sp Possible values: .INDENT 7.0 .INDENT 3.5 Date in \(dqYYYYMMDDTHHMMSS\(dq format. .UNINDENT .UNINDENT .TP .B boolean Read Filter messages by read flag. .sp Possible values: .INDENT 7.0 .INDENT 3.5 True for read or False for unread .UNINDENT .UNINDENT .TP .B string Recipient Filter messages by recipient address. .TP .B string Sender Filter messages by sender address. .TP .B boolean Priority Filter messages by priority flag. .sp Possible values: .INDENT 7.0 .INDENT 3.5 True for high priority or False for non\-high priority. .UNINDENT .UNINDENT .UNINDENT .sp Each message is represented by an object path, which implements \fBorg.bluez.obex.Message(5)\fP interface, followed by a dictionary of its properties. .UNINDENT .UNINDENT .sp void UpdateInbox(void) .INDENT 0.0 .INDENT 3.5 Requests remote to update its inbox. .sp Possible errors: .INDENT 0.0 .TP .B org.bluez.obex.Error.Failed .UNINDENT .UNINDENT .UNINDENT .SS object, dict PushMessage(string sourcefile, string folder, dict args) .INDENT 0.0 .INDENT 3.5 Transfers a message (in bMessage format) to the remote device. .sp The message is transferred either to the given subfolder of the current folder, or to the current folder if folder is empty. .sp Possible args: Transparent, Retry, Charset .sp The returned path represents the newly created transfer, which should be used to find out if the content has been successfully transferred or if the operation fails. .sp The properties of this transfer are also returned along with the object path, to avoid a call to GetProperties, see \fBorg.bluez.obex.Transfer(5)\fP for the possible list of properties. .sp Possible errors: .INDENT 0.0 .TP .B org.bluez.obex.Error.InvalidArguments .TP .B org.bluez.obex.Error.Failed .UNINDENT .UNINDENT .UNINDENT .SS Properties .SS array{string} SupportedTypes [readonly] .INDENT 0.0 .INDENT 3.5 List of supported message types. .sp Possible values: .INDENT 0.0 .TP .B \(dqEMAIL\(dq Email messages. .TP .B \(dqSMS_GSM\(dq GSM short messages. .TP .B \(dqSMS_CDMA\(dq CDMA short messages. .TP .B \(dqMMS\(dq MMS messages. .TP .B \(dqIM\(dq Instant messaging. .UNINDENT .UNINDENT .UNINDENT .\" Generated by docutils manpage writer. . bluez-5.82/doc/PaxHeaders/org.bluez.AdvertisementMonitorManager.rst0000644000000000000000000000005014536422313022533 xustar0020 atime=1743516810 20 ctime=1743591290 bluez-5.82/doc/org.bluez.AdvertisementMonitorManager.rst0000644000000000000000000000522314536422313022216 0ustar00rootroot===================================== org.bluez.AdvertisementMonitorManager ===================================== --------------------------------------------------------- BlueZ D-Bus AdvertisementMonitorManager API documentation --------------------------------------------------------- :Version: BlueZ :Date: October 2023 :Manual section: 5 :Manual group: Linux System Administration Interface ========= Service org.bluez Interface org.bluez.AdvertisementMonitorManager1 [experimental] Object path /org/bluez/{hci0,hci1,...} Methods ------- void RegisterMonitor(object application) ```````````````````````````````````````` Registers the root path of a hierarchy of advertisement monitors implementing **org.bluez.AdvertisementMonitor(5)**. The application object path together with the D-Bus ystem bus connection ID define the identification of the application registering advertisement monitors. Once a root path is registered by a client via this method, the client can freely expose/unexpose advertisement monitors without re-registering the root path again. After use, the client should call **UnregisterMonitor()** method to invalidate the advertisement monitors. Possible errors: :org.bluez.Error.InvalidArguments: :org.bluez.Error.AlreadyExists: :org.bluez.Error.Failed: void UnregisterMonitor(object application) `````````````````````````````````````````` Unregisters a hierarchy of advertisement monitors that has been previously registered with **RegisterMonitor()**. The object path parameter must match the same value that has been used on registration. Upon unregistration, the advertisement monitor(s) should expect to receive **Release()** method as the signal that the advertisement monitor(s) has been deactivated. Possible errors: :org.bluez.Error.InvalidArguments: :org.bluez.Error.DoesNotExist: Properties ---------- array{string} SupportedMonitorTypes [read-only] ``````````````````````````````````````````````` This lists the supported types of advertisement monitors. An application should check this before instantiate and expose an object of **org.bluez.AdvertisementMonitor(5)**. Possible values: :"or_patterns": Patterns with logic OR applied. With this type, property **Patterns** must exist and has at least one pattern. array{string} SupportedFeatures [read-only] ``````````````````````````````````````````` This lists the features of advertisement monitoring supported by **bluetoothd(8)**. Possible values: :"controller-patterns": If the controller is capable of performing advertisement monitoring by patterns, **bluetoothd(8)** would offload the patterns to the controller to reduce power consumption. bluez-5.82/doc/PaxHeaders/org.bluez.obex.Session.50000644000000000000000000000005014773213473017001 xustar0020 atime=1743591227 20 ctime=1743591291 bluez-5.82/doc/org.bluez.obex.Session.50000644000000000000000000000353214773213473016465 0ustar00rootroot.\" Man page generated from reStructuredText. . . .nr rst2man-indent-level 0 . .de1 rstReportMargin \\$1 \\n[an-margin] level \\n[rst2man-indent-level] level margin: \\n[rst2man-indent\\n[rst2man-indent-level]] - \\n[rst2man-indent0] \\n[rst2man-indent1] \\n[rst2man-indent2] .. .de1 INDENT .\" .rstReportMargin pre: . RS \\$1 . nr rst2man-indent\\n[rst2man-indent-level] \\n[an-margin] . nr rst2man-indent-level +1 .\" .rstReportMargin post: .. .de UNINDENT . RE .\" indent \\n[an-margin] .\" old: \\n[rst2man-indent\\n[rst2man-indent-level]] .nr rst2man-indent-level -1 .\" new: \\n[rst2man-indent\\n[rst2man-indent-level]] .in \\n[rst2man-indent\\n[rst2man-indent-level]]u .. .TH "ORG.BLUEZ.OBEX.SESSION" "5" "October 2023" "BlueZ" "Linux System Administration" .SH NAME org.bluez.obex.Session \- BlueZ D-Bus OBEX Client API documentation .SH INTERFACE .INDENT 0.0 .TP .B Service org.bluez.obex .TP .B Interface org.bluez.obex.Session1 .TP .B Object path /org/bluez/obex/server/session{#} or /org/bluez/obex/client/session{#} .UNINDENT .SS Methods .SS string GetCapabilities() .INDENT 0.0 .INDENT 3.5 Get remote device capabilities. .sp Possible errors: .INDENT 0.0 .TP .B org.bluez.obex.Error.NotSupported .TP .B org.bluez.obex.Error.Failed .UNINDENT .UNINDENT .UNINDENT .SS Properties .SS string Source [readonly] .INDENT 0.0 .INDENT 3.5 Bluetooth adapter address .UNINDENT .UNINDENT .SS string Destination [readonly] .INDENT 0.0 .INDENT 3.5 Bluetooth device address .UNINDENT .UNINDENT .SS byte Channel [readonly] .INDENT 0.0 .INDENT 3.5 Bluetooth channel .UNINDENT .UNINDENT .SS uint16 PSM [readonly] .INDENT 0.0 .INDENT 3.5 Bluetooth L2CAP PSM .UNINDENT .UNINDENT .SS string Target [readonly] .INDENT 0.0 .INDENT 3.5 Target UUID .UNINDENT .UNINDENT .SS string Root [readonly] .INDENT 0.0 .INDENT 3.5 Root path .UNINDENT .UNINDENT .\" Generated by docutils manpage writer. . bluez-5.82/doc/PaxHeaders/rfcomm.rst0000644000000000000000000000005014766002272014436 xustar0020 atime=1743515578 20 ctime=1743591290 bluez-5.82/doc/rfcomm.rst0000644000000000000000000001303414766002272014120 0ustar00rootroot====== rfcomm ====== --------------- RFCOMM protocol --------------- :Version: BlueZ :Copyright: Free use of this software is granted under the terms of the GNU Lesser General Public Licenses (LGPL). :Date: May 2024 :Manual section: 7 :Manual group: Linux System Administration SYNOPSIS ======== .. code-block:: #include #include #include rfcomm_socket = socket(PF_BLUETOOTH, SOCK_STREAM, BTPROTO_RFCOMM); DESCRIPTION =========== The RFCOMM protocol provides emulation of serial ports over the L2CAP(7) protocol. The protocol is based on the ETSI standard TS 07.10. RFCOMM is a simple transport protocol, with additional provisions for emulating the 9 circuits of RS-232 (EIATIA-232-E) serial ports. SOCKET ADDRESS ============== .. code-block:: struct sockaddr_rc { sa_family_t rc_family; unsigned short rc_bdaddr; unsigned char rc_channel; }; Example: .. code-block:: struct sockaddr_rc addr; memset(&addr, 0, sizeof(addr)); addr.rc_family = AF_BLUETOOTH; bacpy(&addr.rc_bdaddr, bdaddr); addr.rc_channel = channel; SOCKET OPTIONS ============== The socket options listed below can be set by using **setsockopt(2)** and read with **getsockopt(2)** with the socket level set to SOL_BLUETOOTH. BT_SECURITY (since Linux 2.6.30) -------------------------------- Channel security level, possible values: .. csv-table:: :header: "Value", "Security Level", "Link Key Type", "Encryption" :widths: auto **BT_SECURITY_SDP**, 0 (SDP Only), None, Not required **BT_SECURITY_LOW**, 1 (Low), Unauthenticated, Not required **BT_SECURITY_MEDIUM**, 2 (Medium - default), Unauthenticated, Desired **BT_SECURITY_HIGH**, 3 (High), Authenticated, Required **BT_SECURITY_FIPS** (since Linux 3.15), 4 (Secure Only), Authenticated (P-256 based Secure Simple Pairing and Secure Authentication), Required Example: .. code-block:: int level = BT_SECURITY_HIGH; int err = setsockopt(rfcomm_socket, SOL_BLUETOOTH, BT_SECURITY, &level, sizeof(level)); if (err == -1) { perror("setsockopt"); return 1; } BT_DEFER_SETUP (since Linux 2.6.30) ----------------------------------- Channel defer connection setup, this control if the connection procedure needs to be authorized by userspace before responding which allows authorization at profile level, possible values: .. csv-table:: :header: "Value", "Description", "Authorization" :widths: auto **0**, Disable (default), Not required **1**, Enable, Required Example: .. code-block:: int defer_setup = 1; int err = setsockopt(rfcomm_socket, SOL_BLUETOOTH, BT_DEFER_SETUP, &defer_setup, sizeof(defer_setup)); if (err == -1) { perror("setsockopt"); return err; } err = listen(rfcomm_socket, 5); if (err) { perror("listen"); return err; } struct sockaddr_rc remote_addr = {0}; socklen_t addr_len = sizeof(remote_addr); int new_socket = accept(rfcomm_socket, (struct sockaddr*)&remote_addr, &addr_len); if (new_socket < 0) { perror("accept"); return new_socket; } /* To complete the connection setup of new_socket read 1 byte */ char c; struct pollfd pfd; memset(&pfd, 0, sizeof(pfd)); pfd.fd = new_socket; pfd.events = POLLOUT; err = poll(&pfd, 1, 0); if (err) { perror("poll"); return err; } if (!(pfd.revents & POLLOUT)) { err = read(sk, &c, 1); if (err < 0) { perror("read"); return err; } } BT_FLUSHABLE (since Linux 2.6.39) --------------------------------- Channel flushable flag, this control if the channel data can be flushed or not, possible values: .. csv-table:: :header: "Define", "Value", "Description" :widths: auto **BT_FLUSHABLE_OFF**, 0x00 (default), Do not flush data **BT_FLUSHABLE_ON**, 0x01, Flush data BT_POWER (since Linux 3.1) -------------------------- Channel power policy, this control if the channel shall force exit of sniff mode or not, possible values: .. csv-table:: :header: "Define", "Value", "Description" :widths: auto **BT_POWER_FORCE_ACTIVE_OFF**, 0x00 (default), Don't force exit of sniff mode **BT_POWER_FORCE_ACTIVE_ON**, 0x01, Force exit of sniff mode BT_CHANNEL_POLICY (since Linux 3.10) ------------------------------------ High-speed (AMP) channel policy, possible values: .. csv-table:: :header: "Define", "Value", "Description" :widths: auto **BT_CHANNEL_POLICY_BREDR_ONLY**, 0 (default), BR/EDR only **BT_CHANNEL_POLICY_BREDR_PREFERRED**, 1, BR/EDR Preferred **BT_CHANNEL_POLICY_BREDR_PREFERRED**, 2, AMP Preferred BT_PHY (since Linux 5.10) ------------------------- Channel supported PHY(s), possible values: .. csv-table:: :header: "Define", "Value", "Description" :widths: auto **BT_PHY_BR_1M_1SLOT**, BIT 0, BR 1Mbps 1SLOT **BT_PHY_BR_1M_3SLOT**, BIT 1, BR 1Mbps 3SLOT **BT_PHY_BR_1M_5SLOT**, BIT 2, BR 1Mbps 5SLOT **BT_PHY_BR_2M_1SLOT**, BIT 3, EDR 2Mbps 1SLOT **BT_PHY_BR_2M_3SLOT**, BIT 4, EDR 2Mbps 3SLOT **BT_PHY_BR_2M_5SLOT**, BIT 5, EDR 2Mbps 5SLOT **BT_PHY_BR_3M_1SLOT**, BIT 6, EDR 3Mbps 1SLOT **BT_PHY_BR_3M_3SLOT**, BIT 7, EDR 3Mbps 3SLOT **BT_PHY_BR_3M_5SLOT**, BIT 8, EDR 3Mbps 5SLOT RESOURCES ========= http://www.bluez.org REPORTING BUGS ============== linux-bluetooth@vger.kernel.org SEE ALSO ======== socket(7), rctest(1) bluez-5.82/doc/PaxHeaders/org.bluez.LEAdvertisement.50000644000000000000000000000005014773213467017460 xustar0020 atime=1743591223 20 ctime=1743591291 bluez-5.82/doc/org.bluez.LEAdvertisement.50000644000000000000000000001716214773213467017150 0ustar00rootroot.\" Man page generated from reStructuredText. . . .nr rst2man-indent-level 0 . .de1 rstReportMargin \\$1 \\n[an-margin] level \\n[rst2man-indent-level] level margin: \\n[rst2man-indent\\n[rst2man-indent-level]] - \\n[rst2man-indent0] \\n[rst2man-indent1] \\n[rst2man-indent2] .. .de1 INDENT .\" .rstReportMargin pre: . RS \\$1 . nr rst2man-indent\\n[rst2man-indent-level] \\n[an-margin] . nr rst2man-indent-level +1 .\" .rstReportMargin post: .. .de UNINDENT . RE .\" indent \\n[an-margin] .\" old: \\n[rst2man-indent\\n[rst2man-indent-level]] .nr rst2man-indent-level -1 .\" new: \\n[rst2man-indent\\n[rst2man-indent-level]] .in \\n[rst2man-indent\\n[rst2man-indent-level]]u .. .TH "ORG.BLUEZ.LEADVERTISEMENT" "5" "October 2023" "BlueZ" "Linux System Administration" .SH NAME org.bluez.LEAdvertisement \- BlueZ D-Bus LEAdvertisement API documentation .SH DESCRIPTION .sp Advertising packets are structured data which is broadcast on the LE Advertising channels and available for all devices in range. Because of the limited space available in LE Advertising packets, each packet\(aqs contents must be carefully controlled. .sp The service daemon acts as a store for the Advertisement Data which is meant to be sent. It constructs the correct Advertisement Data from the structured data and configured the kernel to send the correct advertisement. .SH INTERFACE .sp Specifies the Advertisement Data to be broadcast and some advertising parameters. Properties which are not present will not be included in the data. Required advertisement data types will always be included. All UUIDs are 128\-bit versions in the API, and 16 or 32\-bit versions of the same UUID will be used in the advertising data as appropriate. .INDENT 0.0 .TP .B Service org.bluez .TP .B Interface org.bluez.LEAdvertisement1 .TP .B Object path freely definable .UNINDENT .SS Methods .SS void Release() [noreply] .INDENT 0.0 .INDENT 3.5 This method gets called when the service daemon removes the Advertisement. A client can use it to do cleanup tasks. There is no need to call \fBUnregisterAdvertisement()\fP because when this method gets called it has already been unregistered. .UNINDENT .UNINDENT .SS Properties .SS string Type [readonly] .INDENT 0.0 .INDENT 3.5 Determines the type of advertising packet requested. .sp Possible values: .INDENT 0.0 .TP .B \(dqbroadcast\(dq .TP .B \(dqperipheral\(dq .UNINDENT .UNINDENT .UNINDENT .SS array{string} ServiceUUIDs [readonly, optional] .INDENT 0.0 .INDENT 3.5 List of UUIDs to include in the \(dqService UUID\(dq field of the Advertising Data. .UNINDENT .UNINDENT .SS dict ManufacturerData [readonly, optional] .INDENT 0.0 .INDENT 3.5 Manufacturer Data fields to include in the Advertising Data. Keys are the Manufacturer ID to associate with the data. .UNINDENT .UNINDENT .SS array{string} SolicitUUIDs [readonly, optional] .INDENT 0.0 .INDENT 3.5 List of UUIDs to include in the \(dqService Solicitation\(dq field of the Advertising Data. .UNINDENT .UNINDENT .SS dict ServiceData [readonly, optional] .INDENT 0.0 .INDENT 3.5 Service Data elements to include in the Advertising Data. The keys are the UUID to associate with the data. .UNINDENT .UNINDENT .SS dict Data [readonly, optional] .INDENT 0.0 .INDENT 3.5 Advertising Data to include. Key is the advertising type and value is the data as byte array. .sp Note: Types already handled by other properties shall not be used. .sp Possible values: .INDENT 0.0 .TP .B .UNINDENT .INDENT 0.0 .TP .B Example: 0x26 0x01 0x01... .UNINDENT .UNINDENT .UNINDENT .SS array{string} ScanResponseServiceUUIDs [readonly, optional, experimental] .INDENT 0.0 .INDENT 3.5 List of UUIDs to include in the \(dqService UUID\(dq field of the Scan Response Data. .UNINDENT .UNINDENT .SS dict ScanResponseManufacturerData [readonly, optional, experimental] .INDENT 0.0 .INDENT 3.5 Manufacturer Data fields to include in the Scan Response Data. Keys are the Manufacturer ID to associate with the data. .UNINDENT .UNINDENT .SS array{string} ScanResponseSolicitUUIDs [readonly, optional, experimental] .INDENT 0.0 .INDENT 3.5 List of UUIDs to include in the \(dqService Solicitation\(dq field of the Scan Response Data. .UNINDENT .UNINDENT .SS dict ScanResponseServiceData [readonly, optional, experimental] .INDENT 0.0 .INDENT 3.5 Service Data elements to include in the Scan Response Data. The keys are the UUID to associate with the data. .UNINDENT .UNINDENT .SS dict ScanResponseData [readonly, optional, experimental] .INDENT 0.0 .INDENT 3.5 Scan Response Data to include. Key is the advertising type and value is the data as byte array. .UNINDENT .UNINDENT .SS bool Discoverable [readonly, optional] .INDENT 0.0 .INDENT 3.5 Advertise as general discoverable. When present this will override adapter Discoverable property. .sp Note: This property shall not be set when \fBType\fP is set to \(dqbroadcast\(dq. .UNINDENT .UNINDENT .SS uint16 DiscoverableTimeout [readonly, optional] .INDENT 0.0 .INDENT 3.5 The discoverable timeout in seconds. A value of zero means that the timeout is disabled and it will stay in discoverable/limited mode forever. .sp Note: This property shall not be set when \fBType\fP is set to \(dqbroadcast\(dq. .UNINDENT .UNINDENT .SS array{string} Includes [readonly, optional] .INDENT 0.0 .INDENT 3.5 List of features to be included in the advertising packet. .sp Possible values: .sp See \fBorg.bluez.LEAdvertisingManager(5)\fP \fBSupportedIncludes\fP property. .UNINDENT .UNINDENT .SS string LocalName [readonly, optional] .INDENT 0.0 .INDENT 3.5 Local name to be used in the advertising report. If the string is too big to fit into the packet it will be truncated. .sp If this property is available \(aqlocal\-name\(aq cannot be present in the \fBIncludes\fP\&. .UNINDENT .UNINDENT .SS uint16 Appearance [readonly, optional] .INDENT 0.0 .INDENT 3.5 Appearance to be used in the advertising report. .sp Possible values: as found on GAP Service. .UNINDENT .UNINDENT .SS uint16_t Duration [readonly, optional] .INDENT 0.0 .INDENT 3.5 Rotation duration of the advertisement in seconds. If there are other applications advertising no duration is set the default is 2 seconds. .UNINDENT .UNINDENT .SS uint16_t Timeout [readonly, optional] .INDENT 0.0 .INDENT 3.5 Timeout of the advertisement in seconds. This defines the lifetime of the advertisement. .UNINDENT .UNINDENT .SS string SecondaryChannel [readonly, optional] .INDENT 0.0 .INDENT 3.5 Secondary channel to be used. Primary channel is always set to \(dq1M\(dq except when \(dqCoded\(dq is set. .sp Possible value: .INDENT 0.0 .TP .B \(dq1M\(dq (default) .TP .B \(dq2M\(dq .TP .B \(dqCoded\(dq .UNINDENT .UNINDENT .UNINDENT .SS uint32 MinInterval [readonly, optional] .INDENT 0.0 .INDENT 3.5 Minimum advertising interval to be used by the advertising set, in milliseconds. Acceptable values are in the range [20ms, 10,485s]. If the provided MinInterval is larger than the provided MaxInterval, the registration will return failure. .UNINDENT .UNINDENT .SS uint32 MaxInterval [readonly, optional] .INDENT 0.0 .INDENT 3.5 Maximum advertising interval to be used by the advertising set, in milliseconds. Acceptable values are in the range [20ms, 10,485s]. If the provided MinInterval is larger than the provided MaxInterval, the registration will return failure. .UNINDENT .UNINDENT .SS int16 TxPower [readonly, optional] .INDENT 0.0 .INDENT 3.5 Requested transmission power of this advertising set. The provided value is used only if the \(dqCanSetTxPower\(dq feature is enabled on the \fBorg.bluez.LEAdvertisingManager(5)\fP\&. The provided value must be in range [\-127 to +20], where units are in dBm. .UNINDENT .UNINDENT .\" Generated by docutils manpage writer. . bluez-5.82/doc/PaxHeaders/org.bluez.obex.Client.50000644000000000000000000000005014773213472016573 xustar0020 atime=1743591226 20 ctime=1743591291 bluez-5.82/doc/org.bluez.obex.Client.50000644000000000000000000000423614773213472016261 0ustar00rootroot.\" Man page generated from reStructuredText. . . .nr rst2man-indent-level 0 . .de1 rstReportMargin \\$1 \\n[an-margin] level \\n[rst2man-indent-level] level margin: \\n[rst2man-indent\\n[rst2man-indent-level]] - \\n[rst2man-indent0] \\n[rst2man-indent1] \\n[rst2man-indent2] .. .de1 INDENT .\" .rstReportMargin pre: . RS \\$1 . nr rst2man-indent\\n[rst2man-indent-level] \\n[an-margin] . nr rst2man-indent-level +1 .\" .rstReportMargin post: .. .de UNINDENT . RE .\" indent \\n[an-margin] .\" old: \\n[rst2man-indent\\n[rst2man-indent-level]] .nr rst2man-indent-level -1 .\" new: \\n[rst2man-indent\\n[rst2man-indent-level]] .in \\n[rst2man-indent\\n[rst2man-indent-level]]u .. .TH "ORG.BLUEZ.OBEX.CLIENT" "5" "October 2023" "BlueZ" "Linux System Administration" .SH NAME org.bluez.obex.Client \- BlueZ D-Bus OBEX Client API documentation .SH INTERFACE .INDENT 0.0 .TP .B Service org.bluez.obex .TP .B Interface org.bluez.obex.Client1 .TP .B Object path /org/bluez/obex .UNINDENT .SS Methods .SS object CreateSession(string destination, dict args) .INDENT 0.0 .INDENT 3.5 Connects to the destination address and then proceed to create an OBEX session object which implements \fBorg.bluez.obex.Session(5)\fP interface. .sp The last parameter is a dictionary to hold optional or type\-specific parameters. .sp Possible args values: .INDENT 0.0 .TP .B string Target Type of session to be created. .sp Possible values: .INDENT 7.0 .TP .B \(dqftp\(dq .TP .B \(dqmap\(dq .TP .B \(dqopp\(dq .TP .B \(dqpbap\(dq .TP .B \(dqsync\(dq .TP .B \(dqbip\-avrcp\(dq .UNINDENT .TP .B string Source Local address to be used. .TP .B byte Channel Channel to be used. .TP .B uint16 PSM L2CAP PSM to be used. .UNINDENT .sp Possible errors: .INDENT 0.0 .TP .B org.bluez.obex.Error.InvalidArguments .TP .B org.bluez.obex.Error.Failed .UNINDENT .UNINDENT .UNINDENT .SS void RemoveSession(object session) .INDENT 0.0 .INDENT 3.5 Disconnects and removes session previously created by \fBCreateSession()\fP aborting any pending transfers. .sp Possible errors: .INDENT 0.0 .TP .B org.bluez.obex.Error.InvalidArguments .TP .B org.bluez.obex.Error.NotAuthorized .UNINDENT .UNINDENT .UNINDENT .\" Generated by docutils manpage writer. . bluez-5.82/doc/PaxHeaders/org.bluez.MediaControl.50000644000000000000000000000005014773213450016775 xustar0020 atime=1743591208 20 ctime=1743591291 bluez-5.82/doc/org.bluez.MediaControl.50000644000000000000000000000432014773213450016455 0ustar00rootroot.\" Man page generated from reStructuredText. . . .nr rst2man-indent-level 0 . .de1 rstReportMargin \\$1 \\n[an-margin] level \\n[rst2man-indent-level] level margin: \\n[rst2man-indent\\n[rst2man-indent-level]] - \\n[rst2man-indent0] \\n[rst2man-indent1] \\n[rst2man-indent2] .. .de1 INDENT .\" .rstReportMargin pre: . RS \\$1 . nr rst2man-indent\\n[rst2man-indent-level] \\n[an-margin] . nr rst2man-indent-level +1 .\" .rstReportMargin post: .. .de UNINDENT . RE .\" indent \\n[an-margin] .\" old: \\n[rst2man-indent\\n[rst2man-indent-level]] .nr rst2man-indent-level -1 .\" new: \\n[rst2man-indent\\n[rst2man-indent-level]] .in \\n[rst2man-indent\\n[rst2man-indent-level]]u .. .TH "ORG.BLUEZ.MEDIACONTROL" "5" "September 2023" "BlueZ" "Linux System Administration" .SH NAME org.bluez.MediaControl \- BlueZ D-Bus MediaControl API documentation .SH INTERFACE .INDENT 0.0 .TP .B Service org.bluez .TP .B Interface org.bluez.MediaControl1 .TP .B Object path [variable prefix]/{hci0,hci1,...}/dev_XX_XX_XX_XX_XX_XX .UNINDENT .SS Methods .SS void Play() [Deprecated] .INDENT 0.0 .INDENT 3.5 Resume playback. .UNINDENT .UNINDENT .SS void Pause() [Deprecated] .INDENT 0.0 .INDENT 3.5 Pause playback. .UNINDENT .UNINDENT .SS void Stop() [Deprecated] .INDENT 0.0 .INDENT 3.5 Stop playback. .UNINDENT .UNINDENT .SS void Next() [Deprecated] .INDENT 0.0 .INDENT 3.5 Next item. .UNINDENT .UNINDENT .SS void Previous() [Deprecated] .INDENT 0.0 .INDENT 3.5 Previous item. .UNINDENT .UNINDENT .SS void VolumeUp() [Deprecated] .INDENT 0.0 .INDENT 3.5 Adjust remote volume one step up .UNINDENT .UNINDENT .SS void VolumeDown() [Deprecated] .INDENT 0.0 .INDENT 3.5 Adjust remote volume one step down .UNINDENT .UNINDENT .SS void FastForward() [Deprecated] .INDENT 0.0 .INDENT 3.5 Fast forward playback, this action is only stopped when another method in this interface is called. .UNINDENT .UNINDENT .SS void Rewind() [Deprecated] .INDENT 0.0 .INDENT 3.5 Rewind playback, this action is only stopped when another method in this interface is called. .UNINDENT .UNINDENT .SS Properties .SS boolean Connected [readonly] .SS object Player [readonly, optional] .INDENT 0.0 .INDENT 3.5 Addressed Player object path. .UNINDENT .UNINDENT .\" Generated by docutils manpage writer. . bluez-5.82/doc/PaxHeaders/sco.rst0000644000000000000000000000005014772767672013762 xustar0020 atime=1743515579 20 ctime=1743591290 bluez-5.82/doc/sco.rst0000644000000000000000000001414514772767672013450 0ustar00rootroot=== sco === ------------- SCO transport ------------- :Version: BlueZ :Copyright: Free use of this software is granted under the terms of the GNU Lesser General Public Licenses (LGPL). :Date: March 2025 :Manual section: 7 :Manual group: Linux System Administration SYNOPSIS ======== .. code-block:: #include #include #include sco_socket = socket(PF_BLUETOOTH, SOCK_SEQPACKET, BTPROTO_SCO); DESCRIPTION =========== The SCO logical transport, is a symmetric, point-to-point transport between the Central and a specific Peripheral. The SCO logical transport reserves slots and can therefore be considered as a circuit-switched connection between the Central and the Peripheral. In addition to the reserved slots, when eSCO is supported, a retransmission window follows immediately after. Together, the reserved slots and the retransmission window form the complete eSCO window. SOCKET ADDRESS ============== .. code-block:: struct sockaddr_sco { sa_family_t sco_family; bdaddr_t sco_bdaddr; }; Example: .. code-block:: struct sockaddr_sco addr; memset(&addr, 0, sizeof(addr)); addr.sco_family = AF_BLUETOOTH; bacpy(&addr.sco_bdaddr, bdaddr); SOCKET OPTIONS ============== The socket options listed below can be set by using **setsockopt(2)** and read with **getsockopt(2)** with the socket level set to SOL_BLUETOOTH. BT_SECURITY (since Linux 2.6.30) -------------------------------- Channel security level, possible values: .. csv-table:: :header: "Value", "Security Level", "Link Key Type", "Encryption" :widths: auto **BT_SECURITY_SDP**, 0 (SDP Only), None, Not required **BT_SECURITY_LOW**, 1 (Low), Unauthenticated, Not required **BT_SECURITY_MEDIUM**, 2 (Medium - default), Unauthenticated, Desired **BT_SECURITY_HIGH**, 3 (High), Authenticated, Required **BT_SECURITY_FIPS** (since Linux 3.15), 4 (Secure Only), Authenticated (P-256 based Secure Simple Pairing and Secure Authentication), Required Example: .. code-block:: int level = BT_SECURITY_HIGH; int err = setsockopt(sco_socket, SOL_BLUETOOTH, BT_SECURITY, &level, sizeof(level)); if (err == -1) { perror("setsockopt"); return 1; } BT_DEFER_SETUP (since Linux 2.6.30) ----------------------------------- Channel defer connection setup, this control if the connection procedure needs to be authorized by userspace before responding which allows authorization at profile level, possible values: .. csv-table:: :header: "Value", "Description", "Authorization" :widths: auto **0**, Disable (default), Not required **1**, Enable, Required Example: .. code-block:: int defer_setup = 1; int err = setsockopt(sco_socket, SOL_BLUETOOTH, BT_DEFER_SETUP, &defer_setup, sizeof(defer_setup)); if (err == -1) { perror("setsockopt"); return err; } err = listen(sco_socket, 5); if (err) { perror("listen"); return err; } struct sockaddr_sco remote_addr = {0}; socklen_t addr_len = sizeof(remote_addr); int new_socket = accept(sco_socket, (struct sockaddr*)&remote_addr, &addr_len); if (new_socket < 0) { perror("accept"); return new_socket; } /* To complete the connection setup of new_socket read 1 byte */ char c; struct pollfd pfd; memset(&pfd, 0, sizeof(pfd)); pfd.fd = new_socket; pfd.events = POLLOUT; err = poll(&pfd, 1, 0); if (err) { perror("poll"); return err; } if (!(pfd.revents & POLLOUT)) { err = read(sk, &c, 1); if (err < 0) { perror("read"); return err; } } BT_VOICE (since Linux 3.11) ----------------------------- Transport voice settings, possible values: .. code-block:: struct bt_voice { uint16_t setting; }; .. csv-table:: :header: "Define", "Value", "Description" :widths: auto **BT_VOICE_TRANSPARENT**, 0x0003, Transparent output **BT_VOICE_CVSD_16BIT**, 0x0060, C-VSD output PCM 16-bit input **BT_VOICE_TRANSPARENT_16BIT**, 0x0063, Transparent output PCM 16-bit input Example: .. code-block:: struct bt_voice voice; memset(&voice, 0, sizeof(voice)); voice.setting = BT_VOICE_TRANSPARENT; int err = setsockopt(sco_socket, SOL_BLUETOOTH, BT_VOICE, &voice, sizeof(voice)); if (err == -1) { perror("setsockopt"); return 1; } BT_PHY (since Linux 5.10) ------------------------- Transport supported PHY(s), possible values: .. csv-table:: :header: "Define", "Value", "Description" :widths: auto **BT_PHY_BR_1M_1SLOT**, BIT 0, BR 1Mbps 1SLOT **BT_PHY_BR_1M_3SLOT**, BIT 1, BR 1Mbps 3SLOT **BT_PHY_BR_2M_1SLOT**, BIT 3, EDR 2Mbps 1SLOT **BT_PHY_BR_2M_3SLOT**, BIT 4, EDR 2Mbps 3SLOT **BT_PHY_BR_3M_1SLOT**, BIT 6, EDR 3Mbps 1SLOT **BT_PHY_BR_3M_3SLOT**, BIT 7, EDR 3Mbps 3SLOT BT_CODEC (since Linux 5.14) --------------------------- Transport codec offload, possible values: .. code-block:: struct bt_codec { uint8_t id; uint16_t cid; uint16_t vid; uint8_t data_path_id; uint8_t num_caps; struct codec_caps { uint8_t len; uint8_t data[]; } caps[]; } __attribute__((packed)); struct bt_codecs { uint8_t num_codecs; struct bt_codec codecs[]; } __attribute__((packed)); Example: .. code-block:: char buffer[sizeof(struct bt_codecs) + sizeof(struct bt_codec)]; struct bt_codec *codecs = (void *)buffer; memset(codecs, 0, sizeof(codecs)); codec->num_codecs = 1; codecs->codecs[0].id = 0x05; codecs->codecs[0].data_path_id = 1; int err = setsockopt(sco_socket, SOL_BLUETOOTH, BT_CODEC, codecs, sizeof(buffer)); if (err == -1) { perror("setsockopt"); return 1; } RESOURCES ========= http://www.bluez.org REPORTING BUGS ============== linux-bluetooth@vger.kernel.org SEE ALSO ======== socket(7), scotest(1) bluez-5.82/doc/PaxHeaders/sco.70000644000000000000000000000005014773213425013277 xustar0020 atime=1743591189 20 ctime=1743591290 bluez-5.82/doc/sco.70000644000000000000000000001556614773213425012775 0ustar00rootroot'\" t .\" Man page generated from reStructuredText. . . .nr rst2man-indent-level 0 . .de1 rstReportMargin \\$1 \\n[an-margin] level \\n[rst2man-indent-level] level margin: \\n[rst2man-indent\\n[rst2man-indent-level]] - \\n[rst2man-indent0] \\n[rst2man-indent1] \\n[rst2man-indent2] .. .de1 INDENT .\" .rstReportMargin pre: . RS \\$1 . nr rst2man-indent\\n[rst2man-indent-level] \\n[an-margin] . nr rst2man-indent-level +1 .\" .rstReportMargin post: .. .de UNINDENT . RE .\" indent \\n[an-margin] .\" old: \\n[rst2man-indent\\n[rst2man-indent-level]] .nr rst2man-indent-level -1 .\" new: \\n[rst2man-indent\\n[rst2man-indent-level]] .in \\n[rst2man-indent\\n[rst2man-indent-level]]u .. .TH "SCO" "7" "March 2025" "BlueZ" "Linux System Administration" .SH NAME sco \- SCO transport .SH SYNOPSIS .INDENT 0.0 .INDENT 3.5 .sp .EX #include #include #include sco_socket = socket(PF_BLUETOOTH, SOCK_SEQPACKET, BTPROTO_SCO); .EE .UNINDENT .UNINDENT .SH DESCRIPTION .sp The SCO logical transport, is a symmetric, point\-to\-point transport between the Central and a specific Peripheral. The SCO logical transport reserves slots and can therefore be considered as a circuit\-switched connection between the Central and the Peripheral. .sp In addition to the reserved slots, when eSCO is supported, a retransmission window follows immediately after. Together, the reserved slots and the retransmission window form the complete eSCO window. .SH SOCKET ADDRESS .INDENT 0.0 .INDENT 3.5 .sp .EX struct sockaddr_sco { sa_family_t sco_family; bdaddr_t sco_bdaddr; }; .EE .UNINDENT .UNINDENT .sp Example: .INDENT 0.0 .INDENT 3.5 .sp .EX struct sockaddr_sco addr; memset(&addr, 0, sizeof(addr)); addr.sco_family = AF_BLUETOOTH; bacpy(&addr.sco_bdaddr, bdaddr); .EE .UNINDENT .UNINDENT .SH SOCKET OPTIONS .sp The socket options listed below can be set by using \fBsetsockopt(2)\fP and read with \fBgetsockopt(2)\fP with the socket level set to SOL_BLUETOOTH. .SS BT_SECURITY (since Linux 2.6.30) .sp Channel security level, possible values: .TS box center; l|l|l|l. T{ Value T} T{ Security Level T} T{ Link Key Type T} T{ Encryption T} _ T{ \fBBT_SECURITY_SDP\fP T} T{ 0 (SDP Only) T} T{ None T} T{ Not required T} _ T{ \fBBT_SECURITY_LOW\fP T} T{ 1 (Low) T} T{ Unauthenticated T} T{ Not required T} _ T{ \fBBT_SECURITY_MEDIUM\fP T} T{ 2 (Medium \- default) T} T{ Unauthenticated T} T{ Desired T} _ T{ \fBBT_SECURITY_HIGH\fP T} T{ 3 (High) T} T{ Authenticated T} T{ Required T} _ T{ \fBBT_SECURITY_FIPS\fP (since Linux 3.15) T} T{ 4 (Secure Only) T} T{ Authenticated (P\-256 based Secure Simple Pairing and Secure Authentication) T} T{ Required T} .TE .sp Example: .INDENT 0.0 .INDENT 3.5 .sp .EX int level = BT_SECURITY_HIGH; int err = setsockopt(sco_socket, SOL_BLUETOOTH, BT_SECURITY, &level, sizeof(level)); if (err == \-1) { perror(\(dqsetsockopt\(dq); return 1; } .EE .UNINDENT .UNINDENT .SS BT_DEFER_SETUP (since Linux 2.6.30) .sp Channel defer connection setup, this control if the connection procedure needs to be authorized by userspace before responding which allows authorization at profile level, possible values: .TS box center; l|l|l. T{ Value T} T{ Description T} T{ Authorization T} _ T{ \fB0\fP T} T{ Disable (default) T} T{ Not required T} _ T{ \fB1\fP T} T{ Enable T} T{ Required T} .TE .sp Example: .INDENT 0.0 .INDENT 3.5 .sp .EX int defer_setup = 1; int err = setsockopt(sco_socket, SOL_BLUETOOTH, BT_DEFER_SETUP, &defer_setup, sizeof(defer_setup)); if (err == \-1) { perror(\(dqsetsockopt\(dq); return err; } err = listen(sco_socket, 5); if (err) { perror(\(dqlisten\(dq); return err; } struct sockaddr_sco remote_addr = {0}; socklen_t addr_len = sizeof(remote_addr); int new_socket = accept(sco_socket, (struct sockaddr*)&remote_addr, &addr_len); if (new_socket < 0) { perror(\(dqaccept\(dq); return new_socket; } /* To complete the connection setup of new_socket read 1 byte */ char c; struct pollfd pfd; memset(&pfd, 0, sizeof(pfd)); pfd.fd = new_socket; pfd.events = POLLOUT; err = poll(&pfd, 1, 0); if (err) { perror(\(dqpoll\(dq); return err; } if (!(pfd.revents & POLLOUT)) { err = read(sk, &c, 1); if (err < 0) { perror(\(dqread\(dq); return err; } } .EE .UNINDENT .UNINDENT .SS BT_VOICE (since Linux 3.11) .sp Transport voice settings, possible values: .INDENT 0.0 .INDENT 3.5 .sp .EX struct bt_voice { uint16_t setting; }; .EE .UNINDENT .UNINDENT .TS box center; l|l|l. T{ Define T} T{ Value T} T{ Description T} _ T{ \fBBT_VOICE_TRANSPARENT\fP T} T{ 0x0003 T} T{ Transparent output T} _ T{ \fBBT_VOICE_CVSD_16BIT\fP T} T{ 0x0060 T} T{ C\-VSD output PCM 16\-bit input T} _ T{ \fBBT_VOICE_TRANSPARENT_16BIT\fP T} T{ 0x0063 T} T{ Transparent output PCM 16\-bit input T} .TE .sp Example: .INDENT 0.0 .INDENT 3.5 .sp .EX struct bt_voice voice; memset(&voice, 0, sizeof(voice)); voice.setting = BT_VOICE_TRANSPARENT; int err = setsockopt(sco_socket, SOL_BLUETOOTH, BT_VOICE, &voice, sizeof(voice)); if (err == \-1) { perror(\(dqsetsockopt\(dq); return 1; } .EE .UNINDENT .UNINDENT .SS BT_PHY (since Linux 5.10) .sp Transport supported PHY(s), possible values: .TS box center; l|l|l. T{ Define T} T{ Value T} T{ Description T} _ T{ \fBBT_PHY_BR_1M_1SLOT\fP T} T{ BIT 0 T} T{ BR 1Mbps 1SLOT T} _ T{ \fBBT_PHY_BR_1M_3SLOT\fP T} T{ BIT 1 T} T{ BR 1Mbps 3SLOT T} _ T{ \fBBT_PHY_BR_2M_1SLOT\fP T} T{ BIT 3 T} T{ EDR 2Mbps 1SLOT T} _ T{ \fBBT_PHY_BR_2M_3SLOT\fP T} T{ BIT 4 T} T{ EDR 2Mbps 3SLOT T} _ T{ \fBBT_PHY_BR_3M_1SLOT\fP T} T{ BIT 6 T} T{ EDR 3Mbps 1SLOT T} _ T{ \fBBT_PHY_BR_3M_3SLOT\fP T} T{ BIT 7 T} T{ EDR 3Mbps 3SLOT T} .TE .SS BT_CODEC (since Linux 5.14) .sp Transport codec offload, possible values: .INDENT 0.0 .INDENT 3.5 .sp .EX struct bt_codec { uint8_t id; uint16_t cid; uint16_t vid; uint8_t data_path_id; uint8_t num_caps; struct codec_caps { uint8_t len; uint8_t data[]; } caps[]; } __attribute__((packed)); struct bt_codecs { uint8_t num_codecs; struct bt_codec codecs[]; } __attribute__((packed)); .EE .UNINDENT .UNINDENT .sp Example: .INDENT 0.0 .INDENT 3.5 .sp .EX char buffer[sizeof(struct bt_codecs) + sizeof(struct bt_codec)]; struct bt_codec *codecs = (void *)buffer; memset(codecs, 0, sizeof(codecs)); codec\->num_codecs = 1; codecs\->codecs[0].id = 0x05; codecs\->codecs[0].data_path_id = 1; int err = setsockopt(sco_socket, SOL_BLUETOOTH, BT_CODEC, codecs, sizeof(buffer)); if (err == \-1) { perror(\(dqsetsockopt\(dq); return 1; } .EE .UNINDENT .UNINDENT .SH RESOURCES .sp .SH REPORTING BUGS .sp .SH SEE ALSO .sp socket(7), scotest(1) .SH COPYRIGHT Free use of this software is granted under the terms of the GNU Lesser General Public Licenses (LGPL). .\" Generated by docutils manpage writer. . bluez-5.82/doc/PaxHeaders/org.bluez.AdminPolicySet.rst0000644000000000000000000000005014536422313017742 xustar0020 atime=1743516790 20 ctime=1743591290 bluez-5.82/doc/org.bluez.AdminPolicySet.rst0000644000000000000000000000262414536422313017427 0ustar00rootroot======================== org.bluez.AdminPolicySet ======================== -------------------------------------------- BlueZ D-Bus AdminPolicySet API documentation -------------------------------------------- :Version: BlueZ :Date: October 2023 :Manual section: 5 :Manual group: Linux System Administration Description ============ This API provides methods to control the behavior of **bluetoothd(8)** as an administrator. Interface AdminPolicySet1 provides methods to set policies. Once the policy is set successfully, it will affect all clients and stay persistently even after restarting **bluetoothd(8)**. The only way to clear it is to overwrite the policy with the same method. Interface ========= :Service: org.bluez :Interface: org.bluez.AdminPolicySet1 [experimental] :Object path: [variable prefix]/{hci0,hci1,...} Methods ------- void SetServiceAllowList(array{string} UUIDs) ````````````````````````````````````````````` Sets the service allowlist by specifying service UUIDs. When called, **bluetoothd(8)** will block incoming and outgoing connections to the service not in UUIDs for all of the clients. Any subsequent calls to this method will supersede any previously set allowlist values. Calling this method with an empty array will allow any service UUIDs to be used. The default value is an empty array. Possible errors: :org.bluez.Error.InvalidArguments: :org.bluez.Error.Failed: bluez-5.82/doc/PaxHeaders/org.bluez.AdvertisementMonitorManager.50000644000000000000000000000005014773213470022074 xustar0020 atime=1743591224 20 ctime=1743591291 bluez-5.82/doc/org.bluez.AdvertisementMonitorManager.50000644000000000000000000000650514773213470021563 0ustar00rootroot.\" Man page generated from reStructuredText. . . .nr rst2man-indent-level 0 . .de1 rstReportMargin \\$1 \\n[an-margin] level \\n[rst2man-indent-level] level margin: \\n[rst2man-indent\\n[rst2man-indent-level]] - \\n[rst2man-indent0] \\n[rst2man-indent1] \\n[rst2man-indent2] .. .de1 INDENT .\" .rstReportMargin pre: . RS \\$1 . nr rst2man-indent\\n[rst2man-indent-level] \\n[an-margin] . nr rst2man-indent-level +1 .\" .rstReportMargin post: .. .de UNINDENT . RE .\" indent \\n[an-margin] .\" old: \\n[rst2man-indent\\n[rst2man-indent-level]] .nr rst2man-indent-level -1 .\" new: \\n[rst2man-indent\\n[rst2man-indent-level]] .in \\n[rst2man-indent\\n[rst2man-indent-level]]u .. .TH "ORG.BLUEZ.ADVERTISEMENTMONITORMANAGER" "5" "October 2023" "BlueZ" "Linux System Administration" .SH NAME org.bluez.AdvertisementMonitorManager \- BlueZ D-Bus AdvertisementMonitorManager API documentation .SH INTERFACE .sp Service org.bluez Interface org.bluez.AdvertisementMonitorManager1 [experimental] Object path /org/bluez/{hci0,hci1,...} .SS Methods .SS void RegisterMonitor(object application) .INDENT 0.0 .INDENT 3.5 Registers the root path of a hierarchy of advertisement monitors implementing \fBorg.bluez.AdvertisementMonitor(5)\fP\&. .sp The application object path together with the D\-Bus ystem bus connection ID define the identification of the application registering advertisement monitors. .sp Once a root path is registered by a client via this method, the client can freely expose/unexpose advertisement monitors without re\-registering the root path again. After use, the client should call \fBUnregisterMonitor()\fP method to invalidate the advertisement monitors. .sp Possible errors: .INDENT 0.0 .TP .B org.bluez.Error.InvalidArguments .TP .B org.bluez.Error.AlreadyExists .TP .B org.bluez.Error.Failed .UNINDENT .UNINDENT .UNINDENT .SS void UnregisterMonitor(object application) .INDENT 0.0 .INDENT 3.5 Unregisters a hierarchy of advertisement monitors that has been previously registered with \fBRegisterMonitor()\fP\&. The object path parameter must match the same value that has been used on registration. .sp Upon unregistration, the advertisement monitor(s) should expect to receive \fBRelease()\fP method as the signal that the advertisement monitor(s) has been deactivated. .sp Possible errors: .INDENT 0.0 .TP .B org.bluez.Error.InvalidArguments .TP .B org.bluez.Error.DoesNotExist .UNINDENT .UNINDENT .UNINDENT .SS Properties .SS array{string} SupportedMonitorTypes [read\-only] .INDENT 0.0 .INDENT 3.5 This lists the supported types of advertisement monitors. An application should check this before instantiate and expose an object of \fBorg.bluez.AdvertisementMonitor(5)\fP\&. .sp Possible values: .INDENT 0.0 .TP .B \(dqor_patterns\(dq Patterns with logic OR applied. With this type, property \fBPatterns\fP must exist and has at least one pattern. .UNINDENT .UNINDENT .UNINDENT .SS array{string} SupportedFeatures [read\-only] .INDENT 0.0 .INDENT 3.5 This lists the features of advertisement monitoring supported by \fBbluetoothd(8)\fP\&. .sp Possible values: .INDENT 0.0 .TP .B \(dqcontroller\-patterns\(dq If the controller is capable of performing advertisement monitoring by patterns, \fBbluetoothd(8)\fP would offload the patterns to the controller to reduce power consumption. .UNINDENT .UNINDENT .UNINDENT .\" Generated by docutils manpage writer. . bluez-5.82/doc/PaxHeaders/org.bluez.GattProfile.50000644000000000000000000000005014773213461016637 xustar0020 atime=1743591217 20 ctime=1743591291 bluez-5.82/doc/org.bluez.GattProfile.50000644000000000000000000000330714773213461016323 0ustar00rootroot.\" Man page generated from reStructuredText. . . .nr rst2man-indent-level 0 . .de1 rstReportMargin \\$1 \\n[an-margin] level \\n[rst2man-indent-level] level margin: \\n[rst2man-indent\\n[rst2man-indent-level]] - \\n[rst2man-indent0] \\n[rst2man-indent1] \\n[rst2man-indent2] .. .de1 INDENT .\" .rstReportMargin pre: . RS \\$1 . nr rst2man-indent\\n[rst2man-indent-level] \\n[an-margin] . nr rst2man-indent-level +1 .\" .rstReportMargin post: .. .de UNINDENT . RE .\" indent \\n[an-margin] .\" old: \\n[rst2man-indent\\n[rst2man-indent-level]] .nr rst2man-indent-level -1 .\" new: \\n[rst2man-indent\\n[rst2man-indent-level]] .in \\n[rst2man-indent\\n[rst2man-indent-level]]u .. .TH "ORG.BLUEZ.GATTPROFILE" "5" "October 2023" "BlueZ" "Linux System Administration" .SH NAME org.bluez.GattProfile \- BlueZ D-Bus GattProfile API documentation .SH DESCRIPTION .sp Local profile (GATT client) instance. By registering this type of object an application effectively indicates support for a specific GATT profile and requests automatic connections to be established to devices supporting it. .SH INTERFACE .INDENT 0.0 .TP .B Service .TP .B Interface org.bluez.GattProfile1 .TP .B Object path .UNINDENT .SS Methods .SS void Release() .INDENT 0.0 .INDENT 3.5 This method gets called when the service daemon unregisters the profile. The profile can use it to do cleanup tasks. There is no need to unregister the profile, because when this method gets called it has already been unregistered. .UNINDENT .UNINDENT .SS Properties .SS array{string} UUIDs [read\-only] .INDENT 0.0 .INDENT 3.5 128\-bit GATT service UUIDs to auto connect. .UNINDENT .UNINDENT .\" Generated by docutils manpage writer. . bluez-5.82/doc/PaxHeaders/org.bluez.AgentManager.rst0000644000000000000000000000005014536422313017407 xustar0020 atime=1743516779 20 ctime=1743591290 bluez-5.82/doc/org.bluez.AgentManager.rst0000644000000000000000000000376114536422313017077 0ustar00rootroot====================== org.bluez.AgentManager ====================== ------------------------------------------ BlueZ D-Bus AgentManager API documentation ------------------------------------------ :Version: BlueZ :Date: October 2023 :Manual section: 5 :Manual group: Linux System Administration Interface ========= :Service: org.bluez :Interface: org.bluez.AgentManager1 :Object path: /org/bluez Methods ------- void RegisterAgent(object agent, string capability) ``````````````````````````````````````````````````` Registers pairing agent. The object path defines the path of the agent that will be called when user input is needed and must implement **org.bluez.Agent(5)** interface. Every application can register its own agent and for all actions triggered by that application its agent is used. It is not required by an application to register an agent. If an application does chooses to not register an agent, the default agent is used. This is on most cases a good idea. Only application like a pairing wizard should register their own agent. An application can only register one agent. Multiple agents per application is not supported. Possible capability values: :"": Fallback to "KeyboardDisplay". :"DisplayOnly": :"DisplayYesNo": :"KeyboardOnly": :"NoInputNoOutput": :"KeyboardDisplay": Possible errors: :org.bluez.Error.InvalidArguments: :org.bluez.Error.AlreadyExists: void UnregisterAgent(object agent) `````````````````````````````````` Unregisters an agent that has been previously registered using **RegisterAgent**. The object path parameter must match the same value that has been used on registration. Possible errors: :org.bluez.Error.DoesNotExist: void RequestDefaultAgent(object agent) `````````````````````````````````````` Requests to make the application agent the default agent. The application is required to register an agent. Special permission might be required to become the default agent. Possible errors: :org.bluez.Error.DoesNotExist: bluez-5.82/doc/PaxHeaders/org.bluez.MediaTransport.50000644000000000000000000000005014773213456017357 xustar0020 atime=1743591214 20 ctime=1743591291 bluez-5.82/doc/org.bluez.MediaTransport.50000644000000000000000000001754114773213456017050 0ustar00rootroot.\" Man page generated from reStructuredText. . . .nr rst2man-indent-level 0 . .de1 rstReportMargin \\$1 \\n[an-margin] level \\n[rst2man-indent-level] level margin: \\n[rst2man-indent\\n[rst2man-indent-level]] - \\n[rst2man-indent0] \\n[rst2man-indent1] \\n[rst2man-indent2] .. .de1 INDENT .\" .rstReportMargin pre: . RS \\$1 . nr rst2man-indent\\n[rst2man-indent-level] \\n[an-margin] . nr rst2man-indent-level +1 .\" .rstReportMargin post: .. .de UNINDENT . RE .\" indent \\n[an-margin] .\" old: \\n[rst2man-indent\\n[rst2man-indent-level]] .nr rst2man-indent-level -1 .\" new: \\n[rst2man-indent\\n[rst2man-indent-level]] .in \\n[rst2man-indent\\n[rst2man-indent-level]]u .. .TH "ORG.BLUEZ.MEDIATRANSPORT" "5" "July 2024" "BlueZ" "Linux System Administration" .SH NAME org.bluez.MediaTransport \- BlueZ D-Bus MediaTransport API documentation .SH INTERFACE .INDENT 0.0 .TP .B Service org.bluez .TP .B Interface org.bluez.MediaTransport1 .TP .B Object path [variable prefix]/{hci0,hci1,...}/dev_XX_XX_XX_XX_XX_XX/fdX .UNINDENT .SS Methods .SS fd, uint16, uint16 Acquire() .INDENT 0.0 .INDENT 3.5 Acquire transport file descriptor and the MTU for read and write respectively. .sp Possible Errors: .INDENT 0.0 .TP .B org.bluez.Error.NotAuthorized .TP .B org.bluez.Error.Failed .UNINDENT .UNINDENT .UNINDENT .SS fd, uint16, uint16 TryAcquire() .INDENT 0.0 .INDENT 3.5 Acquire transport file descriptor only if the transport is in \(dqpending\(dq state at the time the message is received by BlueZ. Otherwise no request will be sent to the remote device and the function will just fail with org.bluez.Error.NotAvailable. .sp Possible Errors: .INDENT 0.0 .TP .B org.bluez.Error.NotAuthorized .TP .B org.bluez.Error.Failed .TP .B org.bluez.Error.NotAvailable .UNINDENT .UNINDENT .UNINDENT .SS void Release() .INDENT 0.0 .INDENT 3.5 Releases file descriptor. .UNINDENT .UNINDENT .SS void Select() .INDENT 0.0 .INDENT 3.5 Applicable only for transports created by a broadcast sink. This moves the transport from \(aqidle\(aq to \(aqbroadcasting\(aq. This allows the user to select which BISes he wishes to sync to via a 2 step process: 1) the user calls the method, changing the transport\(aqs state to broadcasting 2) the audio server detects that the transport is in the \(aqbroadcasting\(aq state and automatically acquires it .sp Possible Errors: .INDENT 0.0 .TP .B org.bluez.Error.NotAuthorized .UNINDENT .UNINDENT .UNINDENT .SS void Unselect() .INDENT 0.0 .INDENT 3.5 Applicable only for transports created by a broadcast sink. This moves the transport from \(aqbroadcasting\(aq or \(aqactive\(aq to \(aqidle\(aq. This allows the user to terminate the sync to a BIS to via a 2 step process: 1) the user calls this method, changing the transport\(aqs state to idle 2) the audio server detects this event and releases the transport .sp Possible Errors: .INDENT 0.0 .TP .B org.bluez.Error.NotAuthorized .UNINDENT .UNINDENT .UNINDENT .SS Properties .SS object Device [readonly] .INDENT 0.0 .INDENT 3.5 Device object which the transport is connected to. .UNINDENT .UNINDENT .SS string UUID [readonly] .INDENT 0.0 .INDENT 3.5 UUID of the profile which the transport is for. .UNINDENT .UNINDENT .SS byte Codec [readonly] .INDENT 0.0 .INDENT 3.5 Assigned number of codec that the transport support. The values should match the profile specification which is indicated by the UUID. .UNINDENT .UNINDENT .SS array{byte} Configuration [readonly] .INDENT 0.0 .INDENT 3.5 Configuration blob, it is used as it is so the size and byte order must match. .UNINDENT .UNINDENT .SS string State [readonly] .INDENT 0.0 .INDENT 3.5 Indicates the state of the transport. Possible values are: .INDENT 0.0 .TP .B \(dqidle\(dq not streaming .TP .B \(dqpending\(dq streaming but not acquired .TP .B \(dqbroadcasting\(dq streaming but not acquired, applicable only for transports created by a broadcast sink .TP .B \(dqactive\(dq streaming and acquired .UNINDENT .UNINDENT .UNINDENT .SS uint16 Delay [readwrite, optional] .INDENT 0.0 .INDENT 3.5 Transport delay in 1/10 of millisecond, this property is only writeable when the transport corresponds to a sink endpoint and it was acquired by the sender. .UNINDENT .UNINDENT .SS uint16 Volume [readwrite, optional] .INDENT 0.0 .INDENT 3.5 Indicates volume level of the transport, this property is only writeable when the transport was acquired by the sender. .INDENT 0.0 .TP .B Possible Values: 0\-127 (A2DP) 0\-255 (BAP) .UNINDENT .UNINDENT .UNINDENT .SS object Endpoint [readonly, optional, experimental] .INDENT 0.0 .INDENT 3.5 Endpoint object which the transport is associated with. .UNINDENT .UNINDENT .SS uint32 Location [readonly, ISO only, experimental] .INDENT 0.0 .INDENT 3.5 Indicates transport Audio Location. .UNINDENT .UNINDENT .SS array{byte} Metadata [readwrite, ISO Only, experimental] .INDENT 0.0 .INDENT 3.5 Indicates transport Metadata. .UNINDENT .UNINDENT .SS array{object} Links [readonly, optional, CIS only, experimental] .INDENT 0.0 .INDENT 3.5 Linked transport objects which the transport is associated with. .UNINDENT .UNINDENT .SS array{object} Links [readwrite, BIS only, experimental] .INDENT 0.0 .INDENT 3.5 For a Broadcast Sink, the BIG sync procedure requires all desired streams to be enumerated from the start and it cannot be later reconfigured by adding or removing BISes. To avoid terminating and recreating the BIG sync everytime a new transport is selected for acquire, all transports selected via Transport.Select need to be linked together. When the first transport is acquired via Transport.Acquire, all links are included in the BIG sync command. An acquired transport will create and set fds for all its links. Then, each link needs to be acquired separately, to get the fd and start receiving audio. .UNINDENT .UNINDENT .SS dict QoS [readwrite, optional, ISO only, experimental] .INDENT 0.0 .INDENT 3.5 Only present when QoS is configured. .sp Possible values for Unicast: .INDENT 0.0 .TP .B byte CIG Indicates configured CIG. .sp Possible values: .INDENT 7.0 .TP .B 0x00 \- 0xef Valid ID range. .TP .B 0xff Auto allocate. .UNINDENT .TP .B byte CIS Indicates configured CIS. .sp Possible values: .INDENT 7.0 .TP .B 0x00 \- 0xef Valid ID range. .TP .B 0xff Auto allocate. .UNINDENT .TP .B byte Framing Indicates configured framing. .sp Possible values: .INDENT 7.0 .TP .B 0x00 Unframed. .TP .B 0x01 Framed. .UNINDENT .TP .B uint32 PresentationDelay Indicates configured transport presentation delay (us). .TP .B byte TargetLatency Indicates the requested target latency. .sp Possible values: .INDENT 7.0 .TP .B 0x01 Low Latency. .TP .B 0x02 Balanced Latency/Reliability. .TP .B 0x03 High Reliability. .UNINDENT .UNINDENT .sp Possible values for Broadcast: .INDENT 0.0 .TP .B byte BIG Indicates configured QoS BIG. .TP .B byte BIS Indicates configured BIS. .TP .B byte SyncFactor Indicates configured broadcast sync factor. .TP .B byte Packing Indicates configured packing. .TP .B byte Framing Indicates configured framing. .TP .B array{byte} BCode Indicates the string used for encryption/decryption. .TP .B byte encryption Indicates if the stream is encrypted. .TP .B byte Options Indicates configured broadcast options. .TP .B uint16 Skip Indicates configured broadcast skip. .TP .B byte SyncTimeout Indicates configured broadcast sync timeout. .TP .B byte SyncType Indicates configured broadcast sync CTE type. .TP .B byte MSE Indicates configured broadcast MSE. .TP .B uint16 Timeout Indicates configured broadcast timeout. .UNINDENT .sp Possible values for both Unicast and Broadcast: .INDENT 0.0 .TP .B uint32 Interval Indicates configured ISO interval (us). .TP .B uint16 Latency Indicates configured transport latency (ms). .TP .B uint16 SDU Indicates configured maximum SDU. .TP .B byte PHY Indicates configured PHY. .sp Possible values: .INDENT 7.0 .TP .B bit 0 LE 1M .TP .B bit 1 LE 2M .TP .B bit 2 LE Coded .UNINDENT .TP .B byte Retransmissions Indicates configured retransmissions. .UNINDENT .UNINDENT .UNINDENT .\" Generated by docutils manpage writer. . bluez-5.82/doc/PaxHeaders/org.bluez.obex.Agent.50000644000000000000000000000005014773213505016410 xustar0020 atime=1743591237 20 ctime=1743591291 bluez-5.82/doc/org.bluez.obex.Agent.50000644000000000000000000000404714773213505016076 0ustar00rootroot.\" Man page generated from reStructuredText. . . .nr rst2man-indent-level 0 . .de1 rstReportMargin \\$1 \\n[an-margin] level \\n[rst2man-indent-level] level margin: \\n[rst2man-indent\\n[rst2man-indent-level]] - \\n[rst2man-indent0] \\n[rst2man-indent1] \\n[rst2man-indent2] .. .de1 INDENT .\" .rstReportMargin pre: . RS \\$1 . nr rst2man-indent\\n[rst2man-indent-level] \\n[an-margin] . nr rst2man-indent-level +1 .\" .rstReportMargin post: .. .de UNINDENT . RE .\" indent \\n[an-margin] .\" old: \\n[rst2man-indent\\n[rst2man-indent-level]] .nr rst2man-indent-level -1 .\" new: \\n[rst2man-indent\\n[rst2man-indent-level]] .in \\n[rst2man-indent\\n[rst2man-indent-level]]u .. .TH "ORG.BLUEZ.OBEX.AGENT" "5" "October 2023" "BlueZ" "Linux System Administration" .SH NAME org.bluez.obex.Agent \- BlueZ D-Bus OBEX Agent API documentation .SH INTERFACE .sp ;Service: unique name :Interface: org.bluez.obex.Agent1 :Object path: freely definable .SS Methods .SS void Release() .INDENT 0.0 .INDENT 3.5 This method gets called when \fBobexd(8)\fP daemon unregisters the agent. An agent can use it to do cleanup tasks. There is no need to unregister the agent, because when this method gets called it has already been unregistered. .UNINDENT .UNINDENT .SS string AuthorizePush(object transfer) .INDENT 0.0 .INDENT 3.5 This method gets called when the \fBobexd(8)\fP needs to accept/reject a Bluetooth object push request. .sp Returns the full path (including the filename) or the folder name suffixed with \(aq/\(aq where the object shall be stored. .sp The transfer object, see \fBorg.bluez.obex.Transfer(5)\fP will contain a Filename property that contains the default location and name that can be returned. .sp Possible errors: .INDENT 0.0 .TP .B org.bluez.obex.Error.Rejected .TP .B org.bluez.obex.Error.Canceled .UNINDENT .UNINDENT .UNINDENT .SS void Cancel() .INDENT 0.0 .INDENT 3.5 This method gets called to indicate that the agent request failed before a reply was returned. It cancels the previous request. .UNINDENT .UNINDENT .\" Generated by docutils manpage writer. . bluez-5.82/doc/PaxHeaders/org.bluez.obex.Transfer.50000644000000000000000000000005014773213474017143 xustar0020 atime=1743591228 20 ctime=1743591291 bluez-5.82/doc/org.bluez.obex.Transfer.50000644000000000000000000000703314773213474016627 0ustar00rootroot.\" Man page generated from reStructuredText. . . .nr rst2man-indent-level 0 . .de1 rstReportMargin \\$1 \\n[an-margin] level \\n[rst2man-indent-level] level margin: \\n[rst2man-indent\\n[rst2man-indent-level]] - \\n[rst2man-indent0] \\n[rst2man-indent1] \\n[rst2man-indent2] .. .de1 INDENT .\" .rstReportMargin pre: . RS \\$1 . nr rst2man-indent\\n[rst2man-indent-level] \\n[an-margin] . nr rst2man-indent-level +1 .\" .rstReportMargin post: .. .de UNINDENT . RE .\" indent \\n[an-margin] .\" old: \\n[rst2man-indent\\n[rst2man-indent-level]] .nr rst2man-indent-level -1 .\" new: \\n[rst2man-indent\\n[rst2man-indent-level]] .in \\n[rst2man-indent\\n[rst2man-indent-level]]u .. .TH "ORG.BLUEZ.OBEX.TRANSFER" "5" "October 2023" "BlueZ" "Linux System Administration" .SH NAME org.bluez.obex.Transfer \- BlueZ D-Bus OBEX Transfer API documentation .SH INTERFACE .INDENT 0.0 .TP .B Service org.bluez.obex .TP .B Interface org.bluez.obex.Transfer1 .TP .B Object path [Session object path]/transfer{#} .UNINDENT .SS Methods .SS void Cancel() .INDENT 0.0 .INDENT 3.5 Cancels the current transference. .sp Possible errors: .INDENT 0.0 .TP .B org.bluez.obex.Error.NotAuthorized .TP .B org.bluez.obex.Error.InProgress .TP .B org.bluez.obex.Error.Failed .UNINDENT .UNINDENT .UNINDENT .SS void Suspend() .INDENT 0.0 .INDENT 3.5 Suspends transference. .sp Possible errors: .INDENT 0.0 .TP .B org.bluez.obex.Error.NotAuthorized .TP .B org.bluez.obex.Error.NotInProgress If transfer is still in with \fBStatus\fP \fB\(dqqueued\(dq\fP\&. .UNINDENT .UNINDENT .UNINDENT .SS void Resume() .INDENT 0.0 .INDENT 3.5 Resumes transference previously suspended with use of \fBSuspend()\fP method. .sp Possible errors: .INDENT 0.0 .TP .B org.bluez.obex.Error.NotAuthorized .TP .B org.bluez.obex.Error.NotInProgress If transfer is still in with \fBStatus\fP \fB\(dqqueued\(dq\fP\&. .UNINDENT .UNINDENT .UNINDENT .SS Properties .SS string Status [readonly] .INDENT 0.0 .INDENT 3.5 Indicates the current status of the transfer. .sp Possible values: .INDENT 0.0 .TP .B \(dqqueued\(dq .TP .B \(dqactive\(dq .TP .B \(dqsuspended\(dq .TP .B \(dqcomplete\(dq .TP .B \(dqerror\(dq .UNINDENT .UNINDENT .UNINDENT .SS object Session [readonly] .INDENT 0.0 .INDENT 3.5 The object path of the session the transfer belongs to. .UNINDENT .UNINDENT .SS string Name [readonly, optional] .INDENT 0.0 .INDENT 3.5 Name of the object being transferred. .sp Either Name or Type or both will be present. .UNINDENT .UNINDENT .SS string Type [readonly, optional] .INDENT 0.0 .INDENT 3.5 Type of the object transferred being transferred. .sp Either Name or Type or both will be present. .UNINDENT .UNINDENT .SS uint64 Time [readonly, optional] .INDENT 0.0 .INDENT 3.5 Time of the object being transferred if this is provided by the remote party. .UNINDENT .UNINDENT .SS uint64 Size [readonly, optional] .INDENT 0.0 .INDENT 3.5 Size of the object being transferred. .sp If the size is unknown, then this property will not be present. .UNINDENT .UNINDENT .SS uint64 Transferred [readonly, optional] .INDENT 0.0 .INDENT 3.5 Number of bytes transferred. .sp For transfers with \fBStatus\fP set to \fB\(dqqueued\(dq\fP, this value will not be present. .UNINDENT .UNINDENT .SS string Filename [readonly, optional] .INDENT 0.0 .INDENT 3.5 Complete name of the file being received or sent. .sp For incoming object push transaction, this will be the proposed default location and name. It can be overwritten by the \fBAuthorizePush()\fP in \fBorg.bluez.obex.Agent(5)\fP and will be then updated accordingly. .UNINDENT .UNINDENT .\" Generated by docutils manpage writer. . bluez-5.82/doc/PaxHeaders/test-runner.rst0000644000000000000000000000005014471706457015452 xustar0020 atime=1743516877 20 ctime=1743591290 bluez-5.82/doc/test-runner.rst0000644000000000000000000001165614471706457015144 0ustar00rootroot=========== test-runner =========== **test-runner** [*OPTIONS*] -- DESCRIPTION =========== **test-runner(1)** is used to test Kernel changes to the Bluetooth subsystem, it lunches a virtual machine using qemu(1) and mounts the local filesystem using virtio (9p). OPTIONS ======= :-a:--auto: Find tests and run them :-b/--dbus: Start D-Bus system daemon :-s/--dbus-session: Start D-Bus session daemon :-d/--daemon: Start bluetoothd :-m/--monitor: Start btmon :-l/--emulator: Start btvirt :-A/-audio[=path]: Start audio server :-u/--unix[=path]: Provide serial device :-q/--qemu=: QEMU binary :-k/--kernel=: Kernel image (bzImage) :-h/--help: Show help options Kernel ====== The test-runner tool requires a kernel that is at least build with these minimal options for a successful boot. These options should be installed as .config in the kernel source directory followed by: .. code-block:: make olddefconfig After that a default kernel with the required options can be built. More option (like the Bluetooth subsystem) can be enabled on top of this. .. code-block:: CONFIG_VIRTIO=y CONFIG_VIRTIO_PCI=y CONFIG_NET=y CONFIG_INET=y CONFIG_NET_9P=y CONFIG_NET_9P_VIRTIO=y CONFIG_9P_FS=y CONFIG_9P_FS_POSIX_ACL=y CONFIG_SERIAL_8250=y CONFIG_SERIAL_8250_CONSOLE=y CONFIG_SERIAL_8250_PCI=yCONFIG_DEBUG_KERNEL=y CONFIG_SERIAL_8250_NR_UARTS=4 CONFIG_TMPFS=y CONFIG_TMPFS_POSIX_ACL=y CONFIG_TMPFS_XATTR=y CONFIG_DEVTMPFS=y CONFIG_DEBUG_FS=y Bluetooth --------- .. code-block:: CONFIG_BT=y CONFIG_BT_BREDR=y CONFIG_BT_RFCOMM=y CONFIG_BT_BNEP=y CONFIG_BT_HIDP=y CONFIG_BT_LE=y CONFIG_BT_HCIUART=y CONFIG_BT_HCIUART_H4=y CONFIG_BT_HCIVHCI=y CONFIG_CRYPTO_CMAC=y CONFIG_CRYPTO_USER_API=y CONFIG_CRYPTO_USER_API_HASH=y CONFIG_CRYPTO_USER_API_SKCIPHER=y CONFIG_UNIX=y CONFIG_UHID=y Lock debuging ------------- To catch locking related issues the following set of kernel config options may be useful: .. code-block:: CONFIG_DEBUG_KERNEL=y CONFIG_LOCKDEP_SUPPORT=y CONFIG_DEBUG_SPINLOCK=y CONFIG_DEBUG_LOCK_ALLOC=y CONFIG_DEBUG_ATOMIC_SLEEP=y CONFIG_PROVE_LOCKING=y CONFIG_PROVE_RCU=y CONFIG_LOCKDEP=y CONFIG_DEBUG_MUTEXES=y CONFIG_KASAN=y EXAMPLES ======== Running mgmt-tester ------------------- .. code-block:: $ tools/test-runner -k /pathto/bzImage -- tools/mgmt-tester Running a specific test of mgmt-tester -------------------------------------- .. code-block:: $ tools/test-runner -k /pathto/bzImage -- tools/mgmt-tester -s "" Running bluetoothctl with emulated controller --------------------------------------------- .. code-block:: $ tools/test-runner -l -d -k /pathto/bzImage -- client/bluetoothctl [CHG] Controller 00:AA:01:00:00:00 Pairable: yes [bluetooth]# Running bluetoothctl with emulated controller and audio support --------------------------------------------------------------- .. code-block:: $ tools/test-runner -l -d -A -k /pathto/bzImage -- client/bluetoothctl [CHG] Controller 00:AA:01:00:00:00 Pairable: yes [bluetooth]# [CHG] Controller 00:AA:01:00:00:00 Pairable: yes [CHG] Controller 00:AA:01:00:00:00 Class: 0x00600000 (6291456) [CHG] Controller 00:AA:01:00:00:00 UUIDs: 0000110e-0000-1000-8000-00805f9b34fb [CHG] Controller 00:AA:01:00:00:00 UUIDs: 0000111f-0000-1000-8000-00805f9b34fb [CHG] Controller 00:AA:01:00:00:00 UUIDs: 00001200-0000-1000-8000-00805f9b34fb [CHG] Controller 00:AA:01:00:00:00 UUIDs: 0000110b-0000-1000-8000-00805f9b34fb [CHG] Controller 00:AA:01:00:00:00 UUIDs: 0000110a-0000-1000-8000-00805f9b34fb [CHG] Controller 00:AA:01:00:00:00 UUIDs: 0000110c-0000-1000-8000-00805f9b34fb [CHG] Controller 00:AA:01:00:00:00 UUIDs: 00001800-0000-1000-8000-00805f9b34fb [CHG] Controller 00:AA:01:00:00:00 UUIDs: 00001801-0000-1000-8000-00805f9b34fb [CHG] Controller 00:AA:01:00:00:00 UUIDs: 0000180a-0000-1000-8000-00805f9b34fb [CHG] Controller 00:AA:01:00:00:00 UUIDs: 0000111e-0000-1000-8000-00805f9b34fb [CHG] Controller 00:AA:01:00:00:00 Class: 0x006c0000 (7077888) [CHG] Controller 00:AA:01:00:00:00 UUIDs: 0000110e-0000-1000-8000-00805f9b34fb [CHG] Controller 00:AA:01:00:00:00 UUIDs: 0000111f-0000-1000-8000-00805f9b34fb [CHG] Controller 00:AA:01:00:00:00 UUIDs: 00001200-0000-1000-8000-00805f9b34fb [CHG] Controller 00:AA:01:00:00:00 UUIDs: 0000110b-0000-1000-8000-00805f9b34fb [CHG] Controller 00:AA:01:00:00:00 UUIDs: 0000110a-0000-1000-8000-00805f9b34fb [CHG] Controller 00:AA:01:00:00:00 UUIDs: 0000110c-0000-1000-8000-00805f9b34fb [CHG] Controller 00:AA:01:00:00:00 UUIDs: 00001800-0000-1000-8000-00805f9b34fb [CHG] Controller 00:AA:01:00:00:00 UUIDs: 00001801-0000-1000-8000-00805f9b34fb [CHG] Controller 00:AA:01:00:00:00 UUIDs: 0000180a-0000-1000-8000-00805f9b34fb [CHG] Controller 00:AA:01:00:00:00 UUIDs: 0000111e-0000-1000-8000-00805f9b34fb Running shell with host controller using btproxy ------------------------------------------------ .. code-block:: $ tools/btproxy -u [1] $ tools/test-runner -u -d -k /pathto/bzImage -- /bin/bash [2] bluez-5.82/doc/PaxHeaders/org.bluez.obex.Synchronization.50000644000000000000000000000005014773213500020546 xustar0020 atime=1743591232 20 ctime=1743591291 bluez-5.82/doc/org.bluez.obex.Synchronization.50000644000000000000000000000560514773213500020235 0ustar00rootroot.\" Man page generated from reStructuredText. . . .nr rst2man-indent-level 0 . .de1 rstReportMargin \\$1 \\n[an-margin] level \\n[rst2man-indent-level] level margin: \\n[rst2man-indent\\n[rst2man-indent-level]] - \\n[rst2man-indent0] \\n[rst2man-indent1] \\n[rst2man-indent2] .. .de1 INDENT .\" .rstReportMargin pre: . RS \\$1 . nr rst2man-indent\\n[rst2man-indent-level] \\n[an-margin] . nr rst2man-indent-level +1 .\" .rstReportMargin post: .. .de UNINDENT . RE .\" indent \\n[an-margin] .\" old: \\n[rst2man-indent\\n[rst2man-indent-level]] .nr rst2man-indent-level -1 .\" new: \\n[rst2man-indent\\n[rst2man-indent-level]] .in \\n[rst2man-indent\\n[rst2man-indent-level]]u .. .TH "ORG.BLUEZ.OBEX.SYNCHRONIZATION" "5" "October 2023" "BlueZ" "Linux System Administration" .SH NAME org.bluez.obex.Synchronization \- BlueZ D-Bus OBEX Synchronization API documentation .SH INTERFACE .INDENT 0.0 .TP .B Service org.bluez.obex .TP .B Interface org.bluez.obex.Synchronization1 .TP .B Object path [Session object path] .UNINDENT .SS Methods .SS void SetLocation(string location) .INDENT 0.0 .INDENT 3.5 Sets the phonebook object store location for other operations. Should be called before all the other operations. .sp Possible location: .INDENT 0.0 .TP .B \(dqint\(dq ( \(dqinternal\(dq which is default ) Store in the interval memory. .TP .B \(dqsim{#}\(dq Store in sim card number #. .UNINDENT .sp Possible errors: .INDENT 0.0 .TP .B org.bluez.obex.Error.InvalidArguments .UNINDENT .UNINDENT .UNINDENT .SS object, dict GetPhonebook(string targetfile) .INDENT 0.0 .INDENT 3.5 Retrieves an entire Phonebook Object store from remote device, and stores it in a local file. .sp If an empty target file is given, a name will be automatically calculated for the temporary file. .sp The returned path represents the newly created transfer, which should be used to find out if the content has been successfully transferred or if the operation fails. .sp The properties of this transfer are also returned along with the object path, to avoid a call to GetProperties, see \fBorg.bluez.obex.Transfer(5)\fP for the possible list of properties. .sp Possible errors: .INDENT 0.0 .TP .B org.bluez.obex.Error.InvalidArguments .TP .B org.bluez.obex.Error.Failed .UNINDENT .UNINDENT .UNINDENT .SS object, dict PutPhonebook(string sourcefile) .INDENT 0.0 .INDENT 3.5 Sends an entire Phonebook Object store to remote device. .sp The returned path represents the newly created transfer, which should be used to find out if the content has been successfully transferred or if the operation fails. .sp The properties of this transfer are also returned along with the object path, to avoid a call to GetProperties, see \fBorg.bluez.obex.Transfer(5)\fP for the possible list of properties. .sp Possible errors: .INDENT 0.0 .TP .B org.bluez.obex.Error.InvalidArguments .TP .B org.bluez.obex.Error.Failed .UNINDENT .UNINDENT .UNINDENT .\" Generated by docutils manpage writer. . bluez-5.82/doc/PaxHeaders/org.bluez.AdvertisementMonitor.50000644000000000000000000000005014773213471020602 xustar0020 atime=1743591225 20 ctime=1743591291 bluez-5.82/doc/org.bluez.AdvertisementMonitor.50000644000000000000000000001310214773213471020260 0ustar00rootroot.\" Man page generated from reStructuredText. . . .nr rst2man-indent-level 0 . .de1 rstReportMargin \\$1 \\n[an-margin] level \\n[rst2man-indent-level] level margin: \\n[rst2man-indent\\n[rst2man-indent-level]] - \\n[rst2man-indent0] \\n[rst2man-indent1] \\n[rst2man-indent2] .. .de1 INDENT .\" .rstReportMargin pre: . RS \\$1 . nr rst2man-indent\\n[rst2man-indent-level] \\n[an-margin] . nr rst2man-indent-level +1 .\" .rstReportMargin post: .. .de UNINDENT . RE .\" indent \\n[an-margin] .\" old: \\n[rst2man-indent\\n[rst2man-indent-level]] .nr rst2man-indent-level -1 .\" new: \\n[rst2man-indent\\n[rst2man-indent-level]] .in \\n[rst2man-indent\\n[rst2man-indent-level]]u .. .TH "ORG.BLUEZ.ADVERTISEMENTMONITOR" "5" "October 2023" "BlueZ" "Linux System Administration" .SH NAME org.bluez.AdvertisementMonitor \- BlueZ D-Bus AdvertisementMonitor API documentation .SH DESCRIPTION .sp This API allows an client to specify a job of monitoring advertisements by registering the root of hierarchy and then exposing advertisement monitors under the root with filtering conditions, thresholds of RSSI and timers of RSSI thresholds. .sp Once a monitoring job is activated by \fBbluetoothd(8)\fP, the client can expect to get notified on the targeted advertisements no matter if there is an ongoing discovery session (see \fBStartDiscovery()\fP in \fBorg.bluez.Adapter(5)\fP). .SH INTERFACE .INDENT 0.0 .TP .B Service org.bluez .TP .B Interface org.bluez.AdvertisementMonitor1 [experimental] .TP .B Object path freely definable .UNINDENT .SS Methods .SS void Release() [noreply] .INDENT 0.0 .INDENT 3.5 This gets called as a signal for a client to perform clean\-up when: .INDENT 0.0 .IP \(bu 2 Monitor cannot be activated after it was exposed .IP \(bu 2 Monitor has been deactivated. .UNINDENT .UNINDENT .UNINDENT .SS void Activate() [noreply] .INDENT 0.0 .INDENT 3.5 After a monitor was exposed, this gets called as a signal for client to get acknowledged when a monitor has been activated, so the client can expect to receive calls on \fBDeviceFound()\fP or \fBDeviceLost()\fP\&. .UNINDENT .UNINDENT .SS void DeviceFound(object device) [noreply] .INDENT 0.0 .INDENT 3.5 This gets called to notify the client of finding the targeted device. Once receiving the call, the client should start to monitor the corresponding device to retrieve the changes on RSSI and advertisement content. .UNINDENT .UNINDENT .SS void DeviceLost(object device) [noreply] .INDENT 0.0 .INDENT 3.5 This gets called to notify the client of losing the targeted device. Once receiving this call, the client should stop monitoring the corresponding device. .UNINDENT .UNINDENT .SS Properties .SS string Type [read\-only] .INDENT 0.0 .INDENT 3.5 The type of the monitor. See \fBSupportedMonitorTypes\fP in \fBorg.bluez.AdvertisementMonitorManager(5)\fP for the available options. .UNINDENT .UNINDENT .SS int16 RSSILowThreshold [read\-only, optional] .INDENT 0.0 .INDENT 3.5 Used in conjunction with \fBRSSILowTimeout\fP to determine whether a device becomes out\-of\-range. Valid range is \-127 to 20 (dBm), while 127 indicates unset. .UNINDENT .UNINDENT .SS int16 RSSIHighThreshold [read\-only, optional] .INDENT 0.0 .INDENT 3.5 Used in conjunction with RSSIHighTimeout to determine whether a device becomes in\-range. Valid range is \-127 to 20 (dBm), while 127 indicates unset. .UNINDENT .UNINDENT .SS uint16 RSSILowTimeout [read\-only, optional] .INDENT 0.0 .INDENT 3.5 The time it takes to consider a device as out\-of\-range. If this many seconds elapses without receiving any signal at least as strong as \fBRSSILowThreshold\fP, a currently in\-range device will be considered as out\-of\-range (lost). Valid range is 1 to 300 (seconds), while 0 indicates unset. .UNINDENT .UNINDENT .SS uint16 RSSIHighTimeout [read\-only, optional] .INDENT 0.0 .INDENT 3.5 The time it takes to consider a device as in\-range. If this many seconds elapses while we continuouslyreceive signals at least as strong as \fBRSSIHighThreshold\fP, a currently out\-of\-range device will be considered as in\-range (found). Valid range is 1 to 300 (seconds), while 0 indicates unset. .UNINDENT .UNINDENT .SS uint16 RSSISamplingPeriod [read\-only, optional] .INDENT 0.0 .INDENT 3.5 Grouping rules on how to propagate the received advertisement packets to the client. .sp Possible values: .INDENT 0.0 .TP .B 0 All advertisement packets from in\-range devices would be propagated. .TP .B 255 Only the first advertisement packet of in\-range devices would be propagated. If the device becomes lost, then the first packet when it is found again will also be propagated. .TP .B 1 to 254 Advertisement packets would be grouped into 100ms * N time period. Packets in the same group will only be reported once, with the RSSI value being averaged out. .UNINDENT .sp Currently this is unimplemented in user space, so the value is only used to be forwarded to the kernel. .UNINDENT .UNINDENT .SS array{(uint8, uint8, array{byte})} Patterns [read\-only, optional] .INDENT 0.0 .INDENT 3.5 If the \fBType\fP property is set to \fB\(dqor_patterns\(dq\fP, then this property must exist and have at least one entry in the array. .sp The structure of a pattern contains the following: .INDENT 0.0 .TP .B uint8 start_position The index in an AD data field where the search hould start. The beginning of an AD data field is index 0. .TP .B uint8 AD_data_type See generic\-access\-profile/ for the possible allowed value. .TP .B array{byte} content_of_pattern This is the value of the pattern. The maximum length of the bytes is 31. .UNINDENT .UNINDENT .UNINDENT .\" Generated by docutils manpage writer. . bluez-5.82/doc/PaxHeaders/org.bluez.GattService.50000644000000000000000000000005014773213462016640 xustar0020 atime=1743591218 20 ctime=1743591291 bluez-5.82/doc/org.bluez.GattService.50000644000000000000000000000500114773213462016315 0ustar00rootroot.\" Man page generated from reStructuredText. . . .nr rst2man-indent-level 0 . .de1 rstReportMargin \\$1 \\n[an-margin] level \\n[rst2man-indent-level] level margin: \\n[rst2man-indent\\n[rst2man-indent-level]] - \\n[rst2man-indent0] \\n[rst2man-indent1] \\n[rst2man-indent2] .. .de1 INDENT .\" .rstReportMargin pre: . RS \\$1 . nr rst2man-indent\\n[rst2man-indent-level] \\n[an-margin] . nr rst2man-indent-level +1 .\" .rstReportMargin post: .. .de UNINDENT . RE .\" indent \\n[an-margin] .\" old: \\n[rst2man-indent\\n[rst2man-indent-level]] .nr rst2man-indent-level -1 .\" new: \\n[rst2man-indent\\n[rst2man-indent-level]] .in \\n[rst2man-indent\\n[rst2man-indent-level]]u .. .TH "ORG.BLUEZ.GATTSERVICE" "5" "October 2023" "BlueZ" "Linux System Administration" .SH NAME org.bluez.GattService \- BlueZ D-Bus GattService API documentation .SH DESCRIPTION .sp GATT local/server and remote/client services share the same high\-level D\-Bus API. .sp Local/Server refers to GATT based service exported by a plugin or an external application. .sp Remote/Client refers to GATT services exported by the peer. .SH INTERFACE .SS Client .INDENT 0.0 .TP .B Service org.bluez .TP .B Interface org.bluez.GattService1 .TP .B Object path [variable prefix]/{hci0,hci1,...}/dev_XX_XX_XX_XX_XX_XX/serviceXX .UNINDENT .SS Server .INDENT 0.0 .TP .B Service unique name .TP .B Interface org.bluez.GattService1 .TP .B Object path freely definable .UNINDENT .SS Properties .SS string UUID [read\-only] .INDENT 0.0 .INDENT 3.5 128\-bit service UUID. .UNINDENT .UNINDENT .SS boolean Primary [read\-only] .INDENT 0.0 .INDENT 3.5 Indicates whether or not this GATT service is a primary service. If false, the service is secondary. .UNINDENT .UNINDENT .SS object Device [read\-only, optional] .INDENT 0.0 .INDENT 3.5 Object path of the Bluetooth device the service belongs to. Only present on services from remote devices. .UNINDENT .UNINDENT .SS array{object} Includes [read\-only, optional] .INDENT 0.0 .INDENT 3.5 Array of object paths representing the included services of this service. .UNINDENT .UNINDENT .SS uint16 Handle [read\-only] (client only) .INDENT 0.0 .INDENT 3.5 Service handle. .UNINDENT .UNINDENT .SS uint16 Handle [read\-write, optional] (Server Only) .INDENT 0.0 .INDENT 3.5 Service handle. When available in the server it would attempt to use to allocate into the database which may fail, to auto allocate the value 0x0000 shall be used which will cause the allocated handle to be set once registered. .UNINDENT .UNINDENT .\" Generated by docutils manpage writer. . bluez-5.82/doc/PaxHeaders/org.bluez.MediaEndpoint.rst0000644000000000000000000000005014550336161017577 xustar0020 atime=1743516798 20 ctime=1743591290 bluez-5.82/doc/org.bluez.MediaEndpoint.rst0000644000000000000000000001274414550336161017270 0ustar00rootroot======================= org.bluez.MediaEndpoint ======================= ------------------------------------------- BlueZ D-Bus MediaEndpoint API documentation ------------------------------------------- :Version: BlueZ :Date: September 2023 :Manual section: 5 :Manual group: Linux System Administration Interface ========= :Service: unique name (Server role) org.bluez (Client role) :Interface: org.bluez.MediaEndpoint1 :Object path: freely definable (Server role) [variable prefix]/{hci0,hci1,...}/dev_XX_XX_XX_XX_XX_XX/sepX (Client role) Methods ------- .. _SetConfiguration: void SetConfiguration(object transport, dict properties) ```````````````````````````````````````````````````````` Set configuration for the transport. :object transport: Configured transport object. :dict properties: Configured **org.bluez.MediaTransport(5)** properties. For client role transport must be set with a server endpoint object which will be configured and the properties must contain the following properties: :array{byte} Capabilities [Mandatory]: See Capabilities property. :array{byte} Metadata [ISO only]: See Metadata property. :dict QoS [ISO only]: See **org.bluez.MediaTransport(5)** QoS property. array{byte} SelectConfiguration(array{byte} capabilities) ````````````````````````````````````````````````````````` Select preferable configuration from the supported capabilities. Returns a configuration which can be used to setup a transport, see **org.bluez.MediaTransport(5)** for possible values. Note: There is no need to cache the selected configuration since on success the configuration is send back as parameter of SetConfiguration. dict SelectProperties(dict capabilities) ```````````````````````````````````````` Select BAP unicast configuration from the supported capabilities: :object Endpoint: :array{byte} Capabilities: :array{byte} Metadata: :uint32 Locations: :uint32_t ChannelAllocation: :dict QoS: :byte Framing: :byte PHY: :uint16 MaximumLatency: :uint32 MinimumDelay: :uint32 MaximumDelay: :uint32 PreferredMinimumDelay: :uint32 PreferredMaximumDelay: See `MediaEndpoint Properties`_ for their possible values. Returns a configuration which can be used to setup a transport: :array{byte} Capabilities: :array{byte} Metadata [optional]: :dict QoS: See `SetConfiguration`_ for their possible values. Note: There is no need to cache the selected properties since on success the configuration is send back as parameter of SetConfiguration. void ClearConfiguration(object transport) ````````````````````````````````````````` Clear transport configuration. void Release() `````````````` This method gets called when the service daemon unregisters the endpoint. An endpoint can use it to do cleanup tasks. There is no need to unregister the endpoint, because when this method gets called it has already been unregistered. MediaEndpoint Properties ------------------------ string UUID [readonly, optional] ```````````````````````````````` UUID of the profile which the endpoint is for. byte Codec [readonly, optional] ``````````````````````````````` Assigned number of codec that the endpoint implements. The values should match the profile specification which is indicated by the UUID. uint32_t Vendor [readonly, Optional] ```````````````````````````````````` Vendor-specific Company ID, Codec ID tuple that the endpoint implements. It shall be set to appropriate value when Vendor Specific Codec (0xff) is used. array{byte} Capabilities [readonly, optional] ````````````````````````````````````````````` Capabilities blob, it is used as it is so the size and byte order must match. array{byte} Metadata [readonly, Optional] ````````````````````````````````````````` Metadata blob, it is used as it is so the size and byte order must match. object Device [readonly, optional] `````````````````````````````````` Device object which the endpoint is belongs to. bool DelayReporting [readonly, optional] ```````````````````````````````````````` Indicates if endpoint supports Delay Reporting. uint32 Locations [readonly, optional, ISO only, experimental] ````````````````````````````````````````````````````````````` Indicates endpoint supported locations. uint16 SupportedContext [readonly, optional, ISO only, experimental] ```````````````````````````````````````````````````````````````````` Indicates endpoint supported audio context. uint16 Context [readonly, optional, ISO only, experimental] ``````````````````````````````````````````````````````````` Indicates endpoint available audio context. dict QoS [readonly, optional, ISO only, experimental] ````````````````````````````````````````````````````` Indicates QoS capabilities. :byte Framing: Indicates endpoint support framing. Possible Values: :0x00: Unframed PDUs supported. :0x01: Unframed PDUs not supported. :byte PHY: Indicates endpoint preferred PHY. Possible values: :bit 0: LE 1M preferred. :bit 1: LE 2M preferred. :bit 2: LE Coded preferred. :byte Retransmissions: Indicates endpoint preferred number of retransmissions. :uint16 MaximumLatency: Indicates endpoint maximum latency. :uint32 MinimumDelay: Indicates endpoint minimum presentation delay. :uint32 MaximumDelay: Indicates endpoint maximum presentation delay. :uint32 PreferredMinimumDelay: Indicates endpoint preferred minimum presentation delay. :uint32 PreferredMaximumDelay: Indicates endpoint preferred maximum presentation delay. bluez-5.82/doc/PaxHeaders/org.bluez.LEAdvertisingManager.50000644000000000000000000000005014773213466020417 xustar0020 atime=1743591222 20 ctime=1743591291 bluez-5.82/doc/org.bluez.LEAdvertisingManager.50000644000000000000000000001021314773213466020075 0ustar00rootroot.\" Man page generated from reStructuredText. . . .nr rst2man-indent-level 0 . .de1 rstReportMargin \\$1 \\n[an-margin] level \\n[rst2man-indent-level] level margin: \\n[rst2man-indent\\n[rst2man-indent-level]] - \\n[rst2man-indent0] \\n[rst2man-indent1] \\n[rst2man-indent2] .. .de1 INDENT .\" .rstReportMargin pre: . RS \\$1 . nr rst2man-indent\\n[rst2man-indent-level] \\n[an-margin] . nr rst2man-indent-level +1 .\" .rstReportMargin post: .. .de UNINDENT . RE .\" indent \\n[an-margin] .\" old: \\n[rst2man-indent\\n[rst2man-indent-level]] .nr rst2man-indent-level -1 .\" new: \\n[rst2man-indent\\n[rst2man-indent-level]] .in \\n[rst2man-indent\\n[rst2man-indent-level]]u .. .TH "ORG.BLUEZ.LEADVERTISINGMANAGER" "5" "October 2023" "BlueZ" "Linux System Administration" .SH NAME org.bluez.LEAdvertisingManager \- BlueZ D-Bus LEAvertisingManager API documentation .SH INTERFACE .sp The Advertising Manager allows external applications to register Advertisement Data which should be broadcast to devices. Advertisement Data elements must follow the API for LE Advertisement Data described above. .INDENT 0.0 .TP .B Service org.bluez .TP .B Interface org.bluez.LEAdvertisingManager1 .TP .B Object path /org/bluez/{hci0,hci1,...} .UNINDENT .SS Methods .SS void RegisterAdvertisement(object advertisement, dict options) .INDENT 0.0 .INDENT 3.5 Registers an advertisement object to be sent over the LE Advertising channel. The service must implement \fBorg.bluez.LEAdvertisement(5)\fP interface. .sp Possible errors: .INDENT 0.0 .TP .B org.bluez.Error.InvalidArguments Indicates that the object has invalid or conflicting properties. .TP .B org.bluez.Error.AlreadyExists Indicates the object is already registered. .TP .B org.bluez.Error.InvalidLength Indicates that the data provided generates a data packet which is too long .TP .B org.bluez.Error.NotPermitted Indicates the maximum number of advertisement instances has been reached. .UNINDENT .UNINDENT .UNINDENT .SS void UnregisterAdvertisement(object advertisement) .INDENT 0.0 .INDENT 3.5 Unregisters an advertisement that has been previously registered using \fBRegisterAdvertisement()\fP\&. The object path parameter must match the same value that has been used on registration. .sp Possible errors: .INDENT 0.0 .TP .B org.bluez.Error.InvalidArguments .TP .B org.bluez.Error.DoesNotExist .UNINDENT .UNINDENT .UNINDENT .SS Properties .SS byte ActiveInstances [readonly] .INDENT 0.0 .INDENT 3.5 Number of active advertising instances. .UNINDENT .UNINDENT .SS byte SupportedInstances [readonly] .INDENT 0.0 .INDENT 3.5 Number of available advertising instances. .UNINDENT .UNINDENT .SS array{string} SupportedIncludes [readonly] .INDENT 0.0 .INDENT 3.5 List of supported system includes. .sp Possible values: .INDENT 0.0 .TP .B \(dqtx\-power\(dq .TP .B \(dqappearance\(dq .TP .B \(dqlocal\-name\(dq .TP .B \(dqrsi\(dq .UNINDENT .UNINDENT .UNINDENT .SS array{string} SupportedSecondaryChannels [readonly] .INDENT 0.0 .INDENT 3.5 List of supported Secondary channels. Secondary channels can be used to advertise with the corresponding PHY. .sp Possible values: .INDENT 0.0 .TP .B \(dq1M\(dq .TP .B \(dq2M\(dq .TP .B \(dqCoded\(dq .UNINDENT .UNINDENT .UNINDENT .SS dict SupportedCapabilities [readonly] .INDENT 0.0 .INDENT 3.5 Enumerates Advertising\-related controller capabilities useful to the client. .sp Possible Values: .INDENT 0.0 .TP .B byte MaxAdvLen Max advertising data length .TP .B byte MaxScnRspLen Max advertising scan response length .UNINDENT .sp ;int16 MinTxPower: .INDENT 0.0 .INDENT 3.5 Min advertising tx power (dBm) .UNINDENT .UNINDENT .INDENT 0.0 .TP .B int16 MaxTxPower Max advertising tx power (dBm) .UNINDENT .UNINDENT .UNINDENT .SS array{string} SupportedFeatures [readonly,optional] .INDENT 0.0 .INDENT 3.5 List of supported platform features. If no features are available on the platform, the SupportedFeatures array will be empty. .sp Possible values: .INDENT 0.0 .TP .B \(dqCanSetTxPower\(dq Indicates whether platform can specify tx power on each advertising instance. .TP .B \(dqHardwareOffload\(dq Indicates whether multiple advertising will be offloaded to the controller. .UNINDENT .UNINDENT .UNINDENT .\" Generated by docutils manpage writer. . bluez-5.82/doc/PaxHeaders/org.bluez.obex.Session.rst0000644000000000000000000000005014711225433017434 xustar0020 atime=1743516813 20 ctime=1743591290 bluez-5.82/doc/org.bluez.obex.Session.rst0000644000000000000000000000212314711225433017113 0ustar00rootroot====================== org.bluez.obex.Session ====================== ----------------------------------------- BlueZ D-Bus OBEX Client API documentation ----------------------------------------- :Version: BlueZ :Date: October 2023 :Manual section: 5 :Manual group: Linux System Administration Interface ========= :Service: org.bluez.obex :Interface: org.bluez.obex.Session1 :Object path: /org/bluez/obex/server/session{#} or /org/bluez/obex/client/session{#} Methods ------- string GetCapabilities() ```````````````````````` Get remote device capabilities. Possible errors: :org.bluez.obex.Error.NotSupported: :org.bluez.obex.Error.Failed: Properties ---------- string Source [readonly] ```````````````````````` Bluetooth adapter address string Destination [readonly] ````````````````````````````` Bluetooth device address byte Channel [readonly] ``````````````````````` Bluetooth channel uint16 PSM [readonly] ``````````````````````` Bluetooth L2CAP PSM string Target [readonly] ```````````````````````` Target UUID string Root [readonly] `````````````````````` Root path bluez-5.82/doc/PaxHeaders/org.bluez.Device.50000644000000000000000000000005014773213427015620 xustar0020 atime=1743591191 20 ctime=1743591290 bluez-5.82/doc/org.bluez.Device.50000644000000000000000000002706714773213427015315 0ustar00rootroot.\" Man page generated from reStructuredText. . . .nr rst2man-indent-level 0 . .de1 rstReportMargin \\$1 \\n[an-margin] level \\n[rst2man-indent-level] level margin: \\n[rst2man-indent\\n[rst2man-indent-level]] - \\n[rst2man-indent0] \\n[rst2man-indent1] \\n[rst2man-indent2] .. .de1 INDENT .\" .rstReportMargin pre: . RS \\$1 . nr rst2man-indent\\n[rst2man-indent-level] \\n[an-margin] . nr rst2man-indent-level +1 .\" .rstReportMargin post: .. .de UNINDENT . RE .\" indent \\n[an-margin] .\" old: \\n[rst2man-indent\\n[rst2man-indent-level]] .nr rst2man-indent-level -1 .\" new: \\n[rst2man-indent\\n[rst2man-indent-level]] .in \\n[rst2man-indent\\n[rst2man-indent-level]]u .. .TH "ORG.BLUEZ.DEVICE" "5" "October 2023" "BlueZ" "Linux System Administration" .SH NAME org.bluez.Device \- BlueZ D-Bus Device API documentation .SH INTERFACE .INDENT 0.0 .TP .B Service org.bluez .TP .B Interface org.bluez.Device1 .TP .B Object path [variable prefix]/{hci0,hci1,...}/dev_XX_XX_XX_XX_XX_XX .UNINDENT .SS Methods .SS void Connect() .INDENT 0.0 .INDENT 3.5 Connects all profiles the remote device supports that can be connected to and have been flagged as auto\-connectable. If only subset of profiles is already connected it will try to connect currently disconnected ones. .sp If at least one profile was connected successfully this method will indicate success. .sp For dual\-mode devices only one bearer is connected at time, the conditions are in the following order: .INDENT 0.0 .IP 1. 3 Connect the disconnected bearer if already connected. .UNINDENT .sp 2. Connect first the bonded bearer. If no bearers are bonded or both are skip and check latest seen bearer. .sp 3. Connect last seen bearer, in case the timestamps are the same BR/EDR takes precedence, or in case \fBPreferredBearer\fP has been set to a specific bearer then that is used instead. .sp Possible errors: .INDENT 0.0 .TP .B org.bluez.Error.NotReady .TP .B org.bluez.Error.Failed .TP .B org.bluez.Error.InProgress .TP .B org.bluez.Error.AlreadyConnected .UNINDENT .UNINDENT .UNINDENT .SS void Disconnect() .INDENT 0.0 .INDENT 3.5 Disconnects all connected profiles and then terminates low\-level ACL connection. .sp ACL connection will be terminated even if some profiles were not disconnected properly e.g. due to misbehaving device. .sp This method can be also used to cancel a preceding Connect call before a reply to it has been received. .sp For non\-trusted devices connected over LE bearer calling this method will disable incoming connections until Connect method is called again. .sp Possible errors: .INDENT 0.0 .TP .B org.bluez.Error.NotConnected .UNINDENT .UNINDENT .UNINDENT .SS void ConnectProfile(string uuid) .INDENT 0.0 .INDENT 3.5 Connects a specific profile of this device. The UUID provided is the remote service UUID for the profile. .sp Possible errors: .INDENT 0.0 .TP .B org.bluez.Error.Failed .TP .B org.bluez.Error.InProgress .TP .B org.bluez.Error.InvalidArguments .TP .B org.bluez.Error.NotAvailable .TP .B org.bluez.Error.NotReady .UNINDENT .UNINDENT .UNINDENT .SS void DisconnectProfile(string uuid) .INDENT 0.0 .INDENT 3.5 Disconnects a specific profile of this device. The profile needs to be registered client profile. .sp There is no connection tracking for a profile, so as long as the profile is registered this will always succeed. .sp Possible errors: .INDENT 0.0 .TP .B org.bluez.Error.Failed .TP .B org.bluez.Error.InProgress .TP .B org.bluez.Error.InvalidArguments .TP .B org.bluez.Error.NotSupported .UNINDENT .UNINDENT .UNINDENT .SS void Pair() .INDENT 0.0 .INDENT 3.5 Connects to the remote device and initiate pairing procedure then proceed with service discovery. .sp If the application has registered its own agent, then that specific agent will be used. Otherwise it will use the default agent. .sp Only for applications like a pairing wizard it would make sense to have its own agent. In almost all other cases the default agent will handle this just fine. .sp In case there is no application agent and also no default agent present, this method will fail. .sp Possible errors: .INDENT 0.0 .TP .B org.bluez.Error.InvalidArguments .TP .B org.bluez.Error.Failed .TP .B org.bluez.Error.AlreadyExists .TP .B org.bluez.Error.AuthenticationCanceled .TP .B org.bluez.Error.AuthenticationFailed .TP .B org.bluez.Error.AuthenticationRejected .TP .B org.bluez.Error.AuthenticationTimeout .TP .B org.bluez.Error.ConnectionAttemptFailed .UNINDENT .UNINDENT .UNINDENT .SS void CancelPairing() .INDENT 0.0 .INDENT 3.5 Cancels a pairing operation initiated by the \fBPair\fP method. .sp Possible errors: .INDENT 0.0 .TP .B org.bluez.Error.DoesNotExist .TP .B org.bluez.Error.Failed .UNINDENT .UNINDENT .UNINDENT .SS array{array{byte}} GetServiceRecords() [experimental] .INDENT 0.0 .INDENT 3.5 Returns all currently known BR/EDR service records for the device. Each individual byte array represents a raw SDP record, as defined by the Bluetooth Service Discovery Protocol specification. .sp This method is intended to be only used by compatibility layers like Wine, that need to provide access to raw SDP records to support foreign Bluetooth APIs. General applications should instead use the Profile API for services\-related functionality. .sp Possible errors: .INDENT 0.0 .TP .B org.bluez.Error.Failed .TP .B org.bluez.Error.NotReady .TP .B org.bluez.Error.NotConnected .TP .B org.bluez.Error.DoesNotExist .UNINDENT .UNINDENT .UNINDENT .SS Properties .SS string Address [readonly] .INDENT 0.0 .INDENT 3.5 The Bluetooth device address of the remote device. .UNINDENT .UNINDENT .SS string AddressType [readonly] .INDENT 0.0 .INDENT 3.5 The Bluetooth device Address Type. For dual\-mode and BR/EDR only devices this defaults to \(dqpublic\(dq. Single mode LE devices may have either value. If remote device uses privacy than before pairing this represents address type used for connection and Identity Address after pairing. .sp Possible values: .INDENT 0.0 .TP .B \(dqpublic\(dq Public address .TP .B \(dqrandom\(dq Random address .UNINDENT .UNINDENT .UNINDENT .SS string Name [readonly, optional] .INDENT 0.0 .INDENT 3.5 The Bluetooth remote name. .sp This value is only present for completeness. It is better to always use the \fBAlias\fP property when displaying the devices name. .sp If the \fBAlias\fP property is unset, it will reflect this value which makes it more convenient. .UNINDENT .UNINDENT .SS string Icon [readonly, optional] .INDENT 0.0 .INDENT 3.5 Proposed icon name according to the freedesktop.org icon naming specification. .UNINDENT .UNINDENT .SS uint32 Class [readonly, optional] .INDENT 0.0 .INDENT 3.5 The Bluetooth class of device of the remote device. .UNINDENT .UNINDENT .SS uint16 Appearance [readonly, optional] .INDENT 0.0 .INDENT 3.5 External appearance of device, as found on GAP service. .UNINDENT .UNINDENT .SS array{string} UUIDs [readonly, optional] .INDENT 0.0 .INDENT 3.5 List of 128\-bit UUIDs that represents the available remote services. .UNINDENT .UNINDENT .SS boolean Paired [readonly] .INDENT 0.0 .INDENT 3.5 Indicates if the remote device is paired. Paired means the pairing process where devices exchange the information to establish an encrypted connection has been completed. .UNINDENT .UNINDENT .SS boolean Bonded [readonly] .INDENT 0.0 .INDENT 3.5 Indicates if the remote device is bonded. Bonded means the information exchanged on pairing process has been stored and will be persisted. .UNINDENT .UNINDENT .SS boolean Connected [readonly] .INDENT 0.0 .INDENT 3.5 Indicates if the remote device is currently connected. A PropertiesChanged signal indicate changes to this status. .UNINDENT .UNINDENT .SS boolean Trusted [readwrite] .INDENT 0.0 .INDENT 3.5 Indicates if the remote is seen as trusted. This setting can be changed by the application. .UNINDENT .UNINDENT .SS boolean Blocked [readwrite] .INDENT 0.0 .INDENT 3.5 If set to true any incoming connections from the device will be immediately rejected. Any device drivers will also be removed and no new ones will be probed as long as the device is blocked. .UNINDENT .UNINDENT .SS boolean WakeAllowed [readwrite] .INDENT 0.0 .INDENT 3.5 If set to true this device will be allowed to wake the host from system suspend. .UNINDENT .UNINDENT .SS string Alias [readwrite] .INDENT 0.0 .INDENT 3.5 The name alias for the remote device. The alias can be used to have a different friendly name for the remote device. .sp In case no alias is set, it will return the remote device name. Setting an empty string as alias will convert it back to the remote device name. .sp When resetting the alias with an empty string, the property will default back to the remote name. .UNINDENT .UNINDENT .SS object Adapter [readonly] .INDENT 0.0 .INDENT 3.5 The object path of the adapter the device belongs to. .UNINDENT .UNINDENT .SS boolean LegacyPairing [readonly] .INDENT 0.0 .INDENT 3.5 Set to true if the device only supports the pre\-2.1 pairing mechanism. This property is useful during device discovery to anticipate whether legacy or simple pairing will occur if pairing is initiated. .sp Note that this property can exhibit false\-positives in the case of Bluetooth 2.1 (or newer) devices that have disabled Extended Inquiry Response support. .UNINDENT .UNINDENT .SS string Modalias [readonly, optional] .INDENT 0.0 .INDENT 3.5 Remote Device ID information in modalias format used by the kernel and udev. .UNINDENT .UNINDENT .SS int16 RSSI [readonly, optional] .INDENT 0.0 .INDENT 3.5 Received Signal Strength Indicator of the remote device (inquiry or advertising). .UNINDENT .UNINDENT .SS int16 TxPower [readonly, optional] .INDENT 0.0 .INDENT 3.5 Advertised transmitted power level (inquiry or advertising). .UNINDENT .UNINDENT .SS dict ManufacturerData [readonly, optional] .INDENT 0.0 .INDENT 3.5 Manufacturer specific advertisement data. Keys are 16 bits Manufacturer ID followed by its byte array value. .UNINDENT .UNINDENT .SS dict ServiceData [readonly, optional] .INDENT 0.0 .INDENT 3.5 Service advertisement data. Keys are the UUIDs in string format followed by its byte array value. .UNINDENT .UNINDENT .SS bool ServicesResolved [readonly] .INDENT 0.0 .INDENT 3.5 Indicate whether or not service discovery has been resolved. .UNINDENT .UNINDENT .SS array{byte} AdvertisingFlags [readonly] .INDENT 0.0 .INDENT 3.5 The Advertising Data Flags of the remote device. .UNINDENT .UNINDENT .SS dict AdvertisingData [readonly] .INDENT 0.0 .INDENT 3.5 The Advertising Data of the remote device. Keys are 1 byte AD Type followed by data as byte array. .sp Note: Only types considered safe to be handled by application are exposed. .sp Possible values: .INDENT 0.0 .TP .B .UNINDENT .sp Example: .INDENT 0.0 .INDENT 3.5 0x26 0x01 0x01... .UNINDENT .UNINDENT .UNINDENT .UNINDENT .SS array{object, dict} Sets [readonly, experimental] .INDENT 0.0 .INDENT 3.5 The object paths of the sets the device belongs to followed by a dictionary which can contain the following: .INDENT 0.0 .TP .B byte Rank Rank of the device in the Set. .UNINDENT .UNINDENT .UNINDENT .SS string PreferredBearer [readwrite, optional, experimental] .INDENT 0.0 .INDENT 3.5 Indicate the preferred bearer when initiating a connection, only available for dual\-mode devices. .sp When changing from \(dqbredr\(dq to \(dqle\(dq the device will be removed from the \(aqauto\-connect\(aq list so it won\(aqt automatically be connected when adverting. .sp Note: Changes only take effect when the device is disconnected. .sp Possible values: .INDENT 0.0 .TP .B \(dqlast\-seen\(dq Connect to last seen bearer first. Default. .TP .B \(dqbredr\(dq Connect to BR/EDR first. .TP .B \(dqle\(dq Connect to LE first. .UNINDENT .UNINDENT .UNINDENT .\" Generated by docutils manpage writer. . bluez-5.82/doc/PaxHeaders/org.bluez.BatteryProvider.rst0000644000000000000000000000005014536422313020203 xustar0020 atime=1743516788 20 ctime=1743591290 bluez-5.82/doc/org.bluez.BatteryProvider.rst0000644000000000000000000000147414536422313017672 0ustar00rootroot========================= org.bluez.BatteryProvider ========================= --------------------------------------------- BlueZ D-Bus BatteryProvider API documentation --------------------------------------------- :Version: BlueZ :Date: October 2023 :Manual section: 5 :Manual group: Linux System Administration Interface ========= :Service: :Interface: org.bluez.BatteryProvider1 :Object path: {provider_root}/{unique battery object path} Properties ---------- Objects provided on this interface contain the same properties as **org.bluez.Battery(5)** interface. Additionally, this interface needs to have the Device property indicating the object path of the device this battery provides. object Device [readonly] ```````````````````````` The object path of the device that has this battery. bluez-5.82/doc/PaxHeaders/org.bluez.Profile.rst0000644000000000000000000000005014536422313016456 xustar0020 atime=1743516782 20 ctime=1743591290 bluez-5.82/doc/org.bluez.Profile.rst0000644000000000000000000000211214536422313016133 0ustar00rootroot================= org.bluez.Profile ================= ------------------------------------- BlueZ D-Bus Profile API documentation ------------------------------------- :Version: BlueZ :Date: October 2023 :Manual section: 5 :Manual group: Linux System Administration Interface ========= :Service: unique name :Interface: org.bluez.Profile1 :Object path: freely definable Methods ------- void Release() [noreply] ```````````````````````` This method gets called when the service daemon unregisters the profile. A profile can use it to do cleanup tasks. There is no need to unregister the profile, because when this method gets called it has already been unregistered. void NewConnection(object device, fd, dict fd_properties) ````````````````````````````````````````````````````````` This method gets called when a new service level connection has been made and authorized. Possible fd_properties values: :uint16 Version [optional]: Profile version. :uint16 Features [optional]: Profile features. Possible errors: :org.bluez.Error.Rejected: :org.bluez.Error.Canceled: bluez-5.82/doc/PaxHeaders/assigned-numbers.txt0000644000000000000000000000005013725164704016433 xustar0020 atime=1743516877 20 ctime=1743591290 bluez-5.82/doc/assigned-numbers.txt0000644000000000000000000000064713725164704016123 0ustar00rootrootRFCOMM Channels =============== Since there are a limited amount of possible RFCOMM channels (1-31) they've been pre-allocated for currently known profiles in order to avoid conflicts. Profile Channel ----------------------- DUN 1 SPP 3 HSP HS 6 HFP HF 7 OPP 9 FTP 10 BIP 11 HSP AG 12 HFP AG 13 SYNCH (IrMC) 14 PBAP 15 MAP MAS 16 MAP MNS 17 SyncEvolution 19 PC/Ovi Suite 24 SyncML Client 25 SyncML Server 26 bluez-5.82/doc/PaxHeaders/org.bluez.NetworkServer.50000644000000000000000000000005014773213436017241 xustar0020 atime=1743591198 20 ctime=1743591290 bluez-5.82/doc/org.bluez.NetworkServer.50000644000000000000000000000435514773213436016731 0ustar00rootroot.\" Man page generated from reStructuredText. . . .nr rst2man-indent-level 0 . .de1 rstReportMargin \\$1 \\n[an-margin] level \\n[rst2man-indent-level] level margin: \\n[rst2man-indent\\n[rst2man-indent-level]] - \\n[rst2man-indent0] \\n[rst2man-indent1] \\n[rst2man-indent2] .. .de1 INDENT .\" .rstReportMargin pre: . RS \\$1 . nr rst2man-indent\\n[rst2man-indent-level] \\n[an-margin] . nr rst2man-indent-level +1 .\" .rstReportMargin post: .. .de UNINDENT . RE .\" indent \\n[an-margin] .\" old: \\n[rst2man-indent\\n[rst2man-indent-level]] .nr rst2man-indent-level -1 .\" new: \\n[rst2man-indent\\n[rst2man-indent-level]] .in \\n[rst2man-indent\\n[rst2man-indent-level]]u .. .TH "ORG.BLUEZ.NETWORKSERVER" "5" "October 2023" "BlueZ" "Linux System Administration" .SH NAME org.bluez.NetworkServer \- BlueZ D-Bus NetworkServer API documentation .SH INTERFACE .INDENT 0.0 .TP .B Service org.bluez .TP .B Interface org.bluez.NetworkServer1 .TP .B Object path /org/bluez/{hci0,hci1,...} .UNINDENT .SS Methods .SS void Register(string uuid, string bridge) .INDENT 0.0 .INDENT 3.5 Registers server for the provided UUID. .sp Every new connection to this server will be added the bridge interface. .sp Possible uuid values: .INDENT 0.0 .TP .B \(dqpanu\(dq, \(dq00001115\-0000\-1000\-8000\-00805f9b34fb\(dq Personal Network User role. .TP .B \(dqnap\(dq, \(dq00001116\-0000\-1000\-8000\-00805f9b34fb\(dq Network Access Point role. .TP .B \(dqgn\(dq, \(dq00001117\-0000\-1000\-8000\-00805f9b34fb\(dq Group Network role. .UNINDENT .sp Initially no network server SDP is provided. Only after this method a SDP record will be available and the BNEP server will be ready for incoming connections. .sp Possible errors: .INDENT 0.0 .TP .B org.bluez.Error.InvalidArguments .TP .B org.bluez.Error.AlreadyExists .TP .B org.bluez.Error.Failed .UNINDENT .UNINDENT .UNINDENT .SS void Unregister(string uuid) .INDENT 0.0 .INDENT 3.5 Unregisters the server for provided UUID which was previously registered with \fBRegister()\fP method. .sp All servers will be automatically unregistered when the calling application terminates. .sp Possible errors: .INDENT 0.0 .TP .B org.bluez.Error.InvalidArguments .TP .B org.bluez.Error.Failed .UNINDENT .UNINDENT .UNINDENT .\" Generated by docutils manpage writer. . bluez-5.82/doc/PaxHeaders/org.bluez.Network.50000644000000000000000000000005014773213437016053 xustar0020 atime=1743591199 20 ctime=1743591290 bluez-5.82/doc/org.bluez.Network.50000644000000000000000000000503214773213437015534 0ustar00rootroot.\" Man page generated from reStructuredText. . . .nr rst2man-indent-level 0 . .de1 rstReportMargin \\$1 \\n[an-margin] level \\n[rst2man-indent-level] level margin: \\n[rst2man-indent\\n[rst2man-indent-level]] - \\n[rst2man-indent0] \\n[rst2man-indent1] \\n[rst2man-indent2] .. .de1 INDENT .\" .rstReportMargin pre: . RS \\$1 . nr rst2man-indent\\n[rst2man-indent-level] \\n[an-margin] . nr rst2man-indent-level +1 .\" .rstReportMargin post: .. .de UNINDENT . RE .\" indent \\n[an-margin] .\" old: \\n[rst2man-indent\\n[rst2man-indent-level]] .nr rst2man-indent-level -1 .\" new: \\n[rst2man-indent\\n[rst2man-indent-level]] .in \\n[rst2man-indent\\n[rst2man-indent-level]]u .. .TH "ORG.BLUEZ.NETWORK" "5" "October 2023" "BlueZ" "Linux System Administration" .SH NAME org.bluez.Network \- BlueZ D-Bus Network API documentation .SH INTERFACE .INDENT 0.0 .TP .B Service org.bluez .TP .B Interface org.bluez.Network1 .TP .B Object path [variable prefix]/{hci0,hci1,...}/dev_XX_XX_XX_XX_XX_XX .UNINDENT .SS Methods .SS string Connect(string uuid) .INDENT 0.0 .INDENT 3.5 Connects to the network device and return the network interface name. .sp Possible uuid values: .INDENT 0.0 .TP .B \(dqpanu\(dq, \(dq00001115\-0000\-1000\-8000\-00805f9b34fb\(dq Personal Network User role. .TP .B \(dqnap\(dq, \(dq00001116\-0000\-1000\-8000\-00805f9b34fb\(dq Network Access Point role. .TP .B \(dqgn\(dq, \(dq00001117\-0000\-1000\-8000\-00805f9b34fb\(dq Group Network role. .UNINDENT .sp The connection will be closed and network device released either upon calling \fBDisconnect()\fP or when the client disappears from the message bus. .sp Possible errors: .INDENT 0.0 .TP .B org.bluez.Error.InvalidArguments .TP .B org.bluez.Error.NotSupported .TP .B org.bluez.Error.InProgress .TP .B org.bluez.Error.Failed .UNINDENT .UNINDENT .UNINDENT .SS void Disconnect() .INDENT 0.0 .INDENT 3.5 Disconnects from the network device. .sp To abort a connection attempt in case of errors or timeouts in the client it is fine to call this method. .sp Possible errors: .INDENT 0.0 .TP .B org.bluez.Error.Failed .TP .B org.bluez.Error.NotConnected .UNINDENT .UNINDENT .UNINDENT .SS Properties .SS boolean Connected [readonly] .INDENT 0.0 .INDENT 3.5 Indicates if the device is connected. .UNINDENT .UNINDENT .SS string Interface [readonly, optional] .INDENT 0.0 .INDENT 3.5 Indicates the network interface name when available. .UNINDENT .UNINDENT .SS string UUID [readonly, optional] .INDENT 0.0 .INDENT 3.5 Indicates the connection role when available. .UNINDENT .UNINDENT .\" Generated by docutils manpage writer. . bluez-5.82/doc/PaxHeaders/org.bluez.AdminPolicyStatus.rst0000644000000000000000000000005014536422313020472 xustar0020 atime=1743516791 20 ctime=1743591290 bluez-5.82/doc/org.bluez.AdminPolicyStatus.rst0000644000000000000000000000225214536422313020154 0ustar00rootroot=========================== org.bluez.AdminPolicyStatus =========================== ----------------------------------------------- BlueZ D-Bus AdminPolicyStatus API documentation ----------------------------------------------- :Version: BlueZ :Date: October 2023 :Manual section: 5 :Manual group: Linux System Administration Description =========== Interface AdminPolicyStatus1 provides readonly properties to indicate the current values of admin policy affecting the Adapter and Device objects. Interface ========= Adapter ------- :Service: org.bluez :Interface: org.bluez.AdminPolicyStatus1 [experimental] :Object path: [variable prefix]/{hci0,hci1,...} Device ------ :Service: org.bluez :Interface: org.bluez.AdminPolicyStatus1 [experimental] :Object path: [variable prefix]/{hci0,hci1,...}/dev_XX_XX_XX_XX_XX_XX Properties ---------- array{string} ServiceAllowList [readonly, adapter-only] ``````````````````````````````````````````````````````` Current value of service allow list. bool IsAffectedByPolicy [readonly, device-only] ``````````````````````````````````````````````` Indicate if there is any auto-connect profile in this device is not allowed by admin policy. bluez-5.82/doc/PaxHeaders/org.bluez.AdminPolicySet.50000644000000000000000000000005014773213444017304 xustar0020 atime=1743591204 20 ctime=1743591291 bluez-5.82/doc/org.bluez.AdminPolicySet.50000644000000000000000000000403014773213444016762 0ustar00rootroot.\" Man page generated from reStructuredText. . . .nr rst2man-indent-level 0 . .de1 rstReportMargin \\$1 \\n[an-margin] level \\n[rst2man-indent-level] level margin: \\n[rst2man-indent\\n[rst2man-indent-level]] - \\n[rst2man-indent0] \\n[rst2man-indent1] \\n[rst2man-indent2] .. .de1 INDENT .\" .rstReportMargin pre: . RS \\$1 . nr rst2man-indent\\n[rst2man-indent-level] \\n[an-margin] . nr rst2man-indent-level +1 .\" .rstReportMargin post: .. .de UNINDENT . RE .\" indent \\n[an-margin] .\" old: \\n[rst2man-indent\\n[rst2man-indent-level]] .nr rst2man-indent-level -1 .\" new: \\n[rst2man-indent\\n[rst2man-indent-level]] .in \\n[rst2man-indent\\n[rst2man-indent-level]]u .. .TH "ORG.BLUEZ.ADMINPOLICYSET" "5" "October 2023" "BlueZ" "Linux System Administration" .SH NAME org.bluez.AdminPolicySet \- BlueZ D-Bus AdminPolicySet API documentation .SH DESCRIPTION .sp This API provides methods to control the behavior of \fBbluetoothd(8)\fP as an administrator. .sp Interface AdminPolicySet1 provides methods to set policies. Once the policy is set successfully, it will affect all clients and stay persistently even after restarting \fBbluetoothd(8)\fP\&. The only way to clear it is to overwrite the policy with the same method. .SH INTERFACE .INDENT 0.0 .TP .B Service org.bluez .TP .B Interface org.bluez.AdminPolicySet1 [experimental] .TP .B Object path [variable prefix]/{hci0,hci1,...} .UNINDENT .SS Methods .SS void SetServiceAllowList(array{string} UUIDs) .INDENT 0.0 .INDENT 3.5 Sets the service allowlist by specifying service UUIDs. .sp When called, \fBbluetoothd(8)\fP will block incoming and outgoing connections to the service not in UUIDs for all of the clients. .sp Any subsequent calls to this method will supersede any previously set allowlist values. Calling this method with an empty array will allow any service UUIDs to be used. .sp The default value is an empty array. .sp Possible errors: .INDENT 0.0 .TP .B org.bluez.Error.InvalidArguments .TP .B org.bluez.Error.Failed .UNINDENT .UNINDENT .UNINDENT .\" Generated by docutils manpage writer. . bluez-5.82/doc/PaxHeaders/org.bluez.obex.FileTransfer.50000644000000000000000000000005014773213477017746 xustar0020 atime=1743591231 20 ctime=1743591291 bluez-5.82/doc/org.bluez.obex.FileTransfer.50000644000000000000000000001056214773213477017433 0ustar00rootroot.\" Man page generated from reStructuredText. . . .nr rst2man-indent-level 0 . .de1 rstReportMargin \\$1 \\n[an-margin] level \\n[rst2man-indent-level] level margin: \\n[rst2man-indent\\n[rst2man-indent-level]] - \\n[rst2man-indent0] \\n[rst2man-indent1] \\n[rst2man-indent2] .. .de1 INDENT .\" .rstReportMargin pre: . RS \\$1 . nr rst2man-indent\\n[rst2man-indent-level] \\n[an-margin] . nr rst2man-indent-level +1 .\" .rstReportMargin post: .. .de UNINDENT . RE .\" indent \\n[an-margin] .\" old: \\n[rst2man-indent\\n[rst2man-indent-level]] .nr rst2man-indent-level -1 .\" new: \\n[rst2man-indent\\n[rst2man-indent-level]] .in \\n[rst2man-indent\\n[rst2man-indent-level]]u .. .TH "ORG.BLUEZ.OBEX.FILETRANSFER" "5" "October 2023" "BlueZ" "Linux System Administration" .SH NAME org.bluez.obex.FileTransfer \- BlueZ D-Bus OBEX FileTransfer API documentation .SH INTERFACE .INDENT 0.0 .TP .B Service org.bluez.obex .TP .B Interface org.bluez.obex.FileTransfer1 .TP .B Object path [Session object path] .UNINDENT .SS Methods .SS void ChangeFolder(string folder) .INDENT 0.0 .INDENT 3.5 Changes the current folder of the remote device. .sp Possible errors: .INDENT 0.0 .TP .B org.bluez.obex.Error.InvalidArguments .TP .B org.bluez.obex.Error.Failed .UNINDENT .UNINDENT .UNINDENT .SS void CreateFolder(string folder) .INDENT 0.0 .INDENT 3.5 Creates a new folder in the remote device. .sp Possible errors: .INDENT 0.0 .TP .B org.bluez.obex.Error.InvalidArguments .TP .B org.bluez.obex.Error.Failed .UNINDENT .UNINDENT .UNINDENT .SS array{dict} ListFolder() .INDENT 0.0 .INDENT 3.5 Returns a dictionary containing information about the current folder content. .sp Possible return values: .INDENT 0.0 .TP .B string Name Object name in UTF\-8 format. .TP .B string Type Either \(dqfolder\(dq or \(dqfile\(dq. .TP .B uint64 Size Object size or number of items in folder. .TP .B string Permission Group, owner and other permission. .TP .B uint64 Modified Last change. .TP .B uint64 Accessed Last access. .TP .B uint64 Created Creation date. .UNINDENT .sp Possible errors: .INDENT 0.0 .TP .B org.bluez.obex.Error.Failed .UNINDENT .UNINDENT .UNINDENT .SS object, dict GetFile(string targetfile, string sourcefile) .INDENT 0.0 .INDENT 3.5 Copies the contents of the source file (from remote device) to the target file (on local filesystem). .sp If an empty target file is given, a name will be automatically generated for the temporary file. .sp The returned path represents the newly created transfer, which should be used to find out if the content has been successfully transferred or if the operation fails. .sp The properties of this transfer are also returned along with the object path, to avoid a call to GetProperties, see \fBorg.bluez.obex.Transfer(5)\fP for the possible list of properties. .sp Possible errors: .INDENT 0.0 .TP .B org.bluez.obex.Error.InvalidArguments .TP .B org.bluez.obex.Error.Failed .UNINDENT .UNINDENT .UNINDENT .SS object, dict PutFile(string sourcefile, string targetfile) .INDENT 0.0 .INDENT 3.5 Copies the contents of the source file (from local filesystem) to the target file (on remote device). .sp The returned path represents the newly created transfer, which should be used to find out if the content has been successfully transferred or if the operation fails. .sp The properties of this transfer are also returned along with the object path, to avoid a call to GetProperties, see \fBorg.bluez.obex.Transfer(5)\fP for the possible list of properties. .sp Possible errors: .INDENT 0.0 .TP .B org.bluez.obex.Error.InvalidArguments .TP .B org.bluez.obex.Error.Failed .UNINDENT .UNINDENT .UNINDENT .SS void CopyFile(string sourcefile, string targetfile) .INDENT 0.0 .INDENT 3.5 Copies the contents from source file to target file on the remote device. .sp Possible errors: .INDENT 0.0 .TP .B org.bluez.obex.Error.InvalidArguments .TP .B org.bluez.obex.Error.Failed .UNINDENT .UNINDENT .UNINDENT .SS void MoveFile(string sourcefile, string targetfile) .INDENT 0.0 .INDENT 3.5 Moves a file within the remote device from source file to the target file. .sp Possible errors: .sp ;org.bluez.obex.Error.InvalidArguments: :org.bluez.obex.Error.Failed: .UNINDENT .UNINDENT .SS void Delete(string file) .INDENT 0.0 .INDENT 3.5 Deletes the specified file/folder. .sp Possible errors: .INDENT 0.0 .TP .B org.bluez.obex.Error.InvalidArguments .TP .B org.bluez.obex.Error.Failed .UNINDENT .UNINDENT .UNINDENT .\" Generated by docutils manpage writer. . bluez-5.82/doc/PaxHeaders/org.bluez.MediaFolder.rst0000644000000000000000000000005014536422313017231 xustar0020 atime=1743516796 20 ctime=1743591290 bluez-5.82/doc/org.bluez.MediaFolder.rst0000644000000000000000000000451014536422313016712 0ustar00rootroot===================== org.bluez.MediaFolder ===================== ----------------------------------------- BlueZ D-Bus MediaFolder API documentation ----------------------------------------- :Version: BlueZ :Date: September 2023 :Manual section: 5 :Manual group: Linux System Administration Interface ========= :Service: unique name (Target role) org.bluez (Controller role) :Interface: org.bluez.MediaFolder1 :Object path: freely definable (Target role) [variable prefix]/{hci0,hci1,...}/dev_XX_XX_XX_XX_XX_XX/playerX (Controller role) Methods ------- object Search(string value, dict filter) ```````````````````````````````````````` Return a folder object containing the search result. To list the items found use the folder object returned and pass to ChangeFolder. Possible Errors: :org.bluez.Error.NotSupported: :org.bluez.Error.Failed: array{objects, properties} ListItems(dict filter) ````````````````````````````````````````````````` Return a list of items found Possible Errors: :org.bluez.Error.InvalidArguments: :org.bluez.Error.NotSupported: :org.bluez.Error.Failed: void ChangeFolder(object folder) ```````````````````````````````` Change current folder. Note: By changing folder the items of previous folder might be destroyed and have to be listed again, the exception is NowPlaying folder which should be always present while the player is active. Possible Errors: :org.bluez.Error.InvalidArguments: :org.bluez.Error.NotSupported: :org.bluez.Error.Failed: Properties ---------- uint32 NumberOfItems [readonly] ``````````````````````````````` Number of items in the folder string Name [readonly] `````````````````````` Folder name: Possible values: :"/Filesystem/...": Filesystem scope :"/NowPlaying/...": NowPlaying scope Note: /NowPlaying folder might not be listed if player is stopped, folders created by Search are virtual so once another Search is perform or the folder is changed using ChangeFolder it will no longer be listed. Filters ------- :uint32 Start: Offset of the first item. Default value: 0 :uint32 End: Offset of the last item. Default value: NumbeOfItems :array{string} Attributes: Item properties that should be included in the list. Possible Values: "title", "artist", "album", "genre", "number-of-tracks", "number", "duration" Default Value: All bluez-5.82/doc/PaxHeaders/org.bluez.Media.rst0000644000000000000000000000005014536422313016075 xustar0020 atime=1743516792 20 ctime=1743591290 bluez-5.82/doc/org.bluez.Media.rst0000644000000000000000000000623714536422313015566 0ustar00rootroot=============== org.bluez.Media =============== ----------------------------------- BlueZ D-Bus Media API documentation ----------------------------------- :Version: BlueZ :Date: September 2023 :Manual section: 5 :Manual group: Linux System Administration Interface ========= :Service: org.bluez :Interface: org.bluez.Media1 :Object path: [variable prefix]/{hci0,hci1,...} Methods ------- void RegisterEndpoint(object endpoint, dict properties) ``````````````````````````````````````````````````````` Register a local end point to sender, the sender can register as many end points as it likes. Note: If the sender disconnects the end points are automatically unregistered. possible properties: :string UUID: UUID of the profile which the endpoint is for. UUID must be in the list of SupportedUUIDS. :byte Codec: Assigned number of codec that the endpoint implements. The values should match the profile specification which is indicated by the UUID. :uint32_t Vendor [Optional]: Vendor-specific Company ID, Codec ID tuple that the endpoint implements. It shall be set to appropriate value when Vendor Specific Codec (0xff) is used. :array{byte} Capabilities: Capabilities blob, it is used as it is so the size and byte order must match. :array{byte} Metadata [Optional]: Metadata blob, it is used as it is so the size and byte order must match. Possible Errors: :org.bluez.Error.InvalidArguments: :org.bluez.Error.NotSupported: emitted when interface for the end-point is disabled void UnregisterEndpoint(object endpoint) ```````````````````````````````````````` Unregister sender end point. void RegisterPlayer(object player, dict properties) ``````````````````````````````````````````````````` Register a media player object to sender, the sender can register as many objects as it likes. Object must implement at least org.mpris.MediaPlayer2.Player as defined in MPRIS 2.2 spec: http://specifications.freedesktop.org/mpris-spec/latest/ Note: If the sender disconnects its objects are automatically unregistered. Possible Errors: :org.bluez.Error.InvalidArguments: :org.bluez.Error.NotSupported: void UnregisterPlayer(object player) ```````````````````````````````````` Unregister sender media player. void RegisterApplication(object root, dict options) ``````````````````````````````````````````````````` Register endpoints an player objects within root object which must implement ObjectManager. The application object path together with the D-Bus system bus connection ID define the identification of the application. Possible errors: :org.bluez.Error.InvalidArguments: :org.bluez.Error.AlreadyExists: void UnregisterApplication(object application) `````````````````````````````````````````````` This unregisters the services that has been previously registered. The object path parameter must match the same value that has been used on registration. Possible errors: :org.bluez.Error.InvalidArguments: :org.bluez.Error.DoesNotExist: Properties ---------- array{string} SupportedUUIDs [readonly] ``````````````````````````````````````` List of 128-bit UUIDs that represents the supported Endpoint registration. bluez-5.82/doc/PaxHeaders/org.bluez.AdminPolicyStatus.50000644000000000000000000000005014773213446020036 xustar0020 atime=1743591206 20 ctime=1743591291 bluez-5.82/doc/org.bluez.AdminPolicyStatus.50000644000000000000000000000340614773213446017522 0ustar00rootroot.\" Man page generated from reStructuredText. . . .nr rst2man-indent-level 0 . .de1 rstReportMargin \\$1 \\n[an-margin] level \\n[rst2man-indent-level] level margin: \\n[rst2man-indent\\n[rst2man-indent-level]] - \\n[rst2man-indent0] \\n[rst2man-indent1] \\n[rst2man-indent2] .. .de1 INDENT .\" .rstReportMargin pre: . RS \\$1 . nr rst2man-indent\\n[rst2man-indent-level] \\n[an-margin] . nr rst2man-indent-level +1 .\" .rstReportMargin post: .. .de UNINDENT . RE .\" indent \\n[an-margin] .\" old: \\n[rst2man-indent\\n[rst2man-indent-level]] .nr rst2man-indent-level -1 .\" new: \\n[rst2man-indent\\n[rst2man-indent-level]] .in \\n[rst2man-indent\\n[rst2man-indent-level]]u .. .TH "ORG.BLUEZ.ADMINPOLICYSTATUS" "5" "October 2023" "BlueZ" "Linux System Administration" .SH NAME org.bluez.AdminPolicyStatus \- BlueZ D-Bus AdminPolicyStatus API documentation .SH DESCRIPTION .sp Interface AdminPolicyStatus1 provides readonly properties to indicate the current values of admin policy affecting the Adapter and Device objects. .SH INTERFACE .SS Adapter .INDENT 0.0 .TP .B Service org.bluez .TP .B Interface org.bluez.AdminPolicyStatus1 [experimental] .TP .B Object path [variable prefix]/{hci0,hci1,...} .UNINDENT .SS Device .INDENT 0.0 .TP .B Service org.bluez .TP .B Interface org.bluez.AdminPolicyStatus1 [experimental] .TP .B Object path [variable prefix]/{hci0,hci1,...}/dev_XX_XX_XX_XX_XX_XX .UNINDENT .SS Properties .SS array{string} ServiceAllowList [readonly, adapter\-only] .INDENT 0.0 .INDENT 3.5 Current value of service allow list. .UNINDENT .UNINDENT .SS bool IsAffectedByPolicy [readonly, device\-only] .INDENT 0.0 .INDENT 3.5 Indicate if there is any auto\-connect profile in this device is not allowed by admin policy. .UNINDENT .UNINDENT .\" Generated by docutils manpage writer. . bluez-5.82/doc/PaxHeaders/hci.rst0000644000000000000000000000005014766002272013716 xustar0020 atime=1743515577 20 ctime=1743591290 bluez-5.82/doc/hci.rst0000644000000000000000000000646714766002272013414 0ustar00rootroot=== hci === ---------------------- Bluetooth HCI protocol ---------------------- :Version: BlueZ :Copyright: Free use of this software is granted under the terms of the GNU Lesser General Public Licenses (LGPL). :Date: October 2024 :Manual section: 7 :Manual group: Linux System Administration SYNOPSIS ======== .. code-block:: #include #include #include hci_socket = socket(PF_BLUETOOTH, SOCK_RAW, BTPROTO_HCI); DESCRIPTION =========== Bluetooth Host Controller Interface (HCI) is the standard protocol to communicate with Bluetooth adapters. HCI protocol provides a uniform command method for the Host to access Controller capabilities and to control connections to other Controllers. SOCKET ADDRESS ============== .. code-block:: struct sockaddr_hci { sa_family_t hci_family; unsigned short hci_dev; unsigned short hci_channel; }; Possible values for hci_channel: .. csv-table:: :header: "Define", "Value", "Description" :widths: auto **HCI_CHANNEL_RAW**, 0x00, Raw channel - Used for raw HCI communication **HCI_CHANNEL_USER**, 0x01, User channel - Used for userspace HCI communication (disables kernel processing) **HCI_CHANNEL_MONITOR**, 0x02, Monitor channel - Used for monitoring HCI traffic (btmon(1)) **HCI_CHANNEL_CONTROL**, 0x03, Control channel - Used to manage local adapters (bluetoothd(7)) **HCI_CHANNEL_LOGGING**, 0x04, Logging channel - Used to inject logging messages (bluetoothd(7)) Example: .. code-block:: struct sockaddr_hci addr; memset(&addr, 0, sizeof(addr)); addr.hci_family = AF_BLUETOOTH; addr.hci_dev = HCI_DEV_NONE; addr.hci_channel = HCI_CHANNEL_CONTROL; SOCKET OPTIONS ============== The socket options listed below can be set by using **setsockopt(2)** and read with **getsockopt(2)** with the socket level set to SOL_BLUETOOTH or SOL_HCI (HCI_FILTER). HCI_FILTER (since Linux 2.6) ---------------------------- Filter by HCI events, requires hci_channel to be set to HCI_CHANNEL_RAW, possible values: .. code-block:: struct hci_filter { uint32_t type_mask; uint32_t event_mask[2]; uint16_t opcode; }; Example: .. code-block:: struct hci_filter flt; memset(&flt, 0, sizeof(flt)); flt.type_mask = 1 << BT_H4_EVT_PKT; flt.event_mask[0] = 0xffffffff; flt.event_mask[1] = 0xffffffff; setsockopt(fd, SOL_HCI, HCI_FILTER, &flt, sizeof(flt)); BT_SNDBUF (since Linux 5.16) ---------------------------- Set send buffer size, requires hci_channel to be set to HCI_CHANNEL_MONITOR, HCI_CHANNEL_CONTROL or HCI_CHANNEL_LOGGING. Default value is 1028. Example: .. code-block:: uint16_t mtu = UINT16_MAX; int err; err = setsockopt(fd, SOL_BLUETOOTH, BT_SNDMTU, &mtu, sizeof(mtu)); BT_RCVBUF (since Linux 5.16) ---------------------------- Set receive buffer size, requires hci_channel to be set to HCI_CHANNEL_MONITOR, HCI_CHANNEL_CONTROL or HCI_CHANNEL_LOGGING. Default value is 1028. Example: .. code-block:: uint16_t mtu; socklen_t len; int err; len = sizeof(mtu); err = getsockopt(sock, SOL_BLUETOOTH, BT_RCVMTU, mtu, &len); RESOURCES ========= http://www.bluez.org REPORTING BUGS ============== linux-bluetooth@vger.kernel.org SEE ALSO ======== socket(7) bluez-5.82/doc/PaxHeaders/org.bluez.obex.Synchronization.rst0000644000000000000000000000005014536422313021213 xustar0020 atime=1743516817 20 ctime=1743591290 bluez-5.82/doc/org.bluez.obex.Synchronization.rst0000644000000000000000000000424714536422313020703 0ustar00rootroot============================== org.bluez.obex.Synchronization ============================== -------------------------------------------------- BlueZ D-Bus OBEX Synchronization API documentation -------------------------------------------------- :Version: BlueZ :Date: October 2023 :Manual section: 5 :Manual group: Linux System Administration Interface ========= :Service: org.bluez.obex :Interface: org.bluez.obex.Synchronization1 :Object path: [Session object path] Methods ------- void SetLocation(string location) ````````````````````````````````` Sets the phonebook object store location for other operations. Should be called before all the other operations. Possible location: :"int" ( "internal" which is default ): Store in the interval memory. :"sim{#}": Store in sim card number #. Possible errors: :org.bluez.obex.Error.InvalidArguments: object, dict GetPhonebook(string targetfile) ```````````````````````````````````````````` Retrieves an entire Phonebook Object store from remote device, and stores it in a local file. If an empty target file is given, a name will be automatically calculated for the temporary file. The returned path represents the newly created transfer, which should be used to find out if the content has been successfully transferred or if the operation fails. The properties of this transfer are also returned along with the object path, to avoid a call to GetProperties, see **org.bluez.obex.Transfer(5)** for the possible list of properties. Possible errors: :org.bluez.obex.Error.InvalidArguments: :org.bluez.obex.Error.Failed: object, dict PutPhonebook(string sourcefile) ```````````````````````````````````````````` Sends an entire Phonebook Object store to remote device. The returned path represents the newly created transfer, which should be used to find out if the content has been successfully transferred or if the operation fails. The properties of this transfer are also returned along with the object path, to avoid a call to GetProperties, see **org.bluez.obex.Transfer(5)** for the possible list of properties. Possible errors: :org.bluez.obex.Error.InvalidArguments: :org.bluez.obex.Error.Failed: bluez-5.82/doc/PaxHeaders/org.bluez.obex.MessageAccess.rst0000644000000000000000000000005014766002272020523 xustar0020 atime=1743515578 20 ctime=1743591290 bluez-5.82/doc/org.bluez.obex.MessageAccess.rst0000644000000000000000000001032314766002272020203 0ustar00rootroot============================ org.bluez.obex.MessageAccess ============================ ------------------------------------------------ BlueZ D-Bus OBEX MessageAccess API documentation ------------------------------------------------ :Version: BlueZ :Date: October 2023 :Manual section: 5 :Manual group: Linux System Administration Interface ========= :Service: org.bluez.obex :Interface: org.bluez.obex.MessageAccess1 :Object path: [Session object path] Methods ------- void SetFolder(string name) ``````````````````````````` Set working directory for current session. Possible name: Directory name or '..[/dir]'. Possible errors: :org.bluez.obex.Error.InvalidArguments: :org.bluez.obex.Error.Failed: array{dict} ListFolders(dict filter) ```````````````````````````````````` Returns a dictionary containing information about the current folder content. Possible filter: :uint16 Offset (default 0): Offset of the first item. :uint16 MaxCount (default 1024): Maximum number of items. Possible return: :string Name: Folder name Possible errors: :org.bluez.obex.Error.InvalidArguments: :org.bluez.obex.Error.Failed: array{string} ListFilterFields() ```````````````````````````````` Return all available fields that can be used in **Fields** filter. Possible values: :"subject": :"timestamp": :"sender": :"sender-address": :"recipient": :"recipient-address": :"type": :"size": :"status": :"text": :"attachment": :"priority": :"read": :"sent": :"protected": :"replyto": Possible errors: None array{object, dict} ListMessages(string folder, dict filter) ```````````````````````````````````````````````````````````` Returns an array containing the messages objects found in the given subfolder of the current folder, or in the current folder if folder is empty. Possible Filters: :uint16 Offset (default 0): Offset of the first item. uint16 MaxCount (default 1024): Maximum number of items. :byte SubjectLength (default 256): Maximum length of the Subject property in the message. :array{string} Fields: Message fields, default is all values. See **ListFilterFields()** for possible values. :array{string} Types: Filter messages by type. Possible values: :"sms": :"email": :"mms": :string PeriodBegin: Filter messages by starting period. Possible values: Date in "YYYYMMDDTHHMMSS" format. :string PeriodEnd: Filter messages by ending period. Possible values: Date in "YYYYMMDDTHHMMSS" format. :boolean Read: Filter messages by read flag. Possible values: True for read or False for unread :string Recipient: Filter messages by recipient address. :string Sender: Filter messages by sender address. :boolean Priority: Filter messages by priority flag. Possible values: True for high priority or False for non-high priority. Each message is represented by an object path, which implements **org.bluez.obex.Message(5)** interface, followed by a dictionary of its properties. void UpdateInbox(void) Requests remote to update its inbox. Possible errors: :org.bluez.obex.Error.Failed: object, dict PushMessage(string sourcefile, string folder, dict args) ````````````````````````````````````````````````````````````````````` Transfers a message (in bMessage format) to the remote device. The message is transferred either to the given subfolder of the current folder, or to the current folder if folder is empty. Possible args: Transparent, Retry, Charset The returned path represents the newly created transfer, which should be used to find out if the content has been successfully transferred or if the operation fails. The properties of this transfer are also returned along with the object path, to avoid a call to GetProperties, see **org.bluez.obex.Transfer(5)** for the possible list of properties. Possible errors: :org.bluez.obex.Error.InvalidArguments: :org.bluez.obex.Error.Failed: Properties ---------- array{string} SupportedTypes [readonly] ``````````````````````````````````````` List of supported message types. Possible values: :"EMAIL": Email messages. :"SMS_GSM": GSM short messages. :"SMS_CDMA": CDMA short messages. :"MMS": MMS messages. :"IM": Instant messaging. bluez-5.82/doc/PaxHeaders/org.bluez.Input.50000644000000000000000000000005014773213440015513 xustar0020 atime=1743591200 20 ctime=1743591290 bluez-5.82/doc/org.bluez.Input.50000644000000000000000000000342414773213440015177 0ustar00rootroot.\" Man page generated from reStructuredText. . . .nr rst2man-indent-level 0 . .de1 rstReportMargin \\$1 \\n[an-margin] level \\n[rst2man-indent-level] level margin: \\n[rst2man-indent\\n[rst2man-indent-level]] - \\n[rst2man-indent0] \\n[rst2man-indent1] \\n[rst2man-indent2] .. .de1 INDENT .\" .rstReportMargin pre: . RS \\$1 . nr rst2man-indent\\n[rst2man-indent-level] \\n[an-margin] . nr rst2man-indent-level +1 .\" .rstReportMargin post: .. .de UNINDENT . RE .\" indent \\n[an-margin] .\" old: \\n[rst2man-indent\\n[rst2man-indent-level]] .nr rst2man-indent-level -1 .\" new: \\n[rst2man-indent\\n[rst2man-indent-level]] .in \\n[rst2man-indent\\n[rst2man-indent-level]]u .. .TH "ORG.BLUEZ.INPUT" "5" "October 2023" "BlueZ" "Linux System Administration" .SH NAME org.bluez.Input \- BlueZ D-Bus Input API documentation .SH INTERFACE .INDENT 0.0 .TP .B Service org.bluez .TP .B Interface org.bluez.Input1 .TP .B Object path [variable prefix]/{hci0,hci1,...}/dev_XX_XX_XX_XX_XX_XX .UNINDENT .SS Properties .SS string ReconnectMode [readonly] .INDENT 0.0 .INDENT 3.5 Indicates the Connectability mode of the HID device as defined by the HID Profile specification, Section 5.4.2. .sp This mode is based in the two properties HIDReconnectInitiate (see Section 5.3.4.6) and HIDNormallyConnectable (see Section 5.3.4.14) which define the following four possible values: .INDENT 0.0 .TP .B \(dqnone\(dq Device and host are not required to automatically restore the connection. .TP .B \(dqhost\(dq Bluetooth HID host restores connection. .TP .B \(dqdevice\(dq Bluetooth HID device restores connection. .TP .B \(dqany\(dq Bluetooth HID device shall attempt to restore the lost connection, but Bluetooth HID Host may also restore the connection. .UNINDENT .UNINDENT .UNINDENT .\" Generated by docutils manpage writer. . bluez-5.82/doc/PaxHeaders/org.bluez.obex.Message.rst0000644000000000000000000000005014766002272017401 xustar0020 atime=1743515578 20 ctime=1743591290 bluez-5.82/doc/org.bluez.obex.Message.rst0000644000000000000000000000646514766002272017075 0ustar00rootroot====================== org.bluez.obex.Message ====================== ------------------------------------------ BlueZ D-Bus OBEX Message API documentation ------------------------------------------ :Version: BlueZ :Date: October 2023 :Manual section: 5 :Manual group: Linux System Administration Interface ========= :Service: org.bluez.obex :Interface: org.bluez.obex.Message1 :Object path: [Session object path]/message{#} Methods ------- object, dict Get(string targetfile, boolean attachment) ``````````````````````````````````````````````````````` Download message and store it in the target file. If an empty target file is given, a temporary file will be automatically generated. The returned path represents the newly created transfer, which should be used to find out if the content has been successfully transferred or if the operation fails. The properties of this transfer are also returned along with the object path, to avoid a call to GetProperties, see **org.bluez.obex.Transfer(5)** for the possible list of properties. Possible errors: :org.bluez.obex.Error.InvalidArguments: :org.bluez.obex.Error.Failed: Properties ---------- string Folder [readonly] ```````````````````````` Folder which the message belongs to string Subject [readonly] ````````````````````````` Message subject string Timestamp [readonly] ``````````````````````````` Message timestamp string Sender [readonly] ```````````````````````` Message sender name string SenderAddress [readonly] ``````````````````````````````` Message sender address string ReplyTo [readonly] ````````````````````````` Message Reply-To address string Recipient [readonly] ``````````````````````````` Message recipient name string RecipientAddress [readonly] `````````````````````````````````` Message recipient address string Type [readonly] `````````````````````` Message type Possible values: :"email": :"sms-gsm": :"sms-cdma": :"mms": uint64 Size [readonly] `````````````````````` Message size in bytes string Status [readonly] ```````````````````````` Message reception status Possible values: :"complete": :"fractioned": :"notification": boolean Priority [readonly] ``````````````````````````` Message priority flag boolean Read [read/write] ````````````````````````` Message read flag boolean Deleted [writeonly] ``````````````````````````` Message deleted flag boolean Sent [readonly] ``````````````````````` Message sent flag boolean Protected [readonly] ```````````````````````````` Message protected flag string DeliveryStatus [readonly] [optional] ``````````````````````````````````````````` Message delivery status Possible values: :"delivered": :"sent": :"unknown": uint64 ConversationId [readonly] [required] ``````````````````````````````````````````` Message conversation id sent by Server Unique identification of the conversation string ConversationName [readonly] [optional] ````````````````````````````````````````````` Human readable name of the conversation string Direction [readonly] [required] `````````````````````````````````````` Indicate the direction of the message Possible values: :"incoming": :"outgoing": :"outgoingdraft": :"outgoingpending": string AttachmentMimeTypes [readonly] [optional] ```````````````````````````````````````````````` MIME type of the attachment bluez-5.82/doc/PaxHeaders/org.bluez.MediaAssistant.50000644000000000000000000000005014773213457017335 xustar0020 atime=1743591215 20 ctime=1743591291 bluez-5.82/doc/org.bluez.MediaAssistant.50000644000000000000000000000461214773213457017021 0ustar00rootroot.\" Man page generated from reStructuredText. . . .nr rst2man-indent-level 0 . .de1 rstReportMargin \\$1 \\n[an-margin] level \\n[rst2man-indent-level] level margin: \\n[rst2man-indent\\n[rst2man-indent-level]] - \\n[rst2man-indent0] \\n[rst2man-indent1] \\n[rst2man-indent2] .. .de1 INDENT .\" .rstReportMargin pre: . RS \\$1 . nr rst2man-indent\\n[rst2man-indent-level] \\n[an-margin] . nr rst2man-indent-level +1 .\" .rstReportMargin post: .. .de UNINDENT . RE .\" indent \\n[an-margin] .\" old: \\n[rst2man-indent\\n[rst2man-indent-level]] .nr rst2man-indent-level -1 .\" new: \\n[rst2man-indent\\n[rst2man-indent-level]] .in \\n[rst2man-indent\\n[rst2man-indent-level]]u .. .TH "ORG.BLUEZ.MEDIAASSISTANT" "5" "June 2024" "BlueZ" "Linux System Administration" .SH NAME org.bluez.MediaAssistant \- BlueZ D-Bus MediaAssistant API documentation .SH INTERFACE .INDENT 0.0 .TP .B Service org.bluez .TP .B Interface org.bluez.MediaAssistant1 .TP .B Object path /org/bluez/{hci0,hci1,...}/src_XX_XX_XX_XX_XX_XX/dev_YY_YY_YY_YY_YY_YY/bisZ .UNINDENT .SS Methods .SS void Push(dict properties) .INDENT 0.0 .INDENT 3.5 Send stream information to the remote device. .INDENT 0.0 .TP .B dict properties .UNINDENT .sp Indicate stream properties that will be sent to the peer. .sp Values: .INDENT 0.0 .INDENT 3.5 .INDENT 0.0 .TP .B array{byte} Metadata [ISO only] See Metadata property. .TP .B dict QoS [ISO only] See QoS property. .UNINDENT .UNINDENT .UNINDENT .UNINDENT .UNINDENT .SS Properties .SS string State [readonly] .INDENT 0.0 .INDENT 3.5 Indicates the state of the assistant object. Possible values are: .INDENT 0.0 .TP .B \(dqidle\(dq assistant object was created for the stream .TP .B \(dqpending\(dq assistant object was pushed (stream information was sent to the peer) .TP .B \(dqrequesting\(dq remote device requires Broadcast_Code .TP .B \(dqactive\(dq remote device started receiving stream .UNINDENT .UNINDENT .UNINDENT .SS array{byte} Metadata [readwrite, ISO Only, experimental] .INDENT 0.0 .INDENT 3.5 Indicates stream Metadata. .UNINDENT .UNINDENT .SS dict QoS [readwrite, ISO only, experimental] .INDENT 0.0 .INDENT 3.5 Indicates stream QoS capabilities. .sp Values: .INDENT 0.0 .TP .B byte Encryption Indicates whether the stream is encrypted. .UNINDENT .sp :array{byte} BCode .INDENT 0.0 .INDENT 3.5 Indicates Broadcast_Code to decrypt stream. .UNINDENT .UNINDENT .UNINDENT .UNINDENT .\" Generated by docutils manpage writer. . bluez-5.82/doc/PaxHeaders/org.bluez.obex.Image.rst0000644000000000000000000000005014711225433017033 xustar0020 atime=1743516824 20 ctime=1743591290 bluez-5.82/doc/org.bluez.obex.Image.rst0000644000000000000000000000513014711225433016513 0ustar00rootroot==================== org.bluez.obex.Image ==================== -------------------------------------------------- BlueZ D-Bus OBEX Image API documentation -------------------------------------------------- :Version: BlueZ :Date: August 2024 :Manual section: 5 :Manual group: Linux System Administration Interface ========= :Service: org.bluez.obex :Interface: org.bluez.obex.Image1 [experimental] :Object path: [Session object path] Methods ------- object, dict Get(string targetfile, string handle, dict description) ```````````````````````````````````````````````````````````````````` Retrieves the image corresponding to the handle and the description, as one of the descriptions retrieved by GetImageProperties, and store it in a local file. If the "transform" property description exists it should be set to one of the value listed by GetImageProperties for this description. If description is an empty dict, the native image will be retrieved. Possible errors: :org.bluez.obex.Error.InvalidArguments: :org.bluez.obex.Error.Failed: array{dict} Properties(string handle) ````````````````````````````````````` Retrieves the image properties corresponding to the handle. The first dict entry is mandatory and correspond to 'handle' and 'name' of the image. The second dict entry is mandatory and correspond to the native description ('type':'native') of the image. The following dict entries are optional and correspond to variant descriptions of the image. If the 'transform' entry exists in the description, it lists the available possible image transformations and should be set to one of them before using the description as parameter to GetImage. Possible property values: :string type: Type of dict properties. Mandatory for each dict. Possible values: :"native": :"variant": :string encoding: File encoding format. Possible values: :"BMP": :"GIF": :"JPEG": :"JPEG2000": :"PNG": :"WBMP": :string pixel: File encoding format size of form "*". :uint64 size: File size. :uint64 maxsize: File maximum size. :string transformation: List of available transformations separated by space. Possible values: :"crop": :"fill": :"stretch": Possible errors: :org.bluez.obex.Error.InvalidArguments: :org.bluez.obex.Error.Failed: object, dict GetThumbnail(string targetfile, string handle) ``````````````````````````````````````````````````````````` Retrieves the image thumbnail corresponding to the handle and store it in a local file. Possible errors: :org.bluez.obex.Error.InvalidArguments: :org.bluez.obex.Error.Failed: bluez-5.82/doc/PaxHeaders/org.bluez.AgentManager.50000644000000000000000000000005014773213432016746 xustar0020 atime=1743591194 20 ctime=1743591290 bluez-5.82/doc/org.bluez.AgentManager.50000644000000000000000000000541614773213432016435 0ustar00rootroot.\" Man page generated from reStructuredText. . . .nr rst2man-indent-level 0 . .de1 rstReportMargin \\$1 \\n[an-margin] level \\n[rst2man-indent-level] level margin: \\n[rst2man-indent\\n[rst2man-indent-level]] - \\n[rst2man-indent0] \\n[rst2man-indent1] \\n[rst2man-indent2] .. .de1 INDENT .\" .rstReportMargin pre: . RS \\$1 . nr rst2man-indent\\n[rst2man-indent-level] \\n[an-margin] . nr rst2man-indent-level +1 .\" .rstReportMargin post: .. .de UNINDENT . RE .\" indent \\n[an-margin] .\" old: \\n[rst2man-indent\\n[rst2man-indent-level]] .nr rst2man-indent-level -1 .\" new: \\n[rst2man-indent\\n[rst2man-indent-level]] .in \\n[rst2man-indent\\n[rst2man-indent-level]]u .. .TH "ORG.BLUEZ.AGENTMANAGER" "5" "October 2023" "BlueZ" "Linux System Administration" .SH NAME org.bluez.AgentManager \- BlueZ D-Bus AgentManager API documentation .SH INTERFACE .INDENT 0.0 .TP .B Service org.bluez .TP .B Interface org.bluez.AgentManager1 .TP .B Object path /org/bluez .UNINDENT .SS Methods .SS void RegisterAgent(object agent, string capability) .INDENT 0.0 .INDENT 3.5 Registers pairing agent. .sp The object path defines the path of the agent that will be called when user input is needed and must implement \fBorg.bluez.Agent(5)\fP interface. .sp Every application can register its own agent and for all actions triggered by that application its agent is used. .sp It is not required by an application to register an agent. If an application does chooses to not register an agent, the default agent is used. This is on most cases a good idea. Only application like a pairing wizard should register their own agent. .sp An application can only register one agent. Multiple agents per application is not supported. .sp Possible capability values: .INDENT 0.0 .TP .B \(dq\(dq Fallback to \(dqKeyboardDisplay\(dq. .TP .B \(dqDisplayOnly\(dq .TP .B \(dqDisplayYesNo\(dq .TP .B \(dqKeyboardOnly\(dq .TP .B \(dqNoInputNoOutput\(dq .TP .B \(dqKeyboardDisplay\(dq .UNINDENT .sp Possible errors: .INDENT 0.0 .TP .B org.bluez.Error.InvalidArguments .TP .B org.bluez.Error.AlreadyExists .UNINDENT .UNINDENT .UNINDENT .SS void UnregisterAgent(object agent) .INDENT 0.0 .INDENT 3.5 Unregisters an agent that has been previously registered using \fBRegisterAgent\fP\&. The object path parameter must match the same value that has been used on registration. .sp Possible errors: .INDENT 0.0 .TP .B org.bluez.Error.DoesNotExist .UNINDENT .UNINDENT .UNINDENT .SS void RequestDefaultAgent(object agent) .INDENT 0.0 .INDENT 3.5 Requests to make the application agent the default agent. The application is required to register an agent. .sp Special permission might be required to become the default agent. .sp Possible errors: .INDENT 0.0 .TP .B org.bluez.Error.DoesNotExist .UNINDENT .UNINDENT .UNINDENT .\" Generated by docutils manpage writer. . bluez-5.82/doc/PaxHeaders/org.bluez.Adapter.50000644000000000000000000000005014773213426016000 xustar0020 atime=1743591190 20 ctime=1743591290 bluez-5.82/doc/org.bluez.Adapter.50000644000000000000000000003124414773213426015465 0ustar00rootroot.\" Man page generated from reStructuredText. . . .nr rst2man-indent-level 0 . .de1 rstReportMargin \\$1 \\n[an-margin] level \\n[rst2man-indent-level] level margin: \\n[rst2man-indent\\n[rst2man-indent-level]] - \\n[rst2man-indent0] \\n[rst2man-indent1] \\n[rst2man-indent2] .. .de1 INDENT .\" .rstReportMargin pre: . RS \\$1 . nr rst2man-indent\\n[rst2man-indent-level] \\n[an-margin] . nr rst2man-indent-level +1 .\" .rstReportMargin post: .. .de UNINDENT . RE .\" indent \\n[an-margin] .\" old: \\n[rst2man-indent\\n[rst2man-indent-level]] .nr rst2man-indent-level -1 .\" new: \\n[rst2man-indent\\n[rst2man-indent-level]] .in \\n[rst2man-indent\\n[rst2man-indent-level]]u .. .TH "ORG.BLUEZ.ADAPTER" "5" "October 2023" "BlueZ" "Linux System Administration" .SH NAME org.bluez.Adapter \- BlueZ D-Bus Adapter API documentation .SH INTERFACE .INDENT 0.0 .TP .B Service org.bluez .TP .B Interface org.bluez.Adapter1 .TP .B Object path [variable prefix]/{hci0,hci1,...} .UNINDENT .SS Methods .SS void StartDiscovery() .INDENT 0.0 .INDENT 3.5 Starts device discovery session which may include starting an inquiry and/or scanning procedures and remote device name resolving. .sp Use \fBStopDiscovery\fP to release the sessions acquired. .sp This process will start creating Device objects as new devices are discovered. .sp During discovery RSSI delta\-threshold is imposed. .sp Each client can request a single device discovery session per adapter. .sp Possible errors: .INDENT 0.0 .TP .B org.bluez.Error.NotReady .TP .B org.bluez.Error.Failed .TP .B org.bluez.Error.InProgress .UNINDENT .UNINDENT .UNINDENT .SS void StopDiscovery() .INDENT 0.0 .INDENT 3.5 Stops device discovery session started by \fBStartDiscovery\fP\&. .sp Note that a discovery procedure is shared between all discovery sessions thus calling StopDiscovery will only release a single session and discovery will stop when all sessions from all clients have finished. .sp Possible errors: .INDENT 0.0 .TP .B org.bluez.Error.NotReady .TP .B org.bluez.Error.Failed .TP .B org.bluez.Error.NotAuthorized .UNINDENT .UNINDENT .UNINDENT .SS void RemoveDevice(object device) .INDENT 0.0 .INDENT 3.5 Removes the remote device object at the given path including cahed information such as bonding information. .sp Possible errors: .INDENT 0.0 .TP .B org.bluez.Error.InvalidArguments .TP .B org.bluez.Error.Failed .UNINDENT .UNINDENT .UNINDENT .SS void SetDiscoveryFilter(dict filter) .INDENT 0.0 .INDENT 3.5 Sets the device discovery filter for the caller. When this method is called with no filter parameter, filter is removed. .sp Possible filter values: .INDENT 0.0 .TP .B array{string} UUIDs Filter by service UUIDs, empty means match \fIany\fP UUID. .sp When a remote device is found that advertises any UUID from UUIDs, it will be reported if: .INDENT 7.0 .IP \(bu 2 \fBPathloss\fP and \fBRSSI\fP are both empty. .IP \(bu 2 only \fBPathloss\fP param is set, device advertise TX power, and computed pathloss is less than Pathloss param. .IP \(bu 2 only \fBRSSI\fP param is set, and received RSSI is higher than RSSI param. .UNINDENT .TP .B int16 RSSI RSSI threshold value. .sp PropertiesChanged signals will be emitted for already existing Device objects, with updated RSSI value. If one or more discovery filters have been set, the RSSI delta\-threshold, that is imposed by StartDiscovery by default, will not be applied. .TP .B uint16 Pathloss Pathloss threshold value. .sp PropertiesChanged signals will be emitted for already existing Device objects, with updated Pathloss value. .TP .B string Transport (Default \(dqauto\(dq) Transport parameter determines the type of scan. .sp Possible values: .INDENT 7.0 .TP .B \(dqauto\(dq Interleaved scan, use LE, BREDR, or both, depending on what\(aqs currently enabled. .TP .B \(dqbredr\(dq BR/EDR inquiry only. .TP .B \(dqle\(dq LE scan only. .UNINDENT .TP .B bool DuplicateData (Default false) Disables duplicate detection of advertisement data. .sp When enabled PropertiesChanged signals will be generated for either ManufacturerData and ServiceData everytime they are discovered. .TP .B bool Discoverable (Default false) Make adapter discoverable while discovering, if the adapter is already discoverable setting this filter won\(aqt do anything. .TP .B string Pattern (Default none) Discover devices where the pattern matches either the prefix of the address or device name which is convenient way to limited the number of device objects created during a discovery. .sp When set disregards device discoverable flags. .sp Note: The pattern matching is ignored if there are other client that don\(aqt set any pattern as it work as a logical OR, also setting empty string \(dq\(dq pattern will match any device found. .sp When discovery filter is set, Device objects will be created as new devices with matching criteria are discovered regardless of they are connectable or discoverable which enables listening to non\-connectable and non\-discoverable devices. .sp When multiple clients call SetDiscoveryFilter, their filters are internally merged, and notifications about new devices are sent to all clients. Therefore, each client must check that device updates actually match its filter. .sp When SetDiscoveryFilter is called multiple times by the same client, last filter passed will be active for given client. .sp SetDiscoveryFilter can be called before StartDiscovery. It is useful when client will create first discovery session, to ensure that proper scan will be started right after call to StartDiscovery. .sp Possible errors: .INDENT 7.0 .TP .B org.bluez.Error.NotReady .TP .B org.bluez.Error.NotSupported .TP .B org.bluez.Error.Failed .UNINDENT .UNINDENT .UNINDENT .UNINDENT .SS array{string} GetDiscoveryFilters() .INDENT 0.0 .INDENT 3.5 Returns available filters that can be given to \fBSetDiscoveryFilter\fP\&. .sp Possible errors: None .UNINDENT .UNINDENT .SS object ConnectDevice(dict properties) [experimental] .INDENT 0.0 .INDENT 3.5 connects to device without need of performing General Discovery. Connection mechanism is similar to Connect method on \fBorg.bluez.Device1(5)\fP interface with exception that this method returns success when physical connection is established and you can specify bearer to connect with parameter. After this method returns, services discovery will continue and any supported profile will be connected. There is no need for calling Connect on Device1 after this call. If connection was successful this method returns object path to created device object or device that already exist. .sp Possible properties values: .INDENT 0.0 .TP .B string Address (Mandatory) The Bluetooth device address of the remote device. .TP .B string AddressType (Default \(dqBR/EDR\(dq) The Bluetooth device Address Type. This is address type that should be used for initial connection. .sp Possible values: .INDENT 7.0 .TP .B \(dqpublic\(dq Public address .TP .B \(dqrandom\(dq Random address .UNINDENT .UNINDENT .sp Possible errors: .INDENT 0.0 .TP .B org.bluez.Error.InvalidArguments .TP .B org.bluez.Error.AlreadyExists .TP .B org.bluez.Error.NotSupported .TP .B org.bluez.Error.NotReady .TP .B org.bluez.Error.Failed .UNINDENT .UNINDENT .UNINDENT .SS Properties .SS string Address [readonly] .INDENT 0.0 .INDENT 3.5 The Bluetooth device address. .UNINDENT .UNINDENT .SS string AddressType [readonly] .INDENT 0.0 .INDENT 3.5 The Bluetooth Address Type. For dual\-mode and BR/EDR only adapter this defaults to \(dqpublic\(dq. Single mode LE adapters may have either value. With privacy enabled this contains type of Identity Address and not type of address used for connection. .sp Possible values: .INDENT 0.0 .TP .B \(dqpublic\(dq Public address. .TP .B \(dqrandom Random address. .UNINDENT .UNINDENT .UNINDENT .SS string Name [readonly] .INDENT 0.0 .INDENT 3.5 The Bluetooth system name (pretty hostname). .sp This property is either a static system default or controlled by an external daemon providing access to the pretty hostname configuration. .UNINDENT .UNINDENT .SS string Alias [readwrite] .INDENT 0.0 .INDENT 3.5 The Bluetooth friendly name. This value can be changed. .sp In case no alias is set, it will return the system provided name. Setting an empty string as alias will convert it back to the system provided name. .sp When resetting the alias with an empty string, the property will default back to system name. .sp On a well configured system, this property never needs to be changed since it defaults to the system name and provides the pretty hostname. Only if the local name needs to be different from the pretty hostname, this property should be used as last resort. .UNINDENT .UNINDENT .SS uint32 Class [readonly] .INDENT 0.0 .INDENT 3.5 The Bluetooth class of device. .sp This property represents the value that is either automatically configured by DMI/ACPI information or provided as static configuration. .UNINDENT .UNINDENT .SS boolean Connectable [readwrite] .INDENT 0.0 .INDENT 3.5 Set an adapter to connectable or non\-connectable. This is a global setting and should only be used by the settings application. .sp Setting this property to false will set the Discoverable property of the adapter to false as well, which will not be reverted if if Connectable is set back to true. If required, the application will need to manually set Discoverable to true. .sp Note that this property only affects incoming connections. .UNINDENT .UNINDENT .SS boolean Powered [readwrite] .INDENT 0.0 .INDENT 3.5 Switch an adapter on or off. This will also set the appropriate connectable state of the controller. .sp The value of this property is not persistent. After restart or unplugging of the adapter it will reset back to false. .UNINDENT .UNINDENT .SS string PowerState [readonly, experimental] .INDENT 0.0 .INDENT 3.5 The power state of an adapter. .sp The power state will show whether the adapter is turning off, or turning on, as well as being on or off. .sp Possible values: .INDENT 0.0 .TP .B \(dqon\(dq Powered on. .TP .B \(dqoff\(dq Powered off .TP .B \(dqoff\-enabling\(dq Transitioning from \(dqoff\(dq to \(dqon\(dq. .TP .B \(dqon\-disabling\(dq Transitioning from \(dqon\(dq to \(dqoff\(dq. .TP .B \(dqoff\-blocked\(dq Blocked by rfkill. .UNINDENT .UNINDENT .UNINDENT .SS boolean Discoverable [readwrite] (Default: false) .INDENT 0.0 .INDENT 3.5 Switch an adapter to discoverable or non\-discoverable to either make it visible or hide it. This is a global setting and should only be used by the settings application. .sp If the DiscoverableTimeout is set to a non\-zero value then the system will set this value back to false after the timer expired. .sp In case the adapter is switched off, setting this value will fail. .sp When changing the Powered property the new state of this property will be updated via a PropertiesChanged signal. .UNINDENT .UNINDENT .SS boolean Pairable [readwrite] (Default: true) .INDENT 0.0 .INDENT 3.5 Switch an adapter to pairable or non\-pairable. This is a global setting and should only be used by the settings application. .sp Note that this property only affects incoming pairing requests. .UNINDENT .UNINDENT .SS uint32 PairableTimeout [readwrite] (Default: 0) .INDENT 0.0 .INDENT 3.5 The pairable timeout in seconds. A value of zero means that the timeout is disabled and it will stay in pairable mode forever. .UNINDENT .UNINDENT .SS uint32 DiscoverableTimeout [readwrite] (Default: 180) .INDENT 0.0 .INDENT 3.5 The discoverable timeout in seconds. A value of zero means that the timeout is disabled and it will stay in discoverable/limited mode forever. .UNINDENT .UNINDENT .SS boolean Discovering [readonly] .INDENT 0.0 .INDENT 3.5 Indicates that a device discovery procedure is active. .UNINDENT .UNINDENT .SS array{string} UUIDs [readonly] .INDENT 0.0 .INDENT 3.5 List of 128\-bit UUIDs that represents the available local services. .UNINDENT .UNINDENT .SS string Modalias [readonly, optional] .INDENT 0.0 .INDENT 3.5 Local Device ID information in modalias format used by the kernel and udev. .UNINDENT .UNINDENT .SS array{string} Roles [readonly] .INDENT 0.0 .INDENT 3.5 List of supported roles. .sp Possible values: .INDENT 0.0 .TP .B \(dqcentral\(dq Supports the central role. .TP .B \(dqperipheral\(dq Supports the peripheral role. .TP .B \(dqcentral\-peripheral\(dq Supports both roles concurrently. .UNINDENT .UNINDENT .UNINDENT .SS array{string} ExperimentalFeatures [readonly, optional] .INDENT 0.0 .INDENT 3.5 List of 128\-bit UUIDs that represents the experimental features currently enabled. .UNINDENT .UNINDENT .SS uint16 Manufacturer [readonly] .INDENT 0.0 .INDENT 3.5 The manufacturer of the device, as a uint16 company identifier defined by the Core Bluetooth Specification. .UNINDENT .UNINDENT .SS byte Version [readonly] .INDENT 0.0 .INDENT 3.5 The Bluetooth version supported by the device, as a core version code defined by the Core Bluetooth Specification. .UNINDENT .UNINDENT .\" Generated by docutils manpage writer. . bluez-5.82/doc/PaxHeaders/org.bluez.obex.Image.50000644000000000000000000000005014773213507016376 xustar0020 atime=1743591239 20 ctime=1743591291 bluez-5.82/doc/org.bluez.obex.Image.50000644000000000000000000000672314773213507016067 0ustar00rootroot.\" Man page generated from reStructuredText. . . .nr rst2man-indent-level 0 . .de1 rstReportMargin \\$1 \\n[an-margin] level \\n[rst2man-indent-level] level margin: \\n[rst2man-indent\\n[rst2man-indent-level]] - \\n[rst2man-indent0] \\n[rst2man-indent1] \\n[rst2man-indent2] .. .de1 INDENT .\" .rstReportMargin pre: . RS \\$1 . nr rst2man-indent\\n[rst2man-indent-level] \\n[an-margin] . nr rst2man-indent-level +1 .\" .rstReportMargin post: .. .de UNINDENT . RE .\" indent \\n[an-margin] .\" old: \\n[rst2man-indent\\n[rst2man-indent-level]] .nr rst2man-indent-level -1 .\" new: \\n[rst2man-indent\\n[rst2man-indent-level]] .in \\n[rst2man-indent\\n[rst2man-indent-level]]u .. .TH "ORG.BLUEZ.OBEX.IMAGE" "5" "August 2024" "BlueZ" "Linux System Administration" .SH NAME org.bluez.obex.Image \- BlueZ D-Bus OBEX Image API documentation .SH INTERFACE .INDENT 0.0 .TP .B Service org.bluez.obex .TP .B Interface org.bluez.obex.Image1 [experimental] .TP .B Object path [Session object path] .UNINDENT .SS Methods .SS object, dict Get(string targetfile, string handle, dict description) .INDENT 0.0 .INDENT 3.5 Retrieves the image corresponding to the handle and the description, as one of the descriptions retrieved by GetImageProperties, and store it in a local file. .sp If the \(dqtransform\(dq property description exists it should be set to one of the value listed by GetImageProperties for this description. .sp If description is an empty dict, the native image will be retrieved. .sp Possible errors: .INDENT 0.0 .TP .B org.bluez.obex.Error.InvalidArguments .TP .B org.bluez.obex.Error.Failed .UNINDENT .UNINDENT .UNINDENT .SS array{dict} Properties(string handle) .INDENT 0.0 .INDENT 3.5 Retrieves the image properties corresponding to the handle. .sp The first dict entry is mandatory and correspond to \(aqhandle\(aq and \(aqname\(aq of the image. .sp The second dict entry is mandatory and correspond to the native description (\(aqtype\(aq:\(aqnative\(aq) of the image. .sp The following dict entries are optional and correspond to variant descriptions of the image. If the \(aqtransform\(aq entry exists in the description, it lists the available possible image transformations and should be set to one of them before using the description as parameter to GetImage. .sp Possible property values: .INDENT 0.0 .TP .B string type Type of dict properties. Mandatory for each dict. .sp Possible values: .INDENT 7.0 .TP .B \(dqnative\(dq .TP .B \(dqvariant\(dq .UNINDENT .TP .B string encoding File encoding format. .sp Possible values: .INDENT 7.0 .TP .B \(dqBMP\(dq .TP .B \(dqGIF\(dq .TP .B \(dqJPEG\(dq .TP .B \(dqJPEG2000\(dq .TP .B \(dqPNG\(dq .TP .B \(dqWBMP\(dq .UNINDENT .TP .B string pixel File encoding format size of form \(dq*\(dq. .TP .B uint64 size File size. .TP .B uint64 maxsize File maximum size. .TP .B string transformation List of available transformations separated by space. .sp Possible values: .INDENT 7.0 .TP .B \(dqcrop\(dq .TP .B \(dqfill\(dq .TP .B \(dqstretch\(dq .UNINDENT .UNINDENT .sp Possible errors: .INDENT 0.0 .TP .B org.bluez.obex.Error.InvalidArguments .TP .B org.bluez.obex.Error.Failed .UNINDENT .UNINDENT .UNINDENT .SS object, dict GetThumbnail(string targetfile, string handle) .INDENT 0.0 .INDENT 3.5 Retrieves the image thumbnail corresponding to the handle and store it in a local file. .sp Possible errors: .INDENT 0.0 .TP .B org.bluez.obex.Error.InvalidArguments .TP .B org.bluez.obex.Error.Failed .UNINDENT .UNINDENT .UNINDENT .\" Generated by docutils manpage writer. . bluez-5.82/doc/PaxHeaders/org.bluez.obex.Client.rst0000644000000000000000000000005014711225433017227 xustar0020 atime=1743516812 20 ctime=1743591290 bluez-5.82/doc/org.bluez.obex.Client.rst0000644000000000000000000000261414711225433016713 0ustar00rootroot===================== org.bluez.obex.Client ===================== ----------------------------------------- BlueZ D-Bus OBEX Client API documentation ----------------------------------------- :Version: BlueZ :Date: October 2023 :Manual section: 5 :Manual group: Linux System Administration Interface ========= :Service: org.bluez.obex :Interface: org.bluez.obex.Client1 :Object path: /org/bluez/obex Methods ------- object CreateSession(string destination, dict args) ``````````````````````````````````````````````````` Connects to the destination address and then proceed to create an OBEX session object which implements **org.bluez.obex.Session(5)** interface. The last parameter is a dictionary to hold optional or type-specific parameters. Possible args values: :string Target: Type of session to be created. Possible values: :"ftp": :"map": :"opp": :"pbap": :"sync": :"bip-avrcp": :string Source: Local address to be used. :byte Channel: Channel to be used. :uint16 PSM: L2CAP PSM to be used. Possible errors: :org.bluez.obex.Error.InvalidArguments: :org.bluez.obex.Error.Failed: void RemoveSession(object session) `````````````````````````````````` Disconnects and removes session previously created by **CreateSession()** aborting any pending transfers. Possible errors: :org.bluez.obex.Error.InvalidArguments: :org.bluez.obex.Error.NotAuthorized: bluez-5.82/doc/PaxHeaders/org.bluez.DeviceSet.50000644000000000000000000000005014773213430016266 xustar0020 atime=1743591192 20 ctime=1743591290 bluez-5.82/doc/org.bluez.DeviceSet.50000644000000000000000000000463614773213430015760 0ustar00rootroot.\" Man page generated from reStructuredText. . . .nr rst2man-indent-level 0 . .de1 rstReportMargin \\$1 \\n[an-margin] level \\n[rst2man-indent-level] level margin: \\n[rst2man-indent\\n[rst2man-indent-level]] - \\n[rst2man-indent0] \\n[rst2man-indent1] \\n[rst2man-indent2] .. .de1 INDENT .\" .rstReportMargin pre: . RS \\$1 . nr rst2man-indent\\n[rst2man-indent-level] \\n[an-margin] . nr rst2man-indent-level +1 .\" .rstReportMargin post: .. .de UNINDENT . RE .\" indent \\n[an-margin] .\" old: \\n[rst2man-indent\\n[rst2man-indent-level]] .nr rst2man-indent-level -1 .\" new: \\n[rst2man-indent\\n[rst2man-indent-level]] .in \\n[rst2man-indent\\n[rst2man-indent-level]]u .. .TH "ORG.BLUEZ.DEVICESET" "5" "September 2023" "BlueZ" "Linux System Administration" .SH NAME org.bluez.DeviceSet \- BlueZ D-Bus DeviceSet API documentation .SH INTERFACE .INDENT 0.0 .TP .B Service org.bluez .TP .B Interface org.bluez.DeviceSet1 .TP .B Object path [variable prefix]/{hci0,hci1,...}/set_{sirk} .UNINDENT .SS Methods .SS void Connect() [experimental] .INDENT 0.0 .INDENT 3.5 Connects all \fBdevices\fP members of the set, each member is connected in sequence as they were added/loaded following the same proceedure as described in \fBDevice1.Connect\fP\&. .sp Possible errors: .INDENT 0.0 .TP .B org.bluez.Error.NotReady .TP .B org.bluez.Error.Failed .TP .B org.bluez.Error.InProgress .TP .B org.bluez.Error.AlreadyConnected .UNINDENT .UNINDENT .UNINDENT .SS void Disconnect() [experimental] .INDENT 0.0 .INDENT 3.5 Disconnects all \fBdevices\fP members of the set, each member is disconnected in sequence as they were connected following the same proceedure as described in \fBDevice1.Disconnect\fP\&. .sp Possible errors: .INDENT 0.0 .TP .B org.bluez.Error.NotConnected .UNINDENT .UNINDENT .UNINDENT .SS Properties .SS object Adapter [readonly, experimental] .INDENT 0.0 .INDENT 3.5 The object path of the adapter the set belongs to. .UNINDENT .UNINDENT .SS bool AutoConnect [read\-write, experimental] .INDENT 0.0 .INDENT 3.5 Indicates if the \fBdevices\fP members of the set shall be automatically connected once any of its members is connected. .UNINDENT .UNINDENT .SS array(object) Devices [ready\-only, experimental] .INDENT 0.0 .INDENT 3.5 List of devices objects that are members of the set. .UNINDENT .UNINDENT .SS byte Size [read\-only, experimental] .INDENT 0.0 .INDENT 3.5 Set members size. .UNINDENT .UNINDENT .\" Generated by docutils manpage writer. . bluez-5.82/PaxHeaders/obexd0000644000000000000000000000005014773213553012704 xustar0020 atime=1743591291 20 ctime=1743591275 bluez-5.82/obexd/0000755000000000000000000000000014773213553012442 5ustar00rootrootbluez-5.82/obexd/PaxHeaders/src0000644000000000000000000000005014773213570013472 xustar0020 atime=1743591291 20 ctime=1743591288 bluez-5.82/obexd/src/0000755000000000000000000000000014773213570013230 5ustar00rootrootbluez-5.82/obexd/src/PaxHeaders/org.bluez.obex.service.in0000644000000000000000000000005014572354773020411 xustar0020 atime=1743515749 20 ctime=1743591275 bluez-5.82/obexd/src/org.bluez.obex.service.in0000644000000000000000000000015214572354773020070 0ustar00rootroot[D-BUS Service] Name=org.bluez.obex Exec=@PKGLIBEXECDIR@/obexd SystemdService=dbus-org.bluez.obex.service bluez-5.82/obexd/src/PaxHeaders/obex.c0000644000000000000000000000005014711225434014642 xustar0020 atime=1743516736 20 ctime=1743591283 bluez-5.82/obexd/src/obex.c0000644000000000000000000005347214711225434014336 0ustar00rootroot// SPDX-License-Identifier: GPL-2.0-or-later /* * * OBEX Server * * Copyright (C) 2007-2010 Nokia Corporation * Copyright (C) 2007-2010 Marcel Holtmann * * */ #ifdef HAVE_CONFIG_H #include #endif #include #include #include #include #include #include #include #include #include #include #include #include #include #include "gobex/gobex.h" #include "btio/btio.h" #include "obexd.h" #include "log.h" #include "obex.h" #include "obex-priv.h" #include "server.h" #include "manager.h" #include "mimetype.h" #include "service.h" #include "transport.h" #include "src/shared/util.h" typedef struct { uint8_t version; uint8_t flags; uint16_t mtu; } __attribute__ ((packed)) obex_connect_hdr_t; struct auth_header { uint8_t tag; uint8_t len; uint8_t val[0]; } __attribute__ ((packed)); /* Possible commands */ static const struct { int cmd; const char *name; } obex_command[] = { { G_OBEX_OP_CONNECT, "CONNECT" }, { G_OBEX_OP_DISCONNECT, "DISCONNECT" }, { G_OBEX_OP_PUT, "PUT" }, { G_OBEX_OP_GET, "GET" }, { G_OBEX_OP_SETPATH, "SETPATH" }, { G_OBEX_OP_SESSION, "SESSION" }, { G_OBEX_OP_ABORT, "ABORT" }, { G_OBEX_OP_ACTION, "ACTION" }, { 0xFF, NULL }, }; static gboolean handle_async_io(void *object, int flags, int err, void *user_data); static void print_event(int cmd, uint8_t rsp) { const char *cmdstr = NULL; int i; static int lastcmd; if (cmd < 0) cmd = lastcmd; else lastcmd = cmd; for (i = 0; obex_command[i].cmd != 0xFF; i++) { if (obex_command[i].cmd != cmd) continue; cmdstr = obex_command[i].name; } obex_debug("%s(0x%x), %s(0x%x)", cmdstr, cmd, g_obex_strerror(rsp), rsp); } static void os_set_response(struct obex_session *os, int err) { uint8_t rsp; rsp = g_obex_errno_to_rsp(err); print_event(-1, rsp); g_obex_send_rsp(os->obex, rsp, NULL, G_OBEX_HDR_INVALID); } static void os_session_mark_aborted(struct obex_session *os) { /* the session was already cancelled/aborted or size in unknown */ if (os->aborted || os->size == OBJECT_SIZE_UNKNOWN) return; os->aborted = (os->size != os->offset); } static void os_reset_session(struct obex_session *os) { os_session_mark_aborted(os); if (os->object) { obex_object_reset_io_watch(os->object); os->driver->close(os->object); if (os->aborted && os->cmd == G_OBEX_OP_PUT && os->path && os->driver->remove) os->driver->remove(os->path); } if (os->service && os->service->reset) os->service->reset(os, os->service_data); if (os->name) { g_free(os->name); os->name = NULL; } if (os->type) { g_free(os->type); os->type = NULL; } if (os->buf) { g_free(os->buf); os->buf = NULL; } if (os->path) { g_free(os->path); os->path = NULL; } if (os->apparam) { free(os->apparam); os->apparam = NULL; os->apparam_len = 0; } if (os->get_rsp > 0) { g_obex_remove_request_function(os->obex, os->get_rsp); os->get_rsp = 0; } os->object = NULL; os->driver = NULL; os->aborted = FALSE; os->pending = 0; os->offset = 0; os->size = OBJECT_SIZE_DELETE; os->headers_sent = FALSE; os->checked = FALSE; } static void obex_session_free(struct obex_session *os) { if (os->io) { g_io_channel_shutdown(os->io, TRUE, NULL); g_io_channel_unref(os->io); } if (os->obex) g_obex_unref(os->obex); g_free(os->src); g_free(os->dst); g_free(os); } /* From Imendio's GnomeVFS OBEX module (om-utils.c) */ static time_t parse_iso8610(const char *val, int size) { time_t time, tz_offset = 0; struct tm tm; char *date; char tz; int nr; memset(&tm, 0, sizeof(tm)); /* According to spec the time doesn't have to be null terminated */ date = g_strndup(val, size); nr = sscanf(date, "%04u%02u%02uT%02u%02u%02u%c", &tm.tm_year, &tm.tm_mon, &tm.tm_mday, &tm.tm_hour, &tm.tm_min, &tm.tm_sec, &tz); g_free(date); if (nr < 6) { /* Invalid time format */ return -1; } tm.tm_year -= 1900; /* Year since 1900 */ tm.tm_mon--; /* Months since January, values 0-11 */ tm.tm_isdst = -1; /* Daylight savings information not avail */ #if defined(HAVE_TM_GMTOFF) tz_offset = tm.tm_gmtoff; #elif defined(HAVE_TIMEZONE) tz_offset = -timezone; if (tm.tm_isdst > 0) tz_offset += 3600; #endif time = mktime(&tm); if (nr == 7) { /* * Date/Time was in localtime (to remote device) * already. Since we don't know anything about the * timezone on that one we won't try to apply UTC offset */ time += tz_offset; } return time; } static void parse_service(struct obex_session *os, GObexPacket *req) { GObexHeader *hdr; const guint8 *target = NULL, *who = NULL; gsize target_size = 0, who_size = 0; hdr = g_obex_packet_get_header(req, G_OBEX_HDR_WHO); if (hdr == NULL) goto target; g_obex_header_get_bytes(hdr, &who, &who_size); target: hdr = g_obex_packet_get_header(req, G_OBEX_HDR_TARGET); if (hdr == NULL) goto probe; g_obex_header_get_bytes(hdr, &target, &target_size); probe: os->service = obex_service_driver_find(os->server->drivers, target, target_size, who, who_size); } static void cmd_connect(GObex *obex, GObexPacket *req, void *user_data) { struct obex_session *os = user_data; GObexPacket *rsp; GObexHeader *hdr; int err; DBG(""); print_event(G_OBEX_OP_CONNECT, -1); parse_service(os, req); if (os->service == NULL || os->service->connect == NULL) { error("Connect attempt to a non-supported target"); os_set_response(os, -EPERM); return; } DBG("Selected driver: %s", os->service->name); os->service_data = os->service->connect(os, &err); if (err < 0) { os_set_response(os, err); return; } os->cmd = G_OBEX_OP_CONNECT; rsp = g_obex_packet_new(G_OBEX_RSP_SUCCESS, TRUE, G_OBEX_HDR_INVALID); if (os->service->target) { hdr = g_obex_header_new_bytes(G_OBEX_HDR_WHO, os->service->target, os->service->target_size); g_obex_packet_add_header(rsp, hdr); } g_obex_send(obex, rsp, NULL); print_event(-1, 0); } static void cmd_disconnect(GObex *obex, GObexPacket *req, void *user_data) { struct obex_session *os = user_data; DBG("session %p", os); print_event(G_OBEX_OP_DISCONNECT, -1); os->cmd = G_OBEX_OP_DISCONNECT; os_set_response(os, 0); } static ssize_t driver_write(struct obex_session *os) { ssize_t len = 0; while (os->pending > 0) { ssize_t w; w = os->driver->write(os->object, os->buf + len, os->pending); if (w < 0) { error("write(): %s (%zd)", strerror(-w), -w); if (w == -EINTR) continue; else if (w == -EINVAL) memmove(os->buf, os->buf + len, os->pending); return w; } len += w; os->offset += w; os->pending -= w; } DBG("%zd written", len); if (os->service->progress != NULL) os->service->progress(os, os->service_data); return len; } static gssize driver_read(struct obex_session *os, void *buf, gsize size) { gssize len; if (os->object == NULL) return -EIO; if (os->service->progress != NULL) os->service->progress(os, os->service_data); len = os->driver->read(os->object, buf, size); if (len < 0) { error("read(): %s (%zd)", strerror(-len), -len); if (len == -ENOSTR) return 0; if (len == -EAGAIN) obex_object_set_io_watch(os->object, handle_async_io, os); } os->offset += len; DBG("%zd read", len); return len; } static gssize send_data(void *buf, gsize size, gpointer user_data) { struct obex_session *os = user_data; DBG("name=%s type=%s file=%p size=%zu", os->name, os->type, os->object, size); if (os->aborted) return os->err < 0 ? os->err : -EPERM; return driver_read(os, buf, size); } static void transfer_complete(GObex *obex, GError *err, gpointer user_data) { struct obex_session *os = user_data; DBG(""); if (err != NULL) { error("transfer failed: %s\n", err->message); goto reset; } if (os->object && os->driver && os->driver->flush) { if (os->driver->flush(os->object) == -EAGAIN) { g_obex_suspend(os->obex); obex_object_set_io_watch(os->object, handle_async_io, os); return; } } reset: os_reset_session(os); } static int driver_get_headers(struct obex_session *os) { GObexPacket *rsp; gssize len; guint8 data[255]; guint8 id; GObexHeader *hdr; DBG("name=%s type=%s object=%p", os->name, os->type, os->object); if (os->aborted) return os->err < 0 ? os->err : -EPERM; if (os->object == NULL) return -EIO; if (os->headers_sent) return 0; rsp = g_obex_packet_new(G_OBEX_RSP_CONTINUE, TRUE, G_OBEX_HDR_INVALID); if (os->driver->get_next_header == NULL) goto done; while ((len = os->driver->get_next_header(os->object, &data, sizeof(data), &id))) { if (len < 0) { error("get_next_header(): %s (%zd)", strerror(-len), -len); g_obex_packet_free(rsp); if (len == -EAGAIN) return len; g_free(os->buf); os->buf = NULL; return len; } hdr = g_obex_header_new_bytes(id, data, len); g_obex_packet_add_header(rsp, hdr); } done: if (os->size != OBJECT_SIZE_UNKNOWN && os->size < UINT32_MAX) { hdr = g_obex_header_new_uint32(G_OBEX_HDR_LENGTH, os->size); g_obex_packet_add_header(rsp, hdr); } g_obex_get_rsp_pkt(os->obex, rsp, send_data, transfer_complete, os, NULL); os->headers_sent = TRUE; print_event(-1, G_OBEX_RSP_CONTINUE); return 0; } static gboolean handle_async_io(void *object, int flags, int err, void *user_data) { struct obex_session *os = user_data; if (err < 0) goto done; if (flags & G_IO_OUT) err = driver_write(os); if ((flags & G_IO_IN) && !os->headers_sent) err = driver_get_headers(os); if (err == -EAGAIN) return TRUE; done: if (err < 0) { os->err = err; os->aborted = TRUE; } g_obex_resume(os->obex); return FALSE; } static gboolean recv_data(const void *buf, gsize size, gpointer user_data) { struct obex_session *os = user_data; ssize_t ret; DBG("name=%s type=%s file=%p size=%zu", os->name, os->type, os->object, size); if (os->aborted) return FALSE; /* workaround: client didn't send the object length */ if (os->size == OBJECT_SIZE_DELETE) os->size = OBJECT_SIZE_UNKNOWN; os->buf = g_realloc(os->buf, os->pending + size); memcpy(os->buf + os->pending, buf, size); os->pending += size; /* only write if both object and driver are valid */ if (os->object == NULL || os->driver == NULL) { DBG("Stored %" PRIu64 " bytes into temporary buffer", os->pending); return TRUE; } ret = driver_write(os); if (ret >= 0) return TRUE; if (ret == -EAGAIN) { g_obex_suspend(os->obex); obex_object_set_io_watch(os->object, handle_async_io, os); return TRUE; } return FALSE; } static void parse_type(struct obex_session *os, GObexPacket *req) { GObexHeader *hdr; const guint8 *type; gsize len; g_free(os->type); os->type = NULL; hdr = g_obex_packet_get_header(req, G_OBEX_HDR_TYPE); if (hdr == NULL) goto probe; if (!g_obex_header_get_bytes(hdr, &type, &len)) goto probe; /* Ensure null termination */ if (type[len - 1] != '\0') goto probe; os->type = g_strndup((const char *) type, len); DBG("TYPE: %s", os->type); probe: os->driver = obex_mime_type_driver_find(os->service->target, os->service->target_size, os->type, os->service->who, os->service->who_size); } static void parse_name(struct obex_session *os, GObexPacket *req) { GObexHeader *hdr; const char *name; g_free(os->name); os->name = NULL; hdr = g_obex_packet_get_header(req, G_OBEX_HDR_NAME); if (hdr == NULL) return; if (!g_obex_header_get_unicode(hdr, &name)) return; os->name = g_strdup(name); DBG("NAME: %s", os->name); } static void parse_apparam(struct obex_session *os, GObexPacket *req) { GObexHeader *hdr; const guint8 *apparam; gsize len; hdr = g_obex_packet_get_header(req, G_OBEX_HDR_APPARAM); if (hdr == NULL) return; if (!g_obex_header_get_bytes(hdr, &apparam, &len)) return; os->apparam = util_memdup(apparam, len); os->apparam_len = len; DBG("APPARAM"); } static void cmd_get(GObex *obex, GObexPacket *req, gpointer user_data) { struct obex_session *os = user_data; int err; DBG("session %p", os); print_event(G_OBEX_OP_GET, -1); if (os->service == NULL) { os_set_response(os, -EPERM); return; } if (os->service->get == NULL) { os_set_response(os, -ENOSYS); return; } os->headers_sent = FALSE; if (os->type) { g_free(os->type); os->type = NULL; } parse_type(os, req); if (!os->driver) { error("No driver found"); os_set_response(os, -ENOSYS); return; } os->cmd = G_OBEX_OP_GET; parse_name(os, req); parse_apparam(os, req); err = os->service->get(os, os->service_data); if (err == 0) return; os_set_response(os, err); } static void cmd_setpath(GObex *obex, GObexPacket *req, gpointer user_data) { struct obex_session *os = user_data; int err; DBG(""); print_event(G_OBEX_OP_SETPATH, -1); if (os->service == NULL) { err = -EPERM; goto done; } if (os->service->setpath == NULL) { err = -ENOSYS; goto done; } os->cmd = G_OBEX_OP_SETPATH; parse_name(os, req); os->nonhdr = g_obex_packet_get_data(req, &os->nonhdr_len); err = os->service->setpath(os, os->service_data); done: os_set_response(os, err); } int obex_get_stream_start(struct obex_session *os, const char *filename) { int err; void *object; size_t size = OBJECT_SIZE_UNKNOWN; object = os->driver->open(filename, O_RDONLY, 0, os->service_data, &size, &err); if (object == NULL) { error("open(%s): %s (%d)", filename, strerror(-err), -err); return err; } os->object = object; os->offset = 0; os->size = size; err = driver_get_headers(os); if (err != -EAGAIN) return err; g_obex_suspend(os->obex); obex_object_set_io_watch(os->object, handle_async_io, os); return 0; } int obex_put_stream_start(struct obex_session *os, const char *filename) { int err; os->object = os->driver->open(filename, O_WRONLY | O_CREAT | O_TRUNC, 0600, os->service_data, os->size != OBJECT_SIZE_UNKNOWN ? (size_t *) &os->size : NULL, &err); if (os->object == NULL) { error("open(%s): %s (%d)", filename, strerror(-err), -err); return err; } os->path = g_strdup(filename); return 0; } static void parse_length(struct obex_session *os, GObexPacket *req) { GObexHeader *hdr; guint32 size; hdr = g_obex_packet_get_header(req, G_OBEX_HDR_LENGTH); if (hdr == NULL) return; if (!g_obex_header_get_uint32(hdr, &size)) return; os->size = size; DBG("LENGTH: %" PRIu64, os->size); } static void parse_time(struct obex_session *os, GObexPacket *req) { GObexHeader *hdr; const guint8 *time; gsize len; hdr = g_obex_packet_get_header(req, G_OBEX_HDR_TIME); if (hdr == NULL) return; if (!g_obex_header_get_bytes(hdr, &time, &len)) return; os->time = parse_iso8610((const char *) time, len); DBG("TIME: %s", ctime(&os->time)); } static gboolean check_put(GObex *obex, GObexPacket *req, void *user_data) { struct obex_session *os = user_data; int ret; if (os->service->chkput == NULL) goto done; ret = os->service->chkput(os, os->service_data); switch (ret) { case 0: break; case -EAGAIN: g_obex_suspend(os->obex); obex_object_set_io_watch(os->object, handle_async_io, os); return TRUE; default: os_set_response(os, ret); return FALSE; } if (os->size == OBJECT_SIZE_DELETE || os->size == OBJECT_SIZE_UNKNOWN) DBG("Got a PUT without a Length"); done: os->checked = TRUE; return TRUE; } static void cmd_put(GObex *obex, GObexPacket *req, gpointer user_data) { struct obex_session *os = user_data; int err; DBG(""); print_event(G_OBEX_OP_PUT, -1); if (os->service == NULL) { os_set_response(os, -EPERM); return; } /* OPP session don't require CONNECT, in which case just call connect * callback to register the transfer. */ if (!os->service_data && os->service->service == OBEX_OPP) { os->service_data = os->service->connect(os, &err); if (err < 0) { os_set_response(os, err); return; } } parse_type(os, req); if (os->driver == NULL) { error("No driver found"); os_set_response(os, -ENOSYS); return; } os->cmd = G_OBEX_OP_PUT; /* Set size to unknown if a body header exists */ if (g_obex_packet_get_body(req)) os->size = OBJECT_SIZE_UNKNOWN; parse_name(os, req); parse_length(os, req); parse_time(os, req); parse_apparam(os, req); if (!os->checked) { if (!check_put(obex, req, user_data)) return; } if (os->service->put == NULL) { os_set_response(os, -ENOSYS); return; } err = os->service->put(os, os->service_data); if (err == 0) { g_obex_put_rsp(obex, req, recv_data, transfer_complete, os, NULL, G_OBEX_HDR_INVALID); print_event(G_OBEX_OP_PUT, G_OBEX_RSP_CONTINUE); return; } os_set_response(os, err); } static void parse_destname(struct obex_session *os, GObexPacket *req) { GObexHeader *hdr; const char *destname; g_free(os->destname); os->destname = NULL; hdr = g_obex_packet_get_header(req, G_OBEX_HDR_DESTNAME); if (hdr == NULL) return; if (!g_obex_header_get_unicode(hdr, &destname)) return; os->destname = g_strdup(destname); DBG("DESTNAME: %s", os->destname); } static void parse_action(struct obex_session *os, GObexPacket *req) { GObexHeader *hdr; guint8 id; hdr = g_obex_packet_get_header(req, G_OBEX_HDR_ACTION); if (hdr == NULL) return; if (!g_obex_header_get_uint8(hdr, &id)) return; os->action_id = id; DBG("ACTION: 0x%02x", os->action_id); } static void cmd_action(GObex *obex, GObexPacket *req, gpointer user_data) { struct obex_session *os = user_data; int err; DBG(""); print_event(G_OBEX_OP_ACTION, -1); if (os->service == NULL) { err = -EPERM; goto done; } if (os->service->action == NULL) { err = -ENOSYS; goto done; } os->cmd = G_OBEX_OP_ACTION; parse_name(os, req); parse_destname(os, req); parse_action(os, req); os->driver = obex_mime_type_driver_find(os->service->target, os->service->target_size, NULL, os->service->who, os->service->who_size); if (os->driver == NULL) { err = -ENOSYS; goto done; } err = os->service->action(os, os->service_data); done: os_set_response(os, err); } static void cmd_abort(GObex *obex, GObexPacket *req, gpointer user_data) { struct obex_session *os = user_data; DBG(""); print_event(G_OBEX_OP_ABORT, -1); os_reset_session(os); os_set_response(os, 0); } static void obex_session_destroy(struct obex_session *os) { DBG(""); os_reset_session(os); if (os->service && os->service->disconnect) os->service->disconnect(os, os->service_data); obex_session_free(os); } static void disconn_func(GObex *obex, GError *err, gpointer user_data) { struct obex_session *os = user_data; error("disconnected: %s\n", err ? err->message : ""); obex_session_destroy(os); } int obex_session_start(GIOChannel *io, uint16_t tx_mtu, uint16_t rx_mtu, gboolean stream, struct obex_server *server) { struct obex_session *os; GObex *obex; GObexTransportType type; static uint32_t id = 0; DBG(""); os = g_new0(struct obex_session, 1); os->id = ++id; os->service = obex_service_driver_find(server->drivers, NULL, 0, NULL, 0); os->server = server; os->size = OBJECT_SIZE_DELETE; type = stream ? G_OBEX_TRANSPORT_STREAM : G_OBEX_TRANSPORT_PACKET; obex = g_obex_new(io, type, rx_mtu, tx_mtu); if (!obex) { obex_session_free(os); return -EIO; } g_obex_set_disconnect_function(obex, disconn_func, os); g_obex_add_request_function(obex, G_OBEX_OP_CONNECT, cmd_connect, os); g_obex_add_request_function(obex, G_OBEX_OP_DISCONNECT, cmd_disconnect, os); g_obex_add_request_function(obex, G_OBEX_OP_PUT, cmd_put, os); g_obex_add_request_function(obex, G_OBEX_OP_GET, cmd_get, os); g_obex_add_request_function(obex, G_OBEX_OP_SETPATH, cmd_setpath, os); g_obex_add_request_function(obex, G_OBEX_OP_ACTION, cmd_action, os); g_obex_add_request_function(obex, G_OBEX_OP_ABORT, cmd_abort, os); os->obex = obex; os->io = g_io_channel_ref(io); obex_getsockname(os, &os->src); obex_getpeername(os, &os->dst); return 0; } const char *obex_get_name(struct obex_session *os) { return os->name; } const char *obex_get_destname(struct obex_session *os) { return os->destname; } void obex_set_name(struct obex_session *os, const char *name) { g_free(os->name); os->name = g_strdup(name); DBG("Name changed: %s", os->name); } ssize_t obex_get_size(struct obex_session *os) { return os->size; } const char *obex_get_type(struct obex_session *os) { return os->type; } int obex_remove(struct obex_session *os, const char *path) { if (os->driver == NULL) return -ENOSYS; return os->driver->remove(path); } int obex_copy(struct obex_session *os, const char *source, const char *destination) { if (os->driver == NULL || os->driver->copy == NULL) return -ENOSYS; DBG("%s %s", source, destination); return os->driver->copy(source, destination); } int obex_move(struct obex_session *os, const char *source, const char *destination) { if (os->driver == NULL || os->driver->move == NULL) return -ENOSYS; DBG("%s %s", source, destination); return os->driver->move(source, destination); } uint8_t obex_get_action_id(struct obex_session *os) { return os->action_id; } ssize_t obex_get_apparam(struct obex_session *os, const uint8_t **buffer) { *buffer = os->apparam; return os->apparam_len; } ssize_t obex_get_non_header_data(struct obex_session *os, const uint8_t **data) { *data = os->nonhdr; return os->nonhdr_len; } int obex_getpeername(struct obex_session *os, char **name) { const struct obex_transport_driver *transport = os->server->transport; if (transport == NULL || transport->getpeername == NULL) return -ENOTSUP; return transport->getpeername(os->io, name); } int obex_getsockname(struct obex_session *os, char **name) { const struct obex_transport_driver *transport = os->server->transport; if (transport == NULL || transport->getsockname == NULL) return -ENOTSUP; return transport->getsockname(os->io, name); } int memncmp0(const void *a, size_t na, const void *b, size_t nb) { if (na != nb) return na - nb; if (a == NULL) return -(a != b); if (b == NULL) return a != b; return memcmp(a, b, na); } bluez-5.82/obexd/src/PaxHeaders/transport.c0000644000000000000000000000005014572354773015757 xustar0020 atime=1743516740 20 ctime=1743591283 bluez-5.82/obexd/src/transport.c0000644000000000000000000000277314572354773015451 0ustar00rootroot// SPDX-License-Identifier: GPL-2.0-or-later /* * * OBEX Server * * Copyright (C) 2007-2010 Marcel Holtmann * * */ #ifdef HAVE_CONFIG_H #include #endif #include #include #include #include #include #include #include "obex.h" #include "server.h" #include "transport.h" #include "log.h" static GSList *drivers = NULL; static const struct obex_transport_driver * obex_transport_driver_find(const char *name) { const GSList *l; for (l = drivers; l; l = l->next) { const struct obex_transport_driver *driver = l->data; if (g_strcmp0(name, driver->name) == 0) return driver; } return NULL; } const GSList *obex_transport_driver_list(void) { return drivers; } int obex_transport_driver_register(const struct obex_transport_driver *driver) { if (!driver) { error("Invalid driver"); return -EINVAL; } if (obex_transport_driver_find(driver->name) != NULL) { error("Permission denied: transport %s already registered", driver->name); return -EPERM; } DBG("driver %p transport %s registered", driver, driver->name); drivers = g_slist_prepend(drivers, (gpointer)driver); return 0; } void obex_transport_driver_unregister(const struct obex_transport_driver *driver) { if (!g_slist_find(drivers, driver)) { error("Unable to unregister: No such driver %p", driver); return; } DBG("driver %p transport %s unregistered", driver, driver->name); drivers = g_slist_remove(drivers, driver); } bluez-5.82/obexd/src/PaxHeaders/obex.conf0000644000000000000000000000005014766002272015350 xustar0020 atime=1743515578 20 ctime=1743591288 bluez-5.82/obexd/src/obex.conf0000644000000000000000000000207014766002272015030 0ustar00rootroot bluez-5.82/obexd/src/PaxHeaders/mimetype.c0000644000000000000000000000005014572354773015554 xustar0020 atime=1743516739 20 ctime=1743591283 bluez-5.82/obexd/src/mimetype.c0000644000000000000000000000761214572354773015243 0ustar00rootroot// SPDX-License-Identifier: GPL-2.0-or-later /* * * OBEX Server * * Copyright (C) 2007-2010 Marcel Holtmann * * */ #ifdef HAVE_CONFIG_H #include #endif #define _GNU_SOURCE #include #include #include #include #include #include #include #include "log.h" #include "obex.h" #include "mimetype.h" static GSList *drivers = NULL; static GSList *watches = NULL; struct io_watch { void *object; obex_object_io_func func; void *user_data; }; void obex_object_set_io_flags(void *object, int flags, int err) { GSList *l; for (l = watches; l;) { struct io_watch *watch = l->data; l = l->next; if (watch->object != object) continue; if (watch->func(object, flags, err, watch->user_data) == TRUE) continue; if (g_slist_find(watches, watch) == NULL) continue; watches = g_slist_remove(watches, watch); g_free(watch); } } static struct io_watch *find_io_watch(void *object) { GSList *l; for (l = watches; l; l = l->next) { struct io_watch *watch = l->data; if (watch->object == object) return watch; } return NULL; } void obex_object_reset_io_watch(void *object) { struct io_watch *watch; watch = find_io_watch(object); if (watch == NULL) return; watches = g_slist_remove(watches, watch); g_free(watch); } int obex_object_set_io_watch(void *object, obex_object_io_func func, void *user_data) { struct io_watch *watch; watch = find_io_watch(object); if (watch) return -EPERM; watch = g_new0(struct io_watch, 1); watch->object = object; watch->func = func; watch->user_data = user_data; watches = g_slist_append(watches, watch); return 0; } static const struct obex_mime_type_driver *find_driver(const uint8_t *target, unsigned int target_size, const char *mimetype, const uint8_t *who, unsigned int who_size) { GSList *l; for (l = drivers; l; l = l->next) { const struct obex_mime_type_driver *driver = l->data; if (memncmp0(target, target_size, driver->target, driver->target_size)) continue; if (memncmp0(who, who_size, driver->who, driver->who_size)) continue; if (mimetype == NULL || driver->mimetype == NULL) { if (mimetype == driver->mimetype) return driver; else continue; } if (g_ascii_strcasecmp(mimetype, driver->mimetype) == 0) return driver; } return NULL; } const struct obex_mime_type_driver * obex_mime_type_driver_find(const uint8_t *target, unsigned int target_size, const char *mimetype, const uint8_t *who, unsigned int who_size) { const struct obex_mime_type_driver *driver; driver = find_driver(target, target_size, mimetype, who, who_size); if (driver == NULL) { if (who != NULL) { /* Fallback to non-who specific */ driver = find_driver(target, target_size, mimetype, NULL, 0); if (driver != NULL) return driver; } if (mimetype != NULL) /* Fallback to target default */ driver = find_driver(target, target_size, NULL, NULL, 0); if (driver == NULL) /* Fallback to general default */ driver = find_driver(NULL, 0, NULL, NULL, 0); } return driver; } int obex_mime_type_driver_register(const struct obex_mime_type_driver *driver) { if (!driver) { error("Invalid driver"); return -EINVAL; } if (find_driver(driver->target, driver->target_size, driver->mimetype, driver->who, driver->who_size)) { error("Permission denied: %s could not be registered", driver->mimetype); return -EPERM; } DBG("driver %p mimetype %s registered", driver, driver->mimetype); drivers = g_slist_append(drivers, (gpointer)driver); return 0; } void obex_mime_type_driver_unregister(const struct obex_mime_type_driver *driver) { if (!g_slist_find(drivers, driver)) { error("Unable to unregister: No such driver %p", driver); return; } DBG("driver %p mimetype %s unregistered", driver, driver->mimetype); drivers = g_slist_remove(drivers, driver); } bluez-5.82/obexd/src/PaxHeaders/service.h0000644000000000000000000000005014572354773015370 xustar0020 atime=1743516715 20 ctime=1743591283 bluez-5.82/obexd/src/service.h0000644000000000000000000000247514572354773015061 0ustar00rootroot/* SPDX-License-Identifier: GPL-2.0-or-later */ /* * * OBEX Server * * Copyright (C) 2007-2010 Marcel Holtmann * * */ #define OBEX_PORT_RANDOM UINT16_MAX struct obex_service_driver { const char *name; uint16_t service; uint8_t channel; uint16_t port; gboolean secure; const uint8_t *target; unsigned int target_size; const uint8_t *who; unsigned int who_size; const char *record; void *(*connect) (struct obex_session *os, int *err); void (*progress) (struct obex_session *os, void *user_data); int (*get) (struct obex_session *os, void *user_data); int (*put) (struct obex_session *os, void *user_data); int (*chkput) (struct obex_session *os, void *user_data); int (*setpath) (struct obex_session *os, void *user_data); int (*action) (struct obex_session *os, void *user_data); void (*disconnect) (struct obex_session *os, void *user_data); void (*reset) (struct obex_session *os, void *user_data); }; int obex_service_driver_register(const struct obex_service_driver *driver); void obex_service_driver_unregister(const struct obex_service_driver *driver); GSList *obex_service_driver_list(uint16_t services); const struct obex_service_driver *obex_service_driver_find(GSList *drivers, const uint8_t *target, unsigned int target_size, const uint8_t *who, unsigned int who_size); bluez-5.82/obexd/src/PaxHeaders/log.h0000644000000000000000000000005014015011623014461 xustar0020 atime=1743516713 20 ctime=1743591283 bluez-5.82/obexd/src/log.h0000644000000000000000000000225014015011623014141 0ustar00rootroot/* SPDX-License-Identifier: GPL-2.0-or-later */ /* * * OBEX Server * * Copyright (C) 2007-2010 Marcel Holtmann * * */ void info(const char *format, ...) __attribute__((format(printf, 1, 2))); void error(const char *format, ...) __attribute__((format(printf, 1, 2))); void obex_debug(const char *format, ...) __attribute__((format(printf, 1, 2))); void __obex_log_init(const char *debug, int detach); void __obex_log_cleanup(void); void __obex_log_enable_debug(void); struct obex_debug_desc { const char *name; const char *file; #define OBEX_DEBUG_FLAG_DEFAULT (0) #define OBEX_DEBUG_FLAG_PRINT (1 << 0) unsigned int flags; } __attribute__((aligned(8))); /** * DBG: * @fmt: format string * @arg...: list of arguments * * Simple macro around debug() which also include the function * name it is called in. */ #define DBG(fmt, arg...) do { \ static struct obex_debug_desc __obex_debug_desc \ __attribute__((used, section("__debug"), aligned(8))) = { \ .file = __FILE__, .flags = OBEX_DEBUG_FLAG_DEFAULT, \ }; \ if (__obex_debug_desc.flags & OBEX_DEBUG_FLAG_PRINT) \ obex_debug("%s:%s() " fmt, __FILE__, __func__ , ## arg); \ } while (0) bluez-5.82/obexd/src/PaxHeaders/plugin.h0000644000000000000000000000005014572354773015226 xustar0020 atime=1743516712 20 ctime=1743591283 bluez-5.82/obexd/src/plugin.h0000644000000000000000000000136414572354773014713 0ustar00rootroot/* SPDX-License-Identifier: GPL-2.0-or-later */ /* * * OBEX Server * * Copyright (C) 2007-2010 Marcel Holtmann * * */ struct obex_plugin_desc { const char *name; int (*init) (void); void (*exit) (void); }; #ifdef OBEX_PLUGIN_BUILTIN #define OBEX_PLUGIN_DEFINE(name, init, exit) \ const struct obex_plugin_desc __obex_builtin_ ## name = { \ #name, init, exit \ }; #else #if EXTERNAL_PLUGINS #define OBEX_PLUGIN_DEFINE(name,init,exit) \ extern struct obex_plugin_desc obex_plugin_desc \ __attribute__ ((visibility("default"))); \ const struct obex_plugin_desc obex_plugin_desc = { \ #name, init, exit \ }; #else #error "Requested non built-in plugin, while external plugins is disabled" #endif #endif bluez-5.82/obexd/src/PaxHeaders/map_ap.h0000644000000000000000000000005014015011623015135 xustar0020 atime=1743516726 20 ctime=1743591283 bluez-5.82/obexd/src/map_ap.h0000644000000000000000000000254414015011623014623 0ustar00rootroot/* SPDX-License-Identifier: GPL-2.0-or-later */ /* * * OBEX Server * * Copyright (C) 2010-2011 Nokia Corporation * * */ /* List of OBEX application parameters tags as per MAP specification. */ enum map_ap_tag { MAP_AP_MAXLISTCOUNT = 0x01, /* uint16_t */ MAP_AP_STARTOFFSET = 0x02, /* uint16_t */ MAP_AP_FILTERMESSAGETYPE = 0x03, /* uint8_t */ MAP_AP_FILTERPERIODBEGIN = 0x04, /* char * */ MAP_AP_FILTERPERIODEND = 0x05, /* char * */ MAP_AP_FILTERREADSTATUS = 0x06, /* uint8_t */ MAP_AP_FILTERRECIPIENT = 0x07, /* char * */ MAP_AP_FILTERORIGINATOR = 0x08, /* char * */ MAP_AP_FILTERPRIORITY = 0x09, /* uint8_t */ MAP_AP_ATTACHMENT = 0x0A, /* uint8_t */ MAP_AP_TRANSPARENT = 0x0B, /* uint8_t */ MAP_AP_RETRY = 0x0C, /* uint8_t */ MAP_AP_NEWMESSAGE = 0x0D, /* uint8_t */ MAP_AP_NOTIFICATIONSTATUS = 0x0E, /* uint8_t */ MAP_AP_MASINSTANCEID = 0x0F, /* uint8_t */ MAP_AP_PARAMETERMASK = 0x10, /* uint32_t */ MAP_AP_FOLDERLISTINGSIZE = 0x11, /* uint16_t */ MAP_AP_MESSAGESLISTINGSIZE = 0x12, /* uint16_t */ MAP_AP_SUBJECTLENGTH = 0x13, /* uint8_t */ MAP_AP_CHARSET = 0x14, /* uint8_t */ MAP_AP_FRACTIONREQUEST = 0x15, /* uint8_t */ MAP_AP_FRACTIONDELIVER = 0x16, /* uint8_t */ MAP_AP_STATUSINDICATOR = 0x17, /* uint8_t */ MAP_AP_STATUSVALUE = 0x18, /* uint8_t */ MAP_AP_MSETIME = 0x19, /* char * */ }; bluez-5.82/obexd/src/PaxHeaders/server.c0000644000000000000000000000005014572354773015231 xustar0020 atime=1743516741 20 ctime=1743591283 bluez-5.82/obexd/src/server.c0000644000000000000000000000435414572354773014720 0ustar00rootroot// SPDX-License-Identifier: GPL-2.0-or-later /* * * OBEX Server * * Copyright (C) 2007-2010 Nokia Corporation * Copyright (C) 2007-2010 Marcel Holtmann * * */ #ifdef HAVE_CONFIG_H #include #endif #include #include #include #include #include #include #include #include #include "gobex/gobex.h" #include "log.h" #include "obex.h" #include "obex-priv.h" #include "server.h" #include "service.h" #include "transport.h" static GSList *servers = NULL; static void init_server(uint16_t service, const GSList *transports) { const GSList *l; for (l = transports; l; l = l->next) { const struct obex_transport_driver *transport = l->data; struct obex_server *server; int err; if (transport->service != 0 && (transport->service & service) == FALSE) continue; server = g_new0(struct obex_server, 1); server->transport = transport; server->drivers = obex_service_driver_list(service); server->transport_data = transport->start(server, &err); if (server->transport_data == NULL) { DBG("Unable to start %s transport: %s (%d)", transport->name, strerror(err), err); g_free(server); continue; } servers = g_slist_prepend(servers, server); } } int obex_server_init(void) { GSList *drivers; const GSList *transports; GSList *l; drivers = obex_service_driver_list(0); if (drivers == NULL) { DBG("No service driver registered"); return -EINVAL; } transports = obex_transport_driver_list(); if (transports == NULL) { DBG("No transport driver registered"); return -EINVAL; } for (l = drivers; l; l = l->next) { const struct obex_service_driver *driver = l->data; init_server(driver->service, transports); } return 0; } void obex_server_exit(void) { GSList *l; for (l = servers; l; l = l->next) { struct obex_server *server = l->data; server->transport->stop(server->transport_data); g_slist_free(server->drivers); g_free(server); } g_slist_free(servers); return; } int obex_server_new_connection(struct obex_server *server, GIOChannel *io, uint16_t tx_mtu, uint16_t rx_mtu, gboolean stream) { return obex_session_start(io, tx_mtu, rx_mtu, stream, server); } bluez-5.82/obexd/src/PaxHeaders/transport.h0000644000000000000000000000005014572354773015764 xustar0020 atime=1743516715 20 ctime=1743591283 bluez-5.82/obexd/src/transport.h0000644000000000000000000000115514572354773015447 0ustar00rootroot/* SPDX-License-Identifier: GPL-2.0-or-later */ /* * * OBEX Server * * Copyright (C) 2007-2010 Marcel Holtmann * * */ struct obex_transport_driver { const char *name; uint16_t service; void *(*start) (struct obex_server *server, int *err); int (*getpeername) (GIOChannel *io, char **name); int (*getsockname) (GIOChannel *io, char **name); void (*stop) (void *data); }; int obex_transport_driver_register(const struct obex_transport_driver *driver); void obex_transport_driver_unregister(const struct obex_transport_driver *driver); const GSList *obex_transport_driver_list(void); bluez-5.82/obexd/src/PaxHeaders/obex.h0000644000000000000000000000005014015011623014635 xustar0020 atime=1743516715 20 ctime=1743591283 bluez-5.82/obexd/src/obex.h0000644000000000000000000000266014015011623014322 0ustar00rootroot/* SPDX-License-Identifier: GPL-2.0-or-later */ /* * * OBEX Server * * Copyright (C) 2007-2010 Nokia Corporation * Copyright (C) 2007-2010 Marcel Holtmann * * */ #include #define OBJECT_SIZE_UNKNOWN -1 #define OBJECT_SIZE_DELETE -2 #define TARGET_SIZE 16 struct obex_session; int obex_get_stream_start(struct obex_session *os, const char *filename); int obex_put_stream_start(struct obex_session *os, const char *filename); const char *obex_get_name(struct obex_session *os); const char *obex_get_destname(struct obex_session *os); void obex_set_name(struct obex_session *os, const char *name); ssize_t obex_get_size(struct obex_session *os); const char *obex_get_type(struct obex_session *os); int obex_remove(struct obex_session *os, const char *path); int obex_copy(struct obex_session *os, const char *source, const char *destination); int obex_move(struct obex_session *os, const char *source, const char *destination); uint8_t obex_get_action_id(struct obex_session *os); ssize_t obex_get_apparam(struct obex_session *os, const uint8_t **buffer); ssize_t obex_get_non_header_data(struct obex_session *os, const uint8_t **data); int obex_getpeername(struct obex_session *os, char **name); int obex_getsockname(struct obex_session *os, char **name); /* Just a thin wrapper around memcmp to deal with NULL values */ int memncmp0(const void *a, size_t na, const void *b, size_t nb); bluez-5.82/obexd/src/PaxHeaders/plugin.c0000644000000000000000000000005014572354773015221 xustar0020 atime=1743516732 20 ctime=1743591283 bluez-5.82/obexd/src/plugin.c0000644000000000000000000001026314572354773014704 0ustar00rootroot// SPDX-License-Identifier: GPL-2.0-or-later /* * * OBEX Server * * Copyright (C) 2007-2010 Marcel Holtmann * * */ #ifdef HAVE_CONFIG_H #include #endif #include #include #include #include #include #include "obexd.h" #include "plugin.h" #include "log.h" /* * Plugins that are using libraries with threads and their own mainloop * will crash on exit. This is a bug inside these libraries, but there is * nothing much that can be done about it. One bad example is libebook. */ #ifdef NEED_THREADS #define PLUGINFLAG (RTLD_NOW | RTLD_NODELETE) #else #define PLUGINFLAG (RTLD_NOW) #endif #define IS_ENABLED(x) (x) static GSList *plugins = NULL; struct obex_plugin { void *handle; const struct obex_plugin_desc *desc; }; static gboolean add_external_plugin(void *handle, const struct obex_plugin_desc *desc) { struct obex_plugin *plugin; if (desc->init == NULL) return FALSE; plugin = g_try_new0(struct obex_plugin, 1); if (plugin == NULL) return FALSE; plugin->handle = handle; plugin->desc = desc; if (desc->init() < 0) { g_free(plugin); return FALSE; } plugins = g_slist_append(plugins, plugin); DBG("Plugin %s loaded", desc->name); return TRUE; } static void add_plugin(const struct obex_plugin_desc *desc) { struct obex_plugin *plugin; plugin = g_try_new0(struct obex_plugin, 1); if (plugin == NULL) return; plugin->desc = desc; if (desc->init() < 0) { g_free(plugin); return; } plugins = g_slist_append(plugins, plugin); DBG("Plugin %s loaded", desc->name); } static gboolean check_plugin(const struct obex_plugin_desc *desc, char **patterns, char **excludes) { if (excludes) { for (; *excludes; excludes++) if (g_pattern_match_simple(*excludes, desc->name)) break; if (*excludes) { info("Excluding %s", desc->name); return FALSE; } } if (patterns) { for (; *patterns; patterns++) if (g_pattern_match_simple(*patterns, desc->name)) break; if (*patterns == NULL) { info("Ignoring %s", desc->name); return FALSE; } } return TRUE; } static void external_plugin_init(char **patterns, char **excludes) { GDir *dir; const char *file; info("Using external plugins is not officially supported.\n"); info("Consider upstreaming your plugins into the BlueZ project."); if (strlen(PLUGINDIR) == 0) return; DBG("Loading plugins %s", PLUGINDIR); dir = g_dir_open(PLUGINDIR, 0, NULL); if (!dir) { return; } while ((file = g_dir_read_name(dir)) != NULL) { const struct obex_plugin_desc *desc; void *handle; char *filename; if (g_str_has_prefix(file, "lib") == TRUE || g_str_has_suffix(file, ".so") == FALSE) continue; filename = g_build_filename(PLUGINDIR, file, NULL); handle = dlopen(filename, PLUGINFLAG); if (handle == NULL) { error("Can't load plugin %s: %s", filename, dlerror()); g_free(filename); continue; } g_free(filename); desc = dlsym(handle, "obex_plugin_desc"); if (desc == NULL) { error("Can't load plugin description: %s", dlerror()); dlclose(handle); continue; } if (check_plugin(desc, patterns, excludes) == FALSE) { dlclose(handle); continue; } if (add_external_plugin(handle, desc) == FALSE) dlclose(handle); } g_dir_close(dir); } #include "builtin.h" void plugin_init(const char *pattern, const char *exclude) { char **patterns = NULL; char **excludes = NULL; unsigned int i; if (pattern) patterns = g_strsplit_set(pattern, ":, ", -1); if (exclude) excludes = g_strsplit_set(exclude, ":, ", -1); DBG("Loading builtin plugins"); for (i = 0; __obex_builtin[i]; i++) { if (check_plugin(__obex_builtin[i], patterns, excludes) == FALSE) continue; add_plugin(__obex_builtin[i]); } if IS_ENABLED(EXTERNAL_PLUGINS) external_plugin_init(patterns, excludes); g_strfreev(patterns); g_strfreev(excludes); } void plugin_cleanup(void) { GSList *list; DBG("Cleanup plugins"); for (list = plugins; list; list = list->next) { struct obex_plugin *plugin = list->data; if (plugin->desc->exit) plugin->desc->exit(); if (plugin->handle != NULL) dlclose(plugin->handle); g_free(plugin); } g_slist_free(plugins); } bluez-5.82/obexd/src/PaxHeaders/obex-priv.h0000644000000000000000000000005014572354773015643 xustar0020 atime=1743516735 20 ctime=1743591283 bluez-5.82/obexd/src/obex-priv.h0000644000000000000000000000161414572354773015326 0ustar00rootroot/* SPDX-License-Identifier: GPL-2.0-or-later */ /* * * OBEX Server * * Copyright (C) 2007-2010 Nokia Corporation * Copyright (C) 2007-2010 Marcel Holtmann * * */ struct obex_session { GIOChannel *io; uint32_t id; uint8_t cmd; uint8_t action_id; char *src; char *dst; char *name; char *destname; char *type; char *path; time_t time; uint8_t *apparam; size_t apparam_len; const void *nonhdr; size_t nonhdr_len; guint get_rsp; uint8_t *buf; int64_t pending; int64_t offset; int64_t size; void *object; gboolean aborted; int err; const struct obex_service_driver *service; void *service_data; struct obex_server *server; gboolean checked; GObex *obex; const struct obex_mime_type_driver *driver; gboolean headers_sent; }; int obex_session_start(GIOChannel *io, uint16_t tx_mtu, uint16_t rx_mtu, gboolean stream, struct obex_server *server); bluez-5.82/obexd/src/PaxHeaders/main.c0000644000000000000000000000005014766002272014634 xustar0020 atime=1743515578 20 ctime=1743591283 bluez-5.82/obexd/src/main.c0000644000000000000000000001671714766002272014331 0ustar00rootroot// SPDX-License-Identifier: GPL-2.0-or-later /* * * OBEX Server * * Copyright (C) 2007-2010 Marcel Holtmann * * */ #ifdef HAVE_CONFIG_H #include #endif #define _GNU_SOURCE #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "gdbus/gdbus.h" #include "../client/manager.h" #include "log.h" #include "obexd.h" #include "server.h" #define DEFAULT_CAP_FILE CONFIGDIR "/capability.xml" static GMainLoop *main_loop = NULL; static DBusConnection *connection; static gboolean signal_handler(GIOChannel *channel, GIOCondition cond, gpointer user_data) { static unsigned int __terminated = 0; struct signalfd_siginfo si; ssize_t result; int fd; if (cond & (G_IO_NVAL | G_IO_ERR | G_IO_HUP)) return FALSE; fd = g_io_channel_unix_get_fd(channel); result = read(fd, &si, sizeof(si)); if (result != sizeof(si)) return FALSE; switch (si.ssi_signo) { case SIGINT: case SIGTERM: if (__terminated == 0) { info("Terminating"); g_main_loop_quit(main_loop); } __terminated = 1; break; case SIGUSR2: __obex_log_enable_debug(); break; } return TRUE; } static guint setup_signalfd(void) { GIOChannel *channel; guint source; sigset_t mask; int fd; sigemptyset(&mask); sigaddset(&mask, SIGINT); sigaddset(&mask, SIGTERM); sigaddset(&mask, SIGUSR2); if (sigprocmask(SIG_BLOCK, &mask, NULL) < 0) { perror("Failed to set signal mask"); return 0; } fd = signalfd(-1, &mask, 0); if (fd < 0) { perror("Failed to create signal descriptor"); return 0; } channel = g_io_channel_unix_new(fd); g_io_channel_set_close_on_unref(channel, TRUE); g_io_channel_set_encoding(channel, NULL, NULL); g_io_channel_set_buffered(channel, FALSE); source = g_io_add_watch(channel, G_IO_IN | G_IO_HUP | G_IO_ERR | G_IO_NVAL, signal_handler, NULL); g_io_channel_unref(channel); return source; } static gboolean option_detach = TRUE; static char *option_debug = NULL; static char *option_root = NULL; static char *option_root_setup = NULL; static char *option_capability = NULL; static char *option_plugin = NULL; static char *option_noplugin = NULL; static gboolean option_autoaccept = FALSE; static gboolean option_symlinks = FALSE; static gboolean option_system_bus = FALSE; static gboolean parse_debug(const char *key, const char *value, gpointer user_data, GError **error) { if (value) option_debug = g_strdup(value); else option_debug = g_strdup("*"); return TRUE; } static const GOptionEntry options[] = { { "debug", 'd', G_OPTION_FLAG_OPTIONAL_ARG, G_OPTION_ARG_CALLBACK, parse_debug, "Enable debug information output", "DEBUG" }, { "plugin", 'p', 0, G_OPTION_ARG_STRING, &option_plugin, "Specify plugins to load", "NAME,..." }, { "noplugin", 'P', 0, G_OPTION_ARG_STRING, &option_noplugin, "Specify plugins not to load", "NAME,..." }, { "nodetach", 'n', G_OPTION_FLAG_REVERSE, G_OPTION_ARG_NONE, &option_detach, "Run with logging in foreground" }, { "root", 'r', 0, G_OPTION_ARG_STRING, &option_root, "Specify root folder location. Both absolute " "and relative can be used, but relative paths " "are assumed to be relative to user $HOME " "folder. Default $XDG_CACHE_HOME", "PATH" }, { "root-setup", 'S', 0, G_OPTION_ARG_STRING, &option_root_setup, "Root folder setup script", "SCRIPT" }, { "symlinks", 'l', 0, G_OPTION_ARG_NONE, &option_symlinks, "Allow symlinks leading outside of the root " "folder" }, { "capability", 'c', 0, G_OPTION_ARG_STRING, &option_capability, "Specify capability file, use '!' mark for " "scripts", "FILE" }, { "auto-accept", 'a', 0, G_OPTION_ARG_NONE, &option_autoaccept, "Automatically accept push requests" }, { "system-bus", 's', 0, G_OPTION_ARG_NONE, &option_system_bus, "Use System bus "}, { NULL }, }; gboolean obex_option_auto_accept(void) { return option_autoaccept; } const char *obex_option_root_folder(void) { return option_root; } gboolean obex_option_symlinks(void) { return option_symlinks; } const char *obex_option_capability(void) { return option_capability; } static gboolean is_dir(const char *dir) { struct stat st; if (stat(dir, &st) < 0) { error("stat(%s): %s (%d)", dir, strerror(errno), errno); return FALSE; } return S_ISDIR(st.st_mode); } static gboolean root_folder_setup(char *root, char *root_setup) { int status; char *argv[3] = { root_setup, root, NULL }; if (is_dir(root)) return TRUE; if (root_setup == NULL) return FALSE; DBG("Setting up %s using %s", root, root_setup); if (!g_spawn_sync(NULL, argv, NULL, 0, NULL, NULL, NULL, NULL, &status, NULL)) { error("Unable to execute %s", root_setup); return FALSE; } if (WEXITSTATUS(status) != EXIT_SUCCESS) { error("%s exited with status %d", root_setup, WEXITSTATUS(status)); return FALSE; } return is_dir(root); } DBusConnection *obex_get_dbus_connection(void) { if (connection) return dbus_connection_ref(connection); connection = dbus_bus_get(option_system_bus ? DBUS_BUS_SYSTEM : DBUS_BUS_SESSION, NULL); return connection; } DBusConnection *obex_setup_dbus_connection(const char *name, DBusError *error) { connection = g_dbus_setup_bus(option_system_bus ? DBUS_BUS_SYSTEM : DBUS_BUS_SESSION, name, error); return connection; } int main(int argc, char *argv[]) { GOptionContext *context; GError *err = NULL; guint signal; context = g_option_context_new(NULL); g_option_context_add_main_entries(context, options, NULL); if (g_option_context_parse(context, &argc, &argv, &err) == FALSE) { if (err != NULL) { g_printerr("%s\n", err->message); g_error_free(err); } else g_printerr("An unknown error occurred\n"); exit(EXIT_FAILURE); } g_option_context_free(context); __obex_log_init(option_debug, option_detach); DBG("Entering main loop"); main_loop = g_main_loop_new(NULL, FALSE); signal = setup_signalfd(); #ifdef NEED_THREADS if (dbus_threads_init_default() == FALSE) { fprintf(stderr, "Can't init usage of threads\n"); exit(EXIT_FAILURE); } #endif if (manager_init() == FALSE) { error("manager_init failed"); exit(EXIT_FAILURE); } if (option_root == NULL) { option_root = g_build_filename(g_get_user_cache_dir(), "obexd", NULL); if (g_mkdir_with_parents(option_root, 0700) < 0) error("Failed to create dir(%d): %s", errno, option_root); } if (option_root[0] != '/') { const char *home = getenv("HOME"); if (home) { char *old_root = option_root; option_root = g_strdup_printf("%s/%s", home, old_root); g_free(old_root); } } if (option_capability == NULL) option_capability = g_strdup(DEFAULT_CAP_FILE); plugin_init(option_plugin, option_noplugin); if (obex_server_init() < 0) { error("obex_server_init failed"); exit(EXIT_FAILURE); } if (!root_folder_setup(option_root, option_root_setup)) { error("Unable to setup root folder %s", option_root); exit(EXIT_FAILURE); } if (client_manager_init() < 0) { error("client_manager_init failed"); exit(EXIT_FAILURE); } g_main_loop_run(main_loop); g_source_remove(signal); client_manager_exit(); obex_server_exit(); plugin_cleanup(); manager_cleanup(); g_main_loop_unref(main_loop); g_free(option_capability); g_free(option_root); __obex_log_cleanup(); return 0; } bluez-5.82/obexd/src/PaxHeaders/obexd.h0000644000000000000000000000005014766002272015016 xustar0020 atime=1743515578 20 ctime=1743591283 bluez-5.82/obexd/src/obexd.h0000644000000000000000000000154314766002272014502 0ustar00rootroot/* SPDX-License-Identifier: GPL-2.0-or-later */ /* * * OBEX Server * * Copyright (C) 2007-2010 Marcel Holtmann * * */ #include #define OBEX_OPP (1 << 1) #define OBEX_FTP (1 << 2) #define OBEX_BIP (1 << 3) #define OBEX_PBAP (1 << 4) #define OBEX_IRMC (1 << 5) #define OBEX_PCSUITE (1 << 6) #define OBEX_SYNCEVOLUTION (1 << 7) #define OBEX_MAS (1 << 8) #define OBEX_MNS (1 << 9) void plugin_init(const char *pattern, const char *exclude); void plugin_cleanup(void); gboolean manager_init(void); void manager_cleanup(void); gboolean obex_option_auto_accept(void); const char *obex_option_root_folder(void); gboolean obex_option_symlinks(void); const char *obex_option_capability(void); DBusConnection *obex_get_dbus_connection(void); DBusConnection *obex_setup_dbus_connection(const char *name, DBusError *error); bluez-5.82/obexd/src/PaxHeaders/manager.h0000644000000000000000000000005014015011623015312 xustar0020 atime=1743516716 20 ctime=1743591283 bluez-5.82/obexd/src/manager.h0000644000000000000000000000166714015011623015005 0ustar00rootroot/* SPDX-License-Identifier: GPL-2.0-or-later */ /* * * OBEX Server * * Copyright (C) 2007-2010 Nokia Corporation * * */ #include #define OBEXD_SERVICE "org.bluez.obex" struct obex_session; struct obex_transfer; void manager_register_session(struct obex_session *os); void manager_unregister_session(struct obex_session *os); struct obex_transfer *manager_register_transfer(struct obex_session *os); void manager_unregister_transfer(struct obex_transfer *transfer); void manager_emit_transfer_property(struct obex_transfer *transfer, char *name); void manager_emit_transfer_started(struct obex_transfer *transfer); void manager_emit_transfer_progress(struct obex_transfer *transfer); void manager_emit_transfer_completed(struct obex_transfer *transfer); int manager_request_authorization(struct obex_transfer *transfer, char **new_folder, char **new_name); DBusConnection *manager_dbus_get_connection(void); bluez-5.82/obexd/src/PaxHeaders/service.c0000644000000000000000000000005014572354773015363 xustar0020 atime=1743516739 20 ctime=1743591283 bluez-5.82/obexd/src/service.c0000644000000000000000000000460214572354773015046 0ustar00rootroot// SPDX-License-Identifier: GPL-2.0-or-later /* * * OBEX Server * * Copyright (C) 2007-2010 Marcel Holtmann * * */ #ifdef HAVE_CONFIG_H #include #endif #include #include #include #include #include #include #include "obex.h" #include "service.h" #include "log.h" static GSList *drivers = NULL; const struct obex_service_driver *obex_service_driver_find(GSList *drivers, const uint8_t *target, unsigned int target_size, const uint8_t *who, unsigned int who_size) { GSList *l; for (l = drivers; l; l = l->next) { const struct obex_service_driver *driver = l->data; /* who is optional, so only check for it if not NULL */ if (who != NULL && memncmp0(who, who_size, driver->who, driver->who_size)) continue; if (memncmp0(target, target_size, driver->target, driver->target_size) == 0) return driver; } return NULL; } GSList *obex_service_driver_list(uint16_t services) { GSList *l; GSList *list = NULL; if (services == 0) return drivers; for (l = drivers; l && services; l = l->next) { const struct obex_service_driver *driver = l->data; if (driver->service & services) { list = g_slist_append(list, (gpointer)driver); services &= ~driver->service; } } return list; } static const struct obex_service_driver *find_driver(uint16_t service) { GSList *l; for (l = drivers; l; l = l->next) { const struct obex_service_driver *driver = l->data; if (driver->service == service) return driver; } return NULL; } int obex_service_driver_register(const struct obex_service_driver *driver) { if (!driver) { error("Invalid driver"); return -EINVAL; } if (find_driver(driver->service)) { error("Permission denied: service %s already registered", driver->name); return -EPERM; } DBG("driver %p service %s registered", driver, driver->name); /* Drivers that support who has priority */ if (driver->who) drivers = g_slist_prepend(drivers, (gpointer)driver); else drivers = g_slist_append(drivers, (gpointer)driver); return 0; } void obex_service_driver_unregister(const struct obex_service_driver *driver) { if (!g_slist_find(drivers, driver)) { error("Unable to unregister: No such driver %p", driver); return; } DBG("driver %p service %s unregistered", driver, driver->name); drivers = g_slist_remove(drivers, driver); } bluez-5.82/obexd/src/PaxHeaders/mimetype.h0000644000000000000000000000005014572354773015561 xustar0020 atime=1743516713 20 ctime=1743591283 bluez-5.82/obexd/src/mimetype.h0000644000000000000000000000271514572354773015247 0ustar00rootroot/* SPDX-License-Identifier: GPL-2.0-or-later */ /* * * OBEX Server * * Copyright (C) 2007-2010 Marcel Holtmann * * */ typedef gboolean (*obex_object_io_func) (void *object, int flags, int err, void *user_data); struct obex_mime_type_driver { const uint8_t *target; unsigned int target_size; const char *mimetype; const uint8_t *who; unsigned int who_size; void *(*open) (const char *name, int oflag, mode_t mode, void *driver_data, size_t *size, int *err); int (*close) (void *object); ssize_t (*get_next_header)(void *object, void *buf, size_t mtu, uint8_t *hi); ssize_t (*read) (void *object, void *buf, size_t count); ssize_t (*write) (void *object, const void *buf, size_t count); int (*flush) (void *object); int (*copy) (const char *name, const char *destname); int (*move) (const char *name, const char *destname); int (*remove) (const char *name); }; int obex_mime_type_driver_register(const struct obex_mime_type_driver *driver); void obex_mime_type_driver_unregister(const struct obex_mime_type_driver *driver); const struct obex_mime_type_driver * obex_mime_type_driver_find(const uint8_t *target, unsigned int target_size, const char *mimetype, const uint8_t *who, unsigned int who_size); void obex_object_set_io_flags(void *object, int flags, int err); void obex_object_reset_io_watch(void *object); int obex_object_set_io_watch(void *object, obex_object_io_func func, void *user_data); bluez-5.82/obexd/src/PaxHeaders/obex.service.in0000644000000000000000000000005014572354773016503 xustar0020 atime=1743515749 20 ctime=1743591275 bluez-5.82/obexd/src/obex.service.in0000644000000000000000000000024314572354773016163 0ustar00rootroot[Unit] Description=Bluetooth OBEX service [Service] Type=dbus BusName=org.bluez.obex ExecStart=@PKGLIBEXECDIR@/obexd [Install] Alias=dbus-org.bluez.obex.service bluez-5.82/obexd/src/PaxHeaders/manager.c0000644000000000000000000000005014766002272015322 xustar0020 atime=1743515578 20 ctime=1743591283 bluez-5.82/obexd/src/manager.c0000644000000000000000000004413514766002272015012 0ustar00rootroot// SPDX-License-Identifier: GPL-2.0-or-later /* * * OBEX Server * * Copyright (C) 2007-2010 Marcel Holtmann * * */ #ifdef HAVE_CONFIG_H #include #endif #include #include #include #include #include #include #include "gdbus/gdbus.h" #include "gobex/gobex.h" #include "btio/btio.h" #include "obexd.h" #include "obex.h" #include "obex-priv.h" #include "server.h" #include "manager.h" #include "log.h" #include "service.h" #define OBEX_BASE_PATH "/org/bluez/obex" #define SESSION_BASE_PATH OBEX_BASE_PATH "/server" #define OBEX_MANAGER_INTERFACE OBEXD_SERVICE ".AgentManager1" #define ERROR_INTERFACE OBEXD_SERVICE ".Error" #define TRANSFER_INTERFACE OBEXD_SERVICE ".Transfer1" #define SESSION_INTERFACE OBEXD_SERVICE ".Session1" #define AGENT_INTERFACE OBEXD_SERVICE ".Agent1" #define OBEX_ERROR_REJECT "org.bluez.obex.Error.Rejected" #define TIMEOUT 60*1000 /* Timeout for user response (miliseconds) */ struct agent { char *bus_name; char *path; gboolean auth_pending; gboolean auth_reject; char *new_name; char *new_folder; unsigned int watch_id; }; enum { TRANSFER_STATUS_QUEUED = 0, TRANSFER_STATUS_ACTIVE, TRANSFER_STATUS_COMPLETE, TRANSFER_STATUS_ERROR }; struct obex_transfer { uint8_t status; char *path; struct obex_session *session; }; static struct agent *agent = NULL; static DBusConnection *connection = NULL; static void agent_free(struct agent *agent) { if (!agent) return; g_free(agent->new_folder); g_free(agent->new_name); g_free(agent->bus_name); g_free(agent->path); g_free(agent); } static inline DBusMessage *invalid_args(DBusMessage *msg) { return g_dbus_create_error(msg, ERROR_INTERFACE ".InvalidArguments", "Invalid arguments in method call"); } static inline DBusMessage *not_supported(DBusMessage *msg) { return g_dbus_create_error(msg, ERROR_INTERFACE ".NotSupported", "Operation is not supported"); } static inline DBusMessage *agent_already_exists(DBusMessage *msg) { return g_dbus_create_error(msg, ERROR_INTERFACE ".AlreadyExists", "Agent already exists"); } static inline DBusMessage *agent_does_not_exist(DBusMessage *msg) { return g_dbus_create_error(msg, ERROR_INTERFACE ".DoesNotExist", "Agent does not exist"); } static inline DBusMessage *not_authorized(DBusMessage *msg) { return g_dbus_create_error(msg, ERROR_INTERFACE ".NotAuthorized", "Not authorized"); } static void agent_disconnected(DBusConnection *conn, void *user_data) { DBG("Agent exited"); agent_free(agent); agent = NULL; } static DBusMessage *register_agent(DBusConnection *conn, DBusMessage *msg, void *data) { const char *path, *sender; if (agent) return agent_already_exists(msg); if (!dbus_message_get_args(msg, NULL, DBUS_TYPE_OBJECT_PATH, &path, DBUS_TYPE_INVALID)) return invalid_args(msg); sender = dbus_message_get_sender(msg); agent = g_new0(struct agent, 1); agent->bus_name = g_strdup(sender); agent->path = g_strdup(path); agent->watch_id = g_dbus_add_disconnect_watch(conn, sender, agent_disconnected, NULL, NULL); DBG("Agent registered"); return dbus_message_new_method_return(msg); } static DBusMessage *unregister_agent(DBusConnection *conn, DBusMessage *msg, void *data) { const char *path, *sender; if (!agent) return agent_does_not_exist(msg); if (!dbus_message_get_args(msg, NULL, DBUS_TYPE_OBJECT_PATH, &path, DBUS_TYPE_INVALID)) return invalid_args(msg); if (strcmp(agent->path, path) != 0) return agent_does_not_exist(msg); sender = dbus_message_get_sender(msg); if (strcmp(agent->bus_name, sender) != 0) return not_authorized(msg); g_dbus_remove_watch(conn, agent->watch_id); agent_free(agent); agent = NULL; DBG("Agent unregistered"); return dbus_message_new_method_return(msg); } static gboolean get_source(const GDBusPropertyTable *property, DBusMessageIter *iter, void *data) { struct obex_session *os = data; char *s; s = os->src; dbus_message_iter_append_basic(iter, DBUS_TYPE_STRING, &s); return TRUE; } static gboolean get_destination(const GDBusPropertyTable *property, DBusMessageIter *iter, void *data) { struct obex_session *os = data; char *s; s = os->dst; dbus_message_iter_append_basic(iter, DBUS_TYPE_STRING, &s); return TRUE; } static gboolean session_target_exists(const GDBusPropertyTable *property, void *data) { struct obex_session *os = data; return os->service->target ? TRUE : FALSE; } static char *target2str(const uint8_t *t) { if (!t) return NULL; return g_strdup_printf("%02X%02X%02X%02X-%02X%02X-%02X%02X-" "%02X%02X-%02X%02X%02X%02X%02X%02X", t[0], t[1], t[2], t[3], t[4], t[5], t[6], t[7], t[8], t[9], t[10], t[11], t[12], t[13], t[14], t[15]); } static gboolean get_target(const GDBusPropertyTable *property, DBusMessageIter *iter, void *data) { struct obex_session *os = data; char *uuid; uuid = target2str(os->service->target); dbus_message_iter_append_basic(iter, DBUS_TYPE_STRING, &uuid); g_free(uuid); return TRUE; } static gboolean get_root(const GDBusPropertyTable *property, DBusMessageIter *iter, void *data) { const char *root; root = obex_option_root_folder(); dbus_message_iter_append_basic(iter, DBUS_TYPE_STRING, &root); return TRUE; } static DBusMessage *transfer_cancel(DBusConnection *connection, DBusMessage *msg, void *user_data) { struct obex_transfer *transfer = user_data; struct obex_session *os = transfer->session; const char *sender; if (!agent) return agent_does_not_exist(msg); if (!os) return invalid_args(msg); sender = dbus_message_get_sender(msg); if (strcmp(agent->bus_name, sender) != 0) return not_authorized(msg); os->aborted = TRUE; return dbus_message_new_method_return(msg); } static const char *status2str(uint8_t status) { switch (status) { case TRANSFER_STATUS_QUEUED: return "queued"; case TRANSFER_STATUS_ACTIVE: return "active"; case TRANSFER_STATUS_COMPLETE: return "complete"; case TRANSFER_STATUS_ERROR: default: return "error"; } } static gboolean transfer_get_status(const GDBusPropertyTable *property, DBusMessageIter *iter, void *data) { struct obex_transfer *transfer = data; const char *status = status2str(transfer->status); dbus_message_iter_append_basic(iter, DBUS_TYPE_STRING, &status); return TRUE; } static gboolean transfer_get_session(const GDBusPropertyTable *property, DBusMessageIter *iter, void *data) { struct obex_transfer *transfer = data; struct obex_session *session = transfer->session; char *path; if (session == NULL) return FALSE; path = g_strdup_printf("%s/session%u", SESSION_BASE_PATH, session->id); dbus_message_iter_append_basic(iter, DBUS_TYPE_OBJECT_PATH, &path); g_free(path); return TRUE; } static gboolean transfer_name_exists(const GDBusPropertyTable *property, void *data) { struct obex_transfer *transfer = data; struct obex_session *session = transfer->session; return session->name != NULL; } static gboolean transfer_get_name(const GDBusPropertyTable *property, DBusMessageIter *iter, void *data) { struct obex_transfer *transfer = data; struct obex_session *session = transfer->session; if (session->name == NULL) return FALSE; dbus_message_iter_append_basic(iter, DBUS_TYPE_STRING, &session->name); return TRUE; } static gboolean transfer_type_exists(const GDBusPropertyTable *property, void *data) { struct obex_transfer *transfer = data; struct obex_session *session = transfer->session; return session->type != NULL; } static gboolean transfer_get_type(const GDBusPropertyTable *property, DBusMessageIter *iter, void *data) { struct obex_transfer *transfer = data; struct obex_session *session = transfer->session; if (session->type == NULL) return FALSE; dbus_message_iter_append_basic(iter, DBUS_TYPE_STRING, &session->type); return TRUE; } static gboolean transfer_size_exists(const GDBusPropertyTable *property, void *data) { struct obex_transfer *transfer = data; struct obex_session *session = transfer->session; return (session->size != OBJECT_SIZE_UNKNOWN && session->size != OBJECT_SIZE_DELETE); } static gboolean transfer_get_size(const GDBusPropertyTable *property, DBusMessageIter *iter, void *data) { struct obex_transfer *transfer = data; struct obex_session *session = transfer->session; if (session->size == OBJECT_SIZE_UNKNOWN || session->size == OBJECT_SIZE_DELETE) return FALSE; dbus_message_iter_append_basic(iter, DBUS_TYPE_UINT64, &session->size); return TRUE; } static gboolean transfer_time_exists(const GDBusPropertyTable *property, void *data) { struct obex_transfer *transfer = data; struct obex_session *session = transfer->session; return session->time != 0; } static gboolean transfer_get_time(const GDBusPropertyTable *property, DBusMessageIter *iter, void *data) { struct obex_transfer *transfer = data; struct obex_session *session = transfer->session; dbus_uint64_t time_u64; if (session->size == 0) return FALSE; time_u64 = session->time; dbus_message_iter_append_basic(iter, DBUS_TYPE_UINT64, &time_u64); return TRUE; } static gboolean transfer_filename_exists(const GDBusPropertyTable *property, void *data) { struct obex_transfer *transfer = data; struct obex_session *session = transfer->session; return session->path != NULL; } static gboolean transfer_get_filename(const GDBusPropertyTable *property, DBusMessageIter *iter, void *data) { struct obex_transfer *transfer = data; struct obex_session *session = transfer->session; if (session->path == NULL) return FALSE; dbus_message_iter_append_basic(iter, DBUS_TYPE_STRING, &session->path); return TRUE; } static gboolean transfer_get_transferred(const GDBusPropertyTable *property, DBusMessageIter *iter, void *data) { struct obex_transfer *transfer = data; struct obex_session *session = transfer->session; dbus_message_iter_append_basic(iter, DBUS_TYPE_UINT64, &session->offset); return TRUE; } static const GDBusMethodTable manager_methods[] = { { GDBUS_METHOD("RegisterAgent", GDBUS_ARGS({ "agent", "o" }), NULL, register_agent) }, { GDBUS_METHOD("UnregisterAgent", GDBUS_ARGS({ "agent", "o" }), NULL, unregister_agent) }, { } }; static const GDBusMethodTable transfer_methods[] = { { GDBUS_METHOD("Cancel", NULL, NULL, transfer_cancel) }, { } }; static const GDBusPropertyTable transfer_properties[] = { { "Status", "s", transfer_get_status }, { "Session", "o", transfer_get_session }, { "Name", "s", transfer_get_name, NULL, transfer_name_exists }, { "Type", "s", transfer_get_type, NULL, transfer_type_exists }, { "Size", "t", transfer_get_size, NULL, transfer_size_exists }, { "Time", "t", transfer_get_time, NULL, transfer_time_exists }, { "Filename", "s", transfer_get_filename, NULL, transfer_filename_exists }, { "Transferred", "t", transfer_get_transferred }, { } }; static const GDBusPropertyTable session_properties[] = { { "Source", "s", get_source }, { "Destination", "s", get_destination }, { "Target", "s", get_target, NULL, session_target_exists }, { "Root", "s", get_root }, { } }; gboolean manager_init(void) { DBusError err; DBG(""); dbus_error_init(&err); connection = obex_setup_dbus_connection(OBEXD_SERVICE, &err); if (connection == NULL) { if (dbus_error_is_set(&err) == TRUE) { fprintf(stderr, "%s\n", err.message); dbus_error_free(&err); } else fprintf(stderr, "Can't register with session bus\n"); return FALSE; } g_dbus_attach_object_manager(connection); return g_dbus_register_interface(connection, OBEX_BASE_PATH, OBEX_MANAGER_INTERFACE, manager_methods, NULL, NULL, NULL, NULL); } void manager_cleanup(void) { DBG(""); g_dbus_unregister_interface(connection, OBEX_BASE_PATH, OBEX_MANAGER_INTERFACE); /* FIXME: Release agent? */ agent_free(agent); g_dbus_detach_object_manager(connection); dbus_connection_unref(connection); } void manager_emit_transfer_property(struct obex_transfer *transfer, char *name) { if (transfer->path == NULL) return; if (strcasecmp("Size", name) == 0) g_dbus_emit_property_changed_full(connection, transfer->path, TRANSFER_INTERFACE, name, G_DBUS_PROPERTY_CHANGED_FLAG_FLUSH); else g_dbus_emit_property_changed(connection, transfer->path, TRANSFER_INTERFACE, name); } void manager_emit_transfer_started(struct obex_transfer *transfer) { transfer->status = TRANSFER_STATUS_ACTIVE; manager_emit_transfer_property(transfer, "Status"); } static void emit_transfer_completed(struct obex_transfer *transfer, gboolean success) { transfer->status = success ? TRANSFER_STATUS_COMPLETE : TRANSFER_STATUS_ERROR; manager_emit_transfer_property(transfer, "Status"); } static void transfer_free(struct obex_transfer *transfer) { g_free(transfer->path); g_free(transfer); } struct obex_transfer *manager_register_transfer(struct obex_session *os) { struct obex_transfer *transfer; static unsigned int id = 0; transfer = g_new0(struct obex_transfer, 1); transfer->path = g_strdup_printf("%s/session%u/transfer%u", SESSION_BASE_PATH, os->id, id++); transfer->session = os; if (!g_dbus_register_interface(connection, transfer->path, TRANSFER_INTERFACE, transfer_methods, NULL, transfer_properties, transfer, NULL)) { error("Cannot register Transfer interface."); transfer_free(transfer); return NULL; } return transfer; } void manager_unregister_transfer(struct obex_transfer *transfer) { struct obex_session *os; if (transfer == NULL) return; os = transfer->session; if (transfer->status == TRANSFER_STATUS_ACTIVE) emit_transfer_completed(transfer, os->offset == os->size); g_dbus_unregister_interface(connection, transfer->path, TRANSFER_INTERFACE); transfer_free(transfer); } static void agent_cancel(void) { DBusMessage *msg; if (agent == NULL) return; msg = dbus_message_new_method_call(agent->bus_name, agent->path, AGENT_INTERFACE, "Cancel"); g_dbus_send_message(connection, msg); } static void agent_reply(DBusPendingCall *call, void *user_data) { DBusMessage *reply = dbus_pending_call_steal_reply(call); const char *name; DBusError derr; gboolean *got_reply = user_data; *got_reply = TRUE; /* Received a reply after the agent exited */ if (!agent) return; agent->auth_pending = FALSE; dbus_error_init(&derr); if (dbus_set_error_from_message(&derr, reply)) { error("Agent replied with an error: %s, %s", derr.name, derr.message); if (dbus_error_has_name(&derr, DBUS_ERROR_NO_REPLY)) agent_cancel(); else if (dbus_error_has_name(&derr, OBEX_ERROR_REJECT)) agent->auth_reject = TRUE; dbus_error_free(&derr); dbus_message_unref(reply); return; } if (dbus_message_get_args(reply, NULL, DBUS_TYPE_STRING, &name, DBUS_TYPE_INVALID)) { /* Splits folder and name */ const char *slash = strrchr(name, '/'); DBG("Agent replied with %s", name); if (!slash) { agent->new_name = g_strdup(name); agent->new_folder = NULL; } else { if (strlen(slash) == 1) agent->new_name = NULL; else agent->new_name = g_strdup(slash + 1); agent->new_folder = g_strndup(name, slash - name); } } dbus_message_unref(reply); } static gboolean auth_error(GIOChannel *io, GIOCondition cond, void *user_data) { agent->auth_pending = FALSE; return FALSE; } int manager_request_authorization(struct obex_transfer *transfer, char **new_folder, char **new_name) { struct obex_session *os = transfer->session; DBusMessage *msg; DBusPendingCall *call; unsigned int watch; gboolean got_reply; if (!agent) return -1; if (agent->auth_pending) return -EPERM; if (!new_folder || !new_name) return -EINVAL; msg = dbus_message_new_method_call(agent->bus_name, agent->path, AGENT_INTERFACE, "AuthorizePush"); dbus_message_append_args(msg, DBUS_TYPE_OBJECT_PATH, &transfer->path, DBUS_TYPE_INVALID); if (!g_dbus_send_message_with_reply(connection, msg, &call, TIMEOUT)) { dbus_message_unref(msg); return -EPERM; } dbus_message_unref(msg); agent->auth_pending = TRUE; agent->auth_reject = FALSE; got_reply = FALSE; /* Catches errors before authorization response comes */ watch = g_io_add_watch_full(os->io, G_PRIORITY_DEFAULT, G_IO_HUP | G_IO_ERR | G_IO_NVAL, auth_error, NULL, NULL); dbus_pending_call_set_notify(call, agent_reply, &got_reply, NULL); /* Workaround: process events while agent doesn't reply */ while (agent && agent->auth_pending) g_main_context_iteration(NULL, TRUE); g_source_remove(watch); if (!got_reply) { dbus_pending_call_cancel(call); agent_cancel(); } dbus_pending_call_unref(call); if (!agent || agent->auth_reject) return -EPERM; *new_folder = agent->new_folder; *new_name = agent->new_name; agent->new_folder = NULL; agent->new_name = NULL; return 0; } static DBusMessage *session_get_capabilities(DBusConnection *connection, DBusMessage *message, void *user_data) { return not_supported(message); } static const GDBusMethodTable session_methods[] = { { GDBUS_ASYNC_METHOD("GetCapabilities", NULL, GDBUS_ARGS({ "capabilities", "s" }), session_get_capabilities) }, { } }; void manager_register_session(struct obex_session *os) { char *path; path = g_strdup_printf("%s/session%u", SESSION_BASE_PATH, os->id); if (!g_dbus_register_interface(connection, path, SESSION_INTERFACE, session_methods, NULL, session_properties, os, NULL)) error("Cannot register Session interface."); g_free(path); } void manager_unregister_session(struct obex_session *os) { char *path; path = g_strdup_printf("%s/session%u", SESSION_BASE_PATH, os->id); g_dbus_unregister_interface(connection, path, SESSION_INTERFACE); g_free(path); } void manager_emit_transfer_progress(struct obex_transfer *transfer) { manager_emit_transfer_property(transfer, "Transferred"); } void manager_emit_transfer_completed(struct obex_transfer *transfer) { struct obex_session *session; if (transfer == NULL) return; session = transfer->session; if (session == NULL || session->object == NULL) return; emit_transfer_completed(transfer, !session->aborted); } DBusConnection *manager_dbus_get_connection(void) { if (connection == NULL) return NULL; return dbus_connection_ref(connection); } bluez-5.82/obexd/src/PaxHeaders/server.h0000644000000000000000000000005014572354773015236 xustar0020 atime=1743516715 20 ctime=1743591283 bluez-5.82/obexd/src/server.h0000644000000000000000000000077414572354773014727 0ustar00rootroot/* SPDX-License-Identifier: GPL-2.0-or-later */ /* * * OBEX Server * * Copyright (C) 2007-2010 Nokia Corporation * Copyright (C) 2007-2010 Marcel Holtmann * * */ struct obex_server { const struct obex_transport_driver *transport; void *transport_data; GSList *drivers; }; int obex_server_init(void); void obex_server_exit(void); int obex_server_new_connection(struct obex_server *server, GIOChannel *io, uint16_t tx_mtu, uint16_t rx_mtu, gboolean stream); bluez-5.82/obexd/src/PaxHeaders/genbuiltin0000644000000000000000000000005014572354773015642 xustar0020 atime=1743515767 20 ctime=1743591288 bluez-5.82/obexd/src/genbuiltin0000755000000000000000000000036314572354773015330 0ustar00rootroot#!/bin/sh for i in $* do echo "extern const struct obex_plugin_desc __obex_builtin_$i;" done echo echo "static const struct obex_plugin_desc *__obex_builtin[] = {" for i in $* do echo " &__obex_builtin_$i," done echo " NULL" echo "};" bluez-5.82/obexd/src/PaxHeaders/log.c0000644000000000000000000000005014015011623014454 xustar0020 atime=1743516733 20 ctime=1743591283 bluez-5.82/obexd/src/log.c0000644000000000000000000000403314015011623014135 0ustar00rootroot// SPDX-License-Identifier: GPL-2.0-or-later /* * * OBEX Server * * Copyright (C) 2007-2010 Marcel Holtmann * * */ #ifdef HAVE_CONFIG_H #include #endif #define _GNU_SOURCE #include #include #include #include #include "log.h" void info(const char *format, ...) { va_list ap; va_start(ap, format); vsyslog(LOG_INFO, format, ap); va_end(ap); } void error(const char *format, ...) { va_list ap; va_start(ap, format); vsyslog(LOG_ERR, format, ap); va_end(ap); } void obex_debug(const char *format, ...) { va_list ap; va_start(ap, format); vsyslog(LOG_DEBUG, format, ap); va_end(ap); } extern struct obex_debug_desc __start___debug[]; extern struct obex_debug_desc __stop___debug[]; static char **enabled = NULL; static gboolean is_enabled(struct obex_debug_desc *desc) { int i; if (enabled == NULL) return 0; for (i = 0; enabled[i] != NULL; i++) { if (desc->name != NULL && g_pattern_match_simple(enabled[i], desc->name) == TRUE) return 1; if (desc->file != NULL && g_pattern_match_simple(enabled[i], desc->file) == TRUE) return 1; } return 0; } void __obex_log_enable_debug(void) { struct obex_debug_desc *desc; for (desc = __start___debug; desc < __stop___debug; desc++) desc->flags |= OBEX_DEBUG_FLAG_PRINT; } void __obex_log_init(const char *debug, int detach) { int option = LOG_NDELAY | LOG_PID; struct obex_debug_desc *desc; const char *name = NULL, *file = NULL; if (debug != NULL) enabled = g_strsplit_set(debug, ":, ", 0); for (desc = __start___debug; desc < __stop___debug; desc++) { if (file != NULL || name != NULL) { if (g_strcmp0(desc->file, file) == 0) { if (desc->name == NULL) desc->name = name; } else file = NULL; } if (is_enabled(desc)) desc->flags |= OBEX_DEBUG_FLAG_PRINT; } if (!detach) option |= LOG_PERROR; openlog("obexd", option, LOG_DAEMON); info("OBEX daemon %s", VERSION); } void __obex_log_cleanup(void) { closelog(); g_strfreev(enabled); } bluez-5.82/obexd/PaxHeaders/plugins0000644000000000000000000000005014773213570014364 xustar0020 atime=1743591291 20 ctime=1743591288 bluez-5.82/obexd/plugins/0000755000000000000000000000000014773213570014122 5ustar00rootrootbluez-5.82/obexd/plugins/PaxHeaders/messages-dummy.c0000644000000000000000000000005014766002272017542 xustar0020 atime=1743515578 20 ctime=1743591283 bluez-5.82/obexd/plugins/messages-dummy.c0000644000000000000000000003037214766002272017230 0ustar00rootroot// SPDX-License-Identifier: GPL-2.0-or-later /* * * OBEX Server * * Copyright (C) 2010-2011 Nokia Corporation * * */ #ifdef HAVE_CONFIG_H #include #endif #include #include #include #include #include #include #include #include #include "obexd/src/log.h" #include "messages.h" #define MSG_LIST_XML "mlisting.xml" static char *root_folder = NULL; struct session { char *cwd; char *cwd_absolute; void *request; }; struct folder_listing_data { struct session *session; const char *name; uint16_t max; uint16_t offset; messages_folder_listing_cb callback; void *user_data; }; struct message_listing_data { struct session *session; const char *name; uint16_t max; uint16_t offset; uint8_t subject_len; uint16_t size; char *path; FILE *fp; const struct messages_filter *filter; messages_get_messages_listing_cb callback; void *user_data; }; /* NOTE: Neither IrOBEX nor MAP specs says that folder listing needs to * be sorted (in IrOBEX examples it is not). However existing implementations * seem to follow the fig. 3-2 from MAP specification v1.0, and I've seen a * test suite requiring folder listing to be in that order. */ static int folder_names_cmp(gconstpointer a, gconstpointer b, gpointer user_data) { static const char *order[] = { "inbox", "outbox", "sent", "deleted", "draft", NULL }; struct session *session = user_data; int ia, ib; if (g_strcmp0(session->cwd, "telecom/msg") == 0) { for (ia = 0; order[ia]; ia++) { if (g_strcmp0(a, order[ia]) == 0) break; } for (ib = 0; order[ib]; ib++) { if (g_strcmp0(b, order[ib]) == 0) break; } if (ia != ib) return ia - ib; } return g_strcmp0(a, b); } static char *get_next_subdir(DIR *dp, char *path) { struct dirent *ep; char *abs, *name; for (;;) { if ((ep = readdir(dp)) == NULL) return NULL; if (strcmp(ep->d_name, ".") == 0 || strcmp(ep->d_name, "..") == 0) continue; abs = g_build_filename(path, ep->d_name, NULL); if (g_file_test(abs, G_FILE_TEST_IS_DIR)) { g_free(abs); break; } g_free(abs); } name = g_filename_to_utf8(ep->d_name, -1, NULL, NULL, NULL); if (name == NULL) { DBG("g_filename_to_utf8(): invalid filename"); return NULL; } return name; } static ssize_t get_subdirs(struct folder_listing_data *fld, GSList **list) { DIR *dp; char *path, *name; size_t n; path = g_build_filename(fld->session->cwd_absolute, fld->name, NULL); dp = opendir(path); if (dp == NULL) { int err = -errno; DBG("opendir(): %d, %s", -err, strerror(-err)); g_free(path); return err; } n = 0; while ((name = get_next_subdir(dp, path)) != NULL) { n++; if (fld->max > 0) *list = g_slist_prepend(*list, name); } closedir(dp); g_free(path); *list = g_slist_sort_with_data(*list, folder_names_cmp, fld->session); return n; } static void return_folder_listing(struct folder_listing_data *fld, GSList *list) { struct session *session = fld->session; GSList *cur; uint16_t num = 0; uint16_t offs = 0; for (cur = list; offs < fld->offset; offs++) { cur = cur->next; if (cur == NULL) break; } for (; cur != NULL && num < fld->max; cur = cur->next, num++) fld->callback(session, -EAGAIN, 0, cur->data, fld->user_data); fld->callback(session, 0, 0, NULL, fld->user_data); } static gboolean get_folder_listing(void *d) { struct folder_listing_data *fld = d; ssize_t n; GSList *list = NULL; n = get_subdirs(fld, &list); if (n < 0) { fld->callback(fld->session, n, 0, NULL, fld->user_data); return FALSE; } if (fld->max == 0) { fld->callback(fld->session, 0, n, NULL, fld->user_data); return FALSE; } return_folder_listing(fld, list); g_slist_free_full(list, g_free); return FALSE; } int messages_init(void) { char *tmp; if (root_folder) return 0; tmp = getenv("MAP_ROOT"); if (tmp) { root_folder = g_strdup(tmp); return 0; } tmp = getenv("HOME"); if (!tmp) return -ENOENT; root_folder = g_build_filename(tmp, "map-messages", NULL); return 0; } void messages_exit(void) { g_free(root_folder); root_folder = NULL; } int messages_connect(void **s) { struct session *session; session = g_new0(struct session, 1); session->cwd = g_strdup(""); session->cwd_absolute = g_strdup(root_folder); *s = session; return 0; } void messages_disconnect(void *s) { struct session *session = s; g_free(session->cwd); g_free(session->cwd_absolute); g_free(session); } int messages_set_notification_registration(void *session, void (*send_event)(void *session, const struct messages_event *event, void *user_data), void *user_data) { return -ENOSYS; } int messages_set_folder(void *s, const char *name, gboolean cdup) { struct session *session = s; char *newrel = NULL; char *newabs; char *tmp; if (name && (strchr(name, '/') || strcmp(name, "..") == 0)) return -EBADR; if (cdup) { if (session->cwd[0] == 0) return -ENOENT; newrel = g_path_get_dirname(session->cwd); /* We use empty string for indication of the root directory */ if (newrel[0] == '.' && newrel[1] == 0) newrel[0] = 0; } tmp = newrel; if (!cdup && (!name || name[0] == 0)) newrel = g_strdup(""); else newrel = g_build_filename(newrel ? newrel : session->cwd, name, NULL); g_free(tmp); newabs = g_build_filename(root_folder, newrel, NULL); if (!g_file_test(newabs, G_FILE_TEST_IS_DIR)) { g_free(newrel); g_free(newabs); return -ENOENT; } g_free(session->cwd); session->cwd = newrel; g_free(session->cwd_absolute); session->cwd_absolute = newabs; return 0; } int messages_get_folder_listing(void *s, const char *name, uint16_t max, uint16_t offset, messages_folder_listing_cb callback, void *user_data) { struct session *session = s; struct folder_listing_data *fld; fld = g_new0(struct folder_listing_data, 1); fld->session = session; fld->name = name; fld->max = max; fld->offset = offset; fld->callback = callback; fld->user_data = user_data; session->request = fld; g_idle_add_full(G_PRIORITY_DEFAULT_IDLE, get_folder_listing, fld, g_free); return 0; } static void max_msg_element(GMarkupParseContext *ctxt, const char *element, const char **names, const char **values, gpointer user_data, GError **gerr) { struct message_listing_data *mld = user_data; const char *key; int i; for (i = 0, key = names[i]; key; key = names[++i]) { if (g_strcmp0(names[i], "handle") == 0) { mld->size++; break; } } } static void msg_element(GMarkupParseContext *ctxt, const char *element, const char **names, const char **values, gpointer user_data, GError **gerr) { struct message_listing_data *mld = user_data; struct messages_message *entry = NULL; int i; entry = g_new0(struct messages_message, 1); if (mld->filter->parameter_mask == 0) { entry->mask = (entry->mask | PMASK_SUBJECT \ | PMASK_DATETIME | PMASK_RECIPIENT_ADDRESSING \ | PMASK_SENDER_ADDRESSING \ | PMASK_ATTACHMENT_SIZE | PMASK_TYPE \ | PMASK_RECEPTION_STATUS); } else entry->mask = mld->filter->parameter_mask; for (i = 0 ; names[i]; ++i) { if (g_strcmp0(names[i], "handle") == 0) { g_free(entry->handle); entry->handle = g_strdup(values[i]); mld->size++; continue; } if (g_strcmp0(names[i], "attachment_size") == 0) { g_free(entry->attachment_size); entry->attachment_size = g_strdup(values[i]); continue; } if (g_strcmp0(names[i], "datetime") == 0) { g_free(entry->datetime); entry->datetime = g_strdup(values[i]); continue; } if (g_strcmp0(names[i], "subject") == 0) { g_free(entry->subject); entry->subject = g_strdup(values[i]); continue; } if (g_strcmp0(names[i], "recipient_addressing") == 0) { g_free(entry->recipient_addressing); entry->recipient_addressing = g_strdup(values[i]); continue; } if (g_strcmp0(names[i], "sender_addressing") == 0) { g_free(entry->sender_addressing); entry->sender_addressing = g_strdup(values[i]); continue; } if (g_strcmp0(names[i], "type") == 0) { g_free(entry->type); entry->type = g_strdup(values[i]); continue; } if (g_strcmp0(names[i], "reception_status") == 0) { g_free(entry->reception_status); entry->reception_status = g_strdup(values[i]); } } if (mld->size > mld->offset) mld->callback(mld->session, -EAGAIN, mld->size, 0, entry, mld->user_data); g_free(entry->reception_status); g_free(entry->type); g_free(entry->sender_addressing); g_free(entry->recipient_addressing); g_free(entry->subject); g_free(entry->datetime); g_free(entry->attachment_size); g_free(entry->handle); g_free(entry); } static const GMarkupParser msg_parser = { msg_element, NULL, NULL, NULL, NULL }; static const GMarkupParser max_msg_parser = { max_msg_element, NULL, NULL, NULL, NULL }; static gboolean get_messages_listing(void *d) { struct message_listing_data *mld = d; /* 1024 is the maximum size of the line which is calculated to be more * sufficient*/ char buffer[1024]; GMarkupParseContext *ctxt; size_t len; while (fgets(buffer, 1024, mld->fp)) { len = strlen(buffer); if (mld->max == 0) { ctxt = g_markup_parse_context_new(&max_msg_parser, 0, mld, NULL); g_markup_parse_context_parse(ctxt, buffer, len, NULL); g_markup_parse_context_free(ctxt); } else { ctxt = g_markup_parse_context_new(&msg_parser, 0, mld, NULL); g_markup_parse_context_parse(ctxt, buffer, len, NULL); g_markup_parse_context_free(ctxt); } } if (mld->max == 0) { mld->callback(mld->session, 0, mld->size, 0, NULL, mld->user_data); goto done; } mld->callback(mld->session, 0, mld->size, 0, NULL, mld->user_data); done: fclose(mld->fp); return FALSE; } int messages_get_messages_listing(void *session, const char *name, uint16_t max, uint16_t offset, uint8_t subject_len, const struct messages_filter *filter, messages_get_messages_listing_cb callback, void *user_data) { struct message_listing_data *mld; struct session *s = session; char *path; mld = g_new0(struct message_listing_data, 1); mld->session = s; mld->name = name; mld->max = max; mld->offset = offset; mld->subject_len = subject_len; mld->callback = callback; mld->filter = filter; mld->user_data = user_data; path = g_build_filename(s->cwd_absolute, MSG_LIST_XML, NULL); mld->fp = fopen(path, "r"); if (mld->fp == NULL) { g_free(path); messages_set_folder(s, mld->name, 0); path = g_build_filename(s->cwd_absolute, MSG_LIST_XML, NULL); mld->fp = fopen(path, "r"); if (mld->fp == NULL) { int err = -errno; DBG("fopen(): %d, %s", -err, strerror(-err)); g_free(path); g_free(mld); return -EBADR; } } g_idle_add_full(G_PRIORITY_DEFAULT_IDLE, get_messages_listing, mld, g_free); g_free(path); return 0; } int messages_get_message(void *session, const char *handle, unsigned long flags, messages_get_message_cb callback, void *user_data) { struct session *s = session; FILE *fp; char *path; char *msg, *buffer; int file_size, err = 0; struct stat file_info; DBG(" "); path = g_build_filename(s->cwd_absolute, handle, NULL); fp = fopen(path, "r"); if (!fp) { DBG("fopen() failed"); err = -EBADR; goto file_open_err; } if (fstat(fileno(fp), &file_info) < 0) { DBG("Error getting file size"); err = -EBADR; goto mmap_err; } file_size = file_info.st_size; msg = mmap(0, file_size, PROT_READ, MAP_PRIVATE, fileno(fp), 0); if (msg == MAP_FAILED) { DBG("Error mapping file"); err = -EBADR; goto mmap_err; } buffer = strndup(msg, file_size); if (callback) callback(session, 0, 0, buffer, user_data); if (munmap(msg, file_size) == -1) { DBG("Error unmapping"); err = -EBADR; } free(buffer); mmap_err: fclose(fp); file_open_err: g_free(path); return err; } int messages_update_inbox(void *session, messages_status_cb callback, void *user_data) { return -ENOSYS; } int messages_set_read(void *session, const char *handle, uint8_t value, messages_status_cb callback, void *user_data) { return -ENOSYS; } int messages_set_delete(void *session, const char *handle, uint8_t value, messages_status_cb callback, void *user_data) { return -ENOSYS; } void messages_abort(void *s) { struct session *session = s; if (session->request) { g_idle_remove_by_data(session->request); session->request = NULL; } } bluez-5.82/obexd/plugins/PaxHeaders/bluetooth.c0000644000000000000000000000005014772767672016632 xustar0020 atime=1743515579 20 ctime=1743591283 bluez-5.82/obexd/plugins/bluetooth.c0000644000000000000000000002362514772767672016323 0ustar00rootroot// SPDX-License-Identifier: GPL-2.0-or-later /* * * OBEX Server * * Copyright (C) 2007-2010 Nokia Corporation * Copyright (C) 2007-2010 Marcel Holtmann * * */ #ifdef HAVE_CONFIG_H #include #endif #include #include #include #include #include #include #include #include "lib/bluetooth.h" #include "lib/uuid.h" #include "gdbus/gdbus.h" #include "btio/btio.h" #include "obexd/src/obexd.h" #include "obexd/src/plugin.h" #include "obexd/src/server.h" #include "obexd/src/obex.h" #include "obexd/src/transport.h" #include "obexd/src/service.h" #include "obexd/src/log.h" #define BT_RX_MTU 32767 #define BT_TX_MTU 32767 struct bluetooth_profile { struct obex_server *server; const struct obex_service_driver *driver; char *uuid; char *path; }; static GSList *profiles = NULL; static DBusConnection *connection = NULL; static DBusMessage *profile_release(DBusConnection *conn, DBusMessage *msg, void *data) { return g_dbus_create_reply(msg, DBUS_TYPE_INVALID); } static void connect_event(GIOChannel *io, GError *err, void *user_data) { int sk = g_io_channel_unix_get_fd(io); struct bluetooth_profile *profile = user_data; struct obex_server *server = profile->server; int type; uint16_t omtu = BT_TX_MTU; uint16_t imtu = BT_RX_MTU; gboolean stream = TRUE; socklen_t len = sizeof(int); if (err) goto drop; if (getsockopt(sk, SOL_SOCKET, SO_TYPE, &type, &len) < 0) goto done; if (type != SOCK_SEQPACKET) goto done; stream = FALSE; /* Read MTU if io is an L2CAP socket */ bt_io_get(io, NULL, BT_IO_OPT_OMTU, &omtu, BT_IO_OPT_IMTU, &imtu, BT_IO_OPT_INVALID); done: if (obex_server_new_connection(server, io, omtu, imtu, stream) < 0) g_io_channel_shutdown(io, TRUE, NULL); return; drop: error("%s", err->message); g_io_channel_shutdown(io, TRUE, NULL); return; } static DBusMessage *invalid_args(DBusMessage *msg) { return g_dbus_create_error(msg, "org.bluez.Error.InvalidArguments", "Invalid arguments in method call"); } static DBusMessage *profile_new_connection(DBusConnection *conn, DBusMessage *msg, void *data) { DBusMessageIter args; const char *device; int fd; GIOChannel *io; dbus_message_iter_init(msg, &args); if (dbus_message_iter_get_arg_type(&args) != DBUS_TYPE_OBJECT_PATH) return invalid_args(msg); dbus_message_iter_get_basic(&args, &device); dbus_message_iter_next(&args); if (dbus_message_iter_get_arg_type(&args) != DBUS_TYPE_UNIX_FD) return invalid_args(msg); dbus_message_iter_get_basic(&args, &fd); if (fd < 0) { error("bluetooth: NewConnection invalid fd"); return invalid_args(msg); } /* Read fd flags to make sure it can be used */ if (fcntl(fd, F_GETFD) < 0) { error("bluetooth: fcntl(%d, F_GETFD): %s (%d)", fd, strerror(errno), errno); close(fd); return invalid_args(msg); } io = g_io_channel_unix_new(fd); if (io == NULL) { close(fd); return invalid_args(msg); } DBG("device %s", device); connect_event(io, NULL, data); g_io_channel_unref(io); return g_dbus_create_reply(msg, DBUS_TYPE_INVALID); } static DBusMessage *profile_request_disconnection(DBusConnection *conn, DBusMessage *msg, void *data) { return g_dbus_create_reply(msg, DBUS_TYPE_INVALID); } static DBusMessage *profile_cancel(DBusConnection *conn, DBusMessage *msg, void *data) { return g_dbus_create_reply(msg, DBUS_TYPE_INVALID); } static const GDBusMethodTable profile_methods[] = { { GDBUS_METHOD("Release", NULL, NULL, profile_release) }, { GDBUS_METHOD("NewConnection", GDBUS_ARGS({ "device", "o" }, { "fd", "h" }, { "options", "a{sv}" }), NULL, profile_new_connection) }, { GDBUS_METHOD("RequestDisconnection", GDBUS_ARGS({ "device", "o" }), NULL, profile_request_disconnection) }, { GDBUS_METHOD("Cancel", NULL, NULL, profile_cancel) }, { } }; static void unregister_profile(struct bluetooth_profile *profile) { g_dbus_unregister_interface(connection, profile->path, "org.bluez.Profile1"); g_free(profile->path); profile->path = NULL; } static void register_profile_reply(DBusPendingCall *call, void *user_data) { struct bluetooth_profile *profile = user_data; DBusMessage *reply = dbus_pending_call_steal_reply(call); DBusError derr; dbus_error_init(&derr); if (!dbus_set_error_from_message(&derr, reply)) { DBG("Profile %s registered", profile->path); goto done; } unregister_profile(profile); error("bluetooth: RequestProfile error: %s, %s", derr.name, derr.message); dbus_error_free(&derr); done: dbus_message_unref(reply); } static void profile_free(void *data) { struct bluetooth_profile *profile = data; if (profile->path != NULL) unregister_profile(profile); g_free(profile->uuid); g_free(profile); } static int register_profile(struct bluetooth_profile *profile) { DBusMessage *msg; DBusMessageIter iter, opt; DBusPendingCall *call; dbus_bool_t auto_connect = FALSE; char *xml; int ret = 0; profile->path = g_strconcat("/org/bluez/obex/", profile->uuid, NULL); g_strdelimit(profile->path, "-", '_'); if (!g_dbus_register_interface(connection, profile->path, "org.bluez.Profile1", profile_methods, NULL, NULL, profile, NULL)) { error("D-Bus failed to register %s", profile->path); g_free(profile->path); profile->path = NULL; return -1; } msg = dbus_message_new_method_call("org.bluez", "/org/bluez", "org.bluez.ProfileManager1", "RegisterProfile"); dbus_message_iter_init_append(msg, &iter); dbus_message_iter_append_basic(&iter, DBUS_TYPE_OBJECT_PATH, &profile->path); dbus_message_iter_append_basic(&iter, DBUS_TYPE_STRING, &profile->uuid); dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY, DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING DBUS_TYPE_STRING_AS_STRING DBUS_TYPE_VARIANT_AS_STRING DBUS_DICT_ENTRY_END_CHAR_AS_STRING, &opt); g_dbus_dict_append_entry(&opt, "AutoConnect", DBUS_TYPE_BOOLEAN, &auto_connect); if (profile->driver->record) { if (profile->driver->port != 0) xml = g_markup_printf_escaped(profile->driver->record, profile->driver->channel, profile->driver->name, profile->driver->port); else xml = g_markup_printf_escaped(profile->driver->record, profile->driver->channel, profile->driver->name); g_dbus_dict_append_entry(&opt, "ServiceRecord", DBUS_TYPE_STRING, &xml); g_free(xml); } dbus_message_iter_close_container(&iter, &opt); if (!g_dbus_send_message_with_reply(connection, msg, &call, -1)) { ret = -1; unregister_profile(profile); goto failed; } dbus_pending_call_set_notify(call, register_profile_reply, profile, NULL); dbus_pending_call_unref(call); failed: dbus_message_unref(msg); return ret; } static const char *service2uuid(uint16_t service) { switch (service) { case OBEX_OPP: return OBEX_OPP_UUID; case OBEX_FTP: return OBEX_FTP_UUID; case OBEX_PBAP: return OBEX_PSE_UUID; case OBEX_IRMC: return OBEX_SYNC_UUID; case OBEX_PCSUITE: return "00005005-0000-1000-8000-0002ee000001"; case OBEX_SYNCEVOLUTION: return "00000002-0000-1000-8000-0002ee000002"; case OBEX_MAS: return OBEX_MAS_UUID; case OBEX_MNS: return OBEX_MNS_UUID; } return NULL; } static void name_acquired(DBusConnection *conn, void *user_data) { GSList *l; DBG("org.bluez appeared"); for (l = profiles; l; l = l->next) { struct bluetooth_profile *profile = l->data; if (profile->path != NULL) continue; if (register_profile(profile) < 0) { error("bluetooth: Failed to register profile %s", profile->path); g_free(profile->path); profile->path = NULL; } } } static void name_released(DBusConnection *conn, void *user_data) { GSList *l; DBG("org.bluez disappeared"); for (l = profiles; l; l = l->next) { struct bluetooth_profile *profile = l->data; if (profile->path == NULL) continue; unregister_profile(profile); } } static void *bluetooth_start(struct obex_server *server, int *err) { const GSList *l; for (l = server->drivers; l; l = l->next) { const struct obex_service_driver *driver = l->data; struct bluetooth_profile *profile; const char *uuid; uuid = service2uuid(driver->service); if (uuid == NULL) continue; profile = g_new0(struct bluetooth_profile, 1); profile->driver = driver; profile->server = server; profile->uuid = g_strdup(uuid); profiles = g_slist_prepend(profiles, profile); } return profiles; } static void bluetooth_stop(void *data) { g_slist_free_full(profiles, profile_free); profiles = NULL; } static int bluetooth_getpeername(GIOChannel *io, char **name) { GError *gerr = NULL; char address[18]; bt_io_get(io, &gerr, BT_IO_OPT_DEST, address, BT_IO_OPT_INVALID); if (gerr) { error("%s", gerr->message); g_error_free(gerr); return -EINVAL; } *name = g_strdup(address); return 0; } static int bluetooth_getsockname(GIOChannel *io, char **name) { GError *gerr = NULL; char address[18]; bt_io_get(io, &gerr, BT_IO_OPT_SOURCE, address, BT_IO_OPT_INVALID); if (gerr) { error("%s", gerr->message); g_error_free(gerr); return -EINVAL; } *name = g_strdup(address); return 0; } static const struct obex_transport_driver driver = { .name = "bluetooth", .start = bluetooth_start, .getpeername = bluetooth_getpeername, .getsockname = bluetooth_getsockname, .stop = bluetooth_stop }; static unsigned int listener_id = 0; static int bluetooth_init(void) { connection = g_dbus_setup_private(DBUS_BUS_SYSTEM, NULL, NULL); if (connection == NULL) return -EPERM; listener_id = g_dbus_add_service_watch(connection, "org.bluez", name_acquired, name_released, NULL, NULL); return obex_transport_driver_register(&driver); } static void bluetooth_exit(void) { g_dbus_remove_watch(connection, listener_id); g_slist_free_full(profiles, profile_free); if (connection) dbus_connection_unref(connection); obex_transport_driver_unregister(&driver); } OBEX_PLUGIN_DEFINE(bluetooth, bluetooth_init, bluetooth_exit) bluez-5.82/obexd/plugins/PaxHeaders/ftp.h0000644000000000000000000000005014015011623015363 xustar0020 atime=1743516717 20 ctime=1743591283 bluez-5.82/obexd/plugins/ftp.h0000644000000000000000000000105114015011623015041 0ustar00rootroot/* SPDX-License-Identifier: GPL-2.0-or-later */ /* * * OBEX Server * * Copyright (C) 2007-2010 Marcel Holtmann * * */ void *ftp_connect(struct obex_session *os, int *err); int ftp_get(struct obex_session *os, void *user_data); int ftp_chkput(struct obex_session *os, void *user_data); int ftp_put(struct obex_session *os, void *user_data); int ftp_setpath(struct obex_session *os, void *user_data); void ftp_disconnect(struct obex_session *os, void *user_data); int ftp_action(struct obex_session *os, void *user_data); bluez-5.82/obexd/plugins/PaxHeaders/phonebook-dummy.c0000644000000000000000000000005014015011623017702 xustar0020 atime=1743516724 20 ctime=1743591283 bluez-5.82/obexd/plugins/phonebook-dummy.c0000644000000000000000000002715114015011623017371 0ustar00rootroot// SPDX-License-Identifier: GPL-2.0-or-later /* * * OBEX Server * * Copyright (C) 2009-2010 Intel Corporation * Copyright (C) 2007-2010 Marcel Holtmann * * */ #ifdef HAVE_CONFIG_H #include #endif #define _GNU_SOURCE #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "obexd/src/log.h" #include "phonebook.h" typedef void (*vcard_func_t) (const char *file, VObject *vo, void *user_data); struct dummy_data { phonebook_cb cb; void *user_data; const struct apparam_field *apparams; char *folder; int fd; guint id; }; struct cache_query { phonebook_entry_cb entry_cb; phonebook_cache_ready_cb ready_cb; void *user_data; DIR *dp; }; static char *root_folder = NULL; static void dummy_free(void *user_data) { struct dummy_data *dummy = user_data; if (dummy->fd >= 0) close(dummy->fd); g_free(dummy->folder); g_free(dummy); } static void query_free(void *user_data) { struct cache_query *query = user_data; if (query->dp) closedir(query->dp); g_free(query); } int phonebook_init(void) { if (root_folder) return 0; /* FIXME: It should NOT be hard-coded */ root_folder = g_build_filename(getenv("HOME"), "phonebook", NULL); return 0; } void phonebook_exit(void) { g_free(root_folder); root_folder = NULL; } static int handle_cmp(gconstpointer a, gconstpointer b) { const char *f1 = a; const char *f2 = b; unsigned int i1, i2; if (sscanf(f1, "%u.vcf", &i1) != 1) return -1; if (sscanf(f2, "%u.vcf", &i2) != 1) return -1; return (i1 - i2); } static int foreach_vcard(DIR *dp, vcard_func_t func, uint16_t offset, uint16_t maxlistcount, void *user_data, uint16_t *count) { struct dirent *ep; GSList *sorted = NULL, *l; VObject *v; FILE *fp; int err, fd, folderfd; uint16_t n = 0; folderfd = dirfd(dp); if (folderfd < 0) { err = errno; error("dirfd(): %s(%d)", strerror(err), err); return -err; } /* * Sorting vcards by file name. versionsort is a GNU extension. * The simple sorting function implemented on handle_cmp address * vcards handle only(handle is always a number). This sort function * doesn't address filename started by "0". */ while ((ep = readdir(dp))) { char *filename; if (ep->d_name[0] == '.') continue; filename = g_filename_to_utf8(ep->d_name, -1, NULL, NULL, NULL); if (filename == NULL) { error("g_filename_to_utf8: invalid filename"); continue; } if (!g_str_has_suffix(filename, ".vcf")) { g_free(filename); continue; } sorted = g_slist_insert_sorted(sorted, filename, handle_cmp); } /* * Filtering only the requested vCards attributes. Offset * shall be based on the first entry of the phonebook. */ for (l = g_slist_nth(sorted, offset); l && n < maxlistcount; l = l->next) { const char *filename = l->data; fd = openat(folderfd, filename, O_RDONLY); if (fd < 0) { err = errno; error("openat(%s): %s(%d)", filename, strerror(err), err); continue; } fp = fdopen(fd, "r"); v = Parse_MIME_FromFile(fp); if (v != NULL) { func(filename, v, user_data); deleteVObject(v); n++; } close(fd); } g_slist_free_full(sorted, g_free); if (count) *count = n; return 0; } static void entry_concat(const char *filename, VObject *v, void *user_data) { GString *buffer = user_data; char tmp[1024]; int len; /* * VObject API uses len for IN and OUT * Written bytes is also returned in the len variable */ len = sizeof(tmp); memset(tmp, 0, len); writeMemVObject(tmp, &len, v); /* FIXME: only the requested fields must be added */ g_string_append_len(buffer, tmp, len); } static gboolean read_dir(void *user_data) { struct dummy_data *dummy = user_data; GString *buffer; DIR *dp; uint16_t count = 0, max, offset; buffer = g_string_new(""); dp = opendir(dummy->folder); if (dp == NULL) { int err = errno; DBG("opendir(): %s(%d)", strerror(err), err); goto done; } /* * For PullPhoneBook function, the decision of returning the size * or contacts is made in the PBAP core. When MaxListCount is ZERO, * PCE wants to know the size of a given folder, PSE shall ignore all * other applicattion parameters that may be present in the request. */ if (dummy->apparams->maxlistcount == 0) { max = 0xffff; offset = 0; } else { max = dummy->apparams->maxlistcount; offset = dummy->apparams->liststartoffset; } foreach_vcard(dp, entry_concat, offset, max, buffer, &count); closedir(dp); done: /* FIXME: Missing vCards fields filtering */ dummy->cb(buffer->str, buffer->len, count, 0, TRUE, dummy->user_data); g_string_free(buffer, TRUE); return FALSE; } static void entry_notify(const char *filename, VObject *v, void *user_data) { struct cache_query *query = user_data; VObject *property, *subproperty; GString *name; const char *tel; long unsigned int handle; property = isAPropertyOf(v, VCNameProp); if (!property) return; if (sscanf(filename, "%lu.vcf", &handle) != 1) return; if (handle > UINT32_MAX) return; /* LastName; FirstName; MiddleName; Prefix; Suffix */ name = g_string_new(""); subproperty = isAPropertyOf(property, VCFamilyNameProp); if (subproperty) { g_string_append(name, fakeCString(vObjectUStringZValue(subproperty))); } subproperty = isAPropertyOf(property, VCGivenNameProp); if (subproperty) g_string_append_printf(name, ";%s", fakeCString(vObjectUStringZValue(subproperty))); subproperty = isAPropertyOf(property, VCAdditionalNamesProp); if (subproperty) g_string_append_printf(name, ";%s", fakeCString(vObjectUStringZValue(subproperty))); subproperty = isAPropertyOf(property, VCNamePrefixesProp); if (subproperty) g_string_append_printf(name, ";%s", fakeCString(vObjectUStringZValue(subproperty))); subproperty = isAPropertyOf(property, VCNameSuffixesProp); if (subproperty) g_string_append_printf(name, ";%s", fakeCString(vObjectUStringZValue(subproperty))); property = isAPropertyOf(v, VCTelephoneProp); tel = property ? fakeCString(vObjectUStringZValue(property)) : NULL; query->entry_cb(filename, handle, name->str, NULL, tel, query->user_data); g_string_free(name, TRUE); } static gboolean create_cache(void *user_data) { struct cache_query *query = user_data; /* * MaxListCount and ListStartOffset shall not be used * when creating the cache. All entries shall be fetched. * PBAP core is responsible for consider these application * parameters before reply the entries. */ foreach_vcard(query->dp, entry_notify, 0, 0xffff, query, NULL); query->ready_cb(query->user_data); return FALSE; } static gboolean read_entry(void *user_data) { struct dummy_data *dummy = user_data; char buffer[1024]; ssize_t count; memset(buffer, 0, sizeof(buffer)); count = read(dummy->fd, buffer, sizeof(buffer)); if (count < 0) { int err = errno; error("read(): %s(%d)", strerror(err), err); count = 0; } /* FIXME: Missing vCards fields filtering */ dummy->cb(buffer, count, 1, 0, TRUE, dummy->user_data); return FALSE; } static gboolean is_dir(const char *dir) { struct stat st; if (stat(dir, &st) < 0) { int err = errno; error("stat(%s): %s (%d)", dir, strerror(err), err); return FALSE; } return S_ISDIR(st.st_mode); } char *phonebook_set_folder(const char *current_folder, const char *new_folder, uint8_t flags, int *err) { gboolean root, child; char *tmp1, *tmp2, *base, *absolute, *relative = NULL; int len, ret = 0; root = (g_strcmp0("/", current_folder) == 0); child = (new_folder && strlen(new_folder) != 0); switch (flags) { case 0x02: /* Go back to root */ if (!child) { relative = g_strdup("/"); goto done; } relative = g_build_filename(current_folder, new_folder, NULL); break; case 0x03: /* Go up 1 level */ if (root) { /* Already root */ ret = -EBADR; goto done; } /* * Removing one level of the current folder. Current folder * contains AT LEAST one level since it is not at root folder. * Use glib utility functions to handle invalid chars in the * folder path properly. */ tmp1 = g_path_get_basename(current_folder); tmp2 = g_strrstr(current_folder, tmp1); len = tmp2 - (current_folder + 1); g_free(tmp1); if (len == 0) base = g_strdup("/"); else base = g_strndup(current_folder, len); /* Return: one level only */ if (!child) { relative = base; goto done; } relative = g_build_filename(base, new_folder, NULL); g_free(base); break; default: ret = -EBADR; break; } done: if (!relative) { if (err) *err = ret; return NULL; } absolute = g_build_filename(root_folder, relative, NULL); if (!is_dir(absolute)) { g_free(relative); relative = NULL; ret = -ENOENT; } g_free(absolute); if (err) *err = ret; return relative; } void phonebook_req_finalize(void *request) { struct dummy_data *dummy = request; /* dummy_data will be cleaned when request will be finished via * g_source_remove */ if (dummy && dummy->id) g_source_remove(dummy->id); } void *phonebook_pull(const char *name, const struct apparam_field *params, phonebook_cb cb, void *user_data, int *err) { struct dummy_data *dummy; char *filename, *folder; /* * Main phonebook objects will be created dinamically based on the * folder content. All vcards inside the given folder will be appended * in the "virtual" main phonebook object. */ filename = g_build_filename(root_folder, name, NULL); if (!g_str_has_suffix(filename, ".vcf")) { g_free(filename); if (err) *err = -EBADR; return NULL; } folder = g_strndup(filename, strlen(filename) - 4); g_free(filename); if (!is_dir(folder)) { g_free(folder); if (err) *err = -ENOENT; return NULL; } dummy = g_new0(struct dummy_data, 1); dummy->cb = cb; dummy->user_data = user_data; dummy->apparams = params; dummy->folder = folder; dummy->fd = -1; if (err) *err = 0; return dummy; } int phonebook_pull_read(void *request) { struct dummy_data *dummy = request; if (!dummy) return -ENOENT; dummy->id = g_idle_add_full(G_PRIORITY_DEFAULT_IDLE, read_dir, dummy, dummy_free); return 0; } void *phonebook_get_entry(const char *folder, const char *id, const struct apparam_field *params, phonebook_cb cb, void *user_data, int *err) { struct dummy_data *dummy; char *filename; int fd; filename = g_build_filename(root_folder, folder, id, NULL); fd = open(filename, O_RDONLY); g_free(filename); if (fd < 0) { DBG("open(): %s(%d)", strerror(errno), errno); if (err) *err = -ENOENT; return NULL; } dummy = g_new0(struct dummy_data, 1); dummy->cb = cb; dummy->user_data = user_data; dummy->apparams = params; dummy->fd = fd; dummy->id = g_idle_add_full(G_PRIORITY_DEFAULT_IDLE, read_entry, dummy, dummy_free); if (err) *err = 0; return dummy; } void *phonebook_create_cache(const char *name, phonebook_entry_cb entry_cb, phonebook_cache_ready_cb ready_cb, void *user_data, int *err) { struct cache_query *query; char *foldername; DIR *dp; struct dummy_data *dummy; foldername = g_build_filename(root_folder, name, NULL); dp = opendir(foldername); g_free(foldername); if (dp == NULL) { DBG("opendir(): %s(%d)", strerror(errno), errno); if (err) *err = -ENOENT; return NULL; } query = g_new0(struct cache_query, 1); query->entry_cb = entry_cb; query->ready_cb = ready_cb; query->user_data = user_data; query->dp = dp; dummy = g_new0(struct dummy_data, 1); dummy->id = g_idle_add_full(G_PRIORITY_DEFAULT_IDLE, create_cache, query, query_free); if (err) *err = 0; return dummy; } bluez-5.82/obexd/plugins/PaxHeaders/filesystem.c0000644000000000000000000000005014643061455016770 xustar0020 atime=1743516712 20 ctime=1743591283 bluez-5.82/obexd/plugins/filesystem.c0000644000000000000000000003413414643061455016456 0ustar00rootroot// SPDX-License-Identifier: GPL-2.0-or-later /* * * OBEX Server * * Copyright (C) 2009-2010 Intel Corporation * Copyright (C) 2007-2010 Marcel Holtmann * * */ #ifdef HAVE_CONFIG_H #include #endif #define _GNU_SOURCE #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "obexd/src/obexd.h" #include "obexd/src/plugin.h" #include "obexd/src/log.h" #include "obexd/src/mimetype.h" #include "filesystem.h" #define EOL_CHARS "\n" #define FL_VERSION "" EOL_CHARS #define FL_TYPE "" EOL_CHARS #define FL_TYPE_PCSUITE " ]>" EOL_CHARS #define FL_BODY_BEGIN "" EOL_CHARS #define FL_BODY_END "" EOL_CHARS #define FL_PARENT_FOLDER_ELEMENT "" EOL_CHARS #define FL_FILE_ELEMENT "" EOL_CHARS #define FL_FOLDER_ELEMENT "" EOL_CHARS #define FL_FOLDER_ELEMENT_PCSUITE "" EOL_CHARS #define FTP_TARGET_SIZE 16 static const uint8_t FTP_TARGET[FTP_TARGET_SIZE] = { 0xF9, 0xEC, 0x7B, 0xC4, 0x95, 0x3C, 0x11, 0xD2, 0x98, 0x4E, 0x52, 0x54, 0x00, 0xDC, 0x9E, 0x09 }; #define PCSUITE_WHO_SIZE 8 static const uint8_t PCSUITE_WHO[PCSUITE_WHO_SIZE] = { 'P', 'C', ' ', 'S', 'u', 'i', 't', 'e' }; gboolean is_filename(const char *name) { if (strchr(name, '/')) return FALSE; if (strcmp(name, ".") == 0) return FALSE; if (strcmp(name, "..") == 0) return FALSE; return TRUE; } int verify_path(const char *path) { char *t; int ret = 0; if (obex_option_symlinks()) return 0; t = realpath(path, NULL); if (t == NULL) return -errno; if (!g_str_has_prefix(t, obex_option_root_folder())) ret = -EPERM; free(t); return ret; } static char *file_stat_line(char *filename, struct stat *fstat, struct stat *dstat, gboolean root, gboolean pcsuite) { char perm[51], atime[18], ctime[18], mtime[18]; char *escaped, *ret = NULL; struct tm a_gmtime, c_gmtime, m_gmtime; snprintf(perm, 50, "user-perm=\"%s%s%s\" group-perm=\"%s%s%s\" " "other-perm=\"%s%s%s\"", (fstat->st_mode & 0400 ? "R" : ""), (fstat->st_mode & 0200 ? "W" : ""), (dstat->st_mode & 0200 ? "D" : ""), (fstat->st_mode & 0040 ? "R" : ""), (fstat->st_mode & 0020 ? "W" : ""), (dstat->st_mode & 0020 ? "D" : ""), (fstat->st_mode & 0004 ? "R" : ""), (fstat->st_mode & 0002 ? "W" : ""), (dstat->st_mode & 0002 ? "D" : "")); if (!gmtime_r(&fstat->st_atime, &a_gmtime) || !gmtime_r(&fstat->st_ctime, &c_gmtime) || !gmtime_r(&fstat->st_mtime, &m_gmtime)) { error("gmtime_r() returned NULL"); return ret; } strftime(atime, 17, "%Y%m%dT%H%M%SZ", &a_gmtime); strftime(ctime, 17, "%Y%m%dT%H%M%SZ", &c_gmtime); strftime(mtime, 17, "%Y%m%dT%H%M%SZ", &m_gmtime); escaped = g_markup_escape_text(filename, -1); if (S_ISDIR(fstat->st_mode)) { if (pcsuite && root && g_str_equal(filename, "Data")) ret = g_strdup_printf(FL_FOLDER_ELEMENT_PCSUITE, escaped, perm, atime, mtime, ctime); else ret = g_strdup_printf(FL_FOLDER_ELEMENT, escaped, perm, atime, mtime, ctime); } else if (S_ISREG(fstat->st_mode)) ret = g_strdup_printf(FL_FILE_ELEMENT, escaped, (uint64_t) fstat->st_size, perm, atime, mtime, ctime); g_free(escaped); return ret; } static void *filesystem_open(const char *name, int oflag, mode_t mode, void *context, size_t *size, int *err) { struct stat stats; struct statvfs buf; int fd, ret; uint64_t avail; fd = open(name, oflag, mode); if (fd < 0) { if (err) *err = -errno; return NULL; } if (fstat(fd, &stats) < 0) { if (err) *err = -errno; goto failed; } ret = verify_path(name); if (ret < 0) { if (err) *err = ret; goto failed; } if (oflag == O_RDONLY) { if (size) *size = stats.st_size; goto done; } if (fstatvfs(fd, &buf) < 0) { if (err) *err = -errno; goto failed; } if (size == NULL) goto done; avail = (uint64_t) buf.f_bsize * buf.f_bavail; if (avail < *size) { if (err) *err = -ENOSPC; goto failed; } done: if (err) *err = 0; return GINT_TO_POINTER(fd); failed: close(fd); return NULL; } static int filesystem_close(void *object) { if (close(GPOINTER_TO_INT(object)) < 0) return -errno; return 0; } static ssize_t filesystem_read(void *object, void *buf, size_t count) { ssize_t ret; ret = read(GPOINTER_TO_INT(object), buf, count); if (ret < 0) return -errno; return ret; } static ssize_t filesystem_write(void *object, const void *buf, size_t count) { ssize_t ret; ret = write(GPOINTER_TO_INT(object), buf, count); if (ret < 0) return -errno; return ret; } static int filesystem_rename(const char *name, const char *destname) { int ret; ret = rename(name, destname); if (ret < 0) { error("rename(%s, %s): %s (%d)", name, destname, strerror(errno), errno); return -errno; } return ret; } static int sendfile_async(int out_fd, int in_fd, off_t *offset, size_t count) { int pid; /* Run sendfile on child process */ pid = fork(); switch (pid) { case 0: break; case -1: error("fork() %s (%d)", strerror(errno), errno); return -errno; default: DBG("child %d forked", pid); return pid; } /* At child */ if (sendfile(out_fd, in_fd, offset, count) < 0) error("sendfile(): %s (%d)", strerror(errno), errno); close(in_fd); close(out_fd); exit(errno); } static int filesystem_copy(const char *name, const char *destname) { void *in, *out; ssize_t ret; size_t size; struct stat st; int in_fd, out_fd, err; in = filesystem_open(name, O_RDONLY, 0, NULL, &size, &err); if (in == NULL) { error("open(%s): %s (%d)", name, strerror(-err), -err); return -err; } in_fd = GPOINTER_TO_INT(in); ret = fstat(in_fd, &st); if (ret < 0) { error("stat(%s): %s (%d)", name, strerror(errno), errno); return -errno; } out = filesystem_open(destname, O_WRONLY | O_CREAT | O_TRUNC, st.st_mode, NULL, &size, &err); if (out == NULL) { error("open(%s): %s (%d)", destname, strerror(-err), -err); filesystem_close(in); return -errno; } out_fd = GPOINTER_TO_INT(out); /* Check if sendfile is supported */ ret = sendfile(out_fd, in_fd, NULL, 0); if (ret < 0) { ret = -errno; error("sendfile: %s (%zd)", strerror(-ret), -ret); goto done; } ret = sendfile_async(out_fd, in_fd, NULL, st.st_size); if (ret < 0) goto done; return 0; done: filesystem_close(in); filesystem_close(out); return ret; } struct capability_object { int pid; int output; int err; gboolean aborted; GString *buffer; }; static void script_exited(GPid pid, int status, void *data) { struct capability_object *object = data; char buf[128]; object->pid = -1; DBG("pid: %d status: %d", pid, status); g_spawn_close_pid(pid); /* free the object if aborted */ if (object->aborted) { if (object->buffer != NULL) g_string_free(object->buffer, TRUE); g_free(object); return; } if (WEXITSTATUS(status) != EXIT_SUCCESS) { memset(buf, 0, sizeof(buf)); if (read(object->err, buf, sizeof(buf)) > 0) error("%s", buf); obex_object_set_io_flags(data, G_IO_ERR, -EPERM); } else obex_object_set_io_flags(data, G_IO_IN, 0); } static int capability_exec(const char **argv, int *output, int *err) { GError *gerr = NULL; int pid; GSpawnFlags flags = G_SPAWN_DO_NOT_REAP_CHILD | G_SPAWN_SEARCH_PATH; if (!g_spawn_async_with_pipes(NULL, (char **) argv, NULL, flags, NULL, NULL, &pid, NULL, output, err, &gerr)) { error("%s", gerr->message); g_error_free(gerr); return -EPERM; } DBG("executing %s pid %d", argv[0], pid); return pid; } static void *capability_open(const char *name, int oflag, mode_t mode, void *context, size_t *size, int *err) { struct capability_object *object = NULL; char *buf; const char *argv[2]; if (oflag != O_RDONLY) goto fail; object = g_new0(struct capability_object, 1); object->pid = -1; object->output = -1; object->err = -1; if (name[0] != '!') { GError *gerr = NULL; gboolean ret; ret = g_file_get_contents(name, &buf, NULL, &gerr); if (ret == FALSE) { error("%s", gerr->message); g_error_free(gerr); goto fail; } object->buffer = g_string_new(buf); g_free(buf); if (size) *size = object->buffer->len; goto done; } argv[0] = &name[1]; argv[1] = NULL; object->pid = capability_exec(argv, &object->output, &object->err); if (object->pid < 0) goto fail; /* Watch cannot be removed while the process is still running */ g_child_watch_add(object->pid, script_exited, object); done: if (err) *err = 0; return object; fail: if (err) *err = -EPERM; g_free(object); return NULL; } static GString *append_pcsuite_preamble(GString *object) { return g_string_append(object, FL_TYPE_PCSUITE); } static GString *append_folder_preamble(GString *object) { return g_string_append(object, FL_TYPE); } static GString *append_listing(GString *object, const char *name, gboolean pcsuite, size_t *size, int *err) { struct stat fstat, dstat; struct dirent *ep; DIR *dp; gboolean root; int ret; root = g_str_equal(name, obex_option_root_folder()); dp = opendir(name); if (dp == NULL) { if (err) *err = -ENOENT; goto failed; } if (!root) object = g_string_append(object, FL_PARENT_FOLDER_ELEMENT); ret = verify_path(name); if (ret < 0) { *err = ret; goto failed; } ret = stat(name, &dstat); if (ret < 0) { if (err) *err = -errno; goto failed; } while ((ep = readdir(dp))) { char *filename; char *fullname; char *line; if (ep->d_name[0] == '.') continue; filename = g_filename_to_utf8(ep->d_name, -1, NULL, NULL, NULL); if (filename == NULL) { error("g_filename_to_utf8: invalid filename"); continue; } fullname = g_build_filename(name, ep->d_name, NULL); ret = stat(fullname, &fstat); if (ret < 0) { DBG("stat: %s(%d)", strerror(errno), errno); g_free(filename); g_free(fullname); continue; } g_free(fullname); line = file_stat_line(filename, &fstat, &dstat, root, FALSE); if (line == NULL) { g_free(filename); continue; } g_free(filename); object = g_string_append(object, line); g_free(line); } closedir(dp); object = g_string_append(object, FL_BODY_END); if (size) *size = object->len; if (err) *err = 0; return object; failed: if (dp) closedir(dp); g_string_free(object, TRUE); return NULL; } static void *folder_open(const char *name, int oflag, mode_t mode, void *context, size_t *size, int *err) { GString *object; object = g_string_new(FL_VERSION); object = append_folder_preamble(object); object = g_string_append(object, FL_BODY_BEGIN); return append_listing(object, name, FALSE, size, err); } static void *pcsuite_open(const char *name, int oflag, mode_t mode, void *context, size_t *size, int *err) { GString *object; object = g_string_new(FL_VERSION); object = append_pcsuite_preamble(object); object = g_string_append(object, FL_BODY_BEGIN); return append_listing(object, name, TRUE, size, err); } static int string_free(void *object) { GString *string = object; g_string_free(string, TRUE); return 0; } ssize_t string_read(void *object, void *buf, size_t count) { GString *string = object; ssize_t len; if (string->len == 0) return 0; len = MIN(string->len, count); memcpy(buf, string->str, len); g_string_erase(string, 0, len); return len; } static ssize_t folder_read(void *object, void *buf, size_t count) { return string_read(object, buf, count); } static ssize_t capability_read(void *object, void *buf, size_t count) { struct capability_object *obj = object; if (obj->buffer) return string_read(obj->buffer, buf, count); if (obj->pid >= 0) return -EAGAIN; return read(obj->output, buf, count); } static int capability_close(void *object) { struct capability_object *obj = object; int err = 0; if (obj->pid < 0) goto done; DBG("kill: pid %d", obj->pid); err = kill(obj->pid, SIGTERM); if (err < 0) { err = -errno; error("kill: %s (%d)", strerror(-err), -err); goto done; } obj->aborted = TRUE; return 0; done: if (obj->buffer != NULL) g_string_free(obj->buffer, TRUE); g_free(obj); return err; } static const struct obex_mime_type_driver file = { .open = filesystem_open, .close = filesystem_close, .read = filesystem_read, .write = filesystem_write, .remove = remove, .move = filesystem_rename, .copy = filesystem_copy, }; static const struct obex_mime_type_driver capability = { .target = FTP_TARGET, .target_size = FTP_TARGET_SIZE, .mimetype = "x-obex/capability", .open = capability_open, .close = capability_close, .read = capability_read, }; static const struct obex_mime_type_driver folder = { .target = FTP_TARGET, .target_size = FTP_TARGET_SIZE, .mimetype = "x-obex/folder-listing", .open = folder_open, .close = string_free, .read = folder_read, }; static const struct obex_mime_type_driver pcsuite = { .target = FTP_TARGET, .target_size = FTP_TARGET_SIZE, .who = PCSUITE_WHO, .who_size = PCSUITE_WHO_SIZE, .mimetype = "x-obex/folder-listing", .open = pcsuite_open, .close = string_free, .read = folder_read, }; static int filesystem_init(void) { int err; err = obex_mime_type_driver_register(&folder); if (err < 0) return err; err = obex_mime_type_driver_register(&capability); if (err < 0) return err; err = obex_mime_type_driver_register(&pcsuite); if (err < 0) return err; return obex_mime_type_driver_register(&file); } static void filesystem_exit(void) { obex_mime_type_driver_unregister(&folder); obex_mime_type_driver_unregister(&capability); obex_mime_type_driver_unregister(&file); } OBEX_PLUGIN_DEFINE(filesystem, filesystem_init, filesystem_exit) bluez-5.82/obexd/plugins/PaxHeaders/mas.c0000644000000000000000000000005014772767672015405 xustar0020 atime=1743515579 20 ctime=1743591283 bluez-5.82/obexd/plugins/mas.c0000644000000000000000000005062614772767672015077 0ustar00rootroot// SPDX-License-Identifier: GPL-2.0-or-later /* * * OBEX Server * * Copyright (C) 2010-2011 Nokia Corporation * * */ #ifdef HAVE_CONFIG_H #include #endif #define _GNU_SOURCE #include #include #include #include #include #include #include "gobex/gobex.h" #include "gobex/gobex-apparam.h" #include "obexd/src/obexd.h" #include "obexd/src/plugin.h" #include "obexd/src/log.h" #include "obexd/src/obex.h" #include "obexd/src/service.h" #include "obexd/src/mimetype.h" #include "obexd/src/manager.h" #include "obexd/src/map_ap.h" #include "filesystem.h" #include "messages.h" #define READ_STATUS_REQ 0 #define DELETE_STATUS_REQ 1 #define XML_DECL "" /* Building blocks for x-obex/folder-listing */ #define FL_DTD "" #define FL_BODY_BEGIN "" #define FL_BODY_EMPTY "" #define FL_PARENT_FOLDER_ELEMENT "" #define FL_FOLDER_ELEMENT "" #define FL_BODY_END "" #define ML_BODY_BEGIN "" #define ML_BODY_END "" struct mas_session { struct mas_request *request; void *backend_data; gboolean finished; gboolean nth_call; GString *buffer; GObexApparam *inparams; GObexApparam *outparams; gboolean ap_sent; uint8_t notification_status; }; static const uint8_t MAS_TARGET[TARGET_SIZE] = { 0xbb, 0x58, 0x2b, 0x40, 0x42, 0x0c, 0x11, 0xdb, 0xb0, 0xde, 0x08, 0x00, 0x20, 0x0c, 0x9a, 0x66 }; static int get_params(struct obex_session *os, struct mas_session *mas) { const uint8_t *buffer; ssize_t size; size = obex_get_apparam(os, &buffer); if (size <= 0) return 0; mas->inparams = g_obex_apparam_decode(buffer, size); if (mas->inparams == NULL) { DBG("Error when parsing parameters!"); return -EBADR; } return 0; } static void reset_request(struct mas_session *mas) { if (mas->buffer) { g_string_free(mas->buffer, TRUE); mas->buffer = NULL; } if (mas->inparams) { g_obex_apparam_free(mas->inparams); mas->inparams = NULL; } if (mas->outparams) { g_obex_apparam_free(mas->outparams); mas->outparams = NULL; } mas->nth_call = FALSE; mas->finished = FALSE; mas->ap_sent = FALSE; } static void mas_clean(struct mas_session *mas) { reset_request(mas); g_free(mas); } static void *mas_connect(struct obex_session *os, int *err) { struct mas_session *mas; DBG(""); mas = g_new0(struct mas_session, 1); *err = messages_connect(&mas->backend_data); if (*err < 0) goto failed; manager_register_session(os); return mas; failed: g_free(mas); return NULL; } static void mas_disconnect(struct obex_session *os, void *user_data) { struct mas_session *mas = user_data; DBG(""); manager_unregister_session(os); messages_disconnect(mas->backend_data); mas_clean(mas); } static int mas_get(struct obex_session *os, void *user_data) { struct mas_session *mas = user_data; const char *type = obex_get_type(os); const char *name = obex_get_name(os); int ret; DBG("GET: name %s type %s mas %p", name, type, mas); if (type == NULL) return -EBADR; ret = get_params(os, mas); if (ret < 0) goto failed; ret = obex_get_stream_start(os, name); if (ret < 0) goto failed; return 0; failed: reset_request(mas); return ret; } static int mas_put(struct obex_session *os, void *user_data) { struct mas_session *mas = user_data; const char *type = obex_get_type(os); const char *name = obex_get_name(os); int ret; DBG("PUT: name %s type %s mas %p", name, type, mas); if (type == NULL) return -EBADR; ret = get_params(os, mas); if (ret < 0) goto failed; ret = obex_put_stream_start(os, name); if (ret < 0) goto failed; return 0; failed: reset_request(mas); return ret; } /* FIXME: Preserve whitespaces */ static void g_string_append_escaped_printf(GString *string, const char *format, ...) { va_list ap; char *escaped; va_start(ap, format); escaped = g_markup_vprintf_escaped(format, ap); g_string_append(string, escaped); g_free(escaped); va_end(ap); } static gchar *get_mse_timestamp(void) { struct timeval time_val; struct tm ltime; gchar *local_ts; char sign; if (gettimeofday(&time_val, NULL) < 0) return NULL; if (!localtime_r(&time_val.tv_sec, <ime)) return NULL; if (difftime(mktime(localtime(&time_val.tv_sec)), mktime(gmtime(&time_val.tv_sec))) < 0) sign = '+'; else sign = '-'; local_ts = g_strdup_printf("%04d%02d%02dT%02d%02d%02d%c%2ld%2ld", ltime.tm_year + 1900, ltime.tm_mon + 1, ltime.tm_mday, ltime.tm_hour, ltime.tm_min, ltime.tm_sec, sign, ltime.tm_gmtoff/3600, (ltime.tm_gmtoff%3600)/60); return local_ts; } static const char *yesorno(gboolean a) { if (a) return "yes"; return "no"; } static void get_messages_listing_cb(void *session, int err, uint16_t size, gboolean newmsg, const struct messages_message *entry, void *user_data) { struct mas_session *mas = user_data; uint16_t max = 1024; gchar *mse_time; if (err < 0 && err != -EAGAIN) { obex_object_set_io_flags(mas, G_IO_ERR, err); return; } if (mas->inparams) g_obex_apparam_get_uint16(mas->inparams, MAP_AP_MAXLISTCOUNT, &max); if (max == 0) { if (!entry) mas->finished = TRUE; goto proceed; } if (!mas->nth_call) { g_string_append(mas->buffer, ML_BODY_BEGIN); mas->nth_call = TRUE; } if (!entry) { g_string_append(mas->buffer, ML_BODY_END); mas->finished = TRUE; goto proceed; } g_string_append(mas->buffer, "buffer, " handle=\"%s\"", entry->handle); if (entry->mask & PMASK_SUBJECT) g_string_append_escaped_printf(mas->buffer, " subject=\"%s\"", entry->subject); if (entry->mask & PMASK_DATETIME) g_string_append_escaped_printf(mas->buffer, " datetime=\"%s\"", entry->datetime); if (entry->mask & PMASK_SENDER_NAME) g_string_append_escaped_printf(mas->buffer, " sender_name=\"%s\"", entry->sender_name); if (entry->mask & PMASK_SENDER_ADDRESSING) g_string_append_escaped_printf(mas->buffer, " sender_addressing=\"%s\"", entry->sender_addressing); if (entry->mask & PMASK_REPLYTO_ADDRESSING) g_string_append_escaped_printf(mas->buffer, " replyto_addressing=\"%s\"", entry->replyto_addressing); if (entry->mask & PMASK_RECIPIENT_NAME) g_string_append_escaped_printf(mas->buffer, " recipient_name=\"%s\"", entry->recipient_name); if (entry->mask & PMASK_RECIPIENT_ADDRESSING) g_string_append_escaped_printf(mas->buffer, " recipient_addressing=\"%s\"", entry->recipient_addressing); if (entry->mask & PMASK_TYPE) g_string_append_escaped_printf(mas->buffer, " type=\"%s\"", entry->type); if (entry->mask & PMASK_RECEPTION_STATUS) g_string_append_escaped_printf(mas->buffer, " reception_status=\"%s\"", entry->reception_status); if (entry->mask & PMASK_SIZE) g_string_append_escaped_printf(mas->buffer, " size=\"%s\"", entry->size); if (entry->mask & PMASK_ATTACHMENT_SIZE) g_string_append_escaped_printf(mas->buffer, " attachment_size=\"%s\"", entry->attachment_size); if (entry->mask & PMASK_TEXT) g_string_append_escaped_printf(mas->buffer, " text=\"%s\"", yesorno(entry->text)); if (entry->mask & PMASK_READ) g_string_append_escaped_printf(mas->buffer, " read=\"%s\"", yesorno(entry->read)); if (entry->mask & PMASK_SENT) g_string_append_escaped_printf(mas->buffer, " sent=\"%s\"", yesorno(entry->sent)); if (entry->mask & PMASK_PROTECTED) g_string_append_escaped_printf(mas->buffer, " protected=\"%s\"", yesorno(entry->protect)); if (entry->mask & PMASK_PRIORITY) g_string_append_escaped_printf(mas->buffer, " priority=\"%s\"", yesorno(entry->priority)); g_string_append(mas->buffer, "/>\n"); proceed: if (!entry) { mas->outparams = g_obex_apparam_set_uint16(mas->outparams, MAP_AP_MESSAGESLISTINGSIZE, size); mas->outparams = g_obex_apparam_set_uint8(mas->outparams, MAP_AP_NEWMESSAGE, newmsg ? 1 : 0); /* Response to report the local time of MSE */ mse_time = get_mse_timestamp(); if (mse_time) { g_obex_apparam_set_string(mas->outparams, MAP_AP_MSETIME, mse_time); g_free(mse_time); } } if (err != -EAGAIN) obex_object_set_io_flags(mas, G_IO_IN, 0); } static void get_message_cb(void *session, int err, gboolean fmore, const char *chunk, void *user_data) { struct mas_session *mas = user_data; DBG(""); if (err < 0 && err != -EAGAIN) { obex_object_set_io_flags(mas, G_IO_ERR, err); return; } if (!chunk) { mas->finished = TRUE; goto proceed; } g_string_append(mas->buffer, chunk); mas->finished = TRUE; proceed: if (err != -EAGAIN) obex_object_set_io_flags(mas, G_IO_IN, 0); } static void get_folder_listing_cb(void *session, int err, uint16_t size, const char *name, void *user_data) { struct mas_session *mas = user_data; uint16_t max = 1024; if (err < 0 && err != -EAGAIN) { obex_object_set_io_flags(mas, G_IO_ERR, err); return; } if (mas->inparams) g_obex_apparam_get_uint16(mas->inparams, MAP_AP_MAXLISTCOUNT, &max); if (max == 0) { if (err != -EAGAIN) mas->outparams = g_obex_apparam_set_uint16( mas->outparams, MAP_AP_FOLDERLISTINGSIZE, size); if (!name) mas->finished = TRUE; goto proceed; } if (!mas->nth_call) { g_string_append(mas->buffer, XML_DECL); g_string_append(mas->buffer, FL_DTD); if (!name) { g_string_append(mas->buffer, FL_BODY_EMPTY); mas->finished = TRUE; goto proceed; } g_string_append(mas->buffer, FL_BODY_BEGIN); mas->nth_call = TRUE; } if (!name) { g_string_append(mas->buffer, FL_BODY_END); mas->finished = TRUE; goto proceed; } if (g_strcmp0(name, "..") == 0) g_string_append(mas->buffer, FL_PARENT_FOLDER_ELEMENT); else g_string_append_escaped_printf(mas->buffer, FL_FOLDER_ELEMENT, name); proceed: if (err != -EAGAIN) obex_object_set_io_flags(mas, G_IO_IN, err); } static void set_status_cb(void *session, int err, void *user_data) { struct mas_session *mas = user_data; DBG(""); mas->finished = TRUE; if (err < 0) obex_object_set_io_flags(mas, G_IO_ERR, err); else obex_object_set_io_flags(mas, G_IO_OUT, 0); } static int mas_setpath(struct obex_session *os, void *user_data) { const char *name; const uint8_t *nonhdr; struct mas_session *mas = user_data; if (obex_get_non_header_data(os, &nonhdr) != 2) { error("Set path failed: flag and constants not found!"); return -EBADR; } name = obex_get_name(os); DBG("SETPATH: name %s nonhdr 0x%x%x", name, nonhdr[0], nonhdr[1]); if ((nonhdr[0] & 0x02) != 0x02) { DBG("Error: requested directory creation"); return -EBADR; } return messages_set_folder(mas->backend_data, name, nonhdr[0] & 0x01); } static void *folder_listing_open(const char *name, int oflag, mode_t mode, void *driver_data, size_t *size, int *err) { struct mas_session *mas = driver_data; /* 1024 is the default when there was no MaxListCount sent */ uint16_t max = 1024; uint16_t offset = 0; if (oflag != O_RDONLY) { *err = -EBADR; return NULL; } DBG("name = %s", name); if (mas->inparams) { g_obex_apparam_get_uint16(mas->inparams, MAP_AP_MAXLISTCOUNT, &max); g_obex_apparam_get_uint16(mas->inparams, MAP_AP_STARTOFFSET, &offset); } *err = messages_get_folder_listing(mas->backend_data, name, max, offset, get_folder_listing_cb, mas); mas->buffer = g_string_new(""); if (*err < 0) return NULL; else return mas; } static void *msg_listing_open(const char *name, int oflag, mode_t mode, void *driver_data, size_t *size, int *err) { struct mas_session *mas = driver_data; struct messages_filter filter = { 0, }; /* 1024 is the default when there was no MaxListCount sent */ uint16_t max = 1024; uint16_t offset = 0; /* If MAP client does not specify the subject length, then subject_len = 0 and subject should be sent unaltered. */ uint8_t subject_len = 0; DBG(""); if (oflag != O_RDONLY) { *err = -EBADR; return NULL; } if (!mas->inparams) goto done; g_obex_apparam_get_uint16(mas->inparams, MAP_AP_MAXLISTCOUNT, &max); g_obex_apparam_get_uint16(mas->inparams, MAP_AP_STARTOFFSET, &offset); g_obex_apparam_get_uint8(mas->inparams, MAP_AP_SUBJECTLENGTH, &subject_len); g_obex_apparam_get_uint32(mas->inparams, MAP_AP_PARAMETERMASK, &filter.parameter_mask); g_obex_apparam_get_uint8(mas->inparams, MAP_AP_FILTERMESSAGETYPE, &filter.type); filter.period_begin = g_obex_apparam_get_string(mas->inparams, MAP_AP_FILTERPERIODBEGIN); filter.period_end = g_obex_apparam_get_string(mas->inparams, MAP_AP_FILTERPERIODEND); g_obex_apparam_get_uint8(mas->inparams, MAP_AP_FILTERREADSTATUS, &filter.read_status); filter.recipient = g_obex_apparam_get_string(mas->inparams, MAP_AP_FILTERRECIPIENT); filter.originator = g_obex_apparam_get_string(mas->inparams, MAP_AP_FILTERORIGINATOR); g_obex_apparam_get_uint8(mas->inparams, MAP_AP_FILTERPRIORITY, &filter.priority); done: *err = messages_get_messages_listing(mas->backend_data, name, max, offset, subject_len, &filter, get_messages_listing_cb, mas); mas->buffer = g_string_new(""); if (*err < 0) return NULL; else return mas; } static void *message_open(const char *name, int oflag, mode_t mode, void *driver_data, size_t *size, int *err) { struct mas_session *mas = driver_data; DBG(""); if (oflag != O_RDONLY) { DBG("Message pushing unsupported"); *err = -ENOSYS; return NULL; } mas->buffer = g_string_new(""); *err = messages_get_message(mas->backend_data, name, 0, get_message_cb, mas); if (*err < 0) return NULL; else return mas; } static void *message_update_open(const char *name, int oflag, mode_t mode, void *driver_data, size_t *size, int *err) { struct mas_session *mas = driver_data; DBG(""); if (oflag == O_RDONLY) { *err = -EBADR; return NULL; } *err = messages_update_inbox(mas->backend_data, set_status_cb, mas); if (*err < 0) return NULL; else return mas; } static void *message_set_status_open(const char *name, int oflag, mode_t mode, void *driver_data, size_t *size, int *err) { struct mas_session *mas = driver_data; uint8_t indicator; uint8_t value; DBG(""); if (oflag == O_RDONLY) { *err = -EBADR; return NULL; } if (!g_obex_apparam_get_uint8(mas->inparams, MAP_AP_STATUSINDICATOR, &indicator)) { *err = -EBADR; return NULL; } if (!g_obex_apparam_get_uint8(mas->inparams, MAP_AP_STATUSVALUE, &value)) { *err = -EBADR; return NULL; } if (indicator == READ_STATUS_REQ) *err = messages_set_read(mas->backend_data, name, value, set_status_cb, mas); else if (indicator == DELETE_STATUS_REQ) *err = messages_set_delete(mas->backend_data, name, value, set_status_cb, mas); else *err = -EBADR; if (*err < 0) return NULL; return mas; } static ssize_t any_get_next_header(void *object, void *buf, size_t mtu, uint8_t *hi) { struct mas_session *mas = object; DBG(""); if (mas->buffer->len == 0 && !mas->finished) return -EAGAIN; *hi = G_OBEX_HDR_APPARAM; if (mas->ap_sent) return 0; mas->ap_sent = TRUE; if (!mas->outparams) return 0; return g_obex_apparam_encode(mas->outparams, buf, mtu); } static void *any_open(const char *name, int oflag, mode_t mode, void *driver_data, size_t *size, int *err) { DBG(""); *err = -ENOSYS; return NULL; } static ssize_t any_write(void *object, const void *buf, size_t count) { DBG(""); return count; } static ssize_t any_read(void *obj, void *buf, size_t count) { struct mas_session *mas = obj; ssize_t len; DBG(""); len = string_read(mas->buffer, buf, count); if (len == 0 && !mas->finished) return -EAGAIN; return len; } static int any_close(void *obj) { struct mas_session *mas = obj; DBG(""); if (!mas->finished) messages_abort(mas->backend_data); reset_request(mas); return 0; } static void *notification_registration_open(const char *name, int oflag, mode_t mode, void *driver_data, size_t *size, int *err) { struct mas_session *mas = driver_data; uint8_t status; DBG(""); if (O_RDONLY) { *err = -EBADR; return NULL; } if (!g_obex_apparam_get_uint8(mas->inparams, MAP_AP_NOTIFICATIONSTATUS, &status)) { *err = -EBADR; return NULL; } mas->notification_status = status; mas->finished = TRUE; *err = 0; return mas; } static void *message_get_instance_open(const char *name, int oflag, mode_t mode, void *driver_data, size_t *size, int *err) { struct mas_session *mas = driver_data; DBG(""); mas->buffer = g_string_new("Mas Instance 0"); mas->finished = TRUE; *err = 0; return mas; } static void *message_notification_filter_open(const char *name, int oflag, mode_t mode, void *driver_data, size_t *size, int *err) { struct mas_session *mas = driver_data; DBG(""); /* TODO notifcation filter add */ mas->finished = TRUE; *err = 0; return mas; } static const struct obex_service_driver mas = { .name = "Message Access server", .service = OBEX_MAS, .target = MAS_TARGET, .target_size = TARGET_SIZE, .connect = mas_connect, .get = mas_get, .put = mas_put, .setpath = mas_setpath, .disconnect = mas_disconnect, }; static const struct obex_mime_type_driver mime_map = { .target = MAS_TARGET, .target_size = TARGET_SIZE, .mimetype = NULL, .open = any_open, .close = any_close, .read = any_read, .write = any_write, }; static const struct obex_mime_type_driver mime_message = { .target = MAS_TARGET, .target_size = TARGET_SIZE, .mimetype = "x-bt/message", .open = message_open, .close = any_close, .read = any_read, .write = any_write, }; static const struct obex_mime_type_driver mime_folder_listing = { .target = MAS_TARGET, .target_size = TARGET_SIZE, .mimetype = "x-obex/folder-listing", .get_next_header = any_get_next_header, .open = folder_listing_open, .close = any_close, .read = any_read, .write = any_write, }; static const struct obex_mime_type_driver mime_msg_listing = { .target = MAS_TARGET, .target_size = TARGET_SIZE, .mimetype = "x-bt/MAP-msg-listing", .get_next_header = any_get_next_header, .open = msg_listing_open, .close = any_close, .read = any_read, .write = any_write, }; static const struct obex_mime_type_driver mime_notification_registration = { .target = MAS_TARGET, .target_size = TARGET_SIZE, .mimetype = "x-bt/MAP-NotificationRegistration", .open = notification_registration_open, .close = any_close, .read = any_read, .write = any_write, }; static const struct obex_mime_type_driver mime_message_status = { .target = MAS_TARGET, .target_size = TARGET_SIZE, .mimetype = "x-bt/messageStatus", .open = message_set_status_open, .close = any_close, .read = any_read, .write = any_write, }; static const struct obex_mime_type_driver mime_message_update = { .target = MAS_TARGET, .target_size = TARGET_SIZE, .mimetype = "x-bt/MAP-messageUpdate", .open = message_update_open, .close = any_close, .read = any_read, .write = any_write, }; static struct obex_mime_type_driver mime_message_instance = { .target = MAS_TARGET, .target_size = TARGET_SIZE, .mimetype = "x-bt/MASInstanceInformation", .open = message_get_instance_open, .close = any_close, .read = any_read, .write = any_write, }; static struct obex_mime_type_driver mime_message_notification_filter = { .target = MAS_TARGET, .target_size = TARGET_SIZE, .mimetype = "x-bt/MAP-notification-filter", .open = message_notification_filter_open, .close = any_close, .read = any_read, .write = any_write, }; static const struct obex_mime_type_driver *map_drivers[] = { &mime_map, &mime_message, &mime_folder_listing, &mime_msg_listing, &mime_notification_registration, &mime_message_status, &mime_message_update, &mime_message_instance, &mime_message_notification_filter, NULL }; static int mas_init(void) { int err; int i; err = messages_init(); if (err < 0) return err; for (i = 0; map_drivers[i] != NULL; ++i) { err = obex_mime_type_driver_register(map_drivers[i]); if (err < 0) goto failed; } err = obex_service_driver_register(&mas); if (err < 0) goto failed; return 0; failed: for (--i; i >= 0; --i) obex_mime_type_driver_unregister(map_drivers[i]); messages_exit(); return err; } static void mas_exit(void) { int i; obex_service_driver_unregister(&mas); for (i = 0; map_drivers[i] != NULL; ++i) obex_mime_type_driver_unregister(map_drivers[i]); messages_exit(); } OBEX_PLUGIN_DEFINE(mas, mas_init, mas_exit) bluez-5.82/obexd/plugins/PaxHeaders/opp.c0000644000000000000000000000005014711225434015375 xustar0020 atime=1743516715 20 ctime=1743591283 bluez-5.82/obexd/plugins/opp.c0000644000000000000000000000671214711225434015064 0ustar00rootroot// SPDX-License-Identifier: GPL-2.0-or-later /* * * OBEX Server * * Copyright (C) 2007-2010 Nokia Corporation * Copyright (C) 2007-2010 Marcel Holtmann * * */ #ifdef HAVE_CONFIG_H #include #endif #include #include #include #include #include #include "obexd/src/obexd.h" #include "obexd/src/plugin.h" #include "obexd/src/obex.h" #include "obexd/src/service.h" #include "obexd/src/log.h" #include "obexd/src/manager.h" #include "filesystem.h" #define VCARD_TYPE "text/x-vcard" static void *opp_connect(struct obex_session *os, int *err) { manager_register_session(os); if (err) *err = 0; return manager_register_transfer(os); } static void opp_progress(struct obex_session *os, void *user_data) { manager_emit_transfer_progress(user_data); } static int opp_chkput(struct obex_session *os, void *user_data) { char *folder, *name, *path; const char *t; int err; if (obex_get_size(os) == OBJECT_SIZE_DELETE) return -ENOSYS; t = obex_get_name(os); if (t != NULL && !is_filename(t)) return -EBADR; if (obex_option_auto_accept()) { folder = g_strdup(obex_option_root_folder()); name = g_strdup(obex_get_name(os)); goto skip_auth; } err = manager_request_authorization(user_data, &folder, &name); if (err < 0) return -EPERM; if (folder == NULL) folder = g_strdup(obex_option_root_folder()); if (name == NULL) name = g_strdup(obex_get_name(os)); skip_auth: if (name == NULL || strlen(name) == 0) { err = -EBADR; goto failed; } if (g_strcmp0(name, obex_get_name(os)) != 0) obex_set_name(os, name); path = g_build_filename(folder, name, NULL); err = obex_put_stream_start(os, path); if (err == 0 && obex_get_size(os) != OBJECT_SIZE_DELETE && obex_get_size(os) != OBJECT_SIZE_UNKNOWN) { manager_emit_transfer_property(user_data, "Size"); } g_free(path); if (err < 0) goto failed; manager_emit_transfer_started(user_data); failed: g_free(folder); g_free(name); return err; } static int opp_put(struct obex_session *os, void *user_data) { const char *name = obex_get_name(os); const char *folder = obex_option_root_folder(); if (folder == NULL) return -EPERM; if (name == NULL) return -EBADR; return 0; } static int opp_get(struct obex_session *os, void *user_data) { const char *type; char *folder, *path; int err = 0; if (obex_get_name(os)) return -EPERM; type = obex_get_type(os); if (type == NULL) return -EPERM; folder = g_strdup(obex_option_root_folder()); path = g_build_filename(folder, "/vcard.vcf", NULL); if (g_ascii_strcasecmp(type, VCARD_TYPE) == 0) { if (obex_get_stream_start(os, path) < 0) err = -ENOENT; } else err = -EPERM; g_free(folder); g_free(path); return err; } static void opp_disconnect(struct obex_session *os, void *user_data) { manager_unregister_transfer(user_data); manager_unregister_session(os); } static void opp_reset(struct obex_session *os, void *user_data) { manager_emit_transfer_completed(user_data); } static const struct obex_service_driver driver = { .name = "Object Push server", .service = OBEX_OPP, .connect = opp_connect, .progress = opp_progress, .disconnect = opp_disconnect, .get = opp_get, .put = opp_put, .chkput = opp_chkput, .reset = opp_reset }; static int opp_init(void) { return obex_service_driver_register(&driver); } static void opp_exit(void) { obex_service_driver_unregister(&driver); } OBEX_PLUGIN_DEFINE(opp, opp_init, opp_exit) bluez-5.82/obexd/plugins/PaxHeaders/messages.h0000644000000000000000000000005014015011623016401 xustar0020 atime=1743516726 20 ctime=1743591283 bluez-5.82/obexd/plugins/messages.h0000644000000000000000000002250414015011623016065 0ustar00rootroot/* SPDX-License-Identifier: GPL-2.0-or-later */ /* * * OBEX Server * * Copyright (C) 2010-2011 Nokia Corporation * * */ #include #include /* Those are used by backend to notify transport plugin which properties did it * send. */ #define PMASK_SUBJECT 0x0001 #define PMASK_DATETIME 0x0002 #define PMASK_SENDER_NAME 0x0004 #define PMASK_SENDER_ADDRESSING 0x0008 #define PMASK_RECIPIENT_NAME 0x0010 #define PMASK_RECIPIENT_ADDRESSING 0x0020 #define PMASK_TYPE 0x0040 #define PMASK_SIZE 0x0080 #define PMASK_RECEPTION_STATUS 0x0100 #define PMASK_TEXT 0x0200 #define PMASK_ATTACHMENT_SIZE 0x0400 #define PMASK_PRIORITY 0x0800 #define PMASK_READ 0x1000 #define PMASK_SENT 0x2000 #define PMASK_PROTECTED 0x4000 #define PMASK_REPLYTO_ADDRESSING 0x8000 /* This one is used in a response to GetMessagesListing. Use PMASK_* values to * notify the plugin which members are actually set. Backend shall not omit * properties required by MAP specification (subject, datetime, * recipient_addressing, type, size, reception_status, attachment_size) unless * ordered by PARAMETERMASK. Boolean values should be probably * always sent (need checking). Handle is mandatory. Plugin will filter out any * properties that were not wanted by MCE. * * Handle shall be set to hexadecimal representation with upper-case letters. No * prefix shall be appended and without no zeros. This corresponds to PTS * behaviour described in comments to the MAP specification. * * The rest of char * fields shall be set according to the MAP specification * rules. */ struct messages_message { uint32_t mask; char *handle; char *subject; char *datetime; char *sender_name; char *sender_addressing; char *replyto_addressing; char *recipient_name; char *recipient_addressing; char *type; char *reception_status; char *size; char *attachment_size; gboolean text; gboolean read; gboolean sent; gboolean protect; gboolean priority; }; /* Type of message event to be delivered to MNS server */ enum messages_event_type { MET_NEW_MESSAGE, MET_DELIVERY_SUCCESS, MET_SENDING_SUCCESS, MET_DELIVERY_FAILURE, MET_SENDING_FAILURE, MET_MEMORY_FULL, MET_MEMORY_AVAILABLE, MET_MESSAGE_DELETED, MET_MESSAGE_SHIFT }; /* Data for sending MNS notification. Handle shall be formatted as described in * messages_message. */ struct messages_event { enum messages_event_type type; uint8_t instance_id; char *handle; char *folder; char *old_folder; char *msg_type; }; /* parameter_mask: |-ed PMASK_* values * See MAP specification for the rest. */ struct messages_filter { uint32_t parameter_mask; uint8_t type; const char *period_begin; const char *period_end; uint8_t read_status; const char *recipient; const char *originator; uint8_t priority; }; /* This is called once after server starts. * * Returns value less than zero if error. This will prevent MAP plugin from * starting. */ int messages_init(void); /* This gets called right before server finishes */ void messages_exit(void); /* Starts a new MAP session. * * session: variable to store pointer to backend session data. This one shall be * passed to all in-session calls. * * If session start succeeded, backend shall return 0. Otherwise the error value * will be sent as a response to OBEX connect. */ int messages_connect(void **session); /* Closes a MAP session. * * This call should free buffer reserved by messages_connect. */ void messages_disconnect(void *session); /****************************************************************************** * NOTE on callbacks. * * All functions requiring callbacks have to call them asynchronously. * 'user_data' is for passing arbitrary user data. * * Functions for GetMessagesListing, GetFolder listing and GetMessage call their * callbacks multiple times - one for each listing entry or message body chunk. * To indicate the end of operation backend must call callback with the data * pointer parameter set to NULL. * * If err == -EAGAIN the transport * plugin does not wake IO. * * Keep in mind that application parameters has to be send first. Therefore the * first time err == 0 and thus sending is started, callback will use provided * parameters (e.g. size in case of folder listing) to build applications * parameters header used in response. In any other case those parameters will * be ignored. * * If err != 0 && err != -EAGAIN, the operation is finished immediately and err * value is used to set the error code in OBEX response. ******************************************************************************/ /* Registers for messaging events notifications. * * session: Backend session. * send_event: Function that will be called to indicate a new event. * * To unregister currently registered notifications, call this with send_event * set to NULL. */ int messages_set_notification_registration(void *session, void (*send_event)(void *session, const struct messages_event *event, void *user_data), void *user_data); /* Changes current directory. * * session: Backend session. * name: Subdirectory to go to. If empty or null and cdup is false, go to the * root directory. * cdup: If true, go up one level first. */ int messages_set_folder(void *session, const char *name, gboolean cdup); /* Retrieves subdirectories listing from a current directory. * * session: Backend session. * name: Optional subdirectory name (not strictly required by MAP). * max: Maximum number of entries to retrieve. * offset: Offset of the first entry. * size: Total size of listing to be returned. * * Callback shall be called for every entry of the listing. 'name' is the * subdirectory name. */ typedef void (*messages_folder_listing_cb)(void *session, int err, uint16_t size, const char *name, void *user_data); int messages_get_folder_listing(void *session, const char *name, uint16_t max, uint16_t offset, messages_folder_listing_cb callback, void *user_data); /* Retrieves messages listing from a current directory. * * session: Backend session. * name: Optional subdirectory name. * max: Maximum number of entries to retrieve. * offset: Offset of the first entry. * subject_len: Maximum string length of the "subject" parameter in the entries. * filter: Filter to apply on returned message listing. * size: Total size of listing to be returned. * newmsg: Indicates presence of unread messages. * * Callback shall be called for every entry of the listing, giving message data * in 'message'. */ typedef void (*messages_get_messages_listing_cb)(void *session, int err, uint16_t size, gboolean newmsg, const struct messages_message *message, void *user_data); int messages_get_messages_listing(void *session, const char *name, uint16_t max, uint16_t offset, uint8_t subject_len, const struct messages_filter *filter, messages_get_messages_listing_cb callback, void *user_data); #define MESSAGES_ATTACHMENT (1 << 0) #define MESSAGES_UTF8 (1 << 1) #define MESSAGES_FRACTION (1 << 2) #define MESSAGES_NEXT (1 << 3) /* Retrieves bMessage object (see MAP specification, ch. 3.1.3) of a given * message. * * session: Backend session. * handle: Handle of the message to retrieve. * flags: or-ed mask of following: * MESSAGES_ATTACHMENT: Selects whether or not attachments (if any) are to * be included. * MESSAGES_UTF8: If true, convert message to utf-8. Otherwise use native * encoding. * MESSAGES_FRACTION: If true, deliver fractioned message. * MESSAGES_NEXT: If fraction is true this indicates whether to retrieve * first fraction * or the next one. * fmore: Indicates whether next fraction is available. * chunk: chunk of bMessage body * * Callback allows for returning bMessage in chunks. */ typedef void (*messages_get_message_cb)(void *session, int err, gboolean fmore, const char *chunk, void *user_data); int messages_get_message(void *session, const char *handle, unsigned long flags, messages_get_message_cb callback, void *user_data); typedef void (*messages_status_cb)(void *session, int err, void *user_data); /* Informs Message Server to Update Inbox via network. * * session: Backend session. * user_data: User data if any to be sent. * Callback shall be called for every update inbox request received from MCE. */ int messages_update_inbox(void *session, messages_status_cb callback, void *user_data); /* Informs Message Server to modify read status of a given message. * * session: Backend session. * handle: Unique identifier to the message. * value: Indicates the new value of the read status for a given message. * Callback shall be called for every read status update request * recieved from MCE. * user_data: User data if any to be sent. */ int messages_set_read(void *session, const char *handle, uint8_t value, messages_status_cb callback, void *user_data); /* Informs Message Server to modify delete status of a given message. * * session: Backend session. * handle: Unique identifier to the message. * value: Indicates the new value of the delete status for a given message. * Callback shall be called for every delete status update request * recieved from MCE. * user_data: User data if any to be sent. */ int messages_set_delete(void *session, const char *handle, uint8_t value, messages_status_cb callback, void *user_data); /* Aborts currently pending request. * * session: Backend session. */ void messages_abort(void *session); bluez-5.82/obexd/plugins/PaxHeaders/phonebook.h0000644000000000000000000000005014015011623016556 xustar0020 atime=1743516718 20 ctime=1743591283 bluez-5.82/obexd/plugins/phonebook.h0000644000000000000000000001254714015011623016250 0ustar00rootroot/* SPDX-License-Identifier: GPL-2.0-or-later */ /* * * OBEX Server * * Copyright (C) 2007-2010 Marcel Holtmann * * */ #define EOL "\r\n" #define VCARD_LISTING_BEGIN \ "" EOL\ "" EOL\ "" EOL #define VCARD_LISTING_ELEMENT "" EOL #define VCARD_LISTING_END "" #define PB_TELECOM_FOLDER "/telecom" #define PB_CONTACTS_FOLDER "/telecom/pb" #define PB_CALENDAR_FOLDER "/telecom/cal" #define PB_NOTES_FOLDER "/telecom/nt" #define PB_CALLS_COMBINED_FOLDER "/telecom/cch" #define PB_CALLS_INCOMING_FOLDER "/telecom/ich" #define PB_CALLS_MISSED_FOLDER "/telecom/mch" #define PB_CALLS_OUTGOING_FOLDER "/telecom/och" #define PB_CALLS_SPEEDDIAL_FOLDER "/telecom/spd" #define PB_CALLS_FAVORITE_FOLDER "/telecom/fav" #define PB_LUID_FOLDER "/telecom/pb/luid" #define PB_CONTACTS "/telecom/pb.vcf" #define PB_CALLS_COMBINED "/telecom/cch.vcf" #define PB_CALLS_INCOMING "/telecom/ich.vcf" #define PB_CALLS_MISSED "/telecom/mch.vcf" #define PB_CALLS_OUTGOING "/telecom/och.vcf" #define PB_CALLS_SPEEDDIAL "/telecom/spd.vcf" #define PB_CALLS_FAVORITE "/telecom/fav.vcf" #define PB_DEVINFO "/telecom/devinfo.txt" #define PB_INFO_LOG "/telecom/pb/info.log" #define PB_CC_LOG "/telecom/pb/luid/cc.log" struct apparam_field { /* list and pull attributes */ uint16_t maxlistcount; uint16_t liststartoffset; /* pull and vcard attributes */ uint64_t filter; uint8_t format; /* list attributes only */ uint8_t order; uint8_t searchattrib; char *searchval; }; /* * Interface between the PBAP core and backends to retrieve * all contacts that match the application parameters rules. * Contacts will be returned in the vcard format. */ typedef void (*phonebook_cb) (const char *buffer, size_t bufsize, int vcards, int missed, gboolean lastpart, void *user_data); /* * Interface between the PBAP core and backends to * append a new entry in the PBAP folder cache. */ #define PHONEBOOK_INVALID_HANDLE 0xffffffff typedef void (*phonebook_entry_cb) (const char *id, uint32_t handle, const char *name, const char *sound, const char *tel, void *user_data); /* * After notify all entries to PBAP core, the backend * needs to notify that the operation has finished. */ typedef void (*phonebook_cache_ready_cb) (void *user_data); int phonebook_init(void); void phonebook_exit(void); /* * Changes the current folder in the phonebook back-end. The PBAP core * doesn't validate or restrict the possible values for the folders, * allowing non-standard backends implementation which doesn't follow * the PBAP virtual folder architecture. Validate the folder's name * is responsibility of the back-ends. */ char *phonebook_set_folder(const char *current_folder, const char *new_folder, uint8_t flags, int *err); /* * phonebook_pull should be used only to prepare pull request - prepared * request data is returned by this function. Start of fetching data from * back-end will be done only after calling phonebook_pull_read with this * returned request given as a parameter. * * phonebook_req_finalize MUST always be used to free associated resources. */ void *phonebook_pull(const char *name, const struct apparam_field *params, phonebook_cb cb, void *user_data, int *err); /* * phonebook_pull_read should be used to start getting results from back-end. * The back-end can return data as one response or can return it many parts. * After obtaining one part, PBAP core need to call phonebook_pull_read with * the same request again to get more results from back-end. * The back-end MUST return only the content based on the application * parameters requested by the client. * * Returns error code or 0 in case of success */ int phonebook_pull_read(void *request); /* * Function used to retrieve a contact from the backend. Only contacts * found in the cache are requested to the back-ends. The back-end MUST * return only the content based on the application parameters requested * by the client. * * Return value is a pointer to asynchronous request to phonebook back-end. * phonebook_req_finalize MUST always be used to free associated resources. */ void *phonebook_get_entry(const char *folder, const char *id, const struct apparam_field *params, phonebook_cb cb, void *user_data, int *err); /* * PBAP core will keep the contacts cache per folder. SetPhoneBook or * PullvCardListing can invalidate the cache if the current folder changes. * Cache will store only the necessary information required to reply to * PullvCardListing request and verify if a given contact belongs to the * source. * * Return value is a pointer to asynchronous request to phonebook back-end. * phonebook_req_finalize MUST always be used to free associated resources. */ void *phonebook_create_cache(const char *name, phonebook_entry_cb entry_cb, phonebook_cache_ready_cb ready_cb, void *user_data, int *err); /* * Finalizes request to phonebook back-end and deallocates associated * resources. Operation is canceled if not completed. This function MUST * always be used after any of phonebook_pull, phonebook_get_entry, and * phonebook_create_cache invoked. * * request is a pointer to asynchronous operation returned by phonebook_pull, * phonebook_get_entry, and phonebook_create_cache. */ void phonebook_req_finalize(void *request); bluez-5.82/obexd/plugins/PaxHeaders/vcard.c0000644000000000000000000000005014015011623015664 xustar0020 atime=1743516722 20 ctime=1743591283 bluez-5.82/obexd/plugins/vcard.c0000644000000000000000000005275114015011623015357 0ustar00rootroot// SPDX-License-Identifier: GPL-2.0-or-later /* * OBEX Server * * Copyright (C) 2008-2010 Intel Corporation. All rights reserved. * */ #ifdef HAVE_CONFIG_H #include #endif #include #include #include #include #include #include #include #include "gdbus/gdbus.h" #include "vcard.h" #define ADDR_FIELD_AMOUNT 7 #define LEN_MAX 128 #define TYPE_INTERNATIONAL 145 #define PHONEBOOK_FLAG_CACHED 0x1 #define FILTER_VERSION (1 << 0) #define FILTER_FN (1 << 1) #define FILTER_N (1 << 2) #define FILTER_PHOTO (1 << 3) #define FILTER_BDAY (1 << 4) #define FILTER_ADR (1 << 5) #define FILTER_LABEL (1 << 6) #define FILTER_TEL (1 << 7) #define FILTER_EMAIL (1 << 8) #define FILTER_MAILER (1 << 9) #define FILTER_TZ (1 << 10) #define FILTER_GEO (1 << 11) #define FILTER_TITLE (1 << 12) #define FILTER_ROLE (1 << 13) #define FILTER_LOGO (1 << 14) #define FILTER_AGENT (1 << 15) #define FILTER_ORG (1 << 16) #define FILTER_NOTE (1 << 17) #define FILTER_REV (1 << 18) #define FILTER_SOUND (1 << 19) #define FILTER_URL (1 << 20) #define FILTER_UID (1 << 21) #define FILTER_KEY (1 << 22) #define FILTER_NICKNAME (1 << 23) #define FILTER_CATEGORIES (1 << 24) #define FILTER_PROID (1 << 25) #define FILTER_CLASS (1 << 26) #define FILTER_SORT_STRING (1 << 27) #define FILTER_X_IRMC_CALL_DATETIME (1 << 28) #define FORMAT_VCARD21 0x00 #define FORMAT_VCARD30 0x01 #define QP_LINE_LEN 75 #define QP_CHAR_LEN 3 #define QP_CR 0x0D #define QP_LF 0x0A #define QP_ESC 0x5C #define QP_SOFT_LINE_BREAK "=" #define QP_SELECT "\n!\"#$=@[\\]^`{|}~" #define ASCII_LIMIT 0x7F /* according to RFC 2425, the output string may need folding */ static void vcard_printf(GString *str, const char *fmt, ...) { char buf[1024]; va_list ap; int len_temp, line_number, i; unsigned int line_delimit = 75; va_start(ap, fmt); vsnprintf(buf, sizeof(buf), fmt, ap); va_end(ap); line_number = strlen(buf) / line_delimit + 1; for (i = 0; i < line_number; i++) { len_temp = MIN(line_delimit, strlen(buf) - line_delimit * i); g_string_append_len(str, buf + line_delimit * i, len_temp); if (i != line_number - 1) g_string_append(str, "\r\n "); } g_string_append(str, "\r\n"); } /* According to RFC 2426, we need escape following characters: * '\n', '\r', ';', ',', '\'. */ static void add_slash(char *dest, const char *src, int len_max, int len) { int i, j; for (i = 0, j = 0; i < len && j + 1 < len_max; i++, j++) { /* filling dest buffer - last field need to be reserved * for '\0'*/ switch (src[i]) { case '\n': if (j + 2 >= len_max) /* not enough space in the buffer to put char * preceded with escaping sequence (and '\0' in * the end) */ goto done; dest[j++] = '\\'; dest[j] = 'n'; break; case '\r': if (j + 2 >= len_max) goto done; dest[j++] = '\\'; dest[j] = 'r'; break; case '\\': case ';': case ',': if (j + 2 >= len_max) goto done; dest[j++] = '\\'; /* fall through */ default: dest[j] = src[i]; break; } } done: dest[j] = 0; } static void escape_semicolon(char *dest, const char *src, int len_max, int len) { int i, j; for (i = 0, j = 0; i < len && j + 1 < len_max; i++, j++) { if (src[i] == ';') { if (j + 2 >= len_max) break; dest[j++] = '\\'; } dest[j] = src[i]; } dest[j] = 0; } static void set_escape(uint8_t format, char *dest, const char *src, int len_max, int len) { if (format == FORMAT_VCARD30) add_slash(dest, src, len_max, len); else if (format == FORMAT_VCARD21) escape_semicolon(dest, src, len_max, len); } static void get_escaped_fields(uint8_t format, char **fields, ...) { va_list ap; GString *line; char *field; char escaped[LEN_MAX]; va_start(ap, fields); line = g_string_new(""); for (field = va_arg(ap, char *); field; ) { set_escape(format, escaped, field, LEN_MAX, strlen(field)); g_string_append(line, escaped); field = va_arg(ap, char *); if (field) g_string_append(line, ";"); } va_end(ap); *fields = g_string_free(line, FALSE); } static gboolean set_qp_encoding(char c) { unsigned char q = c; if (strchr(QP_SELECT, q) != NULL) return TRUE; if (q < '!' || q > '~') return TRUE; return FALSE; } static void append_qp_break_line(GString *vcards, size_t *limit) { /* Quoted Printable lines of text must be limited to less than 76 * characters and terminated by Quoted Printable softline break * sequence of "=" (if some more characters left) */ g_string_append(vcards, QP_SOFT_LINE_BREAK); g_string_append(vcards, "\r\n "); *limit = QP_LINE_LEN - 1; } static void append_qp_ascii(GString *vcards, size_t *limit, char c) { if (*limit == 0) append_qp_break_line(vcards, limit); g_string_append_c(vcards, c); --*limit; } static void append_qp_hex(GString *vcards, size_t *limit, char c) { if (*limit < QP_CHAR_LEN) append_qp_break_line(vcards, limit); g_string_append_printf(vcards, "=%2.2X", (unsigned char) c); *limit -= QP_CHAR_LEN; } static void append_qp_new_line(GString *vcards, size_t *limit) { /* Multiple lines of text are separated with a Quoted Printable CRLF * sequence of "=0D" followed by "=0A" followed by a Quoted Printable * softline break sequence of "=" */ append_qp_hex(vcards, limit, QP_CR); append_qp_hex(vcards, limit, QP_LF); append_qp_break_line(vcards, limit); } static gboolean utf8_select(const char *field) { const char *pos; if (g_utf8_validate(field, -1, NULL) == FALSE) return FALSE; for (pos = field; *pos != '\0'; pos = g_utf8_next_char(pos)) { /* Test for non-standard UTF-8 character (out of range * standard ASCII set), composed of more than single byte * and represented by 32-bit value greater than 0x7F */ if (g_utf8_get_char(pos) > ASCII_LIMIT) return TRUE; } return FALSE; } static void vcard_qp_print_encoded(GString *vcards, const char *desc, ...) { const char *field, *charset = ""; const char *encoding = ";ENCODING=QUOTED-PRINTABLE"; size_t limit, param_len; va_list ap; va_start(ap, desc); for (field = va_arg(ap, char *); field; field = va_arg(ap, char *)) { if (utf8_select(field) == TRUE) { charset = ";CHARSET=UTF-8"; break; } } va_end(ap); vcard_printf(vcards, "%s%s%s:", desc, encoding, charset); g_string_truncate(vcards, vcards->len - 2); param_len = strlen(desc) + strlen(encoding) + strlen(charset) + 1; limit = QP_LINE_LEN - param_len; va_start(ap, desc); for (field = va_arg(ap, char *); field != NULL; ) { size_t i, size = strlen(field); for (i = 0; i < size; ++i) { if (set_qp_encoding(field[i])) { if (field[i] == '\n') { append_qp_new_line(vcards, &limit); continue; } append_qp_hex(vcards, &limit, field[i]); } else { /* According to vCard 2.1 spec. semicolons in * property parameter value must be escaped */ if (field[i] == ';') append_qp_hex(vcards, &limit, QP_ESC); append_qp_ascii(vcards, &limit, field[i]); } } field = va_arg(ap, char *); if (field) append_qp_ascii(vcards, &limit, ';'); } va_end(ap); g_string_append(vcards, "\r\n"); } static gboolean select_qp_encoding(uint8_t format, ...) { char *field; va_list ap; if (format != FORMAT_VCARD21) return FALSE; va_start(ap, format); for (field = va_arg(ap, char *); field; field = va_arg(ap, char *)) { int i; unsigned char c; if (strpbrk(field, QP_SELECT)) { va_end(ap); return TRUE; } /* Quoted Printable encoding is selected if there is * a character, which value is out of range standard * ASCII set, since it may be a part of some * non-standard character such as specified by UTF-8 */ for (i = 0; (c = field[i]) != '\0'; ++i) { if (c > ASCII_LIMIT) { va_end(ap); return TRUE; } } } va_end(ap); return FALSE; } static void vcard_printf_begin(GString *vcards, uint8_t format) { vcard_printf(vcards, "BEGIN:VCARD"); if (format == FORMAT_VCARD30) vcard_printf(vcards, "VERSION:3.0"); else if (format == FORMAT_VCARD21) vcard_printf(vcards, "VERSION:2.1"); } /* check if there is at least one contact field with personal data present */ static gboolean contact_fields_present(struct phonebook_contact * contact) { if (contact->family && strlen(contact->family) > 0) return TRUE; if (contact->given && strlen(contact->given) > 0) return TRUE; if (contact->additional && strlen(contact->additional) > 0) return TRUE; if (contact->prefix && strlen(contact->prefix) > 0) return TRUE; if (contact->suffix && strlen(contact->suffix) > 0) return TRUE; /* none of the personal data fields are present*/ return FALSE; } static void vcard_printf_name(GString *vcards, uint8_t format, struct phonebook_contact *contact) { char *fields; if (contact_fields_present(contact) == FALSE) { /* If fields are empty, add only 'N:' as parameter. * This is crucial for some devices (Nokia BH-903) which * have problems with history listings and can't determine * that a parameter is really empty if there are unnecessary * characters after 'N:' (e.g. 'N:;;;;'). * We need to add only'N:' param - without semicolons. */ vcard_printf(vcards, "N:"); return; } if (select_qp_encoding(format, contact->family, contact->given, contact->additional, contact->prefix, contact->suffix, NULL)) { vcard_qp_print_encoded(vcards, "N", contact->family, contact->given, contact->additional, contact->prefix, contact->suffix, NULL); return; } get_escaped_fields(format, &fields, contact->family, contact->given, contact->additional, contact->prefix, contact->suffix, NULL); vcard_printf(vcards, "N:%s", fields); g_free(fields); } static void vcard_printf_fullname(GString *vcards, uint8_t format, const char *text) { char field[LEN_MAX]; if (!text || strlen(text) == 0) { vcard_printf(vcards, "FN:"); return; } if (select_qp_encoding(format, text, NULL)) { vcard_qp_print_encoded(vcards, "FN", text, NULL); return; } set_escape(format, field, text, LEN_MAX, strlen(text)); vcard_printf(vcards, "FN:%s", field); } static void vcard_printf_number(GString *vcards, uint8_t format, const char *number, int type, enum phonebook_number_type category) { const char *intl = "", *category_string = ""; char buf[LEN_MAX], field[LEN_MAX]; /* TEL is a mandatory field, include even if empty */ if (!number || !strlen(number) || !type) { vcard_printf(vcards, "TEL:"); return; } switch (category) { case TEL_TYPE_HOME: if (format == FORMAT_VCARD21) category_string = "HOME;VOICE"; else if (format == FORMAT_VCARD30) category_string = "TYPE=HOME;TYPE=VOICE"; break; case TEL_TYPE_MOBILE: if (format == FORMAT_VCARD21) category_string = "CELL;VOICE"; else if (format == FORMAT_VCARD30) category_string = "TYPE=CELL;TYPE=VOICE"; break; case TEL_TYPE_FAX: if (format == FORMAT_VCARD21) category_string = "FAX"; else if (format == FORMAT_VCARD30) category_string = "TYPE=FAX"; break; case TEL_TYPE_WORK: if (format == FORMAT_VCARD21) category_string = "WORK;VOICE"; else if (format == FORMAT_VCARD30) category_string = "TYPE=WORK;TYPE=VOICE"; break; case TEL_TYPE_OTHER: if (format == FORMAT_VCARD21) category_string = "OTHER;VOICE"; else if (format == FORMAT_VCARD30) category_string = "TYPE=OTHER;TYPE=VOICE"; break; } if ((type == TYPE_INTERNATIONAL) && (number[0] != '+')) intl = "+"; snprintf(field, sizeof(field), "%s%s", intl, number); if (select_qp_encoding(format, number, NULL)) { snprintf(buf, sizeof(buf), "TEL;%s", category_string); vcard_qp_print_encoded(vcards, buf, field, NULL); return; } vcard_printf(vcards, "TEL;%s:%s", category_string, field); } static void vcard_printf_tag(GString *vcards, uint8_t format, const char *tag, const char *category, const char *fld) { int len; char *separator = "", *type = ""; char buf[LEN_MAX], field[LEN_MAX]; if (tag == NULL || strlen(tag) == 0) return; if (fld == NULL || (len = strlen(fld)) == 0) { vcard_printf(vcards, "%s:", tag); return; } if (category && strlen(category)) { separator = ";"; if (format == FORMAT_VCARD30) type = "TYPE="; } else { category = ""; } snprintf(buf, LEN_MAX, "%s%s%s%s", tag, separator, type, category); if (select_qp_encoding(format, fld, NULL)) { vcard_qp_print_encoded(vcards, buf, fld, NULL); return; } set_escape(format, field, fld, LEN_MAX, len); vcard_printf(vcards, "%s:%s", buf, field); } static void vcard_printf_email(GString *vcards, uint8_t format, const char *address, enum phonebook_field_type category) { const char *category_string = ""; char buf[LEN_MAX], field[LEN_MAX]; int len = 0; if (!address || !(len = strlen(address))) { vcard_printf(vcards, "EMAIL:"); return; } switch (category) { case FIELD_TYPE_HOME: if (format == FORMAT_VCARD21) category_string = "INTERNET;HOME"; else if (format == FORMAT_VCARD30) category_string = "TYPE=INTERNET;TYPE=HOME"; break; case FIELD_TYPE_WORK: if (format == FORMAT_VCARD21) category_string = "INTERNET;WORK"; else if (format == FORMAT_VCARD30) category_string = "TYPE=INTERNET;TYPE=WORK"; break; case FIELD_TYPE_OTHER: default: if (format == FORMAT_VCARD21) category_string = "INTERNET"; else if (format == FORMAT_VCARD30) category_string = "TYPE=INTERNET;TYPE=OTHER"; } if (select_qp_encoding(format, address, NULL)) { snprintf(buf, sizeof(buf), "EMAIL;%s", category_string); vcard_qp_print_encoded(vcards, buf, address, NULL); return; } set_escape(format, field, address, LEN_MAX, len); vcard_printf(vcards, "EMAIL;%s:%s", category_string, field); } static void vcard_printf_url(GString *vcards, uint8_t format, const char *url, enum phonebook_field_type category) { const char *category_string = ""; char buf[LEN_MAX], field[LEN_MAX]; if (!url || strlen(url) == 0) { vcard_printf(vcards, "URL:"); return; } switch (category) { case FIELD_TYPE_HOME: if (format == FORMAT_VCARD21) category_string = "INTERNET;HOME"; else if (format == FORMAT_VCARD30) category_string = "TYPE=INTERNET;TYPE=HOME"; break; case FIELD_TYPE_WORK: if (format == FORMAT_VCARD21) category_string = "INTERNET;WORK"; else if (format == FORMAT_VCARD30) category_string = "TYPE=INTERNET;TYPE=WORK"; break; case FIELD_TYPE_OTHER: default: if (format == FORMAT_VCARD21) category_string = "INTERNET"; else if (format == FORMAT_VCARD30) category_string = "TYPE=INTERNET"; break; } if (select_qp_encoding(format, url, NULL)) { snprintf(buf, sizeof(buf), "URL;%s", category_string); vcard_qp_print_encoded(vcards, buf, url, NULL); return; } set_escape(format, field, url, LEN_MAX, strlen(url)); vcard_printf(vcards, "URL;%s:%s", category_string, field); } static gboolean org_fields_present(struct phonebook_contact *contact) { if (contact->company && strlen(contact->company)) return TRUE; if (contact->department && strlen(contact->department)) return TRUE; return FALSE; } static void vcard_printf_org(GString *vcards, uint8_t format, struct phonebook_contact *contact) { char *fields; if (org_fields_present(contact) == FALSE) return; if (select_qp_encoding(format, contact->company, contact->department, NULL)) { vcard_qp_print_encoded(vcards, "ORG", contact->company, contact->department, NULL); return; } get_escaped_fields(format, &fields, contact->company, contact->department, NULL); vcard_printf(vcards, "ORG:%s", fields); g_free(fields); } static void vcard_printf_address(GString *vcards, uint8_t format, struct phonebook_addr *address) { char *fields, field_esc[LEN_MAX]; const char *category_string = ""; char buf[LEN_MAX], *address_fields[ADDR_FIELD_AMOUNT]; int i; size_t len; GSList *l; if (!address) { vcard_printf(vcards, "ADR:"); return; } switch (address->type) { case FIELD_TYPE_HOME: if (format == FORMAT_VCARD21) category_string = "HOME"; else if (format == FORMAT_VCARD30) category_string = "TYPE=HOME"; break; case FIELD_TYPE_WORK: if (format == FORMAT_VCARD21) category_string = "WORK"; else if (format == FORMAT_VCARD30) category_string = "TYPE=WORK"; break; default: if (format == FORMAT_VCARD21) category_string = "OTHER"; else if (format == FORMAT_VCARD30) category_string = "TYPE=OTHER"; break; } for (i = 0, l = address->fields; l; l = l->next) address_fields[i++] = l->data; if (select_qp_encoding(format, address_fields[0], address_fields[1], address_fields[2], address_fields[3], address_fields[4], address_fields[5], address_fields[6], NULL)) { snprintf(buf, sizeof(buf), "ADR;%s", category_string); vcard_qp_print_encoded(vcards, buf, address_fields[0], address_fields[1], address_fields[2], address_fields[3], address_fields[4], address_fields[5], address_fields[6], NULL); return; } /* allocate enough memory to insert address fields separated by ';' * and terminated by '\0' */ len = ADDR_FIELD_AMOUNT * LEN_MAX; fields = g_malloc0(len); for (l = address->fields; l; l = l->next) { char *field = l->data; if (field) { set_escape(format, field_esc, field, LEN_MAX, strlen(field)); g_strlcat(fields, field_esc, len); } if (l->next) /* not adding ';' after last addr field */ g_strlcat(fields, ";", len); } vcard_printf(vcards,"ADR;%s:%s", category_string, fields); g_free(fields); } static void vcard_printf_datetime(GString *vcards, uint8_t format, struct phonebook_contact *contact) { const char *type; char buf[LEN_MAX]; switch (contact->calltype) { case CALL_TYPE_MISSED: type = "MISSED"; break; case CALL_TYPE_INCOMING: type = "RECEIVED"; break; case CALL_TYPE_OUTGOING: type = "DIALED"; break; case CALL_TYPE_NOT_A_CALL: default: return; } if (select_qp_encoding(format, contact->datetime, NULL)) { snprintf(buf, sizeof(buf), "X-IRMC-CALL-DATETIME;%s", type); vcard_qp_print_encoded(vcards, buf, contact->datetime, NULL); return; } vcard_printf(vcards, "X-IRMC-CALL-DATETIME;%s:%s", type, contact->datetime); } static void vcard_printf_end(GString *vcards) { vcard_printf(vcards, "END:VCARD"); } void phonebook_add_contact(GString *vcards, struct phonebook_contact *contact, uint64_t filter, uint8_t format) { if (format == FORMAT_VCARD30 && filter) filter |= (FILTER_VERSION | FILTER_FN | FILTER_N | FILTER_TEL); else if (format == FORMAT_VCARD21 && filter) filter |= (FILTER_VERSION | FILTER_N | FILTER_TEL); else filter = (FILTER_VERSION | FILTER_UID | FILTER_N | FILTER_FN | FILTER_TEL | FILTER_EMAIL | FILTER_ADR | FILTER_BDAY | FILTER_NICKNAME | FILTER_URL | FILTER_PHOTO | FILTER_ORG | FILTER_ROLE | FILTER_TITLE | FILTER_X_IRMC_CALL_DATETIME); vcard_printf_begin(vcards, format); if (filter & FILTER_UID && *contact->uid) vcard_printf_tag(vcards, format, "UID", NULL, contact->uid); if (filter & FILTER_N) vcard_printf_name(vcards, format, contact); if (filter & FILTER_FN && (*contact->fullname || format == FORMAT_VCARD30)) vcard_printf_fullname(vcards, format, contact->fullname); if (filter & FILTER_TEL) { GSList *l = contact->numbers; if (g_slist_length(l) == 0) vcard_printf_number(vcards, format, NULL, 1, TEL_TYPE_OTHER); for (; l; l = l->next) { struct phonebook_field *number = l->data; vcard_printf_number(vcards, format, number->text, 1, number->type); } } if (filter & FILTER_EMAIL) { GSList *l = contact->emails; for (; l; l = l->next) { struct phonebook_field *email = l->data; vcard_printf_email(vcards, format, email->text, email->type); } } if (filter & FILTER_ADR) { GSList *l = contact->addresses; for (; l; l = l->next) { struct phonebook_addr *addr = l->data; vcard_printf_address(vcards, format, addr); } } if (filter & FILTER_BDAY && *contact->birthday) vcard_printf_tag(vcards, format, "BDAY", NULL, contact->birthday); if (filter & FILTER_NICKNAME && *contact->nickname) vcard_printf_tag(vcards, format, "NICKNAME", NULL, contact->nickname); if (filter & FILTER_URL) { GSList *l = contact->urls; for (; l; l = l->next) { struct phonebook_field *url = l->data; vcard_printf_url(vcards, format, url->text, url->type); } } if (filter & FILTER_PHOTO && *contact->photo) vcard_printf_tag(vcards, format, "PHOTO", NULL, contact->photo); if (filter & FILTER_ORG) vcard_printf_org(vcards, format, contact); if (filter & FILTER_ROLE && *contact->role) vcard_printf_tag(vcards, format, "ROLE", NULL, contact->role); if (filter & FILTER_TITLE && *contact->title) vcard_printf_tag(vcards, format, "TITLE", NULL, contact->title); if (filter & FILTER_X_IRMC_CALL_DATETIME) vcard_printf_datetime(vcards, format, contact); vcard_printf_end(vcards); } static void field_free(gpointer data) { struct phonebook_field *field = data; g_free(field->text); g_free(field); } void phonebook_addr_free(gpointer addr) { struct phonebook_addr *address = addr; g_slist_free_full(address->fields, g_free); g_free(address); } void phonebook_contact_free(struct phonebook_contact *contact) { if (contact == NULL) return; g_slist_free_full(contact->numbers, field_free); g_slist_free_full(contact->emails, field_free); g_slist_free_full(contact->addresses, phonebook_addr_free); g_slist_free_full(contact->urls, field_free); g_free(contact->uid); g_free(contact->fullname); g_free(contact->given); g_free(contact->family); g_free(contact->additional); g_free(contact->prefix); g_free(contact->suffix); g_free(contact->birthday); g_free(contact->nickname); g_free(contact->photo); g_free(contact->company); g_free(contact->department); g_free(contact->role); g_free(contact->title); g_free(contact->datetime); g_free(contact); } bluez-5.82/obexd/plugins/PaxHeaders/vcard.h0000644000000000000000000000005014015011623015671 xustar0020 atime=1743516722 20 ctime=1743591283 bluez-5.82/obexd/plugins/vcard.h0000644000000000000000000000217214015011623015354 0ustar00rootroot/* SPDX-License-Identifier: GPL-2.0-or-later */ /* * OBEX Server * * Copyright (C) 2008-2010 Intel Corporation. All rights reserved. * */ enum phonebook_number_type { TEL_TYPE_HOME, TEL_TYPE_MOBILE, TEL_TYPE_FAX, TEL_TYPE_WORK, TEL_TYPE_OTHER, }; enum phonebook_field_type { FIELD_TYPE_HOME, FIELD_TYPE_WORK, FIELD_TYPE_OTHER, }; enum phonebook_call_type { CALL_TYPE_NOT_A_CALL, CALL_TYPE_MISSED, CALL_TYPE_INCOMING, CALL_TYPE_OUTGOING, }; struct phonebook_field { char *text; int type; }; struct phonebook_addr { GSList *fields; int type; }; struct phonebook_contact { char *uid; char *fullname; char *given; char *family; char *additional; GSList *numbers; GSList *emails; char *prefix; char *suffix; GSList *addresses; char *birthday; char *nickname; GSList *urls; char *photo; char *company; char *department; char *role; char *title; char *datetime; int calltype; }; void phonebook_add_contact(GString *vcards, struct phonebook_contact *contact, uint64_t filter, uint8_t format); void phonebook_contact_free(struct phonebook_contact *contact); void phonebook_addr_free(gpointer addr); bluez-5.82/obexd/plugins/PaxHeaders/pcsuite.c0000644000000000000000000000005014766002272016256 xustar0020 atime=1743515578 20 ctime=1743591283 bluez-5.82/obexd/plugins/pcsuite.c0000644000000000000000000002523114766002272015742 0ustar00rootroot// SPDX-License-Identifier: GPL-2.0-or-later /* * * OBEX Server * * Copyright (C) 2007-2010 Nokia Corporation * Copyright (C) 2007-2010 Marcel Holtmann * * */ #ifdef HAVE_CONFIG_H #include #endif #include #include #include #include #include #include #include #include #include #include #include #include #include "gdbus/gdbus.h" #include "obexd/src/obexd.h" #include "obexd/src/plugin.h" #include "obexd/src/log.h" #include "obexd/src/obex.h" #include "obexd/src/mimetype.h" #include "obexd/src/service.h" #include "ftp.h" #define PCSUITE_CHANNEL 24 #define PCSUITE_WHO_SIZE 8 #define PCSUITE_RECORD " \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ " #define BACKUP_BUS_NAME "com.nokia.backup.plugin" #define BACKUP_PATH "/com/nokia/backup" #define BACKUP_PLUGIN_INTERFACE "com.nokia.backup.plugin" #define BACKUP_DBUS_TIMEOUT (1000 * 60 * 15) static const uint8_t FTP_TARGET[TARGET_SIZE] = { 0xF9, 0xEC, 0x7B, 0xC4, 0x95, 0x3C, 0x11, 0xD2, 0x98, 0x4E, 0x52, 0x54, 0x00, 0xDC, 0x9E, 0x09 }; static const uint8_t PCSUITE_WHO[PCSUITE_WHO_SIZE] = { 'P', 'C', ' ', 'S', 'u', 'i', 't', 'e' }; struct pcsuite_session { struct ftp_session *ftp; char *lock_file; int fd; }; static void *pcsuite_connect(struct obex_session *os, int *err) { struct pcsuite_session *pcsuite; struct ftp_session *ftp; int fd; char *filename; DBG(""); ftp = ftp_connect(os, err); if (ftp == NULL) return NULL; filename = g_build_filename(g_get_home_dir(), ".pcsuite", NULL); fd = open(filename, O_WRONLY | O_CREAT | O_EXCL, 0644); if (fd < 0 && errno != EEXIST) { error("open(%s): %s(%d)", filename, strerror(errno), errno); goto fail; } /* Try to remove the file before retrying since it could be that some process left/crash without removing it */ if (fd < 0) { if (remove(filename) < 0) { error("remove(%s): %s(%d)", filename, strerror(errno), errno); goto fail; } fd = open(filename, O_WRONLY | O_CREAT | O_EXCL, 0644); if (fd < 0) { error("open(%s): %s(%d)", filename, strerror(errno), errno); goto fail; } } DBG("%s created", filename); pcsuite = g_new0(struct pcsuite_session, 1); pcsuite->ftp = ftp; pcsuite->lock_file = filename; pcsuite->fd = fd; DBG("session %p created", pcsuite); if (err) *err = 0; return pcsuite; fail: if (ftp) ftp_disconnect(os, ftp); if (err) *err = -errno; g_free(filename); return NULL; } static int pcsuite_get(struct obex_session *os, void *user_data) { struct pcsuite_session *pcsuite = user_data; DBG("%p", pcsuite); return ftp_get(os, pcsuite->ftp); } static int pcsuite_chkput(struct obex_session *os, void *user_data) { struct pcsuite_session *pcsuite = user_data; DBG("%p", pcsuite); return ftp_chkput(os, pcsuite->ftp); } static int pcsuite_put(struct obex_session *os, void *user_data) { struct pcsuite_session *pcsuite = user_data; DBG("%p", pcsuite); return ftp_put(os, pcsuite->ftp); } static int pcsuite_setpath(struct obex_session *os, void *user_data) { struct pcsuite_session *pcsuite = user_data; DBG("%p", pcsuite); return ftp_setpath(os, pcsuite->ftp); } static int pcsuite_action(struct obex_session *os, void *user_data) { struct pcsuite_session *pcsuite = user_data; DBG("%p", pcsuite); return ftp_action(os, pcsuite->ftp); } static void pcsuite_disconnect(struct obex_session *os, void *user_data) { struct pcsuite_session *pcsuite = user_data; DBG("%p", pcsuite); if (pcsuite->fd >= 0) close(pcsuite->fd); if (pcsuite->lock_file) { if (remove(pcsuite->lock_file) < 0) error("remove(%s): %s(%d)", pcsuite->lock_file, strerror(errno), errno); g_free(pcsuite->lock_file); } if (pcsuite->ftp) ftp_disconnect(os, pcsuite->ftp); g_free(pcsuite); } static const struct obex_service_driver pcsuite = { .name = "Nokia OBEX PC Suite Services", .service = OBEX_PCSUITE, .channel = PCSUITE_CHANNEL, .secure = TRUE, .record = PCSUITE_RECORD, .target = FTP_TARGET, .target_size = TARGET_SIZE, .who = PCSUITE_WHO, .who_size = PCSUITE_WHO_SIZE, .connect = pcsuite_connect, .get = pcsuite_get, .put = pcsuite_put, .chkput = pcsuite_chkput, .setpath = pcsuite_setpath, .action = pcsuite_action, .disconnect = pcsuite_disconnect }; struct backup_object { char *cmd; int fd; int oflag; int error_code; mode_t mode; DBusPendingCall *pending_call; DBusConnection *conn; }; static void on_backup_dbus_notify(DBusPendingCall *pending_call, void *user_data) { struct backup_object *obj = user_data; DBusMessage *reply; const char *filename; int error_code; DBG("Notification received for pending call - %s", obj->cmd); reply = dbus_pending_call_steal_reply(pending_call); if (reply && dbus_message_get_args(reply, NULL, DBUS_TYPE_INT32, &error_code, DBUS_TYPE_STRING, &filename, DBUS_TYPE_INVALID)) { obj->error_code = error_code; if (filename) { DBG("Notification - file path = %s, error_code = %d", filename, error_code); if (error_code == 0) obj->fd = open(filename,obj->oflag,obj->mode); } } else DBG("Notification timed out or connection got closed"); if (reply) dbus_message_unref(reply); dbus_pending_call_unref(pending_call); obj->pending_call = NULL; dbus_connection_unref(obj->conn); obj->conn = NULL; if (obj->fd >= 0) { DBG("File opened, setting io flags, cmd = %s", obj->cmd); if (obj->oflag == O_RDONLY) obex_object_set_io_flags(user_data, G_IO_IN, 0); else obex_object_set_io_flags(user_data, G_IO_OUT, 0); } else { DBG("File open error, setting io error, cmd = %s", obj->cmd); obex_object_set_io_flags(user_data, G_IO_ERR, -EPERM); } } static gboolean send_backup_dbus_message(const char *oper, struct backup_object *obj, size_t *size) { DBusConnection *conn; DBusMessage *msg; DBusPendingCall *pending_call; gboolean ret = FALSE; dbus_uint32_t file_size; file_size = size ? *size : 0; conn = obex_setup_dbus_connection(NULL, NULL); if (conn == NULL) return FALSE; msg = dbus_message_new_method_call(BACKUP_BUS_NAME, BACKUP_PATH, BACKUP_PLUGIN_INTERFACE, "request"); if (msg == NULL) { dbus_connection_unref(conn); return FALSE; } dbus_message_append_args(msg, DBUS_TYPE_STRING, &oper, DBUS_TYPE_STRING, &obj->cmd, DBUS_TYPE_INT32, &file_size, DBUS_TYPE_INVALID); if (strcmp(oper, "open") == 0) { ret = g_dbus_send_message_with_reply(conn, msg, &pending_call, BACKUP_DBUS_TIMEOUT); dbus_message_unref(msg); if (ret) { obj->conn = conn; obj->pending_call = pending_call; ret = dbus_pending_call_set_notify(pending_call, on_backup_dbus_notify, obj, NULL); } else dbus_connection_unref(conn); } else { g_dbus_send_message(conn, msg); dbus_connection_unref(conn); } return ret; } static void *backup_open(const char *name, int oflag, mode_t mode, void *context, size_t *size, int *err) { struct backup_object *obj = g_new0(struct backup_object, 1); DBG("cmd = %s", name); obj->cmd = g_path_get_basename(name); obj->oflag = oflag; obj->mode = mode; obj->fd = -1; obj->pending_call = NULL; obj->conn = NULL; obj->error_code = 0; if (send_backup_dbus_message("open", obj, size) == FALSE) { g_free(obj->cmd); g_free(obj); obj = NULL; } if (err) *err = 0; return obj; } static int backup_close(void *object) { struct backup_object *obj = object; size_t size = 0; DBG("cmd = %s", obj->cmd); if (obj->fd != -1) close(obj->fd); if (obj->pending_call) { dbus_pending_call_cancel(obj->pending_call); dbus_pending_call_unref(obj->pending_call); dbus_connection_unref(obj->conn); } send_backup_dbus_message("close", obj, &size); g_free(obj->cmd); g_free(obj); return 0; } static ssize_t backup_read(void *object, void *buf, size_t count) { struct backup_object *obj = object; ssize_t ret = 0; if (obj->pending_call) { DBG("cmd = %s, IN WAITING STAGE", obj->cmd); return -EAGAIN; } if (obj->fd != -1) { DBG("cmd = %s, READING DATA", obj->cmd); ret = read(obj->fd, buf, count); if (ret < 0) ret = -errno; } else { DBG("cmd = %s, PERMANENT FAILURE", obj->cmd); ret = obj->error_code ? -obj->error_code : -ENOENT; } return ret; } static ssize_t backup_write(void *object, const void *buf, size_t count) { struct backup_object *obj = object; ssize_t ret = 0; if (obj->pending_call) { DBG("cmd = %s, IN WAITING STAGE", obj->cmd); return -EAGAIN; } if (obj->fd != -1) { ret = write(obj->fd, buf, count); DBG("cmd = %s, WRITTING", obj->cmd); if (ret < 0) { error("backup: cmd = %s", obj->cmd); ret = -errno; } } else { error("backup: cmd = %s", obj->cmd); ret = obj->error_code ? -obj->error_code : -ENOENT; } return ret; } static int backup_flush(void *object) { DBG("%p", object); return 0; } static const struct obex_mime_type_driver backup = { .target = FTP_TARGET, .target_size = TARGET_SIZE, .mimetype = "application/vnd.nokia-backup", .open = backup_open, .close = backup_close, .read = backup_read, .write = backup_write, .flush = backup_flush, }; static int pcsuite_init(void) { int err; err = obex_service_driver_register(&pcsuite); if (err < 0) return err; err = obex_mime_type_driver_register(&backup); if (err < 0) obex_service_driver_unregister(&pcsuite); return err; } static void pcsuite_exit(void) { obex_mime_type_driver_unregister(&backup); obex_service_driver_unregister(&pcsuite); } OBEX_PLUGIN_DEFINE(pcsuite, pcsuite_init, pcsuite_exit) bluez-5.82/obexd/plugins/PaxHeaders/ftp.c0000644000000000000000000000005014711225434015370 xustar0020 atime=1743516716 20 ctime=1743591283 bluez-5.82/obexd/plugins/ftp.c0000644000000000000000000002346014711225434015056 0ustar00rootroot// SPDX-License-Identifier: GPL-2.0-or-later /* * * OBEX Server * * Copyright (C) 2007-2010 Nokia Corporation * Copyright (C) 2007-2010 Marcel Holtmann * * */ #ifdef HAVE_CONFIG_H #include #endif #include #include #include #include #include #include #include #include #include #include #include #include #include "obexd/src/obexd.h" #include "obexd/src/plugin.h" #include "obexd/src/log.h" #include "obexd/src/obex.h" #include "obexd/src/manager.h" #include "obexd/src/mimetype.h" #include "obexd/src/service.h" #include "ftp.h" #include "filesystem.h" #define LST_TYPE "x-obex/folder-listing" #define CAP_TYPE "x-obex/capability" static const uint8_t FTP_TARGET[TARGET_SIZE] = { 0xF9, 0xEC, 0x7B, 0xC4, 0x95, 0x3C, 0x11, 0xD2, 0x98, 0x4E, 0x52, 0x54, 0x00, 0xDC, 0x9E, 0x09 }; struct ftp_session { struct obex_session *os; struct obex_transfer *transfer; char *folder; }; static void set_folder(struct ftp_session *ftp, const char *new_folder) { DBG("%p folder %s", ftp, new_folder); g_free(ftp->folder); ftp->folder = new_folder ? g_strdup(new_folder) : NULL; } static int get_by_type(struct ftp_session *ftp, const char *type) { struct obex_session *os = ftp->os; const char *capability = obex_option_capability(); const char *name = obex_get_name(os); char *path; int err; DBG("%p name %s type %s", ftp, name, type); if (type == NULL && name == NULL) return -EBADR; if (type != NULL && g_ascii_strcasecmp(type, CAP_TYPE) == 0) return obex_get_stream_start(os, capability); if (name != NULL && !is_filename(name)) return -EBADR; path = g_build_filename(ftp->folder, name, NULL); err = obex_get_stream_start(os, path); g_free(path); return err; } void *ftp_connect(struct obex_session *os, int *err) { struct ftp_session *ftp; const char *root_folder; DBG(""); root_folder = obex_option_root_folder(); manager_register_session(os); ftp = g_new0(struct ftp_session, 1); set_folder(ftp, root_folder); ftp->os = os; if (err) *err = 0; ftp->transfer = manager_register_transfer(os); DBG("session %p created", ftp); return ftp; } int ftp_get(struct obex_session *os, void *user_data) { struct ftp_session *ftp = user_data; const char *type = obex_get_type(os); int ret; DBG("%p", ftp); if (ftp->folder == NULL) return -ENOENT; ret = get_by_type(ftp, type); if (ret < 0) return ret; /* Only track progress of file transfer */ if (type == NULL) manager_emit_transfer_started(ftp->transfer); return 0; } static int ftp_delete(struct ftp_session *ftp, const char *name) { char *path; int ret = 0; DBG("%p name %s", ftp, name); if (!(ftp->folder && name)) return -EINVAL; path = g_build_filename(ftp->folder, name, NULL); if (obex_remove(ftp->os, path) < 0) ret = -errno; g_free(path); return ret; } int ftp_chkput(struct obex_session *os, void *user_data) { struct ftp_session *ftp = user_data; const char *name = obex_get_name(os); char *path; int ret; DBG("%p name %s", ftp, name); if (name == NULL) return -EBADR; if (!is_filename(name)) return -EBADR; if (obex_get_size(os) == OBJECT_SIZE_DELETE) return 0; path = g_build_filename(ftp->folder, name, NULL); ret = obex_put_stream_start(os, path); if (ret == 0 && obex_get_size(os) != OBJECT_SIZE_DELETE && obex_get_size(os) != OBJECT_SIZE_UNKNOWN) { manager_emit_transfer_property(ftp->transfer, "Size"); } if (ret == 0) manager_emit_transfer_started(ftp->transfer); g_free(path); return ret; } int ftp_put(struct obex_session *os, void *user_data) { struct ftp_session *ftp = user_data; const char *name = obex_get_name(os); ssize_t size = obex_get_size(os); DBG("%p name %s size %zd", ftp, name, size); if (ftp->folder == NULL) return -EPERM; if (name == NULL) return -EBADR; if (!is_filename(name)) return -EBADR; if (size == OBJECT_SIZE_DELETE) return ftp_delete(ftp, name); return 0; } int ftp_setpath(struct obex_session *os, void *user_data) { struct ftp_session *ftp = user_data; const char *root_folder, *name; const uint8_t *nonhdr; char *fullname; struct stat dstat; gboolean root; int err; if (obex_get_non_header_data(os, &nonhdr) != 2) { error("Set path failed: flag and constants not found!"); return -EBADMSG; } name = obex_get_name(os); root_folder = obex_option_root_folder(); root = g_str_equal(root_folder, ftp->folder); DBG("%p name %s", ftp, name); /* Check flag "Backup" */ if ((nonhdr[0] & 0x01) == 0x01) { DBG("Set to parent path"); if (root) return -EPERM; fullname = g_path_get_dirname(ftp->folder); set_folder(ftp, fullname); g_free(fullname); DBG("Set to parent path: %s", ftp->folder); return 0; } if (!name) { DBG("Set path failed: name missing!"); return -EINVAL; } if (strlen(name) == 0) { DBG("Set to root"); set_folder(ftp, root_folder); return 0; } /* Check and set to name path */ if (!is_filename(name)) { error("Set path failed: name incorrect!"); return -EPERM; } fullname = g_build_filename(ftp->folder, name, NULL); DBG("Fullname: %s", fullname); err = verify_path(fullname); if (err == -ENOENT) goto not_found; if (err < 0) goto done; err = stat(fullname, &dstat); if (err < 0) { err = -errno; if (err == -ENOENT) goto not_found; DBG("stat: %s(%d)", strerror(-err), -err); goto done; } if (S_ISDIR(dstat.st_mode) && (dstat.st_mode & 0400) && (dstat.st_mode & 0100)) { set_folder(ftp, fullname); goto done; } err = -EPERM; goto done; not_found: if (nonhdr[0] != 0) { err = -ENOENT; goto done; } if (mkdir(fullname, 0755) < 0) { err = -errno; DBG("mkdir: %s(%d)", strerror(-err), -err); goto done; } err = 0; set_folder(ftp, fullname); done: g_free(fullname); return err; } static gboolean is_valid_path(const char *path) { char **elements, **cur; int depth = 0; elements = g_strsplit(path, "/", 0); for (cur = elements; *cur != NULL; cur++) { if (**cur == '\0' || strcmp(*cur, ".") == 0) continue; if (strcmp(*cur, "..") == 0) { depth--; if (depth < 0) break; continue; } depth++; } g_strfreev(elements); if (depth < 0) return FALSE; return TRUE; } static char *ftp_build_filename(struct ftp_session *ftp, const char *destname) { char *filename; /* DestName can either be relative or absolute (FTP style) */ if (destname[0] == '/') filename = g_build_filename(obex_option_root_folder(), destname, NULL); else filename = g_build_filename(ftp->folder, destname, NULL); if (is_valid_path(filename + strlen(obex_option_root_folder()))) return filename; g_free(filename); return NULL; } static int ftp_copy(struct ftp_session *ftp, const char *name, const char *destname) { char *source, *destination, *destdir; int ret; DBG("%p name %s destination %s", ftp, name, destname); if (ftp->folder == NULL) { error("No folder set"); return -ENOENT; } if (name == NULL || destname == NULL) return -EINVAL; destination = ftp_build_filename(ftp, destname); if (destination == NULL) return -EBADR; destdir = g_path_get_dirname(destination); ret = verify_path(destdir); g_free(destdir); if (ret < 0) { g_free(destination); return ret; } source = g_build_filename(ftp->folder, name, NULL); ret = obex_copy(ftp->os, source, destination); g_free(source); g_free(destination); return ret; } static int ftp_move(struct ftp_session *ftp, const char *name, const char *destname) { char *source, *destination, *destdir; int ret; DBG("%p name %s destname %s", ftp, name, destname); if (ftp->folder == NULL) { error("No folder set"); return -ENOENT; } if (name == NULL || destname == NULL) return -EINVAL; destination = ftp_build_filename(ftp, destname); if (destination == NULL) return -EBADR; destdir = g_path_get_dirname(destination); ret = verify_path(destdir); g_free(destdir); if (ret < 0) { g_free(destination); return ret; } source = g_build_filename(ftp->folder, name, NULL); ret = obex_move(ftp->os, source, destination); g_free(source); g_free(destination); return ret; } int ftp_action(struct obex_session *os, void *user_data) { struct ftp_session *ftp = user_data; const char *name, *destname; uint8_t action_id; name = obex_get_name(os); if (name == NULL || !is_filename(name)) return -EBADR; destname = obex_get_destname(os); action_id = obex_get_action_id(os); DBG("%p action 0x%x", ftp, action_id); switch (action_id) { case 0x00: /* Copy Object */ return ftp_copy(ftp, name, destname); case 0x01: /* Move/Rename Object */ return ftp_move(ftp, name, destname); default: return -ENOSYS; } } void ftp_disconnect(struct obex_session *os, void *user_data) { struct ftp_session *ftp = user_data; DBG("%p", ftp); manager_unregister_session(os); manager_unregister_transfer(ftp->transfer); g_free(ftp->folder); g_free(ftp); } static void ftp_progress(struct obex_session *os, void *user_data) { struct ftp_session *ftp = user_data; manager_emit_transfer_progress(ftp->transfer); } static void ftp_reset(struct obex_session *os, void *user_data) { struct ftp_session *ftp = user_data; manager_emit_transfer_completed(ftp->transfer); } static const struct obex_service_driver ftp = { .name = "File Transfer server", .service = OBEX_FTP, .target = FTP_TARGET, .target_size = TARGET_SIZE, .connect = ftp_connect, .progress = ftp_progress, .get = ftp_get, .put = ftp_put, .chkput = ftp_chkput, .setpath = ftp_setpath, .action = ftp_action, .disconnect = ftp_disconnect, .reset = ftp_reset }; static int ftp_init(void) { return obex_service_driver_register(&ftp); } static void ftp_exit(void) { obex_service_driver_unregister(&ftp); } OBEX_PLUGIN_DEFINE(ftp, ftp_init, ftp_exit) bluez-5.82/obexd/plugins/PaxHeaders/filesystem.h0000644000000000000000000000005014015011623016756 xustar0020 atime=1743516713 20 ctime=1743591283 bluez-5.82/obexd/plugins/filesystem.h0000644000000000000000000000043614015011623016442 0ustar00rootroot/* SPDX-License-Identifier: GPL-2.0-or-later */ /* * * OBEX Server * * Copyright (C) 2007-2010 Marcel Holtmann * * */ ssize_t string_read(void *object, void *buf, size_t count); gboolean is_filename(const char *name); int verify_path(const char *path); bluez-5.82/obexd/plugins/PaxHeaders/pbap.c0000644000000000000000000000005014766002272015524 xustar0020 atime=1743515578 20 ctime=1743591283 bluez-5.82/obexd/plugins/pbap.c0000644000000000000000000005173714766002272015222 0ustar00rootroot// SPDX-License-Identifier: GPL-2.0-or-later /* * * OBEX Server * * Copyright (C) 2009-2010 Intel Corporation * Copyright (C) 2007-2010 Marcel Holtmann * * */ #ifdef HAVE_CONFIG_H #include #endif #include #include #include #include #include #include #include #include #include #include #include #include "gobex/gobex.h" #include "gobex/gobex-apparam.h" #include "obexd/src/obexd.h" #include "obexd/src/plugin.h" #include "obexd/src/log.h" #include "obexd/src/obex.h" #include "obexd/src/service.h" #include "obexd/src/manager.h" #include "obexd/src/mimetype.h" #include "phonebook.h" #include "filesystem.h" #define PHONEBOOK_TYPE "x-bt/phonebook" #define VCARDLISTING_TYPE "x-bt/vcard-listing" #define VCARDENTRY_TYPE "x-bt/vcard" #define ORDER_TAG 0x01 #define SEARCHVALUE_TAG 0x02 #define SEARCHATTRIB_TAG 0x03 #define MAXLISTCOUNT_TAG 0x04 #define LISTSTARTOFFSET_TAG 0x05 #define FILTER_TAG 0x06 #define FORMAT_TAG 0X07 #define PHONEBOOKSIZE_TAG 0X08 #define NEWMISSEDCALLS_TAG 0X09 struct cache { gboolean valid; uint32_t index; GSList *entries; }; struct cache_entry { uint32_t handle; char *id; char *name; char *sound; char *tel; }; struct pbap_session { struct apparam_field *params; char *folder; uint32_t find_handle; struct cache cache; struct pbap_object *obj; }; struct pbap_object { GString *buffer; GObexApparam *apparam; gboolean firstpacket; gboolean lastpart; struct pbap_session *session; void *request; }; static const uint8_t PBAP_TARGET[TARGET_SIZE] = { 0x79, 0x61, 0x35, 0xF0, 0xF0, 0xC5, 0x11, 0xD8, 0x09, 0x66, 0x08, 0x00, 0x20, 0x0C, 0x9A, 0x66 }; typedef int (*cache_entry_find_f) (const struct cache_entry *entry, const char *value); static void cache_entry_free(void *data) { struct cache_entry *entry = data; g_free(entry->id); g_free(entry->name); g_free(entry->sound); g_free(entry->tel); g_free(entry); } static gboolean entry_name_find(const struct cache_entry *entry, const char *value) { char *name; gboolean ret; if (!entry->name) return FALSE; if (strlen(value) == 0) return TRUE; name = g_utf8_strdown(entry->name, -1); ret = (g_strstr_len(name, -1, value) ? TRUE : FALSE); g_free(name); return ret; } static gboolean entry_sound_find(const struct cache_entry *entry, const char *value) { if (!entry->sound) return FALSE; return (g_strstr_len(entry->sound, -1, value) ? TRUE : FALSE); } static gboolean entry_tel_find(const struct cache_entry *entry, const char *value) { if (!entry->tel) return FALSE; return (g_strstr_len(entry->tel, -1, value) ? TRUE : FALSE); } static const char *cache_find(struct cache *cache, uint32_t handle) { GSList *l; for (l = cache->entries; l; l = l->next) { struct cache_entry *entry = l->data; if (entry->handle == handle) return entry->id; } return NULL; } static void cache_clear(struct cache *cache) { g_slist_free_full(cache->entries, cache_entry_free); cache->entries = NULL; } static void phonebook_size_result(const char *buffer, size_t bufsize, int vcards, int missed, gboolean lastpart, void *user_data) { struct pbap_session *pbap = user_data; uint16_t phonebooksize; if (pbap->obj->request) { phonebook_req_finalize(pbap->obj->request); pbap->obj->request = NULL; } if (vcards < 0) vcards = 0; DBG("vcards %d", vcards); phonebooksize = vcards; pbap->obj->apparam = g_obex_apparam_set_uint16(NULL, PHONEBOOKSIZE_TAG, phonebooksize); pbap->obj->firstpacket = TRUE; if (missed > 0) { DBG("missed %d", missed); } pbap->obj->apparam = g_obex_apparam_set_uint16(pbap->obj->apparam, NEWMISSEDCALLS_TAG, missed); obex_object_set_io_flags(pbap->obj, G_IO_IN, 0); } static void query_result(const char *buffer, size_t bufsize, int vcards, int missed, gboolean lastpart, void *user_data) { struct pbap_session *pbap = user_data; DBG(""); if (pbap->obj->request && lastpart) { phonebook_req_finalize(pbap->obj->request); pbap->obj->request = NULL; } pbap->obj->lastpart = lastpart; if (vcards < 0) { obex_object_set_io_flags(pbap->obj, G_IO_ERR, -ENOENT); return; } if (!pbap->obj->buffer) pbap->obj->buffer = g_string_new_len(buffer, bufsize); else pbap->obj->buffer = g_string_append_len(pbap->obj->buffer, buffer, bufsize); if (missed > 0) { DBG("missed %d", missed); pbap->obj->firstpacket = TRUE; } pbap->obj->apparam = g_obex_apparam_set_uint16(pbap->obj->apparam, NEWMISSEDCALLS_TAG, missed); obex_object_set_io_flags(pbap->obj, G_IO_IN, 0); } static void cache_entry_notify(const char *id, uint32_t handle, const char *name, const char *sound, const char *tel, void *user_data) { struct pbap_session *pbap = user_data; struct cache_entry *entry = g_new0(struct cache_entry, 1); struct cache *cache = &pbap->cache; if (handle != PHONEBOOK_INVALID_HANDLE) entry->handle = handle; else entry->handle = ++pbap->cache.index; entry->id = g_strdup(id); entry->name = g_strdup(name); entry->sound = g_strdup(sound); entry->tel = g_strdup(tel); cache->entries = g_slist_append(cache->entries, entry); } static int alpha_sort(gconstpointer a, gconstpointer b) { const struct cache_entry *e1 = a; const struct cache_entry *e2 = b; return g_strcmp0(e1->name, e2->name); } static int indexed_sort(gconstpointer a, gconstpointer b) { const struct cache_entry *e1 = a; const struct cache_entry *e2 = b; return (e1->handle - e2->handle); } static int phonetical_sort(gconstpointer a, gconstpointer b) { const struct cache_entry *e1 = a; const struct cache_entry *e2 = b; /* SOUND attribute is optional. Use Indexed sort if not present. */ if (!e1->sound || !e2->sound) return indexed_sort(a, b); return g_strcmp0(e1->sound, e2->sound); } static GSList *sort_entries(GSList *l, uint8_t order, uint8_t search_attrib, const char *value) { GSList *sorted = NULL; cache_entry_find_f find; GCompareFunc sort; char *searchval; /* * Default sorter is "Indexed". Some backends doesn't inform the index, * for this case a sequential internal index is assigned. * 0x00 = indexed * 0x01 = alphanumeric * 0x02 = phonetic */ switch (order) { case 0x01: sort = alpha_sort; break; case 0x02: sort = phonetical_sort; break; default: sort = indexed_sort; break; } /* * This implementation checks if the given field CONTAINS the * search value(case insensitive). Name is the default field * when the attribute is not provided. */ switch (search_attrib) { /* Number */ case 1: find = entry_tel_find; break; /* Sound */ case 2: find = entry_sound_find; break; default: find = entry_name_find; break; } searchval = value ? g_utf8_strdown(value, -1) : NULL; for (; l; l = l->next) { struct cache_entry *entry = l->data; if (searchval && !find(entry, (const char *) searchval)) continue; sorted = g_slist_insert_sorted(sorted, entry, sort); } g_free(searchval); return sorted; } static int generate_response(void *user_data) { struct pbap_session *pbap = user_data; GSList *sorted; GSList *l; uint16_t max = pbap->params->maxlistcount; DBG(""); if (max == 0) { /* Ignore all other parameter and return PhoneBookSize */ uint16_t size = g_slist_length(pbap->cache.entries); pbap->obj->firstpacket = TRUE; pbap->obj->apparam = g_obex_apparam_set_uint16( pbap->obj->apparam, PHONEBOOKSIZE_TAG, size); return 0; } /* * Don't free the sorted list content: this list contains * only the reference for the "real" cache entry. */ sorted = sort_entries(pbap->cache.entries, pbap->params->order, pbap->params->searchattrib, (const char *) pbap->params->searchval); /* Computing offset considering first entry of the phonebook */ l = g_slist_nth(sorted, pbap->params->liststartoffset); pbap->obj->buffer = g_string_new(VCARD_LISTING_BEGIN); for (; l && max; l = l->next, max--) { const struct cache_entry *entry = l->data; char *escaped_name = g_markup_escape_text(entry->name, -1); g_string_append_printf(pbap->obj->buffer, VCARD_LISTING_ELEMENT, entry->handle, escaped_name); g_free(escaped_name); } pbap->obj->buffer = g_string_append(pbap->obj->buffer, VCARD_LISTING_END); g_slist_free(sorted); return 0; } static void cache_ready_notify(void *user_data) { struct pbap_session *pbap = user_data; DBG(""); phonebook_req_finalize(pbap->obj->request); pbap->obj->request = NULL; pbap->cache.valid = TRUE; generate_response(pbap); obex_object_set_io_flags(pbap->obj, G_IO_IN, 0); } static void cache_entry_done(void *user_data) { struct pbap_session *pbap = user_data; const char *id; int ret; DBG(""); pbap->cache.valid = TRUE; id = cache_find(&pbap->cache, pbap->find_handle); if (id == NULL) { DBG("Entry %d not found on cache", pbap->find_handle); obex_object_set_io_flags(pbap->obj, G_IO_ERR, -ENOENT); return; } phonebook_req_finalize(pbap->obj->request); pbap->obj->request = phonebook_get_entry(pbap->folder, id, pbap->params, query_result, pbap, &ret); if (ret < 0) obex_object_set_io_flags(pbap->obj, G_IO_ERR, ret); } static struct apparam_field *parse_aparam(const uint8_t *buffer, uint32_t hlen) { GObexApparam *apparam; struct apparam_field *param; param = g_new0(struct apparam_field, 1); /* * As per spec when client doesn't include MAXLISTCOUNT_TAG then it * should be assume as Maximum value in vcardlisting 65535 */ param->maxlistcount = UINT16_MAX; apparam = g_obex_apparam_decode(buffer, hlen); if (apparam) { g_obex_apparam_get_uint8(apparam, ORDER_TAG, ¶m->order); g_obex_apparam_get_uint8(apparam, SEARCHATTRIB_TAG, ¶m->searchattrib); g_obex_apparam_get_uint8(apparam, FORMAT_TAG, ¶m->format); g_obex_apparam_get_uint16(apparam, MAXLISTCOUNT_TAG, ¶m->maxlistcount); g_obex_apparam_get_uint16(apparam, LISTSTARTOFFSET_TAG, ¶m->liststartoffset); g_obex_apparam_get_uint64(apparam, FILTER_TAG, ¶m->filter); param->searchval = g_obex_apparam_get_string(apparam, SEARCHVALUE_TAG); g_obex_apparam_free(apparam); } DBG("o %x sa %x sv %s fil %" G_GINT64_MODIFIER "x for %x max %x off %x", param->order, param->searchattrib, param->searchval, param->filter, param->format, param->maxlistcount, param->liststartoffset); return param; } static void *pbap_connect(struct obex_session *os, int *err) { struct pbap_session *pbap; manager_register_session(os); pbap = g_new0(struct pbap_session, 1); pbap->folder = g_strdup("/"); pbap->find_handle = PHONEBOOK_INVALID_HANDLE; if (err) *err = 0; return pbap; } static int pbap_get(struct obex_session *os, void *user_data) { struct pbap_session *pbap = user_data; const char *type = obex_get_type(os); const char *name = obex_get_name(os); struct apparam_field *params; const uint8_t *buffer; char *path; ssize_t rsize; int ret; DBG("name %s type %s pbap %p", name, type, pbap); if (type == NULL) return -EBADR; rsize = obex_get_apparam(os, &buffer); if (rsize < 0) { if (g_ascii_strcasecmp(type, VCARDENTRY_TYPE) != 0) return -EBADR; rsize = 0; } /* Workaround for PTS client not sending mandatory apparams */ if (!rsize && g_ascii_strcasecmp(type, VCARDLISTING_TYPE) == 0) { static const uint8_t default_apparams[] = { 0x04, 0x02, 0xff, 0xff }; buffer = default_apparams; rsize = sizeof(default_apparams); } else if (!rsize && g_ascii_strcasecmp(type, VCARDENTRY_TYPE) == 0) { static const uint8_t default_apparams[] = { 0x07, 0x01, 0x00 }; buffer = default_apparams; rsize = sizeof(default_apparams); } params = parse_aparam(buffer, rsize); if (params == NULL) return -EBADR; if (pbap->params) { g_free(pbap->params->searchval); g_free(pbap->params); } pbap->params = params; if (g_ascii_strcasecmp(type, PHONEBOOK_TYPE) == 0) { /* Always contains the absolute path */ if (g_path_is_absolute(name)) path = g_strdup(name); else path = g_build_filename("/", name, NULL); } else if (g_ascii_strcasecmp(type, VCARDLISTING_TYPE) == 0) { /* Always relative */ if (!name || strlen(name) == 0) { /* Current folder */ path = g_strdup(pbap->folder); } else { /* Current folder + relative path */ path = g_build_filename(pbap->folder, name, NULL); /* clear cache */ pbap->cache.valid = FALSE; pbap->cache.index = 0; cache_clear(&pbap->cache); } } else if (g_ascii_strcasecmp(type, VCARDENTRY_TYPE) == 0) { /* File name only */ path = g_strdup(name); } else return -EBADR; if (path == NULL) return -EBADR; ret = obex_get_stream_start(os, path); g_free(path); return ret; } static int pbap_setpath(struct obex_session *os, void *user_data) { struct pbap_session *pbap = user_data; const char *name; const uint8_t *nonhdr; char *fullname; int err; if (obex_get_non_header_data(os, &nonhdr) != 2) { error("Set path failed: flag and constants not found!"); return -EBADMSG; } name = obex_get_name(os); DBG("name %s folder %s nonhdr 0x%x%x", name, pbap->folder, nonhdr[0], nonhdr[1]); fullname = phonebook_set_folder(pbap->folder, name, nonhdr[0], &err); if (err < 0) return err; g_free(pbap->folder); pbap->folder = fullname; /* * FIXME: Define a criteria to mark the cache as invalid */ pbap->cache.valid = FALSE; pbap->cache.index = 0; cache_clear(&pbap->cache); return 0; } static void pbap_disconnect(struct obex_session *os, void *user_data) { struct pbap_session *pbap = user_data; manager_unregister_session(os); if (pbap->obj) pbap->obj->session = NULL; if (pbap->params) { g_free(pbap->params->searchval); g_free(pbap->params); } cache_clear(&pbap->cache); g_free(pbap->folder); g_free(pbap); } static int pbap_chkput(struct obex_session *os, void *user_data) { /* Rejects all PUTs */ return -EBADR; } static const struct obex_service_driver pbap = { .name = "Phonebook Access server", .service = OBEX_PBAP, .target = PBAP_TARGET, .target_size = TARGET_SIZE, .connect = pbap_connect, .get = pbap_get, .setpath = pbap_setpath, .disconnect = pbap_disconnect, .chkput = pbap_chkput }; static struct pbap_object *vobject_create(struct pbap_session *pbap, void *request) { struct pbap_object *obj; obj = g_new0(struct pbap_object, 1); obj->session = pbap; pbap->obj = obj; obj->request = request; return obj; } static void *vobject_pull_open(const char *name, int oflag, mode_t mode, void *context, size_t *size, int *err) { struct pbap_session *pbap = context; phonebook_cb cb; int ret; void *request; DBG("name %s context %p maxlistcount %d", name, context, pbap->params->maxlistcount); if (oflag != O_RDONLY) { ret = -EPERM; goto fail; } if (name == NULL) { ret = -EBADR; goto fail; } if (pbap->params->maxlistcount == 0) cb = phonebook_size_result; else cb = query_result; request = phonebook_pull(name, pbap->params, cb, pbap, &ret); if (ret < 0) goto fail; /* reading first part of results from backend */ ret = phonebook_pull_read(request); if (ret < 0) goto fail; if (err) *err = 0; return vobject_create(pbap, request); fail: if (err) *err = ret; return NULL; } static int vobject_close(void *object) { struct pbap_object *obj = object; DBG(""); if (obj->session) obj->session->obj = NULL; if (obj->buffer) g_string_free(obj->buffer, TRUE); if (obj->apparam) g_obex_apparam_free(obj->apparam); if (obj->request) phonebook_req_finalize(obj->request); g_free(obj); return 0; } static void *vobject_list_open(const char *name, int oflag, mode_t mode, void *context, size_t *size, int *err) { struct pbap_session *pbap = context; struct pbap_object *obj = NULL; int ret; void *request; if (name == NULL) { ret = -EBADR; goto fail; } DBG("name %s context %p valid %d", name, context, pbap->cache.valid); if (oflag != O_RDONLY) { ret = -EPERM; goto fail; } /* PullvCardListing always get the contacts from the cache */ if (pbap->cache.valid) { obj = vobject_create(pbap, NULL); ret = generate_response(pbap); } else { request = phonebook_create_cache(name, cache_entry_notify, cache_ready_notify, pbap, &ret); if (ret == 0) obj = vobject_create(pbap, request); } if (ret < 0) goto fail; if (err) *err = 0; return obj; fail: if (obj) vobject_close(obj); if (err) *err = ret; return NULL; } static void *vobject_vcard_open(const char *name, int oflag, mode_t mode, void *context, size_t *size, int *err) { struct pbap_session *pbap = context; const char *id; uint32_t handle; int ret; void *request; DBG("name %s context %p valid %d", name, context, pbap->cache.valid); if (oflag != O_RDONLY) { ret = -EPERM; goto fail; } if (name == NULL || sscanf(name, "%u.vcf", &handle) != 1) { ret = -EBADR; goto fail; } if (pbap->cache.valid == FALSE) { pbap->find_handle = handle; request = phonebook_create_cache(pbap->folder, cache_entry_notify, cache_entry_done, pbap, &ret); goto done; } id = cache_find(&pbap->cache, handle); if (!id) { ret = -ENOENT; goto fail; } request = phonebook_get_entry(pbap->folder, id, pbap->params, query_result, pbap, &ret); done: if (ret < 0) goto fail; if (err) *err = 0; return vobject_create(pbap, request); fail: if (err) *err = ret; return NULL; } static ssize_t vobject_pull_get_next_header(void *object, void *buf, size_t mtu, uint8_t *hi) { struct pbap_object *obj = object; if (!obj->buffer && !obj->apparam) return -EAGAIN; *hi = G_OBEX_HDR_APPARAM; if (obj->firstpacket) { obj->firstpacket = FALSE; return g_obex_apparam_encode(obj->apparam, buf, mtu); } return 0; } static ssize_t vobject_pull_read(void *object, void *buf, size_t count) { struct pbap_object *obj = object; struct pbap_session *pbap = obj->session; int len, ret; DBG("buffer %p maxlistcount %d", obj->buffer, pbap->params->maxlistcount); if (!obj->buffer) { if (pbap->params->maxlistcount == 0) return -ENOSTR; return -EAGAIN; } len = string_read(obj->buffer, buf, count); if (len == 0 && !obj->lastpart) { /* in case when buffer is empty and we know that more * data is still available in backend, requesting new * data part via phonebook_pull_read and returning * -EAGAIN to suspend request for now */ ret = phonebook_pull_read(obj->request); if (ret) return -EPERM; return -EAGAIN; } return len; } static ssize_t vobject_list_get_next_header(void *object, void *buf, size_t mtu, uint8_t *hi) { struct pbap_object *obj = object; struct pbap_session *pbap = obj->session; /* Backend still busy reading contacts */ if (!pbap->cache.valid) return -EAGAIN; *hi = G_OBEX_HDR_APPARAM; if (obj->firstpacket) { obj->firstpacket = FALSE; return g_obex_apparam_encode(obj->apparam, buf, mtu); } return 0; } static ssize_t vobject_list_read(void *object, void *buf, size_t count) { struct pbap_object *obj = object; struct pbap_session *pbap = obj->session; DBG("valid %d maxlistcount %d", pbap->cache.valid, pbap->params->maxlistcount); if (pbap->params->maxlistcount == 0) return -ENOSTR; return string_read(obj->buffer, buf, count); } static ssize_t vobject_vcard_read(void *object, void *buf, size_t count) { struct pbap_object *obj = object; DBG("buffer %p", obj->buffer); if (!obj->buffer) return -EAGAIN; return string_read(obj->buffer, buf, count); } static const struct obex_mime_type_driver mime_pull = { .target = PBAP_TARGET, .target_size = TARGET_SIZE, .mimetype = "x-bt/phonebook", .open = vobject_pull_open, .close = vobject_close, .read = vobject_pull_read, .get_next_header = vobject_pull_get_next_header, }; static const struct obex_mime_type_driver mime_list = { .target = PBAP_TARGET, .target_size = TARGET_SIZE, .mimetype = "x-bt/vcard-listing", .open = vobject_list_open, .close = vobject_close, .read = vobject_list_read, .get_next_header = vobject_list_get_next_header, }; static const struct obex_mime_type_driver mime_vcard = { .target = PBAP_TARGET, .target_size = TARGET_SIZE, .mimetype = "x-bt/vcard", .open = vobject_vcard_open, .close = vobject_close, .read = vobject_vcard_read, }; static int pbap_init(void) { int err; err = phonebook_init(); if (err < 0) return err; err = obex_mime_type_driver_register(&mime_pull); if (err < 0) goto fail_mime_pull; err = obex_mime_type_driver_register(&mime_list); if (err < 0) goto fail_mime_list; err = obex_mime_type_driver_register(&mime_vcard); if (err < 0) goto fail_mime_vcard; err = obex_service_driver_register(&pbap); if (err < 0) goto fail_pbap_reg; return 0; fail_pbap_reg: obex_mime_type_driver_unregister(&mime_vcard); fail_mime_vcard: obex_mime_type_driver_unregister(&mime_list); fail_mime_list: obex_mime_type_driver_unregister(&mime_pull); fail_mime_pull: phonebook_exit(); return err; } static void pbap_exit(void) { obex_service_driver_unregister(&pbap); obex_mime_type_driver_unregister(&mime_pull); obex_mime_type_driver_unregister(&mime_list); obex_mime_type_driver_unregister(&mime_vcard); phonebook_exit(); } OBEX_PLUGIN_DEFINE(pbap, pbap_init, pbap_exit) bluez-5.82/obexd/plugins/PaxHeaders/phonebook-ebook.c0000644000000000000000000000005014766002272017663 xustar0020 atime=1743515578 20 ctime=1743591288 bluez-5.82/obexd/plugins/phonebook-ebook.c0000644000000000000000000003451114766002272017350 0ustar00rootroot// SPDX-License-Identifier: GPL-2.0-or-later /* * * OBEX Server * * Copyright (C) 2009-2021 Intel Corporation * Copyright (C) 2007-2021 Marcel Holtmann * Copyright (C) 2021 Dylan Van Assche * * */ #ifdef HAVE_CONFIG_H #include #endif #define _GNU_SOURCE #include #include #include #include #include #include #include #include #include #include #include #include #include #include "obexd/src/log.h" #include "phonebook.h" #define CONNECTION_TIMEOUT 30 // seconds #define PB_FORMAT_VCARD21 0 #define PB_FORMAT_VCARD30 1 #define PB_FORMAT_NONE 2 ESourceRegistry *registry; ESource *address_book; EBookClient *book_client; struct query_context { const struct apparam_field *params; phonebook_cb contacts_cb; phonebook_entry_cb entry_cb; phonebook_cache_ready_cb ready_cb; gchar *query; unsigned int count; GString *buf; char *uid; unsigned queued_calls; void *user_data; gboolean canceled; }; static const char *attribute_mask[] = { /* 0 */ "VERSION", "FN", "N", "PHOTO", "BDAY", "ADR", "LABEL", "TEL", /* 8 */ "EMAIL", "MAILER", "TZ", "GEO", "TITLE", "ROLE", "LOGO", "AGENT", /* 16 */ "ORG", "NOTE", "REV", "SOUND", "URL", "UID", "KEY", "NICKNAME", /* 24 */ "CATEGORIES", "PROID", "CLASS", "SORT-STRING", /* 28 */ "X-IRMC-CALL-DATETIME", NULL }; static void free_query_context(struct query_context *data) { g_free(data->uid); if (data->buf != NULL) g_string_free(data->buf, TRUE); if (data->query != NULL) g_free(data->query); g_free(data); } static char *evcard_to_string(EVCard *evcard, unsigned int format, uint64_t filter) { EVCard *evcard2; GList *l; char *vcard; if (!filter) return e_vcard_to_string(evcard, format); /* * Mandatory attributes for vCard 2.1 are VERSION ,N and TEL. * Mandatory attributes for vCard 3.0 are VERSION, N, FN and TEL */ filter = format == EVC_FORMAT_VCARD_30 ? filter | 0x87: filter | 0x85; l = e_vcard_get_attributes(evcard); evcard2 = e_vcard_new(); for (; l; l = g_list_next(l)) { EVCardAttribute *attrib = l->data; const char *name; int i; if (!attrib) continue; name = e_vcard_attribute_get_name(attrib); for (i = 0; attribute_mask[i] != NULL; i++) { if (!(filter & (1 << i))) continue; if (g_strcmp0(name, attribute_mask[i]) != 0) continue; e_vcard_add_attribute(evcard2, e_vcard_attribute_copy(attrib)); } } vcard = e_vcard_to_string(evcard2, format); g_object_unref(evcard2); return vcard; } char *phonebook_set_folder(const char *current_folder, const char *new_folder, uint8_t flags, int *err) { gboolean root, child; char *fullname = NULL, *tmp1, *tmp2, *base; int ret = 0, len; root = (g_strcmp0("/", current_folder) == 0); child = (new_folder && strlen(new_folder) != 0); /* Evolution back-end will support /telecom/pb folder only */ switch (flags) { case 0x02: /* Go back to root */ if (!child) { fullname = g_strdup("/"); goto done; } /* Go down 1 level */ fullname = g_build_filename(current_folder, new_folder, NULL); if (strcmp(PB_TELECOM_FOLDER, fullname) != 0 && strcmp(PB_CONTACTS_FOLDER, fullname) != 0) { g_free(fullname); fullname = NULL; ret = -ENOENT; } break; case 0x03: /* Go up 1 level */ if (root) { /* Already root */ ret = -EBADR; goto done; } /* * Removing one level of the current folder. Current folder * contains AT LEAST one level since it is not at root folder. * Use glib utility functions to handle invalid chars in the * folder path properly. */ tmp1 = g_path_get_basename(current_folder); tmp2 = g_strrstr(current_folder, tmp1); len = tmp2 - (current_folder + 1); g_free(tmp1); if (len == 0) base = g_strdup("/"); else base = g_strndup(current_folder, len); /* Return one level only */ if (!child) { fullname = base; goto done; } fullname = g_build_filename(base, new_folder, NULL); if (strcmp(fullname, PB_TELECOM_FOLDER) != 0 && strcmp(fullname, PB_CONTACTS_FOLDER) != 0) { g_free(fullname); fullname = NULL; ret = -ENOENT; } g_free(base); break; default: ret = -EBADR; break; } done: if (err) *err = ret; return fullname; } void phonebook_req_finalize(void *request) { /* Free resources after pull request */ struct query_context *data = request; if (data->queued_calls == 0) free_query_context(data); else data->canceled = TRUE; } void *phonebook_pull(const char *name, const struct apparam_field *params, phonebook_cb cb, void *user_data, int *err) { struct query_context *data; EBookQuery *query; /* Request should be for '/telecom/pb.vcf', reject others */ if (g_strcmp0(PB_CONTACTS, name) != 0) { if (err) *err = -ENOENT; return NULL; } data = g_new0(struct query_context, 1); data->contacts_cb = cb; data->params = params; data->user_data = user_data; data->buf = g_string_new(""); query = e_book_query_any_field_contains(""); // all contacts data->query = e_book_query_to_string(query); e_book_query_unref(query); return data; } static void phonebook_pull_read_ready(GObject *source_object, GAsyncResult *result, gpointer user_data) { struct query_context *data = user_data; GSList *l = NULL; GSList *contacts = NULL; GError *gerr = NULL; unsigned int count, maxcount; /* Finish async call to retrieve contacts */ data->queued_calls--; if (data->canceled) goto canceled; e_book_client_get_contacts_finish(E_BOOK_CLIENT(source_object), result, &contacts, &gerr); if (gerr != NULL) { error("Failed to retrieve contacts, invalid query"); g_error_free(gerr); goto done; } /* * When MaxListCount is zero, PCE wants to know the number of used * indexes in the phonebook of interest. All other parameters that * may be present in the request shall be ignored. */ maxcount = data->params->maxlistcount; if (maxcount == 0) { data->count += g_slist_length(contacts); goto done; } /* * Convert each contact to a vCard and append the card to * the buffer string. */ l = g_slist_nth(contacts, data->params->liststartoffset); for (count = 0; l && count + data->count < maxcount; l = g_slist_next(l), count++) { EContact *contact = E_CONTACT(l->data); EVCard *evcard = E_VCARD(contact); char *vcard; if (data->params->format == PB_FORMAT_VCARD30) vcard = evcard_to_string(evcard, EVC_FORMAT_VCARD_30, data->params->filter); else if (data->params->format == PB_FORMAT_VCARD21) vcard = evcard_to_string(evcard, EVC_FORMAT_VCARD_21, data->params->filter); else error("unknown format: %d", data->params->format); data->buf = g_string_append(data->buf, vcard); data->buf = g_string_append(data->buf, "\r\n"); g_free(vcard); } DBG("collected %d contacts", count); data->count += count; g_slist_free_full(contacts, (GDestroyNotify) g_object_unref); done: if (data->queued_calls == 0) { GString *buf = data->buf; data->buf = NULL; data->contacts_cb(buf->str, buf->len, data->count, 0, TRUE, data->user_data); g_string_free(buf, TRUE); } return; canceled: if (data->queued_calls == 0) free_query_context(data); } int phonebook_pull_read(void *request) { struct query_context *data = request; GError *gerr = NULL; if (!data) { error("Request data is empty"); return -ENOENT; } DBG("retrieving all contacts"); /* Fetch async contacts from default address book */ e_book_client_get_contacts(book_client, data->query, NULL, (GAsyncReadyCallback) phonebook_pull_read_ready, data); data->queued_calls++; return 0; } static void phonebook_get_entry_ready(GObject *source_object, GAsyncResult *result, gpointer user_data) { GError *gerr = NULL; EContact *contact = NULL; struct query_context *data = user_data; EVCard *evcard; char *vcard; size_t len; data->queued_calls--; e_book_client_get_contact_finish(E_BOOK_CLIENT(source_object), result, &contact, &gerr); if (data->canceled) goto done; if (gerr != NULL) { error("Getting contact failed: %s", gerr->message); g_error_free(gerr); goto done; } evcard = E_VCARD(contact); if (data->params->format == PB_FORMAT_VCARD30) vcard = evcard_to_string(evcard, EVC_FORMAT_VCARD_30, data->params->filter); else if (data->params->format == PB_FORMAT_VCARD21) vcard = evcard_to_string(evcard, EVC_FORMAT_VCARD_21, data->params->filter); else error("Unknown vCard format: %d", data->params->format); len = vcard ? strlen(vcard) : 0; data->count++; data->contacts_cb(vcard, len, 1, 0, TRUE, data->user_data); g_free(vcard); DBG("retrieving entry successful"); done: if (data->queued_calls == 0) { if (data->count == 0) data->contacts_cb(NULL, 0, 1, 0, TRUE, data->user_data); else if (data->canceled) free_query_context(data); } g_object_unref(contact); } void *phonebook_get_entry(const char *folder, const char *id, const struct apparam_field *params, phonebook_cb cb, void *user_data, int *err) { struct query_context *data; GSList *l; DBG("retrieving entry: %s", id); data = g_new0(struct query_context, 1); data->contacts_cb = cb; data->params = params; data->user_data = user_data; data->uid = g_strdup(id); /* Fetch async contacts from default address book */ e_book_client_get_contact(book_client, data->uid, NULL, (GAsyncReadyCallback) phonebook_get_entry_ready, data); data->queued_calls++; if (err) *err = (data->queued_calls == 0 ? -ENOENT : 0); return data; } static char *evcard_name_attribute_to_string(EVCard *evcard) { EVCardAttribute *attrib; GList *l; GString *name = NULL; attrib = e_vcard_get_attribute(evcard, EVC_N); if (!attrib) return NULL; for (l = e_vcard_attribute_get_values(attrib); l; l = l->next) { const char *value = l->data; if (!strlen(value)) continue; if (!name) name = g_string_new(value); else { name = g_string_append(name, ";"); name = g_string_append(name, l->data); } } if (!name) return NULL; return g_string_free(name, FALSE); } static void phonebook_create_cache_ready(GObject *source_object, GAsyncResult *result, gpointer user_data) { struct query_context *data = user_data; GSList *l = NULL; GSList *contacts = NULL; GError *gerr = NULL; data->queued_calls--; if (data->canceled) goto canceled; e_book_client_get_contacts_finish(E_BOOK_CLIENT(source_object), result, &contacts, &gerr); if (gerr != NULL) { error("Getting contacts failed: %s", gerr->message); goto done; } for (l = contacts; l; l = g_slist_next(l)) { EContact *contact = E_CONTACT(l->data); EVCard *evcard = E_VCARD(contact); EVCardAttribute *attrib; char *uid, *tel, *name; name = evcard_name_attribute_to_string(evcard); if (!name) continue; attrib = e_vcard_get_attribute(evcard, EVC_UID); if (!attrib) continue; uid = e_vcard_attribute_get_value(attrib); if (!uid) continue; attrib = e_vcard_get_attribute(evcard, EVC_TEL); if (attrib) tel = e_vcard_attribute_get_value(attrib); else tel = g_strdup(""); data->entry_cb(uid, PHONEBOOK_INVALID_HANDLE, name, NULL, tel, data->user_data); g_free(name); g_free(uid); g_free(tel); } DBG("caching successful"); g_slist_free_full(contacts, (GDestroyNotify) g_object_unref); done: if (data->queued_calls == 0) data->ready_cb(data->user_data); return; canceled: if (data->queued_calls == 0) free_query_context(data); } void *phonebook_create_cache(const char *name, phonebook_entry_cb entry_cb, phonebook_cache_ready_cb ready_cb, void *user_data, int *err) { /* Build a cache of contacts */ struct query_context *data; EBookQuery *query; EContact *me; EBookClient *me_client; EVCard *evcard; GError *gerr = NULL; EVCardAttribute *attrib; char *uid, *tel, *cname; if (g_strcmp0(PB_CONTACTS_FOLDER, name) != 0) { if (err) *err = -ENOENT; return NULL; } DBG("creating cache"); data = g_new0(struct query_context, 1); data->entry_cb = entry_cb; data->ready_cb = ready_cb; data->user_data = user_data; query = e_book_query_any_field_contains(""); // all contacts data->query = e_book_query_to_string(query); e_book_query_unref(query); /* Myself as contact should always be 0.vcf if found in address book */ if (!e_book_client_get_self(registry, &me, &me_client, &gerr)) { DBG("owner is not in address book: %s", gerr->message); g_error_free(gerr); goto next; } DBG("caching address book owner"); evcard = E_VCARD(me); cname = evcard_name_attribute_to_string(evcard); if (!cname) cname = g_strdup(""); attrib = e_vcard_get_attribute(evcard, EVC_UID); uid = e_vcard_attribute_get_value(attrib); if (!uid) uid = g_strdup(""); attrib = e_vcard_get_attribute(evcard, EVC_TEL); if (attrib) tel = e_vcard_attribute_get_value(attrib); else tel = g_strdup(""); data->entry_cb(uid, 0, cname, NULL, tel, data->user_data); data->count++; g_free(cname); g_free(uid); g_free(tel); next: /* Fetch async contacts from default address book */ DBG("caching contacts"); e_book_client_get_contacts(book_client, data->query, NULL, (GAsyncReadyCallback) phonebook_create_cache_ready, data); data->queued_calls++; if (err) *err = (data->queued_calls == 0 ? -ENOENT : 0); return data; } int phonebook_init(void) { EClient *client; GError *gerr = NULL; /* Acquire ESource Registry */ registry = e_source_registry_new_sync(NULL, &gerr); if (gerr != NULL) { error("Unable to acquire registry: %s\n", gerr->message); g_error_free(gerr); return -1; } /* Get ref to default address book */ address_book = e_source_registry_ref_default_address_book(registry); if (address_book == NULL) { error("Unable to get reference to default address book"); return -2; } /* Allocate e-book client for address book */ gerr = NULL; client = e_book_client_connect_sync(address_book, CONNECTION_TIMEOUT, NULL, &gerr); if (gerr != NULL || client == NULL) { error("Cannot connect ebook client to EDS: %s", gerr != NULL ? gerr->message : "NULL"); g_error_free(gerr); return -3; } book_client = E_BOOK_CLIENT(client); DBG("created address book client"); return 0; } void phonebook_exit(void) { g_object_unref(book_client); g_object_unref(address_book); g_object_unref(registry); } bluez-5.82/obexd/plugins/PaxHeaders/irmc.c0000644000000000000000000000005014572354773015547 xustar0020 atime=1743516718 20 ctime=1743591283 bluez-5.82/obexd/plugins/irmc.c0000644000000000000000000002366714572354773015246 0ustar00rootroot// SPDX-License-Identifier: GPL-2.0-or-later /* * * OBEX IrMC Sync Server * * Copyright (C) 2010 Marcel Mol * */ #ifdef HAVE_CONFIG_H #include #endif #define _GNU_SOURCE #include #include #include #include #include #include #include #include #include #include #include #include "obexd/src/obexd.h" #include "obexd/src/plugin.h" #include "obexd/src/log.h" #include "obexd/src/obex.h" #include "obexd/src/service.h" #include "obexd/src/manager.h" #include "obexd/src/mimetype.h" #include "phonebook.h" #include "filesystem.h" struct aparam_header { uint8_t tag; uint8_t len; uint8_t val[0]; } __attribute__ ((packed)); #define DID_LEN 18 struct irmc_session { struct obex_session *os; struct apparam_field *params; uint16_t entries; GString *buffer; char sn[DID_LEN]; char did[DID_LEN]; char manu[DID_LEN]; char model[DID_LEN]; void *request; }; #define IRMC_TARGET_SIZE 9 static const guint8 IRMC_TARGET[IRMC_TARGET_SIZE] = { 0x49, 0x52, 0x4d, 0x43, 0x2d, 0x53, 0x59, 0x4e, 0x43 }; /* FIXME: * the IrMC specs state the first vcard should be the owner * vcard. As there is no simple way to collect ownerdetails * just create an empty vcard (which is allowed according to the * specs). */ static const char *owner_vcard = "BEGIN:VCARD\r\n" "VERSION:2.1\r\n" "N:\r\n" "TEL:\r\n" "X-IRMX-LUID:0\r\n" "END:VCARD\r\n"; static void phonebook_size_result(const char *buffer, size_t bufsize, int vcards, int missed, gboolean lastpart, void *user_data) { struct irmc_session *irmc = user_data; DBG("vcards %d", vcards); irmc->params->maxlistcount = vcards; if (irmc->request) { phonebook_req_finalize(irmc->request); irmc->request = NULL; } } static void query_result(const char *buffer, size_t bufsize, int vcards, int missed, gboolean lastpart, void *user_data) { struct irmc_session *irmc = user_data; const char *s, *t; DBG("bufsize %zu vcards %d missed %d", bufsize, vcards, missed); if (irmc->request) { phonebook_req_finalize(irmc->request); irmc->request = NULL; } /* first add a 'owner' vcard */ if (!irmc->buffer) irmc->buffer = g_string_new(owner_vcard); else irmc->buffer = g_string_append(irmc->buffer, owner_vcard); if (buffer == NULL) goto done; /* loop around buffer and add X-IRMC-LUID attribs */ s = buffer; while ((t = strstr(s, "UID:")) != NULL) { /* add up to UID: into buffer */ irmc->buffer = g_string_append_len(irmc->buffer, s, t-s); /* * add UID: line into buffer * Not sure if UID is still needed if X-IRMC-LUID is there */ s = t; t = strstr(s, "\r\n"); t += 2; irmc->buffer = g_string_append_len(irmc->buffer, s, t-s); /* add X-IRMC-LUID with same number as UID */ irmc->buffer = g_string_append_len(irmc->buffer, "X-IRMC-LUID:", 12); s += 4; /* point to uid number */ irmc->buffer = g_string_append_len(irmc->buffer, s, t-s); s = t; } /* add remaining bit of buffer */ irmc->buffer = g_string_append(irmc->buffer, s); done: obex_object_set_io_flags(irmc, G_IO_IN, 0); } static void *irmc_connect(struct obex_session *os, int *err) { struct irmc_session *irmc; struct apparam_field *param; int ret; DBG(""); manager_register_session(os); irmc = g_new0(struct irmc_session, 1); irmc->os = os; /* FIXME: * Ideally get capabilities info here and use that to define * IrMC DID and SN etc parameters. * For now lets used hostname and some 'random' value */ gethostname(irmc->did, DID_LEN); strncpy(irmc->sn, "12345", sizeof(irmc->sn) - 1); strncpy(irmc->manu, "obex", sizeof(irmc->manu) - 1); strncpy(irmc->model, "mymodel", sizeof(irmc->model) - 1); /* We need to know the number of contact/cal/nt entries * somewhere so why not do it now. */ param = g_new0(struct apparam_field, 1); param->maxlistcount = 0; /* to count the number of vcards... */ param->filter = 0x200085; /* UID TEL N VERSION */ irmc->params = param; irmc->request = phonebook_pull(PB_CONTACTS, irmc->params, phonebook_size_result, irmc, err); ret = phonebook_pull_read(irmc->request); if (err) *err = ret; return irmc; } static int irmc_get(struct obex_session *os, void *user_data) { struct irmc_session *irmc = user_data; const char *type = obex_get_type(os); const char *name = obex_get_name(os); char *path; int ret; DBG("name %s type %s irmc %p", name, type ? type : "NA", irmc); path = g_strdup(name); ret = obex_get_stream_start(os, path); g_free(path); return ret; } static void irmc_disconnect(struct obex_session *os, void *user_data) { struct irmc_session *irmc = user_data; DBG(""); manager_unregister_session(os); if (irmc->params) { if (irmc->params->searchval) g_free(irmc->params->searchval); g_free(irmc->params); } if (irmc->buffer) g_string_free(irmc->buffer, TRUE); g_free(irmc); } static int irmc_chkput(struct obex_session *os, void *user_data) { DBG(""); /* Reject all PUTs */ return -EBADR; } static int irmc_open_devinfo(struct irmc_session *irmc) { if (!irmc->buffer) irmc->buffer = g_string_new(""); g_string_append_printf(irmc->buffer, "MANU:%s\r\n" "MOD:%s\r\n" "SN:%s\r\n" "IRMC-VERSION:1.1\r\n" "PB-TYPE-TX:VCARD2.1\r\n" "PB-TYPE-RX:NONE\r\n" "CAL-TYPE-TX:NONE\r\n" "CAL-TYPE-RX:NONE\r\n" "MSG-TYPE-TX:NONE\r\n" "MSG-TYPE-RX:NONE\r\n" "NOTE-TYPE-TX:NONE\r\n" "NOTE-TYPE-RX:NONE\r\n", irmc->manu, irmc->model, irmc->sn); return 0; } static int irmc_open_pb(struct irmc_session *irmc) { int ret; /* how can we tell if the vcard count call already finished? */ irmc->request = phonebook_pull(PB_CONTACTS, irmc->params, query_result, irmc, &ret); if (ret < 0) { DBG("phonebook_pull failed..."); return ret; } ret = phonebook_pull_read(irmc->request); if (ret < 0) { DBG("phonebook_pull_read failed..."); return ret; } return 0; } static int irmc_open_info(struct irmc_session *irmc) { if (irmc->buffer == NULL) irmc->buffer = g_string_new(""); g_string_printf(irmc->buffer, "Total-Records:%d\r\n" "Maximum-Records:%d\r\n" "IEL:2\r\n" "DID:%s\r\n", irmc->params->maxlistcount, irmc->params->maxlistcount, irmc->did); return 0; } static int irmc_open_cc(struct irmc_session *irmc) { if (irmc->buffer == NULL) irmc->buffer = g_string_new(""); g_string_printf(irmc->buffer, "%d\r\n", irmc->params->maxlistcount); return 0; } static int irmc_open_cal(struct irmc_session *irmc) { /* no suport yet. Just return an empty buffer. cal.vcs */ DBG("unsupported, returning empty buffer"); if (!irmc->buffer) irmc->buffer = g_string_new(""); return 0; } static int irmc_open_nt(struct irmc_session *irmc) { /* no suport yet. Just return an empty buffer. nt.vnt */ DBG("unsupported, returning empty buffer"); if (!irmc->buffer) irmc->buffer = g_string_new(""); return 0; } static int irmc_open_luid(struct irmc_session *irmc) { if (irmc->buffer == NULL) irmc->buffer = g_string_new(""); DBG("changelog request, force whole book"); g_string_printf(irmc->buffer, "SN:%s\r\n" "DID:%s\r\n" "Total-Records:%d\r\n" "Maximum-Records:%d\r\n" "*\r\n", irmc->sn, irmc->did, irmc->params->maxlistcount, irmc->params->maxlistcount); return 0; } static void *irmc_open(const char *name, int oflag, mode_t mode, void *context, size_t *size, int *err) { struct irmc_session *irmc = context; int ret = 0; char *path; DBG("name %s context %p", name, context); if (oflag != O_RDONLY) { ret = -EPERM; goto fail; } if (name == NULL) { ret = -EBADR; goto fail; } /* Always contains the absolute path */ if (g_path_is_absolute(name)) path = g_strdup(name); else path = g_build_filename("/", name, NULL); if (g_str_equal(path, PB_DEVINFO)) ret = irmc_open_devinfo(irmc); else if (g_str_equal(path, PB_CONTACTS)) ret = irmc_open_pb(irmc); else if (g_str_equal(path, PB_INFO_LOG)) ret = irmc_open_info(irmc); else if (g_str_equal(path, PB_CC_LOG)) ret = irmc_open_cc(irmc); else if (g_str_has_prefix(path, PB_CALENDAR_FOLDER)) ret = irmc_open_cal(irmc); else if (g_str_has_prefix(path, PB_NOTES_FOLDER)) ret = irmc_open_nt(irmc); else if (g_str_has_prefix(path, PB_LUID_FOLDER)) ret = irmc_open_luid(irmc); else ret = -EBADR; g_free(path); if (ret == 0) return irmc; fail: if (err) *err = ret; return NULL; } static int irmc_close(void *object) { struct irmc_session *irmc = object; DBG(""); if (irmc->buffer) { g_string_free(irmc->buffer, TRUE); irmc->buffer = NULL; } if (irmc->request) { phonebook_req_finalize(irmc->request); irmc->request = NULL; } return 0; } static ssize_t irmc_read(void *object, void *buf, size_t count) { struct irmc_session *irmc = object; int len; DBG("buffer %p count %zu", irmc->buffer, count); if (!irmc->buffer) return -EAGAIN; len = string_read(irmc->buffer, buf, count); DBG("returning %d bytes", len); return len; } static const struct obex_mime_type_driver irmc_driver = { .target = IRMC_TARGET, .target_size = IRMC_TARGET_SIZE, .open = irmc_open, .close = irmc_close, .read = irmc_read, }; static const struct obex_service_driver irmc = { .name = "IRMC Sync server", .service = OBEX_IRMC, .target = IRMC_TARGET, .target_size = IRMC_TARGET_SIZE, .connect = irmc_connect, .get = irmc_get, .disconnect = irmc_disconnect, .chkput = irmc_chkput }; static int irmc_init(void) { int err; DBG(""); err = phonebook_init(); if (err < 0) return err; err = obex_mime_type_driver_register(&irmc_driver); if (err < 0) goto fail_mime_irmc; err = obex_service_driver_register(&irmc); if (err < 0) goto fail_irmc_reg; return 0; fail_irmc_reg: obex_mime_type_driver_unregister(&irmc_driver); fail_mime_irmc: phonebook_exit(); return err; } static void irmc_exit(void) { DBG(""); obex_service_driver_unregister(&irmc); obex_mime_type_driver_unregister(&irmc_driver); phonebook_exit(); } OBEX_PLUGIN_DEFINE(irmc, irmc_init, irmc_exit) bluez-5.82/obexd/plugins/PaxHeaders/phonebook-tracker.c0000644000000000000000000000005014032063376020215 xustar0020 atime=1743516875 20 ctime=1743591288 bluez-5.82/obexd/plugins/phonebook-tracker.c0000644000000000000000000013041114032063376017676 0ustar00rootroot// SPDX-License-Identifier: GPL-2.0-or-later /* * Phonebook access through D-Bus vCard and call history service * * Copyright (C) 2010 Nokia Corporation * * */ #ifdef HAVE_CONFIG_H #include #endif #include #include #include #include #include #include #include #include #include "obexd/src/log.h" #include "obexd/src/obex.h" #include "obexd/src/service.h" #include "obexd/src/mimetype.h" #include "phonebook.h" #include "vcard.h" #define TRACKER_SERVICE "org.freedesktop.Tracker1" #define TRACKER_RESOURCES_PATH "/org/freedesktop/Tracker1/Resources" #define TRACKER_RESOURCES_INTERFACE "org.freedesktop.Tracker1.Resources" #define TRACKER_DEFAULT_CONTACT_ME "http://www.semanticdesktop.org/ontologies/2007/03/22/nco#default-contact-me" #define AFFILATION_HOME "Home" #define AFFILATION_WORK "Work" #define ADDR_FIELD_AMOUNT 7 #define PULL_QUERY_COL_AMOUNT 23 #define COUNT_QUERY_COL_AMOUNT 1 #define COL_PHONE_AFF 0 /* work/home phone numbers */ #define COL_FULL_NAME 1 #define COL_FAMILY_NAME 2 #define COL_GIVEN_NAME 3 #define COL_ADDITIONAL_NAME 4 #define COL_NAME_PREFIX 5 #define COL_NAME_SUFFIX 6 #define COL_ADDR_AFF 7 /* addresses from affilation */ #define COL_BIRTH_DATE 8 #define COL_NICKNAME 9 #define COL_URL 10 #define COL_PHOTO 11 #define COL_ORG_ROLE 12 #define COL_UID 13 #define COL_TITLE 14 #define COL_AFF_TYPE 15 #define COL_ORG_NAME 16 #define COL_ORG_DEPARTMENT 17 #define COL_EMAIL_AFF 18 /* email's from affilation (work/home) */ #define COL_DATE 19 #define COL_SENT 20 #define COL_ANSWERED 21 #define CONTACTS_ID_COL 22 #define CONTACT_ID_PREFIX "urn:uuid:" #define CALL_ID_PREFIX "message:" #define FAX_NUM_TYPE "http://www.semanticdesktop.org/ontologies/2007/03/22/nco#FaxNumber" #define MOBILE_NUM_TYPE "http://www.semanticdesktop.org/ontologies/2007/03/22/nco#CellPhoneNumber" #define MAIN_DELIM "\30" /* Main delimiter between phones, addresses, emails*/ #define SUB_DELIM "\31" /* Delimiter used in telephone number strings*/ #define ADDR_DELIM "\37" /* Delimiter used for address data fields */ #define MAX_FIELDS 100 /* Max amount of fields to be concatenated at once*/ #define VCARDS_PART_COUNT 50 /* amount of vcards sent at once to PBAP core */ #define QUERY_OFFSET_FORMAT "%s OFFSET %d" #define CONTACTS_QUERY_ALL \ "SELECT " \ "(SELECT GROUP_CONCAT(fn:concat(rdf:type(?aff_number)," \ "\"\31\", nco:phoneNumber(?aff_number)), \"\30\")" \ "WHERE {" \ " ?_role nco:hasPhoneNumber ?aff_number" \ "}) " \ "nco:fullname(?_contact) " \ "nco:nameFamily(?_contact) " \ "nco:nameGiven(?_contact) " \ "nco:nameAdditional(?_contact) " \ "nco:nameHonorificPrefix(?_contact) " \ "nco:nameHonorificSuffix(?_contact) " \ "(SELECT GROUP_CONCAT(fn:concat(" \ "tracker:coalesce(nco:pobox(?aff_addr), \"\"), \"\37\"," \ "tracker:coalesce(nco:extendedAddress(?aff_addr), \"\"), \"\37\"," \ "tracker:coalesce(nco:streetAddress(?aff_addr), \"\"), \"\37\"," \ "tracker:coalesce(nco:locality(?aff_addr), \"\"), \"\37\"," \ "tracker:coalesce(nco:region(?aff_addr), \"\"), \"\37\"," \ "tracker:coalesce(nco:postalcode(?aff_addr), \"\"), \"\37\"," \ "tracker:coalesce(nco:country(?aff_addr), \"\"), " \ "\"\31\", rdfs:label(?_role) ), " \ "\"\30\") " \ "WHERE {" \ "?_role nco:hasPostalAddress ?aff_addr" \ "}) " \ "nco:birthDate(?_contact) " \ "(SELECT " \ " ?nick " \ " WHERE { " \ " { " \ " ?_contact nco:nickname ?nick " \ " } UNION { " \ " ?_contact nco:hasAffiliation ?role . " \ " ?role nco:hasIMAddress ?im . " \ " ?im nco:imNickname ?nick " \ " } " \ " } " \ ") " \ "(SELECT GROUP_CONCAT(fn:concat( " \ "?url_val, \"\31\", tracker:coalesce(rdfs:label(?_role), \"\") "\ "), \"\30\") " \ "WHERE {" \ "?_role nco:url ?url_val . " \ "})" \ "nie:url(nco:photo(?_contact)) " \ "nco:role(?_role) " \ "nco:contactUID(?_contact) " \ "nco:title(?_role) " \ "rdfs:label(?_role) " \ "nco:fullname(nco:org(?_role))" \ "nco:department(?_role) " \ "(SELECT GROUP_CONCAT(fn:concat(?emailaddress,\"\31\"," \ "tracker:coalesce(rdfs:label(?_role), \"\"))," \ "\"\30\") " \ "WHERE { " \ "?_role nco:hasEmailAddress " \ " [ nco:emailAddress ?emailaddress ] " \ "}) " \ "\"NOTACALL\" \"false\" \"false\" " \ "?_contact " \ "WHERE {" \ " ?_contact a nco:PersonContact ." \ " OPTIONAL {?_contact nco:hasAffiliation ?_role .}" \ "}" \ "ORDER BY tracker:id(?_contact)" #define CONTACTS_QUERY_ALL_LIST \ "SELECT ?c nco:nameFamily(?c) " \ "nco:nameGiven(?c) nco:nameAdditional(?c) " \ "nco:nameHonorificPrefix(?c) nco:nameHonorificSuffix(?c) " \ "(SELECT " \ "?nick " \ "WHERE { " \ "{ " \ "?c nco:nickname ?nick " \ "} UNION { " \ "?c nco:hasAffiliation ?role . " \ "?role nco:hasIMAddress ?im . " \ "?im nco:imNickname ?nick " \ "} " \ "} " \ ") " \ "nco:phoneNumber(?h) " \ "WHERE { " \ "?c a nco:PersonContact . " \ "OPTIONAL { ?c nco:hasPhoneNumber ?h . } " \ "OPTIONAL { " \ "?c nco:hasAffiliation ?a . " \ "?a nco:hasPhoneNumber ?h . " \ "} " \ "} GROUP BY ?c" #define CALLS_CONSTRAINTS(CONSTRAINT) \ " WHERE { " \ "?_call a nmo:Call . " \ "?_unb_contact a nco:Contact . " \ "?_unb_contact nco:hasPhoneNumber ?_cpn . " \ CONSTRAINT \ "OPTIONAL { " \ "{ SELECT ?_contact ?_no ?_role ?_number " \ "count(?_contact) as ?cnt " \ "WHERE { " \ "?_contact a nco:PersonContact . " \ "{ " \ "?_contact nco:hasAffiliation ?_role . "\ "?_role nco:hasPhoneNumber ?_number . " \ "} UNION { " \ "?_contact nco:hasPhoneNumber ?_number" \ "} " \ "?_number maemo:localPhoneNumber ?_no . " \ "} GROUP BY ?_no } " \ "FILTER(?cnt = 1) " \ "?_cpn maemo:localPhoneNumber ?_no . " \ "} " \ "} " #define CALLS_LIST(CONSTRAINT) \ "SELECT ?_call nco:nameFamily(?_contact) " \ "nco:nameGiven(?_contact) nco:nameAdditional(?_contact) " \ "nco:nameHonorificPrefix(?_contact) " \ "nco:nameHonorificSuffix(?_contact) " \ "(SELECT " \ "?nick " \ "WHERE { " \ "{ " \ "?_contact nco:nickname ?nick " \ "} UNION { " \ "?_contact nco:hasAffiliation ?role . " \ "?role nco:hasIMAddress ?im . " \ "?im nco:imNickname ?nick " \ "} " \ "} " \ ") " \ "nco:phoneNumber(?_cpn) " \ CALLS_CONSTRAINTS(CONSTRAINT) \ "ORDER BY DESC(nmo:sentDate(?_call)) " #define CALLS_QUERY(CONSTRAINT) \ "SELECT " \ "(SELECT fn:concat(rdf:type(?role_number)," \ "\"\31\", nco:phoneNumber(?role_number))" \ "WHERE {" \ "{" \ " ?_role nco:hasPhoneNumber ?role_number " \ " FILTER (?role_number = ?_number)" \ "} UNION { " \ "?_unb_contact nco:hasPhoneNumber ?role_number . " \ " FILTER (!bound(?_role)) " \ "}" \ "} GROUP BY nco:phoneNumber(?role_number) ) " \ "nco:fullname(?_contact) " \ "nco:nameFamily(?_contact) " \ "nco:nameGiven(?_contact) " \ "nco:nameAdditional(?_contact) " \ "nco:nameHonorificPrefix(?_contact) " \ "nco:nameHonorificSuffix(?_contact) " \ "(SELECT GROUP_CONCAT(fn:concat(" \ "tracker:coalesce(nco:pobox(?aff_addr), \"\"), \"\37\"," \ "tracker:coalesce(nco:extendedAddress(?aff_addr), \"\"), \"\37\","\ "tracker:coalesce(nco:streetAddress(?aff_addr), \"\"), \"\37\","\ "tracker:coalesce(nco:locality(?aff_addr), \"\"), \"\37\"," \ "tracker:coalesce(nco:region(?aff_addr), \"\"), \"\37\"," \ "tracker:coalesce(nco:postalcode(?aff_addr), \"\"), \"\37\"," \ "tracker:coalesce(nco:country(?aff_addr), \"\"), " \ "\"\31\", rdfs:label(?c_role) ), " \ "\"\30\") " \ "WHERE {" \ "?_contact nco:hasAffiliation ?c_role . " \ "?c_role nco:hasPostalAddress ?aff_addr" \ "}) " \ "nco:birthDate(?_contact) " \ "(SELECT " \ "?nick " \ "WHERE { " \ " { " \ " ?_contact nco:nickname ?nick " \ " } UNION { " \ " ?_contact nco:hasAffiliation ?role . " \ " ?role nco:hasIMAddress ?im . " \ " ?im nco:imNickname ?nick " \ " } " \ " } " \ ") " \ "(SELECT GROUP_CONCAT(fn:concat(?url_value, \"\31\", " \ "tracker:coalesce(rdfs:label(?c_role), \"\")), \"\30\") " \ "WHERE {" \ "?_contact nco:hasAffiliation ?c_role . " \ "?c_role nco:url ?url_value . " \ "})" \ "nie:url(nco:photo(?_contact)) " \ "nco:role(?_role) " \ "nco:contactUID(?_contact) " \ "nco:title(?_role) " \ "rdfs:label(?_role) " \ "nco:fullname(nco:org(?_role)) " \ "nco:department(?_role) " \ "(SELECT GROUP_CONCAT(fn:concat(?emailaddress,\"\31\"," \ "tracker:coalesce(rdfs:label(?c_role), \"\"))," \ "\"\30\") " \ "WHERE { " \ "?_contact nco:hasAffiliation ?c_role . " \ "?c_role nco:hasEmailAddress " \ " [ nco:emailAddress ?emailaddress ] " \ "}) " \ "nmo:receivedDate(?_call) " \ "nmo:isSent(?_call) " \ "nmo:isAnswered(?_call) " \ "?_call " \ CALLS_CONSTRAINTS(CONSTRAINT) \ "ORDER BY DESC(nmo:sentDate(?_call)) " #define MISSED_CONSTRAINT \ "?_call nmo:from ?_unb_contact . " \ "?_call nmo:isSent false . " \ "?_call nmo:isAnswered false . " #define INCOMING_CONSTRAINT \ "?_call nmo:from ?_unb_contact . " \ "?_call nmo:isSent false . " \ "?_call nmo:isAnswered true . " #define OUTGOING_CONSTRAINT \ "?_call nmo:to ?_unb_contact . " \ "?_call nmo:isSent true . " #define COMBINED_CONSTRAINT \ "{ " \ " ?_call nmo:from ?_unb_contact . " \ " ?_call nmo:isSent false " \ "} UNION { " \ " ?_call nmo:to ?_unb_contact . " \ " ?_call nmo:isSent true " \ "} " #define CALL_URI_CONSTRAINT \ COMBINED_CONSTRAINT \ "FILTER (?_call = <%s>) " #define MISSED_CALLS_QUERY CALLS_QUERY(MISSED_CONSTRAINT) #define MISSED_CALLS_LIST CALLS_LIST(MISSED_CONSTRAINT) #define INCOMING_CALLS_QUERY CALLS_QUERY(INCOMING_CONSTRAINT) #define INCOMING_CALLS_LIST CALLS_LIST(INCOMING_CONSTRAINT) #define OUTGOING_CALLS_QUERY CALLS_QUERY(OUTGOING_CONSTRAINT) #define OUTGOING_CALLS_LIST CALLS_LIST(OUTGOING_CONSTRAINT) #define COMBINED_CALLS_QUERY CALLS_QUERY(COMBINED_CONSTRAINT) #define COMBINED_CALLS_LIST CALLS_LIST(COMBINED_CONSTRAINT) #define CONTACT_FROM_CALL_QUERY CALLS_QUERY(CALL_URI_CONSTRAINT) #define CONTACTS_QUERY_FROM_URI \ "SELECT " \ "(SELECT GROUP_CONCAT(fn:concat(rdf:type(?aff_number)," \ "\"\31\", nco:phoneNumber(?aff_number)), \"\30\")" \ "WHERE {" \ " ?_role nco:hasPhoneNumber ?aff_number" \ "}) " \ "nco:fullname(<%s>) " \ "nco:nameFamily(<%s>) " \ "nco:nameGiven(<%s>) " \ "nco:nameAdditional(<%s>) " \ "nco:nameHonorificPrefix(<%s>) " \ "nco:nameHonorificSuffix(<%s>) " \ "(SELECT GROUP_CONCAT(fn:concat(" \ "tracker:coalesce(nco:pobox(?aff_addr), \"\"), \"\37\"," \ "tracker:coalesce(nco:extendedAddress(?aff_addr), \"\"), \"\37\"," \ "tracker:coalesce(nco:streetAddress(?aff_addr), \"\"), \"\37\"," \ "tracker:coalesce(nco:locality(?aff_addr), \"\"), \"\37\"," \ "tracker:coalesce(nco:region(?aff_addr), \"\"), \"\37\"," \ "tracker:coalesce(nco:postalcode(?aff_addr), \"\"), \"\37\"," \ "tracker:coalesce(nco:country(?aff_addr), \"\"), " \ "\"\31\", rdfs:label(?_role) ), " \ "\"\30\") " \ "WHERE {" \ "?_role nco:hasPostalAddress ?aff_addr" \ "}) " \ "nco:birthDate(<%s>) " \ "(SELECT " \ " ?nick " \ " WHERE { " \ " { " \ " ?_contact nco:nickname ?nick " \ " } UNION { " \ " ?_contact nco:hasAffiliation ?role . " \ " ?role nco:hasIMAddress ?im . " \ " ?im nco:imNickname ?nick " \ " } " \ " FILTER (?_contact = <%s>)" \ " } " \ ") " \ "(SELECT GROUP_CONCAT(fn:concat( " \ "?url_val, \"\31\", tracker:coalesce(rdfs:label(?_role), \"\") "\ "), \"\30\") " \ "WHERE {" \ "?_role nco:url ?url_val . " \ "})" \ "nie:url(nco:photo(<%s>)) " \ "nco:role(?_role) " \ "nco:contactUID(<%s>) " \ "nco:title(?_role) " \ "rdfs:label(?_role) " \ "nco:fullname(nco:org(?_role))" \ "nco:department(?_role) " \ "(SELECT GROUP_CONCAT(fn:concat(?emailaddress,\"\31\"," \ "tracker:coalesce(rdfs:label(?_role), \"\"))," \ "\"\30\") " \ "WHERE { " \ "?_role nco:hasEmailAddress " \ " [ nco:emailAddress ?emailaddress ] " \ "}) " \ "\"NOTACALL\" \"false\" \"false\" " \ "<%s> " \ "WHERE {" \ " <%s> a nco:PersonContact ." \ " OPTIONAL {<%s> nco:hasAffiliation ?_role .}" \ "}" #define CONTACTS_OTHER_QUERY_FROM_URI \ "SELECT fn:concat(\"TYPE_OTHER\", \"\31\", nco:phoneNumber(?t))"\ "\"\" \"\" \"\" \"\" \"\" \"\" \"\" \"\" " \ "\"\" \"\" \"\" \"\" \"\" \"\" \"\" \"\" \"\" \"\" " \ " \"NOTACALL\" \"false\" \"false\" <%s> " \ "WHERE { " \ "<%s> a nco:Contact . " \ "OPTIONAL { <%s> nco:hasPhoneNumber ?t . } " \ "} " #define CONTACTS_COUNT_QUERY \ "SELECT COUNT(?c) " \ "WHERE {" \ "?c a nco:PersonContact ." \ "}" #define MISSED_CALLS_COUNT_QUERY \ "SELECT COUNT(?call) WHERE {" \ "?c a nco:Contact ;" \ "nco:hasPhoneNumber ?h ." \ "?call a nmo:Call ;" \ "nmo:isSent false ;" \ "nmo:from ?c ;" \ "nmo:isAnswered false ." \ "}" #define INCOMING_CALLS_COUNT_QUERY \ "SELECT COUNT(?call) WHERE {" \ "?c a nco:Contact ;" \ "nco:hasPhoneNumber ?h ." \ "?call a nmo:Call ;" \ "nmo:isSent false ;" \ "nmo:from ?c ;" \ "nmo:isAnswered true ." \ "}" #define OUTGOING_CALLS_COUNT_QUERY \ "SELECT COUNT(?call) WHERE {" \ "?c a nco:Contact ;" \ "nco:hasPhoneNumber ?h ." \ "?call a nmo:Call ;" \ "nmo:isSent true ;" \ "nmo:to ?c ." \ "}" #define COMBINED_CALLS_COUNT_QUERY \ "SELECT COUNT(?call) WHERE {" \ "{" \ "?c a nco:Contact ;" \ "nco:hasPhoneNumber ?h ." \ "?call a nmo:Call ;" \ "nmo:isSent true ;" \ "nmo:to ?c ." \ "}UNION {" \ "?c a nco:Contact ;" \ "nco:hasPhoneNumber ?h ." \ "?call a nmo:Call ;" \ "nmo:from ?c ." \ "}" \ "}" #define NEW_MISSED_CALLS_COUNT_QUERY \ "SELECT COUNT(?call) WHERE {" \ "?c a nco:Contact ;" \ "nco:hasPhoneNumber ?h ." \ "?call a nmo:Call ;" \ "nmo:isSent false ;" \ "nmo:from ?c ;" \ "nmo:isAnswered false ;" \ "nmo:isRead false ." \ "}" typedef int (*reply_list_foreach_t) (const char **reply, int num_fields, void *user_data); typedef void (*add_field_t) (struct phonebook_contact *contact, const char *value, int type); struct pending_reply { reply_list_foreach_t callback; void *user_data; int num_fields; }; struct contact_data { char *id; struct phonebook_contact *contact; }; struct phonebook_data { phonebook_cb cb; void *user_data; int index; gboolean vcardentry; const struct apparam_field *params; GSList *contacts; phonebook_cache_ready_cb ready_cb; phonebook_entry_cb entry_cb; int newmissedcalls; GCancellable *query_canc; char *req_name; int vcard_part_count; int tracker_index; }; struct phonebook_index { GArray *phonebook; int index; }; static TrackerSparqlConnection *connection = NULL; static const char *name2query(const char *name) { if (g_str_equal(name, PB_CONTACTS)) return CONTACTS_QUERY_ALL; else if (g_str_equal(name, PB_CALLS_INCOMING)) return INCOMING_CALLS_QUERY; else if (g_str_equal(name, PB_CALLS_OUTGOING)) return OUTGOING_CALLS_QUERY; else if (g_str_equal(name, PB_CALLS_MISSED)) return MISSED_CALLS_QUERY; else if (g_str_equal(name, PB_CALLS_COMBINED)) return COMBINED_CALLS_QUERY; return NULL; } static const char *name2count_query(const char *name) { if (g_str_equal(name, PB_CONTACTS)) return CONTACTS_COUNT_QUERY; else if (g_str_equal(name, PB_CALLS_INCOMING)) return INCOMING_CALLS_COUNT_QUERY; else if (g_str_equal(name, PB_CALLS_OUTGOING)) return OUTGOING_CALLS_COUNT_QUERY; else if (g_str_equal(name, PB_CALLS_MISSED)) return MISSED_CALLS_COUNT_QUERY; else if (g_str_equal(name, PB_CALLS_COMBINED)) return COMBINED_CALLS_COUNT_QUERY; return NULL; } static gboolean folder_is_valid(const char *folder) { if (folder == NULL) return FALSE; if (g_str_equal(folder, "/")) return TRUE; else if (g_str_equal(folder, PB_TELECOM_FOLDER)) return TRUE; else if (g_str_equal(folder, PB_CONTACTS_FOLDER)) return TRUE; else if (g_str_equal(folder, PB_CALLS_INCOMING_FOLDER)) return TRUE; else if (g_str_equal(folder, PB_CALLS_OUTGOING_FOLDER)) return TRUE; else if (g_str_equal(folder, PB_CALLS_MISSED_FOLDER)) return TRUE; else if (g_str_equal(folder, PB_CALLS_COMBINED_FOLDER)) return TRUE; return FALSE; } static const char *folder2query(const char *folder) { if (g_str_equal(folder, PB_CONTACTS_FOLDER)) return CONTACTS_QUERY_ALL_LIST; else if (g_str_equal(folder, PB_CALLS_INCOMING_FOLDER)) return INCOMING_CALLS_LIST; else if (g_str_equal(folder, PB_CALLS_OUTGOING_FOLDER)) return OUTGOING_CALLS_LIST; else if (g_str_equal(folder, PB_CALLS_MISSED_FOLDER)) return MISSED_CALLS_LIST; else if (g_str_equal(folder, PB_CALLS_COMBINED_FOLDER)) return COMBINED_CALLS_LIST; return NULL; } static const char **string_array_from_cursor(TrackerSparqlCursor *cursor, int array_len) { const char **result; int i; result = g_new0(const char *, array_len); for (i = 0; i < array_len; ++i) { TrackerSparqlValueType type; type = tracker_sparql_cursor_get_value_type(cursor, i); if (type == TRACKER_SPARQL_VALUE_TYPE_BLANK_NODE || type == TRACKER_SPARQL_VALUE_TYPE_UNBOUND) /* For null/unbound type filling result part with ""*/ result[i] = ""; else /* Filling with string representation of content*/ result[i] = tracker_sparql_cursor_get_string(cursor, i, NULL); } return result; } static void update_cancellable(struct phonebook_data *pdata, GCancellable *canc) { if (pdata->query_canc) g_object_unref(pdata->query_canc); pdata->query_canc = canc; } static void async_query_cursor_next_cb(GObject *source, GAsyncResult *result, gpointer user_data) { struct pending_reply *pending = user_data; TrackerSparqlCursor *cursor = TRACKER_SPARQL_CURSOR(source); GCancellable *cancellable; GError *error = NULL; gboolean success; const char **node; int err; success = tracker_sparql_cursor_next_finish( TRACKER_SPARQL_CURSOR(source), result, &error); if (!success) { if (error) { DBG("cursor_next error: %s", error->message); g_error_free(error); } else /* When tracker_sparql_cursor_next_finish ends with * failure and no error is set, that means end of * results returned by query */ pending->callback(NULL, 0, pending->user_data); goto failed; } node = string_array_from_cursor(cursor, pending->num_fields); err = pending->callback(node, pending->num_fields, pending->user_data); g_free(node); /* Fetch next result only if processing current chunk ended with * success. Sometimes during processing data, we are able to determine * if there is no need to get more data from tracker - by example * stored amount of data parts is big enough for sending and we might * want to suspend processing or just some error occurred. */ if (!err) { cancellable = g_cancellable_new(); update_cancellable(pending->user_data, cancellable); tracker_sparql_cursor_next_async(cursor, cancellable, async_query_cursor_next_cb, pending); return; } failed: g_object_unref(cursor); g_free(pending); } static int query_tracker(const char *query, int num_fields, reply_list_foreach_t callback, void *user_data) { struct pending_reply *pending; GCancellable *cancellable; TrackerSparqlCursor *cursor; GError *error = NULL; DBG(""); if (connection == NULL) connection = tracker_sparql_connection_get_direct( NULL, &error); if (!connection) { if (error) { DBG("direct-connection error: %s", error->message); g_error_free(error); } return -EINTR; } cancellable = g_cancellable_new(); update_cancellable(user_data, cancellable); cursor = tracker_sparql_connection_query(connection, query, cancellable, &error); if (cursor == NULL) { if (error) { DBG("connection_query error: %s", error->message); g_error_free(error); } g_object_unref(cancellable); return -EINTR; } pending = g_new0(struct pending_reply, 1); pending->callback = callback; pending->user_data = user_data; pending->num_fields = num_fields; /* Now asynchronously going through each row of results - callback * async_query_cursor_next_cb will be called ALWAYS, even if async * request was canceled */ tracker_sparql_cursor_next_async(cursor, cancellable, async_query_cursor_next_cb, pending); return 0; } static char *iso8601_utc_to_localtime(const char *datetime) { time_t time; struct tm tm, *local; char localdate[32]; int nr; memset(&tm, 0, sizeof(tm)); nr = sscanf(datetime, "%04u-%02u-%02uT%02u:%02u:%02u", &tm.tm_year, &tm.tm_mon, &tm.tm_mday, &tm.tm_hour, &tm.tm_min, &tm.tm_sec); if (nr < 6) { /* Invalid time format */ error("sscanf(): %s (%d)", strerror(errno), errno); return g_strdup(""); } /* Time already in localtime */ if (!g_str_has_suffix(datetime, "Z")) { strftime(localdate, sizeof(localdate), "%Y%m%dT%H%M%S", &tm); return g_strdup(localdate); } tm.tm_year -= 1900; /* Year since 1900 */ tm.tm_mon--; /* Months since January, values 0-11 */ time = mktime(&tm); time -= timezone; local = localtime(&time); strftime(localdate, sizeof(localdate), "%Y%m%dT%H%M%S", local); return g_strdup(localdate); } static void set_call_type(struct phonebook_contact *contact, const char *datetime, const char *is_sent, const char *is_answered) { gboolean sent, answered; if (g_strcmp0(datetime, "NOTACALL") == 0) { contact->calltype = CALL_TYPE_NOT_A_CALL; return; } sent = g_str_equal(is_sent, "true"); answered = g_str_equal(is_answered, "true"); if (sent == FALSE) { if (answered == FALSE) contact->calltype = CALL_TYPE_MISSED; else contact->calltype = CALL_TYPE_INCOMING; } else contact->calltype = CALL_TYPE_OUTGOING; /* Tracker gives time in the ISO 8601 format, UTC time */ contact->datetime = iso8601_utc_to_localtime(datetime); } static gboolean contact_matches(struct contact_data *c_data, const char *id, const char *datetime) { char *localtime; int cmp_ret; if (g_strcmp0(c_data->id, id) != 0) return FALSE; /* id is equal and not call history entry => contact matches */ if (c_data->contact->calltype == CALL_TYPE_NOT_A_CALL) return TRUE; /* for call history entries have to compare also timestamps of calls */ localtime = iso8601_utc_to_localtime(datetime); cmp_ret = g_strcmp0(c_data->contact->datetime, localtime); g_free(localtime); return (cmp_ret == 0) ? TRUE : FALSE; } static struct phonebook_contact *find_contact(GSList *contacts, const char *id, const char *datetime) { GSList *l; for (l = contacts; l; l = l->next) { struct contact_data *c_data = l->data; if (contact_matches(c_data, id, datetime)) return c_data->contact; } return NULL; } static struct phonebook_field *find_field(GSList *fields, const char *value, int type) { GSList *l; for (l = fields; l; l = l->next) { struct phonebook_field *field = l->data; /* Returning phonebook number if phone values and type values * are equal */ if (g_strcmp0(field->text, value) == 0 && field->type == type) return field; } return NULL; } static void add_phone_number(struct phonebook_contact *contact, const char *phone, int type) { struct phonebook_field *number; if (phone == NULL || strlen(phone) == 0) return; /* Not adding number if there is already added with the same value */ if (find_field(contact->numbers, phone, type)) return; number = g_new0(struct phonebook_field, 1); number->text = g_strdup(phone); number->type = type; contact->numbers = g_slist_append(contact->numbers, number); } static void add_email(struct phonebook_contact *contact, const char *address, int type) { struct phonebook_field *email; if (address == NULL || strlen(address) == 0) return; /* Not adding email if there is already added with the same value */ if (find_field(contact->emails, address, type)) return; email = g_new0(struct phonebook_field, 1); email->text = g_strdup(address); email->type = type; contact->emails = g_slist_append(contact->emails, email); } static gboolean addr_matches(struct phonebook_addr *a, struct phonebook_addr *b) { GSList *la, *lb; if (a->type != b->type) return FALSE; for (la = a->fields, lb = b->fields; la && lb; la = la->next, lb = lb->next) { char *field_a = la->data; char *field_b = lb->data; if (g_strcmp0(field_a, field_b) != 0) return FALSE; } return TRUE; } /* generates phonebook_addr struct from tracker address data string. */ static struct phonebook_addr *gen_addr(const char *address, int type) { struct phonebook_addr *addr; GSList *fields = NULL; char **addr_parts; int i; /* This test handles cases when address points to empty string * (or address is NULL pointer) or string containing only six * separators. It indicates that none of address fields is present * and there is no sense to create dummy phonebook_addr struct */ if (address == NULL || strlen(address) < ADDR_FIELD_AMOUNT) return NULL; addr_parts = g_strsplit(address, ADDR_DELIM, ADDR_FIELD_AMOUNT); for (i = 0; i < ADDR_FIELD_AMOUNT; ++i) fields = g_slist_append(fields, g_strdup(addr_parts[i])); g_strfreev(addr_parts); addr = g_new0(struct phonebook_addr, 1); addr->fields = fields; addr->type = type; return addr; } static void add_address(struct phonebook_contact *contact, const char *address, int type) { struct phonebook_addr *addr; GSList *l; addr = gen_addr(address, type); if (addr == NULL) return; /* Not adding address if there is already added with the same value. * These type of checks have to be done because sometimes tracker * returns results for contact data in more than 1 row - then the same * address may be returned more than once in query results */ for (l = contact->addresses; l; l = l->next) { struct phonebook_addr *tmp = l->data; if (addr_matches(tmp, addr)) { phonebook_addr_free(addr); return; } } contact->addresses = g_slist_append(contact->addresses, addr); } static void add_url(struct phonebook_contact *contact, const char *url_val, int type) { struct phonebook_field *url; if (url_val == NULL || strlen(url_val) == 0) return; /* Not adding url if there is already added with the same value */ if (find_field(contact->urls, url_val, type)) return; url = g_new0(struct phonebook_field, 1); url->text = g_strdup(url_val); url->type = type; contact->urls = g_slist_append(contact->urls, url); } static GString *gen_vcards(GSList *contacts, const struct apparam_field *params) { GSList *l; GString *vcards; vcards = g_string_new(NULL); /* Generating VCARD string from contacts and freeing used contacts */ for (l = contacts; l; l = l->next) { struct contact_data *c_data = l->data; phonebook_add_contact(vcards, c_data->contact, params->filter, params->format); } return vcards; } static int pull_contacts_size(const char **reply, int num_fields, void *user_data) { struct phonebook_data *data = user_data; if (num_fields < 0) { data->cb(NULL, 0, num_fields, 0, TRUE, data->user_data); return -EINTR; } if (reply != NULL) { data->index = atoi(reply[0]); return 0; } data->cb(NULL, 0, data->index, data->newmissedcalls, TRUE, data->user_data); return 0; /* * phonebook_data is freed in phonebook_req_finalize. Useful in * cases when call is terminated. */ } static void add_affiliation(char **field, const char *value) { if (strlen(*field) > 0 || value == NULL || strlen(value) == 0) return; g_free(*field); *field = g_strdup(value); } static void contact_init(struct phonebook_contact *contact, const char **reply) { if (reply[COL_FAMILY_NAME][0] == '\0' && reply[COL_GIVEN_NAME][0] == '\0' && reply[COL_ADDITIONAL_NAME][0] == '\0' && reply[COL_NAME_PREFIX][0] == '\0' && reply[COL_NAME_SUFFIX][0] == '\0') { if (reply[COL_FULL_NAME][0] != '\0') contact->family = g_strdup(reply[COL_FULL_NAME]); else contact->family = g_strdup(reply[COL_NICKNAME]); } else { contact->family = g_strdup(reply[COL_FAMILY_NAME]); contact->given = g_strdup(reply[COL_GIVEN_NAME]); contact->additional = g_strdup(reply[COL_ADDITIONAL_NAME]); contact->prefix = g_strdup(reply[COL_NAME_PREFIX]); contact->suffix = g_strdup(reply[COL_NAME_SUFFIX]); } contact->fullname = g_strdup(reply[COL_FULL_NAME]); contact->birthday = g_strdup(reply[COL_BIRTH_DATE]); contact->nickname = g_strdup(reply[COL_NICKNAME]); contact->photo = g_strdup(reply[COL_PHOTO]); contact->company = g_strdup(reply[COL_ORG_NAME]); contact->department = g_strdup(reply[COL_ORG_DEPARTMENT]); contact->role = g_strdup(reply[COL_ORG_ROLE]); contact->uid = g_strdup(reply[COL_UID]); contact->title = g_strdup(reply[COL_TITLE]); set_call_type(contact, reply[COL_DATE], reply[COL_SENT], reply[COL_ANSWERED]); } static enum phonebook_number_type get_phone_type(const char *affilation) { if (g_strcmp0(AFFILATION_HOME, affilation) == 0) return TEL_TYPE_HOME; else if (g_strcmp0(AFFILATION_WORK, affilation) == 0) return TEL_TYPE_WORK; return TEL_TYPE_OTHER; } static void add_aff_number(struct phonebook_contact *contact, const char *pnumber, const char *aff_type) { char **num_parts; char *type, *number; /* For phone taken directly from contacts data, phone number string * is represented as number type and number string - those strings are * separated by SUB_DELIM string */ num_parts = g_strsplit(pnumber, SUB_DELIM, 2); if (!num_parts) return; if (num_parts[0]) type = num_parts[0]; else goto failed; if (num_parts[1]) number = num_parts[1]; else goto failed; if (g_strrstr(type, FAX_NUM_TYPE)) add_phone_number(contact, number, TEL_TYPE_FAX); else if (g_strrstr(type, MOBILE_NUM_TYPE)) add_phone_number(contact, number, TEL_TYPE_MOBILE); else /* if this is no fax/mobile phone, then adding phone number * type based on type of the affilation field */ add_phone_number(contact, number, get_phone_type(aff_type)); failed: g_strfreev(num_parts); } static void contact_add_numbers(struct phonebook_contact *contact, const char **reply) { char **aff_numbers; int i; /* Filling phone numbers from contact's affilation */ aff_numbers = g_strsplit(reply[COL_PHONE_AFF], MAIN_DELIM, MAX_FIELDS); if (aff_numbers) for (i = 0; aff_numbers[i]; ++i) add_aff_number(contact, aff_numbers[i], reply[COL_AFF_TYPE]); g_strfreev(aff_numbers); } static enum phonebook_field_type get_field_type(const char *affilation) { if (g_strcmp0(AFFILATION_HOME, affilation) == 0) return FIELD_TYPE_HOME; else if (g_strcmp0(AFFILATION_WORK, affilation) == 0) return FIELD_TYPE_WORK; return FIELD_TYPE_OTHER; } static void add_aff_field(struct phonebook_contact *contact, const char *aff_email, add_field_t add_field_cb) { char **email_parts; char *type, *email; /* Emails from affilation data, are represented as real email * string and affilation type - those strings are separated by * SUB_DELIM string */ email_parts = g_strsplit(aff_email, SUB_DELIM, 2); if (!email_parts) return; if (email_parts[0]) email = email_parts[0]; else goto failed; if (email_parts[1]) type = email_parts[1]; else goto failed; add_field_cb(contact, email, get_field_type(type)); failed: g_strfreev(email_parts); } static void contact_add_emails(struct phonebook_contact *contact, const char **reply) { char **aff_emails; int i; /* Emails from affilation */ aff_emails = g_strsplit(reply[COL_EMAIL_AFF], MAIN_DELIM, MAX_FIELDS); if (aff_emails) for (i = 0; aff_emails[i] != NULL; ++i) add_aff_field(contact, aff_emails[i], add_email); g_strfreev(aff_emails); } static void contact_add_addresses(struct phonebook_contact *contact, const char **reply) { char **aff_addr; int i; /* Addresses from affilation */ aff_addr = g_strsplit(reply[COL_ADDR_AFF], MAIN_DELIM, MAX_FIELDS); if (aff_addr) for (i = 0; aff_addr[i] != NULL; ++i) add_aff_field(contact, aff_addr[i], add_address); g_strfreev(aff_addr); } static void contact_add_urls(struct phonebook_contact *contact, const char **reply) { char **aff_url; int i; /* Addresses from affilation */ aff_url = g_strsplit(reply[COL_URL], MAIN_DELIM, MAX_FIELDS); if (aff_url) for (i = 0; aff_url[i] != NULL; ++i) add_aff_field(contact, aff_url[i], add_url); g_strfreev(aff_url); } static void contact_add_organization(struct phonebook_contact *contact, const char **reply) { /* Adding fields connected by nco:hasAffiliation - they may be in * separate replies */ add_affiliation(&contact->title, reply[COL_TITLE]); add_affiliation(&contact->company, reply[COL_ORG_NAME]); add_affiliation(&contact->department, reply[COL_ORG_DEPARTMENT]); add_affiliation(&contact->role, reply[COL_ORG_ROLE]); } static void free_data_contacts(struct phonebook_data *data) { GSList *l; /* freeing contacts */ for (l = data->contacts; l; l = l->next) { struct contact_data *c_data = l->data; g_free(c_data->id); phonebook_contact_free(c_data->contact); g_free(c_data); } g_slist_free(data->contacts); data->contacts = NULL; } static void send_pull_part(struct phonebook_data *data, const struct apparam_field *params, gboolean lastpart) { GString *vcards; DBG(""); vcards = gen_vcards(data->contacts, params); data->cb(vcards->str, vcards->len, g_slist_length(data->contacts), data->newmissedcalls, lastpart, data->user_data); if (!lastpart) free_data_contacts(data); g_string_free(vcards, TRUE); } static int pull_contacts(const char **reply, int num_fields, void *user_data) { struct phonebook_data *data = user_data; const struct apparam_field *params = data->params; struct phonebook_contact *contact; struct contact_data *contact_data; int last_index, i; gboolean cdata_present = FALSE, part_sent = FALSE; static char *temp_id = NULL; if (num_fields < 0) { data->cb(NULL, 0, num_fields, 0, TRUE, data->user_data); goto fail; } DBG("reply %p", reply); data->tracker_index++; if (reply == NULL) goto done; /* Trying to find contact in recently added contacts. It is needed for * contacts that have more than one telephone number filled */ contact = find_contact(data->contacts, reply[CONTACTS_ID_COL], reply[COL_DATE]); /* If contact is already created then adding only new phone numbers */ if (contact) { cdata_present = TRUE; goto add_numbers; } /* We are doing a PullvCardEntry, no need for those checks */ if (data->vcardentry) goto add_entry; /* Last four fields are always present, ignoring them */ for (i = 0; i < num_fields - 4; i++) { if (reply[i][0] != '\0') break; } if (i == num_fields - 4 && !g_str_equal(reply[CONTACTS_ID_COL], TRACKER_DEFAULT_CONTACT_ME)) return 0; if (g_strcmp0(temp_id, reply[CONTACTS_ID_COL])) { data->index++; g_free(temp_id); temp_id = g_strdup(reply[CONTACTS_ID_COL]); /* Incrementing counter for vcards in current part of data, * but only if liststartoffset has been already reached */ if (data->index > params->liststartoffset) data->vcard_part_count++; } if (data->vcard_part_count > VCARDS_PART_COUNT) { DBG("Part of vcard data ready for sending..."); data->vcard_part_count = 0; /* Sending part of data to PBAP core - more data can be still * fetched, so marking lastpart as FALSE */ send_pull_part(data, params, FALSE); /* Later, after adding contact data, need to return -EINTR to * stop fetching more data for this request. Data will be * downloaded again from this point, when phonebook_pull_read * will be called again with current request as a parameter*/ part_sent = TRUE; } last_index = params->liststartoffset + params->maxlistcount; if (data->index <= params->liststartoffset) return 0; /* max number of results achieved - need send vcards data that was * already collected and stop further data processing (these operations * will be invoked in "done" section) */ if (data->index > last_index && params->maxlistcount > 0) { DBG("Maxlistcount achieved"); goto done; } add_entry: contact = g_new0(struct phonebook_contact, 1); contact_init(contact, reply); add_numbers: contact_add_numbers(contact, reply); contact_add_emails(contact, reply); contact_add_addresses(contact, reply); contact_add_urls(contact, reply); contact_add_organization(contact, reply); DBG("contact %p", contact); /* Adding contacts data to wrapper struct - this data will be used to * generate vcard list */ if (!cdata_present) { contact_data = g_new0(struct contact_data, 1); contact_data->contact = contact; contact_data->id = g_strdup(reply[CONTACTS_ID_COL]); data->contacts = g_slist_append(data->contacts, contact_data); } if (part_sent) return -EINTR; return 0; done: /* Processing is end, this is definitely last part of transmission * (marking lastpart as TRUE) */ send_pull_part(data, params, TRUE); fail: g_free(temp_id); temp_id = NULL; return -EINTR; /* * phonebook_data is freed in phonebook_req_finalize. Useful in * cases when call is terminated. */ } static int add_to_cache(const char **reply, int num_fields, void *user_data) { struct phonebook_data *data = user_data; char *formatted; int i; if (reply == NULL || num_fields < 0) goto done; /* the first element is the URI, always not empty */ for (i = 1; i < num_fields; i++) { if (reply[i][0] != '\0') break; } if (i == num_fields && !g_str_equal(reply[0], TRACKER_DEFAULT_CONTACT_ME)) return 0; if (i == 7) formatted = g_strdup(reply[7]); else if (i == 6) formatted = g_strdup(reply[6]); else formatted = g_strdup_printf("%s;%s;%s;%s;%s", reply[1], reply[2], reply[3], reply[4], reply[5]); /* The owner vCard must have the 0 handle */ if (strcmp(reply[0], TRACKER_DEFAULT_CONTACT_ME) == 0) data->entry_cb(reply[0], 0, formatted, "", reply[6], data->user_data); else data->entry_cb(reply[0], PHONEBOOK_INVALID_HANDLE, formatted, "", reply[6], data->user_data); g_free(formatted); return 0; done: if (num_fields <= 0) data->ready_cb(data->user_data); return -EINTR; /* * phonebook_data is freed in phonebook_req_finalize. Useful in * cases when call is terminated. */ } int phonebook_init(void) { g_type_init(); return 0; } void phonebook_exit(void) { } char *phonebook_set_folder(const char *current_folder, const char *new_folder, uint8_t flags, int *err) { char *tmp1, *tmp2, *base, *path = NULL; gboolean root, child; int ret = 0; int len; root = (g_strcmp0("/", current_folder) == 0); child = (new_folder && strlen(new_folder) != 0); switch (flags) { case 0x02: /* Go back to root */ if (!child) { path = g_strdup("/"); goto done; } path = g_build_filename(current_folder, new_folder, NULL); break; case 0x03: /* Go up 1 level */ if (root) { /* Already root */ path = g_strdup("/"); goto done; } /* * Removing one level of the current folder. Current folder * contains AT LEAST one level since it is not at root folder. * Use glib utility functions to handle invalid chars in the * folder path properly. */ tmp1 = g_path_get_basename(current_folder); tmp2 = g_strrstr(current_folder, tmp1); len = tmp2 - (current_folder + 1); g_free(tmp1); if (len == 0) base = g_strdup("/"); else base = g_strndup(current_folder, len); /* Return: one level only */ if (!child) { path = base; goto done; } path = g_build_filename(base, new_folder, NULL); g_free(base); break; default: ret = -EBADR; break; } done: if (path && !folder_is_valid(path)) ret = -ENOENT; if (ret < 0) { g_free(path); path = NULL; } if (err) *err = ret; return path; } static int pull_newmissedcalls(const char **reply, int num_fields, void *user_data) { struct phonebook_data *data = user_data; reply_list_foreach_t pull_cb; int col_amount, err; const char *query; int nmissed; if (num_fields < 0) { data->cb(NULL, 0, num_fields, 0, TRUE, data->user_data); return -EINTR; } if (reply != NULL) { nmissed = atoi(reply[0]); data->newmissedcalls = nmissed <= UINT8_MAX ? nmissed : UINT8_MAX; DBG("newmissedcalls %d", data->newmissedcalls); return 0; } if (data->params->maxlistcount == 0) { query = name2count_query(PB_CALLS_MISSED); col_amount = COUNT_QUERY_COL_AMOUNT; pull_cb = pull_contacts_size; } else { query = name2query(PB_CALLS_MISSED); col_amount = PULL_QUERY_COL_AMOUNT; pull_cb = pull_contacts; } err = query_tracker(query, col_amount, pull_cb, data); if (err < 0) { data->cb(NULL, 0, err, 0, TRUE, data->user_data); return -EINTR; } return 0; } void phonebook_req_finalize(void *request) { struct phonebook_data *data = request; DBG(""); if (!data) return; /* canceling asynchronous operation on tracker if any is active */ if (data->query_canc) { g_cancellable_cancel(data->query_canc); g_object_unref(data->query_canc); } free_data_contacts(data); g_free(data->req_name); g_free(data); } void *phonebook_pull(const char *name, const struct apparam_field *params, phonebook_cb cb, void *user_data, int *err) { struct phonebook_data *data; DBG("name %s", name); data = g_new0(struct phonebook_data, 1); data->params = params; data->user_data = user_data; data->cb = cb; data->req_name = g_strdup(name); if (err) *err = 0; return data; } int phonebook_pull_read(void *request) { struct phonebook_data *data = request; reply_list_foreach_t pull_cb; const char *query; char *offset_query; int col_amount; int ret; if (!data) return -ENOENT; data->newmissedcalls = 0; if (g_strcmp0(data->req_name, PB_CALLS_MISSED) == 0 && data->tracker_index == 0) { /* new missed calls amount should be counted only once - it * will be done during generating first part of results of * missed calls history */ query = NEW_MISSED_CALLS_COUNT_QUERY; col_amount = COUNT_QUERY_COL_AMOUNT; pull_cb = pull_newmissedcalls; } else if (data->params->maxlistcount == 0) { query = name2count_query(data->req_name); col_amount = COUNT_QUERY_COL_AMOUNT; pull_cb = pull_contacts_size; } else { query = name2query(data->req_name); col_amount = PULL_QUERY_COL_AMOUNT; pull_cb = pull_contacts; } if (query == NULL) return -ENOENT; if (pull_cb == pull_contacts && data->tracker_index > 0) { /* Adding offset to pull query to download next parts of data * from tracker (phonebook_pull_read may be called many times * from PBAP core to fetch data partially) */ offset_query = g_strdup_printf(QUERY_OFFSET_FORMAT, query, data->tracker_index); ret = query_tracker(offset_query, col_amount, pull_cb, data); g_free(offset_query); return ret; } return query_tracker(query, col_amount, pull_cb, data); } void *phonebook_get_entry(const char *folder, const char *id, const struct apparam_field *params, phonebook_cb cb, void *user_data, int *err) { struct phonebook_data *data; char *query; int ret; DBG("folder %s id %s", folder, id); data = g_new0(struct phonebook_data, 1); data->user_data = user_data; data->params = params; data->cb = cb; data->vcardentry = TRUE; if (g_str_has_prefix(id, CONTACT_ID_PREFIX) == TRUE || g_strcmp0(id, TRACKER_DEFAULT_CONTACT_ME) == 0) query = g_strdup_printf(CONTACTS_QUERY_FROM_URI, id, id, id, id, id, id, id, id, id, id, id, id, id); else if (g_str_has_prefix(id, CALL_ID_PREFIX) == TRUE) query = g_strdup_printf(CONTACT_FROM_CALL_QUERY, id); else query = g_strdup_printf(CONTACTS_OTHER_QUERY_FROM_URI, id, id, id); ret = query_tracker(query, PULL_QUERY_COL_AMOUNT, pull_contacts, data); if (err) *err = ret; g_free(query); return data; } void *phonebook_create_cache(const char *name, phonebook_entry_cb entry_cb, phonebook_cache_ready_cb ready_cb, void *user_data, int *err) { struct phonebook_data *data; const char *query; int ret; DBG("name %s", name); query = folder2query(name); if (query == NULL) { if (err) *err = -ENOENT; return NULL; } data = g_new0(struct phonebook_data, 1); data->entry_cb = entry_cb; data->ready_cb = ready_cb; data->user_data = user_data; ret = query_tracker(query, 8, add_to_cache, data); if (err) *err = ret; return data; } bluez-5.82/obexd/PaxHeaders/client0000644000000000000000000000005014773213563014163 xustar0020 atime=1743591291 20 ctime=1743591283 bluez-5.82/obexd/client/0000755000000000000000000000000014773213563013721 5ustar00rootrootbluez-5.82/obexd/client/PaxHeaders/map-event.h0000644000000000000000000000005014015011623016263 xustar0020 atime=1743516730 20 ctime=1743591283 bluez-5.82/obexd/client/map-event.h0000644000000000000000000000247614015011623015755 0ustar00rootroot/* SPDX-License-Identifier: GPL-2.0-or-later */ /* * * OBEX * * Copyright (C) 2013 BMW Car IT GmbH. All rights reserved. * * */ struct obc_session; enum map_event_type { MAP_ET_NEW_MESSAGE, MAP_ET_DELIVERY_SUCCESS, MAP_ET_SENDING_SUCCESS, MAP_ET_DELIVERY_FAILURE, MAP_ET_SENDING_FAILURE, MAP_ET_MEMORY_FULL, MAP_ET_MEMORY_AVAILABLE, MAP_ET_MESSAGE_DELETED, MAP_ET_MESSAGE_SHIFT }; struct map_event { enum map_event_type type; uint64_t handle; char *folder; char *old_folder; char *msg_type; char *datetime; char *subject; char *sender_name; char *priority; }; /* Handle notification in map client. * * event: Event report. * * Callback shall be called for every received event. */ typedef void (*map_event_cb) (struct map_event *event, void *user_data); /* Registers client notification handler callback for events that are * addressed to the given mas instance id for the given device. */ bool map_register_event_handler(struct obc_session *session, int mas_id, map_event_cb cb, void *user_data); /* Unregisters client notification handler callback. */ void map_unregister_event_handler(struct obc_session *session, int mas_id); /* Dispatch notification to a registered notification handler callback. */ void map_dispatch_event(int mas_id, const char *device, struct map_event *event); bluez-5.82/obexd/client/PaxHeaders/bluetooth.c0000644000000000000000000000005014015011623016367 xustar0020 atime=1743516746 20 ctime=1743591283 bluez-5.82/obexd/client/bluetooth.c0000644000000000000000000002472714015011623016064 0ustar00rootroot// SPDX-License-Identifier: GPL-2.0-or-later /* * * OBEX Client * * Copyright (C) 2012 Intel Corporation * * */ #ifdef HAVE_CONFIG_H #include #endif #include #include #include #include #include "lib/bluetooth.h" #include "lib/rfcomm.h" #include "lib/sdp.h" #include "lib/sdp_lib.h" #include "gdbus/gdbus.h" #include "btio/btio.h" #include "obexd/src/log.h" #include "transport.h" #include "bluetooth.h" #define BT_RX_MTU 32767 #define BT_TX_MTU 32767 #define OBC_BT_ERROR obc_bt_error_quark() struct bluetooth_session { guint id; bdaddr_t src; bdaddr_t dst; uint16_t port; sdp_session_t *sdp; sdp_record_t *sdp_record; GIOChannel *io; char *service; obc_transport_func func; void *user_data; }; static GSList *sessions = NULL; static GQuark obc_bt_error_quark(void) { return g_quark_from_static_string("obc-bluetooth-error-quark"); } static void session_destroy(struct bluetooth_session *session) { DBG("%p", session); if (g_slist_find(sessions, session) == NULL) return; sessions = g_slist_remove(sessions, session); if (session->io != NULL) { g_io_channel_shutdown(session->io, TRUE, NULL); g_io_channel_unref(session->io); } if (session->sdp) sdp_close(session->sdp); if (session->sdp_record) sdp_record_free(session->sdp_record); g_free(session->service); g_free(session); } static void transport_callback(GIOChannel *io, GError *err, gpointer user_data) { struct bluetooth_session *session = user_data; DBG(""); if (session->func) session->func(io, err, session->user_data); if (err != NULL) session_destroy(session); } static GIOChannel *transport_connect(const bdaddr_t *src, const bdaddr_t *dst, uint16_t port, BtIOConnect function, gpointer user_data) { GIOChannel *io; GError *err = NULL; DBG("port %u", port); if (port > 31) { io = bt_io_connect(function, user_data, NULL, &err, BT_IO_OPT_SOURCE_BDADDR, src, BT_IO_OPT_DEST_BDADDR, dst, BT_IO_OPT_PSM, port, BT_IO_OPT_MODE, BT_IO_MODE_ERTM, BT_IO_OPT_OMTU, BT_TX_MTU, BT_IO_OPT_IMTU, BT_RX_MTU, BT_IO_OPT_SEC_LEVEL, BT_IO_SEC_LOW, BT_IO_OPT_INVALID); } else { io = bt_io_connect(function, user_data, NULL, &err, BT_IO_OPT_SOURCE_BDADDR, src, BT_IO_OPT_DEST_BDADDR, dst, BT_IO_OPT_CHANNEL, port, BT_IO_OPT_SEC_LEVEL, BT_IO_SEC_LOW, BT_IO_OPT_INVALID); } if (io != NULL) return io; error("%s", err->message); g_error_free(err); return NULL; } static void search_callback(uint8_t type, uint16_t status, uint8_t *rsp, size_t size, void *user_data) { struct bluetooth_session *session = user_data; unsigned int scanned, bytesleft = size; int seqlen = 0; uint8_t dataType; uint16_t port = 0; GError *gerr = NULL; if (status || type != SDP_SVC_SEARCH_ATTR_RSP) goto failed; scanned = sdp_extract_seqtype(rsp, bytesleft, &dataType, &seqlen); if (!scanned || !seqlen) goto failed; rsp += scanned; bytesleft -= scanned; do { sdp_record_t *rec; sdp_list_t *protos; sdp_data_t *data; int recsize, ch = -1; recsize = 0; rec = sdp_extract_pdu(rsp, bytesleft, &recsize); if (!rec) break; if (!recsize) { sdp_record_free(rec); break; } if (!sdp_get_access_protos(rec, &protos)) { ch = sdp_get_proto_port(protos, RFCOMM_UUID); sdp_list_foreach(protos, (sdp_list_func_t) sdp_list_free, NULL); sdp_list_free(protos, NULL); protos = NULL; } data = sdp_data_get(rec, 0x0200); /* PSM must be odd and lsb of upper byte must be 0 */ if (data != NULL && (data->val.uint16 & 0x0101) == 0x0001) ch = data->val.uint16; /* Cache the sdp record associated with the service that we * attempt to connect. This allows reading its application * specific service attributes. */ if (ch > 0) { port = ch; session->sdp_record = rec; break; } sdp_record_free(rec); scanned += recsize; rsp += recsize; bytesleft -= recsize; } while (scanned < size && bytesleft > 0); if (port == 0) goto failed; session->port = port; g_io_channel_set_close_on_unref(session->io, FALSE); g_io_channel_unref(session->io); session->io = transport_connect(&session->src, &session->dst, port, transport_callback, session); if (session->io != NULL) { sdp_close(session->sdp); session->sdp = NULL; return; } failed: if (session->io != NULL) { g_io_channel_shutdown(session->io, TRUE, NULL); g_io_channel_unref(session->io); session->io = NULL; } g_set_error(&gerr, OBC_BT_ERROR, -EIO, "Unable to find service record"); if (session->func) session->func(session->io, gerr, session->user_data); g_clear_error(&gerr); session_destroy(session); } static gboolean process_callback(GIOChannel *io, GIOCondition cond, gpointer user_data) { struct bluetooth_session *session = user_data; if (cond & (G_IO_ERR | G_IO_HUP | G_IO_NVAL)) return FALSE; if (sdp_process(session->sdp) < 0) return FALSE; return TRUE; } static int bt_string2uuid(uuid_t *uuid, const char *string) { uint32_t data0, data4; uint16_t data1, data2, data3, data5; if (sscanf(string, "%08x-%04hx-%04hx-%04hx-%08x%04hx", &data0, &data1, &data2, &data3, &data4, &data5) == 6) { uint8_t val[16]; data0 = g_htonl(data0); data1 = g_htons(data1); data2 = g_htons(data2); data3 = g_htons(data3); data4 = g_htonl(data4); data5 = g_htons(data5); memcpy(&val[0], &data0, 4); memcpy(&val[4], &data1, 2); memcpy(&val[6], &data2, 2); memcpy(&val[8], &data3, 2); memcpy(&val[10], &data4, 4); memcpy(&val[14], &data5, 2); sdp_uuid128_create(uuid, val); return 0; } return -EINVAL; } static gboolean service_callback(GIOChannel *io, GIOCondition cond, gpointer user_data) { struct bluetooth_session *session = user_data; sdp_list_t *search, *attrid; uint32_t range = 0x0000ffff; GError *gerr = NULL; uuid_t uuid; if (cond & G_IO_NVAL) return FALSE; if (cond & G_IO_ERR) goto failed; if (sdp_set_notify(session->sdp, search_callback, session) < 0) goto failed; if (bt_string2uuid(&uuid, session->service) < 0) goto failed; sdp_uuid128_to_uuid(&uuid); search = sdp_list_append(NULL, &uuid); attrid = sdp_list_append(NULL, &range); if (sdp_service_search_attr_async(session->sdp, search, SDP_ATTR_REQ_RANGE, attrid) < 0) { sdp_list_free(attrid, NULL); sdp_list_free(search, NULL); goto failed; } sdp_list_free(attrid, NULL); sdp_list_free(search, NULL); g_io_add_watch(io, G_IO_IN | G_IO_HUP | G_IO_ERR | G_IO_NVAL, process_callback, session); return FALSE; failed: g_io_channel_shutdown(session->io, TRUE, NULL); g_io_channel_unref(session->io); session->io = NULL; g_set_error(&gerr, OBC_BT_ERROR, -EIO, "Unable to find service record"); if (session->func) session->func(session->io, gerr, session->user_data); g_clear_error(&gerr); session_destroy(session); return FALSE; } static sdp_session_t *service_connect(const bdaddr_t *src, const bdaddr_t *dst, GIOFunc function, gpointer user_data) { struct bluetooth_session *session = user_data; sdp_session_t *sdp; GIOChannel *io; DBG(""); sdp = sdp_connect(src, dst, SDP_NON_BLOCKING); if (sdp == NULL) return NULL; io = g_io_channel_unix_new(sdp_get_socket(sdp)); if (io == NULL) { sdp_close(sdp); return NULL; } g_io_add_watch(io, G_IO_OUT | G_IO_HUP | G_IO_ERR | G_IO_NVAL, function, user_data); session->io = io; return sdp; } static int session_connect(struct bluetooth_session *session) { int err; DBG("session %p", session); if (session->port > 0) { session->io = transport_connect(&session->src, &session->dst, session->port, transport_callback, session); err = (session->io == NULL) ? -EINVAL : 0; } else { session->sdp = service_connect(&session->src, &session->dst, service_callback, session); err = (session->sdp == NULL) ? -ENOMEM : 0; } return err; } static guint bluetooth_connect(const char *source, const char *destination, const char *service, uint16_t port, obc_transport_func func, void *user_data) { struct bluetooth_session *session; static guint id = 0; DBG("src %s dest %s service %s port %u", source, destination, service, port); if (destination == NULL) return 0; session = g_try_malloc0(sizeof(*session)); if (session == NULL) return 0; session->id = ++id; session->func = func; session->port = port; session->user_data = user_data; str2ba(destination, &session->dst); str2ba(source, &session->src); if (session_connect(session) < 0) { g_free(session); return 0; } session->service = g_strdup(service); sessions = g_slist_prepend(sessions, session); return session->id; } static void bluetooth_disconnect(guint id) { GSList *l; DBG(""); for (l = sessions; l; l = l->next) { struct bluetooth_session *session = l->data; if (session->id == id) { session_destroy(session); return; } } } static int bluetooth_getpacketopt(GIOChannel *io, int *tx_mtu, int *rx_mtu) { int sk = g_io_channel_unix_get_fd(io); int type; uint16_t omtu = BT_TX_MTU; uint16_t imtu = BT_RX_MTU; socklen_t len = sizeof(int); DBG(""); if (getsockopt(sk, SOL_SOCKET, SO_TYPE, &type, &len) < 0) return -errno; if (type != SOCK_SEQPACKET) return -EINVAL; if (!bt_io_get(io, NULL, BT_IO_OPT_OMTU, &omtu, BT_IO_OPT_IMTU, &imtu, BT_IO_OPT_INVALID)) return -EINVAL; if (tx_mtu) *tx_mtu = omtu; if (rx_mtu) *rx_mtu = imtu; return 0; } static const void *bluetooth_getattribute(guint id, int attribute_id) { GSList *l; sdp_data_t *data; for (l = sessions; l; l = l->next) { struct bluetooth_session *session = l->data; if (session->id != id) continue; if (session->sdp_record == NULL) break; /* Read version since UUID is already known */ if (attribute_id == SDP_ATTR_PFILE_DESC_LIST) { sdp_list_t *descs; void *ret = NULL; if (sdp_get_profile_descs(session->sdp_record, &descs) < 0) return NULL; if (descs && descs->data) { sdp_profile_desc_t *desc = descs->data; ret = GINT_TO_POINTER(desc->version); } sdp_list_free(descs, free); return ret; } data = sdp_data_get(session->sdp_record, attribute_id); if (!data) break; return &data->val; } return NULL; } static struct obc_transport bluetooth = { .name = "Bluetooth", .connect = bluetooth_connect, .getpacketopt = bluetooth_getpacketopt, .disconnect = bluetooth_disconnect, .getattribute = bluetooth_getattribute, }; int bluetooth_init(void) { DBG(""); return obc_transport_register(&bluetooth); } void bluetooth_exit(void) { DBG(""); obc_transport_unregister(&bluetooth); } bluez-5.82/obexd/client/PaxHeaders/pbap.h0000644000000000000000000000005014015011623015311 xustar0020 atime=1743516742 20 ctime=1743591283 bluez-5.82/obexd/client/pbap.h0000644000000000000000000000036114015011623014772 0ustar00rootroot/* SPDX-License-Identifier: GPL-2.0-or-later */ /* * * OBEX Client * * Copyright (C) 2007-2010 Intel Corporation * Copyright (C) 2007-2010 Marcel Holtmann * * */ int pbap_init(void); void pbap_exit(void); bluez-5.82/obexd/client/PaxHeaders/transfer.h0000644000000000000000000000005014711225434016225 xustar0020 atime=1743516742 20 ctime=1743591283 bluez-5.82/obexd/client/transfer.h0000644000000000000000000000321114711225434015703 0ustar00rootroot/* SPDX-License-Identifier: GPL-2.0-or-later */ /* * * OBEX Client * * Copyright (C) 2007-2010 Marcel Holtmann * Copyright (C) 2011-2012 BMW Car IT GmbH. All rights reserved. * * */ struct obc_transfer; typedef void (*transfer_callback_t) (struct obc_transfer *transfer, GError *err, void *user_data); struct obc_transfer *obc_transfer_get(const char *type, const char *name, const char *filename, GError **err); struct obc_transfer *obc_transfer_put(const char *type, const char *name, const char *filename, const void *contents, size_t size, GError **err); gboolean obc_transfer_register(struct obc_transfer *transfer, DBusConnection *conn, const char *session, const char *owner, GError **err); void obc_transfer_unregister(struct obc_transfer *transfer); gboolean obc_transfer_set_callback(struct obc_transfer *transfer, transfer_callback_t func, void *user_data); gboolean obc_transfer_start(struct obc_transfer *transfer, void *obex, GError **err); guint8 obc_transfer_get_operation(struct obc_transfer *transfer); void obc_transfer_set_apparam(struct obc_transfer *transfer, void *data); void *obc_transfer_get_apparam(struct obc_transfer *transfer); int obc_transfer_get_contents(struct obc_transfer *transfer, char **contents, size_t *size); const char *obc_transfer_get_path(struct obc_transfer *transfer); gint64 obc_transfer_get_size(struct obc_transfer *transfer); DBusMessage *obc_transfer_create_dbus_reply(struct obc_transfer *transfer, DBusMessage *message); void obc_transfer_add_header(struct obc_transfer *transfer, void *data); bluez-5.82/obexd/client/PaxHeaders/ftp.h0000644000000000000000000000005014015011623015160 xustar0020 atime=1743516742 20 ctime=1743591283 bluez-5.82/obexd/client/ftp.h0000644000000000000000000000030014015011623014632 0ustar00rootroot/* SPDX-License-Identifier: GPL-2.0-or-later */ /* * * OBEX Client * * Copyright (C) 2007-2010 Marcel Holtmann * * */ int ftp_init(void); void ftp_exit(void); bluez-5.82/obexd/client/PaxHeaders/transport.c0000644000000000000000000000005014015011623016416 xustar0020 atime=1743516763 20 ctime=1743591283 bluez-5.82/obexd/client/transport.c0000644000000000000000000000250114015011623016075 0ustar00rootroot// SPDX-License-Identifier: GPL-2.0-or-later /* * * OBEX Server * * Copyright (C) 2012 Intel Corporation * * */ #ifdef HAVE_CONFIG_H #include #endif #define _GNU_SOURCE #include #include #include #include #include "obexd/src/log.h" #include "transport.h" static GSList *transports = NULL; struct obc_transport *obc_transport_find(const char *name) { GSList *l; for (l = transports; l; l = l->next) { struct obc_transport *transport = l->data; if (strcasecmp(name, transport->name) == 0) return transport; } return NULL; } int obc_transport_register(struct obc_transport *transport) { if (!transport) { error("Invalid transport"); return -EINVAL; } if (obc_transport_find(transport->name)) { error("Permission denied: transport %s already registered", transport->name); return -EPERM; } DBG("transport %p name %s registered", transport, transport->name); transports = g_slist_append(transports, transport); return 0; } void obc_transport_unregister(struct obc_transport *transport) { if (!g_slist_find(transports, transport)) { error("Unable to unregister: No such transport %p", transport); return; } DBG("transport %p name %s unregistered", transport, transport->name); transports = g_slist_remove(transports, transport); } bluez-5.82/obexd/client/PaxHeaders/opp.h0000644000000000000000000000005014015011623015165 xustar0020 atime=1743516742 20 ctime=1743591283 bluez-5.82/obexd/client/opp.h0000644000000000000000000000024614015011623014650 0ustar00rootroot/* SPDX-License-Identifier: GPL-2.0-or-later */ /* * * OBEX Client * * Copyright (C) 2011 Intel Corporation * * */ int opp_init(void); void opp_exit(void); bluez-5.82/obexd/client/PaxHeaders/bip.h0000644000000000000000000000005014711225434015153 xustar0020 atime=1743516742 20 ctime=1743591283 bluez-5.82/obexd/client/bip.h0000644000000000000000000000024414711225434014634 0ustar00rootroot/* SPDX-License-Identifier: GPL-2.0-or-later */ /* * * OBEX Client * * Copyright (C) 2024 Collabora Ltd. * * */ int bip_init(void); void bip_exit(void); bluez-5.82/obexd/client/PaxHeaders/mns.c0000644000000000000000000000005014572354773015207 xustar0020 atime=1743516730 20 ctime=1743591283 bluez-5.82/obexd/client/mns.c0000644000000000000000000002076314572354773014700 0ustar00rootroot// SPDX-License-Identifier: GPL-2.0-or-later /* * * OBEX Server * * Copyright (C) 2013 BMW Car IT GmbH. All rights reserved. * * */ #ifdef HAVE_CONFIG_H #include #endif #define _GNU_SOURCE #include #include #include #include #include #include #include #include "gobex/gobex.h" #include "gobex/gobex-apparam.h" #include "obexd/src/obexd.h" #include "obexd/src/plugin.h" #include "obexd/src/log.h" #include "obexd/src/obex.h" #include "obexd/src/service.h" #include "obexd/src/mimetype.h" #include "obexd/src/map_ap.h" #include "map-event.h" #include "obexd/src/manager.h" struct mns_session { GString *buffer; GObexApparam *inparams; char *remote_address; uint8_t mas_instance_id; }; static const uint8_t MNS_TARGET[TARGET_SIZE] = { 0xbb, 0x58, 0x2b, 0x41, 0x42, 0x0c, 0x11, 0xdb, 0xb0, 0xde, 0x08, 0x00, 0x20, 0x0c, 0x9a, 0x66 }; static int get_params(struct obex_session *os, struct mns_session *mns) { const uint8_t *buffer; ssize_t size; size = obex_get_apparam(os, &buffer); if (size < 0) size = 0; mns->inparams = g_obex_apparam_decode(buffer, size); if (mns->inparams == NULL) { DBG("Error when parsing parameters!"); return -EBADR; } return 0; } static void reset_request(struct mns_session *mns) { if (mns->buffer) { g_string_free(mns->buffer, TRUE); mns->buffer = NULL; } if (mns->inparams) { g_obex_apparam_free(mns->inparams); mns->inparams = NULL; } } static void mns_session_free(struct mns_session *mns) { reset_request(mns); if (mns->remote_address) g_free(mns->remote_address); g_free(mns); } static void *mns_connect(struct obex_session *os, int *err) { struct mns_session *mns; char *address; manager_register_session(os); mns = g_new0(struct mns_session, 1); if (obex_getpeername(os, &address) == 0) { mns->remote_address = g_strdup(address); g_free(address); } DBG("MNS connected to %s", mns->remote_address); if (err) *err = 0; return mns; } static void mns_disconnect(struct obex_session *os, void *user_data) { struct mns_session *mns = user_data; DBG("MNS disconnected from %s", mns->remote_address); manager_unregister_session(os); mns_session_free(mns); } static int mns_put(struct obex_session *os, void *user_data) { struct mns_session *mns = user_data; const char *type = obex_get_type(os); const char *name = obex_get_name(os); int ret; DBG("PUT: name %s type %s mns %p", name, type, mns); if (type == NULL) return -EBADR; ret = get_params(os, mns); if (ret < 0) goto failed; ret = obex_put_stream_start(os, name); if (ret < 0) goto failed; return 0; failed: reset_request(mns); return ret; } static void parse_event_report_type(struct map_event *event, const char *value) { if (!g_ascii_strcasecmp(value, "NewMessage")) event->type = MAP_ET_NEW_MESSAGE; else if (!g_ascii_strcasecmp(value, "DeliverySuccess")) event->type = MAP_ET_DELIVERY_SUCCESS; else if (!g_ascii_strcasecmp(value, "SendingSuccess")) event->type = MAP_ET_SENDING_SUCCESS; else if (!g_ascii_strcasecmp(value, "DeliveryFailure")) event->type = MAP_ET_DELIVERY_FAILURE; else if (!g_ascii_strcasecmp(value, "SendingFailure")) event->type = MAP_ET_SENDING_FAILURE; else if (!g_ascii_strcasecmp(value, "MemoryFull")) event->type = MAP_ET_MEMORY_FULL; else if (!g_ascii_strcasecmp(value, "MemoryAvailable")) event->type = MAP_ET_MEMORY_AVAILABLE; else if (!g_ascii_strcasecmp(value, "MessageDeleted")) event->type = MAP_ET_MESSAGE_DELETED; else if (!g_ascii_strcasecmp(value, "MessageShift")) event->type = MAP_ET_MESSAGE_SHIFT; } static void parse_event_report_handle(struct map_event *event, const char *value) { event->handle = strtoull(value, NULL, 16); } static void parse_event_report_folder(struct map_event *event, const char *value) { g_free(event->folder); if (g_str_has_prefix(value, "/")) event->folder = g_strdup(value); else event->folder = g_strconcat("/", value, NULL); } static void parse_event_report_old_folder(struct map_event *event, const char *value) { g_free(event->old_folder); if (g_str_has_prefix(value, "/")) event->old_folder = g_strdup(value); else event->old_folder = g_strconcat("/", value, NULL); } static void parse_event_report_msg_type(struct map_event *event, const char *value) { g_free(event->msg_type); event->msg_type = g_strdup(value); } static void parse_event_report_date_time(struct map_event *event, const char *value) { g_free(event->datetime); event->datetime = g_strdup(value); } static void parse_event_report_subject(struct map_event *event, const char *value) { g_free(event->subject); event->subject = g_strdup(value); } static void parse_event_report_sender_name(struct map_event *event, const char *value) { g_free(event->sender_name); event->sender_name = g_strdup(value); } static void parse_event_report_priority(struct map_event *event, const char *value) { g_free(event->priority); event->priority = g_strdup(value); } static const struct map_event_report_parser { const char *name; void (*func) (struct map_event *event, const char *value); } event_report_parsers[] = { { "type", parse_event_report_type }, { "handle", parse_event_report_handle }, { "folder", parse_event_report_folder }, { "old_folder", parse_event_report_old_folder }, { "msg_type", parse_event_report_msg_type }, { "datetime", parse_event_report_date_time }, { "subject", parse_event_report_subject }, { "sender_name", parse_event_report_sender_name }, { "priority", parse_event_report_priority }, { } }; static void event_report_element(GMarkupParseContext *ctxt, const char *element, const char **names, const char **values, gpointer user_data, GError **gerr) { struct map_event *event = user_data; const char *key; int i; if (strcasecmp("event", element) != 0) return; for (i = 0, key = names[i]; key; key = names[++i]) { const struct map_event_report_parser *parser; for (parser = event_report_parsers; parser && parser->name; parser++) { if (strcasecmp(key, parser->name) == 0) { if (values[i]) parser->func(event, values[i]); break; } } } } static const GMarkupParser event_report_parser = { event_report_element, NULL, NULL, NULL, NULL }; static void map_event_free(struct map_event *event) { g_free(event->folder); g_free(event->old_folder); g_free(event->msg_type); g_free(event->datetime); g_free(event->subject); g_free(event->sender_name); g_free(event->priority); g_free(event); } static void *event_report_open(const char *name, int oflag, mode_t mode, void *driver_data, size_t *size, int *err) { struct mns_session *mns = driver_data; DBG(""); g_obex_apparam_get_uint8(mns->inparams, MAP_AP_MASINSTANCEID, &mns->mas_instance_id); mns->buffer = g_string_new(""); if (err != NULL) *err = 0; return mns; } static int event_report_close(void *obj) { struct mns_session *mns = obj; GMarkupParseContext *ctxt; struct map_event *event; DBG(""); event = g_new0(struct map_event, 1); ctxt = g_markup_parse_context_new(&event_report_parser, 0, event, NULL); g_markup_parse_context_parse(ctxt, mns->buffer->str, mns->buffer->len, NULL); g_markup_parse_context_free(ctxt); map_dispatch_event(mns->mas_instance_id, mns->remote_address, event); map_event_free(event); reset_request(mns); return 0; } static ssize_t event_report_write(void *obj, const void *buf, size_t count) { struct mns_session *mns = obj; DBG(""); g_string_append_len(mns->buffer, buf, count); return count; } static const struct obex_service_driver mns = { .name = "Message Notification server", .service = OBEX_MNS, .target = MNS_TARGET, .target_size = TARGET_SIZE, .connect = mns_connect, .put = mns_put, .disconnect = mns_disconnect, }; static const struct obex_mime_type_driver mime_event_report = { .target = MNS_TARGET, .target_size = TARGET_SIZE, .mimetype = "x-bt/MAP-event-report", .open = event_report_open, .close = event_report_close, .write = event_report_write, }; static int mns_init(void) { int err; err = obex_mime_type_driver_register(&mime_event_report); if (err < 0) goto fail_mime_event; err = obex_service_driver_register(&mns); if (err < 0) goto fail_mns_reg; return 0; fail_mns_reg: obex_mime_type_driver_unregister(&mime_event_report); fail_mime_event: return err; } static void mns_exit(void) { obex_service_driver_unregister(&mns); obex_mime_type_driver_unregister(&mime_event_report); } OBEX_PLUGIN_DEFINE(mns, mns_init, mns_exit) bluez-5.82/obexd/client/PaxHeaders/opp.c0000644000000000000000000000005014766002272015175 xustar0020 atime=1743515578 20 ctime=1743591283 bluez-5.82/obexd/client/opp.c0000644000000000000000000001027114766002272014657 0ustar00rootroot// SPDX-License-Identifier: GPL-2.0-or-later /* * * OBEX Client * * Copyright (C) 2011 Intel Corporation * * */ #ifdef HAVE_CONFIG_H #include #endif #include #include "gdbus/gdbus.h" #include "obexd/src/log.h" #include "obexd/src/obexd.h" #include "transfer.h" #include "session.h" #include "driver.h" #include "opp.h" #define OPP_UUID "00001105-0000-1000-8000-00805f9b34fb" #define OPP_INTERFACE "org.bluez.obex.ObjectPush1" #define ERROR_INTERFACE "org.bluez.obex.Error" struct opp_data { struct obc_session *session; }; static DBusConnection *conn = NULL; static DBusMessage *opp_send_file(DBusConnection *connection, DBusMessage *message, void *user_data) { struct opp_data *opp = user_data; struct obc_transfer *transfer; DBusMessage *reply; char *filename; char *basename; GError *err = NULL; if (dbus_message_get_args(message, NULL, DBUS_TYPE_STRING, &filename, DBUS_TYPE_INVALID) == FALSE) return g_dbus_create_error(message, ERROR_INTERFACE ".InvalidArguments", NULL); basename = g_path_get_basename(filename); transfer = obc_transfer_put(NULL, basename, filename, NULL, 0, &err); g_free(basename); if (transfer == NULL) goto fail; if (!obc_session_queue(opp->session, transfer, NULL, NULL, &err)) goto fail; return obc_transfer_create_dbus_reply(transfer, message); fail: reply = g_dbus_create_error(message, ERROR_INTERFACE ".Failed", "%s", err->message); g_error_free(err); return reply; } static DBusMessage *opp_pull_business_card(DBusConnection *connection, DBusMessage *message, void *user_data) { struct opp_data *opp = user_data; struct obc_transfer *pull; DBusMessage *reply; const char *filename = NULL; GError *err = NULL; if (dbus_message_get_args(message, NULL, DBUS_TYPE_STRING, &filename, DBUS_TYPE_INVALID) == FALSE) return g_dbus_create_error(message, ERROR_INTERFACE ".InvalidArguments", NULL); pull = obc_transfer_get("text/x-vcard", NULL, filename, &err); if (pull == NULL) goto fail; if (!obc_session_queue(opp->session, pull, NULL, NULL, &err)) goto fail; return obc_transfer_create_dbus_reply(pull, message); fail: reply = g_dbus_create_error(message, ERROR_INTERFACE ".Failed", "%s", err->message); g_error_free(err); return reply; } static DBusMessage *opp_exchange_business_cards(DBusConnection *connection, DBusMessage *message, void *user_data) { return g_dbus_create_error(message, ERROR_INTERFACE ".Failed", "Not Implemented"); } static const GDBusMethodTable opp_methods[] = { { GDBUS_METHOD("SendFile", GDBUS_ARGS({ "sourcefile", "s" }), GDBUS_ARGS({ "transfer", "o" }, { "properties", "a{sv}" }), opp_send_file) }, { GDBUS_METHOD("PullBusinessCard", GDBUS_ARGS({ "targetfile", "s" }), GDBUS_ARGS({ "transfer", "o" }, { "properties", "a{sv}" }), opp_pull_business_card) }, { GDBUS_METHOD("ExchangeBusinessCards", GDBUS_ARGS({ "clientfile", "s" }, { "targetfile", "s" }), GDBUS_ARGS({ "transfer", "o" }, { "properties", "a{sv}" }), opp_exchange_business_cards) }, { } }; static void opp_free(void *data) { struct opp_data *opp = data; obc_session_unref(opp->session); g_free(opp); } static int opp_probe(struct obc_session *session) { struct opp_data *opp; const char *path; path = obc_session_get_path(session); DBG("%s", path); opp = g_try_new0(struct opp_data, 1); if (!opp) return -ENOMEM; opp->session = obc_session_ref(session); if (!g_dbus_register_interface(conn, path, OPP_INTERFACE, opp_methods, NULL, NULL, opp, opp_free)) { opp_free(opp); return -ENOMEM; } return 0; } static void opp_remove(struct obc_session *session) { const char *path = obc_session_get_path(session); DBG("%s", path); g_dbus_unregister_interface(conn, path, OPP_INTERFACE); } static struct obc_driver opp = { .service = "OPP", .uuid = OPP_UUID, .probe = opp_probe, .remove = opp_remove }; int opp_init(void) { int err; DBG(""); conn = obex_get_dbus_connection(); if (!conn) return -EIO; err = obc_driver_register(&opp); if (err < 0) { dbus_connection_unref(conn); conn = NULL; return err; } return 0; } void opp_exit(void) { DBG(""); dbus_connection_unref(conn); conn = NULL; obc_driver_unregister(&opp); } bluez-5.82/obexd/client/PaxHeaders/transport.h0000644000000000000000000000005014015011623016423 xustar0020 atime=1743516743 20 ctime=1743591283 bluez-5.82/obexd/client/transport.h0000644000000000000000000000136414015011623016110 0ustar00rootroot/* SPDX-License-Identifier: GPL-2.0-or-later */ /* * * OBEX Server * * Copyright (C) 2012 Intel Corporation * * */ typedef void (*obc_transport_func)(GIOChannel *io, GError *err, gpointer user_data); struct obc_transport { const char *name; guint (*connect) (const char *source, const char *destination, const char *service, uint16_t port, obc_transport_func func, void *user_data); int (*getpacketopt) (GIOChannel *io, int *tx_mtu, int *rx_mtu); void (*disconnect) (guint id); const void *(*getattribute) (guint id, int attribute_id); }; int obc_transport_register(struct obc_transport *transport); void obc_transport_unregister(struct obc_transport *transport); struct obc_transport *obc_transport_find(const char *name); bluez-5.82/obexd/client/PaxHeaders/sync.h0000644000000000000000000000005014015011623015343 xustar0020 atime=1743516742 20 ctime=1743591283 bluez-5.82/obexd/client/sync.h0000644000000000000000000000036114015011623015024 0ustar00rootroot/* SPDX-License-Identifier: GPL-2.0-or-later */ /* * * OBEX Client * * Copyright (C) 2007-2010 Intel Corporation * Copyright (C) 2007-2010 Marcel Holtmann * * */ int sync_init(void); void sync_exit(void); bluez-5.82/obexd/client/PaxHeaders/ftp.c0000644000000000000000000000005014766002272015170 xustar0020 atime=1743515578 20 ctime=1743591283 bluez-5.82/obexd/client/ftp.c0000644000000000000000000002753014766002272014660 0ustar00rootroot// SPDX-License-Identifier: GPL-2.0-or-later /* * * OBEX Client * * Copyright (C) 2007-2010 Marcel Holtmann * * */ #ifdef HAVE_CONFIG_H #include #endif #define _GNU_SOURCE #include #include #include "gdbus/gdbus.h" #include "obexd/src/log.h" #include "obexd/src/obexd.h" #include "transfer.h" #include "session.h" #include "driver.h" #include "ftp.h" #define OBEX_FTP_UUID \ "\xF9\xEC\x7B\xC4\x95\x3C\x11\xD2\x98\x4E\x52\x54\x00\xDC\x9E\x09" #define OBEX_FTP_UUID_LEN 16 #define FTP_INTERFACE "org.bluez.obex.FileTransfer1" #define ERROR_INTERFACE "org.bluez.obex.Error" #define FTP_UUID "00001106-0000-1000-8000-00805f9b34fb" #define PCSUITE_UUID "00005005-0000-1000-8000-0002ee000001" static DBusConnection *conn = NULL; struct ftp_data { struct obc_session *session; }; static void async_cb(struct obc_session *session, struct obc_transfer *transfer, GError *err, void *user_data) { DBusMessage *reply, *msg = user_data; if (err != NULL) reply = g_dbus_create_error(msg, ERROR_INTERFACE ".Failed", "%s", err->message); else reply = dbus_message_new_method_return(msg); g_dbus_send_message(conn, reply); dbus_message_unref(msg); } static DBusMessage *change_folder(DBusConnection *connection, DBusMessage *message, void *user_data) { struct ftp_data *ftp = user_data; struct obc_session *session = ftp->session; const char *folder; GError *err = NULL; if (dbus_message_get_args(message, NULL, DBUS_TYPE_STRING, &folder, DBUS_TYPE_INVALID) == FALSE) return g_dbus_create_error(message, ERROR_INTERFACE ".InvalidArguments", NULL); obc_session_setpath(session, folder, async_cb, message, &err); if (err != NULL) { DBusMessage *reply; reply = g_dbus_create_error(message, ERROR_INTERFACE ".Failed", "%s", err->message); g_error_free(err); return reply; } dbus_message_ref(message); return NULL; } static void xml_element(GMarkupParseContext *ctxt, const char *element, const char **names, const char **values, gpointer user_data, GError **gerr) { DBusMessageIter dict, *iter = user_data; char *key; int i; if (strcasecmp("folder", element) != 0 && strcasecmp("file", element) != 0) return; dbus_message_iter_open_container(iter, DBUS_TYPE_ARRAY, DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING DBUS_TYPE_STRING_AS_STRING DBUS_TYPE_VARIANT_AS_STRING DBUS_DICT_ENTRY_END_CHAR_AS_STRING, &dict); g_dbus_dict_append_entry(&dict, "Type", DBUS_TYPE_STRING, &element); /* FIXME: User, Group, Other permission must be reviewed */ i = 0; for (key = (char *) names[i]; key; key = (char *) names[++i]) { key[0] = g_ascii_toupper(key[0]); if (g_str_equal("Size", key) == TRUE) { guint64 size; size = g_ascii_strtoll(values[i], NULL, 10); g_dbus_dict_append_entry(&dict, key, DBUS_TYPE_UINT64, &size); } else g_dbus_dict_append_entry(&dict, key, DBUS_TYPE_STRING, &values[i]); } dbus_message_iter_close_container(iter, &dict); } static const GMarkupParser parser = { xml_element, NULL, NULL, NULL, NULL }; static void list_folder_callback(struct obc_session *session, struct obc_transfer *transfer, GError *err, void *user_data) { DBusMessage *msg = user_data; GMarkupParseContext *ctxt; DBusMessage *reply; DBusMessageIter iter, array; char *contents; size_t size; reply = dbus_message_new_method_return(msg); if (obc_transfer_get_contents(transfer, &contents, &size) < 0) goto done; dbus_message_iter_init_append(reply, &iter); dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY, DBUS_TYPE_ARRAY_AS_STRING DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING DBUS_TYPE_STRING_AS_STRING DBUS_TYPE_VARIANT_AS_STRING DBUS_DICT_ENTRY_END_CHAR_AS_STRING, &array); ctxt = g_markup_parse_context_new(&parser, 0, &array, NULL); g_markup_parse_context_parse(ctxt, contents, size, NULL); g_markup_parse_context_free(ctxt); dbus_message_iter_close_container(&iter, &array); g_free(contents); done: g_dbus_send_message(conn, reply); dbus_message_unref(msg); } static DBusMessage *create_folder(DBusConnection *connection, DBusMessage *message, void *user_data) { struct ftp_data *ftp = user_data; struct obc_session *session = ftp->session; const char *folder; GError *err = NULL; if (dbus_message_get_args(message, NULL, DBUS_TYPE_STRING, &folder, DBUS_TYPE_INVALID) == FALSE) return g_dbus_create_error(message, ERROR_INTERFACE ".InvalidArguments", NULL); obc_session_mkdir(session, folder, async_cb, message, &err); if (err != NULL) { DBusMessage *reply; reply = g_dbus_create_error(message, ERROR_INTERFACE ".Failed", "%s", err->message); g_error_free(err); return reply; } dbus_message_ref(message); return NULL; } static DBusMessage *list_folder(DBusConnection *connection, DBusMessage *message, void *user_data) { struct ftp_data *ftp = user_data; struct obc_session *session = ftp->session; struct obc_transfer *transfer; GError *err = NULL; DBusMessage *reply; transfer = obc_transfer_get("x-obex/folder-listing", NULL, NULL, &err); if (transfer == NULL) goto fail; if (obc_session_queue(session, transfer, list_folder_callback, message, &err)) { dbus_message_ref(message); return NULL; } fail: reply = g_dbus_create_error(message, ERROR_INTERFACE ".Failed", "%s", err->message); g_error_free(err); return reply; } static DBusMessage *get_file(DBusConnection *connection, DBusMessage *message, void *user_data) { struct ftp_data *ftp = user_data; struct obc_session *session = ftp->session; struct obc_transfer *transfer; const char *target_file, *source_file; GError *err = NULL; DBusMessage *reply; if (dbus_message_get_args(message, NULL, DBUS_TYPE_STRING, &target_file, DBUS_TYPE_STRING, &source_file, DBUS_TYPE_INVALID) == FALSE) return g_dbus_create_error(message, ERROR_INTERFACE ".InvalidArguments", NULL); transfer = obc_transfer_get(NULL, source_file, target_file, &err); if (transfer == NULL) goto fail; if (!obc_session_queue(session, transfer, NULL, NULL, &err)) goto fail; return obc_transfer_create_dbus_reply(transfer, message); fail: reply = g_dbus_create_error(message, ERROR_INTERFACE ".Failed", "%s", err->message); g_error_free(err); return reply; } static DBusMessage *put_file(DBusConnection *connection, DBusMessage *message, void *user_data) { struct ftp_data *ftp = user_data; struct obc_session *session = ftp->session; struct obc_transfer *transfer; char *sourcefile, *targetfile; GError *err = NULL; DBusMessage *reply; if (dbus_message_get_args(message, NULL, DBUS_TYPE_STRING, &sourcefile, DBUS_TYPE_STRING, &targetfile, DBUS_TYPE_INVALID) == FALSE) return g_dbus_create_error(message, ERROR_INTERFACE ".InvalidArguments", "Invalid arguments in method call"); transfer = obc_transfer_put(NULL, targetfile, sourcefile, NULL, 0, &err); if (transfer == NULL) goto fail; if (!obc_session_queue(session, transfer, NULL, NULL, &err)) goto fail; return obc_transfer_create_dbus_reply(transfer, message); fail: reply = g_dbus_create_error(message, ERROR_INTERFACE ".Failed", "%s", err->message); g_error_free(err); return reply; } static DBusMessage *copy_file(DBusConnection *connection, DBusMessage *message, void *user_data) { struct ftp_data *ftp = user_data; struct obc_session *session = ftp->session; const char *filename, *destname; GError *err = NULL; if (dbus_message_get_args(message, NULL, DBUS_TYPE_STRING, &filename, DBUS_TYPE_STRING, &destname, DBUS_TYPE_INVALID) == FALSE) return g_dbus_create_error(message, ERROR_INTERFACE ".InvalidArguments", NULL); obc_session_copy(session, filename, destname, async_cb, message, &err); if (err != NULL) { DBusMessage *reply; reply = g_dbus_create_error(message, ERROR_INTERFACE ".Failed", "%s", err->message); g_error_free(err); return reply; } dbus_message_ref(message); return NULL; } static DBusMessage *move_file(DBusConnection *connection, DBusMessage *message, void *user_data) { struct ftp_data *ftp = user_data; struct obc_session *session = ftp->session; const char *filename, *destname; GError *err = NULL; if (dbus_message_get_args(message, NULL, DBUS_TYPE_STRING, &filename, DBUS_TYPE_STRING, &destname, DBUS_TYPE_INVALID) == FALSE) return g_dbus_create_error(message, ERROR_INTERFACE ".InvalidArguments", NULL); obc_session_move(session, filename, destname, async_cb, message, &err); if (err != NULL) { DBusMessage *reply; reply = g_dbus_create_error(message, ERROR_INTERFACE ".Failed", "%s", err->message); g_error_free(err); return reply; } dbus_message_ref(message); return NULL; } static DBusMessage *delete(DBusConnection *connection, DBusMessage *message, void *user_data) { struct ftp_data *ftp = user_data; struct obc_session *session = ftp->session; const char *file; GError *err = NULL; if (dbus_message_get_args(message, NULL, DBUS_TYPE_STRING, &file, DBUS_TYPE_INVALID) == FALSE) return g_dbus_create_error(message, ERROR_INTERFACE ".InvalidArguments", NULL); obc_session_delete(session, file, async_cb, message, &err); if (err != NULL) { DBusMessage *reply; reply = g_dbus_create_error(message, ERROR_INTERFACE ".Failed", "%s", err->message); g_error_free(err); return reply; } dbus_message_ref(message); return NULL; } static const GDBusMethodTable ftp_methods[] = { { GDBUS_ASYNC_METHOD("ChangeFolder", GDBUS_ARGS({ "folder", "s" }), NULL, change_folder) }, { GDBUS_ASYNC_METHOD("CreateFolder", GDBUS_ARGS({ "folder", "s" }), NULL, create_folder) }, { GDBUS_ASYNC_METHOD("ListFolder", NULL, GDBUS_ARGS({ "folderinfo", "aa{sv}" }), list_folder) }, { GDBUS_METHOD("GetFile", GDBUS_ARGS({ "targetfile", "s" }, { "sourcefile", "s" }), GDBUS_ARGS({ "transfer", "o" }, { "properties", "a{sv}" }), get_file) }, { GDBUS_METHOD("PutFile", GDBUS_ARGS({ "sourcefile", "s" }, { "targetfile", "s" }), GDBUS_ARGS({ "transfer", "o" }, { "properties", "a{sv}" }), put_file) }, { GDBUS_ASYNC_METHOD("CopyFile", GDBUS_ARGS({ "sourcefile", "s" }, { "targetfile", "s" }), NULL, copy_file) }, { GDBUS_ASYNC_METHOD("MoveFile", GDBUS_ARGS({ "sourcefile", "s" }, { "targetfile", "s" }), NULL, move_file) }, { GDBUS_ASYNC_METHOD("Delete", GDBUS_ARGS({ "file", "s" }), NULL, delete) }, { } }; static void ftp_free(void *data) { struct ftp_data *ftp = data; obc_session_unref(ftp->session); g_free(ftp); } static int ftp_probe(struct obc_session *session) { struct ftp_data *ftp; const char *path; path = obc_session_get_path(session); DBG("%s", path); ftp = g_try_new0(struct ftp_data, 1); if (!ftp) return -ENOMEM; ftp->session = obc_session_ref(session); if (!g_dbus_register_interface(conn, path, FTP_INTERFACE, ftp_methods, NULL, NULL, ftp, ftp_free)) { ftp_free(ftp); return -ENOMEM; } return 0; } static void ftp_remove(struct obc_session *session) { const char *path = obc_session_get_path(session); DBG("%s", path); g_dbus_unregister_interface(conn, path, FTP_INTERFACE); } static struct obc_driver ftp = { .service = "FTP", .uuid = FTP_UUID, .target = OBEX_FTP_UUID, .target_len = OBEX_FTP_UUID_LEN, .probe = ftp_probe, .remove = ftp_remove }; static struct obc_driver pcsuite = { .service = "PCSUITE", .uuid = PCSUITE_UUID, .target = OBEX_FTP_UUID, .target_len = OBEX_FTP_UUID_LEN, .probe = ftp_probe, .remove = ftp_remove }; int ftp_init(void) { int err; DBG(""); conn = obex_get_dbus_connection(); if (!conn) return -EIO; err = obc_driver_register(&ftp); if (err < 0) goto failed; err = obc_driver_register(&pcsuite); if (err < 0) { obc_driver_unregister(&ftp); goto failed; } return 0; failed: dbus_connection_unref(conn); conn = NULL; return err; } void ftp_exit(void) { DBG(""); dbus_connection_unref(conn); conn = NULL; obc_driver_unregister(&ftp); obc_driver_unregister(&pcsuite); } bluez-5.82/obexd/client/PaxHeaders/map.c0000644000000000000000000000005014772767672015177 xustar0020 atime=1743515579 20 ctime=1743591283 bluez-5.82/obexd/client/map.c0000644000000000000000000015707014772767672014672 0ustar00rootroot// SPDX-License-Identifier: GPL-2.0-or-later /* * * OBEX Client * * Copyright (C) 2011 Bartosz Szatkowski for Comarch * */ #ifdef HAVE_CONFIG_H #include #endif #define _GNU_SOURCE #include #include #include #include #include #include #include #include "lib/sdp.h" #include "gobex/gobex-apparam.h" #include "gdbus/gdbus.h" #include "obexd/src/log.h" #include "obexd/src/obexd.h" #include "obexd/src/map_ap.h" #include "map-event.h" #include "map.h" #include "transfer.h" #include "session.h" #include "driver.h" #define OBEX_MAS_UUID \ "\xBB\x58\x2B\x40\x42\x0C\x11\xDB\xB0\xDE\x08\x00\x20\x0C\x9A\x66" #define OBEX_MAS_UUID_LEN 16 #define SUPPORTED_FEATURES_TAG 0x29 #define NOTIFICATION_REGISTRATION_FEATURE 0x00000001 #define NOTIFICATION_FEATURE 0x00000002 #define BROWSING_FEATURE 0x00000004 #define UPLOADING_FEATURE 0x00000008 #define DELETE_FEATURE 0x00000010 #define INSTANCE_INFORMATION_FEATURE 0x00000020 #define EXTENDED_EVENT_REPORT_1_1 0x00000040 #define MESSAGES_LISTING_FORMAT_VERSION_1_1 0x00000200 #define MAPSUPPORTEDFEATURES_IN_CONNECT_REQUEST 0x00080000 #define MAP_INTERFACE "org.bluez.obex.MessageAccess1" #define MAP_MSG_INTERFACE "org.bluez.obex.Message1" #define ERROR_INTERFACE "org.bluez.obex.Error" #define MAS_UUID "00001132-0000-1000-8000-00805f9b34fb" #define DEFAULT_COUNT 1024 #define DEFAULT_OFFSET 0 #define CHARSET_NATIVE 0 #define CHARSET_UTF8 1 #define SDP_MESSAGE_TYPE_EMAIL 0x01 #define SDP_MESSAGE_TYPE_SMS_GSM 0x02 #define SDP_MESSAGE_TYPE_SMS_CDMA 0x04 #define SDP_MESSAGE_TYPE_MMS 0x08 #define SDP_MESSAGE_TYPE_IM 0x10 static const char * const filter_list[] = { "subject", "timestamp", "sender", "sender-address", "recipient", "recipient-address", "type", "size", "status", "text", "attachment", "priority", "read", "sent", "protected", "replyto", NULL }; #define FILTER_BIT_MAX 15 #define FILTER_ALL 0x0000FFFF #define FILTER_READ_STATUS_NONE 0x00 #define FILTER_READ_STATUS_ONLY_UNREAD 0x01 #define FILTER_READ_STATUS_ONLY_READ 0x02 #define FILTER_PRIORITY_NONE 0x00 #define FILTER_PRIORITY_ONLY_HIGH 0x01 #define FILTER_PRIORITY_ONLY_NONHIGH 0x02 #define STATUS_READ 0 #define STATUS_DELETE 1 #define FILLER_BYTE 0x30 struct map_data { struct obc_session *session; GHashTable *messages; int16_t mas_instance_id; uint8_t supported_message_types; uint32_t supported_features; }; struct pending_request { struct map_data *map; DBusMessage *msg; char *folder; }; #define MAP_MSG_FLAG_PRIORITY 0x01 #define MAP_MSG_FLAG_READ 0x02 #define MAP_MSG_FLAG_SENT 0x04 #define MAP_MSG_FLAG_PROTECTED 0x08 #define MAP_MSG_FLAG_TEXT 0x10 struct map_msg { struct map_data *data; char *path; uint64_t handle; char *subject; char *timestamp; char *sender; char *sender_address; char *replyto; char *recipient; char *recipient_address; char *type; uint64_t size; char *status; uint64_t attachment_size; uint8_t flags; char *folder; char *delivery_status; uint64_t conversation_id; char *conversation_name; char *direction; char *attachment_mime_types; GDBusPendingPropertySet pending; }; struct map_parser { struct pending_request *request; DBusMessageIter *iter; }; static DBusConnection *conn = NULL; static struct pending_request *pending_request_new(struct map_data *map, DBusMessage *message) { struct pending_request *p; p = g_new0(struct pending_request, 1); p->map = map; p->msg = dbus_message_ref(message); return p; } static void pending_request_free(struct pending_request *p) { dbus_message_unref(p->msg); g_free(p->folder); g_free(p); } static void simple_cb(struct obc_session *session, struct obc_transfer *transfer, GError *err, void *user_data) { struct pending_request *request = user_data; DBusMessage *reply; if (err != NULL) reply = g_dbus_create_error(request->msg, ERROR_INTERFACE ".Failed", "%s", err->message); else reply = dbus_message_new_method_return(request->msg); g_dbus_send_message(conn, reply); pending_request_free(request); } static DBusMessage *map_setpath(DBusConnection *connection, DBusMessage *message, void *user_data) { struct map_data *map = user_data; const char *folder; struct pending_request *request; GError *err = NULL; if (dbus_message_get_args(message, NULL, DBUS_TYPE_STRING, &folder, DBUS_TYPE_INVALID) == FALSE) return g_dbus_create_error(message, ERROR_INTERFACE ".InvalidArguments", NULL); request = pending_request_new(map, message); obc_session_setpath(map->session, folder, simple_cb, request, &err); if (err != NULL) { DBusMessage *reply; reply = g_dbus_create_error(message, ERROR_INTERFACE ".Failed", "%s", err->message); g_error_free(err); pending_request_free(request); return reply; } return NULL; } static void folder_element(GMarkupParseContext *ctxt, const char *element, const char **names, const char **values, gpointer user_data, GError **gerr) { DBusMessageIter dict, *iter = user_data; const char *key; int i; if (strcasecmp("folder", element) != 0) return; dbus_message_iter_open_container(iter, DBUS_TYPE_ARRAY, DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING DBUS_TYPE_STRING_AS_STRING DBUS_TYPE_VARIANT_AS_STRING DBUS_DICT_ENTRY_END_CHAR_AS_STRING, &dict); for (i = 0, key = names[i]; key; key = names[++i]) { if (strcasecmp("name", key) == 0) g_dbus_dict_append_entry(&dict, "Name", DBUS_TYPE_STRING, &values[i]); } dbus_message_iter_close_container(iter, &dict); } static const GMarkupParser folder_parser = { folder_element, NULL, NULL, NULL, NULL }; static void folder_listing_cb(struct obc_session *session, struct obc_transfer *transfer, GError *err, void *user_data) { struct pending_request *request = user_data; GMarkupParseContext *ctxt; DBusMessage *reply; DBusMessageIter iter, array; char *contents; size_t size; int perr; if (err != NULL) { reply = g_dbus_create_error(request->msg, ERROR_INTERFACE ".Failed", "%s", err->message); goto done; } perr = obc_transfer_get_contents(transfer, &contents, &size); if (perr < 0) { reply = g_dbus_create_error(request->msg, ERROR_INTERFACE ".Failed", "Error reading contents: %s", strerror(-perr)); goto done; } reply = dbus_message_new_method_return(request->msg); if (reply == NULL) { g_free(contents); goto clean; } dbus_message_iter_init_append(reply, &iter); dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY, DBUS_TYPE_ARRAY_AS_STRING DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING DBUS_TYPE_STRING_AS_STRING DBUS_TYPE_VARIANT_AS_STRING DBUS_DICT_ENTRY_END_CHAR_AS_STRING, &array); ctxt = g_markup_parse_context_new(&folder_parser, 0, &array, NULL); g_markup_parse_context_parse(ctxt, contents, size, NULL); g_markup_parse_context_free(ctxt); dbus_message_iter_close_container(&iter, &array); g_free(contents); done: g_dbus_send_message(conn, reply); clean: pending_request_free(request); } static DBusMessage *get_folder_listing(struct map_data *map, DBusMessage *message, GObexApparam *apparam) { struct pending_request *request; struct obc_transfer *transfer; GError *err = NULL; DBusMessage *reply; transfer = obc_transfer_get("x-obex/folder-listing", NULL, NULL, &err); if (transfer == NULL) { g_obex_apparam_free(apparam); goto fail; } obc_transfer_set_apparam(transfer, apparam); request = pending_request_new(map, message); if (!obc_session_queue(map->session, transfer, folder_listing_cb, request, &err)) { pending_request_free(request); goto fail; } return NULL; fail: reply = g_dbus_create_error(message, ERROR_INTERFACE ".Failed", "%s", err->message); g_error_free(err); return reply; } static GObexApparam *parse_offset(GObexApparam *apparam, DBusMessageIter *iter) { guint16 num; if (dbus_message_iter_get_arg_type(iter) != DBUS_TYPE_UINT16) return NULL; dbus_message_iter_get_basic(iter, &num); return g_obex_apparam_set_uint16(apparam, MAP_AP_STARTOFFSET, num); } static GObexApparam *parse_max_count(GObexApparam *apparam, DBusMessageIter *iter) { guint16 num; if (dbus_message_iter_get_arg_type(iter) != DBUS_TYPE_UINT16) return NULL; dbus_message_iter_get_basic(iter, &num); return g_obex_apparam_set_uint16(apparam, MAP_AP_MAXLISTCOUNT, num); } static GObexApparam *parse_folder_filters(GObexApparam *apparam, DBusMessageIter *iter) { DBusMessageIter array; if (dbus_message_iter_get_arg_type(iter) != DBUS_TYPE_ARRAY) return NULL; dbus_message_iter_recurse(iter, &array); while (dbus_message_iter_get_arg_type(&array) == DBUS_TYPE_DICT_ENTRY) { const char *key; DBusMessageIter value, entry; dbus_message_iter_recurse(&array, &entry); dbus_message_iter_get_basic(&entry, &key); dbus_message_iter_next(&entry); dbus_message_iter_recurse(&entry, &value); if (strcasecmp(key, "Offset") == 0) { if (parse_offset(apparam, &value) == NULL) return NULL; } else if (strcasecmp(key, "MaxCount") == 0) { if (parse_max_count(apparam, &value) == NULL) return NULL; } dbus_message_iter_next(&array); } return apparam; } static DBusMessage *map_list_folders(DBusConnection *connection, DBusMessage *message, void *user_data) { struct map_data *map = user_data; GObexApparam *apparam; DBusMessageIter args; dbus_message_iter_init(message, &args); apparam = g_obex_apparam_set_uint16(NULL, MAP_AP_MAXLISTCOUNT, DEFAULT_COUNT); apparam = g_obex_apparam_set_uint16(apparam, MAP_AP_STARTOFFSET, DEFAULT_OFFSET); if (parse_folder_filters(apparam, &args) == NULL) { g_obex_apparam_free(apparam); return g_dbus_create_error(message, ERROR_INTERFACE ".InvalidArguments", NULL); } return get_folder_listing(map, message, apparam); } static void map_msg_free(void *data) { struct map_msg *msg = data; g_free(msg->path); g_free(msg->subject); g_free(msg->folder); g_free(msg->timestamp); g_free(msg->sender); g_free(msg->sender_address); g_free(msg->replyto); g_free(msg->recipient); g_free(msg->recipient_address); g_free(msg->type); g_free(msg->status); g_free(msg->delivery_status); g_free(msg->conversation_name); g_free(msg->direction); g_free(msg->attachment_mime_types); g_free(msg); } static DBusMessage *map_msg_get(DBusConnection *connection, DBusMessage *message, void *user_data) { struct map_msg *msg = user_data; struct obc_transfer *transfer; const char *target_file; gboolean attachment; GError *err = NULL; DBusMessage *reply; GObexApparam *apparam; char handle[17]; if (dbus_message_get_args(message, NULL, DBUS_TYPE_STRING, &target_file, DBUS_TYPE_BOOLEAN, &attachment, DBUS_TYPE_INVALID) == FALSE) return g_dbus_create_error(message, ERROR_INTERFACE ".InvalidArguments", NULL); snprintf(handle, sizeof(handle), "%" PRIx64, msg->handle); transfer = obc_transfer_get("x-bt/message", handle, target_file, &err); if (transfer == NULL) goto fail; apparam = g_obex_apparam_set_uint8(NULL, MAP_AP_ATTACHMENT, attachment); apparam = g_obex_apparam_set_uint8(apparam, MAP_AP_CHARSET, CHARSET_UTF8); obc_transfer_set_apparam(transfer, apparam); if (!obc_session_queue(msg->data->session, transfer, NULL, NULL, &err)) goto fail; return obc_transfer_create_dbus_reply(transfer, message); fail: reply = g_dbus_create_error(message, ERROR_INTERFACE ".Failed", "%s", err->message); g_error_free(err); return reply; } static void set_message_status_cb(struct obc_session *session, struct obc_transfer *transfer, GError *err, void *user_data) { struct map_msg *msg = user_data; if (err != NULL) { g_dbus_pending_property_error(msg->pending, ERROR_INTERFACE ".Failed", "%s", err->message); goto done; } g_dbus_pending_property_success(msg->pending); done: msg->pending = 0; } static gboolean get_folder(const GDBusPropertyTable *property, DBusMessageIter *iter, void *data) { struct map_msg *msg = data; dbus_message_iter_append_basic(iter, DBUS_TYPE_STRING, &msg->folder); return TRUE; } static gboolean subject_exists(const GDBusPropertyTable *property, void *data) { struct map_msg *msg = data; return msg->subject != NULL; } static gboolean get_subject(const GDBusPropertyTable *property, DBusMessageIter *iter, void *data) { struct map_msg *msg = data; dbus_message_iter_append_basic(iter, DBUS_TYPE_STRING, &msg->subject); return TRUE; } static gboolean timestamp_exists(const GDBusPropertyTable *property, void *data) { struct map_msg *msg = data; return msg->timestamp != NULL; } static gboolean get_timestamp(const GDBusPropertyTable *property, DBusMessageIter *iter, void *data) { struct map_msg *msg = data; dbus_message_iter_append_basic(iter, DBUS_TYPE_STRING, &msg->timestamp); return TRUE; } static gboolean sender_exists(const GDBusPropertyTable *property, void *data) { struct map_msg *msg = data; return msg->sender != NULL; } static gboolean get_sender(const GDBusPropertyTable *property, DBusMessageIter *iter, void *data) { struct map_msg *msg = data; dbus_message_iter_append_basic(iter, DBUS_TYPE_STRING, &msg->sender); return TRUE; } static gboolean sender_address_exists(const GDBusPropertyTable *property, void *data) { struct map_msg *msg = data; return msg->sender_address != NULL; } static gboolean get_sender_address(const GDBusPropertyTable *property, DBusMessageIter *iter, void *data) { struct map_msg *msg = data; dbus_message_iter_append_basic(iter, DBUS_TYPE_STRING, &msg->sender_address); return TRUE; } static gboolean replyto_exists(const GDBusPropertyTable *property, void *data) { struct map_msg *msg = data; return msg->replyto != NULL; } static gboolean get_replyto(const GDBusPropertyTable *property, DBusMessageIter *iter, void *data) { struct map_msg *msg = data; dbus_message_iter_append_basic(iter, DBUS_TYPE_STRING, &msg->replyto); return TRUE; } static gboolean recipient_exists(const GDBusPropertyTable *property, void *data) { struct map_msg *msg = data; return msg->recipient != NULL; } static gboolean get_recipient(const GDBusPropertyTable *property, DBusMessageIter *iter, void *data) { struct map_msg *msg = data; dbus_message_iter_append_basic(iter, DBUS_TYPE_STRING, &msg->recipient); return TRUE; } static gboolean recipient_address_exists(const GDBusPropertyTable *property, void *data) { struct map_msg *msg = data; return msg->recipient_address != NULL; } static gboolean get_recipient_address(const GDBusPropertyTable *property, DBusMessageIter *iter, void *data) { struct map_msg *msg = data; dbus_message_iter_append_basic(iter, DBUS_TYPE_STRING, &msg->recipient_address); return TRUE; } static gboolean type_exists(const GDBusPropertyTable *property, void *data) { struct map_msg *msg = data; return msg->type != NULL; } static gboolean get_type(const GDBusPropertyTable *property, DBusMessageIter *iter, void *data) { struct map_msg *msg = data; dbus_message_iter_append_basic(iter, DBUS_TYPE_STRING, &msg->type); return TRUE; } static gboolean get_size(const GDBusPropertyTable *property, DBusMessageIter *iter, void *data) { struct map_msg *msg = data; dbus_message_iter_append_basic(iter, DBUS_TYPE_UINT64, &msg->size); return TRUE; } static gboolean reception_status_exists(const GDBusPropertyTable *property, void *data) { struct map_msg *msg = data; return msg->status != NULL; } static gboolean get_reception_status(const GDBusPropertyTable *property, DBusMessageIter *iter, void *data) { struct map_msg *msg = data; dbus_message_iter_append_basic(iter, DBUS_TYPE_STRING, &msg->status); return TRUE; } static gboolean get_attachment_size(const GDBusPropertyTable *property, DBusMessageIter *iter, void *data) { struct map_msg *msg = data; dbus_message_iter_append_basic(iter, DBUS_TYPE_UINT64, &msg->attachment_size); return TRUE; } static gboolean get_flag(const GDBusPropertyTable *property, DBusMessageIter *iter, uint8_t flag, void *data) { struct map_msg *msg = data; dbus_bool_t value = (msg->flags & flag) != 0; dbus_message_iter_append_basic(iter, DBUS_TYPE_BOOLEAN, &value); return TRUE; } static gboolean get_priority(const GDBusPropertyTable *property, DBusMessageIter *iter, void *data) { return get_flag(property, iter, MAP_MSG_FLAG_PRIORITY, data); } static gboolean get_read(const GDBusPropertyTable *property, DBusMessageIter *iter, void *data) { return get_flag(property, iter, MAP_MSG_FLAG_READ, data); } static gboolean get_sent(const GDBusPropertyTable *property, DBusMessageIter *iter, void *data) { return get_flag(property, iter, MAP_MSG_FLAG_SENT, data); } static gboolean get_protected(const GDBusPropertyTable *property, DBusMessageIter *iter, void *data) { return get_flag(property, iter, MAP_MSG_FLAG_PROTECTED, data); } static gboolean get_text(const GDBusPropertyTable *property, DBusMessageIter *iter, void *data) { return get_flag(property, iter, MAP_MSG_FLAG_TEXT, data); } static void set_status(const GDBusPropertyTable *property, DBusMessageIter *iter, GDBusPendingPropertySet id, uint8_t status, void *data) { struct map_msg *msg = data; struct obc_transfer *transfer; gboolean value; GError *err = NULL; GObexApparam *apparam; char contents[1]; char handle[17]; if (dbus_message_iter_get_arg_type(iter) != DBUS_TYPE_BOOLEAN) { g_dbus_pending_property_error(id, ERROR_INTERFACE ".InvalidArguments", "Invalid arguments in method call"); return; } dbus_message_iter_get_basic(iter, &value); contents[0] = FILLER_BYTE; snprintf(handle, sizeof(handle), "%" PRIx64, msg->handle); transfer = obc_transfer_put("x-bt/messageStatus", handle, NULL, contents, sizeof(contents), &err); if (transfer == NULL) goto fail; apparam = g_obex_apparam_set_uint8(NULL, MAP_AP_STATUSINDICATOR, status); apparam = g_obex_apparam_set_uint8(apparam, MAP_AP_STATUSVALUE, value); obc_transfer_set_apparam(transfer, apparam); if (!obc_session_queue(msg->data->session, transfer, set_message_status_cb, msg, &err)) goto fail; msg->pending = id; return; fail: g_dbus_pending_property_error(id, ERROR_INTERFACE ".Failed", "%s", err->message); g_error_free(err); } static void set_read(const GDBusPropertyTable *property, DBusMessageIter *iter, GDBusPendingPropertySet id, void *data) { set_status(property, iter, id, STATUS_READ, data); } static void set_deleted(const GDBusPropertyTable *property, DBusMessageIter *iter, GDBusPendingPropertySet id, void *data) { set_status(property, iter, id, STATUS_DELETE, data); } static gboolean delivery_status_exists(const GDBusPropertyTable *property, void *data) { struct map_msg *msg = data; return msg->delivery_status != NULL; } static gboolean get_delivery_status(const GDBusPropertyTable *property, DBusMessageIter *iter, void *data) { struct map_msg *msg = data; dbus_message_iter_append_basic(iter, DBUS_TYPE_STRING, &msg->delivery_status); return TRUE; } static gboolean get_conversation_id(const GDBusPropertyTable *property, DBusMessageIter *iter, void *data) { struct map_msg *msg = data; dbus_message_iter_append_basic(iter, DBUS_TYPE_UINT64, &msg->conversation_id); return TRUE; } static gboolean conversation_name_exists(const GDBusPropertyTable *property, void *data) { struct map_msg *msg = data; return msg->conversation_name != NULL; } static gboolean get_conversation_name(const GDBusPropertyTable *property, DBusMessageIter *iter, void *data) { struct map_msg *msg = data; dbus_message_iter_append_basic(iter, DBUS_TYPE_STRING, &msg->conversation_name); return TRUE; } static gboolean direction_exists(const GDBusPropertyTable *property, void *data) { struct map_msg *msg = data; return msg->direction != NULL; } static gboolean get_direction(const GDBusPropertyTable *property, DBusMessageIter *iter, void *data) { struct map_msg *msg = data; dbus_message_iter_append_basic(iter, DBUS_TYPE_STRING, &msg->direction); return TRUE; } static gboolean attachment_mime_exists(const GDBusPropertyTable *property, void *data) { struct map_msg *msg = data; return msg->attachment_mime_types != NULL; } static gboolean get_attachment_mime_types(const GDBusPropertyTable *property, DBusMessageIter *iter, void *data) { struct map_msg *msg = data; dbus_message_iter_append_basic(iter, DBUS_TYPE_STRING, &msg->attachment_mime_types); return TRUE; } static const GDBusMethodTable map_msg_methods[] = { { GDBUS_METHOD("Get", GDBUS_ARGS({ "targetfile", "s" }, { "attachment", "b" }), GDBUS_ARGS({ "transfer", "o" }, { "properties", "a{sv}" }), map_msg_get) }, { } }; static const GDBusPropertyTable map_msg_properties[] = { { "Folder", "s", get_folder }, { "Subject", "s", get_subject, NULL, subject_exists }, { "Timestamp", "s", get_timestamp, NULL, timestamp_exists }, { "Sender", "s", get_sender, NULL, sender_exists }, { "SenderAddress", "s", get_sender_address, NULL, sender_address_exists }, { "ReplyTo", "s", get_replyto, NULL, replyto_exists }, { "Recipient", "s", get_recipient, NULL, recipient_exists }, { "RecipientAddress", "s", get_recipient_address, NULL, recipient_address_exists }, { "Type", "s", get_type, NULL, type_exists }, { "Size", "t", get_size }, { "Text", "b", get_text }, { "Status", "s", get_reception_status, NULL, reception_status_exists }, { "AttachmentSize", "t", get_attachment_size }, { "Priority", "b", get_priority }, { "Read", "b", get_read, set_read }, { "Sent", "b", get_sent }, { "Protected", "b", get_protected }, { "Deleted", "b", NULL, set_deleted }, { "DeliveryStatus", "s", get_delivery_status, NULL, delivery_status_exists }, { "ConversationId", "t", get_conversation_id }, { "ConversationName", "s", get_conversation_name, NULL, conversation_name_exists }, { "Direction", "s", get_direction, NULL, direction_exists }, { "AttachmentMimeTypes", "s", get_attachment_mime_types, NULL, attachment_mime_exists }, { } }; static void parse_type(struct map_msg *msg, const char *value) { const char *type = NULL; if (strcasecmp(value, "SMS_GSM") == 0) type = "sms-gsm"; else if (strcasecmp(value, "SMS_CDMA") == 0) type = "sms-cdma"; else if (strcasecmp(value, "EMAIL") == 0) type = "email"; else if (strcasecmp(value, "MMS") == 0) type = "mms"; if (g_strcmp0(msg->type, type) == 0) return; g_free(msg->type); msg->type = g_strdup(type); g_dbus_emit_property_changed(conn, msg->path, MAP_MSG_INTERFACE, "Type"); } static struct map_msg *map_msg_create(struct map_data *data, uint64_t handle, const char *folder, const char *type) { struct map_msg *msg; msg = g_new0(struct map_msg, 1); msg->data = data; msg->handle = handle; msg->path = g_strdup_printf("%s/message%" PRIu64, obc_session_get_path(data->session), msg->handle); msg->folder = g_strdup(folder); if (!g_dbus_register_interface(conn, msg->path, MAP_MSG_INTERFACE, map_msg_methods, NULL, map_msg_properties, msg, map_msg_free)) { map_msg_free(msg); return NULL; } g_hash_table_insert(data->messages, &msg->handle, msg); if (type) parse_type(msg, type); return msg; } static void parse_subject(struct map_msg *msg, const char *value) { if (g_strcmp0(msg->subject, value) == 0) return; g_free(msg->subject); msg->subject = g_strdup(value); g_dbus_emit_property_changed(conn, msg->path, MAP_MSG_INTERFACE, "Subject"); } static void parse_datetime(struct map_msg *msg, const char *value) { if (g_strcmp0(msg->timestamp, value) == 0) return; g_free(msg->timestamp); msg->timestamp = g_strdup(value); g_dbus_emit_property_changed(conn, msg->path, MAP_MSG_INTERFACE, "Timestamp"); } static void parse_sender(struct map_msg *msg, const char *value) { if (g_strcmp0(msg->sender, value) == 0) return; g_free(msg->sender); msg->sender = g_strdup(value); g_dbus_emit_property_changed(conn, msg->path, MAP_MSG_INTERFACE, "Sender"); } static void parse_sender_address(struct map_msg *msg, const char *value) { if (g_strcmp0(msg->sender_address, value) == 0) return; g_free(msg->sender_address); msg->sender_address = g_strdup(value); g_dbus_emit_property_changed(conn, msg->path, MAP_MSG_INTERFACE, "SenderAddress"); } static void parse_replyto(struct map_msg *msg, const char *value) { if (g_strcmp0(msg->replyto, value) == 0) return; g_free(msg->replyto); msg->replyto = g_strdup(value); g_dbus_emit_property_changed(conn, msg->path, MAP_MSG_INTERFACE, "ReplyTo"); } static void parse_recipient(struct map_msg *msg, const char *value) { if (g_strcmp0(msg->recipient, value) == 0) return; g_free(msg->recipient); msg->recipient = g_strdup(value); g_dbus_emit_property_changed(conn, msg->path, MAP_MSG_INTERFACE, "Recipient"); } static void parse_recipient_address(struct map_msg *msg, const char *value) { if (g_strcmp0(msg->recipient_address, value) == 0) return; g_free(msg->recipient_address); msg->recipient_address = g_strdup(value); g_dbus_emit_property_changed(conn, msg->path, MAP_MSG_INTERFACE, "RecipientAddress"); } static void parse_size(struct map_msg *msg, const char *value) { uint64_t size = g_ascii_strtoll(value, NULL, 10); if (msg->size == size) return; msg->size = size; g_dbus_emit_property_changed(conn, msg->path, MAP_MSG_INTERFACE, "Size"); } static void parse_text(struct map_msg *msg, const char *value) { gboolean flag = strcasecmp(value, "no") != 0; uint8_t oldflags = msg->flags; if (flag) msg->flags |= MAP_MSG_FLAG_TEXT; else msg->flags &= ~MAP_MSG_FLAG_TEXT; if (msg->flags != oldflags) g_dbus_emit_property_changed(conn, msg->path, MAP_MSG_INTERFACE, "Text"); } static void parse_status(struct map_msg *msg, const char *value) { if (g_strcmp0(msg->status, value) == 0) return; g_free(msg->status); msg->status = g_strdup(value); g_dbus_emit_property_changed(conn, msg->path, MAP_MSG_INTERFACE, "Status"); } static void parse_attachment_size(struct map_msg *msg, const char *value) { uint64_t attachment_size = g_ascii_strtoll(value, NULL, 10); if (msg->attachment_size == attachment_size) return; msg->attachment_size = attachment_size; g_dbus_emit_property_changed(conn, msg->path, MAP_MSG_INTERFACE, "AttachmentSize"); } static void parse_priority(struct map_msg *msg, const char *value) { gboolean flag = strcasecmp(value, "no") != 0; uint8_t oldflags = msg->flags; if (flag) msg->flags |= MAP_MSG_FLAG_PRIORITY; else msg->flags &= ~MAP_MSG_FLAG_PRIORITY; if (msg->flags != oldflags) g_dbus_emit_property_changed(conn, msg->path, MAP_MSG_INTERFACE, "Priority"); } static void parse_read(struct map_msg *msg, const char *value) { gboolean flag = strcasecmp(value, "no") != 0; uint8_t oldflags = msg->flags; if (flag) msg->flags |= MAP_MSG_FLAG_READ; else msg->flags &= ~MAP_MSG_FLAG_READ; if (msg->flags != oldflags) g_dbus_emit_property_changed(conn, msg->path, MAP_MSG_INTERFACE, "Read"); } static void parse_sent(struct map_msg *msg, const char *value) { gboolean flag = strcasecmp(value, "no") != 0; uint8_t oldflags = msg->flags; if (flag) msg->flags |= MAP_MSG_FLAG_SENT; else msg->flags &= ~MAP_MSG_FLAG_SENT; if (msg->flags != oldflags) g_dbus_emit_property_changed(conn, msg->path, MAP_MSG_INTERFACE, "Sent"); } static void parse_protected(struct map_msg *msg, const char *value) { gboolean flag = strcasecmp(value, "no") != 0; uint8_t oldflags = msg->flags; if (flag) msg->flags |= MAP_MSG_FLAG_PROTECTED; else msg->flags &= ~MAP_MSG_FLAG_PROTECTED; if (msg->flags != oldflags) g_dbus_emit_property_changed(conn, msg->path, MAP_MSG_INTERFACE, "Protected"); } static void parse_delivery_status(struct map_msg *msg, const char *value) { if (g_strcmp0(msg->delivery_status, value) == 0) return; g_free(msg->delivery_status); msg->delivery_status = g_strdup(value); g_dbus_emit_property_changed(conn, msg->path, MAP_MSG_INTERFACE, "DeliveryStatus"); } static void parse_conversation_id(struct map_msg *msg, const char *value) { uint64_t conversation_id = strtoull(value, NULL, 16); if (msg->conversation_id == conversation_id) return; msg->conversation_id = conversation_id; g_dbus_emit_property_changed(conn, msg->path, MAP_MSG_INTERFACE, "ConversationId"); } static void parse_conversation_name(struct map_msg *msg, const char *value) { if (g_strcmp0(msg->conversation_name, value) == 0) return; g_free(msg->conversation_name); msg->conversation_name = g_strdup(value); g_dbus_emit_property_changed(conn, msg->path, MAP_MSG_INTERFACE, "ConversationName"); } static void parse_direction(struct map_msg *msg, const char *value) { if (g_strcmp0(msg->direction, value) == 0) return; g_free(msg->direction); msg->direction = g_strdup(value); g_dbus_emit_property_changed(conn, msg->path, MAP_MSG_INTERFACE, "Direction"); } static void parse_mime_types(struct map_msg *msg, const char *value) { if (g_strcmp0(msg->attachment_mime_types, value) == 0) return; g_free(msg->attachment_mime_types); msg->attachment_mime_types = g_strdup(value); g_dbus_emit_property_changed(conn, msg->path, MAP_MSG_INTERFACE, "AttachmentMimeTypes"); } static const struct map_msg_parser { const char *name; void (*func) (struct map_msg *msg, const char *value); } msg_parsers[] = { { "subject", parse_subject }, { "datetime", parse_datetime }, { "sender_name", parse_sender }, { "sender_addressing", parse_sender_address }, { "replyto_addressing", parse_replyto }, { "recipient_name", parse_recipient }, { "recipient_addressing", parse_recipient_address }, { "type", parse_type }, { "size", parse_size }, { "text", parse_text }, { "reception_status", parse_status }, { "attachment_size", parse_attachment_size }, { "priority", parse_priority }, { "read", parse_read }, { "sent", parse_sent }, { "protected", parse_protected }, { "delivery_status", parse_delivery_status}, { "conversation_id", parse_conversation_id}, { "conversation_name", parse_conversation_name}, { "direction", parse_direction}, { "attachment_mime_types", parse_mime_types}, { } }; static void msg_element(GMarkupParseContext *ctxt, const char *element, const char **names, const char **values, gpointer user_data, GError **gerr) { struct map_parser *parser = user_data; struct map_data *data = parser->request->map; DBusMessageIter entry, *iter = parser->iter; struct map_msg *msg; const char *key; int i; uint64_t handle; if (strcasecmp("msg", element) != 0) return; for (i = 0, key = names[i]; key; key = names[++i]) { if (strcasecmp(key, "handle") == 0) break; } handle = strtoull(values[i], NULL, 16); msg = g_hash_table_lookup(data->messages, &handle); if (msg == NULL) { msg = map_msg_create(data, handle, parser->request->folder, NULL); if (msg == NULL) return; } dbus_message_iter_open_container(iter, DBUS_TYPE_DICT_ENTRY, NULL, &entry); dbus_message_iter_append_basic(&entry, DBUS_TYPE_OBJECT_PATH, &msg->path); for (i = 0, key = names[i]; key; key = names[++i]) { const struct map_msg_parser *parser; for (parser = msg_parsers; parser && parser->name; parser++) { if (strcasecmp(key, parser->name) == 0) { if (values[i]) parser->func(msg, values[i]); break; } } } g_dbus_get_properties(conn, msg->path, MAP_MSG_INTERFACE, &entry); dbus_message_iter_close_container(iter, &entry); } static const GMarkupParser msg_parser = { msg_element, NULL, NULL, NULL, NULL }; static void message_listing_cb(struct obc_session *session, struct obc_transfer *transfer, GError *err, void *user_data) { struct pending_request *request = user_data; struct map_parser *parser; GMarkupParseContext *ctxt; DBusMessage *reply; DBusMessageIter iter, array; char *contents; size_t size; int perr; if (err != NULL) { reply = g_dbus_create_error(request->msg, ERROR_INTERFACE ".Failed", "%s", err->message); goto done; } perr = obc_transfer_get_contents(transfer, &contents, &size); if (perr < 0) { reply = g_dbus_create_error(request->msg, ERROR_INTERFACE ".Failed", "Error reading contents: %s", strerror(-perr)); goto done; } reply = dbus_message_new_method_return(request->msg); if (reply == NULL) { g_free(contents); goto clean; } dbus_message_iter_init_append(reply, &iter); dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY, DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING DBUS_TYPE_OBJECT_PATH_AS_STRING DBUS_TYPE_ARRAY_AS_STRING DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING DBUS_TYPE_STRING_AS_STRING DBUS_TYPE_VARIANT_AS_STRING DBUS_DICT_ENTRY_END_CHAR_AS_STRING DBUS_DICT_ENTRY_END_CHAR_AS_STRING, &array); parser = g_new(struct map_parser, 1); parser->request = request; parser->iter = &array; ctxt = g_markup_parse_context_new(&msg_parser, 0, parser, NULL); g_markup_parse_context_parse(ctxt, contents, size, NULL); g_markup_parse_context_free(ctxt); dbus_message_iter_close_container(&iter, &array); g_free(contents); g_free(parser); done: g_dbus_send_message(conn, reply); clean: pending_request_free(request); } static char *get_absolute_folder(struct map_data *map, const char *subfolder) { const char *root = obc_session_get_folder(map->session); if (!subfolder || strlen(subfolder) == 0) return g_strdup(root); else if (g_str_has_suffix(root, "/")) return g_strconcat(root, subfolder, NULL); else return g_strconcat(root, "/", subfolder, NULL); } static DBusMessage *get_message_listing(struct map_data *map, DBusMessage *message, const char *folder, GObexApparam *apparam) { struct pending_request *request; struct obc_transfer *transfer; GError *err = NULL; DBusMessage *reply; transfer = obc_transfer_get("x-bt/MAP-msg-listing", folder, NULL, &err); if (transfer == NULL) { g_obex_apparam_free(apparam); goto fail; } obc_transfer_set_apparam(transfer, apparam); request = pending_request_new(map, message); request->folder = get_absolute_folder(map, folder); if (!obc_session_queue(map->session, transfer, message_listing_cb, request, &err)) { pending_request_free(request); goto fail; } return NULL; fail: reply = g_dbus_create_error(message, ERROR_INTERFACE ".Failed", "%s", err->message); g_error_free(err); return reply; } static GObexApparam *parse_subject_length(GObexApparam *apparam, DBusMessageIter *iter) { guint8 num; if (dbus_message_iter_get_arg_type(iter) != DBUS_TYPE_BYTE) return NULL; dbus_message_iter_get_basic(iter, &num); return g_obex_apparam_set_uint8(apparam, MAP_AP_SUBJECTLENGTH, num); } static uint64_t get_filter_mask(const char *filterstr) { int i; if (!filterstr) return 0; if (!g_ascii_strcasecmp(filterstr, "ALL")) return FILTER_ALL; for (i = 0; filter_list[i] != NULL; i++) if (!g_ascii_strcasecmp(filterstr, filter_list[i])) return 1ULL << i; return 0; } static int set_field(guint32 *filter, const char *filterstr) { guint64 mask; mask = get_filter_mask(filterstr); if (mask == 0) return -EINVAL; *filter |= mask; return 0; } static GObexApparam *parse_fields(GObexApparam *apparam, DBusMessageIter *iter) { DBusMessageIter array; guint32 filter = 0; if (dbus_message_iter_get_arg_type(iter) != DBUS_TYPE_ARRAY) return NULL; dbus_message_iter_recurse(iter, &array); while (dbus_message_iter_get_arg_type(&array) == DBUS_TYPE_STRING) { const char *string; dbus_message_iter_get_basic(&array, &string); if (set_field(&filter, string) < 0) return NULL; dbus_message_iter_next(&array); } return g_obex_apparam_set_uint32(apparam, MAP_AP_PARAMETERMASK, filter); } static GObexApparam *parse_filter_type(GObexApparam *apparam, DBusMessageIter *iter) { DBusMessageIter array; guint8 types = 0; if (dbus_message_iter_get_arg_type(iter) != DBUS_TYPE_ARRAY) return NULL; dbus_message_iter_recurse(iter, &array); while (dbus_message_iter_get_arg_type(&array) == DBUS_TYPE_STRING) { const char *string; dbus_message_iter_get_basic(&array, &string); if (!g_ascii_strcasecmp(string, "sms")) types |= 0x03; /* sms-gsm and sms-cdma */ else if (!g_ascii_strcasecmp(string, "email")) types |= 0x04; /* email */ else if (!g_ascii_strcasecmp(string, "mms")) types |= 0x08; /* mms */ else return NULL; dbus_message_iter_next(&array); } return g_obex_apparam_set_uint8(apparam, MAP_AP_FILTERMESSAGETYPE, types); } static GObexApparam *parse_period_begin(GObexApparam *apparam, DBusMessageIter *iter) { const char *string; if (dbus_message_iter_get_arg_type(iter) != DBUS_TYPE_STRING) return NULL; dbus_message_iter_get_basic(iter, &string); return g_obex_apparam_set_string(apparam, MAP_AP_FILTERPERIODBEGIN, string); } static GObexApparam *parse_period_end(GObexApparam *apparam, DBusMessageIter *iter) { const char *string; if (dbus_message_iter_get_arg_type(iter) != DBUS_TYPE_STRING) return NULL; dbus_message_iter_get_basic(iter, &string); return g_obex_apparam_set_string(apparam, MAP_AP_FILTERPERIODEND, string); } static GObexApparam *parse_filter_read(GObexApparam *apparam, DBusMessageIter *iter) { guint8 status = FILTER_READ_STATUS_NONE; dbus_bool_t dbus_status = FALSE; if (dbus_message_iter_get_arg_type(iter) != DBUS_TYPE_BOOLEAN) return NULL; dbus_message_iter_get_basic(iter, &dbus_status); if (dbus_status) status = FILTER_READ_STATUS_ONLY_READ; else status = FILTER_READ_STATUS_ONLY_UNREAD; return g_obex_apparam_set_uint8(apparam, MAP_AP_FILTERREADSTATUS, status); } static GObexApparam *parse_filter_recipient(GObexApparam *apparam, DBusMessageIter *iter) { const char *string; if (dbus_message_iter_get_arg_type(iter) != DBUS_TYPE_STRING) return NULL; dbus_message_iter_get_basic(iter, &string); return g_obex_apparam_set_string(apparam, MAP_AP_FILTERRECIPIENT, string); } static GObexApparam *parse_filter_sender(GObexApparam *apparam, DBusMessageIter *iter) { const char *string; if (dbus_message_iter_get_arg_type(iter) != DBUS_TYPE_STRING) return NULL; dbus_message_iter_get_basic(iter, &string); return g_obex_apparam_set_string(apparam, MAP_AP_FILTERORIGINATOR, string); } static GObexApparam *parse_filter_priority(GObexApparam *apparam, DBusMessageIter *iter) { guint8 priority = FILTER_PRIORITY_NONE; dbus_bool_t dbus_priority = FALSE; if (dbus_message_iter_get_arg_type(iter) != DBUS_TYPE_BOOLEAN) return NULL; dbus_message_iter_get_basic(iter, &dbus_priority); if (dbus_priority) priority = FILTER_PRIORITY_ONLY_HIGH; else priority = FILTER_PRIORITY_ONLY_NONHIGH; return g_obex_apparam_set_uint8(apparam, MAP_AP_FILTERPRIORITY, priority); } static GObexApparam *parse_message_filters(GObexApparam *apparam, DBusMessageIter *iter) { DBusMessageIter array; DBG(""); if (dbus_message_iter_get_arg_type(iter) != DBUS_TYPE_ARRAY) return NULL; dbus_message_iter_recurse(iter, &array); while (dbus_message_iter_get_arg_type(&array) == DBUS_TYPE_DICT_ENTRY) { const char *key; DBusMessageIter value, entry; dbus_message_iter_recurse(&array, &entry); dbus_message_iter_get_basic(&entry, &key); dbus_message_iter_next(&entry); dbus_message_iter_recurse(&entry, &value); if (strcasecmp(key, "Offset") == 0) { if (parse_offset(apparam, &value) == NULL) return NULL; } else if (strcasecmp(key, "MaxCount") == 0) { if (parse_max_count(apparam, &value) == NULL) return NULL; } else if (strcasecmp(key, "SubjectLength") == 0) { if (parse_subject_length(apparam, &value) == NULL) return NULL; } else if (strcasecmp(key, "Fields") == 0) { if (parse_fields(apparam, &value) == NULL) return NULL; } else if (strcasecmp(key, "Types") == 0) { if (parse_filter_type(apparam, &value) == NULL) return NULL; } else if (strcasecmp(key, "PeriodBegin") == 0) { if (parse_period_begin(apparam, &value) == NULL) return NULL; } else if (strcasecmp(key, "PeriodEnd") == 0) { if (parse_period_end(apparam, &value) == NULL) return NULL; } else if (strcasecmp(key, "Read") == 0) { if (parse_filter_read(apparam, &value) == NULL) return NULL; } else if (strcasecmp(key, "Recipient") == 0) { if (parse_filter_recipient(apparam, &value) == NULL) return NULL; } else if (strcasecmp(key, "Sender") == 0) { if (parse_filter_sender(apparam, &value) == NULL) return NULL; } else if (strcasecmp(key, "Priority") == 0) { if (parse_filter_priority(apparam, &value) == NULL) return NULL; } dbus_message_iter_next(&array); } return apparam; } static DBusMessage *map_list_messages(DBusConnection *connection, DBusMessage *message, void *user_data) { struct map_data *map = user_data; const char *folder; GObexApparam *apparam; DBusMessageIter args; dbus_message_iter_init(message, &args); if (dbus_message_iter_get_arg_type(&args) != DBUS_TYPE_STRING) return g_dbus_create_error(message, ERROR_INTERFACE ".InvalidArguments", NULL); dbus_message_iter_get_basic(&args, &folder); apparam = g_obex_apparam_set_uint16(NULL, MAP_AP_MAXLISTCOUNT, DEFAULT_COUNT); apparam = g_obex_apparam_set_uint16(apparam, MAP_AP_STARTOFFSET, DEFAULT_OFFSET); dbus_message_iter_next(&args); if (parse_message_filters(apparam, &args) == NULL) { g_obex_apparam_free(apparam); return g_dbus_create_error(message, ERROR_INTERFACE ".InvalidArguments", NULL); } return get_message_listing(map, message, folder, apparam); } static char **get_filter_strs(uint64_t filter, int *size) { char **list, **item; int i; list = g_malloc0(sizeof(char **) * (FILTER_BIT_MAX + 2)); item = list; for (i = 0; filter_list[i] != NULL; i++) if (filter & (1ULL << i)) *(item++) = g_strdup(filter_list[i]); *item = NULL; *size = item - list; return list; } static DBusMessage *map_list_filter_fields(DBusConnection *connection, DBusMessage *message, void *user_data) { char **filters = NULL; int size; DBusMessage *reply; filters = get_filter_strs(FILTER_ALL, &size); reply = dbus_message_new_method_return(message); dbus_message_append_args(reply, DBUS_TYPE_ARRAY, DBUS_TYPE_STRING, &filters, size, DBUS_TYPE_INVALID); g_strfreev(filters); return reply; } static void update_inbox_cb(struct obc_session *session, struct obc_transfer *transfer, GError *err, void *user_data) { struct pending_request *request = user_data; DBusMessage *reply; if (err != NULL) { reply = g_dbus_create_error(request->msg, ERROR_INTERFACE ".Failed", "%s", err->message); goto done; } reply = dbus_message_new_method_return(request->msg); done: g_dbus_send_message(conn, reply); pending_request_free(request); } static DBusMessage *map_update_inbox(DBusConnection *connection, DBusMessage *message, void *user_data) { struct map_data *map = user_data; DBusMessage *reply; char contents[1]; struct obc_transfer *transfer; GError *err = NULL; struct pending_request *request; contents[0] = FILLER_BYTE; transfer = obc_transfer_put("x-bt/MAP-messageUpdate", NULL, NULL, contents, sizeof(contents), &err); if (transfer == NULL) goto fail; request = pending_request_new(map, message); if (!obc_session_queue(map->session, transfer, update_inbox_cb, request, &err)) { pending_request_free(request); goto fail; } return NULL; fail: reply = g_dbus_create_error(message, ERROR_INTERFACE ".Failed", "%s", err->message); g_error_free(err); return reply; } static DBusMessage *push_message(struct map_data *map, DBusMessage *message, const char *filename, const char *folder, GObexApparam *apparam) { struct obc_transfer *transfer; GError *err = NULL; DBusMessage *reply; transfer = obc_transfer_put("x-bt/message", folder, filename, NULL, 0, &err); if (transfer == NULL) { g_obex_apparam_free(apparam); goto fail; } obc_transfer_set_apparam(transfer, apparam); if (!obc_session_queue(map->session, transfer, NULL, NULL, &err)) goto fail; return obc_transfer_create_dbus_reply(transfer, message); fail: reply = g_dbus_create_error(message, ERROR_INTERFACE ".Failed", "%s", err->message); g_error_free(err); return reply; } static GObexApparam *parse_transparent(GObexApparam *apparam, DBusMessageIter *iter) { dbus_bool_t transparent; if (dbus_message_iter_get_arg_type(iter) != DBUS_TYPE_BOOLEAN) return NULL; dbus_message_iter_get_basic(iter, &transparent); return g_obex_apparam_set_uint8(apparam, MAP_AP_TRANSPARENT, transparent ? TRUE : FALSE); } static GObexApparam *parse_retry(GObexApparam *apparam, DBusMessageIter *iter) { dbus_bool_t retry; if (dbus_message_iter_get_arg_type(iter) != DBUS_TYPE_BOOLEAN) return NULL; dbus_message_iter_get_basic(iter, &retry); return g_obex_apparam_set_uint8(apparam, MAP_AP_RETRY, retry ? TRUE : FALSE); } static GObexApparam *parse_charset(GObexApparam *apparam, DBusMessageIter *iter) { guint8 charset = 0; const char *string; if (dbus_message_iter_get_arg_type(iter) != DBUS_TYPE_STRING) return NULL; dbus_message_iter_get_basic(iter, &string); if (strcasecmp(string, "native") == 0) charset = CHARSET_NATIVE; else if (strcasecmp(string, "utf8") == 0) charset = CHARSET_UTF8; else return NULL; return g_obex_apparam_set_uint8(apparam, MAP_AP_CHARSET, charset); } static GObexApparam *parse_push_options(GObexApparam *apparam, DBusMessageIter *iter) { DBusMessageIter array; if (dbus_message_iter_get_arg_type(iter) != DBUS_TYPE_ARRAY) return NULL; dbus_message_iter_recurse(iter, &array); while (dbus_message_iter_get_arg_type(&array) == DBUS_TYPE_DICT_ENTRY) { const char *key; DBusMessageIter value, entry; dbus_message_iter_recurse(&array, &entry); dbus_message_iter_get_basic(&entry, &key); dbus_message_iter_next(&entry); dbus_message_iter_recurse(&entry, &value); if (strcasecmp(key, "Transparent") == 0) { if (parse_transparent(apparam, &value) == NULL) return NULL; } else if (strcasecmp(key, "Retry") == 0) { if (parse_retry(apparam, &value) == NULL) return NULL; } else if (strcasecmp(key, "Charset") == 0) { if (parse_charset(apparam, &value) == NULL) return NULL; } dbus_message_iter_next(&array); } return apparam; } static DBusMessage *map_push_message(DBusConnection *connection, DBusMessage *message, void *user_data) { struct map_data *map = user_data; char *filename; char *folder; GObexApparam *apparam; DBusMessageIter args; dbus_message_iter_init(message, &args); if (dbus_message_iter_get_arg_type(&args) != DBUS_TYPE_STRING) return g_dbus_create_error(message, ERROR_INTERFACE ".InvalidArguments", NULL); dbus_message_iter_get_basic(&args, &filename); dbus_message_iter_next(&args); if (dbus_message_iter_get_arg_type(&args) != DBUS_TYPE_STRING) { return g_dbus_create_error(message, ERROR_INTERFACE ".InvalidArguments", NULL); } dbus_message_iter_get_basic(&args, &folder); dbus_message_iter_next(&args); apparam = g_obex_apparam_set_uint8(NULL, MAP_AP_CHARSET, CHARSET_UTF8); if (parse_push_options(apparam, &args) == NULL) { g_obex_apparam_free(apparam); return g_dbus_create_error(message, ERROR_INTERFACE ".InvalidArguments", NULL); } return push_message(map, message, filename, folder, apparam); } static const GDBusMethodTable map_methods[] = { { GDBUS_ASYNC_METHOD("SetFolder", GDBUS_ARGS({ "name", "s" }), NULL, map_setpath) }, { GDBUS_ASYNC_METHOD("ListFolders", GDBUS_ARGS({ "filters", "a{sv}" }), GDBUS_ARGS({ "content", "aa{sv}" }), map_list_folders) }, { GDBUS_ASYNC_METHOD("ListMessages", GDBUS_ARGS({ "folder", "s" }, { "filter", "a{sv}" }), GDBUS_ARGS({ "messages", "a{oa{sv}}" }), map_list_messages) }, { GDBUS_METHOD("ListFilterFields", NULL, GDBUS_ARGS({ "fields", "as" }), map_list_filter_fields) }, { GDBUS_ASYNC_METHOD("UpdateInbox", NULL, NULL, map_update_inbox) }, { GDBUS_ASYNC_METHOD("PushMessage", GDBUS_ARGS({ "file", "s" }, { "folder", "s" }, { "args", "a{sv}" }), GDBUS_ARGS({ "transfer", "o" }, { "properties", "a{sv}" }), map_push_message) }, { } }; static gboolean get_supported_types(const GDBusPropertyTable *property, DBusMessageIter *iter, void *user_data) { struct map_data *map = user_data; DBusMessageIter entry; const char *str; dbus_message_iter_open_container(iter, DBUS_TYPE_ARRAY, DBUS_TYPE_STRING_AS_STRING, &entry); if (map->supported_message_types & SDP_MESSAGE_TYPE_EMAIL) { str = "EMAIL"; dbus_message_iter_append_basic(&entry, DBUS_TYPE_STRING, &str); } if (map->supported_message_types & SDP_MESSAGE_TYPE_SMS_GSM) { str = "SMS_GSM"; dbus_message_iter_append_basic(&entry, DBUS_TYPE_STRING, &str); } if (map->supported_message_types & SDP_MESSAGE_TYPE_SMS_CDMA) { str = "SMS_CDMA"; dbus_message_iter_append_basic(&entry, DBUS_TYPE_STRING, &str); } if (map->supported_message_types & SDP_MESSAGE_TYPE_MMS) { str = "MMS"; dbus_message_iter_append_basic(&entry, DBUS_TYPE_STRING, &str); } if (map->supported_message_types & SDP_MESSAGE_TYPE_IM) { str = "IM"; dbus_message_iter_append_basic(&entry, DBUS_TYPE_STRING, &str); } dbus_message_iter_close_container(iter, &entry); return TRUE; } static const GDBusPropertyTable map_properties[] = { { "SupportedTypes", "as", get_supported_types }, { } }; static void map_msg_remove(void *data) { struct map_msg *msg = data; char *path; path = msg->path; msg->path = NULL; g_dbus_unregister_interface(conn, path, MAP_MSG_INTERFACE); g_free(path); } static void map_handle_new_message(struct map_data *map, struct map_event *event) { struct map_msg *msg; msg = g_hash_table_lookup(map->messages, &event->handle); /* New message event can be used if a new message replaces an old one */ if (msg) g_hash_table_remove(map->messages, &event->handle); map_msg_create(map, event->handle, event->folder, event->msg_type); } static void map_handle_status_changed(struct map_data *map, struct map_event *event, const char *status) { struct map_msg *msg; msg = g_hash_table_lookup(map->messages, &event->handle); if (msg == NULL) return; if (g_strcmp0(msg->status, status) == 0) return; g_free(msg->status); msg->status = g_strdup(status); g_dbus_emit_property_changed(conn, msg->path, MAP_MSG_INTERFACE, "Status"); } static void map_handle_folder_changed(struct map_data *map, struct map_event *event, const char *folder) { struct map_msg *msg; if (!folder) return; msg = g_hash_table_lookup(map->messages, &event->handle); if (!msg) return; if (g_strcmp0(msg->folder, folder) == 0) return; g_free(msg->folder); msg->folder = g_strdup(folder); g_dbus_emit_property_changed(conn, msg->path, MAP_MSG_INTERFACE, "Folder"); } static void map_handle_notification(struct map_event *event, void *user_data) { struct map_data *map = user_data; DBG("Event report for %s:%d", obc_session_get_destination(map->session), map->mas_instance_id); DBG("type=%x handle=%" PRIx64 " folder=%s old_folder=%s msg_type=%s", event->type, event->handle, event->folder, event->old_folder, event->msg_type); switch (event->type) { case MAP_ET_NEW_MESSAGE: map_handle_new_message(map, event); map_handle_status_changed(map, event, "notification"); break; case MAP_ET_DELIVERY_SUCCESS: map_handle_status_changed(map, event, "delivery-success"); break; case MAP_ET_SENDING_SUCCESS: map_handle_status_changed(map, event, "sending-success"); break; case MAP_ET_DELIVERY_FAILURE: map_handle_status_changed(map, event, "delivery-failure"); break; case MAP_ET_SENDING_FAILURE: map_handle_status_changed(map, event, "sending-failure"); break; case MAP_ET_MESSAGE_DELETED: map_handle_folder_changed(map, event, "/telecom/msg/deleted"); break; case MAP_ET_MESSAGE_SHIFT: map_handle_folder_changed(map, event, event->folder); break; case MAP_ET_MEMORY_FULL: case MAP_ET_MEMORY_AVAILABLE: default: break; } } static bool set_notification_registration(struct map_data *map, bool status) { struct obc_transfer *transfer; GError *err = NULL; GObexApparam *apparam; char contents[1]; const char *address; address = obc_session_get_destination(map->session); if (!address || map->mas_instance_id < 0) return FALSE; if (status) { map_register_event_handler(map->session, map->mas_instance_id, &map_handle_notification, map); } else { map_unregister_event_handler(map->session, map->mas_instance_id); } contents[0] = FILLER_BYTE; transfer = obc_transfer_put("x-bt/MAP-NotificationRegistration", NULL, NULL, contents, sizeof(contents), &err); if (transfer == NULL) return false; apparam = g_obex_apparam_set_uint8(NULL, MAP_AP_NOTIFICATIONSTATUS, status); obc_transfer_set_apparam(transfer, apparam); if (obc_session_queue(map->session, transfer, NULL, map, &err)) return true; return false; } static void map_free(void *data) { struct map_data *map = data; set_notification_registration(map, false); obc_session_unref(map->session); g_hash_table_unref(map->messages); g_free(map); } static void parse_service_record(struct map_data *map) { const void *data; /* MAS instance id */ map->mas_instance_id = -1; data = obc_session_get_attribute(map->session, SDP_ATTR_MAS_INSTANCE_ID); if (data != NULL) map->mas_instance_id = *(uint8_t *)data; else DBG("Failed to read MAS instance id"); /* Supported Message Types */ data = obc_session_get_attribute(map->session, SDP_ATTR_SUPPORTED_MESSAGE_TYPES); if (data != NULL) map->supported_message_types = *(uint8_t *)data; else DBG("Failed to read supported message types"); /* Supported Feature Bits */ data = obc_session_get_attribute(map->session, SDP_ATTR_MAP_SUPPORTED_FEATURES); if(data != NULL) map->supported_features = *(uint32_t *) data; else map->supported_features = 0x0000001f; } static void *map_supported_features(struct obc_session *session) { const void *data; uint32_t supported_features; /* Supported Feature Bits */ data = obc_session_get_attribute(session, SDP_ATTR_MAP_SUPPORTED_FEATURES); if (!data) return NULL; supported_features = *(uint32_t *) data; if (!supported_features) return NULL; if (supported_features & MAPSUPPORTEDFEATURES_IN_CONNECT_REQUEST) return g_obex_apparam_set_uint32(NULL, SUPPORTED_FEATURES_TAG, NOTIFICATION_REGISTRATION_FEATURE | NOTIFICATION_FEATURE | BROWSING_FEATURE | UPLOADING_FEATURE | DELETE_FEATURE | INSTANCE_INFORMATION_FEATURE | EXTENDED_EVENT_REPORT_1_1 | MESSAGES_LISTING_FORMAT_VERSION_1_1); return NULL; } static int map_probe(struct obc_session *session) { struct map_data *map; const char *path; path = obc_session_get_path(session); map = g_try_new0(struct map_data, 1); if (!map) return -ENOMEM; map->session = obc_session_ref(session); map->messages = g_hash_table_new_full(g_int64_hash, g_int64_equal, NULL, map_msg_remove); parse_service_record(map); DBG("%s, instance id %d", path, map->mas_instance_id); set_notification_registration(map, true); if (!g_dbus_register_interface(conn, path, MAP_INTERFACE, map_methods, NULL, map_properties, map, map_free)) { map_free(map); return -ENOMEM; } return 0; } static void map_remove(struct obc_session *session) { const char *path = obc_session_get_path(session); DBG("%s", path); g_dbus_unregister_interface(conn, path, MAP_INTERFACE); } static struct obc_driver map = { .service = "MAP", .uuid = MAS_UUID, .target = OBEX_MAS_UUID, .target_len = OBEX_MAS_UUID_LEN, .supported_features = map_supported_features, .probe = map_probe, .remove = map_remove }; int map_init(void) { int err; DBG(""); conn = obex_get_dbus_connection(); if (!conn) return -EIO; err = obc_driver_register(&map); if (err < 0) { dbus_connection_unref(conn); conn = NULL; return err; } return 0; } void map_exit(void) { DBG(""); dbus_connection_unref(conn); conn = NULL; obc_driver_unregister(&map); } bluez-5.82/obexd/client/PaxHeaders/transfer.c0000644000000000000000000000005014711225434016220 xustar0020 atime=1743516761 20 ctime=1743591283 bluez-5.82/obexd/client/transfer.c0000644000000000000000000005501414711225434015706 0ustar00rootroot// SPDX-License-Identifier: GPL-2.0-or-later /* * * OBEX Client * * Copyright (C) 2007-2010 Marcel Holtmann * Copyright (C) 2011-2012 BMW Car IT GmbH. All rights reserved. * * */ #ifdef HAVE_CONFIG_H #include #endif #include #include #include #include #include #include #include #include #include #include "gdbus/gdbus.h" #include "gobex/gobex.h" #include "obexd/src/log.h" #include "transfer.h" #define TRANSFER_INTERFACE "org.bluez.obex.Transfer1" #define ERROR_INTERFACE "org.bluez.obex.Error" #define OBC_TRANSFER_ERROR obc_transfer_error_quark() #define FIRST_PACKET_TIMEOUT 60 static guint64 counter = 0; struct transfer_callback { transfer_callback_t func; void *data; }; enum { TRANSFER_STATUS_QUEUED = 0, TRANSFER_STATUS_ACTIVE, TRANSFER_STATUS_SUSPENDED, TRANSFER_STATUS_COMPLETE, TRANSFER_STATUS_SUSPENDED_QUEUED, TRANSFER_STATUS_ERROR }; struct obc_transfer { GObex *obex; uint8_t status; GObexApparam *apparam; GSList *headers; guint8 op; struct transfer_callback *callback; DBusConnection *conn; DBusMessage *msg; char *session; /* Session path */ char *owner; /* Transfer initiator */ char *path; /* Transfer path */ char *filename; /* Transfer file location */ char *name; /* Transfer object name */ char *type; /* Transfer object type */ int fd; guint req; guint xfer; gint64 size; gint64 transferred; gint64 progress; guint progress_id; }; static GQuark obc_transfer_error_quark(void) { return g_quark_from_static_string("obc-transfer-error-quark"); } DBusMessage *obc_transfer_create_dbus_reply(struct obc_transfer *transfer, DBusMessage *message) { DBusMessage *reply; DBusMessageIter iter; reply = dbus_message_new_method_return(message); if (reply == NULL) return NULL; dbus_message_iter_init_append(reply, &iter); dbus_message_iter_append_basic(&iter, DBUS_TYPE_OBJECT_PATH, &transfer->path); g_dbus_get_properties(transfer->conn, transfer->path, TRANSFER_INTERFACE, &iter); return reply; } static void abort_complete(GObex *obex, GError *err, gpointer user_data) { struct obc_transfer *transfer = user_data; struct transfer_callback *callback = transfer->callback; DBusMessage *reply; transfer->xfer = 0; reply = dbus_message_new_method_return(transfer->msg); if (reply) g_dbus_send_message(transfer->conn, reply); dbus_message_unref(transfer->msg); transfer->msg = NULL; if (callback == NULL) return; if (err) { callback->func(transfer, err, callback->data); } else { GError *abort_err; abort_err = g_error_new(OBC_TRANSFER_ERROR, -ECANCELED, "%s", "Transfer cancelled by user"); callback->func(transfer, abort_err, callback->data); g_error_free(abort_err); } } static DBusMessage *obc_transfer_cancel(DBusConnection *connection, DBusMessage *message, void *user_data) { struct obc_transfer *transfer = user_data; const char *sender; sender = dbus_message_get_sender(message); if (g_strcmp0(transfer->owner, sender) != 0) return g_dbus_create_error(message, ERROR_INTERFACE ".NotAuthorized", "Not Authorized"); if (transfer->msg != NULL) return g_dbus_create_error(message, ERROR_INTERFACE ".InProgress", "Cancellation already in progress"); if (transfer->status == TRANSFER_STATUS_SUSPENDED) g_obex_resume(transfer->obex); if (transfer->req > 0) { if (!g_obex_cancel_req(transfer->obex, transfer->req, TRUE)) return g_dbus_create_error(message, ERROR_INTERFACE ".Failed", "Failed"); transfer->req = 0; } if (transfer->xfer == 0) { struct transfer_callback *callback = transfer->callback; if (callback != NULL) { GError *err; err = g_error_new(OBC_TRANSFER_ERROR, -ECANCELED, "%s", "Transfer cancelled by user"); callback->func(transfer, err, callback->data); g_error_free(err); } return dbus_message_new_method_return(message); } if (transfer->progress_id != 0) { g_source_remove(transfer->progress_id); transfer->progress_id = 0; } if (!g_obex_cancel_transfer(transfer->xfer, abort_complete, transfer)) return g_dbus_create_error(message, ERROR_INTERFACE ".Failed", "Failed"); transfer->msg = dbus_message_ref(message); return NULL; } static void transfer_set_status(struct obc_transfer *transfer, uint8_t status) { if (transfer->status == status) return; transfer->status = status; g_dbus_emit_property_changed(transfer->conn, transfer->path, TRANSFER_INTERFACE, "Status"); } static DBusMessage *obc_transfer_suspend(DBusConnection *connection, DBusMessage *message, void *user_data) { struct obc_transfer *transfer = user_data; const char *sender; uint8_t status; sender = dbus_message_get_sender(message); if (g_strcmp0(transfer->owner, sender) != 0) return g_dbus_create_error(message, ERROR_INTERFACE ".NotAuthorized", "Not Authorized"); switch (transfer->status) { case TRANSFER_STATUS_QUEUED: status = TRANSFER_STATUS_SUSPENDED_QUEUED; break; case TRANSFER_STATUS_ACTIVE: if (transfer->xfer) g_obex_suspend(transfer->obex); status = TRANSFER_STATUS_SUSPENDED; break; default: return g_dbus_create_error(message, ERROR_INTERFACE ".NotInProgress", "Not in progress"); } transfer_set_status(transfer, status); return g_dbus_create_reply(message, DBUS_TYPE_INVALID); } static DBusMessage *obc_transfer_resume(DBusConnection *connection, DBusMessage *message, void *user_data) { struct obc_transfer *transfer = user_data; const char *sender; uint8_t status; sender = dbus_message_get_sender(message); if (g_strcmp0(transfer->owner, sender) != 0) return g_dbus_create_error(message, ERROR_INTERFACE ".NotAuthorized", "Not Authorized"); switch (transfer->status) { case TRANSFER_STATUS_SUSPENDED_QUEUED: status = TRANSFER_STATUS_QUEUED; break; case TRANSFER_STATUS_SUSPENDED: if (transfer->xfer) g_obex_resume(transfer->obex); else obc_transfer_start(transfer, NULL, NULL); status = TRANSFER_STATUS_ACTIVE; break; default: return g_dbus_create_error(message, ERROR_INTERFACE ".NotInProgress", "Not in progress"); } transfer_set_status(transfer, status); return g_dbus_create_reply(message, DBUS_TYPE_INVALID); } static gboolean name_exists(const GDBusPropertyTable *property, void *data) { struct obc_transfer *transfer = data; return transfer->name != NULL; } static gboolean get_name(const GDBusPropertyTable *property, DBusMessageIter *iter, void *data) { struct obc_transfer *transfer = data; if (transfer->name == NULL) return FALSE; dbus_message_iter_append_basic(iter, DBUS_TYPE_STRING, &transfer->name); return TRUE; } static gboolean get_size(const GDBusPropertyTable *property, DBusMessageIter *iter, void *data) { struct obc_transfer *transfer = data; dbus_message_iter_append_basic(iter, DBUS_TYPE_UINT64, &transfer->size); return TRUE; } static gboolean filename_exists(const GDBusPropertyTable *property, void *data) { struct obc_transfer *transfer = data; return transfer->filename != NULL; } static gboolean get_filename(const GDBusPropertyTable *property, DBusMessageIter *iter, void *data) { struct obc_transfer *transfer = data; if (transfer->filename == NULL) return FALSE; dbus_message_iter_append_basic(iter, DBUS_TYPE_STRING, &transfer->filename); return TRUE; } static gboolean transferred_exists(const GDBusPropertyTable *property, void *data) { struct obc_transfer *transfer = data; return transfer->obex != NULL; } static gboolean get_transferred(const GDBusPropertyTable *property, DBusMessageIter *iter, void *data) { struct obc_transfer *transfer = data; if (transfer->obex == NULL) return FALSE; dbus_message_iter_append_basic(iter, DBUS_TYPE_UINT64, &transfer->progress); return TRUE; } static const char *status2str(uint8_t status) { switch (status) { case TRANSFER_STATUS_QUEUED: return "queued"; case TRANSFER_STATUS_ACTIVE: return "active"; case TRANSFER_STATUS_SUSPENDED_QUEUED: case TRANSFER_STATUS_SUSPENDED: return "suspended"; case TRANSFER_STATUS_COMPLETE: return "complete"; case TRANSFER_STATUS_ERROR: default: return "error"; } } static gboolean get_status(const GDBusPropertyTable *property, DBusMessageIter *iter, void *data) { struct obc_transfer *transfer = data; const char *status = status2str(transfer->status); dbus_message_iter_append_basic(iter, DBUS_TYPE_STRING, &status); return TRUE; } static gboolean get_session(const GDBusPropertyTable *property, DBusMessageIter *iter, void *data) { struct obc_transfer *transfer = data; dbus_message_iter_append_basic(iter, DBUS_TYPE_OBJECT_PATH, &transfer->session); return TRUE; } static const GDBusMethodTable obc_transfer_methods[] = { { GDBUS_METHOD("Suspend", NULL, NULL, obc_transfer_suspend) }, { GDBUS_METHOD("Resume", NULL, NULL, obc_transfer_resume) }, { GDBUS_ASYNC_METHOD("Cancel", NULL, NULL, obc_transfer_cancel) }, { } }; static const GDBusPropertyTable obc_transfer_properties[] = { { "Status", "s", get_status }, { "Name", "s", get_name, NULL, name_exists }, { "Size", "t", get_size }, { "Filename", "s", get_filename, NULL, filename_exists }, { "Transferred", "t", get_transferred, NULL, transferred_exists }, { "Session", "o", get_session }, { } }; static void header_free(void *data, void *user_data) { g_obex_header_free(data); } static void obc_transfer_free(struct obc_transfer *transfer) { DBG("%p", transfer); if (transfer->status == TRANSFER_STATUS_SUSPENDED) g_obex_resume(transfer->obex); if (transfer->req > 0) g_obex_cancel_req(transfer->obex, transfer->req, TRUE); if (transfer->xfer) g_obex_cancel_transfer(transfer->xfer, NULL, NULL); if (transfer->progress_id != 0) { g_source_remove(transfer->progress_id); transfer->progress_id = 0; } if (transfer->op == G_OBEX_OP_GET && transfer->status != TRANSFER_STATUS_COMPLETE && transfer->filename) { if (remove(transfer->filename) < 0) error("remove(%s): %s(%d)", transfer->filename, strerror(errno), errno); } if (transfer->fd > 0) close(transfer->fd); if (transfer->apparam != NULL) g_obex_apparam_free(transfer->apparam); if (transfer->conn) dbus_connection_unref(transfer->conn); if (transfer->msg) dbus_message_unref(transfer->msg); if (transfer->obex) g_obex_unref(transfer->obex); g_slist_foreach(transfer->headers, header_free, NULL); g_slist_free(transfer->headers); g_free(transfer->callback); g_free(transfer->owner); g_free(transfer->filename); g_free(transfer->name); g_free(transfer->type); g_free(transfer->session); g_free(transfer->path); g_free(transfer); } static struct obc_transfer *obc_transfer_create(guint8 op, const char *filename, const char *name, const char *type) { struct obc_transfer *transfer; transfer = g_new0(struct obc_transfer, 1); transfer->op = op; transfer->filename = g_strdup(filename); transfer->name = g_strdup(name); transfer->type = g_strdup(type); return transfer; } gboolean obc_transfer_register(struct obc_transfer *transfer, DBusConnection *conn, const char *session, const char *owner, GError **err) { transfer->owner = g_strdup(owner); transfer->session = g_strdup(session); transfer->path = g_strdup_printf("%s/transfer%ju", session, counter++); transfer->conn = dbus_connection_ref(conn); if (transfer->conn == NULL) { g_set_error(err, OBC_TRANSFER_ERROR, -EFAULT, "Unable to connect to D-Bus"); return FALSE; } if (g_dbus_register_interface(transfer->conn, transfer->path, TRANSFER_INTERFACE, obc_transfer_methods, NULL, obc_transfer_properties, transfer, NULL) == FALSE) { g_set_error(err, OBC_TRANSFER_ERROR, -EFAULT, "Unable to register to D-Bus"); return FALSE; } DBG("%p registered %s", transfer, transfer->path); return TRUE; } static gboolean transfer_open(struct obc_transfer *transfer, int flags, mode_t mode, GError **err) { int fd; char *filename; if (transfer->filename != NULL && strcmp(transfer->filename, "") != 0) { fd = open(transfer->filename, flags, mode); if (fd < 0) { error("open(): %s(%d)", strerror(errno), errno); g_set_error(err, OBC_TRANSFER_ERROR, -errno, "Unable to open file"); return FALSE; } goto done; } fd = g_file_open_tmp("obex-clientXXXXXX", &filename, err); if (fd < 0) { error("g_file_open_tmp(): %s", (*err)->message); return FALSE; } if (transfer->filename == NULL) { /* remove always only if NULL was given */ if (remove(filename) < 0) error("remove(%s): %s(%d)", filename, strerror(errno), errno); g_free(filename); } else { g_free(transfer->filename); transfer->filename = filename; } done: transfer->fd = fd; return TRUE; } struct obc_transfer *obc_transfer_get(const char *type, const char *name, const char *filename, GError **err) { struct obc_transfer *transfer; int perr; transfer = obc_transfer_create(G_OBEX_OP_GET, filename, name, type); perr = transfer_open(transfer, O_WRONLY | O_CREAT | O_TRUNC, 0600, err); if (perr < 0) { obc_transfer_free(transfer); return NULL; } return transfer; } struct obc_transfer *obc_transfer_put(const char *type, const char *name, const char *filename, const void *contents, size_t size, GError **err) { struct obc_transfer *transfer; struct stat st; int perr; if ((filename == NULL || strcmp(filename, "") == 0) && contents == NULL) { g_set_error(err, OBC_TRANSFER_ERROR, -EINVAL, "Invalid filename given"); return NULL; } transfer = obc_transfer_create(G_OBEX_OP_PUT, filename, name, type); if (contents != NULL) { ssize_t w; if (!transfer_open(transfer, O_RDWR, 0, err)) goto fail; w = write(transfer->fd, contents, size); if (w < 0) { perr = errno; error("write(): %s(%d)", strerror(perr), perr); g_set_error(err, OBC_TRANSFER_ERROR, -perr, "Writing to file failed"); goto fail; } else if ((size_t) w != size) { error("Unable to write all contents to file"); g_set_error(err, OBC_TRANSFER_ERROR, -EFAULT, "Writing all contents to file failed"); goto fail; } lseek(transfer->fd, 0, SEEK_SET); } else { if (!transfer_open(transfer, O_RDONLY, 0, err)) goto fail; } if (fstat(transfer->fd, &st) < 0) { perr = errno; error("fstat(): %s(%d)", strerror(perr), perr); g_set_error(err, OBC_TRANSFER_ERROR, -perr, "Unable to get file status"); goto fail; } transfer->size = st.st_size; return transfer; fail: obc_transfer_free(transfer); return NULL; } void obc_transfer_unregister(struct obc_transfer *transfer) { if (transfer->path) { g_dbus_unregister_interface(transfer->conn, transfer->path, TRANSFER_INTERFACE); } DBG("%p unregistered %s", transfer, transfer->path); obc_transfer_free(transfer); } static gboolean get_xfer_progress(const void *buf, gsize len, gpointer user_data) { struct obc_transfer *transfer = user_data; if (transfer->fd > 0) { int w; w = write(transfer->fd, buf, len); if (w < 0) return FALSE; transfer->transferred += w; } return TRUE; } static void xfer_complete(GObex *obex, GError *err, gpointer user_data) { struct obc_transfer *transfer = user_data; struct transfer_callback *callback = transfer->callback; transfer->xfer = 0; if (transfer->progress_id != 0) { g_source_remove(transfer->progress_id); transfer->progress_id = 0; } if (transfer->status == TRANSFER_STATUS_SUSPENDED) g_obex_resume(transfer->obex); if (err) transfer_set_status(transfer, TRANSFER_STATUS_ERROR); else transfer_set_status(transfer, TRANSFER_STATUS_COMPLETE); if (callback == NULL) return; if (callback->func) callback->func(transfer, err, callback->data); } static void get_xfer_progress_first(GObex *obex, GError *err, GObexPacket *rsp, gpointer user_data) { struct obc_transfer *transfer = user_data; GObexPacket *req; GObexHeader *hdr; GObexApparam *apparam; const guint8 *buf; gsize len; guint8 rspcode; gboolean final; if (err != NULL) { xfer_complete(obex, err, transfer); return; } rspcode = g_obex_packet_get_operation(rsp, &final); if (rspcode != G_OBEX_RSP_SUCCESS && rspcode != G_OBEX_RSP_CONTINUE) { err = g_error_new(OBC_TRANSFER_ERROR, rspcode, "%s", g_obex_strerror(rspcode)); xfer_complete(obex, err, transfer); g_error_free(err); return; } hdr = g_obex_packet_get_header(rsp, G_OBEX_HDR_LENGTH); if (hdr) { uint32_t len; if (g_obex_header_get_uint32(hdr, &len)) { transfer->size = len; g_dbus_emit_property_changed(transfer->conn, transfer->path, TRANSFER_INTERFACE, "Size"); } } hdr = g_obex_packet_get_header(rsp, G_OBEX_HDR_APPARAM); if (hdr) { apparam = g_obex_header_get_apparam(hdr); if (apparam != NULL) obc_transfer_set_apparam(transfer, apparam); } hdr = g_obex_packet_get_body(rsp); if (hdr) { g_obex_header_get_bytes(hdr, &buf, &len); if (len != 0) get_xfer_progress(buf, len, transfer); } if (rspcode == G_OBEX_RSP_SUCCESS) { transfer->req = 0; xfer_complete(obex, err, transfer); return; } if (g_obex_srm_active(obex) || transfer->status == TRANSFER_STATUS_SUSPENDED) return; transfer->req = 0; req = g_obex_packet_new(G_OBEX_OP_GET, TRUE, G_OBEX_HDR_INVALID); transfer->xfer = g_obex_get_req_pkt(obex, req, get_xfer_progress, xfer_complete, transfer, &err); } static gssize put_xfer_progress(void *buf, gsize len, gpointer user_data) { struct obc_transfer *transfer = user_data; gssize size; size = read(transfer->fd, buf, len); if (size <= 0) return size; transfer->transferred += size; return size; } gboolean obc_transfer_set_callback(struct obc_transfer *transfer, transfer_callback_t func, void *user_data) { struct transfer_callback *callback; if (transfer->callback != NULL) return FALSE; callback = g_new0(struct transfer_callback, 1); callback->func = func; callback->data = user_data; transfer->callback = callback; return TRUE; } static gboolean report_progress(gpointer data) { struct obc_transfer *transfer = data; if (transfer->transferred == transfer->progress) return TRUE; transfer->progress = transfer->transferred; if (transfer->transferred == transfer->size) { transfer->progress_id = 0; return FALSE; } if (transfer->status != TRANSFER_STATUS_ACTIVE && transfer->status != TRANSFER_STATUS_SUSPENDED) transfer_set_status(transfer, TRANSFER_STATUS_ACTIVE); g_dbus_emit_property_changed(transfer->conn, transfer->path, TRANSFER_INTERFACE, "Transferred"); return TRUE; } static gboolean transfer_start_get(struct obc_transfer *transfer, GError **err) { GObexPacket *req; GObexHeader *hdr; if (transfer->xfer > 0) { g_set_error(err, OBC_TRANSFER_ERROR, -EALREADY, "Transfer already started"); return FALSE; } req = g_obex_packet_new(G_OBEX_OP_GET, TRUE, G_OBEX_HDR_INVALID); if (transfer->name != NULL) g_obex_packet_add_unicode(req, G_OBEX_HDR_NAME, transfer->name); if (transfer->type != NULL) g_obex_packet_add_bytes(req, G_OBEX_HDR_TYPE, transfer->type, strlen(transfer->type) + 1); while (transfer->headers) { hdr = transfer->headers->data; g_obex_packet_add_header(req, hdr); transfer->headers = g_slist_remove(transfer->headers, hdr); } if (transfer->apparam != NULL) { hdr = g_obex_header_new_apparam(transfer->apparam); g_obex_packet_add_header(req, hdr); } transfer->req = g_obex_send_req(transfer->obex, req, FIRST_PACKET_TIMEOUT, get_xfer_progress_first, transfer, err); if (transfer->req == 0) return FALSE; if (transfer->path == NULL) return TRUE; transfer->progress_id = g_timeout_add_seconds(1, report_progress, transfer); return TRUE; } static gboolean transfer_start_put(struct obc_transfer *transfer, GError **err) { GObexPacket *req; GObexHeader *hdr; if (transfer->xfer > 0) { g_set_error(err, OBC_TRANSFER_ERROR, -EALREADY, "Transfer already started"); return FALSE; } req = g_obex_packet_new(G_OBEX_OP_PUT, FALSE, G_OBEX_HDR_INVALID); if (transfer->name != NULL) g_obex_packet_add_unicode(req, G_OBEX_HDR_NAME, transfer->name); if (transfer->type != NULL) g_obex_packet_add_bytes(req, G_OBEX_HDR_TYPE, transfer->type, strlen(transfer->type) + 1); if (transfer->size < UINT32_MAX) g_obex_packet_add_uint32(req, G_OBEX_HDR_LENGTH, transfer->size); if (transfer->apparam != NULL) { hdr = g_obex_header_new_apparam(transfer->apparam); g_obex_packet_add_header(req, hdr); } transfer->xfer = g_obex_put_req_pkt(transfer->obex, req, put_xfer_progress, xfer_complete, transfer, err); if (transfer->xfer == 0) return FALSE; if (transfer->path == NULL) return TRUE; transfer->progress_id = g_timeout_add_seconds(1, report_progress, transfer); return TRUE; } gboolean obc_transfer_start(struct obc_transfer *transfer, void *obex, GError **err) { if (!transfer->obex) transfer->obex = g_obex_ref(obex); if (transfer->status == TRANSFER_STATUS_SUSPENDED_QUEUED) { /* Reset status so the transfer can be resumed */ transfer->status = TRANSFER_STATUS_SUSPENDED; return TRUE; } switch (transfer->op) { case G_OBEX_OP_GET: return transfer_start_get(transfer, err); case G_OBEX_OP_PUT: return transfer_start_put(transfer, err); } g_set_error(err, OBC_TRANSFER_ERROR, -ENOTSUP, "Not supported"); return FALSE; } guint8 obc_transfer_get_operation(struct obc_transfer *transfer) { return transfer->op; } void obc_transfer_set_apparam(struct obc_transfer *transfer, void *data) { if (transfer->apparam != NULL) g_obex_apparam_free(transfer->apparam); if (data == NULL) return; transfer->apparam = data; } void *obc_transfer_get_apparam(struct obc_transfer *transfer) { return transfer->apparam; } int obc_transfer_get_contents(struct obc_transfer *transfer, char **contents, size_t *size) { struct stat st; ssize_t ret; if (contents == NULL) return -EINVAL; if (fstat(transfer->fd, &st) < 0) { error("fstat(): %s(%d)", strerror(errno), errno); return -errno; } if (lseek(transfer->fd, 0, SEEK_SET) < 0) { error("lseek(): %s(%d)", strerror(errno), errno); return -errno; } *contents = g_malloc(st.st_size + 1); ret = read(transfer->fd, *contents, st.st_size); if (ret < 0) { error("read(): %s(%d)", strerror(errno), errno); g_free(*contents); return -errno; } (*contents)[ret] = '\0'; if (size) *size = ret; return 0; } const char *obc_transfer_get_path(struct obc_transfer *transfer) { return transfer->path; } gint64 obc_transfer_get_size(struct obc_transfer *transfer) { return transfer->size; } void obc_transfer_add_header(struct obc_transfer *transfer, void *data) { transfer->headers = g_slist_append(transfer->headers, data); } bluez-5.82/obexd/client/PaxHeaders/pbap.c0000644000000000000000000000005014772767672015344 xustar0020 atime=1743515579 20 ctime=1743591283 bluez-5.82/obexd/client/pbap.c0000644000000000000000000010630214772767672015027 0ustar00rootroot// SPDX-License-Identifier: GPL-2.0-or-later /* * * OBEX Client * * Copyright (C) 2007-2010 Intel Corporation * Copyright (C) 2007-2010 Marcel Holtmann * * */ #ifdef HAVE_CONFIG_H #include #endif #define _GNU_SOURCE #include #include #include #include #include "lib/bluetooth.h" #include "lib/sdp.h" #include "gobex/gobex-apparam.h" #include "gdbus/gdbus.h" #include "obexd/src/log.h" #include "obexd/src/obexd.h" #include "transfer.h" #include "session.h" #include "driver.h" #include "pbap.h" #define OBEX_PBAP_UUID \ "\x79\x61\x35\xF0\xF0\xC5\x11\xD8\x09\x66\x08\x00\x20\x0C\x9A\x66" #define OBEX_PBAP_UUID_LEN 16 #define FORMAT_VCARD21 0x0 #define FORMAT_VCARD30 0x1 #define ORDER_INDEXED 0x0 #define ORDER_ALPHANUMERIC 0x1 #define ORDER_PHONETIC 0x2 #define ATTRIB_NAME 0x0 #define ATTRIB_NUMBER 0x1 #define ATTRIB_SOUND 0x2 #define DEFAULT_COUNT 65535 #define DEFAULT_OFFSET 0 #define PULLPHONEBOOK 0x1 #define GETPHONEBOOKSIZE 0x2 #define ORDER_TAG 0x01 #define SEARCHVALUE_TAG 0x02 #define SEARCHATTRIB_TAG 0x03 #define MAXLISTCOUNT_TAG 0x04 #define LISTSTARTOFFSET_TAG 0x05 #define FILTER_TAG 0x06 #define FORMAT_TAG 0X07 #define PHONEBOOKSIZE_TAG 0X08 #define NEWMISSEDCALLS_TAG 0X09 #define PRIMARY_COUNTER_TAG 0X0A #define SECONDARY_COUNTER_TAG 0X0B #define DATABASEID_TAG 0X0D #define SUPPORTED_FEATURES_TAG 0x10 #define DOWNLOAD_FEATURE 0x00000001 #define BROWSE_FEATURE 0x00000002 #define DATABASEID_FEATURE 0x00000004 #define FOLDER_VERSION_FEATURE 0x00000008 #define VCARD_SELECTING_FEATURE 0x00000010 #define ENHANCED_CALLS_FEATURE 0x00000020 #define UCI_FEATURE 0x00000040 #define UID_FEATURE 0x00000080 #define REFERENCING_FEATURE 0x00000100 #define DEFAULT_IMAGE_FEATURE 0x00000200 static const char *filter_list[] = { "VERSION", "FN", "N", "PHOTO", "BDAY", "ADR", "LABEL", "TEL", "EMAIL", "MAILER", "TZ", "GEO", "TITLE", "ROLE", "LOGO", "AGENT", "ORG", "NOTE", "REV", "SOUND", "URL", "UID", "KEY", "NICKNAME", "CATEGORIES", "PROID", "CLASS", "SORT-STRING", "X-IRMC-CALL-DATETIME", "X-BT-SPEEDDIALKEY", "X-BT-UCI", "X-BT-UID", NULL }; #define FILTER_BIT_MAX 63 #define FILTER_ALL 0xFFFFFFFFFFFFFFFFULL #define PBAP_INTERFACE "org.bluez.obex.PhonebookAccess1" #define ERROR_INTERFACE "org.bluez.obex.Error" #define PBAP_CLIENT_UUID "0000112e-0000-1000-8000-00805f9b34fb" #define PBAP_UUID "0000112f-0000-1000-8000-00805f9b34fb" struct pbap_data { struct obc_session *session; char *path; uint16_t version; uint32_t supported_features; uint8_t databaseid[16]; uint8_t primary[16]; uint8_t secondary[16]; }; struct pending_request { struct pbap_data *pbap; DBusMessage *msg; }; static DBusConnection *conn = NULL; static DBusConnection *system_conn; static unsigned int listener_id; static char *client_path; static struct pending_request *pending_request_new(struct pbap_data *pbap, DBusMessage *message) { struct pending_request *p; p = g_new0(struct pending_request, 1); p->pbap = pbap; p->msg = dbus_message_ref(message); return p; } static void pending_request_free(struct pending_request *p) { dbus_message_unref(p->msg); g_free(p); } static void listing_element(GMarkupParseContext *ctxt, const char *element, const char **names, const char **values, gpointer user_data, GError **gerr) { DBusMessageIter *item = user_data, entry; char **key; const char *handle = NULL, *vcardname = NULL; if (g_str_equal(element, "card") != TRUE) return; for (key = (char **) names; *key; key++, values++) { if (g_str_equal(*key, "handle") == TRUE) handle = *values; else if (g_str_equal(*key, "name") == TRUE) vcardname = *values; } if (!handle || !vcardname) return; dbus_message_iter_open_container(item, DBUS_TYPE_STRUCT, NULL, &entry); dbus_message_iter_append_basic(&entry, DBUS_TYPE_STRING, &handle); dbus_message_iter_append_basic(&entry, DBUS_TYPE_STRING, &vcardname); dbus_message_iter_close_container(item, &entry); } static const GMarkupParser listing_parser = { listing_element, NULL, NULL, NULL, NULL }; static char *build_phonebook_path(const char *location, const char *item) { char *path = NULL, *tmp, *tmp1; gboolean internal = FALSE; if (!g_ascii_strcasecmp(location, "int") || !g_ascii_strcasecmp(location, "internal")) { path = g_strdup("/telecom"); internal = TRUE; } else if (!g_ascii_strncasecmp(location, "sim", 3)) { if (strlen(location) == 3) tmp = g_strdup("sim1"); else tmp = g_ascii_strup(location, 4); path = g_build_filename("/", tmp, "telecom", NULL); g_free(tmp); } else return NULL; if (!g_ascii_strcasecmp(item, "pb") || !g_ascii_strcasecmp(item, "ich") || !g_ascii_strcasecmp(item, "och") || !g_ascii_strcasecmp(item, "mch") || !g_ascii_strcasecmp(item, "cch") || (internal && !g_ascii_strcasecmp(item, "spd")) || (internal && !g_ascii_strcasecmp(item, "fav"))) { tmp = path; tmp1 = g_ascii_strdown(item, -1); path = g_build_filename(tmp, tmp1, NULL); g_free(tmp); g_free(tmp1); } else { g_free(path); return NULL; } return path; } /* should only be called inside pbap_set_path */ static void pbap_reset_path(struct pbap_data *pbap) { if (!pbap->path) return; obc_session_setpath(pbap->session, pbap->path, NULL, NULL, NULL); } static void pbap_setpath_cb(struct obc_session *session, struct obc_transfer *transfer, GError *err, void *user_data) { struct pending_request *request = user_data; struct pbap_data *pbap = request->pbap; if (err != NULL) pbap_reset_path(pbap); else g_dbus_emit_property_changed(conn, obc_session_get_path(pbap->session), PBAP_INTERFACE, "Folder"); if (err) { DBusMessage *reply = g_dbus_create_error(request->msg, ERROR_INTERFACE ".Failed", "%s", err->message); g_dbus_send_message(conn, reply); } else g_dbus_send_reply(conn, request->msg, DBUS_TYPE_INVALID); pending_request_free(request); } static void read_version(struct pbap_data *pbap, GObexApparam *apparam) { const guint8 *data; uint8_t value[16]; gsize len; if (!(pbap->supported_features & FOLDER_VERSION_FEATURE)) return; if (!g_obex_apparam_get_bytes(apparam, PRIMARY_COUNTER_TAG, &data, &len)) { len = sizeof(value); memset(value, 0, len); data = value; } if (len == sizeof(pbap->primary) && memcmp(pbap->primary, data, len)) { memcpy(pbap->primary, data, len); g_dbus_emit_property_changed(conn, obc_session_get_path(pbap->session), PBAP_INTERFACE, "PrimaryCounter"); } if (!g_obex_apparam_get_bytes(apparam, SECONDARY_COUNTER_TAG, &data, &len)) { len = sizeof(value); memset(value, 0, len); data = value; } if (len == sizeof(pbap->secondary) && memcmp(pbap->secondary, data, len)) { memcpy(pbap->secondary, data, len); g_dbus_emit_property_changed(conn, obc_session_get_path(pbap->session), PBAP_INTERFACE, "SecondaryCounter"); } } static void read_databaseid(struct pbap_data *pbap, GObexApparam *apparam) { const guint8 *data; guint8 value[16]; gsize len; if (!(pbap->supported_features & DATABASEID_FEATURE)) return; if (!g_obex_apparam_get_bytes(apparam, DATABASEID_TAG, &data, &len)) { len = sizeof(value); memset(value, 0, len); data = value; } if (memcmp(data, pbap->databaseid, len)) { memcpy(pbap->databaseid, data, len); g_dbus_emit_property_changed(conn, obc_session_get_path(pbap->session), PBAP_INTERFACE, "DatabaseIdentifier"); } } static void read_return_apparam(struct obc_transfer *transfer, struct pbap_data *pbap, guint16 *phone_book_size, guint8 *new_missed_calls) { GObexApparam *apparam; *phone_book_size = 0; *new_missed_calls = 0; apparam = obc_transfer_get_apparam(transfer); if (apparam == NULL) return; g_obex_apparam_get_uint16(apparam, PHONEBOOKSIZE_TAG, phone_book_size); g_obex_apparam_get_uint8(apparam, NEWMISSEDCALLS_TAG, new_missed_calls); read_version(pbap, apparam); read_databaseid(pbap, apparam); } static void phonebook_size_callback(struct obc_session *session, struct obc_transfer *transfer, GError *err, void *user_data) { struct pending_request *request = user_data; DBusMessage *reply; guint16 phone_book_size; guint8 new_missed_calls; if (err) { reply = g_dbus_create_error(request->msg, ERROR_INTERFACE ".Failed", "%s", err->message); goto send; } reply = dbus_message_new_method_return(request->msg); read_return_apparam(transfer, request->pbap, &phone_book_size, &new_missed_calls); if (dbus_message_is_method_call(request->msg, PBAP_INTERFACE, "GetSize")) dbus_message_append_args(reply, DBUS_TYPE_UINT16, &phone_book_size, DBUS_TYPE_INVALID); send: g_dbus_send_message(conn, reply); pending_request_free(request); } static void pull_vcard_listing_callback(struct obc_session *session, struct obc_transfer *transfer, GError *err, void *user_data) { struct pending_request *request = user_data; GMarkupParseContext *ctxt; DBusMessage *reply; DBusMessageIter iter, array; char *contents; size_t size; int perr; if (err) { reply = g_dbus_create_error(request->msg, ERROR_INTERFACE ".Failed", "%s", err->message); goto send; } perr = obc_transfer_get_contents(transfer, &contents, &size); if (perr < 0) { reply = g_dbus_create_error(request->msg, ERROR_INTERFACE ".Failed", "Error reading contents: %s", strerror(-perr)); goto send; } reply = dbus_message_new_method_return(request->msg); dbus_message_iter_init_append(reply, &iter); dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY, DBUS_STRUCT_BEGIN_CHAR_AS_STRING DBUS_TYPE_STRING_AS_STRING DBUS_TYPE_STRING_AS_STRING DBUS_STRUCT_END_CHAR_AS_STRING, &array); ctxt = g_markup_parse_context_new(&listing_parser, 0, &array, NULL); g_markup_parse_context_parse(ctxt, contents, size, NULL); g_markup_parse_context_free(ctxt); dbus_message_iter_close_container(&iter, &array); g_free(contents); send: g_dbus_send_message(conn, reply); pending_request_free(request); } static GObexApparam *parse_format(GObexApparam *apparam, DBusMessageIter *iter) { const char *string; guint8 format; if (dbus_message_iter_get_arg_type(iter) != DBUS_TYPE_STRING) return NULL; dbus_message_iter_get_basic(iter, &string); if (!string || g_str_equal(string, "")) format = FORMAT_VCARD21; else if (!g_ascii_strcasecmp(string, "vcard21")) format = FORMAT_VCARD21; else if (!g_ascii_strcasecmp(string, "vcard30")) format = FORMAT_VCARD30; else return NULL; return g_obex_apparam_set_uint8(apparam, FORMAT_TAG, format); } static GObexApparam *parse_order(GObexApparam *apparam, DBusMessageIter *iter) { const char *string; guint8 order; if (dbus_message_iter_get_arg_type(iter) != DBUS_TYPE_STRING) return NULL; dbus_message_iter_get_basic(iter, &string); if (!string || g_str_equal(string, "")) order = ORDER_INDEXED; else if (!g_ascii_strcasecmp(string, "indexed")) order = ORDER_INDEXED; else if (!g_ascii_strcasecmp(string, "alphanumeric")) order = ORDER_ALPHANUMERIC; else if (!g_ascii_strcasecmp(string, "phonetic")) order = ORDER_PHONETIC; else return NULL; return g_obex_apparam_set_uint8(apparam, ORDER_TAG, order); } static GObexApparam *parse_offset(GObexApparam *apparam, DBusMessageIter *iter) { guint16 num; if (dbus_message_iter_get_arg_type(iter) != DBUS_TYPE_UINT16) return NULL; dbus_message_iter_get_basic(iter, &num); return g_obex_apparam_set_uint16(apparam, LISTSTARTOFFSET_TAG, num); } static GObexApparam *parse_max_count(GObexApparam *apparam, DBusMessageIter *iter) { guint16 num; if (dbus_message_iter_get_arg_type(iter) != DBUS_TYPE_UINT16) return NULL; dbus_message_iter_get_basic(iter, &num); return g_obex_apparam_set_uint16(apparam, MAXLISTCOUNT_TAG, num); } static uint64_t get_filter_mask(const char *filterstr) { int i, bit = -1; if (!filterstr) return 0; if (!g_ascii_strcasecmp(filterstr, "ALL")) return FILTER_ALL; for (i = 0; filter_list[i] != NULL; i++) if (!g_ascii_strcasecmp(filterstr, filter_list[i])) return 1ULL << i; if (strlen(filterstr) < 4 || strlen(filterstr) > 5 || g_ascii_strncasecmp(filterstr, "bit", 3) != 0) return 0; sscanf(&filterstr[3], "%d", &bit); if (bit >= 0 && bit <= FILTER_BIT_MAX) return 1ULL << bit; else return 0; } static int set_field(guint64 *filter, const char *filterstr) { guint64 mask; mask = get_filter_mask(filterstr); if (mask == 0) return -EINVAL; *filter |= mask; return 0; } static GObexApparam *parse_fields(GObexApparam *apparam, DBusMessageIter *iter) { DBusMessageIter array; guint64 filter = 0; if (dbus_message_iter_get_arg_type(iter) != DBUS_TYPE_ARRAY) return NULL; dbus_message_iter_recurse(iter, &array); while (dbus_message_iter_get_arg_type(&array) == DBUS_TYPE_STRING) { const char *string; dbus_message_iter_get_basic(&array, &string); if (set_field(&filter, string) < 0) return NULL; dbus_message_iter_next(&array); } return g_obex_apparam_set_uint64(apparam, FILTER_TAG, filter); } static GObexApparam *parse_filters(GObexApparam *apparam, DBusMessageIter *iter) { DBusMessageIter array; if (dbus_message_iter_get_arg_type(iter) != DBUS_TYPE_ARRAY) return NULL; dbus_message_iter_recurse(iter, &array); while (dbus_message_iter_get_arg_type(&array) == DBUS_TYPE_DICT_ENTRY) { const char *key; DBusMessageIter value, entry; dbus_message_iter_recurse(&array, &entry); dbus_message_iter_get_basic(&entry, &key); dbus_message_iter_next(&entry); dbus_message_iter_recurse(&entry, &value); if (strcasecmp(key, "Format") == 0) { if (parse_format(apparam, &value) == NULL) return NULL; } else if (strcasecmp(key, "Order") == 0) { if (parse_order(apparam, &value) == NULL) return NULL; } else if (strcasecmp(key, "Offset") == 0) { if (parse_offset(apparam, &value) == NULL) return NULL; } else if (strcasecmp(key, "MaxCount") == 0) { if (parse_max_count(apparam, &value) == NULL) return NULL; } else if (strcasecmp(key, "Fields") == 0) { if (parse_fields(apparam, &value) == NULL) return NULL; } dbus_message_iter_next(&array); } return apparam; } static DBusMessage *pull_phonebook(struct pbap_data *pbap, DBusMessage *message, guint8 type, const char *targetfile, GObexApparam *apparam) { struct pending_request *request; struct obc_transfer *transfer; char *name; session_callback_t func; DBusMessage *reply; GError *err = NULL; name = g_strconcat(g_path_skip_root(pbap->path), ".vcf", NULL); transfer = obc_transfer_get("x-bt/phonebook", name, targetfile, &err); if (transfer == NULL) { g_obex_apparam_free(apparam); goto fail; } switch (type) { case PULLPHONEBOOK: func = NULL; request = NULL; break; case GETPHONEBOOKSIZE: func = phonebook_size_callback; request = pending_request_new(pbap, message); break; default: error("Unexpected type : 0x%2x", type); return NULL; } obc_transfer_set_apparam(transfer, apparam); if (!obc_session_queue(pbap->session, transfer, func, request, &err)) { if (request != NULL) pending_request_free(request); goto fail; } g_free(name); if (targetfile == NULL) return NULL; return obc_transfer_create_dbus_reply(transfer, message); fail: g_free(name); reply = g_dbus_create_error(message, ERROR_INTERFACE ".Failed", "%s", err->message); g_error_free(err); return reply; } static DBusMessage *pull_vcard_listing(struct pbap_data *pbap, DBusMessage *message, const char *name, GObexApparam *apparam) { struct pending_request *request; struct obc_transfer *transfer; GError *err = NULL; DBusMessage *reply; transfer = obc_transfer_get("x-bt/vcard-listing", name, NULL, &err); if (transfer == NULL) { g_obex_apparam_free(apparam); goto fail; } obc_transfer_set_apparam(transfer, apparam); request = pending_request_new(pbap, message); if (obc_session_queue(pbap->session, transfer, pull_vcard_listing_callback, request, &err)) return NULL; pending_request_free(request); fail: reply = g_dbus_create_error(message, ERROR_INTERFACE ".Failed", "%s", err->message); g_error_free(err); return reply; } static DBusMessage *pbap_select(DBusConnection *connection, DBusMessage *message, void *user_data) { struct pbap_data *pbap = user_data; const char *item, *location; char *path; struct pending_request *request; GError *err = NULL; if (dbus_message_get_args(message, NULL, DBUS_TYPE_STRING, &location, DBUS_TYPE_STRING, &item, DBUS_TYPE_INVALID) == FALSE) return g_dbus_create_error(message, ERROR_INTERFACE ".InvalidArguments", NULL); path = build_phonebook_path(location, item); if (path == NULL) return g_dbus_create_error(message, ERROR_INTERFACE ".InvalidArguments", "Invalid path"); if (pbap->path != NULL && g_str_equal(pbap->path, path)) { g_free(path); return dbus_message_new_method_return(message); } request = pending_request_new(pbap, message); obc_session_setpath(pbap->session, path, pbap_setpath_cb, request, &err); if (err != NULL) { DBusMessage *reply; reply = g_dbus_create_error(message, ERROR_INTERFACE ".Failed", "%s", err->message); g_error_free(err); g_free(path); pending_request_free(request); return reply; } g_free(pbap->path); pbap->path = path; return NULL; } static DBusMessage *pbap_pull_all(DBusConnection *connection, DBusMessage *message, void *user_data) { struct pbap_data *pbap = user_data; const char *targetfile; GObexApparam *apparam; DBusMessageIter args; if (!pbap->path) return g_dbus_create_error(message, ERROR_INTERFACE ".Forbidden", "Call Select first of all"); dbus_message_iter_init(message, &args); if (dbus_message_iter_get_arg_type(&args) != DBUS_TYPE_STRING) return g_dbus_create_error(message, ERROR_INTERFACE ".InvalidArguments", NULL); dbus_message_iter_get_basic(&args, &targetfile); dbus_message_iter_next(&args); apparam = g_obex_apparam_set_uint16(NULL, MAXLISTCOUNT_TAG, DEFAULT_COUNT); apparam = g_obex_apparam_set_uint16(apparam, LISTSTARTOFFSET_TAG, DEFAULT_OFFSET); if (parse_filters(apparam, &args) == NULL) { g_obex_apparam_free(apparam); return g_dbus_create_error(message, ERROR_INTERFACE ".InvalidArguments", NULL); } return pull_phonebook(pbap, message, PULLPHONEBOOK, targetfile, apparam); } static DBusMessage *pull_vcard(struct pbap_data *pbap, DBusMessage *message, const char *name, const char *targetfile, GObexApparam *apparam) { struct obc_transfer *transfer; DBusMessage *reply; GError *err = NULL; transfer = obc_transfer_get("x-bt/vcard", name, targetfile, &err); if (transfer == NULL) { g_obex_apparam_free(apparam); goto fail; } obc_transfer_set_apparam(transfer, apparam); if (!obc_session_queue(pbap->session, transfer, NULL, NULL, &err)) goto fail; return obc_transfer_create_dbus_reply(transfer, message); fail: reply = g_dbus_create_error(message, ERROR_INTERFACE ".Failed", "%s", err->message); g_error_free(err); return reply; } static DBusMessage *pbap_pull_vcard(DBusConnection *connection, DBusMessage *message, void *user_data) { struct pbap_data *pbap = user_data; GObexApparam *apparam; const char *name, *targetfile; DBusMessageIter args; if (!pbap->path) return g_dbus_create_error(message, ERROR_INTERFACE ".Forbidden", "Call Select first of all"); dbus_message_iter_init(message, &args); if (dbus_message_iter_get_arg_type(&args) != DBUS_TYPE_STRING) return g_dbus_create_error(message, ERROR_INTERFACE ".InvalidArguments", NULL); dbus_message_iter_get_basic(&args, &name); dbus_message_iter_next(&args); if (dbus_message_iter_get_arg_type(&args) != DBUS_TYPE_STRING) return g_dbus_create_error(message, ERROR_INTERFACE ".InvalidArguments", NULL); dbus_message_iter_get_basic(&args, &targetfile); dbus_message_iter_next(&args); apparam = g_obex_apparam_set_uint16(NULL, MAXLISTCOUNT_TAG, DEFAULT_COUNT); apparam = g_obex_apparam_set_uint16(apparam, LISTSTARTOFFSET_TAG, DEFAULT_OFFSET); if (parse_filters(apparam, &args) == NULL) { g_obex_apparam_free(apparam); return g_dbus_create_error(message, ERROR_INTERFACE ".InvalidArguments", NULL); } return pull_vcard(pbap, message, name, targetfile, apparam); } static DBusMessage *pbap_list(DBusConnection *connection, DBusMessage *message, void *user_data) { struct pbap_data *pbap = user_data; GObexApparam *apparam; DBusMessageIter args; if (!pbap->path) return g_dbus_create_error(message, ERROR_INTERFACE ".Forbidden", "Call Select first of all"); dbus_message_iter_init(message, &args); apparam = g_obex_apparam_set_uint16(NULL, MAXLISTCOUNT_TAG, DEFAULT_COUNT); apparam = g_obex_apparam_set_uint16(apparam, LISTSTARTOFFSET_TAG, DEFAULT_OFFSET); if (parse_filters(apparam, &args) == NULL) { g_obex_apparam_free(apparam); return g_dbus_create_error(message, ERROR_INTERFACE ".InvalidArguments", NULL); } return pull_vcard_listing(pbap, message, "", apparam); } static GObexApparam *parse_attribute(GObexApparam *apparam, const char *field) { guint8 attrib; if (!field || g_str_equal(field, "")) attrib = ATTRIB_NAME; else if (!g_ascii_strcasecmp(field, "name")) attrib = ATTRIB_NAME; else if (!g_ascii_strcasecmp(field, "number")) attrib = ATTRIB_NUMBER; else if (!g_ascii_strcasecmp(field, "sound")) attrib = ATTRIB_SOUND; else return NULL; return g_obex_apparam_set_uint8(apparam, SEARCHATTRIB_TAG, attrib); } static DBusMessage *pbap_search(DBusConnection *connection, DBusMessage *message, void *user_data) { struct pbap_data *pbap = user_data; char *field, *value; GObexApparam *apparam; DBusMessageIter args; if (!pbap->path) return g_dbus_create_error(message, ERROR_INTERFACE ".Forbidden", "Call Select first of all"); dbus_message_iter_init(message, &args); if (dbus_message_iter_get_arg_type(&args) != DBUS_TYPE_STRING) return g_dbus_create_error(message, ERROR_INTERFACE ".InvalidArguments", NULL); dbus_message_iter_get_basic(&args, &field); dbus_message_iter_next(&args); apparam = parse_attribute(NULL, field); if (apparam == NULL) return g_dbus_create_error(message, ERROR_INTERFACE ".InvalidArguments", NULL); if (dbus_message_iter_get_arg_type(&args) != DBUS_TYPE_STRING) { g_obex_apparam_free(apparam); return g_dbus_create_error(message, ERROR_INTERFACE ".InvalidArguments", NULL); } dbus_message_iter_get_basic(&args, &value); dbus_message_iter_next(&args); apparam = g_obex_apparam_set_uint16(apparam, MAXLISTCOUNT_TAG, DEFAULT_COUNT); apparam = g_obex_apparam_set_uint16(apparam, LISTSTARTOFFSET_TAG, DEFAULT_OFFSET); apparam = g_obex_apparam_set_string(apparam, SEARCHVALUE_TAG, value); if (parse_filters(apparam, &args) == NULL) { g_obex_apparam_free(apparam); return g_dbus_create_error(message, ERROR_INTERFACE ".InvalidArguments", NULL); } return pull_vcard_listing(pbap, message, "", apparam); } static DBusMessage *pbap_get_size(DBusConnection *connection, DBusMessage *message, void *user_data) { struct pbap_data *pbap = user_data; GObexApparam *apparam; DBusMessageIter args; if (!pbap->path) return g_dbus_create_error(message, ERROR_INTERFACE ".Forbidden", "Call Select first of all"); dbus_message_iter_init(message, &args); apparam = g_obex_apparam_set_uint16(NULL, MAXLISTCOUNT_TAG, 0); apparam = g_obex_apparam_set_uint16(apparam, LISTSTARTOFFSET_TAG, DEFAULT_OFFSET); return pull_phonebook(pbap, message, GETPHONEBOOKSIZE, NULL, apparam); } static char **get_filter_strs(uint64_t filter, int *size) { char **list, **item; int i; list = g_malloc0(sizeof(char **) * (FILTER_BIT_MAX + 2)); item = list; for (i = 0; filter_list[i] != NULL; i++) if (filter & (1ULL << i)) *(item++) = g_strdup(filter_list[i]); for (; i <= FILTER_BIT_MAX; i++) if (filter & (1ULL << i)) *(item++) = g_strdup_printf("%s%d", "BIT", i); *item = NULL; *size = item - list; return list; } static DBusMessage *pbap_list_filter_fields(DBusConnection *connection, DBusMessage *message, void *user_data) { char **filters = NULL; int size; DBusMessage *reply; filters = get_filter_strs(FILTER_ALL, &size); reply = dbus_message_new_method_return(message); dbus_message_append_args(reply, DBUS_TYPE_ARRAY, DBUS_TYPE_STRING, &filters, size, DBUS_TYPE_INVALID); g_strfreev(filters); return reply; } static DBusMessage *pbap_update_version(DBusConnection *connection, DBusMessage *message, void *user_data) { struct pbap_data *pbap = user_data; if (!(pbap->supported_features & FOLDER_VERSION_FEATURE)) return g_dbus_create_error(message, ERROR_INTERFACE ".NotSupported", "Operation is not supported"); return pbap_get_size(connection, message, user_data); } static const GDBusMethodTable pbap_methods[] = { { GDBUS_ASYNC_METHOD("Select", GDBUS_ARGS({ "location", "s" }, { "phonebook", "s" }), NULL, pbap_select) }, { GDBUS_METHOD("PullAll", GDBUS_ARGS({ "targetfile", "s" }, { "filters", "a{sv}" }), GDBUS_ARGS({ "transfer", "o" }, { "properties", "a{sv}" }), pbap_pull_all) }, { GDBUS_METHOD("Pull", GDBUS_ARGS({ "vcard", "s" }, { "targetfile", "s" }, { "filters", "a{sv}" }), GDBUS_ARGS({ "transfer", "o" }, { "properties", "a{sv}" }), pbap_pull_vcard) }, { GDBUS_ASYNC_METHOD("List", GDBUS_ARGS({ "filters", "a{sv}" }), GDBUS_ARGS({ "vcard_listing", "a(ss)" }), pbap_list) }, { GDBUS_ASYNC_METHOD("Search", GDBUS_ARGS({ "field", "s" }, { "value", "s" }, { "filters", "a{sv}" }), GDBUS_ARGS({ "vcard_listing", "a(ss)" }), pbap_search) }, { GDBUS_ASYNC_METHOD("GetSize", NULL, GDBUS_ARGS({ "size", "q" }), pbap_get_size) }, { GDBUS_METHOD("ListFilterFields", NULL, GDBUS_ARGS({ "fields", "as" }), pbap_list_filter_fields) }, { GDBUS_ASYNC_METHOD("UpdateVersion", NULL, NULL, pbap_update_version) }, { } }; static gboolean folder_exists(const GDBusPropertyTable *property, void *data) { struct pbap_data *pbap = data; return pbap->path != NULL; } static gboolean get_folder(const GDBusPropertyTable *property, DBusMessageIter *iter, void *data) { struct pbap_data *pbap = data; if (!pbap->path) return FALSE; dbus_message_iter_append_basic(iter, DBUS_TYPE_STRING, &pbap->path); return TRUE; } static gboolean databaseid_exists(const GDBusPropertyTable *property, void *data) { struct pbap_data *pbap = data; return pbap->supported_features & DATABASEID_FEATURE; } static int u128_to_string(uint8_t *data, char *str, size_t len) { return snprintf(str, len, "%02X%02X%02X%02X%02X%02X%02X%02X" "%02X%02X%02X%02X%02X%02X%02X%02X", data[0], data[1], data[2], data[3], data[3], data[5], data[6], data[7], data[8], data[9], data[10], data[11], data[12], data[13], data[14], data[15]); } static gboolean get_databaseid(const GDBusPropertyTable *property, DBusMessageIter *iter, void *data) { struct pbap_data *pbap = data; char value[33]; const char *pvalue = value; if (u128_to_string(pbap->databaseid, value, sizeof(value)) < 0) return FALSE; dbus_message_iter_append_basic(iter, DBUS_TYPE_STRING, &pvalue); return TRUE; } static gboolean version_exists(const GDBusPropertyTable *property, void *data) { struct pbap_data *pbap = data; return pbap->supported_features & FOLDER_VERSION_FEATURE; } static gboolean get_primary(const GDBusPropertyTable *property, DBusMessageIter *iter, void *data) { struct pbap_data *pbap = data; char value[33]; const char *pvalue = value; if (u128_to_string(pbap->primary, value, sizeof(value)) < 0) return FALSE; dbus_message_iter_append_basic(iter, DBUS_TYPE_STRING, &pvalue); return TRUE; } static gboolean get_secondary(const GDBusPropertyTable *property, DBusMessageIter *iter, void *data) { struct pbap_data *pbap = data; char value[33]; const char *pvalue = value; if (u128_to_string(pbap->secondary, value, sizeof(value)) < 0) return FALSE; dbus_message_iter_append_basic(iter, DBUS_TYPE_STRING, &pvalue); return TRUE; } static gboolean image_size_exists(const GDBusPropertyTable *property, void *data) { struct pbap_data *pbap = data; return pbap->supported_features & DEFAULT_IMAGE_FEATURE; } static gboolean get_image_size(const GDBusPropertyTable *property, DBusMessageIter *iter, void *data) { dbus_bool_t value = TRUE; dbus_message_iter_append_basic(iter, DBUS_TYPE_BOOLEAN, &value); return TRUE; } static const GDBusPropertyTable pbap_properties[] = { { "Folder", "s", get_folder, NULL, folder_exists }, { "DatabaseIdentifier", "s", get_databaseid, NULL, databaseid_exists }, { "PrimaryCounter", "s", get_primary, NULL, version_exists }, { "SecondaryCounter", "s", get_secondary, NULL, version_exists }, { "FixedImageSize", "b", get_image_size, NULL, image_size_exists }, { } }; static void pbap_free(void *data) { struct pbap_data *pbap = data; obc_session_unref(pbap->session); g_free(pbap->path); g_free(pbap); } static void parse_service_record(struct pbap_data *pbap) { const void *data; /* Version */ data = obc_session_get_attribute(pbap->session, SDP_ATTR_PFILE_DESC_LIST); if (!data) return; pbap->version = GPOINTER_TO_UINT(data); /* * If the PbapSupportedFeatures attribute is not present * 0x00000003 shall be assumed for a remote PSE. */ pbap->supported_features = 0x00000003; if (pbap->version < 0x0102) return; /* Supported Feature Bits */ data = obc_session_get_attribute(pbap->session, SDP_ATTR_PBAP_SUPPORTED_FEATURES); if (data) pbap->supported_features = *(uint32_t *) data; } static void *pbap_supported_features(struct obc_session *session) { const void *data; uint16_t version; uint32_t features; /* Version */ data = obc_session_get_attribute(session, SDP_ATTR_PFILE_DESC_LIST); if (!data) return NULL; version = GPOINTER_TO_UINT(data); if (version < 0x0102) return NULL; /* Supported Feature Bits */ data = obc_session_get_attribute(session, SDP_ATTR_PBAP_SUPPORTED_FEATURES); if (!data) return NULL; features = *(uint32_t *) data; if (!features) return NULL; return g_obex_apparam_set_uint32(NULL, SUPPORTED_FEATURES_TAG, DOWNLOAD_FEATURE | BROWSE_FEATURE | DATABASEID_FEATURE | FOLDER_VERSION_FEATURE | VCARD_SELECTING_FEATURE | ENHANCED_CALLS_FEATURE | UCI_FEATURE | UID_FEATURE | REFERENCING_FEATURE | DEFAULT_IMAGE_FEATURE); } static int pbap_probe(struct obc_session *session) { struct pbap_data *pbap; const char *path; path = obc_session_get_path(session); DBG("%s", path); pbap = g_try_new0(struct pbap_data, 1); if (!pbap) return -ENOMEM; pbap->session = obc_session_ref(session); parse_service_record(pbap); DBG("%s, version 0x%04x supported features 0x%08x", path, pbap->version, pbap->supported_features); if (!g_dbus_register_interface(conn, path, PBAP_INTERFACE, pbap_methods, NULL, pbap_properties, pbap, pbap_free)) { pbap_free(pbap); return -ENOMEM; } return 0; } static void pbap_remove(struct obc_session *session) { const char *path = obc_session_get_path(session); DBG("%s", path); g_dbus_unregister_interface(conn, path, PBAP_INTERFACE); } static DBusMessage *pbap_release(DBusConnection *conn, DBusMessage *msg, void *data) { return g_dbus_create_reply(msg, DBUS_TYPE_INVALID); } static DBusMessage *pbap_new_connection(DBusConnection *conn, DBusMessage *msg, void *data) { return g_dbus_create_reply(msg, DBUS_TYPE_INVALID); } static DBusMessage *pbap_request_disconnection(DBusConnection *conn, DBusMessage *msg, void *data) { return g_dbus_create_reply(msg, DBUS_TYPE_INVALID); } static DBusMessage *pbap_cancel(DBusConnection *conn, DBusMessage *msg, void *data) { return g_dbus_create_reply(msg, DBUS_TYPE_INVALID); } static const GDBusMethodTable profile_methods[] = { { GDBUS_METHOD("Release", NULL, NULL, pbap_release) }, { GDBUS_METHOD("NewConnection", GDBUS_ARGS({ "device", "o" }, { "fd", "h" }, { "options", "a{sv}" }), NULL, pbap_new_connection) }, { GDBUS_METHOD("RequestDisconnection", GDBUS_ARGS({ "device", "o" }), NULL, pbap_request_disconnection) }, { GDBUS_METHOD("Cancel", NULL, NULL, pbap_cancel) }, { } }; static void unregister_profile(void) { g_dbus_unregister_interface(system_conn, client_path, "org.bluez.Profile1"); g_free(client_path); client_path = NULL; } static void register_profile_reply(DBusPendingCall *call, void *user_data) { DBusMessage *reply = dbus_pending_call_steal_reply(call); DBusError derr; dbus_error_init(&derr); if (!dbus_set_error_from_message(&derr, reply)) { DBG("Profile %s registered", client_path); goto done; } unregister_profile(); error("bluetooth: RequestProfile error: %s, %s", derr.name, derr.message); dbus_error_free(&derr); done: dbus_message_unref(reply); } static int register_profile(void) { DBusMessage *msg; DBusMessageIter iter, opt; DBusPendingCall *call; char *uuid = PBAP_CLIENT_UUID; dbus_bool_t auto_connect = FALSE; int ret = 0; client_path = g_strconcat("/org/bluez/obex/", uuid, NULL); g_strdelimit(client_path, "-", '_'); if (!g_dbus_register_interface(system_conn, client_path, "org.bluez.Profile1", profile_methods, NULL, NULL, NULL, NULL)) { error("D-Bus failed to register %s", client_path); g_free(client_path); client_path = NULL; return -1; } msg = dbus_message_new_method_call("org.bluez", "/org/bluez", "org.bluez.ProfileManager1", "RegisterProfile"); dbus_message_iter_init_append(msg, &iter); dbus_message_iter_append_basic(&iter, DBUS_TYPE_OBJECT_PATH, &client_path); dbus_message_iter_append_basic(&iter, DBUS_TYPE_STRING, &uuid); dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY, DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING DBUS_TYPE_STRING_AS_STRING DBUS_TYPE_VARIANT_AS_STRING DBUS_DICT_ENTRY_END_CHAR_AS_STRING, &opt); g_dbus_dict_append_entry(&opt, "AutoConnect", DBUS_TYPE_BOOLEAN, &auto_connect); dbus_message_iter_close_container(&iter, &opt); if (!g_dbus_send_message_with_reply(system_conn, msg, &call, -1)) { ret = -1; unregister_profile(); goto failed; } dbus_pending_call_set_notify(call, register_profile_reply, NULL, NULL); dbus_pending_call_unref(call); failed: dbus_message_unref(msg); return ret; } static void name_acquired(DBusConnection *conn, void *user_data) { DBG("org.bluez appeared"); if (register_profile() < 0) { error("bluetooth: Failed to register profile %s", client_path); g_free(client_path); client_path = NULL; } } static void name_released(DBusConnection *conn, void *user_data) { DBG("org.bluez disappeared"); unregister_profile(); } static struct obc_driver pbap = { .service = "PBAP", .uuid = PBAP_UUID, .target = OBEX_PBAP_UUID, .target_len = OBEX_PBAP_UUID_LEN, .supported_features = pbap_supported_features, .probe = pbap_probe, .remove = pbap_remove }; int pbap_init(void) { int err; DBG(""); conn = obex_get_dbus_connection(); if (!conn) return -EIO; system_conn = g_dbus_setup_private(DBUS_BUS_SYSTEM, NULL, NULL); if (system_conn == NULL) return -EIO; err = obc_driver_register(&pbap); if (err < 0) { dbus_connection_unref(conn); conn = NULL; return err; } listener_id = g_dbus_add_service_watch(system_conn, "org.bluez", name_acquired, name_released, NULL, NULL); return 0; } void pbap_exit(void) { DBG(""); dbus_connection_unref(conn); conn = NULL; obc_driver_unregister(&pbap); } bluez-5.82/obexd/client/PaxHeaders/sync.c0000644000000000000000000000005014766002272015353 xustar0020 atime=1743515578 20 ctime=1743591283 bluez-5.82/obexd/client/sync.c0000644000000000000000000001303414766002272015035 0ustar00rootroot// SPDX-License-Identifier: GPL-2.0-or-later /* * * OBEX Client * * Copyright (C) 2007-2010 Intel Corporation * Copyright (C) 2007-2010 Marcel Holtmann * * */ #ifdef HAVE_CONFIG_H #include #endif #include #include #include #include "gdbus/gdbus.h" #include "obexd/src/log.h" #include "obexd/src/obexd.h" #include "transfer.h" #include "session.h" #include "driver.h" #include "sync.h" #define OBEX_SYNC_UUID "IRMC-SYNC" #define OBEX_SYNC_UUID_LEN 9 #define SYNC_INTERFACE "org.bluez.obex.Synchronization1" #define ERROR_INF SYNC_INTERFACE ".Error" #define SYNC_UUID "00001104-0000-1000-8000-00805f9b34fb" struct sync_data { struct obc_session *session; char *phonebook_path; DBusMessage *msg; }; static DBusConnection *conn = NULL; static DBusMessage *sync_setlocation(DBusConnection *connection, DBusMessage *message, void *user_data) { struct sync_data *sync = user_data; const char *location; char *path = NULL, *tmp; if (dbus_message_get_args(message, NULL, DBUS_TYPE_STRING, &location, DBUS_TYPE_INVALID) == FALSE) return g_dbus_create_error(message, ERROR_INF ".InvalidArguments", NULL); if (!g_ascii_strcasecmp(location, "int") || !g_ascii_strcasecmp(location, "internal")) path = g_strdup("telecom/pb.vcf"); else if (!g_ascii_strncasecmp(location, "sim", 3)) { tmp = g_ascii_strup(location, 4); path = g_build_filename(tmp, "telecom/pb.vcf", NULL); g_free(tmp); } else return g_dbus_create_error(message, ERROR_INF ".InvalidArguments", "InvalidPhonebook"); g_free(sync->phonebook_path); sync->phonebook_path = path; return dbus_message_new_method_return(message); } static DBusMessage *sync_getphonebook(DBusConnection *connection, DBusMessage *message, void *user_data) { struct sync_data *sync = user_data; struct obc_transfer *transfer; const char *target_file; GError *err = NULL; DBusMessage *reply; if (dbus_message_get_args(message, NULL, DBUS_TYPE_STRING, &target_file, DBUS_TYPE_INVALID) == FALSE) return g_dbus_create_error(message, ERROR_INF ".InvalidArguments", "Invalid arguments in method call"); if (sync->msg) return g_dbus_create_error(message, ERROR_INF ".InProgress", "Transfer in progress"); /* set default phonebook_path to memory internal phonebook */ if (!sync->phonebook_path) sync->phonebook_path = g_strdup("telecom/pb.vcf"); transfer = obc_transfer_get("phonebook", sync->phonebook_path, target_file, &err); if (transfer == NULL) goto fail; if (!obc_session_queue(sync->session, transfer, NULL, NULL, &err)) goto fail; return obc_transfer_create_dbus_reply(transfer, message); fail: reply = g_dbus_create_error(message, ERROR_INF ".Failed", "%s", err->message); g_error_free(err); return reply; } static DBusMessage *sync_putphonebook(DBusConnection *connection, DBusMessage *message, void *user_data) { struct sync_data *sync = user_data; struct obc_transfer *transfer; const char *source_file; GError *err = NULL; DBusMessage *reply; if (dbus_message_get_args(message, NULL, DBUS_TYPE_STRING, &source_file, DBUS_TYPE_INVALID) == FALSE) return g_dbus_create_error(message, ERROR_INF ".InvalidArguments", "Invalid arguments in method call"); /* set default phonebook_path to memory internal phonebook */ if (!sync->phonebook_path) sync->phonebook_path = g_strdup("telecom/pb.vcf"); transfer = obc_transfer_put(NULL, sync->phonebook_path, source_file, NULL, 0, &err); if (transfer == NULL) goto fail; if (!obc_session_queue(sync->session, transfer, NULL, NULL, &err)) goto fail; return obc_transfer_create_dbus_reply(transfer, message); fail: reply = g_dbus_create_error(message, ERROR_INF ".Failed", "%s", err->message); g_error_free(err); return reply; } static const GDBusMethodTable sync_methods[] = { { GDBUS_METHOD("SetLocation", GDBUS_ARGS({ "location", "s" }), NULL, sync_setlocation) }, { GDBUS_METHOD("GetPhonebook", GDBUS_ARGS({ "targetfile", "s" }), GDBUS_ARGS({ "transfer", "o" }, { "properties", "a{sv}" }), sync_getphonebook) }, { GDBUS_METHOD("PutPhonebook", GDBUS_ARGS({ "sourcefile", "s" }), GDBUS_ARGS({ "transfer", "o" }, { "properties", "a{sv}" }), sync_putphonebook) }, { } }; static void sync_free(void *data) { struct sync_data *sync = data; obc_session_unref(sync->session); g_free(sync->phonebook_path); g_free(sync); } static int sync_probe(struct obc_session *session) { struct sync_data *sync; const char *path; path = obc_session_get_path(session); DBG("%s", path); sync = g_try_new0(struct sync_data, 1); if (!sync) return -ENOMEM; sync->session = obc_session_ref(session); if (!g_dbus_register_interface(conn, path, SYNC_INTERFACE, sync_methods, NULL, NULL, sync, sync_free)) { sync_free(sync); return -ENOMEM; } return 0; } static void sync_remove(struct obc_session *session) { const char *path = obc_session_get_path(session); DBG("%s", path); g_dbus_unregister_interface(conn, path, SYNC_INTERFACE); } static struct obc_driver sync = { .service = "SYNC", .uuid = SYNC_UUID, .target = OBEX_SYNC_UUID, .target_len = OBEX_SYNC_UUID_LEN, .probe = sync_probe, .remove = sync_remove }; int sync_init(void) { int err; DBG(""); conn = obex_get_dbus_connection(); if (!conn) return -EIO; err = obc_driver_register(&sync); if (err < 0) { dbus_connection_unref(conn); conn = NULL; return err; } return 0; } void sync_exit(void) { DBG(""); dbus_connection_unref(conn); conn = NULL; obc_driver_unregister(&sync); } bluez-5.82/obexd/client/PaxHeaders/bip.c0000644000000000000000000000005014766002272015151 xustar0020 atime=1743515578 20 ctime=1743591283 bluez-5.82/obexd/client/bip.c0000644000000000000000000002463314766002272014642 0ustar00rootroot/* SPDX-License-Identifier: GPL-2.0-or-later */ /* * * OBEX Client * * Copyright (C) 2024 Collabora Ltd. * * */ #define _GNU_SOURCE #include #include #include #include "gdbus/gdbus.h" #include "gobex/gobex.h" #include "obexd/src/log.h" #include "obexd/src/obexd.h" #include "transfer.h" #include "session.h" #include "driver.h" #include "bip-common.h" #include "bip.h" #define OBEX_BIP_AVRCP_UUID \ "\x71\x63\xDD\x54\x4A\x7E\x11\xE2\xB4\x7C\x00\x50\xC2\x49\x00\x48" #define OBEX_BIP_AVRCP_UUID_LEN 16 #define IMAGE_INTERFACE "org.bluez.obex.Image1" #define ERROR_INTERFACE "org.bluez.obex.Error" #define IMAGE_UUID "0000111A-0000-1000-8000-00805f9b34fb" #define IMG_HANDLE_TAG 0x30 #define IMG_DESC_TAG 0x71 #define EOL_CHARS "\n" #define IMG_DESC_BEGIN "" EOL_CHARS #define IMG_BEGIN "" EOL_CHARS #define IMG_DESC_END "" EOL_CHARS static DBusConnection *conn; struct bip_avrcp_data { struct obc_session *session; }; static void image_properties_complete_cb(struct obc_session *session, struct obc_transfer *transfer, GError *err, void *user_data) { DBusMessage *message = user_data; DBusMessage *reply = NULL; DBusMessageIter iter; char *contents = NULL; size_t size; int perr; struct prop_object *prop = NULL; if (err != NULL) { reply = g_dbus_create_error(message, ERROR_INTERFACE ".Failed", "%s", err->message); goto done; } perr = obc_transfer_get_contents(transfer, &contents, &size); if (perr < 0) { reply = g_dbus_create_error(message, ERROR_INTERFACE ".Failed", "Error reading contents: %s", strerror(-perr)); goto done; } prop = parse_properties(contents, size, &perr); if (prop == NULL) { reply = g_dbus_create_error(message, ERROR_INTERFACE ".Failed", "Error parsing contents: %s", strerror(-perr)); goto done; } if (!verify_properties(prop)) { reply = g_dbus_create_error(message, ERROR_INTERFACE ".Failed", "Error verifying contents"); goto done; } reply = dbus_message_new_method_return(message); dbus_message_iter_init_append(reply, &iter); append_properties(&iter, prop); done: g_dbus_send_message(conn, reply); g_free(contents); dbus_message_unref(message); } static DBusMessage *get_image_properties(DBusConnection *connection, DBusMessage *message, void *user_data) { struct bip_avrcp_data *bip_avrcp = user_data; const char *handle = NULL; struct obc_transfer *transfer; GObexHeader *header; DBusMessage *reply = NULL; GError *err = NULL; DBG(""); if (dbus_message_get_args(message, NULL, DBUS_TYPE_STRING, &handle, DBUS_TYPE_INVALID) == FALSE) { reply = g_dbus_create_error(message, ERROR_INTERFACE ".InvalidArguments", NULL); return reply; } transfer = obc_transfer_get("x-bt/img-properties", NULL, NULL, &err); if (transfer == NULL) goto fail; header = g_obex_header_new_unicode(IMG_HANDLE_TAG, handle); obc_transfer_add_header(transfer, header); if (!obc_session_queue(bip_avrcp->session, transfer, image_properties_complete_cb, message, &err)) goto fail; dbus_message_ref(message); return NULL; fail: reply = g_dbus_create_error(message, ERROR_INTERFACE ".Failed", "%s", err->message); g_error_free(err); return reply; } static DBusMessage *get_thumbnail(DBusConnection *connection, DBusMessage *message, void *user_data) { struct bip_avrcp_data *bip_avrcp = user_data; const char *handle = NULL, *image_path = NULL; struct obc_transfer *transfer; GObexHeader *header; DBusMessage *reply = NULL; GError *err = NULL; DBG(""); if (dbus_message_get_args(message, NULL, DBUS_TYPE_STRING, &image_path, DBUS_TYPE_STRING, &handle, DBUS_TYPE_INVALID) == FALSE) { reply = g_dbus_create_error(message, ERROR_INTERFACE ".InvalidArguments", NULL); return reply; } transfer = obc_transfer_get("x-bt/img-thm", NULL, image_path, &err); if (transfer == NULL) goto fail; header = g_obex_header_new_unicode(IMG_HANDLE_TAG, handle); obc_transfer_add_header(transfer, header); if (!obc_session_queue(bip_avrcp->session, transfer, NULL, NULL, &err)) goto fail; return obc_transfer_create_dbus_reply(transfer, message); fail: reply = g_dbus_create_error(message, ERROR_INTERFACE ".Failed", "%s", err->message); g_error_free(err); return reply; } static gboolean parse_get_image_dict(DBusMessage *msg, char **path, char **handle, char **pixel, char **encoding, uint64_t *maxsize, char **transform) { DBusMessageIter iter, array; DBG(""); *path = NULL; *handle = NULL; *pixel = NULL; *encoding = NULL; *transform = NULL; dbus_message_iter_init(msg, &iter); if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_STRING) goto failed; dbus_message_iter_get_basic(&iter, path); *path = g_strdup(*path); if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_STRING) goto failed; dbus_message_iter_next(&iter); dbus_message_iter_get_basic(&iter, handle); *handle = g_strdup(*handle); dbus_message_iter_next(&iter); if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_ARRAY) goto failed; dbus_message_iter_recurse(&iter, &array); while (dbus_message_iter_get_arg_type(&array) == DBUS_TYPE_DICT_ENTRY) { DBusMessageIter entry, value; const char *key, *val; dbus_message_iter_recurse(&array, &entry); if (dbus_message_iter_get_arg_type(&entry) != DBUS_TYPE_STRING) return FALSE; dbus_message_iter_get_basic(&entry, &key); dbus_message_iter_next(&entry); dbus_message_iter_recurse(&entry, &value); switch (dbus_message_iter_get_arg_type(&value)) { case DBUS_TYPE_STRING: dbus_message_iter_get_basic(&value, &val); if (g_str_equal(key, "pixel")) { if (!parse_pixel_range(val, NULL, NULL, NULL)) goto failed; *pixel = g_strdup(val); } else if (g_str_equal(key, "encoding")) { if (!verify_encoding(val)) goto failed; *encoding = g_strdup(val); if (*encoding == NULL) goto failed; } else if (g_str_equal(key, "transformation")) { *transform = parse_transform(val); if (*transform == NULL) goto failed; } break; case DBUS_TYPE_UINT64: if (g_str_equal(key, "maxsize") == TRUE) { dbus_message_iter_get_basic(&value, maxsize); if (*maxsize == 0) goto failed; } break; } dbus_message_iter_next(&array); } if (*pixel == NULL) *pixel = strdup(""); if (*encoding == NULL) *encoding = strdup(""); DBG("pixel: '%s' encoding: '%s' maxsize: '%lu' transform: '%s'", *pixel, *encoding, *maxsize, *transform ); return TRUE; failed: g_free(*path); g_free(*handle); g_free(*pixel); g_free(*encoding); g_free(*transform); return FALSE; } static DBusMessage *get_image(DBusConnection *connection, DBusMessage *message, void *user_data) { struct bip_avrcp_data *bip_avrcp = user_data; char *handle = NULL, *image_path = NULL, *transform = NULL, *encoding = NULL, *pixel = NULL; uint64_t maxsize; struct obc_transfer *transfer; GObexHeader *header; DBusMessage *reply = NULL; GString *descriptor = NULL; GError *err = NULL; DBG(""); if (!parse_get_image_dict(message, &image_path, &handle, &pixel, &encoding, &maxsize, &transform)) return g_dbus_create_error(message, ERROR_INTERFACE ".InvalidArguments", NULL); transfer = obc_transfer_get("x-bt/img-img", NULL, image_path, &err); if (transfer == NULL) { reply = g_dbus_create_error(message, ERROR_INTERFACE ".Failed", "%s", err->message); g_error_free(err); goto fail; } header = g_obex_header_new_unicode(IMG_HANDLE_TAG, handle); obc_transfer_add_header(transfer, header); descriptor = g_string_new(IMG_DESC_BEGIN); g_string_append_printf(descriptor, IMG_BEGIN, encoding, pixel); if (transform != NULL) g_string_append_printf(descriptor, IMG_TRANSFORM, transform); g_string_append(descriptor, IMG_END); descriptor = g_string_append(descriptor, IMG_DESC_END); header = g_obex_header_new_bytes(IMG_DESC_TAG, descriptor->str, descriptor->len); obc_transfer_add_header(transfer, header); g_string_free(descriptor, TRUE); if (!obc_session_queue(bip_avrcp->session, transfer, NULL, NULL, &err)) { reply = g_dbus_create_error(message, ERROR_INTERFACE ".Failed", "%s", err->message); g_error_free(err); goto fail; } reply = obc_transfer_create_dbus_reply(transfer, message); fail: g_free(handle); g_free(image_path); g_free(transform); g_free(encoding); g_free(pixel); return reply; } static const GDBusMethodTable bip_avrcp_methods[] = { { GDBUS_ASYNC_METHOD("Properties", GDBUS_ARGS({ "handle", "s"}), GDBUS_ARGS({ "properties", "aa{sv}" }), get_image_properties) }, { GDBUS_ASYNC_METHOD("Get", GDBUS_ARGS({ "file", "s" }, { "handle", "s"}, {"properties", "a{sv}"}), GDBUS_ARGS({ "transfer", "o" }, { "properties", "a{sv}" }), get_image) }, { GDBUS_ASYNC_METHOD("GetThumbnail", GDBUS_ARGS({ "file", "s" }, { "handle", "s"}), GDBUS_ARGS({ "transfer", "o" }, { "properties", "a{sv}" }), get_thumbnail) }, { } }; static void bip_avrcp_free(void *data) { struct bip_avrcp_data *bip_avrcp = data; obc_session_unref(bip_avrcp->session); g_free(bip_avrcp); } static int bip_avrcp_probe(struct obc_session *session) { struct bip_avrcp_data *bip_avrcp; const char *path; path = obc_session_get_path(session); DBG("%s", path); bip_avrcp = g_try_new0(struct bip_avrcp_data, 1); if (!bip_avrcp) return -ENOMEM; bip_avrcp->session = obc_session_ref(session); if (!g_dbus_register_interface(conn, path, IMAGE_INTERFACE, bip_avrcp_methods, NULL, NULL, bip_avrcp, bip_avrcp_free)) { bip_avrcp_free(bip_avrcp); return -ENOMEM; } return 0; } static void bip_avrcp_remove(struct obc_session *session) { const char *path = obc_session_get_path(session); DBG("%s", path); g_dbus_unregister_interface(conn, path, IMAGE_INTERFACE); } static struct obc_driver bip_avrcp = { .service = "BIP-AVRCP", .uuid = IMAGE_UUID, .target = OBEX_BIP_AVRCP_UUID, .target_len = OBEX_BIP_AVRCP_UUID_LEN, .probe = bip_avrcp_probe, .remove = bip_avrcp_remove }; int bip_init(void) { int err; DBG(""); conn = obex_get_dbus_connection(); if (!conn) return -EIO; err = obc_driver_register(&bip_avrcp); if (err < 0) goto failed; return 0; failed: dbus_connection_unref(conn); conn = NULL; return err; } void bip_exit(void) { DBG(""); dbus_connection_unref(conn); conn = NULL; obc_driver_unregister(&bip_avrcp); } bluez-5.82/obexd/client/PaxHeaders/bip-common.h0000644000000000000000000000005014711225434016441 xustar0020 atime=1743516758 20 ctime=1743591283 bluez-5.82/obexd/client/bip-common.h0000644000000000000000000000116614711225434016126 0ustar00rootroot/* SPDX-License-Identifier: GPL-2.0-or-later */ /* * * OBEX Client * * Copyright (C) 2024 Collabora Ltd. * * */ #include #include "gdbus/gdbus.h" struct prop_object; gboolean parse_pixel_range(const gchar *dim, unsigned int *lower_ret, unsigned int *upper_ret, gboolean *fixed_ratio_ret); gboolean verify_encoding(const char *encoding); char *parse_transform(const char *transform); struct prop_object *parse_properties(char *data, unsigned int length, int *err); gboolean verify_properties(struct prop_object *obj); void append_properties(DBusMessageIter *args, struct prop_object *obj); bluez-5.82/obexd/client/PaxHeaders/manager.h0000644000000000000000000000005014015011623016001 xustar0020 atime=1743516732 20 ctime=1743591283 bluez-5.82/obexd/client/manager.h0000644000000000000000000000032614015011623015463 0ustar00rootroot/* SPDX-License-Identifier: GPL-2.0-or-later */ /* * * OBEX Server * * Copyright (C) 2007-2010 Marcel Holtmann * * */ int client_manager_init(void); void client_manager_exit(void); bluez-5.82/obexd/client/PaxHeaders/session.c0000644000000000000000000000005014766002272016062 xustar0020 atime=1743515578 20 ctime=1743591283 bluez-5.82/obexd/client/session.c0000644000000000000000000007475514766002272015565 0ustar00rootroot// SPDX-License-Identifier: GPL-2.0-or-later /* * * OBEX Client * * Copyright (C) 2007-2010 Marcel Holtmann * Copyright (C) 2011-2012 BMW Car IT GmbH. All rights reserved. * * */ #ifdef HAVE_CONFIG_H #include #endif #include #include #include #include #include #include #include #include #include "gdbus/gdbus.h" #include "gobex/gobex.h" #include "obexd/src/log.h" #include "obexd/src/obexd.h" #include "transfer.h" #include "session.h" #include "driver.h" #include "transport.h" #define SESSION_INTERFACE "org.bluez.obex.Session1" #define ERROR_INTERFACE "org.bluez.obex.Error" #define SESSION_BASEPATH "/org/bluez/obex/client" #define OBEX_IO_ERROR obex_io_error_quark() #define OBEX_IO_ERROR_FIRST (0xff + 1) enum { OBEX_IO_DISCONNECTED = OBEX_IO_ERROR_FIRST, OBEX_IO_BUSY, }; static guint64 counter = 0; struct callback_data { struct obc_session *session; guint id; session_callback_t func; void *data; }; struct pending_request; typedef int (*session_process_t) (struct pending_request *p, GError **err); typedef void (*destroy_t) (void *data); struct pending_request { guint id; guint req_id; struct obc_session *session; session_process_t process; struct obc_transfer *transfer; session_callback_t func; void *data; destroy_t destroy; }; struct setpath_data { char **remaining; int index; session_callback_t func; void *user_data; }; struct file_data { char *srcname; char *destname; session_callback_t func; void *user_data; }; struct obc_session { guint id; int refcount; char *source; char *destination; uint8_t channel; uint16_t psm; struct obc_transport *transport; struct obc_driver *driver; char *path; /* Session path */ DBusConnection *conn; GObex *obex; struct pending_request *p; char *owner; /* Session owner */ guint watch; GQueue *queue; guint process_id; char *folder; struct callback_data *callback; }; static GSList *sessions = NULL; static void session_process_queue(struct obc_session *session); static void session_terminate_transfer(struct obc_session *session, struct obc_transfer *transfer, GError *gerr); static void transfer_complete(struct obc_transfer *transfer, GError *err, void *user_data); static GQuark obex_io_error_quark(void) { return g_quark_from_static_string("obex-io-error-quark"); } struct obc_session *obc_session_ref(struct obc_session *session) { int refs = __sync_add_and_fetch(&session->refcount, 1); DBG("%p: ref=%d", session, refs); return session; } static void session_unregistered(struct obc_session *session) { char *path; if (session->driver && session->driver->remove) session->driver->remove(session); path = session->path; session->path = NULL; g_dbus_unregister_interface(session->conn, path, SESSION_INTERFACE); DBG("Session(%p) unregistered %s", session, path); g_free(path); } static struct pending_request *pending_request_new(struct obc_session *session, session_process_t process, struct obc_transfer *transfer, session_callback_t func, void *data, destroy_t destroy) { struct pending_request *p; static guint id = 0; p = g_new0(struct pending_request, 1); p->id = ++id; p->session = obc_session_ref(session); p->process = process; p->destroy = destroy; p->transfer = transfer; p->func = func; p->data = data; return p; } static void pending_request_free(struct pending_request *p) { if (p->req_id > 0) g_obex_cancel_req(p->session->obex, p->req_id, TRUE); if (p->destroy) p->destroy(p->data); if (p->transfer) obc_transfer_unregister(p->transfer); if (p->session) obc_session_unref(p->session); g_free(p); } static void setpath_data_free(void *process_data) { struct setpath_data *data = process_data; g_strfreev(data->remaining); g_free(data); } static void file_data_free(void *process_data) { struct file_data *data = process_data; g_free(data->srcname); g_free(data->destname); g_free(data); } static void request_free(void *data, void *user_data) { pending_request_free(data); } static void session_free(struct obc_session *session) { DBG("%p", session); if (session->process_id != 0) g_source_remove(session->process_id); if (session->queue) { g_queue_foreach(session->queue, request_free, NULL); g_queue_free(session->queue); } if (session->watch) g_dbus_remove_watch(session->conn, session->watch); if (session->obex) { g_obex_set_disconnect_function(session->obex, NULL, NULL); g_obex_unref(session->obex); } if (session->id > 0 && session->transport != NULL) session->transport->disconnect(session->id); if (session->path) session_unregistered(session); if (session->conn) dbus_connection_unref(session->conn); if (session->p) pending_request_free(session->p); g_free(session->path); g_free(session->owner); g_free(session->source); g_free(session->destination); g_free(session->folder); g_free(session); } static void disconnect_complete(GObex *obex, GError *err, GObexPacket *rsp, void *user_data) { struct obc_session *session = user_data; DBG(""); if (err) error("%s", err->message); /* Disconnect transport */ if (session->id > 0 && session->transport != NULL) { session->transport->disconnect(session->id); session->id = 0; } session_free(session); } void obc_session_unref(struct obc_session *session) { int refs; refs = __sync_sub_and_fetch(&session->refcount, 1); DBG("%p: ref=%d", session, refs); if (refs > 0) return; sessions = g_slist_remove(sessions, session); if (!session->obex) goto disconnect; /* Wait OBEX Disconnect to complete if command succeed otherwise * proceed with transport disconnection since there is nothing else to * be done */ if (g_obex_disconnect(session->obex, disconnect_complete, session, NULL)) return; disconnect: /* Disconnect transport */ if (session->id > 0 && session->transport != NULL) { session->transport->disconnect(session->id); session->id = 0; } session_free(session); } static void callback_destroy(struct callback_data *callback, GError *err) { struct obc_session *session = callback->session; if (callback->id > 0) g_obex_cancel_req(session->obex, callback->id, TRUE); callback->func(session, NULL, err, callback->data); g_free(callback); session->callback = NULL; obc_session_unref(session); } static void connect_cb(GObex *obex, GError *err, GObexPacket *rsp, gpointer user_data) { struct callback_data *callback = user_data; GError *gerr = NULL; uint8_t rsp_code; callback->id = 0; if (err != NULL) { error("connect_cb: %s", err->message); gerr = g_error_copy(err); goto done; } rsp_code = g_obex_packet_get_operation(rsp, NULL); if (rsp_code != G_OBEX_RSP_SUCCESS) gerr = g_error_new(OBEX_IO_ERROR, -EIO, "OBEX Connect failed with 0x%02x", rsp_code); done: callback_destroy(callback, gerr); if (gerr != NULL) g_error_free(gerr); } static void session_disconnected(GObex *obex, GError *err, gpointer user_data) { struct obc_session *session = user_data; if (err) error("%s", err->message); obc_session_shutdown(session); } static void transport_func(GIOChannel *io, GError *err, gpointer user_data) { struct callback_data *callback = user_data; struct obc_session *session = callback->session; struct obc_driver *driver = session->driver; struct obc_transport *transport = session->transport; GObex *obex; GObexApparam *apparam; GObexTransportType type; int tx_mtu = -1; int rx_mtu = -1; DBG(""); if (err != NULL) { error("%s", err->message); goto done; } g_io_channel_set_close_on_unref(io, FALSE); if (transport->getpacketopt && transport->getpacketopt(io, &tx_mtu, &rx_mtu) == 0) type = G_OBEX_TRANSPORT_PACKET; else type = G_OBEX_TRANSPORT_STREAM; obex = g_obex_new(io, type, tx_mtu, rx_mtu); if (obex == NULL) goto done; g_io_channel_set_close_on_unref(io, TRUE); apparam = NULL; if (driver->supported_features) apparam = driver->supported_features(session); if (apparam) { uint8_t buf[1024]; ssize_t len; len = g_obex_apparam_encode(apparam, buf, sizeof(buf)); if (driver->target) callback->id = g_obex_connect(obex, connect_cb, callback, &err, G_OBEX_HDR_TARGET, driver->target, driver->target_len, G_OBEX_HDR_APPARAM, buf, len, G_OBEX_HDR_INVALID); else callback->id = g_obex_connect(obex, connect_cb, callback, &err, G_OBEX_HDR_APPARAM, buf, len, G_OBEX_HDR_INVALID); g_obex_apparam_free(apparam); } else if (driver->target) callback->id = g_obex_connect(obex, connect_cb, callback, &err, G_OBEX_HDR_TARGET, driver->target, driver->target_len, G_OBEX_HDR_INVALID); else callback->id = g_obex_connect(obex, connect_cb, callback, &err, G_OBEX_HDR_INVALID); if (err != NULL) { error("%s", err->message); g_obex_unref(obex); goto done; } session->obex = obex; sessions = g_slist_prepend(sessions, session); g_obex_set_disconnect_function(obex, session_disconnected, session); return; done: callback_destroy(callback, err); } static void owner_disconnected(DBusConnection *connection, void *user_data) { struct obc_session *session = user_data; GError *err; DBG(""); /* * If connection still connecting notify the callback to destroy the * session. */ if (session->callback) { err = g_error_new(OBEX_IO_ERROR, OBEX_IO_DISCONNECTED, "Session closed by user"); callback_destroy(session->callback, err); g_error_free(err); return; } obc_session_shutdown(session); } int obc_session_set_owner(struct obc_session *session, const char *name, GDBusWatchFunction func) { if (session == NULL) return -EINVAL; if (session->watch) g_dbus_remove_watch(session->conn, session->watch); session->watch = g_dbus_add_disconnect_watch(session->conn, name, func, session, NULL); if (session->watch == 0) return -EINVAL; session->owner = g_strdup(name); return 0; } static struct obc_session *session_find(const char *source, const char *destination, const char *service, uint8_t channel, uint16_t psm, const char *owner) { GSList *l; for (l = sessions; l; l = l->next) { struct obc_session *session = l->data; if (g_strcmp0(session->destination, destination)) continue; if (g_strcmp0(service, session->driver->service)) continue; if (source && g_strcmp0(session->source, source)) continue; if (channel && session->channel != channel) continue; if (psm && session->psm != psm) continue; if (g_strcmp0(owner, session->owner)) continue; return session; } return NULL; } static gboolean connection_complete(gpointer data) { struct callback_data *cb = data; cb->func(cb->session, NULL, NULL, cb->data); obc_session_unref(cb->session); g_free(cb); return FALSE; } static int session_connect(struct obc_session *session, session_callback_t function, void *user_data) { struct callback_data *callback; struct obc_transport *transport = session->transport; struct obc_driver *driver = session->driver; callback = g_try_malloc0(sizeof(*callback)); if (callback == NULL) return -ENOMEM; callback->func = function; callback->data = user_data; callback->session = obc_session_ref(session); /* Connection completed */ if (session->obex) { g_idle_add(connection_complete, callback); return 0; } /* Ongoing connection */ if (session->id > 0) { obc_session_unref(callback->session); g_free(callback); return 0; } session->id = transport->connect(session->source, session->destination, driver->uuid, session->channel ? session->channel : session->psm, transport_func, callback); if (session->id == 0) { obc_session_unref(callback->session); g_free(callback); return -EINVAL; } session->callback = callback; return 0; } struct obc_session *obc_session_create(const char *source, const char *destination, const char *service, uint8_t channel, uint16_t psm, const char *owner, session_callback_t function, void *user_data) { DBusConnection *conn; struct obc_session *session; struct obc_transport *transport; struct obc_driver *driver; if (destination == NULL) return NULL; session = session_find(source, destination, service, channel, psm, owner); if (session != NULL) goto proceed; /* FIXME: Do proper transport lookup when the API supports it */ transport = obc_transport_find("Bluetooth"); if (transport == NULL) return NULL; driver = obc_driver_find(service); if (driver == NULL) return NULL; conn = obex_get_dbus_connection(); if (conn == NULL) return NULL; session = g_try_malloc0(sizeof(*session)); if (session == NULL) return NULL; session->refcount = 1; session->transport = transport; session->driver = driver; session->conn = conn; session->source = g_strdup(source); session->destination = g_strdup(destination); session->channel = channel; session->psm = psm; session->queue = g_queue_new(); session->folder = g_strdup("/"); if (owner) obc_session_set_owner(session, owner, owner_disconnected); proceed: if (session_connect(session, function, user_data) < 0) { obc_session_unref(session); return NULL; } DBG("session %p transport %s driver %s", session, session->transport->name, session->driver->service); return session; } void obc_session_shutdown(struct obc_session *session) { struct pending_request *p; GError *err; DBG("%p", session); obc_session_ref(session); /* Unregister any pending transfer */ err = g_error_new(OBEX_IO_ERROR, OBEX_IO_DISCONNECTED, "Session closed by user"); if (session->p != NULL && session->p->id != 0) { p = session->p; session->p = NULL; if (p->func) p->func(session, p->transfer, err, p->data); pending_request_free(p); } while ((p = g_queue_pop_head(session->queue))) { if (p->func) p->func(session, p->transfer, err, p->data); pending_request_free(p); } g_error_free(err); /* Unregister interfaces */ if (session->path) session_unregistered(session); obc_session_unref(session); } static void capabilities_complete_callback(struct obc_session *session, struct obc_transfer *transfer, GError *err, void *user_data) { DBusMessage *message = user_data; char *contents; size_t size; int perr; if (err != NULL) { DBusMessage *error = g_dbus_create_error(message, ERROR_INTERFACE ".Failed", "%s", err->message); g_dbus_send_message(session->conn, error); goto done; } perr = obc_transfer_get_contents(transfer, &contents, &size); if (perr < 0) { DBusMessage *error = g_dbus_create_error(message, ERROR_INTERFACE ".Failed", "Error reading contents: %s", strerror(-perr)); g_dbus_send_message(session->conn, error); goto done; } g_dbus_send_reply(session->conn, message, DBUS_TYPE_STRING, &contents, DBUS_TYPE_INVALID); g_free(contents); done: dbus_message_unref(message); } static DBusMessage *get_capabilities(DBusConnection *connection, DBusMessage *message, void *user_data) { struct obc_session *session = user_data; struct obc_transfer *pull; DBusMessage *reply; GError *gerr = NULL; pull = obc_transfer_get("x-obex/capability", NULL, NULL, &gerr); if (pull == NULL) goto fail; if (!obc_session_queue(session, pull, capabilities_complete_callback, message, &gerr)) goto fail; dbus_message_ref(message); return NULL; fail: reply = g_dbus_create_error(message, ERROR_INTERFACE ".Failed", "%s", gerr->message); g_error_free(gerr); return reply; } static gboolean get_source(const GDBusPropertyTable *property, DBusMessageIter *iter, void *data) { struct obc_session *session = data; if (session->source == NULL) return FALSE; dbus_message_iter_append_basic(iter, DBUS_TYPE_STRING, &session->source); return TRUE; } static gboolean source_exists(const GDBusPropertyTable *property, void *data) { struct obc_session *session = data; return session->source != NULL; } static gboolean get_destination(const GDBusPropertyTable *property, DBusMessageIter *iter, void *data) { struct obc_session *session = data; dbus_message_iter_append_basic(iter, DBUS_TYPE_STRING, &session->destination); return TRUE; } static gboolean get_channel(const GDBusPropertyTable *property, DBusMessageIter *iter, void *data) { struct obc_session *session = data; dbus_message_iter_append_basic(iter, DBUS_TYPE_BYTE, &session->channel); return TRUE; } static gboolean get_psm(const GDBusPropertyTable *property, DBusMessageIter *iter, void *data) { struct obc_session *session = data; dbus_message_iter_append_basic(iter, DBUS_TYPE_UINT16, &session->psm); return TRUE; } static const GDBusMethodTable session_methods[] = { { GDBUS_ASYNC_METHOD("GetCapabilities", NULL, GDBUS_ARGS({ "capabilities", "s" }), get_capabilities) }, { } }; static gboolean get_target(const GDBusPropertyTable *property, DBusMessageIter *iter, void *data) { struct obc_session *session = data; if (session->driver->uuid == NULL) return FALSE; dbus_message_iter_append_basic(iter, DBUS_TYPE_STRING, &session->driver->uuid); return TRUE; } static gboolean target_exists(const GDBusPropertyTable *property, void *data) { struct obc_session *session = data; return session->driver->uuid != NULL; } static const GDBusPropertyTable session_properties[] = { { "Source", "s", get_source, NULL, source_exists }, { "Destination", "s", get_destination }, { "Channel", "y", get_channel }, { "PSM", "q", get_psm }, { "Target", "s", get_target, NULL, target_exists }, { } }; static gboolean session_process(gpointer data) { struct obc_session *session = data; session->process_id = 0; session_process_queue(session); return FALSE; } static void session_queue(struct pending_request *p) { g_queue_push_tail(p->session->queue, p); if (p->session->process_id == 0) p->session->process_id = g_idle_add(session_process, p->session); } static int session_process_transfer(struct pending_request *p, GError **err) { if (!obc_transfer_start(p->transfer, p->session->obex, err)) return -1; DBG("Tranfer(%p) started", p->transfer); p->session->p = p; return 0; } guint obc_session_queue(struct obc_session *session, struct obc_transfer *transfer, session_callback_t func, void *user_data, GError **err) { struct pending_request *p; if (session->obex == NULL) { obc_transfer_unregister(transfer); g_set_error(err, OBEX_IO_ERROR, -ENOTCONN, "Session not connected"); return 0; } if (!obc_transfer_register(transfer, session->conn, session->path, session->owner, err)) { obc_transfer_unregister(transfer); return 0; } obc_transfer_set_callback(transfer, transfer_complete, session); p = pending_request_new(session, session_process_transfer, transfer, func, user_data, NULL); session_queue(p); return p->id; } static void session_process_queue(struct obc_session *session) { struct pending_request *p; if (session->p != NULL) return; if (session->queue == NULL || g_queue_is_empty(session->queue)) return; obc_session_ref(session); while ((p = g_queue_pop_head(session->queue))) { GError *gerr = NULL; if (p->process(p, &gerr) == 0) break; if (p->func) p->func(session, p->transfer, gerr, p->data); g_clear_error(&gerr); pending_request_free(p); } obc_session_unref(session); } static int pending_transfer_cmptransfer(gconstpointer a, gconstpointer b) { const struct pending_request *p = a; const struct obc_transfer *transfer = b; if (p->transfer == transfer) return 0; return -1; } static void session_terminate_transfer(struct obc_session *session, struct obc_transfer *transfer, GError *gerr) { struct pending_request *p = session->p; if (p == NULL || p->transfer != transfer) { GList *match; match = g_list_find_custom(session->queue->head, transfer, pending_transfer_cmptransfer); if (match == NULL) return; p = match->data; g_queue_delete_link(session->queue, match); } else session->p = NULL; obc_session_ref(session); if (p->func) p->func(session, p->transfer, gerr, p->data); pending_request_free(p); if (session->p == NULL) session_process_queue(session); obc_session_unref(session); } static void session_notify_complete(struct obc_session *session, struct obc_transfer *transfer) { DBG("Transfer(%p) complete", transfer); session_terminate_transfer(session, transfer, NULL); } static void session_notify_error(struct obc_session *session, struct obc_transfer *transfer, GError *err) { error("Transfer(%p) Error: %s", transfer, err->message); session_terminate_transfer(session, transfer, err); } static void transfer_complete(struct obc_transfer *transfer, GError *err, void *user_data) { struct obc_session *session = user_data; if (err) goto fail; session_notify_complete(session, transfer); return; fail: session_notify_error(session, transfer, err); } const char *obc_session_register(struct obc_session *session, GDBusDestroyFunction destroy) { if (session->path) return session->path; session->path = g_strdup_printf("%s/session%ju", SESSION_BASEPATH, counter++); if (g_dbus_register_interface(session->conn, session->path, SESSION_INTERFACE, session_methods, NULL, session_properties, session, destroy) == FALSE) goto fail; if (session->driver->probe && session->driver->probe(session) < 0) { g_dbus_unregister_interface(session->conn, session->path, SESSION_INTERFACE); goto fail; } DBG("Session(%p) registered %s", session, session->path); return session->path; fail: g_free(session->path); session->path = NULL; return NULL; } const void *obc_session_get_attribute(struct obc_session *session, int attribute_id) { if (session == NULL || session->id == 0) return NULL; return session->transport->getattribute(session->id, attribute_id); } const char *obc_session_get_owner(struct obc_session *session) { if (session == NULL) return NULL; return session->owner; } const char *obc_session_get_destination(struct obc_session *session) { return session->destination; } const char *obc_session_get_path(struct obc_session *session) { return session->path; } const char *obc_session_get_target(struct obc_session *session) { return session->driver->target; } const char *obc_session_get_folder(struct obc_session *session) { return session->folder; } static void setpath_complete(struct obc_session *session, struct obc_transfer *transfer, GError *err, void *user_data) { struct pending_request *p = user_data; if (p->func) p->func(session, NULL, err, p->data); if (session->p == p) session->p = NULL; pending_request_free(p); session_process_queue(session); } static void setpath_op_complete(struct obc_session *session, struct obc_transfer *transfer, GError *err, void *user_data) { struct setpath_data *data = user_data; if (data->func) data->func(session, NULL, err, data->user_data); } static void setpath_set_folder(struct obc_session *session, const char *cur) { char *folder = NULL; const char *delim; delim = strrchr(session->folder, '/'); if (strlen(cur) == 0 || delim == NULL || (strcmp(cur, "..") == 0 && delim == session->folder)) { folder = g_strdup("/"); } else { if (strcmp(cur, "..") == 0) { folder = g_strndup(session->folder, delim - session->folder); } else { if (g_str_has_suffix(session->folder, "/")) folder = g_strconcat(session->folder, cur, NULL); else folder = g_strconcat(session->folder, "/", cur, NULL); } } g_free(session->folder); session->folder = folder; } static void setpath_cb(GObex *obex, GError *err, GObexPacket *rsp, gpointer user_data) { struct pending_request *p = user_data; struct setpath_data *data = p->data; char *next; char *current; guint8 code; p->req_id = 0; if (err != NULL) { setpath_complete(p->session, NULL, err, user_data); return; } code = g_obex_packet_get_operation(rsp, NULL); if (code != G_OBEX_RSP_SUCCESS) { GError *gerr = NULL; g_set_error(&gerr, OBEX_IO_ERROR, code, "%s", g_obex_strerror(code)); setpath_complete(p->session, NULL, gerr, user_data); g_clear_error(&gerr); return; } current = data->remaining[data->index - 1]; setpath_set_folder(p->session, current); /* Ignore empty folder names to avoid resetting the current path */ while ((next = data->remaining[data->index]) && strlen(next) == 0) data->index++; if (next == NULL) { setpath_complete(p->session, NULL, NULL, user_data); return; } data->index++; p->req_id = g_obex_setpath(obex, next, setpath_cb, p, &err); if (err != NULL) { setpath_complete(p->session, NULL, err, user_data); g_error_free(err); } } static int session_process_setpath(struct pending_request *p, GError **err) { struct setpath_data *req = p->data; const char *first = ""; /* Relative path */ if (req->remaining[0][0] != '/') first = req->remaining[req->index]; req->index++; p->req_id = g_obex_setpath(p->session->obex, first, setpath_cb, p, err); if (*err != NULL) return (*err)->code; p->session->p = p; return 0; } guint obc_session_setpath(struct obc_session *session, const char *path, session_callback_t func, void *user_data, GError **err) { struct setpath_data *data; struct pending_request *p; if (session->obex == NULL) { g_set_error(err, OBEX_IO_ERROR, OBEX_IO_DISCONNECTED, "Session disconnected"); return 0; } data = g_new0(struct setpath_data, 1); data->func = func; data->user_data = user_data; data->remaining = g_strsplit(strlen(path) ? path : "/", "/", 0); if (!data->remaining || !data->remaining[0]) { error("obc_session_setpath: invalid path %s", path); g_set_error(err, OBEX_IO_ERROR, -EINVAL, "Invalid argument"); setpath_data_free(data); return 0; } p = pending_request_new(session, session_process_setpath, NULL, setpath_op_complete, data, setpath_data_free); session_queue(p); return p->id; } static void async_cb(GObex *obex, GError *err, GObexPacket *rsp, gpointer user_data) { struct pending_request *p = user_data; struct obc_session *session = p->session; GError *gerr = NULL; uint8_t code; p->req_id = 0; if (err != NULL) { if (p->func) p->func(p->session, NULL, err, p->data); goto done; } code = g_obex_packet_get_operation(rsp, NULL); if (code != G_OBEX_RSP_SUCCESS) g_set_error(&gerr, OBEX_IO_ERROR, code, "%s", g_obex_strerror(code)); if (p->func) p->func(p->session, NULL, gerr, p->data); if (gerr != NULL) g_clear_error(&gerr); done: pending_request_free(p); session->p = NULL; session_process_queue(session); } static void file_op_complete(struct obc_session *session, struct obc_transfer *transfer, GError *err, void *user_data) { struct file_data *data = user_data; if (data->func) data->func(session, NULL, err, data->user_data); } static int session_process_mkdir(struct pending_request *p, GError **err) { struct file_data *req = p->data; p->req_id = g_obex_mkdir(p->session->obex, req->srcname, async_cb, p, err); if (*err != NULL) return (*err)->code; p->session->p = p; return 0; } guint obc_session_mkdir(struct obc_session *session, const char *folder, session_callback_t func, void *user_data, GError **err) { struct file_data *data; struct pending_request *p; if (session->obex == NULL) { g_set_error(err, OBEX_IO_ERROR, OBEX_IO_DISCONNECTED, "Session disconnected"); return 0; } data = g_new0(struct file_data, 1); data->srcname = g_strdup(folder); data->func = func; data->user_data = user_data; p = pending_request_new(session, session_process_mkdir, NULL, file_op_complete, data, file_data_free); session_queue(p); return p->id; } static int session_process_copy(struct pending_request *p, GError **err) { struct file_data *req = p->data; p->req_id = g_obex_copy(p->session->obex, req->srcname, req->destname, async_cb, p, err); if (*err != NULL) return (*err)->code; p->session->p = p; return 0; } guint obc_session_copy(struct obc_session *session, const char *srcname, const char *destname, session_callback_t func, void *user_data, GError **err) { struct file_data *data; struct pending_request *p; if (session->obex == NULL) { g_set_error(err, OBEX_IO_ERROR, OBEX_IO_DISCONNECTED, "Session disconnected"); return 0; } data = g_new0(struct file_data, 1); data->srcname = g_strdup(srcname); data->destname = g_strdup(destname); data->func = func; data->user_data = user_data; p = pending_request_new(session, session_process_copy, NULL, file_op_complete, data, file_data_free); session_queue(p); return p->id; } static int session_process_move(struct pending_request *p, GError **err) { struct file_data *req = p->data; p->req_id = g_obex_move(p->session->obex, req->srcname, req->destname, async_cb, p, err); if (*err != NULL) return (*err)->code; p->session->p = p; return 0; } guint obc_session_move(struct obc_session *session, const char *srcname, const char *destname, session_callback_t func, void *user_data, GError **err) { struct file_data *data; struct pending_request *p; if (session->obex == NULL) { g_set_error(err, OBEX_IO_ERROR, OBEX_IO_DISCONNECTED, "Session disconnected"); return 0; } data = g_new0(struct file_data, 1); data->srcname = g_strdup(srcname); data->destname = g_strdup(destname); data->func = func; data->user_data = user_data; p = pending_request_new(session, session_process_move, NULL, file_op_complete, data, file_data_free); session_queue(p); return p->id; } static int session_process_delete(struct pending_request *p, GError **err) { struct file_data *req = p->data; p->req_id = g_obex_delete(p->session->obex, req->srcname, async_cb, p, err); if (*err != NULL) return (*err)->code; p->session->p = p; return 0; } guint obc_session_delete(struct obc_session *session, const char *file, session_callback_t func, void *user_data, GError **err) { struct file_data *data; struct pending_request *p; if (session->obex == NULL) { g_set_error(err, OBEX_IO_ERROR, OBEX_IO_DISCONNECTED, "Session disconnected"); return 0; } data = g_new0(struct file_data, 1); data->srcname = g_strdup(file); data->func = func; data->user_data = user_data; p = pending_request_new(session, session_process_delete, NULL, file_op_complete, data, file_data_free); session_queue(p); return p->id; } void obc_session_cancel(struct obc_session *session, guint id, gboolean remove) { struct pending_request *p = session->p; if (p == NULL || p->id != id) return; if (p->req_id == 0) return; g_obex_cancel_req(session->obex, p->req_id, remove); p->req_id = 0; if (!remove) return; pending_request_free(p); session->p = NULL; session_process_queue(session); } bluez-5.82/obexd/client/PaxHeaders/bluetooth.h0000644000000000000000000000005014015011623016374 xustar0020 atime=1743516742 20 ctime=1743591283 bluez-5.82/obexd/client/bluetooth.h0000644000000000000000000000026214015011623016055 0ustar00rootroot/* SPDX-License-Identifier: GPL-2.0-or-later */ /* * * OBEX Client * * Copyright (C) 2011 Intel Corporation * * */ int bluetooth_init(void); void bluetooth_exit(void); bluez-5.82/obexd/client/PaxHeaders/session.h0000644000000000000000000000005014711225434016064 xustar0020 atime=1743516742 20 ctime=1743591283 bluez-5.82/obexd/client/session.h0000644000000000000000000000460514711225434015552 0ustar00rootroot/* SPDX-License-Identifier: GPL-2.0-or-later */ /* * * OBEX Client * * Copyright (C) 2007-2010 Marcel Holtmann * Copyright (C) 2011-2012 BMW Car IT GmbH. All rights reserved. * * */ #include #include struct obc_session; typedef void (*session_callback_t) (struct obc_session *session, struct obc_transfer *transfer, GError *err, void *user_data); struct obc_session *obc_session_create(const char *source, const char *destination, const char *service, uint8_t channel, uint16_t psm, const char *owner, session_callback_t function, void *user_data); struct obc_session *obc_session_ref(struct obc_session *session); void obc_session_unref(struct obc_session *session); void obc_session_shutdown(struct obc_session *session); int obc_session_set_owner(struct obc_session *session, const char *name, GDBusWatchFunction func); const char *obc_session_get_owner(struct obc_session *session); const char *obc_session_get_destination(struct obc_session *session); const char *obc_session_get_path(struct obc_session *session); const char *obc_session_get_target(struct obc_session *session); const char *obc_session_register(struct obc_session *session, GDBusDestroyFunction destroy); const void *obc_session_get_attribute(struct obc_session *session, int attribute_id); const char *obc_session_get_folder(struct obc_session *session); guint obc_session_queue(struct obc_session *session, struct obc_transfer *transfer, session_callback_t func, void *user_data, GError **err); guint obc_session_setpath(struct obc_session *session, const char *path, session_callback_t func, void *user_data, GError **err); guint obc_session_mkdir(struct obc_session *session, const char *folder, session_callback_t func, void *user_data, GError **err); guint obc_session_copy(struct obc_session *session, const char *srcname, const char *destname, session_callback_t func, void *user_data, GError **err); guint obc_session_move(struct obc_session *session, const char *srcname, const char *destname, session_callback_t func, void *user_data, GError **err); guint obc_session_delete(struct obc_session *session, const char *file, session_callback_t func, void *user_data, GError **err); void obc_session_cancel(struct obc_session *session, guint id, gboolean remove); bluez-5.82/obexd/client/PaxHeaders/map-event.c0000644000000000000000000000005014015011623016256 xustar0020 atime=1743516761 20 ctime=1743591283 bluez-5.82/obexd/client/map-event.c0000644000000000000000000000377414015011623015752 0ustar00rootroot// SPDX-License-Identifier: GPL-2.0-or-later /* * * OBEX * * Copyright (C) 2013 BMW Car IT GmbH. All rights reserved. * * */ #ifdef HAVE_CONFIG_H #include #endif #include #include #include #include #include "gdbus/gdbus.h" #include "obexd/src/log.h" #include "map-event.h" #include "transfer.h" #include "session.h" static GSList *handlers; struct mns_handler { int mas_id; struct obc_session *session; map_event_cb cb; void *user_data; }; static struct mns_handler *find_handler(int mas_id, const char *device) { GSList *list; for (list = handlers; list; list = list->next) { struct mns_handler *handler = list->data; if (mas_id != handler->mas_id) continue; if (g_str_equal(device, obc_session_get_destination(handler->session))) return handler; } return NULL; } bool map_register_event_handler(struct obc_session *session, int mas_id, map_event_cb cb, void *user_data) { struct mns_handler *handler; handler = find_handler(mas_id, obc_session_get_destination(session)); if (handler != NULL) return FALSE; handler = g_new0(struct mns_handler, 1); handler->mas_id = mas_id; handler->session = session; handler->cb = cb; handler->user_data = user_data; handlers = g_slist_prepend(handlers, handler); DBG("Handler %p for %s:%d registered", handler, obc_session_get_destination(session), mas_id); return TRUE; } void map_unregister_event_handler(struct obc_session *session, int mas_id) { struct mns_handler *handler; handler = find_handler(mas_id, obc_session_get_destination(session)); if (handler == NULL) return; handlers = g_slist_remove(handlers, handler); DBG("Handler %p for %s:%d unregistered", handler, obc_session_get_destination(session), mas_id); g_free(handler); } void map_dispatch_event(int mas_id, const char *device, struct map_event *event) { struct mns_handler *handler; handler = find_handler(mas_id, device); if (handler) handler->cb(event, handler->user_data); } bluez-5.82/obexd/client/PaxHeaders/driver.c0000644000000000000000000000005014015011623015655 xustar0020 atime=1743516764 20 ctime=1743591283 bluez-5.82/obexd/client/driver.c0000644000000000000000000000256414015011623015345 0ustar00rootroot// SPDX-License-Identifier: GPL-2.0-or-later /* * * OBEX Server * * Copyright (C) 2007-2010 Marcel Holtmann * * */ #ifdef HAVE_CONFIG_H #include #endif #define _GNU_SOURCE #include #include #include #include "gdbus/gdbus.h" #include "obexd/src/log.h" #include "transfer.h" #include "session.h" #include "driver.h" static GSList *drivers = NULL; struct obc_driver *obc_driver_find(const char *pattern) { GSList *l; for (l = drivers; l; l = l->next) { struct obc_driver *driver = l->data; if (strcasecmp(pattern, driver->service) == 0) return driver; if (strcasecmp(pattern, driver->uuid) == 0) return driver; } return NULL; } int obc_driver_register(struct obc_driver *driver) { if (!driver) { error("Invalid driver"); return -EINVAL; } if (obc_driver_find(driver->service)) { error("Permission denied: service %s already registered", driver->service); return -EPERM; } DBG("driver %p service %s registered", driver, driver->service); drivers = g_slist_append(drivers, driver); return 0; } void obc_driver_unregister(struct obc_driver *driver) { if (!g_slist_find(drivers, driver)) { error("Unable to unregister: No such driver %p", driver); return; } DBG("driver %p service %s unregistered", driver, driver->service); drivers = g_slist_remove(drivers, driver); } bluez-5.82/obexd/client/PaxHeaders/manager.c0000644000000000000000000000005014711225434016006 xustar0020 atime=1743516742 20 ctime=1743591283 bluez-5.82/obexd/client/manager.c0000644000000000000000000001650414711225434015475 0ustar00rootroot// SPDX-License-Identifier: GPL-2.0-or-later /* * * OBEX Client * * Copyright (C) 2007-2010 Marcel Holtmann * * */ #ifdef HAVE_CONFIG_H #include #endif #include #include #include #include #include #include #include "gdbus/gdbus.h" #include "obexd/src/log.h" #include "obexd/src/manager.h" #include "transfer.h" #include "session.h" #include "bluetooth.h" #include "opp.h" #include "ftp.h" #include "pbap.h" #include "sync.h" #include "map.h" #include "bip.h" #include "manager.h" #define CLIENT_INTERFACE "org.bluez.obex.Client1" #define ERROR_INTERFACE "org.bluez.obex.Error" #define CLIENT_PATH "/org/bluez/obex" struct send_data { DBusConnection *connection; DBusMessage *message; }; static GSList *sessions = NULL; static void shutdown_session(struct obc_session *session) { obc_session_shutdown(session); obc_session_unref(session); } static void release_session(struct obc_session *session) { sessions = g_slist_remove(sessions, session); shutdown_session(session); } static void unregister_session(void *data) { struct obc_session *session = data; if (g_slist_find(sessions, session) == NULL) return; sessions = g_slist_remove(sessions, session); obc_session_unref(session); } static void create_callback(struct obc_session *session, struct obc_transfer *transfer, GError *err, void *user_data) { struct send_data *data = user_data; const char *path; if (err != NULL) { DBusMessage *error = g_dbus_create_error(data->message, ERROR_INTERFACE ".Failed", "%s", err->message); g_dbus_send_message(data->connection, error); shutdown_session(session); goto done; } path = obc_session_register(session, unregister_session); if (path == NULL) { DBusMessage *error = g_dbus_create_error(data->message, ERROR_INTERFACE ".Failed", NULL); g_dbus_send_message(data->connection, error); shutdown_session(session); goto done; } sessions = g_slist_append(sessions, session); g_dbus_send_reply(data->connection, data->message, DBUS_TYPE_OBJECT_PATH, &path, DBUS_TYPE_INVALID); done: dbus_message_unref(data->message); dbus_connection_unref(data->connection); g_free(data); } static int parse_device_dict(DBusMessageIter *iter, const char **source, const char **target, uint8_t *channel, uint16_t *psm) { while (dbus_message_iter_get_arg_type(iter) == DBUS_TYPE_DICT_ENTRY) { DBusMessageIter entry, value; const char *key; dbus_message_iter_recurse(iter, &entry); dbus_message_iter_get_basic(&entry, &key); dbus_message_iter_next(&entry); dbus_message_iter_recurse(&entry, &value); switch (dbus_message_iter_get_arg_type(&value)) { case DBUS_TYPE_STRING: if (g_str_equal(key, "Source") == TRUE) dbus_message_iter_get_basic(&value, source); else if (g_str_equal(key, "Target") == TRUE) dbus_message_iter_get_basic(&value, target); break; case DBUS_TYPE_BYTE: if (g_str_equal(key, "Channel") == TRUE) dbus_message_iter_get_basic(&value, channel); break; case DBUS_TYPE_UINT16: if (g_str_equal(key, "PSM") == TRUE) dbus_message_iter_get_basic(&value, psm); break; } dbus_message_iter_next(iter); } return 0; } static struct obc_session *find_session(const char *path) { GSList *l; for (l = sessions; l; l = l->next) { struct obc_session *session = l->data; if (g_strcmp0(obc_session_get_path(session), path) == 0) return session; } return NULL; } static DBusMessage *create_session(DBusConnection *connection, DBusMessage *message, void *user_data) { DBusMessageIter iter, dict; struct obc_session *session; struct send_data *data; const char *source = NULL, *dest = NULL, *target = NULL; uint8_t channel = 0; uint16_t psm = 0; dbus_message_iter_init(message, &iter); if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_STRING) return g_dbus_create_error(message, ERROR_INTERFACE ".InvalidArguments", NULL); dbus_message_iter_get_basic(&iter, &dest); dbus_message_iter_next(&iter); if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_ARRAY) return g_dbus_create_error(message, ERROR_INTERFACE ".InvalidArguments", NULL); dbus_message_iter_recurse(&iter, &dict); parse_device_dict(&dict, &source, &target, &channel, &psm); if (dest == NULL || target == NULL || (channel && psm)) return g_dbus_create_error(message, ERROR_INTERFACE ".InvalidArguments", NULL); data = g_try_malloc0(sizeof(*data)); if (data == NULL) return g_dbus_create_error(message, ERROR_INTERFACE ".Error.NoMemory", NULL); data->connection = dbus_connection_ref(connection); data->message = dbus_message_ref(message); session = obc_session_create(source, dest, target, channel, psm, dbus_message_get_sender(message), create_callback, data); if (session != NULL) { return NULL; } dbus_message_unref(data->message); dbus_connection_unref(data->connection); g_free(data); return g_dbus_create_error(message, ERROR_INTERFACE ".Failed", NULL); } static DBusMessage *remove_session(DBusConnection *connection, DBusMessage *message, void *user_data) { struct obc_session *session; const char *sender, *path; if (dbus_message_get_args(message, NULL, DBUS_TYPE_OBJECT_PATH, &path, DBUS_TYPE_INVALID) == FALSE) return g_dbus_create_error(message, ERROR_INTERFACE ".InvalidArguments", NULL); session = find_session(path); if (session == NULL) return g_dbus_create_error(message, ERROR_INTERFACE ".InvalidArguments", NULL); sender = dbus_message_get_sender(message); if (g_str_equal(sender, obc_session_get_owner(session)) == FALSE) return g_dbus_create_error(message, ERROR_INTERFACE ".NotAuthorized", "Not Authorized"); release_session(session); return dbus_message_new_method_return(message); } static const GDBusMethodTable client_methods[] = { { GDBUS_ASYNC_METHOD("CreateSession", GDBUS_ARGS({ "destination", "s" }, { "args", "a{sv}" }), GDBUS_ARGS({ "session", "o" }), create_session) }, { GDBUS_ASYNC_METHOD("RemoveSession", GDBUS_ARGS({ "session", "o" }), NULL, remove_session) }, { } }; static DBusConnection *conn = NULL; static const struct obc_module { const char *name; int (*init) (void); void (*exit) (void); } modules[] = { { "bluetooth", bluetooth_init, bluetooth_exit }, { "opp", opp_init, opp_exit }, { "ftp", ftp_init, ftp_exit }, { "pbap", pbap_init, pbap_exit }, { "sync", sync_init, sync_exit }, { "map", map_init, map_exit }, { "bip", bip_init, bip_exit }, { } }; int client_manager_init(void) { DBusError derr; const struct obc_module *module; dbus_error_init(&derr); conn = manager_dbus_get_connection(); if (conn == NULL) { error("Can't get client D-Bus connection"); return -1; } if (g_dbus_register_interface(conn, CLIENT_PATH, CLIENT_INTERFACE, client_methods, NULL, NULL, NULL, NULL) == FALSE) { error("Can't register client interface"); dbus_connection_unref(conn); conn = NULL; return -1; } for (module = modules; module && module->init; module++) { if (module->init() < 0) continue; DBG("Module %s loaded", module->name); } return 0; } void client_manager_exit(void) { const struct obc_module *module; if (conn == NULL) return; for (module = modules; module && module->exit; module++) module->exit(); g_dbus_unregister_interface(conn, CLIENT_PATH, CLIENT_INTERFACE); dbus_connection_unref(conn); } bluez-5.82/obexd/client/PaxHeaders/map.h0000644000000000000000000000005014015011623015144 xustar0020 atime=1743516742 20 ctime=1743591283 bluez-5.82/obexd/client/map.h0000644000000000000000000000030614015011623014624 0ustar00rootroot/* SPDX-License-Identifier: GPL-2.0-or-later */ /* * * OBEX Client * * Copyright (C) 2011 Bartosz Szatkowski for Comarch * */ int map_init(void); void map_exit(void); bluez-5.82/obexd/client/PaxHeaders/bip-common.c0000644000000000000000000000005014711225434016434 xustar0020 atime=1743516758 20 ctime=1743591283 bluez-5.82/obexd/client/bip-common.c0000644000000000000000000004425614711225434016130 0ustar00rootroot/* SPDX-License-Identifier: GPL-2.0-or-later */ /* * * OBEX Client * * Copyright (C) 2024 Collabora Ltd. * Based on previous work done by Jakub Adamek for GSoC 2011 * */ #define _GNU_SOURCE #include #include #include #include #include #include "gobex/gobex.h" #include "obexd/src/log.h" #include "bip-common.h" #define HANDLE_LEN 7 #define HANDLE_LIMIT 10000000 struct encconv_pair { gchar *bip, *im; }; struct encconv_pair encconv_table[] = { { "JPEG", "JPEG" }, { "GIF", "GIF" }, { "WBMP", "WBMP" }, { "PNG", "PNG" }, { "JPEG2000", "JP2" }, { "BMP", "BMP" }, { } }; static const gchar *convBIP2IM(const gchar *encoding) { struct encconv_pair *et = encconv_table; while (et->bip) { if (g_strcmp0(encoding, et->bip) == 0) return et->im; et++; } return NULL; } gboolean parse_pixel_range(const gchar *dim, unsigned int *lower_ret, unsigned int *upper_ret, gboolean *fixed_ratio_ret) { static regex_t no_range; static regex_t range; static regex_t range_fixed; static int regex_initialized; unsigned int lower[2], upper[2]; gboolean fixed_ratio = FALSE; if (!regex_initialized) { regcomp(&no_range, "^([[:digit:]]{1,5})\\*([[:digit:]]{1,5})$", REG_EXTENDED); regcomp(&range, "^([[:digit:]]{1,5})\\*([[:digit:]]{1,5})" "-([[:digit:]]{1,5})\\*([[:digit:]]{1,5})$", REG_EXTENDED); regcomp(&range_fixed, "^([[:digit:]]{1,5})\\*\\*" "-([[:digit:]]{1,5})\\*([[:digit:]]{1,5})$", REG_EXTENDED); regex_initialized = 1; } if (dim == NULL) return FALSE; if (regexec(&no_range, dim, 0, NULL, 0) == 0) { if (sscanf(dim, "%u*%u", &lower[0], &lower[1]) != 2) return FALSE; upper[0] = lower[0]; upper[1] = lower[1]; fixed_ratio = FALSE; } else if (regexec(&range, dim, 0, NULL, 0) == 0) { if (sscanf(dim, "%u*%u-%u*%u", &lower[0], &lower[1], &upper[0], &upper[1]) != 4) return FALSE; fixed_ratio = FALSE; } else if (regexec(&range_fixed, dim, 0, NULL, 0) == 0) { if (sscanf(dim, "%u**-%u*%u", &lower[0], &upper[0], &upper[1]) != 3) return FALSE; lower[1] = 0; fixed_ratio = TRUE; } else { return FALSE; } if (lower[0] > 65535 || lower[1] > 65535 || upper[0] > 65535 || upper[1] > 65535) return FALSE; if (lower_ret == NULL || upper_ret == NULL || fixed_ratio_ret == NULL) return TRUE; if (upper[0] < lower[0] || upper[1] < lower[1]) return FALSE; lower_ret[0] = lower[0]; lower_ret[1] = lower[1]; upper_ret[0] = upper[0]; upper_ret[1] = upper[1]; *fixed_ratio_ret = fixed_ratio; return TRUE; } static gboolean verify_unsignednumber(const char *size) { static regex_t unumber; static int regex_initialized; if (!regex_initialized) { regcomp(&unumber, "^[[:digit:]]+$", REG_EXTENDED); regex_initialized = 1; } if (regexec(&unumber, size, 0, NULL, 0) != 0) return FALSE; return TRUE; } static uint64_t parse_unsignednumber(const char *size) { if (!verify_unsignednumber(size)) return 0; return g_ascii_strtoll(size, NULL, 10); } char *transforms[] = { "crop", "stretch", "fill", NULL }; gboolean verify_encoding(const char *encoding) { struct encconv_pair *et = encconv_table; while (et->bip) { if (g_strcmp0(encoding, et->bip) == 0) return TRUE; et++; } return FALSE; } static gboolean verify_transform(const char *transform) { char **str = transforms; while (*str != NULL) { if (g_str_equal(transform, *str)) return TRUE; str++; } return FALSE; } char *parse_transform(const char *transform) { if (!verify_transform(transform)) return NULL; return g_strdup(transform); } static char *parse_transform_list(const char *transform) { char **args = NULL, **arg = NULL; gboolean used[3] = { FALSE, FALSE, FALSE }; if (transform == NULL) return NULL; if (strlen(transform) == 0) return NULL; args = g_strsplit(transform, " ", 0); for (arg = args; *arg != NULL; arg++) { char *t = *arg; if (!verify_transform(t)) { g_strfreev(args); return NULL; } switch (t[0]) { case 's': if (used[0]) goto failure; used[0] = TRUE; break; case 'c': if (used[1]) goto failure; used[1] = TRUE; break; case 'f': if (used[2]) goto failure; used[2] = TRUE; break; } } g_strfreev(args); return g_strdup(transform); failure: g_strfreev(args); return NULL; } static time_t parse_iso8601_bip(const gchar *str, int len) { gchar *tstr; struct tm tm; gint nr; gchar tz; time_t time; time_t tz_offset = 0; if (str == NULL) return -1; memset(&tm, 0, sizeof(struct tm)); /* According to spec the time doesn't have to be null terminated */ if (str[len - 1] != '\0') { tstr = g_malloc(len + 1); strncpy(tstr, str, len); tstr[len] = '\0'; } else tstr = g_strdup(str); nr = sscanf(tstr, "%04u%02u%02uT%02u%02u%02u%c", &tm.tm_year, &tm.tm_mon, &tm.tm_mday, &tm.tm_hour, &tm.tm_min, &tm.tm_sec, &tz); g_free(tstr); /* Fixup the tm values */ tm.tm_year -= 1900; /* Year since 1900 */ tm.tm_mon--; /* Months since January, values 0-11 */ tm.tm_isdst = -1; /* Daylight savings information not avail */ if (nr < 6) { /* Invalid time format */ return -1; } time = mktime(&tm); #if defined(HAVE_TM_GMTOFF) tz_offset = tm.tm_gmtoff; #elif defined(HAVE_TIMEZONE) tz_offset = -timezone; if (tm.tm_isdst > 0) tz_offset += 3600; #endif if (nr == 7) { /* Date/Time was in localtime (to remote device) * already. Since we don't know anything about the * timezone on that one we won't try to apply UTC offset */ time += tz_offset; } return time; } static int parse_handle(const char *data) { int handle; char *ptr; if (data == NULL) return -1; if (strlen(data) != HANDLE_LEN) return -1; handle = strtol(data, &ptr, 10); if (ptr != data + HANDLE_LEN) return -1; if (handle < 0 || handle >= HANDLE_LIMIT) return -1; return handle; } struct native_prop { char *encoding, *pixel; uint64_t size; }; struct variant_prop { char *encoding, *pixel, *transform; uint64_t maxsize; }; struct att_prop { char *content_type, *charset, *name; uint64_t size; time_t ctime, mtime; }; struct prop_object { char *handle, *name; GSList *native, *variant, *att; }; static void free_native_prop(struct native_prop *prop) { DBG(""); if (prop == NULL) return; g_free(prop->encoding); g_free(prop->pixel); g_free(prop); } static void free_variant_prop(struct variant_prop *prop) { DBG(""); if (prop == NULL) return; g_free(prop->encoding); g_free(prop->pixel); g_free(prop->transform); g_free(prop); } static void free_att_prop(struct att_prop *prop) { DBG(""); if (prop == NULL) return; g_free(prop->content_type); g_free(prop->charset); g_free(prop->name); g_free(prop); } static void free_prop_object(struct prop_object *object) { GSList *list; DBG(""); if (object == NULL) return; for (list = object->native; list != NULL; list = g_slist_next(list)) free_native_prop(list->data); for (list = object->variant; list != NULL; list = g_slist_next(list)) free_variant_prop(list->data); for (list = object->att; list != NULL; list = g_slist_next(list)) free_att_prop(list->data); g_slist_free(object->native); g_slist_free(object->variant); g_slist_free(object->att); g_free(object->handle); g_free(object->name); g_free(object); } static gboolean parse_attrib_native(struct native_prop *prop, const gchar *key, const gchar *value, GError **gerr) { DBG(""); if (g_str_equal(key, "encoding")) { if (convBIP2IM(value) == NULL) goto invalid; prop->encoding = g_strdup(value); } else if (g_str_equal(key, "pixel")) { if (!parse_pixel_range(value, NULL, NULL, NULL)) goto invalid; prop->pixel = g_strdup(value); } else if (g_str_equal(key, "size")) { prop->size = parse_unsignednumber(value); if (prop->size == 0) goto invalid; } else { g_set_error(gerr, G_MARKUP_ERROR, G_MARKUP_ERROR_UNKNOWN_ATTRIBUTE, NULL); return FALSE; } return TRUE; invalid: g_set_error(gerr, G_MARKUP_ERROR, G_MARKUP_ERROR_INVALID_CONTENT, NULL); return FALSE; } static gboolean parse_attrib_variant(struct variant_prop *prop, const gchar *key, const gchar *value, GError **gerr) { DBG(""); if (g_str_equal(key, "encoding")) { if (convBIP2IM(value) == NULL) goto invalid; prop->encoding = g_strdup(value); } else if (g_str_equal(key, "pixel")) { if (!parse_pixel_range(value, NULL, NULL, NULL)) goto invalid; prop->pixel = g_strdup(value); } else if (g_str_equal(key, "maxsize")) { prop->maxsize = parse_unsignednumber(value); if (prop->maxsize == 0) goto invalid; } else if (g_str_equal(key, "transform")) { prop->transform = parse_transform_list(value); if (prop->transform == NULL) goto invalid; } else { g_set_error(gerr, G_MARKUP_ERROR, G_MARKUP_ERROR_UNKNOWN_ATTRIBUTE, NULL); return FALSE; } return TRUE; invalid: g_set_error(gerr, G_MARKUP_ERROR, G_MARKUP_ERROR_INVALID_CONTENT, NULL); return FALSE; } static gboolean parse_attrib_att(struct att_prop *prop, const gchar *key, const gchar *value, GError **gerr) { DBG(""); if (g_str_equal(key, "content-type")) { prop->content_type = g_strdup(value); } else if (g_str_equal(key, "charset")) { prop->charset = g_strdup(value); } else if (g_str_equal(key, "name")) { prop->name = g_strdup(value); } else if (g_str_equal(key, "size")) { prop->size = parse_unsignednumber(value); if (prop->size == 0) goto invalid; } else if (g_str_equal(key, "created")) { prop->ctime = parse_iso8601_bip(value, strlen(value)); if (prop->ctime == -1) goto invalid; } else if (g_str_equal(key, "modified")) { prop->mtime = parse_iso8601_bip(value, strlen(value)); if (prop->mtime == -1) goto invalid; } else { g_set_error(gerr, G_MARKUP_ERROR, G_MARKUP_ERROR_UNKNOWN_ATTRIBUTE, NULL); return FALSE; } return TRUE; invalid: g_set_error(gerr, G_MARKUP_ERROR, G_MARKUP_ERROR_INVALID_CONTENT, NULL); return FALSE; } static struct att_prop *parse_elem_att(const gchar **names, const gchar **values, GError **gerr) { gchar **key; struct att_prop *prop = g_new0(struct att_prop, 1); DBG(""); for (key = (gchar **) names; *key; key++, values++) { if (!parse_attrib_att(prop, *key, *values, gerr)) { free_att_prop(prop); return NULL; } } return prop; } static struct variant_prop *parse_elem_variant(const gchar **names, const gchar **values, GError **gerr) { gchar **key; struct variant_prop *prop = g_new0(struct variant_prop, 1); DBG(""); for (key = (gchar **) names; *key; key++, values++) { if (!parse_attrib_variant(prop, *key, *values, gerr)) { free_variant_prop(prop); return NULL; } } if (prop->transform == NULL) prop->transform = g_strdup("stretch crop fill"); return prop; } static struct native_prop *parse_elem_native(const gchar **names, const gchar **values, GError **gerr) { gchar **key; struct native_prop *prop = g_new0(struct native_prop, 1); DBG(""); for (key = (gchar **) names; *key; key++, values++) { if (!parse_attrib_native(prop, *key, *values, gerr)) { free_native_prop(prop); return NULL; } } return prop; } static gboolean parse_attrib_prop(struct prop_object *prop, const gchar *key, const gchar *value, GError **gerr) { DBG(""); if (g_str_equal(key, "handle")) { if (parse_handle(value) < 0) goto invalid; prop->handle = g_strdup(value); } else if (g_str_equal(key, "friendly-name")) { prop->name = g_strdup(value); } else if (g_str_equal(key, "version")) { // pass; } else { g_set_error(gerr, G_MARKUP_ERROR, G_MARKUP_ERROR_UNKNOWN_ATTRIBUTE, NULL); return FALSE; } return TRUE; invalid: g_set_error(gerr, G_MARKUP_ERROR, G_MARKUP_ERROR_INVALID_CONTENT, NULL); return FALSE; } static struct prop_object *parse_elem_prop(const gchar **names, const gchar **values, GError **gerr) { gchar **key; struct prop_object *prop = g_new0(struct prop_object, 1); DBG(""); for (key = (gchar **) names; *key; key++, values++) { if (!parse_attrib_prop(prop, *key, *values, gerr)) { free_prop_object(prop); return NULL; } } return prop; } static void prop_element(GMarkupParseContext *ctxt, const gchar *element, const gchar **names, const gchar **values, gpointer user_data, GError **gerr) { struct prop_object **obj = user_data; DBG(""); if (g_str_equal(element, "image-properties")) { if (*obj != NULL) { free_prop_object(*obj); *obj = NULL; goto invalid; } *obj = parse_elem_prop(names, values, gerr); } else if (g_str_equal(element, "native")) { struct native_prop *prop; if (*obj == NULL) goto invalid; prop = parse_elem_native(names, values, gerr); (*obj)->native = g_slist_append((*obj)->native, prop); } else if (g_str_equal(element, "variant")) { struct variant_prop *prop; if (*obj == NULL) goto invalid; prop = parse_elem_variant(names, values, gerr); (*obj)->variant = g_slist_append((*obj)->variant, prop); } else if (g_str_equal(element, "attachment")) { struct att_prop *prop; if (*obj == NULL) goto invalid; prop = parse_elem_att(names, values, gerr); (*obj)->att = g_slist_append((*obj)->att, prop); } else { if (*obj != NULL) { free_prop_object(*obj); *obj = NULL; } goto invalid; } return; invalid: g_set_error(gerr, G_MARKUP_ERROR, G_MARKUP_ERROR_INVALID_CONTENT, NULL); } static const GMarkupParser properties_parser = { prop_element, NULL, NULL, NULL, NULL }; struct prop_object *parse_properties(char *data, unsigned int length, int *err) { struct prop_object *prop = NULL; gboolean status; GError *gerr = NULL; GMarkupParseContext *ctxt = g_markup_parse_context_new( &properties_parser, 0, &prop, NULL); DBG(""); if (err != NULL) *err = 0; status = g_markup_parse_context_parse(ctxt, data, length, &gerr); g_markup_parse_context_free(ctxt); if (!status) { if (err != NULL) *err = -EINVAL; free_prop_object(prop); prop = NULL; } return prop; } gboolean verify_properties(struct prop_object *obj) { GSList *list; if (obj->handle == NULL) return FALSE; for (list = obj->native; list != NULL; list = g_slist_next(list)) { struct native_prop *prop = list->data; if (prop->encoding == NULL || prop->pixel == NULL) return FALSE; } for (list = obj->variant; list != NULL; list = g_slist_next(list)) { struct variant_prop *prop = list->data; if (prop->encoding == NULL || prop->pixel == NULL) return FALSE; } for (list = obj->att; list != NULL; list = g_slist_next(list)) { struct att_prop *prop = list->data; if (prop->content_type == NULL || prop->name == NULL) return FALSE; } return TRUE; } void append_properties(DBusMessageIter *args, struct prop_object *obj) { DBusMessageIter dict, iter; GSList *list; dbus_message_iter_open_container(args, DBUS_TYPE_ARRAY, DBUS_TYPE_ARRAY_AS_STRING DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING DBUS_TYPE_STRING_AS_STRING DBUS_TYPE_VARIANT_AS_STRING DBUS_DICT_ENTRY_END_CHAR_AS_STRING, &dict); dbus_message_iter_open_container(&dict, DBUS_TYPE_ARRAY, DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING DBUS_TYPE_STRING_AS_STRING DBUS_TYPE_VARIANT_AS_STRING DBUS_DICT_ENTRY_END_CHAR_AS_STRING, &iter); g_dbus_dict_append_entry(&iter, "handle", DBUS_TYPE_STRING, &obj->handle); g_dbus_dict_append_entry(&iter, "name", DBUS_TYPE_STRING, &obj->name); dbus_message_iter_close_container(&dict, &iter); for (list = obj->native; list != NULL; list = g_slist_next(list)) { struct native_prop *prop = list->data; static char *native_str = "native"; dbus_message_iter_open_container(&dict, DBUS_TYPE_ARRAY, DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING DBUS_TYPE_STRING_AS_STRING DBUS_TYPE_VARIANT_AS_STRING DBUS_DICT_ENTRY_END_CHAR_AS_STRING, &iter); g_dbus_dict_append_entry(&iter, "type", DBUS_TYPE_STRING, &native_str); if (prop->encoding) g_dbus_dict_append_entry(&iter, "encoding", DBUS_TYPE_STRING, &prop->encoding); if (prop->pixel) g_dbus_dict_append_entry(&iter, "pixel", DBUS_TYPE_STRING, &prop->pixel); if (prop->size) g_dbus_dict_append_entry(&iter, "size", DBUS_TYPE_UINT64, &prop->size); dbus_message_iter_close_container(&dict, &iter); } for (list = obj->variant; list != NULL; list = g_slist_next(list)) { struct variant_prop *prop = list->data; static char *variant_str = "variant"; dbus_message_iter_open_container(&dict, DBUS_TYPE_ARRAY, DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING DBUS_TYPE_STRING_AS_STRING DBUS_TYPE_VARIANT_AS_STRING DBUS_DICT_ENTRY_END_CHAR_AS_STRING, &iter); g_dbus_dict_append_entry(&iter, "type", DBUS_TYPE_STRING, &variant_str); if (prop->encoding) g_dbus_dict_append_entry(&iter, "encoding", DBUS_TYPE_STRING, &prop->encoding); if (prop->pixel) g_dbus_dict_append_entry(&iter, "pixel", DBUS_TYPE_STRING, &prop->pixel); if (prop->maxsize) g_dbus_dict_append_entry(&iter, "maxsize", DBUS_TYPE_UINT64, &prop->maxsize); if (prop->transform) g_dbus_dict_append_entry(&iter, "transformation", DBUS_TYPE_STRING, &prop->transform); dbus_message_iter_close_container(&dict, &iter); } for (list = obj->att; list != NULL; list = g_slist_next(list)) { struct att_prop *prop = list->data; static char *attachment_str = "attachment"; dbus_message_iter_open_container(&dict, DBUS_TYPE_ARRAY, DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING DBUS_TYPE_STRING_AS_STRING DBUS_TYPE_VARIANT_AS_STRING DBUS_DICT_ENTRY_END_CHAR_AS_STRING, &iter); g_dbus_dict_append_entry(&iter, "type", DBUS_TYPE_STRING, &attachment_str); if (prop->content_type) g_dbus_dict_append_entry(&iter, "content-type", DBUS_TYPE_STRING, &prop->content_type); if (prop->charset) g_dbus_dict_append_entry(&iter, "charset", DBUS_TYPE_STRING, &prop->charset); if (prop->name) g_dbus_dict_append_entry(&iter, "name", DBUS_TYPE_STRING, &prop->name); if (prop->size) g_dbus_dict_append_entry(&iter, "size", DBUS_TYPE_UINT64, &prop->size); if (prop->ctime) g_dbus_dict_append_entry(&iter, "ctime", DBUS_TYPE_UINT64, &prop->ctime); if (prop->mtime) g_dbus_dict_append_entry(&iter, "mtime", DBUS_TYPE_UINT64, &prop->mtime); dbus_message_iter_close_container(&dict, &iter); } dbus_message_iter_close_container(args, &dict); } bluez-5.82/obexd/client/PaxHeaders/driver.h0000644000000000000000000000005014015011623015662 xustar0020 atime=1743516743 20 ctime=1743591283 bluez-5.82/obexd/client/driver.h0000644000000000000000000000106614015011623015346 0ustar00rootroot/* SPDX-License-Identifier: GPL-2.0-or-later */ /* * * OBEX Server * * Copyright (C) 2007-2010 Marcel Holtmann * * */ struct obc_driver { const char *service; const char *uuid; void *target; gsize target_len; void *(*supported_features) (struct obc_session *session); int (*probe) (struct obc_session *session); void (*remove) (struct obc_session *session); }; int obc_driver_register(struct obc_driver *driver); void obc_driver_unregister(struct obc_driver *driver); struct obc_driver *obc_driver_find(const char *pattern); bluez-5.82/PaxHeaders/src0000644000000000000000000000005014773213573012374 xustar0020 atime=1743591291 20 ctime=1743591291 bluez-5.82/src/0000755000000000000000000000000014773213573012132 5ustar00rootrootbluez-5.82/src/PaxHeaders/sdp-client.c0000644000000000000000000000005014031556404014645 xustar0020 atime=1743516624 20 ctime=1743591279 bluez-5.82/src/sdp-client.c0000644000000000000000000002261414031556404014333 0ustar00rootroot// SPDX-License-Identifier: GPL-2.0-or-later /* * * BlueZ - Bluetooth protocol stack for Linux * * Copyright (C) 2004-2011 Marcel Holtmann * * */ #ifdef HAVE_CONFIG_H #include #endif #include #include #include "lib/bluetooth.h" #include "lib/sdp.h" #include "lib/sdp_lib.h" #include "btio/btio.h" #include "shared/timeout.h" #include "log.h" #include "sdp-client.h" /* Number of seconds to keep a sdp_session_t in the cache */ #define CACHE_TIMEOUT 2 struct cached_sdp_session { bdaddr_t src; bdaddr_t dst; sdp_session_t *session; unsigned int timer; guint io_id; }; static GSList *cached_sdp_sessions = NULL; static void cleanup_cached_session(struct cached_sdp_session *cached) { cached_sdp_sessions = g_slist_remove(cached_sdp_sessions, cached); sdp_close(cached->session); g_free(cached); } static bool cached_session_expired(gpointer user_data) { struct cached_sdp_session *cached = user_data; g_source_remove(cached->io_id); cleanup_cached_session(cached); return FALSE; } static sdp_session_t *get_cached_sdp_session(const bdaddr_t *src, const bdaddr_t *dst) { GSList *l; for (l = cached_sdp_sessions; l != NULL; l = l->next) { struct cached_sdp_session *c = l->data; sdp_session_t *session; if (bacmp(&c->src, src) || bacmp(&c->dst, dst)) continue; timeout_remove(c->timer); g_source_remove(c->io_id); session = c->session; cached_sdp_sessions = g_slist_remove(cached_sdp_sessions, c); g_free(c); return session; } return NULL; } static gboolean disconnect_watch(GIOChannel *chan, GIOCondition cond, gpointer user_data) { struct cached_sdp_session *cached = user_data; timeout_remove(cached->timer); cleanup_cached_session(cached); return FALSE; } static void cache_sdp_session(bdaddr_t *src, bdaddr_t *dst, sdp_session_t *session) { struct cached_sdp_session *cached; int sk; GIOChannel *chan; cached = g_new0(struct cached_sdp_session, 1); bacpy(&cached->src, src); bacpy(&cached->dst, dst); cached->session = session; cached_sdp_sessions = g_slist_append(cached_sdp_sessions, cached); cached->timer = timeout_add_seconds(CACHE_TIMEOUT, cached_session_expired, cached, NULL); /* Watch the connection state during cache timeout */ sk = sdp_get_socket(session); chan = g_io_channel_unix_new(sk); cached->io_id = g_io_add_watch(chan, G_IO_HUP | G_IO_ERR | G_IO_NVAL, disconnect_watch, cached); g_io_channel_unref(chan); } struct search_context { bdaddr_t src; bdaddr_t dst; sdp_session_t *session; bt_callback_t cb; bt_destroy_t destroy; gpointer user_data; uuid_t uuid; guint io_id; gboolean filter_svc_class; }; static GSList *context_list = NULL; static void search_context_cleanup(struct search_context *ctxt) { context_list = g_slist_remove(context_list, ctxt); if (ctxt->destroy) ctxt->destroy(ctxt->user_data); g_free(ctxt); } static void search_completed_cb(uint8_t type, uint16_t status, uint8_t *rsp, size_t size, void *user_data) { struct search_context *ctxt = user_data; sdp_list_t *recs = NULL; int scanned, seqlen = 0, bytesleft = size; uint8_t dataType; int err = 0; if (status || type != SDP_SVC_SEARCH_ATTR_RSP) { err = -EPROTO; goto done; } scanned = sdp_extract_seqtype(rsp, bytesleft, &dataType, &seqlen); if (!scanned || !seqlen) goto done; rsp += scanned; bytesleft -= scanned; do { sdp_record_t *rec; int recsize; recsize = 0; rec = sdp_extract_pdu(rsp, bytesleft, &recsize); if (!rec) break; if (!recsize) { sdp_record_free(rec); break; } scanned += recsize; rsp += recsize; bytesleft -= recsize; /* Check whether service class ID matches some specified uuid. * If the record is missing service class ID, svclass will be * all zero, and thus will be unequal to the requested uuid. */ if (ctxt->filter_svc_class && sdp_uuid_cmp(&ctxt->uuid, &rec->svclass) != 0) { sdp_record_free(rec); continue; } recs = sdp_list_append(recs, rec); } while (scanned < (ssize_t) size && bytesleft > 0); done: cache_sdp_session(&ctxt->src, &ctxt->dst, ctxt->session); if (ctxt->cb) ctxt->cb(recs, err, ctxt->user_data); if (recs) sdp_list_free(recs, (sdp_free_func_t) sdp_record_free); search_context_cleanup(ctxt); } static gboolean search_process_cb(GIOChannel *chan, GIOCondition cond, gpointer user_data) { struct search_context *ctxt = user_data; if (cond & (G_IO_ERR | G_IO_HUP | G_IO_NVAL)) { sdp_close(ctxt->session); ctxt->session = NULL; if (ctxt->cb) ctxt->cb(NULL, -EIO, ctxt->user_data); search_context_cleanup(ctxt); return FALSE; } /* If sdp_process fails it calls search_completed_cb */ if (sdp_process(ctxt->session) < 0) return FALSE; return TRUE; } static gboolean connect_watch(GIOChannel *chan, GIOCondition cond, gpointer user_data) { struct search_context *ctxt = user_data; sdp_list_t *search, *attrids; uint32_t range = 0x0000ffff; socklen_t len; int sk, err, sk_err = 0; sk = g_io_channel_unix_get_fd(chan); ctxt->io_id = 0; len = sizeof(sk_err); if (getsockopt(sk, SOL_SOCKET, SO_ERROR, &sk_err, &len) < 0) err = -errno; else err = -sk_err; if (err != 0) goto failed; if (sdp_set_notify(ctxt->session, search_completed_cb, ctxt) < 0) { err = -EIO; goto failed; } search = sdp_list_append(NULL, &ctxt->uuid); attrids = sdp_list_append(NULL, &range); if (sdp_service_search_attr_async(ctxt->session, search, SDP_ATTR_REQ_RANGE, attrids) < 0) { sdp_list_free(attrids, NULL); sdp_list_free(search, NULL); err = -EIO; goto failed; } sdp_list_free(attrids, NULL); sdp_list_free(search, NULL); /* Set callback responsible for update the internal SDP transaction */ ctxt->io_id = g_io_add_watch(chan, G_IO_IN | G_IO_HUP | G_IO_ERR | G_IO_NVAL, search_process_cb, ctxt); return FALSE; failed: sdp_close(ctxt->session); ctxt->session = NULL; if (ctxt->cb) ctxt->cb(NULL, err, ctxt->user_data); search_context_cleanup(ctxt); return FALSE; } static int create_search_context(struct search_context **ctxt, const bdaddr_t *src, const bdaddr_t *dst, uuid_t *uuid, uint16_t flags) { sdp_session_t *s; GIOChannel *chan; uint32_t prio = 1; int sk; if (!ctxt) return -EINVAL; s = get_cached_sdp_session(src, dst); if (!s) s = sdp_connect(src, dst, SDP_NON_BLOCKING | flags); if (!s) return -errno; *ctxt = g_try_malloc0(sizeof(struct search_context)); if (!*ctxt) { sdp_close(s); return -ENOMEM; } bacpy(&(*ctxt)->src, src); bacpy(&(*ctxt)->dst, dst); (*ctxt)->session = s; (*ctxt)->uuid = *uuid; sk = sdp_get_socket(s); /* Set low priority for the SDP connection not to interfere with * other potential traffic. */ if (setsockopt(sk, SOL_SOCKET, SO_PRIORITY, &prio, sizeof(prio)) < 0) warn("Setting SDP priority failed: %s (%d)", strerror(errno), errno); chan = g_io_channel_unix_new(sk); (*ctxt)->io_id = g_io_add_watch(chan, G_IO_OUT | G_IO_HUP | G_IO_ERR | G_IO_NVAL, connect_watch, *ctxt); g_io_channel_unref(chan); return 0; } static int create_search_context_full(struct search_context **ctxt, const bdaddr_t *src, const bdaddr_t *dst, uuid_t *uuid, uint16_t flags, void *user_data, bt_callback_t cb, bt_destroy_t destroy, gboolean filter_svc_class) { int err = create_search_context(ctxt, src, dst, uuid, flags); if (err < 0) return err; (*ctxt)->cb = cb; (*ctxt)->destroy = destroy; (*ctxt)->user_data = user_data; (*ctxt)->filter_svc_class = filter_svc_class; return 0; } int bt_search(const bdaddr_t *src, const bdaddr_t *dst, uuid_t *uuid, bt_callback_t cb, void *user_data, bt_destroy_t destroy, uint16_t flags) { struct search_context *ctxt = NULL; int err; if (!cb) return -EINVAL; /* The resulting service class ID doesn't have to match uuid */ err = create_search_context_full(&ctxt, src, dst, uuid, flags, user_data, cb, destroy, FALSE); if (err < 0) return err; context_list = g_slist_append(context_list, ctxt); return 0; } int bt_search_service(const bdaddr_t *src, const bdaddr_t *dst, uuid_t *uuid, bt_callback_t cb, void *user_data, bt_destroy_t destroy, uint16_t flags) { struct search_context *ctxt = NULL; int err; if (!cb) return -EINVAL; /* The resulting service class ID need to match uuid */ err = create_search_context_full(&ctxt, src, dst, uuid, flags, user_data, cb, destroy, TRUE); if (err < 0) return err; context_list = g_slist_append(context_list, ctxt); return 0; } static int find_by_bdaddr(gconstpointer data, gconstpointer user_data) { const struct search_context *ctxt = data, *search = user_data; int ret; ret = bacmp(&ctxt->src, &search->src); if (ret != 0) return ret; return bacmp(&ctxt->dst, &search->dst); } int bt_cancel_discovery(const bdaddr_t *src, const bdaddr_t *dst) { struct search_context match, *ctxt; GSList *l; memset(&match, 0, sizeof(match)); bacpy(&match.src, src); bacpy(&match.dst, dst); /* Ongoing SDP Discovery */ l = g_slist_find_custom(context_list, &match, find_by_bdaddr); if (l == NULL) return -ENOENT; ctxt = l->data; if (!ctxt->session) return -ENOTCONN; if (ctxt->io_id) g_source_remove(ctxt->io_id); if (ctxt->session) sdp_close(ctxt->session); search_context_cleanup(ctxt); return 0; } void bt_clear_cached_session(const bdaddr_t *src, const bdaddr_t *dst) { sdp_session_t *session; session = get_cached_sdp_session(src, dst); if (session) sdp_close(session); } bluez-5.82/src/PaxHeaders/battery.c0000644000000000000000000000005014536422313014256 xustar0020 atime=1743516689 20 ctime=1743591285 bluez-5.82/src/battery.c0000644000000000000000000003533414536422313013747 0ustar00rootroot// SPDX-License-Identifier: GPL-2.0-or-later /* * * BlueZ - Bluetooth protocol stack for Linux * * Copyright (C) 2020 Google LLC * * */ #ifdef HAVE_CONFIG_H #include #endif #define _GNU_SOURCE #include #include #include #include "gdbus/gdbus.h" #include "lib/bluetooth.h" #include "src/shared/queue.h" #include "src/shared/util.h" #include "battery.h" #include "dbus-common.h" #include "adapter.h" #include "device.h" #include "log.h" #include "error.h" #define BATTERY_INTERFACE "org.bluez.Battery1" #define BATTERY_PROVIDER_INTERFACE "org.bluez.BatteryProvider1" #define BATTERY_PROVIDER_MANAGER_INTERFACE "org.bluez.BatteryProviderManager1" #define BATTERY_MAX_PERCENTAGE 100 struct btd_battery { char *path; /* D-Bus object path */ uint8_t percentage; /* valid between 0 to 100 inclusively */ char *source; /* Descriptive source of the battery info */ char *provider_path; /* The provider root path, if any */ }; struct btd_battery_provider_manager { struct btd_adapter *adapter; /* Does not own pointer */ struct queue *battery_providers; }; struct battery_provider { struct btd_battery_provider_manager *manager; /* Does not own pointer */ char *owner; /* Owner D-Bus address */ char *path; /* D-Bus object path */ GDBusClient *client; }; static struct queue *batteries = NULL; static void provider_disconnect_cb(DBusConnection *conn, void *user_data); static void battery_add(struct btd_battery *battery) { if (!batteries) batteries = queue_new(); queue_push_head(batteries, battery); } static void battery_remove(struct btd_battery *battery) { queue_remove(batteries, battery); if (queue_isempty(batteries)) { queue_destroy(batteries, NULL); batteries = NULL; } } static bool match_path(const void *data, const void *user_data) { const struct btd_battery *battery = data; const char *path = user_data; return g_strcmp0(battery->path, path) == 0; } static struct btd_battery *battery_new(const char *path, const char *source, const char *provider_path) { struct btd_battery *battery; battery = new0(struct btd_battery, 1); battery->path = g_strdup(path); battery->percentage = UINT8_MAX; if (source) battery->source = g_strdup(source); if (provider_path) battery->provider_path = g_strdup(provider_path); return battery; } static void battery_free(struct btd_battery *battery) { if (battery->path) g_free(battery->path); if (battery->source) g_free(battery->source); free(battery); } static gboolean property_percentage_get(const GDBusPropertyTable *property, DBusMessageIter *iter, void *data) { struct btd_battery *battery = data; dbus_message_iter_append_basic(iter, DBUS_TYPE_BYTE, &battery->percentage); return TRUE; } static gboolean property_percentage_exists(const GDBusPropertyTable *property, void *data) { struct btd_battery *battery = data; return battery->percentage <= BATTERY_MAX_PERCENTAGE; } static gboolean property_source_get(const GDBusPropertyTable *property, DBusMessageIter *iter, void *data) { struct btd_battery *battery = data; dbus_message_iter_append_basic(iter, DBUS_TYPE_STRING, &battery->source); return TRUE; } static gboolean property_source_exists(const GDBusPropertyTable *property, void *data) { struct btd_battery *battery = data; return battery->source != NULL; } static const GDBusPropertyTable battery_properties[] = { { "Percentage", "y", property_percentage_get, NULL, property_percentage_exists }, { "Source", "s", property_source_get, NULL, property_source_exists }, {} }; struct btd_battery *btd_battery_register(const char *path, const char *source, const char *provider_path) { struct btd_battery *battery; DBG("path = %s", path); if (queue_find(batteries, match_path, path)) { error("error registering battery: path exists"); return NULL; } if (!g_str_has_prefix(path, "/")) { error("error registering battery: invalid D-Bus object path"); return NULL; } battery = battery_new(path, source, provider_path); battery_add(battery); if (!g_dbus_register_interface(btd_get_dbus_connection(), battery->path, BATTERY_INTERFACE, NULL, NULL, battery_properties, battery, NULL)) { error("error registering D-Bus interface for %s", battery->path); battery_remove(battery); battery_free(battery); return NULL; } DBG("registered Battery object: %s", battery->path); return battery; } bool btd_battery_unregister(struct btd_battery *battery) { DBG("path = %s", battery->path); if (!queue_find(batteries, NULL, battery)) { error("error unregistering battery: " "battery %s is not registered", battery->path); return false; } if (!g_dbus_unregister_interface(btd_get_dbus_connection(), battery->path, BATTERY_INTERFACE)) { error("error unregistering battery %s from D-Bus interface", battery->path); return false; } battery_remove(battery); battery_free(battery); return true; } bool btd_battery_update(struct btd_battery *battery, uint8_t percentage) { DBG("path = %s", battery->path); if (!queue_find(batteries, NULL, battery)) { error("error updating battery: battery is not registered"); return false; } if (percentage > BATTERY_MAX_PERCENTAGE) { error("error updating battery: percentage is not valid"); return false; } if (battery->percentage == percentage) return true; battery->percentage = percentage; g_dbus_emit_property_changed(btd_get_dbus_connection(), battery->path, BATTERY_INTERFACE, "Percentage"); return true; } static struct btd_battery *find_battery_by_path(const char *path) { return queue_find(batteries, match_path, path); } static void provided_battery_property_changed_cb(GDBusProxy *proxy, const char *name, DBusMessageIter *iter, void *user_data) { uint8_t percentage = 0; const char *export_path; DBusMessageIter dev_iter; if (g_dbus_proxy_get_property(proxy, "Device", &dev_iter) == FALSE) return; dbus_message_iter_get_basic(&dev_iter, &export_path); if (strcmp(name, "Percentage") != 0) return; if (iter) { if (dbus_message_iter_get_arg_type(iter) != DBUS_TYPE_BYTE) return; dbus_message_iter_get_basic(iter, &percentage); } DBG("battery percentage changed on %s, percentage = %d", g_dbus_proxy_get_path(proxy), percentage); btd_battery_update(find_battery_by_path(export_path), percentage); } static void provided_battery_added_cb(GDBusProxy *proxy, void *user_data) { struct battery_provider *provider = user_data; struct btd_battery *battery; struct btd_device *device; const char *path = g_dbus_proxy_get_path(proxy); const char *export_path; const char *source = NULL; uint8_t percentage; DBusMessageIter iter; if (strcmp(g_dbus_proxy_get_interface(proxy), BATTERY_PROVIDER_INTERFACE) != 0) return; if (g_dbus_proxy_get_property(proxy, "Device", &iter) == FALSE) { warn("Battery object %s does not specify device path", path); return; } dbus_message_iter_get_basic(&iter, &export_path); device = btd_adapter_find_device_by_path(provider->manager->adapter, export_path); if (!device || device_is_temporary(device)) { warn("Ignoring non-existent device path for battery %s", export_path); return; } if (find_battery_by_path(export_path)) { DBG("Battery for %s is already provided, ignoring the new one", export_path); return; } g_dbus_proxy_set_property_watch( proxy, provided_battery_property_changed_cb, provider); if (g_dbus_proxy_get_property(proxy, "Source", &iter) == TRUE) dbus_message_iter_get_basic(&iter, &source); battery = btd_battery_register(export_path, source, provider->path); DBG("provided battery added %s", path); /* Percentage property may not be immediately available, that's okay * since we monitor changes to this property. */ if (g_dbus_proxy_get_property(proxy, "Percentage", &iter) == FALSE) return; dbus_message_iter_get_basic(&iter, &percentage); btd_battery_update(battery, percentage); } static void provided_battery_removed_cb(GDBusProxy *proxy, void *user_data) { struct battery_provider *provider = user_data; struct btd_battery *battery; const char *export_path; DBusMessageIter iter; if (strcmp(g_dbus_proxy_get_interface(proxy), BATTERY_PROVIDER_INTERFACE) != 0) return; if (g_dbus_proxy_get_property(proxy, "Device", &iter) == FALSE) return; dbus_message_iter_get_basic(&iter, &export_path); DBG("provided battery removed %s", g_dbus_proxy_get_path(proxy)); battery = find_battery_by_path(export_path); if (!battery) return; if (g_strcmp0(battery->provider_path, provider->path) != 0) return; g_dbus_proxy_set_property_watch(proxy, NULL, NULL); btd_battery_unregister(battery); } static bool match_provider_path(const void *data, const void *user_data) { const struct battery_provider *provider = data; const char *path = user_data; return strcmp(provider->path, path) == 0; } static void unregister_if_path_has_prefix(void *data, void *user_data) { struct btd_battery *battery = data; struct battery_provider *provider = user_data; if (g_strcmp0(battery->provider_path, provider->path) == 0) btd_battery_unregister(battery); } static void battery_provider_free(gpointer data) { struct battery_provider *provider = data; /* Unregister batteries under the root path of provider->path */ queue_foreach(batteries, unregister_if_path_has_prefix, provider); if (provider->owner) g_free(provider->owner); if (provider->path) g_free(provider->path); if (provider->client) { g_dbus_client_set_disconnect_watch(provider->client, NULL, NULL); g_dbus_client_set_proxy_handlers(provider->client, NULL, NULL, NULL, NULL); g_dbus_client_unref(provider->client); } free(provider); } static struct battery_provider * battery_provider_new(DBusConnection *conn, struct btd_battery_provider_manager *manager, const char *path, const char *sender) { struct battery_provider *provider; provider = new0(struct battery_provider, 1); provider->manager = manager; provider->owner = g_strdup(sender); provider->path = g_strdup(path); provider->client = g_dbus_client_new_full(conn, sender, path, path); if (!provider->client) { error("error creating D-Bus client %s", path); battery_provider_free(provider); return NULL; } g_dbus_client_set_disconnect_watch(provider->client, provider_disconnect_cb, provider); g_dbus_client_set_proxy_handlers(provider->client, provided_battery_added_cb, provided_battery_removed_cb, NULL, provider); return provider; } static void provider_disconnect_cb(DBusConnection *conn, void *user_data) { struct battery_provider *provider = user_data; struct btd_battery_provider_manager *manager = provider->manager; DBG("battery provider client disconnected %s root path %s", provider->owner, provider->path); if (!queue_find(manager->battery_providers, NULL, provider)) { warn("Disconnection on a non-existing provider %s", provider->path); return; } queue_remove(manager->battery_providers, provider); battery_provider_free(provider); } static DBusMessage *register_battery_provider(DBusConnection *conn, DBusMessage *msg, void *user_data) { struct btd_battery_provider_manager *manager = user_data; const char *sender = dbus_message_get_sender(msg); DBusMessageIter args; const char *path; struct battery_provider *provider; if (!dbus_message_iter_init(msg, &args)) return btd_error_invalid_args(msg); if (dbus_message_iter_get_arg_type(&args) != DBUS_TYPE_OBJECT_PATH) return btd_error_invalid_args(msg); dbus_message_iter_get_basic(&args, &path); DBG("register battery provider path = %s", path); if (!g_str_has_prefix(path, "/")) return btd_error_invalid_args(msg); if (queue_find(manager->battery_providers, match_provider_path, path)) { return dbus_message_new_error(msg, ERROR_INTERFACE ".AlreadyExists", "Provider already exists"); } provider = battery_provider_new(conn, manager, path, sender); queue_push_head(manager->battery_providers, provider); return dbus_message_new_method_return(msg); } static DBusMessage *unregister_battery_provider(DBusConnection *conn, DBusMessage *msg, void *user_data) { struct btd_battery_provider_manager *manager = user_data; const char *sender = dbus_message_get_sender(msg); DBusMessageIter args; const char *path; struct battery_provider *provider; if (!dbus_message_iter_init(msg, &args)) return btd_error_invalid_args(msg); if (dbus_message_iter_get_arg_type(&args) != DBUS_TYPE_OBJECT_PATH) return btd_error_invalid_args(msg); dbus_message_iter_get_basic(&args, &path); DBG("unregister battery provider path = %s", path); provider = queue_find(manager->battery_providers, match_provider_path, path); if (!provider || strcmp(provider->owner, sender) != 0) { return dbus_message_new_error(msg, ERROR_INTERFACE ".DoesNotExist", "Provider does not exist"); } queue_remove(manager->battery_providers, provider); battery_provider_free(provider); return dbus_message_new_method_return(msg); } static const GDBusMethodTable methods[] = { { GDBUS_METHOD("RegisterBatteryProvider", GDBUS_ARGS({ "provider", "o" }), NULL, register_battery_provider) }, { GDBUS_METHOD("UnregisterBatteryProvider", GDBUS_ARGS({ "provider", "o" }), NULL, unregister_battery_provider) }, {} }; static struct btd_battery_provider_manager * manager_new(struct btd_adapter *adapter) { struct btd_battery_provider_manager *manager; DBG(""); manager = new0(struct btd_battery_provider_manager, 1); manager->adapter = adapter; manager->battery_providers = queue_new(); return manager; } static void manager_free(struct btd_battery_provider_manager *manager) { if (!manager) return; DBG(""); queue_destroy(manager->battery_providers, battery_provider_free); free(manager); } struct btd_battery_provider_manager * btd_battery_provider_manager_create(struct btd_adapter *adapter) { struct btd_battery_provider_manager *manager; if (!adapter) return NULL; manager = manager_new(adapter); if (!manager) return NULL; if (!g_dbus_register_interface(btd_get_dbus_connection(), adapter_get_path(manager->adapter), BATTERY_PROVIDER_MANAGER_INTERFACE, methods, NULL, NULL, manager, NULL)) { error("error registering " BATTERY_PROVIDER_MANAGER_INTERFACE " interface"); manager_free(manager); return NULL; } info("Battery Provider Manager created"); return manager; } void btd_battery_provider_manager_destroy( struct btd_battery_provider_manager *manager) { if (!manager) return; g_dbus_unregister_interface(btd_get_dbus_connection(), adapter_get_path(manager->adapter), BATTERY_PROVIDER_MANAGER_INTERFACE); info("Battery Provider Manager destroyed"); manager_free(manager); } bluez-5.82/src/PaxHeaders/backtrace.c0000644000000000000000000000005014015011623014511 xustar0020 atime=1743516607 20 ctime=1743591284 bluez-5.82/src/backtrace.c0000644000000000000000000000506514015011623014200 0ustar00rootroot// SPDX-License-Identifier: GPL-2.0-or-later /* * * BlueZ - Bluetooth protocol stack for Linux * * Copyright (C) 2004-2010 Marcel Holtmann * * */ #ifdef HAVE_CONFIG_H #include #endif #include #include #ifdef HAVE_BACKTRACE_SUPPORT #include #include #endif #include "src/log.h" #include "src/backtrace.h" void btd_backtrace_init(void) { #ifdef HAVE_BACKTRACE_SUPPORT void *frames[1]; /* * initialize the backtracer, since the ctor calls dlopen(), which * calls malloc(), which isn't signal-safe. */ backtrace(frames, 1); #endif } void btd_backtrace(uint16_t index) { #ifdef HAVE_BACKTRACE_SUPPORT char *debuginfo_path = NULL; const Dwfl_Callbacks callbacks = { .find_debuginfo = dwfl_standard_find_debuginfo, .find_elf = dwfl_linux_proc_find_elf, .debuginfo_path = &debuginfo_path, }; Dwfl *dwfl; void *frames[48]; int n, n_ptrs; dwfl = dwfl_begin(&callbacks); if (dwfl_linux_proc_report(dwfl, getpid())) goto done; dwfl_report_end(dwfl, NULL, NULL); n_ptrs = backtrace(frames, 48); if (n_ptrs < 1) goto done; btd_error(index, "++++++++ backtrace ++++++++"); for (n = 1; n < n_ptrs; n++) { GElf_Addr addr = (uintptr_t) frames[n]; GElf_Sym sym; GElf_Word shndx; Dwfl_Module *module = dwfl_addrmodule(dwfl, addr); Dwfl_Line *line; const char *name, *modname; if (!module) { btd_error(index, "#%-2u ?? [%#" PRIx64 "]", n, addr); continue; } name = dwfl_module_addrsym(module, addr, &sym, &shndx); if (!name) { modname = dwfl_module_info(module, NULL, NULL, NULL, NULL, NULL, NULL, NULL); btd_error(index, "#%-2u ?? (%s) [%#" PRIx64 "]", n, modname, addr); continue; } line = dwfl_module_getsrc(module, addr); if (line) { int lineno; const char *src = dwfl_lineinfo(line, NULL, &lineno, NULL, NULL, NULL); if (src) { btd_error(index, "#%-2u %s+%#" PRIx64 " " "(%s:%d) [%#" PRIx64 "]", n, name, addr - sym.st_value, src, lineno, addr); continue; } } modname = dwfl_module_info(module, NULL, NULL, NULL, NULL, NULL, NULL, NULL); btd_error(index, "#%-2u %s+%#" PRIx64 " (%s) [%#" PRIx64 "]", n, name, addr - sym.st_value, modname, addr); } btd_error(index, "+++++++++++++++++++++++++++"); done: dwfl_end(dwfl); #endif } void btd_assertion_message_expr(const char *file, int line, const char *func, const char *expr) { btd_error(0xffff, "Assertion failed: (%s) %s:%d in %s", expr, file, line, func); btd_backtrace(0xffff); } bluez-5.82/src/PaxHeaders/sdpd-database.c0000644000000000000000000000005014015011623015266 xustar0020 atime=1743516613 20 ctime=1743591278 bluez-5.82/src/sdpd-database.c0000644000000000000000000001240014015011623014744 0ustar00rootroot// SPDX-License-Identifier: GPL-2.0-or-later /* * * BlueZ - Bluetooth protocol stack for Linux * * Copyright (C) 2001-2002 Nokia Corporation * Copyright (C) 2002-2003 Maxim Krasnyansky * Copyright (C) 2002-2010 Marcel Holtmann * Copyright (C) 2002-2003 Stephen Crane * * */ #ifdef HAVE_CONFIG_H #include #endif #include #include #include "lib/bluetooth.h" #include "lib/sdp.h" #include "lib/sdp_lib.h" #include "sdpd.h" #include "log.h" static sdp_list_t *service_db; static sdp_list_t *access_db; typedef struct { uint32_t handle; bdaddr_t device; } sdp_access_t; /* * Ordering function called when inserting a service record. * The service repository is a linked list in sorted order * and the service record handle is the sort key */ int record_sort(const void *r1, const void *r2) { const sdp_record_t *rec1 = r1; const sdp_record_t *rec2 = r2; if (!rec1 || !rec2) { error("NULL RECORD LIST FATAL"); return -1; } return rec1->handle - rec2->handle; } static int access_sort(const void *r1, const void *r2) { const sdp_access_t *rec1 = r1; const sdp_access_t *rec2 = r2; if (!rec1 || !rec2) { error("NULL RECORD LIST FATAL"); return -1; } return rec1->handle - rec2->handle; } static void access_free(void *p) { free(p); } /* * Reset the service repository by deleting its contents */ void sdp_svcdb_reset(void) { sdp_list_free(service_db, (sdp_free_func_t) sdp_record_free); service_db = NULL; sdp_list_free(access_db, access_free); access_db = NULL; } typedef struct _indexed { int sock; sdp_record_t *record; } sdp_indexed_t; static sdp_list_t *socket_index; /* * collect all services registered over this socket */ void sdp_svcdb_collect_all(int sock) { sdp_list_t *p, *q; for (p = socket_index, q = 0; p; ) { sdp_indexed_t *item = p->data; if (item->sock == sock) { sdp_list_t *next = p->next; sdp_record_remove(item->record->handle); sdp_record_free(item->record); free(item); if (q) q->next = next; else socket_index = next; free(p); p = next; } else if (item->sock > sock) return; else { q = p; p = p->next; } } } void sdp_svcdb_collect(sdp_record_t *rec) { sdp_list_t *p, *q; for (p = socket_index, q = 0; p; q = p, p = p->next) { sdp_indexed_t *item = p->data; if (rec == item->record) { free(item); if (q) q->next = p->next; else socket_index = p->next; free(p); return; } } } static int compare_indices(const void *i1, const void *i2) { const sdp_indexed_t *s1 = i1; const sdp_indexed_t *s2 = i2; return s1->sock - s2->sock; } void sdp_svcdb_set_collectable(sdp_record_t *record, int sock) { sdp_indexed_t *item = malloc(sizeof(sdp_indexed_t)); if (!item) { SDPDBG("No memory"); return; } item->sock = sock; item->record = record; socket_index = sdp_list_insert_sorted(socket_index, item, compare_indices); } /* * Add a service record to the repository */ void sdp_record_add(const bdaddr_t *device, sdp_record_t *rec) { sdp_access_t *dev; SDPDBG("Adding rec : 0x%lx", (long) rec); SDPDBG("with handle : 0x%x", rec->handle); service_db = sdp_list_insert_sorted(service_db, rec, record_sort); dev = malloc(sizeof(*dev)); if (!dev) return; bacpy(&dev->device, device); dev->handle = rec->handle; access_db = sdp_list_insert_sorted(access_db, dev, access_sort); } static sdp_list_t *record_locate(uint32_t handle) { if (service_db) { sdp_list_t *p; sdp_record_t r; r.handle = handle; p = sdp_list_find(service_db, &r, record_sort); return p; } SDPDBG("Could not find svcRec for : 0x%x", handle); return NULL; } static sdp_list_t *access_locate(uint32_t handle) { if (access_db) { sdp_list_t *p; sdp_access_t a; a.handle = handle; p = sdp_list_find(access_db, &a, access_sort); return p; } SDPDBG("Could not find access data for : 0x%x", handle); return NULL; } /* * Given a service record handle, find the record associated with it. */ sdp_record_t *sdp_record_find(uint32_t handle) { sdp_list_t *p = record_locate(handle); if (!p) { SDPDBG("Couldn't find record for : 0x%x", handle); return 0; } return p->data; } /* * Given a service record handle, remove its record from the repository */ int sdp_record_remove(uint32_t handle) { sdp_list_t *p = record_locate(handle); sdp_record_t *r; sdp_access_t *a; if (!p) { error("Remove : Couldn't find record for : 0x%x", handle); return -1; } r = p->data; if (r) service_db = sdp_list_remove(service_db, r); p = access_locate(handle); if (p == NULL || p->data == NULL) return 0; a = p->data; access_db = sdp_list_remove(access_db, a); access_free(a); return 0; } /* * Return a pointer to the linked list containing the records in sorted order */ sdp_list_t *sdp_get_record_list(void) { return service_db; } int sdp_check_access(uint32_t handle, bdaddr_t *device) { sdp_list_t *p = access_locate(handle); sdp_access_t *a; if (!p) return 1; a = p->data; if (!a) return 1; if (bacmp(&a->device, device) && bacmp(&a->device, BDADDR_ANY) && bacmp(device, BDADDR_ANY)) return 0; return 1; } uint32_t sdp_next_handle(void) { uint32_t handle = 0x10000; while (sdp_record_find(handle)) handle++; return handle; } bluez-5.82/src/PaxHeaders/service.h0000644000000000000000000000005014267331527014260 xustar0020 atime=1743516500 20 ctime=1743591285 bluez-5.82/src/service.h0000644000000000000000000000433514267331527013746 0ustar00rootroot/* SPDX-License-Identifier: GPL-2.0-or-later */ /* * * BlueZ - Bluetooth protocol stack for Linux * * Copyright (C) 2012-2013 BMW Car IT GmbH. All rights reserved. * * */ typedef enum { BTD_SERVICE_STATE_UNAVAILABLE, /* Not probed */ BTD_SERVICE_STATE_DISCONNECTED, BTD_SERVICE_STATE_CONNECTING, BTD_SERVICE_STATE_CONNECTED, BTD_SERVICE_STATE_DISCONNECTING, } btd_service_state_t; struct btd_service; struct btd_device; struct btd_profile; typedef void (*btd_service_state_cb) (struct btd_service *service, btd_service_state_t old_state, btd_service_state_t new_state, void *user_data); struct btd_service *btd_service_ref(struct btd_service *service); void btd_service_unref(struct btd_service *service); /* Service management functions used by the core */ struct btd_service *service_create(struct btd_device *device, struct btd_profile *profile); int service_probe(struct btd_service *service); void service_remove(struct btd_service *service); int service_accept(struct btd_service *service, bool initiator); int service_set_connecting(struct btd_service *service); /* Connection control API */ int btd_service_connect(struct btd_service *service); int btd_service_disconnect(struct btd_service *service); /* Public member access */ struct btd_device *btd_service_get_device(const struct btd_service *service); struct btd_profile *btd_service_get_profile(const struct btd_service *service); btd_service_state_t btd_service_get_state(const struct btd_service *service); int btd_service_get_error(const struct btd_service *service); bool btd_service_is_initiator(const struct btd_service *service); unsigned int btd_service_add_state_cb(btd_service_state_cb cb, void *user_data); bool btd_service_remove_state_cb(unsigned int id); void btd_service_set_allowed(struct btd_service *service, bool allowed); bool btd_service_is_allowed(struct btd_service *service); /* Functions used by profile implementation */ void btd_service_connecting_complete(struct btd_service *service, int err); void btd_service_disconnecting_complete(struct btd_service *service, int err); void btd_service_set_user_data(struct btd_service *service, void *user_data); void *btd_service_get_user_data(const struct btd_service *service); bluez-5.82/src/PaxHeaders/adv_monitor.c0000644000000000000000000000005014447506754015142 xustar0020 atime=1743516685 20 ctime=1743591285 bluez-5.82/src/adv_monitor.c0000644000000000000000000017370414447506754014637 0ustar00rootroot// SPDX-License-Identifier: LGPL-2.1-or-later /* * * BlueZ - Bluetooth protocol stack for Linux * * Copyright (C) 2020 Google LLC * * */ #ifdef HAVE_CONFIG_H #include #endif #define _GNU_SOURCE #include #include #include #include #include #include #include "lib/bluetooth.h" #include "lib/mgmt.h" #include "adapter.h" #include "btd.h" #include "dbus-common.h" #include "device.h" #include "log.h" #include "src/error.h" #include "src/shared/mgmt.h" #include "src/shared/queue.h" #include "src/shared/timeout.h" #include "src/shared/util.h" #include "adv_monitor.h" #define ADV_MONITOR_INTERFACE "org.bluez.AdvertisementMonitor1" #define ADV_MONITOR_MGR_INTERFACE "org.bluez.AdvertisementMonitorManager1" #define ADV_MONITOR_UNSET_RSSI 127 /* dBm */ #define ADV_MONITOR_MAX_RSSI 20 /* dBm */ #define ADV_MONITOR_MIN_RSSI -127 /* dBm */ #define ADV_MONITOR_UNSET_TIMEOUT 0 /* second */ #define ADV_MONITOR_MIN_TIMEOUT 1 /* second */ #define ADV_MONITOR_MAX_TIMEOUT 300 /* second */ #define ADV_MONITOR_DEFAULT_LOW_TIMEOUT 5 /* second */ #define ADV_MONITOR_DEFAULT_HIGH_TIMEOUT 10 /* second */ #define ADV_MONITOR_UNSET_SAMPLING_PERIOD 256 /* 100 ms */ #define ADV_MONITOR_MAX_SAMPLING_PERIOD 255 /* 100 ms */ struct btd_adv_monitor_manager { struct btd_adapter *adapter; struct mgmt *mgmt; uint16_t adapter_id; uint32_t supported_features; /* MGMT_ADV_MONITOR_FEATURE_MASK_* */ uint32_t enabled_features; /* MGMT_ADV_MONITOR_FEATURE_MASK_* */ uint16_t max_num_monitors; uint8_t max_num_patterns; struct queue *apps; /* apps who registered for Adv monitoring */ struct queue *merged_patterns; }; struct adv_monitor_app { struct btd_adv_monitor_manager *manager; char *owner; char *path; DBusMessage *reg; GDBusClient *client; struct queue *monitors; }; enum monitor_type { MONITOR_TYPE_NONE, MONITOR_TYPE_OR_PATTERNS, }; enum monitor_state { MONITOR_STATE_NEW, /* New but not yet init'ed with actual values */ MONITOR_STATE_FAILED, /* Failed to be init'ed */ MONITOR_STATE_INITED, /* Init'ed but not yet sent to kernel */ MONITOR_STATE_ACTIVE, /* Accepted by kernel */ MONITOR_STATE_REMOVED, /* Removed from kernel */ MONITOR_STATE_RELEASED, /* Dbus Object removed by app */ }; enum merged_pattern_state { MERGED_PATTERN_STATE_ADDING, /* Adding pattern to kernel */ MERGED_PATTERN_STATE_REMOVING, /* Removing pattern from kernel */ MERGED_PATTERN_STATE_STABLE, /* Idle */ }; struct rssi_parameters { int8_t high_rssi; /* High RSSI threshold */ uint16_t high_rssi_timeout; /* High RSSI threshold timeout */ int8_t low_rssi; /* Low RSSI threshold */ uint16_t low_rssi_timeout; /* Low RSSI threshold timeout */ uint16_t sampling_period; /* Merge packets in the same timeslot. * Currenly unimplemented in user space. * Used only to pass data to kernel. */ }; struct adv_monitor { struct adv_monitor_app *app; GDBusProxy *proxy; char *path; enum monitor_state state; /* MONITOR_STATE_* */ struct rssi_parameters rssi; /* RSSI parameter for this monitor */ struct adv_monitor_merged_pattern *merged_pattern; struct queue *devices; /* List of adv_monitor_device objects */ }; /* Some chipsets doesn't support multiple monitors with the same pattern. * To solve that and to generally ease their task, we merge monitors with the * same pattern, so those monitors will only be sent once to the kernel. */ struct adv_monitor_merged_pattern { struct btd_adv_monitor_manager *manager; uint16_t monitor_handle; /* Kernel Monitor Handle */ struct rssi_parameters rssi; /* Merged RSSI parameter for |monitors|, * this will be sent to the kernel. */ struct queue *monitors; /* List of adv_monitor objects which * have this pattern */ enum monitor_type type; /* MONITOR_TYPE_* */ struct queue *patterns; /* List of bt_ad_pattern objects */ enum merged_pattern_state current_state; /* MERGED_PATTERN_STATE_* */ enum merged_pattern_state next_state; /* MERGED_PATTERN_STATE_* */ }; /* Some data like last_seen, timer/timeout values need to be maintained * per device. struct adv_monitor_device maintains such data. */ struct adv_monitor_device { struct adv_monitor *monitor; struct btd_device *device; time_t high_rssi_first_seen; /* Start time when RSSI climbs above * the high RSSI threshold */ time_t low_rssi_first_seen; /* Start time when RSSI drops below * the low RSSI threshold */ time_t last_seen; /* Time when last Adv was received */ bool found; /* State of the device - lost/found */ unsigned int lost_timer; /* Timer to track if the device goes * offline/out-of-range */ }; struct app_match_data { const char *owner; const char *path; }; struct adv_content_filter_info { struct bt_ad *ad; struct queue *matched_monitors; /* List of matched monitors */ }; struct adv_rssi_filter_info { struct btd_device *device; int8_t rssi; }; struct monitored_device_info { uint16_t monitor_handle; /* Kernel Monitor Handle */ struct btd_device *device; }; static void monitor_device_free(void *data); static void adv_monitor_filter_rssi(struct adv_monitor *monitor, struct btd_device *device, int8_t rssi); static void merged_pattern_send_add( struct adv_monitor_merged_pattern *merged_pattern); static void merged_pattern_send_remove( struct adv_monitor_merged_pattern *merged_pattern); const struct adv_monitor_type { enum monitor_type type; const char *name; } supported_types[] = { { MONITOR_TYPE_OR_PATTERNS, "or_patterns" }, { }, }; static void rssi_unset(struct rssi_parameters *rssi) { rssi->high_rssi = ADV_MONITOR_UNSET_RSSI; rssi->high_rssi_timeout = ADV_MONITOR_UNSET_TIMEOUT; rssi->low_rssi = ADV_MONITOR_UNSET_RSSI; rssi->low_rssi_timeout = ADV_MONITOR_UNSET_TIMEOUT; rssi->sampling_period = ADV_MONITOR_UNSET_SAMPLING_PERIOD; } static bool rssi_is_unset(const struct rssi_parameters *rssi) { return rssi->high_rssi == ADV_MONITOR_UNSET_RSSI && rssi->low_rssi == ADV_MONITOR_UNSET_RSSI && rssi->high_rssi_timeout == ADV_MONITOR_UNSET_TIMEOUT && rssi->low_rssi_timeout == ADV_MONITOR_UNSET_TIMEOUT && rssi->sampling_period == ADV_MONITOR_UNSET_SAMPLING_PERIOD; } /* Replies to an app's D-Bus message and unref it */ static void app_reply_msg(struct adv_monitor_app *app, DBusMessage *reply) { if (!app || !app->reg || !reply) return; g_dbus_send_message(btd_get_dbus_connection(), reply); dbus_message_unref(app->reg); app->reg = NULL; } /* Frees a pattern */ static void pattern_free(void *data) { struct bt_ad_pattern *pattern = data; free(pattern); } static void merged_pattern_free(void *data) { struct adv_monitor_merged_pattern *merged_pattern = data; queue_destroy(merged_pattern->patterns, pattern_free); queue_destroy(merged_pattern->monitors, NULL); if (merged_pattern->manager) queue_remove(merged_pattern->manager->merged_patterns, merged_pattern); free(merged_pattern); } /* Returns the smaller of the two integers |a| and |b| which is not equal to the * |unset| value. If both are unset, return unset. */ static int get_smaller_not_unset(int a, int b, int unset) { if (a == unset) return b; if (b == unset) return a; return a < b ? a : b; } /* Merges two RSSI parameters, return the result. The result is chosen to be * whichever is more lenient of the two inputs, so we can pass that to the * kernel and still do additional filtering in the user space without loss of * information while still receiving benefit from offloading some filtering to * the hardware. * It is allowed for |a|, |b|, and |merged| to point to the same object. */ static void merge_rssi(const struct rssi_parameters *a, const struct rssi_parameters *b, struct rssi_parameters *merged) { /* For low rssi, low_timeout, and high_rssi, choose the minimum of the * two values. Filtering the higher values is done on userspace. */ merged->low_rssi = get_smaller_not_unset(a->low_rssi, b->low_rssi, ADV_MONITOR_UNSET_RSSI); merged->high_rssi = get_smaller_not_unset(a->high_rssi, b->high_rssi, ADV_MONITOR_UNSET_RSSI); merged->low_rssi_timeout = get_smaller_not_unset(a->low_rssi_timeout, b->low_rssi_timeout, ADV_MONITOR_UNSET_TIMEOUT); /* High timeout doesn't matter for now, it will be zeroed when it is * forwarded to kernel anyway. */ merged->high_rssi_timeout = 0; /* Sampling period is not implemented yet in userspace. There is no * good value if the two values are different, so just choose 0 for * always reporting, to avoid missing packets. */ if (a->sampling_period != b->sampling_period) merged->sampling_period = 0; else merged->sampling_period = a->sampling_period; } /* Two merged_pattern are considered equal if all the following are true: * (1) both has the same monitor_type * (2) both has exactly the same pattern in the same order * Therefore, patterns A+B and B+A are considered different, as well as patterns * A and A+A. This shouldn't cause any issue, but solving this issue is a * potential improvement. */ static bool merged_pattern_is_equal(const void *data, const void *match_data) { const struct adv_monitor_merged_pattern *a = data; const struct adv_monitor_merged_pattern *b = match_data; const struct queue_entry *a_entry, *b_entry; struct bt_ad_pattern *a_data, *b_data; if (a->type != b->type) return false; if (queue_length(a->patterns) != queue_length(b->patterns)) return false; a_entry = queue_get_entries(a->patterns); b_entry = queue_get_entries(b->patterns); while (a_entry) { a_data = a_entry->data; b_data = b_entry->data; if (a_data->type != b_data->type || a_data->offset != b_data->offset || a_data->len != b_data->len || memcmp(a_data->data, b_data->data, a_data->len) != 0) return false; a_entry = a_entry->next; b_entry = b_entry->next; } return true; } static char *get_merged_pattern_state_name(enum merged_pattern_state state) { switch (state) { case MERGED_PATTERN_STATE_ADDING: return "Adding"; case MERGED_PATTERN_STATE_REMOVING: return "Removing"; case MERGED_PATTERN_STATE_STABLE: return "Stable"; } return NULL; } /* Adds a new merged pattern */ static void merged_pattern_add( struct adv_monitor_merged_pattern *merged_pattern) { /* This is only called when no merged_pattern found. Therefore, the * state must be stable. */ if (merged_pattern->current_state != MERGED_PATTERN_STATE_STABLE) { btd_error(merged_pattern->manager->adapter_id, "Add merged_pattern request when state is not stable"); return; } merged_pattern->current_state = MERGED_PATTERN_STATE_ADDING; merged_pattern_send_add(merged_pattern); DBG("Monitor state: %s -> %s", get_merged_pattern_state_name(merged_pattern->current_state), get_merged_pattern_state_name(merged_pattern->next_state)); } /* Removes merged pattern, or queues for removal if busy */ static void merged_pattern_remove( struct adv_monitor_merged_pattern *merged_pattern) { rssi_unset(&merged_pattern->rssi); /* If we currently are removing, cancel subsequent ADD command if any */ if (merged_pattern->current_state == MERGED_PATTERN_STATE_REMOVING) { merged_pattern->next_state = MERGED_PATTERN_STATE_STABLE; goto print_state; } /* If stable, we can proceed with removal right away */ if (merged_pattern->current_state == MERGED_PATTERN_STATE_STABLE) { merged_pattern->current_state = MERGED_PATTERN_STATE_REMOVING; merged_pattern_send_remove(merged_pattern); } else { /* otherwise queue the removal */ merged_pattern->next_state = MERGED_PATTERN_STATE_REMOVING; } print_state: DBG("Monitor state: %s -> %s", get_merged_pattern_state_name(merged_pattern->current_state), get_merged_pattern_state_name(merged_pattern->next_state)); } /* Replaces (removes and re-adds) merged pattern, or queues it if busy */ static void merged_pattern_replace( struct adv_monitor_merged_pattern *merged_pattern, const struct rssi_parameters *rssi) { /* If the RSSI are the same then nothing needs to be done, except on * the case where pattern is being removed. In that case, we need to * re-add the pattern. * high_rssi_timeout is purposedly left out in the comparison since * the value is ignored upon submission to kernel. */ if (merged_pattern->rssi.high_rssi == rssi->high_rssi && merged_pattern->rssi.low_rssi == rssi->low_rssi && merged_pattern->rssi.low_rssi_timeout == rssi->low_rssi_timeout && merged_pattern->rssi.sampling_period == rssi->sampling_period && merged_pattern->current_state != MERGED_PATTERN_STATE_REMOVING && merged_pattern->next_state != MERGED_PATTERN_STATE_REMOVING) return; merged_pattern->rssi = *rssi; /* If stable, we can proceed with replacement. */ if (merged_pattern->current_state == MERGED_PATTERN_STATE_STABLE) { /* Replacement is done by first removing, then re-adding */ merged_pattern->current_state = MERGED_PATTERN_STATE_REMOVING; merged_pattern->next_state = MERGED_PATTERN_STATE_ADDING; merged_pattern_send_remove(merged_pattern); } else { /* otherwise queue the replacement */ merged_pattern->next_state = MERGED_PATTERN_STATE_ADDING; } DBG("Monitor state: %s -> %s", get_merged_pattern_state_name(merged_pattern->current_state), get_merged_pattern_state_name(merged_pattern->next_state)); } /* Current_state of merged_pattern is done, proceed to the next_state */ static void merged_pattern_process_next_step( struct adv_monitor_merged_pattern *mp) { if (mp->current_state == MERGED_PATTERN_STATE_STABLE) { btd_error(mp->manager->adapter_id, "Merged pattern invalid current state"); return; } if (mp->current_state == MERGED_PATTERN_STATE_REMOVING) { /* We might need to follow-up with re-adding the pattern */ if (mp->next_state == MERGED_PATTERN_STATE_ADDING) { mp->current_state = MERGED_PATTERN_STATE_ADDING; mp->next_state = MERGED_PATTERN_STATE_STABLE; merged_pattern_send_add(mp); goto print_state; } /* We should never end up with remove-remove sequence */ if (mp->next_state == MERGED_PATTERN_STATE_REMOVING) btd_error(mp->manager->adapter_id, "Merged pattern can't be removed again"); /* No more operations */ mp->current_state = MERGED_PATTERN_STATE_STABLE; mp->next_state = MERGED_PATTERN_STATE_STABLE; goto print_state; } /* current_state == MERGED_PATTERN_STATE_ADDING */ if (mp->next_state == MERGED_PATTERN_STATE_REMOVING) { mp->current_state = MERGED_PATTERN_STATE_REMOVING; mp->next_state = MERGED_PATTERN_STATE_STABLE; merged_pattern_send_remove(mp); goto print_state; } else if (mp->next_state == MERGED_PATTERN_STATE_ADDING) { /* To re-add a just added pattern, we need to remove it first */ mp->current_state = MERGED_PATTERN_STATE_REMOVING; mp->next_state = MERGED_PATTERN_STATE_ADDING; merged_pattern_send_remove(mp); goto print_state; } /* No more operations */ mp->current_state = MERGED_PATTERN_STATE_STABLE; mp->next_state = MERGED_PATTERN_STATE_STABLE; print_state: DBG("Monitor state: %s -> %s", get_merged_pattern_state_name(mp->current_state), get_merged_pattern_state_name(mp->next_state)); } /* Frees a monitor object */ static void monitor_free(struct adv_monitor *monitor) { g_dbus_proxy_unref(monitor->proxy); g_free(monitor->path); queue_destroy(monitor->devices, monitor_device_free); monitor->devices = NULL; free(monitor); } /* Calls Release() method of the remote Adv Monitor */ static void monitor_release(struct adv_monitor *monitor) { /* Release() method on a monitor can be called when - * 1. monitor initialization failed * 2. app calls UnregisterMonitor and monitors held by app are released, * it may or may not be activated at this point * 3. monitor is removed by kernel */ if (monitor->state != MONITOR_STATE_FAILED && monitor->state != MONITOR_STATE_INITED && monitor->state != MONITOR_STATE_ACTIVE && monitor->state != MONITOR_STATE_REMOVED) { return; } DBG("Calling Release() on Adv Monitor of owner %s at path %s", monitor->app->owner, monitor->path); g_dbus_proxy_method_call(monitor->proxy, "Release", NULL, NULL, NULL, NULL); } /* Removes monitor from the merged_pattern. This would result in removing it * from the kernel if there is only one such monitor with that pattern. */ static void monitor_remove(struct adv_monitor *monitor) { struct adv_monitor_app *app = monitor->app; uint16_t adapter_id = app->manager->adapter_id; struct adv_monitor_merged_pattern *merged_pattern; const struct queue_entry *e; struct rssi_parameters rssi; /* Monitor from kernel can be removed when - * 1. monitor object is deleted by app - may or may not be activated * 2. app is destroyed and monitors held by app are marked as released */ if (monitor->state != MONITOR_STATE_INITED && monitor->state != MONITOR_STATE_ACTIVE && monitor->state != MONITOR_STATE_RELEASED) { return; } monitor->state = MONITOR_STATE_REMOVED; if (!monitor->merged_pattern) { btd_error(adapter_id, "Merged_pattern not found when removing monitor"); return; } merged_pattern = monitor->merged_pattern; monitor->merged_pattern = NULL; queue_remove(merged_pattern->monitors, monitor); /* No more monitors - just remove the pattern entirely */ if (queue_length(merged_pattern->monitors) == 0) { merged_pattern_remove(merged_pattern); return; } /* Calculate the merge result of the RSSIs of the monitors with the * same pattern, minus the monitor being removed. */ rssi_unset(&rssi); for (e = queue_get_entries(merged_pattern->monitors); e; e = e->next) { struct adv_monitor *m = e->data; merge_rssi(&rssi, &m->rssi, &rssi); } merged_pattern_replace(merged_pattern, &rssi); } /* Destroys monitor object */ static void monitor_destroy(void *data) { struct adv_monitor *monitor = data; if (!monitor) return; queue_remove(monitor->app->monitors, monitor); monitor_release(monitor); monitor_remove(monitor); monitor_free(monitor); } /* Destroys an app object along with related D-Bus handlers */ static void app_destroy(void *data) { struct adv_monitor_app *app = data; if (!app) return; DBG("Destroy Adv Monitor app %s at path %s", app->owner, app->path); queue_destroy(app->monitors, monitor_destroy); if (app->reg) { app_reply_msg(app, btd_error_failed(app->reg, "Adv Monitor app destroyed")); } if (app->client) { g_dbus_client_set_disconnect_watch(app->client, NULL, NULL); g_dbus_client_set_proxy_handlers(app->client, NULL, NULL, NULL, NULL); g_dbus_client_set_ready_watch(app->client, NULL, NULL); g_dbus_client_unref(app->client); } g_free(app->owner); g_free(app->path); free(app); } /* Updates monitor state to 'released' */ static void monitor_state_released(void *data, void *user_data) { struct adv_monitor *monitor = data; if (!monitor || (monitor->state != MONITOR_STATE_INITED && monitor->state != MONITOR_STATE_ACTIVE)) return; monitor->state = MONITOR_STATE_RELEASED; } /* Updates monitor state to 'active' */ static void monitor_state_active(void *data, void *user_data) { struct adv_monitor *monitor = data; if (!monitor || monitor->state != MONITOR_STATE_INITED) return; monitor->state = MONITOR_STATE_ACTIVE; DBG("Calling Activate() on Adv Monitor of owner %s at path %s", monitor->app->owner, monitor->path); g_dbus_proxy_method_call(monitor->proxy, "Activate", NULL, NULL, NULL, NULL); } /* Handles a D-Bus disconnection event of an app */ static void app_disconnect_cb(DBusConnection *conn, void *user_data) { struct adv_monitor_app *app = user_data; if (!app) { error("Unexpected NULL app object upon app disconnect"); return; } btd_info(app->manager->adapter_id, "Adv Monitor app %s disconnected from D-Bus", app->owner); if (queue_remove(app->manager->apps, app)) { queue_foreach(app->monitors, monitor_state_released, NULL); app_destroy(app); } } /* Handles the ready signal of Adv Monitor app */ static void app_ready_cb(GDBusClient *client, void *user_data) { struct adv_monitor_app *app = user_data; uint16_t adapter_id = app->manager->adapter_id; btd_info(adapter_id, "Path %s reserved for Adv Monitor app %s", app->path, app->owner); app_reply_msg(app, dbus_message_new_method_return(app->reg)); } /* Allocates an Adv Monitor */ static struct adv_monitor *monitor_new(struct adv_monitor_app *app, GDBusProxy *proxy) { struct adv_monitor *monitor; if (!app || !proxy) return NULL; monitor = new0(struct adv_monitor, 1); if (!monitor) return NULL; monitor->app = app; monitor->proxy = g_dbus_proxy_ref(proxy); monitor->path = g_strdup(g_dbus_proxy_get_path(proxy)); monitor->state = MONITOR_STATE_NEW; rssi_unset(&monitor->rssi); monitor->devices = queue_new(); return monitor; } /* Matches a monitor based on its D-Bus path */ static bool monitor_match(const void *a, const void *b) { const GDBusProxy *proxy = b; const struct adv_monitor *monitor = a; if (!proxy || !monitor) return false; if (g_strcmp0(g_dbus_proxy_get_path(proxy), monitor->path) != 0) return false; return true; } /* Retrieves Type from the remote Adv Monitor object, verifies the value and * update the local Adv Monitor */ static bool parse_monitor_type(struct adv_monitor *monitor, const char *path) { DBusMessageIter iter; const struct adv_monitor_type *t; const char *type_str; uint16_t adapter_id = monitor->app->manager->adapter_id; if (!g_dbus_proxy_get_property(monitor->proxy, "Type", &iter)) { btd_error(adapter_id, "Failed to retrieve property Type from the " "Adv Monitor at path %s", path); return false; } if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_STRING) goto failed; dbus_message_iter_get_basic(&iter, &type_str); for (t = supported_types; t->name; t++) { if (strcmp(t->name, type_str) == 0) { monitor->merged_pattern->type = t->type; return true; } } failed: btd_error(adapter_id, "Invalid argument of property Type of the Adv Monitor " "at path %s", path); return false; } /* Retrieves RSSI thresholds and timeouts from the remote Adv Monitor object, * verifies the values and update the local Adv Monitor */ static bool parse_rssi_and_timeout(struct adv_monitor *monitor, const char *path) { DBusMessageIter iter; GDBusProxy *proxy = monitor->proxy; int16_t h_rssi = ADV_MONITOR_UNSET_RSSI; int16_t l_rssi = ADV_MONITOR_UNSET_RSSI; uint16_t h_rssi_timeout = ADV_MONITOR_UNSET_TIMEOUT; uint16_t l_rssi_timeout = ADV_MONITOR_UNSET_TIMEOUT; uint16_t sampling_period = ADV_MONITOR_UNSET_SAMPLING_PERIOD; uint16_t adapter_id = monitor->app->manager->adapter_id; /* Extract RSSIHighThreshold */ if (g_dbus_proxy_get_property(proxy, "RSSIHighThreshold", &iter)) { if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_INT16) goto failed; dbus_message_iter_get_basic(&iter, &h_rssi); } /* Extract RSSIHighTimeout */ if (g_dbus_proxy_get_property(proxy, "RSSIHighTimeout", &iter)) { if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_UINT16) goto failed; dbus_message_iter_get_basic(&iter, &h_rssi_timeout); } /* Extract RSSILowThreshold */ if (g_dbus_proxy_get_property(proxy, "RSSILowThreshold", &iter)) { if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_INT16) goto failed; dbus_message_iter_get_basic(&iter, &l_rssi); } /* Extract RSSILowTimeout */ if (g_dbus_proxy_get_property(proxy, "RSSILowTimeout", &iter)) { if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_UINT16) goto failed; dbus_message_iter_get_basic(&iter, &l_rssi_timeout); } /* Extract RSSISamplingPeriod */ if (g_dbus_proxy_get_property(proxy, "RSSISamplingPeriod", &iter)) { if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_UINT16) goto failed; dbus_message_iter_get_basic(&iter, &sampling_period); } /* Verify the values of RSSIs and their timeouts. All fields should be * either set to the unset values or are set within valid ranges. * If the fields are only partially set, we would try our best to fill * in with some sane values. */ if (h_rssi == ADV_MONITOR_UNSET_RSSI && l_rssi == ADV_MONITOR_UNSET_RSSI && h_rssi_timeout == ADV_MONITOR_UNSET_TIMEOUT && l_rssi_timeout == ADV_MONITOR_UNSET_TIMEOUT && sampling_period == ADV_MONITOR_UNSET_SAMPLING_PERIOD) { goto done; } if (l_rssi == ADV_MONITOR_UNSET_RSSI) l_rssi = ADV_MONITOR_MIN_RSSI; if (h_rssi == ADV_MONITOR_UNSET_RSSI) h_rssi = l_rssi; if (l_rssi_timeout == ADV_MONITOR_UNSET_TIMEOUT) l_rssi_timeout = ADV_MONITOR_DEFAULT_LOW_TIMEOUT; if (h_rssi_timeout == ADV_MONITOR_UNSET_TIMEOUT) h_rssi_timeout = ADV_MONITOR_DEFAULT_HIGH_TIMEOUT; if (sampling_period == ADV_MONITOR_UNSET_SAMPLING_PERIOD) sampling_period = btd_opts.advmon.rssi_sampling_period; if (h_rssi < ADV_MONITOR_MIN_RSSI || h_rssi > ADV_MONITOR_MAX_RSSI || l_rssi < ADV_MONITOR_MIN_RSSI || l_rssi > ADV_MONITOR_MAX_RSSI || h_rssi < l_rssi) { goto failed; } if (h_rssi_timeout < ADV_MONITOR_MIN_TIMEOUT || h_rssi_timeout > ADV_MONITOR_MAX_TIMEOUT || l_rssi_timeout < ADV_MONITOR_MIN_TIMEOUT || l_rssi_timeout > ADV_MONITOR_MAX_TIMEOUT) { goto failed; } if (sampling_period > ADV_MONITOR_MAX_SAMPLING_PERIOD) goto failed; monitor->rssi.high_rssi = h_rssi; monitor->rssi.low_rssi = l_rssi; monitor->rssi.high_rssi_timeout = h_rssi_timeout; monitor->rssi.low_rssi_timeout = l_rssi_timeout; monitor->rssi.sampling_period = sampling_period; done: DBG("Adv Monitor at %s initiated with high RSSI threshold %d, high " "RSSI threshold timeout %d, low RSSI threshold %d, low RSSI " "threshold timeout %d, sampling period %d", path, monitor->rssi.high_rssi, monitor->rssi.high_rssi_timeout, monitor->rssi.low_rssi, monitor->rssi.low_rssi_timeout, monitor->rssi.sampling_period); monitor->merged_pattern->rssi = monitor->rssi; return true; failed: btd_error(adapter_id, "Invalid argument of RSSI thresholds and timeouts " "of the Adv Monitor at path %s", path); return false; } /* Retrieves Patterns from the remote Adv Monitor object, verifies the values * and update the local Adv Monitor */ static bool parse_patterns(struct adv_monitor *monitor, const char *path) { DBusMessageIter array, array_iter; uint16_t adapter_id = monitor->app->manager->adapter_id; if (!g_dbus_proxy_get_property(monitor->proxy, "Patterns", &array)) { btd_error(adapter_id, "Failed to retrieve property Patterns from the " "Adv Monitor at path %s", path); return false; } if (dbus_message_iter_get_arg_type(&array) != DBUS_TYPE_ARRAY || dbus_message_iter_get_element_type(&array) != DBUS_TYPE_STRUCT) { goto failed; } monitor->merged_pattern->patterns = queue_new(); dbus_message_iter_recurse(&array, &array_iter); while (dbus_message_iter_get_arg_type(&array_iter) == DBUS_TYPE_STRUCT) { int value_len; uint8_t *value; uint8_t offset, ad_type; struct bt_ad_pattern *pattern; DBusMessageIter struct_iter, value_iter; dbus_message_iter_recurse(&array_iter, &struct_iter); // Extract start position if (dbus_message_iter_get_arg_type(&struct_iter) != DBUS_TYPE_BYTE) { goto failed; } dbus_message_iter_get_basic(&struct_iter, &offset); if (!dbus_message_iter_next(&struct_iter)) goto failed; // Extract AD data type if (dbus_message_iter_get_arg_type(&struct_iter) != DBUS_TYPE_BYTE) { goto failed; } dbus_message_iter_get_basic(&struct_iter, &ad_type); if (!dbus_message_iter_next(&struct_iter)) goto failed; // Extract value of a pattern if (dbus_message_iter_get_arg_type(&struct_iter) != DBUS_TYPE_ARRAY) { goto failed; } dbus_message_iter_recurse(&struct_iter, &value_iter); dbus_message_iter_get_fixed_array(&value_iter, &value, &value_len); pattern = bt_ad_pattern_new(ad_type, offset, value_len, value); if (!pattern) goto failed; queue_push_tail(monitor->merged_pattern->patterns, pattern); dbus_message_iter_next(&array_iter); } /* There must be at least one pattern. */ if (queue_isempty(monitor->merged_pattern->patterns)) goto failed; return true; failed: btd_error(adapter_id, "Invalid argument of property Patterns of the " "Adv Monitor at path %s", path); return false; } /* Processes the content of the remote Adv Monitor */ static bool monitor_process(struct adv_monitor *monitor) { const char *path = g_dbus_proxy_get_path(monitor->proxy); monitor->state = MONITOR_STATE_FAILED; monitor->merged_pattern = malloc0(sizeof(*monitor->merged_pattern)); monitor->merged_pattern->current_state = MERGED_PATTERN_STATE_STABLE; monitor->merged_pattern->next_state = MERGED_PATTERN_STATE_STABLE; if (!parse_monitor_type(monitor, path)) goto fail; if (!parse_rssi_and_timeout(monitor, path)) goto fail; if (monitor->merged_pattern->type != MONITOR_TYPE_OR_PATTERNS || !parse_patterns(monitor, path)) goto fail; monitor->state = MONITOR_STATE_INITED; monitor->merged_pattern->monitors = queue_new(); queue_push_tail(monitor->merged_pattern->monitors, monitor); return true; fail: merged_pattern_free(monitor->merged_pattern); monitor->merged_pattern = NULL; return false; } static void merged_pattern_destroy_monitors( struct adv_monitor_merged_pattern *merged_pattern) { const struct queue_entry *e; for (e = queue_get_entries(merged_pattern->monitors); e; e = e->next) { struct adv_monitor *monitor = e->data; monitor->merged_pattern = NULL; monitor_destroy(monitor); } } /* Handles the callback of Remove Adv Monitor command */ static void remove_adv_monitor_cb(uint8_t status, uint16_t length, const void *param, void *user_data) { const struct mgmt_rp_remove_adv_monitor *rp = param; struct adv_monitor_merged_pattern *merged_pattern = user_data; if (status != MGMT_STATUS_SUCCESS || !param) { error("Failed to Remove Adv Monitor with status 0x%02x", status); goto fail; } if (length < sizeof(*rp)) { error("Wrong size of Remove Adv Monitor response"); goto fail; } DBG("Adv monitor with handle:0x%04x removed from kernel", le16_to_cpu(rp->monitor_handle)); merged_pattern_process_next_step(merged_pattern); if (merged_pattern->current_state == MERGED_PATTERN_STATE_STABLE) merged_pattern_free(merged_pattern); return; fail: merged_pattern_destroy_monitors(merged_pattern); merged_pattern_free(merged_pattern); } /* sends MGMT_OP_REMOVE_ADV_MONITOR */ static void merged_pattern_send_remove( struct adv_monitor_merged_pattern *merged_pattern) { struct mgmt_cp_remove_adv_monitor cp; struct btd_adv_monitor_manager *manager = merged_pattern->manager; cp.monitor_handle = cpu_to_le16(merged_pattern->monitor_handle); if (!mgmt_send(manager->mgmt, MGMT_OP_REMOVE_ADV_MONITOR, manager->adapter_id, sizeof(cp), &cp, remove_adv_monitor_cb, merged_pattern, NULL)) { btd_error(merged_pattern->manager->adapter_id, "Unable to send Remove Advt Monitor command"); } } /* Handles the callback of Add Adv Patterns Monitor command */ static void add_adv_patterns_monitor_cb(uint8_t status, uint16_t length, const void *param, void *user_data) { const struct mgmt_rp_add_adv_patterns_monitor *rp = param; struct adv_monitor_merged_pattern *merged_pattern = user_data; uint16_t adapter_id = merged_pattern->manager->adapter_id; if (status != MGMT_STATUS_SUCCESS || !param) { btd_error(adapter_id, "Failed to Add Adv Patterns Monitor with status" " 0x%02x", status); goto fail; } if (length < sizeof(*rp)) { btd_error(adapter_id, "Wrong size of Add Adv Patterns Monitor " "response"); goto fail; } merged_pattern->monitor_handle = le16_to_cpu(rp->monitor_handle); DBG("Adv monitor with handle:0x%04x added", merged_pattern->monitor_handle); merged_pattern_process_next_step(merged_pattern); if (merged_pattern->current_state != MERGED_PATTERN_STATE_STABLE) return; queue_foreach(merged_pattern->monitors, monitor_state_active, NULL); return; fail: merged_pattern_destroy_monitors(merged_pattern); merged_pattern_free(merged_pattern); } /* sends MGMT_OP_ADD_ADV_PATTERNS_MONITOR */ static bool merged_pattern_send_add_pattern( struct adv_monitor_merged_pattern *merged_pattern) { struct mgmt_cp_add_adv_monitor *cp = NULL; uint8_t pattern_count, cp_len; const struct queue_entry *e; bool success = true; pattern_count = queue_length(merged_pattern->patterns); cp_len = sizeof(*cp) + pattern_count * sizeof(struct mgmt_adv_pattern); cp = malloc0(cp_len); if (!cp) return false; for (e = queue_get_entries(merged_pattern->patterns); e; e = e->next) { struct bt_ad_pattern *pattern = e->data; memcpy(&cp->patterns[cp->pattern_count++], pattern, sizeof(*pattern)); } if (!mgmt_send(merged_pattern->manager->mgmt, MGMT_OP_ADD_ADV_PATTERNS_MONITOR, merged_pattern->manager->adapter_id, cp_len, cp, add_adv_patterns_monitor_cb, merged_pattern, NULL)) { error("Unable to send Add Adv Patterns Monitor command"); success = false; } free(cp); return success; } /* sends MGMT_OP_ADD_ADV_PATTERNS_MONITOR_RSSI */ static bool merged_pattern_send_add_pattern_rssi( struct adv_monitor_merged_pattern *merged_pattern) { struct mgmt_cp_add_adv_patterns_monitor_rssi *cp = NULL; uint8_t pattern_count, cp_len; const struct queue_entry *e; bool success = true; pattern_count = queue_length(merged_pattern->patterns); cp_len = sizeof(*cp) + pattern_count * sizeof(struct mgmt_adv_pattern); cp = malloc0(cp_len); if (!cp) return false; cp->rssi.high_threshold = merged_pattern->rssi.high_rssi; /* High threshold timeout is unsupported in kernel. Value must be 0. */ cp->rssi.high_threshold_timeout = 0; cp->rssi.low_threshold = merged_pattern->rssi.low_rssi; cp->rssi.low_threshold_timeout = htobs(merged_pattern->rssi.low_rssi_timeout); cp->rssi.sampling_period = merged_pattern->rssi.sampling_period; for (e = queue_get_entries(merged_pattern->patterns); e; e = e->next) { struct bt_ad_pattern *pattern = e->data; memcpy(&cp->patterns[cp->pattern_count++], pattern, sizeof(*pattern)); } if (!mgmt_send(merged_pattern->manager->mgmt, MGMT_OP_ADD_ADV_PATTERNS_MONITOR_RSSI, merged_pattern->manager->adapter_id, cp_len, cp, add_adv_patterns_monitor_cb, merged_pattern, NULL)) { error("Unable to send Add Adv Patterns Monitor RSSI command"); success = false; } free(cp); return success; } /* Sends mgmt command to kernel for adding monitor */ static void merged_pattern_send_add( struct adv_monitor_merged_pattern *merged_pattern) { if (rssi_is_unset(&merged_pattern->rssi)) merged_pattern_send_add_pattern(merged_pattern); else merged_pattern_send_add_pattern_rssi(merged_pattern); } /* Handles an Adv Monitor D-Bus proxy added event */ static void monitor_proxy_added_cb(GDBusProxy *proxy, void *user_data) { struct adv_monitor *monitor; struct adv_monitor_app *app = user_data; struct adv_monitor_merged_pattern *existing_pattern; uint16_t adapter_id = app->manager->adapter_id; const char *path = g_dbus_proxy_get_path(proxy); const char *iface = g_dbus_proxy_get_interface(proxy); struct rssi_parameters rssi; if (strcmp(iface, ADV_MONITOR_INTERFACE) != 0 || !g_str_has_prefix(path, app->path)) { return; } if (queue_find(app->monitors, monitor_match, proxy)) { btd_error(adapter_id, "Adv Monitor proxy already exists with path %s", path); return; } monitor = monitor_new(app, proxy); if (!monitor) { btd_error(adapter_id, "Failed to allocate an Adv Monitor for the " "object at %s", path); return; } if (!monitor_process(monitor)) { monitor_destroy(monitor); DBG("Adv Monitor at path %s released due to invalid content", path); return; } queue_push_tail(app->monitors, monitor); existing_pattern = queue_find(monitor->app->manager->merged_patterns, merged_pattern_is_equal, monitor->merged_pattern); if (!existing_pattern) { monitor->merged_pattern->manager = monitor->app->manager; queue_push_tail(monitor->app->manager->merged_patterns, monitor->merged_pattern); merged_pattern_add(monitor->merged_pattern); } else { /* Since there is a matching pattern, abandon the one we have */ merged_pattern_free(monitor->merged_pattern); monitor->merged_pattern = existing_pattern; queue_push_tail(existing_pattern->monitors, monitor); merge_rssi(&existing_pattern->rssi, &monitor->rssi, &rssi); merged_pattern_replace(existing_pattern, &rssi); /* Stable means request is not forwarded to kernel */ if (existing_pattern->current_state == MERGED_PATTERN_STATE_STABLE) monitor_state_active(monitor, NULL); } DBG("Adv Monitor allocated for the object at path %s", path); } /* Handles the removal of an Adv Monitor D-Bus proxy */ static void monitor_proxy_removed_cb(GDBusProxy *proxy, void *user_data) { struct adv_monitor *monitor; struct adv_monitor_app *app = user_data; monitor = queue_find(app->monitors, monitor_match, proxy); if (!monitor) return; DBG("Adv Monitor removed in state %02x with path %s", monitor->state, monitor->path); monitor_state_released(monitor, NULL); monitor_destroy(monitor); } /* Creates an app object, initiates it and sets D-Bus event handlers */ static struct adv_monitor_app *app_create(DBusConnection *conn, DBusMessage *msg, const char *sender, const char *path, struct btd_adv_monitor_manager *manager) { struct adv_monitor_app *app; if (!path || !sender || !manager) return NULL; app = new0(struct adv_monitor_app, 1); if (!app) return NULL; app->owner = g_strdup(sender); app->path = g_strdup(path); app->manager = manager; app->reg = NULL; app->client = g_dbus_client_new_full(conn, sender, path, path); if (!app->client) { app_destroy(app); return NULL; } app->monitors = queue_new(); app->reg = dbus_message_ref(msg); g_dbus_client_set_disconnect_watch(app->client, app_disconnect_cb, app); /* Note that any property changes on a monitor object would not affect * the content of the corresponding monitor. */ g_dbus_client_set_proxy_handlers(app->client, monitor_proxy_added_cb, monitor_proxy_removed_cb, NULL, app); g_dbus_client_set_ready_watch(app->client, app_ready_cb, app); return app; } /* Matches an app based on its owner and path */ static bool app_match(const void *a, const void *b) { const struct adv_monitor_app *app = a; const struct app_match_data *match = b; if (match->owner && strcmp(app->owner, match->owner)) return false; if (match->path && strcmp(app->path, match->path)) return false; return true; } /* Handles a RegisterMonitor D-Bus call */ static DBusMessage *register_monitor(DBusConnection *conn, DBusMessage *msg, void *user_data) { DBusMessageIter args; struct app_match_data match; struct adv_monitor_app *app; struct btd_adv_monitor_manager *manager = user_data; if (!dbus_message_iter_init(msg, &args)) return btd_error_invalid_args(msg); if (dbus_message_iter_get_arg_type(&args) != DBUS_TYPE_OBJECT_PATH) return btd_error_invalid_args(msg); dbus_message_iter_get_basic(&args, &match.path); if (!strlen(match.path) || !g_str_has_prefix(match.path, "/")) return btd_error_invalid_args(msg); match.owner = dbus_message_get_sender(msg); if (queue_find(manager->apps, app_match, &match)) return btd_error_already_exists(msg); app = app_create(conn, msg, match.owner, match.path, manager); if (!app) { btd_error(manager->adapter_id, "Failed to reserve %s for Adv Monitor app %s", match.path, match.owner); return btd_error_failed(msg, "Failed to create Adv Monitor app"); } queue_push_tail(manager->apps, app); return NULL; } /* Handles UnregisterMonitor D-Bus call */ static DBusMessage *unregister_monitor(DBusConnection *conn, DBusMessage *msg, void *user_data) { DBusMessageIter args; struct app_match_data match; struct adv_monitor_app *app; struct btd_adv_monitor_manager *manager = user_data; if (!dbus_message_iter_init(msg, &args)) return btd_error_invalid_args(msg); if (dbus_message_iter_get_arg_type(&args) != DBUS_TYPE_OBJECT_PATH) return btd_error_invalid_args(msg); dbus_message_iter_get_basic(&args, &match.path); if (!strlen(match.path) || !g_str_has_prefix(match.path, "/")) return btd_error_invalid_args(msg); match.owner = dbus_message_get_sender(msg); app = queue_find(manager->apps, app_match, &match); if (!app) return btd_error_does_not_exist(msg); queue_remove(manager->apps, app); app_destroy(app); btd_info(manager->adapter_id, "Path %s removed along with Adv Monitor app %s", match.path, match.owner); return dbus_message_new_method_return(msg); } static const GDBusMethodTable adv_monitor_methods[] = { { GDBUS_EXPERIMENTAL_ASYNC_METHOD("RegisterMonitor", GDBUS_ARGS({ "application", "o" }), NULL, register_monitor) }, { GDBUS_EXPERIMENTAL_ASYNC_METHOD("UnregisterMonitor", GDBUS_ARGS({ "application", "o" }), NULL, unregister_monitor) }, { } }; /* Gets SupportedMonitorTypes property */ static gboolean get_supported_monitor_types(const GDBusPropertyTable *property, DBusMessageIter *iter, void *data) { DBusMessageIter entry; const struct adv_monitor_type *t; dbus_message_iter_open_container(iter, DBUS_TYPE_ARRAY, DBUS_TYPE_STRING_AS_STRING, &entry); for (t = supported_types; t->name; t++) { dbus_message_iter_append_basic(&entry, DBUS_TYPE_STRING, &t->name); } dbus_message_iter_close_container(iter, &entry); return TRUE; } const struct adv_monitor_feature { uint32_t mask; const char *name; } supported_features[] = { { MGMT_ADV_MONITOR_FEATURE_MASK_OR_PATTERNS, "controller-patterns" }, { } }; /* Gets SupportedFeatures property */ static gboolean get_supported_features(const GDBusPropertyTable *property, DBusMessageIter *iter, void *data) { DBusMessageIter entry; const struct adv_monitor_feature *f; struct btd_adv_monitor_manager *manager = data; dbus_message_iter_open_container(iter, DBUS_TYPE_ARRAY, DBUS_TYPE_STRING_AS_STRING, &entry); for (f = supported_features; f->name; f++) { if (manager->supported_features & f->mask) { dbus_message_iter_append_basic(&entry, DBUS_TYPE_STRING, &f->name); } } dbus_message_iter_close_container(iter, &entry); return TRUE; } static const GDBusPropertyTable adv_monitor_properties[] = { {"SupportedMonitorTypes", "as", get_supported_monitor_types, NULL, NULL, G_DBUS_PROPERTY_FLAG_EXPERIMENTAL}, {"SupportedFeatures", "as", get_supported_features, NULL, NULL, G_DBUS_PROPERTY_FLAG_EXPERIMENTAL}, { } }; /* Updates monitor state to 'removed' */ static void monitor_state_removed(void *data, void *user_data) { struct adv_monitor *monitor = data; if (!monitor || (monitor->state != MONITOR_STATE_INITED && monitor->state != MONITOR_STATE_ACTIVE)) return; monitor->state = MONITOR_STATE_REMOVED; monitor->merged_pattern = NULL; } /* Remove the matched merged_pattern and remove the monitors */ static void remove_merged_pattern(void *data, void *user_data) { struct adv_monitor_merged_pattern *merged_pattern = data; uint16_t *handle = user_data; if (!handle) return; /* handle = 0 indicates kernel has removed all monitors */ if (handle != 0 && *handle != merged_pattern->monitor_handle) return; DBG("Adv monitor with handle:0x%04x removed by kernel", merged_pattern->monitor_handle); queue_foreach(merged_pattern->monitors, monitor_state_removed, NULL); queue_destroy(merged_pattern->monitors, monitor_destroy); merged_pattern_free(merged_pattern); } /* Processes Adv Monitor removed event from kernel */ static void adv_monitor_removed_callback(uint16_t index, uint16_t length, const void *param, void *user_data) { struct btd_adv_monitor_manager *manager = user_data; const struct mgmt_ev_adv_monitor_removed *ev = param; uint16_t handle = ev->monitor_handle; const uint16_t adapter_id = manager->adapter_id; if (length < sizeof(*ev)) { btd_error(adapter_id, "Wrong size of Adv Monitor Removed event"); return; } /* Traverse the merged_patterns to find matching pattern */ queue_foreach(manager->merged_patterns, remove_merged_pattern, &handle); DBG("Adv Monitor removed event with handle 0x%04x processed", ev->monitor_handle); } /* Includes found/lost device's object path into the dbus message */ static void report_device_state_setup(DBusMessageIter *iter, void *user_data) { const char *path = device_get_path(user_data); dbus_message_iter_append_basic(iter, DBUS_TYPE_OBJECT_PATH, &path); } /* Invokes DeviceFound on the matched monitor */ static void notify_device_found_per_monitor(void *data, void *user_data) { struct adv_monitor *monitor = data; struct monitored_device_info *info = user_data; if (monitor->merged_pattern->monitor_handle == info->monitor_handle) { DBG("Calling DeviceFound() on Adv Monitor of owner %s " "at path %s", monitor->app->owner, monitor->path); g_dbus_proxy_method_call(monitor->proxy, "DeviceFound", report_device_state_setup, NULL, info->device, NULL); } } /* Checks all monitors for match in the app to invoke DeviceFound */ static void notify_device_found_per_app(void *data, void *user_data) { struct adv_monitor_app *app = data; queue_foreach(app->monitors, notify_device_found_per_monitor, user_data); } /* Processes Adv Monitor Device Found event from kernel */ static void adv_monitor_device_found_callback(uint16_t index, uint16_t length, const void *param, void *user_data) { const struct mgmt_ev_adv_monitor_device_found *ev = param; struct btd_adv_monitor_manager *manager = user_data; const uint16_t adapter_id = manager->adapter_id; struct btd_adapter *adapter = manager->adapter; uint16_t handle = le16_to_cpu(ev->monitor_handle); struct monitored_device_info info; const uint8_t *ad_data = NULL; uint16_t ad_data_len; uint32_t flags; char addr[18]; if (length < sizeof(*ev)) { btd_error(adapter_id, "Too short Adv Monitor Device Found event"); return; } ad_data_len = btohs(ev->ad_data_len); if (length != sizeof(*ev) + ad_data_len) { btd_error(adapter_id, "Wrong size of Adv Monitor Device Found event"); return; } if (ad_data_len > 0) ad_data = ev->ad_data; flags = le32_to_cpu(ev->flags); ba2str(&ev->addr.bdaddr, addr); DBG("hci%u addr %s, rssi %d flags 0x%04x ad_data_len %u", index, addr, ev->rssi, flags, ad_data_len); btd_adapter_device_found(adapter, &ev->addr.bdaddr, ev->addr.type, ev->rssi, flags, ad_data, ad_data_len, true); if (handle) { DBG("Adv Monitor with handle 0x%04x started tracking " "the device %s", handle, addr); info.device = btd_adapter_find_device(adapter, &ev->addr.bdaddr, ev->addr.type); if (!info.device) { btd_error(adapter_id, "Device object not found for %s", addr); return; } /* Check for matched monitor in all apps */ info.monitor_handle = handle; queue_foreach(manager->apps, notify_device_found_per_app, &info); } } /* Invokes DeviceLost on the matched monitor */ static void notify_device_lost_per_monitor(void *data, void *user_data) { struct adv_monitor *monitor = data; struct monitored_device_info *info = user_data; if (monitor->merged_pattern->monitor_handle == info->monitor_handle) { DBG("Calling DeviceLost() on Adv Monitor of owner %s " "at path %s", monitor->app->owner, monitor->path); g_dbus_proxy_method_call(monitor->proxy, "DeviceLost", report_device_state_setup, NULL, info->device, NULL); } } /* Checks all monitors for match in the app to invoke DeviceLost */ static void notify_device_lost_per_app(void *data, void *user_data) { struct adv_monitor_app *app = data; queue_foreach(app->monitors, notify_device_lost_per_monitor, user_data); } /* Processes Adv Monitor Device Lost event from kernel */ static void adv_monitor_device_lost_callback(uint16_t index, uint16_t length, const void *param, void *user_data) { struct btd_adv_monitor_manager *manager = user_data; const struct mgmt_ev_adv_monitor_device_lost *ev = param; uint16_t handle = le16_to_cpu(ev->monitor_handle); const uint16_t adapter_id = manager->adapter_id; struct btd_adapter *adapter = manager->adapter; struct monitored_device_info info; char addr[18]; if (length < sizeof(*ev)) { btd_error(adapter_id, "Wrong size of Adv Monitor Device Lost event"); return; } ba2str(&ev->addr.bdaddr, addr); DBG("Adv Monitor with handle 0x%04x stopped tracking the device %s", handle, addr); info.device = btd_adapter_find_device(adapter, &ev->addr.bdaddr, ev->addr.type); if (!info.device) { btd_error(adapter_id, "Device object not found for %s", addr); return; } /* Check for matched monitor in all apps */ info.monitor_handle = handle; queue_foreach(manager->apps, notify_device_lost_per_app, &info); } /* Allocates a manager object */ static struct btd_adv_monitor_manager *manager_new( struct btd_adapter *adapter, struct mgmt *mgmt) { struct btd_adv_monitor_manager *manager; if (!adapter || !mgmt) return NULL; manager = new0(struct btd_adv_monitor_manager, 1); if (!manager) return NULL; manager->adapter = adapter; manager->mgmt = mgmt_ref(mgmt); manager->adapter_id = btd_adapter_get_index(adapter); manager->apps = queue_new(); manager->merged_patterns = queue_new(); mgmt_register(manager->mgmt, MGMT_EV_ADV_MONITOR_REMOVED, manager->adapter_id, adv_monitor_removed_callback, manager, NULL); mgmt_register(manager->mgmt, MGMT_EV_ADV_MONITOR_DEVICE_FOUND, manager->adapter_id, adv_monitor_device_found_callback, manager, NULL); mgmt_register(manager->mgmt, MGMT_EV_ADV_MONITOR_DEVICE_LOST, manager->adapter_id, adv_monitor_device_lost_callback, manager, NULL); return manager; } /* Frees a manager object */ static void manager_free(struct btd_adv_monitor_manager *manager) { mgmt_unref(manager->mgmt); queue_destroy(manager->apps, app_destroy); queue_destroy(manager->merged_patterns, merged_pattern_free); free(manager); } /* Destroys a manager object and unregisters its D-Bus interface */ static void manager_destroy(struct btd_adv_monitor_manager *manager) { if (!manager) return; g_dbus_unregister_interface(btd_get_dbus_connection(), adapter_get_path(manager->adapter), ADV_MONITOR_MGR_INTERFACE); manager_free(manager); } /* Initiates manager's members based on the return of * MGMT_OP_READ_ADV_MONITOR_FEATURES */ static void read_adv_monitor_features_cb(uint8_t status, uint16_t length, const void *param, void *user_data) { const struct mgmt_rp_read_adv_monitor_features *rp = param; struct btd_adv_monitor_manager *manager = user_data; if (status != MGMT_STATUS_SUCCESS || !param) { btd_error(manager->adapter_id, "Failed to Read Adv Monitor Features with " "status 0x%02x", status); return; } if (length < sizeof(*rp)) { btd_error(manager->adapter_id, "Wrong size of Read Adv Monitor Features " "response"); return; } manager->supported_features = le32_to_cpu(rp->supported_features); manager->enabled_features = le32_to_cpu(rp->enabled_features); manager->max_num_monitors = le16_to_cpu(rp->max_num_handles); manager->max_num_patterns = rp->max_num_patterns; btd_info(manager->adapter_id, "Adv Monitor Manager created with " "supported features:0x%08x, enabled features:0x%08x, " "max number of supported monitors:%d, " "max number of supported patterns:%d", manager->supported_features, manager->enabled_features, manager->max_num_monitors, manager->max_num_patterns); } /* Creates a manager and registers its D-Bus interface */ struct btd_adv_monitor_manager *btd_adv_monitor_manager_create( struct btd_adapter *adapter, struct mgmt *mgmt) { struct btd_adv_monitor_manager *manager; manager = manager_new(adapter, mgmt); if (!manager) return NULL; if (!g_dbus_register_interface(btd_get_dbus_connection(), adapter_get_path(manager->adapter), ADV_MONITOR_MGR_INTERFACE, adv_monitor_methods, NULL, adv_monitor_properties, manager, NULL)) { btd_error(manager->adapter_id, "Failed to register " ADV_MONITOR_MGR_INTERFACE); manager_free(manager); return NULL; } if (!mgmt_send(manager->mgmt, MGMT_OP_READ_ADV_MONITOR_FEATURES, manager->adapter_id, 0, NULL, read_adv_monitor_features_cb, manager, NULL)) { btd_error(manager->adapter_id, "Failed to send Read Adv Monitor Features"); manager_destroy(manager); return NULL; } return manager; } /* Destroys a manager and unregisters its D-Bus interface */ void btd_adv_monitor_manager_destroy(struct btd_adv_monitor_manager *manager) { if (!manager) return; btd_info(manager->adapter_id, "Destroy Adv Monitor Manager"); manager_destroy(manager); } bool btd_adv_monitor_offload_enabled(struct btd_adv_monitor_manager *manager) { if (!manager) return false; return !!(manager->enabled_features & MGMT_ADV_MONITOR_FEATURE_MASK_OR_PATTERNS); } /* Processes the content matching based pattern(s) of a monitor */ static void adv_match_per_monitor(void *data, void *user_data) { struct adv_monitor *monitor = data; struct adv_content_filter_info *info = user_data; struct queue *patterns; if (!monitor) { error("Unexpected NULL adv_monitor object upon match"); return; } if (monitor->state != MONITOR_STATE_ACTIVE) return; if (!monitor->merged_pattern) return; patterns = monitor->merged_pattern->patterns; if (monitor->merged_pattern->type == MONITOR_TYPE_OR_PATTERNS && bt_ad_pattern_match(info->ad, patterns)) { goto matched; } return; matched: if (!info->matched_monitors) info->matched_monitors = queue_new(); queue_push_tail(info->matched_monitors, monitor); } /* Processes the content matching for the monitor(s) of an app */ static void adv_match_per_app(void *data, void *user_data) { struct adv_monitor_app *app = data; if (!app) { error("Unexpected NULL adv_monitor_app object upon match"); return; } queue_foreach(app->monitors, adv_match_per_monitor, user_data); } /* Processes the content matching for every app without RSSI filtering and * notifying monitors. The caller is responsible of releasing the memory of the * list but not the ad data. * Returns the list of monitors whose content match the ad data. */ struct queue *btd_adv_monitor_content_filter( struct btd_adv_monitor_manager *manager, struct bt_ad *ad) { struct adv_content_filter_info info; if (!manager || !ad) return NULL; info.ad = ad; info.matched_monitors = NULL; queue_foreach(manager->apps, adv_match_per_app, &info); return info.matched_monitors; } /* Wraps adv_monitor_filter_rssi() to processes the content-matched monitor with * RSSI filtering and notifies it on device found/lost event */ static void monitor_filter_rssi(void *data, void *user_data) { struct adv_monitor *monitor = data; struct adv_rssi_filter_info *info = user_data; if (!monitor || !info) return; adv_monitor_filter_rssi(monitor, info->device, info->rssi); } /* Processes every content-matched monitor with RSSI filtering and notifies on * device found/lost event. The caller is responsible of releasing the memory * of matched_monitors list but not its data. */ void btd_adv_monitor_notify_monitors(struct btd_adv_monitor_manager *manager, struct btd_device *device, int8_t rssi, struct queue *matched_monitors) { struct adv_rssi_filter_info info; if (!manager || !device || !matched_monitors || queue_isempty(matched_monitors)) { return; } info.device = device; info.rssi = rssi; queue_foreach(matched_monitors, monitor_filter_rssi, &info); } /* Matches a device based on btd_device object */ static bool monitor_device_match(const void *a, const void *b) { const struct adv_monitor_device *dev = a; const struct btd_device *device = b; if (!dev) { error("Unexpected NULL adv_monitor_device object upon match"); return false; } if (dev->device != device) return false; return true; } /* Frees a monitor device object */ static void monitor_device_free(void *data) { struct adv_monitor_device *dev = data; if (!dev) { error("Unexpected NULL adv_monitor_device object upon free"); return; } if (dev->lost_timer) { timeout_remove(dev->lost_timer); dev->lost_timer = 0; } dev->monitor = NULL; dev->device = NULL; free(dev); } /* Removes a device from monitor->devices list */ static void remove_device_from_monitor(void *data, void *user_data) { struct adv_monitor *monitor = data; struct btd_device *device = user_data; struct adv_monitor_device *dev = NULL; if (!monitor) { error("Unexpected NULL adv_monitor object upon device remove"); return; } dev = queue_remove_if(monitor->devices, monitor_device_match, device); if (dev) { DBG("Device removed from the Adv Monitor at path %s", monitor->path); monitor_device_free(dev); } } /* Removes a device from every monitor in an app */ static void remove_device_from_app(void *data, void *user_data) { struct adv_monitor_app *app = data; struct btd_device *device = user_data; if (!app) { error("Unexpected NULL adv_monitor_app object upon device " "remove"); return; } queue_foreach(app->monitors, remove_device_from_monitor, device); } /* Removes a device from every monitor in all apps */ void btd_adv_monitor_device_remove(struct btd_adv_monitor_manager *manager, struct btd_device *device) { if (!manager || !device) return; queue_foreach(manager->apps, remove_device_from_app, device); } /* Creates a device object to track the per-device information */ static struct adv_monitor_device *monitor_device_create( struct adv_monitor *monitor, struct btd_device *device) { struct adv_monitor_device *dev = NULL; dev = new0(struct adv_monitor_device, 1); if (!dev) return NULL; dev->monitor = monitor; dev->device = device; queue_push_tail(monitor->devices, dev); return dev; } /* Handles a situation where the device goes offline/out-of-range */ static bool handle_device_lost_timeout(gpointer user_data) { struct adv_monitor_device *dev = user_data; struct adv_monitor *monitor = dev->monitor; DBG("Device Lost timeout triggered for device %p. Calling DeviceLost() " "on Adv Monitor of owner %s at path %s", dev->device, monitor->app->owner, monitor->path); g_dbus_proxy_method_call(monitor->proxy, "DeviceLost", report_device_state_setup, NULL, dev->device, NULL); dev->lost_timer = 0; dev->found = false; return FALSE; } /* Filters an Adv based on its RSSI value */ static void adv_monitor_filter_rssi(struct adv_monitor *monitor, struct btd_device *device, int8_t rssi) { struct adv_monitor_device *dev = NULL; time_t curr_time = time(NULL); uint16_t adapter_id = monitor->app->manager->adapter_id; /* If the RSSI thresholds and timeouts are not specified, report the * DeviceFound() event without tracking for the RSSI as the Adv has * already matched the pattern filter. */ if (rssi_is_unset(&monitor->rssi)) { DBG("Calling DeviceFound() on Adv Monitor of owner %s " "at path %s", monitor->app->owner, monitor->path); g_dbus_proxy_method_call(monitor->proxy, "DeviceFound", report_device_state_setup, NULL, device, NULL); return; } dev = queue_find(monitor->devices, monitor_device_match, device); if (!dev) { dev = monitor_device_create(monitor, device); if (!dev) { btd_error(adapter_id, "Failed to create Adv Monitor device object."); return; } } if (dev->lost_timer) { timeout_remove(dev->lost_timer); dev->lost_timer = 0; } /* Reset the timings of found/lost if a device has been offline for * longer than the high/low timeouts. */ if (dev->last_seen) { if (difftime(curr_time, dev->last_seen) > monitor->rssi.high_rssi_timeout) { dev->high_rssi_first_seen = 0; } if (difftime(curr_time, dev->last_seen) > monitor->rssi.low_rssi_timeout) { dev->low_rssi_first_seen = 0; } } dev->last_seen = curr_time; /* Check for the found devices (if the device is not already found) */ if (!dev->found && rssi > monitor->rssi.high_rssi) { if (dev->high_rssi_first_seen) { if (difftime(curr_time, dev->high_rssi_first_seen) >= monitor->rssi.high_rssi_timeout) { dev->found = true; DBG("Calling DeviceFound() on Adv Monitor " "of owner %s at path %s", monitor->app->owner, monitor->path); g_dbus_proxy_method_call( monitor->proxy, "DeviceFound", report_device_state_setup, NULL, dev->device, NULL); } } else { dev->high_rssi_first_seen = curr_time; } } else { dev->high_rssi_first_seen = 0; } /* Check for the lost devices (only if the device is already found, as * it doesn't make any sense to report the Device Lost event if the * device is not found yet) */ if (dev->found && rssi < monitor->rssi.low_rssi) { if (dev->low_rssi_first_seen) { if (difftime(curr_time, dev->low_rssi_first_seen) >= monitor->rssi.low_rssi_timeout) { dev->found = false; DBG("Calling DeviceLost() on Adv Monitor " "of owner %s at path %s", monitor->app->owner, monitor->path); g_dbus_proxy_method_call( monitor->proxy, "DeviceLost", report_device_state_setup, NULL, dev->device, NULL); } } else { dev->low_rssi_first_seen = curr_time; } } else { dev->low_rssi_first_seen = 0; } /* Setup a timer to track if the device goes offline/out-of-range, only * if we are tracking for the Low RSSI Threshold. If we are tracking * the High RSSI Threshold, nothing needs to be done. */ if (dev->found) { dev->lost_timer = timeout_add_seconds(monitor->rssi.low_rssi_timeout, handle_device_lost_timeout, dev, NULL); } } /* Clears running DeviceLost timer for a given device */ static void clear_device_lost_timer(void *data, void *user_data) { struct adv_monitor_device *dev = data; struct adv_monitor *monitor = NULL; if (dev->lost_timer) { timeout_remove(dev->lost_timer); dev->lost_timer = 0; monitor = dev->monitor; DBG("Calling DeviceLost() for device %p on Adv Monitor " "of owner %s at path %s", dev->device, monitor->app->owner, monitor->path); g_dbus_proxy_method_call(monitor->proxy, "DeviceLost", report_device_state_setup, NULL, dev->device, NULL); } } /* Clears running DeviceLost timers from each monitor */ static void clear_lost_timers_from_monitor(void *data, void *user_data) { struct adv_monitor *monitor = data; queue_foreach(monitor->devices, clear_device_lost_timer, NULL); } /* Clears running DeviceLost timers from each app */ static void clear_lost_timers_from_app(void *data, void *user_data) { struct adv_monitor_app *app = data; queue_foreach(app->monitors, clear_lost_timers_from_monitor, NULL); } /* Handles bt power down scenario */ void btd_adv_monitor_power_down(struct btd_adv_monitor_manager *manager) { if (!manager) { error("Unexpected NULL btd_adv_monitor_manager object upon " "power down"); return; } /* Clear any running DeviceLost timers in case of power down */ queue_foreach(manager->apps, clear_lost_timers_from_app, NULL); } bluez-5.82/src/PaxHeaders/gatt-database.c0000644000000000000000000000005014772767672015333 xustar0020 atime=1743515579 20 ctime=1743591284 bluez-5.82/src/gatt-database.c0000644000000000000000000031033014772767672015014 0ustar00rootroot// SPDX-License-Identifier: GPL-2.0-or-later /* * * BlueZ - Bluetooth protocol stack for Linux * * Copyright (C) 2015 Google Inc. * * */ #ifdef HAVE_CONFIG_H #include #endif #include #include #include #include #include #include "lib/bluetooth.h" #include "lib/sdp.h" #include "lib/sdp_lib.h" #include "lib/uuid.h" #include "lib/mgmt.h" #include "btio/btio.h" #include "gdbus/gdbus.h" #include "src/shared/util.h" #include "src/shared/queue.h" #include "src/shared/io.h" #include "src/shared/att.h" #include "src/shared/gatt-db.h" #include "src/shared/gatt-server.h" #include "log.h" #include "error.h" #include "btd.h" #include "adapter.h" #include "device.h" #include "gatt-database.h" #include "dbus-common.h" #include "profile.h" #include "service.h" #include "textfile.h" #include "settings.h" #define GATT_MANAGER_IFACE "org.bluez.GattManager1" #define GATT_PROFILE_IFACE "org.bluez.GattProfile1" #define GATT_SERVICE_IFACE "org.bluez.GattService1" #define GATT_CHRC_IFACE "org.bluez.GattCharacteristic1" #define GATT_DESC_IFACE "org.bluez.GattDescriptor1" #define ERROR_FAILED ERROR_INTERFACE ".Failed" #define UUID_GAP 0x1800 #define UUID_GATT 0x1801 #define UUID_DIS 0x180a #ifndef MIN #define MIN(a, b) ((a) < (b) ? (a) : (b)) #endif struct gatt_record { struct btd_gatt_database *database; uint32_t handle; struct gatt_db_attribute *attr; }; struct btd_gatt_database { struct btd_adapter *adapter; struct gatt_db *db; unsigned int db_id; GIOChannel *le_io; GIOChannel *eatt_io; GIOChannel *bredr_io; struct queue *records; struct queue *device_states; struct queue *ccc_callbacks; struct gatt_db_attribute *svc_chngd; struct gatt_db_attribute *svc_chngd_ccc; struct gatt_db_attribute *cli_feat; struct gatt_db_attribute *db_hash; struct gatt_db_attribute *eatt; struct queue *apps; struct queue *profiles; }; struct gatt_app { struct btd_gatt_database *database; char *owner; char *path; DBusMessage *reg; GDBusClient *client; bool failed; struct queue *profiles; struct queue *services; struct queue *proxies; }; struct external_service { struct gatt_app *app; char *path; /* Path to GattService1 */ GDBusProxy *proxy; struct gatt_db_attribute *attrib; uint16_t attr_cnt; struct queue *chrcs; struct queue *descs; struct queue *includes; }; struct external_profile { struct gatt_app *app; GDBusProxy *proxy; struct queue *profiles; /* btd_profile list */ }; struct client_io { struct bt_att *att; struct external_chrc *chrc; unsigned int disconn_id; struct io *io; }; struct external_chrc { struct external_service *service; char *path; GDBusProxy *proxy; uint8_t props; uint8_t ext_props; uint32_t perm; uint32_t ccc_perm; uint16_t mtu; struct queue *write_ios; struct queue *notify_ios; struct gatt_db_attribute *attrib; struct gatt_db_attribute *ccc; struct queue *pending_reads; struct queue *pending_writes; unsigned int ntfy_cnt; bool prep_authorized; bool req_prep_authorization; }; struct external_desc { struct external_service *service; char *chrc_path; GDBusProxy *proxy; uint32_t perm; struct gatt_db_attribute *attrib; bool handled; struct queue *pending_reads; struct queue *pending_writes; bool prep_authorized; bool req_prep_authorization; }; struct pending_op { struct bt_att *att; unsigned int id; unsigned int disconn_id; uint16_t offset; uint8_t link_type; struct gatt_db_attribute *attrib; struct queue *owner_queue; struct iovec data; bool is_characteristic; bool prep_authorize; }; struct notify { struct btd_gatt_database *database; uint16_t handle, ccc_handle; uint8_t *value; uint16_t len; bt_gatt_server_conf_func_t conf; void *user_data; }; #define CLI_FEAT_SIZE 1 struct device_state { struct btd_gatt_database *db; bdaddr_t bdaddr; uint8_t bdaddr_type; unsigned int disc_id; uint8_t cli_feat[CLI_FEAT_SIZE]; bool change_aware; bool out_of_sync; struct queue *ccc_states; struct notify *pending; }; typedef uint8_t (*btd_gatt_database_ccc_write_t) (struct pending_op *op, void *user_data); typedef void (*btd_gatt_database_destroy_t) (void *data); struct ccc_state { uint16_t handle; uint16_t value; }; struct ccc_cb_data { uint16_t handle; btd_gatt_database_ccc_write_t callback; btd_gatt_database_destroy_t destroy; void *user_data; }; struct device_info { bdaddr_t bdaddr; uint8_t bdaddr_type; }; static void ccc_cb_free(void *data) { struct ccc_cb_data *ccc_cb = data; if (ccc_cb->destroy) ccc_cb->destroy(ccc_cb->user_data); free(ccc_cb); } static bool ccc_cb_match_service(const void *data, const void *match_data) { const struct ccc_cb_data *ccc_cb = data; const struct gatt_db_attribute *attrib = match_data; uint16_t start, end; if (!gatt_db_attribute_get_service_handles(attrib, &start, &end)) return false; return ccc_cb->handle >= start && ccc_cb->handle <= end; } static bool ccc_cb_match_handle(const void *data, const void *match_data) { const struct ccc_cb_data *ccc_cb = data; uint16_t handle = PTR_TO_UINT(match_data); return ccc_cb->handle == handle; } static bool dev_state_match(const void *a, const void *b) { const struct device_state *dev_state = a; const struct device_info *dev_info = b; return bacmp(&dev_state->bdaddr, &dev_info->bdaddr) == 0 && dev_state->bdaddr_type == dev_info->bdaddr_type; } static struct device_state * find_device_state(struct btd_gatt_database *database, const bdaddr_t *bdaddr, uint8_t bdaddr_type) { struct device_info dev_info; memset(&dev_info, 0, sizeof(dev_info)); bacpy(&dev_info.bdaddr, bdaddr); dev_info.bdaddr_type = bdaddr_type; return queue_find(database->device_states, dev_state_match, &dev_info); } static bool ccc_state_match(const void *a, const void *b) { const struct ccc_state *ccc = a; uint16_t handle = PTR_TO_UINT(b); return ccc->handle == handle; } static struct ccc_state *find_ccc_state(struct device_state *dev_state, uint16_t handle) { return queue_find(dev_state->ccc_states, ccc_state_match, UINT_TO_PTR(handle)); } static struct device_state *device_state_create(struct btd_gatt_database *db, const bdaddr_t *bdaddr, uint8_t bdaddr_type) { struct device_state *dev_state; dev_state = new0(struct device_state, 1); dev_state->db = db; dev_state->ccc_states = queue_new(); bacpy(&dev_state->bdaddr, bdaddr); dev_state->bdaddr_type = bdaddr_type; return dev_state; } static void device_state_free(void *data) { struct device_state *state = data; queue_destroy(state->ccc_states, free); if (state->pending) { free(state->pending->value); free(state->pending); } free(state); } static void clear_ccc_state(void *data, void *user_data) { struct ccc_state *ccc = data; struct btd_gatt_database *db = user_data; struct ccc_cb_data *ccc_cb; if (!ccc->value) return; ccc_cb = queue_find(db->ccc_callbacks, ccc_cb_match_handle, UINT_TO_PTR(ccc->handle)); if (!ccc_cb) return; if (ccc_cb->callback) ccc_cb->callback(NULL, ccc_cb->user_data); } static void att_disconnected(int err, void *user_data) { struct device_state *state = user_data; struct btd_device *device; DBG(""); state->disc_id = 0; state->out_of_sync = false; device = btd_adapter_find_device(state->db->adapter, &state->bdaddr, state->bdaddr_type); if (!device) goto remove; if (device_is_bonded(device, state->bdaddr_type)) { struct ccc_state *ccc; uint16_t handle; handle = gatt_db_attribute_get_handle(state->db->svc_chngd_ccc); ccc = find_ccc_state(state, handle); if (ccc && ccc->value) device_store_svc_chng_ccc(device, state->bdaddr_type, ccc->value); return; } remove: /* Remove device state if device no longer exists or is not paired */ if (queue_remove(state->db->device_states, state)) { queue_foreach(state->ccc_states, clear_ccc_state, state->db); device_state_free(state); } } static bool get_dst_info(struct bt_att *att, bdaddr_t *dst, uint8_t *dst_type) { GIOChannel *io = NULL; GError *gerr = NULL; int fd; fd = bt_att_get_fd(att); if (fd < 0) return false; io = g_io_channel_unix_new(fd); if (!io) return false; bt_io_get(io, &gerr, BT_IO_OPT_DEST_BDADDR, dst, BT_IO_OPT_DEST_TYPE, dst_type, BT_IO_OPT_INVALID); if (gerr) { error("gatt: bt_io_get: %s", gerr->message); g_error_free(gerr); g_io_channel_unref(io); return false; } g_io_channel_unref(io); return true; } static struct device_state * find_device_state_by_att(struct btd_gatt_database *database, struct bt_att *att) { bdaddr_t bdaddr; uint8_t bdaddr_type; if (!get_dst_info(att, &bdaddr, &bdaddr_type)) return NULL; return find_device_state(database, &bdaddr, bdaddr_type); } static struct device_state *get_device_state(struct btd_gatt_database *database, struct bt_att *att) { struct device_state *dev_state; bdaddr_t bdaddr; uint8_t bdaddr_type; if (!get_dst_info(att, &bdaddr, &bdaddr_type)) return NULL; /* * Find and return a device state. If a matching state doesn't exist, * then create a new one. */ dev_state = find_device_state(database, &bdaddr, bdaddr_type); if (dev_state) goto done; dev_state = device_state_create(database, &bdaddr, bdaddr_type); queue_push_tail(database->device_states, dev_state); done: if (!dev_state->disc_id) dev_state->disc_id = bt_att_register_disconnect(att, att_disconnected, dev_state, NULL); return dev_state; } static struct ccc_state *get_ccc_state(struct btd_gatt_database *database, struct bt_att *att, uint16_t handle) { struct device_state *dev_state; struct ccc_state *ccc; dev_state = get_device_state(database, att); if (!dev_state) return NULL; ccc = find_ccc_state(dev_state, handle); if (ccc) return ccc; ccc = new0(struct ccc_state, 1); ccc->handle = handle; queue_push_tail(dev_state->ccc_states, ccc); return ccc; } static void cancel_pending_read(void *data) { struct pending_op *op = data; gatt_db_attribute_read_result(op->attrib, op->id, BT_ATT_ERROR_REQUEST_NOT_SUPPORTED, NULL, 0); op->owner_queue = NULL; } static void cancel_pending_write(void *data) { struct pending_op *op = data; gatt_db_attribute_write_result(op->attrib, op->id, BT_ATT_ERROR_REQUEST_NOT_SUPPORTED); op->owner_queue = NULL; } static void client_io_free(void *data) { struct client_io *client = data; bt_att_unregister_disconnect(client->att, client->disconn_id); bt_att_unref(client->att); io_destroy(client->io); free(client); } static void chrc_free(void *data) { struct external_chrc *chrc = data; queue_destroy(chrc->write_ios, client_io_free); queue_destroy(chrc->notify_ios, client_io_free); queue_destroy(chrc->pending_reads, cancel_pending_read); queue_destroy(chrc->pending_writes, cancel_pending_write); g_free(chrc->path); g_dbus_proxy_set_property_watch(chrc->proxy, NULL, NULL); g_dbus_proxy_unref(chrc->proxy); free(chrc); } static void desc_free(void *data) { struct external_desc *desc = data; queue_destroy(desc->pending_reads, cancel_pending_read); queue_destroy(desc->pending_writes, cancel_pending_write); g_dbus_proxy_unref(desc->proxy); g_free(desc->chrc_path); free(desc); } static void inc_free(void *data) { struct external_desc *inc = data; free(inc); } static void service_free(void *data) { struct external_service *service = data; queue_destroy(service->chrcs, chrc_free); queue_destroy(service->descs, desc_free); queue_destroy(service->includes, inc_free); if (service->attrib) gatt_db_remove_service(service->app->database->db, service->attrib); if (service->app->client) g_dbus_proxy_unref(service->proxy); g_free(service->path); free(service); } static void profile_remove(void *data) { struct btd_profile *p = data; DBG("Removed \"%s\"", p->name); adapter_foreach(adapter_remove_profile, p); btd_profile_unregister(p); g_free((void *) p->name); g_free((void *) p->remote_uuid); free(p); } static void profile_release(struct external_profile *profile) { DBG("Releasing \"%s\"", profile->app->owner); g_dbus_proxy_method_call(profile->proxy, "Release", NULL, NULL, NULL, NULL); } static void profile_free(void *data) { struct external_profile *profile = data; queue_destroy(profile->profiles, profile_remove); profile_release(profile); g_dbus_proxy_unref(profile->proxy); free(profile); } static void app_free(void *data) { struct gatt_app *app = data; queue_destroy(app->profiles, profile_free); queue_destroy(app->services, service_free); queue_destroy(app->proxies, NULL); if (app->client) { g_dbus_client_set_disconnect_watch(app->client, NULL, NULL); g_dbus_client_set_proxy_handlers(app->client, NULL, NULL, NULL, NULL); g_dbus_client_set_ready_watch(app->client, NULL, NULL); g_dbus_client_unref(app->client); } if (app->reg) dbus_message_unref(app->reg); g_free(app->owner); g_free(app->path); free(app); } static void gatt_record_free(void *data) { struct gatt_record *rec = data; adapter_service_remove(rec->database->adapter, rec->handle); free(rec); } static void gatt_database_free(void *data) { struct btd_gatt_database *database = data; if (database->le_io) { g_io_channel_shutdown(database->le_io, FALSE, NULL); g_io_channel_unref(database->le_io); } if (database->eatt_io) { g_io_channel_shutdown(database->eatt_io, FALSE, NULL); g_io_channel_unref(database->eatt_io); } if (database->bredr_io) { g_io_channel_shutdown(database->bredr_io, FALSE, NULL); g_io_channel_unref(database->bredr_io); } /* TODO: Persistently store CCC states before freeing them */ gatt_db_unregister(database->db, database->db_id); queue_destroy(database->records, gatt_record_free); queue_destroy(database->device_states, device_state_free); queue_destroy(database->apps, app_free); queue_destroy(database->profiles, profile_free); queue_destroy(database->ccc_callbacks, ccc_cb_free); database->device_states = NULL; database->ccc_callbacks = NULL; gatt_db_unref(database->db); btd_adapter_unref(database->adapter); free(database); } static void connect_cb(GIOChannel *io, GError *gerr, gpointer user_data) { struct btd_adapter *adapter; struct btd_device *device; uint8_t dst_type; bdaddr_t src, dst; if (gerr) { error("%s", gerr->message); return; } bt_io_get(io, &gerr, BT_IO_OPT_SOURCE_BDADDR, &src, BT_IO_OPT_DEST_BDADDR, &dst, BT_IO_OPT_DEST_TYPE, &dst_type, BT_IO_OPT_INVALID); if (gerr) { error("bt_io_get: %s", gerr->message); g_error_free(gerr); return; } DBG("New incoming %s ATT connection", dst_type == BDADDR_BREDR ? "BR/EDR" : "LE"); adapter = adapter_find(&src); if (!adapter) return; device = btd_adapter_get_device(adapter, &dst, dst_type); if (!device) return; device_attach_att(device, io); } static void gap_device_name_read_cb(struct gatt_db_attribute *attrib, unsigned int id, uint16_t offset, uint8_t opcode, struct bt_att *att, void *user_data) { struct btd_gatt_database *database = user_data; uint8_t error = 0; size_t len = 0; const uint8_t *value = NULL; const char *device_name; DBG("GAP Device Name read request\n"); device_name = btd_adapter_get_name(database->adapter); len = strlen(device_name); if (offset > len) { error = BT_ATT_ERROR_INVALID_OFFSET; goto done; } len -= offset; value = len ? (const uint8_t *) &device_name[offset] : NULL; done: gatt_db_attribute_read_result(attrib, id, error, value, len); } static void gap_appearance_read_cb(struct gatt_db_attribute *attrib, unsigned int id, uint16_t offset, uint8_t opcode, struct bt_att *att, void *user_data) { struct btd_gatt_database *database = user_data; uint8_t error = 0; size_t len = 2; const uint8_t *value = NULL; uint8_t appearance[2]; uint32_t dev_class; DBG("GAP Appearance read request\n"); dev_class = btd_adapter_get_class(database->adapter); appearance[0] = dev_class & 0x00ff; appearance[1] = (dev_class >> 8) & 0x001f; len -= offset; value = len ? &appearance[offset] : NULL; gatt_db_attribute_read_result(attrib, id, error, value, len); } static void gap_car_read_cb(struct gatt_db_attribute *attrib, unsigned int id, uint16_t offset, uint8_t opcode, struct bt_att *att, void *user_data) { uint8_t value = 0x00; DBG("GAP Central Address Resolution read request\n"); if (btd_opts.defaults.le.addr_resolution) { struct btd_device *device; device = btd_adapter_find_device_by_fd(bt_att_get_fd(att)); if (device) value = btd_device_flags_enabled(device, DEVICE_FLAG_ADDRESS_RESOLUTION); } gatt_db_attribute_read_result(attrib, id, 0, &value, sizeof(value)); } static sdp_record_t *record_new(uuid_t *uuid, uint16_t start, uint16_t end) { sdp_list_t *svclass_id, *apseq, *proto[2], *root, *aproto; uuid_t root_uuid, proto_uuid, l2cap; sdp_record_t *record; sdp_data_t *psm, *sh, *eh; uint16_t lp = BT_ATT_PSM; if (uuid == NULL) return NULL; if (start > end) return NULL; record = sdp_record_alloc(); if (record == NULL) return NULL; sdp_uuid16_create(&root_uuid, PUBLIC_BROWSE_GROUP); root = sdp_list_append(NULL, &root_uuid); sdp_set_browse_groups(record, root); sdp_list_free(root, NULL); svclass_id = sdp_list_append(NULL, uuid); sdp_set_service_classes(record, svclass_id); sdp_list_free(svclass_id, NULL); sdp_uuid16_create(&l2cap, L2CAP_UUID); proto[0] = sdp_list_append(NULL, &l2cap); psm = sdp_data_alloc(SDP_UINT16, &lp); proto[0] = sdp_list_append(proto[0], psm); apseq = sdp_list_append(NULL, proto[0]); sdp_uuid16_create(&proto_uuid, ATT_UUID); proto[1] = sdp_list_append(NULL, &proto_uuid); sh = sdp_data_alloc(SDP_UINT16, &start); proto[1] = sdp_list_append(proto[1], sh); eh = sdp_data_alloc(SDP_UINT16, &end); proto[1] = sdp_list_append(proto[1], eh); apseq = sdp_list_append(apseq, proto[1]); aproto = sdp_list_append(NULL, apseq); sdp_set_access_protos(record, aproto); sdp_data_free(psm); sdp_data_free(sh); sdp_data_free(eh); sdp_list_free(proto[0], NULL); sdp_list_free(proto[1], NULL); sdp_list_free(apseq, NULL); sdp_list_free(aproto, NULL); return record; } static void database_add_record(struct btd_gatt_database *database, struct gatt_db_attribute *attr) { struct gatt_record *rec; sdp_record_t *record; uint16_t start, end; uuid_t svc, gap_uuid; bt_uuid_t uuid; const char *name = NULL; char uuidstr[MAX_LEN_UUID_STR]; gatt_db_attribute_get_service_uuid(attr, &uuid); switch (uuid.type) { case BT_UUID16: name = bt_uuid16_to_str(uuid.value.u16); sdp_uuid16_create(&svc, uuid.value.u16); break; case BT_UUID32: name = bt_uuid32_to_str(uuid.value.u32); sdp_uuid32_create(&svc, uuid.value.u32); break; case BT_UUID128: bt_uuid_to_string(&uuid, uuidstr, sizeof(uuidstr)); name = bt_uuidstr_to_str(uuidstr); sdp_uuid128_create(&svc, (void *) &uuid.value.u128); break; case BT_UUID_UNSPEC: return; } gatt_db_attribute_get_service_handles(attr, &start, &end); record = record_new(&svc, start, end); if (!record) return; if (name != NULL) sdp_set_info_attr(record, name, "BlueZ", NULL); sdp_uuid16_create(&gap_uuid, UUID_GAP); if (sdp_uuid_cmp(&svc, &gap_uuid) == 0) { sdp_set_url_attr(record, "http://www.bluez.org/", "http://www.bluez.org/", "http://www.bluez.org/"); } if (adapter_service_add(database->adapter, record) < 0) { sdp_record_free(record); return; } rec = new0(struct gatt_record, 1); rec->database = database; rec->handle = record->handle; rec->attr = attr; queue_push_tail(database->records, rec); } static void populate_gap_service(struct btd_gatt_database *database) { bt_uuid_t uuid; struct gatt_db_attribute *service, *attrib; bool ll_privacy = btd_adapter_has_settings(database->adapter, MGMT_SETTING_LL_PRIVACY); /* Add the GAP service */ bt_uuid16_create(&uuid, UUID_GAP); service = gatt_db_add_service(database->db, &uuid, true, ll_privacy ? 7 : 5); /* * Device Name characteristic. */ bt_uuid16_create(&uuid, GATT_CHARAC_DEVICE_NAME); gatt_db_service_add_characteristic(service, &uuid, BT_ATT_PERM_READ, BT_GATT_CHRC_PROP_READ, gap_device_name_read_cb, NULL, database); /* * Device Appearance characteristic. */ bt_uuid16_create(&uuid, GATT_CHARAC_APPEARANCE); attrib = gatt_db_service_add_characteristic(service, &uuid, BT_ATT_PERM_READ, BT_GATT_CHRC_PROP_READ, gap_appearance_read_cb, NULL, database); gatt_db_attribute_set_fixed_length(attrib, 2); /* Only enable Central Address Resolution if LL Privacy is supported */ if (ll_privacy) { /* * Central Address Resolution characteristic. */ bt_uuid16_create(&uuid, GATT_CHARAC_CAR); attrib = gatt_db_service_add_characteristic(service, &uuid, BT_ATT_PERM_READ, BT_GATT_CHRC_PROP_READ, gap_car_read_cb, NULL, database); } gatt_db_attribute_set_fixed_length(attrib, 1); gatt_db_service_set_active(service, true); database_add_record(database, service); } static void gatt_ccc_read_cb(struct gatt_db_attribute *attrib, unsigned int id, uint16_t offset, uint8_t opcode, struct bt_att *att, void *user_data) { struct btd_gatt_database *database = user_data; struct ccc_state *ccc; uint16_t handle; uint8_t ecode = 0; const uint8_t *value = NULL; size_t len = 0; handle = gatt_db_attribute_get_handle(attrib); DBG("CCC read called for handle: 0x%04x", handle); ccc = get_ccc_state(database, att, handle); if (!ccc) { ecode = BT_ATT_ERROR_UNLIKELY; goto done; } len = sizeof(ccc->value); value = (void *) &ccc->value; done: gatt_db_attribute_read_result(attrib, id, ecode, value, len); } static struct btd_device *att_get_device(struct bt_att *att) { GIOChannel *io = NULL; GError *gerr = NULL; bdaddr_t src, dst; uint8_t dst_type; struct btd_adapter *adapter; io = g_io_channel_unix_new(bt_att_get_fd(att)); if (!io) return NULL; bt_io_get(io, &gerr, BT_IO_OPT_SOURCE_BDADDR, &src, BT_IO_OPT_DEST_BDADDR, &dst, BT_IO_OPT_DEST_TYPE, &dst_type, BT_IO_OPT_INVALID); if (gerr) { error("bt_io_get: %s", gerr->message); g_error_free(gerr); g_io_channel_unref(io); return NULL; } g_io_channel_unref(io); adapter = adapter_find(&src); if (!adapter) { error("Unable to find adapter object"); return NULL; } return btd_adapter_find_device(adapter, &dst, dst_type); } static void pending_op_free(void *data) { struct pending_op *op = data; if (op->owner_queue) queue_remove(op->owner_queue, op); bt_att_unregister_disconnect(op->att, op->disconn_id); bt_att_unref(op->att); free(op); } static void pending_disconnect_cb(int err, void *user_data) { struct pending_op *op = user_data; op->owner_queue = NULL; } static struct pending_op *pending_ccc_new(struct bt_att *att, struct gatt_db_attribute *attrib, uint16_t value, uint8_t link_type) { struct pending_op *op; struct btd_device *device; device = att_get_device(att); if (!device) { error("Unable to find device object"); return NULL; } op = new0(struct pending_op, 1); op->data.iov_base = UINT_TO_PTR(value); op->data.iov_len = sizeof(value); op->att = bt_att_ref(att); op->attrib = attrib; op->link_type = link_type; op->disconn_id = bt_att_register_disconnect(att, pending_disconnect_cb, op, NULL); return op; } static void gatt_ccc_write_cb(struct gatt_db_attribute *attrib, unsigned int id, uint16_t offset, const uint8_t *value, size_t len, uint8_t opcode, struct bt_att *att, void *user_data) { struct btd_gatt_database *database = user_data; struct ccc_state *ccc; struct ccc_cb_data *ccc_cb; uint16_t handle, val; uint8_t ecode = 0; handle = gatt_db_attribute_get_handle(attrib); DBG("CCC write called for handle: 0x%04x", handle); if (!value || len > 2) { ecode = BT_ATT_ERROR_INVALID_ATTRIBUTE_VALUE_LEN; goto done; } if (offset > 2) { ecode = BT_ATT_ERROR_INVALID_OFFSET; goto done; } ccc = get_ccc_state(database, att, handle); if (!ccc) { ecode = BT_ATT_ERROR_UNLIKELY; goto done; } if (len == 1) val = *value; else val = get_le16(value); /* If value is identical, then just succeed */ if (val == ccc->value) goto done; ccc_cb = queue_find(database->ccc_callbacks, ccc_cb_match_handle, UINT_TO_PTR(gatt_db_attribute_get_handle(attrib))); if (ccc_cb) { struct pending_op *op; op = pending_ccc_new(att, attrib, val, bt_att_get_link_type(att)); if (!op) { ecode = BT_ATT_ERROR_UNLIKELY; goto done; } ecode = ccc_cb->callback(op, ccc_cb->user_data); if (ecode) pending_op_free(op); } if (!ecode) ccc->value = val; done: gatt_db_attribute_write_result(attrib, id, ecode); } static void ccc_add_cb(struct btd_gatt_database *database, struct gatt_db_attribute *ccc, btd_gatt_database_ccc_write_t callback, void *user_data, btd_gatt_database_destroy_t destroy) { struct ccc_cb_data *ccc_cb; ccc_cb = new0(struct ccc_cb_data, 1); ccc_cb->handle = gatt_db_attribute_get_handle(ccc); ccc_cb->callback = callback; ccc_cb->destroy = destroy; ccc_cb->user_data = user_data; queue_push_tail(database->ccc_callbacks, ccc_cb); } static struct gatt_db_attribute * service_add_ccc(struct gatt_db_attribute *service, struct btd_gatt_database *database, btd_gatt_database_ccc_write_t write_callback, void *user_data, uint32_t perm, btd_gatt_database_destroy_t destroy) { struct gatt_db_attribute *ccc; ccc = gatt_db_service_add_ccc(service, perm); if (!ccc) return ccc; /* Only add ccc_cb if callback is set */ if (write_callback) ccc_add_cb(database, ccc, write_callback, user_data, destroy); return ccc; } static void cli_feat_read_cb(struct gatt_db_attribute *attrib, unsigned int id, uint16_t offset, uint8_t opcode, struct bt_att *att, void *user_data) { struct btd_gatt_database *database = user_data; struct device_state *state; uint8_t ecode = 0; const uint8_t *value = NULL; size_t len = 0; DBG("Client Features read"); state = get_device_state(database, att); if (!state) { ecode = BT_ATT_ERROR_UNLIKELY; goto done; } len = sizeof(state->cli_feat) - offset; value = len ? &state->cli_feat[offset] : NULL; done: gatt_db_attribute_read_result(attrib, id, ecode, value, len); } static void cli_feat_write_cb(struct gatt_db_attribute *attrib, unsigned int id, uint16_t offset, const uint8_t *value, size_t len, uint8_t opcode, struct bt_att *att, void *user_data) { struct btd_gatt_database *database = user_data; struct device_state *state; uint8_t bits[] = { BT_GATT_CHRC_CLI_FEAT_ROBUST_CACHING, BT_GATT_CHRC_CLI_FEAT_EATT, BT_GATT_CHRC_CLI_FEAT_NFY_MULTI }; uint8_t ecode = 0; unsigned int i; DBG("Client Features write"); state = get_device_state(database, att); if (!state) { ecode = BT_ATT_ERROR_UNLIKELY; goto done; } if (!value || !len) { ecode = BT_ATT_ERROR_INVALID_ATTRIBUTE_VALUE_LEN; goto done; } for (i = 0; i < sizeof(bits); i++) { /* A client shall never clear a bit it has set */ if (state->cli_feat[0] & (1 << i) && !(value[0] & (1 << i))) { ecode = BT_ATT_ERROR_VALUE_NOT_ALLOWED; goto done; } } /* Shall we reallocate the feat array if bigger? */ len = MIN(sizeof(state->cli_feat), len); while (len) { state->cli_feat[len - 1] |= value[len - 1]; len--; } state->cli_feat[0] &= ((1 << sizeof(bits)) - 1); state->change_aware = true; done: gatt_db_attribute_write_result(attrib, id, ecode); } static void db_hash_read_cb(struct gatt_db_attribute *attrib, unsigned int id, uint16_t offset, uint8_t opcode, struct bt_att *att, void *user_data) { struct btd_gatt_database *database = user_data; const uint8_t *hash; struct device_state *state; bdaddr_t bdaddr; uint8_t bdaddr_type; DBG("Database Hash read"); hash = gatt_db_get_hash(database->db); gatt_db_attribute_read_result(attrib, id, 0, hash, 16); if (!get_dst_info(att, &bdaddr, &bdaddr_type)) return; state = find_device_state(database, &bdaddr, bdaddr_type); if (state) state->change_aware = true; } static void server_feat_read_cb(struct gatt_db_attribute *attrib, unsigned int id, uint16_t offset, uint8_t opcode, struct bt_att *att, void *user_data) { struct btd_gatt_database *database = user_data; struct device_state *state; uint8_t ecode = 0; uint8_t value = 0; state = get_device_state(database, att); if (!state) { ecode = BT_ATT_ERROR_UNLIKELY; goto done; } if (btd_opts.gatt_channels > 1) value |= BT_GATT_CHRC_SERVER_FEAT_EATT; done: gatt_db_attribute_read_result(attrib, id, ecode, &value, sizeof(value)); } static void populate_gatt_service(struct btd_gatt_database *database) { bt_uuid_t uuid; struct gatt_db_attribute *service; /* Add the GATT service */ bt_uuid16_create(&uuid, UUID_GATT); service = gatt_db_add_service(database->db, &uuid, true, 10); bt_uuid16_create(&uuid, GATT_CHARAC_SERVICE_CHANGED); database->svc_chngd = gatt_db_service_add_characteristic(service, &uuid, BT_ATT_PERM_NONE, BT_GATT_CHRC_PROP_INDICATE, NULL, NULL, database); database->svc_chngd_ccc = service_add_ccc(service, database, NULL, NULL, BT_ATT_PERM_READ | BT_ATT_PERM_WRITE, NULL); bt_uuid16_create(&uuid, GATT_CHARAC_CLI_FEAT); database->cli_feat = gatt_db_service_add_characteristic(service, &uuid, BT_ATT_PERM_READ | BT_ATT_PERM_WRITE, BT_GATT_CHRC_PROP_READ | BT_GATT_CHRC_PROP_WRITE, cli_feat_read_cb, cli_feat_write_cb, database); gatt_db_attribute_set_fixed_length(database->cli_feat, CLI_FEAT_SIZE); /* Only expose database hash chrc if supported */ if (gatt_db_hash_support(database->db)) { bt_uuid16_create(&uuid, GATT_CHARAC_DB_HASH); database->db_hash = gatt_db_service_add_characteristic(service, &uuid, BT_ATT_PERM_READ, BT_GATT_CHRC_PROP_READ, db_hash_read_cb, NULL, database); gatt_db_attribute_set_fixed_length(database->db_hash, 16); } /* Only enable EATT if there is a socket listening */ if (database->eatt_io) { bt_uuid16_create(&uuid, GATT_CHARAC_SERVER_FEAT); database->eatt = gatt_db_service_add_characteristic(service, &uuid, BT_ATT_PERM_READ, BT_GATT_CHRC_PROP_READ, server_feat_read_cb, NULL, database); gatt_db_attribute_set_fixed_length(database->eatt, 1); } gatt_db_service_set_active(service, true); database_add_record(database, service); } static void device_info_read_pnp_id_cb(struct gatt_db_attribute *attrib, unsigned int id, uint16_t offset, uint8_t opcode, struct bt_att *att, void *user_data) { uint8_t pdu[7]; pdu[0] = btd_opts.did_source; put_le16(btd_opts.did_vendor, &pdu[1]); put_le16(btd_opts.did_product, &pdu[3]); put_le16(btd_opts.did_version, &pdu[5]); gatt_db_attribute_read_result(attrib, id, 0, pdu, sizeof(pdu)); } static void populate_devinfo_service(struct btd_gatt_database *database) { struct gatt_db_attribute *service; struct gatt_db_attribute *attrib; bt_uuid_t uuid; if (!btd_opts.did_source) return; bt_uuid16_create(&uuid, UUID_DIS); service = gatt_db_add_service(database->db, &uuid, true, 3); bt_uuid16_create(&uuid, GATT_CHARAC_PNP_ID); attrib = gatt_db_service_add_characteristic(service, &uuid, BT_ATT_PERM_READ, BT_GATT_CHRC_PROP_READ, device_info_read_pnp_id_cb, NULL, database); gatt_db_attribute_set_fixed_length(attrib, 7); gatt_db_service_set_active(service, true); database_add_record(database, service); } static void conf_cb(void *user_data) { GDBusProxy *proxy = user_data; DBG("GATT server received confirmation"); if (proxy != NULL) { g_dbus_proxy_method_call(proxy, "Confirm", NULL, NULL, NULL, NULL); } } static void service_changed_conf(void *user_data) { struct device_state *state = user_data; DBG(""); if (!state) return; state->change_aware = true; } static void state_set_pending(struct device_state *state, struct notify *notify) { uint16_t start, end, old_start, old_end; /* Cache this only for Service Changed */ if (notify->conf != service_changed_conf) return; if (state->pending) { old_start = get_le16(state->pending->value); old_end = get_le16(state->pending->value + 2); start = get_le16(notify->value); end = get_le16(notify->value + 2); if (start < old_start) put_le16(start, state->pending->value); if (end > old_end) put_le16(end, state->pending->value + 2); return; } /* Copy notify contents to pending */ state->pending = new0(struct notify, 1); memcpy(state->pending, notify, sizeof(*notify)); state->pending->value = malloc(notify->len); memcpy(state->pending->value, notify->value, notify->len); } static void send_notification_to_device(void *data, void *user_data) { struct device_state *device_state = data; struct notify *notify = user_data; struct ccc_state *ccc; struct btd_device *device; struct bt_gatt_server *server; if (notify->conf == service_changed_conf) { if (device_state->cli_feat[0] & BT_GATT_CHRC_CLI_FEAT_ROBUST_CACHING) { device_state->change_aware = false; notify->user_data = device_state; } } ccc = find_ccc_state(device_state, notify->ccc_handle); if (!ccc || !(ccc->value & 0x0003)) return; device = btd_adapter_find_device(notify->database->adapter, &device_state->bdaddr, device_state->bdaddr_type); if (!device) { /* If ATT has not disconnect yet don't remove the state as it * will eventually be removed when att_disconnected is called. */ if (device_state->disc_id) return; goto remove; } server = btd_device_get_gatt_server(device); if (!server) { if (!device_is_bonded(device, device_state->bdaddr_type)) goto remove; state_set_pending(device_state, notify); return; } /* * TODO: If the device is not connected but bonded, send the * notification/indication when it becomes connected. */ if (!(ccc->value & 0x0002)) { DBG("GATT server sending notification"); bt_gatt_server_send_notification(server, notify->handle, notify->value, notify->len, device_state->cli_feat[0] & BT_GATT_CHRC_CLI_FEAT_NFY_MULTI); return; } DBG("GATT server sending indication"); bt_gatt_server_send_indication(server, notify->handle, notify->value, notify->len, notify->conf, notify->user_data, NULL); return; remove: /* Remove device state if device no longer exists or is not paired */ if (queue_remove(notify->database->device_states, device_state)) { queue_foreach(device_state->ccc_states, clear_ccc_state, notify->database); device_state_free(device_state); } } static void gatt_notify_cb(struct gatt_db_attribute *attrib, struct gatt_db_attribute *ccc, const uint8_t *value, size_t len, struct bt_att *att, void *user_data) { struct btd_gatt_database *database = user_data; struct notify notify; memset(¬ify, 0, sizeof(notify)); notify.database = database; notify.handle = gatt_db_attribute_get_handle(attrib); notify.ccc_handle = gatt_db_attribute_get_handle(ccc); notify.value = (void *) value; notify.len = len; if (attrib == database->svc_chngd) notify.conf = service_changed_conf; /* If a specific att is provided notify only to that device */ if (att) { struct device_state *state; state = find_device_state_by_att(database, att); if (!state) return; send_notification_to_device(state, ¬ify); } else queue_foreach(database->device_states, send_notification_to_device, ¬ify); } static void register_core_services(struct btd_gatt_database *database) { gatt_db_ccc_register(database->db, gatt_ccc_read_cb, gatt_ccc_write_cb, gatt_notify_cb, database); populate_gap_service(database); populate_gatt_service(database); populate_devinfo_service(database); } static void send_notification_to_devices(struct btd_gatt_database *database, uint16_t handle, uint8_t *value, uint16_t len, uint16_t ccc_handle, bt_gatt_server_conf_func_t conf, void *user_data) { struct notify notify; memset(¬ify, 0, sizeof(notify)); notify.database = database; notify.handle = handle; notify.ccc_handle = ccc_handle; notify.value = value; notify.len = len; notify.conf = conf; notify.user_data = user_data; queue_foreach(database->device_states, send_notification_to_device, ¬ify); } static void send_service_changed(struct btd_gatt_database *database, struct gatt_db_attribute *attrib) { uint16_t start, end; uint8_t value[4]; uint16_t handle, ccc_handle; if (!gatt_db_attribute_get_service_handles(attrib, &start, &end)) { error("Failed to obtain changed service handles"); return; } handle = gatt_db_attribute_get_handle(database->svc_chngd); ccc_handle = gatt_db_attribute_get_handle(database->svc_chngd_ccc); if (!handle || !ccc_handle) { error("Failed to obtain handles for \"Service Changed\"" " characteristic"); return; } put_le16(start, value); put_le16(end, value + 2); if (!gatt_db_attribute_notify(database->svc_chngd, value, sizeof(value), NULL)) error("Failed to notify Service Changed"); } static void database_store(struct btd_gatt_database *database) { char filename[PATH_MAX]; create_filename(filename, PATH_MAX, "/%s/attributes", btd_adapter_get_storage_dir(database->adapter)); create_file(filename, 0600); btd_settings_gatt_db_store(database->db, filename); } static void gatt_db_service_added(struct gatt_db_attribute *attrib, void *user_data) { struct btd_gatt_database *database = user_data; DBG("GATT Service added to local database"); database_add_record(database, attrib); send_service_changed(database, attrib); database_store(database); } static bool ccc_match_service(const void *data, const void *match_data) { const struct ccc_state *ccc = data; const struct gatt_db_attribute *attrib = match_data; uint16_t start, end; if (!gatt_db_attribute_get_service_handles(attrib, &start, &end)) return false; return ccc->handle >= start && ccc->handle <= end; } static void remove_device_ccc(void *data, void *user_data) { struct device_state *state = data; queue_remove_all(state->ccc_states, ccc_match_service, user_data, free); } static bool match_gatt_record(const void *data, const void *user_data) { const struct gatt_record *rec = data; const struct gatt_db_attribute *attr = user_data; return (rec->attr == attr); } static void gatt_db_service_removed(struct gatt_db_attribute *attrib, void *user_data) { struct btd_gatt_database *database = user_data; struct gatt_record *rec; DBG("Local GATT service removed"); rec = queue_remove_if(database->records, match_gatt_record, attrib); if (rec) gatt_record_free(rec); send_service_changed(database, attrib); queue_foreach(database->device_states, remove_device_ccc, attrib); queue_remove_all(database->ccc_callbacks, ccc_cb_match_service, attrib, ccc_cb_free); } struct svc_match_data { const char *path; const char *sender; }; static bool match_app(const void *a, const void *b) { const struct gatt_app *app = a; const struct svc_match_data *data = b; return g_strcmp0(app->path, data->path) == 0 && g_strcmp0(app->owner, data->sender) == 0; } static gboolean app_free_idle_cb(void *data) { app_free(data); return FALSE; } static void client_disconnect_cb(DBusConnection *conn, void *user_data) { struct gatt_app *app = user_data; struct btd_gatt_database *database = app->database; DBG("Client disconnected"); if (queue_remove(database->apps, app)) app_free(app); } static void remove_app(void *data) { struct gatt_app *app = data; /* * Set callback to NULL to avoid potential race condition * when calling remove_app and GDBusClient unref. */ g_dbus_client_set_disconnect_watch(app->client, NULL, NULL); /* * Set proxy handlers to NULL, so that this gets called only once when * the first proxy that belongs to this service gets removed. */ g_dbus_client_set_proxy_handlers(app->client, NULL, NULL, NULL, NULL); queue_remove(app->database->apps, app); /* * Do not run in the same loop, this may be a disconnect * watch call and GDBusClient should not be destroyed. */ g_idle_add(app_free_idle_cb, app); } static bool match_service_by_path(const void *a, const void *b) { const struct external_service *service = a; const char *path = b; return strcmp(service->path, path) == 0; } static bool parse_path(GDBusProxy *proxy, const char *name, const char **path) { DBusMessageIter iter; if (!g_dbus_proxy_get_property(proxy, name, &iter)) return false; if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_OBJECT_PATH) return false; dbus_message_iter_get_basic(&iter, path); return true; } static bool incr_attr_count(struct external_service *service, uint16_t incr) { if (service->attr_cnt > UINT16_MAX - incr) return false; service->attr_cnt += incr; return true; } static bool parse_chrc_flags(DBusMessageIter *array, uint8_t *props, uint8_t *ext_props, uint32_t *perm, uint32_t *ccc_perm, bool *req_prep_authorization) { const char *flag; if (!props || !ext_props || !perm || !ccc_perm) return false; *props = 0; *ext_props = 0; *perm = 0; *ccc_perm = 0; do { if (dbus_message_iter_get_arg_type(array) != DBUS_TYPE_STRING) return false; dbus_message_iter_get_basic(array, &flag); if (!strcmp("broadcast", flag)) *props |= BT_GATT_CHRC_PROP_BROADCAST; else if (!strcmp("read", flag)) { *props |= BT_GATT_CHRC_PROP_READ; *perm |= BT_ATT_PERM_READ; } else if (!strcmp("write-without-response", flag)) { *props |= BT_GATT_CHRC_PROP_WRITE_WITHOUT_RESP; *perm |= BT_ATT_PERM_WRITE; } else if (!strcmp("write", flag)) { *props |= BT_GATT_CHRC_PROP_WRITE; *perm |= BT_ATT_PERM_WRITE; } else if (!strcmp("notify", flag)) { *props |= BT_GATT_CHRC_PROP_NOTIFY; *ccc_perm |= BT_ATT_PERM_WRITE; } else if (!strcmp("indicate", flag)) { *props |= BT_GATT_CHRC_PROP_INDICATE; *ccc_perm |= BT_ATT_PERM_WRITE; } else if (!strcmp("authenticated-signed-writes", flag)) { *props |= BT_GATT_CHRC_PROP_AUTH; *perm |= BT_ATT_PERM_WRITE; } else if (!strcmp("extended-properties", flag)) { *props |= BT_GATT_CHRC_PROP_EXT_PROP; } else if (!strcmp("reliable-write", flag)) { *ext_props |= BT_GATT_CHRC_EXT_PROP_RELIABLE_WRITE; *perm |= BT_ATT_PERM_WRITE; } else if (!strcmp("writable-auxiliaries", flag)) { *ext_props |= BT_GATT_CHRC_EXT_PROP_WRITABLE_AUX; } else if (!strcmp("encrypt-read", flag)) { *props |= BT_GATT_CHRC_PROP_READ; *perm |= BT_ATT_PERM_READ | BT_ATT_PERM_READ_ENCRYPT; } else if (!strcmp("encrypt-write", flag)) { *props |= BT_GATT_CHRC_PROP_WRITE; *perm |= BT_ATT_PERM_WRITE | BT_ATT_PERM_WRITE_ENCRYPT; } else if (!strcmp("encrypt-authenticated-read", flag)) { *props |= BT_GATT_CHRC_PROP_READ; *perm |= BT_ATT_PERM_READ | BT_ATT_PERM_READ_AUTHEN; } else if (!strcmp("encrypt-authenticated-write", flag)) { *props |= BT_GATT_CHRC_PROP_WRITE; *perm |= BT_ATT_PERM_WRITE | BT_ATT_PERM_WRITE_AUTHEN; } else if (!strcmp("secure-read", flag)) { *props |= BT_GATT_CHRC_PROP_READ; *perm |= BT_ATT_PERM_READ | BT_ATT_PERM_READ_SECURE; } else if (!strcmp("secure-write", flag)) { *props |= BT_GATT_CHRC_PROP_WRITE; *perm |= BT_ATT_PERM_WRITE | BT_ATT_PERM_WRITE_SECURE; } else if (!strcmp("authorize", flag)) { *req_prep_authorization = true; } else if (!strcmp("encrypt-notify", flag)) { *ccc_perm |= BT_ATT_PERM_WRITE_ENCRYPT; *props |= BT_GATT_CHRC_PROP_NOTIFY; } else if (!strcmp("encrypt-authenticated-notify", flag)) { *ccc_perm |= BT_ATT_PERM_WRITE_AUTHEN; *props |= BT_GATT_CHRC_PROP_NOTIFY; } else if (!strcmp("secure-notify", flag)) { *ccc_perm |= BT_ATT_PERM_WRITE_SECURE; *props |= BT_GATT_CHRC_PROP_NOTIFY; } else if (!strcmp("encrypt-indicate", flag)) { *ccc_perm |= BT_ATT_PERM_WRITE_ENCRYPT; *props |= BT_GATT_CHRC_PROP_INDICATE; } else if (!strcmp("encrypt-authenticated-indicate", flag)) { *ccc_perm |= BT_ATT_PERM_WRITE_AUTHEN; *props |= BT_GATT_CHRC_PROP_INDICATE; } else if (!strcmp("secure-indicate", flag)) { *ccc_perm |= BT_ATT_PERM_WRITE_SECURE; *props |= BT_GATT_CHRC_PROP_INDICATE; } else { error("Invalid characteristic flag: %s", flag); return false; } } while (dbus_message_iter_next(array)); if (*ext_props) *props |= BT_GATT_CHRC_PROP_EXT_PROP; return true; } static bool parse_desc_flags(DBusMessageIter *array, uint32_t *perm, bool *req_prep_authorization) { const char *flag; *perm = 0; do { if (dbus_message_iter_get_arg_type(array) != DBUS_TYPE_STRING) return false; dbus_message_iter_get_basic(array, &flag); if (!strcmp("read", flag)) *perm |= BT_ATT_PERM_READ; else if (!strcmp("write", flag)) *perm |= BT_ATT_PERM_WRITE; else if (!strcmp("encrypt-read", flag)) *perm |= BT_ATT_PERM_READ | BT_ATT_PERM_READ_ENCRYPT; else if (!strcmp("encrypt-write", flag)) *perm |= BT_ATT_PERM_WRITE | BT_ATT_PERM_WRITE_ENCRYPT; else if (!strcmp("encrypt-authenticated-read", flag)) *perm |= BT_ATT_PERM_READ | BT_ATT_PERM_READ_AUTHEN; else if (!strcmp("encrypt-authenticated-write", flag)) *perm |= BT_ATT_PERM_WRITE | BT_ATT_PERM_WRITE_AUTHEN; else if (!strcmp("secure-read", flag)) *perm |= BT_ATT_PERM_READ | BT_ATT_PERM_READ_SECURE; else if (!strcmp("secure-write", flag)) *perm |= BT_ATT_PERM_WRITE | BT_ATT_PERM_WRITE_SECURE; else if (!strcmp("authorize", flag)) *req_prep_authorization = true; else { error("Invalid descriptor flag: %s", flag); return false; } } while (dbus_message_iter_next(array)); return true; } static bool parse_flags(GDBusProxy *proxy, uint8_t *props, uint8_t *ext_props, uint32_t *perm, uint32_t *ccc_perm, bool *req_prep_authorization) { DBusMessageIter iter, array; const char *iface; if (!g_dbus_proxy_get_property(proxy, "Flags", &iter)) return false; if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_ARRAY) return false; dbus_message_iter_recurse(&iter, &array); iface = g_dbus_proxy_get_interface(proxy); if (!strcmp(iface, GATT_DESC_IFACE)) return parse_desc_flags(&array, perm, req_prep_authorization); return parse_chrc_flags(&array, props, ext_props, perm, ccc_perm, req_prep_authorization); } static struct external_chrc *chrc_create(struct gatt_app *app, GDBusProxy *proxy, const char *path) { struct external_service *service; struct external_chrc *chrc; const char *service_path; if (!parse_path(proxy, "Service", &service_path)) { error("Failed to obtain service path for characteristic"); return NULL; } service = queue_find(app->services, match_service_by_path, service_path); if (!service) { error("Unable to find service for characteristic: %s", path); return NULL; } chrc = new0(struct external_chrc, 1); chrc->pending_reads = queue_new(); chrc->pending_writes = queue_new(); chrc->path = g_strdup(path); if (!chrc->path) goto fail; chrc->service = service; chrc->proxy = g_dbus_proxy_ref(proxy); /* * Add 2 for the characteristic declaration and the value * attribute. */ if (!incr_attr_count(chrc->service, 2)) { error("Failed to increment attribute count"); goto fail; } /* * Parse characteristic flags (i.e. properties) here since they * are used to determine if any special descriptors should be * created. */ if (!parse_flags(proxy, &chrc->props, &chrc->ext_props, &chrc->perm, &chrc->ccc_perm, &chrc->req_prep_authorization)) { error("Failed to parse characteristic properties"); goto fail; } if ((chrc->props & BT_GATT_CHRC_PROP_NOTIFY || chrc->props & BT_GATT_CHRC_PROP_INDICATE) && !incr_attr_count(chrc->service, 1)) { error("Failed to increment attribute count for CCC"); goto fail; } if (chrc->ext_props && !incr_attr_count(chrc->service, 1)) { error("Failed to increment attribute count for CEP"); goto fail; } queue_push_tail(chrc->service->chrcs, chrc); return chrc; fail: chrc_free(chrc); return NULL; } static bool match_chrc(const void *a, const void *b) { const struct external_chrc *chrc = a; const char *path = b; return strcmp(chrc->path, path) == 0; } static bool match_service_by_chrc(const void *a, const void *b) { const struct external_service *service = a; const char *path = b; return queue_find(service->chrcs, match_chrc, path); } static struct external_desc *desc_create(struct gatt_app *app, GDBusProxy *proxy) { struct external_service *service; struct external_desc *desc; const char *chrc_path; if (!parse_path(proxy, "Characteristic", &chrc_path)) { error("Failed to obtain characteristic path for descriptor"); return NULL; } service = queue_find(app->services, match_service_by_chrc, chrc_path); if (!service) { error("Unable to find service for characteristic: %s", chrc_path); return NULL; } desc = new0(struct external_desc, 1); desc->pending_reads = queue_new(); desc->pending_writes = queue_new(); desc->chrc_path = g_strdup(chrc_path); if (!desc->chrc_path) goto fail; desc->service = service; desc->proxy = g_dbus_proxy_ref(proxy); /* Add 1 for the descriptor attribute */ if (!incr_attr_count(desc->service, 1)) { error("Failed to increment attribute count"); goto fail; } /* * Parse descriptors flags here since they are used to * determine the permission the descriptor should have */ if (!parse_flags(proxy, NULL, NULL, &desc->perm, NULL, &desc->req_prep_authorization)) { error("Failed to parse characteristic properties"); goto fail; } queue_push_tail(desc->service->descs, desc); return desc; fail: desc_free(desc); return NULL; } static bool check_service_path(GDBusProxy *proxy, struct external_service *service) { const char *service_path; if (!parse_path(proxy, "Service", &service_path)) return false; return g_strcmp0(service_path, service->path) == 0; } static struct external_service *create_service(struct gatt_app *app, GDBusProxy *proxy, const char *path) { struct external_service *service; if (!path || !g_str_has_prefix(path, "/")) return NULL; service = queue_find(app->services, match_service_by_path, path); if (service) { error("Duplicated service: %s", path); return NULL; } service = new0(struct external_service, 1); service->app = app; service->path = g_strdup(path); if (!service->path) goto fail; service->proxy = g_dbus_proxy_ref(proxy); service->chrcs = queue_new(); service->descs = queue_new(); service->includes = queue_new(); /* Add 1 for the service declaration */ if (!incr_attr_count(service, 1)) { error("Failed to increment attribute count"); goto fail; } queue_push_tail(app->services, service); return service; fail: service_free(service); return NULL; } static void proxy_added_cb(GDBusProxy *proxy, void *user_data) { struct gatt_app *app = user_data; const char *iface, *path; if (app->failed) return; queue_push_tail(app->proxies, proxy); iface = g_dbus_proxy_get_interface(proxy); path = g_dbus_proxy_get_path(proxy); DBG("Object received: %s, iface: %s", path, iface); } static void proxy_removed_cb(GDBusProxy *proxy, void *user_data) { struct gatt_app *app = user_data; struct external_service *service; const char *path; path = g_dbus_proxy_get_path(proxy); service = queue_remove_if(app->services, match_service_by_path, (void *) path); if (!service) return; DBG("Proxy removed - removing service: %s", service->path); service_free(service); } static bool parse_uuid(GDBusProxy *proxy, bt_uuid_t *uuid) { DBusMessageIter iter; bt_uuid_t tmp; const char *uuidstr; if (!g_dbus_proxy_get_property(proxy, "UUID", &iter)) return false; if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_STRING) return false; dbus_message_iter_get_basic(&iter, &uuidstr); if (bt_string_to_uuid(uuid, uuidstr) < 0) return false; /* GAP & GATT services are created and managed by BlueZ */ bt_uuid16_create(&tmp, UUID_GAP); if (!bt_uuid_cmp(&tmp, uuid)) { error("GAP service must be handled by BlueZ"); return false; } bt_uuid16_create(&tmp, UUID_GATT); if (!bt_uuid_cmp(&tmp, uuid)) { error("GATT service must be handled by BlueZ"); return false; } return true; } static bool parse_includes(GDBusProxy *proxy, struct external_service *service) { DBusMessageIter iter; DBusMessageIter array; char *obj; char *includes; int type; /* Includes property is optional */ if (!g_dbus_proxy_get_property(proxy, "Includes", &iter)) return true; if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_ARRAY) return false; dbus_message_iter_recurse(&iter, &array); while ((type = dbus_message_iter_get_arg_type(&array)) != DBUS_TYPE_INVALID) { if (type != DBUS_TYPE_OBJECT_PATH) return false; dbus_message_iter_get_basic(&array, &obj); includes = g_strdup(obj); if (!includes) return false; if (!queue_push_tail(service->includes, includes)) { error("Failed to add Includes path in queue\n"); return false; } incr_attr_count(service, 1); dbus_message_iter_next(&array); } return true; } static bool parse_primary(GDBusProxy *proxy, bool *primary) { DBusMessageIter iter; dbus_bool_t val; if (!g_dbus_proxy_get_property(proxy, "Primary", &iter)) return false; if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_BOOLEAN) return false; dbus_message_iter_get_basic(&iter, &val); *primary = val; return true; } static bool parse_handle(GDBusProxy *proxy, uint16_t *handle) { DBusMessageIter iter; *handle = 0; /* Handle property is optional */ if (!g_dbus_proxy_get_property(proxy, "Handle", &iter)) return true; if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_UINT16) return false; dbus_message_iter_get_basic(&iter, handle); return true; } static uint8_t dbus_error_to_att_ecode(const char *name, const char *msg, uint8_t perm_err) { if (strcmp(name, ERROR_INTERFACE ".Failed") == 0) { char *endptr = NULL; uint32_t ecode; ecode = strtol(msg, &endptr, 0); /* If message doesn't set an error code just use 0x80 */ if (!endptr || *endptr != '\0') return 0x80; if (ecode < 0x80 || ecode > 0x9f) { error("Invalid error code: %s", msg); return BT_ATT_ERROR_UNLIKELY; } return ecode; } if (strcmp(name, ERROR_INTERFACE ".NotSupported") == 0) return BT_ATT_ERROR_REQUEST_NOT_SUPPORTED; if (strcmp(name, ERROR_INTERFACE ".NotAuthorized") == 0) return BT_ATT_ERROR_AUTHORIZATION; if (strcmp(name, ERROR_INTERFACE ".InvalidValueLength") == 0) return BT_ATT_ERROR_INVALID_ATTRIBUTE_VALUE_LEN; if (strcmp(name, ERROR_INTERFACE ".InvalidOffset") == 0) return BT_ATT_ERROR_INVALID_OFFSET; if (strcmp(name, ERROR_INTERFACE ".InProgress") == 0) return BT_ERROR_ALREADY_IN_PROGRESS; if (!strcmp(name, ERROR_INTERFACE ".ImproperlyConfigured")) return BT_ERROR_CCC_IMPROPERLY_CONFIGURED; if (strcmp(name, ERROR_INTERFACE ".NotPermitted") == 0) return perm_err; return BT_ATT_ERROR_UNLIKELY; } static void read_reply_cb(DBusMessage *message, void *user_data) { struct pending_op *op = user_data; DBusError err; DBusMessageIter iter, array; uint8_t ecode = 0; uint8_t *value = NULL; int len = 0; if (!op->owner_queue) { DBG("Pending read was canceled when object got removed"); return; } dbus_error_init(&err); if (dbus_set_error_from_message(&err, message) == TRUE) { DBG("Failed to read value: %s: %s", err.name, err.message); ecode = dbus_error_to_att_ecode(err.name, err.message, BT_ATT_ERROR_READ_NOT_PERMITTED); dbus_error_free(&err); goto done; } dbus_message_iter_init(message, &iter); if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_ARRAY) { /* * Return not supported for this, as the external app basically * doesn't properly support reading from this characteristic. */ ecode = BT_ATT_ERROR_REQUEST_NOT_SUPPORTED; error("Invalid return value received for \"ReadValue\""); goto done; } dbus_message_iter_recurse(&iter, &array); dbus_message_iter_get_fixed_array(&array, &value, &len); if (len < 0) { ecode = BT_ATT_ERROR_REQUEST_NOT_SUPPORTED; value = NULL; len = 0; goto done; } /* Truncate the value if it's too large */ len = MIN(BT_ATT_MAX_VALUE_LEN, len); value = len ? value : NULL; done: gatt_db_attribute_read_result(op->attrib, op->id, ecode, value, len); } static struct pending_op *pending_read_new(struct bt_att *att, struct queue *owner_queue, struct gatt_db_attribute *attrib, unsigned int id, uint16_t offset) { struct pending_op *op; op = new0(struct pending_op, 1); op->owner_queue = owner_queue; op->att = bt_att_ref(att); op->attrib = attrib; op->id = id; op->offset = offset; op->link_type = bt_att_get_link_type(att); queue_push_tail(owner_queue, op); op->disconn_id = bt_att_register_disconnect(att, pending_disconnect_cb, op, NULL); return op; } static void append_options(DBusMessageIter *iter, void *user_data) { struct pending_op *op = user_data; struct btd_device *device = att_get_device(op->att); const char *path = device_get_path(device); struct bt_gatt_server *server; const char *link; uint16_t mtu; switch (op->link_type) { case BT_ATT_BREDR: link = "BR/EDR"; break; case BT_ATT_LE: link = "LE"; break; default: link = NULL; break; } dict_append_entry(iter, "device", DBUS_TYPE_OBJECT_PATH, &path); if (op->offset) dict_append_entry(iter, "offset", DBUS_TYPE_UINT16, &op->offset); if (link) dict_append_entry(iter, "link", DBUS_TYPE_STRING, &link); if (op->prep_authorize) dict_append_entry(iter, "prepare-authorize", DBUS_TYPE_BOOLEAN, &op->prep_authorize); server = btd_device_get_gatt_server(device); mtu = bt_gatt_server_get_mtu(server); dict_append_entry(iter, "mtu", DBUS_TYPE_UINT16, &mtu); } static void read_setup_cb(DBusMessageIter *iter, void *user_data) { struct pending_op *op = user_data; DBusMessageIter dict; dbus_message_iter_open_container(iter, DBUS_TYPE_ARRAY, DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING DBUS_TYPE_STRING_AS_STRING DBUS_TYPE_VARIANT_AS_STRING DBUS_DICT_ENTRY_END_CHAR_AS_STRING, &dict); append_options(&dict, op); dbus_message_iter_close_container(iter, &dict); } static struct pending_op *send_read(struct bt_att *att, struct gatt_db_attribute *attrib, GDBusProxy *proxy, struct queue *owner_queue, unsigned int id, uint16_t offset) { struct pending_op *op; op = pending_read_new(att, owner_queue, attrib, id, offset); if (g_dbus_proxy_method_call(proxy, "ReadValue", read_setup_cb, read_reply_cb, op, pending_op_free) == TRUE) return op; pending_op_free(op); return NULL; } static void write_setup_cb(DBusMessageIter *iter, void *user_data) { struct pending_op *op = user_data; DBusMessageIter array, dict; dbus_message_iter_open_container(iter, DBUS_TYPE_ARRAY, "y", &array); dbus_message_iter_append_fixed_array(&array, DBUS_TYPE_BYTE, &op->data.iov_base, op->data.iov_len); dbus_message_iter_close_container(iter, &array); dbus_message_iter_open_container(iter, DBUS_TYPE_ARRAY, DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING DBUS_TYPE_STRING_AS_STRING DBUS_TYPE_VARIANT_AS_STRING DBUS_DICT_ENTRY_END_CHAR_AS_STRING, &dict); append_options(&dict, op); dbus_message_iter_close_container(iter, &dict); if (!op->owner_queue) { gatt_db_attribute_write_result(op->attrib, op->id, 0); pending_op_free(op); } } static void write_reply_cb(DBusMessage *message, void *user_data) { struct pending_op *op = user_data; struct external_chrc *chrc; struct external_desc *desc; DBusError err; DBusMessageIter iter; uint8_t ecode = 0; if (!op->owner_queue) { DBG("Pending write was canceled when object got removed"); return; } dbus_error_init(&err); if (dbus_set_error_from_message(&err, message) == TRUE) { DBG("Failed to write value: %s: %s", err.name, err.message); ecode = dbus_error_to_att_ecode(err.name, err.message, BT_ATT_ERROR_WRITE_NOT_PERMITTED); dbus_error_free(&err); goto done; } if (op->prep_authorize) { if (op->is_characteristic) { chrc = gatt_db_attribute_get_user_data(op->attrib); chrc->prep_authorized = true; } else { desc = gatt_db_attribute_get_user_data(op->attrib); desc->prep_authorized = true; } } dbus_message_iter_init(message, &iter); if (dbus_message_iter_has_next(&iter)) { /* * Return not supported for this, as the external app basically * doesn't properly support the "WriteValue" API. */ ecode = BT_ATT_ERROR_REQUEST_NOT_SUPPORTED; error("Invalid return value received for \"WriteValue\""); } done: gatt_db_attribute_write_result(op->attrib, op->id, ecode); } static struct pending_op *pending_write_new(struct bt_att *att, struct queue *owner_queue, struct gatt_db_attribute *attrib, unsigned int id, const uint8_t *value, size_t len, uint16_t offset, bool is_characteristic, bool prep_authorize) { struct pending_op *op; op = new0(struct pending_op, 1); op->data.iov_base = (uint8_t *) value; op->data.iov_len = len; op->att = bt_att_ref(att); op->owner_queue = owner_queue; op->attrib = attrib; op->id = id; op->offset = offset; op->link_type = bt_att_get_link_type(att); op->is_characteristic = is_characteristic; op->prep_authorize = prep_authorize; queue_push_tail(owner_queue, op); op->disconn_id = bt_att_register_disconnect(att, pending_disconnect_cb, op, NULL); return op; } static struct pending_op *send_write(struct bt_att *att, struct gatt_db_attribute *attrib, GDBusProxy *proxy, struct queue *owner_queue, unsigned int id, const uint8_t *value, size_t len, uint16_t offset, bool is_characteristic, bool prep_authorize) { struct pending_op *op; op = pending_write_new(att, owner_queue, attrib, id, value, len, offset, is_characteristic, prep_authorize); if (g_dbus_proxy_method_call(proxy, "WriteValue", write_setup_cb, owner_queue ? write_reply_cb : NULL, op, pending_op_free) == TRUE) return op; pending_op_free(op); return NULL; } static void flush_pending_write(void *data, void *user_data) { GDBusProxy *proxy = user_data; struct pending_op *op = data; if (g_dbus_proxy_method_call(proxy, "WriteValue", write_setup_cb, write_reply_cb, op, pending_op_free) == TRUE) return; pending_op_free(op); } static void flush_pending_writes(GDBusProxy *proxy, struct queue *owner_queue) { queue_foreach(owner_queue, flush_pending_write, proxy); queue_remove_all(owner_queue, NULL, NULL, NULL); } static bool match_client_io(const void *data, const void *user_data) { const struct client_io *client = data; const struct io *io = user_data; return client->io == io; } static bool sock_hup(struct io *io, void *user_data) { struct external_chrc *chrc = user_data; struct client_io *client; DBG("%p closed\n", io); client = queue_remove_if(chrc->write_ios, match_client_io, io); if (!client) { client = queue_remove_if(chrc->notify_ios, match_client_io, io); if (!client) return false; } client_io_free(client); return false; } static bool sock_io_write(struct io *io, void *user_data) { uint8_t buf[] = { 1 }; struct iovec iov = { buf, sizeof(buf) }; /* Send a 1 to the server as confirmation */ io_send(io, &iov, 1); return false; } static void sock_io_conf(void *user_data) { struct io *io = user_data; io_set_write_handler(io, sock_io_write, NULL, NULL); } static bool sock_io_read(struct io *io, void *user_data) { struct client_io *client = user_data; struct external_chrc *chrc = client->chrc; uint8_t buf[512]; int fd = io_get_fd(io); ssize_t bytes_read; struct notify notify; struct device_state *state; if (fd < 0) { error("io_get_fd() returned %d\n", fd); return false; } bytes_read = read(fd, buf, sizeof(buf)); if (bytes_read <= 0) return false; memset(¬ify, 0, sizeof(notify)); notify.database = client->chrc->service->app->database; notify.handle = gatt_db_attribute_get_handle(chrc->attrib); notify.ccc_handle = gatt_db_attribute_get_handle(chrc->ccc); notify.value = (void *) buf; notify.len = bytes_read; notify.conf = sock_io_conf; notify.user_data = io; state = find_device_state_by_att(notify.database, client->att); if (!state) return false; send_notification_to_device(state, ¬ify); return true; } static struct io *sock_io_new(int fd, void *user_data) { struct io *io; io = io_new(fd); io_set_close_on_destroy(io, true); io_set_disconnect_handler(io, sock_hup, user_data, NULL); return io; } static int sock_io_send(struct io *io, const void *data, size_t len) { struct msghdr msg; struct iovec iov; int fd; iov.iov_base = (void *) data; iov.iov_len = len; memset(&msg, 0, sizeof(msg)); msg.msg_iov = &iov; msg.msg_iovlen = 1; fd = io_get_fd(io); if (fd < 0) { error("io_get_fd() returned %d\n", fd); return fd; } return sendmsg(fd, &msg, MSG_NOSIGNAL); } static void att_disconnect_cb(int err, void *user_data) { struct client_io *client = user_data; /* If ATT is disconnected shutdown correspondent client IO so sock_hup * is triggered and the server socket is closed. */ io_shutdown(client->io); } static struct client_io * client_io_new(struct external_chrc *chrc, int fd, struct bt_att *att) { struct client_io *client; client = new0(struct client_io, 1); client->att = bt_att_ref(att); client->chrc = chrc; client->disconn_id = bt_att_register_disconnect(att, att_disconnect_cb, client, NULL); client->io = sock_io_new(fd, chrc); return client; } static bool match_client_att(const void *data, const void *user_data) { const struct client_io *client = data; const struct bt_att *att = user_data; /* Always match if ATT instance is not set since that is used by * clear_cc_state to clear all instances. */ if (!att) return true; return client->att == att; } static struct client_io * client_write_io_get(struct external_chrc *chrc, int fd, struct bt_att *att) { struct client_io *client; client = queue_find(chrc->write_ios, match_client_att, att); if (client) return client; client = client_io_new(chrc, fd, att); if (!chrc->write_ios) chrc->write_ios = queue_new(); queue_push_tail(chrc->write_ios, client); return client; } static void acquire_write_reply(DBusMessage *message, void *user_data) { struct pending_op *op = user_data; struct external_chrc *chrc; struct client_io *client; DBusError err; int fd; uint16_t mtu; if (!op->owner_queue) { DBG("Pending write was canceled when object got removed"); return; } chrc = gatt_db_attribute_get_user_data(op->attrib); dbus_error_init(&err); if (dbus_set_error_from_message(&err, message) == TRUE) { uint8_t ecode; error("Failed to acquire write: %s\n", err.name); ecode = dbus_error_to_att_ecode(err.name, err.message, BT_ATT_ERROR_WRITE_NOT_PERMITTED); dbus_error_free(&err); if (ecode != BT_ATT_ERROR_UNLIKELY) { gatt_db_attribute_write_result(op->attrib, op->id, ecode); pending_op_free(op); return; } goto retry; } if ((dbus_message_get_args(message, NULL, DBUS_TYPE_UNIX_FD, &fd, DBUS_TYPE_UINT16, &mtu, DBUS_TYPE_INVALID) == false)) { error("Invalid AcquireWrite response\n"); goto retry; } DBG("AcquireWrite success: fd %d MTU %u\n", fd, mtu); client = client_write_io_get(chrc, fd, op->att); if (!client) goto retry; while ((op = queue_peek_head(chrc->pending_writes)) != NULL) { if (sock_io_send(client->io, op->data.iov_base, op->data.iov_len) < 0) goto retry; gatt_db_attribute_write_result(op->attrib, op->id, 0); pending_op_free(op); } return; retry: flush_pending_writes(chrc->proxy, chrc->pending_writes); } static void acquire_write_setup(DBusMessageIter *iter, void *user_data) { struct pending_op *op = user_data; DBusMessageIter dict; dbus_message_iter_open_container(iter, DBUS_TYPE_ARRAY, DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING DBUS_TYPE_STRING_AS_STRING DBUS_TYPE_VARIANT_AS_STRING DBUS_DICT_ENTRY_END_CHAR_AS_STRING, &dict); append_options(&dict, op); dbus_message_iter_close_container(iter, &dict); } static struct pending_op *acquire_write(struct external_chrc *chrc, struct bt_att *att, struct gatt_db_attribute *attrib, unsigned int id, const uint8_t *value, size_t len) { struct pending_op *op; bool acquiring = !queue_isempty(chrc->pending_writes); op = pending_write_new(att, chrc->pending_writes, attrib, id, value, len, 0, false, false); if (acquiring) return op; if (g_dbus_proxy_method_call(chrc->proxy, "AcquireWrite", acquire_write_setup, acquire_write_reply, op, NULL)) return op; pending_op_free(op); return NULL; } static struct client_io * client_notify_io_get(struct external_chrc *chrc, int fd, struct bt_att *att) { struct client_io *client; client = queue_find(chrc->notify_ios, match_client_att, att); if (client) return client; client = client_io_new(chrc, fd, att); io_set_read_handler(client->io, sock_io_read, client, NULL); if (!chrc->notify_ios) chrc->notify_ios = queue_new(); queue_push_tail(chrc->notify_ios, client); return client; } static void acquire_notify_reply(DBusMessage *message, void *user_data) { struct pending_op *op = user_data; struct external_chrc *chrc = (void *) op->data.iov_base; struct client_io *client; DBusError err; int fd; uint16_t mtu; if (!op->owner_queue) { DBG("Pending notify was canceled when object got removed"); return; } dbus_error_init(&err); if (dbus_set_error_from_message(&err, message) == TRUE) { error("Failed to acquire notify: %s\n", err.name); if (dbus_error_has_name(&err, DBUS_ERROR_NO_REPLY) || dbus_error_has_name(&err, ERROR_FAILED)) { dbus_error_free(&err); return; } dbus_error_free(&err); goto retry; } if ((dbus_message_get_args(message, NULL, DBUS_TYPE_UNIX_FD, &fd, DBUS_TYPE_UINT16, &mtu, DBUS_TYPE_INVALID) == false)) { error("Invalid AcquirNotify response\n"); goto retry; } DBG("AcquireNotify success: fd %d MTU %u\n", fd, mtu); client = client_notify_io_get(chrc, fd, op->att); if (!client) goto retry; __sync_fetch_and_add(&chrc->ntfy_cnt, 1); return; retry: g_dbus_proxy_method_call(chrc->proxy, "StartNotify", NULL, NULL, NULL, NULL); __sync_fetch_and_add(&chrc->ntfy_cnt, 1); } static void acquire_notify_setup(DBusMessageIter *iter, void *user_data) { DBusMessageIter dict; struct pending_op *op = user_data; dbus_message_iter_open_container(iter, DBUS_TYPE_ARRAY, DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING DBUS_TYPE_STRING_AS_STRING DBUS_TYPE_VARIANT_AS_STRING DBUS_DICT_ENTRY_END_CHAR_AS_STRING, &dict); append_options(&dict, op); dbus_message_iter_close_container(iter, &dict); } static uint8_t ccc_write_cb(struct pending_op *op, void *user_data) { struct external_chrc *chrc = user_data; struct client_io *client; DBusMessageIter iter; uint16_t value; value = op ? PTR_TO_UINT(op->data.iov_base) : 0; DBG("External CCC write received with value: 0x%04x", value); /* Notifications/indications disabled */ if (!value) { if (!chrc->ntfy_cnt) goto done; client = queue_remove_if(chrc->notify_ios, match_client_att, op ? op->att : NULL); if (client) { client_io_free(client); __sync_sub_and_fetch(&chrc->ntfy_cnt, 1); goto done; } if (__sync_sub_and_fetch(&chrc->ntfy_cnt, 1)) goto done; /* * Send request to stop notifying. This is best-effort * operation, so simply ignore the return the value. */ g_dbus_proxy_method_call(chrc->proxy, "StopNotify", NULL, NULL, NULL, NULL); goto done; } if (chrc->ntfy_cnt == UINT_MAX) /* Maximum number of per-device CCC descriptors configured */ return BT_ATT_ERROR_INSUFFICIENT_RESOURCES; /* Don't support undefined CCC values yet */ if (value > 2 || (value == 1 && !(chrc->props & BT_GATT_CHRC_PROP_NOTIFY)) || (value == 2 && !(chrc->props & BT_GATT_CHRC_PROP_INDICATE))) return BT_ERROR_CCC_IMPROPERLY_CONFIGURED; client = queue_find(chrc->notify_ios, match_client_att, op->att); if (client) { __sync_fetch_and_add(&chrc->ntfy_cnt, 1); goto done; } /* Make use of AcquireNotify if supported */ if (g_dbus_proxy_get_property(chrc->proxy, "NotifyAcquired", &iter)) { op->data.iov_base = (void *) chrc; op->data.iov_len = sizeof(chrc); op->owner_queue = chrc->pending_writes; if (g_dbus_proxy_method_call(chrc->proxy, "AcquireNotify", acquire_notify_setup, acquire_notify_reply, op, pending_op_free)) return 0; } /* * Always call StartNotify for an incoming enable and ignore the return * value for now. */ if (g_dbus_proxy_method_call(chrc->proxy, "StartNotify", NULL, NULL, NULL, NULL) == FALSE) return BT_ATT_ERROR_UNLIKELY; __sync_fetch_and_add(&chrc->ntfy_cnt, 1); done: if (op) pending_op_free(op); return 0; } static void property_changed_cb(GDBusProxy *proxy, const char *name, DBusMessageIter *iter, void *user_data) { struct external_chrc *chrc = user_data; DBusMessageIter array; uint8_t *value = NULL; int len = 0; if (strcmp(name, "Value")) return; if (iter) { if (dbus_message_iter_get_arg_type(iter) != DBUS_TYPE_ARRAY) { DBG("Malformed \"Value\" property received"); return; } dbus_message_iter_recurse(iter, &array); dbus_message_iter_get_fixed_array(&array, &value, &len); if (len < 0) { DBG("Malformed \"Value\" property received"); return; } } /* Truncate the value if it's too large */ len = MIN(BT_ATT_MAX_VALUE_LEN, len); value = len ? value : NULL; send_notification_to_devices(chrc->service->app->database, gatt_db_attribute_get_handle(chrc->attrib), value, len, gatt_db_attribute_get_handle(chrc->ccc), conf_cb, proxy); } static bool database_add_ccc(struct external_service *service, struct external_chrc *chrc) { if (!(chrc->props & BT_GATT_CHRC_PROP_NOTIFY) && !(chrc->props & BT_GATT_CHRC_PROP_INDICATE)) return true; /* Always set read/write permissions */ chrc->ccc_perm |= BT_ATT_PERM_WRITE | BT_ATT_PERM_READ; chrc->ccc = service_add_ccc(service->attrib, service->app->database, ccc_write_cb, chrc, chrc->ccc_perm, NULL); if (!chrc->ccc) { error("Failed to create CCC entry for characteristic"); return false; } if (g_dbus_proxy_set_property_watch(chrc->proxy, property_changed_cb, chrc) == FALSE) { error("Failed to set up property watch for characteristic"); return false; } DBG("Created CCC entry for characteristic"); return true; } static void cep_write_cb(struct gatt_db_attribute *attrib, int err, void *user_data) { if (err) DBG("Failed to store CEP value in the database"); else DBG("Stored CEP value in the database"); } static bool database_add_cep(struct external_service *service, struct external_chrc *chrc) { struct gatt_db_attribute *cep; bt_uuid_t uuid; uint8_t value[2]; if (!chrc->ext_props) return true; bt_uuid16_create(&uuid, GATT_CHARAC_EXT_PROPER_UUID); cep = gatt_db_service_add_descriptor(service->attrib, &uuid, BT_ATT_PERM_READ, NULL, NULL, NULL); if (!cep) { error("Failed to create CEP entry for characteristic"); return false; } memset(value, 0, sizeof(value)); value[0] = chrc->ext_props; if (!gatt_db_attribute_write(cep, 0, value, sizeof(value), 0, NULL, cep_write_cb, NULL)) { DBG("Failed to store CEP value in the database"); return false; } DBG("Created CEP entry for characteristic"); return true; } static void desc_read_cb(struct gatt_db_attribute *attrib, unsigned int id, uint16_t offset, uint8_t opcode, struct bt_att *att, void *user_data) { struct external_desc *desc = user_data; struct btd_device *device; if (desc->attrib != attrib) { error("Read callback called with incorrect attribute"); goto fail; } device = att_get_device(att); if (!device) { error("Unable to find device object"); goto fail; } if (send_read(att, attrib, desc->proxy, desc->pending_reads, id, offset)) return; fail: gatt_db_attribute_read_result(attrib, id, BT_ATT_ERROR_UNLIKELY, NULL, 0); } static void desc_write_cb(struct gatt_db_attribute *attrib, unsigned int id, uint16_t offset, const uint8_t *value, size_t len, uint8_t opcode, struct bt_att *att, void *user_data) { struct external_desc *desc = user_data; struct btd_device *device; if (desc->attrib != attrib) { error("Read callback called with incorrect attribute"); goto fail; } device = att_get_device(att); if (!device) { error("Unable to find device object"); goto fail; } if (opcode == BT_ATT_OP_PREP_WRITE_REQ) { if (!btd_device_is_trusted(device) && !desc->prep_authorized && desc->req_prep_authorization) send_write(att, attrib, desc->proxy, desc->pending_writes, id, value, len, offset, false, true); else gatt_db_attribute_write_result(attrib, id, 0); return; } if (opcode == BT_ATT_OP_EXEC_WRITE_REQ) desc->prep_authorized = false; if (send_write(att, attrib, desc->proxy, desc->pending_writes, id, value, len, offset, false, false)) return; fail: gatt_db_attribute_write_result(attrib, id, BT_ATT_ERROR_UNLIKELY); } static void write_handle(struct GDBusProxy *proxy, uint16_t handle) { DBusMessageIter iter; /* Check if the attribute has the Handle property */ if (!g_dbus_proxy_get_property(proxy, "Handle", &iter)) return; g_dbus_proxy_set_property_basic(proxy, "Handle", DBUS_TYPE_UINT16, &handle, NULL, NULL, NULL); } static bool database_add_desc(struct external_service *service, struct external_desc *desc) { uint16_t handle; bt_uuid_t uuid; char str[MAX_LEN_UUID_STR]; if (!parse_handle(desc->proxy, &handle)) { error("Failed to read \"Handle\" property of descriptor"); return false; } if (!parse_uuid(desc->proxy, &uuid)) { error("Failed to read \"UUID\" property of descriptor"); return false; } desc->attrib = gatt_db_service_insert_descriptor(service->attrib, handle, &uuid, desc->perm, desc_read_cb, desc_write_cb, desc); if (!desc->attrib) { error("Failed to create descriptor entry in database"); return false; } desc->handled = true; if (!handle) { handle = gatt_db_attribute_get_handle(desc->attrib); write_handle(desc->proxy, handle); } bt_uuid_to_string(&uuid, str, sizeof(str)); DBG("handle 0x%04x UUID %s", handle, str); return true; } static void chrc_read_cb(struct gatt_db_attribute *attrib, unsigned int id, uint16_t offset, uint8_t opcode, struct bt_att *att, void *user_data) { struct external_chrc *chrc = user_data; struct btd_device *device; if (chrc->attrib != attrib) { error("Read callback called with incorrect attribute"); goto fail; } device = att_get_device(att); if (!device) { error("Unable to find device object"); goto fail; } if (send_read(att, attrib, chrc->proxy, chrc->pending_reads, id, offset)) return; fail: gatt_db_attribute_read_result(attrib, id, BT_ATT_ERROR_UNLIKELY, NULL, 0); } static void chrc_write_cb(struct gatt_db_attribute *attrib, unsigned int id, uint16_t offset, const uint8_t *value, size_t len, uint8_t opcode, struct bt_att *att, void *user_data) { struct external_chrc *chrc = user_data; struct client_io *client; struct btd_device *device; struct queue *queue; DBusMessageIter iter; if (chrc->attrib != attrib) { error("Write callback called with incorrect attribute"); goto fail; } device = att_get_device(att); if (!device) { error("Unable to find device object"); goto fail; } if (!(chrc->props & BT_GATT_CHRC_PROP_WRITE_WITHOUT_RESP)) queue = chrc->pending_writes; else queue = NULL; if (opcode == BT_ATT_OP_PREP_WRITE_REQ) { if (!btd_device_is_trusted(device) && !chrc->prep_authorized && chrc->req_prep_authorization) send_write(att, attrib, chrc->proxy, queue, id, value, len, offset, true, true); else gatt_db_attribute_write_result(attrib, id, 0); return; } if (opcode == BT_ATT_OP_EXEC_WRITE_REQ) chrc->prep_authorized = false; client = queue_find(chrc->write_ios, match_client_att, att); if (client) { if (sock_io_send(client->io, value, len) < 0) { error("Unable to write: %s", strerror(errno)); goto fail; } gatt_db_attribute_write_result(attrib, id, 0); return; } if (g_dbus_proxy_get_property(chrc->proxy, "WriteAcquired", &iter)) { if (acquire_write(chrc, att, attrib, id, value, len)) return; } if (send_write(att, attrib, chrc->proxy, queue, id, value, len, offset, false, false)) return; fail: gatt_db_attribute_write_result(attrib, id, BT_ATT_ERROR_UNLIKELY); } static void include_services(void *data ,void *userdata) { char *obj = data; struct external_service *service = userdata; struct gatt_db_attribute *attrib; struct external_service *service_inc; DBG("path %s", obj); service_inc = queue_find(service->app->services, match_service_by_path, obj); if (!service_inc) { error("include service not found\n"); return; } attrib = gatt_db_service_add_included(service->attrib, service_inc->attrib); if (!attrib) { error("include service attributes failed\n"); return; } service->attrib = attrib; } static void database_add_includes(struct external_service *service) { queue_foreach(service->includes, include_services, service); } static bool database_add_chrc(struct external_service *service, struct external_chrc *chrc) { uint16_t handle = 0, value_handle; bt_uuid_t uuid; char str[MAX_LEN_UUID_STR]; const struct queue_entry *entry; if (!parse_handle(chrc->proxy, &value_handle)) { error("Failed to read \"Handle\" property of characteristic"); return false; } if (!parse_uuid(chrc->proxy, &uuid)) { error("Failed to read \"UUID\" property of characteristic"); return false; } if (!check_service_path(chrc->proxy, service)) { error("Invalid service path for characteristic"); return false; } if (value_handle) handle = value_handle - 1; chrc->attrib = gatt_db_service_insert_characteristic(service->attrib, handle, value_handle, &uuid, chrc->perm, chrc->props, chrc_read_cb, chrc_write_cb, chrc); if (!chrc->attrib) { error("Failed to create characteristic entry in database"); return false; } if (!database_add_ccc(service, chrc)) return false; if (!database_add_cep(service, chrc)) return false; if (!handle) { handle = gatt_db_attribute_get_handle(chrc->attrib); write_handle(chrc->proxy, handle); } bt_uuid_to_string(&uuid, str, sizeof(str)); DBG("handle 0x%04x UUID %s", handle, str); /* Handle the descriptors that belong to this characteristic. */ for (entry = queue_get_entries(service->descs); entry; entry = entry->next) { struct external_desc *desc = entry->data; if (desc->handled || g_strcmp0(desc->chrc_path, chrc->path)) continue; if (!database_add_desc(service, desc)) { chrc->attrib = NULL; error("Failed to create descriptor entry"); return false; } } return true; } static bool match_desc_unhandled(const void *a, const void *b) { const struct external_desc *desc = a; return !desc->handled; } static bool database_add_service(struct external_service *service) { bt_uuid_t uuid; bool primary; uint16_t handle; const struct queue_entry *entry; char str[MAX_LEN_UUID_STR]; if (!parse_uuid(service->proxy, &uuid)) { error("Failed to read \"UUID\" property of service"); return false; } if (!parse_primary(service->proxy, &primary)) { error("Failed to read \"Primary\" property of service"); return false; } if (!parse_includes(service->proxy, service)) { error("Failed to read \"Includes\" property of service"); return false; } if (!parse_handle(service->proxy, &handle)) { error("Failed to read \"Handle\" property of service"); return false; } service->attrib = gatt_db_insert_service(service->app->database->db, handle, &uuid, primary, service->attr_cnt); if (!service->attrib) return false; if (!handle) { handle = gatt_db_attribute_get_handle(service->attrib); write_handle(service->proxy, handle); } bt_uuid_to_string(&uuid, str, sizeof(str)); DBG("handle 0x%04x UUID %s", handle, str); database_add_includes(service); entry = queue_get_entries(service->chrcs); while (entry) { struct external_chrc *chrc = entry->data; if (!database_add_chrc(service, chrc)) { error("Failed to add characteristic"); goto fail; } entry = entry->next; } /* If there are any unhandled descriptors, return an error */ if (queue_find(service->descs, match_desc_unhandled, NULL)) { error("Found descriptor with no matching characteristic!"); goto fail; } gatt_db_service_set_active(service->attrib, true); return true; fail: gatt_db_remove_service(service->app->database->db, service->attrib); service->attrib = NULL; return false; } static bool database_add_app(struct gatt_app *app) { const struct queue_entry *entry; entry = queue_get_entries(app->services); while (entry) { if (!database_add_service(entry->data)) { error("Failed to add service"); return false; } entry = entry->next; } return true; } static int profile_device_probe(struct btd_service *service) { struct btd_profile *p = btd_service_get_profile(service); DBG("%s probed", p->name); return 0; } static void profile_device_remove(struct btd_service *service) { struct btd_profile *p = btd_service_get_profile(service); DBG("%s removed", p->name); } static int profile_device_accept(struct btd_service *service) { struct btd_profile *p = btd_service_get_profile(service); DBG("%s accept", p->name); return 0; } static int profile_add(struct external_profile *profile, const char *uuid) { struct btd_profile *p; p = new0(struct btd_profile, 1); /* Assign directly to avoid having extra fields */ p->name = (const void *) g_strdup_printf("%s%s/%s", profile->app->owner, g_dbus_proxy_get_path(profile->proxy), uuid); if (!p->name) { free(p); return -ENOMEM; } p->remote_uuid = (const void *) g_strdup(uuid); if (!p->remote_uuid) { g_free((void *) p->name); free(p); return -ENOMEM; } p->device_probe = profile_device_probe; p->device_remove = profile_device_remove; p->accept = profile_device_accept; p->auto_connect = true; p->external = true; queue_push_tail(profile->profiles, p); DBG("Added \"%s\"", p->name); return 0; } static void add_profile(void *data, void *user_data) { struct btd_adapter *adapter = user_data; btd_profile_register(data); adapter_add_profile(adapter, data); } static struct external_profile *create_profile(struct gatt_app *app, GDBusProxy *proxy, const char *path) { struct external_profile *profile; DBusMessageIter iter, array; if (!path || !g_str_has_prefix(path, "/")) return NULL; profile = new0(struct external_profile, 1); profile->app = app; profile->proxy = g_dbus_proxy_ref(proxy); profile->profiles = queue_new(); if (!g_dbus_proxy_get_property(proxy, "UUIDs", &iter)) { DBG("UUIDs property not found"); goto fail; } if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_ARRAY) { DBG("UUIDs wrongly formatted"); goto fail; } dbus_message_iter_recurse(&iter, &array); while (dbus_message_iter_get_arg_type(&array) == DBUS_TYPE_STRING) { const char *uuid; dbus_message_iter_get_basic(&array, &uuid); if (profile_add(profile, uuid) < 0) goto fail; dbus_message_iter_next(&array); } if (queue_isempty(profile->profiles)) goto fail; queue_foreach(profile->profiles, add_profile, app->database->adapter); queue_push_tail(app->profiles, profile); return profile; fail: profile_free(profile); return NULL; } static void register_profile(void *data, void *user_data) { struct gatt_app *app = user_data; GDBusProxy *proxy = data; const char *iface = g_dbus_proxy_get_interface(proxy); const char *path = g_dbus_proxy_get_path(proxy); if (app->failed) return; if (g_strcmp0(iface, GATT_PROFILE_IFACE) == 0) { struct external_profile *profile; profile = create_profile(app, proxy, path); if (!profile) { app->failed = true; return; } } } static void register_service(void *data, void *user_data) { struct gatt_app *app = user_data; GDBusProxy *proxy = data; const char *iface = g_dbus_proxy_get_interface(proxy); const char *path = g_dbus_proxy_get_path(proxy); if (app->failed) return; if (g_strcmp0(iface, GATT_SERVICE_IFACE) == 0) { struct external_service *service; service = create_service(app, proxy, path); if (!service) { app->failed = true; return; } } } static void register_characteristic(void *data, void *user_data) { struct gatt_app *app = user_data; GDBusProxy *proxy = data; const char *iface; const char *path; if (app->failed) return; iface = g_dbus_proxy_get_interface(proxy); path = g_dbus_proxy_get_path(proxy); if (g_strcmp0(iface, GATT_CHRC_IFACE) == 0) { struct external_chrc *chrc; chrc = chrc_create(app, proxy, path); if (!chrc) { app->failed = true; return; } } } static void register_descriptor(void *data, void *user_data) { struct gatt_app *app = user_data; GDBusProxy *proxy = data; const char *iface = g_dbus_proxy_get_interface(proxy); if (app->failed) return; if (g_strcmp0(iface, GATT_DESC_IFACE) == 0) { struct external_desc *desc; desc = desc_create(app, proxy); if (!desc) { app->failed = true; return; } } } static void client_ready_cb(GDBusClient *client, void *user_data) { struct gatt_app *app = user_data; DBusMessage *reply; bool fail = false; /* * Process received objects */ if (queue_isempty(app->proxies)) { error("No object received"); fail = true; reply = btd_error_failed(app->reg, "No object received"); goto reply; } queue_foreach(app->proxies, register_profile, app); queue_foreach(app->proxies, register_service, app); queue_foreach(app->proxies, register_characteristic, app); queue_foreach(app->proxies, register_descriptor, app); if ((queue_isempty(app->services) && queue_isempty(app->profiles)) || app->failed) { error("No valid external GATT objects found"); fail = true; reply = btd_error_failed(app->reg, "No valid service object found"); goto reply; } if (!database_add_app(app)) { error("Failed to create GATT service entry in local database"); fail = true; reply = btd_error_failed(app->reg, "Failed to create entry in database"); goto reply; } DBG("GATT application registered: %s:%s", app->owner, app->path); reply = dbus_message_new_method_return(app->reg); reply: g_dbus_send_message(btd_get_dbus_connection(), reply); dbus_message_unref(app->reg); app->reg = NULL; if (fail) remove_app(app); } static struct gatt_app *create_app(DBusConnection *conn, DBusMessage *msg, const char *path) { struct gatt_app *app; const char *sender = dbus_message_get_sender(msg); if (!path || !g_str_has_prefix(path, "/")) return NULL; app = new0(struct gatt_app, 1); app->client = g_dbus_client_new_full(conn, sender, path, path); if (!app->client) goto fail; app->owner = g_strdup(sender); if (!app->owner) goto fail; app->path = g_strdup(path); if (!app->path) goto fail; app->services = queue_new(); app->profiles = queue_new(); app->proxies = queue_new(); app->reg = dbus_message_ref(msg); g_dbus_client_set_disconnect_watch(app->client, client_disconnect_cb, app); g_dbus_client_set_proxy_handlers(app->client, proxy_added_cb, proxy_removed_cb, NULL, app); g_dbus_client_set_ready_watch(app->client, client_ready_cb, app); return app; fail: app_free(app); return NULL; } static DBusMessage *manager_register_app(DBusConnection *conn, DBusMessage *msg, void *user_data) { struct btd_gatt_database *database = user_data; const char *sender = dbus_message_get_sender(msg); DBusMessageIter args; const char *path; struct gatt_app *app; struct svc_match_data match_data; if (!dbus_message_iter_init(msg, &args)) return btd_error_invalid_args(msg); if (dbus_message_iter_get_arg_type(&args) != DBUS_TYPE_OBJECT_PATH) return btd_error_invalid_args(msg); dbus_message_iter_get_basic(&args, &path); match_data.path = path; match_data.sender = sender; if (queue_find(database->apps, match_app, &match_data)) return btd_error_already_exists(msg); dbus_message_iter_next(&args); if (dbus_message_iter_get_arg_type(&args) != DBUS_TYPE_ARRAY) return btd_error_invalid_args(msg); app = create_app(conn, msg, path); if (!app) return btd_error_failed(msg, "Failed to register application"); DBG("Registering application: %s:%s", sender, path); app->database = database; queue_push_tail(database->apps, app); return NULL; } static DBusMessage *manager_unregister_app(DBusConnection *conn, DBusMessage *msg, void *user_data) { struct btd_gatt_database *database = user_data; const char *sender = dbus_message_get_sender(msg); const char *path; DBusMessageIter args; struct gatt_app *app; struct svc_match_data match_data; if (!dbus_message_iter_init(msg, &args)) return btd_error_invalid_args(msg); if (dbus_message_iter_get_arg_type(&args) != DBUS_TYPE_OBJECT_PATH) return btd_error_invalid_args(msg); dbus_message_iter_get_basic(&args, &path); match_data.path = path; match_data.sender = sender; app = queue_remove_if(database->apps, match_app, &match_data); if (!app) return btd_error_does_not_exist(msg); app_free(app); return dbus_message_new_method_return(msg); } static const GDBusMethodTable manager_methods[] = { { GDBUS_ASYNC_METHOD("RegisterApplication", GDBUS_ARGS({ "application", "o" }, { "options", "a{sv}" }), NULL, manager_register_app) }, { GDBUS_ASYNC_METHOD("UnregisterApplication", GDBUS_ARGS({ "application", "o" }), NULL, manager_unregister_app) }, { } }; static uint8_t server_authorize(struct bt_att *att, uint8_t opcode, uint16_t handle, void *user_data) { struct btd_gatt_database *database = user_data; struct device_state *state; bdaddr_t bdaddr; uint8_t bdaddr_type; if (!get_dst_info(att, &bdaddr, &bdaddr_type)) return 0; /* Skip if there is no device state */ state = find_device_state(database, &bdaddr, bdaddr_type); if (!state) return 0; /* Skip if client doesn't support Robust Caching */ if (!(state->cli_feat[0] & BT_GATT_CHRC_CLI_FEAT_ROBUST_CACHING)) return 0; if (state->change_aware) return 0; if (state->out_of_sync) { state->out_of_sync = false; state->change_aware = true; return 0; } state->out_of_sync = true; return BT_ATT_ERROR_DB_OUT_OF_SYNC; } static void eatt_confirm_cb(GIOChannel *io, gpointer data) { char address[18]; uint8_t dst_type; bdaddr_t src, dst; GError *gerr = NULL; struct btd_device *device; struct bt_gatt_server *server; struct bt_att *att; bt_io_get(io, &gerr, BT_IO_OPT_SOURCE_BDADDR, &src, BT_IO_OPT_DEST_BDADDR, &dst, BT_IO_OPT_DEST_TYPE, &dst_type, BT_IO_OPT_DEST, address, BT_IO_OPT_INVALID); if (gerr) { error("bt_io_get: %s", gerr->message); g_error_free(gerr); goto drop; } DBG("New incoming EATT connection"); /* Confirm the device exists before accepting the connection, if the * device is using an RPA it could be that the MGMT event has not been * processed yet which would lead to create a second copy of the same * device using its identity address. */ device = btd_adapter_find_device(adapter_find(&src), &dst, dst_type); if (!device) { error("Unable to find device: %s", address); goto drop; } /* Only allow EATT connection from central */ if (btd_device_is_initiator(device)) { warn("EATT connection from peripheral may cause collisions"); goto drop; } server = btd_device_get_gatt_server(device); if (!server) { error("Unable to resolve bt_server"); goto drop; } att = bt_gatt_server_get_att(server); if (bt_att_get_channels(att) == btd_opts.gatt_channels) { DBG("EATT channel limit reached"); goto drop; } if (!bt_io_accept(io, connect_cb, NULL, NULL, &gerr)) { error("bt_io_accept: %s", gerr->message); g_error_free(gerr); goto drop; } return; drop: g_io_channel_shutdown(io, TRUE, NULL); } struct btd_gatt_database *btd_gatt_database_new(struct btd_adapter *adapter) { struct btd_gatt_database *database; GError *gerr = NULL; const bdaddr_t *addr; if (!adapter) return NULL; database = new0(struct btd_gatt_database, 1); database->adapter = btd_adapter_ref(adapter); database->db = gatt_db_new(); database->records = queue_new(); database->device_states = queue_new(); database->apps = queue_new(); database->profiles = queue_new(); database->ccc_callbacks = queue_new(); addr = btd_adapter_get_address(adapter); database->le_io = bt_io_listen(connect_cb, NULL, NULL, NULL, &gerr, BT_IO_OPT_SOURCE_BDADDR, addr, BT_IO_OPT_SOURCE_TYPE, btd_adapter_get_address_type(adapter), BT_IO_OPT_CID, BT_ATT_CID, BT_IO_OPT_SEC_LEVEL, BT_IO_SEC_LOW, BT_IO_OPT_INVALID); if (!database->le_io) { error("Failed to start listening: %s", gerr->message); g_error_free(gerr); goto fail; } /* If just just 1 channel is enabled EATT is not required */ if (btd_opts.gatt_channels == 1) goto bredr; /* EATT socket, encryption is required */ database->eatt_io = bt_io_listen(NULL, eatt_confirm_cb, NULL, NULL, &gerr, BT_IO_OPT_SOURCE_BDADDR, addr, BT_IO_OPT_SOURCE_TYPE, btd_adapter_get_address_type(adapter), BT_IO_OPT_PSM, BT_ATT_EATT_PSM, BT_IO_OPT_SEC_LEVEL, BT_IO_SEC_MEDIUM, BT_IO_OPT_MTU, btd_opts.gatt_mtu, BT_IO_OPT_INVALID); if (!database->eatt_io) { g_error_free(gerr); goto fail; } bredr: /* BR/EDR socket */ database->bredr_io = bt_io_listen(connect_cb, NULL, NULL, NULL, &gerr, BT_IO_OPT_SOURCE_BDADDR, addr, BT_IO_OPT_PSM, BT_ATT_PSM, BT_IO_OPT_SEC_LEVEL, BT_IO_SEC_MEDIUM, BT_IO_OPT_MTU, btd_opts.gatt_mtu, BT_IO_OPT_INVALID); if (database->bredr_io == NULL) { error("Failed to start listening: %s", gerr->message); g_error_free(gerr); goto fail; } if (g_dbus_register_interface(btd_get_dbus_connection(), adapter_get_path(adapter), GATT_MANAGER_IFACE, manager_methods, NULL, NULL, database, NULL)) DBG("GATT Manager registered for adapter: %s", adapter_get_path(adapter)); register_core_services(database); database->db_id = gatt_db_register(database->db, gatt_db_service_added, gatt_db_service_removed, database, NULL); if (!database->db_id) goto fail; return database; fail: gatt_database_free(database); return NULL; } void btd_gatt_database_destroy(struct btd_gatt_database *database) { if (!database) return; g_dbus_unregister_interface(btd_get_dbus_connection(), adapter_get_path(database->adapter), GATT_MANAGER_IFACE); gatt_database_free(database); } struct gatt_db *btd_gatt_database_get_db(struct btd_gatt_database *database) { if (!database) return NULL; return database->db; } void btd_gatt_database_server_connected(struct btd_gatt_database *database, struct bt_gatt_server *server) { struct bt_att *att = bt_gatt_server_get_att(server); struct device_state *state; bdaddr_t bdaddr; uint8_t bdaddr_type; if (!get_dst_info(att, &bdaddr, &bdaddr_type)) return; bt_gatt_server_set_authorize(server, server_authorize, database); state = find_device_state(database, &bdaddr, bdaddr_type); if (!state || !state->pending) return; send_notification_to_device(state, state->pending); state = find_device_state(database, &bdaddr, bdaddr_type); if (!state || !state->pending) return; free(state->pending->value); free(state->pending); state->pending = NULL; } void btd_gatt_database_att_disconnected(struct btd_gatt_database *database, struct btd_device *device) { struct bt_gatt_server *server = btd_device_get_gatt_server(device); struct bt_att *att = bt_gatt_server_get_att(server); struct device_state *state; const bdaddr_t *addr; uint8_t type; DBG(""); addr = device_get_address(device); type = btd_device_get_bdaddr_type(device); state = find_device_state(database, addr, type); if (!state) return; if (state->disc_id) bt_att_unregister_disconnect(att, state->disc_id); att_disconnected(0, state); } static void restore_ccc(struct btd_gatt_database *database, const bdaddr_t *addr, uint8_t addr_type, uint16_t value) { struct device_state *dev_state; struct ccc_state *ccc; dev_state = device_state_create(database, addr, addr_type); queue_push_tail(database->device_states, dev_state); ccc = new0(struct ccc_state, 1); ccc->handle = gatt_db_attribute_get_handle(database->svc_chngd_ccc); ccc->value = value; queue_push_tail(dev_state->ccc_states, ccc); } static void restore_state(struct btd_device *device, void *data) { struct btd_gatt_database *database = data; uint16_t ccc_le, ccc_bredr; device_load_svc_chng_ccc(device, &ccc_le, &ccc_bredr); if (ccc_le) { restore_ccc(database, device_get_address(device), device_get_le_address_type(device), ccc_le); DBG("%s LE", device_get_path(device)); } if (ccc_bredr) { restore_ccc(database, device_get_address(device), BDADDR_BREDR, ccc_bredr); DBG("%s BR/EDR", device_get_path(device)); } } void btd_gatt_database_restore_svc_chng_ccc(struct btd_gatt_database *database) { uint8_t value[4]; uint16_t handle, ccc_handle; if (!database) return; handle = gatt_db_attribute_get_handle(database->svc_chngd); ccc_handle = gatt_db_attribute_get_handle(database->svc_chngd_ccc); if (!handle || !ccc_handle) { error("Failed to obtain handles for \"Service Changed\"" " characteristic"); return; } /* restore states for bonded device that registered for Service Changed * indication */ btd_adapter_for_each_device(database->adapter, restore_state, database); put_le16(0x0001, value); put_le16(0xffff, value + 2); if (!gatt_db_attribute_notify(database->svc_chngd, value, sizeof(value), NULL)) error("Failed to notify Service Changed"); } bluez-5.82/src/PaxHeaders/sdpd-server.c0000644000000000000000000000005014165411566015050 xustar0020 atime=1743516608 20 ctime=1743591278 bluez-5.82/src/sdpd-server.c0000644000000000000000000001332114165411566014531 0ustar00rootroot// SPDX-License-Identifier: GPL-2.0-or-later /* * * BlueZ - Bluetooth protocol stack for Linux * * Copyright (C) 2001-2002 Nokia Corporation * Copyright (C) 2002-2003 Maxim Krasnyansky * Copyright (C) 2002-2010 Marcel Holtmann * Copyright (C) 2002-2003 Stephen Crane * * */ #ifdef HAVE_CONFIG_H #include #endif #define _GNU_SOURCE #include #include #include #include #include #include #include #include "lib/bluetooth.h" #include "lib/l2cap.h" #include "lib/sdp.h" #include "lib/sdp_lib.h" #include "log.h" #include "sdpd.h" static guint l2cap_id = 0, unix_id = 0; static int l2cap_sock = -1, unix_sock = -1; /* * SDP server initialization on startup includes creating the * l2cap and unix sockets over which discovery and registration clients * access us respectively */ static int init_server(uint16_t mtu, int central, int compat) { struct l2cap_options opts; struct sockaddr_l2 l2addr; struct sockaddr_un unaddr; socklen_t optlen; /* Register the public browse group root */ register_public_browse_group(); /* Register the SDP server's service record */ register_server_service(); /* Create L2CAP socket */ l2cap_sock = socket(PF_BLUETOOTH, SOCK_SEQPACKET, BTPROTO_L2CAP); if (l2cap_sock < 0) { error("opening L2CAP socket: %s", strerror(errno)); return -1; } memset(&l2addr, 0, sizeof(l2addr)); l2addr.l2_family = AF_BLUETOOTH; bacpy(&l2addr.l2_bdaddr, BDADDR_ANY); l2addr.l2_psm = htobs(SDP_PSM); if (bind(l2cap_sock, (struct sockaddr *) &l2addr, sizeof(l2addr)) < 0) { error("binding L2CAP socket: %s", strerror(errno)); return -1; } if (central) { int opt = L2CAP_LM_MASTER; if (setsockopt(l2cap_sock, SOL_L2CAP, L2CAP_LM, &opt, sizeof(opt)) < 0) { error("setsockopt: %s", strerror(errno)); return -1; } } if (mtu > 0) { memset(&opts, 0, sizeof(opts)); optlen = sizeof(opts); if (getsockopt(l2cap_sock, SOL_L2CAP, L2CAP_OPTIONS, &opts, &optlen) < 0) { error("getsockopt: %s", strerror(errno)); return -1; } opts.omtu = mtu; opts.imtu = mtu; if (setsockopt(l2cap_sock, SOL_L2CAP, L2CAP_OPTIONS, &opts, sizeof(opts)) < 0) { error("setsockopt: %s", strerror(errno)); return -1; } } if (listen(l2cap_sock, 5) < 0) { error("listen: %s", strerror(errno)); return -1; } if (!compat) { unix_sock = -1; return 0; } /* Create local Unix socket */ unix_sock = socket(PF_UNIX, SOCK_STREAM, 0); if (unix_sock < 0) { error("opening UNIX socket: %s", strerror(errno)); return -1; } memset(&unaddr, 0, sizeof(unaddr)); unaddr.sun_family = AF_UNIX; strcpy(unaddr.sun_path, SDP_UNIX_PATH); unlink(unaddr.sun_path); if (bind(unix_sock, (struct sockaddr *) &unaddr, sizeof(unaddr)) < 0) { error("binding UNIX socket: %s", strerror(errno)); return -1; } if (listen(unix_sock, 5) < 0) { error("listen UNIX socket: %s", strerror(errno)); return -1; } chmod(SDP_UNIX_PATH, 0660); return 0; } static gboolean io_session_event(GIOChannel *chan, GIOCondition cond, gpointer data) { sdp_pdu_hdr_t hdr; uint8_t *buf; int sk, len, size; if (cond & G_IO_NVAL) return FALSE; sk = g_io_channel_unix_get_fd(chan); if (cond & (G_IO_HUP | G_IO_ERR)) goto cleanup; len = recv(sk, &hdr, sizeof(sdp_pdu_hdr_t), MSG_PEEK); if (len < 0 || (unsigned int) len < sizeof(sdp_pdu_hdr_t)) goto cleanup; size = sizeof(sdp_pdu_hdr_t) + ntohs(hdr.plen); buf = malloc(size); if (!buf) return TRUE; len = recv(sk, buf, size, 0); /* Check here only that the received message is not empty. * Incorrect length of message should be processed later * inside handle_request() in order to produce ErrorResponse. */ if (len <= 0) { free(buf); goto cleanup; } handle_request(sk, buf, len); return TRUE; cleanup: sdp_svcdb_collect_all(sk); sdp_cstate_cleanup(sk); return FALSE; } static gboolean io_accept_event(GIOChannel *chan, GIOCondition cond, gpointer data) { GIOChannel *io; int nsk; if (cond & (G_IO_HUP | G_IO_ERR | G_IO_NVAL)) return FALSE; if (data == &l2cap_sock) { struct sockaddr_l2 addr; socklen_t len = sizeof(addr); nsk = accept(l2cap_sock, (struct sockaddr *) &addr, &len); } else if (data == &unix_sock) { struct sockaddr_un addr; socklen_t len = sizeof(addr); nsk = accept(unix_sock, (struct sockaddr *) &addr, &len); } else return FALSE; if (nsk < 0) { error("Can't accept connection: %s", strerror(errno)); return TRUE; } io = g_io_channel_unix_new(nsk); g_io_channel_set_close_on_unref(io, TRUE); g_io_add_watch(io, G_IO_IN | G_IO_ERR | G_IO_HUP | G_IO_NVAL, io_session_event, data); g_io_channel_unref(io); return TRUE; } int start_sdp_server(uint16_t mtu, uint32_t flags) { int compat = flags & SDP_SERVER_COMPAT; int central = flags & SDP_SERVER_CENTRAL; GIOChannel *io; info("Starting SDP server"); if (init_server(mtu, central, compat) < 0) { error("Server initialization failed"); return -1; } io = g_io_channel_unix_new(l2cap_sock); g_io_channel_set_close_on_unref(io, TRUE); l2cap_id = g_io_add_watch(io, G_IO_IN | G_IO_ERR | G_IO_HUP | G_IO_NVAL, io_accept_event, &l2cap_sock); g_io_channel_unref(io); if (compat && unix_sock > fileno(stderr)) { io = g_io_channel_unix_new(unix_sock); g_io_channel_set_close_on_unref(io, TRUE); unix_id = g_io_add_watch(io, G_IO_IN | G_IO_ERR | G_IO_HUP | G_IO_NVAL, io_accept_event, &unix_sock); g_io_channel_unref(io); } return 0; } void stop_sdp_server(void) { info("Stopping SDP server"); sdp_svcdb_reset(); if (unix_id > 0) g_source_remove(unix_id); if (l2cap_id > 0) g_source_remove(l2cap_id); l2cap_id = unix_id = 0; l2cap_sock = unix_sock = -1; } bluez-5.82/src/PaxHeaders/gatt-client.h0000644000000000000000000000005014131623652015024 xustar0020 atime=1743516664 20 ctime=1743591285 bluez-5.82/src/gatt-client.h0000644000000000000000000000177214131623652014514 0ustar00rootroot/* SPDX-License-Identifier: GPL-2.0-or-later */ /* * * BlueZ - Bluetooth protocol stack for Linux * * Copyright (C) 2014 Google Inc. * * */ struct btd_gatt_client; struct btd_gatt_client *btd_gatt_client_new(struct btd_device *device); void btd_gatt_client_destroy(struct btd_gatt_client *client); void btd_gatt_client_ready(struct btd_gatt_client *client); void btd_gatt_client_connected(struct btd_gatt_client *client); void btd_gatt_client_service_added(struct btd_gatt_client *client, struct gatt_db_attribute *attrib); void btd_gatt_client_service_removed(struct btd_gatt_client *client, struct gatt_db_attribute *attrib); void btd_gatt_client_disconnected(struct btd_gatt_client *client); void btd_gatt_client_eatt_connect(struct btd_gatt_client *client); typedef void (*btd_gatt_client_service_path_t)(const char *service_path, void *user_data); void btd_gatt_client_foreach_service(struct btd_gatt_client *client, btd_gatt_client_service_path_t func, void *user_data); bluez-5.82/src/PaxHeaders/log.h0000644000000000000000000000005014267331527013401 xustar0020 atime=1743515935 20 ctime=1743591278 bluez-5.82/src/log.h0000644000000000000000000000361414267331527013066 0ustar00rootroot/* SPDX-License-Identifier: GPL-2.0-or-later */ /* * * BlueZ - Bluetooth protocol stack for Linux * * Copyright (C) 2004-2010 Marcel Holtmann * * */ #include #include void info(const char *format, ...) __attribute__((format(printf, 1, 2))); void btd_log(uint16_t index, int priority, const char *format, ...) __attribute__((format(printf, 3, 4))); void btd_error(uint16_t index, const char *format, ...) __attribute__((format(printf, 2, 3))); void btd_warn(uint16_t index, const char *format, ...) __attribute__((format(printf, 2, 3))); void btd_info(uint16_t index, const char *format, ...) __attribute__((format(printf, 2, 3))); void btd_debug(uint16_t index, const char *format, ...) __attribute__((format(printf, 2, 3))); void __btd_log_init(const char *debug, int detach); void __btd_log_cleanup(void); void __btd_toggle_debug(void); struct btd_debug_desc { const char *file; #define BTD_DEBUG_FLAG_DEFAULT (0) #define BTD_DEBUG_FLAG_PRINT (1 << 0) unsigned int flags; } __attribute__((aligned(8))); void __btd_enable_debug(struct btd_debug_desc *start, struct btd_debug_desc *stop); /** * DBG: * @fmt: format string * @arg...: list of arguments * * Simple macro around btd_debug() which also include the function * name it is called in. */ #define DBG_IDX(idx, fmt, arg...) do { \ static struct btd_debug_desc __btd_debug_desc \ __attribute__((used, section("__debug"), aligned(8))) = { \ .file = __FILE__, .flags = BTD_DEBUG_FLAG_DEFAULT, \ }; \ if (__btd_debug_desc.flags & BTD_DEBUG_FLAG_PRINT) \ btd_debug(idx, fmt, ## arg); \ } while (0) #define DBG(fmt, arg...) \ DBG_IDX(0xffff, "%s:%s() " fmt, __FILE__, __func__, ## arg) #define error(fmt, arg...) \ btd_error(0xffff, "%s:%s() " fmt, __FILE__, __func__, ## arg) #define warn(fmt, arg...) \ btd_warn(0xffff, "%s:%s() " fmt, __FILE__, __func__, ## arg) bluez-5.82/src/PaxHeaders/plugin.h0000644000000000000000000000005014572354773014125 xustar0020 atime=1743516498 20 ctime=1743591284 bluez-5.82/src/plugin.h0000644000000000000000000000256214572354773013613 0ustar00rootroot/* SPDX-License-Identifier: GPL-2.0-or-later */ /* * * BlueZ - Bluetooth protocol stack for Linux * * Copyright (C) 2004-2010 Marcel Holtmann * * */ #define BLUETOOTH_PLUGIN_PRIORITY_LOW -100 #define BLUETOOTH_PLUGIN_PRIORITY_DEFAULT 0 #define BLUETOOTH_PLUGIN_PRIORITY_HIGH 100 struct bluetooth_plugin_desc { const char *name; const char *version; int priority; int (*init) (void); void (*exit) (void); void *debug_start; void *debug_stop; }; #ifdef BLUETOOTH_PLUGIN_BUILTIN #define BLUETOOTH_PLUGIN_DEFINE(name, version, priority, init, exit) \ const struct bluetooth_plugin_desc \ __bluetooth_builtin_ ## name = { \ #name, version, priority, init, exit \ }; #else #if EXTERNAL_PLUGINS #define BLUETOOTH_PLUGIN_DEFINE(name, version, priority, init, exit) \ extern struct btd_debug_desc __start___debug[] \ __attribute__ ((weak, visibility("hidden"))); \ extern struct btd_debug_desc __stop___debug[] \ __attribute__ ((weak, visibility("hidden"))); \ extern const struct bluetooth_plugin_desc \ bluetooth_plugin_desc \ __attribute__ ((visibility("default"))); \ const struct bluetooth_plugin_desc bluetooth_plugin_desc = { \ #name, version, priority, init, exit, \ __start___debug, __stop___debug \ }; #else #error "Requested non built-in plugin, while external plugins is disabled" #endif #endif bluez-5.82/src/PaxHeaders/sdp-client.h0000644000000000000000000000005014015011623014641 xustar0020 atime=1743516558 20 ctime=1743591279 bluez-5.82/src/sdp-client.h0000644000000000000000000000137014015011623014323 0ustar00rootroot/* SPDX-License-Identifier: GPL-2.0-or-later */ /* * * BlueZ - Bluetooth protocol stack for Linux * * Copyright (C) 2004-2010 Marcel Holtmann * * */ typedef void (*bt_callback_t) (sdp_list_t *recs, int err, gpointer user_data); typedef void (*bt_destroy_t) (gpointer user_data); int bt_search(const bdaddr_t *src, const bdaddr_t *dst, uuid_t *uuid, bt_callback_t cb, void *user_data, bt_destroy_t destroy, uint16_t flags); int bt_search_service(const bdaddr_t *src, const bdaddr_t *dst, uuid_t *uuid, bt_callback_t cb, void *user_data, bt_destroy_t destroy, uint16_t flags); int bt_cancel_discovery(const bdaddr_t *src, const bdaddr_t *dst); void bt_clear_cached_session(const bdaddr_t *src, const bdaddr_t *dst); bluez-5.82/src/PaxHeaders/set.h0000644000000000000000000000005014621503015013376 xustar0020 atime=1743516668 20 ctime=1743591285 bluez-5.82/src/set.h0000644000000000000000000000102314621503015013053 0ustar00rootroot/* SPDX-License-Identifier: GPL-2.0-or-later */ /* * * BlueZ - Bluetooth protocol stack for Linux * * Copyright (C) 2023 Intel Corporation * * */ #define BTD_DEVICE_SET_INTERFACE "org.bluez.DeviceSet1" struct btd_device_set; struct btd_device_set *btd_set_add_device(struct btd_device *device, const uint8_t *ltk, const uint8_t sirk[16], uint8_t size); bool btd_set_remove_device(struct btd_device_set *set, struct btd_device *device); const char *btd_set_get_path(struct btd_device_set *set); bluez-5.82/src/PaxHeaders/settings.h0000644000000000000000000000005014267331527014460 xustar0020 atime=1743516062 20 ctime=1743591283 bluez-5.82/src/settings.h0000644000000000000000000000046014267331527014141 0ustar00rootroot/* SPDX-License-Identifier: LGPL-2.1-or-later */ /* * * BlueZ - Bluetooth protocol stack for Linux * * Copyright (C) 2022 Intel Corporation. * */ int btd_settings_gatt_db_load(struct gatt_db *db, const char *filename); void btd_settings_gatt_db_store(struct gatt_db *db, const char *filename); bluez-5.82/src/PaxHeaders/shared0000644000000000000000000000005014773213565013643 xustar0020 atime=1743591291 20 ctime=1743591285 bluez-5.82/src/shared/0000755000000000000000000000000014773213565013401 5ustar00rootrootbluez-5.82/src/shared/PaxHeaders/queue.c0000644000000000000000000000005014015011623015164 xustar0020 atime=1743515856 20 ctime=1743591277 bluez-5.82/src/shared/queue.c0000644000000000000000000001320414015011623014645 0ustar00rootroot// SPDX-License-Identifier: LGPL-2.1-or-later /* * * BlueZ - Bluetooth protocol stack for Linux * * Copyright (C) 2012-2014 Intel Corporation. All rights reserved. * * */ #ifdef HAVE_CONFIG_H #include #endif #include "src/shared/util.h" #include "src/shared/queue.h" struct queue { int ref_count; struct queue_entry *head; struct queue_entry *tail; unsigned int entries; }; static struct queue *queue_ref(struct queue *queue) { if (!queue) return NULL; __sync_fetch_and_add(&queue->ref_count, 1); return queue; } static void queue_unref(struct queue *queue) { if (__sync_sub_and_fetch(&queue->ref_count, 1)) return; free(queue); } struct queue *queue_new(void) { struct queue *queue; queue = new0(struct queue, 1); queue->head = NULL; queue->tail = NULL; queue->entries = 0; return queue_ref(queue); } void queue_destroy(struct queue *queue, queue_destroy_func_t destroy) { if (!queue) return; queue_remove_all(queue, NULL, NULL, destroy); queue_unref(queue); } static struct queue_entry *queue_entry_new(void *data) { struct queue_entry *entry; entry = new0(struct queue_entry, 1); entry->data = data; return entry; } bool queue_push_tail(struct queue *queue, void *data) { struct queue_entry *entry; if (!queue) return false; entry = queue_entry_new(data); if (queue->tail) queue->tail->next = entry; queue->tail = entry; if (!queue->head) queue->head = entry; queue->entries++; return true; } bool queue_push_head(struct queue *queue, void *data) { struct queue_entry *entry; if (!queue) return false; entry = queue_entry_new(data); entry->next = queue->head; queue->head = entry; if (!queue->tail) queue->tail = entry; queue->entries++; return true; } bool queue_push_after(struct queue *queue, void *entry, void *data) { struct queue_entry *qentry, *tmp, *new_entry; qentry = NULL; if (!queue) return false; for (tmp = queue->head; tmp; tmp = tmp->next) { if (tmp->data == entry) { qentry = tmp; break; } } if (!qentry) return false; new_entry = queue_entry_new(data); new_entry->next = qentry->next; if (!qentry->next) queue->tail = new_entry; qentry->next = new_entry; queue->entries++; return true; } void *queue_pop_head(struct queue *queue) { struct queue_entry *entry; void *data; if (!queue || !queue->head) return NULL; entry = queue->head; if (!queue->head->next) { queue->head = NULL; queue->tail = NULL; } else queue->head = queue->head->next; data = entry->data; free(entry); queue->entries--; return data; } void *queue_peek_head(struct queue *queue) { if (!queue || !queue->head) return NULL; return queue->head->data; } void *queue_peek_tail(struct queue *queue) { if (!queue || !queue->tail) return NULL; return queue->tail->data; } void queue_foreach(struct queue *queue, queue_foreach_func_t function, void *user_data) { struct queue_entry *entry; if (!queue || !function) return; entry = queue->head; if (!entry) return; queue_ref(queue); while (entry && queue->head && queue->ref_count > 1) { struct queue_entry *next; next = entry->next; function(entry->data, user_data); entry = next; } queue_unref(queue); } static bool direct_match(const void *a, const void *b) { return a == b; } void *queue_find(struct queue *queue, queue_match_func_t function, const void *match_data) { struct queue_entry *entry; if (!queue) return NULL; if (!function) function = direct_match; for (entry = queue->head; entry; entry = entry->next) if (function(entry->data, match_data)) return entry->data; return NULL; } bool queue_remove(struct queue *queue, void *data) { struct queue_entry *entry, *prev; if (!queue) return false; for (entry = queue->head, prev = NULL; entry; prev = entry, entry = entry->next) { if (entry->data != data) continue; if (prev) prev->next = entry->next; else queue->head = entry->next; if (!entry->next) queue->tail = prev; free(entry); queue->entries--; return true; } return false; } void *queue_remove_if(struct queue *queue, queue_match_func_t function, void *user_data) { struct queue_entry *entry, *prev = NULL; if (!queue) return NULL; if (!function) function = direct_match; entry = queue->head; while (entry) { if (function(entry->data, user_data)) { void *data; if (prev) prev->next = entry->next; else queue->head = entry->next; if (!entry->next) queue->tail = prev; data = entry->data; free(entry); queue->entries--; return data; } else { prev = entry; entry = entry->next; } } return NULL; } unsigned int queue_remove_all(struct queue *queue, queue_match_func_t function, void *user_data, queue_destroy_func_t destroy) { struct queue_entry *entry; unsigned int count = 0; if (!queue) return 0; entry = queue->head; if (function) { while (entry) { void *data; unsigned int entries = queue->entries; data = queue_remove_if(queue, function, user_data); if (entries == queue->entries) break; if (destroy) destroy(data); count++; } } else { queue->head = NULL; queue->tail = NULL; queue->entries = 0; while (entry) { struct queue_entry *tmp = entry; entry = entry->next; if (destroy) destroy(tmp->data); free(tmp); count++; } } return count; } const struct queue_entry *queue_get_entries(struct queue *queue) { if (!queue) return NULL; return queue->head; } unsigned int queue_length(struct queue *queue) { if (!queue) return 0; return queue->entries; } bool queue_isempty(struct queue *queue) { if (!queue) return true; return queue->entries == 0; } bluez-5.82/src/shared/PaxHeaders/tester.h0000644000000000000000000000005014621503015015357 xustar0020 atime=1743515945 20 ctime=1743591277 bluez-5.82/src/shared/tester.h0000644000000000000000000000445314621503015015046 0ustar00rootroot/* SPDX-License-Identifier: LGPL-2.1-or-later */ /* * * BlueZ - Bluetooth protocol stack for Linux * * Copyright (C) 2012-2014 Intel Corporation. All rights reserved. * * */ #include #include #include #include #define data(args...) ((const unsigned char[]) { args }) #define IOV_DATA(args...) \ { \ .iov_base = (void *)data(args), \ .iov_len = sizeof(data(args)), \ } #define IOV_NULL {} void tester_init(int *argc, char ***argv); int tester_run(void); bool tester_use_quiet(void); bool tester_use_debug(void); void tester_print(const char *format, ...) __attribute__((format(printf, 1, 2))); void tester_warn(const char *format, ...) __attribute__((format(printf, 1, 2))); void tester_debug(const char *format, ...) __attribute__((format(printf, 1, 2))); void tester_monitor(char dir, uint16_t cid, uint16_t psm, const void *data, size_t len); typedef void (*tester_destroy_func_t)(void *user_data); typedef void (*tester_data_func_t)(const void *test_data); void tester_add_full(const char *name, const void *test_data, tester_data_func_t pre_setup_func, tester_data_func_t setup_func, tester_data_func_t test_func, tester_data_func_t teardown_func, tester_data_func_t post_teardown_func, unsigned int timeout, void *user_data, tester_destroy_func_t destroy); void tester_add(const char *name, const void *test_data, tester_data_func_t setup_func, tester_data_func_t test_func, tester_data_func_t teardown_func); void *tester_get_data(void); void tester_pre_setup_complete(void); void tester_pre_setup_failed(void); void tester_pre_setup_abort(void); bool tester_pre_setup_skip_by_default(void); void tester_setup_complete(void); void tester_setup_failed(void); void tester_test_passed(void); void tester_test_failed(void); void tester_test_abort(void); void tester_teardown_complete(void); void tester_teardown_failed(void); void tester_post_teardown_complete(void); void tester_post_teardown_failed(void); typedef void (*tester_wait_func_t)(void *user_data); void tester_wait(unsigned int seconds, tester_wait_func_t func, void *user_data); struct io *tester_setup_io(const struct iovec *iov, int iovcnt); void tester_io_send(void); void tester_io_set_complete_func(tester_data_func_t func); bluez-5.82/src/shared/PaxHeaders/vcp.h0000644000000000000000000000005014766002272014652 xustar0020 atime=1743515578 20 ctime=1743591277 bluez-5.82/src/shared/vcp.h0000644000000000000000000000355314766002272014341 0ustar00rootroot/* SPDX-License-Identifier: LGPL-2.1-or-later */ /* * * BlueZ - Bluetooth protocol stack for Linux * * Copyright (C) 2020 Intel Corporation. All rights reserved. * */ #include #include #include "src/shared/io.h" #define BT_VCP_RENDERER 0x01 #define BT_VCP_CONTROLLER 0x02 #define BT_VCP_RELATIVE_VOL_DOWN 0x00 #define BT_VCP_RELATIVE_VOL_UP 0x01 #define BT_VCP_UNMUTE_RELATIVE_VOL_DOWN 0x02 #define BT_VCP_UNMUTE_RELATIVE_VOL_UP 0x03 #define BT_VCP_SET_ABOSULTE_VOL 0x04 #define BT_VCP_UNMUTE 0x05 #define BT_VCP_MUTE 0x06 #ifndef MAX #define MAX(a, b) ((a) > (b) ? (a) : (b)) #endif #ifndef MIN #define MIN(a, b) ((a) < (b) ? (a) : (b)) #endif struct bt_vcp; typedef void (*bt_vcp_destroy_func_t)(void *user_data); typedef void (*bt_vcp_debug_func_t)(const char *str, void *user_data); typedef void (*bt_vcp_func_t)(struct bt_vcp *vcp, void *user_data); typedef void (*bt_vcp_volume_func_t)(struct bt_vcp *vcp, uint8_t volume); struct bt_vcp *bt_vcp_ref(struct bt_vcp *vcp); void bt_vcp_unref(struct bt_vcp *vcp); void bt_vcp_add_db(struct gatt_db *db); bool bt_vcp_attach(struct bt_vcp *vcp, struct bt_gatt_client *client); void bt_vcp_detach(struct bt_vcp *vcp); uint8_t bt_vcp_get_volume(struct bt_vcp *vcp); bool bt_vcp_set_volume(struct bt_vcp *vcp, uint8_t volume); bool bt_vcp_set_debug(struct bt_vcp *vcp, bt_vcp_debug_func_t cb, void *user_data, bt_vcp_destroy_func_t destroy); bool bt_vcp_set_volume_callback(struct bt_vcp *vcp, bt_vcp_volume_func_t volume_changed); struct bt_att *bt_vcp_get_att(struct bt_vcp *vcp); bool bt_vcp_set_user_data(struct bt_vcp *vcp, void *user_data); /* Session related function */ unsigned int bt_vcp_register(bt_vcp_func_t added, bt_vcp_func_t removed, void *user_data); bool bt_vcp_unregister(unsigned int id); struct bt_vcp *bt_vcp_new(struct gatt_db *ldb, struct gatt_db *rdb); bluez-5.82/src/shared/PaxHeaders/micp.c0000644000000000000000000000005014667536076015023 xustar0020 atime=1743515925 20 ctime=1743591277 bluez-5.82/src/shared/micp.c0000644000000000000000000004414014667536076014507 0ustar00rootroot// SPDX-License-Identifier: LGPL-2.1-or-later /* * * BlueZ - Bluetooth protocol stack for Linux * * Copyright (C) 2023 NXP Semiconductors. All rights reserved. * */ #define _GNU_SOURCE #include #include #include #include #include #include #include "lib/bluetooth.h" #include "lib/uuid.h" #include "src/shared/queue.h" #include "src/shared/util.h" #include "src/shared/timeout.h" #include "src/shared/att.h" #include "src/shared/gatt-db.h" #include "src/shared/gatt-server.h" #include "src/shared/gatt-helpers.h" #include "src/shared/micp.h" #define DBG(_micp, fmt, arg...) \ micp_debug(_micp, "%s:%s() " fmt, __FILE__, __func__, ##arg) /* Application error codes */ #define MICP_ERROR_MUTE_DISABLED 0x80 #define MICP_ERROR_VALUE_NOT_ALLOWED 0x13 #define BT_ATT_ERROR_OPCODE_NOT_SUPPORTED 0x81 /* Mute char values */ #define MICS_NOT_MUTED 0x00 #define MICS_MUTED 0x01 #define MICS_DISABLED 0x02 static struct queue *micp_db; static struct queue *micp_cbs; static struct queue *sessions; struct bt_micp_cb { unsigned int id; bt_micp_func_t attached; bt_micp_func_t detached; void *user_data; }; typedef void (*micp_func_t)(struct bt_micp *micp, bool success, uint8_t att_ecode, const uint8_t *value, uint16_t length, void *user_data); struct bt_micp_pending { unsigned int id; struct bt_micp *micp; micp_func_t func; void *userdata; }; struct bt_micp_ready { unsigned int id; bt_micp_ready_func_t func; bt_micp_destroy_func_t destroy; void *data; }; typedef void (*micp_notify_t)(struct bt_micp *micp, uint16_t value_handle, const uint8_t *value, uint16_t length, void *user_data); struct bt_micp_notify { unsigned int id; struct bt_micp *micp; micp_notify_t func; void *user_data; }; static void *iov_pull_mem(struct iovec *iov, size_t len) { void *data = iov->iov_base; if (iov->iov_len < len) return NULL; iov->iov_base += len; iov->iov_len -= len; return data; } static struct bt_micp_db *micp_get_mdb(struct bt_micp *micp) { if (!micp) return NULL; if (micp->ldb) return micp->ldb; return NULL; } static uint8_t *mdb_get_mute_state(struct bt_micp_db *vdb) { if (!vdb->mics) return NULL; return &(vdb->mics->mute_stat); } struct bt_mics *micp_get_mics(struct bt_micp *micp) { if (!micp) return NULL; if (micp->rdb->mics) return micp->rdb->mics; micp->rdb->mics = new0(struct bt_mics, 1); micp->rdb->mics->mdb = micp->rdb; return micp->rdb->mics; } static void micp_detached(void *data, void *user_data) { struct bt_micp_cb *cb = data; struct bt_micp *micp = user_data; cb->detached(micp, cb->user_data); } void bt_micp_detach(struct bt_micp *micp) { if (!queue_remove(sessions, micp)) return; bt_gatt_client_idle_unregister(micp->client, micp->idle_id); bt_gatt_client_unref(micp->client); micp->client = NULL; queue_foreach(micp_cbs, micp_detached, micp); } static void micp_db_free(void *data) { struct bt_micp_db *mdb = data; if (!mdb) return; gatt_db_unref(mdb->db); free(mdb->mics); free(mdb); } static void micp_ready_free(void *data) { struct bt_micp_ready *ready = data; if (ready->destroy) ready->destroy(ready->data); free(ready); } static void micp_free(void *data) { struct bt_micp *micp = data; bt_micp_detach(micp); micp_db_free(micp->rdb); queue_destroy(micp->notify, free); queue_destroy(micp->pending, NULL); queue_destroy(micp->ready_cbs, micp_ready_free); free(micp); } bool bt_micp_set_user_data(struct bt_micp *micp, void *user_data) { if (!micp) return false; micp->user_data = user_data; return true; } static bool micp_db_match(const void *data, const void *match_data) { const struct bt_micp_db *mdb = data; const struct gatt_db *db = match_data; return (mdb->db == db); } struct bt_att *bt_micp_get_att(struct bt_micp *micp) { if (!micp) return NULL; if (micp->att) return micp->att; return bt_gatt_client_get_att(micp->client); } struct bt_micp *bt_micp_ref(struct bt_micp *micp) { if (!micp) return NULL; __sync_fetch_and_add(&micp->ref_count, 1); return micp; } void bt_micp_unref(struct bt_micp *micp) { if (!micp) return; if (__sync_sub_and_fetch(&micp->ref_count, 1)) return; micp_free(micp); } static void micp_debug(struct bt_micp *micp, const char *format, ...) { va_list ap; if (!micp || !format || !micp->debug_func) return; va_start(ap, format); util_debug_va(micp->debug_func, micp->debug_data, format, ap); va_end(ap); } static void micp_disconnected(int err, void *user_data) { struct bt_micp *micp = user_data; DBG(micp, "micp %p disconnected err %d", micp, err); bt_micp_detach(micp); } static struct bt_micp *micp_get_session(struct bt_att *att, struct gatt_db *db) { const struct queue_entry *entry; struct bt_micp *micp; for (entry = queue_get_entries(sessions); entry; entry = entry->next) { struct bt_micp *micp = entry->data; if (att == bt_micp_get_att(micp)) return micp; } micp = bt_micp_new(db, NULL); micp->att = att; bt_att_register_disconnect(att, micp_disconnected, micp, NULL); bt_micp_attach(micp, NULL); return micp; } static void mics_mute_read(struct gatt_db_attribute *attrib, unsigned int id, uint16_t offset, uint8_t opcode, struct bt_att *att, void *user_data) { struct bt_mics *mics = user_data; struct iovec iov; iov.iov_base = &mics->mute_stat; iov.iov_len = sizeof(mics->mute_stat); gatt_db_attribute_read_result(attrib, id, 0, iov.iov_base, iov.iov_len); } static uint8_t mics_not_muted(struct bt_mics *mics, struct bt_micp *micp, struct iovec *iov) { struct bt_micp_db *mdb; uint8_t *mute_state; DBG(micp, "Mute state OP: Not Muted"); mdb = micp_get_mdb(micp); if (!mdb) { DBG(micp, "error: MDB not available"); return 0; } mute_state = mdb_get_mute_state(mdb); if (!mute_state) { DBG(micp, "Error : Mute State not available"); return 0; } *mute_state = MICS_NOT_MUTED; gatt_db_attribute_notify(mdb->mics->ms, (void *)mute_state, sizeof(uint8_t), bt_micp_get_att(micp)); return 0; } static uint8_t mics_muted(struct bt_mics *mics, struct bt_micp *micp, struct iovec *iov) { struct bt_micp_db *mdb; uint8_t *mute_state; DBG(micp, "Mute state OP: Muted"); mdb = micp_get_mdb(micp); if (!mdb) { DBG(micp, "error: MDB not available"); return 0; } mute_state = mdb_get_mute_state(mdb); *mute_state = MICS_MUTED; gatt_db_attribute_notify(mdb->mics->ms, (void *)mute_state, sizeof(uint8_t), bt_micp_get_att(micp)); return 0; } #define MICS_OP(_str, _op, _size, _func) \ { \ .str = _str, \ .op = _op, \ .size = _size, \ .func = _func, \ } struct mics_op_handler { const char *str; uint8_t op; size_t size; uint8_t (*func)(struct bt_mics *mics, struct bt_micp *micp, struct iovec *iov); } micp_handlers[] = { MICS_OP("Not Muted", MICS_NOT_MUTED, sizeof(uint8_t), mics_not_muted), MICS_OP("Muted", MICS_MUTED, sizeof(uint8_t), mics_muted), {}}; static void mics_mute_write(struct gatt_db_attribute *attrib, unsigned int id, uint16_t offset, const uint8_t *value, size_t len, uint8_t opcode, struct bt_att *att, void *user_data) { struct bt_mics *mics = user_data; struct bt_micp *micp = micp_get_session(att, mics->mdb->db); struct iovec iov = { .iov_base = (void *)value, .iov_len = len, }; uint8_t *micp_op, *mute_state; struct mics_op_handler *handler; uint8_t ret = BT_ATT_ERROR_REQUEST_NOT_SUPPORTED; struct bt_micp_db *mdb; DBG(micp, "MICS Mute Char write: len: %ld: %ld", len, iov.iov_len); if (offset) { DBG(micp, "invalid offset: %d", offset); ret = BT_ATT_ERROR_INVALID_OFFSET; goto respond; } if (len < sizeof(*micp_op)) { DBG(micp, "invalid length: %ld < %ld sizeof(param)", len, sizeof(*micp_op)); ret = BT_ATT_ERROR_INVALID_ATTRIBUTE_VALUE_LEN; goto respond; } micp_op = iov_pull_mem(&iov, sizeof(*micp_op)); if (!micp_op) { DBG(micp, "iov_pull_mem() returned NULL"); goto respond; } if ((*micp_op == MICS_DISABLED) || (*micp_op != MICS_NOT_MUTED && *micp_op != MICS_MUTED)) { DBG(micp, "Invalid operation - MICS DISABLED/RFU mics op:%d", micp_op); ret = MICP_ERROR_VALUE_NOT_ALLOWED; goto respond; } mdb = micp_get_mdb(micp); if (!mdb) { DBG(micp, "error: MDB not available"); goto respond; } mute_state = mdb_get_mute_state(mdb); if (*mute_state == MICS_DISABLED) { DBG(micp, "state: MICS DISABLED , can not write value: %d", *micp_op); ret = MICP_ERROR_MUTE_DISABLED; goto respond; } for (handler = micp_handlers; handler && handler->str; handler++) { DBG(micp, "handler->op: %d micp_op: %d iov.iov_len: %ld", handler->op, *micp_op, iov.iov_len); if (handler->op != *micp_op) continue; if (len < handler->size) { DBG(micp, "invalid len %ld : %ld < %ld handler->size", len, iov.iov_len, handler->size); ret = BT_ATT_ERROR_OPCODE_NOT_SUPPORTED; goto respond; } break; } if (handler && handler->str) { DBG(micp, "%s", handler->str); ret = handler->func(mics, micp, &iov); } else { DBG(micp, "unknown opcode 0x%02x", *micp_op); ret = BT_ATT_ERROR_OPCODE_NOT_SUPPORTED; } respond: gatt_db_attribute_write_result(attrib, id, ret); } static struct bt_mics *mics_new(struct gatt_db *db) { struct bt_mics *mics; bt_uuid_t uuid; if (!db) return NULL; mics = new0(struct bt_mics, 1); mics->mute_stat = MICS_MUTED; /* Populate DB with MICS attributes */ bt_uuid16_create(&uuid, MICS_UUID); mics->service = gatt_db_add_service(db, &uuid, true, 4); bt_uuid16_create(&uuid, MUTE_CHRC_UUID); mics->ms = gatt_db_service_add_characteristic(mics->service, &uuid, BT_ATT_PERM_READ | BT_ATT_PERM_WRITE, BT_GATT_CHRC_PROP_READ | BT_GATT_CHRC_PROP_WRITE | BT_GATT_CHRC_PROP_NOTIFY, mics_mute_read, mics_mute_write, mics); mics->ms_ccc = gatt_db_service_add_ccc(mics->service, BT_ATT_PERM_READ | BT_ATT_PERM_WRITE); gatt_db_service_set_active(mics->service, true); return mics; } static struct bt_micp_db *micp_db_new(struct gatt_db *db) { struct bt_micp_db *mdb; if (!db) return NULL; mdb = new0(struct bt_micp_db, 1); mdb->db = gatt_db_ref(db); if (!micp_db) micp_db = queue_new(); mdb->mics = mics_new(db); mdb->mics->mdb = mdb; queue_push_tail(micp_db, mdb); return mdb; } static struct bt_micp_db *micp_get_db(struct gatt_db *db) { struct bt_micp_db *mdb; mdb = queue_find(micp_db, micp_db_match, db); if (mdb) return mdb; return micp_db_new(db); } void bt_micp_add_db(struct gatt_db *db) { micp_db_new(db); } bool bt_micp_set_debug(struct bt_micp *micp, bt_micp_debug_func_t func, void *user_data, bt_micp_destroy_func_t destroy) { if (!micp) return false; if (micp->debug_destroy) micp->debug_destroy(micp->debug_data); micp->debug_func = func; micp->debug_destroy = destroy; micp->debug_data = user_data; return true; } unsigned int bt_micp_register(bt_micp_func_t attached, bt_micp_func_t detached, void *user_data) { struct bt_micp_cb *cb; static unsigned int id; if (!attached && !detached) return 0; if (!micp_cbs) micp_cbs = queue_new(); cb = new0(struct bt_micp_cb, 1); cb->id = ++id ? id : ++id; cb->attached = attached; cb->detached = detached; cb->user_data = user_data; queue_push_tail(micp_cbs, cb); return cb->id; } static bool match_id(const void *data, const void *match_data) { const struct bt_micp_cb *cb = data; unsigned int id = PTR_TO_UINT(match_data); return (cb->id == id); } bool bt_micp_unregister(unsigned int id) { struct bt_micp_cb *cb; cb = queue_remove_if(micp_cbs, match_id, UINT_TO_PTR(id)); if (!cb) return false; free(cb); return true; } struct bt_micp *bt_micp_new(struct gatt_db *ldb, struct gatt_db *rdb) { struct bt_micp *micp; struct bt_micp_db *mdb; if (!ldb) return NULL; mdb = micp_get_db(ldb); if (!mdb) return NULL; micp = new0(struct bt_micp, 1); micp->ldb = mdb; micp->pending = queue_new(); micp->ready_cbs = queue_new(); micp->notify = queue_new(); if (!rdb) goto done; mdb = new0(struct bt_micp_db, 1); mdb->db = gatt_db_ref(rdb); micp->rdb = mdb; done: bt_micp_ref(micp); return micp; } static void micp_pending_destroy(void *data) { struct bt_micp_pending *pending = data; struct bt_micp *micp = pending->micp; if (queue_remove_if(micp->pending, NULL, pending)) free(pending); } static void micp_pending_complete(bool success, uint8_t att_ecode, const uint8_t *value, uint16_t length, void *user_data) { struct bt_micp_pending *pending = user_data; if (pending->func) pending->func(pending->micp, success, att_ecode, value, length, pending->userdata); } static void micp_read_value(struct bt_micp *micp, uint16_t value_handle, micp_func_t func, void *user_data) { struct bt_micp_pending *pending; pending = new0(struct bt_micp_pending, 1); pending->micp = micp; pending->func = func; pending->userdata = user_data; pending->id = bt_gatt_client_read_value(micp->client, value_handle, micp_pending_complete, pending, micp_pending_destroy); if (!pending->id) { DBG(micp, "unable to send read request"); free(pending); return; } queue_push_tail(micp->pending, pending); } static void micp_register(uint16_t att_ecode, void *user_data) { struct bt_micp_notify *notify = user_data; if (att_ecode) DBG(notify->micp, "MICP register failed 0x%04x", att_ecode); } static void micp_notify(uint16_t value_handle, const uint8_t *value, uint16_t length, void *user_data) { struct bt_micp_notify *notify = user_data; if (notify->func) notify->func(notify->micp, value_handle, value, length, notify->user_data); } static void micp_notify_destroy(void *data) { struct bt_micp_notify *notify = data; struct bt_micp *micp = notify->micp; if (queue_remove_if(micp->notify, NULL, notify)) free(notify); } static unsigned int micp_register_notify(struct bt_micp *micp, uint16_t value_handle, micp_notify_t func, void *user_data) { struct bt_micp_notify *notify; notify = new0(struct bt_micp_notify, 1); notify->micp = micp; notify->func = func; notify->user_data = user_data; notify->id = bt_gatt_client_register_notify(micp->client, value_handle, micp_register, micp_notify, notify, micp_notify_destroy); if (!notify->id) { DBG(micp, "Unable to register for notifications"); free(notify); return 0; } queue_push_tail(micp->notify, notify); return notify->id; } static void micp_mute_state_notify(struct bt_micp *micp, uint16_t value_handle, const uint8_t *value, uint16_t length, void *user_data) { uint8_t mute_state; memcpy(&mute_state, value, sizeof(mute_state)); DBG(micp, "Mute state: 0x%x", mute_state); } static void read_mute_state(struct bt_micp *micp, bool success, uint8_t att_ecode, const uint8_t *value, uint16_t length, void *user_data) { uint8_t *mute_state; struct iovec iov = { .iov_base = (void *)value, .iov_len = length, }; if (!success) { DBG(micp, "Unable to read Mute state: error 0x%02x", att_ecode); return; } mute_state = iov_pull_mem(&iov, sizeof(uint8_t)); if (mute_state == NULL) { DBG(micp, "Unable to get Mute state"); return; } DBG(micp, "Mute state: %x", *mute_state); } static void foreach_mics_char(struct gatt_db_attribute *attr, void *user_data) { struct bt_micp *micp = user_data; uint16_t value_handle; bt_uuid_t uuid, uuid_mute; struct bt_mics *mics; if (!gatt_db_attribute_get_char_data(attr, NULL, &value_handle, NULL, NULL, &uuid)) return; bt_uuid16_create(&uuid_mute, MUTE_CHRC_UUID); if (!bt_uuid_cmp(&uuid, &uuid_mute)) { DBG(micp, "MICS Mute characteristic found: handle 0x%04x", value_handle); mics = micp_get_mics(micp); if (!mics || mics->ms) return; mics->ms = attr; micp_read_value(micp, value_handle, read_mute_state, micp); micp->mute_id = micp_register_notify(micp, value_handle, micp_mute_state_notify, NULL); } } static void foreach_mics_service(struct gatt_db_attribute *attr, void *user_data) { struct bt_micp *micp = user_data; struct bt_mics *mics = micp_get_mics(micp); mics->service = attr; gatt_db_service_set_claimed(attr, true); gatt_db_service_foreach_char(attr, foreach_mics_char, micp); } unsigned int bt_micp_ready_register(struct bt_micp *micp, bt_micp_ready_func_t func, void *user_data, bt_micp_destroy_func_t destroy) { struct bt_micp_ready *ready; static unsigned int id; DBG(micp, "bt_micp_ready_register_Entry\n"); if (!micp) return 0; ready = new0(struct bt_micp_ready, 1); ready->id = ++id ? id : ++id; ready->func = func; ready->destroy = destroy; ready->data = user_data; queue_push_tail(micp->ready_cbs, ready); return ready->id; } static bool match_ready_id(const void *data, const void *match_data) { const struct bt_micp_ready *ready = data; unsigned int id = PTR_TO_UINT(match_data); return (ready->id == id); } bool bt_micp_ready_unregister(struct bt_micp *micp, unsigned int id) { struct bt_micp_ready *ready; ready = queue_remove_if(micp->ready_cbs, match_ready_id, UINT_TO_PTR(id)); if (!ready) return false; micp_ready_free(ready); return true; } static struct bt_micp *bt_micp_ref_safe(struct bt_micp *micp) { if (!micp || !micp->ref_count) return NULL; return bt_micp_ref(micp); } static void micp_notify_ready(struct bt_micp *micp) { const struct queue_entry *entry; if (!bt_micp_ref_safe(micp)) return; for (entry = queue_get_entries(micp->ready_cbs); entry; entry = entry->next) { struct bt_micp_ready *ready = entry->data; ready->func(micp, ready->data); } bt_micp_unref(micp); } static void micp_idle(void *data) { struct bt_micp *micp = data; micp->idle_id = 0; micp_notify_ready(micp); } bool bt_micp_attach(struct bt_micp *micp, struct bt_gatt_client *client) { bt_uuid_t uuid; if (!sessions) sessions = queue_new(); queue_push_tail(sessions, micp); if (!client) return true; if (micp->client) return false; micp->client = bt_gatt_client_clone(client); if (!micp->client) return false; bt_gatt_client_idle_register(micp->client, micp_idle, micp, NULL); bt_uuid16_create(&uuid, MICS_UUID); gatt_db_foreach_service(micp->ldb->db, &uuid, foreach_mics_service, micp); return true; } bluez-5.82/src/shared/PaxHeaders/mainloop-notify.h0000644000000000000000000000005014015011623017171 xustar0020 atime=1743515943 20 ctime=1743591277 bluez-5.82/src/shared/mainloop-notify.h0000644000000000000000000000036614015011623016657 0ustar00rootroot/* SPDX-License-Identifier: LGPL-2.1-or-later */ /* * * BlueZ - Bluetooth protocol stack for Linux * * Copyright (C) 2018 Intel Corporation. All rights reserved. * * */ void mainloop_notify_init(void); void mainloop_notify_exit(void); bluez-5.82/src/shared/PaxHeaders/bap-debug.c0000644000000000000000000000005014536422313015700 xustar0020 atime=1743515916 20 ctime=1743591277 bluez-5.82/src/shared/bap-debug.c0000644000000000000000000003474214536422313015373 0ustar00rootroot// SPDX-License-Identifier: LGPL-2.1-or-later /* * * BlueZ - Bluetooth protocol stack for Linux * * Copyright (C) 2023 Intel Corporation. */ #define _GNU_SOURCE #include #include #include #include #include #include #include "src/shared/util.h" #include "src/shared/bap-debug.h" static const struct util_bit_debugger pac_freq_table[] = { UTIL_BIT_DEBUG(0, "8 Khz (0x0001)"), UTIL_BIT_DEBUG(1, "11.25 Khz (0x0002)"), UTIL_BIT_DEBUG(2, "16 Khz (0x0004)"), UTIL_BIT_DEBUG(3, "22.05 Khz (0x0008)"), UTIL_BIT_DEBUG(4, "24 Khz (0x0010)"), UTIL_BIT_DEBUG(5, "32 Khz (0x0020)"), UTIL_BIT_DEBUG(6, "44.1 Khz (0x0040)"), UTIL_BIT_DEBUG(7, "48 Khz (0x0080)"), UTIL_BIT_DEBUG(8, "88.2 Khz (0x0100)"), UTIL_BIT_DEBUG(9, "96 Khz (0x0200)"), UTIL_BIT_DEBUG(10, "176.4 Khz (0x0400)"), UTIL_BIT_DEBUG(11, "192 Khz (0x0800)"), UTIL_BIT_DEBUG(12, "384 Khz (0x1000)"), UTIL_BIT_DEBUG(13, "RFU (0x2000)"), UTIL_BIT_DEBUG(14, "RFU (0x4000)"), UTIL_BIT_DEBUG(15, "RFU (0x8000)"), { } }; static void pac_debug_freq(const uint8_t *data, uint8_t len, util_debug_func_t func, void *user_data) { struct iovec frame = { (void *)data, len }; uint16_t value; uint16_t mask; if (!util_iov_pull_le16(&frame, &value)) { util_debug(func, user_data, "value: invalid size"); goto done; } util_debug(func, user_data, "Sampling Frequencies: 0x%4.4x", value); mask = util_debug_bit("Sampling Frequency: ", value, pac_freq_table, func, user_data); if (mask) util_debug(func, user_data, "Unknown fields (0x%4.4x)", mask); done: if (frame.iov_len) util_hexdump(' ', frame.iov_base, frame.iov_len, func, user_data); } static const struct util_bit_debugger pac_duration_table[] = { UTIL_BIT_DEBUG(0, "7.5 ms (0x01)"), UTIL_BIT_DEBUG(1, "10 ms (0x02)"), UTIL_BIT_DEBUG(2, "RFU (0x04)"), UTIL_BIT_DEBUG(3, "RFU (0x08)"), UTIL_BIT_DEBUG(4, "7.5 ms preferred (0x10)"), UTIL_BIT_DEBUG(5, "10 ms preferred (0x20)"), UTIL_BIT_DEBUG(6, "RFU (0x40)"), UTIL_BIT_DEBUG(7, "RFU (0x80)"), { } }; static void pac_debug_duration(const uint8_t *data, uint8_t len, util_debug_func_t func, void *user_data) { struct iovec frame = { (void *)data, len }; uint8_t value; uint8_t mask; if (!util_iov_pull_u8(&frame, &value)) { util_debug(func, user_data, "value: invalid size"); goto done; } util_debug(func, user_data, "Frame Duration: 0x%2.2x", value); mask = util_debug_bit("Frame Duration: ", value, pac_duration_table, func, user_data); if (mask) util_debug(func, user_data, "Unknown fields (0x%2.2x)", mask); done: if (frame.iov_len) util_hexdump(' ', frame.iov_base, frame.iov_len, func, user_data); } static const struct util_bit_debugger pac_channel_table[] = { UTIL_BIT_DEBUG(0, "1 channel (0x01)"), UTIL_BIT_DEBUG(1, "2 channel (0x02)"), UTIL_BIT_DEBUG(2, "3 channel (0x04)"), UTIL_BIT_DEBUG(3, "4 channel (0x08)"), UTIL_BIT_DEBUG(4, "5 channel (0x10)"), UTIL_BIT_DEBUG(5, "6 channel (0x20)"), UTIL_BIT_DEBUG(6, "7 channel (0x40)"), UTIL_BIT_DEBUG(7, "8 channel (0x80)"), { } }; static void pac_debug_channels(const uint8_t *data, uint8_t len, util_debug_func_t func, void *user_data) { struct iovec frame = { (void *)data, len }; uint8_t value; uint8_t mask; if (!util_iov_pull_u8(&frame, &value)) { util_debug(func, user_data, "value: invalid size"); goto done; } util_debug(func, user_data, "Audio Channel Count: 0x%2.2x", value); mask = util_debug_bit("Audio Channel Count: ", value, pac_channel_table, func, user_data); if (mask) util_debug(func, user_data, "Unknown fields (0x%2.2x)", mask); done: if (frame.iov_len) util_hexdump(' ', frame.iov_base, frame.iov_len, func, user_data); } static void pac_debug_frame_length(const uint8_t *data, uint8_t len, util_debug_func_t func, void *user_data) { struct iovec frame = { (void *)data, len }; uint16_t min, max; if (!util_iov_pull_le16(&frame, &min)) { util_debug(func, user_data, "min: invalid size"); goto done; } if (!util_iov_pull_le16(&frame, &max)) { util_debug(func, user_data, "max: invalid size"); goto done; } util_debug(func, user_data, "Frame Length: %u (0x%4.4x) - %u (0x%4.4x)", min, min, max, max); done: if (frame.iov_len) util_hexdump(' ', frame.iov_base, frame.iov_len, func, user_data); } static void pac_debug_sdu(const uint8_t *data, uint8_t len, util_debug_func_t func, void *user_data) { struct iovec frame = { (void *)data, len }; uint8_t value; if (!util_iov_pull_u8(&frame, &value)) { util_debug(func, user_data, "value: invalid size"); goto done; } util_debug(func, user_data, "Max SDU: %u (0x%2.2x)", value, value); done: if (frame.iov_len) util_hexdump(' ', frame.iov_base, frame.iov_len, func, user_data); } static const struct util_ltv_debugger pac_cap_table[] = { UTIL_LTV_DEBUG(0x01, pac_debug_freq), UTIL_LTV_DEBUG(0x02, pac_debug_duration), UTIL_LTV_DEBUG(0x03, pac_debug_channels), UTIL_LTV_DEBUG(0x04, pac_debug_frame_length), UTIL_LTV_DEBUG(0x05, pac_debug_sdu) }; bool bt_bap_debug_caps(void *data, size_t len, util_debug_func_t func, void *user_data) { return util_debug_ltv(data, len, pac_cap_table, ARRAY_SIZE(pac_cap_table), func, user_data); } static void ase_debug_freq(const uint8_t *data, uint8_t len, util_debug_func_t func, void *user_data) { struct iovec frame = { (void *)data, len }; uint8_t value; if (!util_iov_pull_u8(&frame, &value)) { util_debug(func, user_data, "value: invalid size"); goto done; } switch (value) { case 0x01: util_debug(func, user_data, "Sampling Frequency: 8 Khz (0x01)"); break; case 0x02: util_debug(func, user_data, "Sampling Frequency: 11.25 Khz (0x02)"); break; case 0x03: util_debug(func, user_data, "Sampling Frequency: 16 Khz (0x03)"); break; case 0x04: util_debug(func, user_data, "Sampling Frequency: 22.05 Khz (0x04)"); break; case 0x05: util_debug(func, user_data, "Sampling Frequency: 24 Khz (0x05)"); break; case 0x06: util_debug(func, user_data, "Sampling Frequency: 32 Khz (0x06)"); break; case 0x07: util_debug(func, user_data, "Sampling Frequency: 44.1 Khz (0x07)"); break; case 0x08: util_debug(func, user_data, "Sampling Frequency: 48 Khz (0x08)"); break; case 0x09: util_debug(func, user_data, "Sampling Frequency: 88.2 Khz (0x09)"); break; case 0x0a: util_debug(func, user_data, "Sampling Frequency: 96 Khz (0x0a)"); break; case 0x0b: util_debug(func, user_data, "Sampling Frequency: 176.4 Khz (0x0b)"); break; case 0x0c: util_debug(func, user_data, "Sampling Frequency: 192 Khz (0x0c)"); break; case 0x0d: util_debug(func, user_data, "Sampling Frequency: 384 Khz (0x0d)"); break; default: util_debug(func, user_data, "Sampling Frequency: RFU (0x%2.2x)", value); break; } done: if (frame.iov_len) util_hexdump(' ', frame.iov_base, frame.iov_len, func, user_data); } static void ase_debug_duration(const uint8_t *data, uint8_t len, util_debug_func_t func, void *user_data) { struct iovec frame = { (void *)data, len }; uint8_t value; if (!util_iov_pull_u8(&frame, &value)) { util_debug(func, user_data, "\tvalue: invalid size\n"); goto done; } switch (value) { case 0x00: util_debug(func, user_data, "Frame Duration: 7.5 ms (0x00)"); break; case 0x01: util_debug(func, user_data, "Frame Duration: 10 ms (0x01)"); break; default: util_debug(func, user_data, "Frame Duration: RFU (0x%2.2x)", value); break; } done: if (frame.iov_len) util_hexdump(' ', frame.iov_base, frame.iov_len, func, user_data); } static const struct util_bit_debugger channel_location_table[] = { UTIL_BIT_DEBUG(0, "Front Left (0x00000001)"), UTIL_BIT_DEBUG(1, "Front Right (0x00000002)"), UTIL_BIT_DEBUG(2, "Front Center (0x00000004)"), UTIL_BIT_DEBUG(3, "Low Frequency Effects 1 (0x00000008)"), UTIL_BIT_DEBUG(4, "Back Left (0x00000010)"), UTIL_BIT_DEBUG(5, "Back Right (0x00000020)"), UTIL_BIT_DEBUG(6, "Front Left of Center (0x00000040)"), UTIL_BIT_DEBUG(7, "Front Right of Center (0x00000080)"), UTIL_BIT_DEBUG(8, "Back Center (0x00000100)"), UTIL_BIT_DEBUG(9, "Low Frequency Effects 2 (0x00000200)"), UTIL_BIT_DEBUG(10, "Side Left (0x00000400)"), UTIL_BIT_DEBUG(11, "Side Right (0x00000800)"), UTIL_BIT_DEBUG(12, "Top Front Left (0x00001000)"), UTIL_BIT_DEBUG(13, "Top Front Right (0x00002000)"), UTIL_BIT_DEBUG(14, "Top Front Center (0x00004000)"), UTIL_BIT_DEBUG(15, "Top Center (0x00008000)"), UTIL_BIT_DEBUG(16, "Top Back Left (0x00010000)"), UTIL_BIT_DEBUG(17, "Top Back Right (0x00020000)"), UTIL_BIT_DEBUG(18, "Top Side Left (0x00040000)"), UTIL_BIT_DEBUG(19, "Top Side Right (0x00080000)"), UTIL_BIT_DEBUG(20, "Top Back Center (0x00100000)"), UTIL_BIT_DEBUG(21, "Bottom Front Center (0x00200000)"), UTIL_BIT_DEBUG(22, "Bottom Front Left (0x00400000)"), UTIL_BIT_DEBUG(23, "Bottom Front Right (0x00800000)"), UTIL_BIT_DEBUG(24, "Front Left Wide (0x01000000)"), UTIL_BIT_DEBUG(25, "Front Right Wide (0x02000000)"), UTIL_BIT_DEBUG(26, "Left Surround (0x04000000)"), UTIL_BIT_DEBUG(27, "Right Surround (0x08000000)"), UTIL_BIT_DEBUG(28, "RFU (0x10000000)"), UTIL_BIT_DEBUG(29, "RFU (0x20000000)"), UTIL_BIT_DEBUG(30, "RFU (0x40000000)"), UTIL_BIT_DEBUG(31, "RFU (0x80000000)"), { } }; static void debug_location(const struct iovec *frame, util_debug_func_t func, void *user_data) { uint32_t value; uint32_t mask; if (!util_iov_pull_le32((void *)frame, &value)) { util_debug(func, user_data, "value: invalid size"); goto done; } util_debug(func, user_data, "Location: 0x%8.8x", value); mask = util_debug_bit("Location: ", value, channel_location_table, func, user_data); if (mask) util_debug(func, user_data, "Unknown fields (0x%8.8x)", mask); done: if (frame->iov_len) util_hexdump(' ', frame->iov_base, frame->iov_len, func, user_data); } static void ase_debug_location(const uint8_t *data, uint8_t len, util_debug_func_t func, void *user_data) { struct iovec frame = { (void *)data, len }; debug_location(&frame, func, user_data); } static void ase_debug_frame_length(const uint8_t *data, uint8_t len, util_debug_func_t func, void *user_data) { struct iovec frame = { (void *)data, len }; uint16_t value; if (!util_iov_pull_le16(&frame, &value)) { util_debug(func, user_data, "\tvalue: invalid size\n"); goto done; } util_debug(func, user_data, "Frame Length: %u (0x%4.4x)", value, value); done: if (frame.iov_len) util_hexdump(' ', frame.iov_base, frame.iov_len, func, user_data); } static void ase_debug_blocks(const uint8_t *data, uint8_t len, util_debug_func_t func, void *user_data) { struct iovec frame = { (void *)data, len }; uint8_t value; if (!util_iov_pull_u8(&frame, &value)) { util_debug(func, user_data, "value: invalid size"); goto done; } util_debug(func, user_data, "Frame Blocks per SDU: %u (0x%2.2x)", value, value); done: if (frame.iov_len) util_hexdump(' ', frame.iov_base, frame.iov_len, func, user_data); } static const struct util_ltv_debugger ase_cc_table[] = { UTIL_LTV_DEBUG(0x01, ase_debug_freq), UTIL_LTV_DEBUG(0x02, ase_debug_duration), UTIL_LTV_DEBUG(0x03, ase_debug_location), UTIL_LTV_DEBUG(0x04, ase_debug_frame_length), UTIL_LTV_DEBUG(0x05, ase_debug_blocks) }; bool bt_bap_debug_config(void *data, size_t len, util_debug_func_t func, void *user_data) { return util_debug_ltv(data, len, ase_cc_table, ARRAY_SIZE(ase_cc_table), func, user_data); } static const struct util_bit_debugger pac_context_table[] = { UTIL_BIT_DEBUG(0, "\tUnspecified (0x0001)"), UTIL_BIT_DEBUG(1, "\tConversational (0x0002)"), UTIL_BIT_DEBUG(2, "\tMedia (0x0004)"), UTIL_BIT_DEBUG(3, "\tGame (0x0008)"), UTIL_BIT_DEBUG(4, "\tInstructional (0x0010)"), UTIL_BIT_DEBUG(5, "\tVoice Assistants (0x0020)"), UTIL_BIT_DEBUG(6, "\tLive (0x0040)"), UTIL_BIT_DEBUG(7, "\tSound Effects (0x0080)"), UTIL_BIT_DEBUG(8, "\tNotifications (0x0100)"), UTIL_BIT_DEBUG(9, "\tRingtone (0x0200)"), UTIL_BIT_DEBUG(10, "\tAlerts (0x0400)"), UTIL_BIT_DEBUG(11, "\tEmergency alarm (0x0800)"), UTIL_BIT_DEBUG(12, "\tRFU (0x1000)"), UTIL_BIT_DEBUG(13, "\tRFU (0x2000)"), UTIL_BIT_DEBUG(14, "\tRFU (0x4000)"), UTIL_BIT_DEBUG(15, "\tRFU (0x8000)"), { } }; static void debug_context(const struct iovec *frame, const char *label, util_debug_func_t func, void *user_data) { uint16_t value; uint16_t mask; if (!util_iov_pull_le16((void *)frame, &value)) { util_debug(func, user_data, "value: invalid size"); goto done; } util_debug(func, user_data, "%s: 0x%4.4x", label, value); mask = util_debug_bit(label, value, pac_context_table, func, user_data); if (mask) util_debug(func, user_data, "Unknown fields (0x%4.4x)", mask); done: if (frame->iov_len) util_hexdump(' ', frame->iov_base, frame->iov_len, func, user_data); } static void ase_debug_preferred_context(const uint8_t *data, uint8_t len, util_debug_func_t func, void *user_data) { struct iovec frame = { (void *)data, len }; debug_context(&frame, "Preferred Context", func, user_data); } static void ase_debug_context(const uint8_t *data, uint8_t len, util_debug_func_t func, void *user_data) { struct iovec frame = { (void *)data, len }; debug_context(&frame, "Context", func, user_data); } static void ase_debug_program_info(const uint8_t *data, uint8_t len, util_debug_func_t func, void *user_data) { struct iovec frame = { (void *)data, len }; const char *str; str = util_iov_pull_mem(&frame, len); if (!str) { util_debug(func, user_data, "value: invalid size"); goto done; } util_debug(func, user_data, "Program Info: %*s", len, str); done: if (frame.iov_len) util_hexdump(' ', frame.iov_base, frame.iov_len, func, user_data); } static void ase_debug_language(const uint8_t *data, uint8_t len, util_debug_func_t func, void *user_data) { struct iovec frame = { (void *)data, len }; uint32_t value; if (!util_iov_pull_le24(&frame, &value)) { util_debug(func, user_data, "value: invalid size"); goto done; } util_debug(func, user_data, "Language: 0x%6.6x\n", value); done: if (frame.iov_len) util_hexdump(' ', frame.iov_base, frame.iov_len, func, user_data); } static const struct util_ltv_debugger ase_metadata_table[] = { UTIL_LTV_DEBUG(0x01, ase_debug_preferred_context), UTIL_LTV_DEBUG(0x02, ase_debug_context), UTIL_LTV_DEBUG(0x03, ase_debug_program_info), UTIL_LTV_DEBUG(0x04, ase_debug_language) }; bool bt_bap_debug_metadata(void *data, size_t len, util_debug_func_t func, void *user_data) { return util_debug_ltv(data, len, ase_metadata_table, ARRAY_SIZE(ase_metadata_table), func, user_data); } bluez-5.82/src/shared/PaxHeaders/hfp.c0000644000000000000000000000005014131623652014627 xustar0020 atime=1743515872 20 ctime=1743591277 bluez-5.82/src/shared/hfp.c0000644000000000000000000007014414131623652014316 0ustar00rootroot// SPDX-License-Identifier: LGPL-2.1-or-later /* * * BlueZ - Bluetooth protocol stack for Linux * * Copyright (C) 2012-2014 Intel Corporation. All rights reserved. * * */ #ifdef HAVE_CONFIG_H #include #endif #define _GNU_SOURCE #include #include #include #include #include #include "src/shared/util.h" #include "src/shared/ringbuf.h" #include "src/shared/queue.h" #include "src/shared/io.h" #include "src/shared/hfp.h" struct hfp_gw { int ref_count; int fd; bool close_on_unref; struct io *io; struct ringbuf *read_buf; struct ringbuf *write_buf; struct queue *cmd_handlers; bool writer_active; bool result_pending; hfp_command_func_t command_callback; hfp_destroy_func_t command_destroy; void *command_data; hfp_debug_func_t debug_callback; hfp_destroy_func_t debug_destroy; void *debug_data; hfp_disconnect_func_t disconnect_callback; hfp_destroy_func_t disconnect_destroy; void *disconnect_data; bool in_disconnect; bool destroyed; }; struct hfp_hf { int ref_count; int fd; bool close_on_unref; struct io *io; struct ringbuf *read_buf; struct ringbuf *write_buf; bool writer_active; struct queue *cmd_queue; struct queue *event_handlers; hfp_debug_func_t debug_callback; hfp_destroy_func_t debug_destroy; void *debug_data; hfp_disconnect_func_t disconnect_callback; hfp_destroy_func_t disconnect_destroy; void *disconnect_data; bool in_disconnect; bool destroyed; }; struct cmd_handler { char *prefix; void *user_data; hfp_destroy_func_t destroy; hfp_result_func_t callback; }; struct hfp_context { const char *data; unsigned int offset; }; struct cmd_response { hfp_response_func_t resp_cb; struct hfp_context *response; char *resp_data; void *user_data; }; struct event_handler { char *prefix; void *user_data; hfp_destroy_func_t destroy; hfp_hf_result_func_t callback; }; static void destroy_cmd_handler(void *data) { struct cmd_handler *handler = data; if (handler->destroy) handler->destroy(handler->user_data); free(handler->prefix); free(handler); } static bool match_handler_prefix(const void *a, const void *b) { const struct cmd_handler *handler = a; const char *prefix = b; if (strcmp(handler->prefix, prefix) != 0) return false; return true; } static void write_watch_destroy(void *user_data) { struct hfp_gw *hfp = user_data; hfp->writer_active = false; } static bool can_write_data(struct io *io, void *user_data) { struct hfp_gw *hfp = user_data; ssize_t bytes_written; bytes_written = ringbuf_write(hfp->write_buf, hfp->fd); if (bytes_written < 0) return false; if (ringbuf_len(hfp->write_buf) > 0) return true; return false; } static void wakeup_writer(struct hfp_gw *hfp) { if (hfp->writer_active) return; if (!ringbuf_len(hfp->write_buf)) return; if (!io_set_write_handler(hfp->io, can_write_data, hfp, write_watch_destroy)) return; hfp->writer_active = true; } static void skip_whitespace(struct hfp_context *context) { while (context->data[context->offset] == ' ') context->offset++; } static void handle_unknown_at_command(struct hfp_gw *hfp, const char *data) { if (hfp->command_callback) { hfp->result_pending = true; hfp->command_callback(data, hfp->command_data); } else { hfp_gw_send_result(hfp, HFP_RESULT_ERROR); } } static bool handle_at_command(struct hfp_gw *hfp, const char *data) { struct cmd_handler *handler; const char *separators = ";?=\0"; struct hfp_context context; enum hfp_gw_cmd_type type; char lookup_prefix[18]; uint8_t pref_len = 0; const char *prefix; int i; context.offset = 0; context.data = data; skip_whitespace(&context); if (strlen(data + context.offset) < 3) return false; if (strncmp(data + context.offset, "AT", 2)) if (strncmp(data + context.offset, "at", 2)) return false; context.offset += 2; prefix = data + context.offset; if (isalpha(prefix[0])) { lookup_prefix[pref_len++] = toupper(prefix[0]); } else { pref_len = strcspn(prefix, separators); if (pref_len > 17 || pref_len < 2) return false; for (i = 0; i < pref_len; i++) lookup_prefix[i] = toupper(prefix[i]); } lookup_prefix[pref_len] = '\0'; context.offset += pref_len; if (lookup_prefix[0] == 'D') { type = HFP_GW_CMD_TYPE_SET; goto done; } if (data[context.offset] == '=') { context.offset++; if (data[context.offset] == '?') { context.offset++; type = HFP_GW_CMD_TYPE_TEST; } else { type = HFP_GW_CMD_TYPE_SET; } goto done; } if (data[context.offset] == '?') { context.offset++; type = HFP_GW_CMD_TYPE_READ; goto done; } type = HFP_GW_CMD_TYPE_COMMAND; done: handler = queue_find(hfp->cmd_handlers, match_handler_prefix, lookup_prefix); if (!handler) { handle_unknown_at_command(hfp, data); return true; } hfp->result_pending = true; handler->callback(&context, type, handler->user_data); return true; } static void next_field(struct hfp_context *context) { if (context->data[context->offset] == ',') context->offset++; } bool hfp_context_get_number_default(struct hfp_context *context, unsigned int *val, unsigned int default_val) { skip_whitespace(context); if (context->data[context->offset] == ',') { if (val) *val = default_val; context->offset++; return true; } return hfp_context_get_number(context, val); } bool hfp_context_get_number(struct hfp_context *context, unsigned int *val) { unsigned int i; int tmp = 0; skip_whitespace(context); i = context->offset; while (context->data[i] >= '0' && context->data[i] <= '9') tmp = tmp * 10 + context->data[i++] - '0'; if (i == context->offset) return false; if (val) *val = tmp; context->offset = i; skip_whitespace(context); next_field(context); return true; } bool hfp_context_open_container(struct hfp_context *context) { skip_whitespace(context); /* The list shall be preceded by a left parenthesis "(") */ if (context->data[context->offset] != '(') return false; context->offset++; return true; } bool hfp_context_close_container(struct hfp_context *context) { skip_whitespace(context); /* The list shall be followed by a right parenthesis (")" V250 5.7.3.1*/ if (context->data[context->offset] != ')') return false; context->offset++; next_field(context); return true; } bool hfp_context_get_string(struct hfp_context *context, char *buf, uint8_t len) { int i = 0; const char *data = context->data; unsigned int offset; skip_whitespace(context); if (data[context->offset] != '"') return false; offset = context->offset; offset++; while (data[offset] != '\0' && data[offset] != '"') { if (i == len) return false; buf[i++] = data[offset]; offset++; } if (i == len) return false; buf[i] = '\0'; if (data[offset] == '"') offset++; else return false; context->offset = offset; skip_whitespace(context); next_field(context); return true; } bool hfp_context_get_unquoted_string(struct hfp_context *context, char *buf, uint8_t len) { const char *data = context->data; unsigned int offset; int i = 0; char c; skip_whitespace(context); c = data[context->offset]; if (c == '"' || c == ')' || c == '(') return false; offset = context->offset; while (data[offset] != '\0' && data[offset] != ',' && data[offset] != ')') { if (i == len) return false; buf[i++] = data[offset]; offset++; } if (i == len) return false; buf[i] = '\0'; context->offset = offset; next_field(context); return true; } bool hfp_context_has_next(struct hfp_context *context) { return context->data[context->offset] != '\0'; } void hfp_context_skip_field(struct hfp_context *context) { const char *data = context->data; unsigned int offset = context->offset; while (data[offset] != '\0' && data[offset] != ',') offset++; context->offset = offset; next_field(context); } bool hfp_context_get_range(struct hfp_context *context, uint32_t *min, uint32_t *max) { uint32_t l, h; uint32_t start; start = context->offset; if (!hfp_context_get_number(context, &l)) goto failed; if (context->data[context->offset] != '-') goto failed; context->offset++; if (!hfp_context_get_number(context, &h)) goto failed; *min = l; *max = h; next_field(context); return true; failed: context->offset = start; return false; } static void process_input(struct hfp_gw *hfp) { char *str, *ptr; size_t len, count; bool free_ptr = false; bool read_again; do { str = ringbuf_peek(hfp->read_buf, 0, &len); if (!str) return; ptr = memchr(str, '\r', len); if (!ptr) { char *str2; size_t len2; /* * If there is no more data in ringbuffer, * it's just an incomplete command. */ if (len == ringbuf_len(hfp->read_buf)) return; str2 = ringbuf_peek(hfp->read_buf, len, &len2); if (!str2) return; ptr = memchr(str2, '\r', len2); if (!ptr) return; *ptr = '\0'; count = len2 + len; ptr = malloc(count); if (!ptr) return; memcpy(ptr, str, len); memcpy(ptr + len, str2, len2); free_ptr = true; str = ptr; } else { count = ptr - str; *ptr = '\0'; } if (!handle_at_command(hfp, str)) /* * Command is not handled that means that was some * trash. Let's skip that and keep reading from ring * buffer. */ read_again = true; else /* * Command has been handled. If we are waiting for a * result from upper layer, we can stop reading. If we * already reply i.e. ERROR on unknown command, then we * can keep reading ring buffer. Actually ring buffer * should be empty but lets just look there. */ read_again = !hfp->result_pending; ringbuf_drain(hfp->read_buf, count + 1); if (free_ptr) free(ptr); } while (read_again); } static void read_watch_destroy(void *user_data) { } static bool can_read_data(struct io *io, void *user_data) { struct hfp_gw *hfp = user_data; ssize_t bytes_read; bytes_read = ringbuf_read(hfp->read_buf, hfp->fd); if (bytes_read < 0) return false; if (hfp->result_pending) return true; process_input(hfp); return true; } struct hfp_gw *hfp_gw_new(int fd) { struct hfp_gw *hfp; if (fd < 0) return NULL; hfp = new0(struct hfp_gw, 1); hfp->fd = fd; hfp->close_on_unref = false; hfp->read_buf = ringbuf_new(4096); if (!hfp->read_buf) { free(hfp); return NULL; } hfp->write_buf = ringbuf_new(4096); if (!hfp->write_buf) { ringbuf_free(hfp->read_buf); free(hfp); return NULL; } hfp->io = io_new(fd); if (!hfp->io) { ringbuf_free(hfp->write_buf); ringbuf_free(hfp->read_buf); free(hfp); return NULL; } hfp->cmd_handlers = queue_new(); if (!io_set_read_handler(hfp->io, can_read_data, hfp, read_watch_destroy)) { queue_destroy(hfp->cmd_handlers, destroy_cmd_handler); io_destroy(hfp->io); ringbuf_free(hfp->write_buf); ringbuf_free(hfp->read_buf); free(hfp); return NULL; } hfp->writer_active = false; hfp->result_pending = false; return hfp_gw_ref(hfp); } struct hfp_gw *hfp_gw_ref(struct hfp_gw *hfp) { if (!hfp) return NULL; __sync_fetch_and_add(&hfp->ref_count, 1); return hfp; } void hfp_gw_unref(struct hfp_gw *hfp) { if (!hfp) return; if (__sync_sub_and_fetch(&hfp->ref_count, 1)) return; hfp_gw_set_command_handler(hfp, NULL, NULL, NULL); io_set_write_handler(hfp->io, NULL, NULL, NULL); io_set_read_handler(hfp->io, NULL, NULL, NULL); io_set_disconnect_handler(hfp->io, NULL, NULL, NULL); io_destroy(hfp->io); hfp->io = NULL; if (hfp->close_on_unref) close(hfp->fd); hfp_gw_set_debug(hfp, NULL, NULL, NULL); ringbuf_free(hfp->read_buf); hfp->read_buf = NULL; ringbuf_free(hfp->write_buf); hfp->write_buf = NULL; queue_destroy(hfp->cmd_handlers, destroy_cmd_handler); hfp->cmd_handlers = NULL; if (!hfp->in_disconnect) { free(hfp); return; } hfp->destroyed = true; } static void read_tracing(const void *buf, size_t count, void *user_data) { struct hfp_gw *hfp = user_data; util_hexdump('>', buf, count, hfp->debug_callback, hfp->debug_data); } static void write_tracing(const void *buf, size_t count, void *user_data) { struct hfp_gw *hfp = user_data; util_hexdump('<', buf, count, hfp->debug_callback, hfp->debug_data); } bool hfp_gw_set_debug(struct hfp_gw *hfp, hfp_debug_func_t callback, void *user_data, hfp_destroy_func_t destroy) { if (!hfp) return false; if (hfp->debug_destroy) hfp->debug_destroy(hfp->debug_data); hfp->debug_callback = callback; hfp->debug_destroy = destroy; hfp->debug_data = user_data; if (hfp->debug_callback) { ringbuf_set_input_tracing(hfp->read_buf, read_tracing, hfp); ringbuf_set_input_tracing(hfp->write_buf, write_tracing, hfp); } else { ringbuf_set_input_tracing(hfp->read_buf, NULL, NULL); ringbuf_set_input_tracing(hfp->write_buf, NULL, NULL); } return true; } bool hfp_gw_set_close_on_unref(struct hfp_gw *hfp, bool do_close) { if (!hfp) return false; hfp->close_on_unref = do_close; return true; } bool hfp_gw_send_result(struct hfp_gw *hfp, enum hfp_result result) { const char *str; if (!hfp) return false; switch (result) { case HFP_RESULT_OK: str = "OK"; break; case HFP_RESULT_ERROR: str = "ERROR"; break; case HFP_RESULT_RING: case HFP_RESULT_NO_CARRIER: case HFP_RESULT_BUSY: case HFP_RESULT_NO_ANSWER: case HFP_RESULT_DELAYED: case HFP_RESULT_REJECTED: case HFP_RESULT_CME_ERROR: case HFP_RESULT_NO_DIALTONE: case HFP_RESULT_CONNECT: default: return false; } if (ringbuf_printf(hfp->write_buf, "\r\n%s\r\n", str) < 0) return false; wakeup_writer(hfp); /* * There might be already something to read in the ring buffer. * If so, let's read it. */ if (hfp->result_pending) { hfp->result_pending = false; process_input(hfp); } return true; } bool hfp_gw_send_error(struct hfp_gw *hfp, enum hfp_error error) { if (!hfp) return false; if (ringbuf_printf(hfp->write_buf, "\r\n+CME ERROR: %u\r\n", error) < 0) return false; wakeup_writer(hfp); /* * There might be already something to read in the ring buffer. * If so, let's read it. */ if (hfp->result_pending) { hfp->result_pending = false; process_input(hfp); } return true; } bool hfp_gw_send_info(struct hfp_gw *hfp, const char *format, ...) { va_list ap; char *fmt; int len; if (!hfp || !format) return false; if (asprintf(&fmt, "\r\n%s\r\n", format) < 0) return false; va_start(ap, format); len = ringbuf_vprintf(hfp->write_buf, fmt, ap); va_end(ap); free(fmt); if (len < 0) return false; if (hfp->result_pending) return true; wakeup_writer(hfp); return true; } bool hfp_gw_set_command_handler(struct hfp_gw *hfp, hfp_command_func_t callback, void *user_data, hfp_destroy_func_t destroy) { if (!hfp) return false; if (hfp->command_destroy) hfp->command_destroy(hfp->command_data); hfp->command_callback = callback; hfp->command_destroy = destroy; hfp->command_data = user_data; return true; } bool hfp_gw_register(struct hfp_gw *hfp, hfp_result_func_t callback, const char *prefix, void *user_data, hfp_destroy_func_t destroy) { struct cmd_handler *handler; handler = new0(struct cmd_handler, 1); handler->callback = callback; handler->user_data = user_data; handler->prefix = strdup(prefix); if (!handler->prefix) { free(handler); return false; } if (queue_find(hfp->cmd_handlers, match_handler_prefix, handler->prefix)) { destroy_cmd_handler(handler); return false; } handler->destroy = destroy; return queue_push_tail(hfp->cmd_handlers, handler); } bool hfp_gw_unregister(struct hfp_gw *hfp, const char *prefix) { struct cmd_handler *handler; char *lookup_prefix; lookup_prefix = strdup(prefix); if (!lookup_prefix) return false; handler = queue_remove_if(hfp->cmd_handlers, match_handler_prefix, lookup_prefix); free(lookup_prefix); if (!handler) return false; destroy_cmd_handler(handler); return true; } static void disconnect_watch_destroy(void *user_data) { struct hfp_gw *hfp = user_data; if (hfp->disconnect_destroy) hfp->disconnect_destroy(hfp->disconnect_data); if (hfp->destroyed) free(hfp); } static bool io_disconnected(struct io *io, void *user_data) { struct hfp_gw *hfp = user_data; hfp->in_disconnect = true; if (hfp->disconnect_callback) hfp->disconnect_callback(hfp->disconnect_data); hfp->in_disconnect = false; return false; } bool hfp_gw_set_disconnect_handler(struct hfp_gw *hfp, hfp_disconnect_func_t callback, void *user_data, hfp_destroy_func_t destroy) { if (!hfp) return false; if (hfp->disconnect_destroy) hfp->disconnect_destroy(hfp->disconnect_data); if (!io_set_disconnect_handler(hfp->io, io_disconnected, hfp, disconnect_watch_destroy)) { hfp->disconnect_callback = NULL; hfp->disconnect_destroy = NULL; hfp->disconnect_data = NULL; return false; } hfp->disconnect_callback = callback; hfp->disconnect_destroy = destroy; hfp->disconnect_data = user_data; return true; } bool hfp_gw_disconnect(struct hfp_gw *hfp) { if (!hfp) return false; return io_shutdown(hfp->io); } static bool match_handler_event_prefix(const void *a, const void *b) { const struct event_handler *handler = a; const char *prefix = b; if (strcmp(handler->prefix, prefix) != 0) return false; return true; } static void destroy_event_handler(void *data) { struct event_handler *handler = data; if (handler->destroy) handler->destroy(handler->user_data); free(handler->prefix); free(handler); } static bool hf_can_write_data(struct io *io, void *user_data) { struct hfp_hf *hfp = user_data; ssize_t bytes_written; bytes_written = ringbuf_write(hfp->write_buf, hfp->fd); if (bytes_written < 0) return false; if (ringbuf_len(hfp->write_buf) > 0) return true; return false; } static void hf_write_watch_destroy(void *user_data) { struct hfp_hf *hfp = user_data; hfp->writer_active = false; } static void hf_skip_whitespace(struct hfp_context *context) { while (context->data[context->offset] == ' ') context->offset++; } static bool is_response(const char *prefix, enum hfp_result *result, enum hfp_error *cme_err, struct hfp_context *context) { if (strcmp(prefix, "OK") == 0) { *result = HFP_RESULT_OK; /* * Set cme_err to 0 as this is not valid when result is not * CME ERROR */ *cme_err = 0; return true; } if (strcmp(prefix, "ERROR") == 0) { *result = HFP_RESULT_ERROR; *cme_err = 0; return true; } if (strcmp(prefix, "NO CARRIER") == 0) { *result = HFP_RESULT_NO_CARRIER; *cme_err = 0; return true; } if (strcmp(prefix, "NO ANSWER") == 0) { *result = HFP_RESULT_NO_ANSWER; *cme_err = 0; return true; } if (strcmp(prefix, "BUSY") == 0) { *result = HFP_RESULT_BUSY; *cme_err = 0; return true; } if (strcmp(prefix, "DELAYED") == 0) { *result = HFP_RESULT_DELAYED; *cme_err = 0; return true; } if (strcmp(prefix, "BLACKLISTED") == 0) { *result = HFP_RESULT_REJECTED; *cme_err = 0; return true; } if (strcmp(prefix, "+CME ERROR") == 0) { uint32_t val; *result = HFP_RESULT_CME_ERROR; if (hfp_context_get_number(context, &val) && val <= HFP_ERROR_NETWORK_NOT_ALLOWED) *cme_err = val; else *cme_err = HFP_ERROR_AG_FAILURE; return true; } return false; } static void hf_wakeup_writer(struct hfp_hf *hfp) { if (hfp->writer_active) return; if (!ringbuf_len(hfp->write_buf)) return; if (!io_set_write_handler(hfp->io, hf_can_write_data, hfp, hf_write_watch_destroy)) return; hfp->writer_active = true; } static void hf_call_prefix_handler(struct hfp_hf *hfp, const char *data) { struct event_handler *handler; const char *separators = ";:\0"; struct hfp_context context; enum hfp_result result; enum hfp_error cme_err; char lookup_prefix[18] = {}; uint8_t pref_len = 0; const char *prefix; int i; context.offset = 0; context.data = data; hf_skip_whitespace(&context); if (strlen(data + context.offset) < 2) return; prefix = data + context.offset; pref_len = strcspn(prefix, separators); if (pref_len > 17 || pref_len < 2) return; for (i = 0; i < pref_len; i++) lookup_prefix[i] = toupper(prefix[i]); lookup_prefix[pref_len] = '\0'; context.offset += pref_len + 1; if (is_response(lookup_prefix, &result, &cme_err, &context)) { struct cmd_response *cmd; cmd = queue_peek_head(hfp->cmd_queue); if (!cmd) return; cmd->resp_cb(result, cme_err, cmd->user_data); queue_remove(hfp->cmd_queue, cmd); free(cmd); hf_wakeup_writer(hfp); return; } handler = queue_find(hfp->event_handlers, match_handler_event_prefix, lookup_prefix); if (!handler) return; handler->callback(&context, handler->user_data); } static char *find_cr_lf(char *str, size_t len) { char *ptr; size_t count, offset; offset = 0; ptr = memchr(str, '\r', len); while (ptr) { /* * Check if there is more data after '\r'. If so check for * '\n' */ count = ptr - str; if ((count < (len - 1)) && *(ptr + 1) == '\n') return ptr; /* There is only '\r'? Let's try to find next one */ offset += count + 1; if (offset >= len) return NULL; ptr = memchr(str + offset, '\r', len - offset); } return NULL; } static void hf_process_input(struct hfp_hf *hfp) { char *str, *ptr, *str2, *tmp; size_t len, count, offset, len2; bool free_tmp = false; str = ringbuf_peek(hfp->read_buf, 0, &len); if (!str) return; offset = 0; ptr = find_cr_lf(str, len); while (ptr) { count = ptr - (str + offset); if (count == 0) { /* 2 is for */ offset += 2; } else { *ptr = '\0'; hf_call_prefix_handler(hfp, str + offset); offset += count + 2; } ptr = find_cr_lf(str + offset, len - offset); } /* * Just check if there is no wrapped data in ring buffer. * Should not happen too often */ if (len == ringbuf_len(hfp->read_buf)) goto done; str2 = ringbuf_peek(hfp->read_buf, len, &len2); if (!str2) goto done; ptr = find_cr_lf(str2, len2); if (!ptr) { /* Might happen that we wrap between \r and \n */ ptr = memchr(str2, '\n', len2); if (!ptr) goto done; } count = ptr - str2; if (count) { *ptr = '\0'; tmp = malloc(len + count); if (!tmp) goto done; /* "str" here is not a string so we need to use memcpy */ memcpy(tmp, str, len); memcpy(tmp + len, str2, count); free_tmp = true; } else { str[len-1] = '\0'; tmp = str; } hf_call_prefix_handler(hfp, tmp); offset += count; done: ringbuf_drain(hfp->read_buf, offset); if (free_tmp) free(tmp); } static bool hf_can_read_data(struct io *io, void *user_data) { struct hfp_hf *hfp = user_data; ssize_t bytes_read; bytes_read = ringbuf_read(hfp->read_buf, hfp->fd); if (bytes_read < 0) return false; hf_process_input(hfp); return true; } struct hfp_hf *hfp_hf_new(int fd) { struct hfp_hf *hfp; if (fd < 0) return NULL; hfp = new0(struct hfp_hf, 1); hfp->fd = fd; hfp->close_on_unref = false; hfp->read_buf = ringbuf_new(4096); if (!hfp->read_buf) { free(hfp); return NULL; } hfp->write_buf = ringbuf_new(4096); if (!hfp->write_buf) { ringbuf_free(hfp->read_buf); free(hfp); return NULL; } hfp->io = io_new(fd); if (!hfp->io) { ringbuf_free(hfp->write_buf); ringbuf_free(hfp->read_buf); free(hfp); return NULL; } hfp->event_handlers = queue_new(); hfp->cmd_queue = queue_new(); hfp->writer_active = false; if (!io_set_read_handler(hfp->io, hf_can_read_data, hfp, read_watch_destroy)) { queue_destroy(hfp->event_handlers, destroy_event_handler); io_destroy(hfp->io); ringbuf_free(hfp->write_buf); ringbuf_free(hfp->read_buf); free(hfp); return NULL; } return hfp_hf_ref(hfp); } struct hfp_hf *hfp_hf_ref(struct hfp_hf *hfp) { if (!hfp) return NULL; __sync_fetch_and_add(&hfp->ref_count, 1); return hfp; } void hfp_hf_unref(struct hfp_hf *hfp) { if (!hfp) return; if (__sync_sub_and_fetch(&hfp->ref_count, 1)) return; io_set_write_handler(hfp->io, NULL, NULL, NULL); io_set_read_handler(hfp->io, NULL, NULL, NULL); io_set_disconnect_handler(hfp->io, NULL, NULL, NULL); io_destroy(hfp->io); hfp->io = NULL; if (hfp->close_on_unref) close(hfp->fd); hfp_hf_set_debug(hfp, NULL, NULL, NULL); ringbuf_free(hfp->read_buf); hfp->read_buf = NULL; ringbuf_free(hfp->write_buf); hfp->write_buf = NULL; queue_destroy(hfp->event_handlers, destroy_event_handler); hfp->event_handlers = NULL; queue_destroy(hfp->cmd_queue, free); hfp->cmd_queue = NULL; if (!hfp->in_disconnect) { free(hfp); return; } hfp->destroyed = true; } static void hf_read_tracing(const void *buf, size_t count, void *user_data) { struct hfp_hf *hfp = user_data; util_hexdump('>', buf, count, hfp->debug_callback, hfp->debug_data); } static void hf_write_tracing(const void *buf, size_t count, void *user_data) { struct hfp_hf *hfp = user_data; util_hexdump('<', buf, count, hfp->debug_callback, hfp->debug_data); } bool hfp_hf_set_debug(struct hfp_hf *hfp, hfp_debug_func_t callback, void *user_data, hfp_destroy_func_t destroy) { if (!hfp) return false; if (hfp->debug_destroy) hfp->debug_destroy(hfp->debug_data); hfp->debug_callback = callback; hfp->debug_destroy = destroy; hfp->debug_data = user_data; if (hfp->debug_callback) { ringbuf_set_input_tracing(hfp->read_buf, hf_read_tracing, hfp); ringbuf_set_input_tracing(hfp->write_buf, hf_write_tracing, hfp); } else { ringbuf_set_input_tracing(hfp->read_buf, NULL, NULL); ringbuf_set_input_tracing(hfp->write_buf, NULL, NULL); } return true; } bool hfp_hf_set_close_on_unref(struct hfp_hf *hfp, bool do_close) { if (!hfp) return false; hfp->close_on_unref = do_close; return true; } bool hfp_hf_send_command(struct hfp_hf *hfp, hfp_response_func_t resp_cb, void *user_data, const char *format, ...) { va_list ap; char *fmt; int len; struct cmd_response *cmd; if (!hfp || !format || !resp_cb) return false; if (asprintf(&fmt, "%s\r", format) < 0) return false; cmd = new0(struct cmd_response, 1); va_start(ap, format); len = ringbuf_vprintf(hfp->write_buf, fmt, ap); va_end(ap); free(fmt); if (len < 0) { free(cmd); return false; } cmd->resp_cb = resp_cb; cmd->user_data = user_data; if (!queue_push_tail(hfp->cmd_queue, cmd)) { ringbuf_drain(hfp->write_buf, len); free(cmd); return false; } hf_wakeup_writer(hfp); return true; } bool hfp_hf_register(struct hfp_hf *hfp, hfp_hf_result_func_t callback, const char *prefix, void *user_data, hfp_destroy_func_t destroy) { struct event_handler *handler; if (!callback) return false; handler = new0(struct event_handler, 1); handler->callback = callback; handler->user_data = user_data; handler->prefix = strdup(prefix); if (!handler->prefix) { free(handler); return false; } if (queue_find(hfp->event_handlers, match_handler_event_prefix, handler->prefix)) { destroy_event_handler(handler); return false; } handler->destroy = destroy; return queue_push_tail(hfp->event_handlers, handler); } bool hfp_hf_unregister(struct hfp_hf *hfp, const char *prefix) { struct cmd_handler *handler; /* Cast to void as queue_remove needs that */ handler = queue_remove_if(hfp->event_handlers, match_handler_event_prefix, (void *) prefix); if (!handler) return false; destroy_event_handler(handler); return true; } static void hf_disconnect_watch_destroy(void *user_data) { struct hfp_hf *hfp = user_data; if (hfp->disconnect_destroy) hfp->disconnect_destroy(hfp->disconnect_data); if (hfp->destroyed) free(hfp); } static bool hf_io_disconnected(struct io *io, void *user_data) { struct hfp_hf *hfp = user_data; hfp->in_disconnect = true; if (hfp->disconnect_callback) hfp->disconnect_callback(hfp->disconnect_data); hfp->in_disconnect = false; return false; } bool hfp_hf_set_disconnect_handler(struct hfp_hf *hfp, hfp_disconnect_func_t callback, void *user_data, hfp_destroy_func_t destroy) { if (!hfp) return false; if (hfp->disconnect_destroy) hfp->disconnect_destroy(hfp->disconnect_data); if (!io_set_disconnect_handler(hfp->io, hf_io_disconnected, hfp, hf_disconnect_watch_destroy)) { hfp->disconnect_callback = NULL; hfp->disconnect_destroy = NULL; hfp->disconnect_data = NULL; return false; } hfp->disconnect_callback = callback; hfp->disconnect_destroy = destroy; hfp->disconnect_data = user_data; return true; } bool hfp_hf_disconnect(struct hfp_hf *hfp) { if (!hfp) return false; return io_shutdown(hfp->io); } bluez-5.82/src/shared/PaxHeaders/att-types.h0000644000000000000000000000005014447506754016026 xustar0020 atime=1743515882 20 ctime=1743591277 bluez-5.82/src/shared/att-types.h0000644000000000000000000001275714447506754015523 0ustar00rootroot/* SPDX-License-Identifier: LGPL-2.1-or-later */ /* * * BlueZ - Bluetooth protocol stack for Linux * * Copyright (C) 2014 Google Inc. * Copyright 2023 NXP * * */ #include #ifndef __packed #define __packed __attribute__((packed)) #endif #define BT_ATT_CID 4 #define BT_ATT_PSM 31 #define BT_ATT_EATT_PSM 0x27 #define BT_ATT_SECURITY_AUTO 0 #define BT_ATT_SECURITY_LOW 1 #define BT_ATT_SECURITY_MEDIUM 2 #define BT_ATT_SECURITY_HIGH 3 #define BT_ATT_SECURITY_FIPS 4 #define BT_ATT_DEFAULT_LE_MTU 23 #define BT_ATT_MAX_LE_MTU 517 #define BT_ATT_MAX_VALUE_LEN 512 #define BT_ATT_BREDR 0x00 #define BT_ATT_LE 0x01 #define BT_ATT_EATT 0x02 #define BT_ATT_LOCAL 0xff /* ATT protocol opcodes */ #define BT_ATT_OP_ERROR_RSP 0x01 #define BT_ATT_OP_MTU_REQ 0x02 #define BT_ATT_OP_MTU_RSP 0x03 #define BT_ATT_OP_FIND_INFO_REQ 0x04 #define BT_ATT_OP_FIND_INFO_RSP 0x05 #define BT_ATT_OP_FIND_BY_TYPE_REQ 0x06 #define BT_ATT_OP_FIND_BY_TYPE_RSP 0x07 #define BT_ATT_OP_READ_BY_TYPE_REQ 0x08 #define BT_ATT_OP_READ_BY_TYPE_RSP 0x09 #define BT_ATT_OP_READ_REQ 0x0a #define BT_ATT_OP_READ_RSP 0x0b #define BT_ATT_OP_READ_BLOB_REQ 0x0c #define BT_ATT_OP_READ_BLOB_RSP 0x0d #define BT_ATT_OP_READ_MULT_REQ 0x0e #define BT_ATT_OP_READ_MULT_RSP 0x0f #define BT_ATT_OP_READ_BY_GRP_TYPE_REQ 0x10 #define BT_ATT_OP_READ_BY_GRP_TYPE_RSP 0x11 #define BT_ATT_OP_WRITE_REQ 0x12 #define BT_ATT_OP_WRITE_RSP 0x13 #define BT_ATT_OP_WRITE_CMD 0x52 #define BT_ATT_OP_SIGNED_WRITE_CMD 0xD2 #define BT_ATT_OP_PREP_WRITE_REQ 0x16 #define BT_ATT_OP_PREP_WRITE_RSP 0x17 #define BT_ATT_OP_EXEC_WRITE_REQ 0x18 #define BT_ATT_OP_EXEC_WRITE_RSP 0x19 #define BT_ATT_OP_HANDLE_NFY 0x1B #define BT_ATT_OP_HANDLE_IND 0x1D #define BT_ATT_OP_HANDLE_CONF 0x1E #define BT_ATT_OP_READ_MULT_VL_REQ 0x20 #define BT_ATT_OP_READ_MULT_VL_RSP 0x21 #define BT_ATT_OP_HANDLE_NFY_MULT 0x23 /* Packed struct definitions for ATT protocol PDUs */ /* TODO: Complete these definitions for all opcodes */ struct bt_att_pdu_error_rsp { uint8_t opcode; uint16_t handle; uint8_t ecode; } __packed; /* Special opcode to receive all requests (legacy servers) */ #define BT_ATT_ALL_REQUESTS 0x00 /* Error codes for Error response PDU */ #define BT_ATT_ERROR_INVALID_HANDLE 0x01 #define BT_ATT_ERROR_READ_NOT_PERMITTED 0x02 #define BT_ATT_ERROR_WRITE_NOT_PERMITTED 0x03 #define BT_ATT_ERROR_INVALID_PDU 0x04 #define BT_ATT_ERROR_AUTHENTICATION 0x05 #define BT_ATT_ERROR_REQUEST_NOT_SUPPORTED 0x06 #define BT_ATT_ERROR_INVALID_OFFSET 0x07 #define BT_ATT_ERROR_AUTHORIZATION 0x08 #define BT_ATT_ERROR_PREPARE_QUEUE_FULL 0x09 #define BT_ATT_ERROR_ATTRIBUTE_NOT_FOUND 0x0A #define BT_ATT_ERROR_ATTRIBUTE_NOT_LONG 0x0B #define BT_ATT_ERROR_INSUFFICIENT_ENCRYPTION_KEY_SIZE 0x0C #define BT_ATT_ERROR_INVALID_ATTRIBUTE_VALUE_LEN 0x0D #define BT_ATT_ERROR_UNLIKELY 0x0E #define BT_ATT_ERROR_INSUFFICIENT_ENCRYPTION 0x0F #define BT_ATT_ERROR_UNSUPPORTED_GROUP_TYPE 0x10 #define BT_ATT_ERROR_INSUFFICIENT_RESOURCES 0x11 #define BT_ATT_ERROR_DB_OUT_OF_SYNC 0x12 #define BT_ATT_ERROR_VALUE_NOT_ALLOWED 0x13 /* * Common Profile and Service Error Code descriptions (see Supplement to the * Bluetooth Core Specification, sections 1.2 and 2). The error codes within * 0xE0-0xFB are reserved for future use. The remaining 4 are defined as the * following: */ #define BT_ERROR_WRITE_REQUEST_REJECTED 0xfc #define BT_ERROR_CCC_IMPROPERLY_CONFIGURED 0xfd #define BT_ERROR_ALREADY_IN_PROGRESS 0xfe #define BT_ERROR_OUT_OF_RANGE 0xff /* * ATT attribute permission bitfield values. Permissions are grouped as * "Access", "Encryption", "Authentication", and "Authorization". A bitmask of * permissions is a byte that encodes a combination of these. */ #define BT_ATT_PERM_READ 0x01 #define BT_ATT_PERM_WRITE 0x02 #define BT_ATT_PERM_READ_ENCRYPT 0x04 #define BT_ATT_PERM_WRITE_ENCRYPT 0x08 #define BT_ATT_PERM_ENCRYPT (BT_ATT_PERM_READ_ENCRYPT | \ BT_ATT_PERM_WRITE_ENCRYPT) #define BT_ATT_PERM_READ_AUTHEN 0x10 #define BT_ATT_PERM_WRITE_AUTHEN 0x20 #define BT_ATT_PERM_AUTHEN (BT_ATT_PERM_READ_AUTHEN | \ BT_ATT_PERM_WRITE_AUTHEN) #define BT_ATT_PERM_AUTHOR 0x40 #define BT_ATT_PERM_NONE 0x80 #define BT_ATT_PERM_READ_SECURE 0x0100 #define BT_ATT_PERM_WRITE_SECURE 0x0200 #define BT_ATT_PERM_SECURE (BT_ATT_PERM_READ_SECURE | \ BT_ATT_PERM_WRITE_SECURE) #define BT_ATT_PERM_READ_MASK (BT_ATT_PERM_READ | \ BT_ATT_PERM_READ_AUTHEN | \ BT_ATT_PERM_READ_ENCRYPT | \ BT_ATT_PERM_READ_SECURE) #define BT_ATT_PERM_WRITE_MASK (BT_ATT_PERM_WRITE | \ BT_ATT_PERM_WRITE_AUTHEN | \ BT_ATT_PERM_WRITE_ENCRYPT | \ BT_ATT_PERM_WRITE_SECURE) /* GATT Characteristic Properties Bitfield values */ #define BT_GATT_CHRC_PROP_BROADCAST 0x01 #define BT_GATT_CHRC_PROP_READ 0x02 #define BT_GATT_CHRC_PROP_WRITE_WITHOUT_RESP 0x04 #define BT_GATT_CHRC_PROP_WRITE 0x08 #define BT_GATT_CHRC_PROP_NOTIFY 0x10 #define BT_GATT_CHRC_PROP_INDICATE 0x20 #define BT_GATT_CHRC_PROP_AUTH 0x40 #define BT_GATT_CHRC_PROP_EXT_PROP 0x80 /* GATT Characteristic Extended Properties Bitfield values */ #define BT_GATT_CHRC_EXT_PROP_RELIABLE_WRITE 0x01 #define BT_GATT_CHRC_EXT_PROP_WRITABLE_AUX 0x02 /* GATT Characteristic Client Features Bitfield values */ #define BT_GATT_CHRC_CLI_FEAT_ROBUST_CACHING 0x01 #define BT_GATT_CHRC_CLI_FEAT_EATT 0x02 #define BT_GATT_CHRC_CLI_FEAT_NFY_MULTI 0x04 /* GATT Characteristic Server Features Bitfield values */ #define BT_GATT_CHRC_SERVER_FEAT_EATT 0x01 bluez-5.82/src/shared/PaxHeaders/gatt-server.c0000644000000000000000000000005014643061455016322 xustar0020 atime=1743515894 20 ctime=1743591277 bluez-5.82/src/shared/gatt-server.c0000644000000000000000000012361514643061455016013 0ustar00rootroot// SPDX-License-Identifier: LGPL-2.1-or-later /* * * BlueZ - Bluetooth protocol stack for Linux * * Copyright (C) 2014 Google Inc. * Copyright 2023 NXP * * */ #ifdef HAVE_CONFIG_H #include #endif #include #include #include "src/shared/att.h" #include "lib/bluetooth.h" #include "lib/uuid.h" #include "src/shared/queue.h" #include "src/shared/gatt-db.h" #include "src/shared/gatt-server.h" #include "src/shared/gatt-helpers.h" #include "src/shared/util.h" #include "src/shared/timeout.h" #ifndef MAX #define MAX(a, b) ((a) > (b) ? (a) : (b)) #endif #ifndef MIN #define MIN(a, b) ((a) < (b) ? (a) : (b)) #endif /* * TODO: This is an arbitrary limit. Come up with something reasonable or * perhaps an API to set this value if there is a use case for it. */ #define DEFAULT_MAX_PREP_QUEUE_LEN 30 #define NFY_MULT_TIMEOUT 10 #define DBG(_server, _format, arg...) \ gatt_log(_server, "%s:%s() " _format, __FILE__, __func__, ## arg) struct async_read_op { struct bt_att_chan *chan; struct bt_gatt_server *server; uint8_t opcode; bool done; uint16_t mtu; uint8_t *pdu; size_t pdu_len; size_t value_len; struct queue *db_data; }; struct async_write_op { struct bt_att_chan *chan; struct bt_gatt_server *server; uint8_t opcode; }; struct prep_write_data { struct bt_gatt_server *server; uint8_t *value; uint16_t handle; uint16_t offset; uint16_t length; bool reliable_supported; }; static void prep_write_data_destroy(void *user_data) { struct prep_write_data *data = user_data; free(data->value); free(data); } struct nfy_mult_data { unsigned int id; uint8_t *pdu; uint16_t offset; uint16_t len; }; struct bt_gatt_server { struct gatt_db *db; struct bt_att *att; int ref_count; uint16_t mtu; unsigned int mtu_id; unsigned int read_by_grp_type_id; unsigned int read_by_type_id; unsigned int find_info_id; unsigned int find_by_type_value_id; unsigned int write_id; unsigned int write_cmd_id; unsigned int read_id; unsigned int read_blob_id; unsigned int read_multiple_id; unsigned int read_multiple_vl_id; unsigned int prep_write_id; unsigned int exec_write_id; unsigned int signed_write_cmd_id; uint8_t min_enc_size; struct queue *prep_queue; unsigned int max_prep_queue_len; bt_gatt_server_debug_func_t debug_callback; bt_gatt_server_destroy_func_t debug_destroy; void *debug_data; bt_gatt_server_authorize_cb_t authorize; void *authorize_data; struct nfy_mult_data *nfy_mult; }; static void notify_multiple_free(struct bt_gatt_server *server) { if (!server->nfy_mult) return; if (server->nfy_mult->id) timeout_remove(server->nfy_mult->id); free(server->nfy_mult->pdu); free(server->nfy_mult); server->nfy_mult = NULL; } static void bt_gatt_server_free(struct bt_gatt_server *server) { if (server->debug_destroy) server->debug_destroy(server->debug_data); notify_multiple_free(server); bt_att_unregister(server->att, server->mtu_id); bt_att_unregister(server->att, server->read_by_grp_type_id); bt_att_unregister(server->att, server->read_by_type_id); bt_att_unregister(server->att, server->find_info_id); bt_att_unregister(server->att, server->find_by_type_value_id); bt_att_unregister(server->att, server->write_id); bt_att_unregister(server->att, server->write_cmd_id); bt_att_unregister(server->att, server->read_id); bt_att_unregister(server->att, server->read_blob_id); bt_att_unregister(server->att, server->read_multiple_id); bt_att_unregister(server->att, server->read_multiple_vl_id); bt_att_unregister(server->att, server->prep_write_id); bt_att_unregister(server->att, server->exec_write_id); bt_att_unregister(server->att, server->signed_write_cmd_id); queue_destroy(server->prep_queue, prep_write_data_destroy); gatt_db_unref(server->db); bt_att_unref(server->att); free(server); } static bool get_uuid_le(const uint8_t *uuid, size_t len, bt_uuid_t *out_uuid) { uint128_t u128; switch (len) { case 2: bt_uuid16_create(out_uuid, get_le16(uuid)); return true; case 16: bswap_128(uuid, &u128.data); bt_uuid128_create(out_uuid, u128); return true; default: return false; } return false; } static void attribute_read_cb(struct gatt_db_attribute *attrib, int err, const uint8_t *value, size_t length, void *user_data) { struct iovec *iov = user_data; iov->iov_base = (void *) value; iov->iov_len = length; } static bool encode_read_by_grp_type_rsp(struct gatt_db *db, struct queue *q, struct bt_att *att, uint16_t mtu, uint8_t *pdu, uint16_t *len) { int iter = 0; uint16_t start_handle, end_handle; struct iovec value; uint8_t data_val_len; *len = 0; while (queue_peek_head(q)) { struct gatt_db_attribute *attrib = queue_pop_head(q); value.iov_base = NULL; value.iov_len = 0; /* * This should never be deferred to the read callback for * primary/secondary service declarations. */ if (!gatt_db_attribute_read(attrib, 0, BT_ATT_OP_READ_BY_GRP_TYPE_REQ, att, attribute_read_cb, &value) || !value.iov_len) return false; /* * Use the first attribute to determine the length of each * attribute data unit. Stop the list when a different attribute * value is seen. */ if (iter == 0) { data_val_len = MIN(MIN((unsigned int)mtu - 6, 251), value.iov_len); pdu[0] = data_val_len + 4; iter++; } else if (value.iov_len != data_val_len) break; /* Stop if this unit would surpass the MTU */ if (iter + data_val_len + 4 > mtu - 1) break; gatt_db_attribute_get_service_handles(attrib, &start_handle, &end_handle); put_le16(start_handle, pdu + iter); put_le16(end_handle, pdu + iter + 2); memcpy(pdu + iter + 4, value.iov_base, data_val_len); iter += data_val_len + 4; } *len = iter; return true; } static void gatt_log(struct bt_gatt_server *server, const char *format, ...) { va_list ap; if (!server || !format || !server->debug_callback) return; va_start(ap, format); util_debug_va(server->debug_callback, server->debug_data, format, ap); va_end(ap); } static void read_by_grp_type_cb(struct bt_att_chan *chan, uint16_t mtu, uint8_t opcode, const void *pdu, uint16_t length, void *user_data) { struct bt_gatt_server *server = user_data; uint16_t start, end; bt_uuid_t type; bt_uuid_t prim, snd; uint8_t rsp_pdu[mtu]; uint16_t rsp_len; uint8_t ecode = 0; uint16_t ehandle = 0; struct queue *q = NULL; if (length != 6 && length != 20) { ecode = BT_ATT_ERROR_INVALID_PDU; goto error; } q = queue_new(); start = get_le16(pdu); end = get_le16(pdu + 2); get_uuid_le(pdu + 4, length - 4, &type); DBG(server, "Read By Grp Type - start: 0x%04x end: 0x%04x", start, end); if (!start || !end) { ecode = BT_ATT_ERROR_INVALID_HANDLE; goto error; } ehandle = start; if (start > end) { ecode = BT_ATT_ERROR_INVALID_HANDLE; goto error; } /* * GATT defines that only the <> and * <> group types can be used for the * "Read By Group Type" request (Core v4.1, Vol 3, sec 2.5.3). Return an * error if any other group type is given. */ bt_uuid16_create(&prim, GATT_PRIM_SVC_UUID); bt_uuid16_create(&snd, GATT_SND_SVC_UUID); if (bt_uuid_cmp(&type, &prim) && bt_uuid_cmp(&type, &snd)) { ecode = BT_ATT_ERROR_UNSUPPORTED_GROUP_TYPE; goto error; } gatt_db_read_by_group_type(server->db, start, end, type, q); if (queue_isempty(q)) { ecode = BT_ATT_ERROR_ATTRIBUTE_NOT_FOUND; goto error; } if (!encode_read_by_grp_type_rsp(server->db, q, server->att, mtu, rsp_pdu, &rsp_len)) { ecode = BT_ATT_ERROR_UNLIKELY; goto error; } queue_destroy(q, NULL); bt_att_chan_send_rsp(chan, BT_ATT_OP_READ_BY_GRP_TYPE_RSP, rsp_pdu, rsp_len); return; error: queue_destroy(q, NULL); bt_att_chan_send_error_rsp(chan, opcode, ehandle, ecode); } static void async_read_op_destroy(struct async_read_op *op) { bt_gatt_server_unref(op->server); queue_destroy(op->db_data, NULL); free(op->pdu); free(op); } static void process_read_by_type(struct async_read_op *op); static void read_by_type_read_complete_cb(struct gatt_db_attribute *attr, int err, const uint8_t *value, size_t len, void *user_data) { struct async_read_op *op = user_data; uint16_t handle; handle = gatt_db_attribute_get_handle(attr); /* Terminate the operation if there was an error */ if (err) { bt_att_chan_send_error_rsp(op->chan, BT_ATT_OP_READ_BY_TYPE_REQ, handle, err); async_read_op_destroy(op); return; } if (op->pdu_len == 0) { op->value_len = MIN(MIN((unsigned int)op->mtu - 4, 253), len); op->pdu[0] = op->value_len + 2; op->pdu_len++; } else if (len != op->value_len) { op->done = true; goto done; } /* Stop if this would surpass the MTU */ if (op->pdu_len + op->value_len + 2 > (unsigned int) op->mtu - 1) { op->done = true; goto done; } /* Encode the current value */ put_le16(handle, op->pdu + op->pdu_len); memcpy(op->pdu + op->pdu_len + 2, value, op->value_len); op->pdu_len += op->value_len + 2; if (op->pdu_len == (unsigned int) op->mtu - 1) op->done = true; done: process_read_by_type(op); } static bool check_min_key_size(uint8_t min_size, uint8_t size) { if (!min_size || !size) return true; return min_size <= size; } static uint8_t check_permissions(struct bt_gatt_server *server, struct gatt_db_attribute *attr, uint32_t mask) { uint8_t enc_size; uint32_t perm; int security; perm = gatt_db_attribute_get_permissions(attr); if (perm && mask & BT_ATT_PERM_READ && !(perm & BT_ATT_PERM_READ)) return BT_ATT_ERROR_READ_NOT_PERMITTED; if (perm && mask & BT_ATT_PERM_WRITE && !(perm & BT_ATT_PERM_WRITE)) return BT_ATT_ERROR_WRITE_NOT_PERMITTED; perm &= mask; if (!perm) return 0; security = bt_att_get_security(server->att, &enc_size); if (security < 0) return BT_ATT_ERROR_UNLIKELY; if (perm & BT_ATT_PERM_SECURE) { if (security < BT_ATT_SECURITY_FIPS) return BT_ATT_ERROR_AUTHENTICATION; if (!check_min_key_size(server->min_enc_size, enc_size)) return BT_ATT_ERROR_INSUFFICIENT_ENCRYPTION_KEY_SIZE; } if (perm & BT_ATT_PERM_AUTHEN) { if (security < BT_ATT_SECURITY_HIGH) return BT_ATT_ERROR_AUTHENTICATION; if (!check_min_key_size(server->min_enc_size, enc_size)) return BT_ATT_ERROR_INSUFFICIENT_ENCRYPTION_KEY_SIZE; } if (perm & BT_ATT_PERM_ENCRYPT) { if (security < BT_ATT_SECURITY_MEDIUM) return BT_ATT_ERROR_INSUFFICIENT_ENCRYPTION; if (!check_min_key_size(server->min_enc_size, enc_size)) return BT_ATT_ERROR_INSUFFICIENT_ENCRYPTION_KEY_SIZE; } return 0; } static void process_read_by_type(struct async_read_op *op) { struct bt_gatt_server *server = op->server; uint8_t ecode; struct gatt_db_attribute *attr; attr = queue_pop_head(op->db_data); if (op->done || !attr) { bt_att_chan_send_rsp(op->chan, BT_ATT_OP_READ_BY_TYPE_RSP, op->pdu, op->pdu_len); async_read_op_destroy(op); return; } ecode = check_permissions(server, attr, BT_ATT_PERM_READ_MASK); if (ecode) goto error; if (gatt_db_attribute_read(attr, 0, op->opcode, server->att, read_by_type_read_complete_cb, op)) return; ecode = BT_ATT_ERROR_UNLIKELY; error: bt_att_chan_send_error_rsp(op->chan, BT_ATT_OP_READ_BY_TYPE_REQ, gatt_db_attribute_get_handle(attr), ecode); async_read_op_destroy(op); } static void read_by_type_cb(struct bt_att_chan *chan, uint16_t mtu, uint8_t opcode, const void *pdu, uint16_t length, void *user_data) { struct bt_gatt_server *server = user_data; uint16_t start, end; bt_uuid_t type; uint16_t ehandle = 0; uint8_t ecode; struct queue *q = NULL; struct async_read_op *op; if (length != 6 && length != 20) { ecode = BT_ATT_ERROR_INVALID_PDU; goto error; } q = queue_new(); start = get_le16(pdu); end = get_le16(pdu + 2); get_uuid_le(pdu + 4, length - 4, &type); DBG(server, "Read By Type - start: 0x%04x end: 0x%04x", start, end); if (!start || !end) { ecode = BT_ATT_ERROR_INVALID_HANDLE; goto error; } ehandle = start; if (start > end) { ecode = BT_ATT_ERROR_INVALID_HANDLE; goto error; } gatt_db_read_by_type(server->db, start, end, type, q); if (queue_isempty(q)) { ecode = BT_ATT_ERROR_ATTRIBUTE_NOT_FOUND; goto error; } op = new0(struct async_read_op, 1); op->mtu = mtu; op->pdu = malloc(mtu); if (!op->pdu) { free(op); ecode = BT_ATT_ERROR_INSUFFICIENT_RESOURCES; goto error; } op->chan = chan; op->opcode = opcode; op->server = bt_gatt_server_ref(server); op->db_data = q; process_read_by_type(op); return; error: bt_att_chan_send_error_rsp(chan, opcode, ehandle, ecode); queue_destroy(q, NULL); } static bool encode_find_info_rsp(struct gatt_db *db, struct queue *q, uint16_t mtu, uint8_t *pdu, uint16_t *len) { uint16_t handle; struct gatt_db_attribute *attr; const bt_uuid_t *type; int uuid_len, cur_uuid_len; int iter = 0; *len = 0; while (queue_peek_head(q)) { attr = queue_pop_head(q); handle = gatt_db_attribute_get_handle(attr); type = gatt_db_attribute_get_type(attr); if (!handle || !type) return false; cur_uuid_len = bt_uuid_len(type); if (iter == 0) { switch (cur_uuid_len) { case 2: uuid_len = 2; pdu[0] = 0x01; break; case 4: case 16: uuid_len = 16; pdu[0] = 0x02; break; default: return false; } iter++; } else if (cur_uuid_len != uuid_len) break; if (iter + uuid_len + 2 > mtu - 1) break; put_le16(handle, pdu + iter); bt_uuid_to_le(type, pdu + iter + 2); iter += uuid_len + 2; } *len = iter; return true; } static void find_info_cb(struct bt_att_chan *chan, uint16_t mtu, uint8_t opcode, const void *pdu, uint16_t length, void *user_data) { struct bt_gatt_server *server = user_data; uint16_t start, end; uint8_t rsp_pdu[mtu]; uint16_t rsp_len; uint8_t ecode = 0; uint16_t ehandle = 0; struct queue *q = NULL; if (length != 4) { ecode = BT_ATT_ERROR_INVALID_PDU; goto error; } q = queue_new(); start = get_le16(pdu); end = get_le16(pdu + 2); DBG(server, "Find Info - start: 0x%04x end: 0x%04x", start, end); if (!start || !end) { ecode = BT_ATT_ERROR_INVALID_HANDLE; goto error; } ehandle = start; if (start > end) { ecode = BT_ATT_ERROR_INVALID_HANDLE; goto error; } gatt_db_find_information(server->db, start, end, q); if (queue_isempty(q)) { ecode = BT_ATT_ERROR_ATTRIBUTE_NOT_FOUND; goto error; } if (!encode_find_info_rsp(server->db, q, mtu, rsp_pdu, &rsp_len)) { ecode = BT_ATT_ERROR_UNLIKELY; goto error; } bt_att_chan_send_rsp(chan, BT_ATT_OP_FIND_INFO_RSP, rsp_pdu, rsp_len); queue_destroy(q, NULL); return; error: bt_att_chan_send_error_rsp(chan, opcode, ehandle, ecode); queue_destroy(q, NULL); } struct find_by_type_val_data { uint8_t *pdu; uint16_t len; uint16_t mtu; uint8_t ecode; }; static void find_by_type_val_att_cb(struct gatt_db_attribute *attrib, void *user_data) { uint16_t handle, end_handle; struct find_by_type_val_data *data = user_data; if (data->ecode) return; if (data->len + 4 > data->mtu - 1) return; /* * This OP is only valid for Primary Service per the spec * page 562, so this should work. */ gatt_db_attribute_get_service_data(attrib, &handle, &end_handle, NULL, NULL); if (!handle || !end_handle) { data->ecode = BT_ATT_ERROR_UNLIKELY; return; } put_le16(handle, data->pdu + data->len); put_le16(end_handle, data->pdu + data->len + 2); data->len += 4; } static void find_by_type_val_cb(struct bt_att_chan *chan, uint16_t mtu, uint8_t opcode, const void *pdu, uint16_t length, void *user_data) { struct bt_gatt_server *server = user_data; uint16_t start, end, uuid16; struct find_by_type_val_data data; uint8_t rsp_pdu[mtu]; uint16_t ehandle = 0; bt_uuid_t uuid; if (length < 6) { data.ecode = BT_ATT_ERROR_INVALID_PDU; goto error; } data.pdu = rsp_pdu; data.len = 0; data.mtu = mtu; data.ecode = 0; start = get_le16(pdu); end = get_le16(pdu + 2); uuid16 = get_le16(pdu + 4); DBG(server, "Find By Type Value - start: 0x%04x end: 0x%04x uuid: 0x%04x", start, end, uuid16); ehandle = start; if (start > end) { data.ecode = BT_ATT_ERROR_INVALID_HANDLE; goto error; } bt_uuid16_create(&uuid, uuid16); gatt_db_find_by_type_value(server->db, start, end, &uuid, pdu + 6, length - 6, find_by_type_val_att_cb, &data); if (!data.len) data.ecode = BT_ATT_ERROR_ATTRIBUTE_NOT_FOUND; if (data.ecode) goto error; bt_att_chan_send_rsp(chan, BT_ATT_OP_FIND_BY_TYPE_RSP, data.pdu, data.len); return; error: bt_att_chan_send_error_rsp(chan, opcode, ehandle, data.ecode); } static void async_write_op_destroy(struct async_write_op *op) { bt_gatt_server_unref(op->server); free(op); } static void write_complete_cb(struct gatt_db_attribute *attr, int err, void *user_data) { struct async_write_op *op = user_data; struct bt_gatt_server *server = op->server; uint16_t handle; if (op->opcode == BT_ATT_OP_WRITE_CMD || op->opcode == BT_ATT_OP_SIGNED_WRITE_CMD) { async_write_op_destroy(op); return; } DBG(server, "Write Complete: err %d", err); handle = gatt_db_attribute_get_handle(attr); if (err) bt_att_chan_send_error_rsp(op->chan, op->opcode, handle, err); else bt_att_chan_send_rsp(op->chan, BT_ATT_OP_WRITE_RSP, NULL, 0); async_write_op_destroy(op); } static uint8_t authorize_req(struct bt_gatt_server *server, uint8_t opcode, uint16_t handle) { if (!server->authorize) return 0; return server->authorize(server->att, opcode, handle, server->authorize_data); } static uint8_t check_length(uint16_t length, uint16_t offset) { if (length > BT_ATT_MAX_VALUE_LEN) return BT_ATT_ERROR_INVALID_ATTRIBUTE_VALUE_LEN; if (offset > BT_ATT_MAX_VALUE_LEN) return BT_ATT_ERROR_INVALID_OFFSET; if (length + offset > BT_ATT_MAX_VALUE_LEN) return BT_ATT_ERROR_INVALID_ATTRIBUTE_VALUE_LEN; return 0; } static void write_cb(struct bt_att_chan *chan, uint16_t mtu, uint8_t opcode, const void *pdu, uint16_t length, void *user_data) { struct bt_gatt_server *server = user_data; struct gatt_db_attribute *attr; uint16_t handle = 0; struct async_write_op *op = NULL; uint8_t ecode; if (length < 2) { ecode = BT_ATT_ERROR_INVALID_PDU; goto error; } ecode = authorize_req(server, opcode, handle); if (ecode) goto error; handle = get_le16(pdu); attr = gatt_db_get_attribute(server->db, handle); if (!attr) { ecode = BT_ATT_ERROR_INVALID_HANDLE; goto error; } DBG(server, "Write %s - handle: 0x%04x", (opcode == BT_ATT_OP_WRITE_REQ) ? "Req" : "Cmd", handle); ecode = check_length(length - 2, 0); if (ecode) goto error; ecode = check_permissions(server, attr, BT_ATT_PERM_WRITE_MASK); if (ecode) goto error; op = new0(struct async_write_op, 1); op->chan = chan; op->server = bt_gatt_server_ref(server); op->opcode = opcode; if (gatt_db_attribute_write(attr, 0, pdu + 2, length - 2, opcode, server->att, write_complete_cb, op)) return; async_write_op_destroy(op); ecode = BT_ATT_ERROR_UNLIKELY; error: if (opcode == BT_ATT_OP_WRITE_CMD) return; bt_att_chan_send_error_rsp(chan, opcode, handle, ecode); } static uint8_t get_read_rsp_opcode(uint8_t opcode) { switch (opcode) { case BT_ATT_OP_READ_REQ: return BT_ATT_OP_READ_RSP; case BT_ATT_OP_READ_BLOB_REQ: return BT_ATT_OP_READ_BLOB_RSP; default: /* * Should never happen * * TODO: It would be nice to have a debug-mode assert macro * for development builds. This way bugs could be easily catched * during development and there would be self documenting code * that wouldn't be crash release builds. */ return 0; } return 0; } static void read_complete_cb(struct gatt_db_attribute *attr, int err, const uint8_t *value, size_t len, void *user_data) { struct async_read_op *op = user_data; struct bt_gatt_server *server = op->server; uint8_t rsp_opcode; uint16_t handle; DBG(server, "Read Complete: err %d", err); handle = gatt_db_attribute_get_handle(attr); if (err) { bt_att_chan_send_error_rsp(op->chan, op->opcode, handle, err); async_read_op_destroy(op); return; } rsp_opcode = get_read_rsp_opcode(op->opcode); bt_att_chan_send_rsp(op->chan, rsp_opcode, len ? value : NULL, MIN((unsigned int) op->mtu - 1, len)); async_read_op_destroy(op); } static void handle_read_req(struct bt_att_chan *chan, struct bt_gatt_server *server, uint16_t mtu, uint8_t opcode, uint16_t handle, uint16_t offset) { struct gatt_db_attribute *attr; uint8_t ecode; struct async_read_op *op = NULL; ecode = authorize_req(server, opcode, handle); if (ecode) goto error; attr = gatt_db_get_attribute(server->db, handle); if (!attr) { ecode = BT_ATT_ERROR_INVALID_HANDLE; goto error; } DBG(server, "Read %sReq - handle: 0x%04x", opcode == BT_ATT_OP_READ_BLOB_REQ ? "Blob " : "", handle); ecode = check_permissions(server, attr, BT_ATT_PERM_READ_MASK); if (ecode) goto error; op = new0(struct async_read_op, 1); op->chan = chan; op->opcode = opcode; op->server = bt_gatt_server_ref(server); op->mtu = mtu; if (gatt_db_attribute_read(attr, offset, opcode, server->att, read_complete_cb, op)) return; ecode = BT_ATT_ERROR_UNLIKELY; error: if (op) async_read_op_destroy(op); bt_att_chan_send_error_rsp(chan, opcode, handle, ecode); } static void read_cb(struct bt_att_chan *chan, uint16_t mtu, uint8_t opcode, const void *pdu, uint16_t length, void *user_data) { struct bt_gatt_server *server = user_data; uint16_t handle; if (length != 2) { bt_att_chan_send_error_rsp(chan, opcode, 0, BT_ATT_ERROR_INVALID_PDU); return; } handle = get_le16(pdu); handle_read_req(chan, server, mtu, opcode, handle, 0); } static void read_blob_cb(struct bt_att_chan *chan, uint16_t mtu, uint8_t opcode, const void *pdu, uint16_t length, void *user_data) { struct bt_gatt_server *server = user_data; uint16_t handle, offset; if (length != 4) { bt_att_chan_send_error_rsp(chan, opcode, 0, BT_ATT_ERROR_INVALID_PDU); return; } handle = get_le16(pdu); offset = get_le16(pdu + 2); handle_read_req(chan, server, mtu, opcode, handle, offset); } struct read_mult_data { struct bt_att_chan *chan; struct bt_gatt_server *server; uint8_t opcode; uint16_t *handles; size_t cur_handle; size_t num_handles; uint8_t *rsp_data; size_t length; size_t mtu; }; static void read_mult_data_free(struct read_mult_data *data) { free(data->handles); free(data->rsp_data); free(data); } static void read_multiple_complete_cb(struct gatt_db_attribute *attr, int err, const uint8_t *value, size_t len, void *user_data) { struct read_mult_data *data = user_data; struct gatt_db_attribute *next_attr; uint16_t handle = gatt_db_attribute_get_handle(attr); uint8_t ecode; uint16_t length; if (err != 0) { ecode = err; goto error; } length = data->opcode == BT_ATT_OP_READ_MULT_VL_REQ ? MIN(len, MAX(data->mtu - data->length, 3) - 3) : MIN(len, data->mtu - data->length - 1); if (data->opcode == BT_ATT_OP_READ_MULT_VL_REQ) { /* The Length Value Tuple List may be truncated within the first * two octets of a tuple due to the size limits of the current * ATT_MTU, but the first two octets cannot be separated. */ if (data->mtu - data->length >= 3) { put_le16(len, data->rsp_data + data->length); data->length += 2; } } memcpy(data->rsp_data + data->length, value, length); data->length += length; data->cur_handle++; if (data->cur_handle == data->num_handles) { bt_att_chan_send_rsp(data->chan, data->opcode + 1, data->rsp_data, data->length); read_mult_data_free(data); return; } handle = data->handles[data->cur_handle]; util_debug(data->server->debug_callback, data->server->debug_data, "%s Req - #%zu of %zu: 0x%04x", data->opcode == BT_ATT_OP_READ_MULT_REQ ? "Read Multiple" : "Read Multiple Variable Length", data->cur_handle + 1, data->num_handles, handle); next_attr = gatt_db_get_attribute(data->server->db, handle); if (!next_attr) { ecode = BT_ATT_ERROR_INVALID_HANDLE; goto error; } ecode = check_permissions(data->server, next_attr, BT_ATT_PERM_READ_MASK); if (ecode) goto error; if (gatt_db_attribute_read(next_attr, 0, data->opcode, data->server->att, read_multiple_complete_cb, data)) return; ecode = BT_ATT_ERROR_UNLIKELY; error: bt_att_chan_send_error_rsp(data->chan, data->opcode, handle, ecode); read_mult_data_free(data); } static struct read_mult_data *read_mult_data_new(struct bt_gatt_server *server, struct bt_att_chan *chan, uint16_t mtu, uint8_t opcode, uint16_t num_handles) { struct read_mult_data *data; data = new0(struct read_mult_data, 1); data->chan = chan; data->opcode = opcode; data->handles = new0(uint16_t, num_handles); data->rsp_data = NULL; data->server = server; data->num_handles = num_handles; data->cur_handle = 0; data->mtu = mtu; data->length = 0; data->rsp_data = new0(uint8_t, data->mtu - 1); return data; } static void read_multiple_cb(struct bt_att_chan *chan, uint16_t mtu, uint8_t opcode, const void *pdu, uint16_t length, void *user_data) { struct bt_gatt_server *server = user_data; struct gatt_db_attribute *attr; struct read_mult_data *data = NULL; uint8_t ecode; size_t i = 0; uint16_t handle = 0; if (length < 4) { ecode = BT_ATT_ERROR_INVALID_PDU; goto error; } data = read_mult_data_new(server, chan, mtu, opcode, length / 2); for (i = 0; i < data->num_handles; i++) data->handles[i] = get_le16(pdu + i * 2); handle = data->handles[0]; DBG(server, "%s Req - %zu handles, 1st: 0x%04x", data->opcode == BT_ATT_OP_READ_MULT_REQ ? "Read Multiple" : "Read Multiple Variable Length", data->num_handles, handle); attr = gatt_db_get_attribute(server->db, handle); if (!attr) { ecode = BT_ATT_ERROR_INVALID_HANDLE; goto error; } ecode = check_permissions(data->server, attr, BT_ATT_PERM_READ_MASK); if (ecode) goto error; if (gatt_db_attribute_read(attr, 0, opcode, server->att, read_multiple_complete_cb, data)) return; ecode = BT_ATT_ERROR_UNLIKELY; error: if (data) read_mult_data_free(data); bt_att_chan_send_error_rsp(chan, opcode, handle, ecode); } static bool append_prep_data(struct prep_write_data *prep_data, uint16_t handle, uint16_t length, uint8_t *value) { uint8_t *val; uint16_t len; if (!length) return true; len = prep_data->length + length; val = realloc(prep_data->value, len); if (!val) return false; memcpy(val + prep_data->length, value, length); prep_data->value = val; prep_data->length = len; return true; } static bool is_reliable_write_supported(const struct bt_gatt_server *server, uint16_t handle) { struct gatt_db_attribute *attr; uint16_t ext_prop; attr = gatt_db_get_attribute(server->db, handle); if (!attr) return false; if (!gatt_db_attribute_get_char_data(attr, NULL, NULL, NULL, &ext_prop, NULL)) return false; return (ext_prop & BT_GATT_CHRC_EXT_PROP_RELIABLE_WRITE); } static bool prep_data_new(struct bt_gatt_server *server, uint16_t handle, uint16_t offset, uint16_t length, uint8_t *value) { struct prep_write_data *prep_data; prep_data = new0(struct prep_write_data, 1); if (!append_prep_data(prep_data, handle, length, value)) { prep_write_data_destroy(prep_data); return false; } prep_data->server = server; prep_data->handle = handle; prep_data->offset = offset; /* * Handle is the value handle. We need characteristic declaration * handle which in BlueZ is handle_value -1 */ prep_data->reliable_supported = is_reliable_write_supported(server, handle - 1); queue_push_tail(server->prep_queue, prep_data); return true; } static bool store_prep_data(struct bt_gatt_server *server, uint16_t handle, uint16_t offset, uint16_t length, uint8_t *value) { struct prep_write_data *prep_data = NULL; /* * Now lets check if prep write is a continuation of long write * If so do aggregation of data */ prep_data = queue_peek_tail(server->prep_queue); if (prep_data && (prep_data->handle == handle) && (offset == (prep_data->length + prep_data->offset))) return append_prep_data(prep_data, handle, length, value); return prep_data_new(server, handle, offset, length, value); } struct prep_write_complete_data { struct bt_att_chan *chan; void *pdu; uint16_t length; struct bt_gatt_server *server; }; static void prep_write_complete_cb(struct gatt_db_attribute *attr, int err, void *user_data) { struct prep_write_complete_data *pwcd = user_data; uint16_t handle = 0; uint16_t offset; handle = get_le16(pwcd->pdu); if (err) { bt_att_chan_send_error_rsp(pwcd->chan, BT_ATT_OP_PREP_WRITE_REQ, handle, err); free(pwcd->pdu); free(pwcd); return; } offset = get_le16(pwcd->pdu + 2); if (!store_prep_data(pwcd->server, handle, offset, pwcd->length - 4, &((uint8_t *) pwcd->pdu)[4])) bt_att_chan_send_error_rsp(pwcd->chan, BT_ATT_OP_PREP_WRITE_RSP, handle, BT_ATT_ERROR_INSUFFICIENT_RESOURCES); bt_att_chan_send_rsp(pwcd->chan, BT_ATT_OP_PREP_WRITE_RSP, pwcd->pdu, pwcd->length); free(pwcd->pdu); free(pwcd); } static void prep_write_cb(struct bt_att_chan *chan, uint16_t mtu, uint8_t opcode, const void *pdu, uint16_t length, void *user_data) { struct bt_gatt_server *server = user_data; uint16_t handle = 0; uint16_t offset; struct gatt_db_attribute *attr; struct prep_write_complete_data *pwcd; uint8_t ecode, status; if (length < 4) { ecode = BT_ATT_ERROR_INVALID_PDU; goto error; } if (queue_length(server->prep_queue) >= server->max_prep_queue_len) { ecode = BT_ATT_ERROR_PREPARE_QUEUE_FULL; goto error; } handle = get_le16(pdu); offset = get_le16(pdu + 2); attr = gatt_db_get_attribute(server->db, handle); if (!attr) { ecode = BT_ATT_ERROR_INVALID_HANDLE; goto error; } DBG(server, "Prep Write Req - handle: 0x%04x", handle); ecode = check_length(length - 4, offset); if (ecode) goto error; ecode = check_permissions(server, attr, BT_ATT_PERM_WRITE_MASK); if (ecode) goto error; pwcd = new0(struct prep_write_complete_data, 1); pwcd->chan = chan; pwcd->pdu = malloc(length); memcpy(pwcd->pdu, pdu, length); pwcd->length = length; pwcd->server = server; status = gatt_db_attribute_write(attr, offset, NULL, 0, BT_ATT_OP_PREP_WRITE_REQ, server->att, prep_write_complete_cb, pwcd); if (status) return; ecode = BT_ATT_ERROR_UNLIKELY; error: bt_att_chan_send_error_rsp(chan, opcode, handle, ecode); } struct exec_data { struct bt_att_chan *chan; struct bt_gatt_server *server; }; static void exec_next_prep_write(struct exec_data *data, uint16_t ehandle, int err); static void exec_write_complete_cb(struct gatt_db_attribute *attr, int err, void *user_data) { struct exec_data *data = user_data; uint16_t handle = gatt_db_attribute_get_handle(attr); exec_next_prep_write(data, handle, err); } static void exec_next_prep_write(struct exec_data *data, uint16_t ehandle, int err) { struct prep_write_data *next = NULL; struct gatt_db_attribute *attr; bool status; if (err) goto error; next = queue_pop_head(data->server->prep_queue); if (!next) { bt_att_chan_send_rsp(data->chan, BT_ATT_OP_EXEC_WRITE_RSP, NULL, 0); free(data); return; } attr = gatt_db_get_attribute(data->server->db, next->handle); if (!attr) { err = BT_ATT_ERROR_UNLIKELY; goto error; } status = gatt_db_attribute_write(attr, next->offset, next->value, next->length, BT_ATT_OP_EXEC_WRITE_REQ, data->server->att, exec_write_complete_cb, data); prep_write_data_destroy(next); if (status) return; err = BT_ATT_ERROR_UNLIKELY; error: queue_remove_all(data->server->prep_queue, NULL, NULL, prep_write_data_destroy); bt_att_chan_send_error_rsp(data->chan, BT_ATT_OP_EXEC_WRITE_REQ, ehandle, err); free(data); } static bool find_no_reliable_characteristic(const void *data, const void *match_data) { const struct prep_write_data *prep_data = data; return !prep_data->reliable_supported; } static void exec_write_cb(struct bt_att_chan *chan, uint16_t mtu, uint8_t opcode, const void *pdu, uint16_t length, void *user_data) { struct bt_gatt_server *server = user_data; struct exec_data *data; uint8_t flags; uint8_t ecode; bool write; uint16_t ehandle = 0; if (length != 1) { ecode = BT_ATT_ERROR_INVALID_PDU; goto error; } flags = ((uint8_t *) pdu)[0]; DBG(server, "Exec Write Req - flags: 0x%02x", flags); if (flags == 0x00) write = false; else if (flags == 0x01) write = true; else { ecode = BT_ATT_ERROR_INVALID_PDU; goto error; } if (!write) { queue_remove_all(server->prep_queue, NULL, NULL, prep_write_data_destroy); bt_att_chan_send_rsp(chan, BT_ATT_OP_EXEC_WRITE_RSP, NULL, 0); return; } /* If there is more than one prep request, we are in reliable session */ if (queue_length(server->prep_queue) > 1) { struct prep_write_data *prep_data; prep_data = queue_find(server->prep_queue, find_no_reliable_characteristic, NULL); if (prep_data) { ecode = BT_ATT_ERROR_REQUEST_NOT_SUPPORTED; ehandle = prep_data->handle; goto error; } } data = new0(struct exec_data, 1); data->chan = chan; data->server = server; exec_next_prep_write(data, 0, 0); return; error: queue_remove_all(server->prep_queue, NULL, NULL, prep_write_data_destroy); bt_att_chan_send_error_rsp(chan, opcode, ehandle, ecode); } static void exchange_mtu_cb(struct bt_att_chan *chan, uint16_t mtu, uint8_t opcode, const void *pdu, uint16_t length, void *user_data) { struct bt_gatt_server *server = user_data; uint16_t client_rx_mtu; uint16_t final_mtu; uint8_t rsp_pdu[2]; if (length != 2) { bt_att_chan_send_error_rsp(chan, opcode, 0, BT_ATT_ERROR_INVALID_PDU); return; } client_rx_mtu = get_le16(pdu); final_mtu = MAX(MIN(client_rx_mtu, server->mtu), BT_ATT_DEFAULT_LE_MTU); /* Respond with the server MTU */ put_le16(server->mtu, rsp_pdu); bt_att_chan_send_rsp(chan, BT_ATT_OP_MTU_RSP, rsp_pdu, 2); /* Set MTU to be the minimum */ server->mtu = final_mtu; bt_att_set_mtu(server->att, final_mtu); DBG(server, "MTU exchange complete, with MTU: %u", final_mtu); } static bool gatt_server_register_att_handlers(struct bt_gatt_server *server) { /* Exchange MTU */ server->mtu_id = bt_att_register(server->att, BT_ATT_OP_MTU_REQ, exchange_mtu_cb, server, NULL); if (!server->mtu_id) return false; /* Read By Group Type */ server->read_by_grp_type_id = bt_att_register(server->att, BT_ATT_OP_READ_BY_GRP_TYPE_REQ, read_by_grp_type_cb, server, NULL); if (!server->read_by_grp_type_id) return false; /* Read By Type */ server->read_by_type_id = bt_att_register(server->att, BT_ATT_OP_READ_BY_TYPE_REQ, read_by_type_cb, server, NULL); if (!server->read_by_type_id) return false; /* Find Information */ server->find_info_id = bt_att_register(server->att, BT_ATT_OP_FIND_INFO_REQ, find_info_cb, server, NULL); if (!server->find_info_id) return false; /* Find By Type Value */ server->find_by_type_value_id = bt_att_register(server->att, BT_ATT_OP_FIND_BY_TYPE_REQ, find_by_type_val_cb, server, NULL); if (!server->find_by_type_value_id) return false; /* Write Request */ server->write_id = bt_att_register(server->att, BT_ATT_OP_WRITE_REQ, write_cb, server, NULL); if (!server->write_id) return false; /* Write Command */ server->write_cmd_id = bt_att_register(server->att, BT_ATT_OP_WRITE_CMD, write_cb, server, NULL); if (!server->write_cmd_id) return false; /* Read Request */ server->read_id = bt_att_register(server->att, BT_ATT_OP_READ_REQ, read_cb, server, NULL); if (!server->read_id) return false; /* Read Blob Request */ server->read_blob_id = bt_att_register(server->att, BT_ATT_OP_READ_BLOB_REQ, read_blob_cb, server, NULL); if (!server->read_blob_id) return false; /* Read Multiple Request */ server->read_multiple_id = bt_att_register(server->att, BT_ATT_OP_READ_MULT_REQ, read_multiple_cb, server, NULL); if (!server->read_multiple_id) return false; /* Read Multiple Variable Length Request */ server->read_multiple_vl_id = bt_att_register(server->att, BT_ATT_OP_READ_MULT_VL_REQ, read_multiple_cb, server, NULL); if (!server->read_multiple_vl_id) return false; /* Prepare Write Request */ server->prep_write_id = bt_att_register(server->att, BT_ATT_OP_PREP_WRITE_REQ, prep_write_cb, server, NULL); if (!server->prep_write_id) return false; /* Execute Write Request */ server->exec_write_id = bt_att_register(server->att, BT_ATT_OP_EXEC_WRITE_REQ, exec_write_cb, server, NULL); if (!server->exec_write_id) return NULL; /* Signed Write Command */ server->signed_write_cmd_id = bt_att_register(server->att, BT_ATT_OP_SIGNED_WRITE_CMD, write_cb, server, NULL); if (!server->signed_write_cmd_id) return false; return true; } struct bt_gatt_server *bt_gatt_server_new(struct gatt_db *db, struct bt_att *att, uint16_t mtu, uint8_t min_enc_size) { struct bt_gatt_server *server; if (!att || !db) return NULL; server = new0(struct bt_gatt_server, 1); server->db = gatt_db_ref(db); server->att = bt_att_ref(att); server->mtu = MAX(mtu, BT_ATT_DEFAULT_LE_MTU); server->max_prep_queue_len = DEFAULT_MAX_PREP_QUEUE_LEN; server->prep_queue = queue_new(); server->min_enc_size = min_enc_size; if (!gatt_server_register_att_handlers(server)) { bt_gatt_server_free(server); return NULL; } return bt_gatt_server_ref(server); } uint16_t bt_gatt_server_get_mtu(struct bt_gatt_server *server) { if (!server || !server->att) return 0; return bt_att_get_mtu(server->att); } struct bt_att *bt_gatt_server_get_att(struct bt_gatt_server *server) { if (!server) return NULL; return server->att; } struct bt_gatt_server *bt_gatt_server_ref(struct bt_gatt_server *server) { if (!server) return NULL; __sync_fetch_and_add(&server->ref_count, 1); return server; } void bt_gatt_server_unref(struct bt_gatt_server *server) { if (!server) return; if (__sync_sub_and_fetch(&server->ref_count, 1)) return; bt_gatt_server_free(server); } bool bt_gatt_server_set_debug(struct bt_gatt_server *server, bt_gatt_server_debug_func_t callback, void *user_data, bt_gatt_server_destroy_func_t destroy) { if (!server) return false; if (server->debug_destroy) server->debug_destroy(server->debug_data); server->debug_callback = callback; server->debug_destroy = destroy; server->debug_data = user_data; return true; } static void notify_multiple_timeout_remove(struct bt_gatt_server *server) { if (!server->nfy_mult->id) return; timeout_remove(server->nfy_mult->id); server->nfy_mult->id = 0; } static bool notify_multiple(void *user_data) { struct bt_gatt_server *server = user_data; server->nfy_mult->id = 0; bt_att_send(server->att, BT_ATT_OP_HANDLE_NFY_MULT, server->nfy_mult->pdu, server->nfy_mult->offset, NULL, NULL, NULL); notify_multiple_free(server); return false; } static bool notify_append_le16(struct nfy_mult_data *data, uint16_t value) { if (data->offset + sizeof(value) > data->len) return false; put_le16(value, data->pdu + data->offset); data->offset += sizeof(value); return true; } bool bt_gatt_server_send_notification(struct bt_gatt_server *server, uint16_t handle, const uint8_t *value, uint16_t length, bool multiple) { struct nfy_mult_data *data = NULL; bool result; if (!server || (length && !value)) return false; if (multiple) { data = server->nfy_mult; /* flush buffered data if this request hits buffer size limit */ if (data && data->offset > 0 && data->len - data->offset < 4 + length) { notify_multiple_timeout_remove(server); notify_multiple(server); /* data has been freed by notify_multiple */ data = NULL; } } if (!data) { data = new0(struct nfy_mult_data, 1); data->len = bt_att_get_mtu(server->att) - 1; data->pdu = malloc(data->len); } if (!notify_append_le16(data, handle)) goto error; if (multiple) { length = MIN(data->len - data->offset - 2, length); if (!notify_append_le16(data, length)) goto error; } else { length = MIN(data->len - data->offset, length); } if (value) memcpy(data->pdu + data->offset, value, length); data->offset += length; if (multiple) { if (!server->nfy_mult) server->nfy_mult = data; if (!server->nfy_mult->id) server->nfy_mult->id = timeout_add(NFY_MULT_TIMEOUT, notify_multiple, server, NULL); return true; } result = !!bt_att_send(server->att, BT_ATT_OP_HANDLE_NFY, data->pdu, data->offset, NULL, NULL, NULL); free(data->pdu); free(data); return result; error: if (data) { free(data->pdu); free(data); } return false; } struct ind_data { bt_gatt_server_conf_func_t callback; bt_gatt_server_destroy_func_t destroy; void *user_data; }; static void destroy_ind_data(void *user_data) { struct ind_data *data = user_data; if (data->destroy) data->destroy(data->user_data); free(data); } static void conf_cb(uint8_t opcode, const void *pdu, uint16_t length, void *user_data) { struct ind_data *data = user_data; if (data->callback) data->callback(data->user_data); } bool bt_gatt_server_send_indication(struct bt_gatt_server *server, uint16_t handle, const uint8_t *value, uint16_t length, bt_gatt_server_conf_func_t callback, void *user_data, bt_gatt_server_destroy_func_t destroy) { uint16_t pdu_len; uint8_t *pdu; struct ind_data *data; bool result; if (!server || (length && !value)) return false; pdu_len = MIN(bt_att_get_mtu(server->att) - 1, length + 2); pdu = malloc(pdu_len); if (!pdu) return false; data = new0(struct ind_data, 1); data->callback = callback; data->destroy = destroy; data->user_data = user_data; put_le16(handle, pdu); memcpy(pdu + 2, value, pdu_len - 2); result = !!bt_att_send(server->att, BT_ATT_OP_HANDLE_IND, pdu, pdu_len, conf_cb, data, destroy_ind_data); if (!result) destroy_ind_data(data); free(pdu); return result; } bool bt_gatt_server_set_authorize(struct bt_gatt_server *server, bt_gatt_server_authorize_cb_t cb, void *user_data) { if (!server) return false; server->authorize = cb; server->authorize_data = user_data; return true; } bluez-5.82/src/shared/PaxHeaders/vcp.c0000644000000000000000000000005014766002272014645 xustar0020 atime=1743515578 20 ctime=1743591277 bluez-5.82/src/shared/vcp.c0000644000000000000000000021013614766002272014331 0ustar00rootroot// SPDX-License-Identifier: LGPL-2.1-or-later /* * * BlueZ - Bluetooth protocol stack for Linux * * Copyright (C) 2022 Intel Corporation. All rights reserved. * */ #define _GNU_SOURCE #include #include #include #include #include #include #include "lib/bluetooth.h" #include "lib/uuid.h" #include "src/shared/queue.h" #include "src/shared/util.h" #include "src/shared/timeout.h" #include "src/shared/att.h" #include "src/shared/gatt-db.h" #include "src/shared/gatt-server.h" #include "src/shared/gatt-client.h" #include "src/shared/vcp.h" #define DBG(_vcp, fmt, arg...) \ vcp_debug(_vcp, "%s:%s() " fmt, __FILE__, __func__, ## arg) #define VCP_STEP_SIZE 1 #define VCP_CLIENT_OP_TIMEOUT 2000 #define VOCS_VOL_OFFSET_UPPER_LIMIT 255 #define VOCS_VOL_OFFSET_LOWER_LIMIT -255 /* Apllication Error Code */ #define BT_ATT_ERROR_INVALID_CHANGE_COUNTER 0x80 #define BT_ATT_ERROR_OPCODE_NOT_SUPPORTED 0x81 #define BT_ATT_ERROR_VALUE_OUT_OF_RANGE 0x82 #define BT_ATT_AICS_ERROR_VALUE_OUT_OF_RANGE 0x83 #define BT_ATT_AICS_ERROR_MUTE_DISABLED 0x82 #define BT_ATT_AICS_ERROR_GAIN_MODE_CHANGE_NOT_ALLOWED 0x84 #define BT_VCP_NA BIT(0) #define BT_VCP_FRONT_LEFT BIT(1) #define BT_VCP_FRONT_RIGHT BIT(2) #define BT_VCP_FRONT_CENTER BIT(3) #define BT_VCP_LOW_FRQ_EFF_1 BIT(4) #define BT_VCP_BACK_LEFT BIT(5) #define BT_VCP_BACK_RIGHT BIT(6) #define BT_VCP_FRONT_LEFT_CENTER BIT(7) #define BT_VCP_FRONT_RIGHT_CENTER BIT(8) #define BT_VCP_BACK_CENTER BIT(9) #define BT_VCP_LOW_FRQ_EFF_2 BIT(10) #define BT_VCP_SIDE_LEFT BIT(11) #define BT_VCP_SIDE_RIGHT BIT(12) #define BT_VCP_TOP_FRONT_LEFT BIT(13) #define BT_VCP_TOP_FRONT_RIGHT BIT(14) #define BT_VCP_TOP_FRONT_CENTER BIT(15) #define BT_VCP_TOP_CENTER BIT(16) #define BT_VCP_TOP_BACK_LEFT BIT(17) #define BT_VCP_TOP_BACK_RIGHT BIT(18) #define BT_VCP_TOP_SIDE_LEFT BIT(19) #define BT_VCP_TOP_SIDE_RIGHT BIT(20) #define BT_VCP_TOP_BACK_CENTER BIT(21) #define BT_VCP_BOTTOM_FRONT_CENTER BIT(22) #define BT_VCP_BOTTOM_FRONT_LEFT BIT(23) #define BT_VCP_BOTTOM_FRONT_RIGHT BIT(24) #define BT_VCP_FRONT_LEFT_WIDE BIT(25) #define BT_VCP_FRONT_RIGHT_WIDE BIT(26) #define BT_VCP_LEFT_SURROUND BIT(27) #define BT_VCP_RIGHT_SURROUND BIT(28) #define VCS_TOTAL_NUM_HANDLES 11 #define AICS_TOTAL_NUM_HANDLES 16 /* AICS Audio Input Type Values */ #define AICS_AUD_IP_TYPE_UNSPECIFIED 0x00 #define AICS_AUD_IP_TYPE_BLUETOOTH 0x01 #define AICS_AUD_IP_TYPE_MICROPHONE 0x02 #define AICS_AUD_IP_TYPE_ANALOG 0x03 #define AICS_AUD_IP_TYPE_DIGITAL 0x04 #define AICS_AUD_IP_TYPE_RADIO 0x05 #define AICS_AUD_IP_TYPE_STREAMING 0x06 #define AICS_AUD_IP_TYPE_AMBIENT 0x07 /* AICS Audio Input Status Values */ #define AICS_AUD_IP_STATUS_INACTIVE 0x00 #define AICS_AUD_IP_STATUS_ACTIVE 0x01 /* AICS Audio Input Control Point Opcodes */ #define BT_AICS_SET_GAIN_SETTING 0x01 #define BT_AICS_UNMUTE 0x02 #define BT_AICS_MUTE 0x03 #define BT_AICS_SET_MANUAL_GAIN_MODE 0x04 #define BT_AICS_SET_AUTO_GAIN_MODE 0x05 /* AICS Gain Mode Field Value */ #define AICS_GAIN_MODE_MANUAL_ONLY 0x00 #define AICS_GAIN_MODE_AUTO_ONLY 0x01 #define AICS_GAIN_MODE_MANUAL 0x02 #define AICS_GAIN_MODE_AUTO 0x03 /* AICS Mute Field Values */ #define AICS_NOT_MUTED 0x00 #define AICS_MUTED 0x01 #define AICS_DISABLED 0x02 #define AICS_GAIN_SETTING_UNITS 1 #define AICS_GAIN_SETTING_MAX_VALUE 127 #define AICS_GAIN_SETTING_MIN_VALUE -128 #define AICS_GAIN_SETTING_DEFAULT_VALUE 88 struct bt_vcp_db { struct gatt_db *db; struct bt_vcs *vcs; struct bt_vocs *vocs; struct bt_aics *aics; }; typedef void (*vcp_func_t)(struct bt_vcp *vcp, bool success, uint8_t att_ecode, const uint8_t *value, uint16_t length, void *user_data); struct bt_vcp_pending { unsigned int id; struct bt_vcp *vcp; vcp_func_t func; void *user_data; }; struct bt_vcs_param { uint8_t op; uint8_t change_counter; } __packed; struct bt_vocs_param { uint8_t op; uint8_t change_counter; } __packed; struct bt_vcs_ab_vol { uint8_t change_counter; uint8_t vol_set; } __packed; struct bt_vcs_client_ab_vol { uint8_t op; uint8_t change_counter; uint8_t vol_set; } __packed; struct bt_vocs_set_vol_off { uint8_t change_counter; int16_t set_vol_offset; } __packed; struct bt_vcp_cb { unsigned int id; bt_vcp_func_t attached; bt_vcp_func_t detached; void *user_data; }; typedef void (*vcp_notify_t)(struct bt_vcp *vcp, uint16_t value_handle, const uint8_t *value, uint16_t length, void *user_data); struct bt_vcp_notify { unsigned int id; struct bt_vcp *vcp; vcp_notify_t func; void *user_data; }; struct bt_vcp_client_op { uint8_t volume; bool resend; bool wait_reply; bool wait_notify; unsigned int timeout_id; }; struct bt_vcp { int ref_count; struct bt_vcp_db *ldb; struct bt_vcp_db *rdb; struct bt_gatt_client *client; struct bt_att *att; unsigned int vstate_id; unsigned int vflag_id; unsigned int state_id; unsigned int audio_loc_id; unsigned int ao_dec_id; unsigned int aics_ip_state_id; unsigned int aics_ip_status_id; unsigned int aics_ip_descr_id; struct queue *notify; struct queue *pending; bt_vcp_debug_func_t debug_func; bt_vcp_destroy_func_t debug_destroy; bt_vcp_volume_func_t volume_changed; uint8_t volume; uint8_t volume_counter; struct bt_vcp_client_op pending_op; void *debug_data; void *user_data; }; #define RESET_VOLUME_SETTING 0x00 #define USERSET_VOLUME_SETTING 0x01 /* Contains local bt_vcp_db */ struct vol_state { uint8_t vol_set; uint8_t mute; uint8_t counter; } __packed; struct bt_vcs { struct bt_vcp_db *vdb; struct vol_state *vstate; uint8_t vol_flag; struct gatt_db_attribute *service; struct gatt_db_attribute *vs; struct gatt_db_attribute *vs_ccc; struct gatt_db_attribute *vol_cp; struct gatt_db_attribute *vf; struct gatt_db_attribute *vf_ccc; }; /* Contains local bt_vcp_db */ struct vol_offset_state { int16_t vol_offset; uint8_t counter; } __packed; struct bt_vocs { struct bt_vcp_db *vdb; struct vol_offset_state *vostate; uint32_t vocs_audio_loc; char *vocs_ao_dec; struct gatt_db_attribute *service; struct gatt_db_attribute *vos; struct gatt_db_attribute *vos_ccc; struct gatt_db_attribute *voal; struct gatt_db_attribute *voal_ccc; struct gatt_db_attribute *vo_cp; struct gatt_db_attribute *voaodec; struct gatt_db_attribute *voaodec_ccc; }; struct aud_ip_st { int8_t gain_setting; uint8_t mute; uint8_t gain_mode; uint8_t chg_counter; } __packed; struct gain_setting_prop { uint8_t gain_setting_units; int8_t gain_setting_min; int8_t gain_setting_max; } __packed; struct bt_aics_set_gain_setting { uint8_t change_counter; int8_t gain_setting; } __packed; struct bt_aics { struct bt_vcp_db *vdb; struct aud_ip_st *aud_ipst; struct gain_setting_prop *gain_settingprop; uint8_t aud_input_type; uint8_t aud_input_status; char *aud_input_descr; struct gatt_db_attribute *service; struct gatt_db_attribute *aud_ip_state; struct gatt_db_attribute *aud_ip_state_ccc; struct gatt_db_attribute *gain_stting_prop; struct gatt_db_attribute *aud_ip_type; struct gatt_db_attribute *aud_ip_status; struct gatt_db_attribute *aud_ip_status_ccc; struct gatt_db_attribute *aud_ip_cp; struct gatt_db_attribute *aud_ip_dscrptn; struct gatt_db_attribute *aud_ip_dscrptn_ccc; }; static struct queue *vcp_db; static struct queue *vcp_cbs; static struct queue *sessions; static void *iov_pull_mem(struct iovec *iov, size_t len) { void *data = iov->iov_base; if (iov->iov_len < len) return NULL; iov->iov_base += len; iov->iov_len -= len; return data; } static struct bt_vcp_db *vcp_get_vdb(struct bt_vcp *vcp) { if (!vcp) return NULL; if (vcp->ldb) return vcp->ldb; return NULL; } static struct vol_state *vdb_get_vstate(struct bt_vcp_db *vdb) { if (!vdb->vcs) return NULL; if (vdb->vcs->vstate) return vdb->vcs->vstate; return NULL; } static struct vol_offset_state *vdb_get_vostate(struct bt_vcp_db *vdb) { if (!vdb->vocs) return NULL; if (vdb->vocs->vostate) return vdb->vocs->vostate; return NULL; } static struct bt_vcs *vcp_get_vcs(struct bt_vcp *vcp) { if (!vcp) return NULL; if (vcp->rdb->vcs) return vcp->rdb->vcs; vcp->rdb->vcs = new0(struct bt_vcs, 1); vcp->rdb->vcs->vdb = vcp->rdb; return vcp->rdb->vcs; } static struct bt_vocs *vcp_get_vocs(struct bt_vcp *vcp) { if (!vcp) return NULL; if (vcp->rdb->vocs) return vcp->rdb->vocs; vcp->rdb->vocs = new0(struct bt_vocs, 1); vcp->rdb->vocs->vdb = vcp->rdb; return vcp->rdb->vocs; } static struct bt_aics *vcp_get_aics(struct bt_vcp *vcp) { if (!vcp) return NULL; if (vcp->rdb->aics) return vcp->rdb->aics; vcp->rdb->aics = new0(struct bt_aics, 1); vcp->rdb->aics->vdb = vcp->rdb; return vcp->rdb->aics; } static void vcp_remote_client_attached(void *data, void *user_data) { struct bt_vcp_cb *cb = data; struct bt_vcp *vcp = user_data; cb->attached(vcp, cb->user_data); } static void vcp_remote_client_detached(void *data, void *user_data) { struct bt_vcp_cb *cb = data; struct bt_vcp *vcp = user_data; cb->detached(vcp, cb->user_data); } static void vcp_client_op_clear(struct bt_vcp_client_op *op) { if (op->timeout_id) timeout_remove(op->timeout_id); memset(op, 0, sizeof(*op)); } void bt_vcp_detach(struct bt_vcp *vcp) { if (!queue_remove(sessions, vcp)) return; if (vcp->client) { bt_gatt_client_unref(vcp->client); vcp->client = NULL; } vcp_client_op_clear(&vcp->pending_op); } static void vcp_db_free(void *data) { struct bt_vcp_db *vdb = data; if (!vdb) return; gatt_db_unref(vdb->db); free(vdb->vcs); free(vdb->vocs); free(vdb->aics); free(vdb); } static void vcp_free(void *data) { struct bt_vcp *vcp = data; bt_vcp_detach(vcp); vcp_db_free(vcp->rdb); queue_destroy(vcp->pending, NULL); free(vcp); } bool bt_vcp_set_user_data(struct bt_vcp *vcp, void *user_data) { if (!vcp) return false; vcp->user_data = user_data; return true; } static bool vcp_db_match(const void *data, const void *match_data) { const struct bt_vcp_db *vdb = data; const struct gatt_db *db = match_data; return (vdb->db == db); } struct bt_att *bt_vcp_get_att(struct bt_vcp *vcp) { if (!vcp) return NULL; if (vcp->att) return vcp->att; return bt_gatt_client_get_att(vcp->client); } struct bt_vcp *bt_vcp_ref(struct bt_vcp *vcp) { if (!vcp) return NULL; __sync_fetch_and_add(&vcp->ref_count, 1); return vcp; } void bt_vcp_unref(struct bt_vcp *vcp) { if (!vcp) return; if (__sync_sub_and_fetch(&vcp->ref_count, 1)) return; vcp_free(vcp); } static void vcp_debug(struct bt_vcp *vcp, const char *format, ...) { va_list ap; if (!vcp || !format || !vcp->debug_func) return; va_start(ap, format); util_debug_va(vcp->debug_func, vcp->debug_data, format, ap); va_end(ap); } static void vcp_disconnected(int err, void *user_data) { /* called only when this device is acting a a server */ struct bt_vcp *vcp = user_data; DBG(vcp, "vcp %p disconnected err %d", vcp, err); bt_vcp_detach(vcp); queue_foreach(vcp_cbs, vcp_remote_client_detached, vcp); } static struct bt_vcp *vcp_get_session(struct bt_att *att, struct gatt_db *db) { const struct queue_entry *entry; struct bt_vcp *vcp; for (entry = queue_get_entries(sessions); entry; entry = entry->next) { struct bt_vcp *vcp = entry->data; if (att == bt_vcp_get_att(vcp)) return vcp; } /* called only when this device is acting a a server */ vcp = bt_vcp_new(db, NULL); vcp->att = att; queue_foreach(vcp_cbs, vcp_remote_client_attached, vcp); bt_att_register_disconnect(att, vcp_disconnected, vcp, NULL); if (!sessions) sessions = queue_new(); queue_push_tail(sessions, vcp); return vcp; } static uint8_t vcs_rel_vol_down(struct bt_vcs *vcs, struct bt_vcp *vcp, struct iovec *iov) { struct bt_vcp_db *vdb; struct vol_state *vstate; uint8_t *change_counter; DBG(vcp, "Volume Down"); vdb = vcp_get_vdb(vcp); if (!vdb) { DBG(vcp, "error: VDB not available"); return 0; } vstate = vdb_get_vstate(vdb); if (!vstate) { DBG(vcp, "error: VSTATE not available"); return 0; } change_counter = iov_pull_mem(iov, sizeof(*change_counter)); if (!change_counter) return 0; if (*change_counter != vstate->counter) { DBG(vcp, "Change Counter Mismatch Volume not decremented!"); return BT_ATT_ERROR_INVALID_CHANGE_COUNTER; } vstate->vol_set = MAX((vstate->vol_set - VCP_STEP_SIZE), 0); vstate->counter = -~vstate->counter; /*Increment Change Counter*/ vcp->volume = vstate->vol_set; if (vcp->volume_changed) vcp->volume_changed(vcp, vcp->volume); gatt_db_attribute_notify(vdb->vcs->vs, (void *)vstate, sizeof(struct vol_state), bt_vcp_get_att(vcp)); return 0; } static uint8_t vcs_rel_vol_up(struct bt_vcs *vcs, struct bt_vcp *vcp, struct iovec *iov) { struct bt_vcp_db *vdb; struct vol_state *vstate; uint8_t *change_counter; DBG(vcp, "Volume Up"); vdb = vcp_get_vdb(vcp); if (!vdb) { DBG(vcp, "error: VDB not available"); return 0; } vstate = vdb_get_vstate(vdb); if (!vstate) { DBG(vcp, "error: VCP database not available"); return 0; } change_counter = iov_pull_mem(iov, sizeof(*change_counter)); if (!change_counter) return 0; if (*change_counter != vstate->counter) { DBG(vcp, "Change Counter Mismatch Volume not decremented!"); return BT_ATT_ERROR_INVALID_CHANGE_COUNTER; } vstate->vol_set = MIN((vstate->vol_set + VCP_STEP_SIZE), 255); vstate->counter = -~vstate->counter; /*Increment Change Counter*/ vcp->volume = vstate->vol_set; if (vcp->volume_changed) vcp->volume_changed(vcp, vcp->volume); gatt_db_attribute_notify(vdb->vcs->vs, (void *)vstate, sizeof(struct vol_state), bt_vcp_get_att(vcp)); return 0; } static uint8_t vcs_unmute_rel_vol_down(struct bt_vcs *vcs, struct bt_vcp *vcp, struct iovec *iov) { struct bt_vcp_db *vdb; struct vol_state *vstate; uint8_t *change_counter; DBG(vcp, "Un Mute and Volume Down"); vdb = vcp_get_vdb(vcp); if (!vdb) { DBG(vcp, "error: VDB not available"); return 0; } vstate = vdb_get_vstate(vdb); if (!vstate) { DBG(vcp, "error: VCP database not available"); return 0; } change_counter = iov_pull_mem(iov, sizeof(*change_counter)); if (!change_counter) return 0; if (*change_counter != vstate->counter) { DBG(vcp, "Change Counter Mismatch Volume not decremented!"); return BT_ATT_ERROR_INVALID_CHANGE_COUNTER; } vstate->mute = 0x00; vstate->vol_set = MAX((vstate->vol_set - VCP_STEP_SIZE), 0); vstate->counter = -~vstate->counter; /*Increment Change Counter*/ vcp->volume = vstate->vol_set; if (vcp->volume_changed) vcp->volume_changed(vcp, vcp->volume); gatt_db_attribute_notify(vdb->vcs->vs, (void *)vstate, sizeof(struct vol_state), bt_vcp_get_att(vcp)); return 0; } static uint8_t vcs_unmute_rel_vol_up(struct bt_vcs *vcs, struct bt_vcp *vcp, struct iovec *iov) { struct bt_vcp_db *vdb; struct vol_state *vstate; uint8_t *change_counter; DBG(vcp, "UN Mute and Volume Up"); vdb = vcp_get_vdb(vcp); if (!vdb) { DBG(vcp, "error: VDB not available"); return 0; } vstate = vdb_get_vstate(vdb); if (!vstate) { DBG(vcp, "error: VSTATE not available"); return 0; } change_counter = iov_pull_mem(iov, sizeof(*change_counter)); if (!change_counter) return 0; if (*change_counter != vstate->counter) { DBG(vcp, "Change Counter Mismatch Volume not decremented!"); return BT_ATT_ERROR_INVALID_CHANGE_COUNTER; } vstate->mute = 0x00; vstate->vol_set = MIN((vstate->vol_set + VCP_STEP_SIZE), 255); vstate->counter = -~vstate->counter; /*Increment Change Counter*/ vcp->volume = vstate->vol_set; if (vcp->volume_changed) vcp->volume_changed(vcp, vcp->volume); gatt_db_attribute_notify(vdb->vcs->vs, (void *)vstate, sizeof(struct vol_state), bt_vcp_get_att(vcp)); return 0; } static uint8_t vcs_set_absolute_vol(struct bt_vcs *vcs, struct bt_vcp *vcp, struct iovec *iov) { struct bt_vcp_db *vdb; struct vol_state *vstate; struct bt_vcs_ab_vol *req; DBG(vcp, "Set Absolute Volume"); vdb = vcp_get_vdb(vcp); if (!vdb) { DBG(vcp, "error: VDB not available"); return 0; } vstate = vdb_get_vstate(vdb); if (!vstate) { DBG(vcp, "error: VSTATE not available"); return 0; } req = iov_pull_mem(iov, sizeof(*req)); if (!req) return 0; if (req->change_counter != vstate->counter) { DBG(vcp, "Change Counter Mismatch Volume not decremented!"); return BT_ATT_ERROR_INVALID_CHANGE_COUNTER; } vstate->vol_set = req->vol_set; vstate->counter = -~vstate->counter; /*Increment Change Counter*/ vcp->volume = vstate->vol_set; if (vcp->volume_changed) vcp->volume_changed(vcp, vcp->volume); gatt_db_attribute_notify(vdb->vcs->vs, (void *)vstate, sizeof(struct vol_state), bt_vcp_get_att(vcp)); return 0; } static uint8_t vcs_unmute(struct bt_vcs *vcs, struct bt_vcp *vcp, struct iovec *iov) { struct bt_vcp_db *vdb; struct vol_state *vstate; uint8_t *change_counter; DBG(vcp, "Un Mute"); vdb = vcp_get_vdb(vcp); if (!vdb) { DBG(vcp, "error: VDB not available"); return 0; } vstate = vdb_get_vstate(vdb); if (!vstate) { DBG(vcp, "error: VSTATE not available"); return 0; } change_counter = iov_pull_mem(iov, sizeof(*change_counter)); if (!change_counter) return 0; if (*change_counter != vstate->counter) { DBG(vcp, "Change Counter Mismatch Volume not decremented!"); return BT_ATT_ERROR_INVALID_CHANGE_COUNTER; } vstate->mute = 0x00; vstate->counter = -~vstate->counter; /*Increment Change Counter*/ gatt_db_attribute_notify(vdb->vcs->vs, (void *)vstate, sizeof(struct vol_state), bt_vcp_get_att(vcp)); return 0; } static uint8_t vcs_mute(struct bt_vcs *vcs, struct bt_vcp *vcp, struct iovec *iov) { struct bt_vcp_db *vdb; struct vol_state *vstate; uint8_t *change_counter; DBG(vcp, "MUTE"); vdb = vcp_get_vdb(vcp); if (!vdb) { DBG(vcp, "error: VDB not available"); return 0; } vstate = vdb_get_vstate(vdb); if (!vstate) { DBG(vcp, "error: VSTATE not available"); return 0; } change_counter = iov_pull_mem(iov, sizeof(*change_counter)); if (!change_counter) return 0; if (*change_counter != vstate->counter) { DBG(vcp, "Change Counter Mismatch Volume not decremented!"); return BT_ATT_ERROR_INVALID_CHANGE_COUNTER; } vstate->mute = 0x01; vstate->counter = -~vstate->counter; /*Increment Change Counter*/ return 0; } static uint8_t vocs_set_vol_offset(struct bt_vocs *vocs, struct bt_vcp *vcp, struct iovec *iov) { struct bt_vcp_db *vdb; struct vol_offset_state *vstate, state; struct bt_vocs_set_vol_off *req; DBG(vcp, "Set Volume Offset"); vdb = vcp_get_vdb(vcp); if (!vdb) { DBG(vcp, "error: VDB not available"); return 0; } vstate = vdb_get_vostate(vdb); if (!vstate) { DBG(vcp, "error: VSTATE not available"); return 0; } req = iov_pull_mem(iov, sizeof(*req)); if (!req) return 0; if (req->change_counter != vstate->counter) { DBG(vcp, "Change Counter Mismatch Volume not decremented!"); return BT_ATT_ERROR_INVALID_CHANGE_COUNTER; } vstate->vol_offset = le16_to_cpu(req->set_vol_offset); if (vstate->vol_offset > VOCS_VOL_OFFSET_UPPER_LIMIT || vstate->vol_offset < VOCS_VOL_OFFSET_LOWER_LIMIT) { DBG(vcp, "error: Value Out of Range"); return BT_ATT_ERROR_VALUE_OUT_OF_RANGE; } /* Increment Change Counter */ vstate->counter = -~vstate->counter; /* Notify change */ state.vol_offset = req->set_vol_offset; state.counter = vstate->counter; gatt_db_attribute_notify(vdb->vocs->vos, (void *)&state, sizeof(state), bt_vcp_get_att(vcp)); return 0; } #define BT_VCS_REL_VOL_DOWN 0x00 #define BT_VCS_REL_VOL_UP 0x01 #define BT_VCS_UNMUTE_REL_VOL_DOWN 0x02 #define BT_VCS_UNMUTE_REL_VOL_UP 0x03 #define BT_VCS_SET_ABSOLUTE_VOL 0x04 #define BT_VCS_UNMUTE 0x05 #define BT_VCS_MUTE 0x06 #define BT_VOCS_SET_VOL_OFFSET 0x01 #define VCS_OP(_str, _op, _size, _func) \ { \ .str = _str, \ .op = _op, \ .size = _size, \ .func = _func, \ } struct vcs_op_handler { const char *str; uint8_t op; size_t size; uint8_t (*func)(struct bt_vcs *vcs, struct bt_vcp *vcp, struct iovec *iov); } vcp_handlers[] = { VCS_OP("Relative Volume Down", BT_VCS_REL_VOL_DOWN, sizeof(uint8_t), vcs_rel_vol_down), VCS_OP("Relative Volume Up", BT_VCS_REL_VOL_UP, sizeof(uint8_t), vcs_rel_vol_up), VCS_OP("Unmute - Relative Volume Down", BT_VCS_UNMUTE_REL_VOL_DOWN, sizeof(uint8_t), vcs_unmute_rel_vol_down), VCS_OP("Unmute - Relative Volume Up", BT_VCS_UNMUTE_REL_VOL_UP, sizeof(uint8_t), vcs_unmute_rel_vol_up), VCS_OP("Set Absolute Volume", BT_VCS_SET_ABSOLUTE_VOL, sizeof(struct bt_vcs_ab_vol), vcs_set_absolute_vol), VCS_OP("UnMute", BT_VCS_UNMUTE, sizeof(uint8_t), vcs_unmute), VCS_OP("Mute", BT_VCS_MUTE, sizeof(uint8_t), vcs_mute), {} }; #define VOCS_OP(_str, _op, _size, _func) \ { \ .str = _str, \ .op = _op, \ .size = _size, \ .func = _func, \ } struct vocs_op_handler { const char *str; uint8_t op; size_t size; uint8_t (*func)(struct bt_vocs *vocs, struct bt_vcp *vcp, struct iovec *iov); } vocp_handlers[] = { VOCS_OP("Set Volume Offset", BT_VOCS_SET_VOL_OFFSET, sizeof(uint8_t), vocs_set_vol_offset), {} }; static void vcs_cp_write(struct gatt_db_attribute *attrib, unsigned int id, uint16_t offset, const uint8_t *value, size_t len, uint8_t opcode, struct bt_att *att, void *user_data) { struct bt_vcs *vcs = user_data; struct bt_vcp *vcp = vcp_get_session(att, vcs->vdb->db); struct iovec iov = { .iov_base = (void *) value, .iov_len = len, }; uint8_t *vcp_op; struct vcs_op_handler *handler; uint8_t ret = BT_ATT_ERROR_REQUEST_NOT_SUPPORTED; DBG(vcp, "VCP Control Point Write"); if (offset) { DBG(vcp, "invalid offset %d", offset); ret = BT_ATT_ERROR_INVALID_OFFSET; goto respond; } if (len < sizeof(*vcp_op)) { DBG(vcp, "invalid len %ld < %ld sizeof(*param)", len, sizeof(*vcp_op)); ret = BT_ATT_ERROR_INVALID_ATTRIBUTE_VALUE_LEN; goto respond; } vcp_op = iov_pull_mem(&iov, sizeof(*vcp_op)); if (!vcp_op) { DBG(vcp, "iov_pull_mem() returned NULL"); goto respond; } for (handler = vcp_handlers; handler && handler->str; handler++) { if (handler->op != *vcp_op) continue; if (iov.iov_len < handler->size) { DBG(vcp, "invalid len %ld < %ld handler->size", len, handler->size); ret = BT_ATT_ERROR_OPCODE_NOT_SUPPORTED; goto respond; } break; } if (handler && handler->str) { DBG(vcp, "%s", handler->str); ret = handler->func(vcs, vcp, &iov); } else { DBG(vcp, "Unknown opcode 0x%02x", *vcp_op); ret = BT_ATT_ERROR_OPCODE_NOT_SUPPORTED; } respond: gatt_db_attribute_write_result(attrib, id, ret); } static void vocs_cp_write(struct gatt_db_attribute *attrib, unsigned int id, uint16_t offset, const uint8_t *value, size_t len, uint8_t opcode, struct bt_att *att, void *user_data) { struct bt_vocs *vocs = user_data; struct bt_vcp *vcp = vcp_get_session(att, vocs->vdb->db); struct iovec iov = { .iov_base = (void *) value, .iov_len = len, }; uint8_t *vcp_op; struct vocs_op_handler *handler; uint8_t ret = BT_ATT_ERROR_REQUEST_NOT_SUPPORTED; DBG(vcp, "VOCP Control Point Write"); if (offset) { DBG(vcp, "invalid offset %d", offset); ret = BT_ATT_ERROR_INVALID_OFFSET; goto respond; } if (len < sizeof(*vcp_op)) { DBG(vcp, "invalid len %ld < %ld sizeof(*param)", len, sizeof(*vcp_op)); ret = BT_ATT_ERROR_INVALID_ATTRIBUTE_VALUE_LEN; goto respond; } vcp_op = iov_pull_mem(&iov, sizeof(*vcp_op)); if (!vcp_op) { DBG(vcp, "iov_pull_mem() returned NULL"); goto respond; } for (handler = vocp_handlers; handler && handler->str; handler++) { if (handler->op != *vcp_op) continue; if (iov.iov_len < handler->size) { DBG(vcp, "invalid len %ld < %ld handler->size", len, handler->size); ret = BT_ATT_ERROR_OPCODE_NOT_SUPPORTED; goto respond; } break; } if (handler && handler->str) { DBG(vcp, "%s", handler->str); ret = handler->func(vocs, vcp, &iov); } else { DBG(vcp, "Unknown opcode 0x%02x", *vcp_op); ret = BT_ATT_ERROR_OPCODE_NOT_SUPPORTED; } respond: gatt_db_attribute_write_result(attrib, id, ret); } static void vcs_state_read(struct gatt_db_attribute *attrib, unsigned int id, uint16_t offset, uint8_t opcode, struct bt_att *att, void *user_data) { struct bt_vcs *vcs = user_data; struct iovec iov; iov.iov_base = vcs->vstate; iov.iov_len = sizeof(*vcs->vstate); gatt_db_attribute_read_result(attrib, id, 0, iov.iov_base, iov.iov_len); } static void vocs_state_read(struct gatt_db_attribute *attrib, unsigned int id, uint16_t offset, uint8_t opcode, struct bt_att *att, void *user_data) { struct bt_vocs *vocs = user_data; struct vol_offset_state state; state.vol_offset = cpu_to_le16(vocs->vostate->vol_offset); state.counter = vocs->vostate->counter; gatt_db_attribute_read_result(attrib, id, 0, (void *)&state, sizeof(state)); } static void vcs_flag_read(struct gatt_db_attribute *attrib, unsigned int id, uint16_t offset, uint8_t opcode, struct bt_att *att, void *user_data) { struct bt_vcs *vcs = user_data; struct iovec iov; iov.iov_base = &vcs->vol_flag; iov.iov_len = sizeof(vcs->vol_flag); gatt_db_attribute_read_result(attrib, id, 0, iov.iov_base, iov.iov_len); } static void vocs_voal_read(struct gatt_db_attribute *attrib, unsigned int id, uint16_t offset, uint8_t opcode, struct bt_att *att, void *user_data) { struct bt_vocs *vocs = user_data; uint32_t loc; loc = cpu_to_le32(vocs->vocs_audio_loc); gatt_db_attribute_read_result(attrib, id, 0, (void *)&loc, sizeof(loc)); } static void vocs_voaodec_read(struct gatt_db_attribute *attrib, unsigned int id, uint16_t offset, uint8_t opcode, struct bt_att *att, void *user_data) { struct bt_vocs *vocs = user_data; struct iovec iov; iov.iov_base = vocs->vocs_ao_dec; iov.iov_len = strlen(vocs->vocs_ao_dec); gatt_db_attribute_read_result(attrib, id, 0, iov.iov_base, iov.iov_len); } static void aics_input_state_read(struct gatt_db_attribute *attrib, unsigned int id, uint16_t offset, uint8_t opcode, struct bt_att *att, void *user_data) { struct bt_aics *aics = user_data; struct iovec iov; iov.iov_base = aics->aud_ipst; iov.iov_len = sizeof(*aics->aud_ipst); gatt_db_attribute_read_result(attrib, id, 0, iov.iov_base, iov.iov_len); } static void aics_gain_setting_prop_read(struct gatt_db_attribute *attrib, unsigned int id, uint16_t offset, uint8_t opcode, struct bt_att *att, void *user_data) { struct bt_aics *aics = user_data; struct iovec iov; iov.iov_base = aics->gain_settingprop; iov.iov_len = sizeof(*aics->gain_settingprop); gatt_db_attribute_read_result(attrib, id, 0, iov.iov_base, iov.iov_len); } static void aics_audio_input_type_read(struct gatt_db_attribute *attrib, unsigned int id, uint16_t offset, uint8_t opcode, struct bt_att *att, void *user_data) { struct bt_aics *aics = user_data; struct iovec iov; iov.iov_base = &aics->aud_input_type; iov.iov_len = sizeof(aics->aud_input_type); gatt_db_attribute_read_result(attrib, id, 0, iov.iov_base, iov.iov_len); } static void aics_input_status_read(struct gatt_db_attribute *attrib, unsigned int id, uint16_t offset, uint8_t opcode, struct bt_att *att, void *user_data) { struct bt_aics *aics = user_data; struct iovec iov; iov.iov_base = &aics->aud_input_status; iov.iov_len = sizeof(aics->aud_input_status); gatt_db_attribute_read_result(attrib, id, 0, iov.iov_base, iov.iov_len); } static struct aud_ip_st *vdb_get_audipst(struct bt_vcp_db *vdb) { if (!vdb->aics) return NULL; if (vdb->aics->aud_ipst) return vdb->aics->aud_ipst; return NULL; } static struct gain_setting_prop *vdb_get_gainsettingprop( struct bt_vcp_db *vdb) { if (!vdb->aics) return NULL; if (vdb->aics->gain_settingprop) return vdb->aics->gain_settingprop; return NULL; } static uint8_t aics_set_gain_setting(struct bt_aics *aics, struct bt_vcp *vcp, struct iovec *iov) { struct bt_vcp_db *vdb; struct aud_ip_st *audipst; struct bt_aics_set_gain_setting *req; struct gain_setting_prop *gainsettngprop; uint8_t ret = 1; vdb = vcp_get_vdb(vcp); if (!vdb) { DBG(vcp, "error: VDB not available"); ret = 0; goto respond; } audipst = vdb_get_audipst(vdb); if (!audipst) { DBG(vcp, "error: Audio Input State value is not available"); ret = 0; goto respond; } req = iov_pull_mem(iov, sizeof(*req)); if (!req) { ret = 0; goto respond; } if (req->change_counter != audipst->chg_counter) { DBG(vcp, "Change Counter Mismatch Audio Input State!"); ret = BT_ATT_ERROR_INVALID_CHANGE_COUNTER; goto respond; } if (audipst->gain_mode != AICS_GAIN_MODE_MANUAL_ONLY && audipst->gain_mode != AICS_GAIN_MODE_MANUAL) { DBG(vcp, "Gain Mode is not Manual only or Manual"); ret = BT_ATT_AICS_ERROR_GAIN_MODE_CHANGE_NOT_ALLOWED; goto respond; } gainsettngprop = vdb_get_gainsettingprop(vdb); if (req->gain_setting > gainsettngprop->gain_setting_max || req->gain_setting < gainsettngprop->gain_setting_min) { DBG(vcp, "error: Value Out of Range"); ret = BT_ATT_AICS_ERROR_VALUE_OUT_OF_RANGE; goto respond; } audipst->gain_setting = req->gain_setting; /*Increment Change Counter*/ audipst->chg_counter = -~audipst->chg_counter; gatt_db_attribute_notify(vdb->aics->aud_ip_state, (void *)audipst, sizeof(struct aud_ip_st), bt_vcp_get_att(vcp)); ret = 0; respond: return ret; } static uint8_t aics_unmute(struct bt_aics *aics, struct bt_vcp *vcp, struct iovec *iov) { struct bt_vcp_db *vdb; struct aud_ip_st *audipst; uint8_t *change_counter; uint8_t ret = 1; vdb = vcp_get_vdb(vcp); if (!vdb) { DBG(vcp, "error: VDB not available"); ret = 0; goto respond; } audipst = vdb_get_audipst(vdb); if (!audipst) { DBG(vcp, "error: Audio Input State value is not available"); ret = 0; goto respond; } change_counter = iov_pull_mem(iov, sizeof(*change_counter)); if (!change_counter) { ret = 0; goto respond; } if (*change_counter != audipst->chg_counter) { DBG(vcp, "Change Counter Mismatch Audio Input State!"); ret = BT_ATT_ERROR_INVALID_CHANGE_COUNTER; goto respond; } if (audipst->mute == AICS_DISABLED) { DBG(vcp, "Mute state is Disabled!"); ret = BT_ATT_AICS_ERROR_MUTE_DISABLED; goto respond; } audipst->mute = AICS_NOT_MUTED; /*Increment Change Counter*/ audipst->chg_counter = -~audipst->chg_counter; gatt_db_attribute_notify(vdb->aics->aud_ip_state, (void *)audipst, sizeof(struct aud_ip_st), bt_vcp_get_att(vcp)); ret = 0; respond: return ret; } static uint8_t aics_mute(struct bt_aics *aics, struct bt_vcp *vcp, struct iovec *iov) { struct bt_vcp_db *vdb; struct aud_ip_st *audipst; uint8_t *change_counter; uint8_t ret = 1; vdb = vcp_get_vdb(vcp); if (!vdb) { DBG(vcp, "error: VDB not available"); ret = 0; goto respond; } audipst = vdb_get_audipst(vdb); if (!audipst) { DBG(vcp, "error: Audio Input State value is not available"); ret = 0; goto respond; } change_counter = iov_pull_mem(iov, sizeof(*change_counter)); if (!change_counter) { ret = 0; goto respond; } if (*change_counter != audipst->chg_counter) { DBG(vcp, "Change Counter Mismatch Audio Input State!"); ret = BT_ATT_ERROR_INVALID_CHANGE_COUNTER; goto respond; } if (audipst->mute == AICS_DISABLED) { DBG(vcp, "Mute state is Disabled!"); ret = BT_ATT_AICS_ERROR_MUTE_DISABLED; goto respond; } audipst->mute = AICS_MUTED; /*Increment Change Counter*/ audipst->chg_counter = -~audipst->chg_counter; gatt_db_attribute_notify(vdb->aics->aud_ip_state, (void *)audipst, sizeof(struct aud_ip_st), bt_vcp_get_att(vcp)); ret = 0; respond: return ret; } static uint8_t aics_set_manual_gain_mode(struct bt_aics *aics, struct bt_vcp *vcp, struct iovec *iov) { struct bt_vcp_db *vdb; struct aud_ip_st *audipst; uint8_t *change_counter; uint8_t ret = 1; vdb = vcp_get_vdb(vcp); if (!vdb) { DBG(vcp, "error: VDB not available"); ret = 0; goto respond; } audipst = vdb_get_audipst(vdb); if (!audipst) { DBG(vcp, "error: Audio Input State value is not available"); ret = 0; goto respond; } change_counter = iov_pull_mem(iov, sizeof(*change_counter)); if (!change_counter) { ret = 0; goto respond; } if (*change_counter != audipst->chg_counter) { DBG(vcp, "Change Counter Mismatch Audio Input State!"); ret = BT_ATT_ERROR_INVALID_CHANGE_COUNTER; goto respond; } if (audipst->gain_mode == AICS_GAIN_MODE_AUTO_ONLY || audipst->gain_mode == AICS_GAIN_MODE_MANUAL_ONLY) { DBG(vcp, "error!! gain mode is Automatic only or Manual only"); ret = BT_ATT_AICS_ERROR_GAIN_MODE_CHANGE_NOT_ALLOWED; goto respond; } if (audipst->gain_mode == AICS_GAIN_MODE_AUTO) { audipst->gain_mode = AICS_GAIN_MODE_MANUAL; /*Increment Change Counter*/ audipst->chg_counter = -~audipst->chg_counter; gatt_db_attribute_notify(vdb->aics->aud_ip_state, (void *)audipst, sizeof(struct aud_ip_st), bt_vcp_get_att(vcp)); ret = 0; } else { DBG(vcp, "error!! Gain mode field value not Automatic"); ret = BT_ATT_AICS_ERROR_GAIN_MODE_CHANGE_NOT_ALLOWED; } respond: return ret; } static uint8_t aics_set_auto_gain_mode(struct bt_aics *aics, struct bt_vcp *vcp, struct iovec *iov) { struct bt_vcp_db *vdb; struct aud_ip_st *audipst; uint8_t *change_counter; uint8_t ret = 1; vdb = vcp_get_vdb(vcp); if (!vdb) { DBG(vcp, "error: VDB not available"); ret = 0; goto respond; } audipst = vdb_get_audipst(vdb); if (!audipst) { DBG(vcp, "error: Audio Input State value is not available"); ret = 0; goto respond; } change_counter = iov_pull_mem(iov, sizeof(*change_counter)); if (!change_counter) { ret = 0; goto respond; } if (*change_counter != audipst->chg_counter) { DBG(vcp, "Change Counter Mismatch Audio Input State!"); ret = BT_ATT_ERROR_INVALID_CHANGE_COUNTER; goto respond; } if (audipst->gain_mode == AICS_GAIN_MODE_AUTO_ONLY || audipst->gain_mode == AICS_GAIN_MODE_MANUAL_ONLY) { DBG(vcp, "error!! gain mode is Automatic only or Manual only"); ret = BT_ATT_AICS_ERROR_GAIN_MODE_CHANGE_NOT_ALLOWED; goto respond; } if (audipst->gain_mode == AICS_GAIN_MODE_MANUAL) { audipst->gain_mode = AICS_GAIN_MODE_AUTO; /*Increment Change Counter*/ audipst->chg_counter = -~audipst->chg_counter; gatt_db_attribute_notify(vdb->aics->aud_ip_state, (void *)audipst, sizeof(struct aud_ip_st), bt_vcp_get_att(vcp)); ret = 0; } else { DBG(vcp, "error!! Gain mode field value is not Manual"); ret = BT_ATT_AICS_ERROR_GAIN_MODE_CHANGE_NOT_ALLOWED; } respond: return ret; } #define AICS_OP(_str, _op, _size, _func) \ { \ .str = _str, \ .op = _op, \ .size = _size, \ .func = _func, \ } struct aics_op_handler { const char *str; uint8_t op; size_t size; uint8_t (*func)(struct bt_aics *aics, struct bt_vcp *vcp, struct iovec *iov); } aics_handlers[] = { AICS_OP("Set Gain Setting", BT_AICS_SET_GAIN_SETTING, sizeof(struct bt_aics_set_gain_setting), aics_set_gain_setting), AICS_OP("Unmute", BT_AICS_UNMUTE, sizeof(uint8_t), aics_unmute), AICS_OP("Mute", BT_AICS_MUTE, sizeof(uint8_t), aics_mute), AICS_OP("Set Manual Gain Mode", BT_AICS_SET_MANUAL_GAIN_MODE, sizeof(uint8_t), aics_set_manual_gain_mode), AICS_OP("Set Automatic Gain Mode", BT_AICS_SET_AUTO_GAIN_MODE, sizeof(uint8_t), aics_set_auto_gain_mode), {} }; static void aics_ip_cp_write(struct gatt_db_attribute *attrib, unsigned int id, uint16_t offset, const uint8_t *value, size_t len, uint8_t opcode, struct bt_att *att, void *user_data) { struct bt_aics *aics = user_data; struct bt_vcp *vcp = vcp_get_session(att, aics->vdb->db); struct iovec iov = { .iov_base = (void *) value, .iov_len = len, }; uint8_t *aics_op; struct aics_op_handler *handler; uint8_t ret = BT_ATT_ERROR_REQUEST_NOT_SUPPORTED; DBG(vcp, "AICS Control Point Write"); if (offset) { DBG(vcp, "invalid offset %d", offset); ret = BT_ATT_ERROR_INVALID_OFFSET; goto respond; } if (len < sizeof(*aics_op)) { DBG(vcp, "invalid len %ld < %ld sizeof(*param)", len, sizeof(*aics_op)); ret = BT_ATT_ERROR_INVALID_ATTRIBUTE_VALUE_LEN; goto respond; } aics_op = iov_pull_mem(&iov, sizeof(*aics_op)); if (!aics_op) { DBG(vcp, "iov_pull_mem() returned NULL"); goto respond; } for (handler = aics_handlers; handler && handler->str; handler++) { if (handler->op != *aics_op) continue; if (iov.iov_len < handler->size) { DBG(vcp, "invalid len %ld < %ld handler->size", len, handler->size); ret = BT_ATT_ERROR_OPCODE_NOT_SUPPORTED; goto respond; } break; } if (handler && handler->str) { DBG(vcp, "%s", handler->str); ret = handler->func(aics, vcp, &iov); } else { DBG(vcp, "Unknown opcode 0x%02x", *aics_op); ret = BT_ATT_ERROR_OPCODE_NOT_SUPPORTED; } respond: gatt_db_attribute_write_result(attrib, id, ret); } static void aics_input_descr_read(struct gatt_db_attribute *attrib, unsigned int id, uint16_t offset, uint8_t opcode, struct bt_att *att, void *user_data) { struct bt_aics *aics = user_data; struct iovec iov; iov.iov_base = aics->aud_input_descr; iov.iov_len = strlen(aics->aud_input_descr); gatt_db_attribute_read_result(attrib, id, 0, iov.iov_base, iov.iov_len); } static void aics_input_descr_write(struct gatt_db_attribute *attrib, unsigned int id, uint16_t offset, const uint8_t *value, size_t len, uint8_t opcode, struct bt_att *att, void *user_data) { /* TODO : AICS optional feature */ } static struct bt_vcs *vcs_new(struct gatt_db *db, struct bt_vcp_db *vdb) { struct bt_vcs *vcs; struct vol_state *vstate; bt_uuid_t uuid; if (!db) return NULL; vcs = new0(struct bt_vcs, 1); vstate = new0(struct vol_state, 1); vcs->vstate = vstate; vcs->vol_flag = USERSET_VOLUME_SETTING; /* Populate DB with VCS attributes */ bt_uuid16_create(&uuid, VCS_UUID); vcs->service = gatt_db_add_service(db, &uuid, true, VCS_TOTAL_NUM_HANDLES); gatt_db_service_add_included(vcs->service, vdb->vocs->service); gatt_db_service_set_active(vdb->vocs->service, true); gatt_db_service_add_included(vcs->service, vdb->aics->service); gatt_db_service_set_active(vdb->aics->service, true); bt_uuid16_create(&uuid, VOL_STATE_CHRC_UUID); vcs->vs = gatt_db_service_add_characteristic(vcs->service, &uuid, BT_ATT_PERM_READ, BT_GATT_CHRC_PROP_READ | BT_GATT_CHRC_PROP_NOTIFY, vcs_state_read, NULL, vcs); vcs->vs_ccc = gatt_db_service_add_ccc(vcs->service, BT_ATT_PERM_READ | BT_ATT_PERM_WRITE); bt_uuid16_create(&uuid, VOL_CP_CHRC_UUID); vcs->vol_cp = gatt_db_service_add_characteristic(vcs->service, &uuid, BT_ATT_PERM_WRITE, BT_GATT_CHRC_PROP_WRITE, NULL, vcs_cp_write, vcs); bt_uuid16_create(&uuid, VOL_FLAG_CHRC_UUID); vcs->vf = gatt_db_service_add_characteristic(vcs->service, &uuid, BT_ATT_PERM_READ, BT_GATT_CHRC_PROP_READ | BT_GATT_CHRC_PROP_NOTIFY, vcs_flag_read, NULL, vcs); vcs->vf_ccc = gatt_db_service_add_ccc(vcs->service, BT_ATT_PERM_READ | BT_ATT_PERM_WRITE); gatt_db_service_set_active(vcs->service, true); return vcs; } static struct bt_vocs *vocs_new(struct gatt_db *db) { struct bt_vocs *vocs; struct vol_offset_state *vostate; bt_uuid_t uuid; if (!db) return NULL; vocs = new0(struct bt_vocs, 1); vostate = new0(struct vol_offset_state, 1); vocs->vostate = vostate; vocs->vocs_audio_loc = BT_VCP_FRONT_LEFT; vocs->vocs_ao_dec = "Left Speaker"; /* Populate DB with VOCS attributes */ bt_uuid16_create(&uuid, VOL_OFFSET_CS_UUID); vocs->service = gatt_db_add_service(db, &uuid, false, 12); bt_uuid16_create(&uuid, VOCS_STATE_CHAR_UUID); vocs->vos = gatt_db_service_add_characteristic(vocs->service, &uuid, BT_ATT_PERM_READ, BT_GATT_CHRC_PROP_READ | BT_GATT_CHRC_PROP_NOTIFY, vocs_state_read, NULL, vocs); vocs->vos_ccc = gatt_db_service_add_ccc(vocs->service, BT_ATT_PERM_READ | BT_ATT_PERM_WRITE); bt_uuid16_create(&uuid, VOCS_AUDIO_LOC_CHRC_UUID); vocs->voal = gatt_db_service_add_characteristic(vocs->service, &uuid, BT_ATT_PERM_READ, BT_GATT_CHRC_PROP_READ | BT_GATT_CHRC_PROP_NOTIFY, vocs_voal_read, NULL, vocs); vocs->voal_ccc = gatt_db_service_add_ccc(vocs->service, BT_ATT_PERM_READ | BT_ATT_PERM_WRITE); bt_uuid16_create(&uuid, VOCS_CP_CHRC_UUID); vocs->vo_cp = gatt_db_service_add_characteristic(vocs->service, &uuid, BT_ATT_PERM_WRITE, BT_GATT_CHRC_PROP_WRITE, NULL, vocs_cp_write, vocs); bt_uuid16_create(&uuid, VOCS_AUDIO_OP_DESC_CHAR_UUID); vocs->voaodec = gatt_db_service_add_characteristic(vocs->service, &uuid, BT_ATT_PERM_READ, BT_GATT_CHRC_PROP_READ | BT_GATT_CHRC_PROP_NOTIFY, vocs_voaodec_read, NULL, vocs); vocs->voaodec_ccc = gatt_db_service_add_ccc(vocs->service, BT_ATT_PERM_READ | BT_ATT_PERM_WRITE); return vocs; } static struct bt_aics *aics_new(struct gatt_db *db) { struct bt_aics *aics; struct aud_ip_st *aics_aud_ip_st; struct gain_setting_prop *aics_gain_settng_prop; char *ip_descr; char ip_descr_str[] = "Blueooth"; bt_uuid_t uuid; if (!db) return NULL; aics = new0(struct bt_aics, 1); aics_aud_ip_st = new0(struct aud_ip_st, 1); aics_gain_settng_prop = new0(struct gain_setting_prop, 1); ip_descr = malloc(256); memset(ip_descr, 0, 256); aics_aud_ip_st->mute = AICS_NOT_MUTED; aics_aud_ip_st->gain_mode = AICS_GAIN_MODE_MANUAL; aics_aud_ip_st->gain_setting = AICS_GAIN_SETTING_DEFAULT_VALUE; aics->aud_ipst = aics_aud_ip_st; aics_gain_settng_prop->gain_setting_units = AICS_GAIN_SETTING_UNITS; aics_gain_settng_prop->gain_setting_max = AICS_GAIN_SETTING_MAX_VALUE; aics_gain_settng_prop->gain_setting_min = AICS_GAIN_SETTING_MIN_VALUE; aics->gain_settingprop = aics_gain_settng_prop; aics->aud_input_type = AICS_AUD_IP_TYPE_BLUETOOTH; aics->aud_input_status = AICS_AUD_IP_STATUS_ACTIVE; memcpy(ip_descr, ip_descr_str, strlen(ip_descr_str)); aics->aud_input_descr = ip_descr; /* Populate DB with AICS attributes */ bt_uuid16_create(&uuid, AUDIO_INPUT_CS_UUID); aics->service = gatt_db_add_service(db, &uuid, false, AICS_TOTAL_NUM_HANDLES); bt_uuid16_create(&uuid, AICS_INPUT_STATE_CHAR_UUID); aics->aud_ip_state = gatt_db_service_add_characteristic(aics->service, &uuid, BT_ATT_PERM_READ, BT_GATT_CHRC_PROP_READ | BT_GATT_CHRC_PROP_NOTIFY, aics_input_state_read, NULL, aics); aics->aud_ip_state_ccc = gatt_db_service_add_ccc(aics->service, BT_ATT_PERM_READ | BT_ATT_PERM_WRITE); bt_uuid16_create(&uuid, AICS_GAIN_SETTING_PROP_CHAR_UUID); aics->gain_stting_prop = gatt_db_service_add_characteristic( aics->service, &uuid, BT_ATT_PERM_READ, BT_GATT_CHRC_PROP_READ, aics_gain_setting_prop_read, NULL, aics); bt_uuid16_create(&uuid, AICS_AUDIO_INPUT_TYPE_CHAR_UUID); aics->aud_ip_type = gatt_db_service_add_characteristic(aics->service, &uuid, BT_ATT_PERM_READ, BT_GATT_CHRC_PROP_READ, aics_audio_input_type_read, NULL, aics); bt_uuid16_create(&uuid, AICS_INPUT_STATUS_CHAR_UUID); aics->aud_ip_status = gatt_db_service_add_characteristic(aics->service, &uuid, BT_ATT_PERM_READ, BT_GATT_CHRC_PROP_READ | BT_GATT_CHRC_PROP_NOTIFY, aics_input_status_read, NULL, aics); aics->aud_ip_status_ccc = gatt_db_service_add_ccc(aics->service, BT_ATT_PERM_READ | BT_ATT_PERM_WRITE); bt_uuid16_create(&uuid, AICS_AUDIO_INPUT_CP_CHRC_UUID); aics->aud_ip_cp = gatt_db_service_add_characteristic(aics->service, &uuid, BT_ATT_PERM_WRITE, BT_GATT_CHRC_PROP_WRITE, NULL, aics_ip_cp_write, aics); bt_uuid16_create(&uuid, AICS_INPUT_DESCR_CHAR_UUID); aics->aud_ip_dscrptn = gatt_db_service_add_characteristic(aics->service, &uuid, BT_ATT_PERM_READ | BT_ATT_PERM_WRITE, BT_GATT_CHRC_PROP_READ | BT_GATT_CHRC_PROP_WRITE_WITHOUT_RESP | BT_GATT_CHRC_PROP_NOTIFY, aics_input_descr_read, aics_input_descr_write, aics); aics->aud_ip_dscrptn_ccc = gatt_db_service_add_ccc(aics->service, BT_ATT_PERM_READ | BT_ATT_PERM_WRITE); return aics; } static struct bt_vcp_db *vcp_db_new(struct gatt_db *db) { struct bt_vcp_db *vdb; if (!db) return NULL; vdb = new0(struct bt_vcp_db, 1); vdb->db = gatt_db_ref(db); if (!vcp_db) vcp_db = queue_new(); vdb->vocs = vocs_new(db); vdb->vocs->vdb = vdb; vdb->aics = aics_new(db); vdb->aics->vdb = vdb; vdb->vcs = vcs_new(db, vdb); vdb->vcs->vdb = vdb; queue_push_tail(vcp_db, vdb); return vdb; } static struct bt_vcp_db *vcp_get_db(struct gatt_db *db) { struct bt_vcp_db *vdb; vdb = queue_find(vcp_db, vcp_db_match, db); if (vdb) return vdb; return vcp_db_new(db); } void bt_vcp_add_db(struct gatt_db *db) { vcp_db_new(db); } bool bt_vcp_set_debug(struct bt_vcp *vcp, bt_vcp_debug_func_t func, void *user_data, bt_vcp_destroy_func_t destroy) { if (!vcp) return false; if (vcp->debug_destroy) vcp->debug_destroy(vcp->debug_data); vcp->debug_func = func; vcp->debug_destroy = destroy; vcp->debug_data = user_data; return true; } bool bt_vcp_set_volume_callback(struct bt_vcp *vcp, bt_vcp_volume_func_t volume_changed) { if (!vcp) return false; vcp->volume_changed = volume_changed; return true; } unsigned int bt_vcp_register(bt_vcp_func_t attached, bt_vcp_func_t detached, void *user_data) { struct bt_vcp_cb *cb; static unsigned int id; if (!attached && !detached) return 0; if (!vcp_cbs) vcp_cbs = queue_new(); cb = new0(struct bt_vcp_cb, 1); cb->id = ++id ? id : ++id; cb->attached = attached; cb->detached = detached; cb->user_data = user_data; queue_push_tail(vcp_cbs, cb); return cb->id; } static bool match_id(const void *data, const void *match_data) { const struct bt_vcp_cb *cb = data; unsigned int id = PTR_TO_UINT(match_data); return (cb->id == id); } bool bt_vcp_unregister(unsigned int id) { struct bt_vcp_cb *cb; cb = queue_remove_if(vcp_cbs, match_id, UINT_TO_PTR(id)); if (!cb) return false; free(cb); return true; } struct bt_vcp *bt_vcp_new(struct gatt_db *ldb, struct gatt_db *rdb) { struct bt_vcp *vcp; struct bt_vcp_db *vdb; if (!ldb) return NULL; vdb = vcp_get_db(ldb); if (!vdb) return NULL; vcp = new0(struct bt_vcp, 1); vcp->ldb = vdb; vcp->pending = queue_new(); if (!rdb) goto done; vdb = new0(struct bt_vcp_db, 1); vdb->db = gatt_db_ref(rdb); vcp->rdb = vdb; done: bt_vcp_ref(vcp); return vcp; } static void vcp_set_volume_complete(struct bt_vcp *vcp) { bool resend = vcp->pending_op.resend; uint8_t volume = vcp->pending_op.volume; vcp_client_op_clear(&vcp->pending_op); /* If there were more volume set ops while waiting for the one that * completes, send request to set volume to the latest pending value. */ if (resend) { DBG(vcp, "set pending volume 0x%x", volume); bt_vcp_set_volume(vcp, volume); } } static void vcp_vstate_notify(struct bt_vcp *vcp, uint16_t value_handle, const uint8_t *value, uint16_t length, void *user_data) { struct vol_state vstate; memcpy(&vstate, value, sizeof(struct vol_state)); DBG(vcp, "Vol Settings 0x%x", vstate.vol_set); DBG(vcp, "Mute Status 0x%x", vstate.mute); DBG(vcp, "Vol Counter 0x%x", vstate.counter); vcp->volume = vstate.vol_set; vcp->volume_counter = vstate.counter; if (vcp->volume_changed) vcp->volume_changed(vcp, vcp->volume); vcp->pending_op.wait_notify = false; if (!vcp->pending_op.wait_reply) vcp_set_volume_complete(vcp); } static void vcp_volume_cp_sent(bool success, uint8_t err, void *user_data) { struct bt_vcp *vcp = user_data; if (!success) { if (err == BT_ATT_ERROR_INVALID_CHANGE_COUNTER) DBG(vcp, "setting volume failed: invalid counter"); else DBG(vcp, "setting volume failed: error 0x%x", err); vcp_set_volume_complete(vcp); } else { vcp->pending_op.wait_reply = false; if (!vcp->pending_op.wait_notify) vcp_set_volume_complete(vcp); } } static bool vcp_set_volume_timeout(void *data) { struct bt_vcp *vcp = data; DBG(vcp, "setting volume: timeout"); vcp->pending_op.timeout_id = 0; vcp_set_volume_complete(vcp); return false; } static bool vcp_set_volume_client(struct bt_vcp *vcp, uint8_t volume) { struct bt_vcs_client_ab_vol req; uint16_t value_handle; struct bt_vcs *vcs = vcp_get_vcs(vcp); if (!vcs) { DBG(vcp, "error: vcs not available"); return false; } if (!vcs->vol_cp) { DBG(vcp, "error: vol_cp characteristics not available"); return false; } if (!gatt_db_attribute_get_char_data(vcs->vol_cp, NULL, &value_handle, NULL, NULL, NULL)) { DBG(vcp, "error: vol_cp characteristics not available"); return false; } /* If there is another set volume op in flight, just update the wanted * pending volume value. Req with the latest volume value is sent after * the current one completes. This may skip over some volume changes, * as it only sends a request for the final value. */ if (vcp->pending_op.timeout_id) { vcp->pending_op.volume = volume; vcp->pending_op.resend = true; return true; } else if (vcp->volume == volume) { /* Do not set to current value, as that doesn't generate * a notification */ return true; } req.op = BT_VCS_SET_ABSOLUTE_VOL; req.vol_set = volume; req.change_counter = vcp->volume_counter; if (!bt_gatt_client_write_value(vcp->client, value_handle, (void *)&req, sizeof(req), vcp_volume_cp_sent, vcp, NULL)) { DBG(vcp, "error writing volume"); return false; } vcp->pending_op.timeout_id = timeout_add(VCP_CLIENT_OP_TIMEOUT, vcp_set_volume_timeout, vcp, NULL); vcp->pending_op.wait_notify = true; vcp->pending_op.wait_reply = true; return true; } static bool vcp_set_volume_server(struct bt_vcp *vcp, uint8_t volume) { struct bt_vcp_db *vdb = vcp_get_vdb(vcp); struct vol_state *vstate; vcp->volume = volume; if (!vdb) { DBG(vcp, "error: VDB not available"); return false; } vstate = vdb_get_vstate(vdb); if (!vstate) { DBG(vcp, "error: VSTATE not available"); return false; } vstate->vol_set = vcp->volume; vstate->counter = -~vstate->counter; /*Increment Change Counter*/ gatt_db_attribute_notify(vdb->vcs->vs, (void *) vstate, sizeof(struct vol_state), bt_vcp_get_att(vcp)); return true; } bool bt_vcp_set_volume(struct bt_vcp *vcp, uint8_t volume) { if (vcp->client) return vcp_set_volume_client(vcp, volume); else return vcp_set_volume_server(vcp, volume); } uint8_t bt_vcp_get_volume(struct bt_vcp *vcp) { return vcp->volume; } static void vcp_voffset_state_notify(struct bt_vcp *vcp, uint16_t value_handle, const uint8_t *value, uint16_t length, void *user_data) { struct vol_offset_state vostate; memcpy(&vostate, value, sizeof(struct vol_offset_state)); DBG(vcp, "Vol Offset 0x%x", vostate.vol_offset); DBG(vcp, "Vol Offset Counter 0x%x", vostate.counter); } static void vcp_audio_loc_notify(struct bt_vcp *vcp, uint16_t value_handle, const uint8_t *value, uint16_t length, void *user_data) { uint32_t *vocs_audio_loc_n = malloc(sizeof(uint32_t)); *vocs_audio_loc_n = 0; if (value != NULL) memcpy(vocs_audio_loc_n, value, sizeof(uint32_t)); DBG(vcp, "VOCS Audio Location 0x%x", *vocs_audio_loc_n); free(vocs_audio_loc_n); } static void vcp_audio_descriptor_notify(struct bt_vcp *vcp, uint16_t value_handle, const uint8_t *value, uint16_t length, void *user_data) { char vocs_audio_dec_n[256] = {'\0'}; memcpy(vocs_audio_dec_n, value, length); DBG(vcp, "VOCS Audio Descriptor 0x%s", *vocs_audio_dec_n); } static void vcp_vflag_notify(struct bt_vcp *vcp, uint16_t value_handle, const uint8_t *value, uint16_t length, void *user_data) { uint8_t vflag; memcpy(&vflag, value, sizeof(vflag)); DBG(vcp, "Vol Flag 0x%x", vflag); } static void read_vol_flag(struct bt_vcp *vcp, bool success, uint8_t att_ecode, const uint8_t *value, uint16_t length, void *user_data) { uint8_t *vol_flag; struct iovec iov = { .iov_base = (void *) value, .iov_len = length, }; if (!success) { DBG(vcp, "Unable to read Vol Flag: error 0x%02x", att_ecode); return; } vol_flag = iov_pull_mem(&iov, sizeof(*vol_flag)); if (!vol_flag) { DBG(vcp, "Unable to get Vol Flag"); return; } DBG(vcp, "Vol Flag:%x", *vol_flag); } static void read_vol_state(struct bt_vcp *vcp, bool success, uint8_t att_ecode, const uint8_t *value, uint16_t length, void *user_data) { struct vol_state *vs; struct iovec iov = { .iov_base = (void *) value, .iov_len = length, }; if (!success) { DBG(vcp, "Unable to read Vol State: error 0x%02x", att_ecode); return; } vs = iov_pull_mem(&iov, sizeof(*vs)); if (!vs) { DBG(vcp, "Unable to get Vol State"); return; } DBG(vcp, "Vol Set:%x", vs->vol_set); DBG(vcp, "Vol Mute:%x", vs->mute); DBG(vcp, "Vol Counter:%x", vs->counter); vcp->volume = vs->vol_set; vcp->volume_counter = vs->counter; } static void read_vol_offset_state(struct bt_vcp *vcp, bool success, uint8_t att_ecode, const uint8_t *value, uint16_t length, void *user_data) { struct vol_offset_state *vos; struct iovec iov = { .iov_base = (void *) value, .iov_len = length, }; if (!success) { DBG(vcp, "Unable to read Vol Offset State: error 0x%02x", att_ecode); return; } vos = iov_pull_mem(&iov, sizeof(*vos)); if (!vos) { DBG(vcp, "Unable to get Vol Offset State"); return; } DBG(vcp, "Vol Offset: 0x%04x", le16_to_cpu(vos->vol_offset)); DBG(vcp, "Vol Counter: 0x%02x", vos->counter); } static void read_vocs_audio_location(struct bt_vcp *vcp, bool success, uint8_t att_ecode, const uint8_t *value, uint16_t length, void *user_data) { uint32_t vocs_audio_loc; struct iovec iov; if (!value) { DBG(vcp, "Unable to get VOCS Audio Location"); return; } if (!success) { DBG(vcp, "Unable to read VOCS Audio Location: error 0x%02x", att_ecode); return; } iov.iov_base = (void *)value; iov.iov_len = length; if (!util_iov_pull_le32(&iov, &vocs_audio_loc)) { DBG(vcp, "Invalid size for VOCS Audio Location"); return; } DBG(vcp, "VOCS Audio Loc: 0x%8x", vocs_audio_loc); } static void read_vocs_audio_descriptor(struct bt_vcp *vcp, bool success, uint8_t att_ecode, const uint8_t *value, uint16_t length, void *user_data) { char *vocs_ao_dec_r; if (!value) { DBG(vcp, "Unable to get VOCS Audio Location"); return; } if (!success) { DBG(vcp, "Unable to read VOCS Audio Descriptor: error 0x%02x", att_ecode); return; } vocs_ao_dec_r = util_memdup(value, length + 1); memset(vocs_ao_dec_r + length, 0, 1); DBG(vcp, "VOCS Audio Descriptor: %s", vocs_ao_dec_r); free(vocs_ao_dec_r); vocs_ao_dec_r = NULL; } static void vcp_pending_destroy(void *data) { struct bt_vcp_pending *pending = data; struct bt_vcp *vcp = pending->vcp; if (queue_remove_if(vcp->pending, NULL, pending)) free(pending); } static void vcp_pending_complete(bool success, uint8_t att_ecode, const uint8_t *value, uint16_t length, void *user_data) { struct bt_vcp_pending *pending = user_data; if (pending->func) pending->func(pending->vcp, success, att_ecode, value, length, pending->user_data); } static void vcp_read_value(struct bt_vcp *vcp, uint16_t value_handle, vcp_func_t func, void *user_data) { struct bt_vcp_pending *pending; pending = new0(struct bt_vcp_pending, 1); pending->vcp = vcp; pending->func = func; pending->user_data = user_data; pending->id = bt_gatt_client_read_value(vcp->client, value_handle, vcp_pending_complete, pending, vcp_pending_destroy); if (!pending->id) { DBG(vcp, "Unable to send Read request"); free(pending); return; } queue_push_tail(vcp->pending, pending); } static void vcp_register(uint16_t att_ecode, void *user_data) { struct bt_vcp_notify *notify = user_data; if (att_ecode) DBG(notify->vcp, "VCP register failed: 0x%04x", att_ecode); } static void vcp_notify(uint16_t value_handle, const uint8_t *value, uint16_t length, void *user_data) { struct bt_vcp_notify *notify = user_data; if (notify->func) notify->func(notify->vcp, value_handle, value, length, notify->user_data); } static void vcp_notify_destroy(void *data) { struct bt_vcp_notify *notify = data; struct bt_vcp *vcp = notify->vcp; if (queue_remove_if(vcp->notify, NULL, notify)) free(notify); } static unsigned int vcp_register_notify(struct bt_vcp *vcp, uint16_t value_handle, vcp_notify_t func, void *user_data) { struct bt_vcp_notify *notify; notify = new0(struct bt_vcp_notify, 1); notify->vcp = vcp; notify->func = func; notify->user_data = user_data; notify->id = bt_gatt_client_register_notify(vcp->client, value_handle, vcp_register, vcp_notify, notify, vcp_notify_destroy); if (!notify->id) { DBG(vcp, "Unable to register for notifications"); free(notify); return 0; } queue_push_tail(vcp->notify, notify); return notify->id; } static void foreach_vcs_char(struct gatt_db_attribute *attr, void *user_data) { struct bt_vcp *vcp = user_data; uint16_t value_handle; bt_uuid_t uuid, uuid_vstate, uuid_cp, uuid_vflag; struct bt_vcs *vcs; if (!gatt_db_attribute_get_char_data(attr, NULL, &value_handle, NULL, NULL, &uuid)) return; bt_uuid16_create(&uuid_vstate, VOL_STATE_CHRC_UUID); bt_uuid16_create(&uuid_cp, VOL_CP_CHRC_UUID); bt_uuid16_create(&uuid_vflag, VOL_FLAG_CHRC_UUID); if (!bt_uuid_cmp(&uuid, &uuid_vstate)) { DBG(vcp, "VCS Vol state found: handle 0x%04x", value_handle); vcs = vcp_get_vcs(vcp); if (!vcs) return; vcs->vs = attr; vcp_read_value(vcp, value_handle, read_vol_state, vcp); vcp->vstate_id = vcp_register_notify(vcp, value_handle, vcp_vstate_notify, NULL); return; } if (!bt_uuid_cmp(&uuid, &uuid_cp)) { DBG(vcp, "VCS Volume CP found: handle 0x%04x", value_handle); vcs = vcp_get_vcs(vcp); if (!vcs) return; vcs->vol_cp = attr; return; } if (!bt_uuid_cmp(&uuid, &uuid_vflag)) { DBG(vcp, "VCS Vol Flag found: handle 0x%04x", value_handle); vcs = vcp_get_vcs(vcp); if (!vcs) return; vcs->vf = attr; vcp_read_value(vcp, value_handle, read_vol_flag, vcp); vcp->vflag_id = vcp_register_notify(vcp, value_handle, vcp_vflag_notify, NULL); } } static void foreach_vocs_char(struct gatt_db_attribute *attr, void *user_data) { struct bt_vcp *vcp = user_data; uint16_t value_handle; bt_uuid_t uuid, uuid_vostate, uuid_audio_loc, uuid_vo_cp, uuid_audio_op_decs; struct bt_vocs *vocs; if (!gatt_db_attribute_get_char_data(attr, NULL, &value_handle, NULL, NULL, &uuid)) return; bt_uuid16_create(&uuid_vostate, VOCS_STATE_CHAR_UUID); bt_uuid16_create(&uuid_audio_loc, VOCS_AUDIO_LOC_CHRC_UUID); bt_uuid16_create(&uuid_vo_cp, VOCS_CP_CHRC_UUID); bt_uuid16_create(&uuid_audio_op_decs, VOCS_AUDIO_OP_DESC_CHAR_UUID); if (!bt_uuid_cmp(&uuid, &uuid_vostate)) { DBG(vcp, "VOCS Vol state found: handle 0x%04x", value_handle); vocs = vcp_get_vocs(vcp); if (!vocs || vocs->vos) return; vocs->vos = attr; vcp_read_value(vcp, value_handle, read_vol_offset_state, vcp); vcp->state_id = vcp_register_notify(vcp, value_handle, vcp_voffset_state_notify, NULL); return; } if (!bt_uuid_cmp(&uuid, &uuid_audio_loc)) { DBG(vcp, "VOCS Volume Audio Location found: handle 0x%04x", value_handle); vocs = vcp_get_vocs(vcp); if (!vocs || vocs->voal) return; vocs->voal = attr; vcp_read_value(vcp, value_handle, read_vocs_audio_location, vcp); vcp->audio_loc_id = vcp_register_notify(vcp, value_handle, vcp_audio_loc_notify, NULL); return; } if (!bt_uuid_cmp(&uuid, &uuid_vo_cp)) { DBG(vcp, "VOCS Volume CP found: handle 0x%04x", value_handle); vocs = vcp_get_vocs(vcp); if (!vocs || vocs->vo_cp) return; vocs->vo_cp = attr; return; } if (!bt_uuid_cmp(&uuid, &uuid_audio_op_decs)) { DBG(vcp, "VOCS Vol Audio Descriptor found: handle 0x%04x", value_handle); vocs = vcp_get_vocs(vcp); if (!vocs || vocs->voaodec) return; vocs->voaodec = attr; vcp_read_value(vcp, value_handle, read_vocs_audio_descriptor, vcp); vcp->ao_dec_id = vcp_register_notify(vcp, value_handle, vcp_audio_descriptor_notify, NULL); } } static void read_aics_audio_ip_state(struct bt_vcp *vcp, bool success, uint8_t att_ecode, const uint8_t *value, uint16_t length, void *user_data) { struct aud_ip_st *ip_st; struct iovec iov = { .iov_base = (void *) value, .iov_len = length, }; if (!success) { DBG(vcp, "Unable to read Audio Input State: error 0x%02x", att_ecode); return; } ip_st = iov_pull_mem(&iov, sizeof(*ip_st)); if (!ip_st) { DBG(vcp, "Unable to get Audio Input State"); return; } DBG(vcp, "Audio Input State, Gain Setting:%d", ip_st->gain_setting); DBG(vcp, "Audio Input State, Mute:%x", ip_st->mute); DBG(vcp, "Audio Input State, Gain Mode:%x", ip_st->gain_mode); DBG(vcp, "Audio Input State, Change Counter:%x", ip_st->chg_counter); } static void aics_ip_state_notify(struct bt_vcp *vcp, uint16_t value_handle, const uint8_t *value, uint16_t length, void *user_data) { struct aud_ip_st ip_st; memcpy(&ip_st, value, sizeof(struct aud_ip_st)); DBG(vcp, "Audio Input State, Gain Setting:%d", ip_st.gain_setting); DBG(vcp, "Audio Input State, Mute:%x", ip_st.mute); DBG(vcp, "Audio Input State, Gain Mode:%x", ip_st.gain_mode); DBG(vcp, "Audio Input State, Change Counter:%x", ip_st.chg_counter); } static void read_aics_gain_setting_prop(struct bt_vcp *vcp, bool success, uint8_t att_ecode, const uint8_t *value, uint16_t length, void *user_data) { struct gain_setting_prop *aics_gain_setting_prop; struct iovec iov = { .iov_base = (void *) value, .iov_len = length, }; if (!value) { DBG(vcp, "Unable to get Gain Setting Properties Char"); return; } if (!success) { DBG(vcp, "Unable to read Gain Setting Properties Char: 0x%02x", att_ecode); return; } aics_gain_setting_prop = iov_pull_mem(&iov, sizeof(*aics_gain_setting_prop)); if (!aics_gain_setting_prop) { DBG(vcp, "Unable to get Gain Setting Properties Char"); return; } DBG(vcp, "Gain Setting Properties, Units: %x", aics_gain_setting_prop->gain_setting_units); DBG(vcp, "Gain Setting Properties, Min Value: %d", aics_gain_setting_prop->gain_setting_min); DBG(vcp, "Gain Setting Properties, Max Value: %d", aics_gain_setting_prop->gain_setting_max); } static void read_aics_aud_ip_type(struct bt_vcp *vcp, bool success, uint8_t att_ecode, const uint8_t *value, uint16_t length, void *user_data) { uint8_t ip_type; if (!success) { DBG(vcp, "Unable to read Audio Input Type Char: error 0x%02x", att_ecode); return; } memcpy(&ip_type, value, length); DBG(vcp, "Audio Input Type : %x", ip_type); } static void read_aics_audio_ip_status(struct bt_vcp *vcp, bool success, uint8_t att_ecode, const uint8_t *value, uint16_t length, void *user_data) { uint8_t ip_status; if (!success) { DBG(vcp, "Unable to read Audio Input Status Char: 0x%02x", att_ecode); return; } memcpy(&ip_status, value, length); DBG(vcp, "Audio Input Status : %x", ip_status); } static void aics_ip_status_notify(struct bt_vcp *vcp, uint16_t value_handle, const uint8_t *value, uint16_t length, void *user_data) { uint8_t ip_status; memcpy(&ip_status, value, length); DBG(vcp, "Audio Input Status, %x", ip_status); } static void read_aics_audio_ip_description(struct bt_vcp *vcp, bool success, uint8_t att_ecode, const uint8_t *value, uint16_t length, void *user_data) { char *ip_descrptn; if (!value) { DBG(vcp, "Unable to get Audio Input Description"); return; } if (!success) { DBG(vcp, "Unable to read Audio Input Description Char: error 0x%02x", att_ecode); return; } ip_descrptn = util_memdup(value, length + 1); memset(ip_descrptn + length, 0, 1); DBG(vcp, "Audio Input Description: %s", ip_descrptn); free(ip_descrptn); ip_descrptn = NULL; } static void aics_audio_ip_desr_notify(struct bt_vcp *vcp, uint16_t value_handle, const uint8_t *value, uint16_t length, void *user_data) { char *aud_ip_desr; aud_ip_desr = malloc(length+1); memset(aud_ip_desr, 0, length+1); memcpy(aud_ip_desr, value, length); DBG(vcp, "Audio Input Description Notify, %s", aud_ip_desr); free(aud_ip_desr); aud_ip_desr = NULL; } static void foreach_aics_char(struct gatt_db_attribute *attr, void *user_data) { struct bt_vcp *vcp = user_data; uint16_t value_handle; bt_uuid_t uuid, uuid_ipstate, uuid_gain_setting_prop, uuid_ip_type, uuid_ip_status, uuid_ip_cp, uuid_ip_decs; struct bt_aics *aics; if (!gatt_db_attribute_get_char_data(attr, NULL, &value_handle, NULL, NULL, &uuid)) return; bt_uuid16_create(&uuid_ipstate, AICS_INPUT_STATE_CHAR_UUID); bt_uuid16_create(&uuid_gain_setting_prop, AICS_GAIN_SETTING_PROP_CHAR_UUID); bt_uuid16_create(&uuid_ip_type, AICS_AUDIO_INPUT_TYPE_CHAR_UUID); bt_uuid16_create(&uuid_ip_status, AICS_INPUT_STATUS_CHAR_UUID); bt_uuid16_create(&uuid_ip_cp, AICS_AUDIO_INPUT_CP_CHRC_UUID); bt_uuid16_create(&uuid_ip_decs, AICS_INPUT_DESCR_CHAR_UUID); if (!bt_uuid_cmp(&uuid, &uuid_ipstate)) { DBG(vcp, "AICS Audio Input State Char found: handle 0x%04x", value_handle); aics = vcp_get_aics(vcp); if (!aics || aics->aud_ip_state) return; aics->aud_ip_state = attr; vcp_read_value(vcp, value_handle, read_aics_audio_ip_state, vcp); vcp->aics_ip_state_id = vcp_register_notify(vcp, value_handle, aics_ip_state_notify, NULL); return; } if (!bt_uuid_cmp(&uuid, &uuid_gain_setting_prop)) { DBG(vcp, "AICS Gain Setting Properties Char found: handle 0x%04x", value_handle); aics = vcp_get_aics(vcp); if (!aics || aics->gain_stting_prop) return; aics->gain_stting_prop = attr; vcp_read_value(vcp, value_handle, read_aics_gain_setting_prop, vcp); return; } if (!bt_uuid_cmp(&uuid, &uuid_ip_type)) { DBG(vcp, "AICS Audio Input Type Char found: handle 0x%04x", value_handle); aics = vcp_get_aics(vcp); if (!aics || aics->aud_ip_type) return; aics->aud_ip_type = attr; vcp_read_value(vcp, value_handle, read_aics_aud_ip_type, vcp); return; } if (!bt_uuid_cmp(&uuid, &uuid_ip_status)) { DBG(vcp, "AICS Audio Input Status Char found: handle 0x%04x", value_handle); aics = vcp_get_aics(vcp); if (!aics || aics->aud_ip_status) return; aics->aud_ip_status = attr; vcp_read_value(vcp, value_handle, read_aics_audio_ip_status, vcp); vcp->aics_ip_status_id = vcp_register_notify(vcp, value_handle, aics_ip_status_notify, NULL); return; } if (!bt_uuid_cmp(&uuid, &uuid_ip_cp)) { DBG(vcp, "AICS Input CP found: handle 0x%04x", value_handle); aics = vcp_get_aics(vcp); if (!aics || aics->aud_ip_cp) return; aics->aud_ip_cp = attr; return; } if (!bt_uuid_cmp(&uuid, &uuid_ip_decs)) { DBG(vcp, "AICS Audio Input Description Char found: handle 0x%04x", value_handle); aics = vcp_get_aics(vcp); if (!aics || aics->aud_ip_dscrptn) return; aics->aud_ip_dscrptn = attr; vcp_read_value(vcp, value_handle, read_aics_audio_ip_description, vcp); vcp->aics_ip_descr_id = vcp_register_notify(vcp, value_handle, aics_audio_ip_desr_notify, NULL); } } static void foreach_vcs_service(struct gatt_db_attribute *attr, void *user_data) { struct bt_vcp *vcp = user_data; struct bt_vcs *vcs = vcp_get_vcs(vcp); vcs->service = attr; gatt_db_service_set_claimed(attr, true); gatt_db_service_foreach_char(attr, foreach_vcs_char, vcp); } static void foreach_vocs_service(struct gatt_db_attribute *attr, void *user_data) { struct bt_vcp *vcp = user_data; struct bt_vocs *vocs = vcp_get_vocs(vcp); if (!vocs || !attr) return; vocs->service = attr; gatt_db_service_set_claimed(attr, true); gatt_db_service_foreach_char(attr, foreach_vocs_char, vcp); } static void foreach_aics_service(struct gatt_db_attribute *attr, void *user_data) { struct bt_vcp *vcp = user_data; struct bt_aics *aics = vcp_get_aics(vcp); if (!aics || !attr) return; aics->service = attr; gatt_db_service_set_claimed(attr, true); gatt_db_service_foreach_char(attr, foreach_aics_char, vcp); } bool bt_vcp_attach(struct bt_vcp *vcp, struct bt_gatt_client *client) { bt_uuid_t uuid; if (!sessions) sessions = queue_new(); queue_push_tail(sessions, vcp); if (!client) return true; if (vcp->client) return false; vcp->client = bt_gatt_client_clone(client); if (!vcp->client) return false; bt_uuid16_create(&uuid, VCS_UUID); gatt_db_foreach_service(vcp->rdb->db, &uuid, foreach_vcs_service, vcp); bt_uuid16_create(&uuid, VOL_OFFSET_CS_UUID); gatt_db_foreach_service(vcp->rdb->db, &uuid, foreach_vocs_service, vcp); bt_uuid16_create(&uuid, AUDIO_INPUT_CS_UUID); gatt_db_foreach_service(vcp->rdb->db, &uuid, foreach_aics_service, vcp); return true; } bluez-5.82/src/shared/PaxHeaders/timeout.h0000644000000000000000000000005014031556405015545 xustar0020 atime=1743515768 20 ctime=1743591277 bluez-5.82/src/shared/timeout.h0000644000000000000000000000112614031556405015226 0ustar00rootroot/* SPDX-License-Identifier: LGPL-2.1-or-later */ /* * * BlueZ - Bluetooth protocol stack for Linux * * Copyright (C) 2014 Intel Corporation. All rights reserved. * * */ #include typedef bool (*timeout_func_t)(void *user_data); typedef void (*timeout_destroy_func_t)(void *user_data); unsigned int timeout_add(unsigned int timeout, timeout_func_t func, void *user_data, timeout_destroy_func_t destroy); void timeout_remove(unsigned int id); unsigned int timeout_add_seconds(unsigned int timeout, timeout_func_t func, void *user_data, timeout_destroy_func_t destroy); bluez-5.82/src/shared/PaxHeaders/lc3.h0000644000000000000000000000005014643061455014545 xustar0020 atime=1743515790 20 ctime=1743591277 bluez-5.82/src/shared/lc3.h0000644000000000000000000011003614643061455014227 0ustar00rootroot/* SPDX-License-Identifier: LGPL-2.1-or-later */ /* * * BlueZ - Bluetooth protocol stack for Linux * * Copyright (C) 2022 Intel Corporation. All rights reserved. * Copyright 2024 NXP * */ #include "src/shared/bap-defs.h" #define LC3_ID 0x06 #define LC3_TYPE_BASE 0x01 #define LC3_FREQ LC3_TYPE_BASE #define LC3_FREQ_8KHZ BIT(0) #define LC3_FREQ_11KHZ BIT(1) #define LC3_FREQ_16KHZ BIT(2) #define LC3_FREQ_22KHZ BIT(3) #define LC3_FREQ_24KHZ BIT(4) #define LC3_FREQ_32KHZ BIT(5) #define LC3_FREQ_44KHZ BIT(6) #define LC3_FREQ_48KHZ BIT(7) #define LC3_FREQ_ANY (LC3_FREQ_8KHZ | \ LC3_FREQ_11KHZ | \ LC3_FREQ_16KHZ | \ LC3_FREQ_22KHZ | \ LC3_FREQ_24KHZ | \ LC3_FREQ_32KHZ | \ LC3_FREQ_44KHZ | \ LC3_FREQ_48KHZ) #define LC3_DURATION (LC3_TYPE_BASE + 1) #define LC3_DURATION_7_5 BIT(0) #define LC3_DURATION_10 BIT(1) #define LC3_DURATION_ANY (LC3_DURATION_7_5 | LC3_DURATION_10) #define LC3_DURATION_PREFER_7_5 BIT(4) #define LC3_DURATION_PREFER_10 BIT(5) #define LC3_CHAN_COUNT (LC3_TYPE_BASE + 2) #define LC3_CHAN_COUNT_SUPPORT BIT(0) #define LC3_FRAME_LEN (LC3_TYPE_BASE + 3) #define LC3_FRAME_COUNT (LC3_TYPE_BASE + 4) #define LC3_CAPABILITIES(_freq, _duration, _chan_count, _len_min, _len_max) \ UTIL_IOV_INIT(0x02, LC3_FREQ, _freq, _freq >> 8, \ 0x02, LC3_DURATION, _duration, \ 0x02, LC3_CHAN_COUNT, _chan_count, \ 0x05, LC3_FRAME_LEN, _len_min, _len_min >> 8, \ _len_max, _len_max >> 8) #define LC3_CONFIG_BASE 0x01 #define LC3_CONFIG_FREQ (LC3_CONFIG_BASE) #define LC3_CONFIG_FREQ_8KHZ 0x01 #define LC3_CONFIG_FREQ_11KHZ 0x02 #define LC3_CONFIG_FREQ_16KHZ 0x03 #define LC3_CONFIG_FREQ_22KHZ 0x04 #define LC3_CONFIG_FREQ_24KHZ 0x05 #define LC3_CONFIG_FREQ_32KHZ 0x06 #define LC3_CONFIG_FREQ_44KHZ 0x07 #define LC3_CONFIG_FREQ_48KHZ 0x08 #define LC3_CONFIG_DURATION (LC3_CONFIG_BASE + 1) #define LC3_CONFIG_DURATION_7_5 0x00 #define LC3_CONFIG_DURATION_10 0x01 #define LC3_CONFIG_CHAN_ALLOC (LC3_CONFIG_BASE + 2) #define LC3_CONFIG_CHAN_ALLOC_LEN 0x05 #define LC3_CONFIG_FRAME_LEN (LC3_CONFIG_BASE + 3) #define LC3_CONFIG(_freq, _duration, _len) \ UTIL_IOV_INIT(0x02, LC3_CONFIG_FREQ, _freq, \ 0x02, LC3_CONFIG_DURATION, _duration, \ 0x03, LC3_CONFIG_FRAME_LEN, _len, _len >> 8) #define LC3_AC_BITS(_ac) (BIT(_ac) - 1) #define LC3_CONFIG_AC(_freq, _duration, _len, _ac) \ UTIL_IOV_INIT(0x02, LC3_CONFIG_FREQ, _freq, \ 0x02, LC3_CONFIG_DURATION, _duration, \ 0x03, LC3_CONFIG_FRAME_LEN, _len, _len >> 8, \ 0x05, LC3_CONFIG_CHAN_ALLOC, LC3_AC_BITS(_ac), \ LC3_AC_BITS(_ac) >> 8 & 0xff, \ LC3_AC_BITS(_ac) >> 16 & 0xff, \ LC3_AC_BITS(_ac) >> 24 & 0xff) #define LC3_CONFIG_8(_duration, _len) \ LC3_CONFIG(LC3_CONFIG_FREQ_8KHZ, _duration, _len) #define LC3_CONFIG_8_AC(_duration, _len, _ac) \ LC3_CONFIG_AC(LC3_CONFIG_FREQ_8KHZ, _duration, _len, _ac) #define LC3_CONFIG_11(_duration, _len) \ LC3_CONFIG(LC3_CONFIG_FREQ_11KHZ, _duration, _len) #define LC3_CONFIG_11_AC(_duration, _len, _ac) \ LC3_CONFIG_AC(LC3_CONFIG_FREQ_11KHZ, _duration, _len, _ac) #define LC3_CONFIG_16(_duration, _len) \ LC3_CONFIG(LC3_CONFIG_FREQ_16KHZ, _duration, _len) #define LC3_CONFIG_16_AC(_duration, _len, _ac) \ LC3_CONFIG_AC(LC3_CONFIG_FREQ_16KHZ, _duration, _len, _ac) #define LC3_CONFIG_22(_duration, _len) \ LC3_CONFIG(LC3_CONFIG_FREQ_22KHZ, _duration, _len) #define LC3_CONFIG_22_AC(_duration, _len) \ LC3_CONFIG(LC3_CONFIG_FREQ_22KHZ, _duration, _len, _ac) #define LC3_CONFIG_24(_duration, _len) \ LC3_CONFIG(LC3_CONFIG_FREQ_24KHZ, _duration, _len) #define LC3_CONFIG_24_AC(_duration, _len, _ac) \ LC3_CONFIG_AC(LC3_CONFIG_FREQ_24KHZ, _duration, _len, _ac) #define LC3_CONFIG_32(_duration, _len) \ LC3_CONFIG(LC3_CONFIG_FREQ_32KHZ, _duration, _len) #define LC3_CONFIG_32_AC(_duration, _len, _ac) \ LC3_CONFIG_AC(LC3_CONFIG_FREQ_32KHZ, _duration, _len, _ac) #define LC3_CONFIG_44(_duration, _len) \ LC3_CONFIG(LC3_CONFIG_FREQ_44KHZ, _duration, _len) #define LC3_CONFIG_44_AC(_duration, _len, _ac) \ LC3_CONFIG_AC(LC3_CONFIG_FREQ_44KHZ, _duration, _len, _ac) #define LC3_CONFIG_48(_duration, _len) \ LC3_CONFIG(LC3_CONFIG_FREQ_48KHZ, _duration, _len) #define LC3_CONFIG_48_AC(_duration, _len, _ac) \ LC3_CONFIG_AC(LC3_CONFIG_FREQ_48KHZ, _duration, _len, _ac) #define LC3_CONFIG_FRAME_LEN_8_1 26u #define LC3_CONFIG_8_1 \ LC3_CONFIG_8(LC3_CONFIG_DURATION_7_5, LC3_CONFIG_FRAME_LEN_8_1) #define LC3_CONFIG_8_1_AC(_ac) \ LC3_CONFIG_8_AC(LC3_CONFIG_DURATION_7_5, LC3_CONFIG_FRAME_LEN_8_1, _ac) #define LC3_CONFIG_FRAME_LEN_8_2 30u #define LC3_CONFIG_8_2 \ LC3_CONFIG_8(LC3_CONFIG_DURATION_10, LC3_CONFIG_FRAME_LEN_8_2 ) #define LC3_CONFIG_8_2_AC(_ac) \ LC3_CONFIG_8_AC(LC3_CONFIG_DURATION_10, LC3_CONFIG_FRAME_LEN_8_2, _ac) #define LC3_CONFIG_FRAME_LEN_16_1 30u #define LC3_CONFIG_16_1 \ LC3_CONFIG_16(LC3_CONFIG_DURATION_7_5, LC3_CONFIG_FRAME_LEN_16_1) #define LC3_CONFIG_16_1_AC(_ac) \ LC3_CONFIG_16_AC(LC3_CONFIG_DURATION_7_5, LC3_CONFIG_FRAME_LEN_16_1, \ _ac) #define LC3_CONFIG_FRAME_LEN_16_2 40u #define LC3_CONFIG_16_2 \ LC3_CONFIG_16(LC3_CONFIG_DURATION_10, LC3_CONFIG_FRAME_LEN_16_2) #define LC3_CONFIG_16_2_AC(_ac) \ LC3_CONFIG_16_AC(LC3_CONFIG_DURATION_10, LC3_CONFIG_FRAME_LEN_16_2, _ac) #define LC3_CONFIG_FRAME_LEN_24_1 45u #define LC3_CONFIG_24_1 \ LC3_CONFIG_24(LC3_CONFIG_DURATION_7_5, LC3_CONFIG_FRAME_LEN_24_1) #define LC3_CONFIG_24_1_AC(_ac) \ LC3_CONFIG_24_AC(LC3_CONFIG_DURATION_7_5, LC3_CONFIG_FRAME_LEN_24_1, \ _ac) #define LC3_CONFIG_FRAME_LEN_24_2 60u #define LC3_CONFIG_24_2 \ LC3_CONFIG_24(LC3_CONFIG_DURATION_10, LC3_CONFIG_FRAME_LEN_24_2) #define LC3_CONFIG_24_2_AC(_ac) \ LC3_CONFIG_24_AC(LC3_CONFIG_DURATION_10, LC3_CONFIG_FRAME_LEN_24_2, _ac) #define LC3_CONFIG_FRAME_LEN_32_1 60u #define LC3_CONFIG_32_1 \ LC3_CONFIG_32(LC3_CONFIG_DURATION_7_5, LC3_CONFIG_FRAME_LEN_32_1) #define LC3_CONFIG_32_1_AC(_ac) \ LC3_CONFIG_32_AC(LC3_CONFIG_DURATION_7_5, LC3_CONFIG_FRAME_LEN_32_1, \ _ac) #define LC3_CONFIG_FRAME_LEN_32_2 80u #define LC3_CONFIG_32_2 \ LC3_CONFIG_32(LC3_CONFIG_DURATION_10, LC3_CONFIG_FRAME_LEN_32_2) #define LC3_CONFIG_32_2_AC(_ac) \ LC3_CONFIG_32_AC(LC3_CONFIG_DURATION_10, LC3_CONFIG_FRAME_LEN_32_2, _ac) #define LC3_CONFIG_FRAME_LEN_44_1 98u #define LC3_CONFIG_44_1 \ LC3_CONFIG_44(LC3_CONFIG_DURATION_7_5, LC3_CONFIG_FRAME_LEN_44_1) #define LC3_CONFIG_44_1_AC(_ac) \ LC3_CONFIG_44_AC(LC3_CONFIG_DURATION_7_5, LC3_CONFIG_FRAME_LEN_44_1, \ _ac) #define LC3_CONFIG_FRAME_LEN_44_2 130u #define LC3_CONFIG_44_2 \ LC3_CONFIG_44(LC3_CONFIG_DURATION_10, LC3_CONFIG_FRAME_LEN_44_2) #define LC3_CONFIG_44_2_AC(_ac) \ LC3_CONFIG_44_AC(LC3_CONFIG_DURATION_10, LC3_CONFIG_FRAME_LEN_44_2, _ac) #define LC3_CONFIG_FRAME_LEN_48_1 75u #define LC3_CONFIG_48_1 \ LC3_CONFIG_48(LC3_CONFIG_DURATION_7_5, LC3_CONFIG_FRAME_LEN_48_1) #define LC3_CONFIG_48_1_AC(_ac) \ LC3_CONFIG_48_AC(LC3_CONFIG_DURATION_7_5, LC3_CONFIG_FRAME_LEN_48_1, \ _ac) #define LC3_CONFIG_FRAME_LEN_48_2 100u #define LC3_CONFIG_48_2 \ LC3_CONFIG_48(LC3_CONFIG_DURATION_10, LC3_CONFIG_FRAME_LEN_48_2) #define LC3_CONFIG_48_2_AC(_ac) \ LC3_CONFIG_48_AC(LC3_CONFIG_DURATION_10, LC3_CONFIG_FRAME_LEN_48_2, _ac) #define LC3_CONFIG_FRAME_LEN_48_3 90u #define LC3_CONFIG_48_3 \ LC3_CONFIG_48(LC3_CONFIG_DURATION_7_5, LC3_CONFIG_FRAME_LEN_48_3) #define LC3_CONFIG_48_3_AC(_ac) \ LC3_CONFIG_48_AC(LC3_CONFIG_DURATION_7_5, LC3_CONFIG_FRAME_LEN_48_3, \ _ac) #define LC3_CONFIG_FRAME_LEN_48_4 120u #define LC3_CONFIG_48_4 \ LC3_CONFIG_48(LC3_CONFIG_DURATION_10, LC3_CONFIG_FRAME_LEN_48_4) #define LC3_CONFIG_48_4_AC(_ac) \ LC3_CONFIG_48_AC(LC3_CONFIG_DURATION_10, LC3_CONFIG_FRAME_LEN_48_4, _ac) #define LC3_CONFIG_FRAME_LEN_48_5 117u #define LC3_CONFIG_48_5 \ LC3_CONFIG_48(LC3_CONFIG_DURATION_7_5, LC3_CONFIG_FRAME_LEN_48_5) #define LC3_CONFIG_48_5_AC(_ac) \ LC3_CONFIG_48_AC(LC3_CONFIG_DURATION_7_5, LC3_CONFIG_FRAME_LEN_48_5, \ _ac) #define LC3_CONFIG_FRAME_LEN_48_6 155u #define LC3_CONFIG_48_6 \ LC3_CONFIG_48(LC3_CONFIG_DURATION_10, LC3_CONFIG_FRAME_LEN_48_6) #define LC3_CONFIG_48_6_AC(_ac) \ LC3_CONFIG_48_AC(LC3_CONFIG_DURATION_10, LC3_CONFIG_FRAME_LEN_48_6, \ _ac) #define BASE(_pd, _sgrp, _nbis, _cfg...) \ _pd & 0xff, _pd >> 8, _pd >> 16, \ _sgrp, \ _nbis, \ _cfg #define BASE_LC3(_pd, _sgrp, _nbis, _cc...) \ BASE(_pd, _sgrp, _nbis, \ 0x06, 0x00, 0x00, 0x00, 0x00, \ _cc) #define LC3_CFG(_freq, _dur, _len) \ 0x0a, \ 0x02, LC3_CONFIG_FREQ, _freq, \ 0x02, LC3_CONFIG_DURATION, _dur, \ 0x03, LC3_CONFIG_FRAME_LEN, _len, _len >> 8 #define LC3_CFG_8_1 \ LC3_CFG(LC3_CONFIG_FREQ_8KHZ, \ LC3_CONFIG_DURATION_7_5, \ LC3_CONFIG_FRAME_LEN_8_1) #define BASE_LC3_8_1 \ BASE_LC3(40000, 1, 1, LC3_CFG_8_1, 0x00, 0x01, 0x00) #define LC3_CFG_8_2 \ LC3_CFG(LC3_CONFIG_FREQ_8KHZ, \ LC3_CONFIG_DURATION_10, \ LC3_CONFIG_FRAME_LEN_8_2) #define BASE_LC3_8_2 \ BASE_LC3(40000, 1, 1, LC3_CFG_8_2, 0x00, 0x01, 0x00) #define LC3_CFG_16_1 \ LC3_CFG(LC3_CONFIG_FREQ_16KHZ, \ LC3_CONFIG_DURATION_7_5, \ LC3_CONFIG_FRAME_LEN_16_1) #define BASE_LC3_16_1 \ BASE_LC3(40000, 1, 1, LC3_CFG_16_1, 0x00, 0x01, 0x00) #define LC3_CFG_16_2 \ LC3_CFG(LC3_CONFIG_FREQ_16KHZ, \ LC3_CONFIG_DURATION_10, \ LC3_CONFIG_FRAME_LEN_16_2) #define BASE_LC3_16_2 \ BASE_LC3(40000, 1, 1, LC3_CFG_16_2, 0x00, 0x01, 0x00) #define LC3_CFG_24_1 \ LC3_CFG(LC3_CONFIG_FREQ_24KHZ, \ LC3_CONFIG_DURATION_7_5, \ LC3_CONFIG_FRAME_LEN_24_1) #define BASE_LC3_24_1 \ BASE_LC3(40000, 1, 1, LC3_CFG_24_1, 0x00, 0x01, 0x00) #define LC3_CFG_24_2 \ LC3_CFG(LC3_CONFIG_FREQ_24KHZ, \ LC3_CONFIG_DURATION_10, \ LC3_CONFIG_FRAME_LEN_24_2) #define BASE_LC3_24_2 \ BASE_LC3(40000, 1, 1, LC3_CFG_24_2, 0x00, 0x01, 0x00) #define LC3_CFG_32_1 \ LC3_CFG(LC3_CONFIG_FREQ_32KHZ, \ LC3_CONFIG_DURATION_7_5, \ LC3_CONFIG_FRAME_LEN_32_1) #define BASE_LC3_32_1 \ BASE_LC3(40000, 1, 1, LC3_CFG_32_1, 0x00, 0x01, 0x00) #define LC3_CFG_32_2 \ LC3_CFG(LC3_CONFIG_FREQ_32KHZ, \ LC3_CONFIG_DURATION_10, \ LC3_CONFIG_FRAME_LEN_32_2) #define BASE_LC3_32_2 \ BASE_LC3(40000, 1, 1, LC3_CFG_32_2, 0x00, 0x01, 0x00) #define LC3_CFG_44_1 \ LC3_CFG(LC3_CONFIG_FREQ_44KHZ, \ LC3_CONFIG_DURATION_7_5, \ LC3_CONFIG_FRAME_LEN_44_1) #define BASE_LC3_44_1 \ BASE_LC3(40000, 1, 1, LC3_CFG_44_1, 0x00, 0x01, 0x00) #define LC3_CFG_44_2 \ LC3_CFG(LC3_CONFIG_FREQ_44KHZ, \ LC3_CONFIG_DURATION_10, \ LC3_CONFIG_FRAME_LEN_44_2) #define BASE_LC3_44_2 \ BASE_LC3(40000, 1, 1, LC3_CFG_44_2, 0x00, 0x01, 0x00) #define LC3_CFG_48_1 \ LC3_CFG(LC3_CONFIG_FREQ_48KHZ, \ LC3_CONFIG_DURATION_7_5, \ LC3_CONFIG_FRAME_LEN_48_1) #define BASE_LC3_48_1 \ BASE_LC3(40000, 1, 1, LC3_CFG_48_1, 0x00, 0x01, 0x00) #define LC3_CFG_48_2 \ LC3_CFG(LC3_CONFIG_FREQ_48KHZ, \ LC3_CONFIG_DURATION_10, \ LC3_CONFIG_FRAME_LEN_48_2) #define BASE_LC3_48_2 \ BASE_LC3(40000, 1, 1, LC3_CFG_48_2, 0x00, 0x01, 0x00) #define LC3_CFG_48_3 \ LC3_CFG(LC3_CONFIG_FREQ_48KHZ, \ LC3_CONFIG_DURATION_7_5, \ LC3_CONFIG_FRAME_LEN_48_3) #define BASE_LC3_48_3 \ BASE_LC3(40000, 1, 1, LC3_CFG_48_3, 0x00, 0x01, 0x00) #define LC3_CFG_48_4 \ LC3_CFG(LC3_CONFIG_FREQ_48KHZ, \ LC3_CONFIG_DURATION_10, \ LC3_CONFIG_FRAME_LEN_48_4) #define BASE_LC3_48_4 \ BASE_LC3(40000, 1, 1, LC3_CFG_48_4, 0x00, 0x01, 0x00) #define LC3_CFG_48_5 \ LC3_CFG(LC3_CONFIG_FREQ_48KHZ, \ LC3_CONFIG_DURATION_7_5, \ LC3_CONFIG_FRAME_LEN_48_5) #define BASE_LC3_48_5 \ BASE_LC3(40000, 1, 1, LC3_CFG_48_5, 0x00, 0x01, 0x00) #define LC3_CFG_48_6 \ LC3_CFG(LC3_CONFIG_FREQ_48KHZ, \ LC3_CONFIG_DURATION_10, \ LC3_CONFIG_FRAME_LEN_48_6) #define BASE_LC3_48_6 \ BASE_LC3(40000, 1, 1, LC3_CFG_48_6, 0x00, 0x01, 0x00) #define BASE_VS(_cc) \ BASE(40000, 1, 1, \ 0xFF, 0x00, 0x00, 0x00, 0x00, \ _cc, 0x00, 0x01, 0x00) #define BASE_LC3_8_1_MBIS \ BASE_LC3(40000, 1, 2, LC3_CFG_8_1, 0x00, 0x01, 0x00, 0x02, 0x00) #define BASE_LC3_8_2_MBIS \ BASE_LC3(40000, 1, 2, LC3_CFG_8_2, 0x00, 0x01, 0x00, 0x02, 0x00) #define BASE_LC3_16_1_MBIS \ BASE_LC3(40000, 1, 2, LC3_CFG_16_1, 0x00, 0x01, 0x00, 0x02, 0x00) #define BASE_LC3_16_2_MBIS \ BASE_LC3(40000, 1, 2, LC3_CFG_16_2, 0x00, 0x01, 0x00, 0x02, 0x00) #define BASE_LC3_24_1_MBIS \ BASE_LC3(40000, 1, 2, LC3_CFG_24_1, 0x00, 0x01, 0x00, 0x02, 0x00) #define BASE_LC3_24_2_MBIS \ BASE_LC3(40000, 1, 2, LC3_CFG_24_2, 0x00, 0x01, 0x00, 0x02, 0x00) #define BASE_LC3_32_1_MBIS \ BASE_LC3(40000, 1, 2, LC3_CFG_32_1, 0x00, 0x01, 0x00, 0x02, 0x00) #define BASE_LC3_32_2_MBIS \ BASE_LC3(40000, 1, 2, LC3_CFG_32_2, 0x00, 0x01, 0x00, 0x02, 0x00) #define BASE_LC3_44_1_MBIS \ BASE_LC3(40000, 1, 2, LC3_CFG_44_1, 0x00, 0x01, 0x00, 0x02, 0x00) #define BASE_LC3_44_2_MBIS \ BASE_LC3(40000, 1, 2, LC3_CFG_44_2, 0x00, 0x01, 0x00, 0x02, 0x00) #define BASE_LC3_48_1_MBIS \ BASE_LC3(40000, 1, 2, LC3_CFG_48_1, 0x00, 0x01, 0x00, 0x02, 0x00) #define BASE_LC3_48_2_MBIS \ BASE_LC3(40000, 1, 2, LC3_CFG_48_2, 0x00, 0x01, 0x00, 0x02, 0x00) #define BASE_LC3_48_3_MBIS \ BASE_LC3(40000, 1, 2, LC3_CFG_48_3, 0x00, 0x01, 0x00, 0x02, 0x00) #define BASE_LC3_48_4_MBIS \ BASE_LC3(40000, 1, 2, LC3_CFG_48_4, 0x00, 0x01, 0x00, 0x02, 0x00) #define BASE_LC3_48_5_MBIS \ BASE_LC3(40000, 1, 2, LC3_CFG_48_5, 0x00, 0x01, 0x00, 0x02, 0x00) #define BASE_LC3_48_6_MBIS \ BASE_LC3(40000, 1, 2, LC3_CFG_48_6, 0x00, 0x01, 0x00, 0x02, 0x00) #define BASE_VS_MBIS(_cc) \ BASE(40000, 1, 2, \ 0xFF, 0x00, 0x00, 0x00, 0x00, \ _cc, 0x00, 0x01, 0x00, 0x02, 0x00) #define LC3_QOS_UNFRAMED 0x00 #define LC3_QOS_FRAMED 0x01 #define LC3_QOS_UCAST(_frame, _pd, _t_lat, _interval, _lat, _sdu, _rtn) \ { \ .ucast.cig_id = 0x00, \ .ucast.cis_id = 0x00, \ .ucast.framing = _frame, \ .ucast.delay = _pd, \ .ucast.target_latency = _t_lat, \ .ucast.io_qos.interval = _interval, \ .ucast.io_qos.latency = _lat, \ .ucast.io_qos.sdu = _sdu, \ .ucast.io_qos.phy = BT_BAP_CONFIG_PHY_2M, \ .ucast.io_qos.rtn = _rtn, \ } #define LC3_QOS_UCAST_7_5_UNFRAMED(_pd, _t_lat, _lat, _sdu, _rtn) \ LC3_QOS_UCAST(LC3_QOS_UNFRAMED, _pd, _t_lat, 7500u, _lat, _sdu, _rtn) #define LC3_QOS_UCAST_10_UNFRAMED(_pd, _t_lat, _lat, _sdu, _rtn) \ LC3_QOS_UCAST(LC3_QOS_UNFRAMED, _pd, _t_lat, 10000u, _lat, _sdu, _rtn) #define LC3_QOS_UCAST_FRAMED(_pd, _t_lat, _interval, _lat, _sdu, _rtn) \ LC3_QOS_UCAST(LC3_QOS_FRAMED, _pd, _t_lat, _interval, _lat, _sdu, _rtn) #define LC3_QOS_8_1_1_LATENCY 8u #define LC3_QOS_8_1_1_RTN 2u #define LC3_QOS_8_1_1_AC(_ac) \ LC3_QOS_UCAST_7_5_UNFRAMED(40000u, BT_BAP_CONFIG_LATENCY_BALANCED, \ LC3_QOS_8_1_1_LATENCY, \ (_ac) * LC3_CONFIG_FRAME_LEN_8_1, \ LC3_QOS_8_1_1_RTN) #define LC3_QOS_8_1_1 LC3_QOS_8_1_1_AC(1) #define LC3_QOS_8_1_2_LATENCY 75u #define LC3_QOS_8_1_2_RTN 13u #define LC3_QOS_8_1_2_AC(_ac) \ LC3_QOS_UCAST_7_5_UNFRAMED(40000u, BT_BAP_CONFIG_LATENCY_BALANCED, \ LC3_QOS_8_1_2_LATENCY, \ (_ac) * LC3_CONFIG_FRAME_LEN_8_1, \ LC3_QOS_8_1_2_RTN) #define LC3_QOS_8_1_2 LC3_QOS_8_1_2_AC(1) #define LC3_QOS_8_2_1_LATENCY 10u #define LC3_QOS_8_2_1_RTN 2u #define LC3_QOS_8_2_1_AC(_ac) \ LC3_QOS_UCAST_10_UNFRAMED(40000u, BT_BAP_CONFIG_LATENCY_BALANCED, \ LC3_QOS_8_2_1_LATENCY, \ (_ac) * LC3_CONFIG_FRAME_LEN_8_2, \ LC3_QOS_8_2_1_RTN) #define LC3_QOS_8_2_1 LC3_QOS_8_2_1_AC(1) #define LC3_QOS_8_2_2_LATENCY 95u #define LC3_QOS_8_2_2_RTN 13u #define LC3_QOS_8_2_2_AC(_ac) \ LC3_QOS_UCAST_10_UNFRAMED(40000u, BT_BAP_CONFIG_LATENCY_BALANCED, \ LC3_QOS_8_2_2_LATENCY, \ (_ac) * LC3_CONFIG_FRAME_LEN_8_2, \ LC3_QOS_8_2_2_RTN) #define LC3_QOS_8_2_2 LC3_QOS_8_2_2_AC(1) #define LC3_QOS_16_1_1_LATENCY 8u #define LC3_QOS_16_1_1_RTN 2u #define LC3_QOS_16_1_1_AC(_ac) \ LC3_QOS_UCAST_7_5_UNFRAMED(40000u, BT_BAP_CONFIG_LATENCY_BALANCED, \ LC3_QOS_16_1_1_LATENCY, \ (_ac) * LC3_CONFIG_FRAME_LEN_16_1, \ LC3_QOS_16_1_1_RTN) #define LC3_QOS_16_1_1 LC3_QOS_16_1_1_AC(1) #define LC3_QOS_16_1_2_LATENCY 75u #define LC3_QOS_16_1_2_RTN 13u #define LC3_QOS_16_1_2_AC(_ac) \ LC3_QOS_UCAST_7_5_UNFRAMED(40000u, BT_BAP_CONFIG_LATENCY_BALANCED, \ LC3_QOS_16_1_2_LATENCY, \ (_ac) * LC3_CONFIG_FRAME_LEN_16_1, \ LC3_QOS_16_1_2_RTN) #define LC3_QOS_16_1_2 LC3_QOS_16_1_2_AC(1) #define LC3_QOS_16_1_GS_LATENCY 15u #define LC3_QOS_16_1_GS_RTN 1u #define LC3_QOS_16_1_GS \ LC3_QOS_UCAST_7_5_UNFRAMED(60000u, BT_BAP_CONFIG_LATENCY_BALANCED, \ LC3_QOS_16_1_GS_LATENCY, \ LC3_CONFIG_FRAME_LEN_16_1, \ LC3_QOS_16_1_GS_RTN) #define LC3_QOS_16_2_1_LATENCY 10u #define LC3_QOS_16_2_1_RTN 2u #define LC3_QOS_16_2_1_AC(_ac) \ LC3_QOS_UCAST_10_UNFRAMED(40000u, BT_BAP_CONFIG_LATENCY_BALANCED, \ LC3_QOS_16_2_1_LATENCY, \ (_ac) * LC3_CONFIG_FRAME_LEN_16_2, \ LC3_QOS_16_2_1_RTN) #define LC3_QOS_16_2_1 LC3_QOS_16_2_1_AC(1) #define LC3_QOS_16_2_2_LATENCY 95u #define LC3_QOS_16_2_2_RTN 13u #define LC3_QOS_16_2_2_AC(_ac) \ LC3_QOS_UCAST_10_UNFRAMED(40000u, BT_BAP_CONFIG_LATENCY_BALANCED, \ LC3_QOS_16_2_2_LATENCY, \ (_ac) * LC3_CONFIG_FRAME_LEN_16_2, \ LC3_QOS_16_2_2_RTN) #define LC3_QOS_16_2_2 LC3_QOS_16_2_2_AC(1) #define LC3_QOS_16_2_GS_LATENCY 20u #define LC3_QOS_16_2_GS_RTN 1u #define LC3_QOS_16_2_GS \ LC3_QOS_UCAST_10_UNFRAMED(60000u, BT_BAP_CONFIG_LATENCY_BALANCED, \ LC3_QOS_16_2_GS_LATENCY, \ LC3_CONFIG_FRAME_LEN_16_2, \ LC3_QOS_16_2_GS_RTN) #define LC3_QOS_24_1_1_LATENCY 8u #define LC3_QOS_24_1_1_RTN 2u #define LC3_QOS_24_1_1_AC(_ac) \ LC3_QOS_UCAST_7_5_UNFRAMED(40000u, BT_BAP_CONFIG_LATENCY_BALANCED, \ LC3_QOS_24_1_1_LATENCY, \ (_ac) * LC3_CONFIG_FRAME_LEN_24_1, \ LC3_QOS_24_1_1_RTN) #define LC3_QOS_24_1_1 LC3_QOS_24_1_1_AC(1) #define LC3_QOS_24_1_2_LATENCY 75u #define LC3_QOS_24_1_2_RTN 13u #define LC3_QOS_24_1_2_AC(_ac) \ LC3_QOS_UCAST_7_5_UNFRAMED(40000u, BT_BAP_CONFIG_LATENCY_BALANCED, \ LC3_QOS_24_1_2_LATENCY, \ (_ac) * LC3_CONFIG_FRAME_LEN_24_1, \ LC3_QOS_24_1_2_RTN) #define LC3_QOS_24_1_2 LC3_QOS_24_1_2_AC(1) #define LC3_QOS_24_2_1_LATENCY 10u #define LC3_QOS_24_2_1_RTN 2u #define LC3_QOS_24_2_1_AC(_ac) \ LC3_QOS_UCAST_10_UNFRAMED(40000u, BT_BAP_CONFIG_LATENCY_BALANCED, \ LC3_QOS_24_2_1_LATENCY, \ (_ac) * LC3_CONFIG_FRAME_LEN_24_2, \ LC3_QOS_24_2_1_RTN) #define LC3_QOS_24_2_1 LC3_QOS_24_2_1_AC(1) #define LC3_QOS_24_2_2_LATENCY 95u #define LC3_QOS_24_2_2_RTN 13u #define LC3_QOS_24_2_2_AC(_ac) \ LC3_QOS_UCAST_10_UNFRAMED(40000u, BT_BAP_CONFIG_LATENCY_BALANCED, \ LC3_QOS_24_2_2_LATENCY, \ (_ac) * LC3_CONFIG_FRAME_LEN_24_2, \ LC3_QOS_24_2_2_RTN) #define LC3_QOS_24_2_2 LC3_QOS_24_2_2_AC(1) #define LC3_QOS_32_1_1_LATENCY 8u #define LC3_QOS_32_1_1_RTN 2u #define LC3_QOS_32_1_1_AC(_ac) \ LC3_QOS_UCAST_7_5_UNFRAMED(40000u, BT_BAP_CONFIG_LATENCY_BALANCED, \ LC3_QOS_32_1_1_LATENCY, \ (_ac) * LC3_CONFIG_FRAME_LEN_32_1, \ LC3_QOS_32_1_1_RTN) #define LC3_QOS_32_1_1 LC3_QOS_32_1_1_AC(1) #define LC3_QOS_32_1_2_LATENCY 75u #define LC3_QOS_32_1_2_RTN 13u #define LC3_QOS_32_1_2_AC(_ac) \ LC3_QOS_UCAST_7_5_UNFRAMED(40000u, BT_BAP_CONFIG_LATENCY_BALANCED, \ LC3_QOS_32_1_2_LATENCY, \ (_ac) * LC3_CONFIG_FRAME_LEN_32_1, \ LC3_QOS_32_1_2_RTN) #define LC3_QOS_32_1_2 LC3_QOS_32_1_2_AC(1) #define LC3_QOS_32_1_GS_LATENCY 15u #define LC3_QOS_32_1_GS_RTN 1u #define LC3_QOS_32_1_GS \ LC3_QOS_UCAST_7_5_UNFRAMED(40000u, BT_BAP_CONFIG_LATENCY_BALANCED, \ LC3_QOS_32_1_GS_LATENCY, \ LC3_CONFIG_FRAME_LEN_32_1, \ LC3_QOS_32_1_GS_RTN) #define LC3_QOS_32_1_GR_LATENCY 15u #define LC3_QOS_32_1_GR_RTN 1u #define LC3_QOS_32_1_GR_AC(_ac) \ LC3_QOS_UCAST_7_5_UNFRAMED(10000u, BT_BAP_CONFIG_LATENCY_LOW, \ LC3_QOS_32_1_GS_LATENCY, \ (_ac) * LC3_CONFIG_FRAME_LEN_32_1, \ LC3_QOS_32_1_GS_RTN) #define LC3_QOS_32_1_GR LC3_QOS_32_1_GR_AC(1) #define LC3_QOS_32_2_1_LATENCY 10u #define LC3_QOS_32_2_1_RTN 2u #define LC3_QOS_32_2_1_AC(_ac) \ LC3_QOS_UCAST_10_UNFRAMED(40000u, BT_BAP_CONFIG_LATENCY_BALANCED, \ LC3_QOS_32_2_1_LATENCY, \ (_ac) * LC3_CONFIG_FRAME_LEN_32_2, \ LC3_QOS_32_2_1_RTN) #define LC3_QOS_32_2_1 LC3_QOS_32_2_1_AC(1) #define LC3_QOS_32_2_2_LATENCY 95u #define LC3_QOS_32_2_2_RTN 13u #define LC3_QOS_32_2_2_AC(_ac) \ LC3_QOS_UCAST_10_UNFRAMED(40000u, BT_BAP_CONFIG_LATENCY_BALANCED, \ LC3_QOS_32_2_2_LATENCY, \ (_ac) * LC3_CONFIG_FRAME_LEN_32_2, \ LC3_QOS_32_2_2_RTN) #define LC3_QOS_32_2_2 LC3_QOS_32_2_2_AC(1) #define LC3_QOS_32_2_GS_LATENCY 20u #define LC3_QOS_32_2_GS_RTN 1u #define LC3_QOS_32_2_GS \ LC3_QOS_UCAST_10_UNFRAMED(60000u, BT_BAP_CONFIG_LATENCY_BALANCED, \ LC3_QOS_32_2_GS_LATENCY, \ LC3_CONFIG_FRAME_LEN_32_2, \ LC3_QOS_32_2_GS_RTN) #define LC3_QOS_32_2_GR_LATENCY 20u #define LC3_QOS_32_2_GR_RTN 1u #define LC3_QOS_32_2_GR_AC(_ac) \ LC3_QOS_UCAST_10_UNFRAMED(10000u, BT_BAP_CONFIG_LATENCY_LOW, \ LC3_QOS_32_2_GR_LATENCY, \ (_ac) * LC3_CONFIG_FRAME_LEN_32_2, \ LC3_QOS_32_2_GR_RTN) #define LC3_QOS_32_2_GR LC3_QOS_32_2_GR_AC(1) #define LC3_QOS_44_1_INTERVAL 8163u #define LC3_QOS_44_1_1_LATENCY 24u #define LC3_QOS_44_1_1_RTN 5u #define LC3_QOS_44_1_1_AC(_ac) \ LC3_QOS_UCAST_FRAMED(40000u, BT_BAP_CONFIG_LATENCY_BALANCED, \ LC3_QOS_44_1_INTERVAL, \ LC3_QOS_44_1_1_LATENCY, \ (_ac) * LC3_CONFIG_FRAME_LEN_44_1, \ LC3_QOS_44_1_1_RTN) #define LC3_QOS_44_1_1 LC3_QOS_44_1_1_AC(1) #define LC3_QOS_44_1_2_LATENCY 80u #define LC3_QOS_44_1_2_RTN 13u #define LC3_QOS_44_1_2_AC(_ac) \ LC3_QOS_UCAST_FRAMED(40000u, BT_BAP_CONFIG_LATENCY_BALANCED, \ LC3_QOS_44_1_INTERVAL, \ LC3_QOS_44_1_2_LATENCY, \ (_ac) * LC3_CONFIG_FRAME_LEN_44_1, \ LC3_QOS_44_1_2_RTN) #define LC3_QOS_44_1_2 LC3_QOS_44_1_2_AC(1) #define LC3_QOS_44_2_INTERVAL 10884u #define LC3_QOS_44_2_1_LATENCY 31u #define LC3_QOS_44_2_1_RTN 5u #define LC3_QOS_44_2_1_AC(_ac) \ LC3_QOS_UCAST_FRAMED(40000u, BT_BAP_CONFIG_LATENCY_BALANCED, \ LC3_QOS_44_2_INTERVAL, \ LC3_QOS_44_2_1_LATENCY, \ (_ac) * LC3_CONFIG_FRAME_LEN_44_2, \ LC3_QOS_44_2_1_RTN) #define LC3_QOS_44_2_1 LC3_QOS_44_2_1_AC(1) #define LC3_QOS_44_2_2_LATENCY 85u #define LC3_QOS_44_2_2_RTN 13u #define LC3_QOS_44_2_2_AC(_ac) \ LC3_QOS_UCAST_FRAMED(40000u, BT_BAP_CONFIG_LATENCY_BALANCED, \ LC3_QOS_44_2_INTERVAL, \ LC3_QOS_44_2_2_LATENCY, \ (_ac) * LC3_CONFIG_FRAME_LEN_44_2, \ LC3_QOS_44_2_2_RTN) #define LC3_QOS_44_2_2 LC3_QOS_44_2_2_AC(1) #define LC3_QOS_48_1_1_LATENCY 15u #define LC3_QOS_48_1_1_RTN 5u #define LC3_QOS_48_1_1_AC(_ac) \ LC3_QOS_UCAST_7_5_UNFRAMED(40000u, BT_BAP_CONFIG_LATENCY_BALANCED, \ LC3_QOS_48_1_1_LATENCY, \ (_ac) * LC3_CONFIG_FRAME_LEN_48_1,\ LC3_QOS_48_1_1_RTN) #define LC3_QOS_48_1_1 LC3_QOS_48_1_1_AC(1) #define LC3_QOS_48_1_2_LATENCY 75u #define LC3_QOS_48_1_2_RTN 13u #define LC3_QOS_48_1_2_AC(_ac) \ LC3_QOS_UCAST_7_5_UNFRAMED(40000u, BT_BAP_CONFIG_LATENCY_BALANCED, \ LC3_QOS_48_1_2_LATENCY, \ (_ac) * LC3_CONFIG_FRAME_LEN_48_1, \ LC3_QOS_48_1_2_RTN) #define LC3_QOS_48_1_2 LC3_QOS_48_1_2_AC(1) #define LC3_QOS_48_1_GS_LATENCY 15u #define LC3_QOS_48_1_GS_RTN 1u #define LC3_QOS_48_1_GS \ LC3_QOS_UCAST_7_5_UNFRAMED(60000u, BT_BAP_CONFIG_LATENCY_BALANCED, \ LC3_QOS_48_1_GS_LATENCY, \ LC3_CONFIG_FRAME_LEN_48_1, \ LC3_QOS_48_1_GS_RTN) #define LC3_QOS_48_1_GR_LATENCY 15u #define LC3_QOS_48_1_GR_RTN 1u #define LC3_QOS_48_1_GR_AC(_ac) \ LC3_QOS_UCAST_7_5_UNFRAMED(10000u, BT_BAP_CONFIG_LATENCY_LOW, \ LC3_QOS_48_1_GR_LATENCY, \ (_ac) * LC3_CONFIG_FRAME_LEN_48_1, \ LC3_QOS_48_1_GR_RTN) #define LC3_QOS_48_1_GR LC3_QOS_48_1_GR_AC(1) #define LC3_QOS_48_2_1_LATENCY 20u #define LC3_QOS_48_2_1_RTN 5u #define LC3_QOS_48_2_1_AC(_ac) \ LC3_QOS_UCAST_10_UNFRAMED(40000u, BT_BAP_CONFIG_LATENCY_BALANCED, \ LC3_QOS_48_2_1_LATENCY, \ (_ac) * LC3_CONFIG_FRAME_LEN_48_2, \ LC3_QOS_48_2_1_RTN) #define LC3_QOS_48_2_1 LC3_QOS_48_2_1_AC(1) #define LC3_QOS_48_2_2_LATENCY 95u #define LC3_QOS_48_2_2_RTN 13u #define LC3_QOS_48_2_2_AC(_ac) \ LC3_QOS_UCAST_10_UNFRAMED(40000u, BT_BAP_CONFIG_LATENCY_BALANCED, \ LC3_QOS_48_2_2_LATENCY, \ (_ac) * LC3_CONFIG_FRAME_LEN_48_2, \ LC3_QOS_48_2_2_RTN) #define LC3_QOS_48_2_2 LC3_QOS_48_2_2_AC(1) #define LC3_QOS_48_2_GS_LATENCY 20u #define LC3_QOS_48_2_GS_RTN 1u #define LC3_QOS_48_2_GS \ LC3_QOS_UCAST_10_UNFRAMED(60000u, BT_BAP_CONFIG_LATENCY_BALANCED, \ LC3_QOS_48_2_GS_LATENCY, \ LC3_CONFIG_FRAME_LEN_48_2, \ LC3_QOS_48_2_GS_RTN) #define LC3_QOS_48_2_GR_LATENCY 20u #define LC3_QOS_48_2_GR_RTN 1u #define LC3_QOS_48_2_GR_AC(_ac) \ LC3_QOS_UCAST_10_UNFRAMED(10000u, BT_BAP_CONFIG_LATENCY_LOW, \ LC3_QOS_48_2_GR_LATENCY, \ (_ac) * LC3_CONFIG_FRAME_LEN_48_2, \ LC3_QOS_48_2_GR_RTN) #define LC3_QOS_48_2_GR LC3_QOS_48_2_GR_AC(1) #define LC3_QOS_48_3_1_LATENCY 15u #define LC3_QOS_48_3_1_RTN 5u #define LC3_QOS_48_3_1_AC(_ac) \ LC3_QOS_UCAST_7_5_UNFRAMED(40000u, BT_BAP_CONFIG_LATENCY_BALANCED, \ LC3_QOS_48_3_1_LATENCY, \ (_ac) * LC3_CONFIG_FRAME_LEN_48_3, \ LC3_QOS_48_3_1_RTN) #define LC3_QOS_48_3_1 LC3_QOS_48_3_1_AC(1) #define LC3_QOS_48_3_2_LATENCY 75u #define LC3_QOS_48_3_2_RTN 13u #define LC3_QOS_48_3_2_AC(_ac) \ LC3_QOS_UCAST_7_5_UNFRAMED(40000u, BT_BAP_CONFIG_LATENCY_BALANCED, \ LC3_QOS_48_3_2_LATENCY, \ (_ac) * LC3_CONFIG_FRAME_LEN_48_3, \ LC3_QOS_48_3_2_RTN) #define LC3_QOS_48_3_2 LC3_QOS_48_3_2_AC(1) #define LC3_QOS_48_3_GR_LATENCY 15u #define LC3_QOS_48_3_GR_RTN 1u #define LC3_QOS_48_3_GR_AC(_ac) \ LC3_QOS_UCAST_7_5_UNFRAMED(10000u, BT_BAP_CONFIG_LATENCY_LOW, \ LC3_QOS_48_3_GR_LATENCY, \ (_ac) * LC3_CONFIG_FRAME_LEN_48_3, \ LC3_QOS_48_3_GR_RTN) #define LC3_QOS_48_3_GR LC3_QOS_48_3_GR_AC(1) #define LC3_QOS_48_4_1_LATENCY 20u #define LC3_QOS_48_4_1_RTN 5u #define LC3_QOS_48_4_1_AC(_ac) \ LC3_QOS_UCAST_10_UNFRAMED(40000u, BT_BAP_CONFIG_LATENCY_BALANCED, \ LC3_QOS_48_4_1_LATENCY, \ (_ac) * LC3_CONFIG_FRAME_LEN_48_4, \ LC3_QOS_48_4_1_RTN) #define LC3_QOS_48_4_1 LC3_QOS_48_4_1_AC(1) #define LC3_QOS_48_4_2_LATENCY 100u #define LC3_QOS_48_4_2_RTN 13u #define LC3_QOS_48_4_2_AC(_ac) \ LC3_QOS_UCAST_10_UNFRAMED(40000u, BT_BAP_CONFIG_LATENCY_BALANCED, \ LC3_QOS_48_4_2_LATENCY, \ (_ac) * LC3_CONFIG_FRAME_LEN_48_4, \ LC3_QOS_48_4_2_RTN) #define LC3_QOS_48_4_2 LC3_QOS_48_4_2_AC(1) #define LC3_QOS_48_4_GR_LATENCY 20u #define LC3_QOS_48_4_GR_RTN 1u #define LC3_QOS_48_4_GR_AC(_ac) \ LC3_QOS_UCAST_10_UNFRAMED(10000u, BT_BAP_CONFIG_LATENCY_LOW, \ LC3_QOS_48_4_GR_LATENCY, \ (_ac) * LC3_CONFIG_FRAME_LEN_48_4, \ LC3_QOS_48_4_GR_RTN) #define LC3_QOS_48_4_GR LC3_QOS_48_4_GR_AC(1) #define LC3_QOS_48_5_1_LATENCY 15u #define LC3_QOS_48_5_1_RTN 5u #define LC3_QOS_48_5_1_AC(_ac) \ LC3_QOS_UCAST_7_5_UNFRAMED(40000u, BT_BAP_CONFIG_LATENCY_BALANCED, \ LC3_QOS_48_5_1_LATENCY, \ (_ac) * LC3_CONFIG_FRAME_LEN_48_5, \ LC3_QOS_48_5_1_RTN) #define LC3_QOS_48_5_1 LC3_QOS_48_5_1_AC(1) #define LC3_QOS_48_5_2_LATENCY 75u #define LC3_QOS_48_5_2_RTN 13u #define LC3_QOS_48_5_2_AC(_ac) \ LC3_QOS_UCAST_7_5_UNFRAMED(40000u, BT_BAP_CONFIG_LATENCY_BALANCED, \ LC3_QOS_48_5_2_LATENCY, \ (_ac) * LC3_CONFIG_FRAME_LEN_48_5, \ LC3_QOS_48_5_2_RTN) #define LC3_QOS_48_5_2 LC3_QOS_48_5_2_AC(1) #define LC3_QOS_48_6_1_LATENCY 20u #define LC3_QOS_48_6_1_RTN 5u #define LC3_QOS_48_6_1_AC(_ac) \ LC3_QOS_UCAST_10_UNFRAMED(40000u, BT_BAP_CONFIG_LATENCY_BALANCED, \ LC3_QOS_48_6_1_LATENCY, \ (_ac) * LC3_CONFIG_FRAME_LEN_48_6, \ LC3_QOS_48_6_1_RTN) #define LC3_QOS_48_6_1 LC3_QOS_48_6_1_AC(1) #define LC3_QOS_48_6_2_LATENCY 100u #define LC3_QOS_48_6_2_RTN 13u #define LC3_QOS_48_6_2_AC(_ac) \ LC3_QOS_UCAST_10_UNFRAMED(40000u, BT_BAP_CONFIG_LATENCY_BALANCED, \ LC3_QOS_48_6_2_LATENCY, \ (_ac) * LC3_CONFIG_FRAME_LEN_48_6, \ LC3_QOS_48_6_2_RTN) #define LC3_QOS_48_6_2 LC3_QOS_48_6_2_AC(1) #define LC3_QOS_BCAST(_frame, _pd, _interval, _lat, _sdu, _rtn) \ { \ .bcast.big = 0x00, \ .bcast.bis = 0x00, \ .bcast.framing = _frame, \ .bcast.delay = _pd, \ .bcast.io_qos.interval = _interval, \ .bcast.io_qos.latency = _lat, \ .bcast.io_qos.sdu = _sdu, \ .bcast.io_qos.phy = BT_BAP_CONFIG_PHY_2M, \ .bcast.io_qos.rtn = _rtn, \ } #define LC3_QOS_BCAST_7_5_UNFRAMED(_pd, _lat, _sdu, _rtn) \ LC3_QOS_BCAST(LC3_QOS_UNFRAMED, _pd, 7500u, _lat, _sdu, _rtn) #define LC3_QOS_BCAST_10_UNFRAMED(_pd, _lat, _sdu, _rtn) \ LC3_QOS_BCAST(LC3_QOS_UNFRAMED, _pd, 10000u, _lat, _sdu, _rtn) #define LC3_QOS_BCAST_FRAMED(_pd, _interval, _lat, _sdu, _rtn) \ LC3_QOS_BCAST(LC3_QOS_FRAMED, _pd, _interval, _lat, _sdu, _rtn) #define LC3_QOS_8_1_1_B_AC(_ac) \ LC3_QOS_BCAST_7_5_UNFRAMED(40000u, LC3_QOS_8_1_1_LATENCY, \ (_ac) * LC3_CONFIG_FRAME_LEN_8_1, \ LC3_QOS_8_1_1_RTN) #define LC3_QOS_8_1_1_B LC3_QOS_8_1_1_B_AC(1) #define LC3_QOS_8_1_2_B_LATENCY 75u #define LC3_QOS_8_1_2_B_RTN 4u #define LC3_QOS_8_1_2_B_AC(_ac) \ LC3_QOS_BCAST_7_5_UNFRAMED(40000u, LC3_QOS_8_1_2_B_LATENCY, \ (_ac) * LC3_CONFIG_FRAME_LEN_8_1, \ LC3_QOS_8_1_2_B_RTN) #define LC3_QOS_8_1_2_B LC3_QOS_8_1_2_B_AC(1) #define LC3_QOS_8_2_1_B_AC(_ac) \ LC3_QOS_BCAST_10_UNFRAMED(40000u, LC3_QOS_8_2_1_LATENCY, \ (_ac) * LC3_CONFIG_FRAME_LEN_8_2, \ LC3_QOS_8_2_1_RTN) #define LC3_QOS_8_2_1_B LC3_QOS_8_2_1_B_AC(1) #define LC3_QOS_8_2_2_B_LATENCY 60u #define LC3_QOS_8_2_2_B_RTN 4u #define LC3_QOS_8_2_2_B_AC(_ac) \ LC3_QOS_BCAST_10_UNFRAMED(40000u, LC3_QOS_8_2_2_B_LATENCY, \ (_ac) * LC3_CONFIG_FRAME_LEN_8_2, \ LC3_QOS_8_2_2_B_RTN) #define LC3_QOS_8_2_2_B LC3_QOS_8_2_2_B_AC(1) #define LC3_QOS_16_1_1_B_AC(_ac) \ LC3_QOS_BCAST_7_5_UNFRAMED(40000u, LC3_QOS_16_1_1_LATENCY, \ (_ac) * LC3_CONFIG_FRAME_LEN_16_1, \ LC3_QOS_16_1_1_RTN) #define LC3_QOS_16_1_1_B LC3_QOS_16_1_1_B_AC(1) #define LC3_QOS_16_1_2_B_LATENCY 45u #define LC3_QOS_16_1_2_B_RTN 4u #define LC3_QOS_16_1_2_B_AC(_ac) \ LC3_QOS_BCAST_7_5_UNFRAMED(40000u, LC3_QOS_16_1_2_B_LATENCY, \ (_ac) * LC3_CONFIG_FRAME_LEN_16_1, \ LC3_QOS_16_1_2_B_RTN) #define LC3_QOS_16_1_2_B LC3_QOS_16_1_2_B_AC(1) #define LC3_QOS_16_2_1_B_AC(_ac) \ LC3_QOS_BCAST_10_UNFRAMED(40000u, LC3_QOS_16_2_1_LATENCY, \ (_ac) * LC3_CONFIG_FRAME_LEN_16_2, \ LC3_QOS_16_2_1_RTN) #define LC3_QOS_16_2_1_B LC3_QOS_16_2_1_B_AC(1) #define LC3_QOS_16_2_2_B_LATENCY 60u #define LC3_QOS_16_2_2_B_RTN 4u #define LC3_QOS_16_2_2_B_AC(_ac) \ LC3_QOS_BCAST_10_UNFRAMED(40000u, LC3_QOS_16_2_2_B_LATENCY, \ (_ac) * LC3_CONFIG_FRAME_LEN_16_2, \ LC3_QOS_16_2_2_B_RTN) #define LC3_QOS_16_2_2_B LC3_QOS_16_2_2_B_AC(1) #define LC3_QOS_24_1_1_B_AC(_ac) \ LC3_QOS_BCAST_7_5_UNFRAMED(40000u, LC3_QOS_24_1_1_LATENCY, \ (_ac) * LC3_CONFIG_FRAME_LEN_24_1, \ LC3_QOS_24_1_1_RTN) #define LC3_QOS_24_1_1_B LC3_QOS_24_1_1_B_AC(1) #define LC3_QOS_24_1_2_B_LATENCY 45u #define LC3_QOS_24_1_2_B_RTN 4u #define LC3_QOS_24_1_2_B_AC(_ac) \ LC3_QOS_BCAST_7_5_UNFRAMED(40000u, LC3_QOS_24_1_2_B_LATENCY, \ (_ac) * LC3_CONFIG_FRAME_LEN_24_1, \ LC3_QOS_24_1_2_B_RTN) #define LC3_QOS_24_1_2_B LC3_QOS_24_1_2_B_AC(1) #define LC3_QOS_24_2_1_B_AC(_ac) \ LC3_QOS_BCAST_10_UNFRAMED(40000u, LC3_QOS_24_2_1_LATENCY, \ (_ac) * LC3_CONFIG_FRAME_LEN_24_2, \ LC3_QOS_24_2_1_RTN) #define LC3_QOS_24_2_1_B LC3_QOS_24_2_1_B_AC(1) #define LC3_QOS_24_2_2_B_LATENCY 60u #define LC3_QOS_24_2_2_B_RTN 4u #define LC3_QOS_24_2_2_B_AC(_ac) \ LC3_QOS_BCAST_10_UNFRAMED(40000u, LC3_QOS_24_2_2_B_LATENCY, \ (_ac) * LC3_CONFIG_FRAME_LEN_24_2, \ LC3_QOS_24_2_2_B_RTN) #define LC3_QOS_24_2_2_B LC3_QOS_24_2_2_B_AC(1) #define LC3_QOS_32_1_1_B_AC(_ac) \ LC3_QOS_BCAST_7_5_UNFRAMED(40000u, LC3_QOS_32_1_1_LATENCY, \ LC3_CONFIG_FRAME_LEN_32_1, \ LC3_QOS_32_1_1_RTN) #define LC3_QOS_32_1_1_B LC3_QOS_32_1_1_B_AC(1) #define LC3_QOS_32_1_2_B_LATENCY 45u #define LC3_QOS_32_1_2_B_RTN 4u #define LC3_QOS_32_1_2_B_AC(_ac) \ LC3_QOS_BCAST_7_5_UNFRAMED(40000u, LC3_QOS_32_1_2_B_LATENCY, \ LC3_CONFIG_FRAME_LEN_32_1, \ LC3_QOS_32_1_2_B_RTN) #define LC3_QOS_32_1_2_B LC3_QOS_32_1_2_B_AC(1) #define LC3_QOS_32_2_1_B_AC(_ac) \ LC3_QOS_BCAST_10_UNFRAMED(40000u, LC3_QOS_32_2_1_LATENCY, \ (_ac) * LC3_CONFIG_FRAME_LEN_32_2, \ LC3_QOS_32_2_1_RTN) #define LC3_QOS_32_2_1_B LC3_QOS_32_2_1_B_AC(1) #define LC3_QOS_32_2_2_B_LATENCY 60u #define LC3_QOS_32_2_2_B_RTN 4u #define LC3_QOS_32_2_2_B_AC(_ac) \ LC3_QOS_BCAST_10_UNFRAMED(40000u, LC3_QOS_32_2_2_B_LATENCY, \ (_ac) * LC3_CONFIG_FRAME_LEN_32_2, \ LC3_QOS_32_2_2_B_RTN) #define LC3_QOS_32_2_2_B LC3_QOS_32_2_2_B_AC(1) #define LC3_QOS_44_1_1_B_RTN 4u #define LC3_QOS_44_1_1_B_AC(_ac) \ LC3_QOS_BCAST_FRAMED(40000u, LC3_QOS_44_1_INTERVAL, \ LC3_QOS_44_1_1_LATENCY, \ (_ac) * LC3_CONFIG_FRAME_LEN_44_1, \ LC3_QOS_44_1_1_B_RTN) #define LC3_QOS_44_1_1_B LC3_QOS_44_1_1_B_AC(1) #define LC3_QOS_44_1_2_B_LATENCY 54u #define LC3_QOS_44_1_2_B_RTN 4u #define LC3_QOS_44_1_2_B_AC(_ac) \ LC3_QOS_BCAST_FRAMED(40000u, LC3_QOS_44_1_INTERVAL, \ LC3_QOS_44_1_2_B_LATENCY, \ (_ac) * LC3_CONFIG_FRAME_LEN_44_1, \ LC3_QOS_44_1_2_B_RTN) #define LC3_QOS_44_1_2_B LC3_QOS_44_1_2_B_AC(1) #define LC3_QOS_44_2_1_B_RTN 4u #define LC3_QOS_44_2_1_B_AC(_ac) \ LC3_QOS_BCAST_FRAMED(40000u, LC3_QOS_44_2_INTERVAL, \ LC3_QOS_44_2_1_LATENCY, \ (_ac) * LC3_CONFIG_FRAME_LEN_44_2, \ LC3_QOS_44_2_1_B_RTN) #define LC3_QOS_44_2_1_B LC3_QOS_44_2_1_B_AC(1) #define LC3_QOS_44_2_2_B_LATENCY 60u #define LC3_QOS_44_2_2_B_RTN 4u #define LC3_QOS_44_2_2_B_AC(_ac) \ LC3_QOS_BCAST_FRAMED(40000u, LC3_QOS_44_2_INTERVAL, \ LC3_QOS_44_2_2_LATENCY, \ (_ac) * LC3_CONFIG_FRAME_LEN_44_2, \ LC3_QOS_44_2_2_RTN) #define LC3_QOS_44_2_2_B LC3_QOS_44_2_2_B_AC(1) #define LC3_QOS_48_1_1_B_RTN 4u #define LC3_QOS_48_1_1_B_AC(_ac) \ LC3_QOS_BCAST_7_5_UNFRAMED(40000u, LC3_QOS_48_1_1_LATENCY, \ (_ac) * LC3_CONFIG_FRAME_LEN_48_1,\ LC3_QOS_48_1_1_B_RTN) #define LC3_QOS_48_1_1_B LC3_QOS_48_1_1_B_AC(1) #define LC3_QOS_48_1_2_B_LATENCY 50u #define LC3_QOS_48_1_2_B_RTN 4u #define LC3_QOS_48_1_2_B_AC(_ac) \ LC3_QOS_BCAST_7_5_UNFRAMED(40000u, LC3_QOS_48_1_2_B_LATENCY, \ (_ac) * LC3_CONFIG_FRAME_LEN_48_1, \ LC3_QOS_48_1_2_B_RTN) #define LC3_QOS_48_1_2_B LC3_QOS_48_1_2_B_AC(1) #define LC3_QOS_48_2_1_B_RTN 4u #define LC3_QOS_48_2_1_B_AC(_ac) \ LC3_QOS_BCAST_10_UNFRAMED(40000u, LC3_QOS_48_2_1_LATENCY, \ (_ac) * LC3_CONFIG_FRAME_LEN_48_2, \ LC3_QOS_48_2_1_B_RTN) #define LC3_QOS_48_2_1_B LC3_QOS_48_2_1_B_AC(1) #define LC3_QOS_48_2_2_B_LATENCY 65u #define LC3_QOS_48_2_2_B_RTN 4u #define LC3_QOS_48_2_2_B_AC(_ac) \ LC3_QOS_BCAST_10_UNFRAMED(40000u, LC3_QOS_48_2_2_B_LATENCY, \ (_ac) * LC3_CONFIG_FRAME_LEN_48_2, \ LC3_QOS_48_2_2_B_RTN) #define LC3_QOS_48_2_2_B LC3_QOS_48_2_2_B_AC(1) #define LC3_QOS_48_3_1_B_RTN 4u #define LC3_QOS_48_3_1_B_AC(_ac) \ LC3_QOS_BCAST_7_5_UNFRAMED(40000u, LC3_QOS_48_3_1_LATENCY, \ (_ac) * LC3_CONFIG_FRAME_LEN_48_3, \ LC3_QOS_48_3_1_B_RTN) #define LC3_QOS_48_3_1_B LC3_QOS_48_3_1_B_AC(1) #define LC3_QOS_48_3_2_B_LATENCY 50u #define LC3_QOS_48_3_2_B_RTN 4u #define LC3_QOS_48_3_2_B_AC(_ac) \ LC3_QOS_BCAST_7_5_UNFRAMED(40000u, LC3_QOS_48_3_2_LATENCY, \ (_ac) * LC3_CONFIG_FRAME_LEN_48_3, \ LC3_QOS_48_3_2_RTN) #define LC3_QOS_48_3_2_B LC3_QOS_48_3_2_B_AC(1) #define LC3_QOS_48_4_1_B_RTN 4u #define LC3_QOS_48_4_1_B_AC(_ac) \ LC3_QOS_BCAST_10_UNFRAMED(40000u, LC3_QOS_48_4_1_LATENCY, \ (_ac) * LC3_CONFIG_FRAME_LEN_48_4, \ LC3_QOS_48_4_1_B_RTN) #define LC3_QOS_48_4_1_B LC3_QOS_48_4_1_B_AC(1) #define LC3_QOS_48_4_2_B_LATENCY 65u #define LC3_QOS_48_4_2_B_RTN 4u #define LC3_QOS_48_4_2_B_AC(_ac) \ LC3_QOS_BCAST_10_UNFRAMED(40000u, LC3_QOS_48_4_2_B_LATENCY, \ (_ac) * LC3_CONFIG_FRAME_LEN_48_4, \ LC3_QOS_48_4_2_B_RTN) #define LC3_QOS_48_4_2_B LC3_QOS_48_4_2_B_AC(1) #define LC3_QOS_48_5_1_B_RTN 4u #define LC3_QOS_48_5_1_B_AC(_ac) \ LC3_QOS_BCAST_7_5_UNFRAMED(40000u, LC3_QOS_48_5_1_LATENCY, \ (_ac) * LC3_CONFIG_FRAME_LEN_48_5, \ LC3_QOS_48_5_1_B_RTN) #define LC3_QOS_48_5_1_B LC3_QOS_48_5_1_B_AC(1) #define LC3_QOS_48_5_2_B_LATENCY 50u #define LC3_QOS_48_5_2_B_RTN 4u #define LC3_QOS_48_5_2_B_AC(_ac) \ LC3_QOS_BCAST_7_5_UNFRAMED(40000u, LC3_QOS_48_5_2_B_LATENCY, \ (_ac) * LC3_CONFIG_FRAME_LEN_48_5, \ LC3_QOS_48_5_2_B_RTN) #define LC3_QOS_48_5_2_B LC3_QOS_48_5_2_B_AC(1) #define LC3_QOS_48_6_1_B_RTN 5u #define LC3_QOS_48_6_1_B_AC(_ac) \ LC3_QOS_BCAST_10_UNFRAMED(40000u, LC3_QOS_48_6_1_LATENCY, \ (_ac) * LC3_CONFIG_FRAME_LEN_48_6, \ LC3_QOS_48_6_1_B_RTN) #define LC3_QOS_48_6_1_B LC3_QOS_48_6_1_B_AC(1) #define LC3_QOS_48_6_2_B_LATENCY 65u #define LC3_QOS_48_6_2_B_RTN 4u #define LC3_QOS_48_6_2_B_AC(_ac) \ LC3_QOS_BCAST_10_UNFRAMED(40000u, LC3_QOS_48_6_2_B_LATENCY, \ (_ac) * LC3_CONFIG_FRAME_LEN_48_6, \ LC3_QOS_48_6_2_B_RTN) #define LC3_QOS_48_6_2_B LC3_QOS_48_6_2_B_AC(1) bluez-5.82/src/shared/PaxHeaders/gatt-client.h0000644000000000000000000000005014572354773016310 xustar0020 atime=1743515888 20 ctime=1743591277 bluez-5.82/src/shared/gatt-client.h0000644000000000000000000001254214572354773015775 0ustar00rootroot/* SPDX-License-Identifier: LGPL-2.1-or-later */ /* * * BlueZ - Bluetooth protocol stack for Linux * * Copyright (C) 2014 Google Inc. * * */ #include #include #include #define BT_GATT_UUID_SIZE 16 struct bt_gatt_client; struct bt_gatt_client *bt_gatt_client_new(struct gatt_db *db, struct bt_att *att, uint16_t mtu, uint8_t features); struct bt_gatt_client *bt_gatt_client_clone(struct bt_gatt_client *client); struct bt_gatt_client *bt_gatt_client_ref(struct bt_gatt_client *client); void bt_gatt_client_unref(struct bt_gatt_client *client); typedef void (*bt_gatt_client_destroy_func_t)(void *user_data); typedef void (*bt_gatt_client_idle_callback_t)(void *user_data); typedef void (*bt_gatt_client_callback_t)(bool success, uint8_t att_ecode, void *user_data); typedef void (*bt_gatt_client_debug_func_t)(const char *str, void *user_data); typedef void (*bt_gatt_client_read_callback_t)(bool success, uint8_t att_ecode, const uint8_t *value, uint16_t length, void *user_data); typedef void (*bt_gatt_client_write_long_callback_t)(bool success, bool reliable_error, uint8_t att_ecode, void *user_data); typedef void (*bt_gatt_client_notify_callback_t)(uint16_t value_handle, const uint8_t *value, uint16_t length, void *user_data); typedef void (*bt_gatt_client_register_callback_t)(uint16_t att_ecode, void *user_data); typedef void (*bt_gatt_client_service_changed_callback_t)(uint16_t start_handle, uint16_t end_handle, void *user_data); bool bt_gatt_client_is_ready(struct bt_gatt_client *client); unsigned int bt_gatt_client_ready_register(struct bt_gatt_client *client, bt_gatt_client_callback_t callback, void *user_data, bt_gatt_client_destroy_func_t destroy); bool bt_gatt_client_ready_unregister(struct bt_gatt_client *client, unsigned int id); bool bt_gatt_client_set_service_changed(struct bt_gatt_client *client, bt_gatt_client_service_changed_callback_t callback, void *user_data, bt_gatt_client_destroy_func_t destroy); bool bt_gatt_client_set_debug(struct bt_gatt_client *client, bt_gatt_client_debug_func_t callback, void *user_data, bt_gatt_client_destroy_func_t destroy); uint16_t bt_gatt_client_get_mtu(struct bt_gatt_client *client); struct bt_att *bt_gatt_client_get_att(struct bt_gatt_client *client); struct gatt_db *bt_gatt_client_get_db(struct bt_gatt_client *client); uint8_t bt_gatt_client_get_features(struct bt_gatt_client *client); bool bt_gatt_client_cancel(struct bt_gatt_client *client, unsigned int id); bool bt_gatt_client_cancel_all(struct bt_gatt_client *client); unsigned int bt_gatt_client_read_value(struct bt_gatt_client *client, uint16_t value_handle, bt_gatt_client_read_callback_t callback, void *user_data, bt_gatt_client_destroy_func_t destroy); unsigned int bt_gatt_client_read_long_value(struct bt_gatt_client *client, uint16_t value_handle, uint16_t offset, bt_gatt_client_read_callback_t callback, void *user_data, bt_gatt_client_destroy_func_t destroy); unsigned int bt_gatt_client_read_multiple(struct bt_gatt_client *client, uint16_t *handles, uint8_t num_handles, bt_gatt_client_read_callback_t callback, void *user_data, bt_gatt_client_destroy_func_t destroy); unsigned int bt_gatt_client_write_without_response( struct bt_gatt_client *client, uint16_t value_handle, bool signed_write, const uint8_t *value, uint16_t length); unsigned int bt_gatt_client_write_value(struct bt_gatt_client *client, uint16_t value_handle, const uint8_t *value, uint16_t length, bt_gatt_client_callback_t callback, void *user_data, bt_gatt_client_destroy_func_t destroy); unsigned int bt_gatt_client_write_long_value(struct bt_gatt_client *client, bool reliable, uint16_t value_handle, uint16_t offset, const uint8_t *value, uint16_t length, bt_gatt_client_write_long_callback_t callback, void *user_data, bt_gatt_client_destroy_func_t destroy); unsigned int bt_gatt_client_prepare_write(struct bt_gatt_client *client, unsigned int id, uint16_t value_handle, uint16_t offset, const uint8_t *value, uint16_t length, bt_gatt_client_write_long_callback_t callback, void *user_data, bt_gatt_client_destroy_func_t destroy); unsigned int bt_gatt_client_write_execute(struct bt_gatt_client *client, unsigned int id, bt_gatt_client_callback_t callback, void *user_data, bt_gatt_client_destroy_func_t destroy); unsigned int bt_gatt_client_register_notify(struct bt_gatt_client *client, uint16_t chrc_value_handle, bt_gatt_client_register_callback_t callback, bt_gatt_client_notify_callback_t notify, void *user_data, bt_gatt_client_destroy_func_t destroy); bool bt_gatt_client_unregister_notify(struct bt_gatt_client *client, unsigned int id); bool bt_gatt_client_set_security(struct bt_gatt_client *client, int level); int bt_gatt_client_get_security(struct bt_gatt_client *client); unsigned int bt_gatt_client_idle_register(struct bt_gatt_client *client, bt_gatt_client_idle_callback_t callback, void *user_data, bt_gatt_client_destroy_func_t destroy); bool bt_gatt_client_idle_unregister(struct bt_gatt_client *client, unsigned int id); bool bt_gatt_client_set_retry(struct bt_gatt_client *client, unsigned int id, bool retry); bluez-5.82/src/shared/PaxHeaders/gatt-server.h0000644000000000000000000000005014015011623016310 xustar0020 atime=1743515894 20 ctime=1743591277 bluez-5.82/src/shared/gatt-server.h0000644000000000000000000000312714015011623015774 0ustar00rootroot/* SPDX-License-Identifier: LGPL-2.1-or-later */ /* * * BlueZ - Bluetooth protocol stack for Linux * * Copyright (C) 2014 Google Inc. * * */ #include struct bt_gatt_server; struct bt_gatt_server *bt_gatt_server_new(struct gatt_db *db, struct bt_att *att, uint16_t mtu, uint8_t min_enc_size); uint16_t bt_gatt_server_get_mtu(struct bt_gatt_server *server); struct bt_att *bt_gatt_server_get_att(struct bt_gatt_server *server); struct bt_gatt_server *bt_gatt_server_ref(struct bt_gatt_server *server); void bt_gatt_server_unref(struct bt_gatt_server *server); typedef void (*bt_gatt_server_destroy_func_t)(void *user_data); typedef void (*bt_gatt_server_debug_func_t)(const char *str, void *user_data); typedef void (*bt_gatt_server_conf_func_t)(void *user_data); bool bt_gatt_server_set_debug(struct bt_gatt_server *server, bt_gatt_server_debug_func_t callback, void *user_data, bt_gatt_server_destroy_func_t destroy); typedef uint8_t (*bt_gatt_server_authorize_cb_t)(struct bt_att *att, uint8_t opcode, uint16_t handle, void *user_data); bool bt_gatt_server_set_authorize(struct bt_gatt_server *server, bt_gatt_server_authorize_cb_t cb, void *user_data); bool bt_gatt_server_send_notification(struct bt_gatt_server *server, uint16_t handle, const uint8_t *value, uint16_t length, bool multiple); bool bt_gatt_server_send_indication(struct bt_gatt_server *server, uint16_t handle, const uint8_t *value, uint16_t length, bt_gatt_server_conf_func_t callback, void *user_data, bt_gatt_server_destroy_func_t destroy); bluez-5.82/src/shared/PaxHeaders/log.h0000644000000000000000000000005014015011623014626 xustar0020 atime=1743515903 20 ctime=1743591277 bluez-5.82/src/shared/log.h0000644000000000000000000000102214015011623014302 0ustar00rootroot/* SPDX-License-Identifier: LGPL-2.1-or-later */ /* * * BlueZ - Bluetooth protocol stack for Linux * * Copyright (C) 2018 Intel Corporation. All rights reserved. * * */ int bt_log_open(void); int bt_log_sendmsg(uint16_t index, const char *label, int level, struct iovec *io, size_t io_len); int bt_log_vprintf(uint16_t index, const char *label, int level, const char *format, va_list ap); int bt_log_printf(uint16_t index, const char *label, int level, const char *format, ...); void bt_log_close(void); bluez-5.82/src/shared/PaxHeaders/ccp.h0000644000000000000000000000005014572354773014642 xustar0020 atime=1743515933 20 ctime=1743591277 bluez-5.82/src/shared/ccp.h0000644000000000000000000000236114572354773014325 0ustar00rootroot/* SPDX-License-Identifier: LGPL-2.1-or-later */ /* * * BlueZ - Bluetooth protocol stack for Linux * * Copyright (C) 2020 Intel Corporation. All rights reserved. * */ #include #include #ifndef __packed #define __packed __attribute__((packed)) #endif struct bt_ccp; struct bt_ccp_db; struct bt_ccp_session_info; typedef void (*bt_ccp_debug_func_t)(const char *str, void *user_data); typedef void (*bt_ccp_destroy_func_t)(void *user_data); struct bt_ccp_event_callback { void (*call_state)(struct bt_ccp *ccp, const uint8_t *value, uint16_t length); }; void bt_ccp_set_event_callbacks(struct bt_ccp *ccp, const struct bt_ccp_event_callback *cbs, void *user_data); bool bt_ccp_set_debug(struct bt_ccp *ccp, bt_ccp_debug_func_t cb, void *user_data, bt_ccp_destroy_func_t destroy); void bt_ccp_register(struct gatt_db *db); bool bt_ccp_attach(struct bt_ccp *ccp, struct bt_gatt_client *client); void bt_ccp_detach(struct bt_ccp *ccp); struct bt_ccp *bt_ccp_new(struct gatt_db *ldb, struct gatt_db *rdb); struct bt_ccp *bt_ccp_ref(struct bt_ccp *ccp); void bt_ccp_unref(struct bt_ccp *ccp); bool bt_ccp_set_user_data(struct bt_ccp *ccp, void *user_data); void *bt_ccp_get_user_data(struct bt_ccp *ccp); bluez-5.82/src/shared/PaxHeaders/ad.h0000644000000000000000000000005014621503015014435 xustar0020 atime=1743515768 20 ctime=1743591277 bluez-5.82/src/shared/ad.h0000644000000000000000000001210114621503015014111 0ustar00rootroot/* SPDX-License-Identifier: LGPL-2.1-or-later */ /* * * BlueZ - Bluetooth protocol stack for Linux * * Copyright (C) 2015 Google Inc. * * */ #include #include #include "lib/bluetooth.h" #include "lib/uuid.h" #define BT_AD_MAX_DATA_LEN 31 #define BT_EA_MAX_DATA_LEN 251 #define BT_PA_MAX_DATA_LEN 252 #define BT_AD_FLAGS 0x01 #define BT_AD_UUID16_SOME 0x02 #define BT_AD_UUID16_ALL 0x03 #define BT_AD_UUID32_SOME 0x04 #define BT_AD_UUID32_ALL 0x05 #define BT_AD_UUID128_SOME 0x06 #define BT_AD_UUID128_ALL 0x07 #define BT_AD_NAME_SHORT 0x08 #define BT_AD_NAME_COMPLETE 0x09 #define BT_AD_TX_POWER 0x0a #define BT_AD_CLASS_OF_DEV 0x0d #define BT_AD_SSP_HASH 0x0e #define BT_AD_SSP_RANDOMIZER 0x0f #define BT_AD_DEVICE_ID 0x10 #define BT_AD_SMP_TK 0x10 #define BT_AD_SMP_OOB_FLAGS 0x11 #define BT_AD_PERIPHERAL_CONN_INTERVAL 0x12 #define BT_AD_SOLICIT16 0x14 #define BT_AD_SOLICIT128 0x15 #define BT_AD_SERVICE_DATA16 0x16 #define BT_AD_PUBLIC_ADDRESS 0x17 #define BT_AD_RANDOM_ADDRESS 0x18 #define BT_AD_GAP_APPEARANCE 0x19 #define BT_AD_ADVERTISING_INTERVAL 0x1a #define BT_AD_LE_DEVICE_ADDRESS 0x1b #define BT_AD_LE_ROLE 0x1c #define BT_AD_SSP_HASH_P256 0x1d #define BT_AD_SSP_RANDOMIZER_P256 0x1e #define BT_AD_SOLICIT32 0x1f #define BT_AD_SERVICE_DATA32 0x20 #define BT_AD_SERVICE_DATA128 0x21 #define BT_AD_LE_SC_CONFIRM_VALUE 0x22 #define BT_AD_LE_SC_RANDOM_VALUE 0x23 #define BT_AD_URI 0x24 #define BT_AD_INDOOR_POSITIONING 0x25 #define BT_AD_TRANSPORT_DISCOVERY 0x26 #define BT_AD_LE_SUPPORTED_FEATURES 0x27 #define BT_AD_CHANNEL_MAP_UPDATE_IND 0x28 #define BT_AD_MESH_PROV 0x29 #define BT_AD_MESH_DATA 0x2a #define BT_AD_MESH_BEACON 0x2b #define BT_AD_CSIP_RSI 0x2e #define BT_AD_3D_INFO_DATA 0x3d #define BT_AD_MANUFACTURER_DATA 0xff /* Low Energy Advertising Flags */ #define BT_AD_FLAG_LIMITED 0x01 /* Limited Discoverable */ #define BT_AD_FLAG_GENERAL 0x02 /* General Discoverable */ #define BT_AD_FLAG_NO_BREDR 0x04 /* BR/EDR not supported */ typedef void (*bt_ad_func_t)(void *data, void *user_data); struct bt_ad; struct queue; struct bt_ad_manufacturer_data { uint16_t manufacturer_id; uint8_t *data; size_t len; }; struct bt_ad_service_data { bt_uuid_t uuid; size_t len; void *data; }; struct bt_ad_data { uint8_t type; uint8_t *data; size_t len; }; struct bt_ad_pattern { uint8_t type; uint8_t offset; uint8_t len; uint8_t data[BT_AD_MAX_DATA_LEN]; }; struct bt_ad *bt_ad_new(void); bool bt_ad_set_max_len(struct bt_ad *ad, uint8_t len); struct bt_ad *bt_ad_new_with_data(size_t len, const uint8_t *data); struct bt_ad *bt_ad_ref(struct bt_ad *ad); void bt_ad_unref(struct bt_ad *ad); size_t bt_ad_length(struct bt_ad *ad); uint8_t *bt_ad_generate(struct bt_ad *ad, size_t *length); bool bt_ad_is_empty(struct bt_ad *ad); bool bt_ad_add_service_uuid(struct bt_ad *ad, const bt_uuid_t *uuid); bool bt_ad_has_service_uuid(struct bt_ad *ad, const bt_uuid_t *uuid); bool bt_ad_remove_service_uuid(struct bt_ad *ad, bt_uuid_t *uuid); void bt_ad_clear_service_uuid(struct bt_ad *ad); bool bt_ad_add_manufacturer_data(struct bt_ad *ad, uint16_t id, void *data, size_t len); bool bt_ad_has_manufacturer_data(struct bt_ad *ad, const struct bt_ad_manufacturer_data *data); void bt_ad_foreach_manufacturer_data(struct bt_ad *ad, bt_ad_func_t func, void *user_data); bool bt_ad_remove_manufacturer_data(struct bt_ad *ad, uint16_t manufacturer_id); void bt_ad_clear_manufacturer_data(struct bt_ad *ad); bool bt_ad_add_solicit_uuid(struct bt_ad *ad, const bt_uuid_t *uuid); bool bt_ad_remove_solicit_uuid(struct bt_ad *ad, bt_uuid_t *uuid); void bt_ad_clear_solicit_uuid(struct bt_ad *ad); bool bt_ad_add_service_data(struct bt_ad *ad, const bt_uuid_t *uuid, void *data, size_t len); bool bt_ad_has_service_data(struct bt_ad *ad, const struct bt_ad_service_data *data); void bt_ad_foreach_service_data(struct bt_ad *ad, bt_ad_func_t func, void *user_data); bool bt_ad_remove_service_data(struct bt_ad *ad, bt_uuid_t *uuid); void bt_ad_clear_service_data(struct bt_ad *ad); bool bt_ad_add_name(struct bt_ad *ad, const char *name); const char *bt_ad_get_name(struct bt_ad *ad); void bt_ad_clear_name(struct bt_ad *ad); bool bt_ad_add_appearance(struct bt_ad *ad, uint16_t appearance); void bt_ad_clear_appearance(struct bt_ad *ad); bool bt_ad_add_flags(struct bt_ad *ad, uint8_t *flags, size_t len); bool bt_ad_has_flags(struct bt_ad *ad); uint8_t bt_ad_get_flags(struct bt_ad *ad); void bt_ad_clear_flags(struct bt_ad *ad); bool bt_ad_add_data(struct bt_ad *ad, uint8_t type, void *data, size_t len); bool bt_ad_has_data(struct bt_ad *ad, const struct bt_ad_data *data); void bt_ad_foreach_data(struct bt_ad *ad, bt_ad_func_t func, void *user_data); bool bt_ad_remove_data(struct bt_ad *ad, uint8_t type); void bt_ad_clear_data(struct bt_ad *ad); int8_t bt_ad_get_tx_power(struct bt_ad *ad); struct bt_ad_pattern *bt_ad_pattern_new(uint8_t type, size_t offset, size_t len, const uint8_t *data); struct bt_ad_pattern *bt_ad_pattern_match(struct bt_ad *ad, struct queue *patterns); bluez-5.82/src/shared/PaxHeaders/bap.h0000644000000000000000000000005014766002272014624 xustar0020 atime=1743515578 20 ctime=1743591277 bluez-5.82/src/shared/bap.h0000644000000000000000000002445014766002272014312 0ustar00rootroot/* SPDX-License-Identifier: LGPL-2.1-or-later */ /* * * BlueZ - Bluetooth protocol stack for Linux * * Copyright (C) 2022 Intel Corporation. All rights reserved. * Copyright 2023-2025 NXP * */ #include #include #include "src/shared/bap-defs.h" struct bt_bap; struct bt_bap_pac; struct bt_bap_stream; typedef void (*bt_bap_ready_func_t)(struct bt_bap *bap, void *user_data); typedef void (*bt_bap_destroy_func_t)(void *user_data); typedef void (*bt_bap_debug_func_t)(const char *str, void *user_data); typedef void (*bt_bap_pac_func_t)(struct bt_bap_pac *pac, void *user_data); typedef bool (*bt_bap_pac_foreach_t)(struct bt_bap_pac *lpac, struct bt_bap_pac *rpac, void *user_data); typedef void (*bt_bap_pac_select_t)(struct bt_bap_pac *pac, int err, struct iovec *caps, struct iovec *metadata, struct bt_bap_qos *qos, void *user_data); typedef void (*bt_bap_pac_config_t)(struct bt_bap_stream *stream, int err); typedef void (*bt_bap_state_func_t)(struct bt_bap_stream *stream, uint8_t old_state, uint8_t new_state, void *user_data); typedef void (*bt_bap_connecting_func_t)(struct bt_bap_stream *stream, bool state, int fd, void *user_data); typedef void (*bt_bap_stream_func_t)(struct bt_bap_stream *stream, uint8_t code, uint8_t reason, void *user_data); typedef void (*bt_bap_func_t)(struct bt_bap *bap, void *user_data); typedef void (*bt_bap_bis_func_t)(uint8_t bis, uint8_t sgrp, struct iovec *caps, struct iovec *meta, struct bt_bap_qos *qos, void *user_data); typedef void (*bt_bap_bcode_reply_t)(void *user_data, int err); typedef void (*bt_bap_bcode_func_t)(struct bt_bap_stream *stream, bt_bap_bcode_reply_t reply, void *reply_data, void *user_data); extern struct bt_iso_qos bap_sink_pa_qos; /* Local PAC related functions */ struct bt_bap_pac_qos { uint8_t framing; uint8_t phy; uint8_t rtn; uint16_t latency; uint32_t pd_min; uint32_t pd_max; uint32_t ppd_min; uint32_t ppd_max; uint32_t location; uint16_t supported_context; uint16_t context; }; struct bt_bap_pac *bt_bap_add_vendor_pac(struct gatt_db *db, const char *name, uint8_t type, uint8_t id, uint16_t cid, uint16_t vid, struct bt_bap_pac_qos *qos, struct iovec *data, struct iovec *metadata); struct bt_bap_pac *bt_bap_add_pac(struct gatt_db *db, const char *name, uint8_t type, uint8_t id, struct bt_bap_pac_qos *qos, struct iovec *data, struct iovec *metadata); struct bt_bap_pac_ops { int (*select)(struct bt_bap_pac *lpac, struct bt_bap_pac *rpac, uint32_t chan_alloc, struct bt_bap_pac_qos *qos, bt_bap_pac_select_t cb, void *cb_data, void *user_data); void (*cancel_select)(struct bt_bap_pac *lpac, bt_bap_pac_select_t cb, void *cb_data, void *user_data); int (*config)(struct bt_bap_stream *stream, struct iovec *cfg, struct bt_bap_qos *qos, bt_bap_pac_config_t cb, void *user_data); void (*clear)(struct bt_bap_stream *stream, void *user_data); }; bool bt_bap_pac_set_ops(struct bt_bap_pac *pac, struct bt_bap_pac_ops *ops, void *user_data); bool bt_bap_remove_pac(struct bt_bap_pac *pac); uint8_t bt_bap_pac_get_type(struct bt_bap_pac *pac); uint32_t bt_bap_pac_get_locations(struct bt_bap_pac *pac); uint16_t bt_bap_pac_get_supported_context(struct bt_bap_pac *pac); uint16_t bt_bap_pac_get_context(struct bt_bap_pac *pac); struct bt_bap_pac_qos *bt_bap_pac_get_qos(struct bt_bap_pac *pac); struct iovec *bt_bap_pac_get_data(struct bt_bap_pac *pac); struct iovec *bt_bap_pac_get_metadata(struct bt_bap_pac *pac); uint8_t bt_bap_stream_get_type(struct bt_bap_stream *stream); struct bt_bap_stream *bt_bap_pac_get_stream(struct bt_bap_pac *pac); /* Session related function */ unsigned int bt_bap_register(bt_bap_func_t added, bt_bap_func_t removed, void *user_data); bool bt_bap_unregister(unsigned int id); struct bt_bap *bt_bap_new(struct gatt_db *ldb, struct gatt_db *rdb); bool bt_bap_set_user_data(struct bt_bap *bap, void *user_data); void *bt_bap_get_user_data(struct bt_bap *bap); struct bt_att *bt_bap_get_att(struct bt_bap *bap); struct bt_bap *bt_bap_ref(struct bt_bap *bap); void bt_bap_unref(struct bt_bap *bap); bool bt_bap_attach(struct bt_bap *bap, struct bt_gatt_client *client); bool bt_bap_attach_broadcast(struct bt_bap *bap); void bt_bap_detach(struct bt_bap *bap); bool bt_bap_set_debug(struct bt_bap *bap, bt_bap_debug_func_t cb, void *user_data, bt_bap_destroy_func_t destroy); unsigned int bt_bap_pac_register(struct bt_bap *bap, bt_bap_pac_func_t added, bt_bap_pac_func_t removed, void *user_data, bt_bap_destroy_func_t destroy); bool bt_bap_pac_unregister(struct bt_bap *bap, unsigned int id); unsigned int bt_bap_ready_register(struct bt_bap *bap, bt_bap_ready_func_t func, void *user_data, bt_bap_destroy_func_t destroy); bool bt_bap_ready_unregister(struct bt_bap *bap, unsigned int id); unsigned int bt_bap_state_register(struct bt_bap *bap, bt_bap_state_func_t func, bt_bap_connecting_func_t connecting, void *user_data, bt_bap_destroy_func_t destroy); bool bt_bap_state_unregister(struct bt_bap *bap, unsigned int id); const char *bt_bap_stream_statestr(uint8_t state); void bt_bap_foreach_pac(struct bt_bap *bap, uint8_t type, bt_bap_pac_foreach_t func, void *user_data); int bt_bap_pac_get_vendor_codec(struct bt_bap_pac *pac, uint8_t *id, uint16_t *cid, uint16_t *vid, struct iovec **data, struct iovec **metadata); int bt_bap_pac_get_codec(struct bt_bap_pac *pac, uint8_t *id, struct iovec **data, struct iovec **metadata); void bt_bap_pac_set_user_data(struct bt_bap_pac *pac, void *user_data); void *bt_bap_pac_get_user_data(struct bt_bap_pac *pac); /* Stream related functions */ int bt_bap_select(struct bt_bap_pac *lpac, struct bt_bap_pac *rpac, int *count, bt_bap_pac_select_t func, void *user_data); void bt_bap_cancel_select(struct bt_bap_pac *lpac, bt_bap_pac_select_t func, void *user_data); struct bt_bap_stream *bt_bap_stream_new(struct bt_bap *bap, struct bt_bap_pac *lpac, struct bt_bap_pac *rpac, struct bt_bap_qos *pqos, struct iovec *data); struct bt_bap *bt_bap_stream_get_session(struct bt_bap_stream *stream); uint8_t bt_bap_stream_get_state(struct bt_bap_stream *stream); bool bt_bap_stream_set_user_data(struct bt_bap_stream *stream, void *user_data); void *bt_bap_stream_get_user_data(struct bt_bap_stream *stream); unsigned int bt_bap_stream_config(struct bt_bap_stream *stream, struct bt_bap_qos *pqos, struct iovec *data, bt_bap_stream_func_t func, void *user_data); unsigned int bt_bap_stream_qos(struct bt_bap_stream *stream, struct bt_bap_qos *qos, bt_bap_stream_func_t func, void *user_data); unsigned int bt_bap_stream_enable(struct bt_bap_stream *stream, bool enable_links, struct iovec *metadata, bt_bap_stream_func_t func, void *user_data); unsigned int bt_bap_stream_start(struct bt_bap_stream *stream, bt_bap_stream_func_t func, void *user_data); unsigned int bt_bap_stream_disable(struct bt_bap_stream *stream, bool disable_links, bt_bap_stream_func_t func, void *user_data); unsigned int bt_bap_stream_stop(struct bt_bap_stream *stream, bt_bap_stream_func_t func, void *user_data); unsigned int bt_bap_stream_metadata(struct bt_bap_stream *stream, struct iovec *metadata, bt_bap_stream_func_t func, void *user_data); unsigned int bt_bap_stream_release(struct bt_bap_stream *stream, bt_bap_stream_func_t func, void *user_data); uint8_t bt_bap_stream_get_dir(struct bt_bap_stream *stream); uint32_t bt_bap_stream_get_location(struct bt_bap_stream *stream); struct iovec *bt_bap_stream_get_config(struct bt_bap_stream *stream); struct bt_bap_qos *bt_bap_stream_get_qos(struct bt_bap_stream *stream); struct iovec *bt_bap_stream_get_metadata(struct bt_bap_stream *stream); struct io *bt_bap_stream_get_io(struct bt_bap_stream *stream); bool bt_bap_match_bcast_sink_stream(const void *data, const void *user_data); bool bt_bap_stream_set_io(struct bt_bap_stream *stream, int fd); int bt_bap_stream_cancel(struct bt_bap_stream *stream, unsigned int id); int bt_bap_stream_io_link(struct bt_bap_stream *stream, struct bt_bap_stream *link); int bt_bap_stream_io_unlink(struct bt_bap_stream *stream, struct bt_bap_stream *link); struct queue *bt_bap_stream_io_get_links(struct bt_bap_stream *stream); bool bt_bap_stream_io_get_qos(struct bt_bap_stream *stream, struct bt_bap_qos **in, struct bt_bap_qos **out); uint8_t bt_bap_stream_io_dir(struct bt_bap_stream *stream); int bt_bap_stream_io_connecting(struct bt_bap_stream *stream, int fd); bool bt_bap_stream_io_is_connecting(struct bt_bap_stream *stream, int *fd); bool bt_bap_new_bcast_source(struct bt_bap *bap, const char *name); void bt_bap_update_bcast_source(struct bt_bap_pac *pac, struct bt_bap_codec *codec, struct iovec *data, struct iovec *metadata); bool bt_bap_pac_bcast_is_local(struct bt_bap *bap, struct bt_bap_pac *pac); struct iovec *bt_bap_stream_get_base(struct bt_bap_stream *stream); struct iovec *bt_bap_merge_caps(struct iovec *l2_caps, struct iovec *l3_caps); void bt_bap_verify_bis(struct bt_bap *bap, uint8_t bis_index, struct iovec *caps, struct bt_bap_pac **lpac); bool bt_bap_parse_base(struct iovec *base, struct bt_bap_qos *qos, util_debug_func_t func, bt_bap_bis_func_t handler, void *user_data); unsigned int bt_bap_bis_cb_register(struct bt_bap *bap, bt_bap_bis_func_t probe, bt_bap_func_t remove, void *user_data, bt_bap_destroy_func_t destroy); bool bt_bap_bis_cb_unregister(struct bt_bap *bap, unsigned int id); void bt_bap_bis_probe(struct bt_bap *bap, uint8_t bis, uint8_t sgrp, struct iovec *caps, struct iovec *meta, struct bt_bap_qos *qos); void bt_bap_bis_remove(struct bt_bap *bap); void bt_bap_req_bcode(struct bt_bap_stream *stream, bt_bap_bcode_reply_t reply, void *reply_data); unsigned int bt_bap_bcode_cb_register(struct bt_bap *bap, bt_bap_bcode_func_t func, void *user_data, bt_bap_destroy_func_t destroy); bool bt_bap_bcode_cb_unregister(struct bt_bap *bap, unsigned int id); struct bt_bap *bt_bap_get_session(struct bt_att *att, struct gatt_db *db); void bt_bap_iso_qos_to_bap_qos(struct bt_iso_qos *iso_qos, struct bt_bap_qos *bap_qos); void bt_bap_qos_to_iso_qos(struct bt_bap_qos *bap_qos, struct bt_iso_qos *iso_qos); bluez-5.82/src/shared/PaxHeaders/gatt-db.h0000644000000000000000000000005014643061455015406 xustar0020 atime=1743515888 20 ctime=1743591277 bluez-5.82/src/shared/gatt-db.h0000644000000000000000000002347314643061455015100 0ustar00rootroot/* SPDX-License-Identifier: LGPL-2.1-or-later */ /* * * BlueZ - Bluetooth protocol stack for Linux * * Copyright (C) 2014 Intel Corporation. All rights reserved. * * */ struct gatt_db; struct gatt_db_attribute; struct gatt_db *gatt_db_new(void); struct gatt_db *gatt_db_clone(struct gatt_db *db); struct gatt_db *gatt_db_ref(struct gatt_db *db); void gatt_db_unref(struct gatt_db *db); bool gatt_db_isempty(struct gatt_db *db); struct gatt_db_attribute *gatt_db_add_service(struct gatt_db *db, const bt_uuid_t *uuid, bool primary, uint16_t num_handles); bool gatt_db_remove_service(struct gatt_db *db, struct gatt_db_attribute *attrib); bool gatt_db_clear(struct gatt_db *db); bool gatt_db_clear_range(struct gatt_db *db, uint16_t start_handle, uint16_t end_handle); bool gatt_db_hash_support(struct gatt_db *db); uint8_t *gatt_db_get_hash(struct gatt_db *db); struct gatt_db_attribute *gatt_db_insert_service(struct gatt_db *db, uint16_t handle, const bt_uuid_t *uuid, bool primary, uint16_t num_handles); typedef void (*gatt_db_read_t) (struct gatt_db_attribute *attrib, unsigned int id, uint16_t offset, uint8_t opcode, struct bt_att *att, void *user_data); typedef void (*gatt_db_write_t) (struct gatt_db_attribute *attrib, unsigned int id, uint16_t offset, const uint8_t *value, size_t len, uint8_t opcode, struct bt_att *att, void *user_data); typedef void (*gatt_db_notify_t) (struct gatt_db_attribute *attrib, struct gatt_db_attribute *ccc, const uint8_t *value, size_t len, struct bt_att *att, void *user_data); struct gatt_db_attribute * gatt_db_service_add_characteristic(struct gatt_db_attribute *attrib, const bt_uuid_t *uuid, uint32_t permissions, uint8_t properties, gatt_db_read_t read_func, gatt_db_write_t write_func, void *user_data); struct gatt_db_attribute * gatt_db_service_insert_characteristic(struct gatt_db_attribute *attrib, uint16_t handle, uint16_t value_handle, const bt_uuid_t *uuid, uint32_t permissions, uint8_t properties, gatt_db_read_t read_func, gatt_db_write_t write_func, void *user_data); struct gatt_db_attribute * gatt_db_insert_characteristic(struct gatt_db *db, uint16_t handle, uint16_t value_handle, const bt_uuid_t *uuid, uint32_t permissions, uint8_t properties, gatt_db_read_t read_func, gatt_db_write_t write_func, void *user_data); struct gatt_db_attribute * gatt_db_insert_descriptor(struct gatt_db *db, uint16_t handle, const bt_uuid_t *uuid, uint32_t permissions, gatt_db_read_t read_func, gatt_db_write_t write_func, void *user_data); struct gatt_db_attribute * gatt_db_service_add_descriptor(struct gatt_db_attribute *attrib, const bt_uuid_t *uuid, uint32_t permissions, gatt_db_read_t read_func, gatt_db_write_t write_func, void *user_data); struct gatt_db_attribute * gatt_db_service_insert_descriptor(struct gatt_db_attribute *attrib, uint16_t handle, const bt_uuid_t *uuid, uint32_t permissions, gatt_db_read_t read_func, gatt_db_write_t write_func, void *user_data); struct gatt_db_attribute * gatt_db_service_add_ccc(struct gatt_db_attribute *attrib, uint32_t permissions); struct gatt_db_attribute * gatt_db_insert_included(struct gatt_db *db, uint16_t handle, struct gatt_db_attribute *include); struct gatt_db_attribute * gatt_db_service_add_included(struct gatt_db_attribute *attrib, struct gatt_db_attribute *include); struct gatt_db_attribute * gatt_db_service_insert_included(struct gatt_db_attribute *attrib, uint16_t handle, struct gatt_db_attribute *include); bool gatt_db_service_set_active(struct gatt_db_attribute *attrib, bool active); bool gatt_db_service_get_active(struct gatt_db_attribute *attrib); bool gatt_db_service_set_claimed(struct gatt_db_attribute *attrib, bool claimed); bool gatt_db_service_get_claimed(struct gatt_db_attribute *attrib); typedef void (*gatt_db_attribute_cb_t)(struct gatt_db_attribute *attrib, void *user_data); void gatt_db_read_by_group_type(struct gatt_db *db, uint16_t start_handle, uint16_t end_handle, const bt_uuid_t type, struct queue *queue); unsigned int gatt_db_find_by_type(struct gatt_db *db, uint16_t start_handle, uint16_t end_handle, const bt_uuid_t *type, gatt_db_attribute_cb_t func, void *user_data); unsigned int gatt_db_find_by_type_value(struct gatt_db *db, uint16_t start_handle, uint16_t end_handle, const bt_uuid_t *type, const void *value, size_t value_len, gatt_db_attribute_cb_t func, void *user_data); void gatt_db_read_by_type(struct gatt_db *db, uint16_t start_handle, uint16_t end_handle, const bt_uuid_t type, struct queue *queue); void gatt_db_find_information(struct gatt_db *db, uint16_t start_handle, uint16_t end_handle, struct queue *queue); void gatt_db_foreach_service(struct gatt_db *db, const bt_uuid_t *uuid, gatt_db_attribute_cb_t func, void *user_data); void gatt_db_foreach_in_range(struct gatt_db *db, const bt_uuid_t *uuid, gatt_db_attribute_cb_t func, void *user_data, uint16_t start_handle, uint16_t end_handle); void gatt_db_foreach_service_in_range(struct gatt_db *db, const bt_uuid_t *uuid, gatt_db_attribute_cb_t func, void *user_data, uint16_t start_handle, uint16_t end_handle); void gatt_db_service_foreach(struct gatt_db_attribute *attrib, const bt_uuid_t *uuid, gatt_db_attribute_cb_t func, void *user_data); void gatt_db_service_foreach_char(struct gatt_db_attribute *attrib, gatt_db_attribute_cb_t func, void *user_data); void gatt_db_service_foreach_desc(struct gatt_db_attribute *attrib, gatt_db_attribute_cb_t func, void *user_data); void gatt_db_service_foreach_incl(struct gatt_db_attribute *attrib, gatt_db_attribute_cb_t func, void *user_data); typedef void (*gatt_db_destroy_func_t)(void *user_data); unsigned int gatt_db_register(struct gatt_db *db, gatt_db_attribute_cb_t service_added, gatt_db_attribute_cb_t service_removed, void *user_data, gatt_db_destroy_func_t destroy); bool gatt_db_unregister(struct gatt_db *db, unsigned int id); void gatt_db_ccc_register(struct gatt_db *db, gatt_db_read_t read_func, gatt_db_write_t write_func, gatt_db_notify_t notify_func, void *user_data); typedef uint8_t (*gatt_db_authorize_cb_t)(struct gatt_db_attribute *attrib, uint8_t opcode, struct bt_att *att, void *user_data); bool gatt_db_set_authorize(struct gatt_db *db, gatt_db_authorize_cb_t cb, void *user_data); struct gatt_db_attribute *gatt_db_get_service(struct gatt_db *db, uint16_t handle); struct gatt_db_attribute *gatt_db_get_attribute(struct gatt_db *db, uint16_t handle); struct gatt_db_attribute *gatt_db_get_service_with_uuid(struct gatt_db *db, const bt_uuid_t *uuid); const bt_uuid_t *gatt_db_attribute_get_type( const struct gatt_db_attribute *attrib); uint16_t gatt_db_attribute_get_handle(const struct gatt_db_attribute *attrib); struct gatt_db_attribute * gatt_db_attribute_get_service(const struct gatt_db_attribute *attrib); bool gatt_db_attribute_get_service_uuid(const struct gatt_db_attribute *attrib, bt_uuid_t *uuid); bool gatt_db_attribute_get_service_handles( const struct gatt_db_attribute *attrib, uint16_t *start_handle, uint16_t *end_handle); bool gatt_db_attribute_get_service_data(const struct gatt_db_attribute *attrib, uint16_t *start_handle, uint16_t *end_handle, bool *primary, bt_uuid_t *uuid); bool gatt_db_attribute_get_char_data(const struct gatt_db_attribute *attrib, uint16_t *handle, uint16_t *value_handle, uint8_t *properties, uint16_t *ext_prop, bt_uuid_t *uuid); bool gatt_db_attribute_get_incl_data(const struct gatt_db_attribute *attrib, uint16_t *handle, uint16_t *start_handle, uint16_t *end_handle); uint32_t gatt_db_attribute_get_permissions(const struct gatt_db_attribute *attrib); bool gatt_db_attribute_set_fixed_length(struct gatt_db_attribute *attrib, uint16_t len); typedef void (*gatt_db_attribute_read_t) (struct gatt_db_attribute *attrib, int err, const uint8_t *value, size_t length, void *user_data); bool gatt_db_attribute_read(struct gatt_db_attribute *attrib, uint16_t offset, uint8_t opcode, struct bt_att *att, gatt_db_attribute_read_t func, void *user_data); bool gatt_db_attribute_read_result(struct gatt_db_attribute *attrib, unsigned int id, int err, const uint8_t *value, size_t length); typedef void (*gatt_db_attribute_write_t) (struct gatt_db_attribute *attrib, int err, void *user_data); bool gatt_db_attribute_write(struct gatt_db_attribute *attrib, uint16_t offset, const uint8_t *value, size_t len, uint8_t opcode, struct bt_att *att, gatt_db_attribute_write_t func, void *user_data); bool gatt_db_attribute_write_result(struct gatt_db_attribute *attrib, unsigned int id, int err); struct gatt_db_attribute * gatt_db_attribute_get_value(struct gatt_db_attribute *attrib); struct gatt_db_attribute * gatt_db_attribute_get_ccc(struct gatt_db_attribute *attrib); bool gatt_db_attribute_notify(struct gatt_db_attribute *attrib, const uint8_t *value, size_t len, struct bt_att *att); bool gatt_db_attribute_reset(struct gatt_db_attribute *attrib); void *gatt_db_attribute_get_user_data(struct gatt_db_attribute *attrib); unsigned int gatt_db_attribute_register(struct gatt_db_attribute *attrib, gatt_db_attribute_cb_t removed, void *user_data, gatt_db_destroy_func_t destroy); bool gatt_db_attribute_unregister(struct gatt_db_attribute *attrib, unsigned int id); bluez-5.82/src/shared/PaxHeaders/util.h0000644000000000000000000000005014772767672015062 xustar0020 atime=1743515579 20 ctime=1743591277 bluez-5.82/src/shared/util.h0000644000000000000000000002044214772767672014545 0ustar00rootroot/* SPDX-License-Identifier: LGPL-2.1-or-later */ /* * * BlueZ - Bluetooth protocol stack for Linux * * Copyright (C) 2012-2014 Intel Corporation. All rights reserved. * Copyright 2023-2024 NXP * * */ #include #include #include #include #include #include #include #include #include #define ARRAY_SIZE(arr) (sizeof(arr) / sizeof((arr)[0])) #define BIT(n) (1 << (n)) #if __BYTE_ORDER == __LITTLE_ENDIAN #define le16_to_cpu(val) (val) #define le32_to_cpu(val) (val) #define le64_to_cpu(val) (val) #define cpu_to_le16(val) (val) #define cpu_to_le32(val) (val) #define cpu_to_le64(val) (val) #define be16_to_cpu(val) bswap_16(val) #define be32_to_cpu(val) bswap_32(val) #define be64_to_cpu(val) bswap_64(val) #define cpu_to_be16(val) bswap_16(val) #define cpu_to_be32(val) bswap_32(val) #define cpu_to_be64(val) bswap_64(val) #elif __BYTE_ORDER == __BIG_ENDIAN #define le16_to_cpu(val) bswap_16(val) #define le32_to_cpu(val) bswap_32(val) #define le64_to_cpu(val) bswap_64(val) #define cpu_to_le16(val) bswap_16(val) #define cpu_to_le32(val) bswap_32(val) #define cpu_to_le64(val) bswap_64(val) #define be16_to_cpu(val) (val) #define be32_to_cpu(val) (val) #define be64_to_cpu(val) (val) #define cpu_to_be16(val) (val) #define cpu_to_be32(val) (val) #define cpu_to_be64(val) (val) #else #error "Unknown byte order" #endif #define get_unaligned(ptr) \ __extension__ ({ \ struct __attribute__((packed)) { \ __typeof__(*(ptr)) __v; \ } *__p = (__typeof__(__p)) (ptr); \ __p->__v; \ }) #define put_unaligned(val, ptr) \ do { \ struct __attribute__((packed)) { \ __typeof__(*(ptr)) __v; \ } *__p = (__typeof__(__p)) (ptr); \ __p->__v = (val); \ } while (0) #define PTR_TO_UINT(p) ((unsigned int) ((uintptr_t) (p))) #define UINT_TO_PTR(u) ((void *) ((uintptr_t) (u))) #define PTR_TO_INT(p) ((int) ((intptr_t) (p))) #define INT_TO_PTR(u) ((void *) ((intptr_t) (u))) #define new0(type, count) \ (type *) (__extension__ ({ \ size_t __n = (size_t) (count); \ size_t __s = sizeof(type); \ void *__p; \ __p = util_malloc(__n * __s); \ memset(__p, 0, __n * __s); \ __p; \ })) #define newa(t, n) ((t*) alloca(sizeof(t)*(n))) #define malloc0(n) (calloc(1, (n))) char *strdelimit(char *str, char *del, char c); int strsuffix(const char *str, const char *suffix); char *strstrip(char *str); bool strisutf8(const char *str, size_t length); bool argsisutf8(int argc, char *argv[]); void *util_malloc(size_t size); void *util_memdup(const void *src, size_t size); typedef void (*util_debug_func_t)(const char *str, void *user_data); void util_debug_va(util_debug_func_t function, void *user_data, const char *format, va_list va); void util_debug(util_debug_func_t function, void *user_data, const char *format, ...) __attribute__((format(printf, 3, 4))); void util_hexdump(const char dir, const unsigned char *buf, size_t len, util_debug_func_t function, void *user_data); #define UTIL_BIT_DEBUG(_bit, _str) \ { \ .bit = _bit, \ .str = _str, \ } struct util_bit_debugger { uint64_t bit; const char *str; }; uint64_t util_debug_bit(const char *label, uint64_t val, const struct util_bit_debugger *table, util_debug_func_t func, void *user_data); #define UTIL_LTV_DEBUG(_type, _func) \ { \ .type = _type, \ .func = _func, \ } struct util_ltv_debugger { uint8_t type; void (*func)(const uint8_t *data, uint8_t len, util_debug_func_t func, void *user_data); }; void util_ltv_push(struct iovec *output, uint8_t l, uint8_t t, void *v); bool util_debug_ltv(const uint8_t *data, uint8_t len, const struct util_ltv_debugger *debugger, size_t num, util_debug_func_t function, void *user_data); typedef void (*util_ltv_func_t)(size_t i, uint8_t l, uint8_t t, uint8_t *v, void *user_data); bool util_ltv_foreach(const uint8_t *data, uint8_t len, uint8_t *type, util_ltv_func_t func, void *user_data); unsigned char util_get_dt(const char *parent, const char *name); ssize_t util_getrandom(void *buf, size_t buflen, unsigned int flags); uint8_t util_get_uid(uint64_t *bitmap, uint8_t max); void util_clear_uid(uint64_t *bitmap, uint8_t id); #define util_data(args...) ((const unsigned char[]) { args }) #define UTIL_IOV_INIT(args...) \ { \ .iov_base = (void *)util_data(args), \ .iov_len = sizeof(util_data(args)), \ } struct iovec *util_iov_dup(const struct iovec *iov, size_t cnt); int util_iov_memcmp(const struct iovec *iov1, const struct iovec *iov2); void util_iov_memcpy(struct iovec *iov, void *src, size_t len); void *util_iov_push(struct iovec *iov, size_t len); void *util_iov_push_mem(struct iovec *iov, size_t len, const void *data); void *util_iov_push_le64(struct iovec *iov, uint64_t val); void *util_iov_push_be64(struct iovec *iov, uint64_t val); void *util_iov_push_le32(struct iovec *iov, uint32_t val); void *util_iov_push_be32(struct iovec *iov, uint32_t val); void *util_iov_push_le24(struct iovec *iov, uint32_t val); void *util_iov_push_be24(struct iovec *iov, uint32_t val); void *util_iov_push_le16(struct iovec *iov, uint16_t val); void *util_iov_push_be16(struct iovec *iov, uint16_t val); void *util_iov_push_u8(struct iovec *iov, uint8_t val); void *util_iov_append(struct iovec *iov, const void *data, size_t len); struct iovec *util_iov_new(void *data, size_t len); void *util_iov_pull(struct iovec *iov, size_t len); void *util_iov_pull_mem(struct iovec *iov, size_t len); void *util_iov_pull_le64(struct iovec *iov, uint64_t *val); void *util_iov_pull_be64(struct iovec *iov, uint64_t *val); void *util_iov_pull_le32(struct iovec *iov, uint32_t *val); void *util_iov_pull_be32(struct iovec *iov, uint32_t *val); void *util_iov_pull_le24(struct iovec *iov, uint32_t *val); void *util_iov_pull_be24(struct iovec *iov, uint32_t *val); void *util_iov_pull_le16(struct iovec *iov, uint16_t *val); void *util_iov_pull_be16(struct iovec *iov, uint16_t *val); void *util_iov_pull_u8(struct iovec *iov, uint8_t *val); void util_iov_free(struct iovec *iov, size_t cnt); const char *bt_uuid16_to_str(uint16_t uuid); const char *bt_uuid32_to_str(uint32_t uuid); const char *bt_uuid128_to_str(const uint8_t uuid[16]); const char *bt_uuidstr_to_str(const char *uuid); const char *bt_appear_to_str(uint16_t appearance); static inline int8_t get_s8(const void *ptr) { return *((int8_t *) ptr); } static inline uint8_t get_u8(const void *ptr) { return *((uint8_t *) ptr); } static inline uint16_t get_le16(const void *ptr) { return le16_to_cpu(get_unaligned((const uint16_t *) ptr)); } static inline uint16_t get_be16(const void *ptr) { return be16_to_cpu(get_unaligned((const uint16_t *) ptr)); } static inline uint32_t get_le24(const void *ptr) { const uint8_t *src = ptr; return ((uint32_t)src[2] << 16) | get_le16(ptr); } static inline uint32_t get_be24(const void *ptr) { const uint8_t *src = ptr; return ((uint32_t)src[0] << 16) | get_be16(&src[1]); } static inline uint32_t get_le32(const void *ptr) { return le32_to_cpu(get_unaligned((const uint32_t *) ptr)); } static inline uint32_t get_be32(const void *ptr) { return be32_to_cpu(get_unaligned((const uint32_t *) ptr)); } static inline uint64_t get_le64(const void *ptr) { return le64_to_cpu(get_unaligned((const uint64_t *) ptr)); } static inline uint64_t get_be64(const void *ptr) { return be64_to_cpu(get_unaligned((const uint64_t *) ptr)); } static inline void put_u8(uint8_t val, void *dst) { put_unaligned(val, (uint8_t *) dst); } static inline void put_le16(uint16_t val, void *dst) { put_unaligned(cpu_to_le16(val), (uint16_t *) dst); } static inline void put_be16(uint16_t val, const void *ptr) { put_unaligned(cpu_to_be16(val), (uint16_t *) ptr); } static inline void put_le24(uint32_t val, void *ptr) { put_le16(val, ptr); put_unaligned(val >> 16, (uint8_t *) ptr + 2); } static inline void put_be24(uint32_t val, void *ptr) { put_unaligned(val >> 16, (uint8_t *) ptr + 2); put_be16(val, ptr + 1); } static inline void put_le32(uint32_t val, void *dst) { put_unaligned(cpu_to_le32(val), (uint32_t *) dst); } static inline void put_be32(uint32_t val, void *dst) { put_unaligned(cpu_to_be32(val), (uint32_t *) dst); } static inline void put_le64(uint64_t val, void *dst) { put_unaligned(cpu_to_le64(val), (uint64_t *) dst); } static inline void put_be64(uint64_t val, void *dst) { put_unaligned(cpu_to_be64(val), (uint64_t *) dst); } bluez-5.82/src/shared/PaxHeaders/bass.c0000644000000000000000000000005014766002272015005 xustar0020 atime=1743515578 20 ctime=1743591277 bluez-5.82/src/shared/bass.c0000644000000000000000000012464514766002272014502 0ustar00rootroot// SPDX-License-Identifier: LGPL-2.1-or-later /* * * BlueZ - Bluetooth protocol stack for Linux * * Copyright 2023-2024 NXP * */ #define _GNU_SOURCE #include #include #include #include #include #include #include "lib/bluetooth.h" #include "lib/uuid.h" #include "src/shared/queue.h" #include "src/shared/util.h" #include "src/shared/att.h" #include "src/shared/gatt-db.h" #include "src/shared/gatt-client.h" #include "src/shared/bass.h" #define DBG(_bass, fmt, arg...) \ bass_debug(_bass, "%s:%s() " fmt, __FILE__, __func__, ## arg) struct bt_bass_db; struct bt_bass_cb { unsigned int id; bt_bass_func_t attached; bt_bass_func_t detached; void *user_data; }; struct bt_bcast_recv_state { struct bt_bass_db *bdb; struct gatt_db_attribute *attr; struct gatt_db_attribute *ccc; }; struct bt_bass_db { struct gatt_db *db; bdaddr_t adapter_bdaddr; struct queue *bcast_srcs; struct gatt_db_attribute *service; struct gatt_db_attribute *bcast_audio_scan_cp; struct bt_bcast_recv_state *bcast_recv_states[NUM_BCAST_RECV_STATES]; }; struct bt_bass { int ref_count; struct bt_bass_db *ldb; struct bt_bass_db *rdb; struct bt_gatt_client *client; struct bt_att *att; struct queue *notify; bt_bass_debug_func_t debug_func; bt_bass_destroy_func_t debug_destroy; void *debug_data; struct queue *src_cbs; struct queue *cp_handlers; unsigned int disconn_id; void *user_data; }; struct bt_bass_cp_handler { unsigned int id; bt_bass_cp_handler_func_t handler; bt_bass_destroy_func_t destroy; void *data; }; /* BASS subgroup field of the Broadcast * Receive State characteristic */ struct bt_bass_subgroup_data { uint32_t bis_sync; uint32_t pending_bis_sync; uint8_t meta_len; uint8_t *meta; }; /* BASS Broadcast Source structure */ struct bt_bcast_src { struct bt_bass *bass; struct gatt_db_attribute *attr; uint8_t id; uint8_t addr_type; bdaddr_t addr; uint8_t sid; uint32_t bid; uint8_t sync_state; uint8_t enc; uint8_t bad_code[BT_BASS_BCAST_CODE_SIZE]; uint8_t num_subgroups; struct bt_bass_subgroup_data *subgroup_data; }; typedef void (*bass_notify_t)(struct bt_bass *bass, uint16_t value_handle, const uint8_t *value, uint16_t length, void *user_data); struct bt_bass_notify { unsigned int id; struct bt_bass *bass; bass_notify_t func; void *user_data; }; static struct queue *bass_db; static struct queue *bass_cbs; static struct queue *sessions; struct bt_bass_src_changed { unsigned int id; bt_bass_src_func_t cb; bt_bass_destroy_func_t destroy; void *data; }; static void bass_bcast_src_free(void *data); static void bass_debug(struct bt_bass *bass, const char *format, ...) { va_list ap; if (!bass || !format || !bass->debug_func) return; va_start(ap, format); util_debug_va(bass->debug_func, bass->debug_data, format, ap); va_end(ap); } unsigned int bt_bass_cp_handler_register(struct bt_bass *bass, bt_bass_cp_handler_func_t handler, bt_bass_destroy_func_t destroy, void *user_data) { struct bt_bass_cp_handler *cb; static unsigned int id; if (!bass) return 0; cb = new0(struct bt_bass_cp_handler, 1); cb->id = ++id ? id : ++id; cb->handler = handler; cb->destroy = destroy; cb->data = user_data; queue_push_tail(bass->cp_handlers, cb); return cb->id; } static void bass_cp_handler_free(void *data) { struct bt_bass_cp_handler *cb = data; if (cb->destroy) cb->destroy(cb->data); free(cb); } static bool match_cb_id(const void *data, const void *match_data) { const struct bt_bass_cp_handler *cb = data; unsigned int id = PTR_TO_UINT(match_data); return (cb->id == id); } bool bt_bass_cp_handler_unregister(struct bt_bass *bass, unsigned int id) { struct bt_bass_cp_handler *cb; if (!bass) return false; cb = queue_remove_if(bass->cp_handlers, match_cb_id, UINT_TO_PTR(id)); if (!cb) return false; bass_cp_handler_free(cb); return true; } unsigned int bt_bass_src_register(struct bt_bass *bass, bt_bass_src_func_t cb, void *user_data, bt_bass_destroy_func_t destroy) { struct bt_bass_src_changed *changed; static unsigned int id; if (!bass) return 0; changed = new0(struct bt_bass_src_changed, 1); if (!changed) return 0; changed->id = ++id ? id : ++id; changed->cb = cb; changed->destroy = destroy; changed->data = user_data; queue_push_tail(bass->src_cbs, changed); return changed->id; } static void bass_src_changed_free(void *data) { struct bt_bass_src_changed *changed = data; if (changed->destroy) changed->destroy(changed->data); free(changed); } static bool match_src_changed_id(const void *data, const void *match_data) { const struct bt_bass_src_changed *changed = data; unsigned int id = PTR_TO_UINT(match_data); return (changed->id == id); } bool bt_bass_src_unregister(struct bt_bass *bass, unsigned int id) { struct bt_bass_src_changed *changed; if (!bass) return false; changed = queue_remove_if(bass->src_cbs, match_src_changed_id, UINT_TO_PTR(id)); if (!changed) return false; bass_src_changed_free(changed); return true; } static int bass_build_bcast_src(struct bt_bcast_src *bcast_src, const uint8_t *value, uint16_t length) { struct bt_bass_subgroup_data *subgroup_data = NULL; uint8_t id; uint8_t addr_type; uint8_t *addr; uint8_t sid; uint32_t bid; uint8_t pa_sync_state; uint8_t enc; uint8_t *bad_code = NULL; uint8_t num_subgroups; uint32_t bis_sync_state; uint8_t meta_len; uint8_t *meta; struct iovec iov = { .iov_base = (void *) value, .iov_len = length, }; /* Extract all fields from notification */ if (!util_iov_pull_u8(&iov, &id)) { DBG(bcast_src->bass, "Unable to parse Broadcast Receive State"); return -1; } if (!util_iov_pull_u8(&iov, &addr_type)) { DBG(bcast_src->bass, "Unable to parse Broadcast Receive State"); return -1; } addr = util_iov_pull_mem(&iov, sizeof(bdaddr_t)); if (!addr) { DBG(bcast_src->bass, "Unable to parse Broadcast Receive State"); return -1; } if (!util_iov_pull_u8(&iov, &sid)) { DBG(bcast_src->bass, "Unable to parse Broadcast Receive State"); return -1; } if (!util_iov_pull_le24(&iov, &bid)) { DBG(bcast_src->bass, "Unable to parse Broadcast Receive State"); return -1; } if (!util_iov_pull_u8(&iov, &pa_sync_state)) { DBG(bcast_src->bass, "Unable to parse Broadcast Receive State"); return -1; } if (!util_iov_pull_u8(&iov, &enc)) { DBG(bcast_src->bass, "Unable to parse Broadcast Receive State"); return -1; } if (enc == BT_BASS_BIG_ENC_STATE_BAD_CODE) { bad_code = util_iov_pull_mem(&iov, BT_BASS_BCAST_CODE_SIZE); if (!bad_code) { DBG(bcast_src->bass, "Unable to parse " "Broadcast Receive State"); return -1; } } if (!util_iov_pull_u8(&iov, &num_subgroups)) { DBG(bcast_src->bass, "Unable to parse Broadcast Receive State"); return -1; } if (num_subgroups == 0) goto done; subgroup_data = new0(struct bt_bass_subgroup_data, 1); if (!subgroup_data) { DBG(bcast_src->bass, "Unable to allocate memory"); return -1; } for (int i = 0; i < num_subgroups; i++) { if (!util_iov_pull_le32(&iov, &bis_sync_state)) { DBG(bcast_src->bass, "Unable to parse " "Broadcast Receive State"); for (int j = 0; j < i; j++) free(subgroup_data[j].meta); free(subgroup_data); return -1; } subgroup_data[i].bis_sync = bis_sync_state; if (!util_iov_pull_u8(&iov, &meta_len)) { DBG(bcast_src->bass, "Unable to parse " "Broadcast Receive State"); for (int j = 0; j < i; j++) free(subgroup_data[j].meta); free(subgroup_data); return -1; } subgroup_data[i].meta_len = meta_len; if (meta_len == 0) continue; subgroup_data[i].meta = malloc0(meta_len); if (!subgroup_data[i].meta) { DBG(bcast_src->bass, "Unable to allocate memory"); for (int j = 0; j < i; j++) free(subgroup_data[j].meta); free(subgroup_data); return -1; } meta = util_iov_pull_mem(&iov, meta_len); if (!meta) { DBG(bcast_src->bass, "Unable to parse " "Broadcast Receive State"); for (int j = 0; j < i; j++) free(subgroup_data[j].meta); free(subgroup_data); return -1; } memcpy(subgroup_data[i].meta, meta, meta_len); } done: /* * If no errors occurred, copy extracted fields into * the broadcast source structure */ if (bcast_src->subgroup_data) { for (int i = 0; i < bcast_src->num_subgroups; i++) free(bcast_src->subgroup_data[i].meta); free(bcast_src->subgroup_data); } bcast_src->id = id; bcast_src->addr_type = addr_type; memcpy(&bcast_src->addr, addr, sizeof(bdaddr_t)); bcast_src->sid = sid; bcast_src->bid = bid; bcast_src->sync_state = pa_sync_state; bcast_src->enc = enc; if (enc == BT_BASS_BIG_ENC_STATE_BAD_CODE) memcpy(bcast_src->bad_code, bad_code, BT_BASS_BCAST_CODE_SIZE); else memset(bcast_src->bad_code, 0, BT_BASS_BCAST_CODE_SIZE); bcast_src->num_subgroups = num_subgroups; bcast_src->subgroup_data = subgroup_data; return 0; } static struct iovec *bass_parse_bcast_src(struct bt_bcast_src *bcast_src) { size_t len = 0; uint8_t *notif = NULL; struct iovec *iov; if (!bcast_src) return NULL; len = BT_BASS_BCAST_SRC_LEN + bcast_src->num_subgroups * BT_BASS_BCAST_SRC_SUBGROUP_LEN; if (bcast_src->enc == BT_BASS_BIG_ENC_STATE_BAD_CODE) len += BT_BASS_BCAST_CODE_SIZE; for (size_t i = 0; i < bcast_src->num_subgroups; i++) { /* Add length for subgroup metadata */ len += bcast_src->subgroup_data[i].meta_len; } notif = malloc0(len); if (!notif) return NULL; iov = new0(struct iovec, 1); if (!iov) { free(notif); return NULL; } iov->iov_base = notif; iov->iov_len = 0; util_iov_push_u8(iov, bcast_src->id); util_iov_push_u8(iov, bcast_src->addr_type); util_iov_push_mem(iov, sizeof(bcast_src->addr), &bcast_src->addr); util_iov_push_u8(iov, bcast_src->sid); util_iov_push_le24(iov, bcast_src->bid); util_iov_push_u8(iov, bcast_src->sync_state); util_iov_push_u8(iov, bcast_src->enc); if (bcast_src->enc == BT_BASS_BIG_ENC_STATE_BAD_CODE) util_iov_push_mem(iov, sizeof(bcast_src->bad_code), bcast_src->bad_code); util_iov_push_u8(iov, bcast_src->num_subgroups); for (size_t i = 0; i < bcast_src->num_subgroups; i++) { /* Add subgroup bis_sync */ util_iov_push_le32(iov, bcast_src->subgroup_data[i].bis_sync); /* Add subgroup meta_len */ util_iov_push_u8(iov, bcast_src->subgroup_data[i].meta_len); /* Add subgroup metadata */ if (bcast_src->subgroup_data[i].meta_len > 0) util_iov_push_mem(iov, bcast_src->subgroup_data[i].meta_len, bcast_src->subgroup_data[i].meta); } return iov; } static bool bass_check_cp_command_subgroup_data_len(uint8_t num_subgroups, struct iovec *iov) { uint32_t bis_sync_state; uint8_t *meta_len; uint8_t *meta; for (int i = 0; i < num_subgroups; i++) { if (!util_iov_pull_le32(iov, &bis_sync_state)) return false; meta_len = util_iov_pull_mem(iov, sizeof(*meta_len)); if (!meta_len) return false; meta = util_iov_pull_mem(iov, *meta_len); if (!meta) return false; } return true; } static bool bass_check_cp_command_len(const uint8_t *value, size_t len) { struct bt_bass_bcast_audio_scan_cp_hdr *hdr; union { struct bt_bass_add_src_params *add_src_params; struct bt_bass_mod_src_params *mod_src_params; struct bt_bass_set_bcast_code_params *set_bcast_code_params; struct bt_bass_remove_src_params *remove_src_params; } params; struct iovec iov = { .iov_base = (void *)value, .iov_len = len, }; /* Get command header */ hdr = util_iov_pull_mem(&iov, sizeof(*hdr)); if (!hdr) return false; /* Check command parameters */ switch (hdr->op) { case BT_BASS_ADD_SRC: params.add_src_params = util_iov_pull_mem(&iov, sizeof(*params.add_src_params)); if (!params.add_src_params) return false; if (!bass_check_cp_command_subgroup_data_len( params.add_src_params->num_subgroups, &iov)) return false; break; case BT_BASS_MOD_SRC: params.mod_src_params = util_iov_pull_mem(&iov, sizeof(*params.mod_src_params)); if (!params.mod_src_params) return false; if (!bass_check_cp_command_subgroup_data_len( params.mod_src_params->num_subgroups, &iov)) return false; break; case BT_BASS_SET_BCAST_CODE: params.set_bcast_code_params = util_iov_pull_mem(&iov, sizeof(*params.set_bcast_code_params)); if (!params.set_bcast_code_params) return false; break; case BT_BASS_REMOVE_SRC: params.remove_src_params = util_iov_pull_mem(&iov, sizeof(*params.remove_src_params)); if (!params.remove_src_params) return false; break; case BT_BASS_REMOTE_SCAN_STOPPED: case BT_BASS_REMOTE_SCAN_STARTED: break; default: return true; } if (iov.iov_len > 0) return false; return true; } static void bass_handle_remote_scan_stopped_op(struct bt_bass *bass, struct gatt_db_attribute *attrib, uint8_t opcode, unsigned int id, struct iovec *iov, struct bt_att *att) { gatt_db_attribute_write_result(attrib, id, 0x00); } static void bass_handle_remote_scan_started_op(struct bt_bass *bass, struct gatt_db_attribute *attrib, uint8_t opcode, unsigned int id, struct iovec *iov, struct bt_att *att) { gatt_db_attribute_write_result(attrib, id, 0x00); } static bool bass_src_id_match(const void *data, const void *match_data) { const struct bt_bcast_src *bcast_src = data; const uint8_t *id = match_data; return (bcast_src->id == *id); } static void bass_handle_remove_src_op(struct bt_bass *bass, struct gatt_db_attribute *attrib, uint8_t opcode, unsigned int id, struct iovec *iov, struct bt_att *att) { struct bt_bass_remove_src_params *params; struct bt_bcast_src *bcast_src; int att_err = 0; /* Get Remove Source command parameters */ params = util_iov_pull_mem(iov, sizeof(*params)); bcast_src = queue_find(bass->ldb->bcast_srcs, bass_src_id_match, ¶ms->id); if (!bcast_src) { /* No source matches the written source id */ att_err = BT_BASS_ERROR_INVALID_SOURCE_ID; goto done; } /* Ignore if server is synchronized to the PA * of the source */ if (bcast_src->sync_state == BT_BASS_SYNCHRONIZED_TO_PA) goto done; /* Ignore if server is synchronized to any BIS * of the source */ for (int i = 0; i < bcast_src->num_subgroups; i++) if (bcast_src->subgroup_data[i].bis_sync) goto done; /* Accept the operation and remove source */ queue_remove(bass->ldb->bcast_srcs, bcast_src); gatt_db_attribute_notify(bcast_src->attr, NULL, 0, att); bass_bcast_src_free(bcast_src); done: gatt_db_attribute_write_result(attrib, id, att_err); } static bool bass_src_attr_match(const void *data, const void *match_data) { const struct bt_bcast_src *bcast_src = data; const struct gatt_db_attribute *attr = match_data; return (bcast_src->attr == attr); } static bool bass_trigger_big_sync(struct bt_bcast_src *bcast_src) { for (int i = 0; i < bcast_src->num_subgroups; i++) { struct bt_bass_subgroup_data *data = &bcast_src->subgroup_data[i]; if (data->pending_bis_sync && data->pending_bis_sync != BIS_SYNC_NO_PREF) return true; } return false; } static struct bt_bass *bass_get_session(struct bt_att *att, struct gatt_db *db, const bdaddr_t *adapter_bdaddr) { const struct queue_entry *entry; struct bt_bass *bass; for (entry = queue_get_entries(sessions); entry; entry = entry->next) { struct bt_bass *bass = entry->data; if (att == bt_bass_get_att(bass)) return bass; } bass = bt_bass_new(db, NULL, adapter_bdaddr); bass->att = att; bt_bass_attach(bass, NULL); return bass; } static bool bass_validate_bis_sync(uint8_t num_subgroups, struct iovec *iov) { uint32_t bis_sync_state; uint32_t bitmask = 0U; uint8_t *meta_len; for (int i = 0; i < num_subgroups; i++) { util_iov_pull_le32(iov, &bis_sync_state); if (bis_sync_state != BIS_SYNC_NO_PREF) for (int bis_idx = 0; bis_idx < 31; bis_idx++) { if (bis_sync_state & (1 << bis_idx)) { if (bitmask & (1 << bis_idx)) return false; bitmask |= (1 << bis_idx); } } meta_len = util_iov_pull_mem(iov, sizeof(*meta_len)); util_iov_pull_mem(iov, *meta_len); } return true; } static bool bass_validate_add_src_params(uint8_t *value, size_t len) { struct bt_bass_add_src_params *params; struct iovec iov = { .iov_base = (void *)value, .iov_len = len, }; params = util_iov_pull_mem(&iov, sizeof(*params)); if (params->pa_sync > PA_SYNC_NO_PAST) return false; if (params->addr_type > 0x01) return false; if (params->sid > 0x0F) return false; if (!bass_validate_bis_sync(params->num_subgroups, &iov)) return false; return true; } static void bass_handle_add_src_op(struct bt_bass *bass, struct gatt_db_attribute *attrib, uint8_t opcode, unsigned int id, struct iovec *iov, struct bt_att *att) { struct bt_bcast_src *bcast_src, *src; uint8_t src_id = 0; struct gatt_db_attribute *attr; uint8_t pa_sync; struct iovec *notif; int ret; const struct queue_entry *entry; struct bt_bass_add_src_params *params; gatt_db_attribute_write_result(attrib, id, 0x00); /* Ignore operation if parameters are invalid */ if (!bass_validate_add_src_params(iov->iov_base, iov->iov_len)) return; /* Allocate a new broadcast source */ bcast_src = new0(struct bt_bcast_src, 1); if (!bcast_src) { DBG(bass, "Unable to allocate broadcast source"); return; } queue_push_tail(bass->ldb->bcast_srcs, bcast_src); bcast_src->bass = bass; /* Map the source to a Broadcast Receive State characteristic */ for (int i = 0; i < NUM_BCAST_RECV_STATES; i++) { src = queue_find(bass->ldb->bcast_srcs, bass_src_attr_match, bass->ldb->bcast_recv_states[i]->attr); if (!src) { /* Found and empty characteristic */ bcast_src->attr = bass->ldb->bcast_recv_states[i]->attr; break; } } if (!bcast_src->attr) { /* If no empty characteristic has been found, * overwrite an existing one */ attr = bass->ldb->bcast_recv_states[0]->attr; src = queue_find(bass->ldb->bcast_srcs, bass_src_attr_match, attr); queue_remove(bass->ldb->bcast_srcs, src); bass_bcast_src_free(src); bcast_src->attr = attr; } /* Allocate source id */ while (true) { src = queue_find(bass->ldb->bcast_srcs, bass_src_id_match, &src_id); if (!src) break; if (src_id == 0xFF) { DBG(bass, "Unable to allocate broadcast source id"); return; } src_id++; } bcast_src->id = src_id; params = util_iov_pull_mem(iov, sizeof(*params)); /* Populate broadcast source fields from command parameters */ bcast_src->addr_type = params->addr_type; /* Convert to three-value type */ if (bcast_src->addr_type) params->addr_type = BDADDR_LE_RANDOM; else params->addr_type = BDADDR_LE_PUBLIC; bacpy(&bcast_src->addr, ¶ms->addr); bcast_src->sid = params->sid; memcpy(&bcast_src->bid, params->bid, sizeof(params->bid)); pa_sync = params->pa_sync; bcast_src->sync_state = BT_BASS_NOT_SYNCHRONIZED_TO_PA; bcast_src->num_subgroups = params->num_subgroups; if (!bcast_src->num_subgroups) return; bcast_src->subgroup_data = new0(struct bt_bass_subgroup_data, bcast_src->num_subgroups); if (!bcast_src->subgroup_data) { DBG(bass, "Unable to allocate subgroup data"); goto err; } for (int i = 0; i < bcast_src->num_subgroups; i++) { struct bt_bass_subgroup_data *data = &bcast_src->subgroup_data[i]; util_iov_pull_le32(iov, &data->pending_bis_sync); data->meta_len = *(uint8_t *)util_iov_pull_mem(iov, sizeof(data->meta_len)); if (!data->meta_len) continue; data->meta = malloc0(data->meta_len); if (!data->meta) goto err; memcpy(data->meta, (uint8_t *)util_iov_pull_mem(iov, data->meta_len), data->meta_len); } if (pa_sync != PA_SYNC_NO_SYNC) { for (entry = queue_get_entries(bass->cp_handlers); entry; entry = entry->next) { struct bt_bass_cp_handler *cb = entry->data; if (cb->handler) { ret = cb->handler(bcast_src, BT_BASS_ADD_SRC, params, cb->data); if (ret) goto err; } } } else { for (int i = 0; i < bcast_src->num_subgroups; i++) bcast_src->subgroup_data[i].bis_sync = bcast_src->subgroup_data[i].pending_bis_sync; notif = bass_parse_bcast_src(bcast_src); if (!notif) return; gatt_db_attribute_notify(bcast_src->attr, notif->iov_base, notif->iov_len, bt_bass_get_att(bcast_src->bass)); free(notif->iov_base); free(notif); } return; err: if (bcast_src->subgroup_data) { for (int i = 0; i < bcast_src->num_subgroups; i++) free(bcast_src->subgroup_data[i].meta); free(bcast_src->subgroup_data); } free(bcast_src); } static void bass_handle_set_bcast_code_op(struct bt_bass *bass, struct gatt_db_attribute *attrib, uint8_t opcode, unsigned int id, struct iovec *iov, struct bt_att *att) { struct bt_bass_set_bcast_code_params *params; struct bt_bcast_src *bcast_src; struct iovec *notif; const struct queue_entry *entry; int ret; /* Get Set Broadcast Code command parameters */ params = util_iov_pull_mem(iov, sizeof(*params)); bcast_src = queue_find(bass->ldb->bcast_srcs, bass_src_id_match, ¶ms->id); if (!bcast_src) { /* No source matches the written source id */ gatt_db_attribute_write_result(attrib, id, BT_BASS_ERROR_INVALID_SOURCE_ID); return; } gatt_db_attribute_write_result(attrib, id, 0x00); if (!bass_trigger_big_sync(bcast_src)) { bcast_src->enc = BT_BASS_BIG_ENC_STATE_DEC; notif = bass_parse_bcast_src(bcast_src); if (!notif) return; gatt_db_attribute_notify(bcast_src->attr, notif->iov_base, notif->iov_len, bt_bass_get_att(bcast_src->bass)); free(notif->iov_base); free(notif); return; } for (entry = queue_get_entries(bass->cp_handlers); entry; entry = entry->next) { struct bt_bass_cp_handler *cb = entry->data; if (cb->handler) { ret = cb->handler(bcast_src, BT_BASS_SET_BCAST_CODE, params, cb->data); if (ret) DBG(bass, "Unable to handle Set " "Broadcast Code operation"); } } } static void bass_handle_mod_src_op(struct bt_bass *bass, struct gatt_db_attribute *attrib, uint8_t opcode, unsigned int id, struct iovec *iov, struct bt_att *att) { struct bt_bcast_src *bcast_src; struct bt_bass_mod_src_params *params; const struct queue_entry *entry; struct iovec *notif; bool updated = false; int err = 0; /* Get Modify Source command parameters */ params = util_iov_pull_mem(iov, sizeof(*params)); bcast_src = queue_find(bass->ldb->bcast_srcs, bass_src_id_match, ¶ms->id); if (!bcast_src) { /* No source matches the written source id */ gatt_db_attribute_write_result(attrib, id, BT_BASS_ERROR_INVALID_SOURCE_ID); return; } gatt_db_attribute_write_result(attrib, id, 0x00); for (int i = 0; i < bcast_src->num_subgroups; i++) { struct bt_bass_subgroup_data *data = &bcast_src->subgroup_data[i]; uint8_t meta_len; uint8_t *meta; if (!util_iov_pull_le32(iov, &data->pending_bis_sync)) return; if (!util_iov_pull_u8(iov, &meta_len)) return; /* Check for metadata updates and notify peers */ if (meta_len != data->meta_len) { updated = true; data->meta_len = meta_len; free(data->meta); data->meta = malloc0(data->meta_len); if (!data->meta) return; } if (!data->meta_len) continue; meta = (uint8_t *)util_iov_pull_mem(iov, meta_len); if (!meta) return; if (memcmp(meta, data->meta, data->meta_len)) { updated = true; memcpy(data->meta, meta, data->meta_len); } } for (entry = queue_get_entries(bass->cp_handlers); entry; entry = entry->next) { struct bt_bass_cp_handler *cb = entry->data; if (cb->handler) { err = cb->handler(bcast_src, BT_BASS_MOD_SRC, params, cb->data); if (err) DBG(bass, "Unable to handle Modify Source " "operation"); } } if (!updated) return; notif = bass_parse_bcast_src(bcast_src); if (!notif) return; gatt_db_attribute_notify(bcast_src->attr, notif->iov_base, notif->iov_len, bt_bass_get_att(bcast_src->bass)); free(notif->iov_base); free(notif); } #define BASS_OP(_str, _op, _size, _func) \ { \ .str = _str, \ .op = _op, \ .size = _size, \ .func = _func, \ } struct bass_op_handler { const char *str; uint8_t op; size_t size; void (*func)(struct bt_bass *bass, struct gatt_db_attribute *attrib, uint8_t opcode, unsigned int id, struct iovec *iov, struct bt_att *att); } bass_handlers[] = { BASS_OP("Remote Scan Stopped", BT_BASS_REMOTE_SCAN_STOPPED, 0, bass_handle_remote_scan_stopped_op), BASS_OP("Remote Scan Started", BT_BASS_REMOTE_SCAN_STARTED, 0, bass_handle_remote_scan_started_op), BASS_OP("Remove Source", BT_BASS_REMOVE_SRC, 0, bass_handle_remove_src_op), BASS_OP("Add Source", BT_BASS_ADD_SRC, 0, bass_handle_add_src_op), BASS_OP("Set Broadcast Code", BT_BASS_SET_BCAST_CODE, 0, bass_handle_set_bcast_code_op), BASS_OP("Modify Source", BT_BASS_MOD_SRC, 0, bass_handle_mod_src_op), {} }; static void bass_bcast_audio_scan_cp_write(struct gatt_db_attribute *attrib, unsigned int id, uint16_t offset, const uint8_t *value, size_t len, uint8_t opcode, struct bt_att *att, void *user_data) { struct bt_bass_db *bdb = user_data; struct bt_bass_bcast_audio_scan_cp_hdr *hdr; struct bass_op_handler *handler; struct bt_bass *bass = bass_get_session(att, bdb->db, &bdb->adapter_bdaddr); struct iovec iov = { .iov_base = (void *)value, .iov_len = len, }; /* Validate written command length */ if (!bass_check_cp_command_len(value, len)) { gatt_db_attribute_write_result(attrib, id, BT_ERROR_WRITE_REQUEST_REJECTED); return; } /* Get command header */ hdr = util_iov_pull_mem(&iov, sizeof(*hdr)); /* Call the appropriate opcode handler */ for (handler = bass_handlers; handler && handler->str; handler++) { if (handler->op == hdr->op) { handler->func(bass, attrib, opcode, id, &iov, att); return; } } /* Send error response if unsupported opcode was written */ gatt_db_attribute_write_result(attrib, id, BT_BASS_ERROR_OPCODE_NOT_SUPPORTED); } static bool bass_src_match_attrib(const void *data, const void *match_data) { const struct bt_bcast_src *bcast_src = data; const struct gatt_db_attribute *attr = match_data; return (bcast_src->attr == attr); } static void bass_bcast_recv_state_read(struct gatt_db_attribute *attrib, unsigned int id, uint16_t offset, uint8_t opcode, struct bt_att *att, void *user_data) { struct bt_bass_db *bdb = user_data; struct iovec *rsp; struct bt_bcast_src *bcast_src; struct bt_bass *bass = bass_get_session(att, bdb->db, &bdb->adapter_bdaddr); bcast_src = queue_find(bass->ldb->bcast_srcs, bass_src_match_attrib, attrib); if (!bcast_src) { gatt_db_attribute_read_result(attrib, id, 0, NULL, 0); return; } /* Build read response */ rsp = bass_parse_bcast_src(bcast_src); if (!rsp) { gatt_db_attribute_read_result(attrib, id, BT_ATT_ERROR_UNLIKELY, NULL, 0); return; } gatt_db_attribute_read_result(attrib, id, 0, rsp->iov_base, rsp->iov_len); free(rsp->iov_base); free(rsp); } static void bcast_recv_new(struct bt_bass_db *bdb, int i) { struct bt_bcast_recv_state *bcast_recv_state; bt_uuid_t uuid; if (!bdb) return; bcast_recv_state = new0(struct bt_bcast_recv_state, 1); bcast_recv_state->bdb = bdb; bt_uuid16_create(&uuid, BCAST_RECV_STATE_UUID); bcast_recv_state->attr = gatt_db_service_add_characteristic(bdb->service, &uuid, BT_ATT_PERM_READ, BT_GATT_CHRC_PROP_READ | BT_GATT_CHRC_PROP_NOTIFY, bass_bcast_recv_state_read, NULL, bdb); bcast_recv_state->ccc = gatt_db_service_add_ccc(bdb->service, BT_ATT_PERM_READ | BT_ATT_PERM_WRITE); bdb->bcast_recv_states[i] = bcast_recv_state; } static void bass_new(struct bt_bass_db *bdb) { bt_uuid_t uuid; int i; /* Populate DB with BASS attributes */ bt_uuid16_create(&uuid, BASS_UUID); bdb->service = gatt_db_add_service(bdb->db, &uuid, true, 3 + (NUM_BCAST_RECV_STATES * 3)); for (i = 0; i < NUM_BCAST_RECV_STATES; i++) bcast_recv_new(bdb, i); bt_uuid16_create(&uuid, BCAST_AUDIO_SCAN_CP_UUID); bdb->bcast_audio_scan_cp = gatt_db_service_add_characteristic(bdb->service, &uuid, BT_ATT_PERM_WRITE, BT_GATT_CHRC_PROP_WRITE | BT_GATT_CHRC_PROP_WRITE_WITHOUT_RESP, NULL, bass_bcast_audio_scan_cp_write, bdb); gatt_db_service_set_active(bdb->service, true); } static void bass_bcast_src_free(void *data) { struct bt_bcast_src *bcast_src = data; if (!bcast_src) return; for (int i = 0; i < bcast_src->num_subgroups; i++) free(bcast_src->subgroup_data[i].meta); free(bcast_src->subgroup_data); free(bcast_src); } static void read_bcast_recv_state(bool success, uint8_t att_ecode, const uint8_t *value, uint16_t length, void *user_data) { struct bt_bcast_src *bcast_src = user_data; if (!success) { DBG(bcast_src->bass, "Unable to read " "Broadcast Receive State: error 0x%02x", att_ecode); return; } if (length == 0) { queue_remove(bcast_src->bass->rdb->bcast_srcs, bcast_src); bass_bcast_src_free(bcast_src); return; } if (bass_build_bcast_src(bcast_src, value, length)) { queue_remove(bcast_src->bass->rdb->bcast_srcs, bcast_src); bass_bcast_src_free(bcast_src); return; } } static void notify_src_changed(void *data, void *user_data) { struct bt_bass_src_changed *changed = data; struct bt_bcast_src *bcast_src = user_data; uint32_t bis_sync = 0; for (uint8_t i = 0; i < bcast_src->num_subgroups; i++) { struct bt_bass_subgroup_data *sgrp = &bcast_src->subgroup_data[i]; /* Create a bitmask of all BIS indices that the peer has * synchronized with. */ bis_sync |= sgrp->bis_sync; } if (changed->cb) changed->cb(bcast_src->id, bcast_src->bid, bcast_src->enc, bis_sync, changed->data); } static void bcast_recv_state_notify(struct bt_bass *bass, uint16_t value_handle, const uint8_t *value, uint16_t length, void *user_data) { struct gatt_db_attribute *attr = user_data; struct bt_bcast_src *bcast_src; bool new_src = false; bcast_src = queue_find(bass->rdb->bcast_srcs, bass_src_match_attrib, attr); if (!bcast_src) { new_src = true; bcast_src = new0(struct bt_bcast_src, 1); if (!bcast_src) { DBG(bass, "Failed to allocate " "memory for broadcast source"); return; } bcast_src->bass = bass; bcast_src->attr = attr; } if (bass_build_bcast_src(bcast_src, value, length) && new_src) { bass_bcast_src_free(bcast_src); return; } if (new_src) queue_push_tail(bass->rdb->bcast_srcs, bcast_src); /* Notify the update in the Broadcast Receive State characteristic * to all drivers that registered a callback. */ queue_foreach(bass->src_cbs, notify_src_changed, bcast_src); } static void bass_register(uint16_t att_ecode, void *user_data) { struct bt_bass_notify *notify = user_data; if (att_ecode) DBG(notify->bass, "BASS register notify failed: 0x%04x", att_ecode); } static void bass_notify(uint16_t value_handle, const uint8_t *value, uint16_t length, void *user_data) { struct bt_bass_notify *notify = user_data; if (notify->func) notify->func(notify->bass, value_handle, value, length, notify->user_data); } static void bass_notify_destroy(void *data) { struct bt_bass_notify *notify = data; struct bt_bass *bass = notify->bass; if (queue_remove_if(bass->notify, NULL, notify)) free(notify); } static unsigned int bass_register_notify(struct bt_bass *bass, uint16_t value_handle, bass_notify_t func, void *user_data) { struct bt_bass_notify *notify; notify = new0(struct bt_bass_notify, 1); notify->bass = bass; notify->func = func; notify->user_data = user_data; notify->id = bt_gatt_client_register_notify(bass->client, value_handle, bass_register, bass_notify, notify, bass_notify_destroy); if (!notify->id) { DBG(bass, "Unable to register for notifications"); free(notify); return 0; } queue_push_tail(bass->notify, notify); return notify->id; } static void foreach_bass_char(struct gatt_db_attribute *attr, void *user_data) { struct bt_bass *bass = user_data; uint16_t value_handle; bt_uuid_t uuid, uuid_bcast_audio_scan_cp, uuid_bcast_recv_state; /* Get attribute value handle and uuid */ if (!gatt_db_attribute_get_char_data(attr, NULL, &value_handle, NULL, NULL, &uuid)) return; bt_uuid16_create(&uuid_bcast_audio_scan_cp, BCAST_AUDIO_SCAN_CP_UUID); bt_uuid16_create(&uuid_bcast_recv_state, BCAST_RECV_STATE_UUID); if (!bt_uuid_cmp(&uuid, &uuid_bcast_audio_scan_cp)) { /* Found Broadcast Audio Scan Control Point characteristic */ bass->rdb->bcast_audio_scan_cp = attr; DBG(bass, "Broadcast Audio Scan Control Point " "found: handle 0x%04x", value_handle); } if (!bt_uuid_cmp(&uuid, &uuid_bcast_recv_state)) { /* Found Broadcast Receive State characteristic */ struct bt_bcast_src *bcast_src = queue_find(bass->rdb->bcast_srcs, bass_src_match_attrib, attr); if (!bcast_src) { bcast_src = new0(struct bt_bcast_src, 1); if (bcast_src == NULL) { DBG(bass, "Failed to allocate " "memory for broadcast source"); return; } bcast_src->bass = bass; bcast_src->attr = attr; queue_push_tail(bass->rdb->bcast_srcs, bcast_src); } bt_gatt_client_read_value(bass->client, value_handle, read_bcast_recv_state, bcast_src, NULL); (void)bass_register_notify(bass, value_handle, bcast_recv_state_notify, attr); DBG(bass, "Broadcast Receive State found: handle 0x%04x", value_handle); } } static void foreach_bass_service(struct gatt_db_attribute *attr, void *user_data) { struct bt_bass *bass = user_data; /* Store BASS service reference */ bass->rdb->service = attr; /* Handle BASS characteristics */ gatt_db_service_foreach_char(attr, foreach_bass_char, bass); } static void bass_attached(void *data, void *user_data) { struct bt_bass_cb *cb = data; struct bt_bass *bass = user_data; cb->attached(bass, cb->user_data); } static void bass_disconnected(int err, void *user_data) { struct bt_bass *bass = user_data; bass->disconn_id = 0; DBG(bass, "bass %p disconnected err %d", bass, err); bt_bass_detach(bass); } static void bass_attach_att(struct bt_bass *bass, struct bt_att *att) { if (bass->disconn_id) { if (att == bt_bass_get_att(bass)) return; bt_att_unregister_disconnect(bt_bass_get_att(bass), bass->disconn_id); } bass->disconn_id = bt_att_register_disconnect(att, bass_disconnected, bass, NULL); } bool bt_bass_attach(struct bt_bass *bass, struct bt_gatt_client *client) { bt_uuid_t uuid; if (!sessions) sessions = queue_new(); queue_push_tail(sessions, bass); queue_foreach(bass_cbs, bass_attached, bass); if (!client) { if (bass->att) bass_attach_att(bass, bass->att); return true; } if (bass->client) return false; bass->client = bt_gatt_client_clone(client); if (!bass->client) return false; bass_attach_att(bass, bt_gatt_client_get_att(client)); bt_uuid16_create(&uuid, BASS_UUID); gatt_db_foreach_service(bass->rdb->db, &uuid, foreach_bass_service, bass); return true; } bool bt_bass_set_att(struct bt_bass *bass, struct bt_att *att) { if (!bass) return false; bass->att = att; return true; } static void bass_detached(void *data, void *user_data) { struct bt_bass_cb *cb = data; struct bt_bass *bass = user_data; cb->detached(bass, cb->user_data); } void bt_bass_detach(struct bt_bass *bass) { struct bt_att *att; if (!queue_remove(sessions, bass)) return; if (bass->client) att = bt_gatt_client_get_att(bass->client); else att = bass->att; bt_att_unregister_disconnect(att, bass->disconn_id); bt_gatt_client_unref(bass->client); bass->client = NULL; bass->att = NULL; queue_foreach(bass_cbs, bass_detached, bass); } static void bass_db_free(void *data) { struct bt_bass_db *bdb = data; if (!bdb) return; gatt_db_unref(bdb->db); queue_destroy(bdb->bcast_srcs, bass_bcast_src_free); free(bdb); } static void bass_free(void *data) { struct bt_bass *bass = data; bt_bass_detach(bass); bass_db_free(bass->rdb); queue_destroy(bass->notify, NULL); queue_destroy(bass->src_cbs, bass_src_changed_free); queue_destroy(bass->cp_handlers, bass_cp_handler_free); free(bass); } void bt_bass_unref(struct bt_bass *bass) { if (!bass) return; if (__sync_sub_and_fetch(&bass->ref_count, 1)) return; bass_free(bass); } bool bt_bass_set_user_data(struct bt_bass *bass, void *user_data) { if (!bass) return false; bass->user_data = user_data; return true; } static struct bt_bass_db *bass_db_new(struct gatt_db *db, const bdaddr_t *adapter_bdaddr) { struct bt_bass_db *bdb; if (!db) return NULL; bdb = new0(struct bt_bass_db, 1); bdb->db = gatt_db_ref(db); bacpy(&bdb->adapter_bdaddr, adapter_bdaddr); bdb->bcast_srcs = queue_new(); if (!bass_db) bass_db = queue_new(); bass_new(bdb); queue_push_tail(bass_db, bdb); return bdb; } static bool bass_db_match(const void *data, const void *match_data) { const struct bt_bass_db *bdb = data; const struct gatt_db *db = match_data; return (bdb->db == db); } static struct bt_bass_db *bass_get_db(struct gatt_db *db, const bdaddr_t *adapter_bdaddr) { struct bt_bass_db *bdb; bdb = queue_find(bass_db, bass_db_match, db); if (bdb) return bdb; return bass_db_new(db, adapter_bdaddr); } static struct bt_bass *bt_bass_ref(struct bt_bass *bass) { if (!bass) return NULL; __sync_fetch_and_add(&bass->ref_count, 1); return bass; } struct bt_bass *bt_bass_new(struct gatt_db *ldb, struct gatt_db *rdb, const bdaddr_t *adapter_bdaddr) { struct bt_bass *bass; struct bt_bass_db *db; if (!ldb) return NULL; db = bass_get_db(ldb, adapter_bdaddr); if (!db) return NULL; bass = new0(struct bt_bass, 1); bass->ldb = db; bass->notify = queue_new(); bass->src_cbs = queue_new(); bass->cp_handlers = queue_new(); if (!rdb) goto done; db = new0(struct bt_bass_db, 1); db->db = gatt_db_ref(rdb); db->bcast_srcs = queue_new(); bass->rdb = db; done: bt_bass_ref(bass); return bass; } struct bt_att *bt_bass_get_att(struct bt_bass *bass) { if (!bass) return NULL; if (bass->att) return bass->att; return bt_gatt_client_get_att(bass->client); } struct bt_gatt_client *bt_bass_get_client(struct bt_bass *bass) { if (!bass) return NULL; return bass->client; } bool bt_bass_set_debug(struct bt_bass *bass, bt_bass_debug_func_t func, void *user_data, bt_bass_destroy_func_t destroy) { if (!bass) return false; if (bass->debug_destroy) bass->debug_destroy(bass->debug_data); bass->debug_func = func; bass->debug_destroy = destroy; bass->debug_data = user_data; return true; } unsigned int bt_bass_register(bt_bass_func_t attached, bt_bass_func_t detached, void *user_data) { struct bt_bass_cb *cb; static unsigned int id; if (!attached && !detached) return 0; if (!bass_cbs) bass_cbs = queue_new(); cb = new0(struct bt_bass_cb, 1); cb->id = ++id ? id : ++id; cb->attached = attached; cb->detached = detached; cb->user_data = user_data; queue_push_tail(bass_cbs, cb); return cb->id; } static bool match_id(const void *data, const void *match_data) { const struct bt_bass_cb *cb = data; unsigned int id = PTR_TO_UINT(match_data); return (cb->id == id); } bool bt_bass_unregister(unsigned int id) { struct bt_bass_cb *cb; cb = queue_remove_if(bass_cbs, match_id, UINT_TO_PTR(id)); if (!cb) return false; free(cb); return true; } void bt_bass_add_db(struct gatt_db *db, const bdaddr_t *adapter_bdaddr) { bass_db_new(db, adapter_bdaddr); } int bt_bass_send(struct bt_bass *bass, struct bt_bass_bcast_audio_scan_cp_hdr *hdr, struct iovec *params) { struct iovec req = {0}; uint16_t handle; int err = 0; if (!bass || !bass->client || !bass->rdb) return -EINVAL; DBG(bass, "bass %p", bass); req.iov_base = malloc0(sizeof(*hdr) + params->iov_len); if (!req.iov_base) return -EINVAL; util_iov_push_mem(&req, sizeof(*hdr), hdr); util_iov_push_mem(&req, params->iov_len, params->iov_base); if (!gatt_db_attribute_get_char_data(bass->rdb->bcast_audio_scan_cp, NULL, &handle, NULL, NULL, NULL)) { err = -EINVAL; goto done; } if (!bt_gatt_client_write_without_response(bass->client, handle, false, req.iov_base, req.iov_len)) err = -EINVAL; done: free(req.iov_base); return err; } static void bt_bass_notify_all(struct gatt_db_attribute *attr, struct iovec *iov) { const struct queue_entry *entry; for (entry = queue_get_entries(sessions); entry; entry = entry->next) { struct bt_bass *bass = entry->data; gatt_db_attribute_notify(attr, iov->iov_base, iov->iov_len, bt_bass_get_att(bass)); } } int bt_bass_set_pa_sync(struct bt_bcast_src *bcast_src, uint8_t sync_state) { struct iovec *iov; if (!bcast_src) return -EINVAL; bcast_src->sync_state = sync_state; iov = bass_parse_bcast_src(bcast_src); if (!iov) return -ENOMEM; bt_bass_notify_all(bcast_src->attr, iov); free(iov->iov_base); free(iov); return 0; } int bt_bass_get_pa_sync(struct bt_bcast_src *bcast_src, uint8_t *sync_state) { if (!bcast_src) return -EINVAL; *sync_state = bcast_src->sync_state; return 0; } int bt_bass_set_bis_sync(struct bt_bcast_src *bcast_src, uint8_t bis) { struct iovec *iov; for (uint8_t i = 0; i < bcast_src->num_subgroups; i++) { struct bt_bass_subgroup_data *sgrp = &bcast_src->subgroup_data[i]; uint32_t bitmask = 1 << (bis - 1); if (sgrp->pending_bis_sync & bitmask) { sgrp->bis_sync |= bitmask; if (bcast_src->enc == BT_BASS_BIG_ENC_STATE_BCODE_REQ) bcast_src->enc = BT_BASS_BIG_ENC_STATE_DEC; iov = bass_parse_bcast_src(bcast_src); if (!iov) return -ENOMEM; bt_bass_notify_all(bcast_src->attr, iov); free(iov->iov_base); free(iov); } } return 0; } int bt_bass_clear_bis_sync(struct bt_bcast_src *bcast_src, uint8_t bis) { struct iovec *iov; for (uint8_t i = 0; i < bcast_src->num_subgroups; i++) { struct bt_bass_subgroup_data *sgrp = &bcast_src->subgroup_data[i]; uint32_t bitmask = 1 << (bis - 1); if (sgrp->bis_sync & bitmask) { sgrp->bis_sync &= ~bitmask; iov = bass_parse_bcast_src(bcast_src); if (!iov) return -ENOMEM; bt_bass_notify_all(bcast_src->attr, iov); free(iov->iov_base); free(iov); } } return 0; } bool bt_bass_check_bis(struct bt_bcast_src *bcast_src, uint8_t bis) { for (uint8_t i = 0; i < bcast_src->num_subgroups; i++) { struct bt_bass_subgroup_data *sgrp = &bcast_src->subgroup_data[i]; uint32_t bitmask = 1 << (bis - 1); if (sgrp->pending_bis_sync & bitmask) return true; } return false; } int bt_bass_set_enc(struct bt_bcast_src *bcast_src, uint8_t enc) { struct iovec *iov; if (!bcast_src) return -EINVAL; if (bcast_src->enc == enc) return 0; bcast_src->enc = enc; iov = bass_parse_bcast_src(bcast_src); if (!iov) return -ENOMEM; bt_bass_notify_all(bcast_src->attr, iov); free(iov->iov_base); free(iov); return 0; } bluez-5.82/src/shared/PaxHeaders/asha.c0000644000000000000000000000005014766002272014771 xustar0020 atime=1743515578 20 ctime=1743591277 bluez-5.82/src/shared/asha.c0000644000000000000000000002772614766002272014470 0ustar00rootroot// SPDX-License-Identifier: LGPL-2.1-or-later /* * * BlueZ - Bluetooth protocol stack for Linux * * Copyright (C) 2024 Asymptotic Inc. * * Author: Arun Raghavan * * */ #ifdef HAVE_CONFIG_H #include #endif #include #include #include #include #include "lib/bluetooth.h" #include "lib/uuid.h" #include "src/shared/util.h" #include "src/shared/att.h" #include "src/log.h" #include "src/shared/queue.h" #include "src/shared/gatt-db.h" #include "src/shared/gatt-client.h" #include "asha.h" /* We use strings instead of uint128_t to maintain readability */ #define ASHA_CHRC_READ_ONLY_PROPERTIES_UUID "6333651e-c481-4a3e-9169-7c902aad37bb" #define ASHA_CHRC_AUDIO_CONTROL_POINT_UUID "f0d4de7e-4a88-476c-9d9f-1937b0996cc0" #define ASHA_CHRC_AUDIO_STATUS_UUID "38663f1a-e711-4cac-b641-326b56404837" #define ASHA_CHRC_VOLUME_UUID "00e4ca9e-ab14-41e4-8823-f9e70c7e91df" #define ASHA_CHRC_LE_PSM_OUT_UUID "2d410339-82b6-42aa-b34e-e2e01df8cc1a" static struct queue *asha_devices; static unsigned int bt_asha_status(struct bt_asha *asha, bool connected); static bool match_hisyncid(const void *data, const void *user_data) { const struct bt_asha_set *set = data; const struct bt_asha *asha = user_data; return (memcmp(set->hisyncid, asha->hisyncid, 8) == 0); } static struct bt_asha_set *find_asha_set(struct bt_asha *asha) { return queue_find(asha_devices, match_hisyncid, asha); } static uint8_t is_other_connected(struct bt_asha *asha) { struct bt_asha_set *set = find_asha_set(asha); if (set) { if (asha->right_side && set->left) { DBG("ASHA right and left side connected"); return 1; } if (!asha->right_side && set->right) { DBG("ASHA left and right side connected"); return 1; } } if (asha->right_side) DBG("ASHA right side connected"); else DBG("ASHA left side connected"); return 0; } static void update_asha_set(struct bt_asha *asha, bool connected) { struct bt_asha_set *set; set = queue_find(asha_devices, match_hisyncid, asha); if (connected) { if (!set) { set = new0(struct bt_asha_set, 1); memcpy(set->hisyncid, asha->hisyncid, 8); queue_push_tail(asha_devices, set); DBG("Created ASHA set"); } if (asha->right_side) { set->right = asha; DBG("Right side registered for ASHA set"); } else { set->left = asha; DBG("Left side registered for ASHA set"); } } else { if (!set) { error("Missing ASHA set"); return; } if (asha->right_side && set->right) { set->right = NULL; DBG("Right side unregistered for ASHA set"); } else if (!asha->right_side && set->left) { set->left = NULL; DBG("Left side unregistered for ASHA set"); } if (!set->right && !set->left) { if (queue_remove(asha_devices, set)) { free(set); DBG("Freeing ASHA set"); } if (!queue_peek_tail(asha_devices)) { queue_destroy(asha_devices, NULL); asha_devices = NULL; } } } } static int asha_set_send_status(struct bt_asha *asha, bool other_connected) { struct bt_asha_set *set; int ret = 0; set = queue_find(asha_devices, match_hisyncid, asha); if (set) { if (asha->right_side && set->left) { ret = bt_asha_status(set->left, other_connected); DBG("ASHA left side update: %d, ret: %d", other_connected, ret); } if (!asha->right_side && set->right) { ret = bt_asha_status(set->right, other_connected); DBG("ASHA right side update: %d, ret: %d", other_connected, ret); } } return ret; } struct bt_asha *bt_asha_new(void) { struct bt_asha *asha; asha = new0(struct bt_asha, 1); return asha; } void bt_asha_reset(struct bt_asha *asha) { if (asha->status_notify_id) { bt_gatt_client_unregister_notify(asha->client, asha->status_notify_id); } gatt_db_unref(asha->db); asha->db = NULL; bt_gatt_client_unref(asha->client); asha->client = NULL; asha->psm = 0; update_asha_set(asha, false); } void bt_asha_state_reset(struct bt_asha *asha) { asha->state = ASHA_STOPPED; asha->cb = NULL; asha->cb_user_data = NULL; } void bt_asha_free(struct bt_asha *asha) { update_asha_set(asha, false); gatt_db_unref(asha->db); bt_gatt_client_unref(asha->client); free(asha); } static void asha_acp_sent(bool success, uint8_t err, void *user_data) { struct bt_asha *asha = user_data; if (success) { DBG("AudioControlPoint command successfully sent"); } else { error("Failed to send AudioControlPoint command: %d", err); if (asha->cb) asha->cb(-1, asha->cb_user_data); bt_asha_state_reset(asha); } } static int asha_send_acp(struct bt_asha *asha, uint8_t *cmd, unsigned int len, bt_asha_cb_t cb, void *user_data) { if (!bt_gatt_client_write_value(asha->client, asha->acp_handle, cmd, len, asha_acp_sent, asha, NULL)) { error("Error writing ACP command"); return -1; } asha->cb = cb; asha->cb_user_data = user_data; return 0; } static int asha_send_acp_without_response(struct bt_asha *asha, uint8_t *cmd, unsigned int len) { if (!bt_gatt_client_write_without_response(asha->client, asha->acp_handle, false, cmd, len)) { error("Error writing ACP command"); return -1; } return 0; } unsigned int bt_asha_start(struct bt_asha *asha, bt_asha_cb_t cb, void *user_data) { uint8_t other_connected = is_other_connected(asha); uint8_t acp_start_cmd[] = { 0x01, /* START */ 0x01, /* G.722, 16 kHz */ 0, /* Unknown media type */ asha->volume, /* Volume */ other_connected, }; int ret; if (asha->state != ASHA_STOPPED) { error("ASHA device start failed. Bad state %d", asha->state); return 0; } ret = asha_send_acp(asha, acp_start_cmd, sizeof(acp_start_cmd), cb, user_data); if (ret < 0) return ret; asha->state = ASHA_STARTING; return 0; } unsigned int bt_asha_stop(struct bt_asha *asha, bt_asha_cb_t cb, void *user_data) { uint8_t acp_stop_cmd[] = { 0x02, /* STOP */ }; int ret; if (asha->state != ASHA_STARTED) return 0; asha->state = ASHA_STOPPING; ret = asha_send_acp(asha, acp_stop_cmd, sizeof(acp_stop_cmd), cb, user_data); asha_set_send_status(asha, false); return ret; } static unsigned int bt_asha_status(struct bt_asha *asha, bool other_connected) { uint8_t status = other_connected ? 1 : 0; uint8_t acp_status_cmd[] = { 0x03, /* STATUS */ status, }; int ret; if (asha->state != ASHA_STARTED) { const char *side = asha->right_side ? "right" : "left"; DBG("ASHA %s device not started for status update", side); return 0; } ret = asha_send_acp_without_response(asha, acp_status_cmd, sizeof(acp_status_cmd)); if (ret < 0) return ret; return 0; } bool bt_asha_set_volume(struct bt_asha *asha, int8_t volume) { if (!bt_gatt_client_write_without_response(asha->client, asha->volume_handle, false, (const uint8_t *)&volume, 1)) { error("Error writing volume"); return false; } asha->volume = volume; return true; } static bool uuid_cmp(const char *uuid1, const bt_uuid_t *uuid2) { bt_uuid_t lhs; bt_string_to_uuid(&lhs, uuid1); return bt_uuid_cmp(&lhs, uuid2) == 0; } static void read_psm(bool success, uint8_t att_ecode, const uint8_t *value, uint16_t length, void *user_data) { struct bt_asha *asha = user_data; if (!success) { DBG("Reading PSM failed with ATT error: %u", att_ecode); return; } if (length != 2) { DBG("Reading PSM failed: unexpected length %u", length); return; } asha->psm = get_le16(value); DBG("Got PSM: %u", asha->psm); } static void read_rops(bool success, uint8_t att_ecode, const uint8_t *value, uint16_t length, void *user_data) { struct bt_asha *asha = user_data; if (!success) { DBG("Reading ROPs failed with ATT error: %u", att_ecode); return; } if (length != 17) { DBG("Reading ROPs failed: unexpected length %u", length); return; } if (value[0] != 0x01) { DBG("Unexpected ASHA version: %u", value[0]); return; } /* Device Capabilities */ asha->right_side = (value[1] & 0x1) != 0; asha->binaural = (value[1] & 0x2) != 0; asha->csis_supported = (value[1] & 0x4) != 0; /* HiSyncId: 2 byte company id, 6 byte ID shared by left and right */ memcpy(asha->hisyncid, &value[2], 8); /* FeatureMap */ asha->coc_streaming_supported = (value[10] & 0x1) != 0; /* RenderDelay */ asha->render_delay = get_le16(&value[11]); /* byte 13 & 14 are reserved */ /* Codec IDs */ asha->codec_ids = get_le16(&value[15]); DBG("Got ROPS: side %u, binaural %u, csis: %u, delay %u, codecs: %u", asha->right_side, asha->binaural, asha->csis_supported, asha->render_delay, asha->codec_ids); update_asha_set(asha, true); } static void audio_status_register(uint16_t att_ecode, void *user_data) { if (att_ecode) DBG("AudioStatusPoint register failed 0x%04x", att_ecode); else DBG("AudioStatusPoint register succeeded"); } static void audio_status_notify(uint16_t value_handle, const uint8_t *value, uint16_t length, void *user_data) { struct bt_asha *asha = user_data; uint8_t status = *value; /* Back these up to survive the reset paths */ bt_asha_cb_t cb = asha->cb; bt_asha_cb_t cb_user_data = asha->cb_user_data; if (asha->state == ASHA_STARTING) { if (status == 0) { asha->state = ASHA_STARTED; DBG("ASHA start complete"); asha_set_send_status(asha, true); } else { bt_asha_state_reset(asha); DBG("ASHA start failed"); } } else if (asha->state == ASHA_STOPPING) { /* We reset our state, regardless */ bt_asha_state_reset(asha); DBG("ASHA stop %s", status == 0 ? "complete" : "failed"); } if (cb) { cb(status, cb_user_data); asha->cb = NULL; asha->cb_user_data = NULL; } } static void handle_characteristic(struct gatt_db_attribute *attr, void *user_data) { struct bt_asha *asha = user_data; uint16_t value_handle; bt_uuid_t uuid; char uuid_str[MAX_LEN_UUID_STR]; if (!gatt_db_attribute_get_char_data(attr, NULL, &value_handle, NULL, NULL, &uuid)) { error("Failed to obtain characteristic data"); return; } bt_uuid_to_string(&uuid, uuid_str, sizeof(uuid_str)); if (uuid_cmp(ASHA_CHRC_LE_PSM_OUT_UUID, &uuid)) { DBG("Got chrc %s/0x%x: LE_PSM_ID", uuid_str, value_handle); if (!bt_gatt_client_read_value(asha->client, value_handle, read_psm, asha, NULL)) DBG("Failed to send request to read battery level"); } else if (uuid_cmp(ASHA_CHRC_READ_ONLY_PROPERTIES_UUID, &uuid)) { DBG("Got chrc %s/0x%x: READ_ONLY_PROPERTIES", uuid_str, value_handle); if (!bt_gatt_client_read_value(asha->client, value_handle, read_rops, asha, NULL)) DBG("Failed to send request for readonly properties"); } else if (uuid_cmp(ASHA_CHRC_AUDIO_CONTROL_POINT_UUID, &uuid)) { DBG("Got chrc %s/0x%x: AUDIO_CONTROL_POINT", uuid_str, value_handle); /* Store this for later writes */ asha->acp_handle = value_handle; } else if (uuid_cmp(ASHA_CHRC_VOLUME_UUID, &uuid)) { DBG("Got chrc %s/0x%x: VOLUME", uuid_str, value_handle); /* Store this for later writes */ asha->volume_handle = value_handle; } else if (uuid_cmp(ASHA_CHRC_AUDIO_STATUS_UUID, &uuid)) { DBG("Got chrc %s/0x%x: AUDIO_STATUS", uuid_str, value_handle); asha->status_notify_id = bt_gatt_client_register_notify(asha->client, value_handle, audio_status_register, audio_status_notify, asha, NULL); if (!asha->status_notify_id) DBG("Failed to send request to notify AudioStatus"); } else { DBG("Unsupported characteristic: %s", uuid_str); } } static void foreach_asha_service(struct gatt_db_attribute *attr, void *user_data) { struct bt_asha *asha = user_data; DBG("Found ASHA GATT service"); asha->attr = attr; gatt_db_service_set_claimed(attr, true); gatt_db_service_foreach_char(asha->attr, handle_characteristic, asha); } bool bt_asha_probe(struct bt_asha *asha, struct gatt_db *db, struct bt_gatt_client *client) { bt_uuid_t asha_uuid; asha->db = gatt_db_ref(db); asha->client = bt_gatt_client_clone(client); bt_uuid16_create(&asha_uuid, ASHA_SERVICE); gatt_db_foreach_service(db, &asha_uuid, foreach_asha_service, asha); if (!asha->attr) { error("ASHA attribute not found"); bt_asha_reset(asha); return false; } if (!asha_devices) asha_devices = queue_new(); return true; } bluez-5.82/src/shared/PaxHeaders/mainloop.c0000644000000000000000000000005014015011623015656 xustar0020 atime=1743516174 20 ctime=1743591278 bluez-5.82/src/shared/mainloop.c0000644000000000000000000001330014015011623015334 0ustar00rootroot// SPDX-License-Identifier: LGPL-2.1-or-later /* * * BlueZ - Bluetooth protocol stack for Linux * * Copyright (C) 2011-2014 Intel Corporation * Copyright (C) 2002-2010 Marcel Holtmann * * */ #ifdef HAVE_CONFIG_H #include #endif #define _GNU_SOURCE #include #include #include #include #include #include #include #include #include #include #include #include #include "mainloop.h" #include "mainloop-notify.h" #define MAX_EPOLL_EVENTS 10 static int epoll_fd; static int epoll_terminate; static int exit_status = EXIT_SUCCESS; struct mainloop_data { int fd; uint32_t events; mainloop_event_func callback; mainloop_destroy_func destroy; void *user_data; }; #define MAX_MAINLOOP_ENTRIES 128 static struct mainloop_data *mainloop_list[MAX_MAINLOOP_ENTRIES]; struct timeout_data { int fd; mainloop_timeout_func callback; mainloop_destroy_func destroy; void *user_data; }; void mainloop_init(void) { unsigned int i; epoll_fd = epoll_create1(EPOLL_CLOEXEC); for (i = 0; i < MAX_MAINLOOP_ENTRIES; i++) mainloop_list[i] = NULL; epoll_terminate = 0; mainloop_notify_init(); } void mainloop_quit(void) { epoll_terminate = 1; mainloop_sd_notify("STOPPING=1"); } void mainloop_exit_success(void) { exit_status = EXIT_SUCCESS; epoll_terminate = 1; } void mainloop_exit_failure(void) { exit_status = EXIT_FAILURE; epoll_terminate = 1; } int mainloop_run(void) { unsigned int i; while (!epoll_terminate) { struct epoll_event events[MAX_EPOLL_EVENTS]; int n, nfds; nfds = epoll_wait(epoll_fd, events, MAX_EPOLL_EVENTS, -1); if (nfds < 0) continue; for (n = 0; n < nfds; n++) { struct mainloop_data *data = events[n].data.ptr; data->callback(data->fd, events[n].events, data->user_data); } } for (i = 0; i < MAX_MAINLOOP_ENTRIES; i++) { struct mainloop_data *data = mainloop_list[i]; mainloop_list[i] = NULL; if (data) { epoll_ctl(epoll_fd, EPOLL_CTL_DEL, data->fd, NULL); if (data->destroy) data->destroy(data->user_data); free(data); } } close(epoll_fd); epoll_fd = 0; mainloop_notify_exit(); return exit_status; } int mainloop_add_fd(int fd, uint32_t events, mainloop_event_func callback, void *user_data, mainloop_destroy_func destroy) { struct mainloop_data *data; struct epoll_event ev; int err; if (fd < 0 || fd > MAX_MAINLOOP_ENTRIES - 1 || !callback) return -EINVAL; data = malloc(sizeof(*data)); if (!data) return -ENOMEM; memset(data, 0, sizeof(*data)); data->fd = fd; data->events = events; data->callback = callback; data->destroy = destroy; data->user_data = user_data; memset(&ev, 0, sizeof(ev)); ev.events = events; ev.data.ptr = data; err = epoll_ctl(epoll_fd, EPOLL_CTL_ADD, data->fd, &ev); if (err < 0) { free(data); return err; } mainloop_list[fd] = data; return 0; } int mainloop_modify_fd(int fd, uint32_t events) { struct mainloop_data *data; struct epoll_event ev; int err; if (fd < 0 || fd > MAX_MAINLOOP_ENTRIES - 1) return -EINVAL; data = mainloop_list[fd]; if (!data) return -ENXIO; memset(&ev, 0, sizeof(ev)); ev.events = events; ev.data.ptr = data; err = epoll_ctl(epoll_fd, EPOLL_CTL_MOD, data->fd, &ev); if (err < 0) return err; data->events = events; return 0; } int mainloop_remove_fd(int fd) { struct mainloop_data *data; int err; if (fd < 0 || fd > MAX_MAINLOOP_ENTRIES - 1) return -EINVAL; data = mainloop_list[fd]; if (!data) return -ENXIO; mainloop_list[fd] = NULL; err = epoll_ctl(epoll_fd, EPOLL_CTL_DEL, data->fd, NULL); if (data->destroy) data->destroy(data->user_data); free(data); return err; } static void timeout_destroy(void *user_data) { struct timeout_data *data = user_data; close(data->fd); data->fd = -1; if (data->destroy) data->destroy(data->user_data); free(data); } static void timeout_callback(int fd, uint32_t events, void *user_data) { struct timeout_data *data = user_data; uint64_t expired; ssize_t result; if (events & (EPOLLERR | EPOLLHUP)) return; result = read(data->fd, &expired, sizeof(expired)); if (result != sizeof(expired)) return; if (data->callback) data->callback(data->fd, data->user_data); } static inline int timeout_set(int fd, unsigned int msec) { struct itimerspec itimer; unsigned int sec = msec / 1000; memset(&itimer, 0, sizeof(itimer)); itimer.it_interval.tv_sec = 0; itimer.it_interval.tv_nsec = 0; itimer.it_value.tv_sec = sec; itimer.it_value.tv_nsec = (msec - (sec * 1000)) * 1000 * 1000; return timerfd_settime(fd, 0, &itimer, NULL); } int mainloop_add_timeout(unsigned int msec, mainloop_timeout_func callback, void *user_data, mainloop_destroy_func destroy) { struct timeout_data *data; if (!callback) return -EINVAL; data = malloc(sizeof(*data)); if (!data) return -ENOMEM; memset(data, 0, sizeof(*data)); data->callback = callback; data->destroy = destroy; data->user_data = user_data; data->fd = timerfd_create(CLOCK_MONOTONIC, TFD_NONBLOCK | TFD_CLOEXEC); if (data->fd < 0) { free(data); return -EIO; } if (msec > 0) { if (timeout_set(data->fd, msec) < 0) { close(data->fd); free(data); return -EIO; } } if (mainloop_add_fd(data->fd, EPOLLIN | EPOLLONESHOT, timeout_callback, data, timeout_destroy) < 0) { close(data->fd); free(data); return -EIO; } return data->fd; } int mainloop_modify_timeout(int id, unsigned int msec) { if (msec > 0) { if (timeout_set(id, msec) < 0) return -EIO; } if (mainloop_modify_fd(id, EPOLLIN | EPOLLONESHOT) < 0) return -EIO; return 0; } int mainloop_remove_timeout(int id) { return mainloop_remove_fd(id); } bluez-5.82/src/shared/PaxHeaders/timeout-mainloop.c0000644000000000000000000000005014031556405017354 xustar0020 atime=1743516173 20 ctime=1743591278 bluez-5.82/src/shared/timeout-mainloop.c0000644000000000000000000000301714031556405017036 0ustar00rootroot// SPDX-License-Identifier: LGPL-2.1-or-later /* * * BlueZ - Bluetooth protocol stack for Linux * * Copyright (C) 2014 Intel Corporation. All rights reserved. * * */ #include #include "mainloop.h" #include "util.h" #include "timeout.h" struct timeout_data { int id; timeout_func_t func; timeout_destroy_func_t destroy; unsigned int timeout; void *user_data; }; static void timeout_callback(int id, void *user_data) { struct timeout_data *data = user_data; if (data->func(data->user_data) && !mainloop_modify_timeout(data->id, data->timeout)) return; mainloop_remove_timeout(data->id); } static void timeout_destroy(void *user_data) { struct timeout_data *data = user_data; if (data->destroy) data->destroy(data->user_data); free(data); } unsigned int timeout_add(unsigned int timeout, timeout_func_t func, void *user_data, timeout_destroy_func_t destroy) { struct timeout_data *data; data = new0(struct timeout_data, 1); data->func = func; data->user_data = user_data; data->timeout = timeout; data->destroy = destroy; data->id = mainloop_add_timeout(timeout, timeout_callback, data, timeout_destroy); if (data->id < 0) { free(data); return 0; } return (unsigned int) data->id; } void timeout_remove(unsigned int id) { if (!id) return; mainloop_remove_timeout((int) id); } unsigned int timeout_add_seconds(unsigned int timeout, timeout_func_t func, void *user_data, timeout_destroy_func_t destroy) { return timeout_add(timeout * 1000, func, user_data, destroy); } bluez-5.82/src/shared/PaxHeaders/hci.c0000644000000000000000000000005014766002272014620 xustar0020 atime=1743515578 20 ctime=1743591277 bluez-5.82/src/shared/hci.c0000644000000000000000000002744714766002272014317 0ustar00rootroot// SPDX-License-Identifier: LGPL-2.1-or-later /* * * BlueZ - Bluetooth protocol stack for Linux * * Copyright (C) 2012-2014 Intel Corporation. All rights reserved. * * */ #ifdef HAVE_CONFIG_H #include #endif #include #include #include #include #include #include #include #include #include #include #include "monitor/bt.h" #include "src/shared/mainloop.h" #include "src/shared/io.h" #include "src/shared/util.h" #include "src/shared/queue.h" #include "src/shared/hci.h" #define BTPROTO_HCI 1 struct sockaddr_hci { sa_family_t hci_family; unsigned short hci_dev; unsigned short hci_channel; }; #define HCI_CHANNEL_RAW 0 #define HCI_CHANNEL_USER 1 #define SOL_HCI 0 #define HCI_FILTER 2 struct hci_filter { uint32_t type_mask; uint32_t event_mask[2]; uint16_t opcode; }; struct bt_hci { int ref_count; struct io *io; bool is_stream; bool writer_active; uint8_t num_cmds; unsigned int next_cmd_id; unsigned int next_evt_id; struct queue *cmd_queue; struct queue *rsp_queue; struct queue *evt_list; struct queue *data_queue; }; struct cmd { unsigned int id; uint16_t opcode; void *data; uint8_t size; bt_hci_callback_func_t callback; bt_hci_destroy_func_t destroy; void *user_data; }; struct evt { unsigned int id; uint8_t event; bt_hci_callback_func_t callback; bt_hci_destroy_func_t destroy; void *user_data; }; struct data { uint8_t type; uint16_t handle; void *data; uint8_t size; }; static void cmd_free(void *data) { struct cmd *cmd = data; if (cmd->destroy) cmd->destroy(cmd->user_data); free(cmd->data); free(cmd); } static void evt_free(void *data) { struct evt *evt = data; if (evt->destroy) evt->destroy(evt->user_data); free(evt); } static void data_free(void *data) { struct data *d = data; free(d->data); free(d); } static void send_command(struct bt_hci *hci, uint16_t opcode, void *data, uint8_t size) { uint8_t type = BT_H4_CMD_PKT; struct bt_hci_cmd_hdr hdr; struct iovec iov[3]; int iovcnt; if (hci->num_cmds < 1) return; hdr.opcode = cpu_to_le16(opcode); hdr.plen = size; iov[0].iov_base = &type; iov[0].iov_len = 1; iov[1].iov_base = &hdr; iov[1].iov_len = sizeof(hdr); if (size > 0) { iov[2].iov_base = data; iov[2].iov_len = size; iovcnt = 3; } else iovcnt = 2; if (io_send(hci->io, iov, iovcnt) < 0) return; hci->num_cmds--; } static void send_data(struct bt_hci *hci, uint8_t type, uint16_t handle, void *data, uint16_t size) { struct iovec iov[3]; struct bt_hci_acl_hdr hdr; hdr.handle = cpu_to_le16(handle); hdr.dlen = cpu_to_le16(size); iov[0].iov_base = &type; iov[0].iov_len = 1; iov[1].iov_base = &hdr; iov[1].iov_len = sizeof(hdr); iov[2].iov_base = data; iov[2].iov_len = size; io_send(hci->io, iov, 3); } static bool io_write_callback(struct io *io, void *user_data) { struct bt_hci *hci = user_data; struct cmd *cmd; struct data *data; if (hci->num_cmds) { cmd = queue_pop_head(hci->cmd_queue); if (cmd) { send_command(hci, cmd->opcode, cmd->data, cmd->size); queue_push_tail(hci->rsp_queue, cmd); } } data = queue_pop_head(hci->data_queue); if (data) send_data(hci, data->type, data->handle, data->data, data->size); hci->writer_active = false; return false; } static void wakeup_writer(struct bt_hci *hci) { if (hci->writer_active) return; if (queue_isempty(hci->cmd_queue) && queue_isempty(hci->data_queue)) return; if (!io_set_write_handler(hci->io, io_write_callback, hci, NULL)) return; hci->writer_active = true; } static bool match_cmd_opcode(const void *a, const void *b) { const struct cmd *cmd = a; uint16_t opcode = PTR_TO_UINT(b); return cmd->opcode == opcode; } static void process_response(struct bt_hci *hci, uint16_t opcode, const void *data, size_t size) { struct cmd *cmd; if (opcode == BT_HCI_CMD_NOP) { wakeup_writer(hci); return; } cmd = queue_remove_if(hci->rsp_queue, match_cmd_opcode, UINT_TO_PTR(opcode)); if (!cmd) return; /* Take a reference before calling the callback since that can unref * its reference destroying the instance. */ bt_hci_ref(hci); if (cmd->callback) cmd->callback(data, size, cmd->user_data); cmd_free(cmd); wakeup_writer(hci); bt_hci_unref(hci); } static void process_notify(void *data, void *user_data) { struct bt_hci_evt_hdr *hdr = user_data; struct evt *evt = data; if (evt->event == hdr->evt) evt->callback(user_data + sizeof(struct bt_hci_evt_hdr), hdr->plen, evt->user_data); } static void process_event(struct bt_hci *hci, const void *data, size_t size) { const struct bt_hci_evt_hdr *hdr = data; const struct bt_hci_evt_cmd_complete *cc; const struct bt_hci_evt_cmd_status *cs; if (size < sizeof(struct bt_hci_evt_hdr)) return; data += sizeof(struct bt_hci_evt_hdr); size -= sizeof(struct bt_hci_evt_hdr); if (hdr->plen != size) return; switch (hdr->evt) { case BT_HCI_EVT_CMD_COMPLETE: if (size < sizeof(*cc)) return; cc = data; hci->num_cmds = cc->ncmd; process_response(hci, le16_to_cpu(cc->opcode), data + sizeof(*cc), size - sizeof(*cc)); break; case BT_HCI_EVT_CMD_STATUS: if (size < sizeof(*cs)) return; cs = data; hci->num_cmds = cs->ncmd; process_response(hci, le16_to_cpu(cs->opcode), &cs->status, 1); break; default: queue_foreach(hci->evt_list, process_notify, (void *) hdr); break; } } static bool io_read_callback(struct io *io, void *user_data) { struct bt_hci *hci = user_data; uint8_t buf[512]; ssize_t len; int fd; fd = io_get_fd(hci->io); if (fd < 0) return false; if (hci->is_stream) return false; len = read(fd, buf, sizeof(buf)); if (len < 0) return false; if (len < 1) return true; switch (buf[0]) { case BT_H4_EVT_PKT: process_event(hci, buf + 1, len - 1); break; } return true; } static struct bt_hci *create_hci(int fd) { struct bt_hci *hci; if (fd < 0) return NULL; hci = new0(struct bt_hci, 1); hci->io = io_new(fd); if (!hci->io) { free(hci); return NULL; } hci->is_stream = true; hci->writer_active = false; hci->num_cmds = 1; hci->next_cmd_id = 1; hci->next_evt_id = 1; hci->cmd_queue = queue_new(); hci->rsp_queue = queue_new(); hci->evt_list = queue_new(); hci->data_queue = queue_new(); if (!io_set_read_handler(hci->io, io_read_callback, hci, NULL)) { queue_destroy(hci->evt_list, NULL); queue_destroy(hci->rsp_queue, NULL); queue_destroy(hci->cmd_queue, NULL); queue_destroy(hci->data_queue, NULL); io_destroy(hci->io); free(hci); return NULL; } return bt_hci_ref(hci); } struct bt_hci *bt_hci_new(int fd) { struct bt_hci *hci; hci = create_hci(fd); if (!hci) return NULL; return hci; } static int create_socket(uint16_t index, uint16_t channel) { struct sockaddr_hci addr; int fd; fd = socket(PF_BLUETOOTH, SOCK_RAW | SOCK_CLOEXEC | SOCK_NONBLOCK, BTPROTO_HCI); if (fd < 0) return -1; memset(&addr, 0, sizeof(addr)); addr.hci_family = AF_BLUETOOTH; addr.hci_dev = index; addr.hci_channel = channel; if (bind(fd, (struct sockaddr *) &addr, sizeof(addr)) < 0) { close(fd); return -1; } return fd; } struct bt_hci *bt_hci_new_user_channel(uint16_t index) { struct bt_hci *hci; int fd; fd = create_socket(index, HCI_CHANNEL_USER); if (fd < 0) { printf("Unable to create user channel socket: %s(%d)\n", strerror(errno), -errno); return NULL; } hci = create_hci(fd); if (!hci) { close(fd); return NULL; } hci->is_stream = false; bt_hci_set_close_on_unref(hci, true); return hci; } struct bt_hci *bt_hci_new_raw_device(uint16_t index) { struct bt_hci *hci; struct hci_filter flt; int fd; fd = create_socket(index, HCI_CHANNEL_RAW); if (fd < 0) return NULL; memset(&flt, 0, sizeof(flt)); flt.type_mask = 1 << BT_H4_EVT_PKT; flt.event_mask[0] = 0xffffffff; flt.event_mask[1] = 0xffffffff; if (setsockopt(fd, SOL_HCI, HCI_FILTER, &flt, sizeof(flt)) < 0) { close(fd); return NULL; } hci = create_hci(fd); if (!hci) { close(fd); return NULL; } hci->is_stream = false; bt_hci_set_close_on_unref(hci, true); return hci; } struct bt_hci *bt_hci_ref(struct bt_hci *hci) { if (!hci) return NULL; __sync_fetch_and_add(&hci->ref_count, 1); return hci; } void bt_hci_unref(struct bt_hci *hci) { if (!hci) return; if (__sync_sub_and_fetch(&hci->ref_count, 1)) return; queue_destroy(hci->evt_list, evt_free); queue_destroy(hci->cmd_queue, cmd_free); queue_destroy(hci->rsp_queue, cmd_free); queue_destroy(hci->data_queue, data_free); io_destroy(hci->io); free(hci); } bool bt_hci_set_close_on_unref(struct bt_hci *hci, bool do_close) { if (!hci) return false; return io_set_close_on_destroy(hci->io, do_close); } unsigned int bt_hci_send(struct bt_hci *hci, uint16_t opcode, const void *data, uint8_t size, bt_hci_callback_func_t callback, void *user_data, bt_hci_destroy_func_t destroy) { struct cmd *cmd; if (!hci) return 0; cmd = new0(struct cmd, 1); cmd->opcode = opcode; cmd->size = size; if (cmd->size > 0) { cmd->data = malloc(cmd->size); if (!cmd->data) { free(cmd); return 0; } memcpy(cmd->data, data, cmd->size); } if (hci->next_cmd_id < 1) hci->next_cmd_id = 1; cmd->id = hci->next_cmd_id++; cmd->callback = callback; cmd->destroy = destroy; cmd->user_data = user_data; if (!queue_push_tail(hci->cmd_queue, cmd)) { free(cmd->data); free(cmd); return 0; } wakeup_writer(hci); return cmd->id; } static bool match_cmd_id(const void *a, const void *b) { const struct cmd *cmd = a; unsigned int id = PTR_TO_UINT(b); return cmd->id == id; } bool bt_hci_cancel(struct bt_hci *hci, unsigned int id) { struct cmd *cmd; if (!hci || !id) return false; cmd = queue_remove_if(hci->cmd_queue, match_cmd_id, UINT_TO_PTR(id)); if (!cmd) { cmd = queue_remove_if(hci->rsp_queue, match_cmd_id, UINT_TO_PTR(id)); if (!cmd) return false; } cmd_free(cmd); wakeup_writer(hci); return true; } bool bt_hci_flush(struct bt_hci *hci) { if (!hci) return false; if (hci->writer_active) { io_set_write_handler(hci->io, NULL, NULL, NULL); hci->writer_active = false; } queue_remove_all(hci->cmd_queue, NULL, NULL, cmd_free); queue_remove_all(hci->rsp_queue, NULL, NULL, cmd_free); queue_remove_all(hci->data_queue, NULL, NULL, data_free); return true; } unsigned int bt_hci_register(struct bt_hci *hci, uint8_t event, bt_hci_callback_func_t callback, void *user_data, bt_hci_destroy_func_t destroy) { struct evt *evt; if (!hci) return 0; evt = new0(struct evt, 1); evt->event = event; if (hci->next_evt_id < 1) hci->next_evt_id = 1; evt->id = hci->next_evt_id++; evt->callback = callback; evt->destroy = destroy; evt->user_data = user_data; if (!queue_push_tail(hci->evt_list, evt)) { free(evt); return 0; } return evt->id; } bool bt_hci_send_data(struct bt_hci *hci, uint8_t type, uint16_t handle, const void *data, uint8_t size) { struct data *d; if (!hci) return false; /* Check if type really reflects to a data packet */ switch (type) { case BT_H4_ACL_PKT: case BT_H4_SCO_PKT: case BT_H4_ISO_PKT: break; default: return false; } d = new0(struct data, 1); d->type = type; d->handle = handle; d->size = size; if (d->size > 0) { d->data = util_memdup(data, d->size); if (!d->data) { free(d); return false; } } if (!queue_push_tail(hci->data_queue, d)) { free(d->data); free(d); return false; } wakeup_writer(hci); return true; } static bool match_evt_id(const void *a, const void *b) { const struct evt *evt = a; unsigned int id = PTR_TO_UINT(b); return evt->id == id; } bool bt_hci_unregister(struct bt_hci *hci, unsigned int id) { struct evt *evt; if (!hci || !id) return false; evt = queue_remove_if(hci->evt_list, match_evt_id, UINT_TO_PTR(id)); if (!evt) return false; evt_free(evt); return true; } bluez-5.82/src/shared/PaxHeaders/bap.c0000644000000000000000000000005014773211264014620 xustar0020 atime=1743590089 20 ctime=1743591277 bluez-5.82/src/shared/bap.c0000644000000000000000000051547514773211264014322 0ustar00rootroot// SPDX-License-Identifier: LGPL-2.1-or-later /* * * BlueZ - Bluetooth protocol stack for Linux * * Copyright (C) 2022 Intel Corporation. All rights reserved. * Copyright 2023-2025 NXP * */ #define _GNU_SOURCE #include #include #include #include #include #include #include "lib/bluetooth.h" #include "lib/uuid.h" #include "src/shared/io.h" #include "src/shared/queue.h" #include "src/shared/util.h" #include "src/shared/timeout.h" #include "src/shared/att.h" #include "src/shared/gatt-db.h" #include "src/shared/gatt-server.h" #include "src/shared/gatt-client.h" #include "src/shared/bap.h" #include "src/shared/ascs.h" #include "src/shared/bap-debug.h" /* Maximum number of ASE(s) */ #define NUM_SINKS 2 #define NUM_SOURCE 2 #define NUM_ASES (NUM_SINKS + NUM_SOURCE) #define ASE_UUID(_id) (_id < NUM_SINKS ? ASE_SINK_UUID : ASE_SOURCE_UUID) #define DBG(_bap, fmt, arg...) \ bap_debug(_bap, "%s:%s() " fmt, __FILE__, __func__, ## arg) #define LTV(_type, _bytes...) \ { \ .len = 1 + sizeof((uint8_t []) { _bytes }), \ .type = _type, \ .data = { _bytes }, \ } #define BAP_PROCESS_TIMEOUT 10 #define BAP_FREQ_LTV_TYPE 1 #define BAP_DURATION_LTV_TYPE 2 #define BAP_CHANNEL_ALLOCATION_LTV_TYPE 3 #define BAP_FRAME_LEN_LTV_TYPE 4 #define CODEC_SPECIFIC_CONFIGURATION_MASK (\ (1<db == db); } unsigned int bt_bap_pac_register(struct bt_bap *bap, bt_bap_pac_func_t added, bt_bap_pac_func_t removed, void *user_data, bt_bap_destroy_func_t destroy) { struct bt_bap_pac_changed *changed; static unsigned int id; if (!bap) return 0; changed = new0(struct bt_bap_pac_changed, 1); changed->id = ++id ? id : ++id; changed->added = added; changed->removed = removed; changed->destroy = destroy; changed->data = user_data; queue_push_tail(bap->pac_cbs, changed); return changed->id; } static void pac_changed_free(void *data) { struct bt_bap_pac_changed *changed = data; if (changed->destroy) changed->destroy(changed->data); free(changed); } static bool match_pac_changed_id(const void *data, const void *match_data) { const struct bt_bap_pac_changed *changed = data; unsigned int id = PTR_TO_UINT(match_data); return (changed->id == id); } bool bt_bap_pac_unregister(struct bt_bap *bap, unsigned int id) { struct bt_bap_pac_changed *changed; if (!bap) return false; changed = queue_remove_if(bap->pac_cbs, match_pac_changed_id, UINT_TO_PTR(id)); if (!changed) return false; pac_changed_free(changed); return true; } static void pac_foreach(void *data, void *user_data) { struct bt_bap_pac *pac = data; struct iovec *iov = user_data; struct bt_pacs_read_rsp *rsp; struct bt_pac *p; struct bt_pac_metadata *meta; if (!iov->iov_len) { rsp = util_iov_push(iov, sizeof(*rsp)); rsp->num_pac = 0; } else rsp = iov->iov_base; rsp->num_pac++; p = util_iov_push(iov, sizeof(*p)); p->codec.id = pac->codec.id; p->codec.cid = cpu_to_le16(pac->codec.cid); p->codec.vid = cpu_to_le16(pac->codec.vid); if (pac->data) { p->cc_len = pac->data->iov_len; util_iov_push_mem(iov, p->cc_len, pac->data->iov_base); } else p->cc_len = 0; meta = util_iov_push(iov, sizeof(*meta)); if (pac->metadata) { meta->len = pac->metadata->iov_len; util_iov_push_mem(iov, meta->len, pac->metadata->iov_base); } else meta->len = 0; } static void pacs_sink_read(struct gatt_db_attribute *attrib, unsigned int id, uint16_t offset, uint8_t opcode, struct bt_att *att, void *user_data) { struct bt_pacs *pacs = user_data; struct bt_bap_db *bdb = pacs->bdb; struct iovec iov; uint8_t value[512]; memset(value, 0, sizeof(value)); iov.iov_base = value; iov.iov_len = 0; queue_foreach(bdb->sinks, pac_foreach, &iov); queue_foreach(bdb->broadcast_sinks, pac_foreach, &iov); gatt_db_attribute_read_result(attrib, id, 0, iov.iov_base, iov.iov_len); } static void pacs_sink_loc_read(struct gatt_db_attribute *attrib, unsigned int id, uint16_t offset, uint8_t opcode, struct bt_att *att, void *user_data) { struct bt_pacs *pacs = user_data; uint32_t value = cpu_to_le32(pacs->sink_loc_value); gatt_db_attribute_read_result(attrib, id, 0, (void *) &value, sizeof(value)); } static void pacs_source_read(struct gatt_db_attribute *attrib, unsigned int id, uint16_t offset, uint8_t opcode, struct bt_att *att, void *user_data) { struct bt_pacs *pacs = user_data; struct bt_bap_db *bdb = pacs->bdb; struct iovec iov; uint8_t value[512]; memset(value, 0, sizeof(value)); iov.iov_base = value; iov.iov_len = 0; queue_foreach(bdb->sources, pac_foreach, &iov); gatt_db_attribute_read_result(attrib, id, 0, iov.iov_base, iov.iov_len); } static void pacs_source_loc_read(struct gatt_db_attribute *attrib, unsigned int id, uint16_t offset, uint8_t opcode, struct bt_att *att, void *user_data) { struct bt_pacs *pacs = user_data; uint32_t value = cpu_to_le32(pacs->source_loc_value); gatt_db_attribute_read_result(attrib, id, 0, (void *) &value, sizeof(value)); } static void pacs_context_read(struct gatt_db_attribute *attrib, unsigned int id, uint16_t offset, uint8_t opcode, struct bt_att *att, void *user_data) { struct bt_pacs *pacs = user_data; struct bt_pacs_context ctx = { .snk = cpu_to_le16(pacs->sink_context_value), .src = cpu_to_le16(pacs->source_context_value) }; gatt_db_attribute_read_result(attrib, id, 0, (void *) &ctx, sizeof(ctx)); } static void pacs_supported_context_read(struct gatt_db_attribute *attrib, unsigned int id, uint16_t offset, uint8_t opcode, struct bt_att *att, void *user_data) { struct bt_pacs *pacs = user_data; struct bt_pacs_context ctx = { .snk = cpu_to_le16(pacs->supported_sink_context_value), .src = cpu_to_le16(pacs->supported_source_context_value) }; gatt_db_attribute_read_result(attrib, id, 0, (void *) &ctx, sizeof(ctx)); } static struct bt_pacs *pacs_new(struct gatt_db *db) { struct bt_pacs *pacs; bt_uuid_t uuid; if (!db) return NULL; pacs = new0(struct bt_pacs, 1); /* Populate DB with PACS attributes */ bt_uuid16_create(&uuid, PACS_UUID); pacs->service = gatt_db_add_service(db, &uuid, true, 19); bt_uuid16_create(&uuid, PAC_SINK_CHRC_UUID); pacs->sink = gatt_db_service_add_characteristic(pacs->service, &uuid, BT_ATT_PERM_READ, BT_GATT_CHRC_PROP_READ | BT_GATT_CHRC_PROP_NOTIFY, pacs_sink_read, NULL, pacs); pacs->sink_ccc = gatt_db_service_add_ccc(pacs->service, BT_ATT_PERM_READ | BT_ATT_PERM_WRITE); bt_uuid16_create(&uuid, PAC_SINK_LOC_CHRC_UUID); pacs->sink_loc = gatt_db_service_add_characteristic(pacs->service, &uuid, BT_ATT_PERM_READ, BT_GATT_CHRC_PROP_READ | BT_GATT_CHRC_PROP_NOTIFY, pacs_sink_loc_read, NULL, pacs); pacs->sink_loc_ccc = gatt_db_service_add_ccc(pacs->service, BT_ATT_PERM_READ | BT_ATT_PERM_WRITE); bt_uuid16_create(&uuid, PAC_SOURCE_CHRC_UUID); pacs->source = gatt_db_service_add_characteristic(pacs->service, &uuid, BT_ATT_PERM_READ, BT_GATT_CHRC_PROP_READ | BT_GATT_CHRC_PROP_NOTIFY, pacs_source_read, NULL, pacs); pacs->source_ccc = gatt_db_service_add_ccc(pacs->service, BT_ATT_PERM_READ | BT_ATT_PERM_WRITE); bt_uuid16_create(&uuid, PAC_SOURCE_LOC_CHRC_UUID); pacs->source_loc = gatt_db_service_add_characteristic(pacs->service, &uuid, BT_ATT_PERM_READ, BT_GATT_CHRC_PROP_READ | BT_GATT_CHRC_PROP_NOTIFY, pacs_source_loc_read, NULL, pacs); pacs->source_loc_ccc = gatt_db_service_add_ccc(pacs->service, BT_ATT_PERM_READ | BT_ATT_PERM_WRITE); bt_uuid16_create(&uuid, PAC_CONTEXT); pacs->context = gatt_db_service_add_characteristic(pacs->service, &uuid, BT_ATT_PERM_READ, BT_GATT_CHRC_PROP_READ | BT_GATT_CHRC_PROP_NOTIFY, pacs_context_read, NULL, pacs); pacs->context_ccc = gatt_db_service_add_ccc(pacs->service, BT_ATT_PERM_READ | BT_ATT_PERM_WRITE); bt_uuid16_create(&uuid, PAC_SUPPORTED_CONTEXT); pacs->supported_context = gatt_db_service_add_characteristic(pacs->service, &uuid, BT_ATT_PERM_READ, BT_GATT_CHRC_PROP_READ | BT_GATT_CHRC_PROP_NOTIFY, pacs_supported_context_read, NULL, pacs); pacs->supported_context_ccc = gatt_db_service_add_ccc(pacs->service, BT_ATT_PERM_READ | BT_ATT_PERM_WRITE); gatt_db_service_set_active(pacs->service, true); return pacs; } static void bap_debug(struct bt_bap *bap, const char *format, ...) { va_list ap; if (!bap || !format || !bap->debug_func) return; va_start(ap, format); util_debug_va(bap->debug_func, bap->debug_data, format, ap); va_end(ap); } static void bap_disconnected(int err, void *user_data) { struct bt_bap *bap = user_data; bap->disconn_id = 0; DBG(bap, "bap %p disconnected err %d", bap, err); bt_bap_detach(bap); } struct bt_bap *bt_bap_get_session(struct bt_att *att, struct gatt_db *db) { const struct queue_entry *entry; struct bt_bap *bap; for (entry = queue_get_entries(sessions); entry; entry = entry->next) { struct bt_bap *bap = entry->data; if (att == bt_bap_get_att(bap)) return bap; } bap = bt_bap_new(db, NULL); if (!bap) return NULL; bap->att = att; bt_bap_attach(bap, NULL); return bap; } static bool bap_endpoint_match(const void *data, const void *match_data) { const struct bt_bap_endpoint *ep = data; const struct gatt_db_attribute *attr = match_data; return (ep->attr == attr); } static struct bt_bap_endpoint *bap_endpoint_new(struct bt_bap_db *bdb, struct gatt_db_attribute *attr) { struct bt_bap_endpoint *ep; bt_uuid_t uuid, source, sink; if (!gatt_db_attribute_get_char_data(attr, NULL, NULL, NULL, NULL, &uuid)) return NULL; ep = new0(struct bt_bap_endpoint, 1); ep->bdb = bdb; ep->attr = attr; bt_uuid16_create(&source, ASE_SOURCE_UUID); bt_uuid16_create(&sink, ASE_SINK_UUID); if (!bt_uuid_cmp(&source, &uuid)) ep->dir = BT_BAP_SOURCE; else if (!bt_uuid_cmp(&sink, &uuid)) ep->dir = BT_BAP_SINK; return ep; } static struct bt_bap_endpoint *bap_endpoint_new_broadcast(struct bt_bap_db *bdb, uint8_t type) { struct bt_bap_endpoint *ep; ep = new0(struct bt_bap_endpoint, 1); ep->bdb = bdb; ep->attr = NULL; if (type == BT_BAP_BCAST_SINK) ep->dir = BT_BAP_BCAST_SOURCE; else ep->dir = BT_BAP_BCAST_SINK; return ep; } static struct bt_bap_endpoint *bap_get_endpoint(struct queue *endpoints, struct bt_bap_db *db, struct gatt_db_attribute *attr) { struct bt_bap_endpoint *ep; if (!db || !attr) return NULL; ep = queue_find(endpoints, bap_endpoint_match, attr); if (ep) return ep; ep = bap_endpoint_new(db, attr); if (!ep) return NULL; queue_push_tail(endpoints, ep); return ep; } static bool match_ep_type(const void *data, const void *match_data) { const struct bt_bap_endpoint *ep = data; const uint8_t type = PTR_TO_INT(match_data); return (ep->dir == type); } static struct bt_bap_endpoint *bap_get_endpoint_bcast(struct queue *endpoints, struct bt_bap_db *db, uint8_t type) { struct bt_bap_endpoint *ep; if (!db) return NULL; ep = queue_find(endpoints, match_ep_type, INT_TO_PTR(type)); if (ep) return ep; ep = bap_endpoint_new_broadcast(db, type); if (!ep) return NULL; queue_push_tail(endpoints, ep); return ep; } static bool bap_endpoint_match_id(const void *data, const void *match_data) { const struct bt_bap_endpoint *ep = data; uint8_t id = PTR_TO_UINT(match_data); return (ep->id == id); } static struct bt_bap_endpoint *bap_get_local_endpoint_id(struct bt_bap *bap, uint8_t id) { struct bt_bap_endpoint *ep; struct gatt_db_attribute *attr = NULL; size_t i; if (!bap) return NULL; ep = queue_find(bap->local_eps, bap_endpoint_match_id, UINT_TO_PTR(id)); if (ep) return ep; for (i = 0; i < ARRAY_SIZE(bap->ldb->ascs->ase); i++) { struct bt_ase *ase = bap->ldb->ascs->ase[i]; if (id) { if (ase->id != id) continue; attr = ase->attr; break; } ep = queue_find(bap->local_eps, bap_endpoint_match, ase->attr); if (!ep) { attr = ase->attr; break; } } if (!attr) return NULL; ep = bap_endpoint_new(bap->ldb, attr); if (!ep) return NULL; ep->id = id; queue_push_tail(bap->local_eps, ep); return ep; } static void ascs_ase_read(struct gatt_db_attribute *attrib, unsigned int id, uint16_t offset, uint8_t opcode, struct bt_att *att, void *user_data) { struct bt_ase *ase = user_data; struct bt_bap *bap = NULL; struct bt_bap_endpoint *ep = NULL; struct bt_ascs_ase_status rsp; if (ase) bap = bt_bap_get_session(att, ase->ascs->bdb->db); if (bap) ep = bap_get_endpoint(bap->local_eps, bap->ldb, attrib); if (!ep) { gatt_db_attribute_read_result(attrib, id, BT_ATT_ERROR_UNLIKELY, NULL, 0); return; } memset(&rsp, 0, sizeof(rsp)); /* Initialize Endpoint ID with ASE ID */ if (ase->id != ep->id) ep->id = ase->id; rsp.id = ep->id; rsp.state = ep->state; gatt_db_attribute_read_result(attrib, id, 0, (void *) &rsp, sizeof(rsp)); } static void ase_new(struct bt_ascs *ascs, int i) { struct bt_ase *ase; bt_uuid_t uuid; if (!ascs) return; ase = new0(struct bt_ase, 1); ase->ascs = ascs; ase->id = i + 1; bt_uuid16_create(&uuid, ASE_UUID(i)); ase->attr = gatt_db_service_add_characteristic(ascs->service, &uuid, BT_ATT_PERM_READ, BT_GATT_CHRC_PROP_READ | BT_GATT_CHRC_PROP_NOTIFY, ascs_ase_read, NULL, ase); ase->ccc = gatt_db_service_add_ccc(ascs->service, BT_ATT_PERM_READ | BT_ATT_PERM_WRITE); ascs->ase[i] = ase; } static bool bap_codec_equal(const struct bt_bap_codec *c1, const struct bt_bap_codec *c2) { /* Compare CID and VID if id is 0xff */ if (c1->id == 0xff) return !memcmp(c1, c2, sizeof(*c1)); return c1->id == c2->id; } static void ascs_ase_rsp_add(struct iovec *iov, uint8_t id, uint8_t code, uint8_t reason) { struct bt_ascs_cp_rsp *cp; struct bt_ascs_ase_rsp *rsp; if (!iov) return; cp = iov->iov_base; if (cp->num_ase == 0xff) return; switch (code) { /* If the Response_Code value is 0x01 or 0x02, Number_of_ASEs shall be * set to 0xFF. */ case BT_ASCS_RSP_NOT_SUPPORTED: case BT_ASCS_RSP_TRUNCATED: cp->num_ase = 0xff; break; default: cp->num_ase++; break; } iov->iov_len += sizeof(*rsp); iov->iov_base = realloc(iov->iov_base, iov->iov_len); rsp = iov->iov_base + (iov->iov_len - sizeof(*rsp)); rsp->ase = id; rsp->code = code; rsp->reason = reason; } static void ascs_ase_rsp_success(struct iovec *iov, uint8_t id) { return ascs_ase_rsp_add(iov, id, BT_ASCS_RSP_SUCCESS, BT_ASCS_REASON_NONE); } static void stream_notify_config(struct bt_bap_stream *stream) { struct bt_bap_endpoint *ep = stream->ep; struct bt_bap_pac *lpac = stream->lpac; struct bt_ascs_ase_status *status; struct bt_ascs_ase_status_config *config; size_t len; DBG(stream->bap, "stream %p", stream); len = sizeof(*status) + sizeof(*config) + stream->cc->iov_len; status = malloc(len); memset(status, 0, len); status->id = ep->id; status->state = ep->state; /* Initialize preffered settings if not set */ if (!lpac->qos.phy) lpac->qos.phy = 0x02; if (!lpac->qos.rtn) lpac->qos.rtn = 0x05; if (!lpac->qos.latency) lpac->qos.latency = 10; if (!lpac->qos.pd_min) lpac->qos.pd_min = 20000; if (!lpac->qos.pd_max) lpac->qos.pd_max = 40000; if (!lpac->qos.ppd_min) lpac->qos.ppd_min = lpac->qos.pd_min; if (!lpac->qos.ppd_max) lpac->qos.ppd_max = lpac->qos.pd_max; /* TODO:Add support for setting preffered settings on bt_bap_pac */ config = (void *)status->params; config->framing = lpac->qos.framing; config->phy = lpac->qos.phy; config->rtn = lpac->qos.rtn; config->latency = cpu_to_le16(lpac->qos.latency); put_le24(lpac->qos.pd_min, config->pd_min); put_le24(lpac->qos.pd_max, config->pd_max); put_le24(lpac->qos.ppd_min, config->ppd_min); put_le24(lpac->qos.ppd_max, config->ppd_max); config->codec = lpac->codec; if (config->codec.id == 0x0ff) { config->codec.vid = cpu_to_le16(config->codec.vid); config->codec.cid = cpu_to_le16(config->codec.cid); } config->cc_len = stream->cc->iov_len; memcpy(config->cc, stream->cc->iov_base, stream->cc->iov_len); gatt_db_attribute_notify(ep->attr, (void *) status, len, bt_bap_get_att(stream->bap)); free(status); } static void stream_notify_qos(struct bt_bap_stream *stream) { struct bt_bap_endpoint *ep = stream->ep; struct bt_ascs_ase_status *status; struct bt_ascs_ase_status_qos *qos; size_t len; DBG(stream->bap, "stream %p", stream); len = sizeof(*status) + sizeof(*qos); status = malloc(len); memset(status, 0, len); status->id = ep->id; status->state = ep->state; qos = (void *)status->params; qos->cis_id = stream->qos.ucast.cis_id; qos->cig_id = stream->qos.ucast.cig_id; put_le24(stream->qos.ucast.io_qos.interval, qos->interval); qos->framing = stream->qos.ucast.framing; qos->phy = stream->qos.ucast.io_qos.phy; qos->sdu = cpu_to_le16(stream->qos.ucast.io_qos.sdu); qos->rtn = stream->qos.ucast.io_qos.rtn; qos->latency = cpu_to_le16(stream->qos.ucast.io_qos.latency); put_le24(stream->qos.ucast.delay, qos->pd); gatt_db_attribute_notify(ep->attr, (void *) status, len, bt_bap_get_att(stream->bap)); free(status); } static void stream_notify_metadata(struct bt_bap_stream *stream) { struct bt_bap_endpoint *ep = stream->ep; struct bt_ascs_ase_status *status; struct bt_ascs_ase_status_metadata *meta; size_t len; size_t meta_len = 0; DBG(stream->bap, "stream %p", stream); if (stream->meta) meta_len = stream->meta->iov_len; len = sizeof(*status) + sizeof(*meta) + meta_len; status = malloc(len); memset(status, 0, len); status->id = ep->id; status->state = ep->state; meta = (void *)status->params; meta->cis_id = stream->qos.ucast.cis_id; meta->cig_id = stream->qos.ucast.cig_id; if (stream->meta) { meta->len = stream->meta->iov_len; memcpy(meta->data, stream->meta->iov_base, meta->len); } gatt_db_attribute_notify(ep->attr, (void *) status, len, bt_bap_get_att(stream->bap)); free(status); } static void stream_notify_release(struct bt_bap_stream *stream) { struct bt_bap_endpoint *ep = stream->ep; struct bt_ascs_ase_status status; DBG(stream->bap, "stream %p", stream); memset(&status, 0, sizeof(status)); status.id = ep->id; ep->state = BT_BAP_STREAM_STATE_RELEASING; status.state = ep->state; gatt_db_attribute_notify(ep->attr, (void *)&status, sizeof(status), bt_bap_get_att(stream->bap)); } static struct bt_bap *bt_bap_ref_safe(struct bt_bap *bap) { if (!bap || !bap->ref_count || !queue_find(sessions, NULL, bap)) return NULL; return bt_bap_ref(bap); } static void bap_stream_clear_cfm(struct bt_bap_stream *stream) { if (!stream->lpac->ops || !stream->lpac->ops->clear) return; stream->lpac->ops->clear(stream, stream->lpac->user_data); } static int stream_io_get_fd(struct bt_bap_stream_io *io) { if (!io) return -1; return io_get_fd(io->io); } static void stream_io_free(void *data) { struct bt_bap_stream_io *io = data; int fd; fd = stream_io_get_fd(io); DBG(io->bap, "fd %d", fd); io_destroy(io->io); free(io); /* Shutdown using SHUT_WR as SHUT_RDWR cause the socket to HUP * immediately instead of waiting for Disconnect Complete event. */ shutdown(fd, SHUT_WR); } static void stream_io_unref(struct bt_bap_stream_io *io) { if (!io) return; if (__sync_sub_and_fetch(&io->ref_count, 1)) return; stream_io_free(io); } static void bap_stream_unlink(void *data, void *user_data) { struct bt_bap_stream *stream = data; struct bt_bap_stream *link = user_data; queue_remove(stream->links, link); } static void bap_stream_free(void *data) { struct bt_bap_stream *stream = data; timeout_remove(stream->state_id); queue_destroy(stream->pending_states, NULL); if (stream->ep) stream->ep->stream = NULL; queue_foreach(stream->links, bap_stream_unlink, stream); queue_destroy(stream->links, NULL); stream_io_unref(stream->io); util_iov_free(stream->cc, 1); util_iov_free(stream->meta, 1); free(stream); } static void bap_req_free(void *data) { struct bt_bap_req *req = data; size_t i; queue_destroy(req->group, bap_req_free); for (i = 0; i < req->len; i++) free(req->iov[i].iov_base); free(req->iov); free(req); } static void bap_req_complete(struct bt_bap_req *req, const struct bt_ascs_ase_rsp *rsp) { struct queue *group; if (!req->func) goto done; if (rsp) req->func(req->stream, rsp->code, rsp->reason, req->user_data); else req->func(req->stream, BT_ASCS_RSP_UNSPECIFIED, 0x00, req->user_data); done: /* Detach from request so it can be freed separately */ group = req->group; req->group = NULL; queue_foreach(group, (queue_foreach_func_t)bap_req_complete, (void *)rsp); queue_destroy(group, NULL); bap_req_free(req); } static bool match_req_stream(const void *data, const void *match_data) { const struct bt_bap_req *req = data; return req->stream == match_data; } static void bap_req_abort(void *data) { struct bt_bap_req *req = data; struct bt_bap *bap = req->stream->bap; DBG(bap, "req %p", req); bap_req_complete(req, NULL); } static void bap_abort_stream_req(struct bt_bap *bap, struct bt_bap_stream *stream) { queue_remove_all(bap->reqs, match_req_stream, stream, bap_req_abort); } static void bt_bap_stream_unref(struct bt_bap_stream *stream) { if (!stream) return; if (__sync_sub_and_fetch(&stream->ref_count, 1)) return; bap_stream_free(stream); } static void bap_ucast_detach(struct bt_bap_stream *stream) { struct bt_bap_endpoint *ep = stream->ep; if (!ep) return; DBG(stream->bap, "stream %p ep %p", stream, ep); bap_abort_stream_req(stream->bap, stream); queue_remove(stream->bap->streams, stream); bap_stream_clear_cfm(stream); stream->ep = NULL; ep->stream = NULL; bt_bap_stream_unref(stream); } static void bap_bcast_src_detach(struct bt_bap_stream *stream) { struct bt_bap_endpoint *ep = stream->ep; if (!ep) return; DBG(stream->bap, "stream %p ep %p", stream, ep); queue_remove(stream->bap->streams, stream); bap_stream_clear_cfm(stream); stream->ep = NULL; ep->stream = NULL; bt_bap_stream_unref(stream); } static void bap_bcast_sink_detach(struct bt_bap_stream *stream) { DBG(stream->bap, "stream %p", stream); queue_remove(stream->bap->streams, stream); bap_stream_clear_cfm(stream); bt_bap_stream_unref(stream); } static bool bap_stream_io_link(const void *data, const void *user_data) { struct bt_bap_stream *stream = (void *)data; struct bt_bap_stream *link = (void *)user_data; return !bt_bap_stream_io_link(stream, link); } static void bap_stream_update_io_links(struct bt_bap_stream *stream) { struct bt_bap *bap = stream->bap; DBG(bap, "stream %p", stream); queue_find(bap->streams, bap_stream_io_link, stream); } static bool match_stream_io(const void *data, const void *user_data) { const struct bt_bap_stream *stream = data; const struct bt_bap_stream_io *io = user_data; if (!stream->io) return false; return stream->io == io; } static bool bap_stream_io_detach(struct bt_bap_stream *stream) { struct bt_bap_stream *link; struct bt_bap_stream_io *io; if (!stream->io) return false; DBG(stream->bap, "stream %p", stream); io = stream->io; stream->io = NULL; link = queue_find(stream->links, match_stream_io, io); if (link) { /* Detach link if in QoS state */ if (link->ep->state == BT_ASCS_ASE_STATE_QOS) bap_stream_io_detach(link); } stream_io_unref(io); return true; } static void stream_stop_complete(struct bt_bap_stream *stream, uint8_t code, uint8_t reason, void *user_data) { DBG(stream->bap, "stream %p stop 0x%02x 0x%02x", stream, code, reason); if (stream->ep->state == BT_ASCS_ASE_STATE_DISABLING) bap_stream_io_detach(stream); } static void bap_stream_state_changed(struct bt_bap_stream *stream) { struct bt_bap *bap = stream->bap; const struct queue_entry *entry; /* Pre notification updates */ switch (stream->ep->state) { case BT_ASCS_ASE_STATE_IDLE: break; case BT_ASCS_ASE_STATE_CONFIG: bap_stream_update_io_links(stream); break; case BT_ASCS_ASE_STATE_DISABLING: /* As client, we detach after Receiver Stop Ready */ if (!stream->client) bap_stream_io_detach(stream); break; case BT_ASCS_ASE_STATE_QOS: if (stream->io && !stream->io->connecting) bap_stream_io_detach(stream); else bap_stream_update_io_links(stream); break; case BT_ASCS_ASE_STATE_ENABLING: case BT_ASCS_ASE_STATE_STREAMING: break; } for (entry = queue_get_entries(bap->state_cbs); entry; entry = entry->next) { struct bt_bap_state *state = entry->data; if (state->func) state->func(stream, stream->ep->old_state, stream->ep->state, state->data); } /* Post notification updates */ switch (stream->ep->state) { case BT_ASCS_ASE_STATE_IDLE: if (bap->req && bap->req->stream == stream) { bap_req_complete(bap->req, NULL); bap->req = NULL; } if (stream->ops && stream->ops->detach) stream->ops->detach(stream); break; case BT_ASCS_ASE_STATE_QOS: break; case BT_ASCS_ASE_STATE_ENABLING: if (bt_bap_stream_get_io(stream)) bt_bap_stream_start(stream, NULL, NULL); break; case BT_ASCS_ASE_STATE_DISABLING: /* Send Stop Ready, and detach IO after remote replies */ if (stream->client) bt_bap_stream_stop(stream, stream_stop_complete, NULL); break; } } /* Return false if the stream is being detached */ static bool stream_set_state(struct bt_bap_stream *stream, uint8_t state) { struct bt_bap *bap = stream->bap; /* Check if ref_count is already 0 which means detaching is in * progress. */ bap = bt_bap_ref_safe(bap); if (!bap) { if (stream->ops && stream->ops->detach) stream->ops->detach(stream); return false; } if (stream->ops && stream->ops->set_state) stream->ops->set_state(stream, state); bt_bap_unref(bap); return true; } static void ep_config_cb(struct bt_bap_stream *stream, int err) { if (err) return; stream_set_state(stream, BT_BAP_STREAM_STATE_CONFIG); } static uint8_t stream_config(struct bt_bap_stream *stream, struct iovec *cc, struct iovec *rsp) { struct bt_bap_pac *pac = stream->lpac; DBG(stream->bap, "stream %p", stream); /* TODO: Wait for pac->ops response */ ascs_ase_rsp_success(rsp, stream->ep->id); if (!util_iov_memcmp(stream->cc, cc)) { stream_set_state(stream, BT_BAP_STREAM_STATE_CONFIG); return 0; } util_iov_free(stream->cc, 1); stream->cc = util_iov_dup(cc, 1); if (pac->ops && pac->ops->config) pac->ops->config(stream, cc, NULL, ep_config_cb, pac->user_data); return 0; } static struct bt_bap_req *bap_req_new(struct bt_bap_stream *stream, uint8_t op, struct iovec *iov, size_t len, bt_bap_stream_func_t func, void *user_data) { struct bt_bap_req *req; static unsigned int id; req = new0(struct bt_bap_req, 1); req->id = ++id ? id : ++id; req->stream = stream; req->op = op; req->iov = util_iov_dup(iov, len); req->len = len; req->func = func; req->user_data = user_data; return req; } static uint16_t bap_req_len(struct bt_bap_req *req) { uint16_t len = 0; size_t i; const struct queue_entry *e; for (i = 0; i < req->len; i++) len += req->iov[i].iov_len; e = queue_get_entries(req->group); for (; e; e = e->next) len += bap_req_len(e->data); return len; } static bool match_req(const void *data, const void *match_data) { const struct bt_bap_req *pend = data; const struct bt_bap_req *req = match_data; return pend->op == req->op; } static struct bt_ascs *bap_get_ascs(struct bt_bap *bap) { if (!bap || !bap->rdb) return NULL; if (bap->rdb->ascs) return bap->rdb->ascs; bap->rdb->ascs = new0(struct bt_ascs, 1); bap->rdb->ascs->bdb = bap->rdb; return bap->rdb->ascs; } static void append_group(void *data, void *user_data) { struct bt_bap_req *req = data; struct iovec *iov = user_data; size_t i; for (i = 0; i < req->len; i++) util_iov_push_mem(iov, req->iov[i].iov_len, req->iov[i].iov_base); } static bool bap_send(struct bt_bap *bap, struct bt_bap_req *req) { struct bt_ascs *ascs = bap_get_ascs(bap); int ret; uint16_t handle; struct bt_ascs_ase_hdr hdr; struct iovec iov; size_t i; iov.iov_len = sizeof(hdr) + bap_req_len(req); DBG(bap, "req %p len %u", req, iov.iov_len); if (req->stream && !queue_find(bap->streams, NULL, req->stream)) { DBG(bap, "stream %p detached, aborting op 0x%02x", req->op); return false; } if (!gatt_db_attribute_get_char_data(ascs->ase_cp, NULL, &handle, NULL, NULL, NULL)) { DBG(bap, "Unable to find Control Point"); return false; } iov.iov_base = alloca(iov.iov_len); iov.iov_len = 0; hdr.op = req->op; hdr.num = 1 + queue_length(req->group); util_iov_push_mem(&iov, sizeof(hdr), &hdr); for (i = 0; i < req->len; i++) util_iov_push_mem(&iov, req->iov[i].iov_len, req->iov[i].iov_base); /* Append the request group with the same opcode */ queue_foreach(req->group, append_group, &iov); ret = bt_gatt_client_write_without_response(bap->client, handle, false, iov.iov_base, iov.iov_len); if (!ret) { DBG(bap, "Unable to Write to Control Point"); return false; } bap->req = req; return true; } static bool bap_process_queue(void *data) { struct bt_bap *bap = data; struct bt_bap_req *req; DBG(bap, ""); if (bap->process_id) { timeout_remove(bap->process_id); bap->process_id = 0; } while ((req = queue_pop_head(bap->reqs))) { if (bap_send(bap, req)) break; bap_req_complete(req, NULL); } return false; } static bool bap_queue_req(struct bt_bap *bap, struct bt_bap_req *req) { struct bt_bap_req *pend; struct queue *queue; struct bt_att *att = bt_bap_get_att(bap); uint16_t mtu = bt_att_get_mtu(att); uint16_t len = 2 + bap_req_len(req); if (len > mtu) { DBG(bap, "Unable to queue request: req len %u > %u mtu", len, mtu); return false; } pend = queue_find(bap->reqs, match_req, req); /* Check if req can be grouped together and it fits in the MTU */ if (pend && (bap_req_len(pend) + len < mtu)) { if (!pend->group) pend->group = queue_new(); /* Group requests with the same opcode */ queue = pend->group; } else { queue = bap->reqs; } DBG(bap, "req %p (op 0x%2.2x) queue %p", req, req->op, queue); if (!queue_push_tail(queue, req)) { DBG(bap, "Unable to queue request"); return false; } /* Only attempot to process queue if there is no outstanding request * and it has not been scheduled. */ if (!bap->req && !bap->process_id) bap->process_id = timeout_add(BAP_PROCESS_TIMEOUT, bap_process_queue, bap, NULL); return true; } static void stream_notify(struct bt_bap_stream *stream, uint8_t state) { DBG(stream->bap, "stream %p state %d", stream, state); switch (state) { case BT_ASCS_ASE_STATE_IDLE: break; case BT_ASCS_ASE_STATE_CONFIG: stream_notify_config(stream); break; case BT_ASCS_ASE_STATE_QOS: stream_notify_qos(stream); break; case BT_ASCS_ASE_STATE_ENABLING: case BT_ASCS_ASE_STATE_STREAMING: case BT_ASCS_ASE_STATE_DISABLING: stream_notify_metadata(stream); break; case BT_ASCS_ASE_STATE_RELEASING: stream_notify_release(stream); break; } } static bool stream_notify_state(void *data) { struct bt_bap_stream *stream = data; struct bt_bap_endpoint *ep = stream->ep; uint8_t state; if (stream->state_id) { timeout_remove(stream->state_id); stream->state_id = 0; } /* Notify any pending states before notifying ep->state */ while ((state = PTR_TO_UINT(queue_pop_head(stream->pending_states)))) stream_notify(stream, state); stream_notify(stream, ep->state); return false; } static void bap_ucast_set_state(struct bt_bap_stream *stream, uint8_t state) { struct bt_bap_endpoint *ep = stream->ep; ep->old_state = ep->state; ep->state = state; DBG(stream->bap, "stream %p dir 0x%02x: %s -> %s", stream, bt_bap_stream_get_dir(stream), bt_bap_stream_statestr(stream->ep->old_state), bt_bap_stream_statestr(stream->ep->state)); if (stream->client) goto done; if (!stream->bap->in_cp_write) stream_notify_state(stream); else if (!stream->state_id) stream->state_id = timeout_add(BAP_PROCESS_TIMEOUT, stream_notify_state, stream, NULL); else /* If a state_id is already pending then queue the old one */ queue_push_tail(stream->pending_states, UINT_TO_PTR(ep->old_state)); done: bap_stream_state_changed(stream); } static unsigned int bap_ucast_get_state(struct bt_bap_stream *stream) { return stream->ep->state; } static unsigned int bap_ucast_config(struct bt_bap_stream *stream, struct bt_bap_qos *qos, struct iovec *data, bt_bap_stream_func_t func, void *user_data) { struct iovec iov[2]; struct bt_ascs_config config; uint8_t iovlen = 1; struct bt_bap_req *req; if (!stream->client) { stream_config(stream, data, NULL); return -EINVAL; } memset(&config, 0, sizeof(config)); config.ase = stream->ep->id; config.latency = qos->ucast.target_latency; config.phy = qos->ucast.io_qos.phy; config.codec = stream->rpac->codec; if (config.codec.id == 0xff) { config.codec.cid = cpu_to_le16(config.codec.cid); config.codec.vid = cpu_to_le16(config.codec.vid); } iov[0].iov_base = &config; iov[0].iov_len = sizeof(config); if (data) { if (!bt_bap_debug_config(data->iov_base, data->iov_len, stream->bap->debug_func, stream->bap->debug_data)) return 0; config.cc_len = data->iov_len; iov[1] = *data; iovlen++; } req = bap_req_new(stream, BT_ASCS_CONFIG, iov, iovlen, func, user_data); if (!bap_queue_req(stream->bap, req)) { bap_req_free(req); return 0; } stream->qos = *qos; return req->id; } static unsigned int bap_ucast_qos(struct bt_bap_stream *stream, struct bt_bap_qos *data, bt_bap_stream_func_t func, void *user_data) { struct iovec iov; struct bt_ascs_qos qos; struct bt_bap_req *req; /* Table 3.2: ASE state machine transition * Initiating device - client Only */ if (!stream->client) return 0; memset(&qos, 0, sizeof(qos)); /* TODO: Figure out how to pass these values around */ qos.ase = stream->ep->id; qos.cig = data->ucast.cig_id; qos.cis = data->ucast.cis_id; put_le24(data->ucast.io_qos.interval, qos.interval); qos.framing = data->ucast.framing; qos.phy = data->ucast.io_qos.phy; qos.sdu = cpu_to_le16(data->ucast.io_qos.sdu); qos.rtn = data->ucast.io_qos.rtn; qos.latency = cpu_to_le16(data->ucast.io_qos.latency); put_le24(data->ucast.delay, qos.pd); iov.iov_base = &qos; iov.iov_len = sizeof(qos); req = bap_req_new(stream, BT_ASCS_QOS, &iov, 1, func, user_data); if (!bap_queue_req(stream->bap, req)) { bap_req_free(req); return 0; } stream->qos = *data; return req->id; } static unsigned int bap_stream_metadata(struct bt_bap_stream *stream, uint8_t op, struct iovec *data, bt_bap_stream_func_t func, void *user_data) { struct iovec iov[2]; struct bt_ascs_metadata meta; struct bt_bap_req *req; struct metadata { uint8_t len; uint8_t type; uint8_t data[2]; } ctx = LTV(0x02, 0x01, 0x00); /* Context = Unspecified */ memset(&meta, 0, sizeof(meta)); meta.ase = stream->ep->id; iov[0].iov_base = &meta; iov[0].iov_len = sizeof(meta); if (data) iov[1] = *data; else { iov[1].iov_base = &ctx; iov[1].iov_len = sizeof(ctx); } meta.len = iov[1].iov_len; req = bap_req_new(stream, op, iov, 2, func, user_data); if (!bap_queue_req(stream->bap, req)) { bap_req_free(req); return 0; } return req->id; } static unsigned int bap_bcast_qos(struct bt_bap_stream *stream, struct bt_bap_qos *data, bt_bap_stream_func_t func, void *user_data) { stream->qos = *data; return 1; } static unsigned int bap_bcast_config(struct bt_bap_stream *stream, struct bt_bap_qos *qos, struct iovec *data, bt_bap_stream_func_t func, void *user_data) { stream->qos = *qos; stream->lpac->ops->config(stream, stream->cc, &stream->qos, ep_config_cb, stream->lpac->user_data); return 1; } static void bap_stream_enable_link(void *data, void *user_data) { struct bt_bap_stream *stream = data; struct iovec *metadata = user_data; bap_stream_metadata(stream, BT_ASCS_ENABLE, metadata, NULL, NULL); } static unsigned int bap_ucast_enable(struct bt_bap_stream *stream, bool enable_links, struct iovec *data, bt_bap_stream_func_t func, void *user_data) { int ret; /* Table 3.2: ASE state machine transition * Initiating device - client Only */ if (!stream->client) return 0; ret = bap_stream_metadata(stream, BT_ASCS_ENABLE, data, func, user_data); if (!ret || !enable_links) return ret; queue_foreach(stream->links, bap_stream_enable_link, data); return ret; } static uint8_t stream_start(struct bt_bap_stream *stream, struct iovec *rsp) { DBG(stream->bap, "stream %p", stream); ascs_ase_rsp_success(rsp, stream->ep->id); stream_set_state(stream, BT_BAP_STREAM_STATE_STREAMING); return 0; } static unsigned int bap_ucast_start(struct bt_bap_stream *stream, bt_bap_stream_func_t func, void *user_data) { struct iovec iov; struct bt_ascs_start start; struct bt_bap_req *req; if (!stream->client) { if (stream->ep->dir == BT_BAP_SINK) stream_start(stream, NULL); return 0; } if (stream->ep->dir == BT_BAP_SINK) return 0; memset(&start, 0, sizeof(start)); start.ase = stream->ep->id; iov.iov_base = &start; iov.iov_len = sizeof(start); req = bap_req_new(stream, BT_ASCS_START, &iov, 1, func, user_data); if (!bap_queue_req(stream->bap, req)) { bap_req_free(req); return 0; } return req->id; } static uint8_t stream_disable(struct bt_bap_stream *stream, struct iovec *rsp) { if (!stream || stream->ep->state == BT_BAP_STREAM_STATE_QOS || stream->ep->state == BT_BAP_STREAM_STATE_IDLE) return 0; DBG(stream->bap, "stream %p", stream); ascs_ase_rsp_success(rsp, stream->ep->id); /* Sink can autonomously transit to QOS while source needs to go to * Disabling until BT_ASCS_STOP is received. */ if (stream->ep->dir == BT_BAP_SINK) stream_set_state(stream, BT_BAP_STREAM_STATE_QOS); if (stream->ep->dir == BT_BAP_SOURCE) stream_set_state(stream, BT_BAP_STREAM_STATE_DISABLING); return 0; } static void bap_stream_disable_link(void *data, void *user_data) { struct bt_bap_stream *stream = data; bt_bap_stream_disable(stream, false, NULL, NULL); } static unsigned int bap_ucast_disable(struct bt_bap_stream *stream, bool disable_links, bt_bap_stream_func_t func, void *user_data) { struct iovec iov; struct bt_ascs_disable disable; struct bt_bap_req *req; if (!stream->client) return stream_disable(stream, NULL); memset(&disable, 0, sizeof(disable)); disable.ase = stream->ep->id; iov.iov_base = &disable; iov.iov_len = sizeof(disable); req = bap_req_new(stream, BT_ASCS_DISABLE, &iov, 1, func, user_data); if (!bap_queue_req(stream->bap, req)) { bap_req_free(req); return 0; } if (disable_links) queue_foreach(stream->links, bap_stream_disable_link, NULL); return req->id; } static uint8_t stream_stop(struct bt_bap_stream *stream, struct iovec *rsp) { if (!stream) return 0; DBG(stream->bap, "stream %p", stream); ascs_ase_rsp_success(rsp, stream->ep->id); stream_set_state(stream, BT_BAP_STREAM_STATE_QOS); return 0; } static unsigned int bap_ucast_stop(struct bt_bap_stream *stream, bt_bap_stream_func_t func, void *user_data) { struct iovec iov; struct bt_ascs_stop stop; struct bt_bap_req *req; if (!stream->client) { if (stream->ep->dir == BT_BAP_SINK) stream_stop(stream, NULL); return 0; } if (stream->ep->dir == BT_BAP_SINK) return 0; memset(&stop, 0, sizeof(stop)); stop.ase = stream->ep->id; iov.iov_base = &stop; iov.iov_len = sizeof(stop); req = bap_req_new(stream, BT_ASCS_STOP, &iov, 1, func, user_data); if (!bap_queue_req(stream->bap, req)) { bap_req_free(req); return 0; } return req->id; } static uint8_t stream_metadata(struct bt_bap_stream *stream, struct iovec *meta, struct iovec *rsp) { DBG(stream->bap, "stream %p", stream); ascs_ase_rsp_success(rsp, stream->ep->id); util_iov_free(stream->meta, 1); stream->meta = util_iov_dup(meta, 1); return 0; } static unsigned int bap_ucast_metadata(struct bt_bap_stream *stream, struct iovec *data, bt_bap_stream_func_t func, void *user_data) { if (!stream->client) { stream_metadata(stream, data, NULL); return 0; } switch (bt_bap_stream_get_state(stream)) { /* Valid only if ASE_State field = 0x03 (Enabling) */ case BT_BAP_STREAM_STATE_ENABLING: /* or 0x04 (Streaming) */ case BT_BAP_STREAM_STATE_STREAMING: return bap_stream_metadata(stream, BT_ASCS_METADATA, data, func, user_data); } stream_metadata(stream, data, NULL); return 0; } static uint8_t stream_release(struct bt_bap_stream *stream, struct iovec *rsp) { DBG(stream->bap, "stream %p", stream); ascs_ase_rsp_success(rsp, stream->ep->id); /* In case the stream IO is already down the released transition needs * to take action immeditely. */ if (!stream->io) { switch (bt_bap_stream_get_state(stream)) { case BT_BAP_STREAM_STATE_CONFIG: /* Released (no caching) */ stream_set_state(stream, BT_BAP_STREAM_STATE_RELEASING); stream_set_state(stream, BT_BAP_STREAM_STATE_IDLE); break; default: /* Released (caching) */ stream_set_state(stream, BT_BAP_STREAM_STATE_RELEASING); stream_set_state(stream, BT_BAP_STREAM_STATE_CONFIG); break; } } else stream_set_state(stream, BT_BAP_STREAM_STATE_RELEASING); return 0; } static bool bap_stream_valid(struct bt_bap_stream *stream) { if (!stream || !stream->bap) return false; return queue_find(stream->bap->streams, NULL, stream); } static unsigned int bap_ucast_get_dir(struct bt_bap_stream *stream) { return stream->ep->dir; } static unsigned int bap_ucast_get_location(struct bt_bap_stream *stream) { struct bt_pacs *pacs; if (!stream) return 0x00000000; pacs = stream->client ? stream->bap->rdb->pacs : stream->bap->ldb->pacs; if (stream->ep->dir == BT_BAP_SOURCE) return pacs->source_loc_value; else if (stream->ep->dir == BT_BAP_SINK) return pacs->sink_loc_value; return 0x00000000; } static unsigned int bap_ucast_release(struct bt_bap_stream *stream, bt_bap_stream_func_t func, void *user_data) { struct iovec iov; struct bt_ascs_release rel; struct bt_bap_req *req; struct bt_bap *bap; if (!stream->client) { stream_release(stream, NULL); return 0; } memset(&req, 0, sizeof(req)); rel.ase = stream->ep->id; iov.iov_base = &rel; iov.iov_len = sizeof(rel); bap = stream->bap; /* If stream does not belong to a client session, clean it up now */ if (!bap_stream_valid(stream)) { stream_set_state(stream, BT_BAP_STREAM_STATE_IDLE); stream = NULL; return 0; } req = bap_req_new(stream, BT_ASCS_RELEASE, &iov, 1, func, user_data); if (!bap_queue_req(bap, req)) { bap_req_free(req); return 0; } return req->id; } static struct bt_bap_stream *bt_bap_stream_ref(struct bt_bap_stream *stream) { if (!stream) return NULL; __sync_fetch_and_add(&stream->ref_count, 1); return stream; } static void bap_bcast_set_state(struct bt_bap_stream *stream, uint8_t state) { struct bt_bap *bap = stream->bap; const struct queue_entry *entry; stream->old_state = stream->state; stream->state = state; bt_bap_stream_ref(stream); DBG(bap, "stream %p dir 0x%02x: %s -> %s", stream, bt_bap_stream_get_dir(stream), bt_bap_stream_statestr(stream->old_state), bt_bap_stream_statestr(stream->state)); for (entry = queue_get_entries(bap->state_cbs); entry; entry = entry->next) { struct bt_bap_state *state = entry->data; if (state->func) state->func(stream, stream->old_state, stream->state, state->data); } /* Post notification updates */ switch (stream->state) { case BT_ASCS_ASE_STATE_IDLE: if (stream->ops && stream->ops->detach) stream->ops->detach(stream); break; case BT_ASCS_ASE_STATE_RELEASING: bap_stream_io_detach(stream); stream_set_state(stream, BT_BAP_STREAM_STATE_IDLE); break; case BT_ASCS_ASE_STATE_ENABLING: if (bt_bap_stream_get_io(stream)) /* Start stream if fd has already been set */ bt_bap_stream_start(stream, NULL, NULL); break; } bt_bap_stream_unref(stream); } static unsigned int bap_bcast_get_state(struct bt_bap_stream *stream) { return stream->state; } static bool bcast_sink_stream_enabled(const void *data, const void *match_data) { struct bt_bap_stream *stream = (struct bt_bap_stream *)data; struct bt_bap_stream *match = (struct bt_bap_stream *)match_data; uint8_t state = bt_bap_stream_get_state(stream); if (stream == match) return false; if (queue_find(stream->links, NULL, match)) return false; /* Ignore streams that are not Broadcast Sink */ if (bt_bap_pac_get_type(stream->lpac) != BT_BAP_BCAST_SINK) return false; return ((state == BT_BAP_STREAM_STATE_ENABLING) || bt_bap_stream_get_io(stream)); } static unsigned int bap_bcast_sink_enable(struct bt_bap_stream *stream, bool enable_links, struct iovec *data, bt_bap_stream_func_t func, void *user_data) { struct bt_bap *bap = stream->bap; /* The stream cannot be enabled if there is any other * unlinked stream for the same source that is in the * process of enabling or that has already been started. */ if (queue_find(bap->streams, bcast_sink_stream_enabled, stream)) return 0; stream_set_state(stream, BT_BAP_STREAM_STATE_ENABLING); return 1; } static unsigned int bap_bcast_src_enable(struct bt_bap_stream *stream, bool enable_links, struct iovec *data, bt_bap_stream_func_t func, void *user_data) { stream_set_state(stream, BT_BAP_STREAM_STATE_ENABLING); return 1; } static unsigned int bap_bcast_start(struct bt_bap_stream *stream, bt_bap_stream_func_t func, void *user_data) { stream_set_state(stream, BT_BAP_STREAM_STATE_STREAMING); return 1; } static unsigned int bap_bcast_disable(struct bt_bap_stream *stream, bool disable_links, bt_bap_stream_func_t func, void *user_data) { bap_stream_io_detach(stream); stream_set_state(stream, BT_BAP_STREAM_STATE_CONFIG); return 1; } static unsigned int bap_bcast_metadata(struct bt_bap_stream *stream, struct iovec *data, bt_bap_stream_func_t func, void *user_data) { util_iov_free(stream->meta, 1); stream->meta = util_iov_dup(data, 1); return 1; } static unsigned int bap_bcast_src_get_dir(struct bt_bap_stream *stream) { return BT_BAP_BCAST_SINK; } static unsigned int bap_bcast_sink_get_dir(struct bt_bap_stream *stream) { return BT_BAP_BCAST_SOURCE; } static void bap_sink_get_allocation(size_t i, uint8_t l, uint8_t t, uint8_t *v, void *user_data) { uint32_t location32; if (!v) return; memcpy(&location32, v, l); *((uint32_t *)user_data) = le32_to_cpu(location32); } static unsigned int bap_bcast_get_location(struct bt_bap_stream *stream) { uint8_t type = BAP_CHANNEL_ALLOCATION_LTV_TYPE; uint32_t allocation = 0; struct iovec *caps; caps = bt_bap_stream_get_config(stream); /* Get stream allocation from capabilities */ util_ltv_foreach(caps->iov_base, caps->iov_len, &type, bap_sink_get_allocation, &allocation); return allocation; } static unsigned int bap_bcast_release(struct bt_bap_stream *stream, bt_bap_stream_func_t func, void *user_data) { stream_set_state(stream, BT_BAP_STREAM_STATE_RELEASING); return 1; } static bool bap_ucast_set_io(struct bt_bap_stream *stream, int fd) { if (!stream || (fd >= 0 && stream->io && !stream->io->connecting)) return false; bap_stream_set_io(stream, INT_TO_PTR(fd)); queue_foreach(stream->links, bap_stream_set_io, INT_TO_PTR(fd)); return true; } static bool bap_bcast_set_io(struct bt_bap_stream *stream, int fd) { if (!stream || (fd >= 0 && stream->io && !stream->io->connecting)) return false; bap_stream_set_io(stream, INT_TO_PTR(fd)); return true; } static struct bt_bap_stream_io *bap_ucast_get_io(struct bt_bap_stream *stream) { struct bt_bap_stream_io *io = NULL; if (!stream) return NULL; if (stream->io) return stream->io; queue_foreach(stream->links, stream_find_io, &io); return io; } static struct bt_bap_stream_io *bap_bcast_get_io(struct bt_bap_stream *stream) { if (!stream) return NULL; return stream->io; } static uint8_t bap_ucast_io_dir(struct bt_bap_stream *stream) { uint8_t dir; if (!stream) return 0x00; dir = stream->ep->dir; queue_foreach(stream->links, bap_stream_get_dir, &dir); return dir; } static uint8_t bap_bcast_io_dir(struct bt_bap_stream *stream) { uint8_t dir; uint8_t pac_type = bt_bap_pac_get_type(stream->lpac); if (!stream) return 0x00; if (pac_type == BT_BAP_BCAST_SINK) dir = BT_BAP_BCAST_SOURCE; else dir = BT_BAP_BCAST_SINK; return dir; } static int bap_ucast_io_link(struct bt_bap_stream *stream, struct bt_bap_stream *link) { struct bt_bap *bap; if (!stream || !link || stream == link) return -EINVAL; bap = stream->bap; if (!queue_isempty(stream->links) || !queue_isempty(link->links)) return -EALREADY; if (stream->client != link->client || stream->qos.ucast.cig_id != link->qos.ucast.cig_id || stream->qos.ucast.cis_id != link->qos.ucast.cis_id || stream->ep->dir == link->ep->dir) return -EINVAL; if (!stream->links) stream->links = queue_new(); if (!link->links) link->links = queue_new(); queue_push_tail(stream->links, link); queue_push_tail(link->links, stream); /* Link IOs if already set on stream/link */ if (stream->io && !link->io) link->io = stream_io_ref(stream->io); else if (link->io && !stream->io) stream->io = stream_io_ref(link->io); DBG(bap, "stream %p link %p", stream, link); return 0; } static void stream_link(void *data, void *user_data) { struct bt_bap_stream *stream = (void *)data; struct bt_bap_stream *link = (void *)user_data; bt_bap_stream_io_link(stream, link); } static int bap_bcast_io_link(struct bt_bap_stream *stream, struct bt_bap_stream *link) { struct bt_bap *bap; if (!stream || !link || stream == link) return -EINVAL; bap = stream->bap; if (queue_find(stream->links, NULL, link) || queue_find(link->links, NULL, stream)) return -EALREADY; if (!stream->links) stream->links = queue_new(); if (!link->links) link->links = queue_new(); queue_push_tail(stream->links, link); queue_push_tail(link->links, stream); DBG(bap, "stream %p link %p", stream, link); queue_foreach(stream->links, stream_link, link); return 0; } static void stream_unlink(void *data, void *user_data) { struct bt_bap_stream *stream = (void *)data; struct bt_bap_stream *link = (void *)user_data; bap_bcast_io_unlink(stream, link); } static int bap_bcast_io_unlink(struct bt_bap_stream *stream, struct bt_bap_stream *link) { struct bt_bap *bap; if (!stream || !link || stream == link) return -EINVAL; bap = stream->bap; if (!queue_find(stream->links, NULL, link) || !queue_find(link->links, NULL, stream)) return -EALREADY; queue_remove(stream->links, link); queue_remove(link->links, stream); DBG(bap, "stream %p unlink %p", stream, link); queue_foreach(stream->links, stream_unlink, link); return 0; } #define STREAM_OPS(_type, _set_state, _get_state, _config, _qos, _enable, \ _start, _disable, _stop, _metadata, _get_dir, _get_loc, _release, \ _detach, _set_io, _get_io, _io_dir, _io_link, _io_unlink) \ { \ .type = _type, \ .set_state = _set_state, \ .get_state = _get_state, \ .config = _config, \ .qos = _qos, \ .enable = _enable, \ .start = _start, \ .disable = _disable, \ .stop = _stop, \ .metadata = _metadata, \ .get_dir = _get_dir,\ .get_loc = _get_loc, \ .release = _release, \ .detach = _detach, \ .set_io = _set_io, \ .get_io = _get_io, \ .io_dir = _io_dir, \ .io_link = _io_link, \ .io_unlink = _io_unlink, \ } static const struct bt_bap_stream_ops stream_ops[] = { STREAM_OPS(BT_BAP_SINK, bap_ucast_set_state, bap_ucast_get_state, bap_ucast_config, bap_ucast_qos, bap_ucast_enable, bap_ucast_start, bap_ucast_disable, bap_ucast_stop, bap_ucast_metadata, bap_ucast_get_dir, bap_ucast_get_location, bap_ucast_release, bap_ucast_detach, bap_ucast_set_io, bap_ucast_get_io, bap_ucast_io_dir, bap_ucast_io_link, NULL), STREAM_OPS(BT_BAP_SOURCE, bap_ucast_set_state, bap_ucast_get_state, bap_ucast_config, bap_ucast_qos, bap_ucast_enable, bap_ucast_start, bap_ucast_disable, bap_ucast_stop, bap_ucast_metadata, bap_ucast_get_dir, bap_ucast_get_location, bap_ucast_release, bap_ucast_detach, bap_ucast_set_io, bap_ucast_get_io, bap_ucast_io_dir, bap_ucast_io_link, NULL), STREAM_OPS(BT_BAP_BCAST_SINK, bap_bcast_set_state, bap_bcast_get_state, bap_bcast_config, bap_bcast_qos, bap_bcast_sink_enable, bap_bcast_start, bap_bcast_disable, NULL, bap_bcast_metadata, bap_bcast_sink_get_dir, bap_bcast_get_location, bap_bcast_release, bap_bcast_sink_detach, bap_bcast_set_io, bap_bcast_get_io, bap_bcast_io_dir, bap_bcast_io_link, bap_bcast_io_unlink), STREAM_OPS(BT_BAP_BCAST_SOURCE, bap_bcast_set_state, bap_bcast_get_state, bap_bcast_config, bap_bcast_qos, bap_bcast_src_enable, bap_bcast_start, bap_bcast_disable, NULL, bap_bcast_metadata, bap_bcast_src_get_dir, bap_bcast_get_location, bap_bcast_release, bap_bcast_src_detach, bap_bcast_set_io, bap_bcast_get_io, bap_bcast_io_dir, bap_bcast_io_link, bap_bcast_io_unlink), }; static const struct bt_bap_stream_ops * bap_stream_new_ops(struct bt_bap_stream *stream) { const struct bt_bap_stream_ops *ops; uint8_t type = bt_bap_pac_get_type(stream->lpac); size_t i; for (i = 0; i < ARRAY_SIZE(stream_ops); i++) { ops = &stream_ops[i]; if (ops->type == type) return ops; } return NULL; } static struct bt_bap_stream *bap_stream_new(struct bt_bap *bap, struct bt_bap_endpoint *ep, struct bt_bap_pac *lpac, struct bt_bap_pac *rpac, struct iovec *data, bool client) { struct bt_bap_stream *stream; stream = new0(struct bt_bap_stream, 1); stream->bap = bap; stream->ep = ep; if (ep != NULL) ep->stream = stream; stream->lpac = lpac; stream->rpac = rpac; stream->cc = util_iov_dup(data, 1); stream->client = client; stream->ops = bap_stream_new_ops(stream); stream->pending_states = queue_new(); queue_push_tail(bap->streams, stream); return bt_bap_stream_ref(stream); } static struct bt_bap_stream_io *stream_io_ref(struct bt_bap_stream_io *io) { if (!io) return NULL; __sync_fetch_and_add(&io->ref_count, 1); return io; } static struct bt_bap_stream_io *stream_io_new(struct bt_bap *bap, int fd) { struct io *io; struct bt_bap_stream_io *sio; io = io_new(fd); if (!io) return NULL; DBG(bap, "fd %d", fd); sio = new0(struct bt_bap_stream_io, 1); sio->bap = bap; sio->io = io; return stream_io_ref(sio); } static void stream_find_io(void *data, void *user_data) { struct bt_bap_stream *stream = data; struct bt_bap_stream_io **io = user_data; if (*io) return; *io = stream->io; } static struct bt_bap_stream_io *stream_get_io(struct bt_bap_stream *stream) { struct bt_bap_stream_io *io; struct bt_bap *bap; if (!bap_stream_valid(stream)) return NULL; if (!stream->ops || !stream->ops->get_io) return NULL; if (!bt_bap_ref_safe(stream->bap)) return NULL; bap = stream->bap; io = stream->ops->get_io(stream); bt_bap_unref(bap); return io; } static bool stream_io_disconnected(struct io *io, void *user_data); static bool bap_stream_io_attach(struct bt_bap_stream *stream, int fd, bool connecting) { struct bt_bap_stream_io *io; io = stream_get_io(stream); if (io) { if (fd == stream_io_get_fd(io)) { if (!stream->io) stream->io = stream_io_ref(io); io->connecting = connecting; return true; } DBG(stream->bap, "stream %p io already set", stream); return false; } DBG(stream->bap, "stream %p connecting %s", stream, connecting ? "true" : "false"); io = stream_io_new(stream->bap, fd); if (!io) return false; io->connecting = connecting; stream->io = io; io_set_disconnect_handler(io->io, stream_io_disconnected, stream, NULL); return true; } static void bap_stream_set_io(void *data, void *user_data) { struct bt_bap_stream *stream = data; int fd = PTR_TO_INT(user_data); bool ret; uint8_t state; if (fd >= 0) ret = bap_stream_io_attach(stream, fd, false); else ret = bap_stream_io_detach(stream); if (!ret) return; if (bt_bap_stream_get_type(stream) == BT_BAP_STREAM_TYPE_BCAST) state = stream->state; else state = stream->ep->state; switch (state) { case BT_BAP_STREAM_STATE_ENABLING: if (fd < 0) bt_bap_stream_disable(stream, false, NULL, NULL); else bt_bap_stream_start(stream, NULL, NULL); break; case BT_BAP_STREAM_STATE_DISABLING: if (fd < 0) bt_bap_stream_stop(stream, NULL, NULL); break; } } static void ascs_ase_rsp_add_errno(struct iovec *iov, uint8_t id, int err) { struct bt_ascs_cp_rsp *rsp = iov->iov_base; switch (err) { case -ENOBUFS: case -ENOMEM: return ascs_ase_rsp_add(iov, id, BT_ASCS_RSP_NO_MEM, BT_ASCS_REASON_NONE); case -EINVAL: switch (rsp->op) { case BT_ASCS_CONFIG: /* Fallthrough */ case BT_ASCS_QOS: return ascs_ase_rsp_add(iov, id, BT_ASCS_RSP_CONF_INVALID, BT_ASCS_REASON_NONE); case BT_ASCS_ENABLE: /* Fallthrough */ case BT_ASCS_METADATA: return ascs_ase_rsp_add(iov, id, BT_ASCS_RSP_METADATA_INVALID, BT_ASCS_REASON_NONE); default: return ascs_ase_rsp_add(iov, id, BT_ASCS_RSP_UNSPECIFIED, BT_ASCS_REASON_NONE); } case -ENOTSUP: switch (rsp->op) { case BT_ASCS_CONFIG: /* Fallthrough */ case BT_ASCS_QOS: return ascs_ase_rsp_add(iov, id, BT_ASCS_RSP_CONF_UNSUPPORTED, BT_ASCS_REASON_NONE); case BT_ASCS_ENABLE: /* Fallthrough */ case BT_ASCS_METADATA: return ascs_ase_rsp_add(iov, id, BT_ASCS_RSP_METADATA_UNSUPPORTED, BT_ASCS_REASON_NONE); default: return ascs_ase_rsp_add(iov, id, BT_ASCS_RSP_NOT_SUPPORTED, BT_ASCS_REASON_NONE); } case -EBADMSG: return ascs_ase_rsp_add(iov, id, BT_ASCS_RSP_INVALID_ASE_STATE, BT_ASCS_REASON_NONE); case -ENOMSG: return ascs_ase_rsp_add(iov, id, BT_ASCS_RSP_TRUNCATED, BT_ASCS_REASON_NONE); default: return ascs_ase_rsp_add(iov, id, BT_ASCS_RSP_UNSPECIFIED, BT_ASCS_REASON_NONE); } } static uint8_t ep_config(struct bt_bap_endpoint *ep, struct bt_bap *bap, struct bt_ascs_config *req, struct iovec *iov, struct iovec *rsp) { struct iovec cc; const struct queue_entry *e; DBG(bap, "ep %p id 0x%02x dir 0x%02x", ep, ep->id, ep->dir); switch (ep->state) { /* Valid only if ASE_State field = 0x00 (Idle) */ case BT_ASCS_ASE_STATE_IDLE: /* or 0x01 (Codec Configured) */ case BT_ASCS_ASE_STATE_CONFIG: /* or 0x02 (QoS Configured) */ case BT_ASCS_ASE_STATE_QOS: break; default: DBG(bap, "Invalid state %s", bt_bap_stream_statestr(ep->state)); ascs_ase_rsp_add(rsp, req->ase, BT_ASCS_RSP_INVALID_ASE_STATE, BT_ASCS_REASON_NONE); return 0; } if (iov->iov_len < req->cc_len) return BT_ATT_ERROR_INVALID_ATTRIBUTE_VALUE_LEN; cc.iov_base = util_iov_pull_mem(iov, req->cc_len); cc.iov_len = req->cc_len; if (!bt_bap_debug_caps(cc.iov_base, cc.iov_len, bap->debug_func, bap->debug_data)) { ascs_ase_rsp_add(rsp, req->ase, BT_ASCS_RSP_CONF_INVALID, BT_ASCS_REASON_CODEC_DATA); return 0; } switch (ep->dir) { case BT_BAP_SINK: e = queue_get_entries(bap->ldb->sinks); break; case BT_BAP_SOURCE: e = queue_get_entries(bap->ldb->sources); break; default: e = NULL; } for (; e; e = e->next) { struct bt_bap_pac *pac = e->data; if (!bap_codec_equal(&req->codec, &pac->codec)) continue; if (!ep->stream) ep->stream = bap_stream_new(bap, ep, pac, NULL, NULL, false); break; } if (!e) { ascs_ase_rsp_add(rsp, req->ase, BT_ASCS_RSP_CONF_INVALID, BT_ASCS_REASON_CODEC); return 0; } return stream_config(ep->stream, &cc, rsp); } static uint8_t ascs_config(struct bt_ascs *ascs, struct bt_bap *bap, struct iovec *iov, struct iovec *rsp) { struct bt_bap_endpoint *ep; struct bt_ascs_config *req; req = util_iov_pull_mem(iov, sizeof(*req)); DBG(bap, "codec 0x%02x phy 0x%02x latency %u", req->codec.id, req->phy, req->latency); ep = bap_get_local_endpoint_id(bap, req->ase); if (!ep) { DBG(bap, "Invalid ASE ID 0x%02x", req->ase); ascs_ase_rsp_add(rsp, req->ase, BT_ASCS_RSP_INVALID_ASE, BT_ASCS_REASON_NONE); return 0; } return ep_config(ep, bap, req, iov, rsp); } static uint8_t stream_qos(struct bt_bap_stream *stream, struct bt_bap_qos *qos, struct iovec *rsp) { DBG(stream->bap, "stream %p", stream); ascs_ase_rsp_success(rsp, stream->ep->id); if (memcmp(&stream->qos, qos, sizeof(*qos))) stream->qos = *qos; stream_set_state(stream, BT_BAP_STREAM_STATE_QOS); return 0; } static uint8_t ep_qos(struct bt_bap_endpoint *ep, struct bt_bap *bap, struct bt_bap_qos *qos, struct iovec *rsp) { DBG(bap, "ep %p id 0x%02x dir 0x%02x", ep, ep->id, ep->dir); switch (ep->state) { /* Valid only if ASE_State field = 0x01 (Codec Configured) */ case BT_ASCS_ASE_STATE_CONFIG: /* or 0x02 (QoS Configured) */ case BT_ASCS_ASE_STATE_QOS: break; default: DBG(bap, "Invalid state %s", bt_bap_stream_statestr(ep->state)); ascs_ase_rsp_add(rsp, ep->id, BT_ASCS_RSP_INVALID_ASE_STATE, BT_ASCS_REASON_NONE); return 0; } if (!ep->stream) { DBG(bap, "No stream found"); ascs_ase_rsp_add(rsp, ep->id, BT_ASCS_RSP_INVALID_ASE_STATE, BT_ASCS_REASON_NONE); return 0; } return stream_qos(ep->stream, qos, rsp); } static uint8_t ascs_qos(struct bt_ascs *ascs, struct bt_bap *bap, struct iovec *iov, struct iovec *rsp) { struct bt_bap_endpoint *ep; struct bt_ascs_qos *req; struct bt_bap_qos qos; req = util_iov_pull_mem(iov, sizeof(*req)); memset(&qos, 0, sizeof(qos)); qos.ucast.cig_id = req->cig; qos.ucast.cis_id = req->cis; qos.ucast.io_qos.interval = get_le24(req->interval); qos.ucast.framing = req->framing; qos.ucast.io_qos.phy = req->phy; qos.ucast.io_qos.sdu = le16_to_cpu(req->sdu); qos.ucast.io_qos.rtn = req->rtn; qos.ucast.io_qos.latency = le16_to_cpu(req->latency); qos.ucast.delay = get_le24(req->pd); DBG(bap, "CIG 0x%02x CIS 0x%02x interval %u framing 0x%02x " "phy 0x%02x SDU %u rtn %u latency %u pd %u", req->cig, req->cis, qos.ucast.io_qos.interval, qos.ucast.framing, qos.ucast.io_qos.phy, qos.ucast.io_qos.sdu, qos.ucast.io_qos.rtn, qos.ucast.io_qos.latency, qos.ucast.delay); ep = bap_get_local_endpoint_id(bap, req->ase); if (!ep) { DBG(bap, "%s: Invalid ASE ID 0x%02x", req->ase); ascs_ase_rsp_add(rsp, req->ase, BT_ASCS_RSP_INVALID_ASE, BT_ASCS_REASON_NONE); return 0; } return ep_qos(ep, bap, &qos, rsp); } static uint8_t stream_enable(struct bt_bap_stream *stream, struct iovec *meta, struct iovec *rsp) { DBG(stream->bap, "stream %p", stream); ascs_ase_rsp_success(rsp, stream->ep->id); util_iov_free(stream->meta, 1); stream->meta = util_iov_dup(meta, 1); if (!stream_set_state(stream, BT_BAP_STREAM_STATE_ENABLING)) return 1; /* Sink can autonomously for to Streaming state if io already exits */ if (stream->io && stream->ep->dir == BT_BAP_SINK) stream_set_state(stream, BT_BAP_STREAM_STATE_STREAMING); return 0; } static uint8_t ep_enable(struct bt_bap_endpoint *ep, struct bt_bap *bap, struct bt_ascs_enable *req, struct iovec *iov, struct iovec *rsp) { struct iovec meta; DBG(bap, "ep %p id 0x%02x dir 0x%02x", ep, ep->id, ep->dir); switch (ep->state) { /* Valid only if ASE_State field = 0x02 (QoS Configured) */ case BT_ASCS_ASE_STATE_QOS: break; default: DBG(bap, "Invalid state %s", bt_bap_stream_statestr(ep->state)); ascs_ase_rsp_add(rsp, ep->id, BT_ASCS_RSP_INVALID_ASE_STATE, BT_ASCS_REASON_NONE); return 0; } meta.iov_base = util_iov_pull_mem(iov, req->meta.len); meta.iov_len = req->meta.len; if (!bt_bap_debug_metadata(meta.iov_base, meta.iov_len, bap->debug_func, bap->debug_data)) { ascs_ase_rsp_add(rsp, ep->id, BT_ASCS_RSP_METADATA_INVALID, BT_ASCS_REASON_NONE); return 0; } if (!ep->stream) { DBG(bap, "No stream found"); ascs_ase_rsp_add(rsp, ep->id, BT_ASCS_RSP_INVALID_ASE_STATE, BT_ASCS_REASON_NONE); return 0; } return stream_enable(ep->stream, &meta, rsp); } static uint8_t ascs_enable(struct bt_ascs *ascs, struct bt_bap *bap, struct iovec *iov, struct iovec *rsp) { struct bt_bap_endpoint *ep; struct bt_ascs_enable *req; req = util_iov_pull_mem(iov, sizeof(*req)); ep = bap_get_local_endpoint_id(bap, req->meta.ase); if (!ep) { DBG(bap, "Invalid ASE ID 0x%02x", req->meta.ase); ascs_ase_rsp_add(rsp, req->meta.ase, BT_ASCS_RSP_INVALID_ASE, BT_ASCS_REASON_NONE); return 0; } return ep_enable(ep, bap, req, iov, rsp); } static uint8_t ep_start(struct bt_bap_endpoint *ep, struct iovec *rsp) { struct bt_bap_stream *stream = ep->stream; DBG(stream->bap, "ep %p id 0x%02x dir 0x%02x", ep, ep->id, ep->dir); switch (ep->state) { /* Valid only if ASE_State field = 0x03 (Enabling) */ case BT_ASCS_ASE_STATE_ENABLING: break; default: DBG(ep->stream->bap, "Invalid state %s", bt_bap_stream_statestr(ep->state)); ascs_ase_rsp_add(rsp, ep->id, BT_ASCS_RSP_INVALID_ASE_STATE, BT_ASCS_REASON_NONE); return 0; } /* If the ASE_ID written by the client represents a Sink ASE, the * server shall not accept the Receiver Start Ready operation for that * ASE. The server shall send a notification of the ASE Control Point * characteristic to the client, and the server shall set the * Response_Code value for that ASE to 0x05 (Invalid ASE direction). */ if (ep->dir == BT_BAP_SINK) { ascs_ase_rsp_add(rsp, ep->id, BT_ASCS_RSP_INVALID_DIR, BT_ASCS_REASON_NONE); return 0; } return stream_start(ep->stream, rsp); } static uint8_t ascs_start(struct bt_ascs *ascs, struct bt_bap *bap, struct iovec *iov, struct iovec *rsp) { struct bt_bap_endpoint *ep; struct bt_ascs_start *req; req = util_iov_pull_mem(iov, sizeof(*req)); ep = bap_get_local_endpoint_id(bap, req->ase); if (!ep) { DBG(bap, "Invalid ASE ID 0x%02x", req->ase); ascs_ase_rsp_add(rsp, req->ase, BT_ASCS_RSP_INVALID_ASE, BT_ASCS_REASON_NONE); return 0; } if (!ep->stream) { DBG(bap, "No stream found for %p", ep); ascs_ase_rsp_add(rsp, ep->id, BT_ASCS_RSP_INVALID_ASE_STATE, BT_ASCS_REASON_NONE); return 0; } return ep_start(ep, rsp); } static uint8_t ep_disable(struct bt_bap_endpoint *ep, struct iovec *rsp) { struct bt_bap_stream *stream = ep->stream; DBG(stream->bap, "ep %p id 0x%02x dir 0x%02x", ep, ep->id, ep->dir); switch (ep->state) { /* Valid only if ASE_State field = 0x03 (Enabling) */ case BT_ASCS_ASE_STATE_ENABLING: /* or 0x04 (Streaming) */ case BT_ASCS_ASE_STATE_STREAMING: break; default: DBG(stream->bap, "Invalid state %s", bt_bap_stream_statestr(ep->state)); ascs_ase_rsp_add(rsp, ep->id, BT_ASCS_RSP_INVALID_ASE_STATE, BT_ASCS_REASON_NONE); return 0; } return stream_disable(ep->stream, rsp); } static uint8_t ascs_disable(struct bt_ascs *ascs, struct bt_bap *bap, struct iovec *iov, struct iovec *rsp) { struct bt_bap_endpoint *ep; struct bt_ascs_disable *req; req = util_iov_pull_mem(iov, sizeof(*req)); ep = bap_get_local_endpoint_id(bap, req->ase); if (!ep) { DBG(bap, "Invalid ASE ID 0x%02x", req->ase); ascs_ase_rsp_add(rsp, req->ase, BT_ASCS_RSP_INVALID_ASE, BT_ASCS_REASON_NONE); return 0; } if (!ep->stream) { DBG(bap, "No stream found"); ascs_ase_rsp_add(rsp, ep->id, BT_ASCS_RSP_INVALID_ASE_STATE, BT_ASCS_REASON_NONE); return 0; } return ep_disable(ep, rsp); } static uint8_t ep_stop(struct bt_bap_endpoint *ep, struct iovec *rsp) { struct bt_bap_stream *stream = ep->stream; DBG(stream->bap, "ep %p id 0x%02x dir 0x%02x", ep, ep->id, ep->dir); switch (ep->state) { /* Valid only if ASE_State field = 0x05 (Disabling) */ case BT_ASCS_ASE_STATE_DISABLING: break; default: DBG(stream->bap, "Invalid state %s", bt_bap_stream_statestr(ep->state)); ascs_ase_rsp_add(rsp, ep->id, BT_ASCS_RSP_INVALID_ASE_STATE, BT_ASCS_REASON_NONE); return 0; } /* If the ASE_ID written by the client represents a Sink ASE, the * server shall not accept the Receiver Stop Ready operation for that * ASE. The server shall send a notification of the ASE Control Point * characteristic to the client, and the server shall set the * Response_Code value for that ASE to 0x05 (Invalid ASE direction). */ if (ep->dir == BT_BAP_SINK) { ascs_ase_rsp_add(rsp, ep->id, BT_ASCS_RSP_INVALID_DIR, BT_ASCS_REASON_NONE); return 0; } return stream_stop(ep->stream, rsp); } static uint8_t ascs_stop(struct bt_ascs *ascs, struct bt_bap *bap, struct iovec *iov, struct iovec *rsp) { struct bt_bap_endpoint *ep; struct bt_ascs_stop *req; req = util_iov_pull_mem(iov, sizeof(*req)); ep = bap_get_local_endpoint_id(bap, req->ase); if (!ep) { DBG(bap, "Invalid ASE ID 0x%02x", req->ase); ascs_ase_rsp_add(rsp, req->ase, BT_ASCS_RSP_INVALID_ASE, BT_ASCS_REASON_NONE); return 0; } if (!ep->stream) { DBG(bap, "No stream found"); ascs_ase_rsp_add(rsp, ep->id, BT_ASCS_RSP_INVALID_ASE_STATE, BT_ASCS_REASON_NONE); return 0; } return ep_stop(ep, rsp); } static uint8_t ep_metadata(struct bt_bap_endpoint *ep, struct iovec *meta, struct iovec *rsp) { struct bt_bap_stream *stream = ep->stream; DBG(stream->bap, "ep %p id 0x%02x dir 0x%02x", ep, ep->id, ep->dir); switch (ep->state) { /* Valid only if ASE_State field = 0x03 (Enabling) */ case BT_ASCS_ASE_STATE_ENABLING: /* or 0x04 (Streaming) */ case BT_ASCS_ASE_STATE_STREAMING: break; default: DBG(stream->bap, "Invalid state %s", bt_bap_stream_statestr(ep->state)); ascs_ase_rsp_add(rsp, ep->id, BT_ASCS_RSP_INVALID_ASE_STATE, BT_ASCS_REASON_NONE); return 0; } return stream_metadata(ep->stream, meta, rsp); } static uint8_t ascs_metadata(struct bt_ascs *ascs, struct bt_bap *bap, struct iovec *iov, struct iovec *rsp) { struct bt_bap_endpoint *ep; struct bt_ascs_metadata *req; req = util_iov_pull_mem(iov, sizeof(*req)); ep = bap_get_local_endpoint_id(bap, req->ase); if (!ep) { DBG(bap, "Invalid ASE ID 0x%02x", req->ase); ascs_ase_rsp_add(rsp, req->ase, BT_ASCS_RSP_INVALID_ASE, BT_ASCS_REASON_NONE); return 0; } if (!ep->stream) { DBG(bap, "No stream found"); ascs_ase_rsp_add(rsp, ep->id, BT_ASCS_RSP_INVALID_ASE_STATE, BT_ASCS_REASON_NONE); return 0; } return ep_metadata(ep, iov, rsp); } static uint8_t ascs_release(struct bt_ascs *ascs, struct bt_bap *bap, struct iovec *iov, struct iovec *rsp) { struct bt_bap_endpoint *ep; struct bt_ascs_release *req; req = util_iov_pull_mem(iov, sizeof(*req)); ep = bap_get_local_endpoint_id(bap, req->ase); if (!ep) { DBG(bap, "Invalid ASE ID 0x%02x", req->ase); ascs_ase_rsp_add(rsp, req->ase, BT_ASCS_RSP_INVALID_ASE, BT_ASCS_REASON_NONE); return 0; } if (!ep->stream) { DBG(bap, "No stream found"); ascs_ase_rsp_add(rsp, ep->id, BT_ASCS_RSP_INVALID_ASE_STATE, BT_ASCS_REASON_NONE); return 0; } return stream_release(ep->stream, rsp); } #define ASCS_OP(_str, _op, _size, _func) \ { \ .str = _str, \ .op = _op, \ .size = _size, \ .func = _func, \ } struct ascs_op_handler { const char *str; uint8_t op; size_t size; uint8_t (*func)(struct bt_ascs *ascs, struct bt_bap *bap, struct iovec *iov, struct iovec *rsp); } handlers[] = { ASCS_OP("Codec Config", BT_ASCS_CONFIG, sizeof(struct bt_ascs_config), ascs_config), ASCS_OP("QoS Config", BT_ASCS_QOS, sizeof(struct bt_ascs_qos), ascs_qos), ASCS_OP("Enable", BT_ASCS_ENABLE, sizeof(struct bt_ascs_enable), ascs_enable), ASCS_OP("Receiver Start Ready", BT_ASCS_START, sizeof(struct bt_ascs_start), ascs_start), ASCS_OP("Disable", BT_ASCS_DISABLE, sizeof(struct bt_ascs_disable), ascs_disable), ASCS_OP("Receiver Stop Ready", BT_ASCS_STOP, sizeof(struct bt_ascs_stop), ascs_stop), ASCS_OP("Update Metadata", BT_ASCS_METADATA, sizeof(struct bt_ascs_metadata), ascs_metadata), ASCS_OP("Release", BT_ASCS_RELEASE, sizeof(struct bt_ascs_release), ascs_release), {} }; static struct iovec *ascs_ase_cp_rsp_new(uint8_t op) { struct bt_ascs_cp_rsp *rsp; struct iovec *iov; iov = new0(struct iovec, 1); rsp = new0(struct bt_ascs_cp_rsp, 1); rsp->op = op; iov->iov_base = rsp; iov->iov_len = sizeof(*rsp); return iov; } static void ascs_ase_cp_write(struct gatt_db_attribute *attrib, unsigned int id, uint16_t offset, const uint8_t *value, size_t len, uint8_t opcode, struct bt_att *att, void *user_data) { struct bt_ascs *ascs = user_data; struct bt_bap *bap = bt_bap_get_session(att, ascs->bdb->db); struct iovec iov = { .iov_base = (void *) value, .iov_len = len, }; struct bt_ascs_ase_hdr *hdr; struct ascs_op_handler *handler; uint8_t ret = BT_ATT_ERROR_REQUEST_NOT_SUPPORTED; struct iovec *rsp; if (offset) { DBG(bap, "invalid offset %u", offset); gatt_db_attribute_write_result(attrib, id, BT_ATT_ERROR_INVALID_OFFSET); return; } if (len < sizeof(*hdr)) { DBG(bap, "invalid len %u < %u sizeof(*hdr)", len, sizeof(*hdr)); gatt_db_attribute_write_result(attrib, id, BT_ATT_ERROR_INVALID_ATTRIBUTE_VALUE_LEN); return; } hdr = util_iov_pull_mem(&iov, sizeof(*hdr)); rsp = ascs_ase_cp_rsp_new(hdr->op); for (handler = handlers; handler && handler->str; handler++) { if (handler->op != hdr->op) continue; if (iov.iov_len < hdr->num * handler->size) { DBG(bap, "invalid len %u < %u " "hdr->num * handler->size", len, hdr->num * handler->size); ret = BT_ATT_ERROR_INVALID_ATTRIBUTE_VALUE_LEN; goto respond; } break; } if (handler && handler->str) { int i; DBG(bap, "%s", handler->str); /* Set in_cp_write so ASE notification are not sent ahead of * CP notifcation. */ bap->in_cp_write = true; for (i = 0; i < hdr->num; i++) ret = handler->func(ascs, bap, &iov, rsp); bap->in_cp_write = false; } else { DBG(bap, "Unknown opcode 0x%02x", hdr->op); ascs_ase_rsp_add_errno(rsp, 0x00, -ENOTSUP); } respond: if (ret == BT_ATT_ERROR_INVALID_ATTRIBUTE_VALUE_LEN) ascs_ase_rsp_add_errno(rsp, 0x00, -ENOMSG); gatt_db_attribute_notify(attrib, rsp->iov_base, rsp->iov_len, att); gatt_db_attribute_write_result(attrib, id, ret); util_iov_free(rsp, 1); } static struct bt_ascs *ascs_new(struct gatt_db *db) { struct bt_ascs *ascs; bt_uuid_t uuid; int i; if (!db) return NULL; ascs = new0(struct bt_ascs, 1); /* Populate DB with ASCS attributes */ bt_uuid16_create(&uuid, ASCS_UUID); ascs->service = gatt_db_add_service(db, &uuid, true, 4 + (NUM_ASES * 3)); for (i = 0; i < NUM_ASES; i++) ase_new(ascs, i); bt_uuid16_create(&uuid, ASE_CP_UUID); ascs->ase_cp = gatt_db_service_add_characteristic(ascs->service, &uuid, BT_ATT_PERM_WRITE, BT_GATT_CHRC_PROP_WRITE | BT_GATT_CHRC_PROP_NOTIFY, NULL, ascs_ase_cp_write, ascs); ascs->ase_cp_ccc = gatt_db_service_add_ccc(ascs->service, BT_ATT_PERM_READ | BT_ATT_PERM_WRITE); gatt_db_service_set_active(ascs->service, true); return ascs; } static struct bt_bap_db *bap_db_new(struct gatt_db *db) { struct bt_bap_db *bdb; if (!db) return NULL; bdb = new0(struct bt_bap_db, 1); bdb->db = gatt_db_ref(db); bdb->sinks = queue_new(); bdb->sources = queue_new(); bdb->broadcast_sources = queue_new(); bdb->broadcast_sinks = queue_new(); if (!bap_db) bap_db = queue_new(); bdb->pacs = pacs_new(db); bdb->pacs->bdb = bdb; bdb->ascs = ascs_new(db); bdb->ascs->bdb = bdb; queue_push_tail(bap_db, bdb); return bdb; } static struct bt_bap_db *bap_get_db(struct gatt_db *db) { struct bt_bap_db *bdb; bdb = queue_find(bap_db, bap_db_match, db); if (bdb) return bdb; return bap_db_new(db); } static struct bt_pacs *bap_get_pacs(struct bt_bap *bap) { if (!bap) return NULL; if (bap->rdb->pacs) return bap->rdb->pacs; bap->rdb->pacs = new0(struct bt_pacs, 1); bap->rdb->pacs->bdb = bap->rdb; return bap->rdb->pacs; } static bool match_codec(const void *data, const void *user_data) { const struct bt_bap_pac *pac = data; const struct bt_bap_codec *codec = user_data; return bap_codec_equal(&pac->codec, codec); } static struct bt_bap_pac *bap_pac_find(struct bt_bap_db *bdb, uint8_t type, struct bt_bap_codec *codec) { switch (type) { case BT_BAP_SOURCE: return queue_find(bdb->sources, match_codec, codec); case BT_BAP_SINK: return queue_find(bdb->sinks, match_codec, codec); case BT_BAP_BCAST_SOURCE: return queue_find(bdb->broadcast_sources, match_codec, codec); case BT_BAP_BCAST_SINK: return queue_find(bdb->broadcast_sinks, match_codec, codec); } return NULL; } static void *ltv_merge(struct iovec *data, struct iovec *cont) { uint8_t delimiter = 0; if (!data) return NULL; if (!cont || !cont->iov_len || !cont->iov_base) return data->iov_base; util_iov_append(data, &delimiter, sizeof(delimiter)); return util_iov_append(data, cont->iov_base, cont->iov_len); } static void bap_pac_chan_add(struct bt_bap_pac *pac, uint8_t count, uint32_t location) { struct bt_bap_chan *chan; if (!pac->channels) pac->channels = queue_new(); chan = new0(struct bt_bap_chan, 1); chan->count = count; chan->location = location; queue_push_tail(pac->channels, chan); } static void bap_pac_foreach_channel(size_t i, uint8_t l, uint8_t t, uint8_t *v, void *user_data) { struct bt_bap_pac *pac = user_data; if (!v) return; bap_pac_chan_add(pac, *v, bt_bap_pac_get_locations(pac)); } static void bap_pac_update_channels(struct bt_bap_pac *pac, struct iovec *data) { uint8_t type = 0x03; if (!data) return; util_ltv_foreach(data->iov_base, data->iov_len, &type, bap_pac_foreach_channel, pac); /* If record didn't set a channel count but set a location use that as * channel count. */ if (queue_isempty(pac->channels) && pac->qos.location) bap_pac_chan_add(pac, pac->qos.location, pac->qos.location); } static void bap_pac_merge(struct bt_bap_pac *pac, struct iovec *data, struct iovec *metadata) { /* Merge data into existing record */ if (pac->data) ltv_merge(pac->data, data); else pac->data = util_iov_dup(data, 1); /* Update channels */ bap_pac_update_channels(pac, data); /* Merge metadata into existing record */ if (pac->metadata) ltv_merge(pac->metadata, metadata); else pac->metadata = util_iov_dup(metadata, 1); } static struct bt_bap_pac *bap_pac_new(struct bt_bap_db *bdb, const char *name, uint8_t type, struct bt_bap_codec *codec, struct bt_bap_pac_qos *qos, struct iovec *data, struct iovec *metadata) { struct bt_bap_pac *pac; pac = new0(struct bt_bap_pac, 1); pac->bdb = bdb; pac->name = name ? strdup(name) : NULL; pac->type = type; if (codec) pac->codec = *codec; if (qos) pac->qos = *qos; bap_pac_merge(pac, data, metadata); return pac; } static void bap_pac_free(void *data) { struct bt_bap_pac *pac = data; free(pac->name); util_iov_free(pac->metadata, 1); util_iov_free(pac->data, 1); queue_destroy(pac->channels, free); free(pac); } static void pacs_sink_location_changed(struct bt_pacs *pacs) { uint32_t location = cpu_to_le32(pacs->sink_loc_value); gatt_db_attribute_notify(pacs->sink_loc, (void *)&location, sizeof(location), NULL); } static void pacs_add_sink_location(struct bt_pacs *pacs, uint32_t location) { /* Check if location value needs updating */ if (location == pacs->sink_loc_value) return; pacs->sink_loc_value |= location; pacs_sink_location_changed(pacs); } static void pacs_supported_context_changed(struct bt_pacs *pacs) { struct bt_pacs_context ctx; memset(&ctx, 0, sizeof(ctx)); ctx.snk = cpu_to_le16(pacs->supported_sink_context_value); ctx.src = cpu_to_le16(pacs->supported_source_context_value); gatt_db_attribute_notify(pacs->supported_context, (void *)&ctx, sizeof(ctx), NULL); } static void pacs_add_sink_supported_context(struct bt_pacs *pacs, uint16_t context) { context |= pacs->supported_sink_context_value; /* Check if context value needs updating */ if (context == pacs->supported_sink_context_value) return; pacs->supported_sink_context_value = context; pacs_supported_context_changed(pacs); } static void pacs_context_changed(struct bt_pacs *pacs) { struct bt_pacs_context ctx; memset(&ctx, 0, sizeof(ctx)); ctx.snk = cpu_to_le16(pacs->sink_context_value); ctx.src = cpu_to_le16(pacs->source_context_value); gatt_db_attribute_notify(pacs->context, (void *)&ctx, sizeof(ctx), NULL); } static void pacs_add_sink_context(struct bt_pacs *pacs, uint16_t context) { context |= pacs->supported_sink_context_value; /* Check if context value needs updating */ if (context == pacs->sink_context_value) return; pacs->sink_context_value = context; pacs_context_changed(pacs); } static void bap_add_sink(struct bt_bap_pac *pac) { struct iovec iov; uint8_t value[512]; queue_push_tail(pac->bdb->sinks, pac); memset(value, 0, sizeof(value)); iov.iov_base = value; iov.iov_len = 0; queue_foreach(pac->bdb->sinks, pac_foreach, &iov); pacs_add_sink_location(pac->bdb->pacs, pac->qos.location); pacs_add_sink_supported_context(pac->bdb->pacs, pac->qos.supported_context); pacs_add_sink_context(pac->bdb->pacs, pac->qos.context); gatt_db_attribute_notify(pac->bdb->pacs->sink, iov.iov_base, iov.iov_len, NULL); } static void pacs_source_location_changed(struct bt_pacs *pacs) { uint32_t location = cpu_to_le32(pacs->source_loc_value); gatt_db_attribute_notify(pacs->source_loc, (void *)&location, sizeof(location), NULL); } static void pacs_add_source_location(struct bt_pacs *pacs, uint32_t location) { location |= pacs->source_loc_value; /* Check if location value needs updating */ if (location == pacs->source_loc_value) return; pacs->source_loc_value = location; pacs_source_location_changed(pacs); } static void pacs_add_source_supported_context(struct bt_pacs *pacs, uint16_t context) { context |= pacs->supported_source_context_value; /* Check if context value needs updating */ if (context == pacs->supported_source_context_value) return; pacs->supported_source_context_value = context; pacs_supported_context_changed(pacs); } static void pacs_add_source_context(struct bt_pacs *pacs, uint16_t context) { context |= pacs->source_context_value; /* Check if context value needs updating */ if (context == pacs->source_context_value) return; pacs->source_context_value = context; pacs_context_changed(pacs); } static void bap_add_source(struct bt_bap_pac *pac) { struct iovec iov; uint8_t value[512]; queue_push_tail(pac->bdb->sources, pac); memset(value, 0, sizeof(value)); iov.iov_base = value; iov.iov_len = 0; queue_foreach(pac->bdb->sources, pac_foreach, &iov); pacs_add_source_location(pac->bdb->pacs, pac->qos.location); pacs_add_source_supported_context(pac->bdb->pacs, pac->qos.supported_context); pacs_add_source_context(pac->bdb->pacs, pac->qos.context); gatt_db_attribute_notify(pac->bdb->pacs->source, iov.iov_base, iov.iov_len, NULL); } static void bap_add_broadcast_source(struct bt_bap_pac *pac) { queue_push_tail(pac->bdb->broadcast_sources, pac); } static void bap_add_broadcast_sink(struct bt_bap_pac *pac) { queue_push_tail(pac->bdb->broadcast_sinks, pac); /* Update local PACS for broadcast sink also, when registering an * endpoint */ pacs_add_sink_location(pac->bdb->pacs, pac->qos.location); pacs_add_sink_supported_context(pac->bdb->pacs, pac->qos.supported_context); } static void notify_pac_added(void *data, void *user_data) { struct bt_bap_pac_changed *changed = data; struct bt_bap_pac *pac = user_data; if (changed->added) changed->added(pac, changed->data); } static void notify_session_pac_added(void *data, void *user_data) { struct bt_bap *bap = data; queue_foreach(bap->pac_cbs, notify_pac_added, user_data); } struct bt_bap_pac *bt_bap_add_vendor_pac(struct gatt_db *db, const char *name, uint8_t type, uint8_t id, uint16_t cid, uint16_t vid, struct bt_bap_pac_qos *qos, struct iovec *data, struct iovec *metadata) { struct bt_bap_db *bdb; struct bt_bap_pac *pac; struct bt_bap_codec codec; if (!db) return NULL; bdb = bap_get_db(db); if (!bdb) return NULL; if ((id != 0xff) && ((cid != 0U) || (vid != 0U))) return NULL; codec.id = id; codec.cid = cid; codec.vid = vid; pac = bap_pac_new(bdb, name, type, &codec, qos, data, metadata); switch (type) { case BT_BAP_SINK: bap_add_sink(pac); break; case BT_BAP_SOURCE: bap_add_source(pac); break; case BT_BAP_BCAST_SOURCE: bap_add_broadcast_source(pac); break; case BT_BAP_BCAST_SINK: bap_add_broadcast_sink(pac); break; default: bap_pac_free(pac); return NULL; } queue_foreach(sessions, notify_session_pac_added, pac); return pac; } struct bt_bap_pac *bt_bap_add_pac(struct gatt_db *db, const char *name, uint8_t type, uint8_t id, struct bt_bap_pac_qos *qos, struct iovec *data, struct iovec *metadata) { return bt_bap_add_vendor_pac(db, name, type, id, 0x0000, 0x0000, qos, data, metadata); } uint8_t bt_bap_pac_get_type(struct bt_bap_pac *pac) { if (!pac) return 0x00; return pac->type; } uint32_t bt_bap_pac_get_locations(struct bt_bap_pac *pac) { struct bt_pacs *pacs; if (!pac) return 0; if (pac->qos.location) return pac->qos.location; pacs = pac->bdb->pacs; switch (pac->type) { case BT_BAP_SOURCE: return pacs->source_loc_value; case BT_BAP_SINK: return pacs->sink_loc_value; default: return 0; } } uint16_t bt_bap_pac_get_supported_context(struct bt_bap_pac *pac) { struct bt_pacs *pacs; if (!pac) return 0; pacs = pac->bdb->pacs; switch (pac->type) { case BT_BAP_SOURCE: return pacs->supported_source_context_value; case BT_BAP_SINK: return pacs->supported_sink_context_value; default: return 0; } } uint16_t bt_bap_pac_get_context(struct bt_bap_pac *pac) { struct bt_pacs *pacs; if (!pac) return 0; pacs = pac->bdb->pacs; switch (pac->type) { case BT_BAP_SOURCE: return pacs->source_context_value; case BT_BAP_SINK: return pacs->sink_context_value; default: return 0; } } struct bt_bap_pac_qos *bt_bap_pac_get_qos(struct bt_bap_pac *pac) { if (!pac || !pac->qos.phy) return NULL; return &pac->qos; } struct iovec *bt_bap_pac_get_data(struct bt_bap_pac *pac) { return pac->data; } struct iovec *bt_bap_pac_get_metadata(struct bt_bap_pac *pac) { return pac->metadata; } uint8_t bt_bap_stream_get_type(struct bt_bap_stream *stream) { if (!stream) return 0x00; switch (bt_bap_pac_get_type(stream->lpac)) { case BT_BAP_SINK: case BT_BAP_SOURCE: return BT_BAP_STREAM_TYPE_UCAST; case BT_BAP_BCAST_SOURCE: case BT_BAP_BCAST_SINK: return BT_BAP_STREAM_TYPE_BCAST; } return 0x00; } static void notify_pac_removed(void *data, void *user_data) { struct bt_bap_pac_changed *changed = data; struct bt_bap_pac *pac = user_data; if (changed->removed) changed->removed(pac, changed->data); } static void notify_session_pac_removed(void *data, void *user_data) { struct bt_bap *bap = data; queue_foreach(bap->pac_cbs, notify_pac_removed, user_data); } bool bt_bap_pac_set_ops(struct bt_bap_pac *pac, struct bt_bap_pac_ops *ops, void *user_data) { if (!pac) return false; pac->ops = ops; pac->user_data = user_data; return true; } static bool match_stream_lpac(const void *data, const void *user_data) { const struct bt_bap_stream *stream = data; const struct bt_bap_pac *pac = user_data; return stream->lpac == pac; } static void remove_streams(void *data, void *user_data) { struct bt_bap *bap = data; struct bt_bap_pac *pac = user_data; struct bt_bap_stream *stream; stream = queue_remove_if(bap->streams, match_stream_lpac, pac); if (stream) bt_bap_stream_release(stream, NULL, NULL); } static void bap_pac_sink_removed(void *data, void *user_data) { struct bt_bap_pac *pac = data; struct bt_bap_pac_qos *qos = user_data; qos->location |= pac->qos.location; qos->supported_context |= pac->qos.supported_context; qos->context |= pac->qos.context; } bool bt_bap_remove_pac(struct bt_bap_pac *pac) { if (!pac) return false; if (queue_remove_if(pac->bdb->sinks, NULL, pac)) { struct bt_pacs *pacs = pac->bdb->pacs; struct bt_bap_pac_qos qos; memset(&qos, 0, sizeof(qos)); queue_foreach(pac->bdb->sinks, bap_pac_sink_removed, &qos); if (pacs->sink_loc_value != qos.location) { pacs->sink_loc_value = qos.location; pacs_sink_location_changed(pacs); } if (pacs->supported_sink_context_value != qos.supported_context) { pacs->supported_sink_context_value = qos.supported_context; pacs_supported_context_changed(pacs); } if (pacs->sink_context_value != qos.context) { pacs->sink_context_value = qos.context; pacs_context_changed(pacs); } goto found; } if (queue_remove_if(pac->bdb->sources, NULL, pac)) goto found; if (queue_remove_if(pac->bdb->broadcast_sources, NULL, pac)) goto found; return false; found: queue_foreach(sessions, remove_streams, pac); queue_foreach(sessions, notify_session_pac_removed, pac); bap_pac_free(pac); return true; } static void bap_db_free(void *data) { struct bt_bap_db *bdb = data; if (!bdb) return; queue_destroy(bdb->sinks, bap_pac_free); queue_destroy(bdb->sources, bap_pac_free); gatt_db_unref(bdb->db); free(bdb->pacs); free(bdb->ascs); free(bdb); } static void bap_ready_free(void *data) { struct bt_bap_ready *ready = data; if (ready->destroy) ready->destroy(ready->data); free(ready); } static void bap_state_free(void *data) { struct bt_bap_state *state = data; if (state->destroy) state->destroy(state->data); free(state); } static void bap_bis_cb_free(void *data) { struct bt_bap_bis_cb *bis_cb = data; if (bis_cb->destroy) bis_cb->destroy(bis_cb->data); free(bis_cb); } static void bap_bcode_cb_free(void *data) { struct bt_bap_bcode_cb *cb = data; if (cb->destroy) cb->destroy(cb->data); free(cb); } static void bap_ep_free(void *data) { struct bt_bap_endpoint *ep = data; if (ep && ep->stream) ep->stream->ep = NULL; free(ep); } static void bap_detached(void *data, void *user_data) { struct bt_bap_cb *cb = data; struct bt_bap *bap = user_data; cb->detached(bap, cb->user_data); } static void bap_stream_unref(void *data) { struct bt_bap_stream *stream = data; bt_bap_stream_unref(stream); } static void bap_free(void *data) { struct bt_bap *bap = data; timeout_remove(bap->process_id); bt_bap_detach(bap); bap_db_free(bap->rdb); queue_destroy(bap->pac_cbs, pac_changed_free); queue_destroy(bap->ready_cbs, bap_ready_free); queue_destroy(bap->state_cbs, bap_state_free); queue_destroy(bap->bis_cbs, bap_bis_cb_free); queue_destroy(bap->bcode_cbs, bap_bcode_cb_free); queue_destroy(bap->local_eps, free); queue_destroy(bap->remote_eps, bap_ep_free); queue_destroy(bap->reqs, bap_req_free); queue_destroy(bap->notify, NULL); queue_destroy(bap->streams, bap_stream_unref); free(bap); } unsigned int bt_bap_register(bt_bap_func_t attached, bt_bap_func_t detached, void *user_data) { struct bt_bap_cb *cb; static unsigned int id; if (!attached && !detached) return 0; if (!bap_cbs) bap_cbs = queue_new(); cb = new0(struct bt_bap_cb, 1); cb->id = ++id ? id : ++id; cb->attached = attached; cb->detached = detached; cb->user_data = user_data; queue_push_tail(bap_cbs, cb); return cb->id; } static bool match_id(const void *data, const void *match_data) { const struct bt_bap_cb *cb = data; unsigned int id = PTR_TO_UINT(match_data); return (cb->id == id); } bool bt_bap_unregister(unsigned int id) { struct bt_bap_cb *cb; cb = queue_remove_if(bap_cbs, match_id, UINT_TO_PTR(id)); if (!cb) return false; free(cb); return true; } static void bap_attached(void *data, void *user_data) { struct bt_bap_cb *cb = data; struct bt_bap *bap = user_data; cb->attached(bap, cb->user_data); } struct bt_bap *bt_bap_new(struct gatt_db *ldb, struct gatt_db *rdb) { struct bt_bap *bap; struct bt_bap_db *bdb; if (!ldb) return NULL; bdb = bap_get_db(ldb); if (!bdb) return NULL; bap = new0(struct bt_bap, 1); bap->ldb = bdb; bap->reqs = queue_new(); bap->notify = queue_new(); bap->pac_cbs = queue_new(); bap->ready_cbs = queue_new(); bap->streams = queue_new(); bap->state_cbs = queue_new(); bap->bis_cbs = queue_new(); bap->bcode_cbs = queue_new(); bap->local_eps = queue_new(); if (!rdb) goto done; bdb = new0(struct bt_bap_db, 1); bdb->db = gatt_db_ref(rdb); bdb->sinks = queue_new(); bdb->sources = queue_new(); bap->rdb = bdb; bap->remote_eps = queue_new(); done: return bt_bap_ref(bap); } bool bt_bap_set_user_data(struct bt_bap *bap, void *user_data) { if (!bap) return false; bap->user_data = user_data; return true; } void *bt_bap_get_user_data(struct bt_bap *bap) { if (!bap) return NULL; return bap->user_data; } struct bt_att *bt_bap_get_att(struct bt_bap *bap) { if (!bap) return NULL; if (bap->att) return bap->att; return bt_gatt_client_get_att(bap->client); } struct bt_bap *bt_bap_ref(struct bt_bap *bap) { if (!bap) return NULL; __sync_fetch_and_add(&bap->ref_count, 1); return bap; } void bt_bap_unref(struct bt_bap *bap) { if (!bap) return; if (__sync_sub_and_fetch(&bap->ref_count, 1)) return; bap_free(bap); } static void bap_notify_ready(struct bt_bap *bap) { const struct queue_entry *entry; if (!bt_bap_ref_safe(bap)) return; for (entry = queue_get_entries(bap->ready_cbs); entry; entry = entry->next) { struct bt_bap_ready *ready = entry->data; ready->func(bap, ready->data); } bt_bap_unref(bap); } static void bap_parse_pacs(struct bt_bap *bap, uint8_t type, struct queue *queue, const uint8_t *value, uint16_t len) { struct bt_pacs_read_rsp *rsp; struct iovec iov = { .iov_base = (void *) value, .iov_len = len, }; int i; rsp = util_iov_pull_mem(&iov, sizeof(*rsp)); if (!rsp) { DBG(bap, "Unable to parse PAC"); return; } DBG(bap, "PAC(s) %u", rsp->num_pac); for (i = 0; i < rsp->num_pac; i++) { struct bt_bap_pac *pac; struct bt_pac *p; struct bt_ltv *cc; struct bt_pac_metadata *meta; struct iovec data, metadata; p = util_iov_pull_mem(&iov, sizeof(*p)); if (!p) { DBG(bap, "Unable to parse PAC"); return; } if (p->codec.id == 0xff) { p->codec.cid = le16_to_cpu(p->codec.cid); p->codec.vid = le16_to_cpu(p->codec.vid); } pac = NULL; if (!bt_bap_debug_caps(iov.iov_base, p->cc_len, bap->debug_func, bap->debug_data)) return; cc = util_iov_pull_mem(&iov, p->cc_len); if (!cc) { DBG(bap, "Unable to parse PAC codec capabilities"); return; } meta = util_iov_pull_mem(&iov, sizeof(*meta)); if (!meta) { DBG(bap, "Unable to parse PAC metadata"); return; } data.iov_len = p->cc_len; data.iov_base = cc; metadata.iov_len = meta->len; metadata.iov_base = meta->data; util_iov_pull_mem(&iov, meta->len); DBG(bap, "PAC #%u: type %u codec 0x%02x cc_len %u meta_len %u", i, type, p->codec.id, p->cc_len, meta->len); /* Check if there is already a PAC record for the codec */ pac = bap_pac_find(bap->rdb, type, &p->codec); if (pac) { bap_pac_merge(pac, &data, &metadata); continue; } pac = bap_pac_new(bap->rdb, NULL, type, &p->codec, NULL, &data, &metadata); if (!pac) continue; queue_push_tail(queue, pac); } } static void read_source_pac(bool success, uint8_t att_ecode, const uint8_t *value, uint16_t length, void *user_data) { struct bt_bap *bap = user_data; if (!success) { DBG(bap, "Unable to read Source PAC: error 0x%02x", att_ecode); return; } bap_parse_pacs(bap, BT_BAP_SOURCE, bap->rdb->sources, value, length); } static void read_sink_pac(bool success, uint8_t att_ecode, const uint8_t *value, uint16_t length, void *user_data) { struct bt_bap *bap = user_data; if (!success) { DBG(bap, "Unable to read Sink PAC: error 0x%02x", att_ecode); return; } bap_parse_pacs(bap, BT_BAP_SINK, bap->rdb->sinks, value, length); } static void read_source_pac_loc(bool success, uint8_t att_ecode, const uint8_t *value, uint16_t length, void *user_data) { struct bt_bap *bap = user_data; struct bt_pacs *pacs = bap_get_pacs(bap); if (!success) { DBG(bap, "Unable to read Source PAC Location: error 0x%02x", att_ecode); return; } if (length != sizeof(uint32_t)) { DBG(bap, "Invalid Source PAC Location size: %d", length); return; } pacs->source_loc_value = get_le32(value); /* Resume reading sinks if supported but for some reason is empty */ if (pacs->source && queue_isempty(bap->rdb->sources)) { uint16_t value_handle; if (gatt_db_attribute_get_char_data(pacs->source, NULL, &value_handle, NULL, NULL, NULL)) bt_gatt_client_read_value(bap->client, value_handle, read_source_pac, bap, NULL); } } static void read_sink_pac_loc(bool success, uint8_t att_ecode, const uint8_t *value, uint16_t length, void *user_data) { struct bt_bap *bap = user_data; struct bt_pacs *pacs = bap_get_pacs(bap); if (!success) { DBG(bap, "Unable to read Sink PAC Location: error 0x%02x", att_ecode); return; } if (length != sizeof(uint32_t)) { DBG(bap, "Invalid Sink PAC Location size: %d", length); return; } pacs->sink_loc_value = get_le32(value); /* Resume reading sinks if supported but for some reason is empty */ if (pacs->sink && queue_isempty(bap->rdb->sinks)) { uint16_t value_handle; if (gatt_db_attribute_get_char_data(pacs->sink, NULL, &value_handle, NULL, NULL, NULL)) bt_gatt_client_read_value(bap->client, value_handle, read_sink_pac, bap, NULL); } } static void read_pac_context(bool success, uint8_t att_ecode, const uint8_t *value, uint16_t length, void *user_data) { struct bt_bap *bap = user_data; struct bt_pacs *pacs = bap_get_pacs(bap); const struct bt_pacs_context *ctx = (void *)value; if (!success) { DBG(bap, "Unable to read PAC Context: error 0x%02x", att_ecode); return; } if (length != sizeof(*ctx)) { DBG(bap, "Invalid PAC Context size: %d", length); return; } pacs->sink_context_value = le16_to_cpu(ctx->snk); pacs->source_context_value = le16_to_cpu(ctx->src); } static void read_pac_supported_context(bool success, uint8_t att_ecode, const uint8_t *value, uint16_t length, void *user_data) { struct bt_bap *bap = user_data; struct bt_pacs *pacs = bap_get_pacs(bap); const struct bt_pacs_context *ctx = (void *)value; if (!success) { DBG(bap, "Unable to read PAC Supproted Context: error 0x%02x", att_ecode); return; } if (length != sizeof(*ctx)) { DBG(bap, "Invalid PAC Supported Context size: %d", length); return; } pacs->supported_sink_context_value = le16_to_cpu(ctx->snk); pacs->supported_source_context_value = le16_to_cpu(ctx->src); } static void foreach_pacs_char(struct gatt_db_attribute *attr, void *user_data) { struct bt_bap *bap = user_data; uint16_t value_handle; bt_uuid_t uuid, uuid_sink, uuid_source, uuid_sink_loc, uuid_source_loc; bt_uuid_t uuid_context, uuid_supported_context; struct bt_pacs *pacs; if (!gatt_db_attribute_get_char_data(attr, NULL, &value_handle, NULL, NULL, &uuid)) return; bt_uuid16_create(&uuid_sink, PAC_SINK_CHRC_UUID); bt_uuid16_create(&uuid_source, PAC_SOURCE_CHRC_UUID); bt_uuid16_create(&uuid_sink_loc, PAC_SINK_LOC_CHRC_UUID); bt_uuid16_create(&uuid_source_loc, PAC_SOURCE_LOC_CHRC_UUID); bt_uuid16_create(&uuid_context, PAC_CONTEXT); bt_uuid16_create(&uuid_supported_context, PAC_SUPPORTED_CONTEXT); if (!bt_uuid_cmp(&uuid, &uuid_sink)) { DBG(bap, "Sink PAC found: handle 0x%04x", value_handle); pacs = bap_get_pacs(bap); if (!pacs) return; if (!pacs->sink) pacs->sink = attr; bt_gatt_client_read_value(bap->client, value_handle, read_sink_pac, bap, NULL); } if (!bt_uuid_cmp(&uuid, &uuid_source)) { DBG(bap, "Source PAC found: handle 0x%04x", value_handle); pacs = bap_get_pacs(bap); if (!pacs) return; if (!pacs->source) pacs->source = attr; bt_gatt_client_read_value(bap->client, value_handle, read_source_pac, bap, NULL); } if (!bt_uuid_cmp(&uuid, &uuid_sink_loc)) { DBG(bap, "Sink PAC Location found: handle 0x%04x", value_handle); pacs = bap_get_pacs(bap); if (!pacs || pacs->sink_loc) return; pacs->sink_loc = attr; bt_gatt_client_read_value(bap->client, value_handle, read_sink_pac_loc, bap, NULL); } if (!bt_uuid_cmp(&uuid, &uuid_source_loc)) { DBG(bap, "Source PAC Location found: handle 0x%04x", value_handle); pacs = bap_get_pacs(bap); if (!pacs || pacs->source_loc) return; pacs->source_loc = attr; bt_gatt_client_read_value(bap->client, value_handle, read_source_pac_loc, bap, NULL); } if (!bt_uuid_cmp(&uuid, &uuid_context)) { DBG(bap, "PAC Context found: handle 0x%04x", value_handle); pacs = bap_get_pacs(bap); if (!pacs || pacs->context) return; pacs->context = attr; bt_gatt_client_read_value(bap->client, value_handle, read_pac_context, bap, NULL); } if (!bt_uuid_cmp(&uuid, &uuid_supported_context)) { DBG(bap, "PAC Supported Context found: handle 0x%04x", value_handle); pacs = bap_get_pacs(bap); if (!pacs || pacs->supported_context) return; pacs->supported_context = attr; bt_gatt_client_read_value(bap->client, value_handle, read_pac_supported_context, bap, NULL); } } static void foreach_pacs_service(struct gatt_db_attribute *attr, void *user_data) { struct bt_bap *bap = user_data; struct bt_pacs *pacs = bap_get_pacs(bap); pacs->service = attr; gatt_db_service_foreach_char(attr, foreach_pacs_char, bap); } struct match_pac { struct bt_bap_codec codec; struct bt_bap_pac *lpac; struct bt_bap_pac *rpac; struct bt_bap_endpoint *ep; }; static bool match_stream_pac(struct bt_bap_pac *lpac, struct bt_bap_pac *rpac, void *user_data) { struct match_pac *match = user_data; if (!bap_codec_equal(&match->codec, &lpac->codec)) return true; match->lpac = lpac; match->rpac = rpac; return false; } static void ep_status_config(struct bt_bap *bap, struct bt_bap_endpoint *ep, struct iovec *iov) { struct bt_ascs_ase_status_config *cfg; struct bt_ltv *cc; uint32_t pd_min, pd_max, ppd_min, ppd_max; int i; cfg = util_iov_pull_mem(iov, sizeof(*cfg)); if (!cfg) { DBG(bap, "Unable to parse Config Status"); return; } pd_min = get_le24(cfg->pd_min); pd_max = get_le24(cfg->pd_max); ppd_min = get_le24(cfg->ppd_min); ppd_max = get_le24(cfg->ppd_max); DBG(bap, "codec 0x%02x framing 0x%02x phy 0x%02x rtn %u " "latency %u pd %u - %u ppd %u - %u", cfg->codec.id, cfg->framing, cfg->phy, cfg->rtn, le16_to_cpu(cfg->latency), pd_min, pd_max, ppd_min, ppd_max); if (iov->iov_len < cfg->cc_len) { DBG(bap, "Unable to parse Config Status: len %zu < %u cc_len", iov->iov_len, cfg->cc_len); return; } for (i = 0; iov->iov_len >= sizeof(*cc); i++) { cc = util_iov_pull_mem(iov, sizeof(*cc)); if (!cc) break; DBG(bap, "Codec Config #%u: type 0x%02x len %u", i, cc->type, cc->len); util_iov_pull_mem(iov, cc->len - 1); } /* Any previously applied codec configuration may be cached by the * server. */ if (!ep->stream) { struct match_pac match; match.lpac = NULL; match.rpac = NULL; match.codec.id = cfg->codec.id; match.codec.cid = le16_to_cpu(cfg->codec.cid); match.codec.vid = le16_to_cpu(cfg->codec.vid); bt_bap_foreach_pac(bap, ep->dir, match_stream_pac, &match); if (!match.lpac || !match.rpac) return; bap_stream_new(bap, ep, match.lpac, match.rpac, NULL, true); } if (!ep->stream) return; /* Set preferred settings */ if (ep->stream->rpac) { ep->stream->rpac->qos.framing = cfg->framing; ep->stream->rpac->qos.phy = cfg->phy; ep->stream->rpac->qos.rtn = cfg->rtn; ep->stream->rpac->qos.latency = le16_to_cpu(cfg->latency); ep->stream->rpac->qos.pd_min = pd_min; ep->stream->rpac->qos.pd_max = pd_max; ep->stream->rpac->qos.ppd_min = ppd_min; ep->stream->rpac->qos.ppd_max = ppd_max; } if (!ep->stream->cc) ep->stream->cc = new0(struct iovec, 1); util_iov_memcpy(ep->stream->cc, cfg->cc, cfg->cc_len); } static void bap_stream_config_cfm_cb(struct bt_bap_stream *stream, int err) { struct bt_bap *bap = stream->bap; if (err) { DBG(bap, "Config Confirmation failed: %d", err); bt_bap_stream_release(stream, NULL, NULL); return; } } static void bap_stream_config_cfm(struct bt_bap_stream *stream) { int err; if (!stream->lpac->ops || !stream->lpac->ops->config) return; err = stream->lpac->ops->config(stream, stream->cc, &stream->qos, bap_stream_config_cfm_cb, stream->lpac->user_data); if (err < 0) { DBG(stream->bap, "Unable to send Config Confirmation: %d", err); bt_bap_stream_release(stream, NULL, NULL); } } static void ep_status_qos(struct bt_bap *bap, struct bt_bap_endpoint *ep, struct iovec *iov) { struct bt_ascs_ase_status_qos *qos; uint32_t interval; uint32_t pd; uint16_t sdu; uint16_t latency; qos = util_iov_pull_mem(iov, sizeof(*qos)); if (!qos) { DBG(bap, "Unable to parse QoS Status"); return; } interval = get_le24(qos->interval); pd = get_le24(qos->pd); sdu = le16_to_cpu(qos->sdu); latency = le16_to_cpu(qos->latency); DBG(bap, "CIG 0x%02x CIS 0x%02x interval %u framing 0x%02x " "phy 0x%02x rtn %u latency %u pd %u", qos->cig_id, qos->cis_id, interval, qos->framing, qos->phy, qos->rtn, latency, pd); if (!ep->stream) return; ep->stream->qos.ucast.io_qos.interval = interval; ep->stream->qos.ucast.framing = qos->framing; ep->stream->qos.ucast.io_qos.phy = qos->phy; ep->stream->qos.ucast.io_qos.sdu = sdu; ep->stream->qos.ucast.io_qos.rtn = qos->rtn; ep->stream->qos.ucast.io_qos.latency = latency; ep->stream->qos.ucast.delay = pd; if (ep->old_state == BT_ASCS_ASE_STATE_CONFIG) bap_stream_config_cfm(ep->stream); } static void ep_status_metadata(struct bt_bap *bap, struct bt_bap_endpoint *ep, struct iovec *iov) { struct bt_ascs_ase_status_metadata *meta; meta = util_iov_pull_mem(iov, sizeof(*meta)); if (!meta) { DBG(bap, "Unable to parse Metadata Status"); return; } DBG(bap, "CIS 0x%02x CIG 0x%02x metadata len %u", meta->cis_id, meta->cig_id, meta->len); } static void bap_ep_set_status(struct bt_bap *bap, struct bt_bap_endpoint *ep, const uint8_t *value, uint16_t length) { struct bt_ascs_ase_status *rsp; struct iovec iov = { .iov_base = (void *) value, .iov_len = length, }; rsp = util_iov_pull_mem(&iov, sizeof(*rsp)); if (!rsp) return; ep->id = rsp->id; ep->old_state = ep->state; ep->state = rsp->state; DBG(bap, "ASE status: ep %p id 0x%02x handle 0x%04x state %s " "len %zu", ep, ep->id, gatt_db_attribute_get_handle(ep->attr), bt_bap_stream_statestr(ep->state), iov.iov_len); switch (ep->state) { case BT_ASCS_ASE_STATE_IDLE: break; case BT_ASCS_ASE_STATE_CONFIG: ep_status_config(bap, ep, &iov); break; case BT_ASCS_ASE_STATE_QOS: ep_status_qos(bap, ep, &iov); break; case BT_ASCS_ASE_STATE_ENABLING: case BT_ASCS_ASE_STATE_STREAMING: case BT_ASCS_ASE_STATE_DISABLING: ep_status_metadata(bap, ep, &iov); break; case BT_ASCS_ASE_STATE_RELEASING: break; } /* Only notifify if there is a stream */ if (!ep->stream) return; bap_stream_state_changed(ep->stream); } static void read_ase_status(bool success, uint8_t att_ecode, const uint8_t *value, uint16_t length, void *user_data) { struct bt_bap_endpoint *ep = user_data; struct bt_bap *bap = ep->bap; if (!success) { DBG(bap, "ASE read status failed: 0x%04x", att_ecode); return; } bap_ep_set_status(bap, ep, value, length); } static void bap_register(uint16_t att_ecode, void *user_data) { struct bt_bap_notify *notify = user_data; if (att_ecode) DBG(notify->bap, "ASE register failed: 0x%04x", att_ecode); } static void bap_endpoint_notify(struct bt_bap *bap, uint16_t value_handle, const uint8_t *value, uint16_t length, void *user_data) { struct bt_bap_endpoint *ep = user_data; bap_ep_set_status(bap, ep, value, length); } static void bap_notify(uint16_t value_handle, const uint8_t *value, uint16_t length, void *user_data) { struct bt_bap_notify *notify = user_data; if (notify->func) notify->func(notify->bap, value_handle, value, length, notify->user_data); } static void bap_notify_destroy(void *data) { struct bt_bap_notify *notify = data; struct bt_bap *bap = notify->bap; if (queue_remove_if(bap->notify, NULL, notify)) free(notify); } static unsigned int bap_register_notify(struct bt_bap *bap, uint16_t value_handle, bap_notify_t func, void *user_data) { struct bt_bap_notify *notify; notify = new0(struct bt_bap_notify, 1); notify->bap = bap; notify->func = func; notify->user_data = user_data; notify->id = bt_gatt_client_register_notify(bap->client, value_handle, bap_register, bap_notify, notify, bap_notify_destroy); if (!notify->id) { DBG(bap, "Unable to register for notifications"); free(notify); return 0; } queue_push_tail(bap->notify, notify); return notify->id; } static void bap_endpoint_attach(struct bt_bap *bap, struct bt_bap_endpoint *ep) { uint16_t value_handle; if (!gatt_db_attribute_get_char_data(ep->attr, NULL, &value_handle, NULL, NULL, NULL)) return; DBG(bap, "ASE handle 0x%04x", value_handle); ep->bap = bap; bt_gatt_client_read_value(bap->client, value_handle, read_ase_status, ep, NULL); ep->state_id = bap_register_notify(bap, value_handle, bap_endpoint_notify, ep); } static void bap_cp_notify(struct bt_bap *bap, uint16_t value_handle, const uint8_t *value, uint16_t length, void *user_data) { const struct bt_ascs_cp_rsp *rsp = (void *)value; const struct bt_ascs_ase_rsp *ase_rsp = NULL; struct bt_bap_req *req; int i; if (!bap->req) return; req = bap->req; bap->req = NULL; if (length < sizeof(*rsp)) { DBG(bap, "Invalid ASE CP notification: length %u < %zu", length, sizeof(*rsp)); goto done; } if (rsp->op != req->op) { DBG(bap, "Invalid ASE CP notification: op 0x%02x != 0x%02x", rsp->op, req->op); goto done; } length -= sizeof(*rsp); if (rsp->num_ase == 0xff) { ase_rsp = rsp->rsp; goto done; } for (i = 0; i < rsp->num_ase; i++) { if (length < sizeof(*ase_rsp)) { DBG(bap, "Invalid ASE CP notification: length %u < %zu", length, sizeof(*ase_rsp)); goto done; } ase_rsp = &rsp->rsp[i]; } done: bap_req_complete(req, ase_rsp); bap_process_queue(bap); } static void bap_cp_attach(struct bt_bap *bap) { uint16_t value_handle; struct bt_ascs *ascs = bap_get_ascs(bap); if (!gatt_db_attribute_get_char_data(ascs->ase_cp, NULL, &value_handle, NULL, NULL, NULL)) return; DBG(bap, "ASE CP handle 0x%04x", value_handle); bap->cp_id = bap_register_notify(bap, value_handle, bap_cp_notify, NULL); } static void foreach_ascs_char(struct gatt_db_attribute *attr, void *user_data) { struct bt_bap *bap = user_data; uint16_t value_handle; bt_uuid_t uuid, uuid_sink, uuid_source, uuid_cp; struct bt_ascs *ascs; if (!gatt_db_attribute_get_char_data(attr, NULL, &value_handle, NULL, NULL, &uuid)) return; bt_uuid16_create(&uuid_sink, ASE_SINK_UUID); bt_uuid16_create(&uuid_source, ASE_SOURCE_UUID); bt_uuid16_create(&uuid_cp, ASE_CP_UUID); if (!bt_uuid_cmp(&uuid, &uuid_sink) || !bt_uuid_cmp(&uuid, &uuid_source)) { struct bt_bap_endpoint *ep; ep = bap_get_endpoint(bap->remote_eps, bap->rdb, attr); if (!ep) return; if (!bt_uuid_cmp(&uuid, &uuid_sink)) DBG(bap, "ASE Sink found: handle 0x%04x", value_handle); else DBG(bap, "ASE Source found: handle 0x%04x", value_handle); bap_endpoint_attach(bap, ep); return; } if (!bt_uuid_cmp(&uuid, &uuid_cp)) { ascs = bap_get_ascs(bap); if (!ascs || ascs->ase_cp) return; ascs->ase_cp = attr; DBG(bap, "ASE Control Point found: handle 0x%04x", value_handle); bap_cp_attach(bap); } } static void foreach_ascs_service(struct gatt_db_attribute *attr, void *user_data) { struct bt_bap *bap = user_data; struct bt_ascs *ascs = bap_get_ascs(bap); ascs->service = attr; gatt_db_service_set_claimed(attr, true); gatt_db_service_foreach_char(attr, foreach_ascs_char, bap); } static void bap_endpoint_foreach(void *data, void *user_data) { struct bt_bap_endpoint *ep = data; struct bt_bap *bap = user_data; bap_endpoint_attach(bap, ep); } static void bap_attach_att(struct bt_bap *bap, struct bt_att *att) { if (bap->disconn_id) { if (att == bt_bap_get_att(bap)) return; bt_att_unregister_disconnect(bap->att, bap->disconn_id); } bap->disconn_id = bt_att_register_disconnect(bap->att, bap_disconnected, bap, NULL); } static void bap_idle(void *data) { struct bt_bap *bap = data; bap->idle_id = 0; bap_notify_ready(bap); } bool bt_bap_attach(struct bt_bap *bap, struct bt_gatt_client *client) { bt_uuid_t uuid; if (queue_find(sessions, NULL, bap)) { /* If instance already been set but there is no client proceed * to clone it otherwise considered it already attached. */ if (client && !bap->client) goto clone; return true; } if (!sessions) sessions = queue_new(); queue_push_tail(sessions, bap); queue_foreach(bap_cbs, bap_attached, bap); if (!client) { if (bap->att) bap_attach_att(bap, bap->att); return true; } if (bap->client) return false; clone: bap->client = bt_gatt_client_clone(client); if (!bap->client) return false; bap_attach_att(bap, bt_gatt_client_get_att(client)); bap->idle_id = bt_gatt_client_idle_register(bap->client, bap_idle, bap, NULL); if (bap->rdb->pacs) { uint16_t value_handle; struct bt_pacs *pacs = bap->rdb->pacs; /* Resume reading sinks if supported */ if (pacs->sink && queue_isempty(bap->rdb->sinks)) { if (gatt_db_attribute_get_char_data(pacs->sink, NULL, &value_handle, NULL, NULL, NULL)) { bt_gatt_client_read_value(bap->client, value_handle, read_sink_pac, bap, NULL); } } /* Resume reading sink locations if supported */ if (pacs->sink && pacs->sink_loc && !pacs->sink_loc_value) { if (gatt_db_attribute_get_char_data(pacs->sink_loc, NULL, &value_handle, NULL, NULL, NULL)) { bt_gatt_client_read_value(bap->client, value_handle, read_sink_pac_loc, bap, NULL); } } /* Resume reading sources if supported */ if (pacs->source && queue_isempty(bap->rdb->sources)) { if (gatt_db_attribute_get_char_data(pacs->source, NULL, &value_handle, NULL, NULL, NULL)) { bt_gatt_client_read_value(bap->client, value_handle, read_source_pac, bap, NULL); } } /* Resume reading source locations if supported */ if (pacs->source && pacs->source_loc && !pacs->source_loc_value) { if (gatt_db_attribute_get_char_data(pacs->source_loc, NULL, &value_handle, NULL, NULL, NULL)) { bt_gatt_client_read_value(bap->client, value_handle, read_source_pac_loc, bap, NULL); } } /* Resume reading supported contexts if supported */ if (pacs->sink && pacs->supported_context && !pacs->supported_sink_context_value && !pacs->supported_source_context_value) { if (gatt_db_attribute_get_char_data( pacs->supported_context, NULL, &value_handle, NULL, NULL, NULL)) { bt_gatt_client_read_value(bap->client, value_handle, read_pac_supported_context, bap, NULL); } } /* Resume reading contexts if supported */ if (pacs->sink && pacs->context && !pacs->sink_context_value && !pacs->source_context_value) { if (gatt_db_attribute_get_char_data(pacs->context, NULL, &value_handle, NULL, NULL, NULL)) { bt_gatt_client_read_value(bap->client, value_handle, read_pac_context, bap, NULL); } } queue_foreach(bap->remote_eps, bap_endpoint_foreach, bap); bap_cp_attach(bap); return true; } bt_uuid16_create(&uuid, PACS_UUID); gatt_db_foreach_service(bap->rdb->db, &uuid, foreach_pacs_service, bap); bt_uuid16_create(&uuid, ASCS_UUID); gatt_db_foreach_service(bap->rdb->db, &uuid, foreach_ascs_service, bap); return true; } bool bt_bap_attach_broadcast(struct bt_bap *bap) { struct bt_bap_endpoint *ep; if (queue_find(sessions, NULL, bap)) return true; if (!sessions) sessions = queue_new(); queue_push_tail(sessions, bap); ep = bap_get_endpoint_bcast(bap->remote_eps, bap->ldb, BT_BAP_BCAST_SOURCE); if (ep) ep->bap = bap; return true; } static void stream_foreach_detach(void *data, void *user_data) { struct bt_bap_stream *stream = data; stream_set_state(stream, BT_BAP_STREAM_STATE_IDLE); } static void bap_req_detach(void *data) { struct bt_bap_req *req = data; bap_req_complete(req, NULL); } void bt_bap_detach(struct bt_bap *bap) { DBG(bap, "%p", bap); if (!queue_remove(sessions, bap)) return; /* Cancel ongoing request */ if (bap->req) { bap_req_detach(bap->req); bap->req = NULL; } bt_gatt_client_idle_unregister(bap->client, bap->idle_id); /* Cancel queued requests */ queue_remove_all(bap->reqs, NULL, NULL, bap_req_detach); bt_gatt_client_unref(bap->client); bap->client = NULL; bt_att_unregister_disconnect(bap->att, bap->disconn_id); bap->att = NULL; queue_foreach(bap->streams, stream_foreach_detach, bap); queue_foreach(bap_cbs, bap_detached, bap); } bool bt_bap_set_debug(struct bt_bap *bap, bt_bap_debug_func_t func, void *user_data, bt_bap_destroy_func_t destroy) { if (!bap) return false; if (bap->debug_destroy) bap->debug_destroy(bap->debug_data); bap->debug_func = func; bap->debug_destroy = destroy; bap->debug_data = user_data; return true; } unsigned int bt_bap_ready_register(struct bt_bap *bap, bt_bap_ready_func_t func, void *user_data, bt_bap_destroy_func_t destroy) { struct bt_bap_ready *ready; static unsigned int id; if (!bap) return 0; ready = new0(struct bt_bap_ready, 1); ready->id = ++id ? id : ++id; ready->func = func; ready->destroy = destroy; ready->data = user_data; queue_push_tail(bap->ready_cbs, ready); return ready->id; } static bool match_ready_id(const void *data, const void *match_data) { const struct bt_bap_ready *ready = data; unsigned int id = PTR_TO_UINT(match_data); return (ready->id == id); } bool bt_bap_ready_unregister(struct bt_bap *bap, unsigned int id) { struct bt_bap_ready *ready; ready = queue_remove_if(bap->ready_cbs, match_ready_id, UINT_TO_PTR(id)); if (!ready) return false; bap_ready_free(ready); return true; } unsigned int bt_bap_state_register(struct bt_bap *bap, bt_bap_state_func_t func, bt_bap_connecting_func_t connecting, void *user_data, bt_bap_destroy_func_t destroy) { struct bt_bap_state *state; static unsigned int id; if (!bap) return 0; state = new0(struct bt_bap_state, 1); state->id = ++id ? id : ++id; state->func = func; state->connecting = connecting; state->destroy = destroy; state->data = user_data; queue_push_tail(bap->state_cbs, state); return state->id; } static bool match_state_id(const void *data, const void *match_data) { const struct bt_bap_state *state = data; unsigned int id = PTR_TO_UINT(match_data); return (state->id == id); } bool bt_bap_state_unregister(struct bt_bap *bap, unsigned int id) { struct bt_bap_state *state; if (!bap) return false; state = queue_remove_if(bap->state_cbs, match_state_id, UINT_TO_PTR(id)); if (!state) return false; bap_state_free(state); return false; } unsigned int bt_bap_bis_cb_register(struct bt_bap *bap, bt_bap_bis_func_t probe, bt_bap_func_t remove, void *user_data, bt_bap_destroy_func_t destroy) { struct bt_bap_bis_cb *bis_cb; static unsigned int id; if (!bap) return 0; bis_cb = new0(struct bt_bap_bis_cb, 1); bis_cb->id = ++id ? id : ++id; bis_cb->probe = probe; bis_cb->remove = remove; bis_cb->destroy = destroy; bis_cb->data = user_data; queue_push_tail(bap->bis_cbs, bis_cb); return bis_cb->id; } static bool match_bis_cb_id(const void *data, const void *match_data) { const struct bt_bap_bis_cb *bis_cb = data; unsigned int id = PTR_TO_UINT(match_data); return (bis_cb->id == id); } bool bt_bap_bis_cb_unregister(struct bt_bap *bap, unsigned int id) { struct bt_bap_bis_cb *bis_cb; if (!bap) return false; bis_cb = queue_remove_if(bap->bis_cbs, match_bis_cb_id, UINT_TO_PTR(id)); if (!bis_cb) return false; bap_bis_cb_free(bis_cb); return false; } void bt_bap_bis_probe(struct bt_bap *bap, uint8_t bis, uint8_t sgrp, struct iovec *caps, struct iovec *meta, struct bt_bap_qos *qos) { const struct queue_entry *entry; if (!bt_bap_ref_safe(bap)) return; entry = queue_get_entries(bap->bis_cbs); while (entry) { struct bt_bap_bis_cb *cb = entry->data; entry = entry->next; if (cb->probe) cb->probe(bis, sgrp, caps, meta, qos, cb->data); } bt_bap_unref(bap); } void bt_bap_bis_remove(struct bt_bap *bap) { const struct queue_entry *entry; if (!bt_bap_ref_safe(bap)) return; entry = queue_get_entries(bap->bis_cbs); while (entry) { struct bt_bap_bis_cb *cb = entry->data; entry = entry->next; if (cb->remove) cb->remove(bap, cb->data); } bt_bap_unref(bap); } const char *bt_bap_stream_statestr(uint8_t state) { switch (state) { case BT_BAP_STREAM_STATE_IDLE: return "idle"; case BT_BAP_STREAM_STATE_CONFIG: return "config"; case BT_BAP_STREAM_STATE_QOS: return "qos"; case BT_BAP_STREAM_STATE_ENABLING: return "enabling"; case BT_BAP_STREAM_STATE_STREAMING: return "streaming"; case BT_BAP_STREAM_STATE_DISABLING: return "disabling"; case BT_BAP_STREAM_STATE_RELEASING: return "releasing"; } return "unknown"; } static void bap_foreach_pac(struct queue *l, struct queue *r, bt_bap_pac_foreach_t func, void *user_data) { const struct queue_entry *el; for (el = queue_get_entries(l); el; el = el->next) { struct bt_bap_pac *lpac = el->data; const struct queue_entry *er; for (er = queue_get_entries(r); er; er = er->next) { struct bt_bap_pac *rpac = er->data; /* Skip checking codec for bcast source, * it will be checked when BASE info are received */ if ((rpac->type != BT_BAP_BCAST_SOURCE) && (!bap_codec_equal(&lpac->codec, &rpac->codec))) continue; if (!func(lpac, rpac, user_data)) return; } } } void bt_bap_foreach_pac(struct bt_bap *bap, uint8_t type, bt_bap_pac_foreach_t func, void *user_data) { if (!bap || !func || !bap->rdb || queue_isempty(bap_db)) return; switch (type) { case BT_BAP_SINK: return bap_foreach_pac(bap->ldb->sources, bap->rdb->sinks, func, user_data); case BT_BAP_SOURCE: return bap_foreach_pac(bap->ldb->sinks, bap->rdb->sources, func, user_data); case BT_BAP_BCAST_SOURCE: case BT_BAP_BCAST_SINK: return bap_foreach_pac(bap->ldb->broadcast_sinks, bap->rdb->broadcast_sources, func, user_data); } } int bt_bap_pac_get_vendor_codec(struct bt_bap_pac *pac, uint8_t *id, uint16_t *cid, uint16_t *vid, struct iovec **data, struct iovec **metadata) { if (!pac) return -EINVAL; if (id) *id = pac->codec.id; if (cid) *cid = pac->codec.cid; if (vid) *vid = pac->codec.cid; if (data && pac->data) *data = pac->data; if (metadata && pac->metadata) *metadata = pac->metadata; return 0; } int bt_bap_pac_get_codec(struct bt_bap_pac *pac, uint8_t *id, struct iovec **data, struct iovec **metadata) { return bt_bap_pac_get_vendor_codec(pac, id, NULL, NULL, data, metadata); } void bt_bap_pac_set_user_data(struct bt_bap_pac *pac, void *user_data) { pac->user_data = user_data; } void *bt_bap_pac_get_user_data(struct bt_bap_pac *pac) { return pac->user_data; } bool bt_bap_pac_bcast_is_local(struct bt_bap *bap, struct bt_bap_pac *pac) { if (!bap->ldb) return false; if (queue_find(bap->ldb->broadcast_sinks, NULL, pac)) return true; if (queue_find(bap->ldb->broadcast_sources, NULL, pac)) return true; return false; } static bool find_ep_unused(const void *data, const void *user_data) { const struct bt_bap_endpoint *ep = data; const struct match_pac *match = user_data; if (ep->stream) return false; if (match->rpac) return ep->dir == match->rpac->type; else return true; } static bool find_ep_pacs(const void *data, const void *user_data) { const struct bt_bap_endpoint *ep = data; const struct match_pac *match = user_data; if (!ep->stream) return false; if (ep->stream->lpac != match->lpac) return false; if (ep->stream->rpac != match->rpac) return false; switch (ep->state) { case BT_BAP_STREAM_STATE_CONFIG: case BT_BAP_STREAM_STATE_QOS: return true; } return false; } static bool find_ep_source(const void *data, const void *user_data) { const struct bt_bap_endpoint *ep = data; if (ep->dir == BT_BAP_BCAST_SINK) return true; else return false; } unsigned int bt_bap_stream_config(struct bt_bap_stream *stream, struct bt_bap_qos *qos, struct iovec *data, bt_bap_stream_func_t func, void *user_data) { unsigned int id; struct bt_bap *bap; if (!bap_stream_valid(stream)) return 0; if (!stream->ops || !stream->ops->config) return 0; if (!bt_bap_ref_safe(stream->bap)) return 0; bap = stream->bap; id = stream->ops->config(stream, qos, data, func, user_data); bt_bap_unref(bap); return id; } static bool match_pac(struct bt_bap_pac *lpac, struct bt_bap_pac *rpac, void *user_data) { struct match_pac *match = user_data; if (match->lpac && match->lpac != lpac) return true; if (match->rpac && match->rpac != rpac) return true; match->lpac = lpac; match->rpac = rpac; return false; } int bt_bap_select(struct bt_bap_pac *lpac, struct bt_bap_pac *rpac, int *count, bt_bap_pac_select_t func, void *user_data) { const struct queue_entry *lchan, *rchan; int selected = 0; if (!lpac || !rpac || !func) return -EINVAL; if (!lpac->ops || !lpac->ops->select) return -EOPNOTSUPP; for (lchan = queue_get_entries(lpac->channels); lchan; lchan = lchan->next) { struct bt_bap_chan *lc = lchan->data; struct bt_bap_chan map = *lc; int i; for (i = 0, rchan = queue_get_entries(rpac->channels); rchan; rchan = rchan->next, i++) { struct bt_bap_chan *rc = rchan->data; /* Try matching the channel count */ if (!(map.count & rc->count)) break; /* Check if location was set otherwise attempt to * assign one based on the number of channels it * supports. */ if (!rc->location) { rc->location = bt_bap_pac_get_locations(rpac); /* If channel count is 1 use a single * location */ if (rc->count == 0x01) rc->location &= BIT(i); } /* Try matching the channel location */ if (!(map.location & rc->location)) continue; lpac->ops->select(lpac, rpac, map.location & rc->location, &rpac->qos, func, user_data, lpac->user_data); selected++; /* Check if there are any channels left to select */ map.count &= ~(map.count & rc->count); /* Check if there are any locations left to select */ map.location &= ~(map.location & rc->location); if (!map.count || !map.location) break; /* Check if device require AC*(i) settings */ if (rc->count == 0x01) map.count = map.count >> 1; } } /* Fallback to no channel allocation since none could be matched. */ if (!selected) { lpac->ops->select(lpac, rpac, 0, &rpac->qos, func, user_data, lpac->user_data); selected++; } if (count) *count += selected; return 0; } void bt_bap_cancel_select(struct bt_bap_pac *lpac, bt_bap_pac_select_t func, void *user_data) { if (!lpac || !func) return; if (!lpac->ops || !lpac->ops->cancel_select) return; lpac->ops->cancel_select(lpac, func, user_data, lpac->user_data); } static struct bt_bap_stream *bap_bcast_stream_new(struct bt_bap *bap, struct bt_bap_pac *lpac, struct bt_bap_qos *pqos, struct iovec *data) { struct bt_bap_stream *stream = NULL; struct bt_bap_endpoint *ep = NULL; struct match_pac match; if (!bap) return NULL; if (lpac->type == BT_BAP_BCAST_SOURCE) { match.lpac = lpac; match.rpac = NULL; memset(&match.codec, 0, sizeof(match.codec)); bt_bap_foreach_pac(bap, BT_BAP_BCAST_SINK, match_pac, &match); if ((!match.lpac) || (!lpac)) return NULL; lpac = match.lpac; ep = queue_find(bap->remote_eps, find_ep_source, NULL); if (!ep) return NULL; } if (!stream) stream = bap_stream_new(bap, ep, lpac, NULL, data, true); return stream; } static struct bt_bap_stream *bap_ucast_stream_new(struct bt_bap *bap, struct bt_bap_pac *lpac, struct bt_bap_pac *rpac, struct bt_bap_qos *pqos, struct iovec *data) { struct bt_bap_stream *stream = NULL; struct bt_bap_endpoint *ep = NULL; struct match_pac match; if (!lpac || !rpac || !bap_codec_equal(&lpac->codec, &rpac->codec)) return NULL; memset(&match, 0, sizeof(match)); match.lpac = lpac; match.rpac = rpac; /* Check for existing stream */ ep = queue_find(bap->remote_eps, find_ep_pacs, &match); if (!ep) { /* Check for unused ASE */ ep = queue_find(bap->remote_eps, find_ep_unused, &match); if (!ep) { DBG(bap, "Unable to find unused ASE"); return NULL; } } stream = ep->stream; if (!stream) stream = bap_stream_new(bap, ep, lpac, rpac, data, true); return stream; } struct bt_bap_stream *bt_bap_stream_new(struct bt_bap *bap, struct bt_bap_pac *lpac, struct bt_bap_pac *rpac, struct bt_bap_qos *pqos, struct iovec *data) { if (!bap) return NULL; /* Check if ATT is attached then it must be a unicast stream */ if (bt_bap_get_att(bap)) return bap_ucast_stream_new(bap, lpac, rpac, pqos, data); return bap_bcast_stream_new(bap, lpac, pqos, data); } struct bt_bap *bt_bap_stream_get_session(struct bt_bap_stream *stream) { if (!stream) return NULL; return stream->bap; } uint8_t bt_bap_stream_get_state(struct bt_bap_stream *stream) { if (!stream) return BT_BAP_STREAM_STATE_IDLE; return stream->ops->get_state(stream); } bool bt_bap_stream_set_user_data(struct bt_bap_stream *stream, void *user_data) { if (!stream) return false; stream->user_data = user_data; return true; } void *bt_bap_stream_get_user_data(struct bt_bap_stream *stream) { if (!stream) return NULL; return stream->user_data; } unsigned int bt_bap_stream_qos(struct bt_bap_stream *stream, struct bt_bap_qos *data, bt_bap_stream_func_t func, void *user_data) { unsigned int id; if (!bap_stream_valid(stream)) return 0; if (!stream->ops || !stream->ops->qos) return 0; if (!bt_bap_ref_safe(stream->bap)) return 0; id = stream->ops->qos(stream, data, func, user_data); bt_bap_unref(stream->bap); return id; } unsigned int bt_bap_stream_enable(struct bt_bap_stream *stream, bool enable_links, struct iovec *metadata, bt_bap_stream_func_t func, void *user_data) { unsigned int id; struct bt_bap *bap; if (!bap_stream_valid(stream)) return 0; if (!stream->ops || !stream->ops->enable) return 0; if (!bt_bap_ref_safe(stream->bap)) return 0; bap = stream->bap; id = stream->ops->enable(stream, enable_links, metadata, func, user_data); bt_bap_unref(bap); return id; } unsigned int bt_bap_stream_start(struct bt_bap_stream *stream, bt_bap_stream_func_t func, void *user_data) { unsigned int id; struct bt_bap *bap; if (!bap_stream_valid(stream)) return 0; if (!stream->ops || !stream->ops->start) return 0; if (!bt_bap_ref_safe(stream->bap)) return 0; bap = stream->bap; id = stream->ops->start(stream, func, user_data); bt_bap_unref(bap); return id; } unsigned int bt_bap_stream_disable(struct bt_bap_stream *stream, bool disable_links, bt_bap_stream_func_t func, void *user_data) { unsigned int id; struct bt_bap *bap; if (!bap_stream_valid(stream)) return 0; if (!stream->ops || !stream->ops->disable) return 0; if (!bt_bap_ref_safe(stream->bap)) return 0; bap = stream->bap; id = stream->ops->disable(stream, disable_links, func, user_data); bt_bap_unref(bap); return id; } unsigned int bt_bap_stream_stop(struct bt_bap_stream *stream, bt_bap_stream_func_t func, void *user_data) { unsigned int id; if (!bap_stream_valid(stream)) return 0; if (!stream->ops || !stream->ops->stop) return 0; if (!bt_bap_ref_safe(stream->bap)) return 0; id = stream->ops->stop(stream, func, user_data); bt_bap_unref(stream->bap); return id; } unsigned int bt_bap_stream_metadata(struct bt_bap_stream *stream, struct iovec *metadata, bt_bap_stream_func_t func, void *user_data) { unsigned int id; if (!bap_stream_valid(stream)) return 0; if (!stream->ops || !stream->ops->metadata) return 0; if (!bt_bap_ref_safe(stream->bap)) return 0; id = stream->ops->metadata(stream, metadata, func, user_data); bt_bap_unref(stream->bap); return id; } unsigned int bt_bap_stream_release(struct bt_bap_stream *stream, bt_bap_stream_func_t func, void *user_data) { unsigned int id; struct bt_bap *bap = stream->bap; if (!stream || !stream->ops || !stream->ops->release) return 0; if (!bt_bap_ref_safe(bap)) return 0; id = stream->ops->release(stream, func, user_data); bt_bap_unref(bap); return id; } uint8_t bt_bap_stream_get_dir(struct bt_bap_stream *stream) { if (!stream) return 0x00; return stream->ops->get_dir(stream); } uint32_t bt_bap_stream_get_location(struct bt_bap_stream *stream) { if (!stream) return 0x00000000; return stream->ops->get_loc(stream); } struct iovec *bt_bap_stream_get_config(struct bt_bap_stream *stream) { if (!stream) return NULL; return stream->cc; } struct bt_bap_qos *bt_bap_stream_get_qos(struct bt_bap_stream *stream) { if (!stream) return NULL; return &stream->qos; } struct iovec *bt_bap_stream_get_metadata(struct bt_bap_stream *stream) { if (!stream) return NULL; return stream->meta; } struct io *bt_bap_stream_get_io(struct bt_bap_stream *stream) { struct bt_bap_stream_io *io; io = stream_get_io(stream); if (!io || io->connecting) return NULL; return io->io; } bool bt_bap_match_bcast_sink_stream(const void *data, const void *user_data) { const struct bt_bap_stream *stream = data; return stream->lpac->type == BT_BAP_BCAST_SINK; } static bool stream_io_disconnected(struct io *io, void *user_data) { struct bt_bap_stream *stream = user_data; DBG(stream->bap, "stream %p io disconnected", stream); if (stream->ep->state == BT_ASCS_ASE_STATE_RELEASING) stream_set_state(stream, BT_BAP_STREAM_STATE_CONFIG); bt_bap_stream_set_io(stream, -1); return false; } bool bt_bap_stream_set_io(struct bt_bap_stream *stream, int fd) { bool ret; struct bt_bap *bap; if (!bap_stream_valid(stream)) return false; if (!stream->ops || !stream->ops->set_io) return false; if (!bt_bap_ref_safe(stream->bap)) return false; bap = stream->bap; ret = stream->ops->set_io(stream, fd); bt_bap_unref(bap); return ret; } static bool match_req_id(const void *data, const void *match_data) { const struct bt_bap_req *req = data; unsigned int id = PTR_TO_UINT(match_data); return (req->id == id); } static bool match_name(const void *data, const void *match_data) { const struct bt_bap_pac *pac = data; const char *name = match_data; return (!strcmp(pac->name, name)); } int bt_bap_stream_cancel(struct bt_bap_stream *stream, unsigned int id) { struct bt_bap_req *req; if (!stream) return -EINVAL; if (stream->bap->req && stream->bap->req->id == id) { req = stream->bap->req; stream->bap->req = NULL; bap_req_free(req); return 0; } req = queue_remove_if(stream->bap->reqs, match_req_id, UINT_TO_PTR(id)); if (!req) return 0; bap_req_free(req); return 0; } int bt_bap_stream_io_link(struct bt_bap_stream *stream, struct bt_bap_stream *link) { int ret; struct bt_bap *bap; if (!bap_stream_valid(stream)) return -EINVAL; if (!stream->ops || !stream->ops->io_link) return -EINVAL; if (!bt_bap_ref_safe(stream->bap)) return -EINVAL; bap = stream->bap; ret = stream->ops->io_link(stream, link); bt_bap_unref(bap); return ret; } int bt_bap_stream_io_unlink(struct bt_bap_stream *stream, struct bt_bap_stream *link) { int ret; struct bt_bap *bap; if (!bap_stream_valid(stream)) return -EINVAL; if (!stream->ops || !stream->ops->io_unlink) return -EINVAL; if (!bt_bap_ref_safe(stream->bap)) return -EINVAL; bap = stream->bap; ret = stream->ops->io_unlink(stream, link); bt_bap_unref(bap); return ret; } struct queue *bt_bap_stream_io_get_links(struct bt_bap_stream *stream) { if (!stream) return NULL; return stream->links; } static void bap_stream_get_in_qos(void *data, void *user_data) { struct bt_bap_stream *stream = data; struct bt_bap_qos **qos = user_data; if (!stream) return; if (!qos || *qos || stream->ep->dir != BT_BAP_SOURCE || !stream->qos.ucast.io_qos.sdu) return; *qos = &stream->qos; } static void bap_stream_get_out_qos(void *data, void *user_data) { struct bt_bap_stream *stream = data; struct bt_bap_qos **qos = user_data; if (!stream) return; if (!qos || *qos || stream->ep->dir != BT_BAP_SINK || !stream->qos.ucast.io_qos.sdu) return; *qos = &stream->qos; } bool bt_bap_stream_io_get_qos(struct bt_bap_stream *stream, struct bt_bap_qos **in, struct bt_bap_qos **out) { if (!stream || (!in && !out)) return false; switch (stream->ep->dir) { case BT_BAP_SOURCE: bap_stream_get_in_qos(stream, in); queue_foreach(stream->links, bap_stream_get_out_qos, out); break; case BT_BAP_SINK: bap_stream_get_out_qos(stream, out); queue_foreach(stream->links, bap_stream_get_in_qos, in); break; default: return false; } DBG(stream->bap, "in %p out %p", in ? *in : NULL, out ? *out : NULL); return in && out; } static void bap_stream_get_dir(void *data, void *user_data) { struct bt_bap_stream *stream = data; uint8_t *dir = user_data; *dir |= stream->ep->dir; } uint8_t bt_bap_stream_io_dir(struct bt_bap_stream *stream) { uint8_t dir; struct bt_bap *bap; if (!bap_stream_valid(stream)) return 0; if (!stream->ops || !stream->ops->set_io) return 0; if (!bt_bap_ref_safe(stream->bap)) return 00; bap = stream->bap; dir = stream->ops->io_dir(stream); bt_bap_unref(bap); return dir; } static void bap_stream_io_connecting(void *data, void *user_data) { struct bt_bap_stream *stream = data; int fd = PTR_TO_INT(user_data); const struct queue_entry *entry; if (!stream) return; if (fd >= 0) bap_stream_io_attach(stream, fd, true); else bap_stream_io_detach(stream); for (entry = queue_get_entries(stream->bap->state_cbs); entry; entry = entry->next) { struct bt_bap_state *state = entry->data; if (state->connecting) state->connecting(stream, stream->io ? true : false, fd, state->data); } } int bt_bap_stream_io_connecting(struct bt_bap_stream *stream, int fd) { if (!stream) return -EINVAL; bap_stream_io_connecting(stream, INT_TO_PTR(fd)); queue_foreach(stream->links, bap_stream_io_connecting, INT_TO_PTR(fd)); return 0; } bool bt_bap_stream_io_is_connecting(struct bt_bap_stream *stream, int *fd) { struct bt_bap_stream_io *io; if (!stream) return false; io = stream_get_io(stream); if (!io) return false; if (fd) *fd = stream_io_get_fd(io); return io->connecting; } bool bt_bap_new_bcast_source(struct bt_bap *bap, const char *name) { struct bt_bap_endpoint *ep; struct bt_bap_pac *pac_broadcast_source; /* Add the remote source only if a local sink endpoint was registered */ if (queue_isempty(bap->ldb->broadcast_sinks)) return false; /* Add remote source endpoint */ if (!bap->rdb->broadcast_sources) bap->rdb->broadcast_sources = queue_new(); if (queue_find(bap->rdb->broadcast_sources, match_name, name)) return true; pac_broadcast_source = bap_pac_new(bap->rdb, name, BT_BAP_BCAST_SOURCE, NULL, NULL, NULL, NULL); queue_push_tail(bap->rdb->broadcast_sources, pac_broadcast_source); if (!pac_broadcast_source) return false; queue_foreach(bap->pac_cbs, notify_pac_added, pac_broadcast_source); /* Push remote endpoint with direction sink */ ep = bap_endpoint_new_broadcast(bap->rdb, BT_BAP_BCAST_SINK); if (ep) queue_push_tail(bap->remote_eps, ep); return true; } void bt_bap_update_bcast_source(struct bt_bap_pac *pac, struct bt_bap_codec *codec, struct iovec *data, struct iovec *metadata) { bap_pac_merge(pac, data, metadata); pac->codec = *codec; } static void destroy_base_bis(void *data) { struct bt_bis *bis = data; if (!bis) return; if (bis->caps) util_iov_free(bis->caps, 1); free(bis); } static void generate_bis_base(void *data, void *user_data) { struct bt_bis *bis = data; struct iovec *base_iov = user_data; uint8_t cc_length = bis->caps->iov_len; if (!util_iov_push_u8(base_iov, bis->index)) return; if (!util_iov_push_u8(base_iov, cc_length)) return; if (cc_length) util_iov_push_mem(base_iov, bis->caps->iov_len, bis->caps->iov_base); } static void generate_subgroup_base(void *data, void *user_data) { struct bt_subgroup *sgrp = data; struct iovec *base_iov = user_data; if (!util_iov_push_u8(base_iov, queue_length(sgrp->bises))) return; if (!util_iov_push_u8(base_iov, sgrp->codec.id)) return; if (!util_iov_push_le16(base_iov, sgrp->codec.cid)) return; if (!util_iov_push_le16(base_iov, sgrp->codec.vid)) return; if (sgrp->caps) { if (!util_iov_push_u8(base_iov, sgrp->caps->iov_len)) return; if (sgrp->caps->iov_len) util_iov_push_mem(base_iov, sgrp->caps->iov_len, sgrp->caps->iov_base); } else if (!util_iov_push_u8(base_iov, 0)) return; if (sgrp->meta) { if (!util_iov_push_u8(base_iov, sgrp->meta->iov_len)) return; if (sgrp->meta->iov_len) util_iov_push_mem(base_iov, sgrp->meta->iov_len, sgrp->meta->iov_base); } else if (!util_iov_push_u8(base_iov, 0)) return; queue_foreach(sgrp->bises, generate_bis_base, base_iov); } static struct iovec *generate_base(struct bt_base *base) { struct iovec *base_iov = new0(struct iovec, 0x1); base_iov->iov_base = util_malloc(BASE_MAX_LENGTH); if (!util_iov_push_le24(base_iov, base->pres_delay)) { free(base_iov->iov_base); free(base_iov); return NULL; } if (!util_iov_push_u8(base_iov, queue_length(base->subgroups))) { free(base_iov->iov_base); free(base_iov); return NULL; } queue_foreach(base->subgroups, generate_subgroup_base, base_iov); return base_iov; } static void add_new_bis(struct bt_subgroup *subgroup, uint8_t bis_index, struct iovec *caps) { struct bt_bis *bis = new0(struct bt_bis, 1); bis->index = bis_index; if (caps) bis->caps = caps; else bis->caps = new0(struct iovec, 1); queue_push_tail(subgroup->bises, bis); } static void add_new_subgroup(struct bt_base *base, struct bt_bap_stream *stream) { struct bt_bap_pac *lpac = stream->lpac; struct bt_subgroup *sgrp = new0( struct bt_subgroup, 1); uint16_t cid = 0; uint16_t vid = 0; bt_bap_pac_get_vendor_codec(lpac, &sgrp->codec.id, &cid, &vid, NULL, NULL); sgrp->codec.cid = cid; sgrp->codec.vid = vid; sgrp->caps = util_iov_dup(stream->cc, 1); sgrp->meta = util_iov_dup(stream->meta, 1); sgrp->bises = queue_new(); stream->qos.bcast.bis = base->next_bis_index++; add_new_bis(sgrp, stream->qos.bcast.bis, NULL); queue_push_tail(base->subgroups, sgrp); } struct bt_ltv_match { uint8_t l; void *data; bool found; uint32_t data32; }; struct bt_ltv_search { struct iovec *iov; bool found; }; static void match_ltv(size_t i, uint8_t l, uint8_t t, uint8_t *v, void *user_data) { struct bt_ltv_match *ltv_match = user_data; if (ltv_match->found == true) return; if (ltv_match->l != l) return; if (!memcmp(v, ltv_match->data, l)) ltv_match->found = true; } static void search_ltv(size_t i, uint8_t l, uint8_t t, uint8_t *v, void *user_data) { struct bt_ltv_search *ltv_search = user_data; struct bt_ltv_match ltv_match; ltv_match.found = false; ltv_match.l = l; ltv_match.data = v; util_ltv_foreach(ltv_search->iov->iov_base, ltv_search->iov->iov_len, &t, match_ltv, <v_match); /* Once "found" has been updated to "false", * do not overwrite it anymore. * It means that an ltv was not found in the search list, * and this should be detected back in the parent function. */ if (ltv_search->found) ltv_search->found = ltv_match.found; } static bool compare_ltv(struct iovec *iov1, struct iovec *iov2) { struct bt_ltv_search ltv_search; if ((!iov1) && (!iov2)) return true; if ((!iov1) || (!iov2)) return false; /* Compare metadata length */ if (iov1->iov_len != iov2->iov_len) return false; ltv_search.found = true; ltv_search.iov = iov2; util_ltv_foreach(iov1->iov_base, iov1->iov_len, NULL, search_ltv, <v_search); return ltv_search.found; } struct bt_ltv_extract { struct iovec *src; void *value; uint8_t len; struct iovec *result; }; static void extract_ltv(size_t i, uint8_t l, uint8_t t, uint8_t *v, void *user_data) { struct bt_ltv_extract *ext_data = user_data; struct bt_ltv_match ltv_match; uint8_t ltv_len = 0; ltv_match.found = false; ltv_match.l = l; ltv_match.data = v; /* Search each BIS caps ltv in subgroup caps * to extract the one that are BIS specific */ util_ltv_foreach(ext_data->src->iov_base, ext_data->src->iov_len, &t, match_ltv, <v_match); if (!ltv_match.found) { ltv_len = l + 1; util_iov_append(ext_data->result, <v_len, 1); util_iov_append(ext_data->result, &t, 1); util_iov_append(ext_data->result, v, l); } } static struct iovec *extract_diff_caps( struct iovec *subgroup_caps, struct iovec *bis_caps) { struct bt_ltv_extract ext_data; ext_data.src = subgroup_caps; ext_data.result = new0(struct iovec, 1); util_ltv_foreach(bis_caps->iov_base, bis_caps->iov_len, NULL, extract_ltv, &ext_data); return ext_data.result; } static void set_base_subgroup(void *data, void *user_data) { struct bt_bap_stream *stream = data; struct bt_base *base = user_data; /* BIS specific codec capabilities */ struct iovec *bis_caps; if (bt_bap_pac_get_type(stream->lpac) != BT_BAP_BCAST_SOURCE) return; if (stream->qos.bcast.big != base->big_id) return; if (base->pres_delay < stream->qos.bcast.delay) base->pres_delay = stream->qos.bcast.delay; if (queue_isempty(base->subgroups)) { add_new_subgroup(base, stream); } else { /* Verify if a subgroup has the same metadata */ const struct queue_entry *entry; struct bt_subgroup *subgroup = NULL; bool same_meta = false; for (entry = queue_get_entries(base->subgroups); entry; entry = entry->next) { subgroup = entry->data; same_meta = compare_ltv(subgroup->meta, stream->meta); if (same_meta) break; } if (!same_meta) { /* No subgroup with the same metadata found. * Create a new one. */ add_new_subgroup(base, stream); } else { /* Subgroup found with the same metadata. * Extract different codec capabilities. */ bis_caps = extract_diff_caps( subgroup->caps, stream->cc); stream->qos.bcast.bis = base->next_bis_index++; add_new_bis(subgroup, stream->qos.bcast.bis, bis_caps); } } } static void destroy_base_subgroup(void *data) { struct bt_subgroup *subgroup = data; if (!subgroup) return; if (subgroup->caps) util_iov_free(subgroup->caps, 1); if (subgroup->meta) util_iov_free(subgroup->meta, 1); queue_destroy(subgroup->bises, destroy_base_bis); free(subgroup); } /* * Function to update the BASE using configuration data * from each BIS belonging to the same BIG */ struct iovec *bt_bap_stream_get_base(struct bt_bap_stream *stream) { struct bt_base base; struct iovec *base_iov; base.subgroups = queue_new(); base.next_bis_index = 1; base.big_id = stream->qos.bcast.big; base.pres_delay = stream->qos.bcast.delay; /* If the BIG ID was explicitly set, create a BASE with information * from all streams belonging to this BIG. Otherwise, create a BASE * with only this BIS. */ if (stream->qos.bcast.big != 0xFF) queue_foreach(stream->bap->streams, set_base_subgroup, &base); else { base.pres_delay = stream->qos.bcast.delay; set_base_subgroup(stream, &base); } base_iov = generate_base(&base); queue_destroy(base.subgroups, destroy_base_subgroup); return base_iov; } /* * This function compares PAC Codec Specific Capabilities, with the Codec * Specific Configuration LTVs received in the BASE of the BAP Source. The * result is accumulated in data32 which is a bitmask of types. */ static void check_pac_caps_ltv(size_t i, uint8_t l, uint8_t t, uint8_t *v, void *user_data) { struct bt_ltv_match *compare_data = user_data; uint8_t *bis_v = compare_data->data; uint16_t mask; uint16_t min; uint16_t max; uint16_t frame_len; switch (t) { case BAP_FREQ_LTV_TYPE: mask = get_le16(v); if (mask & (1 << (bis_v[0] - 1))) compare_data->data32 |= 1<data32 |= 1<= min) && (frame_len <= max)) compare_data->data32 |= 1<data; struct bt_ltv_match compare_data; compare_data.data = v; /* Search inside local PAC's caps for LTV of type t */ util_ltv_foreach(pac_caps->iov_base, pac_caps->iov_len, &t, check_pac_caps_ltv, &compare_data); local_data->data32 |= compare_data.data32; } static void bap_sink_check_level3_ltv(size_t i, uint8_t l, uint8_t t, uint8_t *v, void *user_data) { struct bt_ltv_extract *merge_data = user_data; merge_data->value = v; merge_data->len = l; } static void bap_sink_check_level2_ltv(size_t i, uint8_t l, uint8_t t, uint8_t *v, void *user_data) { struct bt_ltv_extract *merge_data = user_data; merge_data->value = NULL; util_ltv_foreach(merge_data->src->iov_base, merge_data->src->iov_len, &t, bap_sink_check_level3_ltv, merge_data); /* If the LTV at level 2 was found at level 3 add the one from level 3, * otherwise add the one at level 2 */ if (merge_data->value) util_ltv_push(merge_data->result, merge_data->len, t, merge_data->value); else util_ltv_push(merge_data->result, l, t, v); } static void bap_sink_append_level3_ltv(size_t i, uint8_t l, uint8_t t, uint8_t *v, void *user_data) { struct bt_ltv_extract *merge_data = user_data; merge_data->value = NULL; util_ltv_foreach(merge_data->result->iov_base, merge_data->result->iov_len, &t, bap_sink_check_level3_ltv, merge_data); /* If the LTV at level 3 was not found in merged configuration, * append value */ if (!merge_data->value) util_ltv_push(merge_data->result, l, t, v); } static void check_local_pac(void *data, void *user_data) { struct bt_ltv_match *compare_data = user_data; struct iovec *bis_data = (struct iovec *)compare_data->data; const struct bt_bap_pac *pac = data; /* Keep searching for a matching PAC if one wasn't found * in previous PAC element */ if (compare_data->found == false) { struct bt_ltv_match bis_compare_data = { .data = pac->data, .data32 = 0, /* LTVs bitmask result */ .found = false }; /* loop each BIS LTV */ util_ltv_foreach(bis_data->iov_base, bis_data->iov_len, NULL, check_source_ltv, &bis_compare_data); /* We have a match if all selected LTVs have a match */ if ((bis_compare_data.data32 & CODEC_SPECIFIC_CONFIGURATION_MASK) == CODEC_SPECIFIC_CONFIGURATION_MASK) { compare_data->found = true; compare_data->data = data; } } } static void bap_sink_match_allocation(size_t i, uint8_t l, uint8_t t, uint8_t *v, void *user_data) { struct bt_ltv_match *data = user_data; uint32_t location32; if (!v) return; memcpy(&location32, v, l); location32 = le32_to_cpu(location32); /* If all the bits in the received bitmask are found in * the local bitmask then we have a match */ if ((location32 & data->data32) == location32) data->found = true; else data->found = false; } static struct bt_ltv_match bap_check_bis(uint32_t sink_loc, struct queue *pacs, struct iovec *bis_data) { struct bt_ltv_match compare_data = {}; /* Check channel allocation against the PACS location. * If we don't have a location set we can accept any BIS location. * If the BIS doesn't have a location set we also accept it */ compare_data.found = true; if (sink_loc) { uint8_t type = BAP_CHANNEL_ALLOCATION_LTV_TYPE; compare_data.data32 = sink_loc; util_ltv_foreach(bis_data->iov_base, bis_data->iov_len, &type, bap_sink_match_allocation, &compare_data); } /* Check remaining LTVs against the PACs list */ if (compare_data.found) { compare_data.data = bis_data; compare_data.found = false; queue_foreach(pacs, check_local_pac, &compare_data); } return compare_data; } struct iovec *bt_bap_merge_caps(struct iovec *l2_caps, struct iovec *l3_caps) { struct bt_ltv_extract merge_data = {0}; if (!l2_caps) /* Codec_Specific_Configuration parameters shall * be present at Level 2. */ return NULL; if (!l3_caps) /* Codec_Specific_Configuration parameters may * be present at Level 3. */ return util_iov_dup(l2_caps, 1); merge_data.src = l3_caps; merge_data.result = new0(struct iovec, 1); /* Create a Codec Specific Configuration with LTVs at level 2 (subgroup) * overwritten by LTVs at level 3 (BIS) */ util_ltv_foreach(l2_caps->iov_base, l2_caps->iov_len, NULL, bap_sink_check_level2_ltv, &merge_data); /* Append LTVs at level 3 (BIS) that were not found at * level 2 (subgroup) */ util_ltv_foreach(l3_caps->iov_base, l3_caps->iov_len, NULL, bap_sink_append_level3_ltv, &merge_data); return merge_data.result; } void bt_bap_verify_bis(struct bt_bap *bap, uint8_t bis_index, struct iovec *caps, struct bt_bap_pac **lpac) { struct bt_ltv_match match_data; uint32_t sink_loc; struct queue *pacs; if (!caps) return; /* If the bap session corresponds to a client connection with * a BAP Server, bis caps should be checked against peer caps. * If the bap session corresponds to a scanned broadcast source, * bis caps should be checked against local broadcast sink caps. */ if (bap->client) { sink_loc = bap->rdb->pacs->sink_loc_value; pacs = bap->rdb->sinks; } else { sink_loc = bap->ldb->pacs->sink_loc_value; pacs = bap->ldb->broadcast_sinks; } /* Check each BIS Codec Specific Configuration LTVs against our Codec * Specific Capabilities and if the BIS matches create a PAC with it */ match_data = bap_check_bis(sink_loc, pacs, caps); if (match_data.found == true) { *lpac = match_data.data; DBG(bap, "Matching BIS %i", bis_index); } else { *lpac = NULL; } } bool bt_bap_parse_base(struct iovec *iov, struct bt_bap_qos *qos, util_debug_func_t func, bt_bap_bis_func_t handler, void *user_data) { uint32_t delay; uint8_t sgrps; bool ret = true; util_debug(func, NULL, "BASE len: %zd", iov->iov_len); if (!util_iov_pull_le24(iov, &delay)) return false; util_debug(func, NULL, "PresentationDelay: %d", delay); if (!util_iov_pull_u8(iov, &sgrps)) return false; util_debug(func, NULL, "Number of Subgroups: %d", sgrps); /* Loop subgroups */ for (int idx = 0; idx < sgrps; idx++) { uint8_t num_bis; struct bt_bap_codec *codec; struct iovec l2_cc; uint8_t l2_cc_len; struct iovec meta; uint8_t meta_len; util_debug(func, NULL, "Subgroup #%d", idx); if (!util_iov_pull_u8(iov, &num_bis)) { ret = false; goto done; } util_debug(func, NULL, "Number of BISes: %d", num_bis); codec = util_iov_pull_mem(iov, sizeof(*codec)); util_debug(func, NULL, "Codec: ID %d CID 0x%2.2x VID 0x%2.2x", codec->id, codec->cid, codec->vid); /* Level 2 */ /* Read Codec Specific Configuration */ if (!util_iov_pull_u8(iov, &l2_cc_len)) { ret = false; goto done; } l2_cc.iov_base = util_iov_pull_mem(iov, l2_cc_len); l2_cc.iov_len = l2_cc_len; /* Print Codec Specific Configuration */ util_debug(func, NULL, "CC len: %zd", l2_cc.iov_len); bt_bap_debug_config(l2_cc.iov_base, l2_cc.iov_len, func, NULL); /* Read Metadata */ if (!util_iov_pull_u8(iov, &meta_len)) { ret = false; goto done; } meta.iov_base = util_iov_pull_mem(iov, meta_len); meta.iov_len = meta_len; /* Print Metadata */ util_debug(func, NULL, "Metadata len: %i", (uint8_t)meta.iov_len); bt_bap_debug_metadata(meta.iov_base, meta.iov_len, func, NULL); /* Level 3 */ for (; num_bis; num_bis--) { uint8_t bis_index; struct iovec l3_cc; uint8_t l3_cc_len; struct iovec *bis_cc; if (!util_iov_pull_u8(iov, &bis_index)) { ret = false; goto done; } util_debug(func, NULL, "BIS #%d", bis_index); /* Read Codec Specific Configuration */ if (!util_iov_pull_u8(iov, &l3_cc_len)) { ret = false; goto done; } l3_cc.iov_base = util_iov_pull_mem(iov, l3_cc_len); l3_cc.iov_len = l3_cc_len; /* Print Codec Specific Configuration */ util_debug(func, NULL, "CC Len: %d", (uint8_t)l3_cc.iov_len); bt_bap_debug_config(l3_cc.iov_base, l3_cc.iov_len, func, NULL); bis_cc = bt_bap_merge_caps(&l2_cc, &l3_cc); if (!bis_cc) continue; handler(bis_index, idx, bis_cc, &meta, qos, user_data); util_iov_free(bis_cc, 1); } } done: if (!ret) util_debug(func, NULL, "Unable to parse Base"); return ret; } void bt_bap_req_bcode(struct bt_bap_stream *stream, bt_bap_bcode_reply_t reply, void *reply_data) { const struct queue_entry *entry; if (!bap_stream_valid(stream)) return; bt_bap_stream_ref(stream); if (!bt_bap_ref_safe(stream->bap)) goto done; entry = queue_get_entries(stream->bap->bcode_cbs); while (entry) { struct bt_bap_bcode_cb *cb = entry->data; entry = entry->next; if (cb->func) cb->func(stream, reply, reply_data, cb->data); } bt_bap_unref(stream->bap); done: bt_bap_stream_unref(stream); } unsigned int bt_bap_bcode_cb_register(struct bt_bap *bap, bt_bap_bcode_func_t func, void *user_data, bt_bap_destroy_func_t destroy) { struct bt_bap_bcode_cb *cb; static unsigned int id; if (!bap) return 0; cb = new0(struct bt_bap_bcode_cb, 1); cb->id = ++id ? id : ++id; cb->func = func; cb->destroy = destroy; cb->data = user_data; queue_push_tail(bap->bcode_cbs, cb); return cb->id; } static bool match_bcode_cb_id(const void *data, const void *match_data) { const struct bt_bap_bcode_cb *cb = data; unsigned int id = PTR_TO_UINT(match_data); return (cb->id == id); } bool bt_bap_bcode_cb_unregister(struct bt_bap *bap, unsigned int id) { struct bt_bap_bcode_cb *cb; if (!bap) return false; cb = queue_remove_if(bap->bcode_cbs, match_bcode_cb_id, UINT_TO_PTR(id)); if (!cb) return false; bap_bcode_cb_free(cb); return false; } void bt_bap_iso_qos_to_bap_qos(struct bt_iso_qos *iso_qos, struct bt_bap_qos *bap_qos) { bap_qos->bcast.big = iso_qos->bcast.big; bap_qos->bcast.bis = iso_qos->bcast.bis; bap_qos->bcast.sync_factor = iso_qos->bcast.sync_factor; bap_qos->bcast.packing = iso_qos->bcast.packing; bap_qos->bcast.framing = iso_qos->bcast.framing; bap_qos->bcast.encryption = iso_qos->bcast.encryption; if (bap_qos->bcast.encryption) bap_qos->bcast.bcode = util_iov_new(iso_qos->bcast.bcode, sizeof(iso_qos->bcast.bcode)); bap_qos->bcast.options = iso_qos->bcast.options; bap_qos->bcast.skip = iso_qos->bcast.skip; bap_qos->bcast.sync_timeout = iso_qos->bcast.sync_timeout; bap_qos->bcast.sync_cte_type = iso_qos->bcast.sync_cte_type; bap_qos->bcast.mse = iso_qos->bcast.mse; bap_qos->bcast.timeout = iso_qos->bcast.timeout; bap_qos->bcast.io_qos.interval = iso_qos->bcast.in.interval; bap_qos->bcast.io_qos.latency = iso_qos->bcast.in.latency; bap_qos->bcast.io_qos.phy = iso_qos->bcast.in.phy; bap_qos->bcast.io_qos.rtn = iso_qos->bcast.in.rtn; bap_qos->bcast.io_qos.sdu = iso_qos->bcast.in.sdu; } void bt_bap_qos_to_iso_qos(struct bt_bap_qos *bap_qos, struct bt_iso_qos *iso_qos) { memset(iso_qos, 0, sizeof(*iso_qos)); iso_qos->bcast.big = bap_qos->bcast.big; iso_qos->bcast.bis = bap_qos->bcast.bis; iso_qos->bcast.sync_factor = bap_qos->bcast.sync_factor; iso_qos->bcast.packing = bap_qos->bcast.packing; iso_qos->bcast.framing = bap_qos->bcast.framing; iso_qos->bcast.encryption = bap_qos->bcast.encryption; if (bap_qos->bcast.bcode && bap_qos->bcast.bcode->iov_base) memcpy(iso_qos->bcast.bcode, bap_qos->bcast.bcode->iov_base, bap_qos->bcast.bcode->iov_len); iso_qos->bcast.options = bap_qos->bcast.options; iso_qos->bcast.skip = bap_qos->bcast.skip; iso_qos->bcast.sync_timeout = bap_qos->bcast.sync_timeout; iso_qos->bcast.sync_cte_type = bap_qos->bcast.sync_cte_type; iso_qos->bcast.mse = bap_qos->bcast.mse; iso_qos->bcast.timeout = bap_qos->bcast.timeout; memcpy(&iso_qos->bcast.out, &bap_qos->bcast.io_qos, sizeof(struct bt_iso_io_qos)); } bluez-5.82/src/shared/PaxHeaders/ad.c0000644000000000000000000000005014766002272014441 xustar0020 atime=1743515578 20 ctime=1743591277 bluez-5.82/src/shared/ad.c0000644000000000000000000007033314766002272014130 0ustar00rootroot// SPDX-License-Identifier: LGPL-2.1-or-later /* * * BlueZ - Bluetooth protocol stack for Linux * * Copyright (C) 2015 Google Inc. * * */ #ifdef HAVE_CONFIG_H #include #endif #define _GNU_SOURCE #include #include "lib/bluetooth.h" #include "lib/hci.h" #include "src/shared/ad.h" #include "src/eir.h" #include "src/shared/queue.h" #include "src/shared/util.h" struct bt_ad { int ref_count; uint8_t max_len; char *name; uint16_t appearance; struct queue *service_uuids; struct queue *manufacturer_data; struct queue *solicit_uuids; struct queue *service_data; struct queue *data; }; struct pattern_match_info { struct bt_ad *ad; struct bt_ad_pattern *current_pattern; struct bt_ad_pattern *matched_pattern; }; struct bt_ad *bt_ad_new(void) { struct bt_ad *ad; ad = new0(struct bt_ad, 1); ad->max_len = BT_EA_MAX_DATA_LEN; ad->service_uuids = queue_new(); ad->manufacturer_data = queue_new(); ad->solicit_uuids = queue_new(); ad->service_data = queue_new(); ad->data = queue_new(); ad->appearance = UINT16_MAX; return bt_ad_ref(ad); } bool bt_ad_set_max_len(struct bt_ad *ad, uint8_t len) { if (!ad || len < BT_AD_MAX_DATA_LEN) return false; ad->max_len = len; return true; } static bool ad_replace_data(struct bt_ad *ad, uint8_t type, const void *data, size_t len); static bool ad_is_type_valid(uint8_t type) { if (type > BT_AD_3D_INFO_DATA && type != BT_AD_MANUFACTURER_DATA) return false; if (type < BT_AD_FLAGS) return false; return true; } struct bt_ad *bt_ad_new_with_data(size_t len, const uint8_t *data) { struct bt_ad *ad; struct iovec iov = { .iov_base = (void *)data, .iov_len = len, }; uint8_t elen; if (data == NULL || !len) return NULL; ad = bt_ad_new(); if (!ad) return NULL; bt_ad_set_max_len(ad, len); while (util_iov_pull_u8(&iov, &elen)) { uint8_t type; void *data; if (elen == 0 || elen > iov.iov_len) break; if (!util_iov_pull_u8(&iov, &type)) goto failed; elen--; if (!ad_is_type_valid(type)) goto failed; data = util_iov_pull_mem(&iov, elen); if (!data) goto failed; if (!ad_replace_data(ad, type, data, elen)) goto failed; } return ad; failed: bt_ad_unref(ad); return NULL; } struct bt_ad *bt_ad_ref(struct bt_ad *ad) { if (!ad) return NULL; ad->ref_count++; return ad; } static void uuid_destroy(void *data) { struct bt_ad_service_data *uuid_data = data; free(uuid_data->data); free(uuid_data); } static bool uuid_data_match(const void *data, const void *elem) { const struct bt_ad_service_data *uuid_data = elem; const bt_uuid_t *uuid = data; return !bt_uuid_cmp(&uuid_data->uuid, uuid); } static void manuf_destroy(void *data) { struct bt_ad_manufacturer_data *manuf = data; free(manuf->data); free(manuf); } static bool manuf_match(const void *data, const void *elem) { const struct bt_ad_manufacturer_data *manuf = elem; uint16_t manuf_id = PTR_TO_UINT(elem); return manuf->manufacturer_id == manuf_id; } static void data_destroy(void *data) { struct bt_ad_data *ad = data; free(ad->data); free(ad); } void bt_ad_unref(struct bt_ad *ad) { if (!ad) return; if (__sync_sub_and_fetch(&ad->ref_count, 1)) return; queue_destroy(ad->service_uuids, free); queue_destroy(ad->manufacturer_data, manuf_destroy); queue_destroy(ad->solicit_uuids, free); queue_destroy(ad->service_data, uuid_destroy); queue_destroy(ad->data, data_destroy); free(ad->name); free(ad); } static bool data_type_match(const void *data, const void *user_data) { const struct bt_ad_data *a = data; const uint8_t type = PTR_TO_UINT(user_data); return a->type == type; } static bool ad_replace_uuid16(struct bt_ad *ad, struct iovec *iov) { uint16_t value; while ((util_iov_pull_le16(iov, &value))) { bt_uuid_t uuid; if (bt_uuid16_create(&uuid, value)) return false; if (bt_ad_has_service_uuid(ad, &uuid)) continue; if (!bt_ad_add_service_uuid(ad, &uuid)) return false; } return true; } static bool ad_replace_uuid32(struct bt_ad *ad, struct iovec *iov) { uint32_t value; while ((util_iov_pull_le32(iov, &value))) { bt_uuid_t uuid; if (bt_uuid32_create(&uuid, value)) return false; if (bt_ad_has_service_uuid(ad, &uuid)) continue; if (!bt_ad_add_service_uuid(ad, &uuid)) return false; } return true; } static bool ad_replace_uuid128(struct bt_ad *ad, struct iovec *iov) { void *data; while ((data = util_iov_pull_mem(iov, 16))) { uint128_t value; bt_uuid_t uuid; bswap_128(data, &value); if (bt_uuid128_create(&uuid, value)) return false; if (bt_ad_has_service_uuid(ad, &uuid)) continue; if (!bt_ad_add_service_uuid(ad, &uuid)) return false; } return true; } static bool ad_replace_name(struct bt_ad *ad, struct iovec *iov) { char utf8_name[HCI_MAX_NAME_LENGTH + 2]; int i; memset(utf8_name, 0, sizeof(utf8_name)); strncpy(utf8_name, (const char *)iov->iov_base, iov->iov_len); if (strisutf8(utf8_name, iov->iov_len)) goto done; /* Assume ASCII, and replace all non-ASCII with spaces */ for (i = 0; utf8_name[i] != '\0'; i++) { if (!isascii(utf8_name[i])) utf8_name[i] = ' '; } /* Remove leading and trailing whitespace characters */ strstrip(utf8_name); done: return bt_ad_add_name(ad, utf8_name); } static bool ad_replace_uuid16_data(struct bt_ad *ad, struct iovec *iov) { uint16_t value; bt_uuid_t uuid; if (!util_iov_pull_le16(iov, &value)) return false; if (bt_uuid16_create(&uuid, value)) return false; return bt_ad_add_service_data(ad, &uuid, iov->iov_base, iov->iov_len); } static bool ad_replace_uuid32_data(struct bt_ad *ad, struct iovec *iov) { uint32_t value; bt_uuid_t uuid; if (!util_iov_pull_le32(iov, &value)) return false; if (bt_uuid32_create(&uuid, value)) return false; return bt_ad_add_service_data(ad, &uuid, iov->iov_base, iov->iov_len); } static bool ad_replace_uuid128_data(struct bt_ad *ad, struct iovec *iov) { void *data; uint128_t value; bt_uuid_t uuid; data = util_iov_pull_mem(iov, 16); if (!data) return false; bswap_128(data, &value); if (bt_uuid128_create(&uuid, value)) return false; return bt_ad_add_service_data(ad, &uuid, iov->iov_base, iov->iov_len); } static bool ad_replace_manufacturer_data(struct bt_ad *ad, struct iovec *iov) { uint16_t value; if (!util_iov_pull_le16(iov, &value)) return false; return bt_ad_add_manufacturer_data(ad, value, iov->iov_base, iov->iov_len); } static bool ad_replace_data(struct bt_ad *ad, uint8_t type, const void *data, size_t len) { struct bt_ad_data *new_data; struct iovec iov = { .iov_base = (void *)data, .iov_len = len, }; switch (type) { case BT_AD_UUID16_SOME: case BT_AD_UUID16_ALL: return ad_replace_uuid16(ad, &iov); case BT_AD_UUID32_SOME: case BT_AD_UUID32_ALL: return ad_replace_uuid32(ad, &iov); case BT_AD_UUID128_SOME: case BT_AD_UUID128_ALL: return ad_replace_uuid128(ad, &iov); case BT_AD_NAME_SHORT: case BT_AD_NAME_COMPLETE: return ad_replace_name(ad, &iov); case BT_AD_SERVICE_DATA16: return ad_replace_uuid16_data(ad, &iov); case BT_AD_SERVICE_DATA32: return ad_replace_uuid32_data(ad, &iov); case BT_AD_SERVICE_DATA128: return ad_replace_uuid128_data(ad, &iov); case BT_AD_MANUFACTURER_DATA: return ad_replace_manufacturer_data(ad, &iov); } new_data = queue_find(ad->data, data_type_match, UINT_TO_PTR(type)); if (new_data) { if (new_data->len == len && !memcmp(new_data->data, data, len)) return false; new_data->data = realloc(new_data->data, len); memcpy(new_data->data, data, len); new_data->len = len; return true; } new_data = new0(struct bt_ad_data, 1); new_data->type = type; new_data->data = util_memdup(data, len); if (!new_data->data) { free(new_data); return false; } new_data->len = len; if (queue_push_tail(ad->data, new_data)) return true; data_destroy(new_data); return false; } static size_t uuid_list_length(struct queue *uuid_queue) { bool uuid16_included = false; bool uuid32_included = false; bool uuid128_included = false; size_t length = 0; const struct queue_entry *entry; entry = queue_get_entries(uuid_queue); while (entry) { bt_uuid_t *uuid = entry->data; length += bt_uuid_len(uuid); if (uuid->type == BT_UUID16) uuid16_included = true; else if (uuid->type == BT_UUID32) uuid32_included = true; else uuid128_included = true; entry = entry->next; } if (uuid16_included) length += 2; if (uuid32_included) length += 2; if (uuid128_included) length += 2; return length; } static size_t mfg_data_length(struct queue *manuf_data) { size_t length = 0; const struct queue_entry *entry; entry = queue_get_entries(manuf_data); while (entry) { struct bt_ad_manufacturer_data *data = entry->data; length += 2 + sizeof(uint16_t) + data->len; entry = entry->next; } return length; } static size_t uuid_data_length(struct queue *uuid_data) { size_t length = 0; const struct queue_entry *entry; entry = queue_get_entries(uuid_data); while (entry) { struct bt_ad_service_data *data = entry->data; length += 2 + bt_uuid_len(&data->uuid) + data->len; entry = entry->next; } return length; } static size_t name_length(struct bt_ad *ad, size_t *pos) { size_t len; if (!ad->name) return 0; len = 2 + strlen(ad->name); if (len > ad->max_len - (*pos)) len = ad->max_len - (*pos); return len; } static size_t data_length(struct queue *queue) { size_t length = 0; const struct queue_entry *entry; entry = queue_get_entries(queue); while (entry) { struct bt_ad_data *data = entry->data; length += 1 + 1 + data->len; entry = entry->next; } return length; } size_t bt_ad_length(struct bt_ad *ad) { size_t length; if (!ad) return 0; length = 0; length += uuid_list_length(ad->service_uuids); length += uuid_list_length(ad->solicit_uuids); length += mfg_data_length(ad->manufacturer_data); length += uuid_data_length(ad->service_data); length += name_length(ad, &length); length += ad->appearance != UINT16_MAX ? 4 : 0; length += data_length(ad->data); return length; } static void serialize_uuids(struct queue *uuids, uint8_t uuid_type, uint8_t ad_type, struct iovec *iov) { const struct queue_entry *entry = queue_get_entries(uuids); uint8_t *len = NULL; while (entry) { bt_uuid_t *uuid = entry->data; if (uuid->type == uuid_type) { if (!len) { len = iov->iov_base + iov->iov_len; util_iov_push_u8(iov, 1); util_iov_push_u8(iov, ad_type); } switch (uuid->type) { case BT_UUID16: util_iov_push_le16(iov, uuid->value.u16); *len += 2; break; case BT_UUID32: util_iov_push_le32(iov, uuid->value.u32); *len += 4; break; case BT_UUID128: bt_uuid_to_le(uuid, util_iov_push(iov, 16)); *len += 16; break; case BT_UUID_UNSPEC: break; } } entry = entry->next; } } static void serialize_service_uuids(struct queue *uuids, struct iovec *iov) { serialize_uuids(uuids, BT_UUID16, BT_AD_UUID16_ALL, iov); serialize_uuids(uuids, BT_UUID32, BT_AD_UUID32_ALL, iov); serialize_uuids(uuids, BT_UUID128, BT_AD_UUID128_ALL, iov); } static void serialize_solicit_uuids(struct queue *uuids, struct iovec *iov) { serialize_uuids(uuids, BT_UUID16, BT_AD_SOLICIT16, iov); serialize_uuids(uuids, BT_UUID32, BT_AD_SOLICIT32, iov); serialize_uuids(uuids, BT_UUID128, BT_AD_SOLICIT128, iov); } static void serialize_manuf_data(struct queue *manuf_data, struct iovec *iov) { const struct queue_entry *entry = queue_get_entries(manuf_data); while (entry) { struct bt_ad_manufacturer_data *data = entry->data; util_iov_push_u8(iov, data->len + 2 + 1); util_iov_push_u8(iov, BT_AD_MANUFACTURER_DATA); util_iov_push_le16(iov, data->manufacturer_id); util_iov_push_mem(iov, data->len, data->data); entry = entry->next; } } static void serialize_service_data(struct queue *service_data, struct iovec *iov) { const struct queue_entry *entry = queue_get_entries(service_data); while (entry) { struct bt_ad_service_data *data = entry->data; int uuid_len = bt_uuid_len(&data->uuid); util_iov_push_u8(iov, data->len + uuid_len + 1); switch (uuid_len) { case 2: util_iov_push_u8(iov, BT_AD_SERVICE_DATA16); util_iov_push_le16(iov, data->uuid.value.u16); break; case 4: util_iov_push_u8(iov, BT_AD_SERVICE_DATA32); util_iov_push_le32(iov, data->uuid.value.u32); break; case 16: util_iov_push_u8(iov, BT_AD_SERVICE_DATA128); bt_uuid_to_le(&data->uuid, util_iov_push(iov, uuid_len)); break; } util_iov_push_mem(iov, data->len, data->data); entry = entry->next; } } static void serialize_name(struct bt_ad *ad, struct iovec *iov) { size_t len; uint8_t type = BT_AD_NAME_COMPLETE; if (!ad->name) return; len = strlen(ad->name); if (len > ad->max_len - (iov->iov_len + 2)) { type = BT_AD_NAME_SHORT; len = ad->max_len - (iov->iov_len + 2); } util_iov_push_u8(iov, len + 1); util_iov_push_u8(iov, type); util_iov_push_mem(iov, len, ad->name); } static void serialize_appearance(struct bt_ad *ad, struct iovec *iov) { if (ad->appearance == UINT16_MAX) return; util_iov_push_u8(iov, sizeof(ad->appearance) + 1); util_iov_push_u8(iov, BT_AD_GAP_APPEARANCE); util_iov_push_le16(iov, ad->appearance); } static void serialize_data(struct queue *queue, struct iovec *iov) { const struct queue_entry *entry = queue_get_entries(queue); while (entry) { struct bt_ad_data *data = entry->data; util_iov_push_u8(iov, data->len + 1); util_iov_push_u8(iov, data->type); util_iov_push_mem(iov, data->len, data->data); entry = entry->next; } } uint8_t *bt_ad_generate(struct bt_ad *ad, size_t *length) { struct iovec iov; if (!ad) return NULL; *length = bt_ad_length(ad); if (*length > ad->max_len) return NULL; iov.iov_base = malloc0(*length); if (!iov.iov_base) return NULL; iov.iov_len = 0; serialize_service_uuids(ad->service_uuids, &iov); serialize_solicit_uuids(ad->solicit_uuids, &iov); serialize_manuf_data(ad->manufacturer_data, &iov); serialize_service_data(ad->service_data, &iov); serialize_name(ad, &iov); serialize_appearance(ad, &iov); serialize_data(ad->data, &iov); return iov.iov_base; } bool bt_ad_is_empty(struct bt_ad *ad) { /* If any of the bt_ad fields are non-empty or don't have the default * value, then bt_ad_generate will return a non-empty buffer */ if (!ad->name && ad->appearance == UINT16_MAX && queue_isempty(ad->service_uuids) && queue_isempty(ad->manufacturer_data) && queue_isempty(ad->solicit_uuids) && queue_isempty(ad->service_data) && queue_isempty(ad->data)) { return true; } return false; } static bool queue_add_uuid(struct queue *queue, const bt_uuid_t *uuid) { bt_uuid_t *new_uuid; if (!queue) return false; new_uuid = new0(bt_uuid_t, 1); *new_uuid = *uuid; if (queue_push_tail(queue, new_uuid)) return true; free(new_uuid); return false; } static bool uuid_match(const void *data, const void *elem) { const bt_uuid_t *match_uuid = data; const bt_uuid_t *uuid = elem; return !bt_uuid_cmp(match_uuid, uuid); } static bool queue_remove_uuid(struct queue *queue, bt_uuid_t *uuid) { bt_uuid_t *removed; if (!queue || !uuid) return false; removed = queue_remove_if(queue, uuid_match, uuid); if (removed) { free(removed); return true; } return false; } bool bt_ad_add_service_uuid(struct bt_ad *ad, const bt_uuid_t *uuid) { if (!ad) return false; return queue_add_uuid(ad->service_uuids, uuid); } bool bt_ad_has_service_uuid(struct bt_ad *ad, const bt_uuid_t *uuid) { if (!ad) return false; return queue_find(ad->service_uuids, uuid_match, uuid); } bool bt_ad_remove_service_uuid(struct bt_ad *ad, bt_uuid_t *uuid) { if (!ad) return false; return queue_remove_uuid(ad->service_uuids, uuid); } void bt_ad_clear_service_uuid(struct bt_ad *ad) { if (!ad) return; queue_remove_all(ad->service_uuids, NULL, NULL, free); } static bool manufacturer_id_data_match(const void *data, const void *user_data) { const struct bt_ad_manufacturer_data *m = data; uint16_t id = PTR_TO_UINT(user_data); return m->manufacturer_id == id; } bool bt_ad_add_manufacturer_data(struct bt_ad *ad, uint16_t manufacturer_id, void *data, size_t len) { struct bt_ad_manufacturer_data *new_data; if (!ad) return false; if (len > (ad->max_len - 2 - sizeof(uint16_t))) return false; new_data = queue_find(ad->manufacturer_data, manufacturer_id_data_match, UINT_TO_PTR(manufacturer_id)); if (new_data) { if (new_data->len == len && !memcmp(new_data->data, data, len)) return false; new_data->data = realloc(new_data->data, len); memcpy(new_data->data, data, len); new_data->len = len; return true; } new_data = new0(struct bt_ad_manufacturer_data, 1); new_data->manufacturer_id = manufacturer_id; new_data->data = malloc(len); if (!new_data->data) { free(new_data); return false; } memcpy(new_data->data, data, len); new_data->len = len; if (queue_push_tail(ad->manufacturer_data, new_data)) return true; manuf_destroy(new_data); return false; } static bool manufacturer_data_match(const void *data, const void *user_data) { const struct bt_ad_manufacturer_data *m1 = data; const struct bt_ad_manufacturer_data *m2 = user_data; if (m1->manufacturer_id != m2->manufacturer_id) return false; if (m1->len != m2->len) return false; return !memcmp(m1->data, m2->data, m1->len); } bool bt_ad_has_manufacturer_data(struct bt_ad *ad, const struct bt_ad_manufacturer_data *data) { if (!ad) return false; if (!data) return !queue_isempty(ad->manufacturer_data); return queue_find(ad->manufacturer_data, manufacturer_data_match, data); } void bt_ad_foreach_manufacturer_data(struct bt_ad *ad, bt_ad_func_t func, void *user_data) { if (!ad) return; queue_foreach(ad->manufacturer_data, func, user_data); } bool bt_ad_remove_manufacturer_data(struct bt_ad *ad, uint16_t manufacturer_id) { struct bt_ad_manufacturer_data *data; if (!ad) return false; data = queue_remove_if(ad->manufacturer_data, manuf_match, UINT_TO_PTR(manufacturer_id)); if (!data) return false; manuf_destroy(data); return true; } void bt_ad_clear_manufacturer_data(struct bt_ad *ad) { if (!ad) return; queue_remove_all(ad->manufacturer_data, NULL, NULL, manuf_destroy); } bool bt_ad_add_solicit_uuid(struct bt_ad *ad, const bt_uuid_t *uuid) { if (!ad) return false; return queue_add_uuid(ad->solicit_uuids, uuid); } bool bt_ad_remove_solicit_uuid(struct bt_ad *ad, bt_uuid_t *uuid) { if (!ad) return false; return queue_remove_uuid(ad->solicit_uuids, uuid); } void bt_ad_clear_solicit_uuid(struct bt_ad *ad) { if (!ad) return; queue_remove_all(ad->solicit_uuids, NULL, NULL, free); } static bool service_uuid_match(const void *data, const void *user_data) { const struct bt_ad_service_data *s = data; const bt_uuid_t *uuid = user_data; return !bt_uuid_cmp(&s->uuid, uuid); } bool bt_ad_add_service_data(struct bt_ad *ad, const bt_uuid_t *uuid, void *data, size_t len) { struct bt_ad_service_data *new_data; if (!ad) return false; if (len > (ad->max_len - 2 - (size_t)bt_uuid_len(uuid))) return false; new_data = queue_find(ad->service_data, service_uuid_match, uuid); if (new_data) { if (new_data->len == len && !memcmp(new_data->data, data, len)) return false; new_data->data = realloc(new_data->data, len); memcpy(new_data->data, data, len); new_data->len = len; return true; } new_data = new0(struct bt_ad_service_data, 1); new_data->uuid = *uuid; new_data->data = malloc(len); if (!new_data->data) { free(new_data); return false; } memcpy(new_data->data, data, len); new_data->len = len; if (queue_push_tail(ad->service_data, new_data)) return true; uuid_destroy(new_data); return false; } static bool service_data_match(const void *data, const void *user_data) { const struct bt_ad_service_data *s1 = data; const struct bt_ad_service_data *s2 = user_data; if (bt_uuid_cmp(&s1->uuid, &s2->uuid)) return false; if (s1->len != s2->len) return false; return !memcmp(s1->data, s2->data, s1->len); } bool bt_ad_has_service_data(struct bt_ad *ad, const struct bt_ad_service_data *data) { if (!ad) return false; if (!data) return !queue_isempty(ad->service_data); return queue_find(ad->service_data, service_data_match, data); } void bt_ad_foreach_service_data(struct bt_ad *ad, bt_ad_func_t func, void *user_data) { if (!ad) return; queue_foreach(ad->service_data, func, user_data); } bool bt_ad_remove_service_data(struct bt_ad *ad, bt_uuid_t *uuid) { struct bt_ad_service_data *data; if (!ad) return false; data = queue_remove_if(ad->service_data, uuid_data_match, uuid); if (!data) return false; uuid_destroy(data); return true; } void bt_ad_clear_service_data(struct bt_ad *ad) { if (!ad) return; queue_remove_all(ad->service_data, NULL, NULL, uuid_destroy); } bool bt_ad_add_name(struct bt_ad *ad, const char *name) { if (!ad) return false; free(ad->name); ad->name = strdup(name); return true; } const char *bt_ad_get_name(struct bt_ad *ad) { if (!ad) return NULL; return ad->name; } void bt_ad_clear_name(struct bt_ad *ad) { if (!ad) return; free(ad->name); ad->name = NULL; } bool bt_ad_add_appearance(struct bt_ad *ad, uint16_t appearance) { if (!ad) return false; ad->appearance = appearance; return true; } void bt_ad_clear_appearance(struct bt_ad *ad) { if (!ad) return; ad->appearance = UINT16_MAX; } bool bt_ad_add_flags(struct bt_ad *ad, uint8_t *flags, size_t len) { if (!ad) return false; /* TODO: Add table to check other flags */ if (len > 1 || flags[0] & 0xe0) return false; return ad_replace_data(ad, BT_AD_FLAGS, flags, len); } uint8_t bt_ad_get_flags(struct bt_ad *ad) { struct bt_ad_data *data; if (!ad) return 0; data = queue_find(ad->data, data_type_match, UINT_TO_PTR(BT_AD_FLAGS)); if (!data || data->len != 1) return 0; return data->data[0]; } bool bt_ad_has_flags(struct bt_ad *ad) { struct bt_ad_data *data; if (!ad) return false; data = queue_find(ad->data, data_type_match, UINT_TO_PTR(BT_AD_FLAGS)); if (!data) return false; return true; } void bt_ad_clear_flags(struct bt_ad *ad) { if (!ad) return; queue_remove_all(ad->data, data_type_match, UINT_TO_PTR(BT_AD_FLAGS), data_destroy); } static uint8_t type_reject_list[] = { BT_AD_FLAGS, BT_AD_UUID16_SOME, BT_AD_UUID16_ALL, BT_AD_UUID32_SOME, BT_AD_UUID32_ALL, BT_AD_UUID128_SOME, BT_AD_UUID128_ALL, BT_AD_NAME_SHORT, BT_AD_NAME_COMPLETE, BT_AD_TX_POWER, BT_AD_CLASS_OF_DEV, BT_AD_SSP_HASH, BT_AD_SSP_RANDOMIZER, BT_AD_DEVICE_ID, BT_AD_SMP_TK, BT_AD_SMP_OOB_FLAGS, BT_AD_PERIPHERAL_CONN_INTERVAL, BT_AD_SOLICIT16, BT_AD_SOLICIT128, BT_AD_SERVICE_DATA16, BT_AD_PUBLIC_ADDRESS, BT_AD_RANDOM_ADDRESS, BT_AD_GAP_APPEARANCE, BT_AD_ADVERTISING_INTERVAL, BT_AD_LE_DEVICE_ADDRESS, BT_AD_LE_ROLE, BT_AD_SSP_HASH_P256, BT_AD_SSP_RANDOMIZER_P256, BT_AD_SOLICIT32, BT_AD_SERVICE_DATA32, BT_AD_SERVICE_DATA128, BT_AD_LE_SC_CONFIRM_VALUE, BT_AD_LE_SC_RANDOM_VALUE, BT_AD_LE_SUPPORTED_FEATURES, BT_AD_CHANNEL_MAP_UPDATE_IND, BT_AD_MESH_PROV, BT_AD_MESH_DATA, BT_AD_MESH_BEACON, BT_AD_3D_INFO_DATA, BT_AD_MANUFACTURER_DATA, }; bool bt_ad_add_data(struct bt_ad *ad, uint8_t type, void *data, size_t len) { size_t i; if (!ad) return false; if (len > (size_t)(ad->max_len - 2)) return false; for (i = 0; i < sizeof(type_reject_list); i++) { if (type == type_reject_list[i]) return false; } return ad_replace_data(ad, type, data, len); } static bool data_match(const void *data, const void *user_data) { const struct bt_ad_data *d1 = data; const struct bt_ad_data *d2 = user_data; if (d1->type != d2->type) return false; if (!d2->len && !d2->data) return true; if (d1->len != d2->len) return false; return !memcmp(d1->data, d2->data, d1->len); } bool bt_ad_has_data(struct bt_ad *ad, const struct bt_ad_data *data) { if (!ad) return false; if (!data) return !queue_isempty(ad->data); return queue_find(ad->data, data_match, data); } void bt_ad_foreach_data(struct bt_ad *ad, bt_ad_func_t func, void *user_data) { if (!ad) return; queue_foreach(ad->data, func, user_data); } bool bt_ad_remove_data(struct bt_ad *ad, uint8_t type) { struct bt_ad_data *data; if (!ad) return false; data = queue_remove_if(ad->data, data_type_match, UINT_TO_PTR(type)); if (!data) return false; data_destroy(data); return true; } void bt_ad_clear_data(struct bt_ad *ad) { if (!ad) return; queue_remove_all(ad->data, NULL, NULL, data_destroy); } int8_t bt_ad_get_tx_power(struct bt_ad *ad) { struct bt_ad_data *data; if (!ad) return 0; data = queue_find(ad->data, data_type_match, UINT_TO_PTR(BT_AD_TX_POWER)); if (!data || data->len != 1) return 127; return data->data[0]; } struct bt_ad_pattern *bt_ad_pattern_new(uint8_t type, size_t offset, size_t len, const uint8_t *data) { struct bt_ad_pattern *pattern; if (!data || !len || offset >= BT_AD_MAX_DATA_LEN || len > BT_AD_MAX_DATA_LEN || offset + len > BT_AD_MAX_DATA_LEN) { return NULL; } if (!ad_is_type_valid(type)) return NULL; pattern = new0(struct bt_ad_pattern, 1); if (!pattern) return NULL; pattern->len = len; pattern->type = type; pattern->offset = offset; memcpy(pattern->data, data, len); return pattern; } static bool match_manufacturer(const void *data, const void *user_data) { const struct bt_ad_manufacturer_data *manufacturer_data = data; const struct pattern_match_info *info = user_data; const struct bt_ad_pattern *pattern; uint8_t all_data[BT_AD_MAX_DATA_LEN]; if (!manufacturer_data || !info) return false; if (info->matched_pattern) return false; pattern = info->current_pattern; if (!pattern || pattern->type != BT_AD_MANUFACTURER_DATA) return false; /* Take the manufacturer ID into account */ if (manufacturer_data->len + 2 < pattern->offset + pattern->len) return false; memcpy(&all_data[0], &manufacturer_data->manufacturer_id, 2); memcpy(&all_data[2], manufacturer_data->data, manufacturer_data->len); if (!memcmp(all_data + pattern->offset, pattern->data, pattern->len)) { return true; } return false; } static bool match_service(const void *data, const void *user_data) { const struct bt_ad_service_data *service_data = data; const struct pattern_match_info *info = user_data; const struct bt_ad_pattern *pattern; if (!service_data || !info) return false; if (info->matched_pattern) return false; pattern = info->current_pattern; if (!pattern) return false; switch (pattern->type) { case BT_AD_SERVICE_DATA16: case BT_AD_SERVICE_DATA32: case BT_AD_SERVICE_DATA128: break; default: return false; } if (service_data->len < pattern->offset + pattern->len) return false; if (!memcmp(service_data->data + pattern->offset, pattern->data, pattern->len)) { return true; } return false; } static bool match_ad_data(const void *data, const void *user_data) { const struct bt_ad_data *ad_data = data; const struct pattern_match_info *info = user_data; const struct bt_ad_pattern *pattern; if (!ad_data || !info) return false; if (info->matched_pattern) return false; pattern = info->current_pattern; if (!pattern || ad_data->type != pattern->type) return false; if (ad_data->len < pattern->offset + pattern->len) return false; if (!memcmp(ad_data->data + pattern->offset, pattern->data, pattern->len)) { return true; } return false; } static void pattern_match(void *data, void *user_data) { struct bt_ad_pattern *pattern = data; struct pattern_match_info *info = user_data; struct bt_ad *ad; void *matched = NULL; if (!pattern || !info) return; if (info->matched_pattern) return; info->current_pattern = pattern; ad = info->ad; if (!ad) return; switch (pattern->type) { case BT_AD_MANUFACTURER_DATA: matched = queue_find(ad->manufacturer_data, match_manufacturer, user_data); break; case BT_AD_SERVICE_DATA16: case BT_AD_SERVICE_DATA32: case BT_AD_SERVICE_DATA128: matched = queue_find(ad->service_data, match_service, user_data); break; default: matched = queue_find(ad->data, match_ad_data, user_data); break; } if (matched) info->matched_pattern = info->current_pattern; } struct bt_ad_pattern *bt_ad_pattern_match(struct bt_ad *ad, struct queue *patterns) { struct pattern_match_info info; if (!ad || queue_isempty(patterns)) return NULL; info.ad = ad; info.matched_pattern = NULL; info.current_pattern = NULL; queue_foreach(patterns, pattern_match, &info); return info.matched_pattern; } bluez-5.82/src/shared/PaxHeaders/pcap.c0000644000000000000000000000005014015011623014763 xustar0020 atime=1743515876 20 ctime=1743591277 bluez-5.82/src/shared/pcap.c0000644000000000000000000001006214015011623014443 0ustar00rootroot// SPDX-License-Identifier: LGPL-2.1-or-later /* * * BlueZ - Bluetooth protocol stack for Linux * * Copyright (C) 2012-2014 Intel Corporation. All rights reserved. * * */ #ifdef HAVE_CONFIG_H #include #endif #define _GNU_SOURCE #include #include #include #include "src/shared/util.h" #include "src/shared/pcap.h" struct pcap_hdr { uint32_t magic_number; /* magic number */ uint16_t version_major; /* major version number */ uint16_t version_minor; /* minor version number */ int32_t thiszone; /* GMT to local correction */ uint32_t sigfigs; /* accuracy of timestamps */ uint32_t snaplen; /* max length of captured packets, in octets */ uint32_t network; /* data link type */ } __attribute__ ((packed)); #define PCAP_HDR_SIZE (sizeof(struct pcap_hdr)) struct pcap_pkt { uint32_t ts_sec; /* timestamp seconds */ uint32_t ts_usec; /* timestamp microseconds */ uint32_t incl_len; /* number of octets of packet saved in file */ uint32_t orig_len; /* actual length of packet */ } __attribute__ ((packed)); #define PCAP_PKT_SIZE (sizeof(struct pcap_pkt)) struct pcap_ppi { uint8_t version; /* version, currently 0 */ uint8_t flags; /* flags */ uint16_t len; /* length of entire message */ uint32_t dlt; /* data link type */ } __attribute__ ((packed)); #define PCAP_PPI_SIZE (sizeof(struct pcap_ppi)) struct pcap { int ref_count; int fd; uint32_t type; uint32_t snaplen; }; struct pcap *pcap_open(const char *path) { struct pcap *pcap; struct pcap_hdr hdr; ssize_t len; pcap = calloc(1, sizeof(*pcap)); if (!pcap) return NULL; pcap->fd = open(path, O_RDONLY | O_CLOEXEC); if (pcap->fd < 0) { free(pcap); return NULL; } len = read(pcap->fd, &hdr, PCAP_HDR_SIZE); if (len < 0 || len != PCAP_HDR_SIZE) goto failed; if (hdr.magic_number != 0xa1b2c3d4) goto failed; if (hdr.version_major != 2 || hdr.version_minor != 4) goto failed; pcap->snaplen = hdr.snaplen; pcap->type = hdr.network; return pcap_ref(pcap); failed: close(pcap->fd); free(pcap); return NULL; } struct pcap *pcap_ref(struct pcap *pcap) { if (!pcap) return NULL; __sync_fetch_and_add(&pcap->ref_count, 1); return pcap; } void pcap_unref(struct pcap *pcap) { if (!pcap) return; if (__sync_sub_and_fetch(&pcap->ref_count, 1)) return; if (pcap->fd >= 0) close(pcap->fd); free(pcap); } uint32_t pcap_get_type(struct pcap *pcap) { if (!pcap) return PCAP_TYPE_INVALID; return pcap->type; } uint32_t pcap_get_snaplen(struct pcap *pcap) { if (!pcap) return 0; return pcap->snaplen; } bool pcap_read(struct pcap *pcap, struct timeval *tv, void *data, uint32_t size, uint32_t *len) { struct pcap_pkt pkt; uint32_t toread; ssize_t bytes_read; if (!pcap) return false; bytes_read = read(pcap->fd, &pkt, PCAP_PKT_SIZE); if (bytes_read != PCAP_PKT_SIZE) return false; if (pkt.incl_len > size) toread = size; else toread = pkt.incl_len; bytes_read = read(pcap->fd, data, toread); if (bytes_read < 0) return false; if (tv) { tv->tv_sec = pkt.ts_sec; tv->tv_usec = pkt.ts_usec; } if (len) *len = toread; return true; } bool pcap_read_ppi(struct pcap *pcap, struct timeval *tv, uint32_t *type, void *data, uint32_t size, uint32_t *offset, uint32_t *len) { struct pcap_pkt pkt; struct pcap_ppi ppi; uint16_t pph_len; uint32_t toread; ssize_t bytes_read; if (!pcap) return false; bytes_read = read(pcap->fd, &pkt, PCAP_PKT_SIZE); if (bytes_read != PCAP_PKT_SIZE) return false; if (pkt.incl_len > size) toread = size; else toread = pkt.incl_len; bytes_read = read(pcap->fd, &ppi, PCAP_PPI_SIZE); if (bytes_read != PCAP_PPI_SIZE) return false; if (ppi.flags) return false; pph_len = le16_to_cpu(ppi.len); if (pph_len < PCAP_PPI_SIZE) return false; bytes_read = read(pcap->fd, data, toread - PCAP_PPI_SIZE); if (bytes_read < 0) return false; if (tv) { tv->tv_sec = pkt.ts_sec; tv->tv_usec = pkt.ts_usec; } if (type) *type = le32_to_cpu(ppi.dlt); if (offset) *offset = pph_len - PCAP_PPI_SIZE; if (len) *len = toread - pph_len; return true; } bluez-5.82/src/shared/PaxHeaders/ascs.h0000644000000000000000000000005014333256743015017 xustar0020 atime=1743515904 20 ctime=1743591277 bluez-5.82/src/shared/ascs.h0000644000000000000000000001123314333256743014500 0ustar00rootroot/* SPDX-License-Identifier: LGPL-2.1-or-later */ /* * * BlueZ - Bluetooth protocol stack for Linux * * Copyright (C) 2022 Intel Corporation. All rights reserved. * */ /* Response Status Code */ #define BT_ASCS_RSP_SUCCESS 0x00 #define BT_ASCS_RSP_NOT_SUPPORTED 0x01 #define BT_ASCS_RSP_TRUNCATED 0x02 #define BT_ASCS_RSP_INVALID_ASE 0x03 #define BT_ASCS_RSP_INVALID_ASE_STATE 0x04 #define BT_ASCS_RSP_INVALID_DIR 0x05 #define BT_ASCS_RSP_CAP_UNSUPPORTED 0x06 #define BT_ASCS_RSP_CONF_UNSUPPORTED 0x07 #define BT_ASCS_RSP_CONF_REJECTED 0x08 #define BT_ASCS_RSP_CONF_INVALID 0x09 #define BT_ASCS_RSP_METADATA_UNSUPPORTED 0x0a #define BT_ASCS_RSP_METADATA_REJECTED 0x0b #define BT_ASCS_RSP_METADATA_INVALID 0x0c #define BT_ASCS_RSP_NO_MEM 0x0d #define BT_ASCS_RSP_UNSPECIFIED 0x0e /* Response Reasons */ #define BT_ASCS_REASON_NONE 0x00 #define BT_ASCS_REASON_CODEC 0x01 #define BT_ASCS_REASON_CODEC_DATA 0x02 #define BT_ASCS_REASON_INTERVAL 0x03 #define BT_ASCS_REASON_FRAMING 0x04 #define BT_ASCS_REASON_PHY 0x05 #define BT_ASCS_REASON_SDU 0x06 #define BT_ASCS_REASON_RTN 0x07 #define BT_ASCS_REASON_LATENCY 0x08 #define BT_ASCS_REASON_PD 0x09 #define BT_ASCS_REASON_CIS 0x0a /* Transport QoS Packing */ #define BT_ASCS_QOS_PACKING_SEQ 0x00 #define BT_ASCS_QOS_PACKING_INT 0x01 /* Transport QoS Framing */ #define BT_ASCS_QOS_FRAMING_UNFRAMED 0x00 #define BT_ASCS_QOS_FRAMING_FRAMED 0x01 /* ASE characteristic states */ #define BT_ASCS_ASE_STATE_IDLE 0x00 #define BT_ASCS_ASE_STATE_CONFIG 0x01 #define BT_ASCS_ASE_STATE_QOS 0x02 #define BT_ASCS_ASE_STATE_ENABLING 0x03 #define BT_ASCS_ASE_STATE_STREAMING 0x04 #define BT_ASCS_ASE_STATE_DISABLING 0x05 #define BT_ASCS_ASE_STATE_RELEASING 0x06 struct bt_ascs_ase_rsp { uint8_t ase; uint8_t code; uint8_t reason; } __packed; struct bt_ascs_cp_rsp { uint8_t op; uint8_t num_ase; struct bt_ascs_ase_rsp rsp[0]; } __packed; struct bt_ascs_ase_status { uint8_t id; uint8_t state; uint8_t params[0]; } __packed; /* ASE_State = 0x01 (Codec Configured), defined in Table 4.7. */ struct bt_ascs_ase_status_config { uint8_t framing; uint8_t phy; uint8_t rtn; uint16_t latency; uint8_t pd_min[3]; uint8_t pd_max[3]; uint8_t ppd_min[3]; uint8_t ppd_max[3]; struct bt_bap_codec codec; uint8_t cc_len; /* LTV-formatted Codec-Specific Configuration */ struct bt_ltv cc[0]; } __packed; /* ASE_State = 0x02 (QoS Configured), defined in Table 4.8. */ struct bt_ascs_ase_status_qos { uint8_t cig_id; uint8_t cis_id; uint8_t interval[3]; uint8_t framing; uint8_t phy; uint16_t sdu; uint8_t rtn; uint16_t latency; uint8_t pd[3]; } __packed; /* ASE_Status = 0x03 (Enabling), 0x04 (Streaming), or 0x05 (Disabling) * defined in Table 4.9. */ struct bt_ascs_ase_status_metadata { uint8_t cig_id; uint8_t cis_id; uint8_t len; uint8_t data[0]; } __packed; struct bt_ascs_ase_hdr { uint8_t op; uint8_t num; } __packed; #define BT_ASCS_CONFIG 0x01 #define BT_ASCS_CONFIG_LATENCY_LOW 0x01 #define BT_ASCS_CONFIG_LATENCY_MEDIUM 0x02 #define BT_ASCS_CONFIG_LATENCY_HIGH 0x03 #define BT_ASCS_CONFIG_PHY_LE_1M 0x01 #define BT_ASCS_CONFIG_PHY_LE_2M 0x02 #define BT_ASCS_CONFIG_PHY_LE_CODED 0x03 struct bt_ascs_codec_config { uint8_t len; uint8_t type; uint8_t data[0]; } __packed; struct bt_ascs_config { uint8_t ase; /* ASE ID */ uint8_t latency; /* Target Latency */ uint8_t phy; /* Target PHY */ struct bt_bap_codec codec; /* Codec ID */ uint8_t cc_len; /* Codec Specific Config Length */ /* LTV-formatted Codec-Specific Configuration */ struct bt_ascs_codec_config cc[0]; } __packed; #define BT_ASCS_QOS 0x02 struct bt_ascs_qos { uint8_t ase; /* ASE ID */ uint8_t cig; /* CIG ID*/ uint8_t cis; /* CIG ID*/ uint8_t interval[3]; /* Frame interval */ uint8_t framing; /* Frame framing */ uint8_t phy; /* PHY */ uint16_t sdu; /* Maximum SDU Size */ uint8_t rtn; /* Retransmission Effort */ uint16_t latency; /* Transport Latency */ uint8_t pd[3]; /* Presentation Delay */ } __packed; #define BT_ASCS_ENABLE 0x03 struct bt_ascs_metadata { uint8_t ase; /* ASE ID */ uint8_t len; /* Metadata length */ uint8_t data[0]; /* LTV-formatted Metadata */ } __packed; struct bt_ascs_enable { struct bt_ascs_metadata meta; /* Metadata */ } __packed; #define BT_ASCS_START 0x04 struct bt_ascs_start { uint8_t ase; /* ASE ID */ } __packed; #define BT_ASCS_DISABLE 0x05 struct bt_ascs_disable { uint8_t ase; /* ASE ID */ } __packed; #define BT_ASCS_STOP 0x06 struct bt_ascs_stop { uint8_t ase; /* ASE ID */ } __packed; #define BT_ASCS_METADATA 0x07 #define BT_ASCS_RELEASE 0x08 struct bt_ascs_release { uint8_t ase; /* ASE ID */ } __packed; bluez-5.82/src/shared/PaxHeaders/micp.h0000644000000000000000000000005014505354670015015 xustar0020 atime=1743515925 20 ctime=1743591277 bluez-5.82/src/shared/micp.h0000644000000000000000000000427514505354670014506 0ustar00rootroot/* SPDX-License-Identifier: LGPL-2.1-or-later */ /* * * BlueZ - Bluetooth protocol stack for Linux * * Copyright (C) 2023 NXP Semiconductors. All rights reserved. * */ #include #include #include "src/shared/io.h" #include "src/shared/gatt-client.h" struct bt_mics; struct bt_micp; typedef void (*bt_micp_ready_func_t)(struct bt_micp *micp, void *user_data); typedef void (*bt_micp_destroy_func_t)(void *user_data); typedef void (*bt_micp_debug_func_t)(const char *str, void *user_data); typedef void (*bt_micp_func_t)(struct bt_micp *micp, void *user_data); struct bt_micp_db { struct gatt_db *db; struct bt_mics *mics; }; struct bt_mics { struct bt_micp_db *mdb; uint8_t mute_stat; struct gatt_db_attribute *service; struct gatt_db_attribute *ms; struct gatt_db_attribute *ms_ccc; }; struct bt_micp { int ref_count; struct bt_micp_db *ldb; struct bt_micp_db *rdb; struct bt_gatt_client *client; struct bt_att *att; unsigned int mute_id; unsigned int idle_id; uint8_t mute; struct queue *notify; struct queue *pending; struct queue *ready_cbs; bt_micp_debug_func_t debug_func; bt_micp_destroy_func_t debug_destroy; void *debug_data; void *user_data; }; struct bt_micp *bt_micp_ref(struct bt_micp *micp); void bt_micp_unref(struct bt_micp *micp); void bt_micp_add_db(struct gatt_db *db); bool bt_micp_attach(struct bt_micp *micp, struct bt_gatt_client *client); void bt_micp_detach(struct bt_micp *micp); bool bt_micp_set_debug(struct bt_micp *micp, bt_micp_debug_func_t func, void *user_data, bt_micp_destroy_func_t destroy); struct bt_att *bt_micp_get_att(struct bt_micp *micp); bool bt_micp_set_user_data(struct bt_micp *micp, void *user_data); /* session related functions */ unsigned int bt_micp_register(bt_micp_func_t attached, bt_micp_func_t detached, void *user_data); unsigned int bt_micp_ready_register(struct bt_micp *micp, bt_micp_ready_func_t func, void *user_data, bt_micp_destroy_func_t destroy); bool bt_micp_ready_unregister(struct bt_micp *micp, unsigned int id); bool bt_micp_unregister(unsigned int id); struct bt_micp *bt_micp_new(struct gatt_db *ldb, struct gatt_db *rdb); struct bt_mics *micp_get_mics(struct bt_micp *micp); bluez-5.82/src/shared/PaxHeaders/ecc.h0000644000000000000000000000005014015011623014577 xustar0020 atime=1743515864 20 ctime=1743591277 bluez-5.82/src/shared/ecc.h0000644000000000000000000000340314015011623014260 0ustar00rootroot/* SPDX-License-Identifier: BSD-2-Clause */ /* * Copyright (c) 2013, Kenneth MacKay * All rights reserved. * */ #include #include /* Create a public key from a private key. * * Inputs: * private_key - Your private key. * * Outputs: * public_key - Will be filled in with the public key. * * Returns true if the public key was generated successfully, false * if an error occurred. The keys are with the LSB first. */ bool ecc_make_public_key(const uint8_t private_key[32], uint8_t public_key[64]); /* Create a public/private key pair. * * Outputs: * public_key - Will be filled in with the public key. * private_key - Will be filled in with the private key. * * Returns true if the key pair was generated successfully, false * if an error occurred. The keys are with the LSB first. */ bool ecc_make_key(uint8_t public_key[64], uint8_t private_key[32]); /* Check to see if a public key is valid. * * Inputs: * public_key - The public key to check. * * Returns true if the public key is valid, false if it is invalid. */ bool ecc_valid_public_key(const uint8_t public_key[64]); /* Compute a shared secret given your secret key and someone else's * public key. * Note: It is recommended that you hash the result of ecdh_shared_secret * before using it for symmetric encryption or HMAC. * * Inputs: * public_key - The public key of the remote party. * private_key - Your private key. * * Outputs: * secret - Will be filled in with the shared secret value. * * Returns true if the shared secret was generated successfully, false * if an error occurred. Both input and output parameters are with the * LSB first. */ bool ecdh_shared_secret(const uint8_t public_key[64], const uint8_t private_key[32], uint8_t secret[32]); bluez-5.82/src/shared/PaxHeaders/mcp.h0000644000000000000000000000005014643061455014643 xustar0020 atime=1743515918 20 ctime=1743591277 bluez-5.82/src/shared/mcp.h0000644000000000000000000000413414643061455014326 0ustar00rootroot/* SPDX-License-Identifier: LGPL-2.1-or-later */ /* * * BlueZ - Bluetooth protocol stack for Linux * * Copyright (C) 2020 Intel Corporation. All rights reserved. * */ #include #include #ifndef __packed #define __packed __attribute__((packed)) #endif struct bt_mcp; struct bt_mcp_db; struct bt_mcp_session_info; typedef void (*bt_mcp_debug_func_t)(const char *str, void *user_data); typedef void (*bt_mcp_destroy_func_t)(void *user_data); struct bt_mcp_event_callback { void (*player_name)(struct bt_mcp *mcp, const uint8_t *value, uint16_t length); void (*track_changed)(struct bt_mcp *mcp); void (*track_title)(struct bt_mcp *mcp, const uint8_t *value, uint16_t length); void (*track_duration)(struct bt_mcp *mcp, int32_t duration); void (*track_position)(struct bt_mcp *mcp, int32_t position); void (*playback_speed)(struct bt_mcp *mcp, int8_t speed); void (*seeking_speed)(struct bt_mcp *mcp, int8_t speed); void (*play_order)(struct bt_mcp *mcp, uint8_t order); void (*play_order_supported)(struct bt_mcp *mcp, uint16_t order_supported); void (*media_state)(struct bt_mcp *mcp, uint8_t state); void (*content_control_id)(struct bt_mcp *mcp, uint8_t cc_id); }; void bt_mcp_set_event_callbacks(struct bt_mcp *mcp, const struct bt_mcp_event_callback *cbs, void *user_data); bool bt_mcp_set_debug(struct bt_mcp *mcp, bt_mcp_debug_func_t cb, void *user_data, bt_mcp_destroy_func_t destroy); void bt_mcp_register(struct gatt_db *db); bool bt_mcp_attach(struct bt_mcp *mcp, struct bt_gatt_client *client); void bt_mcp_detach(struct bt_mcp *mcp); struct bt_mcp *bt_mcp_new(struct gatt_db *ldb, struct gatt_db *rdb); struct bt_mcp *bt_mcp_ref(struct bt_mcp *mcp); void bt_mcp_unref(struct bt_mcp *mcp); bool bt_mcp_set_user_data(struct bt_mcp *mcp, void *user_data); void *bt_mcp_get_user_data(struct bt_mcp *mcp); unsigned int bt_mcp_play(struct bt_mcp *mcp); unsigned int bt_mcp_pause(struct bt_mcp *mcp); unsigned int bt_mcp_stop(struct bt_mcp *mcp); unsigned int bt_mcp_next_track(struct bt_mcp *mcp); unsigned int bt_mcp_previous_track(struct bt_mcp *mcp); bluez-5.82/src/shared/PaxHeaders/uhid.c0000644000000000000000000000005014711225434015003 xustar0020 atime=1743515874 20 ctime=1743591277 bluez-5.82/src/shared/uhid.c0000644000000000000000000002774014711225434014476 0ustar00rootroot// SPDX-License-Identifier: LGPL-2.1-or-later /* * * BlueZ - Bluetooth protocol stack for Linux * * Copyright (C) 2014 Intel Corporation. All rights reserved. * * */ #ifdef HAVE_CONFIG_H #include #endif #define _GNU_SOURCE #include #include #include #include #include #include "src/shared/io.h" #include "src/shared/util.h" #include "src/shared/queue.h" #include "src/shared/uhid.h" #define UHID_DEVICE_FILE "/dev/uhid" #ifndef MIN #define MIN(x, y) ((x) < (y) ? (x) : (y)) #endif struct uhid_replay { bool active; struct queue *out; struct queue *in; struct queue *rout; struct queue *rin; }; struct bt_uhid { int ref_count; struct io *io; unsigned int notify_id; bool notifying; struct queue *notify_list; struct queue *input; uint8_t type; bool created; unsigned int start_id; bool started; struct uhid_replay *replay; }; struct uhid_notify { unsigned int id; uint32_t event; bt_uhid_callback_t func; void *user_data; bool removed; }; static void uhid_replay_free(struct uhid_replay *replay) { if (!replay) return; queue_destroy(replay->rin, NULL); queue_destroy(replay->in, free); queue_destroy(replay->rout, NULL); queue_destroy(replay->out, free); free(replay); } static void uhid_free(struct bt_uhid *uhid) { if (uhid->io) io_destroy(uhid->io); if (uhid->notify_list) queue_destroy(uhid->notify_list, free); if (uhid->input) queue_destroy(uhid->input, free); uhid_replay_free(uhid->replay); free(uhid); } static void notify_handler(void *data, void *user_data) { struct uhid_notify *notify = data; struct uhid_event *ev = user_data; if (notify->event != ev->type) return; if (notify->func) notify->func(ev, notify->user_data); } static struct uhid_replay *uhid_replay_new(void) { struct uhid_replay *replay = new0(struct uhid_replay, 1); replay->out = queue_new(); replay->in = queue_new(); return replay; } static int bt_uhid_record(struct bt_uhid *uhid, bool input, struct uhid_event *ev) { if (!uhid) return -EINVAL; /* Capture input events in replay mode and send the next replay event */ if (uhid->replay && uhid->replay->active && input) { queue_pop_head(uhid->replay->rin); bt_uhid_replay(uhid); return -EALREADY; } if (!uhid->replay) uhid->replay = uhid_replay_new(); if (input) queue_push_tail(uhid->replay->in, util_memdup(ev, sizeof(*ev))); else queue_push_tail(uhid->replay->out, util_memdup(ev, sizeof(*ev))); return 0; } static bool match_removed(const void *a, const void *b) { const struct uhid_notify *notify = a; return notify->removed; } static void uhid_notify(struct bt_uhid *uhid, struct uhid_event *ev) { /* Add a reference to the uhid to ensure it doesn't get freed while at * notify_handler. */ bt_uhid_ref(uhid); uhid->notifying = true; queue_foreach(uhid->notify_list, notify_handler, ev); uhid->notifying = false; queue_remove_all(uhid->notify_list, match_removed, NULL, free); bt_uhid_unref(uhid); } static bool uhid_read_handler(struct io *io, void *user_data) { struct bt_uhid *uhid = user_data; int fd; ssize_t len; struct uhid_event ev; fd = io_get_fd(io); if (fd < 0) return false; memset(&ev, 0, sizeof(ev)); len = read(fd, &ev, sizeof(ev)); if (len < 0) return false; if ((size_t) len < sizeof(ev.type)) return false; switch (ev.type) { case UHID_GET_REPORT: case UHID_SET_REPORT: bt_uhid_record(uhid, false, &ev); break; } uhid_notify(uhid, &ev); return true; } struct bt_uhid *bt_uhid_new_default(void) { struct bt_uhid *uhid; int fd; fd = open(UHID_DEVICE_FILE, O_RDWR | O_CLOEXEC); if (fd < 0) return NULL; uhid = bt_uhid_new(fd); if (!uhid) { close(fd); return NULL; } io_set_close_on_destroy(uhid->io, true); return uhid; } struct bt_uhid *bt_uhid_new(int fd) { struct bt_uhid *uhid; uhid = new0(struct bt_uhid, 1); uhid->io = io_new(fd); if (!uhid->io) goto failed; uhid->notify_list = queue_new(); if (!io_set_read_handler(uhid->io, uhid_read_handler, uhid, NULL)) goto failed; return bt_uhid_ref(uhid); failed: uhid_free(uhid); return NULL; } struct bt_uhid *bt_uhid_ref(struct bt_uhid *uhid) { if (!uhid) return NULL; __sync_fetch_and_add(&uhid->ref_count, 1); return uhid; } void bt_uhid_unref(struct bt_uhid *uhid) { if (!uhid) return; if (__sync_sub_and_fetch(&uhid->ref_count, 1)) return; uhid_free(uhid); } bool bt_uhid_set_close_on_unref(struct bt_uhid *uhid, bool do_close) { if (!uhid || !uhid->io) return false; io_set_close_on_destroy(uhid->io, do_close); return true; } unsigned int bt_uhid_register(struct bt_uhid *uhid, uint32_t event, bt_uhid_callback_t func, void *user_data) { struct uhid_notify *notify; if (!uhid) return 0; notify = new0(struct uhid_notify, 1); notify->id = ++uhid->notify_id ? uhid->notify_id : ++uhid->notify_id; notify->event = event; notify->func = func; notify->user_data = user_data; if (!queue_push_tail(uhid->notify_list, notify)) { free(notify); return 0; } return notify->id; } static bool match_notify_id(const void *a, const void *b) { const struct uhid_notify *notify = a; unsigned int id = PTR_TO_UINT(b); return notify->id == id; } bool bt_uhid_unregister(struct bt_uhid *uhid, unsigned int id) { struct uhid_notify *notify; if (!uhid || !id) return false; notify = queue_remove_if(uhid->notify_list, match_notify_id, UINT_TO_PTR(id)); if (!notify) return false; free(notify); return true; } static bool match_not_id(const void *a, const void *b) { const struct uhid_notify *notify = a; unsigned int id = PTR_TO_UINT(b); return notify->id != id; } static void uhid_notify_removed(void *data, void *user_data) { struct uhid_notify *notify = data; struct bt_uhid *uhid = user_data; /* Skip marking start_id as removed since that is not removed with * unregister all. */ if (notify->id == uhid->start_id) return; notify->removed = true; } bool bt_uhid_unregister_all(struct bt_uhid *uhid) { if (!uhid) return false; if (!uhid->notifying) queue_remove_all(uhid->notify_list, match_not_id, UINT_TO_PTR(uhid->start_id), free); else queue_foreach(uhid->notify_list, uhid_notify_removed, uhid); return true; } static int uhid_send(struct bt_uhid *uhid, const struct uhid_event *ev) { ssize_t len; struct iovec iov; iov.iov_base = (void *) ev; iov.iov_len = sizeof(*ev); len = io_send(uhid->io, &iov, 1); if (len < 0) return -errno; /* uHID kernel driver does not handle partial writes */ return len != sizeof(*ev) ? -EIO : 0; } int bt_uhid_send(struct bt_uhid *uhid, const struct uhid_event *ev) { if (!uhid || !ev) return -EINVAL; if (!uhid->io) return -ENOTCONN; return uhid_send(uhid, ev); } static bool input_dequeue(const void *data, const void *match_data) { struct uhid_event *ev = (void *)data; struct bt_uhid *uhid = (void *)match_data; return bt_uhid_send(uhid, ev) == 0; } static void uhid_start(struct uhid_event *ev, void *user_data) { struct bt_uhid *uhid = user_data; uhid->started = true; /* dequeue input events send while UHID_CREATE2 was in progress */ queue_remove_all(uhid->input, input_dequeue, uhid, free); } int bt_uhid_create(struct bt_uhid *uhid, const char *name, bdaddr_t *src, bdaddr_t *dst, uint32_t vendor, uint32_t product, uint32_t version, uint32_t country, uint8_t type, void *rd_data, size_t rd_size) { struct uhid_event ev; int err; if (!uhid || !name || rd_size > sizeof(ev.u.create2.rd_data)) return -EINVAL; if (uhid->created) return 0; /* Register callback for UHID_START if not registered yet */ if (!uhid->start_id) { uhid->start_id = bt_uhid_register(uhid, UHID_START, uhid_start, uhid); if (!uhid->start_id) return -ENOMEM; } memset(&ev, 0, sizeof(ev)); ev.type = UHID_CREATE2; strncpy((char *) ev.u.create2.name, name, sizeof(ev.u.create2.name) - 1); if (src) sprintf((char *)ev.u.create2.phys, "%2.2x:%2.2x:%2.2x:%2.2x:%2.2x:%2.2x", src->b[5], src->b[4], src->b[3], src->b[2], src->b[1], src->b[0]); if (dst) sprintf((char *)ev.u.create2.uniq, "%2.2x:%2.2x:%2.2x:%2.2x:%2.2x:%2.2x", dst->b[5], dst->b[4], dst->b[3], dst->b[2], dst->b[1], dst->b[0]); ev.u.create2.vendor = vendor; ev.u.create2.product = product; ev.u.create2.version = version; ev.u.create2.country = country; ev.u.create2.bus = BUS_BLUETOOTH; if (rd_size) memcpy(ev.u.create2.rd_data, rd_data, rd_size); ev.u.create2.rd_size = rd_size; err = bt_uhid_send(uhid, &ev); if (err) return err; uhid->created = true; uhid->started = false; uhid->type = type; return 0; } bool bt_uhid_created(struct bt_uhid *uhid) { if (!uhid) return false; return uhid->created; } bool bt_uhid_started(struct bt_uhid *uhid) { if (!uhid) return false; return uhid->started; } int bt_uhid_input(struct bt_uhid *uhid, uint8_t number, const void *data, size_t size) { struct uhid_event ev; struct uhid_input2_req *req = &ev.u.input2; size_t len = 0; if (!uhid) return -EINVAL; memset(&ev, 0, sizeof(ev)); ev.type = UHID_INPUT2; if (number) { req->data[len++] = number; req->size = 1 + MIN(size, sizeof(req->data) - 1); } else req->size = MIN(size, sizeof(req->data)); if (data && size) memcpy(&req->data[len], data, req->size - len); /* Queue events if UHID_START has not been received yet */ if (!uhid->started) { if (!uhid->input) uhid->input = queue_new(); queue_push_tail(uhid->input, util_memdup(&ev, sizeof(ev))); return 0; } return bt_uhid_send(uhid, &ev); } int bt_uhid_set_report_reply(struct bt_uhid *uhid, uint8_t id, uint8_t status) { struct uhid_event ev; struct uhid_set_report_reply_req *rsp = &ev.u.set_report_reply; if (!uhid) return false; memset(&ev, 0, sizeof(ev)); ev.type = UHID_SET_REPORT_REPLY; rsp->id = id; rsp->err = status; if (bt_uhid_record(uhid, true, &ev) == -EALREADY) return 0; return bt_uhid_send(uhid, &ev); } int bt_uhid_get_report_reply(struct bt_uhid *uhid, uint8_t id, uint8_t number, uint8_t status, const void *data, size_t size) { struct uhid_event ev; struct uhid_get_report_reply_req *rsp = &ev.u.get_report_reply; size_t len = 0; if (!uhid) return false; memset(&ev, 0, sizeof(ev)); ev.type = UHID_GET_REPORT_REPLY; rsp->id = id; rsp->err = status; if (!data || !size) goto done; if (number) { rsp->data[len++] = number; rsp->size += MIN(size, sizeof(rsp->data) - 1); } else rsp->size = MIN(size, sizeof(ev.u.input.data)); memcpy(&rsp->data[len], data, rsp->size - len); done: if (bt_uhid_record(uhid, true, &ev) == -EALREADY) return 0; return bt_uhid_send(uhid, &ev); } int bt_uhid_destroy(struct bt_uhid *uhid, bool force) { struct uhid_event ev; int err; if (!uhid) return -EINVAL; /* Cleanup input queue */ queue_destroy(uhid->input, free); uhid->input = NULL; /* Force destroy for non-keyboard devices - keyboards are not destroyed * on disconnect since they can glitch on reconnection losing * keypresses. */ if (!force && uhid->type != BT_UHID_KEYBOARD) force = true; if (!uhid->created || !force) return 0; memset(&ev, 0, sizeof(ev)); ev.type = UHID_DESTROY; err = bt_uhid_send(uhid, &ev); if (err < 0) return err; uhid->created = false; uhid_replay_free(uhid->replay); uhid->replay = NULL; return err; } static void queue_append(void *data, void *user_data) { queue_push_tail(user_data, data); } static struct queue *queue_dup(struct queue *q) { struct queue *dup; if (!q || queue_isempty(q)) return NULL; dup = queue_new(); queue_foreach(q, queue_append, dup); return dup; } int bt_uhid_replay(struct bt_uhid *uhid) { struct uhid_event *ev; if (!uhid || !uhid->started) return -EINVAL; if (!uhid->replay) return 0; if (uhid->replay->active) goto resend; uhid->replay->active = true; queue_destroy(uhid->replay->rin, NULL); uhid->replay->rin = queue_dup(uhid->replay->in); queue_destroy(uhid->replay->rout, NULL); uhid->replay->rout = queue_dup(uhid->replay->out); resend: ev = queue_pop_head(uhid->replay->rout); if (!ev) { uhid->replay->active = false; return 0; } uhid_notify(uhid, ev); return 0; } bluez-5.82/src/shared/PaxHeaders/tester.c0000644000000000000000000000005014621503015015352 xustar0020 atime=1743515944 20 ctime=1743591277 bluez-5.82/src/shared/tester.c0000644000000000000000000005161414621503015015042 0ustar00rootroot// SPDX-License-Identifier: LGPL-2.1-or-later /* * * BlueZ - Bluetooth protocol stack for Linux * * Copyright (C) 2012-2014 Intel Corporation. All rights reserved. * * */ #ifdef HAVE_CONFIG_H #include #endif #define _GNU_SOURCE #include #include #include #include #include #include #include #include #include #include #include "lib/bluetooth.h" #include "lib/hci.h" #ifdef HAVE_VALGRIND_MEMCHECK_H #include #endif #include "src/shared/mainloop.h" #include "src/shared/util.h" #include "src/shared/io.h" #include "src/shared/tester.h" #include "src/shared/log.h" #include "src/shared/timeout.h" #define COLOR_OFF "\x1B[0m" #define COLOR_BLACK "\x1B[0;30m" #define COLOR_RED "\x1B[0;31m" #define COLOR_GREEN "\x1B[0;32m" #define COLOR_YELLOW "\x1B[0;33m" #define COLOR_BLUE "\x1B[0;34m" #define COLOR_MAGENTA "\x1B[0;35m" #define COLOR_CYAN "\x1B[0;36m" #define COLOR_WHITE "\x1B[0;37m" #define COLOR_HIGHLIGHT "\x1B[1;39m" #define print_text(color, fmt, args...) \ tester_log(color fmt COLOR_OFF, ## args) #define print_summary(label, color, value, fmt, args...) \ tester_log("%-52s " color "%-10s" COLOR_OFF fmt, \ label, value, ## args) #define print_progress(name, color, fmt, args...) \ tester_log(COLOR_HIGHLIGHT "%s" COLOR_OFF " - " \ color fmt COLOR_OFF, name, ## args) enum test_result { TEST_RESULT_NOT_RUN, TEST_RESULT_PASSED, TEST_RESULT_FAILED, TEST_RESULT_TIMED_OUT, }; enum test_stage { TEST_STAGE_INVALID, TEST_STAGE_PRE_SETUP, TEST_STAGE_SETUP, TEST_STAGE_RUN, TEST_STAGE_TEARDOWN, TEST_STAGE_POST_TEARDOWN, }; struct test_case { char *name; enum test_result result; enum test_stage stage; const void *test_data; const struct iovec *iov; size_t iovcnt; tester_data_func_t pre_setup_func; tester_data_func_t setup_func; tester_data_func_t test_func; tester_data_func_t teardown_func; tester_data_func_t post_teardown_func; tester_data_func_t io_complete_func; gdouble start_time; gdouble end_time; unsigned int timeout; unsigned int timeout_id; unsigned int teardown_id; tester_destroy_func_t destroy; void *user_data; }; static char *tester_name; static GList *test_list; static GList *test_current; static GTimer *test_timer; static gboolean option_version = FALSE; static gboolean option_quiet = FALSE; static gboolean option_debug = FALSE; static gboolean option_monitor = FALSE; static gboolean option_list = FALSE; static const char *option_prefix = NULL; static const char *option_string = NULL; struct monitor_hdr { uint16_t opcode; uint16_t index; uint16_t len; uint8_t priority; uint8_t ident_len; } __attribute__((packed)); struct monitor_l2cap_hdr { uint16_t cid; uint16_t psm; } __attribute__((packed)); static void test_destroy(gpointer data) { struct test_case *test = data; if (test->timeout_id > 0) timeout_remove(test->timeout_id); if (test->teardown_id > 0) g_source_remove(test->teardown_id); if (test->destroy) test->destroy(test->user_data); free(test->name); free(test); } static void tester_vprintf(const char *format, va_list ap) { if (tester_use_quiet()) return; printf(" %s", COLOR_WHITE); vprintf(format, ap); printf("%s\n", COLOR_OFF); } static void tester_log(const char *format, ...) { va_list ap; va_start(ap, format); vprintf(format, ap); printf("\n"); va_end(ap); va_start(ap, format); bt_log_vprintf(HCI_DEV_NONE, tester_name, LOG_INFO, format, ap); va_end(ap); } void tester_print(const char *format, ...) { va_list ap; va_start(ap, format); tester_vprintf(format, ap); va_end(ap); va_start(ap, format); bt_log_vprintf(HCI_DEV_NONE, tester_name, LOG_INFO, format, ap); va_end(ap); } void tester_debug(const char *format, ...) { va_list ap; va_start(ap, format); tester_vprintf(format, ap); va_end(ap); va_start(ap, format); bt_log_vprintf(HCI_DEV_NONE, tester_name, LOG_DEBUG, format, ap); va_end(ap); } void tester_warn(const char *format, ...) { va_list ap; va_start(ap, format); tester_vprintf(format, ap); va_end(ap); va_start(ap, format); bt_log_vprintf(HCI_DEV_NONE, tester_name, LOG_WARNING, format, ap); va_end(ap); } static void monitor_debug(const char *str, void *user_data) { const char *label = user_data; tester_debug("%s: %s", label, str); } static void monitor_log(char dir, uint16_t cid, uint16_t psm, const void *data, size_t len) { struct iovec iov[3]; struct monitor_l2cap_hdr hdr; uint8_t term = 0x00; char label[16]; if (snprintf(label, sizeof(label), "%c %s", dir, tester_name) < 0) return; hdr.cid = cpu_to_le16(cid); hdr.psm = cpu_to_le16(psm); iov[0].iov_base = &hdr; iov[0].iov_len = sizeof(hdr); iov[1].iov_base = (void *) data; iov[1].iov_len = len; /* Kernel won't forward if data is no NULL terminated */ iov[2].iov_base = &term; iov[2].iov_len = sizeof(term); bt_log_sendmsg(HCI_DEV_NONE, label, LOG_INFO, iov, 3); } void tester_monitor(char dir, uint16_t cid, uint16_t psm, const void *data, size_t len) { monitor_log(dir, cid, psm, data, len); if (!tester_use_debug()) return; util_hexdump(dir, data, len, monitor_debug, (void *) tester_name); } static void default_pre_setup(const void *test_data) { tester_pre_setup_complete(); } static void default_setup(const void *test_data) { tester_setup_complete(); } static void default_teardown(const void *test_data) { tester_teardown_complete(); } static void default_post_teardown(const void *test_data) { tester_post_teardown_complete(); } void tester_add_full(const char *name, const void *test_data, tester_data_func_t pre_setup_func, tester_data_func_t setup_func, tester_data_func_t test_func, tester_data_func_t teardown_func, tester_data_func_t post_teardown_func, unsigned int timeout, void *user_data, tester_destroy_func_t destroy) { struct test_case *test; if (!test_func) return; if (option_prefix && !g_str_has_prefix(name, option_prefix)) { if (destroy) destroy(user_data); return; } if (option_string && !strstr(name, option_string)) { if (destroy) destroy(user_data); return; } if (option_list) { tester_log("%s", name); if (destroy) destroy(user_data); return; } test = new0(struct test_case, 1); test->name = strdup(name); test->result = TEST_RESULT_NOT_RUN; test->stage = TEST_STAGE_INVALID; test->test_data = test_data; if (pre_setup_func) test->pre_setup_func = pre_setup_func; else test->pre_setup_func = default_pre_setup; if (setup_func) test->setup_func = setup_func; else test->setup_func = default_setup; test->test_func = test_func; if (teardown_func) test->teardown_func = teardown_func; else test->teardown_func = default_teardown; if (post_teardown_func) test->post_teardown_func = post_teardown_func; else test->post_teardown_func = default_post_teardown; test->timeout = timeout; test->destroy = destroy; test->user_data = user_data; test_list = g_list_append(test_list, test); } void tester_add(const char *name, const void *test_data, tester_data_func_t setup_func, tester_data_func_t test_func, tester_data_func_t teardown_func) { tester_add_full(name, test_data, NULL, setup_func, test_func, teardown_func, NULL, 0, NULL, NULL); } static struct test_case *tester_get_test(void) { if (!test_current) return NULL; return test_current->data; } void *tester_get_data(void) { struct test_case *test = tester_get_test(); if (!test) return NULL; return test->user_data; } static int tester_summarize(void) { unsigned int not_run = 0, passed = 0, failed = 0; gdouble execution_time; GList *list; tester_log(""); print_text(COLOR_HIGHLIGHT, ""); print_text(COLOR_HIGHLIGHT, "Test Summary"); print_text(COLOR_HIGHLIGHT, "------------"); for (list = g_list_first(test_list); list; list = g_list_next(list)) { struct test_case *test = list->data; gdouble exec_time; exec_time = test->end_time - test->start_time; switch (test->result) { case TEST_RESULT_NOT_RUN: print_summary(test->name, COLOR_YELLOW, "Not Run", ""); not_run++; break; case TEST_RESULT_PASSED: print_summary(test->name, COLOR_GREEN, "Passed", "%8.3f seconds", exec_time); passed++; break; case TEST_RESULT_FAILED: print_summary(test->name, COLOR_RED, "Failed", "%8.3f seconds", exec_time); failed++; break; case TEST_RESULT_TIMED_OUT: print_summary(test->name, COLOR_RED, "Timed out", "%8.3f seconds", exec_time); failed++; break; } } tester_log("Total: %d, " COLOR_GREEN "Passed: %d (%.1f%%)" COLOR_OFF ", " COLOR_RED "Failed: %d" COLOR_OFF ", " COLOR_YELLOW "Not Run: %d" COLOR_OFF, not_run + passed + failed, passed, (not_run + passed + failed) ? (float) passed * 100 / (not_run + passed + failed) : 0, failed, not_run); execution_time = g_timer_elapsed(test_timer, NULL); tester_log("Overall execution time: %.3g seconds", execution_time); return failed; } static gboolean teardown_callback(gpointer user_data) { struct test_case *test = user_data; test->teardown_id = 0; test->stage = TEST_STAGE_TEARDOWN; print_progress(test->name, COLOR_MAGENTA, "teardown"); test->teardown_func(test->test_data); #ifdef HAVE_VALGRIND_MEMCHECK_H VALGRIND_DO_ADDED_LEAK_CHECK; #endif return FALSE; } static bool test_timeout(gpointer user_data) { struct test_case *test = user_data; test->timeout_id = 0; if (!test_current) return FALSE; test->result = TEST_RESULT_TIMED_OUT; print_progress(test->name, COLOR_RED, "test timed out"); g_idle_add(teardown_callback, test); return FALSE; } static void next_test_case(void) { struct test_case *test; if (test_current) test_current = g_list_next(test_current); else test_current = test_list; if (!test_current) { g_timer_stop(test_timer); mainloop_quit(); return; } test = test_current->data; tester_log(""); print_progress(test->name, COLOR_BLACK, "init"); test->start_time = g_timer_elapsed(test_timer, NULL); if (test->timeout > 0) test->timeout_id = timeout_add_seconds(test->timeout, test_timeout, test, NULL); test->stage = TEST_STAGE_PRE_SETUP; test->pre_setup_func(test->test_data); } static gboolean setup_callback(gpointer user_data) { struct test_case *test = user_data; test->stage = TEST_STAGE_SETUP; print_progress(test->name, COLOR_BLUE, "setup"); test->setup_func(test->test_data); return FALSE; } static gboolean run_callback(gpointer user_data) { struct test_case *test = user_data; test->stage = TEST_STAGE_RUN; print_progress(test->name, COLOR_BLACK, "run"); test->test_func(test->test_data); return FALSE; } static gboolean done_callback(gpointer user_data) { struct test_case *test = user_data; test->end_time = g_timer_elapsed(test_timer, NULL); print_progress(test->name, COLOR_BLACK, "done"); next_test_case(); return FALSE; } void tester_pre_setup_complete(void) { struct test_case *test; if (!test_current) return; test = test_current->data; if (test->stage != TEST_STAGE_PRE_SETUP) return; g_idle_add(setup_callback, test); } void tester_pre_setup_failed(void) { struct test_case *test; if (!test_current) return; test = test_current->data; if (test->stage != TEST_STAGE_PRE_SETUP) return; if (test->timeout_id > 0) { timeout_remove(test->timeout_id); test->timeout_id = 0; } print_progress(test->name, COLOR_RED, "pre setup failed"); g_idle_add(done_callback, test); } void tester_pre_setup_abort(void) { struct test_case *test; if (!test_current) return; test = test_current->data; if (test->stage != TEST_STAGE_PRE_SETUP) return; if (test->timeout_id > 0) { timeout_remove(test->timeout_id); test->timeout_id = 0; } print_progress(test->name, COLOR_YELLOW, "not run"); g_idle_add(done_callback, test); } bool tester_pre_setup_skip_by_default(void) { if (!option_prefix && !option_string) { tester_pre_setup_abort(); return true; } return false; } void tester_setup_complete(void) { struct test_case *test; if (!test_current) return; test = test_current->data; if (test->stage != TEST_STAGE_SETUP) return; print_progress(test->name, COLOR_BLUE, "setup complete"); g_idle_add(run_callback, test); } void tester_setup_failed(void) { struct test_case *test; if (!test_current) return; test = test_current->data; if (test->stage != TEST_STAGE_SETUP) return; test->stage = TEST_STAGE_POST_TEARDOWN; if (test->timeout_id > 0) { timeout_remove(test->timeout_id); test->timeout_id = 0; } print_progress(test->name, COLOR_RED, "setup failed"); print_progress(test->name, COLOR_MAGENTA, "teardown"); test->post_teardown_func(test->test_data); } static void test_result(enum test_result result) { struct test_case *test; if (!test_current) return; test = test_current->data; if (test->stage != TEST_STAGE_RUN) return; if (test->timeout_id > 0) { timeout_remove(test->timeout_id); test->timeout_id = 0; } if (test->result == TEST_RESULT_FAILED) result = TEST_RESULT_FAILED; test->result = result; switch (result) { case TEST_RESULT_PASSED: print_progress(test->name, COLOR_GREEN, "test passed"); break; case TEST_RESULT_FAILED: print_progress(test->name, COLOR_RED, "test failed"); break; case TEST_RESULT_NOT_RUN: print_progress(test->name, COLOR_YELLOW, "test not run"); break; case TEST_RESULT_TIMED_OUT: print_progress(test->name, COLOR_RED, "test timed out"); break; } if (test->teardown_id > 0) return; test->teardown_id = g_idle_add(teardown_callback, test); } void tester_test_passed(void) { test_result(TEST_RESULT_PASSED); } void tester_test_failed(void) { test_result(TEST_RESULT_FAILED); } void tester_test_abort(void) { test_result(TEST_RESULT_NOT_RUN); } void tester_teardown_complete(void) { struct test_case *test; if (!test_current) return; test = test_current->data; if (test->stage != TEST_STAGE_TEARDOWN) return; test->stage = TEST_STAGE_POST_TEARDOWN; test->post_teardown_func(test->test_data); } void tester_teardown_failed(void) { struct test_case *test; if (!test_current) return; test = test_current->data; if (test->stage != TEST_STAGE_TEARDOWN) return; test->stage = TEST_STAGE_POST_TEARDOWN; tester_post_teardown_failed(); } void tester_post_teardown_complete(void) { struct test_case *test; if (!test_current) return; test = test_current->data; if (test->stage != TEST_STAGE_POST_TEARDOWN) return; print_progress(test->name, COLOR_MAGENTA, "teardown complete"); g_idle_add(done_callback, test); } void tester_post_teardown_failed(void) { struct test_case *test; if (!test_current) return; test = test_current->data; if (test->stage != TEST_STAGE_POST_TEARDOWN) return; print_progress(test->name, COLOR_RED, "teardown failed"); g_idle_add(done_callback, test); } static gboolean start_tester(gpointer user_data) { test_timer = g_timer_new(); next_test_case(); return FALSE; } struct wait_data { unsigned int seconds; struct test_case *test; tester_wait_func_t func; void *user_data; }; static gboolean wait_callback(gpointer user_data) { struct wait_data *wait = user_data; struct test_case *test = wait->test; wait->seconds--; if (wait->seconds > 0) { print_progress(test->name, COLOR_BLACK, "%u seconds left", wait->seconds); return TRUE; } print_progress(test->name, COLOR_BLACK, "waiting done"); wait->func(wait->user_data); free(wait); return FALSE; } void tester_wait(unsigned int seconds, tester_wait_func_t func, void *user_data) { struct test_case *test; struct wait_data *wait; if (!func || seconds < 1) return; if (!test_current) return; test = test_current->data; wait = new0(struct wait_data, 1); wait->seconds = seconds; wait->test = test; wait->func = func; wait->user_data = user_data; g_timeout_add(1000, wait_callback, wait); print_progress(test->name, COLOR_BLACK, "waiting %u seconds", seconds); } static void signal_callback(int signum, void *user_data) { static bool terminated = false; switch (signum) { case SIGINT: case SIGTERM: if (!terminated) mainloop_quit(); terminated = true; break; } } bool tester_use_quiet(void) { return option_quiet == TRUE ? true : false; } bool tester_use_debug(void) { return option_debug == TRUE ? true : false; } static GOptionEntry options[] = { { "version", 'v', 0, G_OPTION_ARG_NONE, &option_version, "Show version information and exit" }, { "quiet", 'q', 0, G_OPTION_ARG_NONE, &option_quiet, "Run tests without logging" }, { "debug", 'd', 0, G_OPTION_ARG_NONE, &option_debug, "Run tests with debug output" }, { "monitor", 'm', 0, G_OPTION_ARG_NONE, &option_monitor, "Enable monitor output" }, { "list", 'l', 0, G_OPTION_ARG_NONE, &option_list, "Only list the tests to be run" }, { "prefix", 'p', 0, G_OPTION_ARG_STRING, &option_prefix, "Run tests matching provided prefix" }, { "string", 's', 0, G_OPTION_ARG_STRING, &option_string, "Run tests matching provided string" }, { NULL }, }; void tester_init(int *argc, char ***argv) { GOptionContext *context; GError *error = NULL; context = g_option_context_new(NULL); g_option_context_add_main_entries(context, options, NULL); if (g_option_context_parse(context, argc, argv, &error) == FALSE) { if (error != NULL) { g_printerr("%s\n", error->message); g_error_free(error); } else g_printerr("An unknown error occurred\n"); exit(1); } g_option_context_free(context); if (option_version == TRUE) { g_print("%s\n", VERSION); exit(EXIT_SUCCESS); } mainloop_init(); tester_name = strrchr(*argv[0], '/'); if (!tester_name) tester_name = strdup(*argv[0]); else tester_name = strdup(++tester_name); test_list = NULL; test_current = NULL; } static struct io *ios[2]; static bool io_disconnected(struct io *io, void *user_data) { if (io == ios[0]) { io_destroy(ios[0]); ios[0] = NULL; } else if (io == ios[1]) { io_destroy(ios[1]); ios[1] = NULL; } return false; } static const struct iovec *test_get_iov(struct test_case *test) { const struct iovec *iov; if (!test || !test->iov || !test->iovcnt) return NULL; iov = test->iov; test->iov++; test->iovcnt--; return iov; } static bool test_io_send(struct io *io, void *user_data) { struct test_case *test = tester_get_test(); const struct iovec *iov = test_get_iov(test); ssize_t len; if (!iov) return false; len = io_send(io, iov, 1); tester_monitor('<', 0x0004, 0x0000, iov->iov_base, len); g_assert_cmpint(len, ==, iov->iov_len); if (!test->iovcnt && test->io_complete_func) { test->io_complete_func(test->test_data); } else if (test->iovcnt && !test->iov->iov_base) { test_get_iov(test); return test_io_send(io, user_data); } return false; } static bool test_io_recv(struct io *io, void *user_data) { struct test_case *test = tester_get_test(); const struct iovec *iov = test_get_iov(test); unsigned char buf[512]; int fd; ssize_t len; fd = io_get_fd(io); len = read(fd, buf, sizeof(buf)); g_assert(len > 0); tester_monitor('>', 0x0004, 0x0000, buf, len); if (!iov) return true; g_assert_cmpint(len, ==, iov->iov_len); if (memcmp(buf, iov->iov_base, len)) tester_monitor('!', 0x0004, 0x0000, iov->iov_base, len); g_assert(memcmp(buf, iov->iov_base, len) == 0); if (test->iovcnt) io_set_write_handler(io, test_io_send, NULL, NULL); else if (test->io_complete_func) test->io_complete_func(test->test_data); return true; } static void setup_io(void) { int fd[2], err; io_destroy(ios[0]); io_destroy(ios[1]); err = socketpair(AF_UNIX, SOCK_SEQPACKET | SOCK_CLOEXEC, 0, fd); if (err < 0) { tester_warn("socketpair: %s (%d)", strerror(errno), errno); return; } ios[0] = io_new(fd[0]); if (!ios[0]) { tester_warn("io_new: %p", ios[0]); return; } io_set_close_on_destroy(ios[0], true); io_set_disconnect_handler(ios[0], io_disconnected, NULL, NULL); ios[1] = io_new(fd[1]); if (!ios[1]) { tester_warn("io_new: %p", ios[1]); return; } io_set_close_on_destroy(ios[1], true); io_set_disconnect_handler(ios[1], io_disconnected, NULL, NULL); io_set_read_handler(ios[1], test_io_recv, NULL, NULL); } struct io *tester_setup_io(const struct iovec *iov, int iovcnt) { struct test_case *test = tester_get_test(); if (!ios[0] || !ios[1]) { setup_io(); if (!ios[0] || !ios[1]) { tester_warn("Unable to setup IO"); return NULL; } } test->iov = iov; test->iovcnt = iovcnt; return ios[0]; } void tester_io_send(void) { struct test_case *test = tester_get_test(); if (test->iovcnt) io_set_write_handler(ios[1], test_io_send, NULL, NULL); } void tester_io_set_complete_func(tester_data_func_t func) { struct test_case *test = tester_get_test(); test->io_complete_func = func; } int tester_run(void) { int ret; if (option_list) { mainloop_quit(); return EXIT_SUCCESS; } g_idle_add(start_tester, NULL); mainloop_run_with_signal(signal_callback, NULL); ret = tester_summarize(); g_list_free_full(test_list, test_destroy); if (option_monitor) bt_log_close(); io_destroy(ios[0]); io_destroy(ios[1]); return ret == 0 ? EXIT_SUCCESS : EXIT_FAILURE; } bluez-5.82/src/shared/PaxHeaders/gatt-helpers.c0000644000000000000000000000005014766002272016454 xustar0020 atime=1743515578 20 ctime=1743591277 bluez-5.82/src/shared/gatt-helpers.c0000644000000000000000000010063714766002272016144 0ustar00rootroot// SPDX-License-Identifier: LGPL-2.1-or-later /* * * BlueZ - Bluetooth protocol stack for Linux * * Copyright (C) 2014 Google Inc. * * */ #ifdef HAVE_CONFIG_H #include #endif #include "src/shared/queue.h" #include "src/shared/att.h" #include "lib/bluetooth.h" #include "lib/uuid.h" #include "src/shared/gatt-helpers.h" #include "src/shared/util.h" #ifndef MIN #define MIN(a, b) ((a) < (b) ? (a) : (b)) #endif struct bt_gatt_result { uint8_t opcode; void *pdu; uint16_t pdu_len; uint16_t data_len; void *op; /* Discovery operation data */ struct bt_gatt_result *next; }; static struct bt_gatt_result *result_create(uint8_t opcode, const void *pdu, uint16_t pdu_len, uint16_t data_len, void *op) { struct bt_gatt_result *result; result = new0(struct bt_gatt_result, 1); result->pdu = malloc(pdu_len); if (!result->pdu) { free(result); return NULL; } result->opcode = opcode; result->pdu_len = pdu_len; result->data_len = data_len; result->op = op; memcpy(result->pdu, pdu, pdu_len); return result; } static void result_destroy(struct bt_gatt_result *result) { struct bt_gatt_result *next; while (result) { next = result->next; free(result->pdu); free(result); result = next; } } static unsigned int result_element_count(struct bt_gatt_result *result) { unsigned int count = 0; struct bt_gatt_result *cur; cur = result; while (cur) { count += cur->pdu_len / cur->data_len; cur = cur->next; } return count; } unsigned int bt_gatt_result_service_count(struct bt_gatt_result *result) { if (!result) return 0; if (result->opcode != BT_ATT_OP_READ_BY_GRP_TYPE_RSP && result->opcode != BT_ATT_OP_FIND_BY_TYPE_RSP) return 0; return result_element_count(result); } unsigned int bt_gatt_result_characteristic_count(struct bt_gatt_result *result) { if (!result) return 0; if (result->opcode != BT_ATT_OP_READ_BY_TYPE_RSP) return 0; /* * Data length contains 7 or 21 octets: * 2 octets: Attribute handle * 1 octet: Characteristic properties * 2 octets: Characteristic value handle * 2 or 16 octets: characteristic UUID */ if (result->data_len != 21 && result->data_len != 7) return 0; return result_element_count(result); } unsigned int bt_gatt_result_descriptor_count(struct bt_gatt_result *result) { if (!result) return 0; if (result->opcode != BT_ATT_OP_FIND_INFO_RSP) return 0; return result_element_count(result); } unsigned int bt_gatt_result_included_count(struct bt_gatt_result *result) { struct bt_gatt_result *cur; unsigned int count = 0; if (!result) return 0; if (result->opcode != BT_ATT_OP_READ_BY_TYPE_RSP) return 0; /* * Data length can be of length 6 or 8 octets: * 2 octets - include service handle * 2 octets - start handle of included service * 2 octets - end handle of included service * 2 octets (optionally) - 16 bit Bluetooth UUID */ if (result->data_len != 6 && result->data_len != 8) return 0; for (cur = result; cur; cur = cur->next) if (cur->opcode == BT_ATT_OP_READ_BY_TYPE_RSP) count += cur->pdu_len / cur->data_len; return count; } bool bt_gatt_iter_init(struct bt_gatt_iter *iter, struct bt_gatt_result *result) { if (!iter || !result) return false; iter->result = result; iter->pos = 0; return true; } static const uint8_t bt_base_uuid[16] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x80, 0x00, 0x00, 0x80, 0x5F, 0x9B, 0x34, 0xFB }; static bool convert_uuid_le(const uint8_t *src, size_t len, uint8_t dst[16]) { if (len == 16) { bswap_128(src, dst); return true; } if (len != 2) return false; memcpy(dst, bt_base_uuid, sizeof(bt_base_uuid)); dst[2] = src[1]; dst[3] = src[0]; return true; } struct bt_gatt_request { struct bt_att *att; unsigned int id; uint16_t start_handle; uint16_t end_handle; int ref_count; bt_uuid_t uuid; uint16_t service_type; struct bt_gatt_result *result_head; struct bt_gatt_result *result_tail; bt_gatt_request_callback_t callback; void *user_data; bt_gatt_destroy_func_t destroy; }; static struct bt_gatt_result *result_append(uint8_t opcode, const void *pdu, uint16_t pdu_len, uint16_t data_len, struct bt_gatt_request *op) { struct bt_gatt_result *result; result = result_create(opcode, pdu, pdu_len, data_len, op); if (!result) return NULL; if (!op->result_head) op->result_head = op->result_tail = result; else { op->result_tail->next = result; op->result_tail = result; } return result; } bool bt_gatt_iter_next_included_service(struct bt_gatt_iter *iter, uint16_t *handle, uint16_t *start_handle, uint16_t *end_handle, uint8_t uuid[16]) { struct bt_gatt_result *read_result; struct bt_gatt_request *op; const void *pdu_ptr; int i = 0; if (!iter || !iter->result || !handle || !start_handle || !end_handle || !uuid) return false; if (iter->result->opcode != BT_ATT_OP_READ_BY_TYPE_RSP) return false; /* UUID in discovery_op is set in read_by_type and service_discovery */ op = iter->result->op; if (op->uuid.type != BT_UUID_UNSPEC) return false; /* * iter->result points to READ_BY_TYPE_RSP with data length containing: * 2 octets - include service handle * 2 octets - start handle of included service * 2 octets - end handle of included service * optional 2 octets - Bluetooth UUID */ if (iter->result->data_len != 8 && iter->result->data_len != 6) return false; pdu_ptr = iter->result->pdu + iter->pos; /* This result contains 16 bit UUID */ if (iter->result->data_len == 8) { *handle = get_le16(pdu_ptr); *start_handle = get_le16(pdu_ptr + 2); *end_handle = get_le16(pdu_ptr + 4); convert_uuid_le(pdu_ptr + 6, 2, uuid); iter->pos += iter->result->data_len; if (iter->pos == iter->result->pdu_len) { iter->result = iter->result->next; iter->pos = 0; } return true; } *handle = get_le16(pdu_ptr); *start_handle = get_le16(pdu_ptr + 2); *end_handle = get_le16(pdu_ptr + 4); read_result = iter->result; /* * Find READ_RSP with include service UUID. * If number of current data set in READ_BY_TYPE_RSP is n, then we must * go to n'th PDU next to current item->result */ for (read_result = read_result->next; read_result; i++) { if (i >= (iter->pos / iter->result->data_len)) break; read_result = read_result->next; } if (!read_result) return false; convert_uuid_le(read_result->pdu, read_result->data_len, uuid); iter->pos += iter->result->data_len; if (iter->pos == iter->result->pdu_len) { iter->result = read_result->next; iter->pos = 0; } return true; } bool bt_gatt_iter_next_service(struct bt_gatt_iter *iter, uint16_t *start_handle, uint16_t *end_handle, uint8_t uuid[16]) { struct bt_gatt_request *op; const void *pdu_ptr; bt_uuid_t tmp; if (!iter || !iter->result || !start_handle || !end_handle || !uuid) return false; op = iter->result->op; pdu_ptr = iter->result->pdu + iter->pos; switch (iter->result->opcode) { case BT_ATT_OP_READ_BY_GRP_TYPE_RSP: *start_handle = get_le16(pdu_ptr); *end_handle = get_le16(pdu_ptr + 2); convert_uuid_le(pdu_ptr + 4, iter->result->data_len - 4, uuid); break; case BT_ATT_OP_FIND_BY_TYPE_RSP: *start_handle = get_le16(pdu_ptr); *end_handle = get_le16(pdu_ptr + 2); bt_uuid_to_uuid128(&op->uuid, &tmp); memcpy(uuid, tmp.value.u128.data, 16); break; default: return false; } iter->pos += iter->result->data_len; if (iter->pos == iter->result->pdu_len) { iter->result = iter->result->next; iter->pos = 0; } return true; } bool bt_gatt_iter_next_characteristic(struct bt_gatt_iter *iter, uint16_t *start_handle, uint16_t *end_handle, uint16_t *value_handle, uint8_t *properties, uint8_t uuid[16]) { struct bt_gatt_request *op; const void *pdu_ptr; if (!iter || !iter->result || !start_handle || !end_handle || !value_handle || !properties || !uuid) return false; if (iter->result->opcode != BT_ATT_OP_READ_BY_TYPE_RSP) return false; /* UUID in discovery_op is set in read_by_type and service_discovery */ op = iter->result->op; if (op->uuid.type != BT_UUID_UNSPEC) return false; /* * Data length contains 7 or 21 octets: * 2 octets: Attribute handle * 1 octet: Characteristic properties * 2 octets: Characteristic value handle * 2 or 16 octets: characteristic UUID */ if (iter->result->data_len != 21 && iter->result->data_len != 7) return false; pdu_ptr = iter->result->pdu + iter->pos; *start_handle = get_le16(pdu_ptr); *properties = ((uint8_t *) pdu_ptr)[2]; *value_handle = get_le16(pdu_ptr + 3); convert_uuid_le(pdu_ptr + 5, iter->result->data_len - 5, uuid); iter->pos += iter->result->data_len; if (iter->pos == iter->result->pdu_len) { iter->result = iter->result->next; iter->pos = 0; } if (!iter->result) { *end_handle = op->end_handle; return true; } *end_handle = get_le16(iter->result->pdu + iter->pos) - 1; return true; } bool bt_gatt_iter_next_descriptor(struct bt_gatt_iter *iter, uint16_t *handle, uint8_t uuid[16]) { const void *pdu_ptr; if (!iter || !iter->result || !handle || !uuid) return false; if (iter->result->opcode != BT_ATT_OP_FIND_INFO_RSP) return false; pdu_ptr = iter->result->pdu + iter->pos; *handle = get_le16(pdu_ptr); convert_uuid_le(pdu_ptr + 2, iter->result->data_len - 2, uuid); iter->pos += iter->result->data_len; if (iter->pos == iter->result->pdu_len) { iter->result = iter->result->next; iter->pos = 0; } return true; } bool bt_gatt_iter_next_read_by_type(struct bt_gatt_iter *iter, uint16_t *handle, uint16_t *length, const uint8_t **value) { struct bt_gatt_request *op; const void *pdu_ptr; if (!iter || !iter->result || !handle || !length || !value) return false; if (iter->result->opcode != BT_ATT_OP_READ_BY_TYPE_RSP) return false; /* * Check if UUID is set, otherwise results can contain characteristic * discovery service or included service discovery results */ op = iter->result->op; if (op->uuid.type == BT_UUID_UNSPEC) return false; pdu_ptr = iter->result->pdu + iter->pos; *handle = get_le16(pdu_ptr); *length = iter->result->data_len - 2; *value = pdu_ptr + 2; iter->pos += iter->result->data_len; if (iter->pos == iter->result->pdu_len) { iter->result = iter->result->next; iter->pos = 0; } return true; } struct mtu_op { struct bt_att *att; uint16_t client_rx_mtu; bt_gatt_result_callback_t callback; void *user_data; bt_gatt_destroy_func_t destroy; }; static void destroy_mtu_op(void *user_data) { struct mtu_op *op = user_data; if (op->destroy) op->destroy(op->user_data); free(op); } static uint8_t process_error(const void *pdu, uint16_t length) { const struct bt_att_pdu_error_rsp *error_pdu; if (!pdu || length != sizeof(struct bt_att_pdu_error_rsp)) return 0; error_pdu = pdu; return error_pdu->ecode; } static void mtu_cb(uint8_t opcode, const void *pdu, uint16_t length, void *user_data) { struct mtu_op *op = user_data; bool success = true; uint8_t att_ecode = 0; uint16_t server_rx_mtu; if (opcode == BT_ATT_OP_ERROR_RSP) { success = false; att_ecode = process_error(pdu, length); goto done; } if (opcode != BT_ATT_OP_MTU_RSP || !pdu || length != 2) { success = false; goto done; } server_rx_mtu = get_le16(pdu); bt_att_set_mtu(op->att, MIN(op->client_rx_mtu, server_rx_mtu)); done: if (op->callback) op->callback(success, att_ecode, op->user_data); } unsigned int bt_gatt_exchange_mtu(struct bt_att *att, uint16_t client_rx_mtu, bt_gatt_result_callback_t callback, void *user_data, bt_gatt_destroy_func_t destroy) { struct mtu_op *op; uint8_t pdu[2]; unsigned int id; if (!att || !client_rx_mtu) return false; op = new0(struct mtu_op, 1); op->att = att; op->client_rx_mtu = client_rx_mtu; op->callback = callback; op->user_data = user_data; op->destroy = destroy; put_le16(client_rx_mtu, pdu); id = bt_att_send(att, BT_ATT_OP_MTU_REQ, pdu, sizeof(pdu), mtu_cb, op, destroy_mtu_op); if (!id) free(op); return id; } static inline int get_uuid_len(const bt_uuid_t *uuid) { if (!uuid) return 0; return (uuid->type == BT_UUID16) ? 2 : 16; } struct bt_gatt_request *bt_gatt_request_ref(struct bt_gatt_request *req) { if (!req) return NULL; __sync_fetch_and_add(&req->ref_count, 1); return req; } void bt_gatt_request_unref(struct bt_gatt_request *req) { if (!req) return; if (__sync_sub_and_fetch(&req->ref_count, 1)) return; bt_gatt_request_cancel(req); if (req->destroy) req->destroy(req->user_data); result_destroy(req->result_head); free(req); } void bt_gatt_request_cancel(struct bt_gatt_request *req) { if (!req) return; if (!req->id) return; bt_att_cancel(req->att, req->id); req->id = 0; } static void async_req_unref(void *data) { struct bt_gatt_request *req = data; bt_gatt_request_unref(req); } static void discovery_op_complete(struct bt_gatt_request *op, bool success, uint8_t ecode) { /* Reset success if there is some result to report */ if (ecode == BT_ATT_ERROR_ATTRIBUTE_NOT_FOUND && op->result_head) success = true; if (op->callback) op->callback(success, ecode, success ? op->result_head : NULL, op->user_data); if (!op->id) async_req_unref(op); else op->id = 0; } static void read_by_grp_type_cb(uint8_t opcode, const void *pdu, uint16_t length, void *user_data) { struct bt_gatt_request *op = user_data; bool success; uint8_t att_ecode = 0; struct bt_gatt_result *cur_result; size_t data_length; size_t list_length; uint16_t last_end; if (opcode == BT_ATT_OP_ERROR_RSP) { success = false; att_ecode = process_error(pdu, length); goto done; } /* PDU must contain at least the following (sans opcode): * - Attr Data Length (1 octet) * - Attr Data List (at least 6 octets): * -- 2 octets: Attribute handle * -- 2 octets: End group handle * -- 2 or 16 octets: service UUID */ if (opcode != BT_ATT_OP_READ_BY_GRP_TYPE_RSP || !pdu || length < 7) { success = false; goto done; } data_length = ((uint8_t *) pdu)[0]; list_length = length - 1; if ((data_length != 6 && data_length != 20) || (list_length % data_length)) { success = false; goto done; } /* PDU is correctly formatted. Get the last end handle to process the * next request and store the PDU. */ cur_result = result_append(opcode, pdu + 1, list_length, data_length, op); if (!cur_result) { success = false; goto done; } last_end = get_le16(pdu + length - data_length + 2); /* * If last handle is lower from previous start handle then it is smth * wrong. Let's stop search, otherwise we might enter infinite loop. */ if (last_end < op->start_handle) { success = false; goto done; } op->start_handle = last_end + 1; if (last_end < op->end_handle) { uint8_t pdu[6]; put_le16(op->start_handle, pdu); put_le16(op->end_handle, pdu + 2); put_le16(op->service_type, pdu + 4); op->id = bt_att_send(op->att, BT_ATT_OP_READ_BY_GRP_TYPE_REQ, pdu, sizeof(pdu), read_by_grp_type_cb, bt_gatt_request_ref(op), async_req_unref); if (op->id) return; success = false; goto done; } /* Some devices incorrectly return 0xffff as the end group handle when * the read-by-group-type request is performed within a smaller range. * Manually set the end group handle that we report in the result to the * end handle in the original request. */ if (last_end == 0xffff && last_end != op->end_handle) put_le16(op->end_handle, cur_result->pdu + length - data_length + 1); success = true; done: discovery_op_complete(op, success, att_ecode); } static void find_by_type_val_cb(uint8_t opcode, const void *pdu, uint16_t length, void *user_data) { struct bt_gatt_request *op = user_data; bool success; uint8_t att_ecode = 0; uint16_t last_end; if (opcode == BT_ATT_OP_ERROR_RSP) { success = false; att_ecode = process_error(pdu, length); goto done; } /* PDU must contain 4 bytes and it must be a multiple of 4, where each * 4 bytes contain the 16-bit attribute and group end handles. */ if (opcode != BT_ATT_OP_FIND_BY_TYPE_RSP || !pdu || !length || length % 4) { success = false; goto done; } if (!result_append(opcode, pdu, length, 4, op)) { success = false; goto done; } /* * Each data set contains: * 2 octets with start handle * 2 octets with end handle * last_end is end handle of last data set */ last_end = get_le16(pdu + length - 2); /* * If last handle is lower from previous start handle then it is smth * wrong. Let's stop search, otherwise we might enter infinite loop. */ if (last_end < op->start_handle) { success = false; goto done; } op->start_handle = last_end + 1; if (last_end < op->end_handle) { uint8_t pdu[6 + get_uuid_len(&op->uuid)]; put_le16(op->start_handle, pdu); put_le16(op->end_handle, pdu + 2); put_le16(op->service_type, pdu + 4); bt_uuid_to_le(&op->uuid, pdu + 6); op->id = bt_att_send(op->att, BT_ATT_OP_FIND_BY_TYPE_REQ, pdu, sizeof(pdu), find_by_type_val_cb, bt_gatt_request_ref(op), async_req_unref); if (op->id) return; success = false; goto done; } success = true; done: discovery_op_complete(op, success, att_ecode); } static struct bt_gatt_request *discover_services(struct bt_att *att, bt_uuid_t *uuid, uint16_t start, uint16_t end, bt_gatt_request_callback_t callback, void *user_data, bt_gatt_destroy_func_t destroy, bool primary) { struct bt_gatt_request *op; if (!att) return NULL; op = new0(struct bt_gatt_request, 1); op->att = att; op->start_handle = start; op->end_handle = end; op->callback = callback; op->user_data = user_data; op->destroy = destroy; /* set service uuid to primary or secondary */ op->service_type = primary ? GATT_PRIM_SVC_UUID : GATT_SND_SVC_UUID; /* If UUID is NULL, then discover all primary services */ if (!uuid) { uint8_t pdu[6]; put_le16(start, pdu); put_le16(end, pdu + 2); put_le16(op->service_type, pdu + 4); op->id = bt_att_send(att, BT_ATT_OP_READ_BY_GRP_TYPE_REQ, pdu, sizeof(pdu), read_by_grp_type_cb, bt_gatt_request_ref(op), async_req_unref); } else { uint8_t pdu[6 + get_uuid_len(uuid)]; if (uuid->type == BT_UUID_UNSPEC) { free(op); return NULL; } /* Discover by UUID */ op->uuid = *uuid; put_le16(start, pdu); put_le16(end, pdu + 2); put_le16(op->service_type, pdu + 4); bt_uuid_to_le(&op->uuid, pdu + 6); op->id = bt_att_send(att, BT_ATT_OP_FIND_BY_TYPE_REQ, pdu, sizeof(pdu), find_by_type_val_cb, bt_gatt_request_ref(op), async_req_unref); } if (!op->id) { free(op); return NULL; } return bt_gatt_request_ref(op); } struct bt_gatt_request *bt_gatt_discover_all_primary_services( struct bt_att *att, bt_uuid_t *uuid, bt_gatt_request_callback_t callback, void *user_data, bt_gatt_destroy_func_t destroy) { return bt_gatt_discover_primary_services(att, uuid, 0x0001, 0xffff, callback, user_data, destroy); } struct bt_gatt_request *bt_gatt_discover_primary_services( struct bt_att *att, bt_uuid_t *uuid, uint16_t start, uint16_t end, bt_gatt_request_callback_t callback, void *user_data, bt_gatt_destroy_func_t destroy) { return discover_services(att, uuid, start, end, callback, user_data, destroy, true); } struct bt_gatt_request *bt_gatt_discover_secondary_services( struct bt_att *att, bt_uuid_t *uuid, uint16_t start, uint16_t end, bt_gatt_request_callback_t callback, void *user_data, bt_gatt_destroy_func_t destroy) { return discover_services(att, uuid, start, end, callback, user_data, destroy, false); } struct read_incl_data { struct bt_gatt_request *op; struct bt_gatt_result *result; int pos; int ref_count; }; static struct read_incl_data *new_read_included(struct bt_gatt_result *res) { struct read_incl_data *data; data = new0(struct read_incl_data, 1); data->op = bt_gatt_request_ref(res->op); data->result = res; return data; }; static struct read_incl_data *read_included_ref(struct read_incl_data *data) { __sync_fetch_and_add(&data->ref_count, 1); return data; } static void read_included_unref(void *data) { struct read_incl_data *read_data = data; if (__sync_sub_and_fetch(&read_data->ref_count, 1)) return; async_req_unref(read_data->op); free(read_data); } static void discover_included_cb(uint8_t opcode, const void *pdu, uint16_t length, void *user_data); static void read_included_cb(uint8_t opcode, const void *pdu, uint16_t length, void *user_data) { struct read_incl_data *data = user_data; struct bt_gatt_request *op = data->op; uint8_t att_ecode = 0; uint8_t read_pdu[2]; bool success; if (opcode == BT_ATT_OP_ERROR_RSP) { success = false; att_ecode = process_error(pdu, length); goto done; } if (opcode != BT_ATT_OP_READ_RSP || (!pdu && length)) { success = false; goto done; } /* * UUID should be in 128 bit format, as it couldn't be read in * READ_BY_TYPE request */ if (length != 16) { success = false; goto done; } if (!result_append(opcode, pdu, length, length, op)) { success = false; goto done; } if (data->pos == data->result->pdu_len) { uint16_t last_handle; uint8_t pdu[6]; last_handle = get_le16(data->result->pdu + data->pos - data->result->data_len); if (last_handle == op->end_handle) { success = true; goto done; } put_le16(last_handle + 1, pdu); put_le16(op->end_handle, pdu + 2); put_le16(GATT_INCLUDE_UUID, pdu + 4); op->id = bt_att_send(op->att, BT_ATT_OP_READ_BY_TYPE_REQ, pdu, sizeof(pdu), discover_included_cb, bt_gatt_request_ref(op), async_req_unref); if (op->id) return; success = false; goto done; } memcpy(read_pdu, data->result->pdu + data->pos + 2, sizeof(uint16_t)); data->pos += data->result->data_len; if (bt_att_send(op->att, BT_ATT_OP_READ_REQ, read_pdu, sizeof(read_pdu), read_included_cb, read_included_ref(data), read_included_unref)) return; read_included_unref(data); success = false; done: discovery_op_complete(op, success, att_ecode); } static void read_included(struct read_incl_data *data) { struct bt_gatt_request *op = data->op; uint8_t pdu[2]; memcpy(pdu, data->result->pdu + 2, sizeof(uint16_t)); data->pos += data->result->data_len; if (bt_att_send(op->att, BT_ATT_OP_READ_REQ, pdu, sizeof(pdu), read_included_cb, read_included_ref(data), read_included_unref)) return; if (op->callback) op->callback(false, 0, NULL, data->op->user_data); read_included_unref(data); } static void discover_included_cb(uint8_t opcode, const void *pdu, uint16_t length, void *user_data) { struct bt_gatt_request *op = user_data; struct bt_gatt_result *cur_result; uint8_t att_ecode = 0; uint16_t last_handle; size_t data_length; bool success; if (opcode == BT_ATT_OP_ERROR_RSP) { att_ecode = process_error(pdu, length); success = false; goto failed; } if (opcode != BT_ATT_OP_READ_BY_TYPE_RSP || !pdu || length < 6) { success = false; goto failed; } data_length = ((const uint8_t *) pdu)[0]; /* * Check if PDU contains data sets with length declared in the beginning * of frame and if this length is correct. * Data set length may be 6 or 8 octets: * 2 octets - include service handle * 2 octets - start handle of included service * 2 octets - end handle of included service * optional 2 octets - Bluetooth UUID of included service */ if ((data_length != 8 && data_length != 6) || (length - 1) % data_length) { success = false; goto failed; } cur_result = result_append(opcode, pdu + 1, length - 1, data_length, op); if (!cur_result) { success = false; goto failed; } if (data_length == 6) { struct read_incl_data *data; data = new_read_included(cur_result); if (!data) { success = false; goto failed; } read_included(data); return; } last_handle = get_le16(pdu + length - data_length); /* * If last handle is lower from previous start handle then it is smth * wrong. Let's stop search, otherwise we might enter infinite loop. */ if (last_handle < op->start_handle) { success = false; goto failed; } op->start_handle = last_handle + 1; if (last_handle != op->end_handle) { uint8_t pdu[6]; put_le16(op->start_handle, pdu); put_le16(op->end_handle, pdu + 2); put_le16(GATT_INCLUDE_UUID, pdu + 4); op->id = bt_att_send(op->att, BT_ATT_OP_READ_BY_TYPE_REQ, pdu, sizeof(pdu), discover_included_cb, bt_gatt_request_ref(op), async_req_unref); if (op->id) return; success = false; goto failed; } success = true; failed: discovery_op_complete(op, success, att_ecode); } struct bt_gatt_request *bt_gatt_discover_included_services(struct bt_att *att, uint16_t start, uint16_t end, bt_gatt_request_callback_t callback, void *user_data, bt_gatt_destroy_func_t destroy) { struct bt_gatt_request *op; uint8_t pdu[6]; if (!att) return NULL; op = new0(struct bt_gatt_request, 1); op->att = att; op->callback = callback; op->user_data = user_data; op->destroy = destroy; op->start_handle = start; op->end_handle = end; put_le16(start, pdu); put_le16(end, pdu + 2); put_le16(GATT_INCLUDE_UUID, pdu + 4); op->id = bt_att_send(att, BT_ATT_OP_READ_BY_TYPE_REQ, pdu, sizeof(pdu), discover_included_cb, bt_gatt_request_ref(op), async_req_unref); if (!op->id) { free(op); return NULL; } return bt_gatt_request_ref(op); } static void discover_chrcs_cb(uint8_t opcode, const void *pdu, uint16_t length, void *user_data) { struct bt_gatt_request *op = user_data; bool success; uint8_t att_ecode = 0; size_t data_length; uint16_t last_handle; if (opcode == BT_ATT_OP_ERROR_RSP) { success = false; att_ecode = process_error(pdu, length); goto done; } /* PDU must contain at least the following (sans opcode): * - Attr Data Length (1 octet) * - Attr Data List (at least 7 octets): * -- 2 octets: Attribute handle * -- 1 octet: Characteristic properties * -- 2 octets: Characteristic value handle * -- 2 or 16 octets: characteristic UUID */ if (opcode != BT_ATT_OP_READ_BY_TYPE_RSP || !pdu || length < 8) { success = false; goto done; } data_length = ((uint8_t *) pdu)[0]; if ((data_length != 7 && data_length != 21) || ((length - 1) % data_length)) { success = false; goto done; } if (!result_append(opcode, pdu + 1, length - 1, data_length, op)) { success = false; goto done; } last_handle = get_le16(pdu + length - data_length); /* * If last handle is lower from previous start handle then it is smth * wrong. Let's stop search, otherwise we might enter infinite loop. */ if (last_handle < op->start_handle) { success = false; goto done; } op->start_handle = last_handle + 1; if (last_handle != op->end_handle) { uint8_t pdu[6]; put_le16(op->start_handle, pdu); put_le16(op->end_handle, pdu + 2); put_le16(GATT_CHARAC_UUID, pdu + 4); op->id = bt_att_send(op->att, BT_ATT_OP_READ_BY_TYPE_REQ, pdu, sizeof(pdu), discover_chrcs_cb, bt_gatt_request_ref(op), async_req_unref); if (op->id) return; success = false; goto done; } success = true; done: discovery_op_complete(op, success, att_ecode); } struct bt_gatt_request *bt_gatt_discover_characteristics(struct bt_att *att, uint16_t start, uint16_t end, bt_gatt_request_callback_t callback, void *user_data, bt_gatt_destroy_func_t destroy) { struct bt_gatt_request *op; uint8_t pdu[6]; if (!att) return NULL; op = new0(struct bt_gatt_request, 1); op->att = att; op->callback = callback; op->user_data = user_data; op->destroy = destroy; op->start_handle = start; op->end_handle = end; put_le16(start, pdu); put_le16(end, pdu + 2); put_le16(GATT_CHARAC_UUID, pdu + 4); op->id = bt_att_send(att, BT_ATT_OP_READ_BY_TYPE_REQ, pdu, sizeof(pdu), discover_chrcs_cb, bt_gatt_request_ref(op), async_req_unref); if (!op->id) { free(op); return NULL; } return bt_gatt_request_ref(op); } static void read_by_type_cb(uint8_t opcode, const void *pdu, uint16_t length, void *user_data) { struct bt_gatt_request *op = user_data; bool success; uint8_t att_ecode = 0; size_t data_length; uint16_t last_handle; if (opcode == BT_ATT_OP_ERROR_RSP) { att_ecode = process_error(pdu, length); success = false; goto done; } if (opcode != BT_ATT_OP_READ_BY_TYPE_RSP || !pdu) { success = false; att_ecode = 0; goto done; } data_length = ((uint8_t *) pdu)[0]; if (((length - 1) % data_length)) { success = false; att_ecode = 0; goto done; } if (!result_append(opcode, pdu + 1, length - 1, data_length, op)) { success = false; att_ecode = 0; goto done; } last_handle = get_le16(pdu + length - data_length); /* * If last handle is lower from previous start handle then it is smth * wrong. Let's stop search, otherwise we might enter infinite loop. */ if (last_handle < op->start_handle) { success = false; goto done; } op->start_handle = last_handle + 1; if (last_handle != op->end_handle) { uint8_t pdu[4 + get_uuid_len(&op->uuid)]; put_le16(op->start_handle, pdu); put_le16(op->end_handle, pdu + 2); bt_uuid_to_le(&op->uuid, pdu + 4); op->id = bt_att_send(op->att, BT_ATT_OP_READ_BY_TYPE_REQ, pdu, sizeof(pdu), read_by_type_cb, bt_gatt_request_ref(op), async_req_unref); if (op->id) return; success = false; goto done; } success = true; done: discovery_op_complete(op, success, att_ecode); } bool bt_gatt_read_by_type(struct bt_att *att, uint16_t start, uint16_t end, const bt_uuid_t *uuid, bt_gatt_request_callback_t callback, void *user_data, bt_gatt_destroy_func_t destroy) { struct bt_gatt_request *op; uint8_t pdu[4 + get_uuid_len(uuid)]; if (!att || !uuid || uuid->type == BT_UUID_UNSPEC) return false; op = new0(struct bt_gatt_request, 1); op->att = att; op->callback = callback; op->user_data = user_data; op->destroy = destroy; op->start_handle = start; op->end_handle = end; op->uuid = *uuid; put_le16(start, pdu); put_le16(end, pdu + 2); bt_uuid_to_le(uuid, pdu + 4); op->id = bt_att_send(att, BT_ATT_OP_READ_BY_TYPE_REQ, pdu, sizeof(pdu), read_by_type_cb, bt_gatt_request_ref(op), async_req_unref); if (op->id) return true; free(op); return false; } static void discover_descs_cb(uint8_t opcode, const void *pdu, uint16_t length, void *user_data) { struct bt_gatt_request *op = user_data; bool success; uint8_t att_ecode = 0; uint8_t format; uint16_t last_handle; size_t data_length; if (opcode == BT_ATT_OP_ERROR_RSP) { success = false; att_ecode = process_error(pdu, length); goto done; } /* The PDU should contain the following data (sans opcode): * - Format (1 octet) * - Attr Data List (at least 4 octets): * -- 2 octets: Attribute handle * -- 2 or 16 octets: UUID. */ if (opcode != BT_ATT_OP_FIND_INFO_RSP || !pdu || length < 5) { success = false; goto done; } format = ((uint8_t *) pdu)[0]; if (format == 0x01) data_length = 4; else if (format == 0x02) data_length = 18; else { success = false; goto done; } if ((length - 1) % data_length) { success = false; goto done; } if (!result_append(opcode, pdu + 1, length - 1, data_length, op)) { success = false; goto done; } last_handle = get_le16(pdu + length - data_length); /* * If last handle is lower from previous start handle then it is smth * wrong. Let's stop search, otherwise we might enter infinite loop. */ if (last_handle < op->start_handle) { success = false; goto done; } op->start_handle = last_handle + 1; if (last_handle != op->end_handle) { uint8_t pdu[4]; put_le16(op->start_handle, pdu); put_le16(op->end_handle, pdu + 2); op->id = bt_att_send(op->att, BT_ATT_OP_FIND_INFO_REQ, pdu, sizeof(pdu), discover_descs_cb, bt_gatt_request_ref(op), async_req_unref); if (op->id) return; success = false; goto done; } success = true; done: discovery_op_complete(op, success, att_ecode); } struct bt_gatt_request *bt_gatt_discover_descriptors(struct bt_att *att, uint16_t start, uint16_t end, bt_gatt_request_callback_t callback, void *user_data, bt_gatt_destroy_func_t destroy) { struct bt_gatt_request *op; uint8_t pdu[4]; if (!att) return NULL; op = new0(struct bt_gatt_request, 1); op->att = att; op->callback = callback; op->user_data = user_data; op->destroy = destroy; op->start_handle = start; op->end_handle = end; put_le16(start, pdu); put_le16(end, pdu + 2); op->id = bt_att_send(att, BT_ATT_OP_FIND_INFO_REQ, pdu, sizeof(pdu), discover_descs_cb, bt_gatt_request_ref(op), async_req_unref); if (!op->id) { free(op); return NULL; } return bt_gatt_request_ref(op); } bluez-5.82/src/shared/PaxHeaders/uhid.h0000644000000000000000000000005014643061455015015 xustar0020 atime=1743515875 20 ctime=1743591277 bluez-5.82/src/shared/uhid.h0000644000000000000000000000407614643061455014505 0ustar00rootroot/* SPDX-License-Identifier: LGPL-2.1-or-later */ /* * * BlueZ - Bluetooth protocol stack for Linux * * Copyright (C) 2014 Intel Corporation. All rights reserved. * * */ #include #include #include #include struct bt_uhid; enum { BT_UHID_NONE = 0, BT_UHID_KEYBOARD, BT_UHID_MOUSE, BT_UHID_GAMING, BT_UHID_TABLET }; static inline uint8_t bt_uhid_icon_to_type(const char *icon) { if (!icon) return BT_UHID_NONE; if (!strcmp(icon, "input-keyboard")) return BT_UHID_KEYBOARD; else if (!strcmp(icon, "input-mouse")) return BT_UHID_MOUSE; else if (!strcmp(icon, "input-gaming")) return BT_UHID_GAMING; else if (!strcmp(icon, "input-tablet")) return BT_UHID_TABLET; else return BT_UHID_NONE; } struct bt_uhid *bt_uhid_new_default(void); struct bt_uhid *bt_uhid_new(int fd); struct bt_uhid *bt_uhid_ref(struct bt_uhid *uhid); void bt_uhid_unref(struct bt_uhid *uhid); bool bt_uhid_set_close_on_unref(struct bt_uhid *uhid, bool do_close); typedef void (*bt_uhid_callback_t)(struct uhid_event *ev, void *user_data); unsigned int bt_uhid_register(struct bt_uhid *uhid, uint32_t event, bt_uhid_callback_t func, void *user_data); bool bt_uhid_unregister(struct bt_uhid *uhid, unsigned int id); bool bt_uhid_unregister_all(struct bt_uhid *uhid); int bt_uhid_send(struct bt_uhid *uhid, const struct uhid_event *ev); int bt_uhid_create(struct bt_uhid *uhid, const char *name, bdaddr_t *src, bdaddr_t *dst, uint32_t vendor, uint32_t product, uint32_t version, uint32_t country, uint8_t type, void *rd_data, size_t rd_size); bool bt_uhid_created(struct bt_uhid *uhid); bool bt_uhid_started(struct bt_uhid *uhid); int bt_uhid_input(struct bt_uhid *uhid, uint8_t number, const void *data, size_t size); int bt_uhid_set_report_reply(struct bt_uhid *uhid, uint8_t id, uint8_t status); int bt_uhid_get_report_reply(struct bt_uhid *uhid, uint8_t id, uint8_t number, uint8_t status, const void *data, size_t size); int bt_uhid_destroy(struct bt_uhid *uhid, bool force); int bt_uhid_replay(struct bt_uhid *uhid); bluez-5.82/src/shared/PaxHeaders/crypto.h0000644000000000000000000000005014447506754015414 xustar0020 atime=1743515862 20 ctime=1743591277 bluez-5.82/src/shared/crypto.h0000644000000000000000000000501114447506754015072 0ustar00rootroot/* SPDX-License-Identifier: LGPL-2.1-or-later */ /* * * BlueZ - Bluetooth protocol stack for Linux * * Copyright (C) 2012-2014 Intel Corporation. All rights reserved. * * */ #include #include #include struct bt_crypto; struct bt_crypto *bt_crypto_new(void); struct bt_crypto *bt_crypto_ref(struct bt_crypto *crypto); void bt_crypto_unref(struct bt_crypto *crypto); bool bt_crypto_random_bytes(struct bt_crypto *crypto, void *buf, uint8_t num_bytes); bool bt_crypto_e(struct bt_crypto *crypto, const uint8_t key[16], const uint8_t plaintext[16], uint8_t encrypted[16]); bool bt_crypto_ah(struct bt_crypto *crypto, const uint8_t k[16], const uint8_t r[3], uint8_t hash[3]); bool bt_crypto_c1(struct bt_crypto *crypto, const uint8_t k[16], const uint8_t r[16], const uint8_t pres[7], const uint8_t preq[7], uint8_t iat, const uint8_t ia[6], uint8_t rat, const uint8_t ra[6], uint8_t res[16]); bool bt_crypto_s1(struct bt_crypto *crypto, const uint8_t k[16], const uint8_t r1[16], const uint8_t r2[16], uint8_t res[16]); bool bt_crypto_f4(struct bt_crypto *crypto, uint8_t u[32], uint8_t v[32], uint8_t x[16], uint8_t z, uint8_t res[16]); bool bt_crypto_f5(struct bt_crypto *crypto, uint8_t w[32], uint8_t n1[16], uint8_t n2[16], uint8_t a1[7], uint8_t a2[7], uint8_t mackey[16], uint8_t ltk[16]); bool bt_crypto_f6(struct bt_crypto *crypto, uint8_t w[16], uint8_t n1[16], uint8_t n2[16], uint8_t r[16], uint8_t io_cap[3], uint8_t a1[7], uint8_t a2[7], uint8_t res[16]); bool bt_crypto_g2(struct bt_crypto *crypto, uint8_t u[32], uint8_t v[32], uint8_t x[16], uint8_t y[16], uint32_t *val); bool bt_crypto_h6(struct bt_crypto *crypto, const uint8_t w[16], const uint8_t keyid[4], uint8_t res[16]); bool bt_crypto_sign_att(struct bt_crypto *crypto, const uint8_t key[16], const uint8_t *m, uint16_t m_len, uint32_t sign_cnt, uint8_t signature[12]); bool bt_crypto_verify_att_sign(struct bt_crypto *crypto, const uint8_t key[16], const uint8_t *pdu, uint16_t pdu_len); bool bt_crypto_gatt_hash(struct bt_crypto *crypto, struct iovec *iov, size_t iov_len, uint8_t res[16]); bool bt_crypto_sef(struct bt_crypto *crypto, const uint8_t k[16], const uint8_t sirk[16], uint8_t out[16]); bool bt_crypto_sih(struct bt_crypto *crypto, const uint8_t k[16], const uint8_t r[3], uint8_t hash[3]); bool bt_crypto_sirk(struct bt_crypto *crypto, const char *str, uint16_t vendor, uint16_t product, uint16_t version, uint16_t source, uint8_t sirk[16]); bluez-5.82/src/shared/PaxHeaders/gap.h0000644000000000000000000000005014015011623014614 xustar0020 atime=1743515902 20 ctime=1743591277 bluez-5.82/src/shared/gap.h0000644000000000000000000000201314015011623014271 0ustar00rootroot/* SPDX-License-Identifier: LGPL-2.1-or-later */ /* * * BlueZ - Bluetooth protocol stack for Linux * * Copyright (C) 2012-2014 Intel Corporation. All rights reserved. * * */ #include #include #define BT_GAP_ADDR_TYPE_BREDR 0x00 #define BT_GAP_ADDR_TYPE_LE_PUBLIC 0x01 #define BT_GAP_ADDR_TYPE_LE_RANDOM 0x02 struct bt_gap; struct bt_gap *bt_gap_new_default(void); struct bt_gap *bt_gap_new_index(uint16_t index); struct bt_gap *bt_gap_ref(struct bt_gap *gap); void bt_gap_unref(struct bt_gap *gap); typedef void (*bt_gap_destroy_func_t)(void *user_data); typedef void (*bt_gap_ready_func_t)(bool success, void *user_data); bool bt_gap_set_ready_handler(struct bt_gap *gap, bt_gap_ready_func_t handler, void *user_data, bt_gap_destroy_func_t destroy); bool bt_gap_set_static_addr(struct bt_gap *gap, uint8_t addr[6]); bool bt_gap_set_local_irk(struct bt_gap *gap, uint8_t key[16]); bool bt_gap_add_peer_irk(struct bt_gap *gap, uint8_t addr_type, uint8_t addr[6], uint8_t key[16]); bluez-5.82/src/shared/PaxHeaders/ringbuf.h0000644000000000000000000000005014015011623015501 xustar0020 atime=1743515868 20 ctime=1743591277 bluez-5.82/src/shared/ringbuf.h0000644000000000000000000000217114015011623015163 0ustar00rootroot/* SPDX-License-Identifier: LGPL-2.1-or-later */ /* * * BlueZ - Bluetooth protocol stack for Linux * * Copyright (C) 2012-2014 Intel Corporation. All rights reserved. * * */ #include #include #include typedef void (*ringbuf_tracing_func_t)(const void *buf, size_t count, void *user_data); struct ringbuf; struct ringbuf *ringbuf_new(size_t size); void ringbuf_free(struct ringbuf *ringbuf); bool ringbuf_set_input_tracing(struct ringbuf *ringbuf, ringbuf_tracing_func_t callback, void *user_data); size_t ringbuf_capacity(struct ringbuf *ringbuf); size_t ringbuf_len(struct ringbuf *ringbuf); size_t ringbuf_drain(struct ringbuf *ringbuf, size_t count); void *ringbuf_peek(struct ringbuf *ringbuf, size_t offset, size_t *len_nowrap); ssize_t ringbuf_write(struct ringbuf *ringbuf, int fd); size_t ringbuf_avail(struct ringbuf *ringbuf); int ringbuf_printf(struct ringbuf *ringbuf, const char *format, ...) __attribute__((format(printf, 2, 3))); int ringbuf_vprintf(struct ringbuf *ringbuf, const char *format, va_list ap); ssize_t ringbuf_read(struct ringbuf *ringbuf, int fd); bluez-5.82/src/shared/PaxHeaders/io-mainloop.c0000644000000000000000000000005014015011623016263 xustar0020 atime=1743516172 20 ctime=1743591278 bluez-5.82/src/shared/io-mainloop.c0000644000000000000000000001254614015011623015754 0ustar00rootroot// SPDX-License-Identifier: LGPL-2.1-or-later /* * * BlueZ - Bluetooth protocol stack for Linux * * Copyright (C) 2012-2014 Intel Corporation. All rights reserved. * * */ #ifdef HAVE_CONFIG_H #include #endif #include #include #include #include "src/shared/mainloop.h" #include "src/shared/util.h" #include "src/shared/io.h" struct io { int ref_count; int fd; uint32_t events; bool close_on_destroy; io_callback_func_t read_callback; io_destroy_func_t read_destroy; void *read_data; io_callback_func_t write_callback; io_destroy_func_t write_destroy; void *write_data; io_callback_func_t disconnect_callback; io_destroy_func_t disconnect_destroy; void *disconnect_data; }; static struct io *io_ref(struct io *io) { if (!io) return NULL; __sync_fetch_and_add(&io->ref_count, 1); return io; } static void io_unref(struct io *io) { if (!io) return; if (__sync_sub_and_fetch(&io->ref_count, 1)) return; free(io); } static void io_cleanup(void *user_data) { struct io *io = user_data; if (io->write_destroy) io->write_destroy(io->write_data); if (io->read_destroy) io->read_destroy(io->read_data); if (io->disconnect_destroy) io->disconnect_destroy(io->disconnect_data); if (io->close_on_destroy) close(io->fd); io->fd = -1; } static void io_callback(int fd, uint32_t events, void *user_data) { struct io *io = user_data; io_ref(io); if ((events & (EPOLLRDHUP | EPOLLHUP | EPOLLERR))) { io->read_callback = NULL; io->write_callback = NULL; if (!io->disconnect_callback) { mainloop_remove_fd(io->fd); io_unref(io); return; } if (!io->disconnect_callback(io, io->disconnect_data)) { if (io->disconnect_destroy) io->disconnect_destroy(io->disconnect_data); io->disconnect_callback = NULL; io->disconnect_destroy = NULL; io->disconnect_data = NULL; io->events &= ~EPOLLRDHUP; mainloop_modify_fd(io->fd, io->events); } } if ((events & EPOLLIN) && io->read_callback) { if (!io->read_callback(io, io->read_data)) { if (io->read_destroy) io->read_destroy(io->read_data); io->read_callback = NULL; io->read_destroy = NULL; io->read_data = NULL; io->events &= ~EPOLLIN; mainloop_modify_fd(io->fd, io->events); } } if ((events & EPOLLOUT) && io->write_callback) { if (!io->write_callback(io, io->write_data)) { if (io->write_destroy) io->write_destroy(io->write_data); io->write_callback = NULL; io->write_destroy = NULL; io->write_data = NULL; io->events &= ~EPOLLOUT; mainloop_modify_fd(io->fd, io->events); } } io_unref(io); } struct io *io_new(int fd) { struct io *io; if (fd < 0) return NULL; io = new0(struct io, 1); io->fd = fd; io->events = 0; io->close_on_destroy = false; if (mainloop_add_fd(io->fd, io->events, io_callback, io, io_cleanup) < 0) { free(io); return NULL; } return io_ref(io); } void io_destroy(struct io *io) { if (!io) return; io->read_callback = NULL; io->write_callback = NULL; io->disconnect_callback = NULL; mainloop_remove_fd(io->fd); io_unref(io); } int io_get_fd(struct io *io) { if (!io) return -ENOTCONN; return io->fd; } bool io_set_close_on_destroy(struct io *io, bool do_close) { if (!io) return false; io->close_on_destroy = do_close; return true; } bool io_set_read_handler(struct io *io, io_callback_func_t callback, void *user_data, io_destroy_func_t destroy) { uint32_t events; if (!io || io->fd < 0) return false; if (io->read_destroy) io->read_destroy(io->read_data); if (callback) events = io->events | EPOLLIN; else events = io->events & ~EPOLLIN; io->read_callback = callback; io->read_destroy = destroy; io->read_data = user_data; if (events == io->events) return true; if (mainloop_modify_fd(io->fd, events) < 0) return false; io->events = events; return true; } bool io_set_write_handler(struct io *io, io_callback_func_t callback, void *user_data, io_destroy_func_t destroy) { uint32_t events; if (!io || io->fd < 0) return false; if (io->write_destroy) io->write_destroy(io->write_data); if (callback) events = io->events | EPOLLOUT; else events = io->events & ~EPOLLOUT; io->write_callback = callback; io->write_destroy = destroy; io->write_data = user_data; if (events == io->events) return true; if (mainloop_modify_fd(io->fd, events) < 0) return false; io->events = events; return true; } bool io_set_disconnect_handler(struct io *io, io_callback_func_t callback, void *user_data, io_destroy_func_t destroy) { uint32_t events; if (!io || io->fd < 0) return false; if (io->disconnect_destroy) io->disconnect_destroy(io->disconnect_data); if (callback) events = io->events | EPOLLRDHUP; else events = io->events & ~EPOLLRDHUP; io->disconnect_callback = callback; io->disconnect_destroy = destroy; io->disconnect_data = user_data; if (events == io->events) return true; if (mainloop_modify_fd(io->fd, events) < 0) return false; io->events = events; return true; } ssize_t io_send(struct io *io, const struct iovec *iov, int iovcnt) { ssize_t ret; if (!io || io->fd < 0) return -ENOTCONN; do { ret = writev(io->fd, iov, iovcnt); } while (ret < 0 && errno == EINTR); if (ret < 0) return -errno; return ret; } bool io_shutdown(struct io *io) { if (!io || io->fd < 0) return false; return shutdown(io->fd, SHUT_RDWR) == 0; } bluez-5.82/src/shared/PaxHeaders/btsnoop.c0000644000000000000000000000005014606723200015533 xustar0020 atime=1743515877 20 ctime=1743591277 bluez-5.82/src/shared/btsnoop.c0000644000000000000000000002741014606723200015220 0ustar00rootroot// SPDX-License-Identifier: LGPL-2.1-or-later /* * * BlueZ - Bluetooth protocol stack for Linux * * Copyright (C) 2012-2014 Intel Corporation. All rights reserved. * * */ #ifdef HAVE_CONFIG_H #include #endif #define _GNU_SOURCE #include #include #include #include #include #include #include #include #include #include "src/shared/btsnoop.h" struct btsnoop_hdr { uint8_t id[8]; /* Identification Pattern */ uint32_t version; /* Version Number = 1 */ uint32_t type; /* Datalink Type */ } __attribute__ ((packed)); #define BTSNOOP_HDR_SIZE (sizeof(struct btsnoop_hdr)) struct btsnoop_pkt { uint32_t size; /* Original Length */ uint32_t len; /* Included Length */ uint32_t flags; /* Packet Flags */ uint32_t drops; /* Cumulative Drops */ uint64_t ts; /* Timestamp microseconds */ uint8_t data[0]; /* Packet Data */ } __attribute__ ((packed)); #define BTSNOOP_PKT_SIZE (sizeof(struct btsnoop_pkt)) static const uint8_t btsnoop_id[] = { 0x62, 0x74, 0x73, 0x6e, 0x6f, 0x6f, 0x70, 0x00 }; static const uint32_t btsnoop_version = 1; struct pklg_pkt { uint32_t len; uint64_t ts; uint8_t type; } __attribute__ ((packed)); #define PKLG_PKT_SIZE (sizeof(struct pklg_pkt)) struct btsnoop { int ref_count; int fd; unsigned long flags; uint32_t format; uint16_t index; bool aborted; bool pklg_format; bool pklg_v2; const char *path; size_t max_size; size_t cur_size; unsigned int max_count; unsigned int cur_count; }; struct btsnoop *btsnoop_open(const char *path, unsigned long flags) { struct btsnoop *btsnoop; struct btsnoop_hdr hdr; ssize_t len; btsnoop = calloc(1, sizeof(*btsnoop)); if (!btsnoop) return NULL; btsnoop->fd = open(path, O_RDONLY | O_CLOEXEC); if (btsnoop->fd < 0) { free(btsnoop); return NULL; } btsnoop->flags = flags; len = read(btsnoop->fd, &hdr, BTSNOOP_HDR_SIZE); if (len < 0 || len != BTSNOOP_HDR_SIZE) goto failed; if (!memcmp(hdr.id, btsnoop_id, sizeof(btsnoop_id))) { /* Check for BTSnoop version 1 format */ if (be32toh(hdr.version) != btsnoop_version) goto failed; btsnoop->format = be32toh(hdr.type); btsnoop->index = 0xffff; } else { if (!(btsnoop->flags & BTSNOOP_FLAG_PKLG_SUPPORT)) goto failed; /* Check for Apple Packet Logger format */ if (hdr.id[0] != 0x00 || (hdr.id[1] != 0x00 && hdr.id[1] != 0x01)) goto failed; btsnoop->format = BTSNOOP_FORMAT_MONITOR; btsnoop->index = 0xffff; btsnoop->pklg_format = true; btsnoop->pklg_v2 = (hdr.id[1] == 0x01); /* Apple Packet Logger format has no header */ lseek(btsnoop->fd, 0, SEEK_SET); } return btsnoop_ref(btsnoop); failed: close(btsnoop->fd); free(btsnoop); return NULL; } struct btsnoop *btsnoop_create(const char *path, size_t max_size, unsigned int max_count, uint32_t format) { struct btsnoop *btsnoop; struct btsnoop_hdr hdr; const char *real_path; char tmp[PATH_MAX]; ssize_t written; if (!max_size && max_count) return NULL; btsnoop = calloc(1, sizeof(*btsnoop)); if (!btsnoop) return NULL; /* If max file size is specified, always add counter to file path */ if (max_size) { snprintf(tmp, PATH_MAX, "%s.0", path); real_path = tmp; } else { real_path = path; } btsnoop->fd = open(real_path, O_WRONLY | O_CREAT | O_TRUNC | O_CLOEXEC, 0644); if (btsnoop->fd < 0) { free(btsnoop); return NULL; } btsnoop->format = format; btsnoop->index = 0xffff; btsnoop->path = path; btsnoop->max_count = max_count; btsnoop->max_size = max_size; memcpy(hdr.id, btsnoop_id, sizeof(btsnoop_id)); hdr.version = htobe32(btsnoop_version); hdr.type = htobe32(btsnoop->format); written = write(btsnoop->fd, &hdr, BTSNOOP_HDR_SIZE); if (written < 0) { close(btsnoop->fd); free(btsnoop); return NULL; } btsnoop->cur_size = BTSNOOP_HDR_SIZE; return btsnoop_ref(btsnoop); } struct btsnoop *btsnoop_ref(struct btsnoop *btsnoop) { if (!btsnoop) return NULL; __sync_fetch_and_add(&btsnoop->ref_count, 1); return btsnoop; } void btsnoop_unref(struct btsnoop *btsnoop) { if (!btsnoop) return; if (__sync_sub_and_fetch(&btsnoop->ref_count, 1)) return; if (btsnoop->fd >= 0) close(btsnoop->fd); free(btsnoop); } uint32_t btsnoop_get_format(struct btsnoop *btsnoop) { if (!btsnoop) return BTSNOOP_FORMAT_INVALID; return btsnoop->format; } static bool btsnoop_rotate(struct btsnoop *btsnoop) { struct btsnoop_hdr hdr; char path[PATH_MAX]; ssize_t written; close(btsnoop->fd); /* Check if max number of log files has been reached */ if (btsnoop->max_count && btsnoop->cur_count >= btsnoop->max_count) { snprintf(path, PATH_MAX, "%s.%u", btsnoop->path, btsnoop->cur_count - btsnoop->max_count); unlink(path); } snprintf(path, PATH_MAX,"%s.%u", btsnoop->path, btsnoop->cur_count); btsnoop->cur_count++; btsnoop->fd = open(path, O_WRONLY | O_CREAT | O_TRUNC | O_CLOEXEC, 0644); if (btsnoop->fd < 0) return false; memcpy(hdr.id, btsnoop_id, sizeof(btsnoop_id)); hdr.version = htobe32(btsnoop_version); hdr.type = htobe32(btsnoop->format); written = write(btsnoop->fd, &hdr, BTSNOOP_HDR_SIZE); if (written < 0) return false; btsnoop->cur_size = BTSNOOP_HDR_SIZE; return true; } bool btsnoop_write(struct btsnoop *btsnoop, struct timeval *tv, uint32_t flags, uint32_t drops, const void *data, uint16_t size) { struct btsnoop_pkt pkt; uint64_t ts; ssize_t written; if (!btsnoop || !tv) return false; if (btsnoop->max_size && btsnoop->max_size <= btsnoop->cur_size + size + BTSNOOP_PKT_SIZE) if (!btsnoop_rotate(btsnoop)) return false; ts = (tv->tv_sec - 946684800ll) * 1000000ll + tv->tv_usec; pkt.size = htobe32(size); pkt.len = htobe32(size); pkt.flags = htobe32(flags); pkt.drops = htobe32(drops); pkt.ts = htobe64(ts + 0x00E03AB44A676000ll); written = write(btsnoop->fd, &pkt, BTSNOOP_PKT_SIZE); if (written < 0) return false; btsnoop->cur_size += BTSNOOP_PKT_SIZE; if (data && size > 0) { written = write(btsnoop->fd, data, size); if (written < 0) return false; } btsnoop->cur_size += size; return true; } static uint32_t get_flags_from_opcode(uint16_t opcode) { switch (opcode) { case BTSNOOP_OPCODE_NEW_INDEX: case BTSNOOP_OPCODE_DEL_INDEX: break; case BTSNOOP_OPCODE_COMMAND_PKT: return 0x02; case BTSNOOP_OPCODE_EVENT_PKT: return 0x03; case BTSNOOP_OPCODE_ACL_TX_PKT: return 0x00; case BTSNOOP_OPCODE_ACL_RX_PKT: return 0x01; case BTSNOOP_OPCODE_SCO_TX_PKT: case BTSNOOP_OPCODE_SCO_RX_PKT: break; case BTSNOOP_OPCODE_ISO_TX_PKT: case BTSNOOP_OPCODE_ISO_RX_PKT: break; case BTSNOOP_OPCODE_OPEN_INDEX: case BTSNOOP_OPCODE_CLOSE_INDEX: break; } return 0xff; } bool btsnoop_write_hci(struct btsnoop *btsnoop, struct timeval *tv, uint16_t index, uint16_t opcode, uint32_t drops, const void *data, uint16_t size) { uint32_t flags; if (!btsnoop) return false; switch (btsnoop->format) { case BTSNOOP_FORMAT_HCI: if (btsnoop->index == 0xffff) btsnoop->index = index; if (index != btsnoop->index) return false; flags = get_flags_from_opcode(opcode); if (flags == 0xff) return false; break; case BTSNOOP_FORMAT_MONITOR: flags = ((uint32_t)index << 16) | opcode; break; default: return false; } return btsnoop_write(btsnoop, tv, flags, drops, data, size); } bool btsnoop_write_phy(struct btsnoop *btsnoop, struct timeval *tv, uint16_t frequency, const void *data, uint16_t size) { uint32_t flags; if (!btsnoop) return false; switch (btsnoop->format) { case BTSNOOP_FORMAT_SIMULATOR: flags = (1 << 16) | frequency; break; default: return false; } return btsnoop_write(btsnoop, tv, flags, 0, data, size); } static bool pklg_read_hci(struct btsnoop *btsnoop, struct timeval *tv, uint16_t *index, uint16_t *opcode, void *data, uint16_t *size) { struct pklg_pkt pkt; uint32_t toread; uint64_t ts; ssize_t len; len = read(btsnoop->fd, &pkt, PKLG_PKT_SIZE); if (len == 0) return false; if (len < 0 || len != PKLG_PKT_SIZE) { btsnoop->aborted = true; return false; } if (btsnoop->pklg_v2) { toread = le32toh(pkt.len) - (PKLG_PKT_SIZE - 4); ts = le64toh(pkt.ts); tv->tv_sec = ts & 0xffffffff; tv->tv_usec = ts >> 32; } else { toread = be32toh(pkt.len) - (PKLG_PKT_SIZE - 4); ts = be64toh(pkt.ts); tv->tv_sec = ts >> 32; tv->tv_usec = ts & 0xffffffff; } if (toread > BTSNOOP_MAX_PACKET_SIZE) { btsnoop->aborted = true; return false; } switch (pkt.type) { case 0x00: *index = 0x0000; *opcode = BTSNOOP_OPCODE_COMMAND_PKT; break; case 0x01: *index = 0x0000; *opcode = BTSNOOP_OPCODE_EVENT_PKT; break; case 0x02: *index = 0x0000; *opcode = BTSNOOP_OPCODE_ACL_TX_PKT; break; case 0x03: *index = 0x0000; *opcode = BTSNOOP_OPCODE_ACL_RX_PKT; break; case 0x08: *index = 0x0000; *opcode = BTSNOOP_OPCODE_SCO_TX_PKT; break; case 0x09: *index = 0x0000; *opcode = BTSNOOP_OPCODE_SCO_RX_PKT; break; case 0x12: *index = 0x0000; *opcode = BTSNOOP_OPCODE_ISO_TX_PKT; break; case 0x13: *index = 0x0000; *opcode = BTSNOOP_OPCODE_ISO_RX_PKT; break; case 0x0b: *index = 0x0000; *opcode = BTSNOOP_OPCODE_VENDOR_DIAG; break; case 0xfc: *index = 0xffff; *opcode = BTSNOOP_OPCODE_SYSTEM_NOTE; break; default: *index = 0xffff; *opcode = 0xffff; break; } len = read(btsnoop->fd, data, toread); if (len < 0) { btsnoop->aborted = true; return false; } *size = toread; return true; } static uint16_t get_opcode_from_flags(uint8_t type, uint32_t flags) { switch (type) { case 0x01: return BTSNOOP_OPCODE_COMMAND_PKT; case 0x02: if (flags & 0x01) return BTSNOOP_OPCODE_ACL_RX_PKT; else return BTSNOOP_OPCODE_ACL_TX_PKT; case 0x03: if (flags & 0x01) return BTSNOOP_OPCODE_SCO_RX_PKT; else return BTSNOOP_OPCODE_SCO_TX_PKT; case 0x04: return BTSNOOP_OPCODE_EVENT_PKT; case 0x05: if (flags & 0x01) return BTSNOOP_OPCODE_ISO_RX_PKT; else return BTSNOOP_OPCODE_ISO_TX_PKT; case 0xff: if (flags & 0x02) { if (flags & 0x01) return BTSNOOP_OPCODE_EVENT_PKT; else return BTSNOOP_OPCODE_COMMAND_PKT; } else { if (flags & 0x01) return BTSNOOP_OPCODE_ACL_RX_PKT; else return BTSNOOP_OPCODE_ACL_TX_PKT; } break; } return 0xffff; } bool btsnoop_read_hci(struct btsnoop *btsnoop, struct timeval *tv, uint16_t *index, uint16_t *opcode, void *data, uint16_t *size) { struct btsnoop_pkt pkt; uint32_t toread, flags; uint64_t ts; uint8_t pkt_type; ssize_t len; if (!btsnoop || btsnoop->aborted) return false; if (btsnoop->pklg_format) return pklg_read_hci(btsnoop, tv, index, opcode, data, size); len = read(btsnoop->fd, &pkt, BTSNOOP_PKT_SIZE); if (len == 0) return false; if (len < 0 || len != BTSNOOP_PKT_SIZE) { btsnoop->aborted = true; return false; } toread = be32toh(pkt.len); if (toread > BTSNOOP_MAX_PACKET_SIZE) { btsnoop->aborted = true; return false; } flags = be32toh(pkt.flags); ts = be64toh(pkt.ts) - 0x00E03AB44A676000ll; tv->tv_sec = (ts / 1000000ll) + 946684800ll; tv->tv_usec = ts % 1000000ll; switch (btsnoop->format) { case BTSNOOP_FORMAT_HCI: *index = 0; *opcode = get_opcode_from_flags(0xff, flags); break; case BTSNOOP_FORMAT_UART: len = read(btsnoop->fd, &pkt_type, 1); if (len < 0) { btsnoop->aborted = true; return false; } toread--; *index = 0; *opcode = get_opcode_from_flags(pkt_type, flags); break; case BTSNOOP_FORMAT_MONITOR: *index = flags >> 16; *opcode = flags & 0xffff; break; default: btsnoop->aborted = true; return false; } len = read(btsnoop->fd, data, toread); if (len < 0) { btsnoop->aborted = true; return false; } *size = toread; return true; } bool btsnoop_read_phy(struct btsnoop *btsnoop, struct timeval *tv, uint16_t *frequency, void *data, uint16_t *size) { return false; } bluez-5.82/src/shared/PaxHeaders/tty.h0000644000000000000000000000005014015011623014665 xustar0020 atime=1743515951 20 ctime=1743591277 bluez-5.82/src/shared/tty.h0000644000000000000000000000177614015011623014361 0ustar00rootroot/* SPDX-License-Identifier: LGPL-2.1-or-later */ /* * * BlueZ - Bluetooth protocol stack for Linux * * Copyright (C) 2016 Intel Corporation. All rights reserved. * * */ #include static inline unsigned int tty_get_speed(int speed) { switch (speed) { case 9600: return B9600; case 19200: return B19200; case 38400: return B38400; case 57600: return B57600; case 115200: return B115200; case 230400: return B230400; case 460800: return B460800; case 500000: return B500000; case 576000: return B576000; case 921600: return B921600; case 1000000: return B1000000; case 1152000: return B1152000; case 1500000: return B1500000; case 2000000: return B2000000; #ifdef B2500000 case 2500000: return B2500000; #endif #ifdef B3000000 case 3000000: return B3000000; #endif #ifdef B3500000 case 3500000: return B3500000; #endif #ifdef B3710000 case 3710000: return B3710000; #endif #ifdef B4000000 case 4000000: return B4000000; #endif } return 0; } bluez-5.82/src/shared/PaxHeaders/timeout-glib.c0000644000000000000000000000005014031556405016453 xustar0020 atime=1743515942 20 ctime=1743591277 bluez-5.82/src/shared/timeout-glib.c0000644000000000000000000000351514031556405016140 0ustar00rootroot// SPDX-License-Identifier: LGPL-2.1-or-later /* * * BlueZ - Bluetooth protocol stack for Linux * * Copyright (C) 2014 Intel Corporation. All rights reserved. * * */ #include "timeout.h" #include struct timeout_data { timeout_func_t func; timeout_destroy_func_t destroy; void *user_data; }; static gboolean timeout_callback(gpointer user_data) { struct timeout_data *data = user_data; if (data->func(data->user_data)) return TRUE; return FALSE; } static void timeout_destroy(gpointer user_data) { struct timeout_data *data = user_data; if (data->destroy) data->destroy(data->user_data); g_free(data); } unsigned int timeout_add(unsigned int timeout, timeout_func_t func, void *user_data, timeout_destroy_func_t destroy) { struct timeout_data *data; guint id; data = g_try_new0(struct timeout_data, 1); if (!data) return 0; data->func = func; data->destroy = destroy; data->user_data = user_data; id = g_timeout_add_full(G_PRIORITY_DEFAULT, timeout, timeout_callback, data, timeout_destroy); if (!id) g_free(data); return id; } void timeout_remove(unsigned int id) { GSource *source; if (!id) return; source = g_main_context_find_source_by_id(NULL, id); if (source) g_source_destroy(source); } unsigned int timeout_add_seconds(unsigned int timeout, timeout_func_t func, void *user_data, timeout_destroy_func_t destroy) { struct timeout_data *data; guint id; data = g_try_new0(struct timeout_data, 1); if (!data) return 0; data->func = func; data->destroy = destroy; data->user_data = user_data; if (!timeout) id = g_idle_add_full(G_PRIORITY_DEFAULT_IDLE, timeout_callback, data, timeout_destroy); else id = g_timeout_add_seconds_full(G_PRIORITY_DEFAULT, timeout, timeout_callback, data, timeout_destroy); if (!id) g_free(data); return id; } bluez-5.82/src/shared/PaxHeaders/csip.c0000644000000000000000000000005014643061455015015 xustar0020 atime=1743515927 20 ctime=1743591277 bluez-5.82/src/shared/csip.c0000644000000000000000000004107114643061455014501 0ustar00rootroot// SPDX-License-Identifier: LGPL-2.1-or-later /* * * BlueZ - Bluetooth protocol stack for Linux * * Copyright (C) 2022 Intel Corporation. All rights reserved. * */ #define _GNU_SOURCE #include #include #include #include #include #include #include #include "lib/bluetooth.h" #include "lib/uuid.h" #include "src/shared/queue.h" #include "src/shared/util.h" #include "src/shared/timeout.h" #include "src/shared/att.h" #include "src/shared/gatt-db.h" #include "src/shared/gatt-server.h" #include "src/shared/gatt-client.h" #include "src/shared/crypto.h" #include "src/shared/csip.h" #define DBG(_csip, fmt, arg...) \ csip_debug(_csip, "%s:%s() " fmt, __FILE__, __func__, ## arg) /* SIRK is now hardcoded in the code. This can be moved * to a configuration file. Since the code is to validate * the CSIP use case of set member */ #define SIRK "761FAE703ED681F0C50B34155B6434FB" #define CSIS_SIZE 0x02 #define CSIS_LOCK 0x01 #define CSIS_RANK 0x01 #define CSIS_PLAINTEXT 0x01 #define CSIS_ENC 0x02 struct bt_csip_db { struct gatt_db *db; struct bt_csis *csis; }; struct csis_sirk { uint8_t type; uint8_t val[16]; } __packed; struct bt_csis { struct bt_csip_db *cdb; struct csis_sirk *sirk_val; uint8_t size_val; uint8_t lock_val; uint8_t rank_val; struct gatt_db_attribute *service; struct gatt_db_attribute *sirk; struct gatt_db_attribute *size; struct gatt_db_attribute *lock; struct gatt_db_attribute *lock_ccc; struct gatt_db_attribute *rank; bt_csip_encrypt_func_t encrypt; }; struct bt_csip_cb { unsigned int id; bt_csip_func_t attached; bt_csip_func_t detached; void *user_data; }; struct bt_csip_ready { unsigned int id; bt_csip_ready_func_t func; bt_csip_destroy_func_t destroy; void *data; }; struct bt_csip { int ref_count; struct bt_csip_db *ldb; struct bt_csip_db *rdb; struct bt_gatt_client *client; struct bt_att *att; unsigned int idle_id; struct queue *ready_cbs; bt_csip_debug_func_t debug_func; bt_csip_destroy_func_t debug_destroy; void *debug_data; bt_csip_sirk_func_t sirk_func; void *sirk_data; void *user_data; }; static struct queue *csip_db; static struct queue *csip_cbs; static struct queue *sessions; static void csip_detached(void *data, void *user_data) { struct bt_csip_cb *cb = data; struct bt_csip *csip = user_data; cb->detached(csip, cb->user_data); } void bt_csip_detach(struct bt_csip *csip) { if (!queue_remove(sessions, csip)) return; bt_gatt_client_idle_unregister(csip->client, csip->idle_id); bt_gatt_client_unref(csip->client); csip->client = NULL; queue_foreach(csip_cbs, csip_detached, csip); } static void csis_free(struct bt_csis *csis) { if (!csis) return; free(csis->sirk_val); free(csis); } static void csip_db_free(void *data) { struct bt_csip_db *cdb = data; if (!cdb) return; gatt_db_unref(cdb->db); csis_free(cdb->csis); free(cdb); } static void csip_ready_free(void *data) { struct bt_csip_ready *ready = data; if (ready->destroy) ready->destroy(ready->data); free(ready); } static void csip_free(void *data) { struct bt_csip *csip = data; bt_csip_detach(csip); csip_db_free(csip->rdb); queue_destroy(csip->ready_cbs, csip_ready_free); free(csip); } struct bt_att *bt_csip_get_att(struct bt_csip *csip) { if (!csip) return NULL; if (csip->att) return csip->att; return bt_gatt_client_get_att(csip->client); } struct bt_csip *bt_csip_ref(struct bt_csip *csip) { if (!csip) return NULL; __sync_fetch_and_add(&csip->ref_count, 1); return csip; } static struct bt_csip *bt_csip_ref_safe(struct bt_csip *csip) { if (!csip || !csip->ref_count) return NULL; return bt_csip_ref(csip); } void bt_csip_unref(struct bt_csip *csip) { if (!csip) return; if (__sync_sub_and_fetch(&csip->ref_count, 1)) return; csip_free(csip); } static void csip_debug(struct bt_csip *csip, const char *format, ...) { va_list ap; if (!csip || !format || !csip->debug_func) return; va_start(ap, format); util_debug_va(csip->debug_func, csip->debug_data, format, ap); va_end(ap); } static void csis_sirk_read(struct gatt_db_attribute *attrib, unsigned int id, uint16_t offset, uint8_t opcode, struct bt_att *att, void *user_data) { struct bt_csis *csis = user_data; struct csis_sirk sirk; struct iovec iov; memcpy(&sirk, csis->sirk_val, sizeof(sirk)); if (sirk.type == BT_CSIP_SIRK_ENCRYPT && !csis->encrypt(att, sirk.val)) { gatt_db_attribute_read_result(attrib, id, BT_ATT_ERROR_UNLIKELY, NULL, 0); return; } iov.iov_base = &sirk; iov.iov_len = sizeof(sirk); gatt_db_attribute_read_result(attrib, id, 0, iov.iov_base, iov.iov_len); } static void csis_size_read(struct gatt_db_attribute *attrib, unsigned int id, uint16_t offset, uint8_t opcode, struct bt_att *att, void *user_data) { struct bt_csis *csis = user_data; struct iovec iov; iov.iov_base = &csis->size_val; iov.iov_len = sizeof(csis->size_val); gatt_db_attribute_read_result(attrib, id, 0, iov.iov_base, iov.iov_len); } static void csis_lock_read_cb(struct gatt_db_attribute *attrib, unsigned int id, uint16_t offset, uint8_t opcode, struct bt_att *att, void *user_data) { uint8_t value = CSIS_LOCK; gatt_db_attribute_read_result(attrib, id, 0, &value, sizeof(value)); } static void csis_lock_write_cb(struct gatt_db_attribute *attrib, unsigned int id, uint16_t offset, const uint8_t *value, size_t len, uint8_t opcode, struct bt_att *att, void *user_data) { gatt_db_attribute_write_result(attrib, id, 0); } static void csis_rank_read_cb(struct gatt_db_attribute *attrib, unsigned int id, uint16_t offset, uint8_t opcode, struct bt_att *att, void *user_data) { struct bt_csis *csis = user_data; struct iovec iov; iov.iov_base = &csis->rank_val; iov.iov_len = sizeof(csis->rank_val); gatt_db_attribute_read_result(attrib, id, 0, iov.iov_base, iov.iov_len); } static struct bt_csis *csis_new(struct gatt_db *db) { struct bt_csis *csis; if (!db) return NULL; csis = new0(struct bt_csis, 1); return csis; } static struct bt_csip_db *csip_db_new(struct gatt_db *db) { struct bt_csip_db *cdb; if (!db) return NULL; cdb = new0(struct bt_csip_db, 1); cdb->db = gatt_db_ref(db); if (!csip_db) csip_db = queue_new(); cdb->csis = csis_new(db); cdb->csis->cdb = cdb; queue_push_tail(csip_db, cdb); return cdb; } bool bt_csip_set_user_data(struct bt_csip *csip, void *user_data) { if (!csip) return false; csip->user_data = user_data; return true; } static bool csip_db_match(const void *data, const void *match_data) { const struct bt_csip_db *cdb = data; const struct gatt_db *db = match_data; return (cdb->db == db); } static struct bt_csip_db *csip_get_db(struct gatt_db *db) { struct bt_csip_db *cdb; cdb = queue_find(csip_db, csip_db_match, db); if (cdb) return cdb; return csip_db_new(db); } bool bt_csip_set_debug(struct bt_csip *csip, bt_csip_debug_func_t func, void *user_data, bt_csip_destroy_func_t destroy) { if (!csip) return false; if (csip->debug_destroy) csip->debug_destroy(csip->debug_data); csip->debug_func = func; csip->debug_destroy = destroy; csip->debug_data = user_data; return true; } unsigned int bt_csip_register(bt_csip_func_t attached, bt_csip_func_t detached, void *user_data) { struct bt_csip_cb *cb; static unsigned int id; if (!attached && !detached) return 0; if (!csip_cbs) csip_cbs = queue_new(); cb = new0(struct bt_csip_cb, 1); cb->id = ++id ? id : ++id; cb->attached = attached; cb->detached = detached; cb->user_data = user_data; queue_push_tail(csip_cbs, cb); return cb->id; } static bool match_id(const void *data, const void *match_data) { const struct bt_csip_cb *cb = data; unsigned int id = PTR_TO_UINT(match_data); return (cb->id == id); } bool bt_csip_unregister(unsigned int id) { struct bt_csip_cb *cb; cb = queue_remove_if(csip_cbs, match_id, UINT_TO_PTR(id)); if (!cb) return false; free(cb); return true; } struct bt_csip *bt_csip_new(struct gatt_db *ldb, struct gatt_db *rdb) { struct bt_csip *csip; struct bt_csip_db *db; if (!ldb) return NULL; db = csip_get_db(ldb); if (!db) return NULL; csip = new0(struct bt_csip, 1); csip->ldb = db; csip->ready_cbs = queue_new(); if (!rdb) goto done; db = new0(struct bt_csip_db, 1); db->db = gatt_db_ref(rdb); csip->rdb = db; done: bt_csip_ref(csip); return csip; } static struct bt_csis *csip_get_csis(struct bt_csip *csip) { if (!csip) return NULL; if (csip->rdb->csis) return csip->rdb->csis; csip->rdb->csis = new0(struct bt_csis, 1); csip->rdb->csis->cdb = csip->rdb; return csip->rdb->csis; } static void read_sirk(bool success, uint8_t att_ecode, const uint8_t *value, uint16_t length, void *user_data) { struct bt_csip *csip = user_data; struct bt_csis *csis; struct csis_sirk *sirk; struct iovec iov = { .iov_base = (void *)value, .iov_len = length }; if (!success) { DBG(csip, "Unable to read SIRK: error 0x%02x", att_ecode); return; } csis = csip_get_csis(csip); if (!csis) return; sirk = util_iov_pull_mem(&iov, sizeof(*sirk)); if (!sirk) { DBG(csip, "Invalid size for SIRK: len %u", length); return; } if (!csis->sirk_val) csis->sirk_val = new0(struct csis_sirk, 1); memcpy(csis->sirk_val, sirk, sizeof(*sirk)); } static void read_size(bool success, uint8_t att_ecode, const uint8_t *value, uint16_t length, void *user_data) { struct bt_csip *csip = user_data; struct bt_csis *csis; if (!success) { DBG(csip, "Unable to read Size: error 0x%02x", att_ecode); return; } csis = csip_get_csis(csip); if (!csis) return; csis->size_val = *value; } static void read_rank(bool success, uint8_t att_ecode, const uint8_t *value, uint16_t length, void *user_data) { struct bt_csip *csip = user_data; struct bt_csis *csis; if (!success) { DBG(csip, "Unable to read Rank: error 0x%02x", att_ecode); return; } csis = csip_get_csis(csip); if (!csis) return; csis->rank_val = *value; } static void csip_notify_ready(struct bt_csip *csip) { const struct queue_entry *entry; if (!bt_csip_ref_safe(csip)) return; for (entry = queue_get_entries(csip->ready_cbs); entry; entry = entry->next) { struct bt_csip_ready *ready = entry->data; ready->func(csip, ready->data); } bt_csip_unref(csip); } static void foreach_csis_char(struct gatt_db_attribute *attr, void *user_data) { struct bt_csip *csip = user_data; uint16_t value_handle; bt_uuid_t uuid, uuid_sirk, uuid_size, uuid_rank; struct bt_csis *csis; if (!gatt_db_attribute_get_char_data(attr, NULL, &value_handle, NULL, NULL, &uuid)) return; bt_uuid16_create(&uuid_sirk, CS_SIRK); bt_uuid16_create(&uuid_size, CS_SIZE); bt_uuid16_create(&uuid_rank, CS_RANK); if (!bt_uuid_cmp(&uuid, &uuid_sirk)) { DBG(csip, "SIRK found: handle 0x%04x", value_handle); csis = csip_get_csis(csip); if (!csis) return; csis->sirk = attr; bt_gatt_client_read_value(csip->client, value_handle, read_sirk, csip, NULL); return; } if (!bt_uuid_cmp(&uuid, &uuid_size)) { DBG(csip, "Size found: handle 0x%04x", value_handle); csis = csip_get_csis(csip); if (!csis) return; csis->size = attr; bt_gatt_client_read_value(csip->client, value_handle, read_size, csip, NULL); } if (!bt_uuid_cmp(&uuid, &uuid_rank)) { DBG(csip, "Rank found: handle 0x%04x", value_handle); csis = csip_get_csis(csip); if (!csis) return; csis->rank = attr; bt_gatt_client_read_value(csip->client, value_handle, read_rank, csip, NULL); } } static void foreach_csis_service(struct gatt_db_attribute *attr, void *user_data) { struct bt_csip *csip = user_data; struct bt_csis *csis = csip_get_csis(csip); if (!csis) return; csis->service = attr; gatt_db_service_set_claimed(attr, true); gatt_db_service_foreach_char(attr, foreach_csis_char, csip); } static void csip_idle(void *data) { struct bt_csip *csip = data; csip->idle_id = 0; csip_notify_ready(csip); } bool bt_csip_attach(struct bt_csip *csip, struct bt_gatt_client *client) { bt_uuid_t uuid; if (!sessions) sessions = queue_new(); queue_push_tail(sessions, csip); if (!client) return true; if (csip->client) return false; csip->client = bt_gatt_client_clone(client); if (!csip->client) return false; csip->idle_id = bt_gatt_client_idle_register(csip->client, csip_idle, csip, NULL); bt_uuid16_create(&uuid, CSIS_UUID); gatt_db_foreach_service(csip->rdb->db, &uuid, foreach_csis_service, csip); return true; } static struct csis_sirk *sirk_new(struct bt_csis *csis, struct gatt_db *db, uint8_t type, uint8_t k[16], uint8_t size, uint8_t rank) { struct csis_sirk *sirk; bt_uuid_t uuid; struct gatt_db_attribute *cas; if (!csis) return NULL; if (csis->sirk) sirk = csis->sirk_val; else sirk = new0(struct csis_sirk, 1); sirk->type = type; memcpy(sirk->val, k, sizeof(sirk->val)); csis->sirk_val = sirk; csis->size_val = size; csis->lock_val = 1; csis->rank_val = rank; /* Check if service already active as that means the attributes have * already been registered. */ if (gatt_db_service_get_active(csis->service)) return sirk; /* Populate DB with CSIS attributes */ bt_uuid16_create(&uuid, CSIS_UUID); csis->service = gatt_db_add_service(db, &uuid, true, 10); bt_uuid16_create(&uuid, CS_SIRK); csis->sirk = gatt_db_service_add_characteristic(csis->service, &uuid, BT_ATT_PERM_READ | BT_ATT_PERM_READ_ENCRYPT, BT_GATT_CHRC_PROP_READ, csis_sirk_read, NULL, csis); bt_uuid16_create(&uuid, CS_SIZE); csis->size = gatt_db_service_add_characteristic(csis->service, &uuid, BT_ATT_PERM_READ | BT_ATT_PERM_READ_ENCRYPT, BT_GATT_CHRC_PROP_READ, csis_size_read, NULL, csis); /* Lock */ bt_uuid16_create(&uuid, CS_LOCK); csis->lock = gatt_db_service_add_characteristic(csis->service, &uuid, BT_ATT_PERM_READ | BT_ATT_PERM_READ_ENCRYPT | BT_ATT_PERM_WRITE | BT_ATT_PERM_WRITE_ENCRYPT, BT_GATT_CHRC_PROP_READ | BT_GATT_CHRC_PROP_WRITE | BT_GATT_CHRC_PROP_NOTIFY, csis_lock_read_cb, csis_lock_write_cb, csis); csis->lock_ccc = gatt_db_service_add_ccc(csis->service, BT_ATT_PERM_READ | BT_ATT_PERM_WRITE); /* Rank */ bt_uuid16_create(&uuid, CS_RANK); csis->rank = gatt_db_service_add_characteristic(csis->service, &uuid, BT_ATT_PERM_READ | BT_ATT_PERM_READ_ENCRYPT, BT_GATT_CHRC_PROP_READ, csis_rank_read_cb, NULL, csis); /* Add the CAS service */ bt_uuid16_create(&uuid, 0x1853); cas = gatt_db_add_service(db, &uuid, true, 2); gatt_db_service_add_included(cas, csis->service); gatt_db_service_set_active(cas, true); gatt_db_service_add_included(cas, csis->service); gatt_db_service_set_active(csis->service, true); return sirk; } bool bt_csip_set_sirk(struct bt_csip *csip, bool encrypt, uint8_t k[16], uint8_t size, uint8_t rank, bt_csip_encrypt_func_t func) { uint8_t zero[16] = {}; uint8_t type; if (!csip || !csip->ldb || !memcmp(k, zero, sizeof(zero))) return false; type = encrypt ? BT_CSIP_SIRK_ENCRYPT : BT_CSIP_SIRK_CLEARTEXT; /* In case of encrypted type requires sef key function */ if (type == BT_CSIP_SIRK_ENCRYPT && !func) return false; if (!sirk_new(csip->ldb->csis, csip->ldb->db, type, k, size, rank)) return false; csip->ldb->csis->encrypt = func; return true; } bool bt_csip_get_sirk(struct bt_csip *csip, uint8_t *type, uint8_t k[16], uint8_t *size, uint8_t *rank) { struct bt_csis *csis; if (!csip) return false; csis = csip_get_csis(csip); if (!csis) return false; if (!csis->sirk_val) return false; if (type) *type = csis->sirk_val->type; memcpy(k, csis->sirk_val->val, sizeof(csis->sirk_val->val)); if (size) *size = csis->size_val; if (rank) *rank = csis->rank_val; return true; } unsigned int bt_csip_ready_register(struct bt_csip *csip, bt_csip_ready_func_t func, void *user_data, bt_csip_destroy_func_t destroy) { struct bt_csip_ready *ready; static unsigned int id; if (!csip) return 0; ready = new0(struct bt_csip_ready, 1); ready->id = ++id ? id : ++id; ready->func = func; ready->destroy = destroy; ready->data = user_data; queue_push_tail(csip->ready_cbs, ready); return ready->id; } static bool match_ready_id(const void *data, const void *match_data) { const struct bt_csip_ready *ready = data; unsigned int id = PTR_TO_UINT(match_data); return (ready->id == id); } bool bt_csip_ready_unregister(struct bt_csip *csip, unsigned int id) { struct bt_csip_ready *ready; ready = queue_remove_if(csip->ready_cbs, match_ready_id, UINT_TO_PTR(id)); if (!ready) return false; csip_ready_free(ready); return true; } bluez-5.82/src/shared/PaxHeaders/gatt-helpers.h0000644000000000000000000000005014015011623016444 xustar0020 atime=1743515885 20 ctime=1743591277 bluez-5.82/src/shared/gatt-helpers.h0000644000000000000000000000722614015011623016134 0ustar00rootroot/* SPDX-License-Identifier: LGPL-2.1-or-later */ /* * * BlueZ - Bluetooth protocol stack for Linux * * Copyright (C) 2014 Google Inc. * * */ /* This file defines helpers for performing client-side procedures defined by * the Generic Attribute Profile. */ #include #include struct bt_gatt_result; struct bt_gatt_iter { struct bt_gatt_result *result; uint16_t pos; }; unsigned int bt_gatt_result_service_count(struct bt_gatt_result *result); unsigned int bt_gatt_result_characteristic_count(struct bt_gatt_result *result); unsigned int bt_gatt_result_descriptor_count(struct bt_gatt_result *result); unsigned int bt_gatt_result_included_count(struct bt_gatt_result *result); bool bt_gatt_iter_init(struct bt_gatt_iter *iter, struct bt_gatt_result *result); bool bt_gatt_iter_next_service(struct bt_gatt_iter *iter, uint16_t *start_handle, uint16_t *end_handle, uint8_t uuid[16]); bool bt_gatt_iter_next_characteristic(struct bt_gatt_iter *iter, uint16_t *start_handle, uint16_t *end_handle, uint16_t *value_handle, uint8_t *properties, uint8_t uuid[16]); bool bt_gatt_iter_next_descriptor(struct bt_gatt_iter *iter, uint16_t *handle, uint8_t uuid[16]); bool bt_gatt_iter_next_included_service(struct bt_gatt_iter *iter, uint16_t *handle, uint16_t *start_handle, uint16_t *end_handle, uint8_t uuid[16]); bool bt_gatt_iter_next_read_by_type(struct bt_gatt_iter *iter, uint16_t *handle, uint16_t *length, const uint8_t **value); typedef void (*bt_gatt_destroy_func_t)(void *user_data); typedef void (*bt_gatt_result_callback_t)(bool success, uint8_t att_ecode, void *user_data); typedef void (*bt_gatt_request_callback_t)(bool success, uint8_t att_ecode, struct bt_gatt_result *result, void *user_data); struct bt_gatt_request; struct bt_gatt_request *bt_gatt_request_ref(struct bt_gatt_request *req); void bt_gatt_request_unref(struct bt_gatt_request *req); void bt_gatt_request_cancel(struct bt_gatt_request *req); unsigned int bt_gatt_exchange_mtu(struct bt_att *att, uint16_t client_rx_mtu, bt_gatt_result_callback_t callback, void *user_data, bt_gatt_destroy_func_t destroy); struct bt_gatt_request *bt_gatt_discover_all_primary_services( struct bt_att *att, bt_uuid_t *uuid, bt_gatt_request_callback_t callback, void *user_data, bt_gatt_destroy_func_t destroy); struct bt_gatt_request *bt_gatt_discover_primary_services( struct bt_att *att, bt_uuid_t *uuid, uint16_t start, uint16_t end, bt_gatt_request_callback_t callback, void *user_data, bt_gatt_destroy_func_t destroy); struct bt_gatt_request *bt_gatt_discover_secondary_services( struct bt_att *att, bt_uuid_t *uuid, uint16_t start, uint16_t end, bt_gatt_request_callback_t callback, void *user_data, bt_gatt_destroy_func_t destroy); struct bt_gatt_request *bt_gatt_discover_included_services(struct bt_att *att, uint16_t start, uint16_t end, bt_gatt_request_callback_t callback, void *user_data, bt_gatt_destroy_func_t destroy); struct bt_gatt_request *bt_gatt_discover_characteristics(struct bt_att *att, uint16_t start, uint16_t end, bt_gatt_request_callback_t callback, void *user_data, bt_gatt_destroy_func_t destroy); struct bt_gatt_request *bt_gatt_discover_descriptors(struct bt_att *att, uint16_t start, uint16_t end, bt_gatt_request_callback_t callback, void *user_data, bt_gatt_destroy_func_t destroy); bool bt_gatt_read_by_type(struct bt_att *att, uint16_t start, uint16_t end, const bt_uuid_t *uuid, bt_gatt_request_callback_t callback, void *user_data, bt_gatt_destroy_func_t destroy); bluez-5.82/src/shared/PaxHeaders/ringbuf.c0000644000000000000000000000005014643061455015513 xustar0020 atime=1743515868 20 ctime=1743591277 bluez-5.82/src/shared/ringbuf.c0000644000000000000000000001311214643061455015172 0ustar00rootroot// SPDX-License-Identifier: LGPL-2.1-or-later /* * * BlueZ - Bluetooth protocol stack for Linux * * Copyright (C) 2012-2014 Intel Corporation. All rights reserved. * * */ #ifdef HAVE_CONFIG_H #include #endif #define _GNU_SOURCE #include #include #include #include #include "src/shared/util.h" #include "src/shared/ringbuf.h" #ifndef MIN #define MIN(x,y) ((x)<(y)?(x):(y)) #endif struct ringbuf { void *buffer; size_t size; size_t in; size_t out; ringbuf_tracing_func_t in_tracing; void *in_data; }; #define RINGBUF_RESET 0 /* Find last (most siginificant) set bit */ static inline unsigned int fls(unsigned int x) { return x ? sizeof(x) * 8 - __builtin_clz(x) : 0; } /* Round up to nearest power of two */ static inline unsigned int align_power2(unsigned int u) { return 1 << fls(u - 1); } struct ringbuf *ringbuf_new(size_t size) { struct ringbuf *ringbuf; size_t real_size; if (size < 2 || size > UINT_MAX) return NULL; /* Find the next power of two for size */ real_size = align_power2(size); ringbuf = new0(struct ringbuf, 1); ringbuf->buffer = malloc(real_size); if (!ringbuf->buffer) { free(ringbuf); return NULL; } ringbuf->size = real_size; ringbuf->in = RINGBUF_RESET; ringbuf->out = RINGBUF_RESET; return ringbuf; } void ringbuf_free(struct ringbuf *ringbuf) { if (!ringbuf) return; free(ringbuf->buffer); free(ringbuf); } bool ringbuf_set_input_tracing(struct ringbuf *ringbuf, ringbuf_tracing_func_t callback, void *user_data) { if (!ringbuf) return false; ringbuf->in_tracing = callback; ringbuf->in_data = user_data; return true; } size_t ringbuf_capacity(struct ringbuf *ringbuf) { if (!ringbuf) return 0; return ringbuf->size; } size_t ringbuf_len(struct ringbuf *ringbuf) { if (!ringbuf) return 0; return ringbuf->in - ringbuf->out; } size_t ringbuf_drain(struct ringbuf *ringbuf, size_t count) { size_t len; if (!ringbuf) return 0; len = MIN(count, ringbuf->in - ringbuf->out); if (!len) return 0; ringbuf->out += len; if (ringbuf->out == ringbuf->in) { ringbuf->in = RINGBUF_RESET; ringbuf->out = RINGBUF_RESET; } return len; } void *ringbuf_peek(struct ringbuf *ringbuf, size_t offset, size_t *len_nowrap) { if (!ringbuf) return NULL; offset = (ringbuf->out + offset) & (ringbuf->size - 1); if (len_nowrap) { size_t len = ringbuf->in - ringbuf->out; *len_nowrap = MIN(len, ringbuf->size - offset); } return ringbuf->buffer + offset; } ssize_t ringbuf_write(struct ringbuf *ringbuf, int fd) { size_t len, offset, end; struct iovec iov[2]; ssize_t consumed; if (!ringbuf || fd < 0) return -1; /* Determine how much data is available */ len = ringbuf->in - ringbuf->out; if (!len) return 0; /* Grab data from buffer starting at offset until the end */ offset = ringbuf->out & (ringbuf->size - 1); end = MIN(len, ringbuf->size - offset); iov[0].iov_base = ringbuf->buffer + offset; iov[0].iov_len = end; /* Use second vector for remainder from the beginning */ iov[1].iov_base = ringbuf->buffer; iov[1].iov_len = len - end; consumed = writev(fd, iov, 2); if (consumed < 0) return -1; ringbuf->out += consumed; if (ringbuf->out == ringbuf->in) { ringbuf->in = RINGBUF_RESET; ringbuf->out = RINGBUF_RESET; } return consumed; } size_t ringbuf_avail(struct ringbuf *ringbuf) { if (!ringbuf) return 0; return ringbuf->size - ringbuf->in + ringbuf->out; } int ringbuf_printf(struct ringbuf *ringbuf, const char *format, ...) { va_list ap; int len; va_start(ap, format); len = ringbuf_vprintf(ringbuf, format, ap); va_end(ap); return len; } int ringbuf_vprintf(struct ringbuf *ringbuf, const char *format, va_list ap) { size_t avail, offset, end; char *str; int len; if (!ringbuf || !format) return -1; /* Determine maximum length available for string */ avail = ringbuf->size - ringbuf->in + ringbuf->out; if (!avail) return -1; len = vasprintf(&str, format, ap); if (len < 0) return -1; if ((size_t) len > avail) { free(str); return -1; } /* Determine possible length of string before wrapping */ offset = ringbuf->in & (ringbuf->size - 1); end = MIN((size_t) len, ringbuf->size - offset); memcpy(ringbuf->buffer + offset, str, end); if (ringbuf->in_tracing) ringbuf->in_tracing(ringbuf->buffer + offset, end, ringbuf->in_data); if ((size_t) len > end) { /* Put the remainder of string at the beginning */ memcpy(ringbuf->buffer, str + end, len - end); if (ringbuf->in_tracing) ringbuf->in_tracing(ringbuf->buffer, len - end, ringbuf->in_data); } free(str); ringbuf->in += len; return len; } ssize_t ringbuf_read(struct ringbuf *ringbuf, int fd) { size_t avail, offset, end; struct iovec iov[2]; ssize_t consumed; if (!ringbuf || fd < 0) return -1; /* Determine how much can actually be consumed */ avail = ringbuf->size - ringbuf->in + ringbuf->out; if (!avail) return -1; /* Determine how much to consume before wrapping */ offset = ringbuf->in & (ringbuf->size - 1); end = MIN(avail, ringbuf->size - offset); iov[0].iov_base = ringbuf->buffer + offset; iov[0].iov_len = end; /* Now put the remainder into the second vector */ iov[1].iov_base = ringbuf->buffer; iov[1].iov_len = avail - end; consumed = readv(fd, iov, 2); if (consumed < 0) return -1; if (ringbuf->in_tracing) { size_t len = MIN((size_t) consumed, end); ringbuf->in_tracing(ringbuf->buffer + offset, len, ringbuf->in_data); if (consumed - len > 0) ringbuf->in_tracing(ringbuf->buffer, consumed - len, ringbuf->in_data); } ringbuf->in += consumed; return consumed; } bluez-5.82/src/shared/PaxHeaders/bap-defs.h0000644000000000000000000000005014643061455015545 xustar0020 atime=1743515790 20 ctime=1743591277 bluez-5.82/src/shared/bap-defs.h0000644000000000000000000000420714643061455015231 0ustar00rootroot/* SPDX-License-Identifier: LGPL-2.1-or-later */ /* * * BlueZ - Bluetooth protocol stack for Linux * * Copyright (C) 2022 Intel Corporation. All rights reserved. * Copyright 2023-2024 NXP * */ #ifndef SRC_SHARED_BAP_DEFS_H_ #define SRC_SHARED_BAP_DEFS_H_ #ifndef __packed #define __packed __attribute__((packed)) #endif #define BT_BAP_SINK BIT(0) #define BT_BAP_SOURCE BIT(1) #define BT_BAP_BCAST_SOURCE BIT(2) #define BT_BAP_BCAST_SINK BIT(3) #define BT_BAP_STREAM_TYPE_UCAST 0x01 #define BT_BAP_STREAM_TYPE_BCAST 0x02 #define BT_BAP_STREAM_STATE_IDLE 0x00 #define BT_BAP_STREAM_STATE_CONFIG 0x01 #define BT_BAP_STREAM_STATE_QOS 0x02 #define BT_BAP_STREAM_STATE_ENABLING 0x03 #define BT_BAP_STREAM_STATE_STREAMING 0x04 #define BT_BAP_STREAM_STATE_DISABLING 0x05 #define BT_BAP_STREAM_STATE_RELEASING 0x06 #define BT_BAP_CONFIG_LATENCY_LOW 0x01 #define BT_BAP_CONFIG_LATENCY_BALANCED 0x02 #define BT_BAP_CONFIG_LATENCY_HIGH 0x03 #define BT_BAP_CONFIG_PHY_1M 0x01 #define BT_BAP_CONFIG_PHY_2M 0x02 #define BT_BAP_CONFIG_PHY_CODEC 0x03 struct bt_bap_codec { uint8_t id; uint16_t cid; uint16_t vid; } __packed; struct bt_ltv { uint8_t len; uint8_t type; uint8_t value[]; } __packed; struct bt_bap_io_qos { uint32_t interval; /* Frame interval */ uint16_t latency; /* Transport Latency */ uint16_t sdu; /* Maximum SDU Size */ uint8_t phy; /* PHY */ uint8_t rtn; /* Retransmission Effort */ }; struct bt_bap_ucast_qos { uint8_t cig_id; uint8_t cis_id; uint8_t framing; /* Frame framing */ uint32_t delay; /* Presentation Delay */ uint8_t target_latency; /* Target Latency */ struct bt_bap_io_qos io_qos; }; struct bt_bap_bcast_qos { uint8_t big; uint8_t bis; uint8_t sync_factor; uint8_t packing; uint8_t framing; uint8_t encryption; struct iovec *bcode; uint8_t options; uint16_t skip; uint16_t sync_timeout; uint8_t sync_cte_type; uint8_t mse; uint16_t timeout; uint8_t pa_sync; struct bt_bap_io_qos io_qos; uint32_t delay; /* Presentation Delay */ }; struct bt_bap_qos { union { struct bt_bap_ucast_qos ucast; struct bt_bap_bcast_qos bcast; }; }; #endif /* SRC_SHARED_BAP_DEFS_H_ */ bluez-5.82/src/shared/PaxHeaders/hci-crypto.c0000644000000000000000000000005014015011623016121 xustar0020 atime=1743515871 20 ctime=1743591277 bluez-5.82/src/shared/hci-crypto.c0000644000000000000000000000661714015011623015614 0ustar00rootroot// SPDX-License-Identifier: LGPL-2.1-or-later /* * * BlueZ - Bluetooth protocol stack for Linux * * Copyright (C) 2013-2014 Intel Corporation * * */ #ifdef HAVE_CONFIG_H #include #endif #include #include "monitor/bt.h" #include "src/shared/util.h" #include "src/shared/hci.h" #include "src/shared/hci-crypto.h" struct crypto_data { uint8_t size; bt_hci_crypto_func_t callback; void *user_data; }; static void le_encrypt_callback(const void *response, uint8_t size, void *user_data) { struct crypto_data *data = user_data; const struct bt_hci_rsp_le_encrypt *rsp = response; if (rsp->status) { data->callback(NULL, 0, data->user_data); return; } data->callback(rsp->data, data->size, data->user_data); } static bool le_encrypt(struct bt_hci *hci, uint8_t size, const uint8_t key[16], const uint8_t plaintext[16], bt_hci_crypto_func_t callback, void *user_data) { struct crypto_data *data; struct bt_hci_cmd_le_encrypt cmd; if (!callback || !size || size > 16) return false; memcpy(cmd.key, key, 16); memcpy(cmd.plaintext, plaintext, 16); data = new0(struct crypto_data, 1); data->size = size; data->callback = callback; data->user_data = user_data; if (!bt_hci_send(hci, BT_HCI_CMD_LE_ENCRYPT, &cmd, sizeof(cmd), le_encrypt_callback, data, free)) { free(data); return false; } return true; } static void prand_callback(const void *response, uint8_t size, void *user_data) { struct crypto_data *data = user_data; const struct bt_hci_rsp_le_rand *rsp = response; uint8_t prand[3]; if (rsp->status) { data->callback(NULL, 0, data->user_data); return; } prand[0] = (rsp->number & 0xff0000) >> 16; prand[1] = (rsp->number & 0x00ff00) >> 8; prand[2] = (rsp->number & 0x00003f) | 0x40; data->callback(prand, 3, data->user_data); } bool bt_hci_crypto_prand(struct bt_hci *hci, bt_hci_crypto_func_t callback, void *user_data) { struct crypto_data *data; if (!callback) return false; data = new0(struct crypto_data, 1); data->callback = callback; data->user_data = user_data; if (!bt_hci_send(hci, BT_HCI_CMD_LE_RAND, NULL, 0, prand_callback, data, free)) { free(data); return false; } return true; } bool bt_hci_crypto_e(struct bt_hci *hci, const uint8_t key[16], const uint8_t plaintext[16], bt_hci_crypto_func_t callback, void *user_data) { return le_encrypt(hci, 16, key, plaintext, callback, user_data); } bool bt_hci_crypto_d1(struct bt_hci *hci, const uint8_t k[16], uint16_t d, uint16_t r, bt_hci_crypto_func_t callback, void *user_data) { uint8_t dp[16]; /* d' = padding || r || d */ dp[0] = d & 0xff; dp[1] = d >> 8; dp[2] = r & 0xff; dp[3] = r >> 8; memset(dp + 4, 0, 12); /* d1(k, d, r) = e(k, d') */ return le_encrypt(hci, 16, k, dp, callback, user_data); } bool bt_hci_crypto_dm(struct bt_hci *hci, const uint8_t k[16], const uint8_t r[8], bt_hci_crypto_func_t callback, void *user_data) { uint8_t rp[16]; /* r' = padding || r */ memcpy(rp, r, 8); memset(rp + 8, 0, 8); /* dm(k, r) = e(k, r') mod 2^16 */ return le_encrypt(hci, 8, k, rp, callback, user_data); } bool bt_hci_crypto_ah(struct bt_hci *hci, const uint8_t k[16], const uint8_t r[3], bt_hci_crypto_func_t callback, void *user_data) { uint8_t rp[16]; /* r' = padding || r */ memcpy(rp, r, 3); memset(rp + 3, 0, 13); /* ah(k, r) = e(k, r') mod 2^24 */ return le_encrypt(hci, 3, k, rp, callback, user_data); } bluez-5.82/src/shared/PaxHeaders/btsnoop.h0000644000000000000000000000005014766002272015546 xustar0020 atime=1743515578 20 ctime=1743591277 bluez-5.82/src/shared/btsnoop.h0000644000000000000000000000644214766002272015235 0ustar00rootroot/* SPDX-License-Identifier: LGPL-2.1-or-later */ /* * * BlueZ - Bluetooth protocol stack for Linux * * Copyright (C) 2012-2014 Intel Corporation. All rights reserved. * * */ #include #include #include #define BTSNOOP_FORMAT_INVALID 0 #define BTSNOOP_FORMAT_HCI 1001 #define BTSNOOP_FORMAT_UART 1002 #define BTSNOOP_FORMAT_BCSP 1003 #define BTSNOOP_FORMAT_3WIRE 1004 #define BTSNOOP_FORMAT_MONITOR 2001 #define BTSNOOP_FORMAT_SIMULATOR 2002 #define BTSNOOP_FLAG_PKLG_SUPPORT (1 << 0) #define BTSNOOP_OPCODE_NEW_INDEX 0 #define BTSNOOP_OPCODE_DEL_INDEX 1 #define BTSNOOP_OPCODE_COMMAND_PKT 2 #define BTSNOOP_OPCODE_EVENT_PKT 3 #define BTSNOOP_OPCODE_ACL_TX_PKT 4 #define BTSNOOP_OPCODE_ACL_RX_PKT 5 #define BTSNOOP_OPCODE_SCO_TX_PKT 6 #define BTSNOOP_OPCODE_SCO_RX_PKT 7 #define BTSNOOP_OPCODE_OPEN_INDEX 8 #define BTSNOOP_OPCODE_CLOSE_INDEX 9 #define BTSNOOP_OPCODE_INDEX_INFO 10 #define BTSNOOP_OPCODE_VENDOR_DIAG 11 #define BTSNOOP_OPCODE_SYSTEM_NOTE 12 #define BTSNOOP_OPCODE_USER_LOGGING 13 #define BTSNOOP_OPCODE_CTRL_OPEN 14 #define BTSNOOP_OPCODE_CTRL_CLOSE 15 #define BTSNOOP_OPCODE_CTRL_COMMAND 16 #define BTSNOOP_OPCODE_CTRL_EVENT 17 #define BTSNOOP_OPCODE_ISO_TX_PKT 18 #define BTSNOOP_OPCODE_ISO_RX_PKT 19 #define BTSNOOP_MAX_PACKET_SIZE (1486 + 4) #define BTSNOOP_TYPE_PRIMARY 0 #define BTSNOOP_TYPE_AMP 1 #define BTSNOOP_BUS_VIRTUAL 0 #define BTSNOOP_BUS_USB 1 #define BTSNOOP_BUS_PCCARD 2 #define BTSNOOP_BUS_UART 3 #define BTSNOOP_BUS_RS232 4 #define BTSNOOP_BUS_PCI 5 #define BTSNOOP_BUS_SDIO 6 #define BTSNOOP_BUS_SPI 7 #define BTSNOOP_BUS_I2C 8 #define BTSNOOP_BUS_SMD 9 #define BTSNOOP_BUS_VIRTIO 10 #define BTSNOOP_BUS_IPC 11 struct btsnoop_opcode_new_index { uint8_t type; uint8_t bus; uint8_t bdaddr[6]; char name[8]; } __attribute__((packed)); struct btsnoop_opcode_index_info { uint8_t bdaddr[6]; uint16_t manufacturer; } __attribute__((packed)); #define BTSNOOP_PRIORITY_EMERG 0 #define BTSNOOP_PRIORITY_ALERT 1 #define BTSNOOP_PRIORITY_CRIT 2 #define BTSNOOP_PRIORITY_ERR 3 #define BTSNOOP_PRIORITY_WARNING 4 #define BTSNOOP_PRIORITY_NOTICE 5 #define BTSNOOP_PRIORITY_INFO 6 #define BTSNOOP_PRIORITY_DEBUG 7 struct btsnoop_opcode_user_logging { uint8_t priority; uint8_t ident_len; } __attribute__((packed)); struct btsnoop; struct btsnoop *btsnoop_open(const char *path, unsigned long flags); struct btsnoop *btsnoop_create(const char *path, size_t max_size, unsigned int max_count, uint32_t format); struct btsnoop *btsnoop_ref(struct btsnoop *btsnoop); void btsnoop_unref(struct btsnoop *btsnoop); uint32_t btsnoop_get_format(struct btsnoop *btsnoop); bool btsnoop_write(struct btsnoop *btsnoop, struct timeval *tv, uint32_t flags, uint32_t drops, const void *data, uint16_t size); bool btsnoop_write_hci(struct btsnoop *btsnoop, struct timeval *tv, uint16_t index, uint16_t opcode, uint32_t drops, const void *data, uint16_t size); bool btsnoop_write_phy(struct btsnoop *btsnoop, struct timeval *tv, uint16_t frequency, const void *data, uint16_t size); bool btsnoop_read_hci(struct btsnoop *btsnoop, struct timeval *tv, uint16_t *index, uint16_t *opcode, void *data, uint16_t *size); bool btsnoop_read_phy(struct btsnoop *btsnoop, struct timeval *tv, uint16_t *frequency, void *data, uint16_t *size); bluez-5.82/src/shared/PaxHeaders/mgmt.h0000644000000000000000000000005014214376354015031 xustar0020 atime=1743515801 20 ctime=1743591277 bluez-5.82/src/shared/mgmt.h0000644000000000000000000000646614214376354014526 0ustar00rootroot/* SPDX-License-Identifier: LGPL-2.1-or-later */ /* * * BlueZ - Bluetooth protocol stack for Linux * * Copyright (C) 2012-2014 Intel Corporation. All rights reserved. * * */ #include #include #define MGMT_VERSION(v, r) (((v) << 16) + (r)) typedef void (*mgmt_destroy_func_t)(void *user_data); struct mgmt; struct mgmt_tlv_list; struct mgmt *mgmt_new(int fd); struct mgmt *mgmt_new_default(void); struct mgmt *mgmt_ref(struct mgmt *mgmt); void mgmt_unref(struct mgmt *mgmt); typedef void (*mgmt_debug_func_t)(const char *str, void *user_data); bool mgmt_set_debug(struct mgmt *mgmt, mgmt_debug_func_t callback, void *user_data, mgmt_destroy_func_t destroy); bool mgmt_set_close_on_unref(struct mgmt *mgmt, bool do_close); typedef void (*mgmt_request_func_t)(uint8_t status, uint16_t length, const void *param, void *user_data); struct mgmt_tlv_list *mgmt_tlv_list_new(void); void mgmt_tlv_list_free(struct mgmt_tlv_list *tlv_list); bool mgmt_tlv_add(struct mgmt_tlv_list *tlv_list, uint16_t type, uint8_t length, void *value); #define mgmt_tlv_add_fixed(_list, _type, _value) \ mgmt_tlv_add(_list, _type, sizeof(*(_value)), _value) struct mgmt_tlv_list *mgmt_tlv_list_load_from_buf(const uint8_t *buf, uint16_t len); typedef void (*mgmt_tlv_list_foreach_func_t)(void *data, void *user_data); void mgmt_tlv_list_foreach(struct mgmt_tlv_list *tlv_list, mgmt_tlv_list_foreach_func_t callback, void *user_data); unsigned int mgmt_send_tlv(struct mgmt *mgmt, uint16_t opcode, uint16_t index, struct mgmt_tlv_list *tlv_list, mgmt_request_func_t callback, void *user_data, mgmt_destroy_func_t destroy); unsigned int mgmt_send(struct mgmt *mgmt, uint16_t opcode, uint16_t index, uint16_t length, const void *param, mgmt_request_func_t callback, void *user_data, mgmt_destroy_func_t destroy); unsigned int mgmt_send_timeout(struct mgmt *mgmt, uint16_t opcode, uint16_t index, uint16_t length, const void *param, mgmt_request_func_t callback, void *user_data, mgmt_destroy_func_t destroy, int timeout); unsigned int mgmt_send_nowait(struct mgmt *mgmt, uint16_t opcode, uint16_t index, uint16_t length, const void *param, mgmt_request_func_t callback, void *user_data, mgmt_destroy_func_t destroy); unsigned int mgmt_reply(struct mgmt *mgmt, uint16_t opcode, uint16_t index, uint16_t length, const void *param, mgmt_request_func_t callback, void *user_data, mgmt_destroy_func_t destroy); unsigned int mgmt_reply_timeout(struct mgmt *mgmt, uint16_t opcode, uint16_t index, uint16_t length, const void *param, mgmt_request_func_t callback, void *user_data, mgmt_destroy_func_t destroy, int timeout); bool mgmt_cancel(struct mgmt *mgmt, unsigned int id); bool mgmt_cancel_index(struct mgmt *mgmt, uint16_t index); bool mgmt_cancel_all(struct mgmt *mgmt); typedef void (*mgmt_notify_func_t)(uint16_t index, uint16_t length, const void *param, void *user_data); unsigned int mgmt_register(struct mgmt *mgmt, uint16_t event, uint16_t index, mgmt_notify_func_t callback, void *user_data, mgmt_destroy_func_t destroy); bool mgmt_unregister(struct mgmt *mgmt, unsigned int id); bool mgmt_unregister_index(struct mgmt *mgmt, uint16_t index); bool mgmt_unregister_all(struct mgmt *mgmt); uint16_t mgmt_get_mtu(struct mgmt *mgmt); bluez-5.82/src/shared/PaxHeaders/bap-debug.h0000644000000000000000000000005014536422313015705 xustar0020 atime=1743515790 20 ctime=1743591277 bluez-5.82/src/shared/bap-debug.h0000644000000000000000000000067414536422313015375 0ustar00rootroot/* SPDX-License-Identifier: LGPL-2.1-or-later */ /* * * BlueZ - Bluetooth protocol stack for Linux * * Copyright (C) 2023 Intel Corporation. */ bool bt_bap_debug_caps(void *data, size_t len, util_debug_func_t func, void *user_data); bool bt_bap_debug_config(void *data, size_t len, util_debug_func_t func, void *user_data); bool bt_bap_debug_metadata(void *data, size_t len, util_debug_func_t func, void *user_data); bluez-5.82/src/shared/PaxHeaders/pcap.h0000644000000000000000000000005014015011623014770 xustar0020 atime=1743515876 20 ctime=1743591277 bluez-5.82/src/shared/pcap.h0000644000000000000000000000151714015011623014455 0ustar00rootroot/* SPDX-License-Identifier: LGPL-2.1-or-later */ /* * * BlueZ - Bluetooth protocol stack for Linux * * Copyright (C) 2012-2014 Intel Corporation. All rights reserved. * * */ #include #include #include #define PCAP_TYPE_INVALID 0 #define PCAP_TYPE_USER0 147 #define PCAP_TYPE_PPI 192 #define PCAP_TYPE_BLUETOOTH_LE_LL 251 struct pcap; struct pcap *pcap_open(const char *path); struct pcap *pcap_ref(struct pcap *pcap); void pcap_unref(struct pcap *pcap); uint32_t pcap_get_type(struct pcap *pcap); uint32_t pcap_get_snaplen(struct pcap *pcap); bool pcap_read(struct pcap *pcap, struct timeval *tv, void *data, uint32_t size, uint32_t *len); bool pcap_read_ppi(struct pcap *pcap, struct timeval *tv, uint32_t *type, void *data, uint32_t size, uint32_t *offset, uint32_t *len); bluez-5.82/src/shared/PaxHeaders/mainloop-glib.c0000644000000000000000000000005014015011623016571 xustar0020 atime=1743515943 20 ctime=1743591277 bluez-5.82/src/shared/mainloop-glib.c0000644000000000000000000000340414015011623016253 0ustar00rootroot// SPDX-License-Identifier: LGPL-2.1-or-later /* * * BlueZ - Bluetooth protocol stack for Linux * * Copyright (C) 2018 Intel Corporation * * */ #ifdef HAVE_CONFIG_H #include #endif #define _GNU_SOURCE #include #include #include #include #include #include #include #include #include "mainloop.h" #include "mainloop-notify.h" #include "io.h" static GMainLoop *main_loop; static int exit_status; void mainloop_init(void) { main_loop = g_main_loop_new(NULL, FALSE); mainloop_notify_init(); } void mainloop_quit(void) { if (!main_loop) return; g_main_loop_quit(main_loop); mainloop_sd_notify("STOPPING=1"); } void mainloop_exit_success(void) { exit_status = EXIT_SUCCESS; mainloop_quit(); } void mainloop_exit_failure(void) { exit_status = EXIT_FAILURE; mainloop_quit(); } int mainloop_run(void) { if (!main_loop) return -EINVAL; g_main_loop_run(main_loop); g_main_loop_unref(main_loop); main_loop = NULL; mainloop_notify_exit(); return exit_status; } int mainloop_add_fd(int fd, uint32_t events, mainloop_event_func callback, void *user_data, mainloop_destroy_func destroy) { return -ENOSYS; } int mainloop_modify_fd(int fd, uint32_t events) { return -ENOSYS; } int mainloop_remove_fd(int fd) { return -ENOSYS; } int mainloop_add_timeout(unsigned int msec, mainloop_timeout_func callback, void *user_data, mainloop_destroy_func destroy) { return -ENOSYS; } int mainloop_modify_timeout(int fd, unsigned int msec) { return -ENOSYS; } int mainloop_remove_timeout(int id) { return -ENOSYS; } int mainloop_set_signal(sigset_t *mask, mainloop_signal_func callback, void *user_data, mainloop_destroy_func destroy) { return -ENOSYS; } bluez-5.82/src/shared/PaxHeaders/gap.c0000644000000000000000000000005014015011623014607 xustar0020 atime=1743515902 20 ctime=1743591277 bluez-5.82/src/shared/gap.c0000644000000000000000000001121714015011623014272 0ustar00rootroot// SPDX-License-Identifier: LGPL-2.1-or-later /* * * BlueZ - Bluetooth protocol stack for Linux * * Copyright (C) 2012-2014 Intel Corporation. All rights reserved. * * */ #ifdef HAVE_CONFIG_H #include #endif #include #include #include "lib/bluetooth.h" #include "lib/mgmt.h" #include "src/shared/util.h" #include "src/shared/queue.h" #include "src/shared/mgmt.h" #include "src/shared/gap.h" #define FLAG_MGMT_CONN_CONTROL (0 << 1) struct bt_gap { int ref_count; uint16_t index; struct mgmt *mgmt; uint8_t mgmt_version; uint16_t mgmt_revision; bool mgmt_ready; unsigned long flags; bt_gap_ready_func_t ready_handler; bt_gap_destroy_func_t ready_destroy; void *ready_data; uint8_t static_addr[6]; uint8_t local_irk[16]; struct queue *irk_list; }; struct irk_entry { uint8_t addr_type; uint8_t addr[6]; uint8_t key[16]; }; static void irk_entry_free(void *data) { struct irk_entry *irk = data; free(irk); } static void ready_status(struct bt_gap *gap, bool status) { gap->mgmt_ready = status; if (gap->ready_handler) gap->ready_handler(status, gap->ready_data); } static void read_commands_complete(uint8_t status, uint16_t length, const void *param, void *user_data) { struct bt_gap *gap = user_data; const struct mgmt_rp_read_commands *rp = param; uint16_t num_commands, num_events; size_t expected_len; int i; if (status != MGMT_STATUS_SUCCESS) { ready_status(gap, false); return; } if (length < sizeof(*rp)) { ready_status(gap, false); return; } num_commands = le16_to_cpu(rp->num_commands); num_events = le16_to_cpu(rp->num_events); expected_len = sizeof(*rp) + num_commands * sizeof(uint16_t) + num_events * sizeof(uint16_t); if (length < expected_len) { ready_status(gap, false); return; } for (i = 0; i < num_commands; i++) { uint16_t op = get_le16(rp->opcodes + i); if (op == MGMT_OP_ADD_DEVICE) gap->flags |= FLAG_MGMT_CONN_CONTROL; } ready_status(gap, true); } static void read_version_complete(uint8_t status, uint16_t length, const void *param, void *user_data) { struct bt_gap *gap = user_data; const struct mgmt_rp_read_version *rp = param; if (status != MGMT_STATUS_SUCCESS) { ready_status(gap, false); return; } if (length < sizeof(*rp)) { ready_status(gap, false); return; } gap->mgmt_version = rp->version; gap->mgmt_revision = le16_to_cpu(rp->revision); if (!mgmt_send(gap->mgmt, MGMT_OP_READ_COMMANDS, MGMT_INDEX_NONE, 0, NULL, read_commands_complete, gap, NULL)) { ready_status(gap, false); return; } } struct bt_gap *bt_gap_new_default(void) { return bt_gap_new_index(0x0000); } struct bt_gap *bt_gap_new_index(uint16_t index) { struct bt_gap *gap; if (index == MGMT_INDEX_NONE) return NULL; gap = new0(struct bt_gap, 1); gap->index = index; gap->mgmt = mgmt_new_default(); if (!gap->mgmt) { free(gap); return NULL; } gap->irk_list = queue_new(); gap->mgmt_ready = false; if (!mgmt_send(gap->mgmt, MGMT_OP_READ_VERSION, MGMT_INDEX_NONE, 0, NULL, read_version_complete, gap, NULL)) { mgmt_unref(gap->mgmt); return NULL; } return bt_gap_ref(gap); } struct bt_gap *bt_gap_ref(struct bt_gap *gap) { if (!gap) return NULL; __sync_fetch_and_add(&gap->ref_count, 1); return gap; } void bt_gap_unref(struct bt_gap *gap) { if (!gap) return; if (__sync_sub_and_fetch(&gap->ref_count, 1)) return; gap->mgmt_ready = false; mgmt_cancel_all(gap->mgmt); mgmt_unregister_all(gap->mgmt); if (gap->ready_destroy) gap->ready_destroy(gap->ready_data); queue_destroy(gap->irk_list, irk_entry_free); mgmt_unref(gap->mgmt); free(gap); } bool bt_gap_set_ready_handler(struct bt_gap *gap, bt_gap_ready_func_t handler, void *user_data, bt_gap_destroy_func_t destroy) { if (!gap) return false; if (gap->ready_destroy) gap->ready_destroy(gap->ready_data); gap->ready_handler = handler; gap->ready_destroy = destroy; gap->ready_data = user_data; return true; } bool bt_gap_set_static_addr(struct bt_gap *gap, uint8_t addr[6]) { if (!gap) return false; memcpy(gap->static_addr, addr, 6); return true; } bool bt_gap_set_local_irk(struct bt_gap *gap, uint8_t key[16]) { if (!gap) return false; memcpy(gap->local_irk, key, 16); return true; } bool bt_gap_add_peer_irk(struct bt_gap *gap, uint8_t addr_type, uint8_t addr[6], uint8_t key[16]) { struct irk_entry *irk; if (!gap) return false; if (addr_type > BT_GAP_ADDR_TYPE_LE_RANDOM) return false; irk = new0(struct irk_entry, 1); irk->addr_type = addr_type; memcpy(irk->addr, addr, 6); memcpy(irk->key, key, 16); if (!queue_push_tail(gap->irk_list, irk)) { free(irk); return false; } return true; } bluez-5.82/src/shared/PaxHeaders/hfp.h0000644000000000000000000000005014131623652014634 xustar0020 atime=1743515872 20 ctime=1743591277 bluez-5.82/src/shared/hfp.h0000644000000000000000000001137014131623652014317 0ustar00rootroot/* SPDX-License-Identifier: LGPL-2.1-or-later */ /* * * BlueZ - Bluetooth protocol stack for Linux * * Copyright (C) 2012-2014 Intel Corporation. All rights reserved. * * */ #include enum hfp_result { HFP_RESULT_OK = 0, HFP_RESULT_CONNECT = 1, HFP_RESULT_RING = 2, HFP_RESULT_NO_CARRIER = 3, HFP_RESULT_ERROR = 4, HFP_RESULT_NO_DIALTONE = 6, HFP_RESULT_BUSY = 7, HFP_RESULT_NO_ANSWER = 8, HFP_RESULT_DELAYED = 9, HFP_RESULT_REJECTED = 10, HFP_RESULT_CME_ERROR = 11, }; enum hfp_error { HFP_ERROR_AG_FAILURE = 0, HFP_ERROR_NO_CONNECTION_TO_PHONE = 1, HFP_ERROR_OPERATION_NOT_ALLOWED = 3, HFP_ERROR_OPERATION_NOT_SUPPORTED = 4, HFP_ERROR_PH_SIM_PIN_REQUIRED = 5, HFP_ERROR_SIM_NOT_INSERTED = 10, HFP_ERROR_SIM_PIN_REQUIRED = 11, HFP_ERROR_SIM_PUK_REQUIRED = 12, HFP_ERROR_SIM_FAILURE = 13, HFP_ERROR_SIM_BUSY = 14, HFP_ERROR_INCORRECT_PASSWORD = 16, HFP_ERROR_SIM_PIN2_REQUIRED = 17, HFP_ERROR_SIM_PUK2_REQUIRED = 18, HFP_ERROR_MEMORY_FULL = 20, HFP_ERROR_INVALID_INDEX = 21, HFP_ERROR_MEMORY_FAILURE = 23, HFP_ERROR_TEXT_STRING_TOO_LONG = 24, HFP_ERROR_INVALID_CHARS_IN_TEXT_STRING = 25, HFP_ERROR_DIAL_STRING_TO_LONG = 26, HFP_ERROR_INVALID_CHARS_IN_DIAL_STRING = 27, HFP_ERROR_NO_NETWORK_SERVICE = 30, HFP_ERROR_NETWORK_TIMEOUT = 31, HFP_ERROR_NETWORK_NOT_ALLOWED = 32, }; enum hfp_gw_cmd_type { HFP_GW_CMD_TYPE_READ, HFP_GW_CMD_TYPE_SET, HFP_GW_CMD_TYPE_TEST, HFP_GW_CMD_TYPE_COMMAND }; struct hfp_context; typedef void (*hfp_result_func_t)(struct hfp_context *context, enum hfp_gw_cmd_type type, void *user_data); typedef void (*hfp_destroy_func_t)(void *user_data); typedef void (*hfp_debug_func_t)(const char *str, void *user_data); typedef void (*hfp_command_func_t)(const char *command, void *user_data); typedef void (*hfp_disconnect_func_t)(void *user_data); struct hfp_gw; struct hfp_gw *hfp_gw_new(int fd); struct hfp_gw *hfp_gw_ref(struct hfp_gw *hfp); void hfp_gw_unref(struct hfp_gw *hfp); bool hfp_gw_set_debug(struct hfp_gw *hfp, hfp_debug_func_t callback, void *user_data, hfp_destroy_func_t destroy); bool hfp_gw_set_close_on_unref(struct hfp_gw *hfp, bool do_close); bool hfp_gw_set_permissive_syntax(struct hfp_gw *hfp, bool permissive); bool hfp_gw_send_result(struct hfp_gw *hfp, enum hfp_result result); bool hfp_gw_send_error(struct hfp_gw *hfp, enum hfp_error error); bool hfp_gw_send_info(struct hfp_gw *hfp, const char *format, ...) __attribute__((format(printf, 2, 3))); bool hfp_gw_set_command_handler(struct hfp_gw *hfp, hfp_command_func_t callback, void *user_data, hfp_destroy_func_t destroy); bool hfp_gw_set_disconnect_handler(struct hfp_gw *hfp, hfp_disconnect_func_t callback, void *user_data, hfp_destroy_func_t destroy); bool hfp_gw_disconnect(struct hfp_gw *hfp); bool hfp_gw_register(struct hfp_gw *hfp, hfp_result_func_t callback, const char *prefix, void *user_data, hfp_destroy_func_t destroy); bool hfp_gw_unregister(struct hfp_gw *hfp, const char *prefix); bool hfp_context_get_number(struct hfp_context *context, unsigned int *val); bool hfp_context_get_number_default(struct hfp_context *context, unsigned int *val, unsigned int default_val); bool hfp_context_open_container(struct hfp_context *context); bool hfp_context_close_container(struct hfp_context *context); bool hfp_context_get_string(struct hfp_context *context, char *buf, uint8_t len); bool hfp_context_get_unquoted_string(struct hfp_context *context, char *buf, uint8_t len); bool hfp_context_get_range(struct hfp_context *context, unsigned int *min, unsigned int *max); bool hfp_context_has_next(struct hfp_context *context); void hfp_context_skip_field(struct hfp_context *context); typedef void (*hfp_hf_result_func_t)(struct hfp_context *context, void *user_data); typedef void (*hfp_response_func_t)(enum hfp_result result, enum hfp_error cme_err, void *user_data); struct hfp_hf; struct hfp_hf *hfp_hf_new(int fd); struct hfp_hf *hfp_hf_ref(struct hfp_hf *hfp); void hfp_hf_unref(struct hfp_hf *hfp); bool hfp_hf_set_debug(struct hfp_hf *hfp, hfp_debug_func_t callback, void *user_data, hfp_destroy_func_t destroy); bool hfp_hf_set_close_on_unref(struct hfp_hf *hfp, bool do_close); bool hfp_hf_set_disconnect_handler(struct hfp_hf *hfp, hfp_disconnect_func_t callback, void *user_data, hfp_destroy_func_t destroy); bool hfp_hf_disconnect(struct hfp_hf *hfp); bool hfp_hf_register(struct hfp_hf *hfp, hfp_hf_result_func_t callback, const char *prefix, void *user_data, hfp_destroy_func_t destroy); bool hfp_hf_unregister(struct hfp_hf *hfp, const char *prefix); bool hfp_hf_send_command(struct hfp_hf *hfp, hfp_response_func_t resp_cb, void *user_data, const char *format, ...); bluez-5.82/src/shared/PaxHeaders/io-ell.c0000644000000000000000000000005014772767672015261 xustar0020 atime=1743515579 20 ctime=1743591277 bluez-5.82/src/shared/io-ell.c0000644000000000000000000001225414772767672014746 0ustar00rootroot// SPDX-License-Identifier: LGPL-2.1-or-later /* * * BlueZ - Bluetooth protocol stack for Linux * * Copyright (C) 2018 Intel Corporation. All rights reserved. * * */ #ifdef HAVE_CONFIG_H #include #endif #include #include #include #include #include #include "src/shared/io.h" struct io_watch { struct io *io; io_callback_func_t cb; io_destroy_func_t destroy; void *user_data; }; struct io { int ref_count; struct l_io *l_io; struct io_watch *read_watch; struct io_watch *write_watch; struct io_watch *disc_watch; }; static struct io *io_ref(struct io *io) { if (!io) return NULL; __sync_fetch_and_add(&io->ref_count, 1); return io; } static void io_unref(struct io *io) { if (!io) return; if (__sync_sub_and_fetch(&io->ref_count, 1)) return; l_free(io); } static void watch_destroy(void *user_data) { struct io_watch *watch = user_data; struct io *io; if (!watch) return; io = watch->io; if (watch == io->read_watch) io->read_watch = NULL; else if (watch == io->write_watch) io->write_watch = NULL; else if (watch == io->disc_watch) io->disc_watch = NULL; if (watch->destroy) watch->destroy(watch->user_data); io_unref(watch->io); l_free(watch); } static struct io_watch *watch_new(struct io *io, io_callback_func_t cb, void *user_data, io_destroy_func_t destroy) { struct io_watch *watch; watch = l_new(struct io_watch, 1); watch->io = io_ref(io); watch->cb = cb; watch->user_data = user_data; watch->destroy = destroy; return watch; } static bool watch_callback(struct l_io *l_io, void *user_data) { struct io_watch *watch = user_data; if (!watch->cb) return false; return watch->cb(watch->io, watch->user_data); } static void disc_callback(struct l_io *l_io, void *user_data) { struct io_watch *watch = user_data; if (watch->cb) watch->cb(watch->io, watch->user_data); } struct io *io_new(int fd) { struct io *io; struct l_io *l_io; if (fd < 0) return NULL; io = l_new(struct io, 1); if (!io) return NULL; l_io = l_io_new(fd); if (!l_io) { l_free(io); return NULL; } io->l_io = l_io; return io_ref(io); } void io_destroy(struct io *io) { if (!io) return; l_io_set_read_handler(io->l_io, NULL, NULL, NULL); watch_destroy(io->read_watch); io->read_watch = NULL; l_io_set_write_handler(io->l_io, NULL, NULL, NULL); watch_destroy(io->write_watch); io->write_watch = NULL; l_io_set_disconnect_handler(io->l_io, NULL, NULL, NULL); watch_destroy(io->disc_watch); io->disc_watch = NULL; l_io_destroy(io->l_io); io->l_io = NULL; io_unref(io); } int io_get_fd(struct io *io) { if (!io || !io->l_io) return -ENOTCONN; return l_io_get_fd(io->l_io); } bool io_set_close_on_destroy(struct io *io, bool do_close) { if (!io || !io->l_io) return false; return l_io_set_close_on_destroy(io->l_io, do_close); } bool io_set_read_handler(struct io *io, io_callback_func_t callback, void *user_data, io_destroy_func_t destroy) { bool result; if (!io || !io->l_io) return false; if (io->read_watch) { l_io_set_read_handler(io->l_io, NULL, NULL, NULL); if (!callback) { watch_destroy(io->read_watch); io->read_watch = NULL; return true; } } io->read_watch = watch_new(io, callback, user_data, destroy); result = l_io_set_read_handler(io->l_io, watch_callback, io->read_watch, watch_destroy); if (!result) { watch_destroy(io->read_watch); io->read_watch = NULL; } return result; } bool io_set_write_handler(struct io *io, io_callback_func_t callback, void *user_data, io_destroy_func_t destroy) { bool result; if (!io || !io->l_io) return false; if (io->write_watch) { l_io_set_write_handler(io->l_io, NULL, NULL, NULL); if (!callback) { watch_destroy(io->write_watch); io->write_watch = NULL; return true; } } io->write_watch = watch_new(io, callback, user_data, destroy); result = l_io_set_write_handler(io->l_io, watch_callback, io->write_watch, watch_destroy); if (!result) { watch_destroy(io->write_watch); io->write_watch = NULL; } return result; } bool io_set_disconnect_handler(struct io *io, io_callback_func_t callback, void *user_data, io_destroy_func_t destroy) { bool result; if (!io || !io->l_io) return false; if (io->disc_watch) { l_io_set_disconnect_handler(io->l_io, NULL, NULL, NULL); if (!callback) { watch_destroy(io->disc_watch); io->disc_watch = NULL; return true; } } io->disc_watch = watch_new(io, callback, user_data, destroy); result = l_io_set_disconnect_handler(io->l_io, disc_callback, io->disc_watch, watch_destroy); if (!result) { watch_destroy(io->disc_watch); io->disc_watch = NULL; } return result; } ssize_t io_send(struct io *io, const struct iovec *iov, int iovcnt) { ssize_t ret; int fd; if (!io || !io->l_io) return -ENOTCONN; fd = l_io_get_fd(io->l_io); if (fd < 0) return -ENOTCONN; do { ret = writev(fd, iov, iovcnt); } while (ret < 0 && errno == EINTR); if (ret < 0) return -errno; return ret; } bool io_shutdown(struct io *io) { int fd; if (!io || !io->l_io) return false; fd = l_io_get_fd(io->l_io); if (fd < 0) return false; return shutdown(fd, SHUT_RDWR) == 0; } bluez-5.82/src/shared/PaxHeaders/bass.h0000644000000000000000000000005014766002272015012 xustar0020 atime=1743515578 20 ctime=1743591277 bluez-5.82/src/shared/bass.h0000644000000000000000000001051414766002272014474 0ustar00rootroot/* SPDX-License-Identifier: LGPL-2.1-or-later */ /* * * BlueZ - Bluetooth protocol stack for Linux * * Copyright 2023-2024 NXP * */ struct bt_bass; struct bt_bcast_src; #define NUM_BCAST_RECV_STATES 2 #define BT_BASS_BCAST_CODE_SIZE 16 #define BT_BASS_BIG_SYNC_FAILED_BITMASK 0xFFFFFFFF #define BT_BASS_BCAST_SRC_LEN 15 #define BT_BASS_BCAST_SRC_SUBGROUP_LEN 5 /* Application error codes */ #define BT_BASS_ERROR_OPCODE_NOT_SUPPORTED 0x80 #define BT_BASS_ERROR_INVALID_SOURCE_ID 0x81 /* PA_Sync_State values */ #define BT_BASS_NOT_SYNCHRONIZED_TO_PA 0x00 #define BT_BASS_SYNC_INFO_RE 0x01 #define BT_BASS_SYNCHRONIZED_TO_PA 0x02 #define BT_BASS_FAILED_TO_SYNCHRONIZE_TO_PA 0x03 #define BT_BASS_NO_PAST 0x04 /* BIG_Encryption values */ #define BT_BASS_BIG_ENC_STATE_NO_ENC 0x00 #define BT_BASS_BIG_ENC_STATE_BCODE_REQ 0x01 #define BT_BASS_BIG_ENC_STATE_DEC 0x02 #define BT_BASS_BIG_ENC_STATE_BAD_CODE 0x03 /* Broadcast Audio Scan Control Point * header structure */ struct bt_bass_bcast_audio_scan_cp_hdr { uint8_t op; } __packed; #define BT_BASS_REMOTE_SCAN_STOPPED 0x00 #define BT_BASS_REMOTE_SCAN_STARTED 0x01 #define BT_BASS_ADD_SRC 0x02 #define BT_BASS_ADDR_PUBLIC 0x00 #define BT_BASS_ADDR_RANDOM 0x01 /* PA_Sync values */ #define PA_SYNC_NO_SYNC 0x00 #define PA_SYNC_PAST 0x01 #define PA_SYNC_NO_PAST 0x02 /* BIS_Sync no preference bitmask */ #define BIS_SYNC_NO_PREF 0xFFFFFFFF #define PA_INTERVAL_UNKNOWN 0xFFFF struct bt_bass_add_src_params { uint8_t addr_type; bdaddr_t addr; uint8_t sid; uint8_t bid[3]; uint8_t pa_sync; uint16_t pa_interval; uint8_t num_subgroups; uint8_t subgroup_data[]; } __packed; #define BT_BASS_MOD_SRC 0x03 struct bt_bass_mod_src_params { uint8_t id; uint8_t pa_sync; uint16_t pa_interval; uint8_t num_subgroups; uint8_t subgroup_data[]; } __packed; #define BT_BASS_SET_BCAST_CODE 0x04 struct bt_bass_set_bcast_code_params { uint8_t id; uint8_t bcast_code[BT_BASS_BCAST_CODE_SIZE]; } __packed; #define BT_BASS_REMOVE_SRC 0x05 struct bt_bass_remove_src_params { uint8_t id; } __packed; typedef void (*bt_bass_func_t)(struct bt_bass *bass, void *user_data); typedef void (*bt_bass_destroy_func_t)(void *user_data); typedef void (*bt_bass_debug_func_t)(const char *str, void *user_data); typedef void (*bt_bass_src_func_t)(uint8_t id, uint32_t bid, uint8_t enc, uint32_t bis_sync, void *user_data); typedef int (*bt_bass_cp_handler_func_t)(struct bt_bcast_src *bcast_src, uint8_t op, void *params, void *user_data); struct bt_att *bt_bass_get_att(struct bt_bass *bass); struct bt_gatt_client *bt_bass_get_client(struct bt_bass *bass); unsigned int bt_bass_register(bt_bass_func_t attached, bt_bass_func_t detached, void *user_data); bool bt_bass_unregister(unsigned int id); bool bt_bass_set_debug(struct bt_bass *bass, bt_bass_debug_func_t func, void *user_data, bt_bass_destroy_func_t destroy); struct bt_bass *bt_bass_new(struct gatt_db *ldb, struct gatt_db *rdb, const bdaddr_t *adapter_bdaddr); bool bt_bass_set_user_data(struct bt_bass *bass, void *user_data); void bt_bass_unref(struct bt_bass *bass); bool bt_bass_attach(struct bt_bass *bass, struct bt_gatt_client *client); bool bt_bass_set_att(struct bt_bass *bass, struct bt_att *att); void bt_bass_detach(struct bt_bass *bass); void bt_bass_add_db(struct gatt_db *db, const bdaddr_t *adapter_bdaddr); int bt_bass_send(struct bt_bass *bass, struct bt_bass_bcast_audio_scan_cp_hdr *hdr, struct iovec *params); unsigned int bt_bass_src_register(struct bt_bass *bass, bt_bass_src_func_t cb, void *user_data, bt_bass_destroy_func_t destroy); bool bt_bass_src_unregister(struct bt_bass *bass, unsigned int id); unsigned int bt_bass_cp_handler_register(struct bt_bass *bass, bt_bass_cp_handler_func_t handler, bt_bass_destroy_func_t destroy, void *user_data); bool bt_bass_cp_handler_unregister(struct bt_bass *bass, unsigned int id); int bt_bass_set_pa_sync(struct bt_bcast_src *bcast_src, uint8_t sync_state); int bt_bass_get_pa_sync(struct bt_bcast_src *bcast_src, uint8_t *sync_state); int bt_bass_set_bis_sync(struct bt_bcast_src *bcast_src, uint8_t bis); int bt_bass_clear_bis_sync(struct bt_bcast_src *bcast_src, uint8_t bis); bool bt_bass_check_bis(struct bt_bcast_src *bcast_src, uint8_t bis); int bt_bass_set_enc(struct bt_bcast_src *bcast_src, uint8_t enc); bluez-5.82/src/shared/PaxHeaders/util.c0000644000000000000000000000005014772767672015055 xustar0020 atime=1743515579 20 ctime=1743591277 bluez-5.82/src/shared/util.c0000644000000000000000000015450514772767672014550 0ustar00rootroot// SPDX-License-Identifier: LGPL-2.1-or-later /* * * BlueZ - Bluetooth protocol stack for Linux * * Copyright (C) 2012-2014 Intel Corporation. All rights reserved. * Copyright 2023-2024 NXP * * */ #ifdef HAVE_CONFIG_H #include #endif #define _GNU_SOURCE #include #include #include #include #include #include #include #include #include #include #ifdef HAVE_SYS_RANDOM_H #include #endif #include /* define MAX_INPUT for musl */ #ifndef MAX_INPUT #define MAX_INPUT _POSIX_MAX_INPUT #endif #include "src/shared/util.h" void *util_malloc(size_t size) { if (__builtin_expect(!!size, 1)) { void *ptr; ptr = malloc(size); if (ptr) return ptr; fprintf(stderr, "failed to allocate %zu bytes\n", size); abort(); } return NULL; } void *util_memdup(const void *src, size_t size) { void *cpy; if (!src || !size) return NULL; cpy = util_malloc(size); if (!cpy) return NULL; memcpy(cpy, src, size); return cpy; } void util_debug_va(util_debug_func_t function, void *user_data, const char *format, va_list va) { char str[MAX_INPUT]; if (!function || !format) return; vsnprintf(str, sizeof(str), format, va); function(str, user_data); } void util_debug(util_debug_func_t function, void *user_data, const char *format, ...) { va_list ap; if (!function || !format) return; va_start(ap, format); util_debug_va(function, user_data, format, ap); va_end(ap); } void util_hexdump(const char dir, const unsigned char *buf, size_t len, util_debug_func_t function, void *user_data) { static const char hexdigits[] = "0123456789abcdef"; char str[68]; size_t i; if (!function || !len) return; str[0] = dir; for (i = 0; i < len; i++) { str[((i % 16) * 3) + 1] = ' '; str[((i % 16) * 3) + 2] = hexdigits[buf[i] >> 4]; str[((i % 16) * 3) + 3] = hexdigits[buf[i] & 0xf]; str[(i % 16) + 51] = isprint(buf[i]) ? buf[i] : '.'; if ((i + 1) % 16 == 0) { str[49] = ' '; str[50] = ' '; str[67] = '\0'; function(str, user_data); str[0] = ' '; } } if (i % 16 > 0) { size_t j; for (j = (i % 16); j < 16; j++) { str[(j * 3) + 1] = ' '; str[(j * 3) + 2] = ' '; str[(j * 3) + 3] = ' '; str[j + 51] = ' '; } str[49] = ' '; str[50] = ' '; str[67] = '\0'; function(str, user_data); } } /* Helper to print debug information of bitfields */ uint64_t util_debug_bit(const char *label, uint64_t val, const struct util_bit_debugger *table, util_debug_func_t function, void *user_data) { uint64_t mask = val; int i; for (i = 0; table[i].str; i++) { if (val & (((uint64_t) 1) << table[i].bit)) { util_debug(function, user_data, "%s%s", label, table[i].str); mask &= ~(((uint64_t) 1) << table[i].bit); } } return mask; } static const struct util_ltv_debugger* ltv_debugger(const struct util_ltv_debugger *debugger, size_t num, uint8_t type) { size_t i; if (!debugger || !num) return NULL; for (i = 0; i < num; i++) { const struct util_ltv_debugger *debug = &debugger[i]; if (debug->type == type) return debug; } return NULL; } /* Helper to itertate over LTV entries */ bool util_ltv_foreach(const uint8_t *data, uint8_t len, uint8_t *type, util_ltv_func_t func, void *user_data) { struct iovec iov; int i; if (!func || !data) return false; iov.iov_base = (void *) data; iov.iov_len = len; for (i = 0; iov.iov_len; i++) { uint8_t l, t, *v; if (!util_iov_pull_u8(&iov, &l)) return false; if (!l) { func(i, l, 0, NULL, user_data); continue; } if (!util_iov_pull_u8(&iov, &t)) return false; l--; if (l) { v = util_iov_pull_mem(&iov, l); if (!v) return false; } else v = NULL; if (!type || *type == t) func(i, l, t, v, user_data); } return true; } /* Helper to add l,t,v data in an iovec struct */ void util_ltv_push(struct iovec *output, uint8_t l, uint8_t t, void *v) { output->iov_base = realloc(output->iov_base, output->iov_len + l + 2); util_iov_push_u8(output, l + 1); util_iov_push_u8(output, t); util_iov_push_mem(output, l, v); } /* Helper to print debug information of LTV entries */ bool util_debug_ltv(const uint8_t *data, uint8_t len, const struct util_ltv_debugger *debugger, size_t num, util_debug_func_t function, void *user_data) { struct iovec iov; int i; iov.iov_base = (void *) data; iov.iov_len = len; for (i = 0; iov.iov_len; i++) { uint8_t l, t, *v; const struct util_ltv_debugger *debug; if (!util_iov_pull_u8(&iov, &l)) { util_debug(function, user_data, "Unable to pull length"); return false; } if (!l) { util_debug(function, user_data, "#%d: len 0x%02x", i, l); continue; } if (!util_iov_pull_u8(&iov, &t)) { util_debug(function, user_data, "Unable to pull type"); return false; } util_debug(function, user_data, "#%d: len 0x%02x type 0x%02x", i, l, t); l--; v = util_iov_pull_mem(&iov, l); if (!v) { util_debug(function, user_data, "Unable to pull value"); return false; } debug = ltv_debugger(debugger, num, t); if (debug) debug->func(v, l, function, user_data); else util_hexdump(' ', (void *)v, l, function, user_data); } return true; } /* Helper for getting the dirent type in case readdir returns DT_UNKNOWN */ unsigned char util_get_dt(const char *parent, const char *name) { char filename[PATH_MAX]; struct stat st; snprintf(filename, PATH_MAX, "%s/%s", parent, name); if (lstat(filename, &st) == 0 && S_ISDIR(st.st_mode)) return DT_DIR; return DT_UNKNOWN; } /* Helper for getting a random in case getrandom unavailable (glibc < 2.25) */ ssize_t util_getrandom(void *buf, size_t buflen, unsigned int flags) { #ifdef HAVE_GETRANDOM return getrandom(buf, buflen, flags); #else int fd; ssize_t bytes; fd = open("/dev/urandom", O_CLOEXEC); if (fd < 0) return -1; bytes = read(fd, buf, buflen); close(fd); return bytes; #endif } /* Helpers for bitfield operations */ /* Find unique id in range from 1 to max but no bigger than 64. */ uint8_t util_get_uid(uint64_t *bitmap, uint8_t max) { uint8_t id; id = ffsll(~*bitmap); if (!id || id > max) return 0; *bitmap |= ((uint64_t)1) << (id - 1); return id; } /* Clear id bit in bitmap */ void util_clear_uid(uint64_t *bitmap, uint8_t id) { if (!id || id > 64) return; *bitmap &= ~(((uint64_t)1) << (id - 1)); } struct iovec *util_iov_dup(const struct iovec *iov, size_t cnt) { struct iovec *dup; size_t i; if (!iov) return NULL; dup = new0(struct iovec, cnt); for (i = 0; i < cnt; i++) util_iov_memcpy(&dup[i], iov[i].iov_base, iov[i].iov_len); return dup; } int util_iov_memcmp(const struct iovec *iov1, const struct iovec *iov2) { if (!iov1) return 1; if (!iov2) return -1; if (iov1->iov_len != iov2->iov_len) return iov1->iov_len - iov2->iov_len; return memcmp(iov1->iov_base, iov2->iov_base, iov1->iov_len); } void util_iov_memcpy(struct iovec *iov, void *src, size_t len) { if (!iov || !src || !len) return; iov->iov_base = realloc(iov->iov_base, len); iov->iov_len = len; memcpy(iov->iov_base, src, len); } void util_iov_free(struct iovec *iov, size_t cnt) { size_t i; if (!iov) return; for (i = 0; i < cnt; i++) free(iov[i].iov_base); free(iov); } void *util_iov_push(struct iovec *iov, size_t len) { void *data; if (!iov) return NULL; data = iov->iov_base + iov->iov_len; iov->iov_len += len; return data; } void *util_iov_push_mem(struct iovec *iov, size_t len, const void *data) { void *p; p = util_iov_push(iov, len); if (!p) return NULL; if (data) memcpy(p, data, len); return p; } void *util_iov_push_le64(struct iovec *iov, uint64_t val) { void *p; p = util_iov_push(iov, sizeof(val)); if (!p) return NULL; put_le64(val, p); return p; } void *util_iov_push_be64(struct iovec *iov, uint64_t val) { void *p; p = util_iov_push(iov, sizeof(val)); if (!p) return NULL; put_be64(val, p); return p; } void *util_iov_push_le32(struct iovec *iov, uint32_t val) { void *p; p = util_iov_push(iov, sizeof(val)); if (!p) return NULL; put_le32(val, p); return p; } void *util_iov_push_be32(struct iovec *iov, uint32_t val) { void *p; p = util_iov_push(iov, sizeof(val)); if (!p) return NULL; put_be32(val, p); return p; } void *util_iov_push_le24(struct iovec *iov, uint32_t val) { void *p; p = util_iov_push(iov, sizeof(uint24_t)); if (!p) return NULL; put_le24(val, p); return p; } void *util_iov_push_be24(struct iovec *iov, uint32_t val) { void *p; p = util_iov_push(iov, sizeof(uint24_t)); if (!p) return NULL; put_le24(val, p); return p; } void *util_iov_push_le16(struct iovec *iov, uint16_t val) { void *p; p = util_iov_push(iov, sizeof(val)); if (!p) return NULL; put_le16(val, p); return p; } void *util_iov_push_be16(struct iovec *iov, uint16_t val) { void *p; p = util_iov_push(iov, sizeof(val)); if (!p) return NULL; put_be16(val, p); return p; } void *util_iov_push_u8(struct iovec *iov, uint8_t val) { void *p; p = util_iov_push(iov, sizeof(val)); if (!p) return NULL; put_u8(val, p); return p; } void *util_iov_append(struct iovec *iov, const void *data, size_t len) { iov->iov_base = realloc(iov->iov_base, iov->iov_len + len); return util_iov_push_mem(iov, len, data); } struct iovec *util_iov_new(void *data, size_t len) { struct iovec *iov; iov = new0(struct iovec, 1); util_iov_append(iov, data, len); return iov; } void *util_iov_pull(struct iovec *iov, size_t len) { if (!iov) return NULL; if (iov->iov_len < len) return NULL; iov->iov_base += len; iov->iov_len -= len; return iov->iov_base; } void *util_iov_pull_mem(struct iovec *iov, size_t len) { void *data = iov->iov_base; if (util_iov_pull(iov, len)) return data; return NULL; } void *util_iov_pull_le64(struct iovec *iov, uint64_t *val) { void *data = iov->iov_base; if (util_iov_pull(iov, sizeof(*val))) { *val = get_le64(data); return data; } return NULL; } void *util_iov_pull_be64(struct iovec *iov, uint64_t *val) { void *data = iov->iov_base; if (util_iov_pull(iov, sizeof(*val))) { *val = get_be64(data); return data; } return NULL; } void *util_iov_pull_le32(struct iovec *iov, uint32_t *val) { void *data = iov->iov_base; if (util_iov_pull(iov, sizeof(*val))) { *val = get_le32(data); return data; } return NULL; } void *util_iov_pull_be32(struct iovec *iov, uint32_t *val) { void *data = iov->iov_base; if (util_iov_pull(iov, sizeof(*val))) { *val = get_be32(data); return data; } return NULL; } void *util_iov_pull_le24(struct iovec *iov, uint32_t *val) { void *data = iov->iov_base; if (util_iov_pull(iov, sizeof(uint24_t))) { *val = get_le24(data); return data; } return NULL; } void *util_iov_pull_be24(struct iovec *iov, uint32_t *val) { void *data = iov->iov_base; if (util_iov_pull(iov, sizeof(uint24_t))) { *val = get_be24(data); return data; } return NULL; } void *util_iov_pull_le16(struct iovec *iov, uint16_t *val) { void *data = iov->iov_base; if (util_iov_pull(iov, sizeof(*val))) { *val = get_le16(data); return data; } return NULL; } void *util_iov_pull_be16(struct iovec *iov, uint16_t *val) { void *data = iov->iov_base; if (util_iov_pull(iov, sizeof(*val))) { *val = get_be16(data); return data; } return NULL; } void *util_iov_pull_u8(struct iovec *iov, uint8_t *val) { void *data = iov->iov_base; if (util_iov_pull(iov, sizeof(*val))) { *val = get_u8(data); return data; } return NULL; } static const struct { uint16_t uuid; const char *str; } uuid16_table[] = { { 0x0001, "SDP" }, { 0x0003, "RFCOMM" }, { 0x0005, "TCS-BIN" }, { 0x0007, "ATT" }, { 0x0008, "OBEX" }, { 0x000f, "BNEP" }, { 0x0010, "UPNP" }, { 0x0011, "HIDP" }, { 0x0012, "Hardcopy Control Channel" }, { 0x0014, "Hardcopy Data Channel" }, { 0x0016, "Hardcopy Notification" }, { 0x0017, "AVCTP" }, { 0x0019, "AVDTP" }, { 0x001b, "CMTP" }, { 0x001e, "MCAP Control Channel" }, { 0x001f, "MCAP Data Channel" }, { 0x0100, "L2CAP" }, /* 0x0101 to 0x0fff undefined */ { 0x1000, "Service Discovery Server Service Class" }, { 0x1001, "Browse Group Descriptor Service Class" }, { 0x1002, "Public Browse Root" }, /* 0x1003 to 0x1100 undefined */ { 0x1101, "Serial Port" }, { 0x1102, "LAN Access Using PPP" }, { 0x1103, "Dialup Networking" }, { 0x1104, "IrMC Sync" }, { 0x1105, "OBEX Object Push" }, { 0x1106, "OBEX File Transfer" }, { 0x1107, "IrMC Sync Command" }, { 0x1108, "Headset" }, { 0x1109, "Cordless Telephony" }, { 0x110a, "Audio Source" }, { 0x110b, "Audio Sink" }, { 0x110c, "A/V Remote Control Target" }, { 0x110d, "Advanced Audio Distribution" }, { 0x110e, "A/V Remote Control" }, { 0x110f, "A/V Remote Control Controller" }, { 0x1110, "Intercom" }, { 0x1111, "Fax" }, { 0x1112, "Headset AG" }, { 0x1113, "WAP" }, { 0x1114, "WAP Client" }, { 0x1115, "PANU" }, { 0x1116, "NAP" }, { 0x1117, "GN" }, { 0x1118, "Direct Printing" }, { 0x1119, "Reference Printing" }, { 0x111a, "Basic Imaging Profile" }, { 0x111b, "Imaging Responder" }, { 0x111c, "Imaging Automatic Archive" }, { 0x111d, "Imaging Referenced Objects" }, { 0x111e, "Handsfree" }, { 0x111f, "Handsfree Audio Gateway" }, { 0x1120, "Direct Printing Refrence Objects Service" }, { 0x1121, "Reflected UI" }, { 0x1122, "Basic Printing" }, { 0x1123, "Printing Status" }, { 0x1124, "Human Interface Device Service" }, { 0x1125, "Hardcopy Cable Replacement" }, { 0x1126, "HCR Print" }, { 0x1127, "HCR Scan" }, { 0x1128, "Common ISDN Access" }, /* 0x1129 and 0x112a undefined */ { 0x112d, "SIM Access" }, { 0x112e, "Phonebook Access Client" }, { 0x112f, "Phonebook Access Server" }, { 0x1130, "Phonebook Access" }, { 0x1131, "Headset HS" }, { 0x1132, "Message Access Server" }, { 0x1133, "Message Notification Server" }, { 0x1134, "Message Access Profile" }, { 0x1135, "GNSS" }, { 0x1136, "GNSS Server" }, { 0x1137, "3D Display" }, { 0x1138, "3D Glasses" }, { 0x1139, "3D Synchronization" }, { 0x113a, "MPS Profile" }, { 0x113b, "MPS Service" }, /* 0x113c to 0x11ff undefined */ { 0x1200, "PnP Information" }, { 0x1201, "Generic Networking" }, { 0x1202, "Generic File Transfer" }, { 0x1203, "Generic Audio" }, { 0x1204, "Generic Telephony" }, { 0x1205, "UPNP Service" }, { 0x1206, "UPNP IP Service" }, { 0x1300, "UPNP IP PAN" }, { 0x1301, "UPNP IP LAP" }, { 0x1302, "UPNP IP L2CAP" }, { 0x1303, "Video Source" }, { 0x1304, "Video Sink" }, { 0x1305, "Video Distribution" }, /* 0x1306 to 0x13ff undefined */ { 0x1400, "HDP" }, { 0x1401, "HDP Source" }, { 0x1402, "HDP Sink" }, /* 0x1403 to 0x17ff undefined */ { 0x1800, "Generic Access Profile" }, { 0x1801, "Generic Attribute Profile" }, { 0x1802, "Immediate Alert" }, { 0x1803, "Link Loss" }, { 0x1804, "Tx Power" }, { 0x1805, "Current Time Service" }, { 0x1806, "Reference Time Update Service" }, { 0x1807, "Next DST Change Service" }, { 0x1808, "Glucose" }, { 0x1809, "Health Thermometer" }, { 0x180a, "Device Information" }, /* 0x180b and 0x180c undefined */ { 0x180d, "Heart Rate" }, { 0x180e, "Phone Alert Status Service" }, { 0x180f, "Battery Service" }, { 0x1810, "Blood Pressure" }, { 0x1811, "Alert Notification Service" }, { 0x1812, "Human Interface Device" }, { 0x1813, "Scan Parameters" }, { 0x1814, "Running Speed and Cadence" }, { 0x1815, "Automation IO" }, { 0x1816, "Cycling Speed and Cadence" }, /* 0x1817 undefined */ { 0x1818, "Cycling Power" }, { 0x1819, "Location and Navigation" }, { 0x181a, "Environmental Sensing" }, { 0x181b, "Body Composition" }, { 0x181c, "User Data" }, { 0x181d, "Weight Scale" }, { 0x181e, "Bond Management" }, { 0x181f, "Continuous Glucose Monitoring" }, { 0x1820, "Internet Protocol Support" }, { 0x1821, "Indoor Positioning" }, { 0x1822, "Pulse Oximeter" }, { 0x1823, "HTTP Proxy" }, { 0x1824, "Transport Discovery" }, { 0x1825, "Object Transfer" }, { 0x1826, "Fitness Machine" }, { 0x1827, "Mesh Provisioning" }, { 0x1828, "Mesh Proxy" }, { 0x1843, "Audio Input Control" }, { 0x1844, "Volume Control" }, { 0x1845, "Volume Offset Control" }, { 0x1846, "Coordinated Set Identification" }, { 0x1848, "Media Control" }, { 0x1849, "Generic Media Control" }, { 0x184b, "Telephony Bearer" }, { 0x184c, "Generic Telephony Bearer" }, { 0x184d, "Microphone Control" }, { 0x184e, "Audio Stream Control" }, { 0x184f, "Broadcast Audio Scan" }, { 0x1850, "Published Audio Capabilities" }, { 0x1851, "Basic Audio Announcement" }, { 0x1852, "Broadcast Audio Announcement" }, { 0x1853, "Common Audio" }, { 0x1854, "Hearing Aid" }, { 0x1855, "Telephony and Media Audio" }, { 0x1856, "Public Broadcast Announcement" }, { 0x1858, "Gaming Audio" }, /* 0x1857 to 0x27ff undefined */ { 0x2800, "Primary Service" }, { 0x2801, "Secondary Service" }, { 0x2802, "Include" }, { 0x2803, "Characteristic" }, /* 0x2804 to 0x28ff undefined */ { 0x2900, "Characteristic Extended Properties" }, { 0x2901, "Characteristic User Description" }, { 0x2902, "Client Characteristic Configuration" }, { 0x2903, "Server Characteristic Configuration" }, { 0x2904, "Characteristic Format" }, { 0x2905, "Characteristic Aggregate Formate" }, { 0x2906, "Valid Range" }, { 0x2907, "External Report Reference" }, { 0x2908, "Report Reference" }, { 0x2909, "Number of Digitals" }, { 0x290a, "Value Trigger Setting" }, { 0x290b, "Environmental Sensing Configuration" }, { 0x290c, "Environmental Sensing Measurement" }, { 0x290d, "Environmental Sensing Trigger Setting" }, { 0x290e, "Time Trigger Setting" }, /* 0x290f to 0x29ff undefined */ { 0x2a00, "Device Name" }, { 0x2a01, "Appearance" }, { 0x2a02, "Peripheral Privacy Flag" }, { 0x2a03, "Reconnection Address" }, { 0x2a04, "Peripheral Preferred Connection Parameters" }, { 0x2a05, "Service Changed" }, { 0x2a06, "Alert Level" }, { 0x2a07, "Tx Power Level" }, { 0x2a08, "Date Time" }, { 0x2a09, "Day of Week" }, { 0x2a0a, "Day Date Time" }, /* 0x2a0b undefined */ { 0x2a0c, "Exact Time 256" }, { 0x2a0d, "DST Offset" }, { 0x2a0e, "Time Zone" }, { 0x2a0f, "Local Time Information" }, /* 0x2a10 undefined */ { 0x2a11, "Time with DST" }, { 0x2a12, "Time Accuracy" }, { 0x2a13, "Time Source" }, { 0x2a14, "Reference Time Information" }, /* 0x2a15 undefined */ { 0x2a16, "Time Update Control Point" }, { 0x2a17, "Time Update State" }, { 0x2a18, "Glucose Measurement" }, { 0x2a19, "Battery Level" }, /* 0x2a1a and 0x2a1b undefined */ { 0x2a1c, "Temperature Measurement" }, { 0x2a1d, "Temperature Type" }, { 0x2a1e, "Intermediate Temperature" }, /* 0x2a1f and 0x2a20 undefined */ { 0x2a21, "Measurement Interval" }, { 0x2a22, "Boot Keyboard Input Report" }, { 0x2a23, "System ID" }, { 0x2a24, "Model Number String" }, { 0x2a25, "Serial Number String" }, { 0x2a26, "Firmware Revision String" }, { 0x2a27, "Hardware Revision String" }, { 0x2a28, "Software Revision String" }, { 0x2a29, "Manufacturer Name String" }, { 0x2a2a, "IEEE 11073-20601 Regulatory Cert. Data List" }, { 0x2a2b, "Current Time" }, { 0x2a2c, "Magnetic Declination" }, /* 0x2a2d to 0x2a30 undefined */ { 0x2a31, "Scan Refresh" }, { 0x2a32, "Boot Keyboard Output Report" }, { 0x2a33, "Boot Mouse Input Report" }, { 0x2a34, "Glucose Measurement Context" }, { 0x2a35, "Blood Pressure Measurement" }, { 0x2a36, "Intermediate Cuff Pressure" }, { 0x2a37, "Heart Rate Measurement" }, { 0x2a38, "Body Sensor Location" }, { 0x2a39, "Heart Rate Control Point" }, /* 0x2a3a to 0x2a3e undefined */ { 0x2a3f, "Alert Status" }, { 0x2a40, "Ringer Control Point" }, { 0x2a41, "Ringer Setting" }, { 0x2a42, "Alert Category ID Bit Mask" }, { 0x2a43, "Alert Category ID" }, { 0x2a44, "Alert Notification Control Point" }, { 0x2a45, "Unread Alert Status" }, { 0x2a46, "New Alert" }, { 0x2a47, "Supported New Alert Category" }, { 0x2a48, "Supported Unread Alert Category" }, { 0x2a49, "Blood Pressure Feature" }, { 0x2a4a, "HID Information" }, { 0x2a4b, "Report Map" }, { 0x2a4c, "HID Control Point" }, { 0x2a4d, "Report" }, { 0x2a4e, "Protocol Mode" }, { 0x2a4f, "Scan Interval Window" }, { 0x2a50, "PnP ID" }, { 0x2a51, "Glucose Feature" }, { 0x2a52, "Record Access Control Point" }, { 0x2a53, "RSC Measurement" }, { 0x2a54, "RSC Feature" }, { 0x2a55, "SC Control Point" }, { 0x2a56, "Digital" }, /* 0x2a57 undefined */ { 0x2a58, "Analog" }, /* 0x2a59 undefined */ { 0x2a5a, "Aggregate" }, { 0x2a5b, "CSC Measurement" }, { 0x2a5c, "CSC Feature" }, { 0x2a5d, "Sensor Location" }, /* 0x2a5e to 0x2a62 undefined */ { 0x2a63, "Cycling Power Measurement" }, { 0x2a64, "Cycling Power Vector" }, { 0x2a65, "Cycling Power Feature" }, { 0x2a66, "Cycling Power Control Point" }, { 0x2a67, "Location and Speed" }, { 0x2a68, "Navigation" }, { 0x2a69, "Position Quality" }, { 0x2a6a, "LN Feature" }, { 0x2a6b, "LN Control Point" }, { 0x2a6c, "Elevation" }, { 0x2a6d, "Pressure" }, { 0x2a6e, "Temperature" }, { 0x2a6f, "Humidity" }, { 0x2a70, "True Wind Speed" }, { 0x2a71, "True Wind Direction" }, { 0x2a72, "Apparent Wind Speed" }, { 0x2a73, "Apparent Wind Direction" }, { 0x2a74, "Gust Factor" }, { 0x2a75, "Pollen Concentration" }, { 0x2a76, "UV Index" }, { 0x2a77, "Irradiance" }, { 0x2a78, "Rainfall" }, { 0x2a79, "Wind Chill" }, { 0x2a7a, "Heat Index" }, { 0x2a7b, "Dew Point" }, { 0x2a7c, "Trend" }, { 0x2a7d, "Descriptor Value Changed" }, { 0x2a7e, "Aerobic Heart Rate Lower Limit" }, { 0x2a7f, "Aerobic Threshold" }, { 0x2a80, "Age" }, { 0x2a81, "Anaerobic Heart Rate Lower Limit" }, { 0x2a82, "Anaerobic Heart Rate Upper Limit" }, { 0x2a83, "Anaerobic Threshold" }, { 0x2a84, "Aerobic Heart Rate Upper Limit" }, { 0x2a85, "Date of Birth" }, { 0x2a86, "Date of Threshold Assessment" }, { 0x2a87, "Email Address" }, { 0x2a88, "Fat Burn Heart Rate Lower Limit" }, { 0x2a89, "Fat Burn Heart Rate Upper Limit" }, { 0x2a8a, "First Name" }, { 0x2a8b, "Five Zone Heart Rate Limits" }, { 0x2a8c, "Gender" }, { 0x2a8d, "Heart Rate Max" }, { 0x2a8e, "Height" }, { 0x2a8f, "Hip Circumference" }, { 0x2a90, "Last Name" }, { 0x2a91, "Maximum Recommended Heart Rate" }, { 0x2a92, "Resting Heart Rate" }, { 0x2a93, "Sport Type for Aerobic/Anaerobic Thresholds" }, { 0x2a94, "Three Zone Heart Rate Limits" }, { 0x2a95, "Two Zone Heart Rate Limit" }, { 0x2a96, "VO2 Max" }, { 0x2a97, "Waist Circumference" }, { 0x2a98, "Weight" }, { 0x2a99, "Database Change Increment" }, { 0x2a9a, "User Index" }, { 0x2a9b, "Body Composition Feature" }, { 0x2a9c, "Body Composition Measurement" }, { 0x2a9d, "Weight Measurement" }, { 0x2a9e, "Weight Scale Feature" }, { 0x2a9f, "User Control Point" }, { 0x2aa0, "Magnetic Flux Density - 2D" }, { 0x2aa1, "Magnetic Flux Density - 3D" }, { 0x2aa2, "Language" }, { 0x2aa3, "Barometric Pressure Trend" }, { 0x2aa4, "Bond Management Control Point" }, { 0x2aa5, "Bond Management Feature" }, { 0x2aa6, "Central Address Resolution" }, { 0x2aa7, "CGM Measurement" }, { 0x2aa8, "CGM Feature" }, { 0x2aa9, "CGM Status" }, { 0x2aaa, "CGM Session Start Time" }, { 0x2aab, "CGM Session Run Time" }, { 0x2aac, "CGM Specific Ops Control Point" }, { 0x2aad, "Indoor Positioning Configuration" }, { 0x2aae, "Latitude" }, { 0x2aaf, "Longitude" }, { 0x2ab0, "Local North Coordinate" }, { 0x2ab1, "Local East Coordinate" }, { 0x2ab2, "Floor Number" }, { 0x2ab3, "Altitude" }, { 0x2ab4, "Uncertainty" }, { 0x2ab5, "Location Name" }, { 0x2ab6, "URI" }, { 0x2ab7, "HTTP Headers" }, { 0x2ab8, "HTTP Status Code" }, { 0x2ab9, "HTTP Entity Body" }, { 0x2aba, "HTTP Control Point" }, { 0x2abb, "HTTPS Security" }, { 0x2abc, "TDS Control Point" }, { 0x2abd, "OTS Feature" }, { 0x2abe, "Object Name" }, { 0x2abf, "Object Type" }, { 0x2ac0, "Object Size" }, { 0x2ac1, "Object First-Created" }, { 0x2ac2, "Object Last-Modified" }, { 0x2ac3, "Object ID" }, { 0x2ac4, "Object Properties" }, { 0x2ac5, "Object Action Control Point" }, { 0x2ac6, "Object List Control Point" }, { 0x2ac7, "Object List Filter" }, { 0x2ac8, "Object Changed" }, { 0x2ac9, "Resolvable Private Address Only" }, /* 0x2aca and 0x2acb undefined */ { 0x2acc, "Fitness Machine Feature" }, { 0x2acd, "Treadmill Data" }, { 0x2ace, "Cross Trainer Data" }, { 0x2acf, "Step Climber Data" }, { 0x2ad0, "Stair Climber Data" }, { 0x2ad1, "Rower Data" }, { 0x2ad2, "Indoor Bike Data" }, { 0x2ad3, "Training Status" }, { 0x2ad4, "Supported Speed Range" }, { 0x2ad5, "Supported Inclination Range" }, { 0x2ad6, "Supported Resistance Level Range" }, { 0x2ad7, "Supported Heart Rate Range" }, { 0x2ad8, "Supported Power Range" }, { 0x2ad9, "Fitness Machine Control Point" }, { 0x2ada, "Fitness Machine Status" }, { 0x2adb, "Mesh Provisioning Data In" }, { 0x2adc, "Mesh Provisioning Data Out" }, { 0x2add, "Mesh Proxy Data In" }, { 0x2ade, "Mesh Proxy Data Out" }, { 0x2b29, "Client Supported Features" }, { 0x2b2A, "Database Hash" }, { 0x2b3a, "Server Supported Features" }, { 0x2b51, "Telephony and Media Audio Profile Role" }, { 0x2b77, "Audio Input State" }, { 0x2b78, "Gain Settings Attribute" }, { 0x2b79, "Audio Input Type" }, { 0x2b7a, "Audio Input Status" }, { 0x2b7b, "Audio Input Control Point" }, { 0x2b7c, "Audio Input Description" }, { 0x2b7d, "Volume State" }, { 0x2b7e, "Volume Control Point" }, { 0x2b7f, "Volume Flags" }, { 0x2b80, "Offset State" }, { 0x2b81, "Audio Location" }, { 0x2b82, "Volume Offset Control Point" }, { 0x2b83, "Audio Output Description" }, { 0x2b84, "Set Identity Resolving Key" }, { 0x2b85, "Coordinated Set Size" }, { 0x2b86, "Set Member Lock" }, { 0x2b87, "Set Member Rank" }, { 0x2b93, "Media Player Name" }, { 0x2b94, "Media Player Icon Object ID" }, { 0x2b95, "Media Player Icon URL" }, { 0x2b96, "Track Changed" }, { 0x2b97, "Track Title" }, { 0x2b98, "Track Duration" }, { 0x2b99, "Track Position" }, { 0x2b9a, "Playback Speed" }, { 0x2b9b, "Seeking Speed" }, { 0x2b9c, "Current Track Segments Object ID" }, { 0x2b9d, "Current Track Object ID" }, { 0x2b9e, "Next Track Object ID" }, { 0x2b9f, "Parent Group Object ID" }, { 0x2ba0, "Current Group Object ID" }, { 0x2ba1, "Playing Order" }, { 0x2ba2, "Playing Orders Supported" }, { 0x2ba3, "Media State" }, { 0x2ba4, "Media Control Point" }, { 0x2ba5, "Media Control Point Opcodes Supported" }, { 0x2ba6, "Search Results Object ID" }, { 0x2ba7, "Search Control Point" }, { 0x2ba9, "Media Player Icon Object Type" }, { 0x2baa, "Track Segments Object Type" }, { 0x2bab, "Track Object Type" }, { 0x2bac, "Group Object Type" }, { 0x2bb3, "Bearer Provider Name" }, { 0x2bb4, "Bearer UCI" }, { 0x2bb5, "Bearer Technology" }, { 0x2bb6, "Bearer URI Schemes Supported List" }, { 0x2bb7, "Bearer Signal Strength" }, { 0x2bb8, "Bearer Signal Strength Reporting Interval" }, { 0x2bb9, "Bearer List Current Calls" }, { 0x2bba, "Content Control ID" }, { 0x2bbb, "Status Flags" }, { 0x2bbc, "Incoming Call Target Bearer URI" }, { 0x2bbd, "Call State" }, { 0x2bbe, "Call Control Point" }, { 0x2bbf, "Call Control Point Optional Opcodes" }, { 0x2bc0, "Termination Reason" }, { 0x2bc1, "Incoming Call" }, { 0x2bc2, "Call Friendly Name" }, { 0x2bc3, "Mute" }, { 0x2bc4, "Sink ASE" }, { 0x2bc5, "Source ASE" }, { 0x2bc6, "ASE Control Point" }, { 0x2bc7, "Broadcast Audio Scan Control Point" }, { 0x2bc8, "Broadcast Receive State" }, { 0x2bc9, "Sink PAC" }, { 0x2bca, "Sink Audio Locations" }, { 0x2bcb, "Source PAC" }, { 0x2bcc, "Source Audio Locations" }, { 0x2bcd, "Available Audio Contexts" }, { 0x2bce, "Supported Audio Contexts" }, { 0x2bda, "Hearing Aid Features" }, { 0x2bdb, "Hearing Aid Preset Control Point" }, { 0x2bdc, "Active Preset Index" }, { 0x2c00, "GMAP Role" }, { 0x2c01, "UGG Features" }, { 0x2c02, "UGT Features" }, { 0x2c03, "BGS Features" }, { 0x2c03, "BGR Features" }, /* vendor defined */ { 0xfeff, "GN Netcom" }, { 0xfefe, "GN ReSound A/S" }, { 0xfefd, "Gimbal, Inc." }, { 0xfefc, "Gimbal, Inc." }, { 0xfefb, "Telit Wireless Solutions (Formerly Stollmann E+V GmbH)" }, { 0xfefa, "PayPal, Inc." }, { 0xfef9, "PayPal, Inc." }, { 0xfef8, "Aplix Corporation" }, { 0xfef7, "Aplix Corporation" }, { 0xfef6, "Wicentric, Inc." }, { 0xfef5, "Dialog Semiconductor GmbH" }, { 0xfef4, "Google" }, { 0xfef3, "Google" }, { 0xfef2, "CSR" }, { 0xfef1, "CSR" }, { 0xfef0, "Intel" }, { 0xfeef, "Polar Electro Oy " }, { 0xfeee, "Polar Electro Oy " }, { 0xfeed, "Tile, Inc." }, { 0xfeec, "Tile, Inc." }, { 0xfeeb, "Swirl Networks, Inc." }, { 0xfeea, "Swirl Networks, Inc." }, { 0xfee9, "Quintic Corp." }, { 0xfee8, "Quintic Corp." }, { 0xfee7, "Tencent Holdings Limited." }, { 0xfee6, "Silvair, Inc." }, { 0xfee5, "Nordic Semiconductor ASA" }, { 0xfee4, "Nordic Semiconductor ASA" }, { 0xfee3, "Anki, Inc." }, { 0xfee2, "Anki, Inc." }, { 0xfee1, "Anhui Huami Information Technology Co., Ltd. " }, { 0xfee0, "Anhui Huami Information Technology Co., Ltd. " }, { 0xfedf, "Design SHIFT" }, { 0xfede, "Coin, Inc." }, { 0xfedd, "Jawbone" }, { 0xfedc, "Jawbone" }, { 0xfedb, "Perka, Inc." }, { 0xfeda, "ISSC Technologies Corp. " }, { 0xfed9, "Pebble Technology Corporation" }, { 0xfed8, "Google" }, { 0xfed7, "Broadcom" }, { 0xfed6, "Broadcom" }, { 0xfed5, "Plantronics Inc." }, { 0xfed4, "Apple, Inc." }, { 0xfed3, "Apple, Inc." }, { 0xfed2, "Apple, Inc." }, { 0xfed1, "Apple, Inc." }, { 0xfed0, "Apple, Inc." }, { 0xfecf, "Apple, Inc." }, { 0xfece, "Apple, Inc." }, { 0xfecd, "Apple, Inc." }, { 0xfecc, "Apple, Inc." }, { 0xfecb, "Apple, Inc." }, { 0xfeca, "Apple, Inc." }, { 0xfec9, "Apple, Inc." }, { 0xfec8, "Apple, Inc." }, { 0xfec7, "Apple, Inc." }, { 0xfec6, "Kocomojo, LLC" }, { 0xfec5, "Realtek Semiconductor Corp." }, { 0xfec4, "PLUS Location Systems" }, { 0xfec3, "360fly, Inc." }, { 0xfec2, "Blue Spark Technologies, Inc." }, { 0xfec1, "KDDI Corporation" }, { 0xfec0, "KDDI Corporation" }, { 0xfebf, "Nod, Inc." }, { 0xfebe, "Bose Corporation" }, { 0xfebd, "Clover Network, Inc" }, { 0xfebc, "Dexcom Inc" }, { 0xfebb, "adafruit industries" }, { 0xfeba, "Tencent Holdings Limited" }, { 0xfeb9, "LG Electronics" }, { 0xfeb8, "Facebook, Inc." }, { 0xfeb7, "Facebook, Inc." }, { 0xfeb6, "Vencer Co., Ltd" }, { 0xfeb5, "WiSilica Inc." }, { 0xfeb4, "WiSilica Inc." }, { 0xfeb3, "Taobao" }, { 0xfeb2, "Microsoft Corporation" }, { 0xfeb1, "Electronics Tomorrow Limited" }, { 0xfeb0, "Nest Labs Inc" }, { 0xfeaf, "Nest Labs Inc" }, { 0xfeae, "Nokia" }, { 0xfead, "Nokia" }, { 0xfeac, "Nokia" }, { 0xfeab, "Nokia" }, { 0xfeaa, "Google" }, { 0xfea9, "Savant Systems LLC" }, { 0xfea8, "Savant Systems LLC" }, { 0xfea7, "UTC Fire and Security" }, { 0xfea6, "GoPro, Inc." }, { 0xfea5, "GoPro, Inc." }, { 0xfea4, "Paxton Access Ltd" }, { 0xfea3, "ITT Industries" }, { 0xfea2, "Intrepid Control Systems, Inc." }, { 0xfea1, "Intrepid Control Systems, Inc." }, { 0xfea0, "Google" }, { 0xfe9f, "Google" }, { 0xfe9e, "Dialog Semiconductor B.V." }, { 0xfe9d, "Mobiquity Networks Inc" }, { 0xfe9c, "GSI Laboratories, Inc." }, { 0xfe9b, "Samsara Networks, Inc" }, { 0xfe9a, "Estimote" }, { 0xfe99, "Currant Inc" }, { 0xfe98, "Currant Inc" }, { 0xfe97, "Tesla Motors Inc." }, { 0xfe96, "Tesla Motors Inc." }, { 0xfe95, "Xiaomi Inc." }, { 0xfe94, "OttoQ In" }, { 0xfe93, "OttoQ In" }, { 0xfe92, "Jarden Safety & Security" }, { 0xfe91, "Shanghai Imilab Technology Co.,Ltd" }, { 0xfe90, "JUMA" }, { 0xfe8f, "CSR" }, { 0xfe8e, "ARM Ltd" }, { 0xfe8d, "Interaxon Inc." }, { 0xfe8c, "TRON Forum" }, { 0xfe8b, "Apple, Inc." }, { 0xfe8a, "Apple, Inc." }, { 0xfe89, "B&O Play A/S" }, { 0xfe88, "SALTO SYSTEMS S.L." }, { 0xfe87, "Qingdao Yeelink Information Technology Co., Ltd. ( 青岛亿联客信息技术有限公司 )" }, { 0xfe86, "HUAWEI Technologies Co., Ltd. ( 华为技术有限公司 )" }, { 0xfe85, "RF Digital Corp" }, { 0xfe84, "RF Digital Corp" }, { 0xfe83, "Blue Bite" }, { 0xfe82, "Medtronic Inc." }, { 0xfe81, "Medtronic Inc." }, { 0xfe80, "Doppler Lab" }, { 0xfe7f, "Doppler Lab" }, { 0xfe7e, "Awear Solutions Ltd" }, { 0xfe7d, "Aterica Health Inc." }, { 0xfe7c, "Telit Wireless Solutions (Formerly Stollmann E+V GmbH)" }, { 0xfe7b, "Orion Labs, Inc." }, { 0xfe7a, "Bragi GmbH" }, { 0xfe79, "Zebra Technologies" }, { 0xfe78, "Hewlett-Packard Company" }, { 0xfe77, "Hewlett-Packard Company" }, { 0xfe76, "TangoMe" }, { 0xfe75, "TangoMe" }, { 0xfe74, "unwire" }, { 0xfe73, "Abbott (formerly St. Jude Medical, Inc.)" }, { 0xfe72, "Abbott (formerly St. Jude Medical, Inc.)" }, { 0xfe71, "Plume Design Inc" }, { 0xfe70, "Beijing Jingdong Century Trading Co., Ltd." }, { 0xfe6f, "LINE Corporation" }, { 0xfe6e, "The University of Tokyo " }, { 0xfe6d, "The University of Tokyo " }, { 0xfe6c, "TASER International, Inc." }, { 0xfe6b, "TASER International, Inc." }, { 0xfe6a, "Kontakt Micro-Location Sp. z o.o." }, { 0xfe69, "Capsule Technologies Inc." }, { 0xfe68, "Capsule Technologies Inc." }, { 0xfe67, "Lab Sensor Solutions" }, { 0xfe66, "Intel Corporation " }, { 0xfe65, "CHIPOLO d.o.o. " }, { 0xfe64, "Siemens AG" }, { 0xfe63, "Connected Yard, Inc. " }, { 0xfe62, "Indagem Tech LLC " }, { 0xfe61, "Logitech International SA " }, { 0xfe60, "Lierda Science & Technology Group Co., Ltd." }, { 0xfe5f, "Eyefi, Inc." }, { 0xfe5e, "Plastc Corporation " }, { 0xfe5d, "Grundfos A/S " }, { 0xfe5c, "million hunters GmbH " }, { 0xfe5b, "GT-tronics HK Ltd" }, { 0xfe5a, "Cronologics Corporation" }, { 0xfe59, "Nordic Semiconductor ASA " }, { 0xfe58, "Nordic Semiconductor ASA " }, { 0xfe57, "Dotted Labs " }, { 0xfe56, "Google Inc. " }, { 0xfe55, "Google Inc. " }, { 0xfe54, "Motiv, Inc. " }, { 0xfe53, "3M" }, { 0xfe52, "SetPoint Medical " }, { 0xfe51, "SRAM " }, { 0xfe50, "Google Inc." }, { 0xfe4f, "Molekule, Inc." }, { 0xfe4e, "NTT docomo " }, { 0xfe4d, "Casambi Technologies Oy" }, { 0xfe4c, "Volkswagen AG " }, { 0xfe4b, "Signify Netherlands B.V. (formerly Philips Lighting B.V.)" }, { 0xfe4a, "OMRON HEALTHCARE Co., Ltd." }, { 0xfe49, "SenionLab AB" }, { 0xfe48, "General Motors " }, { 0xfe47, "General Motors " }, { 0xfe46, "B&O Play A/S " }, { 0xfe45, "Snapchat Inc" }, { 0xfe44, "SK Telecom " }, { 0xfe43, "Andreas Stihl AG & Co. KG" }, { 0xfe42, "Nets A/S " }, { 0xfe41, "Inugo Systems Limited" }, { 0xfe40, "Inugo Systems Limited" }, { 0xfe3f, "Friday Labs Limited" }, { 0xfe3e, "BD Medical" }, { 0xfe3d, "BD Medical" }, { 0xfe3c, "alibaba" }, { 0xfe3b, "Dobly Laboratories" }, { 0xfe3a, "TTS Tooltechnic Systems AG & Co. KG" }, { 0xfe39, "TTS Tooltechnic Systems AG & Co. KG" }, { 0xfe38, "Spaceek LTD" }, { 0xfe37, "Spaceek LTD" }, { 0xfe36, "HUAWEI Technologies Co., Ltd" }, { 0xfe35, "HUAWEI Technologies Co., Ltd" }, { 0xfe34, "SmallLoop LLC" }, { 0xfe33, "CHIPOLO d.o.o." }, { 0xfe32, "Pro-Mark, Inc." }, { 0xfe31, "Volkswagen AG" }, { 0xfe30, "Volkswagen AG" }, { 0xfe2f, "CRESCO Wireless, Inc" }, { 0xfe2e, "ERi,Inc." }, { 0xfe2d, "SMART INNOVATION Co.,Ltd" }, { 0xfe2c, "Google" }, { 0xfe2b, "ITT Industries" }, { 0xfe2a, "DaisyWorks, Inc." }, { 0xfe29, "Gibson Innovations" }, { 0xfe28, "Ayla Networks" }, { 0xfe27, "Google" }, { 0xfe26, "Google" }, { 0xfe25, "Apple, Inc. " }, { 0xfe24, "August Home Inc" }, { 0xfe23, "Zoll Medical Corporation" }, { 0xfe22, "Zoll Medical Corporation" }, { 0xfe21, "Bose Corporation" }, { 0xfe20, "Emerson" }, { 0xfe1f, "Garmin International, Inc." }, { 0xfe1e, "Smart Innovations Co., Ltd" }, { 0xfe1d, "Illuminati Instrument Corporation" }, { 0xfe1c, "NetMedia, Inc." }, { 0xfe1b, "Tyto Life LLC" }, { 0xfe1a, "Tyto Life LLC" }, { 0xfe19, "Google, Inc" }, { 0xfe18, "Runtime, Inc." }, { 0xfe17, "Telit Wireless Solutions GmbH" }, { 0xfe16, "Footmarks, Inc." }, { 0xfe15, "Amazon.com Services, Inc.." }, { 0xfe14, "Flextronics International USA Inc." }, { 0xfe13, "Apple Inc." }, { 0xfe12, "M-Way Solutions GmbH" }, { 0xfe11, "GMC-I Messtechnik GmbH" }, { 0xfe10, "Lapis Semiconductor Co., Ltd." }, { 0xfe0f, "Signify Netherlands B.V. (formerly Philips Lighting B.V.)" }, { 0xfe0e, "Setec Pty Ltd" }, { 0xfe0d, "Procter & Gamble" }, { 0xfe0c, "Procter & Gamble" }, { 0xfe0b, "ruwido austria gmbh" }, { 0xfe0a, "ruwido austria gmbh" }, { 0xfe09, "Pillsy, Inc." }, { 0xfe08, "Microsoft" }, { 0xfe07, "Sonos, Inc." }, { 0xfe06, "Qualcomm Technologies, Inc." }, { 0xfe05, "CORE Transport Technologies NZ Limited " }, { 0xfe04, "OpenPath Security Inc" }, { 0xfe03, "Amazon.com Services, Inc." }, { 0xfe02, "Robert Bosch GmbH" }, { 0xfe01, "Duracell U.S. Operations Inc." }, { 0xfe00, "Amazon.com Services, Inc." }, { 0xfdff, "OSRAM GmbH" }, { 0xfdfe, "ADHERIUM(NZ) LIMITED" }, { 0xfdfd, "RecursiveSoft Inc." }, { 0xfdfc, "Optrel AG" }, { 0xfdfb, "Tandem Diabetes Care" }, { 0xfdfa, "Tandem Diabetes Care" }, { 0xfdf9, "INIA" }, { 0xfdf8, "Onvocal" }, { 0xfdf7, "HP Inc." }, { 0xfdf6, "AIAIAI ApS" }, { 0xfdf5, "Milwaukee Electric Tools" }, { 0xfdf4, "O. E. M. Controls, Inc." }, { 0xfdf3, "Amersports" }, { 0xfdf2, "AMICCOM Electronics Corporation" }, { 0xfdf1, "LAMPLIGHT Co.,Ltd" }, { 0xfdf0, "Google Inc." }, { 0xfdef, "ART AND PROGRAM, INC." }, { 0xfdee, "Huawei Technologies Co., Ltd." }, { 0xfded, "Pole Star" }, { 0xfdec, "Mannkind Corporation" }, { 0xfdeb, "Syntronix Corporation" }, { 0xfdea, "SeeScan, Inc" }, { 0xfde9, "Spacesaver Corporation" }, { 0xfde8, "Robert Bosch GmbH" }, { 0xfde7, "SECOM Co., LTD" }, { 0xfde6, "Intelletto Technologies Inc" }, { 0xfde5, "SMK Corporation " }, { 0xfde4, "JUUL Labs, Inc." }, { 0xfde3, "Abbott Diabetes Care" }, { 0xfde2, "Google Inc." }, { 0xfde1, "Fortin Electronic Systems " }, { 0xfde0, "John Deere" }, { 0xfddf, "Harman International" }, { 0xfdde, "Noodle Technology Inc. " }, { 0xfddd, "Arch Systems Inc" }, { 0xfddc, "4iiii Innovations Inc." }, { 0xfddb, "Samsung Electronics Co., Ltd. " }, { 0xfdda, "MHCS" }, { 0xfdd9, "Jiangsu Teranovo Tech Co., Ltd." }, { 0xfdd8, "Jiangsu Teranovo Tech Co., Ltd." }, { 0xfdd7, "Emerson" }, { 0xfdd6, "Ministry of Supply " }, { 0xfdd5, "Brompton Bicycle Ltd" }, { 0xfdd4, "LX Solutions Pty Limited" }, { 0xfdd3, "FUBA Automotive Electronics GmbH" }, { 0xfdd2, "Bose Corporation" }, { 0xfdd1, "Huawei Technologies Co., Ltd " }, { 0xfdd0, "Huawei Technologies Co., Ltd " }, { 0xfdcf, "Nalu Medical, Inc" }, { 0xfdce, "SENNHEISER electronic GmbH & Co. KG" }, { 0xfdcd, "Qingping Technology (Beijing) Co., Ltd." }, { 0xfdcc, "Shoof Technologies" }, { 0xfdcb, "Meggitt SA" }, { 0xfdca, "Fortin Electronic Systems " }, { 0xfdc9, "Busch-Jaeger Elektro GmbH" }, { 0xfdc8, "Hach – Danaher" }, { 0xfdc7, "Eli Lilly and Company" }, { 0xfdc6, "Eli Lilly and Company" }, { 0xfdc5, "Automatic Labs" }, { 0xfdc4, "Simavita (Aust) Pty Ltd" }, { 0xfdc3, "Baidu Online Network Technology (Beijing) Co., Ltd" }, { 0xfdc2, "Baidu Online Network Technology (Beijing) Co., Ltd" }, { 0xfdc1, "Hunter Douglas" }, { 0xfdc0, "Hunter Douglas" }, { 0xfdbf, "California Things Inc. " }, { 0xfdbe, "California Things Inc. " }, { 0xfdbd, "Clover Network, Inc." }, { 0xfdbc, "Emerson" }, { 0xfdbb, "Profoto" }, { 0xfdba, "Comcast Cable Corporation" }, { 0xfdb9, "Comcast Cable Corporation" }, { 0xfdb8, "LivaNova USA Inc." }, { 0xfdb7, "LivaNova USA Inc." }, { 0xfdb6, "GWA Hygiene GmbH" }, { 0xfdb5, "ECSG" }, { 0xfdb4, "HP Inc" }, { 0xfdb3, "Audiodo AB" }, { 0xfdb2, "Portable Multimedia Ltd " }, { 0xfdb1, "Proxy Technologies, Inc." }, { 0xfdb0, "Proxy Technologies, Inc." }, { 0xfdaf, "Wiliot LTD" }, { 0xfdae, "Houwa System Design, k.k." }, { 0xfdad, "Houwa System Design, k.k." }, { 0xfdac, "Tentacle Sync GmbH" }, { 0xfdab, "Xiaomi Inc." }, { 0xfdaa, "Xiaomi Inc." }, { 0xfda9, "Rhombus Systems, Inc." }, { 0xfda8, "PSA Peugeot Citroën" }, { 0xfda7, "WWZN Information Technology Company Limited" }, { 0xfda6, "WWZN Information Technology Company Limited" }, { 0xfda5, "Neurostim OAB, Inc." }, { 0xfda4, "Inseego Corp." }, { 0xfda3, "Inseego Corp." }, { 0xfda2, "Groove X, Inc" }, { 0xfda1, "Groove X, Inc" }, { 0xfda0, "Secugen Corporation" }, { 0xfd9f, "VitalTech Affiliates LLC" }, { 0xfd9e, "The Coca-Cola Company" }, { 0xfd9d, "Gastec Corporation" }, { 0xfd9c, "Huawei Technologies Co., Ltd." }, { 0xfd9b, "Huawei Technologies Co., Ltd." }, { 0xfd9a, "Huawei Technologies Co., Ltd." }, { 0xfd99, "ABB Oy" }, { 0xfd98, "Disney Worldwide Services, Inc." }, { 0xfd97, "June Life, Inc." }, { 0xfd96, "Google LLC" }, { 0xfd95, "Rigado" }, { 0xfd94, "Hewlett Packard Enterprise" }, { 0xfd93, "Bayerische Motoren Werke AG" }, { 0xfd92, "Qualcomm Technologies International, Ltd. (QTIL)" }, { 0xfd91, "Groove X, Inc." }, { 0xfd90, "Guangzhou SuperSound Information Technology Co.,Ltd" }, { 0xfd8f, "Matrix ComSec Pvt. Ltd." }, { 0xfd8e, "Motorola Solutions" }, { 0xfd8d, "quip NYC Inc." }, { 0xfd8c, "Google LLC" }, { 0xfd8b, "Jigowatts Inc." }, { 0xfd8a, "Signify Netherlands B.V." }, { 0xfd89, "Urbanminded LTD" }, { 0xfd88, "Urbanminded LTD" }, { 0xfd87, "Google LLC" }, { 0xfd86, "Abbott" }, { 0xfd85, "Husqvarna AB" }, { 0xfd84, "Tile, Inc." }, { 0xfd83, "iNFORM Technology GmbH" }, { 0xfd82, "Sony Corporation" }, { 0xfd81, "CANDY HOUSE, Inc." }, { 0xfd80, "Phindex Technologies, Inc" }, { 0xfd7f, "Husqvarna AB" }, { 0xfd7e, "Samsung Electronics Co., Ltd." }, { 0xfd7d, "Center for Advanced Research Wernher Von Braun" }, { 0xfd7c, "Toshiba Information Systems(Japan) Corporation" }, { 0xfd7b, "WYZE LABS, INC." }, { 0xfd7a, "Withings" }, { 0xfd79, "Withings" }, { 0xfd78, "Withings" }, { 0xfd77, "Withings" }, { 0xfd76, "Insulet Corporation" }, { 0xfd75, "Insulet Corporation" }, { 0xfd74, "BRControls Products BV" }, { 0xfd73, "BRControls Products BV" }, { 0xfd72, "Logitech International SA" }, { 0xfd71, "GN Hearing A/S" }, { 0xfd70, "GuangDong Oppo Mobile Telecommunications Corp., Ltd." }, { 0xfd6f, "Apple, Inc." }, { 0xfd6e, "Polidea sp. z o.o." }, { 0xfd6d, "Sigma Elektro GmbH" }, { 0xfd6c, "Samsung Electronics Co., Ltd." }, { 0xfd6b, " rapitag GmbH" }, { 0xfd6a, "Emerson" }, { 0xfd69, "Samsung Electronics Co., Ltd." }, { 0xfd68, "Ubique Innovation AG" }, { 0xfd67, "Montblanc Simplo GmbH" }, { 0xfd66, "Zebra Technologies Corporation" }, { 0xfd65, "Razer Inc." }, { 0xfd64, "INRIA" }, { 0xfd63, "Fitbit, Inc." }, { 0xfd62, "Fitbit, Inc." }, { 0xfd61, "Arendi AG" }, { 0xfd60, "Sercomm Corporation" }, { 0xfd5f, "Oculus VR, LLC" }, /* SDO defined */ { 0xfccc, "Wi-Fi Easy Connect Specification" }, { 0xffef, "Wi-Fi Direct Specification" }, { 0xfff0, "Public Key Open Credential (PKOC)" }, { 0xfff1, "ICCE Digital Key" }, { 0xfff2, "Aliro" }, { 0xfff3, "FiRa Consortium" }, { 0xfff4, "FiRa Consortium" }, { 0xfff5, "Car Connectivity Consortium, LLC" }, { 0xfff6, "Matter Profile ID" }, { 0xfff7, "Zigbee Direct" }, { 0xfff8, "Mopria Alliance BLE" }, { 0xfff9, "FIDO2 Secure Client-To-Authenticator Transport" }, { 0xfffa, "ASTM Remote ID" }, { 0xfffb, "Direct Thread Commissioning" }, { 0xfffc, "Wireless Power Transfer (WPT)" }, { 0xfffd, "Universal Second Factor Authenticator" }, { 0xfffe, "Wireless Power Transfer" }, { } }; static const struct { const char *uuid; const char *str; } uuid128_table[] = { { "a3c87500-8ed3-4bdf-8a39-a01bebede295", "Eddystone Configuration Service" }, { "a3c87501-8ed3-4bdf-8a39-a01bebede295", "Capabilities" }, { "a3c87502-8ed3-4bdf-8a39-a01bebede295", "Active Slot" }, { "a3c87503-8ed3-4bdf-8a39-a01bebede295", "Advertising Interval" }, { "a3c87504-8ed3-4bdf-8a39-a01bebede295", "Radio Tx Power" }, { "a3c87505-8ed3-4bdf-8a39-a01bebede295", "(Advanced) Advertised Tx Power" }, { "a3c87506-8ed3-4bdf-8a39-a01bebede295", "Lock State" }, { "a3c87507-8ed3-4bdf-8a39-a01bebede295", "Unlock" }, { "a3c87508-8ed3-4bdf-8a39-a01bebede295", "Public ECDH Key" }, { "a3c87509-8ed3-4bdf-8a39-a01bebede295", "EID Identity Key" }, { "a3c8750a-8ed3-4bdf-8a39-a01bebede295", "ADV Slot Data" }, { "a3c8750b-8ed3-4bdf-8a39-a01bebede295", "(Advanced) Factory reset" }, { "a3c8750c-8ed3-4bdf-8a39-a01bebede295", "(Advanced) Remain Connectable" }, /* BBC micro:bit Bluetooth Profiles */ { "e95d0753-251d-470a-a062-fa1922dfa9a8", "MicroBit Accelerometer Service" }, { "e95dca4b-251d-470a-a062-fa1922dfa9a8", "MicroBit Accelerometer Data" }, { "e95dfb24-251d-470a-a062-fa1922dfa9a8", "MicroBit Accelerometer Period" }, { "e95df2d8-251d-470a-a062-fa1922dfa9a8", "MicroBit Magnetometer Service" }, { "e95dfb11-251d-470a-a062-fa1922dfa9a8", "MicroBit Magnetometer Data" }, { "e95d386c-251d-470a-a062-fa1922dfa9a8", "MicroBit Magnetometer Period" }, { "e95d9715-251d-470a-a062-fa1922dfa9a8", "MicroBit Magnetometer Bearing" }, { "e95d9882-251d-470a-a062-fa1922dfa9a8", "MicroBit Button Service" }, { "e95dda90-251d-470a-a062-fa1922dfa9a8", "MicroBit Button A State" }, { "e95dda91-251d-470a-a062-fa1922dfa9a8", "MicroBit Button B State" }, { "e95d127b-251d-470a-a062-fa1922dfa9a8", "MicroBit IO PIN Service" }, { "e95d8d00-251d-470a-a062-fa1922dfa9a8", "MicroBit PIN Data" }, { "e95d5899-251d-470a-a062-fa1922dfa9a8", "MicroBit PIN AD Configuration" }, { "e95dd822-251d-470a-a062-fa1922dfa9a8", "MicroBit PWM Control" }, { "e95dd91d-251d-470a-a062-fa1922dfa9a8", "MicroBit LED Service" }, { "e95d7b77-251d-470a-a062-fa1922dfa9a8", "MicroBit LED Matrix state" }, { "e95d93ee-251d-470a-a062-fa1922dfa9a8", "MicroBit LED Text" }, { "e95d0d2d-251d-470a-a062-fa1922dfa9a8", "MicroBit Scrolling Delay" }, { "e95d93af-251d-470a-a062-fa1922dfa9a8", "MicroBit Event Service" }, { "e95db84c-251d-470a-a062-fa1922dfa9a8", "MicroBit Requirements" }, { "e95d9775-251d-470a-a062-fa1922dfa9a8", "MicroBit Event Data" }, { "e95d23c4-251d-470a-a062-fa1922dfa9a8", "MicroBit Client Requirements" }, { "e95d5404-251d-470a-a062-fa1922dfa9a8", "MicroBit Client Events" }, { "e95d93b0-251d-470a-a062-fa1922dfa9a8", "MicroBit DFU Control Service" }, { "e95d93b1-251d-470a-a062-fa1922dfa9a8", "MicroBit DFU Control" }, { "e95d6100-251d-470a-a062-fa1922dfa9a8", "MicroBit Temperature Service" }, { "e95d1b25-251d-470a-a062-fa1922dfa9a8", "MicroBit Temperature Period" }, /* Nordic UART Port Emulation */ { "6e400001-b5a3-f393-e0a9-e50e24dcca9e", "Nordic UART Service" }, { "6e400002-b5a3-f393-e0a9-e50e24dcca9e", "Nordic UART TX" }, { "6e400003-b5a3-f393-e0a9-e50e24dcca9e", "Nordic UART RX" }, /* BlueZ Experimental Features */ { "d4992530-b9ec-469f-ab01-6c481c47da1c", "BlueZ Experimental Debug" }, { "671b10b5-42c0-4696-9227-eb28d1b049d6", "BlueZ Experimental Simultaneous Central and Peripheral" }, { "15c0a148-c273-11ea-b3de-0242ac130004", "BlueZ Experimental LL privacy" }, { "330859bc-7506-492d-9370-9a6f0614037f", "BlueZ Experimental Bluetooth Quality Report" }, { "a6695ace-ee7f-4fb9-881a-5fac66c629af", "BlueZ Offload Codecs"}, { "6fbaf188-05e0-496a-9885-d6ddfdb4e03e", "BlueZ Experimental ISO Socket"}, { "69518c4c-b69f-4679-8bc1-c021b47b5733", "BlueZ Experimental Poll Errqueue"}, { } }; const char *bt_uuid16_to_str(uint16_t uuid) { int i; for (i = 0; uuid16_table[i].str; i++) { if (uuid16_table[i].uuid == uuid) return uuid16_table[i].str; } return "Unknown"; } const char *bt_uuid32_to_str(uint32_t uuid) { if ((uuid & 0xffff0000) == 0x0000) return bt_uuid16_to_str(uuid & 0x0000ffff); return "Unknown"; } const char *bt_uuid128_to_str(const uint8_t uuid[16]) { char uuidstr[37]; sprintf(uuidstr, "%8.8x-%4.4x-%4.4x-%4.4x-%8.8x%4.4x", get_le32(&uuid[12]), get_le16(&uuid[10]), get_le16(&uuid[8]), get_le16(&uuid[6]), get_le32(&uuid[2]), get_le16(&uuid[0])); return bt_uuidstr_to_str(uuidstr); } const char *bt_uuidstr_to_str(const char *uuid) { uint32_t val; size_t len; int i; if (!uuid) return NULL; len = strlen(uuid); if (len < 36) { char *endptr = NULL; val = strtol(uuid, &endptr, 0); if (!endptr || *endptr != '\0') return NULL; if (val > UINT16_MAX) return bt_uuid32_to_str(val); return bt_uuid16_to_str(val); } if (len != 36) return NULL; for (i = 0; uuid128_table[i].str; i++) { if (strcasecmp(uuid128_table[i].uuid, uuid) == 0) return uuid128_table[i].str; } if (strncasecmp(uuid + 8, "-0000-1000-8000-00805f9b34fb", 28)) return "Vendor specific"; if (sscanf(uuid, "%08x-0000-1000-8000-00805f9b34fb", &val) != 1) return NULL; return bt_uuid32_to_str(val); } static const struct { uint16_t val; bool generic; const char *str; } appearance_table[] = { { 0, true, "Unknown" }, { 64, true, "Phone" }, { 128, true, "Computer" }, { 192, true, "Watch" }, { 193, false, "Sports Watch" }, { 256, true, "Clock" }, { 320, true, "Display" }, { 384, true, "Remote Control" }, { 448, true, "Eye-glasses" }, { 512, true, "Tag" }, { 576, true, "Keyring" }, { 640, true, "Media Player" }, { 704, true, "Barcode Scanner" }, { 768, true, "Thermometer" }, { 769, false, "Thermometer: Ear" }, { 832, true, "Heart Rate Sensor" }, { 833, false, "Heart Rate Belt" }, { 896, true, "Blood Pressure" }, { 897, false, "Blood Pressure: Arm" }, { 898, false, "Blood Pressure: Wrist" }, { 960, true, "Human Interface Device" }, { 961, false, "Keyboard" }, { 962, false, "Mouse" }, { 963, false, "Joystick" }, { 964, false, "Gamepad" }, { 965, false, "Digitizer Tablet" }, { 966, false, "Card Reader" }, { 967, false, "Digital Pen" }, { 968, false, "Barcode Scanner" }, { 1024, true, "Glucose Meter" }, { 1088, true, "Running Walking Sensor" }, { 1089, false, "Running Walking Sensor: In-Shoe" }, { 1090, false, "Running Walking Sensor: On-Shoe" }, { 1091, false, "Running Walking Sensor: On-Hip" }, { 1152, true, "Cycling" }, { 1153, false, "Cycling: Cycling Computer" }, { 1154, false, "Cycling: Speed Sensor" }, { 1155, false, "Cycling: Cadence Sensor" }, { 1156, false, "Cycling: Power Sensor" }, { 1157, false, "Cycling: Speed and Cadence Sensor" }, { 1216, true, "Undefined" }, { 3136, true, "Pulse Oximeter" }, { 3137, false, "Pulse Oximeter: Fingertip" }, { 3138, false, "Pulse Oximeter: Wrist Worn" }, { 3200, true, "Weight Scale" }, { 3264, true, "Undefined" }, { 5184, true, "Outdoor Sports Activity" }, { 5185, false, "Location Display Device" }, { 5186, false, "Location and Navigation Display Device" }, { 5187, false, "Location Pod" }, { 5188, false, "Location and Navigation Pod" }, { 5248, true, "Undefined" }, { } }; const char *bt_appear_to_str(uint16_t appearance) { const char *str = NULL; int i, type = 0; for (i = 0; appearance_table[i].str; i++) { if (appearance_table[i].generic) { if (appearance < appearance_table[i].val) break; type = i; } if (appearance_table[i].val == appearance) { str = appearance_table[i].str; break; } } if (!str) str = appearance_table[type].str; return str; } char *strdelimit(char *str, char *del, char c) { char *dup; if (!str) return NULL; dup = strdup(str); if (dup[0] == '\0') return dup; while (del[0] != '\0') { char *rep = dup; while ((rep = strchr(rep, del[0]))) rep[0] = c; del++; } return dup; } int strsuffix(const char *str, const char *suffix) { int len; int suffix_len; if (!str || !suffix) return -1; if (str[0] == '\0' && suffix[0] != '\0') return -1; if (suffix[0] == '\0' && str[0] != '\0') return -1; len = strlen(str); suffix_len = strlen(suffix); if (len < suffix_len) return -1; return strncmp(str + len - suffix_len, suffix, suffix_len); } char *strstrip(char *str) { size_t size; char *end; if (!str) return NULL; size = strlen(str); if (!size) return str; end = str + size - 1; while (end >= str && isspace(*end)) end--; *(end + 1) = '\0'; while (*str && isspace(*str)) str++; return str; } bool strisutf8(const char *str, size_t len) { size_t i = 0; while (i < len) { unsigned char c = str[i]; size_t size = 0; /* Check the first byte to determine the number of bytes in the * UTF-8 character. */ if ((c & 0x80) == 0x00) size = 1; else if ((c & 0xE0) == 0xC0) size = 2; else if ((c & 0xF0) == 0xE0) size = 3; else if ((c & 0xF8) == 0xF0) size = 4; else /* Invalid UTF-8 sequence */ return false; /* Check the following bytes to ensure they have the correct * format. */ for (size_t j = 1; j < size; ++j) { if (i + j > len || (str[i + j] & 0xC0) != 0x80) /* Invalid UTF-8 sequence */ return false; } /* Move to the next character */ i += size; } return true; } bool argsisutf8(int argc, char *argv[]) { for (int i = 0; i < argc; i++) { if (!strisutf8(argv[i], strlen(argv[i]))) { printf("Invalid character in string: %s\n", argv[i]); return false; } } return true; } bluez-5.82/src/shared/PaxHeaders/ecc.c0000644000000000000000000000005014621503015014576 xustar0020 atime=1743515864 20 ctime=1743591277 bluez-5.82/src/shared/ecc.c0000644000000000000000000005413314621503015014265 0ustar00rootroot// SPDX-License-Identifier: BSD-2-Clause /* * Copyright (c) 2013, Kenneth MacKay * All rights reserved. * */ #ifdef HAVE_CONFIG_H #include #endif #define _GNU_SOURCE #include #include #include #include #include #include "ecc.h" /* 256-bit curve */ #define ECC_BYTES 32 /* Number of uint64_t's needed */ #define NUM_ECC_DIGITS (ECC_BYTES / 8) struct ecc_point { uint64_t x[NUM_ECC_DIGITS]; uint64_t y[NUM_ECC_DIGITS]; }; #define MAX_TRIES 16 typedef struct { uint64_t m_low; uint64_t m_high; } uint128_t; #define CURVE_P_32 { 0xFFFFFFFFFFFFFFFFull, 0x00000000FFFFFFFFull, \ 0x0000000000000000ull, 0xFFFFFFFF00000001ull } #define CURVE_G_32 { \ { 0xF4A13945D898C296ull, 0x77037D812DEB33A0ull, \ 0xF8BCE6E563A440F2ull, 0x6B17D1F2E12C4247ull }, \ { 0xCBB6406837BF51F5ull, 0x2BCE33576B315ECEull, \ 0x8EE7EB4A7C0F9E16ull, 0x4FE342E2FE1A7F9Bull } \ } #define CURVE_N_32 { 0xF3B9CAC2FC632551ull, 0xBCE6FAADA7179E84ull, \ 0xFFFFFFFFFFFFFFFFull, 0xFFFFFFFF00000000ull } #define CURVE_B_32 { 0x3BCE3C3E27D2604Bull, 0x651D06B0CC53B0F6ull, \ 0xB3EBBD55769886BCull, 0x5AC635D8AA3A93E7ull } static uint64_t curve_p[NUM_ECC_DIGITS] = CURVE_P_32; static struct ecc_point curve_g = CURVE_G_32; static uint64_t curve_n[NUM_ECC_DIGITS] = CURVE_N_32; static uint64_t curve_b[NUM_ECC_DIGITS] = CURVE_B_32; static bool get_random_number(uint64_t *vli) { char *ptr = (char *) vli; size_t left = ECC_BYTES; int fd; fd = open("/dev/urandom", O_RDONLY | O_CLOEXEC); if (fd < 0) { fd = open("/dev/random", O_RDONLY | O_CLOEXEC); if (fd < 0) return false; } while (left > 0) { ssize_t ret; ret = read(fd, ptr, left); if (ret <= 0) { close(fd); return false; } left -= ret; ptr += ret; } close(fd); return true; } static void vli_clear(uint64_t *vli) { int i; for (i = 0; i < NUM_ECC_DIGITS; i++) vli[i] = 0; } /* Returns true if vli == 0, false otherwise. */ static bool vli_is_zero(const uint64_t *vli) { int i; for (i = 0; i < NUM_ECC_DIGITS; i++) { if (vli[i]) return false; } return true; } /* Returns nonzero if bit bit of vli is set. */ static uint64_t vli_test_bit(const uint64_t *vli, unsigned int bit) { return (vli[bit / 64] & ((uint64_t) 1 << (bit % 64))); } /* Counts the number of 64-bit "digits" in vli. */ static unsigned int vli_num_digits(const uint64_t *vli) { int i; /* Search from the end until we find a non-zero digit. * We do it in reverse because we expect that most digits will * be nonzero. */ for (i = NUM_ECC_DIGITS - 1; i >= 0 && vli[i] == 0; i--); return (i + 1); } /* Counts the number of bits required for vli. */ static unsigned int vli_num_bits(const uint64_t *vli) { unsigned int i, num_digits; uint64_t digit; num_digits = vli_num_digits(vli); if (num_digits == 0) return 0; digit = vli[num_digits - 1]; for (i = 0; digit; i++) digit >>= 1; return ((num_digits - 1) * 64 + i); } /* Sets dest = src. */ static void vli_set(uint64_t *dest, const uint64_t *src) { int i; for (i = 0; i < NUM_ECC_DIGITS; i++) dest[i] = src[i]; } /* Returns sign of left - right. */ static int vli_cmp(const uint64_t *left, const uint64_t *right) { int i; for (i = NUM_ECC_DIGITS - 1; i >= 0; i--) { if (left[i] > right[i]) return 1; else if (left[i] < right[i]) return -1; } return 0; } /* Constant-time comparison function - secure way to compare long integers */ /* Returns one if left == right, zero otherwise. */ static bool vli_equal(const uint64_t *left, const uint64_t *right) { uint64_t diff = 0; int i; for (i = NUM_ECC_DIGITS - 1; i >= 0; --i) diff |= (left[i] ^ right[i]); return (diff == 0); } /* Computes result = in << c, returning carry. Can modify in place * (if result == in). 0 < shift < 64. */ static uint64_t vli_lshift(uint64_t *result, const uint64_t *in, unsigned int shift) { uint64_t carry = 0; int i; for (i = 0; i < NUM_ECC_DIGITS; i++) { uint64_t temp = in[i]; result[i] = (temp << shift) | carry; carry = temp >> (64 - shift); } return carry; } /* Computes vli = vli >> 1. */ static void vli_rshift1(uint64_t *vli) { uint64_t *end = vli; uint64_t carry = 0; vli += NUM_ECC_DIGITS; while (vli-- > end) { uint64_t temp = *vli; *vli = (temp >> 1) | carry; carry = temp << 63; } } /* Computes result = left + right, returning carry. Can modify in place. */ static uint64_t vli_add(uint64_t *result, const uint64_t *left, const uint64_t *right) { uint64_t carry = 0; int i; for (i = 0; i < NUM_ECC_DIGITS; i++) { uint64_t sum; sum = left[i] + right[i] + carry; if (sum != left[i]) carry = (sum < left[i]); result[i] = sum; } return carry; } /* Computes result = left - right, returning borrow. Can modify in place. */ static uint64_t vli_sub(uint64_t *result, const uint64_t *left, const uint64_t *right) { uint64_t borrow = 0; int i; for (i = 0; i < NUM_ECC_DIGITS; i++) { uint64_t diff; diff = left[i] - right[i] - borrow; if (diff != left[i]) borrow = (diff > left[i]); result[i] = diff; } return borrow; } static uint128_t mul_64_64(uint64_t left, uint64_t right) { uint64_t a0 = left & 0xffffffffull; uint64_t a1 = left >> 32; uint64_t b0 = right & 0xffffffffull; uint64_t b1 = right >> 32; uint64_t m0 = a0 * b0; uint64_t m1 = a0 * b1; uint64_t m2 = a1 * b0; uint64_t m3 = a1 * b1; uint128_t result; m2 += (m0 >> 32); m2 += m1; /* Overflow */ if (m2 < m1) m3 += 0x100000000ull; result.m_low = (m0 & 0xffffffffull) | (m2 << 32); result.m_high = m3 + (m2 >> 32); return result; } static uint128_t add_128_128(uint128_t a, uint128_t b) { uint128_t result; result.m_low = a.m_low + b.m_low; result.m_high = a.m_high + b.m_high + (result.m_low < a.m_low); return result; } static void vli_mult(uint64_t *result, const uint64_t *left, const uint64_t *right) { uint128_t r01 = { 0, 0 }; uint64_t r2 = 0; unsigned int i, k; /* Compute each digit of result in sequence, maintaining the * carries. */ for (k = 0; k < NUM_ECC_DIGITS * 2 - 1; k++) { unsigned int min; if (k < NUM_ECC_DIGITS) min = 0; else min = (k + 1) - NUM_ECC_DIGITS; for (i = min; i <= k && i < NUM_ECC_DIGITS; i++) { uint128_t product; product = mul_64_64(left[i], right[k - i]); r01 = add_128_128(r01, product); r2 += (r01.m_high < product.m_high); } result[k] = r01.m_low; r01.m_low = r01.m_high; r01.m_high = r2; r2 = 0; } result[NUM_ECC_DIGITS * 2 - 1] = r01.m_low; } static void vli_square(uint64_t *result, const uint64_t *left) { uint128_t r01 = { 0, 0 }; uint64_t r2 = 0; int i, k; for (k = 0; k < NUM_ECC_DIGITS * 2 - 1; k++) { unsigned int min; if (k < NUM_ECC_DIGITS) min = 0; else min = (k + 1) - NUM_ECC_DIGITS; for (i = min; i <= k && i <= k - i; i++) { uint128_t product; product = mul_64_64(left[i], left[k - i]); if (i < k - i) { r2 += product.m_high >> 63; product.m_high = (product.m_high << 1) | (product.m_low >> 63); product.m_low <<= 1; } r01 = add_128_128(r01, product); r2 += (r01.m_high < product.m_high); } result[k] = r01.m_low; r01.m_low = r01.m_high; r01.m_high = r2; r2 = 0; } result[NUM_ECC_DIGITS * 2 - 1] = r01.m_low; } /* Computes result = (left + right) % mod. * Assumes that left < mod and right < mod, result != mod. */ static void vli_mod_add(uint64_t *result, const uint64_t *left, const uint64_t *right, const uint64_t *mod) { uint64_t carry; carry = vli_add(result, left, right); /* result > mod (result = mod + remainder), so subtract mod to * get remainder. */ if (carry || vli_cmp(result, mod) >= 0) vli_sub(result, result, mod); } /* Computes result = (left - right) % mod. * Assumes that left < mod and right < mod, result != mod. */ static void vli_mod_sub(uint64_t *result, const uint64_t *left, const uint64_t *right, const uint64_t *mod) { uint64_t borrow = vli_sub(result, left, right); /* In this case, p_result == -diff == (max int) - diff. * Since -x % d == d - x, we can get the correct result from * result + mod (with overflow). */ if (borrow) vli_add(result, result, mod); } /* Computes result = product % curve_p from http://www.nsa.gov/ia/_files/nist-routines.pdf */ static void vli_mmod_fast(uint64_t *result, const uint64_t *product) { uint64_t tmp[NUM_ECC_DIGITS]; int carry; /* t */ vli_set(result, product); /* s1 */ tmp[0] = 0; tmp[1] = product[5] & 0xffffffff00000000ull; tmp[2] = product[6]; tmp[3] = product[7]; carry = vli_lshift(tmp, tmp, 1); carry += vli_add(result, result, tmp); /* s2 */ tmp[1] = product[6] << 32; tmp[2] = (product[6] >> 32) | (product[7] << 32); tmp[3] = product[7] >> 32; carry += vli_lshift(tmp, tmp, 1); carry += vli_add(result, result, tmp); /* s3 */ tmp[0] = product[4]; tmp[1] = product[5] & 0xffffffff; tmp[2] = 0; tmp[3] = product[7]; carry += vli_add(result, result, tmp); /* s4 */ tmp[0] = (product[4] >> 32) | (product[5] << 32); tmp[1] = (product[5] >> 32) | (product[6] & 0xffffffff00000000ull); tmp[2] = product[7]; tmp[3] = (product[6] >> 32) | (product[4] << 32); carry += vli_add(result, result, tmp); /* d1 */ tmp[0] = (product[5] >> 32) | (product[6] << 32); tmp[1] = (product[6] >> 32); tmp[2] = 0; tmp[3] = (product[4] & 0xffffffff) | (product[5] << 32); carry -= vli_sub(result, result, tmp); /* d2 */ tmp[0] = product[6]; tmp[1] = product[7]; tmp[2] = 0; tmp[3] = (product[4] >> 32) | (product[5] & 0xffffffff00000000ull); carry -= vli_sub(result, result, tmp); /* d3 */ tmp[0] = (product[6] >> 32) | (product[7] << 32); tmp[1] = (product[7] >> 32) | (product[4] << 32); tmp[2] = (product[4] >> 32) | (product[5] << 32); tmp[3] = (product[6] << 32); carry -= vli_sub(result, result, tmp); /* d4 */ tmp[0] = product[7]; tmp[1] = product[4] & 0xffffffff00000000ull; tmp[2] = product[5]; tmp[3] = product[6] & 0xffffffff00000000ull; carry -= vli_sub(result, result, tmp); if (carry < 0) { do { carry += vli_add(result, result, curve_p); } while (carry < 0); } else { while (carry || vli_cmp(curve_p, result) != 1) carry -= vli_sub(result, result, curve_p); } } /* Computes result = (left * right) % curve_p. */ static void vli_mod_mult_fast(uint64_t *result, const uint64_t *left, const uint64_t *right) { uint64_t product[2 * NUM_ECC_DIGITS]; vli_mult(product, left, right); vli_mmod_fast(result, product); } /* Computes result = left^2 % curve_p. */ static void vli_mod_square_fast(uint64_t *result, const uint64_t *left) { uint64_t product[2 * NUM_ECC_DIGITS]; vli_square(product, left); vli_mmod_fast(result, product); } #define EVEN(vli) (!(vli[0] & 1)) /* Computes result = (1 / p_input) % mod. All VLIs are the same size. * See "From Euclid's GCD to Montgomery Multiplication to the Great Divide" * https://labs.oracle.com/techrep/2001/smli_tr-2001-95.pdf */ static void vli_mod_inv(uint64_t *result, const uint64_t *input, const uint64_t *mod) { uint64_t a[NUM_ECC_DIGITS], b[NUM_ECC_DIGITS]; uint64_t u[NUM_ECC_DIGITS], v[NUM_ECC_DIGITS]; uint64_t carry; int cmp_result; if (vli_is_zero(input)) { vli_clear(result); return; } vli_set(a, input); vli_set(b, mod); vli_clear(u); u[0] = 1; vli_clear(v); while ((cmp_result = vli_cmp(a, b)) != 0) { carry = 0; if (EVEN(a)) { vli_rshift1(a); if (!EVEN(u)) carry = vli_add(u, u, mod); vli_rshift1(u); if (carry) u[NUM_ECC_DIGITS - 1] |= 0x8000000000000000ull; } else if (EVEN(b)) { vli_rshift1(b); if (!EVEN(v)) carry = vli_add(v, v, mod); vli_rshift1(v); if (carry) v[NUM_ECC_DIGITS - 1] |= 0x8000000000000000ull; } else if (cmp_result > 0) { vli_sub(a, a, b); vli_rshift1(a); if (vli_cmp(u, v) < 0) vli_add(u, u, mod); vli_sub(u, u, v); if (!EVEN(u)) carry = vli_add(u, u, mod); vli_rshift1(u); if (carry) u[NUM_ECC_DIGITS - 1] |= 0x8000000000000000ull; } else { vli_sub(b, b, a); vli_rshift1(b); if (vli_cmp(v, u) < 0) vli_add(v, v, mod); vli_sub(v, v, u); if (!EVEN(v)) carry = vli_add(v, v, mod); vli_rshift1(v); if (carry) v[NUM_ECC_DIGITS - 1] |= 0x8000000000000000ull; } } vli_set(result, u); } /* ------ Point operations ------ */ /* Returns true if p_point is the point at infinity, false otherwise. */ static bool ecc_point_is_zero(const struct ecc_point *point) { return (vli_is_zero(point->x) && vli_is_zero(point->y)); } /* Point multiplication algorithm using Montgomery's ladder with co-Z * coordinates. From http://eprint.iacr.org/2011/338.pdf */ /* Double in place */ static void ecc_point_double_jacobian(uint64_t *x1, uint64_t *y1, uint64_t *z1) { /* t1 = x, t2 = y, t3 = z */ uint64_t t4[NUM_ECC_DIGITS]; uint64_t t5[NUM_ECC_DIGITS]; if (vli_is_zero(z1)) return; vli_mod_square_fast(t4, y1); /* t4 = y1^2 */ vli_mod_mult_fast(t5, x1, t4); /* t5 = x1*y1^2 = A */ vli_mod_square_fast(t4, t4); /* t4 = y1^4 */ vli_mod_mult_fast(y1, y1, z1); /* t2 = y1*z1 = z3 */ vli_mod_square_fast(z1, z1); /* t3 = z1^2 */ vli_mod_add(x1, x1, z1, curve_p); /* t1 = x1 + z1^2 */ vli_mod_add(z1, z1, z1, curve_p); /* t3 = 2*z1^2 */ vli_mod_sub(z1, x1, z1, curve_p); /* t3 = x1 - z1^2 */ vli_mod_mult_fast(x1, x1, z1); /* t1 = x1^2 - z1^4 */ vli_mod_add(z1, x1, x1, curve_p); /* t3 = 2*(x1^2 - z1^4) */ vli_mod_add(x1, x1, z1, curve_p); /* t1 = 3*(x1^2 - z1^4) */ if (vli_test_bit(x1, 0)) { uint64_t carry = vli_add(x1, x1, curve_p); vli_rshift1(x1); x1[NUM_ECC_DIGITS - 1] |= carry << 63; } else { vli_rshift1(x1); } /* t1 = 3/2*(x1^2 - z1^4) = B */ vli_mod_square_fast(z1, x1); /* t3 = B^2 */ vli_mod_sub(z1, z1, t5, curve_p); /* t3 = B^2 - A */ vli_mod_sub(z1, z1, t5, curve_p); /* t3 = B^2 - 2A = x3 */ vli_mod_sub(t5, t5, z1, curve_p); /* t5 = A - x3 */ vli_mod_mult_fast(x1, x1, t5); /* t1 = B * (A - x3) */ vli_mod_sub(t4, x1, t4, curve_p); /* t4 = B * (A - x3) - y1^4 = y3 */ vli_set(x1, z1); vli_set(z1, y1); vli_set(y1, t4); } /* Modify (x1, y1) => (x1 * z^2, y1 * z^3) */ static void apply_z(uint64_t *x1, uint64_t *y1, uint64_t *z) { uint64_t t1[NUM_ECC_DIGITS]; vli_mod_square_fast(t1, z); /* z^2 */ vli_mod_mult_fast(x1, x1, t1); /* x1 * z^2 */ vli_mod_mult_fast(t1, t1, z); /* z^3 */ vli_mod_mult_fast(y1, y1, t1); /* y1 * z^3 */ } /* P = (x1, y1) => 2P, (x2, y2) => P' */ static void xycz_initial_double(uint64_t *x1, uint64_t *y1, uint64_t *x2, uint64_t *y2, uint64_t *p_initial_z) { uint64_t z[NUM_ECC_DIGITS]; vli_set(x2, x1); vli_set(y2, y1); vli_clear(z); z[0] = 1; if (p_initial_z) vli_set(z, p_initial_z); apply_z(x1, y1, z); ecc_point_double_jacobian(x1, y1, z); apply_z(x2, y2, z); } /* Input P = (x1, y1, Z), Q = (x2, y2, Z) * Output P' = (x1', y1', Z3), P + Q = (x3, y3, Z3) * or P => P', Q => P + Q */ static void xycz_add(uint64_t *x1, uint64_t *y1, uint64_t *x2, uint64_t *y2) { /* t1 = X1, t2 = Y1, t3 = X2, t4 = Y2 */ uint64_t t5[NUM_ECC_DIGITS]; vli_mod_sub(t5, x2, x1, curve_p); /* t5 = x2 - x1 */ vli_mod_square_fast(t5, t5); /* t5 = (x2 - x1)^2 = A */ vli_mod_mult_fast(x1, x1, t5); /* t1 = x1*A = B */ vli_mod_mult_fast(x2, x2, t5); /* t3 = x2*A = C */ vli_mod_sub(y2, y2, y1, curve_p); /* t4 = y2 - y1 */ vli_mod_square_fast(t5, y2); /* t5 = (y2 - y1)^2 = D */ vli_mod_sub(t5, t5, x1, curve_p); /* t5 = D - B */ vli_mod_sub(t5, t5, x2, curve_p); /* t5 = D - B - C = x3 */ vli_mod_sub(x2, x2, x1, curve_p); /* t3 = C - B */ vli_mod_mult_fast(y1, y1, x2); /* t2 = y1*(C - B) */ vli_mod_sub(x2, x1, t5, curve_p); /* t3 = B - x3 */ vli_mod_mult_fast(y2, y2, x2); /* t4 = (y2 - y1)*(B - x3) */ vli_mod_sub(y2, y2, y1, curve_p); /* t4 = y3 */ vli_set(x2, t5); } /* Input P = (x1, y1, Z), Q = (x2, y2, Z) * Output P + Q = (x3, y3, Z3), P - Q = (x3', y3', Z3) * or P => P - Q, Q => P + Q */ static void xycz_add_c(uint64_t *x1, uint64_t *y1, uint64_t *x2, uint64_t *y2) { /* t1 = X1, t2 = Y1, t3 = X2, t4 = Y2 */ uint64_t t5[NUM_ECC_DIGITS]; uint64_t t6[NUM_ECC_DIGITS]; uint64_t t7[NUM_ECC_DIGITS]; vli_mod_sub(t5, x2, x1, curve_p); /* t5 = x2 - x1 */ vli_mod_square_fast(t5, t5); /* t5 = (x2 - x1)^2 = A */ vli_mod_mult_fast(x1, x1, t5); /* t1 = x1*A = B */ vli_mod_mult_fast(x2, x2, t5); /* t3 = x2*A = C */ vli_mod_add(t5, y2, y1, curve_p); /* t4 = y2 + y1 */ vli_mod_sub(y2, y2, y1, curve_p); /* t4 = y2 - y1 */ vli_mod_sub(t6, x2, x1, curve_p); /* t6 = C - B */ vli_mod_mult_fast(y1, y1, t6); /* t2 = y1 * (C - B) */ vli_mod_add(t6, x1, x2, curve_p); /* t6 = B + C */ vli_mod_square_fast(x2, y2); /* t3 = (y2 - y1)^2 */ vli_mod_sub(x2, x2, t6, curve_p); /* t3 = x3 */ vli_mod_sub(t7, x1, x2, curve_p); /* t7 = B - x3 */ vli_mod_mult_fast(y2, y2, t7); /* t4 = (y2 - y1)*(B - x3) */ vli_mod_sub(y2, y2, y1, curve_p); /* t4 = y3 */ vli_mod_square_fast(t7, t5); /* t7 = (y2 + y1)^2 = F */ vli_mod_sub(t7, t7, t6, curve_p); /* t7 = x3' */ vli_mod_sub(t6, t7, x1, curve_p); /* t6 = x3' - B */ vli_mod_mult_fast(t6, t6, t5); /* t6 = (y2 + y1)*(x3' - B) */ vli_mod_sub(y1, t6, y1, curve_p); /* t2 = y3' */ vli_set(x1, t7); } static void ecc_point_mult(struct ecc_point *result, const struct ecc_point *point, uint64_t *scalar, uint64_t *initial_z, int num_bits) { /* R0 and R1 */ uint64_t rx[2][NUM_ECC_DIGITS]; uint64_t ry[2][NUM_ECC_DIGITS]; uint64_t z[NUM_ECC_DIGITS]; int i, nb; vli_set(rx[1], point->x); vli_set(ry[1], point->y); xycz_initial_double(rx[1], ry[1], rx[0], ry[0], initial_z); for (i = num_bits - 2; i > 0; i--) { nb = !vli_test_bit(scalar, i); xycz_add_c(rx[1 - nb], ry[1 - nb], rx[nb], ry[nb]); xycz_add(rx[nb], ry[nb], rx[1 - nb], ry[1 - nb]); } nb = !vli_test_bit(scalar, 0); xycz_add_c(rx[1 - nb], ry[1 - nb], rx[nb], ry[nb]); /* Find final 1/Z value. */ vli_mod_sub(z, rx[1], rx[0], curve_p); /* X1 - X0 */ vli_mod_mult_fast(z, z, ry[1 - nb]); /* Yb * (X1 - X0) */ vli_mod_mult_fast(z, z, point->x); /* xP * Yb * (X1 - X0) */ vli_mod_inv(z, z, curve_p); /* 1 / (xP * Yb * (X1 - X0)) */ vli_mod_mult_fast(z, z, point->y); /* yP / (xP * Yb * (X1 - X0)) */ vli_mod_mult_fast(z, z, rx[1 - nb]); /* Xb * yP / (xP * Yb * (X1 - X0)) */ /* End 1/Z calculation */ xycz_add(rx[nb], ry[nb], rx[1 - nb], ry[1 - nb]); apply_z(rx[0], ry[0], z); vli_set(result->x, rx[0]); vli_set(result->y, ry[0]); } static bool ecc_valid_point(const struct ecc_point *point) { uint64_t tmp1[NUM_ECC_DIGITS]; uint64_t tmp2[NUM_ECC_DIGITS]; uint64_t _3[NUM_ECC_DIGITS] = { 3 }; /* -a = 3 */ /* The point at infinity is invalid. */ if (ecc_point_is_zero(point)) return false; /* x and y must be smaller than p. */ if (vli_cmp(curve_p, point->x) != 1 || vli_cmp(curve_p, point->y) != 1) return false; /* Computes result = y^2. */ vli_mod_square_fast(tmp1, point->y); /* Computes result = x^3 + ax + b. result must not overlap x. */ vli_mod_square_fast(tmp2, point->x); /* r = x^2 */ vli_mod_sub(tmp2, tmp2, _3, curve_p); /* r = x^2 - 3 */ vli_mod_mult_fast(tmp2, tmp2, point->x); /* r = x^3 - 3x */ vli_mod_add(tmp2, tmp2, curve_b, curve_p); /* r = x^3 - 3x + b */ /* Make sure that y^2 == x^3 + ax + b */ return vli_equal(tmp1, tmp2); } /* Little endian byte-array to native conversion */ static void ecc_bytes2native(const uint8_t bytes[ECC_BYTES], uint64_t native[NUM_ECC_DIGITS]) { int i; for (i = 0; i < NUM_ECC_DIGITS; i++) { const uint8_t *digit = bytes + 8 * (NUM_ECC_DIGITS - 1 - i); native[NUM_ECC_DIGITS - 1 - i] = ((uint64_t) digit[0] << 0) | ((uint64_t) digit[1] << 8) | ((uint64_t) digit[2] << 16) | ((uint64_t) digit[3] << 24) | ((uint64_t) digit[4] << 32) | ((uint64_t) digit[5] << 40) | ((uint64_t) digit[6] << 48) | ((uint64_t) digit[7] << 56); } } /* Native to little endian byte-array conversion */ static void ecc_native2bytes(const uint64_t native[NUM_ECC_DIGITS], uint8_t bytes[ECC_BYTES]) { int i; for (i = 0; i < NUM_ECC_DIGITS; i++) { uint8_t *digit = bytes + 8 * (NUM_ECC_DIGITS - 1 - i); digit[0] = native[NUM_ECC_DIGITS - 1 - i] >> 0; digit[1] = native[NUM_ECC_DIGITS - 1 - i] >> 8; digit[2] = native[NUM_ECC_DIGITS - 1 - i] >> 16; digit[3] = native[NUM_ECC_DIGITS - 1 - i] >> 24; digit[4] = native[NUM_ECC_DIGITS - 1 - i] >> 32; digit[5] = native[NUM_ECC_DIGITS - 1 - i] >> 40; digit[6] = native[NUM_ECC_DIGITS - 1 - i] >> 48; digit[7] = native[NUM_ECC_DIGITS - 1 - i] >> 56; } } bool ecc_make_public_key(const uint8_t private_key[32], uint8_t public_key[64]) { struct ecc_point pk; uint64_t priv[NUM_ECC_DIGITS]; ecc_bytes2native(private_key, priv); if (vli_is_zero(priv)) return false; /* Make sure the private key is in the range [1, n-1]. */ if (vli_cmp(curve_n, priv) != 1) return false; ecc_point_mult(&pk, &curve_g, priv, NULL, vli_num_bits(priv)); if (ecc_point_is_zero(&pk)) return false; ecc_native2bytes(pk.x, public_key); ecc_native2bytes(pk.y, &public_key[32]); return true; } bool ecc_make_key(uint8_t public_key[64], uint8_t private_key[32]) { struct ecc_point pk; uint64_t priv[NUM_ECC_DIGITS]; unsigned int tries = 0; memset(&pk, 0, sizeof(pk)); do { if (!get_random_number(priv) || (tries++ >= MAX_TRIES)) return false; if (vli_is_zero(priv)) continue; /* Make sure the private key is in the range [1, n-1]. */ if (vli_cmp(curve_n, priv) != 1) continue; ecc_point_mult(&pk, &curve_g, priv, NULL, vli_num_bits(priv)); } while (ecc_point_is_zero(&pk)); ecc_native2bytes(priv, private_key); ecc_native2bytes(pk.x, public_key); ecc_native2bytes(pk.y, &public_key[32]); return true; } bool ecc_valid_public_key(const uint8_t public_key[64]) { struct ecc_point pk; ecc_bytes2native(public_key, pk.x); ecc_bytes2native(&public_key[32], pk.y); return ecc_valid_point(&pk); } bool ecdh_shared_secret(const uint8_t public_key[64], const uint8_t private_key[32], uint8_t secret[32]) { uint64_t priv[NUM_ECC_DIGITS]; uint64_t rand[NUM_ECC_DIGITS]; struct ecc_point product, pk; if (!get_random_number(rand)) return false; ecc_bytes2native(public_key, pk.x); ecc_bytes2native(&public_key[32], pk.y); if (!ecc_valid_point(&pk)) return false; ecc_bytes2native(private_key, priv); ecc_point_mult(&product, &pk, priv, rand, vli_num_bits(priv)); ecc_native2bytes(product.x, secret); return !ecc_point_is_zero(&product); } bluez-5.82/src/shared/PaxHeaders/btp.h0000644000000000000000000000005014015011623014632 xustar0020 atime=1743516396 20 ctime=1743591285 bluez-5.82/src/shared/btp.h0000644000000000000000000001657514015011623014331 0ustar00rootroot/* SPDX-License-Identifier: LGPL-2.1-or-later */ /* * * BlueZ - Bluetooth protocol stack for Linux * * Copyright (C) 2012-2017 Intel Corporation. All rights reserved. * * */ #include #ifndef __packed #define __packed __attribute__((packed)) #endif #define BTP_INDEX_NON_CONTROLLER 0xff #define BTP_ERROR_FAIL 0x01 #define BTP_ERROR_UNKNOWN_CMD 0x02 #define BTP_ERROR_NOT_READY 0x03 #define BTP_ERROR_INVALID_INDEX 0x04 #define BTP_CORE_SERVICE 0 #define BTP_GAP_SERVICE 1 #define BTP_GATT_SERVICE 2 #define BTP_L2CAP_SERVICE 3 #define BTP_MESH_NODE_SERVICE 4 struct btp_hdr { uint8_t service; uint8_t opcode; uint8_t index; uint16_t data_len; uint8_t data[0]; } __packed; struct btp_error { uint8_t status; } __packed; #define BTP_OP_ERROR 0x00 #define BTP_OP_CORE_READ_SUPPORTED_COMMANDS 0x01 #define BTP_OP_CORE_READ_SUPPORTED_SERVICES 0x02 #define BTP_OP_CORE_REGISTER 0x03 struct btp_core_register_cp { uint8_t service_id; } __packed; #define BTP_OP_CORE_UNREGISTER 0x04 struct btp_core_unregister_cp { uint8_t service_id; } __packed; #define BTP_EV_CORE_READY 0x80 #define BTP_OP_GAP_READ_SUPPORTED_COMMANDS 0x01 #define BTP_OP_GAP_READ_CONTROLLER_INDEX_LIST 0x02 struct btp_gap_read_index_rp { uint8_t num; uint8_t indexes[0]; } __packed; #define BTP_GAP_SETTING_POWERED 0x00000001 #define BTP_GAP_SETTING_CONNECTABLE 0x00000002 #define BTP_GAP_SETTING_FAST_CONNECTABLE 0x00000004 #define BTP_GAP_SETTING_DISCOVERABLE 0x00000008 #define BTP_GAP_SETTING_BONDABLE 0x00000010 #define BTP_GAP_SETTING_LL_SECURITY 0x00000020 #define BTP_GAP_SETTING_SSP 0x00000040 #define BTP_GAP_SETTING_BREDR 0x00000080 #define BTP_GAP_SETTING_HS 0x00000100 #define BTP_GAP_SETTING_LE 0x00000200 #define BTP_GAP_SETTING_ADVERTISING 0x00000400 #define BTP_GAP_SETTING_SC 0x00000800 #define BTP_GAP_SETTING_DEBUG_KEYS 0x00001000 #define BTP_GAP_SETTING_PRIVACY 0x00002000 #define BTP_GAP_SETTING_CONTROLLER_CONF 0x00004000 #define BTP_GAP_SETTING_STATIC_ADDRESS 0x00008000 #define BTP_OP_GAP_READ_COTROLLER_INFO 0x03 struct btp_gap_read_info_rp { bdaddr_t address; uint32_t supported_settings; uint32_t current_settings; uint8_t cod[3]; uint8_t name[249]; uint8_t short_name[11]; } __packed; #define BTP_OP_GAP_RESET 0x04 struct btp_gap_reset_rp { uint32_t current_settings; } __packed; #define BTP_OP_GAP_SET_POWERED 0x05 struct btp_gap_set_powered_cp { uint8_t powered; } __packed; struct btp_gap_set_powered_rp { uint32_t current_settings; } __packed; #define BTP_OP_GAP_SET_CONNECTABLE 0x06 struct btp_gap_set_connectable_cp { uint8_t connectable; } __packed; struct btp_gap_set_connectable_rp { uint32_t current_settings; } __packed; #define BTP_OP_GAP_SET_FAST_CONNECTABLE 0x07 struct btp_gap_set_fast_connectable_cp { uint8_t fast_connectable; } __packed; struct btp_gap_set_fast_connectable_rp { uint32_t current_settings; } __packed; #define BTP_OP_GAP_SET_DISCOVERABLE 0x08 struct btp_gap_set_discoverable_cp { uint8_t discoverable; } __packed; struct btp_gap_set_discoverable_rp { uint32_t current_settings; } __packed; #define BTP_OP_GAP_SET_BONDABLE 0x09 struct btp_gap_set_bondable_cp { uint8_t bondable; } __packed; struct btp_gap_set_bondable_rp { uint32_t current_settings; } __packed; #define BTP_OP_GAP_START_ADVERTISING 0x0a struct btp_gap_start_adv_cp { uint8_t adv_data_len; uint8_t scan_rsp_len; uint8_t data[0]; } __packed; struct btp_gap_start_adv_rp { uint32_t current_settings; } __packed; #define BTP_OP_GAP_STOP_ADVERTISING 0x0b struct btp_gap_stop_adv_rp { uint32_t current_settings; } __packed; #define BTP_GAP_DISCOVERY_FLAG_LE 0x01 #define BTP_GAP_DISCOVERY_FLAG_BREDR 0x02 #define BTP_GAP_DISCOVERY_FLAG_LIMITED 0x04 #define BTP_GAP_DISCOVERY_FLAG_ACTIVE 0x08 #define BTP_GAP_DISCOVERY_FLAG_OBSERVATION 0x10 #define BTP_OP_GAP_START_DISCOVERY 0x0c struct btp_gap_start_discovery_cp { uint8_t flags; } __packed; #define BTP_OP_GAP_STOP_DISCOVERY 0x0d #define BTP_GAP_ADDR_PUBLIC 0x00 #define BTP_GAP_ADDR_RANDOM 0x01 #define BTP_OP_GAP_CONNECT 0x0e struct btp_gap_connect_cp { uint8_t address_type; bdaddr_t address; } __packed; #define BTP_OP_GAP_DISCONNECT 0x0f struct btp_gap_disconnect_cp { uint8_t address_type; bdaddr_t address; } __packed; #define BTP_GAP_IOCAPA_DISPLAY_ONLY 0x00 #define BTP_GAP_IOCAPA_DISPLAY_YESNO 0x01 #define BTP_GAP_IOCAPA_KEYBOARD_ONLY 0x02 #define BTP_GAP_IOCAPA_NO_INPUT_NO_OUTPUT 0x03 #define BTP_GAP_IOCAPA_KEYBOARD_DISPLAY 0x04 #define BTP_OP_GAP_SET_IO_CAPA 0x10 struct btp_gap_set_io_capa_cp { uint8_t capa; } __packed; #define BTP_OP_GAP_PAIR 0x11 struct btp_gap_pair_cp { uint8_t address_type; bdaddr_t address; } __packed; #define BTP_OP_GAP_UNPAIR 0x12 struct btp_gap_unpair_cp { uint8_t address_type; bdaddr_t address; } __packed; #define BTP_OP_GAP_PASSKEY_ENTRY_RSP 0x13 struct btp_gap_passkey_entry_rsp_cp { uint8_t address_type; bdaddr_t address; uint32_t passkey; } __packed; #define BTP_OP_GAP_PASSKEY_CONFIRM_RSP 0x14 struct btp_gap_passkey_confirm_rsp_cp { uint8_t address_type; bdaddr_t address; uint8_t match; } __packed; #define BTP_EV_GAP_NEW_SETTINGS 0x80 struct btp_new_settings_ev { uint32_t current_settings; } __packed; #define BTP_EV_GAP_DEVICE_FOUND_FLAG_RSSI 0x01 #define BTP_EV_GAP_DEVICE_FOUND_FLAG_AD 0x02 #define BTP_EV_GAP_DEVICE_FOUND_FLAG_SR 0x04 #define BTP_EV_GAP_DEVICE_FOUND 0x81 struct btp_device_found_ev { uint8_t address_type; bdaddr_t address; int8_t rssi; uint8_t flags; uint16_t eir_len; uint8_t eir[0]; } __packed; #define BTP_EV_GAP_DEVICE_CONNECTED 0x82 struct btp_gap_device_connected_ev { uint8_t address_type; bdaddr_t address; uint16_t connection_interval; uint16_t connection_latency; uint16_t supervision_timeout; } __packed; #define BTP_EV_GAP_DEVICE_DISCONNECTED 0x83 struct btp_gap_device_disconnected_ev { uint8_t address_type; bdaddr_t address; } __packed; #define BTP_EV_GAP_PASSKEY_DISPLAY 0x84 struct btp_gap_passkey_display_ev { uint8_t address_type; bdaddr_t address; uint32_t passkey; } __packed; #define BTP_EV_GAP_PASSKEY_REQUEST 0x85 struct btp_gap_passkey_req_ev { uint8_t address_type; bdaddr_t address; } __packed; #define BTP_EV_GAP_PASSKEY_CONFIRM 0x86 struct btp_gap_passkey_confirm_ev { uint8_t address_type; bdaddr_t address; uint32_t passkey; } __packed; #define BTP_EV_GAP_IDENTITY_RESOLVED 0x87 struct btp_gap_identity_resolved_ev { uint8_t address_type; bdaddr_t address; uint8_t identity_address_type; bdaddr_t identity_address; } __packed; struct btp; typedef void (*btp_destroy_func_t)(void *user_data); typedef void (*btp_disconnect_func_t)(struct btp *btp, void *user_data); typedef void (*btp_cmd_func_t)(uint8_t index, const void *param, uint16_t length, void *user_data); struct btp *btp_new(const char *path); void btp_cleanup(struct btp *btp); bool btp_set_disconnect_handler(struct btp *btp, btp_disconnect_func_t callback, void *user_data, btp_destroy_func_t destroy); bool btp_send_error(struct btp *btp, uint8_t service, uint8_t index, uint8_t status); bool btp_send(struct btp *btp, uint8_t service, uint8_t opcode, uint8_t index, uint16_t length, const void *param); unsigned int btp_register(struct btp *btp, uint8_t service, uint8_t opcode, btp_cmd_func_t callback, void *user_data, btp_destroy_func_t destroy); bool btp_unregister(struct btp *btp, unsigned int id); void btp_unregister_service(struct btp *btp, uint8_t service); bluez-5.82/src/shared/PaxHeaders/asha.h0000644000000000000000000000005014766002272014776 xustar0020 atime=1743515578 20 ctime=1743591277 bluez-5.82/src/shared/asha.h0000644000000000000000000000264214766002272014463 0ustar00rootroot// SPDX-License-Identifier: LGPL-2.1-or-later /* * * BlueZ - Bluetooth protocol stack for Linux * * Copyright (C) 2024 Asymptotic Inc. * * Author: Arun Raghavan * * */ # #include #include enum bt_asha_state_t { ASHA_STOPPED = 0, ASHA_STARTING, ASHA_STARTED, ASHA_STOPPING, }; typedef void (*bt_asha_cb_t)(int status, void *data); struct bt_asha { struct bt_gatt_client *client; struct gatt_db *db; struct gatt_db_attribute *attr; uint16_t acp_handle; uint16_t volume_handle; unsigned int status_notify_id; uint16_t psm; bool right_side; bool binaural; bool csis_supported; bool coc_streaming_supported; uint8_t hisyncid[8]; uint16_t render_delay; uint16_t codec_ids; int8_t volume; enum bt_asha_state_t state; bt_asha_cb_t cb; void *cb_user_data; }; struct bt_asha_set { uint8_t hisyncid[8]; struct bt_asha *left; struct bt_asha *right; }; struct bt_asha *bt_asha_new(void); void bt_asha_reset(struct bt_asha *asha); void bt_asha_state_reset(struct bt_asha *asha); void bt_asha_free(struct bt_asha *asha); unsigned int bt_asha_start(struct bt_asha *asha, bt_asha_cb_t cb, void *user_data); unsigned int bt_asha_stop(struct bt_asha *asha, bt_asha_cb_t cb, void *user_data); bool bt_asha_set_volume(struct bt_asha *asha, int8_t volume); bool bt_asha_probe(struct bt_asha *asha, struct gatt_db *db, struct bt_gatt_client *client); bluez-5.82/src/shared/PaxHeaders/io.h0000644000000000000000000000005014015011623014454 xustar0020 atime=1743515782 20 ctime=1743591277 bluez-5.82/src/shared/io.h0000644000000000000000000000171414015011623014140 0ustar00rootroot/* SPDX-License-Identifier: LGPL-2.1-or-later */ /* * * BlueZ - Bluetooth protocol stack for Linux * * Copyright (C) 2012-2014 Intel Corporation. All rights reserved. * * */ #include #include typedef void (*io_destroy_func_t)(void *data); struct io; struct io *io_new(int fd); void io_destroy(struct io *io); int io_get_fd(struct io *io); bool io_set_close_on_destroy(struct io *io, bool do_close); ssize_t io_send(struct io *io, const struct iovec *iov, int iovcnt); bool io_shutdown(struct io *io); typedef bool (*io_callback_func_t)(struct io *io, void *user_data); bool io_set_read_handler(struct io *io, io_callback_func_t callback, void *user_data, io_destroy_func_t destroy); bool io_set_write_handler(struct io *io, io_callback_func_t callback, void *user_data, io_destroy_func_t destroy); bool io_set_disconnect_handler(struct io *io, io_callback_func_t callback, void *user_data, io_destroy_func_t destroy); bluez-5.82/src/shared/PaxHeaders/btp.c0000644000000000000000000000005014772767672014665 xustar0020 atime=1743515579 20 ctime=1743591285 bluez-5.82/src/shared/btp.c0000644000000000000000000001646514772767672014362 0ustar00rootroot// SPDX-License-Identifier: LGPL-2.1-or-later /* * * BlueZ - Bluetooth protocol stack for Linux * * Copyright (C) 2012-2017 Intel Corporation. All rights reserved. * * */ #include #include #include #include #include #include #include "lib/bluetooth.h" #include "src/shared/btp.h" #define BTP_MTU 512 struct btp_handler { unsigned int id; uint8_t service; uint8_t opcode; btp_cmd_func_t callback; void *user_data; btp_destroy_func_t destroy; }; struct btp { struct l_io *io; struct l_queue *pending; bool writer_active; bool reader_active; struct l_queue *handlers; unsigned int next_handler; uint8_t buf[BTP_MTU]; btp_disconnect_func_t disconnect_cb; void *disconnect_cb_data; btp_destroy_func_t disconnect_cb_data_destroy; }; static struct l_io *btp_connect(const char *path) { struct sockaddr_un addr; struct l_io *io; int sk; sk = socket(AF_UNIX, SOCK_STREAM | SOCK_NONBLOCK, 0); if (sk < 0) return NULL; memset(&addr, 0, sizeof(addr)); addr.sun_family = AF_UNIX; strncpy(addr.sun_path, path, sizeof(addr.sun_path) - 1); if (connect(sk, (struct sockaddr *) &addr, sizeof(addr)) < 0) { close(sk); return NULL; } io = l_io_new(sk); if (!io) { close(sk); return NULL; } l_io_set_close_on_destroy(io, true); return io; } static void disconnect_handler(struct l_io *io, void *user_data) { struct btp *btp = user_data; btp->disconnect_cb(btp, btp->disconnect_cb_data); } static void disconnect_handler_destroy(void *user_data) { struct btp *btp = user_data; if (btp->disconnect_cb_data_destroy) btp->disconnect_cb_data_destroy(btp->disconnect_cb_data); } struct handler_match_data { uint8_t service; uint8_t opcode; }; static bool handler_match(const void *a, const void *b) { const struct btp_handler *handler = a; const struct handler_match_data *match = b; return (handler->service == match->service) && (handler->opcode == match->opcode); } static bool can_read_data(struct l_io *io, void *user_data) { struct handler_match_data match; struct btp *btp = user_data; struct btp_handler *handler; struct btp_hdr *hdr; ssize_t bytes_read; uint16_t data_len; bytes_read = read(l_io_get_fd(btp->io), btp->buf, sizeof(btp->buf)); if (bytes_read < 0) return false; if ((size_t) bytes_read < sizeof(*hdr)) return false; hdr = (void *)btp->buf; data_len = L_LE16_TO_CPU(hdr->data_len); if ((size_t) bytes_read < sizeof(*hdr) + data_len) return false; match.service = hdr->service; match.opcode = hdr->opcode; handler = l_queue_find(btp->handlers, handler_match, &match); if (handler) { handler->callback(hdr->index, hdr->data, data_len, handler->user_data); return false; } /* keep reader active if we sent error reply */ btp_send_error(btp, match.service, hdr->index, BTP_ERROR_UNKNOWN_CMD); return true; } static void read_watch_destroy(void *user_data) { struct btp *btp = user_data; btp->reader_active = false; } static void wakeup_reader(struct btp *btp) { if (btp->reader_active) return; btp->reader_active = l_io_set_read_handler(btp->io, can_read_data, btp, read_watch_destroy); } struct btp *btp_new(const char *path) { struct btp *btp; struct l_io *io; io = btp_connect(path); if (!io) return NULL; btp = l_new(struct btp, 1); btp->pending = l_queue_new(); btp->handlers = l_queue_new(); btp->io = io; btp->next_handler = 1; wakeup_reader(btp); return btp; } struct pending_message { size_t len; void *data; bool wakeup_read; }; static void destroy_message(struct pending_message *msg) { l_free(msg->data); l_free(msg); } void btp_cleanup(struct btp *btp) { if (!btp) return; l_io_destroy(btp->io); l_queue_destroy(btp->pending, (l_queue_destroy_func_t)destroy_message); l_queue_destroy(btp->handlers, (l_queue_destroy_func_t)l_free); l_free(btp); } bool btp_set_disconnect_handler(struct btp *btp, btp_disconnect_func_t callback, void *user_data, btp_destroy_func_t destroy) { if (callback) { if (!l_io_set_disconnect_handler(btp->io, disconnect_handler, btp, disconnect_handler_destroy)) return false; } else { if (!l_io_set_disconnect_handler(btp->io, NULL, NULL, NULL)) return false; } btp->disconnect_cb = callback; btp->disconnect_cb_data = user_data; btp->disconnect_cb_data_destroy = destroy; return true; } static bool can_write_data(struct l_io *io, void *user_data) { struct btp *btp = user_data; struct pending_message *msg; msg = l_queue_pop_head(btp->pending); if (!msg) return false; if (msg->wakeup_read) wakeup_reader(btp); if (write(l_io_get_fd(btp->io), msg->data, msg->len) < 0) { l_error("Failed to send BTP message"); destroy_message(msg); return false; } destroy_message(msg); return !l_queue_isempty(btp->pending); } static void write_watch_destroy(void *user_data) { struct btp *btp = user_data; btp->writer_active = false; } static void wakeup_writer(struct btp *btp) { if (l_queue_isempty(btp->pending)) return; if (btp->writer_active) return; btp->writer_active = l_io_set_write_handler(btp->io, can_write_data, btp, write_watch_destroy); } bool btp_send_error(struct btp *btp, uint8_t service, uint8_t index, uint8_t status) { struct btp_error rsp; rsp.status = status; return btp_send(btp, service, BTP_OP_ERROR, index, sizeof(rsp), &rsp); } bool btp_send(struct btp *btp, uint8_t service, uint8_t opcode, uint8_t index, uint16_t length, const void *param) { struct btp_hdr *hdr; struct pending_message *msg; size_t len; if (!btp) return false; len = sizeof(*hdr) + length; hdr = l_malloc(len); if (!hdr) return false; hdr->service = service; hdr->opcode = opcode; hdr->index = index; hdr->data_len = L_CPU_TO_LE16(length); if (length) memcpy(hdr->data, param, length); msg = l_new(struct pending_message, 1); msg->len = len; msg->data = hdr; msg->wakeup_read = opcode < 0x80; l_queue_push_tail(btp->pending, msg); wakeup_writer(btp); return true; } unsigned int btp_register(struct btp *btp, uint8_t service, uint8_t opcode, btp_cmd_func_t callback, void *user_data, btp_destroy_func_t destroy) { struct btp_handler *handler; handler = l_new(struct btp_handler, 1); handler->id = btp->next_handler++; handler->service = service; handler->opcode = opcode; handler->callback = callback; handler->user_data = user_data; handler->destroy = destroy; l_queue_push_tail(btp->handlers, handler); return handler->id; } static bool handler_match_by_id(const void *a, const void *b) { const struct btp_handler *handler = a; unsigned int id = L_PTR_TO_UINT(b); return handler->id == id; } bool btp_unregister(struct btp *btp, unsigned int id) { struct btp_handler *handler; handler = l_queue_remove_if(btp->handlers, handler_match_by_id, L_UINT_TO_PTR(id)); if (!handler) return false; if (handler->destroy) handler->destroy(handler->user_data); l_free(handler); return true; } static bool handler_remove_by_service(void *a, void *b) { struct btp_handler *handler = a; uint8_t service = L_PTR_TO_UINT(b); if (handler->service != service) return false; if (handler->destroy) handler->destroy(handler->user_data); l_free(handler); return true; } void btp_unregister_service(struct btp *btp, uint8_t service) { l_queue_foreach_remove(btp->handlers, handler_remove_by_service, L_UINT_TO_PTR(service)); } bluez-5.82/src/shared/PaxHeaders/hci-crypto.h0000644000000000000000000000005014015011623016126 xustar0020 atime=1743515871 20 ctime=1743591277 bluez-5.82/src/shared/hci-crypto.h0000644000000000000000000000172314015011623015612 0ustar00rootroot/* SPDX-License-Identifier: LGPL-2.1-or-later */ /* * * BlueZ - Bluetooth protocol stack for Linux * * Copyright (C) 2013-2014 Intel Corporation * * */ #include #include struct bt_hci; typedef void (*bt_hci_crypto_func_t)(const void *data, uint8_t size, void *user_data); bool bt_hci_crypto_prand(struct bt_hci *hci, bt_hci_crypto_func_t callback, void *user_data); bool bt_hci_crypto_e(struct bt_hci *hci, const uint8_t key[16], const uint8_t plaintext[16], bt_hci_crypto_func_t callback, void *user_data); bool bt_hci_crypto_d1(struct bt_hci *hci, const uint8_t k[16], uint16_t d, uint16_t r, bt_hci_crypto_func_t callback, void *user_data); bool bt_hci_crypto_dm(struct bt_hci *hci, const uint8_t k[16], const uint8_t r[8], bt_hci_crypto_func_t callback, void *user_data); bool bt_hci_crypto_ah(struct bt_hci *hci, const uint8_t k[16], const uint8_t r[3], bt_hci_crypto_func_t callback, void *user_data); bluez-5.82/src/shared/PaxHeaders/timeout-ell.c0000644000000000000000000000005014772767672016340 xustar0020 atime=1743515579 20 ctime=1743591277 bluez-5.82/src/shared/timeout-ell.c0000644000000000000000000000402714772767672016024 0ustar00rootroot// SPDX-License-Identifier: LGPL-2.1-or-later /* * * BlueZ - Bluetooth protocol stack for Linux * * Copyright (C) 2019 Intel Corporation. All rights reserved. * * */ #include #include #include "timeout.h" static struct l_queue *timeout_q; struct timeout_data { timeout_func_t func; timeout_destroy_func_t destroy; void *user_data; unsigned int timeout; }; static bool match_id(const void *a, const void *b) { unsigned int to_id = L_PTR_TO_UINT(a); unsigned int id = L_PTR_TO_UINT(b); return (to_id == id); } static void timeout_callback(struct l_timeout *timeout, void *user_data) { struct timeout_data *data = user_data; if (data->func) data->func(data->user_data); l_timeout_modify(timeout, data->timeout); } static void timeout_destroy(void *user_data) { struct timeout_data *data = user_data; if (data->destroy) data->destroy(data->user_data); l_free(data); } unsigned int timeout_add(unsigned int timeout, timeout_func_t func, void *user_data, timeout_destroy_func_t destroy) { struct timeout_data *data; unsigned int id = 0; struct l_timeout *to; int tries = 0; if (!timeout_q) timeout_q = l_queue_new(); data = l_new(struct timeout_data, 1); data->func = func; data->destroy = destroy; data->user_data = user_data; data->timeout = timeout; while (id == 0 && tries < 3) { to = l_timeout_create_ms(timeout, timeout_callback, data, timeout_destroy); if (!to) break; tries++; id = L_PTR_TO_UINT(to); if (id == 0 || l_queue_find(timeout_q, match_id, L_UINT_TO_PTR(id))) { l_timeout_remove(to); continue; } l_queue_push_tail(timeout_q, to); } if (id == 0) l_free(data); return id; } void timeout_remove(unsigned int id) { struct l_timeout *to; to = l_queue_remove_if(timeout_q, match_id, L_UINT_TO_PTR(id)); if (to) l_timeout_remove(to); } unsigned int timeout_add_seconds(unsigned int timeout, timeout_func_t func, void *user_data, timeout_destroy_func_t destroy) { return timeout_add(timeout * 1000, func, user_data, destroy); } bluez-5.82/src/shared/PaxHeaders/queue.h0000644000000000000000000000005014015011623015171 xustar0020 atime=1743515782 20 ctime=1743591277 bluez-5.82/src/shared/queue.h0000644000000000000000000000277614015011623014666 0ustar00rootroot/* SPDX-License-Identifier: LGPL-2.1-or-later */ /* * * BlueZ - Bluetooth protocol stack for Linux * * Copyright (C) 2012-2014 Intel Corporation. All rights reserved. * * */ #include typedef void (*queue_destroy_func_t)(void *data); struct queue; struct queue_entry { void *data; struct queue_entry *next; }; struct queue *queue_new(void); void queue_destroy(struct queue *queue, queue_destroy_func_t destroy); bool queue_push_tail(struct queue *queue, void *data); bool queue_push_head(struct queue *queue, void *data); bool queue_push_after(struct queue *queue, void *entry, void *data); void *queue_pop_head(struct queue *queue); void *queue_peek_head(struct queue *queue); void *queue_peek_tail(struct queue *queue); typedef void (*queue_foreach_func_t)(void *data, void *user_data); void queue_foreach(struct queue *queue, queue_foreach_func_t function, void *user_data); typedef bool (*queue_match_func_t)(const void *data, const void *match_data); void *queue_find(struct queue *queue, queue_match_func_t function, const void *match_data); bool queue_remove(struct queue *queue, void *data); void *queue_remove_if(struct queue *queue, queue_match_func_t function, void *user_data); unsigned int queue_remove_all(struct queue *queue, queue_match_func_t function, void *user_data, queue_destroy_func_t destroy); const struct queue_entry *queue_get_entries(struct queue *queue); unsigned int queue_length(struct queue *queue); bool queue_isempty(struct queue *queue); bluez-5.82/src/shared/PaxHeaders/io-glib.c0000644000000000000000000000005014015011623015362 xustar0020 atime=1743515940 20 ctime=1743591277 bluez-5.82/src/shared/io-glib.c0000644000000000000000000001201714015011623015044 0ustar00rootroot// SPDX-License-Identifier: LGPL-2.1-or-later /* * * BlueZ - Bluetooth protocol stack for Linux * * Copyright (C) 2012-2014 Intel Corporation. All rights reserved. * * */ #ifdef HAVE_CONFIG_H #include #endif #include #include #include "src/shared/io.h" struct io_watch { struct io *io; guint id; io_callback_func_t callback; io_destroy_func_t destroy; void *user_data; }; struct io { int ref_count; GIOChannel *channel; struct io_watch *read_watch; struct io_watch *write_watch; struct io_watch *disconnect_watch; }; static struct io *io_ref(struct io *io) { if (!io) return NULL; __sync_fetch_and_add(&io->ref_count, 1); return io; } static void io_unref(struct io *io) { if (!io) return; if (__sync_sub_and_fetch(&io->ref_count, 1)) return; g_free(io); } struct io *io_new(int fd) { struct io *io; if (fd < 0) return NULL; io = g_try_new0(struct io, 1); if (!io) return NULL; io->channel = g_io_channel_unix_new(fd); g_io_channel_set_encoding(io->channel, NULL, NULL); g_io_channel_set_buffered(io->channel, FALSE); g_io_channel_set_close_on_unref(io->channel, FALSE); return io_ref(io); } static void watch_destroy(void *user_data) { struct io_watch *watch = user_data; struct io *io = watch->io; if (watch == io->read_watch) io->read_watch = NULL; else if (watch == io->write_watch) io->write_watch = NULL; else if (watch == io->disconnect_watch) io->disconnect_watch = NULL; if (watch->destroy) watch->destroy(watch->user_data); io_unref(watch->io); g_free(watch); } void io_destroy(struct io *io) { if (!io) return; if (io->read_watch) { g_source_remove(io->read_watch->id); io->read_watch = NULL; } if (io->write_watch) { g_source_remove(io->write_watch->id); io->write_watch = NULL; } if (io->disconnect_watch) { g_source_remove(io->disconnect_watch->id); io->disconnect_watch = NULL; } g_io_channel_unref(io->channel); io->channel = NULL; io_unref(io); } int io_get_fd(struct io *io) { if (!io) return -ENOTCONN; return g_io_channel_unix_get_fd(io->channel); } bool io_set_close_on_destroy(struct io *io, bool do_close) { if (!io) return false; if (do_close) g_io_channel_set_close_on_unref(io->channel, TRUE); else g_io_channel_set_close_on_unref(io->channel, FALSE); return true; } static gboolean watch_callback(GIOChannel *channel, GIOCondition cond, gpointer user_data) { struct io_watch *watch = user_data; bool result, destroy; destroy = watch == watch->io->disconnect_watch; if (!destroy && (cond & (G_IO_ERR | G_IO_NVAL))) return FALSE; if (watch->callback) result = watch->callback(watch->io, watch->user_data); else result = false; return result ? TRUE : FALSE; } static struct io_watch *watch_new(struct io *io, GIOCondition cond, io_callback_func_t callback, void *user_data, io_destroy_func_t destroy) { struct io_watch *watch; int prio; watch = g_try_new0(struct io_watch, 1); if (!watch) return NULL; watch->io = io_ref(io); watch->callback = callback; watch->destroy = destroy; watch->user_data = user_data; prio = cond == G_IO_HUP ? G_PRIORITY_DEFAULT_IDLE : G_PRIORITY_DEFAULT; watch->id = g_io_add_watch_full(io->channel, prio, cond | G_IO_ERR | G_IO_NVAL, watch_callback, watch, watch_destroy); if (watch->id == 0) { watch_destroy(watch); return NULL; } return watch; } static bool io_set_handler(struct io *io, GIOCondition cond, io_callback_func_t callback, void *user_data, io_destroy_func_t destroy) { struct io_watch **watch; if (!io) return false; switch (cond) { case G_IO_IN: watch = &io->read_watch; break; case G_IO_OUT: watch = &io->write_watch; break; case G_IO_HUP: watch = &io->disconnect_watch; break; case G_IO_PRI: case G_IO_ERR: case G_IO_NVAL: default: return false; } if (*watch) { g_source_remove((*watch)->id); *watch = NULL; } if (!callback) return true; *watch = watch_new(io, cond, callback, user_data, destroy); if (!*watch) return false; return true; } bool io_set_read_handler(struct io *io, io_callback_func_t callback, void *user_data, io_destroy_func_t destroy) { return io_set_handler(io, G_IO_IN, callback, user_data, destroy); } bool io_set_write_handler(struct io *io, io_callback_func_t callback, void *user_data, io_destroy_func_t destroy) { return io_set_handler(io, G_IO_OUT, callback, user_data, destroy); } bool io_set_disconnect_handler(struct io *io, io_callback_func_t callback, void *user_data, io_destroy_func_t destroy) { return io_set_handler(io, G_IO_HUP, callback, user_data, destroy); } ssize_t io_send(struct io *io, const struct iovec *iov, int iovcnt) { int fd; ssize_t ret; if (!io || !io->channel) return -ENOTCONN; fd = io_get_fd(io); do { ret = writev(fd, iov, iovcnt); } while (ret < 0 && errno == EINTR); if (ret < 0) return -errno; return ret; } bool io_shutdown(struct io *io) { if (!io || !io->channel) return false; return g_io_channel_shutdown(io->channel, TRUE, NULL) == G_IO_STATUS_NORMAL; } bluez-5.82/src/shared/PaxHeaders/crypto.c0000644000000000000000000000005014447506754015407 xustar0020 atime=1743515862 20 ctime=1743591277 bluez-5.82/src/shared/crypto.c0000644000000000000000000005356014447506754015101 0ustar00rootroot// SPDX-License-Identifier: LGPL-2.1-or-later /* * * BlueZ - Bluetooth protocol stack for Linux * * Copyright (C) 2012-2014 Intel Corporation. All rights reserved. * * */ #ifdef HAVE_CONFIG_H #include #endif #include #include #include #include #include "src/shared/util.h" #include "src/shared/crypto.h" #ifndef HAVE_LINUX_IF_ALG_H #ifndef HAVE_LINUX_TYPES_H typedef uint8_t __u8; typedef uint16_t __u16; typedef uint32_t __u32; #else #include #endif struct sockaddr_alg { __u16 salg_family; __u8 salg_type[14]; __u32 salg_feat; __u32 salg_mask; __u8 salg_name[64]; }; struct af_alg_iv { __u32 ivlen; __u8 iv[0]; }; #define ALG_SET_KEY 1 #define ALG_SET_IV 2 #define ALG_SET_OP 3 #define ALG_OP_DECRYPT 0 #define ALG_OP_ENCRYPT 1 #define PF_ALG 38 /* Algorithm sockets. */ #define AF_ALG PF_ALG #else #include #endif #ifndef SOL_ALG #define SOL_ALG 279 #endif /* Maximum message length that can be passed to aes_cmac */ #define CMAC_MSG_MAX 80 #define ATT_SIGN_LEN 12 struct bt_crypto { int ref_count; int ecb_aes; int urandom; int cmac_aes; }; static int urandom_setup(void) { int fd; fd = open("/dev/urandom", O_RDONLY); if (fd < 0) return -1; return fd; } static int ecb_aes_setup(void) { struct sockaddr_alg salg; int fd; fd = socket(PF_ALG, SOCK_SEQPACKET | SOCK_CLOEXEC, 0); if (fd < 0) return -1; memset(&salg, 0, sizeof(salg)); salg.salg_family = AF_ALG; strcpy((char *) salg.salg_type, "skcipher"); strcpy((char *) salg.salg_name, "ecb(aes)"); if (bind(fd, (struct sockaddr *) &salg, sizeof(salg)) < 0) { close(fd); return -1; } return fd; } static int cmac_aes_setup(void) { struct sockaddr_alg salg; int fd; fd = socket(PF_ALG, SOCK_SEQPACKET | SOCK_CLOEXEC, 0); if (fd < 0) return -1; memset(&salg, 0, sizeof(salg)); salg.salg_family = AF_ALG; strcpy((char *) salg.salg_type, "hash"); strcpy((char *) salg.salg_name, "cmac(aes)"); if (bind(fd, (struct sockaddr *) &salg, sizeof(salg)) < 0) { close(fd); return -1; } return fd; } static struct bt_crypto *singleton; struct bt_crypto *bt_crypto_new(void) { if (singleton) return bt_crypto_ref(singleton); singleton = new0(struct bt_crypto, 1); singleton->ecb_aes = ecb_aes_setup(); if (singleton->ecb_aes < 0) { free(singleton); singleton = NULL; return NULL; } singleton->urandom = urandom_setup(); if (singleton->urandom < 0) { close(singleton->ecb_aes); free(singleton); singleton = NULL; return NULL; } singleton->cmac_aes = cmac_aes_setup(); if (singleton->cmac_aes < 0) { close(singleton->urandom); close(singleton->ecb_aes); free(singleton); singleton = NULL; return NULL; } return bt_crypto_ref(singleton); } struct bt_crypto *bt_crypto_ref(struct bt_crypto *crypto) { if (!crypto) return NULL; __sync_fetch_and_add(&crypto->ref_count, 1); return crypto; } void bt_crypto_unref(struct bt_crypto *crypto) { if (!crypto) return; if (__sync_sub_and_fetch(&crypto->ref_count, 1)) return; close(crypto->urandom); close(crypto->ecb_aes); close(crypto->cmac_aes); free(crypto); singleton = NULL; } bool bt_crypto_random_bytes(struct bt_crypto *crypto, void *buf, uint8_t num_bytes) { ssize_t len; if (!crypto) return false; len = read(crypto->urandom, buf, num_bytes); if (len < num_bytes) return false; return true; } static int alg_new(int fd, const void *keyval, socklen_t keylen) { if (setsockopt(fd, SOL_ALG, ALG_SET_KEY, keyval, keylen) < 0) return -1; /* FIXME: This should use accept4() with SOCK_CLOEXEC */ return accept(fd, NULL, 0); } static bool alg_encrypt(int fd, const void *inbuf, size_t inlen, void *outbuf, size_t outlen) { __u32 alg_op = ALG_OP_ENCRYPT; char cbuf[CMSG_SPACE(sizeof(alg_op))]; struct cmsghdr *cmsg; struct msghdr msg; struct iovec iov; ssize_t len; memset(cbuf, 0, sizeof(cbuf)); memset(&msg, 0, sizeof(msg)); msg.msg_control = cbuf; msg.msg_controllen = sizeof(cbuf); cmsg = CMSG_FIRSTHDR(&msg); cmsg->cmsg_level = SOL_ALG; cmsg->cmsg_type = ALG_SET_OP; cmsg->cmsg_len = CMSG_LEN(sizeof(alg_op)); memcpy(CMSG_DATA(cmsg), &alg_op, sizeof(alg_op)); iov.iov_base = (void *) inbuf; iov.iov_len = inlen; msg.msg_iov = &iov; msg.msg_iovlen = 1; len = sendmsg(fd, &msg, 0); if (len < 0) return false; len = read(fd, outbuf, outlen); if (len < 0) return false; return true; } static inline void swap_buf(const uint8_t *src, uint8_t *dst, uint16_t len) { int i; for (i = 0; i < len; i++) dst[len - 1 - i] = src[i]; } bool bt_crypto_sign_att(struct bt_crypto *crypto, const uint8_t key[16], const uint8_t *m, uint16_t m_len, uint32_t sign_cnt, uint8_t signature[ATT_SIGN_LEN]) { int fd; int len; uint8_t tmp[16], out[16]; uint16_t msg_len = m_len + sizeof(uint32_t); uint8_t msg[msg_len]; uint8_t msg_s[msg_len]; if (!crypto) return false; memset(msg, 0, msg_len); memcpy(msg, m, m_len); /* Add sign_counter to the message */ put_le32(sign_cnt, msg + m_len); /* The most significant octet of key corresponds to key[0] */ swap_buf(key, tmp, 16); fd = alg_new(crypto->cmac_aes, tmp, 16); if (fd < 0) return false; /* Swap msg before signing */ swap_buf(msg, msg_s, msg_len); len = send(fd, msg_s, msg_len, 0); if (len < 0) { close(fd); return false; } len = read(fd, out, 16); if (len < 0) { close(fd); return false; } close(fd); /* * As to BT spec. 4.1 Vol[3], Part C, chapter 10.4.1 sign counter should * be placed in the signature */ put_be32(sign_cnt, out + 8); /* * The most significant octet of hash corresponds to out[0] - swap it. * Then truncate in most significant bit first order to a length of * 12 octets */ swap_buf(out, tmp, 16); memcpy(signature, tmp + 4, ATT_SIGN_LEN); return true; } bool bt_crypto_verify_att_sign(struct bt_crypto *crypto, const uint8_t key[16], const uint8_t *pdu, uint16_t pdu_len) { uint8_t generated_sign[ATT_SIGN_LEN]; const uint8_t *sign; uint32_t sign_cnt; if (pdu_len < ATT_SIGN_LEN) return false; sign = pdu + pdu_len - ATT_SIGN_LEN; sign_cnt = get_le32(sign); if (!bt_crypto_sign_att(crypto, key, pdu, pdu_len - ATT_SIGN_LEN, sign_cnt, generated_sign)) return false; return memcmp(generated_sign, sign, ATT_SIGN_LEN) == 0; } /* * Security function e * * Security function e generates 128-bit encryptedData from a 128-bit key * and 128-bit plaintextData using the AES-128-bit block cypher: * * encryptedData = e(key, plaintextData) * * The most significant octet of key corresponds to key[0], the most * significant octet of plaintextData corresponds to in[0] and the * most significant octet of encryptedData corresponds to out[0]. * */ bool bt_crypto_e(struct bt_crypto *crypto, const uint8_t key[16], const uint8_t plaintext[16], uint8_t encrypted[16]) { uint8_t tmp[16], in[16], out[16]; int fd; if (!crypto) return false; /* The most significant octet of key corresponds to key[0] */ swap_buf(key, tmp, 16); fd = alg_new(crypto->ecb_aes, tmp, 16); if (fd < 0) return false; /* Most significant octet of plaintextData corresponds to in[0] */ swap_buf(plaintext, in, 16); if (!alg_encrypt(fd, in, 16, out, 16)) { close(fd); return false; } /* Most significant octet of encryptedData corresponds to out[0] */ swap_buf(out, encrypted, 16); close(fd); return true; } /* * Random Address Hash function ah * * The random address hash function ah is used to generate a hash value * that is used in resolvable private addresses. * * The following are inputs to the random address hash function ah: * * k is 128 bits * r is 24 bits * padding is 104 bits * * r is concatenated with padding to generate r' which is used as the * 128-bit input parameter plaintextData to security function e: * * r' = padding || r * * The least significant octet of r becomes the least significant octet * of r’ and the most significant octet of padding becomes the most * significant octet of r'. * * For example, if the 24-bit value r is 0x423456 then r' is * 0x00000000000000000000000000423456. * * The output of the random address function ah is: * * ah(k, r) = e(k, r') mod 2^24 * * The output of the security function e is then truncated to 24 bits by * taking the least significant 24 bits of the output of e as the result * of ah. */ bool bt_crypto_ah(struct bt_crypto *crypto, const uint8_t k[16], const uint8_t r[3], uint8_t hash[3]) { uint8_t rp[16]; uint8_t encrypted[16]; if (!crypto) return false; /* r' = padding || r */ memcpy(rp, r, 3); memset(rp + 3, 0, 13); /* e(k, r') */ if (!bt_crypto_e(crypto, k, rp, encrypted)) return false; /* ah(k, r) = e(k, r') mod 2^24 */ memcpy(hash, encrypted, 3); return true; } typedef struct { uint64_t a, b; } u128; static inline void u128_xor(const uint8_t p[16], const uint8_t q[16], uint8_t r[16]) { u128 pp, qq, rr; memcpy(&pp, p, 16); memcpy(&qq, q, 16); rr.a = pp.a ^ qq.a; rr.b = pp.b ^ qq.b; memcpy(r, &rr, 16); } /* * Confirm value generation function c1 * * During the pairing process confirm values are exchanged. This confirm * value generation function c1 is used to generate the confirm values. * * The following are inputs to the confirm value generation function c1: * * k is 128 bits * r is 128 bits * pres is 56 bits * preq is 56 bits * iat is 1 bit * ia is 48 bits * rat is 1 bit * ra is 48 bits * padding is 32 bits of 0 * * iat is concatenated with 7-bits of 0 to create iat' which is 8 bits * in length. iat is the least significant bit of iat' * * rat is concatenated with 7-bits of 0 to create rat' which is 8 bits * in length. rat is the least significant bit of rat' * * pres, preq, rat' and iat' are concatenated to generate p1 which is * XORed with r and used as 128-bit input parameter plaintextData to * security function e: * * p1 = pres || preq || rat' || iat' * * The octet of iat' becomes the least significant octet of p1 and the * most significant octet of pres becomes the most significant octet of * p1. * * ra is concatenated with ia and padding to generate p2 which is XORed * with the result of the security function e using p1 as the input * paremter plaintextData and is then used as the 128-bit input * parameter plaintextData to security function e: * * p2 = padding || ia || ra * * The least significant octet of ra becomes the least significant octet * of p2 and the most significant octet of padding becomes the most * significant octet of p2. * * The output of the confirm value generation function c1 is: * * c1(k, r, preq, pres, iat, rat, ia, ra) = e(k, e(k, r XOR p1) XOR p2) * * The 128-bit output of the security function e is used as the result * of confirm value generation function c1. */ bool bt_crypto_c1(struct bt_crypto *crypto, const uint8_t k[16], const uint8_t r[16], const uint8_t pres[7], const uint8_t preq[7], uint8_t iat, const uint8_t ia[6], uint8_t rat, const uint8_t ra[6], uint8_t res[16]) { uint8_t p1[16], p2[16]; /* p1 = pres || preq || _rat || _iat */ p1[0] = iat; p1[1] = rat; memcpy(p1 + 2, preq, 7); memcpy(p1 + 9, pres, 7); /* p2 = padding || ia || ra */ memcpy(p2, ra, 6); memcpy(p2 + 6, ia, 6); memset(p2 + 12, 0, 4); /* res = r XOR p1 */ u128_xor(r, p1, res); /* res = e(k, res) */ if (!bt_crypto_e(crypto, k, res, res)) return false; /* res = res XOR p2 */ u128_xor(res, p2, res); /* res = e(k, res) */ return bt_crypto_e(crypto, k, res, res); } /* * Key generation function s1 * * The key generation function s1 is used to generate the STK during the * pairing process. * * The following are inputs to the key generation function s1: * * k is 128 bits * r1 is 128 bits * r2 is 128 bits * * The most significant 64-bits of r1 are discarded to generate r1' and * the most significant 64-bits of r2 are discarded to generate r2'. * * r1' is concatenated with r2' to generate r' which is used as the * 128-bit input parameter plaintextData to security function e: * * r' = r1' || r2' * * The least significant octet of r2' becomes the least significant * octet of r' and the most significant octet of r1' becomes the most * significant octet of r'. * * The output of the key generation function s1 is: * * s1(k, r1, r2) = e(k, r') * * The 128-bit output of the security function e is used as the result * of key generation function s1. */ bool bt_crypto_s1(struct bt_crypto *crypto, const uint8_t k[16], const uint8_t r1[16], const uint8_t r2[16], uint8_t res[16]) { memcpy(res, r2, 8); memcpy(res + 8, r1, 8); return bt_crypto_e(crypto, k, res, res); } static bool aes_cmac_be(struct bt_crypto *crypto, const uint8_t key[16], const uint8_t *msg, size_t msg_len, uint8_t res[16]) { ssize_t len; int fd; if (msg_len > CMAC_MSG_MAX) return false; fd = alg_new(crypto->cmac_aes, key, 16); if (fd < 0) return false; len = send(fd, msg, msg_len, 0); if (len < 0) { close(fd); return false; } len = read(fd, res, 16); if (len < 0) { close(fd); return false; } close(fd); return true; } static bool aes_cmac(struct bt_crypto *crypto, const uint8_t key[16], const uint8_t *msg, size_t msg_len, uint8_t res[16]) { uint8_t key_msb[16], out[16], msg_msb[CMAC_MSG_MAX]; if (msg_len > CMAC_MSG_MAX) return false; swap_buf(key, key_msb, 16); swap_buf(msg, msg_msb, msg_len); if (!aes_cmac_be(crypto, key_msb, msg_msb, msg_len, out)) return false; swap_buf(out, res, 16); return true; } bool bt_crypto_f4(struct bt_crypto *crypto, uint8_t u[32], uint8_t v[32], uint8_t x[16], uint8_t z, uint8_t res[16]) { uint8_t m[65]; if (!crypto) return false; m[0] = z; memcpy(&m[1], v, 32); memcpy(&m[33], u, 32); return aes_cmac(crypto, x, m, sizeof(m), res); } bool bt_crypto_f5(struct bt_crypto *crypto, uint8_t w[32], uint8_t n1[16], uint8_t n2[16], uint8_t a1[7], uint8_t a2[7], uint8_t mackey[16], uint8_t ltk[16]) { uint8_t btle[4] = { 0x65, 0x6c, 0x74, 0x62 }; uint8_t salt[16] = { 0xbe, 0x83, 0x60, 0x5a, 0xdb, 0x0b, 0x37, 0x60, 0x38, 0xa5, 0xf5, 0xaa, 0x91, 0x83, 0x88, 0x6c }; uint8_t length[2] = { 0x00, 0x01 }; uint8_t m[53], t[16]; if (!aes_cmac(crypto, salt, w, 32, t)) return false; memcpy(&m[0], length, 2); memcpy(&m[2], a2, 7); memcpy(&m[9], a1, 7); memcpy(&m[16], n2, 16); memcpy(&m[32], n1, 16); memcpy(&m[48], btle, 4); m[52] = 0; /* Counter */ if (!aes_cmac(crypto, t, m, sizeof(m), mackey)) return false; m[52] = 1; /* Counter */ return aes_cmac(crypto, t, m, sizeof(m), ltk); } bool bt_crypto_f6(struct bt_crypto *crypto, uint8_t w[16], uint8_t n1[16], uint8_t n2[16], uint8_t r[16], uint8_t io_cap[3], uint8_t a1[7], uint8_t a2[7], uint8_t res[16]) { uint8_t m[65]; memcpy(&m[0], a2, 7); memcpy(&m[7], a1, 7); memcpy(&m[14], io_cap, 3); memcpy(&m[17], r, 16); memcpy(&m[33], n2, 16); memcpy(&m[49], n1, 16); return aes_cmac(crypto, w, m, sizeof(m), res); } bool bt_crypto_g2(struct bt_crypto *crypto, uint8_t u[32], uint8_t v[32], uint8_t x[16], uint8_t y[16], uint32_t *val) { uint8_t m[80], tmp[16]; memcpy(&m[0], y, 16); memcpy(&m[16], v, 32); memcpy(&m[48], u, 32); if (!aes_cmac(crypto, x, m, sizeof(m), tmp)) return false; *val = get_le32(tmp); *val %= 1000000; return true; } bool bt_crypto_h6(struct bt_crypto *crypto, const uint8_t w[16], const uint8_t keyid[4], uint8_t res[16]) { if (!aes_cmac(crypto, w, keyid, 4, res)) return false; return true; } bool bt_crypto_gatt_hash(struct bt_crypto *crypto, struct iovec *iov, size_t iov_len, uint8_t res[16]) { const uint8_t key[16] = {}; ssize_t len; int fd; if (!crypto) return false; fd = alg_new(crypto->cmac_aes, key, 16); if (fd < 0) return false; len = writev(fd, iov, iov_len); if (len < 0) { close(fd); return false; } len = read(fd, res, 16); if (len < 0) { close(fd); return false; } close(fd); return true; } /* * Resolvable Set Identifier hash function sih * * The RSI hash function sih is used to generate a hash value that is used in * RSIs. * * The following variables are the inputs to the RSI hash function sih: * * k is 128 bits * r is 24 bits * padding is 104 bits, all set to 0 * * r is concatenated with padding to generate r', which is used as the 128-bit * input parameter plaintextData to security function e: * * r'=padding||r * * The LSO of r becomes the LSO of r', and the MSO of padding becomes the MSO * of r'. * * For example, if the 24-bit value r is 0x3A98B5, then r' is * 0x000000000000000000000000003A98B5. * * The output of the Resolvable Set Identifier function sih is: * * sih(k, r)=e(k, r') mod 2^24 * * The output of the security function e is truncated to 24 bits by taking the * least significant 24 bits of the output of e as the result of sih. */ bool bt_crypto_sih(struct bt_crypto *crypto, const uint8_t k[16], const uint8_t r[3], uint8_t hash[3]) { return bt_crypto_ah(crypto, k, r, hash); } static bool aes_cmac_zero(struct bt_crypto *crypto, const uint8_t *msg, size_t msg_len, uint8_t res[16]) { const uint8_t zero[16] = {}; return aes_cmac_be(crypto, zero, msg, msg_len, res); } /* The inputs to function s1 are: * * M is a non-zero length octet array or ASCII encoded string * * If M is an ASCII encoded string, M shall be converted into an integer number * by replacing each string character with its ASCII code preserving the order. * For example, if M is the string “CSIS”, M is converted into the integer * number: 0x4353 4953. * * ZERO is the 128-bit value: * * 0x0000 0000 0000 0000 0000 0000 0000 0000 * * The output of the salt generation function s1 shall be calculated as follows: * * s1(M)=AES‐CMACZERO(M) * * Where AES-CMACZERO is the CMAC function defined in Section 4.2. */ static bool sef_s1(struct bt_crypto *crypto, const uint8_t *m, size_t m_len, uint8_t res[16]) { /* s1(M)=AES‐CMACZERO(M) */ return aes_cmac_zero(crypto, m, m_len, res); } /* The key derivation function k1 is used to derive a key. The derived key is * used to encrypt and decrypt the value of the Set Identity Resolving Key * characteristic (see Section 5.1). * * The definition of this key generation function uses the MAC function * AES-CMACT with a 128-bit key T. * * The inputs to function k1 are: * * N is 0 or more octets * * SALT is 128 bits * * P is 0 or more octets * * The key (T) shall be computed as follows: * * T=AES‐CMACSALT(N) * * Where AES-CMACSALT is the CMAC function defined in Section 4.2. * * The output of the key generation function k1 shall be calculated as follows: * * k1(N, SALT, P)=AES‐CMACT(P) * * Where AES-CMACT is the CMAC function defined in Section 4.2. */ static bool sef_k1(struct bt_crypto *crypto, const uint8_t n[16], uint8_t salt[16], const uint8_t *p, size_t p_len, uint8_t res[16]) { uint8_t res1[16]; /* T=AES‐CMACSALT(N) */ if (!aes_cmac_be(crypto, salt, n, 16, res1)) return false; /* k1(N, SALT, P)=AES‐CMACT(P) */ return aes_cmac_be(crypto, res1, p, p_len, res); } /* * SIRK encryption function sef * * The SIRK encryption function sef shall be used by the server to encrypt the * SIRK with a key K. The value of K depends on the transport on which the Set * Identity Resolving Key characteristic is read or notified. * * If the Set Identity Resolving Key characteristic is read or notified on the * Basic Rate/Enhanced Data Rate (BR/EDR) transport, K shall be equal to the * Link Key shared by the server and the client. * * K=LinkKey * * If the Set Identity Resolving Key characteristic is read or notified on the * Bluetooth Low Energy (LE) transport, K shall be equal to the LTK shared by * the server and client. That is, * * K=LTK * * The inputs to the function sef are: * * K is the key defined above in this section * * SIRK is the value of the SIRK to be encrypted * * The output of the SIRK encryption function sef is as follows: * * sef(K, SIRK)=k1(K, s1(“SIRKenc”), “csis”)^SIRK * * Where ^ is the bitwise exclusive or operation. */ bool bt_crypto_sef(struct bt_crypto *crypto, const uint8_t k[16], const uint8_t sirk[16], uint8_t out[16]) { const uint8_t m[] = {'S', 'I', 'R', 'K', 'e', 'n', 'c'}; const uint8_t p[] = {'c', 's', 'i', 's'}; uint8_t k_msb[16]; uint8_t salt[16]; uint8_t res_msb[16]; uint8_t res[16]; if (!crypto) return false; /* salt = s1(“SIRKenc”) */ if (!sef_s1(crypto, m, sizeof(m), salt)) return false; /* Convert K to MSB/BE format */ swap_buf(k, k_msb, 16); /* res_msb = k1(K, salt, “csis”) */ if (!sef_k1(crypto, k_msb, salt, p, sizeof(p), res_msb)) return false; /* Convert back to LSB/LE format */ swap_buf(res_msb, res, 16); /* res^SIRK */ u128_xor(res, sirk, out); return true; } /* Generates a SIRK from a string using the following steps: * - Generate a hash (k) using the str as input * - Generate a hash (sirk) using vendor, product, version and source as input * - Encrypt sirk using k as LTK with sef function. */ bool bt_crypto_sirk(struct bt_crypto *crypto, const char *str, uint16_t vendor, uint16_t product, uint16_t version, uint16_t source, uint8_t sirk[16]) { struct iovec iov[4]; uint8_t k[16]; uint8_t sirk_plaintext[16]; if (!crypto) return false; iov[0].iov_base = (void *)str; iov[0].iov_len = strlen(str); /* Generate a k using the str as input */ if (!bt_crypto_gatt_hash(crypto, iov, 1, k)) return false; iov[0].iov_base = &vendor; iov[0].iov_len = sizeof(vendor); iov[1].iov_base = &product; iov[1].iov_len = sizeof(product); iov[2].iov_base = &version; iov[2].iov_len = sizeof(version); iov[3].iov_base = &source; iov[3].iov_len = sizeof(source); /* Generate a sirk using vendor, product, version and source as input */ if (!bt_crypto_gatt_hash(crypto, iov, 4, sirk_plaintext)) return false; /* Encrypt sirk using k as LTK with sef function */ return bt_crypto_sef(crypto, k, sirk_plaintext, sirk); } bluez-5.82/src/shared/PaxHeaders/mgmt.c0000644000000000000000000000005014267331527015025 xustar0020 atime=1743515860 20 ctime=1743591277 bluez-5.82/src/shared/mgmt.c0000644000000000000000000005205114267331527014511 0ustar00rootroot// SPDX-License-Identifier: LGPL-2.1-or-later /* * * BlueZ - Bluetooth protocol stack for Linux * * Copyright (C) 2012-2014 Intel Corporation. All rights reserved. * * */ #ifdef HAVE_CONFIG_H #include #endif #include #include #include #include #include "lib/bluetooth.h" #include "lib/mgmt.h" #include "lib/hci.h" #include "src/shared/io.h" #include "src/shared/queue.h" #include "src/shared/util.h" #include "src/shared/mgmt.h" #include "src/shared/timeout.h" #define DBG(_mgmt, _format, arg...) \ mgmt_log(_mgmt, "%s:%s() " _format, __FILE__, __func__, ## arg) struct mgmt { int ref_count; int fd; bool close_on_unref; struct io *io; bool writer_active; struct queue *request_queue; struct queue *reply_queue; struct queue *pending_list; struct queue *notify_list; unsigned int next_request_id; unsigned int next_notify_id; bool need_notify_cleanup; bool in_notify; void *buf; uint16_t len; uint16_t mtu; mgmt_debug_func_t debug_callback; mgmt_destroy_func_t debug_destroy; void *debug_data; }; struct mgmt_request { struct mgmt *mgmt; unsigned int id; uint16_t opcode; uint16_t index; void *buf; uint16_t len; mgmt_request_func_t callback; mgmt_destroy_func_t destroy; void *user_data; int timeout; unsigned int timeout_id; }; struct mgmt_notify { unsigned int id; uint16_t event; uint16_t index; bool removed; mgmt_notify_func_t callback; mgmt_destroy_func_t destroy; void *user_data; }; struct mgmt_tlv_list { struct queue *tlv_queue; uint16_t size; }; static void destroy_request(void *data) { struct mgmt_request *request = data; if (request->destroy) request->destroy(request->user_data); if (request->timeout_id) timeout_remove(request->timeout_id); free(request->buf); free(request); } static bool match_request_id(const void *a, const void *b) { const struct mgmt_request *request = a; unsigned int id = PTR_TO_UINT(b); return request->id == id; } static bool match_request_index(const void *a, const void *b) { const struct mgmt_request *request = a; uint16_t index = PTR_TO_UINT(b); return request->index == index; } static void destroy_notify(void *data) { struct mgmt_notify *notify = data; if (notify->destroy) notify->destroy(notify->user_data); free(notify); } static bool match_notify_id(const void *a, const void *b) { const struct mgmt_notify *notify = a; unsigned int id = PTR_TO_UINT(b); return notify->id == id; } static bool match_notify_index(const void *a, const void *b) { const struct mgmt_notify *notify = a; uint16_t index = PTR_TO_UINT(b); return notify->index == index; } static bool match_notify_removed(const void *a, const void *b) { const struct mgmt_notify *notify = a; return notify->removed; } static void mark_notify_removed(void *data , void *user_data) { struct mgmt_notify *notify = data; uint16_t index = PTR_TO_UINT(user_data); if (notify->index == index || index == MGMT_INDEX_NONE) notify->removed = true; } static void write_watch_destroy(void *user_data) { struct mgmt *mgmt = user_data; mgmt->writer_active = false; } static bool request_timeout(void *data) { struct mgmt_request *request = data; if (!request) return false; request->timeout_id = 0; queue_remove_if(request->mgmt->pending_list, NULL, request); if (request->callback) request->callback(MGMT_STATUS_TIMEOUT, 0, NULL, request->user_data); destroy_request(request); return false; } static void mgmt_log(struct mgmt *mgmt, const char *format, ...) { va_list ap; if (!mgmt || !format || !mgmt->debug_callback) return; va_start(ap, format); util_debug_va(mgmt->debug_callback, mgmt->debug_data, format, ap); va_end(ap); } static bool send_request(struct mgmt *mgmt, struct mgmt_request *request) { struct iovec iov; ssize_t ret; iov.iov_base = request->buf; iov.iov_len = request->len; ret = io_send(mgmt->io, &iov, 1); if (ret < 0) { DBG(mgmt, "write failed: %s", strerror(-ret)); if (request->callback) request->callback(MGMT_STATUS_FAILED, 0, NULL, request->user_data); destroy_request(request); return false; } if (request->timeout) request->timeout_id = timeout_add_seconds(request->timeout, request_timeout, request, NULL); DBG(mgmt, "[0x%04x] command 0x%04x", request->index, request->opcode); queue_push_tail(mgmt->pending_list, request); return true; } static bool can_write_data(struct io *io, void *user_data) { struct mgmt *mgmt = user_data; struct mgmt_request *request; bool can_write; request = queue_pop_head(mgmt->reply_queue); if (!request) { /* only reply commands can jump the queue */ if (!queue_isempty(mgmt->pending_list)) return false; request = queue_pop_head(mgmt->request_queue); if (!request) return false; can_write = false; } else { /* allow multiple replies to jump the queue */ can_write = !queue_isempty(mgmt->reply_queue); } if (!send_request(mgmt, request)) return true; return can_write; } static void wakeup_writer(struct mgmt *mgmt) { if (!queue_isempty(mgmt->pending_list)) { /* only queued reply commands trigger wakeup */ if (queue_isempty(mgmt->reply_queue)) return; } if (mgmt->writer_active) return; mgmt->writer_active = true; io_set_write_handler(mgmt->io, can_write_data, mgmt, write_watch_destroy); } struct opcode_index { uint16_t opcode; uint16_t index; }; static bool match_request_opcode_index(const void *a, const void *b) { const struct mgmt_request *request = a; const struct opcode_index *match = b; return request->opcode == match->opcode && request->index == match->index; } static void request_complete(struct mgmt *mgmt, uint8_t status, uint16_t opcode, uint16_t index, uint16_t length, const void *param) { struct opcode_index match = { .opcode = opcode, .index = index }; struct mgmt_request *request; request = queue_remove_if(mgmt->pending_list, match_request_opcode_index, &match); if (!request) { DBG(mgmt, "Unable to find request for opcode 0x%04x", opcode); /* Attempt to remove with no opcode */ request = queue_remove_if(mgmt->pending_list, match_request_index, UINT_TO_PTR(index)); } if (request) { if (request->callback) request->callback(status, length, param, request->user_data); destroy_request(request); } wakeup_writer(mgmt); } struct event_index { uint16_t event; uint16_t index; uint16_t length; const void *param; }; static void notify_handler(void *data, void *user_data) { struct mgmt_notify *notify = data; struct event_index *match = user_data; if (notify->removed) return; if (notify->event != match->event) return; if (notify->index != match->index && notify->index != MGMT_INDEX_NONE) return; if (notify->callback) notify->callback(match->index, match->length, match->param, notify->user_data); } static void process_notify(struct mgmt *mgmt, uint16_t event, uint16_t index, uint16_t length, const void *param) { struct event_index match = { .event = event, .index = index, .length = length, .param = param }; mgmt->in_notify = true; queue_foreach(mgmt->notify_list, notify_handler, &match); mgmt->in_notify = false; if (mgmt->need_notify_cleanup) { queue_remove_all(mgmt->notify_list, match_notify_removed, NULL, destroy_notify); mgmt->need_notify_cleanup = false; } } static bool can_read_data(struct io *io, void *user_data) { struct mgmt *mgmt = user_data; struct mgmt_hdr *hdr; struct mgmt_ev_cmd_complete *cc; struct mgmt_ev_cmd_status *cs; ssize_t bytes_read; uint16_t opcode, event, index, length; bytes_read = read(mgmt->fd, mgmt->buf, mgmt->len); if (bytes_read < 0) return false; if (bytes_read < MGMT_HDR_SIZE) return true; hdr = mgmt->buf; event = btohs(hdr->opcode); index = btohs(hdr->index); length = btohs(hdr->len); if (bytes_read < length + MGMT_HDR_SIZE) return true; mgmt_ref(mgmt); switch (event) { case MGMT_EV_CMD_COMPLETE: cc = mgmt->buf + MGMT_HDR_SIZE; opcode = btohs(cc->opcode); DBG(mgmt, "[0x%04x] command 0x%04x complete: 0x%02x", index, opcode, cc->status); request_complete(mgmt, cc->status, opcode, index, length - 3, mgmt->buf + MGMT_HDR_SIZE + 3); break; case MGMT_EV_CMD_STATUS: cs = mgmt->buf + MGMT_HDR_SIZE; opcode = btohs(cs->opcode); DBG(mgmt, "[0x%04x] command 0x%02x status: 0x%02x", index, opcode, cs->status); request_complete(mgmt, cs->status, opcode, index, 0, NULL); break; default: DBG(mgmt, "[0x%04x] event 0x%04x", index, event); process_notify(mgmt, event, index, length, mgmt->buf + MGMT_HDR_SIZE); break; } mgmt_unref(mgmt); return true; } static void mgmt_set_mtu(struct mgmt *mgmt) { socklen_t len = 0; /* Check if kernel support BT_SNDMTU to read the current MTU set */ if (getsockopt(mgmt->fd, SOL_BLUETOOTH, BT_SNDMTU, &mgmt->mtu, &len) < 0) { /* If BT_SNDMTU is not supported then MTU cannot be changed and * MTU is fixed to HCI_MAX_ACL_SIZE. */ mgmt->mtu = HCI_MAX_ACL_SIZE; return; } if (mgmt->mtu < UINT16_MAX) { uint16_t mtu = UINT16_MAX; /* Try increasing the MTU since some commands may go * over HCI_MAX_ACL_SIZE (1024) */ if (!setsockopt(mgmt->fd, SOL_BLUETOOTH, BT_SNDMTU, &mtu, sizeof(mtu))) mgmt->mtu = mtu; } } struct mgmt *mgmt_new(int fd) { struct mgmt *mgmt; if (fd < 0) return NULL; mgmt = new0(struct mgmt, 1); mgmt->fd = fd; mgmt->close_on_unref = false; mgmt->len = 512; mgmt->buf = malloc(mgmt->len); if (!mgmt->buf) { free(mgmt); return NULL; } mgmt->io = io_new(fd); if (!mgmt->io) { free(mgmt->buf); free(mgmt); return NULL; } mgmt->request_queue = queue_new(); mgmt->reply_queue = queue_new(); mgmt->pending_list = queue_new(); mgmt->notify_list = queue_new(); if (!io_set_read_handler(mgmt->io, can_read_data, mgmt, NULL)) { queue_destroy(mgmt->notify_list, NULL); queue_destroy(mgmt->pending_list, NULL); queue_destroy(mgmt->reply_queue, NULL); queue_destroy(mgmt->request_queue, NULL); io_destroy(mgmt->io); free(mgmt->buf); free(mgmt); return NULL; } mgmt->writer_active = false; mgmt_set_mtu(mgmt); return mgmt_ref(mgmt); } struct mgmt *mgmt_new_default(void) { struct mgmt *mgmt; union { struct sockaddr common; struct sockaddr_hci hci; } addr; int fd; fd = socket(PF_BLUETOOTH, SOCK_RAW | SOCK_CLOEXEC | SOCK_NONBLOCK, BTPROTO_HCI); if (fd < 0) return NULL; memset(&addr, 0, sizeof(addr)); addr.hci.hci_family = AF_BLUETOOTH; addr.hci.hci_dev = HCI_DEV_NONE; addr.hci.hci_channel = HCI_CHANNEL_CONTROL; if (bind(fd, &addr.common, sizeof(addr.hci)) < 0) { close(fd); return NULL; } mgmt = mgmt_new(fd); if (!mgmt) { close(fd); return NULL; } mgmt->close_on_unref = true; return mgmt; } struct mgmt *mgmt_ref(struct mgmt *mgmt) { if (!mgmt) return NULL; __sync_fetch_and_add(&mgmt->ref_count, 1); return mgmt; } void mgmt_unref(struct mgmt *mgmt) { if (!mgmt) return; if (__sync_sub_and_fetch(&mgmt->ref_count, 1)) return; mgmt_unregister_all(mgmt); mgmt_cancel_all(mgmt); queue_destroy(mgmt->reply_queue, NULL); queue_destroy(mgmt->request_queue, NULL); io_set_write_handler(mgmt->io, NULL, NULL, NULL); io_set_read_handler(mgmt->io, NULL, NULL, NULL); io_destroy(mgmt->io); mgmt->io = NULL; if (mgmt->close_on_unref) close(mgmt->fd); if (mgmt->debug_destroy) mgmt->debug_destroy(mgmt->debug_data); free(mgmt->buf); mgmt->buf = NULL; if (!mgmt->in_notify) { queue_destroy(mgmt->notify_list, NULL); queue_destroy(mgmt->pending_list, NULL); free(mgmt); return; } } bool mgmt_set_debug(struct mgmt *mgmt, mgmt_debug_func_t callback, void *user_data, mgmt_destroy_func_t destroy) { if (!mgmt) return false; if (mgmt->debug_destroy) mgmt->debug_destroy(mgmt->debug_data); mgmt->debug_callback = callback; mgmt->debug_destroy = destroy; mgmt->debug_data = user_data; return true; } bool mgmt_set_close_on_unref(struct mgmt *mgmt, bool do_close) { if (!mgmt) return false; mgmt->close_on_unref = do_close; return true; } static struct mgmt_request *create_request(struct mgmt *mgmt, uint16_t opcode, uint16_t index, uint16_t length, const void *param, mgmt_request_func_t callback, void *user_data, mgmt_destroy_func_t destroy, int timeout) { struct mgmt_request *request; struct mgmt_hdr *hdr; if (!opcode) return NULL; if (length > 0 && !param) return NULL; if (length > mgmt->mtu) { printf("length %u > %u mgmt->mtu", length, mgmt->mtu); return NULL; } request = new0(struct mgmt_request, 1); request->len = length + MGMT_HDR_SIZE; request->buf = malloc(request->len); if (!request->buf) { free(request); return NULL; } if (length > 0) memcpy(request->buf + MGMT_HDR_SIZE, param, length); hdr = request->buf; hdr->opcode = htobs(opcode); hdr->index = htobs(index); hdr->len = htobs(length); /* Use a weak reference so requests don't prevent mgmt_unref to * cancel requests and free in case of the last reference is dropped by * the user. */ request->mgmt = mgmt; request->opcode = opcode; request->index = index; request->callback = callback; request->destroy = destroy; request->user_data = user_data; request->timeout = timeout; return request; } struct mgmt_tlv_list *mgmt_tlv_list_new(void) { struct mgmt_tlv_list *tlv_list = new0(struct mgmt_tlv_list, 1); tlv_list->tlv_queue = queue_new(); tlv_list->size = 0; return tlv_list; } static struct mgmt_tlv *mgmt_tlv_new(uint16_t type, uint8_t length, void *value) { struct mgmt_tlv *entry = malloc(sizeof(*entry) + length); if (!entry) return NULL; entry->type = htobs(type); entry->length = length; memcpy(entry->value, value, length); return entry; } static void mgmt_tlv_free(struct mgmt_tlv *entry) { free(entry); } void mgmt_tlv_list_free(struct mgmt_tlv_list *tlv_list) { queue_destroy(tlv_list->tlv_queue, free); free(tlv_list); } bool mgmt_tlv_add(struct mgmt_tlv_list *tlv_list, uint16_t type, uint8_t length, void *value) { struct mgmt_tlv *entry = mgmt_tlv_new(type, length, value); if (!entry) return false; if (!queue_push_tail(tlv_list->tlv_queue, entry)) { mgmt_tlv_free(entry); return false; } tlv_list->size += sizeof(*entry) + entry->length; return true; } static void mgmt_tlv_to_buf(void *data, void *user_data) { struct mgmt_tlv *entry = data; uint8_t **buf_ptr = user_data; size_t entry_size = sizeof(*entry) + entry->length; memcpy(*buf_ptr, entry, entry_size); *buf_ptr += entry_size; } struct mgmt_tlv_list *mgmt_tlv_list_load_from_buf(const uint8_t *buf, uint16_t len) { struct mgmt_tlv_list *tlv_list; const uint8_t *cur = buf; if (!len || !buf) return NULL; tlv_list = mgmt_tlv_list_new(); while (cur < buf + len) { struct mgmt_tlv *entry = (struct mgmt_tlv *)cur; cur += sizeof(*entry) + entry->length; if (cur > buf + len) goto failed; if (!mgmt_tlv_add(tlv_list, entry->type, entry->length, entry->value)) { goto failed; } } return tlv_list; failed: mgmt_tlv_list_free(tlv_list); return NULL; } void mgmt_tlv_list_foreach(struct mgmt_tlv_list *tlv_list, mgmt_tlv_list_foreach_func_t callback, void *user_data) { queue_foreach(tlv_list->tlv_queue, callback, user_data); } unsigned int mgmt_send_tlv(struct mgmt *mgmt, uint16_t opcode, uint16_t index, struct mgmt_tlv_list *tlv_list, mgmt_request_func_t callback, void *user_data, mgmt_destroy_func_t destroy) { uint8_t *buf, *buf_ptr; unsigned int ret; if (!tlv_list) return 0; buf = malloc(tlv_list->size); if (!buf) return 0; buf_ptr = buf; queue_foreach(tlv_list->tlv_queue, mgmt_tlv_to_buf, &buf_ptr); ret = mgmt_send(mgmt, opcode, index, tlv_list->size, (void *)buf, callback, user_data, destroy); free(buf); return ret; } unsigned int mgmt_send_timeout(struct mgmt *mgmt, uint16_t opcode, uint16_t index, uint16_t length, const void *param, mgmt_request_func_t callback, void *user_data, mgmt_destroy_func_t destroy, int timeout) { struct mgmt_request *request; if (!mgmt) return 0; request = create_request(mgmt, opcode, index, length, param, callback, user_data, destroy, timeout); if (!request) return 0; if (mgmt->next_request_id < 1) mgmt->next_request_id = 1; request->id = mgmt->next_request_id++; if (!queue_push_tail(mgmt->request_queue, request)) { free(request->buf); free(request); return 0; } wakeup_writer(mgmt); return request->id; } unsigned int mgmt_send(struct mgmt *mgmt, uint16_t opcode, uint16_t index, uint16_t length, const void *param, mgmt_request_func_t callback, void *user_data, mgmt_destroy_func_t destroy) { return mgmt_send_timeout(mgmt, opcode, index, length, param, callback, user_data, destroy, 0); } unsigned int mgmt_send_nowait(struct mgmt *mgmt, uint16_t opcode, uint16_t index, uint16_t length, const void *param, mgmt_request_func_t callback, void *user_data, mgmt_destroy_func_t destroy) { struct mgmt_request *request; if (!mgmt) return 0; request = create_request(mgmt, opcode, index, length, param, callback, user_data, destroy, 0); if (!request) return 0; if (mgmt->next_request_id < 1) mgmt->next_request_id = 1; request->id = mgmt->next_request_id++; if (!send_request(mgmt, request)) return 0; return request->id; } unsigned int mgmt_reply_timeout(struct mgmt *mgmt, uint16_t opcode, uint16_t index, uint16_t length, const void *param, mgmt_request_func_t callback, void *user_data, mgmt_destroy_func_t destroy, int timeout) { struct mgmt_request *request; if (!mgmt) return 0; request = create_request(mgmt, opcode, index, length, param, callback, user_data, destroy, timeout); if (!request) return 0; if (mgmt->next_request_id < 1) mgmt->next_request_id = 1; request->id = mgmt->next_request_id++; if (!queue_push_tail(mgmt->reply_queue, request)) { free(request->buf); free(request); return 0; } wakeup_writer(mgmt); return request->id; } unsigned int mgmt_reply(struct mgmt *mgmt, uint16_t opcode, uint16_t index, uint16_t length, const void *param, mgmt_request_func_t callback, void *user_data, mgmt_destroy_func_t destroy) { return mgmt_reply_timeout(mgmt, opcode, index, length, param, callback, user_data, destroy, 0); } bool mgmt_cancel(struct mgmt *mgmt, unsigned int id) { struct mgmt_request *request; if (!mgmt || !id) return false; request = queue_remove_if(mgmt->request_queue, match_request_id, UINT_TO_PTR(id)); if (request) goto done; request = queue_remove_if(mgmt->reply_queue, match_request_id, UINT_TO_PTR(id)); if (request) goto done; request = queue_remove_if(mgmt->pending_list, match_request_id, UINT_TO_PTR(id)); if (!request) return false; done: destroy_request(request); wakeup_writer(mgmt); return true; } bool mgmt_cancel_index(struct mgmt *mgmt, uint16_t index) { if (!mgmt) return false; queue_remove_all(mgmt->request_queue, match_request_index, UINT_TO_PTR(index), destroy_request); queue_remove_all(mgmt->reply_queue, match_request_index, UINT_TO_PTR(index), destroy_request); queue_remove_all(mgmt->pending_list, match_request_index, UINT_TO_PTR(index), destroy_request); return true; } bool mgmt_cancel_all(struct mgmt *mgmt) { if (!mgmt) return false; queue_remove_all(mgmt->pending_list, NULL, NULL, destroy_request); queue_remove_all(mgmt->reply_queue, NULL, NULL, destroy_request); queue_remove_all(mgmt->request_queue, NULL, NULL, destroy_request); return true; } unsigned int mgmt_register(struct mgmt *mgmt, uint16_t event, uint16_t index, mgmt_notify_func_t callback, void *user_data, mgmt_destroy_func_t destroy) { struct mgmt_notify *notify; if (!mgmt || !event) return 0; notify = new0(struct mgmt_notify, 1); notify->event = event; notify->index = index; notify->callback = callback; notify->destroy = destroy; notify->user_data = user_data; if (mgmt->next_notify_id < 1) mgmt->next_notify_id = 1; notify->id = mgmt->next_notify_id++; if (!queue_push_tail(mgmt->notify_list, notify)) { free(notify); return 0; } return notify->id; } bool mgmt_unregister(struct mgmt *mgmt, unsigned int id) { struct mgmt_notify *notify; if (!mgmt || !id) return false; notify = queue_remove_if(mgmt->notify_list, match_notify_id, UINT_TO_PTR(id)); if (!notify) return false; if (!mgmt->in_notify) { destroy_notify(notify); return true; } notify->removed = true; mgmt->need_notify_cleanup = true; return true; } bool mgmt_unregister_index(struct mgmt *mgmt, uint16_t index) { if (!mgmt) return false; if (mgmt->in_notify) { queue_foreach(mgmt->notify_list, mark_notify_removed, UINT_TO_PTR(index)); mgmt->need_notify_cleanup = true; } else queue_remove_all(mgmt->notify_list, match_notify_index, UINT_TO_PTR(index), destroy_notify); return true; } bool mgmt_unregister_all(struct mgmt *mgmt) { if (!mgmt) return false; if (mgmt->in_notify) { queue_foreach(mgmt->notify_list, mark_notify_removed, UINT_TO_PTR(MGMT_INDEX_NONE)); mgmt->need_notify_cleanup = true; } else queue_remove_all(mgmt->notify_list, NULL, NULL, destroy_notify); return true; } uint16_t mgmt_get_mtu(struct mgmt *mgmt) { if (!mgmt) return 0; return mgmt->mtu; } bluez-5.82/src/shared/PaxHeaders/mcs.h0000644000000000000000000000005014333256743014650 xustar0020 atime=1743515918 20 ctime=1743591277 bluez-5.82/src/shared/mcs.h0000644000000000000000000000445214333256743014336 0ustar00rootroot/* SPDX-License-Identifier: LGPL-2.1-or-later */ /* * * BlueZ - Bluetooth protocol stack for Linux * * Copyright (C) 2020 Intel Corporation. All rights reserved. * */ /* MCP Media State */ #define BT_MCS_STATUS_INACTIVE 0x00 #define BT_MCS_STATUS_PLAYING 0x01 #define BT_MCS_STATUS_PAUSED 0x02 #define BT_MCS_STATUS_SEEKING 0x03 /* MCP Control Point Opcodes */ #define BT_MCS_CMD_PLAY 0x01 #define BT_MCS_CMD_PAUSE 0x02 #define BT_MCS_CMD_FAST_REWIND 0x03 #define BT_MCS_CMD_FAST_FORWARD 0x04 #define BT_MCS_CMD_STOP 0x05 #define BT_MCS_CMD_MOVE_RELATIVE 0x10 #define BT_MCS_CMD_PREV_SEGMENT 0x20 #define BT_MCS_CMD_NEXT_SEGMENT 0x21 #define BT_MCS_CMD_FIRST_SEGMENT 0x22 #define BT_MCS_CMD_LAST_SEGMENT 0x23 #define BT_MCS_CMD_GOTO_SEGMENT 0x24 #define BT_MCS_CMD_PREV_TRACK 0x30 #define BT_MCS_CMD_NEXT_TRACK 0x31 #define BT_MCS_CMD_FIRST_TRACK 0x32 #define BT_MCS_CMD_LAST_TRACK 0x33 #define BT_MCS_CMD_GOTO_TRACK 0x34 #define BT_MCS_CMD_PREV_GROUP 0x40 #define BT_MCS_CMD_NEXT_GROUP 0x41 #define BT_MCS_CMD_FIRST_GROUP 0x42 #define BT_MCS_CMD_LAST_GROUP 0x43 #define BT_MCS_CMD_GOTO_GROUP 0x44 /* MCP Control Point Opcodes Supported */ #define BT_MCS_CMD_PLAY_SUPPORTED 0x00000001 #define BT_MCS_CMD_PAUSE_SUPPORTED 0x00000002 #define BT_MCS_CMD_FAST_REWIND_SUPPORTED 0x00000004 #define BT_MCS_CMD_FAST_FORWARD_SUPPORTED 0x00000008 #define BT_MCS_CMD_STOP_SUPPORTED 0x00000010 #define BT_MCS_CMD_MOVE_RELATIVE_SUPPORTED 0x00000020 #define BT_MCS_CMD_PREV_SEGMENT_SUPPORTED 0x00000040 #define BT_MCS_CMD_NEXT_SEGMENT_SUPPORTED 0x00000080 #define BT_MCS_CMD_FIRST_SEGMENT_SUPPORTED 0x00000100 #define BT_MCS_CMD_LAST_SEGMENT_SUPPORTED 0x00000200 #define BT_MCS_CMD_GOTO_SEGMENT_SUPPORTED 0x00000400 #define BT_MCS_CMD_PREV_TRACK_SUPPORTED 0x00000800 #define BT_MCS_CMD_NEXT_TRACK_SUPPORTED 0x00001000 #define BT_MCS_CMD_FIRST_TRACK_SUPPORTED 0x00002000 #define BT_MCS_CMD_LAST_TRACK_SUPPORTED 0x00004000 #define BT_MCS_CMD_GOTO_TRACK_SUPPORTED 0x00008000 #define BT_MCS_CMD_PREV_GROUP_SUPPORTED 0x00010000 #define BT_MCS_CMD_NEXT_GROUP_SUPPORTED 0x00020000 #define BT_MCS_CMD_FIRST_GROUP_SUPPORTED 0x00040000 #define BT_MCS_CMD_LAST_GROUP_SUPPORTED 0x00080000 #define BT_MCS_CMD_GOTO_GROUP_SUPPORTED 0x00100000 bluez-5.82/src/shared/PaxHeaders/mainloop.h0000644000000000000000000000005014015011623015663 xustar0020 atime=1743515768 20 ctime=1743591277 bluez-5.82/src/shared/mainloop.h0000644000000000000000000000252514015011623015350 0ustar00rootroot/* SPDX-License-Identifier: LGPL-2.1-or-later */ /* * * BlueZ - Bluetooth protocol stack for Linux * * Copyright (C) 2011-2014 Intel Corporation * Copyright (C) 2002-2010 Marcel Holtmann * * */ #include #include typedef void (*mainloop_destroy_func) (void *user_data); typedef void (*mainloop_event_func) (int fd, uint32_t events, void *user_data); typedef void (*mainloop_timeout_func) (int id, void *user_data); typedef void (*mainloop_signal_func) (int signum, void *user_data); void mainloop_init(void); void mainloop_quit(void); void mainloop_exit_success(void); void mainloop_exit_failure(void); int mainloop_run(void); int mainloop_run_with_signal(mainloop_signal_func func, void *user_data); int mainloop_add_fd(int fd, uint32_t events, mainloop_event_func callback, void *user_data, mainloop_destroy_func destroy); int mainloop_modify_fd(int fd, uint32_t events); int mainloop_remove_fd(int fd); int mainloop_add_timeout(unsigned int msec, mainloop_timeout_func callback, void *user_data, mainloop_destroy_func destroy); int mainloop_modify_timeout(int fd, unsigned int msec); int mainloop_remove_timeout(int id); int mainloop_set_signal(sigset_t *mask, mainloop_signal_func callback, void *user_data, mainloop_destroy_func destroy); int mainloop_sd_notify(const char *state); bluez-5.82/src/shared/PaxHeaders/hci.h0000644000000000000000000000005014766002272014625 xustar0020 atime=1743515578 20 ctime=1743591277 bluez-5.82/src/shared/hci.h0000644000000000000000000000243514766002272014312 0ustar00rootroot/* SPDX-License-Identifier: LGPL-2.1-or-later */ /* * * BlueZ - Bluetooth protocol stack for Linux * * Copyright (C) 2012-2014 Intel Corporation. All rights reserved. * * */ #include #include typedef void (*bt_hci_destroy_func_t)(void *user_data); struct bt_hci; struct bt_hci *bt_hci_new(int fd); struct bt_hci *bt_hci_new_user_channel(uint16_t index); struct bt_hci *bt_hci_new_raw_device(uint16_t index); struct bt_hci *bt_hci_ref(struct bt_hci *hci); void bt_hci_unref(struct bt_hci *hci); bool bt_hci_set_close_on_unref(struct bt_hci *hci, bool do_close); typedef void (*bt_hci_callback_func_t)(const void *data, uint8_t size, void *user_data); unsigned int bt_hci_send(struct bt_hci *hci, uint16_t opcode, const void *data, uint8_t size, bt_hci_callback_func_t callback, void *user_data, bt_hci_destroy_func_t destroy); bool bt_hci_cancel(struct bt_hci *hci, unsigned int id); bool bt_hci_flush(struct bt_hci *hci); bool bt_hci_send_data(struct bt_hci *hci, uint8_t type, uint16_t handle, const void *data, uint8_t size); unsigned int bt_hci_register(struct bt_hci *hci, uint8_t event, bt_hci_callback_func_t callback, void *user_data, bt_hci_destroy_func_t destroy); bool bt_hci_unregister(struct bt_hci *hci, unsigned int id); bluez-5.82/src/shared/PaxHeaders/mcp.c0000644000000000000000000000005014643061455014636 xustar0020 atime=1743515917 20 ctime=1743591277 bluez-5.82/src/shared/mcp.c0000644000000000000000000010431414643061455014322 0ustar00rootroot// SPDX-License-Identifier: LGPL-2.1-or-later /* * * BlueZ - Bluetooth protocol stack for Linux * * Copyright (C) 2022 Intel Corporation. All rights reserved. * */ #define _GNU_SOURCE #include #include #include #include #include #include #include "lib/bluetooth.h" #include "lib/uuid.h" #include "lib/hci.h" #include "src/shared/queue.h" #include "src/shared/util.h" #include "src/shared/timeout.h" #include "src/shared/att.h" #include "src/shared/gatt-db.h" #include "src/shared/gatt-server.h" #include "src/shared/gatt-client.h" #include "src/shared/mcp.h" #include "src/shared/mcs.h" #define DBG(_mcp, fmt, arg...) \ mcp_debug(_mcp, "%s:%s() " fmt, __FILE__, __func__, ## arg) struct bt_mcp_db { struct gatt_db *db; struct bt_mcs *mcs; }; struct bt_mcp_pending { unsigned int id; struct bt_mcp *mcp; bt_gatt_client_read_callback_t func; void *user_data; }; struct event_callback { const struct bt_mcp_event_callback *cbs; void *user_data; }; struct bt_mcp_session_info { uint8_t content_control_id; uint32_t cp_op_supported; }; struct bt_mcp { int ref_count; struct bt_gatt_client *client; struct bt_mcp_db *ldb; struct bt_mcp_db *rdb; unsigned int mp_name_id; unsigned int track_changed_id; unsigned int track_title_id; unsigned int track_duration_id; unsigned int track_position_id; unsigned int media_state_id; unsigned int media_cp_id; unsigned int media_cp_op_supported_id; struct bt_mcp_session_info session; struct event_callback *cb; struct queue *pending; bt_mcp_debug_func_t debug_func; bt_mcp_destroy_func_t debug_destroy; void *debug_data; void *user_data; }; struct bt_mcs { struct bt_mcp_db *mdb; struct gatt_db_attribute *service; struct gatt_db_attribute *mp_name; struct gatt_db_attribute *track_changed; struct gatt_db_attribute *track_changed_ccc; struct gatt_db_attribute *track_title; struct gatt_db_attribute *track_duration; struct gatt_db_attribute *track_position; struct gatt_db_attribute *playback_speed; struct gatt_db_attribute *seeking_speed; struct gatt_db_attribute *play_order; struct gatt_db_attribute *play_order_supported; struct gatt_db_attribute *media_state; struct gatt_db_attribute *media_state_ccc; struct gatt_db_attribute *media_cp; struct gatt_db_attribute *media_cp_ccc; struct gatt_db_attribute *media_cp_op_supportd; struct gatt_db_attribute *content_control_id; struct gatt_db_attribute *content_control_id_ccc; }; static struct queue *mcp_db; static void mcp_debug(struct bt_mcp *mcp, const char *format, ...) { va_list ap; if (!mcp || !format || !mcp->debug_func) return; va_start(ap, format); util_debug_va(mcp->debug_func, mcp->debug_data, format, ap); va_end(ap); } static bool mcp_db_match(const void *data, const void *match_data) { const struct bt_mcp_db *mdb = data; const struct gatt_db *db = match_data; return (mdb->db == db); } static void mcp_db_free(void *data) { struct bt_mcp_db *bdb = data; if (!bdb) return; gatt_db_unref(bdb->db); free(bdb->mcs); free(bdb); } static void mcp_free(void *data) { struct bt_mcp *mcp = data; DBG(mcp, ""); bt_mcp_detach(mcp); mcp_db_free(mcp->rdb); queue_destroy(mcp->pending, NULL); free(mcp); } struct bt_mcp *bt_mcp_ref(struct bt_mcp *mcp) { if (!mcp) return NULL; __sync_fetch_and_add(&mcp->ref_count, 1); return mcp; } void bt_mcp_unref(struct bt_mcp *mcp) { if (!mcp) return; if (__sync_sub_and_fetch(&mcp->ref_count, 1)) return; mcp_free(mcp); } bool bt_mcp_set_user_data(struct bt_mcp *mcp, void *user_data) { if (!mcp) return false; mcp->user_data = user_data; return true; } void *bt_mcp_get_user_data(struct bt_mcp *mcp) { if (!mcp) return NULL; return mcp->user_data; } bool bt_mcp_set_debug(struct bt_mcp *mcp, bt_mcp_debug_func_t func, void *user_data, bt_mcp_destroy_func_t destroy) { if (!mcp) return false; if (mcp->debug_destroy) mcp->debug_destroy(mcp->debug_data); mcp->debug_func = func; mcp->debug_destroy = destroy; mcp->debug_data = user_data; return true; } static void mcs_mp_name_read(struct gatt_db_attribute *attrib, unsigned int id, uint16_t offset, uint8_t opcode, struct bt_att *att, void *user_data) { char mp_name[] = ""; struct iovec iov; iov.iov_base = mp_name; iov.iov_len = sizeof(mp_name); gatt_db_attribute_read_result(attrib, id, 0, iov.iov_base, iov.iov_len); } static void mcs_track_title_read(struct gatt_db_attribute *attrib, unsigned int id, uint16_t offset, uint8_t opcode, struct bt_att *att, void *user_data) { char track_title[] = ""; struct iovec iov; iov.iov_base = track_title; iov.iov_len = 0; gatt_db_attribute_read_result(attrib, id, 0, iov.iov_base, iov.iov_len); } static void mcs_track_duration_read(struct gatt_db_attribute *attrib, unsigned int id, uint16_t offset, uint8_t opcode, struct bt_att *att, void *user_data) { int32_t track_duration = 0xFFFFFFFF; struct iovec iov; iov.iov_base = &track_duration; iov.iov_len = sizeof(track_duration); gatt_db_attribute_read_result(attrib, id, 0, iov.iov_base, iov.iov_len); } static void mcs_track_position_read(struct gatt_db_attribute *attrib, unsigned int id, uint16_t offset, uint8_t opcode, struct bt_att *att, void *user_data) { int32_t track_position = 0xFFFFFFFF; struct iovec iov; iov.iov_base = &track_position; iov.iov_len = sizeof(track_position); gatt_db_attribute_read_result(attrib, id, 0, iov.iov_base, iov.iov_len); } static void mcs_track_position_write(struct gatt_db_attribute *attrib, unsigned int id, uint16_t offset, const uint8_t *value, size_t len, uint8_t opcode, struct bt_att *att, void *user_data) { gatt_db_attribute_write_result(attrib, id, BT_ATT_ERROR_INSUFFICIENT_RESOURCES); } static void mcs_playback_speed_read(struct gatt_db_attribute *attrib, unsigned int id, uint16_t offset, uint8_t opcode, struct bt_att *att, void *user_data) { int8_t playback_speed = 0x00; struct iovec iov; iov.iov_base = &playback_speed; iov.iov_len = sizeof(playback_speed); gatt_db_attribute_read_result(attrib, id, 0, iov.iov_base, iov.iov_len); } static void mcs_playback_speed_write(struct gatt_db_attribute *attrib, unsigned int id, uint16_t offset, const uint8_t *value, size_t len, uint8_t opcode, struct bt_att *att, void *user_data) { gatt_db_attribute_write_result(attrib, id, BT_ATT_ERROR_INSUFFICIENT_RESOURCES); } static void mcs_seeking_speed_read(struct gatt_db_attribute *attrib, unsigned int id, uint16_t offset, uint8_t opcode, struct bt_att *att, void *user_data) { int8_t seeking_speed = 0x00; struct iovec iov; iov.iov_base = &seeking_speed; iov.iov_len = sizeof(seeking_speed); gatt_db_attribute_read_result(attrib, id, 0, iov.iov_base, iov.iov_len); } static void mcs_playing_order_read(struct gatt_db_attribute *attrib, unsigned int id, uint16_t offset, uint8_t opcode, struct bt_att *att, void *user_data) { uint8_t playing_order = 0x01; struct iovec iov; iov.iov_base = &playing_order; iov.iov_len = sizeof(playing_order); gatt_db_attribute_read_result(attrib, id, 0, iov.iov_base, iov.iov_len); } static void mcs_playing_order_write(struct gatt_db_attribute *attrib, unsigned int id, uint16_t offset, const uint8_t *value, size_t len, uint8_t opcode, struct bt_att *att, void *user_data) { gatt_db_attribute_write_result(attrib, id, BT_ATT_ERROR_INSUFFICIENT_RESOURCES); } static void mcs_playing_order_supported_read(struct gatt_db_attribute *attrib, unsigned int id, uint16_t offset, uint8_t opcode, struct bt_att *att, void *user_data) { uint16_t playing_order_supported = 0x01; struct iovec iov; iov.iov_base = &playing_order_supported; iov.iov_len = sizeof(playing_order_supported); gatt_db_attribute_read_result(attrib, id, 0, iov.iov_base, iov.iov_len); } static void mcs_media_state_read(struct gatt_db_attribute *attrib, unsigned int id, uint16_t offset, uint8_t opcode, struct bt_att *att, void *user_data) { uint8_t media_state = 0x00; struct iovec iov; iov.iov_base = &media_state; iov.iov_len = sizeof(media_state); gatt_db_attribute_read_result(attrib, id, 0, iov.iov_base, iov.iov_len); } static void mcs_media_cp_write(struct gatt_db_attribute *attrib, unsigned int id, uint16_t offset, const uint8_t *value, size_t len, uint8_t opcode, struct bt_att *att, void *user_data) { gatt_db_attribute_write_result(attrib, id, BT_ATT_ERROR_INSUFFICIENT_RESOURCES); } static void mcs_media_cp_op_supported_read(struct gatt_db_attribute *attrib, unsigned int id, uint16_t offset, uint8_t opcode, struct bt_att *att, void *user_data) { uint32_t cp_op_supported = 0x00000000; struct iovec iov; iov.iov_base = &cp_op_supported; iov.iov_len = sizeof(cp_op_supported); gatt_db_attribute_read_result(attrib, id, 0, iov.iov_base, iov.iov_len); } static void mcs_media_content_control_id_read(struct gatt_db_attribute *attrib, unsigned int id, uint16_t offset, uint8_t opcode, struct bt_att *att, void *user_data) { uint8_t content_control_id = 0x00; struct iovec iov; iov.iov_base = &content_control_id; iov.iov_len = sizeof(content_control_id); gatt_db_attribute_read_result(attrib, id, 0, iov.iov_base, iov.iov_len); } static struct bt_mcs *mcs_new(struct gatt_db *db) { struct bt_mcs *mcs; bt_uuid_t uuid; if (!db) return NULL; mcs = new0(struct bt_mcs, 1); /* Populate DB with MCS attributes */ bt_uuid16_create(&uuid, GMCS_UUID); mcs->service = gatt_db_add_service(db, &uuid, true, 31); bt_uuid16_create(&uuid, MEDIA_PLAYER_NAME_CHRC_UUID); mcs->mp_name = gatt_db_service_add_characteristic(mcs->service, &uuid, BT_ATT_PERM_READ, BT_GATT_CHRC_PROP_READ, mcs_mp_name_read, NULL, mcs); bt_uuid16_create(&uuid, MEDIA_TRACK_CHNGD_CHRC_UUID); mcs->track_changed = gatt_db_service_add_characteristic(mcs->service, &uuid, BT_ATT_PERM_NONE, BT_GATT_CHRC_PROP_NOTIFY, NULL, NULL, mcs); mcs->track_changed_ccc = gatt_db_service_add_ccc(mcs->service, BT_ATT_PERM_READ | BT_ATT_PERM_WRITE); bt_uuid16_create(&uuid, MEDIA_TRACK_TITLE_CHRC_UUID); mcs->track_title = gatt_db_service_add_characteristic(mcs->service, &uuid, BT_ATT_PERM_READ, BT_GATT_CHRC_PROP_READ, mcs_track_title_read, NULL, mcs); bt_uuid16_create(&uuid, MEDIA_TRACK_DURATION_CHRC_UUID); mcs->track_duration = gatt_db_service_add_characteristic(mcs->service, &uuid, BT_ATT_PERM_READ, BT_GATT_CHRC_PROP_READ, mcs_track_duration_read, NULL, mcs); bt_uuid16_create(&uuid, MEDIA_TRACK_POSTION_CHRC_UUID); mcs->track_position = gatt_db_service_add_characteristic(mcs->service, &uuid, BT_ATT_PERM_READ | BT_ATT_PERM_WRITE, BT_GATT_CHRC_PROP_READ | BT_GATT_CHRC_PROP_WRITE | BT_GATT_CHRC_PROP_WRITE_WITHOUT_RESP, mcs_track_position_read, mcs_track_position_write, mcs); bt_uuid16_create(&uuid, MEDIA_PLAYBACK_SPEED_CHRC_UUID); mcs->playback_speed = gatt_db_service_add_characteristic(mcs->service, &uuid, BT_ATT_PERM_READ | BT_ATT_PERM_WRITE, BT_GATT_CHRC_PROP_READ | BT_GATT_CHRC_PROP_WRITE | BT_GATT_CHRC_PROP_WRITE_WITHOUT_RESP, mcs_playback_speed_read, mcs_playback_speed_write, mcs); bt_uuid16_create(&uuid, MEDIA_SEEKING_SPEED_CHRC_UUID); mcs->seeking_speed = gatt_db_service_add_characteristic(mcs->service, &uuid, BT_ATT_PERM_READ, BT_GATT_CHRC_PROP_READ, mcs_seeking_speed_read, NULL, mcs); bt_uuid16_create(&uuid, MEDIA_PLAYING_ORDER_CHRC_UUID); mcs->play_order = gatt_db_service_add_characteristic(mcs->service, &uuid, BT_ATT_PERM_READ | BT_ATT_PERM_WRITE, BT_GATT_CHRC_PROP_READ | BT_GATT_CHRC_PROP_WRITE | BT_GATT_CHRC_PROP_WRITE_WITHOUT_RESP, mcs_playing_order_read, mcs_playing_order_write, mcs); bt_uuid16_create(&uuid, MEDIA_PLAY_ORDER_SUPPRTD_CHRC_UUID); mcs->play_order_supported = gatt_db_service_add_characteristic( mcs->service, &uuid, BT_ATT_PERM_READ, BT_GATT_CHRC_PROP_READ, mcs_playing_order_supported_read, NULL, mcs); bt_uuid16_create(&uuid, MEDIA_STATE_CHRC_UUID); mcs->media_state = gatt_db_service_add_characteristic(mcs->service, &uuid, BT_ATT_PERM_READ, BT_GATT_CHRC_PROP_READ | BT_GATT_CHRC_PROP_NOTIFY, mcs_media_state_read, NULL, mcs); mcs->media_state_ccc = gatt_db_service_add_ccc(mcs->service, BT_ATT_PERM_READ | BT_ATT_PERM_WRITE); bt_uuid16_create(&uuid, MEDIA_CP_CHRC_UUID); mcs->media_cp = gatt_db_service_add_characteristic(mcs->service, &uuid, BT_ATT_PERM_WRITE, BT_GATT_CHRC_PROP_WRITE | BT_GATT_CHRC_PROP_NOTIFY | BT_GATT_CHRC_PROP_WRITE_WITHOUT_RESP, NULL, mcs_media_cp_write, mcs); mcs->media_cp_ccc = gatt_db_service_add_ccc(mcs->service, BT_ATT_PERM_READ | BT_ATT_PERM_WRITE); bt_uuid16_create(&uuid, MEDIA_CP_OP_SUPPORTED_CHRC_UUID); mcs->media_cp_op_supportd = gatt_db_service_add_characteristic( mcs->service, &uuid, BT_ATT_PERM_READ, BT_GATT_CHRC_PROP_READ, mcs_media_cp_op_supported_read, NULL, mcs); bt_uuid16_create(&uuid, MEDIA_CONTENT_CONTROL_ID_CHRC_UUID); mcs->content_control_id = gatt_db_service_add_characteristic( mcs->service, &uuid, BT_ATT_PERM_READ, BT_GATT_CHRC_PROP_READ | BT_GATT_CHRC_PROP_NOTIFY, mcs_media_content_control_id_read, NULL, mcs); mcs->content_control_id_ccc = gatt_db_service_add_ccc(mcs->service, BT_ATT_PERM_READ | BT_ATT_PERM_WRITE); gatt_db_service_set_active(mcs->service, false); return mcs; } static struct bt_mcs *mcp_get_mcs(struct bt_mcp *mcp) { if (!mcp) return NULL; if (mcp->rdb->mcs) return mcp->rdb->mcs; mcp->rdb->mcs = new0(struct bt_mcs, 1); mcp->rdb->mcs->mdb = mcp->rdb; return mcp->rdb->mcs; } static unsigned int mcp_send(struct bt_mcp *mcp, uint8_t operation) { struct bt_mcs *mcs = mcp_get_mcs(mcp); int ret; uint16_t handle; DBG(mcp, "mcs %p", mcs); if (!mcp->client) return -1; if (!gatt_db_attribute_get_char_data(mcs->media_cp, NULL, &handle, NULL, NULL, NULL)) return -1; ret = bt_gatt_client_write_without_response(mcp->client, handle, false, &operation, sizeof(uint8_t)); if (!ret) return -1; return 0; } unsigned int bt_mcp_play(struct bt_mcp *mcp) { if (!mcp) return 0; if (!(mcp->session.cp_op_supported & BT_MCS_CMD_PLAY_SUPPORTED)) return -ENOTSUP; DBG(mcp, "mcp %p", mcp); return mcp_send(mcp, BT_MCS_CMD_PLAY); } unsigned int bt_mcp_pause(struct bt_mcp *mcp) { if (!mcp) return 0; if (!(mcp->session.cp_op_supported & BT_MCS_CMD_PAUSE_SUPPORTED)) return -ENOTSUP; DBG(mcp, "mcp %p", mcp); return mcp_send(mcp, BT_MCS_CMD_PAUSE); } unsigned int bt_mcp_stop(struct bt_mcp *mcp) { if (!mcp) return 0; if (!(mcp->session.cp_op_supported & BT_MCS_CMD_STOP_SUPPORTED)) return -ENOTSUP; DBG(mcp, "mcp %p", mcp); return mcp_send(mcp, BT_MCS_CMD_STOP); } unsigned int bt_mcp_next_track(struct bt_mcp *mcp) { if (!mcp) return 0; if (!(mcp->session.cp_op_supported & BT_MCS_CMD_NEXT_TRACK_SUPPORTED)) return -ENOTSUP; DBG(mcp, "mcp %p", mcp); return mcp_send(mcp, BT_MCS_CMD_NEXT_TRACK); } unsigned int bt_mcp_previous_track(struct bt_mcp *mcp) { if (!mcp) return 0; if (!(mcp->session.cp_op_supported & BT_MCS_CMD_PREV_TRACK_SUPPORTED)) return -ENOTSUP; DBG(mcp, "mcp %p", mcp); return mcp_send(mcp, BT_MCS_CMD_PREV_TRACK); } static void mcp_mp_set_player_name(struct bt_mcp *mcp, const uint8_t *value, uint16_t length) { struct event_callback *cb; if (!mcp) return; cb = mcp->cb; if (cb && cb->cbs && cb->cbs->player_name) cb->cbs->player_name(mcp, value, length); } static void mcp_mp_set_track_title(struct bt_mcp *mcp, const uint8_t *value, uint16_t length) { struct event_callback *cb; if (!mcp) return; cb = mcp->cb; if (cb && cb->cbs && cb->cbs->track_title) cb->cbs->track_title(mcp, value, length); } static void mcp_mp_set_title_duration(struct bt_mcp *mcp, int32_t duration) { struct event_callback *cb; if (!mcp) return; cb = mcp->cb; DBG(mcp, "Track Duration 0x%08x", duration); if (cb && cb->cbs && cb->cbs->track_duration) cb->cbs->track_duration(mcp, duration); } static void mcp_mp_set_title_position(struct bt_mcp *mcp, int32_t position) { struct event_callback *cb; if (!mcp) return; cb = mcp->cb; DBG(mcp, "Track Position 0x%08x", position); if (cb && cb->cbs && cb->cbs->track_position) cb->cbs->track_position(mcp, position); } static void mcp_mp_set_media_state(struct bt_mcp *mcp, uint8_t state) { struct event_callback *cb; if (!mcp) return; cb = mcp->cb; DBG(mcp, "Media State 0x%02x", state); if (cb && cb->cbs && cb->cbs->media_state) cb->cbs->media_state(mcp, state); } static void read_media_player_name(bool success, uint8_t att_ecode, const uint8_t *value, uint16_t length, void *user_data) { struct bt_mcp *mcp = user_data; if (!success) { DBG(mcp, "Unable to read media player name: error 0x%02x", att_ecode); return; } if (!length) return; mcp_mp_set_player_name(mcp, value, length); } static void read_track_title(bool success, uint8_t att_ecode, const uint8_t *value, uint16_t length, void *user_data) { struct bt_mcp *mcp = user_data; if (!success) { DBG(mcp, "Unable to read track title: error 0x%02x", att_ecode); return; } if (!length) return; mcp_mp_set_track_title(mcp, value, length); } static void read_track_duration(bool success, uint8_t att_ecode, const uint8_t *value, uint16_t length, void *user_data) { struct bt_mcp *mcp = user_data; int32_t duration; if (!success) { DBG(mcp, "Unable to read track duration: error 0x%02x", att_ecode); return; } if (length != sizeof(duration)) DBG(mcp, "Wrong length received Length : %u", length); memcpy(&duration, value, length); mcp_mp_set_title_duration(mcp, duration); } static void read_track_position(bool success, uint8_t att_ecode, const uint8_t *value, uint16_t length, void *user_data) { struct bt_mcp *mcp = user_data; int32_t position; if (!success) { DBG(mcp, "Unable to read track position: error 0x%02x", att_ecode); return; } if (length != sizeof(position)) DBG(mcp, "Wrong length received Length : %u", length); memcpy(&position, value, length); mcp_mp_set_title_position(mcp, position); } static void read_media_state(bool success, uint8_t att_ecode, const uint8_t *value, uint16_t length, void *user_data) { struct bt_mcp *mcp = user_data; if (!success) { DBG(mcp, "Unable to read media state: error 0x%02x", att_ecode); return; } if (length != sizeof(uint8_t)) DBG(mcp, "Wrong length received Length : %u", length); mcp_mp_set_media_state(mcp, *value); } static void read_media_cp_op_supported(bool success, uint8_t att_ecode, const uint8_t *value, uint16_t length, void *user_data) { struct bt_mcp *mcp = user_data; if (!success) { DBG(mcp, "Unable to read media CP OP supported: error 0x%02x", att_ecode); return; } if (length != sizeof(uint32_t)) DBG(mcp, "Wrong length received Length : %u", length); memcpy(&mcp->session.cp_op_supported, value, sizeof(uint32_t)); DBG(mcp, "Media Control Point Opcodes Supported 0x%08x", mcp->session.cp_op_supported); } static void read_content_control_id(bool success, uint8_t att_ecode, const uint8_t *value, uint16_t length, void *user_data) { struct bt_mcp *mcp = user_data; if (!success) { DBG(mcp, "Unable to read content control id: error 0x%02x", att_ecode); return; } if (length != sizeof(uint8_t)) DBG(mcp, "Wrong length received Length : %u", length); DBG(mcp, "Content Control ID 0x%02x", *value); } static void mcp_pending_destroy(void *data) { struct bt_mcp_pending *pending = data; struct bt_mcp *mcp = pending->mcp; queue_remove_if(mcp->pending, NULL, pending); } static void mcp_pending_complete(bool success, uint8_t att_ecode, const uint8_t *value, uint16_t length, void *user_data) { struct bt_mcp_pending *pending = user_data; if (pending->func) pending->func(success, att_ecode, value, length, pending->user_data); } static void mcp_read_value(struct bt_mcp *mcp, uint16_t value_handle, bt_gatt_client_read_callback_t func, void *user_data) { struct bt_mcp_pending *pending; pending = new0(struct bt_mcp_pending, 1); pending->mcp = mcp; pending->func = func; pending->user_data = user_data; pending->id = bt_gatt_client_read_value(mcp->client, value_handle, mcp_pending_complete, pending, mcp_pending_destroy); if (!pending->id) { DBG(mcp, "Unable to send Read request"); free(pending); return; } queue_push_tail(mcp->pending, pending); } static void mcp_mp_name_register(uint16_t att_ecode, void *user_data) { struct bt_mcp *mcp = user_data; if (att_ecode) DBG(mcp, "Media Player Name notification failed: 0x%04x", att_ecode); } static void mcp_mp_name_notify(uint16_t value_handle, const uint8_t *value, uint16_t length, void *user_data) { struct bt_mcp *mcp = user_data; if (!length) return; mcp_mp_set_player_name(mcp, value, length); } static void mcp_track_changed_register(uint16_t att_ecode, void *user_data) { struct bt_mcp *mcp = user_data; if (att_ecode) DBG(mcp, "Media Track Changed notification failed: 0x%04x", att_ecode); } static void mcp_track_changed_notify(uint16_t value_handle, const uint8_t *value, uint16_t length, void *user_data) { struct bt_mcp *mcp = user_data; struct event_callback *cb = mcp->cb; DBG(mcp, "Track Changed"); if (cb && cb->cbs && cb->cbs->track_changed) cb->cbs->track_changed(mcp); } static void mcp_track_title_register(uint16_t att_ecode, void *user_data) { struct bt_mcp *mcp = user_data; if (att_ecode) DBG(mcp, "Media Track Title notification failed: 0x%04x", att_ecode); } static void mcp_track_title_notify(uint16_t value_handle, const uint8_t *value, uint16_t length, void *user_data) { struct bt_mcp *mcp = user_data; mcp_mp_set_track_title(mcp, value, length); } static void mcp_track_duration_register(uint16_t att_ecode, void *user_data) { struct bt_mcp *mcp = user_data; if (att_ecode) DBG(mcp, "Media Track Duration notification failed: 0x%04x", att_ecode); } static void mcp_track_duration_notify(uint16_t value_handle, const uint8_t *value, uint16_t length, void *user_data) { struct bt_mcp *mcp = user_data; int32_t duration; memcpy(&duration, value, sizeof(int32_t)); mcp_mp_set_title_duration(mcp, duration); } static void mcp_track_position_register(uint16_t att_ecode, void *user_data) { struct bt_mcp *mcp = user_data; if (att_ecode) DBG(mcp, "Media Track Position notification failed: 0x%04x", att_ecode); } static void mcp_track_position_notify(uint16_t value_handle, const uint8_t *value, uint16_t length, void *user_data) { struct bt_mcp *mcp = user_data; int32_t position; memcpy(&position, value, sizeof(int32_t)); mcp_mp_set_title_position(mcp, position); } static void mcp_media_state_register(uint16_t att_ecode, void *user_data) { struct bt_mcp *mcp = user_data; if (att_ecode) DBG(mcp, "Media Media State notification failed: 0x%04x", att_ecode); } static void mcp_media_state_notify(uint16_t value_handle, const uint8_t *value, uint16_t length, void *user_data) { struct bt_mcp *mcp = user_data; mcp_mp_set_media_state(mcp, *value); } static void mcp_media_cp_register(uint16_t att_ecode, void *user_data) { struct bt_mcp *mcp = user_data; if (att_ecode) DBG(mcp, "Media Media CP notification failed: 0x%04x", att_ecode); } static void mcp_media_cp_notify(uint16_t value_handle, const uint8_t *value, uint16_t length, void *user_data) { struct bt_mcp *mcp = user_data; DBG(mcp, "Media CP Notification"); } static void mcp_media_cp_op_supported_register(uint16_t att_ecode, void *user_data) { struct bt_mcp *mcp = user_data; if (att_ecode) DBG(mcp, "Media Media CP OP Supported notify failed: 0x%04x", att_ecode); } static void mcp_media_cp_op_supported_notify(uint16_t value_handle, const uint8_t *value, uint16_t length, void *user_data) { struct bt_mcp *mcp = user_data; memcpy(&mcp->session.cp_op_supported, value, sizeof(uint32_t)); DBG(mcp, "Media CP Opcodes Supported Notification 0x%08x", mcp->session.cp_op_supported); } static void bt_mcp_mp_name_attach(struct bt_mcp *mcp) { uint16_t value_handle; struct bt_mcs *mcs = mcp_get_mcs(mcp); if (!gatt_db_attribute_get_char_data(mcs->mp_name, NULL, &value_handle, NULL, NULL, NULL)) return; DBG(mcp, "Media Player handle 0x%04x", value_handle); mcp_read_value(mcp, value_handle, read_media_player_name, mcp); mcp->mp_name_id = bt_gatt_client_register_notify(mcp->client, value_handle, mcp_mp_name_register, mcp_mp_name_notify, mcp, NULL); } static void bt_mcp_track_changed_attach(struct bt_mcp *mcp) { uint16_t value_handle; struct bt_mcs *mcs = mcp_get_mcs(mcp); if (!gatt_db_attribute_get_char_data(mcs->track_changed, NULL, &value_handle, NULL, NULL, NULL)) return; DBG(mcp, "Track Changed handle 0x%04x", value_handle); mcp->track_changed_id = bt_gatt_client_register_notify(mcp->client, value_handle, mcp_track_changed_register, mcp_track_changed_notify, mcp, NULL); } static void bt_mcp_track_title_attach(struct bt_mcp *mcp) { uint16_t value_handle; struct bt_mcs *mcs = mcp_get_mcs(mcp); if (!gatt_db_attribute_get_char_data(mcs->track_title, NULL, &value_handle, NULL, NULL, NULL)) return; DBG(mcp, "Track Title handle 0x%04x", value_handle); mcp_read_value(mcp, value_handle, read_track_title, mcp); mcp->track_title_id = bt_gatt_client_register_notify(mcp->client, value_handle, mcp_track_title_register, mcp_track_title_notify, mcp, NULL); } static void bt_mcp_track_duration_attach(struct bt_mcp *mcp) { uint16_t value_handle; struct bt_mcs *mcs = mcp_get_mcs(mcp); if (!gatt_db_attribute_get_char_data(mcs->track_duration, NULL, &value_handle, NULL, NULL, NULL)) return; DBG(mcp, "Track Duration handle 0x%04x", value_handle); mcp_read_value(mcp, value_handle, read_track_duration, mcp); mcp->track_duration_id = bt_gatt_client_register_notify(mcp->client, value_handle, mcp_track_duration_register, mcp_track_duration_notify, mcp, NULL); } static void bt_mcp_track_position_attach(struct bt_mcp *mcp) { uint16_t value_handle; struct bt_mcs *mcs = mcp_get_mcs(mcp); if (!gatt_db_attribute_get_char_data(mcs->track_position, NULL, &value_handle, NULL, NULL, NULL)) return; DBG(mcp, "Track Position handle 0x%04x", value_handle); mcp_read_value(mcp, value_handle, read_track_position, mcp); mcp->track_position_id = bt_gatt_client_register_notify(mcp->client, value_handle, mcp_track_position_register, mcp_track_position_notify, mcp, NULL); } static void bt_mcp_media_state_attach(struct bt_mcp *mcp) { uint16_t value_handle; struct bt_mcs *mcs = mcp_get_mcs(mcp); if (!gatt_db_attribute_get_char_data(mcs->media_state, NULL, &value_handle, NULL, NULL, NULL)) return; DBG(mcp, "Media State handle 0x%04x", value_handle); mcp_read_value(mcp, value_handle, read_media_state, mcp); mcp->media_state_id = bt_gatt_client_register_notify(mcp->client, value_handle, mcp_media_state_register, mcp_media_state_notify, mcp, NULL); } static void bt_mcp_media_cp_attach(struct bt_mcp *mcp) { uint16_t value_handle; struct bt_mcs *mcs = mcp_get_mcs(mcp); if (!gatt_db_attribute_get_char_data(mcs->media_cp, NULL, &value_handle, NULL, NULL, NULL)) return; DBG(mcp, "Media Control Point handle 0x%04x", value_handle); mcp->media_cp_id = bt_gatt_client_register_notify(mcp->client, value_handle, mcp_media_cp_register, mcp_media_cp_notify, mcp, NULL); } static void bt_mcp_media_cp_op_supported_attach(struct bt_mcp *mcp) { uint16_t value_handle; struct bt_mcs *mcs = mcp_get_mcs(mcp); if (!gatt_db_attribute_get_char_data(mcs->media_cp_op_supportd, NULL, &value_handle, NULL, NULL, NULL)) return; DBG(mcp, "Media Control Point Opcodes Supported handle 0x%04x", value_handle); mcp_read_value(mcp, value_handle, read_media_cp_op_supported, mcp); mcp->media_cp_op_supported_id = bt_gatt_client_register_notify( mcp->client, value_handle, mcp_media_cp_op_supported_register, mcp_media_cp_op_supported_notify, mcp, NULL); } static void bt_mcp_content_control_id_supported_attach(struct bt_mcp *mcp) { uint16_t value_handle; struct bt_mcs *mcs = mcp_get_mcs(mcp); if (!gatt_db_attribute_get_char_data(mcs->content_control_id, NULL, &value_handle, NULL, NULL, NULL)) return; DBG(mcp, "Media Content Control id Supported handle 0x%04x", value_handle); mcp_read_value(mcp, value_handle, read_content_control_id, mcp); } static void foreach_mcs_char(struct gatt_db_attribute *attr, void *user_data) { struct bt_mcp *mcp = user_data; uint16_t value_handle; bt_uuid_t uuid, uuid_mp_name, uuid_track_changed, uuid_track_title, uuid_track_duration, uuid_track_position, uuid_media_state, uuid_media_cp, uuid_media_cp_op_supported, uuid_content_control_id; struct bt_mcs *mcs; if (!gatt_db_attribute_get_char_data(attr, NULL, &value_handle, NULL, NULL, &uuid)) return; bt_uuid16_create(&uuid_mp_name, MEDIA_PLAYER_NAME_CHRC_UUID); bt_uuid16_create(&uuid_track_changed, MEDIA_TRACK_CHNGD_CHRC_UUID); bt_uuid16_create(&uuid_track_title, MEDIA_TRACK_TITLE_CHRC_UUID); bt_uuid16_create(&uuid_track_duration, MEDIA_TRACK_DURATION_CHRC_UUID); bt_uuid16_create(&uuid_track_position, MEDIA_TRACK_POSTION_CHRC_UUID); bt_uuid16_create(&uuid_media_state, MEDIA_STATE_CHRC_UUID); bt_uuid16_create(&uuid_media_cp, MEDIA_CP_CHRC_UUID); bt_uuid16_create(&uuid_media_cp_op_supported, MEDIA_CP_OP_SUPPORTED_CHRC_UUID); bt_uuid16_create(&uuid_content_control_id, MEDIA_CONTENT_CONTROL_ID_CHRC_UUID); if (!bt_uuid_cmp(&uuid, &uuid_mp_name)) { DBG(mcp, "Media Player Name found: handle 0x%04x", value_handle); mcs = mcp_get_mcs(mcp); if (!mcs || mcs->mp_name) return; mcs->mp_name = attr; bt_mcp_mp_name_attach(mcp); } if (!bt_uuid_cmp(&uuid, &uuid_track_changed)) { DBG(mcp, "Track Changed found: handle 0x%04x", value_handle); mcs = mcp_get_mcs(mcp); if (!mcs || mcs->track_changed) return; mcs->track_changed = attr; bt_mcp_track_changed_attach(mcp); } if (!bt_uuid_cmp(&uuid, &uuid_track_title)) { DBG(mcp, "Track Title found: handle 0x%04x", value_handle); mcs = mcp_get_mcs(mcp); if (!mcs || mcs->track_title) return; mcs->track_title = attr; bt_mcp_track_title_attach(mcp); } if (!bt_uuid_cmp(&uuid, &uuid_track_duration)) { DBG(mcp, "Track Duration found: handle 0x%04x", value_handle); mcs = mcp_get_mcs(mcp); if (!mcs || mcs->track_duration) return; mcs->track_duration = attr; bt_mcp_track_duration_attach(mcp); } if (!bt_uuid_cmp(&uuid, &uuid_track_position)) { DBG(mcp, "Track Position found: handle 0x%04x", value_handle); mcs = mcp_get_mcs(mcp); if (!mcs || mcs->track_position) return; mcs->track_position = attr; bt_mcp_track_position_attach(mcp); } if (!bt_uuid_cmp(&uuid, &uuid_media_state)) { DBG(mcp, "Media State found: handle 0x%04x", value_handle); mcs = mcp_get_mcs(mcp); if (!mcs || mcs->media_state) return; mcs->media_state = attr; bt_mcp_media_state_attach(mcp); } if (!bt_uuid_cmp(&uuid, &uuid_media_cp)) { DBG(mcp, "Media Control Point found: handle 0x%04x", value_handle); mcs = mcp_get_mcs(mcp); if (!mcs || mcs->media_cp) return; mcs->media_cp = attr; bt_mcp_media_cp_attach(mcp); } if (!bt_uuid_cmp(&uuid, &uuid_media_cp_op_supported)) { DBG(mcp, "Media CP Opcodes Supported found: handle 0x%04x", value_handle); mcs = mcp_get_mcs(mcp); if (!mcs || mcs->media_cp_op_supportd) return; mcs->media_cp_op_supportd = attr; bt_mcp_media_cp_op_supported_attach(mcp); } if (!bt_uuid_cmp(&uuid, &uuid_content_control_id)) { DBG(mcp, "Content Control ID found: handle 0x%04x", value_handle); mcs = mcp_get_mcs(mcp); if (!mcs || mcs->content_control_id) return; mcs->content_control_id = attr; bt_mcp_content_control_id_supported_attach(mcp); } } void bt_mcp_set_event_callbacks(struct bt_mcp *mcp, const struct bt_mcp_event_callback *cbs, void *user_data) { struct event_callback *cb; if (!mcp) return; if (mcp->cb) free(mcp->cb); cb = new0(struct event_callback, 1); cb->cbs = cbs; cb->user_data = user_data; mcp->cb = cb; } static void foreach_mcs_service(struct gatt_db_attribute *attr, void *user_data) { struct bt_mcp *mcp = user_data; struct bt_mcs *mcs = mcp_get_mcs(mcp); DBG(mcp, ""); mcs->service = attr; gatt_db_service_foreach_char(attr, foreach_mcs_char, mcp); } static struct bt_mcp_db *mcp_db_new(struct gatt_db *db) { struct bt_mcp_db *mdb; if (!db) return NULL; mdb = new0(struct bt_mcp_db, 1); mdb->db = gatt_db_ref(db); if (!mcp_db) mcp_db = queue_new(); queue_push_tail(mcp_db, mdb); mdb->mcs = mcs_new(db); return mdb; } static struct bt_mcp_db *mcp_get_db(struct gatt_db *db) { struct bt_mcp_db *mdb; mdb = queue_find(mcp_db, mcp_db_match, db); if (mdb) return mdb; return mcp_db_new(db); } struct bt_mcp *bt_mcp_new(struct gatt_db *ldb, struct gatt_db *rdb) { struct bt_mcp *mcp; struct bt_mcp_db *mdb; if (!ldb) return NULL; mdb = mcp_get_db(ldb); if (!mdb) return NULL; mcp = new0(struct bt_mcp, 1); mcp->ldb = mdb; mcp->pending = queue_new(); if (!rdb) goto done; mdb = new0(struct bt_mcp_db, 1); mdb->db = gatt_db_ref(rdb); mcp->rdb = mdb; done: bt_mcp_ref(mcp); return mcp; } void bt_mcp_register(struct gatt_db *db) { if (!db) return; mcp_db_new(db); } bool bt_mcp_attach(struct bt_mcp *mcp, struct bt_gatt_client *client) { bt_uuid_t uuid; if (!mcp) return false; DBG(mcp, "mcp %p", mcp); mcp->client = bt_gatt_client_clone(client); if (!mcp->client) return false; if (mcp->rdb->mcs) { bt_mcp_mp_name_attach(mcp); bt_mcp_track_changed_attach(mcp); bt_mcp_track_title_attach(mcp); bt_mcp_track_duration_attach(mcp); bt_mcp_track_position_attach(mcp); bt_mcp_media_state_attach(mcp); bt_mcp_media_cp_attach(mcp); bt_mcp_media_cp_op_supported_attach(mcp); bt_mcp_content_control_id_supported_attach(mcp); return true; } bt_uuid16_create(&uuid, GMCS_UUID); gatt_db_foreach_service(mcp->rdb->db, &uuid, foreach_mcs_service, mcp); return true; } void bt_mcp_detach(struct bt_mcp *mcp) { if (!mcp) return; DBG(mcp, "%p", mcp); bt_gatt_client_unref(mcp->client); mcp->client = NULL; } bluez-5.82/src/shared/PaxHeaders/mainloop-ell.c0000644000000000000000000000005014772767672016470 xustar0020 atime=1743515579 20 ctime=1743591277 bluez-5.82/src/shared/mainloop-ell.c0000644000000000000000000000361014772767672016151 0ustar00rootroot// SPDX-License-Identifier: LGPL-2.1-or-later /* * * BlueZ - Bluetooth protocol stack for Linux * * Copyright (C) 2019 Intel Corporation * * */ #ifdef HAVE_CONFIG_H #include #endif #define _GNU_SOURCE #include #include #include #include #include #include "mainloop.h" static bool is_initialized; static int exit_status; static mainloop_signal_func sig_func; static void l_sig_func(uint32_t signo, void *user_data) { if (sig_func) sig_func(signo, user_data); } void mainloop_init(void) { is_initialized = l_main_init(); } void mainloop_quit(void) { l_main_quit(); } void mainloop_exit_success(void) { exit_status = EXIT_SUCCESS; l_main_quit(); } void mainloop_exit_failure(void) { exit_status = EXIT_FAILURE; l_main_quit(); } int mainloop_run(void) { if (!is_initialized) return -EINVAL; l_main_run(); is_initialized = false; sig_func = NULL; return exit_status; } int mainloop_run_with_signal(mainloop_signal_func func, void *user_data) { if (!is_initialized || !func) return -EINVAL; /* Workaround for sign discrepancy in ell and bluez */ sig_func = func; return l_main_run_with_signal(l_sig_func, user_data); } int mainloop_add_fd(int fd, uint32_t events, mainloop_event_func callback, void *user_data, mainloop_destroy_func destroy) { return -ENOSYS; } int mainloop_modify_fd(int fd, uint32_t events) { return -ENOSYS; } int mainloop_remove_fd(int fd) { return -ENOSYS; } int mainloop_add_timeout(unsigned int msec, mainloop_timeout_func callback, void *user_data, mainloop_destroy_func destroy) { return -ENOSYS; } int mainloop_modify_timeout(int fd, unsigned int msec) { return -ENOSYS; } int mainloop_remove_timeout(int id) { return -ENOSYS; } int mainloop_set_signal(sigset_t *mask, mainloop_signal_func callback, void *user_data, mainloop_destroy_func destroy) { return -ENOSYS; } bluez-5.82/src/shared/PaxHeaders/shell.c0000644000000000000000000000005014772767672015207 xustar0020 atime=1743515579 20 ctime=1743591277 bluez-5.82/src/shared/shell.c0000644000000000000000000010020114772767672014662 0ustar00rootroot// SPDX-License-Identifier: LGPL-2.1-or-later /* * * BlueZ - Bluetooth protocol stack for Linux * * Copyright (C) 2017 Intel Corporation. All rights reserved. * Copyright 2024 NXP * */ #ifdef HAVE_CONFIG_H #include #endif #define _GNU_SOURCE #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "src/shared/mainloop.h" #include "src/shared/timeout.h" #include "src/shared/io.h" #include "src/shared/util.h" #include "src/shared/queue.h" #include "src/shared/shell.h" #include "src/shared/log.h" #define CMD_LENGTH 48 #define print_text(color, fmt, args...) \ printf(color fmt COLOR_OFF "\n", ## args) #define print_menu(cmd, args, desc) \ printf(COLOR_HIGHLIGHT "%s %-*s " COLOR_OFF "%s\n", \ cmd, (int)(CMD_LENGTH - strlen(cmd)), args, desc) #define print_submenu(cmd, desc) \ printf(COLOR_BLUE "%s %-*s " COLOR_OFF "%s\n", \ cmd, (int)(CMD_LENGTH - strlen(cmd)), "", desc) struct bt_shell_env { char *name; void *value; }; static char *cmplt = "help"; struct bt_shell_prompt_input { char *str; bt_shell_prompt_input_func func; void *user_data; }; struct input { struct io *io; FILE *f; }; static struct { bool init; char *name; char history[256]; int argc; char **argv; bool mode; bool zsh; bool monitor; int timeout; int init_fd; struct queue *inputs; char *line; struct queue *queue; bool saved_prompt; bt_shell_prompt_input_func saved_func; void *saved_user_data; struct queue *prompts; const struct bt_shell_menu *menu; const struct bt_shell_menu *main; struct queue *submenus; const struct bt_shell_menu_entry *exec; struct queue *envs; } data; static void shell_print_menu(void); static void shell_print_menu_zsh_complete(void); static void cmd_version(int argc, char *argv[]) { bt_shell_printf("Version %s\n", VERSION); return bt_shell_noninteractive_quit(EXIT_SUCCESS); } static void cmd_quit(int argc, char *argv[]) { mainloop_quit(); } static void print_cmds(void) { const struct bt_shell_menu_entry *entry; const struct queue_entry *submenu; if (!data.menu) return; printf("Commands:\n"); for (entry = data.menu->entries; entry->cmd; entry++) { printf("\t%s%s\t%s\n", entry->cmd, strlen(entry->cmd) < 8 ? "\t" : "", entry->desc); } for (submenu = queue_get_entries(data.submenus); submenu; submenu = submenu->next) { struct bt_shell_menu *menu = submenu->data; printf("\n\t%s.:\n", menu->name); for (entry = menu->entries; entry->cmd; entry++) { printf("\t\t%s%s\t%s\n", entry->cmd, strlen(entry->cmd) < 8 ? "\t" : "", entry->desc); } } } static void cmd_help(int argc, char *argv[]) { if (argv[0] == cmplt) print_cmds(); else shell_print_menu(); return bt_shell_noninteractive_quit(EXIT_SUCCESS); } static const struct bt_shell_menu *find_menu(const char *name, size_t len, int *index) { const struct queue_entry *entry; int i; for (i = 0, entry = queue_get_entries(data.submenus); entry; entry = entry->next, i++) { struct bt_shell_menu *menu = entry->data; if (index) { if (i < *index) continue; (*index)++; } if (!strncmp(menu->name, name, len)) return menu; } return NULL; } static char *menu_generator(const char *text, int state) { static unsigned int index, len; static struct queue_entry *entry; if (!state) { index = 0; len = strlen(text); entry = (void *) queue_get_entries(data.submenus); } for (; entry; entry = entry->next) { struct bt_shell_menu *menu = entry->data; index++; if (!strncmp(menu->name, text, len)) { entry = entry->next; return strdup(menu->name); } } return NULL; } static void cmd_menu(int argc, char *argv[]) { const struct bt_shell_menu *menu; if (argc < 2 || !strlen(argv[1])) { bt_shell_printf("Missing name argument\n"); return bt_shell_noninteractive_quit(EXIT_FAILURE); } menu = find_menu(argv[1], strlen(argv[1]), NULL); if (!menu) { bt_shell_printf("Unable find menu with name: %s\n", argv[1]); return bt_shell_noninteractive_quit(EXIT_FAILURE); } bt_shell_set_menu(menu); shell_print_menu(); return bt_shell_noninteractive_quit(EXIT_SUCCESS); } static bool cmd_menu_exists(const struct bt_shell_menu *menu) { /* Skip menu command if not on main menu or if there are no * submenus. */ if (menu != data.main || queue_isempty(data.submenus)) return false; return true; } static void cmd_back(int argc, char *argv[]) { if (data.menu == data.main) { bt_shell_printf("Already on main menu\n"); return; } bt_shell_set_menu(data.main); shell_print_menu(); } static bool cmd_back_exists(const struct bt_shell_menu *menu) { /* Skip back command if on main menu */ if (menu == data.main) return false; return true; } static void cmd_export(int argc, char *argv[]) { const struct queue_entry *entry; for (entry = queue_get_entries(data.envs); entry; entry = entry->next) { struct bt_shell_env *env = entry->data; print_text(COLOR_HIGHLIGHT, "%s=%p", env->name, env->value); } } static int bt_shell_queue_exec(char *line) { int err; /* Ignore comments */ if (line[0] == '#') return 0; /* Queue if already executing */ if (data.line) { /* Check if prompt is being held then release using the line */ if (!bt_shell_release_prompt(line)) { bt_shell_printf("%s\n", line); return 0; } queue_push_tail(data.queue, strdup(line)); return 0; } bt_shell_printf("%s\n", line); err = bt_shell_exec(line); if (!err) data.line = strdup(line); return err; } static bool bt_shell_input_line(struct input *input) { int fd; char *line = NULL; size_t len = 0; ssize_t nread; fd = io_get_fd(input->io); if (fd < 0) { printf("io_get_fd() returned %d\n", fd); return false; } if (fd == STDIN_FILENO) { rl_callback_read_char(); return true; } if (!input->f) { input->f = fdopen(fd, "r"); if (!input->f) { printf("fdopen: %s (%d)\n", strerror(errno), errno); return false; } } nread = getline(&line, &len, input->f); if (nread > 0) { int err; if (line[nread - 1] == '\n') line[nread - 1] = '\0'; err = bt_shell_queue_exec(line); if (err < 0) printf("%s: %s (%d)\n", line, strerror(-err), -err); } else if (input->f) { fclose(input->f); input->f = NULL; } free(line); return input->f ? true : false; } static bool input_read(struct io *io, void *user_data) { return bt_shell_input_line(user_data); } static bool input_hup(struct io *io, void *user_data) { if (queue_remove(data.inputs, user_data)) { if (!queue_isempty(data.inputs)) return false; } mainloop_quit(); return false; } static struct input *input_new(int fd) { struct input *input; struct io *io; io = io_new(fd); if (!io) return NULL; input = new0(struct input, 1); input->io = io; queue_push_tail(data.inputs, input); return input; } static bool bt_shell_input_attach(int fd) { struct input *input; struct queue *queue; input = input_new(fd); if (!input) return false; /* Save executing queue so input lines can be placed in the correct * order. */ queue = data.queue; data.queue = queue_new(); while (bt_shell_input_line(input)); /* Push existing input lines back into the executing queue */ while (!queue_isempty(queue)) queue_push_tail(data.queue, queue_pop_head(queue)); queue_destroy(queue, free); return true; } static void cmd_script(int argc, char *argv[]) { int fd; fd = open(argv[1], O_RDONLY); if (fd < 0) { printf("Unable to open %s: %s (%d)\n", argv[1], strerror(errno), errno); bt_shell_noninteractive_quit(EXIT_FAILURE); return; } printf("Running script %s...\n", argv[1]); if (!bt_shell_input_attach(fd)) return bt_shell_noninteractive_quit(EXIT_FAILURE); return bt_shell_noninteractive_quit(EXIT_SUCCESS); } static const struct bt_shell_menu_entry default_menu[] = { { "back", NULL, cmd_back, "Return to main menu", NULL, NULL, cmd_back_exists }, { "menu", "", cmd_menu, "Select submenu", menu_generator, NULL, cmd_menu_exists}, { "version", NULL, cmd_version, "Display version" }, { "quit", NULL, cmd_quit, "Quit program" }, { "exit", NULL, cmd_quit, "Quit program" }, { "help", NULL, cmd_help, "Display help about this program" }, { "export", NULL, cmd_export, "Print environment variables" }, { "script", "", cmd_script, "Run script" }, { } }; static void shell_print_help(void) { print_text(COLOR_HIGHLIGHT, "\n" "Use \"help\" for a list of available commands in a menu.\n" "Use \"menu \" if you want to enter any submenu.\n" "Use \"back\" if you want to return to menu main."); } static void shell_print_menu(void) { const struct bt_shell_menu_entry *entry; const struct queue_entry *submenu; if (!data.menu) return; if (data.zsh) { shell_print_menu_zsh_complete(); return; } print_text(COLOR_HIGHLIGHT, "Menu %s:", data.menu->name); print_text(COLOR_HIGHLIGHT, "Available commands:"); print_text(COLOR_HIGHLIGHT, "-------------------"); if (data.menu == data.main) { for (submenu = queue_get_entries(data.submenus); submenu; submenu = submenu->next) { struct bt_shell_menu *menu = submenu->data; print_submenu(menu->name, menu->desc ? menu->desc : "Submenu"); } } for (entry = data.menu->entries; entry->cmd; entry++) { print_menu(entry->cmd, entry->arg ? : "", entry->desc ? : ""); } for (entry = default_menu; entry->cmd; entry++) { if (entry->exists && !entry->exists(data.menu)) continue; print_menu(entry->cmd, entry->arg ? : "", entry->desc ? : ""); } } static void shell_print_menu_zsh_complete(void) { const struct bt_shell_menu_entry *entry; for (entry = data.menu->entries; entry->cmd; entry++) printf("%s:%s\n", entry->cmd, entry->desc ? : ""); for (entry = default_menu; entry->cmd; entry++) { if (entry->exists && !entry->exists(data.menu)) continue; printf("%s:%s\n", entry->cmd, entry->desc ? : ""); } } static int parse_args(char *arg, wordexp_t *w, char *del, int flags) { char *str; str = strdelimit(arg, del, '"'); if (wordexp(str, w, flags)) { free(str); return -EINVAL; } /* If argument ends with ... set we_offs bypass strict checks */ if (w->we_wordc && !strsuffix(w->we_wordv[w->we_wordc -1], "...")) w->we_offs = 1; free(str); return 0; } static int cmd_exec(const struct bt_shell_menu_entry *entry, int argc, char *argv[]) { wordexp_t w; size_t len; char *man, *opt; int flags = WRDE_NOCMD; bool optargs = false; if (argc == 2 && (!memcmp(argv[1], "help", 4) || !memcmp(argv[1], "--help", 6))) { printf("%s\n", entry->desc); printf(COLOR_HIGHLIGHT "Usage:" COLOR_OFF "\n"); printf("\t %s %-*s\n", entry->cmd, (int)(CMD_LENGTH - strlen(entry->cmd)), !entry->arg ? "" : entry->arg); return 0; } if (!entry->arg || entry->arg[0] == '\0') { if (argc > 1) { print_text(COLOR_HIGHLIGHT, "Too many arguments"); return -EINVAL; } goto exec; } /* Find last mandatory arguments */ man = strrchr(entry->arg, '>'); if (!man) { opt = strdup(entry->arg); goto optional; } len = man - entry->arg; if (entry->arg[0] == '<') man = strndup(entry->arg, len + 1); else { /* Find where mandatory arguments start */ opt = strrchr(entry->arg, '<'); /* Skip if mandatory arguments are not in the right format */ if (!opt || opt > man) { opt = strdup(entry->arg); goto optional; } man = strndup(opt, man - opt + 1); optargs = true; } if (parse_args(man, &w, "<>", flags) < 0) { print_text(COLOR_HIGHLIGHT, "Unable to parse mandatory command arguments: %s", man ); free(man); return -EINVAL; } free(man); /* Check if there are enough arguments */ if ((unsigned) argc - 1 < w.we_wordc) { print_text(COLOR_HIGHLIGHT, "Missing %s argument", w.we_wordv[argc - 1]); goto fail; } flags |= WRDE_APPEND; opt = strdup(entry->arg + len + 1); optional: if (parse_args(opt, &w, "[]", flags) < 0) { print_text(COLOR_HIGHLIGHT, "Unable to parse optional command arguments: %s", opt); free(opt); return -EINVAL; } free(opt); /* Check if there are too many arguments */ if (!optargs && ((unsigned int) argc - 1 > w.we_wordc && !w.we_offs)) { print_text(COLOR_HIGHLIGHT, "Too many arguments: %d > %zu", argc - 1, w.we_wordc); goto fail; } w.we_offs = 0; wordfree(&w); exec: data.exec = entry; if (entry->func) entry->func(argc, argv); data.exec = NULL; return 0; fail: w.we_offs = 0; wordfree(&w); return -EINVAL; } static int menu_exec(const struct bt_shell_menu_entry *entry, int argc, char *argv[]) { for (; entry->cmd; entry++) { if (strcmp(argv[0], entry->cmd)) continue; /* Skip menu command if not on main menu */ if (data.menu != data.main && !strcmp(entry->cmd, "menu")) continue; /* Skip back command if on main menu */ if (data.menu == data.main && !strcmp(entry->cmd, "back")) continue; return cmd_exec(entry, argc, argv); } return -ENOENT; } static int submenu_exec(int argc, char *argv[]) { char *name; int len, tlen; const struct bt_shell_menu *submenu; if (data.menu != data.main) return -ENOENT; name = strchr(argv[0], '.'); if (!name) return -ENOENT; tlen = strlen(argv[0]); len = name - argv[0]; name[0] = '\0'; submenu = find_menu(argv[0], strlen(argv[0]), NULL); if (!submenu) return -ENOENT; /* Replace submenu.command with command */ memmove(argv[0], argv[0] + len + 1, tlen - len - 1); memset(argv[0] + tlen - len - 1, 0, len + 1); return menu_exec(submenu->entries, argc, argv); } static int shell_exec(int argc, char *argv[]) { int err; if (!data.menu || !argv[0]) return -EINVAL; if (!argsisutf8(argc, argv)) return -EINVAL; err = menu_exec(default_menu, argc, argv); if (err == -ENOENT) { err = menu_exec(data.menu->entries, argc, argv); if (err == -ENOENT) { err = submenu_exec(argc, argv); if (err == -ENOENT) { print_text(COLOR_HIGHLIGHT, "Invalid command in menu %s: %s", data.menu->name , argv[0]); shell_print_help(); } } } return err; } void bt_shell_printf(const char *fmt, ...) { va_list args; bool save_input; char *saved_line; int saved_point; if (queue_isempty(data.inputs)) return; if (data.mode) { va_start(args, fmt); vprintf(fmt, args); va_end(args); return; } save_input = !RL_ISSTATE(RL_STATE_DONE); if (save_input) { saved_point = rl_point; saved_line = rl_copy_text(0, rl_end); if (!data.saved_prompt) rl_save_prompt(); rl_clear_visible_line(); rl_reset_line_state(); } va_start(args, fmt); vprintf(fmt, args); va_end(args); if (data.monitor) { va_start(args, fmt); bt_log_vprintf(0xffff, data.name, LOG_INFO, fmt, args); va_end(args); } if (save_input) { if (!data.saved_prompt) rl_restore_prompt(); rl_replace_line(saved_line, 0); rl_point = saved_point; rl_redisplay(); free(saved_line); } } void bt_shell_echo(const char *fmt, ...) { va_list args; char *str; int ret; va_start(args, fmt); ret = vasprintf(&str, fmt, args); va_end(args); if (ret < 0) return; rl_save_prompt(); bt_shell_set_prompt(str, COLOR_HIGHLIGHT); rl_restore_prompt(); } static void print_string(const char *str, void *user_data) { bt_shell_printf("%s\n", str); } void bt_shell_hexdump(const unsigned char *buf, size_t len) { util_hexdump(' ', buf, len, print_string, NULL); } void bt_shell_usage(void) { if (!data.exec) return; bt_shell_printf("Usage: %s %s\n", data.exec->cmd, data.exec->arg ? data.exec->arg : ""); } static void bt_shell_dequeue_exec(void) { int err; if (!data.line) return; free(data.line); data.line = NULL; data.line = queue_pop_head(data.queue); if (!data.line) return; bt_shell_printf("%s\n", data.line); if (!bt_shell_release_prompt(data.line)) { /* If a prompt was released with this line, * try to release all the other prompts, * if any are left. Otherwise, the next * line will be executed on * bt_shell_noninteractive_quit. */ if (data.saved_prompt) bt_shell_dequeue_exec(); return; } err = bt_shell_exec(data.line); if (err) bt_shell_dequeue_exec(); } static void prompt_input(const char *str, bt_shell_prompt_input_func func, void *user_data) { data.saved_prompt = true; data.saved_func = func; data.saved_user_data = user_data; rl_save_prompt(); bt_shell_set_prompt(str, COLOR_HIGHLIGHT); } void bt_shell_prompt_input(const char *label, const char *msg, bt_shell_prompt_input_func func, void *user_data) { char *str; if (!data.init || data.mode) return; if (data.saved_prompt) { struct bt_shell_prompt_input *prompt; prompt = new0(struct bt_shell_prompt_input, 1); if (asprintf(&str, COLOR_HIGHLIGHT "[%s] %s " COLOR_OFF, label, msg) < 0) { free(prompt); return; } prompt->func = func; prompt->user_data = user_data; queue_push_tail(data.prompts, prompt); free(str); return; } if (asprintf(&str, COLOR_HIGHLIGHT "[%s] %s " COLOR_OFF, label, msg) < 0) return; prompt_input(str, func, user_data); free(str); if (data.line && !queue_isempty(data.queue)) /* If a prompt was set to receive input and * data is already available, try to execute * the line and release the prompt. */ bt_shell_dequeue_exec(); } static void prompt_free(void *data) { struct bt_shell_prompt_input *prompt = data; free(prompt->str); free(prompt); } int bt_shell_release_prompt(const char *input) { struct bt_shell_prompt_input *prompt; bt_shell_prompt_input_func func; void *user_data; if (!data.saved_prompt) return -1; data.saved_prompt = false; rl_restore_prompt(); func = data.saved_func; user_data = data.saved_user_data; prompt = queue_pop_head(data.prompts); if (prompt) data.saved_prompt = true; data.saved_func = NULL; data.saved_user_data = NULL; func(input, user_data); if (prompt) { prompt_input(prompt->str, prompt->func, prompt->user_data); prompt_free(prompt); } return 0; } static void rl_handler(char *input) { if (!input) { rl_insert_text("quit"); rl_redisplay(); rl_crlf(); mainloop_quit(); return; } /* Ignore empty/comment lines */ if (!strlen(input) || input[0] == '#') goto done; if (!bt_shell_release_prompt(input)) goto done; bt_shell_exec(input); done: free(input); } static char *find_cmd(const char *text, const struct bt_shell_menu_entry *entry, int *index) { const struct bt_shell_menu_entry *tmp; int len; len = strlen(text); while ((tmp = &entry[*index])) { (*index)++; if (!tmp->cmd) break; if (tmp->exists && !tmp->exists(data.menu)) continue; if (!strncmp(tmp->cmd, text, len)) return strdup(tmp->cmd); } return NULL; } static char *cmd_generator(const char *text, int state) { static int index; static bool default_menu_enabled, menu_enabled, submenu_enabled; static const struct bt_shell_menu *menu; char *cmd; if (!state) { index = 0; menu = NULL; default_menu_enabled = true; submenu_enabled = false; } if (default_menu_enabled) { cmd = find_cmd(text, default_menu, &index); if (cmd) { return cmd; } else { index = 0; menu = data.menu; default_menu_enabled = false; if (data.main == data.menu) menu_enabled = true; } } if (menu_enabled) { menu = find_menu(text, strlen(text), &index); if (menu) return strdup(menu->name); index = 0; menu = data.menu; menu_enabled = false; } if (!submenu_enabled) { cmd = find_cmd(text, menu->entries, &index); if (cmd || menu != data.main) return cmd; cmd = strrchr(text, '.'); if (!cmd) return NULL; menu = find_menu(text, cmd - text, NULL); if (!menu) return NULL; index = 0; submenu_enabled = true; } cmd = find_cmd(text + strlen(menu->name) + 1, menu->entries, &index); if (cmd) { int err; char *tmp; err = asprintf(&tmp, "%s.%s", menu->name, cmd); free(cmd); if (err < 0) return NULL; cmd = tmp; } return cmd; } static wordexp_t args; static char *arg_generator(const char *text, int state) { static unsigned int index, len; const char *arg; if (!state) { index = 0; len = strlen(text); } while (index < args.we_wordc) { arg = args.we_wordv[index]; index++; if (!strncmp(arg, text, len)) return strdup(arg); } return NULL; } static char **args_completion(const struct bt_shell_menu_entry *entry, int argc, const char *text) { char **matches = NULL; char *str; int index; index = text[0] == '\0' ? argc - 1 : argc - 2; if (index < 0) return NULL; if (!entry->arg) goto end; str = strdup(entry->arg); if (parse_args(str, &args, "<>[]", WRDE_NOCMD)) goto done; /* Check if argument is valid */ if ((unsigned) index > args.we_wordc - 1) goto done; /* Check if there are multiple values */ if (!strrchr(entry->arg, '/')) goto done; free(str); /* Split values separated by / */ str = strdelimit(args.we_wordv[index], "/", ' '); args.we_offs = 0; wordfree(&args); if (wordexp(str, &args, WRDE_NOCMD)) goto done; rl_completion_display_matches_hook = NULL; matches = rl_completion_matches(text, arg_generator); done: free(str); end: if (!matches && text[0] == '\0') bt_shell_printf("Usage: %s %s\n", entry->cmd, entry->arg ? entry->arg : ""); args.we_offs = 0; wordfree(&args); return matches; } static char **menu_completion(const struct bt_shell_menu_entry *entry, const char *text, int argc, char *input_cmd) { char **matches = NULL; for (; entry->cmd; entry++) { if (strcmp(entry->cmd, input_cmd)) continue; if (!entry->gen) { matches = args_completion(entry, argc, text); break; } rl_completion_display_matches_hook = entry->disp; matches = rl_completion_matches(text, entry->gen); break; } return matches; } static char **submenu_completion(const char *text, int argc, char *input_cmd) { const struct bt_shell_menu *menu; char *cmd; if (data.main != data.menu) return NULL; cmd = strrchr(input_cmd, '.'); if (!cmd) return NULL; menu = find_menu(input_cmd, cmd - input_cmd, NULL); if (!menu) return NULL; return menu_completion(menu->entries, text, argc, input_cmd + strlen(menu->name) + 1); } static char **shell_completion(const char *text, int start, int end) { char **matches = NULL; rl_attempted_completion_over = 1; if (!data.menu) return NULL; if (start > 0) { wordexp_t w; if (wordexp(rl_line_buffer, &w, WRDE_NOCMD)) return NULL; matches = menu_completion(default_menu, text, w.we_wordc, w.we_wordv[0]); if (!matches) { matches = menu_completion(data.menu->entries, text, w.we_wordc, w.we_wordv[0]); if (!matches) matches = submenu_completion(text, w.we_wordc, w.we_wordv[0]); } wordfree(&w); } else { rl_completion_display_matches_hook = NULL; matches = rl_completion_matches(text, cmd_generator); } return matches; } static void signal_callback(int signum, void *user_data) { static bool terminated = false; switch (signum) { case SIGINT: if (!queue_isempty(data.inputs) && !data.mode) { rl_replace_line("", 0); rl_crlf(); rl_on_new_line(); rl_redisplay(); return; } /* * If input was not yet setup up that means signal was received * while daemon was not yet running. Since user is not able * to terminate client by CTRL-D or typing exit treat this as * exit and fall through. */ /* fall through */ case SIGTERM: if (!terminated) { if (!data.mode) { rl_replace_line("", 0); rl_crlf(); } mainloop_quit(); } terminated = true; break; } } static void rl_init_history(void) { const char *name; char *dir; memset(data.history, 0, sizeof(data.history)); name = strrchr(data.name, '/'); if (!name) name = data.name; else name++; dir = getenv("XDG_CACHE_HOME"); if (dir) { snprintf(data.history, sizeof(data.history), "%s/.%s_history", dir, name); goto done; } dir = getenv("HOME"); if (dir) { snprintf(data.history, sizeof(data.history), "%s/.cache/.%s_history", dir, name); goto done; } dir = getenv("PWD"); if (dir) { snprintf(data.history, sizeof(data.history), "%s/.%s_history", dir, name); goto done; } return; done: read_history(data.history); using_history(); bt_shell_set_env("HISTORY", data.history); } static void rl_init(void) { if (data.mode) return; /* Allow conditional parsing of the ~/.inputrc file. */ rl_readline_name = data.name; rl_attempted_completion_function = shell_completion; rl_erase_empty_line = 1; rl_callback_handler_install(NULL, rl_handler); rl_init_history(); } static const struct option main_options[] = { { "version", no_argument, 0, 'v' }, { "help", no_argument, 0, 'h' }, { "init-script", required_argument, 0, 's' }, { "timeout", required_argument, 0, 't' }, { "monitor", no_argument, 0, 'm' }, { "zsh-complete", no_argument, 0, 'z' }, }; static void usage(int argc, char **argv, const struct bt_shell_opt *opt) { unsigned int i; printf("%s ver %s\n", data.name, VERSION); printf("Usage:\n" "\t%s [--options] [commands]\n", data.name); printf("Options:\n"); for (i = 0; opt && opt->options[i].name; i++) printf("\t--%s \t%s\n", opt->options[i].name, opt->help[i]); printf("\t--monitor \tEnable monitor output\n" "\t--timeout \tTimeout in seconds for non-interactive mode\n" "\t--version \tDisplay version\n" "\t--init-script \tInit script file\n" "\t--help \t\tDisplay help\n"); } void bt_shell_init(int argc, char **argv, const struct bt_shell_opt *opt) { int c, index = -1; struct option options[256]; char optstr[256]; size_t offset; char *endptr = NULL; offset = sizeof(main_options) / sizeof(struct option); memcpy(options, main_options, sizeof(struct option) * offset); if (opt) { memcpy(options + offset, opt->options, sizeof(struct option) * opt->optno); snprintf(optstr, sizeof(optstr), "+mhvs:t:%s", opt->optstr); } else snprintf(optstr, sizeof(optstr), "+mhvs:t:"); data.name = strrchr(argv[0], '/'); if (!data.name) data.name = strdup(argv[0]); else data.name = strdup(++data.name); data.init_fd = -1; while ((c = getopt_long(argc, argv, optstr, options, &index)) != -1) { switch (c) { case 'v': printf("%s: %s\n", data.name, VERSION); exit(EXIT_SUCCESS); return; case 'h': usage(argc, argv, opt); data.argc = 1; data.argv = &cmplt; data.mode = 1; goto done; case 's': if (optarg && data.init_fd < 0) { data.init_fd = open(optarg, O_RDONLY); if (data.init_fd < 0) printf("Unable to open %s: %s (%d)\n", optarg, strerror(errno), errno); } break; case 't': if (optarg) data.timeout = strtol(optarg, &endptr, 0); if (!endptr || *endptr != '\0') printf("Unable to parse timeout\n"); break; case 'z': data.zsh = 1; break; case 'm': data.monitor = true; if (bt_log_open() < 0) { data.monitor = false; printf("Unable to open logging channel\n"); } break; default: if (index < 0) { for (index = 0; options[index].val; index++) { if (c == options[index].val) break; } } if (opt && index >= 0 && (size_t)index >= offset) { if (c != opt->options[index - offset].val) { usage(argc, argv, opt); exit(EXIT_SUCCESS); return; } *opt->optarg[index - offset] = optarg ? : ""; } } index = -1; } bt_shell_set_env("SHELL", data.name); data.argc = argc - optind; data.argv = argv + optind; optind = 0; data.mode = (data.argc > 0); done: if (data.mode) bt_shell_set_env("NON_INTERACTIVE", &data.mode); mainloop_init(); /* Always set stdout as line buffered */ setlinebuf(stdout); rl_init(); data.init = true; data.inputs = queue_new(); data.queue = queue_new(); data.prompts = queue_new(); } static void rl_cleanup(void) { if (data.mode) return; if (data.history[0] != '\0') write_history(data.history); rl_message(""); rl_callback_handler_remove(); } static void env_destroy(void *data) { struct bt_shell_env *env = data; free(env->name); free(env); } int bt_shell_run(void) { int status; status = mainloop_run_with_signal(signal_callback, NULL); bt_shell_cleanup(); return status; } int bt_shell_exec(const char *input) { HIST_ENTRY *last; wordexp_t w; int err; if (!input) return 0; last = history_get(history_length + history_base - 1); /* append only if input is different from previous command */ if (!last || strcmp(input, last->line)) add_history(input); if (data.monitor) bt_log_printf(0xffff, data.name, LOG_INFO, "%s", input); err = wordexp(input, &w, WRDE_NOCMD); switch (err) { case WRDE_BADCHAR: return -EBADMSG; case WRDE_BADVAL: case WRDE_SYNTAX: return -EINVAL; case WRDE_NOSPACE: return -ENOMEM; case WRDE_CMDSUB: if (wordexp(input, &w, 0)) return -ENOEXEC; break; }; if (w.we_wordc == 0) { wordfree(&w); return -ENOEXEC; } err = shell_exec(w.we_wordc, w.we_wordv); wordfree(&w); return err; } static void input_destroy(void *data) { struct input *input = data; if (input->f) fclose(input->f); io_destroy(input->io); free(input); } void bt_shell_cleanup(void) { bt_shell_release_prompt(""); bt_shell_detach(); if (data.envs) { queue_destroy(data.envs, env_destroy); data.envs = NULL; } if (data.monitor) bt_log_close(); rl_cleanup(); queue_destroy(data.inputs, input_destroy); data.inputs = NULL; queue_destroy(data.queue, free); data.queue = NULL; queue_destroy(data.prompts, prompt_free); data.prompts = NULL; data.init = false; free(data.name); } void bt_shell_quit(int status) { if (status == EXIT_SUCCESS) mainloop_exit_success(); else mainloop_exit_failure(); } void bt_shell_noninteractive_quit(int status) { if (!data.mode || data.timeout) { bt_shell_dequeue_exec(); return; } bt_shell_quit(status); } bool bt_shell_set_menu(const struct bt_shell_menu *menu) { if (!menu) return false; data.menu = menu; if (!data.main) data.main = menu; return true; } bool bt_shell_add_submenu(const struct bt_shell_menu *menu) { if (!menu) return false; if (!data.main) return bt_shell_set_menu(menu); if (!data.submenus) data.submenus = queue_new(); queue_push_tail(data.submenus, (void *) menu); return true; } void bt_shell_set_prompt(const char *string, const char *color) { char *prompt; if (!data.init || data.mode) return; /* Envelope color within RL_PROMPT_START_IGNORE (\001) and * RL_PROMPT_END_IGNORE (\002) so readline can properly calculate the * prompt length. */ if (!color || asprintf(&prompt, "\001%s\002%s\001%s\002", color, string, COLOR_OFF) < 0) { rl_set_prompt(string); } else { rl_set_prompt(prompt); free(prompt); } rl_redisplay(); } static bool shell_quit(void *data) { mainloop_quit(); return false; } bool bt_shell_attach(int fd) { struct input *input; input = input_new(fd); if (!input) return false; if (!data.mode) { io_set_read_handler(input->io, input_read, input, NULL); io_set_disconnect_handler(input->io, input_hup, input, NULL); } if (data.mode) { if (shell_exec(data.argc, data.argv) < 0) { bt_shell_noninteractive_quit(EXIT_FAILURE); return true; } if (data.timeout) timeout_add(data.timeout * 1000, shell_quit, NULL, NULL); } else if (data.init_fd >= 0) { int fd = data.init_fd; data.init_fd = -1; if (!bt_shell_attach(fd)) return false; } return true; } bool bt_shell_detach(void) { if (queue_isempty(data.inputs)) return false; queue_remove_all(data.inputs, NULL, NULL, input_destroy); return true; } static bool match_env(const void *data, const void *user_data) { const struct bt_shell_env *env = data; const char *name = user_data; return !strcmp(env->name, name); } void bt_shell_set_env(const char *name, void *value) { struct bt_shell_env *env; if (!data.envs) { if (!value) return; data.envs = queue_new(); goto done; } env = queue_remove_if(data.envs, match_env, (void *) name); if (env) env_destroy(env); /* Don't create an env if value is not set */ if (!value) return; done: env = new0(struct bt_shell_env, 1); env->name = strdup(name); env->value = value; queue_push_tail(data.envs, env); } void *bt_shell_get_env(const char *name) { const struct bt_shell_env *env; if (!data.envs) return NULL; env = queue_find(data.envs, match_env, name); if (!env) return NULL; return env->value; } int bt_shell_get_timeout(void) { return data.timeout; } bluez-5.82/src/shared/PaxHeaders/gatt-client.c0000644000000000000000000000005014766002272016270 xustar0020 atime=1743515578 20 ctime=1743591277 bluez-5.82/src/shared/gatt-client.c0000644000000000000000000025131614766002272015761 0ustar00rootroot// SPDX-License-Identifier: LGPL-2.1-or-later /* * * BlueZ - Bluetooth protocol stack for Linux * * Copyright (C) 2014 Google Inc. * * */ #ifdef HAVE_CONFIG_H #include #endif #include "src/shared/att.h" #include "lib/bluetooth.h" #include "lib/uuid.h" #include "src/shared/gatt-helpers.h" #include "src/shared/util.h" #include "src/shared/queue.h" #include "src/shared/gatt-db.h" #include "src/shared/gatt-client.h" #include #include #include #ifndef MAX #define MAX(a, b) ((a) > (b) ? (a) : (b)) #endif #ifndef MIN #define MIN(a, b) ((a) < (b) ? (a) : (b)) #endif #define UUID_BYTES (BT_GATT_UUID_SIZE * sizeof(uint8_t)) #define GATT_SVC_UUID 0x1801 #define SVC_CHNGD_UUID 0x2a05 #define DBG(_client, _format, arg...) \ gatt_log(_client, "[%p] %s:%s() " _format, _client, __FILE__, \ __func__, ## arg) struct ready_cb { bt_gatt_client_callback_t callback; bt_gatt_client_destroy_func_t destroy; void *data; }; struct idle_cb { bt_gatt_client_idle_callback_t callback; bt_gatt_client_destroy_func_t destroy; void *data; }; struct bt_gatt_client { struct bt_att *att; int ref_count; uint8_t features; struct bt_gatt_client *parent; struct queue *clones; struct queue *ready_cbs; struct queue *idle_cbs; bt_gatt_client_service_changed_callback_t svc_chngd_callback; bt_gatt_client_destroy_func_t svc_chngd_destroy; void *svc_chngd_data; bt_gatt_client_debug_func_t debug_callback; bt_gatt_client_destroy_func_t debug_destroy; void *debug_data; struct gatt_db *db; bool in_init; bool ready; /* * Queue of long write requests. An error during "prepare write" * requests can result in a cancel through "execute write". To prevent * cancelation of prepared writes to the wrong attribute and multiple * requests to the same attribute that may result in a corrupted final * value, we avoid interleaving prepared writes. */ struct queue *long_write_queue; bool in_long_write; unsigned int reliable_write_session_id; /* List of registered disconnect/notification/indication callbacks */ struct queue *notify_list; struct queue *notify_chrcs; int next_reg_id; unsigned int disc_id, nfy_id, nfy_mult_id, ind_id; /* * Handles of the GATT Service and the Service Changed characteristic * value handle. These will have the value 0 if they are not present on * the remote peripheral. */ unsigned int svc_chngd_ind_id; bool svc_chngd_registered; struct queue *svc_chngd_queue; /* Queued service changed events */ bool in_svc_chngd; /* * List of pending read/write operations. For operations that span * across multiple PDUs, this list provides a mapping from an operation * id to an ATT request id. */ struct queue *pending_requests; unsigned int next_request_id; struct bt_gatt_request *discovery_req; unsigned int mtu_req_id; }; struct request { struct bt_gatt_client *client; bool long_write; bool prep_write; bool removed; int ref_count; unsigned int id; unsigned int att_id; void *data; void (*destroy)(void *); }; static struct request *request_ref(struct request *req) { __sync_fetch_and_add(&req->ref_count, 1); return req; } static struct request *request_create(struct bt_gatt_client *client) { struct request *req; if (!client->att) return NULL; req = new0(struct request, 1); if (client->next_request_id < 1) client->next_request_id = 1; queue_push_tail(client->pending_requests, req); req->client = client; req->id = client->next_request_id++; return request_ref(req); } static void idle_destroy(void *data) { struct idle_cb *idle = data; if (idle->destroy) idle->destroy(idle->data); free(idle); } static bool idle_notify(const void *data, const void *user_data) { const struct idle_cb *idle = data; idle->callback(idle->data); return true; } static struct bt_gatt_client * bt_gatt_client_ref_safe(struct bt_gatt_client *client) { if (!client || !client->ref_count) return NULL; return bt_gatt_client_ref(client); } static void notify_client_idle(struct bt_gatt_client *client) { client = bt_gatt_client_ref_safe(client); if (!client) return; queue_remove_all(client->idle_cbs, idle_notify, NULL, idle_destroy); bt_gatt_client_unref(client); } static void request_unref(void *data) { struct request *req = data; struct bt_gatt_client *client = req->client; if (__sync_sub_and_fetch(&req->ref_count, 1)) return; if (req->destroy) req->destroy(req->data); if (!req->removed) { queue_remove(client->pending_requests, req); if (queue_isempty(client->pending_requests)) notify_client_idle(client); } free(req); } struct notify_chrc { struct bt_gatt_client *client; struct gatt_db_attribute *attr; uint16_t value_handle; uint16_t ccc_handle; uint16_t properties; unsigned int notify_id; int notify_count; /* Reference count of registered notify callbacks */ /* Pending calls to register_notify are queued here so that they can be * processed after a write that modifies the CCC descriptor. */ struct queue *reg_notify_queue; unsigned int ccc_write_id; }; struct notify_data { struct bt_gatt_client *client; unsigned int id; unsigned int att_id; uint8_t att_ecode; int ref_count; struct notify_chrc *chrc; bt_gatt_client_register_callback_t callback; bt_gatt_client_notify_callback_t notify; void *user_data; bt_gatt_client_destroy_func_t destroy; }; static struct notify_data *notify_data_ref(struct notify_data *notify_data) { __sync_fetch_and_add(¬ify_data->ref_count, 1); return notify_data; } static void notify_data_unref(void *data) { struct notify_data *notify_data = data; if (__sync_sub_and_fetch(¬ify_data->ref_count, 1)) return; if (notify_data->destroy) notify_data->destroy(notify_data->user_data); free(notify_data); } static bool match_notify_chrc(const void *data, const void *user_data) { const struct notify_data *notify_data = data; const struct notify_chrc *chrc = user_data; return notify_data->chrc == chrc; } static void notify_data_cleanup(void *data) { struct notify_data *notify_data = data; if (notify_data->att_id) bt_att_cancel(notify_data->client->att, notify_data->att_id); notify_data_unref(notify_data); } static void notify_chrc_free(void *data) { struct notify_chrc *chrc = data; if (chrc->notify_id) gatt_db_attribute_unregister(chrc->attr, chrc->notify_id); queue_destroy(chrc->reg_notify_queue, notify_data_unref); free(chrc); } static void chrc_removed(struct gatt_db_attribute *attr, void *user_data) { struct notify_chrc *chrc = user_data; struct bt_gatt_client *client = chrc->client; struct notify_data *data; chrc->notify_id = 0; while ((data = queue_remove_if(client->notify_list, match_notify_chrc, chrc))) notify_data_cleanup(data); queue_remove(client->notify_chrcs, chrc); notify_chrc_free(chrc); } static struct notify_chrc *notify_chrc_create(struct bt_gatt_client *client, uint16_t handle) { struct gatt_db_attribute *attr, *ccc; struct notify_chrc *chrc; uint16_t value_handle; uint8_t properties; /* Check that there is an attribute with handle */ attr = gatt_db_get_attribute(client->db, handle); if (!attr) return NULL; if (!gatt_db_attribute_get_char_data(attr, NULL, &value_handle, &properties, NULL, NULL)) return NULL; /* Check that there is an attribute with value_handle */ attr = gatt_db_get_attribute(client->db, value_handle); if (!attr) return NULL; chrc = new0(struct notify_chrc, 1); chrc->reg_notify_queue = queue_new(); if (!chrc->reg_notify_queue) { free(chrc); return NULL; } ccc = gatt_db_attribute_get_ccc(attr); if (ccc) chrc->ccc_handle = gatt_db_attribute_get_handle(ccc); chrc->client = client; chrc->attr = attr; chrc->value_handle = value_handle; chrc->properties = properties; chrc->notify_id = gatt_db_attribute_register(attr, chrc_removed, chrc, NULL); queue_push_tail(client->notify_chrcs, chrc); return chrc; } static bool match_notify_data_id(const void *a, const void *b) { const struct notify_data *notify_data = a; unsigned int id = PTR_TO_UINT(b); return notify_data->id == id; } struct handle_range { uint16_t start; uint16_t end; }; struct discovery_op; typedef void (*discovery_op_complete_func_t)(struct discovery_op *op, bool success, uint8_t att_ecode); typedef void (*discovery_op_fail_func_t)(struct discovery_op *op); struct discovery_op { struct bt_gatt_client *client; struct queue *discov_ranges; struct queue *pending_svcs; struct queue *pending_chrcs; struct queue *ext_prop_desc; struct gatt_db_attribute *cur_svc; struct gatt_db_attribute *hash; uint8_t server_feat; bool success; uint16_t start; uint16_t end; uint16_t last; uint16_t svc_first; uint16_t svc_last; unsigned int db_id; int ref_count; discovery_op_complete_func_t complete_func; discovery_op_fail_func_t failure_func; }; static void discovery_op_free(struct discovery_op *op) { if (op->db_id > 0) gatt_db_unregister(op->client->db, op->db_id); queue_destroy(op->discov_ranges, free); queue_destroy(op->pending_svcs, NULL); queue_destroy(op->pending_chrcs, free); queue_destroy(op->ext_prop_desc, NULL); free(op); } static bool read_db_hash(struct discovery_op *op); static void gatt_log_va(struct bt_gatt_client *client, const char *format, va_list va) { if (!client || !format) return; if (client->debug_callback) util_debug_va(client->debug_callback, client->debug_data, format, va); else gatt_log_va(client->parent, format, va); } static void gatt_log(struct bt_gatt_client *client, const char *format, ...) { va_list ap; if (!client || !format) return; va_start(ap, format); gatt_log_va(client, format, ap); va_end(ap); } static void discovery_op_complete(struct discovery_op *op, bool success, uint8_t err) { const struct queue_entry *svc; op->success = success; /* Read database hash if discovery has been successful */ if (success && read_db_hash(op)) return; /* * Unregister remove callback so it is not called when clearing unused * range. */ gatt_db_unregister(op->client->db, op->db_id); op->db_id = 0; /* Remove services pending */ for (svc = queue_get_entries(op->pending_svcs); svc; svc = svc->next) { struct gatt_db_attribute *attr = svc->data; uint16_t start, end; /* Leave active services if operation was aborted */ if ((!success && err == 0) && gatt_db_service_get_active(attr)) continue; gatt_db_attribute_get_service_data(attr, &start, &end, NULL, NULL); DBG(op->client, "service disappeared: start 0x%04x end 0x%04x", start, end); gatt_db_remove_service(op->client->db, attr); } /* Reset remaining range */ if (op->last != UINT16_MAX) gatt_db_clear_range(op->client->db, op->last + 1, UINT16_MAX); op->complete_func(op, success, err); } static void discovery_load_services(struct gatt_db_attribute *attr, void *user_data) { struct discovery_op *op = user_data; queue_push_tail(op->pending_svcs, attr); } static void discovery_service_changed(struct gatt_db_attribute *attr, void *user_data) { struct discovery_op *op = user_data; queue_remove(op->pending_svcs, attr); } static struct discovery_op *discovery_op_create(struct bt_gatt_client *client, uint16_t start, uint16_t end, discovery_op_complete_func_t complete_func, discovery_op_fail_func_t failure_func) { struct discovery_op *op; struct handle_range *range; op = new0(struct discovery_op, 1); op->discov_ranges = queue_new(); op->pending_svcs = queue_new(); op->pending_chrcs = queue_new(); op->ext_prop_desc = queue_new(); op->client = client; op->complete_func = complete_func; op->failure_func = failure_func; op->start = start; op->end = end; op->last = gatt_db_isempty(client->db) ? 0 : UINT16_MAX; op->svc_first = UINT16_MAX; op->svc_last = 0; /* Load existing services as pending */ gatt_db_foreach_service_in_range(client->db, NULL, discovery_load_services, op, start, end); /* * Services are only added when set active in which case they are no * longer pending so it is safe to remove either way. */ op->db_id = gatt_db_register(client->db, discovery_service_changed, discovery_service_changed, op, NULL); range = new0(struct handle_range, 1); range->start = start; range->end = end; queue_push_tail(op->discov_ranges, range); return op; } static struct discovery_op *discovery_op_ref(struct discovery_op *op) { __sync_fetch_and_add(&op->ref_count, 1); return op; } static void discovery_op_unref(void *data) { struct discovery_op *op = data; if (__sync_sub_and_fetch(&op->ref_count, 1)) return; if (!op->success && op->failure_func) op->failure_func(op); discovery_op_free(op); } static void discovery_req_clear(struct bt_gatt_client *client) { if (!client->discovery_req) return; bt_gatt_request_unref(client->discovery_req); client->discovery_req = NULL; } static void discover_remove_pending(struct discovery_op *op, struct gatt_db_attribute *attr) { struct gatt_db_attribute *svc; svc = gatt_db_attribute_get_service(attr); if (!svc) return; if (!queue_remove(op->pending_svcs, svc)) return; gatt_db_service_set_active(svc, true); if (op->cur_svc == svc) op->cur_svc = NULL; } static void discover_chrcs_cb(bool success, uint8_t att_ecode, struct bt_gatt_result *result, void *user_data); static void discover_incl_cb(bool success, uint8_t att_ecode, struct bt_gatt_result *result, void *user_data) { struct discovery_op *op = user_data; struct bt_gatt_client *client = op->client; struct bt_gatt_iter iter; struct gatt_db_attribute *attr; uint16_t handle, start, end; uint128_t u128; bt_uuid_t uuid; char uuid_str[MAX_LEN_UUID_STR]; unsigned int includes_count, i; struct handle_range *range; discovery_req_clear(client); if (!success) { if (att_ecode == BT_ATT_ERROR_ATTRIBUTE_NOT_FOUND) goto next; goto failed; } if (!result || !bt_gatt_iter_init(&iter, result)) goto failed; includes_count = bt_gatt_result_included_count(result); if (includes_count == 0) goto failed; DBG(client, "Included services found: %u", includes_count); for (i = 0; i < includes_count; i++) { if (!bt_gatt_iter_next_included_service(&iter, &handle, &start, &end, u128.data)) break; bt_uuid128_create(&uuid, u128); /* Log debug message */ bt_uuid_to_string(&uuid, uuid_str, sizeof(uuid_str)); DBG(client, "handle: 0x%04x, start: 0x%04x, end: 0x%04x," "uuid: %s", handle, start, end, uuid_str); attr = gatt_db_get_attribute(client->db, start); if (!attr) { DBG(client, "Unable to find attribute at 0x%04x: skipping", start); continue; } attr = gatt_db_insert_included(client->db, handle, attr); if (!attr) { DBG(client, "Unable to add include attribute at 0x%04x", handle); goto failed; } /* * GATT requires that all include definitions precede * characteristic declarations. Based on the order we're adding * these entries, the correct handle must be assigned to the new * attribute. */ if (gatt_db_attribute_get_handle(attr) != handle) { DBG(client, "Invalid attribute 0x%04x expect it at 0x%04x", gatt_db_attribute_get_handle(attr), handle); goto failed; } if (!gatt_db_attribute_get_service_data(attr, NULL, &end, NULL, NULL)) { DBG(client, "Unable to get service data at 0x%04x", handle); goto failed; } /* Skip if there are no attributes */ if (handle == end) discover_remove_pending(op, attr); } next: range = queue_pop_head(op->discov_ranges); if (!range) { /* Skip if there are no attributes */ discover_remove_pending(op, op->cur_svc); goto failed; } client->discovery_req = bt_gatt_discover_characteristics(client->att, range->start, range->end, discover_chrcs_cb, discovery_op_ref(op), discovery_op_unref); free(range); if (client->discovery_req) return; DBG(client, "Failed to start characteristic discovery"); discovery_op_unref(op); failed: discovery_op_complete(op, false, att_ecode); } struct chrc { uint16_t start_handle; uint16_t end_handle; uint16_t value_handle; uint8_t properties; bt_uuid_t uuid; }; static void discover_descs_cb(bool success, uint8_t att_ecode, struct bt_gatt_result *result, void *user_data); static bool discover_descs(struct discovery_op *op, bool *discovering) { struct bt_gatt_client *client = op->client; struct gatt_db_attribute *attr; struct chrc *chrc_data; uint16_t desc_start; *discovering = false; while ((chrc_data = queue_pop_head(op->pending_chrcs))) { struct gatt_db_attribute *svc; uint16_t start, end; /* Adjust current service */ svc = gatt_db_get_service(client->db, chrc_data->value_handle); if (op->cur_svc != svc) { if (op->cur_svc) { queue_remove(op->pending_svcs, op->cur_svc); /* Done with the current service */ gatt_db_service_set_active(op->cur_svc, true); } op->cur_svc = svc; } attr = gatt_db_insert_characteristic(client->db, chrc_data->start_handle, chrc_data->value_handle, &chrc_data->uuid, 0, chrc_data->properties, NULL, NULL, NULL); if (!attr) { DBG(client, "Failed to insert characteristic at 0x%04x", chrc_data->value_handle); /* Some devices have been seen reporting orphaned * characteristics. In order to favor interoperability * we skip over characteristics in error */ free(chrc_data); continue; } if (gatt_db_attribute_get_handle(attr) != chrc_data->value_handle) goto failed; gatt_db_attribute_get_service_handles(svc, &start, &end); /* * Adjust end_handle in case the next chrc is not within the * same service. */ if (chrc_data->end_handle > end) chrc_data->end_handle = end; /* * check for descriptors presence, before initializing the * desc_handle and avoid integer overflow during desc_handle * initialization. */ if (chrc_data->value_handle >= chrc_data->end_handle) { free(chrc_data); continue; } desc_start = chrc_data->value_handle + 1; if (desc_start == chrc_data->end_handle && (chrc_data->properties & BT_GATT_CHRC_PROP_NOTIFY || chrc_data->properties & BT_GATT_CHRC_PROP_INDICATE)) { bt_uuid_t ccc_uuid; /* If there is only one descriptor that must be the CCC * in case either notify or indicate are supported. */ bt_uuid16_create(&ccc_uuid, GATT_CLIENT_CHARAC_CFG_UUID); attr = gatt_db_insert_descriptor(client->db, desc_start, &ccc_uuid, 0, NULL, NULL, NULL); if (attr) { free(chrc_data); continue; } } /* Check if the start range is within characteristic range */ if (desc_start > chrc_data->end_handle) { free(chrc_data); continue; } client->discovery_req = bt_gatt_discover_descriptors( client->att, desc_start, chrc_data->end_handle, discover_descs_cb, discovery_op_ref(op), discovery_op_unref); if (client->discovery_req) { *discovering = true; goto done; } DBG(client, "Failed to start descriptor discovery"); discovery_op_unref(op); goto failed; } /* Done with the current service */ discover_remove_pending(op, op->cur_svc); done: free(chrc_data); return true; failed: DBG(client, "Failed to discover descriptors"); free(chrc_data); return false; } static void ext_prop_write_cb(struct gatt_db_attribute *attrib, int err, void *user_data) { struct bt_gatt_client *client = user_data; DBG(client, "Value set status: %d", err); } static void ext_prop_read_cb(bool success, uint8_t att_ecode, const uint8_t *value, uint16_t length, void *user_data); static bool read_ext_prop_desc(struct discovery_op *op) { struct bt_gatt_client *client = op->client; uint16_t handle; struct gatt_db_attribute *attr; attr = queue_peek_head(op->ext_prop_desc); if (!attr) return false; handle = gatt_db_attribute_get_handle(attr); if (!bt_gatt_client_read_value(client, handle, ext_prop_read_cb, discovery_op_ref(op), discovery_op_unref)) return false; return true; } static void ext_prop_read_cb(bool success, uint8_t att_ecode, const uint8_t *value, uint16_t length, void *user_data) { struct discovery_op *op = user_data; struct bt_gatt_client *client = op->client; bool discovering; struct gatt_db_attribute *desc_attr = NULL; if (!success) goto done; DBG(client, "Ext. prop value: 0x%04x", (uint16_t)value[0]); desc_attr = queue_pop_head(op->ext_prop_desc); if (!desc_attr) goto failed; if (!gatt_db_attribute_write(desc_attr, 0, value, length, 0, NULL, ext_prop_write_cb, client)) goto failed; /* Any other descriptor to read? */ if (read_ext_prop_desc(op)) return; if (!discover_descs(op, &discovering)) goto failed; if (discovering) return; goto done; failed: success = false; done: discovery_op_complete(op, success, att_ecode); } static void discover_descs_cb(bool success, uint8_t att_ecode, struct bt_gatt_result *result, void *user_data) { struct discovery_op *op = user_data; struct bt_gatt_client *client = op->client; struct bt_gatt_iter iter; struct gatt_db_attribute *attr; uint16_t handle; uint128_t u128; bt_uuid_t uuid; char uuid_str[MAX_LEN_UUID_STR]; unsigned int desc_count; bool discovering; bt_uuid_t ext_prop_uuid; discovery_req_clear(client); if (!success) { if (att_ecode == BT_ATT_ERROR_ATTRIBUTE_NOT_FOUND) { success = true; goto next; } goto done; } if (!result || !bt_gatt_iter_init(&iter, result)) goto failed; desc_count = bt_gatt_result_descriptor_count(result); if (desc_count == 0) goto failed; DBG(client, "Descriptors found: %u", desc_count); bt_uuid16_create(&ext_prop_uuid, GATT_CHARAC_EXT_PROPER_UUID); while (bt_gatt_iter_next_descriptor(&iter, &handle, u128.data)) { bt_uuid128_create(&uuid, u128); /* Log debug message */ bt_uuid_to_string(&uuid, uuid_str, sizeof(uuid_str)); DBG(client, "handle: 0x%04x, uuid: %s", handle, uuid_str); attr = gatt_db_insert_descriptor(client->db, handle, &uuid, 0, NULL, NULL, NULL); if (!attr) { attr = gatt_db_get_attribute(client->db, handle); if (attr && !bt_uuid_cmp(&uuid, gatt_db_attribute_get_type(attr))) continue; DBG(client, "Failed to insert descriptor at 0x%04x", handle); goto failed; } if (gatt_db_attribute_get_handle(attr) != handle) goto failed; if (!bt_uuid_cmp(&ext_prop_uuid, &uuid)) queue_push_tail(op->ext_prop_desc, attr); } /* If we got extended prop descriptor, lets read it right away */ if (read_ext_prop_desc(op)) return; next: if (!discover_descs(op, &discovering)) goto failed; if (discovering) return; goto done; failed: success = false; done: discovery_op_complete(op, success, att_ecode); } static void discover_chrcs_cb(bool success, uint8_t att_ecode, struct bt_gatt_result *result, void *user_data) { struct discovery_op *op = user_data; struct bt_gatt_client *client = op->client; struct bt_gatt_iter iter; struct chrc *chrc_data; uint16_t start, end, value; uint8_t properties; uint128_t u128; bt_uuid_t uuid; char uuid_str[MAX_LEN_UUID_STR]; unsigned int chrc_count; bool discovering; discovery_req_clear(client); if (!success) { if (att_ecode == BT_ATT_ERROR_ATTRIBUTE_NOT_FOUND) { success = true; goto next; } goto done; } if (!result || !bt_gatt_iter_init(&iter, result)) goto failed; chrc_count = bt_gatt_result_characteristic_count(result); DBG(client, "Characteristics found: %u", chrc_count); if (chrc_count == 0) goto failed; while (bt_gatt_iter_next_characteristic(&iter, &start, &end, &value, &properties, u128.data)) { bt_uuid128_create(&uuid, u128); /* Log debug message */ bt_uuid_to_string(&uuid, uuid_str, sizeof(uuid_str)); DBG(client, "start: 0x%04x, end: 0x%04x, value: 0x%04x, " "props: 0x%02x, uuid: %s", start, end, value, properties, uuid_str); chrc_data = new0(struct chrc, 1); chrc_data->start_handle = start; chrc_data->end_handle = end; chrc_data->value_handle = value; chrc_data->properties = properties; chrc_data->uuid = uuid; queue_push_tail(op->pending_chrcs, chrc_data); } next: /* * Before attempting to process discovered characteristics make sure we * discovered all missing ranges. */ if (queue_length(op->discov_ranges)) { struct handle_range *range; range = queue_peek_head(op->discov_ranges); if (!range) goto failed; client->discovery_req = bt_gatt_discover_included_services(client->att, range->start, range->end, discover_incl_cb, discovery_op_ref(op), discovery_op_unref); if (client->discovery_req) return; DBG(client, "Failed to start included services discovery"); discovery_op_unref(op); goto failed; } /* * Sequentially discover descriptors for each characteristic and insert * the characteristics into the database as we proceed. */ if (!discover_descs(op, &discovering)) goto failed; if (discovering) return; goto done; failed: success = false; done: discovery_op_complete(op, success, att_ecode); } static bool match_handle_range(const void *data, const void *match_data) { const struct handle_range *range = data; const struct handle_range *match_range = match_data; return (match_range->start >= range->start) && (match_range->start <= range->end); } static struct handle_range *range_new(uint16_t start, uint16_t end) { struct handle_range *range; if (!start || !end || start > end) return NULL; range = new0(struct handle_range, 1); range->start = start; range->end = end; return range; } static void remove_discov_range(struct discovery_op *op, uint16_t start, uint16_t end) { struct handle_range match_range; struct handle_range *range, *new_range; match_range.start = start; match_range.end = end; range = queue_find(op->discov_ranges, match_handle_range, &match_range); if (!range) return; if ((range->start == start) && (range->end == end)) { queue_remove(op->discov_ranges, range); free(range); } else if (range->start == start) { range->start = end + 1; if (!range->start || range->start > range->end) { queue_remove(op->discov_ranges, range); free(range); } } else if (range->end == end) range->end = start - 1; else { new_range = range_new(end + 1, range->end); if (new_range) queue_push_after(op->discov_ranges, range, new_range); range->end = start - 1; } } static void discovery_found_service(struct discovery_op *op, struct gatt_db_attribute *attr, uint16_t start, uint16_t end) { /* Skip if service already active */ if (!gatt_db_service_get_active(attr)) { /* Skip if there are no attributes */ if (end == start) gatt_db_service_set_active(attr, true); else queue_push_tail(op->pending_svcs, attr); if (start < op->svc_first) op->svc_first = start; if (end > op->svc_last) op->svc_last = end; } else { /* Remove from pending if active */ queue_remove(op->pending_svcs, attr); remove_discov_range(op, start, end); } /* Update last handle */ if (end > op->last) op->last = end; } static bool discovery_parse_services(struct discovery_op *op, bool primary, struct bt_gatt_iter *iter) { struct bt_gatt_client *client = op->client; struct gatt_db_attribute *attr; uint16_t start, end; uint128_t u128; bt_uuid_t uuid; char uuid_str[MAX_LEN_UUID_STR]; while (bt_gatt_iter_next_service(iter, &start, &end, u128.data)) { bt_uuid128_create(&uuid, u128); /* Log debug message */ bt_uuid_to_string(&uuid, uuid_str, sizeof(uuid_str)); DBG(client, "start: 0x%04x, end: 0x%04x, uuid: %s", start, end, uuid_str); /* Store the service */ attr = gatt_db_insert_service(client->db, start, &uuid, primary, end - start + 1); if (!attr) { gatt_db_clear_range(client->db, start, end); attr = gatt_db_insert_service(client->db, start, &uuid, false, end - start + 1); if (!attr) { DBG(client, "Failed to store service"); return false; } /* Database has changed adjust last handle */ op->last = end; } /* Update pending list */ discovery_found_service(op, attr, start, end); } return true; } static void discover_secondary_cb(bool success, uint8_t att_ecode, struct bt_gatt_result *result, void *user_data) { struct discovery_op *op = user_data; struct bt_gatt_client *client = op->client; struct bt_gatt_iter iter; struct handle_range *range; discovery_req_clear(client); if (!success) { switch (att_ecode) { case BT_ATT_ERROR_ATTRIBUTE_NOT_FOUND: case BT_ATT_ERROR_UNSUPPORTED_GROUP_TYPE: success = true; att_ecode = 0; goto next; default: DBG(client, "Secondary service discovery failed." " ATT ECODE: 0x%02x", att_ecode); goto done; } } if (!result || !bt_gatt_iter_init(&iter, result)) { success = false; goto done; } DBG(client, "Secondary services found: %u", bt_gatt_result_service_count(result)); if (!discovery_parse_services(op, false, &iter)) { success = false; goto done; } next: if (queue_isempty(op->pending_svcs) || queue_isempty(op->discov_ranges)) goto done; if (op->svc_first > 0x0001) remove_discov_range(op, 1, op->svc_first - 1); if (op->svc_last < 0xffff) remove_discov_range(op, op->svc_last + 1, 0xffff); range = queue_peek_head(op->discov_ranges); if (range) client->discovery_req = bt_gatt_discover_included_services( client->att, range->start, range->end, discover_incl_cb, discovery_op_ref(op), discovery_op_unref); if (client->discovery_req) return; DBG(client, "Failed to start included services discovery"); discovery_op_unref(op); success = false; done: discovery_op_complete(op, success, att_ecode); } static void discover_primary_cb(bool success, uint8_t att_ecode, struct bt_gatt_result *result, void *user_data) { struct discovery_op *op = user_data; struct bt_gatt_client *client = op->client; struct bt_gatt_iter iter; discovery_req_clear(client); if (!success) { /* Reset error in case of not found */ switch (att_ecode) { case BT_ATT_ERROR_ATTRIBUTE_NOT_FOUND: success = true; att_ecode = 0; goto secondary; default: DBG(client, "Primary service discovery failed." " ATT ECODE: 0x%02x", att_ecode); goto done; } } if (!result || !bt_gatt_iter_init(&iter, result)) { success = false; goto done; } DBG(client, "Primary services found: %u", bt_gatt_result_service_count(result)); if (!discovery_parse_services(op, true, &iter)) { success = false; goto done; } secondary: /* * Version 4.2 [Vol 1, Part A] page 101: * A secondary service is a service that provides auxiliary * functionality of a device and is referenced from at least one * primary service on the device. */ if (queue_isempty(op->pending_svcs)) goto done; /* Discover secondary services */ client->discovery_req = bt_gatt_discover_secondary_services(client->att, NULL, op->start, op->end, discover_secondary_cb, discovery_op_ref(op), discovery_op_unref); if (client->discovery_req) return; DBG(client, "Failed to start secondary service discovery"); discovery_op_unref(op); success = false; done: discovery_op_complete(op, success, att_ecode); } static void ready_destroy(void *data) { struct ready_cb *ready = data; if (ready->destroy) ready->destroy(ready->data); free(ready); } static void notify_client_ready(struct bt_gatt_client *client, bool success, uint8_t att_ecode) { const struct queue_entry *entry; client = bt_gatt_client_ref_safe(client); if (!client) return; if (client->ready) goto done; client->ready = success; if (client->parent) client->features = client->parent->features; for (entry = queue_get_entries(client->ready_cbs); entry; entry = entry->next) { struct ready_cb *ready = entry->data; ready->callback(success, att_ecode, ready->data); } queue_remove_all(client->ready_cbs, NULL, NULL, ready_destroy); /* Notify clones */ for (entry = queue_get_entries(client->clones); entry; entry = entry->next) { struct bt_gatt_client *clone = entry->data; notify_client_ready(clone, success, att_ecode); } done: bt_gatt_client_unref(client); } static void discover_all(struct discovery_op *op) { struct bt_gatt_client *client = op->client; client->discovery_req = bt_gatt_discover_all_primary_services( client->att, NULL, discover_primary_cb, discovery_op_ref(op), discovery_op_unref); if (client->discovery_req) return; DBG(client, "Failed to initiate primary service discovery"); client->in_init = false; notify_client_ready(client, false, BT_ATT_ERROR_UNLIKELY); discovery_op_unref(op); } static void db_hash_write_value_cb(struct gatt_db_attribute *attrib, int err, void *user_data) { struct bt_gatt_client *client = user_data; DBG(client, "Value set status: %d", err); } static void db_hash_read_value_cb(struct gatt_db_attribute *attrib, int err, const uint8_t *value, size_t length, void *user_data) { const uint8_t **hash = user_data; if (err || (length != 16)) return; *hash = value; } static void db_hash_read_cb(bool success, uint8_t att_ecode, struct bt_gatt_result *result, void *user_data) { struct discovery_op *op = user_data; struct bt_gatt_client *client = op->client; const uint8_t *hash = NULL, *value; uint16_t len, handle; struct bt_gatt_iter iter; if (!success) goto discover; bt_gatt_iter_init(&iter, result); bt_gatt_iter_next_read_by_type(&iter, &handle, &len, &value); DBG(client, "DB Hash found: handle 0x%04x length 0x%04x", handle, len); if (len != 16) goto discover; /* Read stored value in the db */ gatt_db_attribute_read(op->hash, 0, BT_ATT_OP_READ_REQ, NULL, db_hash_read_value_cb, &hash); /* Check if the has has changed since last time */ if (hash && !memcmp(hash, value, len)) { DBG(client, "DB Hash match: skipping discovery"); queue_remove_all(op->pending_svcs, NULL, NULL, NULL); discovery_op_complete(op, true, 0); return; } DBG(client, "DB Hash value:"); util_hexdump(' ', value, len, client->debug_callback, client->debug_data); /* Store ithe new hash in the db */ gatt_db_attribute_write(op->hash, 0, value, len, 0, NULL, db_hash_write_value_cb, client); discover: if (!op->success) { discover_all(op); return; } discovery_op_complete(op, true, 0); } static void get_first_attribute(struct gatt_db_attribute *attrib, void *user_data) { struct gatt_db_attribute **stored = user_data; if (*stored) return; *stored = attrib; } static bool read_db_hash(struct discovery_op *op) { struct bt_gatt_client *client = op->client; bt_uuid_t uuid; /* Check if hash was already been read or there are more services to * process. */ if (op->hash || !queue_isempty(client->svc_chngd_queue)) return false; bt_uuid16_create(&uuid, GATT_CHARAC_DB_HASH); gatt_db_find_by_type(client->db, 0x0001, 0xffff, &uuid, get_first_attribute, &op->hash); if (!op->hash) return false; if (!bt_gatt_read_by_type(client->att, 0x0001, 0xffff, &uuid, db_hash_read_cb, discovery_op_ref(op), discovery_op_unref)) { discovery_op_unref(op); return false; } return true; } static void db_server_feat_read(bool success, uint8_t att_ecode, struct bt_gatt_result *result, void *user_data) { struct discovery_op *op = user_data; struct bt_gatt_client *client = op->client; const uint8_t *value; uint16_t len, handle; struct bt_gatt_iter iter; if (!result) return; bt_gatt_iter_init(&iter, result); bt_gatt_iter_next_read_by_type(&iter, &handle, &len, &value); DBG(client, "Server Features found: handle 0x%04x " "length 0x%04x value 0x%02x", handle, len, value[0]); op->server_feat = value[0]; } static void server_feat_read_value(struct gatt_db_attribute *attrib, int err, const uint8_t *value, size_t length, void *user_data) { const uint8_t **feat = user_data; if (err) return; *feat = value; } static void read_server_feat(struct discovery_op *op) { struct bt_gatt_client *client = op->client; struct gatt_db_attribute *attr = NULL; const uint8_t *feat = NULL; bt_uuid_t uuid; bt_uuid16_create(&uuid, GATT_CHARAC_SERVER_FEAT); gatt_db_find_by_type(client->db, 0x0001, 0xffff, &uuid, get_first_attribute, &attr); if (attr) { /* Read stored value in the db */ gatt_db_attribute_read(attr, 0, BT_ATT_OP_READ_REQ, NULL, server_feat_read_value, &feat); if (feat) return; } if (!bt_gatt_read_by_type(client->att, 0x0001, 0xffff, &uuid, db_server_feat_read, discovery_op_ref(op), discovery_op_unref)) discovery_op_unref(op); } static void exchange_mtu_cb(bool success, uint8_t att_ecode, void *user_data) { struct discovery_op *op = user_data; struct bt_gatt_client *client = op->client; op->success = success; client->mtu_req_id = 0; if (!success) { DBG(client, "MTU Exchange failed. ATT ECODE: 0x%02x", att_ecode); /* * BLUETOOTH SPECIFICATION Version 4.2 [Vol 3, Part G] page 546 * If the Error Response is sent by the server with the Error * Code set to RequestNot Supported , the Attribute Opcode is * not supported and the default MTU shall be used. */ if (att_ecode == BT_ATT_ERROR_REQUEST_NOT_SUPPORTED) goto discover; client->in_init = false; notify_client_ready(client, success, att_ecode); return; } DBG(client, "MTU exchange complete, with MTU: %u", bt_att_get_mtu(client->att)); discover: read_server_feat(op); if (read_db_hash(op)) { op->success = false; return; } discover_all(op); } struct service_changed_op { struct bt_gatt_client *client; uint16_t start_handle; uint16_t end_handle; }; static void process_service_changed(struct bt_gatt_client *client, uint16_t start_handle, uint16_t end_handle); static void service_changed_cb(uint16_t value_handle, const uint8_t *value, uint16_t length, void *user_data); static void complete_notify_request(void *data) { struct notify_data *notify_data = data; notify_data->att_id = 0; if (notify_data->callback) notify_data->callback(notify_data->att_ecode, notify_data->user_data); } static bool notify_data_write_ccc(struct notify_data *notify_data, bool enable, bt_gatt_client_callback_t callback) { unsigned int att_id; uint16_t value = 0x0000; uint16_t properties = notify_data->chrc->properties; assert(notify_data->chrc->ccc_handle); if (enable) { /* Try to enable notifications or indications based on * whatever the characteristic supports. */ if (properties & BT_GATT_CHRC_PROP_NOTIFY) value = cpu_to_le16(0x0001); else if (properties & BT_GATT_CHRC_PROP_INDICATE) value = cpu_to_le16(0x0002); else return false; } att_id = bt_gatt_client_write_value(notify_data->client, notify_data->chrc->ccc_handle, (void *)&value, sizeof(value), callback, notify_data_ref(notify_data), notify_data_unref); notify_data->chrc->ccc_write_id = notify_data->att_id = att_id; return !!att_id; } static uint8_t process_error(const void *pdu, uint16_t length) { const struct bt_att_pdu_error_rsp *error_pdu; if (!pdu || length != sizeof(struct bt_att_pdu_error_rsp)) return BT_ATT_ERROR_UNLIKELY; error_pdu = pdu; return error_pdu->ecode; } static bool notify_set_ecode(const void *data, const void *match_data) { struct notify_data *notify_data = (void *)data; uint8_t ecode = PTR_TO_UINT(match_data); notify_data->att_ecode = ecode; return true; } static void enable_ccc_callback(bool success, uint8_t att_ecode, void *user_data) { struct notify_data *notify_data = user_data; assert(notify_data->chrc->ccc_write_id); notify_data->chrc->ccc_write_id = 0; bt_gatt_client_ref_safe(notify_data->client); notify_data->att_ecode = att_ecode; /* Notify for all remaining requests. */ complete_notify_request(notify_data); queue_remove_all(notify_data->chrc->reg_notify_queue, notify_set_ecode, UINT_TO_PTR(notify_data->att_ecode), complete_notify_request); bt_gatt_client_unref(notify_data->client); } static bool match_notify_chrc_value_handle(const void *a, const void *b) { const struct notify_chrc *chrc = a; uint16_t value_handle = PTR_TO_UINT(b); return chrc->value_handle == value_handle; } static unsigned int register_notify(struct bt_gatt_client *client, uint16_t handle, bt_gatt_client_register_callback_t callback, bt_gatt_client_notify_callback_t notify, void *user_data, bt_gatt_client_destroy_func_t destroy) { struct notify_data *notify_data; struct notify_chrc *chrc = NULL; /* Check if a characteristic ref count has been started already */ chrc = queue_find(client->notify_chrcs, match_notify_chrc_value_handle, UINT_TO_PTR(handle)); if (!chrc) { /* * Create an entry if the characteristic is known and has a CCC * descriptor. */ chrc = notify_chrc_create(client, handle); if (!chrc) { DBG(client, "Unable to locate characteristic at 0x%04x", handle); return 0; } } /* Fail if we've hit the maximum allowed notify sessions */ if (chrc->notify_count == INT_MAX) return 0; notify_data = new0(struct notify_data, 1); notify_data->client = client; notify_data->ref_count = 1; notify_data->chrc = chrc; notify_data->callback = callback; notify_data->notify = notify; notify_data->user_data = user_data; notify_data->destroy = destroy; /* Add the handler to the bt_gatt_client's general list */ queue_push_tail(client->notify_list, notify_data); /* Assign an ID to the handler. */ if (client->next_reg_id < 1) client->next_reg_id = 1; notify_data->id = client->next_reg_id++; /* Increment the per-characteristic ref count of notify handlers */ __sync_fetch_and_add(¬ify_data->chrc->notify_count, 1); /* * If a write to the CCC descriptor is in progress, then queue this * request. */ if (chrc->ccc_write_id) { queue_push_tail(chrc->reg_notify_queue, notify_data); return notify_data->id; } /* * If the ref count > 1, ccc handle cannot be found or registration * callback is not set consider notifications are already enabled. */ if (chrc->notify_count > 1 || !chrc->ccc_handle || !callback) { complete_notify_request(notify_data); return notify_data->id; } /* Write to the CCC descriptor */ if (!notify_data_write_ccc(notify_data, true, enable_ccc_callback)) { queue_remove(client->notify_list, notify_data); free(notify_data); return 0; } return notify_data->id; } static void service_changed_register_cb(uint16_t att_ecode, void *user_data) { bool success; struct bt_gatt_client *client = user_data; if (att_ecode) { DBG(client, "Failed to register handler for \"Service Changed\""); success = false; client->svc_chngd_ind_id = 0; goto done; } client->svc_chngd_registered = true; success = true; DBG(client, "Registered handler for \"Service Changed\": %u", client->svc_chngd_ind_id); done: notify_client_ready(client, success, att_ecode); } static bool register_service_changed(struct bt_gatt_client *client) { bt_uuid_t uuid; struct gatt_db_attribute *attr = NULL; bt_uuid16_create(&uuid, SVC_CHNGD_UUID); if (client->svc_chngd_ind_id) return true; gatt_db_find_by_type(client->db, 0x0001, 0xffff, &uuid, get_first_attribute, &attr); if (!attr) return true; /* * Register an indication handler for the "Service Changed" * characteristic and report ready only if the handler is registered * successfully. */ client->svc_chngd_ind_id = register_notify(client, gatt_db_attribute_get_handle(attr), service_changed_register_cb, service_changed_cb, client, NULL); return client->svc_chngd_ind_id ? true : false; } static void service_changed_complete(struct discovery_op *op, bool success, uint8_t att_ecode) { struct bt_gatt_client *client = op->client; struct service_changed_op *next_sc_op; uint16_t start_handle = op->start; uint16_t end_handle = op->end; const struct queue_entry *entry; client->in_svc_chngd = false; if (!success && att_ecode != BT_ATT_ERROR_ATTRIBUTE_NOT_FOUND) { DBG(client, "Failed to discover services within changed range - " "error: 0x%02x", att_ecode); gatt_db_clear_range(client->db, start_handle, end_handle); } /* Notify the upper layer of changed services */ if (client->svc_chngd_callback) client->svc_chngd_callback(start_handle, end_handle, client->svc_chngd_data); /* Notify clones */ for (entry = queue_get_entries(client->clones); entry; entry = entry->next) { struct bt_gatt_client *clone = entry->data; if (clone->svc_chngd_callback) clone->svc_chngd_callback(start_handle, end_handle, clone->svc_chngd_data); } /* Process any queued events */ next_sc_op = queue_pop_head(client->svc_chngd_queue); if (next_sc_op) { process_service_changed(client, next_sc_op->start_handle, next_sc_op->end_handle); free(next_sc_op); return; } if (register_service_changed(client)) return; DBG(client, "Failed to re-register handler for \"Service Changed\""); } static void service_changed_failure(struct discovery_op *op) { struct bt_gatt_client *client = op->client; gatt_db_clear_range(client->db, op->start, op->end); } static void process_service_changed(struct bt_gatt_client *client, uint16_t start_handle, uint16_t end_handle) { struct discovery_op *op; op = discovery_op_create(client, start_handle, end_handle, service_changed_complete, service_changed_failure); if (!op) goto fail; client->discovery_req = bt_gatt_discover_primary_services(client->att, NULL, start_handle, end_handle, discover_primary_cb, discovery_op_ref(op), discovery_op_unref); if (client->discovery_req) { client->in_svc_chngd = true; return; } discovery_op_free(op); fail: DBG(client, "Failed to initiate service discovery after Service Changed"); } static void service_changed_cb(uint16_t value_handle, const uint8_t *value, uint16_t length, void *user_data) { struct bt_gatt_client *client = user_data; struct service_changed_op *op; uint16_t start, end; if (length != 4) return; start = get_le16(value); end = get_le16(value + 2); if (start > end) { DBG(client, "Service Changed received with invalid handles"); return; } DBG(client, "Service Changed received - start: 0x%04x end: 0x%04x", start, end); if (!client->in_svc_chngd) { process_service_changed(client, start, end); return; } op = new0(struct service_changed_op, 1); op->start_handle = start; op->end_handle = end; queue_push_tail(client->svc_chngd_queue, op); } static void server_feat_write_value(struct gatt_db_attribute *attrib, int err, void *user_data) { struct bt_gatt_client *client = user_data; DBG(client, "Server Features Value set status: %d", err); } static void write_server_features(struct bt_gatt_client *client, uint8_t feat) { bt_uuid_t uuid; struct gatt_db_attribute *attr = NULL; bt_uuid16_create(&uuid, GATT_CHARAC_SERVER_FEAT); gatt_db_find_by_type(client->db, 0x0001, 0xffff, &uuid, get_first_attribute, &attr); if (!attr) return; /* Store value in the DB */ if (!gatt_db_attribute_write(attr, 0, &feat, sizeof(feat), 0, NULL, server_feat_write_value, client)) DBG(client, "Unable to store Server Features"); } static void write_client_features(struct bt_gatt_client *client) { bt_uuid_t uuid; struct gatt_db_attribute *attr = NULL; uint16_t handle; const uint8_t *feat = NULL; bt_uuid16_create(&uuid, GATT_CHARAC_CLI_FEAT); gatt_db_find_by_type(client->db, 0x0001, 0xffff, &uuid, get_first_attribute, &attr); if (!attr) return; handle = gatt_db_attribute_get_handle(attr); client->features |= BT_GATT_CHRC_CLI_FEAT_ROBUST_CACHING; bt_uuid16_create(&uuid, GATT_CHARAC_SERVER_FEAT); attr = NULL; gatt_db_find_by_type(client->db, 0x0001, 0xffff, &uuid, get_first_attribute, &attr); if (attr) { /* Read stored value in the db */ gatt_db_attribute_read(attr, 0, BT_ATT_OP_READ_REQ, NULL, server_feat_read_value, &feat); if (!(feat && feat[0] & BT_GATT_CHRC_SERVER_FEAT_EATT) || !(client->features & BT_GATT_CHRC_CLI_FEAT_EATT)) client->features &= ~BT_GATT_CHRC_CLI_FEAT_EATT; } client->features |= BT_GATT_CHRC_CLI_FEAT_NFY_MULTI; DBG(client, "Writing Client Features 0x%02x", client->features); bt_gatt_client_write_value(client, handle, &client->features, sizeof(client->features), NULL, NULL, NULL); } static void init_complete(struct discovery_op *op, bool success, uint8_t att_ecode) { struct bt_gatt_client *client = op->client; client->in_init = false; if (!success) goto fail; if (op->server_feat) write_server_features(client, op->server_feat); write_client_features(client); if (register_service_changed(client)) goto done; DBG(client, "Failed to register handler for \"Service Changed\""); success = false; fail: DBG(client, "Failed to initialize gatt-client"); op->success = false; done: notify_client_ready(client, success, att_ecode); } static bool gatt_client_init(struct bt_gatt_client *client, uint16_t mtu) { struct discovery_op *op; if (client->in_init || client->ready) return false; op = discovery_op_create(client, 0x0001, 0xffff, init_complete, NULL); if (!op) return false; /* * BLUETOOTH SPECIFICATION Version 4.2 [Vol 3, Part G] page 546: * * 4.3.1 Exchange MTU * * This sub-procedure shall not be used on a BR/EDR physical link since * the MTU size is negotiated using L2CAP channel configuration * procedures. */ if (bt_att_get_link_type(client->att) == BT_ATT_BREDR) goto discover; /* Check if MTU needs to be send */ mtu = MAX(BT_ATT_DEFAULT_LE_MTU, mtu); if (mtu == BT_ATT_DEFAULT_LE_MTU) goto discover; /* Configure the MTU */ client->mtu_req_id = bt_gatt_exchange_mtu(client->att, mtu, exchange_mtu_cb, discovery_op_ref(op), discovery_op_unref); if (!client->mtu_req_id) { discovery_op_free(op); return false; } client->in_init = true; return true; discover: read_server_feat(op); if (read_db_hash(op)) { op->success = false; goto done; } client->discovery_req = bt_gatt_discover_all_primary_services( client->att, NULL, discover_primary_cb, discovery_op_ref(op), discovery_op_unref); if (!client->discovery_req) { discovery_op_free(op); return false; } done: client->in_init = true; return true; } struct value_data { uint16_t handle; uint16_t len; const void *data; }; static void disable_ccc_callback(bool success, uint8_t att_ecode, void *user_data) { struct notify_data *notify_data = user_data; struct notify_data *next_data; assert(notify_data->chrc->ccc_write_id); notify_data->chrc->ccc_write_id = 0; /* This is a best effort procedure, so ignore errors and process any * queued requests. */ while (1) { next_data = queue_pop_head(notify_data->chrc->reg_notify_queue); if (!next_data || notify_data_write_ccc(next_data, true, enable_ccc_callback)) return; } } static void complete_unregister_notify(void *data) { struct notify_data *notify_data = data; /* * If a procedure to enable the CCC is still pending, then cancel it and * return. */ if (notify_data->att_id) { bt_att_cancel(notify_data->client->att, notify_data->att_id); notify_data->att_id = 0; goto done; } if (__sync_sub_and_fetch(¬ify_data->chrc->notify_count, 1) || !notify_data->chrc->ccc_handle) goto done; notify_data_write_ccc(notify_data, false, disable_ccc_callback); done: notify_data_unref(notify_data); } static void notify_handler(void *data, void *user_data) { struct notify_data *notify_data = data; struct value_data *value_data = user_data; if (notify_data->chrc->value_handle != value_data->handle) return; /* * Even if the notify data has a pending ATT request to write to the * CCC, there is really no reason not to notify the handlers. */ if (notify_data->notify) notify_data->notify(value_data->handle, value_data->data, value_data->len, notify_data->user_data); } static void notify_cb(struct bt_att_chan *chan, uint16_t mtu, uint8_t opcode, const void *pdu, uint16_t length, void *user_data) { struct bt_gatt_client *client = user_data; struct value_data data; bt_gatt_client_ref(client); if (queue_isempty(client->notify_list)) goto done; memset(&data, 0, sizeof(data)); if (opcode == BT_ATT_OP_HANDLE_NFY_MULT) { while (length >= 4) { data.handle = get_le16(pdu); length -= 2; pdu += 2; data.len = get_le16(pdu); length -= 2; pdu += 2; if (data.len > length) data.len = length; data.data = pdu; queue_foreach(client->notify_list, notify_handler, &data); length -= data.len; pdu += data.len; } } else { data.handle = get_le16(pdu); length -= 2; pdu += 2; data.len = length; data.data = pdu; queue_foreach(client->notify_list, notify_handler, &data); } done: if (opcode == BT_ATT_OP_HANDLE_IND && !client->parent) bt_att_chan_send(chan, BT_ATT_OP_HANDLE_CONF, NULL, 0, NULL, NULL, NULL); bt_gatt_client_unref(client); } static void bt_gatt_client_free(struct bt_gatt_client *client) { bt_gatt_client_cancel_all(client); queue_destroy(client->notify_chrcs, notify_chrc_free); queue_destroy(client->notify_list, notify_data_cleanup); queue_destroy(client->ready_cbs, ready_destroy); queue_destroy(client->idle_cbs, idle_destroy); if (client->debug_destroy) client->debug_destroy(client->debug_data); if (client->att) { bt_att_unregister_disconnect(client->att, client->disc_id); bt_att_unregister(client->att, client->nfy_id); bt_att_unregister(client->att, client->nfy_mult_id); bt_att_unregister(client->att, client->ind_id); bt_att_unref(client->att); } gatt_db_unref(client->db); queue_destroy(client->clones, NULL); queue_destroy(client->svc_chngd_queue, free); queue_destroy(client->long_write_queue, request_unref); queue_destroy(client->pending_requests, request_unref); if (client->parent) { queue_remove(client->parent->clones, client); bt_gatt_client_unref(client->parent); } free(client); } static void att_disconnect_cb(int err, void *user_data) { struct bt_gatt_client *client = user_data; bool in_init = client->in_init; client->disc_id = 0; bt_att_unref(client->att); client->att = NULL; client->in_init = false; client->ready = false; if (in_init) notify_client_ready(client, false, 0); } static struct bt_gatt_client *gatt_client_new(struct gatt_db *db, struct bt_att *att, uint8_t features) { struct bt_gatt_client *client; client = new0(struct bt_gatt_client, 1); client->disc_id = bt_att_register_disconnect(att, att_disconnect_cb, client, NULL); if (!client->disc_id) goto fail; client->clones = queue_new(); client->ready_cbs = queue_new(); client->idle_cbs = queue_new(); client->long_write_queue = queue_new(); client->svc_chngd_queue = queue_new(); client->notify_list = queue_new(); client->notify_chrcs = queue_new(); client->pending_requests = queue_new(); client->nfy_id = bt_att_register(att, BT_ATT_OP_HANDLE_NFY, notify_cb, client, NULL); if (!client->nfy_id) goto fail; client->nfy_mult_id = bt_att_register(att, BT_ATT_OP_HANDLE_NFY_MULT, notify_cb, client, NULL); if (!client->nfy_mult_id) goto fail; client->ind_id = bt_att_register(att, BT_ATT_OP_HANDLE_IND, notify_cb, client, NULL); if (!client->ind_id) goto fail; client->att = bt_att_ref(att); client->db = gatt_db_ref(db); client->features = features; return client; fail: bt_gatt_client_free(client); return NULL; } struct bt_gatt_client *bt_gatt_client_new(struct gatt_db *db, struct bt_att *att, uint16_t mtu, uint8_t features) { struct bt_gatt_client *client; if (!att || !db) return NULL; client = gatt_client_new(db, att, features); if (!client) return NULL; if (!gatt_client_init(client, mtu)) { bt_gatt_client_free(client); return NULL; } return bt_gatt_client_ref(client); } struct bt_gatt_client *bt_gatt_client_clone(struct bt_gatt_client *client) { struct bt_gatt_client *clone; if (!client) return NULL; clone = gatt_client_new(client->db, client->att, client->features); if (!clone) return NULL; queue_push_tail(client->clones, clone); /* * Reference the parent since the clones depend on it to propagate * service changed and ready callbacks. */ clone->parent = bt_gatt_client_ref(client); clone->ready = client->ready; return bt_gatt_client_ref(clone); } struct bt_gatt_client *bt_gatt_client_ref(struct bt_gatt_client *client) { if (!client) return NULL; __sync_fetch_and_add(&client->ref_count, 1); return client; } void bt_gatt_client_unref(struct bt_gatt_client *client) { if (!client) return; if (__sync_sub_and_fetch(&client->ref_count, 1)) return; bt_gatt_client_free(client); } bool bt_gatt_client_is_ready(struct bt_gatt_client *client) { return (client && client->ready); } unsigned int bt_gatt_client_ready_register(struct bt_gatt_client *client, bt_gatt_client_callback_t callback, void *user_data, bt_gatt_client_destroy_func_t destroy) { struct ready_cb *ready; if (!client) return 0; ready = new0(struct ready_cb, 1); ready->callback = callback; ready->destroy = destroy; ready->data = user_data; queue_push_tail(client->ready_cbs, ready); return PTR_TO_UINT(ready); } bool bt_gatt_client_ready_unregister(struct bt_gatt_client *client, unsigned int id) { struct ready_cb *ready = UINT_TO_PTR(id); if (queue_remove(client->ready_cbs, ready)) { ready_destroy(ready); return true; } return false; } bool bt_gatt_client_set_service_changed(struct bt_gatt_client *client, bt_gatt_client_service_changed_callback_t callback, void *user_data, bt_gatt_client_destroy_func_t destroy) { if (!client) return false; if (client->svc_chngd_destroy) client->svc_chngd_destroy(client->svc_chngd_data); client->svc_chngd_callback = callback; client->svc_chngd_destroy = destroy; client->svc_chngd_data = user_data; return true; } bool bt_gatt_client_set_debug(struct bt_gatt_client *client, bt_gatt_client_debug_func_t callback, void *user_data, bt_gatt_client_destroy_func_t destroy) { if (!client) return false; if (client->debug_destroy) client->debug_destroy(client->debug_data); client->debug_callback = callback; client->debug_destroy = destroy; client->debug_data = user_data; return true; } uint16_t bt_gatt_client_get_mtu(struct bt_gatt_client *client) { if (!client || !client->att) return 0; return bt_att_get_mtu(client->att); } struct bt_att *bt_gatt_client_get_att(struct bt_gatt_client *client) { if (!client) return NULL; return client->att; } struct gatt_db *bt_gatt_client_get_db(struct bt_gatt_client *client) { if (!client || !client->db) return NULL; return client->db; } uint8_t bt_gatt_client_get_features(struct bt_gatt_client *client) { if (!client) return 0; if (client->parent) return client->parent->features; return client->features; } static bool match_req_id(const void *a, const void *b) { const struct request *req = a; unsigned int id = PTR_TO_UINT(b); return req->id == id; } static void cancel_long_write_cb(uint8_t opcode, const void *pdu, uint16_t len, void *user_data) { struct bt_gatt_client *client = user_data; if (queue_isempty(client->long_write_queue)) client->in_long_write = false; } static bool cancel_long_write_req(struct bt_gatt_client *client, struct request *req) { uint8_t pdu = 0x00; /* * att_id == 0 means that request has been queued and no prepare write * has been sent so far.Let's just remove if from the queue. * Otherwise execute write needs to be send. */ if (!req->att_id) return queue_remove(client->long_write_queue, req); return !!bt_att_send(client->att, BT_ATT_OP_EXEC_WRITE_REQ, &pdu, sizeof(pdu), cancel_long_write_cb, client, NULL); } static void cancel_prep_write_cb(uint8_t opcode, const void *pdu, uint16_t len, void *user_data) { struct request *req = user_data; struct bt_gatt_client *client = req->client; client->reliable_write_session_id = 0; } static bool cancel_prep_write_session(struct bt_gatt_client *client, struct request *req) { uint8_t pdu = 0x00; return !!bt_att_send(client->att, BT_ATT_OP_EXEC_WRITE_REQ, &pdu, sizeof(pdu), cancel_prep_write_cb, req, request_unref); } static bool cancel_request(struct request *req) { req->removed = true; if (req->long_write) return cancel_long_write_req(req->client, req); if (req->prep_write) return cancel_prep_write_session(req->client, req); return bt_att_cancel(req->client->att, req->att_id); } bool bt_gatt_client_cancel(struct bt_gatt_client *client, unsigned int id) { struct request *req; if (!client || !id || !client->att) return false; req = queue_remove_if(client->pending_requests, match_req_id, UINT_TO_PTR(id)); if (!req) return false; return cancel_request(req); } static void cancel_pending(void *data) { cancel_request(data); } bool bt_gatt_client_cancel_all(struct bt_gatt_client *client) { if (!client || !client->att) return false; queue_remove_all(client->pending_requests, NULL, NULL, cancel_pending); if (client->discovery_req) { bt_gatt_request_cancel(client->discovery_req); bt_gatt_request_unref(client->discovery_req); client->discovery_req = NULL; } if (client->mtu_req_id) bt_att_cancel(client->att, client->mtu_req_id); return true; } struct read_op { bt_gatt_client_read_callback_t callback; void *user_data; bt_gatt_client_destroy_func_t destroy; }; static void destroy_read_op(void *data) { struct read_op *op = data; if (op->destroy) op->destroy(op->user_data); free(op); } static void read_cb(uint8_t opcode, const void *pdu, uint16_t length, void *user_data) { struct request *req = user_data; struct read_op *op = req->data; bool success; uint8_t att_ecode = 0; const uint8_t *value = NULL; uint16_t value_len = 0; if (opcode == BT_ATT_OP_ERROR_RSP) { success = false; att_ecode = process_error(pdu, length); goto done; } if (opcode != BT_ATT_OP_READ_RSP || (!pdu && length)) { success = false; goto done; } success = true; value_len = length; if (value_len) value = pdu; done: if (op->callback) op->callback(success, att_ecode, value, length, op->user_data); } unsigned int bt_gatt_client_read_value(struct bt_gatt_client *client, uint16_t value_handle, bt_gatt_client_read_callback_t callback, void *user_data, bt_gatt_client_destroy_func_t destroy) { struct request *req; struct read_op *op; uint8_t pdu[2]; if (!client) return 0; op = new0(struct read_op, 1); req = request_create(client); if (!req) { free(op); return 0; } op->callback = callback; op->user_data = user_data; op->destroy = destroy; req->data = op; req->destroy = destroy_read_op; put_le16(value_handle, pdu); req->att_id = bt_att_send(client->att, BT_ATT_OP_READ_REQ, pdu, sizeof(pdu), read_cb, req, request_unref); if (!req->att_id) { op->destroy = NULL; request_unref(req); return 0; } return req->id; } static void read_multiple_cb(uint8_t opcode, const void *pdu, uint16_t length, void *user_data) { struct request *req = user_data; struct read_op *op = req->data; uint8_t att_ecode; bool success; if ((opcode != BT_ATT_OP_READ_MULT_RSP && opcode != BT_ATT_OP_READ_MULT_VL_RSP) || (!pdu && length)) { success = false; if (opcode == BT_ATT_OP_ERROR_RSP) att_ecode = process_error(pdu, length); else att_ecode = 0; pdu = NULL; length = 0; } else { success = true; att_ecode = 0; } if (!op->callback) return; if (opcode == BT_ATT_OP_READ_MULT_RSP || att_ecode) { op->callback(success, att_ecode, pdu, length, op->user_data); return; } if (length < 2) { op->callback(success, att_ecode, pdu, length, op->user_data); return; } /* Parse response */ while (length >= 2) { uint16_t len; len = get_le16(pdu); length -= 2; pdu += 2; /* The Length Value Tuple List may be truncated within the * first two octets of a tuple due to the size limits of the * current ATT_MTU. */ if (len > length) length = len; op->callback(success, att_ecode, pdu, len, op->user_data); } } unsigned int bt_gatt_client_read_multiple(struct bt_gatt_client *client, uint16_t *handles, uint8_t num_handles, bt_gatt_client_read_callback_t callback, void *user_data, bt_gatt_client_destroy_func_t destroy) { uint8_t *pdu = newa(uint8_t, num_handles * 2); struct request *req; struct read_op *op; uint8_t opcode; int i; if (!client) return 0; if (num_handles < 2) return 0; if (num_handles * 2 > bt_att_get_mtu(client->att) - 1) return 0; op = new0(struct read_op, 1); req = request_create(client); if (!req) { free(op); return 0; } op->callback = callback; op->user_data = user_data; op->destroy = destroy; req->data = op; req->destroy = destroy_read_op; for (i = 0; i < num_handles; i++) put_le16(handles[i], pdu + (2 * i)); opcode = bt_gatt_client_get_features(client) & BT_GATT_CHRC_CLI_FEAT_EATT ? BT_ATT_OP_READ_MULT_VL_REQ : BT_ATT_OP_READ_MULT_REQ; req->att_id = bt_att_send(client->att, opcode, pdu, num_handles * 2, read_multiple_cb, req, request_unref); if (!req->att_id) { op->destroy = NULL; request_unref(req); return 0; } return req->id; } struct read_long_op { struct bt_gatt_client *client; int ref_count; uint16_t value_handle; uint16_t offset; struct iovec iov; bt_gatt_client_read_callback_t callback; void *user_data; bt_gatt_client_destroy_func_t destroy; }; static void destroy_read_long_op(void *data) { struct read_long_op *op = data; if (op->destroy) op->destroy(op->user_data); free(op->iov.iov_base); free(op); } static bool append_chunk(struct read_long_op *op, const uint8_t *data, uint16_t len) { void *buf; /* Truncate if the data would exceed maximum length */ if (op->offset + len > BT_ATT_MAX_VALUE_LEN) len = BT_ATT_MAX_VALUE_LEN - op->offset; buf = realloc(op->iov.iov_base, op->iov.iov_len + len); if (!buf) return false; op->iov.iov_base = buf; memcpy(op->iov.iov_base + op->iov.iov_len, data, len); op->iov.iov_len += len; op->offset += len; return true; } static void read_long_cb(uint8_t opcode, const void *pdu, uint16_t length, void *user_data) { struct request *req = user_data; struct read_long_op *op = req->data; bool success; uint8_t att_ecode = 0; if (opcode == BT_ATT_OP_ERROR_RSP) { success = false; att_ecode = process_error(pdu, length); goto done; } if ((!op->offset && opcode != BT_ATT_OP_READ_RSP) || (op->offset && opcode != BT_ATT_OP_READ_BLOB_RSP) || (!pdu && length)) { success = false; goto done; } if (!length) goto success; if (!append_chunk(op, pdu, length)) { success = false; goto done; } if (op->offset >= BT_ATT_MAX_VALUE_LEN) goto success; if (length >= bt_att_get_mtu(op->client->att) - 1) { uint8_t pdu[4]; int err; put_le16(op->value_handle, pdu); put_le16(op->offset, pdu + 2); err = bt_att_resend(op->client->att, req->att_id, BT_ATT_OP_READ_BLOB_REQ, pdu, sizeof(pdu), read_long_cb, request_ref(req), request_unref); if (!err) return; request_unref(req); success = false; goto done; } success: success = true; done: if (op->callback) op->callback(success, att_ecode, op->iov.iov_base, op->iov.iov_len, op->user_data); } unsigned int bt_gatt_client_read_long_value(struct bt_gatt_client *client, uint16_t value_handle, uint16_t offset, bt_gatt_client_read_callback_t callback, void *user_data, bt_gatt_client_destroy_func_t destroy) { struct request *req; struct read_long_op *op; uint8_t att_op; uint8_t pdu[4]; uint16_t pdu_len; if (!client) return 0; op = new0(struct read_long_op, 1); req = request_create(client); if (!req) { free(op); return 0; } op->client = client; op->value_handle = value_handle; op->offset = offset; op->callback = callback; op->user_data = user_data; op->destroy = destroy; req->data = op; req->destroy = destroy_read_long_op; put_le16(value_handle, pdu); pdu_len = sizeof(value_handle); /* * Core v4.2, part F, section 1.3.4.4.5: * If the attribute value has a fixed length that is less than or equal * to (ATT_MTU - 3) octets in length, then an Error Response can be sent * with the error code «Attribute Not Long». * * To remove need for caller to handle "Attribute Not Long" error when * reading characteristics with short values, use Read Request for * reading first part of characteristics value instead of Read Blob * Request. Both are allowed in this case. */ if (op->offset) { att_op = BT_ATT_OP_READ_BLOB_REQ; pdu_len += sizeof(op->offset); put_le16(op->offset, pdu + 2); } else { att_op = BT_ATT_OP_READ_REQ; } req->att_id = bt_att_send(client->att, att_op, pdu, pdu_len, read_long_cb, req, request_unref); if (!req->att_id) { op->destroy = NULL; request_unref(req); return 0; } return req->id; } unsigned int bt_gatt_client_write_without_response( struct bt_gatt_client *client, uint16_t value_handle, bool signed_write, const uint8_t *value, uint16_t length) { uint8_t *pdu = newa(uint8_t, 2 + length); struct request *req; int security; uint8_t op; if (!client) return 0; req = request_create(client); if (!req) return 0; /* Only use signed write if unencrypted */ if (signed_write) { security = bt_att_get_security(client->att, NULL); op = security > BT_SECURITY_LOW ? BT_ATT_OP_WRITE_CMD : BT_ATT_OP_SIGNED_WRITE_CMD; } else op = BT_ATT_OP_WRITE_CMD; put_le16(value_handle, pdu); memcpy(pdu + 2, value, length); req->att_id = bt_att_send(client->att, op, pdu, 2 + length, NULL, req, request_unref); if (!req->att_id) { request_unref(req); return 0; } return req->id; } struct write_op { struct bt_gatt_client *client; bt_gatt_client_callback_t callback; void *user_data; bt_gatt_destroy_func_t destroy; }; static void destroy_write_op(void *data) { struct write_op *op = data; if (op->destroy) op->destroy(op->user_data); free(op); } static void write_cb(uint8_t opcode, const void *pdu, uint16_t length, void *user_data) { struct request *req = user_data; struct write_op *op = req->data; bool success = true; uint8_t att_ecode = 0; if (opcode == BT_ATT_OP_ERROR_RSP) { success = false; att_ecode = process_error(pdu, length); goto done; } if (opcode != BT_ATT_OP_WRITE_RSP || pdu || length) success = false; done: if (op->callback) op->callback(success, att_ecode, op->user_data); } unsigned int bt_gatt_client_write_value(struct bt_gatt_client *client, uint16_t value_handle, const uint8_t *value, uint16_t length, bt_gatt_client_callback_t callback, void *user_data, bt_gatt_client_destroy_func_t destroy) { struct request *req; struct write_op *op; uint8_t *pdu = newa(uint8_t, 2 + length); if (!client) return 0; op = new0(struct write_op, 1); req = request_create(client); if (!req) { free(op); return 0; } op->callback = callback; op->user_data = user_data; op->destroy = destroy; req->data = op; req->destroy = destroy_write_op; put_le16(value_handle, pdu); memcpy(pdu + 2, value, length); req->att_id = bt_att_send(client->att, BT_ATT_OP_WRITE_REQ, pdu, 2 + length, write_cb, req, request_unref); if (!req->att_id) { op->destroy = NULL; request_unref(req); return 0; } return req->id; } struct long_write_op { struct bt_gatt_client *client; bool reliable; bool success; uint8_t att_ecode; bool reliable_error; uint16_t value_handle; uint8_t *value; uint16_t length; uint16_t offset; uint16_t index; uint16_t cur_length; bt_gatt_client_write_long_callback_t callback; void *user_data; bt_gatt_client_destroy_func_t destroy; }; static void long_write_op_free(void *data) { struct long_write_op *op = data; if (op->destroy) op->destroy(op->user_data); free(op->value); free(op); } static void prepare_write_cb(uint8_t opcode, const void *pdu, uint16_t length, void *user_data); static void complete_write_long_op(struct request *req, bool success, uint8_t att_ecode, bool reliable_error); static void handle_next_prep_write(struct request *req) { struct long_write_op *op = req->data; bool success = true; uint8_t *pdu; int err; pdu = malloc(op->cur_length + 4); if (!pdu) { success = false; goto done; } put_le16(op->value_handle, pdu); put_le16(op->offset + op->index, pdu + 2); memcpy(pdu + 4, op->value + op->index, op->cur_length); err = bt_att_resend(op->client->att, req->att_id, BT_ATT_OP_PREP_WRITE_REQ, pdu, op->cur_length + 4, prepare_write_cb, request_ref(req), request_unref); if (err) { request_unref(req); success = false; } free(pdu); /* If so far successful, then the operation should continue. * Otherwise, there was an error and the procedure should be * completed. */ if (success) return; done: complete_write_long_op(req, success, 0, false); } static void start_next_long_write(struct bt_gatt_client *client) { struct request *req; if (queue_isempty(client->long_write_queue)) { client->in_long_write = false; return; } req = queue_pop_head(client->long_write_queue); if (!req) return; handle_next_prep_write(req); /* * send_next_prep_write adds an extra ref. Unref here to clean up if * necessary, since we also added a ref before pushing to the queue. */ request_unref(req); } static void execute_write_cb(uint8_t opcode, const void *pdu, uint16_t length, void *user_data) { struct request *req = user_data; struct long_write_op *op = req->data; bool success = op->success; uint8_t att_ecode = op->att_ecode; if (opcode == BT_ATT_OP_ERROR_RSP) { success = false; att_ecode = process_error(pdu, length); } else if (opcode != BT_ATT_OP_EXEC_WRITE_RSP || pdu || length) success = false; bt_gatt_client_ref(op->client); if (op->callback) op->callback(success, op->reliable_error, att_ecode, op->user_data); start_next_long_write(op->client); bt_gatt_client_unref(op->client); } static void complete_write_long_op(struct request *req, bool success, uint8_t att_ecode, bool reliable_error) { struct long_write_op *op = req->data; uint8_t pdu; int err; op->success = success; op->att_ecode = att_ecode; op->reliable_error = reliable_error; if (success) pdu = 0x01; /* Write */ else pdu = 0x00; /* Cancel */ err = bt_att_resend(op->client->att, req->att_id, BT_ATT_OP_EXEC_WRITE_REQ, &pdu, sizeof(pdu), execute_write_cb, request_ref(req), request_unref); if (!err) return; request_unref(req); success = false; bt_gatt_client_ref(op->client); if (op->callback) op->callback(success, reliable_error, att_ecode, op->user_data); start_next_long_write(op->client); bt_gatt_client_unref(op->client); } static void prepare_write_cb(uint8_t opcode, const void *pdu, uint16_t length, void *user_data) { struct request *req = user_data; struct long_write_op *op = req->data; bool success = true; bool reliable_error = false; uint8_t att_ecode = 0; uint16_t next_index; if (opcode == BT_ATT_OP_ERROR_RSP) { success = false; att_ecode = process_error(pdu, length); goto done; } if (opcode != BT_ATT_OP_PREP_WRITE_RSP) { success = false; goto done; } if (op->reliable) { if (!pdu || length != (op->cur_length + 4)) { success = false; reliable_error = true; goto done; } if (get_le16(pdu) != op->value_handle || get_le16(pdu + 2) != (op->offset + op->index)) { success = false; reliable_error = true; goto done; } if (memcmp(pdu + 4, op->value + op->index, op->cur_length)) { success = false; reliable_error = true; goto done; } } next_index = op->index + op->cur_length; if (next_index == op->length) { /* All bytes written */ goto done; } /* If the last written length was greater than or equal to what can fit * inside a PDU, then there is more data to send. */ if (op->cur_length >= bt_att_get_mtu(op->client->att) - 5) { op->index = next_index; op->cur_length = MIN(op->length - op->index, bt_att_get_mtu(op->client->att) - 5); handle_next_prep_write(req); return; } done: complete_write_long_op(req, success, att_ecode, reliable_error); } unsigned int bt_gatt_client_write_long_value(struct bt_gatt_client *client, bool reliable, uint16_t value_handle, uint16_t offset, const uint8_t *value, uint16_t length, bt_gatt_client_write_long_callback_t callback, void *user_data, bt_gatt_client_destroy_func_t destroy) { struct request *req; struct long_write_op *op; uint8_t *pdu; if (!client) return 0; if ((size_t)(length + offset) > UINT16_MAX) return 0; /* Don't allow writing a 0-length value using this procedure. The * upper-layer should use bt_gatt_write_value for that instead. */ if (!length || !value) return 0; op = new0(struct long_write_op, 1); op->value = malloc(length); if (!op->value) { free(op); return 0; } req = request_create(client); if (!req) { free(op->value); free(op); return 0; } memcpy(op->value, value, length); op->client = client; op->reliable = reliable; op->value_handle = value_handle; op->length = length; op->offset = offset; op->cur_length = MIN(length, bt_att_get_mtu(client->att) - 5); op->callback = callback; op->user_data = user_data; op->destroy = destroy; req->data = op; req->destroy = long_write_op_free; req->long_write = true; if (client->in_long_write || client->reliable_write_session_id > 0) { queue_push_tail(client->long_write_queue, req); return req->id; } pdu = malloc(op->cur_length + 4); if (!pdu) { free(op->value); free(op); return 0; } put_le16(value_handle, pdu); put_le16(offset, pdu + 2); memcpy(pdu + 4, op->value, op->cur_length); req->att_id = bt_att_send(client->att, BT_ATT_OP_PREP_WRITE_REQ, pdu, op->cur_length + 4, prepare_write_cb, req, request_unref); free(pdu); if (!req->att_id) { op->destroy = NULL; request_unref(req); return 0; } client->in_long_write = true; return req->id; } struct prep_write_op { bt_gatt_client_write_long_callback_t callback; void *user_data; bt_gatt_destroy_func_t destroy; uint8_t *pdu; uint16_t pdu_len; }; static void destroy_prep_write_op(void *data) { struct prep_write_op *op = data; if (op->destroy) op->destroy(op->user_data); free(op->pdu); free(op); } static void prep_write_cb(uint8_t opcode, const void *pdu, uint16_t length, void *user_data) { struct request *req = user_data; struct prep_write_op *op = req->data; bool success; uint8_t att_ecode; bool reliable_error; if (opcode == BT_ATT_OP_ERROR_RSP) { success = false; reliable_error = false; att_ecode = process_error(pdu, length); goto done; } if (opcode != BT_ATT_OP_PREP_WRITE_RSP) { success = false; reliable_error = false; att_ecode = 0; goto done; } if (!pdu || length != op->pdu_len || memcmp(pdu, op->pdu, op->pdu_len)) { success = false; reliable_error = true; att_ecode = 0; goto done; } success = true; reliable_error = false; att_ecode = 0; done: if (op->callback) op->callback(success, reliable_error, att_ecode, op->user_data); } static struct request *get_reliable_request(struct bt_gatt_client *client, unsigned int id) { struct request *req; struct prep_write_op *op; op = new0(struct prep_write_op, 1); /* Following prepare writes */ if (id != 0) req = queue_find(client->pending_requests, match_req_id, UINT_TO_PTR(id)); else req = request_create(client); if (!req) { free(op); return NULL; } req->data = op; return req; } unsigned int bt_gatt_client_prepare_write(struct bt_gatt_client *client, unsigned int id, uint16_t value_handle, uint16_t offset, const uint8_t *value, uint16_t length, bt_gatt_client_write_long_callback_t callback, void *user_data, bt_gatt_client_destroy_func_t destroy) { struct request *req; struct prep_write_op *op; uint8_t *pdu = newa(uint8_t, 4 + length); if (!client) return 0; if (client->in_long_write) return 0; /* * Make sure that client who owns reliable session continues with * prepare writes or this is brand new reliable session (id == 0) */ if (id != client->reliable_write_session_id) { DBG(client, "There is other reliable write session ongoing %u", client->reliable_write_session_id); return 0; } req = get_reliable_request(client, id); if (!req) return 0; op = (struct prep_write_op *)req->data; op->callback = callback; op->user_data = user_data; op->destroy = destroy; req->destroy = destroy_prep_write_op; req->prep_write = true; put_le16(value_handle, pdu); put_le16(offset, pdu + 2); memcpy(pdu + 4, value, length); /* * Before sending command we need to remember pdu as we need to validate * it in the response. Store handle, offset and value. Therefore * increase length by 4 (handle + offset) as we need it in couple places * below */ length += 4; op->pdu = malloc(length); if (!op->pdu) { op->destroy = NULL; request_unref(req); return 0; } memcpy(op->pdu, pdu, length); op->pdu_len = length; /* * Now we are ready to send command * Note that request_unref will be done on write execute */ req->att_id = bt_att_send(client->att, BT_ATT_OP_PREP_WRITE_REQ, pdu, length, prep_write_cb, req, NULL); if (!req->att_id) { op->destroy = NULL; request_unref(req); return 0; } /* * Store first request id for prepare write and treat it as a session id * valid until write execute is done */ if (client->reliable_write_session_id == 0) client->reliable_write_session_id = req->id; return client->reliable_write_session_id; } static void exec_write_cb(uint8_t opcode, const void *pdu, uint16_t length, void *user_data) { struct request *req = user_data; struct write_op *op = req->data; bool success; uint8_t att_ecode; if (opcode == BT_ATT_OP_ERROR_RSP) { success = false; att_ecode = process_error(pdu, length); goto done; } if (opcode != BT_ATT_OP_EXEC_WRITE_RSP || pdu || length) { success = false; att_ecode = 0; goto done; } success = true; att_ecode = 0; done: if (op->callback) op->callback(success, att_ecode, op->user_data); op->client->reliable_write_session_id = 0; start_next_long_write(op->client); } unsigned int bt_gatt_client_write_execute(struct bt_gatt_client *client, unsigned int id, bt_gatt_client_callback_t callback, void *user_data, bt_gatt_client_destroy_func_t destroy) { struct request *req; struct write_op *op; uint8_t pdu; if (!client) return 0; if (client->in_long_write) return 0; if (client->reliable_write_session_id != id) return 0; op = new0(struct write_op, 1); req = queue_find(client->pending_requests, match_req_id, UINT_TO_PTR(id)); if (!req) { free(op); return 0; } op->client = client; op->callback = callback; op->user_data = user_data; op->destroy = destroy; pdu = 0x01; req->data = op; req->destroy = destroy_write_op; req->att_id = bt_att_send(client->att, BT_ATT_OP_EXEC_WRITE_REQ, &pdu, sizeof(pdu), exec_write_cb, req, request_unref); if (!req->att_id) { op->destroy = NULL; request_unref(req); return 0; } return id; } unsigned int bt_gatt_client_register_notify(struct bt_gatt_client *client, uint16_t chrc_value_handle, bt_gatt_client_register_callback_t callback, bt_gatt_client_notify_callback_t notify, void *user_data, bt_gatt_client_destroy_func_t destroy) { if (!client || !client->db || !chrc_value_handle || (!callback && !notify)) return 0; if (client->in_svc_chngd) return 0; return register_notify(client, chrc_value_handle, callback, notify, user_data, destroy); } bool bt_gatt_client_unregister_notify(struct bt_gatt_client *client, unsigned int id) { struct notify_data *notify_data; if (!client || !id) return false; notify_data = queue_remove_if(client->notify_list, match_notify_data_id, UINT_TO_PTR(id)); if (!notify_data) return false; /* Remove data if it has been queued */ queue_remove(notify_data->chrc->reg_notify_queue, notify_data); /* Reset callbacks */ notify_data->callback = NULL; notify_data->notify = NULL; complete_unregister_notify(notify_data); return true; } bool bt_gatt_client_set_security(struct bt_gatt_client *client, int level) { if (!client) return false; return bt_att_set_security(client->att, level); } int bt_gatt_client_get_security(struct bt_gatt_client *client) { if (!client) return -1; return bt_att_get_security(client->att, NULL); } unsigned int bt_gatt_client_idle_register(struct bt_gatt_client *client, bt_gatt_client_idle_callback_t callback, void *user_data, bt_gatt_client_destroy_func_t destroy) { struct idle_cb *idle; if (!client) return 0; idle = new0(struct idle_cb, 1); idle->callback = callback; idle->destroy = destroy; idle->data = user_data; queue_push_tail(client->idle_cbs, idle); return PTR_TO_UINT(idle); } bool bt_gatt_client_idle_unregister(struct bt_gatt_client *client, unsigned int id) { struct idle_cb *idle = UINT_TO_PTR(id); if (!client || !id) return false; if (queue_remove(client->idle_cbs, idle)) { idle_destroy(idle); return true; } return false; } bool bt_gatt_client_set_retry(struct bt_gatt_client *client, unsigned int id, bool retry) { struct request *req; if (!client || !id) return false; req = queue_find(client->pending_requests, match_req_id, UINT_TO_PTR(id)); if (!req) return false; bt_att_set_retry(client->att, req->att_id, retry); return true; } bluez-5.82/src/shared/PaxHeaders/ccp.c0000644000000000000000000000005014572354773014635 xustar0020 atime=1743515933 20 ctime=1743591277 bluez-5.82/src/shared/ccp.c0000644000000000000000000007064514572354773014332 0ustar00rootroot// SPDX-License-Identifier: LGPL-2.1-or-later /* * * BlueZ - Bluetooth protocol stack for Linux * * Copyright (C) 2022 Intel Corporation. All rights reserved. * */ #define _GNU_SOURCE #include #include #include #include #include #include #include "lib/bluetooth.h" #include "lib/uuid.h" #include "lib/hci.h" #include "src/shared/queue.h" #include "src/shared/util.h" #include "src/shared/timeout.h" #include "src/shared/att.h" #include "src/shared/gatt-db.h" #include "src/shared/gatt-server.h" #include "src/shared/gatt-client.h" #include "src/shared/ccp.h" #define DBG(_ccp, fmt, arg...) \ ccp_debug(_ccp, "%s:%s() " fmt, __FILE__, __func__, ## arg) struct bt_ccp_db { struct gatt_db *db; struct bt_ccs *ccs; }; struct bt_ccp_pending { unsigned int id; struct bt_ccp *ccp; bt_gatt_client_read_callback_t func; void *user_data; }; struct event_callback { const struct bt_ccp_event_callback *cbs; void *user_data; }; struct bt_ccp { int ref_count; struct bt_gatt_client *client; struct bt_ccp_db *ldb; struct bt_ccp_db *rdb; unsigned int bearer_name_id; unsigned int bearer_uci_id; unsigned int bearer_technology_id; unsigned int bearer_uri_schemes_list_id; unsigned int signal_strength_id; unsigned int signal_reporting_intrvl_id; unsigned int current_call_list_id; unsigned int ccid_id; unsigned int status_flag_id; unsigned int target_bearer_uri_id; unsigned int call_state_id; unsigned int call_control_pt_id; unsigned int call_control_opt_opcode_id; unsigned int termination_reason_id; unsigned int incoming_call_id; unsigned int friendly_name_id; struct event_callback *cb; struct queue *pending; bt_ccp_debug_func_t debug_func; bt_ccp_destroy_func_t debug_destroy; void *debug_data; void *user_data; }; struct bt_ccs { struct bt_ccp_db *mdb; struct gatt_db_attribute *service; struct gatt_db_attribute *bearer_name; struct gatt_db_attribute *bearer_name_ccc; struct gatt_db_attribute *bearer_uci; struct gatt_db_attribute *bearer_technology; struct gatt_db_attribute *bearer_technology_ccc; struct gatt_db_attribute *bearer_uri_schemes_list; struct gatt_db_attribute *signal_strength; struct gatt_db_attribute *signal_strength_ccc; struct gatt_db_attribute *signal_reporting_intrvl; struct gatt_db_attribute *current_call_list; struct gatt_db_attribute *current_call_list_ccc; struct gatt_db_attribute *ccid; struct gatt_db_attribute *status_flag; struct gatt_db_attribute *status_flag_ccc; struct gatt_db_attribute *target_bearer_uri; struct gatt_db_attribute *call_state; struct gatt_db_attribute *call_state_ccc; struct gatt_db_attribute *call_ctrl_point; struct gatt_db_attribute *call_ctrl_point_ccc; struct gatt_db_attribute *call_ctrl_opt_opcode; struct gatt_db_attribute *termination_reason; struct gatt_db_attribute *termination_reason_ccc; struct gatt_db_attribute *incoming_call; struct gatt_db_attribute *incoming_call_ccc; struct gatt_db_attribute *friendly_name; struct gatt_db_attribute *friendly_name_ccc; }; static struct queue *ccp_db; static void ccp_debug(struct bt_ccp *ccp, const char *format, ...) { va_list ap; if (!ccp || !format || !ccp->debug_func) return; va_start(ap, format); util_debug_va(ccp->debug_func, ccp->debug_data, format, ap); va_end(ap); } static bool ccp_db_match(const void *data, const void *match_data) { const struct bt_ccp_db *mdb = data; const struct gatt_db *db = match_data; return (mdb->db == db); } static void ccp_db_free(void *data) { struct bt_ccp_db *bdb = data; if (!bdb) return; gatt_db_unref(bdb->db); free(bdb->ccs); free(bdb); } static void ccp_free(void *data) { struct bt_ccp *ccp = data; DBG(ccp, ""); bt_ccp_detach(ccp); ccp_db_free(ccp->rdb); queue_destroy(ccp->pending, NULL); free(ccp); } struct bt_ccp *bt_ccp_ref(struct bt_ccp *ccp) { if (!ccp) return NULL; __sync_fetch_and_add(&ccp->ref_count, 1); return ccp; } void bt_ccp_unref(struct bt_ccp *ccp) { if (!ccp) return; if (__sync_sub_and_fetch(&ccp->ref_count, 1)) return; ccp_free(ccp); } bool bt_ccp_set_user_data(struct bt_ccp *ccp, void *user_data) { if (!ccp) return false; ccp->user_data = user_data; return true; } void *bt_ccp_get_user_data(struct bt_ccp *ccp) { if (!ccp) return NULL; return ccp->user_data; } bool bt_ccp_set_debug(struct bt_ccp *ccp, bt_ccp_debug_func_t func, void *user_data, bt_ccp_destroy_func_t destroy) { if (!ccp) return false; if (ccp->debug_destroy) ccp->debug_destroy(ccp->debug_data); ccp->debug_func = func; ccp->debug_destroy = destroy; ccp->debug_data = user_data; return true; } static void ccs_call_state_read(struct gatt_db_attribute *attrib, unsigned int id, uint16_t offset, uint8_t opcode, struct bt_att *att, void *user_data) { int call_state = 0; struct iovec iov; iov.iov_base = &call_state; iov.iov_len = sizeof(int); gatt_db_attribute_read_result(attrib, id, 0, iov.iov_base, iov.iov_len); } static void ccs_call_state_write(struct gatt_db_attribute *attrib, unsigned int id, uint16_t offset, const uint8_t *value, size_t len, uint8_t opcode, struct bt_att *att, void *user_data) { gatt_db_attribute_write_result(attrib, id, BT_ATT_ERROR_INSUFFICIENT_RESOURCES); } static struct bt_ccs *ccs_new(struct gatt_db *db) { struct bt_ccs *ccs; bt_uuid_t uuid; if (!db) return NULL; ccs = new0(struct bt_ccs, 1); /* Populate DB with ccs attributes */ bt_uuid16_create(&uuid, GTBS_UUID); ccs->service = gatt_db_add_service(db, &uuid, true, 42); bt_uuid16_create(&uuid, BEARER_PROVIDER_NAME_CHRC_UUID); ccs->bearer_name = gatt_db_service_add_characteristic(ccs->service, &uuid, BT_ATT_PERM_READ, BT_GATT_CHRC_PROP_READ | BT_GATT_CHRC_PROP_NOTIFY, ccs_call_state_read, NULL, ccs); ccs->bearer_name_ccc = gatt_db_service_add_ccc(ccs->service, BT_ATT_PERM_READ | BT_ATT_PERM_WRITE); bt_uuid16_create(&uuid, BEARER_UCI_CHRC_UUID); ccs->bearer_uci = gatt_db_service_add_characteristic(ccs->service, &uuid, BT_ATT_PERM_READ, BT_GATT_CHRC_PROP_READ, ccs_call_state_read, NULL, ccs); bt_uuid16_create(&uuid, BEARER_TECH_CHRC_UUID); ccs->bearer_technology = gatt_db_service_add_characteristic(ccs->service, &uuid, BT_ATT_PERM_READ, BT_GATT_CHRC_PROP_READ | BT_GATT_CHRC_PROP_NOTIFY, ccs_call_state_read, NULL, ccs); ccs->bearer_technology_ccc = gatt_db_service_add_ccc(ccs->service, BT_ATT_PERM_READ | BT_ATT_PERM_WRITE); bt_uuid16_create(&uuid, BEARER_URI_SCHEME_CHRC_UUID); ccs->bearer_uri_schemes_list = gatt_db_service_add_characteristic(ccs->service, &uuid, BT_ATT_PERM_READ, BT_GATT_CHRC_PROP_READ, ccs_call_state_read, NULL, ccs); bt_uuid16_create(&uuid, BEARER_SIGNAL_STR_CHRC_UUID); ccs->signal_strength = gatt_db_service_add_characteristic(ccs->service, &uuid, BT_ATT_PERM_READ, BT_GATT_CHRC_PROP_READ | BT_GATT_CHRC_PROP_NOTIFY, ccs_call_state_read, NULL, ccs); ccs->signal_strength_ccc = gatt_db_service_add_ccc(ccs->service, BT_ATT_PERM_READ | BT_ATT_PERM_WRITE); bt_uuid16_create(&uuid, BEARER_SIGNAL_INTRVL_CHRC_UUID); ccs->signal_reporting_intrvl = gatt_db_service_add_characteristic(ccs->service, &uuid, BT_ATT_PERM_READ | BT_ATT_PERM_WRITE, BT_GATT_CHRC_PROP_READ | BT_GATT_CHRC_PROP_WRITE | BT_GATT_CHRC_PROP_WRITE_WITHOUT_RESP, ccs_call_state_read, ccs_call_state_write, ccs); bt_uuid16_create(&uuid, CURR_CALL_LIST_CHRC_UUID); ccs->current_call_list = gatt_db_service_add_characteristic(ccs->service, &uuid, BT_ATT_PERM_READ, BT_GATT_CHRC_PROP_READ | BT_GATT_CHRC_PROP_NOTIFY, ccs_call_state_read, NULL, ccs); ccs->current_call_list_ccc = gatt_db_service_add_ccc(ccs->service, BT_ATT_PERM_READ | BT_ATT_PERM_WRITE); bt_uuid16_create(&uuid, BEARER_CCID_CHRC_UUID); ccs->ccid = gatt_db_service_add_characteristic(ccs->service, &uuid, BT_ATT_PERM_READ, BT_GATT_CHRC_PROP_READ, ccs_call_state_read, NULL, ccs); bt_uuid16_create(&uuid, CALL_STATUS_FLAG_CHRC_UUID); ccs->status_flag = gatt_db_service_add_characteristic(ccs->service, &uuid, BT_ATT_PERM_READ, BT_GATT_CHRC_PROP_READ | BT_GATT_CHRC_PROP_NOTIFY, ccs_call_state_read, NULL, ccs); ccs->status_flag_ccc = gatt_db_service_add_ccc(ccs->service, BT_ATT_PERM_READ | BT_ATT_PERM_WRITE); bt_uuid16_create(&uuid, INCOM_CALL_TARGET_URI_CHRC_UUID); ccs->target_bearer_uri = gatt_db_service_add_characteristic(ccs->service, &uuid, BT_ATT_PERM_READ, BT_GATT_CHRC_PROP_READ, ccs_call_state_read, NULL, ccs); bt_uuid16_create(&uuid, CALL_STATE_CHRC_UUID); ccs->call_ctrl_point = gatt_db_service_add_characteristic(ccs->service, &uuid, BT_ATT_PERM_READ, BT_GATT_CHRC_PROP_READ | BT_GATT_CHRC_PROP_NOTIFY, ccs_call_state_read, NULL, ccs); ccs->call_ctrl_point_ccc = gatt_db_service_add_ccc(ccs->service, BT_ATT_PERM_READ | BT_ATT_PERM_WRITE); bt_uuid16_create(&uuid, CALL_CTRL_POINT_CHRC_UUID); ccs->call_ctrl_opt_opcode = gatt_db_service_add_characteristic(ccs->service, &uuid, BT_ATT_PERM_WRITE, BT_GATT_CHRC_PROP_WRITE | BT_GATT_CHRC_PROP_WRITE_WITHOUT_RESP | BT_GATT_CHRC_PROP_NOTIFY, NULL, ccs_call_state_write, ccs); bt_uuid16_create(&uuid, CALL_CTRL_POINT_OPT_OPCODE_CHRC_UUID); ccs->call_ctrl_opt_opcode = gatt_db_service_add_characteristic(ccs->service, &uuid, BT_ATT_PERM_READ, BT_GATT_CHRC_PROP_READ, ccs_call_state_read, NULL, ccs); bt_uuid16_create(&uuid, TERMINATION_REASON_CHRC_UUID); ccs->termination_reason = gatt_db_service_add_characteristic(ccs->service, &uuid, BT_ATT_PERM_READ, BT_GATT_CHRC_PROP_READ | BT_GATT_CHRC_PROP_NOTIFY, ccs_call_state_read, NULL, ccs); bt_uuid16_create(&uuid, INCOMING_CALL_CHRC_UUID); ccs->incoming_call = gatt_db_service_add_characteristic(ccs->service, &uuid, BT_ATT_PERM_NONE, BT_GATT_CHRC_PROP_NOTIFY, NULL, NULL, ccs); ccs->incoming_call_ccc = gatt_db_service_add_ccc(ccs->service, BT_ATT_PERM_READ | BT_ATT_PERM_WRITE); bt_uuid16_create(&uuid, CALL_FRIENDLY_NAME_CHRC_UUID); ccs->friendly_name = gatt_db_service_add_characteristic(ccs->service, &uuid, BT_ATT_PERM_READ, BT_GATT_CHRC_PROP_READ | BT_GATT_CHRC_PROP_NOTIFY, ccs_call_state_read, NULL, ccs); ccs->friendly_name_ccc = gatt_db_service_add_ccc(ccs->service, BT_ATT_PERM_READ | BT_ATT_PERM_WRITE); gatt_db_service_set_active(ccs->service, false); return ccs; } static struct bt_ccs *ccp_get_ccs(struct bt_ccp *ccp) { if (!ccp) return NULL; if (ccp->rdb->ccs) return ccp->rdb->ccs; ccp->rdb->ccs = new0(struct bt_ccs, 1); ccp->rdb->ccs->mdb = ccp->rdb; return ccp->rdb->ccs; } static void ccp_pending_destroy(void *data) { struct bt_ccp_pending *pending = data; struct bt_ccp *ccp = pending->ccp; queue_remove_if(ccp->pending, NULL, pending); } static void ccp_pending_complete(bool success, uint8_t att_ecode, const uint8_t *value, uint16_t length, void *user_data) { struct bt_ccp_pending *pending = user_data; if (pending->func) pending->func(success, att_ecode, value, length, pending->user_data); } static void ccp_read_value(struct bt_ccp *ccp, uint16_t value_handle, bt_gatt_client_read_callback_t func, void *user_data) { struct bt_ccp_pending *pending; pending = new0(struct bt_ccp_pending, 1); pending->ccp = ccp; pending->func = func; pending->user_data = user_data; pending->id = bt_gatt_client_read_value(ccp->client, value_handle, ccp_pending_complete, pending, ccp_pending_destroy); if (!pending->id) { DBG(ccp, "Unable to send Read request"); free(pending); return; } queue_push_tail(ccp->pending, pending); } static void read_call_back(bool success, uint8_t att_ecode, const uint8_t *value, uint16_t length, void *user_data) { struct bt_ccp *ccp = user_data; DBG(ccp, ""); if (!success) { DBG(ccp, "Unable to read call state: error 0x%02x", att_ecode); return; } } static void ccp_cb_register(uint16_t att_ecode, void *user_data) { struct bt_ccp *ccp = user_data; if (att_ecode) DBG(ccp, "ccp cb notification failed: 0x%04x", att_ecode); /* TODO: generic handler for non-mandatory CCP call backs */ } static void ccp_cb_notify(uint16_t value_handle, const uint8_t *value, uint16_t length, void *user_data) { /* TODO: generic handler for non-mandatory CCP notifications */ } static void ccp_cb_status_flag_register(uint16_t att_ecode, void *user_data) { struct bt_ccp *ccp = user_data; if (att_ecode) DBG(ccp, "ccp cb notification failed: 0x%04x", att_ecode); } static void ccp_cb_status_flag_notify(uint16_t value_handle, const uint8_t *value, uint16_t length, void *user_data) { struct bt_ccp *ccp = user_data; DBG(ccp, ""); if (!length) return; } static void ccp_cb_terminate_register(uint16_t att_ecode, void *user_data) { struct bt_ccp *ccp = user_data; if (att_ecode) DBG(ccp, "ccp cb notification failed: 0x%04x", att_ecode); } static void ccp_cb_terminate_notify(uint16_t value_handle, const uint8_t *value, uint16_t length, void *user_data) { struct bt_ccp *ccp = user_data; DBG(ccp, ""); if (!length) return; /* TODO: update call state in Local context */ } static void ccp_cb_bearer_name_register(uint16_t att_ecode, void *user_data) { struct bt_ccp *ccp = user_data; DBG(ccp, ""); if (att_ecode) DBG(ccp, "ccp cb notification failed: 0x%04x", att_ecode); } static void ccp_cb_bearer_name_notify(uint16_t value_handle, const uint8_t *value, uint16_t length, void *user_data) { struct bt_ccp *ccp = user_data; DBG(ccp, ""); if (!length) return; /* TODO: update call details in Local context */ } static void ccp_cb_call_list_register(uint16_t att_ecode, void *user_data) { struct bt_ccp *ccp = user_data; DBG(ccp, ""); if (att_ecode) DBG(ccp, "ccp cb notification failed: 0x%04x", att_ecode); } static void ccp_cb_call_list_notify(uint16_t value_handle, const uint8_t *value, uint16_t length, void *user_data) { struct bt_ccp *ccp = user_data; DBG(ccp, ""); if (!length) return; /* TODO: update call list in Local context */ } static void ccp_cb_call_state_register(uint16_t att_ecode, void *user_data) { struct bt_ccp *ccp = user_data; DBG(ccp, ""); if (att_ecode) DBG(ccp, "ccp cb notification failed: 0x%04x", att_ecode); } static void ccp_cb_call_state_notify(uint16_t value_handle, const uint8_t *value, uint16_t length, void *user_data) { struct bt_ccp *ccp = user_data; DBG(ccp, ""); if (!length) return; /* TODO: update call state in Local context */ } static void ccp_cb_incom_call_register(uint16_t att_ecode, void *user_data) { struct bt_ccp *ccp = user_data; DBG(ccp, ""); if (att_ecode) DBG(ccp, "ccp cb notification failed: 0x%04x", att_ecode); } static void ccp_cb_incom_call_notify(uint16_t value_handle, const uint8_t *value, uint16_t length, void *user_data) { struct bt_ccp *ccp = user_data; DBG(ccp, ""); if (!length) return; /* TODO: Handle incoming call notofiation, Answer/reject etc */ } static void bt_ccp_incom_call_attach(struct bt_ccp *ccp) { uint16_t value_handle; struct bt_ccs *ccs = ccp_get_ccs(ccp); DBG(ccp, ""); if (!gatt_db_attribute_get_char_data(ccs->incoming_call, NULL, &value_handle, NULL, NULL, NULL)) return; ccp_read_value(ccp, value_handle, read_call_back, ccp); ccp->incoming_call_id = bt_gatt_client_register_notify(ccp->client, value_handle, ccp_cb_incom_call_register, ccp_cb_incom_call_notify, ccp, NULL); } static void bt_ccp_call_state_attach(struct bt_ccp *ccp) { uint16_t value_handle; struct bt_ccs *ccs = ccp_get_ccs(ccp); DBG(ccp, ""); if (!gatt_db_attribute_get_char_data(ccs->call_state, NULL, &value_handle, NULL, NULL, NULL)) return; ccp_read_value(ccp, value_handle, read_call_back, ccp); ccp->call_state_id = bt_gatt_client_register_notify(ccp->client, value_handle, ccp_cb_call_state_register, ccp_cb_call_state_notify, ccp, NULL); } static void bt_ccp_call_list_attach(struct bt_ccp *ccp) { uint16_t value_handle; struct bt_ccs *ccs = ccp_get_ccs(ccp); DBG(ccp, ""); if (!gatt_db_attribute_get_char_data(ccs->current_call_list, NULL, &value_handle, NULL, NULL, NULL)) return; ccp_read_value(ccp, value_handle, read_call_back, ccp); ccp->current_call_list_id = bt_gatt_client_register_notify(ccp->client, value_handle, ccp_cb_call_list_register, ccp_cb_call_list_notify, ccp, NULL); } static void bt_ccp_name_attach(struct bt_ccp *ccp) { uint16_t value_handle; struct bt_ccs *ccs = ccp_get_ccs(ccp); DBG(ccp, ""); if (!gatt_db_attribute_get_char_data(ccs->bearer_name, NULL, &value_handle, NULL, NULL, NULL)) return; ccp_read_value(ccp, value_handle, read_call_back, ccp); ccp->bearer_name_id = bt_gatt_client_register_notify(ccp->client, value_handle, ccp_cb_bearer_name_register, ccp_cb_bearer_name_notify, ccp, NULL); } static void bt_ccp_term_reason_attach(struct bt_ccp *ccp) { uint16_t value_handle; struct bt_ccs *ccs = ccp_get_ccs(ccp); DBG(ccp, ""); if (!gatt_db_attribute_get_char_data(ccs->termination_reason, NULL, &value_handle, NULL, NULL, NULL)) return; ccp_read_value(ccp, value_handle, read_call_back, ccp); ccp->termination_reason_id = bt_gatt_client_register_notify(ccp->client, value_handle, ccp_cb_terminate_register, ccp_cb_terminate_notify, ccp, NULL); } static void bt_ccp_status_attach(struct bt_ccp *ccp) { uint16_t value_handle; struct bt_ccs *ccs = ccp_get_ccs(ccp); DBG(ccp, ""); if (!gatt_db_attribute_get_char_data(ccs->status_flag, NULL, &value_handle, NULL, NULL, NULL)) return; ccp_read_value(ccp, value_handle, read_call_back, ccp); ccp->status_flag_id = bt_gatt_client_register_notify(ccp->client, value_handle, ccp_cb_status_flag_register, ccp_cb_status_flag_notify, ccp, NULL); } static void bt_ccp_uci_attach(struct bt_ccp *ccp) { uint16_t value_handle; struct bt_ccs *ccs = ccp_get_ccs(ccp); DBG(ccp, ""); if (!gatt_db_attribute_get_char_data(ccs->bearer_uci, NULL, &value_handle, NULL, NULL, NULL)) return; ccp_read_value(ccp, value_handle, read_call_back, ccp); ccp->bearer_uci_id = bt_gatt_client_register_notify(ccp->client, value_handle, ccp_cb_register, ccp_cb_notify, ccp, NULL); } static void bt_ccp_technology_attach(struct bt_ccp *ccp) { uint16_t value_handle; struct bt_ccs *ccs = ccp_get_ccs(ccp); DBG(ccp, ""); if (!gatt_db_attribute_get_char_data(ccs->bearer_technology, NULL, &value_handle, NULL, NULL, NULL)) return; ccp_read_value(ccp, value_handle, read_call_back, ccp); ccp->bearer_technology_id = bt_gatt_client_register_notify(ccp->client, value_handle, ccp_cb_register, ccp_cb_notify, ccp, NULL); } static void bt_ccp_strength_attach(struct bt_ccp *ccp) { uint16_t value_handle; struct bt_ccs *ccs = ccp_get_ccs(ccp); DBG(ccp, ""); if (!gatt_db_attribute_get_char_data(ccs->signal_strength, NULL, &value_handle, NULL, NULL, NULL)) return; ccp_read_value(ccp, value_handle, read_call_back, ccp); ccp->signal_strength_id = bt_gatt_client_register_notify(ccp->client, value_handle, ccp_cb_register, ccp_cb_notify, ccp, NULL); } static void bt_ccp_ccid_attach(struct bt_ccp *ccp) { uint16_t value_handle; struct bt_ccs *ccs = ccp_get_ccs(ccp); DBG(ccp, ""); if (!gatt_db_attribute_get_char_data(ccs->ccid, NULL, &value_handle, NULL, NULL, NULL)) return; ccp_read_value(ccp, value_handle, read_call_back, ccp); ccp->ccid_id = bt_gatt_client_register_notify(ccp->client, value_handle, ccp_cb_register, ccp_cb_notify, ccp, NULL); } static void bt_ccp_tar_uri_attach(struct bt_ccp *ccp) { uint16_t value_handle; struct bt_ccs *ccs = ccp_get_ccs(ccp); DBG(ccp, ""); if (!gatt_db_attribute_get_char_data(ccs->target_bearer_uri, NULL, &value_handle, NULL, NULL, NULL)) return; ccp_read_value(ccp, value_handle, read_call_back, ccp); ccp->target_bearer_uri_id = bt_gatt_client_register_notify(ccp->client, value_handle, ccp_cb_register, ccp_cb_notify, ccp, NULL); } static void bt_ccp_ctrl_point_attach(struct bt_ccp *ccp) { uint16_t value_handle; struct bt_ccs *ccs = ccp_get_ccs(ccp); DBG(ccp, ""); if (!gatt_db_attribute_get_char_data(ccs->call_ctrl_point, NULL, &value_handle, NULL, NULL, NULL)) return; ccp_read_value(ccp, value_handle, read_call_back, ccp); ccp->call_control_pt_id = bt_gatt_client_register_notify(ccp->client, value_handle, ccp_cb_register, ccp_cb_notify, ccp, NULL); } static void bt_ccp_ctrl_opcode_attach(struct bt_ccp *ccp) { uint16_t value_handle; struct bt_ccs *ccs = ccp_get_ccs(ccp); DBG(ccp, ""); if (!gatt_db_attribute_get_char_data(ccs->call_ctrl_opt_opcode, NULL, &value_handle, NULL, NULL, NULL)) return; ccp_read_value(ccp, value_handle, read_call_back, ccp); ccp->call_control_opt_opcode_id = bt_gatt_client_register_notify(ccp->client, value_handle, ccp_cb_register, ccp_cb_notify, ccp, NULL); } static void bt_ccp_friendly_name_attach(struct bt_ccp *ccp) { uint16_t value_handle; struct bt_ccs *ccs = ccp_get_ccs(ccp); DBG(ccp, ""); if (!gatt_db_attribute_get_char_data(ccs->friendly_name, NULL, &value_handle, NULL, NULL, NULL)) return; ccp_read_value(ccp, value_handle, read_call_back, ccp); ccp->friendly_name_id = bt_gatt_client_register_notify(ccp->client, value_handle, ccp_cb_register, ccp_cb_notify, ccp, NULL); } static void bt_ccp_signal_intrvl_attach(struct bt_ccp *ccp) { uint16_t value_handle; struct bt_ccs *ccs = ccp_get_ccs(ccp); DBG(ccp, ""); if (!gatt_db_attribute_get_char_data(ccs->signal_reporting_intrvl, NULL, &value_handle, NULL, NULL, NULL)) return; ccp_read_value(ccp, value_handle, read_call_back, ccp); ccp->signal_reporting_intrvl_id = bt_gatt_client_register_notify(ccp->client, value_handle, ccp_cb_register, ccp_cb_notify, ccp, NULL); } static void bt_ccp_uri_list_attach(struct bt_ccp *ccp) { uint16_t value_handle; struct bt_ccs *ccs = ccp_get_ccs(ccp); DBG(ccp, ""); if (!gatt_db_attribute_get_char_data(ccs->bearer_uri_schemes_list, NULL, &value_handle, NULL, NULL, NULL)) return; ccp_read_value(ccp, value_handle, read_call_back, ccp); ccp->bearer_uri_schemes_list_id = bt_gatt_client_register_notify(ccp->client, value_handle, ccp_cb_register, ccp_cb_notify, ccp, NULL); } static void foreach_ccs_char(struct gatt_db_attribute *attr, void *user_data) { struct bt_ccp *ccp = user_data; struct bt_ccs *ccs; uint16_t value_handle; bt_uuid_t uuid; if (!gatt_db_attribute_get_char_data(attr, NULL, &value_handle, NULL, NULL, &uuid)) return; ccs = ccp_get_ccs(ccp); if (!ccs || ccs->call_state) return; if (bt_uuid16_cmp(&uuid, BEARER_PROVIDER_NAME_CHRC_UUID)) { DBG(ccp, "Found Bearer Name, handle 0x%04x", value_handle); ccs->bearer_name = attr; bt_ccp_name_attach(ccp); } if (bt_uuid16_cmp(&uuid, BEARER_UCI_CHRC_UUID)) { DBG(ccp, "Found Bearer Uci, handle 0x%04x", value_handle); ccs->bearer_uci = attr; bt_ccp_uci_attach(ccp); } if (bt_uuid16_cmp(&uuid, BEARER_TECH_CHRC_UUID)) { DBG(ccp, "Found Bearer Technology, handle %x", value_handle); ccs->bearer_technology = attr; bt_ccp_technology_attach(ccp); } if (bt_uuid16_cmp(&uuid, BEARER_SIGNAL_STR_CHRC_UUID)) { DBG(ccp, "Found Signal Strength, handle 0x%04x", value_handle); ccs->signal_strength = attr; bt_ccp_strength_attach(ccp); } if (bt_uuid16_cmp(&uuid, BEARER_SIGNAL_INTRVL_CHRC_UUID)) { DBG(ccp, "Found Signal Interval, handle 0x%04x", value_handle); ccs->signal_reporting_intrvl = attr; bt_ccp_signal_intrvl_attach(ccp); } if (bt_uuid16_cmp(&uuid, CALL_STATUS_FLAG_CHRC_UUID)) { DBG(ccp, "Found Status Flag, handle 0x%04x", value_handle); ccs->status_flag = attr; bt_ccp_status_attach(ccp); } if (bt_uuid16_cmp(&uuid, BEARER_URI_SCHEME_CHRC_UUID)) { DBG(ccp, "Found URI Scheme, handle 0x%04x", value_handle); ccs->bearer_uri_schemes_list = attr; bt_ccp_uri_list_attach(ccp); } if (bt_uuid16_cmp(&uuid, CURR_CALL_LIST_CHRC_UUID)) { DBG(ccp, "Found Call List, handle 0x%04x", value_handle); ccs->current_call_list = attr; bt_ccp_call_list_attach(ccp); } if (bt_uuid16_cmp(&uuid, BEARER_CCID_CHRC_UUID)) { DBG(ccp, "Found CCID, handle 0x%04x", value_handle); ccs->ccid = attr; bt_ccp_ccid_attach(ccp); } if (bt_uuid16_cmp(&uuid, INCOM_CALL_TARGET_URI_CHRC_UUID)) { DBG(ccp, "Found Bearer Uri, handle 0x%04x", value_handle); ccs->target_bearer_uri = attr; bt_ccp_tar_uri_attach(ccp); } if (bt_uuid16_cmp(&uuid, CALL_CTRL_POINT_CHRC_UUID)) { DBG(ccp, "Found Control Point, handle 0x%04x", value_handle); ccs->call_ctrl_point = attr; bt_ccp_ctrl_point_attach(ccp); } if (bt_uuid16_cmp(&uuid, CALL_CTRL_POINT_OPT_OPCODE_CHRC_UUID)) { DBG(ccp, "Found Control opcode, handle 0x%04x", value_handle); ccs->call_ctrl_opt_opcode = attr; bt_ccp_ctrl_opcode_attach(ccp); } if (bt_uuid16_cmp(&uuid, TERMINATION_REASON_CHRC_UUID)) { DBG(ccp, "Found Termination Reason, handle %x", value_handle); ccs->termination_reason = attr; bt_ccp_term_reason_attach(ccp); } if (bt_uuid16_cmp(&uuid, INCOMING_CALL_CHRC_UUID)) { DBG(ccp, "Found Incoming call, handle 0x%04x", value_handle); ccs->incoming_call = attr; bt_ccp_incom_call_attach(ccp); } if (bt_uuid16_cmp(&uuid, CALL_FRIENDLY_NAME_CHRC_UUID)) { DBG(ccp, "Found Friendly name, handle 0x%04x", value_handle); ccs->friendly_name = attr; bt_ccp_friendly_name_attach(ccp); } } void bt_ccp_set_event_callbacks(struct bt_ccp *ccp, const struct bt_ccp_event_callback *cbs, void *user_data) { struct event_callback *cb; if (ccp->cb) free(ccp->cb); cb = new0(struct event_callback, 1); cb->cbs = cbs; cb->user_data = user_data; ccp->cb = cb; } static void foreach_ccs_service(struct gatt_db_attribute *attr, void *user_data) { struct bt_ccp *ccp = user_data; struct bt_ccs *ccs = ccp_get_ccs(ccp); ccs->service = attr; gatt_db_service_foreach_char(attr, foreach_ccs_char, ccp); } static struct bt_ccp_db *ccp_db_new(struct gatt_db *db) { struct bt_ccp_db *mdb; if (!db) return NULL; mdb = new0(struct bt_ccp_db, 1); mdb->db = gatt_db_ref(db); if (!ccp_db) ccp_db = queue_new(); queue_push_tail(ccp_db, mdb); mdb->ccs = ccs_new(db); return mdb; } static struct bt_ccp_db *ccp_get_db(struct gatt_db *db) { struct bt_ccp_db *mdb; mdb = queue_find(ccp_db, ccp_db_match, db); if (mdb) return mdb; return ccp_db_new(db); } struct bt_ccp *bt_ccp_new(struct gatt_db *ldb, struct gatt_db *rdb) { struct bt_ccp *ccp; struct bt_ccp_db *mdb; if (!ldb) return NULL; mdb = ccp_get_db(ldb); if (!mdb) return NULL; ccp = new0(struct bt_ccp, 1); ccp->ldb = mdb; ccp->pending = queue_new(); if (!rdb) goto done; mdb = new0(struct bt_ccp_db, 1); mdb->db = gatt_db_ref(rdb); ccp->rdb = mdb; done: bt_ccp_ref(ccp); return ccp; } void bt_ccp_register(struct gatt_db *db) { ccp_db_new(db); } bool bt_ccp_attach(struct bt_ccp *ccp, struct bt_gatt_client *client) { bt_uuid_t uuid; DBG(ccp, "ccp %p", ccp); ccp->client = bt_gatt_client_clone(client); if (!ccp->client) return false; if (ccp->rdb->ccs) { bt_ccp_call_state_attach(ccp); return true; } bt_uuid16_create(&uuid, GTBS_UUID); gatt_db_foreach_service(ccp->rdb->db, &uuid, foreach_ccs_service, ccp); return true; } void bt_ccp_detach(struct bt_ccp *ccp) { DBG(ccp, "%p", ccp); bt_gatt_client_unref(ccp->client); ccp->client = NULL; } bluez-5.82/src/shared/PaxHeaders/att.c0000644000000000000000000000005014772767672014670 xustar0020 atime=1743515579 20 ctime=1743591277 bluez-5.82/src/shared/att.c0000644000000000000000000012675614772767672014372 0ustar00rootroot// SPDX-License-Identifier: LGPL-2.1-or-later /* * * BlueZ - Bluetooth protocol stack for Linux * * Copyright (C) 2014 Google Inc. * * */ #ifdef HAVE_CONFIG_H #include #endif #include #include #include #include "src/shared/io.h" #include "src/shared/queue.h" #include "src/shared/util.h" #include "src/shared/timeout.h" #include "lib/bluetooth.h" #include "lib/l2cap.h" #include "lib/uuid.h" #include "src/shared/att.h" #include "src/shared/crypto.h" #define ATT_MIN_PDU_LEN 1 /* At least 1 byte for the opcode. */ #define ATT_OP_CMD_MASK 0x40 #define ATT_OP_SIGNED_MASK 0x80 #define ATT_TIMEOUT_INTERVAL 30000 /* 30000 ms */ /* Length of signature in write signed packet */ #define BT_ATT_SIGNATURE_LEN 12 struct att_send_op; struct bt_att_chan { struct bt_att *att; int fd; struct io *io; uint8_t type; int sec_level; /* Only used for non-L2CAP */ struct queue *queue; /* Channel dedicated queue */ struct att_send_op *pending_req; struct att_send_op *pending_ind; bool writer_active; bool in_req; /* There's a pending incoming request */ uint8_t *buf; uint16_t mtu; }; struct bt_att { int ref_count; bool close_on_unref; struct queue *chans; uint8_t enc_size; uint16_t mtu; /* Biggest possible MTU */ struct queue *notify_list; /* List of registered callbacks */ struct queue *disconn_list; /* List of disconnect handlers */ struct queue *exchange_list; /* List of MTU changed handlers */ unsigned int next_send_id; /* IDs for "send" ops */ unsigned int next_reg_id; /* IDs for registered callbacks */ struct queue *req_queue; /* Queued ATT protocol requests */ struct queue *ind_queue; /* Queued ATT protocol indications */ struct queue *write_queue; /* Queue of PDUs ready to send */ bool in_disc; /* Cleanup queues on disconnect_cb */ bt_att_timeout_func_t timeout_callback; bt_att_destroy_func_t timeout_destroy; void *timeout_data; uint8_t debug_level; bt_att_debug_func_t debug_callback; bt_att_destroy_func_t debug_destroy; void *debug_data; struct bt_crypto *crypto; struct sign_info *local_sign; struct sign_info *remote_sign; }; struct sign_info { uint8_t key[16]; bt_att_counter_func_t counter; void *user_data; }; enum att_op_type { ATT_OP_TYPE_REQ, ATT_OP_TYPE_RSP, ATT_OP_TYPE_CMD, ATT_OP_TYPE_IND, ATT_OP_TYPE_NFY, ATT_OP_TYPE_CONF, ATT_OP_TYPE_UNKNOWN, }; static const struct { uint8_t opcode; enum att_op_type type; } att_opcode_type_table[] = { { BT_ATT_OP_ERROR_RSP, ATT_OP_TYPE_RSP }, { BT_ATT_OP_MTU_REQ, ATT_OP_TYPE_REQ }, { BT_ATT_OP_MTU_RSP, ATT_OP_TYPE_RSP }, { BT_ATT_OP_FIND_INFO_REQ, ATT_OP_TYPE_REQ }, { BT_ATT_OP_FIND_INFO_RSP, ATT_OP_TYPE_RSP }, { BT_ATT_OP_FIND_BY_TYPE_REQ, ATT_OP_TYPE_REQ }, { BT_ATT_OP_FIND_BY_TYPE_RSP, ATT_OP_TYPE_RSP }, { BT_ATT_OP_READ_BY_TYPE_REQ, ATT_OP_TYPE_REQ }, { BT_ATT_OP_READ_BY_TYPE_RSP, ATT_OP_TYPE_RSP }, { BT_ATT_OP_READ_REQ, ATT_OP_TYPE_REQ }, { BT_ATT_OP_READ_RSP, ATT_OP_TYPE_RSP }, { BT_ATT_OP_READ_BLOB_REQ, ATT_OP_TYPE_REQ }, { BT_ATT_OP_READ_BLOB_RSP, ATT_OP_TYPE_RSP }, { BT_ATT_OP_READ_MULT_REQ, ATT_OP_TYPE_REQ }, { BT_ATT_OP_READ_MULT_RSP, ATT_OP_TYPE_RSP }, { BT_ATT_OP_READ_BY_GRP_TYPE_REQ, ATT_OP_TYPE_REQ }, { BT_ATT_OP_READ_BY_GRP_TYPE_RSP, ATT_OP_TYPE_RSP }, { BT_ATT_OP_WRITE_REQ, ATT_OP_TYPE_REQ }, { BT_ATT_OP_WRITE_RSP, ATT_OP_TYPE_RSP }, { BT_ATT_OP_WRITE_CMD, ATT_OP_TYPE_CMD }, { BT_ATT_OP_SIGNED_WRITE_CMD, ATT_OP_TYPE_CMD }, { BT_ATT_OP_PREP_WRITE_REQ, ATT_OP_TYPE_REQ }, { BT_ATT_OP_PREP_WRITE_RSP, ATT_OP_TYPE_RSP }, { BT_ATT_OP_EXEC_WRITE_REQ, ATT_OP_TYPE_REQ }, { BT_ATT_OP_EXEC_WRITE_RSP, ATT_OP_TYPE_RSP }, { BT_ATT_OP_HANDLE_NFY, ATT_OP_TYPE_NFY }, { BT_ATT_OP_HANDLE_NFY_MULT, ATT_OP_TYPE_NFY }, { BT_ATT_OP_HANDLE_IND, ATT_OP_TYPE_IND }, { BT_ATT_OP_HANDLE_CONF, ATT_OP_TYPE_CONF }, { } }; static enum att_op_type get_op_type(uint8_t opcode) { int i; for (i = 0; att_opcode_type_table[i].opcode; i++) { if (att_opcode_type_table[i].opcode == opcode) return att_opcode_type_table[i].type; } if (opcode & ATT_OP_CMD_MASK) return ATT_OP_TYPE_CMD; return ATT_OP_TYPE_UNKNOWN; } static const struct { uint8_t req_opcode; uint8_t rsp_opcode; } att_req_rsp_mapping_table[] = { { BT_ATT_OP_MTU_REQ, BT_ATT_OP_MTU_RSP }, { BT_ATT_OP_FIND_INFO_REQ, BT_ATT_OP_FIND_INFO_RSP}, { BT_ATT_OP_FIND_BY_TYPE_REQ, BT_ATT_OP_FIND_BY_TYPE_RSP }, { BT_ATT_OP_READ_BY_TYPE_REQ, BT_ATT_OP_READ_BY_TYPE_RSP }, { BT_ATT_OP_READ_REQ, BT_ATT_OP_READ_RSP }, { BT_ATT_OP_READ_BLOB_REQ, BT_ATT_OP_READ_BLOB_RSP }, { BT_ATT_OP_READ_MULT_REQ, BT_ATT_OP_READ_MULT_RSP }, { BT_ATT_OP_READ_BY_GRP_TYPE_REQ, BT_ATT_OP_READ_BY_GRP_TYPE_RSP }, { BT_ATT_OP_WRITE_REQ, BT_ATT_OP_WRITE_RSP }, { BT_ATT_OP_PREP_WRITE_REQ, BT_ATT_OP_PREP_WRITE_RSP }, { BT_ATT_OP_EXEC_WRITE_REQ, BT_ATT_OP_EXEC_WRITE_RSP }, { } }; static uint8_t get_req_opcode(uint8_t rsp_opcode) { int i; for (i = 0; att_req_rsp_mapping_table[i].rsp_opcode; i++) { if (att_req_rsp_mapping_table[i].rsp_opcode == rsp_opcode) return att_req_rsp_mapping_table[i].req_opcode; } return 0; } struct att_send_op { unsigned int id; unsigned int timeout_id; enum att_op_type type; uint8_t opcode; void *pdu; uint16_t len; bool retry; bt_att_response_func_t callback; bt_att_destroy_func_t destroy; void *user_data; }; static void destroy_att_send_op(void *data) { struct att_send_op *op = data; if (op->timeout_id) timeout_remove(op->timeout_id); if (op->destroy) op->destroy(op->user_data); free(op->pdu); free(op); } static void cancel_att_send_op(void *data) { struct att_send_op *op = data; if (op->destroy) op->destroy(op->user_data); op->user_data = NULL; op->callback = NULL; op->destroy = NULL; } struct att_notify { unsigned int id; uint16_t opcode; bt_att_notify_func_t callback; bt_att_destroy_func_t destroy; void *user_data; }; static void destroy_att_notify(void *data) { struct att_notify *notify = data; if (notify->destroy) notify->destroy(notify->user_data); free(notify); } static bool match_notify_id(const void *a, const void *b) { const struct att_notify *notify = a; unsigned int id = PTR_TO_UINT(b); return notify->id == id; } struct att_disconn { unsigned int id; bool removed; bt_att_disconnect_func_t callback; bt_att_destroy_func_t destroy; void *user_data; }; struct att_exchange { unsigned int id; bool removed; bt_att_exchange_func_t callback; bt_att_destroy_func_t destroy; void *user_data; }; static void destroy_att_disconn(void *data) { struct att_disconn *disconn = data; if (disconn->destroy) disconn->destroy(disconn->user_data); free(disconn); } static void destroy_att_exchange(void *data) { struct att_exchange *exchange = data; if (exchange->destroy) exchange->destroy(exchange->user_data); free(exchange); } static bool match_disconn_id(const void *a, const void *b) { const struct att_disconn *disconn = a; unsigned int id = PTR_TO_UINT(b); return disconn->id == id; } static void att_log(struct bt_att *att, uint8_t level, const char *format, ...) { va_list va; if (att->debug_level < level) return; va_start(va, format); util_debug_va(att->debug_callback, att->debug_data, format, va); va_end(va); } #define DBG(_att, _format, _arg...) \ att_log(_att, BT_ATT_DEBUG, "%s:%s() " _format, __FILE__, __func__,\ ## _arg) #define VERBOSE(_att, _format, _arg...) \ att_log(_att, BT_ATT_DEBUG_VERBOSE, "%s:%s() " _format, __FILE__, \ __func__, ## _arg) static void att_hexdump(struct bt_att *att, char dir, const void *data, size_t len) { if (att->debug_level < 2) return; util_hexdump(dir, data, len, att->debug_callback, att->debug_data); } static bool encode_pdu(struct bt_att *att, struct att_send_op *op, const void *pdu, uint16_t length) { uint16_t pdu_len = 1; struct sign_info *sign = att->local_sign; uint32_t sign_cnt; if (sign && (op->opcode & ATT_OP_SIGNED_MASK)) pdu_len += BT_ATT_SIGNATURE_LEN; if (length && pdu) pdu_len += length; if (pdu_len > att->mtu) return false; op->len = pdu_len; op->pdu = malloc(op->len); if (!op->pdu) return false; ((uint8_t *) op->pdu)[0] = op->opcode; if (pdu_len > 1) memcpy(op->pdu + 1, pdu, length); if (!sign || !(op->opcode & ATT_OP_SIGNED_MASK) || !att->crypto) return true; if (!sign->counter(&sign_cnt, sign->user_data)) goto fail; if ((bt_crypto_sign_att(att->crypto, sign->key, op->pdu, 1 + length, sign_cnt, &((uint8_t *) op->pdu)[1 + length]))) return true; DBG(att, "ATT unable to generate signature"); fail: free(op->pdu); return false; } static struct att_send_op *create_att_send_op(struct bt_att *att, uint8_t opcode, const void *pdu, uint16_t length, bt_att_response_func_t callback, void *user_data, bt_att_destroy_func_t destroy) { struct att_send_op *op; enum att_op_type type; if (length && !pdu) return NULL; type = get_op_type(opcode); if (type == ATT_OP_TYPE_UNKNOWN) return NULL; /* If the opcode corresponds to an operation type that does not elicit a * response from the remote end, then no callback should have been * provided, since it will never be called. */ if (callback && type != ATT_OP_TYPE_REQ && type != ATT_OP_TYPE_IND) return NULL; /* Similarly, if the operation does elicit a response then a callback * must be provided. */ if (!callback && (type == ATT_OP_TYPE_REQ || type == ATT_OP_TYPE_IND)) return NULL; op = new0(struct att_send_op, 1); op->type = type; op->opcode = opcode; op->callback = callback; op->destroy = destroy; op->user_data = user_data; if (!encode_pdu(att, op, pdu, length)) { free(op); return NULL; } return op; } static struct att_send_op *pick_next_send_op(struct bt_att_chan *chan) { struct bt_att *att = chan->att; struct att_send_op *op; /* Check if there is anything queued on the channel */ op = queue_pop_head(chan->queue); if (op) return op; /* See if any operations are already in the write queue */ op = queue_peek_head(att->write_queue); if (op && op->len <= chan->mtu) return queue_pop_head(att->write_queue); /* If there is no pending request, pick an operation from the * request queue. */ if (!chan->pending_req) { op = queue_peek_head(att->req_queue); if (op && op->len <= chan->mtu) { /* Don't send Exchange MTU over EATT */ if (op->opcode == BT_ATT_OP_MTU_REQ && chan->type == BT_ATT_EATT) goto indicate; return queue_pop_head(att->req_queue); } } indicate: /* There is either a request pending or no requests queued. If there is * no pending indication, pick an operation from the indication queue. */ if (!chan->pending_ind) { op = queue_peek_head(att->ind_queue); if (op && op->len <= chan->mtu) return queue_pop_head(att->ind_queue); } return NULL; } static void disc_att_send_op(void *data) { struct att_send_op *op = data; if (op->callback) op->callback(BT_ATT_OP_ERROR_RSP, NULL, 0, op->user_data); destroy_att_send_op(op); } struct timeout_data { struct bt_att_chan *chan; unsigned int id; }; static bool timeout_cb(void *user_data) { struct timeout_data *timeout = user_data; struct bt_att_chan *chan = timeout->chan; struct bt_att *att = chan->att; struct att_send_op *op = NULL; if (chan->pending_req && chan->pending_req->id == timeout->id) { op = chan->pending_req; chan->pending_req = NULL; } else if (chan->pending_ind && chan->pending_ind->id == timeout->id) { op = chan->pending_ind; chan->pending_ind = NULL; } if (!op) return false; DBG(att, "(chan %p) Operation timed out: 0x%02x", chan, op->opcode); if (att->timeout_callback) att->timeout_callback(op->id, op->opcode, att->timeout_data); op->timeout_id = 0; disc_att_send_op(op); /* * Directly terminate the connection as required by the ATT protocol. * This should trigger an io disconnect event which will clean up the * io and notify the upper layer. */ io_shutdown(chan->io); return false; } static void write_watch_destroy(void *user_data) { struct bt_att_chan *chan = user_data; chan->writer_active = false; } static ssize_t bt_att_chan_write(struct bt_att_chan *chan, uint8_t opcode, const void *pdu, uint16_t len) { struct bt_att *att = chan->att; ssize_t ret; struct iovec iov; iov.iov_base = (void *) pdu; iov.iov_len = len; VERBOSE(att, "(chan %p) ATT op 0x%02x", chan, opcode); ret = io_send(chan->io, &iov, 1); if (ret < 0) { DBG(att, "(chan %p) write failed: %s", chan, strerror(-ret)); return ret; } if (att->debug_level) util_hexdump('<', pdu, ret, att->debug_callback, att->debug_data); return ret; } static bool can_write_data(struct io *io, void *user_data) { struct bt_att_chan *chan = user_data; struct att_send_op *op; struct timeout_data *timeout; op = pick_next_send_op(chan); if (!op) return false; if (bt_att_chan_write(chan, op->opcode, op->pdu, op->len) < 0) { if (op->callback) op->callback(BT_ATT_OP_ERROR_RSP, NULL, 0, op->user_data); destroy_att_send_op(op); return true; } /* Based on the operation type, set either the pending request or the * pending indication. If it came from the write queue, then there is * no need to keep it around. */ switch (op->type) { case ATT_OP_TYPE_REQ: chan->pending_req = op; break; case ATT_OP_TYPE_IND: chan->pending_ind = op; break; case ATT_OP_TYPE_RSP: /* Set in_req to false to indicate that no request is pending */ chan->in_req = false; /* fall through */ case ATT_OP_TYPE_CMD: case ATT_OP_TYPE_NFY: case ATT_OP_TYPE_CONF: case ATT_OP_TYPE_UNKNOWN: default: destroy_att_send_op(op); return true; } timeout = new0(struct timeout_data, 1); timeout->chan = chan; timeout->id = op->id; op->timeout_id = timeout_add(ATT_TIMEOUT_INTERVAL, timeout_cb, timeout, free); /* Return true as there may be more operations ready to write. */ return true; } static void wakeup_chan_writer(void *data, void *user_data) { struct bt_att_chan *chan = data; struct bt_att *att = chan->att; if (chan->writer_active) return; /* Set the write handler only if there is anything that can be sent * at all. */ if (queue_isempty(chan->queue) && queue_isempty(att->write_queue)) { if ((chan->pending_req || queue_isempty(att->req_queue)) && (chan->pending_ind || queue_isempty(att->ind_queue))) return; } if (!io_set_write_handler(chan->io, can_write_data, chan, write_watch_destroy)) return; chan->writer_active = true; } static void wakeup_writer(struct bt_att *att) { queue_foreach(att->chans, wakeup_chan_writer, NULL); } static void disconn_handler(void *data, void *user_data) { struct att_disconn *disconn = data; int err = PTR_TO_INT(user_data); if (disconn->removed) return; if (disconn->callback) disconn->callback(err, disconn->user_data); } static void bt_att_chan_free(void *data) { struct bt_att_chan *chan = data; if (chan->pending_req) destroy_att_send_op(chan->pending_req); if (chan->pending_ind) destroy_att_send_op(chan->pending_ind); queue_destroy(chan->queue, destroy_att_send_op); io_destroy(chan->io); free(chan->buf); free(chan); } static bool disconnect_cb(struct io *io, void *user_data) { struct bt_att_chan *chan = user_data; struct bt_att *att = chan->att; int err; socklen_t len; len = sizeof(err); if (getsockopt(chan->fd, SOL_SOCKET, SO_ERROR, &err, &len) < 0) { DBG(att, "(chan %p) Failed to obtain disconnect " "error: %s", chan, strerror(errno)); err = 0; } DBG(att, "Channel %p disconnected: %s", chan, strerror(err)); /* Dettach channel */ queue_remove(att->chans, chan); if (chan->pending_req) { disc_att_send_op(chan->pending_req); chan->pending_req = NULL; } if (chan->pending_ind) { disc_att_send_op(chan->pending_ind); chan->pending_ind = NULL; } bt_att_chan_free(chan); /* Don't run disconnect callback if there are channels left */ if (!queue_isempty(att->chans)) return false; bt_att_ref(att); att->in_disc = true; /* Notify request callbacks */ queue_remove_all(att->req_queue, NULL, NULL, disc_att_send_op); queue_remove_all(att->ind_queue, NULL, NULL, disc_att_send_op); queue_remove_all(att->write_queue, NULL, NULL, disc_att_send_op); att->in_disc = false; queue_foreach(att->disconn_list, disconn_handler, INT_TO_PTR(err)); bt_att_unregister_all(att); bt_att_unref(att); return false; } static int bt_att_chan_get_security(struct bt_att_chan *chan) { struct bt_security sec; socklen_t len; if (chan->type == BT_ATT_LOCAL) return chan->sec_level; memset(&sec, 0, sizeof(sec)); len = sizeof(sec); if (getsockopt(chan->fd, SOL_BLUETOOTH, BT_SECURITY, &sec, &len) < 0) return -EIO; return sec.level; } static bool bt_att_chan_set_security(struct bt_att_chan *chan, int level) { struct bt_security sec; if (chan->type == BT_ATT_LOCAL) { chan->sec_level = level; return true; } /* Check if security level has already been set, if the security level * is higher it shall satisfy the request since we never want to * downgrade security. */ if (level <= bt_att_chan_get_security(chan)) return true; memset(&sec, 0, sizeof(sec)); sec.level = level; if (setsockopt(chan->fd, SOL_BLUETOOTH, BT_SECURITY, &sec, sizeof(sec)) < 0) return false; return true; } static bool change_security(struct bt_att_chan *chan, uint8_t ecode) { int security; if (chan->sec_level != BT_ATT_SECURITY_AUTO) return false; security = bt_att_chan_get_security(chan); if (ecode == BT_ATT_ERROR_INSUFFICIENT_ENCRYPTION && security < BT_ATT_SECURITY_MEDIUM) { security = BT_ATT_SECURITY_MEDIUM; } else if (ecode == BT_ATT_ERROR_AUTHENTICATION) { if (security < BT_ATT_SECURITY_MEDIUM) security = BT_ATT_SECURITY_MEDIUM; else if (security < BT_ATT_SECURITY_HIGH) security = BT_ATT_SECURITY_HIGH; else if (security < BT_ATT_SECURITY_FIPS) security = BT_ATT_SECURITY_FIPS; else return false; } else { return false; } return bt_att_chan_set_security(chan, security); } static bool handle_error_rsp(struct bt_att_chan *chan, uint8_t *pdu, ssize_t pdu_len, uint8_t *opcode) { struct bt_att *att = chan->att; const struct bt_att_pdu_error_rsp *rsp; struct att_send_op *op = chan->pending_req; if (pdu_len != sizeof(*rsp)) { *opcode = 0; return false; } rsp = (void *) pdu; *opcode = rsp->opcode; /* If operation has already been marked as retry don't attempt to change * the security again. */ if (op->retry) return false; /* Attempt to change security */ if (!change_security(chan, rsp->ecode)) return false; /* Remove timeout_id if outstanding */ if (op->timeout_id) { timeout_remove(op->timeout_id); op->timeout_id = 0; } DBG(att, "(chan %p) Retrying operation %p", chan, op); chan->pending_req = NULL; op->retry = true; /* Push operation back to channel queue */ return queue_push_head(chan->queue, op); } static void handle_rsp(struct bt_att_chan *chan, uint8_t opcode, uint8_t *pdu, ssize_t pdu_len) { struct bt_att *att = chan->att; struct att_send_op *op = chan->pending_req; uint8_t req_opcode; uint8_t rsp_opcode; uint8_t *rsp_pdu = NULL; uint16_t rsp_pdu_len = 0; /* * If no request is pending, then the response is unexpected. Disconnect * the bearer. */ if (!op) { DBG(att, "(chan %p) Received unexpected ATT response", chan); io_shutdown(chan->io); return; } /* * If the received response doesn't match the pending request, or if * the request is malformed, end the current request with failure. */ if (opcode == BT_ATT_OP_ERROR_RSP) { /* Return if error response cause a retry */ if (handle_error_rsp(chan, pdu, pdu_len, &req_opcode)) { wakeup_chan_writer(chan, NULL); return; } } else if (!(req_opcode = get_req_opcode(opcode))) goto fail; if (req_opcode != op->opcode) goto fail; rsp_opcode = opcode; if (pdu_len > 0) { rsp_pdu = pdu; rsp_pdu_len = pdu_len; } goto done; fail: DBG(att, "(chan %p) Failed to handle response PDU; opcode: " "0x%02x", chan, opcode); rsp_opcode = BT_ATT_OP_ERROR_RSP; done: if (op->callback) op->callback(rsp_opcode, rsp_pdu, rsp_pdu_len, op->user_data); destroy_att_send_op(op); chan->pending_req = NULL; wakeup_chan_writer(chan, NULL); } static void handle_conf(struct bt_att_chan *chan, uint8_t *pdu, ssize_t pdu_len) { struct bt_att *att = chan->att; struct att_send_op *op = chan->pending_ind; /* * Disconnect the bearer if the confirmation is unexpected or the PDU is * invalid. */ if (!op || pdu_len) { DBG(att, "(chan %p) Received unexpected/invalid ATT " "confirmation", chan); io_shutdown(chan->io); return; } if (op->callback) op->callback(BT_ATT_OP_HANDLE_CONF, NULL, 0, op->user_data); destroy_att_send_op(op); chan->pending_ind = NULL; wakeup_chan_writer(chan, NULL); } struct notify_data { uint8_t opcode; uint8_t *pdu; ssize_t pdu_len; bool handler_found; }; static bool opcode_match(uint8_t opcode, uint8_t test_opcode) { enum att_op_type op_type = get_op_type(test_opcode); if (opcode == BT_ATT_ALL_REQUESTS && (op_type == ATT_OP_TYPE_REQ || op_type == ATT_OP_TYPE_CMD)) return true; return opcode == test_opcode; } static void respond_not_supported(struct bt_att *att, uint8_t opcode) { struct bt_att_pdu_error_rsp pdu; pdu.opcode = opcode; pdu.handle = 0x0000; pdu.ecode = BT_ATT_ERROR_REQUEST_NOT_SUPPORTED; bt_att_send(att, BT_ATT_OP_ERROR_RSP, &pdu, sizeof(pdu), NULL, NULL, NULL); } static bool handle_signed(struct bt_att *att, uint8_t *pdu, ssize_t pdu_len) { uint8_t *signature; uint32_t sign_cnt; struct sign_info *sign; uint8_t opcode = pdu[0]; /* Check if there is enough data for a signature */ if (pdu_len < 3 + BT_ATT_SIGNATURE_LEN) goto fail; sign = att->remote_sign; if (!sign) goto fail; signature = pdu + (pdu_len - BT_ATT_SIGNATURE_LEN); sign_cnt = get_le32(signature); /* Validate counter */ if (!sign->counter(&sign_cnt, sign->user_data)) goto fail; /* Verify received signature */ if (!bt_crypto_verify_att_sign(att->crypto, sign->key, pdu, pdu_len)) goto fail; return true; fail: DBG(att, "ATT failed to verify signature: 0x%02x", opcode); return false; } static void handle_notify(struct bt_att_chan *chan, uint8_t *pdu, ssize_t pdu_len) { struct bt_att *att = chan->att; const struct queue_entry *entry; bool found; uint8_t opcode = pdu[0]; bt_att_ref(att); found = false; entry = queue_get_entries(att->notify_list); while (entry) { struct att_notify *notify = entry->data; entry = entry->next; if (!opcode_match(notify->opcode, opcode)) continue; if ((opcode & ATT_OP_SIGNED_MASK) && att->crypto) { if (!handle_signed(att, pdu, pdu_len)) return; pdu_len -= BT_ATT_SIGNATURE_LEN; } /* BLUETOOTH CORE SPECIFICATION Version 5.1 | Vol 3, Part G * page 2370 * * 4.3.1 Exchange MTU * * This sub-procedure shall not be used on a BR/EDR physical * link since the MTU size is negotiated using L2CAP channel * configuration procedures. */ if (bt_att_get_link_type(att) == BT_ATT_BREDR || chan->type == BT_ATT_EATT) { switch (opcode) { case BT_ATT_OP_MTU_REQ: goto not_supported; } } found = true; if (notify->callback) notify->callback(chan, chan->mtu, opcode, pdu + 1, pdu_len - 1, notify->user_data); /* callback could remove all entries from notify list */ if (queue_isempty(att->notify_list)) break; } not_supported: /* * If this was not a command and no handler was registered for it, * respond with "Not Supported" */ if (!found && get_op_type(opcode) != ATT_OP_TYPE_CMD) respond_not_supported(att, opcode); bt_att_unref(att); } static bool can_read_data(struct io *io, void *user_data) { struct bt_att_chan *chan = user_data; struct bt_att *att = chan->att; uint8_t opcode; uint8_t *pdu; ssize_t bytes_read; bytes_read = read(chan->fd, chan->buf, chan->mtu); if (bytes_read < 0) return false; VERBOSE(att, "(chan %p) ATT received: %zd", chan, bytes_read); att_hexdump(att, '>', chan->buf, bytes_read); if (bytes_read < ATT_MIN_PDU_LEN) return true; pdu = chan->buf; opcode = pdu[0]; bt_att_ref(att); /* Act on the received PDU based on the opcode type */ switch (get_op_type(opcode)) { case ATT_OP_TYPE_RSP: VERBOSE(att, "(chan %p) ATT response received: 0x%02x", chan, opcode); handle_rsp(chan, opcode, pdu + 1, bytes_read - 1); break; case ATT_OP_TYPE_CONF: VERBOSE(att, "(chan %p) ATT confirmation received: 0x%02x", chan, opcode); handle_conf(chan, pdu + 1, bytes_read - 1); break; case ATT_OP_TYPE_REQ: /* * If a request is currently pending, then the sequential * protocol was violated. Disconnect the bearer, which will * promptly notify the upper layer via disconnect handlers. */ if (chan->in_req) { DBG(att, "(chan %p) Received request while " "another is pending: 0x%02x", chan, opcode); io_shutdown(chan->io); bt_att_unref(chan->att); return false; } chan->in_req = true; /* fall through */ case ATT_OP_TYPE_CMD: case ATT_OP_TYPE_NFY: case ATT_OP_TYPE_UNKNOWN: case ATT_OP_TYPE_IND: /* fall through */ default: /* For all other opcodes notify the upper layer of the PDU and * let them act on it. */ DBG(att, "(chan %p) ATT PDU received: 0x%02x", chan, opcode); handle_notify(chan, pdu, bytes_read); break; } bt_att_unref(att); return true; } static bool is_io_l2cap_based(int fd) { int domain; int proto; int err; socklen_t len; domain = 0; len = sizeof(domain); err = getsockopt(fd, SOL_SOCKET, SO_DOMAIN, &domain, &len); if (err < 0) return false; if (domain != AF_BLUETOOTH) return false; proto = 0; len = sizeof(proto); err = getsockopt(fd, SOL_SOCKET, SO_PROTOCOL, &proto, &len); if (err < 0) return false; return proto == BTPROTO_L2CAP; } static void bt_att_free(struct bt_att *att) { bt_crypto_unref(att->crypto); if (att->timeout_destroy) att->timeout_destroy(att->timeout_data); if (att->debug_destroy) att->debug_destroy(att->debug_data); free(att->local_sign); free(att->remote_sign); queue_destroy(att->req_queue, NULL); queue_destroy(att->ind_queue, NULL); queue_destroy(att->write_queue, NULL); queue_destroy(att->notify_list, NULL); queue_destroy(att->disconn_list, NULL); queue_destroy(att->exchange_list, NULL); queue_destroy(att->chans, bt_att_chan_free); free(att); } static uint16_t io_get_mtu(int fd) { socklen_t len; struct l2cap_options l2o; len = sizeof(l2o); if (!getsockopt(fd, SOL_L2CAP, L2CAP_OPTIONS, &l2o, &len)) return l2o.omtu; if (!getsockopt(fd, SOL_BLUETOOTH, BT_SNDMTU, &l2o.omtu, &len)) return l2o.omtu; return 0; } static uint8_t io_get_type(int fd) { struct sockaddr_l2 src; socklen_t len; if (!is_io_l2cap_based(fd)) return BT_ATT_LOCAL; len = sizeof(src); memset(&src, 0, len); if (getsockname(fd, (void *)&src, &len) < 0) return -errno; if (src.l2_bdaddr_type == BDADDR_BREDR) return BT_ATT_BREDR; return BT_ATT_LE; } static struct bt_att_chan *bt_att_chan_new(int fd, uint8_t type) { struct bt_att_chan *chan; if (fd < 0) return NULL; chan = new0(struct bt_att_chan, 1); chan->fd = fd; chan->io = io_new(fd); if (!chan->io) goto fail; if (!io_set_read_handler(chan->io, can_read_data, chan, NULL)) goto fail; if (!io_set_disconnect_handler(chan->io, disconnect_cb, chan, NULL)) goto fail; chan->type = type; switch (chan->type) { case BT_ATT_LOCAL: chan->sec_level = BT_ATT_SECURITY_LOW; /* fall through */ case BT_ATT_LE: chan->mtu = BT_ATT_DEFAULT_LE_MTU; break; default: chan->mtu = io_get_mtu(chan->fd); } if (chan->mtu < BT_ATT_DEFAULT_LE_MTU) goto fail; chan->buf = malloc(chan->mtu); if (!chan->buf) goto fail; chan->queue = queue_new(); return chan; fail: bt_att_chan_free(chan); return NULL; } static void bt_att_attach_chan(struct bt_att *att, struct bt_att_chan *chan) { /* Push to head as EATT channels have higher priority */ queue_push_head(att->chans, chan); chan->att = att; if (chan->mtu > att->mtu) att->mtu = chan->mtu; io_set_close_on_destroy(chan->io, att->close_on_unref); DBG(att, "Channel %p attached", chan); wakeup_chan_writer(chan, NULL); } struct bt_att *bt_att_new(int fd, bool ext_signed) { struct bt_att *att; struct bt_att_chan *chan; chan = bt_att_chan_new(fd, io_get_type(fd)); if (!chan) return NULL; att = new0(struct bt_att, 1); att->chans = queue_new(); att->mtu = chan->mtu; /* crypto is optional, if not available leave it NULL */ if (!ext_signed) att->crypto = bt_crypto_new(); att->req_queue = queue_new(); att->ind_queue = queue_new(); att->write_queue = queue_new(); att->notify_list = queue_new(); att->disconn_list = queue_new(); att->exchange_list = queue_new(); bt_att_attach_chan(att, chan); return bt_att_ref(att); } struct bt_att *bt_att_ref(struct bt_att *att) { if (!att) return NULL; __sync_fetch_and_add(&att->ref_count, 1); return att; } void bt_att_unref(struct bt_att *att) { if (!att) return; if (__sync_sub_and_fetch(&att->ref_count, 1)) return; bt_att_unregister_all(att); bt_att_cancel_all(att); bt_att_free(att); } bool bt_att_set_close_on_unref(struct bt_att *att, bool do_close) { const struct queue_entry *entry; if (!att) return false; att->close_on_unref = do_close; for (entry = queue_get_entries(att->chans); entry; entry = entry->next) { struct bt_att_chan *chan = entry->data; if (!io_set_close_on_destroy(chan->io, do_close)) return false; } return true; } int bt_att_attach_fd(struct bt_att *att, int fd) { struct bt_att_chan *chan; if (!att || fd < 0) return -EINVAL; chan = bt_att_chan_new(fd, BT_ATT_EATT); if (!chan) return -EINVAL; bt_att_attach_chan(att, chan); return 0; } int bt_att_get_fd(struct bt_att *att) { struct bt_att_chan *chan; if (!att) return -1; if (queue_isempty(att->chans)) return -ENOTCONN; chan = queue_peek_tail(att->chans); return chan->fd; } int bt_att_get_channels(struct bt_att *att) { if (!att) return 0; return queue_length(att->chans); } bool bt_att_set_debug(struct bt_att *att, uint8_t level, bt_att_debug_func_t callback, void *user_data, bt_att_destroy_func_t destroy) { if (!att) return false; if (att->debug_destroy) att->debug_destroy(att->debug_data); att->debug_level = level; att->debug_callback = callback; att->debug_destroy = destroy; att->debug_data = user_data; return true; } uint16_t bt_att_get_mtu(struct bt_att *att) { if (!att) return 0; return att->mtu; } static void exchange_handler(void *data, void *user_data) { struct att_exchange *exchange = data; uint16_t mtu = PTR_TO_INT(user_data); if (exchange->removed) return; if (exchange->callback) exchange->callback(mtu, exchange->user_data); } bool bt_att_set_mtu(struct bt_att *att, uint16_t mtu) { struct bt_att_chan *chan; void *buf; if (!att) return false; if (mtu < BT_ATT_DEFAULT_LE_MTU) return false; /* Original channel is always the last */ chan = queue_peek_tail(att->chans); if (!chan) return -ENOTCONN; buf = malloc(mtu); if (!buf) return false; free(chan->buf); chan->mtu = mtu; chan->buf = buf; if (chan->mtu > att->mtu) { att->mtu = chan->mtu; queue_foreach(att->exchange_list, exchange_handler, INT_TO_PTR(att->mtu)); } return true; } uint8_t bt_att_get_link_type(struct bt_att *att) { struct bt_att_chan *chan; if (!att) return -EINVAL; chan = queue_peek_tail(att->chans); if (!chan) return -ENOTCONN; return chan->type; } bool bt_att_set_timeout_cb(struct bt_att *att, bt_att_timeout_func_t callback, void *user_data, bt_att_destroy_func_t destroy) { if (!att) return false; if (att->timeout_destroy) att->timeout_destroy(att->timeout_data); att->timeout_callback = callback; att->timeout_destroy = destroy; att->timeout_data = user_data; return true; } unsigned int bt_att_register_disconnect(struct bt_att *att, bt_att_disconnect_func_t callback, void *user_data, bt_att_destroy_func_t destroy) { struct att_disconn *disconn; if (!att || queue_isempty(att->chans)) return 0; disconn = new0(struct att_disconn, 1); disconn->callback = callback; disconn->destroy = destroy; disconn->user_data = user_data; if (att->next_reg_id < 1) att->next_reg_id = 1; disconn->id = att->next_reg_id++; if (!queue_push_tail(att->disconn_list, disconn)) { free(disconn); return 0; } return disconn->id; } bool bt_att_unregister_disconnect(struct bt_att *att, unsigned int id) { struct att_disconn *disconn; if (!att || !id) return false; /* Check if disconnect is running */ if (queue_isempty(att->chans)) { disconn = queue_find(att->disconn_list, match_disconn_id, UINT_TO_PTR(id)); if (!disconn) return false; disconn->removed = true; return true; } disconn = queue_remove_if(att->disconn_list, match_disconn_id, UINT_TO_PTR(id)); if (!disconn) return false; destroy_att_disconn(disconn); return true; } unsigned int bt_att_register_exchange(struct bt_att *att, bt_att_exchange_func_t callback, void *user_data, bt_att_destroy_func_t destroy) { struct att_exchange *mtu; if (!att || queue_isempty(att->chans)) return 0; mtu = new0(struct att_exchange, 1); mtu->callback = callback; mtu->destroy = destroy; mtu->user_data = user_data; if (att->next_reg_id < 1) att->next_reg_id = 1; mtu->id = att->next_reg_id++; if (!queue_push_tail(att->exchange_list, mtu)) { free(att); return 0; } return mtu->id; } bool bt_att_unregister_exchange(struct bt_att *att, unsigned int id) { struct att_exchange *mtu; if (!att || !id) return false; /* Check if disconnect is running */ if (queue_isempty(att->chans)) { mtu = queue_find(att->exchange_list, match_disconn_id, UINT_TO_PTR(id)); if (!mtu) return false; mtu->removed = true; return true; } mtu = queue_remove_if(att->exchange_list, match_disconn_id, UINT_TO_PTR(id)); if (!mtu) return false; destroy_att_exchange(mtu); return true; } unsigned int bt_att_send(struct bt_att *att, uint8_t opcode, const void *pdu, uint16_t length, bt_att_response_func_t callback, void *user_data, bt_att_destroy_func_t destroy) { struct att_send_op *op; bool result; if (!att || queue_isempty(att->chans)) return 0; op = create_att_send_op(att, opcode, pdu, length, callback, user_data, destroy); if (!op) return 0; if (att->next_send_id < 1) att->next_send_id = 1; op->id = att->next_send_id++; /* Always use fixed channel for BT_ATT_OP_MTU_REQ */ if (opcode == BT_ATT_OP_MTU_REQ) { struct bt_att_chan *chan = queue_peek_tail(att->chans); result = queue_push_tail(chan->queue, op); goto done; } /* Add the op to the correct queue based on its type */ switch (op->type) { case ATT_OP_TYPE_REQ: result = queue_push_tail(att->req_queue, op); break; case ATT_OP_TYPE_IND: result = queue_push_tail(att->ind_queue, op); break; case ATT_OP_TYPE_CMD: case ATT_OP_TYPE_NFY: case ATT_OP_TYPE_UNKNOWN: case ATT_OP_TYPE_RSP: case ATT_OP_TYPE_CONF: default: result = queue_push_tail(att->write_queue, op); break; } done: if (!result) { free(op->pdu); free(op); return 0; } wakeup_writer(att); return op->id; } int bt_att_resend(struct bt_att *att, unsigned int id, uint8_t opcode, const void *pdu, uint16_t length, bt_att_response_func_t callback, void *user_data, bt_att_destroy_func_t destroy) { const struct queue_entry *entry; struct att_send_op *op; bool result; if (!att || !id) return -EINVAL; /* Lookup request on each channel */ for (entry = queue_get_entries(att->chans); entry; entry = entry->next) { struct bt_att_chan *chan = entry->data; if (chan->pending_req && chan->pending_req->id == id) break; } if (!entry) return -ENOENT; /* Only allow requests to be resend */ if (get_op_type(opcode) != ATT_OP_TYPE_REQ) return -EOPNOTSUPP; op = create_att_send_op(att, opcode, pdu, length, callback, user_data, destroy); if (!op) return -ENOMEM; op->id = id; switch (opcode) { /* Only prepend requests that could be a continuation */ case BT_ATT_OP_READ_BLOB_REQ: case BT_ATT_OP_PREP_WRITE_REQ: case BT_ATT_OP_EXEC_WRITE_REQ: result = queue_push_head(att->req_queue, op); break; default: result = queue_push_tail(att->req_queue, op); break; } if (!result) { free(op->pdu); free(op); return -ENOMEM; } wakeup_writer(att); return 0; } unsigned int bt_att_chan_send(struct bt_att_chan *chan, uint8_t opcode, const void *pdu, uint16_t len, bt_att_response_func_t callback, void *user_data, bt_att_destroy_func_t destroy) { struct att_send_op *op; if (!chan || !chan->att) return -EINVAL; op = create_att_send_op(chan->att, opcode, pdu, len, callback, user_data, destroy); if (!op) return -EINVAL; if (!queue_push_tail(chan->queue, op)) { free(op->pdu); free(op); return 0; } wakeup_chan_writer(chan, NULL); return op->id; } static bool match_op_id(const void *a, const void *b) { const struct att_send_op *op = a; unsigned int id = PTR_TO_UINT(b); return op->id == id; } bool bt_att_chan_cancel(struct bt_att_chan *chan, unsigned int id) { struct att_send_op *op; if (chan->pending_req && chan->pending_req->id == id) { /* Don't cancel the pending request; remove it's handlers */ cancel_att_send_op(chan->pending_req); return true; } if (chan->pending_ind && chan->pending_ind->id == id) { /* Don't cancel the pending indication; remove it's handlers. */ cancel_att_send_op(chan->pending_ind); return true; } op = queue_remove_if(chan->queue, match_op_id, UINT_TO_PTR(id)); if (!op) return false; destroy_att_send_op(op); wakeup_chan_writer(chan, NULL); return true; } static bool bt_att_disc_cancel(struct bt_att *att, unsigned int id) { struct att_send_op *op; op = queue_find(att->req_queue, match_op_id, UINT_TO_PTR(id)); if (op) goto done; op = queue_find(att->ind_queue, match_op_id, UINT_TO_PTR(id)); if (op) goto done; op = queue_find(att->write_queue, match_op_id, UINT_TO_PTR(id)); done: if (!op) return false; /* Just cancel since disconnect_cb will be cleaning up */ cancel_att_send_op(op); return true; } bool bt_att_cancel(struct bt_att *att, unsigned int id) { const struct queue_entry *entry; struct att_send_op *op; if (!att || !id) return false; /* Lookuo request on each channel first */ for (entry = queue_get_entries(att->chans); entry; entry = entry->next) { struct bt_att_chan *chan = entry->data; if (bt_att_chan_cancel(chan, id)) return true; } if (att->in_disc) return bt_att_disc_cancel(att, id); op = queue_remove_if(att->req_queue, match_op_id, UINT_TO_PTR(id)); if (op) goto done; op = queue_remove_if(att->ind_queue, match_op_id, UINT_TO_PTR(id)); if (op) goto done; op = queue_remove_if(att->write_queue, match_op_id, UINT_TO_PTR(id)); if (op) goto done; if (!op) return false; done: destroy_att_send_op(op); wakeup_writer(att); return true; } bool bt_att_cancel_all(struct bt_att *att) { const struct queue_entry *entry; if (!att) return false; queue_remove_all(att->req_queue, NULL, NULL, destroy_att_send_op); queue_remove_all(att->ind_queue, NULL, NULL, destroy_att_send_op); queue_remove_all(att->write_queue, NULL, NULL, destroy_att_send_op); for (entry = queue_get_entries(att->chans); entry; entry = entry->next) { struct bt_att_chan *chan = entry->data; if (chan->pending_req) /* Don't cancel the pending request; remove it's * handlers */ cancel_att_send_op(chan->pending_req); if (chan->pending_ind) /* Don't cancel the pending request; remove it's * handlers */ cancel_att_send_op(chan->pending_ind); } return true; } static uint8_t att_ecode_from_error(int err) { /* * If the error fits in a single byte, treat it as an ATT protocol * error as is. Since "0" is not a valid ATT protocol error code, we map * that to UNLIKELY below. */ if (err > 0 && err < UINT8_MAX) return err; /* * Since we allow UNIX errnos, map them to appropriate ATT protocol * and "Common Profile and Service" error codes. */ switch (err) { case -ENOENT: return BT_ATT_ERROR_INVALID_HANDLE; case -ENOMEM: return BT_ATT_ERROR_INSUFFICIENT_RESOURCES; case -EALREADY: return BT_ERROR_ALREADY_IN_PROGRESS; case -EOVERFLOW: return BT_ERROR_OUT_OF_RANGE; } return BT_ATT_ERROR_UNLIKELY; } int bt_att_chan_send_error_rsp(struct bt_att_chan *chan, uint8_t opcode, uint16_t handle, int error) { struct bt_att_pdu_error_rsp pdu; uint8_t ecode; if (!chan || !chan->att || !opcode) return -EINVAL; ecode = att_ecode_from_error(error); memset(&pdu, 0, sizeof(pdu)); pdu.opcode = opcode; put_le16(handle, &pdu.handle); pdu.ecode = ecode; return bt_att_chan_send_rsp(chan, BT_ATT_OP_ERROR_RSP, &pdu, sizeof(pdu)); } unsigned int bt_att_register(struct bt_att *att, uint8_t opcode, bt_att_notify_func_t callback, void *user_data, bt_att_destroy_func_t destroy) { struct att_notify *notify; if (!att || !callback || queue_isempty(att->chans)) return 0; notify = new0(struct att_notify, 1); notify->opcode = opcode; notify->callback = callback; notify->destroy = destroy; notify->user_data = user_data; if (att->next_reg_id < 1) att->next_reg_id = 1; notify->id = att->next_reg_id++; if (!queue_push_tail(att->notify_list, notify)) { free(notify); return 0; } return notify->id; } bool bt_att_unregister(struct bt_att *att, unsigned int id) { struct att_notify *notify; if (!att || !id) return false; notify = queue_remove_if(att->notify_list, match_notify_id, UINT_TO_PTR(id)); if (!notify) return false; destroy_att_notify(notify); return true; } bool bt_att_unregister_all(struct bt_att *att) { if (!att) return false; queue_remove_all(att->notify_list, NULL, NULL, destroy_att_notify); queue_remove_all(att->disconn_list, NULL, NULL, destroy_att_disconn); queue_remove_all(att->exchange_list, NULL, NULL, destroy_att_exchange); return true; } int bt_att_get_security(struct bt_att *att, uint8_t *enc_size) { struct bt_att_chan *chan; int ret; if (!att) return -EINVAL; chan = queue_peek_tail(att->chans); if (!chan) return -ENOTCONN; ret = bt_att_chan_get_security(chan); if (ret < 0) return ret; if (enc_size) *enc_size = att->enc_size; return ret; } bool bt_att_set_security(struct bt_att *att, int level) { struct bt_att_chan *chan; if (!att || level < BT_ATT_SECURITY_AUTO || level > BT_ATT_SECURITY_HIGH) return false; chan = queue_peek_tail(att->chans); if (!chan) return -ENOTCONN; return bt_att_chan_set_security(chan, level); } void bt_att_set_enc_key_size(struct bt_att *att, uint8_t enc_size) { if (!att) return; att->enc_size = enc_size; } static bool sign_set_key(struct sign_info **sign, uint8_t key[16], bt_att_counter_func_t func, void *user_data) { if (!(*sign)) *sign = new0(struct sign_info, 1); (*sign)->counter = func; (*sign)->user_data = user_data; memcpy((*sign)->key, key, 16); return true; } bool bt_att_set_local_key(struct bt_att *att, uint8_t sign_key[16], bt_att_counter_func_t func, void *user_data) { if (!att) return false; return sign_set_key(&att->local_sign, sign_key, func, user_data); } bool bt_att_set_remote_key(struct bt_att *att, uint8_t sign_key[16], bt_att_counter_func_t func, void *user_data) { if (!att) return false; return sign_set_key(&att->remote_sign, sign_key, func, user_data); } bool bt_att_has_crypto(struct bt_att *att) { if (!att) return false; return att->crypto ? true : false; } bool bt_att_set_retry(struct bt_att *att, unsigned int id, bool retry) { struct att_send_op *op; if (!id) return false; op = queue_find(att->req_queue, match_op_id, UINT_TO_PTR(id)); if (op) goto done; op = queue_find(att->ind_queue, match_op_id, UINT_TO_PTR(id)); if (op) goto done; op = queue_find(att->write_queue, match_op_id, UINT_TO_PTR(id)); done: if (!op) return false; op->retry = !retry; return true; } bluez-5.82/src/shared/PaxHeaders/mainloop-notify.c0000644000000000000000000000005014643061455017203 xustar0020 atime=1743515944 20 ctime=1743591277 bluez-5.82/src/shared/mainloop-notify.c0000644000000000000000000000651514643061455016673 0ustar00rootroot// SPDX-License-Identifier: LGPL-2.1-or-later /* * * BlueZ - Bluetooth protocol stack for Linux * * Copyright (C) 2018 Intel Corporation. All rights reserved. * * */ #ifdef HAVE_CONFIG_H #include #endif #define _GNU_SOURCE #include #include #include #include #include #include #include #include #include #include #include #include "mainloop.h" #include "mainloop-notify.h" #include "timeout.h" #include "util.h" #include "io.h" #define WATCHDOG_TRIGGER_FREQ 2 static int notify_fd = -1; static unsigned int watchdog; struct signal_data { struct io *io; mainloop_signal_func func; void *user_data; }; static struct signal_data *signal_data; static bool watchdog_callback(void *user_data) { mainloop_sd_notify("WATCHDOG=1"); return true; } void mainloop_notify_init(void) { const char *sock; struct sockaddr_un addr; const char *watchdog_usec; int msec; sock = getenv("NOTIFY_SOCKET"); if (!sock) return; /* check for abstract socket or absolute path */ if (sock[0] != '@' && sock[0] != '/') return; notify_fd = socket(AF_UNIX, SOCK_DGRAM | SOCK_CLOEXEC, 0); if (notify_fd < 0) return; memset(&addr, 0, sizeof(addr)); addr.sun_family = AF_UNIX; strncpy(addr.sun_path, sock, sizeof(addr.sun_path) - 1); if (addr.sun_path[0] == '@') addr.sun_path[0] = '\0'; if (connect(notify_fd, (struct sockaddr *) &addr, sizeof(addr)) < 0) { close(notify_fd); notify_fd = -1; return; } watchdog_usec = getenv("WATCHDOG_USEC"); if (!watchdog_usec) return; msec = atoi(watchdog_usec) / 1000; if (msec < 0) return; watchdog = timeout_add(msec / WATCHDOG_TRIGGER_FREQ, watchdog_callback, NULL, NULL); } void mainloop_notify_exit(void) { if (notify_fd > 0) { close(notify_fd); notify_fd = -1; } timeout_remove(watchdog); } int mainloop_sd_notify(const char *state) { int err; if (notify_fd <= 0) return -ENOTCONN; err = send(notify_fd, state, strlen(state), MSG_NOSIGNAL); if (err < 0) return -errno; return err; } static bool signal_read(struct io *io, void *user_data) { struct signal_data *data = user_data; struct signalfd_siginfo si; ssize_t result; int fd; fd = io_get_fd(io); result = read(fd, &si, sizeof(si)); if (result != sizeof(si) || si.ssi_signo > INT_MAX) return false; if (data && data->func) data->func(si.ssi_signo, data->user_data); return true; } static struct io *setup_signalfd(void *user_data) { struct io *io; sigset_t mask; int fd; sigemptyset(&mask); sigaddset(&mask, SIGINT); sigaddset(&mask, SIGTERM); sigaddset(&mask, SIGUSR2); sigaddset(&mask, SIGCHLD); if (sigprocmask(SIG_BLOCK, &mask, NULL) < 0) return NULL; fd = signalfd(-1, &mask, SFD_NONBLOCK | SFD_CLOEXEC); if (fd < 0) return NULL; io = io_new(fd); io_set_close_on_destroy(io, true); io_set_read_handler(io, signal_read, user_data, free); return io; } int mainloop_run_with_signal(mainloop_signal_func func, void *user_data) { struct signal_data *data; struct io *io; int ret; if (!func) return -EINVAL; data = new0(struct signal_data, 1); data->func = func; data->user_data = user_data; io = setup_signalfd(data); if (!io) { free(data); return -errno; } ret = mainloop_run(); io_destroy(io); free(signal_data); return ret; } bluez-5.82/src/shared/PaxHeaders/shell.h0000644000000000000000000000005014711225434015166 xustar0020 atime=1743515768 20 ctime=1743591277 bluez-5.82/src/shared/shell.h0000644000000000000000000000462514711225434014656 0ustar00rootroot/* SPDX-License-Identifier: LGPL-2.1-or-later */ /* * * BlueZ - Bluetooth protocol stack for Linux * * Copyright (C) 2017 Intel Corporation. All rights reserved. * * */ #include #include #define COLOR_OFF "\x1B[0m" #define COLOR_RED "\x1B[0;91m" #define COLOR_GREEN "\x1B[0;92m" #define COLOR_YELLOW "\x1B[0;93m" #define COLOR_BLUE "\x1B[0;94m" #define COLOR_BOLDGRAY "\x1B[1;30m" #define COLOR_BOLDWHITE "\x1B[1;37m" #define COLOR_HIGHLIGHT "\x1B[1;39m" struct bt_shell_menu; typedef void (*bt_shell_menu_cb_t)(int argc, char *argv[]); typedef char * (*bt_shell_menu_gen_t)(const char *text, int state); typedef void (*bt_shell_menu_disp_t) (char **matches, int num_matches, int max_length); typedef void (*bt_shell_prompt_input_func) (const char *input, void *user_data); typedef bool (*bt_shell_menu_exists_t) (const struct bt_shell_menu *menu); struct bt_shell_menu_entry { const char *cmd; const char *arg; bt_shell_menu_cb_t func; const char *desc; bt_shell_menu_gen_t gen; bt_shell_menu_disp_t disp; bt_shell_menu_exists_t exists; }; struct bt_shell_menu { const char *name; const char *desc; const struct bt_shell_menu_entry entries[]; }; struct bt_shell_opt { const struct option *options; size_t optno; const char *optstr; const char ***optarg; const char **help; }; void bt_shell_init(int argc, char **argv, const struct bt_shell_opt *opt); int bt_shell_run(void); int bt_shell_exec(const char *input); void bt_shell_quit(int status); void bt_shell_noninteractive_quit(int status); bool bt_shell_set_menu(const struct bt_shell_menu *menu); bool bt_shell_add_submenu(const struct bt_shell_menu *menu); bool bt_shell_remove_submenu(const struct bt_shell_menu *menu); void bt_shell_set_prompt(const char *string, const char *color); void bt_shell_printf(const char *fmt, ...) __attribute__((format(printf, 1, 2))); void bt_shell_echo(const char *fmt, ...) __attribute__((format(printf, 1, 2))); void bt_shell_hexdump(const unsigned char *buf, size_t len); void bt_shell_usage(void); void bt_shell_prompt_input(const char *label, const char *msg, bt_shell_prompt_input_func func, void *user_data); int bt_shell_release_prompt(const char *input); bool bt_shell_attach(int fd); bool bt_shell_detach(void); void bt_shell_set_env(const char *name, void *value); void *bt_shell_get_env(const char *name); int bt_shell_get_timeout(void); void bt_shell_cleanup(void); bluez-5.82/src/shared/PaxHeaders/csip.h0000644000000000000000000000005014536422313015015 xustar0020 atime=1743515928 20 ctime=1743591277 bluez-5.82/src/shared/csip.h0000644000000000000000000000403414536422313014477 0ustar00rootroot/* SPDX-License-Identifier: LGPL-2.1-or-later */ /* * * BlueZ - Bluetooth protocol stack for Linux * * Copyright (C) 2022 Intel Corporation. All rights reserved. * */ #include #include #include "src/shared/io.h" #ifndef __packed #define __packed __attribute__((packed)) #endif struct bt_csip; enum { BT_CSIP_SIRK_ENCRYPT = 0x00, BT_CSIP_SIRK_CLEARTEXT = 0x01 }; typedef void (*bt_csip_ready_func_t)(struct bt_csip *csip, void *user_data); typedef void (*bt_csip_destroy_func_t)(void *user_data); typedef void (*bt_csip_debug_func_t)(const char *str, void *user_data); typedef void (*bt_csip_func_t)(struct bt_csip *csip, void *user_data); typedef bool (*bt_csip_encrypt_func_t)(struct bt_att *att, uint8_t k[16]); typedef bool (*bt_csip_sirk_func_t)(struct bt_csip *csip, uint8_t type, uint8_t k[16], uint8_t size, uint8_t rank, void *user_data); struct bt_csip *bt_csip_ref(struct bt_csip *csip); void bt_csip_unref(struct bt_csip *csip); bool bt_csip_attach(struct bt_csip *csip, struct bt_gatt_client *client); void bt_csip_detach(struct bt_csip *csip); bool bt_csip_set_debug(struct bt_csip *csip, bt_csip_debug_func_t func, void *user_data, bt_csip_destroy_func_t destroy); struct bt_att *bt_csip_get_att(struct bt_csip *csip); bool bt_csip_set_user_data(struct bt_csip *csip, void *user_data); /* Session related function */ unsigned int bt_csip_register(bt_csip_func_t added, bt_csip_func_t removed, void *user_data); bool bt_csip_unregister(unsigned int id); struct bt_csip *bt_csip_new(struct gatt_db *ldb, struct gatt_db *rdb); bool bt_csip_set_sirk(struct bt_csip *csip, bool encrypt, uint8_t k[16], uint8_t size, uint8_t rank, bt_csip_encrypt_func_t func); bool bt_csip_get_sirk(struct bt_csip *csip, uint8_t *type, uint8_t k[16], uint8_t *size, uint8_t *rank); unsigned int bt_csip_ready_register(struct bt_csip *csip, bt_csip_ready_func_t func, void *user_data, bt_csip_destroy_func_t destroy); bool bt_csip_ready_unregister(struct bt_csip *csip, unsigned int id); bluez-5.82/src/shared/PaxHeaders/gatt-db.c0000644000000000000000000000005014766002272015377 xustar0020 atime=1743515578 20 ctime=1743591277 bluez-5.82/src/shared/gatt-db.c0000644000000000000000000014732314766002272015072 0ustar00rootroot// SPDX-License-Identifier: LGPL-2.1-or-later /* * * BlueZ - Bluetooth protocol stack for Linux * * Copyright (C) 2014 Intel Corporation. All rights reserved. * * */ #ifdef HAVE_CONFIG_H #include #endif #include #include #include "lib/bluetooth.h" #include "lib/uuid.h" #include "src/shared/util.h" #include "src/shared/queue.h" #include "src/shared/timeout.h" #include "src/shared/att.h" #include "src/shared/gatt-db.h" #include "src/shared/crypto.h" #ifndef MAX #define MAX(a, b) ((a) > (b) ? (a) : (b)) #endif #define MAX_CHAR_DECL_VALUE_LEN 19 #define MAX_INCLUDED_VALUE_LEN 6 #define ATTRIBUTE_TIMEOUT 5000 #define HASH_UPDATE_TIMEOUT 100 static const bt_uuid_t primary_service_uuid = { .type = BT_UUID16, .value.u16 = GATT_PRIM_SVC_UUID }; static const bt_uuid_t secondary_service_uuid = { .type = BT_UUID16, .value.u16 = GATT_SND_SVC_UUID }; static const bt_uuid_t characteristic_uuid = { .type = BT_UUID16, .value.u16 = GATT_CHARAC_UUID }; static const bt_uuid_t included_service_uuid = { .type = BT_UUID16, .value.u16 = GATT_INCLUDE_UUID }; static const bt_uuid_t ext_desc_uuid = { .type = BT_UUID16, .value.u16 = GATT_CHARAC_EXT_PROPER_UUID }; static const bt_uuid_t ccc_uuid = { .type = BT_UUID16, .value.u16 = GATT_CLIENT_CHARAC_CFG_UUID }; struct gatt_db_ccc { gatt_db_read_t read_func; gatt_db_write_t write_func; gatt_db_notify_t notify_func; void *user_data; }; struct gatt_db { int ref_count; struct bt_crypto *crypto; uint8_t hash[16]; unsigned int hash_id; uint16_t last_handle; struct queue *services; struct queue *notify_list; unsigned int next_notify_id; gatt_db_authorize_cb_t authorize; void *authorize_data; struct gatt_db_ccc *ccc; }; struct notify { unsigned int id; gatt_db_attribute_cb_t service_added; gatt_db_attribute_cb_t service_removed; gatt_db_authorize_cb_t authorize_cb; gatt_db_destroy_func_t destroy; void *user_data; }; struct attribute_notify { unsigned int id; gatt_db_attribute_cb_t removed; gatt_db_destroy_func_t destroy; void *user_data; }; struct pending_read { struct gatt_db_attribute *attrib; unsigned int id; unsigned int timeout_id; gatt_db_attribute_read_t func; void *user_data; }; struct pending_write { struct gatt_db_attribute *attrib; unsigned int id; unsigned int timeout_id; gatt_db_attribute_write_t func; void *user_data; }; struct gatt_db_attribute { struct gatt_db_service *service; uint16_t handle; bt_uuid_t uuid; uint32_t permissions; uint16_t value_len; uint8_t *value; gatt_db_read_t read_func; gatt_db_write_t write_func; gatt_db_notify_t notify_func; void *user_data; unsigned int read_id; struct queue *pending_reads; unsigned int write_id; struct queue *pending_writes; unsigned int next_notify_id; struct queue *notify_list; }; struct gatt_db_service { struct gatt_db *db; bool active; bool claimed; uint16_t num_handles; struct gatt_db_attribute **attributes; }; static void set_attribute_data(struct gatt_db_attribute *attribute, gatt_db_read_t read_func, gatt_db_write_t write_func, uint32_t permissions, void *user_data) { attribute->permissions = permissions; attribute->read_func = read_func; attribute->write_func = write_func; attribute->user_data = user_data; } static void pending_read_result(struct pending_read *p, int err, const uint8_t *data, size_t length) { if (p->timeout_id > 0) timeout_remove(p->timeout_id); p->func(p->attrib, err, data, length, p->user_data); free(p); } static void pending_read_free(void *data) { struct pending_read *p = data; pending_read_result(p, -ECANCELED, NULL, 0); } static void pending_write_result(struct pending_write *p, int err) { if (p->timeout_id > 0) timeout_remove(p->timeout_id); p->func(p->attrib, err, p->user_data); free(p); } static void pending_write_free(void *data) { struct pending_write *p = data; pending_write_result(p, -ECANCELED); } static void attribute_notify_destroy(void *data) { struct attribute_notify *notify = data; if (notify->destroy) notify->destroy(notify->user_data); free(notify); } static void attribute_destroy(struct gatt_db_attribute *attribute) { /* Attribute was not initialized by user */ if (!attribute) return; queue_destroy(attribute->pending_reads, pending_read_free); queue_destroy(attribute->pending_writes, pending_write_free); queue_destroy(attribute->notify_list, attribute_notify_destroy); free(attribute->value); free(attribute); } static struct gatt_db_attribute *new_attribute(struct gatt_db_service *service, uint16_t handle, const bt_uuid_t *type, const uint8_t *val, uint16_t len) { struct gatt_db_attribute *attribute; attribute = new0(struct gatt_db_attribute, 1); attribute->service = service; attribute->handle = handle; attribute->uuid = *type; attribute->value_len = len; if (len) { attribute->value = malloc0(len); if (!attribute->value) goto failed; memcpy(attribute->value, val, len); } attribute->pending_reads = queue_new(); attribute->pending_writes = queue_new(); attribute->notify_list = queue_new(); return attribute; failed: attribute_destroy(attribute); return NULL; } struct gatt_db *gatt_db_ref(struct gatt_db *db) { if (!db) return NULL; __sync_fetch_and_add(&db->ref_count, 1); return db; } struct gatt_db *gatt_db_new(void) { struct gatt_db *db; db = new0(struct gatt_db, 1); db->crypto = bt_crypto_new(); db->services = queue_new(); db->notify_list = queue_new(); db->last_handle = 0x0000; return gatt_db_ref(db); } static void service_clone(void *data, void *user_data) { struct gatt_db_service *service = data; struct gatt_db *db = user_data; struct gatt_db_service *clone; int i; clone = new0(struct gatt_db_service, 1); clone->db = db; clone->active = service->active; clone->num_handles = service->num_handles; clone->attributes = new0(struct gatt_db_attribute *, service->num_handles); /* Clone attributes */ for (i = 0; i < service->num_handles; i++) { struct gatt_db_attribute *attr = service->attributes[i]; if (!attr) continue; /* Only clone values for characteristics declaration since that * is considered when calculating the db hash. */ if (bt_uuid_len(&attr->uuid) != 2) { clone->attributes[i] = new_attribute(clone, attr->handle, &attr->uuid, NULL, 0); continue; } /* Attribute values that are used for generating the hash needs * to be cloned as well. */ switch (attr->uuid.value.u16) { case GATT_PRIM_SVC_UUID: case GATT_SND_SVC_UUID: case GATT_INCLUDE_UUID: case GATT_CHARAC_UUID: clone->attributes[i] = new_attribute(clone, attr->handle, &attr->uuid, attr->value, attr->value_len); break; default: clone->attributes[i] = new_attribute(clone, attr->handle, &attr->uuid, NULL, 0); break; } } queue_push_tail(db->services, clone); } struct gatt_db *gatt_db_clone(struct gatt_db *db) { struct gatt_db *clone; if (!db) return NULL; clone = gatt_db_new(); if (!clone) return NULL; queue_foreach(db->services, service_clone, clone); return clone; } static void notify_destroy(void *data) { struct notify *notify = data; if (notify->destroy) notify->destroy(notify->user_data); free(notify); } static bool match_notify_id(const void *a, const void *b) { const struct notify *notify = a; unsigned int id = PTR_TO_UINT(b); return notify->id == id; } struct notify_data { struct gatt_db_attribute *attr; bool added; }; static void handle_notify(void *data, void *user_data) { struct notify *notify = data; struct notify_data *notify_data = user_data; if (notify_data->added) notify->service_added(notify_data->attr, notify->user_data); else notify->service_removed(notify_data->attr, notify->user_data); } struct hash_data { struct iovec *iov; uint16_t i; }; static void gen_hash_m(struct gatt_db_attribute *attr, void *user_data) { struct hash_data *hash = user_data; uint8_t *data; size_t len; if (!attr || !attr->value) return; if (bt_uuid_len(&attr->uuid) != 2) return; switch (attr->uuid.value.u16) { case GATT_PRIM_SVC_UUID: case GATT_SND_SVC_UUID: case GATT_INCLUDE_UUID: case GATT_CHARAC_UUID: /* Allocate space for handle + type + value */ len = 2 + 2 + attr->value_len; data = malloc(2 + 2 + attr->value_len); put_le16(attr->handle, data); bt_uuid_to_le(&attr->uuid, data + 2); memcpy(data + 4, attr->value, attr->value_len); break; case GATT_CHARAC_USER_DESC_UUID: case GATT_CLIENT_CHARAC_CFG_UUID: case GATT_SERVER_CHARAC_CFG_UUID: case GATT_CHARAC_FMT_UUID: case GATT_CHARAC_AGREG_FMT_UUID: /* Allocate space for handle + type */ len = 2 + 2; data = malloc(2 + 2 + attr->value_len); put_le16(attr->handle, data); bt_uuid_to_le(&attr->uuid, data + 2); break; default: return; } hash->iov[hash->i].iov_base = data; hash->iov[hash->i].iov_len = len; hash->i++; return; } static void service_gen_hash_m(struct gatt_db_attribute *attr, void *user_data) { gatt_db_service_foreach(attr, NULL, gen_hash_m, user_data); } static bool db_hash_update(void *user_data) { struct gatt_db *db = user_data; struct hash_data hash; uint16_t i; db->hash_id = 0; if (gatt_db_isempty(db)) return false; hash.iov = new0(struct iovec, db->last_handle + 1); hash.i = 0; gatt_db_foreach_service(db, NULL, service_gen_hash_m, &hash); bt_crypto_gatt_hash(db->crypto, hash.iov, db->last_handle + 1, db->hash); for (i = 0; i < hash.i; i++) free(hash.iov[i].iov_base); free(hash.iov); return false; } static void handle_attribute_notify(void *data, void *user_data) { struct attribute_notify *notify = data; struct gatt_db_attribute *attrib = user_data; if (notify->removed) notify->removed(attrib, notify->user_data); } static void notify_attribute_changed(struct gatt_db_service *service) { int i; for (i = 0; i < service->num_handles; i++) { struct gatt_db_attribute *attr = service->attributes[i]; if (!attr) continue; queue_foreach(attr->notify_list, handle_attribute_notify, attr); } } static void notify_service_changed(struct gatt_db *db, struct gatt_db_service *service, bool added) { struct notify_data data; if (!added) notify_attribute_changed(service); if (queue_isempty(db->notify_list)) return; data.attr = service->attributes[0]; data.added = added; gatt_db_ref(db); queue_foreach(db->notify_list, handle_notify, &data); /* Tigger hash update */ if (!db->hash_id && db->crypto) db->hash_id = timeout_add(HASH_UPDATE_TIMEOUT, db_hash_update, db, NULL); gatt_db_unref(db); } static void gatt_db_service_destroy(void *data) { struct gatt_db_service *service = data; int i; if (service->active) notify_service_changed(service->db, service, false); for (i = 0; i < service->num_handles; i++) attribute_destroy(service->attributes[i]); free(service->attributes); free(service); } static void gatt_db_destroy(struct gatt_db *db) { if (!db) return; bt_crypto_unref(db->crypto); /* * Clear the notify list before clearing the services to prevent the * latter from sending service_removed events. */ queue_destroy(db->notify_list, notify_destroy); db->notify_list = NULL; if (db->hash_id) timeout_remove(db->hash_id); queue_destroy(db->services, gatt_db_service_destroy); free(db->ccc); free(db); } void gatt_db_unref(struct gatt_db *db) { if (!db) return; if (__sync_sub_and_fetch(&db->ref_count, 1)) return; gatt_db_destroy(db); } bool gatt_db_isempty(struct gatt_db *db) { if (!db) return true; return queue_isempty(db->services); } static int uuid_to_le(const bt_uuid_t *uuid, uint8_t *dst) { bt_uuid_t uuid128; if (uuid->type == BT_UUID16) { put_le16(uuid->value.u16, dst); return bt_uuid_len(uuid); } bt_uuid_to_uuid128(uuid, &uuid128); bswap_128(&uuid128.value.u128, dst); return bt_uuid_len(&uuid128); } static bool le_to_uuid(const uint8_t *src, size_t len, bt_uuid_t *uuid) { uint128_t u128; if (len == 2) { bt_uuid16_create(uuid, get_le16(src)); return true; } if (len == 4) { bt_uuid32_create(uuid, get_le32(src)); return true; } if (len != 16) return false; bswap_128(src, &u128); bt_uuid128_create(uuid, u128); return true; } static struct gatt_db_service *gatt_db_service_create(const bt_uuid_t *uuid, uint16_t handle, bool primary, uint16_t num_handles) { struct gatt_db_service *service; const bt_uuid_t *type; uint8_t value[16]; uint16_t len; if (num_handles < 1) return NULL; service = new0(struct gatt_db_service, 1); service->attributes = new0(struct gatt_db_attribute *, num_handles); if (primary) type = &primary_service_uuid; else type = &secondary_service_uuid; len = uuid_to_le(uuid, value); service->attributes[0] = new_attribute(service, handle, type, value, len); if (!service->attributes[0]) { gatt_db_service_destroy(service); return NULL; } set_attribute_data(service->attributes[0], NULL, NULL, BT_ATT_PERM_READ, NULL); return service; } bool gatt_db_remove_service(struct gatt_db *db, struct gatt_db_attribute *attrib) { struct gatt_db_service *service; if (!db || !attrib) return false; service = attrib->service; queue_remove(db->services, service); gatt_db_service_destroy(service); return true; } bool gatt_db_clear(struct gatt_db *db) { return gatt_db_clear_range(db, 1, UINT16_MAX); } static void gatt_db_service_get_handles(const struct gatt_db_service *service, uint16_t *start_handle, uint16_t *end_handle) { if (start_handle) *start_handle = service->attributes[0]->handle; if (end_handle) *end_handle = service->attributes[0]->handle + service->num_handles - 1; } struct clear_range { uint16_t start, end; }; static bool match_range(const void *a, const void *b) { const struct gatt_db_service *service = a; const struct clear_range *range = b; uint16_t svc_start, svc_end; gatt_db_service_get_handles(service, &svc_start, &svc_end); return svc_start <= range->end && svc_end >= range->start; } bool gatt_db_clear_range(struct gatt_db *db, uint16_t start_handle, uint16_t end_handle) { struct clear_range range; if (!db || start_handle > end_handle) return false; /* Check if it is a full clear */ if (start_handle == 1 && end_handle == UINT16_MAX) { queue_remove_all(db->services, NULL, NULL, gatt_db_service_destroy); goto done; } range.start = start_handle; range.end = end_handle; queue_remove_all(db->services, match_range, &range, gatt_db_service_destroy); done: if (gatt_db_isempty(db)) db->last_handle = 0; return true; } uint8_t *gatt_db_get_hash(struct gatt_db *db) { uint8_t hash[16] = {}; if (!db || !db->crypto) return NULL; /* Generate hash if if has not been generated yet */ if (db->hash_id || !memcmp(db->hash, hash, 16)) { timeout_remove(db->hash_id); db_hash_update(db); } return db->hash; } bool gatt_db_hash_support(struct gatt_db *db) { if (!db || !db->crypto) return false; return true; } static struct gatt_db_service *find_insert_loc(struct gatt_db *db, uint16_t start, uint16_t end, struct gatt_db_service **after) { const struct queue_entry *services_entry; struct gatt_db_service *service; uint16_t cur_start, cur_end; *after = NULL; services_entry = queue_get_entries(db->services); while (services_entry) { service = services_entry->data; gatt_db_service_get_handles(service, &cur_start, &cur_end); if (start >= cur_start && start <= cur_end) return service; if (end >= cur_start && end <= cur_end) return service; if (end < cur_start) return NULL; *after = service; services_entry = services_entry->next; } return NULL; } struct gatt_db_attribute *gatt_db_insert_service(struct gatt_db *db, uint16_t handle, const bt_uuid_t *uuid, bool primary, uint16_t num_handles) { struct gatt_db_service *service, *after; after = NULL; if (!db) return NULL; if (!handle) handle = db->last_handle + 1; if (num_handles < 1 || (handle + num_handles - 1) > UINT16_MAX) return NULL; service = find_insert_loc(db, handle, handle + num_handles - 1, &after); if (service) { const bt_uuid_t *type; bt_uuid_t value; struct gatt_db_attribute *attr = service->attributes[0]; if (!attr) return NULL; if (primary) type = &primary_service_uuid; else type = &secondary_service_uuid; gatt_db_attribute_get_service_uuid(attr, &value); /* Check if service match */ if (!bt_uuid_cmp(&attr->uuid, type) && !bt_uuid_cmp(&value, uuid) && service->num_handles == num_handles && attr->handle == handle) return attr; return NULL; } service = gatt_db_service_create(uuid, handle, primary, num_handles); if (!service) return NULL; if (after) { if (!queue_push_after(db->services, after, service)) goto fail; } else if (!queue_push_head(db->services, service)) { goto fail; } service->db = db; service->attributes[0]->handle = handle; service->num_handles = num_handles; /* Fast-forward last_handle if the new service was added to the end */ db->last_handle = MAX(handle + num_handles - 1, db->last_handle); return service->attributes[0]; fail: gatt_db_service_destroy(service); return NULL; } struct gatt_db_attribute *gatt_db_add_service(struct gatt_db *db, const bt_uuid_t *uuid, bool primary, uint16_t num_handles) { return gatt_db_insert_service(db, 0, uuid, primary, num_handles); } unsigned int gatt_db_register(struct gatt_db *db, gatt_db_attribute_cb_t service_added, gatt_db_attribute_cb_t service_removed, void *user_data, gatt_db_destroy_func_t destroy) { struct notify *notify; if (!db || !(service_added || service_removed)) return 0; notify = new0(struct notify, 1); notify->service_added = service_added; notify->service_removed = service_removed; notify->destroy = destroy; notify->user_data = user_data; if (db->next_notify_id < 1) db->next_notify_id = 1; notify->id = db->next_notify_id++; if (!queue_push_tail(db->notify_list, notify)) { free(notify); return 0; } return notify->id; } bool gatt_db_unregister(struct gatt_db *db, unsigned int id) { struct notify *notify; if (!db || !id) return false; notify = queue_find(db->notify_list, match_notify_id, UINT_TO_PTR(id)); if (!notify) return false; queue_remove(db->notify_list, notify); notify_destroy(notify); return true; } bool gatt_db_set_authorize(struct gatt_db *db, gatt_db_authorize_cb_t cb, void *user_data) { if (!db) return false; db->authorize = cb; db->authorize_data = user_data; return true; } static uint16_t service_get_attribute_index(struct gatt_db_service *service, uint16_t *handle, int end_offset) { int i = 0; if (!service || !service->attributes[0] || !handle) return 0; if (*handle) { /* Check if handle is in within service range */ if (*handle < service->attributes[0]->handle) return 0; /* Return index based on given handle */ i = *handle - service->attributes[0]->handle; } else { /* Here we look for first free attribute index with given * offset. */ while (i < (service->num_handles - end_offset) && service->attributes[i]) i++; } if (i >= (service->num_handles - end_offset)) return 0; /* Set handle based on the index */ if (!(*handle)) *handle = service->attributes[0]->handle + i; return i; } static struct gatt_db_attribute * service_insert_characteristic(struct gatt_db_service *service, uint16_t handle, uint16_t value_handle, const bt_uuid_t *uuid, uint32_t permissions, uint8_t properties, gatt_db_read_t read_func, gatt_db_write_t write_func, void *user_data) { struct gatt_db_attribute **chrc; uint8_t value[MAX_CHAR_DECL_VALUE_LEN]; uint16_t len = 0; int i; /* Check if handle is in within service range */ if (handle && handle <= service->attributes[0]->handle) return NULL; /* * It is not possible to allocate last handle for a Characteristic * since it would not have space for its value: * 3.3.2 Characteristic Value Declaration * The Characteristic Value declaration contains the value of the * characteristic. It is the first Attribute after the characteristic * declaration. All characteristic definitions shall have a * Characteristic Value declaration. */ if (handle == UINT16_MAX) return NULL; i = service_get_attribute_index(service, &handle, 1); if (!i) return NULL; value[0] = properties; len += sizeof(properties); /* We set handle of characteristic value, which will be added next */ put_le16(value_handle, &value[1]); len += sizeof(uint16_t); len += uuid_to_le(uuid, &value[3]); service->attributes[i] = new_attribute(service, handle, &characteristic_uuid, value, len); if (!service->attributes[i]) return NULL; chrc = &service->attributes[i]; set_attribute_data(service->attributes[i], NULL, NULL, BT_ATT_PERM_READ, NULL); i = service_get_attribute_index(service, &value_handle, 0); if (!i) { free(*chrc); *chrc = NULL; return NULL; } service->attributes[i] = new_attribute(service, value_handle, uuid, NULL, 0); if (!service->attributes[i]) { free(*chrc); *chrc = NULL; return NULL; } /* Update handle of characteristic value_handle if it has changed */ put_le16(value_handle, &value[1]); if (!(*chrc) || !(*chrc)->value) return NULL; if (memcmp((*chrc)->value, value, len)) memcpy((*chrc)->value, value, len); set_attribute_data(service->attributes[i], read_func, write_func, permissions, user_data); return service->attributes[i]; } struct gatt_db_attribute * gatt_db_insert_characteristic(struct gatt_db *db, uint16_t handle, uint16_t value_handle, const bt_uuid_t *uuid, uint32_t permissions, uint8_t properties, gatt_db_read_t read_func, gatt_db_write_t write_func, void *user_data) { struct gatt_db_attribute *attrib; attrib = gatt_db_get_service(db, handle); if (!attrib) return NULL; return service_insert_characteristic(attrib->service, handle, value_handle, uuid, permissions, properties, read_func, write_func, user_data); } struct gatt_db_attribute * gatt_db_service_insert_characteristic(struct gatt_db_attribute *attrib, uint16_t handle, uint16_t value_handle, const bt_uuid_t *uuid, uint32_t permissions, uint8_t properties, gatt_db_read_t read_func, gatt_db_write_t write_func, void *user_data) { if (!attrib) return NULL; return service_insert_characteristic(attrib->service, handle, value_handle, uuid, permissions, properties, read_func, write_func, user_data); } struct gatt_db_attribute * gatt_db_service_add_characteristic(struct gatt_db_attribute *attrib, const bt_uuid_t *uuid, uint32_t permissions, uint8_t properties, gatt_db_read_t read_func, gatt_db_write_t write_func, void *user_data) { if (!attrib) return NULL; return service_insert_characteristic(attrib->service, 0, 0, uuid, permissions, properties, read_func, write_func, user_data); } static struct gatt_db_attribute * service_insert_descriptor(struct gatt_db_service *service, uint16_t handle, const bt_uuid_t *uuid, uint32_t permissions, gatt_db_read_t read_func, gatt_db_write_t write_func, void *user_data) { int i; i = service_get_attribute_index(service, &handle, 0); if (!i) return NULL; service->attributes[i] = new_attribute(service, handle, uuid, NULL, 0); if (!service->attributes[i]) return NULL; set_attribute_data(service->attributes[i], read_func, write_func, permissions, user_data); return service->attributes[i]; } struct gatt_db_attribute * gatt_db_insert_descriptor(struct gatt_db *db, uint16_t handle, const bt_uuid_t *uuid, uint32_t permissions, gatt_db_read_t read_func, gatt_db_write_t write_func, void *user_data) { struct gatt_db_attribute *attrib; attrib = gatt_db_get_service(db, handle); if (!attrib) return NULL; return service_insert_descriptor(attrib->service, handle, uuid, permissions, read_func, write_func, user_data); } struct gatt_db_attribute * gatt_db_service_insert_descriptor(struct gatt_db_attribute *attrib, uint16_t handle, const bt_uuid_t *uuid, uint32_t permissions, gatt_db_read_t read_func, gatt_db_write_t write_func, void *user_data) { if (!attrib) return NULL; return service_insert_descriptor(attrib->service, handle, uuid, permissions, read_func, write_func, user_data); } struct gatt_db_attribute * gatt_db_service_add_descriptor(struct gatt_db_attribute *attrib, const bt_uuid_t *uuid, uint32_t permissions, gatt_db_read_t read_func, gatt_db_write_t write_func, void *user_data) { if (!attrib) return NULL; return service_insert_descriptor(attrib->service, 0, uuid, permissions, read_func, write_func, user_data); } static void find_ccc_value(struct gatt_db_attribute *attrib, void *user_data) { uint16_t *handle = user_data; gatt_db_attribute_get_char_data(attrib, NULL, handle, NULL, NULL, NULL); } struct gatt_db_attribute * gatt_db_service_add_ccc(struct gatt_db_attribute *attrib, uint32_t permissions) { struct gatt_db *db; struct gatt_db_attribute *ccc; struct gatt_db_attribute *value; uint16_t handle = 0; if (!attrib || !permissions) return NULL; db = attrib->service->db; if (!db->ccc) return NULL; /* Locate value handle */ gatt_db_service_foreach_char(attrib, find_ccc_value, &handle); if (!handle) return NULL; value = gatt_db_get_attribute(db, handle); if (!value || value->notify_func) return NULL; ccc = service_insert_descriptor(attrib->service, 0, &ccc_uuid, permissions, db->ccc->read_func, db->ccc->write_func, db->ccc->user_data); if (!ccc) return ccc; gatt_db_attribute_set_fixed_length(ccc, 2); ccc->notify_func = db->ccc->notify_func; value->notify_func = db->ccc->notify_func; return ccc; } void gatt_db_ccc_register(struct gatt_db *db, gatt_db_read_t read_func, gatt_db_write_t write_func, gatt_db_notify_t notify_func, void *user_data) { if (!db) return; if (!db->ccc) db->ccc = new0(struct gatt_db_ccc, 1); db->ccc->read_func = read_func; db->ccc->write_func = write_func; db->ccc->notify_func = notify_func; db->ccc->user_data = user_data; } static struct gatt_db_attribute * service_insert_included(struct gatt_db_service *service, uint16_t handle, struct gatt_db_attribute *include) { struct gatt_db_service *included; uint8_t value[MAX_INCLUDED_VALUE_LEN]; uint16_t included_handle, len = 0; int index; if (!include || !include->value || !include->service || !service) return NULL; included = include->service; /* Adjust include to point to the first attribute */ if (include != included->attributes[0]) include = included->attributes[0]; included_handle = include->handle; put_le16(included_handle, &value[len]); len += sizeof(uint16_t); put_le16(included_handle + included->num_handles - 1, &value[len]); len += sizeof(uint16_t); /* The Service UUID shall only be present when the UUID is a 16-bit * Bluetooth UUID. Vol 2. Part G. 3.2 */ if (include->value_len == sizeof(uint16_t)) { memcpy(&value[len], include->value, include->value_len); len += include->value_len; } index = service_get_attribute_index(service, &handle, 0); if (!index) return NULL; service->attributes[index] = new_attribute(service, handle, &included_service_uuid, value, len); if (!service->attributes[index]) return NULL; /* The Attribute Permissions shall be read only and not require * authentication or authorization. Vol 2. Part G. 3.2 * * TODO handle permissions */ set_attribute_data(service->attributes[index], NULL, NULL, BT_ATT_PERM_READ, NULL); return service->attributes[index]; } struct gatt_db_attribute * gatt_db_service_add_included(struct gatt_db_attribute *attrib, struct gatt_db_attribute *include) { if (!attrib || !include) return NULL; return service_insert_included(attrib->service, 0, include); } struct gatt_db_attribute * gatt_db_service_insert_included(struct gatt_db_attribute *attrib, uint16_t handle, struct gatt_db_attribute *include) { if (!attrib || !handle || !include) return NULL; return service_insert_included(attrib->service, handle, include); } struct gatt_db_attribute * gatt_db_insert_included(struct gatt_db *db, uint16_t handle, struct gatt_db_attribute *include) { struct gatt_db_attribute *attrib; attrib = gatt_db_get_service(db, handle); if (!attrib) return NULL; return service_insert_included(attrib->service, handle, include); } bool gatt_db_service_set_active(struct gatt_db_attribute *attrib, bool active) { struct gatt_db_service *service; if (!attrib) return false; service = attrib->service; if (service->active == active) return true; service->active = active; notify_service_changed(service->db, service, active); return true; } bool gatt_db_service_get_active(struct gatt_db_attribute *attrib) { if (!attrib) return false; return attrib->service->active; } bool gatt_db_service_set_claimed(struct gatt_db_attribute *attrib, bool claimed) { if (!attrib) return false; attrib->service->claimed = claimed; return true; } bool gatt_db_service_get_claimed(struct gatt_db_attribute *attrib) { if (!attrib) return false; return attrib->service->claimed; } static void read_by_group_type(struct gatt_db_attribute *attribute, void *user_data) { struct queue *queue = user_data; queue_push_tail(queue, attribute); } void gatt_db_read_by_group_type(struct gatt_db *db, uint16_t start_handle, uint16_t end_handle, const bt_uuid_t type, struct queue *queue) { gatt_db_foreach_service_in_range(db, &type, read_by_group_type, queue, start_handle, end_handle); } struct find_by_type_value_data { gatt_db_attribute_cb_t func; void *user_data; const void *value; size_t value_len; unsigned int num_of_res; }; static void find_by_type(struct gatt_db_attribute *attribute, void *user_data) { struct find_by_type_value_data *search_data = user_data; /* TODO: fix for read-callback based attributes */ if (search_data->value) { if (search_data->value_len != attribute->value_len) return; if (!attribute || !attribute->value) return; if (memcmp(attribute->value, search_data->value, search_data->value_len)) return; } search_data->num_of_res++; search_data->func(attribute, search_data->user_data); } unsigned int gatt_db_find_by_type(struct gatt_db *db, uint16_t start_handle, uint16_t end_handle, const bt_uuid_t *type, gatt_db_attribute_cb_t func, void *user_data) { struct find_by_type_value_data data; memset(&data, 0, sizeof(data)); data.func = func; data.user_data = user_data; gatt_db_foreach_in_range(db, type, find_by_type, &data, start_handle, end_handle); return data.num_of_res; } unsigned int gatt_db_find_by_type_value(struct gatt_db *db, uint16_t start_handle, uint16_t end_handle, const bt_uuid_t *type, const void *value, size_t value_len, gatt_db_attribute_cb_t func, void *user_data) { struct find_by_type_value_data data; memset(&data, 0, sizeof(data)); data.func = func; data.user_data = user_data; data.value = value; data.value_len = value_len; gatt_db_foreach_in_range(db, type, find_by_type, &data, start_handle, end_handle); return data.num_of_res; } static void read_by_type(struct gatt_db_attribute *attribute, void *user_data) { struct queue *queue = user_data; queue_push_tail(queue, attribute); } void gatt_db_read_by_type(struct gatt_db *db, uint16_t start_handle, uint16_t end_handle, const bt_uuid_t type, struct queue *queue) { gatt_db_foreach_in_range(db, &type, read_by_type, queue, start_handle, end_handle); } static void find_information(struct gatt_db_attribute *attribute, void *user_data) { struct queue *queue = user_data; queue_push_tail(queue, attribute); } void gatt_db_find_information(struct gatt_db *db, uint16_t start_handle, uint16_t end_handle, struct queue *queue) { gatt_db_foreach_in_range(db, NULL, find_information, queue, start_handle, end_handle); } void gatt_db_foreach_service(struct gatt_db *db, const bt_uuid_t *uuid, gatt_db_attribute_cb_t func, void *user_data) { gatt_db_foreach_service_in_range(db, uuid, func, user_data, 0x0001, 0xffff); } struct foreach_data { gatt_db_attribute_cb_t func; const bt_uuid_t *uuid; void *user_data; uint16_t start, end; bool attr; }; static void foreach_service_in_range(void *data, void *user_data) { struct gatt_db_service *service = data; struct gatt_db_attribute *attribute = service->attributes[0]; struct foreach_data *foreach_data = user_data; bt_uuid_t uuid; if (foreach_data->uuid) { gatt_db_attribute_get_service_uuid(attribute, &uuid); if (bt_uuid_cmp(&uuid, foreach_data->uuid)) { /* Compare with attribute UUID in case it is a lookup * by group type. */ if (bt_uuid_cmp(&attribute->uuid, foreach_data->uuid)) return; } } foreach_data->func(service->attributes[0], foreach_data->user_data); } static void foreach_in_range(void *data, void *user_data) { struct gatt_db_service *service = data; struct foreach_data *foreach_data = user_data; uint16_t svc_start, svc_end; int i; if (!service->active) return; gatt_db_service_get_handles(service, &svc_start, &svc_end); /* Check if service is within requested range */ if (svc_start > foreach_data->end || svc_end < foreach_data->start) return; if (!foreach_data->attr) { if (svc_start < foreach_data->start) return; return foreach_service_in_range(data, user_data); } for (i = 0; i < service->num_handles; i++) { struct gatt_db_attribute *attribute = service->attributes[i]; if (!attribute) continue; if (attribute->handle < foreach_data->start) continue; if (attribute->handle > foreach_data->end) return; if (foreach_data->uuid && bt_uuid_cmp(foreach_data->uuid, &attribute->uuid)) continue; foreach_data->func(attribute, foreach_data->user_data); } } void gatt_db_foreach_service_in_range(struct gatt_db *db, const bt_uuid_t *uuid, gatt_db_attribute_cb_t func, void *user_data, uint16_t start_handle, uint16_t end_handle) { struct foreach_data data; if (!db || !func || start_handle > end_handle) return; data.func = func; data.uuid = uuid; data.user_data = user_data; data.start = start_handle; data.end = end_handle; data.attr = false; queue_foreach(db->services, foreach_in_range, &data); } void gatt_db_foreach_in_range(struct gatt_db *db, const bt_uuid_t *uuid, gatt_db_attribute_cb_t func, void *user_data, uint16_t start_handle, uint16_t end_handle) { struct foreach_data data; if (!db || !func || start_handle > end_handle) return; data.func = func; data.uuid = uuid; data.user_data = user_data; data.start = start_handle; data.end = end_handle; data.attr = true; queue_foreach(db->services, foreach_in_range, &data); } void gatt_db_service_foreach(struct gatt_db_attribute *attrib, const bt_uuid_t *uuid, gatt_db_attribute_cb_t func, void *user_data) { struct gatt_db_service *service; struct gatt_db_attribute *attr; uint16_t i; if (!attrib || !func) return; service = attrib->service; for (i = 0; i < service->num_handles; i++) { attr = service->attributes[i]; if (!attr) continue; if (uuid && bt_uuid_cmp(uuid, &attr->uuid)) continue; func(attr, user_data); } } void gatt_db_service_foreach_char(struct gatt_db_attribute *attrib, gatt_db_attribute_cb_t func, void *user_data) { gatt_db_service_foreach(attrib, &characteristic_uuid, func, user_data); } static int gatt_db_attribute_get_index(const struct gatt_db_attribute *attrib) { struct gatt_db_service *service; int index; if (!attrib) return -1; service = attrib->service; for (index = 0; index < service->num_handles; index++) { if (service->attributes[index] == attrib) return index; } return -1; } struct gatt_db_attribute * gatt_db_attribute_get_value(struct gatt_db_attribute *attrib) { struct gatt_db_service *service; int index; if (!attrib) return NULL; index = gatt_db_attribute_get_index(attrib); if (index <= 0) return NULL; service = attrib->service; if (!bt_uuid_cmp(&characteristic_uuid, &attrib->uuid)) return service->attributes[index + 1]; else if (service->attributes[index - 1] == NULL) return NULL; else if (!bt_uuid_cmp(&characteristic_uuid, &service->attributes[index - 1]->uuid)) return service->attributes[index]; return gatt_db_attribute_get_value(service->attributes[index - 1]); } void gatt_db_service_foreach_desc(struct gatt_db_attribute *attrib, gatt_db_attribute_cb_t func, void *user_data) { struct gatt_db_service *service; struct gatt_db_attribute *attr; int index; uint16_t i; if (!attrib || !func) return; attrib = gatt_db_attribute_get_value(attrib); if (!attrib) return; index = gatt_db_attribute_get_index(attrib); if (index < 0) return; service = attrib->service; /* Start from the attribute following the value handle */ for (i = index + 1; i < service->num_handles; i++) { attr = service->attributes[i]; if (!attr) continue; /* Return if we reached the end of this characteristic */ if (!bt_uuid_cmp(&characteristic_uuid, &attr->uuid) || !bt_uuid_cmp(&included_service_uuid, &attr->uuid)) return; func(attr, user_data); } } void gatt_db_service_foreach_incl(struct gatt_db_attribute *attrib, gatt_db_attribute_cb_t func, void *user_data) { gatt_db_service_foreach(attrib, &included_service_uuid, func, user_data); } static bool find_service_for_handle(const void *data, const void *user_data) { const struct gatt_db_service *service = data; uint16_t handle = PTR_TO_UINT(user_data); uint16_t start, end; gatt_db_service_get_handles(service, &start, &end); return (start <= handle) && (handle <= end); } struct gatt_db_attribute *gatt_db_get_service(struct gatt_db *db, uint16_t handle) { struct gatt_db_service *service; if (!db || !handle) return NULL; service = queue_find(db->services, find_service_for_handle, UINT_TO_PTR(handle)); if (!service) return NULL; return service->attributes[0]; } struct gatt_db_attribute *gatt_db_get_attribute(struct gatt_db *db, uint16_t handle) { struct gatt_db_attribute *attrib; struct gatt_db_service *service; int i; attrib = gatt_db_get_service(db, handle); if (!attrib) return NULL; service = attrib->service; for (i = 0; i < service->num_handles; i++) { if (!service->attributes[i]) continue; if (service->attributes[i]->handle == handle) return service->attributes[i]; } return NULL; } static bool find_service_with_uuid(const void *data, const void *user_data) { const struct gatt_db_service *service = data; const bt_uuid_t *uuid = user_data; bt_uuid_t svc_uuid; gatt_db_attribute_get_service_uuid(service->attributes[0], &svc_uuid); return bt_uuid_cmp(uuid, &svc_uuid) == 0; } struct gatt_db_attribute *gatt_db_get_service_with_uuid(struct gatt_db *db, const bt_uuid_t *uuid) { struct gatt_db_service *service; if (!db || !uuid) return NULL; service = queue_find(db->services, find_service_with_uuid, uuid); if (!service) return NULL; return service->attributes[0]; } const bt_uuid_t *gatt_db_attribute_get_type( const struct gatt_db_attribute *attrib) { if (!attrib) return NULL; return &attrib->uuid; } uint16_t gatt_db_attribute_get_handle(const struct gatt_db_attribute *attrib) { if (!attrib) return 0; return attrib->handle; } struct gatt_db_attribute * gatt_db_attribute_get_service(const struct gatt_db_attribute *attrib) { if (!attrib) return NULL; return attrib->service->attributes[0]; } bool gatt_db_attribute_get_service_uuid(const struct gatt_db_attribute *attrib, bt_uuid_t *uuid) { struct gatt_db_service *service; if (!attrib || !uuid) return false; service = attrib->service; if (service->attributes[0]->value_len == sizeof(uint16_t)) { uint16_t value; value = get_le16(service->attributes[0]->value); bt_uuid16_create(uuid, value); return true; } if (service->attributes[0]->value_len == sizeof(uint128_t)) { uint128_t value; bswap_128(service->attributes[0]->value, &value); bt_uuid128_create(uuid, value); return true; } return false; } bool gatt_db_attribute_get_service_handles( const struct gatt_db_attribute *attrib, uint16_t *start_handle, uint16_t *end_handle) { struct gatt_db_service *service; if (!attrib) return false; service = attrib->service; gatt_db_service_get_handles(service, start_handle, end_handle); return true; } bool gatt_db_attribute_get_service_data(const struct gatt_db_attribute *attrib, uint16_t *start_handle, uint16_t *end_handle, bool *primary, bt_uuid_t *uuid) { struct gatt_db_service *service; struct gatt_db_attribute *decl; if (!attrib) return false; service = attrib->service; decl = service->attributes[0]; gatt_db_service_get_handles(service, start_handle, end_handle); if (primary) *primary = bt_uuid_cmp(&decl->uuid, &secondary_service_uuid); if (!uuid) return true; /* * The service declaration attribute value is the 16 or 128 bit service * UUID. */ return le_to_uuid(decl->value, decl->value_len, uuid); } static void read_ext_prop_value(struct gatt_db_attribute *attrib, int err, const uint8_t *value, size_t length, void *user_data) { uint16_t *ext_prop = user_data; if (err || (length != sizeof(uint16_t))) return; *ext_prop = (uint16_t) value[0]; } static void read_ext_prop(struct gatt_db_attribute *attrib, void *user_data) { uint16_t *ext_prop = user_data; /* * If ext_prop is set that means extended properties descriptor * has been already found */ if (*ext_prop != 0) return; if (bt_uuid_cmp(&ext_desc_uuid, &attrib->uuid)) return; gatt_db_attribute_read(attrib, 0, BT_ATT_OP_READ_REQ, NULL, read_ext_prop_value, ext_prop); } static uint8_t get_char_extended_prop(const struct gatt_db_attribute *attrib) { uint16_t ext_prop; if (!attrib) return 0; if (bt_uuid_cmp(&characteristic_uuid, &attrib->uuid)) return 0; /* Check properties first */ if (!(attrib->value[0] & BT_GATT_CHRC_PROP_EXT_PROP)) return 0; ext_prop = 0; /* * Cast needed for foreach function. We do not change attrib during * this call */ gatt_db_service_foreach_desc((struct gatt_db_attribute *) attrib, read_ext_prop, &ext_prop); return ext_prop; } bool gatt_db_attribute_get_char_data(const struct gatt_db_attribute *attrib, uint16_t *handle, uint16_t *value_handle, uint8_t *properties, uint16_t *ext_prop, bt_uuid_t *uuid) { if (!attrib) return false; if (bt_uuid_cmp(&characteristic_uuid, &attrib->uuid)) { int index; /* Check if Characteristic Value was passed instead */ index = gatt_db_attribute_get_index(attrib); if (index < 0) return NULL; attrib = attrib->service->attributes[index - 1]; if (bt_uuid_cmp(&characteristic_uuid, &attrib->uuid)) return false; } /* * Characteristic declaration value: * 1 octet: Characteristic properties * 2 octets: Characteristic value handle * 2 or 16 octets: characteristic UUID */ if (!attrib->value || (attrib->value_len != 5 && attrib->value_len != 19)) return false; if (handle) *handle = attrib->handle; if (properties) *properties = attrib->value[0]; if (ext_prop) *ext_prop = get_char_extended_prop(attrib); if (value_handle) *value_handle = get_le16(attrib->value + 1); if (!uuid) return true; return le_to_uuid(attrib->value + 3, attrib->value_len - 3, uuid); } bool gatt_db_attribute_get_incl_data(const struct gatt_db_attribute *attrib, uint16_t *handle, uint16_t *start_handle, uint16_t *end_handle) { if (!attrib) return false; if (bt_uuid_cmp(&included_service_uuid, &attrib->uuid)) return false; /* * Include definition value: * 2 octets: start handle of included service * 2 octets: end handle of included service * optional 2 octets: 16-bit Bluetooth UUID */ if (!attrib->value || attrib->value_len < 4 || attrib->value_len > 6) return false; /* * We only return the handles since the UUID can be easily obtained * from the corresponding attribute. */ if (handle) *handle = attrib->handle; if (start_handle) *start_handle = get_le16(attrib->value); if (end_handle) *end_handle = get_le16(attrib->value + 2); return true; } uint32_t gatt_db_attribute_get_permissions(const struct gatt_db_attribute *attrib) { if (!attrib) return 0; return attrib->permissions; } static bool read_timeout(void *user_data) { struct pending_read *p = user_data; p->timeout_id = 0; queue_remove(p->attrib->pending_reads, p); pending_read_result(p, -ETIMEDOUT, NULL, 0); return false; } static uint8_t attribute_authorize(struct gatt_db_attribute *attrib, uint8_t opcode, struct bt_att *att) { struct gatt_db *db = attrib->service->db; if (!db->authorize) return 0; return db->authorize(attrib, opcode, att, db->authorize_data); } bool gatt_db_attribute_set_fixed_length(struct gatt_db_attribute *attrib, uint16_t len) { struct gatt_db_service *service; if (!attrib) return false; service = attrib->service; /* Don't allow overwriting length of service attribute */ if (attrib->service->attributes[0] == attrib) return false; /* If attribute is a characteristic declaration ajust to its value */ if (!bt_uuid_cmp(&characteristic_uuid, &attrib->uuid)) { int i; /* Start from the attribute following the value handle */ for (i = 0; i < service->num_handles; i++) { if (service->attributes[i] == attrib) { attrib = service->attributes[i + 1]; break; } } } attrib->value_len = len; return true; } bool gatt_db_attribute_read(struct gatt_db_attribute *attrib, uint16_t offset, uint8_t opcode, struct bt_att *att, gatt_db_attribute_read_t func, void *user_data) { uint8_t *value; if (!attrib || !func) return false; /* Check boundaries if value_len is set */ if (attrib->value_len && offset > attrib->value_len) { func(attrib, BT_ATT_ERROR_INVALID_OFFSET, NULL, 0, user_data); return true; } if (attrib->read_func) { struct pending_read *p; uint8_t err; err = attribute_authorize(attrib, opcode, att); if (err) { func(attrib, err, NULL, 0, user_data); return true; } p = new0(struct pending_read, 1); p->attrib = attrib; p->id = ++attrib->read_id; p->timeout_id = timeout_add(ATTRIBUTE_TIMEOUT, read_timeout, p, NULL); p->func = func; p->user_data = user_data; queue_push_tail(attrib->pending_reads, p); attrib->read_func(attrib, p->id, offset, opcode, att, attrib->user_data); return true; } /* Guard against invalid access if offset equals to value length */ value = offset == attrib->value_len ? NULL : &attrib->value[offset]; func(attrib, 0, value, attrib->value_len - offset, user_data); return true; } static bool find_pending(const void *a, const void *b) { const struct pending_read *p = a; unsigned int id = PTR_TO_UINT(b); return p->id == id; } bool gatt_db_attribute_read_result(struct gatt_db_attribute *attrib, unsigned int id, int err, const uint8_t *value, size_t length) { struct pending_read *p; if (!attrib || !id) return false; p = queue_remove_if(attrib->pending_reads, find_pending, UINT_TO_PTR(id)); if (!p) return false; pending_read_result(p, err, value, length); return true; } static bool write_timeout(void *user_data) { struct pending_write *p = user_data; p->timeout_id = 0; queue_remove(p->attrib->pending_writes, p); pending_write_result(p, -ETIMEDOUT); return false; } bool gatt_db_attribute_write(struct gatt_db_attribute *attrib, uint16_t offset, const uint8_t *value, size_t len, uint8_t opcode, struct bt_att *att, gatt_db_attribute_write_t func, void *user_data) { uint8_t err = 0; if (!attrib || (!func && attrib->write_func)) return false; if (attrib->write_func) { struct pending_write *p; /* Check boundaries if value_len is set */ if (attrib->value_len) { if (offset > attrib->value_len) { err = BT_ATT_ERROR_INVALID_OFFSET; goto done; } if (offset + len > attrib->value_len) { err = BT_ATT_ERROR_INVALID_ATTRIBUTE_VALUE_LEN; goto done; } } err = attribute_authorize(attrib, opcode, att); if (err) goto done; p = new0(struct pending_write, 1); p->attrib = attrib; p->id = ++attrib->write_id; p->timeout_id = timeout_add(ATTRIBUTE_TIMEOUT, write_timeout, p, NULL); p->func = func; p->user_data = user_data; queue_push_tail(attrib->pending_writes, p); attrib->write_func(attrib, p->id, offset, value, len, opcode, att, attrib->user_data); return true; } /* Nothing to write just skip */ if (len == 0) goto done; /* For values stored in db allocate on demand */ if (!attrib->value || offset >= attrib->value_len || len > (unsigned) (attrib->value_len - offset)) { void *buf; buf = realloc(attrib->value, len + offset); if (!buf) return false; attrib->value = buf; /* Init data in the first allocation */ if (!attrib->value_len) memset(attrib->value, 0, offset); attrib->value_len = len + offset; } memcpy(&attrib->value[offset], value, len); done: if (func) func(attrib, err, user_data); return true; } bool gatt_db_attribute_write_result(struct gatt_db_attribute *attrib, unsigned int id, int err) { struct pending_write *p; if (!attrib || !id) return false; p = queue_remove_if(attrib->pending_writes, find_pending, UINT_TO_PTR(id)); if (!p) return false; pending_write_result(p, err); return true; } static void find_ccc(struct gatt_db_attribute *attrib, void *user_data) { struct gatt_db_attribute **ccc = user_data; if (*ccc) return; if (bt_uuid_cmp(&ccc_uuid, &attrib->uuid)) return; *ccc = attrib; } struct gatt_db_attribute * gatt_db_attribute_get_ccc(struct gatt_db_attribute *attrib) { struct gatt_db_attribute *ccc = NULL; if (!attrib) return NULL; gatt_db_service_foreach_desc(attrib, find_ccc, &ccc); return ccc; } bool gatt_db_attribute_notify(struct gatt_db_attribute *attrib, const uint8_t *value, size_t len, struct bt_att *att) { struct gatt_db_attribute *ccc; if (!attrib || !attrib->notify_func) return false; attrib = gatt_db_attribute_get_value(attrib); if (!attrib) return false; ccc = gatt_db_attribute_get_ccc(attrib); if (!ccc) return false; attrib->notify_func(attrib, ccc, value, len, att, ccc->user_data); return true; } bool gatt_db_attribute_reset(struct gatt_db_attribute *attrib) { if (!attrib) return false; if (!attrib->value || !attrib->value_len) return true; free(attrib->value); attrib->value = NULL; attrib->value_len = 0; return true; } void *gatt_db_attribute_get_user_data(struct gatt_db_attribute *attrib) { if (!attrib) return NULL; return attrib->user_data; } static bool match_attribute_notify_id(const void *a, const void *b) { const struct attribute_notify *notify = a; unsigned int id = PTR_TO_UINT(b); return notify->id == id; } unsigned int gatt_db_attribute_register(struct gatt_db_attribute *attrib, gatt_db_attribute_cb_t removed, void *user_data, gatt_db_destroy_func_t destroy) { struct attribute_notify *notify; if (!attrib || !removed) return 0; notify = new0(struct attribute_notify, 1); notify->removed = removed; notify->destroy = destroy; notify->user_data = user_data; if (attrib->next_notify_id < 1) attrib->next_notify_id = 1; notify->id = attrib->next_notify_id++; if (!queue_push_tail(attrib->notify_list, notify)) { free(notify); return 0; } return notify->id; } bool gatt_db_attribute_unregister(struct gatt_db_attribute *attrib, unsigned int id) { struct attribute_notify *notify; if (!attrib || !id) return false; notify = queue_find(attrib->notify_list, match_attribute_notify_id, UINT_TO_PTR(id)); if (!notify) return false; queue_remove(attrib->notify_list, notify); attribute_notify_destroy(notify); return true; } bluez-5.82/src/shared/PaxHeaders/att.h0000644000000000000000000000005014643061455014654 xustar0020 atime=1743515882 20 ctime=1743591277 bluez-5.82/src/shared/att.h0000644000000000000000000001003214643061455014331 0ustar00rootroot/* SPDX-License-Identifier: LGPL-2.1-or-later */ /* * * BlueZ - Bluetooth protocol stack for Linux * * Copyright (C) 2014 Google Inc. * * */ #include #include #include "src/shared/att-types.h" #define BT_ATT_DEBUG 0x00 #define BT_ATT_DEBUG_VERBOSE 0x01 #define BT_ATT_DEBUG_HEXDUMP 0x02 struct bt_att; struct bt_att_chan; struct bt_att *bt_att_new(int fd, bool ext_signed); struct bt_att *bt_att_ref(struct bt_att *att); void bt_att_unref(struct bt_att *att); bool bt_att_set_close_on_unref(struct bt_att *att, bool do_close); int bt_att_get_fd(struct bt_att *att); int bt_att_attach_fd(struct bt_att *att, int fd); int bt_att_get_channels(struct bt_att *att); typedef void (*bt_att_response_func_t)(uint8_t opcode, const void *pdu, uint16_t length, void *user_data); typedef void (*bt_att_notify_func_t)(struct bt_att_chan *chan, uint16_t mtu, uint8_t opcode, const void *pdu, uint16_t length, void *user_data); typedef void (*bt_att_destroy_func_t)(void *user_data); typedef void (*bt_att_debug_func_t)(const char *str, void *user_data); typedef void (*bt_att_timeout_func_t)(unsigned int id, uint8_t opcode, void *user_data); typedef void (*bt_att_disconnect_func_t)(int err, void *user_data); typedef void (*bt_att_exchange_func_t)(uint16_t mtu, void *user_data); typedef bool (*bt_att_counter_func_t)(uint32_t *sign_cnt, void *user_data); bool bt_att_set_debug(struct bt_att *att, uint8_t level, bt_att_debug_func_t callback, void *user_data, bt_att_destroy_func_t destroy); uint16_t bt_att_get_mtu(struct bt_att *att); bool bt_att_set_mtu(struct bt_att *att, uint16_t mtu); uint8_t bt_att_get_link_type(struct bt_att *att); bool bt_att_set_timeout_cb(struct bt_att *att, bt_att_timeout_func_t callback, void *user_data, bt_att_destroy_func_t destroy); unsigned int bt_att_send(struct bt_att *att, uint8_t opcode, const void *pdu, uint16_t length, bt_att_response_func_t callback, void *user_data, bt_att_destroy_func_t destroy); int bt_att_resend(struct bt_att *att, unsigned int id, uint8_t opcode, const void *pdu, uint16_t length, bt_att_response_func_t callback, void *user_data, bt_att_destroy_func_t destroy); unsigned int bt_att_chan_send(struct bt_att_chan *chan, uint8_t opcode, const void *pdu, uint16_t len, bt_att_response_func_t callback, void *user_data, bt_att_destroy_func_t destroy); #define bt_att_chan_send_rsp(chan, opcode, pdu, len) \ bt_att_chan_send(chan, opcode, pdu, len, NULL, NULL, NULL) bool bt_att_chan_cancel(struct bt_att_chan *chan, unsigned int id); bool bt_att_cancel(struct bt_att *att, unsigned int id); bool bt_att_cancel_all(struct bt_att *att); int bt_att_chan_send_error_rsp(struct bt_att_chan *chan, uint8_t opcode, uint16_t handle, int error); unsigned int bt_att_register(struct bt_att *att, uint8_t opcode, bt_att_notify_func_t callback, void *user_data, bt_att_destroy_func_t destroy); bool bt_att_unregister(struct bt_att *att, unsigned int id); unsigned int bt_att_register_disconnect(struct bt_att *att, bt_att_disconnect_func_t callback, void *user_data, bt_att_destroy_func_t destroy); bool bt_att_unregister_disconnect(struct bt_att *att, unsigned int id); unsigned int bt_att_register_exchange(struct bt_att *att, bt_att_exchange_func_t callback, void *user_data, bt_att_destroy_func_t destroy); bool bt_att_unregister_exchange(struct bt_att *att, unsigned int id); bool bt_att_unregister_all(struct bt_att *att); int bt_att_get_security(struct bt_att *att, uint8_t *enc_size); bool bt_att_set_security(struct bt_att *att, int level); void bt_att_set_enc_key_size(struct bt_att *att, uint8_t enc_size); bool bt_att_set_local_key(struct bt_att *att, uint8_t sign_key[16], bt_att_counter_func_t func, void *user_data); bool bt_att_set_remote_key(struct bt_att *att, uint8_t sign_key[16], bt_att_counter_func_t func, void *user_data); bool bt_att_has_crypto(struct bt_att *att); bool bt_att_set_retry(struct bt_att *att, unsigned int id, bool retry); bluez-5.82/src/shared/PaxHeaders/log.c0000644000000000000000000000005014505354670014641 xustar0020 atime=1743515903 20 ctime=1743591277 bluez-5.82/src/shared/log.c0000644000000000000000000000570314505354670014327 0ustar00rootroot// SPDX-License-Identifier: LGPL-2.1-or-later /* * * BlueZ - Bluetooth protocol stack for Linux * * Copyright (C) 2018 Intel Corporation. All rights reserved. * * */ #ifdef HAVE_CONFIG_H #include #endif #define _GNU_SOURCE #include #include #include #include #include #include #include #include #include #include "lib/bluetooth.h" #include "lib/hci.h" #include "src/shared/util.h" #include "src/shared/log.h" struct log_hdr { uint16_t opcode; uint16_t index; uint16_t len; uint8_t priority; uint8_t ident_len; } __attribute__((packed)); struct log_l2cap_hdr { uint16_t cid; uint16_t psm; } __attribute__((packed)); static int log_fd = -1; int bt_log_sendmsg(uint16_t index, const char *label, int level, struct iovec *io, size_t io_len) { struct log_hdr hdr; struct msghdr msg; struct iovec iov[5]; size_t i; int err; if (io_len > 3) return -EMSGSIZE; log_fd = bt_log_open(); if (log_fd < 0) return log_fd; hdr.opcode = cpu_to_le16(0x0000); hdr.index = cpu_to_le16(index); hdr.ident_len = strlen(label) + 1; hdr.len = cpu_to_le16(2 + hdr.ident_len); hdr.priority = level; iov[0].iov_base = &hdr; iov[0].iov_len = sizeof(hdr); iov[1].iov_base = (void *) label; iov[1].iov_len = hdr.ident_len; memset(&msg, 0, sizeof(msg)); msg.msg_iov = iov; msg.msg_iovlen = 2; for (i = 0; i < io_len; i++) { iov[i + 2] = io[i]; hdr.len += io[i].iov_len; msg.msg_iovlen++; } err = sendmsg(log_fd, &msg, 0); if (err < 0) { err = -errno; close(log_fd); log_fd = -1; } return err; } int bt_log_open(void) { struct sockaddr_hci addr; int fd; static int err; if (err < 0) return err; if (log_fd >= 0) return log_fd; fd = socket(PF_BLUETOOTH, SOCK_RAW, BTPROTO_HCI); if (fd < 0) { err = -errno; return -errno; } memset(&addr, 0, sizeof(addr)); addr.hci_family = AF_BLUETOOTH; addr.hci_dev = HCI_DEV_NONE; addr.hci_channel = HCI_CHANNEL_LOGGING; err = bind(fd, (struct sockaddr *) &addr, sizeof(addr)); if (err < 0) { err = -errno; close(fd); return err; } log_fd = fd; return fd; } int bt_log_vprintf(uint16_t index, const char *label, int level, const char *format, va_list ap) { struct iovec iov; char *str; int len; len = vasprintf(&str, format, ap); if (len < 0 || !str) return errno; len = strlen(str); /* Replace new line since btmon already adds it */ if (len > 1 && str[len - 1] == '\n') { str[len - 1] = '\0'; len--; } iov.iov_base = str; iov.iov_len = len + 1; len = bt_log_sendmsg(index, label, level, &iov, 1); free(str); return len; } int bt_log_printf(uint16_t index, const char *label, int level, const char *format, ...) { va_list ap; int err; va_start(ap, format); err = bt_log_vprintf(index, label, level, format, ap); va_end(ap); return err; } void bt_log_close(void) { if (log_fd < 0) return; close(log_fd); log_fd = -1; } bluez-5.82/src/PaxHeaders/bluetoothd.80000644000000000000000000000005014773213417014710 xustar0020 atime=1743591183 20 ctime=1743591290 bluez-5.82/src/bluetoothd.80000644000000000000000000000702014773213417014370 0ustar00rootroot.\" Man page generated from reStructuredText. . . .nr rst2man-indent-level 0 . .de1 rstReportMargin \\$1 \\n[an-margin] level \\n[rst2man-indent-level] level margin: \\n[rst2man-indent\\n[rst2man-indent-level]] - \\n[rst2man-indent0] \\n[rst2man-indent1] \\n[rst2man-indent2] .. .de1 INDENT .\" .rstReportMargin pre: . RS \\$1 . nr rst2man-indent\\n[rst2man-indent-level] \\n[an-margin] . nr rst2man-indent-level +1 .\" .rstReportMargin post: .. .de UNINDENT . RE .\" indent \\n[an-margin] .\" old: \\n[rst2man-indent\\n[rst2man-indent-level]] .nr rst2man-indent-level -1 .\" new: \\n[rst2man-indent\\n[rst2man-indent-level]] .in \\n[rst2man-indent\\n[rst2man-indent-level]]u .. .TH "BLUETOOTHD" "8" "March, 2004" "BlueZ" "System management commands" .SH NAME bluetoothd \- Bluetooth daemon .SH SYNOPSIS .sp \fBbluetoothd\fP [\-\-version] | [\-\-help] .sp \fBbluetoothd\fP [\-\-nodetach] [\-\-compat] [\-\-experimental] [\-\-debug=<\fIfiles\fP>] [\-\-plugin=<\fIplugins\fP>] [\-\-noplugin=<\fIplugins\fP>] .SH DESCRIPTION .sp This manual page documents briefly the \fBbluetoothd\fP daemon, which manages all the Bluetooth devices. \fBbluetoothd\fP can also provide a number of services via the D\-Bus message bus system. .SH OPTIONS .INDENT 0.0 .TP .B \-v\fP,\fB \-\-version Print bluetoothd version and exit. .TP .B \-h\fP,\fB \-\-help Print bluetoothd options and exit. .TP .B \-n\fP,\fB \-\-nodetach Enable logging in foreground. Directs log output to the controlling terminal in addition to syslog. .TP .B \-f\fP,\fB \-\-configfile Specifies an explicit config file path instead of relying on the default path(\fI/usr/local/etc/bluetooth/main.conf\fP) for the config file. .UNINDENT .INDENT 0.0 .TP .B \-d, \-\-debug=::... Sets how much information bluetoothd sends to the log destination (usually syslog\(aqs \(dqdaemon\(dq facility). If the file options are omitted, then debugging information from all the source files are printed. If file options are present, then only debug prints from that source file are printed. The option can be a pattern containing \(dq*\(dq and \(dq?\(dq characters. .sp Example: \-\-debug=src/adapter.c:src/agent.c .TP .B \-p, \-\-plugin=,,.. Load these plugins only. The option can be a pattern containing \(dq*\(dq and \(dq?\(dq characters. .TP .B \-P, \-\-noplugin=,,.. Never load these plugins. The option can be a pattern containing \(dq*\(dq and \(dq?\(dq characters. .UNINDENT .INDENT 0.0 .TP .B \-C\fP,\fB \-\-compat Provide deprecated command line interfaces. .TP .B \-E\fP,\fB \-\-experimental Enable D\-Bus experimental interfaces. These interfaces are not guaranteed to be compatible or present in future releases. .TP .B \-T\fP,\fB \-\-testing Enable D\-Bus testing interfaces. These interfaces are only meant for test validation of the internals of bluetoothd and shall not never be used by anything other than that. .UNINDENT .INDENT 0.0 .TP .B \-K, \-\-kernel=,,... Enable Kernel experimental features. Kernel experimental features are considered unstable and may be removed from future kernel releases. .UNINDENT .SH FILES .INDENT 0.0 .TP .B \fI/usr/local/etc/bluetooth/main.conf\fP Location of the global configuration file. .UNINDENT .SH RESOURCES .sp .SH REPORTING BUGS .sp .SH AUTHOR Marcel Holtmann, Philipp Matthias Hahn, Fredrik Noring .SH COPYRIGHT Free use of this software is granted under the terms of the GNU Lesser General Public Licenses (LGPL). .\" Generated by docutils manpage writer. . bluez-5.82/src/PaxHeaders/main.conf0000644000000000000000000000005014766002272014236 xustar0020 atime=1743515578 20 ctime=1743591288 bluez-5.82/src/main.conf0000644000000000000000000003175114766002272013726 0ustar00rootroot[General] # Default adapter name # Defaults to 'BlueZ X.YZ' #Name = BlueZ # Default device class. Only the major and minor device class bits are # considered. Defaults to '0x000000'. #Class = 0x000100 # How long to stay in discoverable mode before going back to non-discoverable # The value is in seconds. Default is 180, i.e. 3 minutes. # 0 = disable timer, i.e. stay discoverable forever #DiscoverableTimeout = 0 # Always allow pairing even if there are no agent registered # Possible values: true, false # Default: false #AlwaysPairable = false # How long to stay in pairable mode before going back to non-discoverable # The value is in seconds. Default is 0. # 0 = disable timer, i.e. stay pairable forever #PairableTimeout = 0 # Use vendor id source (assigner), vendor, product and version information for # DID profile support. The values are separated by ":" and assigner, VID, PID # and version. # Possible vendor id source values: bluetooth, usb (default) or false (disabled) #DeviceID = bluetooth:1234:5678:abcd # Do reverse service discovery for previously unknown devices that connect to # us. For BR/EDR this option is really only needed for qualification since the # BITE tester doesn't like us doing reverse SDP for some test cases, for LE # this disables the GATT client functionally so it can be used in system which # can only operate as peripheral (see also GATT Client option). # Defaults to 'true'. #ReverseServiceDiscovery = true # Enable name resolving after inquiry. Set it to 'false' if you don't need # remote devices name and want shorter discovery cycle. Defaults to 'true'. #NameResolving = true # Enable runtime persistency of debug link keys. Default is false which # makes debug link keys valid only for the duration of the connection # that they were created for. #DebugKeys = false # Restricts all controllers to the specified transport. Default value # is "dual", i.e. both BR/EDR and LE enabled (when supported by the HW). # Possible values: "dual", "bredr", "le" #ControllerMode = dual # Maximum number of controllers allowed to be exposed to the system. # Default=0 (unlimited) #MaxControllers=0 # Enables Multi Profile Specification support. This allows to specify if # system supports only Multiple Profiles Single Device (MPSD) configuration # or both Multiple Profiles Single Device (MPSD) and Multiple Profiles Multiple # Devices (MPMD) configurations. # Possible values: "off", "single", "multiple" #MultiProfile = off # Permanently enables the Fast Connectable setting for adapters that # support it. When enabled other devices can connect faster to us, # however the tradeoff is increased power consumptions. This feature # will fully work only on kernel version 4.1 and newer. Defaults to # 'false'. #FastConnectable = false # Default privacy setting. # Enables use of private address. # Possible values for LE mode: "off", "network/on", "device" # Possible values for Dual mode: "off", "network/on", "device", # "limited-network", "limited-device" # # - off: Local privacy disabled. # # - network/on: A device will only accept advertising packets from peer # devices that contain private addresses. It may not be compatible with some # legacy devices since it requires the use of RPA(s) all the time. # # - device: A device in device privacy mode is only concerned about the # privacy of the device and will accept advertising packets from peer devices # that contain their Identity Address as well as ones that contain a private # address, even if the peer device has distributed its IRK in the past. # - limited-network: Apply Limited Discoverable Mode to advertising, which # follows the same policy as to BR/EDR that publishes the identity address when # discoverable, and Network Privacy Mode for scanning. # # - limited-device: Apply Limited Discoverable Mode to advertising, which # follows the same policy as to BR/EDR that publishes the identity address when # discoverable, and Device Privacy Mode for scanning. # # Defaults to "off" #Privacy = off # Specify the policy to the JUST-WORKS repairing initiated by peer # Possible values: "never", "confirm", "always" # Defaults to "never" #JustWorksRepairing = never # How long to keep temporary devices around # The value is in seconds. Default is 30. # 0 = disable timer, i.e. never keep temporary devices #TemporaryTimeout = 30 # Enables the device to issue an SDP request to update known services when # profile is connected. Defaults to true. #RefreshDiscovery = true # Default Secure Connections setting. # Enables the Secure Connections setting for adapters that support it. It # provides better crypto algorithms for BT links and also enables CTKD (cross # transport key derivation) during pairing on any link. # Possible values: "off", "on", "only" # - "off": Secure Connections are disabled # - "on": Secure Connections are enabled when peer device supports them # - "only": we allow only Secure Connections # Defaults to "on" #SecureConnections = on # Enables D-Bus experimental interfaces # Possible values: true or false #Experimental = false # Enables D-Bus testing interfaces # Possible values: true or false #Testing = false # Enables kernel experimental features, alternatively a list of UUIDs # can be given. # Possible values: true,false, # Possible UUIDS: # d4992530-b9ec-469f-ab01-6c481c47da1c (BlueZ Experimental Debug) # 671b10b5-42c0-4696-9227-eb28d1b049d6 (BlueZ Experimental Simultaneous Central and Peripheral) # 15c0a148-c273-11ea-b3de-0242ac130004 (BlueZ Experimental LL privacy) # 330859bc-7506-492d-9370-9a6f0614037f (BlueZ Experimental Bluetooth Quality Report) # a6695ace-ee7f-4fb9-881a-5fac66c629af (BlueZ Experimental Offload Codecs) # 6fbaf188-05e0-496a-9885-d6ddfdb4e03e (BlueZ Experimental ISO socket) # Defaults to false. #KernelExperimental = false # The duration to avoid retrying to resolve a peer's name, if the previous # try failed. # The value is in seconds. Default is 300, i.e. 5 minutes. #RemoteNameRequestRetryDelay = 300 [BR] # The following values are used to load default adapter parameters for BR/EDR. # BlueZ loads the values into the kernel before the adapter is powered if the # kernel supports the MGMT_LOAD_DEFAULT_PARAMETERS command. If a value isn't # provided, the kernel will be initialized to it's default value. The actual # value will vary based on the kernel version and thus aren't provided here. # The Bluetooth Core Specification should be consulted for the meaning and valid # domain of each of these values. # BR/EDR Page scan activity configuration #PageScanType= #PageScanInterval= #PageScanWindow= # BR/EDR Inquiry scan activity configuration #InquiryScanType= #InquiryScanInterval= #InquiryScanWindow= # BR/EDR Link supervision timeout #LinkSupervisionTimeout= # BR/EDR Page Timeout #PageTimeout= # BR/EDR Sniff Intervals #MinSniffInterval= #MaxSniffInterval= [LE] # Enable/Disable Central Address Resolution. # 0: disable # 1: enable # Defaults to 1 #CentralAddressResolution = 1 # The following values are used to load default adapter parameters for LE. # BlueZ loads the values into the kernel before the adapter is powered if the # kernel supports the MGMT_LOAD_DEFAULT_PARAMETERS command. If a value isn't # provided, the kernel will be initialized to it's default value. The actual # value will vary based on the kernel version and thus aren't provided here. # The Bluetooth Core Specification should be consulted for the meaning and valid # domain of each of these values. # LE advertisement interval (used for legacy advertisement interface only) #MinAdvertisementInterval= #MaxAdvertisementInterval= #MultiAdvertisementRotationInterval= # LE scanning parameters used for passive scanning supporting auto connect # scenarios #ScanIntervalAutoConnect= #ScanWindowAutoConnect= # LE scanning parameters used for passive scanning supporting wake from suspend # scenarios #ScanIntervalSuspend= #ScanWindowSuspend= # LE scanning parameters used for active scanning supporting discovery # proceedure #ScanIntervalDiscovery= #ScanWindowDiscovery= # LE scanning parameters used for passive scanning supporting the advertisement # monitor Apis #ScanIntervalAdvMonitor= #ScanWindowAdvMonitor= # LE scanning parameters used for connection establishment. #ScanIntervalConnect= #ScanWindowConnect= # LE default connection parameters. These values are superceeded by any # specific values provided via the Load Connection Parameters interface #MinConnectionInterval= #MaxConnectionInterval= #ConnectionLatency= #ConnectionSupervisionTimeout= #Autoconnecttimeout= # Scan duration during interleaving scan. Only used when scanning for ADV # monitors. The units are msec. # Default: 300 #AdvMonAllowlistScanDuration= # Default: 500 #AdvMonNoFilterScanDuration= # Enable/Disable Advertisement Monitor interleave scan for power saving. # 0: disable # 1: enable # Defaults to 1 #EnableAdvMonInterleaveScan= [GATT] # GATT attribute cache. # Possible values: # always: Always cache attributes even for devices not paired, this is # recommended as it is best for interoperability, with more consistent # reconnection times and enables proper tracking of notifications for all # devices. # yes: Only cache attributes of paired devices. # no: Never cache attributes # Default: always #Cache = always # Minimum required Encryption Key Size for accessing secured characteristics. # Possible values: 0 and 7-16. 0 means don't care. # Defaults to 0 #KeySize = 0 # Exchange MTU size. # Possible values: 23-517 # Defaults to 517 #ExchangeMTU = 517 # Number of ATT channels, 1 is mandatory since it is used for ATT fixed channel # index 2-6 are used for EATT which is optional. # Possible values: 1-6 (1 disables EATT) # Default to 1 #Channels = 1 # Export claimed services by plugins # Possible values: no, read-only, read-write # Default: read-only #ExportClaimedServices = read-only [CSIS] # SIRK - Set Identification Resolution Key which is common for all the # sets. They SIRK key is used to identify its sets. This can be any # 128 bit value or a string value (e.g. product name) which is then hashed. # Possible Values: # 16 byte hexadecimal value: 861FAE703ED681F0C50B34155B6434FB # String value: "My Product Name" # Defaults to none #SIRK = # SIRK Encryption # Possible values: # true: Encrypt SIRK when read # false: Do not encrypt SIRK when read. (plaintext) # Defaults to true #Encryption = true # Total no of sets belongs to this Profile # Defaults to 0 #Size = 0 # Rank for the device # Defaults to 0 #Rank = 0 # This enables the GATT client functionally, so it can be disabled in system # which can only operate as a peripheral. # Defaults to 'true'. #Client = true [AVDTP] # AVDTP L2CAP Signalling Channel Mode. # Possible values: # basic: Use L2CAP Basic Mode # ertm: Use L2CAP Enhanced Retransmission Mode #SessionMode = basic # AVDTP L2CAP Transport Channel Mode. # Possible values: # basic: Use L2CAP Basic Mode # streaming: Use L2CAP Streaming Mode #StreamMode = basic [AVRCP] # Allow SetAbsoluteVolume calls to a peer device that does not advertise the # AVRCP remote control target profile. If it does advertise this profile, the # version is ignored. #VolumeWithoutTarget = false # Validate that remote AVRCP profiles advertise the category-2 bit before # allowing SetAbsoluteVolume calls or registering for EVENT_VOLUME_CHANGED # notifications. #VolumeCategory = true [Policy] # # The ReconnectUUIDs defines the set of remote services that should try # to be reconnected to in case of a link loss (link supervision # timeout). The policy plugin should contain a sane set of values by # default, but this list can be overridden here. By setting the list to # empty the reconnection feature gets disabled. #ReconnectUUIDs=00001112-0000-1000-8000-00805f9b34fb,0000111f-0000-1000-8000-00805f9b34fb,0000110a-0000-1000-8000-00805f9b34fb,0000110b-0000-1000-8000-00805f9b34fb # ReconnectAttempts define the number of attempts to reconnect after a link # lost. Setting the value to 0 disables reconnecting feature. #ReconnectAttempts=7 # ReconnectIntervals define the set of intervals in seconds to use in between # attempts. # If the number of attempts defined in ReconnectAttempts is bigger than the # set of intervals the last interval is repeated until the last attempt. #ReconnectIntervals=1,2,4,8,16,32,64 # AutoEnable defines option to enable all controllers when they are found. # This includes adapters present on start as well as adapters that are plugged # in later on. Defaults to 'true'. #AutoEnable=true # Audio devices that were disconnected due to suspend will be reconnected on # resume. ResumeDelay determines the delay between when the controller # resumes from suspend and a connection attempt is made. A longer delay is # better for better co-existence with Wi-Fi. # The value is in seconds. # Default: 2 #ResumeDelay = 2 [AdvMon] # Default RSSI Sampling Period. This is used when a client registers an # advertisement monitor and leaves the RSSISamplingPeriod unset. # Possible values: # 0x00 Report all advertisements # N = 0xXX Report advertisements every N x 100 msec (range: 0x01 to 0xFE) # 0xFF Report only one advertisement per device during monitoring period # Default: 0xFF #RSSISamplingPeriod=0xFF bluez-5.82/src/PaxHeaders/oui.c0000644000000000000000000000005014572354773013416 xustar0020 atime=1743516209 20 ctime=1743591285 bluez-5.82/src/oui.c0000644000000000000000000000211114572354773013072 0ustar00rootroot// SPDX-License-Identifier: GPL-2.0-or-later /* * * BlueZ - Bluetooth protocol stack for Linux * * Copyright (C) 2004-2010 Marcel Holtmann * * */ #ifdef HAVE_CONFIG_H #include #endif #define _GNU_SOURCE #include "lib/bluetooth.h" #include "oui.h" #ifdef HAVE_UDEV #include char *batocomp(const bdaddr_t *ba) { struct udev *udev; struct udev_hwdb *hwdb; struct udev_list_entry *head, *entry; char modalias[11], *comp = NULL; sprintf(modalias, "OUI:%2.2X%2.2X%2.2X", ba->b[5], ba->b[4], ba->b[3]); udev = udev_new(); if (!udev) return NULL; hwdb = udev_hwdb_new(udev); if (!hwdb) goto done; head = udev_hwdb_get_properties_list_entry(hwdb, modalias, 0); udev_list_entry_foreach(entry, head) { const char *name = udev_list_entry_get_name(entry); if (name && !strcmp(name, "ID_OUI_FROM_DATABASE")) { comp = strdup(udev_list_entry_get_value(entry)); break; } } hwdb = udev_hwdb_unref(hwdb); done: udev = udev_unref(udev); return comp; } #else char *batocomp(const bdaddr_t *ba) { return NULL; } #endif bluez-5.82/src/PaxHeaders/adapter.h0000644000000000000000000000005014711225434014231 xustar0020 atime=1743516498 20 ctime=1743591284 bluez-5.82/src/adapter.h0000644000000000000000000002602114711225434013713 0ustar00rootroot/* SPDX-License-Identifier: GPL-2.0-or-later */ /* * * BlueZ - Bluetooth protocol stack for Linux * * Copyright (C) 2006-2010 Nokia Corporation * Copyright (C) 2004-2010 Marcel Holtmann * * */ #include #include #include #include #include #define ADAPTER_INTERFACE "org.bluez.Adapter1" #define MAX_NAME_LENGTH 248 /* Invalid SSP passkey value used to indicate negative replies */ #define INVALID_PASSKEY 0xffffffff struct btd_adapter; struct btd_device; struct queue; struct btd_adapter *btd_adapter_get_default(void); bool btd_adapter_is_default(struct btd_adapter *adapter); uint16_t btd_adapter_get_index(struct btd_adapter *adapter); typedef void (*adapter_cb) (struct btd_adapter *adapter, gpointer user_data); typedef void (*oob_read_local_cb_t) (struct btd_adapter *adapter, const uint8_t *hash, const uint8_t *randomizer, void *user_data); typedef void (*oob_bonding_cb_t) (struct btd_adapter *adapter, const bdaddr_t *bdaddr, uint8_t status, void *user_data); struct oob_handler { oob_read_local_cb_t read_local_cb; oob_bonding_cb_t bonding_cb; bdaddr_t remote_addr; void *user_data; }; int adapter_init(void); void adapter_cleanup(void); void adapter_shutdown(void); typedef void (*btd_disconnect_cb) (struct btd_device *device, uint8_t reason); void btd_add_disconnect_cb(btd_disconnect_cb func); void btd_remove_disconnect_cb(btd_disconnect_cb func); typedef void (*btd_conn_fail_cb) (struct btd_device *device, uint8_t status); void btd_add_conn_fail_cb(btd_conn_fail_cb func); void btd_remove_conn_fail_cb(btd_conn_fail_cb func); struct btd_adapter *adapter_find(const bdaddr_t *sba); struct btd_adapter *adapter_find_by_id(int id); void adapter_foreach(adapter_cb func, gpointer user_data); bool btd_adapter_get_pairable(struct btd_adapter *adapter); bool btd_adapter_get_powered(struct btd_adapter *adapter); bool btd_adapter_get_connectable(struct btd_adapter *adapter); bool btd_adapter_get_discoverable(struct btd_adapter *adapter); bool btd_adapter_get_bredr(struct btd_adapter *adapter); struct btd_gatt_database *btd_adapter_get_database(struct btd_adapter *adapter); uint32_t btd_adapter_get_class(struct btd_adapter *adapter); const char *btd_adapter_get_name(struct btd_adapter *adapter); void btd_adapter_remove_device(struct btd_adapter *adapter, struct btd_device *dev); struct btd_device *btd_adapter_get_device(struct btd_adapter *adapter, const bdaddr_t *addr, uint8_t addr_type); sdp_list_t *btd_adapter_get_services(struct btd_adapter *adapter); struct btd_device *btd_adapter_find_device(struct btd_adapter *adapter, const bdaddr_t *dst, uint8_t dst_type); struct btd_device *btd_adapter_find_device_by_path(struct btd_adapter *adapter, const char *path); struct btd_device *btd_adapter_find_device_by_fd(int fd); void btd_adapter_device_found(struct btd_adapter *adapter, const bdaddr_t *bdaddr, uint8_t bdaddr_type, int8_t rssi, uint32_t flags, const uint8_t *data, uint8_t data_len, bool monitoring); const char *adapter_get_path(struct btd_adapter *adapter); const bdaddr_t *btd_adapter_get_address(struct btd_adapter *adapter); uint8_t btd_adapter_get_address_type(struct btd_adapter *adapter); const char *btd_adapter_get_storage_dir(struct btd_adapter *adapter); int adapter_set_name(struct btd_adapter *adapter, const char *name); int adapter_service_add(struct btd_adapter *adapter, sdp_record_t *rec); void adapter_service_remove(struct btd_adapter *adapter, uint32_t handle); struct agent *adapter_get_agent(struct btd_adapter *adapter); bool btd_adapter_uuid_is_allowed(struct btd_adapter *adapter, const char *uuid); struct btd_adapter *btd_adapter_ref(struct btd_adapter *adapter); void btd_adapter_unref(struct btd_adapter *adapter); void btd_adapter_set_class(struct btd_adapter *adapter, uint8_t major, uint8_t minor); struct btd_adapter_driver { const char *name; int (*probe)(struct btd_adapter *adapter); void (*remove)(struct btd_adapter *adapter); void (*resume)(struct btd_adapter *adapter); void (*device_added)(struct btd_adapter *adapter, struct btd_device *device); void (*device_removed)(struct btd_adapter *adapter, struct btd_device *device); void (*device_resolved)(struct btd_adapter *adapter, struct btd_device *device); /* Indicates the driver is experimental and shall only be registered * when experimental has been enabled (see: main.conf:Experimental). */ bool experimental; }; void device_resolved_drivers(struct btd_adapter *adapter, struct btd_device *device); typedef void (*service_auth_cb) (DBusError *derr, void *user_data); void adapter_add_profile(struct btd_adapter *adapter, gpointer p); void adapter_remove_profile(struct btd_adapter *adapter, gpointer p); int btd_register_adapter_driver(struct btd_adapter_driver *driver); void btd_unregister_adapter_driver(struct btd_adapter_driver *driver); guint btd_request_authorization(const bdaddr_t *src, const bdaddr_t *dst, const char *uuid, service_auth_cb cb, void *user_data); guint btd_request_authorization_cable_configured(const bdaddr_t *src, const bdaddr_t *dst, const char *uuid, service_auth_cb cb, void *user_data); int btd_cancel_authorization(guint id); int btd_adapter_restore_powered(struct btd_adapter *adapter); int btd_adapter_set_blocked(struct btd_adapter *adapter); typedef ssize_t (*btd_adapter_pin_cb_t) (struct btd_adapter *adapter, struct btd_device *dev, char *out, bool *display, unsigned int attempt); void btd_adapter_register_pin_cb(struct btd_adapter *adapter, btd_adapter_pin_cb_t cb); void btd_adapter_unregister_pin_cb(struct btd_adapter *adapter, btd_adapter_pin_cb_t cb); struct btd_adapter_pin_cb_iter *btd_adapter_pin_cb_iter_new( struct btd_adapter *adapter); void btd_adapter_pin_cb_iter_free(struct btd_adapter_pin_cb_iter *iter); bool btd_adapter_pin_cb_iter_end(struct btd_adapter_pin_cb_iter *iter); typedef void (*btd_msd_cb_t) (struct btd_adapter *adapter, struct btd_device *dev, uint16_t company, const uint8_t *data, uint8_t data_len); void btd_adapter_register_msd_cb(struct btd_adapter *adapter, btd_msd_cb_t cb); void btd_adapter_unregister_msd_cb(struct btd_adapter *adapter, btd_msd_cb_t cb); /* If TRUE, enables fast connectabe, i.e. reduces page scan interval and changes * type. If FALSE, disables fast connectable, i.e. sets page scan interval and * type to default values. Valid for both connectable and discoverable modes. */ int btd_adapter_set_fast_connectable(struct btd_adapter *adapter, gboolean enable); int btd_adapter_read_clock(struct btd_adapter *adapter, const bdaddr_t *bdaddr, int which, int timeout, uint32_t *clock, uint16_t *accuracy); int btd_adapter_block_address(struct btd_adapter *adapter, const bdaddr_t *bdaddr, uint8_t bdaddr_type); int btd_adapter_unblock_address(struct btd_adapter *adapter, const bdaddr_t *bdaddr, uint8_t bdaddr_type); int btd_adapter_disconnect_device(struct btd_adapter *adapter, const bdaddr_t *bdaddr, uint8_t bdaddr_type); int btd_adapter_remove_bonding(struct btd_adapter *adapter, const bdaddr_t *bdaddr, uint8_t bdaddr_type); int btd_adapter_pincode_reply(struct btd_adapter *adapter, const bdaddr_t *bdaddr, const char *pin, size_t pin_len); int btd_adapter_confirm_reply(struct btd_adapter *adapter, const bdaddr_t *bdaddr, uint8_t bdaddr_type, gboolean success); int btd_adapter_passkey_reply(struct btd_adapter *adapter, const bdaddr_t *bdaddr, uint8_t bdaddr_type, uint32_t passkey); int adapter_create_bonding(struct btd_adapter *adapter, const bdaddr_t *bdaddr, uint8_t addr_type, uint8_t io_cap); int adapter_bonding_attempt(struct btd_adapter *adapter, const bdaddr_t *bdaddr, uint8_t addr_type, uint8_t io_cap); int adapter_cancel_bonding(struct btd_adapter *adapter, const bdaddr_t *bdaddr, uint8_t addr_type); int adapter_set_io_capability(struct btd_adapter *adapter, uint8_t io_cap); int btd_adapter_read_local_oob_data(struct btd_adapter *adapter); int btd_adapter_add_remote_oob_data(struct btd_adapter *adapter, const bdaddr_t *bdaddr, uint8_t *hash, uint8_t *randomizer); int btd_adapter_remove_remote_oob_data(struct btd_adapter *adapter, const bdaddr_t *bdaddr); int btd_adapter_gatt_server_start(struct btd_adapter *adapter); void btd_adapter_gatt_server_stop(struct btd_adapter *adapter); bool btd_adapter_ssp_enabled(struct btd_adapter *adapter); int adapter_connect_list_add(struct btd_adapter *adapter, struct btd_device *device); void adapter_connect_list_remove(struct btd_adapter *adapter, struct btd_device *device); typedef void (*adapter_set_device_flags_func_t)(uint8_t status, uint16_t length, const void *param, void *user_data); void adapter_set_device_flags(struct btd_adapter *adapter, struct btd_device *device, uint32_t flags, adapter_set_device_flags_func_t func, void *user_data); void adapter_auto_connect_add(struct btd_adapter *adapter, struct btd_device *device); void adapter_auto_connect_remove(struct btd_adapter *adapter, struct btd_device *device); void adapter_accept_list_add(struct btd_adapter *adapter, struct btd_device *dev); void adapter_accept_list_remove(struct btd_adapter *adapter, struct btd_device *dev); void btd_adapter_set_oob_handler(struct btd_adapter *adapter, struct oob_handler *handler); gboolean btd_adapter_check_oob_handler(struct btd_adapter *adapter); void btd_adapter_for_each_device(struct btd_adapter *adapter, void (*cb)(struct btd_device *device, void *data), void *data); bool btd_le_connect_before_pairing(void); bool btd_adapter_has_settings(struct btd_adapter *adapter, uint32_t settings); enum experimental_features { EXP_FEAT_DEBUG = 1 << 0, EXP_FEAT_LE_SIMULT_ROLES = 1 << 1, EXP_FEAT_BQR = 1 << 2, EXP_FEAT_RPA_RESOLUTION = 1 << 3, EXP_FEAT_CODEC_OFFLOAD = 1 << 4, EXP_FEAT_ISO_SOCKET = 1 << 5, }; bool btd_adapter_has_exp_feature(struct btd_adapter *adapter, uint32_t feature); enum kernel_features { KERNEL_CONN_CONTROL = 1 << 0, KERNEL_BLOCKED_KEYS_SUPPORTED = 1 << 1, KERNEL_SET_SYSTEM_CONFIG = 1 << 2, KERNEL_EXP_FEATURES = 1 << 3, KERNEL_HAS_RESUME_EVT = 1 << 4, KERNEL_HAS_EXT_ADV_ADD_CMDS = 1 << 5, KERNEL_HAS_CONTROLLER_CAP_CMD = 1 << 6, }; bool btd_has_kernel_features(uint32_t feature); bool btd_adapter_set_allowed_uuids(struct btd_adapter *adapter, struct queue *uuids); bool btd_adapter_is_uuid_allowed(struct btd_adapter *adapter, const char *uuid_str); void btd_adapter_load_conn_param(struct btd_adapter *adapter, const bdaddr_t *peer, uint8_t bdaddr_type, uint16_t min_interval, uint16_t max_interval, uint16_t latency, uint16_t timeout); void btd_adapter_store_conn_param(struct btd_adapter *adapter, const bdaddr_t *peer, uint8_t bdaddr_type, uint16_t min_interval, uint16_t max_interval, uint16_t latency, uint16_t timeout); void btd_adapter_cancel_service_auth(struct btd_adapter *adapter, struct btd_device *device); bluez-5.82/src/PaxHeaders/bluetoothd.rst0000644000000000000000000000005014773211427015350 xustar0020 atime=1743590182 20 ctime=1743591291 bluez-5.82/src/bluetoothd.rst0000644000000000000000000000534614773211427015041 0ustar00rootroot========== bluetoothd ========== ---------------- Bluetooth daemon ---------------- :Authors: - Marcel Holtmann - Philipp Matthias Hahn - Fredrik Noring :Version: BlueZ :Copyright: Free use of this software is granted under the terms of the GNU Lesser General Public Licenses (LGPL). :Date: March, 2004 :Manual section: 8 :Manual group: System management commands SYNOPSIS ======== **bluetoothd** [--version] | [--help] **bluetoothd** [--nodetach] [--compat] [--experimental] [--debug=<*files*>] [--plugin=<*plugins*>] [--noplugin=<*plugins*>] DESCRIPTION =========== This manual page documents briefly the **bluetoothd** daemon, which manages all the Bluetooth devices. **bluetoothd** can also provide a number of services via the D-Bus message bus system. OPTIONS ======= -v, --version Print bluetoothd version and exit. -h, --help Print bluetoothd options and exit. -n, --nodetach Enable logging in foreground. Directs log output to the controlling terminal in addition to syslog. -f, --configfile Specifies an explicit config file path instead of relying on the default path(*/usr/local/etc/bluetooth/main.conf*) for the config file. -d, --debug=::... Sets how much information bluetoothd sends to the log destination (usually syslog's "daemon" facility). If the file options are omitted, then debugging information from all the source files are printed. If file options are present, then only debug prints from that source file are printed. The option can be a pattern containing "*" and "?" characters. Example: --debug=src/adapter.c:src/agent.c -p, --plugin=,,.. Load these plugins only. The option can be a pattern containing "*" and "?" characters. -P, --noplugin=,,.. Never load these plugins. The option can be a pattern containing "*" and "?" characters. -C, --compat Provide deprecated command line interfaces. -E, --experimental Enable D-Bus experimental interfaces. These interfaces are not guaranteed to be compatible or present in future releases. -T, --testing Enable D-Bus testing interfaces. These interfaces are only meant for test validation of the internals of bluetoothd and shall not never be used by anything other than that. -K, --kernel=,,... Enable Kernel experimental features. Kernel experimental features are considered unstable and may be removed from future kernel releases. FILES ===== */usr/local/etc/bluetooth/main.conf* Location of the global configuration file. RESOURCES ========= http://www.bluez.org REPORTING BUGS ============== linux-bluetooth@vger.kernel.org bluez-5.82/src/PaxHeaders/bluetooth.service.in0000644000000000000000000000005014572354773016452 xustar0020 atime=1743515749 20 ctime=1743591275 bluez-5.82/src/bluetooth.service.in0000644000000000000000000000136114572354773016134 0ustar00rootroot[Unit] Description=Bluetooth service Documentation=man:bluetoothd(8) ConditionPathIsDirectory=/sys/class/bluetooth [Service] Type=dbus BusName=org.bluez ExecStart=@PKGLIBEXECDIR@/bluetoothd NotifyAccess=main #WatchdogSec=10 #Restart=on-failure CapabilityBoundingSet=CAP_NET_ADMIN CAP_NET_BIND_SERVICE LimitNPROC=1 # Filesystem lockdown ProtectHome=true ProtectSystem=strict PrivateTmp=true ProtectKernelTunables=true ProtectControlGroups=true StateDirectory=bluetooth StateDirectoryMode=0700 ConfigurationDirectory=bluetooth ConfigurationDirectoryMode=0555 # Execute Mappings MemoryDenyWriteExecute=true # Privilege escalation NoNewPrivileges=true # Real-time RestrictRealtime=true [Install] WantedBy=bluetooth.target Alias=dbus-org.bluez.service bluez-5.82/src/PaxHeaders/battery.h0000644000000000000000000000005014015011623014251 xustar0020 atime=1743516573 20 ctime=1743591285 bluez-5.82/src/battery.h0000644000000000000000000000123514015011623013733 0ustar00rootroot/* SPDX-License-Identifier: GPL-2.0-or-later */ /* * * BlueZ - Bluetooth protocol stack for Linux * * Copyright (C) 2020 Google LLC * * */ struct btd_adapter; struct btd_battery; struct btd_battery_provider_manager; struct btd_battery *btd_battery_register(const char *path, const char *source, const char *provider_path); bool btd_battery_unregister(struct btd_battery *battery); bool btd_battery_update(struct btd_battery *battery, uint8_t percentage); struct btd_battery_provider_manager * btd_battery_provider_manager_create(struct btd_adapter *adapter); void btd_battery_provider_manager_destroy( struct btd_battery_provider_manager *manager); bluez-5.82/src/PaxHeaders/bluetoothd.rst.in0000644000000000000000000000005014766002272015753 xustar0020 atime=1743515578 20 ctime=1743591275 bluez-5.82/src/bluetoothd.rst.in0000644000000000000000000000531414766002272015437 0ustar00rootroot========== bluetoothd ========== ---------------- Bluetooth daemon ---------------- :Authors: - Marcel Holtmann - Philipp Matthias Hahn - Fredrik Noring :Version: BlueZ :Copyright: Free use of this software is granted under the terms of the GNU Lesser General Public Licenses (LGPL). :Date: March, 2004 :Manual section: 8 :Manual group: System management commands SYNOPSIS ======== **bluetoothd** [--version] | [--help] **bluetoothd** [--nodetach] [--compat] [--experimental] [--debug=<*files*>] [--plugin=<*plugins*>] [--noplugin=<*plugins*>] DESCRIPTION =========== This manual page documents briefly the **bluetoothd** daemon, which manages all the Bluetooth devices. **bluetoothd** can also provide a number of services via the D-Bus message bus system. OPTIONS ======= -v, --version Print bluetoothd version and exit. -h, --help Print bluetoothd options and exit. -n, --nodetach Enable logging in foreground. Directs log output to the controlling terminal in addition to syslog. -f, --configfile Specifies an explicit config file path instead of relying on the default path(*@CONFIGDIR@/main.conf*) for the config file. -d, --debug=::... Sets how much information bluetoothd sends to the log destination (usually syslog's "daemon" facility). If the file options are omitted, then debugging information from all the source files are printed. If file options are present, then only debug prints from that source file are printed. The option can be a pattern containing "*" and "?" characters. Example: --debug=src/adapter.c:src/agent.c -p, --plugin=,,.. Load these plugins only. The option can be a pattern containing "*" and "?" characters. -P, --noplugin=,,.. Never load these plugins. The option can be a pattern containing "*" and "?" characters. -C, --compat Provide deprecated command line interfaces. -E, --experimental Enable D-Bus experimental interfaces. These interfaces are not guaranteed to be compatible or present in future releases. -T, --testing Enable D-Bus testing interfaces. These interfaces are only meant for test validation of the internals of bluetoothd and shall not never be used by anything other than that. -K, --kernel=,,... Enable Kernel experimental features. Kernel experimental features are considered unstable and may be removed from future kernel releases. FILES ===== *@CONFIGDIR@/main.conf* Location of the global configuration file. RESOURCES ========= http://www.bluez.org REPORTING BUGS ============== linux-bluetooth@vger.kernel.org bluez-5.82/src/PaxHeaders/agent.c0000644000000000000000000000005014015011623013670 xustar0020 atime=1743516633 20 ctime=1743591284 bluez-5.82/src/agent.c0000644000000000000000000006056714015011623013367 0ustar00rootroot// SPDX-License-Identifier: GPL-2.0-or-later /* * * BlueZ - Bluetooth protocol stack for Linux * * Copyright (C) 2006-2010 Nokia Corporation * Copyright (C) 2004-2010 Marcel Holtmann * * */ #ifdef HAVE_CONFIG_H #include #endif #include #include #include #include #include #include #include #include #include "lib/bluetooth.h" #include "lib/sdp.h" #include "gdbus/gdbus.h" #include "log.h" #include "error.h" #include "btd.h" #include "dbus-common.h" #include "adapter.h" #include "device.h" #include "agent.h" #include "shared/queue.h" #define REQUEST_TIMEOUT (60 * 1000) /* 60 seconds */ #define AGENT_INTERFACE "org.bluez.Agent1" static GHashTable *agent_list; struct queue *default_agents = NULL; typedef enum { AGENT_REQUEST_PASSKEY, AGENT_REQUEST_CONFIRMATION, AGENT_REQUEST_AUTHORIZATION, AGENT_REQUEST_PINCODE, AGENT_REQUEST_AUTHORIZE_SERVICE, AGENT_REQUEST_DISPLAY_PINCODE, } agent_request_type_t; struct agent { int ref; char *owner; char *path; uint8_t capability; struct agent_request *request; guint watch; }; struct agent_request { agent_request_type_t type; struct btd_device *device; /* Weak reference */ struct agent *agent; DBusMessage *msg; DBusPendingCall *call; void *cb; void *user_data; GDestroyNotify destroy; }; static void agent_release(struct agent *agent) { DBusMessage *message; DBG("Releasing agent %s, %s", agent->owner, agent->path); if (agent->request) agent_cancel(agent); message = dbus_message_new_method_call(agent->owner, agent->path, AGENT_INTERFACE, "Release"); if (message == NULL) { error("Couldn't allocate D-Bus message"); return; } g_dbus_send_message(btd_get_dbus_connection(), message); } static int send_cancel_request(struct agent_request *req) { DBusMessage *message; DBG("Sending Cancel request to %s, %s", req->agent->owner, req->agent->path); message = dbus_message_new_method_call(req->agent->owner, req->agent->path, AGENT_INTERFACE, "Cancel"); if (message == NULL) { error("Couldn't allocate D-Bus message"); return -ENOMEM; } g_dbus_send_message(btd_get_dbus_connection(), message); return 0; } static void agent_request_free(struct agent_request *req, gboolean destroy) { if (req->msg) dbus_message_unref(req->msg); if (req->call) dbus_pending_call_unref(req->call); if (req->agent && req->agent->request) req->agent->request = NULL; if (destroy && req->destroy) req->destroy(req->user_data); g_free(req); } static void set_io_cap(struct btd_adapter *adapter, gpointer user_data) { struct agent *agent = user_data; uint8_t io_cap; if (agent) io_cap = agent->capability; else io_cap = IO_CAPABILITY_INVALID; adapter_set_io_capability(adapter, io_cap); } static bool add_default_agent(struct agent *agent) { if (queue_peek_head(default_agents) == agent) return true; queue_remove(default_agents, agent); if (!queue_push_head(default_agents, agent)) return false; DBG("Default agent set to %s %s", agent->owner, agent->path); adapter_foreach(set_io_cap, agent); return true; } static void remove_default_agent(struct agent *agent) { if (queue_peek_head(default_agents) != agent) { queue_remove(default_agents, agent); return; } queue_remove(default_agents, agent); agent = queue_peek_head(default_agents); if (agent) DBG("Default agent set to %s %s", agent->owner, agent->path); else DBG("Default agent cleared"); adapter_foreach(set_io_cap, agent); } static void agent_disconnect(DBusConnection *conn, void *user_data) { struct agent *agent = user_data; DBG("Agent %s disconnected", agent->owner); if (agent->watch > 0) { g_dbus_remove_watch(conn, agent->watch); agent->watch = 0; } remove_default_agent(agent); g_hash_table_remove(agent_list, agent->owner); } struct agent *agent_ref(struct agent *agent) { agent->ref++; DBG("%p: ref=%d", agent, agent->ref); return agent; } void agent_unref(struct agent *agent) { agent->ref--; DBG("%p: ref=%d", agent, agent->ref); if (agent->ref > 0) return; if (agent->request) { DBusError err; agent_pincode_cb pincode_cb; agent_passkey_cb passkey_cb; agent_cb cb; dbus_error_init(&err); dbus_set_error_const(&err, ERROR_INTERFACE ".Failed", "Canceled"); switch (agent->request->type) { case AGENT_REQUEST_PINCODE: pincode_cb = agent->request->cb; pincode_cb(agent, &err, NULL, agent->request->user_data); break; case AGENT_REQUEST_PASSKEY: passkey_cb = agent->request->cb; passkey_cb(agent, &err, 0, agent->request->user_data); break; case AGENT_REQUEST_CONFIRMATION: case AGENT_REQUEST_AUTHORIZATION: case AGENT_REQUEST_AUTHORIZE_SERVICE: case AGENT_REQUEST_DISPLAY_PINCODE: default: cb = agent->request->cb; cb(agent, &err, agent->request->user_data); } dbus_error_free(&err); agent_cancel(agent); } g_free(agent->owner); g_free(agent->path); g_free(agent); } struct agent *agent_get(const char *owner) { struct agent *agent; if (owner) { agent = g_hash_table_lookup(agent_list, owner); if (agent) return agent_ref(agent); } if (!queue_isempty(default_agents)) return agent_ref(queue_peek_head(default_agents)); return NULL; } static struct agent *agent_create( const char *name, const char *path, uint8_t capability) { struct agent *agent; agent = g_new0(struct agent, 1); agent->owner = g_strdup(name); agent->path = g_strdup(path); agent->capability = capability; agent->watch = g_dbus_add_disconnect_watch(btd_get_dbus_connection(), name, agent_disconnect, agent, NULL); if (queue_isempty(default_agents)) add_default_agent(agent); else queue_push_tail(default_agents, agent); return agent_ref(agent); } static struct agent_request *agent_request_new(struct agent *agent, struct btd_device *device, agent_request_type_t type, void *cb, void *user_data, GDestroyNotify destroy) { struct agent_request *req; req = g_new0(struct agent_request, 1); req->agent = agent; req->device = device; req->type = type; req->cb = cb; req->user_data = user_data; req->destroy = destroy; return req; } int agent_cancel(struct agent *agent) { if (!agent->request) return -EINVAL; if (agent->request->call) { dbus_pending_call_cancel(agent->request->call); send_cancel_request(agent->request); } agent_request_free(agent->request, TRUE); agent->request = NULL; return 0; } static void simple_agent_reply(DBusPendingCall *call, void *user_data) { struct agent_request *req = user_data; struct agent *agent = req->agent; DBusMessage *message; DBusError err; agent_cb cb = req->cb; /* steal_reply will always return non-NULL since the callback * is only called after a reply has been received */ message = dbus_pending_call_steal_reply(call); /* Protect from the callback freeing the agent */ agent_ref(agent); dbus_error_init(&err); if (dbus_set_error_from_message(&err, message)) { DBG("agent error reply: %s, %s", err.name, err.message); cb(agent, &err, req->user_data); if (dbus_error_has_name(&err, DBUS_ERROR_NO_REPLY)) { error("Timed out waiting for reply from agent"); agent_cancel(agent); dbus_message_unref(message); dbus_error_free(&err); agent_unref(agent); return; } dbus_error_free(&err); goto done; } if (!dbus_message_get_args(message, &err, DBUS_TYPE_INVALID)) { error("Wrong reply signature: %s", err.message); cb(agent, &err, req->user_data); dbus_error_free(&err); goto done; } cb(agent, NULL, req->user_data); done: dbus_message_unref(message); agent->request = NULL; agent_request_free(req, TRUE); agent_unref(agent); } static int agent_call_authorize_service(struct agent_request *req, const char *uuid) { struct agent *agent = req->agent; const char *path; req->msg = dbus_message_new_method_call(agent->owner, agent->path, AGENT_INTERFACE, "AuthorizeService"); if (!req->msg) { error("Couldn't allocate D-Bus message"); return -ENOMEM; } path = device_get_path(req->device); dbus_message_append_args(req->msg, DBUS_TYPE_OBJECT_PATH, &path, DBUS_TYPE_STRING, &uuid, DBUS_TYPE_INVALID); if (g_dbus_send_message_with_reply(btd_get_dbus_connection(), req->msg, &req->call, REQUEST_TIMEOUT) == FALSE) { error("D-Bus send failed"); return -EIO; } dbus_pending_call_set_notify(req->call, simple_agent_reply, req, NULL); DBG("authorize service request was sent for %s", path); return 0; } static int agent_has_request(struct agent *agent, struct btd_device *device, agent_request_type_t type) { if (!agent->request) return 0; if (agent->request->type != type) return -EBUSY; /* Check if request pending is for the same address */ if (bacmp(device_get_address(agent->request->device), btd_adapter_get_address(device_get_adapter(device)))) return -EBUSY; /* It must match in either direction */ if (bacmp(device_get_address(device), btd_adapter_get_address( device_get_adapter(agent->request->device)))) return -EBUSY; return -EINPROGRESS; } int agent_authorize_service(struct agent *agent, struct btd_device *device, const char *uuid, agent_cb cb, void *user_data, GDestroyNotify destroy) { struct agent_request *req; int err; err = agent_has_request(agent, device, AGENT_REQUEST_AUTHORIZE_SERVICE); if (err) return err; req = agent_request_new(agent, device, AGENT_REQUEST_AUTHORIZE_SERVICE, cb, user_data, destroy); err = agent_call_authorize_service(req, uuid); if (err < 0) { agent_request_free(req, FALSE); return -ENOMEM; } agent->request = req; return 0; } static void pincode_reply(DBusPendingCall *call, void *user_data) { struct agent_request *req = user_data; struct agent *agent = req->agent; agent_pincode_cb cb = req->cb; DBusMessage *message; DBusError err; size_t len; char *pin; /* steal_reply will always return non-NULL since the callback * is only called after a reply has been received */ message = dbus_pending_call_steal_reply(call); /* Protect from the callback freeing the agent */ agent_ref(agent); dbus_error_init(&err); if (dbus_set_error_from_message(&err, message)) { error("Agent %s replied with an error: %s, %s", agent->path, err.name, err.message); cb(agent, &err, NULL, req->user_data); dbus_error_free(&err); goto done; } if (!dbus_message_get_args(message, &err, DBUS_TYPE_STRING, &pin, DBUS_TYPE_INVALID)) { error("Wrong passkey reply signature: %s", err.message); cb(agent, &err, NULL, req->user_data); dbus_error_free(&err); goto done; } len = strlen(pin); if (len > 16 || len < 1) { error("Invalid PIN length (%zu) from agent", len); dbus_set_error_const(&err, ERROR_INTERFACE ".InvalidArgs", "Invalid passkey length"); cb(agent, &err, NULL, req->user_data); dbus_error_free(&err); goto done; } cb(agent, NULL, pin, req->user_data); done: if (message) dbus_message_unref(message); dbus_pending_call_cancel(req->call); agent->request = NULL; agent_request_free(req, TRUE); agent_unref(agent); } static int pincode_request_new(struct agent_request *req, dbus_bool_t secure) { struct agent *agent = req->agent; const char *path; /* TODO: Add a new method or a new param to Agent interface to request secure pin. */ req->msg = dbus_message_new_method_call(agent->owner, agent->path, AGENT_INTERFACE, "RequestPinCode"); if (req->msg == NULL) { error("Couldn't allocate D-Bus message"); return -ENOMEM; } path = device_get_path(req->device); dbus_message_append_args(req->msg, DBUS_TYPE_OBJECT_PATH, &path, DBUS_TYPE_INVALID); if (g_dbus_send_message_with_reply(btd_get_dbus_connection(), req->msg, &req->call, REQUEST_TIMEOUT) == FALSE) { error("D-Bus send failed"); return -EIO; } dbus_pending_call_set_notify(req->call, pincode_reply, req, NULL); return 0; } int agent_request_pincode(struct agent *agent, struct btd_device *device, agent_pincode_cb cb, gboolean secure, void *user_data, GDestroyNotify destroy) { struct agent_request *req; int err; if (agent->request) return -EBUSY; req = agent_request_new(agent, device, AGENT_REQUEST_PINCODE, cb, user_data, destroy); err = pincode_request_new(req, secure); if (err < 0) goto failed; agent->request = req; return 0; failed: agent_request_free(req, FALSE); return err; } static void passkey_reply(DBusPendingCall *call, void *user_data) { struct agent_request *req = user_data; struct agent *agent = req->agent; agent_passkey_cb cb = req->cb; DBusMessage *message; DBusError err; uint32_t passkey; /* steal_reply will always return non-NULL since the callback * is only called after a reply has been received */ message = dbus_pending_call_steal_reply(call); dbus_error_init(&err); if (dbus_set_error_from_message(&err, message)) { error("Agent replied with an error: %s, %s", err.name, err.message); cb(agent, &err, 0, req->user_data); dbus_error_free(&err); goto done; } if (!dbus_message_get_args(message, &err, DBUS_TYPE_UINT32, &passkey, DBUS_TYPE_INVALID)) { error("Wrong passkey reply signature: %s", err.message); cb(agent, &err, 0, req->user_data); dbus_error_free(&err); goto done; } cb(agent, NULL, passkey, req->user_data); done: if (message) dbus_message_unref(message); dbus_pending_call_cancel(req->call); agent->request = NULL; agent_request_free(req, TRUE); } static int passkey_request_new(struct agent_request *req) { struct agent *agent = req->agent; const char *path; req->msg = dbus_message_new_method_call(agent->owner, agent->path, AGENT_INTERFACE, "RequestPasskey"); if (req->msg == NULL) { error("Couldn't allocate D-Bus message"); return -ENOMEM; } path = device_get_path(req->device); dbus_message_append_args(req->msg, DBUS_TYPE_OBJECT_PATH, &path, DBUS_TYPE_INVALID); if (g_dbus_send_message_with_reply(btd_get_dbus_connection(), req->msg, &req->call, REQUEST_TIMEOUT) == FALSE) { error("D-Bus send failed"); return -EIO; } dbus_pending_call_set_notify(req->call, passkey_reply, req, NULL); return 0; } int agent_request_passkey(struct agent *agent, struct btd_device *device, agent_passkey_cb cb, void *user_data, GDestroyNotify destroy) { struct agent_request *req; int err; if (agent->request) return -EBUSY; DBG("Calling Agent.RequestPasskey: name=%s, path=%s", agent->owner, agent->path); req = agent_request_new(agent, device, AGENT_REQUEST_PASSKEY, cb, user_data, destroy); err = passkey_request_new(req); if (err < 0) goto failed; agent->request = req; return 0; failed: agent_request_free(req, FALSE); return err; } static int confirmation_request_new(struct agent_request *req, uint32_t passkey) { struct agent *agent = req->agent; const char *path; req->msg = dbus_message_new_method_call(agent->owner, agent->path, AGENT_INTERFACE, "RequestConfirmation"); if (req->msg == NULL) { error("Couldn't allocate D-Bus message"); return -ENOMEM; } path = device_get_path(req->device); dbus_message_append_args(req->msg, DBUS_TYPE_OBJECT_PATH, &path, DBUS_TYPE_UINT32, &passkey, DBUS_TYPE_INVALID); if (g_dbus_send_message_with_reply(btd_get_dbus_connection(), req->msg, &req->call, REQUEST_TIMEOUT) == FALSE) { error("D-Bus send failed"); return -EIO; } dbus_pending_call_set_notify(req->call, simple_agent_reply, req, NULL); return 0; } int agent_request_confirmation(struct agent *agent, struct btd_device *device, uint32_t passkey, agent_cb cb, void *user_data, GDestroyNotify destroy) { struct agent_request *req; int err; err = agent_has_request(agent, device, AGENT_REQUEST_CONFIRMATION); if (err) return err; DBG("Calling Agent.RequestConfirmation: name=%s, path=%s, passkey=%06u", agent->owner, agent->path, passkey); req = agent_request_new(agent, device, AGENT_REQUEST_CONFIRMATION, cb, user_data, destroy); err = confirmation_request_new(req, passkey); if (err < 0) goto failed; agent->request = req; return 0; failed: agent_request_free(req, FALSE); return err; } static int authorization_request_new(struct agent_request *req) { struct agent *agent = req->agent; const char *path; req->msg = dbus_message_new_method_call(agent->owner, agent->path, AGENT_INTERFACE, "RequestAuthorization"); if (req->msg == NULL) { error("Couldn't allocate D-Bus message"); return -ENOMEM; } path = device_get_path(req->device); dbus_message_append_args(req->msg, DBUS_TYPE_OBJECT_PATH, &path, DBUS_TYPE_INVALID); if (g_dbus_send_message_with_reply(btd_get_dbus_connection(), req->msg, &req->call, REQUEST_TIMEOUT) == FALSE) { error("D-Bus send failed"); return -EIO; } dbus_pending_call_set_notify(req->call, simple_agent_reply, req, NULL); return 0; } int agent_request_authorization(struct agent *agent, struct btd_device *device, agent_cb cb, void *user_data, GDestroyNotify destroy) { struct agent_request *req; int err; err = agent_has_request(agent, device, AGENT_REQUEST_AUTHORIZATION); if (err) return err; DBG("Calling Agent.RequestAuthorization: name=%s, path=%s", agent->owner, agent->path); req = agent_request_new(agent, device, AGENT_REQUEST_AUTHORIZATION, cb, user_data, destroy); err = authorization_request_new(req); if (err < 0) goto failed; agent->request = req; return 0; failed: agent_request_free(req, FALSE); return err; } int agent_display_passkey(struct agent *agent, struct btd_device *device, uint32_t passkey, uint16_t entered) { DBusMessage *message; const char *dev_path = device_get_path(device); message = dbus_message_new_method_call(agent->owner, agent->path, AGENT_INTERFACE, "DisplayPasskey"); if (!message) { error("Couldn't allocate D-Bus message"); return -1; } dbus_message_append_args(message, DBUS_TYPE_OBJECT_PATH, &dev_path, DBUS_TYPE_UINT32, &passkey, DBUS_TYPE_UINT16, &entered, DBUS_TYPE_INVALID); if (!g_dbus_send_message(btd_get_dbus_connection(), message)) { error("D-Bus send failed"); return -1; } return 0; } static void display_pincode_reply(DBusPendingCall *call, void *user_data) { struct agent_request *req = user_data; struct agent *agent = req->agent; DBusMessage *message; DBusError err; agent_cb cb = req->cb; /* clear agent->request early; our callback will likely try * another request */ agent->request = NULL; /* steal_reply will always return non-NULL since the callback * is only called after a reply has been received */ message = dbus_pending_call_steal_reply(call); dbus_error_init(&err); if (dbus_set_error_from_message(&err, message)) { error("Agent replied with an error: %s, %s", err.name, err.message); cb(agent, &err, req->user_data); if (dbus_error_has_name(&err, DBUS_ERROR_NO_REPLY)) { agent_cancel(agent); dbus_message_unref(message); dbus_error_free(&err); return; } dbus_error_free(&err); goto done; } if (!dbus_message_get_args(message, &err, DBUS_TYPE_INVALID)) { error("Wrong reply signature: %s", err.message); cb(agent, &err, req->user_data); dbus_error_free(&err); goto done; } cb(agent, NULL, req->user_data); done: dbus_message_unref(message); agent_request_free(req, TRUE); } static int display_pincode_request_new(struct agent_request *req, const char *pincode) { struct agent *agent = req->agent; const char *path; req->msg = dbus_message_new_method_call(agent->owner, agent->path, AGENT_INTERFACE, "DisplayPinCode"); if (req->msg == NULL) { error("Couldn't allocate D-Bus message"); return -ENOMEM; } path = device_get_path(req->device); dbus_message_append_args(req->msg, DBUS_TYPE_OBJECT_PATH, &path, DBUS_TYPE_STRING, &pincode, DBUS_TYPE_INVALID); if (g_dbus_send_message_with_reply(btd_get_dbus_connection(), req->msg, &req->call, REQUEST_TIMEOUT) == FALSE) { error("D-Bus send failed"); return -EIO; } dbus_pending_call_set_notify(req->call, display_pincode_reply, req, NULL); return 0; } int agent_display_pincode(struct agent *agent, struct btd_device *device, const char *pincode, agent_cb cb, void *user_data, GDestroyNotify destroy) { struct agent_request *req; int err; err = agent_has_request(agent, device, AGENT_REQUEST_DISPLAY_PINCODE); if (err) return err; DBG("Calling Agent.DisplayPinCode: name=%s, path=%s, pincode=%s", agent->owner, agent->path, pincode); req = agent_request_new(agent, device, AGENT_REQUEST_DISPLAY_PINCODE, cb, user_data, destroy); err = display_pincode_request_new(req, pincode); if (err < 0) goto failed; agent->request = req; return 0; failed: agent_request_free(req, FALSE); return err; } uint8_t agent_get_io_capability(struct agent *agent) { return agent->capability; } static void agent_destroy(gpointer data) { struct agent *agent = data; DBG("agent %s", agent->owner); if (agent->watch > 0) { g_dbus_remove_watch(btd_get_dbus_connection(), agent->watch); agent->watch = 0; agent_release(agent); } remove_default_agent(agent); agent_unref(agent); } static uint8_t parse_io_capability(const char *capability) { if (g_str_equal(capability, "")) return IO_CAPABILITY_KEYBOARDDISPLAY; if (g_str_equal(capability, "DisplayOnly")) return IO_CAPABILITY_DISPLAYONLY; if (g_str_equal(capability, "DisplayYesNo")) return IO_CAPABILITY_DISPLAYYESNO; if (g_str_equal(capability, "KeyboardOnly")) return IO_CAPABILITY_KEYBOARDONLY; if (g_str_equal(capability, "NoInputNoOutput")) return IO_CAPABILITY_NOINPUTNOOUTPUT; if (g_str_equal(capability, "KeyboardDisplay")) return IO_CAPABILITY_KEYBOARDDISPLAY; return IO_CAPABILITY_INVALID; } static DBusMessage *register_agent(DBusConnection *conn, DBusMessage *msg, void *user_data) { struct agent *agent; const char *sender, *path, *capability; uint8_t cap; sender = dbus_message_get_sender(msg); agent = g_hash_table_lookup(agent_list, sender); if (agent) return btd_error_already_exists(msg); if (dbus_message_get_args(msg, NULL, DBUS_TYPE_OBJECT_PATH, &path, DBUS_TYPE_STRING, &capability, DBUS_TYPE_INVALID) == FALSE) return btd_error_invalid_args(msg); cap = parse_io_capability(capability); if (cap == IO_CAPABILITY_INVALID) return btd_error_invalid_args(msg); agent = agent_create(sender, path, cap); if (!agent) return btd_error_invalid_args(msg); DBG("agent %s", agent->owner); g_hash_table_replace(agent_list, agent->owner, agent); return dbus_message_new_method_return(msg); } static DBusMessage *unregister_agent(DBusConnection *conn, DBusMessage *msg, void *user_data) { struct agent *agent; const char *sender, *path; sender = dbus_message_get_sender(msg); agent = g_hash_table_lookup(agent_list, sender); if (!agent) return btd_error_does_not_exist(msg); DBG("agent %s", agent->owner); if (dbus_message_get_args(msg, NULL, DBUS_TYPE_OBJECT_PATH, &path, DBUS_TYPE_INVALID) == FALSE) return btd_error_invalid_args(msg); if (g_str_equal(path, agent->path) == FALSE) return btd_error_does_not_exist(msg); agent_disconnect(conn, agent); return dbus_message_new_method_return(msg); } static DBusMessage *request_default(DBusConnection *conn, DBusMessage *msg, void *user_data) { struct agent *agent; const char *sender, *path; sender = dbus_message_get_sender(msg); agent = g_hash_table_lookup(agent_list, sender); if (!agent) return btd_error_does_not_exist(msg); if (dbus_message_get_args(msg, NULL, DBUS_TYPE_OBJECT_PATH, &path, DBUS_TYPE_INVALID) == FALSE) return btd_error_invalid_args(msg); if (g_str_equal(path, agent->path) == FALSE) return btd_error_does_not_exist(msg); if (!add_default_agent(agent)) return btd_error_failed(msg, "Failed to set as default"); return dbus_message_new_method_return(msg); } static const GDBusMethodTable methods[] = { { GDBUS_METHOD("RegisterAgent", GDBUS_ARGS({ "agent", "o"}, { "capability", "s" }), NULL, register_agent) }, { GDBUS_METHOD("UnregisterAgent", GDBUS_ARGS({ "agent", "o" }), NULL, unregister_agent) }, { GDBUS_METHOD("RequestDefaultAgent", GDBUS_ARGS({ "agent", "o" }), NULL, request_default ) }, { } }; void btd_agent_init(void) { agent_list = g_hash_table_new_full(g_str_hash, g_str_equal, NULL, agent_destroy); default_agents = queue_new(); g_dbus_register_interface(btd_get_dbus_connection(), "/org/bluez", "org.bluez.AgentManager1", methods, NULL, NULL, NULL, NULL); } void btd_agent_cleanup(void) { g_dbus_unregister_interface(btd_get_dbus_connection(), "/org/bluez", "org.bluez.AgentManager1"); g_hash_table_destroy(agent_list); queue_destroy(default_agents, NULL); } bluez-5.82/src/PaxHeaders/uuid-helper.h0000644000000000000000000000005014015011623015022 xustar0020 atime=1743515801 20 ctime=1743591278 bluez-5.82/src/uuid-helper.h0000644000000000000000000000063014015011623014502 0ustar00rootroot/* SPDX-License-Identifier: GPL-2.0-or-later */ /* * * BlueZ - Bluetooth protocol stack for Linux * * Copyright (C) 2004-2010 Marcel Holtmann * * */ char *bt_modalias(uint16_t source, uint16_t vendor, uint16_t product, uint16_t version); char *bt_uuid2string(uuid_t *uuid); char *bt_name2string(const char *string); int bt_string2uuid(uuid_t *uuid, const char *string); bluez-5.82/src/PaxHeaders/storage.c0000644000000000000000000000005014711225434014250 xustar0020 atime=1743516628 20 ctime=1743591284 bluez-5.82/src/storage.c0000644000000000000000000000610614711225434013734 0ustar00rootroot// SPDX-License-Identifier: GPL-2.0-or-later /* * * BlueZ - Bluetooth protocol stack for Linux * * Copyright (C) 2006-2010 Nokia Corporation * Copyright (C) 2004-2010 Marcel Holtmann * * */ #ifdef HAVE_CONFIG_H #include #endif #define _GNU_SOURCE #include #include #include #include #include #include #include #include #include #include #include #include "lib/bluetooth.h" #include "lib/sdp.h" #include "lib/sdp_lib.h" #include "lib/uuid.h" #include "textfile.h" #include "uuid-helper.h" #include "storage.h" /* When all services should trust a remote device */ #define GLOBAL_TRUST "[all]" struct match { GSList *keys; char *pattern; }; int read_discoverable_timeout(const char *src, int *timeout) { char filename[PATH_MAX], *str; create_name(filename, PATH_MAX, src, "config"); str = textfile_get(filename, "discovto"); if (!str) return -ENOENT; if (sscanf(str, "%d", timeout) != 1) { free(str); return -ENOENT; } free(str); return 0; } int read_pairable_timeout(const char *src, int *timeout) { char filename[PATH_MAX], *str; create_name(filename, PATH_MAX, src, "config"); str = textfile_get(filename, "pairto"); if (!str) return -ENOENT; if (sscanf(str, "%d", timeout) != 1) { free(str); return -ENOENT; } free(str); return 0; } int read_on_mode(const char *src, char *mode, int length) { char filename[PATH_MAX], *str; create_name(filename, PATH_MAX, src, "config"); str = textfile_get(filename, "onmode"); if (!str) return -ENOENT; strncpy(mode, str, length); mode[length - 1] = '\0'; free(str); return 0; } int read_local_name(const bdaddr_t *bdaddr, char *name) { char filename[PATH_MAX], *str; int len; char addr[18]; ba2str(bdaddr, addr); create_filename(filename, PATH_MAX, "/%s/config", addr); str = textfile_get(filename, "name"); if (!str) return -ENOENT; len = strlen(str); if (len > HCI_MAX_NAME_LENGTH) str[HCI_MAX_NAME_LENGTH] = '\0'; strcpy(name, str); free(str); return 0; } sdp_record_t *record_from_string(const char *str) { sdp_record_t *rec; int size, i, len; uint8_t *pdata; char tmp[3]; size = strlen(str)/2; pdata = g_malloc0(size); tmp[2] = 0; for (i = 0; i < size; i++) { memcpy(tmp, str + (i * 2), 2); pdata[i] = (uint8_t) strtol(tmp, NULL, 16); } rec = sdp_extract_pdu(pdata, size, &len); g_free(pdata); return rec; } sdp_record_t *find_record_in_list(sdp_list_t *recs, const char *uuid) { sdp_list_t *seq; for (seq = recs; seq; seq = seq->next) { sdp_record_t *rec = (sdp_record_t *) seq->data; sdp_list_t *svcclass = NULL; char *uuid_str; if (sdp_get_service_classes(rec, &svcclass) < 0) continue; /* Extract the uuid */ uuid_str = bt_uuid2string(svcclass->data); if (!uuid_str) { sdp_list_free(svcclass, free); continue; } if (!strcasecmp(uuid_str, uuid)) { sdp_list_free(svcclass, free); free(uuid_str); return rec; } sdp_list_free(svcclass, free); free(uuid_str); } return NULL; } bluez-5.82/src/PaxHeaders/profile.h0000644000000000000000000000005014621503015014243 xustar0020 atime=1743516500 20 ctime=1743591285 bluez-5.82/src/profile.h0000644000000000000000000000416014621503015013725 0ustar00rootroot/* SPDX-License-Identifier: GPL-2.0-or-later */ /* * * BlueZ - Bluetooth protocol stack for Linux * * Copyright (C) 2012 Intel Corporation. All rights reserved. * * */ #define BTD_PROFILE_PRIORITY_LOW 0 #define BTD_PROFILE_PRIORITY_MEDIUM 1 #define BTD_PROFILE_PRIORITY_HIGH 2 struct btd_service; struct btd_profile { const char *name; int priority; const char *local_uuid; const char *remote_uuid; bool auto_connect; /* Some profiles are considered safe to be handled internally and also * be exposed in the GATT API. This flag give such profiles exception * from being claimed internally. */ bool external; /* Indicates the profile is experimental and shall only be registered * when experimental has been enabled (see: main.conf:Experimental). */ bool experimental; /* Indicates the profile for testing only and shall only be registered * when testing has been enabled (see: main.conf:Testing). */ bool testing; int (*device_probe) (struct btd_service *service); void (*device_remove) (struct btd_service *service); int (*connect) (struct btd_service *service); int (*disconnect) (struct btd_service *service); int (*accept) (struct btd_service *service); int (*adapter_probe) (struct btd_profile *p, struct btd_adapter *adapter); void (*adapter_remove) (struct btd_profile *p, struct btd_adapter *adapter); }; void btd_profile_foreach(void (*func)(struct btd_profile *p, void *data), void *data); int btd_profile_register(struct btd_profile *profile); void btd_profile_unregister(struct btd_profile *profile); typedef bool (*btd_profile_prop_exists)(const char *uuid, struct btd_device *dev, void *user_data); typedef bool (*btd_profile_prop_get)(const char *uuid, struct btd_device *dev, DBusMessageIter *iter, void *user_data); bool btd_profile_add_custom_prop(const char *uuid, const char *type, const char *name, btd_profile_prop_exists exists, btd_profile_prop_get get, void *user_data); bool btd_profile_remove_custom_prop(const char *uuid, const char *name); void btd_profile_init(void); void btd_profile_cleanup(void); bluez-5.82/src/PaxHeaders/plugin.c0000644000000000000000000000005014766002272014105 xustar0020 atime=1743515578 20 ctime=1743591284 bluez-5.82/src/plugin.c0000644000000000000000000001211114766002272013562 0ustar00rootroot// SPDX-License-Identifier: GPL-2.0-or-later /* * * BlueZ - Bluetooth protocol stack for Linux * * Copyright (C) 2004-2010 Marcel Holtmann * * */ #ifdef HAVE_CONFIG_H #include #endif #include #include #include #include #include #include "lib/bluetooth.h" #include "btio/btio.h" #include "src/plugin.h" #include "src/log.h" #include "src/btd.h" #define IS_ENABLED(x) (x) static GSList *plugins = NULL; struct bluetooth_plugin { void *handle; const struct bluetooth_plugin_desc *desc; }; static int compare_priority(gconstpointer a, gconstpointer b) { const struct bluetooth_plugin_desc *plugin1 = a; const struct bluetooth_plugin_desc *plugin2 = b; return plugin2->priority - plugin1->priority; } static int init_plugin(const struct bluetooth_plugin_desc *desc) { int err; err = desc->init(); if (err < 0) { if (err == -ENOSYS || err == -ENOTSUP) DBG("System does not support %s plugin", desc->name); else error("Failed to init %s plugin", desc->name); } return err; } static gboolean add_external_plugin(void *handle, const struct bluetooth_plugin_desc *desc) { struct bluetooth_plugin *plugin; if (desc->init == NULL) return FALSE; if (g_str_equal(desc->version, VERSION) == FALSE) { error("Version mismatch for %s", desc->name); return FALSE; } plugin = g_try_new0(struct bluetooth_plugin, 1); if (plugin == NULL) return FALSE; plugin->handle = handle; plugin->desc = desc; if (init_plugin(desc) < 0) { g_free(plugin); return FALSE; } __btd_enable_debug(desc->debug_start, desc->debug_stop); plugins = g_slist_append(plugins, plugin); DBG("Plugin %s loaded", desc->name); return TRUE; } static void add_plugin(void *data, void *user_data) { struct bluetooth_plugin_desc *desc = data; struct bluetooth_plugin *plugin; DBG("Loading %s plugin", desc->name); plugin = g_try_new0(struct bluetooth_plugin, 1); if (plugin == NULL) return; plugin->desc = desc; if (init_plugin(desc) < 0) { g_free(plugin); return; } plugins = g_slist_append(plugins, plugin); DBG("Plugin %s loaded", desc->name); } static gboolean enable_plugin(const char *name, char **cli_enable, char **cli_disable) { if (cli_disable) { for (; *cli_disable; cli_disable++) if (g_pattern_match_simple(*cli_disable, name)) break; if (*cli_disable) { info("Excluding (cli) %s", name); return FALSE; } } if (cli_enable) { for (; *cli_enable; cli_enable++) if (g_pattern_match_simple(*cli_enable, name)) break; if (!*cli_enable) { info("Ignoring (cli) %s", name); return FALSE; } } return TRUE; } static void external_plugin_init(char **cli_disabled, char **cli_enabled) { GDir *dir; const char *file; info("Using external plugins is not officially supported.\n"); info("Consider upstreaming your plugins into the BlueZ project."); if (strlen(PLUGINDIR) == 0) return; DBG("Loading plugins %s", PLUGINDIR); dir = g_dir_open(PLUGINDIR, 0, NULL); if (!dir) return; while ((file = g_dir_read_name(dir)) != NULL) { const struct bluetooth_plugin_desc *desc; void *handle; char *filename; if (g_str_has_prefix(file, "lib") == TRUE || g_str_has_suffix(file, ".so") == FALSE) continue; filename = g_build_filename(PLUGINDIR, file, NULL); handle = dlopen(filename, RTLD_NOW); if (handle == NULL) { error("Can't load plugin %s: %s", filename, dlerror()); g_free(filename); continue; } g_free(filename); desc = dlsym(handle, "bluetooth_plugin_desc"); if (desc == NULL) { error("Can't load plugin description: %s", dlerror()); dlclose(handle); continue; } if (!enable_plugin(desc->name, cli_enabled, cli_disabled)) { dlclose(handle); continue; } if (add_external_plugin(handle, desc) == FALSE) dlclose(handle); } g_dir_close(dir); } #include "src/builtin.h" void plugin_init(const char *enable, const char *disable) { GSList *builtins = NULL; char **cli_disabled = NULL; char **cli_enabled = NULL; unsigned int i; /* Make a call to BtIO API so its symbols got resolved before the * plugins are loaded. */ bt_io_error_quark(); if (enable) cli_enabled = g_strsplit_set(enable, ", ", -1); if (disable) cli_disabled = g_strsplit_set(disable, ", ", -1); DBG("Loading builtin plugins"); for (i = 0; __bluetooth_builtin[i]; i++) { if (!enable_plugin(__bluetooth_builtin[i]->name, cli_enabled, cli_disabled)) continue; builtins = g_slist_insert_sorted(builtins, (void *) __bluetooth_builtin[i], compare_priority); } g_slist_foreach(builtins, add_plugin, NULL); if IS_ENABLED(EXTERNAL_PLUGINS) external_plugin_init(cli_enabled, cli_disabled); g_slist_free(builtins); g_strfreev(cli_enabled); g_strfreev(cli_disabled); } void plugin_cleanup(void) { GSList *list; DBG("Cleanup plugins"); for (list = plugins; list; list = list->next) { struct bluetooth_plugin *plugin = list->data; if (plugin->desc->exit) plugin->desc->exit(); if (plugin->handle != NULL) dlclose(plugin->handle); g_free(plugin); } g_slist_free(plugins); } bluez-5.82/src/PaxHeaders/profile.c0000644000000000000000000000005014766002272014247 xustar0020 atime=1743515578 20 ctime=1743591285 bluez-5.82/src/profile.c0000644000000000000000000017115414766002272013741 0ustar00rootroot// SPDX-License-Identifier: GPL-2.0-or-later /* * * BlueZ - Bluetooth protocol stack for Linux * * Copyright (C) 2012 Intel Corporation. All rights reserved. * * */ #ifdef HAVE_CONFIG_H #include #endif #define _GNU_SOURCE #include #include #include #include #include #include #include "lib/bluetooth.h" #include "lib/sdp.h" #include "lib/sdp_lib.h" #include "lib/uuid.h" #include "gdbus/gdbus.h" #include "btio/btio.h" #include "sdpd.h" #include "log.h" #include "error.h" #include "uuid-helper.h" #include "dbus-common.h" #include "sdp-client.h" #include "sdp-xml.h" #include "adapter.h" #include "device.h" #include "profile.h" #include "service.h" #define DUN_DEFAULT_CHANNEL 1 #define SPP_DEFAULT_CHANNEL 3 #define HSP_HS_DEFAULT_CHANNEL 6 #define HFP_HF_DEFAULT_CHANNEL 7 #define OPP_DEFAULT_CHANNEL 9 #define FTP_DEFAULT_CHANNEL 10 #define BIP_DEFAULT_CHANNEL 11 #define HSP_AG_DEFAULT_CHANNEL 12 #define HFP_AG_DEFAULT_CHANNEL 13 #define SYNC_DEFAULT_CHANNEL 14 #define PBAP_DEFAULT_CHANNEL 15 #define MAS_DEFAULT_CHANNEL 16 #define MNS_DEFAULT_CHANNEL 17 #define BTD_PROFILE_PSM_AUTO -1 #define BTD_PROFILE_CHAN_AUTO -1 #define HFP_HF_RECORD \ " \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ " #define HFP_AG_RECORD \ " \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ " /* SDP record for Headset role of HSP 1.2 profile with Erratum 3507 */ #define HSP_HS_RECORD \ " \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ " #define HSP_AG_RECORD \ " \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ " #define SPP_RECORD \ " \ \ \ \ \ %s \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ " #define DUN_RECORD \ " \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ " #define OPP_RECORD \ " \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ " #define FTP_RECORD \ " \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ " #define PCE_RECORD \ " \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ " #define PSE_RECORD \ " \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ " #define MAS_RECORD \ " \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ " #define MNS_RECORD \ " \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ " #define SYNC_RECORD \ " \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ " #define GENERIC_RECORD \ " \ \ \ \ \ \ \ \ \ \ \ %s \ \ %s \ \ \ \ \ \ \ \ %s \ \ \ \ " struct ext_io; struct ext_profile { struct btd_profile p; char *name; char *owner; char *path; char *uuid; char *service; char *role; char *record; char *(*get_record)(struct ext_profile *ext, struct ext_io *l2cap, struct ext_io *rfcomm); char *remote_uuid; guint id; BtIOMode mode; BtIOSecLevel sec_level; bool authorize; bool enable_client; bool enable_server; int local_psm; int local_chan; uint16_t remote_psm; uint8_t remote_chan; uint16_t version; uint16_t features; GSList *records; GSList *servers; GSList *conns; GSList *connects; }; struct ext_io { struct ext_profile *ext; int proto; GIOChannel *io; guint io_id; struct btd_adapter *adapter; struct btd_device *device; struct btd_service *service; bool resolving; bool connected; uint16_t version; uint16_t features; uint16_t psm; uint8_t chan; guint auth_id; DBusPendingCall *pending; }; struct ext_record { struct btd_adapter *adapter; uint32_t handle; }; struct btd_profile_custom_property { char *uuid; char *type; char *name; btd_profile_prop_exists exists; btd_profile_prop_get get; void *user_data; }; static GSList *custom_props = NULL; static GSList *profiles = NULL; static GSList *ext_profiles = NULL; void btd_profile_foreach(void (*func)(struct btd_profile *p, void *data), void *data) { GSList *l, *next; for (l = profiles; l != NULL; l = next) { struct btd_profile *profile = l->data; next = g_slist_next(l); func(profile, data); } for (l = ext_profiles; l != NULL; l = next) { struct ext_profile *profile = l->data; next = g_slist_next(l); func(&profile->p, data); } } static struct btd_profile *btd_profile_find_uuid(const char *uuid) { GSList *l, *next; for (l = profiles; l != NULL; l = next) { struct btd_profile *p = l->data; if (!g_strcmp0(p->local_uuid, uuid)) return p; next = g_slist_next(l); } for (l = ext_profiles; l != NULL; l = next) { struct ext_profile *ext = l->data; struct btd_profile *p = &ext->p; if (!g_strcmp0(p->local_uuid, uuid)) return p; next = g_slist_next(l); } return NULL; } int btd_profile_register(struct btd_profile *profile) { if (profile->experimental && !(g_dbus_get_flags() & G_DBUS_FLAG_ENABLE_EXPERIMENTAL)) { DBG("D-Bus experimental not enabled"); return -ENOTSUP; } if (profile->testing && !(g_dbus_get_flags() & G_DBUS_FLAG_ENABLE_TESTING)) { DBG("D-Bus testing not enabled"); return -ENOTSUP; } profiles = g_slist_append(profiles, profile); return 0; } void btd_profile_unregister(struct btd_profile *profile) { profiles = g_slist_remove(profiles, profile); } static struct ext_profile *find_ext_profile(const char *owner, const char *path) { GSList *l; for (l = ext_profiles; l != NULL; l = g_slist_next(l)) { struct ext_profile *ext = l->data; if (g_strcmp0(ext->owner, owner)) continue; if (!g_strcmp0(ext->path, path)) return ext; } return NULL; } static void ext_io_destroy(gpointer p) { struct ext_io *ext_io = p; if (ext_io->io_id > 0) g_source_remove(ext_io->io_id); if (ext_io->io) { g_io_channel_shutdown(ext_io->io, FALSE, NULL); g_io_channel_unref(ext_io->io); } if (ext_io->auth_id != 0) btd_cancel_authorization(ext_io->auth_id); if (ext_io->pending) { dbus_pending_call_cancel(ext_io->pending); dbus_pending_call_unref(ext_io->pending); } if (ext_io->resolving) bt_cancel_discovery(btd_adapter_get_address(ext_io->adapter), device_get_address(ext_io->device)); if (ext_io->adapter) btd_adapter_unref(ext_io->adapter); if (ext_io->device) btd_device_unref(ext_io->device); if (ext_io->service) btd_service_unref(ext_io->service); g_free(ext_io); } static gboolean ext_io_disconnected(GIOChannel *io, GIOCondition cond, gpointer user_data) { struct ext_io *conn = user_data; struct ext_profile *ext = conn->ext; GError *gerr = NULL; char addr[18]; if (cond & G_IO_NVAL) return FALSE; bt_io_get(io, &gerr, BT_IO_OPT_DEST, addr, BT_IO_OPT_INVALID); if (gerr != NULL) { error("Unable to get io data for %s: %s", ext->name, gerr->message); g_error_free(gerr); goto drop; } DBG("%s disconnected from %s", ext->name, addr); drop: if (conn->service) { if (btd_service_get_state(conn->service) == BTD_SERVICE_STATE_CONNECTING) btd_service_connecting_complete(conn->service, -EIO); else btd_service_disconnecting_complete(conn->service, 0); } ext->conns = g_slist_remove(ext->conns, conn); ext_io_destroy(conn); return FALSE; } static void new_conn_reply(DBusPendingCall *call, void *user_data) { struct ext_io *conn = user_data; struct ext_profile *ext = conn->ext; DBusMessage *reply = dbus_pending_call_steal_reply(call); DBusError err; dbus_error_init(&err); dbus_set_error_from_message(&err, reply); dbus_message_unref(reply); dbus_pending_call_unref(conn->pending); conn->pending = NULL; if (!dbus_error_is_set(&err)) { if (conn->service) btd_service_connecting_complete(conn->service, 0); conn->connected = true; return; } error("%s replied with an error: %s, %s", ext->name, err.name, err.message); if (conn->service) btd_service_connecting_complete(conn->service, -ECONNREFUSED); dbus_error_free(&err); ext->conns = g_slist_remove(ext->conns, conn); ext_io_destroy(conn); } static void disconn_reply(DBusPendingCall *call, void *user_data) { struct ext_io *conn = user_data; struct ext_profile *ext = conn->ext; DBusMessage *reply = dbus_pending_call_steal_reply(call); DBusError err; dbus_error_init(&err); dbus_set_error_from_message(&err, reply); dbus_message_unref(reply); dbus_pending_call_unref(conn->pending); conn->pending = NULL; if (!dbus_error_is_set(&err)) { if (conn->service) btd_service_disconnecting_complete(conn->service, 0); goto disconnect; } error("%s replied with an error: %s, %s", ext->name, err.name, err.message); if (conn->service) btd_service_disconnecting_complete(conn->service, -ECONNREFUSED); dbus_error_free(&err); disconnect: ext->conns = g_slist_remove(ext->conns, conn); ext_io_destroy(conn); } struct prop_append_data { DBusMessageIter *dict; struct ext_io *io; }; static void append_prop(gpointer a, gpointer b) { struct btd_profile_custom_property *p = a; struct prop_append_data *data = b; DBusMessageIter entry, value, *dict = data->dict; struct btd_device *dev = data->io->device; struct ext_profile *ext = data->io->ext; const char *uuid = ext->service ? ext->service : ext->uuid; if (strcasecmp(p->uuid, uuid) != 0) return; if (p->exists && !p->exists(p->uuid, dev, p->user_data)) return; dbus_message_iter_open_container(dict, DBUS_TYPE_DICT_ENTRY, NULL, &entry); dbus_message_iter_append_basic(&entry, DBUS_TYPE_STRING, &p->name); dbus_message_iter_open_container(&entry, DBUS_TYPE_VARIANT, p->type, &value); p->get(p->uuid, dev, &value, p->user_data); dbus_message_iter_close_container(&entry, &value); dbus_message_iter_close_container(dict, &entry); } static int get_supported_features(const sdp_record_t *rec, const char *uuid) { sdp_data_t *data; if (strcasecmp(uuid, HSP_AG_UUID) == 0) { /* HSP AG role does not provide any features */ return -ENOENT; } else if (strcasecmp(uuid, HSP_HS_UUID) == 0) { /* HSP HS role provides Remote Audio Volume Control */ data = sdp_data_get(rec, SDP_ATTR_REMOTE_AUDIO_VOLUME_CONTROL); if (!data || data->dtd != SDP_BOOL) return -ENOENT; else return data->val.int8 ? 0x1 : 0x0; } data = sdp_data_get(rec, SDP_ATTR_SUPPORTED_FEATURES); if (!data || data->dtd != SDP_UINT16) { return -ENOENT; } return data->val.uint16; } static uint16_t get_profile_version(const sdp_record_t *rec) { sdp_list_t *descs; uint16_t version; if (sdp_get_profile_descs(rec, &descs) < 0) return 0; if (descs && descs->data) { sdp_profile_desc_t *desc = descs->data; version = desc->version; } else { version = 0; } sdp_list_free(descs, free); return version; } static bool send_new_connection(struct ext_profile *ext, struct ext_io *conn) { DBusMessage *msg; DBusMessageIter iter, dict; struct prop_append_data data = { &dict, conn }; const char *remote_uuid = ext->remote_uuid; const sdp_record_t *rec; const char *path; int fd, features; bool has_features = false; msg = dbus_message_new_method_call(ext->owner, ext->path, "org.bluez.Profile1", "NewConnection"); if (!msg) { error("Unable to create NewConnection call for %s", ext->name); return false; } if (remote_uuid) { rec = btd_device_get_record(conn->device, remote_uuid); if (rec) { features = get_supported_features(rec, remote_uuid); if (features >= 0) { conn->features = features; has_features = true; } conn->version = get_profile_version(rec); } } dbus_message_iter_init_append(msg, &iter); path = device_get_path(conn->device); dbus_message_iter_append_basic(&iter, DBUS_TYPE_OBJECT_PATH, &path); fd = g_io_channel_unix_get_fd(conn->io); dbus_message_iter_append_basic(&iter, DBUS_TYPE_UNIX_FD, &fd); dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY, "{sv}", &dict); if (conn->version) dict_append_entry(&dict, "Version", DBUS_TYPE_UINT16, &conn->version); if (has_features) dict_append_entry(&dict, "Features", DBUS_TYPE_UINT16, &conn->features); g_slist_foreach(custom_props, append_prop, &data); dbus_message_iter_close_container(&iter, &dict); if (!g_dbus_send_message_with_reply(btd_get_dbus_connection(), msg, &conn->pending, -1)) { error("%s: sending NewConnection failed", ext->name); dbus_message_unref(msg); return false; } dbus_message_unref(msg); dbus_pending_call_set_notify(conn->pending, new_conn_reply, conn, NULL); return true; } static void ext_connect(GIOChannel *io, GError *err, gpointer user_data) { struct ext_io *conn = user_data; struct ext_profile *ext = conn->ext; GError *io_err = NULL; char addr[18]; if (!bt_io_get(io, &io_err, BT_IO_OPT_DEST, addr, BT_IO_OPT_INVALID)) { if (err) { error("%s failed %s", ext->name, err->message); g_error_free(io_err); io_err = NULL; } else { error("Unable to get connect data for %s: %s", ext->name, io_err->message); err = io_err; } goto drop; } if (err != NULL) { error("%s failed to connect to %s: %s", ext->name, addr, err->message); goto drop; } DBG("%s connected to %s", ext->name, addr); if (conn->io_id == 0) { GIOCondition cond = G_IO_HUP | G_IO_ERR | G_IO_NVAL; conn->io_id = g_io_add_watch(io, cond, ext_io_disconnected, conn); } if (conn->service && service_set_connecting(conn->service) < 0) goto drop; if (send_new_connection(ext, conn)) return; drop: if (conn->service) btd_service_connecting_complete(conn->service, err ? -err->code : -EIO); if (io_err) g_error_free(io_err); ext->conns = g_slist_remove(ext->conns, conn); ext_io_destroy(conn); } static void ext_auth(DBusError *err, void *user_data) { struct ext_io *conn = user_data; struct ext_profile *ext = conn->ext; GError *gerr = NULL; char addr[18]; conn->auth_id = 0; bt_io_get(conn->io, &gerr, BT_IO_OPT_DEST, addr, BT_IO_OPT_INVALID); if (gerr != NULL) { error("Unable to get connect data for %s: %s", ext->name, gerr->message); g_error_free(gerr); goto drop; } if (err && dbus_error_is_set(err)) { error("%s rejected %s: %s", ext->name, addr, err->message); goto drop; } if (!bt_io_accept(conn->io, ext_connect, conn, NULL, &gerr)) { error("bt_io_accept: %s", gerr->message); g_error_free(gerr); goto drop; } DBG("%s authorized to connect to %s", addr, ext->name); return; drop: ext->conns = g_slist_remove(ext->conns, conn); ext_io_destroy(conn); } static struct ext_io *create_conn(struct ext_io *server, GIOChannel *io, bdaddr_t *src, bdaddr_t *dst) { struct btd_device *device; struct btd_service *service; struct ext_io *conn; GIOCondition cond; char addr[18]; device = btd_adapter_find_device(server->adapter, dst, BDADDR_BREDR); if (device == NULL) { ba2str(dst, addr); error("%s device %s not found", server->ext->name, addr); return NULL; } /* Do not add UUID if client role is not enabled */ if (!server->ext->enable_client) { service = NULL; goto done; } btd_device_add_uuid(device, server->ext->remote_uuid); service = btd_device_get_service(device, server->ext->remote_uuid); if (service == NULL) { ba2str(dst, addr); error("%s service not found for device %s", server->ext->name, addr); return NULL; } done: conn = g_new0(struct ext_io, 1); conn->io = g_io_channel_ref(io); conn->proto = server->proto; conn->ext = server->ext; conn->adapter = btd_adapter_ref(server->adapter); conn->device = btd_device_ref(device); if (service) conn->service = btd_service_ref(service); cond = G_IO_HUP | G_IO_ERR | G_IO_NVAL; conn->io_id = g_io_add_watch(io, cond, ext_io_disconnected, conn); return conn; } static void ext_confirm(GIOChannel *io, gpointer user_data) { struct ext_io *server = user_data; struct ext_profile *ext = server->ext; const char *uuid = ext->service ? ext->service : ext->uuid; struct ext_io *conn; GError *gerr = NULL; bdaddr_t src, dst; char addr[18]; bt_io_get(io, &gerr, BT_IO_OPT_SOURCE_BDADDR, &src, BT_IO_OPT_DEST_BDADDR, &dst, BT_IO_OPT_DEST, addr, BT_IO_OPT_INVALID); if (gerr != NULL) { error("%s failed to get connect data: %s", ext->name, gerr->message); g_error_free(gerr); return; } DBG("incoming connect from %s", addr); if (!btd_adapter_is_uuid_allowed(adapter_find(&src), uuid)) { info("UUID %s is not allowed. Igoring the connection", uuid); return; } conn = create_conn(server, io, &src, &dst); if (conn == NULL) return; conn->auth_id = btd_request_authorization(&src, &dst, uuid, ext_auth, conn); if (conn->auth_id == 0) { error("%s authorization failure", ext->name); ext_io_destroy(conn); return; } ext->conns = g_slist_append(ext->conns, conn); DBG("%s authorizing connection from %s", ext->name, addr); } static void ext_direct_connect(GIOChannel *io, GError *err, gpointer user_data) { struct ext_io *server = user_data; struct ext_profile *ext = server->ext; GError *gerr = NULL; struct ext_io *conn; const char *uuid = ext->service ? ext->service : ext->uuid; bdaddr_t src, dst; bt_io_get(io, &gerr, BT_IO_OPT_SOURCE_BDADDR, &src, BT_IO_OPT_DEST_BDADDR, &dst, BT_IO_OPT_INVALID); if (gerr != NULL) { error("%s failed to get connect data: %s", ext->name, gerr->message); g_error_free(gerr); return; } if (!btd_adapter_is_uuid_allowed(adapter_find(&src), uuid)) { info("UUID %s is not allowed. Igoring the connection", uuid); return; } conn = create_conn(server, io, &src, &dst); if (conn == NULL) return; ext->conns = g_slist_append(ext->conns, conn); ext_connect(io, err, conn); } static uint32_t ext_register_record(struct ext_profile *ext, struct ext_io *l2cap, struct ext_io *rfcomm, struct btd_adapter *a) { sdp_record_t *rec; char *dyn_record = NULL; const char *record = ext->record; if (!record && ext->get_record) { dyn_record = ext->get_record(ext, l2cap, rfcomm); record = dyn_record; } if (!record) return 0; rec = sdp_xml_parse_record(record, strlen(record)); g_free(dyn_record); if (!rec) { error("Unable to parse record for %s", ext->name); return 0; } if (adapter_service_add(a, rec) < 0) { error("Failed to register service record"); sdp_record_free(rec); return 0; } return rec->handle; } static uint32_t ext_start_servers(struct ext_profile *ext, struct btd_adapter *adapter) { struct ext_io *l2cap = NULL; struct ext_io *rfcomm = NULL; BtIOConfirm confirm; BtIOConnect connect; GError *err = NULL; GIOChannel *io; if (ext->authorize) { confirm = ext_confirm; connect = NULL; } else { confirm = NULL; connect = ext_direct_connect; } if (ext->local_psm) { uint16_t psm; if (ext->local_psm > 0) psm = ext->local_psm; else psm = 0; l2cap = g_new0(struct ext_io, 1); l2cap->ext = ext; io = bt_io_listen(connect, confirm, l2cap, NULL, &err, BT_IO_OPT_SOURCE_BDADDR, btd_adapter_get_address(adapter), BT_IO_OPT_MODE, ext->mode, BT_IO_OPT_PSM, psm, BT_IO_OPT_SEC_LEVEL, ext->sec_level, BT_IO_OPT_INVALID); if (err != NULL) { error("L2CAP server failed for %s: %s", ext->name, err->message); g_free(l2cap); l2cap = NULL; g_clear_error(&err); goto failed; } else { if (psm == 0) bt_io_get(io, NULL, BT_IO_OPT_PSM, &psm, BT_IO_OPT_INVALID); l2cap->io = io; l2cap->proto = BTPROTO_L2CAP; l2cap->psm = psm; l2cap->adapter = btd_adapter_ref(adapter); ext->servers = g_slist_append(ext->servers, l2cap); DBG("%s listening on PSM %u", ext->name, psm); } } if (ext->local_chan) { uint8_t chan; if (ext->local_chan > 0) chan = ext->local_chan; else chan = 0; rfcomm = g_new0(struct ext_io, 1); rfcomm->ext = ext; io = bt_io_listen(connect, confirm, rfcomm, NULL, &err, BT_IO_OPT_SOURCE_BDADDR, btd_adapter_get_address(adapter), BT_IO_OPT_CHANNEL, chan, BT_IO_OPT_SEC_LEVEL, ext->sec_level, BT_IO_OPT_INVALID); if (err != NULL) { error("RFCOMM server failed for %s: %s", ext->name, err->message); g_free(rfcomm); g_clear_error(&err); goto failed; } else { if (chan == 0) bt_io_get(io, NULL, BT_IO_OPT_CHANNEL, &chan, BT_IO_OPT_INVALID); rfcomm->io = io; rfcomm->proto = BTPROTO_RFCOMM; rfcomm->chan = chan; rfcomm->adapter = btd_adapter_ref(adapter); ext->servers = g_slist_append(ext->servers, rfcomm); DBG("%s listening on chan %u", ext->name, chan); } } return ext_register_record(ext, l2cap, rfcomm, adapter); failed: if (l2cap) { ext->servers = g_slist_remove(ext->servers, l2cap); ext_io_destroy(l2cap); } return 0; } static struct ext_profile *find_ext(struct btd_profile *p) { GSList *l; l = g_slist_find(ext_profiles, p); if (!l) return NULL; return l->data; } static int ext_adapter_probe(struct btd_profile *p, struct btd_adapter *adapter) { struct ext_profile *ext; struct ext_record *rec; uint32_t handle; ext = find_ext(p); if (!ext) return -ENOENT; DBG("\"%s\" probed", ext->name); handle = ext_start_servers(ext, adapter); if (!handle) return 0; rec = g_new0(struct ext_record, 1); rec->adapter = btd_adapter_ref(adapter); rec->handle = handle; ext->records = g_slist_append(ext->records, rec); return 0; } static void ext_remove_records(struct ext_profile *ext, struct btd_adapter *adapter) { GSList *l, *next; for (l = ext->records; l != NULL; l = next) { struct ext_record *r = l->data; next = g_slist_next(l); if (adapter && r->adapter != adapter) continue; ext->records = g_slist_remove(ext->records, r); adapter_service_remove(adapter, r->handle); btd_adapter_unref(r->adapter); g_free(r); } } static void ext_adapter_remove(struct btd_profile *p, struct btd_adapter *adapter) { struct ext_profile *ext; GSList *l, *next; ext = find_ext(p); if (!ext) return; DBG("\"%s\" removed", ext->name); ext_remove_records(ext, adapter); for (l = ext->servers; l != NULL; l = next) { struct ext_io *server = l->data; next = g_slist_next(l); if (server->adapter != adapter) continue; ext->servers = g_slist_remove(ext->servers, server); ext_io_destroy(server); } } static int ext_device_probe(struct btd_service *service) { struct btd_profile *p = btd_service_get_profile(service); struct ext_profile *ext; ext = find_ext(p); if (!ext) return -ENOENT; DBG("%s probed with UUID %s", ext->name, p->remote_uuid); return 0; } static struct ext_io *find_connection(struct ext_profile *ext, struct btd_device *dev) { GSList *l; for (l = ext->conns; l != NULL; l = g_slist_next(l)) { struct ext_io *conn = l->data; if (conn->device == dev) return conn; } return NULL; } static void ext_device_remove(struct btd_service *service) { struct btd_profile *p = btd_service_get_profile(service); struct btd_device *dev = btd_service_get_device(service); struct ext_profile *ext; struct ext_io *conn; ext = find_ext(p); if (!ext) return; DBG("%s", ext->name); conn = find_connection(ext, dev); if (conn) { ext->conns = g_slist_remove(ext->conns, conn); ext_io_destroy(conn); } } static int connect_io(struct ext_io *conn, const bdaddr_t *src, const bdaddr_t *dst) { struct ext_profile *ext = conn->ext; GError *gerr = NULL; GIOChannel *io; if (conn->psm) { conn->proto = BTPROTO_L2CAP; io = bt_io_connect(ext_connect, conn, NULL, &gerr, BT_IO_OPT_SOURCE_BDADDR, src, BT_IO_OPT_DEST_BDADDR, dst, BT_IO_OPT_SEC_LEVEL, ext->sec_level, BT_IO_OPT_PSM, conn->psm, BT_IO_OPT_INVALID); } else { conn->proto = BTPROTO_RFCOMM; io = bt_io_connect(ext_connect, conn, NULL, &gerr, BT_IO_OPT_SOURCE_BDADDR, src, BT_IO_OPT_DEST_BDADDR, dst, BT_IO_OPT_SEC_LEVEL, ext->sec_level, BT_IO_OPT_CHANNEL, conn->chan, BT_IO_OPT_INVALID); } if (gerr != NULL) { error("Unable to connect %s: %s", ext->name, gerr->message); g_error_free(gerr); return -EIO; } conn->io = io; return 0; } static uint16_t get_goep_l2cap_psm(sdp_record_t *rec) { sdp_data_t *data; data = sdp_data_get(rec, SDP_ATTR_GOEP_L2CAP_PSM); if (!data) return 0; if (data->dtd != SDP_UINT16) return 0; /* PSM must be odd and lsb of upper byte must be 0 */ if ((data->val.uint16 & 0x0101) != 0x0001) return 0; return data->val.uint16; } static void record_cb(sdp_list_t *recs, int err, gpointer user_data) { struct ext_io *conn = user_data; struct ext_profile *ext = conn->ext; sdp_list_t *r; conn->resolving = false; if (err < 0) { error("Unable to get %s SDP record: %s", ext->name, strerror(-err)); goto failed; } if (!recs || !recs->data) { error("No SDP records found for %s", ext->name); err = -ENOTSUP; goto failed; } for (r = recs; r != NULL; r = r->next) { sdp_record_t *rec = r->data; sdp_list_t *protos; int port; if (sdp_get_access_protos(rec, &protos) < 0) { error("Unable to get proto list from %s record", ext->name); err = -ENOTSUP; goto failed; } port = sdp_get_proto_port(protos, L2CAP_UUID); if (port > 0) conn->psm = port; port = sdp_get_proto_port(protos, RFCOMM_UUID); if (port > 0) conn->chan = port; if (conn->psm == 0 && sdp_get_proto_desc(protos, OBEX_UUID)) conn->psm = get_goep_l2cap_psm(rec); sdp_list_foreach(protos, (sdp_list_func_t) sdp_list_free, NULL); sdp_list_free(protos, NULL); if (conn->chan || conn->psm) break; } if (!conn->chan && !conn->psm) { error("Failed to find L2CAP PSM or RFCOMM channel for %s", ext->name); err = -ENOTSUP; goto failed; } err = connect_io(conn, btd_adapter_get_address(conn->adapter), device_get_address(conn->device)); if (err < 0) { error("Connecting %s failed: %s", ext->name, strerror(-err)); goto failed; } return; failed: if (conn->service) btd_service_connecting_complete(conn->service, err); ext->conns = g_slist_remove(ext->conns, conn); ext_io_destroy(conn); } static int resolve_service(struct ext_io *conn, const bdaddr_t *src, const bdaddr_t *dst) { struct ext_profile *ext = conn->ext; uuid_t uuid; int err; bt_string2uuid(&uuid, ext->remote_uuid); sdp_uuid128_to_uuid(&uuid); err = bt_search_service(src, dst, &uuid, record_cb, conn, NULL, 0); if (err == 0) conn->resolving = true; return err; } static int ext_connect_dev(struct btd_service *service) { struct btd_device *dev = btd_service_get_device(service); struct btd_profile *profile = btd_service_get_profile(service); struct btd_adapter *adapter; struct ext_io *conn; struct ext_profile *ext; int err; ext = find_ext(profile); if (!ext) return -ENOENT; conn = find_connection(ext, dev); if (conn) return -EALREADY; adapter = device_get_adapter(dev); conn = g_new0(struct ext_io, 1); conn->ext = ext; if (ext->remote_psm || ext->remote_chan) { conn->psm = ext->remote_psm; conn->chan = ext->remote_chan; err = connect_io(conn, btd_adapter_get_address(adapter), device_get_address(dev)); } else { err = resolve_service(conn, btd_adapter_get_address(adapter), device_get_address(dev)); } if (err < 0) goto failed; conn->adapter = btd_adapter_ref(adapter); conn->device = btd_device_ref(dev); conn->service = btd_service_ref(service); ext->conns = g_slist_append(ext->conns, conn); return 0; failed: g_free(conn); return err; } static int send_disconn_req(struct ext_profile *ext, struct ext_io *conn) { DBusMessage *msg; const char *path; msg = dbus_message_new_method_call(ext->owner, ext->path, "org.bluez.Profile1", "RequestDisconnection"); if (!msg) { error("Unable to create RequestDisconnection call for %s", ext->name); return -ENOMEM; } path = device_get_path(conn->device); dbus_message_append_args(msg, DBUS_TYPE_OBJECT_PATH, &path, DBUS_TYPE_INVALID); if (!g_dbus_send_message_with_reply(btd_get_dbus_connection(), msg, &conn->pending, -1)) { error("%s: sending RequestDisconnection failed", ext->name); dbus_message_unref(msg); return -EIO; } dbus_message_unref(msg); dbus_pending_call_set_notify(conn->pending, disconn_reply, conn, NULL); return 0; } static int ext_disconnect_dev(struct btd_service *service) { struct btd_device *dev = btd_service_get_device(service); struct btd_profile *profile = btd_service_get_profile(service); struct ext_profile *ext; struct ext_io *conn; int err; ext = find_ext(profile); if (!ext) return -ENOENT; conn = find_connection(ext, dev); if (!conn || !conn->connected) return -ENOTCONN; if (conn->pending) return -EBUSY; err = send_disconn_req(ext, conn); if (err < 0) return err; return 0; } static char *get_hfp_hf_record(struct ext_profile *ext, struct ext_io *l2cap, struct ext_io *rfcomm) { return g_strdup_printf(HFP_HF_RECORD, rfcomm->chan, ext->version, ext->name, ext->features); } static char *get_hfp_ag_record(struct ext_profile *ext, struct ext_io *l2cap, struct ext_io *rfcomm) { return g_strdup_printf(HFP_AG_RECORD, rfcomm->chan, ext->version, ext->name, ext->features); } static char *get_hsp_hs_record(struct ext_profile *ext, struct ext_io *l2cap, struct ext_io *rfcomm) { /* HSP 1.2: By default Remote Audio Volume Control is off */ return g_strdup_printf(HSP_HS_RECORD, rfcomm->chan, ext->version, ext->name, (ext->features & 0x1) ? "true" : "false"); } static char *get_hsp_ag_record(struct ext_profile *ext, struct ext_io *l2cap, struct ext_io *rfcomm) { return g_strdup_printf(HSP_AG_RECORD, rfcomm->chan, ext->version, ext->name); } static char *get_spp_record(struct ext_profile *ext, struct ext_io *l2cap, struct ext_io *rfcomm) { char *svc, *rec; if (ext->service) svc = g_strdup_printf("", ext->service); else svc = g_strdup(""); rec = g_strdup_printf(SPP_RECORD, svc, rfcomm->chan, ext->version, ext->name); g_free(svc); return rec; } static char *get_dun_record(struct ext_profile *ext, struct ext_io *l2cap, struct ext_io *rfcomm) { return g_strdup_printf(DUN_RECORD, rfcomm->chan, ext->version, ext->name); } static char *get_pce_record(struct ext_profile *ext, struct ext_io *l2cap, struct ext_io *rfcomm) { return g_strdup_printf(PCE_RECORD, ext->version, ext->name); } static char *get_pse_record(struct ext_profile *ext, struct ext_io *l2cap, struct ext_io *rfcomm) { uint16_t psm = 0; uint8_t chan = 0; if (l2cap) psm = l2cap->psm; if (rfcomm) chan = rfcomm->chan; return g_strdup_printf(PSE_RECORD, chan, ext->version, ext->name, psm); } static char *get_mas_record(struct ext_profile *ext, struct ext_io *l2cap, struct ext_io *rfcomm) { uint16_t psm = 0; uint8_t chan = 0; if (l2cap) psm = l2cap->psm; if (rfcomm) chan = rfcomm->chan; return g_strdup_printf(MAS_RECORD, chan, ext->version, ext->name, psm); } static char *get_mns_record(struct ext_profile *ext, struct ext_io *l2cap, struct ext_io *rfcomm) { uint16_t psm = 0; uint8_t chan = 0; if (l2cap) psm = l2cap->psm; if (rfcomm) chan = rfcomm->chan; return g_strdup_printf(MNS_RECORD, chan, ext->version, ext->name, psm); } static char *get_sync_record(struct ext_profile *ext, struct ext_io *l2cap, struct ext_io *rfcomm) { return g_strdup_printf(SYNC_RECORD, rfcomm->chan, ext->version, ext->name); } static char *get_opp_record(struct ext_profile *ext, struct ext_io *l2cap, struct ext_io *rfcomm) { uint16_t psm = 0; uint8_t chan = 0; if (l2cap) psm = l2cap->psm; if (rfcomm) chan = rfcomm->chan; return g_strdup_printf(OPP_RECORD, chan, ext->version, psm, ext->name); } static char *get_ftp_record(struct ext_profile *ext, struct ext_io *l2cap, struct ext_io *rfcomm) { uint16_t psm = 0; uint8_t chan = 0; if (l2cap) psm = l2cap->psm; if (rfcomm) chan = rfcomm->chan; return g_strdup_printf(FTP_RECORD, chan, ext->version, psm, ext->name); } #define RFCOMM_SEQ " \ \ \ " #define VERSION_ATTR \ " \ \ \ \ \ \ \ " static char *get_generic_record(struct ext_profile *ext, struct ext_io *l2cap, struct ext_io *rfcomm) { char uuid_str[MAX_LEN_UUID_STR], svc_str[MAX_LEN_UUID_STR], psm[30]; char *rf_seq, *ver_attr, *rec; uuid_t uuid; bt_string2uuid(&uuid, ext->uuid); sdp_uuid2strn(&uuid, uuid_str, sizeof(uuid_str)); if (ext->service) { bt_string2uuid(&uuid, ext->service); sdp_uuid2strn(&uuid, svc_str, sizeof(svc_str)); } else { strncpy(svc_str, uuid_str, sizeof(svc_str)); } if (l2cap) snprintf(psm, sizeof(psm), "", l2cap->psm); else psm[0] = '\0'; if (rfcomm) rf_seq = g_strdup_printf(RFCOMM_SEQ, rfcomm->chan); else rf_seq = g_strdup(""); if (ext->version) ver_attr = g_strdup_printf(VERSION_ATTR, uuid_str, ext->version); else ver_attr = g_strdup(""); rec = g_strdup_printf(GENERIC_RECORD, svc_str, psm, rf_seq, ver_attr, ext->name); g_free(rf_seq); g_free(ver_attr); return rec; } static struct default_settings { const char *uuid; const char *name; int priority; const char *remote_uuid; int channel; int psm; BtIOMode mode; BtIOSecLevel sec_level; bool authorize; bool auto_connect; char * (*get_record)(struct ext_profile *ext, struct ext_io *l2cap, struct ext_io *rfcomm); uint16_t version; uint16_t features; } defaults[] = { { .uuid = SPP_UUID, .name = "Serial Port", .channel = SPP_DEFAULT_CHANNEL, .authorize = true, .get_record = get_spp_record, .version = 0x0102, }, { .uuid = DUN_GW_UUID, .name = "Dial-Up Networking", .channel = DUN_DEFAULT_CHANNEL, .authorize = true, .get_record = get_dun_record, .version = 0x0102, }, { .uuid = HFP_HS_UUID, .name = "Hands-Free unit", .priority = BTD_PROFILE_PRIORITY_HIGH, .remote_uuid = HFP_AG_UUID, .channel = HFP_HF_DEFAULT_CHANNEL, .authorize = true, .auto_connect = true, .get_record = get_hfp_hf_record, .version = 0x0108, }, { .uuid = HSP_HS_UUID, .name = "Headset unit", .priority = BTD_PROFILE_PRIORITY_HIGH, .remote_uuid = HSP_AG_UUID, .channel = HSP_HS_DEFAULT_CHANNEL, .authorize = true, .auto_connect = true, .get_record = get_hsp_hs_record, .version = 0x0102, }, { .uuid = HFP_AG_UUID, .name = "Hands-Free Voice gateway", .priority = BTD_PROFILE_PRIORITY_HIGH, .remote_uuid = HFP_HS_UUID, .channel = HFP_AG_DEFAULT_CHANNEL, .authorize = true, .auto_connect = true, .get_record = get_hfp_ag_record, .version = 0x0108, /* HFP 1.7.2: By default features bitfield is 0b001001 */ .features = 0x09, }, { .uuid = HSP_AG_UUID, .name = "Headset Voice gateway", .priority = BTD_PROFILE_PRIORITY_HIGH, .remote_uuid = HSP_HS_UUID, .channel = HSP_AG_DEFAULT_CHANNEL, .authorize = true, .auto_connect = true, .get_record = get_hsp_ag_record, .version = 0x0102, }, { .uuid = OBEX_OPP_UUID, .name = "Object Push", .channel = OPP_DEFAULT_CHANNEL, .psm = BTD_PROFILE_PSM_AUTO, .mode = BT_IO_MODE_ERTM, .sec_level = BT_IO_SEC_LOW, .authorize = false, .get_record = get_opp_record, .version = 0x0102, }, { .uuid = OBEX_FTP_UUID, .name = "File Transfer", .channel = FTP_DEFAULT_CHANNEL, .psm = BTD_PROFILE_PSM_AUTO, .mode = BT_IO_MODE_ERTM, .authorize = true, .get_record = get_ftp_record, .version = 0x0103, }, { .uuid = OBEX_SYNC_UUID, .name = "Synchronization", .channel = SYNC_DEFAULT_CHANNEL, .authorize = true, .get_record = get_sync_record, .version = 0x0100, }, { .uuid = OBEX_PSE_UUID, .name = "Phone Book Access", .channel = PBAP_DEFAULT_CHANNEL, .psm = BTD_PROFILE_PSM_AUTO, .mode = BT_IO_MODE_ERTM, .authorize = true, .get_record = get_pse_record, .version = 0x0101, }, { .uuid = OBEX_PCE_UUID, .name = "Phone Book Access Client", .remote_uuid = OBEX_PSE_UUID, .authorize = true, .get_record = get_pce_record, .version = 0x0102, }, { .uuid = OBEX_MAS_UUID, .name = "Message Access", .channel = MAS_DEFAULT_CHANNEL, .psm = BTD_PROFILE_PSM_AUTO, .mode = BT_IO_MODE_ERTM, .authorize = true, .get_record = get_mas_record, .version = 0x0100 }, { .uuid = OBEX_MNS_UUID, .name = "Message Notification", .channel = MNS_DEFAULT_CHANNEL, .psm = BTD_PROFILE_PSM_AUTO, .mode = BT_IO_MODE_ERTM, .authorize = true, .get_record = get_mns_record, .version = 0x0104 }, }; static void ext_set_defaults(struct ext_profile *ext) { unsigned int i; ext->mode = BT_IO_MODE_BASIC; ext->sec_level = BT_IO_SEC_MEDIUM; ext->authorize = true; ext->enable_client = true; ext->enable_server = true; ext->remote_uuid = NULL; for (i = 0; i < G_N_ELEMENTS(defaults); i++) { struct default_settings *settings = &defaults[i]; const char *remote_uuid; if (strcasecmp(ext->uuid, settings->uuid) != 0) continue; if (settings->remote_uuid) remote_uuid = settings->remote_uuid; else remote_uuid = ext->uuid; ext->remote_uuid = g_strdup(remote_uuid); if (settings->channel) ext->local_chan = settings->channel; if (settings->psm) ext->local_psm = settings->psm; if (settings->sec_level) ext->sec_level = settings->sec_level; if (settings->mode) ext->mode = settings->mode; ext->authorize = settings->authorize; if (settings->auto_connect) ext->p.auto_connect = true; if (settings->priority) ext->p.priority = settings->priority; if (settings->get_record) ext->get_record = settings->get_record; if (settings->version) ext->version = settings->version; if (settings->features) ext->features = settings->features; if (settings->name) ext->name = g_strdup(settings->name); } } static int parse_ext_opt(struct ext_profile *ext, const char *key, DBusMessageIter *value) { int type = dbus_message_iter_get_arg_type(value); const char *str; uint16_t u16; dbus_bool_t b; if (strcasecmp(key, "Name") == 0) { if (type != DBUS_TYPE_STRING) return -EINVAL; dbus_message_iter_get_basic(value, &str); g_free(ext->name); ext->name = g_strdup(str); } else if (strcasecmp(key, "AutoConnect") == 0) { if (type != DBUS_TYPE_BOOLEAN) return -EINVAL; dbus_message_iter_get_basic(value, &b); ext->p.auto_connect = b; } else if (strcasecmp(key, "PSM") == 0) { if (type != DBUS_TYPE_UINT16) return -EINVAL; dbus_message_iter_get_basic(value, &u16); ext->local_psm = u16 ? u16 : BTD_PROFILE_PSM_AUTO; } else if (strcasecmp(key, "Channel") == 0) { if (type != DBUS_TYPE_UINT16) return -EINVAL; dbus_message_iter_get_basic(value, &u16); if (u16 > 31) return -EINVAL; ext->local_chan = u16 ? u16 : BTD_PROFILE_CHAN_AUTO; } else if (strcasecmp(key, "RequireAuthentication") == 0) { if (type != DBUS_TYPE_BOOLEAN) return -EINVAL; dbus_message_iter_get_basic(value, &b); if (b) ext->sec_level = BT_IO_SEC_MEDIUM; else ext->sec_level = BT_IO_SEC_LOW; } else if (strcasecmp(key, "RequireAuthorization") == 0) { if (type != DBUS_TYPE_BOOLEAN) return -EINVAL; dbus_message_iter_get_basic(value, &b); ext->authorize = b; } else if (strcasecmp(key, "Role") == 0) { if (type != DBUS_TYPE_STRING) return -EINVAL; dbus_message_iter_get_basic(value, &str); g_free(ext->role); ext->role = g_strdup(str); if (g_str_equal(ext->role, "client")) { ext->enable_server = false; ext->enable_client = true; } else if (g_str_equal(ext->role, "server")) { ext->enable_server = true; ext->enable_client = false; } } else if (strcasecmp(key, "ServiceRecord") == 0) { if (type != DBUS_TYPE_STRING) return -EINVAL; dbus_message_iter_get_basic(value, &str); g_free(ext->record); ext->record = g_strdup(str); ext->enable_server = true; } else if (strcasecmp(key, "Version") == 0) { uint16_t ver; if (type != DBUS_TYPE_UINT16) return -EINVAL; dbus_message_iter_get_basic(value, &ver); ext->version = ver; } else if (strcasecmp(key, "Features") == 0) { uint16_t feat; if (type != DBUS_TYPE_UINT16) return -EINVAL; dbus_message_iter_get_basic(value, &feat); ext->features = feat; } else if (strcasecmp(key, "Service") == 0) { if (type != DBUS_TYPE_STRING) return -EINVAL; dbus_message_iter_get_basic(value, &str); free(ext->service); ext->service = bt_name2string(str); if (ext->service == NULL) return -EINVAL; } return 0; } static void set_service(struct ext_profile *ext) { if (strcasecmp(ext->uuid, HSP_HS_UUID) == 0) { ext->service = strdup(ext->uuid); } else if (strcasecmp(ext->uuid, HSP_AG_UUID) == 0) { ext->service = ext->uuid; ext->uuid = strdup(HSP_HS_UUID); } else if (strcasecmp(ext->uuid, HFP_HS_UUID) == 0) { ext->service = strdup(ext->uuid); } else if (strcasecmp(ext->uuid, HFP_AG_UUID) == 0) { ext->service = ext->uuid; ext->uuid = strdup(HFP_HS_UUID); } else if (strcasecmp(ext->uuid, OBEX_SYNC_UUID) == 0 || strcasecmp(ext->uuid, OBEX_OPP_UUID) == 0 || strcasecmp(ext->uuid, OBEX_FTP_UUID) == 0) { ext->service = strdup(ext->uuid); } else if (strcasecmp(ext->uuid, OBEX_PSE_UUID) == 0 || strcasecmp(ext->uuid, OBEX_PCE_UUID) == 0) { ext->service = ext->uuid; ext->uuid = strdup(OBEX_PBAP_UUID); } else if (strcasecmp(ext->uuid, OBEX_MAS_UUID) == 0 || strcasecmp(ext->uuid, OBEX_MNS_UUID) == 0) { ext->service = ext->uuid; ext->uuid = strdup(OBEX_MAP_UUID); } } static struct ext_profile *create_ext(const char *owner, const char *path, const char *uuid, DBusMessageIter *opts) { struct btd_profile *p; struct ext_profile *ext; ext = g_new0(struct ext_profile, 1); ext->uuid = bt_name2string(uuid); if (ext->uuid == NULL) { g_free(ext); return NULL; } ext->owner = g_strdup(owner); ext->path = g_strdup(path); ext_set_defaults(ext); while (dbus_message_iter_get_arg_type(opts) == DBUS_TYPE_DICT_ENTRY) { DBusMessageIter value, entry; const char *key; dbus_message_iter_recurse(opts, &entry); dbus_message_iter_get_basic(&entry, &key); dbus_message_iter_next(&entry); dbus_message_iter_recurse(&entry, &value); if (parse_ext_opt(ext, key, &value) < 0) error("Invalid value for profile option %s", key); dbus_message_iter_next(opts); } if (!ext->service) set_service(ext); if (ext->enable_server && !(ext->record || ext->get_record)) ext->get_record = get_generic_record; if (!ext->name) ext->name = g_strdup_printf("%s%s/%s", owner, path, uuid); if (!ext->remote_uuid) { if (ext->service) ext->remote_uuid = g_strdup(ext->service); else ext->remote_uuid = g_strdup(ext->uuid); } p = &ext->p; p->name = ext->name; p->local_uuid = ext->service ? ext->service : ext->uuid; p->remote_uuid = ext->remote_uuid; p->external = true; if (ext->enable_server) { p->adapter_probe = ext_adapter_probe; p->adapter_remove = ext_adapter_remove; } if (ext->enable_client) { p->device_probe = ext_device_probe; p->device_remove = ext_device_remove; p->connect = ext_connect_dev; p->disconnect = ext_disconnect_dev; } DBG("Created \"%s\"", ext->name); ext_profiles = g_slist_append(ext_profiles, ext); adapter_foreach(adapter_add_profile, &ext->p); return ext; } static void remove_ext(struct ext_profile *ext) { adapter_foreach(adapter_remove_profile, &ext->p); ext_profiles = g_slist_remove(ext_profiles, ext); DBG("Removed \"%s\"", ext->name); ext_remove_records(ext, NULL); g_slist_free_full(ext->servers, ext_io_destroy); g_slist_free_full(ext->conns, ext_io_destroy); g_free(ext->remote_uuid); g_free(ext->name); g_free(ext->owner); free(ext->uuid); free(ext->service); g_free(ext->role); g_free(ext->path); g_free(ext->record); g_free(ext); } static void ext_exited(DBusConnection *conn, void *user_data) { struct ext_profile *ext = user_data; DBG("\"%s\" exited", ext->name); remove_ext(ext); } static DBusMessage *register_profile(DBusConnection *conn, DBusMessage *msg, void *user_data) { const char *path, *sender, *uuid; DBusMessageIter args, opts; struct ext_profile *ext; sender = dbus_message_get_sender(msg); DBG("sender %s", sender); dbus_message_iter_init(msg, &args); dbus_message_iter_get_basic(&args, &path); dbus_message_iter_next(&args); ext = find_ext_profile(sender, path); if (ext) return btd_error_already_exists(msg); dbus_message_iter_get_basic(&args, &uuid); dbus_message_iter_next(&args); if (btd_profile_find_uuid(uuid)) { warn("%s tried to register %s which is already registered", sender, uuid); return btd_error_not_permitted(msg, "UUID already registered"); } if (dbus_message_iter_get_arg_type(&args) != DBUS_TYPE_ARRAY) return btd_error_invalid_args(msg); dbus_message_iter_recurse(&args, &opts); ext = create_ext(sender, path, uuid, &opts); if (!ext) return btd_error_invalid_args(msg); ext->id = g_dbus_add_disconnect_watch(conn, sender, ext_exited, ext, NULL); return dbus_message_new_method_return(msg); } static DBusMessage *unregister_profile(DBusConnection *conn, DBusMessage *msg, void *user_data) { const char *path, *sender; struct ext_profile *ext; sender = dbus_message_get_sender(msg); DBG("sender %s", sender); if (!dbus_message_get_args(msg, NULL, DBUS_TYPE_OBJECT_PATH, &path, DBUS_TYPE_INVALID)) return btd_error_invalid_args(msg); ext = find_ext_profile(sender, path); if (!ext) return btd_error_does_not_exist(msg); g_dbus_remove_watch(conn, ext->id); remove_ext(ext); return dbus_message_new_method_return(msg); } static const GDBusMethodTable methods[] = { { GDBUS_METHOD("RegisterProfile", GDBUS_ARGS({ "profile", "o"}, { "UUID", "s" }, { "options", "a{sv}" }), NULL, register_profile) }, { GDBUS_METHOD("UnregisterProfile", GDBUS_ARGS({ "profile", "o" }), NULL, unregister_profile) }, { } }; static struct btd_profile_custom_property *find_custom_prop(const char *uuid, const char *name) { GSList *l; for (l = custom_props; l; l = l->next) { struct btd_profile_custom_property *prop = l->data; if (strcasecmp(prop->uuid, uuid) != 0) continue; if (g_strcmp0(prop->name, name) == 0) return prop; } return NULL; } bool btd_profile_add_custom_prop(const char *uuid, const char *type, const char *name, btd_profile_prop_exists exists, btd_profile_prop_get get, void *user_data) { struct btd_profile_custom_property *prop; prop = find_custom_prop(uuid, name); if (prop != NULL) return false; prop = g_new0(struct btd_profile_custom_property, 1); prop->uuid = strdup(uuid); prop->type = g_strdup(type); prop->name = g_strdup(name); prop->exists = exists; prop->get = get; prop->user_data = user_data; custom_props = g_slist_append(custom_props, prop); return true; } static void free_property(gpointer data) { struct btd_profile_custom_property *p = data; g_free(p->uuid); g_free(p->type); g_free(p->name); g_free(p); } bool btd_profile_remove_custom_prop(const char *uuid, const char *name) { struct btd_profile_custom_property *prop; prop = find_custom_prop(uuid, name); if (prop == NULL) return false; custom_props = g_slist_remove(custom_props, prop); free_property(prop); return false; } void btd_profile_init(void) { g_dbus_register_interface(btd_get_dbus_connection(), "/org/bluez", "org.bluez.ProfileManager1", methods, NULL, NULL, NULL, NULL); } void btd_profile_cleanup(void) { while (ext_profiles) { struct ext_profile *ext = ext_profiles->data; DBusConnection *conn = btd_get_dbus_connection(); DBusMessage *msg; DBG("Releasing \"%s\"", ext->name); g_slist_free_full(ext->conns, ext_io_destroy); ext->conns = NULL; msg = dbus_message_new_method_call(ext->owner, ext->path, "org.bluez.Profile1", "Release"); if (msg) g_dbus_send_message(conn, msg); g_dbus_remove_watch(conn, ext->id); remove_ext(ext); } g_slist_free_full(custom_props, free_property); custom_props = NULL; g_dbus_unregister_interface(btd_get_dbus_connection(), "/org/bluez", "org.bluez.ProfileManager1"); } bluez-5.82/src/PaxHeaders/rfkill.c0000644000000000000000000000005014766002272014072 xustar0020 atime=1743515578 20 ctime=1743591284 bluez-5.82/src/rfkill.c0000644000000000000000000000720614766002272013560 0ustar00rootroot// SPDX-License-Identifier: GPL-2.0-or-later /* * * BlueZ - Bluetooth protocol stack for Linux * * Copyright (C) 2004-2010 Marcel Holtmann * * */ #ifdef HAVE_CONFIG_H #include #endif #define _GNU_SOURCE #include #include #include #include #include #include #include #include #include #include "lib/bluetooth.h" #include "lib/sdp.h" #include "log.h" #include "adapter.h" #include "btd.h" enum rfkill_type { RFKILL_TYPE_ALL = 0, RFKILL_TYPE_WLAN, RFKILL_TYPE_BLUETOOTH, RFKILL_TYPE_UWB, RFKILL_TYPE_WIMAX, RFKILL_TYPE_WWAN, }; enum rfkill_operation { RFKILL_OP_ADD = 0, RFKILL_OP_DEL, RFKILL_OP_CHANGE, RFKILL_OP_CHANGE_ALL, }; struct rfkill_event { uint32_t idx; uint8_t type; uint8_t op; uint8_t soft; uint8_t hard; }; #define RFKILL_EVENT_SIZE_V1 8 #define RFKILL_DEVICE_PATH "/dev/rfkill" static int get_adapter_id_for_rfkill(uint32_t rfkill_id) { char sysname[PATH_MAX]; int namefd; snprintf(sysname, sizeof(sysname) - 1, "/sys/class/rfkill/rfkill%u/name", rfkill_id); namefd = open(sysname, O_RDONLY); if (namefd < 0) return -1; memset(sysname, 0, sizeof(sysname)); if (read(namefd, sysname, sizeof(sysname) - 1) < 4) { close(namefd); return -1; } close(namefd); if (g_str_has_prefix(sysname, "hci") == FALSE) return -1; return atoi(sysname + 3); } int rfkill_get_blocked(uint16_t index) { int fd; int blocked = -1; fd = open(RFKILL_DEVICE_PATH, O_RDWR); if (fd < 0) { DBG("Failed to open RFKILL control device"); return -1; } while (1) { struct rfkill_event event = { 0 }; int id; ssize_t len; len = read(fd, &event, sizeof(event)); if (len < RFKILL_EVENT_SIZE_V1) break; id = get_adapter_id_for_rfkill(event.idx); if (index == id) { blocked = event.soft || event.hard; break; } } close(fd); return blocked; } static gboolean rfkill_event(GIOChannel *chan, GIOCondition cond, gpointer data) { struct rfkill_event event = { 0 }; struct btd_adapter *adapter; bool blocked = false; ssize_t len; int fd, id; if (cond & (G_IO_NVAL | G_IO_HUP | G_IO_ERR)) return FALSE; fd = g_io_channel_unix_get_fd(chan); len = read(fd, &event, sizeof(event)); if (len < 0) { if (errno == EAGAIN) return TRUE; return FALSE; } if (len < RFKILL_EVENT_SIZE_V1) return TRUE; DBG("RFKILL event idx %u type %u op %u soft %u hard %u", event.idx, event.type, event.op, event.soft, event.hard); if (event.soft || event.hard) blocked = true; if (event.op != RFKILL_OP_CHANGE) return TRUE; if (event.type != RFKILL_TYPE_BLUETOOTH && event.type != RFKILL_TYPE_ALL) return TRUE; id = get_adapter_id_for_rfkill(event.idx); if (id < 0) return TRUE; adapter = adapter_find_by_id(id); if (!adapter) return TRUE; DBG("RFKILL unblock for hci%d", id); if (blocked) btd_adapter_set_blocked(adapter); else btd_adapter_restore_powered(adapter); return TRUE; } static guint watch = 0; void rfkill_init(void) { int fd; GIOChannel *channel; errno = 0; fd = open(RFKILL_DEVICE_PATH, O_RDWR); if (fd < 0) { if (errno == ENOENT) { DBG("No RFKILL device available at '%s'", RFKILL_DEVICE_PATH); } else { error("Failed to open RFKILL control device: %s", strerror(errno)); } return; } channel = g_io_channel_unix_new(fd); g_io_channel_set_close_on_unref(channel, TRUE); watch = g_io_add_watch(channel, G_IO_IN | G_IO_NVAL | G_IO_HUP | G_IO_ERR, rfkill_event, NULL); g_io_channel_unref(channel); } void rfkill_exit(void) { if (watch == 0) return; g_source_remove(watch); watch = 0; } bluez-5.82/src/PaxHeaders/adapter.c0000644000000000000000000000005014772767672014252 xustar0020 atime=1743515579 20 ctime=1743591285 bluez-5.82/src/adapter.c0000644000000000000000000103140614772767672013740 0ustar00rootroot// SPDX-License-Identifier: GPL-2.0-or-later /* * * BlueZ - Bluetooth protocol stack for Linux * * Copyright (C) 2006-2010 Nokia Corporation * Copyright (C) 2004-2010 Marcel Holtmann * * */ #ifdef HAVE_CONFIG_H #include #endif #define _GNU_SOURCE #include #include #include #include #include #include #include #include #include #include #include #include #include #include "bluetooth/bluetooth.h" #include "bluetooth/hci.h" #include "bluetooth/hci_lib.h" #include "bluetooth/sdp.h" #include "bluetooth/sdp_lib.h" #include "lib/uuid.h" #include "lib/mgmt.h" #include "gdbus/gdbus.h" #include "log.h" #include "textfile.h" #include "src/shared/mgmt.h" #include "src/shared/util.h" #include "src/shared/queue.h" #include "src/shared/att.h" #include "src/shared/gatt-db.h" #include "src/shared/timeout.h" #include "btio/btio.h" #include "btd.h" #include "sdpd.h" #include "adapter.h" #include "device.h" #include "profile.h" #include "dbus-common.h" #include "error.h" #include "uuid-helper.h" #include "agent.h" #include "storage.h" #include "attrib/gattrib.h" #include "attrib/att.h" #include "attrib/gatt.h" #include "gatt-database.h" #include "advertising.h" #include "adv_monitor.h" #include "eir.h" #include "battery.h" #define MODE_OFF 0x00 #define MODE_CONNECTABLE 0x01 #define MODE_DISCOVERABLE 0x02 #define MODE_UNKNOWN 0xff #define CONN_SCAN_TIMEOUT (3) #define IDLE_DISCOV_TIMEOUT (5) #define TEMP_DEV_TIMEOUT (3 * 60) #define BONDING_TIMEOUT (2 * 60) #define SCAN_TYPE_BREDR (1 << BDADDR_BREDR) #define SCAN_TYPE_LE ((1 << BDADDR_LE_PUBLIC) | (1 << BDADDR_LE_RANDOM)) #define SCAN_TYPE_DUAL (SCAN_TYPE_BREDR | SCAN_TYPE_LE) #define HCI_RSSI_INVALID 127 #define DISTANCE_VAL_INVALID 0x7FFF #define PATHLOSS_MAX 137 /* * These are known security keys that have been compromised. * If this grows or there are needs to be platform specific, it is * conceivable that these could be read from a config file. */ static const struct mgmt_blocked_key_info blocked_keys[] = { /* Google Titan Security Keys */ { HCI_BLOCKED_KEY_TYPE_LTK, {0xbf, 0x01, 0xfb, 0x9d, 0x4e, 0xf3, 0xbc, 0x36, 0xd8, 0x74, 0xf5, 0x39, 0x41, 0x38, 0x68, 0x4c}}, { HCI_BLOCKED_KEY_TYPE_IRK, {0xa5, 0x99, 0xba, 0xe4, 0xe1, 0x7c, 0xa6, 0x18, 0x22, 0x8e, 0x07, 0x56, 0xb4, 0xe8, 0x5f, 0x01}}, }; struct mgmt_exp_uuid { uint8_t val[16]; const char *str; }; /* d4992530-b9ec-469f-ab01-6c481c47da1c */ static const struct mgmt_exp_uuid debug_uuid = { .val = { 0x1c, 0xda, 0x47, 0x1c, 0x48, 0x6c, 0x01, 0xab, 0x9f, 0x46, 0xec, 0xb9, 0x30, 0x25, 0x99, 0xd4 }, .str = "d4992530-b9ec-469f-ab01-6c481c47da1c" }; /* 671b10b5-42c0-4696-9227-eb28d1b049d6 */ static const struct mgmt_exp_uuid le_simult_central_peripheral_uuid = { .val = { 0xd6, 0x49, 0xb0, 0xd1, 0x28, 0xeb, 0x27, 0x92, 0x96, 0x46, 0xc0, 0x42, 0xb5, 0x10, 0x1b, 0x67 }, .str = "671b10b5-42c0-4696-9227-eb28d1b049d6" }; /* 330859bc-7506-492d-9370-9a6f0614037f */ static const struct mgmt_exp_uuid quality_report_uuid = { .val = { 0x7f, 0x03, 0x14, 0x06, 0x6f, 0x9a, 0x70, 0x93, 0x2d, 0x49, 0x06, 0x75, 0xbc, 0x59, 0x08, 0x33 }, .str = "330859bc-7506-492d-9370-9a6f0614037f" }; /* 15c0a148-c273-11ea-b3de-0242ac130004 */ static const struct mgmt_exp_uuid rpa_resolution_uuid = { .val = { 0x04, 0x00, 0x13, 0xac, 0x42, 0x02, 0xde, 0xb3, 0xea, 0x11, 0x73, 0xc2, 0x48, 0xa1, 0xc0, 0x15 }, .str = "15c0a148-c273-11ea-b3de-0242ac130004" }; /* a6695ace-ee7f-4fb9-881a-5fac66c629af */ static const struct mgmt_exp_uuid codec_offload_uuid = { .val = { 0xaf, 0x29, 0xc6, 0x66, 0xac, 0x5f, 0x1a, 0x88, 0xb9, 0x4f, 0x7f, 0xee, 0xce, 0x5a, 0x69, 0xa6 }, .str = "a6695ace-ee7f-4fb9-881a-5fac66c629af" }; /* 6fbaf188-05e0-496a-9885-d6ddfdb4e03e */ static const struct mgmt_exp_uuid iso_socket_uuid = { .val = { 0x3e, 0xe0, 0xb4, 0xfd, 0xdd, 0xd6, 0x85, 0x98, 0x6a, 0x49, 0xe0, 0x05, 0x88, 0xf1, 0xba, 0x6f }, .str = "6fbaf188-05e0-496a-9885-d6ddfdb4e03e" }; static DBusConnection *dbus_conn = NULL; static uint32_t kernel_features = 0; static GList *adapter_list = NULL; static unsigned int adapter_remaining = 0; static bool powering_down = false; static GSList *adapters = NULL; static struct mgmt *mgmt_primary = NULL; static uint8_t mgmt_version = 0; static uint8_t mgmt_revision = 0; static GSList *adapter_drivers = NULL; static GSList *disconnect_list = NULL; static GSList *conn_fail_list = NULL; struct link_key_info { bdaddr_t bdaddr; uint8_t bdaddr_type; unsigned char key[16]; uint8_t type; uint8_t pin_len; bool is_blocked; }; struct smp_ltk_info { bdaddr_t bdaddr; uint8_t bdaddr_type; uint8_t authenticated; bool central; uint8_t enc_size; uint16_t ediv; uint64_t rand; uint8_t val[16]; bool is_blocked; }; struct irk_info { bdaddr_t bdaddr; uint8_t bdaddr_type; uint8_t val[16]; bool is_blocked; }; struct conn_param { bdaddr_t bdaddr; uint8_t bdaddr_type; uint16_t min_interval; uint16_t max_interval; uint16_t latency; uint16_t timeout; }; struct discovery_filter { uint8_t type; char *pattern; uint16_t pathloss; int16_t rssi; GSList *uuids; bool duplicate; bool discoverable; }; struct discovery_client { struct btd_adapter *adapter; DBusMessage *msg; char *owner; guint watch; struct discovery_filter *discovery_filter; }; struct service_auth { guint id; unsigned int svc_id; service_auth_cb cb; void *user_data; const char *uuid; struct btd_device *device; struct btd_adapter *adapter; struct agent *agent; /* NULL for queued auths */ }; struct btd_adapter_pin_cb_iter { GSList *it; /* current callback function */ unsigned int attempt; /* numer of times it() was called */ /* When the iterator reaches the end, it is NULL and attempt is 0 */ }; struct exp_pending { struct btd_adapter *adapter; unsigned int id; }; enum { ADAPTER_POWER_STATE_OFF, ADAPTER_POWER_STATE_ON, ADAPTER_POWER_STATE_ON_DISABLING, ADAPTER_POWER_STATE_OFF_ENABLING, ADAPTER_POWER_STATE_OFF_BLOCKED, }; struct btd_adapter { int ref_count; uint16_t dev_id; struct mgmt *mgmt; bdaddr_t bdaddr; /* controller Bluetooth address */ uint8_t bdaddr_type; /* address type */ uint8_t version; /* controller core spec version */ uint32_t dev_class; /* controller class of device */ char *name; /* controller device name */ char *short_name; /* controller short name */ uint32_t supported_settings; /* controller supported settings */ uint32_t pending_settings; /* pending controller settings */ uint32_t power_state; /* the power state */ uint32_t current_settings; /* current controller settings */ char *path; /* adapter object path */ uint16_t manufacturer; /* adapter manufacturer */ uint8_t major_class; /* configured major class */ uint8_t minor_class; /* configured minor class */ char *system_name; /* configured system name */ char *modalias; /* device id (modalias) */ bool stored_discoverable; /* stored discoverable mode */ uint32_t discoverable_timeout; /* discoverable time(sec) */ uint32_t pairable_timeout; /* pairable time(sec) */ char *current_alias; /* current adapter name alias */ char *stored_alias; /* stored adapter name alias */ bool discovering; /* discovering property state */ bool filtered_discovery; /* we are doing filtered discovery */ bool no_scan_restart_delay; /* when this flag is set, restart scan * without delay */ uint8_t discovery_type; /* current active discovery type */ uint8_t discovery_enable; /* discovery enabled/disabled */ bool discovery_suspended; /* discovery has been suspended */ bool discovery_discoverable; /* discoverable while discovering */ GSList *discovery_list; /* list of discovery clients */ GSList *set_filter_list; /* list of clients that specified * filter, but don't scan yet */ /* current discovery filter, if any */ struct mgmt_cp_start_service_discovery *current_discovery_filter; struct discovery_client *client; /* active discovery client */ GSList *discovery_found; /* list of found devices */ unsigned int discovery_idle_timeout; /* timeout between discovery * runs */ unsigned int passive_scan_timeout; /* timeout between passive scans */ unsigned int pairable_timeout_id; /* pairable timeout id */ guint auth_idle_id; /* Pending authorization dequeue */ GQueue *auths; /* Ongoing and pending auths */ bool pincode_requested; /* PIN requested during last bonding */ GSList *connections; /* Connected devices */ GSList *devices; /* Devices structure pointers */ GSList *connect_list; /* Devices to connect when found */ struct btd_device *connect_le; /* LE device waiting to be connected */ sdp_list_t *services; /* Services associated to adapter */ struct btd_gatt_database *database; struct btd_adv_manager *adv_manager; struct btd_adv_monitor_manager *adv_monitor_manager; struct btd_battery_provider_manager *battery_provider_manager; GHashTable *allowed_uuid_set; /* Set of allowed service UUIDs */ gboolean initialized; GSList *pin_callbacks; GSList *msd_callbacks; GSList *drivers; GSList *profiles; struct oob_handler *oob_handler; unsigned int db_id; /* Service event handler for GATT db */ bool is_default; /* true if adapter is default one */ struct queue *exp_pending; struct queue *exps; }; static char *adapter_power_state_str(uint32_t power_state) { switch (power_state) { case ADAPTER_POWER_STATE_OFF: return "off"; case ADAPTER_POWER_STATE_ON: return "on"; case ADAPTER_POWER_STATE_ON_DISABLING: return "on-disabling"; case ADAPTER_POWER_STATE_OFF_ENABLING: return "off-enabling"; case ADAPTER_POWER_STATE_OFF_BLOCKED: return "off-blocked"; } DBG("Invalid power state %d", power_state); return ""; } typedef enum { ADAPTER_AUTHORIZE_DISCONNECTED = 0, ADAPTER_AUTHORIZE_CHECK_CONNECTED } adapter_authorize_type; static struct btd_adapter *btd_adapter_lookup(uint16_t index) { GList *list; for (list = g_list_first(adapter_list); list; list = g_list_next(list)) { struct btd_adapter *adapter = list->data; if (adapter->dev_id == index) return adapter; } return NULL; } struct btd_adapter *btd_adapter_get_default(void) { GList *list; for (list = g_list_first(adapter_list); list; list = g_list_next(list)) { struct btd_adapter *adapter = list->data; if (adapter->is_default) return adapter; } return NULL; } bool btd_adapter_is_default(struct btd_adapter *adapter) { if (!adapter) return false; return adapter->is_default; } uint16_t btd_adapter_get_index(struct btd_adapter *adapter) { if (!adapter) return MGMT_INDEX_NONE; return adapter->dev_id; } static gboolean process_auth_queue(gpointer user_data); static void dev_class_changed_callback(uint16_t index, uint16_t length, const void *param, void *user_data) { struct btd_adapter *adapter = user_data; const struct mgmt_cod *rp = param; uint32_t dev_class; if (length < sizeof(*rp)) { btd_error(adapter->dev_id, "Wrong size of class of device changed parameters"); return; } dev_class = rp->val[0] | (rp->val[1] << 8) | (rp->val[2] << 16); if (dev_class == adapter->dev_class) return; DBG("Class: 0x%06x", dev_class); adapter->dev_class = dev_class; g_dbus_emit_property_changed(dbus_conn, adapter->path, ADAPTER_INTERFACE, "Class"); } static void set_dev_class_complete(uint8_t status, uint16_t length, const void *param, void *user_data) { struct btd_adapter *adapter = user_data; if (status != MGMT_STATUS_SUCCESS) { btd_error(adapter->dev_id, "Failed to set device class: %s (0x%02x)", mgmt_errstr(status), status); return; } /* * The parameters are identical and also the task that is * required in both cases. So it is safe to just call the * event handling functions here. */ dev_class_changed_callback(adapter->dev_id, length, param, adapter); } static void set_dev_class(struct btd_adapter *adapter) { struct mgmt_cp_set_dev_class cp; /* * If the controller does not support BR/EDR operation, * there is no point in trying to set a major and minor * class value. * * This is an optimization for Low Energy only controllers. */ if (!(adapter->supported_settings & MGMT_SETTING_BREDR)) return; memset(&cp, 0, sizeof(cp)); /* * Silly workaround for a really stupid kernel bug :( * * All current kernel versions assign the major and minor numbers * straight to dev_class[0] and dev_class[1] without considering * the proper bit shifting. * * To make this work, shift the value in userspace for now until * we get a fixed kernel version. */ cp.major = adapter->major_class & 0x1f; cp.minor = adapter->minor_class << 2; DBG("sending set device class command for index %u", adapter->dev_id); if (mgmt_send(adapter->mgmt, MGMT_OP_SET_DEV_CLASS, adapter->dev_id, sizeof(cp), &cp, set_dev_class_complete, adapter, NULL) > 0) return; btd_error(adapter->dev_id, "Failed to set class of device for index %u", adapter->dev_id); } void btd_adapter_set_class(struct btd_adapter *adapter, uint8_t major, uint8_t minor) { if (adapter->major_class == major && adapter->minor_class == minor) return; DBG("class: major %u minor %u", major, minor); adapter->major_class = major; adapter->minor_class = minor; set_dev_class(adapter); } static uint8_t get_mode(const char *mode) { if (strcasecmp("off", mode) == 0) return MODE_OFF; else if (strcasecmp("connectable", mode) == 0) return MODE_CONNECTABLE; else if (strcasecmp("discoverable", mode) == 0) return MODE_DISCOVERABLE; else return MODE_UNKNOWN; } const char *btd_adapter_get_storage_dir(struct btd_adapter *adapter) { static char dir[25]; if (adapter->bdaddr_type == BDADDR_LE_RANDOM) { strcpy(dir, "static-"); ba2str(&adapter->bdaddr, dir + 7); } else { ba2str(&adapter->bdaddr, dir); } return dir; } uint8_t btd_adapter_get_address_type(struct btd_adapter *adapter) { return adapter->bdaddr_type; } static void store_adapter_info(struct btd_adapter *adapter) { GKeyFile *key_file; GError *gerr = NULL; char filename[PATH_MAX]; char *str; gsize length = 0; gboolean discoverable; key_file = g_key_file_new(); if (adapter->pairable_timeout != btd_opts.pairto) g_key_file_set_integer(key_file, "General", "PairableTimeout", adapter->pairable_timeout); if ((adapter->current_settings & MGMT_SETTING_DISCOVERABLE) && !adapter->discoverable_timeout) discoverable = TRUE; else discoverable = FALSE; g_key_file_set_boolean(key_file, "General", "Discoverable", discoverable); if (adapter->discoverable_timeout != btd_opts.discovto) g_key_file_set_integer(key_file, "General", "DiscoverableTimeout", adapter->discoverable_timeout); if (adapter->stored_alias) g_key_file_set_string(key_file, "General", "Alias", adapter->stored_alias); create_filename(filename, PATH_MAX, "/%s/settings", btd_adapter_get_storage_dir(adapter)); create_file(filename, 0600); str = g_key_file_to_data(key_file, &length, NULL); if (!g_file_set_contents(filename, str, length, &gerr)) { error("Unable set contents for %s: (%s)", filename, gerr->message); g_error_free(gerr); } g_free(str); g_key_file_free(key_file); } static void trigger_pairable_timeout(struct btd_adapter *adapter); static void adapter_start(struct btd_adapter *adapter); static void adapter_stop(struct btd_adapter *adapter); static void trigger_passive_scanning(struct btd_adapter *adapter); static bool set_mode(struct btd_adapter *adapter, uint16_t opcode, uint8_t mode); static void settings_changed(struct btd_adapter *adapter, uint32_t settings) { uint32_t changed_mask; changed_mask = adapter->current_settings ^ settings; adapter->current_settings = settings; adapter->pending_settings &= ~changed_mask; DBG("Changed settings: 0x%08x", changed_mask); DBG("Pending settings: 0x%08x", adapter->pending_settings); if (changed_mask & MGMT_SETTING_POWERED) { g_dbus_emit_property_changed(dbus_conn, adapter->path, ADAPTER_INTERFACE, "Powered"); if (adapter->current_settings & MGMT_SETTING_POWERED) { adapter_start(adapter); } else { adapter_stop(adapter); if (powering_down) { adapter_remaining--; if (!adapter_remaining) btd_exit(); } } } if ((changed_mask & MGMT_SETTING_LE) && btd_adapter_get_powered(adapter) && (adapter->current_settings & MGMT_SETTING_LE)) trigger_passive_scanning(adapter); if (changed_mask & MGMT_SETTING_DISCOVERABLE) { g_dbus_emit_property_changed(dbus_conn, adapter->path, ADAPTER_INTERFACE, "Discoverable"); /* Only persist discoverable setting if it was not set * temporarily by discovery. */ if (!adapter->discovery_discoverable) store_adapter_info(adapter); btd_adv_manager_refresh(adapter->adv_manager); } if (changed_mask & MGMT_SETTING_CONNECTABLE) g_dbus_emit_property_changed(dbus_conn, adapter->path, ADAPTER_INTERFACE, "Connectable"); if (changed_mask & MGMT_SETTING_BONDABLE) { g_dbus_emit_property_changed(dbus_conn, adapter->path, ADAPTER_INTERFACE, "Pairable"); trigger_pairable_timeout(adapter); } } static void adapter_set_power_state(struct btd_adapter *adapter, uint32_t value) { if (adapter->power_state == value) return; DBG("%s", adapter_power_state_str(value)); adapter->power_state = value; g_dbus_emit_property_changed(dbus_conn, adapter->path, ADAPTER_INTERFACE, "PowerState"); } static void reset_power_state_target(struct btd_adapter *adapter, uint32_t value) { if (value && adapter->power_state == ADAPTER_POWER_STATE_OFF_ENABLING) { adapter_set_power_state(adapter, ADAPTER_POWER_STATE_ON); } else if (!value && adapter->power_state == ADAPTER_POWER_STATE_ON_DISABLING) { adapter_set_power_state(adapter, ADAPTER_POWER_STATE_OFF); } } static void new_settings_callback(uint16_t index, uint16_t length, const void *param, void *user_data) { struct btd_adapter *adapter = user_data; uint32_t settings; if (length < sizeof(settings)) { btd_error(adapter->dev_id, "Wrong size of new settings parameters"); return; } settings = get_le32(param); if (settings == adapter->current_settings) return; if ((adapter->current_settings ^ settings) & MGMT_SETTING_POWERED) { reset_power_state_target(adapter, settings & MGMT_SETTING_POWERED ? 0x01 : 0x00); } DBG("Settings: 0x%08x", settings); settings_changed(adapter, settings); } struct set_mode_data { struct btd_adapter *adapter; uint32_t setting; uint8_t value; }; static void set_mode_complete(uint8_t status, uint16_t length, const void *param, void *user_data) { struct set_mode_data *data = user_data; struct btd_adapter *adapter = data->adapter; if (status != MGMT_STATUS_SUCCESS) { btd_error(adapter->dev_id, "Failed to set mode: %s (0x%02x)", mgmt_errstr(status), status); if (status == MGMT_STATUS_RFKILLED) adapter_set_power_state(adapter, ADAPTER_POWER_STATE_OFF_BLOCKED); adapter->pending_settings &= ~data->setting; if (status != MGMT_STATUS_RFKILLED && data->setting & MGMT_SETTING_POWERED) reset_power_state_target(adapter, data->value); return; } /* * The parameters are identical and also the task that is * required in both cases. So it is safe to just call the * event handling functions here. */ new_settings_callback(adapter->dev_id, length, param, adapter); } static void remove_temporary_devices(struct btd_adapter *adapter) { GSList *l, *next; for (l = adapter->devices; l; l = next) { struct btd_device *dev = l->data; next = g_slist_next(l); if (device_is_temporary(dev)) btd_adapter_remove_device(adapter, dev); } } static bool set_mode(struct btd_adapter *adapter, uint16_t opcode, uint8_t mode) { struct mgmt_mode cp; uint32_t setting = 0; struct set_mode_data *data; memset(&cp, 0, sizeof(cp)); cp.val = mode; switch (opcode) { case MGMT_OP_SET_POWERED: setting = MGMT_SETTING_POWERED; if (adapter->power_state != ADAPTER_POWER_STATE_OFF_BLOCKED) { adapter_set_power_state(adapter, mode ? ADAPTER_POWER_STATE_OFF_ENABLING : ADAPTER_POWER_STATE_ON_DISABLING); } break; case MGMT_OP_SET_CONNECTABLE: setting = MGMT_SETTING_CONNECTABLE; break; case MGMT_OP_SET_FAST_CONNECTABLE: setting = MGMT_SETTING_FAST_CONNECTABLE; break; case MGMT_OP_SET_DISCOVERABLE: setting = MGMT_SETTING_DISCOVERABLE; break; case MGMT_OP_SET_BONDABLE: setting = MGMT_SETTING_BONDABLE; break; } DBG("sending set mode command for index %u", adapter->dev_id); data = g_new0(struct set_mode_data, 1); data->adapter = adapter; data->setting = setting; data->value = mode; if (mgmt_send(adapter->mgmt, opcode, adapter->dev_id, sizeof(cp), &cp, set_mode_complete, data, g_free) > 0) { adapter->pending_settings |= setting; return true; } g_free(data); if (setting == MGMT_SETTING_POWERED) { /* cancel the earlier setting */ adapter_set_power_state(adapter, mode ? ADAPTER_POWER_STATE_OFF : ADAPTER_POWER_STATE_ON); } btd_error(adapter->dev_id, "Failed to set mode for index %u", adapter->dev_id); return false; } static bool set_discoverable(struct btd_adapter *adapter, uint8_t mode, uint16_t timeout) { struct mgmt_cp_set_discoverable cp; struct set_mode_data *data; memset(&cp, 0, sizeof(cp)); cp.val = mode; cp.timeout = htobs(timeout); DBG("sending set mode command for index %u", adapter->dev_id); if (btd_has_kernel_features(KERNEL_CONN_CONTROL)) { if (mode) set_mode(adapter, MGMT_OP_SET_CONNECTABLE, mode); else /* This also disables discoverable so we're done */ return set_mode(adapter, MGMT_OP_SET_CONNECTABLE, mode); } data = g_new0(struct set_mode_data, 1); data->adapter = adapter; data->setting = 0; if (mgmt_send(adapter->mgmt, MGMT_OP_SET_DISCOVERABLE, adapter->dev_id, sizeof(cp), &cp, set_mode_complete, data, g_free) > 0) return true; g_free(data); btd_error(adapter->dev_id, "Failed to set mode for index %u", adapter->dev_id); return false; } static bool pairable_timeout_handler(gpointer user_data) { struct btd_adapter *adapter = user_data; adapter->pairable_timeout_id = 0; set_mode(adapter, MGMT_OP_SET_BONDABLE, 0x00); return FALSE; } static void trigger_pairable_timeout(struct btd_adapter *adapter) { if (adapter->pairable_timeout_id > 0) { timeout_remove(adapter->pairable_timeout_id); adapter->pairable_timeout_id = 0; } if (!(adapter->current_settings & MGMT_SETTING_BONDABLE)) return; if (adapter->pairable_timeout > 0) adapter->pairable_timeout_id = timeout_add_seconds(adapter->pairable_timeout, pairable_timeout_handler, adapter, NULL); } static void local_name_changed_callback(uint16_t index, uint16_t length, const void *param, void *user_data) { struct btd_adapter *adapter = user_data; const struct mgmt_cp_set_local_name *rp = param; if (length < sizeof(*rp)) { btd_error(adapter->dev_id, "Wrong size of local name changed parameters"); return; } if (!g_strcmp0(adapter->short_name, (const char *) rp->short_name) && !g_strcmp0(adapter->name, (const char *) rp->name)) return; DBG("Name: %s", rp->name); DBG("Short name: %s", rp->short_name); g_free(adapter->name); adapter->name = g_strdup((const char *) rp->name); g_free(adapter->short_name); adapter->short_name = g_strdup((const char *) rp->short_name); /* * Changing the name (even manually via HCI) will update the * current alias property. * * In case the name is empty, use the short name. * * There is a difference between the stored alias (which is * configured by the user) and the current alias. The current * alias is temporary for the lifetime of the daemon. */ if (adapter->name && adapter->name[0] != '\0') { g_free(adapter->current_alias); adapter->current_alias = g_strdup(adapter->name); } else { g_free(adapter->current_alias); adapter->current_alias = g_strdup(adapter->short_name); } DBG("Current alias: %s", adapter->current_alias); if (!adapter->current_alias) return; g_dbus_emit_property_changed(dbus_conn, adapter->path, ADAPTER_INTERFACE, "Alias"); } static void set_local_name_complete(uint8_t status, uint16_t length, const void *param, void *user_data) { struct btd_adapter *adapter = user_data; if (status != MGMT_STATUS_SUCCESS) { btd_error(adapter->dev_id, "Failed to set local name: %s (0x%02x)", mgmt_errstr(status), status); return; } /* * The parameters are identical and also the task that is * required in both cases. So it is safe to just call the * event handling functions here. */ local_name_changed_callback(adapter->dev_id, length, param, adapter); } static int set_name(struct btd_adapter *adapter, const char *name) { struct mgmt_cp_set_local_name cp; char maxname[MAX_NAME_LENGTH]; memset(maxname, 0, sizeof(maxname)); strncpy(maxname, name, MAX_NAME_LENGTH - 1); if (!g_utf8_validate(maxname, -1, NULL)) { btd_error(adapter->dev_id, "Name change failed: supplied name isn't valid UTF-8"); return -EINVAL; } memset(&cp, 0, sizeof(cp)); strncpy((char *) cp.name, maxname, sizeof(cp.name) - 1); DBG("sending set local name command for index %u", adapter->dev_id); if (mgmt_send(adapter->mgmt, MGMT_OP_SET_LOCAL_NAME, adapter->dev_id, sizeof(cp), &cp, set_local_name_complete, adapter, NULL) > 0) return 0; btd_error(adapter->dev_id, "Failed to set local name for index %u", adapter->dev_id); return -EIO; } int adapter_set_name(struct btd_adapter *adapter, const char *name) { if (g_strcmp0(adapter->system_name, name) == 0) return 0; DBG("name: %s", name); g_free(adapter->system_name); adapter->system_name = g_strdup(name); g_dbus_emit_property_changed(dbus_conn, adapter->path, ADAPTER_INTERFACE, "Name"); /* alias is preferred over system name */ if (adapter->stored_alias) return 0; DBG("alias: %s", name); g_dbus_emit_property_changed(dbus_conn, adapter->path, ADAPTER_INTERFACE, "Alias"); return set_name(adapter, name); } struct btd_device *btd_adapter_find_device(struct btd_adapter *adapter, const bdaddr_t *dst, uint8_t bdaddr_type) { struct device_addr_type addr; struct btd_device *device; GSList *list; if (!adapter) return NULL; bacpy(&addr.bdaddr, dst); addr.bdaddr_type = bdaddr_type; list = g_slist_find_custom(adapter->devices, &addr, device_addr_type_cmp); if (!list) return NULL; device = list->data; /* * If we're looking up based on public address and the address * was not previously used over this bearer we may need to * update LE or BR/EDR support information. */ if (bdaddr_type == BDADDR_BREDR) device_set_bredr_support(device); else device_set_le_support(device, bdaddr_type); return device; } static int device_path_cmp(gconstpointer a, gconstpointer b) { const struct btd_device *device = a; const char *path = b; const char *dev_path = device_get_path(device); return strcasecmp(dev_path, path); } struct btd_device *btd_adapter_find_device_by_path(struct btd_adapter *adapter, const char *path) { GSList *list; if (!adapter) return NULL; list = g_slist_find_custom(adapter->devices, path, device_path_cmp); if (!list) return NULL; return list->data; } static void uuid_to_uuid128(uuid_t *uuid128, const uuid_t *uuid) { if (uuid->type == SDP_UUID16) sdp_uuid16_to_uuid128(uuid128, uuid); else if (uuid->type == SDP_UUID32) sdp_uuid32_to_uuid128(uuid128, uuid); else memcpy(uuid128, uuid, sizeof(*uuid)); } static bool is_supported_uuid(const uuid_t *uuid) { uuid_t tmp; /* mgmt versions from 1.3 onwards support all types of UUIDs */ if (MGMT_VERSION(mgmt_version, mgmt_revision) >= MGMT_VERSION(1, 3)) return true; uuid_to_uuid128(&tmp, uuid); if (!sdp_uuid128_to_uuid(&tmp)) return false; if (tmp.type != SDP_UUID16) return false; return true; } static void add_uuid_complete(uint8_t status, uint16_t length, const void *param, void *user_data) { struct btd_adapter *adapter = user_data; if (status != MGMT_STATUS_SUCCESS) { btd_error(adapter->dev_id, "Failed to add UUID: %s (0x%02x)", mgmt_errstr(status), status); return; } /* * The parameters are identical and also the task that is * required in both cases. So it is safe to just call the * event handling functions here. */ dev_class_changed_callback(adapter->dev_id, length, param, adapter); if (adapter->initialized) g_dbus_emit_property_changed(dbus_conn, adapter->path, ADAPTER_INTERFACE, "UUIDs"); } static int add_uuid(struct btd_adapter *adapter, uuid_t *uuid, uint8_t svc_hint) { struct mgmt_cp_add_uuid cp; uuid_t uuid128; uint128_t uint128; if (!is_supported_uuid(uuid)) { btd_warn(adapter->dev_id, "Ignoring unsupported UUID for addition"); return 0; } uuid_to_uuid128(&uuid128, uuid); ntoh128((uint128_t *) uuid128.value.uuid128.data, &uint128); htob128(&uint128, (uint128_t *) cp.uuid); cp.svc_hint = svc_hint; DBG("sending add uuid command for index %u", adapter->dev_id); if (mgmt_send(adapter->mgmt, MGMT_OP_ADD_UUID, adapter->dev_id, sizeof(cp), &cp, add_uuid_complete, adapter, NULL) > 0) return 0; btd_error(adapter->dev_id, "Failed to add UUID for index %u", adapter->dev_id); return -EIO; } static void remove_uuid_complete(uint8_t status, uint16_t length, const void *param, void *user_data) { struct btd_adapter *adapter = user_data; if (status != MGMT_STATUS_SUCCESS) { btd_error(adapter->dev_id, "Failed to remove UUID: %s (0x%02x)", mgmt_errstr(status), status); return; } /* * The parameters are identical and also the task that is * required in both cases. So it is safe to just call the * event handling functions here. */ dev_class_changed_callback(adapter->dev_id, length, param, adapter); if (adapter->initialized) g_dbus_emit_property_changed(dbus_conn, adapter->path, ADAPTER_INTERFACE, "UUIDs"); } static int remove_uuid(struct btd_adapter *adapter, uuid_t *uuid) { struct mgmt_cp_remove_uuid cp; uuid_t uuid128; uint128_t uint128; if (!is_supported_uuid(uuid)) { btd_warn(adapter->dev_id, "Ignoring unsupported UUID for removal"); return 0; } uuid_to_uuid128(&uuid128, uuid); ntoh128((uint128_t *) uuid128.value.uuid128.data, &uint128); htob128(&uint128, (uint128_t *) cp.uuid); DBG("sending remove uuid command for index %u", adapter->dev_id); if (mgmt_send(adapter->mgmt, MGMT_OP_REMOVE_UUID, adapter->dev_id, sizeof(cp), &cp, remove_uuid_complete, adapter, NULL) > 0) return 0; btd_error(adapter->dev_id, "Failed to remove UUID for index %u", adapter->dev_id); return -EIO; } static void clear_uuids_complete(uint8_t status, uint16_t length, const void *param, void *user_data) { struct btd_adapter *adapter = user_data; if (status != MGMT_STATUS_SUCCESS) { btd_error(adapter->dev_id, "Failed to clear UUIDs: %s (0x%02x)", mgmt_errstr(status), status); return; } /* * The parameters are identical and also the task that is * required in both cases. So it is safe to just call the * event handling functions here. */ dev_class_changed_callback(adapter->dev_id, length, param, adapter); } static int clear_uuids(struct btd_adapter *adapter) { struct mgmt_cp_remove_uuid cp; memset(&cp, 0, sizeof(cp)); DBG("sending clear uuids command for index %u", adapter->dev_id); if (mgmt_send(adapter->mgmt, MGMT_OP_REMOVE_UUID, adapter->dev_id, sizeof(cp), &cp, clear_uuids_complete, adapter, NULL) > 0) return 0; btd_error(adapter->dev_id, "Failed to clear UUIDs for index %u", adapter->dev_id); return -EIO; } static uint8_t get_uuid_mask(uuid_t *uuid) { if (uuid->type != SDP_UUID16) return 0; switch (uuid->value.uuid16) { case DIALUP_NET_SVCLASS_ID: case CIP_SVCLASS_ID: return 0x42; /* Telephony & Networking */ case IRMC_SYNC_SVCLASS_ID: case OBEX_OBJPUSH_SVCLASS_ID: case OBEX_FILETRANS_SVCLASS_ID: case IRMC_SYNC_CMD_SVCLASS_ID: case PBAP_PSE_SVCLASS_ID: return 0x10; /* Object Transfer */ case HEADSET_SVCLASS_ID: case HANDSFREE_SVCLASS_ID: return 0x20; /* Audio */ case CORDLESS_TELEPHONY_SVCLASS_ID: case INTERCOM_SVCLASS_ID: case FAX_SVCLASS_ID: case SAP_SVCLASS_ID: /* * Setting the telephony bit for the handsfree audio gateway * role is not required by the HFP specification, but the * Nokia 616 carkit is just plain broken! It will refuse * pairing without this bit set. */ case HANDSFREE_AGW_SVCLASS_ID: return 0x40; /* Telephony */ case AUDIO_SOURCE_SVCLASS_ID: case VIDEO_SOURCE_SVCLASS_ID: return 0x08; /* Capturing */ case AUDIO_SINK_SVCLASS_ID: case VIDEO_SINK_SVCLASS_ID: return 0x04; /* Rendering */ case PANU_SVCLASS_ID: case NAP_SVCLASS_ID: case GN_SVCLASS_ID: return 0x02; /* Networking */ default: return 0; } } static int uuid_cmp(const void *a, const void *b) { const sdp_record_t *rec = a; const uuid_t *uuid = b; return sdp_uuid_cmp(&rec->svclass, uuid); } static void adapter_service_insert(struct btd_adapter *adapter, sdp_record_t *rec) { sdp_list_t *browse_list = NULL; uuid_t browse_uuid; gboolean new_uuid; DBG("%s", adapter->path); /* skip record without a browse group */ if (sdp_get_browse_groups(rec, &browse_list) < 0) { DBG("skipping record without browse group"); return; } sdp_uuid16_create(&browse_uuid, PUBLIC_BROWSE_GROUP); /* skip record without public browse group */ if (!sdp_list_find(browse_list, &browse_uuid, sdp_uuid_cmp)) goto done; if (sdp_list_find(adapter->services, &rec->svclass, uuid_cmp) == NULL) new_uuid = TRUE; else new_uuid = FALSE; adapter->services = sdp_list_insert_sorted(adapter->services, rec, record_sort); if (new_uuid) { uint8_t svc_hint = get_uuid_mask(&rec->svclass); add_uuid(adapter, &rec->svclass, svc_hint); } done: sdp_list_free(browse_list, free); } int adapter_service_add(struct btd_adapter *adapter, sdp_record_t *rec) { int ret; /* * If the controller does not support BR/EDR operation, * there is no point in trying to add SDP records. */ if (btd_opts.mode == BT_MODE_LE) return -ENOTSUP; DBG("%s", adapter->path); ret = add_record_to_server(&adapter->bdaddr, rec); if (ret < 0) return ret; adapter_service_insert(adapter, rec); return 0; } void adapter_service_remove(struct btd_adapter *adapter, uint32_t handle) { sdp_record_t *rec; if (!adapter) return; /* * If the controller does not support BR/EDR operation, * there is no point in trying to remote SDP records. */ if (btd_opts.mode == BT_MODE_LE) return; DBG("%s", adapter->path); rec = sdp_record_find(handle); if (!rec) return; adapter->services = sdp_list_remove(adapter->services, rec); if (sdp_list_find(adapter->services, &rec->svclass, uuid_cmp) == NULL) remove_uuid(adapter, &rec->svclass); remove_record_from_server(rec->handle); } static void adapter_add_device(struct btd_adapter *adapter, struct btd_device *device); static struct btd_device *adapter_create_device(struct btd_adapter *adapter, const bdaddr_t *bdaddr, uint8_t bdaddr_type) { struct btd_device *device; device = device_create(adapter, bdaddr, bdaddr_type); if (!device) return NULL; adapter_add_device(adapter, device); return device; } static void service_auth_cancel(struct service_auth *auth) { DBusError derr; if (auth->svc_id > 0) device_remove_svc_complete_callback(auth->device, auth->svc_id); dbus_error_init(&derr); dbus_set_error_const(&derr, ERROR_INTERFACE ".Canceled", NULL); auth->cb(&derr, auth->user_data); dbus_error_free(&derr); if (auth->agent != NULL) { agent_cancel(auth->agent); agent_unref(auth->agent); } g_free(auth); } static void adapter_remove_device(struct btd_adapter *adapter, struct btd_device *device); void btd_adapter_remove_device(struct btd_adapter *adapter, struct btd_device *dev) { adapter->connect_list = g_slist_remove(adapter->connect_list, dev); adapter_remove_device(adapter, dev); btd_adv_monitor_device_remove(adapter->adv_monitor_manager, dev); adapter->discovery_found = g_slist_remove(adapter->discovery_found, dev); adapter->connections = g_slist_remove(adapter->connections, dev); if (adapter->connect_le == dev) adapter->connect_le = NULL; btd_adapter_cancel_service_auth(adapter, dev); device_remove(dev, TRUE); } struct btd_device *btd_adapter_get_device(struct btd_adapter *adapter, const bdaddr_t *addr, uint8_t addr_type) { struct btd_device *device; if (!adapter) return NULL; device = btd_adapter_find_device(adapter, addr, addr_type); if (device) return device; return adapter_create_device(adapter, addr, addr_type); } struct btd_device *btd_adapter_find_device_by_fd(int fd) { bdaddr_t src, dst; uint8_t dst_type; GIOChannel *io = NULL; GError *gerr = NULL; struct btd_adapter *adapter; io = g_io_channel_unix_new(fd); if (!io) return NULL; bt_io_get(io, &gerr, BT_IO_OPT_SOURCE_BDADDR, &src, BT_IO_OPT_DEST_BDADDR, &dst, BT_IO_OPT_DEST_TYPE, &dst_type, BT_IO_OPT_INVALID); if (gerr) { error("bt_io_get: %s", gerr->message); g_error_free(gerr); g_io_channel_unref(io); return NULL; } g_io_channel_unref(io); adapter = adapter_find(&src); if (!adapter) return NULL; return btd_adapter_find_device(adapter, &dst, dst_type); } sdp_list_t *btd_adapter_get_services(struct btd_adapter *adapter) { return adapter->services; } static void passive_scanning_complete(uint8_t status, uint16_t length, const void *param, void *user_data) { struct btd_adapter *adapter = user_data; const struct mgmt_cp_start_discovery *rp = param; DBG("status 0x%02x", status); if (length < sizeof(*rp)) { btd_error(adapter->dev_id, "Wrong size of start scanning return parameters"); return; } if (status == MGMT_STATUS_SUCCESS) { adapter->discovery_type = rp->type; adapter->discovery_enable = 0x01; } } static bool passive_scanning_timeout(gpointer user_data) { struct btd_adapter *adapter = user_data; struct mgmt_cp_start_discovery cp; adapter->passive_scan_timeout = 0; cp.type = SCAN_TYPE_LE; mgmt_send(adapter->mgmt, MGMT_OP_START_DISCOVERY, adapter->dev_id, sizeof(cp), &cp, passive_scanning_complete, adapter, NULL); return FALSE; } static void trigger_passive_scanning(struct btd_adapter *adapter) { if (!(adapter->current_settings & MGMT_SETTING_LE)) return; DBG(""); if (adapter->passive_scan_timeout > 0) { timeout_remove(adapter->passive_scan_timeout); adapter->passive_scan_timeout = 0; } /* * When the kernel background scanning is available, there is * no need to start any discovery. The kernel will keep scanning * as long as devices are in its auto-connection list. */ if (btd_has_kernel_features(KERNEL_CONN_CONTROL)) return; /* * If any client is running a discovery right now, then do not * even try to start passive scanning. * * The discovery procedure is using interleaved scanning and * thus will discover Low Energy devices as well. */ if (adapter->discovery_list) return; if (adapter->discovery_enable == 0x01) return; /* * In case the discovery is suspended (for example for an ongoing * pairing attempt), then also do not start passive scanning. */ if (adapter->discovery_suspended) return; /* * If the list of connectable Low Energy devices is empty, * then do not start passive scanning. */ if (!adapter->connect_list) return; adapter->passive_scan_timeout = timeout_add_seconds(CONN_SCAN_TIMEOUT, passive_scanning_timeout, adapter, NULL); } static void stop_passive_scanning_complete(uint8_t status, uint16_t length, const void *param, void *user_data) { struct btd_adapter *adapter = user_data; struct btd_device *dev; int err; DBG("status 0x%02x (%s)", status, mgmt_errstr(status)); dev = adapter->connect_le; adapter->connect_le = NULL; /* * When the kernel background scanning is available, there is * no need to stop any discovery. The kernel will handle the * auto-connection by itself. */ if (btd_has_kernel_features(KERNEL_CONN_CONTROL)) return; /* * MGMT_STATUS_REJECTED may be returned from kernel because the passive * scan timer had expired in kernel and passive scan was disabled just * around the time we called stop_passive_scanning(). */ if (status != MGMT_STATUS_SUCCESS && status != MGMT_STATUS_REJECTED) { btd_error(adapter->dev_id, "Stopping passive scanning failed: %s", mgmt_errstr(status)); return; } adapter->discovery_type = 0x00; adapter->discovery_enable = 0x00; if (!dev) { DBG("Device removed while stopping passive scanning"); trigger_passive_scanning(adapter); return; } err = device_connect_le(dev); if (err < 0) { btd_error(adapter->dev_id, "LE auto connection failed: %s (%d)", strerror(-err), -err); trigger_passive_scanning(adapter); } } static void stop_passive_scanning(struct btd_adapter *adapter) { struct mgmt_cp_stop_discovery cp; DBG(""); /* If there are any normal discovery clients passive scanning * wont be running */ if (adapter->discovery_list) return; if (adapter->discovery_enable == 0x00) return; cp.type = adapter->discovery_type; mgmt_send(adapter->mgmt, MGMT_OP_STOP_DISCOVERY, adapter->dev_id, sizeof(cp), &cp, stop_passive_scanning_complete, adapter, NULL); } static void cancel_passive_scanning(struct btd_adapter *adapter) { if (!(adapter->current_settings & MGMT_SETTING_LE)) return; DBG(""); if (adapter->passive_scan_timeout > 0) { timeout_remove(adapter->passive_scan_timeout); adapter->passive_scan_timeout = 0; } } static uint8_t get_scan_type(struct btd_adapter *adapter) { uint8_t type; if (adapter->current_settings & MGMT_SETTING_BREDR) type = SCAN_TYPE_BREDR; else type = 0; if (adapter->current_settings & MGMT_SETTING_LE) type |= SCAN_TYPE_LE; return type; } static void free_discovery_filter(struct discovery_filter *discovery_filter) { if (!discovery_filter) return; g_slist_free_full(discovery_filter->uuids, free); free(discovery_filter->pattern); g_free(discovery_filter); } static void invalidate_rssi_and_tx_power(gpointer a) { struct btd_device *dev = a; device_set_rssi(dev, 0); device_set_tx_power(dev, 127); } static void discovery_cleanup(struct btd_adapter *adapter, int timeout) { GSList *l, *next; adapter->discovery_type = 0x00; if (adapter->discovery_idle_timeout > 0) { timeout_remove(adapter->discovery_idle_timeout); adapter->discovery_idle_timeout = 0; } g_slist_free_full(adapter->discovery_found, invalidate_rssi_and_tx_power); adapter->discovery_found = NULL; if (!adapter->devices) return; for (l = adapter->devices; l != NULL; l = next) { struct btd_device *dev = l->data; next = g_slist_next(l); if (device_is_temporary(dev) && !device_is_connectable(dev) && !btd_device_is_connected(dev)) btd_adapter_remove_device(adapter, dev); } } static void discovery_free(void *user_data) { struct discovery_client *client = user_data; struct btd_adapter *adapter = client->adapter; DBG("%p", client); if (client->watch) g_dbus_remove_watch(dbus_conn, client->watch); if (client->discovery_filter) { free_discovery_filter(client->discovery_filter); client->discovery_filter = NULL; } if (client->msg) { if (client == adapter->client) { g_dbus_send_message(dbus_conn, btd_error_busy(client->msg)); adapter->client = NULL; } dbus_message_unref(client->msg); } g_free(client->owner); g_free(client); } static void discovery_remove(struct discovery_client *client) { struct btd_adapter *adapter = client->adapter; DBG("owner %s", client->owner); adapter->set_filter_list = g_slist_remove(adapter->set_filter_list, client); adapter->discovery_list = g_slist_remove(adapter->discovery_list, client); if (adapter->client == client) adapter->client = NULL; if (client->watch && client->discovery_filter) adapter->set_filter_list = g_slist_prepend( adapter->set_filter_list, client); else discovery_free(client); /* * If there are other client discoveries in progress, then leave * it active. If not, then make sure to stop the restart timeout. */ if (adapter->discovery_list) return; discovery_cleanup(adapter, TEMP_DEV_TIMEOUT); } static void trigger_start_discovery(struct btd_adapter *adapter, guint delay); static struct discovery_client *discovery_complete(struct btd_adapter *adapter, uint8_t status) { struct discovery_client *client = adapter->client; DBusMessage *reply; if (!client) return NULL; adapter->client = NULL; if (!client->msg) return client; if (!status) { g_dbus_send_reply(dbus_conn, client->msg, DBUS_TYPE_INVALID); } else { reply = btd_error_busy(client->msg); g_dbus_send_message(dbus_conn, reply); } dbus_message_unref(client->msg); client->msg = NULL; return client; } static void start_discovery_complete(uint8_t status, uint16_t length, const void *param, void *user_data) { struct btd_adapter *adapter = user_data; struct discovery_client *client; const struct mgmt_cp_start_discovery *rp = param; DBG("status 0x%02x", status); /* Is there are no clients the discovery must have been stopped while * discovery command was pending. */ if (!adapter->discovery_list) { struct mgmt_cp_stop_discovery cp; if (status != MGMT_STATUS_SUCCESS) return; /* Stop discovering as there are no clients left */ cp.type = rp->type; mgmt_send(adapter->mgmt, MGMT_OP_STOP_DISCOVERY, adapter->dev_id, sizeof(cp), &cp, NULL, NULL, NULL); return; } if (length < sizeof(*rp)) { btd_error(adapter->dev_id, "Wrong size of start discovery return parameters"); discovery_complete(adapter, MGMT_STATUS_FAILED); return; } if (status == MGMT_STATUS_SUCCESS) { adapter->discovery_type = rp->type; adapter->discovery_enable = 0x01; if (adapter->current_discovery_filter) adapter->filtered_discovery = true; else adapter->filtered_discovery = false; discovery_complete(adapter, status); if (adapter->discovering) return; adapter->discovering = true; g_dbus_emit_property_changed(dbus_conn, adapter->path, ADAPTER_INTERFACE, "Discovering"); return; } /* Reply with an error if the first discovery has failed */ client = discovery_complete(adapter, status); if (client) { discovery_remove(client); return; } /* * In case the restart of the discovery failed, then just trigger * it for the next idle timeout again. */ trigger_start_discovery(adapter, IDLE_DISCOV_TIMEOUT * 2); } static bool start_discovery_timeout(gpointer user_data) { struct btd_adapter *adapter = user_data; struct mgmt_cp_start_service_discovery *sd_cp; uint8_t new_type; DBG(""); adapter->discovery_idle_timeout = 0; /* If we're doing filtered discovery, it must be quickly restarted */ adapter->no_scan_restart_delay = !!adapter->current_discovery_filter; DBG("adapter->current_discovery_filter == %d", !!adapter->current_discovery_filter); new_type = get_scan_type(adapter); if (adapter->discovery_enable == 0x01) { struct mgmt_cp_stop_discovery cp; /* * If we're asked to start regular discovery, and there is an * already running regular discovery and it has the same type, * then just keep it. */ if (!adapter->current_discovery_filter && !adapter->filtered_discovery && adapter->discovery_type == new_type) { if (adapter->discovering) return FALSE; adapter->discovering = true; g_dbus_emit_property_changed(dbus_conn, adapter->path, ADAPTER_INTERFACE, "Discovering"); return FALSE; } /* * Otherwise the current discovery must be stopped. So * queue up a stop discovery command. * * This can happen if a passive scanning for Low Energy * devices is ongoing, or scan type is changed between * regular and filtered, or filter was updated. */ cp.type = adapter->discovery_type; mgmt_send(adapter->mgmt, MGMT_OP_STOP_DISCOVERY, adapter->dev_id, sizeof(cp), &cp, NULL, NULL, NULL); /* Don't even bother to try to quickly start discovery * just after stopping it, it would fail with status * MGMT_BUSY. Instead discovering_callback will take * care of that. */ return FALSE; } /* Regular discovery is required */ if (!adapter->current_discovery_filter) { struct mgmt_cp_start_discovery cp; cp.type = new_type; mgmt_send(adapter->mgmt, MGMT_OP_START_DISCOVERY, adapter->dev_id, sizeof(cp), &cp, start_discovery_complete, adapter, NULL); return FALSE; } /* Filtered discovery is required */ sd_cp = adapter->current_discovery_filter; DBG("sending MGMT_OP_START_SERVICE_DISCOVERY %d, %d, %d", sd_cp->rssi, sd_cp->type, btohs(sd_cp->uuid_count)); mgmt_send(adapter->mgmt, MGMT_OP_START_SERVICE_DISCOVERY, adapter->dev_id, sizeof(*sd_cp) + btohs(sd_cp->uuid_count) * 16, sd_cp, start_discovery_complete, adapter, NULL); return FALSE; } static void trigger_start_discovery(struct btd_adapter *adapter, guint delay) { DBG(""); cancel_passive_scanning(adapter); if (adapter->discovery_idle_timeout > 0) { timeout_remove(adapter->discovery_idle_timeout); adapter->discovery_idle_timeout = 0; } /* * If the controller got powered down in between, then ensure * that we do not keep trying to restart discovery. * * This is safe-guard and should actually never trigger. */ if (!btd_adapter_get_powered(adapter)) return; adapter->discovery_idle_timeout = timeout_add_seconds(delay, start_discovery_timeout, adapter, NULL); } static void suspend_discovery_complete(uint8_t status, uint16_t length, const void *param, void *user_data) { struct btd_adapter *adapter = user_data; DBG("status 0x%02x", status); if (status == MGMT_STATUS_SUCCESS) { adapter->discovery_type = 0x00; adapter->discovery_enable = 0x00; return; } } static void suspend_discovery(struct btd_adapter *adapter) { struct mgmt_cp_stop_discovery cp; DBG(""); adapter->discovery_suspended = true; /* * If there are no clients discovering right now, then there is * also nothing to suspend. */ if (!adapter->discovery_list) return; /* * In case of being inside the idle phase, make sure to remove * the timeout to not trigger a restart. * * The restart will be triggered when the discovery is resumed. */ if (adapter->discovery_idle_timeout > 0) { timeout_remove(adapter->discovery_idle_timeout); adapter->discovery_idle_timeout = 0; } if (adapter->discovery_enable == 0x00) return; cp.type = adapter->discovery_type; mgmt_send(adapter->mgmt, MGMT_OP_STOP_DISCOVERY, adapter->dev_id, sizeof(cp), &cp, suspend_discovery_complete, adapter, NULL); } static void resume_discovery(struct btd_adapter *adapter) { DBG(""); adapter->discovery_suspended = false; /* * If there are no clients discovering right now, then there is * also nothing to resume. */ if (!adapter->discovery_list) return; /* * Treat a suspended discovery session the same as extra long * idle time for a normal discovery. So just trigger the default * restart procedure. */ trigger_start_discovery(adapter, IDLE_DISCOV_TIMEOUT); } static void discovering_callback(uint16_t index, uint16_t length, const void *param, void *user_data) { const struct mgmt_ev_discovering *ev = param; struct btd_adapter *adapter = user_data; if (length < sizeof(*ev)) { btd_error(adapter->dev_id, "Too small discovering event"); return; } DBG("hci%u type %u discovering %u method %d", adapter->dev_id, ev->type, ev->discovering, adapter->filtered_discovery); if (adapter->discovery_enable == ev->discovering) return; adapter->discovery_type = ev->type; adapter->discovery_enable = ev->discovering; /* * Check for existing discoveries triggered by client applications * and ignore all others. * * If there are no clients, then it is good idea to trigger a * passive scanning attempt. */ if (!adapter->discovery_list) { if (!adapter->connect_le) trigger_passive_scanning(adapter); return; } if (adapter->discovery_suspended) return; switch (adapter->discovery_enable) { case 0x00: if (adapter->no_scan_restart_delay) trigger_start_discovery(adapter, 0); else trigger_start_discovery(adapter, IDLE_DISCOV_TIMEOUT); break; case 0x01: if (adapter->discovery_idle_timeout > 0) { timeout_remove(adapter->discovery_idle_timeout); adapter->discovery_idle_timeout = 0; } break; } } static bool set_discovery_discoverable(struct btd_adapter *adapter, bool enable) { if (adapter->discovery_discoverable == enable) return true; /* Reset discoverable filter if already set */ if (enable && (adapter->current_settings & MGMT_SETTING_DISCOVERABLE)) return true; adapter->discovery_discoverable = enable; return set_discoverable(adapter, enable, 0); } static void stop_discovery_complete(uint8_t status, uint16_t length, const void *param, void *user_data) { struct btd_adapter *adapter = user_data; struct discovery_client *client; DBG("status 0x%02x", status); client = discovery_complete(adapter, status); if (client) discovery_remove(client); if (status != MGMT_STATUS_SUCCESS) return; adapter->discovery_type = 0x00; adapter->discovery_enable = 0x00; adapter->filtered_discovery = false; adapter->no_scan_restart_delay = false; adapter->discovering = false; g_dbus_emit_property_changed(dbus_conn, adapter->path, ADAPTER_INTERFACE, "Discovering"); trigger_passive_scanning(adapter); } static int compare_sender(gconstpointer a, gconstpointer b) { const struct discovery_client *client = a; const char *sender = b; return g_strcmp0(client->owner, sender); } static gint g_strcmp(gconstpointer a, gconstpointer b) { return strcmp(a, b); } static void extract_unique_uuids(gpointer data, gpointer user_data) { char *uuid_str = data; GSList **uuids = user_data; if (!g_slist_find_custom(*uuids, uuid_str, g_strcmp)) *uuids = g_slist_insert_sorted(*uuids, uuid_str, g_strcmp); } /* * This method merges all adapter filters into rssi, transport and uuids. * Returns 1 if there was no filtered scan, 0 otherwise. */ static int merge_discovery_filters(struct btd_adapter *adapter, int *rssi, uint8_t *transport, GSList **uuids) { GSList *l; bool empty_uuid = false; bool has_regular_discovery = false; bool has_filtered_discovery = false; uint8_t adapter_scan_type = get_scan_type(adapter); for (l = adapter->discovery_list; l != NULL; l = g_slist_next(l)) { struct discovery_client *client = l->data; struct discovery_filter *item = client->discovery_filter; if (!item) { has_regular_discovery = true; continue; } /* * Detect empty filter with only discoverable * (which does not require a kernel filter) set. */ if (item->uuids == NULL && item->pathloss == DISTANCE_VAL_INVALID && item->rssi == DISTANCE_VAL_INVALID && item->type == adapter_scan_type && item->duplicate == false && item->pattern == NULL) { has_regular_discovery = true; continue; } has_filtered_discovery = true; *transport |= item->type; /* * Rule for merging rssi and pathloss into rssi field of kernel * filter is as follow: * - if there's any client without proximity filter, then do no * proximity filtering, * - if all clients specified RSSI, then use lowest value, * - if any client specified pathloss, then kernel filter should * do no proximity, as kernel can't compute pathloss. We'll do * filtering on our own. */ if (item->rssi == DISTANCE_VAL_INVALID) *rssi = HCI_RSSI_INVALID; else if (*rssi != HCI_RSSI_INVALID && *rssi >= item->rssi) *rssi = item->rssi; else if (item->pathloss != DISTANCE_VAL_INVALID) *rssi = HCI_RSSI_INVALID; if (!g_slist_length(item->uuids)) empty_uuid = true; g_slist_foreach(item->uuids, extract_unique_uuids, uuids); } /* If no proximity filtering is set, disable it */ if (*rssi == DISTANCE_VAL_INVALID) *rssi = HCI_RSSI_INVALID; /* * Empty_uuid variable determines wether there was any filter with no * uuids. In this case someone might be looking for all devices in * certain proximity, and we need to have empty uuids in kernel filter. */ if (empty_uuid) { g_slist_free(*uuids); *uuids = NULL; } if (has_regular_discovery) { if (!has_filtered_discovery) return 1; /* * It there is both regular and filtered scan running, then * clear whole fitler to report all devices. */ *transport = adapter_scan_type; *rssi = HCI_RSSI_INVALID; g_slist_free(*uuids); *uuids = NULL; } return 0; } static void populate_mgmt_filter_uuids(uint8_t (*mgmt_uuids)[16], GSList *uuids) { GSList *l; for (l = uuids; l != NULL; l = g_slist_next(l)) { bt_uuid_t uuid, u128; uint128_t uint128; bt_string_to_uuid(&uuid, l->data); bt_uuid_to_uuid128(&uuid, &u128); ntoh128((uint128_t *) u128.value.u128.data, &uint128); htob128(&uint128, (uint128_t *) mgmt_uuids); mgmt_uuids++; } } /* * This method merges all adapter filters into one that will be send to kernel. * cp_ptr is set to null when regular non-filtered discovery is needed, * otherwise it's pointing to filter. Returns 0 on succes, -1 on error */ static int discovery_filter_to_mgmt_cp(struct btd_adapter *adapter, struct mgmt_cp_start_service_discovery **cp_ptr) { GSList *uuids = NULL; struct mgmt_cp_start_service_discovery *cp; int rssi = DISTANCE_VAL_INVALID; int uuid_count; uint8_t discovery_type = 0; DBG(""); if (merge_discovery_filters(adapter, &rssi, &discovery_type, &uuids)) { /* There are only regular scans, run just regular scan. */ *cp_ptr = NULL; return 0; } uuid_count = g_slist_length(uuids); cp = g_try_malloc(sizeof(*cp) + 16*uuid_count); *cp_ptr = cp; if (!cp) { g_slist_free(uuids); return -1; } cp->type = discovery_type; cp->rssi = rssi; cp->uuid_count = htobs(uuid_count); populate_mgmt_filter_uuids(cp->uuids, uuids); g_slist_free(uuids); return 0; } static bool filters_equal(struct mgmt_cp_start_service_discovery *a, struct mgmt_cp_start_service_discovery *b) { if (!a && !b) return true; if ((!a && b) || (a && !b)) return false; if (a->type != b->type) return false; if (a->rssi != b->rssi) return false; /* * When we create mgmt_cp_start_service_discovery structure inside * discovery_filter_to_mgmt_cp, we always keep uuids sorted, and * unique, so we're safe to compare uuid_count, and uuids like that. */ if (a->uuid_count != b->uuid_count) return false; if (memcmp(a->uuids, b->uuids, 16 * a->uuid_count) != 0) return false; return true; } static int update_discovery_filter(struct btd_adapter *adapter) { struct mgmt_cp_start_service_discovery *sd_cp; DBG(""); if (discovery_filter_to_mgmt_cp(adapter, &sd_cp)) { btd_error(adapter->dev_id, "discovery_filter_to_mgmt_cp returned error"); return -ENOMEM; } /* Only attempt to overwrite current discoverable setting when not * discoverable. */ if (!(adapter->current_settings & MGMT_SETTING_DISCOVERABLE)) { GSList *l; for (l = adapter->discovery_list; l; l = g_slist_next(l)) { struct discovery_client *client = l->data; if (!client->discovery_filter) continue; if (client->discovery_filter->discoverable) { set_discovery_discoverable(adapter, true); break; } } } /* * If filters are equal, then don't update scan, except for when * starting discovery. */ if (filters_equal(adapter->current_discovery_filter, sd_cp) && adapter->discovering != false) { DBG("filters were equal, deciding to not restart the scan."); g_free(sd_cp); return 0; } g_free(adapter->current_discovery_filter); adapter->current_discovery_filter = sd_cp; trigger_start_discovery(adapter, 0); return -EINPROGRESS; } static int discovery_stop(struct discovery_client *client) { struct btd_adapter *adapter = client->adapter; struct mgmt_cp_stop_discovery cp; /* Check if there are more client discovering */ if (g_slist_next(adapter->discovery_list)) { discovery_remove(client); update_discovery_filter(adapter); return 0; } set_discovery_discoverable(adapter, false); /* * In the idle phase of a discovery, there is no need to stop it * and so it is enough to send out the signal and just return. */ if (adapter->discovery_enable == 0x00) { discovery_remove(client); adapter->discovering = false; g_dbus_emit_property_changed(dbus_conn, adapter->path, ADAPTER_INTERFACE, "Discovering"); trigger_passive_scanning(adapter); return 0; } cp.type = adapter->discovery_type; adapter->client = client; mgmt_send(adapter->mgmt, MGMT_OP_STOP_DISCOVERY, adapter->dev_id, sizeof(cp), &cp, stop_discovery_complete, adapter, NULL); return -EINPROGRESS; } static void discovery_disconnect(DBusConnection *conn, void *user_data) { struct discovery_client *client = user_data; DBG("owner %s", client->owner); client->watch = 0; discovery_stop(client); } /* * Returns true if client was already discovering, false otherwise. *client * will point to discovering client, or client that have pre-set his filter. */ static bool get_discovery_client(struct btd_adapter *adapter, const char *owner, struct discovery_client **client) { GSList *list = g_slist_find_custom(adapter->discovery_list, owner, compare_sender); if (list) { *client = list->data; return true; } list = g_slist_find_custom(adapter->set_filter_list, owner, compare_sender); if (list) { *client = list->data; return false; } *client = NULL; return false; } static DBusMessage *start_discovery(DBusConnection *conn, DBusMessage *msg, void *user_data) { struct btd_adapter *adapter = user_data; const char *sender = dbus_message_get_sender(msg); struct discovery_client *client; bool is_discovering; int err; DBG("sender %s", sender); if (!btd_adapter_get_powered(adapter)) return btd_error_not_ready(msg); is_discovering = get_discovery_client(adapter, sender, &client); /* * Every client can only start one discovery, if the client * already started a discovery then return an error. */ if (is_discovering) return btd_error_busy(msg); /* * If there was pre-set filter, just reconnect it to discovery_list, * and trigger scan. */ if (client) { if (client->msg) return btd_error_busy(msg); adapter->set_filter_list = g_slist_remove( adapter->set_filter_list, client); adapter->discovery_list = g_slist_prepend( adapter->discovery_list, client); goto done; } client = g_new0(struct discovery_client, 1); client->adapter = adapter; client->owner = g_strdup(sender); client->discovery_filter = NULL; client->watch = g_dbus_add_disconnect_watch(dbus_conn, sender, discovery_disconnect, client, NULL); adapter->discovery_list = g_slist_prepend(adapter->discovery_list, client); done: /* * Just trigger the discovery here. In case an already running * discovery in idle phase exists, it will be restarted right * away. */ err = update_discovery_filter(adapter); if (!err) return dbus_message_new_method_return(msg); /* If the discovery has to be started wait it complete to reply */ if (err == -EINPROGRESS) { client->msg = dbus_message_ref(msg); adapter->client = client; return NULL; } return btd_error_failed(msg, strerror(-err)); } static bool parse_uuids(DBusMessageIter *value, struct discovery_filter *filter) { DBusMessageIter arriter; if (dbus_message_iter_get_arg_type(value) != DBUS_TYPE_ARRAY) return false; dbus_message_iter_recurse(value, &arriter); while (dbus_message_iter_get_arg_type(&arriter) != DBUS_TYPE_INVALID) { bt_uuid_t uuid, u128; char uuidstr[MAX_LEN_UUID_STR + 1]; char *uuid_param; if (dbus_message_iter_get_arg_type(&arriter) != DBUS_TYPE_STRING) return false; dbus_message_iter_get_basic(&arriter, &uuid_param); if (bt_string_to_uuid(&uuid, uuid_param)) return false; bt_uuid_to_uuid128(&uuid, &u128); bt_uuid_to_string(&u128, uuidstr, sizeof(uuidstr)); filter->uuids = g_slist_prepend(filter->uuids, strdup(uuidstr)); dbus_message_iter_next(&arriter); } return true; } static bool parse_rssi(DBusMessageIter *value, struct discovery_filter *filter) { if (dbus_message_iter_get_arg_type(value) != DBUS_TYPE_INT16) return false; dbus_message_iter_get_basic(value, &filter->rssi); /* -127 <= RSSI <= +20 (spec V4.2 [Vol 2, Part E] 7.7.65.2) */ if (filter->rssi > 20 || filter->rssi < -127) return false; return true; } static bool parse_pathloss(DBusMessageIter *value, struct discovery_filter *filter) { if (dbus_message_iter_get_arg_type(value) != DBUS_TYPE_UINT16) return false; dbus_message_iter_get_basic(value, &filter->pathloss); /* pathloss filter must be smaller that PATHLOSS_MAX */ if (filter->pathloss > PATHLOSS_MAX) return false; return true; } static bool parse_transport(DBusMessageIter *value, struct discovery_filter *filter) { char *transport_str; if (dbus_message_iter_get_arg_type(value) != DBUS_TYPE_STRING) return false; dbus_message_iter_get_basic(value, &transport_str); if (!strcmp(transport_str, "bredr")) filter->type = SCAN_TYPE_BREDR; else if (!strcmp(transport_str, "le")) filter->type = SCAN_TYPE_LE; else if (strcmp(transport_str, "auto")) return false; return true; } static bool parse_duplicate_data(DBusMessageIter *value, struct discovery_filter *filter) { dbus_bool_t duplicate = false; if (dbus_message_iter_get_arg_type(value) != DBUS_TYPE_BOOLEAN) return false; dbus_message_iter_get_basic(value, &duplicate); filter->duplicate = duplicate; return true; } static bool parse_discoverable(DBusMessageIter *value, struct discovery_filter *filter) { dbus_bool_t discoverable = false; if (dbus_message_iter_get_arg_type(value) != DBUS_TYPE_BOOLEAN) return false; dbus_message_iter_get_basic(value, &discoverable); filter->discoverable = discoverable; return true; } static bool parse_pattern(DBusMessageIter *value, struct discovery_filter *filter) { const char *pattern; if (dbus_message_iter_get_arg_type(value) != DBUS_TYPE_STRING) return false; dbus_message_iter_get_basic(value, &pattern); free(filter->pattern); filter->pattern = strdup(pattern); return true; } struct filter_parser { const char *name; bool (*func)(DBusMessageIter *iter, struct discovery_filter *filter); } parsers[] = { { "UUIDs", parse_uuids }, { "RSSI", parse_rssi }, { "Pathloss", parse_pathloss }, { "Transport", parse_transport }, { "DuplicateData", parse_duplicate_data }, { "Discoverable", parse_discoverable }, { "Pattern", parse_pattern }, { } }; static bool parse_discovery_filter_entry(char *key, DBusMessageIter *value, struct discovery_filter *filter) { struct filter_parser *parser; for (parser = parsers; parser && parser->name; parser++) { if (!strcmp(parser->name, key)) return parser->func(value, filter); } DBG("Unknown key parameter: %s!\n", key); return false; } /* * This method is responsible for parsing parameters to SetDiscoveryFilter. If * filter in msg was empty, sets *filter to NULL. If whole parsing was * successful, sets *filter to proper value. * Returns false on any error, and true on success. */ static bool parse_discovery_filter_dict(struct btd_adapter *adapter, struct discovery_filter **filter, DBusMessage *msg) { DBusMessageIter iter, subiter, dictiter, variantiter; bool is_empty = true; *filter = g_try_malloc(sizeof(**filter)); if (!*filter) return false; (*filter)->uuids = NULL; (*filter)->pathloss = DISTANCE_VAL_INVALID; (*filter)->rssi = DISTANCE_VAL_INVALID; (*filter)->type = get_scan_type(adapter); (*filter)->duplicate = false; (*filter)->discoverable = false; (*filter)->pattern = NULL; dbus_message_iter_init(msg, &iter); if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_ARRAY || dbus_message_iter_get_element_type(&iter) != DBUS_TYPE_DICT_ENTRY) goto invalid_args; dbus_message_iter_recurse(&iter, &subiter); do { int type = dbus_message_iter_get_arg_type(&subiter); char *key; if (type == DBUS_TYPE_INVALID) break; is_empty = false; dbus_message_iter_recurse(&subiter, &dictiter); dbus_message_iter_get_basic(&dictiter, &key); if (!dbus_message_iter_next(&dictiter)) goto invalid_args; if (dbus_message_iter_get_arg_type(&dictiter) != DBUS_TYPE_VARIANT) goto invalid_args; dbus_message_iter_recurse(&dictiter, &variantiter); if (!parse_discovery_filter_entry(key, &variantiter, *filter)) goto invalid_args; dbus_message_iter_next(&subiter); } while (true); if (is_empty) { g_free(*filter); *filter = NULL; return true; } /* only pathlos or rssi can be set, never both */ if ((*filter)->pathloss != DISTANCE_VAL_INVALID && (*filter)->rssi != DISTANCE_VAL_INVALID) goto invalid_args; DBG("filtered discovery params: transport: %d rssi: %d pathloss: %d " " duplicate data: %s discoverable %s pattern %s", (*filter)->type, (*filter)->rssi, (*filter)->pathloss, (*filter)->duplicate ? "true" : "false", (*filter)->discoverable ? "true" : "false", (*filter)->pattern); return true; invalid_args: g_slist_free_full((*filter)->uuids, g_free); g_free(*filter); *filter = NULL; return false; } static DBusMessage *set_discovery_filter(DBusConnection *conn, DBusMessage *msg, void *user_data) { struct btd_adapter *adapter = user_data; struct discovery_client *client; struct discovery_filter *discovery_filter; const char *sender = dbus_message_get_sender(msg); bool is_discovering; DBG("sender %s", sender); if (!btd_adapter_get_powered(adapter)) return btd_error_not_ready(msg); if (MGMT_VERSION(mgmt_version, mgmt_revision) < MGMT_VERSION(1, 8)) return btd_error_not_supported(msg); /* parse parameters */ if (!parse_discovery_filter_dict(adapter, &discovery_filter, msg)) return btd_error_invalid_args(msg); is_discovering = get_discovery_client(adapter, sender, &client); if (client) { free_discovery_filter(client->discovery_filter); client->discovery_filter = discovery_filter; if (is_discovering) update_discovery_filter(adapter); if (discovery_filter || is_discovering) return dbus_message_new_method_return(msg); /* Removing pre-set filter */ adapter->set_filter_list = g_slist_remove( adapter->set_filter_list, client); discovery_free(client); DBG("successfully cleared pre-set filter"); } else if (discovery_filter) { /* Client pre-setting his filter for first time */ client = g_new0(struct discovery_client, 1); client->adapter = adapter; client->owner = g_strdup(sender); client->discovery_filter = discovery_filter; client->watch = g_dbus_add_disconnect_watch(dbus_conn, sender, discovery_disconnect, client, NULL); adapter->set_filter_list = g_slist_prepend( adapter->set_filter_list, client); DBG("successfully pre-set filter"); } return dbus_message_new_method_return(msg); } static DBusMessage *stop_discovery(DBusConnection *conn, DBusMessage *msg, void *user_data) { struct btd_adapter *adapter = user_data; const char *sender = dbus_message_get_sender(msg); struct discovery_client *client; GSList *list; int err; DBG("sender %s", sender); if (!btd_adapter_get_powered(adapter)) return btd_error_not_ready(msg); list = g_slist_find_custom(adapter->discovery_list, sender, compare_sender); if (!list) return btd_error_failed(msg, "No discovery started"); client = list->data; if (client->msg) return btd_error_busy(msg); err = discovery_stop(client); switch (err) { case 0: return dbus_message_new_method_return(msg); case -EINPROGRESS: client->msg = dbus_message_ref(msg); adapter->client = client; return NULL; default: return btd_error_failed(msg, strerror(-err)); } } static gboolean property_get_address(const GDBusPropertyTable *property, DBusMessageIter *iter, void *user_data) { struct btd_adapter *adapter = user_data; char addr[18]; const char *str = addr; ba2str(&adapter->bdaddr, addr); dbus_message_iter_append_basic(iter, DBUS_TYPE_STRING, &str); return TRUE; } static gboolean property_get_address_type(const GDBusPropertyTable *property, DBusMessageIter *iter, void *user_data) { struct btd_adapter *adapter = user_data; const char *str; if ((adapter->current_settings & MGMT_SETTING_LE) && (adapter->bdaddr_type == BDADDR_LE_RANDOM)) str = "random"; else str = "public"; dbus_message_iter_append_basic(iter, DBUS_TYPE_STRING, &str); return TRUE; } static gboolean property_get_name(const GDBusPropertyTable *property, DBusMessageIter *iter, void *user_data) { struct btd_adapter *adapter = user_data; const char *str = adapter->system_name ? : ""; dbus_message_iter_append_basic(iter, DBUS_TYPE_STRING, &str); return TRUE; } static gboolean property_get_alias(const GDBusPropertyTable *property, DBusMessageIter *iter, void *user_data) { struct btd_adapter *adapter = user_data; const char *str; if (adapter->current_alias) str = adapter->current_alias; else if (adapter->stored_alias) str = adapter->stored_alias; else str = adapter->system_name ? : ""; dbus_message_iter_append_basic(iter, DBUS_TYPE_STRING, &str); return TRUE; } static void property_set_alias(const GDBusPropertyTable *property, DBusMessageIter *iter, GDBusPendingPropertySet id, void *user_data) { struct btd_adapter *adapter = user_data; const char *name; int ret; dbus_message_iter_get_basic(iter, &name); if (g_str_equal(name, "") == TRUE) { if (adapter->stored_alias == NULL) { /* no alias set, nothing to restore */ g_dbus_pending_property_success(id); return; } /* restore to system name */ ret = set_name(adapter, adapter->system_name); } else { if (g_strcmp0(adapter->stored_alias, name) == 0) { /* alias already set, nothing to do */ g_dbus_pending_property_success(id); return; } /* set to alias */ ret = set_name(adapter, name); } if (ret >= 0) { g_free(adapter->stored_alias); if (g_str_equal(name, "") == TRUE) adapter->stored_alias = NULL; else adapter->stored_alias = g_strdup(name); store_adapter_info(adapter); g_dbus_pending_property_success(id); return; } if (ret == -EINVAL) g_dbus_pending_property_error(id, ERROR_INTERFACE ".InvalidArguments", "Invalid arguments in method call"); else g_dbus_pending_property_error(id, ERROR_INTERFACE ".Failed", strerror(-ret)); } static gboolean property_get_class(const GDBusPropertyTable *property, DBusMessageIter *iter, void *user_data) { struct btd_adapter *adapter = user_data; dbus_uint32_t val = adapter->dev_class; dbus_message_iter_append_basic(iter, DBUS_TYPE_UINT32, &val); return TRUE; } static gboolean property_get_mode(struct btd_adapter *adapter, uint32_t setting, DBusMessageIter *iter) { dbus_bool_t enable; enable = (adapter->current_settings & setting) ? TRUE : FALSE; dbus_message_iter_append_basic(iter, DBUS_TYPE_BOOLEAN, &enable); return TRUE; } struct property_set_data { struct btd_adapter *adapter; uint32_t setting; GDBusPendingPropertySet id; uint8_t value; }; static void property_set_mode_complete(uint8_t status, uint16_t length, const void *param, void *user_data) { struct property_set_data *data = user_data; struct btd_adapter *adapter = data->adapter; DBG("%s (0x%02x)", mgmt_errstr(status), status); if (status != MGMT_STATUS_SUCCESS) { const char *dbus_err; btd_error(adapter->dev_id, "Failed to set mode: %s (0x%02x)", mgmt_errstr(status), status); if (status == MGMT_STATUS_RFKILLED) { dbus_err = ERROR_INTERFACE ".Blocked"; adapter_set_power_state(adapter, ADAPTER_POWER_STATE_OFF_BLOCKED); } else { dbus_err = ERROR_INTERFACE ".Failed"; } g_dbus_pending_property_error(data->id, dbus_err, mgmt_errstr(status)); adapter->pending_settings &= ~data->setting; if (status != MGMT_STATUS_RFKILLED && data->setting & MGMT_SETTING_POWERED) reset_power_state_target(adapter, data->value); return; } g_dbus_pending_property_success(data->id); /* * The parameters are identical and also the task that is * required in both cases. So it is safe to just call the * event handling functions here. */ new_settings_callback(adapter->dev_id, length, param, adapter); } static void clear_discoverable(struct btd_adapter *adapter) { if (!btd_has_kernel_features(KERNEL_CONN_CONTROL)) return; if (!(adapter->current_settings & MGMT_SETTING_DISCOVERABLE)) return; /* If no timeout is set do nothing as both connectable and discoverable * flags are persistent on power toggle. */ if (!adapter->discoverable_timeout) return; /* If timeout was set kernel clears discoverable on its own when * powering off controller. This would leave connectable flag set * after power on. * * With kernel control clearing connectable clear also discoverable * flag so we need to clear connectable. */ set_mode(adapter, MGMT_OP_SET_CONNECTABLE, 0x00); } static void property_set_mode(struct btd_adapter *adapter, uint32_t setting, DBusMessageIter *value, GDBusPendingPropertySet id) { struct property_set_data *data; struct mgmt_cp_set_discoverable cp; void *param; dbus_bool_t enable, current_enable; uint16_t opcode, len; uint8_t mode; dbus_message_iter_get_basic(value, &enable); if (adapter->pending_settings & setting) { g_dbus_pending_property_error(id, ERROR_INTERFACE ".Busy", NULL); return; } if (adapter->current_settings & setting) current_enable = TRUE; else current_enable = FALSE; if (enable == current_enable) { g_dbus_pending_property_success(id); return; } mode = (enable == TRUE) ? 0x01 : 0x00; switch (setting) { case MGMT_SETTING_POWERED: opcode = MGMT_OP_SET_POWERED; param = &mode; len = sizeof(mode); if (!mode) { btd_adv_monitor_power_down( adapter->adv_monitor_manager); clear_discoverable(adapter); remove_temporary_devices(adapter); } break; case MGMT_SETTING_DISCOVERABLE: if (btd_has_kernel_features(KERNEL_CONN_CONTROL)) { if (mode) { set_mode(adapter, MGMT_OP_SET_CONNECTABLE, mode); } else { opcode = MGMT_OP_SET_CONNECTABLE; param = &mode; len = sizeof(mode); break; } } memset(&cp, 0, sizeof(cp)); cp.val = mode; if (cp.val) cp.timeout = htobs(adapter->discoverable_timeout); opcode = MGMT_OP_SET_DISCOVERABLE; param = &cp; len = sizeof(cp); break; case MGMT_SETTING_BONDABLE: opcode = MGMT_OP_SET_BONDABLE; param = &mode; len = sizeof(mode); break; case MGMT_SETTING_CONNECTABLE: opcode = MGMT_OP_SET_CONNECTABLE; param = &mode; len = sizeof(mode); break; default: goto failed; } DBG("sending %s command for index %u", mgmt_opstr(opcode), adapter->dev_id); data = g_try_new0(struct property_set_data, 1); if (!data) goto failed; data->adapter = adapter; data->setting = setting; data->id = id; data->setting = setting; data->value = mode; if (setting == MGMT_SETTING_POWERED && adapter->power_state != ADAPTER_POWER_STATE_OFF_BLOCKED) { adapter_set_power_state(adapter, mode ? ADAPTER_POWER_STATE_OFF_ENABLING : ADAPTER_POWER_STATE_ON_DISABLING); } if (mgmt_send(adapter->mgmt, opcode, adapter->dev_id, len, param, property_set_mode_complete, data, g_free) > 0) { adapter->pending_settings |= setting; return; } g_free(data); if (setting == MGMT_SETTING_POWERED) { /* cancel the earlier setting */ adapter_set_power_state(adapter, mode ? ADAPTER_POWER_STATE_OFF : ADAPTER_POWER_STATE_ON); } failed: btd_error(adapter->dev_id, "Failed to set mode for index %u", adapter->dev_id); g_dbus_pending_property_error(id, ERROR_INTERFACE ".Failed", NULL); } static gboolean property_get_powered(const GDBusPropertyTable *property, DBusMessageIter *iter, void *user_data) { struct btd_adapter *adapter = user_data; return property_get_mode(adapter, MGMT_SETTING_POWERED, iter); } static void property_set_powered(const GDBusPropertyTable *property, DBusMessageIter *iter, GDBusPendingPropertySet id, void *user_data) { struct btd_adapter *adapter = user_data; if (powering_down) { g_dbus_pending_property_error(id, ERROR_INTERFACE ".Failed", "Powering down"); return; } property_set_mode(adapter, MGMT_SETTING_POWERED, iter, id); } static gboolean property_get_power_state(const GDBusPropertyTable *property, DBusMessageIter *iter, void *user_data) { struct btd_adapter *adapter = user_data; const char *str; str = adapter_power_state_str(adapter->power_state); dbus_message_iter_append_basic(iter, DBUS_TYPE_STRING, &str); return TRUE; } static gboolean property_get_discoverable(const GDBusPropertyTable *property, DBusMessageIter *iter, void *user_data) { struct btd_adapter *adapter = user_data; return property_get_mode(adapter, MGMT_SETTING_DISCOVERABLE, iter); } static void property_set_discoverable(const GDBusPropertyTable *property, DBusMessageIter *iter, GDBusPendingPropertySet id, void *user_data) { struct btd_adapter *adapter = user_data; if (adapter->discoverable_timeout > 0 && !btd_adapter_get_powered(adapter)) { g_dbus_pending_property_error(id, ERROR_INTERFACE ".Failed", "Not Powered"); return; } /* Reset discovery_discoverable as Discoverable takes precedence */ adapter->discovery_discoverable = false; property_set_mode(adapter, MGMT_SETTING_DISCOVERABLE, iter, id); } static gboolean property_get_discoverable_timeout( const GDBusPropertyTable *property, DBusMessageIter *iter, void *user_data) { struct btd_adapter *adapter = user_data; dbus_uint32_t value = adapter->discoverable_timeout; dbus_message_iter_append_basic(iter, DBUS_TYPE_UINT32, &value); return TRUE; } static void property_set_discoverable_timeout( const GDBusPropertyTable *property, DBusMessageIter *iter, GDBusPendingPropertySet id, void *user_data) { struct btd_adapter *adapter = user_data; bool enabled; dbus_uint32_t value; dbus_message_iter_get_basic(iter, &value); adapter->discoverable_timeout = value; g_dbus_pending_property_success(id); store_adapter_info(adapter); g_dbus_emit_property_changed(dbus_conn, adapter->path, ADAPTER_INTERFACE, "DiscoverableTimeout"); if (adapter->pending_settings & MGMT_SETTING_DISCOVERABLE) { if (adapter->current_settings & MGMT_SETTING_DISCOVERABLE) enabled = false; else enabled = true; } else { if (adapter->current_settings & MGMT_SETTING_DISCOVERABLE) enabled = true; else enabled = false; } if (enabled) set_discoverable(adapter, 0x01, adapter->discoverable_timeout); } static gboolean property_get_pairable(const GDBusPropertyTable *property, DBusMessageIter *iter, void *user_data) { struct btd_adapter *adapter = user_data; return property_get_mode(adapter, MGMT_SETTING_BONDABLE, iter); } static void property_set_pairable(const GDBusPropertyTable *property, DBusMessageIter *iter, GDBusPendingPropertySet id, void *user_data) { struct btd_adapter *adapter = user_data; property_set_mode(adapter, MGMT_SETTING_BONDABLE, iter, id); } static gboolean property_get_pairable_timeout( const GDBusPropertyTable *property, DBusMessageIter *iter, void *user_data) { struct btd_adapter *adapter = user_data; dbus_uint32_t value = adapter->pairable_timeout; dbus_message_iter_append_basic(iter, DBUS_TYPE_UINT32, &value); return TRUE; } static void property_set_pairable_timeout(const GDBusPropertyTable *property, DBusMessageIter *iter, GDBusPendingPropertySet id, void *user_data) { struct btd_adapter *adapter = user_data; dbus_uint32_t value; dbus_message_iter_get_basic(iter, &value); adapter->pairable_timeout = value; g_dbus_pending_property_success(id); store_adapter_info(adapter); g_dbus_emit_property_changed(dbus_conn, adapter->path, ADAPTER_INTERFACE, "PairableTimeout"); trigger_pairable_timeout(adapter); } static gboolean property_get_discovering(const GDBusPropertyTable *property, DBusMessageIter *iter, void *user_data) { struct btd_adapter *adapter = user_data; dbus_bool_t discovering = adapter->discovering; dbus_message_iter_append_basic(iter, DBUS_TYPE_BOOLEAN, &discovering); return TRUE; } static void add_gatt_uuid(struct gatt_db_attribute *attrib, void *user_data) { GHashTable *uuids = user_data; bt_uuid_t uuid, u128; char uuidstr[MAX_LEN_UUID_STR + 1]; if (!gatt_db_service_get_active(attrib)) return; if (!gatt_db_attribute_get_service_uuid(attrib, &uuid)) return; bt_uuid_to_uuid128(&uuid, &u128); bt_uuid_to_string(&u128, uuidstr, sizeof(uuidstr)); g_hash_table_add(uuids, strdup(uuidstr)); } static void iter_append_uuid(gpointer key, gpointer value, gpointer user_data) { DBusMessageIter *iter = user_data; const char *uuid = key; dbus_message_iter_append_basic(iter, DBUS_TYPE_STRING, &uuid); } static gboolean property_get_uuids(const GDBusPropertyTable *property, DBusMessageIter *iter, void *user_data) { struct btd_adapter *adapter = user_data; DBusMessageIter entry; sdp_list_t *l; struct gatt_db *db; GHashTable *uuids; uuids = g_hash_table_new_full(g_str_hash, g_str_equal, free, NULL); if (!uuids) return FALSE; /* SDP records */ for (l = adapter->services; l != NULL; l = l->next) { sdp_record_t *rec = l->data; char *uuid; uuid = bt_uuid2string(&rec->svclass); if (uuid == NULL) continue; g_hash_table_add(uuids, uuid); } /* GATT services */ db = btd_gatt_database_get_db(adapter->database); if (db) gatt_db_foreach_service(db, NULL, add_gatt_uuid, uuids); dbus_message_iter_open_container(iter, DBUS_TYPE_ARRAY, DBUS_TYPE_STRING_AS_STRING, &entry); g_hash_table_foreach(uuids, iter_append_uuid, &entry); dbus_message_iter_close_container(iter, &entry); g_hash_table_destroy(uuids); return TRUE; } static gboolean property_exists_modalias(const GDBusPropertyTable *property, void *user_data) { struct btd_adapter *adapter = user_data; return adapter->modalias ? TRUE : FALSE; } static gboolean property_get_modalias(const GDBusPropertyTable *property, DBusMessageIter *iter, void *user_data) { struct btd_adapter *adapter = user_data; const char *str = adapter->modalias ? : ""; dbus_message_iter_append_basic(iter, DBUS_TYPE_STRING, &str); return TRUE; } static gboolean property_get_roles(const GDBusPropertyTable *property, DBusMessageIter *iter, void *user_data) { struct btd_adapter *adapter = user_data; DBusMessageIter entry; dbus_message_iter_open_container(iter, DBUS_TYPE_ARRAY, DBUS_TYPE_STRING_AS_STRING, &entry); if (adapter->supported_settings & MGMT_SETTING_LE) { const char *str = "central"; dbus_message_iter_append_basic(&entry, DBUS_TYPE_STRING, &str); } if (adapter->supported_settings & MGMT_SETTING_ADVERTISING) { const char *str = "peripheral"; dbus_message_iter_append_basic(&entry, DBUS_TYPE_STRING, &str); } if (queue_find(adapter->exps, NULL, le_simult_central_peripheral_uuid.val)) { const char *str = "central-peripheral"; dbus_message_iter_append_basic(&entry, DBUS_TYPE_STRING, &str); } dbus_message_iter_close_container(iter, &entry); return TRUE; } static void property_append_experimental(void *data, void *user_data) { uint8_t *feature = data; DBusMessageIter *iter = user_data; uint128_t value; bt_uuid_t uuid; char str[MAX_LEN_UUID_STR + 1]; char *ptr; bswap_128(feature, &value); bt_uuid128_create(&uuid, value); bt_uuid_to_string(&uuid, str, sizeof(str)); ptr = str; dbus_message_iter_append_basic(iter, DBUS_TYPE_STRING, &ptr); } static gboolean property_get_experimental(const GDBusPropertyTable *property, DBusMessageIter *iter, void *user_data) { struct btd_adapter *adapter = user_data; DBusMessageIter entry; dbus_message_iter_open_container(iter, DBUS_TYPE_ARRAY, DBUS_TYPE_STRING_AS_STRING, &entry); queue_foreach(adapter->exps, property_append_experimental, &entry); dbus_message_iter_close_container(iter, &entry); return TRUE; } static gboolean property_experimental_exists(const GDBusPropertyTable *property, void *data) { struct btd_adapter *adapter = data; return !queue_isempty(adapter->exps); } static gboolean property_get_manufacturer(const GDBusPropertyTable *property, DBusMessageIter *iter, void *user_data) { struct btd_adapter *adapter = user_data; dbus_uint16_t val = adapter->manufacturer; dbus_message_iter_append_basic(iter, DBUS_TYPE_UINT16, &val); return TRUE; } static gboolean property_get_version(const GDBusPropertyTable *property, DBusMessageIter *iter, void *user_data) { struct btd_adapter *adapter = user_data; uint8_t val = adapter->version; dbus_message_iter_append_basic(iter, DBUS_TYPE_BYTE, &val); return TRUE; } static gboolean property_get_connectable(const GDBusPropertyTable *property, DBusMessageIter *iter, void *user_data) { struct btd_adapter *adapter = user_data; return property_get_mode(adapter, MGMT_SETTING_CONNECTABLE, iter); } static void property_set_connectable(const GDBusPropertyTable *property, DBusMessageIter *iter, GDBusPendingPropertySet id, void *user_data) { struct btd_adapter *adapter = user_data; property_set_mode(adapter, MGMT_SETTING_CONNECTABLE, iter, id); } static DBusMessage *remove_device(DBusConnection *conn, DBusMessage *msg, void *user_data) { struct btd_adapter *adapter = user_data; struct btd_device *device; const char *path; GSList *list; if (dbus_message_get_args(msg, NULL, DBUS_TYPE_OBJECT_PATH, &path, DBUS_TYPE_INVALID) == FALSE) return btd_error_invalid_args(msg); list = g_slist_find_custom(adapter->devices, path, device_path_cmp); if (!list) return btd_error_does_not_exist(msg); if (!btd_adapter_get_powered(adapter)) return btd_error_not_ready(msg); device = list->data; btd_device_set_temporary(device, true); if (!btd_device_is_connected(device)) { btd_adapter_remove_device(adapter, device); return dbus_message_new_method_return(msg); } device_request_disconnect(device, msg); return NULL; } static DBusMessage *get_discovery_filters(DBusConnection *conn, DBusMessage *msg, void *user_data) { DBusMessage *reply; DBusMessageIter iter, array; struct filter_parser *parser; reply = dbus_message_new_method_return(msg); dbus_message_iter_init_append(reply, &iter); dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY, DBUS_TYPE_STRING_AS_STRING, &array); for (parser = parsers; parser && parser->name; parser++) { dbus_message_iter_append_basic(&array, DBUS_TYPE_STRING, &parser->name); } dbus_message_iter_close_container(&iter, &array); return reply; } struct device_connect_data { struct btd_adapter *adapter; bdaddr_t dst; uint8_t dst_type; DBusMessage *msg; }; static void device_browse_cb(struct btd_device *dev, int err, void *user_data) { DBG("err %d (%s)", err, strerror(-err)); if (!err) btd_device_connect_services(dev, NULL); } static void device_connect_cb(GIOChannel *io, GError *gerr, gpointer user_data) { struct device_connect_data *data = user_data; struct btd_adapter *adapter = data->adapter; struct btd_device *device; const char *path; DBG("%s", gerr ? gerr->message : ""); if (gerr) goto failed; /* object might already exist due to mgmt socket event */ device = btd_adapter_get_device(adapter, &data->dst, data->dst_type); if (!device) goto failed; path = device_get_path(device); g_dbus_send_reply(dbus_conn, data->msg, DBUS_TYPE_OBJECT_PATH, &path, DBUS_TYPE_INVALID); /* continue with service discovery and connection */ btd_device_set_temporary(device, false); device_update_last_seen(device, data->dst_type, true); if (data->dst_type != BDADDR_BREDR){ g_io_channel_set_close_on_unref(io, FALSE); device_attach_att(device, io); } device_discover_services(device); device_wait_for_svc_complete(device, device_browse_cb, NULL); g_io_channel_unref(io); dbus_message_unref(data->msg); free(data); return; failed: g_dbus_send_error(dbus_conn, data->msg, "org.bluez.Failed", NULL); g_io_channel_unref(io); dbus_message_unref(data->msg); free(data); } static void device_connect(struct btd_adapter *adapter, const bdaddr_t *dst, uint8_t dst_type, DBusMessage *msg) { struct device_connect_data *data; GIOChannel *io; data = new0(struct device_connect_data, 1); data->adapter = adapter; bacpy(&data->dst, dst); data->dst_type = dst_type; data->msg = dbus_message_ref(msg); if (dst_type == BDADDR_BREDR) io = bt_io_connect(device_connect_cb, data, NULL, NULL, BT_IO_OPT_SOURCE_BDADDR, &adapter->bdaddr, BT_IO_OPT_SOURCE_TYPE, BDADDR_BREDR, BT_IO_OPT_DEST_BDADDR, dst, BT_IO_OPT_DEST_TYPE, BDADDR_BREDR, BT_IO_OPT_PSM, SDP_PSM, BT_IO_OPT_SEC_LEVEL, BT_IO_SEC_LOW, BT_IO_OPT_INVALID); else io = bt_io_connect(device_connect_cb, data, NULL, NULL, BT_IO_OPT_SOURCE_BDADDR, &adapter->bdaddr, BT_IO_OPT_SOURCE_TYPE, adapter->bdaddr_type, BT_IO_OPT_DEST_BDADDR, dst, BT_IO_OPT_DEST_TYPE, dst_type, BT_IO_OPT_CID, ATT_CID, BT_IO_OPT_SEC_LEVEL, BT_IO_SEC_LOW, BT_IO_OPT_INVALID); if (!io) { g_dbus_send_message(dbus_conn, btd_error_failed(msg, "Connect failed")); dbus_message_unref(data->msg); free(data); } } static DBusMessage *connect_device(DBusConnection *conn, DBusMessage *msg, void *user_data) { struct btd_adapter *adapter = user_data; DBusMessageIter iter, subiter, dictiter, value; uint8_t addr_type = BDADDR_BREDR; bdaddr_t addr = *BDADDR_ANY; DBG("sender %s", dbus_message_get_sender(msg)); if (!btd_adapter_get_powered(adapter)) return btd_error_not_ready(msg); dbus_message_iter_init(msg, &iter); if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_ARRAY || dbus_message_iter_get_element_type(&iter) != DBUS_TYPE_DICT_ENTRY) return btd_error_invalid_args(msg); dbus_message_iter_recurse(&iter, &subiter); while (true) { int type = dbus_message_iter_get_arg_type(&subiter); char *key; char *str; if (type == DBUS_TYPE_INVALID) break; dbus_message_iter_recurse(&subiter, &dictiter); dbus_message_iter_get_basic(&dictiter, &key); if (!dbus_message_iter_next(&dictiter)) return btd_error_invalid_args(msg); if (dbus_message_iter_get_arg_type(&dictiter) != DBUS_TYPE_VARIANT) return btd_error_invalid_args(msg); dbus_message_iter_recurse(&dictiter, &value); if (!strcmp(key, "Address")) { if (dbus_message_iter_get_arg_type(&value) != DBUS_TYPE_STRING) return btd_error_invalid_args(msg); dbus_message_iter_get_basic(&value, &str); if (str2ba(str, &addr) < 0 ) return btd_error_invalid_args(msg); } else if (!strcmp(key, "AddressType")) { if (dbus_message_iter_get_arg_type(&value) != DBUS_TYPE_STRING) return btd_error_invalid_args(msg); dbus_message_iter_get_basic(&value, &str); if (!strcmp(str, "public")) addr_type = BDADDR_LE_PUBLIC; else if (!strcmp(str, "random")) addr_type = BDADDR_LE_RANDOM; else return btd_error_invalid_args(msg); } else { return btd_error_invalid_args(msg); } dbus_message_iter_next(&subiter); } if (!bacmp(&addr, BDADDR_ANY)) return btd_error_invalid_args(msg); device_connect(adapter, &addr, addr_type, msg); return NULL; } static void update_device_allowed_services(void *data, void *user_data) { struct btd_device *device = data; btd_device_update_allowed_services(device); } static void add_uuid_to_uuid_set(void *data, void *user_data) { bt_uuid_t *uuid = data; GHashTable *uuid_set = user_data; if (!uuid) { error("Found NULL in UUID allowed list"); return; } g_hash_table_add(uuid_set, uuid); } static guint bt_uuid_hash(gconstpointer key) { const bt_uuid_t *uuid = key; uint64_t uuid_128[2]; if (!uuid) return 0; bt_uuid_to_uuid128(uuid, (bt_uuid_t *)uuid_128); return g_int64_hash(uuid_128) ^ g_int64_hash(uuid_128+1); } static gboolean bt_uuid_equal(gconstpointer v1, gconstpointer v2) { const bt_uuid_t *uuid1 = v1; const bt_uuid_t *uuid2 = v2; if (!uuid1 || !uuid2) return !uuid1 && !uuid2; return bt_uuid_cmp(uuid1, uuid2) == 0; } bool btd_adapter_set_allowed_uuids(struct btd_adapter *adapter, struct queue *uuids) { if (!adapter) return false; if (adapter->allowed_uuid_set) g_hash_table_destroy(adapter->allowed_uuid_set); adapter->allowed_uuid_set = g_hash_table_new(bt_uuid_hash, bt_uuid_equal); if (!adapter->allowed_uuid_set) { btd_error(adapter->dev_id, "Failed to allocate allowed_uuid_set"); return false; } queue_foreach(uuids, add_uuid_to_uuid_set, adapter->allowed_uuid_set); g_slist_foreach(adapter->devices, update_device_allowed_services, NULL); return true; } bool btd_adapter_is_uuid_allowed(struct btd_adapter *adapter, const char *uuid_str) { bt_uuid_t uuid; if (!adapter || !adapter->allowed_uuid_set) return true; if (bt_string_to_uuid(&uuid, uuid_str)) { btd_error(adapter->dev_id, "Failed to parse UUID string '%s'", uuid_str); return false; } return !g_hash_table_size(adapter->allowed_uuid_set) || g_hash_table_contains(adapter->allowed_uuid_set, &uuid); } static const GDBusMethodTable adapter_methods[] = { { GDBUS_ASYNC_METHOD("StartDiscovery", NULL, NULL, start_discovery) }, { GDBUS_METHOD("SetDiscoveryFilter", GDBUS_ARGS({ "properties", "a{sv}" }), NULL, set_discovery_filter) }, { GDBUS_ASYNC_METHOD("StopDiscovery", NULL, NULL, stop_discovery) }, { GDBUS_ASYNC_METHOD("RemoveDevice", GDBUS_ARGS({ "device", "o" }), NULL, remove_device) }, { GDBUS_METHOD("GetDiscoveryFilters", NULL, GDBUS_ARGS({ "filters", "as" }), get_discovery_filters) }, { GDBUS_EXPERIMENTAL_ASYNC_METHOD("ConnectDevice", GDBUS_ARGS({ "properties", "a{sv}" }), NULL, connect_device) }, { } }; static const GDBusPropertyTable adapter_properties[] = { { "Address", "s", property_get_address }, { "AddressType", "s", property_get_address_type }, { "Name", "s", property_get_name }, { "Alias", "s", property_get_alias, property_set_alias }, { "Class", "u", property_get_class }, { "Connectable", "b", property_get_connectable, property_set_connectable }, { "Powered", "b", property_get_powered, property_set_powered }, { "PowerState", "s", property_get_power_state }, { "Discoverable", "b", property_get_discoverable, property_set_discoverable }, { "DiscoverableTimeout", "u", property_get_discoverable_timeout, property_set_discoverable_timeout }, { "Pairable", "b", property_get_pairable, property_set_pairable }, { "PairableTimeout", "u", property_get_pairable_timeout, property_set_pairable_timeout }, { "Discovering", "b", property_get_discovering }, { "UUIDs", "as", property_get_uuids }, { "Modalias", "s", property_get_modalias, NULL, property_exists_modalias }, { "Roles", "as", property_get_roles }, { "ExperimentalFeatures", "as", property_get_experimental, NULL, property_experimental_exists }, { "Manufacturer", "q", property_get_manufacturer }, { "Version", "y", property_get_version }, { } }; static int str2buf(const char *str, uint8_t *buf, size_t blen) { int i, dlen; if (str == NULL) return -EINVAL; memset(buf, 0, blen); dlen = MIN((strlen(str) / 2), blen); for (i = 0; i < dlen; i++) sscanf(str + (i * 2), "%02hhX", &buf[i]); return 0; } static bool is_blocked_key(uint8_t key_type, uint8_t *key_value) { uint32_t i = 0; for (i = 0; i < ARRAY_SIZE(blocked_keys); ++i) { if (key_type == blocked_keys[i].type && !memcmp(blocked_keys[i].val, key_value, sizeof(blocked_keys[i].val))) return true; } return false; } static struct link_key_info *get_key_info(GKeyFile *key_file, const char *peer, uint8_t bdaddr_type) { struct link_key_info *info = NULL; char *str; str = g_key_file_get_string(key_file, "LinkKey", "Key", NULL); if (!str || strlen(str) < 32) goto failed; info = g_new0(struct link_key_info, 1); str2ba(peer, &info->bdaddr); info->bdaddr_type = bdaddr_type; /* Fix up address type if it was stored with the wrong * address type since Load Link Keys are only meant to * work with BR/EDR addresses as per MGMT documentation. */ if (info->bdaddr_type != BDADDR_BREDR) info->bdaddr_type = BDADDR_BREDR; if (!strncmp(str, "0x", 2)) str2buf(&str[2], info->key, sizeof(info->key)); else str2buf(&str[0], info->key, sizeof(info->key)); info->type = g_key_file_get_integer(key_file, "LinkKey", "Type", NULL); info->pin_len = g_key_file_get_integer(key_file, "LinkKey", "PINLength", NULL); info->is_blocked = is_blocked_key(HCI_BLOCKED_KEY_TYPE_LINKKEY, info->key); failed: g_free(str); return info; } static struct smp_ltk_info *get_ltk(GKeyFile *key_file, const char *peer, uint8_t peer_type, const char *group) { struct smp_ltk_info *ltk = NULL; char *key; char *rand = NULL; key = g_key_file_get_string(key_file, group, "Key", NULL); if (!key || strlen(key) < 32) goto failed; rand = g_key_file_get_string(key_file, group, "Rand", NULL); if (!rand) goto failed; ltk = g_new0(struct smp_ltk_info, 1); /* Default to assuming a central key */ ltk->central = true; str2ba(peer, <k->bdaddr); ltk->bdaddr_type = peer_type; /* Fix up address type if it was stored with the wrong * address type since Load Long Term Keys are only meant * to work with LE addresses as per MGMT documentation. */ if (ltk->bdaddr_type == BDADDR_BREDR) ltk->bdaddr_type = BDADDR_LE_PUBLIC; /* * Long term keys should respond to an identity address which can * either be a public address or a random static address. Keys * stored for resolvable random and unresolvable random addresses * are ignored. * * This is an extra sanity check for older kernel versions or older * daemons that might have been instructed to store long term keys * for these temporary addresses. */ if (ltk->bdaddr_type == BDADDR_LE_RANDOM && (ltk->bdaddr.b[5] & 0xc0) != 0xc0) { g_free(ltk); ltk = NULL; goto failed; } if (!strncmp(key, "0x", 2)) str2buf(&key[2], ltk->val, sizeof(ltk->val)); else str2buf(&key[0], ltk->val, sizeof(ltk->val)); if (!strncmp(rand, "0x", 2)) { uint64_t rand_le; str2buf(&rand[2], (uint8_t *) &rand_le, sizeof(rand_le)); ltk->rand = le64_to_cpu(rand_le); } else { sscanf(rand, "%" PRIu64, <k->rand); } ltk->authenticated = g_key_file_get_integer(key_file, group, "Authenticated", NULL); ltk->enc_size = g_key_file_get_integer(key_file, group, "EncSize", NULL); ltk->ediv = g_key_file_get_integer(key_file, group, "EDiv", NULL); ltk->is_blocked = is_blocked_key(HCI_BLOCKED_KEY_TYPE_LTK, ltk->val); failed: g_free(key); g_free(rand); return ltk; } static struct smp_ltk_info *get_ltk_info(GKeyFile *key_file, const char *peer, uint8_t bdaddr_type) { DBG("%s", peer); return get_ltk(key_file, peer, bdaddr_type, "LongTermKey"); } static struct smp_ltk_info *get_peripheral_ltk_info(GKeyFile *key_file, const char *peer, uint8_t bdaddr_type) { struct smp_ltk_info *ltk; DBG("%s", peer); /* Peripheral* is the proper term, but for now read both entries * so it won't break when user up/downgrades. Remove the other * term after a few releases. */ ltk = get_ltk(key_file, peer, bdaddr_type, "PeripheralLongTermKey"); if (!ltk) ltk = get_ltk(key_file, peer, bdaddr_type, "SlaveLongTermKey"); if (ltk) ltk->central = false; return ltk; } static struct irk_info *get_irk_info(GKeyFile *key_file, const char *peer, uint8_t bdaddr_type) { struct irk_info *irk = NULL; char *str; str = g_key_file_get_string(key_file, "IdentityResolvingKey", "Key", NULL); if (!str || strlen(str) < 32) goto failed; irk = g_new0(struct irk_info, 1); str2ba(peer, &irk->bdaddr); irk->bdaddr_type = bdaddr_type; /* Fix up address type if it was stored with the wrong * address type since Load Identity Keys are only meant * to work with LE addresses as per MGMT documentation. */ if (irk->bdaddr_type == BDADDR_BREDR) irk->bdaddr_type = BDADDR_LE_PUBLIC; if (!strncmp(str, "0x", 2)) str2buf(&str[2], irk->val, sizeof(irk->val)); else str2buf(&str[0], irk->val, sizeof(irk->val)); irk->is_blocked = is_blocked_key(HCI_BLOCKED_KEY_TYPE_LINKKEY, irk->val); failed: g_free(str); return irk; } static struct conn_param *get_conn_param(GKeyFile *key_file, const char *peer, uint8_t bdaddr_type) { struct conn_param *param; if (!g_key_file_has_group(key_file, "ConnectionParameters")) return NULL; param = g_new0(struct conn_param, 1); param->min_interval = g_key_file_get_integer(key_file, "ConnectionParameters", "MinInterval", NULL); param->max_interval = g_key_file_get_integer(key_file, "ConnectionParameters", "MaxInterval", NULL); param->latency = g_key_file_get_integer(key_file, "ConnectionParameters", "Latency", NULL); param->timeout = g_key_file_get_integer(key_file, "ConnectionParameters", "Timeout", NULL); str2ba(peer, ¶m->bdaddr); param->bdaddr_type = bdaddr_type; return param; } static int generate_and_write_irk(uint8_t *irk, GKeyFile *key_file, const char *filename) { struct bt_crypto *crypto; char str_irk_out[33]; gsize length = 0; GError *gerr = NULL; char *str; int i; crypto = bt_crypto_new(); if (!crypto) { error("Failed to open crypto"); return -1; } if (!bt_crypto_random_bytes(crypto, irk, 16)) { error("Failed to generate IRK"); bt_crypto_unref(crypto); return -1; } bt_crypto_unref(crypto); for (i = 0; i < 16; i++) sprintf(str_irk_out + (i * 2), "%02x", irk[i]); str_irk_out[32] = '\0'; info("Generated IRK successfully"); g_key_file_set_string(key_file, "General", "IdentityResolvingKey", str_irk_out); create_file(filename, S_IRUSR | S_IWUSR); str = g_key_file_to_data(key_file, &length, NULL); if (!g_file_set_contents(filename, str, length, &gerr)) { error("Unable set contents for %s: (%s)", filename, gerr->message); g_error_free(gerr); } g_free(str); DBG("Generated IRK written to file"); return 0; } static int load_irk(struct btd_adapter *adapter, uint8_t *irk) { char filename[PATH_MAX]; GKeyFile *key_file; GError *gerr = NULL; char *str_irk; int ret; create_filename(filename, PATH_MAX, "/%s/identity", btd_adapter_get_storage_dir(adapter)); key_file = g_key_file_new(); if (!g_key_file_load_from_file(key_file, filename, 0, &gerr)) { error("Unable to load key file from %s: (%s)", filename, gerr->message); g_error_free(gerr); } str_irk = g_key_file_get_string(key_file, "General", "IdentityResolvingKey", NULL); if (!str_irk) { info("No IRK stored"); ret = generate_and_write_irk(irk, key_file, filename); g_key_file_free(key_file); return ret; } g_key_file_free(key_file); if (strlen(str_irk) != 32 || str2buf(str_irk, irk, 16)) { /* TODO re-create new IRK here? */ error("Invalid IRK format, disabling privacy"); g_free(str_irk); return -1; } g_free(str_irk); DBG("Successfully read IRK from file"); return 0; } static void set_privacy_complete(uint8_t status, uint16_t length, const void *param, void *user_data) { struct btd_adapter *adapter = user_data; if (status != MGMT_STATUS_SUCCESS) { btd_error(adapter->dev_id, "Failed to set privacy: %s (0x%02x)", mgmt_errstr(status), status); return; } DBG("Successfuly set privacy for index %u", adapter->dev_id); } static int set_privacy(struct btd_adapter *adapter, uint8_t privacy) { struct mgmt_cp_set_privacy cp; memset(&cp, 0, sizeof(cp)); if (privacy) { uint8_t irk[16]; if (load_irk(adapter, irk) == 0) { cp.privacy = privacy; memcpy(cp.irk, irk, 16); } } DBG("sending set privacy command for index %u", adapter->dev_id); DBG("setting privacy mode 0x%02x for index %u", cp.privacy, adapter->dev_id); if (mgmt_send(adapter->mgmt, MGMT_OP_SET_PRIVACY, adapter->dev_id, sizeof(cp), &cp, set_privacy_complete, adapter, NULL) > 0) return 0; btd_error(adapter->dev_id, "Failed to set privacy for index %u", adapter->dev_id); return -1; } static void load_link_keys_complete(uint8_t status, uint16_t length, const void *param, void *user_data) { struct btd_adapter *adapter = user_data; if (status != MGMT_STATUS_SUCCESS) { btd_error(adapter->dev_id, "Failed to load link keys for hci%u: %s (0x%02x)", adapter->dev_id, mgmt_errstr(status), status); return; } DBG("link keys loaded for hci%u", adapter->dev_id); } static void load_link_keys(struct btd_adapter *adapter, GSList *keys, bool debug_keys) { struct mgmt_cp_load_link_keys *cp; struct mgmt_link_key_info *key; size_t key_count, cp_size; unsigned int id; GSList *l; /* * If the controller does not support BR/EDR operation, * there is no point in trying to load the link keys into * the kernel. * * This is an optimization for Low Energy only controllers. */ if (!(adapter->supported_settings & MGMT_SETTING_BREDR)) return; key_count = g_slist_length(keys); DBG("hci%u keys %zu debug_keys %d", adapter->dev_id, key_count, debug_keys); cp_size = sizeof(*cp) + (key_count * sizeof(*key)); cp = g_try_malloc0(cp_size); if (cp == NULL) { btd_error(adapter->dev_id, "No memory for link keys for hci%u", adapter->dev_id); return; } /* * Even if the list of stored keys is empty, it is important to * load an empty list into the kernel. That way it is ensured * that no old keys from a previous daemon are present. * * In addition it is also the only way to toggle the different * behavior for debug keys. */ cp->debug_keys = debug_keys; cp->key_count = htobs(key_count); for (l = keys, key = cp->keys; l != NULL; l = g_slist_next(l), key++) { struct link_key_info *info = l->data; bacpy(&key->addr.bdaddr, &info->bdaddr); key->addr.type = info->bdaddr_type; key->type = info->type; memcpy(key->val, info->key, 16); key->pin_len = info->pin_len; } id = mgmt_send(adapter->mgmt, MGMT_OP_LOAD_LINK_KEYS, adapter->dev_id, cp_size, cp, load_link_keys_complete, adapter, NULL); g_free(cp); if (id == 0) btd_error(adapter->dev_id, "Failed to load link keys for hci%u", adapter->dev_id); } static void load_ltks_complete(uint8_t status, uint16_t length, const void *param, void *user_data) { struct btd_adapter *adapter = user_data; if (status != MGMT_STATUS_SUCCESS) { btd_error(adapter->dev_id, "Failed to load LTKs for hci%u: %s (0x%02x)", adapter->dev_id, mgmt_errstr(status), status); } DBG("LTKs loaded for hci%u", adapter->dev_id); } static void load_ltks(struct btd_adapter *adapter, GSList *keys) { struct mgmt_cp_load_long_term_keys *cp; struct mgmt_ltk_info *key; size_t key_count, max_key_count, cp_size; GSList *l; uint16_t mtu; /* * If the controller does not support Low Energy operation, * there is no point in trying to load the long term keys * into the kernel. * * While there is no harm in loading keys into the kernel, * this is an optimization to avoid a confusing warning * message when the loading of the keys timed out due to * a kernel bug (see comment below). */ if (!(adapter->supported_settings & MGMT_SETTING_LE)) return; key_count = g_slist_length(keys); mtu = mgmt_get_mtu(adapter->mgmt); max_key_count = (mtu - sizeof(*cp)) / sizeof(*key); key_count = MIN(max_key_count, key_count); DBG("hci%u keys %zu", adapter->dev_id, key_count); cp_size = sizeof(*cp) + (key_count * sizeof(*key)); cp = g_try_malloc0(cp_size); if (cp == NULL) { btd_error(adapter->dev_id, "No memory for LTKs for hci%u", adapter->dev_id); return; } /* * Even if the list of stored keys is empty, it is important to * load an empty list into the kernel. That way it is ensured * that no old keys from a previous daemon are present. */ cp->key_count = htobs(key_count); for (l = keys, key = cp->keys; l && key_count; l = g_slist_next(l), key++, key_count--) { struct smp_ltk_info *info = l->data; struct btd_device *dev; bacpy(&key->addr.bdaddr, &info->bdaddr); key->addr.type = info->bdaddr_type; memcpy(key->val, info->val, sizeof(info->val)); key->rand = cpu_to_le64(info->rand); key->ediv = cpu_to_le16(info->ediv); key->type = info->authenticated; key->central = info->central; key->enc_size = info->enc_size; /* Mark device as paired as their LTKs can be loaded. */ dev = btd_adapter_find_device(adapter, &info->bdaddr, info->bdaddr_type); if (dev) { device_set_paired(dev, info->bdaddr_type); device_set_bonded(dev, info->bdaddr_type); device_set_ltk(dev, info->val, info->central, info->enc_size); } } /* * This timeout handling is needed since the kernel is stupid * and forgets to send a command complete response. However in * case of failures it does send a command status. */ if (!mgmt_send_timeout(adapter->mgmt, MGMT_OP_LOAD_LONG_TERM_KEYS, adapter->dev_id, cp_size, cp, load_ltks_complete, adapter, NULL, 2)) btd_error(adapter->dev_id, "Failed to load LTKs for hci%u", adapter->dev_id); g_free(cp); } static void load_irks_complete(uint8_t status, uint16_t length, const void *param, void *user_data) { struct btd_adapter *adapter = user_data; if (status == MGMT_STATUS_UNKNOWN_COMMAND) { btd_info(adapter->dev_id, "Load IRKs failed: Kernel doesn't support LE Privacy"); return; } if (status != MGMT_STATUS_SUCCESS) { btd_error(adapter->dev_id, "Failed to load IRKs for hci%u: %s (0x%02x)", adapter->dev_id, mgmt_errstr(status), status); return; } DBG("IRKs loaded for hci%u", adapter->dev_id); } static void load_irks(struct btd_adapter *adapter, GSList *irks) { struct mgmt_cp_load_irks *cp; struct mgmt_irk_info *irk; size_t irk_count, cp_size; unsigned int id; GSList *l; /* * If the controller does not support LE Privacy operation, * there is no support for loading identity resolving keys * into the kernel. */ if (!(adapter->supported_settings & MGMT_SETTING_PRIVACY)) return; irk_count = g_slist_length(irks); DBG("hci%u irks %zu", adapter->dev_id, irk_count); cp_size = sizeof(*cp) + (irk_count * sizeof(*irk)); cp = g_try_malloc0(cp_size); if (cp == NULL) { btd_error(adapter->dev_id, "No memory for IRKs for hci%u", adapter->dev_id); return; } /* * Even if the list of stored keys is empty, it is important to * load an empty list into the kernel. That way we tell the * kernel that we are able to handle New IRK events. */ cp->irk_count = htobs(irk_count); for (l = irks, irk = cp->irks; l != NULL; l = g_slist_next(l), irk++) { struct irk_info *info = l->data; bacpy(&irk->addr.bdaddr, &info->bdaddr); irk->addr.type = info->bdaddr_type; memcpy(irk->val, info->val, sizeof(irk->val)); } id = mgmt_send(adapter->mgmt, MGMT_OP_LOAD_IRKS, adapter->dev_id, cp_size, cp, load_irks_complete, adapter, NULL); g_free(cp); if (id == 0) btd_error(adapter->dev_id, "Failed to IRKs for hci%u", adapter->dev_id); } static void load_conn_params_complete(uint8_t status, uint16_t length, const void *param, void *user_data) { struct btd_adapter *adapter = user_data; if (status != MGMT_STATUS_SUCCESS) { btd_error(adapter->dev_id, "hci%u Load Connection Parameters failed: %s (0x%02x)", adapter->dev_id, mgmt_errstr(status), status); return; } DBG("Connection Parameters loaded for hci%u", adapter->dev_id); } static void load_conn_params(struct btd_adapter *adapter, GSList *params) { struct mgmt_cp_load_conn_param *cp; struct mgmt_conn_param *param; size_t param_count, cp_size; unsigned int id; GSList *l; /* * If the controller does not support Low Energy operation, * there is no point in trying to load the connection * parameters into the kernel. */ if (!(adapter->supported_settings & MGMT_SETTING_LE)) return; param_count = g_slist_length(params); DBG("hci%u conn params %zu", adapter->dev_id, param_count); cp_size = sizeof(*cp) + (param_count * sizeof(*param)); cp = g_try_malloc0(cp_size); if (cp == NULL) { btd_error(adapter->dev_id, "Failed to allocate memory for connection parameters"); return; } cp->param_count = htobs(param_count); for (l = params, param = cp->params; l; l = g_slist_next(l), param++) { struct conn_param *info = l->data; bacpy(¶m->addr.bdaddr, &info->bdaddr); param->addr.type = info->bdaddr_type; param->min_interval = htobs(info->min_interval); param->max_interval = htobs(info->max_interval); param->latency = htobs(info->latency); param->timeout = htobs(info->timeout); } id = mgmt_send(adapter->mgmt, MGMT_OP_LOAD_CONN_PARAM, adapter->dev_id, cp_size, cp, load_conn_params_complete, adapter, NULL); g_free(cp); if (id == 0) btd_error(adapter->dev_id, "Load connection parameters failed"); } void btd_adapter_load_conn_param(struct btd_adapter *adapter, const bdaddr_t *peer, uint8_t bdaddr_type, uint16_t min_interval, uint16_t max_interval, uint16_t latency, uint16_t timeout) { GSList *params = NULL; struct conn_param param; /* Only versions >= 1.23 support updating connection parameters */ if (MGMT_VERSION(mgmt_version, mgmt_revision) < MGMT_VERSION(1, 23)) return; bacpy(¶m.bdaddr, peer); param.bdaddr_type = bdaddr_type; param.max_interval = max_interval; param.min_interval = min_interval; param.latency = latency; param.timeout = timeout; params = g_slist_append(params, ¶m); load_conn_params(adapter, params); g_slist_free(params); } static uint8_t get_addr_type(GKeyFile *keyfile) { uint8_t addr_type; char *type; /* The AddressType is written to file only When dev->le is * set to true, as referenced in the update_technologies(). * Therefore, When type is NULL, it default to BDADDR_BREDR. */ type = g_key_file_get_string(keyfile, "General", "AddressType", NULL); if (!type) return BDADDR_BREDR; if (g_str_equal(type, "public")) addr_type = BDADDR_LE_PUBLIC; else if (g_str_equal(type, "static")) addr_type = BDADDR_LE_RANDOM; else addr_type = BDADDR_LE_PUBLIC; g_free(type); return addr_type; } static void probe_devices(void *user_data) { struct btd_device *device = user_data; device_probe_profiles(device, btd_device_get_uuids(device)); device_resolved_drivers(device_get_adapter(device), device); } static bool load_bredr_defaults(struct btd_adapter *adapter, struct mgmt_tlv_list *list, struct btd_br_defaults *defaults) { if (btd_opts.mode == BT_MODE_LE) return true; if (defaults->page_scan_type != 0xFFFF) { if (!mgmt_tlv_add_fixed(list, 0x0000, &defaults->page_scan_type)) return false; } if (defaults->page_scan_interval) { if (!mgmt_tlv_add_fixed(list, 0x0001, &defaults->page_scan_interval)) return false; } if (defaults->page_scan_win) { if (!mgmt_tlv_add_fixed(list, 0x0002, &defaults->page_scan_win)) return false; } if (defaults->scan_type != 0xFFFF) { if (!mgmt_tlv_add_fixed(list, 0x0003, &defaults->scan_type)) return false; } if (defaults->scan_interval) { if (!mgmt_tlv_add_fixed(list, 0x0004, &defaults->scan_interval)) return false; } if (defaults->scan_win) { if (!mgmt_tlv_add_fixed(list, 0x0005, &defaults->scan_win)) return false; } if (defaults->link_supervision_timeout) { if (!mgmt_tlv_add_fixed(list, 0x0006, &defaults->link_supervision_timeout)) return false; } if (defaults->page_timeout) { if (!mgmt_tlv_add_fixed(list, 0x0007, &defaults->page_timeout)) return false; } if (defaults->min_sniff_interval) { if (!mgmt_tlv_add_fixed(list, 0x0008, &defaults->min_sniff_interval)) return false; } if (defaults->max_sniff_interval) { if (!mgmt_tlv_add_fixed(list, 0x0009, &defaults->max_sniff_interval)) return false; } return true; } static bool load_le_defaults(struct btd_adapter *adapter, struct mgmt_tlv_list *list, struct btd_le_defaults *defaults) { if (btd_opts.mode == BT_MODE_BREDR) return true; if (defaults->min_adv_interval) { if (!mgmt_tlv_add_fixed(list, 0x000a, &defaults->min_adv_interval)) return false; } if (defaults->max_adv_interval) { if (!mgmt_tlv_add_fixed(list, 0x000b, &defaults->max_adv_interval)) return false; } if (defaults->adv_rotation_interval) { if (!mgmt_tlv_add_fixed(list, 0x000c, &defaults->adv_rotation_interval)) return false; } if (defaults->scan_interval_autoconnect) { if (!mgmt_tlv_add_fixed(list, 0x000d, &defaults->scan_interval_autoconnect)) return false; } if (defaults->scan_win_autoconnect) { if (!mgmt_tlv_add_fixed(list, 0x000e, &defaults->scan_win_autoconnect)) return false; } if (defaults->scan_interval_suspend) { if (!mgmt_tlv_add_fixed(list, 0x000f, &defaults->scan_interval_suspend)) return false; } if (defaults->scan_win_suspend) { if (!mgmt_tlv_add_fixed(list, 0x0010, &defaults->scan_win_suspend)) return false; } if (defaults->scan_interval_discovery) { if (!mgmt_tlv_add_fixed(list, 0x0011, &defaults->scan_interval_discovery)) return false; } if (defaults->scan_win_discovery) { if (!mgmt_tlv_add_fixed(list, 0x0012, &defaults->scan_win_discovery)) return false; } if (defaults->scan_interval_adv_monitor) { if (!mgmt_tlv_add_fixed(list, 0x0013, &defaults->scan_interval_adv_monitor)) return false; } if (defaults->scan_win_adv_monitor) { if (!mgmt_tlv_add_fixed(list, 0x0014, &defaults->scan_win_adv_monitor)) return false; } if (defaults->scan_interval_connect) { if (!mgmt_tlv_add_fixed(list, 0x0015, &defaults->scan_interval_connect)) return false; } if (defaults->scan_win_connect) { if (!mgmt_tlv_add_fixed(list, 0x0016, &defaults->scan_win_connect)) return false; } if (defaults->min_conn_interval) { if (!mgmt_tlv_add_fixed(list, 0x0017, &defaults->min_conn_interval)) return false; } if (defaults->max_conn_interval) { if (!mgmt_tlv_add_fixed(list, 0x0018, &defaults->max_conn_interval)) return false; } if (defaults->conn_latency) { if (!mgmt_tlv_add_fixed(list, 0x0019, &defaults->conn_latency)) return false; } if (defaults->conn_lsto) { if (!mgmt_tlv_add_fixed(list, 0x001a, &defaults->conn_lsto)) return false; } if (defaults->autoconnect_timeout) { if (!mgmt_tlv_add_fixed(list, 0x001b, &defaults->autoconnect_timeout)) return false; } if (defaults->advmon_allowlist_scan_duration) { if (!mgmt_tlv_add_fixed(list, 0x001d, &defaults->advmon_allowlist_scan_duration)) return false; } if (defaults->advmon_no_filter_scan_duration) { if (!mgmt_tlv_add_fixed(list, 0x001e, &defaults->advmon_no_filter_scan_duration)) return false; } if (defaults->enable_advmon_interleave_scan != 0xFF) { if (!mgmt_tlv_add_fixed(list, 0x001f, &defaults->enable_advmon_interleave_scan)) return false; } return true; } static void load_defaults(struct btd_adapter *adapter) { struct mgmt_tlv_list *list; unsigned int err = 0; if (!btd_opts.defaults.num_entries || !btd_has_kernel_features(KERNEL_SET_SYSTEM_CONFIG)) return; list = mgmt_tlv_list_new(); if (!load_bredr_defaults(adapter, list, &btd_opts.defaults.br)) goto done; if (!load_le_defaults(adapter, list, &btd_opts.defaults.le)) goto done; err = mgmt_send_tlv(adapter->mgmt, MGMT_OP_SET_DEF_SYSTEM_CONFIG, adapter->dev_id, list, NULL, NULL, NULL); done: if (!err) btd_error(adapter->dev_id, "Failed to set default system config for hci%u", adapter->dev_id); mgmt_tlv_list_free(list); } static void load_devices(struct btd_adapter *adapter) { char dirname[PATH_MAX]; GSList *keys = NULL; GSList *ltks = NULL; GSList *irks = NULL; GSList *params = NULL; GSList *added_devices = NULL; GError *gerr = NULL; DIR *dir; struct dirent *entry; create_filename(dirname, PATH_MAX, "/%s", btd_adapter_get_storage_dir(adapter)); dir = opendir(dirname); if (!dir) { btd_error(adapter->dev_id, "Unable to open adapter storage directory: %s", dirname); return; } while ((entry = readdir(dir)) != NULL) { struct btd_device *device; char filename[PATH_MAX]; GKeyFile *key_file; struct link_key_info *key_info; struct smp_ltk_info *ltk_info; struct smp_ltk_info *peripheral_ltk_info; GSList *list; struct irk_info *irk_info; struct conn_param *param; uint8_t bdaddr_type; if (entry->d_type == DT_UNKNOWN) entry->d_type = util_get_dt(dirname, entry->d_name); if (entry->d_type != DT_DIR || bachk(entry->d_name) < 0) continue; create_filename(filename, PATH_MAX, "/%s/%s/info", btd_adapter_get_storage_dir(adapter), entry->d_name); key_file = g_key_file_new(); if (!g_key_file_load_from_file(key_file, filename, 0, &gerr)) { error("Unable to load key file from %s: (%s)", filename, gerr->message); g_clear_error(&gerr); } bdaddr_type = get_addr_type(key_file); key_info = get_key_info(key_file, entry->d_name, bdaddr_type); ltk_info = get_ltk_info(key_file, entry->d_name, bdaddr_type); peripheral_ltk_info = get_peripheral_ltk_info(key_file, entry->d_name, bdaddr_type); irk_info = get_irk_info(key_file, entry->d_name, bdaddr_type); // If any key for the device is blocked, we discard all. if ((key_info && key_info->is_blocked) || (ltk_info && ltk_info->is_blocked) || (peripheral_ltk_info && peripheral_ltk_info->is_blocked) || (irk_info && irk_info->is_blocked)) { if (key_info) { g_free(key_info); key_info = NULL; } if (ltk_info) { g_free(ltk_info); ltk_info = NULL; } if (peripheral_ltk_info) { g_free(peripheral_ltk_info); peripheral_ltk_info = NULL; } if (irk_info) { g_free(irk_info); irk_info = NULL; } goto free; } if (key_info) keys = g_slist_append(keys, key_info); if (ltk_info) ltks = g_slist_append(ltks, ltk_info); if (peripheral_ltk_info) ltks = g_slist_append(ltks, peripheral_ltk_info); if (irk_info) irks = g_slist_append(irks, irk_info); param = get_conn_param(key_file, entry->d_name, bdaddr_type); if (param) params = g_slist_append(params, param); list = g_slist_find_custom(adapter->devices, entry->d_name, device_address_cmp); if (list) { device = list->data; goto device_exist; } device = device_create_from_storage(adapter, entry->d_name, key_file); if (!device) goto free; if (irk_info) device_set_rpa(device, true); btd_device_set_temporary(device, false); adapter_add_device(adapter, device); /* TODO: register services from pre-loaded list of primaries */ added_devices = g_slist_append(added_devices, device); device_exist: if (key_info) { device_set_paired(device, BDADDR_BREDR); device_set_bonded(device, BDADDR_BREDR); } free: g_key_file_free(key_file); } closedir(dir); load_link_keys(adapter, keys, btd_opts.debug_keys); g_slist_free_full(keys, g_free); load_ltks(adapter, ltks); g_slist_free_full(ltks, g_free); load_irks(adapter, irks); g_slist_free_full(irks, g_free); load_conn_params(adapter, params); g_slist_free_full(params, g_free); g_slist_free_full(added_devices, probe_devices); } int btd_adapter_block_address(struct btd_adapter *adapter, const bdaddr_t *bdaddr, uint8_t bdaddr_type) { struct mgmt_cp_block_device cp; char addr[18]; ba2str(bdaddr, addr); DBG("hci%u %s", adapter->dev_id, addr); memset(&cp, 0, sizeof(cp)); bacpy(&cp.addr.bdaddr, bdaddr); cp.addr.type = bdaddr_type; if (mgmt_send(adapter->mgmt, MGMT_OP_BLOCK_DEVICE, adapter->dev_id, sizeof(cp), &cp, NULL, NULL, NULL) > 0) return 0; return -EIO; } int btd_adapter_unblock_address(struct btd_adapter *adapter, const bdaddr_t *bdaddr, uint8_t bdaddr_type) { struct mgmt_cp_unblock_device cp; char addr[18]; ba2str(bdaddr, addr); DBG("hci%u %s", adapter->dev_id, addr); memset(&cp, 0, sizeof(cp)); bacpy(&cp.addr.bdaddr, bdaddr); cp.addr.type = bdaddr_type; if (mgmt_send(adapter->mgmt, MGMT_OP_UNBLOCK_DEVICE, adapter->dev_id, sizeof(cp), &cp, NULL, NULL, NULL) > 0) return 0; return -EIO; } static int clear_blocked(struct btd_adapter *adapter) { return btd_adapter_unblock_address(adapter, BDADDR_ANY, 0); } static void probe_driver(struct btd_adapter *adapter, gpointer user_data) { struct btd_adapter_driver *driver = user_data; int err; if (driver->probe == NULL) return; err = driver->probe(adapter); if (err < 0) { btd_error(adapter->dev_id, "%s: %s (%d)", driver->name, strerror(-err), -err); return; } adapter->drivers = g_slist_prepend(adapter->drivers, driver); } static void load_drivers(struct btd_adapter *adapter) { GSList *l; for (l = adapter_drivers; l; l = l->next) probe_driver(adapter, l->data); } static void probe_profile(struct btd_profile *profile, void *data) { struct btd_adapter *adapter = data; int err; if (profile->adapter_probe == NULL) return; err = profile->adapter_probe(profile, adapter); if (err < 0) { btd_error(adapter->dev_id, "%s: %s (%d)", profile->name, strerror(-err), -err); return; } adapter->profiles = g_slist_prepend(adapter->profiles, profile); } void adapter_add_profile(struct btd_adapter *adapter, gpointer p) { struct btd_profile *profile = p; if (!adapter->initialized) return; probe_profile(profile, adapter); g_slist_foreach(adapter->devices, device_probe_profile, profile); } void adapter_remove_profile(struct btd_adapter *adapter, gpointer p) { struct btd_profile *profile = p; if (!adapter->initialized) return; if (profile->device_remove) g_slist_foreach(adapter->devices, device_remove_profile, p); adapter->profiles = g_slist_remove(adapter->profiles, profile); if (profile->adapter_remove) profile->adapter_remove(profile, adapter); } static void device_added_drivers(struct btd_adapter *adapter, struct btd_device *device) { struct btd_adapter_driver *driver; GSList *l; for (l = adapter_drivers; l; l = l->next) { driver = l->data; if (driver->device_added) driver->device_added(adapter, device); } } static void device_removed_drivers(struct btd_adapter *adapter, struct btd_device *device) { struct btd_adapter_driver *driver; GSList *l; for (l = adapter_drivers; l; l = l->next) { driver = l->data; if (driver->device_removed) driver->device_removed(adapter, device); } } void device_resolved_drivers(struct btd_adapter *adapter, struct btd_device *device) { struct btd_adapter_driver *driver; GSList *l; for (l = adapter_drivers; l; l = l->next) { driver = l->data; if (driver->device_resolved) driver->device_resolved(adapter, device); } } static void adapter_add_device(struct btd_adapter *adapter, struct btd_device *device) { adapter->devices = g_slist_prepend(adapter->devices, device); device_added_drivers(adapter, device); } static void adapter_remove_device(struct btd_adapter *adapter, struct btd_device *device) { adapter->devices = g_slist_remove(adapter->devices, device); device_removed_drivers(adapter, device); } static void adapter_add_connection(struct btd_adapter *adapter, struct btd_device *device, uint8_t bdaddr_type, uint32_t flags) { device_add_connection(device, bdaddr_type, flags); if (g_slist_find(adapter->connections, device)) { btd_error(adapter->dev_id, "Device is already marked as connected"); return; } adapter->connections = g_slist_append(adapter->connections, device); } static void get_connections_complete(uint8_t status, uint16_t length, const void *param, void *user_data) { struct btd_adapter *adapter = user_data; const struct mgmt_rp_get_connections *rp = param; uint16_t i, conn_count; if (status != MGMT_STATUS_SUCCESS) { btd_error(adapter->dev_id, "Failed to get connections: %s (0x%02x)", mgmt_errstr(status), status); return; } if (length < sizeof(*rp)) { btd_error(adapter->dev_id, "Wrong size of get connections response"); return; } conn_count = btohs(rp->conn_count); DBG("Connection count: %d", conn_count); if (conn_count * sizeof(struct mgmt_addr_info) + sizeof(*rp) != length) { btd_error(adapter->dev_id, "Incorrect packet size for get connections response"); return; } for (i = 0; i < conn_count; i++) { const struct mgmt_addr_info *addr = &rp->addr[i]; struct btd_device *device; char address[18]; ba2str(&addr->bdaddr, address); DBG("Adding existing connection to %s", address); device = btd_adapter_get_device(adapter, &addr->bdaddr, addr->type); if (device) adapter_add_connection(adapter, device, addr->type, 0); } } static void load_connections(struct btd_adapter *adapter) { DBG("sending get connections command for index %u", adapter->dev_id); if (mgmt_send(adapter->mgmt, MGMT_OP_GET_CONNECTIONS, adapter->dev_id, 0, NULL, get_connections_complete, adapter, NULL) > 0) return; btd_error(adapter->dev_id, "Failed to get connections for index %u", adapter->dev_id); } bool btd_adapter_get_pairable(struct btd_adapter *adapter) { if (adapter->current_settings & MGMT_SETTING_BONDABLE) return true; return false; } bool btd_adapter_get_powered(struct btd_adapter *adapter) { if ((adapter->current_settings & MGMT_SETTING_POWERED) && !(adapter->pending_settings & MGMT_SETTING_POWERED)) return true; return false; } bool btd_adapter_get_connectable(struct btd_adapter *adapter) { if (adapter->current_settings & MGMT_SETTING_CONNECTABLE) return true; return false; } bool btd_adapter_get_discoverable(struct btd_adapter *adapter) { if (adapter->current_settings & MGMT_SETTING_DISCOVERABLE) return true; return false; } bool btd_adapter_get_bredr(struct btd_adapter *adapter) { if (adapter->current_settings & MGMT_SETTING_BREDR) return true; return false; } struct btd_gatt_database *btd_adapter_get_database(struct btd_adapter *adapter) { if (!adapter) return NULL; return adapter->database; } uint32_t btd_adapter_get_class(struct btd_adapter *adapter) { return adapter->dev_class; } const char *btd_adapter_get_name(struct btd_adapter *adapter) { if (adapter->stored_alias) return adapter->stored_alias; if (adapter->system_name) return adapter->system_name; return NULL; } int adapter_connect_list_add(struct btd_adapter *adapter, struct btd_device *device) { /* * If the adapter->connect_le device is getting added back to * the connect list it probably means that the connect attempt * failed and hence we should clear this pointer */ if (device == adapter->connect_le) adapter->connect_le = NULL; /* * If kernel background scanning is supported then the * adapter_auto_connect_add() function is used to maintain what to * connect. */ if (btd_has_kernel_features(KERNEL_CONN_CONTROL)) return 0; if (g_slist_find(adapter->connect_list, device)) { DBG("ignoring already added device %s", device_get_path(device)); goto done; } if (!(adapter->supported_settings & MGMT_SETTING_LE)) { btd_error(adapter->dev_id, "Can't add %s to non-LE capable adapter connect list", device_get_path(device)); return -ENOTSUP; } adapter->connect_list = g_slist_append(adapter->connect_list, device); DBG("%s added to %s's connect_list", device_get_path(device), adapter->system_name); done: if (!btd_adapter_get_powered(adapter)) return 0; trigger_passive_scanning(adapter); return 0; } void adapter_connect_list_remove(struct btd_adapter *adapter, struct btd_device *device) { /* * If the adapter->connect_le device is being removed from the * connect list it means the connection was successful and hence * the pointer should be cleared */ if (device == adapter->connect_le) adapter->connect_le = NULL; if (btd_has_kernel_features(KERNEL_CONN_CONTROL)) return; if (!g_slist_find(adapter->connect_list, device)) { DBG("device %s is not on the list, ignoring", device_get_path(device)); return; } adapter->connect_list = g_slist_remove(adapter->connect_list, device); DBG("%s removed from %s's connect_list", device_get_path(device), adapter->system_name); if (!adapter->connect_list) { stop_passive_scanning(adapter); return; } if (!btd_adapter_get_powered(adapter)) return; trigger_passive_scanning(adapter); } static void add_accept_list_complete(uint8_t status, uint16_t length, const void *param, void *user_data) { const struct mgmt_rp_add_device *rp = param; struct btd_adapter *adapter = user_data; struct btd_device *dev; char addr[18]; if (length < sizeof(*rp)) { btd_error(adapter->dev_id, "Too small Add Device complete event"); return; } ba2str(&rp->addr.bdaddr, addr); dev = btd_adapter_find_device(adapter, &rp->addr.bdaddr, rp->addr.type); if (!dev) { btd_error(adapter->dev_id, "Add Device complete for unknown device %s", addr); return; } if (status != MGMT_STATUS_SUCCESS) { btd_error(adapter->dev_id, "Failed to add device %s: %s (0x%02x)", addr, mgmt_errstr(status), status); return; } DBG("%s added to kernel accept list", addr); } void adapter_accept_list_add(struct btd_adapter *adapter, struct btd_device *dev) { struct mgmt_cp_add_device cp; if (!btd_has_kernel_features(KERNEL_CONN_CONTROL)) return; memset(&cp, 0, sizeof(cp)); bacpy(&cp.addr.bdaddr, device_get_address(dev)); cp.addr.type = BDADDR_BREDR; cp.action = 0x01; mgmt_send(adapter->mgmt, MGMT_OP_ADD_DEVICE, adapter->dev_id, sizeof(cp), &cp, add_accept_list_complete, adapter, NULL); } static void remove_accept_list_complete(uint8_t status, uint16_t length, const void *param, void *user_data) { const struct mgmt_rp_remove_device *rp = param; char addr[18]; if (length < sizeof(*rp)) { error("Too small Remove Device complete event"); return; } ba2str(&rp->addr.bdaddr, addr); if (status != MGMT_STATUS_SUCCESS) { error("Failed to remove device %s: %s (0x%02x)", addr, mgmt_errstr(status), status); return; } DBG("%s removed from kernel accept list", addr); } void adapter_accept_list_remove(struct btd_adapter *adapter, struct btd_device *dev) { struct mgmt_cp_remove_device cp; if (!btd_has_kernel_features(KERNEL_CONN_CONTROL)) return; memset(&cp, 0, sizeof(cp)); bacpy(&cp.addr.bdaddr, device_get_address(dev)); cp.addr.type = BDADDR_BREDR; mgmt_send(adapter->mgmt, MGMT_OP_REMOVE_DEVICE, adapter->dev_id, sizeof(cp), &cp, remove_accept_list_complete, adapter, NULL); } static void set_device_privacy_complete(uint8_t status, uint16_t length, const void *param, void *user_data) { struct btd_device *dev = user_data; const struct mgmt_rp_set_device_flags *rp = param; if (status != MGMT_STATUS_SUCCESS) { error("Set device flags return status: %s", mgmt_errstr(status)); btd_device_set_pending_flags(dev, 0); return; } if (length < sizeof(*rp)) { error("Too small Set Device Flags complete event: %d", length); return; } btd_device_flags_changed(dev, btd_device_get_supported_flags(dev), btd_device_get_pending_flags(dev)); } static void add_device_complete(uint8_t status, uint16_t length, const void *param, void *user_data) { const struct mgmt_rp_add_device *rp = param; struct btd_adapter *adapter = user_data; struct btd_device *dev; char addr[18]; if (length < sizeof(*rp)) { btd_error(adapter->dev_id, "Too small Add Device complete event"); return; } ba2str(&rp->addr.bdaddr, addr); dev = btd_adapter_find_device(adapter, &rp->addr.bdaddr, rp->addr.type); if (!dev) { btd_error(adapter->dev_id, "Add Device complete for unknown device %s", addr); return; } if (status != MGMT_STATUS_SUCCESS) { btd_error(adapter->dev_id, "Failed to add device %s (%u): %s (0x%02x)", addr, rp->addr.type, mgmt_errstr(status), status); adapter->connect_list = g_slist_remove(adapter->connect_list, dev); return; } DBG("%s (%u) added to kernel connect list", addr, rp->addr.type); if (btd_opts.device_privacy) { uint32_t flags = btd_device_get_current_flags(dev); /* Set Device Privacy Mode if it has not set the flag yet. */ if (!(flags & DEVICE_FLAG_DEVICE_PRIVACY)) { /* Include the pending flags, or they may get * overwritten. */ flags |= btd_device_get_pending_flags(dev); adapter_set_device_flags(adapter, dev, flags | DEVICE_FLAG_DEVICE_PRIVACY, set_device_privacy_complete, dev); } } } void adapter_auto_connect_add(struct btd_adapter *adapter, struct btd_device *device) { struct mgmt_cp_add_device cp; const bdaddr_t *bdaddr; uint8_t bdaddr_type; unsigned int id; if (!btd_has_kernel_features(KERNEL_CONN_CONTROL)) return; if (g_slist_find(adapter->connect_list, device)) { DBG("ignoring already added device %s", device_get_path(device)); return; } bdaddr = device_get_address(device); bdaddr_type = btd_device_get_bdaddr_type(device); if (bdaddr_type == BDADDR_BREDR) { DBG("auto-connection feature is not avaiable for BR/EDR"); return; } memset(&cp, 0, sizeof(cp)); bacpy(&cp.addr.bdaddr, bdaddr); cp.addr.type = bdaddr_type; cp.action = 0x02; id = mgmt_send(adapter->mgmt, MGMT_OP_ADD_DEVICE, adapter->dev_id, sizeof(cp), &cp, add_device_complete, adapter, NULL); if (id == 0) return; adapter->connect_list = g_slist_append(adapter->connect_list, device); } void adapter_set_device_flags(struct btd_adapter *adapter, struct btd_device *device, uint32_t flags, mgmt_request_func_t func, void *user_data) { struct mgmt_cp_set_device_flags cp; uint32_t current = btd_device_get_current_flags(device); uint32_t supported = btd_device_get_supported_flags(device); uint32_t pending = btd_device_get_pending_flags(device); const bdaddr_t *bdaddr; uint8_t bdaddr_type; bool ll_privacy = btd_adapter_has_settings(adapter, MGMT_SETTING_LL_PRIVACY); if (!btd_has_kernel_features(KERNEL_CONN_CONTROL) || (supported | flags) != supported) return; /* Check if changing flags are pending */ if ((current ^ flags) == (flags & pending)) return; /* Set Device Privacy Mode if it has not set the flag yet. */ if (btd_opts.device_privacy && !(flags & DEVICE_FLAG_DEVICE_PRIVACY)) flags |= DEVICE_FLAG_DEVICE_PRIVACY & supported & ~pending; /* Set Address Resolution if it has not been set the flag yet. */ if (ll_privacy && btd_opts.defaults.le.addr_resolution && device_address_is_private(device) && !(flags & DEVICE_FLAG_ADDRESS_RESOLUTION)) flags |= DEVICE_FLAG_ADDRESS_RESOLUTION & supported & ~pending; bdaddr = device_get_address(device); bdaddr_type = btd_device_get_bdaddr_type(device); memset(&cp, 0, sizeof(cp)); bacpy(&cp.addr.bdaddr, bdaddr); cp.addr.type = bdaddr_type; cp.current_flags = cpu_to_le32(flags); if (mgmt_send(adapter->mgmt, MGMT_OP_SET_DEVICE_FLAGS, adapter->dev_id, sizeof(cp), &cp, func, user_data, NULL)) btd_device_set_pending_flags(device, flags); } static void device_flags_changed_callback(uint16_t index, uint16_t length, const void *param, void *user_data) { const struct mgmt_ev_device_flags_changed *ev = param; struct btd_adapter *adapter = user_data; struct btd_device *dev; char addr[18]; if (length < sizeof(*ev)) { btd_error(adapter->dev_id, "Too small Device Flags Changed event: %d", length); return; } ba2str(&ev->addr.bdaddr, addr); dev = btd_adapter_find_device(adapter, &ev->addr.bdaddr, ev->addr.type); if (!dev) { btd_error(adapter->dev_id, "Device Flags Changed for unknown device %s", addr); return; } btd_device_flags_changed(dev, ev->supported_flags, ev->current_flags); } static void remove_device_complete(uint8_t status, uint16_t length, const void *param, void *user_data) { const struct mgmt_rp_remove_device *rp = param; char addr[18]; if (length < sizeof(*rp)) { error("Too small Remove Device complete event"); return; } ba2str(&rp->addr.bdaddr, addr); if (status != MGMT_STATUS_SUCCESS) { error("Failed to remove device %s (%u): %s (0x%02x)", addr, rp->addr.type, mgmt_errstr(status), status); return; } DBG("%s (%u) removed from kernel connect list", addr, rp->addr.type); } void adapter_auto_connect_remove(struct btd_adapter *adapter, struct btd_device *device) { struct mgmt_cp_remove_device cp; const bdaddr_t *bdaddr; uint8_t bdaddr_type; unsigned int id; if (!btd_has_kernel_features(KERNEL_CONN_CONTROL)) return; if (!g_slist_find(adapter->connect_list, device)) { DBG("ignoring not added device %s", device_get_path(device)); return; } bdaddr = device_get_address(device); bdaddr_type = btd_device_get_bdaddr_type(device); if (bdaddr_type == BDADDR_BREDR) { DBG("auto-connection feature is not avaiable for BR/EDR"); return; } memset(&cp, 0, sizeof(cp)); bacpy(&cp.addr.bdaddr, bdaddr); cp.addr.type = bdaddr_type; id = mgmt_send(adapter->mgmt, MGMT_OP_REMOVE_DEVICE, adapter->dev_id, sizeof(cp), &cp, remove_device_complete, adapter, NULL); if (id == 0) return; adapter->connect_list = g_slist_remove(adapter->connect_list, device); } static void adapter_start(struct btd_adapter *adapter) { g_dbus_emit_property_changed(dbus_conn, adapter->path, ADAPTER_INTERFACE, "Powered"); adapter_set_power_state(adapter, ADAPTER_POWER_STATE_ON); DBG("adapter %s has been enabled", adapter->path); trigger_passive_scanning(adapter); } static void reply_pending_requests(struct btd_adapter *adapter) { GSList *l; if (!adapter) return; /* pending bonding */ for (l = adapter->devices; l; l = l->next) { struct btd_device *device = l->data; if (device_is_bonding(device, NULL)) device_bonding_failed(device, HCI_OE_USER_ENDED_CONNECTION); } } static void remove_driver(gpointer data, gpointer user_data) { struct btd_adapter_driver *driver = data; struct btd_adapter *adapter = user_data; if (driver->remove) driver->remove(adapter); } static void remove_profile(gpointer data, gpointer user_data) { struct btd_profile *profile = data; struct btd_adapter *adapter = user_data; if (profile->adapter_remove) profile->adapter_remove(profile, adapter); } static void unload_drivers(struct btd_adapter *adapter) { g_slist_foreach(adapter->drivers, remove_driver, adapter); g_slist_free(adapter->drivers); adapter->drivers = NULL; g_slist_foreach(adapter->profiles, remove_profile, adapter); g_slist_free(adapter->profiles); adapter->profiles = NULL; } static void free_service_auth(gpointer data, gpointer user_data) { struct service_auth *auth = data; g_free(auth); } static void remove_discovery_list(struct btd_adapter *adapter) { g_slist_free_full(adapter->set_filter_list, discovery_free); adapter->set_filter_list = NULL; g_slist_free_full(adapter->discovery_list, discovery_free); adapter->discovery_list = NULL; } static void cancel_exp_pending(void *data) { struct exp_pending *pending = data; struct btd_adapter *adapter = pending->adapter; pending->adapter = NULL; mgmt_cancel(adapter->mgmt, pending->id); g_free(pending); } static void adapter_free(gpointer user_data) { struct btd_adapter *adapter = user_data; DBG("%p", adapter); /* Make sure the adapter's discovery list is cleaned up before freeing * the adapter. */ remove_discovery_list(adapter); if (adapter->pairable_timeout_id > 0) { timeout_remove(adapter->pairable_timeout_id); adapter->pairable_timeout_id = 0; } if (adapter->passive_scan_timeout > 0) { timeout_remove(adapter->passive_scan_timeout); adapter->passive_scan_timeout = 0; } if (adapter->auth_idle_id) g_source_remove(adapter->auth_idle_id); g_queue_foreach(adapter->auths, free_service_auth, NULL); g_queue_free(adapter->auths); queue_destroy(adapter->exps, NULL); queue_destroy(adapter->exp_pending, cancel_exp_pending); /* * Unregister all handlers for this specific index since * the adapter bound to them is no longer valid. * * This also avoids having multiple instances of the same * handler in case indexes got removed and re-added. */ mgmt_unregister_index(adapter->mgmt, adapter->dev_id); /* * Cancel all pending commands for this specific index * since the adapter bound to them is no longer valid. */ mgmt_cancel_index(adapter->mgmt, adapter->dev_id); mgmt_unref(adapter->mgmt); sdp_list_free(adapter->services, NULL); g_slist_free(adapter->connections); g_free(adapter->path); g_free(adapter->name); g_free(adapter->short_name); g_free(adapter->system_name); g_free(adapter->stored_alias); g_free(adapter->current_alias); free(adapter->modalias); if (adapter->allowed_uuid_set) g_hash_table_destroy(adapter->allowed_uuid_set); g_free(adapter); } struct btd_adapter *btd_adapter_ref(struct btd_adapter *adapter) { __sync_fetch_and_add(&adapter->ref_count, 1); return adapter; } void btd_adapter_unref(struct btd_adapter *adapter) { if (__sync_sub_and_fetch(&adapter->ref_count, 1)) return; if (!adapter->path) { DBG("Freeing adapter %u", adapter->dev_id); adapter_free(adapter); return; } DBG("Freeing adapter %s", adapter->path); g_dbus_unregister_interface(dbus_conn, adapter->path, ADAPTER_INTERFACE); } static void convert_names_entry(char *key, char *value, void *user_data) { char *address = user_data; char *str = key; char filename[PATH_MAX]; GKeyFile *key_file; GError *gerr = NULL; char *data; gsize length = 0; if (strchr(key, '#')) str[17] = '\0'; if (bachk(str) != 0) return; create_filename(filename, PATH_MAX, "/%s/cache/%s", address, str); create_file(filename, 0600); key_file = g_key_file_new(); if (!g_key_file_load_from_file(key_file, filename, 0, &gerr)) { error("Unable to load key file from %s: (%s)", filename, gerr->message); g_clear_error(&gerr); } g_key_file_set_string(key_file, "General", "Name", value); data = g_key_file_to_data(key_file, &length, NULL); if (!g_file_set_contents(filename, data, length, &gerr)) { error("Unable set contents for %s: (%s)", filename, gerr->message); g_error_free(gerr); } g_free(data); g_key_file_free(key_file); } struct device_converter { char *address; void (*cb)(GKeyFile *key_file, void *value); gboolean force; }; static void set_device_type(GKeyFile *key_file, char type) { char *techno; char *addr_type = NULL; char *str; switch (type) { case BDADDR_BREDR: techno = "BR/EDR"; break; case BDADDR_LE_PUBLIC: techno = "LE"; addr_type = "public"; break; case BDADDR_LE_RANDOM: techno = "LE"; addr_type = "static"; break; default: return; } str = g_key_file_get_string(key_file, "General", "SupportedTechnologies", NULL); if (!str) g_key_file_set_string(key_file, "General", "SupportedTechnologies", techno); else if (!strstr(str, techno)) g_key_file_set_string(key_file, "General", "SupportedTechnologies", "BR/EDR;LE"); g_free(str); if (addr_type) g_key_file_set_string(key_file, "General", "AddressType", addr_type); } static void convert_aliases_entry(GKeyFile *key_file, void *value) { g_key_file_set_string(key_file, "General", "Alias", value); } static void convert_trusts_entry(GKeyFile *key_file, void *value) { g_key_file_set_boolean(key_file, "General", "Trusted", TRUE); } static void convert_classes_entry(GKeyFile *key_file, void *value) { g_key_file_set_string(key_file, "General", "Class", value); } static void convert_blocked_entry(GKeyFile *key_file, void *value) { g_key_file_set_boolean(key_file, "General", "Blocked", TRUE); } static void convert_did_entry(GKeyFile *key_file, void *value) { char *vendor_str, *product_str, *version_str; uint16_t val; vendor_str = strchr(value, ' '); if (!vendor_str) return; *(vendor_str++) = 0; if (g_str_equal(value, "FFFF")) return; product_str = strchr(vendor_str, ' '); if (!product_str) return; *(product_str++) = 0; version_str = strchr(product_str, ' '); if (!version_str) return; *(version_str++) = 0; val = (uint16_t) strtol(value, NULL, 16); g_key_file_set_integer(key_file, "DeviceID", "Source", val); val = (uint16_t) strtol(vendor_str, NULL, 16); g_key_file_set_integer(key_file, "DeviceID", "Vendor", val); val = (uint16_t) strtol(product_str, NULL, 16); g_key_file_set_integer(key_file, "DeviceID", "Product", val); val = (uint16_t) strtol(version_str, NULL, 16); g_key_file_set_integer(key_file, "DeviceID", "Version", val); } static void convert_linkkey_entry(GKeyFile *key_file, void *value) { char *type_str, *length_str, *str; int val; type_str = strchr(value, ' '); if (!type_str) return; *(type_str++) = 0; length_str = strchr(type_str, ' '); if (!length_str) return; *(length_str++) = 0; str = g_strconcat("0x", value, NULL); g_key_file_set_string(key_file, "LinkKey", "Key", str); g_free(str); val = strtol(type_str, NULL, 16); g_key_file_set_integer(key_file, "LinkKey", "Type", val); val = strtol(length_str, NULL, 16); g_key_file_set_integer(key_file, "LinkKey", "PINLength", val); } static void convert_ltk_entry(GKeyFile *key_file, void *value) { char *auth_str, *rand_str, *str; int i, ret; unsigned char auth, central, enc_size; unsigned short ediv; auth_str = strchr(value, ' '); if (!auth_str) return; *(auth_str++) = 0; for (i = 0, rand_str = auth_str; i < 4; i++) { rand_str = strchr(rand_str, ' '); if (!rand_str || rand_str[1] == '\0') return; rand_str++; } ret = sscanf(auth_str, " %hhd %hhd %hhd %hd", &auth, ¢ral, &enc_size, &ediv); if (ret < 4) return; str = g_strconcat("0x", value, NULL); g_key_file_set_string(key_file, "LongTermKey", "Key", str); g_free(str); g_key_file_set_integer(key_file, "LongTermKey", "Authenticated", auth); g_key_file_set_integer(key_file, "LongTermKey", "EncSize", enc_size); g_key_file_set_integer(key_file, "LongTermKey", "EDiv", ediv); str = g_strconcat("0x", rand_str, NULL); g_key_file_set_string(key_file, "LongTermKey", "Rand", str); g_free(str); } static void convert_profiles_entry(GKeyFile *key_file, void *value) { g_strdelimit(value, " ", ';'); g_key_file_set_string(key_file, "General", "Services", value); } static void convert_appearances_entry(GKeyFile *key_file, void *value) { g_key_file_set_string(key_file, "General", "Appearance", value); } static void convert_entry(char *key, char *value, void *user_data) { struct device_converter *converter = user_data; char type = BDADDR_BREDR; char filename[PATH_MAX]; GKeyFile *key_file; GError *gerr = NULL; char *data; gsize length = 0; if (strchr(key, '#')) { key[17] = '\0'; type = key[18] - '0'; } if (bachk(key) != 0) return; if (converter->force == FALSE) { struct stat st; int err; create_filename(filename, PATH_MAX, "/%s/%s", converter->address, key); err = stat(filename, &st); if (err || !S_ISDIR(st.st_mode)) return; } create_filename(filename, PATH_MAX, "/%s/%s/info", converter->address, key); key_file = g_key_file_new(); if (!g_key_file_load_from_file(key_file, filename, 0, &gerr)) { error("Unable to load key file from %s: (%s)", filename, gerr->message); g_clear_error(&gerr); } set_device_type(key_file, type); converter->cb(key_file, value); data = g_key_file_to_data(key_file, &length, NULL); if (length > 0) { create_file(filename, 0600); if (!g_file_set_contents(filename, data, length, &gerr)) { error("Unable set contents for %s: (%s)", filename, gerr->message); g_error_free(gerr); } } g_free(data); g_key_file_free(key_file); } static void convert_file(char *file, char *address, void (*cb)(GKeyFile *key_file, void *value), gboolean force) { char filename[PATH_MAX]; struct device_converter converter; create_filename(filename, PATH_MAX, "/%s/%s", address, file); converter.address = address; converter.cb = cb; converter.force = force; textfile_foreach(filename, convert_entry, &converter); } static gboolean record_has_uuid(const sdp_record_t *rec, const char *profile_uuid) { sdp_list_t *pat; for (pat = rec->pattern; pat != NULL; pat = pat->next) { char *uuid; int ret; uuid = bt_uuid2string(pat->data); if (!uuid) continue; ret = strcasecmp(uuid, profile_uuid); free(uuid); if (ret == 0) return TRUE; } return FALSE; } static void store_attribute_uuid(GKeyFile *key_file, uint16_t start, uint16_t end, char *att_uuid, uuid_t uuid) { char handle[6], uuid_str[33]; int i; switch (uuid.type) { case SDP_UUID16: sprintf(uuid_str, "%4.4X", uuid.value.uuid16); break; case SDP_UUID32: sprintf(uuid_str, "%8.8X", uuid.value.uuid32); break; case SDP_UUID128: for (i = 0; i < 16; i++) sprintf(uuid_str + (i * 2), "%2.2X", uuid.value.uuid128.data[i]); break; default: uuid_str[0] = '\0'; } sprintf(handle, "%hu", start); g_key_file_set_string(key_file, handle, "UUID", att_uuid); g_key_file_set_string(key_file, handle, "Value", uuid_str); g_key_file_set_integer(key_file, handle, "EndGroupHandle", end); } static void store_sdp_record(char *local, char *peer, int handle, char *value) { char filename[PATH_MAX]; GKeyFile *key_file; GError *gerr = NULL; char handle_str[11]; char *data; gsize length = 0; create_filename(filename, PATH_MAX, "/%s/cache/%s", local, peer); key_file = g_key_file_new(); if (!g_key_file_load_from_file(key_file, filename, 0, &gerr)) { error("Unable to load key file from %s: (%s)", filename, gerr->message); g_clear_error(&gerr); } sprintf(handle_str, "0x%8.8X", handle); g_key_file_set_string(key_file, "ServiceRecords", handle_str, value); data = g_key_file_to_data(key_file, &length, NULL); if (length > 0) { create_file(filename, 0600); if (!g_file_set_contents(filename, data, length, &gerr)) { error("Unable set contents for %s: (%s)", filename, gerr->message); g_error_free(gerr); } } g_free(data); g_key_file_free(key_file); } static void convert_sdp_entry(char *key, char *value, void *user_data) { char *src_addr = user_data; char dst_addr[18]; char type = BDADDR_BREDR; int handle, ret; char filename[PATH_MAX]; GKeyFile *key_file; GError *gerr = NULL; struct stat st; sdp_record_t *rec; uuid_t uuid; char *att_uuid, *prim_uuid; uint16_t start = 0, end = 0, psm = 0; int err; char *data; gsize length = 0; ret = sscanf(key, "%17s#%hhu#%08X", dst_addr, &type, &handle); if (ret < 3) { ret = sscanf(key, "%17s#%08X", dst_addr, &handle); if (ret < 2) return; } if (bachk(dst_addr) != 0) return; /* Check if the device directory has been created as records should * only be converted for known devices */ create_filename(filename, PATH_MAX, "/%s/%s", src_addr, dst_addr); err = stat(filename, &st); if (err || !S_ISDIR(st.st_mode)) return; /* store device records in cache */ store_sdp_record(src_addr, dst_addr, handle, value); /* Retrieve device record and check if there is an * attribute entry in it */ sdp_uuid16_create(&uuid, ATT_UUID); att_uuid = bt_uuid2string(&uuid); sdp_uuid16_create(&uuid, GATT_PRIM_SVC_UUID); prim_uuid = bt_uuid2string(&uuid); rec = record_from_string(value); if (record_has_uuid(rec, att_uuid)) goto failed; /* TODO: Do this through btd_gatt_database */ if (!gatt_parse_record(rec, &uuid, &psm, &start, &end)) goto failed; create_filename(filename, PATH_MAX, "/%s/%s/attributes", src_addr, dst_addr); key_file = g_key_file_new(); if (!g_key_file_load_from_file(key_file, filename, 0, &gerr)) { error("Unable to load key file from %s: (%s)", filename, gerr->message); g_clear_error(&gerr); } store_attribute_uuid(key_file, start, end, prim_uuid, uuid); data = g_key_file_to_data(key_file, &length, NULL); if (length > 0) { create_file(filename, 0600); if (!g_file_set_contents(filename, data, length, &gerr)) { error("Unable set contents for %s: (%s)", filename, gerr->message); g_error_free(gerr); } } g_free(data); g_key_file_free(key_file); failed: sdp_record_free(rec); free(prim_uuid); free(att_uuid); } static void convert_primaries_entry(char *key, char *value, void *user_data) { char *address = user_data; int device_type = -1; uuid_t uuid; char **services, **service, *prim_uuid; char filename[PATH_MAX]; GKeyFile *key_file; GError *gerr = NULL; int ret; uint16_t start, end; char uuid_str[MAX_LEN_UUID_STR + 1]; char *data; gsize length = 0; if (strchr(key, '#')) { key[17] = '\0'; device_type = key[18] - '0'; } if (bachk(key) != 0) return; services = g_strsplit(value, " ", 0); if (services == NULL) return; sdp_uuid16_create(&uuid, GATT_PRIM_SVC_UUID); prim_uuid = bt_uuid2string(&uuid); create_filename(filename, PATH_MAX, "/%s/%s/attributes", address, key); key_file = g_key_file_new(); if (!g_key_file_load_from_file(key_file, filename, 0, &gerr)) { error("Unable to load key file from %s: (%s)", filename, gerr->message); g_clear_error(&gerr); } for (service = services; *service; service++) { ret = sscanf(*service, "%04hX#%04hX#%s", &start, &end, uuid_str); if (ret < 3) continue; bt_string2uuid(&uuid, uuid_str); sdp_uuid128_to_uuid(&uuid); store_attribute_uuid(key_file, start, end, prim_uuid, uuid); } g_strfreev(services); data = g_key_file_to_data(key_file, &length, NULL); if (length == 0) goto end; create_file(filename, 0600); if (!g_file_set_contents(filename, data, length, &gerr)) { error("Unable set contents for %s: (%s)", filename, gerr->message); g_clear_error(&gerr); } if (device_type < 0) goto end; g_free(data); g_key_file_free(key_file); create_filename(filename, PATH_MAX, "/%s/%s/info", address, key); key_file = g_key_file_new(); if (!g_key_file_load_from_file(key_file, filename, 0, &gerr)) { error("Unable to load key file from %s: (%s)", filename, gerr->message); g_clear_error(&gerr); } set_device_type(key_file, device_type); data = g_key_file_to_data(key_file, &length, NULL); if (length > 0) { create_file(filename, 0600); if (!g_file_set_contents(filename, data, length, &gerr)) { error("Unable set contents for %s: (%s)", filename, gerr->message); g_error_free(gerr); } } end: g_free(data); free(prim_uuid); g_key_file_free(key_file); } static void convert_ccc_entry(char *key, char *value, void *user_data) { char *src_addr = user_data; char dst_addr[18]; char type = BDADDR_BREDR; uint16_t handle; int ret, err; char filename[PATH_MAX]; GKeyFile *key_file; GError *gerr = NULL; struct stat st; char group[6]; char *data; gsize length = 0; ret = sscanf(key, "%17s#%hhu#%04hX", dst_addr, &type, &handle); if (ret < 3) return; if (bachk(dst_addr) != 0) return; /* Check if the device directory has been created as records should * only be converted for known devices */ create_filename(filename, PATH_MAX, "/%s/%s", src_addr, dst_addr); err = stat(filename, &st); if (err || !S_ISDIR(st.st_mode)) return; create_filename(filename, PATH_MAX, "/%s/%s/ccc", src_addr, dst_addr); key_file = g_key_file_new(); if (!g_key_file_load_from_file(key_file, filename, 0, &gerr)) { error("Unable to load key file from %s: (%s)", filename, gerr->message); g_clear_error(&gerr); } sprintf(group, "%hu", handle); g_key_file_set_string(key_file, group, "Value", value); data = g_key_file_to_data(key_file, &length, NULL); if (length > 0) { create_file(filename, 0600); if (!g_file_set_contents(filename, data, length, &gerr)) { error("Unable set contents for %s: (%s)", filename, gerr->message); g_error_free(gerr); } } g_free(data); g_key_file_free(key_file); } static void convert_gatt_entry(char *key, char *value, void *user_data) { char *src_addr = user_data; char dst_addr[18]; char type = BDADDR_BREDR; uint16_t handle; int ret, err; char filename[PATH_MAX]; GKeyFile *key_file; GError *gerr = NULL; struct stat st; char group[6]; char *data; gsize length = 0; ret = sscanf(key, "%17s#%hhu#%04hX", dst_addr, &type, &handle); if (ret < 3) return; if (bachk(dst_addr) != 0) return; /* Check if the device directory has been created as records should * only be converted for known devices */ create_filename(filename, PATH_MAX, "/%s/%s", src_addr, dst_addr); err = stat(filename, &st); if (err || !S_ISDIR(st.st_mode)) return; create_filename(filename, PATH_MAX, "/%s/%s/gatt", src_addr, dst_addr); key_file = g_key_file_new(); if (!g_key_file_load_from_file(key_file, filename, 0, &gerr)) { error("Unable to load key file from %s: (%s)", filename, gerr->message); g_clear_error(&gerr); } sprintf(group, "%hu", handle); g_key_file_set_string(key_file, group, "Value", value); data = g_key_file_to_data(key_file, &length, NULL); if (length > 0) { create_file(filename, 0600); if (!g_file_set_contents(filename, data, length, &gerr)) { error("Unable set contents for %s: (%s)", filename, gerr->message); g_error_free(gerr); } } g_free(data); g_key_file_free(key_file); } static void convert_proximity_entry(char *key, char *value, void *user_data) { char *src_addr = user_data; char *alert; char filename[PATH_MAX]; GKeyFile *key_file; GError *gerr = NULL; struct stat st; int err; char *data; gsize length = 0; if (!strchr(key, '#')) return; key[17] = '\0'; alert = &key[18]; if (bachk(key) != 0) return; /* Check if the device directory has been created as records should * only be converted for known devices */ create_filename(filename, PATH_MAX, "/%s/%s", src_addr, key); err = stat(filename, &st); if (err || !S_ISDIR(st.st_mode)) return; create_filename(filename, PATH_MAX, "/%s/%s/proximity", src_addr, key); key_file = g_key_file_new(); if (!g_key_file_load_from_file(key_file, filename, 0, &gerr)) { error("Unable to load key file from %s: (%s)", filename, gerr->message); g_clear_error(&gerr); } g_key_file_set_string(key_file, alert, "Level", value); data = g_key_file_to_data(key_file, &length, NULL); if (length > 0) { create_file(filename, 0600); if (!g_file_set_contents(filename, data, length, &gerr)) { error("Unable set contents for %s: (%s)", filename, gerr->message); g_error_free(gerr); } } g_free(data); g_key_file_free(key_file); } static void convert_device_storage(struct btd_adapter *adapter) { char filename[PATH_MAX]; char address[18]; ba2str(&adapter->bdaddr, address); /* Convert device's name cache */ create_filename(filename, PATH_MAX, "/%s/names", address); textfile_foreach(filename, convert_names_entry, address); /* Convert aliases */ convert_file("aliases", address, convert_aliases_entry, TRUE); /* Convert trusts */ convert_file("trusts", address, convert_trusts_entry, TRUE); /* Convert blocked */ convert_file("blocked", address, convert_blocked_entry, TRUE); /* Convert profiles */ convert_file("profiles", address, convert_profiles_entry, TRUE); /* Convert primaries */ create_filename(filename, PATH_MAX, "/%s/primaries", address); textfile_foreach(filename, convert_primaries_entry, address); /* Convert linkkeys */ convert_file("linkkeys", address, convert_linkkey_entry, TRUE); /* Convert longtermkeys */ convert_file("longtermkeys", address, convert_ltk_entry, TRUE); /* Convert classes */ convert_file("classes", address, convert_classes_entry, FALSE); /* Convert device ids */ convert_file("did", address, convert_did_entry, FALSE); /* Convert sdp */ create_filename(filename, PATH_MAX, "/%s/sdp", address); textfile_foreach(filename, convert_sdp_entry, address); /* Convert ccc */ create_filename(filename, PATH_MAX, "/%s/ccc", address); textfile_foreach(filename, convert_ccc_entry, address); /* Convert appearances */ convert_file("appearances", address, convert_appearances_entry, FALSE); /* Convert gatt */ create_filename(filename, PATH_MAX, "/%s/gatt", address); textfile_foreach(filename, convert_gatt_entry, address); /* Convert proximity */ create_filename(filename, PATH_MAX, "/%s/proximity", address); textfile_foreach(filename, convert_proximity_entry, address); } static void convert_config(struct btd_adapter *adapter, const char *filename, GKeyFile *key_file) { char address[18]; char str[MAX_NAME_LENGTH + 1]; char config_path[PATH_MAX]; int timeout; uint8_t mode; char *data; gsize length = 0; GError *gerr = NULL; ba2str(&adapter->bdaddr, address); create_filename(config_path, PATH_MAX, "/%s/config", address); if (read_pairable_timeout(address, &timeout) == 0) g_key_file_set_integer(key_file, "General", "PairableTimeout", timeout); if (read_discoverable_timeout(address, &timeout) == 0) g_key_file_set_integer(key_file, "General", "DiscoverableTimeout", timeout); if (read_on_mode(address, str, sizeof(str)) == 0) { mode = get_mode(str); g_key_file_set_boolean(key_file, "General", "Discoverable", mode == MODE_DISCOVERABLE); } if (read_local_name(&adapter->bdaddr, str) == 0) g_key_file_set_string(key_file, "General", "Alias", str); create_file(filename, 0600); data = g_key_file_to_data(key_file, &length, NULL); if (!g_file_set_contents(filename, data, length, &gerr)) { error("Unable set contents for %s: (%s)", filename, gerr->message); g_error_free(gerr); } g_free(data); } static void fix_storage(struct btd_adapter *adapter) { char filename[PATH_MAX]; char address[18]; char *converted; ba2str(&adapter->bdaddr, address); create_filename(filename, PATH_MAX, "/%s/config", address); converted = textfile_get(filename, "converted"); if (!converted) return; free(converted); textfile_del(filename, "converted"); create_filename(filename, PATH_MAX, "/%s/names", address); textfile_del(filename, "converted"); create_filename(filename, PATH_MAX, "/%s/aliases", address); textfile_del(filename, "converted"); create_filename(filename, PATH_MAX, "/%s/trusts", address); textfile_del(filename, "converted"); create_filename(filename, PATH_MAX, "/%s/blocked", address); textfile_del(filename, "converted"); create_filename(filename, PATH_MAX, "/%s/profiles", address); textfile_del(filename, "converted"); create_filename(filename, PATH_MAX, "/%s/primaries", address); textfile_del(filename, "converted"); create_filename(filename, PATH_MAX, "/%s/linkkeys", address); textfile_del(filename, "converted"); create_filename(filename, PATH_MAX, "/%s/longtermkeys", address); textfile_del(filename, "converted"); create_filename(filename, PATH_MAX, "/%s/classes", address); textfile_del(filename, "converted"); create_filename(filename, PATH_MAX, "/%s/did", address); textfile_del(filename, "converted"); create_filename(filename, PATH_MAX, "/%s/sdp", address); textfile_del(filename, "converted"); create_filename(filename, PATH_MAX, "/%s/ccc", address); textfile_del(filename, "converted"); create_filename(filename, PATH_MAX, "/%s/appearances", address); textfile_del(filename, "converted"); create_filename(filename, PATH_MAX, "/%s/gatt", address); textfile_del(filename, "converted"); create_filename(filename, PATH_MAX, "/%s/proximity", address); textfile_del(filename, "converted"); } static void load_config(struct btd_adapter *adapter) { GKeyFile *key_file; char filename[PATH_MAX]; struct stat st; GError *gerr = NULL; key_file = g_key_file_new(); create_filename(filename, PATH_MAX, "/%s/settings", btd_adapter_get_storage_dir(adapter)); if (stat(filename, &st) < 0) { convert_config(adapter, filename, key_file); convert_device_storage(adapter); } if (!g_key_file_load_from_file(key_file, filename, 0, &gerr)) { error("Unable to load key file from %s: (%s)", filename, gerr->message); g_clear_error(&gerr); } /* Get alias */ adapter->stored_alias = g_key_file_get_string(key_file, "General", "Alias", NULL); if (!adapter->stored_alias) { /* fallback */ adapter->stored_alias = g_key_file_get_string(key_file, "General", "Name", NULL); } /* Get pairable timeout */ adapter->pairable_timeout = g_key_file_get_integer(key_file, "General", "PairableTimeout", &gerr); if (gerr) { adapter->pairable_timeout = btd_opts.pairto; g_error_free(gerr); gerr = NULL; } /* Get discoverable mode */ adapter->stored_discoverable = g_key_file_get_boolean(key_file, "General", "Discoverable", &gerr); if (gerr) { adapter->stored_discoverable = false; g_error_free(gerr); gerr = NULL; } /* Get discoverable timeout */ adapter->discoverable_timeout = g_key_file_get_integer(key_file, "General", "DiscoverableTimeout", &gerr); if (gerr) { adapter->discoverable_timeout = btd_opts.discovto; g_error_free(gerr); gerr = NULL; } g_key_file_free(key_file); } static struct btd_adapter *btd_adapter_new(uint16_t index) { struct btd_adapter *adapter; int blocked; adapter = g_try_new0(struct btd_adapter, 1); if (!adapter) return NULL; adapter->dev_id = index; adapter->mgmt = mgmt_ref(mgmt_primary); adapter->pincode_requested = false; blocked = rfkill_get_blocked(index); if (blocked > 0) adapter->power_state = ADAPTER_POWER_STATE_OFF_BLOCKED; /* * Setup default configuration values. These are either adapter * defaults or from a system wide configuration file. * * Some value might be overwritten later on by adapter specific * configuration. This is to make sure that sane defaults are * always present. */ adapter->system_name = g_strdup(btd_opts.name); adapter->major_class = (btd_opts.class & 0x001f00) >> 8; adapter->minor_class = (btd_opts.class & 0x0000fc) >> 2; adapter->modalias = bt_modalias(btd_opts.did_source, btd_opts.did_vendor, btd_opts.did_product, btd_opts.did_version); adapter->discoverable_timeout = btd_opts.discovto; adapter->pairable_timeout = btd_opts.pairto; DBG("System name: %s", adapter->system_name); DBG("Major class: %u", adapter->major_class); DBG("Minor class: %u", adapter->minor_class); DBG("Modalias: %s", adapter->modalias); DBG("Discoverable timeout: %u seconds", adapter->discoverable_timeout); DBG("Pairable timeout: %u seconds", adapter->pairable_timeout); if (blocked > 0) DBG("Power state: %s", adapter_power_state_str(adapter->power_state)); adapter->auths = g_queue_new(); adapter->exps = queue_new(); adapter->exp_pending = queue_new(); return btd_adapter_ref(adapter); } static void adapter_remove(struct btd_adapter *adapter) { GSList *l; struct gatt_db *db; DBG("Removing adapter %s", adapter->path); g_slist_free(adapter->connect_list); adapter->connect_list = NULL; for (l = adapter->devices; l; l = l->next) { device_removed_drivers(adapter, l->data); device_remove(l->data, FALSE); } g_slist_free(adapter->devices); adapter->devices = NULL; discovery_cleanup(adapter, 0); unload_drivers(adapter); db = btd_gatt_database_get_db(adapter->database); gatt_db_unregister(db, adapter->db_id); adapter->db_id = 0; btd_gatt_database_destroy(adapter->database); adapter->database = NULL; btd_adv_manager_destroy(adapter->adv_manager); adapter->adv_manager = NULL; btd_adv_monitor_manager_destroy(adapter->adv_monitor_manager); adapter->adv_monitor_manager = NULL; btd_battery_provider_manager_destroy(adapter->battery_provider_manager); adapter->battery_provider_manager = NULL; g_slist_free(adapter->pin_callbacks); adapter->pin_callbacks = NULL; g_slist_free(adapter->msd_callbacks); adapter->msd_callbacks = NULL; queue_remove_all(adapter->exp_pending, NULL, NULL, cancel_exp_pending); } const char *adapter_get_path(struct btd_adapter *adapter) { if (!adapter) return NULL; return adapter->path; } const bdaddr_t *btd_adapter_get_address(struct btd_adapter *adapter) { return &adapter->bdaddr; } static void confirm_name_complete(uint8_t status, uint16_t length, const void *param, void *user_data) { struct btd_adapter *adapter = user_data; if (status != MGMT_STATUS_SUCCESS) { btd_error(adapter->dev_id, "Failed to confirm name for hci%u: %s (0x%02x)", adapter->dev_id, mgmt_errstr(status), status); return; } DBG("Confirm name complete for hci%u", adapter->dev_id); } static void confirm_name(struct btd_adapter *adapter, const bdaddr_t *bdaddr, uint8_t bdaddr_type, bool name_known) { struct mgmt_cp_confirm_name cp; char addr[18]; ba2str(bdaddr, addr); DBG("hci%d bdaddr %s name_known %u", adapter->dev_id, addr, name_known); memset(&cp, 0, sizeof(cp)); bacpy(&cp.addr.bdaddr, bdaddr); cp.addr.type = bdaddr_type; cp.name_known = name_known; /* * This timeout handling is needed since the kernel is stupid * and forgets to send a command complete response. However in * case of failures it does send a command status. */ if (!mgmt_reply_timeout(adapter->mgmt, MGMT_OP_CONFIRM_NAME, adapter->dev_id, sizeof(cp), &cp, confirm_name_complete, adapter, NULL, 2)) btd_error(adapter->dev_id, "Failed to confirm name for hci%u", adapter->dev_id); } static void adapter_msd_notify(struct btd_adapter *adapter, struct btd_device *dev, GSList *msd_list) { GSList *cb_l, *cb_next; GSList *msd_l, *msd_next; for (cb_l = adapter->msd_callbacks; cb_l != NULL; cb_l = cb_next) { btd_msd_cb_t cb = cb_l->data; cb_next = g_slist_next(cb_l); for (msd_l = msd_list; msd_l != NULL; msd_l = msd_next) { const struct eir_msd *msd = msd_l->data; msd_next = g_slist_next(msd_l); cb(adapter, dev, msd->company, msd->data, msd->data_len); } } } static bool is_filter_match(GSList *discovery_filter, struct eir_data *eir_data, int8_t rssi) { GSList *l, *m; bool got_match = false; for (l = discovery_filter; l != NULL && got_match != true; l = g_slist_next(l)) { struct discovery_client *client = l->data; struct discovery_filter *item = client->discovery_filter; /* * If one of currently running scans is regular scan, then * return all devices as matches */ if (!item) { got_match = true; continue; } /* if someone started discovery with empty uuids, he wants all * devices in given proximity. */ if (!item->uuids) got_match = true; else { for (m = item->uuids; m != NULL && got_match != true; m = g_slist_next(m)) { /* m->data contains string representation of * uuid. */ if (g_slist_find_custom(eir_data->services, m->data, g_strcmp) != NULL) got_match = true; } } if (got_match) { /* we have service match, check proximity */ if (item->rssi == DISTANCE_VAL_INVALID || item->rssi <= rssi || item->pathloss == DISTANCE_VAL_INVALID || (eir_data->tx_power != 127 && eir_data->tx_power - rssi <= item->pathloss)) return true; got_match = false; } } return got_match; } static void filter_duplicate_data(void *data, void *user_data) { struct discovery_client *client = data; bool *duplicate = user_data; if (*duplicate || !client->discovery_filter) return; *duplicate = client->discovery_filter->duplicate; } static bool device_is_discoverable(struct btd_adapter *adapter, struct eir_data *eir, const char *addr, uint8_t bdaddr_type) { GSList *l; bool discoverable; if (bdaddr_type == BDADDR_BREDR || adapter->filtered_discovery) discoverable = true; else discoverable = eir->flags & (EIR_LIM_DISC | EIR_GEN_DISC); /* * Mark as not discoverable if no client has requested discovery and * report has not set any discoverable flags. */ if (!adapter->discovery_list && !discoverable) return false; /* Do a prefix match for both address and name if pattern is set */ for (l = adapter->discovery_list; l; l = g_slist_next(l)) { struct discovery_client *client = l->data; struct discovery_filter *filter = client->discovery_filter; size_t pattern_len; if (!filter || !filter->pattern) continue; /* Reset discoverable if a client has a pattern filter */ discoverable = false; pattern_len = strlen(filter->pattern); if (!pattern_len) return true; if (!strncmp(filter->pattern, addr, pattern_len)) return true; if (eir->name && !strncmp(filter->pattern, eir->name, pattern_len)) return true; } return discoverable; } void btd_adapter_device_found(struct btd_adapter *adapter, const bdaddr_t *bdaddr, uint8_t bdaddr_type, int8_t rssi, uint32_t flags, const uint8_t *data, uint8_t data_len, bool monitoring) { struct btd_device *dev; struct bt_ad *ad = NULL; struct eir_data eir_data; bool name_known, discoverable; char addr[18]; bool confirm; bool legacy; bool not_connectable; bool name_resolve_failed; bool scan_rsp; bool duplicate = false; struct queue *matched_monitors = NULL; confirm = (flags & MGMT_DEV_FOUND_CONFIRM_NAME); legacy = (flags & MGMT_DEV_FOUND_LEGACY_PAIRING); not_connectable = (flags & MGMT_DEV_FOUND_NOT_CONNECTABLE); name_resolve_failed = (flags & MGMT_DEV_FOUND_NAME_REQUEST_FAILED); scan_rsp = (flags & MGMT_DEV_FOUND_SCAN_RSP); if (!btd_adv_monitor_offload_enabled(adapter->adv_monitor_manager) || (MGMT_VERSION(mgmt_version, mgmt_revision) < MGMT_VERSION(1, 22))) { if (bdaddr_type != BDADDR_BREDR) ad = bt_ad_new_with_data(data_len, data); /* During the background scanning, update the device only when * the data match at least one Adv monitor */ if (ad) { matched_monitors = btd_adv_monitor_content_filter( adapter->adv_monitor_manager, ad); bt_ad_unref(ad); ad = NULL; monitoring = matched_monitors ? true : false; } } if (!adapter->discovering && !monitoring) return; memset(&eir_data, 0, sizeof(eir_data)); eir_parse(&eir_data, data, data_len); ba2str(bdaddr, addr); discoverable = device_is_discoverable(adapter, &eir_data, addr, bdaddr_type); dev = btd_adapter_find_device(adapter, bdaddr, bdaddr_type); if (!dev) { /* In case of being just a scan response don't attempt to create * the device. */ if (scan_rsp) { eir_data_free(&eir_data); return; } /* Monitor Devices advertising Broadcast Announcements if the * adapter is capable of synchronizing to it. */ if (eir_get_service_data(&eir_data, BCAA_SERVICE_UUID) && btd_adapter_has_settings(adapter, MGMT_SETTING_ISO_SYNC_RECEIVER)) monitoring = true; if (!discoverable && !monitoring && not_connectable) { eir_data_free(&eir_data); return; } dev = adapter_create_device(adapter, bdaddr, bdaddr_type); } if (!dev) { btd_error(adapter->dev_id, "Unable to create object for found device %s", addr); eir_data_free(&eir_data); return; } device_update_last_seen(dev, bdaddr_type, !not_connectable); /* * FIXME: We need to check for non-zero flags first because * older kernels send separate adv_ind and scan_rsp. Newer * kernels send them merged, so once we know which mgmt version * supports this we can make the non-zero check conditional. */ if (bdaddr_type != BDADDR_BREDR && eir_data.flags && !(eir_data.flags & EIR_BREDR_UNSUP)) { device_set_bredr_support(dev); /* Update last seen for BR/EDR in case its flag is set */ device_update_last_seen(dev, BDADDR_BREDR, !not_connectable); } if (eir_data.name != NULL && eir_data.name_complete) device_store_cached_name(dev, eir_data.name); /* * Only skip devices that are not connected, are temporary, and there * is no active discovery session ongoing and no matched Adv monitors */ if (!btd_device_is_connected(dev) && (device_is_temporary(dev) && !adapter->discovery_list) && !monitoring) { eir_data_free(&eir_data); return; } /* If there is no matched Adv monitors, don't continue if not * discoverable or if active discovery filter don't match. */ if (!eir_data.rsi && !monitoring && (!discoverable || (adapter->filtered_discovery && !is_filter_match( adapter->discovery_list, &eir_data, rssi)))) { eir_data_free(&eir_data); return; } device_set_legacy(dev, legacy); if (name_resolve_failed) device_name_resolve_fail(dev); if (adapter->filtered_discovery) device_set_rssi_with_delta(dev, rssi, 0); else device_set_rssi(dev, rssi); if (eir_data.tx_power != 127) device_set_tx_power(dev, eir_data.tx_power); if (eir_data.appearance != 0) device_set_appearance(dev, eir_data.appearance); /* Report an unknown name to the kernel even if there is a short name * known, but still update the name with the known short name. */ name_known = device_name_known(dev); if (eir_data.name && (eir_data.name_complete || !name_known)) btd_device_device_set_name(dev, eir_data.name); if (eir_data.class != 0) device_set_class(dev, eir_data.class); if (eir_data.did_source || eir_data.did_vendor || eir_data.did_product || eir_data.did_version) btd_device_set_pnpid(dev, eir_data.did_source, eir_data.did_vendor, eir_data.did_product, eir_data.did_version); device_add_eir_uuids(dev, eir_data.services); if (adapter->discovery_list) g_slist_foreach(adapter->discovery_list, filter_duplicate_data, &duplicate); if (eir_data.msd_list) { device_set_manufacturer_data(dev, eir_data.msd_list, duplicate); adapter_msd_notify(adapter, dev, eir_data.msd_list); } if (eir_data.sd_list) device_set_service_data(dev, eir_data.sd_list, duplicate); if (eir_data.data_list) device_set_data(dev, eir_data.data_list, duplicate); if (bdaddr_type != BDADDR_BREDR) device_set_flags(dev, eir_data.flags); eir_data_free(&eir_data); /* After the device is updated, notify the matched Adv monitors */ if (matched_monitors) { btd_adv_monitor_notify_monitors(adapter->adv_monitor_manager, dev, rssi, matched_monitors); queue_destroy(matched_monitors, NULL); matched_monitors = NULL; } /* * Only if at least one client has requested discovery, maintain * list of found devices and name confirming for legacy devices. * Otherwise, this is an event from passive discovery and we * should check if the device needs connecting to. */ if (!adapter->discovery_list) goto connect_le; if (g_slist_find(adapter->discovery_found, dev)) return; /* If name is unknown but it's not allowed to resolve, don't send * MGMT_OP_CONFIRM_NAME. */ if (confirm && (name_known || device_is_name_resolve_allowed(dev))) confirm_name(adapter, bdaddr, bdaddr_type, name_known); adapter->discovery_found = g_slist_prepend(adapter->discovery_found, dev); return; connect_le: /* Ignore non-connectable events */ if (not_connectable) return; /* * If we're in the process of stopping passive scanning and * connecting another (or maybe even the same) LE device just * ignore this one. */ if (adapter->connect_le) return; /* * If kernel background scan is used then the kernel is * responsible for connecting. */ if (btd_has_kernel_features(KERNEL_CONN_CONTROL)) return; /* * If this is an LE device that's not connected and part of the * connect_list stop passive scanning so that a connection * attempt to it can be made */ if (bdaddr_type != BDADDR_BREDR && !btd_device_is_connected(dev) && g_slist_find(adapter->connect_list, dev)) { adapter->connect_le = dev; stop_passive_scanning(adapter); } } static void device_found_callback(uint16_t index, uint16_t length, const void *param, void *user_data) { const struct mgmt_ev_device_found *ev = param; struct btd_adapter *adapter = user_data; const uint8_t *eir; uint16_t eir_len; uint32_t flags; char addr[18]; if (length < sizeof(*ev)) { btd_error(adapter->dev_id, "Too short device found event (%u bytes)", length); return; } eir_len = btohs(ev->eir_len); if (length != sizeof(*ev) + eir_len) { btd_error(adapter->dev_id, "Device found event size mismatch (%u != %zu)", length, sizeof(*ev) + eir_len); return; } if (eir_len == 0) eir = NULL; else eir = ev->eir; flags = le32_to_cpu(ev->flags); ba2str(&ev->addr.bdaddr, addr); DBG("hci%u addr %s, rssi %d flags 0x%04x eir_len %u", index, addr, ev->rssi, flags, eir_len); btd_adapter_device_found(adapter, &ev->addr.bdaddr, ev->addr.type, ev->rssi, flags, eir, eir_len, false); } struct agent *adapter_get_agent(struct btd_adapter *adapter) { return agent_get(NULL); } static void adapter_remove_connection(struct btd_adapter *adapter, struct btd_device *device, uint8_t bdaddr_type) { bool remove_device = false; DBG(""); if (!g_slist_find(adapter->connections, device)) { btd_error(adapter->dev_id, "No matching connection for device"); return; } device_remove_connection(device, bdaddr_type, &remove_device); device_cancel_authentication(device, TRUE); /* If another bearer is still connected */ if (btd_device_bearer_is_connected(device)) return; adapter->connections = g_slist_remove(adapter->connections, device); if (remove_device) { const char *path = device_get_path(device); DBG("Removing temporary device %s", path); btd_adapter_remove_device(adapter, device); } } static void adapter_stop(struct btd_adapter *adapter) { /* check pending requests */ reply_pending_requests(adapter); cancel_passive_scanning(adapter); remove_discovery_list(adapter); discovery_cleanup(adapter, 0); adapter->filtered_discovery = false; adapter->no_scan_restart_delay = false; g_free(adapter->current_discovery_filter); adapter->current_discovery_filter = NULL; set_discovery_discoverable(adapter, false); adapter->discovering = false; while (adapter->connections) { struct btd_device *device = adapter->connections->data; uint8_t addr_type = btd_device_get_bdaddr_type(device); adapter_remove_connection(adapter, device, BDADDR_BREDR); if (addr_type != BDADDR_BREDR) adapter_remove_connection(adapter, device, addr_type); } g_dbus_emit_property_changed(dbus_conn, adapter->path, ADAPTER_INTERFACE, "Discovering"); if (adapter->dev_class) { /* the kernel should reset the class of device when powering * down, but it does not. So force it here ... */ adapter->dev_class = 0; g_dbus_emit_property_changed(dbus_conn, adapter->path, ADAPTER_INTERFACE, "Class"); } g_dbus_emit_property_changed(dbus_conn, adapter->path, ADAPTER_INTERFACE, "Powered"); g_dbus_emit_property_changed(dbus_conn, adapter->path, ADAPTER_INTERFACE, "PowerState"); DBG("adapter %s has been disabled", adapter->path); } int btd_register_adapter_driver(struct btd_adapter_driver *driver) { if (driver->experimental && !(g_dbus_get_flags() & G_DBUS_FLAG_ENABLE_EXPERIMENTAL)) { DBG("D-Bus experimental not enabled"); return -ENOTSUP; } adapter_drivers = g_slist_append(adapter_drivers, driver); if (driver->probe == NULL) return 0; adapter_foreach(probe_driver, driver); return 0; } static void unload_driver(struct btd_adapter *adapter, gpointer data) { struct btd_adapter_driver *driver = data; if (driver->remove) driver->remove(adapter); adapter->drivers = g_slist_remove(adapter->drivers, data); } void btd_unregister_adapter_driver(struct btd_adapter_driver *driver) { adapter_drivers = g_slist_remove(adapter_drivers, driver); adapter_foreach(unload_driver, driver); } static void agent_auth_cb(struct agent *agent, DBusError *derr, void *user_data) { struct btd_adapter *adapter = user_data; struct service_auth *auth = g_queue_pop_head(adapter->auths); if (!auth) { DBG("No pending authorization"); return; } auth->cb(derr, auth->user_data); if (auth->agent) agent_unref(auth->agent); g_free(auth); /* Stop processing if queue is empty */ if (g_queue_is_empty(adapter->auths)) { if (adapter->auth_idle_id > 0) g_source_remove(adapter->auth_idle_id); return; } if (adapter->auth_idle_id > 0) return; adapter->auth_idle_id = g_idle_add(process_auth_queue, adapter); } static gboolean process_auth_queue(gpointer user_data) { struct btd_adapter *adapter = user_data; DBusError err; adapter->auth_idle_id = 0; dbus_error_init(&err); dbus_set_error_const(&err, ERROR_INTERFACE ".Rejected", NULL); while (!g_queue_is_empty(adapter->auths)) { struct service_auth *auth = adapter->auths->head->data; struct btd_device *device = auth->device; /* Wait services to be resolved before asking authorization */ if (auth->svc_id > 0) return FALSE; if (!btd_adapter_is_uuid_allowed(adapter, auth->uuid)) { auth->cb(&err, auth->user_data); goto next; } if (btd_device_is_trusted(device) == TRUE) { auth->cb(NULL, auth->user_data); goto next; } /* If agent is set authorization is already ongoing */ if (auth->agent) return FALSE; auth->agent = agent_get(NULL); if (auth->agent == NULL) { btd_warn(adapter->dev_id, "Authentication attempt without agent"); auth->cb(&err, auth->user_data); goto next; } if (agent_authorize_service(auth->agent, device, auth->uuid, agent_auth_cb, adapter, NULL) < 0) { auth->cb(&err, auth->user_data); goto next; } break; next: if (auth->agent) agent_unref(auth->agent); g_free(auth); g_queue_pop_head(adapter->auths); } dbus_error_free(&err); return FALSE; } static void svc_complete(struct btd_device *dev, int err, void *user_data) { struct service_auth *auth = user_data; struct btd_adapter *adapter = auth->adapter; auth->svc_id = 0; if (adapter->auth_idle_id != 0) return; adapter->auth_idle_id = g_idle_add(process_auth_queue, adapter); } static int adapter_authorize(struct btd_adapter *adapter, const bdaddr_t *dst, const char *uuid, adapter_authorize_type check_for_connection, service_auth_cb cb, void *user_data) { struct service_auth *auth; struct btd_device *device; static guint id = 0; device = btd_adapter_find_device(adapter, dst, BDADDR_BREDR); if (!device) return 0; if (device_is_disconnecting(device)) { DBG("Authorization request while disconnecting"); return 0; } /* Device connected? */ if (check_for_connection && !g_slist_find(adapter->connections, device)) btd_error(adapter->dev_id, "Authorization request for non-connected device!?"); auth = g_try_new0(struct service_auth, 1); if (!auth) return 0; auth->cb = cb; auth->user_data = user_data; auth->uuid = uuid; auth->device = device; auth->adapter = adapter; auth->id = ++id; if (check_for_connection) auth->svc_id = device_wait_for_svc_complete(device, svc_complete, auth); else { if (adapter->auth_idle_id == 0) adapter->auth_idle_id = g_idle_add(process_auth_queue, adapter); } g_queue_push_tail(adapter->auths, auth); return auth->id; } guint btd_request_authorization(const bdaddr_t *src, const bdaddr_t *dst, const char *uuid, service_auth_cb cb, void *user_data) { struct btd_adapter *adapter; GSList *l; if (bacmp(src, BDADDR_ANY) != 0) { adapter = adapter_find(src); if (!adapter) return 0; return adapter_authorize(adapter, dst, uuid, ADAPTER_AUTHORIZE_CHECK_CONNECTED, cb, user_data); } for (l = adapters; l != NULL; l = g_slist_next(l)) { guint id; adapter = l->data; id = adapter_authorize(adapter, dst, uuid, ADAPTER_AUTHORIZE_CHECK_CONNECTED, cb, user_data); if (id != 0) return id; } return 0; } guint btd_request_authorization_cable_configured(const bdaddr_t *src, const bdaddr_t *dst, const char *uuid, service_auth_cb cb, void *user_data) { struct btd_adapter *adapter; if (bacmp(src, BDADDR_ANY) == 0) return 0; adapter = adapter_find(src); if (!adapter) return 0; return adapter_authorize(adapter, dst, uuid, ADAPTER_AUTHORIZE_DISCONNECTED, cb, user_data); } static struct service_auth *find_authorization(guint id) { GSList *l; GList *l2; for (l = adapters; l != NULL; l = g_slist_next(l)) { struct btd_adapter *adapter = l->data; for (l2 = adapter->auths->head; l2 != NULL; l2 = l2->next) { struct service_auth *auth = l2->data; if (auth->id == id) return auth; } } return NULL; } int btd_cancel_authorization(guint id) { struct service_auth *auth; auth = find_authorization(id); if (auth == NULL) return -EPERM; if (auth->svc_id > 0) device_remove_svc_complete_callback(auth->device, auth->svc_id); g_queue_remove(auth->adapter->auths, auth); if (auth->agent) { agent_cancel(auth->agent); agent_unref(auth->agent); } g_free(auth); return 0; } int btd_adapter_restore_powered(struct btd_adapter *adapter) { bool powered; powered = btd_adapter_get_powered(adapter); if (adapter->power_state == ADAPTER_POWER_STATE_OFF_BLOCKED && rfkill_get_blocked(adapter->dev_id) == 0) { adapter_set_power_state(adapter, powered ? ADAPTER_POWER_STATE_ON : ADAPTER_POWER_STATE_OFF); } if (powered) return 0; set_mode(adapter, MGMT_OP_SET_POWERED, 0x01); return 0; } int btd_adapter_set_blocked(struct btd_adapter *adapter) { adapter_set_power_state(adapter, ADAPTER_POWER_STATE_OFF_BLOCKED); return 0; } void btd_adapter_register_pin_cb(struct btd_adapter *adapter, btd_adapter_pin_cb_t cb) { adapter->pin_callbacks = g_slist_prepend(adapter->pin_callbacks, cb); } void btd_adapter_unregister_pin_cb(struct btd_adapter *adapter, btd_adapter_pin_cb_t cb) { adapter->pin_callbacks = g_slist_remove(adapter->pin_callbacks, cb); } void btd_adapter_unregister_msd_cb(struct btd_adapter *adapter, btd_msd_cb_t cb) { adapter->msd_callbacks = g_slist_remove(adapter->msd_callbacks, cb); } void btd_adapter_register_msd_cb(struct btd_adapter *adapter, btd_msd_cb_t cb) { adapter->msd_callbacks = g_slist_prepend(adapter->msd_callbacks, cb); } int btd_adapter_set_fast_connectable(struct btd_adapter *adapter, gboolean enable) { if (!btd_adapter_get_powered(adapter)) return -EINVAL; set_mode(adapter, MGMT_OP_SET_FAST_CONNECTABLE, enable ? 0x01 : 0x00); return 0; } int btd_adapter_read_clock(struct btd_adapter *adapter, const bdaddr_t *bdaddr, int which, int timeout, uint32_t *clock, uint16_t *accuracy) { if (!btd_adapter_get_powered(adapter)) return -EINVAL; return -ENOSYS; } int btd_adapter_remove_bonding(struct btd_adapter *adapter, const bdaddr_t *bdaddr, uint8_t bdaddr_type) { struct mgmt_cp_unpair_device cp; memset(&cp, 0, sizeof(cp)); bacpy(&cp.addr.bdaddr, bdaddr); cp.addr.type = bdaddr_type; cp.disconnect = 1; if (mgmt_send(adapter->mgmt, MGMT_OP_UNPAIR_DEVICE, adapter->dev_id, sizeof(cp), &cp, NULL, NULL, NULL) > 0) return 0; return -EIO; } static void pincode_reply_complete(uint8_t status, uint16_t length, const void *param, void *user_data) { struct btd_device *device = user_data; /* If the MGMT_OP_PIN_CODE_REPLY command is acknowledged, move the * starting time to that point. This give a better sense of time * evaluating the pincode. */ device_bonding_restart_timer(device); } int btd_adapter_pincode_reply(struct btd_adapter *adapter, const bdaddr_t *bdaddr, const char *pin, size_t pin_len) { struct btd_device *device; unsigned int id; char addr[18]; ba2str(bdaddr, addr); DBG("hci%u addr %s pinlen %zu", adapter->dev_id, addr, pin_len); if (pin == NULL) { struct mgmt_cp_pin_code_neg_reply cp; memset(&cp, 0, sizeof(cp)); bacpy(&cp.addr.bdaddr, bdaddr); cp.addr.type = BDADDR_BREDR; id = mgmt_reply(adapter->mgmt, MGMT_OP_PIN_CODE_NEG_REPLY, adapter->dev_id, sizeof(cp), &cp, NULL, NULL, NULL); } else { struct mgmt_cp_pin_code_reply cp; if (pin_len > 16) return -EINVAL; memset(&cp, 0, sizeof(cp)); bacpy(&cp.addr.bdaddr, bdaddr); cp.addr.type = BDADDR_BREDR; cp.pin_len = pin_len; memcpy(cp.pin_code, pin, pin_len); /* Since a pincode was requested, update the starting time to * the point where the pincode is provided. */ device = btd_adapter_find_device(adapter, bdaddr, BDADDR_BREDR); device_bonding_restart_timer(device); id = mgmt_reply(adapter->mgmt, MGMT_OP_PIN_CODE_REPLY, adapter->dev_id, sizeof(cp), &cp, pincode_reply_complete, device, NULL); } if (id == 0) return -EIO; return 0; } int btd_adapter_confirm_reply(struct btd_adapter *adapter, const bdaddr_t *bdaddr, uint8_t bdaddr_type, gboolean success) { struct mgmt_cp_user_confirm_reply cp; uint16_t opcode; char addr[18]; ba2str(bdaddr, addr); DBG("hci%u addr %s success %d", adapter->dev_id, addr, success); if (success) opcode = MGMT_OP_USER_CONFIRM_REPLY; else opcode = MGMT_OP_USER_CONFIRM_NEG_REPLY; memset(&cp, 0, sizeof(cp)); bacpy(&cp.addr.bdaddr, bdaddr); cp.addr.type = bdaddr_type; if (mgmt_reply(adapter->mgmt, opcode, adapter->dev_id, sizeof(cp), &cp, NULL, NULL, NULL) > 0) return 0; return -EIO; } static void user_confirm_request_callback(uint16_t index, uint16_t length, const void *param, void *user_data) { const struct mgmt_ev_user_confirm_request *ev = param; struct btd_adapter *adapter = user_data; struct btd_device *device; char addr[18]; int err; if (length < sizeof(*ev)) { btd_error(adapter->dev_id, "Too small user confirm request event"); return; } ba2str(&ev->addr.bdaddr, addr); DBG("hci%u %s confirm_hint %u", adapter->dev_id, addr, ev->confirm_hint); device = btd_adapter_get_device(adapter, &ev->addr.bdaddr, ev->addr.type); if (!device) { btd_error(adapter->dev_id, "Unable to get device object for %s", addr); return; } err = device_confirm_passkey(device, ev->addr.type, btohl(ev->value), ev->confirm_hint); if (err < 0) { btd_error(adapter->dev_id, "device_confirm_passkey: %s", strerror(-err)); btd_adapter_confirm_reply(adapter, &ev->addr.bdaddr, ev->addr.type, FALSE); } } int btd_adapter_passkey_reply(struct btd_adapter *adapter, const bdaddr_t *bdaddr, uint8_t bdaddr_type, uint32_t passkey) { unsigned int id; char addr[18]; ba2str(bdaddr, addr); DBG("hci%u addr %s passkey %06u", adapter->dev_id, addr, passkey); if (passkey == INVALID_PASSKEY) { struct mgmt_cp_user_passkey_neg_reply cp; memset(&cp, 0, sizeof(cp)); bacpy(&cp.addr.bdaddr, bdaddr); cp.addr.type = bdaddr_type; id = mgmt_reply(adapter->mgmt, MGMT_OP_USER_PASSKEY_NEG_REPLY, adapter->dev_id, sizeof(cp), &cp, NULL, NULL, NULL); } else { struct mgmt_cp_user_passkey_reply cp; memset(&cp, 0, sizeof(cp)); bacpy(&cp.addr.bdaddr, bdaddr); cp.addr.type = bdaddr_type; cp.passkey = htobl(passkey); id = mgmt_reply(adapter->mgmt, MGMT_OP_USER_PASSKEY_REPLY, adapter->dev_id, sizeof(cp), &cp, NULL, NULL, NULL); } if (id == 0) return -EIO; return 0; } static void user_passkey_request_callback(uint16_t index, uint16_t length, const void *param, void *user_data) { const struct mgmt_ev_user_passkey_request *ev = param; struct btd_adapter *adapter = user_data; struct btd_device *device; char addr[18]; int err; if (length < sizeof(*ev)) { btd_error(adapter->dev_id, "Too small passkey request event"); return; } ba2str(&ev->addr.bdaddr, addr); DBG("hci%u %s", index, addr); device = btd_adapter_get_device(adapter, &ev->addr.bdaddr, ev->addr.type); if (!device) { btd_error(adapter->dev_id, "Unable to get device object for %s", addr); return; } err = device_request_passkey(device, ev->addr.type); if (err < 0) { btd_error(adapter->dev_id, "device_request_passkey: %s", strerror(-err)); btd_adapter_passkey_reply(adapter, &ev->addr.bdaddr, ev->addr.type, INVALID_PASSKEY); } } static void user_passkey_notify_callback(uint16_t index, uint16_t length, const void *param, void *user_data) { const struct mgmt_ev_passkey_notify *ev = param; struct btd_adapter *adapter = user_data; struct btd_device *device; uint32_t passkey; char addr[18]; int err; if (length < sizeof(*ev)) { btd_error(adapter->dev_id, "Too small passkey notify event"); return; } ba2str(&ev->addr.bdaddr, addr); DBG("hci%u %s", index, addr); device = btd_adapter_get_device(adapter, &ev->addr.bdaddr, ev->addr.type); if (!device) { btd_error(adapter->dev_id, "Unable to get device object for %s", addr); return; } passkey = get_le32(&ev->passkey); DBG("passkey %06u entered %u", passkey, ev->entered); err = device_notify_passkey(device, ev->addr.type, passkey, ev->entered); if (err < 0) btd_error(adapter->dev_id, "device_notify_passkey: %s", strerror(-err)); } struct btd_adapter_pin_cb_iter *btd_adapter_pin_cb_iter_new( struct btd_adapter *adapter) { struct btd_adapter_pin_cb_iter *iter = g_new0(struct btd_adapter_pin_cb_iter, 1); iter->it = adapter->pin_callbacks; iter->attempt = 1; return iter; } void btd_adapter_pin_cb_iter_free(struct btd_adapter_pin_cb_iter *iter) { g_free(iter); } bool btd_adapter_pin_cb_iter_end(struct btd_adapter_pin_cb_iter *iter) { return iter->it == NULL && iter->attempt == 0; } static ssize_t btd_adapter_pin_cb_iter_next( struct btd_adapter_pin_cb_iter *iter, struct btd_adapter *adapter, struct btd_device *device, char *pin_buf, bool *display) { btd_adapter_pin_cb_t cb; ssize_t ret; while (iter->it != NULL) { cb = iter->it->data; ret = cb(adapter, device, pin_buf, display, iter->attempt); iter->attempt++; if (ret > 0) return ret; iter->attempt = 1; iter->it = g_slist_next(iter->it); } iter->attempt = 0; return 0; } static void pin_code_request_callback(uint16_t index, uint16_t length, const void *param, void *user_data) { const struct mgmt_ev_pin_code_request *ev = param; struct btd_adapter *adapter = user_data; struct btd_device *device; bool display = false; char pin[17]; ssize_t pinlen; char addr[18]; int err; struct btd_adapter_pin_cb_iter *iter; if (length < sizeof(*ev)) { btd_error(adapter->dev_id, "Too small PIN code request event"); return; } ba2str(&ev->addr.bdaddr, addr); DBG("hci%u %s", adapter->dev_id, addr); device = btd_adapter_get_device(adapter, &ev->addr.bdaddr, ev->addr.type); if (!device) { btd_error(adapter->dev_id, "Unable to get device object for %s", addr); return; } /* Flag the request of a pincode to allow a bonding retry. */ adapter->pincode_requested = true; memset(pin, 0, sizeof(pin)); iter = device_bonding_iter(device); if (iter == NULL) pinlen = 0; else pinlen = btd_adapter_pin_cb_iter_next(iter, adapter, device, pin, &display); if (pinlen > 0 && (!ev->secure || pinlen == 16)) { if (display && device_is_bonding(device, NULL)) { err = device_notify_pincode(device, ev->secure, pin); if (err < 0) { btd_error(adapter->dev_id, "device_notify_pin: %s", strerror(-err)); btd_adapter_pincode_reply(adapter, &ev->addr.bdaddr, NULL, 0); } } else { btd_adapter_pincode_reply(adapter, &ev->addr.bdaddr, pin, pinlen); } return; } err = device_request_pincode(device, ev->secure); if (err < 0) { btd_error(adapter->dev_id, "device_request_pin: %s", strerror(-err)); btd_adapter_pincode_reply(adapter, &ev->addr.bdaddr, NULL, 0); } } int adapter_cancel_bonding(struct btd_adapter *adapter, const bdaddr_t *bdaddr, uint8_t addr_type) { struct mgmt_addr_info cp; char addr[18]; ba2str(bdaddr, addr); DBG("hci%u bdaddr %s type %u", adapter->dev_id, addr, addr_type); memset(&cp, 0, sizeof(cp)); bacpy(&cp.bdaddr, bdaddr); cp.type = addr_type; if (mgmt_reply(adapter->mgmt, MGMT_OP_CANCEL_PAIR_DEVICE, adapter->dev_id, sizeof(cp), &cp, NULL, NULL, NULL) > 0) return 0; return -EIO; } static void check_oob_bonding_complete(struct btd_adapter *adapter, const bdaddr_t *bdaddr, uint8_t status) { if (!adapter->oob_handler || !adapter->oob_handler->bonding_cb) return; if (bacmp(bdaddr, &adapter->oob_handler->remote_addr) != 0) return; adapter->oob_handler->bonding_cb(adapter, bdaddr, status, adapter->oob_handler->user_data); g_free(adapter->oob_handler); adapter->oob_handler = NULL; } static void bonding_complete(struct btd_adapter *adapter, const bdaddr_t *bdaddr, uint8_t addr_type, uint8_t status) { struct btd_device *device; if (status == 0) device = btd_adapter_get_device(adapter, bdaddr, addr_type); else device = btd_adapter_find_device(adapter, bdaddr, addr_type); if (device != NULL) device_bonding_complete(device, addr_type, status); resume_discovery(adapter); check_oob_bonding_complete(adapter, bdaddr, status); } /* bonding_attempt_complete() handles the end of a "bonding attempt" checking if * it should begin a new attempt or complete the bonding. */ static void bonding_attempt_complete(struct btd_adapter *adapter, const bdaddr_t *bdaddr, uint8_t addr_type, uint8_t status) { struct btd_device *device; char addr[18]; ba2str(bdaddr, addr); DBG("hci%u bdaddr %s type %u status 0x%x", adapter->dev_id, addr, addr_type, status); if (status == 0) device = btd_adapter_get_device(adapter, bdaddr, addr_type); else device = btd_adapter_find_device(adapter, bdaddr, addr_type); if (status == MGMT_STATUS_AUTH_FAILED && adapter->pincode_requested) { /* On faliure, issue a bonding_retry if possible. */ if (device != NULL) { if (device_bonding_attempt_retry(device) == 0) return; } } /* Ignore disconnects during retry. */ if (status == MGMT_STATUS_DISCONNECTED && device && device_is_retrying(device)) return; /* In any other case, finish the bonding. */ bonding_complete(adapter, bdaddr, addr_type, status); } struct pair_device_data { struct btd_adapter *adapter; bdaddr_t bdaddr; uint8_t addr_type; }; static void free_pair_device_data(void *user_data) { struct pair_device_data *data = user_data; g_free(data); } static void pair_device_complete(uint8_t status, uint16_t length, const void *param, void *user_data) { const struct mgmt_rp_pair_device *rp = param; struct pair_device_data *data = user_data; struct btd_adapter *adapter = data->adapter; DBG("%s (0x%02x)", mgmt_errstr(status), status); /* Workaround for a kernel bug * * Broken kernels may reply to device pairing command with command * status instead of command complete event e.g. if adapter was not * powered. */ if (status != MGMT_STATUS_SUCCESS && length < sizeof(*rp)) { btd_error(adapter->dev_id, "Pair device failed: %s (0x%02x)", mgmt_errstr(status), status); bonding_attempt_complete(adapter, &data->bdaddr, data->addr_type, status); return; } if (length < sizeof(*rp)) { btd_error(adapter->dev_id, "Too small pair device response"); return; } bonding_attempt_complete(adapter, &rp->addr.bdaddr, rp->addr.type, status); } int adapter_create_bonding(struct btd_adapter *adapter, const bdaddr_t *bdaddr, uint8_t addr_type, uint8_t io_cap) { suspend_discovery(adapter); return adapter_bonding_attempt(adapter, bdaddr, addr_type, io_cap); } /* Starts a new bonding attempt in a fresh new bonding_req or a retried one. */ int adapter_bonding_attempt(struct btd_adapter *adapter, const bdaddr_t *bdaddr, uint8_t addr_type, uint8_t io_cap) { struct mgmt_cp_pair_device cp; char addr[18]; struct pair_device_data *data; unsigned int id; ba2str(bdaddr, addr); DBG("hci%u bdaddr %s type %d io_cap 0x%02x", adapter->dev_id, addr, addr_type, io_cap); /* Reset the pincode_requested flag for a new bonding attempt. */ adapter->pincode_requested = false; memset(&cp, 0, sizeof(cp)); bacpy(&cp.addr.bdaddr, bdaddr); cp.addr.type = addr_type; cp.io_cap = io_cap; data = g_new0(struct pair_device_data, 1); data->adapter = adapter; bacpy(&data->bdaddr, bdaddr); data->addr_type = addr_type; /* Due to a bug in the kernel it is possible that a LE pairing * request never times out. Therefore, add a timer to clean up * if no response arrives */ id = mgmt_send_timeout(adapter->mgmt, MGMT_OP_PAIR_DEVICE, adapter->dev_id, sizeof(cp), &cp, pair_device_complete, data, free_pair_device_data, BONDING_TIMEOUT); if (id == 0) { btd_error(adapter->dev_id, "Failed to pair %s for hci%u", addr, adapter->dev_id); free_pair_device_data(data); return -EIO; } return 0; } static void disconnect_notify(struct btd_device *dev, uint8_t reason) { GSList *l; for (l = disconnect_list; l; l = g_slist_next(l)) { btd_disconnect_cb disconnect_cb = l->data; disconnect_cb(dev, reason); } } static void dev_disconnected(struct btd_adapter *adapter, const struct mgmt_addr_info *addr, uint8_t reason) { struct btd_device *device; char dst[18]; ba2str(&addr->bdaddr, dst); DBG("Device %s disconnected, reason %u", dst, reason); device = btd_adapter_find_device(adapter, &addr->bdaddr, addr->type); if (device) { adapter_remove_connection(adapter, device, addr->type); disconnect_notify(device, reason); } bonding_attempt_complete(adapter, &addr->bdaddr, addr->type, MGMT_STATUS_DISCONNECTED); } void btd_add_disconnect_cb(btd_disconnect_cb func) { disconnect_list = g_slist_append(disconnect_list, func); } void btd_remove_disconnect_cb(btd_disconnect_cb func) { disconnect_list = g_slist_remove(disconnect_list, func); } static void disconnect_complete(uint8_t status, uint16_t length, const void *param, void *user_data) { const struct mgmt_rp_disconnect *rp = param; struct btd_adapter *adapter = user_data; if (status == MGMT_STATUS_NOT_CONNECTED) { btd_warn(adapter->dev_id, "Disconnecting failed: already disconnected"); } else if (status != MGMT_STATUS_SUCCESS) { btd_error(adapter->dev_id, "Failed to disconnect device: %s (0x%02x)", mgmt_errstr(status), status); return; } if (length < sizeof(*rp)) { btd_error(adapter->dev_id, "Too small device disconnect response"); return; } dev_disconnected(adapter, &rp->addr, MGMT_DEV_DISCONN_LOCAL_HOST); } int btd_adapter_disconnect_device(struct btd_adapter *adapter, const bdaddr_t *bdaddr, uint8_t bdaddr_type) { struct mgmt_cp_disconnect cp; memset(&cp, 0, sizeof(cp)); bacpy(&cp.addr.bdaddr, bdaddr); cp.addr.type = bdaddr_type; if (mgmt_send(adapter->mgmt, MGMT_OP_DISCONNECT, adapter->dev_id, sizeof(cp), &cp, disconnect_complete, adapter, NULL) > 0) return 0; return -EIO; } static void auth_failed_callback(uint16_t index, uint16_t length, const void *param, void *user_data) { const struct mgmt_ev_auth_failed *ev = param; struct btd_adapter *adapter = user_data; if (length < sizeof(*ev)) { btd_error(adapter->dev_id, "Too small auth failed mgmt event"); return; } bonding_attempt_complete(adapter, &ev->addr.bdaddr, ev->addr.type, ev->status); } static void store_link_key(struct btd_adapter *adapter, struct btd_device *device, const uint8_t *key, uint8_t type, uint8_t pin_length) { char device_addr[18]; char filename[PATH_MAX]; GKeyFile *key_file; GError *gerr = NULL; gsize length = 0; char key_str[33]; char *str; int i; ba2str(device_get_address(device), device_addr); create_filename(filename, PATH_MAX, "/%s/%s/info", btd_adapter_get_storage_dir(adapter), device_addr); create_file(filename, 0600); key_file = g_key_file_new(); if (!g_key_file_load_from_file(key_file, filename, 0, &gerr)) { error("Unable to load key file from %s: (%s)", filename, gerr->message); g_error_free(gerr); g_key_file_free(key_file); return; } for (i = 0; i < 16; i++) sprintf(key_str + (i * 2), "%2.2X", key[i]); g_key_file_set_string(key_file, "LinkKey", "Key", key_str); g_key_file_set_integer(key_file, "LinkKey", "Type", type); g_key_file_set_integer(key_file, "LinkKey", "PINLength", pin_length); str = g_key_file_to_data(key_file, &length, NULL); if (!g_file_set_contents(filename, str, length, &gerr)) { error("Unable set contents for %s: (%s)", filename, gerr->message); g_error_free(gerr); } g_free(str); g_key_file_free(key_file); } static void new_link_key_callback(uint16_t index, uint16_t length, const void *param, void *user_data) { const struct mgmt_ev_new_link_key *ev = param; const struct mgmt_addr_info *addr = &ev->key.addr; struct btd_adapter *adapter = user_data; struct btd_device *device; char dst[18]; if (length < sizeof(*ev)) { btd_error(adapter->dev_id, "Too small new link key event"); return; } ba2str(&addr->bdaddr, dst); DBG("hci%u new key for %s type %u pin_len %u store_hint %u", adapter->dev_id, dst, ev->key.type, ev->key.pin_len, ev->store_hint); if (ev->key.pin_len > 16) { btd_error(adapter->dev_id, "Invalid PIN length (%u) in new_key event", ev->key.pin_len); return; } device = btd_adapter_get_device(adapter, &addr->bdaddr, addr->type); if (!device) { btd_error(adapter->dev_id, "Unable to get device object for %s", dst); return; } if (ev->store_hint) { const struct mgmt_link_key_info *key = &ev->key; store_link_key(adapter, device, key->val, key->type, key->pin_len); device_set_bonded(device, BDADDR_BREDR); } bonding_complete(adapter, &addr->bdaddr, addr->type, 0); } static void store_ltk_group(struct btd_adapter *adapter, const bdaddr_t *peer, uint8_t bdaddr_type, const unsigned char *key, const char *group, uint8_t authenticated, uint8_t enc_size, uint16_t ediv, uint64_t rand) { char device_addr[18]; char filename[PATH_MAX]; GKeyFile *key_file; GError *gerr = NULL; char key_str[33]; gsize length = 0; char *str; int i; ba2str(peer, device_addr); create_filename(filename, PATH_MAX, "/%s/%s/info", btd_adapter_get_storage_dir(adapter), device_addr); key_file = g_key_file_new(); if (!g_key_file_load_from_file(key_file, filename, 0, &gerr)) { error("Unable to load key file from %s: (%s)", filename, gerr->message); g_clear_error(&gerr); } for (i = 0; i < 16; i++) sprintf(key_str + (i * 2), "%2.2X", key[i]); g_key_file_set_string(key_file, group, "Key", key_str); g_key_file_set_integer(key_file, group, "Authenticated", authenticated); g_key_file_set_integer(key_file, group, "EncSize", enc_size); g_key_file_set_integer(key_file, group, "EDiv", ediv); g_key_file_set_uint64(key_file, group, "Rand", rand); create_file(filename, 0600); str = g_key_file_to_data(key_file, &length, NULL); if (!g_file_set_contents(filename, str, length, &gerr)) { error("Unable set contents for %s: (%s)", filename, gerr->message); g_error_free(gerr); } g_free(str); g_key_file_free(key_file); } static void store_longtermkey(struct btd_adapter *adapter, const bdaddr_t *peer, uint8_t bdaddr_type, const unsigned char *key, uint8_t central, uint8_t authenticated, uint8_t enc_size, uint16_t ediv, uint64_t rand) { if (central != 0x00 && central != 0x01) { error("Unsupported LTK type %u", central); return; } if (central) { store_ltk_group(adapter, peer, bdaddr_type, key, "LongTermKey", authenticated, enc_size, ediv, rand); } else { /* Peripheral* is the proper term, but for now keep duplicates * so it won't break when user up/downgrades. Remove the other * term after a few releases. */ store_ltk_group(adapter, peer, bdaddr_type, key, "PeripheralLongTermKey", authenticated, enc_size, ediv, rand); store_ltk_group(adapter, peer, bdaddr_type, key, "SlaveLongTermKey", authenticated, enc_size, ediv, rand); } } static void new_long_term_key_callback(uint16_t index, uint16_t length, const void *param, void *user_data) { const struct mgmt_ev_new_long_term_key *ev = param; const struct mgmt_addr_info *addr = &ev->key.addr; struct btd_adapter *adapter = user_data; struct btd_device *device; bool persistent; char dst[18]; if (length < sizeof(*ev)) { btd_error(adapter->dev_id, "Too small long term key event"); return; } ba2str(&addr->bdaddr, dst); DBG("hci%u new LTK for %s type %u enc_size %u", adapter->dev_id, dst, ev->key.type, ev->key.enc_size); device = btd_adapter_get_device(adapter, &addr->bdaddr, addr->type); if (!device) { btd_error(adapter->dev_id, "Unable to get device object for %s", dst); return; } /* * Some older kernel versions set store_hint for long term keys * from resolvable and unresolvable random addresses, but there * is no point in storing these. Next time around the device * address will be invalid. * * So only for identity addresses (public and static random) use * the store_hint as an indication if the long term key should * be persistently stored. * */ if (addr->type == BDADDR_LE_RANDOM && (addr->bdaddr.b[5] & 0xc0) != 0xc0) persistent = false; else persistent = !!ev->store_hint; if (persistent) { const struct mgmt_ltk_info *key = &ev->key; uint16_t ediv; uint64_t rand; ediv = le16_to_cpu(key->ediv); rand = le64_to_cpu(key->rand); store_longtermkey(adapter, &key->addr.bdaddr, key->addr.type, key->val, key->central, key->type, key->enc_size, ediv, rand); device_set_bonded(device, addr->type); } device_set_ltk(device, ev->key.val, ev->key.central, ev->key.enc_size); bonding_complete(adapter, &addr->bdaddr, addr->type, 0); } static void new_csrk_callback(uint16_t index, uint16_t length, const void *param, void *user_data) { const struct mgmt_ev_new_csrk *ev = param; const struct mgmt_addr_info *addr = &ev->key.addr; const struct mgmt_csrk_info *key = &ev->key; struct btd_adapter *adapter = user_data; struct btd_device *device; char dst[18]; if (length < sizeof(*ev)) { btd_error(adapter->dev_id, "Too small CSRK event"); return; } ba2str(&addr->bdaddr, dst); DBG("hci%u new CSRK for %s type %u", adapter->dev_id, dst, ev->key.type); device = btd_adapter_get_device(adapter, &addr->bdaddr, addr->type); if (!device) { btd_error(adapter->dev_id, "Unable to get device object for %s", dst); return; } device_set_csrk(device, key->val, 0, key->type, ev->store_hint); } static void store_irk(struct btd_adapter *adapter, const bdaddr_t *peer, uint8_t bdaddr_type, const unsigned char *key) { char device_addr[18]; char filename[PATH_MAX]; GKeyFile *key_file; GError *gerr = NULL; char *store_data; char str[33]; size_t length = 0; int i; ba2str(peer, device_addr); create_filename(filename, PATH_MAX, "/%s/%s/info", btd_adapter_get_storage_dir(adapter), device_addr); create_file(filename, 0600); key_file = g_key_file_new(); if (!g_key_file_load_from_file(key_file, filename, 0, &gerr)) { error("Unable to load key file from %s: (%s)", filename, gerr->message); g_error_free(gerr); g_key_file_free(key_file); return; } for (i = 0; i < 16; i++) sprintf(str + (i * 2), "%2.2X", key[i]); g_key_file_set_string(key_file, "IdentityResolvingKey", "Key", str); store_data = g_key_file_to_data(key_file, &length, NULL); if (!g_file_set_contents(filename, store_data, length, &gerr)) { error("Unable set contents for %s: (%s)", filename, gerr->message); g_error_free(gerr); } g_free(store_data); g_key_file_free(key_file); } static void new_irk_callback(uint16_t index, uint16_t length, const void *param, void *user_data) { const struct mgmt_ev_new_irk *ev = param; const struct mgmt_addr_info *addr = &ev->key.addr; const struct mgmt_irk_info *irk = &ev->key; struct btd_adapter *adapter = user_data; struct btd_device *device, *duplicate; bool persistent; char dst[18], rpa[18]; if (length < sizeof(*ev)) { btd_error(adapter->dev_id, "Too small New IRK event"); return; } ba2str(&addr->bdaddr, dst); ba2str(&ev->rpa, rpa); DBG("hci%u new IRK for %s RPA %s", adapter->dev_id, dst, rpa); if (bacmp(&ev->rpa, BDADDR_ANY)) { device = btd_adapter_get_device(adapter, &ev->rpa, BDADDR_LE_RANDOM); duplicate = btd_adapter_find_device(adapter, &addr->bdaddr, addr->type); if (duplicate == device) duplicate = NULL; } else { device = btd_adapter_get_device(adapter, &addr->bdaddr, addr->type); duplicate = NULL; } if (!device) { btd_error(adapter->dev_id, "Unable to get device object for %s", dst); return; } device_update_addr(device, &addr->bdaddr, addr->type); if (duplicate) device_merge_duplicate(device, duplicate); persistent = !!ev->store_hint; if (!persistent) return; store_irk(adapter, &addr->bdaddr, addr->type, irk->val); btd_device_set_temporary(device, false); } void btd_adapter_store_conn_param(struct btd_adapter *adapter, const bdaddr_t *peer, uint8_t bdaddr_type, uint16_t min_interval, uint16_t max_interval, uint16_t latency, uint16_t timeout) { char device_addr[18]; char filename[PATH_MAX]; GKeyFile *key_file; GError *gerr = NULL; char *store_data; size_t length = 0; ba2str(peer, device_addr); DBG(""); create_filename(filename, PATH_MAX, "/%s/%s/info", btd_adapter_get_storage_dir(adapter), device_addr); key_file = g_key_file_new(); if (!g_key_file_load_from_file(key_file, filename, 0, &gerr)) { error("Unable to load key file from %s: (%s)", filename, gerr->message); g_clear_error(&gerr); } g_key_file_set_integer(key_file, "ConnectionParameters", "MinInterval", min_interval); g_key_file_set_integer(key_file, "ConnectionParameters", "MaxInterval", max_interval); g_key_file_set_integer(key_file, "ConnectionParameters", "Latency", latency); g_key_file_set_integer(key_file, "ConnectionParameters", "Timeout", timeout); create_file(filename, 0600); store_data = g_key_file_to_data(key_file, &length, NULL); if (!g_file_set_contents(filename, store_data, length, &gerr)) { error("Unable set contents for %s: (%s)", filename, gerr->message); g_error_free(gerr); } g_free(store_data); g_key_file_free(key_file); } static void new_conn_param(uint16_t index, uint16_t length, const void *param, void *user_data) { const struct mgmt_ev_new_conn_param *ev = param; struct btd_adapter *adapter = user_data; uint16_t min, max, latency, timeout; struct btd_device *dev; char dst[18]; if (length < sizeof(*ev)) { btd_error(adapter->dev_id, "Too small New Connection Parameter event"); return; } ba2str(&ev->addr.bdaddr, dst); min = btohs(ev->min_interval); max = btohs(ev->max_interval); latency = btohs(ev->latency); timeout = btohs(ev->timeout); DBG("hci%u %s (%u) min 0x%04x max 0x%04x latency 0x%04x timeout 0x%04x", adapter->dev_id, dst, ev->addr.type, min, max, latency, timeout); dev = btd_adapter_get_device(adapter, &ev->addr.bdaddr, ev->addr.type); if (!dev) { btd_error(adapter->dev_id, "Unable to get device object for %s", dst); return; } if (!ev->store_hint) return; btd_adapter_store_conn_param(adapter, &ev->addr.bdaddr, ev->addr.type, ev->min_interval, ev->max_interval, ev->latency, ev->timeout); } int adapter_set_io_capability(struct btd_adapter *adapter, uint8_t io_cap) { struct mgmt_cp_set_io_capability cp; if (!btd_opts.pairable) { if (io_cap == IO_CAPABILITY_INVALID) { if (adapter->current_settings & MGMT_SETTING_BONDABLE) set_mode(adapter, MGMT_OP_SET_BONDABLE, 0x00); return 0; } if (!(adapter->current_settings & MGMT_SETTING_BONDABLE)) set_mode(adapter, MGMT_OP_SET_BONDABLE, 0x01); } else if (io_cap == IO_CAPABILITY_INVALID) io_cap = IO_CAPABILITY_NOINPUTNOOUTPUT; memset(&cp, 0, sizeof(cp)); cp.io_capability = io_cap; if (mgmt_send(adapter->mgmt, MGMT_OP_SET_IO_CAPABILITY, adapter->dev_id, sizeof(cp), &cp, NULL, NULL, NULL) > 0) return 0; return -EIO; } int btd_adapter_add_remote_oob_data(struct btd_adapter *adapter, const bdaddr_t *bdaddr, uint8_t *hash, uint8_t *randomizer) { struct mgmt_cp_add_remote_oob_data cp; char addr[18]; ba2str(bdaddr, addr); DBG("hci%d bdaddr %s", adapter->dev_id, addr); memset(&cp, 0, sizeof(cp)); bacpy(&cp.addr.bdaddr, bdaddr); memcpy(cp.hash192, hash, 16); if (randomizer) memcpy(cp.rand192, randomizer, 16); if (mgmt_send(adapter->mgmt, MGMT_OP_ADD_REMOTE_OOB_DATA, adapter->dev_id, sizeof(cp), &cp, NULL, NULL, NULL) > 0) return 0; return -EIO; } int btd_adapter_remove_remote_oob_data(struct btd_adapter *adapter, const bdaddr_t *bdaddr) { struct mgmt_cp_remove_remote_oob_data cp; char addr[18]; ba2str(bdaddr, addr); DBG("hci%d bdaddr %s", adapter->dev_id, addr); memset(&cp, 0, sizeof(cp)); bacpy(&cp.addr.bdaddr, bdaddr); if (mgmt_send(adapter->mgmt, MGMT_OP_REMOVE_REMOTE_OOB_DATA, adapter->dev_id, sizeof(cp), &cp, NULL, NULL, NULL) > 0) return 0; return -EIO; } bool btd_adapter_ssp_enabled(struct btd_adapter *adapter) { if (adapter->current_settings & MGMT_SETTING_SSP) return true; return false; } void btd_adapter_set_oob_handler(struct btd_adapter *adapter, struct oob_handler *handler) { adapter->oob_handler = handler; } gboolean btd_adapter_check_oob_handler(struct btd_adapter *adapter) { return adapter->oob_handler != NULL; } static void read_local_oob_data_complete(uint8_t status, uint16_t length, const void *param, void *user_data) { const struct mgmt_rp_read_local_oob_data *rp = param; struct btd_adapter *adapter = user_data; const uint8_t *hash, *randomizer; if (status != MGMT_STATUS_SUCCESS) { btd_error(adapter->dev_id, "Read local OOB data failed: %s (0x%02x)", mgmt_errstr(status), status); hash = NULL; randomizer = NULL; } else if (length < sizeof(*rp)) { btd_error(adapter->dev_id, "Too small read local OOB data response"); return; } else { hash = rp->hash192; randomizer = rp->rand192; } if (!adapter->oob_handler || !adapter->oob_handler->read_local_cb) return; adapter->oob_handler->read_local_cb(adapter, hash, randomizer, adapter->oob_handler->user_data); g_free(adapter->oob_handler); adapter->oob_handler = NULL; } int btd_adapter_read_local_oob_data(struct btd_adapter *adapter) { DBG("hci%u", adapter->dev_id); if (mgmt_send(adapter->mgmt, MGMT_OP_READ_LOCAL_OOB_DATA, adapter->dev_id, 0, NULL, read_local_oob_data_complete, adapter, NULL) > 0) return 0; return -EIO; } void btd_adapter_for_each_device(struct btd_adapter *adapter, void (*cb)(struct btd_device *device, void *data), void *data) { g_slist_foreach(adapter->devices, (GFunc) cb, data); } static int adapter_cmp(gconstpointer a, gconstpointer b) { struct btd_adapter *adapter = (struct btd_adapter *) a; const bdaddr_t *bdaddr = b; return bacmp(&adapter->bdaddr, bdaddr); } static int adapter_id_cmp(gconstpointer a, gconstpointer b) { struct btd_adapter *adapter = (struct btd_adapter *) a; uint16_t id = GPOINTER_TO_UINT(b); return adapter->dev_id == id ? 0 : -1; } struct btd_adapter *adapter_find(const bdaddr_t *sba) { GSList *match; match = g_slist_find_custom(adapters, sba, adapter_cmp); if (!match) return NULL; return match->data; } struct btd_adapter *adapter_find_by_id(int id) { GSList *match; match = g_slist_find_custom(adapters, GINT_TO_POINTER(id), adapter_id_cmp); if (!match) return NULL; return match->data; } void adapter_foreach(adapter_cb func, gpointer user_data) { g_slist_foreach(adapters, (GFunc) func, user_data); } static int set_did(struct btd_adapter *adapter, uint16_t vendor, uint16_t product, uint16_t version, uint16_t source) { struct mgmt_cp_set_device_id cp; DBG("hci%u source %x vendor %x product %x version %x", adapter->dev_id, source, vendor, product, version); memset(&cp, 0, sizeof(cp)); cp.source = htobs(source); cp.vendor = htobs(vendor); cp.product = htobs(product); cp.version = htobs(version); if (mgmt_send(adapter->mgmt, MGMT_OP_SET_DEVICE_ID, adapter->dev_id, sizeof(cp), &cp, NULL, NULL, NULL) > 0) return 0; return -EIO; } static void services_modified(struct gatt_db_attribute *attrib, void *user_data) { struct btd_adapter *adapter = user_data; g_dbus_emit_property_changed(dbus_conn, adapter->path, ADAPTER_INTERFACE, "UUIDs"); } static int adapter_register(struct btd_adapter *adapter) { struct agent *agent; struct gatt_db *db; if (powering_down) return -EBUSY; adapter->path = g_strdup_printf("/org/bluez/hci%d", adapter->dev_id); if (!g_dbus_register_interface(dbus_conn, adapter->path, ADAPTER_INTERFACE, adapter_methods, NULL, adapter_properties, adapter, adapter_free)) { btd_error(adapter->dev_id, "Adapter interface init failed on path %s", adapter->path); g_free(adapter->path); adapter->path = NULL; return -EINVAL; } if (adapters == NULL) adapter->is_default = true; adapters = g_slist_append(adapters, adapter); agent = agent_get(NULL); if (agent) { uint8_t io_cap = agent_get_io_capability(agent); adapter_set_io_capability(adapter, io_cap); agent_unref(agent); } adapter->battery_provider_manager = btd_battery_provider_manager_create(adapter); /* Don't start GATT database and advertising managers on * non-LE controllers. */ if (!(adapter->supported_settings & MGMT_SETTING_LE) || btd_opts.mode == BT_MODE_BREDR) goto load; adapter->database = btd_gatt_database_new(adapter); if (!adapter->database) { btd_error(adapter->dev_id, "Failed to create GATT database for adapter"); adapters = g_slist_remove(adapters, adapter); return -EINVAL; } adapter->adv_manager = btd_adv_manager_new(adapter, adapter->mgmt); if (g_dbus_get_flags() & G_DBUS_FLAG_ENABLE_EXPERIMENTAL) { if (adapter->supported_settings & MGMT_SETTING_LE) { adapter->adv_monitor_manager = btd_adv_monitor_manager_create(adapter, adapter->mgmt); if (!adapter->adv_monitor_manager) { btd_error(adapter->dev_id, "Failed to create Adv Monitor " "Manager for adapter"); return -EINVAL; } } else { btd_info(adapter->dev_id, "Adv Monitor Manager " "skipped, LE unavailable"); } } db = btd_gatt_database_get_db(adapter->database); adapter->db_id = gatt_db_register(db, services_modified, services_modified, adapter, NULL); load: mgmt_register(adapter->mgmt, MGMT_EV_DEVICE_FLAGS_CHANGED, adapter->dev_id, device_flags_changed_callback, adapter, NULL); load_config(adapter); fix_storage(adapter); load_drivers(adapter); btd_profile_foreach(probe_profile, adapter); clear_blocked(adapter); load_defaults(adapter); load_devices(adapter); /* restore Service Changed CCC value for bonded devices */ btd_gatt_database_restore_svc_chng_ccc(adapter->database); /* retrieve the active connections: address the scenario where * the are active connections before the daemon've started */ if (btd_adapter_get_powered(adapter)) load_connections(adapter); adapter->initialized = TRUE; if (btd_opts.did_source) { /* DeviceID record is added by sdpd-server before any other * record is registered. */ adapter_service_insert(adapter, sdp_record_find(0x10000)); set_did(adapter, btd_opts.did_vendor, btd_opts.did_product, btd_opts.did_version, btd_opts.did_source); } DBG("Adapter %s registered", adapter->path); return 0; } static int adapter_unregister(struct btd_adapter *adapter) { DBG("Unregister path: %s", adapter->path); adapters = g_slist_remove(adapters, adapter); if (adapter->is_default && adapters != NULL) { struct btd_adapter *new_default; new_default = adapter_find_by_id(hci_get_route(NULL)); if (new_default == NULL) new_default = adapters->data; new_default->is_default = true; } adapter_list = g_list_remove(adapter_list, adapter); adapter_remove(adapter); btd_adapter_unref(adapter); return 0; } static void disconnected_callback(uint16_t index, uint16_t length, const void *param, void *user_data) { const struct mgmt_ev_device_disconnected *ev = param; struct btd_adapter *adapter = user_data; uint8_t reason; if (length < sizeof(struct mgmt_addr_info)) { btd_error(adapter->dev_id, "Too small device disconnected event"); return; } if (length < sizeof(*ev)) reason = MGMT_DEV_DISCONN_UNKNOWN; else reason = ev->reason; dev_disconnected(adapter, &ev->addr, reason); } static void connected_callback(uint16_t index, uint16_t length, const void *param, void *user_data) { const struct mgmt_ev_device_connected *ev = param; struct btd_adapter *adapter = user_data; struct btd_device *device; struct eir_data eir_data; uint16_t eir_len; char addr[18]; bool name_known; if (length < sizeof(*ev)) { btd_error(adapter->dev_id, "Too small device connected event"); return; } eir_len = btohs(ev->eir_len); if (length < sizeof(*ev) + eir_len) { btd_error(adapter->dev_id, "Too small device connected event"); return; } ba2str(&ev->addr.bdaddr, addr); DBG("hci%u device %s connected eir_len %u", index, addr, eir_len); device = btd_adapter_get_device(adapter, &ev->addr.bdaddr, ev->addr.type); if (!device) { btd_error(adapter->dev_id, "Unable to get device object for %s", addr); return; } memset(&eir_data, 0, sizeof(eir_data)); if (eir_len > 0) eir_parse(&eir_data, ev->eir, eir_len); if (eir_data.class != 0) device_set_class(device, eir_data.class); adapter_add_connection(adapter, device, ev->addr.type, le32_to_cpu(ev->flags)); name_known = device_name_known(device); if (eir_data.name && (eir_data.name_complete || !name_known)) { device_store_cached_name(device, eir_data.name); btd_device_device_set_name(device, eir_data.name); } if (eir_data.msd_list) adapter_msd_notify(adapter, device, eir_data.msd_list); eir_data_free(&eir_data); } static void controller_resume_notify(struct btd_adapter *adapter) { GSList *l; for (l = adapter->drivers; l; l = g_slist_next(l)) { struct btd_adapter_driver *driver = l->data; if (driver->resume) driver->resume(adapter); } } static void controller_resume_callback(uint16_t index, uint16_t length, const void *param, void *user_data) { const struct mgmt_ev_controller_resume *ev = param; struct btd_adapter *adapter = user_data; if (length < sizeof(*ev)) { btd_error(adapter->dev_id, "Too small device resume event"); return; } info("Controller resume with wake event 0x%x", ev->wake_reason); controller_resume_notify(adapter); } static void device_blocked_callback(uint16_t index, uint16_t length, const void *param, void *user_data) { const struct mgmt_ev_device_blocked *ev = param; struct btd_adapter *adapter = user_data; struct btd_device *device; char addr[18]; if (length < sizeof(*ev)) { btd_error(adapter->dev_id, "Too small device blocked event"); return; } ba2str(&ev->addr.bdaddr, addr); DBG("hci%u %s blocked", index, addr); device = btd_adapter_find_device(adapter, &ev->addr.bdaddr, ev->addr.type); if (device) device_block(device, TRUE); } static void device_unblocked_callback(uint16_t index, uint16_t length, const void *param, void *user_data) { const struct mgmt_ev_device_unblocked *ev = param; struct btd_adapter *adapter = user_data; struct btd_device *device; char addr[18]; if (length < sizeof(*ev)) { btd_error(adapter->dev_id, "Too small device unblocked event"); return; } ba2str(&ev->addr.bdaddr, addr); DBG("hci%u %s unblocked", index, addr); device = btd_adapter_find_device(adapter, &ev->addr.bdaddr, ev->addr.type); if (device) device_unblock(device, FALSE, TRUE); } static void conn_fail_notify(struct btd_device *dev, uint8_t status) { GSList *l; for (l = conn_fail_list; l; l = g_slist_next(l)) { btd_conn_fail_cb conn_fail_cb = l->data; conn_fail_cb(dev, status); } } void btd_add_conn_fail_cb(btd_conn_fail_cb func) { conn_fail_list = g_slist_append(conn_fail_list, func); } void btd_remove_conn_fail_cb(btd_conn_fail_cb func) { conn_fail_list = g_slist_remove(conn_fail_list, func); } static void connect_failed_callback(uint16_t index, uint16_t length, const void *param, void *user_data) { const struct mgmt_ev_connect_failed *ev = param; struct btd_adapter *adapter = user_data; struct btd_device *device; char addr[18]; if (length < sizeof(*ev)) { btd_error(adapter->dev_id, "Too small connect failed event"); return; } ba2str(&ev->addr.bdaddr, addr); DBG("hci%u %s status %u", index, addr, ev->status); device = btd_adapter_find_device(adapter, &ev->addr.bdaddr, ev->addr.type); if (device) { conn_fail_notify(device, ev->status); /* If the device is in a bonding process cancel any auth request * sent to the agent before proceeding, but keep the bonding * request structure. */ if (device_is_bonding(device, NULL)) device_cancel_authentication(device, FALSE); } /* In the case of security mode 3 devices */ bonding_attempt_complete(adapter, &ev->addr.bdaddr, ev->addr.type, ev->status); /* If the device is scheduled to retry the bonding wait until the retry * happens. In other case, proceed with cancel the bondig. */ if (device && device_is_bonding(device, NULL) && !device_is_retrying(device)) { device_cancel_authentication(device, TRUE); device_bonding_failed(device, ev->status); } /* In the case the bonding was canceled or did exists, remove the device * when it is temporary. */ if (device && !device_is_bonding(device, NULL) && device_is_temporary(device)) btd_adapter_remove_device(adapter, device); } static void remove_keys(struct btd_adapter *adapter, struct btd_device *device, uint8_t type) { char device_addr[18]; char filename[PATH_MAX]; GKeyFile *key_file; GError *gerr = NULL; gsize length = 0; char *str; ba2str(device_get_address(device), device_addr); create_filename(filename, PATH_MAX, "/%s/%s/info", btd_adapter_get_storage_dir(adapter), device_addr); key_file = g_key_file_new(); if (!g_key_file_load_from_file(key_file, filename, 0, &gerr)) { error("Unable to load key file from %s: (%s)", filename, gerr->message); g_clear_error(&gerr); } if (type == BDADDR_BREDR) { g_key_file_remove_group(key_file, "LinkKey", NULL); } else { g_key_file_remove_group(key_file, "LongTermKey", NULL); g_key_file_remove_group(key_file, "LocalSignatureKey", NULL); g_key_file_remove_group(key_file, "RemoteSignatureKey", NULL); g_key_file_remove_group(key_file, "IdentityResolvingKey", NULL); } str = g_key_file_to_data(key_file, &length, NULL); if (!g_file_set_contents(filename, str, length, &gerr)) { error("Unable set contents for %s: (%s)", filename, gerr->message); g_error_free(gerr); } g_free(str); g_key_file_free(key_file); } static void unpaired_callback(uint16_t index, uint16_t length, const void *param, void *user_data) { const struct mgmt_ev_device_unpaired *ev = param; struct btd_adapter *adapter = user_data; struct btd_device *device; char addr[18]; if (length < sizeof(*ev)) { btd_error(adapter->dev_id, "Too small device unpaired event"); return; } ba2str(&ev->addr.bdaddr, addr); DBG("hci%u addr %s", index, addr); device = btd_adapter_find_device(adapter, &ev->addr.bdaddr, ev->addr.type); if (!device) { btd_warn(adapter->dev_id, "No device object for unpaired device %s", addr); return; } remove_keys(adapter, device, ev->addr.type); device_set_unpaired(device, ev->addr.type); } static void clear_devices_complete(uint8_t status, uint16_t length, const void *param, void *user_data) { if (status != MGMT_STATUS_SUCCESS) { error("Failed to clear devices: %s (0x%02x)", mgmt_errstr(status), status); return; } } static int clear_devices(struct btd_adapter *adapter) { struct mgmt_cp_remove_device cp; if (!btd_has_kernel_features(KERNEL_CONN_CONTROL)) return 0; memset(&cp, 0, sizeof(cp)); DBG("sending clear devices command for index %u", adapter->dev_id); if (mgmt_send(adapter->mgmt, MGMT_OP_REMOVE_DEVICE, adapter->dev_id, sizeof(cp), &cp, clear_devices_complete, adapter, NULL) > 0) return 0; btd_error(adapter->dev_id, "Failed to clear devices for index %u", adapter->dev_id); return -EIO; } static bool get_static_addr(struct btd_adapter *adapter) { struct bt_crypto *crypto; GKeyFile *file; GError *gerr = NULL; char filename[PATH_MAX]; char **addrs; char mfg[7]; char *str; bool ret; gsize len, i; snprintf(mfg, sizeof(mfg), "0x%04x", adapter->manufacturer); create_filename(filename, PATH_MAX, "/addresses"); file = g_key_file_new(); if (!g_key_file_load_from_file(file, filename, 0, &gerr)) { error("Unable to load key file from %s: (%s)", filename, gerr->message); g_clear_error(&gerr); } addrs = g_key_file_get_string_list(file, "Static", mfg, &len, NULL); if (addrs) { for (i = 0; i < len; i++) { bdaddr_t addr; str2ba(addrs[i], &addr); if (adapter_find(&addr)) continue; /* Usable address found in list */ bacpy(&adapter->bdaddr, &addr); adapter->bdaddr_type = BDADDR_LE_RANDOM; ret = true; goto done; } len++; addrs = g_renew(char *, addrs, len + 1); } else { len = 1; addrs = g_new(char *, len + 1); } /* Initialize slot for new address */ addrs[len - 1] = g_malloc(18); addrs[len] = NULL; crypto = bt_crypto_new(); if (!crypto) { error("Failed to open crypto"); ret = false; goto done; } ret = bt_crypto_random_bytes(crypto, &adapter->bdaddr, sizeof(adapter->bdaddr)); if (!ret) { error("Failed to generate static address"); bt_crypto_unref(crypto); goto done; } bt_crypto_unref(crypto); adapter->bdaddr.b[5] |= 0xc0; adapter->bdaddr_type = BDADDR_LE_RANDOM; ba2str(&adapter->bdaddr, addrs[len - 1]); g_key_file_set_string_list(file, "Static", mfg, (const char **)addrs, len); str = g_key_file_to_data(file, &len, NULL); if (!g_file_set_contents(filename, str, len, &gerr)) { error("Unable set contents for %s: (%s)", filename, gerr->message); g_error_free(gerr); } g_free(str); ret = true; done: g_key_file_free(file); g_strfreev(addrs); return ret; } static bool set_static_addr(struct btd_adapter *adapter) { struct mgmt_cp_set_static_address cp; /* dual-mode adapters must have a public address */ if (adapter->supported_settings & MGMT_SETTING_BREDR) return false; if (!(adapter->supported_settings & MGMT_SETTING_LE)) return false; DBG("Setting static address"); if (!get_static_addr(adapter)) return false; bacpy(&cp.bdaddr, &adapter->bdaddr); if (mgmt_send(adapter->mgmt, MGMT_OP_SET_STATIC_ADDRESS, adapter->dev_id, sizeof(cp), &cp, NULL, NULL, NULL) > 0) { return true; } return false; } static void set_blocked_keys_complete(uint8_t status, uint16_t length, const void *param, void *user_data) { struct btd_adapter *adapter = user_data; if (status != MGMT_STATUS_SUCCESS) { btd_error(adapter->dev_id, "Failed to set blocked keys: %s (0x%02x)", mgmt_errstr(status), status); return; } DBG("Successfully set blocked keys for index %u", adapter->dev_id); } static bool set_blocked_keys(struct btd_adapter *adapter) { uint8_t buffer[sizeof(struct mgmt_cp_set_blocked_keys) + sizeof(blocked_keys)] = { 0 }; struct mgmt_cp_set_blocked_keys *cp = (struct mgmt_cp_set_blocked_keys *)buffer; int i; cp->key_count = ARRAY_SIZE(blocked_keys); for (i = 0; i < cp->key_count; ++i) { cp->keys[i].type = blocked_keys[i].type; memcpy(cp->keys[i].val, blocked_keys[i].val, sizeof(cp->keys[i].val)); } return mgmt_send(mgmt_primary, MGMT_OP_SET_BLOCKED_KEYS, adapter->dev_id, sizeof(buffer), buffer, set_blocked_keys_complete, adapter, NULL); } #define EXP_FEAT(_flag, _uuid, _func) \ { \ .flag = _flag, \ .uuid = _uuid, \ .func = _func, \ } static void exp_complete(void *user_data); static bool exp_mgmt_send(struct btd_adapter *adapter, uint16_t opcode, uint16_t index, uint16_t length, const void *param, mgmt_request_func_t callback) { struct exp_pending *pending; pending = g_new0(struct exp_pending, 1); pending->adapter = adapter; if (!queue_push_tail(adapter->exp_pending, pending)) { g_free(pending); return false; } pending->id = mgmt_send(adapter->mgmt, opcode, index, length, param, callback, pending, exp_complete); if (!pending->id) { queue_remove(adapter->exp_pending, pending); g_free(pending); return false; } return true; } static void set_exp_debug_complete(uint8_t status, uint16_t len, const void *param, void *user_data) { struct exp_pending *pending = user_data; struct btd_adapter *adapter = pending->adapter; uint8_t action; if (status != 0) { error("Set Experimental Debug failed with status 0x%02x (%s)", status, mgmt_errstr(status)); return; } action = btd_kernel_experimental_enabled(debug_uuid.str); DBG("Experimental Debug successfully %s", action ? "set" : "reset"); if (action) queue_push_tail(adapter->exps, (void *)debug_uuid.val); } static void exp_debug_func(struct btd_adapter *adapter, uint8_t action) { struct mgmt_cp_set_exp_feature cp; memset(&cp, 0, sizeof(cp)); memcpy(cp.uuid, debug_uuid.val, 16); cp.action = action; if (exp_mgmt_send(adapter, MGMT_OP_SET_EXP_FEATURE, adapter->dev_id, sizeof(cp), &cp, set_exp_debug_complete)) return; btd_error(adapter->dev_id, "Failed to set exp debug"); } static void le_simult_central_peripheral_func(struct btd_adapter *adapter, uint8_t action) { if (action) queue_push_tail(adapter->exps, (void *)le_simult_central_peripheral_uuid.val); } static void quality_report_func(struct btd_adapter *adapter, uint8_t action) { if (action) queue_push_tail(adapter->exps, (void *)quality_report_uuid.val); } static void set_rpa_resolution_complete(uint8_t status, uint16_t len, const void *param, void *user_data) { struct exp_pending *pending = user_data; struct btd_adapter *adapter = pending->adapter; uint8_t action; if (status != 0) { error("Set RPA Resolution failed with status 0x%02x (%s)", status, mgmt_errstr(status)); return; } action = btd_kernel_experimental_enabled(rpa_resolution_uuid.str); DBG("RPA Resolution successfully %s", action ? "set" : "reset"); if (action) queue_push_tail(adapter->exps, (void *)rpa_resolution_uuid.val); } static void rpa_resolution_func(struct btd_adapter *adapter, uint8_t action) { struct mgmt_cp_set_exp_feature cp; memset(&cp, 0, sizeof(cp)); memcpy(cp.uuid, rpa_resolution_uuid.val, 16); cp.action = action; if (exp_mgmt_send(adapter, MGMT_OP_SET_EXP_FEATURE, adapter->dev_id, sizeof(cp), &cp, set_rpa_resolution_complete)) return; btd_error(adapter->dev_id, "Failed to set RPA Resolution"); } static void codec_offload_complete(uint8_t status, uint16_t len, const void *param, void *user_data) { struct exp_pending *pending = user_data; struct btd_adapter *adapter = pending->adapter; uint8_t action; if (status != 0) { error("Set Codec Offload failed with status 0x%02x (%s)", status, mgmt_errstr(status)); return; } action = btd_kernel_experimental_enabled(codec_offload_uuid.str); DBG("Codec Offload successfully %s", action ? "set" : "reset"); if (action) queue_push_tail(adapter->exps, (void *)codec_offload_uuid.val); } static void codec_offload_func(struct btd_adapter *adapter, uint8_t action) { struct mgmt_cp_set_exp_feature cp; memset(&cp, 0, sizeof(cp)); memcpy(cp.uuid, codec_offload_uuid.val, 16); cp.action = action; if (exp_mgmt_send(adapter, MGMT_OP_SET_EXP_FEATURE, adapter->dev_id, sizeof(cp), &cp, codec_offload_complete)) return; btd_error(adapter->dev_id, "Failed to set Codec Offload"); } static void iso_socket_complete(uint8_t status, uint16_t len, const void *param, void *user_data) { struct exp_pending *pending = user_data; struct btd_adapter *adapter = pending->adapter; uint8_t action; if (status != 0) { error("Set ISO Socket failed with status 0x%02x (%s)", status, mgmt_errstr(status)); return; } action = btd_kernel_experimental_enabled(iso_socket_uuid.str); DBG("ISO Socket successfully %s", action ? "set" : "reset"); if (action) queue_push_tail(adapter->exps, (void *)iso_socket_uuid.val); } static void iso_socket_func(struct btd_adapter *adapter, uint8_t action) { struct mgmt_cp_set_exp_feature cp; memset(&cp, 0, sizeof(cp)); memcpy(cp.uuid, iso_socket_uuid.val, 16); cp.action = action; if (exp_mgmt_send(adapter, MGMT_OP_SET_EXP_FEATURE, MGMT_INDEX_NONE, sizeof(cp), &cp, iso_socket_complete)) return; btd_error(adapter->dev_id, "Failed to set ISO Socket"); } static const struct exp_feat { uint32_t flag; const struct mgmt_exp_uuid *uuid; void (*func)(struct btd_adapter *adapter, uint8_t action); } exp_table[] = { EXP_FEAT(EXP_FEAT_DEBUG, &debug_uuid, exp_debug_func), EXP_FEAT(EXP_FEAT_LE_SIMULT_ROLES, &le_simult_central_peripheral_uuid, le_simult_central_peripheral_func), EXP_FEAT(EXP_FEAT_BQR, &quality_report_uuid, quality_report_func), EXP_FEAT(EXP_FEAT_RPA_RESOLUTION, &rpa_resolution_uuid, rpa_resolution_func), EXP_FEAT(EXP_FEAT_CODEC_OFFLOAD, &codec_offload_uuid, codec_offload_func), EXP_FEAT(EXP_FEAT_ISO_SOCKET, &iso_socket_uuid, iso_socket_func), }; static void read_exp_features_complete(uint8_t status, uint16_t length, const void *param, void *user_data) { struct exp_pending *pending = user_data; struct btd_adapter *adapter = pending->adapter; const struct mgmt_rp_read_exp_features_info *rp = param; size_t feature_count = 0; size_t i = 0; DBG("index %u status 0x%02x", adapter->dev_id, status); if (status != MGMT_STATUS_SUCCESS) { btd_error(adapter->dev_id, "Failed to read exp features info: %s (0x%02x)", mgmt_errstr(status), status); return; } if (length < sizeof(*rp)) { btd_error(adapter->dev_id, "Response too small"); return; } feature_count = le16_to_cpu(rp->feature_count); if (length < sizeof(*rp) + (sizeof(*rp->features) * feature_count)) { btd_error(adapter->dev_id, "Response too small"); return; } for (i = 0; i < feature_count; ++i) { size_t j; for (j = 0; j < ARRAY_SIZE(exp_table); j++) { const struct exp_feat *feat = &exp_table[j]; const char *str; uint8_t action; if (memcmp(rp->features[i].uuid, feat->uuid->val, sizeof(rp->features[i].uuid))) continue; str = feat->uuid->str; action = btd_kernel_experimental_enabled(str); DBG("%s flags %u action %u", str, rp->features[i].flags, action); /* If already set don't attempt to set it again */ if (action == (rp->features[i].flags & BIT(0))) { if (action & BIT(0)) queue_push_tail(adapter->exps, (void *)feat->uuid->val); continue; } if (feat->func) feat->func(adapter, action); } } } static void read_exp_features(struct btd_adapter *adapter) { if (exp_mgmt_send(adapter, MGMT_OP_READ_EXP_FEATURES_INFO, adapter->dev_id, 0, NULL, read_exp_features_complete)) return; btd_error(adapter->dev_id, "Failed to read exp features info"); } static void read_info_complete(uint8_t status, uint16_t length, const void *param, void *user_data) { struct btd_adapter *adapter = user_data; const struct mgmt_rp_read_info *rp = param; uint32_t missing_settings; int err; DBG("index %u status 0x%02x", adapter->dev_id, status); if (status != MGMT_STATUS_SUCCESS) { btd_error(adapter->dev_id, "Failed to read info for index %u: %s (0x%02x)", adapter->dev_id, mgmt_errstr(status), status); goto failed; } if (length < sizeof(*rp)) { btd_error(adapter->dev_id, "Too small read info complete response"); goto failed; } /* * Store controller information for class of device, device * name, short name and settings. * * During the lifetime of the controller these will be updated by * events and the information is required to keep the current * state of the controller. */ adapter->dev_class = rp->dev_class[0] | (rp->dev_class[1] << 8) | (rp->dev_class[2] << 16); adapter->name = g_strdup((const char *) rp->name); adapter->short_name = g_strdup((const char *) rp->short_name); adapter->manufacturer = btohs(rp->manufacturer); adapter->supported_settings = btohl(rp->supported_settings); adapter->current_settings = btohl(rp->current_settings); adapter->version = rp->version; clear_uuids(adapter); clear_devices(adapter); if (bacmp(&rp->bdaddr, BDADDR_ANY) == 0) { if (!set_static_addr(adapter)) { btd_error(adapter->dev_id, "No Bluetooth address for index %u", adapter->dev_id); goto failed; } } else { struct btd_adapter *tmp; tmp = adapter_find(&rp->bdaddr); if (tmp) { btd_error(adapter->dev_id, "Bluetooth address for index %u match index %u", adapter->dev_id, tmp->dev_id); goto failed; } bacpy(&adapter->bdaddr, &rp->bdaddr); if (!(adapter->supported_settings & MGMT_SETTING_LE)) adapter->bdaddr_type = BDADDR_BREDR; else adapter->bdaddr_type = BDADDR_LE_PUBLIC; } missing_settings = adapter->current_settings ^ adapter->supported_settings; switch (btd_opts.mode) { case BT_MODE_DUAL: if (missing_settings & MGMT_SETTING_LE) set_mode(adapter, MGMT_OP_SET_LE, 0x01); if (missing_settings & MGMT_SETTING_BREDR) set_mode(adapter, MGMT_OP_SET_BREDR, 0x01); if (missing_settings & MGMT_SETTING_SSP) set_mode(adapter, MGMT_OP_SET_SSP, 0x01); break; case BT_MODE_BREDR: if (!(adapter->supported_settings & MGMT_SETTING_BREDR)) { btd_error(adapter->dev_id, "Ignoring adapter withouth BR/EDR support"); goto failed; } if (missing_settings & MGMT_SETTING_BREDR) set_mode(adapter, MGMT_OP_SET_BREDR, 0x01); if (missing_settings & MGMT_SETTING_SSP) set_mode(adapter, MGMT_OP_SET_SSP, 0x01); if (adapter->current_settings & MGMT_SETTING_LE) set_mode(adapter, MGMT_OP_SET_LE, 0x00); break; case BT_MODE_LE: if (!(adapter->supported_settings & MGMT_SETTING_LE)) { btd_error(adapter->dev_id, "Ignoring adapter withouth LE support"); goto failed; } if (missing_settings & MGMT_SETTING_LE) set_mode(adapter, MGMT_OP_SET_LE, 0x01); if (adapter->current_settings & MGMT_SETTING_BREDR) set_mode(adapter, MGMT_OP_SET_BREDR, 0x00); break; } if (missing_settings & MGMT_SETTING_SECURE_CONN) set_mode(adapter, MGMT_OP_SET_SECURE_CONN, btd_opts.secure_conn); if (missing_settings & MGMT_SETTING_WIDEBAND_SPEECH) set_mode(adapter, MGMT_OP_SET_WIDEBAND_SPEECH, 0x01); if (adapter->supported_settings & MGMT_SETTING_PRIVACY) set_privacy(adapter, btd_opts.privacy); if (btd_opts.fast_conn && (missing_settings & MGMT_SETTING_FAST_CONNECTABLE)) set_mode(adapter, MGMT_OP_SET_FAST_CONNECTABLE, 0x01); err = adapter_register(adapter); if (err < 0) { btd_error(adapter->dev_id, "Unable to register new adapter"); goto failed; } /* * Register all event notification handlers for controller. * * The handlers are registered after a succcesful read of the * controller info. From now on they can track updates and * notifications. */ mgmt_register(adapter->mgmt, MGMT_EV_NEW_SETTINGS, adapter->dev_id, new_settings_callback, adapter, NULL); mgmt_register(adapter->mgmt, MGMT_EV_CLASS_OF_DEV_CHANGED, adapter->dev_id, dev_class_changed_callback, adapter, NULL); mgmt_register(adapter->mgmt, MGMT_EV_LOCAL_NAME_CHANGED, adapter->dev_id, local_name_changed_callback, adapter, NULL); mgmt_register(adapter->mgmt, MGMT_EV_DISCOVERING, adapter->dev_id, discovering_callback, adapter, NULL); mgmt_register(adapter->mgmt, MGMT_EV_DEVICE_FOUND, adapter->dev_id, device_found_callback, adapter, NULL); mgmt_register(adapter->mgmt, MGMT_EV_DEVICE_DISCONNECTED, adapter->dev_id, disconnected_callback, adapter, NULL); mgmt_register(adapter->mgmt, MGMT_EV_DEVICE_CONNECTED, adapter->dev_id, connected_callback, adapter, NULL); mgmt_register(adapter->mgmt, MGMT_EV_CONNECT_FAILED, adapter->dev_id, connect_failed_callback, adapter, NULL); mgmt_register(adapter->mgmt, MGMT_EV_DEVICE_UNPAIRED, adapter->dev_id, unpaired_callback, adapter, NULL); mgmt_register(adapter->mgmt, MGMT_EV_AUTH_FAILED, adapter->dev_id, auth_failed_callback, adapter, NULL); mgmt_register(adapter->mgmt, MGMT_EV_NEW_LINK_KEY, adapter->dev_id, new_link_key_callback, adapter, NULL); mgmt_register(adapter->mgmt, MGMT_EV_NEW_LONG_TERM_KEY, adapter->dev_id, new_long_term_key_callback, adapter, NULL); mgmt_register(adapter->mgmt, MGMT_EV_NEW_CSRK, adapter->dev_id, new_csrk_callback, adapter, NULL); mgmt_register(adapter->mgmt, MGMT_EV_NEW_IRK, adapter->dev_id, new_irk_callback, adapter, NULL); mgmt_register(adapter->mgmt, MGMT_EV_NEW_CONN_PARAM, adapter->dev_id, new_conn_param, adapter, NULL); mgmt_register(adapter->mgmt, MGMT_EV_DEVICE_BLOCKED, adapter->dev_id, device_blocked_callback, adapter, NULL); mgmt_register(adapter->mgmt, MGMT_EV_DEVICE_UNBLOCKED, adapter->dev_id, device_unblocked_callback, adapter, NULL); mgmt_register(adapter->mgmt, MGMT_EV_PIN_CODE_REQUEST, adapter->dev_id, pin_code_request_callback, adapter, NULL); mgmt_register(adapter->mgmt, MGMT_EV_USER_CONFIRM_REQUEST, adapter->dev_id, user_confirm_request_callback, adapter, NULL); mgmt_register(adapter->mgmt, MGMT_EV_USER_PASSKEY_REQUEST, adapter->dev_id, user_passkey_request_callback, adapter, NULL); mgmt_register(adapter->mgmt, MGMT_EV_PASSKEY_NOTIFY, adapter->dev_id, user_passkey_notify_callback, adapter, NULL); mgmt_register(adapter->mgmt, MGMT_EV_CONTROLLER_RESUME, adapter->dev_id, controller_resume_callback, adapter, NULL); set_dev_class(adapter); set_name(adapter, btd_adapter_get_name(adapter)); if (btd_has_kernel_features(KERNEL_BLOCKED_KEYS_SUPPORTED) && !set_blocked_keys(adapter)) { btd_error(adapter->dev_id, "Failed to set blocked keys for index %u", adapter->dev_id); goto failed; } if (btd_opts.pairable && !(adapter->current_settings & MGMT_SETTING_BONDABLE)) set_mode(adapter, MGMT_OP_SET_BONDABLE, 0x01); if (!btd_has_kernel_features(KERNEL_CONN_CONTROL)) set_mode(adapter, MGMT_OP_SET_CONNECTABLE, 0x01); else if (adapter->current_settings & MGMT_SETTING_CONNECTABLE) set_mode(adapter, MGMT_OP_SET_CONNECTABLE, 0x00); if (adapter->stored_discoverable && !adapter->discoverable_timeout) set_discoverable(adapter, 0x01, 0); if (btd_adapter_get_powered(adapter)) adapter_start(adapter); return; failed: /* * Remove adapter from list in case of a failure. * * Leaving an adapter structure around for a controller that can * not be initilized makes no sense at the moment. * * This is a simplification to avoid constant checks if the * adapter is ready to do anything. */ adapter_list = g_list_remove(adapter_list, adapter); btd_adapter_unref(adapter); } static void reset_adv_monitors_complete(uint8_t status, uint16_t length, const void *param, void *user_data) { const struct mgmt_rp_remove_adv_monitor *rp = param; if (status != MGMT_STATUS_SUCCESS) { error("Failed to reset Adv Monitors: %s (0x%02x)", mgmt_errstr(status), status); return; } if (length < sizeof(*rp)) { error("Wrong size of remove Adv Monitor response for reset " "all Adv Monitors"); return; } DBG("Removed all Adv Monitors"); } static void reset_adv_monitors(uint16_t index) { struct mgmt_cp_remove_adv_monitor cp; DBG("sending remove Adv Monitor command with handle 0"); /* Handle 0 indicates to remove all */ cp.monitor_handle = 0; if (mgmt_send(mgmt_primary, MGMT_OP_REMOVE_ADV_MONITOR, index, sizeof(cp), &cp, reset_adv_monitors_complete, NULL, NULL) > 0) { return; } error("Failed to reset Adv Monitors"); } static void read_info(struct btd_adapter *adapter) { DBG("sending read info command for index %u", adapter->dev_id); if (mgmt_send(mgmt_primary, MGMT_OP_READ_INFO, adapter->dev_id, 0, NULL, read_info_complete, adapter, NULL) > 0) return; btd_error(adapter->dev_id, "Failed to read controller info for index %u", adapter->dev_id); adapter_list = g_list_remove(adapter_list, adapter); btd_adapter_unref(adapter); } static void exp_complete(void *user_data) { struct exp_pending *pending = user_data; struct btd_adapter *adapter = pending->adapter; if (!adapter) return; /* canceled */ queue_remove(adapter->exp_pending, pending); g_free(pending); if (queue_isempty(adapter->exp_pending)) { read_info(adapter); return; } DBG("index %u has %u pending MGMT EXP requests", adapter->dev_id, queue_length(adapter->exp_pending)); } static void index_added(uint16_t index, uint16_t length, const void *param, void *user_data) { struct btd_adapter *adapter; DBG("index %u", index); adapter = btd_adapter_lookup(index); if (adapter) { btd_warn(adapter->dev_id, "Ignoring index added for an already existing adapter"); return; } /* Check if at maximum adapters allowed in the system then ignore the * adapter. */ if (btd_opts.max_adapters && btd_opts.max_adapters == g_slist_length(adapters)) return; reset_adv_monitors(index); adapter = btd_adapter_new(index); if (!adapter) { btd_error(index, "Unable to create new adapter for index %u", index); return; } if (btd_has_kernel_features(KERNEL_EXP_FEATURES)) read_exp_features(adapter); /* * Protect against potential two executions of read controller info. * * In case the start of the daemon and the action of adding a new * controller coincide this function might be called twice. * * To avoid the double execution of reading the controller info, * add the adapter already to the list. If an adapter is already * present, the second notification will cause a warning. If the * command fails the adapter is removed from the list again. */ adapter_list = g_list_append(adapter_list, adapter); if (queue_isempty(adapter->exp_pending)) read_info(adapter); } static void index_removed(uint16_t index, uint16_t length, const void *param, void *user_data) { struct btd_adapter *adapter; DBG("index %u", index); adapter = btd_adapter_lookup(index); if (!adapter) { warn("Ignoring index removal for a non-existent adapter"); return; } adapter_unregister(adapter); } static void read_index_list_complete(uint8_t status, uint16_t length, const void *param, void *user_data) { const struct mgmt_rp_read_index_list *rp = param; uint16_t num; int i; if (status != MGMT_STATUS_SUCCESS) { error("Failed to read index list: %s (0x%02x)", mgmt_errstr(status), status); return; } if (length < sizeof(*rp)) { error("Wrong size of read index list response"); return; } num = btohs(rp->num_controllers); DBG("Number of controllers: %d", num); if (num * sizeof(uint16_t) + sizeof(*rp) != length) { error("Incorrect packet size for index list response"); return; } for (i = 0; i < num; i++) { uint16_t index; index = btohs(rp->index[i]); DBG("Found index %u", index); /* * Pretend to be index added event notification. * * It is safe to just trigger the procedure for index * added notification. It does check against itself. */ index_added(index, 0, NULL, NULL); } } static void read_commands_complete(uint8_t status, uint16_t length, const void *param, void *user_data) { const struct mgmt_rp_read_commands *rp = param; uint16_t num_commands, num_events; size_t expected_len; int i; if (status != MGMT_STATUS_SUCCESS) { error("Failed to read supported commands: %s (0x%02x)", mgmt_errstr(status), status); return; } if (length < sizeof(*rp)) { error("Wrong size of read commands response"); return; } num_commands = btohs(rp->num_commands); num_events = btohs(rp->num_events); DBG("Number of commands: %d", num_commands); DBG("Number of events: %d", num_events); expected_len = sizeof(*rp) + num_commands * sizeof(uint16_t) + num_events * sizeof(uint16_t); if (length < expected_len) { error("Too small reply for supported commands: (%u != %zu)", length, expected_len); return; } for (i = 0; i < num_commands; i++) { uint16_t op = get_le16(rp->opcodes + i); switch (op) { case MGMT_OP_ADD_DEVICE: DBG("enabling kernel-side connection control"); kernel_features |= KERNEL_CONN_CONTROL; break; case MGMT_OP_SET_BLOCKED_KEYS: DBG("kernel supports the set_blocked_keys op"); kernel_features |= KERNEL_BLOCKED_KEYS_SUPPORTED; break; case MGMT_OP_SET_DEF_SYSTEM_CONFIG: DBG("kernel supports set system confic"); kernel_features |= KERNEL_SET_SYSTEM_CONFIG; break; case MGMT_OP_READ_EXP_FEATURES_INFO: DBG("kernel supports exp features"); kernel_features |= KERNEL_EXP_FEATURES; break; case MGMT_OP_ADD_EXT_ADV_PARAMS: DBG("kernel supports ext adv commands"); kernel_features |= KERNEL_HAS_EXT_ADV_ADD_CMDS; break; case MGMT_OP_READ_CONTROLLER_CAP: DBG("kernel supports controller cap command"); kernel_features |= KERNEL_HAS_CONTROLLER_CAP_CMD; break; default: break; } } for (i = 0; i < num_events; i++) { uint16_t ev = get_le16(rp->opcodes + num_commands + i); switch(ev) { case MGMT_EV_CONTROLLER_RESUME: DBG("kernel supports suspend/resume events"); kernel_features |= KERNEL_HAS_RESUME_EVT; break; } } } static void read_version_complete(uint8_t status, uint16_t length, const void *param, void *user_data) { const struct mgmt_rp_read_version *rp = param; if (status != MGMT_STATUS_SUCCESS) { error("Failed to read version information: %s (0x%02x)", mgmt_errstr(status), status); return; } if (length < sizeof(*rp)) { error("Wrong size of read version response"); return; } mgmt_version = rp->version; mgmt_revision = btohs(rp->revision); info("Bluetooth management interface %u.%u initialized", mgmt_version, mgmt_revision); if (mgmt_version < 1) { error("Version 1.0 or later of management interface required"); abort(); } DBG("sending read supported commands command"); /* * It is irrelevant if this command succeeds or fails. In case of * failure safe settings are assumed. */ mgmt_send(mgmt_primary, MGMT_OP_READ_COMMANDS, MGMT_INDEX_NONE, 0, NULL, read_commands_complete, NULL, NULL); mgmt_register(mgmt_primary, MGMT_EV_INDEX_ADDED, MGMT_INDEX_NONE, index_added, NULL, NULL); mgmt_register(mgmt_primary, MGMT_EV_INDEX_REMOVED, MGMT_INDEX_NONE, index_removed, NULL, NULL); DBG("sending read index list command"); if (mgmt_send(mgmt_primary, MGMT_OP_READ_INDEX_LIST, MGMT_INDEX_NONE, 0, NULL, read_index_list_complete, NULL, NULL) > 0) return; error("Failed to read controller index list"); } static void mgmt_debug(const char *str, void *user_data) { DBG_IDX(0xffff, "%s", str); } int adapter_init(void) { dbus_conn = btd_get_dbus_connection(); mgmt_primary = mgmt_new_default(); if (!mgmt_primary) { error("Failed to access management interface"); return -EIO; } mgmt_set_debug(mgmt_primary, mgmt_debug, NULL, NULL); DBG("sending read version command"); if (mgmt_send(mgmt_primary, MGMT_OP_READ_VERSION, MGMT_INDEX_NONE, 0, NULL, read_version_complete, NULL, NULL) > 0) return 0; error("Failed to read management version information"); return -EIO; } void adapter_cleanup(void) { g_list_free(adapter_list); while (adapters) { struct btd_adapter *adapter = adapters->data; adapter_remove(adapter); adapters = g_slist_remove(adapters, adapter); btd_adapter_unref(adapter); } /* * In case there is another reference active, clear out * registered handlers for index added and index removed. * * This is just an extra precaution to be safe, and in * reality should not make a difference. */ mgmt_unregister_index(mgmt_primary, MGMT_INDEX_NONE); /* * In case there is another reference active, cancel * all pending global commands. * * This is just an extra precaution to avoid callbacks * that potentially then could leak memory or access * an invalid structure. */ mgmt_cancel_index(mgmt_primary, MGMT_INDEX_NONE); mgmt_unref(mgmt_primary); mgmt_primary = NULL; dbus_conn = NULL; } void adapter_shutdown(void) { GList *list; DBG(""); powering_down = true; for (list = g_list_first(adapter_list); list; list = g_list_next(list)) { struct btd_adapter *adapter = list->data; if (!(adapter->current_settings & MGMT_SETTING_POWERED)) continue; clear_discoverable(adapter); remove_temporary_devices(adapter); set_mode(adapter, MGMT_OP_SET_POWERED, 0x00); adapter_remaining++; } if (!adapter_remaining) btd_exit(); } /* * Check if workaround for broken ATT server socket behavior is needed * where we need to connect an ATT client socket before pairing to get * early access to the ATT channel. */ bool btd_le_connect_before_pairing(void) { if (MGMT_VERSION(mgmt_version, mgmt_revision) < MGMT_VERSION(1, 4)) return true; return false; } bool btd_adapter_has_settings(struct btd_adapter *adapter, uint32_t settings) { if (!adapter) return false; return (adapter->current_settings & settings) ? true : false; } bool btd_has_kernel_features(uint32_t features) { return (kernel_features & features) ? true : false; } bool btd_adapter_has_exp_feature(struct btd_adapter *adapter, uint32_t feature) { size_t i; for (i = 0; i < ARRAY_SIZE(exp_table); i++) { const struct exp_feat *feat = &exp_table[i]; if ((feat->flag & feature) && queue_find(adapter->exps, NULL, feat->uuid->val)) return true; } return false; } void btd_adapter_cancel_service_auth(struct btd_adapter *adapter, struct btd_device *device) { GList *l; l = adapter->auths->head; while (l != NULL) { struct service_auth *auth = l->data; GList *next = g_list_next(l); if (auth->device != device) { l = next; continue; } g_queue_delete_link(adapter->auths, l); l = next; service_auth_cancel(auth); } } bluez-5.82/src/PaxHeaders/sdpd-request.c0000644000000000000000000000005014165411566015232 xustar0020 atime=1743516609 20 ctime=1743591278 bluez-5.82/src/sdpd-request.c0000644000000000000000000007033714165411566014725 0ustar00rootroot// SPDX-License-Identifier: GPL-2.0-or-later /* * * BlueZ - Bluetooth protocol stack for Linux * * Copyright (C) 2001-2002 Nokia Corporation * Copyright (C) 2002-2003 Maxim Krasnyansky * Copyright (C) 2002-2010 Marcel Holtmann * Copyright (C) 2002-2003 Stephen Crane * * */ #ifdef HAVE_CONFIG_H #include #endif #include #include #include #include #include "lib/bluetooth.h" #include "lib/l2cap.h" #include "lib/sdp.h" #include "lib/sdp_lib.h" #include "src/shared/util.h" #include "sdpd.h" #include "log.h" typedef struct { uint32_t timestamp; union { uint16_t maxBytesSent; uint16_t lastIndexSent; } cStateValue; } sdp_cont_state_t; #define SDP_CONT_STATE_SIZE (sizeof(uint8_t) + sizeof(sdp_cont_state_t)) #define MIN(x, y) ((x) < (y)) ? (x): (y) typedef struct sdp_cont_info sdp_cont_info_t; struct sdp_cont_info { int sock; uint8_t opcode; uint32_t timestamp; sdp_buf_t buf; }; static sdp_list_t *cstates; static int cstate_match(const void *data, const void *user_data) { const sdp_cont_info_t *cinfo = data; const sdp_cont_state_t *cstate = user_data; /* Check timestamp */ return cinfo->timestamp - cstate->timestamp; } static void sdp_cont_info_free(sdp_cont_info_t *cinfo) { if (!cinfo) return; cstates = sdp_list_remove(cstates, cinfo); free(cinfo->buf.data); free(cinfo); } static sdp_cont_info_t *sdp_get_cont_info(sdp_req_t *req, sdp_cont_state_t *cstate) { sdp_list_t *list; list = sdp_list_find(cstates, cstate, cstate_match); if (list) { sdp_cont_info_t *cinfo = list->data; if (cinfo->opcode == req->opcode) return cinfo; /* Cleanup continuation if the opcode doesn't match since its * response buffer shall only be valid for the original requests */ sdp_cont_info_free(cinfo); return NULL; } /* Cleanup cstates if no continuation info could be found */ sdp_cstate_cleanup(req->sock); return NULL; } static uint32_t sdp_cstate_alloc_buf(sdp_req_t *req, sdp_buf_t *buf) { sdp_cont_info_t *cinfo = malloc(sizeof(sdp_cont_info_t)); uint8_t *data = malloc(buf->data_size); memcpy(data, buf->data, buf->data_size); memset(cinfo, 0, sizeof(sdp_cont_info_t)); cinfo->buf.data = data; cinfo->buf.data_size = buf->data_size; cinfo->buf.buf_size = buf->data_size; cinfo->timestamp = sdp_get_time(); cinfo->sock = req->sock; cinfo->opcode = req->opcode; cstates = sdp_list_append(cstates, cinfo); return cinfo->timestamp; } /* Additional values for checking datatype (not in spec) */ #define SDP_TYPE_UUID 0xfe #define SDP_TYPE_ATTRID 0xff struct attrid { uint8_t dtd; union { uint16_t uint16; uint32_t uint32; }; }; /* * Generic data element sequence extractor. Builds * a list whose elements are those found in the * sequence. The data type of elements found in the * sequence is returned in the reference pDataType */ static int extract_des(uint8_t *buf, int len, sdp_list_t **svcReqSeq, uint8_t *pDataType, uint8_t expectedType) { uint8_t seqType; int scanned, data_size = 0; short numberOfElements = 0; int seqlen = 0; sdp_list_t *pSeq = NULL; uint8_t dataType; int status = 0; const uint8_t *p; size_t bufsize; scanned = sdp_extract_seqtype(buf, len, &seqType, &data_size); SDPDBG("Seq type : %d", seqType); if (!scanned || (seqType != SDP_SEQ8 && seqType != SDP_SEQ16)) { error("Unknown seq type"); return -1; } p = buf + scanned; bufsize = len - scanned; SDPDBG("Data size : %d", data_size); for (;;) { char *pElem = NULL; int localSeqLength = 0; uuid_t *puuid; if (bufsize < sizeof(uint8_t)) { SDPDBG("->Unexpected end of buffer"); goto failed; } dataType = *p; SDPDBG("Data type: 0x%02x", dataType); if (expectedType == SDP_TYPE_UUID) { if (dataType != SDP_UUID16 && dataType != SDP_UUID32 && dataType != SDP_UUID128) { SDPDBG("->Unexpected Data type (expected UUID_ANY)"); goto failed; } } else if (expectedType == SDP_TYPE_ATTRID && (dataType != SDP_UINT16 && dataType != SDP_UINT32)) { SDPDBG("->Unexpected Data type (expected 0x%02x or 0x%02x)", SDP_UINT16, SDP_UINT32); goto failed; } else if (expectedType != SDP_TYPE_ATTRID && dataType != expectedType) { SDPDBG("->Unexpected Data type (expected 0x%02x)", expectedType); goto failed; } switch (dataType) { case SDP_UINT16: p += sizeof(uint8_t); seqlen += sizeof(uint8_t); bufsize -= sizeof(uint8_t); if (bufsize < sizeof(uint16_t)) { SDPDBG("->Unexpected end of buffer"); goto failed; } if (expectedType == SDP_TYPE_ATTRID) { struct attrid *aid; aid = malloc(sizeof(struct attrid)); aid->dtd = dataType; aid->uint16 = get_be16(p); pElem = (char *) aid; } else { uint16_t tmp; memcpy(&tmp, p, sizeof(tmp)); pElem = malloc(sizeof(uint16_t)); put_be16(tmp, pElem); } p += sizeof(uint16_t); seqlen += sizeof(uint16_t); bufsize -= sizeof(uint16_t); break; case SDP_UINT32: p += sizeof(uint8_t); seqlen += sizeof(uint8_t); bufsize -= sizeof(uint8_t); if (bufsize < (int)sizeof(uint32_t)) { SDPDBG("->Unexpected end of buffer"); goto failed; } if (expectedType == SDP_TYPE_ATTRID) { struct attrid *aid; aid = malloc(sizeof(struct attrid)); aid->dtd = dataType; aid->uint32 = get_be32(p); pElem = (char *) aid; } else { uint32_t tmp; memcpy(&tmp, p, sizeof(tmp)); pElem = malloc(sizeof(uint32_t)); put_be32(tmp, pElem); } p += sizeof(uint32_t); seqlen += sizeof(uint32_t); bufsize -= sizeof(uint32_t); break; case SDP_UUID16: case SDP_UUID32: case SDP_UUID128: puuid = malloc(sizeof(uuid_t)); status = sdp_uuid_extract(p, bufsize, puuid, &localSeqLength); if (status < 0) { free(puuid); goto failed; } pElem = (char *) puuid; seqlen += localSeqLength; p += localSeqLength; bufsize -= localSeqLength; break; default: return -1; } if (status == 0) { pSeq = sdp_list_append(pSeq, pElem); numberOfElements++; SDPDBG("No of elements : %d", numberOfElements); if (seqlen == data_size) break; else if (seqlen > data_size || seqlen > len) goto failed; } else free(pElem); } *svcReqSeq = pSeq; scanned += seqlen; *pDataType = dataType; return scanned; failed: sdp_list_free(pSeq, free); return -1; } static int sdp_set_cstate_pdu(sdp_buf_t *buf, sdp_cont_state_t *cstate) { uint8_t *pdata = buf->data + buf->data_size; int length = 0; if (cstate) { SDPDBG("Non null sdp_cstate_t id : 0x%x", cstate->timestamp); *pdata = sizeof(sdp_cont_state_t); pdata += sizeof(uint8_t); length += sizeof(uint8_t); memcpy(pdata, cstate, sizeof(sdp_cont_state_t)); length += sizeof(sdp_cont_state_t); } else { /* set "null" continuation state */ *pdata = 0; length += sizeof(uint8_t); } buf->data_size += length; return length; } static int sdp_cstate_get(sdp_req_t *req, uint8_t *buffer, size_t len, sdp_cont_state_t **cstate, sdp_cont_info_t **cinfo) { uint8_t cStateSize = *buffer; SDPDBG("Continuation State size : %d", cStateSize); if (cStateSize == 0) { /* Cleanup cstates if request doesn't contain a cstate */ sdp_cstate_cleanup(req->sock); *cstate = NULL; return 0; } buffer++; len--; if (len < sizeof(sdp_cont_state_t)) return -EINVAL; /* * Check if continuation state exists, if yes attempt * to get response remainder from cache, else send error */ *cstate = malloc(sizeof(sdp_cont_state_t)); if (!(*cstate)) return -ENOMEM; memcpy(*cstate, buffer, sizeof(sdp_cont_state_t)); SDPDBG("Cstate TS : 0x%x", (*cstate)->timestamp); SDPDBG("Bytes sent : %d", (*cstate)->cStateValue.maxBytesSent); *cinfo = sdp_get_cont_info(req, *cstate); return 0; } /* * The matching process is defined as "each and every UUID * specified in the "search pattern" must be present in the * "target pattern". Here "search pattern" is the set of UUIDs * specified by the service discovery client and "target pattern" * is the set of UUIDs present in a service record. * * Return 1 if each and every UUID in the search * pattern exists in the target pattern, 0 if the * match succeeds and -1 on error. */ static int sdp_match_uuid(sdp_list_t *search, sdp_list_t *pattern) { /* * The target is a sorted list, so we need not look * at all elements to confirm existence of an element * from the search pattern */ int patlen = sdp_list_len(pattern); if (patlen < sdp_list_len(search)) return -1; for (; search; search = search->next) { uuid_t *uuid128; void *data = search->data; sdp_list_t *list; if (data == NULL) return -1; /* create 128-bit form of the search UUID */ uuid128 = sdp_uuid_to_uuid128((uuid_t *)data); list = sdp_list_find(pattern, uuid128, sdp_uuid128_cmp); bt_free(uuid128); if (!list) return 0; } return 1; } /* * Service search request PDU. This method extracts the search pattern * (a sequence of UUIDs) and calls the matching function * to find matching services */ static int service_search_req(sdp_req_t *req, sdp_buf_t *buf) { int status = 0, i, plen, mlen, mtu, scanned; sdp_list_t *pattern = NULL; uint16_t expected, actual, rsp_count = 0; uint8_t dtd; sdp_cont_state_t *cstate = NULL; sdp_cont_info_t *cinfo = NULL; uint8_t *pCacheBuffer = NULL; int handleSize = 0; uint32_t cStateId = 0; uint8_t *pTotalRecordCount, *pCurrentRecordCount; uint8_t *pdata = req->buf + sizeof(sdp_pdu_hdr_t); size_t data_left = req->len - sizeof(sdp_pdu_hdr_t); scanned = extract_des(pdata, data_left, &pattern, &dtd, SDP_TYPE_UUID); if (scanned == -1) { status = SDP_INVALID_SYNTAX; goto done; } pdata += scanned; data_left -= scanned; plen = ntohs(((sdp_pdu_hdr_t *)(req->buf))->plen); mlen = scanned + sizeof(uint16_t) + 1; /* ensure we don't read past buffer */ if (plen < mlen || plen != mlen + *(uint8_t *)(pdata+sizeof(uint16_t))) { status = SDP_INVALID_SYNTAX; goto done; } if (data_left < sizeof(uint16_t)) { status = SDP_INVALID_SYNTAX; goto done; } expected = get_be16(pdata); SDPDBG("Expected count: %d", expected); SDPDBG("Bytes scanned : %d", scanned); pdata += sizeof(uint16_t); data_left -= sizeof(uint16_t); /* * Check if continuation state exists, if yes attempt * to get rsp remainder from continuation info, else send error */ if (sdp_cstate_get(req, pdata, data_left, &cstate, &cinfo) < 0) { status = SDP_INVALID_SYNTAX; goto done; } mtu = req->mtu - sizeof(sdp_pdu_hdr_t) - sizeof(uint16_t) - sizeof(uint16_t) - SDP_CONT_STATE_SIZE; actual = MIN(expected, mtu >> 2); /* make space in the rsp buffer for total and current record counts */ pdata = buf->data; /* total service record count = 0 */ pTotalRecordCount = pdata; put_be16(0, pdata); pdata += sizeof(uint16_t); buf->data_size += sizeof(uint16_t); /* current service record count = 0 */ pCurrentRecordCount = pdata; put_be16(0, pdata); pdata += sizeof(uint16_t); buf->data_size += sizeof(uint16_t); if (cstate == NULL) { /* for every record in the DB, do a pattern search */ sdp_list_t *list = sdp_get_record_list(); handleSize = 0; for (; list && rsp_count < expected; list = list->next) { sdp_record_t *rec = list->data; SDPDBG("Checking svcRec : 0x%x", rec->handle); if (sdp_match_uuid(pattern, rec->pattern) > 0 && sdp_check_access(rec->handle, &req->device)) { rsp_count++; put_be32(rec->handle, pdata); pdata += sizeof(uint32_t); handleSize += sizeof(uint32_t); } } SDPDBG("Match count: %d", rsp_count); buf->data_size += handleSize; put_be16(rsp_count, pTotalRecordCount); put_be16(rsp_count, pCurrentRecordCount); if (rsp_count > actual) { /* cache the rsp and generate a continuation state */ cStateId = sdp_cstate_alloc_buf(req, buf); /* * subtract handleSize since we now send only * a subset of handles */ buf->data_size -= handleSize; } else { /* NULL continuation state */ sdp_cont_info_free(cinfo); sdp_set_cstate_pdu(buf, NULL); } } /* under both the conditions below, the rsp buffer is not built yet */ if (cstate || cStateId > 0) { short lastIndex = 0; if (cstate) { if (cinfo) { /* Check if requesting more than available */ if (cstate->cStateValue.maxBytesSent >= cinfo->buf.data_size) { status = SDP_INVALID_CSTATE; goto done; } pCacheBuffer = cinfo->buf.data; /* get the rsp_count from the cached buffer */ rsp_count = get_be16(pCacheBuffer); /* get index of the last sdp_record_t sent */ lastIndex = cstate->cStateValue.lastIndexSent; } else { status = SDP_INVALID_CSTATE; goto done; } } else { pCacheBuffer = buf->data; lastIndex = 0; } /* * Set the local buffer pointer to after the * current record count and increment the cached * buffer pointer to beyond the counters */ pdata = pCurrentRecordCount + sizeof(uint16_t); /* increment beyond the totalCount and the currentCount */ pCacheBuffer += 2 * sizeof(uint16_t); if (cstate) { handleSize = 0; for (i = lastIndex; (i - lastIndex) < actual && i < rsp_count; i++) { memcpy(pdata, pCacheBuffer + i * sizeof(uint32_t), sizeof(uint32_t)); pdata += sizeof(uint32_t); handleSize += sizeof(uint32_t); } } else { handleSize = actual << 2; i = actual; } buf->data_size += handleSize; put_be16(rsp_count, pTotalRecordCount); put_be16(i - lastIndex, pCurrentRecordCount); if (i == rsp_count) { /* set "null" continuationState */ sdp_set_cstate_pdu(buf, NULL); sdp_cont_info_free(cinfo); } else { /* * there's more: set lastIndexSent to * the new value and move on */ sdp_cont_state_t newState; SDPDBG("Setting non-NULL sdp_cstate_t"); if (cstate) memcpy(&newState, cstate, sizeof(sdp_cont_state_t)); else { memset(&newState, 0, sizeof(sdp_cont_state_t)); newState.timestamp = cStateId; } newState.cStateValue.lastIndexSent = i; sdp_set_cstate_pdu(buf, &newState); } } done: free(cstate); if (pattern) sdp_list_free(pattern, free); return status; } /* * Extract attribute identifiers from the request PDU. * Clients could request a subset of attributes (by id) * from a service record, instead of the whole set. The * requested identifiers are present in the PDU form of * the request */ static int extract_attrs(sdp_record_t *rec, sdp_list_t *seq, sdp_buf_t *buf) { sdp_buf_t pdu; if (!rec) return SDP_INVALID_RECORD_HANDLE; if (seq == NULL) { SDPDBG("Attribute sequence is NULL"); return 0; } SDPDBG("Entries in attr seq : %d", sdp_list_len(seq)); sdp_gen_record_pdu(rec, &pdu); for (; seq; seq = seq->next) { struct attrid *aid = seq->data; SDPDBG("AttrDataType : %d", aid->dtd); if (aid->dtd == SDP_UINT16) { uint16_t attr = aid->uint16; sdp_data_t *a = sdp_data_get(rec, attr); if (a) sdp_append_to_pdu(buf, a); } else if (aid->dtd == SDP_UINT32) { uint32_t range = aid->uint32; uint16_t attr; uint16_t low = (0xffff0000 & range) >> 16; uint16_t high = 0x0000ffff & range; sdp_data_t *data; SDPDBG("attr range : 0x%x", range); SDPDBG("Low id : 0x%x", low); SDPDBG("High id : 0x%x", high); if (low == 0x0000 && high == 0xffff && pdu.data_size <= buf->buf_size) { /* copy it */ memcpy(buf->data, pdu.data, pdu.data_size); buf->data_size = pdu.data_size; break; } /* (else) sub-range of attributes */ for (attr = low; attr < high; attr++) { data = sdp_data_get(rec, attr); if (data) sdp_append_to_pdu(buf, data); } data = sdp_data_get(rec, high); if (data) sdp_append_to_pdu(buf, data); } else { error("Unexpected data type : 0x%x", aid->dtd); error("Expect uint16_t or uint32_t"); free(pdu.data); return SDP_INVALID_SYNTAX; } } free(pdu.data); return 0; } /* Build cstate response */ static int sdp_cstate_rsp(sdp_cont_info_t *cinfo, sdp_cont_state_t *cstate, sdp_buf_t *buf, uint16_t max) { sdp_buf_t *cache; uint16_t sent; if (!cinfo) return 0; if (cstate->cStateValue.maxBytesSent >= cinfo->buf.data_size) { sdp_cont_info_free(cinfo); return 0; } cache = &cinfo->buf; sent = MIN(max, cache->data_size - cstate->cStateValue.maxBytesSent); memcpy(buf->data, cache->data + cstate->cStateValue.maxBytesSent, sent); buf->data_size += sent; cstate->cStateValue.maxBytesSent += sent; SDPDBG("Response size : %d sending now : %d bytes sent so far : %d", cache->data_size, sent, cstate->cStateValue.maxBytesSent); if (cstate->cStateValue.maxBytesSent == cache->data_size) { sdp_cont_info_free(cinfo); return sdp_set_cstate_pdu(buf, NULL); } return sdp_set_cstate_pdu(buf, cstate); } /* * A request for the attributes of a service record. * First check if the service record (specified by * service record handle) exists, then call the attribute * streaming function */ static int service_attr_req(sdp_req_t *req, sdp_buf_t *buf) { sdp_cont_state_t *cstate = NULL; sdp_cont_info_t *cinfo = NULL; short cstate_size = 0; sdp_list_t *seq = NULL; uint8_t dtd = 0; int scanned = 0; unsigned int max_rsp_size; int status = 0, plen, mlen; uint8_t *pdata = req->buf + sizeof(sdp_pdu_hdr_t); size_t data_left = req->len - sizeof(sdp_pdu_hdr_t); uint32_t handle; if (data_left < sizeof(uint32_t)) { status = SDP_INVALID_SYNTAX; goto done; } handle = get_be32(pdata); pdata += sizeof(uint32_t); data_left -= sizeof(uint32_t); if (data_left < sizeof(uint16_t)) { status = SDP_INVALID_SYNTAX; goto done; } max_rsp_size = get_be16(pdata); pdata += sizeof(uint16_t); data_left -= sizeof(uint16_t); if (data_left < sizeof(sdp_pdu_hdr_t)) { status = SDP_INVALID_SYNTAX; goto done; } /* extract the attribute list */ scanned = extract_des(pdata, data_left, &seq, &dtd, SDP_TYPE_ATTRID); if (scanned == -1) { status = SDP_INVALID_SYNTAX; goto done; } pdata += scanned; data_left -= scanned; plen = ntohs(((sdp_pdu_hdr_t *)(req->buf))->plen); mlen = scanned + sizeof(uint32_t) + sizeof(uint16_t) + 1; /* ensure we don't read past buffer */ if (plen < mlen || plen != mlen + *(uint8_t *)pdata) { status = SDP_INVALID_PDU_SIZE; goto done; } /* * if continuation state exists, attempt * to get rsp remainder from cache, else send error */ if (sdp_cstate_get(req, pdata, data_left, &cstate, &cinfo) < 0) { status = SDP_INVALID_SYNTAX; goto done; } SDPDBG("SvcRecHandle : 0x%x", handle); SDPDBG("max_rsp_size : %d", max_rsp_size); /* * Check that max_rsp_size is within valid range * a minimum size of 0x0007 has to be used for data field */ if (max_rsp_size < 0x0007) { status = SDP_INVALID_SYNTAX; goto done; } /* * Calculate Attribute size according to MTU * We can send only (MTU - sizeof(sdp_pdu_hdr_t) - sizeof(sdp_cont_state_t)) */ max_rsp_size = MIN(max_rsp_size, req->mtu - sizeof(sdp_pdu_hdr_t) - sizeof(uint32_t) - SDP_CONT_STATE_SIZE - sizeof(uint16_t)); /* pull header for AttributeList byte count */ buf->data += sizeof(uint16_t); buf->buf_size -= sizeof(uint16_t); if (cstate) { cstate_size = sdp_cstate_rsp(cinfo, cstate, buf, max_rsp_size); if (!cstate_size) { status = SDP_INVALID_CSTATE; error("NULL cache buffer and non-NULL continuation state"); } } else { sdp_record_t *rec = sdp_record_find(handle); status = extract_attrs(rec, seq, buf); if (buf->data_size > max_rsp_size) { sdp_cont_state_t newState; memset((char *)&newState, 0, sizeof(sdp_cont_state_t)); newState.timestamp = sdp_cstate_alloc_buf(req, buf); /* * Reset the buffer size to the maximum expected and * set the sdp_cont_state_t */ SDPDBG("Creating continuation state of size : %d", buf->data_size); buf->data_size = max_rsp_size; newState.cStateValue.maxBytesSent = max_rsp_size; cstate_size = sdp_set_cstate_pdu(buf, &newState); } else { if (buf->data_size == 0) sdp_append_to_buf(buf, NULL, 0); cstate_size = sdp_set_cstate_pdu(buf, NULL); } } /* push header */ buf->data -= sizeof(uint16_t); buf->buf_size += sizeof(uint16_t); done: free(cstate); if (seq) sdp_list_free(seq, free); if (status) return status; /* set attribute list byte count */ put_be16(buf->data_size - cstate_size, buf->data); buf->data_size += sizeof(uint16_t); return 0; } /* * combined service search and attribute extraction */ static int service_search_attr_req(sdp_req_t *req, sdp_buf_t *buf) { int status = 0, plen, totscanned; uint8_t *pdata; unsigned int max; int scanned, rsp_count = 0; sdp_list_t *pattern = NULL, *seq = NULL, *svcList; sdp_cont_state_t *cstate = NULL; sdp_cont_info_t *cinfo = NULL; short cstate_size = 0; uint8_t dtd = 0; sdp_buf_t tmpbuf; size_t data_left; tmpbuf.data = NULL; pdata = req->buf + sizeof(sdp_pdu_hdr_t); data_left = req->len - sizeof(sdp_pdu_hdr_t); scanned = extract_des(pdata, data_left, &pattern, &dtd, SDP_TYPE_UUID); if (scanned == -1) { status = SDP_INVALID_SYNTAX; goto done; } totscanned = scanned; SDPDBG("Bytes scanned: %d", scanned); pdata += scanned; data_left -= scanned; if (data_left < sizeof(uint16_t)) { status = SDP_INVALID_SYNTAX; goto done; } max = get_be16(pdata); pdata += sizeof(uint16_t); data_left -= sizeof(uint16_t); SDPDBG("Max Attr expected: %d", max); if (data_left < sizeof(sdp_pdu_hdr_t)) { status = SDP_INVALID_SYNTAX; goto done; } /* extract the attribute list */ scanned = extract_des(pdata, data_left, &seq, &dtd, SDP_TYPE_ATTRID); if (scanned == -1) { status = SDP_INVALID_SYNTAX; goto done; } pdata += scanned; data_left -= scanned; totscanned += scanned + sizeof(uint16_t) + 1; plen = ntohs(((sdp_pdu_hdr_t *)(req->buf))->plen); if (plen < totscanned || plen != totscanned + *(uint8_t *)pdata) { status = SDP_INVALID_PDU_SIZE; goto done; } /* * if continuation state exists attempt * to get rsp remainder from cache, else send error */ if (sdp_cstate_get(req, pdata, data_left, &cstate, &cinfo) < 0) { status = SDP_INVALID_SYNTAX; goto done; } svcList = sdp_get_record_list(); tmpbuf.data = malloc(USHRT_MAX); tmpbuf.data_size = 0; tmpbuf.buf_size = USHRT_MAX; memset(tmpbuf.data, 0, USHRT_MAX); /* * Calculate Attribute size according to MTU * We can send only (MTU - sizeof(sdp_pdu_hdr_t) - sizeof(sdp_cont_state_t)) */ max = MIN(max, req->mtu - sizeof(sdp_pdu_hdr_t) - SDP_CONT_STATE_SIZE - sizeof(uint16_t)); /* pull header for AttributeList byte count */ buf->data += sizeof(uint16_t); buf->buf_size -= sizeof(uint16_t); if (cstate == NULL) { /* no continuation state -> create new response */ sdp_list_t *p; for (p = svcList; p; p = p->next) { sdp_record_t *rec = p->data; if (sdp_match_uuid(pattern, rec->pattern) > 0 && sdp_check_access(rec->handle, &req->device)) { rsp_count++; status = extract_attrs(rec, seq, &tmpbuf); SDPDBG("Response count : %d", rsp_count); SDPDBG("Local PDU size : %d", tmpbuf.data_size); if (status) { SDPDBG("Extract attr from record returns err"); break; } if (buf->data_size + tmpbuf.data_size < buf->buf_size) { /* to be sure no relocations */ sdp_append_to_buf(buf, tmpbuf.data, tmpbuf.data_size); tmpbuf.data_size = 0; memset(tmpbuf.data, 0, USHRT_MAX); } else { error("Relocation needed"); break; } SDPDBG("Net PDU size : %d", buf->data_size); } } if (buf->data_size > max) { sdp_cont_state_t newState; memset((char *)&newState, 0, sizeof(sdp_cont_state_t)); newState.timestamp = sdp_cstate_alloc_buf(req, buf); /* * Reset the buffer size to the maximum expected and * set the sdp_cont_state_t */ buf->data_size = max; newState.cStateValue.maxBytesSent = max; cstate_size = sdp_set_cstate_pdu(buf, &newState); } else cstate_size = sdp_set_cstate_pdu(buf, NULL); } else { cstate_size = sdp_cstate_rsp(cinfo, cstate, buf, max); if (!cstate_size) { status = SDP_INVALID_CSTATE; SDPDBG("Non-null continuation state, but null cache buffer"); } } if (!rsp_count && !cstate) { /* found nothing */ buf->data_size = 0; sdp_append_to_buf(buf, tmpbuf.data, tmpbuf.data_size); sdp_set_cstate_pdu(buf, NULL); } /* push header */ buf->data -= sizeof(uint16_t); buf->buf_size += sizeof(uint16_t); if (!status) { /* set attribute list byte count */ put_be16(buf->data_size - cstate_size, buf->data); buf->data_size += sizeof(uint16_t); } done: free(cstate); free(tmpbuf.data); if (pattern) sdp_list_free(pattern, free); if (seq) sdp_list_free(seq, free); return status; } /* * Top level request processor. Calls the appropriate processing * function based on request type. Handles service registration * client requests also. */ static void process_request(sdp_req_t *req) { sdp_pdu_hdr_t *reqhdr = (sdp_pdu_hdr_t *)req->buf; sdp_pdu_hdr_t *rsphdr; sdp_buf_t rsp; uint8_t *buf = malloc(USHRT_MAX); int status = SDP_INVALID_SYNTAX; memset(buf, 0, USHRT_MAX); rsp.data = buf + sizeof(sdp_pdu_hdr_t); rsp.data_size = 0; rsp.buf_size = USHRT_MAX - sizeof(sdp_pdu_hdr_t); rsphdr = (sdp_pdu_hdr_t *)buf; if (ntohs(reqhdr->plen) != req->len - sizeof(sdp_pdu_hdr_t)) { status = SDP_INVALID_PDU_SIZE; goto send_rsp; } req->opcode = reqhdr->pdu_id; switch (reqhdr->pdu_id) { case SDP_SVC_SEARCH_REQ: SDPDBG("Got a svc srch req"); status = service_search_req(req, &rsp); rsphdr->pdu_id = SDP_SVC_SEARCH_RSP; break; case SDP_SVC_ATTR_REQ: SDPDBG("Got a svc attr req"); status = service_attr_req(req, &rsp); rsphdr->pdu_id = SDP_SVC_ATTR_RSP; break; case SDP_SVC_SEARCH_ATTR_REQ: SDPDBG("Got a svc srch attr req"); status = service_search_attr_req(req, &rsp); rsphdr->pdu_id = SDP_SVC_SEARCH_ATTR_RSP; break; /* Following requests are allowed only for local connections */ case SDP_SVC_REGISTER_REQ: SDPDBG("Service register request"); if (req->local) { status = service_register_req(req, &rsp); rsphdr->pdu_id = SDP_SVC_REGISTER_RSP; } break; case SDP_SVC_UPDATE_REQ: SDPDBG("Service update request"); if (req->local) { status = service_update_req(req, &rsp); rsphdr->pdu_id = SDP_SVC_UPDATE_RSP; } break; case SDP_SVC_REMOVE_REQ: SDPDBG("Service removal request"); if (req->local) { status = service_remove_req(req, &rsp); rsphdr->pdu_id = SDP_SVC_REMOVE_RSP; } break; default: error("Unknown PDU ID : 0x%x received", reqhdr->pdu_id); status = SDP_INVALID_SYNTAX; break; } send_rsp: if (status) { /* Cleanup cstates on error */ sdp_cstate_cleanup(req->sock); rsphdr->pdu_id = SDP_ERROR_RSP; put_be16(status, rsp.data); rsp.data_size = sizeof(uint16_t); } SDPDBG("Sending rsp. status %d", status); rsphdr->tid = reqhdr->tid; rsphdr->plen = htons(rsp.data_size); /* point back to the real buffer start and set the real rsp length */ rsp.data_size += sizeof(sdp_pdu_hdr_t); rsp.data = buf; /* stream the rsp PDU */ if (send(req->sock, rsp.data, rsp.data_size, 0) < 0) error("send: %s (%d)", strerror(errno), errno); SDPDBG("Bytes Sent : %d", rsp.data_size); free(rsp.data); free(req->buf); } void handle_internal_request(int sk, int mtu, void *data, int len) { sdp_req_t req; bacpy(&req.device, BDADDR_ANY); bacpy(&req.bdaddr, BDADDR_LOCAL); req.local = 0; req.sock = sk; req.mtu = mtu; req.flags = 0; req.buf = data; req.len = len; process_request(&req); } void handle_request(int sk, uint8_t *data, int len) { struct sockaddr_l2 sa; socklen_t size; sdp_req_t req; size = sizeof(sa); if (getpeername(sk, (struct sockaddr *) &sa, &size) < 0) { error("getpeername: %s", strerror(errno)); return; } if (sa.l2_family == AF_BLUETOOTH) { struct l2cap_options lo; memset(&lo, 0, sizeof(lo)); size = sizeof(lo); if (getsockopt(sk, SOL_L2CAP, L2CAP_OPTIONS, &lo, &size) < 0) { error("getsockopt: %s", strerror(errno)); return; } bacpy(&req.bdaddr, &sa.l2_bdaddr); req.mtu = lo.omtu; req.local = 0; memset(&sa, 0, sizeof(sa)); size = sizeof(sa); if (getsockname(sk, (struct sockaddr *) &sa, &size) < 0) { error("getsockname: %s", strerror(errno)); return; } bacpy(&req.device, &sa.l2_bdaddr); } else { bacpy(&req.device, BDADDR_ANY); bacpy(&req.bdaddr, BDADDR_LOCAL); req.mtu = 2048; req.local = 1; } req.sock = sk; req.buf = data; req.len = len; process_request(&req); } void sdp_cstate_cleanup(int sock) { sdp_list_t *list; /* Remove any cinfo for the client */ for (list = cstates; list;) { sdp_cont_info_t *cinfo = list->data; list = list->next; if (cinfo->sock != sock) continue; sdp_cont_info_free(cinfo); } } bluez-5.82/src/PaxHeaders/device.c0000644000000000000000000000005014772767672014071 xustar0020 atime=1743515579 20 ctime=1743591285 bluez-5.82/src/device.c0000644000000000000000000054565714772767672013600 0ustar00rootroot// SPDX-License-Identifier: GPL-2.0-or-later /* * * BlueZ - Bluetooth protocol stack for Linux * * Copyright (C) 2006-2010 Nokia Corporation * Copyright (C) 2004-2010 Marcel Holtmann * Copyright 2024 NXP * */ #ifdef HAVE_CONFIG_H #include #endif #define _GNU_SOURCE #include #include #include #include #include #include #include #include #include #include #include #include #include "lib/bluetooth.h" #include "lib/sdp.h" #include "lib/sdp_lib.h" #include "lib/uuid.h" #include "gdbus/gdbus.h" #include "log.h" #include "src/shared/util.h" #include "src/shared/att.h" #include "src/shared/queue.h" #include "src/shared/gatt-db.h" #include "src/shared/gatt-client.h" #include "src/shared/gatt-server.h" #include "src/shared/ad.h" #include "src/shared/timeout.h" #include "btio/btio.h" #include "lib/mgmt.h" #include "attrib/att.h" #include "btd.h" #include "adapter.h" #include "gatt-database.h" #include "attrib/gattrib.h" #include "device.h" #include "gatt-client.h" #include "profile.h" #include "service.h" #include "dbus-common.h" #include "error.h" #include "uuid-helper.h" #include "sdp-client.h" #include "attrib/gatt.h" #include "agent.h" #include "textfile.h" #include "storage.h" #include "eir.h" #include "settings.h" #include "set.h" #define DISCONNECT_TIMER 2 #define DISCOVERY_TIMER 1 #define INVALID_FLAGS 0xff #ifndef MIN #define MIN(a, b) ((a) < (b) ? (a) : (b)) #endif #define RSSI_THRESHOLD 8 static DBusConnection *dbus_conn = NULL; static unsigned service_state_cb_id; struct btd_disconnect_data { guint id; disconnect_watch watch; void *user_data; GDestroyNotify destroy; }; struct bonding_req { DBusMessage *msg; guint listener_id; struct btd_device *device; uint8_t bdaddr_type; struct agent *agent; struct btd_adapter_pin_cb_iter *cb_iter; uint8_t status; guint retry_timer; struct timespec attempt_start_time; long last_attempt_duration_ms; }; typedef enum { AUTH_TYPE_PINCODE, AUTH_TYPE_PASSKEY, AUTH_TYPE_CONFIRM, AUTH_TYPE_NOTIFY_PASSKEY, AUTH_TYPE_NOTIFY_PINCODE, } auth_type_t; struct authentication_req { auth_type_t type; struct agent *agent; struct btd_device *device; uint8_t addr_type; uint32_t passkey; char *pincode; gboolean secure; }; enum { BROWSE_SDP, BROWSE_GATT }; struct browse_req { DBusMessage *msg; struct btd_device *device; uint8_t type; GSList *match_uuids; GSList *profiles_added; sdp_list_t *records; int search_uuid; int reconnect_attempt; guint listener_id; uint16_t sdp_flags; }; struct included_search { struct browse_req *req; GSList *services; GSList *current; }; struct svc_callback { unsigned int id; guint idle_id; struct btd_device *dev; device_svc_cb_t func; void *user_data; }; /* Per-bearer (LE or BR/EDR) device state */ struct bearer_state { bool prefer; bool paired; bool bonded; bool connected; bool svc_resolved; bool initiator; bool connectable; time_t last_seen; }; struct ltk_info { uint8_t key[16]; bool central; uint8_t enc_size; }; struct csrk_info { uint8_t key[16]; uint32_t counter; bool auth; }; struct sirk_info { struct btd_device_set *set; uint8_t encrypted; uint8_t key[16]; uint8_t size; uint8_t rank; }; enum { WAKE_FLAG_DEFAULT = 0, WAKE_FLAG_ENABLED, WAKE_FLAG_DISABLED, }; struct btd_device { int ref_count; bdaddr_t conn_bdaddr; uint8_t conn_bdaddr_type; bdaddr_t bdaddr; uint8_t bdaddr_type; bool rpa; char *path; bool bredr; bool le; bool pending_paired; /* "Paired" waiting for SDP */ bool svc_refreshed; bool refresh_discovery; /* Manage whether this device can wake the system from suspend. * - wake_support: Requires a profile that supports wake (i.e. HID) * - wake_allowed: Is wake currently allowed? * - pending_wake_allowed - Wake flag sent via set_device_flags * - wake_override - User configured wake setting */ bool wake_support; bool wake_allowed; bool pending_wake_allowed; uint8_t wake_override; GDBusPendingPropertySet wake_id; uint32_t supported_flags; uint32_t pending_flags; uint32_t current_flags; GSList *svc_callbacks; GSList *eir_uuids; struct bt_ad *ad; uint8_t ad_flags[1]; char name[MAX_NAME_LENGTH + 1]; char *alias; uint32_t class; uint16_t vendor_src; uint16_t vendor; uint16_t product; uint16_t version; uint16_t appearance; char *modalias; struct btd_adapter *adapter; GSList *uuids; GSList *primaries; /* List of primary services */ GSList *services; /* List of btd_service */ GSList *pending; /* Pending services */ GSList *watches; /* List of disconnect_data */ bool temporary; bool connectable; unsigned int disconn_timer; unsigned int discov_timer; unsigned int temporary_timer; /* Temporary/disappear timer */ struct browse_req *browse; /* service discover request */ struct bonding_req *bonding; struct authentication_req *authr; /* authentication request */ GSList *disconnects; /* disconnects message */ DBusMessage *connect; /* connect message */ DBusMessage *disconnect; /* disconnect message */ GAttrib *attrib; struct bt_att *att; /* The new ATT transport */ uint16_t att_mtu; /* The ATT MTU */ unsigned int att_disconn_id; /* * TODO: For now, device creates and owns the client-role gatt_db, but * this needs to be persisted in a more central place so that proper * attribute cache support can be built. */ struct gatt_db *db; /* GATT db cache */ unsigned int db_id; struct bt_gatt_client *client; /* GATT client instance */ struct bt_gatt_server *server; /* GATT server instance */ unsigned int gatt_ready_id; struct btd_gatt_client *client_dbus; struct bearer_state bredr_state; struct bearer_state le_state; struct csrk_info *local_csrk; struct csrk_info *remote_csrk; struct ltk_info *ltk; struct queue *sirks; sdp_list_t *tmp_records; bool trusted; gboolean blocked; gboolean auto_connect; gboolean disable_auto_connect; gboolean general_connect; bool legacy; int8_t rssi; int8_t tx_power; GIOChannel *att_io; guint store_id; time_t name_resolve_failed_time; int8_t volume; }; static const uint16_t uuid_list[] = { L2CAP_UUID, PNP_INFO_SVCLASS_ID, PUBLIC_BROWSE_GROUP, 0 }; static int device_browse_gatt(struct btd_device *device, DBusMessage *msg); static int device_browse_sdp(struct btd_device *device, DBusMessage *msg); static struct bearer_state *get_state(struct btd_device *dev, uint8_t bdaddr_type) { if (bdaddr_type == BDADDR_BREDR) return &dev->bredr_state; else return &dev->le_state; } bool btd_device_is_initiator(struct btd_device *dev) { if (dev->le_state.connected) return dev->le_state.initiator; else if (dev->bredr_state.connected) return dev->bredr_state.initiator; else if (dev->bonding) return true; return dev->att_io ? true : false; } static GSList *find_service_with_profile(GSList *list, struct btd_profile *p) { GSList *l; for (l = list; l != NULL; l = g_slist_next(l)) { struct btd_service *service = l->data; if (btd_service_get_profile(service) == p) return l; } return NULL; } static GSList *find_service_with_state(GSList *list, btd_service_state_t state) { GSList *l; for (l = list; l != NULL; l = g_slist_next(l)) { struct btd_service *service = l->data; if (btd_service_get_state(service) == state) return l; } return NULL; } static GSList *find_service_with_uuid(GSList *list, char *uuid) { GSList *l; for (l = list; l != NULL; l = g_slist_next(l)) { struct btd_service *service = l->data; struct btd_profile *profile = btd_service_get_profile(service); if (bt_uuid_strcmp(profile->remote_uuid, uuid) == 0) return l; } return NULL; } static const char *device_prefer_bearer_str(struct btd_device *device) { /* Check if both BR/EDR and LE bearer are supported */ if (!device->bredr || !device->le) return NULL; if (device->bredr_state.prefer) return "bredr"; else if (device->le_state.prefer) return "le"; else return "last-seen"; } static void update_technologies(GKeyFile *file, struct btd_device *dev) { const char *list[2]; size_t len = 0; const char *bearer; if (dev->bredr) list[len++] = "BR/EDR"; if (dev->le) { const char *type; if (dev->bdaddr_type == BDADDR_LE_PUBLIC) type = "public"; else type = "static"; g_key_file_set_string(file, "General", "AddressType", type); list[len++] = "LE"; } g_key_file_set_string_list(file, "General", "SupportedTechnologies", list, len); /* Store the PreferredBearer in case of dual-mode devices */ bearer = device_prefer_bearer_str(dev); if (bearer) g_key_file_set_string(file, "General", "PreferredBearer", bearer); } static void store_csrk(struct csrk_info *csrk, GKeyFile *key_file, const char *group) { char key[33]; int i; for (i = 0; i < 16; i++) sprintf(key + (i * 2), "%2.2X", csrk->key[i]); g_key_file_set_string(key_file, group, "Key", key); g_key_file_set_integer(key_file, group, "Counter", csrk->counter); g_key_file_set_boolean(key_file, group, "Authenticated", csrk->auth); } static void store_sirk(struct sirk_info *sirk, GKeyFile *key_file, uint8_t index) { char group[28]; char key[33]; int i; sprintf(group, "SetIdentityResolvingKey#%u", index); for (i = 0; i < 16; i++) sprintf(key + (i * 2), "%2.2X", sirk->key[i]); g_key_file_set_boolean(key_file, group, "Encrypted", sirk->encrypted); g_key_file_set_string(key_file, group, "Key", key); g_key_file_set_integer(key_file, group, "Size", sirk->size); g_key_file_set_integer(key_file, group, "Rank", sirk->rank); } static gboolean store_device_info_cb(gpointer user_data) { struct btd_device *device = user_data; GKeyFile *key_file; GError *gerr = NULL; char filename[PATH_MAX]; char device_addr[18]; char *str; char class[9]; char **uuids = NULL; gsize length = 0; device->store_id = 0; ba2str(&device->bdaddr, device_addr); create_filename(filename, PATH_MAX, "/%s/%s/info", btd_adapter_get_storage_dir(device->adapter), device_addr); create_file(filename, 0600); key_file = g_key_file_new(); if (!g_key_file_load_from_file(key_file, filename, 0, &gerr)) { error("Unable to load key file from %s: (%s)", filename, gerr->message); g_error_free(gerr); g_key_file_free(key_file); return FALSE; } g_key_file_set_string(key_file, "General", "Name", device->name); if (device->alias != NULL) g_key_file_set_string(key_file, "General", "Alias", device->alias); else g_key_file_remove_key(key_file, "General", "Alias", NULL); if (device->class) { sprintf(class, "0x%6.6x", device->class & 0xffffff); g_key_file_set_string(key_file, "General", "Class", class); } else { g_key_file_remove_key(key_file, "General", "Class", NULL); } if (device->appearance) { sprintf(class, "0x%4.4x", device->appearance); g_key_file_set_string(key_file, "General", "Appearance", class); } else { g_key_file_remove_key(key_file, "General", "Appearance", NULL); } update_technologies(key_file, device); g_key_file_set_boolean(key_file, "General", "Trusted", device->trusted); g_key_file_set_boolean(key_file, "General", "Blocked", device->blocked); if (device->wake_override != WAKE_FLAG_DEFAULT) { g_key_file_set_boolean(key_file, "General", "WakeAllowed", device->wake_override == WAKE_FLAG_ENABLED); } if (device->uuids) { GSList *l; int i; uuids = g_new0(char *, g_slist_length(device->uuids) + 1); for (i = 0, l = device->uuids; l; l = g_slist_next(l), i++) uuids[i] = l->data; g_key_file_set_string_list(key_file, "General", "Services", (const char **)uuids, i); } else { g_key_file_remove_key(key_file, "General", "Services", NULL); } if (device->vendor_src) { g_key_file_set_integer(key_file, "DeviceID", "Source", device->vendor_src); g_key_file_set_integer(key_file, "DeviceID", "Vendor", device->vendor); g_key_file_set_integer(key_file, "DeviceID", "Product", device->product); g_key_file_set_integer(key_file, "DeviceID", "Version", device->version); } else { g_key_file_remove_group(key_file, "DeviceID", NULL); } if (device->local_csrk) store_csrk(device->local_csrk, key_file, "LocalSignatureKey"); if (device->remote_csrk) store_csrk(device->remote_csrk, key_file, "RemoteSignatureKey"); if (!queue_isempty(device->sirks)) { const struct queue_entry *entry; int i; for (entry = queue_get_entries(device->sirks), i = 0; entry; entry = entry->next, i++) { struct sirk_info *sirk = entry->data; store_sirk(sirk, key_file, i); } } str = g_key_file_to_data(key_file, &length, NULL); if (!g_file_set_contents(filename, str, length, &gerr)) { error("Unable set contents for %s: (%s)", filename, gerr->message); g_error_free(gerr); } g_free(str); g_key_file_free(key_file); g_free(uuids); return FALSE; } bool device_address_is_private(struct btd_device *dev) { if (dev->bdaddr_type != BDADDR_LE_RANDOM) return false; switch (dev->bdaddr.b[5] >> 6) { case 0x00: /* Private non-resolvable */ case 0x01: /* Private resolvable */ return true; default: return false; } } static void store_device_info(struct btd_device *device) { if (device->temporary || device->store_id > 0) return; if (device_address_is_private(device)) { DBG("Can't store info for private addressed device %s", device->path); return; } device->store_id = g_idle_add(store_device_info_cb, device); } void device_store_cached_name(struct btd_device *dev, const char *name) { char filename[PATH_MAX]; char d_addr[18]; GKeyFile *key_file; GError *gerr = NULL; char *data; char *data_old; gsize length = 0; gsize length_old = 0; if (device_address_is_private(dev)) { DBG("Can't store name for private addressed device %s", dev->path); return; } ba2str(&dev->bdaddr, d_addr); create_filename(filename, PATH_MAX, "/%s/cache/%s", btd_adapter_get_storage_dir(dev->adapter), d_addr); create_file(filename, 0600); key_file = g_key_file_new(); if (!g_key_file_load_from_file(key_file, filename, 0, &gerr)) { error("Unable to load key file from %s: (%s)", filename, gerr->message); g_clear_error(&gerr); } data_old = g_key_file_to_data(key_file, &length_old, NULL); g_key_file_set_string(key_file, "General", "Name", name); data = g_key_file_to_data(key_file, &length, NULL); if ((length != length_old) || (memcmp(data, data_old, length))) { if (!g_file_set_contents(filename, data, length, &gerr)) { error("Unable set contents for %s: (%s)", filename, gerr->message); g_clear_error(&gerr); } } g_free(data); g_free(data_old); g_key_file_free(key_file); } static void device_store_cached_name_resolve(struct btd_device *dev) { char filename[PATH_MAX]; char d_addr[18]; GKeyFile *key_file; GError *gerr = NULL; char *data; char *data_old; gsize length = 0; gsize length_old = 0; uint64_t failed_time; if (device_address_is_private(dev)) { DBG("Can't store name resolve for private addressed device %s", dev->path); return; } ba2str(&dev->bdaddr, d_addr); create_filename(filename, PATH_MAX, "/%s/cache/%s", btd_adapter_get_storage_dir(dev->adapter), d_addr); create_file(filename, 0600); key_file = g_key_file_new(); if (!g_key_file_load_from_file(key_file, filename, 0, &gerr)) { error("Unable to load key file from %s: (%s)", filename, gerr->message); g_clear_error(&gerr); } failed_time = (uint64_t) dev->name_resolve_failed_time; data_old = g_key_file_to_data(key_file, &length_old, NULL); g_key_file_set_uint64(key_file, "NameResolving", "FailedTime", failed_time); data = g_key_file_to_data(key_file, &length, NULL); if ((length != length_old) || (memcmp(data, data_old, length))) { if (!g_file_set_contents(filename, data, length, &gerr)) { error("Unable set contents for %s: (%s)", filename, gerr->message); g_error_free(gerr); } } g_free(data); g_free(data_old); g_key_file_free(key_file); } static void browse_request_free(struct browse_req *req) { struct btd_device *device = req->device; if (device->browse == req) device->browse = NULL; if (req->listener_id) g_dbus_remove_watch(dbus_conn, req->listener_id); if (req->msg) dbus_message_unref(req->msg); g_slist_free_full(req->profiles_added, g_free); if (req->records) sdp_list_free(req->records, (sdp_free_func_t) sdp_record_free); g_free(req); } static bool gatt_cache_is_enabled(struct btd_device *device) { switch (btd_opts.gatt_cache) { case BT_GATT_CACHE_YES: return device_is_paired(device, device->bdaddr_type); case BT_GATT_CACHE_NO: return false; case BT_GATT_CACHE_ALWAYS: default: return true; } } static void gatt_cache_cleanup(struct btd_device *device) { if (gatt_cache_is_enabled(device)) return; bt_gatt_client_cancel_all(device->client); gatt_db_clear(device->db); device->le_state.svc_resolved = false; } static void gatt_client_cleanup(struct btd_device *device) { if (!device->client) return; gatt_cache_cleanup(device); bt_gatt_client_set_service_changed(device->client, NULL, NULL, NULL); if (device->gatt_ready_id > 0) { bt_gatt_client_ready_unregister(device->client, device->gatt_ready_id); device->gatt_ready_id = 0; } bt_gatt_client_unref(device->client); device->client = NULL; } static void gatt_server_cleanup(struct btd_device *device) { if (!device->server) return; btd_gatt_database_att_disconnected( btd_adapter_get_database(device->adapter), device); bt_gatt_server_unref(device->server); device->server = NULL; } static void attio_cleanup(struct btd_device *device) { if (device->att_disconn_id) bt_att_unregister_disconnect(device->att, device->att_disconn_id); if (device->att_io) { g_io_channel_shutdown(device->att_io, FALSE, NULL); g_io_channel_unref(device->att_io); device->att_io = NULL; } gatt_client_cleanup(device); gatt_server_cleanup(device); if (device->att) { bt_att_unref(device->att); device->att = NULL; } if (device->attrib) { GAttrib *attrib = device->attrib; device->attrib = NULL; g_attrib_cancel_all(attrib); g_attrib_unref(attrib); } } static void browse_request_cancel(struct browse_req *req) { struct btd_device *device = req->device; struct btd_adapter *adapter = device->adapter; DBG(""); bt_cancel_discovery(btd_adapter_get_address(adapter), &device->bdaddr); attio_cleanup(device); browse_request_free(req); } static void svc_dev_remove(gpointer user_data) { struct svc_callback *cb = user_data; if (cb->idle_id > 0) g_source_remove(cb->idle_id); cb->func(cb->dev, -ENODEV, cb->user_data); g_free(cb); } static void device_free(gpointer user_data) { struct btd_device *device = user_data; btd_gatt_client_destroy(device->client_dbus); device->client_dbus = NULL; g_slist_free_full(device->uuids, g_free); g_slist_free_full(device->primaries, g_free); g_slist_free_full(device->svc_callbacks, svc_dev_remove); /* Reset callbacks since the device is going to be freed */ gatt_db_unregister(device->db, device->db_id); attio_cleanup(device); gatt_db_unref(device->db); bt_ad_unref(device->ad); if (device->tmp_records) sdp_list_free(device->tmp_records, (sdp_free_func_t) sdp_record_free); if (device->disconn_timer) timeout_remove(device->disconn_timer); if (device->discov_timer) timeout_remove(device->discov_timer); if (device->temporary_timer) timeout_remove(device->temporary_timer); if (device->connect) dbus_message_unref(device->connect); if (device->disconnect) dbus_message_unref(device->disconnect); DBG("%p", device); if (device->authr) { if (device->authr->agent) agent_unref(device->authr->agent); g_free(device->authr->pincode); g_free(device->authr); } if (device->eir_uuids) g_slist_free_full(device->eir_uuids, g_free); queue_destroy(device->sirks, free); g_free(device->local_csrk); g_free(device->remote_csrk); free(device->ltk); g_free(device->path); g_free(device->alias); free(device->modalias); g_free(device); } bool device_is_paired(struct btd_device *device, uint8_t bdaddr_type) { struct bearer_state *state = get_state(device, bdaddr_type); return state->paired; } bool device_is_bonded(struct btd_device *device, uint8_t bdaddr_type) { struct bearer_state *state = get_state(device, bdaddr_type); return state->bonded; } bool btd_device_is_trusted(struct btd_device *device) { return device->trusted; } static gboolean dev_property_get_address(const GDBusPropertyTable *property, DBusMessageIter *iter, void *data) { struct btd_device *device = data; char dstaddr[18]; const char *ptr = dstaddr; ba2str(&device->bdaddr, dstaddr); dbus_message_iter_append_basic(iter, DBUS_TYPE_STRING, &ptr); return TRUE; } static gboolean property_get_address_type(const GDBusPropertyTable *property, DBusMessageIter *iter, void *user_data) { struct btd_device *device = user_data; const char *str; if (device->le && device->bdaddr_type == BDADDR_LE_RANDOM) str = "random"; else str = "public"; dbus_message_iter_append_basic(iter, DBUS_TYPE_STRING, &str); return TRUE; } static gboolean dev_property_get_name(const GDBusPropertyTable *property, DBusMessageIter *iter, void *data) { struct btd_device *device = data; const char *ptr = device->name; dbus_message_iter_append_basic(iter, DBUS_TYPE_STRING, &ptr); return TRUE; } static gboolean dev_property_exists_name(const GDBusPropertyTable *property, void *data) { struct btd_device *dev = data; return device_name_known(dev); } static gboolean dev_property_get_alias(const GDBusPropertyTable *property, DBusMessageIter *iter, void *data) { struct btd_device *device = data; char dstaddr[18]; const char *ptr; /* Alias (fallback to name or address) */ if (device->alias != NULL) ptr = device->alias; else if (strlen(device->name) > 0) { ptr = device->name; } else { ba2str(&device->bdaddr, dstaddr); g_strdelimit(dstaddr, ":", '-'); ptr = dstaddr; } dbus_message_iter_append_basic(iter, DBUS_TYPE_STRING, &ptr); return TRUE; } static void set_alias(GDBusPendingPropertySet id, const char *alias, void *data) { struct btd_device *device = data; /* No change */ if ((device->alias == NULL && g_str_equal(alias, "")) || g_strcmp0(device->alias, alias) == 0) { g_dbus_pending_property_success(id); return; } g_free(device->alias); device->alias = g_str_equal(alias, "") ? NULL : g_strdup(alias); store_device_info(device); g_dbus_emit_property_changed(dbus_conn, device->path, DEVICE_INTERFACE, "Alias"); g_dbus_pending_property_success(id); } static void dev_property_set_alias(const GDBusPropertyTable *property, DBusMessageIter *value, GDBusPendingPropertySet id, void *data) { const char *alias; if (dbus_message_iter_get_arg_type(value) != DBUS_TYPE_STRING) { g_dbus_pending_property_error(id, ERROR_INTERFACE ".InvalidArguments", "Invalid arguments in method call"); return; } dbus_message_iter_get_basic(value, &alias); set_alias(id, alias, data); } static gboolean dev_property_exists_class(const GDBusPropertyTable *property, void *data) { struct btd_device *device = data; return device->class != 0; } static gboolean dev_property_get_class(const GDBusPropertyTable *property, DBusMessageIter *iter, void *data) { struct btd_device *device = data; if (device->class == 0) return FALSE; dbus_message_iter_append_basic(iter, DBUS_TYPE_UINT32, &device->class); return TRUE; } static gboolean get_appearance(const GDBusPropertyTable *property, void *data, uint16_t *appearance) { struct btd_device *device = data; if (dev_property_exists_class(property, data)) return FALSE; if (device->appearance) { *appearance = device->appearance; return TRUE; } return FALSE; } static gboolean dev_property_exists_appearance( const GDBusPropertyTable *property, void *data) { uint16_t appearance; return get_appearance(property, data, &appearance); } static gboolean dev_property_get_appearance(const GDBusPropertyTable *property, DBusMessageIter *iter, void *data) { uint16_t appearance; if (!get_appearance(property, data, &appearance)) return FALSE; dbus_message_iter_append_basic(iter, DBUS_TYPE_UINT16, &appearance); return TRUE; } const char *btd_device_get_icon(struct btd_device *device) { const char *icon = NULL; if (device->class != 0) icon = class_to_icon(device->class); else if (device->appearance != 0) icon = gap_appearance_to_icon(device->appearance); return icon; } static gboolean dev_property_exists_icon( const GDBusPropertyTable *property, void *data) { return btd_device_get_icon(data) != NULL; } static gboolean dev_property_get_icon(const GDBusPropertyTable *property, DBusMessageIter *iter, void *data) { const char *icon; icon = btd_device_get_icon(data); if (icon == NULL) return FALSE; dbus_message_iter_append_basic(iter, DBUS_TYPE_STRING, &icon); return TRUE; } static gboolean dev_property_get_paired(const GDBusPropertyTable *property, DBusMessageIter *iter, void *data) { struct btd_device *dev = data; dbus_bool_t val; if (dev->bredr_state.paired || dev->le_state.paired) val = TRUE; else val = FALSE; dbus_message_iter_append_basic(iter, DBUS_TYPE_BOOLEAN, &val); return TRUE; } static gboolean dev_property_get_bonded(const GDBusPropertyTable *property, DBusMessageIter *iter, void *data) { struct btd_device *dev = data; dbus_bool_t val; if (dev->bredr_state.bonded || dev->le_state.bonded) val = TRUE; else val = FALSE; dbus_message_iter_append_basic(iter, DBUS_TYPE_BOOLEAN, &val); return TRUE; } static gboolean dev_property_get_legacy(const GDBusPropertyTable *property, DBusMessageIter *iter, void *data) { struct btd_device *device = data; dbus_bool_t val = device->legacy; dbus_message_iter_append_basic(iter, DBUS_TYPE_BOOLEAN, &val); return TRUE; } static gboolean dev_property_get_rssi(const GDBusPropertyTable *property, DBusMessageIter *iter, void *data) { struct btd_device *dev = data; dbus_int16_t val = dev->rssi; dbus_message_iter_append_basic(iter, DBUS_TYPE_INT16, &val); return TRUE; } static gboolean dev_property_exists_rssi(const GDBusPropertyTable *property, void *data) { struct btd_device *dev = data; if (dev->rssi == 0) return FALSE; return TRUE; } static gboolean dev_property_get_tx_power(const GDBusPropertyTable *property, DBusMessageIter *iter, void *data) { struct btd_device *dev = data; dbus_int16_t val = dev->tx_power; dbus_message_iter_append_basic(iter, DBUS_TYPE_INT16, &val); return TRUE; } static gboolean dev_property_exists_tx_power(const GDBusPropertyTable *property, void *data) { struct btd_device *dev = data; if (dev->tx_power == 127) return FALSE; return TRUE; } static gboolean dev_property_get_svc_resolved(const GDBusPropertyTable *property, DBusMessageIter *iter, void *data) { struct btd_device *device = data; gboolean val = device->svc_refreshed; dbus_message_iter_append_basic(iter, DBUS_TYPE_BOOLEAN, &val); return TRUE; } static gboolean dev_property_flags_exist(const GDBusPropertyTable *property, void *data) { struct btd_device *device = data; return device->ad_flags[0] != INVALID_FLAGS; } static gboolean dev_property_get_flags(const GDBusPropertyTable *property, DBusMessageIter *iter, void *data) { struct btd_device *device = data; uint8_t *flags = device->ad_flags; DBusMessageIter array; dbus_message_iter_open_container(iter, DBUS_TYPE_ARRAY, DBUS_TYPE_BYTE_AS_STRING, &array); dbus_message_iter_append_fixed_array(&array, DBUS_TYPE_BYTE, &flags, sizeof(device->ad_flags)); dbus_message_iter_close_container(iter, &array); return TRUE; } static gboolean dev_property_get_trusted(const GDBusPropertyTable *property, DBusMessageIter *iter, void *data) { struct btd_device *device = data; gboolean val = btd_device_is_trusted(device); dbus_message_iter_append_basic(iter, DBUS_TYPE_BOOLEAN, &val); return TRUE; } static void set_trust(GDBusPendingPropertySet id, gboolean value, void *data) { struct btd_device *device = data; btd_device_set_trusted(device, value); g_dbus_pending_property_success(id); } static void dev_property_set_trusted(const GDBusPropertyTable *property, DBusMessageIter *value, GDBusPendingPropertySet id, void *data) { dbus_bool_t b; if (dbus_message_iter_get_arg_type(value) != DBUS_TYPE_BOOLEAN) { g_dbus_pending_property_error(id, ERROR_INTERFACE ".InvalidArguments", "Invalid arguments in method call"); return; } dbus_message_iter_get_basic(value, &b); set_trust(id, b, data); } static gboolean dev_property_get_blocked(const GDBusPropertyTable *property, DBusMessageIter *iter, void *data) { struct btd_device *device = data; dbus_message_iter_append_basic(iter, DBUS_TYPE_BOOLEAN, &device->blocked); return TRUE; } static void set_blocked(GDBusPendingPropertySet id, gboolean value, void *data) { struct btd_device *device = data; int err; if (value) err = device_block(device, FALSE); else err = device_unblock(device, FALSE, FALSE); switch (-err) { case 0: g_dbus_pending_property_success(id); break; case EINVAL: g_dbus_pending_property_error(id, ERROR_INTERFACE ".Failed", "Kernel lacks reject list support"); break; default: g_dbus_pending_property_error(id, ERROR_INTERFACE ".Failed", strerror(-err)); break; } } static void dev_property_set_blocked(const GDBusPropertyTable *property, DBusMessageIter *value, GDBusPendingPropertySet id, void *data) { dbus_bool_t b; if (dbus_message_iter_get_arg_type(value) != DBUS_TYPE_BOOLEAN) { g_dbus_pending_property_error(id, ERROR_INTERFACE ".InvalidArguments", "Invalid arguments in method call"); return; } dbus_message_iter_get_basic(value, &b); set_blocked(id, b, data); } static gboolean dev_property_get_connected(const GDBusPropertyTable *property, DBusMessageIter *iter, void *data) { struct btd_device *dev = data; dbus_bool_t connected; if (dev->bredr_state.connected || dev->le_state.connected) connected = TRUE; else connected = FALSE; dbus_message_iter_append_basic(iter, DBUS_TYPE_BOOLEAN, &connected); return TRUE; } static gboolean dev_property_get_uuids(const GDBusPropertyTable *property, DBusMessageIter *iter, void *data) { struct btd_device *dev = data; DBusMessageIter entry; GSList *l; dbus_message_iter_open_container(iter, DBUS_TYPE_ARRAY, DBUS_TYPE_STRING_AS_STRING, &entry); if (dev->bredr_state.svc_resolved || dev->le_state.svc_resolved) l = dev->uuids; else if (dev->eir_uuids) l = dev->eir_uuids; else l = dev->uuids; for (; l != NULL; l = l->next) dbus_message_iter_append_basic(&entry, DBUS_TYPE_STRING, &l->data); dbus_message_iter_close_container(iter, &entry); return TRUE; } static gboolean dev_property_get_modalias(const GDBusPropertyTable *property, DBusMessageIter *iter, void *data) { struct btd_device *device = data; if (!device->modalias) return FALSE; dbus_message_iter_append_basic(iter, DBUS_TYPE_STRING, &device->modalias); return TRUE; } static gboolean dev_property_exists_modalias(const GDBusPropertyTable *property, void *data) { struct btd_device *device = data; return device->modalias ? TRUE : FALSE; } static gboolean dev_property_get_adapter(const GDBusPropertyTable *property, DBusMessageIter *iter, void *data) { struct btd_device *device = data; const char *str = adapter_get_path(device->adapter); dbus_message_iter_append_basic(iter, DBUS_TYPE_OBJECT_PATH, &str); return TRUE; } static void append_manufacturer_data(void *data, void *user_data) { struct bt_ad_manufacturer_data *md = data; DBusMessageIter *dict = user_data; g_dbus_dict_append_basic_array(dict, DBUS_TYPE_UINT16, &md->manufacturer_id, DBUS_TYPE_BYTE, &md->data, md->len); } static gboolean dev_property_get_manufacturer_data(const GDBusPropertyTable *property, DBusMessageIter *iter, void *data) { struct btd_device *device = data; DBusMessageIter dict; dbus_message_iter_open_container(iter, DBUS_TYPE_ARRAY, DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING DBUS_TYPE_UINT16_AS_STRING DBUS_TYPE_VARIANT_AS_STRING DBUS_DICT_ENTRY_END_CHAR_AS_STRING, &dict); bt_ad_foreach_manufacturer_data(device->ad, append_manufacturer_data, &dict); dbus_message_iter_close_container(iter, &dict); return TRUE; } static gboolean dev_property_manufacturer_data_exist(const GDBusPropertyTable *property, void *data) { struct btd_device *device = data; return bt_ad_has_manufacturer_data(device->ad, NULL); } static void append_service_data(void *data, void *user_data) { struct bt_ad_service_data *sd = data; DBusMessageIter *dict = user_data; char uuid_str[MAX_LEN_UUID_STR]; bt_uuid_to_string(&sd->uuid, uuid_str, sizeof(uuid_str)); dict_append_array(dict, uuid_str, DBUS_TYPE_BYTE, &sd->data, sd->len); } static gboolean dev_property_get_service_data(const GDBusPropertyTable *property, DBusMessageIter *iter, void *data) { struct btd_device *device = data; DBusMessageIter dict; dbus_message_iter_open_container(iter, DBUS_TYPE_ARRAY, DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING DBUS_TYPE_STRING_AS_STRING DBUS_TYPE_VARIANT_AS_STRING DBUS_DICT_ENTRY_END_CHAR_AS_STRING, &dict); bt_ad_foreach_service_data(device->ad, append_service_data, &dict); dbus_message_iter_close_container(iter, &dict); return TRUE; } static gboolean dev_property_service_data_exist(const GDBusPropertyTable *property, void *data) { struct btd_device *device = data; return bt_ad_has_service_data(device->ad, NULL); } static void append_advertising_data(void *data, void *user_data) { struct bt_ad_data *ad = data; DBusMessageIter *dict = user_data; g_dbus_dict_append_basic_array(dict, DBUS_TYPE_BYTE, &ad->type, DBUS_TYPE_BYTE, &ad->data, ad->len); } static gboolean dev_property_get_advertising_data(const GDBusPropertyTable *property, DBusMessageIter *iter, void *data) { struct btd_device *device = data; DBusMessageIter dict; dbus_message_iter_open_container(iter, DBUS_TYPE_ARRAY, DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING DBUS_TYPE_BYTE_AS_STRING DBUS_TYPE_VARIANT_AS_STRING DBUS_DICT_ENTRY_END_CHAR_AS_STRING, &dict); bt_ad_foreach_data(device->ad, append_advertising_data, &dict); dbus_message_iter_close_container(iter, &dict); return TRUE; } static gboolean dev_property_advertising_data_exist(const GDBusPropertyTable *property, void *data) { struct btd_device *device = data; return bt_ad_has_data(device->ad, NULL); } static bool device_get_wake_support(struct btd_device *device) { return device->wake_support; } void device_set_wake_support(struct btd_device *device, bool wake_support) { if (device->rpa && !btd_adapter_has_exp_feature(device->adapter, EXP_FEAT_RPA_RESOLUTION)) { warn("Unable to set wake_support without RPA resolution"); return; } device->wake_support = wake_support; if (device->wake_support) device->supported_flags |= DEVICE_FLAG_REMOTE_WAKEUP; else device->supported_flags &= ~DEVICE_FLAG_REMOTE_WAKEUP; /* If there is not override set, set the default the same as * support value. */ if (device->wake_override == WAKE_FLAG_DEFAULT) device_set_wake_override(device, wake_support); /* Set wake_allowed according to the override value. * Limit this to bonded device to avoid trying to set it * to new devices that are simply in range. */ if (device_is_bonded(device, device->bdaddr_type)) device_set_wake_allowed(device, device->wake_override == WAKE_FLAG_ENABLED, -1U); } static bool device_get_wake_allowed(struct btd_device *device) { return device->wake_allowed; } void device_set_wake_override(struct btd_device *device, bool wake_override) { if (wake_override) device->wake_override = WAKE_FLAG_ENABLED; else device->wake_override = WAKE_FLAG_DISABLED; } static void device_set_wake_allowed_complete(struct btd_device *device) { if (device->wake_id != -1U) { g_dbus_pending_property_success(device->wake_id); device->wake_id = -1U; } device->wake_allowed = device->pending_wake_allowed; g_dbus_emit_property_changed(dbus_conn, device->path, DEVICE_INTERFACE, "WakeAllowed"); store_device_info(device); } static void set_wake_allowed_complete(uint8_t status, uint16_t length, const void *param, void *user_data) { const struct mgmt_rp_set_device_flags *rp = param; struct btd_device *dev = user_data; if (status != MGMT_STATUS_SUCCESS) { error("Set device flags return status: %s", mgmt_errstr(status)); if (dev->wake_id != -1U) { g_dbus_pending_property_error(dev->wake_id, ERROR_INTERFACE ".Failed", mgmt_errstr(status)); dev->wake_id = -1U; } dev->pending_wake_allowed = FALSE; dev->pending_flags = 0; return; } if (length < sizeof(*rp)) { error("Too small Set Device Flags complete event: %d", length); return; } btd_device_flags_changed(dev, dev->supported_flags, dev->pending_flags); } void device_set_wake_allowed(struct btd_device *device, bool wake_allowed, GDBusPendingPropertySet id) { uint32_t flags; /* Pending and current value are the same unless there is a change in * progress. Only update wake allowed if pending value doesn't match the * new value. */ if (device->wake_id != -1U && id != -1U) { g_dbus_pending_property_error(id, ERROR_INTERFACE ".Busy", "Property change in progress"); return; } device->wake_id = id; device->pending_wake_allowed = wake_allowed; flags = device->current_flags; /* Include the pending flags, or they may get overwritten. */ flags |= device->pending_flags; if (wake_allowed) flags |= DEVICE_FLAG_REMOTE_WAKEUP; else flags &= ~DEVICE_FLAG_REMOTE_WAKEUP; adapter_set_device_flags(device->adapter, device, flags, set_wake_allowed_complete, device); } static gboolean dev_property_get_wake_allowed(const GDBusPropertyTable *property, DBusMessageIter *iter, void *data) { struct btd_device *device = data; dbus_bool_t wake_allowed = device_get_wake_allowed(device); dbus_message_iter_append_basic(iter, DBUS_TYPE_BOOLEAN, &wake_allowed); return TRUE; } static void dev_property_set_wake_allowed(const GDBusPropertyTable *property, DBusMessageIter *value, GDBusPendingPropertySet id, void *data) { struct btd_device *device = data; dbus_bool_t b; if (dbus_message_iter_get_arg_type(value) != DBUS_TYPE_BOOLEAN) { g_dbus_pending_property_error(id, ERROR_INTERFACE ".InvalidArguments", "Invalid arguments in method call"); return; } if (device->temporary) { g_dbus_pending_property_error(id, ERROR_INTERFACE ".Unsupported", "Cannot set property while temporary"); return; } dbus_message_iter_get_basic(value, &b); /* Emit busy or success depending on current value. */ if (b == device->pending_wake_allowed) { if (device->wake_allowed == device->pending_wake_allowed) g_dbus_pending_property_success(id); else g_dbus_pending_property_error( id, ERROR_INTERFACE ".Busy", "Property change in progress"); return; } device_set_wake_override(device, b); device_set_wake_allowed(device, b, id); } static gboolean dev_property_wake_allowed_exist( const GDBusPropertyTable *property, void *data) { struct btd_device *device = data; return device_get_wake_support(device); } static void append_set(void *data, void *user_data) { struct sirk_info *info = data; const char *path; DBusMessageIter *iter = user_data; DBusMessageIter entry, dict; if (!info->set) return; path = btd_set_get_path(info->set); dbus_message_iter_open_container(iter, DBUS_TYPE_DICT_ENTRY, NULL, &entry); dbus_message_iter_append_basic(&entry, DBUS_TYPE_OBJECT_PATH, &path); dbus_message_iter_open_container(&entry, DBUS_TYPE_ARRAY, DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING DBUS_TYPE_STRING_AS_STRING DBUS_TYPE_VARIANT_AS_STRING DBUS_DICT_ENTRY_END_CHAR_AS_STRING, &dict); g_dbus_dict_append_entry(&dict, "Rank", DBUS_TYPE_BYTE, &info->rank); dbus_message_iter_close_container(&entry, &dict); dbus_message_iter_close_container(iter, &entry); } static gboolean dev_property_get_set(const GDBusPropertyTable *property, DBusMessageIter *iter, void *data) { struct btd_device *device = data; DBusMessageIter array; dbus_message_iter_open_container(iter, DBUS_TYPE_ARRAY, DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING DBUS_TYPE_OBJECT_PATH_AS_STRING DBUS_TYPE_ARRAY_AS_STRING DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING DBUS_TYPE_STRING_AS_STRING DBUS_TYPE_VARIANT_AS_STRING DBUS_DICT_ENTRY_END_CHAR_AS_STRING DBUS_DICT_ENTRY_END_CHAR_AS_STRING, &array); queue_foreach(device->sirks, append_set, &array); dbus_message_iter_close_container(iter, &array); return TRUE; } static gboolean dev_property_set_exists(const GDBusPropertyTable *property, void *data) { struct btd_device *device = data; return !queue_isempty(device->sirks); } static bool disconnect_all(gpointer user_data) { struct btd_device *device = user_data; device->disconn_timer = 0; if (device->bredr_state.connected) btd_adapter_disconnect_device(device->adapter, &device->bdaddr, BDADDR_BREDR); if (device->le_state.connected) btd_adapter_disconnect_device(device->adapter, &device->bdaddr, device->bdaddr_type); return FALSE; } int device_block(struct btd_device *device, gboolean update_only) { int err = 0; if (device->blocked) return 0; if (device->disconn_timer > 0) timeout_remove(device->disconn_timer); disconnect_all(device); while (device->services != NULL) { struct btd_service *service = device->services->data; device->services = g_slist_remove(device->services, service); service_remove(service); } if (!update_only) { if (device->le) err = btd_adapter_block_address(device->adapter, &device->bdaddr, device->bdaddr_type); if (!err && device->bredr) err = btd_adapter_block_address(device->adapter, &device->bdaddr, BDADDR_BREDR); } if (err < 0) return err; device->blocked = TRUE; store_device_info(device); btd_device_set_temporary(device, false); g_dbus_emit_property_changed(dbus_conn, device->path, DEVICE_INTERFACE, "Blocked"); return 0; } int device_unblock(struct btd_device *device, gboolean silent, gboolean update_only) { int err = 0; if (!device->blocked) return 0; if (!update_only) { if (device->le) err = btd_adapter_unblock_address(device->adapter, &device->bdaddr, device->bdaddr_type); if (!err && device->bredr) err = btd_adapter_unblock_address(device->adapter, &device->bdaddr, BDADDR_BREDR); } if (err < 0) return err; device->blocked = FALSE; store_device_info(device); if (!silent) { g_dbus_emit_property_changed(dbus_conn, device->path, DEVICE_INTERFACE, "Blocked"); device_probe_profiles(device, device->uuids); } return 0; } static void browse_request_exit(DBusConnection *conn, void *user_data) { struct browse_req *req = user_data; DBG("Requestor exited"); browse_request_cancel(req); } static void bonding_request_cancel(struct bonding_req *bonding) { struct btd_device *device = bonding->device; struct btd_adapter *adapter = device->adapter; adapter_cancel_bonding(adapter, &device->bdaddr, device->bdaddr_type); } static void dev_disconn_service(gpointer a, gpointer b) { btd_service_disconnect(a); } void device_request_disconnect(struct btd_device *device, DBusMessage *msg) { if (device->bonding) bonding_request_cancel(device->bonding); if (device->browse) browse_request_cancel(device->browse); if (device->att_io) { g_io_channel_shutdown(device->att_io, FALSE, NULL); g_io_channel_unref(device->att_io); device->att_io = NULL; } if (device->connect) { DBusMessage *reply = btd_error_failed(device->connect, ERR_BREDR_CONN_CANCELED); g_dbus_send_message(dbus_conn, reply); dbus_message_unref(device->connect); device->connect = NULL; } if (btd_device_is_connected(device) && msg) device->disconnects = g_slist_append(device->disconnects, dbus_message_ref(msg)); if (device->disconn_timer) return; g_slist_foreach(device->services, dev_disconn_service, NULL); g_slist_free(device->pending); device->pending = NULL; while (device->watches) { struct btd_disconnect_data *data = device->watches->data; if (data->watch) /* temporary is set if device is going to be removed */ data->watch(device, device->temporary, data->user_data); /* Check if the watch has been removed by callback function */ if (!g_slist_find(device->watches, data)) continue; device->watches = g_slist_remove(device->watches, data); g_free(data); } if (!btd_device_is_connected(device)) { if (msg) g_dbus_send_reply(dbus_conn, msg, DBUS_TYPE_INVALID); return; } device->disconn_timer = timeout_add_seconds(DISCONNECT_TIMER, disconnect_all, device, NULL); } bool device_is_disconnecting(struct btd_device *device) { return device->disconn_timer > 0; } static void add_set(void *data, void *user_data) { struct sirk_info *sirk = data; struct btd_device *device = user_data; struct btd_device_set *set; if (!sirk->encrypted) return; set = btd_set_add_device(device, device->ltk->key, sirk->key, sirk->size); if (!set) return; if (sirk->set != set) { sirk->set = set; g_dbus_emit_property_changed(dbus_conn, device->path, DEVICE_INTERFACE, "Sets"); } } void device_set_ltk(struct btd_device *device, const uint8_t val[16], bool central, uint8_t enc_size) { if (!device->ltk) device->ltk = new0(struct ltk_info, 1); memcpy(device->ltk->key, val, sizeof(device->ltk->key)); device->ltk->central = central; device->ltk->enc_size = enc_size; bt_att_set_enc_key_size(device->att, enc_size); /* Check if there is any set/sirk that needs decryption */ queue_foreach(device->sirks, add_set, device); } bool btd_device_get_ltk(struct btd_device *device, uint8_t key[16], bool *central, uint8_t *enc_size) { if (!device || !device->ltk || !key) return false; memcpy(key, device->ltk->key, sizeof(device->ltk->key)); if (central) *central = device->ltk->central; if (enc_size) *enc_size = device->ltk->enc_size; return true; } void device_set_csrk(struct btd_device *device, const uint8_t val[16], uint32_t counter, uint8_t type, bool store_hint) { struct csrk_info **handle; struct csrk_info *csrk; bool auth; switch (type) { case 0x00: handle = &device->local_csrk; auth = FALSE; break; case 0x01: handle = &device->remote_csrk; auth = FALSE; break; case 0x02: handle = &device->local_csrk; auth = TRUE; break; case 0x03: handle = &device->remote_csrk; auth = TRUE; break; default: warn("Unsupported CSRK type %u", type); return; } if (!*handle) *handle = g_new0(struct csrk_info, 1); csrk = *handle; memcpy(csrk->key, val, sizeof(csrk->key)); csrk->counter = counter; csrk->auth = auth; if (!store_hint) return; store_device_info(device); btd_device_set_temporary(device, false); } static bool match_sirk(const void *data, const void *match_data) { const struct sirk_info *sirk = data; const uint8_t *key = match_data; return !memcmp(sirk->key, key, sizeof(sirk->key)); } static struct sirk_info *device_add_sirk_info(struct btd_device *device, bool encrypted, uint8_t key[16], uint8_t size, uint8_t rank) { struct sirk_info *sirk; sirk = queue_find(device->sirks, match_sirk, key); if (sirk) return sirk; sirk = new0(struct sirk_info, 1); sirk->encrypted = encrypted; memcpy(sirk->key, key, sizeof(sirk->key)); sirk->size = size; sirk->rank = rank; queue_push_tail(device->sirks, sirk); store_device_info(device); return sirk; } bool btd_device_add_set(struct btd_device *device, bool encrypted, uint8_t key[16], uint8_t size, uint8_t rank) { struct btd_device_set *set; struct sirk_info *sirk; if (encrypted && !device->ltk) return false; sirk = device_add_sirk_info(device, encrypted, key, size, rank); if (!sirk) return false; set = btd_set_add_device(device, encrypted ? device->ltk->key : NULL, key, size); if (!set) return false; if (sirk->set != set) { sirk->set = set; g_dbus_emit_property_changed(dbus_conn, device->path, DEVICE_INTERFACE, "Sets"); } return true; } static void device_set_auto_connect(struct btd_device *device, gboolean enable) { char addr[18]; const char *bearer; if (!device || !device->le || device_address_is_private(device)) return; ba2str(&device->bdaddr, addr); DBG("%s auto connect: %d", addr, enable); if (device->auto_connect == enable) return; device->auto_connect = enable; /* Disabling auto connect */ if (enable == FALSE) { adapter_connect_list_remove(device->adapter, device); adapter_auto_connect_remove(device->adapter, device); return; } /* Inhibit auto connect if BR/EDR bearer is preferred */ bearer = device_prefer_bearer_str(device); if (bearer && !strcasecmp(bearer, "bredr")) return; /* Enabling auto connect */ adapter_auto_connect_add(device->adapter, device); if (device->attrib) { DBG("Already connected"); return; } adapter_connect_list_add(device->adapter, device); } static DBusMessage *dev_disconnect(DBusConnection *conn, DBusMessage *msg, void *user_data) { struct btd_device *device = user_data; /* * If device is not trusted disable connections through passive * scanning until Device1.Connect is called */ if (device->auto_connect && !device->trusted) { device->disable_auto_connect = TRUE; device_set_auto_connect(device, FALSE); } device_request_disconnect(device, msg); return NULL; } static int connect_next(struct btd_device *dev) { struct btd_service *service; int err = -ENOENT; while (dev->pending) { service = dev->pending->data; err = btd_service_connect(service); if (!err) return 0; dev->pending = g_slist_delete_link(dev->pending, dev->pending); } return err; } static void device_profile_connected(struct btd_device *dev, struct btd_profile *profile, int err) { struct btd_service *pending; GSList *l; DBG("%s %s (%d)", profile->name, strerror(-err), -err); if (!err) btd_device_set_temporary(dev, false); if (dev->pending == NULL) goto done; if (!btd_device_is_connected(dev)) { switch (-err) { case EHOSTDOWN: /* page timeout */ case EHOSTUNREACH: /* adapter not powered */ case ECONNABORTED: /* adapter powered down */ goto done; } } pending = dev->pending->data; l = find_service_with_profile(dev->pending, profile); if (l != NULL) dev->pending = g_slist_delete_link(dev->pending, l); /* Only continue connecting the next profile if it matches the first * pending, otherwise it will trigger another connect to the same * profile */ if (profile != btd_service_get_profile(pending)) return; if (connect_next(dev) == 0) return; done: g_slist_free(dev->pending); dev->pending = NULL; if (!dev->connect) return; if (dbus_message_is_method_call(dev->connect, DEVICE_INTERFACE, "Connect")) { if (!err) dev->general_connect = TRUE; else if (find_service_with_state(dev->services, BTD_SERVICE_STATE_CONNECTED)) /* Reset error if there are services connected */ err = 0; } DBG("returning response to %s", dbus_message_get_sender(dev->connect)); if (err) { /* Fallback to LE bearer if supported */ if (err == -EHOSTDOWN && dev->le && !dev->le_state.connected) { err = device_connect_le(dev); if (err == 0) return; } g_dbus_send_message(dbus_conn, btd_error_failed(dev->connect, btd_error_bredr_conn_from_errno(err))); } else { /* Start passive SDP discovery to update known services */ if (dev->bredr && !dev->svc_refreshed && dev->refresh_discovery) device_browse_sdp(dev, NULL); g_dbus_send_reply(dbus_conn, dev->connect, DBUS_TYPE_INVALID); } dbus_message_unref(dev->connect); dev->connect = NULL; } void device_add_eir_uuids(struct btd_device *dev, GSList *uuids) { GSList *l; GSList *added = NULL; if (dev->bredr_state.svc_resolved || dev->le_state.svc_resolved) return; for (l = uuids; l != NULL; l = l->next) { const char *str = l->data; if (g_slist_find_custom(dev->eir_uuids, str, bt_uuid_strcmp)) continue; added = g_slist_append(added, (void *)str); dev->eir_uuids = g_slist_append(dev->eir_uuids, g_strdup(str)); } device_probe_profiles(dev, added); } static void add_manufacturer_data(void *data, void *user_data) { struct eir_msd *msd = data; struct btd_device *dev = user_data; if (!bt_ad_add_manufacturer_data(dev->ad, msd->company, msd->data, msd->data_len)) return; g_dbus_emit_property_changed(dbus_conn, dev->path, DEVICE_INTERFACE, "ManufacturerData"); } void device_set_manufacturer_data(struct btd_device *dev, GSList *list, bool duplicate) { if (duplicate) bt_ad_clear_manufacturer_data(dev->ad); g_slist_foreach(list, add_manufacturer_data, dev); } static void add_service_data(void *data, void *user_data) { struct eir_sd *sd = data; struct btd_device *dev = user_data; bt_uuid_t uuid; GSList *l; if (bt_string_to_uuid(&uuid, sd->uuid) < 0) return; if (!bt_ad_add_service_data(dev->ad, &uuid, sd->data, sd->data_len)) return; l = g_slist_append(NULL, sd->uuid); device_add_eir_uuids(dev, l); g_slist_free(l); g_dbus_emit_property_changed(dbus_conn, dev->path, DEVICE_INTERFACE, "ServiceData"); } void device_set_service_data(struct btd_device *dev, GSList *list, bool duplicate) { if (duplicate) bt_ad_clear_service_data(dev->ad); g_slist_foreach(list, add_service_data, dev); } static void add_data(void *data, void *user_data) { struct eir_ad *ad = data; struct btd_device *dev = user_data; if (!bt_ad_add_data(dev->ad, ad->type, ad->data, ad->len)) return; if (ad->type == EIR_TRANSPORT_DISCOVERY) g_dbus_emit_property_changed(dbus_conn, dev->path, DEVICE_INTERFACE, "AdvertisingData"); } void device_set_data(struct btd_device *dev, GSList *list, bool duplicate) { if (duplicate) bt_ad_clear_data(dev->ad); g_slist_foreach(list, add_data, dev); } static struct btd_service *find_connectable_service(struct btd_device *dev, const char *uuid) { GSList *l; for (l = dev->services; l != NULL; l = g_slist_next(l)) { struct btd_service *service = l->data; struct btd_profile *p = btd_service_get_profile(service); if (!p->connect || !p->remote_uuid) continue; if (strcasecmp(uuid, p->remote_uuid) == 0) return service; } return NULL; } static int service_prio_cmp(gconstpointer a, gconstpointer b) { struct btd_profile *p1 = btd_service_get_profile(a); struct btd_profile *p2 = btd_service_get_profile(b); return p2->priority - p1->priority; } bool btd_device_all_services_allowed(struct btd_device *dev) { GSList *l; struct btd_adapter *adapter = dev->adapter; struct btd_service *service; struct btd_profile *profile; for (l = dev->services; l != NULL; l = g_slist_next(l)) { service = l->data; profile = btd_service_get_profile(service); if (!profile || !profile->auto_connect) continue; if (!btd_adapter_is_uuid_allowed(adapter, profile->remote_uuid)) return false; } return true; } void btd_device_update_allowed_services(struct btd_device *dev) { struct btd_adapter *adapter = dev->adapter; struct btd_service *service; struct btd_profile *profile; GSList *l; bool is_allowed; char addr[18]; /* If service discovery is ongoing, let the service discovery complete * callback call this function. */ if (dev->browse) { ba2str(&dev->bdaddr, addr); DBG("service discovery of %s is ongoing. Skip updating allowed " "services", addr); return; } for (l = dev->services; l != NULL; l = g_slist_next(l)) { service = l->data; profile = btd_service_get_profile(service); is_allowed = btd_adapter_is_uuid_allowed(adapter, profile->remote_uuid); btd_service_set_allowed(service, is_allowed); } } static GSList *create_pending_list(struct btd_device *dev, const char *uuid) { struct btd_service *service; struct btd_profile *p; GSList *l; if (uuid) { service = find_connectable_service(dev, uuid); if (!service) return dev->pending; if (btd_service_is_allowed(service)) return g_slist_prepend(dev->pending, service); info("service %s is blocked", uuid); return dev->pending; } for (l = dev->services; l != NULL; l = g_slist_next(l)) { service = l->data; p = btd_service_get_profile(service); if (!p->auto_connect) continue; if (!btd_service_is_allowed(service)) { info("service %s is blocked", p->remote_uuid); continue; } if (g_slist_find(dev->pending, service)) continue; if (btd_service_get_state(service) != BTD_SERVICE_STATE_DISCONNECTED) continue; dev->pending = g_slist_insert_sorted(dev->pending, service, service_prio_cmp); } return dev->pending; } int btd_device_connect_services(struct btd_device *dev, GSList *services) { GSList *l; if (dev->pending || dev->connect || dev->browse) return -EBUSY; if (!btd_adapter_get_powered(dev->adapter)) return -ENETDOWN; if (!dev->bredr_state.svc_resolved) return -ENOENT; if (services) { for (l = services; l; l = g_slist_next(l)) { struct btd_service *service = l->data; dev->pending = g_slist_append(dev->pending, service); } } else { dev->pending = create_pending_list(dev, NULL); } return connect_next(dev); } static DBusMessage *connect_profiles(struct btd_device *dev, uint8_t bdaddr_type, DBusMessage *msg, const char *uuid) { struct bearer_state *state = get_state(dev, bdaddr_type); int err; DBG("%s %s, client %s", dev->path, uuid ? uuid : "(all)", dbus_message_get_sender(msg)); if (dev->pending || dev->connect || dev->browse) return btd_error_in_progress_str(msg, ERR_BREDR_CONN_BUSY); if (!btd_adapter_get_powered(dev->adapter)) { return btd_error_not_ready_str(msg, ERR_BREDR_CONN_ADAPTER_NOT_POWERED); } btd_device_set_temporary(dev, false); if (!state->svc_resolved) goto resolve_services; dev->pending = create_pending_list(dev, uuid); if (!dev->pending) { if (dev->svc_refreshed) { if (dbus_message_is_method_call(msg, DEVICE_INTERFACE, "Connect") && find_service_with_state(dev->services, BTD_SERVICE_STATE_CONNECTED)) { return dbus_message_new_method_return(msg); } else { return btd_error_not_available_str(msg, ERR_BREDR_CONN_PROFILE_UNAVAILABLE); } } goto resolve_services; } err = connect_next(dev); if (err < 0) { if (err == -EALREADY) return dbus_message_new_method_return(msg); return btd_error_failed(msg, btd_error_bredr_conn_from_errno(err)); } dev->connect = dbus_message_ref(msg); return NULL; resolve_services: DBG("Resolving services for %s", dev->path); if (bdaddr_type == BDADDR_BREDR) err = device_browse_sdp(dev, msg); else err = device_browse_gatt(dev, msg); if (err < 0) { return btd_error_failed(msg, bdaddr_type == BDADDR_BREDR ? ERR_BREDR_CONN_SDP_SEARCH : ERR_LE_CONN_GATT_BROWSE); } return NULL; } #define NVAL_TIME ((time_t) -1) #define SEEN_TRESHHOLD 300 static uint8_t select_conn_bearer(struct btd_device *dev) { time_t bredr_last = NVAL_TIME, le_last = NVAL_TIME; time_t current = time(NULL); /* Use preferred bearer or bonded bearer in case only one is bonded */ if (dev->bredr_state.prefer || (dev->bredr_state.bonded && !dev->le_state.bonded)) return BDADDR_BREDR; else if (dev->le_state.prefer || (!dev->bredr_state.bonded && dev->le_state.bonded)) return dev->bdaddr_type; /* If the address is random it can only be connected over LE */ if (dev->bdaddr_type == BDADDR_LE_RANDOM) return dev->bdaddr_type; if (dev->bredr_state.connectable && dev->bredr_state.last_seen) { bredr_last = current - dev->bredr_state.last_seen; if (bredr_last > SEEN_TRESHHOLD) bredr_last = NVAL_TIME; } if (dev->le_state.connectable && dev->le_state.last_seen) { le_last = current - dev->le_state.last_seen; if (le_last > SEEN_TRESHHOLD) le_last = NVAL_TIME; } if (le_last == NVAL_TIME && bredr_last == NVAL_TIME) return dev->bdaddr_type; if (dev->bredr && (!dev->le || le_last == NVAL_TIME)) return BDADDR_BREDR; if (dev->le && (!dev->bredr || bredr_last == NVAL_TIME)) return dev->bdaddr_type; /* * Prefer BR/EDR if time is the same since it might be from an * advertisement with BR/EDR flag set. */ if (bredr_last <= le_last && btd_adapter_get_bredr(dev->adapter)) return BDADDR_BREDR; return dev->bdaddr_type; } static DBusMessage *dev_connect(DBusConnection *conn, DBusMessage *msg, void *user_data) { struct btd_device *dev = user_data; uint8_t bdaddr_type; if (dev->bonding) return btd_error_in_progress(msg); if (dev->bredr_state.connected) { /* * Check if services have been resolved and there is at least * one connected before switching to connect LE. */ if (dev->bredr_state.svc_resolved && find_service_with_state(dev->services, BTD_SERVICE_STATE_CONNECTED)) bdaddr_type = dev->bdaddr_type; else bdaddr_type = BDADDR_BREDR; } else if (dev->le_state.connected && dev->bredr) bdaddr_type = BDADDR_BREDR; else bdaddr_type = select_conn_bearer(dev); if (bdaddr_type != BDADDR_BREDR) { int err; if (dev->connect) return btd_error_in_progress(msg); if (dev->le_state.connected) return dbus_message_new_method_return(msg); btd_device_set_temporary(dev, false); if (dev->disable_auto_connect) { dev->disable_auto_connect = FALSE; device_set_auto_connect(dev, TRUE); } err = device_connect_le(dev); if (err < 0) return btd_error_failed(msg, strerror(-err)); dev->connect = dbus_message_ref(msg); return NULL; } return connect_profiles(dev, bdaddr_type, msg, NULL); } static DBusMessage *connect_profile(DBusConnection *conn, DBusMessage *msg, void *user_data) { struct btd_device *dev = user_data; const char *pattern; char *uuid; DBusMessage *reply; if (!dbus_message_get_args(msg, NULL, DBUS_TYPE_STRING, &pattern, DBUS_TYPE_INVALID)) { return btd_error_invalid_args_str(msg, ERR_BREDR_CONN_INVALID_ARGUMENTS); } uuid = bt_name2string(pattern); if (uuid == NULL) return btd_error_invalid_args_str(msg, ERR_BREDR_CONN_INVALID_ARGUMENTS); reply = connect_profiles(dev, BDADDR_BREDR, msg, uuid); free(uuid); return reply; } static void device_profile_disconnected(struct btd_device *dev, struct btd_profile *profile, int err) { if (!dev->disconnect) return; if (err) g_dbus_send_message(dbus_conn, btd_error_failed(dev->disconnect, strerror(-err))); else g_dbus_send_reply(dbus_conn, dev->disconnect, DBUS_TYPE_INVALID); dbus_message_unref(dev->disconnect); dev->disconnect = NULL; } static DBusMessage *disconnect_profile(DBusConnection *conn, DBusMessage *msg, void *user_data) { struct btd_device *dev = user_data; struct btd_service *service; const char *pattern; char *uuid; int err; if (!dbus_message_get_args(msg, NULL, DBUS_TYPE_STRING, &pattern, DBUS_TYPE_INVALID)) return btd_error_invalid_args(msg); uuid = bt_name2string(pattern); if (uuid == NULL) return btd_error_invalid_args(msg); service = find_connectable_service(dev, uuid); free(uuid); if (!service) return btd_error_invalid_args(msg); if (dev->disconnect) return btd_error_in_progress(msg); if (btd_service_get_state(service) == BTD_SERVICE_STATE_DISCONNECTED) return dbus_message_new_method_return(msg); dev->disconnect = dbus_message_ref(msg); err = btd_service_disconnect(service); if (err == 0) return NULL; dbus_message_unref(dev->disconnect); dev->disconnect = NULL; if (err == -ENOTSUP) return btd_error_not_supported(msg); else if (err == -EALREADY) return dbus_message_new_method_return(msg); return btd_error_failed(msg, strerror(-err)); } static void store_services(struct btd_device *device) { char filename[PATH_MAX]; char dst_addr[18]; uuid_t uuid; char *prim_uuid; GKeyFile *key_file; GError *gerr = NULL; GSList *l; char *data; gsize length = 0; if (device_address_is_private(device)) { DBG("Can't store services for private addressed device %s", device->path); return; } sdp_uuid16_create(&uuid, GATT_PRIM_SVC_UUID); prim_uuid = bt_uuid2string(&uuid); if (prim_uuid == NULL) return; ba2str(&device->bdaddr, dst_addr); create_filename(filename, PATH_MAX, "/%s/%s/attributes", btd_adapter_get_storage_dir(device->adapter), dst_addr); key_file = g_key_file_new(); for (l = device->primaries; l; l = l->next) { struct gatt_primary *primary = l->data; char handle[6], uuid_str[33]; int i; sprintf(handle, "%hu", primary->range.start); bt_string2uuid(&uuid, primary->uuid); sdp_uuid128_to_uuid(&uuid); switch (uuid.type) { case SDP_UUID16: sprintf(uuid_str, "%4.4X", uuid.value.uuid16); break; case SDP_UUID32: sprintf(uuid_str, "%8.8X", uuid.value.uuid32); break; case SDP_UUID128: for (i = 0; i < 16; i++) sprintf(uuid_str + (i * 2), "%2.2X", uuid.value.uuid128.data[i]); break; default: uuid_str[0] = '\0'; } g_key_file_set_string(key_file, handle, "UUID", prim_uuid); g_key_file_set_string(key_file, handle, "Value", uuid_str); g_key_file_set_integer(key_file, handle, "EndGroupHandle", primary->range.end); } data = g_key_file_to_data(key_file, &length, NULL); if (length > 0) { create_file(filename, 0600); if (!g_file_set_contents(filename, data, length, &gerr)) { error("Unable set contents for %s: (%s)", filename, gerr->message); g_error_free(gerr); } } free(prim_uuid); g_free(data); g_key_file_free(key_file); } static void store_gatt_db(struct btd_device *device) { char filename[PATH_MAX]; char dst_addr[18]; if (device_address_is_private(device)) { DBG("Can't store GATT db for private addressed device %s", device->path); return; } if (!gatt_cache_is_enabled(device)) return; ba2str(&device->bdaddr, dst_addr); create_filename(filename, PATH_MAX, "/%s/cache/%s", btd_adapter_get_storage_dir(device->adapter), dst_addr); create_file(filename, 0600); btd_settings_gatt_db_store(device->db, filename); } static void browse_request_complete(struct browse_req *req, uint8_t type, uint8_t bdaddr_type, int err) { struct btd_device *dev = req->device; DBusMessage *reply = NULL; DBusMessage *msg; if (req->type != type) return; if (!req->msg) goto done; if (dbus_message_is_method_call(req->msg, DEVICE_INTERFACE, "Pair")) { if (!device_is_paired(dev, bdaddr_type)) { reply = btd_error_failed(req->msg, "Not paired"); goto done; } if (dev->pending_paired) { g_dbus_emit_property_changed(dbus_conn, dev->path, DEVICE_INTERFACE, "Paired"); dev->pending_paired = false; } /* Disregard browse errors in case of Pair */ reply = g_dbus_create_reply(req->msg, DBUS_TYPE_INVALID); goto done; } if (err) { /* Fallback to LE bearer if supported */ if (err == -EHOSTDOWN && bdaddr_type == BDADDR_BREDR && dev->le && !dev->le_state.connected) { err = device_connect_le(dev); if (err == 0) goto done; } reply = btd_error_failed(req->msg, bdaddr_type == BDADDR_BREDR ? btd_error_bredr_conn_from_errno(err) : btd_error_le_conn_from_errno(err)); goto done; } /* if successfully resolved services we need to free browsing request * before passing message back to connect functions, otherwise * device->browse is set and "InProgress" error is returned instead * of actually connecting services */ msg = dbus_message_ref(req->msg); browse_request_free(req); req = NULL; if (dbus_message_is_method_call(msg, DEVICE_INTERFACE, "Connect")) reply = dev_connect(dbus_conn, msg, dev); else if (dbus_message_is_method_call(msg, DEVICE_INTERFACE, "ConnectProfile")) reply = connect_profile(dbus_conn, msg, dev); else reply = g_dbus_create_reply(msg, DBUS_TYPE_INVALID); dbus_message_unref(msg); done: if (reply) g_dbus_send_message(dbus_conn, reply); if (req) browse_request_free(req); } void device_set_refresh_discovery(struct btd_device *dev, bool refresh) { dev->refresh_discovery = refresh; } static void device_set_svc_refreshed(struct btd_device *device, bool value) { if (device->svc_refreshed == value) return; device->svc_refreshed = value; g_dbus_emit_property_changed(dbus_conn, device->path, DEVICE_INTERFACE, "ServicesResolved"); } static void device_svc_resolved(struct btd_device *dev, uint8_t browse_type, uint8_t bdaddr_type, int err) { struct bearer_state *state = get_state(dev, bdaddr_type); struct browse_req *req = dev->browse; DBG("%s err %d", dev->path, err); state->svc_resolved = true; /* Disconnection notification can happen before this function * gets called, so don't set svc_refreshed for a disconnected * device. */ if (state->connected) device_set_svc_refreshed(dev, true); g_slist_free_full(dev->eir_uuids, g_free); dev->eir_uuids = NULL; if (dev->pending_paired) { g_dbus_emit_property_changed(dbus_conn, dev->path, DEVICE_INTERFACE, "Paired"); dev->pending_paired = false; } if (!dev->temporary) { store_device_info(dev); if (bdaddr_type != BDADDR_BREDR && err == 0) store_services(dev); } if (req) browse_request_complete(req, browse_type, bdaddr_type, err); while (dev->svc_callbacks) { struct svc_callback *cb = dev->svc_callbacks->data; if (cb->idle_id > 0) g_source_remove(cb->idle_id); cb->func(dev, err, cb->user_data); dev->svc_callbacks = g_slist_delete_link(dev->svc_callbacks, dev->svc_callbacks); g_free(cb); } btd_device_update_allowed_services(dev); device_resolved_drivers(dev->adapter, dev); } static struct bonding_req *bonding_request_new(DBusMessage *msg, struct btd_device *device, uint8_t bdaddr_type, struct agent *agent) { struct bonding_req *bonding; char addr[18]; ba2str(&device->bdaddr, addr); DBG("Requesting bonding for %s", addr); bonding = g_new0(struct bonding_req, 1); bonding->msg = dbus_message_ref(msg); bonding->bdaddr_type = bdaddr_type; bonding->cb_iter = btd_adapter_pin_cb_iter_new(device->adapter); /* Marks the bonding start time for the first attempt on request * construction. The following attempts will be updated on * device_bonding_retry. */ clock_gettime(CLOCK_MONOTONIC, &bonding->attempt_start_time); if (agent) bonding->agent = agent_ref(agent); return bonding; } void device_bonding_restart_timer(struct btd_device *device) { if (!device || !device->bonding) return; clock_gettime(CLOCK_MONOTONIC, &device->bonding->attempt_start_time); } static void bonding_request_stop_timer(struct bonding_req *bonding) { struct timespec current; clock_gettime(CLOCK_MONOTONIC, ¤t); /* Compute the time difference in ms. */ bonding->last_attempt_duration_ms = (current.tv_sec - bonding->attempt_start_time.tv_sec) * 1000L + (current.tv_nsec - bonding->attempt_start_time.tv_nsec) / 1000000L; } /* Returns the duration of the last bonding attempt in milliseconds. The * duration is measured starting from the latest of the following three * events and finishing when the Command complete event is received for the * authentication request: * - MGMT_OP_PAIR_DEVICE is sent, * - MGMT_OP_PIN_CODE_REPLY is sent and * - Command complete event is received for the sent MGMT_OP_PIN_CODE_REPLY. */ long device_bonding_last_duration(struct btd_device *device) { struct bonding_req *bonding = device->bonding; if (!bonding) return 0; return bonding->last_attempt_duration_ms; } static void create_bond_req_exit(DBusConnection *conn, void *user_data) { struct btd_device *device = user_data; char addr[18]; ba2str(&device->bdaddr, addr); DBG("%s: requestor exited before bonding was completed", addr); if (device->authr) device_cancel_authentication(device, FALSE); if (device->bonding) { device->bonding->listener_id = 0; device_request_disconnect(device, NULL); } } static void bonding_request_free(struct bonding_req *bonding) { if (!bonding) return; if (bonding->listener_id) g_dbus_remove_watch(dbus_conn, bonding->listener_id); if (bonding->msg) dbus_message_unref(bonding->msg); if (bonding->cb_iter) g_free(bonding->cb_iter); if (bonding->agent) { agent_cancel(bonding->agent); agent_unref(bonding->agent); bonding->agent = NULL; } if (bonding->retry_timer) g_source_remove(bonding->retry_timer); if (bonding->device) bonding->device->bonding = NULL; g_free(bonding); } static DBusMessage *pair_device(DBusConnection *conn, DBusMessage *msg, void *data) { struct btd_device *device = data; struct btd_adapter *adapter = device->adapter; struct bearer_state *state; uint8_t bdaddr_type; const char *sender; struct agent *agent; struct bonding_req *bonding; uint8_t io_cap; int err; btd_device_set_temporary(device, false); if (!dbus_message_get_args(msg, NULL, DBUS_TYPE_INVALID)) return btd_error_invalid_args(msg); if (device->bonding || device->connect) return btd_error_in_progress(msg); /* Only use this selection algorithms when device is combo * chip. Ohterwise, it will use the wrong bearer to establish * a connection if the device is already paired, which will * stall the pairing procedure. For example, for a BLE only * device, if the device is already paired, and upper layer * issue the pair device again, it will set bdaddr_type to * BDADDR_BREDR since LE is bonded, then it goes with BR/EDR * bearer. */ if (device->bredr && device->le) { if (device->bredr_state.bonded) bdaddr_type = device->bdaddr_type; else if (device->le_state.bonded) bdaddr_type = BDADDR_BREDR; else bdaddr_type = select_conn_bearer(device); } else { bdaddr_type = device->bdaddr_type; } state = get_state(device, bdaddr_type); if (state->bonded) return btd_error_already_exists(msg); sender = dbus_message_get_sender(msg); agent = agent_get(sender); if (agent) io_cap = agent_get_io_capability(agent); else io_cap = IO_CAPABILITY_NOINPUTNOOUTPUT; bonding = bonding_request_new(msg, device, bdaddr_type, agent); if (agent) agent_unref(agent); bonding->listener_id = g_dbus_add_disconnect_watch(dbus_conn, sender, create_bond_req_exit, device, NULL); device->bonding = bonding; bonding->device = device; /* Due to a bug in the kernel we might loose out on ATT commands * that arrive during the SMP procedure, so connect the ATT * channel first and only then start pairing (there's code for * this in the ATT connect callback) */ if (bdaddr_type != BDADDR_BREDR) { if (device->disable_auto_connect) { device->disable_auto_connect = FALSE; device_set_auto_connect(device, TRUE); } if (!state->connected && btd_le_connect_before_pairing()) err = device_connect_le(device); else if (!state->connected || !bt_att_set_security(device->att, BT_ATT_SECURITY_MEDIUM)) err = adapter_create_bonding(adapter, &device->bdaddr, device->bdaddr_type, io_cap); else err = 0; } else { err = adapter_create_bonding(adapter, &device->bdaddr, BDADDR_BREDR, io_cap); } if (err < 0) { bonding_request_free(device->bonding); return btd_error_failed(msg, strerror(-err)); } return NULL; } static DBusMessage *new_authentication_return(DBusMessage *msg, uint8_t status) { switch (status) { case MGMT_STATUS_SUCCESS: return dbus_message_new_method_return(msg); case MGMT_STATUS_CONNECT_FAILED: return dbus_message_new_error(msg, ERROR_INTERFACE ".ConnectionAttemptFailed", "Page Timeout"); case MGMT_STATUS_TIMEOUT: return dbus_message_new_error(msg, ERROR_INTERFACE ".AuthenticationTimeout", "Authentication Timeout"); case MGMT_STATUS_BUSY: case MGMT_STATUS_REJECTED: return dbus_message_new_error(msg, ERROR_INTERFACE ".AuthenticationRejected", "Authentication Rejected"); case MGMT_STATUS_CANCELLED: case MGMT_STATUS_NO_RESOURCES: case MGMT_STATUS_DISCONNECTED: return dbus_message_new_error(msg, ERROR_INTERFACE ".AuthenticationCanceled", "Authentication Canceled"); case MGMT_STATUS_ALREADY_PAIRED: return dbus_message_new_error(msg, ERROR_INTERFACE ".AlreadyExists", "Already Paired"); default: return dbus_message_new_error(msg, ERROR_INTERFACE ".AuthenticationFailed", "Authentication Failed"); } } static void device_cancel_bonding(struct btd_device *device, uint8_t status) { struct bonding_req *bonding = device->bonding; DBusMessage *reply; char addr[18]; if (!bonding) return; ba2str(&device->bdaddr, addr); DBG("Canceling bonding request for %s", addr); if (device->authr) device_cancel_authentication(device, FALSE); reply = new_authentication_return(bonding->msg, status); g_dbus_send_message(dbus_conn, reply); bonding_request_cancel(bonding); bonding_request_free(bonding); } static DBusMessage *cancel_pairing(DBusConnection *conn, DBusMessage *msg, void *data) { struct btd_device *device = data; struct bonding_req *req = device->bonding; DBG(""); if (!req) { btd_adapter_remove_bonding(device->adapter, &device->bdaddr, device->bdaddr_type); return btd_error_does_not_exist(msg); } device_cancel_bonding(device, MGMT_STATUS_CANCELLED); return dbus_message_new_method_return(msg); } static sdp_list_t *read_device_records(struct btd_device *device); static DBusMessage *get_service_records(DBusConnection *conn, DBusMessage *msg, void *data) { DBusMessage *reply; DBusMessageIter records_arr, record; struct btd_device *device = data; sdp_list_t *cur; if (!btd_adapter_get_powered(device->adapter)) return btd_error_not_ready(msg); if (!btd_device_is_connected(device)) return btd_error_not_connected(msg); if (!device->bredr_state.svc_resolved) return btd_error_not_ready(msg); if (!device->tmp_records) { device->tmp_records = read_device_records(device); if (!device->tmp_records) return btd_error_does_not_exist(msg); } reply = dbus_message_new_method_return(msg); if (!reply) return btd_error_failed(msg, "Could not create method reply"); dbus_message_iter_init_append(reply, &records_arr); if (!dbus_message_iter_open_container(&records_arr, DBUS_TYPE_ARRAY, "ay", &record)) { dbus_message_unref(reply); return btd_error_failed(msg, "Could not initialize iterator"); } for (cur = device->tmp_records; cur; cur = cur->next) { DBusMessageIter record_bytes; sdp_record_t *rec = cur->data; sdp_buf_t buf; int result; result = sdp_gen_record_pdu(rec, &buf); if (result) { dbus_message_iter_abandon_container(&records_arr, &record); dbus_message_unref(reply); return btd_error_failed( msg, "Could not marshal service record"); } if (!dbus_message_iter_open_container(&record, DBUS_TYPE_ARRAY, "y", &record_bytes)) { bt_free(buf.data); dbus_message_iter_abandon_container(&records_arr, &record); dbus_message_unref(reply); return btd_error_failed( msg, "Could not initialize iterator"); } if (!dbus_message_iter_append_fixed_array( &record_bytes, DBUS_TYPE_BYTE, &buf.data, buf.data_size)) { bt_free(buf.data); dbus_message_iter_abandon_container(&record, &record_bytes); dbus_message_iter_abandon_container(&records_arr, &record); dbus_message_unref(reply); return btd_error_failed( msg, "Could not append record data to reply"); } dbus_message_iter_close_container(&record, &record_bytes); bt_free(buf.data); } dbus_message_iter_close_container(&records_arr, &record); return reply; } static const GDBusMethodTable device_methods[] = { { GDBUS_ASYNC_METHOD("Disconnect", NULL, NULL, dev_disconnect) }, { GDBUS_ASYNC_METHOD("Connect", NULL, NULL, dev_connect) }, { GDBUS_ASYNC_METHOD("ConnectProfile", GDBUS_ARGS({ "UUID", "s" }), NULL, connect_profile) }, { GDBUS_ASYNC_METHOD("DisconnectProfile", GDBUS_ARGS({ "UUID", "s" }), NULL, disconnect_profile) }, { GDBUS_ASYNC_METHOD("Pair", NULL, NULL, pair_device) }, { GDBUS_METHOD("CancelPairing", NULL, NULL, cancel_pairing) }, { GDBUS_EXPERIMENTAL_METHOD("GetServiceRecords", NULL, GDBUS_ARGS({ "Records", "aay" }), get_service_records) }, { } }; static gboolean dev_property_get_prefer_bearer(const GDBusPropertyTable *property, DBusMessageIter *iter, void *data) { struct btd_device *device = data; const char *str = device_prefer_bearer_str(device); dbus_message_iter_append_basic(iter, DBUS_TYPE_STRING, &str); return TRUE; } static void dev_property_set_prefer_bearer(const GDBusPropertyTable *property, DBusMessageIter *value, GDBusPendingPropertySet id, void *data) { struct btd_device *device = data; const char *str; if (dbus_message_iter_get_arg_type(value) != DBUS_TYPE_STRING) { g_dbus_pending_property_error(id, ERROR_INTERFACE ".InvalidArguments", "Invalid arguments in method call"); return; } dbus_message_iter_get_basic(value, &str); /* Check if current preferred bearer is the same */ if (!strcasecmp(device_prefer_bearer_str(device), str)) goto done; if (!strcasecmp(str, "last-seen")) { device->bredr_state.prefer = false; device->le_state.prefer = false; } else if (!strcasecmp(str, "bredr")) { device->bredr_state.prefer = true; device->le_state.prefer = false; /* Remove device from auto-connect list so the kernel does not * attempt to auto-connect to it in case it starts advertising. */ device_set_auto_connect(device, FALSE); } else if (!strcasecmp(str, "le")) { device->le_state.prefer = true; device->bredr_state.prefer = false; /* Add device to auto-connect list */ device_set_auto_connect(device, TRUE); } else { g_dbus_pending_property_error(id, ERROR_INTERFACE ".InvalidArguments", "Invalid arguments in method call"); return; } store_device_info(device); g_dbus_emit_property_changed(dbus_conn, device->path, DEVICE_INTERFACE, "PreferredBearer"); done: g_dbus_pending_property_success(id); } static gboolean dev_property_prefer_bearer_exists(const GDBusPropertyTable *property, void *data) { struct btd_device *device = data; return device_prefer_bearer_str(device) != NULL; } static const GDBusPropertyTable device_properties[] = { { "Address", "s", dev_property_get_address }, { "AddressType", "s", property_get_address_type }, { "Name", "s", dev_property_get_name, NULL, dev_property_exists_name }, { "Alias", "s", dev_property_get_alias, dev_property_set_alias }, { "Class", "u", dev_property_get_class, NULL, dev_property_exists_class }, { "Appearance", "q", dev_property_get_appearance, NULL, dev_property_exists_appearance }, { "Icon", "s", dev_property_get_icon, NULL, dev_property_exists_icon }, { "Paired", "b", dev_property_get_paired }, { "Bonded", "b", dev_property_get_bonded }, { "Trusted", "b", dev_property_get_trusted, dev_property_set_trusted }, { "Blocked", "b", dev_property_get_blocked, dev_property_set_blocked }, { "LegacyPairing", "b", dev_property_get_legacy }, { "RSSI", "n", dev_property_get_rssi, NULL, dev_property_exists_rssi }, { "Connected", "b", dev_property_get_connected }, { "UUIDs", "as", dev_property_get_uuids }, { "Modalias", "s", dev_property_get_modalias, NULL, dev_property_exists_modalias }, { "Adapter", "o", dev_property_get_adapter }, { "ManufacturerData", "a{qv}", dev_property_get_manufacturer_data, NULL, dev_property_manufacturer_data_exist }, { "ServiceData", "a{sv}", dev_property_get_service_data, NULL, dev_property_service_data_exist }, { "TxPower", "n", dev_property_get_tx_power, NULL, dev_property_exists_tx_power }, { "ServicesResolved", "b", dev_property_get_svc_resolved, NULL, NULL }, { "AdvertisingFlags", "ay", dev_property_get_flags, NULL, dev_property_flags_exist }, { "AdvertisingData", "a{yv}", dev_property_get_advertising_data, NULL, dev_property_advertising_data_exist }, { "WakeAllowed", "b", dev_property_get_wake_allowed, dev_property_set_wake_allowed, dev_property_wake_allowed_exist }, { "Sets", "a{oa{sv}}", dev_property_get_set, NULL, dev_property_set_exists }, { "PreferredBearer", "s", dev_property_get_prefer_bearer, dev_property_set_prefer_bearer, dev_property_prefer_bearer_exists, G_DBUS_PROPERTY_FLAG_EXPERIMENTAL }, { } }; uint8_t btd_device_get_bdaddr_type(struct btd_device *dev) { return dev->bdaddr_type; } bool btd_device_is_connected(struct btd_device *dev) { if (btd_device_bearer_is_connected(dev)) return true; return find_service_with_state(dev->services, BTD_SERVICE_STATE_CONNECTED); } bool btd_device_bearer_is_connected(struct btd_device *dev) { return dev->bredr_state.connected || dev->le_state.connected; } static void clear_temporary_timer(struct btd_device *dev) { if (dev->temporary_timer) { timeout_remove(dev->temporary_timer); dev->temporary_timer = 0; } } void device_add_connection(struct btd_device *dev, uint8_t bdaddr_type, uint32_t flags) { struct bearer_state *state = get_state(dev, bdaddr_type); device_update_last_seen(dev, bdaddr_type, true); if (state->connected) { char addr[18]; ba2str(&dev->bdaddr, addr); error("Device %s is already connected", addr); return; } bacpy(&dev->conn_bdaddr, &dev->bdaddr); dev->conn_bdaddr_type = dev->bdaddr_type; /* If this is the first connection over this bearer */ if (bdaddr_type == BDADDR_BREDR) device_set_bredr_support(dev); else device_set_le_support(dev, bdaddr_type); state->connected = true; state->initiator = flags & BIT(3); if (dev->le_state.connected && dev->bredr_state.connected) return; /* Remove temporary timer while connected */ clear_temporary_timer(dev); g_dbus_emit_property_changed(dbus_conn, dev->path, DEVICE_INTERFACE, "Connected"); } static bool device_service_connected(struct btd_device *dev) { if (find_service_with_state(dev->services, BTD_SERVICE_STATE_CONNECTING)) return true; return find_service_with_state(dev->services, BTD_SERVICE_STATE_CONNECTED); } static bool device_disappeared(gpointer user_data) { struct btd_device *dev = user_data; /* If there are services connected restart the timer to give more time * for the service to either complete the connection or disconnect. */ if (device_service_connected(dev)) return TRUE; dev->temporary_timer = 0; btd_adapter_remove_device(dev->adapter, dev); return FALSE; } static void set_temporary_timer(struct btd_device *dev, unsigned int timeout) { clear_temporary_timer(dev); if (!timeout) return; dev->temporary_timer = timeout_add_seconds(timeout, device_disappeared, dev, NULL); } void device_remove_connection(struct btd_device *device, uint8_t bdaddr_type, bool *remove) { struct bearer_state *state = get_state(device, bdaddr_type); DBusMessage *reply; bool remove_device = false; bool paired_status_updated = false; if (!state->connected) return; state->connected = false; state->initiator = false; device->general_connect = FALSE; device_set_svc_refreshed(device, false); if (device->disconn_timer > 0) { timeout_remove(device->disconn_timer); device->disconn_timer = 0; } /* This could be executed while the client is waiting for Connect() but * att_connect_cb has not been invoked. * In that case reply the client that the connection failed. */ if (device->connect) { DBG("connection removed while Connect() is waiting reply"); reply = btd_error_failed(device->connect, ERR_BREDR_CONN_CANCELED); g_dbus_send_message(dbus_conn, reply); dbus_message_unref(device->connect); device->connect = NULL; } /* Check paired status of both bearers since it's possible to be * paired but not connected via link key to LTK conversion. */ if (!device->bredr_state.connected && device->bredr_state.paired && !device->bredr_state.bonded) { btd_adapter_remove_bonding(device->adapter, &device->bdaddr, BDADDR_BREDR); device->bredr_state.paired = false; paired_status_updated = true; } if (!device->le_state.connected && device->le_state.paired && !device->le_state.bonded) { btd_adapter_remove_bonding(device->adapter, &device->bdaddr, device->bdaddr_type); device->le_state.paired = false; paired_status_updated = true; } /* report change only if both bearers are unpaired */ if (!device->bredr_state.paired && !device->le_state.paired && paired_status_updated) g_dbus_emit_property_changed(dbus_conn, device->path, DEVICE_INTERFACE, "Paired"); if (device->bredr_state.connected || device->le_state.connected) return; device_update_last_seen(device, bdaddr_type, true); g_slist_free_full(device->eir_uuids, g_free); device->eir_uuids = NULL; g_dbus_emit_property_changed(dbus_conn, device->path, DEVICE_INTERFACE, "Connected"); /* remove device only if both bearers are disconnected */ while (device->disconnects) { DBusMessage *msg = device->disconnects->data; if (dbus_message_is_method_call(msg, ADAPTER_INTERFACE, "RemoveDevice")) remove_device = true; g_dbus_send_reply(dbus_conn, msg, DBUS_TYPE_INVALID); device->disconnects = g_slist_remove(device->disconnects, msg); dbus_message_unref(msg); } if (remove_device) *remove = remove_device; } guint device_add_disconnect_watch(struct btd_device *device, disconnect_watch watch, void *user_data, GDestroyNotify destroy) { struct btd_disconnect_data *data; static guint id = 0; data = g_new0(struct btd_disconnect_data, 1); data->id = ++id; data->watch = watch; data->user_data = user_data; data->destroy = destroy; device->watches = g_slist_append(device->watches, data); return data->id; } void device_remove_disconnect_watch(struct btd_device *device, guint id) { GSList *l; for (l = device->watches; l; l = l->next) { struct btd_disconnect_data *data = l->data; if (data->id == id) { device->watches = g_slist_remove(device->watches, data); if (data->destroy) data->destroy(data->user_data); g_free(data); return; } } } static char *load_cached_name(struct btd_device *device, const char *local, const char *peer) { char filename[PATH_MAX]; GKeyFile *key_file; char *str = NULL; int len; if (device_address_is_private(device)) return NULL; create_filename(filename, PATH_MAX, "/%s/cache/%s", local, peer); key_file = g_key_file_new(); if (!g_key_file_load_from_file(key_file, filename, 0, NULL)) goto failed; str = g_key_file_get_string(key_file, "General", "Name", NULL); if (str) { len = strlen(str); if (len > HCI_MAX_NAME_LENGTH) str[HCI_MAX_NAME_LENGTH] = '\0'; } failed: g_key_file_free(key_file); return str; } static void load_cached_name_resolve(struct btd_device *device, const char *local, const char *peer) { char filename[PATH_MAX]; GKeyFile *key_file; uint64_t failed_time; if (device_address_is_private(device)) return; create_filename(filename, PATH_MAX, "/%s/cache/%s", local, peer); key_file = g_key_file_new(); if (!g_key_file_load_from_file(key_file, filename, 0, NULL)) goto failed; failed_time = g_key_file_get_uint64(key_file, "NameResolving", "FailedTime", NULL); device->name_resolve_failed_time = failed_time; failed: g_key_file_free(key_file); } static struct csrk_info *load_csrk(GKeyFile *key_file, const char *group) { struct csrk_info *csrk; char *str; int i; str = g_key_file_get_string(key_file, group, "Key", NULL); if (!str) return NULL; csrk = g_new0(struct csrk_info, 1); for (i = 0; i < 16; i++) { if (sscanf(str + (i * 2), "%2hhx", &csrk->key[i]) != 1) goto fail; } /* * In case of older storage this will return 0 which is fine since it * didn't support signing at that point the counter should never have * been used. */ csrk->counter = g_key_file_get_integer(key_file, group, "Counter", NULL); g_free(str); return csrk; fail: g_free(str); g_free(csrk); return NULL; } static struct sirk_info *load_sirk(GKeyFile *key_file, uint8_t index) { char group[28]; struct sirk_info *sirk; char *str; int i; sprintf(group, "SetIdentityResolvingKey#%u", index); str = g_key_file_get_string(key_file, group, "Key", NULL); if (!str) return NULL; sirk = g_new0(struct sirk_info, 1); for (i = 0; i < 16; i++) { if (sscanf(str + (i * 2), "%2hhx", &sirk->key[i]) != 1) goto fail; } sirk->encrypted = g_key_file_get_boolean(key_file, group, "Encrypted", NULL); sirk->size = g_key_file_get_integer(key_file, group, "Size", NULL); sirk->rank = g_key_file_get_integer(key_file, group, "Rank", NULL); g_free(str); return sirk; fail: g_free(str); g_free(sirk); return NULL; } static void load_sirks(struct btd_device *device, GKeyFile *key_file) { struct sirk_info *sirk; uint8_t i; for (i = 0; i < UINT8_MAX; i++) { sirk = load_sirk(key_file, i); if (!sirk) break; queue_push_tail(device->sirks, sirk); /* Only add DeviceSet object if sirk does need * decryption otherwise it has to wait for the LTK in * order to decrypt. */ if (!sirk->encrypted) btd_set_add_device(device, NULL, sirk->key, sirk->size); } } static void load_services(struct btd_device *device, char **uuids) { char **uuid; for (uuid = uuids; *uuid; uuid++) { if (g_slist_find_custom(device->uuids, *uuid, bt_uuid_strcmp)) continue; device->uuids = g_slist_insert_sorted(device->uuids, g_strdup(*uuid), bt_uuid_strcmp); } g_strfreev(uuids); } static void convert_info(struct btd_device *device, GKeyFile *key_file) { char filename[PATH_MAX]; char adapter_addr[18]; char device_addr[18]; char **uuids; char *str; gsize length = 0; GError *gerr = NULL; /* Load device profile list from legacy properties */ uuids = g_key_file_get_string_list(key_file, "General", "SDPServices", NULL, NULL); if (uuids) load_services(device, uuids); uuids = g_key_file_get_string_list(key_file, "General", "GATTServices", NULL, NULL); if (uuids) load_services(device, uuids); if (!device->uuids) return; /* Remove old entries so they are not loaded again */ g_key_file_remove_key(key_file, "General", "SDPServices", NULL); g_key_file_remove_key(key_file, "General", "GATTServices", NULL); ba2str(btd_adapter_get_address(device->adapter), adapter_addr); ba2str(&device->bdaddr, device_addr); create_filename(filename, PATH_MAX, "/%s/%s/info", adapter_addr, device_addr); str = g_key_file_to_data(key_file, &length, NULL); if (!g_file_set_contents(filename, str, length, &gerr)) { error("Unable set contents for %s: (%s)", filename, gerr->message); g_error_free(gerr); } g_free(str); store_device_info(device); } static void load_info(struct btd_device *device, const char *local, const char *peer, GKeyFile *key_file) { GError *gerr = NULL; char *str; gboolean store_needed = FALSE; gboolean blocked; gboolean wake_allowed; char **uuids; int source, vendor, product, version; char **techno, **t; /* Load device name from storage info file, if that fails fall back to * the cache. */ str = g_key_file_get_string(key_file, "General", "Name", NULL); if (str == NULL) { str = load_cached_name(device, local, peer); if (str) store_needed = TRUE; } if (str) { strcpy(device->name, str); g_free(str); } /* Load alias */ device->alias = g_key_file_get_string(key_file, "General", "Alias", NULL); /* Load class */ str = g_key_file_get_string(key_file, "General", "Class", NULL); if (str) { uint32_t class; if (sscanf(str, "%x", &class) == 1) device->class = class; g_free(str); } /* Load appearance */ str = g_key_file_get_string(key_file, "General", "Appearance", NULL); if (str) { device->appearance = strtol(str, NULL, 16); g_free(str); } /* Load device technology */ techno = g_key_file_get_string_list(key_file, "General", "SupportedTechnologies", NULL, NULL); if (!techno) goto next; for (t = techno; *t; t++) { if (g_str_equal(*t, "BR/EDR")) device->bredr = true; else if (g_str_equal(*t, "LE")) device->le = true; else error("Unknown device technology"); } if (!device->le) { device->bdaddr_type = BDADDR_BREDR; } else { str = g_key_file_get_string(key_file, "General", "AddressType", NULL); if (str && g_str_equal(str, "public")) device->bdaddr_type = BDADDR_LE_PUBLIC; else if (str && g_str_equal(str, "static")) device->bdaddr_type = BDADDR_LE_RANDOM; else error("Unknown LE device technology"); g_free(str); device->local_csrk = load_csrk(key_file, "LocalSignatureKey"); device->remote_csrk = load_csrk(key_file, "RemoteSignatureKey"); load_sirks(device, key_file); } g_strfreev(techno); /* Load preferred bearer */ str = g_key_file_get_string(key_file, "General", "PreferredBearer", NULL); if (str) { if (!strcasecmp(str, "last-seen")) { device->bredr_state.prefer = false; device->le_state.prefer = false; } else if (!strcasecmp(str, "bredr")) { device->bredr_state.prefer = true; device->le_state.prefer = false; } else if (!strcasecmp(str, "le")) { device->le_state.prefer = true; device->bredr_state.prefer = false; } g_free(str); } next: /* Load trust */ device->trusted = g_key_file_get_boolean(key_file, "General", "Trusted", NULL); /* Load device blocked */ blocked = g_key_file_get_boolean(key_file, "General", "Blocked", NULL); if (blocked) device_block(device, FALSE); /* Load device profile list */ uuids = g_key_file_get_string_list(key_file, "General", "Services", NULL, NULL); if (uuids) { load_services(device, uuids); /* Discovered services restored from storage */ device->bredr_state.svc_resolved = true; } /* Load device id */ source = g_key_file_get_integer(key_file, "DeviceID", "Source", NULL); if (source) { vendor = g_key_file_get_integer(key_file, "DeviceID", "Vendor", NULL); product = g_key_file_get_integer(key_file, "DeviceID", "Product", NULL); version = g_key_file_get_integer(key_file, "DeviceID", "Version", NULL); btd_device_set_pnpid(device, source, vendor, product, version); } /* Wake allowed is only configured and stored if user changed it. * Otherwise, we enable if profile supports it. */ wake_allowed = g_key_file_get_boolean(key_file, "General", "WakeAllowed", &gerr); if (!gerr) { device_set_wake_override(device, wake_allowed); } else { g_error_free(gerr); gerr = NULL; } if (store_needed) store_device_info(device); } static void load_att_info(struct btd_device *device, const char *local, const char *peer) { char filename[PATH_MAX]; struct stat st; GKeyFile *key_file; GError *gerr = NULL; char *prim_uuid, *str; char **groups, **handle, *service_uuid; struct gatt_primary *prim; uuid_t uuid; char tmp[3]; int i; create_filename(filename, PATH_MAX, "/%s/%s/attributes", local, peer); /* Check if attributes file exists */ if (stat(filename, &st) < 0) return; key_file = g_key_file_new(); if (!g_key_file_load_from_file(key_file, filename, 0, &gerr)) { error("Unable to load key file from %s: (%s)", filename, gerr->message); g_clear_error(&gerr); } groups = g_key_file_get_groups(key_file, NULL); sdp_uuid16_create(&uuid, GATT_PRIM_SVC_UUID); prim_uuid = bt_uuid2string(&uuid); for (handle = groups; *handle; handle++) { gboolean uuid_ok; int end; str = g_key_file_get_string(key_file, *handle, "UUID", NULL); if (!str) continue; uuid_ok = g_str_equal(str, prim_uuid); g_free(str); if (!uuid_ok) continue; str = g_key_file_get_string(key_file, *handle, "Value", NULL); if (!str) continue; end = g_key_file_get_integer(key_file, *handle, "EndGroupHandle", NULL); if (end == 0) { g_free(str); continue; } prim = g_new0(struct gatt_primary, 1); prim->range.start = atoi(*handle); prim->range.end = end; switch (strlen(str)) { case 4: uuid.type = SDP_UUID16; sscanf(str, "%04hx", &uuid.value.uuid16); break; case 8: uuid.type = SDP_UUID32; sscanf(str, "%08x", &uuid.value.uuid32); break; case 32: uuid.type = SDP_UUID128; memset(tmp, 0, sizeof(tmp)); for (i = 0; i < 16; i++) { memcpy(tmp, str + (i * 2), 2); uuid.value.uuid128.data[i] = (uint8_t) strtol(tmp, NULL, 16); } break; default: g_free(str); g_free(prim); continue; } service_uuid = bt_uuid2string(&uuid); memcpy(prim->uuid, service_uuid, MAX_LEN_UUID_STR); free(service_uuid); g_free(str); device->primaries = g_slist_append(device->primaries, prim); } g_strfreev(groups); g_key_file_free(key_file); free(prim_uuid); } static void device_register_primaries(struct btd_device *device, GSList *prim_list, int psm) { device->primaries = g_slist_concat(device->primaries, prim_list); } static void add_primary(struct gatt_db_attribute *attr, void *user_data) { GSList **new_services = user_data; struct gatt_primary *prim; bt_uuid_t uuid; prim = g_new0(struct gatt_primary, 1); if (!prim) { DBG("Failed to allocate gatt_primary structure"); return; } gatt_db_attribute_get_service_handles(attr, &prim->range.start, &prim->range.end); gatt_db_attribute_get_service_uuid(attr, &uuid); bt_uuid_to_string(&uuid, prim->uuid, sizeof(prim->uuid)); *new_services = g_slist_append(*new_services, prim); } static void load_gatt_db(struct btd_device *device, const char *local, const char *peer) { char filename[PATH_MAX]; int err; if (!gatt_cache_is_enabled(device)) return; DBG("Restoring %s gatt database from file", peer); create_filename(filename, PATH_MAX, "/%s/cache/%s", local, peer); err = btd_settings_gatt_db_load(device->db, filename); if (err < 0) { if (err == -ENOENT) return; warn("Error loading db from cache for %s: %s (%d)", peer, strerror(-err), err); } g_slist_free_full(device->primaries, g_free); device->primaries = NULL; gatt_db_foreach_service(device->db, NULL, add_primary, &device->primaries); } static void device_add_uuids(struct btd_device *device, GSList *uuids) { GSList *l; bool changed = false; for (l = uuids; l != NULL; l = g_slist_next(l)) { GSList *match = g_slist_find_custom(device->uuids, l->data, bt_uuid_strcmp); if (match) continue; changed = true; device->uuids = g_slist_insert_sorted(device->uuids, g_strdup(l->data), bt_uuid_strcmp); } if (changed) g_dbus_emit_property_changed(dbus_conn, device->path, DEVICE_INTERFACE, "UUIDs"); } static bool device_match_profile(struct btd_device *device, struct btd_profile *profile, GSList *uuids) { GSList *l; if (profile->remote_uuid == NULL) return false; l = g_slist_find_custom(uuids, profile->remote_uuid, bt_uuid_strcmp); if (!l) return false; return true; } static void add_gatt_service(struct gatt_db_attribute *attr, void *user_data) { struct btd_device *device = user_data; struct btd_service *service; struct btd_profile *profile; bt_uuid_t uuid; char uuid_str[MAX_LEN_UUID_STR]; GSList *l; gatt_db_attribute_get_service_uuid(attr, &uuid); bt_uuid_to_string(&uuid, uuid_str, sizeof(uuid_str)); /* Check if service was already probed */ l = find_service_with_uuid(device->services, uuid_str); if (l) goto done; /* Add UUID and probe service */ btd_device_add_uuid(device, uuid_str); /* Check if service was probed */ l = find_service_with_uuid(device->services, uuid_str); if (!l) return; done: /* Mark service as active to skip discovering it again */ gatt_db_service_set_active(attr, true); service = l->data; profile = btd_service_get_profile(service); /* Claim attributes of internal profiles */ if (!profile->external) { /* Mark the service as claimed by the existing profile. */ gatt_db_service_set_claimed(attr, true); } /* Notify driver about the new connection */ service_accept(service, btd_device_is_initiator(device)); } static void device_add_gatt_services(struct btd_device *device) { char addr[18]; ba2str(&device->bdaddr, addr); if (device->blocked) { DBG("Skipping profiles for blocked device %s", addr); return; } gatt_db_foreach_service(device->db, NULL, add_gatt_service, device); } static void device_accept_gatt_profiles(struct btd_device *device) { GSList *l; bool initiator = btd_device_is_initiator(device); DBG("initiator %s", initiator ? "true" : "false"); for (l = device->services; l != NULL; l = g_slist_next(l)) service_accept(l->data, initiator); } static void device_remove_gatt_service(struct btd_device *device, struct gatt_db_attribute *attr) { struct btd_service *service; bt_uuid_t uuid; char uuid_str[MAX_LEN_UUID_STR]; GSList *l; gatt_db_attribute_get_service_uuid(attr, &uuid); bt_uuid_to_string(&uuid, uuid_str, sizeof(uuid_str)); l = find_service_with_uuid(device->services, uuid_str); if (!l) return; service = l->data; device->services = g_slist_delete_link(device->services, l); device->pending = g_slist_remove(device->pending, service); service_remove(service); } static gboolean gatt_services_changed(gpointer user_data) { struct btd_device *device = user_data; store_gatt_db(device); return FALSE; } static void gatt_service_added(struct gatt_db_attribute *attr, void *user_data) { struct btd_device *device = user_data; GSList *new_service = NULL; uint16_t start, end; if (!bt_gatt_client_is_ready(device->client)) return; gatt_db_attribute_get_service_data(attr, &start, &end, NULL, NULL); DBG("start: 0x%04x, end: 0x%04x", start, end); /* * TODO: Remove the primaries list entirely once all profiles use * shared/gatt. */ add_primary(attr, &new_service); if (!new_service) return; device_register_primaries(device, new_service, -1); add_gatt_service(attr, device); btd_gatt_client_service_added(device->client_dbus, attr); gatt_services_changed(device); } static gint prim_attr_cmp(gconstpointer a, gconstpointer b) { const struct gatt_primary *prim = a; const struct gatt_db_attribute *attr = b; uint16_t start, end; gatt_db_attribute_get_service_handles(attr, &start, &end); return !(prim->range.start == start && prim->range.end == end); } static gint prim_uuid_cmp(gconstpointer a, gconstpointer b) { const struct gatt_primary *prim = a; const char *uuid = b; return bt_uuid_strcmp(prim->uuid, uuid); } static void gatt_service_removed(struct gatt_db_attribute *attr, void *user_data) { struct btd_device *device = user_data; GSList *l; struct gatt_primary *prim; uint16_t start, end; /* * NOTE: shared/gatt-client clears the database in case of failure. This * triggers the service_removed callback for all affected services. * Hence, this function will be called in the following cases: * * 1. When a GATT service gets removed due to "Service Changed". * * 2. When a GATT service gets removed when the database get cleared * upon disconnection with a non-bonded device. * * 3. When a GATT service gets removed when the database get cleared * by shared/gatt-client when its initialization procedure fails, * e.g. due to an ATT protocol error or an unexpected disconnect. * In this case the gatt-client will not be ready. */ gatt_db_attribute_get_service_handles(attr, &start, &end); DBG("start: 0x%04x, end: 0x%04x", start, end); /* Remove the corresponding gatt_primary */ l = g_slist_find_custom(device->primaries, attr, prim_attr_cmp); if (!l) return; prim = l->data; device->primaries = g_slist_delete_link(device->primaries, l); /* * Remove the corresponding UUIDs entry and profile, only if this is * the last service with this UUID. */ l = g_slist_find_custom(device->uuids, prim->uuid, bt_uuid_strcmp); if (l && !g_slist_find_custom(device->primaries, prim->uuid, prim_uuid_cmp)) { /* * If this happend since the db was cleared for a non-bonded * device, then don't remove the btd_service just yet. We do * this so that we can avoid re-probing the profile if the same * GATT service is found on the device on re-connection. * However, if the device is marked as temporary, then we * remove it anyway. */ if (device->client || device->temporary == TRUE) device_remove_gatt_service(device, attr); g_free(l->data); device->uuids = g_slist_delete_link(device->uuids, l); g_dbus_emit_property_changed(dbus_conn, device->path, DEVICE_INTERFACE, "UUIDs"); } g_free(prim); store_device_info(device); btd_gatt_client_service_removed(device->client_dbus, attr); gatt_services_changed(device); } static struct btd_device *device_new(struct btd_adapter *adapter, const char *address) { char *address_up; struct btd_device *device; const char *adapter_path = adapter_get_path(adapter); DBG("address %s", address); device = g_try_malloc0(sizeof(struct btd_device)); if (device == NULL) return NULL; device->tx_power = 127; device->volume = -1; device->wake_id = -1U; device->db = gatt_db_new(); if (!device->db) { g_free(device); return NULL; } memset(device->ad_flags, INVALID_FLAGS, sizeof(device->ad_flags)); device->ad = bt_ad_new(); if (!device->ad) { device_free(device); return NULL; } address_up = g_ascii_strup(address, -1); device->path = g_strdup_printf("%s/dev_%s", adapter_path, address_up); g_strdelimit(device->path, ":", '_'); g_free(address_up); str2ba(address, &device->bdaddr); device->client_dbus = btd_gatt_client_new(device); if (!device->client_dbus) { error("Failed to create btd_gatt_client"); device_free(device); return NULL; } DBG("Creating device %s", device->path); if (g_dbus_register_interface(dbus_conn, device->path, DEVICE_INTERFACE, device_methods, NULL, device_properties, device, device_free) == FALSE) { error("Unable to register device interface for %s", address); device_free(device); return NULL; } device->adapter = adapter; device->sirks = queue_new(); device->temporary = true; device->db_id = gatt_db_register(device->db, gatt_service_added, gatt_service_removed, device, NULL); device->refresh_discovery = btd_opts.refresh_discovery; return btd_device_ref(device); } struct btd_device *device_create_from_storage(struct btd_adapter *adapter, const char *address, GKeyFile *key_file) { struct btd_device *device; const char *src_dir; DBG("address %s", address); device = device_new(adapter, address); if (device == NULL) return NULL; convert_info(device, key_file); src_dir = btd_adapter_get_storage_dir(adapter); load_info(device, src_dir, address, key_file); load_att_info(device, src_dir, address); return device; } struct btd_device *device_create(struct btd_adapter *adapter, const bdaddr_t *bdaddr, uint8_t bdaddr_type) { struct btd_device *device; char dst[18]; char *str; const char *storage_dir; ba2str(bdaddr, dst); DBG("dst %s", dst); device = device_new(adapter, dst); if (device == NULL) return NULL; device->bdaddr_type = bdaddr_type; if (bdaddr_type == BDADDR_BREDR) device->bredr = true; else device->le = true; storage_dir = btd_adapter_get_storage_dir(adapter); str = load_cached_name(device, storage_dir, dst); if (str) { strcpy(device->name, str); g_free(str); } load_cached_name_resolve(device, storage_dir, dst); return device; } char *btd_device_get_storage_path(struct btd_device *device, const char *name) { char filename[PATH_MAX]; char dstaddr[18]; if (device_address_is_private(device)) { warn("Refusing storage path for private addressed device %s", device->path); return NULL; } ba2str(&device->bdaddr, dstaddr); if (!name) create_filename(filename, PATH_MAX, "/%s/%s", btd_adapter_get_storage_dir(device->adapter), dstaddr); else create_filename(filename, PATH_MAX, "/%s/%s/%s", btd_adapter_get_storage_dir(device->adapter), dstaddr, name); return strdup(filename); } void btd_device_device_set_name(struct btd_device *device, const char *name) { if (strncmp(name, device->name, MAX_NAME_LENGTH) == 0) return; DBG("%s %s", device->path, name); strncpy(device->name, name, MAX_NAME_LENGTH); store_device_info(device); g_dbus_emit_property_changed(dbus_conn, device->path, DEVICE_INTERFACE, "Name"); if (device->alias != NULL) return; g_dbus_emit_property_changed(dbus_conn, device->path, DEVICE_INTERFACE, "Alias"); } void device_get_name(struct btd_device *device, char *name, size_t len) { if (name != NULL && len > 0) { strncpy(name, device->name, len - 1); name[len - 1] = '\0'; } } bool device_name_known(struct btd_device *device) { return device->name[0] != '\0'; } bool device_is_name_resolve_allowed(struct btd_device *device) { struct timespec now; if (!device) return false; clock_gettime(CLOCK_MONOTONIC, &now); /* failed_time is empty (0), meaning no prior failure. */ if (device->name_resolve_failed_time == 0) return true; /* now < failed_time, meaning the clock has somehow turned back, * possibly because of system restart. Allow just to be safe. */ if (now.tv_sec < device->name_resolve_failed_time) return true; /* now >= failed_time + name_request_retry_delay, meaning the * period of not sending name request is over. */ if (now.tv_sec >= (time_t)(device->name_resolve_failed_time + btd_opts.name_request_retry_delay)) return true; return false; } void device_name_resolve_fail(struct btd_device *device) { struct timespec now; if (!device) return; clock_gettime(CLOCK_MONOTONIC, &now); device->name_resolve_failed_time = now.tv_sec; device_store_cached_name_resolve(device); } void device_set_class(struct btd_device *device, uint32_t class) { if (device->class == class) return; DBG("%s 0x%06X", device->path, class); device->class = class; store_device_info(device); g_dbus_emit_property_changed(dbus_conn, device->path, DEVICE_INTERFACE, "Class"); g_dbus_emit_property_changed(dbus_conn, device->path, DEVICE_INTERFACE, "Icon"); } void device_set_rpa(struct btd_device *device, bool value) { device->rpa = value; } void device_update_addr(struct btd_device *device, const bdaddr_t *bdaddr, uint8_t bdaddr_type) { bool auto_connect = device->auto_connect; device_set_rpa(device, true); if (!bacmp(bdaddr, &device->bdaddr) && bdaddr_type == device->bdaddr_type) return; /* Since this function is only used for LE SMP Identity * Resolving purposes we can now assume LE is supported. */ device->le = true; /* Remove old address from accept/auto-connect list since its address * will be changed. */ if (auto_connect) device_set_auto_connect(device, FALSE); bacpy(&device->bdaddr, bdaddr); device->bdaddr_type = bdaddr_type; if (device->temporary) btd_device_set_temporary(device, false); else store_device_info(device); g_dbus_emit_property_changed(dbus_conn, device->path, DEVICE_INTERFACE, "Address"); g_dbus_emit_property_changed(dbus_conn, device->path, DEVICE_INTERFACE, "AddressType"); if (auto_connect) device_set_auto_connect(device, TRUE); } void device_set_bredr_support(struct btd_device *device) { if (btd_opts.mode == BT_MODE_LE || device->bredr) return; device->bredr = true; store_device_info(device); } void device_set_le_support(struct btd_device *device, uint8_t bdaddr_type) { if (btd_opts.mode == BT_MODE_BREDR || device->le) return; device->le = true; device->bdaddr_type = bdaddr_type; g_dbus_emit_property_changed(dbus_conn, device->path, DEVICE_INTERFACE, "AddressType"); store_device_info(device); } void device_update_last_seen(struct btd_device *device, uint8_t bdaddr_type, bool connectable) { struct bearer_state *state; state = get_state(device, bdaddr_type); state->last_seen = time(NULL); state->connectable = connectable; if (!device_is_temporary(device)) return; /* Restart temporary timer */ set_temporary_timer(device, btd_opts.tmpto); } void btd_device_set_connectable(struct btd_device *device, bool connectable) { device_update_last_seen(device, device->bdaddr_type, connectable); } /* It is possible that we have two device objects for the same device in * case it has first been discovered over BR/EDR and has a private * address when discovered over LE for the first time. In such a case we * need to inherit critical values from the duplicate so that we don't * ovewrite them when writing to storage. The next time bluetoothd * starts the device will show up as a single instance. */ void device_merge_duplicate(struct btd_device *dev, struct btd_device *dup) { GSList *l; DBG(""); dev->bredr = dup->bredr; dev->trusted = dup->trusted; dev->blocked = dup->blocked; for (l = dup->uuids; l; l = g_slist_next(l)) dev->uuids = g_slist_append(dev->uuids, g_strdup(l->data)); if (dev->name[0] == '\0') strcpy(dev->name, dup->name); if (!dev->alias) dev->alias = g_strdup(dup->alias); dev->class = dup->class; dev->vendor_src = dup->vendor_src; dev->vendor = dup->vendor; dev->product = dup->product; dev->version = dup->version; } uint32_t btd_device_get_class(struct btd_device *device) { return device->class; } uint16_t btd_device_get_vendor(struct btd_device *device) { return device->vendor; } uint16_t btd_device_get_vendor_src(struct btd_device *device) { return device->vendor_src; } uint16_t btd_device_get_product(struct btd_device *device) { return device->product; } uint16_t btd_device_get_version(struct btd_device *device) { return device->version; } static void delete_folder_tree(const char *dirname) { DIR *dir; struct dirent *entry; char filename[PATH_MAX]; dir = opendir(dirname); if (dir == NULL) return; while ((entry = readdir(dir)) != NULL) { if (g_str_equal(entry->d_name, ".") || g_str_equal(entry->d_name, "..")) continue; if (entry->d_type == DT_UNKNOWN) entry->d_type = util_get_dt(dirname, entry->d_name); snprintf(filename, PATH_MAX, "%s/%s", dirname, entry->d_name); if (entry->d_type == DT_DIR) delete_folder_tree(filename); else unlink(filename); } closedir(dir); rmdir(dirname); } void device_remove_bonding(struct btd_device *device, uint8_t bdaddr_type) { if (bdaddr_type == BDADDR_BREDR) device->bredr_state.bonded = false; else device->le_state.bonded = false; if (!device->bredr_state.bonded && !device->le_state.bonded) btd_device_set_temporary(device, true); btd_adapter_remove_bonding(device->adapter, &device->bdaddr, bdaddr_type); } static void device_remove_stored(struct btd_device *device) { char device_addr[18]; char filename[PATH_MAX]; GKeyFile *key_file; GError *gerr = NULL; char *data; gsize length = 0; if (device->bredr_state.bonded) device_remove_bonding(device, BDADDR_BREDR); if (device->le_state.bonded) device_remove_bonding(device, device->bdaddr_type); device->bredr_state.paired = false; device->le_state.paired = false; if (device->blocked) device_unblock(device, TRUE, FALSE); ba2str(&device->bdaddr, device_addr); create_filename(filename, PATH_MAX, "/%s/%s", btd_adapter_get_storage_dir(device->adapter), device_addr); delete_folder_tree(filename); create_filename(filename, PATH_MAX, "/%s/cache/%s", btd_adapter_get_storage_dir(device->adapter), device_addr); key_file = g_key_file_new(); if (!g_key_file_load_from_file(key_file, filename, 0, &gerr)) { g_error_free(gerr); g_key_file_free(key_file); return; } g_key_file_remove_group(key_file, "ServiceRecords", NULL); g_key_file_remove_group(key_file, "Attributes", NULL); g_key_file_remove_group(key_file, "Endpoints", NULL); data = g_key_file_to_data(key_file, &length, NULL); if (length > 0) { create_file(filename, 0600); if (!g_file_set_contents(filename, data, length, &gerr)) { error("Unable set contents for %s: (%s)", filename, gerr->message); g_error_free(gerr); } } g_free(data); g_key_file_free(key_file); } void device_remove(struct btd_device *device, gboolean remove_stored) { DBG("Removing device %s", device->path); if (device->auto_connect) { device->disable_auto_connect = TRUE; device_set_auto_connect(device, FALSE); } if (device->bonding) { uint8_t status; if (device->bredr_state.connected) status = MGMT_STATUS_DISCONNECTED; else status = MGMT_STATUS_CONNECT_FAILED; device_cancel_bonding(device, status); } if (device->browse) browse_request_cancel(device->browse); while (device->services != NULL) { struct btd_service *service = device->services->data; device->services = g_slist_remove(device->services, service); service_remove(service); } g_slist_free(device->pending); device->pending = NULL; if (btd_device_is_connected(device)) { if (device->disconn_timer > 0) timeout_remove(device->disconn_timer); disconnect_all(device); } clear_temporary_timer(device); if (device->store_id > 0) { g_source_remove(device->store_id); device->store_id = 0; if (!remove_stored) store_device_info_cb(device); } if (remove_stored) device_remove_stored(device); btd_device_unref(device); } int device_address_cmp(gconstpointer a, gconstpointer b) { const struct btd_device *device = a; const char *address = b; char addr[18]; ba2str(&device->bdaddr, addr); return strcasecmp(addr, address); } int device_bdaddr_cmp(gconstpointer a, gconstpointer b) { const struct btd_device *device = a; const bdaddr_t *bdaddr = b; return bacmp(&device->bdaddr, bdaddr); } static bool addr_is_public(uint8_t addr_type) { if (addr_type == BDADDR_BREDR || addr_type == BDADDR_LE_PUBLIC) return true; return false; } int device_addr_type_cmp(gconstpointer a, gconstpointer b) { const struct btd_device *dev = a; const struct device_addr_type *addr = b; int cmp; cmp = bacmp(&dev->bdaddr, &addr->bdaddr); /* * Address matches and both old and new are public addresses * (doesn't matter whether LE or BR/EDR, then consider this a * match. */ if (!cmp && addr_is_public(addr->bdaddr_type) && addr_is_public(dev->bdaddr_type)) return 0; if (addr->bdaddr_type == BDADDR_BREDR) { if (!dev->bredr) return -1; return cmp; } if (!dev->le) return -1; if (addr->bdaddr_type != dev->bdaddr_type) { if (addr->bdaddr_type == dev->conn_bdaddr_type) return bacmp(&dev->conn_bdaddr, &addr->bdaddr); return -1; } return cmp; } static gboolean record_has_uuid(const sdp_record_t *rec, const char *profile_uuid) { sdp_list_t *pat; for (pat = rec->pattern; pat != NULL; pat = pat->next) { char *uuid; int ret; uuid = bt_uuid2string(pat->data); if (!uuid) continue; ret = strcasecmp(uuid, profile_uuid); free(uuid); if (ret == 0) return TRUE; } return FALSE; } GSList *btd_device_get_uuids(struct btd_device *device) { return device->uuids; } bool btd_device_has_uuid(struct btd_device *device, const char *uuid) { return g_slist_find_custom(device->uuids, uuid, (GCompareFunc)strcasecmp); } struct probe_data { struct btd_device *dev; GSList *uuids; }; static struct btd_service *probe_service(struct btd_device *device, struct btd_profile *profile, GSList *uuids) { GSList *l; struct btd_service *service; if (profile->device_probe == NULL) return NULL; if (!device_match_profile(device, profile, uuids)) return NULL; l = find_service_with_profile(device->services, profile); /* If the service already exists, return NULL so that it won't be added * to the device->services. */ if (l) return NULL; service = service_create(device, profile); if (service_probe(service)) { btd_service_unref(service); return NULL; } /* Only set auto connect if profile has set the flag and can really * accept connections. */ if (profile->auto_connect && profile->accept) { /* If temporary mark auto_connect as disabled so when the * device is connected it attempts to enable it. */ if (device->temporary) device->disable_auto_connect = TRUE; else device_set_auto_connect(device, TRUE); } return service; } static void dev_probe(struct btd_profile *p, void *user_data) { struct probe_data *d = user_data; struct btd_service *service; service = probe_service(d->dev, p, d->uuids); if (!service) return; d->dev->services = g_slist_append(d->dev->services, service); } void device_probe_profile(gpointer a, gpointer b) { struct btd_device *device = a; struct btd_profile *profile = b; struct btd_service *service; service = probe_service(device, profile, device->uuids); if (!service) return; device->services = g_slist_append(device->services, service); if (!profile->auto_connect || (!btd_device_is_connected(device) && !device->general_connect)) return; device->pending = g_slist_append(device->pending, service); if (g_slist_length(device->pending) == 1) connect_next(device); } void device_remove_profile(gpointer a, gpointer b) { struct btd_device *device = a; struct btd_profile *profile = b; struct btd_service *service; GSList *l; l = find_service_with_profile(device->services, profile); if (l == NULL) return; service = l->data; device->services = g_slist_delete_link(device->services, l); device->pending = g_slist_remove(device->pending, service); service_remove(service); } void device_probe_profiles(struct btd_device *device, GSList *uuids) { struct probe_data d = { device, uuids }; char addr[18]; if (!uuids) return; ba2str(&device->bdaddr, addr); if (device->blocked) { DBG("Skipping profiles for blocked device %s", addr); goto add_uuids; } btd_profile_foreach(dev_probe, &d); add_uuids: device_add_uuids(device, uuids); } static void store_sdp_record(GKeyFile *key_file, sdp_record_t *rec) { char handle_str[11]; sdp_buf_t buf; int size, i; char *str; sprintf(handle_str, "0x%8.8X", rec->handle); if (sdp_gen_record_pdu(rec, &buf) < 0) return; size = buf.data_size; str = g_malloc0(size*2+1); for (i = 0; i < size; i++) sprintf(str + (i * 2), "%02X", buf.data[i]); g_key_file_set_string(key_file, "ServiceRecords", handle_str, str); free(buf.data); g_free(str); } static void store_primaries_from_sdp_record(GKeyFile *key_file, sdp_record_t *rec) { uuid_t uuid; char *att_uuid, *prim_uuid; uint16_t start = 0, end = 0, psm = 0; char handle[6], uuid_str[33]; int i; sdp_uuid16_create(&uuid, ATT_UUID); att_uuid = bt_uuid2string(&uuid); sdp_uuid16_create(&uuid, GATT_PRIM_SVC_UUID); prim_uuid = bt_uuid2string(&uuid); if (!record_has_uuid(rec, att_uuid)) goto done; if (!gatt_parse_record(rec, &uuid, &psm, &start, &end)) goto done; sprintf(handle, "%hu", start); switch (uuid.type) { case SDP_UUID16: sprintf(uuid_str, "%4.4X", uuid.value.uuid16); break; case SDP_UUID32: sprintf(uuid_str, "%8.8X", uuid.value.uuid32); break; case SDP_UUID128: for (i = 0; i < 16; i++) sprintf(uuid_str + (i * 2), "%2.2X", uuid.value.uuid128.data[i]); break; default: uuid_str[0] = '\0'; } g_key_file_set_string(key_file, handle, "UUID", prim_uuid); g_key_file_set_string(key_file, handle, "Value", uuid_str); g_key_file_set_integer(key_file, handle, "EndGroupHandle", end); done: free(prim_uuid); free(att_uuid); } static int rec_cmp(const void *a, const void *b) { const sdp_record_t *r1 = a; const sdp_record_t *r2 = b; return r1->handle - r2->handle; } static int update_record(struct browse_req *req, const char *uuid, sdp_record_t *rec) { GSList *l; /* Check for duplicates */ if (sdp_list_find(req->records, rec, rec_cmp)) return -EALREADY; /* Copy record */ req->records = sdp_list_append(req->records, sdp_copy_record(rec)); /* Check if UUID is duplicated */ l = g_slist_find_custom(req->device->uuids, uuid, bt_uuid_strcmp); if (l == NULL) { l = g_slist_find_custom(req->profiles_added, uuid, bt_uuid_strcmp); if (l != NULL) return 0; req->profiles_added = g_slist_append(req->profiles_added, g_strdup(uuid)); } return 0; } static void update_bredr_services(struct browse_req *req, sdp_list_t *recs) { struct btd_device *device = req->device; sdp_list_t *seq; char srcaddr[18], dstaddr[18]; char sdp_file[PATH_MAX]; char att_file[PATH_MAX]; GKeyFile *sdp_key_file; GKeyFile *att_key_file; GError *gerr = NULL; char *data; gsize length = 0; ba2str(btd_adapter_get_address(device->adapter), srcaddr); ba2str(&device->bdaddr, dstaddr); create_filename(sdp_file, PATH_MAX, "/%s/cache/%s", srcaddr, dstaddr); create_file(sdp_file, 0600); sdp_key_file = g_key_file_new(); if (!g_key_file_load_from_file(sdp_key_file, sdp_file, 0, &gerr)) { error("Unable to load key file from %s: (%s)", sdp_file, gerr->message); g_clear_error(&gerr); g_key_file_free(sdp_key_file); sdp_key_file = NULL; } create_filename(att_file, PATH_MAX, "/%s/%s/attributes", srcaddr, dstaddr); create_file(att_file, 0600); att_key_file = g_key_file_new(); if (!g_key_file_load_from_file(att_key_file, att_file, 0, &gerr)) { error("Unable to load key file from %s: (%s)", att_file, gerr->message); g_clear_error(&gerr); g_key_file_free(att_key_file); att_key_file = NULL; } for (seq = recs; seq; seq = seq->next) { sdp_record_t *rec = (sdp_record_t *) seq->data; char *profile_uuid; if (!rec) break; /* If service class attribute is missing, svclass will be all * zero and the resulting uuid string will be NULL. */ profile_uuid = bt_uuid2string(&rec->svclass); if (!profile_uuid) continue; if (bt_uuid_strcmp(profile_uuid, PNP_UUID) == 0) { uint16_t source, vendor, product, version; sdp_data_t *pdlist; pdlist = sdp_data_get(rec, SDP_ATTR_VENDOR_ID_SOURCE); source = pdlist ? pdlist->val.uint16 : 0x0000; pdlist = sdp_data_get(rec, SDP_ATTR_VENDOR_ID); vendor = pdlist ? pdlist->val.uint16 : 0x0000; pdlist = sdp_data_get(rec, SDP_ATTR_PRODUCT_ID); product = pdlist ? pdlist->val.uint16 : 0x0000; pdlist = sdp_data_get(rec, SDP_ATTR_VERSION); version = pdlist ? pdlist->val.uint16 : 0x0000; if (source || vendor || product || version) btd_device_set_pnpid(device, source, vendor, product, version); } if (update_record(req, profile_uuid, rec) < 0) goto next; if (sdp_key_file) store_sdp_record(sdp_key_file, rec); if (att_key_file) store_primaries_from_sdp_record(att_key_file, rec); next: free(profile_uuid); } if (sdp_key_file) { data = g_key_file_to_data(sdp_key_file, &length, NULL); if (length > 0) { if (!g_file_set_contents(sdp_file, data, length, &gerr)) { error("Unable set contents for %s: (%s)", sdp_file, gerr->message); g_clear_error(&gerr); } } g_free(data); g_key_file_free(sdp_key_file); } if (att_key_file) { data = g_key_file_to_data(att_key_file, &length, NULL); if (length > 0) { if (!g_file_set_contents(att_file, data, length, &gerr)) { error("Unable set contents for %s: (%s)", att_file, gerr->message); g_clear_error(&gerr); } } g_free(data); g_key_file_free(att_key_file); } } static int primary_cmp(gconstpointer a, gconstpointer b) { return memcmp(a, b, sizeof(struct gatt_primary)); } static void update_gatt_uuids(struct browse_req *req, GSList *current, GSList *found) { GSList *l, *lmatch; /* Added Profiles */ for (l = found; l; l = g_slist_next(l)) { struct gatt_primary *prim = l->data; /* Entry found ? */ lmatch = g_slist_find_custom(current, prim, primary_cmp); if (lmatch) continue; /* New entry */ req->profiles_added = g_slist_append(req->profiles_added, g_strdup(prim->uuid)); DBG("UUID Added: %s", prim->uuid); } } static GSList *device_services_from_record(struct btd_device *device, GSList *profiles) { GSList *l, *prim_list = NULL; char *att_uuid; uuid_t proto_uuid; sdp_uuid16_create(&proto_uuid, ATT_UUID); att_uuid = bt_uuid2string(&proto_uuid); for (l = profiles; l; l = l->next) { const char *profile_uuid = l->data; const sdp_record_t *rec; struct gatt_primary *prim; uint16_t start = 0, end = 0, psm = 0; uuid_t prim_uuid; rec = btd_device_get_record(device, profile_uuid); if (!rec) continue; if (!record_has_uuid(rec, att_uuid)) continue; if (!gatt_parse_record(rec, &prim_uuid, &psm, &start, &end)) continue; prim = g_new0(struct gatt_primary, 1); prim->range.start = start; prim->range.end = end; sdp_uuid2strn(&prim_uuid, prim->uuid, sizeof(prim->uuid)); prim_list = g_slist_append(prim_list, prim); } free(att_uuid); return prim_list; } static void search_cb(sdp_list_t *recs, int err, gpointer user_data) { struct browse_req *req = user_data; struct btd_device *device = req->device; DBusMessage *reply; GSList *primaries; char addr[18]; ba2str(&device->bdaddr, addr); if (err < 0) { error("%s: error updating services: %s (%d)", addr, strerror(-err), -err); goto send_reply; } update_bredr_services(req, recs); if (device->tmp_records) sdp_list_free(device->tmp_records, (sdp_free_func_t) sdp_record_free); device->tmp_records = req->records; req->records = NULL; if (!req->profiles_added) { DBG("%s: No service update", addr); goto send_reply; } primaries = device_services_from_record(device, req->profiles_added); if (primaries) device_register_primaries(device, primaries, ATT_PSM); /* * TODO: The btd_service instances for GATT services need to be * initialized with the service handles. Eventually this code should * perform ATT protocol service discovery over the ATT PSM to obtain * the full list of services and populate a client-role gatt_db over * BR/EDR. */ device_probe_profiles(device, req->profiles_added); /* Propagate services changes */ g_dbus_emit_property_changed(dbus_conn, req->device->path, DEVICE_INTERFACE, "UUIDs"); send_reply: /* If SDP search failed during an ongoing connection request, we should * reply to D-Bus method call. */ if (err < 0 && device->connect) { DBG("SDP failed during connection"); reply = btd_error_failed(device->connect, strerror(-err)); g_dbus_send_message(dbus_conn, reply); dbus_message_unref(device->connect); device->connect = NULL; } device_svc_resolved(device, BROWSE_SDP, BDADDR_BREDR, err); } static void browse_cb(sdp_list_t *recs, int err, gpointer user_data) { struct browse_req *req = user_data; struct btd_device *device = req->device; struct btd_adapter *adapter = device->adapter; uuid_t uuid; /* If we have a valid response and req->search_uuid == 2, then L2CAP * UUID & PNP searching was successful -- we are done */ if (err < 0 || (req->search_uuid == 2 && req->records)) { if (err == -ECONNRESET && req->reconnect_attempt < 1) { req->search_uuid--; req->reconnect_attempt++; } else goto done; } update_bredr_services(req, recs); /* Search for mandatory uuids */ if (uuid_list[req->search_uuid]) { sdp_uuid16_create(&uuid, uuid_list[req->search_uuid++]); bt_search_service(btd_adapter_get_address(adapter), &device->bdaddr, &uuid, browse_cb, user_data, NULL, req->sdp_flags); return; } done: search_cb(recs, err, user_data); } static bool device_get_auto_connect(struct btd_device *device) { if (device->disable_auto_connect) return false; return device->auto_connect; } static void disconnect_gatt_service(gpointer data, gpointer user_data) { struct btd_service *service = data; struct btd_profile *profile = btd_service_get_profile(service); /* Ignore if profile cannot accept connections */ if (!profile->accept) return; btd_service_disconnect(service); } static void att_disconnected_cb(int err, void *user_data) { struct btd_device *device = user_data; DBG(""); if (device->browse) goto done; DBG("%s (%d)", strerror(err), err); g_slist_foreach(device->services, disconnect_gatt_service, NULL); btd_gatt_client_disconnected(device->client_dbus); if (!device_get_auto_connect(device)) { DBG("Automatic connection disabled"); goto done; } /* * Keep scanning/re-connection active if disconnection reason * is connection timeout, remote user terminated connection or local * initiated disconnection. */ if (err == ETIMEDOUT || err == ECONNRESET || err == ECONNABORTED) adapter_connect_list_add(device->adapter, device); done: attio_cleanup(device); } static void register_gatt_services(struct btd_device *device) { struct browse_req *req = device->browse; GSList *services = NULL; if (!bt_gatt_client_is_ready(device->client)) return; /* * TODO: Remove the primaries list entirely once all profiles use * shared/gatt. */ gatt_db_foreach_service(device->db, NULL, add_primary, &services); btd_device_set_temporary(device, false); if (req) update_gatt_uuids(req, device->primaries, services); g_slist_free_full(device->primaries, g_free); device->primaries = NULL; device_register_primaries(device, services, -1); device_add_gatt_services(device); } static void gatt_client_init(struct btd_device *device); static void gatt_client_ready_cb(bool success, uint8_t att_ecode, void *user_data) { struct btd_device *device = user_data; DBG("status: %s, error: %u", success ? "success" : "failed", att_ecode); if (!success) { device_svc_resolved(device, BROWSE_GATT, device->bdaddr_type, -EIO); return; } register_gatt_services(device); btd_gatt_client_ready(device->client_dbus); device_svc_resolved(device, BROWSE_GATT, device->bdaddr_type, 0); store_gatt_db(device); } static void gatt_client_service_changed(uint16_t start_handle, uint16_t end_handle, void *user_data) { DBG("start 0x%04x, end: 0x%04x", start_handle, end_handle); } static void gatt_debug(const char *str, void *user_data) { DBG_IDX(0xffff, "%s", str); } static void gatt_client_init(struct btd_device *device) { uint8_t features; gatt_client_cleanup(device); if (!btd_device_is_initiator(device) && !btd_opts.reverse_discovery) { DBG("Reverse service discovery disabled: skipping GATT client"); return; } if (!btd_device_is_initiator(device) && !btd_opts.gatt_client) { DBG("GATT client disabled: skipping GATT client"); return; } features = BT_GATT_CHRC_CLI_FEAT_ROBUST_CACHING | BT_GATT_CHRC_CLI_FEAT_NFY_MULTI; if (btd_opts.gatt_channels > 1) features |= BT_GATT_CHRC_CLI_FEAT_EATT; if (device->bonding) { DBG("Elevating security level since bonding is in progress"); bt_att_set_security(device->att, BT_ATT_SECURITY_MEDIUM); } device->client = bt_gatt_client_new(device->db, device->att, device->att_mtu, features); if (!device->client) { DBG("Failed to initialize"); return; } bt_gatt_client_set_debug(device->client, gatt_debug, NULL, NULL); g_attrib_attach_client(device->attrib, device->client); /* * If we have cache, notify existing service about the new connection * so they can react to notifications while discovering services */ if (!gatt_db_isempty(device->db)) device_accept_gatt_profiles(device); device->gatt_ready_id = bt_gatt_client_ready_register(device->client, gatt_client_ready_cb, device, NULL); if (!device->gatt_ready_id) { DBG("Failed to register GATT ready callback"); gatt_client_cleanup(device); return; } if (!bt_gatt_client_set_service_changed(device->client, gatt_client_service_changed, device, NULL)) { DBG("Failed to set service changed handler"); gatt_client_cleanup(device); return; } btd_gatt_client_connected(device->client_dbus); /* Only initiate EATT connection when acting as initiator, as acceptor * it shall be triggered only when ready to avoid possible clashes where * both sides attempt to connection at same time. */ if (btd_device_is_initiator(device)) btd_gatt_client_eatt_connect(device->client_dbus); } static void gatt_server_init(struct btd_device *device, struct btd_gatt_database *database) { struct gatt_db *db = btd_gatt_database_get_db(database); if (!db) { error("No local GATT database exists for this adapter"); return; } gatt_server_cleanup(device); device->server = bt_gatt_server_new(db, device->att, device->att_mtu, btd_opts.key_size); if (!device->server) { error("Failed to initialize bt_gatt_server"); return; } if (device->ltk) bt_att_set_enc_key_size(device->att, device->ltk->enc_size); bt_gatt_server_set_debug(device->server, gatt_debug, NULL, NULL); btd_gatt_database_server_connected(database, device->server); } static bool local_counter(uint32_t *sign_cnt, void *user_data) { struct btd_device *dev = user_data; if (!dev->local_csrk) return false; *sign_cnt = dev->local_csrk->counter++; store_device_info(dev); return true; } static bool remote_counter(uint32_t *sign_cnt, void *user_data) { struct btd_device *dev = user_data; if (!dev->remote_csrk || *sign_cnt < dev->remote_csrk->counter) return false; dev->remote_csrk->counter = *sign_cnt; store_device_info(dev); return true; } bool device_attach_att(struct btd_device *dev, GIOChannel *io) { GError *gerr = NULL; GAttrib *attrib; BtIOSecLevel sec_level; uint16_t mtu; uint16_t cid; struct btd_gatt_database *database; const bdaddr_t *dst; char dstaddr[18]; bt_io_get(io, &gerr, BT_IO_OPT_SEC_LEVEL, &sec_level, BT_IO_OPT_IMTU, &mtu, BT_IO_OPT_CID, &cid, BT_IO_OPT_INVALID); if (gerr) { error("bt_io_get: %s", gerr->message); g_error_free(gerr); return false; } if (dev->att) { if (btd_opts.gatt_channels == bt_att_get_channels(dev->att)) { DBG("EATT channel limit reached"); return false; } if (!bt_att_attach_fd(dev->att, g_io_channel_unix_get_fd(io))) { DBG("EATT channel connected"); g_io_channel_set_close_on_unref(io, FALSE); return true; } error("Failed to attach EATT channel"); return false; } if (sec_level == BT_IO_SEC_LOW && dev->le_state.paired) { DBG("Elevating security level since LTK is available"); sec_level = BT_IO_SEC_MEDIUM; bt_io_set(io, &gerr, BT_IO_OPT_SEC_LEVEL, sec_level, BT_IO_OPT_INVALID); if (gerr) { error("bt_io_set: %s", gerr->message); g_error_free(gerr); return false; } } dev->att_mtu = MIN(mtu, btd_opts.gatt_mtu); attrib = g_attrib_new(io, cid == ATT_CID ? BT_ATT_DEFAULT_LE_MTU : dev->att_mtu, false); if (!attrib) { error("Unable to create new GAttrib instance"); return false; } dev->attrib = attrib; dev->att = g_attrib_get_att(attrib); bt_att_ref(dev->att); bt_att_set_debug(dev->att, BT_ATT_DEBUG, gatt_debug, NULL, NULL); dev->att_disconn_id = bt_att_register_disconnect(dev->att, att_disconnected_cb, dev, NULL); bt_att_set_close_on_unref(dev->att, true); if (dev->local_csrk) bt_att_set_local_key(dev->att, dev->local_csrk->key, local_counter, dev); if (dev->remote_csrk) bt_att_set_remote_key(dev->att, dev->remote_csrk->key, remote_counter, dev); database = btd_adapter_get_database(dev->adapter); dst = device_get_address(dev); ba2str(dst, dstaddr); if (gatt_db_isempty(dev->db)) load_gatt_db(dev, btd_adapter_get_storage_dir(dev->adapter), dstaddr); gatt_client_init(dev); gatt_server_init(dev, database); /* * Remove the device from the connect_list and give the passive * scanning another chance to be restarted in case there are * other devices in the connect_list. */ adapter_connect_list_remove(dev->adapter, dev); return true; } static void att_connect_cb(GIOChannel *io, GError *gerr, gpointer user_data) { struct btd_device *device = user_data; DBusMessage *reply; uint8_t io_cap; int err = 0; g_io_channel_unref(device->att_io); device->att_io = NULL; if (gerr) { DBG("%s", gerr->message); if (g_error_matches(gerr, BT_IO_ERROR, ECONNABORTED)) goto done; if (device_get_auto_connect(device)) { DBG("Enabling automatic connections"); adapter_connect_list_add(device->adapter, device); } if (device->browse) browse_request_complete(device->browse, BROWSE_GATT, device->bdaddr_type, -ECONNABORTED); err = -ECONNABORTED; goto done; } /* Update connected state */ device->le_state.connected = true; if (!device_attach_att(device, io)) goto done; if (!device->bonding) goto done; if (device->bonding->agent) io_cap = agent_get_io_capability(device->bonding->agent); else io_cap = IO_CAPABILITY_NOINPUTNOOUTPUT; err = adapter_create_bonding(device->adapter, &device->bdaddr, device->bdaddr_type, io_cap); done: if (device->bonding && err < 0) { reply = btd_error_failed(device->bonding->msg, strerror(-err)); g_dbus_send_message(dbus_conn, reply); bonding_request_cancel(device->bonding); bonding_request_free(device->bonding); } if (!err) device_browse_gatt(device, NULL); if (device->connect) { if (err < 0) reply = btd_error_failed(device->connect, btd_error_le_conn_from_errno(err)); else reply = dbus_message_new_method_return(device->connect); g_dbus_send_message(dbus_conn, reply); dbus_message_unref(device->connect); device->connect = NULL; } } int device_connect_le(struct btd_device *dev) { struct btd_adapter *adapter = dev->adapter; BtIOSecLevel sec_level; GIOChannel *io; GError *gerr = NULL; char addr[18]; /* There is one connection attempt going on */ if (dev->att_io || dev->att) return -EALREADY; ba2str(&dev->bdaddr, addr); DBG("Connection attempt to: %s", addr); /* Set as initiator */ dev->le_state.initiator = true; if (dev->le_state.paired) sec_level = BT_IO_SEC_MEDIUM; else sec_level = BT_IO_SEC_LOW; /* * This connection will help us catch any PDUs that comes before * pairing finishes */ io = bt_io_connect(att_connect_cb, dev, NULL, &gerr, BT_IO_OPT_SOURCE_BDADDR, btd_adapter_get_address(adapter), BT_IO_OPT_SOURCE_TYPE, btd_adapter_get_address_type(adapter), BT_IO_OPT_DEST_BDADDR, &dev->bdaddr, BT_IO_OPT_DEST_TYPE, dev->bdaddr_type, BT_IO_OPT_CID, ATT_CID, BT_IO_OPT_SEC_LEVEL, sec_level, BT_IO_OPT_INVALID); if (io == NULL) { if (dev->bonding) { DBusMessage *reply = btd_error_failed( dev->bonding->msg, gerr->message); g_dbus_send_message(dbus_conn, reply); bonding_request_cancel(dev->bonding); bonding_request_free(dev->bonding); } error("ATT bt_io_connect(%s): %s", addr, gerr->message); g_error_free(gerr); return -EIO; } /* Keep this, so we can cancel the connection */ dev->att_io = io; /* Restart temporary timer to give it time to connect/pair, etc. */ if (dev->temporary) set_temporary_timer(dev, btd_opts.tmpto); return 0; } static struct browse_req *browse_request_new(struct btd_device *device, uint8_t type, DBusMessage *msg) { struct browse_req *req; if (device->browse) return NULL; req = g_new0(struct browse_req, 1); req->device = device; req->type = type; device->browse = req; if (!msg) return req; req->msg = dbus_message_ref(msg); /* * Track the request owner to cancel it automatically if the owner * exits */ req->listener_id = g_dbus_add_disconnect_watch(dbus_conn, dbus_message_get_sender(msg), browse_request_exit, req, NULL); return req; } static int device_browse_gatt(struct btd_device *device, DBusMessage *msg) { struct btd_adapter *adapter = device->adapter; struct browse_req *req; req = browse_request_new(device, BROWSE_GATT, msg); if (!req) return -EBUSY; if (device->client) { /* * If discovery has not yet completed, then wait for gatt-client * to become ready. */ if (!bt_gatt_client_is_ready(device->client)) return 0; /* * Services have already been discovered, so signal this browse * request as resolved. */ device_svc_resolved(device, BROWSE_GATT, device->bdaddr_type, 0); return 0; } device->att_io = bt_io_connect(att_connect_cb, device, NULL, NULL, BT_IO_OPT_SOURCE_BDADDR, btd_adapter_get_address(adapter), BT_IO_OPT_SOURCE_TYPE, btd_adapter_get_address_type(adapter), BT_IO_OPT_DEST_BDADDR, &device->bdaddr, BT_IO_OPT_DEST_TYPE, device->bdaddr_type, BT_IO_OPT_CID, ATT_CID, BT_IO_OPT_SEC_LEVEL, BT_IO_SEC_LOW, BT_IO_OPT_INVALID); if (device->att_io == NULL) { browse_request_free(req); return -EIO; } return 0; } static uint16_t get_sdp_flags(struct btd_device *device) { uint16_t vid, pid; vid = btd_device_get_vendor(device); pid = btd_device_get_product(device); /* Sony DualShock 4 is not respecting negotiated L2CAP MTU. This might * results in SDP response being dropped by kernel. Workaround this by * forcing SDP code to use bigger MTU while connecting. */ if (vid == 0x054c && pid == 0x05c4) return SDP_LARGE_MTU; if (btd_adapter_ssp_enabled(device->adapter)) return 0; /* if no EIR try matching Sony DualShock 4 with name and class */ if (!strncmp(device->name, "Wireless Controller", MAX_NAME_LENGTH) && device->class == 0x2508) return SDP_LARGE_MTU; return 0; } static int device_browse_sdp(struct btd_device *device, DBusMessage *msg) { struct btd_adapter *adapter = device->adapter; struct browse_req *req; uuid_t uuid; int err; req = browse_request_new(device, BROWSE_SDP, msg); if (!req) return -EBUSY; sdp_uuid16_create(&uuid, uuid_list[req->search_uuid++]); req->sdp_flags = get_sdp_flags(device); err = bt_search(btd_adapter_get_address(adapter), &device->bdaddr, &uuid, browse_cb, req, NULL, req->sdp_flags); if (err < 0) { browse_request_free(req); return err; } return err; } int device_discover_services(struct btd_device *device) { int err; if (device->bredr) err = device_browse_sdp(device, NULL); else err = device_browse_gatt(device, NULL); if (err == 0 && device->discov_timer) { timeout_remove(device->discov_timer); device->discov_timer = 0; } return err; } struct btd_adapter *device_get_adapter(struct btd_device *device) { if (!device) return NULL; return device->adapter; } const bdaddr_t *device_get_address(struct btd_device *device) { return &device->bdaddr; } uint8_t device_get_le_address_type(struct btd_device *device) { return device->bdaddr_type; } const char *device_get_path(const struct btd_device *device) { if (!device) return NULL; return device->path; } gboolean device_is_temporary(struct btd_device *device) { return device->temporary; } void btd_device_set_temporary(struct btd_device *device, bool temporary) { if (!device) return; if (device->temporary == temporary) return; if (device_address_is_private(device)) return; DBG("temporary %d", temporary); device->temporary = temporary; if (temporary) { if (device->bredr) adapter_accept_list_remove(device->adapter, device); adapter_connect_list_remove(device->adapter, device); if (device->auto_connect) { device->disable_auto_connect = TRUE; device_set_auto_connect(device, FALSE); } set_temporary_timer(device, btd_opts.tmpto); return; } else clear_temporary_timer(device); if (device->bredr) adapter_accept_list_add(device->adapter, device); store_device_info(device); /* attributes were not stored when resolved if device was temporary */ if (device->bdaddr_type != BDADDR_BREDR && device->le_state.svc_resolved && g_slist_length(device->primaries) != 0) store_services(device); } void btd_device_set_trusted(struct btd_device *device, gboolean trusted) { if (!device) return; if (device->trusted == trusted) return; DBG("trusted %d", trusted); device->trusted = trusted; store_device_info(device); g_dbus_emit_property_changed(dbus_conn, device->path, DEVICE_INTERFACE, "Trusted"); } void device_set_bonded(struct btd_device *device, uint8_t bdaddr_type) { struct bearer_state *state; if (!device) return; state = get_state(device, bdaddr_type); if (state->bonded) return; DBG("setting bonded for device to true"); state->bonded = true; btd_device_set_temporary(device, false); /* If the other bearer state was already true we don't need to * send any property signals. */ if (device->bredr_state.bonded == device->le_state.bonded) return; g_dbus_emit_property_changed(dbus_conn, device->path, DEVICE_INTERFACE, "Bonded"); } void device_set_legacy(struct btd_device *device, bool legacy) { if (!device) return; DBG("legacy %d", legacy); if (device->legacy == legacy) return; device->legacy = legacy; g_dbus_emit_property_changed(dbus_conn, device->path, DEVICE_INTERFACE, "LegacyPairing"); } void device_store_svc_chng_ccc(struct btd_device *device, uint8_t bdaddr_type, uint16_t value) { char filename[PATH_MAX]; char device_addr[18]; GKeyFile *key_file; GError *gerr = NULL; uint16_t old_value; gsize length = 0; char *str; ba2str(&device->bdaddr, device_addr); create_filename(filename, PATH_MAX, "/%s/%s/info", btd_adapter_get_storage_dir(device->adapter), device_addr); key_file = g_key_file_new(); if (!g_key_file_load_from_file(key_file, filename, 0, &gerr)) { error("Unable to load key file from %s: (%s)", filename, gerr->message); g_clear_error(&gerr); } /* for bonded devices this is done on every connection so limit writes * to storage if no change needed */ if (bdaddr_type == BDADDR_BREDR) { old_value = g_key_file_get_integer(key_file, "ServiceChanged", "CCC_BR/EDR", NULL); if (old_value == value) goto done; g_key_file_set_integer(key_file, "ServiceChanged", "CCC_BR/EDR", value); } else { old_value = g_key_file_get_integer(key_file, "ServiceChanged", "CCC_LE", NULL); if (old_value == value) goto done; g_key_file_set_integer(key_file, "ServiceChanged", "CCC_LE", value); } create_file(filename, 0600); str = g_key_file_to_data(key_file, &length, NULL); if (!g_file_set_contents(filename, str, length, &gerr)) { error("Unable set contents for %s: (%s)", filename, gerr->message); g_error_free(gerr); } g_free(str); done: g_key_file_free(key_file); } void device_load_svc_chng_ccc(struct btd_device *device, uint16_t *ccc_le, uint16_t *ccc_bredr) { char filename[PATH_MAX]; char device_addr[18]; GKeyFile *key_file; GError *gerr = NULL; ba2str(&device->bdaddr, device_addr); create_filename(filename, PATH_MAX, "/%s/%s/info", btd_adapter_get_storage_dir(device->adapter), device_addr); key_file = g_key_file_new(); if (!g_key_file_load_from_file(key_file, filename, 0, &gerr)) { error("Unable to load key file from %s: (%s)", filename, gerr->message); g_error_free(gerr); } if (!g_key_file_has_group(key_file, "ServiceChanged")) { if (ccc_le) *ccc_le = 0x0000; if (ccc_bredr) *ccc_bredr = 0x0000; g_key_file_free(key_file); return; } if (ccc_le) *ccc_le = g_key_file_get_integer(key_file, "ServiceChanged", "CCC_LE", NULL); if (ccc_bredr) *ccc_bredr = g_key_file_get_integer(key_file, "ServiceChanged", "CCC_BR/EDR", NULL); g_key_file_free(key_file); } void device_set_rssi_with_delta(struct btd_device *device, int8_t rssi, int8_t delta_threshold) { if (!device) return; if (rssi == 0 || device->rssi == 0) { if (device->rssi == rssi) return; DBG("rssi %d", rssi); device->rssi = rssi; } else { int delta; if (device->rssi > rssi) delta = device->rssi - rssi; else delta = rssi - device->rssi; /* only report changes of delta_threshold dBm or more */ if (delta < delta_threshold) return; DBG("rssi %d delta %d", rssi, delta); device->rssi = rssi; } g_dbus_emit_property_changed(dbus_conn, device->path, DEVICE_INTERFACE, "RSSI"); } void device_set_rssi(struct btd_device *device, int8_t rssi) { device_set_rssi_with_delta(device, rssi, RSSI_THRESHOLD); } void device_set_tx_power(struct btd_device *device, int8_t tx_power) { if (!device) return; if (device->tx_power == tx_power) return; DBG("tx_power %d", tx_power); device->tx_power = tx_power; g_dbus_emit_property_changed(dbus_conn, device->path, DEVICE_INTERFACE, "TxPower"); } void device_set_flags(struct btd_device *device, uint8_t flags) { if (!device) return; DBG("flags %d", flags); if (device->ad_flags[0] == flags) return; device->ad_flags[0] = flags; g_dbus_emit_property_changed(dbus_conn, device->path, DEVICE_INTERFACE, "AdvertisingFlags"); } bool device_is_connectable(struct btd_device *device) { struct bearer_state *state; if (!device) return false; if (device->bredr) return true; state = get_state(device, device->bdaddr_type); return state->connectable; } static bool start_discovery(gpointer user_data) { struct btd_device *device = user_data; if (device->bredr) device_browse_sdp(device, NULL); else device_browse_gatt(device, NULL); device->discov_timer = 0; return FALSE; } void device_set_paired(struct btd_device *dev, uint8_t bdaddr_type) { struct bearer_state *state = get_state(dev, bdaddr_type); if (state->paired) return; state->paired = true; /* If the other bearer state was already true we don't need to * send any property signals. */ if (dev->bredr_state.paired == dev->le_state.paired) return; if (!state->svc_resolved) { dev->pending_paired = true; return; } g_dbus_emit_property_changed(dbus_conn, dev->path, DEVICE_INTERFACE, "Paired"); } void device_set_unpaired(struct btd_device *dev, uint8_t bdaddr_type) { struct bearer_state *state = get_state(dev, bdaddr_type); if (!state->paired) return; state->paired = false; /* * If the other bearer state is still true we don't need to * send any property signals or remove device. */ if (dev->bredr_state.paired != dev->le_state.paired) { /* TODO disconnect only unpaired bearer */ if (state->connected) device_request_disconnect(dev, NULL); return; } g_dbus_emit_property_changed(dbus_conn, dev->path, DEVICE_INTERFACE, "Paired"); btd_device_set_temporary(dev, true); if (btd_device_is_connected(dev)) device_request_disconnect(dev, NULL); else btd_adapter_remove_device(dev->adapter, dev); } static void device_auth_req_free(struct btd_device *device) { struct authentication_req *authr = device->authr; if (!authr) return; if (authr->agent) agent_unref(authr->agent); g_free(authr->pincode); g_free(authr); device->authr = NULL; } bool device_is_retrying(struct btd_device *device) { struct bonding_req *bonding = device->bonding; return bonding && bonding->retry_timer > 0; } void device_bonding_complete(struct btd_device *device, uint8_t bdaddr_type, uint8_t status) { struct bonding_req *bonding = device->bonding; struct authentication_req *auth = device->authr; struct bearer_state *state = get_state(device, bdaddr_type); DBG("bonding %p status 0x%02x", bonding, status); if (auth && auth->agent) agent_cancel(auth->agent); if (status) { device_cancel_authentication(device, TRUE); /* Put the device back to the temporary state so that it will be * treated as a newly discovered device. */ if (!btd_device_bearer_is_connected(device) && !device_is_paired(device, bdaddr_type) && !btd_device_is_trusted(device)) btd_device_set_temporary(device, true); device_bonding_failed(device, status); return; } device_auth_req_free(device); /* Enable the wake_allowed property if required */ if (device->wake_override == WAKE_FLAG_ENABLED) device_set_wake_allowed(device, true, -1U); /* If we're already paired nothing more is needed */ if (state->paired) return; device_set_paired(device, bdaddr_type); /* If services are already resolved just reply to the pairing * request */ if (state->svc_resolved && bonding) { /* Attept to store services for this device failed because it * was not paired. Now that we're paired retry. */ store_gatt_db(device); g_dbus_send_reply(dbus_conn, bonding->msg, DBUS_TYPE_INVALID); bonding_request_free(bonding); return; } /* If we were initiators start service discovery immediately. * However if the other end was the initator wait a few seconds * before SDP. This is due to potential IOP issues if the other * end starts doing SDP at the same time as us */ if (bonding) { DBG("Proceeding with service discovery"); /* If we are initiators remove any discovery timer and just * start discovering services directly */ if (device->discov_timer) { timeout_remove(device->discov_timer); device->discov_timer = 0; } if (bdaddr_type == BDADDR_BREDR) device_browse_sdp(device, bonding->msg); else device_browse_gatt(device, bonding->msg); bonding_request_free(bonding); } else if (!state->svc_resolved) { if (!device->browse && !device->discov_timer && btd_opts.reverse_discovery) { /* If we are not initiators and there is no currently * active discovery or discovery timer, set discovery * timer */ DBG("setting timer for reverse service discovery"); device->discov_timer = timeout_add_seconds( DISCOVERY_TIMER, start_discovery, device, NULL); } } } static gboolean svc_idle_cb(gpointer user_data) { struct svc_callback *cb = user_data; struct btd_device *dev = cb->dev; dev->svc_callbacks = g_slist_remove(dev->svc_callbacks, cb); cb->func(cb->dev, 0, cb->user_data); g_free(cb); return FALSE; } unsigned int device_wait_for_svc_complete(struct btd_device *dev, device_svc_cb_t func, void *user_data) { /* This API is only used for BR/EDR (for now) */ struct bearer_state *state = &dev->bredr_state; static unsigned int id = 0; struct svc_callback *cb; cb = g_new0(struct svc_callback, 1); cb->func = func; cb->user_data = user_data; cb->dev = dev; cb->id = ++id; dev->svc_callbacks = g_slist_prepend(dev->svc_callbacks, cb); if (state->svc_resolved || !btd_opts.reverse_discovery) cb->idle_id = g_idle_add(svc_idle_cb, cb); else if (dev->discov_timer > 0) { timeout_remove(dev->discov_timer); dev->discov_timer = timeout_add_seconds( 0, start_discovery, dev, NULL); } return cb->id; } bool device_remove_svc_complete_callback(struct btd_device *dev, unsigned int id) { GSList *l; for (l = dev->svc_callbacks; l != NULL; l = g_slist_next(l)) { struct svc_callback *cb = l->data; if (cb->id != id) continue; if (cb->idle_id > 0) g_source_remove(cb->idle_id); dev->svc_callbacks = g_slist_remove(dev->svc_callbacks, cb); g_free(cb); return true; } return false; } gboolean device_is_bonding(struct btd_device *device, const char *sender) { struct bonding_req *bonding = device->bonding; if (!device->bonding) return FALSE; if (!sender) return TRUE; return g_str_equal(sender, dbus_message_get_sender(bonding->msg)); } static gboolean device_bonding_retry(gpointer data) { struct btd_device *device = data; struct btd_adapter *adapter = device_get_adapter(device); struct bonding_req *bonding = device->bonding; uint8_t io_cap; int err; if (!bonding) return FALSE; DBG("retrying bonding"); bonding->retry_timer = 0; /* Restart the bonding timer to the begining of the pairing. If not * pincode request/reply occurs during this retry, * device_bonding_last_duration() will return a consistent value from * this point. */ device_bonding_restart_timer(device); if (bonding->agent) io_cap = agent_get_io_capability(bonding->agent); else io_cap = IO_CAPABILITY_NOINPUTNOOUTPUT; err = adapter_bonding_attempt(adapter, &device->bdaddr, device->bdaddr_type, io_cap); if (err < 0) device_bonding_complete(device, bonding->bdaddr_type, bonding->status); return FALSE; } int device_bonding_attempt_retry(struct btd_device *device) { struct bonding_req *bonding = device->bonding; /* Ignore other failure events while retrying */ if (device_is_retrying(device)) return 0; if (!bonding) return -EINVAL; /* Mark the end of a bonding attempt to compute the delta for the * retry. */ bonding_request_stop_timer(bonding); if (btd_adapter_pin_cb_iter_end(bonding->cb_iter)) return -EINVAL; DBG("scheduling retry"); bonding->retry_timer = g_timeout_add(3000, device_bonding_retry, device); return 0; } void device_bonding_failed(struct btd_device *device, uint8_t status) { struct bonding_req *bonding = device->bonding; DBusMessage *reply; DBG("status %u", status); if (!bonding) return; if (device->authr) device_cancel_authentication(device, FALSE); reply = new_authentication_return(bonding->msg, status); g_dbus_send_message(dbus_conn, reply); bonding_request_free(bonding); } struct btd_adapter_pin_cb_iter *device_bonding_iter(struct btd_device *device) { if (device->bonding == NULL) return NULL; return device->bonding->cb_iter; } static void pincode_cb(struct agent *agent, DBusError *err, const char *pin, void *data) { struct authentication_req *auth = data; struct btd_device *device = auth->device; /* No need to reply anything if the authentication already failed */ if (auth->agent == NULL) return; btd_adapter_pincode_reply(device->adapter, &device->bdaddr, pin, pin ? strlen(pin) : 0); agent_unref(device->authr->agent); device->authr->agent = NULL; } static void confirm_cb(struct agent *agent, DBusError *err, void *data) { struct authentication_req *auth = data; struct btd_device *device = auth->device; /* No need to reply anything if the authentication already failed */ if (auth->agent == NULL) return; btd_adapter_confirm_reply(device->adapter, &device->bdaddr, auth->addr_type, err ? FALSE : TRUE); agent_unref(device->authr->agent); device->authr->agent = NULL; } static void passkey_cb(struct agent *agent, DBusError *err, uint32_t passkey, void *data) { struct authentication_req *auth = data; struct btd_device *device = auth->device; /* No need to reply anything if the authentication already failed */ if (auth->agent == NULL) return; if (err) passkey = INVALID_PASSKEY; btd_adapter_passkey_reply(device->adapter, &device->bdaddr, auth->addr_type, passkey); agent_unref(device->authr->agent); device->authr->agent = NULL; } static void display_pincode_cb(struct agent *agent, DBusError *err, void *data) { struct authentication_req *auth = data; struct btd_device *device = auth->device; pincode_cb(agent, err, auth->pincode, auth); g_free(device->authr->pincode); device->authr->pincode = NULL; } static struct authentication_req *new_auth(struct btd_device *device, uint8_t addr_type, auth_type_t type, gboolean secure) { struct authentication_req *auth; struct agent *agent; char addr[18]; ba2str(&device->bdaddr, addr); DBG("Requesting agent authentication for %s", addr); if (device->authr) { error("Authentication already requested for %s", addr); return NULL; } if (device->bonding && device->bonding->agent) agent = agent_ref(device->bonding->agent); else agent = agent_get(NULL); if (!agent) { error("No agent available for request type %d", type); return NULL; } auth = g_new0(struct authentication_req, 1); auth->agent = agent; auth->device = device; auth->type = type; auth->addr_type = addr_type; auth->secure = secure; device->authr = auth; return auth; } int device_request_pincode(struct btd_device *device, gboolean secure) { struct authentication_req *auth; int err; auth = new_auth(device, BDADDR_BREDR, AUTH_TYPE_PINCODE, secure); if (!auth) return -EPERM; err = agent_request_pincode(auth->agent, device, pincode_cb, secure, auth, NULL); if (err < 0) { error("Failed requesting authentication"); device_auth_req_free(device); } return err; } int device_request_passkey(struct btd_device *device, uint8_t type) { struct authentication_req *auth; int err; auth = new_auth(device, type, AUTH_TYPE_PASSKEY, FALSE); if (!auth) return -EPERM; err = agent_request_passkey(auth->agent, device, passkey_cb, auth, NULL); if (err < 0) { error("Failed requesting authentication"); device_auth_req_free(device); } return err; } int device_confirm_passkey(struct btd_device *device, uint8_t type, int32_t passkey, uint8_t confirm_hint) { struct authentication_req *auth; int err; /* Just-Works repairing policy */ if (confirm_hint && device_is_paired(device, type)) { if (btd_opts.jw_repairing == JW_REPAIRING_NEVER) { btd_adapter_confirm_reply(device->adapter, &device->bdaddr, type, FALSE); return 0; } else if (btd_opts.jw_repairing == JW_REPAIRING_ALWAYS) { btd_adapter_confirm_reply(device->adapter, &device->bdaddr, type, TRUE); return 0; } } auth = new_auth(device, type, AUTH_TYPE_CONFIRM, FALSE); if (!auth) return -EPERM; auth->passkey = passkey; if (confirm_hint) { if (device->bonding != NULL) { /* We know the client has indicated the intent to pair * with the peer device, so we can auto-accept. */ btd_adapter_confirm_reply(device->adapter, &device->bdaddr, type, TRUE); return 0; } err = agent_request_authorization(auth->agent, device, confirm_cb, auth, NULL); } else { err = agent_request_confirmation(auth->agent, device, passkey, confirm_cb, auth, NULL); } if (err < 0) { if (err == -EINPROGRESS) { /* Already in progress */ confirm_cb(auth->agent, NULL, auth); return 0; } error("Failed requesting authentication"); device_auth_req_free(device); } return err; } int device_notify_passkey(struct btd_device *device, uint8_t type, uint32_t passkey, uint8_t entered) { struct authentication_req *auth; int err; if (device->authr) { auth = device->authr; if (auth->type != AUTH_TYPE_NOTIFY_PASSKEY) return -EPERM; } else { auth = new_auth(device, type, AUTH_TYPE_NOTIFY_PASSKEY, FALSE); if (!auth) return -EPERM; } err = agent_display_passkey(auth->agent, device, passkey, entered); if (err < 0) { error("Failed requesting authentication"); device_auth_req_free(device); } return err; } int device_notify_pincode(struct btd_device *device, gboolean secure, const char *pincode) { struct authentication_req *auth; int err; auth = new_auth(device, BDADDR_BREDR, AUTH_TYPE_NOTIFY_PINCODE, secure); if (!auth) return -EPERM; auth->pincode = g_strdup(pincode); err = agent_display_pincode(auth->agent, device, pincode, display_pincode_cb, auth, NULL); if (err < 0) { if (err == -EINPROGRESS) { /* Already in progress */ display_pincode_cb(auth->agent, NULL, auth); return 0; } error("Failed requesting authentication"); device_auth_req_free(device); } return err; } static void cancel_authentication(struct authentication_req *auth) { struct agent *agent; DBusError err; if (!auth || !auth->agent) return; agent = auth->agent; auth->agent = NULL; dbus_error_init(&err); dbus_set_error_const(&err, ERROR_INTERFACE ".Canceled", NULL); switch (auth->type) { case AUTH_TYPE_PINCODE: pincode_cb(agent, &err, NULL, auth); break; case AUTH_TYPE_CONFIRM: confirm_cb(agent, &err, auth); break; case AUTH_TYPE_PASSKEY: passkey_cb(agent, &err, 0, auth); break; case AUTH_TYPE_NOTIFY_PASSKEY: /* User Notify doesn't require any reply */ break; case AUTH_TYPE_NOTIFY_PINCODE: pincode_cb(agent, &err, NULL, auth); break; } dbus_error_free(&err); } void device_cancel_authentication(struct btd_device *device, gboolean aborted) { struct authentication_req *auth = device->authr; char addr[18]; if (device->adapter) btd_adapter_cancel_service_auth(device->adapter, device); if (!auth) return; ba2str(&device->bdaddr, addr); DBG("Canceling authentication request for %s", addr); if (auth->agent) agent_cancel(auth->agent); if (!aborted) cancel_authentication(auth); device_auth_req_free(device); } gboolean device_is_authenticating(struct btd_device *device) { return (device->authr != NULL); } struct gatt_primary *btd_device_get_primary(struct btd_device *device, const char *uuid) { GSList *match; match = g_slist_find_custom(device->primaries, uuid, bt_uuid_strcmp); if (match) return match->data; return NULL; } GSList *btd_device_get_primaries(struct btd_device *device) { return device->primaries; } struct gatt_db *btd_device_get_gatt_db(struct btd_device *device) { if (!device) return NULL; return device->db; } bool btd_device_set_gatt_db(struct btd_device *device, struct gatt_db *db) { struct gatt_db *clone; if (!device) return false; clone = gatt_db_clone(db); if (clone) return false; gatt_db_unregister(device->db, device->db_id); gatt_db_unref(device->db); device->db = clone; device->db_id = gatt_db_register(device->db, gatt_service_added, gatt_service_removed, device, NULL); return true; } struct bt_gatt_client *btd_device_get_gatt_client(struct btd_device *device) { if (!device) return NULL; return device->client; } void *btd_device_get_attrib(struct btd_device *device) { if (!device) return NULL; return device->attrib; } struct bt_gatt_server *btd_device_get_gatt_server(struct btd_device *device) { if (!device) return NULL; return device->server; } void btd_device_gatt_set_service_changed(struct btd_device *device, uint16_t start, uint16_t end) { /* * TODO: Remove this function and handle service changed via * gatt-client. */ } void btd_device_add_uuid(struct btd_device *device, const char *uuid) { GSList *uuid_list; char *new_uuid; new_uuid = g_strdup(uuid); uuid_list = g_slist_append(NULL, new_uuid); device_probe_profiles(device, uuid_list); g_free(new_uuid); g_slist_free(uuid_list); } static sdp_list_t *read_device_records(struct btd_device *device) { char local[18], peer[18]; char filename[PATH_MAX]; GKeyFile *key_file; GError *gerr = NULL; char **keys, **handle; char *str; sdp_list_t *recs = NULL; sdp_record_t *rec; ba2str(btd_adapter_get_address(device->adapter), local); ba2str(&device->bdaddr, peer); create_filename(filename, PATH_MAX, "/%s/cache/%s", local, peer); key_file = g_key_file_new(); if (!g_key_file_load_from_file(key_file, filename, 0, &gerr)) { error("Unable to load key file from %s: (%s)", filename, gerr->message); g_error_free(gerr); } keys = g_key_file_get_keys(key_file, "ServiceRecords", NULL, NULL); for (handle = keys; handle && *handle; handle++) { str = g_key_file_get_string(key_file, "ServiceRecords", *handle, NULL); if (!str) continue; rec = record_from_string(str); recs = sdp_list_append(recs, rec); g_free(str); } g_strfreev(keys); g_key_file_free(key_file); return recs; } void btd_device_set_record(struct btd_device *device, const char *uuid, const char *record) { /* This API is only used for BR/EDR */ struct bearer_state *state = &device->bredr_state; struct browse_req *req; sdp_list_t *recs = NULL; sdp_record_t *rec; if (!record) return; req = browse_request_new(device, BROWSE_SDP, NULL); if (!req) return; rec = record_from_string(record); recs = sdp_list_append(recs, rec); update_bredr_services(req, recs); sdp_list_free(recs, NULL); device->svc_refreshed = true; state->svc_resolved = true; device_probe_profiles(device, req->profiles_added); /* Propagate services changes */ g_dbus_emit_property_changed(dbus_conn, req->device->path, DEVICE_INTERFACE, "UUIDs"); device_svc_resolved(device, BROWSE_SDP, device->bdaddr_type, 0); } const sdp_record_t *btd_device_get_record(struct btd_device *device, const char *uuid) { /* Load records from storage if there is nothing in cache */ if (!device->tmp_records) { device->tmp_records = read_device_records(device); if (!device->tmp_records) return NULL; } return find_record_in_list(device->tmp_records, uuid); } struct btd_device *btd_device_ref(struct btd_device *device) { __sync_fetch_and_add(&device->ref_count, 1); return device; } static void remove_sirk_info(void *data, void *user_data) { struct sirk_info *info = data; struct btd_device *device = user_data; btd_set_remove_device(info->set, device); } void btd_device_unref(struct btd_device *device) { if (__sync_sub_and_fetch(&device->ref_count, 1)) return; if (!device->path) { error("freeing device without an object path"); return; } if (!queue_isempty(device->sirks)) queue_foreach(device->sirks, remove_sirk_info, device); DBG("Freeing device %s", device->path); g_dbus_unregister_interface(dbus_conn, device->path, DEVICE_INTERFACE); } int device_get_appearance(struct btd_device *device, uint16_t *value) { if (device->appearance == 0) return -1; if (value) *value = device->appearance; return 0; } void device_set_appearance(struct btd_device *device, uint16_t value) { const char *icon = gap_appearance_to_icon(value); if (device->appearance == value) return; g_dbus_emit_property_changed(dbus_conn, device->path, DEVICE_INTERFACE, "Appearance"); if (icon) g_dbus_emit_property_changed(dbus_conn, device->path, DEVICE_INTERFACE, "Icon"); device->appearance = value; store_device_info(device); } void btd_device_set_pnpid(struct btd_device *device, uint16_t source, uint16_t vendor, uint16_t product, uint16_t version) { if (device->vendor_src == source && device->version == version && device->vendor == vendor && device->product == product) return; device->vendor_src = source; device->vendor = vendor; device->product = product; device->version = version; free(device->modalias); device->modalias = bt_modalias(source, vendor, product, version); g_dbus_emit_property_changed(dbus_conn, device->path, DEVICE_INTERFACE, "Modalias"); store_device_info(device); } bool btd_device_flags_enabled(struct btd_device *dev, uint32_t flags) { const char *ll_privacy = "15c0a148-c273-11ea-b3de-0242ac130004"; if (!dev) return false; if (dev->current_flags & flags) return true; /* For backward compatibility check for LL Privacy experimental UUID * since that shall be equivalent to DEVICE_FLAG_ADDRESS_RESOLUTION on * older kernels. */ if ((flags & DEVICE_FLAG_ADDRESS_RESOLUTION) && btd_kernel_experimental_enabled(ll_privacy)) return true; return false; } uint32_t btd_device_get_current_flags(struct btd_device *dev) { return dev->current_flags; } uint32_t btd_device_get_supported_flags(struct btd_device *dev) { return dev->supported_flags; } void btd_device_set_pending_flags(struct btd_device *dev, uint32_t flags) { if (!dev) return; dev->pending_flags = flags; } uint32_t btd_device_get_pending_flags(struct btd_device *dev) { if (!dev) return 0; return dev->pending_flags; } /* This event is sent immediately after add device on all mgmt sockets. * Afterwards, it is only sent to mgmt sockets other than the one which called * set_device_flags. */ void btd_device_flags_changed(struct btd_device *dev, uint32_t supported_flags, uint32_t current_flags) { const uint32_t changed_flags = dev->current_flags ^ current_flags; bool flag_value; dev->supported_flags = supported_flags; dev->current_flags = current_flags; dev->pending_flags &= ~current_flags; if (!changed_flags) return; if (changed_flags & DEVICE_FLAG_REMOTE_WAKEUP && dev->wake_support) { flag_value = !!(current_flags & DEVICE_FLAG_REMOTE_WAKEUP); dev->pending_wake_allowed = flag_value; /* If an override exists and doesn't match the current state, * apply it. This logic will run after Add Device only and will * enable wake for previously paired devices. */ if (dev->wake_override != WAKE_FLAG_DEFAULT) { bool wake_allowed = dev->wake_override == WAKE_FLAG_ENABLED; if (flag_value != wake_allowed) device_set_wake_allowed(dev, wake_allowed, -1U); else device_set_wake_allowed_complete(dev); } else { device_set_wake_allowed_complete(dev); } } } static void service_state_changed(struct btd_service *service, btd_service_state_t old_state, btd_service_state_t new_state, void *user_data) { struct btd_profile *profile = btd_service_get_profile(service); struct btd_device *device = btd_service_get_device(service); int err = btd_service_get_error(service); if (new_state == BTD_SERVICE_STATE_CONNECTING || new_state == BTD_SERVICE_STATE_DISCONNECTING) return; if (old_state == BTD_SERVICE_STATE_CONNECTING) device_profile_connected(device, profile, err); else if (old_state == BTD_SERVICE_STATE_DISCONNECTING) device_profile_disconnected(device, profile, err); } struct btd_service *btd_device_get_service(struct btd_device *dev, const char *remote_uuid) { GSList *l; for (l = dev->services; l != NULL; l = g_slist_next(l)) { struct btd_service *service = l->data; struct btd_profile *p = btd_service_get_profile(service); if (g_str_equal(p->remote_uuid, remote_uuid)) return service; } return NULL; } void btd_device_init(void) { dbus_conn = btd_get_dbus_connection(); service_state_cb_id = btd_service_add_state_cb( service_state_changed, NULL); } void btd_device_cleanup(void) { btd_service_remove_state_cb(service_state_cb_id); } void btd_device_set_volume(struct btd_device *device, int8_t volume) { device->volume = volume; } int8_t btd_device_get_volume(struct btd_device *device) { return device->volume; } void btd_device_foreach_ad(struct btd_device *dev, bt_ad_func_t func, void *data) { bt_ad_foreach_data(dev->ad, func, data); } void btd_device_set_conn_param(struct btd_device *device, uint16_t min_interval, uint16_t max_interval, uint16_t latency, uint16_t timeout) { /* Attempt to load the new connection parameters, in case it is * successful the MGMT_EV_NEW_CONN_PARAM will be generated which will * then trigger btd_adapter_store_conn_param. */ btd_adapter_load_conn_param(device->adapter, &device->bdaddr, device->bdaddr_type, min_interval, max_interval, latency, timeout); } void btd_device_foreach_service_data(struct btd_device *dev, bt_ad_func_t func, void *data) { bt_ad_foreach_service_data(dev->ad, func, data); } bluez-5.82/src/PaxHeaders/eir.h0000644000000000000000000000005014471706460013376 xustar0020 atime=1743515878 20 ctime=1743591278 bluez-5.82/src/eir.h0000644000000000000000000000745114471706460013066 0ustar00rootroot/* SPDX-License-Identifier: GPL-2.0-or-later */ /* * * BlueZ - Bluetooth protocol stack for Linux * * Copyright (C) 2011 Nokia Corporation * Copyright (C) 2011 Marcel Holtmann * * */ #include #include "lib/sdp.h" #include "lib/uuid.h" #define EIR_FLAGS 0x01 /* flags */ #define EIR_UUID16_SOME 0x02 /* 16-bit UUID, more available */ #define EIR_UUID16_ALL 0x03 /* 16-bit UUID, all listed */ #define EIR_UUID32_SOME 0x04 /* 32-bit UUID, more available */ #define EIR_UUID32_ALL 0x05 /* 32-bit UUID, all listed */ #define EIR_UUID128_SOME 0x06 /* 128-bit UUID, more available */ #define EIR_UUID128_ALL 0x07 /* 128-bit UUID, all listed */ #define EIR_NAME_SHORT 0x08 /* shortened local name */ #define EIR_NAME_COMPLETE 0x09 /* complete local name */ #define EIR_TX_POWER 0x0A /* transmit power level */ #define EIR_CLASS_OF_DEV 0x0D /* Class of Device */ #define EIR_SSP_HASH 0x0E /* SSP Hash */ #define EIR_SSP_RANDOMIZER 0x0F /* SSP Randomizer */ #define EIR_DEVICE_ID 0x10 /* device ID */ #define EIR_SOLICIT16 0x14 /* LE: Solicit UUIDs, 16-bit */ #define EIR_SOLICIT128 0x15 /* LE: Solicit UUIDs, 128-bit */ #define EIR_SVC_DATA16 0x16 /* LE: Service data, 16-bit UUID */ #define EIR_PUB_TRGT_ADDR 0x17 /* LE: Public Target Address */ #define EIR_RND_TRGT_ADDR 0x18 /* LE: Random Target Address */ #define EIR_GAP_APPEARANCE 0x19 /* GAP appearance */ #define EIR_SOLICIT32 0x1F /* LE: Solicit UUIDs, 32-bit */ #define EIR_SVC_DATA32 0x20 /* LE: Service data, 32-bit UUID */ #define EIR_SVC_DATA128 0x21 /* LE: Service data, 128-bit UUID */ #define EIR_TRANSPORT_DISCOVERY 0x26 /* Transport Discovery Service */ #define EIR_CSIP_RSI 0x2e /* Resolvable Set Identifier */ #define EIR_MANUFACTURER_DATA 0xFF /* Manufacturer Specific Data */ /* Flags Descriptions */ #define EIR_LIM_DISC 0x01 /* LE Limited Discoverable Mode */ #define EIR_GEN_DISC 0x02 /* LE General Discoverable Mode */ #define EIR_BREDR_UNSUP 0x04 /* BR/EDR Not Supported */ #define EIR_CONTROLLER 0x08 /* Simultaneous LE and BR/EDR to Same Device Capable (Controller) */ #define EIR_SIM_HOST 0x10 /* Simultaneous LE and BR/EDR to Same Device Capable (Host) */ #define EIR_SD_MAX_LEN 238 /* 240 (EIR) - 2 (len) */ #define EIR_MSD_MAX_LEN 236 /* 240 (EIR) - 2 (len & type) - 2 */ struct eir_msd { uint16_t company; uint8_t data[EIR_MSD_MAX_LEN]; uint8_t data_len; }; struct eir_sd { char *uuid; uint8_t data[EIR_SD_MAX_LEN]; uint8_t data_len; }; struct eir_ad { uint8_t type; uint8_t len; void *data; }; struct eir_data { GSList *services; unsigned int flags; char *name; uint32_t class; uint16_t appearance; bool name_complete; bool rsi; int8_t tx_power; uint8_t *hash; uint8_t *randomizer; bdaddr_t addr; uint16_t did_vendor; uint16_t did_product; uint16_t did_version; uint16_t did_source; GSList *msd_list; GSList *sd_list; GSList *data_list; }; void eir_data_free(struct eir_data *eir); void eir_parse(struct eir_data *eir, const uint8_t *eir_data, uint8_t eir_len); int eir_parse_oob(struct eir_data *eir, uint8_t *eir_data, uint16_t eir_len); int eir_create_oob(const bdaddr_t *addr, const char *name, uint32_t cod, const uint8_t *hash, const uint8_t *randomizer, uint16_t did_vendor, uint16_t did_product, uint16_t did_version, uint16_t did_source, sdp_list_t *uuids, uint8_t *data); struct eir_sd *eir_get_service_data(struct eir_data *eir, const char *uuid); bluez-5.82/src/PaxHeaders/org.bluez.service0000644000000000000000000000005011766125764015745 xustar0020 atime=1743516875 20 ctime=1743591288 bluez-5.82/src/org.bluez.service0000644000000000000000000000013711766125764015427 0ustar00rootroot[D-BUS Service] Name=org.bluez Exec=/bin/false User=root SystemdService=dbus-org.bluez.service bluez-5.82/src/PaxHeaders/storage.h0000644000000000000000000000005014015011623014243 xustar0020 atime=1743516499 20 ctime=1743591284 bluez-5.82/src/storage.h0000644000000000000000000000103414015011623013722 0ustar00rootroot/* SPDX-License-Identifier: GPL-2.0-or-later */ /* * * BlueZ - Bluetooth protocol stack for Linux * * Copyright (C) 2002-2010 Marcel Holtmann * * */ int read_discoverable_timeout(const char *src, int *timeout); int read_pairable_timeout(const char *src, int *timeout); int read_on_mode(const char *src, char *mode, int length); int read_local_name(const bdaddr_t *bdaddr, char *name); sdp_record_t *record_from_string(const char *str); sdp_record_t *find_record_in_list(sdp_list_t *recs, const char *uuid); bluez-5.82/src/PaxHeaders/uuid-helper.c0000644000000000000000000000005014766002272015032 xustar0020 atime=1743515578 20 ctime=1743591278 bluez-5.82/src/uuid-helper.c0000644000000000000000000001151014766002272014511 0ustar00rootroot// SPDX-License-Identifier: GPL-2.0-or-later /* * * BlueZ - Bluetooth protocol stack for Linux * * Copyright (C) 2004-2010 Marcel Holtmann * * */ #ifdef HAVE_CONFIG_H #include #endif #define _GNU_SOURCE #include #include #include #include #include #include "lib/bluetooth.h" #include "lib/sdp.h" #include "lib/sdp_lib.h" #include "uuid-helper.h" char *bt_modalias(uint16_t source, uint16_t vendor, uint16_t product, uint16_t version) { char *str; int err; switch (source) { case 0x0001: err = asprintf(&str, "%s:v%04Xp%04Xd%04X", "bluetooth", vendor, product, version); break; case 0x0002: err = asprintf(&str, "%s:v%04Xp%04Xd%04X", "usb", vendor, product, version); break; default: return NULL; } if (err < 0) return NULL; return str; } char *bt_uuid2string(uuid_t *uuid) { char *str; uuid_t uuid128; unsigned int data0; unsigned short data1; unsigned short data2; unsigned short data3; unsigned int data4; unsigned short data5; int err; if (!uuid) return NULL; switch (uuid->type) { case SDP_UUID16: sdp_uuid16_to_uuid128(&uuid128, uuid); break; case SDP_UUID32: sdp_uuid32_to_uuid128(&uuid128, uuid); break; case SDP_UUID128: memcpy(&uuid128, uuid, sizeof(uuid_t)); break; default: /* Type of UUID unknown */ return NULL; } memcpy(&data0, &uuid128.value.uuid128.data[0], 4); memcpy(&data1, &uuid128.value.uuid128.data[4], 2); memcpy(&data2, &uuid128.value.uuid128.data[6], 2); memcpy(&data3, &uuid128.value.uuid128.data[8], 2); memcpy(&data4, &uuid128.value.uuid128.data[10], 4); memcpy(&data5, &uuid128.value.uuid128.data[14], 2); err = asprintf(&str, "%.8x-%.4x-%.4x-%.4x-%.8x%.4x", ntohl(data0), ntohs(data1), ntohs(data2), ntohs(data3), ntohl(data4), ntohs(data5)); if (err < 0) return NULL; return str; } static struct { const char *name; uint16_t class; } bt_services[] = { { "a2dp-sink", AUDIO_SINK_SVCLASS_ID }, { "a2dp-source",AUDIO_SOURCE_SVCLASS_ID }, { "bip", IMAGING_SVCLASS_ID }, { "bpp", BASIC_PRINTING_SVCLASS_ID }, { "dun", DIALUP_NET_SVCLASS_ID }, { "fax", FAX_SVCLASS_ID }, { "ftp", OBEX_FILETRANS_SVCLASS_ID }, { "gnss", GNSS_SERVER_SVCLASS_ID }, { "hfp", HANDSFREE_SVCLASS_ID }, { "hfp-ag", HANDSFREE_AGW_SVCLASS_ID }, { "hfp-hf", HANDSFREE_SVCLASS_ID }, { "hsp", HEADSET_SVCLASS_ID }, { "hsp-ag", HEADSET_AGW_SVCLASS_ID }, { "hsp-hs", HEADSET_SVCLASS_ID }, { "map-mas", MAP_MSE_SVCLASS_ID }, { "map-mce", MAP_MCE_SVCLASS_ID }, { "map-mns", MAP_MCE_SVCLASS_ID }, { "map-mse", MAP_MSE_SVCLASS_ID }, { "opp", OBEX_OBJPUSH_SVCLASS_ID }, { "pbap", PBAP_SVCLASS_ID }, { "pbap-pce", PBAP_PCE_SVCLASS_ID }, { "pbap-pse", PBAP_PSE_SVCLASS_ID }, { "sap", SAP_SVCLASS_ID }, { "spp", SERIAL_PORT_SVCLASS_ID }, { "synch", IRMC_SYNC_SVCLASS_ID }, { } }; static uint16_t name2class(const char *pattern) { int i; for (i = 0; bt_services[i].name; i++) { if (strcasecmp(bt_services[i].name, pattern) == 0) return bt_services[i].class; } return 0; } static inline bool is_uuid128(const char *string) { return (strlen(string) == 36 && string[8] == '-' && string[13] == '-' && string[18] == '-' && string[23] == '-'); } static int string2uuid16(uuid_t *uuid, const char *string) { int length = strlen(string); char *endptr = NULL; uint16_t u16; if (length != 4 && length != 6) return -EINVAL; u16 = strtol(string, &endptr, 16); if (endptr && *endptr == '\0') { sdp_uuid16_create(uuid, u16); return 0; } return -EINVAL; } char *bt_name2string(const char *pattern) { uuid_t uuid; unsigned int uuid16; char *endptr = NULL; /* UUID 128 string format */ if (is_uuid128(pattern)) return strdup(pattern); /* Friendly service name format */ uuid16 = name2class(pattern); if (uuid16) goto proceed; /* HEX format */ uuid16 = strtoul(pattern, &endptr, 16); if (uuid16 <= 0xffff && *endptr == '\0') goto proceed; return NULL; proceed: sdp_uuid16_create(&uuid, uuid16); return bt_uuid2string(&uuid); } int bt_string2uuid(uuid_t *uuid, const char *string) { uint32_t data0, data4; uint16_t data1, data2, data3, data5; if (is_uuid128(string) && sscanf(string, "%08x-%04hx-%04hx-%04hx-%08x%04hx", &data0, &data1, &data2, &data3, &data4, &data5) == 6) { uint8_t val[16]; data0 = htonl(data0); data1 = htons(data1); data2 = htons(data2); data3 = htons(data3); data4 = htonl(data4); data5 = htons(data5); memcpy(&val[0], &data0, 4); memcpy(&val[4], &data1, 2); memcpy(&val[6], &data2, 2); memcpy(&val[8], &data3, 2); memcpy(&val[10], &data4, 4); memcpy(&val[14], &data5, 2); sdp_uuid128_create(uuid, val); return 0; } else { uint16_t class = name2class(string); if (class) { sdp_uuid16_create(uuid, class); return 0; } return string2uuid16(uuid, string); } } bluez-5.82/src/PaxHeaders/textfile.c0000644000000000000000000000005014711225434014430 xustar0020 atime=1743516085 20 ctime=1743591283 bluez-5.82/src/textfile.c0000644000000000000000000001772714711225434014127 0ustar00rootroot// SPDX-License-Identifier: GPL-2.0-or-later /* * * BlueZ - Bluetooth protocol stack for Linux * * Copyright (C) 2004-2010 Marcel Holtmann * * */ #ifdef HAVE_CONFIG_H #include #endif #define _GNU_SOURCE #include #include #include #include #include #include #include #include #include #include #include #include #include #include "textfile.h" int create_filename(char *str, size_t size, const char *fmt, ...) { static const char *prefix; static int prefix_len; char suffix[PATH_MAX]; va_list ap; int err; if (!prefix) { const char *statedir = getenv("STATE_DIRECTORY"); /* Check if running as service */ if (statedir) { prefix = statedir; /* Check if there multiple paths given */ if (strstr(prefix, ":")) prefix_len = strstr(prefix, ":") - prefix; else prefix_len = strlen(prefix); } else { prefix = STORAGEDIR; prefix_len = strlen(prefix); } } va_start(ap, fmt); err = vsnprintf(suffix, sizeof(suffix), fmt, ap); va_end(ap); if (err < 0) return err; return snprintf(str, size, "%*s%s", prefix_len, prefix, suffix); } static int create_dirs(const char *filename, const mode_t mode) { struct stat st; char dir[PATH_MAX + 1], *prev, *next; int err; err = stat(filename, &st); if (!err && S_ISREG(st.st_mode)) return 0; memset(dir, 0, PATH_MAX + 1); strcat(dir, "/"); prev = strchr(filename, '/'); while (prev) { next = strchr(prev + 1, '/'); if (!next) break; if (next - prev == 1) { prev = next; continue; } strncat(dir, prev + 1, next - prev); mkdir(dir, mode); prev = next; } return 0; } int create_file(const char *filename, const mode_t mode) { int fd; create_dirs(filename, 0700); fd = open(filename, O_RDWR | O_CREAT, mode); if (fd < 0) return fd; close(fd); return 0; } int create_name(char *buf, size_t size, const char *address, const char *name) { return create_filename(buf, size, "/%s/%s", address, name); } static inline char *find_key(char *map, size_t size, const char *key, size_t len, int icase) { char *ptr = map; size_t ptrlen = size; while (ptrlen > len + 1) { int cmp = (icase) ? strncasecmp(ptr, key, len) : strncmp(ptr, key, len); if (cmp == 0) { if (ptr == map) { if (*(ptr + len) == ' ') return ptr; } else if ((*(ptr - 1) == '\r' || *(ptr - 1) == '\n') && *(ptr + len) == ' ') return ptr; } if (icase) { char *p1 = memchr(ptr + 1, tolower(*key), ptrlen - 1); char *p2 = memchr(ptr + 1, toupper(*key), ptrlen - 1); if (!p1) ptr = p2; else if (!p2) ptr = p1; else ptr = (p1 < p2) ? p1 : p2; } else ptr = memchr(ptr + 1, *key, ptrlen - 1); if (!ptr) return NULL; ptrlen = size - (ptr - map); } return NULL; } static inline int write_key_value(int fd, const char *key, const char *value) { char *str; size_t size; int err = 0; size = strlen(key) + strlen(value) + 2; str = malloc(size + 1); if (!str) return ENOMEM; sprintf(str, "%s %s\n", key, value); if (write(fd, str, size) < 0) err = -errno; free(str); return err; } static char *strnpbrk(const char *s, ssize_t len, const char *accept) { const char *p = s; const char *end; end = s + len - 1; while (p <= end && *p) { const char *a = accept; while (*a) { if (*p == *a) return (char *) p; a++; } p++; } return NULL; } static int write_key(const char *pathname, const char *key, const char *value, int icase) { struct stat st; char *map, *off, *end, *str; off_t size; size_t base; int fd, len, err = 0; fd = open(pathname, O_RDWR); if (fd < 0) return -errno; if (flock(fd, LOCK_EX) < 0) { err = -errno; goto close; } if (fstat(fd, &st) < 0) { err = -errno; goto unlock; } size = st.st_size; if (!size) { if (value) { lseek(fd, size, SEEK_SET); err = write_key_value(fd, key, value); } goto unlock; } map = mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_LOCKED, fd, 0); if (!map || map == MAP_FAILED) { err = -errno; goto unlock; } len = strlen(key); off = find_key(map, size, key, len, icase); if (!off) { munmap(map, size); if (value) { lseek(fd, size, SEEK_SET); err = write_key_value(fd, key, value); } goto unlock; } base = off - map; end = strnpbrk(off, size, "\r\n"); if (!end) { err = -EILSEQ; goto unmap; } if (value && ((ssize_t) strlen(value) == end - off - len - 1) && !strncmp(off + len + 1, value, end - off - len - 1)) goto unmap; len = strspn(end, "\r\n"); end += len; len = size - (end - map); if (!len) { munmap(map, size); if (ftruncate(fd, base) < 0) { err = -errno; goto unlock; } lseek(fd, base, SEEK_SET); if (value) err = write_key_value(fd, key, value); goto unlock; } if (len < 0 || len > size) { err = -EILSEQ; goto unmap; } str = malloc(len); if (!str) { err = -errno; goto unmap; } memcpy(str, end, len); munmap(map, size); if (ftruncate(fd, base) < 0) { err = -errno; free(str); goto unlock; } lseek(fd, base, SEEK_SET); if (value) err = write_key_value(fd, key, value); if (write(fd, str, len) < 0) err = -errno; free(str); goto unlock; unmap: munmap(map, size); unlock: flock(fd, LOCK_UN); close: fdatasync(fd); close(fd); errno = -err; return err; } static char *read_key(const char *pathname, const char *key, int icase) { struct stat st; char *map, *off, *end, *str = NULL; off_t size; size_t len; int fd, err = 0; fd = open(pathname, O_RDONLY); if (fd < 0) return NULL; if (flock(fd, LOCK_SH) < 0) { err = -errno; goto close; } if (fstat(fd, &st) < 0) { err = -errno; goto unlock; } size = st.st_size; map = mmap(NULL, size, PROT_READ, MAP_SHARED, fd, 0); if (!map || map == MAP_FAILED) { err = -errno; goto unlock; } len = strlen(key); off = find_key(map, size, key, len, icase); if (!off) { err = -EILSEQ; goto unmap; } end = strnpbrk(off, size - (off - map), "\r\n"); if (!end) { err = -EILSEQ; goto unmap; } str = malloc(end - off - len); if (!str) { err = -EILSEQ; goto unmap; } memset(str, 0, end - off - len); strncpy(str, off + len + 1, end - off - len - 1); unmap: munmap(map, size); unlock: flock(fd, LOCK_UN); close: close(fd); errno = -err; return str; } int textfile_put(const char *pathname, const char *key, const char *value) { return write_key(pathname, key, value, 0); } int textfile_del(const char *pathname, const char *key) { return write_key(pathname, key, NULL, 0); } char *textfile_get(const char *pathname, const char *key) { return read_key(pathname, key, 0); } int textfile_foreach(const char *pathname, textfile_cb func, void *data) { struct stat st; char *map, *off, *end, *key, *value; off_t size; size_t len; int fd, err = 0; fd = open(pathname, O_RDONLY); if (fd < 0) return -errno; if (flock(fd, LOCK_SH) < 0) { err = -errno; goto close; } if (fstat(fd, &st) < 0) { err = -errno; goto unlock; } size = st.st_size; map = mmap(NULL, size, PROT_READ, MAP_SHARED, fd, 0); if (!map || map == MAP_FAILED) { err = -errno; goto unlock; } off = map; while (size - (off - map) > 0) { end = strnpbrk(off, size - (off - map), " "); if (!end) { err = -EILSEQ; break; } len = end - off; key = malloc(len + 1); if (!key) { err = -errno; break; } memset(key, 0, len + 1); memcpy(key, off, len); off = end + 1; if (size - (off - map) < 0) { err = -EILSEQ; free(key); break; } end = strnpbrk(off, size - (off - map), "\r\n"); if (!end) { err = -EILSEQ; free(key); break; } len = end - off; value = malloc(len + 1); if (!value) { err = -errno; free(key); break; } memset(value, 0, len + 1); memcpy(value, off, len); func(key, value, data); free(key); free(value); off = end + 1; } munmap(map, size); unlock: flock(fd, LOCK_UN); close: close(fd); errno = -err; return 0; } bluez-5.82/src/PaxHeaders/oui.h0000644000000000000000000000005014015011623013373 xustar0020 atime=1743516209 20 ctime=1743591285 bluez-5.82/src/oui.h0000644000000000000000000000033214015011623013052 0ustar00rootroot/* SPDX-License-Identifier: GPL-2.0-or-later */ /* * * BlueZ - Bluetooth protocol stack for Linux * * Copyright (C) 2004-2010 Marcel Holtmann * * */ char *batocomp(const bdaddr_t *ba); bluez-5.82/src/PaxHeaders/btd.h0000644000000000000000000000005014766002272013365 xustar0020 atime=1743515578 20 ctime=1743591284 bluez-5.82/src/btd.h0000644000000000000000000000657414766002272013062 0ustar00rootroot/* SPDX-License-Identifier: GPL-2.0-or-later */ /* * * BlueZ - Bluetooth protocol stack for Linux * * Copyright (C) 2000-2001 Qualcomm Incorporated * Copyright (C) 2002-2003 Maxim Krasnyansky * Copyright (C) 2002-2010 Marcel Holtmann * * */ #include typedef enum { BT_MODE_DUAL, BT_MODE_BREDR, BT_MODE_LE, } bt_mode_t; typedef enum { BT_GATT_CACHE_ALWAYS, BT_GATT_CACHE_YES, BT_GATT_CACHE_NO, } bt_gatt_cache_t; enum jw_repairing_t { JW_REPAIRING_NEVER, JW_REPAIRING_CONFIRM, JW_REPAIRING_ALWAYS, }; enum mps_mode_t { MPS_OFF, MPS_SINGLE, MPS_MULTIPLE, }; enum sc_mode_t { SC_OFF, SC_ON, SC_ONLY, }; enum bt_gatt_export_t { BT_GATT_EXPORT_OFF, BT_GATT_EXPORT_READ_ONLY, BT_GATT_EXPORT_READ_WRITE, }; struct btd_br_defaults { uint16_t page_scan_type; uint16_t page_scan_interval; uint16_t page_scan_win; uint16_t scan_type; uint16_t scan_interval; uint16_t scan_win; uint16_t link_supervision_timeout; uint16_t page_timeout; uint16_t min_sniff_interval; uint16_t max_sniff_interval; }; struct btd_le_defaults { uint8_t addr_resolution; uint16_t min_adv_interval; uint16_t max_adv_interval; uint16_t adv_rotation_interval; uint16_t scan_interval_autoconnect; uint16_t scan_win_autoconnect; uint16_t scan_interval_suspend; uint16_t scan_win_suspend; uint16_t scan_interval_discovery; uint16_t scan_win_discovery; uint16_t scan_interval_adv_monitor; uint16_t scan_win_adv_monitor; uint16_t scan_interval_connect; uint16_t scan_win_connect; uint16_t min_conn_interval; uint16_t max_conn_interval; uint16_t conn_latency; uint16_t conn_lsto; uint16_t autoconnect_timeout; uint16_t advmon_allowlist_scan_duration; uint16_t advmon_no_filter_scan_duration; uint8_t enable_advmon_interleave_scan; }; struct btd_defaults { uint16_t num_entries; struct btd_br_defaults br; struct btd_le_defaults le; }; struct btd_csis { bool encrypt; uint8_t sirk[16]; uint8_t size; uint8_t rank; }; struct btd_avdtp_opts { uint8_t session_mode; uint8_t stream_mode; }; struct btd_avrcp_opts { bool volume_without_target; bool volume_category; }; struct btd_advmon_opts { uint8_t rssi_sampling_period; }; struct btd_opts { char *name; uint32_t class; bool pairable; uint32_t pairto; uint32_t discovto; uint32_t tmpto; uint8_t privacy; bool device_privacy; uint32_t name_request_retry_delay; uint8_t secure_conn; struct btd_defaults defaults; bool reverse_discovery; bool name_resolv; bool debug_keys; bool fast_conn; bool refresh_discovery; bool experimental; bool testing; struct queue *kernel; uint16_t did_source; uint16_t did_vendor; uint16_t did_product; uint16_t did_version; bt_mode_t mode; uint16_t max_adapters; bt_gatt_cache_t gatt_cache; uint16_t gatt_mtu; uint8_t gatt_channels; bool gatt_client; enum bt_gatt_export_t gatt_export; enum mps_mode_t mps; struct btd_avdtp_opts avdtp; struct btd_avrcp_opts avrcp; uint8_t key_size; enum jw_repairing_t jw_repairing; struct btd_advmon_opts advmon; struct btd_csis csis; }; extern struct btd_opts btd_opts; void plugin_init(const char *enable, const char *disable); void plugin_cleanup(void); void rfkill_init(void); void rfkill_exit(void); int rfkill_get_blocked(uint16_t index); GKeyFile *btd_get_main_conf(void); bool btd_kernel_experimental_enabled(const char *uuid); void btd_exit(void); bluez-5.82/src/PaxHeaders/dbus-common.h0000644000000000000000000000005014015011623015022 xustar0020 atime=1743516498 20 ctime=1743591285 bluez-5.82/src/dbus-common.h0000644000000000000000000000115114015011623014501 0ustar00rootroot/* SPDX-License-Identifier: GPL-2.0-or-later */ /* * * BlueZ - Bluetooth protocol stack for Linux * * Copyright (C) 2006-2010 Nokia Corporation * Copyright (C) 2004-2010 Marcel Holtmann * * */ void dict_append_entry(DBusMessageIter *dict, const char *key, int type, void *val); void dict_append_array(DBusMessageIter *dict, const char *key, int type, void *val, int n_elements); void set_dbus_connection(DBusConnection *conn); DBusConnection *btd_get_dbus_connection(void); const char *class_to_icon(uint32_t class); const char *gap_appearance_to_icon(uint16_t appearance); bluez-5.82/src/PaxHeaders/device.h0000644000000000000000000000005014766002272014053 xustar0020 atime=1743515578 20 ctime=1743591285 bluez-5.82/src/device.h0000644000000000000000000002510014766002272013532 0ustar00rootroot/* SPDX-License-Identifier: GPL-2.0-or-later */ /* * * BlueZ - Bluetooth protocol stack for Linux * * Copyright (C) 2006-2010 Nokia Corporation * Copyright (C) 2004-2010 Marcel Holtmann * Copyright 2024 NXP * */ #define DEVICE_INTERFACE "org.bluez.Device1" struct btd_device; struct btd_device *device_create(struct btd_adapter *adapter, const bdaddr_t *address, uint8_t bdaddr_type); struct btd_device *device_create_from_storage(struct btd_adapter *adapter, const char *address, GKeyFile *key_file); char *btd_device_get_storage_path(struct btd_device *device, const char *filename); void btd_device_device_set_name(struct btd_device *device, const char *name); void device_store_cached_name(struct btd_device *dev, const char *name); void device_get_name(struct btd_device *device, char *name, size_t len); bool device_name_known(struct btd_device *device); bool device_is_name_resolve_allowed(struct btd_device *device); void device_name_resolve_fail(struct btd_device *device); void device_set_class(struct btd_device *device, uint32_t class); bool device_address_is_private(struct btd_device *dev); void device_set_rpa(struct btd_device *device, bool value); void device_update_addr(struct btd_device *device, const bdaddr_t *bdaddr, uint8_t bdaddr_type); void device_set_bredr_support(struct btd_device *device); void device_set_le_support(struct btd_device *device, uint8_t bdaddr_type); void device_update_last_seen(struct btd_device *device, uint8_t bdaddr_type, bool connectable); void device_merge_duplicate(struct btd_device *dev, struct btd_device *dup); uint32_t btd_device_get_class(struct btd_device *device); uint16_t btd_device_get_vendor(struct btd_device *device); uint16_t btd_device_get_vendor_src(struct btd_device *device); uint16_t btd_device_get_product(struct btd_device *device); uint16_t btd_device_get_version(struct btd_device *device); const char *btd_device_get_icon(struct btd_device *device); void device_remove_bonding(struct btd_device *device, uint8_t bdaddr_type); void device_remove(struct btd_device *device, gboolean remove_stored); int device_address_cmp(gconstpointer a, gconstpointer b); int device_bdaddr_cmp(gconstpointer a, gconstpointer b); /* Struct used by device_addr_type_cmp() */ struct device_addr_type { bdaddr_t bdaddr; uint8_t bdaddr_type; }; int device_addr_type_cmp(gconstpointer a, gconstpointer b); GSList *btd_device_get_uuids(struct btd_device *device); bool btd_device_has_uuid(struct btd_device *device, const char *uuid); void device_probe_profiles(struct btd_device *device, GSList *profiles); void btd_device_set_record(struct btd_device *device, const char *uuid, const char *record); const sdp_record_t *btd_device_get_record(struct btd_device *device, const char *uuid); struct gatt_primary *btd_device_get_primary(struct btd_device *device, const char *uuid); GSList *btd_device_get_primaries(struct btd_device *device); struct gatt_db *btd_device_get_gatt_db(struct btd_device *device); bool btd_device_set_gatt_db(struct btd_device *device, struct gatt_db *db); struct bt_gatt_client *btd_device_get_gatt_client(struct btd_device *device); struct bt_gatt_server *btd_device_get_gatt_server(struct btd_device *device); bool btd_device_is_initiator(struct btd_device *device); void *btd_device_get_attrib(struct btd_device *device); void btd_device_gatt_set_service_changed(struct btd_device *device, uint16_t start, uint16_t end); bool device_attach_att(struct btd_device *dev, GIOChannel *io); void btd_device_add_uuid(struct btd_device *device, const char *uuid); void device_add_eir_uuids(struct btd_device *dev, GSList *uuids); void device_set_manufacturer_data(struct btd_device *dev, GSList *list, bool duplicate); void device_set_service_data(struct btd_device *dev, GSList *list, bool duplicate); void device_set_data(struct btd_device *dev, GSList *list, bool duplicate); void device_probe_profile(gpointer a, gpointer b); void device_remove_profile(gpointer a, gpointer b); struct btd_adapter *device_get_adapter(struct btd_device *device); const bdaddr_t *device_get_address(struct btd_device *device); uint8_t device_get_le_address_type(struct btd_device *device); const char *device_get_path(const struct btd_device *device); gboolean device_is_temporary(struct btd_device *device); bool device_is_connectable(struct btd_device *device); bool device_is_paired(struct btd_device *device, uint8_t bdaddr_type); bool device_is_bonded(struct btd_device *device, uint8_t bdaddr_type); bool btd_device_is_trusted(struct btd_device *device); void device_set_paired(struct btd_device *dev, uint8_t bdaddr_type); void device_set_unpaired(struct btd_device *dev, uint8_t bdaddr_type); void btd_device_set_temporary(struct btd_device *device, bool temporary); void btd_device_set_trusted(struct btd_device *device, gboolean trusted); void btd_device_set_connectable(struct btd_device *device, bool connectable); void device_set_bonded(struct btd_device *device, uint8_t bdaddr_type); void device_set_legacy(struct btd_device *device, bool legacy); void device_set_rssi_with_delta(struct btd_device *device, int8_t rssi, int8_t delta_threshold); void device_set_rssi(struct btd_device *device, int8_t rssi); void device_set_tx_power(struct btd_device *device, int8_t tx_power); void device_set_flags(struct btd_device *device, uint8_t flags); bool btd_device_is_connected(struct btd_device *dev); bool btd_device_bearer_is_connected(struct btd_device *dev); uint8_t btd_device_get_bdaddr_type(struct btd_device *dev); bool device_is_retrying(struct btd_device *device); void device_bonding_complete(struct btd_device *device, uint8_t bdaddr_type, uint8_t status); gboolean device_is_bonding(struct btd_device *device, const char *sender); void device_bonding_attempt_failed(struct btd_device *device, uint8_t status); void device_bonding_failed(struct btd_device *device, uint8_t status); struct btd_adapter_pin_cb_iter *device_bonding_iter(struct btd_device *device); int device_bonding_attempt_retry(struct btd_device *device); long device_bonding_last_duration(struct btd_device *device); void device_bonding_restart_timer(struct btd_device *device); int device_request_pincode(struct btd_device *device, gboolean secure); int device_request_passkey(struct btd_device *device, uint8_t type); int device_confirm_passkey(struct btd_device *device, uint8_t type, int32_t passkey, uint8_t confirm_hint); int device_notify_passkey(struct btd_device *device, uint8_t type, uint32_t passkey, uint8_t entered); int device_notify_pincode(struct btd_device *device, gboolean secure, const char *pincode); void device_cancel_authentication(struct btd_device *device, gboolean aborted); gboolean device_is_authenticating(struct btd_device *device); void device_add_connection(struct btd_device *dev, uint8_t bdaddr_type, uint32_t flags); void device_remove_connection(struct btd_device *device, uint8_t bdaddr_type, bool *remove); void device_request_disconnect(struct btd_device *device, DBusMessage *msg); bool device_is_disconnecting(struct btd_device *device); void device_set_ltk(struct btd_device *device, const uint8_t val[16], bool central, uint8_t enc_size); bool btd_device_get_ltk(struct btd_device *device, uint8_t val[16], bool *central, uint8_t *enc_size); void device_set_csrk(struct btd_device *device, const uint8_t val[16], uint32_t counter, uint8_t type, bool store_hint); bool btd_device_add_set(struct btd_device *device, bool encrypted, uint8_t sirk[16], uint8_t size, uint8_t rank); void device_store_svc_chng_ccc(struct btd_device *device, uint8_t bdaddr_type, uint16_t value); void device_load_svc_chng_ccc(struct btd_device *device, uint16_t *ccc_le, uint16_t *ccc_bredr); void device_set_wake_support(struct btd_device *device, bool wake_support); void device_set_wake_override(struct btd_device *device, bool wake_override); void device_set_wake_allowed(struct btd_device *device, bool wake_allowed, guint32 id); void device_set_refresh_discovery(struct btd_device *dev, bool refresh); typedef void (*disconnect_watch) (struct btd_device *device, gboolean removal, void *user_data); guint device_add_disconnect_watch(struct btd_device *device, disconnect_watch watch, void *user_data, GDestroyNotify destroy); void device_remove_disconnect_watch(struct btd_device *device, guint id); int device_get_appearance(struct btd_device *device, uint16_t *value); void device_set_appearance(struct btd_device *device, uint16_t value); struct btd_device *btd_device_ref(struct btd_device *device); void btd_device_unref(struct btd_device *device); int device_block(struct btd_device *device, gboolean update_only); int device_unblock(struct btd_device *device, gboolean silent, gboolean update_only); void btd_device_set_pnpid(struct btd_device *device, uint16_t source, uint16_t vendor, uint16_t product, uint16_t version); int device_connect_le(struct btd_device *dev); typedef void (*device_svc_cb_t) (struct btd_device *dev, int err, void *user_data); unsigned int device_wait_for_svc_complete(struct btd_device *dev, device_svc_cb_t func, void *user_data); bool device_remove_svc_complete_callback(struct btd_device *dev, unsigned int id); struct btd_service *btd_device_get_service(struct btd_device *dev, const char *remote_uuid); int device_discover_services(struct btd_device *device); int btd_device_connect_services(struct btd_device *dev, GSList *services); bool btd_device_flags_enabled(struct btd_device *dev, uint32_t flags); uint32_t btd_device_get_current_flags(struct btd_device *dev); uint32_t btd_device_get_supported_flags(struct btd_device *dev); uint32_t btd_device_get_pending_flags(struct btd_device *dev); void btd_device_set_pending_flags(struct btd_device *dev, uint32_t flags); void btd_device_flags_changed(struct btd_device *dev, uint32_t supported_flags, uint32_t current_flags); bool btd_device_all_services_allowed(struct btd_device *dev); void btd_device_update_allowed_services(struct btd_device *dev); void btd_device_init(void); void btd_device_cleanup(void); void btd_device_set_volume(struct btd_device *dev, int8_t volume); int8_t btd_device_get_volume(struct btd_device *dev); typedef void (*bt_device_ad_func_t)(void *data, void *user_data); void btd_device_foreach_ad(struct btd_device *dev, bt_device_ad_func_t func, void *data); void btd_device_set_conn_param(struct btd_device *device, uint16_t min_interval, uint16_t max_interval, uint16_t latency, uint16_t timeout); void btd_device_foreach_service_data(struct btd_device *dev, bt_device_ad_func_t func, void *data); bluez-5.82/src/PaxHeaders/main.c0000644000000000000000000000005014772767672013556 xustar0020 atime=1743515579 20 ctime=1743591284 bluez-5.82/src/main.c0000644000000000000000000010474014772767672013245 0ustar00rootroot// SPDX-License-Identifier: GPL-2.0-or-later /* * * BlueZ - Bluetooth protocol stack for Linux * * Copyright (C) 2000-2001 Qualcomm Incorporated * Copyright (C) 2002-2003 Maxim Krasnyansky * Copyright (C) 2002-2010 Marcel Holtmann * * */ #ifdef HAVE_CONFIG_H #include #endif #define _GNU_SOURCE #include #include #include #include #include #include #include #include #include #include #include #include #include #include "lib/bluetooth.h" #include "lib/sdp.h" #include "gdbus/gdbus.h" #include "btio/btio.h" #include "log.h" #include "backtrace.h" #include "shared/att-types.h" #include "shared/mainloop.h" #include "shared/timeout.h" #include "shared/queue.h" #include "shared/crypto.h" #include "lib/uuid.h" #include "shared/util.h" #include "btd.h" #include "sdpd.h" #include "adapter.h" #include "device.h" #include "dbus-common.h" #include "agent.h" #include "profile.h" #define BLUEZ_NAME "org.bluez" #define DEFAULT_PAIRABLE_TIMEOUT 0 /* disabled */ #define DEFAULT_DISCOVERABLE_TIMEOUT 180 /* 3 minutes */ #define DEFAULT_TEMPORARY_TIMEOUT 30 /* 30 seconds */ #define DEFAULT_NAME_REQUEST_RETRY_DELAY 300 /* 5 minutes */ #define SHUTDOWN_GRACE_SECONDS 10 struct btd_opts btd_opts; static GKeyFile *main_conf; static char main_conf_file_path[PATH_MAX]; static const char *supported_options[] = { "Name", "Class", "DiscoverableTimeout", "AlwaysPairable", "PairableTimeout", "DeviceID", "ReverseServiceDiscovery", "NameResolving", "DebugKeys", "ControllerMode", "MaxControllers", "MultiProfile", "FastConnectable", "SecureConnections", "Privacy", "JustWorksRepairing", "TemporaryTimeout", "RefreshDiscovery", "Experimental", "Testing", "KernelExperimental", "RemoteNameRequestRetryDelay", NULL }; static const char *br_options[] = { "PageScanType", "PageScanInterval", "PageScanWindow", "InquiryScanType", "InquiryScanInterval", "InquiryScanWindow", "LinkSupervisionTimeout", "PageTimeout", "MinSniffInterval", "MaxSniffInterval", NULL }; static const char *le_options[] = { "CentralAddressResolution", "MinAdvertisementInterval", "MaxAdvertisementInterval", "MultiAdvertisementRotationInterval", "ScanIntervalAutoConnect", "ScanWindowAutoConnect", "ScanIntervalSuspend", "ScanWindowSuspend", "ScanIntervalDiscovery", "ScanWindowDiscovery", "ScanIntervalAdvMonitoring", "ScanWindowAdvMonitoring", "ScanIntervalConnect", "ScanWindowConnect", "MinConnectionInterval", "MaxConnectionInterval", "ConnectionLatency", "ConnectionSupervisionTimeout", "Autoconnecttimeout", "AdvMonAllowlistScanDuration", "AdvMonNoFilterScanDuration", "EnableAdvMonInterleaveScan", NULL }; static const char *policy_options[] = { "ReconnectUUIDs", "ReconnectAttempts", "ReconnectIntervals", "AutoEnable", "ResumeDelay", NULL }; static const char *gatt_options[] = { "Cache", "KeySize", "ExchangeMTU", "Channels", "Client", "ExportClaimedServices", NULL }; static const char *csip_options[] = { "SIRK", "Encryption", "Size", "Rank", NULL }; static const char *avdtp_options[] = { "SessionMode", "StreamMode", NULL }; static const char *avrcp_options[] = { "VolumeWithoutTarget", "VolumeCategory", NULL }; static const char *advmon_options[] = { "RSSISamplingPeriod", NULL }; static const struct group_table { const char *name; const char **options; } valid_groups[] = { { "General", supported_options }, { "BR", br_options }, { "LE", le_options }, { "Policy", policy_options }, { "GATT", gatt_options }, { "CSIS", csip_options }, { "AVDTP", avdtp_options }, { "AVRCP", avrcp_options }, { "AdvMon", advmon_options }, { } }; #ifndef MIN #define MIN(x, y) ((x) < (y) ? (x) : (y)) #endif static int8_t check_sirk_alpha_numeric(char *str) { int8_t val = 0; char *s = str; if (strlen(s) != 32) /* 32 Bytes of Alpha numeric string */ return 0; for ( ; *s; s++) { if (((*s >= '0') & (*s <= '9')) || ((*s >= 'a') && (*s <= 'z')) || ((*s >= 'A') && (*s <= 'Z'))) { val = 1; } else { val = 0; break; } } return val; } static size_t hex2bin(const char *hexstr, uint8_t *buf, size_t buflen) { size_t i, len; if (!hexstr) return 0; len = MIN((strlen(hexstr) / 2), buflen); memset(buf, 0, len); for (i = 0; i < len; i++) { if (sscanf(hexstr + (i * 2), "%02hhX", &buf[i]) != 1) continue; } return len; } GKeyFile *btd_get_main_conf(void) { return main_conf; } static GKeyFile *load_config(const char *name) { GError *err = NULL; GKeyFile *keyfile; int len; if (name) snprintf(main_conf_file_path, PATH_MAX, "%s", name); else { const char *configdir = getenv("CONFIGURATION_DIRECTORY"); /* Check if running as service */ if (configdir) { /* Check if there multiple paths given */ if (strstr(configdir, ":")) len = strstr(configdir, ":") - configdir; else len = strlen(configdir); } else { configdir = CONFIGDIR; len = strlen(configdir); } snprintf(main_conf_file_path, PATH_MAX, "%*s/main.conf", len, configdir); } keyfile = g_key_file_new(); g_key_file_set_list_separator(keyfile, ','); if (!g_key_file_load_from_file(keyfile, main_conf_file_path, 0, &err)) { if (!g_error_matches(err, G_FILE_ERROR, G_FILE_ERROR_NOENT)) error("Parsing %s failed: %s", main_conf_file_path, err->message); g_error_free(err); g_key_file_free(keyfile); return NULL; } return keyfile; } static void parse_did(const char *did) { int result; uint16_t vendor, product, version , source; vendor = 0x0000; product = 0x0000; version = 0x0000; source = 0x0002; if (!strcasecmp(did, "false")) { source = 0x0000; goto done; } result = sscanf(did, "bluetooth:%4hx:%4hx:%4hx", &vendor, &product, &version); if (result != EOF && result >= 2) { source = 0x0001; goto done; } result = sscanf(did, "usb:%4hx:%4hx:%4hx", &vendor, &product, &version); if (result != EOF && result >= 2) goto done; result = sscanf(did, "%4hx:%4hx:%4hx", &vendor, &product, &version); if (result == EOF || result < 2) return; done: btd_opts.did_source = source; btd_opts.did_vendor = vendor; btd_opts.did_product = product; btd_opts.did_version = version; } static bt_gatt_cache_t parse_gatt_cache_str(const char *cache) { if (!strcmp(cache, "always")) { return BT_GATT_CACHE_ALWAYS; } else if (!strcmp(cache, "yes")) { return BT_GATT_CACHE_YES; } else if (!strcmp(cache, "no")) { return BT_GATT_CACHE_NO; } else { DBG("Invalid value for KeepCache=%s", cache); return BT_GATT_CACHE_ALWAYS; } } static enum jw_repairing_t parse_jw_repairing(const char *jw_repairing) { if (!strcmp(jw_repairing, "never")) { return JW_REPAIRING_NEVER; } else if (!strcmp(jw_repairing, "confirm")) { return JW_REPAIRING_CONFIRM; } else if (!strcmp(jw_repairing, "always")) { return JW_REPAIRING_ALWAYS; } else { return JW_REPAIRING_NEVER; } } static void check_options(GKeyFile *config, const char *group, const char **options) { char **keys; int i; keys = g_key_file_get_keys(config, group, NULL, NULL); for (i = 0; keys != NULL && keys[i] != NULL; i++) { bool found; unsigned int j; found = false; for (j = 0; options != NULL && options[j] != NULL; j++) { if (g_str_equal(keys[i], options[j])) { found = true; break; } } if (!found) warn("Unknown key %s for group %s in %s", keys[i], group, main_conf_file_path); } g_strfreev(keys); } static void check_config(GKeyFile *config) { char **keys; int i; const struct group_table *group; if (!config) return; keys = g_key_file_get_groups(config, NULL); for (i = 0; keys != NULL && keys[i] != NULL; i++) { bool match = false; for (group = valid_groups; group && group->name ; group++) { if (g_str_equal(keys[i], group->name)) { match = true; break; } } if (!match) warn("Unknown group %s in %s", keys[i], main_conf_file_path); } g_strfreev(keys); for (group = valid_groups; group && group->name; group++) check_options(config, group->name, group->options); } static int get_mode(const char *str) { if (strcmp(str, "dual") == 0) return BT_MODE_DUAL; else if (strcmp(str, "bredr") == 0) return BT_MODE_BREDR; else if (strcmp(str, "le") == 0) return BT_MODE_LE; error("Unknown controller mode \"%s\"", str); return BT_MODE_DUAL; } static bool parse_config_string(GKeyFile *config, const char *group, const char *key, char **val) { GError *err = NULL; char *tmp; tmp = g_key_file_get_string(config, group, key, &err); if (err) { if (err->code != G_KEY_FILE_ERROR_KEY_NOT_FOUND) DBG("%s", err->message); g_error_free(err); return false; } DBG("%s.%s = %s", group, key, tmp); if (val) { g_free(*val); *val = tmp; } return true; } static bool parse_config_int(GKeyFile *config, const char *group, const char *key, int *val, size_t min, size_t max) { size_t tmp; char *str = NULL; char *endptr = NULL; if (!parse_config_string(config, group, key, &str)) return false; tmp = strtol(str, &endptr, 0); if (!endptr || *endptr != '\0') { error("%s.%s = %s is not integer", group, key, str); g_free(str); return false; } if (tmp < min) { g_free(str); warn("%s.%s = %zu is out of range (< %zu)", group, key, tmp, min); return false; } if (tmp > max) { g_free(str); warn("%s.%s = %zu is out of range (> %zu)", group, key, tmp, max); return false; } g_free(str); if (val) *val = tmp; return true; } struct config_param { const char * const val_name; void * const val; const size_t size; const uint16_t min; const uint16_t max; }; static void parse_mode_config(GKeyFile *config, const char *group, const struct config_param *params, size_t params_len) { uint16_t i; if (!config) return; for (i = 0; i < params_len; ++i) { int val; if (parse_config_int(config, group, params[i].val_name, &val, params[i].min, params[i].max)) { val = htobl(val); memcpy(params[i].val, &val, params[i].size); } ++btd_opts.defaults.num_entries; } } static void parse_br_config(GKeyFile *config) { static const struct config_param params[] = { { "PageScanType", &btd_opts.defaults.br.page_scan_type, sizeof(btd_opts.defaults.br.page_scan_type), 0, 1}, { "PageScanInterval", &btd_opts.defaults.br.page_scan_interval, sizeof(btd_opts.defaults.br.page_scan_interval), 0x0012, 0x1000}, { "PageScanWindow", &btd_opts.defaults.br.page_scan_win, sizeof(btd_opts.defaults.br.page_scan_win), 0x0011, 0x1000}, { "InquiryScanType", &btd_opts.defaults.br.scan_type, sizeof(btd_opts.defaults.br.scan_type), 0, 1}, { "InquiryScanInterval", &btd_opts.defaults.br.scan_interval, sizeof(btd_opts.defaults.br.scan_interval), 0x0012, 0x1000}, { "InquiryScanWindow", &btd_opts.defaults.br.scan_win, sizeof(btd_opts.defaults.br.scan_win), 0x0011, 0x1000}, { "LinkSupervisionTimeout", &btd_opts.defaults.br.link_supervision_timeout, sizeof(btd_opts.defaults.br.link_supervision_timeout), 0x0001, 0xFFFF}, { "PageTimeout", &btd_opts.defaults.br.page_timeout, sizeof(btd_opts.defaults.br.page_timeout), 0x0001, 0xFFFF}, { "MinSniffInterval", &btd_opts.defaults.br.min_sniff_interval, sizeof(btd_opts.defaults.br.min_sniff_interval), 0x0001, 0xFFFE}, { "MaxSniffInterval", &btd_opts.defaults.br.max_sniff_interval, sizeof(btd_opts.defaults.br.max_sniff_interval), 0x0001, 0xFFFE}, }; if (btd_opts.mode == BT_MODE_LE) return; parse_mode_config(config, "BR", params, ARRAY_SIZE(params)); } static void parse_le_config(GKeyFile *config) { static const struct config_param params[] = { { "CentralAddressResolution", &btd_opts.defaults.le.addr_resolution, sizeof(btd_opts.defaults.le.addr_resolution), 0, 1}, { "MinAdvertisementInterval", &btd_opts.defaults.le.min_adv_interval, sizeof(btd_opts.defaults.le.min_adv_interval), 0x0020, 0x4000}, { "MaxAdvertisementInterval", &btd_opts.defaults.le.max_adv_interval, sizeof(btd_opts.defaults.le.max_adv_interval), 0x0020, 0x4000}, { "MultiAdvertisementRotationInterval", &btd_opts.defaults.le.adv_rotation_interval, sizeof(btd_opts.defaults.le.adv_rotation_interval), 0x0001, 0xFFFF}, { "ScanIntervalAutoConnect", &btd_opts.defaults.le.scan_interval_autoconnect, sizeof(btd_opts.defaults.le.scan_interval_autoconnect), 0x0004, 0x4000}, { "ScanWindowAutoConnect", &btd_opts.defaults.le.scan_win_autoconnect, sizeof(btd_opts.defaults.le.scan_win_autoconnect), 0x0004, 0x4000}, { "ScanIntervalSuspend", &btd_opts.defaults.le.scan_interval_suspend, sizeof(btd_opts.defaults.le.scan_interval_suspend), 0x0004, 0x4000}, { "ScanWindowSuspend", &btd_opts.defaults.le.scan_win_suspend, sizeof(btd_opts.defaults.le.scan_win_suspend), 0x0004, 0x4000}, { "ScanIntervalDiscovery", &btd_opts.defaults.le.scan_interval_discovery, sizeof(btd_opts.defaults.le.scan_interval_discovery), 0x0004, 0x4000}, { "ScanWindowDiscovery", &btd_opts.defaults.le.scan_win_discovery, sizeof(btd_opts.defaults.le.scan_win_discovery), 0x0004, 0x4000}, { "ScanIntervalAdvMonitor", &btd_opts.defaults.le.scan_interval_adv_monitor, sizeof(btd_opts.defaults.le.scan_interval_adv_monitor), 0x0004, 0x4000}, { "ScanWindowAdvMonitor", &btd_opts.defaults.le.scan_win_adv_monitor, sizeof(btd_opts.defaults.le.scan_win_adv_monitor), 0x0004, 0x4000}, { "ScanIntervalConnect", &btd_opts.defaults.le.scan_interval_connect, sizeof(btd_opts.defaults.le.scan_interval_connect), 0x0004, 0x4000}, { "ScanWindowConnect", &btd_opts.defaults.le.scan_win_connect, sizeof(btd_opts.defaults.le.scan_win_connect), 0x0004, 0x4000}, { "MinConnectionInterval", &btd_opts.defaults.le.min_conn_interval, sizeof(btd_opts.defaults.le.min_conn_interval), 0x0006, 0x0C80}, { "MaxConnectionInterval", &btd_opts.defaults.le.max_conn_interval, sizeof(btd_opts.defaults.le.max_conn_interval), 0x0006, 0x0C80}, { "ConnectionLatency", &btd_opts.defaults.le.conn_latency, sizeof(btd_opts.defaults.le.conn_latency), 0x0000, 0x01F3}, { "ConnectionSupervisionTimeout", &btd_opts.defaults.le.conn_lsto, sizeof(btd_opts.defaults.le.conn_lsto), 0x000A, 0x0C80}, { "Autoconnecttimeout", &btd_opts.defaults.le.autoconnect_timeout, sizeof(btd_opts.defaults.le.autoconnect_timeout), 0x0001, 0x4000}, { "AdvMonAllowlistScanDuration", &btd_opts.defaults.le.advmon_allowlist_scan_duration, sizeof(btd_opts.defaults.le.advmon_allowlist_scan_duration), 1, 10000}, { "AdvMonNoFilterScanDuration", &btd_opts.defaults.le.advmon_no_filter_scan_duration, sizeof(btd_opts.defaults.le.advmon_no_filter_scan_duration), 1, 10000}, { "EnableAdvMonInterleaveScan", &btd_opts.defaults.le.enable_advmon_interleave_scan, sizeof(btd_opts.defaults.le.enable_advmon_interleave_scan), 0, 1}, }; if (btd_opts.mode == BT_MODE_BREDR) return; parse_mode_config(config, "LE", params, ARRAY_SIZE(params)); } static bool match_experimental(const void *data, const void *match_data) { const char *value = data; const char *uuid = match_data; if (!strcmp(value, "*")) return true; return !strcasecmp(value, uuid); } bool btd_kernel_experimental_enabled(const char *uuid) { if (!btd_opts.kernel) return false; if (queue_find(btd_opts.kernel, match_experimental, uuid)) return true; return false; } static const char *valid_uuids[] = { "d4992530-b9ec-469f-ab01-6c481c47da1c", "671b10b5-42c0-4696-9227-eb28d1b049d6", "15c0a148-c273-11ea-b3de-0242ac130004", "330859bc-7506-492d-9370-9a6f0614037f", "a6695ace-ee7f-4fb9-881a-5fac66c629af", "6fbaf188-05e0-496a-9885-d6ddfdb4e03e", "*" }; static void btd_parse_kernel_experimental(char **list) { int i; if (btd_opts.kernel) { warn("Unable to parse KernelExperimental: list already set"); return; } btd_opts.kernel = queue_new(); for (i = 0; list[i]; i++) { size_t j; const char *uuid = list[i]; if (!strcasecmp("false", uuid) || !strcasecmp("off", uuid)) { queue_destroy(btd_opts.kernel, free); btd_opts.kernel = NULL; } if (!strcasecmp("true", uuid) || !strcasecmp("on", uuid)) uuid = "*"; for (j = 0; j < ARRAY_SIZE(valid_uuids); j++) { if (!strcasecmp(valid_uuids[j], uuid)) break; } /* Ignored if UUID is considered invalid */ if (j == ARRAY_SIZE(valid_uuids)) { warn("Invalid KernelExperimental UUID: %s", uuid); continue; } DBG("%s", uuid); queue_push_tail(btd_opts.kernel, strdup(uuid)); } } static bool gen_sirk(const char *str) { struct bt_crypto *crypto; int ret; crypto = bt_crypto_new(); if (!crypto) { error("Failed to open crypto"); return false; } ret = bt_crypto_sirk(crypto, str, btd_opts.did_vendor, btd_opts.did_product, btd_opts.did_version, btd_opts.did_source, btd_opts.csis.sirk); if (!ret) error("Failed to generate SIRK"); bt_crypto_unref(crypto); return ret; } static bool parse_config_u32(GKeyFile *config, const char *group, const char *key, uint32_t *val, uint32_t min, uint32_t max) { int tmp; if (!parse_config_int(config, group, key, &tmp, min, max)) return false; if (val) *val = tmp; return true; } static bool parse_config_u16(GKeyFile *config, const char *group, const char *key, uint16_t *val, uint16_t min, uint16_t max) { int tmp; if (!parse_config_int(config, group, key, &tmp, min, max)) return false; if (val) *val = tmp; return true; } static bool parse_config_u8(GKeyFile *config, const char *group, const char *key, uint8_t *val, uint8_t min, uint8_t max) { int tmp; if (!parse_config_int(config, group, key, &tmp, min, max)) return false; if (val) *val = tmp; return true; } static bool parse_config_bool(GKeyFile *config, const char *group, const char *key, bool *val) { GError *err = NULL; gboolean tmp; tmp = g_key_file_get_boolean(config, group, key, &err); if (err) { if (err->code != G_KEY_FILE_ERROR_KEY_NOT_FOUND) DBG("%s", err->message); g_error_free(err); return false; } DBG("%s.%s = %s", group, key, tmp ? "true" : "false"); if (val) *val = tmp; return true; } static void parse_privacy(GKeyFile *config) { char *str = NULL; if (!parse_config_string(config, "General", "Privacy", &str)) { btd_opts.privacy = 0x00; btd_opts.device_privacy = true; return; } if (!strcmp(str, "network") || !strcmp(str, "on")) { btd_opts.privacy = 0x01; } else if (!strcmp(str, "device")) { btd_opts.privacy = 0x01; btd_opts.device_privacy = true; } else if (!strcmp(str, "limited-network")) { if (btd_opts.mode != BT_MODE_DUAL) { DBG("Invalid privacy option: %s", str); btd_opts.privacy = 0x00; } btd_opts.privacy = 0x01; } else if (!strcmp(str, "limited-device")) { if (btd_opts.mode != BT_MODE_DUAL) { DBG("Invalid privacy option: %s", str); btd_opts.privacy = 0x00; } btd_opts.privacy = 0x02; btd_opts.device_privacy = true; } else if (!strcmp(str, "off")) { btd_opts.privacy = 0x00; btd_opts.device_privacy = true; } else { DBG("Invalid privacy option: %s", str); btd_opts.privacy = 0x00; } g_free(str); } static void parse_repairing(GKeyFile *config) { char *str = NULL; if (!parse_config_string(config, "General", "JustWorksRepairing", &str)) { btd_opts.jw_repairing = JW_REPAIRING_NEVER; return; } btd_opts.jw_repairing = parse_jw_repairing(str); g_free(str); } static bool parse_config_hex(GKeyFile *config, char *group, const char *key, uint32_t *val) { char *str = NULL; if (!parse_config_string(config, group, key, &str)) return false; if (val) *val = strtol(str, NULL, 16); g_free(str); return true; } static void parse_device_id(GKeyFile *config) { char *str = NULL; parse_config_string(config, "General", "DeviceID", &str); if (!str) return; parse_did(str); g_free(str); } static void parse_ctrl_mode(GKeyFile *config) { char *str = NULL; parse_config_string(config, "General", "ControllerMode", &str); if (!str) return; btd_opts.mode = get_mode(str); g_free(str); } static void parse_multi_profile(GKeyFile *config) { char *str = NULL; parse_config_string(config, "General", "MultiProfile", &str); if (!str) return; if (!strcmp(str, "single")) btd_opts.mps = MPS_SINGLE; else if (!strcmp(str, "multiple")) btd_opts.mps = MPS_MULTIPLE; else btd_opts.mps = MPS_OFF; g_free(str); } static gboolean parse_kernel_experimental(const char *key, const char *value, gpointer user_data, GError **error) { char **strlist; if (value && value[0] != '*') { strlist = g_strsplit(value, ",", -1); btd_parse_kernel_experimental(strlist); g_strfreev(strlist); } else { if (!btd_opts.kernel) btd_opts.kernel = queue_new(); queue_push_head(btd_opts.kernel, strdup("*")); } return TRUE; } static void parse_kernel_exp(GKeyFile *config) { char *str = NULL; if (!parse_config_string(config, "General", "KernelExperimental", &str)) return; parse_kernel_experimental(NULL, str, NULL, NULL); g_free(str); } static void parse_secure_conns(GKeyFile *config) { char *str = NULL; if (!parse_config_string(config, "General", "SecureConnections", &str)) return; if (!strcmp(str, "off")) btd_opts.secure_conn = SC_OFF; else if (!strcmp(str, "on")) btd_opts.secure_conn = SC_ON; else if (!strcmp(str, "only")) btd_opts.secure_conn = SC_ONLY; g_free(str); } static void parse_general(GKeyFile *config) { parse_config_string(config, "General", "Name", &btd_opts.name); parse_config_hex(config, "General", "Class", &btd_opts.class); parse_config_u32(config, "General", "DiscoverableTimeout", &btd_opts.discovto, 0, UINT32_MAX); parse_config_bool(config, "General", "AlwaysPairable", &btd_opts.pairable); parse_config_u32(config, "General", "PairableTimeout", &btd_opts.pairto, 0, UINT32_MAX); parse_device_id(config); parse_config_bool(config, "General", "ReverseServiceDiscovery", &btd_opts.reverse_discovery); parse_config_bool(config, "General", "NameResolving", &btd_opts.name_resolv); parse_config_bool(config, "General", "DebugKeys", &btd_opts.debug_keys); parse_ctrl_mode(config); parse_config_u16(config, "General", "MaxControllers", &btd_opts.max_adapters, 0, UINT16_MAX); parse_multi_profile(config); parse_config_bool(config, "General", "FastConnectable", &btd_opts.fast_conn); parse_privacy(config); parse_repairing(config); parse_config_u32(config, "General", "TemporaryTimeout", &btd_opts.tmpto, 0, UINT32_MAX); parse_config_bool(config, "General", "RefreshDiscovery", &btd_opts.refresh_discovery); parse_secure_conns(config); parse_config_bool(config, "General", "Experimental", &btd_opts.experimental); parse_config_bool(config, "General", "Testing", &btd_opts.testing); parse_kernel_exp(config); parse_config_u32(config, "General", "RemoteNameRequestRetryDelay", &btd_opts.name_request_retry_delay, 0, UINT32_MAX); } static void parse_gatt_cache(GKeyFile *config) { char *str = NULL; parse_config_string(config, "GATT", "Cache", &str); if (!str) return; btd_opts.gatt_cache = parse_gatt_cache_str(str); g_free(str); } static enum bt_gatt_export_t parse_gatt_export_str(const char *str) { if (!strcmp(str, "no") || !strcmp(str, "false") || !strcmp(str, "off")) { return BT_GATT_EXPORT_OFF; } else if (!strcmp(str, "read-only")) { return BT_GATT_EXPORT_READ_ONLY; } else if (!strcmp(str, "read-write")) { return BT_GATT_EXPORT_READ_WRITE; } DBG("Invalid value for ExportClaimedServices=%s", str); return BT_GATT_EXPORT_READ_ONLY; } static void parse_gatt_export(GKeyFile *config) { char *str = NULL; parse_config_string(config, "GATT", "ExportClaimedServices", &str); if (!str) return; btd_opts.gatt_export = parse_gatt_export_str(str); g_free(str); } static void parse_gatt(GKeyFile *config) { parse_gatt_cache(config); parse_config_u8(config, "GATT", "KeySize", &btd_opts.key_size, 7, 16); parse_config_u16(config, "GATT", "ExchangeMTU", &btd_opts.gatt_mtu, BT_ATT_DEFAULT_LE_MTU, BT_ATT_MAX_LE_MTU); parse_config_u8(config, "GATT", "Channels", &btd_opts.gatt_channels, 1, 6); parse_config_bool(config, "GATT", "Client", &btd_opts.gatt_client); parse_gatt_export(config); } static void parse_csis_sirk(GKeyFile *config) { char *str = NULL; if (!parse_config_string(config, "CSIS", "SIRK", &str)) return; if (strlen(str) == 32 && check_sirk_alpha_numeric(str)) hex2bin(str, btd_opts.csis.sirk, sizeof(btd_opts.csis.sirk)); else if (!gen_sirk(str)) DBG("Unable to generate SIRK from string"); g_free(str); } static void parse_csis(GKeyFile *config) { parse_csis_sirk(config); parse_config_bool(config, "CSIS", "Encryption", &btd_opts.csis.encrypt); parse_config_u8(config, "CSIS", "Size", &btd_opts.csis.size, 0, UINT8_MAX); parse_config_u8(config, "CSIS", "Rank", &btd_opts.csis.rank, 0, UINT8_MAX); } static void parse_avdtp_session_mode(GKeyFile *config) { char *str = NULL; if (!parse_config_string(config, "AVDTP", "SessionMode", &str)) return; if (!strcmp(str, "basic")) btd_opts.avdtp.session_mode = BT_IO_MODE_BASIC; else if (!strcmp(str, "ertm")) btd_opts.avdtp.session_mode = BT_IO_MODE_ERTM; else { DBG("Invalid mode option: %s", str); btd_opts.avdtp.session_mode = BT_IO_MODE_BASIC; } g_free(str); } static void parse_avdtp_stream_mode(GKeyFile *config) { char *str = NULL; if (!parse_config_string(config, "AVDTP", "StreamMode", &str)) return; if (!strcmp(str, "basic")) btd_opts.avdtp.stream_mode = BT_IO_MODE_BASIC; else if (!strcmp(str, "streaming")) btd_opts.avdtp.stream_mode = BT_IO_MODE_STREAMING; else { DBG("Invalid mode option: %s", str); btd_opts.avdtp.stream_mode = BT_IO_MODE_BASIC; } g_free(str); } static void parse_avdtp(GKeyFile *config) { parse_avdtp_session_mode(config); parse_avdtp_stream_mode(config); } static void parse_avrcp(GKeyFile *config) { parse_config_bool(config, "AVRCP", "VolumeWithoutTarget", &btd_opts.avrcp.volume_without_target); parse_config_bool(config, "AVRCP", "VolumeCategory", &btd_opts.avrcp.volume_category); } static void parse_advmon(GKeyFile *config) { parse_config_u8(config, "AdvMon", "RSSISamplingPeriod", &btd_opts.advmon.rssi_sampling_period, 0, UINT8_MAX); } static void parse_config(GKeyFile *config) { if (!config) return; check_config(config); DBG("parsing %s", main_conf_file_path); /* Parse Groups */ parse_general(config); parse_br_config(config); parse_le_config(config); parse_gatt(config); parse_csis(config); parse_avdtp(config); parse_avrcp(config); parse_advmon(config); } static void init_defaults(void) { uint8_t major, minor; /* Default HCId settings */ memset(&btd_opts, 0, sizeof(btd_opts)); btd_opts.name = g_strdup_printf("BlueZ %s", VERSION); btd_opts.class = 0x000000; btd_opts.pairto = DEFAULT_PAIRABLE_TIMEOUT; btd_opts.discovto = DEFAULT_DISCOVERABLE_TIMEOUT; btd_opts.tmpto = DEFAULT_TEMPORARY_TIMEOUT; btd_opts.reverse_discovery = TRUE; btd_opts.name_resolv = TRUE; btd_opts.debug_keys = FALSE; btd_opts.refresh_discovery = TRUE; btd_opts.name_request_retry_delay = DEFAULT_NAME_REQUEST_RETRY_DELAY; btd_opts.secure_conn = SC_ON; btd_opts.defaults.num_entries = 0; btd_opts.defaults.br.page_scan_type = 0xFFFF; btd_opts.defaults.br.scan_type = 0xFFFF; btd_opts.defaults.le.addr_resolution = 0x01; btd_opts.defaults.le.enable_advmon_interleave_scan = 0xFF; if (sscanf(VERSION, "%hhu.%hhu", &major, &minor) != 2) return; btd_opts.did_source = 0x0002; /* USB */ btd_opts.did_vendor = 0x1d6b; /* Linux Foundation */ btd_opts.did_product = 0x0246; /* BlueZ */ btd_opts.did_version = (major << 8 | minor); btd_opts.gatt_cache = BT_GATT_CACHE_ALWAYS; btd_opts.gatt_mtu = BT_ATT_MAX_LE_MTU; btd_opts.gatt_channels = 1; btd_opts.gatt_client = true; btd_opts.gatt_export = BT_GATT_EXPORT_READ_ONLY; btd_opts.avdtp.session_mode = BT_IO_MODE_BASIC; btd_opts.avdtp.stream_mode = BT_IO_MODE_BASIC; btd_opts.avrcp.volume_without_target = false; btd_opts.avrcp.volume_category = true; btd_opts.advmon.rssi_sampling_period = 0xFF; btd_opts.csis.encrypt = true; } static void log_handler(const gchar *log_domain, GLogLevelFlags log_level, const gchar *message, gpointer user_data) { int priority; if (log_level & (G_LOG_LEVEL_ERROR | G_LOG_LEVEL_CRITICAL | G_LOG_LEVEL_WARNING)) priority = 0x03; else priority = 0x06; btd_log(0xffff, priority, "GLib: %s", message); btd_backtrace(0xffff); } void btd_exit(void) { mainloop_quit(); } static bool quit_eventloop(gpointer user_data) { btd_exit(); return FALSE; } static void signal_callback(int signum, void *user_data) { static bool terminated = false; switch (signum) { case SIGINT: case SIGTERM: if (!terminated) { info("Terminating"); timeout_add_seconds(SHUTDOWN_GRACE_SECONDS, quit_eventloop, NULL, NULL); mainloop_sd_notify("STATUS=Powering down"); adapter_shutdown(); } terminated = true; break; case SIGUSR2: __btd_toggle_debug(); break; } } static char *option_debug = NULL; static char *option_plugin = NULL; static char *option_noplugin = NULL; static char *option_configfile = NULL; static gboolean option_compat = FALSE; static gboolean option_detach = TRUE; static gboolean option_version = FALSE; static void free_options(void) { g_free(option_debug); option_debug = NULL; g_free(option_plugin); option_plugin = NULL; g_free(option_noplugin); option_noplugin = NULL; g_free(option_configfile); option_configfile = NULL; } static void disconnect_dbus(void) { DBusConnection *conn = btd_get_dbus_connection(); if (!conn || !dbus_connection_get_is_connected(conn)) return; g_dbus_detach_object_manager(conn); set_dbus_connection(NULL); dbus_connection_unref(conn); } static void disconnected_dbus(DBusConnection *conn, void *data) { info("Disconnected from D-Bus. Exiting."); mainloop_quit(); } static void dbus_debug(const char *str, void *data) { DBG_IDX(0xffff, "%s", str); } static int connect_dbus(void) { DBusConnection *conn; DBusError err; dbus_error_init(&err); conn = g_dbus_setup_bus(DBUS_BUS_SYSTEM, BLUEZ_NAME, &err); if (!conn) { if (dbus_error_is_set(&err)) { g_printerr("D-Bus setup failed: %s\n", err.message); dbus_error_free(&err); return -EIO; } return -EALREADY; } set_dbus_connection(conn); g_dbus_set_disconnect_function(conn, disconnected_dbus, NULL, NULL); g_dbus_attach_object_manager(conn); g_dbus_set_debug(dbus_debug, NULL, NULL); return 0; } static gboolean parse_debug(const char *key, const char *value, gpointer user_data, GError **error) { if (value) option_debug = g_strdup(value); else option_debug = g_strdup("*"); return TRUE; } static GOptionEntry options[] = { { "debug", 'd', G_OPTION_FLAG_OPTIONAL_ARG, G_OPTION_ARG_CALLBACK, parse_debug, "Specify debug options to enable", "DEBUG" }, { "plugin", 'p', 0, G_OPTION_ARG_STRING, &option_plugin, "Specify plugins to load", "NAME,..," }, { "noplugin", 'P', 0, G_OPTION_ARG_STRING, &option_noplugin, "Specify plugins not to load", "NAME,..." }, { "configfile", 'f', 0, G_OPTION_ARG_STRING, &option_configfile, "Specify an explicit path to the config file", "FILE"}, { "compat", 'C', 0, G_OPTION_ARG_NONE, &option_compat, "Provide deprecated command line interfaces" }, { "experimental", 'E', 0, G_OPTION_ARG_NONE, &btd_opts.experimental, "Enable experimental D-Bus interfaces" }, { "testing", 'T', 0, G_OPTION_ARG_NONE, &btd_opts.testing, "Enable testing D-Bus interfaces" }, { "kernel", 'K', G_OPTION_FLAG_OPTIONAL_ARG, G_OPTION_ARG_CALLBACK, parse_kernel_experimental, "Enable kernel experimental features" }, { "nodetach", 'n', G_OPTION_FLAG_REVERSE, G_OPTION_ARG_NONE, &option_detach, "Run with logging in foreground" }, { "version", 'v', 0, G_OPTION_ARG_NONE, &option_version, "Show version information and exit" }, { NULL }, }; int main(int argc, char *argv[]) { GOptionContext *context; GError *err = NULL; uint16_t sdp_mtu = 0; uint32_t sdp_flags = 0; int gdbus_flags = 0; init_defaults(); context = g_option_context_new(NULL); g_option_context_add_main_entries(context, options, NULL); if (g_option_context_parse(context, &argc, &argv, &err) == FALSE) { if (err != NULL) { g_printerr("%s\n", err->message); g_error_free(err); } else g_printerr("An unknown error occurred\n"); exit(1); } g_option_context_free(context); if (option_version == TRUE) { printf("%s\n", VERSION); exit(0); } umask(0077); btd_backtrace_init(); mainloop_init(); __btd_log_init(option_debug, option_detach); g_log_set_handler("GLib", G_LOG_LEVEL_MASK | G_LOG_FLAG_FATAL | G_LOG_FLAG_RECURSION, log_handler, NULL); mainloop_sd_notify("STATUS=Starting up"); main_conf = load_config(option_configfile); parse_config(main_conf); if (connect_dbus() < 0) { error("Unable to get on D-Bus"); exit(1); } if (btd_opts.experimental) gdbus_flags = G_DBUS_FLAG_ENABLE_EXPERIMENTAL; if (btd_opts.testing) gdbus_flags |= G_DBUS_FLAG_ENABLE_TESTING; g_dbus_set_flags(gdbus_flags); if (adapter_init() < 0) { error("Adapter handling initialization failed"); exit(1); } btd_device_init(); btd_agent_init(); btd_profile_init(); if (btd_opts.mode != BT_MODE_LE) { if (option_compat == TRUE) sdp_flags |= SDP_SERVER_COMPAT; start_sdp_server(sdp_mtu, sdp_flags); if (btd_opts.did_source > 0) register_device_id(btd_opts.did_source, btd_opts.did_vendor, btd_opts.did_product, btd_opts.did_version); } if (btd_opts.mps != MPS_OFF) register_mps(btd_opts.mps == MPS_MULTIPLE); /* Loading plugins has to be done after D-Bus has been setup since * the plugins might wanna expose some paths on the bus. However the * best order of how to init various subsystems of the Bluetooth * daemon needs to be re-worked. */ plugin_init(option_plugin, option_noplugin); /* no need to keep parsed option in memory */ free_options(); rfkill_init(); DBG("Entering main loop"); mainloop_sd_notify("STATUS=Running"); mainloop_sd_notify("READY=1"); mainloop_run_with_signal(signal_callback, NULL); mainloop_sd_notify("STATUS=Quitting"); plugin_cleanup(); btd_profile_cleanup(); btd_agent_cleanup(); btd_device_cleanup(); adapter_cleanup(); rfkill_exit(); if (btd_opts.mode != BT_MODE_LE) stop_sdp_server(); if (btd_opts.kernel) queue_destroy(btd_opts.kernel, free); if (main_conf) g_key_file_free(main_conf); disconnect_dbus(); info("Exit"); __btd_log_cleanup(); return 0; } bluez-5.82/src/PaxHeaders/sdpd-service.c0000644000000000000000000000005014015011623015162 xustar0020 atime=1743516612 20 ctime=1743591278 bluez-5.82/src/sdpd-service.c0000644000000000000000000007147114015011623014655 0ustar00rootroot// SPDX-License-Identifier: GPL-2.0-or-later /* * * BlueZ - Bluetooth protocol stack for Linux * * Copyright (C) 2001-2002 Nokia Corporation * Copyright (C) 2002-2003 Maxim Krasnyansky * Copyright (C) 2002-2010 Marcel Holtmann * Copyright (C) 2002-2003 Stephen Crane * * */ #ifdef HAVE_CONFIG_H #include #endif #include #include #include #include #include #include #include "lib/bluetooth.h" #include "lib/sdp.h" #include "lib/sdp_lib.h" #include "src/shared/util.h" #include "sdpd.h" #include "log.h" #define MPSD_HFP_AG_A2DP_SRC_ANSWER_CALL_DURING_AUDIO (1ULL << 0) #define MPSD_HFP_HF_A2DP_SNK_ANSWER_CALL_DURING_AUDIO (1ULL << 1) #define MPSD_HFP_AG_A2DP_SRC_OUTGOING_CALL_DURING_AUDIO (1ULL << 2) #define MPSD_HFP_HF_A2DP_SNK_OUTGOING_CALL_DURING_AUDIO (1ULL << 3) #define MPSD_HFP_AG_A2DP_SRC_REJECT_CALL_DURING_AUDIO (1ULL << 4) #define MPSD_HFP_HF_A2DP_SNK_SRC_REJECT_CALL_DURING_AUDIO (1ULL << 5) #define MPSD_HFP_AG_A2DP_SRC_TERMINATE_CALL_DURING_AVP (1ULL << 6) #define MPSD_HFP_HF_A2DP_SNK_TERMINATE_CALL_DURING_AVP (1ULL << 7) #define MPSD_HFP_AG_A2DP_SRC_PRESS_PLAY_DURING_ACTIVE_CALL (1ULL << 8) #define MPSD_HFP_HF_A2DP_SNK_PRESS_PLAY_DURING_ACTIVE_CALL (1ULL << 9) #define MPSD_HFP_AG_A2DP_SRC_START_AUDIO_STREAM_AFTER_PLAY (1ULL << 10) #define MPSD_HFP_HF_A2DP_SNK_START_AUDIO_STREAM_AFTER_PLAY (1ULL << 11) #define MPSD_HFP_AG_A2DP_SRC_SUSPEND_AUDIO_STREAM_ON_PAUSE (1ULL << 12) #define MPSD_HFP_HF_A2DP_SNK_SUSPEND_AUDIO_STREAM_ON_PAUSE (1ULL << 13) #define MPSD_HFP_AG_DUN_GW_DATA_COMM_DURING_VOICE_CALL (1ULL << 14) #define MPSD_HFP_HF_DUN_DT_DATA_COMM_DURING_VOICE_CALL (1ULL << 15) #define MPSD_HFP_AG_DUN_GW_OUTGOING_CALL_DURING_DATA_COMM (1ULL << 16) #define MPSD_HFP_HF_DUN_DT_OUTGOING_CALL_DURING_DATA_COMM (1ULL << 17) #define MPSD_HFP_AG_DUN_GW_INCOMING_CALL_DURING_DATA_COMM (1ULL << 18) #define MPSD_HFP_HF_DUN_DT_INCOMING_CALL_DURING_DATA_COMM (1ULL << 19) #define MPSD_A2DP_SRC_DUN_GW_START_AUDIO_DURING_DATA_COMM (1ULL << 20) #define MPSD_A2DP_SNK_DUN_DT_START_AUDIO_DURING_DATA_COMM (1ULL << 21) #define MPSD_A2DP_SRC_DUN_GW_DATA_COMM_DURING_AUDIO_STREAM (1ULL << 22) #define MPSD_A2DP_SNK_DUN_DT_DATA_COMM_DURING_AUDIO_STREAM (1ULL << 23) #define MPSD_HFP_AG_DUN_GW_TERMINATE_CALL_DURING_DATA_COMM (1ULL << 24) #define MPSD_HFP_HF_DUN_DT_TERMINATE_CALL_DURING_DATA_COMM (1ULL << 25) #define MPSD_HFP_AG_PAN_NAP_DATA_COMM_DURING_VOICE_CALL (1ULL << 26) #define MPSD_HFP_HF_PAN_PANU_DATA_COMM_DURING_VOICE_CALL (1ULL << 27) #define MPSD_HFP_AG_PAN_NAP_OUTGOING_CALL_DURING_DATA_COMM (1ULL << 28) #define MPSD_HFP_HF_PAN_PANU_OUTGOING_CALL_DURING_DATA_COMM (1ULL << 29) #define MPSD_HFP_AG_PAN_NAP_INCOMING_CALL_DURING_DATA_COMM (1ULL << 30) #define MPSD_HFP_HF_PAN_PANU_INCOMING_CALL_DURING_DATA_COMM (1ULL << 31) #define MPSD_A2DP_SRC_PAN_NAP_START_AUDIO_DURING_DATA_COMM (1ULL << 32) #define MPSD_A2DP_SNK_PAN_PANU_START_AUDIO_DURING_DATA_COMM (1ULL << 33) #define MPSD_A2DP_SRC_PAN_NAP_DATA_COMM_DURING_AUDIO_STREAM (1ULL << 34) #define MPSD_A2DP_SNK_PAN_PANU_DATA_COMM_DURING_AUDIO_STREAM (1ULL << 35) #define MPSD_A2DP_SRC_PBAP_SRV_PB_DL_DURING_AUDIO_STREAM (1ULL << 36) #define MPSD_A2DP_SNK_PBAP_CLI_PB_DL_DURING_AUDIO_STREAM (1ULL << 37) #define MPMD_HFP_HF_A2DP_SNK_AVRCP_CT_ANSWER_CALL_DURING_AUDIO (1ULL << 0) #define MPMD_A2DP_SRC_AVRCP_TG_ANSWER_CALL_DURING_AUDIO (1ULL << 1) #define MPMD_HFP_HF_A2DP_SNK_AVRCP_CT_OUTGOING_CALL_DURING_AUDIO (1ULL << 2) #define MPMD_A2DP_SRC_AVRCP_TG_OUTGOING_CALL_DURING_AUDIO (1ULL << 3) #define MPMD_HFP_HF_A2DP_SNK_AVRCP_CT_REJECT_CALL_DURING_AUDIO (1ULL << 4) #define MPMD_A2DP_SRC_AVRCP_TG_REJECT_CALL_DURING_AUDIO (1ULL << 5) #define MPMD_HFP_AG_CALL_TERMINATION_DURING_AVP (1ULL << 6) #define MPMD_HFP_HF_A2DP_SNK_AVRCP_CT_TERMINATION_DURING_AVP (1ULL << 7) #define MPMD_A2DP_SRC_AVRCP_TG_TERMINATION_DURING_AVP (1ULL << 8) #define MPMD_HFP_HF_A2DP_SNK_AVRCP_CT_PLAY_DURING_CALL (1ULL << 9) #define MPMD_A2DP_SRC_AVRCP_TG_PRESS_PLAY_DURING_CALL (1ULL << 10) #define MPMD_AVRCP_CT_NO_A2DP_SNK_START_AUDIO_AFTER_PLAY (1ULL << 11) #define MPMD_A2DP_SRC_AVRCP_TG_START_AUDIO_AFTER_PLAY (1ULL << 12) #define MPMD_AVRCP_CT_NO_A2DP_SNK_SUSPEND_AUDIO_AFTER_PAUSE (1ULL << 13) #define MPMD_A2DP_SRC_AVRCP_TG_SUSPEND_AUDIO_AFTER_PAUSE (1ULL << 14) #define MPMD_A2DP_SRC_AVRCP_TG_START_AUDIO_DURING_DATA_COMM (1ULL << 15) #define MPMD_A2DP_SNK_AVRCP_CT_DUN_DT_AUDIO_DURING_DATA_COMM (1ULL << 16) #define MPMD_A2DP_SRC_AVRCP_TG_START_DATA_DURING_AUDIO (1ULL << 17) #define MPMD_A2DP_SNK_AVRCP_CT_DUN_DT_START_DATA_DURING_AUDIO (1ULL << 18) /* Note: in spec dependency bit position starts from 1 (bit 0 unused?) */ #define MPS_DEPS_SNIFF_MODE_DURRING_STREAMING (1ULL << 1) #define MPS_DEPS_GAVDP_REQUIREMENTS (1ULL << 2) #define MPS_DEPS_DIS_CONNECTION_ORDER_BEHAVIOR (1ULL << 3) /* * default MPS features are all disabled, will be updated if relevant service * is (un)registered */ #define MPS_MPSD_DEFAULT_FEATURES 0 #define MPS_MPMD_DEFAULT_FEATURES 0 /* * Those defines bits for all features that depend on specific profile and role. * If profile is not supported then all those bits should not be set in record */ #define MPS_MPSD_HFP_AG (MPSD_HFP_AG_A2DP_SRC_ANSWER_CALL_DURING_AUDIO | \ MPSD_HFP_AG_A2DP_SRC_OUTGOING_CALL_DURING_AUDIO | \ MPSD_HFP_AG_A2DP_SRC_REJECT_CALL_DURING_AUDIO | \ MPSD_HFP_AG_A2DP_SRC_TERMINATE_CALL_DURING_AVP | \ MPSD_HFP_AG_A2DP_SRC_PRESS_PLAY_DURING_ACTIVE_CALL | \ MPSD_HFP_AG_A2DP_SRC_START_AUDIO_STREAM_AFTER_PLAY | \ MPSD_HFP_AG_DUN_GW_DATA_COMM_DURING_VOICE_CALL | \ MPSD_HFP_AG_A2DP_SRC_SUSPEND_AUDIO_STREAM_ON_PAUSE | \ MPSD_HFP_AG_DUN_GW_OUTGOING_CALL_DURING_DATA_COMM | \ MPSD_HFP_AG_DUN_GW_INCOMING_CALL_DURING_DATA_COMM | \ MPSD_HFP_AG_DUN_GW_TERMINATE_CALL_DURING_DATA_COMM | \ MPSD_HFP_AG_PAN_NAP_DATA_COMM_DURING_VOICE_CALL | \ MPSD_HFP_AG_PAN_NAP_OUTGOING_CALL_DURING_DATA_COMM | \ MPSD_HFP_AG_PAN_NAP_INCOMING_CALL_DURING_DATA_COMM) #define MPS_MPSD_HFP_HF (MPSD_HFP_HF_A2DP_SNK_ANSWER_CALL_DURING_AUDIO | \ MPSD_HFP_HF_A2DP_SNK_OUTGOING_CALL_DURING_AUDIO | \ MPSD_HFP_HF_A2DP_SNK_SRC_REJECT_CALL_DURING_AUDIO | \ MPSD_HFP_HF_A2DP_SNK_TERMINATE_CALL_DURING_AVP | \ MPSD_HFP_HF_A2DP_SNK_PRESS_PLAY_DURING_ACTIVE_CALL | \ MPSD_HFP_HF_A2DP_SNK_START_AUDIO_STREAM_AFTER_PLAY | \ MPSD_HFP_HF_A2DP_SNK_SUSPEND_AUDIO_STREAM_ON_PAUSE | \ MPSD_HFP_HF_DUN_DT_DATA_COMM_DURING_VOICE_CALL | \ MPSD_HFP_HF_DUN_DT_OUTGOING_CALL_DURING_DATA_COMM | \ MPSD_HFP_HF_DUN_DT_INCOMING_CALL_DURING_DATA_COMM | \ MPSD_HFP_HF_DUN_DT_TERMINATE_CALL_DURING_DATA_COMM | \ MPSD_HFP_HF_PAN_PANU_DATA_COMM_DURING_VOICE_CALL | \ MPSD_HFP_HF_PAN_PANU_OUTGOING_CALL_DURING_DATA_COMM | \ MPSD_HFP_HF_PAN_PANU_INCOMING_CALL_DURING_DATA_COMM) #define MPS_MPSD_A2DP_SRC (MPSD_HFP_AG_A2DP_SRC_ANSWER_CALL_DURING_AUDIO | \ MPSD_HFP_AG_A2DP_SRC_OUTGOING_CALL_DURING_AUDIO | \ MPSD_HFP_AG_A2DP_SRC_REJECT_CALL_DURING_AUDIO | \ MPSD_HFP_AG_A2DP_SRC_TERMINATE_CALL_DURING_AVP | \ MPSD_HFP_AG_A2DP_SRC_PRESS_PLAY_DURING_ACTIVE_CALL | \ MPSD_HFP_AG_A2DP_SRC_START_AUDIO_STREAM_AFTER_PLAY | \ MPSD_HFP_AG_A2DP_SRC_SUSPEND_AUDIO_STREAM_ON_PAUSE | \ MPSD_A2DP_SRC_DUN_GW_START_AUDIO_DURING_DATA_COMM | \ MPSD_A2DP_SRC_DUN_GW_DATA_COMM_DURING_AUDIO_STREAM | \ MPSD_A2DP_SRC_PAN_NAP_START_AUDIO_DURING_DATA_COMM | \ MPSD_A2DP_SRC_PAN_NAP_DATA_COMM_DURING_AUDIO_STREAM | \ MPSD_A2DP_SRC_PBAP_SRV_PB_DL_DURING_AUDIO_STREAM) #define MPS_MPSD_A2DP_SNK (MPSD_HFP_HF_A2DP_SNK_ANSWER_CALL_DURING_AUDIO | \ MPSD_HFP_HF_A2DP_SNK_OUTGOING_CALL_DURING_AUDIO | \ MPSD_HFP_HF_A2DP_SNK_SRC_REJECT_CALL_DURING_AUDIO | \ MPSD_HFP_HF_A2DP_SNK_TERMINATE_CALL_DURING_AVP | \ MPSD_HFP_HF_A2DP_SNK_PRESS_PLAY_DURING_ACTIVE_CALL | \ MPSD_HFP_HF_A2DP_SNK_START_AUDIO_STREAM_AFTER_PLAY | \ MPSD_HFP_HF_A2DP_SNK_SUSPEND_AUDIO_STREAM_ON_PAUSE | \ MPSD_A2DP_SNK_DUN_DT_START_AUDIO_DURING_DATA_COMM | \ MPSD_A2DP_SNK_DUN_DT_DATA_COMM_DURING_AUDIO_STREAM | \ MPSD_A2DP_SNK_PAN_PANU_START_AUDIO_DURING_DATA_COMM | \ MPSD_A2DP_SNK_PAN_PANU_DATA_COMM_DURING_AUDIO_STREAM | \ MPSD_A2DP_SNK_PBAP_CLI_PB_DL_DURING_AUDIO_STREAM) #define MPS_MPSD_AVRCP_CT MPS_MPSD_A2DP_SNK #define MPS_MPSD_AVRCP_TG MPS_MPSD_A2DP_SRC #define MPS_MPSD_DUN_GW (MPSD_HFP_AG_DUN_GW_DATA_COMM_DURING_VOICE_CALL | \ MPSD_HFP_AG_DUN_GW_OUTGOING_CALL_DURING_DATA_COMM | \ MPSD_HFP_AG_DUN_GW_INCOMING_CALL_DURING_DATA_COMM | \ MPSD_A2DP_SRC_DUN_GW_START_AUDIO_DURING_DATA_COMM | \ MPSD_A2DP_SRC_DUN_GW_DATA_COMM_DURING_AUDIO_STREAM | \ MPSD_HFP_AG_DUN_GW_TERMINATE_CALL_DURING_DATA_COMM) #define MPS_MPSD_DUN_DT (MPSD_HFP_HF_DUN_DT_DATA_COMM_DURING_VOICE_CALL | \ MPSD_HFP_HF_DUN_DT_OUTGOING_CALL_DURING_DATA_COMM | \ MPSD_HFP_HF_DUN_DT_INCOMING_CALL_DURING_DATA_COMM | \ MPSD_A2DP_SNK_DUN_DT_START_AUDIO_DURING_DATA_COMM | \ MPSD_A2DP_SNK_DUN_DT_DATA_COMM_DURING_AUDIO_STREAM | \ MPSD_HFP_HF_DUN_DT_TERMINATE_CALL_DURING_DATA_COMM) #define MPS_MPSD_PAN_NAP (MPSD_HFP_AG_PAN_NAP_DATA_COMM_DURING_VOICE_CALL | \ MPSD_HFP_AG_PAN_NAP_OUTGOING_CALL_DURING_DATA_COMM | \ MPSD_HFP_AG_PAN_NAP_INCOMING_CALL_DURING_DATA_COMM | \ MPSD_A2DP_SRC_PAN_NAP_START_AUDIO_DURING_DATA_COMM | \ MPSD_A2DP_SRC_PAN_NAP_DATA_COMM_DURING_AUDIO_STREAM) #define MPS_MPSD_PAN_PANU (MPSD_HFP_HF_PAN_PANU_DATA_COMM_DURING_VOICE_CALL | \ MPSD_HFP_HF_PAN_PANU_OUTGOING_CALL_DURING_DATA_COMM | \ MPSD_HFP_HF_PAN_PANU_INCOMING_CALL_DURING_DATA_COMM | \ MPSD_A2DP_SNK_PAN_PANU_START_AUDIO_DURING_DATA_COMM | \ MPSD_A2DP_SNK_PAN_PANU_DATA_COMM_DURING_AUDIO_STREAM) #define MPS_MPSD_PBAP_SRC MPSD_A2DP_SRC_PBAP_SRV_PB_DL_DURING_AUDIO_STREAM #define MPS_MPSD_PBAP_CLI MPSD_A2DP_SNK_PBAP_CLI_PB_DL_DURING_AUDIO_STREAM #define MPS_MPSD_ALL (MPS_MPSD_HFP_AG | MPS_MPSD_HFP_HF | \ MPS_MPSD_A2DP_SRC | MPS_MPSD_A2DP_SNK | \ MPS_MPSD_AVRCP_CT | MPS_MPSD_AVRCP_TG | \ MPS_MPSD_DUN_GW | MPS_MPSD_DUN_DT | \ MPS_MPSD_PAN_NAP | MPS_MPSD_PAN_PANU | \ MPS_MPSD_PBAP_SRC | MPS_MPSD_PBAP_CLI) #define MPS_MPMD_HFP_AG MPMD_HFP_AG_CALL_TERMINATION_DURING_AVP #define MPS_MPMD_HFP_HF ( \ MPMD_HFP_HF_A2DP_SNK_AVRCP_CT_ANSWER_CALL_DURING_AUDIO | \ MPMD_HFP_HF_A2DP_SNK_AVRCP_CT_OUTGOING_CALL_DURING_AUDIO | \ MPMD_HFP_HF_A2DP_SNK_AVRCP_CT_REJECT_CALL_DURING_AUDIO | \ MPMD_HFP_HF_A2DP_SNK_AVRCP_CT_TERMINATION_DURING_AVP | \ MPMD_HFP_HF_A2DP_SNK_AVRCP_CT_PLAY_DURING_CALL) #define MPS_MPMD_A2DP_SRC (MPMD_A2DP_SRC_AVRCP_TG_ANSWER_CALL_DURING_AUDIO | \ MPMD_A2DP_SRC_AVRCP_TG_OUTGOING_CALL_DURING_AUDIO | \ MPMD_A2DP_SRC_AVRCP_TG_REJECT_CALL_DURING_AUDIO | \ MPMD_A2DP_SRC_AVRCP_TG_TERMINATION_DURING_AVP | \ MPMD_A2DP_SRC_AVRCP_TG_PRESS_PLAY_DURING_CALL | \ MPMD_A2DP_SRC_AVRCP_TG_START_AUDIO_AFTER_PLAY | \ MPMD_A2DP_SRC_AVRCP_TG_SUSPEND_AUDIO_AFTER_PAUSE | \ MPMD_A2DP_SRC_AVRCP_TG_START_AUDIO_DURING_DATA_COMM | \ MPMD_A2DP_SRC_AVRCP_TG_START_DATA_DURING_AUDIO) #define MPS_MPMD_A2DP_SNK ( \ MPMD_HFP_HF_A2DP_SNK_AVRCP_CT_ANSWER_CALL_DURING_AUDIO | \ MPMD_HFP_HF_A2DP_SNK_AVRCP_CT_OUTGOING_CALL_DURING_AUDIO | \ MPMD_HFP_HF_A2DP_SNK_AVRCP_CT_REJECT_CALL_DURING_AUDIO | \ MPMD_HFP_HF_A2DP_SNK_AVRCP_CT_TERMINATION_DURING_AVP | \ MPMD_HFP_HF_A2DP_SNK_AVRCP_CT_PLAY_DURING_CALL | \ MPMD_A2DP_SNK_AVRCP_CT_DUN_DT_AUDIO_DURING_DATA_COMM | \ MPMD_A2DP_SNK_AVRCP_CT_DUN_DT_START_DATA_DURING_AUDIO) #define MPS_MPMD_AVRCP_CT MPS_MPMD_A2DP_SNK /* should be set only if CT is supported but SNK is not supported */ #define MPS_MPMD_AVRCP_CT_ONLY ( \ MPMD_AVRCP_CT_NO_A2DP_SNK_START_AUDIO_AFTER_PLAY | \ MPMD_AVRCP_CT_NO_A2DP_SNK_SUSPEND_AUDIO_AFTER_PAUSE) #define MPS_MPMD_AVRCP_TG MPS_MPMD_A2DP_SRC #define MPS_MPMD_DUN_DT ( \ MPMD_A2DP_SNK_AVRCP_CT_DUN_DT_AUDIO_DURING_DATA_COMM | \ MPMD_A2DP_SNK_AVRCP_CT_DUN_DT_START_DATA_DURING_AUDIO) #define MPS_MPMD_ALL (MPS_MPMD_HFP_AG | MPS_MPMD_HFP_HF | MPS_MPMD_A2DP_SRC | \ MPS_MPMD_A2DP_SNK | MPS_MPMD_AVRCP_CT | \ MPS_MPMD_AVRCP_CT_ONLY | MPS_MPMD_AVRCP_TG | \ MPS_MPMD_DUN_DT) /* Assume all dependencies are supported */ #define MPS_DEFAULT_DEPS (MPS_DEPS_SNIFF_MODE_DURRING_STREAMING | \ MPS_DEPS_GAVDP_REQUIREMENTS | \ MPS_DEPS_DIS_CONNECTION_ORDER_BEHAVIOR) static sdp_record_t *server = NULL; static uint32_t fixed_dbts = 0; /* * List of version numbers supported by the SDP server. * Add to this list when newer versions are supported. */ static sdp_version_t sdpVnumArray[1] = { { 1, 0 } }; static const int sdpServerVnumEntries = 1; static uint32_t mps_handle = 0; static bool mps_mpmd = false; /* * A simple function which returns the time of day in * seconds. Used for updating the service db state * attribute of the service record of the SDP server */ uint32_t sdp_get_time(void) { /* * To handle failure in gettimeofday, so an old * value is returned and service does not fail */ static struct timeval tm; gettimeofday(&tm, NULL); return (uint32_t) tm.tv_sec; } /* * The service database state is an attribute of the service record * of the SDP server itself. This attribute is guaranteed to * change if any of the contents of the service repository * changes. This function updates the timestamp of value of * the svcDBState attribute * Set the SDP server DB. Simply a timestamp which is the marker * when the DB was modified. */ static void update_db_timestamp(void) { if (fixed_dbts) { sdp_data_t *d = sdp_data_alloc(SDP_UINT32, &fixed_dbts); sdp_attr_replace(server, SDP_ATTR_SVCDB_STATE, d); } else { uint32_t dbts = sdp_get_time(); sdp_data_t *d = sdp_data_alloc(SDP_UINT32, &dbts); sdp_attr_replace(server, SDP_ATTR_SVCDB_STATE, d); } } void set_fixed_db_timestamp(uint32_t dbts) { fixed_dbts = dbts; } void register_public_browse_group(void) { sdp_list_t *browselist; uuid_t bgscid, pbgid; sdp_data_t *sdpdata; sdp_record_t *browse = sdp_record_alloc(); browse->handle = SDP_SERVER_RECORD_HANDLE + 1; sdp_record_add(BDADDR_ANY, browse); sdpdata = sdp_data_alloc(SDP_UINT32, &browse->handle); sdp_attr_add(browse, SDP_ATTR_RECORD_HANDLE, sdpdata); sdp_uuid16_create(&bgscid, BROWSE_GRP_DESC_SVCLASS_ID); browselist = sdp_list_append(0, &bgscid); sdp_set_service_classes(browse, browselist); sdp_list_free(browselist, 0); sdp_uuid16_create(&pbgid, PUBLIC_BROWSE_GROUP); sdp_attr_add_new(browse, SDP_ATTR_GROUP_ID, SDP_UUID16, &pbgid.value.uuid16); } /* * The SDP server must present its own service record to * the service repository. This can be accessed by service * discovery clients. This method constructs a service record * and stores it in the repository */ void register_server_service(void) { sdp_list_t *classIDList; uuid_t classID; void **versions, **versionDTDs; uint8_t dtd; sdp_data_t *pData; int i; server = sdp_record_alloc(); server->pattern = NULL; /* Force the record to be SDP_SERVER_RECORD_HANDLE */ server->handle = SDP_SERVER_RECORD_HANDLE; sdp_record_add(BDADDR_ANY, server); sdp_attr_add(server, SDP_ATTR_RECORD_HANDLE, sdp_data_alloc(SDP_UINT32, &server->handle)); sdp_uuid16_create(&classID, SDP_SERVER_SVCLASS_ID); classIDList = sdp_list_append(0, &classID); sdp_set_service_classes(server, classIDList); sdp_list_free(classIDList, 0); /* * Set the version numbers supported, these are passed as arguments * to the server on command line. Now defaults to 1.0 * Build the version number sequence first */ versions = malloc(sdpServerVnumEntries * sizeof(void *)); versionDTDs = malloc(sdpServerVnumEntries * sizeof(void *)); dtd = SDP_UINT16; for (i = 0; i < sdpServerVnumEntries; i++) { uint16_t *version = malloc(sizeof(uint16_t)); *version = sdpVnumArray[i].major; *version = (*version << 8); *version |= sdpVnumArray[i].minor; versions[i] = version; versionDTDs[i] = &dtd; } pData = sdp_seq_alloc(versionDTDs, versions, sdpServerVnumEntries); for (i = 0; i < sdpServerVnumEntries; i++) free(versions[i]); free(versions); free(versionDTDs); sdp_attr_add(server, SDP_ATTR_VERSION_NUM_LIST, pData); update_db_timestamp(); } void register_device_id(uint16_t source, uint16_t vendor, uint16_t product, uint16_t version) { const uint16_t spec = 0x0103; const uint8_t primary = 1; sdp_list_t *class_list, *group_list, *profile_list; uuid_t class_uuid, group_uuid; sdp_data_t *sdp_data, *primary_data, *source_data; sdp_data_t *spec_data, *vendor_data, *product_data, *version_data; sdp_profile_desc_t profile; sdp_record_t *record = sdp_record_alloc(); DBG("Adding device id record for %04x:%04x:%04x:%04x", source, vendor, product, version); record->handle = sdp_next_handle(); sdp_record_add(BDADDR_ANY, record); sdp_data = sdp_data_alloc(SDP_UINT32, &record->handle); sdp_attr_add(record, SDP_ATTR_RECORD_HANDLE, sdp_data); sdp_uuid16_create(&class_uuid, PNP_INFO_SVCLASS_ID); class_list = sdp_list_append(0, &class_uuid); sdp_set_service_classes(record, class_list); sdp_list_free(class_list, NULL); sdp_uuid16_create(&group_uuid, PUBLIC_BROWSE_GROUP); group_list = sdp_list_append(NULL, &group_uuid); sdp_set_browse_groups(record, group_list); sdp_list_free(group_list, NULL); sdp_uuid16_create(&profile.uuid, PNP_INFO_PROFILE_ID); profile.version = spec; profile_list = sdp_list_append(NULL, &profile); sdp_set_profile_descs(record, profile_list); sdp_list_free(profile_list, NULL); spec_data = sdp_data_alloc(SDP_UINT16, &spec); sdp_attr_add(record, 0x0200, spec_data); vendor_data = sdp_data_alloc(SDP_UINT16, &vendor); sdp_attr_add(record, 0x0201, vendor_data); product_data = sdp_data_alloc(SDP_UINT16, &product); sdp_attr_add(record, 0x0202, product_data); version_data = sdp_data_alloc(SDP_UINT16, &version); sdp_attr_add(record, 0x0203, version_data); primary_data = sdp_data_alloc(SDP_BOOL, &primary); sdp_attr_add(record, 0x0204, primary_data); source_data = sdp_data_alloc(SDP_UINT16, &source); sdp_attr_add(record, 0x0205, source_data); update_db_timestamp(); } static bool class_supported(uint16_t class) { sdp_list_t *list; uuid_t uuid; sdp_uuid16_create(&uuid, class); for (list = sdp_get_record_list(); list; list = list->next) { sdp_record_t *rec = list->data; if (sdp_uuid_cmp(&rec->svclass, &uuid) == 0) return true; } return false; } static uint64_t mps_mpsd_features(void) { uint64_t feat = MPS_MPSD_ALL; if (!class_supported(HANDSFREE_AGW_SVCLASS_ID)) feat &= ~MPS_MPSD_HFP_AG; if (!class_supported(HANDSFREE_SVCLASS_ID)) feat &= ~MPS_MPSD_HFP_HF; if (!class_supported(AUDIO_SOURCE_SVCLASS_ID)) feat &= ~MPS_MPSD_A2DP_SRC; if (!class_supported(AUDIO_SINK_SVCLASS_ID)) feat &= ~MPS_MPSD_A2DP_SNK; if (!class_supported(AV_REMOTE_CONTROLLER_SVCLASS_ID)) feat &= ~MPS_MPSD_AVRCP_CT; if (!class_supported(AV_REMOTE_TARGET_SVCLASS_ID)) feat &= ~MPS_MPSD_AVRCP_TG; if (!class_supported(DIALUP_NET_SVCLASS_ID)) feat &= ~MPS_MPSD_DUN_GW; /* TODO */ feat &= ~MPS_MPSD_DUN_DT; if (!class_supported(NAP_SVCLASS_ID)) feat &= ~MPS_MPSD_PAN_NAP; if (!class_supported(PANU_SVCLASS_ID)) feat &= ~MPS_MPSD_PAN_PANU; if (!class_supported(PBAP_PSE_SVCLASS_ID)) feat &= ~MPS_MPSD_PBAP_SRC; if (!class_supported(PBAP_PCE_SVCLASS_ID)) feat &= ~MPS_MPSD_PBAP_CLI; return feat; } static uint64_t mps_mpmd_features(void) { uint64_t feat = MPS_MPMD_ALL; if (!class_supported(HANDSFREE_AGW_SVCLASS_ID)) feat &= ~MPS_MPMD_HFP_AG; if (!class_supported(HANDSFREE_SVCLASS_ID)) feat &= ~MPS_MPMD_HFP_HF; if (!class_supported(AUDIO_SOURCE_SVCLASS_ID)) feat &= ~MPS_MPMD_A2DP_SRC; if (!class_supported(AUDIO_SINK_SVCLASS_ID)) feat &= ~MPS_MPMD_A2DP_SNK; else feat &= ~MPS_MPMD_AVRCP_CT_ONLY; if (!class_supported(AV_REMOTE_CONTROLLER_SVCLASS_ID)) { feat &= ~MPS_MPMD_AVRCP_CT; feat &= ~MPS_MPMD_AVRCP_CT_ONLY; } if (!class_supported(AV_REMOTE_TARGET_SVCLASS_ID)) feat &= ~MPS_MPMD_AVRCP_TG; /* TODO */ feat &= ~MPS_MPMD_DUN_DT; return feat; } static sdp_record_t *mps_record(int mpmd) { sdp_data_t *mpsd_features, *mpmd_features, *dependencies; sdp_list_t *svclass_id, *pfseq, *root; uuid_t root_uuid, svclass_uuid; sdp_profile_desc_t profile; sdp_record_t *record; uint64_t mpsd_feat = MPS_MPSD_DEFAULT_FEATURES; uint64_t mpmd_feat = MPS_MPMD_DEFAULT_FEATURES; uint16_t deps = MPS_DEFAULT_DEPS; record = sdp_record_alloc(); if (!record) return NULL; sdp_uuid16_create(&root_uuid, PUBLIC_BROWSE_GROUP); root = sdp_list_append(NULL, &root_uuid); sdp_set_browse_groups(record, root); sdp_list_free(root, NULL); sdp_uuid16_create(&svclass_uuid, MPS_SVCLASS_ID); svclass_id = sdp_list_append(NULL, &svclass_uuid); sdp_set_service_classes(record, svclass_id); sdp_list_free(svclass_id, NULL); sdp_uuid16_create(&profile.uuid, MPS_PROFILE_ID); profile.version = 0x0100; pfseq = sdp_list_append(NULL, &profile); sdp_set_profile_descs(record, pfseq); sdp_list_free(pfseq, NULL); mpsd_features = sdp_data_alloc(SDP_UINT64, &mpsd_feat); sdp_attr_add(record, SDP_ATTR_MPSD_SCENARIOS, mpsd_features); if (mpmd) { mpmd_features = sdp_data_alloc(SDP_UINT64, &mpmd_feat); sdp_attr_add(record, SDP_ATTR_MPMD_SCENARIOS, mpmd_features); } dependencies = sdp_data_alloc(SDP_UINT16, &deps); sdp_attr_add(record, SDP_ATTR_MPS_DEPENDENCIES, dependencies); sdp_set_info_attr(record, "Multi Profile", 0, 0); return record; } void register_mps(bool mpmd) { sdp_record_t *record; record = mps_record(mpmd); if (!record) return; if (add_record_to_server(BDADDR_ANY, record) < 0) { sdp_record_free(record); return; } mps_handle = record->handle; mps_mpmd = mpmd; } static void update_mps(void) { sdp_record_t *rec; sdp_data_t *data; uint64_t mpsd_feat, mpmd_feat; if (!mps_handle) return; rec = sdp_record_find(mps_handle); if (!rec) return; mpsd_feat = mps_mpsd_features(); data = sdp_data_alloc(SDP_UINT64, &mpsd_feat); sdp_attr_replace(rec, SDP_ATTR_MPSD_SCENARIOS, data); if (mps_mpmd) { mpmd_feat = mps_mpmd_features(); data = sdp_data_alloc(SDP_UINT64, &mpmd_feat); sdp_attr_replace(rec, SDP_ATTR_MPMD_SCENARIOS, data); } } int add_record_to_server(const bdaddr_t *src, sdp_record_t *rec) { sdp_data_t *data; sdp_list_t *pattern; if (rec->handle == 0xffffffff) { rec->handle = sdp_next_handle(); if (rec->handle < 0x10000) return -ENOSPC; } else { if (sdp_record_find(rec->handle)) return -EEXIST; } DBG("Adding record with handle 0x%05x", rec->handle); sdp_record_add(src, rec); data = sdp_data_alloc(SDP_UINT32, &rec->handle); sdp_attr_replace(rec, SDP_ATTR_RECORD_HANDLE, data); if (sdp_data_get(rec, SDP_ATTR_BROWSE_GRP_LIST) == NULL) { uuid_t uuid; sdp_uuid16_create(&uuid, PUBLIC_BROWSE_GROUP); sdp_pattern_add_uuid(rec, &uuid); } for (pattern = rec->pattern; pattern; pattern = pattern->next) { char uuid[32]; if (pattern->data == NULL) continue; sdp_uuid2strn((uuid_t *) pattern->data, uuid, sizeof(uuid)); DBG("Record pattern UUID %s", uuid); } update_mps(); update_db_timestamp(); return 0; } int remove_record_from_server(uint32_t handle) { sdp_record_t *rec; /* Refuse to remove the server's own record */ if (handle == SDP_SERVER_RECORD_HANDLE) return -EINVAL; DBG("Removing record with handle 0x%05x", handle); rec = sdp_record_find(handle); if (!rec) return -ENOENT; if (sdp_record_remove(handle) == 0) { update_mps(); update_db_timestamp(); } sdp_record_free(rec); return 0; } /* FIXME: refactor for server-side */ static sdp_record_t *extract_pdu_server(bdaddr_t *device, uint8_t *p, unsigned int bufsize, uint32_t handleExpected, int *scanned) { int extractStatus = -1, localExtractedLength = 0; uint8_t dtd; int seqlen = 0; sdp_record_t *rec = NULL; uint16_t attrId, lookAheadAttrId; sdp_data_t *pAttr = NULL; uint32_t handle = 0xffffffff; *scanned = sdp_extract_seqtype(p, bufsize, &dtd, &seqlen); p += *scanned; bufsize -= *scanned; if (bufsize < sizeof(uint8_t) + sizeof(uint8_t)) { SDPDBG("Unexpected end of packet"); return NULL; } lookAheadAttrId = get_be16(p + sizeof(uint8_t)); SDPDBG("Look ahead attr id : %d", lookAheadAttrId); if (lookAheadAttrId == SDP_ATTR_RECORD_HANDLE) { if (bufsize < (sizeof(uint8_t) * 2) + sizeof(uint16_t) + sizeof(uint32_t)) { SDPDBG("Unexpected end of packet"); return NULL; } handle = get_be32(p + sizeof(uint8_t) + sizeof(uint16_t) + sizeof(uint8_t)); SDPDBG("SvcRecHandle : 0x%x", handle); rec = sdp_record_find(handle); } else if (handleExpected != 0xffffffff) rec = sdp_record_find(handleExpected); if (!rec) { rec = sdp_record_alloc(); rec->attrlist = NULL; if (lookAheadAttrId == SDP_ATTR_RECORD_HANDLE) { rec->handle = handle; sdp_record_add(device, rec); } else if (handleExpected != 0xffffffff) { rec->handle = handleExpected; sdp_record_add(device, rec); } } else { sdp_list_free(rec->attrlist, (sdp_free_func_t) sdp_data_free); rec->attrlist = NULL; } while (localExtractedLength < seqlen) { int attrSize = sizeof(uint8_t); int attrValueLength = 0; if (bufsize < attrSize + sizeof(uint16_t)) { SDPDBG("Unexpected end of packet: Terminating extraction of attributes"); break; } SDPDBG("Extract PDU, sequenceLength: %d localExtractedLength: %d", seqlen, localExtractedLength); dtd = *(uint8_t *) p; attrId = get_be16(p + attrSize); attrSize += sizeof(uint16_t); SDPDBG("DTD of attrId : %d Attr id : 0x%x", dtd, attrId); pAttr = sdp_extract_attr(p + attrSize, bufsize - attrSize, &attrValueLength, rec); SDPDBG("Attr id : 0x%x attrValueLength : %d", attrId, attrValueLength); attrSize += attrValueLength; if (pAttr == NULL) { SDPDBG("Terminating extraction of attributes"); break; } localExtractedLength += attrSize; p += attrSize; bufsize -= attrSize; sdp_attr_replace(rec, attrId, pAttr); extractStatus = 0; SDPDBG("Extract PDU, seqLength: %d localExtractedLength: %d", seqlen, localExtractedLength); } if (extractStatus == 0) { SDPDBG("Successful extracting of Svc Rec attributes"); #ifdef SDP_DEBUG sdp_print_service_attr(rec->attrlist); #endif *scanned += seqlen; } return rec; } /* * Add the newly created service record to the service repository */ int service_register_req(sdp_req_t *req, sdp_buf_t *rsp) { int scanned = 0; sdp_data_t *handle; uint8_t *p = req->buf + sizeof(sdp_pdu_hdr_t); int bufsize = req->len - sizeof(sdp_pdu_hdr_t); sdp_record_t *rec; req->flags = *p++; if (req->flags & SDP_DEVICE_RECORD) { bacpy(&req->device, (bdaddr_t *) p); p += sizeof(bdaddr_t); bufsize -= sizeof(bdaddr_t); } /* save image of PDU: we need it when clients request this attribute */ rec = extract_pdu_server(&req->device, p, bufsize, 0xffffffff, &scanned); if (!rec) goto invalid; if (rec->handle == 0xffffffff) { rec->handle = sdp_next_handle(); if (rec->handle < 0x10000) { sdp_record_free(rec); goto invalid; } } else { if (sdp_record_find(rec->handle)) { /* extract_pdu_server will add the record handle * if it is missing. So instead of failing, skip * the record adding to avoid duplication. */ goto success; } } sdp_record_add(&req->device, rec); if (!(req->flags & SDP_RECORD_PERSIST)) sdp_svcdb_set_collectable(rec, req->sock); handle = sdp_data_alloc(SDP_UINT32, &rec->handle); sdp_attr_replace(rec, SDP_ATTR_RECORD_HANDLE, handle); success: /* if the browse group descriptor is NULL, * ensure that the record belongs to the ROOT group */ if (sdp_data_get(rec, SDP_ATTR_BROWSE_GRP_LIST) == NULL) { uuid_t uuid; sdp_uuid16_create(&uuid, PUBLIC_BROWSE_GROUP); sdp_pattern_add_uuid(rec, &uuid); } update_db_timestamp(); /* Build a rsp buffer */ put_be32(rec->handle, rsp->data); rsp->data_size = sizeof(uint32_t); return 0; invalid: put_be16(SDP_INVALID_SYNTAX, rsp->data); rsp->data_size = sizeof(uint16_t); return -1; } /* * Update a service record */ int service_update_req(sdp_req_t *req, sdp_buf_t *rsp) { sdp_record_t *orec, *nrec; int status = 0, scanned = 0; uint8_t *p = req->buf + sizeof(sdp_pdu_hdr_t); int bufsize = req->len - sizeof(sdp_pdu_hdr_t); uint32_t handle = get_be32(p); SDPDBG("Svc Rec Handle: 0x%x", handle); p += sizeof(uint32_t); bufsize -= sizeof(uint32_t); orec = sdp_record_find(handle); SDPDBG("SvcRecOld: %p", orec); if (!orec) { status = SDP_INVALID_RECORD_HANDLE; goto done; } nrec = extract_pdu_server(BDADDR_ANY, p, bufsize, handle, &scanned); if (!nrec) { status = SDP_INVALID_SYNTAX; goto done; } assert(nrec == orec); update_db_timestamp(); done: p = rsp->data; put_be16(status, p); rsp->data_size = sizeof(uint16_t); return status; } /* * Remove a registered service record */ int service_remove_req(sdp_req_t *req, sdp_buf_t *rsp) { uint8_t *p = req->buf + sizeof(sdp_pdu_hdr_t); uint32_t handle = get_be32(p); sdp_record_t *rec; int status = 0; /* extract service record handle */ rec = sdp_record_find(handle); if (rec) { sdp_svcdb_collect(rec); status = sdp_record_remove(handle); sdp_record_free(rec); if (status == 0) update_db_timestamp(); } else { status = SDP_INVALID_RECORD_HANDLE; SDPDBG("Could not find record : 0x%x", handle); } p = rsp->data; put_be16(status, p); rsp->data_size = sizeof(uint16_t); return status; } bluez-5.82/src/PaxHeaders/set.c0000644000000000000000000000005014643061455013404 xustar0020 atime=1743516692 20 ctime=1743591285 bluez-5.82/src/set.c0000644000000000000000000002160514643061455013071 0ustar00rootroot// SPDX-License-Identifier: GPL-2.0-or-later /* * * BlueZ - Bluetooth protocol stack for Linux * * Copyright (C) 2023 Intel Corporation * * */ #ifdef HAVE_CONFIG_H #include #endif #define _GNU_SOURCE #include #include #include #include #include #include #include #include #include "gdbus/gdbus.h" #include "src/shared/util.h" #include "src/shared/queue.h" #include "src/shared/ad.h" #include "src/shared/crypto.h" #include "src/shared/att.h" #include "src/shared/gatt-db.h" #include "log.h" #include "error.h" #include "adapter.h" #include "device.h" #include "dbus-common.h" #include "set.h" static struct queue *set_list; struct btd_device_set { struct btd_adapter *adapter; char *path; uint8_t sirk[16]; uint8_t size; bool auto_connect; struct queue *devices; struct btd_device *device; }; static DBusMessage *set_disconnect(DBusConnection *conn, DBusMessage *msg, void *user_data) { /* TODO */ return NULL; } static DBusMessage *set_connect(DBusConnection *conn, DBusMessage *msg, void *user_data) { /* TODO */ return NULL; } static const GDBusMethodTable set_methods[] = { { GDBUS_EXPERIMENTAL_ASYNC_METHOD("Disconnect", NULL, NULL, set_disconnect) }, { GDBUS_EXPERIMENTAL_ASYNC_METHOD("Connect", NULL, NULL, set_connect) }, {} }; static gboolean get_adapter(const GDBusPropertyTable *property, DBusMessageIter *iter, void *data) { struct btd_device_set *set = data; const char *path = adapter_get_path(set->adapter); dbus_message_iter_append_basic(iter, DBUS_TYPE_OBJECT_PATH, &path); return TRUE; } static gboolean get_auto_connect(const GDBusPropertyTable *property, DBusMessageIter *iter, void *data) { struct btd_device_set *set = data; dbus_message_iter_append_basic(iter, DBUS_TYPE_BOOLEAN, &set->auto_connect); return TRUE; } static void set_auto_connect(const GDBusPropertyTable *property, DBusMessageIter *iter, GDBusPendingPropertySet id, void *data) { struct btd_device_set *set = data; dbus_bool_t b; if (dbus_message_iter_get_arg_type(iter) != DBUS_TYPE_BOOLEAN) { g_dbus_pending_property_error(id, ERROR_INTERFACE ".InvalidArguments", "Invalid arguments in method call"); return; } dbus_message_iter_get_basic(iter, &b); set->auto_connect = b ? true : false; g_dbus_pending_property_success(id); } static void append_device(void *data, void *user_data) { struct btd_device *device = data; const char *path = device_get_path(device); DBusMessageIter *entry = user_data; dbus_message_iter_append_basic(entry, DBUS_TYPE_OBJECT_PATH, &path); } static gboolean get_devices(const GDBusPropertyTable *property, DBusMessageIter *iter, void *data) { struct btd_device_set *set = data; DBusMessageIter entry; dbus_message_iter_open_container(iter, DBUS_TYPE_ARRAY, DBUS_TYPE_OBJECT_PATH_AS_STRING, &entry); queue_foreach(set->devices, append_device, &entry); dbus_message_iter_close_container(iter, &entry); return TRUE; } static gboolean get_size(const GDBusPropertyTable *property, DBusMessageIter *iter, void *data) { struct btd_device_set *set = data; dbus_message_iter_append_basic(iter, DBUS_TYPE_BYTE, &set->size); return TRUE; } static const GDBusPropertyTable set_properties[] = { { "Adapter", "o", get_adapter, NULL, NULL, G_DBUS_PROPERTY_FLAG_EXPERIMENTAL }, { "AutoConnect", "b", get_auto_connect, set_auto_connect, NULL, G_DBUS_PROPERTY_FLAG_EXPERIMENTAL }, { "Devices", "ao", get_devices, NULL, NULL, G_DBUS_PROPERTY_FLAG_EXPERIMENTAL }, { "Size", "y", get_size, NULL, NULL, G_DBUS_PROPERTY_FLAG_EXPERIMENTAL }, {} }; static void set_free(void *data) { struct btd_device_set *set = data; queue_destroy(set->devices, NULL); g_free(set->path); free(set); } static struct btd_device_set *set_new(struct btd_device *device, const uint8_t sirk[16], uint8_t size) { struct btd_device_set *set; set = new0(struct btd_device_set, 1); set->adapter = device_get_adapter(device); memcpy(set->sirk, sirk, sizeof(set->sirk)); set->size = size; set->auto_connect = true; set->devices = queue_new(); queue_push_tail(set->devices, device); set->path = g_strdup_printf("%s/set_%02x%02x%02x%02x%02x%02x%02x%02x" "%02x%02x%02x%02x%02x%02x%02x%02x", adapter_get_path(set->adapter), sirk[15], sirk[14], sirk[13], sirk[12], sirk[11], sirk[10], sirk[9], sirk[8], sirk[7], sirk[6], sirk[5], sirk[4], sirk[3], sirk[2], sirk[1], sirk[0]); DBG("Creating set %s", set->path); if (g_dbus_register_interface(btd_get_dbus_connection(), set->path, BTD_DEVICE_SET_INTERFACE, set_methods, NULL, set_properties, set, set_free) == FALSE) { error("Unable to register set interface"); set_free(set); return NULL; } return set; } static struct btd_device_set *set_find(struct btd_device *device, const uint8_t sirk[16]) { struct btd_adapter *adapter = device_get_adapter(device); const struct queue_entry *entry; for (entry = queue_get_entries(set_list); entry; entry = entry->next) { struct btd_device_set *set = entry->data; if (set->adapter != adapter) continue; if (!memcmp(set->sirk, sirk, sizeof(set->sirk))) return set; } return NULL; } static void set_connect_next(struct btd_device_set *set) { const struct queue_entry *entry; for (entry = queue_get_entries(set->devices); entry; entry = entry->next) { struct btd_device *device = entry->data; /* Only connect one at time(?) */ if (!device_connect_le(device)) return; } } static void set_add(struct btd_device_set *set, struct btd_device *device) { /* Check if device is already part of the set then skip to connect */ if (queue_find(set->devices, NULL, device)) goto done; DBG("set %s device %s", set->path, device_get_path(device)); queue_push_tail(set->devices, device); g_dbus_emit_property_changed(btd_get_dbus_connection(), set->path, BTD_DEVICE_SET_INTERFACE, "Devices"); done: /* Check if set is marked to auto-connect */ if (btd_device_is_connected(device) && set->auto_connect) set_connect_next(set); } static void foreach_rsi(void *data, void *user_data) { struct bt_ad_data *ad = data; struct btd_device_set *set = user_data; struct bt_crypto *crypto; uint8_t res[3]; if (ad->type != BT_AD_CSIP_RSI || ad->len < 6) return; crypto = bt_crypto_new(); if (!crypto) return; if (!bt_crypto_sih(crypto, set->sirk, ad->data + 3, res)) { bt_crypto_unref(crypto); return; } bt_crypto_unref(crypto); if (memcmp(ad->data, res, sizeof(res))) return; /* Attempt to use existing gatt_db from set if device has never been * connected before. * * If dbs don't really match bt_gatt_client will attempt to rediscover * the ranges that don't match. */ if (gatt_db_isempty(btd_device_get_gatt_db(set->device))) { struct btd_device *device; device = queue_get_entries(set->devices)->data; btd_device_set_gatt_db(set->device, btd_device_get_gatt_db(device)); } device_connect_le(set->device); } static void foreach_device(struct btd_device *device, void *data) { struct btd_device_set *set = data; /* Check if device is already part of the set then skip */ if (queue_find(set->devices, NULL, device)) return; set->device = device; btd_device_foreach_ad(device, foreach_rsi, set); } struct btd_device_set *btd_set_add_device(struct btd_device *device, const uint8_t *key, const uint8_t sirk_value[16], uint8_t size) { struct btd_device_set *set; uint8_t sirk[16]; memcpy(sirk, sirk_value, sizeof(sirk)); /* In case key has been set it means SIRK is encrypted */ if (key) { struct bt_crypto *crypto = bt_crypto_new(); if (!crypto) return NULL; /* sef and sdf are symmetric */ bt_crypto_sef(crypto, key, sirk, sirk); bt_crypto_unref(crypto); } /* Check if DeviceSet already exists */ set = set_find(device, sirk); if (set) { set_add(set, device); /* Check if there are new devices with RSI found */ goto done; } set = set_new(device, sirk, size); if (!set) return NULL; if (!set_list) set_list = queue_new(); queue_push_tail(set_list, set); done: /* Attempt to add devices which have matching RSI */ btd_adapter_for_each_device(device_get_adapter(device), foreach_device, set); return set; } bool btd_set_remove_device(struct btd_device_set *set, struct btd_device *device) { if (!set || !device) return false; if (!queue_remove_if(set->devices, NULL, device)) return false; if (!queue_isempty(set->devices)) { g_dbus_emit_property_changed(btd_get_dbus_connection(), set->path, BTD_DEVICE_SET_INTERFACE, "Devices"); return true; } if (!queue_remove(set_list, set)) return false; /* Unregister if there are no devices left in the set */ g_dbus_unregister_interface(btd_get_dbus_connection(), set->path, BTD_DEVICE_SET_INTERFACE); return true; } const char *btd_set_get_path(struct btd_device_set *set) { return set->path; } bluez-5.82/src/PaxHeaders/service.c0000644000000000000000000000005014267331527014253 xustar0020 atime=1743516662 20 ctime=1743591285 bluez-5.82/src/service.c0000644000000000000000000002224114267331527013735 0ustar00rootroot// SPDX-License-Identifier: GPL-2.0-or-later /* * * BlueZ - Bluetooth protocol stack for Linux * * Copyright (C) 2012-2013 BMW Car IT GmbH. All rights reserved. * * */ #ifdef HAVE_CONFIG_H #include #endif #include #include #include #include #include #include #include #include #include #include "lib/bluetooth.h" #include "lib/sdp.h" #include "log.h" #include "backtrace.h" #include "adapter.h" #include "device.h" #include "profile.h" #include "service.h" struct btd_service { int ref; struct btd_device *device; struct btd_profile *profile; void *user_data; btd_service_state_t state; int err; bool is_allowed; bool initiator; }; struct service_state_callback { btd_service_state_cb cb; void *user_data; unsigned int id; }; static GSList *state_callbacks = NULL; static const char *state2str(btd_service_state_t state) { switch (state) { case BTD_SERVICE_STATE_UNAVAILABLE: return "unavailable"; case BTD_SERVICE_STATE_DISCONNECTED: return "disconnected"; case BTD_SERVICE_STATE_CONNECTING: return "connecting"; case BTD_SERVICE_STATE_CONNECTED: return "connected"; case BTD_SERVICE_STATE_DISCONNECTING: return "disconnecting"; } return NULL; } static void change_state(struct btd_service *service, btd_service_state_t state, int err) { btd_service_state_t old = service->state; char addr[18]; GSList *l; if (state == old) return; btd_assert(service->device != NULL); btd_assert(service->profile != NULL); service->state = state; service->err = err; ba2str(device_get_address(service->device), addr); DBG("%p: device %s profile %s state changed: %s -> %s (%d)", service, addr, service->profile->name, state2str(old), state2str(state), err); for (l = state_callbacks; l != NULL; l = g_slist_next(l)) { struct service_state_callback *cb = l->data; cb->cb(service, old, state, cb->user_data); } if (state == BTD_SERVICE_STATE_DISCONNECTED) service->initiator = false; } struct btd_service *btd_service_ref(struct btd_service *service) { service->ref++; DBG("%p: ref=%d", service, service->ref); return service; } void btd_service_unref(struct btd_service *service) { service->ref--; DBG("%p: ref=%d", service, service->ref); if (service->ref > 0) return; g_free(service); } struct btd_service *service_create(struct btd_device *device, struct btd_profile *profile) { struct btd_service *service; service = g_try_new0(struct btd_service, 1); if (!service) { error("service_create: failed to alloc memory"); return NULL; } service->ref = 1; service->device = device; /* Weak ref */ service->profile = profile; service->state = BTD_SERVICE_STATE_UNAVAILABLE; service->is_allowed = true; return service; } int service_probe(struct btd_service *service) { char addr[18]; int err; btd_assert(service->state == BTD_SERVICE_STATE_UNAVAILABLE); err = service->profile->device_probe(service); if (err == 0) { change_state(service, BTD_SERVICE_STATE_DISCONNECTED, 0); return 0; } ba2str(device_get_address(service->device), addr); error("%s profile probe failed for %s", service->profile->name, addr); return err; } void service_remove(struct btd_service *service) { change_state(service, BTD_SERVICE_STATE_DISCONNECTED, -ECONNABORTED); change_state(service, BTD_SERVICE_STATE_UNAVAILABLE, 0); service->profile->device_remove(service); service->device = NULL; service->profile = NULL; btd_service_unref(service); } int service_accept(struct btd_service *service, bool initiator) { char addr[18]; int err; switch (service->state) { case BTD_SERVICE_STATE_UNAVAILABLE: return -EINVAL; case BTD_SERVICE_STATE_DISCONNECTED: break; case BTD_SERVICE_STATE_CONNECTING: case BTD_SERVICE_STATE_CONNECTED: return 0; case BTD_SERVICE_STATE_DISCONNECTING: return -EBUSY; } if (!service->profile->accept) return -ENOSYS; if (!service->is_allowed) { info("service %s is not allowed", service->profile->remote_uuid); return -ECONNABORTED; } service->initiator = initiator; err = service->profile->accept(service); if (!err) goto done; ba2str(device_get_address(service->device), addr); error("%s profile accept failed for %s", service->profile->name, addr); return err; done: if (service->state == BTD_SERVICE_STATE_DISCONNECTED) change_state(service, BTD_SERVICE_STATE_CONNECTING, 0); return 0; } int service_set_connecting(struct btd_service *service) { switch (service->state) { case BTD_SERVICE_STATE_UNAVAILABLE: return -EINVAL; case BTD_SERVICE_STATE_DISCONNECTED: break; case BTD_SERVICE_STATE_CONNECTING: case BTD_SERVICE_STATE_CONNECTED: return 0; case BTD_SERVICE_STATE_DISCONNECTING: return -EBUSY; } change_state(service, BTD_SERVICE_STATE_CONNECTING, 0); return 0; } int btd_service_connect(struct btd_service *service) { struct btd_profile *profile = service->profile; char addr[18]; int err; if (!profile->connect) return -ENOTSUP; if (!btd_adapter_get_powered(device_get_adapter(service->device))) return -ENETDOWN; switch (service->state) { case BTD_SERVICE_STATE_UNAVAILABLE: return -EINVAL; case BTD_SERVICE_STATE_DISCONNECTED: break; case BTD_SERVICE_STATE_CONNECTING: return 0; case BTD_SERVICE_STATE_CONNECTED: return -EALREADY; case BTD_SERVICE_STATE_DISCONNECTING: return -EBUSY; } if (!service->is_allowed) { info("service %s is not allowed", service->profile->remote_uuid); return -ECONNABORTED; } err = profile->connect(service); if (err == 0) { service->initiator = true; change_state(service, BTD_SERVICE_STATE_CONNECTING, 0); return 0; } ba2str(device_get_address(service->device), addr); error("%s profile connect failed for %s: %s", profile->name, addr, strerror(-err)); return err; } int btd_service_disconnect(struct btd_service *service) { struct btd_profile *profile = service->profile; char addr[18]; int err; if (!profile->disconnect) return -ENOTSUP; switch (service->state) { case BTD_SERVICE_STATE_UNAVAILABLE: return -EINVAL; case BTD_SERVICE_STATE_DISCONNECTED: return -EALREADY; case BTD_SERVICE_STATE_DISCONNECTING: return 0; case BTD_SERVICE_STATE_CONNECTING: case BTD_SERVICE_STATE_CONNECTED: break; } change_state(service, BTD_SERVICE_STATE_DISCONNECTING, 0); err = profile->disconnect(service); if (err == 0) return 0; if (err == -ENOTCONN) { btd_service_disconnecting_complete(service, 0); return 0; } ba2str(device_get_address(service->device), addr); error("%s profile disconnect failed for %s: %s", profile->name, addr, strerror(-err)); btd_service_disconnecting_complete(service, err); return err; } struct btd_device *btd_service_get_device(const struct btd_service *service) { return service->device; } struct btd_profile *btd_service_get_profile(const struct btd_service *service) { return service->profile; } void btd_service_set_user_data(struct btd_service *service, void *user_data) { service->user_data = user_data; } void *btd_service_get_user_data(const struct btd_service *service) { return service->user_data; } btd_service_state_t btd_service_get_state(const struct btd_service *service) { return service->state; } int btd_service_get_error(const struct btd_service *service) { return service->err; } bool btd_service_is_initiator(const struct btd_service *service) { return service->initiator; } unsigned int btd_service_add_state_cb(btd_service_state_cb cb, void *user_data) { struct service_state_callback *state_cb; static unsigned int id = 0; state_cb = g_new0(struct service_state_callback, 1); state_cb->cb = cb; state_cb->user_data = user_data; state_cb->id = ++id; state_callbacks = g_slist_append(state_callbacks, state_cb); return state_cb->id; } bool btd_service_remove_state_cb(unsigned int id) { GSList *l; for (l = state_callbacks; l != NULL; l = g_slist_next(l)) { struct service_state_callback *cb = l->data; if (cb && cb->id == id) { state_callbacks = g_slist_remove(state_callbacks, cb); g_free(cb); return true; } } return false; } void btd_service_set_allowed(struct btd_service *service, bool allowed) { if (allowed == service->is_allowed) return; service->is_allowed = allowed; if (!allowed && (service->state == BTD_SERVICE_STATE_CONNECTING || service->state == BTD_SERVICE_STATE_CONNECTED)) { btd_service_disconnect(service); return; } } bool btd_service_is_allowed(struct btd_service *service) { return service->is_allowed; } void btd_service_connecting_complete(struct btd_service *service, int err) { if (service->state != BTD_SERVICE_STATE_DISCONNECTED && service->state != BTD_SERVICE_STATE_CONNECTING) return; if (err == 0) change_state(service, BTD_SERVICE_STATE_CONNECTED, 0); else change_state(service, BTD_SERVICE_STATE_DISCONNECTED, err); } void btd_service_disconnecting_complete(struct btd_service *service, int err) { if (service->state != BTD_SERVICE_STATE_CONNECTED && service->state != BTD_SERVICE_STATE_DISCONNECTING) return; if (err == 0) change_state(service, BTD_SERVICE_STATE_DISCONNECTED, 0); else /* If disconnect fails, we assume it remains connected */ change_state(service, BTD_SERVICE_STATE_CONNECTED, err); } bluez-5.82/src/PaxHeaders/dbus-common.c0000644000000000000000000000005014131623652015027 xustar0020 atime=1743516682 20 ctime=1743591285 bluez-5.82/src/dbus-common.c0000644000000000000000000000522314131623652014512 0ustar00rootroot// SPDX-License-Identifier: GPL-2.0-or-later /* * * BlueZ - Bluetooth protocol stack for Linux * * Copyright (C) 2006-2010 Nokia Corporation * Copyright (C) 2004-2010 Marcel Holtmann * Copyright (C) 2005-2007 Johan Hedberg * * */ #ifdef HAVE_CONFIG_H #include #endif #include #include #include #include #include "gdbus/gdbus.h" #include "log.h" #include "dbus-common.h" static DBusConnection *connection = NULL; void dict_append_entry(DBusMessageIter *dict, const char *key, int type, void *val) { g_dbus_dict_append_entry(dict, key, type, val); } void dict_append_array(DBusMessageIter *dict, const char *key, int type, void *val, int n_elements) { g_dbus_dict_append_array(dict, key, type, val, n_elements); } void set_dbus_connection(DBusConnection *conn) { connection = conn; } DBusConnection *btd_get_dbus_connection(void) { return connection; } const char *class_to_icon(uint32_t class) { switch ((class & 0x1f00) >> 8) { case 0x01: return "computer"; case 0x02: switch ((class & 0xfc) >> 2) { case 0x01: case 0x02: case 0x03: case 0x05: return "phone"; case 0x04: return "modem"; } break; case 0x03: return "network-wireless"; case 0x04: switch ((class & 0xfc) >> 2) { case 0x01: case 0x02: return "audio-headset"; case 0x06: return "audio-headphones"; case 0x0b: /* VCR */ case 0x0c: /* Video Camera */ case 0x0d: /* Camcorder */ return "camera-video"; default: return "audio-card"; /* Other audio device */ } break; case 0x05: switch ((class & 0xc0) >> 6) { case 0x00: switch ((class & 0x1e) >> 2) { case 0x01: case 0x02: return "input-gaming"; } break; case 0x01: return "input-keyboard"; case 0x02: switch ((class & 0x1e) >> 2) { case 0x05: return "input-tablet"; default: return "input-mouse"; } } break; case 0x06: if (class & 0x80) return "printer"; if (class & 0x20) return "camera-photo"; break; } return NULL; } const char *gap_appearance_to_icon(uint16_t appearance) { switch ((appearance & 0xffc0) >> 6) { case 0x00: return "unknown"; case 0x01: return "phone"; case 0x02: return "computer"; case 0x05: return "video-display"; case 0x0a: return "multimedia-player"; case 0x0b: return "scanner"; case 0x0f: /* HID Generic */ switch (appearance & 0x3f) { case 0x01: return "input-keyboard"; case 0x02: return "input-mouse"; case 0x03: case 0x04: return "input-gaming"; case 0x05: return "input-tablet"; case 0x08: return "scanner"; } break; } return NULL; } bluez-5.82/src/PaxHeaders/sdp-xml.c0000644000000000000000000000005014015011623014156 xustar0020 atime=1743516622 20 ctime=1743591284 bluez-5.82/src/sdp-xml.c0000644000000000000000000005347514015011623013655 0ustar00rootroot// SPDX-License-Identifier: GPL-2.0-or-later /* * * BlueZ - Bluetooth protocol stack for Linux * * Copyright (C) 2005-2010 Marcel Holtmann * * */ #ifdef HAVE_CONFIG_H #include #endif #define _GNU_SOURCE #include #include #include #include #include #include #include #include "lib/sdp.h" #include "lib/sdp_lib.h" #include "sdp-xml.h" #define DBG(...) (void)(0) #define error(...) (void)(0) #define SDP_XML_ENCODING_NORMAL 0 #define SDP_XML_ENCODING_HEX 1 #define STRBUFSIZE 1024 #define MAXINDENT 64 struct sdp_xml_data { char *text; /* Pointer to the current buffer */ int size; /* Size of the current buffer */ sdp_data_t *data; /* The current item being built */ struct sdp_xml_data *next; /* Next item on the stack */ char type; /* 0 = Text or Hexadecimal */ char *name; /* Name, optional in the dtd */ /* TODO: What is it used for? */ }; struct context_data { sdp_record_t *record; sdp_data_t attr_data; struct sdp_xml_data *stack_head; uint16_t attr_id; }; static int compute_seq_size(sdp_data_t *data) { int unit_size = data->unitSize; sdp_data_t *seq = data->val.dataseq; for (; seq; seq = seq->next) unit_size += seq->unitSize; return unit_size; } #define DEFAULT_XML_DATA_SIZE 1024 static struct sdp_xml_data *sdp_xml_data_alloc(void) { struct sdp_xml_data *elem; elem = malloc(sizeof(struct sdp_xml_data)); if (!elem) return NULL; memset(elem, 0, sizeof(struct sdp_xml_data)); /* Null terminate the text */ elem->size = DEFAULT_XML_DATA_SIZE; elem->text = malloc(DEFAULT_XML_DATA_SIZE); if (!elem->text) { free(elem); return NULL; } elem->text[0] = '\0'; return elem; } static struct sdp_xml_data *sdp_xml_data_expand(struct sdp_xml_data *elem) { char *newbuf; newbuf = malloc(elem->size * 2); if (!newbuf) return NULL; memcpy(newbuf, elem->text, elem->size); elem->size *= 2; free(elem->text); elem->text = newbuf; return elem; } static sdp_data_t *sdp_xml_parse_uuid128(const char *data) { uint128_t val; unsigned int i, j; char buf[3]; memset(&val, 0, sizeof(val)); buf[2] = '\0'; for (j = 0, i = 0; i < strlen(data);) { if (data[i] == '-') { i++; continue; } buf[0] = data[i]; buf[1] = data[i + 1]; val.data[j++] = strtoul(buf, 0, 16); i += 2; } return sdp_data_alloc(SDP_UUID128, &val); } static sdp_data_t *sdp_xml_parse_uuid(const char *data, sdp_record_t *record) { sdp_data_t *ret; char *endptr; uint32_t val; uint16_t val2; int len; len = strlen(data); if (len == 36) { ret = sdp_xml_parse_uuid128(data); goto result; } val = strtoll(data, &endptr, 16); /* Couldn't parse */ if (*endptr != '\0') return NULL; if (val > USHRT_MAX) { ret = sdp_data_alloc(SDP_UUID32, &val); goto result; } val2 = val; ret = sdp_data_alloc(SDP_UUID16, &val2); result: if (record && ret) sdp_pattern_add_uuid(record, &ret->val.uuid); return ret; } static sdp_data_t *sdp_xml_parse_int(const char *data, uint8_t dtd) { char *endptr; sdp_data_t *ret = NULL; switch (dtd) { case SDP_BOOL: { uint8_t val = 0; if (!strcmp("true", data)) val = 1; else if (!strcmp("false", data)) val = 0; else return NULL; ret = sdp_data_alloc(dtd, &val); break; } case SDP_INT8: { int8_t val = strtoul(data, &endptr, 0); /* Failed to parse */ if ((endptr != data) && (*endptr != '\0')) return NULL; ret = sdp_data_alloc(dtd, &val); break; } case SDP_UINT8: { uint8_t val = strtoul(data, &endptr, 0); /* Failed to parse */ if ((endptr != data) && (*endptr != '\0')) return NULL; ret = sdp_data_alloc(dtd, &val); break; } case SDP_INT16: { int16_t val = strtoul(data, &endptr, 0); /* Failed to parse */ if ((endptr != data) && (*endptr != '\0')) return NULL; ret = sdp_data_alloc(dtd, &val); break; } case SDP_UINT16: { uint16_t val = strtoul(data, &endptr, 0); /* Failed to parse */ if ((endptr != data) && (*endptr != '\0')) return NULL; ret = sdp_data_alloc(dtd, &val); break; } case SDP_INT32: { int32_t val = strtoul(data, &endptr, 0); /* Failed to parse */ if ((endptr != data) && (*endptr != '\0')) return NULL; ret = sdp_data_alloc(dtd, &val); break; } case SDP_UINT32: { uint32_t val = strtoul(data, &endptr, 0); /* Failed to parse */ if ((endptr != data) && (*endptr != '\0')) return NULL; ret = sdp_data_alloc(dtd, &val); break; } case SDP_INT64: { int64_t val = strtoull(data, &endptr, 0); /* Failed to parse */ if ((endptr != data) && (*endptr != '\0')) return NULL; ret = sdp_data_alloc(dtd, &val); break; } case SDP_UINT64: { uint64_t val = strtoull(data, &endptr, 0); /* Failed to parse */ if ((endptr != data) && (*endptr != '\0')) return NULL; ret = sdp_data_alloc(dtd, &val); break; } case SDP_INT128: case SDP_UINT128: { uint128_t val; int i = 0; char buf[3]; buf[2] = '\0'; for (; i < 32; i += 2) { buf[0] = data[i]; buf[1] = data[i + 1]; val.data[i >> 1] = strtoul(buf, 0, 16); } ret = sdp_data_alloc(dtd, &val); break; } }; return ret; } static char *sdp_xml_parse_string_decode(const char *data, char encoding, uint32_t *length) { int len = strlen(data); char *text; if (encoding == SDP_XML_ENCODING_NORMAL) { text = strdup(data); *length = len; } else { char buf[3], *decoded; int i; decoded = malloc((len >> 1) + 1); if (!decoded) return NULL; /* Ensure the string is a power of 2 */ len = (len >> 1) << 1; buf[2] = '\0'; for (i = 0; i < len; i += 2) { buf[0] = data[i]; buf[1] = data[i + 1]; decoded[i >> 1] = strtoul(buf, 0, 16); } decoded[len >> 1] = '\0'; text = decoded; *length = len >> 1; } return text; } static sdp_data_t *sdp_xml_parse_url(const char *data) { uint8_t dtd = SDP_URL_STR8; char *url; uint32_t length; sdp_data_t *ret; url = sdp_xml_parse_string_decode(data, SDP_XML_ENCODING_NORMAL, &length); if (!url) return NULL; if (length > UCHAR_MAX) dtd = SDP_URL_STR16; ret = sdp_data_alloc_with_length(dtd, url, length); free(url); return ret; } static sdp_data_t *sdp_xml_parse_text(const char *data, char encoding) { uint8_t dtd = SDP_TEXT_STR8; char *text; uint32_t length; sdp_data_t *ret; text = sdp_xml_parse_string_decode(data, encoding, &length); if (!text) return NULL; if (length > UCHAR_MAX) dtd = SDP_TEXT_STR16; ret = sdp_data_alloc_with_length(dtd, text, length); free(text); return ret; } static sdp_data_t *sdp_xml_parse_nil(const char *data) { return sdp_data_alloc(SDP_DATA_NIL, 0); } static sdp_data_t *sdp_xml_parse_datatype(const char *el, struct sdp_xml_data *elem, sdp_record_t *record) { const char *data = elem->text; if (!strcmp(el, "boolean")) return sdp_xml_parse_int(data, SDP_BOOL); else if (!strcmp(el, "uint8")) return sdp_xml_parse_int(data, SDP_UINT8); else if (!strcmp(el, "uint16")) return sdp_xml_parse_int(data, SDP_UINT16); else if (!strcmp(el, "uint32")) return sdp_xml_parse_int(data, SDP_UINT32); else if (!strcmp(el, "uint64")) return sdp_xml_parse_int(data, SDP_UINT64); else if (!strcmp(el, "uint128")) return sdp_xml_parse_int(data, SDP_UINT128); else if (!strcmp(el, "int8")) return sdp_xml_parse_int(data, SDP_INT8); else if (!strcmp(el, "int16")) return sdp_xml_parse_int(data, SDP_INT16); else if (!strcmp(el, "int32")) return sdp_xml_parse_int(data, SDP_INT32); else if (!strcmp(el, "int64")) return sdp_xml_parse_int(data, SDP_INT64); else if (!strcmp(el, "int128")) return sdp_xml_parse_int(data, SDP_INT128); else if (!strcmp(el, "uuid")) return sdp_xml_parse_uuid(data, record); else if (!strcmp(el, "url")) return sdp_xml_parse_url(data); else if (!strcmp(el, "text")) return sdp_xml_parse_text(data, elem->type); else if (!strcmp(el, "nil")) return sdp_xml_parse_nil(data); return NULL; } static void element_start(GMarkupParseContext *context, const char *element_name, const char **attribute_names, const char **attribute_values, gpointer user_data, GError **err) { struct context_data *ctx_data = user_data; if (!strcmp(element_name, "record")) return; if (!strcmp(element_name, "attribute")) { int i; for (i = 0; attribute_names[i]; i++) { if (!strcmp(attribute_names[i], "id")) { ctx_data->attr_id = strtol(attribute_values[i], 0, 0); break; } } DBG("New attribute 0x%04x", ctx_data->attr_id); return; } if (ctx_data->stack_head) { struct sdp_xml_data *newelem = sdp_xml_data_alloc(); newelem->next = ctx_data->stack_head; ctx_data->stack_head = newelem; } else { ctx_data->stack_head = sdp_xml_data_alloc(); ctx_data->stack_head->next = NULL; } if (!strcmp(element_name, "sequence")) ctx_data->stack_head->data = sdp_data_alloc(SDP_SEQ8, NULL); else if (!strcmp(element_name, "alternate")) ctx_data->stack_head->data = sdp_data_alloc(SDP_ALT8, NULL); else { int i; /* Parse value, name, encoding */ for (i = 0; attribute_names[i]; i++) { if (!strcmp(attribute_names[i], "value")) { int curlen = strlen(ctx_data->stack_head->text); int attrlen = strlen(attribute_values[i]); /* Ensure we're big enough */ while ((curlen + 1 + attrlen) > ctx_data->stack_head->size) sdp_xml_data_expand(ctx_data->stack_head); memcpy(ctx_data->stack_head->text + curlen, attribute_values[i], attrlen); ctx_data->stack_head->text[curlen + attrlen] = '\0'; } if (!strcmp(attribute_names[i], "encoding")) { if (!strcmp(attribute_values[i], "hex")) ctx_data->stack_head->type = 1; } if (!strcmp(attribute_names[i], "name")) ctx_data->stack_head->name = strdup(attribute_values[i]); } ctx_data->stack_head->data = sdp_xml_parse_datatype(element_name, ctx_data->stack_head, ctx_data->record); if (ctx_data->stack_head->data == NULL) error("Can't parse element %s", element_name); } } static void sdp_xml_data_free(struct sdp_xml_data *elem) { if (elem->data) sdp_data_free(elem->data); free(elem->name); free(elem->text); free(elem); } static void element_end(GMarkupParseContext *context, const char *element_name, gpointer user_data, GError **err) { struct context_data *ctx_data = user_data; struct sdp_xml_data *elem; if (!strcmp(element_name, "record")) return; if (!strcmp(element_name, "attribute")) { if (ctx_data->stack_head && ctx_data->stack_head->data) { int ret = sdp_attr_add(ctx_data->record, ctx_data->attr_id, ctx_data->stack_head->data); if (ret == -1) DBG("Could not add attribute 0x%04x", ctx_data->attr_id); ctx_data->stack_head->data = NULL; sdp_xml_data_free(ctx_data->stack_head); ctx_data->stack_head = NULL; } else { DBG("No data for attribute 0x%04x", ctx_data->attr_id); } return; } if (!ctx_data->stack_head || !ctx_data->stack_head->data) { DBG("No data for %s", element_name); return; } if (!strcmp(element_name, "sequence")) { ctx_data->stack_head->data->unitSize = compute_seq_size(ctx_data->stack_head->data); if (ctx_data->stack_head->data->unitSize > USHRT_MAX) { ctx_data->stack_head->data->unitSize += sizeof(uint32_t); ctx_data->stack_head->data->dtd = SDP_SEQ32; } else if (ctx_data->stack_head->data->unitSize > UCHAR_MAX) { ctx_data->stack_head->data->unitSize += sizeof(uint16_t); ctx_data->stack_head->data->dtd = SDP_SEQ16; } else { ctx_data->stack_head->data->unitSize += sizeof(uint8_t); } } else if (!strcmp(element_name, "alternate")) { ctx_data->stack_head->data->unitSize = compute_seq_size(ctx_data->stack_head->data); if (ctx_data->stack_head->data->unitSize > USHRT_MAX) { ctx_data->stack_head->data->unitSize += sizeof(uint32_t); ctx_data->stack_head->data->dtd = SDP_ALT32; } else if (ctx_data->stack_head->data->unitSize > UCHAR_MAX) { ctx_data->stack_head->data->unitSize += sizeof(uint16_t); ctx_data->stack_head->data->dtd = SDP_ALT16; } else { ctx_data->stack_head->data->unitSize += sizeof(uint8_t); } } if (ctx_data->stack_head->next && ctx_data->stack_head->data && ctx_data->stack_head->next->data) { switch (ctx_data->stack_head->next->data->dtd) { case SDP_SEQ8: case SDP_SEQ16: case SDP_SEQ32: case SDP_ALT8: case SDP_ALT16: case SDP_ALT32: ctx_data->stack_head->next->data->val.dataseq = sdp_seq_append(ctx_data->stack_head->next->data->val.dataseq, ctx_data->stack_head->data); ctx_data->stack_head->data = NULL; break; } elem = ctx_data->stack_head; ctx_data->stack_head = ctx_data->stack_head->next; sdp_xml_data_free(elem); } } static GMarkupParser parser = { element_start, element_end, NULL, NULL, NULL }; sdp_record_t *sdp_xml_parse_record(const char *data, int size) { GMarkupParseContext *ctx; struct context_data *ctx_data; sdp_record_t *record; ctx_data = malloc(sizeof(*ctx_data)); if (!ctx_data) return NULL; record = sdp_record_alloc(); if (!record) { free(ctx_data); return NULL; } memset(ctx_data, 0, sizeof(*ctx_data)); ctx_data->record = record; ctx = g_markup_parse_context_new(&parser, 0, ctx_data, NULL); if (g_markup_parse_context_parse(ctx, data, size, NULL) == FALSE) { error("XML parsing error"); g_markup_parse_context_free(ctx); sdp_record_free(record); free(ctx_data); return NULL; } g_markup_parse_context_free(ctx); free(ctx_data); return record; } static void convert_raw_data_to_xml(sdp_data_t *value, int indent_level, void *data, void (*appender)(void *, const char *)) { int i, hex; char buf[STRBUFSIZE]; char indent[MAXINDENT]; if (!value) return; if (indent_level >= MAXINDENT) indent_level = MAXINDENT - 2; for (i = 0; i < indent_level; i++) indent[i] = '\t'; indent[i] = '\0'; buf[STRBUFSIZE - 1] = '\0'; switch (value->dtd) { case SDP_DATA_NIL: appender(data, indent); appender(data, "\n"); break; case SDP_BOOL: appender(data, indent); appender(data, "val.uint8 ? "true" : "false"); appender(data, "\" />\n"); break; case SDP_UINT8: appender(data, indent); appender(data, "val.uint8); appender(data, buf); appender(data, "\" />\n"); break; case SDP_UINT16: appender(data, indent); appender(data, "val.uint16); appender(data, buf); appender(data, "\" />\n"); break; case SDP_UINT32: appender(data, indent); appender(data, "val.uint32); appender(data, buf); appender(data, "\" />\n"); break; case SDP_UINT64: appender(data, indent); appender(data, "val.uint64); appender(data, buf); appender(data, "\" />\n"); break; case SDP_UINT128: appender(data, indent); appender(data, "val.uint128.data[i]); } appender(data, buf); appender(data, "\" />\n"); break; case SDP_INT8: appender(data, indent); appender(data, "val.int8); appender(data, buf); appender(data, "\" />\n"); break; case SDP_INT16: appender(data, indent); appender(data, "val.int16); appender(data, buf); appender(data, "\" />\n"); break; case SDP_INT32: appender(data, indent); appender(data, "val.int32); appender(data, buf); appender(data, "\" />\n"); break; case SDP_INT64: appender(data, indent); appender(data, "val.int64); appender(data, buf); appender(data, "\" />\n"); break; case SDP_INT128: appender(data, indent); appender(data, "val.int128.data[i]); } appender(data, buf); appender(data, "\" />\n"); break; case SDP_UUID16: appender(data, indent); appender(data, "val.uuid.value.uuid16); appender(data, buf); appender(data, "\" />\n"); break; case SDP_UUID32: appender(data, indent); appender(data, "val.uuid.value.uuid32); appender(data, buf); appender(data, "\" />\n"); break; case SDP_UUID128: appender(data, indent); appender(data, "val.uuid.value. uuid128.data[0], (unsigned char) value->val.uuid.value. uuid128.data[1], (unsigned char) value->val.uuid.value. uuid128.data[2], (unsigned char) value->val.uuid.value. uuid128.data[3], (unsigned char) value->val.uuid.value. uuid128.data[4], (unsigned char) value->val.uuid.value. uuid128.data[5], (unsigned char) value->val.uuid.value. uuid128.data[6], (unsigned char) value->val.uuid.value. uuid128.data[7], (unsigned char) value->val.uuid.value. uuid128.data[8], (unsigned char) value->val.uuid.value. uuid128.data[9], (unsigned char) value->val.uuid.value. uuid128.data[10], (unsigned char) value->val.uuid.value. uuid128.data[11], (unsigned char) value->val.uuid.value. uuid128.data[12], (unsigned char) value->val.uuid.value. uuid128.data[13], (unsigned char) value->val.uuid.value. uuid128.data[14], (unsigned char) value->val.uuid.value. uuid128.data[15]); appender(data, buf); appender(data, "\" />\n"); break; case SDP_TEXT_STR8: case SDP_TEXT_STR16: case SDP_TEXT_STR32: { int num_chars_to_escape = 0; int length = value->unitSize - 1; char *strBuf; hex = 0; for (i = 0; i < length; i++) { if (!isprint(value->val.str[i]) && value->val.str[i] != '\0') { hex = 1; break; } /* XML is evil, must do this... */ if ((value->val.str[i] == '<') || (value->val.str[i] == '>') || (value->val.str[i] == '"') || (value->val.str[i] == '&')) num_chars_to_escape++; } appender(data, indent); appender(data, "unitSize-1) * 2 + 1)); if (!strBuf) { DBG("No memory to convert raw data to xml"); return; } /* Unit Size seems to include the size for dtd It is thus off by 1 This is safe for Normal strings, but not hex encoded data */ for (i = 0; i < (value->unitSize-1); i++) sprintf(&strBuf[i*sizeof(char)*2], "%02x", (unsigned char) value->val.str[i]); strBuf[(value->unitSize-1) * 2] = '\0'; } else { int j; /* escape the XML disallowed chars */ strBuf = malloc(sizeof(char) * (value->unitSize + 1 + num_chars_to_escape * 4)); if (!strBuf) { DBG("No memory to convert raw data to xml"); return; } for (i = 0, j = 0; i < length; i++) { if (value->val.str[i] == '&') { strBuf[j++] = '&'; strBuf[j++] = 'a'; strBuf[j++] = 'm'; strBuf[j++] = 'p'; } else if (value->val.str[i] == '<') { strBuf[j++] = '&'; strBuf[j++] = 'l'; strBuf[j++] = 't'; } else if (value->val.str[i] == '>') { strBuf[j++] = '&'; strBuf[j++] = 'g'; strBuf[j++] = 't'; } else if (value->val.str[i] == '"') { strBuf[j++] = '&'; strBuf[j++] = 'q'; strBuf[j++] = 'u'; strBuf[j++] = 'o'; strBuf[j++] = 't'; } else if (value->val.str[i] == '\0') { strBuf[j++] = ' '; } else { strBuf[j++] = value->val.str[i]; } } strBuf[j] = '\0'; } appender(data, "value=\""); appender(data, strBuf); appender(data, "\" />\n"); free(strBuf); break; } case SDP_URL_STR8: case SDP_URL_STR16: case SDP_URL_STR32: { char *strBuf; appender(data, indent); appender(data, "val.str, value->unitSize - 1); appender(data, strBuf); free(strBuf); appender(data, "\" />\n"); break; } case SDP_SEQ8: case SDP_SEQ16: case SDP_SEQ32: appender(data, indent); appender(data, "\n"); convert_raw_data_to_xml(value->val.dataseq, indent_level + 1, data, appender); appender(data, indent); appender(data, "\n"); break; case SDP_ALT8: case SDP_ALT16: case SDP_ALT32: appender(data, indent); appender(data, "\n"); convert_raw_data_to_xml(value->val.dataseq, indent_level + 1, data, appender); appender(data, indent); appender(data, "\n"); break; } convert_raw_data_to_xml(value->next, indent_level, data, appender); } struct conversion_data { void *data; void (*appender)(void *data, const char *); }; static void convert_raw_attr_to_xml_func(void *val, void *data) { struct conversion_data *cd = data; sdp_data_t *value = val; char buf[STRBUFSIZE]; buf[STRBUFSIZE - 1] = '\0'; snprintf(buf, STRBUFSIZE - 1, "\t\n", value->attrId); cd->appender(cd->data, buf); convert_raw_data_to_xml(value, 2, cd->data, cd->appender); cd->appender(cd->data, "\t\n"); } /* * Will convert the sdp record to XML. The appender and data can be used * to control where to output the record (e.g. file or a data buffer). The * appender will be called repeatedly with data and the character buffer * (containing parts of the generated XML) to append. */ void convert_sdp_record_to_xml(sdp_record_t *rec, void *data, void (*appender)(void *, const char *)) { struct conversion_data cd; cd.data = data; cd.appender = appender; if (rec && rec->attrlist) { appender(data, "\n\n"); appender(data, "\n"); sdp_list_foreach(rec->attrlist, convert_raw_attr_to_xml_func, &cd); appender(data, "\n"); } } bluez-5.82/src/PaxHeaders/sdpd.h0000644000000000000000000000005014165411566013551 xustar0020 atime=1743516521 20 ctime=1743591284 bluez-5.82/src/sdpd.h0000644000000000000000000000405414165411566013235 0ustar00rootroot/* SPDX-License-Identifier: GPL-2.0-or-later */ /* * * BlueZ - Bluetooth protocol stack for Linux * * Copyright (C) 2001-2002 Nokia Corporation * Copyright (C) 2002-2003 Maxim Krasnyansky * Copyright (C) 2002-2010 Marcel Holtmann * Copyright (C) 2002-2003 Stephen Crane * * */ #ifdef SDP_DEBUG #include #define SDPDBG(fmt, arg...) syslog(LOG_DEBUG, "%s: " fmt "\n", __func__ , ## arg) #else #define SDPDBG(fmt...) #endif typedef struct request { bdaddr_t device; bdaddr_t bdaddr; int local; int sock; int mtu; int flags; uint8_t *buf; int len; uint8_t opcode; } sdp_req_t; void sdp_cstate_cleanup(int sock); void handle_internal_request(int sk, int mtu, void *data, int len); void handle_request(int sk, uint8_t *data, int len); void set_fixed_db_timestamp(uint32_t dbts); int service_register_req(sdp_req_t *req, sdp_buf_t *rsp); int service_update_req(sdp_req_t *req, sdp_buf_t *rsp); int service_remove_req(sdp_req_t *req, sdp_buf_t *rsp); void register_public_browse_group(void); void register_server_service(void); void register_device_id(uint16_t source, uint16_t vendor, uint16_t product, uint16_t version); void register_mps(bool mpmd); int record_sort(const void *r1, const void *r2); void sdp_svcdb_reset(void); void sdp_svcdb_collect_all(int sock); void sdp_svcdb_set_collectable(sdp_record_t *rec, int sock); void sdp_svcdb_collect(sdp_record_t *rec); sdp_record_t *sdp_record_find(uint32_t handle); void sdp_record_add(const bdaddr_t *device, sdp_record_t *rec); int sdp_record_remove(uint32_t handle); sdp_list_t *sdp_get_record_list(void); int sdp_check_access(uint32_t handle, bdaddr_t *device); uint32_t sdp_next_handle(void); uint32_t sdp_get_time(void); #define SDP_SERVER_COMPAT (1 << 0) #define SDP_SERVER_CENTRAL (1 << 1) int start_sdp_server(uint16_t mtu, uint32_t flags); void stop_sdp_server(void); int add_record_to_server(const bdaddr_t *src, sdp_record_t *rec); int remove_record_from_server(uint32_t handle); bluez-5.82/src/PaxHeaders/gatt-database.h0000644000000000000000000000005014015011623015300 xustar0020 atime=1743516503 20 ctime=1743591284 bluez-5.82/src/gatt-database.h0000644000000000000000000000130514015011623014760 0ustar00rootroot/* SPDX-License-Identifier: GPL-2.0-or-later */ /* * * BlueZ - Bluetooth protocol stack for Linux * * Copyright (C) 2015 Google Inc. * * */ struct btd_gatt_database; struct btd_gatt_database *btd_gatt_database_new(struct btd_adapter *adapter); void btd_gatt_database_destroy(struct btd_gatt_database *database); struct gatt_db *btd_gatt_database_get_db(struct btd_gatt_database *database); void btd_gatt_database_att_disconnected(struct btd_gatt_database *database, struct btd_device *device); void btd_gatt_database_server_connected(struct btd_gatt_database *database, struct bt_gatt_server *server); void btd_gatt_database_restore_svc_chng_ccc(struct btd_gatt_database *database); bluez-5.82/src/PaxHeaders/bluetooth.ver0000644000000000000000000000005014447506754015200 xustar0020 atime=1743516871 20 ctime=1743591285 bluez-5.82/src/bluetooth.ver0000644000000000000000000000032614447506754014662 0ustar00rootroot{ global: btd_*; g_dbus_*; info; error; debug; baswap; ba2str; /* Don't break LLVM sanitizers */ __asan*; __dfsan*; __lsan*; __msan*; __sanitizer*; __tsan*; __ubsan*; local: *; }; bluez-5.82/src/PaxHeaders/textfile.h0000644000000000000000000000005014267331527014444 xustar0020 atime=1743516062 20 ctime=1743591283 bluez-5.82/src/textfile.h0000644000000000000000000000137514267331527014133 0ustar00rootroot/* SPDX-License-Identifier: GPL-2.0-or-later */ /* * * BlueZ - Bluetooth protocol stack for Linux * * Copyright (C) 2004-2010 Marcel Holtmann * * */ int create_filename(char *str, size_t size, const char *fmt, ...) __attribute__((format(printf, 3, 4))); int create_file(const char *filename, const mode_t mode); int create_name(char *buf, size_t size, const char *address, const char *name); int textfile_put(const char *pathname, const char *key, const char *value); int textfile_del(const char *pathname, const char *key); char *textfile_get(const char *pathname, const char *key); typedef void (*textfile_cb) (char *key, char *value, void *data); int textfile_foreach(const char *pathname, textfile_cb func, void *data); bluez-5.82/src/PaxHeaders/bluetooth.conf0000644000000000000000000000005014015011623015302 xustar0020 atime=1743516875 20 ctime=1743591288 bluez-5.82/src/bluetooth.conf0000644000000000000000000000223614015011623014766 0ustar00rootroot bluez-5.82/src/PaxHeaders/settings.c0000644000000000000000000000005014667536076014465 xustar0020 atime=1743516086 20 ctime=1743591283 bluez-5.82/src/settings.c0000644000000000000000000003147214667536076014155 0ustar00rootroot// SPDX-License-Identifier: LGPL-2.1-or-later /* * * BlueZ - Bluetooth protocol stack for Linux * * Copyright (C) 2022 Intel Corporation. * */ #ifdef HAVE_CONFIG_H #include #endif #include #include #include #include "lib/bluetooth.h" #include "lib/uuid.h" #include "log.h" #include "src/shared/queue.h" #include "src/shared/att.h" #include "src/shared/gatt-db.h" #include "settings.h" #define GATT_PRIM_SVC_UUID_STR "2800" #define GATT_SND_SVC_UUID_STR "2801" #define GATT_INCLUDE_UUID_STR "2802" #define GATT_CHARAC_UUID_STR "2803" static ssize_t str2val(const char *str, uint8_t *val, size_t len) { const char *pos = str; size_t i; for (i = 0; i < len; i++) { if (sscanf(pos, "%2hhx", &val[i]) != 1) break; pos += 2; } return i; } static void load_desc_value(struct gatt_db_attribute *attrib, int err, void *user_data) { } static int load_desc(struct gatt_db *db, char *handle, char *value, struct gatt_db_attribute *service) { char uuid_str[MAX_LEN_UUID_STR]; struct gatt_db_attribute *att; uint16_t handle_int; uint16_t val; bt_uuid_t uuid, ext_uuid; if (sscanf(handle, "%04hx", &handle_int) != 1) { DBG("Failed to parse handle: %s", handle); return -EIO; } /* Check if there is any value stored, otherwise it is just the UUID */ if (sscanf(value, "%04hx:%36s", &val, uuid_str) != 2) { if (sscanf(value, "%36s", uuid_str) != 1) { DBG("Failed to parse value: %s", value); return -EIO; } val = 0; } DBG("loading descriptor handle: 0x%04x, value: 0x%04x, value uuid: %s", handle_int, val, uuid_str); bt_string_to_uuid(&uuid, uuid_str); bt_uuid16_create(&ext_uuid, GATT_CHARAC_EXT_PROPER_UUID); /* If it is CEP then it must contain the value */ if (!bt_uuid_cmp(&uuid, &ext_uuid) && !val) return -EIO; att = gatt_db_service_insert_descriptor(service, handle_int, &uuid, 0, NULL, NULL, NULL); if (!att || gatt_db_attribute_get_handle(att) != handle_int) return -EIO; if (val) { if (!gatt_db_attribute_write(att, 0, (uint8_t *)&val, sizeof(val), 0, NULL, load_desc_value, NULL)) return -EIO; } return 0; } static int load_chrc(struct gatt_db *db, char *handle, char *value, struct gatt_db_attribute *service) { uint16_t properties, value_handle, handle_int; char uuid_str[MAX_LEN_UUID_STR]; struct gatt_db_attribute *att; char val_str[33]; uint8_t val[16]; size_t val_len; bt_uuid_t uuid; if (sscanf(handle, "%04hx", &handle_int) != 1) { DBG("Failed to parse handle: %s", handle); return -EIO; } /* Check if there is any value stored */ if (sscanf(value, GATT_CHARAC_UUID_STR ":%04hx:%02hx:%32s:%36s", &value_handle, &properties, val_str, uuid_str) != 4) { if (sscanf(value, GATT_CHARAC_UUID_STR ":%04hx:%02hx:%36s", &value_handle, &properties, uuid_str) != 3) return -EIO; val_len = 0; } else val_len = str2val(val_str, val, sizeof(val)); bt_string_to_uuid(&uuid, uuid_str); /* Log debug message. */ DBG("loading characteristic handle: 0x%04x, value handle: 0x%04x, " "properties 0x%04x value: %s uuid: %s", handle_int, value_handle, properties, val_len ? val_str : "", uuid_str); att = gatt_db_service_insert_characteristic(service, handle_int, value_handle, &uuid, 0, properties, NULL, NULL, NULL); if (!att || gatt_db_attribute_get_handle(att) != value_handle) return -EIO; if (val_len) { if (!gatt_db_attribute_write(att, 0, val, val_len, 0, NULL, load_desc_value, NULL)) return -EIO; } return 0; } static int load_incl(struct gatt_db *db, char *handle, char *value, struct gatt_db_attribute *service) { char uuid_str[MAX_LEN_UUID_STR]; struct gatt_db_attribute *att; uint16_t start, end; if (sscanf(handle, "%04hx", &start) != 1) { DBG("Failed to parse handle: %s", handle); return -EIO; } if (sscanf(value, GATT_INCLUDE_UUID_STR ":%04hx:%04hx:%36s", &start, &end, uuid_str) != 3) { DBG("Failed to parse value: %s", value); return -EIO; } /* Log debug message. */ DBG("loading included service: 0x%04x, end: 0x%04x, uuid: %s", start, end, uuid_str); att = gatt_db_get_attribute(db, start); if (!att) return -EIO; att = gatt_db_service_add_included(service, att); if (!att) return -EIO; return 0; } static int load_service(struct gatt_db *db, char *handle, char *value) { struct gatt_db_attribute *att; uint16_t start, end; char type[MAX_LEN_UUID_STR], uuid_str[MAX_LEN_UUID_STR]; bt_uuid_t uuid; bool primary; if (sscanf(handle, "%04hx", &start) != 1) { DBG("Failed to parse handle: %s", handle); return -EIO; } if (sscanf(value, "%36[^:]:%04hx:%36s", type, &end, uuid_str) != 3) { DBG("Failed to parse value: %s", value); return -EIO; } if (g_str_equal(type, GATT_PRIM_SVC_UUID_STR)) primary = true; else if (g_str_equal(type, GATT_SND_SVC_UUID_STR)) primary = false; else return -EIO; bt_string_to_uuid(&uuid, uuid_str); /* Log debug message. */ DBG("loading service: 0x%04x, end: 0x%04x, uuid: %s", start, end, uuid_str); att = gatt_db_insert_service(db, start, &uuid, primary, end - start + 1); if (!att) { DBG("Unable load service into db!"); return -EIO; } return 0; } static int gatt_db_load(struct gatt_db *db, GKeyFile *key_file, char **keys) { struct gatt_db_attribute *current_service; char **handle, *value, type[MAX_LEN_UUID_STR]; int ret; /* first load service definitions */ for (handle = keys; *handle; handle++) { value = g_key_file_get_string(key_file, "Attributes", *handle, NULL); if (!value || sscanf(value, "%36[^:]:", type) != 1) { g_free(value); return -EIO; } if (g_str_equal(type, GATT_PRIM_SVC_UUID_STR) || g_str_equal(type, GATT_SND_SVC_UUID_STR)) { ret = load_service(db, *handle, value); if (ret) { g_free(value); return ret; } } g_free(value); } current_service = NULL; /* then fill them with data*/ for (handle = keys; *handle; handle++) { value = g_key_file_get_string(key_file, "Attributes", *handle, NULL); if (!value || sscanf(value, "%36[^:]:", type) != 1) { g_free(value); return -EIO; } if (g_str_equal(type, GATT_PRIM_SVC_UUID_STR) || g_str_equal(type, GATT_SND_SVC_UUID_STR)) { uint16_t tmp; uint16_t start, end; bool primary; bt_uuid_t uuid; char uuid_str[MAX_LEN_UUID_STR]; if (sscanf(*handle, "%04hx", &tmp) != 1) { g_free(value); return -EIO; } if (current_service) gatt_db_service_set_active(current_service, true); current_service = gatt_db_get_attribute(db, tmp); gatt_db_attribute_get_service_data(current_service, &start, &end, &primary, &uuid); bt_uuid_to_string(&uuid, uuid_str, sizeof(uuid_str)); ret = 0; } else if (g_str_equal(type, GATT_INCLUDE_UUID_STR)) { ret = load_incl(db, *handle, value, current_service); } else if (g_str_equal(type, GATT_CHARAC_UUID_STR)) { ret = load_chrc(db, *handle, value, current_service); } else { ret = load_desc(db, *handle, value, current_service); } g_free(value); if (ret) { gatt_db_clear(db); return ret; } } if (current_service) gatt_db_service_set_active(current_service, true); return 0; } int btd_settings_gatt_db_load(struct gatt_db *db, const char *filename) { char **keys; GKeyFile *key_file; GError *gerr = NULL; int err; key_file = g_key_file_new(); if (!g_key_file_load_from_file(key_file, filename, 0, &gerr)) { DBG("Unable to load key file from %s: (%s)", filename, gerr->message); g_clear_error(&gerr); } keys = g_key_file_get_keys(key_file, "Attributes", NULL, NULL); if (!keys) { g_key_file_free(key_file); return -ENOENT; } err = gatt_db_load(db, key_file, keys); g_strfreev(keys); g_key_file_free(key_file); return err; } struct gatt_saver { struct gatt_db *db; uint16_t ext_props; GKeyFile *key_file; }; static void db_hash_read_value_cb(struct gatt_db_attribute *attrib, int err, const uint8_t *value, size_t length, void *user_data) { const uint8_t **hash = user_data; if (err || (length != 16)) return; *hash = value; } static void store_desc(struct gatt_db_attribute *attr, void *user_data) { struct gatt_saver *saver = user_data; GKeyFile *key_file = saver->key_file; char handle[6], value[100], uuid_str[MAX_LEN_UUID_STR]; const bt_uuid_t *uuid; bt_uuid_t ext_uuid; uint16_t handle_num; handle_num = gatt_db_attribute_get_handle(attr); sprintf(handle, "%04hx", handle_num); uuid = gatt_db_attribute_get_type(attr); bt_uuid_to_string(uuid, uuid_str, sizeof(uuid_str)); bt_uuid16_create(&ext_uuid, GATT_CHARAC_EXT_PROPER_UUID); if (!bt_uuid_cmp(uuid, &ext_uuid) && saver->ext_props) sprintf(value, "%04hx:%s", saver->ext_props, uuid_str); else sprintf(value, "%s", uuid_str); g_key_file_set_string(key_file, "Attributes", handle, value); } static void store_chrc(struct gatt_db_attribute *attr, void *user_data) { struct gatt_saver *saver = user_data; GKeyFile *key_file = saver->key_file; char handle[6], value[100], uuid_str[MAX_LEN_UUID_STR]; uint16_t handle_num, value_handle; uint8_t properties; bt_uuid_t uuid, hash_uuid; if (!gatt_db_attribute_get_char_data(attr, &handle_num, &value_handle, &properties, &saver->ext_props, &uuid)) { DBG("Unable to locate Characteristic data"); return; } sprintf(handle, "%04hx", handle_num); bt_uuid_to_string(&uuid, uuid_str, sizeof(uuid_str)); /* Store Database Hash value if available */ bt_uuid16_create(&hash_uuid, GATT_CHARAC_DB_HASH); if (!bt_uuid_cmp(&uuid, &hash_uuid)) { const uint8_t *hash = NULL; attr = gatt_db_get_attribute(saver->db, value_handle); gatt_db_attribute_read(attr, 0, BT_ATT_OP_READ_REQ, NULL, db_hash_read_value_cb, &hash); if (hash) sprintf(value, GATT_CHARAC_UUID_STR ":%04hx:%02hhx:" "%02hhx%02hhx%02hhx%02hhx%02hhx%02hhx%02hhx" "%02hhx%02hhx%02hhx%02hhx%02hhx%02hhx%02hhx" "%02hhx%02hhx:%s", value_handle, properties, hash[0], hash[1], hash[2], hash[3], hash[4], hash[5], hash[6], hash[7], hash[8], hash[9], hash[10], hash[11], hash[12], hash[13], hash[14], hash[15], uuid_str); else sprintf(value, GATT_CHARAC_UUID_STR ":%04hx:%02hhx:%s", value_handle, properties, uuid_str); } else sprintf(value, GATT_CHARAC_UUID_STR ":%04hx:%02hhx:%s", value_handle, properties, uuid_str); g_key_file_set_string(key_file, "Attributes", handle, value); gatt_db_service_foreach_desc(attr, store_desc, saver); } static void store_incl(struct gatt_db_attribute *attr, void *user_data) { struct gatt_saver *saver = user_data; GKeyFile *key_file = saver->key_file; struct gatt_db_attribute *service; char handle[6], value[100], uuid_str[MAX_LEN_UUID_STR]; uint16_t handle_num, start, end; bt_uuid_t uuid; if (!gatt_db_attribute_get_incl_data(attr, &handle_num, &start, &end)) { DBG("Unable to locate Included data"); return; } service = gatt_db_get_attribute(saver->db, start); if (!service) { DBG("Unable to locate Included Service"); return; } sprintf(handle, "%04hx", handle_num); gatt_db_attribute_get_service_uuid(service, &uuid); bt_uuid_to_string(&uuid, uuid_str, sizeof(uuid_str)); sprintf(value, GATT_INCLUDE_UUID_STR ":%04hx:%04hx:%s", start, end, uuid_str); g_key_file_set_string(key_file, "Attributes", handle, value); } static void store_service(struct gatt_db_attribute *attr, void *user_data) { struct gatt_saver *saver = user_data; GKeyFile *key_file = saver->key_file; char uuid_str[MAX_LEN_UUID_STR], handle[6], value[256]; uint16_t start, end; bt_uuid_t uuid; bool primary; char *type; if (!gatt_db_attribute_get_service_data(attr, &start, &end, &primary, &uuid)) { DBG("Unable to locate Service data"); return; } sprintf(handle, "%04hx", start); bt_uuid_to_string(&uuid, uuid_str, sizeof(uuid_str)); if (primary) type = GATT_PRIM_SVC_UUID_STR; else type = GATT_SND_SVC_UUID_STR; sprintf(value, "%s:%04hx:%s", type, end, uuid_str); g_key_file_set_string(key_file, "Attributes", handle, value); gatt_db_service_foreach_incl(attr, store_incl, saver); gatt_db_service_foreach_char(attr, store_chrc, saver); } void btd_settings_gatt_db_store(struct gatt_db *db, const char *filename) { GKeyFile *key_file; GError *gerr = NULL; char *data; gsize length = 0; struct gatt_saver saver; key_file = g_key_file_new(); if (!g_key_file_load_from_file(key_file, filename, 0, &gerr)) { DBG("Unable to load key file from %s: (%s)", filename, gerr->message); g_clear_error(&gerr); } /* Remove current attributes since it might have changed */ g_key_file_remove_group(key_file, "Attributes", NULL); saver.key_file = key_file; saver.db = db; gatt_db_foreach_service(db, NULL, store_service, &saver); data = g_key_file_to_data(key_file, &length, NULL); if (!g_file_set_contents(filename, data, length, &gerr)) { DBG("Unable set contents for %s: (%s)", filename, gerr->message); g_error_free(gerr); } g_free(data); g_key_file_free(key_file); } bluez-5.82/src/PaxHeaders/advertising.h0000644000000000000000000000005014015011623015116 xustar0020 atime=1743516630 20 ctime=1743591284 bluez-5.82/src/advertising.h0000644000000000000000000000064614015011623014605 0ustar00rootroot/* SPDX-License-Identifier: GPL-2.0-or-later */ /* * * BlueZ - Bluetooth protocol stack for Linux * * Copyright (C) 2015 Google Inc. * * */ struct btd_adapter; struct btd_adv_manager; struct btd_adv_manager *btd_adv_manager_new(struct btd_adapter *adapter, struct mgmt *mgmt); void btd_adv_manager_destroy(struct btd_adv_manager *manager); void btd_adv_manager_refresh(struct btd_adv_manager *manager); bluez-5.82/src/PaxHeaders/backtrace.h0000644000000000000000000000005014015011623014516 xustar0020 atime=1743516604 20 ctime=1743591284 bluez-5.82/src/backtrace.h0000644000000000000000000000077414015011623014207 0ustar00rootroot/* SPDX-License-Identifier: GPL-2.0-or-later */ /* * * BlueZ - Bluetooth protocol stack for Linux * * Copyright (C) 2004-2010 Marcel Holtmann * * */ #include void btd_backtrace_init(void); void btd_backtrace(uint16_t index); void btd_assertion_message_expr(const char *file, int line, const char *func, const char *expr); #define btd_assert(expr) do { \ if (expr) ; else \ btd_assertion_message_expr(__FILE__, __LINE__, __func__, #expr); \ } while (0) bluez-5.82/src/PaxHeaders/error.h0000644000000000000000000000005014471706460013750 xustar0020 atime=1743516279 20 ctime=1743591284 bluez-5.82/src/error.h0000644000000000000000000001033014471706460013426 0ustar00rootroot/* SPDX-License-Identifier: GPL-2.0-or-later */ /* * * BlueZ - Bluetooth protocol stack for Linux * * Copyright (C) 2006-2010 Nokia Corporation * Copyright (C) 2004-2010 Marcel Holtmann * Copyright (C) 2007-2008 Fabien Chevalier * * */ #include #include #define ERROR_INTERFACE "org.bluez.Error" /* BR/EDR connection failure reasons */ #define ERR_BREDR_CONN_ALREADY_CONNECTED "br-connection-already-"\ "connected" #define ERR_BREDR_CONN_PAGE_TIMEOUT "br-connection-page-timeout" #define ERR_BREDR_CONN_PROFILE_UNAVAILABLE "br-connection-profile-"\ "unavailable" #define ERR_BREDR_CONN_SDP_SEARCH "br-connection-sdp-search" #define ERR_BREDR_CONN_CREATE_SOCKET "br-connection-create-socket" #define ERR_BREDR_CONN_INVALID_ARGUMENTS "br-connection-invalid-"\ "argument" #define ERR_BREDR_CONN_ADAPTER_NOT_POWERED "br-connection-adapter-not-"\ "powered" #define ERR_BREDR_CONN_NOT_SUPPORTED "br-connection-not-supported" #define ERR_BREDR_CONN_BAD_SOCKET "br-connection-bad-socket" #define ERR_BREDR_CONN_MEMORY_ALLOC "br-connection-memory-"\ "allocation" #define ERR_BREDR_CONN_BUSY "br-connection-busy" #define ERR_BREDR_CONN_CNCR_CONNECT_LIMIT "br-connection-concurrent-"\ "connection-limit" #define ERR_BREDR_CONN_TIMEOUT "br-connection-timeout" #define ERR_BREDR_CONN_REFUSED "br-connection-refused" #define ERR_BREDR_CONN_ABORT_BY_REMOTE "br-connection-aborted-by-"\ "remote" #define ERR_BREDR_CONN_ABORT_BY_LOCAL "br-connection-aborted-by-"\ "local" #define ERR_BREDR_CONN_LMP_PROTO_ERROR "br-connection-lmp-protocol-"\ "error" #define ERR_BREDR_CONN_CANCELED "br-connection-canceled" #define ERR_BREDR_CONN_KEY_MISSING "br-connection-key-missing" #define ERR_BREDR_CONN_UNKNOWN "br-connection-unknown" /* LE connection failure reasons */ #define ERR_LE_CONN_INVALID_ARGUMENTS "le-connection-invalid-arguments" #define ERR_LE_CONN_ADAPTER_NOT_POWERED "le-connection-adapter-not-powered" #define ERR_LE_CONN_NOT_SUPPORTED "le-connection-not-supported" #define ERR_LE_CONN_ALREADY_CONNECTED "le-connection-already-connected" #define ERR_LE_CONN_BAD_SOCKET "le-connection-bad-socket" #define ERR_LE_CONN_MEMORY_ALLOC "le-connection-memory-allocation" #define ERR_LE_CONN_BUSY "le-connection-busy" #define ERR_LE_CONN_REFUSED "le-connection-refused" #define ERR_LE_CONN_CREATE_SOCKET "le-connection-create-socket" #define ERR_LE_CONN_TIMEOUT "le-connection-timeout" #define ERR_LE_CONN_SYNC_CONNECT_LIMIT "le-connection-concurrent-connection-"\ "limit" #define ERR_LE_CONN_ABORT_BY_REMOTE "le-connection-abort-by-remote" #define ERR_LE_CONN_ABORT_BY_LOCAL "le-connection-abort-by-local" #define ERR_LE_CONN_LL_PROTO_ERROR "le-connection-link-layer-protocol-"\ "error" #define ERR_LE_CONN_GATT_BROWSE "le-connection-gatt-browsing" #define ERR_LE_CONN_KEY_MISSING "le-connection-key-missing" #define ERR_LE_CONN_UNKNOWN "le-connection-unknown" DBusMessage *btd_error_invalid_args(DBusMessage *msg); DBusMessage *btd_error_invalid_args_str(DBusMessage *msg, const char *str); DBusMessage *btd_error_busy(DBusMessage *msg); DBusMessage *btd_error_already_exists(DBusMessage *msg); DBusMessage *btd_error_not_supported(DBusMessage *msg); DBusMessage *btd_error_not_connected(DBusMessage *msg); DBusMessage *btd_error_already_connected(DBusMessage *msg); DBusMessage *btd_error_not_available(DBusMessage *msg); DBusMessage *btd_error_not_available_str(DBusMessage *msg, const char *str); DBusMessage *btd_error_in_progress(DBusMessage *msg); DBusMessage *btd_error_in_progress_str(DBusMessage *msg, const char *str); DBusMessage *btd_error_does_not_exist(DBusMessage *msg); DBusMessage *btd_error_not_authorized(DBusMessage *msg); DBusMessage *btd_error_not_permitted(DBusMessage *msg, const char *str); DBusMessage *btd_error_no_such_adapter(DBusMessage *msg); DBusMessage *btd_error_agent_not_available(DBusMessage *msg); DBusMessage *btd_error_not_ready(DBusMessage *msg); DBusMessage *btd_error_not_ready_str(DBusMessage *msg, const char *str); DBusMessage *btd_error_failed(DBusMessage *msg, const char *str); const char *btd_error_bredr_conn_from_errno(int errno_code); const char *btd_error_le_conn_from_errno(int errno_code); bluez-5.82/src/PaxHeaders/agent.h0000644000000000000000000000005014015011623013675 xustar0020 atime=1743516604 20 ctime=1743591284 bluez-5.82/src/agent.h0000644000000000000000000000412714015011623013362 0ustar00rootroot/* SPDX-License-Identifier: GPL-2.0-or-later */ /* * * BlueZ - Bluetooth protocol stack for Linux * * Copyright (C) 2006-2010 Nokia Corporation * Copyright (C) 2004-2010 Marcel Holtmann * * */ #define IO_CAPABILITY_DISPLAYONLY 0x00 #define IO_CAPABILITY_DISPLAYYESNO 0x01 #define IO_CAPABILITY_KEYBOARDONLY 0x02 #define IO_CAPABILITY_NOINPUTNOOUTPUT 0x03 #define IO_CAPABILITY_KEYBOARDDISPLAY 0x04 #define IO_CAPABILITY_INVALID 0xFF struct agent; typedef void (*agent_cb) (struct agent *agent, DBusError *err, void *user_data); typedef void (*agent_pincode_cb) (struct agent *agent, DBusError *err, const char *pincode, void *user_data); typedef void (*agent_passkey_cb) (struct agent *agent, DBusError *err, uint32_t passkey, void *user_data); struct agent *agent_ref(struct agent *agent); void agent_unref(struct agent *agent); struct agent *agent_get(const char *owner); int agent_authorize_service(struct agent *agent, struct btd_device *device, const char *uuid, agent_cb cb, void *user_data, GDestroyNotify destroy); int agent_request_pincode(struct agent *agent, struct btd_device *device, agent_pincode_cb cb, gboolean secure, void *user_data, GDestroyNotify destroy); int agent_request_passkey(struct agent *agent, struct btd_device *device, agent_passkey_cb cb, void *user_data, GDestroyNotify destroy); int agent_request_confirmation(struct agent *agent, struct btd_device *device, uint32_t passkey, agent_cb cb, void *user_data, GDestroyNotify destroy); int agent_request_authorization(struct agent *agent, struct btd_device *device, agent_cb cb, void *user_data, GDestroyNotify destroy); int agent_display_passkey(struct agent *agent, struct btd_device *device, uint32_t passkey, uint16_t entered); int agent_display_pincode(struct agent *agent, struct btd_device *device, const char *pincode, agent_cb cb, void *user_data, GDestroyNotify destroy); int agent_cancel(struct agent *agent); uint8_t agent_get_io_capability(struct agent *agent); void btd_agent_init(void); void btd_agent_cleanup(void); bluez-5.82/src/PaxHeaders/gatt-client.c0000644000000000000000000000005014766002272015022 xustar0020 atime=1743515578 20 ctime=1743591285 bluez-5.82/src/gatt-client.c0000644000000000000000000017174514766002272014522 0ustar00rootroot// SPDX-License-Identifier: GPL-2.0-or-later /* * * BlueZ - Bluetooth protocol stack for Linux * * Copyright (C) 2014 Google Inc. * * */ #ifdef HAVE_CONFIG_H #include #endif #define _GNU_SOURCE #include #include #include #include #include #include #include "lib/bluetooth.h" #include "lib/sdp.h" #include "lib/uuid.h" #include "gdbus/gdbus.h" #include "btio/btio.h" #include "log.h" #include "error.h" #include "btd.h" #include "adapter.h" #include "device.h" #include "src/shared/io.h" #include "src/shared/queue.h" #include "src/shared/att.h" #include "src/shared/gatt-db.h" #include "src/shared/gatt-client.h" #include "src/shared/util.h" #include "gatt-client.h" #include "dbus-common.h" #ifndef NELEM #define NELEM(x) (sizeof(x) / sizeof((x)[0])) #endif #define GATT_SERVICE_IFACE "org.bluez.GattService1" #define GATT_CHARACTERISTIC_IFACE "org.bluez.GattCharacteristic1" #define GATT_DESCRIPTOR_IFACE "org.bluez.GattDescriptor1" struct btd_gatt_client { struct btd_device *device; uint8_t features; bool ready; char devaddr[18]; struct gatt_db *db; struct bt_gatt_client *gatt; struct queue *services; struct queue *all_notify_clients; struct queue *ios; }; struct service { struct btd_gatt_client *client; bool primary; bool claimed; uint16_t start_handle; uint16_t end_handle; bt_uuid_t uuid; char *path; struct queue *chrcs; struct queue *incl_services; }; typedef bool (*async_dbus_op_complete_t)(void *data); struct async_dbus_op { int ref_count; unsigned int id; struct queue *msgs; void *data; uint16_t offset; async_dbus_op_complete_t complete; }; struct sock_io { DBusMessage *msg; struct io *io; void (*destroy)(void *data); void *data; }; struct characteristic { struct service *service; struct gatt_db_attribute *attr; uint16_t handle; uint16_t value_handle; uint8_t props; uint16_t ext_props; uint16_t ext_props_handle; bt_uuid_t uuid; char *path; unsigned int ready_id; unsigned int exchange_id; struct sock_io *write_io; struct sock_io *notify_io; struct async_dbus_op *read_op; struct async_dbus_op *write_op; struct queue *descs; bool notifying; struct queue *notify_clients; }; struct descriptor { struct characteristic *chrc; struct gatt_db_attribute *attr; uint16_t handle; bt_uuid_t uuid; char *path; struct async_dbus_op *read_op; struct async_dbus_op *write_op; }; static bool uuid_cmp(const bt_uuid_t *uuid, uint16_t u16) { bt_uuid_t uuid16; bt_uuid16_create(&uuid16, u16); return bt_uuid_cmp(uuid, &uuid16) == 0; } static gboolean descriptor_get_handle(const GDBusPropertyTable *property, DBusMessageIter *iter, void *data) { struct descriptor *desc = data; uint16_t handle = desc->handle; dbus_message_iter_append_basic(iter, DBUS_TYPE_UINT16, &handle); return TRUE; } static gboolean descriptor_get_uuid(const GDBusPropertyTable *property, DBusMessageIter *iter, void *data) { char uuid[MAX_LEN_UUID_STR + 1]; const char *ptr = uuid; struct descriptor *desc = data; bt_uuid_to_string(&desc->uuid, uuid, sizeof(uuid)); dbus_message_iter_append_basic(iter, DBUS_TYPE_STRING, &ptr); return TRUE; } static gboolean descriptor_get_characteristic( const GDBusPropertyTable *property, DBusMessageIter *iter, void *data) { struct descriptor *desc = data; const char *str = desc->chrc->path; dbus_message_iter_append_basic(iter, DBUS_TYPE_OBJECT_PATH, &str); return TRUE; } static void read_cb(struct gatt_db_attribute *attrib, int err, const uint8_t *value, size_t length, void *user_data) { DBusMessageIter *array = user_data; if (err) return; dbus_message_iter_append_fixed_array(array, DBUS_TYPE_BYTE, &value, length); } static gboolean descriptor_get_value(const GDBusPropertyTable *property, DBusMessageIter *iter, void *data) { struct descriptor *desc = data; DBusMessageIter array; dbus_message_iter_open_container(iter, DBUS_TYPE_ARRAY, "y", &array); gatt_db_attribute_read(desc->attr, 0, 0, NULL, read_cb, &array); dbus_message_iter_close_container(iter, &array); return TRUE; } static void read_check_cb(struct gatt_db_attribute *attrib, int err, const uint8_t *value, size_t length, void *user_data) { gboolean *ret = user_data; if (err) { *ret = FALSE; return; } *ret = TRUE; } static gboolean descriptor_value_exists(const GDBusPropertyTable *property, void *data) { struct descriptor *desc = data; gboolean ret; gatt_db_attribute_read(desc->attr, 0, 0, NULL, read_check_cb, &ret); return ret; } static int parse_value_arg(DBusMessageIter *iter, uint8_t **value, int *len) { DBusMessageIter array; if (dbus_message_iter_get_arg_type(iter) != DBUS_TYPE_ARRAY) return -EINVAL; dbus_message_iter_recurse(iter, &array); dbus_message_iter_get_fixed_array(&array, value, len); return 0; } static void async_dbus_op_free(void *data) { struct async_dbus_op *op = data; queue_destroy(op->msgs, (void *)dbus_message_unref); free(op); } static struct async_dbus_op *async_dbus_op_ref(struct async_dbus_op *op) { __sync_fetch_and_add(&op->ref_count, 1); return op; } static void async_dbus_op_unref(void *data) { struct async_dbus_op *op = data; if (__sync_sub_and_fetch(&op->ref_count, 1)) return; async_dbus_op_free(op); } static void message_append_byte_array(DBusMessage *msg, const uint8_t *bytes, size_t len) { DBusMessageIter iter, array; dbus_message_iter_init_append(msg, &iter); dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY, "y", &array); dbus_message_iter_append_fixed_array(&array, DBUS_TYPE_BYTE, &bytes, len); dbus_message_iter_close_container(&iter, &array); } static DBusMessage *create_gatt_dbus_error(DBusMessage *msg, uint8_t att_ecode) { switch (att_ecode) { case BT_ATT_ERROR_READ_NOT_PERMITTED: return btd_error_not_permitted(msg, "Read not permitted"); case BT_ATT_ERROR_WRITE_NOT_PERMITTED: return btd_error_not_permitted(msg, "Write not permitted"); case BT_ATT_ERROR_AUTHENTICATION: case BT_ATT_ERROR_INSUFFICIENT_ENCRYPTION: case BT_ATT_ERROR_INSUFFICIENT_ENCRYPTION_KEY_SIZE: return btd_error_not_permitted(msg, "Not paired"); case BT_ATT_ERROR_INVALID_OFFSET: return btd_error_invalid_args_str(msg, "Invalid offset"); case BT_ATT_ERROR_INVALID_ATTRIBUTE_VALUE_LEN: return btd_error_invalid_args_str(msg, "Invalid Length"); case BT_ATT_ERROR_AUTHORIZATION: return btd_error_not_authorized(msg); case BT_ATT_ERROR_REQUEST_NOT_SUPPORTED: return btd_error_not_supported(msg); case 0: return btd_error_failed(msg, "Operation failed"); default: return g_dbus_create_error(msg, ERROR_INTERFACE ".Failed", "Operation failed with ATT error: 0x%02x", att_ecode); } return NULL; } static void write_descriptor_cb(struct gatt_db_attribute *attr, int err, void *user_data) { struct descriptor *desc = user_data; if (err) return; g_dbus_emit_property_changed(btd_get_dbus_connection(), desc->path, GATT_DESCRIPTOR_IFACE, "Value"); } static void async_dbus_op_reply(struct async_dbus_op *op, int err, const uint8_t *value, ssize_t length) { const struct queue_entry *entry; DBusMessage *reply; op->id = 0; for (entry = queue_get_entries(op->msgs); entry; entry = entry->next) { DBusMessage *msg = entry->data; if (err) { reply = err > 0 ? create_gatt_dbus_error(msg, err) : btd_error_failed(msg, strerror(-err)); goto send_reply; } reply = g_dbus_create_reply(msg, DBUS_TYPE_INVALID); if (!reply) { error("Failed to allocate D-Bus message reply"); return; } if (length >= 0) message_append_byte_array(reply, value, length); send_reply: g_dbus_send_message(btd_get_dbus_connection(), reply); } } static void read_op_cb(struct gatt_db_attribute *attrib, int err, const uint8_t *value, size_t length, void *user_data) { struct async_dbus_op *op = user_data; async_dbus_op_reply(op, err, value, length); } static void desc_read_cb(bool success, uint8_t att_ecode, const uint8_t *value, uint16_t length, void *user_data) { struct async_dbus_op *op = user_data; struct descriptor *desc = op->data; if (!success) goto fail; if (!op->offset) gatt_db_attribute_reset(desc->attr); if (!gatt_db_attribute_write(desc->attr, op->offset, value, length, 0, NULL, write_descriptor_cb, desc)) { error("Failed to store attribute"); att_ecode = BT_ATT_ERROR_UNLIKELY; goto fail; } /* Read the stored data from db */ if (!gatt_db_attribute_read(desc->attr, op->offset, 0, NULL, read_op_cb, op)) { error("Failed to read database"); att_ecode = BT_ATT_ERROR_UNLIKELY; goto fail; } desc->read_op = NULL; return; fail: async_dbus_op_reply(op, att_ecode, NULL, 0); desc->read_op = NULL; } static int parse_options(DBusMessageIter *iter, uint16_t *offset, const char **type) { DBusMessageIter dict; if (dbus_message_iter_get_arg_type(iter) != DBUS_TYPE_ARRAY) return -EINVAL; dbus_message_iter_recurse(iter, &dict); while (dbus_message_iter_get_arg_type(&dict) == DBUS_TYPE_DICT_ENTRY) { const char *key; DBusMessageIter value, entry; int var; dbus_message_iter_recurse(&dict, &entry); dbus_message_iter_get_basic(&entry, &key); dbus_message_iter_next(&entry); dbus_message_iter_recurse(&entry, &value); var = dbus_message_iter_get_arg_type(&value); if (strcasecmp(key, "offset") == 0) { if (var != DBUS_TYPE_UINT16) return -EINVAL; dbus_message_iter_get_basic(&value, offset); } if (type && strcasecmp(key, "type") == 0) { if (var != DBUS_TYPE_STRING) return -EINVAL; dbus_message_iter_get_basic(&value, type); } dbus_message_iter_next(&dict); } return 0; } static struct async_dbus_op *async_dbus_op_new(DBusMessage *msg, void *data) { struct async_dbus_op *op; op = new0(struct async_dbus_op, 1); op->msgs = queue_new(); queue_push_tail(op->msgs, dbus_message_ref(msg)); op->data = data; return op; } static struct async_dbus_op *read_value(struct bt_gatt_client *gatt, DBusMessage *msg, uint16_t handle, uint16_t offset, bt_gatt_client_read_callback_t callback, void *data) { struct async_dbus_op *op; op = async_dbus_op_new(msg, data); op->offset = offset; op->id = bt_gatt_client_read_long_value(gatt, handle, offset, callback, async_dbus_op_ref(op), async_dbus_op_unref); if (op->id) return op; async_dbus_op_free(op); return NULL; } static DBusMessage *descriptor_read_value(DBusConnection *conn, DBusMessage *msg, void *user_data) { struct descriptor *desc = user_data; struct bt_gatt_client *gatt = desc->chrc->service->client->gatt; DBusMessageIter iter; uint16_t offset = 0; if (!gatt) return btd_error_failed(msg, "Not connected"); dbus_message_iter_init(msg, &iter); if (parse_options(&iter, &offset, NULL)) return btd_error_invalid_args(msg); if (desc->read_op) { if (desc->read_op->offset != offset) return btd_error_in_progress(msg); queue_push_tail(desc->read_op->msgs, dbus_message_ref(msg)); return NULL; } desc->read_op = read_value(gatt, msg, desc->handle, offset, desc_read_cb, desc); if (!desc->read_op) return btd_error_failed(msg, "Failed to send read request"); return NULL; } static void write_result_cb(bool success, bool reliable_error, uint8_t att_ecode, void *user_data) { struct async_dbus_op *op = user_data; int err = 0; if (op->complete && !op->complete(op->data)) { err = -EFAULT; goto done; } if (!success) { if (reliable_error) err = -EFAULT; else err = att_ecode; } done: async_dbus_op_reply(op, err, NULL, -1); } static void write_cb(bool success, uint8_t att_ecode, void *user_data) { write_result_cb(success, false, att_ecode, user_data); } static struct async_dbus_op *start_long_write(DBusMessage *msg, uint16_t handle, struct bt_gatt_client *gatt, bool reliable, const uint8_t *value, size_t value_len, uint16_t offset, void *data, async_dbus_op_complete_t complete) { struct async_dbus_op *op; op = async_dbus_op_new(msg, data); op->complete = complete; op->offset = offset; op->id = bt_gatt_client_write_long_value(gatt, reliable, handle, offset, value, value_len, write_result_cb, op, async_dbus_op_free); if (!op->id) { async_dbus_op_free(op); return NULL; } return op; } static struct async_dbus_op *start_write_request(DBusMessage *msg, uint16_t handle, struct bt_gatt_client *gatt, const uint8_t *value, size_t value_len, void *data, async_dbus_op_complete_t complete) { struct async_dbus_op *op; op = async_dbus_op_new(msg, data); op->complete = complete; op->id = bt_gatt_client_write_value(gatt, handle, value, value_len, write_cb, op, async_dbus_op_free); if (!op->id) { async_dbus_op_free(op); return NULL; } return op; } static bool desc_write_complete(void *data) { struct descriptor *desc = data; desc->write_op = NULL; /* * The descriptor might have been unregistered during the read. Return * failure. */ return !!desc->chrc; } static DBusMessage *descriptor_write_value(DBusConnection *conn, DBusMessage *msg, void *user_data) { struct descriptor *desc = user_data; struct bt_gatt_client *gatt = desc->chrc->service->client->gatt; DBusMessageIter iter; uint8_t *value = NULL; int value_len = 0; uint16_t offset = 0; if (!gatt) return btd_error_failed(msg, "Not connected"); if (desc->write_op) return btd_error_in_progress(msg); dbus_message_iter_init(msg, &iter); if (parse_value_arg(&iter, &value, &value_len)) return btd_error_invalid_args(msg); dbus_message_iter_next(&iter); if (parse_options(&iter, &offset, NULL)) return btd_error_invalid_args(msg); /* * Check if service was previously claimed by a plugin and if we shall * consider it read-only, in that case return not authorized. */ if (desc->chrc->service->claimed && btd_opts.gatt_export == BT_GATT_EXPORT_READ_ONLY) return btd_error_not_authorized(msg); /* * Don't allow writing to Client Characteristic Configuration * descriptors. We achieve this through the StartNotify and StopNotify * methods on GattCharacteristic1. */ if (uuid_cmp(&desc->uuid, GATT_CLIENT_CHARAC_CFG_UUID)) return btd_error_not_permitted(msg, "Write not permitted"); /* * Based on the value length and the MTU, either use a write or a long * write. */ if (value_len <= bt_gatt_client_get_mtu(gatt) - 3 && !offset) desc->write_op = start_write_request(msg, desc->handle, gatt, value, value_len, desc, desc_write_complete); else desc->write_op = start_long_write(msg, desc->handle, gatt, false, value, value_len, offset, desc, desc_write_complete); if (!desc->write_op) return btd_error_failed(msg, "Failed to initiate write"); return NULL; } static const GDBusPropertyTable descriptor_properties[] = { { "Handle", "q", descriptor_get_handle }, { "UUID", "s", descriptor_get_uuid }, { "Characteristic", "o", descriptor_get_characteristic, }, { "Value", "ay", descriptor_get_value, NULL, descriptor_value_exists }, { } }; static const GDBusMethodTable descriptor_methods[] = { { GDBUS_ASYNC_METHOD("ReadValue", GDBUS_ARGS({ "options", "a{sv}" }), GDBUS_ARGS({ "value", "ay" }), descriptor_read_value) }, { GDBUS_ASYNC_METHOD("WriteValue", GDBUS_ARGS({ "value", "ay" }, { "options", "a{sv}" }), NULL, descriptor_write_value) }, { } }; static void descriptor_free(void *data) { struct descriptor *desc = data; g_free(desc->path); free(desc); } static struct descriptor *descriptor_create(struct gatt_db_attribute *attr, struct characteristic *chrc) { struct descriptor *desc; desc = new0(struct descriptor, 1); desc->chrc = chrc; desc->attr = attr; desc->handle = gatt_db_attribute_get_handle(attr); bt_uuid_to_uuid128(gatt_db_attribute_get_type(attr), &desc->uuid); desc->path = g_strdup_printf("%s/desc%04x", chrc->path, desc->handle); if (!g_dbus_register_interface(btd_get_dbus_connection(), desc->path, GATT_DESCRIPTOR_IFACE, descriptor_methods, NULL, descriptor_properties, desc, descriptor_free)) { error("Unable to register GATT descriptor with handle 0x%04x", desc->handle); descriptor_free(desc); return NULL; } DBG("Exported GATT characteristic descriptor: %s", desc->path); if (uuid_cmp(&desc->uuid, GATT_CHARAC_EXT_PROPER_UUID)) chrc->ext_props_handle = desc->handle; return desc; } static void unregister_descriptor(void *data) { struct descriptor *desc = data; struct bt_gatt_client *gatt = desc->chrc->service->client->gatt; DBG("Removing GATT descriptor: %s", desc->path); if (desc->read_op) bt_gatt_client_cancel(gatt, desc->read_op->id); if (desc->write_op) bt_gatt_client_cancel(gatt, desc->write_op->id); desc->chrc = NULL; g_dbus_unregister_interface(btd_get_dbus_connection(), desc->path, GATT_DESCRIPTOR_IFACE); } static gboolean characteristic_get_handle(const GDBusPropertyTable *property, DBusMessageIter *iter, void *data) { struct characteristic *chrc = data; uint16_t handle = chrc->handle; dbus_message_iter_append_basic(iter, DBUS_TYPE_UINT16, &handle); return TRUE; } static gboolean characteristic_get_uuid(const GDBusPropertyTable *property, DBusMessageIter *iter, void *data) { char uuid[MAX_LEN_UUID_STR + 1]; const char *ptr = uuid; struct characteristic *chrc = data; bt_uuid_to_string(&chrc->uuid, uuid, sizeof(uuid)); dbus_message_iter_append_basic(iter, DBUS_TYPE_STRING, &ptr); return TRUE; } static gboolean characteristic_get_service(const GDBusPropertyTable *property, DBusMessageIter *iter, void *data) { struct characteristic *chrc = data; const char *str = chrc->service->path; dbus_message_iter_append_basic(iter, DBUS_TYPE_OBJECT_PATH, &str); return TRUE; } static gboolean characteristic_get_value(const GDBusPropertyTable *property, DBusMessageIter *iter, void *data) { struct characteristic *chrc = data; DBusMessageIter array; dbus_message_iter_open_container(iter, DBUS_TYPE_ARRAY, "y", &array); gatt_db_attribute_read(chrc->attr, 0, 0, NULL, read_cb, &array); dbus_message_iter_close_container(iter, &array); return TRUE; } static gboolean characteristic_value_exists(const GDBusPropertyTable *property, void *data) { struct characteristic *chrc = data; gboolean ret; gatt_db_attribute_read(chrc->attr, 0, 0, NULL, read_check_cb, &ret); return ret; } static gboolean characteristic_get_notifying(const GDBusPropertyTable *property, DBusMessageIter *iter, void *data) { struct characteristic *chrc = data; dbus_bool_t notifying = chrc->notifying ? TRUE : FALSE; dbus_message_iter_append_basic(iter, DBUS_TYPE_BOOLEAN, ¬ifying); return TRUE; } static gboolean characteristic_notifying_exists(const GDBusPropertyTable *property, void *data) { struct characteristic *chrc = data; return (chrc->props & BT_GATT_CHRC_PROP_NOTIFY || chrc->props & BT_GATT_CHRC_PROP_INDICATE); } struct chrc_prop_data { uint8_t prop; char *str; }; static struct chrc_prop_data chrc_props[] = { /* Default Properties */ { BT_GATT_CHRC_PROP_BROADCAST, "broadcast" }, { BT_GATT_CHRC_PROP_READ, "read" }, { BT_GATT_CHRC_PROP_WRITE_WITHOUT_RESP, "write-without-response" }, { BT_GATT_CHRC_PROP_WRITE, "write" }, { BT_GATT_CHRC_PROP_NOTIFY, "notify" }, { BT_GATT_CHRC_PROP_INDICATE, "indicate" }, { BT_GATT_CHRC_PROP_AUTH, "authenticated-signed-writes" }, { BT_GATT_CHRC_PROP_EXT_PROP, "extended-properties" } }; static struct chrc_prop_data chrc_ext_props[] = { /* Extended Properties */ { BT_GATT_CHRC_EXT_PROP_RELIABLE_WRITE, "reliable-write" }, { BT_GATT_CHRC_EXT_PROP_WRITABLE_AUX, "writable-auxiliaries" } }; static gboolean characteristic_get_flags(const GDBusPropertyTable *property, DBusMessageIter *iter, void *data) { struct characteristic *chrc = data; DBusMessageIter array; unsigned i; dbus_message_iter_open_container(iter, DBUS_TYPE_ARRAY, "s", &array); for (i = 0; i < NELEM(chrc_props); i++) { if (chrc->props & chrc_props[i].prop) dbus_message_iter_append_basic(&array, DBUS_TYPE_STRING, &chrc_props[i].str); } for (i = 0; i < NELEM(chrc_ext_props); i++) { if (chrc->ext_props & chrc_ext_props[i].prop) dbus_message_iter_append_basic(&array, DBUS_TYPE_STRING, &chrc_ext_props[i].str); } dbus_message_iter_close_container(iter, &array); return TRUE; } static gboolean characteristic_get_write_acquired(const GDBusPropertyTable *property, DBusMessageIter *iter, void *data) { struct characteristic *chrc = data; dbus_bool_t locked = chrc->write_io ? TRUE : FALSE; dbus_message_iter_append_basic(iter, DBUS_TYPE_BOOLEAN, &locked); return TRUE; } static gboolean characteristic_write_acquired_exists(const GDBusPropertyTable *property, void *data) { struct characteristic *chrc = data; return (chrc->props & BT_GATT_CHRC_PROP_WRITE_WITHOUT_RESP); } static gboolean characteristic_get_notify_acquired(const GDBusPropertyTable *property, DBusMessageIter *iter, void *data) { struct characteristic *chrc = data; dbus_bool_t locked = chrc->notify_io ? TRUE : FALSE; dbus_message_iter_append_basic(iter, DBUS_TYPE_BOOLEAN, &locked); return TRUE; } static gboolean characteristic_notify_acquired_exists(const GDBusPropertyTable *property, void *data) { struct characteristic *chrc = data; return (chrc->props & BT_GATT_CHRC_PROP_NOTIFY); } static gboolean characteristic_get_mtu(const GDBusPropertyTable *property, DBusMessageIter *iter, void *data) { struct characteristic *chrc = data; struct bt_gatt_client *gatt = chrc->service->client->gatt; struct bt_att *att; uint16_t mtu; att = bt_gatt_client_get_att(gatt); mtu = att ? bt_att_get_mtu(att) : BT_ATT_DEFAULT_LE_MTU; dbus_message_iter_append_basic(iter, DBUS_TYPE_UINT16, &mtu); return TRUE; } static gboolean characteristic_mtu_exists(const GDBusPropertyTable *property, void *data) { struct characteristic *chrc = data; return chrc->service->client->gatt ? TRUE : FALSE; } static void write_characteristic_cb(struct gatt_db_attribute *attr, int err, void *user_data) { struct characteristic *chrc = user_data; if (err) return; g_dbus_emit_property_changed_full(btd_get_dbus_connection(), chrc->path, GATT_CHARACTERISTIC_IFACE, "Value", G_DBUS_PROPERTY_CHANGED_FLAG_FLUSH); } static void chrc_read_cb(bool success, uint8_t att_ecode, const uint8_t *value, uint16_t length, void *user_data) { struct async_dbus_op *op = user_data; struct characteristic *chrc = op->data; if (!success) goto fail; if (!op->offset) gatt_db_attribute_reset(chrc->attr); if (!gatt_db_attribute_write(chrc->attr, op->offset, value, length, 0, NULL, write_characteristic_cb, chrc)) { error("Failed to store attribute"); att_ecode = BT_ATT_ERROR_UNLIKELY; goto fail; } /* Read the stored data from db */ if (!gatt_db_attribute_read(chrc->attr, op->offset, 0, NULL, read_op_cb, op)) { error("Failed to read database"); att_ecode = BT_ATT_ERROR_UNLIKELY; goto fail; } chrc->read_op = NULL; return; fail: async_dbus_op_reply(op, att_ecode, NULL, 0); chrc->read_op = NULL; } static DBusMessage *characteristic_read_value(DBusConnection *conn, DBusMessage *msg, void *user_data) { struct characteristic *chrc = user_data; struct bt_gatt_client *gatt = chrc->service->client->gatt; DBusMessageIter iter; uint16_t offset = 0; if (!gatt) return btd_error_failed(msg, "Not connected"); dbus_message_iter_init(msg, &iter); if (parse_options(&iter, &offset, NULL)) return btd_error_invalid_args(msg); if (chrc->read_op) { if (chrc->read_op->offset != offset) return btd_error_in_progress(msg); queue_push_tail(chrc->read_op->msgs, dbus_message_ref(msg)); return NULL; } chrc->read_op = read_value(gatt, msg, chrc->value_handle, offset, chrc_read_cb, chrc); if (!chrc->read_op) return btd_error_failed(msg, "Failed to send read request"); return NULL; } static bool chrc_write_complete(void *data) { struct characteristic *chrc = data; chrc->write_op = NULL; /* * The characteristic might have been unregistered during the read. * Return failure. */ return !!chrc->service; } static DBusMessage *characteristic_write_value(DBusConnection *conn, DBusMessage *msg, void *user_data) { struct characteristic *chrc = user_data; struct bt_gatt_client *gatt = chrc->service->client->gatt; DBusMessageIter iter; uint8_t *value = NULL; int value_len = 0; bool supported = false; uint16_t offset = 0; const char *type = NULL; if (!gatt) return btd_error_failed(msg, "Not connected"); if (chrc->write_io) return btd_error_not_permitted(msg, "Write acquired"); if (chrc->write_op) return btd_error_in_progress(msg); dbus_message_iter_init(msg, &iter); if (parse_value_arg(&iter, &value, &value_len)) return btd_error_invalid_args(msg); dbus_message_iter_next(&iter); if (parse_options(&iter, &offset, &type)) return btd_error_invalid_args(msg); /* * Check if service was previously claimed by a plugin and if we shall * consider it read-only, in that case return not authorized. */ if (chrc->service->claimed && btd_opts.gatt_export == BT_GATT_EXPORT_READ_ONLY) return btd_error_not_authorized(msg); /* * Decide which write to use based on characteristic properties. For now * we don't perform signed writes since gatt-client doesn't support them * and the user can always encrypt the through pairing. The procedure to * use is determined based on the following priority: * * * "reliable-write" property set -> reliable long-write. * * "write" property set -> write request. * - If value is larger than MTU - 3: long-write * * "write-without-response" property set -> write command. */ if ((!type && (chrc->ext_props & BT_GATT_CHRC_EXT_PROP_RELIABLE_WRITE)) || (type && !strcasecmp(type, "reliable"))) { supported = true; chrc->write_op = start_long_write(msg, chrc->value_handle, gatt, true, value, value_len, offset, chrc, chrc_write_complete); if (chrc->write_op) return NULL; } if ((!type && chrc->props & BT_GATT_CHRC_PROP_WRITE) || (type && !strcasecmp(type, "request"))) { uint16_t mtu; supported = true; mtu = bt_gatt_client_get_mtu(gatt); if (!mtu) return btd_error_failed(msg, "No ATT transport"); if (value_len <= mtu - 3 && !offset) chrc->write_op = start_write_request(msg, chrc->value_handle, gatt, value, value_len, chrc, chrc_write_complete); else chrc->write_op = start_long_write(msg, chrc->value_handle, gatt, false, value, value_len, offset, chrc, chrc_write_complete); if (chrc->write_op) return NULL; } if ((type && strcasecmp(type, "command")) || offset || (!type && !(chrc->props & BT_GATT_CHRC_PROP_WRITE_WITHOUT_RESP))) goto fail; supported = true; if (bt_gatt_client_write_without_response(gatt, chrc->value_handle, chrc->props & BT_GATT_CHRC_PROP_AUTH, value, value_len)) return dbus_message_new_method_return(msg); fail: if (supported) return btd_error_failed(msg, "Failed to initiate write"); return btd_error_not_supported(msg); } static bool sock_read(struct io *io, void *user_data) { struct characteristic *chrc = user_data; struct bt_gatt_client *gatt = chrc->service->client->gatt; struct msghdr msg; uint8_t buf[512]; struct iovec iov; int fd = io_get_fd(io); ssize_t bytes_read; iov.iov_base = buf; iov.iov_len = sizeof(buf); memset(&msg, 0, sizeof(msg)); msg.msg_iov = &iov; msg.msg_iovlen = 1; if (fd < 0) { error("io_get_fd() returned %d\n", fd); return false; } bytes_read = recvmsg(fd, &msg, MSG_DONTWAIT); if (bytes_read < 0) { error("recvmsg: %s", strerror(errno)); return false; } if (!gatt || bytes_read == 0) return false; bt_gatt_client_write_without_response(gatt, chrc->value_handle, chrc->props & BT_GATT_CHRC_PROP_AUTH, buf, bytes_read); return true; } static void sock_io_destroy(struct sock_io *io) { if (io->destroy) io->destroy(io->data); if (io->msg) dbus_message_unref(io->msg); io_destroy(io->io); free(io); } static void destroy_sock(struct characteristic *chrc, struct io *io) { queue_remove(chrc->service->client->ios, io); if (chrc->write_io && io == chrc->write_io->io) { sock_io_destroy(chrc->write_io); chrc->write_io = NULL; g_dbus_emit_property_changed(btd_get_dbus_connection(), chrc->path, GATT_CHARACTERISTIC_IFACE, "WriteAcquired"); } else if (chrc->notify_io) { sock_io_destroy(chrc->notify_io); chrc->notify_io = NULL; g_dbus_emit_property_changed(btd_get_dbus_connection(), chrc->path, GATT_CHARACTERISTIC_IFACE, "NotifyAcquired"); } } static bool sock_hup(struct io *io, void *user_data) { struct characteristic *chrc = user_data; DBG("%s: io %p", chrc->path, io); destroy_sock(chrc, io); return false; } static DBusMessage *create_sock(struct characteristic *chrc, DBusMessage *msg) { struct bt_gatt_client *gatt = chrc->service->client->gatt; int fds[2]; struct io *io; bool dir; uint16_t mtu; DBusMessage *reply; if (!gatt || !bt_gatt_client_is_ready(gatt)) return btd_error_failed(msg, "Not connected"); if (socketpair(AF_LOCAL, SOCK_SEQPACKET | SOCK_NONBLOCK | SOCK_CLOEXEC, 0, fds) < 0) return btd_error_failed(msg, strerror(errno)); dir = dbus_message_has_member(msg, "AcquireWrite"); io = io_new(fds[!dir]); if (!io) { close(fds[0]); close(fds[1]); return btd_error_failed(msg, strerror(EIO)); } io_set_close_on_destroy(io, true); if (!io_set_read_handler(io, sock_read, chrc, NULL)) goto fail; if (!io_set_disconnect_handler(io, sock_hup, chrc, NULL)) goto fail; mtu = bt_gatt_client_get_mtu(gatt); reply = g_dbus_create_reply(msg, DBUS_TYPE_UNIX_FD, &fds[dir], DBUS_TYPE_UINT16, &mtu, DBUS_TYPE_INVALID); close(fds[dir]); if (dir) { chrc->write_io->io = io; g_dbus_emit_property_changed(btd_get_dbus_connection(), chrc->path, GATT_CHARACTERISTIC_IFACE, "WriteAcquired"); } else { chrc->notify_io->io = io; g_dbus_emit_property_changed(btd_get_dbus_connection(), chrc->path, GATT_CHARACTERISTIC_IFACE, "NotifyAcquired"); } queue_push_tail(chrc->service->client->ios, io); DBG("%s: sender %s io %p", dbus_message_get_member(msg), dbus_message_get_sender(msg), io); return reply; fail: io_destroy(io); close(fds[dir]); return btd_error_failed(msg, strerror(EIO)); } static void characteristic_ready(bool success, uint8_t ecode, void *user_data) { struct characteristic *chrc = user_data; DBusMessage *reply; chrc->ready_id = 0; if (chrc->write_io && chrc->write_io->msg) { reply = create_sock(chrc, chrc->write_io->msg); g_dbus_send_message(btd_get_dbus_connection(), reply); dbus_message_unref(chrc->write_io->msg); chrc->write_io->msg = NULL; } if (chrc->notify_io && chrc->notify_io->msg) { reply = create_sock(chrc, chrc->notify_io->msg); g_dbus_send_message(btd_get_dbus_connection(), reply); dbus_message_unref(chrc->notify_io->msg); chrc->notify_io->msg = NULL; } } static DBusMessage *characteristic_acquire_write(DBusConnection *conn, DBusMessage *msg, void *user_data) { struct characteristic *chrc = user_data; struct bt_gatt_client *gatt = chrc->service->client->gatt; if (!gatt) return btd_error_failed(msg, "Not connected"); /* * Check if service was previously claimed by a plugin and if we shall * consider it read-only, in that case return not authorized. */ if (chrc->service->claimed && btd_opts.gatt_export == BT_GATT_EXPORT_READ_ONLY) return btd_error_not_authorized(msg); if (chrc->write_io) return btd_error_not_permitted(msg, "Write acquired"); if (!(chrc->props & BT_GATT_CHRC_PROP_WRITE_WITHOUT_RESP)) return btd_error_not_supported(msg); chrc->write_io = new0(struct sock_io, 1); if (!bt_gatt_client_is_ready(gatt)) { /* GATT not ready, wait until it becomes ready */ if (!chrc->ready_id) chrc->ready_id = bt_gatt_client_ready_register(gatt, characteristic_ready, chrc, NULL); chrc->write_io->msg = dbus_message_ref(msg); return NULL; } return create_sock(chrc, msg); } struct notify_client { struct characteristic *chrc; int ref_count; char *owner; guint watch; unsigned int notify_id; }; static void notify_client_free(struct notify_client *client) { DBG("owner %s", client->owner); g_dbus_remove_watch(btd_get_dbus_connection(), client->watch); bt_gatt_client_unregister_notify(client->chrc->service->client->gatt, client->notify_id); free(client->owner); free(client); } static void notify_client_unref(void *data) { struct notify_client *client = data; DBG("owner %s", client->owner); if (__sync_sub_and_fetch(&client->ref_count, 1)) return; notify_client_free(client); } static struct notify_client *notify_client_ref(struct notify_client *client) { DBG("owner %s", client->owner); __sync_fetch_and_add(&client->ref_count, 1); return client; } static bool match_notifying(const void *a, const void *b) { const struct notify_client *client = a; return !!client->notify_id; } static void update_notifying(struct characteristic *chrc) { if (!chrc->notifying) return; if (queue_find(chrc->notify_clients, match_notifying, NULL)) return; chrc->notifying = false; g_dbus_emit_property_changed(btd_get_dbus_connection(), chrc->path, GATT_CHARACTERISTIC_IFACE, "Notifying"); } static void notify_client_disconnect(DBusConnection *conn, void *user_data) { struct notify_client *client = user_data; struct characteristic *chrc = client->chrc; DBG("owner %s", client->owner); queue_remove(chrc->notify_clients, client); queue_remove(chrc->service->client->all_notify_clients, client); update_notifying(chrc); notify_client_unref(client); } static struct notify_client *notify_client_create(struct characteristic *chrc, const char *owner) { struct notify_client *client; client = new0(struct notify_client, 1); client->chrc = chrc; client->owner = strdup(owner); if (!client->owner) { free(client); return NULL; } client->watch = g_dbus_add_disconnect_watch(btd_get_dbus_connection(), owner, notify_client_disconnect, client, NULL); if (!client->watch) { free(client->owner); free(client); return NULL; } return notify_client_ref(client); } static bool match_notify_sender(const void *a, const void *b) { const struct notify_client *client = a; const char *sender = b; return strcmp(client->owner, sender) == 0; } static void notify_cb(uint16_t value_handle, const uint8_t *value, uint16_t length, void *user_data) { struct async_dbus_op *op = user_data; struct notify_client *client = op->data; struct characteristic *chrc = client->chrc; /* * Even if the value didn't change, we want to send a PropertiesChanged * signal so that we propagate the notification/indication to * applications. */ gatt_db_attribute_reset(chrc->attr); gatt_db_attribute_write(chrc->attr, 0, value, length, 0, NULL, write_characteristic_cb, chrc); } static void create_notify_reply(struct async_dbus_op *op, bool success, uint8_t att_ecode) { int err; if (success) err = 0; else err = att_ecode ? att_ecode : -ENOENT; async_dbus_op_reply(op, err, NULL, -1); } static void register_notify_cb(uint16_t att_ecode, void *user_data) { struct async_dbus_op *op = user_data; struct notify_client *client = op->data; struct characteristic *chrc = client->chrc; if (att_ecode) { queue_remove(chrc->notify_clients, client); queue_remove(chrc->service->client->all_notify_clients, client); notify_client_free(client); create_notify_reply(op, false, att_ecode); return; } if (!chrc->notifying) { chrc->notifying = true; g_dbus_emit_property_changed(btd_get_dbus_connection(), chrc->path, GATT_CHARACTERISTIC_IFACE, "Notifying"); } create_notify_reply(op, true, 0); } static void notify_io_cb(uint16_t value_handle, const uint8_t *value, uint16_t length, void *user_data) { struct msghdr msg; struct iovec iov; struct notify_client *client = user_data; struct characteristic *chrc = client->chrc; int err; /* Drop notification if the sock is not ready */ if (!chrc->notify_io || !chrc->notify_io->io) return; iov.iov_base = (void *) value; iov.iov_len = length; memset(&msg, 0, sizeof(msg)); msg.msg_iov = &iov; msg.msg_iovlen = 1; err = sendmsg(io_get_fd(chrc->notify_io->io), &msg, MSG_NOSIGNAL); if (err < 0) error("sendmsg: %s", strerror(errno)); } static void register_notify_io_cb(uint16_t att_ecode, void *user_data) { struct notify_client *client = user_data; struct characteristic *chrc = client->chrc; struct bt_gatt_client *gatt = chrc->service->client->gatt; if (att_ecode) { queue_remove(chrc->notify_clients, client); notify_client_free(client); return; } if (!bt_gatt_client_is_ready(gatt)) { if (!chrc->ready_id) chrc->ready_id = bt_gatt_client_ready_register(gatt, characteristic_ready, chrc, NULL); return; } characteristic_ready(true, 0, chrc); } static void notify_io_destroy(void *data) { struct notify_client *client = data; if (queue_remove(client->chrc->notify_clients, client)) notify_client_unref(client); } static DBusMessage *characteristic_acquire_notify(DBusConnection *conn, DBusMessage *msg, void *user_data) { struct characteristic *chrc = user_data; struct bt_gatt_client *gatt = chrc->service->client->gatt; const char *sender = dbus_message_get_sender(msg); struct notify_client *client; if (!gatt) return btd_error_failed(msg, "Not connected"); if (chrc->notify_io) return btd_error_not_permitted(msg, "Notify acquired"); /* Each client can only have one active notify session. */ if (!queue_isempty(chrc->notify_clients)) return btd_error_in_progress(msg); if (!(chrc->props & (BT_GATT_CHRC_PROP_NOTIFY | BT_GATT_CHRC_PROP_INDICATE))) return btd_error_not_supported(msg); client = notify_client_create(chrc, sender); if (!client) return btd_error_failed(msg, "Failed allocate notify session"); client->notify_id = bt_gatt_client_register_notify(gatt, chrc->value_handle, register_notify_io_cb, notify_io_cb, client, NULL); if (!client->notify_id) { notify_client_unref(client); return btd_error_failed(msg, "Failed to subscribe"); } queue_push_tail(chrc->notify_clients, client); chrc->notify_io = new0(struct sock_io, 1); chrc->notify_io->data = client; chrc->notify_io->msg = dbus_message_ref(msg); chrc->notify_io->destroy = notify_io_destroy; return NULL; } static DBusMessage *characteristic_start_notify(DBusConnection *conn, DBusMessage *msg, void *user_data) { struct characteristic *chrc = user_data; struct bt_gatt_client *gatt = chrc->service->client->gatt; const char *sender = dbus_message_get_sender(msg); struct async_dbus_op *op; struct notify_client *client; struct btd_device *device = chrc->service->client->device; if (device_is_disconnecting(device)) { error("Device is disconnecting. StartNotify is not allowed."); return btd_error_not_connected(msg); } if (chrc->notify_io) return btd_error_not_permitted(msg, "Notify acquired"); if (!(chrc->props & (BT_GATT_CHRC_PROP_NOTIFY | BT_GATT_CHRC_PROP_INDICATE))) return btd_error_not_supported(msg); /* Each client can only have one active notify session. */ client = queue_find(chrc->notify_clients, match_notify_sender, sender); if (client) return client->notify_id ? g_dbus_create_reply(msg, DBUS_TYPE_INVALID) : btd_error_in_progress(msg); client = notify_client_create(chrc, sender); if (!client) return btd_error_failed(msg, "Failed allocate notify session"); queue_push_tail(chrc->notify_clients, client); queue_push_tail(chrc->service->client->all_notify_clients, client); /* * If the device is currently not connected, return success. We will * automatically try and register all clients when a GATT client becomes * ready. */ if (!gatt) { DBusMessage *reply; reply = g_dbus_create_reply(msg, DBUS_TYPE_INVALID); if (reply) return reply; /* * Clean up and respond with an error instead of timing out to * avoid any ambiguities. */ error("Failed to construct D-Bus message reply"); goto fail; } op = async_dbus_op_new(msg, client); client->notify_id = bt_gatt_client_register_notify(gatt, chrc->value_handle, register_notify_cb, notify_cb, op, async_dbus_op_free); if (client->notify_id) return NULL; async_dbus_op_free(op); fail: queue_remove(chrc->notify_clients, client); queue_remove(chrc->service->client->all_notify_clients, client); /* Directly free the client */ notify_client_free(client); return btd_error_failed(msg, "Failed to register notify session"); } static DBusMessage *characteristic_stop_notify(DBusConnection *conn, DBusMessage *msg, void *user_data) { struct characteristic *chrc = user_data; struct bt_gatt_client *gatt = chrc->service->client->gatt; const char *sender = dbus_message_get_sender(msg); struct notify_client *client; if (chrc->notify_io) { destroy_sock(chrc, chrc->notify_io->io); return dbus_message_new_method_return(msg); } client = queue_remove_if(chrc->notify_clients, match_notify_sender, (void *) sender); if (!client) return btd_error_failed(msg, "No notify session started"); queue_remove(chrc->service->client->all_notify_clients, client); bt_gatt_client_unregister_notify(gatt, client->notify_id); update_notifying(chrc); notify_client_unref(client); return dbus_message_new_method_return(msg); } static const GDBusPropertyTable characteristic_properties[] = { { "Handle", "q", characteristic_get_handle }, { "UUID", "s", characteristic_get_uuid, NULL, NULL }, { "Service", "o", characteristic_get_service, NULL, NULL }, { "Value", "ay", characteristic_get_value, NULL, characteristic_value_exists }, { "Notifying", "b", characteristic_get_notifying, NULL, characteristic_notifying_exists }, { "Flags", "as", characteristic_get_flags, NULL, NULL }, { "WriteAcquired", "b", characteristic_get_write_acquired, NULL, characteristic_write_acquired_exists }, { "NotifyAcquired", "b", characteristic_get_notify_acquired, NULL, characteristic_notify_acquired_exists }, { "MTU", "q", characteristic_get_mtu, NULL, characteristic_mtu_exists }, { } }; static const GDBusMethodTable characteristic_methods[] = { { GDBUS_ASYNC_METHOD("ReadValue", GDBUS_ARGS({ "options", "a{sv}" }), GDBUS_ARGS({ "value", "ay" }), characteristic_read_value) }, { GDBUS_ASYNC_METHOD("WriteValue", GDBUS_ARGS({ "value", "ay" }, { "options", "a{sv}" }), NULL, characteristic_write_value) }, { GDBUS_ASYNC_METHOD("AcquireWrite", GDBUS_ARGS({ "options", "a{sv}" }), GDBUS_ARGS({ "fd", "h" }, { "mtu", "q" }), characteristic_acquire_write) }, { GDBUS_ASYNC_METHOD("AcquireNotify", GDBUS_ARGS({ "options", "a{sv}" }), GDBUS_ARGS({ "fd", "h" }, { "mtu", "q" }), characteristic_acquire_notify) }, { GDBUS_ASYNC_METHOD("StartNotify", NULL, NULL, characteristic_start_notify) }, { GDBUS_METHOD("StopNotify", NULL, NULL, characteristic_stop_notify) }, { } }; static void remove_client(void *data) { struct notify_client *ntfy_client = data; struct btd_gatt_client *client = ntfy_client->chrc->service->client; queue_remove(client->all_notify_clients, ntfy_client); notify_client_unref(ntfy_client); } static void characteristic_free(void *data) { struct characteristic *chrc = data; struct bt_gatt_client *gatt = chrc->service->client->gatt; struct bt_att *att; /* List should be empty here */ queue_destroy(chrc->descs, NULL); if (chrc->write_io) { queue_remove(chrc->service->client->ios, chrc->write_io->io); sock_io_destroy(chrc->write_io); } if (chrc->notify_io) { queue_remove(chrc->service->client->ios, chrc->notify_io->io); sock_io_destroy(chrc->notify_io); } queue_destroy(chrc->notify_clients, remove_client); att = bt_gatt_client_get_att(gatt); if (att) bt_att_unregister_exchange(att, chrc->exchange_id); g_free(chrc->path); free(chrc); } static void att_exchange(uint16_t mtu, void *user_data) { struct characteristic *chrc = user_data; g_dbus_emit_property_changed(btd_get_dbus_connection(), chrc->path, GATT_CHARACTERISTIC_IFACE, "MTU"); } static struct characteristic *characteristic_create( struct gatt_db_attribute *attr, struct service *service) { struct characteristic *chrc; struct bt_gatt_client *gatt = service->client->gatt; struct bt_att *att; bt_uuid_t uuid; chrc = new0(struct characteristic, 1); chrc->descs = queue_new(); chrc->notify_clients = queue_new(); chrc->service = service; gatt_db_attribute_get_char_data(attr, &chrc->handle, &chrc->value_handle, &chrc->props, &chrc->ext_props, &uuid); chrc->attr = gatt_db_get_attribute(service->client->db, chrc->value_handle); if (!chrc->attr) { error("Attribute 0x%04x not found", chrc->value_handle); characteristic_free(chrc); return NULL; } bt_uuid_to_uuid128(&uuid, &chrc->uuid); chrc->path = g_strdup_printf("%s/char%04x", service->path, chrc->handle); if (!g_dbus_register_interface(btd_get_dbus_connection(), chrc->path, GATT_CHARACTERISTIC_IFACE, characteristic_methods, NULL, characteristic_properties, chrc, characteristic_free)) { error("Unable to register GATT characteristic with handle " "0x%04x", chrc->handle); characteristic_free(chrc); return NULL; } att = bt_gatt_client_get_att(gatt); if (att) chrc->exchange_id = bt_att_register_exchange(att, att_exchange, chrc, NULL); DBG("Exported GATT characteristic: %s", chrc->path); return chrc; } static void unregister_characteristic(void *data) { struct characteristic *chrc = data; struct bt_gatt_client *gatt = chrc->service->client->gatt; DBG("Removing GATT characteristic: %s", chrc->path); if (chrc->read_op) bt_gatt_client_cancel(gatt, chrc->read_op->id); if (chrc->write_op) bt_gatt_client_cancel(gatt, chrc->write_op->id); queue_remove_all(chrc->descs, NULL, NULL, unregister_descriptor); g_dbus_unregister_interface(btd_get_dbus_connection(), chrc->path, GATT_CHARACTERISTIC_IFACE); } static gboolean service_get_handle(const GDBusPropertyTable *property, DBusMessageIter *iter, void *data) { struct service *service = data; uint16_t handle = service->start_handle; dbus_message_iter_append_basic(iter, DBUS_TYPE_UINT16, &handle); return TRUE; } static gboolean service_get_uuid(const GDBusPropertyTable *property, DBusMessageIter *iter, void *data) { char uuid[MAX_LEN_UUID_STR + 1]; const char *ptr = uuid; struct service *service = data; bt_uuid_to_string(&service->uuid, uuid, sizeof(uuid)); dbus_message_iter_append_basic(iter, DBUS_TYPE_STRING, &ptr); return TRUE; } static gboolean service_get_device(const GDBusPropertyTable *property, DBusMessageIter *iter, void *data) { struct service *service = data; const char *str = device_get_path(service->client->device); dbus_message_iter_append_basic(iter, DBUS_TYPE_OBJECT_PATH, &str); return TRUE; } static gboolean service_get_primary(const GDBusPropertyTable *property, DBusMessageIter *iter, void *data) { struct service *service = data; dbus_bool_t primary; primary = service->primary ? TRUE : FALSE; dbus_message_iter_append_basic(iter, DBUS_TYPE_BOOLEAN, &primary); return TRUE; } static void append_incl_service_path(void *data, void *user_data) { struct service *incl_service = data; DBusMessageIter *array = user_data; dbus_message_iter_append_basic(array, DBUS_TYPE_OBJECT_PATH, &incl_service->path); } static gboolean service_get_includes(const GDBusPropertyTable *property, DBusMessageIter *iter, void *data) { struct service *service = data; DBusMessageIter array; dbus_message_iter_open_container(iter, DBUS_TYPE_ARRAY, "{o}", &array); queue_foreach(service->incl_services, append_incl_service_path, &array); dbus_message_iter_close_container(iter, &array); return TRUE; } static const GDBusPropertyTable service_properties[] = { { "Handle", "q", service_get_handle }, { "UUID", "s", service_get_uuid }, { "Device", "o", service_get_device }, { "Primary", "b", service_get_primary }, { "Includes", "ao", service_get_includes }, { } }; static void service_free(void *data) { struct service *service = data; queue_destroy(service->chrcs, NULL); /* List should be empty here */ queue_destroy(service->incl_services, NULL); g_free(service->path); free(service); } static bool match_service_handle(const void *a, const void *b) { const struct service *service = a; uint16_t start_handle = PTR_TO_UINT(b); return service->start_handle == start_handle; } static struct service *service_create(struct gatt_db_attribute *attr, struct btd_gatt_client *client) { struct service *service; const char *device_path = device_get_path(client->device); bt_uuid_t uuid; uint16_t start_handle, end_handle; bool primary; gatt_db_attribute_get_service_data(attr, &start_handle, &end_handle, &primary, &uuid); /* Check if service is already on list then return NULL to skip it */ service = queue_find(client->services, match_service_handle, UINT_TO_PTR(start_handle)); if (service) return NULL; service = new0(struct service, 1); service->chrcs = queue_new(); service->incl_services = queue_new(); service->client = client; service->claimed = gatt_db_service_get_claimed(attr); gatt_db_attribute_get_service_data(attr, &service->start_handle, &service->end_handle, &service->primary, &uuid); bt_uuid_to_uuid128(&uuid, &service->uuid); service->path = g_strdup_printf("%s/service%04x", device_path, service->start_handle); if (!g_dbus_register_interface(btd_get_dbus_connection(), service->path, GATT_SERVICE_IFACE, NULL, NULL, service_properties, service, service_free)) { error("Unable to register GATT service with handle 0x%04x for " "device %s", service->start_handle, client->devaddr); service_free(service); return NULL; } DBG("Exported GATT service: %s", service->path); /* Set service active so we can skip discovering next time */ gatt_db_service_set_active(attr, true); /* Mark the service as claimed since it going to be exported */ gatt_db_service_set_claimed(attr, true); return service; } static void on_service_removed(void *data, void *user_data) { struct service *service = data; struct service *removed_service = user_data; if (queue_remove(service->incl_services, removed_service)) g_dbus_emit_property_changed(btd_get_dbus_connection(), service->path, GATT_SERVICE_IFACE, "Includes"); } static void unregister_service(void *data) { struct service *service = data; struct btd_gatt_client *client = service->client; DBG("Removing GATT service: %s", service->path); queue_remove_all(service->chrcs, NULL, NULL, unregister_characteristic); queue_remove_all(service->incl_services, NULL, NULL, NULL); queue_foreach(client->services, on_service_removed, service); g_dbus_unregister_interface(btd_get_dbus_connection(), service->path, GATT_SERVICE_IFACE); } struct export_data { void *root; bool failed; }; static void export_desc(struct gatt_db_attribute *attr, void *user_data) { struct descriptor *desc; struct export_data *data = user_data; struct characteristic *charac = data->root; if (data->failed) return; desc = descriptor_create(attr, charac); if (!desc) { data->failed = true; return; } queue_push_tail(charac->descs, desc); } static bool create_descriptors(struct gatt_db_attribute *attr, struct characteristic *charac) { struct export_data data; data.root = charac; data.failed = false; gatt_db_service_foreach_desc(attr, export_desc, &data); return !data.failed; } static void export_char(struct gatt_db_attribute *attr, void *user_data) { struct characteristic *charac; struct export_data *data = user_data; struct service *service = data->root; if (data->failed) return; charac = characteristic_create(attr, service); if (!charac) goto fail; if (!create_descriptors(attr, charac)) { unregister_characteristic(charac); goto fail; } queue_push_tail(service->chrcs, charac); return; fail: data->failed = true; } static bool create_characteristics(struct gatt_db_attribute *attr, struct service *service) { struct export_data data; data.root = service; data.failed = false; gatt_db_service_foreach_char(attr, export_char, &data); return !data.failed; } static void export_service(struct gatt_db_attribute *attr, void *user_data) { struct btd_gatt_client *client = user_data; struct service *service; if (gatt_db_service_get_claimed(attr)) { switch (btd_opts.gatt_export) { case BT_GATT_EXPORT_OFF: return; case BT_GATT_EXPORT_READ_ONLY: case BT_GATT_EXPORT_READ_WRITE: break; } } service = service_create(attr, client); if (!service) return; if (!create_characteristics(attr, service)) { error("Exporting characteristics failed"); unregister_service(service); return; } queue_push_tail(client->services, service); } struct update_incl_data { struct service *service; bool changed; }; static void update_included_service(struct gatt_db_attribute *attrib, void *user_data) { struct update_incl_data *update_data = user_data; struct btd_gatt_client *client = update_data->service->client; struct service *service = update_data->service; struct service *incl_service; uint16_t start_handle; gatt_db_attribute_get_incl_data(attrib, NULL, &start_handle, NULL); incl_service = queue_find(client->services, match_service_handle, UINT_TO_PTR(start_handle)); if (!incl_service) return; /* Check if service is already on list */ if (queue_find(service->incl_services, NULL, incl_service)) return; queue_push_tail(service->incl_services, incl_service); update_data->changed = true; } static void update_included_services(void *data, void *user_data) { struct btd_gatt_client *client = user_data; struct service *service = data; struct gatt_db_attribute *attr; struct update_incl_data inc_data = { .changed = false, .service = service, }; attr = gatt_db_get_attribute(client->db, service->start_handle); gatt_db_service_foreach_incl(attr, update_included_service, &inc_data); if (inc_data.changed) g_dbus_emit_property_changed(btd_get_dbus_connection(), service->path, GATT_SERVICE_IFACE, "Includes"); } static void create_services(struct btd_gatt_client *client) { DBG("Exporting objects for GATT services: %s", client->devaddr); gatt_db_foreach_service(client->db, NULL, export_service, client); queue_foreach(client->services, update_included_services, client); } struct btd_gatt_client *btd_gatt_client_new(struct btd_device *device) { struct btd_gatt_client *client; struct gatt_db *db; if (!device) return NULL; db = btd_device_get_gatt_db(device); if (!db) return NULL; client = new0(struct btd_gatt_client, 1); client->services = queue_new(); client->all_notify_clients = queue_new(); client->ios = queue_new(); client->device = device; ba2str(device_get_address(device), client->devaddr); client->db = gatt_db_ref(db); return client; } void btd_gatt_client_destroy(struct btd_gatt_client *client) { if (!client) return; queue_destroy(client->services, unregister_service); queue_destroy(client->all_notify_clients, NULL); queue_destroy(client->ios, NULL); bt_gatt_client_unref(client->gatt); gatt_db_unref(client->db); free(client); } static void register_notify(void *data, void *user_data) { struct notify_client *notify_client = data; struct btd_gatt_client *client = user_data; struct async_dbus_op *op; DBG("Re-register subscribed notification client"); op = new0(struct async_dbus_op, 1); op->data = notify_client; notify_client->notify_id = bt_gatt_client_register_notify(client->gatt, notify_client->chrc->value_handle, register_notify_cb, notify_cb, op, async_dbus_op_free); if (notify_client->notify_id) return; async_dbus_op_free(op); DBG("Failed to re-register notification client"); queue_remove(notify_client->chrc->notify_clients, notify_client); queue_remove(client->all_notify_clients, notify_client); notify_client_free(notify_client); } void btd_gatt_client_ready(struct btd_gatt_client *client) { if (!client) return; if (!client->gatt) { struct bt_gatt_client *gatt; gatt = btd_device_get_gatt_client(client->device); client->gatt = bt_gatt_client_clone(gatt); if (!client->gatt) { error("GATT client not initialized"); return; } } client->ready = true; DBG("GATT client ready"); create_services(client); DBG("Features 0x%02x", client->features); if (!client->features) { client->features = bt_gatt_client_get_features(client->gatt); DBG("Update Features 0x%02x", client->features); if (client->features & BT_GATT_CHRC_CLI_FEAT_EATT) btd_gatt_client_eatt_connect(client); } } static void eatt_connect_cb(GIOChannel *io, GError *gerr, gpointer user_data) { struct btd_gatt_client *client = user_data; if (gerr) return; device_attach_att(client->device, io); } void btd_gatt_client_eatt_connect(struct btd_gatt_client *client) { struct bt_att *att = bt_gatt_client_get_att(client->gatt); struct btd_device *dev = client->device; struct btd_adapter *adapter = device_get_adapter(dev); GIOChannel *io; GError *gerr = NULL; char addr[18]; int i; if (!(client->features & BT_GATT_CHRC_CLI_FEAT_EATT) || !btd_device_is_initiator(dev)) return; if (bt_att_get_channels(att) == btd_opts.gatt_channels) return; ba2str(device_get_address(dev), addr); for (i = bt_att_get_channels(att); i < btd_opts.gatt_channels; i++) { int defer_timeout = i + 1 < btd_opts.gatt_channels ? 1 : 0; DBG("Connection attempt to: %s defer %s", addr, defer_timeout ? "true" : "false"); /* Attempt to connect using the Ext-Flowctl */ io = bt_io_connect(eatt_connect_cb, client, NULL, &gerr, BT_IO_OPT_SOURCE_BDADDR, btd_adapter_get_address(adapter), BT_IO_OPT_SOURCE_TYPE, btd_adapter_get_address_type(adapter), BT_IO_OPT_DEST_BDADDR, device_get_address(dev), BT_IO_OPT_DEST_TYPE, device_get_le_address_type(dev), BT_IO_OPT_MODE, BT_IO_MODE_EXT_FLOWCTL, BT_IO_OPT_PSM, BT_ATT_EATT_PSM, BT_IO_OPT_SEC_LEVEL, BT_IO_SEC_LOW, BT_IO_OPT_MTU, btd_opts.gatt_mtu, BT_IO_OPT_DEFER_TIMEOUT, defer_timeout, BT_IO_OPT_INVALID); if (!io) { g_error_free(gerr); gerr = NULL; /* Fallback to legacy LE Mode */ io = bt_io_connect(eatt_connect_cb, client, NULL, &gerr, BT_IO_OPT_SOURCE_BDADDR, btd_adapter_get_address(adapter), BT_IO_OPT_SOURCE_TYPE, btd_adapter_get_address_type(adapter), BT_IO_OPT_DEST_BDADDR, device_get_address(dev), BT_IO_OPT_DEST_TYPE, device_get_le_address_type(dev), BT_IO_OPT_PSM, BT_ATT_EATT_PSM, BT_IO_OPT_SEC_LEVEL, BT_IO_SEC_LOW, BT_IO_OPT_MTU, btd_opts.gatt_mtu, BT_IO_OPT_INVALID); if (!io) { error("EATT bt_io_connect(%s): %s", addr, gerr->message); g_error_free(gerr); return; } } g_io_channel_unref(io); } } void btd_gatt_client_connected(struct btd_gatt_client *client) { struct bt_gatt_client *gatt; gatt = btd_device_get_gatt_client(client->device); if (!gatt) { error("GATT client not initialized"); return; } DBG("Device connected."); bt_gatt_client_unref(client->gatt); client->gatt = bt_gatt_client_clone(gatt); /* * Services have already been created before. Re-enable notifications * for any pre-registered notification sessions. */ queue_foreach(client->all_notify_clients, register_notify, client); } void btd_gatt_client_service_added(struct btd_gatt_client *client, struct gatt_db_attribute *attrib) { if (!client || !attrib || !client->ready) return; export_service(attrib, client); queue_foreach(client->services, update_included_services, client); } void btd_gatt_client_service_removed(struct btd_gatt_client *client, struct gatt_db_attribute *attrib) { uint16_t start_handle, end_handle; if (!client || !attrib || !client->ready) return; gatt_db_attribute_get_service_handles(attrib, &start_handle, &end_handle); DBG("GATT Services Removed - start: 0x%04x, end: 0x%04x", start_handle, end_handle); queue_remove_all(client->services, match_service_handle, UINT_TO_PTR(start_handle), unregister_service); } static void clear_notify_id(void *data, void *user_data) { struct notify_client *client = data; client->notify_id = 0; } static void client_shutdown(void *data) { io_shutdown(data); } void btd_gatt_client_disconnected(struct btd_gatt_client *client) { if (!client || !client->gatt) return; DBG("Device disconnected. Cleaning up."); queue_remove_all(client->ios, NULL, NULL, client_shutdown); /* * TODO: Once GATT over BR/EDR is properly supported, we should pass the * correct bdaddr_type based on the transport over which GATT is being * done. */ queue_foreach(client->all_notify_clients, clear_notify_id, NULL); bt_gatt_client_unref(client->gatt); client->gatt = NULL; } struct foreach_service_data { btd_gatt_client_service_path_t func; void *user_data; }; static void client_service_foreach(void *data, void *user_data) { struct service *service = data; struct foreach_service_data *foreach_data = user_data; foreach_data->func(service->path, foreach_data->user_data); } void btd_gatt_client_foreach_service(struct btd_gatt_client *client, btd_gatt_client_service_path_t func, void *user_data) { struct foreach_service_data data; if (!client) return; data.func = func; data.user_data = user_data; queue_foreach(client->services, client_service_foreach, &data); } bluez-5.82/src/PaxHeaders/adv_monitor.h0000644000000000000000000000005014214376354015140 xustar0020 atime=1743516637 20 ctime=1743591285 bluez-5.82/src/adv_monitor.h0000644000000000000000000000220314214376354014616 0ustar00rootroot/* SPDX-License-Identifier: LGPL-2.1-or-later */ /* * * BlueZ - Bluetooth protocol stack for Linux * * Copyright (C) 2020 Google LLC * * */ #ifndef __ADV_MONITOR_H #define __ADV_MONITOR_H #include #include "src/shared/ad.h" struct mgmt; struct queue; struct btd_device; struct btd_adapter; struct btd_adv_monitor_manager; struct btd_adv_monitor_pattern; struct btd_adv_monitor_manager *btd_adv_monitor_manager_create( struct btd_adapter *adapter, struct mgmt *mgmt); void btd_adv_monitor_manager_destroy(struct btd_adv_monitor_manager *manager); bool btd_adv_monitor_offload_enabled(struct btd_adv_monitor_manager *manager); struct queue *btd_adv_monitor_content_filter( struct btd_adv_monitor_manager *manager, struct bt_ad *ad); void btd_adv_monitor_notify_monitors(struct btd_adv_monitor_manager *manager, struct btd_device *device, int8_t rssi, struct queue *matched_monitors); void btd_adv_monitor_device_remove(struct btd_adv_monitor_manager *manager, struct btd_device *device); void btd_adv_monitor_power_down(struct btd_adv_monitor_manager *manager); #endif /* __ADV_MONITOR_H */ bluez-5.82/src/PaxHeaders/sdp-xml.h0000644000000000000000000000005014015011623014163 xustar0020 atime=1743516622 20 ctime=1743591284 bluez-5.82/src/sdp-xml.h0000644000000000000000000000055114015011623013645 0ustar00rootroot/* SPDX-License-Identifier: GPL-2.0-or-later */ /* * * BlueZ - Bluetooth protocol stack for Linux * * Copyright (C) 2005-2010 Marcel Holtmann * * */ void convert_sdp_record_to_xml(sdp_record_t *rec, void *user_data, void (*append_func) (void *, const char *)); sdp_record_t *sdp_xml_parse_record(const char *data, int size); bluez-5.82/src/PaxHeaders/eir.c0000644000000000000000000000005014471706460013371 xustar0020 atime=1743516683 20 ctime=1743591278 bluez-5.82/src/eir.c0000644000000000000000000003137614471706460013064 0ustar00rootroot// SPDX-License-Identifier: GPL-2.0-or-later /* * * BlueZ - Bluetooth protocol stack for Linux * * Copyright (C) 2011 Nokia Corporation * Copyright (C) 2011 Marcel Holtmann * * */ #ifdef HAVE_CONFIG_H #include #endif #define _GNU_SOURCE #include #include #include #include #include #include #include "lib/bluetooth.h" #include "lib/hci.h" #include "lib/sdp.h" #include "src/shared/util.h" #include "uuid-helper.h" #include "eir.h" #define EIR_OOB_MIN (2 + 6) static void sd_free(void *data) { struct eir_sd *sd = data; free(sd->uuid); g_free(sd); } static void data_free(void *data) { struct eir_ad *ad = data; g_free(ad->data); g_free(ad); } void eir_data_free(struct eir_data *eir) { g_slist_free_full(eir->services, free); eir->services = NULL; g_free(eir->name); eir->name = NULL; free(eir->hash); eir->hash = NULL; free(eir->randomizer); eir->randomizer = NULL; g_slist_free_full(eir->msd_list, g_free); eir->msd_list = NULL; g_slist_free_full(eir->sd_list, sd_free); eir->sd_list = NULL; g_slist_free_full(eir->data_list, data_free); eir->data_list = NULL; } static void eir_parse_uuid16(struct eir_data *eir, const void *data, uint8_t len) { const uint16_t *uuid16 = data; uuid_t service; char *uuid_str; unsigned int i; service.type = SDP_UUID16; for (i = 0; i < len / 2; i++, uuid16++) { service.value.uuid16 = get_le16(uuid16); uuid_str = bt_uuid2string(&service); if (!uuid_str) continue; eir->services = g_slist_append(eir->services, uuid_str); } } static void eir_parse_uuid32(struct eir_data *eir, const void *data, uint8_t len) { const uint32_t *uuid32 = data; uuid_t service; char *uuid_str; unsigned int i; service.type = SDP_UUID32; for (i = 0; i < len / 4; i++, uuid32++) { service.value.uuid32 = get_le32(uuid32); uuid_str = bt_uuid2string(&service); if (!uuid_str) continue; eir->services = g_slist_append(eir->services, uuid_str); } } static void eir_parse_uuid128(struct eir_data *eir, const uint8_t *data, uint8_t len) { const uint8_t *uuid_ptr = data; uuid_t service; char *uuid_str; unsigned int i; int k; service.type = SDP_UUID128; for (i = 0; i < len / 16; i++) { for (k = 0; k < 16; k++) service.value.uuid128.data[k] = uuid_ptr[16 - k - 1]; uuid_str = bt_uuid2string(&service); if (!uuid_str) continue; eir->services = g_slist_append(eir->services, uuid_str); uuid_ptr += 16; } } static char *name2utf8(const uint8_t *name, uint8_t len) { char utf8_name[HCI_MAX_NAME_LENGTH + 2]; int i; if (g_utf8_validate((const char *) name, len, NULL)) return g_strndup((char *) name, len); memset(utf8_name, 0, sizeof(utf8_name)); strncpy(utf8_name, (char *) name, len); /* Assume ASCII, and replace all non-ASCII with spaces */ for (i = 0; utf8_name[i] != '\0'; i++) { if (!isascii(utf8_name[i])) utf8_name[i] = ' '; } /* Remove leading and trailing whitespace characters */ g_strstrip(utf8_name); return g_strdup(utf8_name); } static void eir_parse_msd(struct eir_data *eir, const uint8_t *data, uint8_t len) { struct eir_msd *msd; if (len < 2 || len > 2 + sizeof(msd->data)) return; msd = g_malloc(sizeof(*msd)); msd->company = get_le16(data); msd->data_len = len - 2; memcpy(&msd->data, data + 2, msd->data_len); eir->msd_list = g_slist_append(eir->msd_list, msd); } static void eir_parse_sd(struct eir_data *eir, uuid_t *service, const uint8_t *data, uint8_t len) { struct eir_sd *sd; char *uuid; uuid = bt_uuid2string(service); if (!uuid) return; sd = g_malloc(sizeof(*sd)); sd->uuid = uuid; sd->data_len = len; memcpy(&sd->data, data, sd->data_len); eir->sd_list = g_slist_append(eir->sd_list, sd); } static void eir_parse_uuid16_data(struct eir_data *eir, const uint8_t *data, uint8_t len) { uuid_t service; if (len < 2 || len > EIR_SD_MAX_LEN) return; service.type = SDP_UUID16; service.value.uuid16 = get_le16(data); eir_parse_sd(eir, &service, data + 2, len - 2); } static void eir_parse_uuid32_data(struct eir_data *eir, const uint8_t *data, uint8_t len) { uuid_t service; if (len < 4 || len > EIR_SD_MAX_LEN) return; service.type = SDP_UUID32; service.value.uuid32 = get_le32(data); eir_parse_sd(eir, &service, data + 4, len - 4); } static void eir_parse_uuid128_data(struct eir_data *eir, const uint8_t *data, uint8_t len) { uuid_t service; int k; if (len < 16 || len > EIR_SD_MAX_LEN) return; service.type = SDP_UUID128; for (k = 0; k < 16; k++) service.value.uuid128.data[k] = data[16 - k - 1]; eir_parse_sd(eir, &service, data + 16, len - 16); } static void eir_parse_data(struct eir_data *eir, uint8_t type, const uint8_t *data, uint8_t len) { struct eir_ad *ad; ad = g_malloc(sizeof(*ad)); ad->type = type; ad->len = len; ad->data = g_malloc(len); memcpy(ad->data, data, len); eir->data_list = g_slist_append(eir->data_list, ad); if (type == EIR_CSIP_RSI) eir->rsi = true; } void eir_parse(struct eir_data *eir, const uint8_t *eir_data, uint8_t eir_len) { uint16_t len = 0; eir->flags = 0; eir->tx_power = 127; /* No EIR data to parse */ if (eir_data == NULL) return; while (len < eir_len - 1) { uint8_t field_len = eir_data[0]; const uint8_t *data; uint8_t data_len; /* Check for the end of EIR */ if (field_len == 0) break; len += field_len + 1; /* Do not continue EIR Data parsing if got incorrect length */ if (len > eir_len) break; data = &eir_data[2]; data_len = field_len - 1; switch (eir_data[1]) { case EIR_UUID16_SOME: case EIR_UUID16_ALL: eir_parse_uuid16(eir, data, data_len); break; case EIR_UUID32_SOME: case EIR_UUID32_ALL: eir_parse_uuid32(eir, data, data_len); break; case EIR_UUID128_SOME: case EIR_UUID128_ALL: eir_parse_uuid128(eir, data, data_len); break; case EIR_FLAGS: if (data_len > 0) eir->flags = *data; break; case EIR_NAME_SHORT: case EIR_NAME_COMPLETE: /* Some vendors put a NUL byte terminator into * the name */ while (data_len > 0 && data[data_len - 1] == '\0') data_len--; g_free(eir->name); eir->name = name2utf8(data, data_len); eir->name_complete = eir_data[1] == EIR_NAME_COMPLETE; break; case EIR_TX_POWER: if (data_len < 1) break; eir->tx_power = (int8_t) data[0]; break; case EIR_CLASS_OF_DEV: if (data_len < 3) break; eir->class = data[0] | (data[1] << 8) | (data[2] << 16); break; case EIR_GAP_APPEARANCE: if (data_len < 2) break; eir->appearance = get_le16(data); break; case EIR_SSP_HASH: if (data_len < 16) break; eir->hash = util_memdup(data, 16); break; case EIR_SSP_RANDOMIZER: if (data_len < 16) break; eir->randomizer = util_memdup(data, 16); break; case EIR_DEVICE_ID: if (data_len < 8) break; eir->did_source = data[0] | (data[1] << 8); eir->did_vendor = data[2] | (data[3] << 8); eir->did_product = data[4] | (data[5] << 8); eir->did_version = data[6] | (data[7] << 8); break; case EIR_SVC_DATA16: eir_parse_uuid16_data(eir, data, data_len); break; case EIR_SVC_DATA32: eir_parse_uuid32_data(eir, data, data_len); break; case EIR_SVC_DATA128: eir_parse_uuid128_data(eir, data, data_len); break; case EIR_MANUFACTURER_DATA: eir_parse_msd(eir, data, data_len); break; default: eir_parse_data(eir, eir_data[1], data, data_len); break; } eir_data += field_len + 1; } } int eir_parse_oob(struct eir_data *eir, uint8_t *eir_data, uint16_t eir_len) { if (eir_len < EIR_OOB_MIN) return -1; if (eir_len != get_le16(eir_data)) return -1; eir_data += sizeof(uint16_t); eir_len -= sizeof(uint16_t); memcpy(&eir->addr, eir_data, sizeof(bdaddr_t)); eir_data += sizeof(bdaddr_t); eir_len -= sizeof(bdaddr_t); /* optional OOB EIR data */ if (eir_len > 0) eir_parse(eir, eir_data, eir_len); return 0; } #define SIZEOF_UUID128 16 static void eir_generate_uuid128(sdp_list_t *list, uint8_t *ptr, uint16_t *eir_len) { int i, k, uuid_count = 0; uint16_t len = *eir_len; uint8_t *uuid128; bool truncated = false; /* Store UUIDs in place, skip 2 bytes to write type and length later */ uuid128 = ptr + 2; for (; list; list = list->next) { sdp_record_t *rec = list->data; uuid_t *uuid = &rec->svclass; uint8_t *uuid128_data = uuid->value.uuid128.data; if (uuid->type != SDP_UUID128) continue; /* Stop if not enough space to put next UUID128 */ if ((len + 2 + SIZEOF_UUID128) > HCI_MAX_EIR_LENGTH) { truncated = true; break; } /* Check for duplicates, EIR data is Little Endian */ for (i = 0; i < uuid_count; i++) { for (k = 0; k < SIZEOF_UUID128; k++) { if (uuid128[i * SIZEOF_UUID128 + k] != uuid128_data[SIZEOF_UUID128 - 1 - k]) break; } if (k == SIZEOF_UUID128) break; } if (i < uuid_count) continue; /* EIR data is Little Endian */ for (k = 0; k < SIZEOF_UUID128; k++) uuid128[uuid_count * SIZEOF_UUID128 + k] = uuid128_data[SIZEOF_UUID128 - 1 - k]; len += SIZEOF_UUID128; uuid_count++; } if (uuid_count > 0 || truncated) { /* EIR Data length */ ptr[0] = (uuid_count * SIZEOF_UUID128) + 1; /* EIR Data type */ ptr[1] = truncated ? EIR_UUID128_SOME : EIR_UUID128_ALL; len += 2; *eir_len = len; } } int eir_create_oob(const bdaddr_t *addr, const char *name, uint32_t cod, const uint8_t *hash, const uint8_t *randomizer, uint16_t did_vendor, uint16_t did_product, uint16_t did_version, uint16_t did_source, sdp_list_t *uuids, uint8_t *data) { sdp_list_t *l; uint8_t *ptr = data; uint16_t eir_optional_len = 0; uint16_t eir_total_len; uint16_t uuid16[HCI_MAX_EIR_LENGTH / 2]; int i, uuid_count = 0; bool truncated = false; size_t name_len; eir_total_len = sizeof(uint16_t) + sizeof(bdaddr_t); ptr += sizeof(uint16_t); memcpy(ptr, addr, sizeof(bdaddr_t)); ptr += sizeof(bdaddr_t); if (cod > 0) { uint8_t class[3]; class[0] = (uint8_t) cod; class[1] = (uint8_t) (cod >> 8); class[2] = (uint8_t) (cod >> 16); *ptr++ = 4; *ptr++ = EIR_CLASS_OF_DEV; memcpy(ptr, class, sizeof(class)); ptr += sizeof(class); eir_optional_len += sizeof(class) + 2; } if (hash) { *ptr++ = 17; *ptr++ = EIR_SSP_HASH; memcpy(ptr, hash, 16); ptr += 16; eir_optional_len += 16 + 2; } if (randomizer) { *ptr++ = 17; *ptr++ = EIR_SSP_RANDOMIZER; memcpy(ptr, randomizer, 16); ptr += 16; eir_optional_len += 16 + 2; } name_len = strlen(name); if (name_len > 0) { /* EIR Data type */ if (name_len > 48) { name_len = 48; ptr[1] = EIR_NAME_SHORT; } else ptr[1] = EIR_NAME_COMPLETE; /* EIR Data length */ ptr[0] = name_len + 1; memcpy(ptr + 2, name, name_len); eir_optional_len += (name_len + 2); ptr += (name_len + 2); } if (did_vendor != 0x0000) { *ptr++ = 9; *ptr++ = EIR_DEVICE_ID; *ptr++ = (did_source & 0x00ff); *ptr++ = (did_source & 0xff00) >> 8; *ptr++ = (did_vendor & 0x00ff); *ptr++ = (did_vendor & 0xff00) >> 8; *ptr++ = (did_product & 0x00ff); *ptr++ = (did_product & 0xff00) >> 8; *ptr++ = (did_version & 0x00ff); *ptr++ = (did_version & 0xff00) >> 8; eir_optional_len += 10; } /* Group all UUID16 types */ for (l = uuids; l != NULL; l = l->next) { sdp_record_t *rec = l->data; uuid_t *uuid = &rec->svclass; if (uuid->type != SDP_UUID16) continue; if (uuid->value.uuid16 < 0x1100) continue; if (uuid->value.uuid16 == PNP_INFO_SVCLASS_ID) continue; /* Stop if not enough space to put next UUID16 */ if ((eir_optional_len + 2 + sizeof(uint16_t)) > HCI_MAX_EIR_LENGTH) { truncated = true; break; } /* Check for duplicates */ for (i = 0; i < uuid_count; i++) if (uuid16[i] == uuid->value.uuid16) break; if (i < uuid_count) continue; uuid16[uuid_count++] = uuid->value.uuid16; eir_optional_len += sizeof(uint16_t); } if (uuid_count > 0) { /* EIR Data length */ ptr[0] = (uuid_count * sizeof(uint16_t)) + 1; /* EIR Data type */ ptr[1] = truncated ? EIR_UUID16_SOME : EIR_UUID16_ALL; ptr += 2; eir_optional_len += 2; for (i = 0; i < uuid_count; i++) { *ptr++ = (uuid16[i] & 0x00ff); *ptr++ = (uuid16[i] & 0xff00) >> 8; } } /* Group all UUID128 types */ if (eir_optional_len <= HCI_MAX_EIR_LENGTH - 2) eir_generate_uuid128(uuids, ptr, &eir_optional_len); eir_total_len += eir_optional_len; /* store total length */ put_le16(eir_total_len, data); return eir_total_len; } static int match_sd_uuid(const void *data, const void *user_data) { const struct eir_sd *sd = data; const char *uuid = user_data; return strcmp(sd->uuid, uuid); } struct eir_sd *eir_get_service_data(struct eir_data *eir, const char *uuid) { GSList *l; if (!eir || !uuid) return NULL; l = g_slist_find_custom(eir->sd_list, uuid, match_sd_uuid); if (!l) return NULL; return l->data; } bluez-5.82/src/PaxHeaders/error.c0000644000000000000000000000005014471706460013743 xustar0020 atime=1743516635 20 ctime=1743591284 bluez-5.82/src/error.c0000644000000000000000000001250114471706460013423 0ustar00rootroot// SPDX-License-Identifier: GPL-2.0-or-later /* * * BlueZ - Bluetooth protocol stack for Linux * * Copyright (C) 2006-2010 Nokia Corporation * Copyright (C) 2004-2010 Marcel Holtmann * Copyright (C) 2007-2008 Fabien Chevalier * * */ #ifdef HAVE_CONFIG_H #include #endif #include #include #include "gdbus/gdbus.h" #include "error.h" DBusMessage *btd_error_invalid_args(DBusMessage *msg) { return btd_error_invalid_args_str(msg, "Invalid arguments in method call"); } DBusMessage *btd_error_invalid_args_str(DBusMessage *msg, const char *str) { return g_dbus_create_error(msg, ERROR_INTERFACE ".InvalidArguments", "%s", str); } DBusMessage *btd_error_busy(DBusMessage *msg) { return g_dbus_create_error(msg, ERROR_INTERFACE ".InProgress", "Operation already in progress"); } DBusMessage *btd_error_already_exists(DBusMessage *msg) { return g_dbus_create_error(msg, ERROR_INTERFACE ".AlreadyExists", "Already Exists"); } DBusMessage *btd_error_not_supported(DBusMessage *msg) { return g_dbus_create_error(msg, ERROR_INTERFACE ".NotSupported", "Operation is not supported"); } DBusMessage *btd_error_not_connected(DBusMessage *msg) { return g_dbus_create_error(msg, ERROR_INTERFACE ".NotConnected", "Not Connected"); } DBusMessage *btd_error_already_connected(DBusMessage *msg) { return g_dbus_create_error(msg, ERROR_INTERFACE ".AlreadyConnected", "Already Connected"); } DBusMessage *btd_error_in_progress(DBusMessage *msg) { return g_dbus_create_error(msg, ERROR_INTERFACE ".InProgress", "In Progress"); } DBusMessage *btd_error_in_progress_str(DBusMessage *msg, const char *str) { return g_dbus_create_error(msg, ERROR_INTERFACE ".InProgress", "%s", str); } DBusMessage *btd_error_not_available(DBusMessage *msg) { return g_dbus_create_error(msg, ERROR_INTERFACE ".NotAvailable", "Operation currently not available"); } DBusMessage *btd_error_not_available_str(DBusMessage *msg, const char *str) { return g_dbus_create_error(msg, ERROR_INTERFACE ".NotAvailable", "%s", str); } DBusMessage *btd_error_does_not_exist(DBusMessage *msg) { return g_dbus_create_error(msg, ERROR_INTERFACE ".DoesNotExist", "Does Not Exist"); } DBusMessage *btd_error_not_authorized(DBusMessage *msg) { return g_dbus_create_error(msg, ERROR_INTERFACE ".NotAuthorized", "Operation Not Authorized"); } DBusMessage *btd_error_not_permitted(DBusMessage *msg, const char *str) { return g_dbus_create_error(msg, ERROR_INTERFACE ".NotPermitted", "%s", str); } DBusMessage *btd_error_no_such_adapter(DBusMessage *msg) { return g_dbus_create_error(msg, ERROR_INTERFACE ".NoSuchAdapter", "No such adapter"); } DBusMessage *btd_error_agent_not_available(DBusMessage *msg) { return g_dbus_create_error(msg, ERROR_INTERFACE ".AgentNotAvailable", "Agent Not Available"); } DBusMessage *btd_error_not_ready(DBusMessage *msg) { return g_dbus_create_error(msg, ERROR_INTERFACE ".NotReady", "Resource Not Ready"); } DBusMessage *btd_error_not_ready_str(DBusMessage *msg, const char *str) { return g_dbus_create_error(msg, ERROR_INTERFACE ".NotReady", "%s", str); } DBusMessage *btd_error_failed(DBusMessage *msg, const char *str) { return g_dbus_create_error(msg, ERROR_INTERFACE ".Failed", "%s", str); } const char *btd_error_bredr_conn_from_errno(int errno_code) { switch (-errno_code) { case EALREADY: case EISCONN: return ERR_BREDR_CONN_ALREADY_CONNECTED; case EHOSTDOWN: return ERR_BREDR_CONN_PAGE_TIMEOUT; case ENOPROTOOPT: return ERR_BREDR_CONN_PROFILE_UNAVAILABLE; case EIO: return ERR_BREDR_CONN_CREATE_SOCKET; case EINVAL: return ERR_BREDR_CONN_INVALID_ARGUMENTS; case EHOSTUNREACH: return ERR_BREDR_CONN_ADAPTER_NOT_POWERED; case EOPNOTSUPP: case EPROTONOSUPPORT: return ERR_BREDR_CONN_NOT_SUPPORTED; case EBADFD: return ERR_BREDR_CONN_BAD_SOCKET; case ENOMEM: return ERR_BREDR_CONN_MEMORY_ALLOC; case EBUSY: return ERR_BREDR_CONN_BUSY; case EMLINK: return ERR_BREDR_CONN_CNCR_CONNECT_LIMIT; case ETIMEDOUT: return ERR_BREDR_CONN_TIMEOUT; case ECONNREFUSED: return ERR_BREDR_CONN_REFUSED; case ECONNRESET: return ERR_BREDR_CONN_ABORT_BY_REMOTE; case ECONNABORTED: return ERR_BREDR_CONN_ABORT_BY_LOCAL; case EPROTO: return ERR_BREDR_CONN_LMP_PROTO_ERROR; case EBADE: return ERR_BREDR_CONN_KEY_MISSING; default: return ERR_BREDR_CONN_UNKNOWN; } } const char *btd_error_le_conn_from_errno(int errno_code) { switch (-errno_code) { case EINVAL: return ERR_LE_CONN_INVALID_ARGUMENTS; case EHOSTUNREACH: return ERR_LE_CONN_ADAPTER_NOT_POWERED; case EOPNOTSUPP: case EPROTONOSUPPORT: return ERR_LE_CONN_NOT_SUPPORTED; case EALREADY: case EISCONN: return ERR_LE_CONN_ALREADY_CONNECTED; case EBADFD: return ERR_LE_CONN_BAD_SOCKET; case ENOMEM: return ERR_LE_CONN_MEMORY_ALLOC; case EBUSY: return ERR_LE_CONN_BUSY; case ECONNREFUSED: return ERR_LE_CONN_REFUSED; case EIO: return ERR_LE_CONN_CREATE_SOCKET; case ETIMEDOUT: return ERR_LE_CONN_TIMEOUT; case EMLINK: return ERR_LE_CONN_SYNC_CONNECT_LIMIT; case ECONNRESET: return ERR_LE_CONN_ABORT_BY_REMOTE; case ECONNABORTED: return ERR_LE_CONN_ABORT_BY_LOCAL; case EPROTO: return ERR_LE_CONN_LL_PROTO_ERROR; case EBADE: return ERR_LE_CONN_KEY_MISSING; default: return ERR_LE_CONN_UNKNOWN; } } bluez-5.82/src/PaxHeaders/genbuiltin0000644000000000000000000000005014572354773014541 xustar0020 atime=1743515767 20 ctime=1743591288 bluez-5.82/src/genbuiltin0000755000000000000000000000041414572354773014224 0ustar00rootroot#!/bin/sh for i in $* do echo "extern const struct bluetooth_plugin_desc __bluetooth_builtin_$i;" done echo echo "static const struct bluetooth_plugin_desc *__bluetooth_builtin[] = {" for i in $* do echo " &__bluetooth_builtin_$i," done echo " NULL" echo "};" bluez-5.82/src/PaxHeaders/log.c0000644000000000000000000000005014572354773013403 xustar0020 atime=1743516084 20 ctime=1743591278 bluez-5.82/src/log.c0000644000000000000000000000646014572354773013072 0ustar00rootroot// SPDX-License-Identifier: GPL-2.0-or-later /* * * BlueZ - Bluetooth protocol stack for Linux * * Copyright (C) 2004-2010 Marcel Holtmann * * */ #ifdef HAVE_CONFIG_H #include #endif #define _GNU_SOURCE #include #include #include #include #include #include #include #include #include #include #include "lib/bluetooth.h" #include "lib/hci.h" #include "src/shared/util.h" #include "src/shared/log.h" #include "log.h" #define LOG_IDENT "bluetoothd" static void monitor_log(uint16_t index, int priority, const char *format, va_list ap) { bt_log_vprintf(index, LOG_IDENT, priority, format, ap); } void info(const char *format, ...) { va_list ap; va_start(ap, format); vsyslog(LOG_INFO, format, ap); va_end(ap); va_start(ap, format); monitor_log(HCI_DEV_NONE, LOG_INFO, format, ap); va_end(ap); } void btd_log(uint16_t index, int priority, const char *format, ...) { va_list ap; va_start(ap, format); vsyslog(priority, format, ap); va_end(ap); va_start(ap, format); monitor_log(index, priority, format, ap); va_end(ap); } void btd_error(uint16_t index, const char *format, ...) { va_list ap; va_start(ap, format); vsyslog(LOG_ERR, format, ap); va_end(ap); va_start(ap, format); monitor_log(index, LOG_ERR, format, ap); va_end(ap); } void btd_warn(uint16_t index, const char *format, ...) { va_list ap; va_start(ap, format); vsyslog(LOG_WARNING, format, ap); va_end(ap); va_start(ap, format); monitor_log(index, LOG_WARNING, format, ap); va_end(ap); } void btd_info(uint16_t index, const char *format, ...) { va_list ap; va_start(ap, format); vsyslog(LOG_INFO, format, ap); va_end(ap); va_start(ap, format); monitor_log(index, LOG_INFO, format, ap); va_end(ap); } void btd_debug(uint16_t index, const char *format, ...) { va_list ap; va_start(ap, format); vsyslog(LOG_DEBUG, format, ap); va_end(ap); va_start(ap, format); monitor_log(index, LOG_DEBUG, format, ap); va_end(ap); } extern struct btd_debug_desc __start___debug[]; extern struct btd_debug_desc __stop___debug[]; static char **enabled = NULL; static gboolean is_enabled(const struct btd_debug_desc *desc) { int i; if (enabled == NULL) return 0; for (i = 0; enabled[i] != NULL; i++) if (desc->file != NULL && g_pattern_match_simple(enabled[i], desc->file) == TRUE) return 1; return 0; } void __btd_enable_debug(struct btd_debug_desc *start, struct btd_debug_desc *stop) { struct btd_debug_desc *desc; if (start == NULL || stop == NULL) return; for (desc = start; desc < stop; desc++) { if (is_enabled(desc)) desc->flags |= BTD_DEBUG_FLAG_PRINT; } } void __btd_toggle_debug(void) { struct btd_debug_desc *desc; for (desc = __start___debug; desc < __stop___debug; desc++) desc->flags |= BTD_DEBUG_FLAG_PRINT; } void __btd_log_init(const char *debug, int detach) { int option = LOG_NDELAY | LOG_PID; if (debug != NULL) enabled = g_strsplit_set(debug, ":, ", 0); __btd_enable_debug(__start___debug, __stop___debug); bt_log_open(); if (!detach) option |= LOG_PERROR; openlog(LOG_IDENT, option, LOG_DAEMON); info("Bluetooth daemon %s", VERSION); } void __btd_log_cleanup(void) { closelog(); bt_log_close(); g_strfreev(enabled); } bluez-5.82/src/PaxHeaders/advertising.c0000644000000000000000000000005014766002272015126 xustar0020 atime=1743515578 20 ctime=1743591284 bluez-5.82/src/advertising.c0000644000000000000000000014134614766002272014620 0ustar00rootroot// SPDX-License-Identifier: GPL-2.0-or-later /* * * BlueZ - Bluetooth protocol stack for Linux * * Copyright (C) 2015 Google Inc. * * */ #ifdef HAVE_CONFIG_H #include #endif #define _GNU_SOURCE #include #include #include #include #include #include "lib/bluetooth.h" #include "lib/mgmt.h" #include "lib/sdp.h" #include "adapter.h" #include "dbus-common.h" #include "error.h" #include "log.h" #include "eir.h" #include "btd.h" #include "src/shared/ad.h" #include "src/shared/mgmt.h" #include "src/shared/queue.h" #include "src/shared/timeout.h" #include "src/shared/util.h" #include "src/shared/crypto.h" #include "advertising.h" #define LE_ADVERTISING_MGR_IFACE "org.bluez.LEAdvertisingManager1" #define LE_ADVERTISEMENT_IFACE "org.bluez.LEAdvertisement1" struct btd_adv_manager { struct btd_adapter *adapter; struct queue *clients; struct mgmt *mgmt; uint16_t mgmt_index; uint8_t max_adv_len; uint8_t max_scan_rsp_len; uint8_t max_ads; uint32_t supported_flags; uint64_t instance_bitmap; bool extended_add_cmds; int8_t min_tx_power; int8_t max_tx_power; }; #define AD_TYPE_BROADCAST 0 #define AD_TYPE_PERIPHERAL 1 /* BLUETOOTH SPECIFICATION Version 5.2 | Vol 4, Part E, page 2585 * defines tx power value indicating no preference */ #define ADV_TX_POWER_NO_PREFERENCE 0x7F struct btd_adv_client { struct btd_adv_manager *manager; char *owner; char *path; char *name; uint16_t appearance; uint16_t duration; uint16_t timeout; uint16_t discoverable_to; unsigned int to_id; unsigned int disc_to_id; unsigned int add_adv_id; GDBusClient *client; GDBusProxy *proxy; DBusMessage *reg; uint8_t type; /* Advertising type */ uint32_t flags; struct bt_ad *data; struct bt_ad *scan; uint8_t instance; uint32_t min_interval; uint32_t max_interval; int8_t tx_power; mgmt_request_func_t refresh_done_func; }; struct dbus_obj_match { const char *owner; const char *path; }; static bool match_client(const void *a, const void *b) { const struct btd_adv_client *client = a; const struct dbus_obj_match *match = b; if (match->owner && g_strcmp0(client->owner, match->owner)) return false; if (match->path && g_strcmp0(client->path, match->path)) return false; return true; } static void client_free(void *data) { struct btd_adv_client *client = data; if (client->to_id > 0) timeout_remove(client->to_id); if (client->disc_to_id > 0) timeout_remove(client->disc_to_id); if (client->client) { g_dbus_client_set_disconnect_watch(client->client, NULL, NULL); g_dbus_client_unref(client->client); } if (client->reg) { DBusMessage *reply; reply = btd_error_failed(client->reg, "Failed to complete registration"); g_dbus_send_message(btd_get_dbus_connection(), reply); dbus_message_unref(client->reg); client->reg = NULL; } if (client->add_adv_id) mgmt_cancel(client->manager->mgmt, client->add_adv_id); if (client->instance) util_clear_uid(&client->manager->instance_bitmap, client->instance); bt_ad_unref(client->data); bt_ad_unref(client->scan); g_dbus_proxy_unref(client->proxy); if (client->owner) g_free(client->owner); if (client->path) g_free(client->path); free(client->name); free(client); } static gboolean client_free_idle_cb(void *data) { client_free(data); return FALSE; } static void client_release(void *data) { struct btd_adv_client *client = data; DBG("Releasing advertisement %s, %s", client->owner, client->path); g_dbus_proxy_method_call(client->proxy, "Release", NULL, NULL, NULL, NULL); } static void client_destroy(void *data) { client_release(data); client_free(data); } static void remove_advertising(struct btd_adv_manager *manager, uint8_t instance) { struct mgmt_cp_remove_advertising cp; if (instance) DBG("instance %u", instance); else DBG("all instances"); cp.instance = instance; mgmt_send(manager->mgmt, MGMT_OP_REMOVE_ADVERTISING, manager->mgmt_index, sizeof(cp), &cp, NULL, NULL, NULL); } static void client_remove(void *data) { struct btd_adv_client *client = data; struct mgmt_cp_remove_advertising cp; g_dbus_client_set_proxy_handlers(client->client, NULL, NULL, NULL, client); g_dbus_client_set_disconnect_watch(client->client, NULL, NULL); cp.instance = client->instance; mgmt_send(client->manager->mgmt, MGMT_OP_REMOVE_ADVERTISING, client->manager->mgmt_index, sizeof(cp), &cp, NULL, NULL, NULL); queue_remove(client->manager->clients, client); g_idle_add(client_free_idle_cb, client); g_dbus_emit_property_changed(btd_get_dbus_connection(), adapter_get_path(client->manager->adapter), LE_ADVERTISING_MGR_IFACE, "SupportedInstances"); g_dbus_emit_property_changed(btd_get_dbus_connection(), adapter_get_path(client->manager->adapter), LE_ADVERTISING_MGR_IFACE, "ActiveInstances"); } static void client_disconnect_cb(DBusConnection *conn, void *user_data) { DBG("Client disconnected"); client_remove(user_data); } static bool parse_type(DBusMessageIter *iter, struct btd_adv_client *client) { const char *msg_type; if (!iter) return true; if (dbus_message_iter_get_arg_type(iter) != DBUS_TYPE_STRING) return false; dbus_message_iter_get_basic(iter, &msg_type); if (!g_strcmp0(msg_type, "broadcast")) { client->type = AD_TYPE_BROADCAST; return true; } if (!g_strcmp0(msg_type, "peripheral")) { client->type = AD_TYPE_PERIPHERAL; return true; } return false; } static bool parse_service_uuids(DBusMessageIter *iter, struct bt_ad *ad) { DBusMessageIter ariter; if (!iter) { bt_ad_clear_service_uuid(ad); return true; } if (dbus_message_iter_get_arg_type(iter) != DBUS_TYPE_ARRAY) return false; dbus_message_iter_recurse(iter, &ariter); bt_ad_clear_service_uuid(ad); while (dbus_message_iter_get_arg_type(&ariter) == DBUS_TYPE_STRING) { const char *uuid_str; bt_uuid_t uuid; dbus_message_iter_get_basic(&ariter, &uuid_str); DBG("Adding ServiceUUID: %s", uuid_str); if (bt_string_to_uuid(&uuid, uuid_str) < 0) goto fail; if (!bt_ad_add_service_uuid(ad, &uuid)) goto fail; dbus_message_iter_next(&ariter); } return true; fail: bt_ad_clear_service_uuid(ad); return false; } static bool parse_service_uuids_ad(DBusMessageIter *iter, struct btd_adv_client *client) { return parse_service_uuids(iter, client->data); } static bool parse_service_uuids_sr(DBusMessageIter *iter, struct btd_adv_client *client) { return parse_service_uuids(iter, client->scan); } static bool parse_solicit_uuids(DBusMessageIter *iter, struct bt_ad *ad) { DBusMessageIter ariter; if (!iter) { bt_ad_clear_solicit_uuid(ad); return true; } if (dbus_message_iter_get_arg_type(iter) != DBUS_TYPE_ARRAY) return false; dbus_message_iter_recurse(iter, &ariter); bt_ad_clear_solicit_uuid(ad); while (dbus_message_iter_get_arg_type(&ariter) == DBUS_TYPE_STRING) { const char *uuid_str; bt_uuid_t uuid; dbus_message_iter_get_basic(&ariter, &uuid_str); DBG("Adding SolicitUUID: %s", uuid_str); if (bt_string_to_uuid(&uuid, uuid_str) < 0) goto fail; if (!bt_ad_add_solicit_uuid(ad, &uuid)) goto fail; dbus_message_iter_next(&ariter); } return true; fail: bt_ad_clear_solicit_uuid(ad); return false; } static bool parse_solicit_uuids_ad(DBusMessageIter *iter, struct btd_adv_client *client) { return parse_solicit_uuids(iter, client->data); } static bool parse_solicit_uuids_sr(DBusMessageIter *iter, struct btd_adv_client *client) { return parse_solicit_uuids(iter, client->scan); } static bool parse_manufacturer_data(DBusMessageIter *iter, struct bt_ad *ad) { DBusMessageIter entries; if (!iter) { bt_ad_clear_manufacturer_data(ad); return true; } if (dbus_message_iter_get_arg_type(iter) != DBUS_TYPE_ARRAY) return false; dbus_message_iter_recurse(iter, &entries); bt_ad_clear_manufacturer_data(ad); while (dbus_message_iter_get_arg_type(&entries) == DBUS_TYPE_DICT_ENTRY) { DBusMessageIter value, entry, array; uint16_t manuf_id; uint8_t *manuf_data; int len; dbus_message_iter_recurse(&entries, &entry); dbus_message_iter_get_basic(&entry, &manuf_id); dbus_message_iter_next(&entry); if (dbus_message_iter_get_arg_type(&entry) != DBUS_TYPE_VARIANT) goto fail; dbus_message_iter_recurse(&entry, &value); if (dbus_message_iter_get_arg_type(&value) != DBUS_TYPE_ARRAY) goto fail; dbus_message_iter_recurse(&value, &array); if (dbus_message_iter_get_arg_type(&array) != DBUS_TYPE_BYTE) goto fail; dbus_message_iter_get_fixed_array(&array, &manuf_data, &len); DBG("Adding ManufacturerData for %04x", manuf_id); if (!bt_ad_add_manufacturer_data(ad, manuf_id, manuf_data, len)) goto fail; dbus_message_iter_next(&entries); } return true; fail: bt_ad_clear_manufacturer_data(ad); return false; } static bool parse_manufacturer_data_ad(DBusMessageIter *iter, struct btd_adv_client *client) { return parse_manufacturer_data(iter, client->data); } static bool parse_manufacturer_data_sr(DBusMessageIter *iter, struct btd_adv_client *client) { return parse_manufacturer_data(iter, client->scan); } static bool parse_service_data(DBusMessageIter *iter, struct bt_ad *ad) { DBusMessageIter entries; if (!iter) { bt_ad_clear_service_data(ad); return true; } if (dbus_message_iter_get_arg_type(iter) != DBUS_TYPE_ARRAY) return false; dbus_message_iter_recurse(iter, &entries); bt_ad_clear_service_data(ad); while (dbus_message_iter_get_arg_type(&entries) == DBUS_TYPE_DICT_ENTRY) { DBusMessageIter value, entry, array; const char *uuid_str; bt_uuid_t uuid; uint8_t *service_data; int len; dbus_message_iter_recurse(&entries, &entry); dbus_message_iter_get_basic(&entry, &uuid_str); if (bt_string_to_uuid(&uuid, uuid_str) < 0) goto fail; dbus_message_iter_next(&entry); if (dbus_message_iter_get_arg_type(&entry) != DBUS_TYPE_VARIANT) goto fail; dbus_message_iter_recurse(&entry, &value); if (dbus_message_iter_get_arg_type(&value) != DBUS_TYPE_ARRAY) goto fail; dbus_message_iter_recurse(&value, &array); if (dbus_message_iter_get_arg_type(&array) != DBUS_TYPE_BYTE) goto fail; dbus_message_iter_get_fixed_array(&array, &service_data, &len); DBG("Adding ServiceData for %s", uuid_str); if (!bt_ad_add_service_data(ad, &uuid, service_data, len)) goto fail; dbus_message_iter_next(&entries); } return true; fail: bt_ad_clear_service_data(ad); return false; } static bool parse_service_data_ad(DBusMessageIter *iter, struct btd_adv_client *client) { return parse_service_data(iter, client->data); } static bool parse_service_data_sr(DBusMessageIter *iter, struct btd_adv_client *client) { return parse_service_data(iter, client->scan); } static bool set_rsi(struct btd_adv_client *client) { struct bt_crypto *crypto; uint8_t zero[16] = {}; struct bt_ad_data rsi = { .type = BT_AD_CSIP_RSI }; uint8_t data[6]; bool ret; /* Check if a valid SIRK has been set */ if (!memcmp(btd_opts.csis.sirk, zero, sizeof(zero))) return false; /* Check if RSI needs to be set or data already contains RSI data */ if (!client || bt_ad_has_data(client->data, &rsi)) return true; crypto = bt_crypto_new(); if (!crypto) return false; ret = bt_crypto_random_bytes(crypto, data + 3, sizeof(data) - 3); if (!ret) goto done; ret = bt_crypto_sih(crypto, btd_opts.csis.sirk, data + 3, data); if (!ret) goto done; ret = bt_ad_add_data(client->data, BT_AD_CSIP_RSI, data, sizeof(data)); done: bt_crypto_unref(crypto); return ret; } static struct adv_include { uint8_t flag; const char *name; bool (*set)(struct btd_adv_client *client); } includes[] = { { MGMT_ADV_FLAG_TX_POWER, "tx-power" }, { MGMT_ADV_FLAG_APPEARANCE, "appearance" }, { MGMT_ADV_FLAG_LOCAL_NAME, "local-name" }, { 0 , "rsi", set_rsi }, { }, }; static bool parse_includes(DBusMessageIter *iter, struct btd_adv_client *client) { DBusMessageIter entries; if (!iter) { client->flags = 0; return true; } if (dbus_message_iter_get_arg_type(iter) != DBUS_TYPE_ARRAY) return false; dbus_message_iter_recurse(iter, &entries); /* Reset flags before parsing */ client->flags = 0; while (dbus_message_iter_get_arg_type(&entries) == DBUS_TYPE_STRING) { const char *str; struct adv_include *inc; dbus_message_iter_get_basic(&entries, &str); for (inc = includes; inc && inc->name; inc++) { if (strcmp(str, inc->name)) continue; if (inc->set && inc->set(client)) { DBG("Including Feature: %s", str); continue; } if (!(client->manager->supported_flags & inc->flag)) continue; DBG("Including Feature: %s", str); client->flags |= inc->flag; } dbus_message_iter_next(&entries); } return true; } static bool parse_local_name(DBusMessageIter *iter, struct btd_adv_client *client) { const char *name; if (!iter) { free(client->name); client->name = NULL; return true; } if (dbus_message_iter_get_arg_type(iter) != DBUS_TYPE_STRING) return false; if (client->flags & MGMT_ADV_FLAG_LOCAL_NAME) { error("Local name already included"); return false; } dbus_message_iter_get_basic(iter, &name); free(client->name); /* Treat empty string the same as omitting since there is no point on * adding a empty name as AD data as it just take space that could be * used for something else. */ if (name[0] != '\0') client->name = strdup(name); else client->name = NULL; return true; } static bool parse_appearance(DBusMessageIter *iter, struct btd_adv_client *client) { if (!iter) { client->appearance = 0; return true; } if (dbus_message_iter_get_arg_type(iter) != DBUS_TYPE_UINT16) return false; if (client->flags & MGMT_ADV_FLAG_APPEARANCE) { error("Appearance already included"); return false; } dbus_message_iter_get_basic(iter, &client->appearance); return true; } static bool parse_duration(DBusMessageIter *iter, struct btd_adv_client *client) { if (!iter) { client->duration = 0; return true; } if (dbus_message_iter_get_arg_type(iter) != DBUS_TYPE_UINT16) return false; dbus_message_iter_get_basic(iter, &client->duration); return true; } static bool client_timeout(void *user_data) { struct btd_adv_client *client = user_data; DBG(""); client->to_id = 0; client_release(client); client_remove(client); return FALSE; } static bool parse_timeout(DBusMessageIter *iter, struct btd_adv_client *client) { if (!iter) { client->timeout = 0; timeout_remove(client->to_id); client->to_id = 0; return true; } if (dbus_message_iter_get_arg_type(iter) != DBUS_TYPE_UINT16) return false; dbus_message_iter_get_basic(iter, &client->timeout); if (client->to_id) timeout_remove(client->to_id); if (client->timeout > 0) client->to_id = timeout_add_seconds(client->timeout, client_timeout, client, NULL); return true; } static bool parse_data(DBusMessageIter *iter, struct bt_ad *ad) { DBusMessageIter entries; if (!iter) { bt_ad_clear_data(ad); return true; } if (dbus_message_iter_get_arg_type(iter) != DBUS_TYPE_ARRAY) return false; dbus_message_iter_recurse(iter, &entries); bt_ad_clear_data(ad); while (dbus_message_iter_get_arg_type(&entries) == DBUS_TYPE_DICT_ENTRY) { DBusMessageIter value, entry, array; uint8_t type; uint8_t *data; int len; dbus_message_iter_recurse(&entries, &entry); dbus_message_iter_get_basic(&entry, &type); dbus_message_iter_next(&entry); if (dbus_message_iter_get_arg_type(&entry) != DBUS_TYPE_VARIANT) goto fail; dbus_message_iter_recurse(&entry, &value); if (dbus_message_iter_get_arg_type(&value) != DBUS_TYPE_ARRAY) goto fail; dbus_message_iter_recurse(&value, &array); if (dbus_message_iter_get_arg_type(&array) != DBUS_TYPE_BYTE) goto fail; dbus_message_iter_get_fixed_array(&array, &data, &len); DBG("Adding Data for type 0x%02x len %u", type, len); if (!bt_ad_add_data(ad, type, data, len)) goto fail; dbus_message_iter_next(&entries); } return true; fail: bt_ad_clear_data(ad); return false; } static bool parse_data_ad(DBusMessageIter *iter, struct btd_adv_client *client) { return parse_data(iter, client->data); } static bool parse_data_sr(DBusMessageIter *iter, struct btd_adv_client *client) { return parse_data(iter, client->scan); } static bool set_flags(struct btd_adv_client *client, uint8_t flags) { /* Set BR/EDR Not Supported for LE only */ if (!btd_adapter_get_bredr(client->manager->adapter)) flags |= BT_AD_FLAG_NO_BREDR; /* Set BR/EDR Not Supported if adapter is not discoverable but the * instance is. */ if (!btd_adapter_get_discoverable(client->manager->adapter)) flags |= BT_AD_FLAG_NO_BREDR; if (!bt_ad_add_flags(client->data, &flags, 1)) return false; return true; } static bool parse_discoverable(DBusMessageIter *iter, struct btd_adv_client *client) { uint8_t flags; dbus_bool_t discoverable; if (!iter) { bt_ad_clear_flags(client->data); return true; } if (dbus_message_iter_get_arg_type(iter) != DBUS_TYPE_BOOLEAN) return false; dbus_message_iter_get_basic(iter, &discoverable); /* For broadcast mode, need not add any flags * just return true without adding flags. */ if (discoverable) flags = BT_AD_FLAG_GENERAL; else if (client->type != AD_TYPE_BROADCAST) flags = 0x00; else return true; if (!set_flags(client , flags)) goto fail; DBG("Adding Flags 0x%02x", flags); return true; fail: bt_ad_clear_flags(client->data); return false; } static size_t calc_max_adv_len(struct btd_adv_client *client, uint32_t flags) { size_t max = client->manager->max_adv_len; /* * Flags which reduce the amount of space available for advertising. * See doc/mgmt-api.txt */ if (flags & MGMT_ADV_FLAG_TX_POWER) max -= 3; if (flags & (MGMT_ADV_FLAG_DISCOV | MGMT_ADV_FLAG_LIMITED_DISCOV | MGMT_ADV_FLAG_MANAGED_FLAGS)) max -= 3; if (flags & MGMT_ADV_FLAG_APPEARANCE) max -= 4; return max; } static uint8_t *generate_adv_data(struct btd_adv_client *client, uint32_t *flags, size_t *len) { if ((*flags & MGMT_ADV_FLAG_APPEARANCE) || client->appearance != UINT16_MAX) { uint16_t appearance; appearance = client->appearance; if (appearance == UINT16_MAX) /* TODO: Get the appearance from the adaptor once * supported. */ appearance = 0x000; bt_ad_add_appearance(client->data, appearance); } /* Scan response shall not be used when connectable and setting a * secondary PHY since that would end up using EA types instead of * legacy which doesn't support being connectable and scannable * simultaneously. */ if ((*flags & MGMT_ADV_FLAG_CONNECTABLE) && (*flags & MGMT_ADV_FLAG_SEC_MASK) && client->name) { *flags &= ~MGMT_ADV_FLAG_LOCAL_NAME; bt_ad_add_name(client->data, client->name); } return bt_ad_generate(client->data, len); } static uint8_t *generate_scan_rsp(struct btd_adv_client *client, uint32_t *flags, size_t *len) { if (client->name) { *flags &= ~MGMT_ADV_FLAG_LOCAL_NAME; bt_ad_add_name(client->scan, client->name); } else if (bt_ad_is_empty(client->scan)) { *len = 0; return NULL; } return bt_ad_generate(client->scan, len); } static bool adv_client_has_scan_response(struct btd_adv_client *client, uint32_t flags) { /* The local name isn't added into the bt_ad structure until * generate_scan_rsp is called, so we must check these conditions as * well. */ if (!(flags & MGMT_ADV_FLAG_LOCAL_NAME) && !client->name && bt_ad_is_empty(client->scan)) { return false; } /* Scan response shall not be used when connectable and setting a * secondary PHY since that would end up using EA types instead of * legacy which doesn't support being connectable and scannable * simultaneously. */ if (flags & MGMT_ADV_FLAG_CONNECTABLE && flags & MGMT_ADV_FLAG_SEC_MASK) return false; return true; } static int get_adv_flags(struct btd_adv_client *client) { uint32_t flags = 0; if (client->type == AD_TYPE_PERIPHERAL) { flags = MGMT_ADV_FLAG_CONNECTABLE; if (btd_adapter_get_discoverable(client->manager->adapter) && !(bt_ad_has_flags(client->data))) flags |= MGMT_ADV_FLAG_DISCOV; } flags |= client->flags; /* Detect if the length is bigger that legacy and secondary is not set * then force it to be set to ensure the kernel uses EA. */ if (bt_ad_length(client->data) > BT_AD_MAX_DATA_LEN && !(flags & MGMT_ADV_FLAG_SEC_MASK)) flags |= MGMT_ADV_FLAG_SEC_1M; return flags; } static int refresh_legacy_adv(struct btd_adv_client *client, mgmt_request_func_t func) { struct mgmt_cp_add_advertising *cp; uint8_t param_len; uint8_t *adv_data; size_t adv_data_len; uint8_t *scan_rsp; size_t scan_rsp_len = -1; uint32_t flags = 0; unsigned int mgmt_ret; DBG("Refreshing advertisement: %s", client->path); flags = get_adv_flags(client); adv_data = generate_adv_data(client, &flags, &adv_data_len); if (!adv_data || (adv_data_len > calc_max_adv_len(client, flags))) { error("Advertising data too long or couldn't be generated."); return -EINVAL; } scan_rsp = generate_scan_rsp(client, &flags, &scan_rsp_len); if (!scan_rsp && scan_rsp_len) { error("Scan data couldn't be generated."); free(adv_data); return -EINVAL; } param_len = sizeof(struct mgmt_cp_add_advertising) + adv_data_len + scan_rsp_len; cp = malloc0(param_len); if (!cp) { error("Couldn't allocate for MGMT!"); free(adv_data); free(scan_rsp); return -ENOMEM; } cp->flags = htobl(flags); cp->instance = client->instance; cp->duration = client->duration; cp->adv_data_len = adv_data_len; cp->scan_rsp_len = scan_rsp_len; memcpy(cp->data, adv_data, adv_data_len); if (scan_rsp) memcpy(cp->data + adv_data_len, scan_rsp, scan_rsp_len); free(adv_data); free(scan_rsp); mgmt_ret = mgmt_send(client->manager->mgmt, MGMT_OP_ADD_ADVERTISING, client->manager->mgmt_index, param_len, cp, func, client, NULL); if (!mgmt_ret) { error("Failed to add Advertising Data"); free(cp); return -EINVAL; } if (func) client->add_adv_id = mgmt_ret; free(cp); return 0; } static void add_adv_params_callback(uint8_t status, uint16_t length, const void *param, void *user_data); static int refresh_extended_adv(struct btd_adv_client *client, mgmt_request_func_t func) { struct mgmt_cp_add_ext_adv_params cp; uint32_t flags = 0; unsigned int mgmt_ret = 0; DBG("Refreshing advertisement parameters: %s", client->path); flags = get_adv_flags(client); memset(&cp, 0, sizeof(cp)); cp.instance = client->instance; /* Not all advertising instances will use all possible parameters. The * included_params bit field tells the kernel which parameters are * relevant, and sensible defaults will be used for the rest */ if (client->duration) { cp.duration = client->duration; flags |= MGMT_ADV_PARAM_DURATION; } if (client->min_interval && client->max_interval) { cp.min_interval = client->min_interval; cp.max_interval = client->max_interval; flags |= MGMT_ADV_PARAM_INTERVALS; } if (client->tx_power != ADV_TX_POWER_NO_PREFERENCE) { cp.tx_power = client->tx_power; flags |= MGMT_ADV_PARAM_TX_POWER; } /* Indicate that this instance will be configured as scannable */ if (adv_client_has_scan_response(client, flags) && client->manager->supported_flags & MGMT_ADV_PARAM_SCAN_RSP) { flags |= MGMT_ADV_PARAM_SCAN_RSP; } cp.flags = cpu_to_le32(flags); mgmt_ret = mgmt_send(client->manager->mgmt, MGMT_OP_ADD_EXT_ADV_PARAMS, client->manager->mgmt_index, sizeof(cp), &cp, add_adv_params_callback, client, NULL); if (!mgmt_ret) { error("Failed to request extended advertising parameters"); return -EINVAL; } /* Store callback, called after we set advertising data */ client->refresh_done_func = func; client->add_adv_id = mgmt_ret; return 0; } static int refresh_advertisement(struct btd_adv_client *client, mgmt_request_func_t func) { if (client->manager->extended_add_cmds) return refresh_extended_adv(client, func); return refresh_legacy_adv(client, func); } static bool client_discoverable_timeout(void *user_data) { struct btd_adv_client *client = user_data; DBG(""); client->disc_to_id = 0; bt_ad_clear_flags(client->data); refresh_advertisement(client, NULL); return FALSE; } static bool parse_discoverable_timeout(DBusMessageIter *iter, struct btd_adv_client *client) { if (!iter) { client->discoverable_to = 0; timeout_remove(client->disc_to_id); client->disc_to_id = 0; return true; } if (dbus_message_iter_get_arg_type(iter) != DBUS_TYPE_UINT16) return false; dbus_message_iter_get_basic(iter, &client->discoverable_to); if (client->disc_to_id) timeout_remove(client->disc_to_id); if (client->discoverable_to > 0) client->disc_to_id = timeout_add_seconds( client->discoverable_to, client_discoverable_timeout, client, NULL); return true; } static struct adv_secondary { int flag; const char *name; } secondary[] = { { MGMT_ADV_FLAG_SEC_1M, "1M" }, { MGMT_ADV_FLAG_SEC_2M, "2M" }, { MGMT_ADV_FLAG_SEC_CODED, "Coded" }, { }, }; static bool parse_secondary(DBusMessageIter *iter, struct btd_adv_client *client) { const char *str; struct adv_secondary *sec; if (!iter) { /* Reset secondary channels */ client->flags &= ~MGMT_ADV_FLAG_SEC_MASK; return true; } if (dbus_message_iter_get_arg_type(iter) != DBUS_TYPE_STRING) return false; /* Reset secondary channels before parsing */ client->flags &= ~MGMT_ADV_FLAG_SEC_MASK; dbus_message_iter_get_basic(iter, &str); for (sec = secondary; sec->name; sec++) { if (strcmp(str, sec->name)) continue; if (!(client->manager->supported_flags & sec->flag)) return false; DBG("Secondary Channel: %s", str); client->flags |= sec->flag; return true; } return false; } static bool parse_min_interval(DBusMessageIter *iter, struct btd_adv_client *client) { uint32_t min_interval_ms; if (!iter) { client->min_interval = 0; return false; } if (dbus_message_iter_get_arg_type(iter) != DBUS_TYPE_UINT32) return false; dbus_message_iter_get_basic(iter, &min_interval_ms); /* Convert ms to jiffies to be used in adv request */ client->min_interval = min_interval_ms / 0.625; /* BLUETOOTH SPECIFICATION Version 5.2 | Vol 4, Part E, page 2584 * defines acceptable interval range */ if (client->min_interval < 0x20 || client->min_interval > 0xFFFFFF) { client->min_interval = 0; return false; } return true; } static bool parse_max_interval(DBusMessageIter *iter, struct btd_adv_client *client) { uint32_t max_interval_ms; if (!iter) { client->max_interval = 0; return false; } if (dbus_message_iter_get_arg_type(iter) != DBUS_TYPE_UINT32) return false; dbus_message_iter_get_basic(iter, &max_interval_ms); /* Convert ms to jiffies to be used in adv request */ client->max_interval = max_interval_ms / 0.625; /* BLUETOOTH SPECIFICATION Version 5.2 | Vol 4, Part E, page 2584 * defines acceptable interval range */ if (client->max_interval < 0x20 || client->max_interval > 0xFFFFFF) { client->max_interval = 0; return false; } return true; } static bool parse_tx_power(DBusMessageIter *iter, struct btd_adv_client *client) { int16_t val; if (!iter) { client->tx_power = ADV_TX_POWER_NO_PREFERENCE; return false; } if (dbus_message_iter_get_arg_type(iter) != DBUS_TYPE_INT16) return false; dbus_message_iter_get_basic(iter, &val); /* BLUETOOTH SPECIFICATION Version 5.2 | Vol 4, Part E, page 2585 * defines acceptable tx power range */ if (val < -127 || val > 20) return false; client->tx_power = val; return true; } static struct adv_parser { const char *name; bool (*func)(DBusMessageIter *iter, struct btd_adv_client *client); bool experimental; } parsers[] = { { "Type", parse_type }, { "ServiceUUIDs", parse_service_uuids_ad }, { "ScanResponseServiceUUIDs", parse_service_uuids_sr, true }, { "SolicitUUIDs", parse_solicit_uuids_ad }, { "ScanResponseSolicitUUIDs", parse_solicit_uuids_sr, true }, { "ManufacturerData", parse_manufacturer_data_ad }, { "ScanResponseManufacturerData", parse_manufacturer_data_sr, true }, { "ServiceData", parse_service_data_ad }, { "ScanResponseServiceData", parse_service_data_sr, true }, { "Includes", parse_includes }, { "LocalName", parse_local_name }, { "Appearance", parse_appearance }, { "Duration", parse_duration }, { "Timeout", parse_timeout }, { "Data", parse_data_ad }, { "ScanResponseData", parse_data_sr }, { "Discoverable", parse_discoverable }, { "DiscoverableTimeout", parse_discoverable_timeout }, { "SecondaryChannel", parse_secondary }, { "MinInterval", parse_min_interval }, { "MaxInterval", parse_max_interval }, { "TxPower", parse_tx_power }, { }, }; static void properties_changed(GDBusProxy *proxy, const char *name, DBusMessageIter *iter, void *user_data) { struct btd_adv_client *client = user_data; struct adv_parser *parser; for (parser = parsers; parser && parser->name; parser++) { if (strcmp(parser->name, name)) continue; /* Ignore experimental parsers if the experimental flag is not * set. */ if (parser->experimental && !(g_dbus_get_flags() & G_DBUS_FLAG_ENABLE_EXPERIMENTAL)) continue; if (parser->func(iter, client)) { refresh_advertisement(client, NULL); break; } } } static void add_client_complete(struct btd_adv_client *client, uint8_t status) { DBusMessage *reply; if (status) error("Failed to add advertisement: %s (0x%02x)", mgmt_errstr(status), status); /* If the advertising request was not started by a direct call from * the client, but rather by a refresh due to properties update or * our internal timer, there is nothing to reply to. */ if (!client->reg) return; if (status) { reply = btd_error_failed(client->reg, "Failed to register advertisement"); queue_remove(client->manager->clients, client); g_idle_add(client_free_idle_cb, client); } else reply = dbus_message_new_method_return(client->reg); g_dbus_send_message(btd_get_dbus_connection(), reply); dbus_message_unref(client->reg); client->reg = NULL; } static void add_adv_callback(uint8_t status, uint16_t length, const void *param, void *user_data) { struct btd_adv_client *client = user_data; const struct mgmt_rp_add_advertising *rp = param; client->add_adv_id = 0; if (status) goto done; if (!param || length < sizeof(*rp)) { status = MGMT_STATUS_FAILED; goto done; } client->instance = rp->instance; g_dbus_client_set_disconnect_watch(client->client, client_disconnect_cb, client); DBG("Advertisement registered: %s", client->path); g_dbus_emit_property_changed(btd_get_dbus_connection(), adapter_get_path(client->manager->adapter), LE_ADVERTISING_MGR_IFACE, "SupportedInstances"); g_dbus_emit_property_changed(btd_get_dbus_connection(), adapter_get_path(client->manager->adapter), LE_ADVERTISING_MGR_IFACE, "ActiveInstances"); g_dbus_proxy_set_property_watch(client->proxy, properties_changed, client); done: add_client_complete(client, status); } static void add_adv_params_callback(uint8_t status, uint16_t length, const void *param, void *user_data) { struct btd_adv_client *client = user_data; const struct mgmt_rp_add_ext_adv_params *rp = param; struct mgmt_cp_add_ext_adv_data *cp = NULL; uint8_t param_len; uint8_t *adv_data = NULL; size_t adv_data_len; uint8_t *scan_rsp = NULL; size_t scan_rsp_len = 0; uint32_t flags = 0; unsigned int mgmt_ret; dbus_int16_t tx_power; client->add_adv_id = 0; if (status) goto fail; if (!param || length < sizeof(*rp)) { status = MGMT_STATUS_FAILED; goto fail; } DBG("Refreshing advertisement data: %s", client->path); /* Update tx power held by client */ tx_power = rp->tx_power; if (tx_power != ADV_TX_POWER_NO_PREFERENCE) g_dbus_proxy_set_property_basic(client->proxy, "TxPower", DBUS_TYPE_INT16, &tx_power, NULL, NULL, NULL); client->instance = rp->instance; flags = get_adv_flags(client); adv_data = generate_adv_data(client, &flags, &adv_data_len); if (!adv_data || (adv_data_len > rp->max_adv_data_len)) { error("Advertising data too long or couldn't be generated."); goto fail; } if (adv_client_has_scan_response(client, flags)) { scan_rsp = generate_scan_rsp(client, &flags, &scan_rsp_len); if ((!scan_rsp && scan_rsp_len) || scan_rsp_len > rp->max_scan_rsp_len) { error("Scan data couldn't be generated."); goto fail; } } param_len = sizeof(struct mgmt_cp_add_advertising) + adv_data_len + scan_rsp_len; cp = malloc0(param_len); if (!cp) { error("Couldn't allocate for MGMT!"); goto fail; } cp->instance = client->instance; cp->adv_data_len = adv_data_len; cp->scan_rsp_len = scan_rsp_len; memcpy(cp->data, adv_data, adv_data_len); if (scan_rsp) memcpy(cp->data + adv_data_len, scan_rsp, scan_rsp_len); free(adv_data); free(scan_rsp); adv_data = NULL; scan_rsp = NULL; /* Submit request to update instance data */ mgmt_ret = mgmt_send(client->manager->mgmt, MGMT_OP_ADD_EXT_ADV_DATA, client->manager->mgmt_index, param_len, cp, client->refresh_done_func, client, NULL); if (mgmt_ret && client->refresh_done_func) client->add_adv_id = mgmt_ret; /* Clear the callback */ client->refresh_done_func = NULL; if (!mgmt_ret) { error("Failed to add Advertising Data"); goto fail; } free(cp); cp = NULL; return; fail: if (adv_data) free(adv_data); if (scan_rsp) free(scan_rsp); if (cp) free(cp); if (!status) status = -EINVAL; /* Failure for any reason ends this advertising request */ add_client_complete(client, status); } static DBusMessage *parse_advertisement(struct btd_adv_client *client) { struct adv_parser *parser; int err; for (parser = parsers; parser && parser->name; parser++) { DBusMessageIter iter; if (!g_dbus_proxy_get_property(client->proxy, parser->name, &iter)) continue; if (!parser->func(&iter, client)) { error("Error parsing %s property", parser->name); goto fail; } } if (bt_ad_get_flags(client->data) & (BT_AD_FLAG_GENERAL | BT_AD_FLAG_LIMITED)) { /* BLUETOOTH SPECIFICATION Version 5.0 | Vol 3, Part C * page 2042: * A device in the broadcast mode shall not set the * ‘LE General Discoverable Mode’ flag or the * ‘LE Limited Discoverable Mode’ flag in the Flags AD Type * as defined in [Core Specification Supplement], Part A, * Section 1.3. */ if (client->type == AD_TYPE_BROADCAST) { error("Broadcast cannot set flags"); goto fail; } /* Set Limited Discoverable if DiscoverableTimeout is set */ if (client->disc_to_id && !set_flags(client, BT_AD_FLAG_LIMITED)) { error("Failed to set Limited Discoverable Flag"); goto fail; } } else if (client->disc_to_id) { /* Ignore DiscoverableTimeout if not discoverable */ timeout_remove(client->disc_to_id); client->disc_to_id = 0; client->discoverable_to = 0; } if (client->timeout && client->timeout < client->discoverable_to) { /* DiscoverableTimeout must not be bigger than Timeout */ error("DiscoverableTimeout > Timeout"); goto fail; } if (client->min_interval > client->max_interval) { /* Min interval must not be bigger than max interval */ error("MinInterval must be less than MaxInterval (%u > %u)", client->min_interval, client->max_interval); goto fail; } err = refresh_advertisement(client, add_adv_callback); if (!err) return NULL; fail: return btd_error_failed(client->reg, "Failed to parse advertisement."); } static void client_proxy_added(GDBusProxy *proxy, void *data) { struct btd_adv_client *client = data; DBusMessage *reply; const char *interface; interface = g_dbus_proxy_get_interface(proxy); if (g_str_equal(interface, LE_ADVERTISEMENT_IFACE) == FALSE) return; reply = parse_advertisement(client); if (!reply) return; /* Failed to publish for some reason, remove. */ queue_remove(client->manager->clients, client); g_idle_add(client_free_idle_cb, client); g_dbus_send_message(btd_get_dbus_connection(), reply); dbus_message_unref(client->reg); client->reg = NULL; } static struct btd_adv_client *client_create(struct btd_adv_manager *manager, DBusConnection *conn, DBusMessage *msg, const char *path) { struct btd_adv_client *client; const char *sender = dbus_message_get_sender(msg); if (!path || !g_str_has_prefix(path, "/")) return NULL; client = new0(struct btd_adv_client, 1); client->client = g_dbus_client_new_full(conn, sender, path, path); if (!client->client) goto fail; client->owner = g_strdup(sender); if (!client->owner) goto fail; client->path = g_strdup(path); if (!client->path) goto fail; DBG("Adding proxy for %s", path); client->proxy = g_dbus_proxy_new(client->client, path, LE_ADVERTISEMENT_IFACE); if (!client->proxy) goto fail; g_dbus_client_set_proxy_handlers(client->client, client_proxy_added, NULL, NULL, client); client->data = bt_ad_new(); if (!client->data) goto fail; bt_ad_set_max_len(client->data, manager->max_adv_len); client->scan = bt_ad_new(); if (!client->scan) goto fail; bt_ad_set_max_len(client->scan, manager->max_scan_rsp_len); client->manager = manager; client->appearance = UINT16_MAX; client->tx_power = ADV_TX_POWER_NO_PREFERENCE; client->min_interval = 0; client->max_interval = 0; client->refresh_done_func = NULL; return client; fail: client_free(client); return NULL; } static DBusMessage *register_advertisement(DBusConnection *conn, DBusMessage *msg, void *user_data) { struct btd_adv_manager *manager = user_data; DBusMessageIter args; struct btd_adv_client *client; struct dbus_obj_match match; DBG("RegisterAdvertisement"); if (!dbus_message_iter_init(msg, &args)) return btd_error_invalid_args(msg); if (dbus_message_iter_get_arg_type(&args) != DBUS_TYPE_OBJECT_PATH) return btd_error_invalid_args(msg); dbus_message_iter_get_basic(&args, &match.path); match.owner = dbus_message_get_sender(msg); if (queue_find(manager->clients, match_client, &match)) return btd_error_already_exists(msg); dbus_message_iter_next(&args); if (dbus_message_iter_get_arg_type(&args) != DBUS_TYPE_ARRAY) return btd_error_invalid_args(msg); client = client_create(manager, conn, msg, match.path); if (!client) return btd_error_failed(msg, "Failed to register advertisement"); client->instance = util_get_uid(&manager->instance_bitmap, manager->max_ads); if (!client->instance) { client_free(client); return btd_error_not_permitted(msg, "Maximum advertisements reached"); } DBG("Registered advertisement at path %s", match.path); client->reg = dbus_message_ref(msg); queue_push_tail(manager->clients, client); return NULL; } static DBusMessage *unregister_advertisement(DBusConnection *conn, DBusMessage *msg, void *user_data) { struct btd_adv_manager *manager = user_data; DBusMessageIter args; struct btd_adv_client *client; struct dbus_obj_match match; DBG("UnregisterAdvertisement"); if (!dbus_message_iter_init(msg, &args)) return btd_error_invalid_args(msg); if (dbus_message_iter_get_arg_type(&args) != DBUS_TYPE_OBJECT_PATH) return btd_error_invalid_args(msg); dbus_message_iter_get_basic(&args, &match.path); match.owner = dbus_message_get_sender(msg); client = queue_find(manager->clients, match_client, &match); if (!client) return btd_error_does_not_exist(msg); client_remove(client); return dbus_message_new_method_return(msg); } static gboolean get_instances(const GDBusPropertyTable *property, DBusMessageIter *iter, void *data) { struct btd_adv_manager *manager = data; uint8_t instances; instances = manager->max_ads - queue_length(manager->clients); dbus_message_iter_append_basic(iter, DBUS_TYPE_BYTE, &instances); return TRUE; } static gboolean get_active_instances(const GDBusPropertyTable *property, DBusMessageIter *iter, void *data) { struct btd_adv_manager *manager = data; uint8_t instances; instances = queue_length(manager->clients); dbus_message_iter_append_basic(iter, DBUS_TYPE_BYTE, &instances); return TRUE; } static void append_include(struct btd_adv_manager *manager, DBusMessageIter *iter) { struct adv_include *inc; for (inc = includes; inc && inc->name; inc++) { if ((inc->set && inc->set(NULL)) || (manager->supported_flags & inc->flag)) dbus_message_iter_append_basic(iter, DBUS_TYPE_STRING, &inc->name); } } static gboolean get_supported_includes(const GDBusPropertyTable *property, DBusMessageIter *iter, void *data) { struct btd_adv_manager *manager = data; DBusMessageIter entry; dbus_message_iter_open_container(iter, DBUS_TYPE_ARRAY, DBUS_TYPE_STRING_AS_STRING, &entry); append_include(manager, &entry); dbus_message_iter_close_container(iter, &entry); return TRUE; } static void append_secondary(struct btd_adv_manager *manager, DBusMessageIter *iter) { struct adv_secondary *sec; for (sec = secondary; sec && sec->name; sec++) { if (manager->supported_flags & sec->flag) dbus_message_iter_append_basic(iter, DBUS_TYPE_STRING, &sec->name); } } static gboolean secondary_exists(const GDBusPropertyTable *property, void *data) { struct btd_adv_manager *manager = data; /* 1M PHY shall always be supported if ext_adv is supported */ return manager->supported_flags & MGMT_ADV_FLAG_SEC_1M; } static gboolean get_supported_secondary(const GDBusPropertyTable *property, DBusMessageIter *iter, void *data) { struct btd_adv_manager *manager = data; DBusMessageIter entry; dbus_message_iter_open_container(iter, DBUS_TYPE_ARRAY, DBUS_TYPE_STRING_AS_STRING, &entry); append_secondary(manager, &entry); dbus_message_iter_close_container(iter, &entry); return TRUE; } static struct adv_feature { int flag; const char *name; } features[] = { { MGMT_ADV_FLAG_CAN_SET_TX_POWER, "CanSetTxPower" }, { MGMT_ADV_FLAG_HW_OFFLOAD, "HardwareOffload" }, { }, }; static void append_features(struct btd_adv_manager *manager, DBusMessageIter *iter) { struct adv_feature *feat; for (feat = features; feat->name; feat++) { if (manager->supported_flags & feat->flag) dbus_message_iter_append_basic(iter, DBUS_TYPE_STRING, &feat->name); } } static gboolean get_supported_features(const GDBusPropertyTable *property, DBusMessageIter *iter, void *data) { struct btd_adv_manager *manager = data; DBusMessageIter entry; dbus_message_iter_open_container(iter, DBUS_TYPE_ARRAY, DBUS_TYPE_STRING_AS_STRING, &entry); append_features(manager, &entry); dbus_message_iter_close_container(iter, &entry); return TRUE; } static gboolean get_supported_cap(const GDBusPropertyTable *property, DBusMessageIter *iter, void *data) { struct btd_adv_manager *manager = data; DBusMessageIter dict; int16_t min_tx_power = manager->min_tx_power; int16_t max_tx_power = manager->max_tx_power; dbus_message_iter_open_container(iter, DBUS_TYPE_ARRAY, DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING DBUS_TYPE_STRING_AS_STRING DBUS_TYPE_VARIANT_AS_STRING DBUS_DICT_ENTRY_END_CHAR_AS_STRING, &dict); if (min_tx_power != ADV_TX_POWER_NO_PREFERENCE) dict_append_entry(&dict, "MinTxPower", DBUS_TYPE_INT16, &min_tx_power); if (max_tx_power != ADV_TX_POWER_NO_PREFERENCE) dict_append_entry(&dict, "MaxTxPower", DBUS_TYPE_INT16, &max_tx_power); dict_append_entry(&dict, "MaxAdvLen", DBUS_TYPE_BYTE, &manager->max_adv_len); dict_append_entry(&dict, "MaxScnRspLen", DBUS_TYPE_BYTE, &manager->max_scan_rsp_len); dbus_message_iter_close_container(iter, &dict); return TRUE; } static const GDBusPropertyTable properties[] = { { "ActiveInstances", "y", get_active_instances, NULL, NULL }, { "SupportedInstances", "y", get_instances, NULL, NULL }, { "SupportedIncludes", "as", get_supported_includes, NULL, NULL }, { "SupportedSecondaryChannels", "as", get_supported_secondary, NULL, secondary_exists }, { "SupportedFeatures", "as", get_supported_features, NULL, NULL }, { "SupportedCapabilities", "a{sv}", get_supported_cap, NULL, NULL }, { } }; static const GDBusMethodTable methods[] = { { GDBUS_ASYNC_METHOD("RegisterAdvertisement", GDBUS_ARGS({ "advertisement", "o" }, { "options", "a{sv}" }), NULL, register_advertisement) }, { GDBUS_ASYNC_METHOD("UnregisterAdvertisement", GDBUS_ARGS({ "service", "o" }), NULL, unregister_advertisement) }, { } }; static void manager_destroy(void *user_data) { struct btd_adv_manager *manager = user_data; queue_destroy(manager->clients, client_destroy); mgmt_unref(manager->mgmt); free(manager); } static void read_adv_features_callback(uint8_t status, uint16_t length, const void *param, void *user_data) { struct btd_adv_manager *manager = user_data; const struct mgmt_rp_read_adv_features *feat = param; if (status || !param) { error("Failed to read advertising features: %s (0x%02x)", mgmt_errstr(status), status); return; } if (length < sizeof(*feat)) { error("Wrong size of read adv features response"); return; } manager->max_adv_len = feat->max_adv_data_len; manager->max_scan_rsp_len = feat->max_scan_rsp_len; manager->max_ads = feat->max_instances; manager->supported_flags |= feat->supported_flags; /* Registering interface after querying properties */ if (!g_dbus_register_interface(btd_get_dbus_connection(), adapter_get_path(manager->adapter), LE_ADVERTISING_MGR_IFACE, methods, NULL, properties, manager, NULL)) error("Failed to register " LE_ADVERTISING_MGR_IFACE); if (manager->max_ads == 0) return; /* Reset existing instances */ if (feat->num_instances) remove_advertising(manager, 0); /* Emit property update */ g_dbus_emit_property_changed(btd_get_dbus_connection(), adapter_get_path(manager->adapter), LE_ADVERTISING_MGR_IFACE, "SupportedFeatures"); g_dbus_emit_property_changed(btd_get_dbus_connection(), adapter_get_path(manager->adapter), LE_ADVERTISING_MGR_IFACE, "SupportedIncludes"); g_dbus_emit_property_changed(btd_get_dbus_connection(), adapter_get_path(manager->adapter), LE_ADVERTISING_MGR_IFACE, "SupportedSecondaryChannels"); } static void read_controller_cap_complete(uint8_t status, uint16_t length, const void *param, void *user_data) { struct btd_adv_manager *manager = user_data; const struct mgmt_rp_read_controller_cap *rp = param; const uint8_t *ptr = rp->cap; size_t offset = 0; uint8_t tag_len; uint8_t tag_type; if (status || !param) { error("Failed to read advertising features: %s (0x%02x)", mgmt_errstr(status), status); return; } if (sizeof(rp->cap_len) + rp->cap_len != length) { error("Controller capabilities malformed, size %zu != %u", sizeof(rp->cap_len) + rp->cap_len, length); return; } while (offset < rp->cap_len) { tag_len = ptr[offset++]; tag_type = ptr[offset++]; switch (tag_type) { case MGMT_CAP_LE_TX_PWR: if ((tag_len - sizeof(tag_type)) != 2*sizeof(manager->min_tx_power)) { error("TX power had unexpected length %d", tag_len); break; } memcpy(&manager->min_tx_power, &ptr[offset], tag_len); memcpy(&manager->max_tx_power, &ptr[offset+1], tag_len); } /* Step to the next entry */ offset += (tag_len - sizeof(tag_type)); } } static struct btd_adv_manager *manager_create(struct btd_adapter *adapter, struct mgmt *mgmt) { struct btd_adv_manager *manager; manager = new0(struct btd_adv_manager, 1); manager->adapter = adapter; manager->mgmt = mgmt_ref(mgmt); if (!manager->mgmt) { error("Failed to access management interface"); free(manager); return NULL; } manager->mgmt_index = btd_adapter_get_index(adapter); manager->clients = queue_new(); manager->supported_flags = MGMT_ADV_FLAG_LOCAL_NAME; manager->extended_add_cmds = btd_has_kernel_features(KERNEL_HAS_EXT_ADV_ADD_CMDS); manager->min_tx_power = ADV_TX_POWER_NO_PREFERENCE; manager->max_tx_power = ADV_TX_POWER_NO_PREFERENCE; if (!mgmt_send(manager->mgmt, MGMT_OP_READ_ADV_FEATURES, manager->mgmt_index, 0, NULL, read_adv_features_callback, manager, NULL)) { error("Failed to read advertising features"); goto fail; } /* Query controller capabilities. This will be used to display valid * advertising tx power range to the client. */ if (btd_has_kernel_features(KERNEL_HAS_CONTROLLER_CAP_CMD)) mgmt_send(manager->mgmt, MGMT_OP_READ_CONTROLLER_CAP, manager->mgmt_index, 0, NULL, read_controller_cap_complete, manager, NULL); return manager; fail: manager_destroy(manager); return NULL; } struct btd_adv_manager *btd_adv_manager_new(struct btd_adapter *adapter, struct mgmt *mgmt) { struct btd_adv_manager *manager; if (!adapter || !mgmt) return NULL; manager = manager_create(adapter, mgmt); if (!manager) return NULL; DBG("LE Advertising Manager created for adapter: %s", adapter_get_path(adapter)); return manager; } void btd_adv_manager_destroy(struct btd_adv_manager *manager) { if (!manager) return; g_dbus_unregister_interface(btd_get_dbus_connection(), adapter_get_path(manager->adapter), LE_ADVERTISING_MGR_IFACE); manager_destroy(manager); } static void manager_refresh(void *data, void *user_data) { struct btd_adv_client *client = data; refresh_advertisement(client, NULL); } void btd_adv_manager_refresh(struct btd_adv_manager *manager) { if (!manager) return; queue_foreach(manager->clients, manager_refresh, NULL); } bluez-5.82/PaxHeaders/profiles0000644000000000000000000000005014773213553013426 xustar0020 atime=1743591291 20 ctime=1743591275 bluez-5.82/profiles/0000755000000000000000000000000014773213553013164 5ustar00rootrootbluez-5.82/profiles/PaxHeaders/cups0000644000000000000000000000005014773213563014401 xustar0020 atime=1743591291 20 ctime=1743591283 bluez-5.82/profiles/cups/0000755000000000000000000000000014773213563014137 5ustar00rootrootbluez-5.82/profiles/cups/PaxHeaders/cups.h0000644000000000000000000000005014015011623015557 xustar0020 atime=1743516204 20 ctime=1743591283 bluez-5.82/profiles/cups/cups.h0000644000000000000000000000204114015011623015235 0ustar00rootroot/* SPDX-License-Identifier: GPL-2.0-or-later */ /* * * BlueZ - Bluetooth protocol stack for Linux * * Copyright (C) 2003-2010 Marcel Holtmann * * */ enum { /**** Backend exit codes ****/ CUPS_BACKEND_OK = 0, /* Job completed successfully */ CUPS_BACKEND_FAILED = 1, /* Job failed, use error-policy */ CUPS_BACKEND_AUTH_REQUIRED = 2, /* Job failed, authentication required */ CUPS_BACKEND_HOLD = 3, /* Job failed, hold job */ CUPS_BACKEND_STOP = 4, /* Job failed, stop queue */ CUPS_BACKEND_CANCEL = 5, /* Job failed, cancel job */ CUPS_BACKEND_RETRY = 6, /* Failure requires us to retry (BlueZ specific) */ }; int sdp_search_spp(sdp_session_t *sdp, uint8_t *channel); int sdp_search_hcrp(sdp_session_t *sdp, unsigned short *ctrl_psm, unsigned short *data_psm); int spp_print(bdaddr_t *src, bdaddr_t *dst, uint8_t channel, int fd, int copies, const char *cups_class); int hcrp_print(bdaddr_t *src, bdaddr_t *dst, unsigned short ctrl_psm, unsigned short data_psm, int fd, int copies, const char *cups_class); bluez-5.82/profiles/cups/PaxHeaders/spp.c0000644000000000000000000000005014015011623015402 xustar0020 atime=1743516206 20 ctime=1743591283 bluez-5.82/profiles/cups/spp.c0000644000000000000000000000420514015011623015064 0ustar00rootroot// SPDX-License-Identifier: GPL-2.0-or-later /* * * BlueZ - Bluetooth protocol stack for Linux * * Copyright (C) 2003-2010 Marcel Holtmann * * */ #ifdef HAVE_CONFIG_H #include #endif #include #include #include #include #include #include "lib/bluetooth.h" #include "lib/rfcomm.h" #include "lib/sdp.h" #include "lib/sdp_lib.h" #include "cups.h" int spp_print(bdaddr_t *src, bdaddr_t *dst, uint8_t channel, int fd, int copies, const char *cups_class) { struct sockaddr_rc addr; unsigned char buf[2048]; int i, sk, err, len; if ((sk = socket(PF_BLUETOOTH, SOCK_STREAM, BTPROTO_RFCOMM)) < 0) { perror("ERROR: Can't create socket"); if (cups_class) return CUPS_BACKEND_FAILED; else return CUPS_BACKEND_RETRY; } addr.rc_family = AF_BLUETOOTH; bacpy(&addr.rc_bdaddr, src); addr.rc_channel = 0; if (bind(sk, (struct sockaddr *) &addr, sizeof(addr)) < 0) { perror("ERROR: Can't bind socket"); close(sk); if (cups_class) return CUPS_BACKEND_FAILED; else return CUPS_BACKEND_RETRY; } addr.rc_family = AF_BLUETOOTH; bacpy(&addr.rc_bdaddr, dst); addr.rc_channel = channel; if (connect(sk, (struct sockaddr *) &addr, sizeof(addr)) < 0) { perror("ERROR: Can't connect to device"); close(sk); if (cups_class) return CUPS_BACKEND_FAILED; else return CUPS_BACKEND_RETRY; } fputs("STATE: -connecting-to-device\n", stderr); /* Ignore SIGTERM signals if printing from stdin */ if (fd == 0) { #ifdef HAVE_SIGSET sigset(SIGTERM, SIG_IGN); #elif defined(HAVE_SIGACTION) memset(&action, 0, sizeof(action)); sigemptyset(&action.sa_mask); action.sa_handler = SIG_IGN; sigaction(SIGTERM, &action, NULL); #else signal(SIGTERM, SIG_IGN); #endif /* HAVE_SIGSET */ } for (i = 0; i < copies; i++) { if (fd != 0) { fprintf(stderr, "PAGE: 1 1\n"); lseek(fd, 0, SEEK_SET); } while ((len = read(fd, buf, sizeof(buf))) > 0) { err = write(sk, buf, len); if (err < 0) { perror("ERROR: Error writing to device"); close(sk); return CUPS_BACKEND_FAILED; } } } close(sk); return CUPS_BACKEND_OK; } bluez-5.82/profiles/cups/PaxHeaders/hcrp.c0000644000000000000000000000005014015011623015534 xustar0020 atime=1743516207 20 ctime=1743591283 bluez-5.82/profiles/cups/hcrp.c0000644000000000000000000001714614015011623015226 0ustar00rootroot// SPDX-License-Identifier: GPL-2.0-or-later /* * * BlueZ - Bluetooth protocol stack for Linux * * Copyright (C) 2003-2010 Marcel Holtmann * * */ #ifdef HAVE_CONFIG_H #include #endif #include #include #include #include #include #include #include #include "lib/bluetooth.h" #include "lib/l2cap.h" #include "lib/sdp.h" #include "lib/sdp_lib.h" #include "cups.h" #define HCRP_PDU_CREDIT_GRANT 0x0001 #define HCRP_PDU_CREDIT_REQUEST 0x0002 #define HCRP_PDU_GET_LPT_STATUS 0x0005 #define HCRP_STATUS_FEATURE_UNSUPPORTED 0x0000 #define HCRP_STATUS_SUCCESS 0x0001 #define HCRP_STATUS_CREDIT_SYNC_ERROR 0x0002 #define HCRP_STATUS_GENERIC_FAILURE 0xffff struct hcrp_pdu_hdr { uint16_t pid; uint16_t tid; uint16_t plen; } __attribute__ ((packed)); #define HCRP_PDU_HDR_SIZE 6 struct hcrp_credit_grant_cp { uint32_t credit; } __attribute__ ((packed)); #define HCRP_CREDIT_GRANT_CP_SIZE 4 struct hcrp_credit_grant_rp { uint16_t status; } __attribute__ ((packed)); #define HCRP_CREDIT_GRANT_RP_SIZE 2 struct hcrp_credit_request_rp { uint16_t status; uint32_t credit; } __attribute__ ((packed)); #define HCRP_CREDIT_REQUEST_RP_SIZE 6 struct hcrp_get_lpt_status_rp { uint16_t status; uint8_t lpt_status; } __attribute__ ((packed)); #define HCRP_GET_LPT_STATUS_RP_SIZE 3 static int hcrp_credit_grant(int sk, uint16_t tid, uint32_t credit) { struct hcrp_pdu_hdr hdr; struct hcrp_credit_grant_cp cp; struct hcrp_credit_grant_rp rp; unsigned char buf[128]; int len; hdr.pid = htons(HCRP_PDU_CREDIT_GRANT); hdr.tid = htons(tid); hdr.plen = htons(HCRP_CREDIT_GRANT_CP_SIZE); cp.credit = credit; memcpy(buf, &hdr, HCRP_PDU_HDR_SIZE); memcpy(buf + HCRP_PDU_HDR_SIZE, &cp, HCRP_CREDIT_GRANT_CP_SIZE); len = write(sk, buf, HCRP_PDU_HDR_SIZE + HCRP_CREDIT_GRANT_CP_SIZE); if (len < 0) return len; len = read(sk, buf, sizeof(buf)); if (len < 0) return len; memcpy(&hdr, buf, HCRP_PDU_HDR_SIZE); memcpy(&rp, buf + HCRP_PDU_HDR_SIZE, HCRP_CREDIT_GRANT_RP_SIZE); if (ntohs(rp.status) != HCRP_STATUS_SUCCESS) { errno = EIO; return -1; } return 0; } static int hcrp_credit_request(int sk, uint16_t tid, uint32_t *credit) { struct hcrp_pdu_hdr hdr; struct hcrp_credit_request_rp rp; unsigned char buf[128]; int len; hdr.pid = htons(HCRP_PDU_CREDIT_REQUEST); hdr.tid = htons(tid); hdr.plen = htons(0); memcpy(buf, &hdr, HCRP_PDU_HDR_SIZE); len = write(sk, buf, HCRP_PDU_HDR_SIZE); if (len < 0) return len; len = read(sk, buf, sizeof(buf)); if (len < 0) return len; memcpy(&hdr, buf, HCRP_PDU_HDR_SIZE); memcpy(&rp, buf + HCRP_PDU_HDR_SIZE, HCRP_CREDIT_REQUEST_RP_SIZE); if (ntohs(rp.status) != HCRP_STATUS_SUCCESS) { errno = EIO; return -1; } if (credit) *credit = ntohl(rp.credit); return 0; } static int hcrp_get_lpt_status(int sk, uint16_t tid, uint8_t *lpt_status) { struct hcrp_pdu_hdr hdr; struct hcrp_get_lpt_status_rp rp; unsigned char buf[128]; int len; hdr.pid = htons(HCRP_PDU_GET_LPT_STATUS); hdr.tid = htons(tid); hdr.plen = htons(0); memcpy(buf, &hdr, HCRP_PDU_HDR_SIZE); len = write(sk, buf, HCRP_PDU_HDR_SIZE); if (len < 0) return len; len = read(sk, buf, sizeof(buf)); if (len < 0) return len; memcpy(&hdr, buf, HCRP_PDU_HDR_SIZE); memcpy(&rp, buf + HCRP_PDU_HDR_SIZE, HCRP_GET_LPT_STATUS_RP_SIZE); if (ntohs(rp.status) != HCRP_STATUS_SUCCESS) { errno = EIO; return -1; } if (lpt_status) *lpt_status = rp.lpt_status; return 0; } static inline int hcrp_get_next_tid(int tid) { if (tid > 0xf000) return 0; else return tid + 1; } int hcrp_print(bdaddr_t *src, bdaddr_t *dst, unsigned short ctrl_psm, unsigned short data_psm, int fd, int copies, const char *cups_class) { struct sockaddr_l2 addr; struct l2cap_options opts; socklen_t size; unsigned char buf[2048]; int i, ctrl_sk, data_sk, count, len, timeout = 0; unsigned int mtu; uint8_t status; uint16_t tid = 0; uint32_t tmp, credit = 0; if ((ctrl_sk = socket(PF_BLUETOOTH, SOCK_SEQPACKET, BTPROTO_L2CAP)) < 0) { perror("ERROR: Can't create socket"); if (cups_class) return CUPS_BACKEND_FAILED; else return CUPS_BACKEND_RETRY; } memset(&addr, 0, sizeof(addr)); addr.l2_family = AF_BLUETOOTH; bacpy(&addr.l2_bdaddr, src); if (bind(ctrl_sk, (struct sockaddr *) &addr, sizeof(addr)) < 0) { perror("ERROR: Can't bind socket"); close(ctrl_sk); if (cups_class) return CUPS_BACKEND_FAILED; else return CUPS_BACKEND_RETRY; } memset(&addr, 0, sizeof(addr)); addr.l2_family = AF_BLUETOOTH; bacpy(&addr.l2_bdaddr, dst); addr.l2_psm = htobs(ctrl_psm); if (connect(ctrl_sk, (struct sockaddr *) &addr, sizeof(addr)) < 0) { perror("ERROR: Can't connect to device"); close(ctrl_sk); if (cups_class) return CUPS_BACKEND_FAILED; else return CUPS_BACKEND_RETRY; } if ((data_sk = socket(PF_BLUETOOTH, SOCK_SEQPACKET, BTPROTO_L2CAP)) < 0) { perror("ERROR: Can't create socket"); close(ctrl_sk); if (cups_class) return CUPS_BACKEND_FAILED; else return CUPS_BACKEND_RETRY; } memset(&addr, 0, sizeof(addr)); addr.l2_family = AF_BLUETOOTH; bacpy(&addr.l2_bdaddr, src); if (bind(data_sk, (struct sockaddr *) &addr, sizeof(addr)) < 0) { perror("ERROR: Can't bind socket"); close(data_sk); close(ctrl_sk); if (cups_class) return CUPS_BACKEND_FAILED; else return CUPS_BACKEND_RETRY; } memset(&addr, 0, sizeof(addr)); addr.l2_family = AF_BLUETOOTH; bacpy(&addr.l2_bdaddr, dst); addr.l2_psm = htobs(data_psm); if (connect(data_sk, (struct sockaddr *) &addr, sizeof(addr)) < 0) { perror("ERROR: Can't connect to device"); close(data_sk); close(ctrl_sk); if (cups_class) return CUPS_BACKEND_FAILED; else return CUPS_BACKEND_RETRY; } fputs("STATE: -connecting-to-device\n", stderr); memset(&opts, 0, sizeof(opts)); size = sizeof(opts); if (getsockopt(data_sk, SOL_L2CAP, L2CAP_OPTIONS, &opts, &size) < 0) { perror("ERROR: Can't get socket options"); close(data_sk); close(ctrl_sk); if (cups_class) return CUPS_BACKEND_FAILED; else return CUPS_BACKEND_RETRY; } mtu = opts.omtu; /* Ignore SIGTERM signals if printing from stdin */ if (fd == 0) { #ifdef HAVE_SIGSET sigset(SIGTERM, SIG_IGN); #elif defined(HAVE_SIGACTION) memset(&action, 0, sizeof(action)); sigemptyset(&action.sa_mask); action.sa_handler = SIG_IGN; sigaction(SIGTERM, &action, NULL); #else signal(SIGTERM, SIG_IGN); #endif /* HAVE_SIGSET */ } tid = hcrp_get_next_tid(tid); if (hcrp_credit_grant(ctrl_sk, tid, 0) < 0) { fprintf(stderr, "ERROR: Can't grant initial credits\n"); close(data_sk); close(ctrl_sk); if (cups_class) return CUPS_BACKEND_FAILED; else return CUPS_BACKEND_RETRY; } for (i = 0; i < copies; i++) { if (fd != 0) { fprintf(stderr, "PAGE: 1 1\n"); lseek(fd, 0, SEEK_SET); } while (1) { if (credit < mtu) { tid = hcrp_get_next_tid(tid); if (!hcrp_credit_request(ctrl_sk, tid, &tmp)) { credit += tmp; timeout = 0; } } if (!credit) { if (timeout++ > 300) { tid = hcrp_get_next_tid(tid); if (!hcrp_get_lpt_status(ctrl_sk, tid, &status)) fprintf(stderr, "ERROR: LPT status 0x%02x\n", status); break; } sleep(1); continue; } count = read(fd, buf, (credit > mtu) ? mtu : credit); if (count <= 0) break; len = write(data_sk, buf, count); if (len < 0) { perror("ERROR: Error writing to device"); close(data_sk); close(ctrl_sk); return CUPS_BACKEND_FAILED; } if (len != count) fprintf(stderr, "ERROR: Can't send complete data\n"); credit -= len; } } close(data_sk); close(ctrl_sk); return CUPS_BACKEND_OK; } bluez-5.82/profiles/cups/PaxHeaders/sdp.c0000644000000000000000000000005014015011623015366 xustar0020 atime=1743516206 20 ctime=1743591283 bluez-5.82/profiles/cups/sdp.c0000644000000000000000000000421414015011623015050 0ustar00rootroot// SPDX-License-Identifier: GPL-2.0-or-later /* * * BlueZ - Bluetooth protocol stack for Linux * * Copyright (C) 2003-2010 Marcel Holtmann * * */ #ifdef HAVE_CONFIG_H #include #endif #include #include #include #include #include #include "lib/bluetooth.h" #include "lib/sdp.h" #include "lib/sdp_lib.h" #include "cups.h" int sdp_search_hcrp(sdp_session_t *sdp, unsigned short *ctrl_psm, unsigned short *data_psm) { sdp_list_t *srch, *attrs, *rsp; uuid_t svclass; uint16_t attr1, attr2; int err; if (!sdp) return -1; sdp_uuid16_create(&svclass, HCR_PRINT_SVCLASS_ID); srch = sdp_list_append(NULL, &svclass); attr1 = SDP_ATTR_PROTO_DESC_LIST; attrs = sdp_list_append(NULL, &attr1); attr2 = SDP_ATTR_ADD_PROTO_DESC_LIST; attrs = sdp_list_append(attrs, &attr2); err = sdp_service_search_attr_req(sdp, srch, SDP_ATTR_REQ_INDIVIDUAL, attrs, &rsp); if (err) return -1; for (; rsp; rsp = rsp->next) { sdp_record_t *rec = (sdp_record_t *) rsp->data; sdp_list_t *protos; if (!sdp_get_access_protos(rec, &protos)) { unsigned short psm = sdp_get_proto_port(protos, L2CAP_UUID); if (psm > 0) { *ctrl_psm = psm; } } if (!sdp_get_add_access_protos(rec, &protos)) { unsigned short psm = sdp_get_proto_port(protos, L2CAP_UUID); if (psm > 0 && *ctrl_psm > 0) { *data_psm = psm; return 0; } } } return -1; } int sdp_search_spp(sdp_session_t *sdp, uint8_t *channel) { sdp_list_t *srch, *attrs, *rsp; uuid_t svclass; uint16_t attr; int err; if (!sdp) return -1; sdp_uuid16_create(&svclass, SERIAL_PORT_SVCLASS_ID); srch = sdp_list_append(NULL, &svclass); attr = SDP_ATTR_PROTO_DESC_LIST; attrs = sdp_list_append(NULL, &attr); err = sdp_service_search_attr_req(sdp, srch, SDP_ATTR_REQ_INDIVIDUAL, attrs, &rsp); if (err) return -1; for (; rsp; rsp = rsp->next) { sdp_record_t *rec = (sdp_record_t *) rsp->data; sdp_list_t *protos; if (!sdp_get_access_protos(rec, &protos)) { uint8_t ch = sdp_get_proto_port(protos, RFCOMM_UUID); if (ch > 0) { *channel = ch; return 0; } } } return -1; } bluez-5.82/profiles/cups/PaxHeaders/main.c0000644000000000000000000000005014015011623015524 xustar0020 atime=1743516204 20 ctime=1743591283 bluez-5.82/profiles/cups/main.c0000644000000000000000000004716714015011623015224 0ustar00rootroot// SPDX-License-Identifier: GPL-2.0-or-later /* * * BlueZ - Bluetooth protocol stack for Linux * * Copyright (C) 2003-2010 Marcel Holtmann * * */ #ifdef HAVE_CONFIG_H #include #endif #define _GNU_SOURCE #include #include #include #include #include #include #include #include #include #include #include "lib/bluetooth.h" #include "lib/sdp.h" #include "lib/sdp_lib.h" #include "gdbus/gdbus.h" #include "cups.h" struct cups_device { char *bdaddr; char *name; char *id; }; static GSList *device_list = NULL; static GMainLoop *loop = NULL; static DBusConnection *conn = NULL; static gboolean doing_disco = FALSE; #define ATTRID_1284ID 0x0300 struct context_data { gboolean found; char *id; }; static void element_start(GMarkupParseContext *context, const char *element_name, const char **attribute_names, const char **attribute_values, gpointer user_data, GError **err) { struct context_data *ctx_data = user_data; if (!strcmp(element_name, "record")) return; if (!strcmp(element_name, "attribute")) { int i; for (i = 0; attribute_names[i]; i++) { if (strcmp(attribute_names[i], "id") != 0) continue; if (strtol(attribute_values[i], 0, 0) == ATTRID_1284ID) ctx_data->found = TRUE; break; } return; } if (ctx_data->found && !strcmp(element_name, "text")) { int i; for (i = 0; attribute_names[i]; i++) { if (!strcmp(attribute_names[i], "value")) { ctx_data->id = g_strdup(attribute_values[i] + 2); ctx_data->found = FALSE; } } } } static GMarkupParser parser = { element_start, NULL, NULL, NULL, NULL }; static char *sdp_xml_parse_record(const char *data) { GMarkupParseContext *ctx; struct context_data ctx_data; int size; size = strlen(data); ctx_data.found = FALSE; ctx_data.id = NULL; ctx = g_markup_parse_context_new(&parser, 0, &ctx_data, NULL); if (g_markup_parse_context_parse(ctx, data, size, NULL) == FALSE) { g_markup_parse_context_free(ctx); g_free(ctx_data.id); return NULL; } g_markup_parse_context_free(ctx); return ctx_data.id; } static char *device_get_ieee1284_id(const char *adapter, const char *device) { DBusMessage *message, *reply; DBusMessageIter iter, reply_iter; DBusMessageIter reply_iter_entry; const char *hcr_print = "00001126-0000-1000-8000-00805f9b34fb"; const char *xml; char *id = NULL; /* Look for the service handle of the HCRP service */ message = dbus_message_new_method_call("org.bluez", device, "org.bluez.Device1", "DiscoverServices"); dbus_message_iter_init_append(message, &iter); dbus_message_iter_append_basic(&iter, DBUS_TYPE_STRING, &hcr_print); reply = dbus_connection_send_with_reply_and_block(conn, message, -1, NULL); dbus_message_unref(message); if (!reply) return NULL; dbus_message_iter_init(reply, &reply_iter); if (dbus_message_iter_get_arg_type(&reply_iter) != DBUS_TYPE_ARRAY) { dbus_message_unref(reply); return NULL; } dbus_message_iter_recurse(&reply_iter, &reply_iter_entry); /* Hopefully we only get one handle, or take a punt */ while (dbus_message_iter_get_arg_type(&reply_iter_entry) == DBUS_TYPE_DICT_ENTRY) { guint32 key; DBusMessageIter dict_entry; dbus_message_iter_recurse(&reply_iter_entry, &dict_entry); /* Key ? */ dbus_message_iter_get_basic(&dict_entry, &key); if (!key) { dbus_message_iter_next(&reply_iter_entry); continue; } /* Try to get the value */ if (!dbus_message_iter_next(&dict_entry)) { dbus_message_iter_next(&reply_iter_entry); continue; } dbus_message_iter_get_basic(&dict_entry, &xml); id = sdp_xml_parse_record(xml); if (id != NULL) break; dbus_message_iter_next(&reply_iter_entry); } dbus_message_unref(reply); return id; } static void print_printer_details(const char *name, const char *bdaddr, const char *id) { char *uri, *escaped; escaped = g_strdelimit(g_strdup(name), "\"", '\''); uri = g_strdup_printf("bluetooth://%c%c%c%c%c%c%c%c%c%c%c%c", bdaddr[0], bdaddr[1], bdaddr[3], bdaddr[4], bdaddr[6], bdaddr[7], bdaddr[9], bdaddr[10], bdaddr[12], bdaddr[13], bdaddr[15], bdaddr[16]); printf("direct %s \"%s\" \"%s (Bluetooth)\"", uri, escaped, escaped); if (id != NULL) printf(" \"%s\"\n", id); else printf("\n"); g_free(escaped); g_free(uri); } static void add_device_to_list(const char *name, const char *bdaddr, const char *id) { struct cups_device *device; GSList *l; /* Look for the device in the list */ for (l = device_list; l != NULL; l = l->next) { device = (struct cups_device *) l->data; if (strcmp(device->bdaddr, bdaddr) == 0) { if (device->name != name) { g_free(device->name); device->name = g_strdup(name); } g_free(device->id); device->id = g_strdup(id); return; } } /* Or add it to the list if it's not there */ device = g_new0(struct cups_device, 1); device->bdaddr = g_strdup(bdaddr); device->name = g_strdup(name); device->id = g_strdup(id); device_list = g_slist_prepend(device_list, device); print_printer_details(device->name, device->bdaddr, device->id); } static gboolean parse_device_properties(DBusMessageIter *reply_iter, char **name, char **bdaddr) { guint32 class = 0; DBusMessageIter reply_iter_entry; if (dbus_message_iter_get_arg_type(reply_iter) != DBUS_TYPE_ARRAY) return FALSE; dbus_message_iter_recurse(reply_iter, &reply_iter_entry); while (dbus_message_iter_get_arg_type(&reply_iter_entry) == DBUS_TYPE_DICT_ENTRY) { const char *key; DBusMessageIter dict_entry, iter_dict_val; dbus_message_iter_recurse(&reply_iter_entry, &dict_entry); /* Key == Class ? */ dbus_message_iter_get_basic(&dict_entry, &key); if (!key) { dbus_message_iter_next(&reply_iter_entry); continue; } if (strcmp(key, "Class") != 0 && strcmp(key, "Alias") != 0 && strcmp(key, "Address") != 0) { dbus_message_iter_next(&reply_iter_entry); continue; } /* Try to get the value */ if (!dbus_message_iter_next(&dict_entry)) { dbus_message_iter_next(&reply_iter_entry); continue; } dbus_message_iter_recurse(&dict_entry, &iter_dict_val); if (strcmp(key, "Class") == 0) { dbus_message_iter_get_basic(&iter_dict_val, &class); } else { const char *value; dbus_message_iter_get_basic(&iter_dict_val, &value); if (strcmp(key, "Alias") == 0) { *name = g_strdup(value); } else if (bdaddr) { *bdaddr = g_strdup(value); } } dbus_message_iter_next(&reply_iter_entry); } if (class == 0) return FALSE; if (((class & 0x1f00) >> 8) == 0x06 && (class & 0x80)) return TRUE; return FALSE; } static gboolean device_is_printer(const char *adapter, const char *device_path, char **name, char **bdaddr) { DBusMessage *message, *reply; DBusMessageIter reply_iter; gboolean retval; message = dbus_message_new_method_call("org.bluez", device_path, "org.bluez.Device1", "GetProperties"); reply = dbus_connection_send_with_reply_and_block(conn, message, -1, NULL); dbus_message_unref(message); if (!reply) return FALSE; dbus_message_iter_init(reply, &reply_iter); retval = parse_device_properties(&reply_iter, name, bdaddr); dbus_message_unref(reply); return retval; } static void remote_device_found(const char *adapter, const char *bdaddr, const char *name) { DBusMessage *message, *reply; DBusMessageIter iter; char *object_path = NULL; char *id; assert(adapter != NULL); message = dbus_message_new_method_call("org.bluez", adapter, "org.bluez.Adapter1", "FindDevice"); dbus_message_iter_init_append(message, &iter); dbus_message_iter_append_basic(&iter, DBUS_TYPE_STRING, &bdaddr); reply = dbus_connection_send_with_reply_and_block(conn, message, -1, NULL); dbus_message_unref(message); if (!reply) { message = dbus_message_new_method_call("org.bluez", adapter, "org.bluez.Adapter1", "CreateDevice"); dbus_message_iter_init_append(message, &iter); dbus_message_iter_append_basic(&iter, DBUS_TYPE_STRING, &bdaddr); reply = dbus_connection_send_with_reply_and_block(conn, message, -1, NULL); dbus_message_unref(message); if (!reply) return; } if (dbus_message_get_args(reply, NULL, DBUS_TYPE_OBJECT_PATH, &object_path, DBUS_TYPE_INVALID) == FALSE) { dbus_message_unref(reply); return; } id = device_get_ieee1284_id(adapter, object_path); add_device_to_list(name, bdaddr, id); g_free(id); dbus_message_unref(reply); } static void discovery_completed(void) { g_slist_free(device_list); device_list = NULL; g_main_loop_quit(loop); } static void remote_device_disappeared(const char *bdaddr) { GSList *l; for (l = device_list; l != NULL; l = l->next) { struct cups_device *device = l->data; if (strcmp(device->bdaddr, bdaddr) == 0) { g_free(device->name); g_free(device->bdaddr); g_free(device); device_list = g_slist_delete_link(device_list, l); return; } } } static gboolean list_known_printers(const char *adapter) { DBusMessageIter reply_iter, iter_array; DBusError error; DBusMessage *message, *reply; message = dbus_message_new_method_call("org.bluez", adapter, "org.bluez.Adapter1", "ListDevices"); if (message == NULL) return FALSE; dbus_error_init(&error); reply = dbus_connection_send_with_reply_and_block(conn, message, -1, &error); dbus_message_unref(message); if (dbus_error_is_set(&error)) { dbus_error_free(&error); return FALSE; } dbus_message_iter_init(reply, &reply_iter); if (dbus_message_iter_get_arg_type(&reply_iter) != DBUS_TYPE_ARRAY) { dbus_message_unref(reply); return FALSE; } dbus_message_iter_recurse(&reply_iter, &iter_array); while (dbus_message_iter_get_arg_type(&iter_array) == DBUS_TYPE_OBJECT_PATH) { const char *object_path; char *name = NULL; char *bdaddr = NULL; dbus_message_iter_get_basic(&iter_array, &object_path); if (device_is_printer(adapter, object_path, &name, &bdaddr)) { char *id; id = device_get_ieee1284_id(adapter, object_path); add_device_to_list(name, bdaddr, id); g_free(id); } g_free(name); g_free(bdaddr); dbus_message_iter_next(&iter_array); } dbus_message_unref(reply); return FALSE; } static DBusHandlerResult filter_func(DBusConnection *connection, DBusMessage *message, void *user_data) { if (dbus_message_is_signal(message, "org.bluez.Adapter1", "DeviceFound")) { const char *adapter, *bdaddr; char *name; DBusMessageIter iter; dbus_message_iter_init(message, &iter); dbus_message_iter_get_basic(&iter, &bdaddr); dbus_message_iter_next(&iter); adapter = dbus_message_get_path(message); if (parse_device_properties(&iter, &name, NULL)) remote_device_found(adapter, bdaddr, name); g_free (name); } else if (dbus_message_is_signal(message, "org.bluez.Adapter1", "DeviceDisappeared")) { const char *bdaddr; dbus_message_get_args(message, NULL, DBUS_TYPE_STRING, &bdaddr, DBUS_TYPE_INVALID); remote_device_disappeared(bdaddr); } else if (dbus_message_is_signal(message, "org.bluez.Adapter1", "PropertyChanged")) { DBusMessageIter iter, value_iter; const char *name; gboolean discovering; dbus_message_iter_init(message, &iter); dbus_message_iter_get_basic(&iter, &name); if (name == NULL || strcmp(name, "Discovering") != 0) return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; dbus_message_iter_next(&iter); dbus_message_iter_recurse(&iter, &value_iter); dbus_message_iter_get_basic(&value_iter, &discovering); if (discovering == FALSE && doing_disco) { doing_disco = FALSE; discovery_completed(); } } return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; } static gboolean list_printers(void) { /* 1. Connect to the bus * 2. Get the manager * 3. Get the default adapter * 4. Get a list of devices * 5. Get the class of each device * 6. Print the details from each printer device */ DBusError error; dbus_bool_t hcid_exists; DBusMessage *reply, *message; DBusMessageIter reply_iter; char *adapter, *match; conn = g_dbus_setup_bus(DBUS_BUS_SYSTEM, NULL, NULL); if (conn == NULL) return TRUE; dbus_error_init(&error); hcid_exists = dbus_bus_name_has_owner(conn, "org.bluez", &error); if (dbus_error_is_set(&error)) { dbus_error_free(&error); return TRUE; } if (!hcid_exists) return TRUE; /* Get the default adapter */ message = dbus_message_new_method_call("org.bluez", "/", "org.bluez.Manager", "DefaultAdapter"); if (message == NULL) { dbus_connection_unref(conn); return FALSE; } reply = dbus_connection_send_with_reply_and_block(conn, message, -1, &error); dbus_message_unref(message); if (dbus_error_is_set(&error)) { dbus_error_free(&error); dbus_connection_unref(conn); /* No adapter */ return TRUE; } dbus_message_iter_init(reply, &reply_iter); if (dbus_message_iter_get_arg_type(&reply_iter) != DBUS_TYPE_OBJECT_PATH) { dbus_message_unref(reply); dbus_connection_unref(conn); return FALSE; } dbus_message_iter_get_basic(&reply_iter, &adapter); adapter = g_strdup(adapter); dbus_message_unref(reply); if (!dbus_connection_add_filter(conn, filter_func, adapter, g_free)) { g_free(adapter); dbus_connection_unref(conn); return FALSE; } #define MATCH_FORMAT \ "type='signal'," \ "interface='org.bluez.Adapter1'," \ "sender='org.bluez'," \ "path='%s'" match = g_strdup_printf(MATCH_FORMAT, adapter); dbus_bus_add_match(conn, match, &error); g_free(match); /* Add the the recent devices */ list_known_printers(adapter); doing_disco = TRUE; message = dbus_message_new_method_call("org.bluez", adapter, "org.bluez.Adapter1", "StartDiscovery"); if (!dbus_connection_send_with_reply(conn, message, NULL, -1)) { dbus_message_unref(message); dbus_connection_unref(conn); g_free(adapter); return FALSE; } dbus_message_unref(message); loop = g_main_loop_new(NULL, TRUE); g_main_loop_run(loop); g_free(adapter); dbus_connection_unref(conn); return TRUE; } static gboolean print_ieee1284(const char *bdaddr) { DBusMessage *message, *reply, *adapter_reply; DBusMessageIter iter; char *object_path = NULL; char *adapter; char *id; adapter_reply = NULL; conn = g_dbus_setup_bus(DBUS_BUS_SYSTEM, NULL, NULL); if (conn == NULL) return FALSE; message = dbus_message_new_method_call("org.bluez", "/", "org.bluez.Manager", "DefaultAdapter"); adapter_reply = dbus_connection_send_with_reply_and_block(conn, message, -1, NULL); dbus_message_unref(message); if (!adapter_reply) return FALSE; if (dbus_message_get_args(adapter_reply, NULL, DBUS_TYPE_OBJECT_PATH, &adapter, DBUS_TYPE_INVALID) == FALSE) { dbus_message_unref(adapter_reply); return FALSE; } message = dbus_message_new_method_call("org.bluez", adapter, "org.bluez.Adapter1", "FindDevice"); dbus_message_iter_init_append(message, &iter); dbus_message_iter_append_basic(&iter, DBUS_TYPE_STRING, &bdaddr); if (adapter_reply != NULL) dbus_message_unref(adapter_reply); reply = dbus_connection_send_with_reply_and_block(conn, message, -1, NULL); dbus_message_unref(message); if (!reply) { message = dbus_message_new_method_call("org.bluez", adapter, "org.bluez.Adapter1", "CreateDevice"); dbus_message_iter_init_append(message, &iter); dbus_message_iter_append_basic(&iter, DBUS_TYPE_STRING, &bdaddr); reply = dbus_connection_send_with_reply_and_block(conn, message, -1, NULL); dbus_message_unref(message); if (!reply) return FALSE; } if (dbus_message_get_args(reply, NULL, DBUS_TYPE_OBJECT_PATH, &object_path, DBUS_TYPE_INVALID) == FALSE) { dbus_message_unref(reply); return FALSE; } id = device_get_ieee1284_id(adapter, object_path); if (id == NULL) { dbus_message_unref(reply); return FALSE; } printf("%s", id); g_free(id); dbus_message_unref(reply); return TRUE; } /* * Usage: printer-uri job-id user title copies options [file] * */ int main(int argc, char *argv[]) { sdp_session_t *sdp; bdaddr_t bdaddr; unsigned short ctrl_psm, data_psm; uint8_t channel, b[6]; char *ptr, str[3], device[18], service[13]; const char *uri, *cups_class; int i, err, fd, copies, proto; /* Make sure status messages are not buffered */ setbuf(stderr, NULL); /* Make sure output is not buffered */ setbuf(stdout, NULL); /* Ignore SIGPIPE signals */ #ifdef HAVE_SIGSET sigset(SIGPIPE, SIG_IGN); #elif defined(HAVE_SIGACTION) memset(&action, 0, sizeof(action)); action.sa_handler = SIG_IGN; sigaction(SIGPIPE, &action, NULL); #else signal(SIGPIPE, SIG_IGN); #endif /* HAVE_SIGSET */ if (argc == 1) { if (list_printers() == TRUE) return CUPS_BACKEND_OK; else return CUPS_BACKEND_FAILED; } else if (argc == 3 && strcmp(argv[1], "--get-deviceid") == 0) { if (bachk(argv[2]) < 0) { fprintf(stderr, "Invalid Bluetooth address '%s'\n", argv[2]); return CUPS_BACKEND_FAILED; } if (print_ieee1284(argv[2]) == FALSE) return CUPS_BACKEND_FAILED; return CUPS_BACKEND_OK; } if (argc < 6 || argc > 7) { fprintf(stderr, "Usage: bluetooth job-id user title copies" " options [file]\n"); fprintf(stderr, " bluetooth --get-deviceid [bdaddr]\n"); return CUPS_BACKEND_FAILED; } if (argc == 6) { fd = 0; copies = 1; } else { if ((fd = open(argv[6], O_RDONLY)) < 0) { perror("ERROR: Unable to open print file"); return CUPS_BACKEND_FAILED; } copies = atoi(argv[4]); } uri = getenv("DEVICE_URI"); if (!uri) uri = argv[0]; if (strncasecmp(uri, "bluetooth://", 12)) { fprintf(stderr, "ERROR: No device URI found\n"); return CUPS_BACKEND_FAILED; } ptr = argv[0] + 12; for (i = 0; i < 6; i++) { strncpy(str, ptr, 2); b[i] = (uint8_t) strtol(str, NULL, 16); ptr += 2; } sprintf(device, "%2.2X:%2.2X:%2.2X:%2.2X:%2.2X:%2.2X", b[0], b[1], b[2], b[3], b[4], b[5]); str2ba(device, &bdaddr); ptr = strchr(ptr, '/'); if (ptr) { strncpy(service, ptr + 1, 12); if (!strncasecmp(ptr + 1, "spp", 3)) proto = 1; else if (!strncasecmp(ptr + 1, "hcrp", 4)) proto = 2; else proto = 0; } else { strcpy(service, "auto"); proto = 0; } cups_class = getenv("CLASS"); fprintf(stderr, "DEBUG: %s device %s service %s fd %d copies %d class %s\n", argv[0], device, service, fd, copies, cups_class ? cups_class : "(none)"); fputs("STATE: +connecting-to-device\n", stderr); service_search: sdp = sdp_connect(BDADDR_ANY, &bdaddr, SDP_RETRY_IF_BUSY); if (!sdp) { fprintf(stderr, "ERROR: Can't open Bluetooth connection\n"); return CUPS_BACKEND_FAILED; } switch (proto) { case 1: err = sdp_search_spp(sdp, &channel); break; case 2: err = sdp_search_hcrp(sdp, &ctrl_psm, &data_psm); break; default: proto = 2; err = sdp_search_hcrp(sdp, &ctrl_psm, &data_psm); if (err) { proto = 1; err = sdp_search_spp(sdp, &channel); } break; } sdp_close(sdp); if (err) { if (cups_class) { fputs("INFO: Unable to contact printer, queuing on " "next printer in class...\n", stderr); sleep(5); return CUPS_BACKEND_FAILED; } sleep(20); fprintf(stderr, "ERROR: Can't get service information\n"); goto service_search; } connect: switch (proto) { case 1: err = spp_print(BDADDR_ANY, &bdaddr, channel, fd, copies, cups_class); break; case 2: err = hcrp_print(BDADDR_ANY, &bdaddr, ctrl_psm, data_psm, fd, copies, cups_class); break; default: err = CUPS_BACKEND_FAILED; fprintf(stderr, "ERROR: Unsupported protocol\n"); break; } if (err == CUPS_BACKEND_FAILED && cups_class) { fputs("INFO: Unable to contact printer, queuing on " "next printer in class...\n", stderr); sleep(5); return CUPS_BACKEND_FAILED; } else if (err == CUPS_BACKEND_RETRY) { sleep(20); goto connect; } if (fd != 0) close(fd); if (!err) fprintf(stderr, "INFO: Ready to print\n"); return err; } bluez-5.82/profiles/PaxHeaders/deviceinfo0000644000000000000000000000005014773213564015543 xustar0020 atime=1743591291 20 ctime=1743591284 bluez-5.82/profiles/deviceinfo/0000755000000000000000000000000014773213564015301 5ustar00rootrootbluez-5.82/profiles/deviceinfo/PaxHeaders/deviceinfo.c0000644000000000000000000000005014766002272020071 xustar0020 atime=1743515578 20 ctime=1743591284 bluez-5.82/profiles/deviceinfo/deviceinfo.c0000644000000000000000000000717614766002272017565 0ustar00rootroot// SPDX-License-Identifier: GPL-2.0-or-later /* * * BlueZ - Bluetooth protocol stack for Linux * * Copyright (C) 2012 Texas Instruments, Inc. * Copyright (C) 2015 Google Inc. * */ #ifdef HAVE_CONFIG_H #include #endif #include #include #include #include "lib/bluetooth.h" #include "lib/sdp.h" #include "lib/uuid.h" #include "src/plugin.h" #include "src/adapter.h" #include "src/device.h" #include "src/profile.h" #include "src/service.h" #include "attrib/gattrib.h" #include "src/shared/util.h" #include "src/shared/queue.h" #include "src/shared/gatt-db.h" #include "src/shared/gatt-client.h" #include "attrib/att.h" #include "src/log.h" #define PNP_ID_SIZE 7 static void read_pnpid_cb(bool success, uint8_t att_ecode, const uint8_t *value, uint16_t length, void *user_data) { struct btd_device *device = user_data; if (!success) { error("Error reading PNP_ID value: %s", att_ecode2str(att_ecode)); return; } if (length < PNP_ID_SIZE) { error("Error reading PNP_ID: Invalid pdu length received"); return; } btd_device_set_pnpid(device, value[0], get_le16(&value[1]), get_le16(&value[3]), get_le16(&value[5])); } static void handle_pnpid(struct btd_device *device, uint16_t value_handle) { struct bt_gatt_client *client = btd_device_get_gatt_client(device); if (!bt_gatt_client_read_value(client, value_handle, read_pnpid_cb, device, NULL)) DBG("Failed to send request to read pnpid"); } static void handle_characteristic(struct gatt_db_attribute *attr, void *user_data) { struct btd_device *device = user_data; uint16_t value_handle; bt_uuid_t uuid, pnpid_uuid; bt_string_to_uuid(&pnpid_uuid, PNPID_UUID); if (!gatt_db_attribute_get_char_data(attr, NULL, &value_handle, NULL, NULL, &uuid)) { error("Failed to obtain characteristic data"); return; } if (bt_uuid_cmp(&pnpid_uuid, &uuid) == 0) handle_pnpid(device, value_handle); else { char uuid_str[MAX_LEN_UUID_STR]; bt_uuid_to_string(&uuid, uuid_str, sizeof(uuid_str)); DBG("Unsupported characteristic: %s", uuid_str); } } static void foreach_deviceinfo_service(struct gatt_db_attribute *attr, void *user_data) { struct btd_device *device = user_data; gatt_db_service_foreach_char(attr, handle_characteristic, device); } static int deviceinfo_probe(struct btd_service *service) { return 0; } static void deviceinfo_remove(struct btd_service *service) { } static int deviceinfo_accept(struct btd_service *service) { struct btd_device *device = btd_service_get_device(service); struct gatt_db *db = btd_device_get_gatt_db(device); char addr[18]; bt_uuid_t deviceinfo_uuid; ba2str(device_get_address(device), addr); DBG("deviceinfo profile accept (%s)", addr); /* Handle the device info service */ bt_string_to_uuid(&deviceinfo_uuid, DEVICE_INFORMATION_UUID); gatt_db_foreach_service(db, &deviceinfo_uuid, foreach_deviceinfo_service, device); btd_service_connecting_complete(service, 0); return 0; } static int deviceinfo_disconnect(struct btd_service *service) { btd_service_disconnecting_complete(service, 0); return 0; } static struct btd_profile deviceinfo_profile = { .name = "deviceinfo", .remote_uuid = DEVICE_INFORMATION_UUID, .device_probe = deviceinfo_probe, .device_remove = deviceinfo_remove, .accept = deviceinfo_accept, .disconnect = deviceinfo_disconnect, }; static int deviceinfo_init(void) { return btd_profile_register(&deviceinfo_profile); } static void deviceinfo_exit(void) { btd_profile_unregister(&deviceinfo_profile); } BLUETOOTH_PLUGIN_DEFINE(deviceinfo, VERSION, BLUETOOTH_PLUGIN_PRIORITY_DEFAULT, deviceinfo_init, deviceinfo_exit) bluez-5.82/profiles/deviceinfo/PaxHeaders/dis.h0000644000000000000000000000005014015011623016525 xustar0020 atime=1743516562 20 ctime=1743591278 bluez-5.82/profiles/deviceinfo/dis.h0000644000000000000000000000127114015011623016207 0ustar00rootroot/* SPDX-License-Identifier: LGPL-2.1-or-later */ /* * * BlueZ - Bluetooth protocol stack for Linux * * Copyright (C) 2014 Intel Corporation. All rights reserved. * * */ struct bt_dis; struct bt_dis *bt_dis_new(struct gatt_db *db); struct bt_dis *bt_dis_new_primary(void *primary); struct bt_dis *bt_dis_ref(struct bt_dis *dis); void bt_dis_unref(struct bt_dis *dis); bool bt_dis_attach(struct bt_dis *dis, void *gatt); void bt_dis_detach(struct bt_dis *dis); typedef void (*bt_dis_notify) (uint8_t source, uint16_t vendor, uint16_t product, uint16_t version, void *user_data); bool bt_dis_set_notification(struct bt_dis *dis, bt_dis_notify func, void *user_data); bluez-5.82/profiles/deviceinfo/PaxHeaders/dis.c0000644000000000000000000000005014214376354016540 xustar0020 atime=1743516566 20 ctime=1743591278 bluez-5.82/profiles/deviceinfo/dis.c0000644000000000000000000001536114214376354016227 0ustar00rootroot// SPDX-License-Identifier: GPL-2.0-or-later /* * * BlueZ - Bluetooth protocol stack for Linux * * Copyright (C) 2012 Texas Instruments, Inc. * */ #ifdef HAVE_CONFIG_H #include #endif #include #include #include #include "src/log.h" #include "lib/bluetooth.h" #include "lib/sdp.h" #include "lib/uuid.h" #include "src/shared/util.h" #include "src/shared/queue.h" #include "src/shared/att.h" #include "src/shared/gatt-db.h" #include "attrib/gattrib.h" #include "attrib/att.h" #include "attrib/gatt.h" #include "profiles/deviceinfo/dis.h" #define DIS_UUID16 0x180a #define PNP_ID_SIZE 7 struct bt_dis { int ref_count; uint16_t handle; uint8_t source; uint16_t vendor; uint16_t product; uint16_t version; GAttrib *attrib; /* GATT connection */ struct gatt_primary *primary; /* Primary details */ bt_dis_notify notify; void *notify_data; struct queue *gatt_op; }; struct characteristic { struct gatt_char attr; /* Characteristic */ struct bt_dis *d; /* deviceinfo where the char belongs */ }; struct gatt_request { unsigned int id; struct bt_dis *dis; void *user_data; }; static void destroy_gatt_req(struct gatt_request *req) { queue_remove(req->dis->gatt_op, req); bt_dis_unref(req->dis); free(req); } static void dis_free(struct bt_dis *dis) { bt_dis_detach(dis); free(dis->primary); queue_destroy(dis->gatt_op, (void *) destroy_gatt_req); g_free(dis); } static void foreach_dis_char(struct gatt_db_attribute *attr, void *user_data) { struct bt_dis *dis = user_data; bt_uuid_t pnpid_uuid, uuid; uint16_t value_handle; /* Ignore if there are multiple instances */ if (dis->handle) return; if (!gatt_db_attribute_get_char_data(attr, NULL, &value_handle, NULL, NULL, &uuid)) return; /* Find PNPID characteristic's value handle */ bt_string_to_uuid(&pnpid_uuid, PNPID_UUID); if (bt_uuid_cmp(&pnpid_uuid, &uuid) == 0) dis->handle = value_handle; } static void foreach_dis_service(struct gatt_db_attribute *attr, void *user_data) { struct bt_dis *dis = user_data; /* Ignore if there are multiple instances */ if (dis->handle) return; gatt_db_service_foreach_char(attr, foreach_dis_char, dis); } struct bt_dis *bt_dis_new(struct gatt_db *db) { struct bt_dis *dis; dis = g_try_new0(struct bt_dis, 1); if (!dis) return NULL; dis->gatt_op = queue_new(); if (db) { bt_uuid_t uuid; /* Handle the DIS service */ bt_uuid16_create(&uuid, DIS_UUID16); gatt_db_foreach_service(db, &uuid, foreach_dis_service, dis); if (!dis->handle) { dis_free(dis); return NULL; } } return bt_dis_ref(dis); } struct bt_dis *bt_dis_new_primary(void *primary) { struct bt_dis *dis; dis = g_try_new0(struct bt_dis, 1); if (!dis) return NULL; dis->gatt_op = queue_new(); if (primary) dis->primary = util_memdup(primary, sizeof(*dis->primary)); return bt_dis_ref(dis); } struct bt_dis *bt_dis_ref(struct bt_dis *dis) { if (!dis) return NULL; __sync_fetch_and_add(&dis->ref_count, 1); return dis; } void bt_dis_unref(struct bt_dis *dis) { if (!dis) return; if (__sync_sub_and_fetch(&dis->ref_count, 1)) return; dis_free(dis); } static struct gatt_request *create_request(struct bt_dis *dis, void *user_data) { struct gatt_request *req; req = new0(struct gatt_request, 1); req->user_data = user_data; req->dis = bt_dis_ref(dis); return req; } static bool set_and_store_gatt_req(struct bt_dis *dis, struct gatt_request *req, unsigned int id) { req->id = id; return queue_push_head(dis->gatt_op, req); } static void read_pnpid_cb(guint8 status, const guint8 *pdu, guint16 len, gpointer user_data) { struct gatt_request *req = user_data; struct bt_dis *dis = req->user_data; uint8_t value[PNP_ID_SIZE]; ssize_t vlen; destroy_gatt_req(req); if (status != 0) { error("Error reading PNP_ID value: %s", att_ecode2str(status)); return; } vlen = dec_read_resp(pdu, len, value, sizeof(value)); if (vlen < 0) { error("Error reading PNP_ID: Protocol error"); return; } if (vlen < 7) { error("Error reading PNP_ID: Invalid pdu length received"); return; } dis->source = value[0]; dis->vendor = get_le16(&value[1]); dis->product = get_le16(&value[3]); dis->version = get_le16(&value[5]); DBG("source: 0x%02X vendor: 0x%04X product: 0x%04X version: 0x%04X", dis->source, dis->vendor, dis->product, dis->version); if (dis->notify) dis->notify(dis->source, dis->vendor, dis->product, dis->version, dis->notify_data); } static void read_char(struct bt_dis *dis, GAttrib *attrib, uint16_t handle, GAttribResultFunc func, gpointer user_data) { struct gatt_request *req; unsigned int id; req = create_request(dis, user_data); id = gatt_read_char(attrib, handle, func, req); if (set_and_store_gatt_req(dis, req, id)) return; error("dis: Could not read characteristic"); g_attrib_cancel(attrib, id); free(req); } static void discover_char(struct bt_dis *dis, GAttrib *attrib, uint16_t start, uint16_t end, bt_uuid_t *uuid, gatt_cb_t func, gpointer user_data) { struct gatt_request *req; unsigned int id; req = create_request(dis, user_data); id = gatt_discover_char(attrib, start, end, uuid, func, req); if (set_and_store_gatt_req(dis, req, id)) return; error("dis: Could not send discover characteristic"); g_attrib_cancel(attrib, id); free(req); } static void configure_deviceinfo_cb(uint8_t status, GSList *characteristics, void *user_data) { struct gatt_request *req = user_data; struct bt_dis *d = req->user_data; GSList *l; destroy_gatt_req(req); if (status != 0) { error("Discover deviceinfo characteristics: %s", att_ecode2str(status)); return; } for (l = characteristics; l; l = l->next) { struct gatt_char *c = l->data; if (strcmp(c->uuid, PNPID_UUID) == 0) { d->handle = c->value_handle; read_char(d, d->attrib, d->handle, read_pnpid_cb, d); break; } } } bool bt_dis_attach(struct bt_dis *dis, void *attrib) { struct gatt_primary *primary = dis->primary; if (dis->attrib) return false; dis->attrib = g_attrib_ref(attrib); if (!dis->handle) discover_char(dis, dis->attrib, primary->range.start, primary->range.end, NULL, configure_deviceinfo_cb, dis); else read_char(dis, attrib, dis->handle, read_pnpid_cb, dis); return true; } static void cancel_gatt_req(struct gatt_request *req) { if (g_attrib_cancel(req->dis->attrib, req->id)) destroy_gatt_req(req); } void bt_dis_detach(struct bt_dis *dis) { if (!dis->attrib) return; queue_foreach(dis->gatt_op, (void *) cancel_gatt_req, NULL); g_attrib_unref(dis->attrib); dis->attrib = NULL; } bool bt_dis_set_notification(struct bt_dis *dis, bt_dis_notify func, void *user_data) { if (!dis) return false; dis->notify = func; dis->notify_data = user_data; return true; } bluez-5.82/profiles/PaxHeaders/network0000644000000000000000000000005014773213570015116 xustar0020 atime=1743591291 20 ctime=1743591288 bluez-5.82/profiles/network/0000755000000000000000000000000014773213570014654 5ustar00rootrootbluez-5.82/profiles/network/PaxHeaders/connection.c0000644000000000000000000000005014015011623017456 xustar0020 atime=1743516553 20 ctime=1743591284 bluez-5.82/profiles/network/connection.c0000644000000000000000000003125214015011623017142 0ustar00rootroot// SPDX-License-Identifier: GPL-2.0-or-later /* * * BlueZ - Bluetooth protocol stack for Linux * * Copyright (C) 2004-2010 Marcel Holtmann * * */ #ifdef HAVE_CONFIG_H #include #endif #define _GNU_SOURCE #include #include #include #include #include #include #include "lib/bluetooth.h" #include "lib/bnep.h" #include "lib/sdp.h" #include "gdbus/gdbus.h" #include "btio/btio.h" #include "src/log.h" #include "src/dbus-common.h" #include "src/adapter.h" #include "src/device.h" #include "src/profile.h" #include "src/service.h" #include "src/error.h" #include "lib/uuid.h" #include "bnep.h" #include "connection.h" #define NETWORK_PEER_INTERFACE "org.bluez.Network1" #define BNEP_INTERFACE "bnep%d" typedef enum { CONNECTED, CONNECTING, DISCONNECTED } conn_state; struct network_peer { struct btd_device *device; GSList *connections; }; struct network_conn { struct btd_service *service; char dev[16]; /* Interface name */ uint16_t id; /* Role: Service Class Identifier */ conn_state state; GIOChannel *io; guint dc_id; struct network_peer *peer; DBusMessage *connect; struct bnep *session; }; static GSList *peers = NULL; static uint16_t get_pan_srv_id(const char *svc) { if (!strcasecmp(svc, "panu") || !strcasecmp(svc, PANU_UUID)) return BNEP_SVC_PANU; if (!strcasecmp(svc, "nap") || !strcasecmp(svc, NAP_UUID)) return BNEP_SVC_NAP; if (!strcasecmp(svc, "gn") || !strcasecmp(svc, GN_UUID)) return BNEP_SVC_GN; return 0; } static struct network_peer *find_peer(GSList *list, struct btd_device *device) { for (; list; list = list->next) { struct network_peer *peer = list->data; if (peer->device == device) return peer; } return NULL; } static struct network_conn *find_connection_by_state(GSList *list, conn_state state) { for (; list; list = list->next) { struct network_conn *nc = list->data; if (nc->state == state) return nc; } return NULL; } static void bnep_disconn_cb(gpointer data) { struct network_conn *nc = data; DBusConnection *conn = btd_get_dbus_connection(); const char *path = device_get_path(nc->peer->device); g_dbus_emit_property_changed(conn, path, NETWORK_PEER_INTERFACE, "Connected"); g_dbus_emit_property_changed(conn, path, NETWORK_PEER_INTERFACE, "Interface"); g_dbus_emit_property_changed(conn, path, NETWORK_PEER_INTERFACE, "UUID"); device_remove_disconnect_watch(nc->peer->device, nc->dc_id); nc->dc_id = 0; btd_service_disconnecting_complete(nc->service, 0); info("%s disconnected", nc->dev); nc->state = DISCONNECTED; memset(nc->dev, 0, sizeof(nc->dev)); strncpy(nc->dev, BNEP_INTERFACE, 16); nc->dev[15] = '\0'; bnep_free(nc->session); nc->session = NULL; } static void local_connect_cb(struct network_conn *nc, int err) { DBusConnection *conn = btd_get_dbus_connection(); const char *pdev = nc->dev; if (err < 0) { DBusMessage *reply = btd_error_failed(nc->connect, strerror(-err)); g_dbus_send_message(conn, reply); } else { g_dbus_send_reply(conn, nc->connect, DBUS_TYPE_STRING, &pdev, DBUS_TYPE_INVALID); } dbus_message_unref(nc->connect); nc->connect = NULL; } static void cancel_connection(struct network_conn *nc, int err) { btd_service_connecting_complete(nc->service, err); if (nc->connect) local_connect_cb(nc, err); if (nc->io) { g_io_channel_shutdown(nc->io, FALSE, NULL); g_io_channel_unref(nc->io); nc->io = NULL; } if (nc->state == CONNECTED) bnep_disconnect(nc->session); bnep_free(nc->session); nc->session = NULL; nc->state = DISCONNECTED; } static void connection_destroy(DBusConnection *conn, void *user_data) { struct network_conn *nc = user_data; cancel_connection(nc, -EIO); } static void disconnect_cb(struct btd_device *device, gboolean removal, void *user_data) { struct network_conn *nc = user_data; info("Network: disconnect %s", device_get_path(nc->peer->device)); connection_destroy(NULL, user_data); } static void bnep_conn_cb(char *iface, int err, void *data) { struct network_conn *nc = data; const char *path; DBusConnection *conn; DBG(""); if (err < 0) { error("connect failed %s", strerror(-err)); goto failed; } memcpy(nc->dev, iface, sizeof(nc->dev)); info("%s connected", nc->dev); btd_service_connecting_complete(nc->service, 0); if (nc->connect) local_connect_cb(nc, 0); conn = btd_get_dbus_connection(); path = device_get_path(nc->peer->device); g_dbus_emit_property_changed(conn, path, NETWORK_PEER_INTERFACE, "Connected"); g_dbus_emit_property_changed(conn, path, NETWORK_PEER_INTERFACE, "Interface"); g_dbus_emit_property_changed(conn, path, NETWORK_PEER_INTERFACE, "UUID"); nc->state = CONNECTED; nc->dc_id = device_add_disconnect_watch(nc->peer->device, disconnect_cb, nc, NULL); return; failed: cancel_connection(nc, -EIO); } static void connect_cb(GIOChannel *chan, GError *err, gpointer data) { struct network_conn *nc = data; int sk, perr; if (err) { error("%s", err->message); goto failed; } sk = g_io_channel_unix_get_fd(nc->io); nc->session = bnep_new(sk, BNEP_SVC_PANU, nc->id, BNEP_INTERFACE); if (!nc->session) goto failed; perr = bnep_connect(nc->session, bnep_conn_cb, bnep_disconn_cb, nc, nc); if (perr < 0) { error("bnep connect(): %s (%d)", strerror(-perr), -perr); goto failed; } if (nc->io) { g_io_channel_unref(nc->io); nc->io = NULL; } return; failed: cancel_connection(nc, -EIO); } static DBusMessage *local_connect(DBusConnection *conn, DBusMessage *msg, void *data) { struct network_peer *peer = data; struct btd_service *service; struct network_conn *nc; const char *svc; uint16_t id; int err; char uuid_str[MAX_LEN_UUID_STR]; bt_uuid_t uuid16, uuid128; if (dbus_message_get_args(msg, NULL, DBUS_TYPE_STRING, &svc, DBUS_TYPE_INVALID) == FALSE) return btd_error_invalid_args(msg); id = get_pan_srv_id(svc); bt_uuid16_create(&uuid16, id); bt_uuid_to_uuid128(&uuid16, &uuid128); if (bt_uuid_to_string(&uuid128, uuid_str, MAX_LEN_UUID_STR) < 0) return btd_error_invalid_args(msg); service = btd_device_get_service(peer->device, uuid_str); if (service == NULL) return btd_error_not_supported(msg); nc = btd_service_get_user_data(service); if (nc->connect != NULL) return btd_error_busy(msg); err = connection_connect(nc->service); if (err < 0) return btd_error_failed(msg, strerror(-err)); nc->connect = dbus_message_ref(msg); return NULL; } /* Connect and initiate BNEP session */ int connection_connect(struct btd_service *svc) { struct network_conn *nc = btd_service_get_user_data(svc); struct network_peer *peer = nc->peer; uint16_t id = get_pan_srv_id(btd_service_get_profile(svc)->remote_uuid); GError *err = NULL; const bdaddr_t *src; const bdaddr_t *dst; DBG("id %u", id); if (nc->state != DISCONNECTED) return -EALREADY; src = btd_adapter_get_address(device_get_adapter(peer->device)); dst = device_get_address(peer->device); nc->io = bt_io_connect(connect_cb, nc, NULL, &err, BT_IO_OPT_SOURCE_BDADDR, src, BT_IO_OPT_DEST_BDADDR, dst, BT_IO_OPT_PSM, BNEP_PSM, BT_IO_OPT_SEC_LEVEL, BT_IO_SEC_MEDIUM, BT_IO_OPT_OMTU, BNEP_MTU, BT_IO_OPT_IMTU, BNEP_MTU, BT_IO_OPT_INVALID); if (!nc->io) return -EIO; nc->state = CONNECTING; return 0; } int connection_disconnect(struct btd_service *svc) { struct network_conn *nc = btd_service_get_user_data(svc); if (nc->state == DISCONNECTED) return 0; connection_destroy(NULL, nc); return 0; } static DBusMessage *local_disconnect(DBusConnection *conn, DBusMessage *msg, void *data) { struct network_peer *peer = data; GSList *l; for (l = peer->connections; l; l = l->next) { struct network_conn *nc = l->data; int err; if (nc->state == DISCONNECTED) continue; err = connection_disconnect(nc->service); if (err < 0) return btd_error_failed(msg, strerror(-err)); return g_dbus_create_reply(msg, DBUS_TYPE_INVALID); } return btd_error_not_connected(msg); } static gboolean network_property_get_connected(const GDBusPropertyTable *property, DBusMessageIter *iter, void *data) { struct network_peer *peer = data; struct network_conn *nc; dbus_bool_t connected; nc = find_connection_by_state(peer->connections, CONNECTED); connected = nc != NULL ? TRUE : FALSE; dbus_message_iter_append_basic(iter, DBUS_TYPE_BOOLEAN, &connected); return TRUE; } static gboolean network_property_exists(const GDBusPropertyTable *property, void *data) { struct network_peer *peer = data; struct network_conn *nc; nc = find_connection_by_state(peer->connections, CONNECTED); if (nc == NULL) return FALSE; return TRUE; } static gboolean network_property_get_interface(const GDBusPropertyTable *property, DBusMessageIter *iter, void *data) { struct network_peer *peer = data; struct network_conn *nc; const char *iface; nc = find_connection_by_state(peer->connections, CONNECTED); if (nc == NULL) return FALSE; iface = nc->dev; dbus_message_iter_append_basic(iter, DBUS_TYPE_STRING, &iface); return TRUE; } static gboolean network_property_get_uuid(const GDBusPropertyTable *property, DBusMessageIter *iter, void *data) { struct network_peer *peer = data; struct network_conn *nc; char uuid_str[MAX_LEN_UUID_STR]; const char *uuid = uuid_str; bt_uuid_t uuid16, uuid128; nc = find_connection_by_state(peer->connections, CONNECTED); if (nc == NULL) return FALSE; bt_uuid16_create(&uuid16, nc->id); bt_uuid_to_uuid128(&uuid16, &uuid128); bt_uuid_to_string(&uuid128, uuid_str, MAX_LEN_UUID_STR); dbus_message_iter_append_basic(iter, DBUS_TYPE_STRING, &uuid); return TRUE; } static void connection_free(void *data) { struct network_conn *nc = data; if (nc->dc_id) device_remove_disconnect_watch(nc->peer->device, nc->dc_id); connection_destroy(NULL, nc); if (nc->connect) dbus_message_unref(nc->connect); btd_service_unref(nc->service); g_free(nc); } static void peer_free(struct network_peer *peer) { g_slist_free_full(peer->connections, connection_free); btd_device_unref(peer->device); g_free(peer); } static void path_unregister(void *data) { struct network_peer *peer = data; DBG("Unregistered interface %s on path %s", NETWORK_PEER_INTERFACE, device_get_path(peer->device)); peers = g_slist_remove(peers, peer); peer_free(peer); } static const GDBusMethodTable connection_methods[] = { { GDBUS_ASYNC_METHOD("Connect", GDBUS_ARGS({"uuid", "s"}), GDBUS_ARGS({"interface", "s"}), local_connect) }, { GDBUS_METHOD("Disconnect", NULL, NULL, local_disconnect) }, { } }; static const GDBusPropertyTable connection_properties[] = { { "Connected", "b", network_property_get_connected }, { "Interface", "s", network_property_get_interface, NULL, network_property_exists }, { "UUID", "s", network_property_get_uuid, NULL, network_property_exists }, { } }; void connection_unregister(struct btd_service *svc) { struct btd_device *device = btd_service_get_device(svc); struct network_conn *conn = btd_service_get_user_data(svc); struct network_peer *peer = conn->peer; uint16_t id = get_pan_srv_id(btd_service_get_profile(svc)->remote_uuid); DBG("%s id %u", device_get_path(device), id); peer->connections = g_slist_remove(peer->connections, conn); connection_free(conn); if (peer->connections != NULL) return; g_dbus_unregister_interface(btd_get_dbus_connection(), device_get_path(device), NETWORK_PEER_INTERFACE); } static struct network_peer *create_peer(struct btd_device *device) { struct network_peer *peer; const char *path; peer = g_new0(struct network_peer, 1); peer->device = btd_device_ref(device); path = device_get_path(device); if (g_dbus_register_interface(btd_get_dbus_connection(), path, NETWORK_PEER_INTERFACE, connection_methods, NULL, connection_properties, peer, path_unregister) == FALSE) { error("D-Bus failed to register %s interface", NETWORK_PEER_INTERFACE); peer_free(peer); return NULL; } DBG("Registered interface %s on path %s", NETWORK_PEER_INTERFACE, path); return peer; } int connection_register(struct btd_service *svc) { struct btd_device *device = btd_service_get_device(svc); struct network_peer *peer; struct network_conn *nc; uint16_t id = get_pan_srv_id(btd_service_get_profile(svc)->remote_uuid); DBG("%s id %u", device_get_path(device), id); peer = find_peer(peers, device); if (!peer) { peer = create_peer(device); if (!peer) return -1; peers = g_slist_append(peers, peer); } nc = g_new0(struct network_conn, 1); nc->id = id; nc->service = btd_service_ref(svc); nc->state = DISCONNECTED; nc->peer = peer; btd_service_set_user_data(svc, nc); DBG("id %u registered", id); peer->connections = g_slist_append(peer->connections, nc); return 0; } bluez-5.82/profiles/network/PaxHeaders/bnep.h0000644000000000000000000000005014766002272016265 xustar0020 atime=1743515578 20 ctime=1743591279 bluez-5.82/profiles/network/bnep.h0000644000000000000000000000162714766002272015754 0ustar00rootroot/* SPDX-License-Identifier: GPL-2.0-or-later */ /* * * BlueZ - Bluetooth protocol stack for Linux * * Copyright (C) 2004-2010 Marcel Holtmann * * */ struct bnep; int bnep_init(void); int bnep_cleanup(void); struct bnep *bnep_new(int sk, uint16_t local_role, uint16_t remote_role, char *iface); void bnep_free(struct bnep *session); typedef void (*bnep_connect_cb) (char *iface, int err, void *data); typedef void (*bnep_disconnect_cb) (void *data); int bnep_connect(struct bnep *session, bnep_connect_cb conn_cb, bnep_disconnect_cb disconn_cb, void *conn_data, void *disconn_data); void bnep_disconnect(struct bnep *session); int bnep_server_add(int sk, char *bridge, char *iface, const bdaddr_t *addr, uint8_t *setup_data, int len); void bnep_server_delete(char *bridge, char *iface, const bdaddr_t *addr); int bnep_send_unkown_rsp(int sk, uint16_t resp); bluez-5.82/profiles/network/PaxHeaders/server.c0000644000000000000000000000005014766002272016642 xustar0020 atime=1743515578 20 ctime=1743591284 bluez-5.82/profiles/network/server.c0000644000000000000000000004065314766002272016333 0ustar00rootroot// SPDX-License-Identifier: GPL-2.0-or-later /* * * BlueZ - Bluetooth protocol stack for Linux * * Copyright (C) 2004-2010 Marcel Holtmann * * */ #ifdef HAVE_CONFIG_H #include #endif #define _GNU_SOURCE #include #include #include #include #include #include #include "lib/bluetooth.h" #include "lib/bnep.h" #include "lib/sdp.h" #include "lib/sdp_lib.h" #include "lib/uuid.h" #include "gdbus/gdbus.h" #include "btio/btio.h" #include "src/dbus-common.h" #include "src/adapter.h" #include "src/log.h" #include "src/error.h" #include "src/sdpd.h" #include "src/shared/util.h" #include "bnep.h" #include "server.h" #define NETWORK_SERVER_INTERFACE "org.bluez.NetworkServer1" #define BNEP_INTERFACE "bnep%d" #define SETUP_TIMEOUT 1 /* Pending Authorization */ struct network_session { bdaddr_t dst; /* Remote Bluetooth Address */ char dev[16]; /* Interface name */ GIOChannel *io; /* Pending connect channel */ guint watch; /* BNEP socket watch */ }; struct network_adapter { struct btd_adapter *adapter; /* Adapter pointer */ GIOChannel *io; /* Bnep socket */ struct network_session *setup; /* Setup in progress */ GSList *servers; /* Server register to adapter */ }; /* Main server structure */ struct network_server { bdaddr_t src; /* Bluetooth Local Address */ char *name; /* Server service name */ char *bridge; /* Bridge name */ uint32_t record_id; /* Service record id */ uint16_t id; /* Service class identifier */ GSList *sessions; /* Active connections */ struct network_adapter *na; /* Adapter reference */ guint watch_id; /* Client service watch */ }; static GSList *adapters = NULL; static gboolean security = TRUE; static struct network_adapter *find_adapter(GSList *list, struct btd_adapter *adapter) { for (; list; list = list->next) { struct network_adapter *na = list->data; if (na->adapter == adapter) return na; } return NULL; } static struct network_server *find_server(GSList *list, uint16_t id) { for (; list; list = list->next) { struct network_server *ns = list->data; if (ns->id == id) return ns; } return NULL; } static struct network_server *find_server_by_uuid(GSList *list, const char *uuid) { bt_uuid_t srv_uuid, bnep_uuid; if (!bt_string_to_uuid(&srv_uuid, uuid)) { for (; list; list = list->next) { struct network_server *ns = list->data; bt_uuid16_create(&bnep_uuid, ns->id); /* UUID value compare */ if (!bt_uuid_cmp(&srv_uuid, &bnep_uuid)) return ns; } } else { for (; list; list = list->next) { struct network_server *ns = list->data; /* String value compare */ switch (ns->id) { case BNEP_SVC_PANU: if (!strcasecmp(uuid, "panu")) return ns; break; case BNEP_SVC_NAP: if (!strcasecmp(uuid, "nap")) return ns; break; case BNEP_SVC_GN: if (!strcasecmp(uuid, "gn")) return ns; break; } } } return NULL; } static sdp_record_t *server_record_new(const char *name, uint16_t id) { sdp_list_t *svclass, *pfseq, *apseq, *root, *aproto; uuid_t root_uuid, pan, l2cap, bnep; sdp_profile_desc_t profile[1]; sdp_list_t *proto[2]; sdp_data_t *v, *p; uint16_t psm = BNEP_PSM, version = 0x0100; uint16_t security_desc = (security ? 0x0001 : 0x0000); uint16_t net_access_type = 0xfffe; uint32_t max_net_access_rate = 0; const char *desc = "Network service"; sdp_record_t *record; record = sdp_record_alloc(); if (!record) return NULL; record->attrlist = NULL; record->pattern = NULL; switch (id) { case BNEP_SVC_NAP: sdp_uuid16_create(&pan, NAP_SVCLASS_ID); svclass = sdp_list_append(NULL, &pan); sdp_set_service_classes(record, svclass); sdp_uuid16_create(&profile[0].uuid, NAP_PROFILE_ID); profile[0].version = 0x0100; pfseq = sdp_list_append(NULL, &profile[0]); sdp_set_profile_descs(record, pfseq); sdp_set_info_attr(record, name, NULL, desc); sdp_attr_add_new(record, SDP_ATTR_NET_ACCESS_TYPE, SDP_UINT16, &net_access_type); sdp_attr_add_new(record, SDP_ATTR_MAX_NET_ACCESSRATE, SDP_UINT32, &max_net_access_rate); break; case BNEP_SVC_GN: sdp_uuid16_create(&pan, GN_SVCLASS_ID); svclass = sdp_list_append(NULL, &pan); sdp_set_service_classes(record, svclass); sdp_uuid16_create(&profile[0].uuid, GN_PROFILE_ID); profile[0].version = 0x0100; pfseq = sdp_list_append(NULL, &profile[0]); sdp_set_profile_descs(record, pfseq); sdp_set_info_attr(record, name, NULL, desc); break; case BNEP_SVC_PANU: sdp_uuid16_create(&pan, PANU_SVCLASS_ID); svclass = sdp_list_append(NULL, &pan); sdp_set_service_classes(record, svclass); sdp_uuid16_create(&profile[0].uuid, PANU_PROFILE_ID); profile[0].version = 0x0100; pfseq = sdp_list_append(NULL, &profile[0]); sdp_set_profile_descs(record, pfseq); sdp_set_info_attr(record, name, NULL, desc); break; default: sdp_record_free(record); return NULL; } sdp_uuid16_create(&root_uuid, PUBLIC_BROWSE_GROUP); root = sdp_list_append(NULL, &root_uuid); sdp_set_browse_groups(record, root); sdp_uuid16_create(&l2cap, L2CAP_UUID); proto[0] = sdp_list_append(NULL, &l2cap); p = sdp_data_alloc(SDP_UINT16, &psm); proto[0] = sdp_list_append(proto[0], p); apseq = sdp_list_append(NULL, proto[0]); sdp_uuid16_create(&bnep, BNEP_UUID); proto[1] = sdp_list_append(NULL, &bnep); v = sdp_data_alloc(SDP_UINT16, &version); proto[1] = sdp_list_append(proto[1], v); /* Supported protocols */ { uint16_t ptype[] = { 0x0800, /* IPv4 */ 0x0806, /* ARP */ }; sdp_data_t *head, *pseq; int p; for (p = 0, head = NULL; p < 2; p++) { sdp_data_t *data = sdp_data_alloc(SDP_UINT16, &ptype[p]); if (head) sdp_seq_append(head, data); else head = data; } pseq = sdp_data_alloc(SDP_SEQ16, head); proto[1] = sdp_list_append(proto[1], pseq); } apseq = sdp_list_append(apseq, proto[1]); aproto = sdp_list_append(NULL, apseq); sdp_set_access_protos(record, aproto); sdp_add_lang_attr(record); sdp_attr_add_new(record, SDP_ATTR_SECURITY_DESC, SDP_UINT16, &security_desc); sdp_data_free(p); sdp_data_free(v); sdp_list_free(apseq, NULL); sdp_list_free(root, NULL); sdp_list_free(aproto, NULL); sdp_list_free(proto[0], NULL); sdp_list_free(proto[1], NULL); sdp_list_free(svclass, NULL); sdp_list_free(pfseq, NULL); return record; } static void session_free(void *data) { struct network_session *session = data; if (session->watch) g_source_remove(session->watch); if (session->io) g_io_channel_unref(session->io); g_free(session); } static void setup_destroy(void *user_data) { struct network_adapter *na = user_data; struct network_session *setup = na->setup; if (!setup) return; na->setup = NULL; session_free(setup); } static gboolean bnep_setup(GIOChannel *chan, GIOCondition cond, gpointer user_data) { const uint8_t bt_base[] = { 0x00, 0x00, 0x10, 0x00, 0x80, 0x00, 0x00, 0x80, 0x5F, 0x9B, 0x34, 0xFB }; struct network_adapter *na = user_data; struct network_server *ns; uint8_t packet[BNEP_MTU]; struct bnep_setup_conn_req *req = (void *) packet; uint16_t dst_role = 0; uint32_t val; int n, sk; char *bridge = NULL; if (cond & G_IO_NVAL) return FALSE; if (cond & (G_IO_ERR | G_IO_HUP)) { error("Hangup or error on BNEP socket"); return FALSE; } sk = g_io_channel_unix_get_fd(chan); /* * BNEP_SETUP_CONNECTION_REQUEST_MSG should be read and left in case * of kernel setup connection msg handling. */ n = recv(sk, packet, sizeof(packet), MSG_PEEK); if (n < 0) { error("read(): %s(%d)", strerror(errno), errno); return FALSE; } /* * Initial received data packet is BNEP_SETUP_CONNECTION_REQUEST_MSG * minimal size of this frame is 3 octets: 1 byte of BNEP Type + * 1 byte of BNEP Control Type + 1 byte of BNEP services UUID size. */ if (n < 3) { /* Added a response to the error control command * This packet reply to any control message received, * which contains an unknown BNEP control type value. */ if (req->type == BNEP_CONTROL) bnep_send_unkown_rsp(sk, req->ctrl); error("To few setup connection request data received"); return FALSE; } switch (req->uuid_size) { case 2: dst_role = get_be16(req->service); break; case 16: if (memcmp(&req->service[4], bt_base, sizeof(bt_base)) != 0) break; /* fall through */ case 4: val = get_be32(req->service); if (val > 0xffff) break; dst_role = val; break; default: break; } ns = find_server(na->servers, dst_role); if (!ns || !ns->record_id || !ns->bridge) error("Server error, bridge not initialized: (0x%x)", dst_role); else bridge = ns->bridge; strncpy(na->setup->dev, BNEP_INTERFACE, 16); na->setup->dev[15] = '\0'; if (bnep_server_add(sk, bridge, na->setup->dev, &na->setup->dst, packet, n) < 0) error("BNEP server cannot be added"); na->setup = NULL; return FALSE; } static void connect_event(GIOChannel *chan, GError *err, gpointer user_data) { struct network_adapter *na = user_data; if (err) { error("%s", err->message); setup_destroy(na); return; } g_io_channel_set_close_on_unref(chan, TRUE); na->setup->watch = g_io_add_watch_full(chan, G_PRIORITY_DEFAULT, G_IO_IN | G_IO_HUP | G_IO_ERR | G_IO_NVAL, bnep_setup, na, setup_destroy); } static void auth_cb(DBusError *derr, void *user_data) { struct network_adapter *na = user_data; GError *err = NULL; if (derr) { error("Access denied: %s", derr->message); goto reject; } if (!bt_io_accept(na->setup->io, connect_event, na, NULL, &err)) { error("bt_io_accept: %s", err->message); g_error_free(err); goto reject; } return; reject: g_io_channel_shutdown(na->setup->io, TRUE, NULL); setup_destroy(na); } static void confirm_event(GIOChannel *chan, gpointer user_data) { struct network_adapter *na = user_data; bdaddr_t src, dst; char address[18]; GError *err = NULL; guint ret; bt_io_get(chan, &err, BT_IO_OPT_SOURCE_BDADDR, &src, BT_IO_OPT_DEST_BDADDR, &dst, BT_IO_OPT_DEST, address, BT_IO_OPT_INVALID); if (err) { error("%s", err->message); g_error_free(err); goto drop; } DBG("BNEP: incoming connect from %s", address); if (na->setup) { error("Refusing connect from %s: setup in progress", address); goto drop; } if (!na->servers) goto drop; na->setup = g_new0(struct network_session, 1); bacpy(&na->setup->dst, &dst); na->setup->io = g_io_channel_ref(chan); ret = btd_request_authorization(&src, &dst, BNEP_SVC_UUID, auth_cb, na); if (ret == 0) { error("Refusing connect from %s", address); setup_destroy(na); goto drop; } return; drop: g_io_channel_shutdown(chan, TRUE, NULL); } int server_init(gboolean secure) { security = secure; return 0; } static uint32_t register_server_record(struct network_server *ns) { sdp_record_t *record; record = server_record_new(ns->name, ns->id); if (!record) { error("Unable to allocate new service record"); return 0; } if (adapter_service_add(ns->na->adapter, record) < 0) { error("Failed to register service record"); sdp_record_free(record); return 0; } DBG("got record id 0x%x", record->handle); return record->handle; } static void server_remove_sessions(struct network_server *ns) { GSList *list; for (list = ns->sessions; list; list = list->next) { struct network_session *session = list->data; if (*session->dev == '\0') continue; bnep_server_delete(ns->bridge, session->dev, &session->dst); } g_slist_free_full(ns->sessions, session_free); ns->sessions = NULL; } static void server_disconnect(DBusConnection *conn, void *user_data) { struct network_server *ns = user_data; server_remove_sessions(ns); ns->watch_id = 0; if (ns->record_id) { adapter_service_remove(ns->na->adapter, ns->record_id); ns->record_id = 0; } g_free(ns->bridge); ns->bridge = NULL; } static DBusMessage *register_server(DBusConnection *conn, DBusMessage *msg, void *data) { struct network_adapter *na = data; struct network_server *ns; DBusMessage *reply; const char *uuid, *bridge; if (!dbus_message_get_args(msg, NULL, DBUS_TYPE_STRING, &uuid, DBUS_TYPE_STRING, &bridge, DBUS_TYPE_INVALID)) return btd_error_invalid_args(msg); ns = find_server_by_uuid(na->servers, uuid); if (ns == NULL) return btd_error_failed(msg, "Invalid UUID"); if (ns->record_id) return btd_error_already_exists(msg); reply = dbus_message_new_method_return(msg); if (!reply) return NULL; ns->record_id = register_server_record(ns); if (!ns->record_id) return btd_error_failed(msg, "SDP record registration failed"); g_free(ns->bridge); ns->bridge = g_strdup(bridge); ns->watch_id = g_dbus_add_disconnect_watch(conn, dbus_message_get_sender(msg), server_disconnect, ns, NULL); return reply; } static DBusMessage *unregister_server(DBusConnection *conn, DBusMessage *msg, void *data) { struct network_adapter *na = data; struct network_server *ns; DBusMessage *reply; const char *uuid; if (!dbus_message_get_args(msg, NULL, DBUS_TYPE_STRING, &uuid, DBUS_TYPE_INVALID)) return btd_error_invalid_args(msg); ns = find_server_by_uuid(na->servers, uuid); if (!ns) return btd_error_failed(msg, "Invalid UUID"); reply = dbus_message_new_method_return(msg); if (!reply) return NULL; g_dbus_remove_watch(conn, ns->watch_id); server_disconnect(conn, ns); return reply; } static void adapter_free(struct network_adapter *na) { if (na->io != NULL) { g_io_channel_shutdown(na->io, TRUE, NULL); g_io_channel_unref(na->io); } setup_destroy(na); btd_adapter_unref(na->adapter); g_free(na); } static void server_free(void *data) { struct network_server *ns = data; if (!ns) return; server_remove_sessions(ns); if (ns->record_id) adapter_service_remove(ns->na->adapter, ns->record_id); g_dbus_remove_watch(btd_get_dbus_connection(), ns->watch_id); g_free(ns->name); g_free(ns->bridge); g_free(ns); } static void path_unregister(void *data) { struct network_adapter *na = data; DBG("Unregistered interface %s on path %s", NETWORK_SERVER_INTERFACE, adapter_get_path(na->adapter)); g_slist_free_full(na->servers, server_free); adapters = g_slist_remove(adapters, na); adapter_free(na); } static const GDBusMethodTable server_methods[] = { { GDBUS_METHOD("Register", GDBUS_ARGS({ "uuid", "s" }, { "bridge", "s" }), NULL, register_server) }, { GDBUS_METHOD("Unregister", GDBUS_ARGS({ "uuid", "s" }), NULL, unregister_server) }, { } }; static struct network_adapter *create_adapter(struct btd_adapter *adapter) { struct network_adapter *na; GError *err = NULL; na = g_new0(struct network_adapter, 1); na->adapter = btd_adapter_ref(adapter); na->io = bt_io_listen(NULL, confirm_event, na, NULL, &err, BT_IO_OPT_SOURCE_BDADDR, btd_adapter_get_address(adapter), BT_IO_OPT_PSM, BNEP_PSM, BT_IO_OPT_OMTU, BNEP_MTU, BT_IO_OPT_IMTU, BNEP_MTU, BT_IO_OPT_SEC_LEVEL, security ? BT_IO_SEC_MEDIUM : BT_IO_SEC_LOW, BT_IO_OPT_INVALID); if (!na->io) { error("%s", err->message); g_error_free(err); adapter_free(na); return NULL; } return na; } int server_register(struct btd_adapter *adapter, uint16_t id) { struct network_adapter *na; struct network_server *ns; const char *path; na = find_adapter(adapters, adapter); if (!na) { na = create_adapter(adapter); if (!na) return -EINVAL; adapters = g_slist_append(adapters, na); } ns = find_server(na->servers, id); if (ns) return 0; ns = g_new0(struct network_server, 1); ns->name = g_strdup("Network service"); path = adapter_get_path(adapter); if (g_slist_length(na->servers) > 0) goto done; if (!g_dbus_register_interface(btd_get_dbus_connection(), path, NETWORK_SERVER_INTERFACE, server_methods, NULL, NULL, na, path_unregister)) { error("D-Bus failed to register %s interface", NETWORK_SERVER_INTERFACE); server_free(ns); return -1; } DBG("Registered interface %s on path %s", NETWORK_SERVER_INTERFACE, path); done: bacpy(&ns->src, btd_adapter_get_address(adapter)); ns->id = id; ns->na = na; ns->record_id = 0; na->servers = g_slist_append(na->servers, ns); return 0; } int server_unregister(struct btd_adapter *adapter, uint16_t id) { struct network_adapter *na; struct network_server *ns; na = find_adapter(adapters, adapter); if (!na) return -EINVAL; ns = find_server(na->servers, id); if (!ns) return -EINVAL; na->servers = g_slist_remove(na->servers, ns); server_free(ns); if (g_slist_length(na->servers) > 0) return 0; g_dbus_unregister_interface(btd_get_dbus_connection(), adapter_get_path(adapter), NETWORK_SERVER_INTERFACE); return 0; } bluez-5.82/profiles/network/PaxHeaders/connection.h0000644000000000000000000000005014015011623017463 xustar0020 atime=1743516549 20 ctime=1743591284 bluez-5.82/profiles/network/connection.h0000644000000000000000000000062214015011623017144 0ustar00rootroot/* SPDX-License-Identifier: GPL-2.0-or-later */ /* * * BlueZ - Bluetooth protocol stack for Linux * * Copyright (C) 2004-2010 Marcel Holtmann * * */ int connection_register(struct btd_service *service); void connection_unregister(struct btd_service *service); int connection_connect(struct btd_service *service); int connection_disconnect(struct btd_service *service); bluez-5.82/profiles/network/PaxHeaders/manager.c0000644000000000000000000000005014015011623016731 xustar0020 atime=1743516549 20 ctime=1743591284 bluez-5.82/profiles/network/manager.c0000644000000000000000000001033214015011623016411 0ustar00rootroot// SPDX-License-Identifier: GPL-2.0-or-later /* * * BlueZ - Bluetooth protocol stack for Linux * * Copyright (C) 2004-2010 Marcel Holtmann * * */ #ifdef HAVE_CONFIG_H #include #endif #include #include #include #include "lib/bluetooth.h" #include "lib/bnep.h" #include "lib/sdp.h" #include "lib/uuid.h" #include "src/log.h" #include "src/plugin.h" #include "src/adapter.h" #include "src/device.h" #include "src/profile.h" #include "src/service.h" #include "bnep.h" #include "connection.h" #include "server.h" static gboolean conf_security = TRUE; static void read_config(const char *file) { GKeyFile *keyfile; GError *err = NULL; keyfile = g_key_file_new(); if (!g_key_file_load_from_file(keyfile, file, 0, &err)) { g_clear_error(&err); goto done; } conf_security = !g_key_file_get_boolean(keyfile, "General", "DisableSecurity", &err); if (err) { DBG("%s: %s", file, err->message); g_clear_error(&err); } done: g_key_file_free(keyfile); DBG("Config options: Security=%s", conf_security ? "true" : "false"); } static int panu_server_probe(struct btd_profile *p, struct btd_adapter *adapter) { const char *path = adapter_get_path(adapter); DBG("path %s", path); return server_register(adapter, BNEP_SVC_PANU); } static void panu_server_remove(struct btd_profile *p, struct btd_adapter *adapter) { const char *path = adapter_get_path(adapter); DBG("path %s", path); server_unregister(adapter, BNEP_SVC_PANU); } static int gn_server_probe(struct btd_profile *p, struct btd_adapter *adapter) { const char *path = adapter_get_path(adapter); DBG("path %s", path); return server_register(adapter, BNEP_SVC_GN); } static void gn_server_remove(struct btd_profile *p, struct btd_adapter *adapter) { const char *path = adapter_get_path(adapter); DBG("path %s", path); server_unregister(adapter, BNEP_SVC_GN); } static int nap_server_probe(struct btd_profile *p, struct btd_adapter *adapter) { const char *path = adapter_get_path(adapter); DBG("path %s", path); return server_register(adapter, BNEP_SVC_NAP); } static void nap_server_remove(struct btd_profile *p, struct btd_adapter *adapter) { const char *path = adapter_get_path(adapter); DBG("path %s", path); server_unregister(adapter, BNEP_SVC_NAP); } static struct btd_profile panu_profile = { .name = "network-panu", .local_uuid = NAP_UUID, .remote_uuid = PANU_UUID, .device_probe = connection_register, .device_remove = connection_unregister, .connect = connection_connect, .disconnect = connection_disconnect, .adapter_probe = panu_server_probe, .adapter_remove = panu_server_remove, }; static struct btd_profile gn_profile = { .name = "network-gn", .local_uuid = PANU_UUID, .remote_uuid = GN_UUID, .device_probe = connection_register, .device_remove = connection_unregister, .connect = connection_connect, .disconnect = connection_disconnect, .adapter_probe = gn_server_probe, .adapter_remove = gn_server_remove, }; static struct btd_profile nap_profile = { .name = "network-nap", .local_uuid = PANU_UUID, .remote_uuid = NAP_UUID, .device_probe = connection_register, .device_remove = connection_unregister, .connect = connection_connect, .disconnect = connection_disconnect, .adapter_probe = nap_server_probe, .adapter_remove = nap_server_remove, }; static int network_init(void) { int err; read_config(CONFIGDIR "/network.conf"); err = bnep_init(); if (err) { if (err == -EPROTONOSUPPORT) err = -ENOSYS; return err; } /* * There is one socket to handle the incoming connections. NAP, * GN and PANU servers share the same PSM. The initial BNEP message * (setup connection request) contains the destination service * field that defines which service the source is connecting to. */ if (server_init(conf_security) < 0) return -1; btd_profile_register(&panu_profile); btd_profile_register(&gn_profile); btd_profile_register(&nap_profile); return 0; } static void network_exit(void) { btd_profile_unregister(&panu_profile); btd_profile_unregister(&gn_profile); btd_profile_unregister(&nap_profile); bnep_cleanup(); } BLUETOOTH_PLUGIN_DEFINE(network, VERSION, BLUETOOTH_PLUGIN_PRIORITY_DEFAULT, network_init, network_exit) bluez-5.82/profiles/network/PaxHeaders/server.h0000644000000000000000000000005014015011623016632 xustar0020 atime=1743516549 20 ctime=1743591284 bluez-5.82/profiles/network/server.h0000644000000000000000000000053014015011623016311 0ustar00rootroot/* SPDX-License-Identifier: GPL-2.0-or-later */ /* * * BlueZ - Bluetooth protocol stack for Linux * * Copyright (C) 2004-2010 Marcel Holtmann * * */ int server_init(gboolean secure); int server_register(struct btd_adapter *adapter, uint16_t id); int server_unregister(struct btd_adapter *adapter, uint16_t id); bluez-5.82/profiles/network/PaxHeaders/bnep.c0000644000000000000000000000005014766002272016260 xustar0020 atime=1743515578 20 ctime=1743591279 bluez-5.82/profiles/network/bnep.c0000644000000000000000000003521314766002272015745 0ustar00rootroot// SPDX-License-Identifier: GPL-2.0-or-later /* * * BlueZ - Bluetooth protocol stack for Linux * * Copyright (C) 2004-2010 Marcel Holtmann * * */ #ifdef HAVE_CONFIG_H #include #endif #define _GNU_SOURCE #include #include #include #include #include #include #include #include #include #include #include #include "lib/bluetooth.h" #include "lib/l2cap.h" #include "lib/bnep.h" #include "lib/uuid.h" #include "src/log.h" #include "src/shared/timeout.h" #include "src/shared/util.h" #include "btio/btio.h" #include "bnep.h" #define CON_SETUP_RETRIES 3 #define CON_SETUP_TO 9 static int ctl; struct __service_16 { uint16_t dst; uint16_t src; } __attribute__ ((packed)); struct bnep { GIOChannel *io; uint16_t src; uint16_t dst; bdaddr_t dst_addr; char iface[16]; guint attempts; unsigned int setup_to; guint watch; bnep_connect_cb conn_cb; void *conn_data; bnep_disconnect_cb disconn_cb; void *disconn_data; }; int bnep_init(void) { ctl = socket(PF_BLUETOOTH, SOCK_RAW, BTPROTO_BNEP); if (ctl < 0) { int err = -errno; if (err == -EPROTONOSUPPORT) warn("kernel lacks bnep-protocol support"); else error("bnep: Failed to open control socket: %s (%d)", strerror(-err), -err); return err; } return 0; } int bnep_cleanup(void) { close(ctl); return 0; } static int bnep_conndel(const bdaddr_t *dst) { struct bnep_conndel_req req; memset(&req, 0, sizeof(req)); baswap((bdaddr_t *)&req.dst, dst); req.flags = 0; if (ioctl(ctl, BNEPCONNDEL, &req) < 0) { int err = -errno; error("bnep: Failed to kill connection: %s (%d)", strerror(-err), -err); return err; } return 0; } static int bnep_connadd(int sk, uint16_t role, char *dev) { struct bnep_connadd_req req; memset(&req, 0, sizeof(req)); strncpy(req.device, dev, 15); req.device[15] = '\0'; req.sock = sk; req.role = role; req.flags = (1 << BNEP_SETUP_RESPONSE); if (ioctl(ctl, BNEPCONNADD, &req) < 0) { int err = -errno; error("bnep: Failed to add device %s: %s(%d)", dev, strerror(-err), -err); return err; } strncpy(dev, req.device, 16); return 0; } static uint32_t bnep_getsuppfeat(void) { uint32_t feat; if (ioctl(ctl, BNEPGETSUPPFEAT, &feat) < 0) feat = 0; DBG("supported features: 0x%x", feat); return feat; } static int bnep_if_up(const char *devname) { struct ifreq ifr; int sk, err = 0; sk = socket(AF_INET, SOCK_DGRAM, 0); memset(&ifr, 0, sizeof(ifr)); strncpy(ifr.ifr_name, devname, IF_NAMESIZE - 1); ifr.ifr_flags |= IFF_UP; ifr.ifr_flags |= IFF_MULTICAST; if (ioctl(sk, SIOCSIFFLAGS, (void *) &ifr) < 0) { err = -errno; error("bnep: Could not bring up %s: %s(%d)", devname, strerror(-err), -err); } close(sk); return err; } static int bnep_if_down(const char *devname) { struct ifreq ifr; int sk, err = 0; sk = socket(AF_INET, SOCK_DGRAM, 0); memset(&ifr, 0, sizeof(ifr)); strncpy(ifr.ifr_name, devname, IF_NAMESIZE - 1); ifr.ifr_flags &= ~IFF_UP; /* Bring down the interface */ if (ioctl(sk, SIOCSIFFLAGS, (void *) &ifr) < 0) { err = -errno; error("bnep: Could not bring down %s: %s(%d)", devname, strerror(-err), -err); } close(sk); return err; } static gboolean bnep_watchdog_cb(GIOChannel *chan, GIOCondition cond, gpointer data) { struct bnep *session = data; if (session->disconn_cb) session->disconn_cb(session->disconn_data); return FALSE; } static gboolean bnep_setup_cb(GIOChannel *chan, GIOCondition cond, gpointer data) { struct bnep *session = data; struct bnep_control_rsp *rsp; struct timeval timeo; char pkt[BNEP_MTU]; ssize_t r; int sk; if (cond & G_IO_NVAL) return FALSE; if (session->setup_to > 0) { timeout_remove(session->setup_to); session->setup_to = 0; } if (cond & (G_IO_HUP | G_IO_ERR)) { error("bnep: Hangup or error on l2cap server socket"); goto failed; } sk = g_io_channel_unix_get_fd(chan); memset(pkt, 0, BNEP_MTU); r = read(sk, pkt, sizeof(pkt) - 1); if (r < 0) { error("bnep: IO Channel read error"); goto failed; } if (r == 0) { error("bnep: No packet received on l2cap socket"); goto failed; } errno = EPROTO; if ((size_t) r < sizeof(*rsp)) { error("bnep: Packet received is not bnep type"); goto failed; } rsp = (void *) pkt; if (rsp->type != BNEP_CONTROL) { error("bnep: Packet received is not bnep type"); goto failed; } if (rsp->ctrl != BNEP_SETUP_CONN_RSP) return TRUE; r = ntohs(rsp->resp); if (r != BNEP_SUCCESS) { error("bnep: failed"); goto failed; } memset(&timeo, 0, sizeof(timeo)); timeo.tv_sec = 0; if (setsockopt(sk, SOL_SOCKET, SO_RCVTIMEO, &timeo, sizeof(timeo)) < 0) { error("bnep: Set setsockopt failed: %s", strerror(errno)); goto failed; }; sk = g_io_channel_unix_get_fd(session->io); if (bnep_connadd(sk, session->src, session->iface) < 0) goto failed; if (bnep_if_up(session->iface) < 0) { bnep_conndel(&session->dst_addr); goto failed; } session->watch = g_io_add_watch(session->io, G_IO_ERR | G_IO_HUP | G_IO_NVAL, (GIOFunc) bnep_watchdog_cb, session); g_io_channel_unref(session->io); session->io = NULL; session->conn_cb(session->iface, 0, session->conn_data); return FALSE; failed: session->conn_cb(NULL, -EIO, session->conn_data); return FALSE; } static int bnep_setup_conn_req(struct bnep *session) { struct bnep_setup_conn_req *req; struct __service_16 *s; unsigned char pkt[BNEP_MTU]; int fd; /* Send request */ req = (void *) pkt; req->type = BNEP_CONTROL; req->ctrl = BNEP_SETUP_CONN_REQ; req->uuid_size = 2; /* 16bit UUID */ s = (void *) req->service; s->src = htons(session->src); s->dst = htons(session->dst); fd = g_io_channel_unix_get_fd(session->io); if (write(fd, pkt, sizeof(*req) + sizeof(*s)) < 0) { error("bnep: connection req send failed: %s", strerror(errno)); return -errno; } session->attempts++; return 0; } static bool bnep_conn_req_to(gpointer user_data) { struct bnep *session = user_data; if (session->attempts == CON_SETUP_RETRIES) { error("bnep: Too many bnep connection attempts"); } else { error("bnep: connection setup TO, retrying..."); if (bnep_setup_conn_req(session) == 0) return TRUE; } session->conn_cb(NULL, -ETIMEDOUT, session->conn_data); return FALSE; } struct bnep *bnep_new(int sk, uint16_t local_role, uint16_t remote_role, char *iface) { struct bnep *session; int dup_fd; dup_fd = dup(sk); if (dup_fd < 0) return NULL; session = g_new0(struct bnep, 1); session->io = g_io_channel_unix_new(dup_fd); session->src = local_role; session->dst = remote_role; strncpy(session->iface, iface, 15); session->iface[15] = '\0'; g_io_channel_set_close_on_unref(session->io, TRUE); session->watch = g_io_add_watch(session->io, G_IO_IN | G_IO_ERR | G_IO_HUP | G_IO_NVAL, (GIOFunc) bnep_setup_cb, session); return session; } void bnep_free(struct bnep *session) { if (!session) return; if (session->io) { g_io_channel_shutdown(session->io, FALSE, NULL); g_io_channel_unref(session->io); session->io = NULL; } if (session->watch > 0) { g_source_remove(session->watch); session->watch = 0; } g_free(session); } int bnep_connect(struct bnep *session, bnep_connect_cb conn_cb, bnep_disconnect_cb disconn_cb, void *conn_data, void *disconn_data) { GError *gerr = NULL; int err; if (!session || !conn_cb || !disconn_cb) return -EINVAL; session->attempts = 0; session->conn_cb = conn_cb; session->disconn_cb = disconn_cb; session->conn_data = conn_data; session->disconn_data = disconn_data; bt_io_get(session->io, &gerr, BT_IO_OPT_DEST_BDADDR, &session->dst_addr, BT_IO_OPT_INVALID); if (gerr) { error("bnep: connect failed: %s", gerr->message); g_error_free(gerr); return -EINVAL; } err = bnep_setup_conn_req(session); if (err < 0) return err; session->setup_to = timeout_add_seconds(CON_SETUP_TO, bnep_conn_req_to, session, NULL); return 0; } void bnep_disconnect(struct bnep *session) { if (!session) return; if (session->watch > 0) { g_source_remove(session->watch); session->watch = 0; } if (session->io) { g_io_channel_unref(session->io); session->io = NULL; } bnep_if_down(session->iface); bnep_conndel(&session->dst_addr); } static int bnep_add_to_bridge(const char *devname, const char *bridge) { int ifindex; struct ifreq ifr; int sk, err = 0; if (!devname || !bridge) return -EINVAL; ifindex = if_nametoindex(devname); sk = socket(AF_INET, SOCK_STREAM, 0); if (sk < 0) return -1; memset(&ifr, 0, sizeof(ifr)); strncpy(ifr.ifr_name, bridge, IFNAMSIZ - 1); ifr.ifr_ifindex = ifindex; if (ioctl(sk, SIOCBRADDIF, &ifr) < 0) { err = -errno; error("bnep: Can't add %s to the bridge %s: %s(%d)", devname, bridge, strerror(-err), -err); } else { info("bnep: bridge %s: interface %s added", bridge, devname); } close(sk); return err; } static int bnep_del_from_bridge(const char *devname, const char *bridge) { int ifindex; struct ifreq ifr; int sk, err = 0; if (!devname || !bridge) return -EINVAL; ifindex = if_nametoindex(devname); sk = socket(AF_INET, SOCK_STREAM, 0); if (sk < 0) return -1; memset(&ifr, 0, sizeof(ifr)); strncpy(ifr.ifr_name, bridge, IFNAMSIZ - 1); ifr.ifr_ifindex = ifindex; if (ioctl(sk, SIOCBRDELIF, &ifr) < 0) { err = -errno; error("bnep: Can't delete %s from the bridge %s: %s(%d)", devname, bridge, strerror(-err), -err); } else { info("bnep: bridge %s: interface %s removed", bridge, devname); } close(sk); return err; } static ssize_t bnep_send_ctrl_rsp(int sk, uint8_t ctrl, uint16_t resp) { ssize_t sent; switch (ctrl) { case BNEP_CMD_NOT_UNDERSTOOD: { struct bnep_ctrl_cmd_not_understood_cmd rsp; rsp.type = BNEP_CONTROL; rsp.ctrl = ctrl; rsp.unkn_ctrl = (uint8_t) resp; sent = send(sk, &rsp, sizeof(rsp), 0); break; } case BNEP_FILTER_MULT_ADDR_RSP: case BNEP_FILTER_NET_TYPE_RSP: case BNEP_SETUP_CONN_RSP: { struct bnep_control_rsp rsp; rsp.type = BNEP_CONTROL; rsp.ctrl = ctrl; rsp.resp = htons(resp); sent = send(sk, &rsp, sizeof(rsp), 0); break; } default: error("bnep: wrong response type"); sent = -1; break; } return sent; } static uint16_t bnep_setup_decode(int sk, struct bnep_setup_conn_req *req, uint16_t *dst) { const uint8_t bt_base[] = { 0x00, 0x00, 0x10, 0x00, 0x80, 0x00, 0x00, 0x80, 0x5F, 0x9B, 0x34, 0xFB }; uint16_t src; uint8_t *dest, *source; uint32_t val; if (((req->type != BNEP_CONTROL) && (req->type != (BNEP_CONTROL | BNEP_EXT_HEADER))) || req->ctrl != BNEP_SETUP_CONN_REQ) return BNEP_CONN_NOT_ALLOWED; dest = req->service; source = req->service + req->uuid_size; switch (req->uuid_size) { case 2: /* UUID16 */ *dst = get_be16(dest); src = get_be16(source); break; case 16: /* UUID128 */ /* Check that the bytes in the UUID, except the service ID * itself, are correct. The service ID is checked in * bnep_setup_chk(). */ if (memcmp(&dest[4], bt_base, sizeof(bt_base)) != 0) return BNEP_CONN_INVALID_DST; if (memcmp(&source[4], bt_base, sizeof(bt_base)) != 0) return BNEP_CONN_INVALID_SRC; /* fall through */ case 4: /* UUID32 */ val = get_be32(dest); if (val > 0xffff) return BNEP_CONN_INVALID_DST; *dst = val; val = get_be32(source); if (val > 0xffff) return BNEP_CONN_INVALID_SRC; src = val; break; default: return BNEP_CONN_INVALID_SVC; } /* Allowed PAN Profile scenarios */ switch (*dst) { case BNEP_SVC_NAP: case BNEP_SVC_GN: if (src == BNEP_SVC_PANU) return BNEP_SUCCESS; return BNEP_CONN_INVALID_SRC; case BNEP_SVC_PANU: if (src == BNEP_SVC_PANU || src == BNEP_SVC_GN || src == BNEP_SVC_NAP) return BNEP_SUCCESS; return BNEP_CONN_INVALID_SRC; } return BNEP_CONN_INVALID_DST; } static int bnep_server_add_legacy(int sk, uint16_t dst, char *bridge, char *iface, const bdaddr_t *addr, uint8_t *setup_data, int len) { int err, n; uint16_t rsp; n = read(sk, setup_data, len); if (n != len) { err = -EIO; rsp = BNEP_CONN_NOT_ALLOWED; goto reply; } err = bnep_connadd(sk, dst, iface); if (err < 0) { rsp = BNEP_CONN_NOT_ALLOWED; goto reply; } err = bnep_add_to_bridge(iface, bridge); if (err < 0) { bnep_conndel(addr); rsp = BNEP_CONN_NOT_ALLOWED; goto reply; } err = bnep_if_up(iface); if (err < 0) { bnep_del_from_bridge(iface, bridge); bnep_conndel(addr); rsp = BNEP_CONN_NOT_ALLOWED; goto reply; } rsp = BNEP_SUCCESS; reply: if (bnep_send_ctrl_rsp(sk, BNEP_SETUP_CONN_RSP, rsp) < 0) { err = -errno; error("bnep: send ctrl rsp error: %s (%d)", strerror(-err), -err); } return err; } int bnep_server_add(int sk, char *bridge, char *iface, const bdaddr_t *addr, uint8_t *setup_data, int len) { int err; uint32_t feat; uint16_t rsp, dst; struct bnep_setup_conn_req *req = (void *) setup_data; /* Highest known Control command ID * is BNEP_FILTER_MULT_ADDR_RSP = 0x06 */ if (req->type == BNEP_CONTROL && req->ctrl > BNEP_FILTER_MULT_ADDR_RSP) { error("bnep: cmd not understood"); err = bnep_send_ctrl_rsp(sk, BNEP_CMD_NOT_UNDERSTOOD, req->ctrl); if (err < 0) error("send not understood ctrl rsp error: %s (%d)", strerror(errno), errno); return err; } /* Processing BNEP_SETUP_CONNECTION_REQUEST_MSG */ rsp = bnep_setup_decode(sk, req, &dst); if (rsp != BNEP_SUCCESS) { err = -rsp; error("bnep: error while decoding setup connection request: %d", rsp); goto failed; } feat = bnep_getsuppfeat(); /* * Take out setup data if kernel doesn't support handling it, especially * setup request. If kernel would have set session flags, they should * be checked and handled respectively. */ if (!feat || !(feat & (1 << BNEP_SETUP_RESPONSE))) return bnep_server_add_legacy(sk, dst, bridge, iface, addr, setup_data, len); err = bnep_connadd(sk, dst, iface); if (err < 0) { rsp = BNEP_CONN_NOT_ALLOWED; goto failed; } err = bnep_add_to_bridge(iface, bridge); if (err < 0) goto failed_conn; err = bnep_if_up(iface); if (err < 0) goto failed_bridge; return 0; failed_bridge: bnep_del_from_bridge(iface, bridge); failed_conn: bnep_conndel(addr); return err; failed: if (bnep_send_ctrl_rsp(sk, BNEP_SETUP_CONN_RSP, rsp) < 0) { err = -errno; error("bnep: send ctrl rsp error: %s (%d)", strerror(-err), -err); } return err; } void bnep_server_delete(char *bridge, char *iface, const bdaddr_t *addr) { if (!bridge || !iface || !addr) return; bnep_del_from_bridge(iface, bridge); bnep_if_down(iface); bnep_conndel(addr); } int bnep_send_unkown_rsp(int sk, uint16_t resp) { return bnep_send_ctrl_rsp(sk, BNEP_CMD_NOT_UNDERSTOOD, resp); } bluez-5.82/profiles/network/PaxHeaders/network.conf0000644000000000000000000000005012066112417017522 xustar0020 atime=1743516875 20 ctime=1743591288 bluez-5.82/profiles/network/network.conf0000644000000000000000000000017012066112417017201 0ustar00rootroot# Configuration file for the network service [General] # Disable link encryption: default=false #DisableSecurity=true bluez-5.82/profiles/PaxHeaders/iap0000644000000000000000000000005014773213563014200 xustar0020 atime=1743591291 20 ctime=1743591283 bluez-5.82/profiles/iap/0000755000000000000000000000000014773213563013736 5ustar00rootrootbluez-5.82/profiles/iap/PaxHeaders/main.c0000644000000000000000000000005014572354773015353 xustar0020 atime=1743516281 20 ctime=1743591283 bluez-5.82/profiles/iap/main.c0000644000000000000000000002503514572354773015041 0ustar00rootroot// SPDX-License-Identifier: GPL-2.0-or-later /* * * BlueZ - Bluetooth protocol stack for Linux * * Copyright (C) 2012 Marcel Holtmann * * */ #ifdef HAVE_CONFIG_H #include #endif #define _GNU_SOURCE #include #include #include #include #include #include #include #include #include #include "gdbus/gdbus.h" #define IAP_PATH "/org/bluez/iap" #define IAP_UUID "00000000-deca-fade-deca-deafdecacafe" #define IAP_RECORD \ " \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ " static GMainLoop *main_loop; static guint iap_source = 0; static gboolean iap_handler(GIOChannel *channel, GIOCondition condition, gpointer user_data) { unsigned char buf[512]; ssize_t len; int fd; if (condition & (G_IO_NVAL | G_IO_ERR | G_IO_HUP)) { iap_source = 0; return FALSE; } fd = g_io_channel_unix_get_fd(channel); len = read(fd, buf, sizeof(buf)); if (len < 0) { iap_source = 0; return FALSE; } return TRUE; } static guint create_source(int fd, GIOFunc func) { GIOChannel *channel; guint source; channel = g_io_channel_unix_new(fd); g_io_channel_set_close_on_unref(channel, TRUE); g_io_channel_set_encoding(channel, NULL, NULL); g_io_channel_set_buffered(channel, FALSE); source = g_io_add_watch(channel, G_IO_IN | G_IO_HUP | G_IO_ERR | G_IO_NVAL, func, NULL); g_io_channel_unref(channel); return source; } static void remove_source(const char *path) { if (iap_source > 0) { g_source_remove(iap_source); iap_source = 0; } } static DBusMessage *release_profile(DBusConnection *conn, DBusMessage *msg, void *user_data) { g_print("Profile released\n"); remove_source(IAP_PATH); return dbus_message_new_method_return(msg); } static DBusMessage *new_connection(DBusConnection *conn, DBusMessage *msg, void *user_data) { const char *path, *device; int fd; g_print("New connection\n"); path = dbus_message_get_path(msg); dbus_message_get_args(msg, NULL, DBUS_TYPE_OBJECT_PATH, &device, DBUS_TYPE_UNIX_FD, &fd, DBUS_TYPE_INVALID); g_print(" from %s\n", path); g_print(" for device %s with fd %d\n", device, fd); if (iap_source == 0) iap_source = create_source(fd, iap_handler); else close(fd); return dbus_message_new_method_return(msg); } static DBusMessage *request_disconnection(DBusConnection *conn, DBusMessage *msg, void *user_data) { DBusMessageIter iter; const char *path, *device; g_print("Request disconnection\n"); path = dbus_message_get_path(msg); dbus_message_iter_init(msg, &iter); dbus_message_iter_get_basic(&iter, &device); g_print(" from %s\n", path); g_print(" for device %s\n", device); remove_source(path); return dbus_message_new_method_return(msg); } static DBusMessage *cancel_request(DBusConnection *conn, DBusMessage *msg, void *user_data) { const char *path; g_print("Request canceled\n"); path = dbus_message_get_path(msg); g_print(" from %s\n", path); remove_source(path); return dbus_message_new_method_return(msg); } static const GDBusMethodTable methods[] = { { GDBUS_METHOD("Release", NULL, NULL, release_profile) }, { GDBUS_METHOD("NewConnection", GDBUS_ARGS({ "device", "o" }, { "fd", "h"}, { "opts", "a{sv}"}), NULL, new_connection) }, { GDBUS_METHOD("RequestDisconnection", GDBUS_ARGS({ "device", "o" }), NULL, request_disconnection) }, { GDBUS_METHOD("Cancel", NULL, NULL, cancel_request) }, { } }; static void register_profile_setup(DBusMessageIter *iter, void *user_data) { const char *path = IAP_PATH; const char *uuid = IAP_UUID; DBusMessageIter dict, entry, value; dbus_uint16_t channel; char *record; const char *str; dbus_message_iter_append_basic(iter, DBUS_TYPE_OBJECT_PATH, &path); dbus_message_iter_append_basic(iter, DBUS_TYPE_STRING, &uuid); dbus_message_iter_open_container(iter, DBUS_TYPE_ARRAY, DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING DBUS_TYPE_STRING_AS_STRING DBUS_TYPE_VARIANT_AS_STRING DBUS_DICT_ENTRY_END_CHAR_AS_STRING, &dict); dbus_message_iter_open_container(&dict, DBUS_TYPE_DICT_ENTRY, NULL, &entry); str = "Role"; dbus_message_iter_append_basic(&entry, DBUS_TYPE_STRING, &str); dbus_message_iter_open_container(&entry, DBUS_TYPE_VARIANT, DBUS_TYPE_STRING_AS_STRING, &value); str = "server"; dbus_message_iter_append_basic(&value, DBUS_TYPE_STRING, &str); dbus_message_iter_close_container(&entry, &value); dbus_message_iter_close_container(&dict, &entry); dbus_message_iter_open_container(&dict, DBUS_TYPE_DICT_ENTRY, NULL, &entry); str = "Channel"; dbus_message_iter_append_basic(&entry, DBUS_TYPE_STRING, &str); dbus_message_iter_open_container(&entry, DBUS_TYPE_VARIANT, DBUS_TYPE_UINT16_AS_STRING, &value); channel = 23; dbus_message_iter_append_basic(&value, DBUS_TYPE_UINT16, &channel); dbus_message_iter_close_container(&entry, &value); dbus_message_iter_close_container(&dict, &entry); dbus_message_iter_open_container(&dict, DBUS_TYPE_DICT_ENTRY, NULL, &entry); str = "ServiceRecord"; dbus_message_iter_append_basic(&entry, DBUS_TYPE_STRING, &str); dbus_message_iter_open_container(&entry, DBUS_TYPE_VARIANT, DBUS_TYPE_STRING_AS_STRING, &value); record = g_strdup_printf(IAP_RECORD, IAP_UUID, channel); dbus_message_iter_append_basic(&value, DBUS_TYPE_STRING, &record); g_free(record); dbus_message_iter_close_container(&entry, &value); dbus_message_iter_close_container(&dict, &entry); dbus_message_iter_close_container(iter, &dict); } static void register_profile_reply(DBusMessage *message, void *user_data) { DBusError error; dbus_error_init(&error); if (dbus_set_error_from_message(&error, message) == TRUE) { g_print("Failed to register profile\n"); return; } g_print("Profile registered\n"); } static void connect_handler(DBusConnection *connection, void *user_data) { GDBusClient *client = user_data; GDBusProxy *proxy; g_print("Bluetooth connected\n"); proxy = g_dbus_proxy_new(client, "/org/bluez", "org.bluez.ProfileManager1"); if (!proxy) return; g_dbus_register_interface(connection, IAP_PATH, "org.bluez.Profile1", methods, NULL, NULL, NULL, NULL); g_dbus_proxy_method_call(proxy, "RegisterProfile", register_profile_setup, register_profile_reply, NULL, NULL); g_dbus_proxy_unref(proxy); } static void disconnect_handler(DBusConnection *connection, void *user_data) { g_print("Bluetooth disconnected\n"); g_dbus_unregister_interface(connection, IAP_PATH, "org.bluez.Profile1"); } static gboolean signal_handler(GIOChannel *channel, GIOCondition condition, gpointer user_data) { static unsigned int __terminated = 0; struct signalfd_siginfo si; ssize_t result; int fd; if (condition & (G_IO_NVAL | G_IO_ERR | G_IO_HUP)) { g_main_loop_quit(main_loop); return FALSE; } fd = g_io_channel_unix_get_fd(channel); result = read(fd, &si, sizeof(si)); if (result != sizeof(si)) return FALSE; switch (si.ssi_signo) { case SIGINT: case SIGTERM: if (__terminated == 0) g_main_loop_quit(main_loop); __terminated = 1; break; } return TRUE; } static guint setup_signalfd(void) { GIOChannel *channel; guint source; sigset_t mask; int fd; sigemptyset(&mask); sigaddset(&mask, SIGINT); sigaddset(&mask, SIGTERM); if (sigprocmask(SIG_BLOCK, &mask, NULL) < 0) { perror("Failed to set signal mask"); return 0; } fd = signalfd(-1, &mask, 0); if (fd < 0) { perror("Failed to create signal descriptor"); return 0; } channel = g_io_channel_unix_new(fd); g_io_channel_set_close_on_unref(channel, TRUE); g_io_channel_set_encoding(channel, NULL, NULL); g_io_channel_set_buffered(channel, FALSE); source = g_io_add_watch(channel, G_IO_IN | G_IO_HUP | G_IO_ERR | G_IO_NVAL, signal_handler, NULL); g_io_channel_unref(channel); return source; } static gboolean option_version = FALSE; static const GOptionEntry options[] = { { "version", 'v', 0, G_OPTION_ARG_NONE, &option_version, "Show version information and exit" }, { NULL }, }; int main(int argc, char *argv[]) { GOptionContext *context; GError *error = NULL; DBusConnection *dbus_conn; GDBusClient *client; guint signal; context = g_option_context_new(NULL); g_option_context_add_main_entries(context, options, NULL); if (g_option_context_parse(context, &argc, &argv, &error) == FALSE) { if (error != NULL) { g_printerr("%s\n", error->message); g_error_free(error); } else g_printerr("An unknown error occurred\n"); exit(1); } g_option_context_free(context); if (option_version == TRUE) { g_print("%s\n", VERSION); exit(0); } main_loop = g_main_loop_new(NULL, FALSE); dbus_conn = g_dbus_setup_bus(DBUS_BUS_SYSTEM, NULL, NULL); signal = setup_signalfd(); client = g_dbus_client_new(dbus_conn, "org.bluez", "/org/bluez"); g_dbus_client_set_connect_watch(client, connect_handler, client); g_dbus_client_set_disconnect_watch(client, disconnect_handler, NULL); g_main_loop_run(main_loop); g_dbus_client_unref(client); g_source_remove(signal); dbus_connection_unref(dbus_conn); g_main_loop_unref(main_loop); return 0; } bluez-5.82/profiles/PaxHeaders/health0000644000000000000000000000005014773213564014675 xustar0020 atime=1743591291 20 ctime=1743591284 bluez-5.82/profiles/health/0000755000000000000000000000000014773213564014433 5ustar00rootrootbluez-5.82/profiles/health/PaxHeaders/hdp_main.c0000644000000000000000000000005014015011623016652 xustar0020 atime=1743516870 20 ctime=1743591284 bluez-5.82/profiles/health/hdp_main.c0000644000000000000000000000102514015011623016331 0ustar00rootroot// SPDX-License-Identifier: GPL-2.0-or-later /* * * BlueZ - Bluetooth protocol stack for Linux * * Copyright (C) 2010 GSyC/LibreSoft, Universidad Rey Juan Carlos. * */ #ifdef HAVE_CONFIG_H #include #endif #include #include "gdbus/gdbus.h" #include "src/plugin.h" #include "hdp_manager.h" static int hdp_init(void) { return hdp_manager_init(); } static void hdp_exit(void) { hdp_manager_exit(); } BLUETOOTH_PLUGIN_DEFINE(health, VERSION, BLUETOOTH_PLUGIN_PRIORITY_DEFAULT, hdp_init, hdp_exit) bluez-5.82/profiles/health/PaxHeaders/hdp.h0000644000000000000000000000005014015011623015653 xustar0020 atime=1743516870 20 ctime=1743591284 bluez-5.82/profiles/health/hdp.h0000644000000000000000000000102714015011623015334 0ustar00rootroot/* SPDX-License-Identifier: GPL-2.0-or-later */ /* * * BlueZ - Bluetooth protocol stack for Linux * * Copyright (C) 2010 GSyC/LibreSoft, Universidad Rey Juan Carlos. * */ int hdp_adapter_register(struct btd_adapter *btd_adapter); void hdp_adapter_unregister(struct btd_adapter *btd_adapter); int hdp_device_register(struct btd_device *device); void hdp_device_unregister(struct btd_device *device); int hdp_manager_start(void); void hdp_manager_stop(void); gboolean hdp_set_mcl_cb(struct hdp_device *device, GError **err); bluez-5.82/profiles/health/PaxHeaders/mcap.h0000644000000000000000000000005014131623652016032 xustar0020 atime=1743516242 20 ctime=1743591279 bluez-5.82/profiles/health/mcap.h0000644000000000000000000003014614131623652015517 0ustar00rootroot/* SPDX-License-Identifier: GPL-2.0-or-later */ /* * * BlueZ - Bluetooth protocol stack for Linux * * Copyright (C) 2010 GSyC/LibreSoft, Universidad Rey Juan Carlos. * Copyright (C) 2010 Signove * Copyright (C) 2014 Intel Corporation. All rights reserved. * */ #define MCAP_VERSION 0x0100 /* current version 01.00 */ /* bytes to get MCAP Supported Procedures */ #define MCAP_SUP_PROC 0x06 /* maximum transmission unit for channels */ #define MCAP_CC_MTU 48 #define MCAP_DC_MTU 65535 /* MCAP Standard Op Codes */ #define MCAP_ERROR_RSP 0x00 #define MCAP_MD_CREATE_MDL_REQ 0x01 #define MCAP_MD_CREATE_MDL_RSP 0x02 #define MCAP_MD_RECONNECT_MDL_REQ 0x03 #define MCAP_MD_RECONNECT_MDL_RSP 0x04 #define MCAP_MD_ABORT_MDL_REQ 0x05 #define MCAP_MD_ABORT_MDL_RSP 0x06 #define MCAP_MD_DELETE_MDL_REQ 0x07 #define MCAP_MD_DELETE_MDL_RSP 0x08 /* MCAP Clock Sync Op Codes */ #define MCAP_MD_SYNC_CAP_REQ 0x11 #define MCAP_MD_SYNC_CAP_RSP 0x12 #define MCAP_MD_SYNC_SET_REQ 0x13 #define MCAP_MD_SYNC_SET_RSP 0x14 #define MCAP_MD_SYNC_INFO_IND 0x15 /* MCAP Response codes */ #define MCAP_SUCCESS 0x00 #define MCAP_INVALID_OP_CODE 0x01 #define MCAP_INVALID_PARAM_VALUE 0x02 #define MCAP_INVALID_MDEP 0x03 #define MCAP_MDEP_BUSY 0x04 #define MCAP_INVALID_MDL 0x05 #define MCAP_MDL_BUSY 0x06 #define MCAP_INVALID_OPERATION 0x07 #define MCAP_RESOURCE_UNAVAILABLE 0x08 #define MCAP_UNSPECIFIED_ERROR 0x09 #define MCAP_REQUEST_NOT_SUPPORTED 0x0A #define MCAP_CONFIGURATION_REJECTED 0x0B /* MDL IDs */ #define MCAP_MDLID_RESERVED 0x0000 #define MCAP_MDLID_INITIAL 0x0001 #define MCAP_MDLID_FINAL 0xFEFF #define MCAP_ALL_MDLIDS 0xFFFF /* MDEP IDs */ #define MCAP_MDEPID_INITIAL 0x00 #define MCAP_MDEPID_FINAL 0x7F /* CSP special values */ #define MCAP_BTCLOCK_IMMEDIATE 0xffffffffUL #define MCAP_TMSTAMP_DONTSET 0xffffffffffffffffULL #define MCAP_BTCLOCK_MAX 0x0fffffff #define MCAP_BTCLOCK_FIELD (MCAP_BTCLOCK_MAX + 1) #define MCAP_CTRL_CACHED 0x01 /* MCL is cached */ #define MCAP_CTRL_STD_OP 0x02 /* Support for standard op codes */ #define MCAP_CTRL_SYNC_OP 0x04 /* Support for synchronization commands */ #define MCAP_CTRL_CONN 0x08 /* MCL is in connecting process */ #define MCAP_CTRL_FREE 0x10 /* MCL is marked as releasable */ #define MCAP_CTRL_NOCACHE 0x20 /* MCL is marked as not cacheable */ /* * MCAP Request Packet Format */ typedef struct { uint8_t op; uint16_t mdl; uint8_t mdep; uint8_t conf; } __attribute__ ((packed)) mcap_md_create_mdl_req; typedef struct { uint8_t op; uint16_t mdl; } __attribute__ ((packed)) mcap_md_req; /* MCAP Response Packet Format */ typedef struct { uint8_t op; uint8_t rc; uint16_t mdl; uint8_t data[0]; } __attribute__ ((packed)) mcap_rsp; /* MCAP Clock Synchronization Protocol */ typedef struct { uint8_t op; uint16_t timest; } __attribute__ ((packed)) mcap_md_sync_cap_req; typedef struct { uint8_t op; uint8_t rc; } __attribute__ ((packed)) mcap_md_sync_rsp; typedef struct { uint8_t op; uint8_t rc; uint8_t btclock; uint16_t sltime; uint16_t timestnr; uint16_t timestna; } __attribute__ ((packed)) mcap_md_sync_cap_rsp; typedef struct { uint8_t op; uint8_t timestui; uint32_t btclock; uint64_t timestst; } __attribute__ ((packed)) mcap_md_sync_set_req; typedef struct { int8_t op; uint8_t rc; uint32_t btclock; uint64_t timestst; uint16_t timestsa; } __attribute__ ((packed)) mcap_md_sync_set_rsp; typedef struct { uint8_t op; uint32_t btclock; uint64_t timestst; uint16_t timestsa; } __attribute__ ((packed)) mcap_md_sync_info_ind; typedef enum { /* MCAP Error Response Codes */ MCAP_ERROR_INVALID_OP_CODE = 1, MCAP_ERROR_INVALID_PARAM_VALUE, MCAP_ERROR_INVALID_MDEP, MCAP_ERROR_MDEP_BUSY, MCAP_ERROR_INVALID_MDL, MCAP_ERROR_MDL_BUSY, MCAP_ERROR_INVALID_OPERATION, MCAP_ERROR_RESOURCE_UNAVAILABLE, MCAP_ERROR_UNSPECIFIED_ERROR, MCAP_ERROR_REQUEST_NOT_SUPPORTED, MCAP_ERROR_CONFIGURATION_REJECTED, /* MCAP Internal Errors */ MCAP_ERROR_INVALID_ARGS, MCAP_ERROR_ALREADY_EXISTS, MCAP_ERROR_REQ_IGNORED, MCAP_ERROR_MCL_CLOSED, MCAP_ERROR_FAILED } McapError; typedef enum { MCAP_MDL_CB_INVALID, MCAP_MDL_CB_CONNECTED, /* mcap_mdl_event_cb */ MCAP_MDL_CB_CLOSED, /* mcap_mdl_event_cb */ MCAP_MDL_CB_DELETED, /* mcap_mdl_event_cb */ MCAP_MDL_CB_ABORTED, /* mcap_mdl_event_cb */ MCAP_MDL_CB_REMOTE_CONN_REQ, /* mcap_remote_mdl_conn_req_cb */ MCAP_MDL_CB_REMOTE_RECONN_REQ /* mcap_remote_mdl_reconn_req_cb */ } McapMclCb; typedef enum { MCL_CONNECTED, MCL_PENDING, MCL_ACTIVE, MCL_IDLE } MCLState; typedef enum { MCL_ACCEPTOR, MCL_INITIATOR } MCLRole; typedef enum { MCL_AVAILABLE, MCL_WAITING_RSP } MCAPCtrl; typedef enum { MDL_WAITING, MDL_CONNECTED, MDL_DELETING, MDL_CLOSED } MDLState; struct mcap_csp; struct mcap_mdl_op_cb; struct mcap_instance; struct mcap_mcl; struct mcap_mdl; struct sync_info_ind_data; /************ Callbacks ************/ /* MDL callbacks */ typedef void (* mcap_mdl_event_cb) (struct mcap_mdl *mdl, gpointer data); typedef void (* mcap_mdl_operation_conf_cb) (struct mcap_mdl *mdl, uint8_t conf, GError *err, gpointer data); typedef void (* mcap_mdl_operation_cb) (struct mcap_mdl *mdl, GError *err, gpointer data); typedef void (* mcap_mdl_notify_cb) (GError *err, gpointer data); /* Next function should return an MCAP appropriate response code */ typedef uint8_t (* mcap_remote_mdl_conn_req_cb) (struct mcap_mcl *mcl, uint8_t mdepid, uint16_t mdlid, uint8_t *conf, gpointer data); typedef uint8_t (* mcap_remote_mdl_reconn_req_cb) (struct mcap_mdl *mdl, gpointer data); /* MCL callbacks */ typedef void (* mcap_mcl_event_cb) (struct mcap_mcl *mcl, gpointer data); typedef void (* mcap_mcl_connect_cb) (struct mcap_mcl *mcl, GError *err, gpointer data); /* CSP callbacks */ typedef void (* mcap_info_ind_event_cb) (struct mcap_mcl *mcl, struct sync_info_ind_data *data); typedef void (* mcap_sync_cap_cb) (struct mcap_mcl *mcl, uint8_t mcap_err, uint8_t btclockres, uint16_t synclead, uint16_t tmstampres, uint16_t tmstampacc, GError *err, gpointer data); typedef void (* mcap_sync_set_cb) (struct mcap_mcl *mcl, uint8_t mcap_err, uint32_t btclock, uint64_t timestamp, uint16_t accuracy, GError *err, gpointer data); struct mcap_mdl_cb { mcap_mdl_event_cb mdl_connected; /* Remote device has created a MDL */ mcap_mdl_event_cb mdl_closed; /* Remote device has closed a MDL */ mcap_mdl_event_cb mdl_deleted; /* Remote device requested deleting a MDL */ mcap_mdl_event_cb mdl_aborted; /* Remote device aborted the mdl creation */ mcap_remote_mdl_conn_req_cb mdl_conn_req; /* Remote device requested creating a MDL */ mcap_remote_mdl_reconn_req_cb mdl_reconn_req; /* Remote device requested reconnecting a MDL */ gpointer user_data; /* User data */ }; struct mcap_instance { bdaddr_t src; /* Source address */ GIOChannel *ccio; /* Control Channel IO */ GIOChannel *dcio; /* Data Channel IO */ GSList *mcls; /* MCAP instance list */ GSList *cached; /* List with all cached MCLs (MAX_CACHED macro) */ BtIOSecLevel sec; /* Security level */ mcap_mcl_event_cb mcl_connected_cb; /* New MCL connected */ mcap_mcl_event_cb mcl_reconnected_cb; /* Old MCL has been reconnected */ mcap_mcl_event_cb mcl_disconnected_cb; /* MCL disconnected */ mcap_mcl_event_cb mcl_uncached_cb; /* MCL has been removed from MCAP cache */ mcap_info_ind_event_cb mcl_sync_infoind_cb; /* (CSP Central) Received info indication */ gpointer user_data; /* Data to be provided in callbacks */ int ref; /* Reference counter */ gboolean csp_enabled; /* CSP: functionality enabled */ }; struct mcap_mcl { struct mcap_instance *mi; /* MCAP instance where this MCL belongs */ bdaddr_t addr; /* Device address */ GIOChannel *cc; /* MCAP Control Channel IO */ guint wid; /* MCL Watcher id */ GSList *mdls; /* List of Data Channels shorted by mdlid */ MCLState state; /* Current MCL State */ MCLRole role; /* Initiator or acceptor of this MCL */ MCAPCtrl req; /* Request control flag */ struct mcap_mdl_op_cb *priv_data; /* Temporal data to manage responses */ struct mcap_mdl_cb *cb; /* MDL callbacks */ guint tid; /* Timer id for waiting for a response */ uint8_t *lcmd; /* Last command sent */ int ref; /* References counter */ uint8_t ctrl; /* MCL control flag */ uint16_t next_mdl; /* id used to create next MDL */ struct mcap_csp *csp; /* CSP control structure */ }; struct mcap_mdl { struct mcap_mcl *mcl; /* MCL where this MDL belongs */ GIOChannel *dc; /* MCAP Data Channel IO */ guint wid; /* MDL Watcher id */ uint16_t mdlid; /* MDL id */ uint8_t mdep_id; /* MCAP Data End Point */ MDLState state; /* MDL state */ int ref; /* References counter */ }; struct sync_info_ind_data { uint32_t btclock; uint64_t timestamp; uint16_t accuracy; }; /************ Operations ************/ /* MDL operations */ gboolean mcap_create_mdl(struct mcap_mcl *mcl, uint8_t mdepid, uint8_t conf, mcap_mdl_operation_conf_cb connect_cb, gpointer user_data, GDestroyNotify destroy, GError **err); gboolean mcap_reconnect_mdl(struct mcap_mdl *mdl, mcap_mdl_operation_cb reconnect_cb, gpointer user_data, GDestroyNotify destroy, GError **err); gboolean mcap_delete_all_mdls(struct mcap_mcl *mcl, mcap_mdl_notify_cb delete_cb, gpointer user_data, GDestroyNotify destroy, GError **err); gboolean mcap_delete_mdl(struct mcap_mdl *mdl, mcap_mdl_notify_cb delete_cb, gpointer user_data, GDestroyNotify destroy, GError **err); gboolean mcap_connect_mdl(struct mcap_mdl *mdl, uint8_t mode, uint16_t dcpsm, mcap_mdl_operation_cb connect_cb, gpointer user_data, GDestroyNotify destroy, GError **err); gboolean mcap_mdl_abort(struct mcap_mdl *mdl, mcap_mdl_notify_cb abort_cb, gpointer user_data, GDestroyNotify destroy, GError **err); int mcap_mdl_get_fd(struct mcap_mdl *mdl); uint16_t mcap_mdl_get_mdlid(struct mcap_mdl *mdl); struct mcap_mdl *mcap_mdl_ref(struct mcap_mdl *mdl); void mcap_mdl_unref(struct mcap_mdl *mdl); /* MCL operations */ gboolean mcap_create_mcl(struct mcap_instance *mi, const bdaddr_t *addr, uint16_t ccpsm, mcap_mcl_connect_cb connect_cb, gpointer user_data, GDestroyNotify destroy, GError **err); void mcap_close_mcl(struct mcap_mcl *mcl, gboolean cache); gboolean mcap_mcl_set_cb(struct mcap_mcl *mcl, gpointer user_data, GError **gerr, McapMclCb cb1, ...); void mcap_mcl_get_addr(struct mcap_mcl *mcl, bdaddr_t *addr); struct mcap_mcl *mcap_mcl_ref(struct mcap_mcl *mcl); void mcap_mcl_unref(struct mcap_mcl *mcl); /* CSP operations */ void mcap_enable_csp(struct mcap_instance *mi); void mcap_disable_csp(struct mcap_instance *mi); uint64_t mcap_get_timestamp(struct mcap_mcl *mcl, struct timespec *given_time); uint32_t mcap_get_btclock(struct mcap_mcl *mcl); void mcap_sync_cap_req(struct mcap_mcl *mcl, uint16_t reqacc, mcap_sync_cap_cb cb, gpointer user_data, GError **err); void mcap_sync_set_req(struct mcap_mcl *mcl, uint8_t update, uint32_t btclock, uint64_t timestamp, mcap_sync_set_cb cb, gpointer user_data, GError **err); /* MCAP main operations */ struct mcap_instance *mcap_create_instance(const bdaddr_t *src, BtIOSecLevel sec, uint16_t ccpsm, uint16_t dcpsm, mcap_mcl_event_cb mcl_connected, mcap_mcl_event_cb mcl_reconnected, mcap_mcl_event_cb mcl_disconnected, mcap_mcl_event_cb mcl_uncached, mcap_info_ind_event_cb mcl_sync_info_ind, gpointer user_data, GError **gerr); void mcap_release_instance(struct mcap_instance *mi); struct mcap_instance *mcap_instance_ref(struct mcap_instance *mi); void mcap_instance_unref(struct mcap_instance *mi); uint16_t mcap_get_ctrl_psm(struct mcap_instance *mi, GError **err); uint16_t mcap_get_data_psm(struct mcap_instance *mi, GError **err); gboolean mcap_set_data_chan_mode(struct mcap_instance *mi, uint8_t mode, GError **err); int mcap_send_data(int sock, const void *buf, uint32_t size); void proc_sync_cmd(struct mcap_mcl *mcl, uint8_t *cmd, uint32_t len); void mcap_sync_init(struct mcap_mcl *mcl); void mcap_sync_stop(struct mcap_mcl *mcl); bluez-5.82/profiles/health/PaxHeaders/hdp_util.c0000644000000000000000000000005014572354773016733 xustar0020 atime=1743516870 20 ctime=1743591284 bluez-5.82/profiles/health/hdp_util.c0000644000000000000000000006410714572354773016424 0ustar00rootroot// SPDX-License-Identifier: GPL-2.0-or-later /* * * BlueZ - Bluetooth protocol stack for Linux * * Copyright (C) 2010 GSyC/LibreSoft, Universidad Rey Juan Carlos. * */ #ifdef HAVE_CONFIG_H #include #endif #define _GNU_SOURCE #include #include #include #include "lib/bluetooth.h" #include "lib/sdp.h" #include "lib/sdp_lib.h" #include "lib/uuid.h" #include "gdbus/gdbus.h" #include "btio/btio.h" #include "src/adapter.h" #include "src/device.h" #include "src/sdpd.h" #include "src/sdp-client.h" #include "src/uuid-helper.h" #include "src/log.h" #include "src/dbus-common.h" #include "mcap.h" #include "hdp_types.h" #include "hdp.h" #include "hdp_util.h" typedef gboolean (*parse_item_f)(DBusMessageIter *iter, gpointer user_data, GError **err); struct dict_entry_func { const char *key; parse_item_f func; }; struct get_mdep_data { struct hdp_application *app; gpointer data; hdp_continue_mdep_f func; GDestroyNotify destroy; }; struct conn_mcl_data { int refs; gpointer data; hdp_continue_proc_f func; GDestroyNotify destroy; struct hdp_device *dev; }; struct get_dcpsm_data { gpointer data; hdp_continue_dcpsm_f func; GDestroyNotify destroy; }; static gboolean parse_dict_entry(const struct dict_entry_func dict_context[], DBusMessageIter *iter, GError **err, gpointer user_data) { DBusMessageIter entry; char *key; int ctype, i; dbus_message_iter_recurse(iter, &entry); ctype = dbus_message_iter_get_arg_type(&entry); if (ctype != DBUS_TYPE_STRING) { g_set_error(err, HDP_ERROR, HDP_DIC_ENTRY_PARSE_ERROR, "Dictionary entries should have a string as key"); return FALSE; } dbus_message_iter_get_basic(&entry, &key); dbus_message_iter_next(&entry); /* Find function and call it */ for (i = 0; dict_context[i].key; i++) { if (g_ascii_strcasecmp(dict_context[i].key, key) == 0) return dict_context[i].func(&entry, user_data, err); } g_set_error(err, HDP_ERROR, HDP_DIC_ENTRY_PARSE_ERROR, "No function found for parsing value for key %s", key); return FALSE; } static gboolean parse_dict(const struct dict_entry_func dict_context[], DBusMessageIter *iter, GError **err, gpointer user_data) { int ctype; DBusMessageIter dict; ctype = dbus_message_iter_get_arg_type(iter); if (ctype != DBUS_TYPE_ARRAY) { g_set_error(err, HDP_ERROR, HDP_DIC_PARSE_ERROR, "Dictionary should be an array"); return FALSE; } dbus_message_iter_recurse(iter, &dict); while ((ctype = dbus_message_iter_get_arg_type(&dict)) != DBUS_TYPE_INVALID) { if (ctype != DBUS_TYPE_DICT_ENTRY) { g_set_error(err, HDP_ERROR, HDP_DIC_PARSE_ERROR, "Dictionary array should " "contain dict entries"); return FALSE; } /* Start parsing entry */ if (!parse_dict_entry(dict_context, &dict, err, user_data)) return FALSE; /* Finish entry parsing */ dbus_message_iter_next(&dict); } return TRUE; } static gboolean parse_data_type(DBusMessageIter *iter, gpointer data, GError **err) { struct hdp_application *app = data; DBusMessageIter *value; DBusMessageIter variant; int ctype; ctype = dbus_message_iter_get_arg_type(iter); value = iter; if (ctype == DBUS_TYPE_VARIANT) { /* Get value inside the variable */ dbus_message_iter_recurse(iter, &variant); ctype = dbus_message_iter_get_arg_type(&variant); value = &variant; } if (ctype != DBUS_TYPE_UINT16) { g_set_error(err, HDP_ERROR, HDP_DIC_ENTRY_PARSE_ERROR, "Final value for data type should be uint16"); return FALSE; } dbus_message_iter_get_basic(value, &app->data_type); app->data_type_set = TRUE; return TRUE; } static gboolean parse_role(DBusMessageIter *iter, gpointer data, GError **err) { struct hdp_application *app = data; DBusMessageIter *string; DBusMessageIter value; int ctype; const char *role; ctype = dbus_message_iter_get_arg_type(iter); if (ctype == DBUS_TYPE_VARIANT) { /* Get value inside the variable */ dbus_message_iter_recurse(iter, &value); ctype = dbus_message_iter_get_arg_type(&value); string = &value; } else { string = iter; } if (ctype != DBUS_TYPE_STRING) { g_set_error(err, HDP_ERROR, HDP_UNSPECIFIED_ERROR, "Value data spec should be variable or string"); return FALSE; } dbus_message_iter_get_basic(string, &role); if (g_ascii_strcasecmp(role, HDP_SINK_ROLE_AS_STRING) == 0) { app->role = HDP_SINK; } else if (g_ascii_strcasecmp(role, HDP_SOURCE_ROLE_AS_STRING) == 0) { app->role = HDP_SOURCE; } else { g_set_error(err, HDP_ERROR, HDP_UNSPECIFIED_ERROR, "Role value should be \"source\" or \"sink\""); return FALSE; } app->role_set = TRUE; return TRUE; } static gboolean parse_desc(DBusMessageIter *iter, gpointer data, GError **err) { struct hdp_application *app = data; DBusMessageIter *string; DBusMessageIter variant; int ctype; const char *desc; ctype = dbus_message_iter_get_arg_type(iter); if (ctype == DBUS_TYPE_VARIANT) { /* Get value inside the variable */ dbus_message_iter_recurse(iter, &variant); ctype = dbus_message_iter_get_arg_type(&variant); string = &variant; } else { string = iter; } if (ctype != DBUS_TYPE_STRING) { g_set_error(err, HDP_ERROR, HDP_DIC_ENTRY_PARSE_ERROR, "Value data spec should be variable or string"); return FALSE; } dbus_message_iter_get_basic(string, &desc); app->description = g_strdup(desc); return TRUE; } static gboolean parse_chan_type(DBusMessageIter *iter, gpointer data, GError **err) { struct hdp_application *app = data; DBusMessageIter *value; DBusMessageIter variant; char *chan_type; int ctype; ctype = dbus_message_iter_get_arg_type(iter); value = iter; if (ctype == DBUS_TYPE_VARIANT) { /* Get value inside the variable */ dbus_message_iter_recurse(iter, &variant); ctype = dbus_message_iter_get_arg_type(&variant); value = &variant; } if (ctype != DBUS_TYPE_STRING) { g_set_error(err, HDP_ERROR, HDP_DIC_ENTRY_PARSE_ERROR, "Final value for channel type should be an string"); return FALSE; } dbus_message_iter_get_basic(value, &chan_type); if (g_ascii_strcasecmp("reliable", chan_type) == 0) app->chan_type = HDP_RELIABLE_DC; else if (g_ascii_strcasecmp("streaming", chan_type) == 0) app->chan_type = HDP_STREAMING_DC; else { g_set_error(err, HDP_ERROR, HDP_DIC_ENTRY_PARSE_ERROR, "Invalid value for data type"); return FALSE; } app->chan_type_set = TRUE; return TRUE; } static const struct dict_entry_func dict_parser[] = { {"DataType", parse_data_type}, {"Role", parse_role}, {"Description", parse_desc}, {"ChannelType", parse_chan_type}, {NULL, NULL} }; struct hdp_application *hdp_get_app_config(DBusMessageIter *iter, GError **err) { struct hdp_application *app; app = g_new0(struct hdp_application, 1); app->ref = 1; if (!parse_dict(dict_parser, iter, err, app)) goto fail; if (!app->data_type_set || !app->role_set) { g_set_error(err, HDP_ERROR, HDP_DIC_PARSE_ERROR, "Mandatory fields aren't set"); goto fail; } return app; fail: hdp_application_unref(app); return NULL; } static gboolean is_app_role(GSList *app_list, HdpRole role) { GSList *l; for (l = app_list; l; l = l->next) { struct hdp_application *app = l->data; if (app->role == role) return TRUE; } return FALSE; } static gboolean set_sdp_services_uuid(sdp_record_t *record, HdpRole role) { uuid_t svc_uuid_source, svc_uuid_sink; sdp_list_t *svc_list = NULL; sdp_uuid16_create(&svc_uuid_sink, HDP_SINK_SVCLASS_ID); sdp_uuid16_create(&svc_uuid_source, HDP_SOURCE_SVCLASS_ID); sdp_get_service_classes(record, &svc_list); if (role == HDP_SOURCE) { if (!sdp_list_find(svc_list, &svc_uuid_source, sdp_uuid_cmp)) svc_list = sdp_list_append(svc_list, &svc_uuid_source); } else if (role == HDP_SINK) { if (!sdp_list_find(svc_list, &svc_uuid_sink, sdp_uuid_cmp)) svc_list = sdp_list_append(svc_list, &svc_uuid_sink); } if (sdp_set_service_classes(record, svc_list) < 0) { sdp_list_free(svc_list, NULL); return FALSE; } sdp_list_free(svc_list, NULL); return TRUE; } static gboolean register_service_protocols(struct hdp_adapter *adapter, sdp_record_t *sdp_record) { gboolean ret; uuid_t l2cap_uuid, mcap_c_uuid; sdp_list_t *l2cap_list, *proto_list = NULL, *mcap_list = NULL; sdp_list_t *access_proto_list = NULL; sdp_data_t *psm = NULL, *mcap_ver = NULL; uint16_t version = MCAP_VERSION; /* set l2cap information */ sdp_uuid16_create(&l2cap_uuid, L2CAP_UUID); l2cap_list = sdp_list_append(NULL, &l2cap_uuid); if (l2cap_list == NULL) { ret = FALSE; goto end; } psm = sdp_data_alloc(SDP_UINT16, &adapter->ccpsm); if (psm == NULL) { ret = FALSE; goto end; } if (sdp_list_append(l2cap_list, psm) == NULL) { ret = FALSE; goto end; } proto_list = sdp_list_append(NULL, l2cap_list); if (proto_list == NULL) { ret = FALSE; goto end; } /* set mcap information */ sdp_uuid16_create(&mcap_c_uuid, MCAP_CTRL_UUID); mcap_list = sdp_list_append(NULL, &mcap_c_uuid); if (mcap_list == NULL) { ret = FALSE; goto end; } mcap_ver = sdp_data_alloc(SDP_UINT16, &version); if (mcap_ver == NULL) { ret = FALSE; goto end; } if (sdp_list_append(mcap_list, mcap_ver) == NULL) { ret = FALSE; goto end; } if (sdp_list_append(proto_list, mcap_list) == NULL) { ret = FALSE; goto end; } /* attach protocol information to service record */ access_proto_list = sdp_list_append(NULL, proto_list); if (access_proto_list == NULL) { ret = FALSE; goto end; } ret = TRUE; sdp_set_access_protos(sdp_record, access_proto_list); end: if (l2cap_list != NULL) sdp_list_free(l2cap_list, NULL); if (mcap_list != NULL) sdp_list_free(mcap_list, NULL); if (proto_list != NULL) sdp_list_free(proto_list, NULL); if (access_proto_list != NULL) sdp_list_free(access_proto_list, NULL); if (psm != NULL) sdp_data_free(psm); if (mcap_ver != NULL) sdp_data_free(mcap_ver); return ret; } static gboolean register_service_profiles(sdp_record_t *sdp_record) { gboolean ret; sdp_list_t *profile_list; sdp_profile_desc_t hdp_profile; /* set hdp information */ sdp_uuid16_create(&hdp_profile.uuid, HDP_SVCLASS_ID); hdp_profile.version = HDP_VERSION; profile_list = sdp_list_append(NULL, &hdp_profile); if (profile_list == NULL) return FALSE; /* set profile descriptor list */ if (sdp_set_profile_descs(sdp_record, profile_list) < 0) ret = FALSE; else ret = TRUE; sdp_list_free(profile_list, NULL); return ret; } static gboolean register_service_additional_protocols( struct hdp_adapter *adapter, sdp_record_t *sdp_record) { gboolean ret = TRUE; uuid_t l2cap_uuid, mcap_d_uuid; sdp_list_t *l2cap_list, *proto_list = NULL, *mcap_list = NULL; sdp_list_t *access_proto_list = NULL; sdp_data_t *psm = NULL; /* set l2cap information */ sdp_uuid16_create(&l2cap_uuid, L2CAP_UUID); l2cap_list = sdp_list_append(NULL, &l2cap_uuid); if (l2cap_list == NULL) { ret = FALSE; goto end; } psm = sdp_data_alloc(SDP_UINT16, &adapter->dcpsm); if (psm == NULL) { ret = FALSE; goto end; } if (sdp_list_append(l2cap_list, psm) == NULL) { ret = FALSE; goto end; } proto_list = sdp_list_append(NULL, l2cap_list); if (proto_list == NULL) { ret = FALSE; goto end; } /* set mcap information */ sdp_uuid16_create(&mcap_d_uuid, MCAP_DATA_UUID); mcap_list = sdp_list_append(NULL, &mcap_d_uuid); if (mcap_list == NULL) { ret = FALSE; goto end; } if (sdp_list_append(proto_list, mcap_list) == NULL) { ret = FALSE; goto end; } /* attach protocol information to service record */ access_proto_list = sdp_list_append(NULL, proto_list); if (access_proto_list == NULL) { ret = FALSE; goto end; } sdp_set_add_access_protos(sdp_record, access_proto_list); end: if (l2cap_list != NULL) sdp_list_free(l2cap_list, NULL); if (mcap_list != NULL) sdp_list_free(mcap_list, NULL); if (proto_list != NULL) sdp_list_free(proto_list, NULL); if (access_proto_list != NULL) sdp_list_free(access_proto_list, NULL); if (psm != NULL) sdp_data_free(psm); return ret; } static sdp_list_t *app_to_sdplist(struct hdp_application *app) { sdp_data_t *mdepid, *dtype = NULL, *role = NULL, *desc = NULL; sdp_list_t *f_list = NULL; mdepid = sdp_data_alloc(SDP_UINT8, &app->id); if (mdepid == NULL) return NULL; dtype = sdp_data_alloc(SDP_UINT16, &app->data_type); if (dtype == NULL) goto fail; role = sdp_data_alloc(SDP_UINT8, &app->role); if (role == NULL) goto fail; if (app->description != NULL) { desc = sdp_data_alloc(SDP_TEXT_STR8, app->description); if (desc == NULL) goto fail; } f_list = sdp_list_append(NULL, mdepid); if (f_list == NULL) goto fail; if (sdp_list_append(f_list, dtype) == NULL) goto fail; if (sdp_list_append(f_list, role) == NULL) goto fail; if (desc != NULL) if (sdp_list_append(f_list, desc) == NULL) goto fail; return f_list; fail: if (f_list != NULL) sdp_list_free(f_list, NULL); if (mdepid != NULL) sdp_data_free(mdepid); if (dtype != NULL) sdp_data_free(dtype); if (role != NULL) sdp_data_free(role); if (desc != NULL) sdp_data_free(desc); return NULL; } static void free_hdp_list(void *list) { sdp_list_t *hdp_list = list; sdp_list_free(hdp_list, (sdp_free_func_t)sdp_data_free); } static gboolean register_features(struct hdp_application *app, sdp_list_t **sup_features) { sdp_list_t *hdp_feature; hdp_feature = app_to_sdplist(app); if (hdp_feature == NULL) goto fail; if (*sup_features == NULL) { *sup_features = sdp_list_append(NULL, hdp_feature); if (*sup_features == NULL) goto fail; } else if (sdp_list_append(*sup_features, hdp_feature) == NULL) { goto fail; } return TRUE; fail: if (hdp_feature != NULL) sdp_list_free(hdp_feature, (sdp_free_func_t)sdp_data_free); if (*sup_features != NULL) sdp_list_free(*sup_features, free_hdp_list); return FALSE; } static gboolean register_service_sup_features(GSList *app_list, sdp_record_t *sdp_record) { GSList *l; sdp_list_t *sup_features = NULL; for (l = app_list; l; l = l->next) { if (!register_features(l->data, &sup_features)) return FALSE; } if (sdp_set_supp_feat(sdp_record, sup_features) < 0) { sdp_list_free(sup_features, free_hdp_list); return FALSE; } sdp_list_free(sup_features, free_hdp_list); return TRUE; } static gboolean register_data_exchange_spec(sdp_record_t *record) { sdp_data_t *spec; uint8_t data_spec = DATA_EXCHANGE_SPEC_11073; /* As by now 11073 is the only supported we set it by default */ spec = sdp_data_alloc(SDP_UINT8, &data_spec); if (spec == NULL) return FALSE; if (sdp_attr_add(record, SDP_ATTR_DATA_EXCHANGE_SPEC, spec) < 0) { sdp_data_free(spec); return FALSE; } return TRUE; } static gboolean register_mcap_features(sdp_record_t *sdp_record) { sdp_data_t *mcap_proc; uint8_t mcap_sup_proc = MCAP_SUP_PROC; mcap_proc = sdp_data_alloc(SDP_UINT8, &mcap_sup_proc); if (mcap_proc == NULL) return FALSE; if (sdp_attr_add(sdp_record, SDP_ATTR_MCAP_SUPPORTED_PROCEDURES, mcap_proc) < 0) { sdp_data_free(mcap_proc); return FALSE; } return TRUE; } gboolean hdp_update_sdp_record(struct hdp_adapter *adapter, GSList *app_list) { sdp_record_t *sdp_record; if (adapter->sdp_handler > 0) adapter_service_remove(adapter->btd_adapter, adapter->sdp_handler); if (app_list == NULL) { adapter->sdp_handler = 0; return TRUE; } sdp_record = sdp_record_alloc(); if (sdp_record == NULL) return FALSE; if (adapter->sdp_handler > 0) sdp_record->handle = adapter->sdp_handler; else sdp_record->handle = 0xffffffff; /* Set automatically */ if (is_app_role(app_list, HDP_SINK)) set_sdp_services_uuid(sdp_record, HDP_SINK); if (is_app_role(app_list, HDP_SOURCE)) set_sdp_services_uuid(sdp_record, HDP_SOURCE); if (!register_service_protocols(adapter, sdp_record)) goto fail; if (!register_service_profiles(sdp_record)) goto fail; if (!register_service_additional_protocols(adapter, sdp_record)) goto fail; sdp_set_info_attr(sdp_record, HDP_SERVICE_NAME, HDP_SERVICE_PROVIDER, HDP_SERVICE_DSC); if (!register_service_sup_features(app_list, sdp_record)) goto fail; if (!register_data_exchange_spec(sdp_record)) goto fail; register_mcap_features(sdp_record); if (sdp_set_record_state(sdp_record, adapter->record_state++) < 0) goto fail; if (adapter_service_add(adapter->btd_adapter, sdp_record) < 0) goto fail; adapter->sdp_handler = sdp_record->handle; return TRUE; fail: if (sdp_record != NULL) sdp_record_free(sdp_record); return FALSE; } static gboolean check_role(uint8_t rec_role, uint8_t app_role) { if ((rec_role == HDP_SINK && app_role == HDP_SOURCE) || (rec_role == HDP_SOURCE && app_role == HDP_SINK)) return TRUE; return FALSE; } static gboolean get_mdep_from_rec(const sdp_record_t *rec, uint8_t role, uint16_t d_type, uint8_t *mdep, char **desc) { sdp_data_t *list, *feat; if (desc == NULL && mdep == NULL) return TRUE; list = sdp_data_get(rec, SDP_ATTR_SUPPORTED_FEATURES_LIST); if (list == NULL || !SDP_IS_SEQ(list->dtd)) return FALSE; for (feat = list->val.dataseq; feat; feat = feat->next) { sdp_data_t *data_type, *mdepid, *role_t, *desc_t; if (!SDP_IS_SEQ(feat->dtd)) continue; mdepid = feat->val.dataseq; if (mdepid == NULL) continue; data_type = mdepid->next; if (data_type == NULL) continue; role_t = data_type->next; if (role_t == NULL) continue; desc_t = role_t->next; if (data_type->dtd != SDP_UINT16 || mdepid->dtd != SDP_UINT8 || role_t->dtd != SDP_UINT8) continue; if (data_type->val.uint16 != d_type || !check_role(role_t->val.uint8, role)) continue; if (mdep != NULL) *mdep = mdepid->val.uint8; if (desc != NULL && desc_t != NULL && SDP_IS_TEXT_STR(desc_t->dtd)) *desc = g_strdup(desc_t->val.str); return TRUE; } return FALSE; } static void get_mdep_cb(sdp_list_t *recs, int err, gpointer user_data) { struct get_mdep_data *mdep_data = user_data; GError *gerr = NULL; uint8_t mdep; if (err < 0 || recs == NULL) { g_set_error(&gerr, HDP_ERROR, HDP_CONNECTION_ERROR, "Error getting remote SDP records"); mdep_data->func(0, mdep_data->data, gerr); g_error_free(gerr); return; } if (!get_mdep_from_rec(recs->data, mdep_data->app->role, mdep_data->app->data_type, &mdep, NULL)) { g_set_error(&gerr, HDP_ERROR, HDP_CONNECTION_ERROR, "No matching MDEP found"); mdep_data->func(0, mdep_data->data, gerr); g_error_free(gerr); return; } mdep_data->func(mdep, mdep_data->data, NULL); } static void free_mdep_data(gpointer data) { struct get_mdep_data *mdep_data = data; if (mdep_data->destroy) mdep_data->destroy(mdep_data->data); hdp_application_unref(mdep_data->app); g_free(mdep_data); } gboolean hdp_get_mdep(struct hdp_device *device, struct hdp_application *app, hdp_continue_mdep_f func, gpointer data, GDestroyNotify destroy, GError **err) { struct get_mdep_data *mdep_data; const bdaddr_t *src; const bdaddr_t *dst; uuid_t uuid; src = btd_adapter_get_address(device_get_adapter(device->dev)); dst = device_get_address(device->dev); mdep_data = g_new0(struct get_mdep_data, 1); mdep_data->app = hdp_application_ref(app); mdep_data->func = func; mdep_data->data = data; mdep_data->destroy = destroy; bt_string2uuid(&uuid, HDP_UUID); if (bt_search_service(src, dst, &uuid, get_mdep_cb, mdep_data, free_mdep_data, 0) < 0) { g_set_error(err, HDP_ERROR, HDP_CONNECTION_ERROR, "Can't get remote SDP record"); g_free(mdep_data); return FALSE; } return TRUE; } static gboolean get_prot_desc_entry(sdp_data_t *entry, int type, guint16 *val) { sdp_data_t *iter; int proto; if (entry == NULL || !SDP_IS_SEQ(entry->dtd)) return FALSE; iter = entry->val.dataseq; if (!(iter->dtd & SDP_UUID_UNSPEC)) return FALSE; proto = sdp_uuid_to_proto(&iter->val.uuid); if (proto != type) return FALSE; if (val == NULL) return TRUE; iter = iter->next; if (iter->dtd != SDP_UINT16) return FALSE; *val = iter->val.uint16; return TRUE; } static gboolean hdp_get_prot_desc_list(const sdp_record_t *rec, guint16 *psm, guint16 *version) { sdp_data_t *pdl, *p0, *p1; if (psm == NULL && version == NULL) return TRUE; pdl = sdp_data_get(rec, SDP_ATTR_PROTO_DESC_LIST); if (pdl == NULL || !SDP_IS_SEQ(pdl->dtd)) return FALSE; p0 = pdl->val.dataseq; if (!get_prot_desc_entry(p0, L2CAP_UUID, psm)) return FALSE; p1 = p0->next; if (!get_prot_desc_entry(p1, MCAP_CTRL_UUID, version)) return FALSE; return TRUE; } static gboolean hdp_get_add_prot_desc_list(const sdp_record_t *rec, guint16 *psm) { sdp_data_t *pdl, *p0, *p1; if (psm == NULL) return TRUE; pdl = sdp_data_get(rec, SDP_ATTR_ADD_PROTO_DESC_LIST); if (pdl == NULL || pdl->dtd != SDP_SEQ8) return FALSE; pdl = pdl->val.dataseq; if (pdl->dtd != SDP_SEQ8) return FALSE; p0 = pdl->val.dataseq; if (!get_prot_desc_entry(p0, L2CAP_UUID, psm)) return FALSE; p1 = p0->next; if (!get_prot_desc_entry(p1, MCAP_DATA_UUID, NULL)) return FALSE; return TRUE; } static gboolean get_ccpsm(sdp_list_t *recs, uint16_t *ccpsm) { sdp_list_t *l; for (l = recs; l; l = l->next) { sdp_record_t *rec = l->data; if (hdp_get_prot_desc_list(rec, ccpsm, NULL)) return TRUE; } return FALSE; } static gboolean get_dcpsm(sdp_list_t *recs, uint16_t *dcpsm) { sdp_list_t *l; for (l = recs; l; l = l->next) { sdp_record_t *rec = l->data; if (hdp_get_add_prot_desc_list(rec, dcpsm)) return TRUE; } return FALSE; } static void con_mcl_data_unref(struct conn_mcl_data *conn_data) { if (conn_data == NULL) return; if (--conn_data->refs > 0) return; if (conn_data->destroy) conn_data->destroy(conn_data->data); health_device_unref(conn_data->dev); g_free(conn_data); } static void destroy_con_mcl_data(gpointer data) { con_mcl_data_unref(data); } static struct conn_mcl_data *con_mcl_data_ref(struct conn_mcl_data *conn_data) { if (conn_data == NULL) return NULL; conn_data->refs++; return conn_data; } static void create_mcl_cb(struct mcap_mcl *mcl, GError *err, gpointer data) { struct conn_mcl_data *conn_data = data; struct hdp_device *device = conn_data->dev; GError *gerr = NULL; if (err != NULL) { conn_data->func(conn_data->data, err); return; } if (device->mcl == NULL) device->mcl = mcap_mcl_ref(mcl); device->mcl_conn = TRUE; hdp_set_mcl_cb(device, &gerr); conn_data->func(conn_data->data, gerr); if (gerr != NULL) g_error_free(gerr); } static void search_cb(sdp_list_t *recs, int err, gpointer user_data) { struct conn_mcl_data *conn_data = user_data; GError *gerr = NULL; uint16_t ccpsm; if (conn_data->dev->hdp_adapter->mi == NULL) { g_set_error(&gerr, HDP_ERROR, HDP_CONNECTION_ERROR, "Mcap instance released"); goto fail; } if (err < 0 || recs == NULL) { g_set_error(&gerr, HDP_ERROR, HDP_CONNECTION_ERROR, "Error getting remote SDP records"); goto fail; } if (!get_ccpsm(recs, &ccpsm)) { g_set_error(&gerr, HDP_ERROR, HDP_CONNECTION_ERROR, "Can't get remote PSM for control channel"); goto fail; } conn_data = con_mcl_data_ref(conn_data); if (!mcap_create_mcl(conn_data->dev->hdp_adapter->mi, device_get_address(conn_data->dev->dev), ccpsm, create_mcl_cb, conn_data, destroy_con_mcl_data, &gerr)) { con_mcl_data_unref(conn_data); goto fail; } return; fail: conn_data->func(conn_data->data, gerr); g_error_free(gerr); } gboolean hdp_establish_mcl(struct hdp_device *device, hdp_continue_proc_f func, gpointer data, GDestroyNotify destroy, GError **err) { struct conn_mcl_data *conn_data; const bdaddr_t *src; const bdaddr_t *dst; uuid_t uuid; src = btd_adapter_get_address(device_get_adapter(device->dev)); dst = device_get_address(device->dev); conn_data = g_new0(struct conn_mcl_data, 1); conn_data->refs = 1; conn_data->func = func; conn_data->data = data; conn_data->destroy = destroy; conn_data->dev = health_device_ref(device); bt_string2uuid(&uuid, HDP_UUID); if (bt_search_service(src, dst, &uuid, search_cb, conn_data, destroy_con_mcl_data, 0) < 0) { g_set_error(err, HDP_ERROR, HDP_CONNECTION_ERROR, "Can't get remote SDP record"); g_free(conn_data); return FALSE; } return TRUE; } static void get_dcpsm_cb(sdp_list_t *recs, int err, gpointer data) { struct get_dcpsm_data *dcpsm_data = data; GError *gerr = NULL; uint16_t dcpsm; if (err < 0 || recs == NULL) { g_set_error(&gerr, HDP_ERROR, HDP_CONNECTION_ERROR, "Error getting remote SDP records"); goto fail; } if (!get_dcpsm(recs, &dcpsm)) { g_set_error(&gerr, HDP_ERROR, HDP_CONNECTION_ERROR, "Can't get remote PSM for data channel"); goto fail; } dcpsm_data->func(dcpsm, dcpsm_data->data, NULL); return; fail: dcpsm_data->func(0, dcpsm_data->data, gerr); g_error_free(gerr); } static void free_dcpsm_data(gpointer data) { struct get_dcpsm_data *dcpsm_data = data; if (dcpsm_data == NULL) return; if (dcpsm_data->destroy) dcpsm_data->destroy(dcpsm_data->data); g_free(dcpsm_data); } gboolean hdp_get_dcpsm(struct hdp_device *device, hdp_continue_dcpsm_f func, gpointer data, GDestroyNotify destroy, GError **err) { struct get_dcpsm_data *dcpsm_data; const bdaddr_t *src; const bdaddr_t *dst; uuid_t uuid; src = btd_adapter_get_address(device_get_adapter(device->dev)); dst = device_get_address(device->dev); dcpsm_data = g_new0(struct get_dcpsm_data, 1); dcpsm_data->func = func; dcpsm_data->data = data; dcpsm_data->destroy = destroy; bt_string2uuid(&uuid, HDP_UUID); if (bt_search_service(src, dst, &uuid, get_dcpsm_cb, dcpsm_data, free_dcpsm_data, 0) < 0) { g_set_error(err, HDP_ERROR, HDP_CONNECTION_ERROR, "Can't get remote SDP record"); g_free(dcpsm_data); return FALSE; } return TRUE; } static void hdp_free_application(struct hdp_application *app) { if (app->dbus_watcher > 0) g_dbus_remove_watch(btd_get_dbus_connection(), app->dbus_watcher); g_free(app->oname); g_free(app->description); g_free(app->path); g_free(app); } struct hdp_application *hdp_application_ref(struct hdp_application *app) { if (app == NULL) return NULL; app->ref++; DBG("health_application_ref(%p): ref=%d", app, app->ref); return app; } void hdp_application_unref(struct hdp_application *app) { if (app == NULL) return; app->ref--; DBG("health_application_unref(%p): ref=%d", app, app->ref); if (app->ref > 0) return; hdp_free_application(app); } bluez-5.82/profiles/health/PaxHeaders/hdp_manager.c0000644000000000000000000000005014015011623017340 xustar0020 atime=1743516870 20 ctime=1743591284 bluez-5.82/profiles/health/hdp_manager.c0000644000000000000000000000354314015011623017026 0ustar00rootroot// SPDX-License-Identifier: GPL-2.0-or-later /* * * BlueZ - Bluetooth protocol stack for Linux * * Copyright (C) 2010 GSyC/LibreSoft, Universidad Rey Juan Carlos. * */ #ifdef HAVE_CONFIG_H #include #endif #include #include "lib/sdp.h" #include "lib/sdp_lib.h" #include "lib/uuid.h" #include "btio/btio.h" #include "src/adapter.h" #include "src/device.h" #include "src/profile.h" #include "src/service.h" #include "src/uuid-helper.h" #include "src/log.h" #include "hdp_types.h" #include "hdp_manager.h" #include "hdp.h" static int hdp_adapter_probe(struct btd_profile *p, struct btd_adapter *adapter) { return hdp_adapter_register(adapter); } static void hdp_adapter_remove(struct btd_profile *p, struct btd_adapter *adapter) { hdp_adapter_unregister(adapter); } static int hdp_driver_probe(struct btd_service *service) { struct btd_device *device = btd_service_get_device(service); return hdp_device_register(device); } static void hdp_driver_remove(struct btd_service *service) { struct btd_device *device = btd_service_get_device(service); hdp_device_unregister(device); } static struct btd_profile hdp_source_profile = { .name = "hdp-source", .remote_uuid = HDP_SOURCE_UUID, .device_probe = hdp_driver_probe, .device_remove = hdp_driver_remove, .adapter_probe = hdp_adapter_probe, .adapter_remove = hdp_adapter_remove, }; static struct btd_profile hdp_sink_profile = { .name = "hdp-sink", .remote_uuid = HDP_SINK_UUID, .device_probe = hdp_driver_probe, .device_remove = hdp_driver_remove, }; int hdp_manager_init(void) { if (hdp_manager_start() < 0) return -1; btd_profile_register(&hdp_source_profile); btd_profile_register(&hdp_sink_profile); return 0; } void hdp_manager_exit(void) { btd_profile_unregister(&hdp_sink_profile); btd_profile_unregister(&hdp_source_profile); hdp_manager_stop(); } bluez-5.82/profiles/health/PaxHeaders/mcap.c0000644000000000000000000000005014667536076016046 xustar0020 atime=1743516243 20 ctime=1743591279 bluez-5.82/profiles/health/mcap.c0000644000000000000000000021274114667536076015536 0ustar00rootroot// SPDX-License-Identifier: GPL-2.0-or-later /* * * BlueZ - Bluetooth protocol stack for Linux * * Copyright (C) 2010 GSyC/LibreSoft, Universidad Rey Juan Carlos. * Copyright (C) 2010 Signove * Copyright (C) 2014 Intel Corporation. All rights reserved. * */ #ifdef HAVE_CONFIG_H #include #endif #define _GNU_SOURCE #include #include #include #include #include #include #include "lib/bluetooth.h" #include "bluetooth/l2cap.h" #include "btio/btio.h" #include "src/log.h" #include "src/shared/timeout.h" #include "src/shared/util.h" #include "mcap.h" #define MCAP_BTCLOCK_HALF (MCAP_BTCLOCK_FIELD / 2) #define CLK CLOCK_MONOTONIC #define MCAP_CSP_ERROR g_quark_from_static_string("mcap-csp-error-quark") #define MAX_RETRIES 10 #define SAMPLE_COUNT 20 #define RESPONSE_TIMER 6 /* seconds */ #define MAX_CACHED 10 /* 10 devices */ #define MCAP_ERROR g_quark_from_static_string("mcap-error-quark") #define RELEASE_TIMER(__mcl) do { \ if (__mcl->tid) { \ timeout_remove(__mcl->tid); \ __mcl->tid = 0; \ } \ } while(0) struct mcap_csp { uint64_t base_tmstamp; /* CSP base timestamp */ struct timespec base_time; /* CSP base time when timestamp set */ guint local_caps; /* CSP-Cent.: have got remote caps */ guint remote_caps; /* CSP-Perip: remote central got caps */ guint rem_req_acc; /* CSP-Perip: accuracy req by central */ guint ind_expected; /* CSP-Cent.: indication expected */ uint8_t csp_req; /* CSP-Cent.: Request control flag */ guint ind_timer; /* CSP-Perip: indication timer */ guint set_timer; /* CSP-Perip: delayed set timer */ void *set_data; /* CSP-Perip: delayed set data */ void *csp_priv_data; /* CSP-Cent.: In-flight request data */ }; struct mcap_sync_cap_cbdata { mcap_sync_cap_cb cb; gpointer user_data; }; struct mcap_sync_set_cbdata { mcap_sync_set_cb cb; gpointer user_data; }; struct csp_caps { int ts_acc; /* timestamp accuracy */ int ts_res; /* timestamp resolution */ int latency; /* Read BT clock latency */ int preempt_thresh; /* Preemption threshold for latency */ int syncleadtime_ms; /* SyncLeadTime in ms */ }; struct sync_set_data { uint8_t update; uint32_t sched_btclock; uint64_t timestamp; int ind_freq; gboolean role; }; struct connect_mcl { struct mcap_mcl *mcl; /* MCL for this operation */ mcap_mcl_connect_cb connect_cb; /* Connect callback */ GDestroyNotify destroy; /* Destroy callback */ gpointer user_data; /* Callback user data */ }; typedef union { mcap_mdl_operation_cb op; mcap_mdl_operation_conf_cb op_conf; mcap_mdl_notify_cb notify; } mcap_cb_type; struct mcap_mdl_op_cb { struct mcap_mdl *mdl; /* MDL for this operation */ mcap_cb_type cb; /* Operation callback */ GDestroyNotify destroy; /* Destroy callback */ gpointer user_data; /* Callback user data */ }; /* MCAP finite state machine functions */ static void proc_req_connected(struct mcap_mcl *mcl, uint8_t *cmd, uint32_t l); static void proc_req_pending(struct mcap_mcl *mcl, uint8_t *cmd, uint32_t l); static void proc_req_active(struct mcap_mcl *mcl, uint8_t *cmd, uint32_t l); static void (*proc_req[])(struct mcap_mcl *mcl, uint8_t *cmd, uint32_t len) = { proc_req_connected, proc_req_pending, proc_req_active }; static gboolean csp_caps_initialized = FALSE; struct csp_caps _caps; static void mcap_cache_mcl(struct mcap_mcl *mcl); static void default_mdl_connected_cb(struct mcap_mdl *mdl, gpointer data) { DBG("MCAP Unmanaged mdl connection"); } static void default_mdl_closed_cb(struct mcap_mdl *mdl, gpointer data) { DBG("MCAP Unmanaged mdl closed"); } static void default_mdl_deleted_cb(struct mcap_mdl *mdl, gpointer data) { DBG("MCAP Unmanaged mdl deleted"); } static void default_mdl_aborted_cb(struct mcap_mdl *mdl, gpointer data) { DBG("MCAP Unmanaged mdl aborted"); } static uint8_t default_mdl_conn_req_cb(struct mcap_mcl *mcl, uint8_t mdepid, uint16_t mdlid, uint8_t *conf, gpointer data) { DBG("MCAP mdl remote connection aborted"); /* Due to this callback isn't managed this request won't be supported */ return MCAP_REQUEST_NOT_SUPPORTED; } static uint8_t default_mdl_reconn_req_cb(struct mcap_mdl *mdl, gpointer data) { DBG("MCAP mdl remote reconnection aborted"); /* Due to this callback isn't managed this request won't be supported */ return MCAP_REQUEST_NOT_SUPPORTED; } static void set_default_cb(struct mcap_mcl *mcl) { if (!mcl->cb) mcl->cb = g_new0(struct mcap_mdl_cb, 1); mcl->cb->mdl_connected = default_mdl_connected_cb; mcl->cb->mdl_closed = default_mdl_closed_cb; mcl->cb->mdl_deleted = default_mdl_deleted_cb; mcl->cb->mdl_aborted = default_mdl_aborted_cb; mcl->cb->mdl_conn_req = default_mdl_conn_req_cb; mcl->cb->mdl_reconn_req = default_mdl_reconn_req_cb; } static char *error2str(uint8_t rc) { switch (rc) { case MCAP_SUCCESS: return "Success"; case MCAP_INVALID_OP_CODE: return "Invalid Op Code"; case MCAP_INVALID_PARAM_VALUE: return "Invalid Parameter Value"; case MCAP_INVALID_MDEP: return "Invalid MDEP"; case MCAP_MDEP_BUSY: return "MDEP Busy"; case MCAP_INVALID_MDL: return "Invalid MDL"; case MCAP_MDL_BUSY: return "MDL Busy"; case MCAP_INVALID_OPERATION: return "Invalid Operation"; case MCAP_RESOURCE_UNAVAILABLE: return "Resource Unavailable"; case MCAP_UNSPECIFIED_ERROR: return "Unspecified Error"; case MCAP_REQUEST_NOT_SUPPORTED: return "Request Not Supported"; case MCAP_CONFIGURATION_REJECTED: return "Configuration Rejected"; default: return "Unknown Response Code"; } } static gboolean mcap_send_std_opcode(struct mcap_mcl *mcl, void *cmd, uint32_t size, GError **err) { if (mcl->state == MCL_IDLE) { g_set_error(err, MCAP_ERROR, MCAP_ERROR_FAILED, "MCL is not connected"); return FALSE; } if (mcl->req != MCL_AVAILABLE) { g_set_error(err, MCAP_ERROR, MCAP_ERROR_RESOURCE_UNAVAILABLE, "Pending request"); return FALSE; } if (!(mcl->ctrl & MCAP_CTRL_STD_OP)) { g_set_error(err, MCAP_ERROR, MCAP_ERROR_REQUEST_NOT_SUPPORTED, "Remote does not support standard opcodes"); return FALSE; } if (mcl->state == MCL_PENDING) { g_set_error(err, MCAP_ERROR, MCAP_ERROR_INVALID_OPERATION, "Not Std Op. Codes can be sent in PENDING State"); return FALSE; } if (mcap_send_data(g_io_channel_unix_get_fd(mcl->cc), cmd, size) < 0) { g_set_error(err, MCAP_ERROR, MCAP_ERROR_FAILED, "Command can't be sent, write error"); return FALSE; } mcl->lcmd = cmd; mcl->req = MCL_WAITING_RSP; return TRUE; } static void update_mcl_state(struct mcap_mcl *mcl) { GSList *l; struct mcap_mdl *mdl; if (mcl->state == MCL_PENDING) return; for (l = mcl->mdls; l; l = l->next) { mdl = l->data; if (mdl->state == MDL_CONNECTED) { mcl->state = MCL_ACTIVE; return; } } mcl->state = MCL_CONNECTED; } static void shutdown_mdl(struct mcap_mdl *mdl) { mdl->state = MDL_CLOSED; if (mdl->wid) { g_source_remove(mdl->wid); mdl->wid = 0; } if (mdl->dc) { g_io_channel_shutdown(mdl->dc, TRUE, NULL); g_io_channel_unref(mdl->dc); mdl->dc = NULL; } } static void free_mdl(struct mcap_mdl *mdl) { if (!mdl) return; mcap_mcl_unref(mdl->mcl); g_free(mdl); } static int cmp_mdl_state(gconstpointer a, gconstpointer b) { const struct mcap_mdl *mdl = a; const MDLState *st = b; if (mdl->state == *st) return 0; else if (mdl->state < *st) return -1; else return 1; } static void free_mcap_mdl_op(struct mcap_mdl_op_cb *op) { if (op->destroy) op->destroy(op->user_data); if (op->mdl) mcap_mdl_unref(op->mdl); g_free(op); } static void free_mcl_priv_data(struct mcap_mcl *mcl) { free_mcap_mdl_op(mcl->priv_data); mcl->priv_data = NULL; } static void mcap_notify_error(struct mcap_mcl *mcl, GError *err) { struct mcap_mdl_op_cb *con = mcl->priv_data; struct mcap_mdl *mdl; MDLState st; GSList *l; if (!con || !mcl->lcmd) return; switch (mcl->lcmd[0]) { case MCAP_MD_CREATE_MDL_REQ: st = MDL_WAITING; l = g_slist_find_custom(mcl->mdls, &st, cmp_mdl_state); if (!l) return; mdl = l->data; mcl->mdls = g_slist_remove(mcl->mdls, mdl); mcap_mdl_unref(mdl); update_mcl_state(mcl); con->cb.op_conf(NULL, 0, err, con->user_data); break; case MCAP_MD_ABORT_MDL_REQ: st = MDL_WAITING; l = g_slist_find_custom(mcl->mdls, &st, cmp_mdl_state); if (!l) return; shutdown_mdl(l->data); update_mcl_state(mcl); con->cb.notify(err, con->user_data); break; case MCAP_MD_DELETE_MDL_REQ: for (l = mcl->mdls; l; l = l->next) { mdl = l->data; if (mdl->state == MDL_DELETING) mdl->state = (mdl->dc) ? MDL_CONNECTED : MDL_CLOSED; } update_mcl_state(mcl); con->cb.notify(err, con->user_data); break; case MCAP_MD_RECONNECT_MDL_REQ: st = MDL_WAITING; l = g_slist_find_custom(mcl->mdls, &st, cmp_mdl_state); if (!l) return; shutdown_mdl(l->data); update_mcl_state(mcl); con->cb.op(NULL, err, con->user_data); break; } free_mcl_priv_data(mcl); g_free(mcl->lcmd); mcl->lcmd = NULL; } int mcap_send_data(int sock, const void *buf, uint32_t size) { const uint8_t *buf_b = buf; uint32_t sent = 0; while (sent < size) { int n = write(sock, buf_b + sent, size - sent); if (n < 0) return -1; sent += n; } return 0; } static int mcap_send_cmd(struct mcap_mcl *mcl, uint8_t oc, uint8_t rc, uint16_t mdl, uint8_t *data, size_t len) { mcap_rsp *cmd; int sock, sent; if (mcl->cc == NULL) return -1; sock = g_io_channel_unix_get_fd(mcl->cc); cmd = g_malloc(sizeof(mcap_rsp) + len); cmd->op = oc; cmd->rc = rc; cmd->mdl = htons(mdl); if (data && len > 0) memcpy(cmd->data, data, len); sent = mcap_send_data(sock, cmd, sizeof(mcap_rsp) + len); g_free(cmd); return sent; } static struct mcap_mdl *get_mdl(struct mcap_mcl *mcl, uint16_t mdlid) { GSList *l; struct mcap_mdl *mdl; for (l = mcl->mdls; l; l = l->next) { mdl = l->data; if (mdlid == mdl->mdlid) return mdl; } return NULL; } static uint16_t generate_mdlid(struct mcap_mcl *mcl) { uint16_t mdlid = mcl->next_mdl; struct mcap_mdl *mdl; do { mdl = get_mdl(mcl, mdlid); if (!mdl) { mcl->next_mdl = (mdlid % MCAP_MDLID_FINAL) + 1; return mdlid; } else mdlid = (mdlid % MCAP_MDLID_FINAL) + 1; } while (mdlid != mcl->next_mdl); /* No more mdlids availables */ return 0; } static mcap_md_req *create_req(uint8_t op, uint16_t mdl_id) { mcap_md_req *req_cmd; req_cmd = g_new0(mcap_md_req, 1); req_cmd->op = op; req_cmd->mdl = htons(mdl_id); return req_cmd; } static mcap_md_create_mdl_req *create_mdl_req(uint16_t mdl_id, uint8_t mdep, uint8_t conf) { mcap_md_create_mdl_req *req_mdl; req_mdl = g_new0(mcap_md_create_mdl_req, 1); req_mdl->op = MCAP_MD_CREATE_MDL_REQ; req_mdl->mdl = htons(mdl_id); req_mdl->mdep = mdep; req_mdl->conf = conf; return req_mdl; } static int compare_mdl(gconstpointer a, gconstpointer b) { const struct mcap_mdl *mdla = a; const struct mcap_mdl *mdlb = b; if (mdla->mdlid == mdlb->mdlid) return 0; else if (mdla->mdlid < mdlb->mdlid) return -1; else return 1; } static bool wait_response_timer(gpointer data) { struct mcap_mcl *mcl = data; GError *gerr = NULL; RELEASE_TIMER(mcl); g_set_error(&gerr, MCAP_ERROR, MCAP_ERROR_FAILED, "Timeout waiting response"); mcap_notify_error(mcl, gerr); g_error_free(gerr); mcl->mi->mcl_disconnected_cb(mcl, mcl->mi->user_data); mcap_cache_mcl(mcl); return FALSE; } gboolean mcap_create_mdl(struct mcap_mcl *mcl, uint8_t mdepid, uint8_t conf, mcap_mdl_operation_conf_cb connect_cb, gpointer user_data, GDestroyNotify destroy, GError **err) { struct mcap_mdl *mdl; struct mcap_mdl_op_cb *con; mcap_md_create_mdl_req *cmd; uint16_t id; id = generate_mdlid(mcl); if (!id) { g_set_error(err, MCAP_ERROR, MCAP_ERROR_FAILED, "Not more mdlids available"); return FALSE; } mdl = g_new0(struct mcap_mdl, 1); mdl->mcl = mcap_mcl_ref(mcl); mdl->mdlid = id; mdl->mdep_id = mdepid; mdl->state = MDL_WAITING; con = g_new0(struct mcap_mdl_op_cb, 1); con->mdl = mcap_mdl_ref(mdl); con->cb.op_conf = connect_cb; con->destroy = destroy; con->user_data = user_data; cmd = create_mdl_req(id, mdepid, conf); if (!mcap_send_std_opcode(mcl, cmd, sizeof(mcap_md_create_mdl_req), err)) { mcap_mdl_unref(con->mdl); g_free(con); g_free(cmd); return FALSE; } mcl->state = MCL_ACTIVE; mcl->priv_data = con; mcl->mdls = g_slist_insert_sorted(mcl->mdls, mcap_mdl_ref(mdl), compare_mdl); mcl->tid = timeout_add_seconds(RESPONSE_TIMER, wait_response_timer, mcl, NULL); return TRUE; } gboolean mcap_reconnect_mdl(struct mcap_mdl *mdl, mcap_mdl_operation_cb reconnect_cb, gpointer user_data, GDestroyNotify destroy, GError **err) { struct mcap_mdl_op_cb *con; struct mcap_mcl *mcl = mdl->mcl; mcap_md_req *cmd; if (mdl->state != MDL_CLOSED) { g_set_error(err, MCAP_ERROR, MCAP_ERROR_FAILED, "MDL is not closed"); return FALSE; } cmd = create_req(MCAP_MD_RECONNECT_MDL_REQ, mdl->mdlid); if (!mcap_send_std_opcode(mcl, cmd, sizeof(mcap_md_req), err)) { g_free(cmd); return FALSE; } mdl->state = MDL_WAITING; con = g_new0(struct mcap_mdl_op_cb, 1); con->mdl = mcap_mdl_ref(mdl); con->cb.op = reconnect_cb; con->destroy = destroy; con->user_data = user_data; mcl->state = MCL_ACTIVE; mcl->priv_data = con; mcl->tid = timeout_add_seconds(RESPONSE_TIMER, wait_response_timer, mcl, NULL); return TRUE; } static gboolean send_delete_req(struct mcap_mcl *mcl, struct mcap_mdl_op_cb *con, uint16_t mdlid, GError **err) { mcap_md_req *cmd; cmd = create_req(MCAP_MD_DELETE_MDL_REQ, mdlid); if (!mcap_send_std_opcode(mcl, cmd, sizeof(mcap_md_req), err)) { g_free(cmd); return FALSE; } mcl->priv_data = con; mcl->tid = timeout_add_seconds(RESPONSE_TIMER, wait_response_timer, mcl, NULL); return TRUE; } gboolean mcap_delete_all_mdls(struct mcap_mcl *mcl, mcap_mdl_notify_cb delete_cb, gpointer user_data, GDestroyNotify destroy, GError **err) { GSList *l; struct mcap_mdl *mdl; struct mcap_mdl_op_cb *con; DBG("MCL in state: %d", mcl->state); if (!mcl->mdls) { g_set_error(err, MCAP_ERROR, MCAP_ERROR_FAILED, "There are not MDLs created"); return FALSE; } for (l = mcl->mdls; l; l = l->next) { mdl = l->data; if (mdl->state != MDL_WAITING) mdl->state = MDL_DELETING; } con = g_new0(struct mcap_mdl_op_cb, 1); con->mdl = NULL; con->cb.notify = delete_cb; con->destroy = destroy; con->user_data = user_data; if (!send_delete_req(mcl, con, MCAP_ALL_MDLIDS, err)) { g_free(con); return FALSE; } return TRUE; } gboolean mcap_delete_mdl(struct mcap_mdl *mdl, mcap_mdl_notify_cb delete_cb, gpointer user_data, GDestroyNotify destroy, GError **err) { struct mcap_mcl *mcl= mdl->mcl; struct mcap_mdl_op_cb *con; GSList *l; l = g_slist_find(mcl->mdls, mdl); if (!l) { g_set_error(err, MCAP_ERROR, MCAP_ERROR_INVALID_MDL, "%s" , error2str(MCAP_INVALID_MDEP)); return FALSE; } if (mdl->state == MDL_WAITING) { g_set_error(err, MCAP_ERROR, MCAP_ERROR_FAILED, "Mdl is not created"); return FALSE; } mdl->state = MDL_DELETING; con = g_new0(struct mcap_mdl_op_cb, 1); con->mdl = mcap_mdl_ref(mdl); con->cb.notify = delete_cb; con->destroy = destroy; con->user_data = user_data; if (!send_delete_req(mcl, con, mdl->mdlid, err)) { mcap_mdl_unref(con->mdl); g_free(con); return FALSE; } return TRUE; } gboolean mcap_mdl_abort(struct mcap_mdl *mdl, mcap_mdl_notify_cb abort_cb, gpointer user_data, GDestroyNotify destroy, GError **err) { struct mcap_mdl_op_cb *con; struct mcap_mcl *mcl = mdl->mcl; mcap_md_req *cmd; if (mdl->state != MDL_WAITING) { g_set_error(err, MCAP_ERROR, MCAP_ERROR_FAILED, "Mdl in invalid state"); return FALSE; } cmd = create_req(MCAP_MD_ABORT_MDL_REQ, mdl->mdlid); if (!mcap_send_std_opcode(mcl, cmd, sizeof(mcap_md_req), err)) { g_free(cmd); return FALSE; } con = g_new0(struct mcap_mdl_op_cb, 1); con->mdl = mcap_mdl_ref(mdl); con->cb.notify = abort_cb; con->destroy = destroy; con->user_data = user_data; mcl->priv_data = con; mcl->tid = timeout_add_seconds(RESPONSE_TIMER, wait_response_timer, mcl, NULL); return TRUE; } static struct mcap_mcl *find_mcl(GSList *list, const bdaddr_t *addr) { struct mcap_mcl *mcl; for (; list; list = list->next) { mcl = list->data; if (!bacmp(&mcl->addr, addr)) return mcl; } return NULL; } int mcap_mdl_get_fd(struct mcap_mdl *mdl) { if (!mdl || mdl->state != MDL_CONNECTED) return -ENOTCONN; return g_io_channel_unix_get_fd(mdl->dc); } uint16_t mcap_mdl_get_mdlid(struct mcap_mdl *mdl) { if (!mdl) return MCAP_MDLID_RESERVED; return mdl->mdlid; } static void shutdown_mdl_cb(void *data, void *user_data) { shutdown_mdl(data); } static void mdl_unref_cb(void *data, void *user_data) { mcap_mdl_unref(data); } static void close_mcl(struct mcap_mcl *mcl, gboolean cache_requested) { gboolean save = ((!(mcl->ctrl & MCAP_CTRL_FREE)) && cache_requested); RELEASE_TIMER(mcl); if (mcl->cc) { g_io_channel_shutdown(mcl->cc, TRUE, NULL); g_io_channel_unref(mcl->cc); mcl->cc = NULL; } if (mcl->wid) { g_source_remove(mcl->wid); mcl->wid = 0; } if (mcl->lcmd) { g_free(mcl->lcmd); mcl->lcmd = NULL; } if (mcl->priv_data) free_mcl_priv_data(mcl); g_slist_foreach(mcl->mdls, shutdown_mdl_cb, NULL); mcap_sync_stop(mcl); mcl->state = MCL_IDLE; if (save) return; g_slist_foreach(mcl->mdls, mdl_unref_cb, NULL); g_slist_free(mcl->mdls); mcl->mdls = NULL; } static void mcap_mcl_shutdown(struct mcap_mcl *mcl) { close_mcl(mcl, TRUE); } static void mcap_mcl_release(struct mcap_mcl *mcl) { close_mcl(mcl, FALSE); } static void mcap_cache_mcl(struct mcap_mcl *mcl) { GSList *l; struct mcap_mcl *last; int len; if (mcl->ctrl & MCAP_CTRL_CACHED) return; mcl->mi->mcls = g_slist_remove(mcl->mi->mcls, mcl); if (mcl->ctrl & MCAP_CTRL_NOCACHE) { mcl->mi->cached = g_slist_remove(mcl->mi->cached, mcl); mcap_mcl_release(mcl); mcap_mcl_unref(mcl); return; } DBG("Caching MCL"); len = g_slist_length(mcl->mi->cached); if (len == MAX_CACHED) { /* Remove the latest cached mcl */ l = g_slist_last(mcl->mi->cached); last = l->data; mcl->mi->cached = g_slist_remove(mcl->mi->cached, last); last->ctrl &= ~MCAP_CTRL_CACHED; if (last->ctrl & MCAP_CTRL_CONN) { /* * We have to release this MCL if connection is not * successful */ last->ctrl |= MCAP_CTRL_FREE; } else { mcap_mcl_release(last); last->mi->mcl_uncached_cb(last, last->mi->user_data); } mcap_mcl_unref(last); } mcl->mi->cached = g_slist_prepend(mcl->mi->cached, mcl); mcl->ctrl |= MCAP_CTRL_CACHED; mcap_mcl_shutdown(mcl); } static void mcap_uncache_mcl(struct mcap_mcl *mcl) { if (!(mcl->ctrl & MCAP_CTRL_CACHED)) return; DBG("Got MCL from cache"); mcl->mi->cached = g_slist_remove(mcl->mi->cached, mcl); mcl->mi->mcls = g_slist_prepend(mcl->mi->mcls, mcl); mcl->ctrl &= ~MCAP_CTRL_CACHED; mcl->ctrl &= ~MCAP_CTRL_FREE; } void mcap_close_mcl(struct mcap_mcl *mcl, gboolean cache) { if (!mcl) return; if (mcl->ctrl & MCAP_CTRL_FREE) { mcap_mcl_release(mcl); return; } if (!cache) mcl->ctrl |= MCAP_CTRL_NOCACHE; if (mcl->cc) { g_io_channel_shutdown(mcl->cc, TRUE, NULL); g_io_channel_unref(mcl->cc); mcl->cc = NULL; mcl->state = MCL_IDLE; } else if ((mcl->ctrl & MCAP_CTRL_CACHED) && (mcl->ctrl & MCAP_CTRL_NOCACHE)) { mcl->ctrl &= ~MCAP_CTRL_CACHED; mcl->mi->cached = g_slist_remove(mcl->mi->cached, mcl); mcap_mcl_release(mcl); mcap_mcl_unref(mcl); } } struct mcap_mcl *mcap_mcl_ref(struct mcap_mcl *mcl) { mcl->ref++; DBG("mcap_mcl_ref(%p): ref=%d", mcl, mcl->ref); return mcl; } void mcap_mcl_unref(struct mcap_mcl *mcl) { mcl->ref--; DBG("mcap_mcl_unref(%p): ref=%d", mcl, mcl->ref); if (mcl->ref > 0) return; mcap_mcl_release(mcl); mcap_instance_unref(mcl->mi); g_free(mcl->cb); g_free(mcl); } static gboolean parse_set_opts(struct mcap_mdl_cb *mdl_cb, GError **err, McapMclCb cb1, va_list args) { McapMclCb cb = cb1; struct mcap_mdl_cb *c; c = g_new0(struct mcap_mdl_cb, 1); while (cb != MCAP_MDL_CB_INVALID) { switch (cb) { case MCAP_MDL_CB_CONNECTED: c->mdl_connected = va_arg(args, mcap_mdl_event_cb); break; case MCAP_MDL_CB_CLOSED: c->mdl_closed = va_arg(args, mcap_mdl_event_cb); break; case MCAP_MDL_CB_DELETED: c->mdl_deleted = va_arg(args, mcap_mdl_event_cb); break; case MCAP_MDL_CB_ABORTED: c->mdl_aborted = va_arg(args, mcap_mdl_event_cb); break; case MCAP_MDL_CB_REMOTE_CONN_REQ: c->mdl_conn_req = va_arg(args, mcap_remote_mdl_conn_req_cb); break; case MCAP_MDL_CB_REMOTE_RECONN_REQ: c->mdl_reconn_req = va_arg(args, mcap_remote_mdl_reconn_req_cb); break; case MCAP_MDL_CB_INVALID: default: g_set_error(err, MCAP_ERROR, MCAP_ERROR_INVALID_ARGS, "Unknown option %d", cb); g_free(c); return FALSE; } cb = va_arg(args, int); } /* Set new callbacks */ if (c->mdl_connected) mdl_cb->mdl_connected = c->mdl_connected; if (c->mdl_closed) mdl_cb->mdl_closed = c->mdl_closed; if (c->mdl_deleted) mdl_cb->mdl_deleted = c->mdl_deleted; if (c->mdl_aborted) mdl_cb->mdl_aborted = c->mdl_aborted; if (c->mdl_conn_req) mdl_cb->mdl_conn_req = c->mdl_conn_req; if (c->mdl_reconn_req) mdl_cb->mdl_reconn_req = c->mdl_reconn_req; g_free(c); return TRUE; } gboolean mcap_mcl_set_cb(struct mcap_mcl *mcl, gpointer user_data, GError **gerr, McapMclCb cb1, ...) { va_list args; gboolean ret; va_start(args, cb1); ret = parse_set_opts(mcl->cb, gerr, cb1, args); va_end(args); if (!ret) return FALSE; mcl->cb->user_data = user_data; return TRUE; } void mcap_mcl_get_addr(struct mcap_mcl *mcl, bdaddr_t *addr) { bacpy(addr, &mcl->addr); } static void mcap_del_mdl(gpointer elem, gpointer user_data) { struct mcap_mdl *mdl = elem; gboolean notify = *(gboolean *) user_data; if (notify) mdl->mcl->cb->mdl_deleted(mdl, mdl->mcl->cb->user_data); shutdown_mdl(mdl); mcap_mdl_unref(mdl); } static gboolean check_cmd_req_length(struct mcap_mcl *mcl, void *cmd, uint32_t rlen, uint32_t explen, uint8_t rspcod) { mcap_md_req *req; uint16_t mdl_id; if (rlen != explen) { if (rlen >= sizeof(mcap_md_req)) { req = cmd; mdl_id = ntohs(req->mdl); } else { /* We can't get mdlid */ mdl_id = MCAP_MDLID_RESERVED; } mcap_send_cmd(mcl, rspcod, MCAP_INVALID_PARAM_VALUE, mdl_id, NULL, 0); return FALSE; } return TRUE; } static void process_md_create_mdl_req(struct mcap_mcl *mcl, void *cmd, uint32_t len) { mcap_md_create_mdl_req *req; struct mcap_mdl *mdl; uint16_t mdl_id; uint8_t mdep_id; uint8_t cfga, conf; uint8_t rsp; if (!check_cmd_req_length(mcl, cmd, len, sizeof(mcap_md_create_mdl_req), MCAP_MD_CREATE_MDL_RSP)) return; req = cmd; mdl_id = ntohs(req->mdl); if (mdl_id < MCAP_MDLID_INITIAL || mdl_id > MCAP_MDLID_FINAL) { mcap_send_cmd(mcl, MCAP_MD_CREATE_MDL_RSP, MCAP_INVALID_MDL, mdl_id, NULL, 0); return; } mdep_id = req->mdep; if (mdep_id > MCAP_MDEPID_FINAL) { mcap_send_cmd(mcl, MCAP_MD_CREATE_MDL_RSP, MCAP_INVALID_MDEP, mdl_id, NULL, 0); return; } mdl = get_mdl(mcl, mdl_id); if (mdl && (mdl->state == MDL_WAITING || mdl->state == MDL_DELETING )) { /* * Creation request arrives for a MDL that is being managed * at current moment */ mcap_send_cmd(mcl, MCAP_MD_CREATE_MDL_RSP, MCAP_MDL_BUSY, mdl_id, NULL, 0); return; } cfga = conf = req->conf; /* Callback to upper layer */ rsp = mcl->cb->mdl_conn_req(mcl, mdep_id, mdl_id, &conf, mcl->cb->user_data); if (mcl->state == MCL_IDLE) { /* MCL has been closed int the callback */ return; } if (cfga != 0 && cfga != conf) { /* * Remote device set default configuration but upper profile * has changed it. Protocol Error: force closing the MCL by * remote device using UNSPECIFIED_ERROR response */ mcap_send_cmd(mcl, MCAP_MD_CREATE_MDL_RSP, MCAP_UNSPECIFIED_ERROR, mdl_id, NULL, 0); return; } if (rsp != MCAP_SUCCESS) { mcap_send_cmd(mcl, MCAP_MD_CREATE_MDL_RSP, rsp, mdl_id, NULL, 0); return; } if (!mdl) { mdl = g_new0(struct mcap_mdl, 1); mdl->mcl = mcap_mcl_ref(mcl); mdl->mdlid = mdl_id; mcl->mdls = g_slist_insert_sorted(mcl->mdls, mcap_mdl_ref(mdl), compare_mdl); } else if (mdl->state == MDL_CONNECTED) { /* * MCAP specification says that we should close the MCL if * it is open when we receive a MD_CREATE_MDL_REQ */ shutdown_mdl(mdl); } mdl->mdep_id = mdep_id; mdl->state = MDL_WAITING; mcl->state = MCL_PENDING; mcap_send_cmd(mcl, MCAP_MD_CREATE_MDL_RSP, MCAP_SUCCESS, mdl_id, &conf, 1); } static void process_md_reconnect_mdl_req(struct mcap_mcl *mcl, void *cmd, uint32_t len) { mcap_md_req *req; struct mcap_mdl *mdl; uint16_t mdl_id; uint8_t rsp; if (!check_cmd_req_length(mcl, cmd, len, sizeof(mcap_md_req), MCAP_MD_RECONNECT_MDL_RSP)) return; req = cmd; mdl_id = ntohs(req->mdl); mdl = get_mdl(mcl, mdl_id); if (!mdl) { mcap_send_cmd(mcl, MCAP_MD_RECONNECT_MDL_RSP, MCAP_INVALID_MDL, mdl_id, NULL, 0); return; } else if (mdl->state == MDL_WAITING || mdl->state == MDL_DELETING ) { /* * Creation request arrives for a MDL that is being managed * at current moment */ mcap_send_cmd(mcl, MCAP_MD_RECONNECT_MDL_RSP, MCAP_MDL_BUSY, mdl_id, NULL, 0); return; } /* Callback to upper layer */ rsp = mcl->cb->mdl_reconn_req(mdl, mcl->cb->user_data); if (mcl->state == MCL_IDLE) return; if (rsp != MCAP_SUCCESS) { mcap_send_cmd(mcl, MCAP_MD_RECONNECT_MDL_RSP, rsp, mdl_id, NULL, 0); return; } if (mdl->state == MDL_CONNECTED) shutdown_mdl(mdl); mdl->state = MDL_WAITING; mcl->state = MCL_PENDING; mcap_send_cmd(mcl, MCAP_MD_RECONNECT_MDL_RSP, MCAP_SUCCESS, mdl_id, NULL, 0); } static void process_md_abort_mdl_req(struct mcap_mcl *mcl, void *cmd, uint32_t len) { mcap_md_req *req; GSList *l; struct mcap_mdl *mdl, *abrt; uint16_t mdl_id; if (!check_cmd_req_length(mcl, cmd, len, sizeof(mcap_md_req), MCAP_MD_ABORT_MDL_RSP)) return; req = cmd; mdl_id = ntohs(req->mdl); mcl->state = MCL_CONNECTED; abrt = NULL; for (l = mcl->mdls; l; l = l->next) { mdl = l->data; if (mdl_id == mdl->mdlid && mdl->state == MDL_WAITING) { abrt = mdl; if (mcl->state != MCL_CONNECTED) break; continue; } if (mdl->state == MDL_CONNECTED && mcl->state != MCL_ACTIVE) mcl->state = MCL_ACTIVE; if (abrt && mcl->state == MCL_ACTIVE) break; } if (!abrt) { mcap_send_cmd(mcl, MCAP_MD_ABORT_MDL_RSP, MCAP_INVALID_MDL, mdl_id, NULL, 0); return; } mcl->cb->mdl_aborted(abrt, mcl->cb->user_data); abrt->state = MDL_CLOSED; mcap_send_cmd(mcl, MCAP_MD_ABORT_MDL_RSP, MCAP_SUCCESS, mdl_id, NULL, 0); } static void process_md_delete_mdl_req(struct mcap_mcl *mcl, void *cmd, uint32_t len) { mcap_md_req *req; struct mcap_mdl *mdl, *aux; uint16_t mdlid; gboolean notify; GSList *l; if (!check_cmd_req_length(mcl, cmd, len, sizeof(mcap_md_req), MCAP_MD_DELETE_MDL_RSP)) return; req = cmd; mdlid = ntohs(req->mdl); if (mdlid == MCAP_ALL_MDLIDS) { notify = TRUE; g_slist_foreach(mcl->mdls, mcap_del_mdl, ¬ify); g_slist_free(mcl->mdls); mcl->mdls = NULL; mcl->state = MCL_CONNECTED; goto resp; } if (mdlid < MCAP_MDLID_INITIAL || mdlid > MCAP_MDLID_FINAL) { mcap_send_cmd(mcl, MCAP_MD_DELETE_MDL_RSP, MCAP_INVALID_MDL, mdlid, NULL, 0); return; } for (l = mcl->mdls, mdl = NULL; l; l = l->next) { aux = l->data; if (aux->mdlid == mdlid) { mdl = aux; break; } } if (!mdl || mdl->state == MDL_WAITING) { mcap_send_cmd(mcl, MCAP_MD_DELETE_MDL_RSP, MCAP_INVALID_MDL, mdlid, NULL, 0); return; } mcl->mdls = g_slist_remove(mcl->mdls, mdl); update_mcl_state(mcl); notify = TRUE; mcap_del_mdl(mdl, ¬ify); resp: mcap_send_cmd(mcl, MCAP_MD_DELETE_MDL_RSP, MCAP_SUCCESS, mdlid, NULL, 0); } static void invalid_req_state(struct mcap_mcl *mcl, uint8_t *cmd, uint32_t len) { uint16_t mdlr; error("Invalid cmd received (op code = %d) in state %d", cmd[0], mcl->state); /* * Get previously mdlid sent to generate an appropriate * response if it is possible */ mdlr = len < sizeof(mcap_md_req) ? MCAP_MDLID_RESERVED : ntohs(((mcap_md_req *) cmd)->mdl); mcap_send_cmd(mcl, cmd[0]+1, MCAP_INVALID_OPERATION, mdlr, NULL, 0); } /* Function used to process commands depending of MCL state */ static void proc_req_connected(struct mcap_mcl *mcl, uint8_t *cmd, uint32_t len) { switch (cmd[0]) { case MCAP_MD_CREATE_MDL_REQ: process_md_create_mdl_req(mcl, cmd, len); break; case MCAP_MD_RECONNECT_MDL_REQ: process_md_reconnect_mdl_req(mcl, cmd, len); break; case MCAP_MD_DELETE_MDL_REQ: process_md_delete_mdl_req(mcl, cmd, len); break; default: invalid_req_state(mcl, cmd, len); } } static void proc_req_pending(struct mcap_mcl *mcl, uint8_t *cmd, uint32_t len) { if (cmd[0] == MCAP_MD_ABORT_MDL_REQ) process_md_abort_mdl_req(mcl, cmd, len); else invalid_req_state(mcl, cmd, len); } static void proc_req_active(struct mcap_mcl *mcl, uint8_t *cmd, uint32_t len) { switch (cmd[0]) { case MCAP_MD_CREATE_MDL_REQ: process_md_create_mdl_req(mcl, cmd, len); break; case MCAP_MD_RECONNECT_MDL_REQ: process_md_reconnect_mdl_req(mcl, cmd, len); break; case MCAP_MD_DELETE_MDL_REQ: process_md_delete_mdl_req(mcl, cmd, len); break; default: invalid_req_state(mcl, cmd, len); } } /* Function used to process replies */ static gboolean check_err_rsp(struct mcap_mcl *mcl, mcap_rsp *rsp, uint32_t rlen, uint32_t len, GError **gerr) { mcap_md_req *cmdlast = (mcap_md_req *) mcl->lcmd; int err = MCAP_ERROR_FAILED; gboolean close = FALSE; char *msg; if (rsp->op == MCAP_ERROR_RSP) { msg = "MCAP_ERROR_RSP received"; close = FALSE; goto fail; } /* Check if the response matches with the last request */ if (rlen < sizeof(mcap_rsp) || (mcl->lcmd[0] + 1) != rsp->op) { msg = "Protocol error"; close = FALSE; goto fail; } if (rlen < len) { msg = "Protocol error"; close = FALSE; goto fail; } if (rsp->mdl != cmdlast->mdl) { msg = "MDLID received doesn't match with MDLID sent"; close = TRUE; goto fail; } if (rsp->rc == MCAP_REQUEST_NOT_SUPPORTED) { msg = "Remote does not support opcodes"; mcl->ctrl &= ~MCAP_CTRL_STD_OP; goto fail; } if (rsp->rc == MCAP_UNSPECIFIED_ERROR) { msg = "Unspecified error"; close = TRUE; goto fail; } if (rsp->rc != MCAP_SUCCESS) { msg = error2str(rsp->rc); err = rsp->rc; goto fail; } return FALSE; fail: g_set_error(gerr, MCAP_ERROR, err, "%s", msg); return close; } static gboolean process_md_create_mdl_rsp(struct mcap_mcl *mcl, mcap_rsp *rsp, uint32_t len) { mcap_md_create_mdl_req *cmdlast = (mcap_md_create_mdl_req *) mcl->lcmd; struct mcap_mdl_op_cb *conn = mcl->priv_data; mcap_mdl_operation_conf_cb connect_cb = conn->cb.op_conf; gpointer user_data = conn->user_data; struct mcap_mdl *mdl = conn->mdl; uint8_t conf = cmdlast->conf; gboolean close; GError *gerr = NULL; close = check_err_rsp(mcl, rsp, len, sizeof(mcap_rsp) + 1, &gerr); g_free(mcl->lcmd); mcl->lcmd = NULL; mcl->req = MCL_AVAILABLE; if (gerr) goto fail; /* Check if preferences changed */ if (conf != 0x00 && rsp->data[0] != conf) { g_set_error(&gerr, MCAP_ERROR, MCAP_ERROR_FAILED, "Configuration changed"); close = TRUE; goto fail; } connect_cb(mdl, rsp->data[0], gerr, user_data); return close; fail: connect_cb(NULL, 0, gerr, user_data); mcl->mdls = g_slist_remove(mcl->mdls, mdl); mcap_mdl_unref(mdl); g_error_free(gerr); update_mcl_state(mcl); return close; } static gboolean process_md_reconnect_mdl_rsp(struct mcap_mcl *mcl, mcap_rsp *rsp, uint32_t len) { struct mcap_mdl_op_cb *reconn = mcl->priv_data; mcap_mdl_operation_cb reconn_cb = reconn->cb.op; gpointer user_data = reconn->user_data; struct mcap_mdl *mdl = reconn->mdl; GError *gerr = NULL; gboolean close; close = check_err_rsp(mcl, rsp, len, sizeof(mcap_rsp), &gerr); g_free(mcl->lcmd); mcl->lcmd = NULL; mcl->req = MCL_AVAILABLE; reconn_cb(mdl, gerr, user_data); if (!gerr) return close; g_error_free(gerr); shutdown_mdl(mdl); update_mcl_state(mcl); if (rsp->rc != MCAP_INVALID_MDL) return close; /* Remove cached mdlid */ mcl->mdls = g_slist_remove(mcl->mdls, mdl); mcl->cb->mdl_deleted(mdl, mcl->cb->user_data); mcap_mdl_unref(mdl); return close; } static gboolean process_md_abort_mdl_rsp(struct mcap_mcl *mcl, mcap_rsp *rsp, uint32_t len) { struct mcap_mdl_op_cb *abrt = mcl->priv_data; mcap_mdl_notify_cb abrt_cb = abrt->cb.notify; gpointer user_data = abrt->user_data; struct mcap_mdl *mdl = abrt->mdl; GError *gerr = NULL; gboolean close; close = check_err_rsp(mcl, rsp, len, sizeof(mcap_rsp), &gerr); g_free(mcl->lcmd); mcl->lcmd = NULL; mcl->req = MCL_AVAILABLE; abrt_cb(gerr, user_data); shutdown_mdl(mdl); if (len >= sizeof(mcap_rsp) && rsp->rc == MCAP_INVALID_MDL) { mcl->mdls = g_slist_remove(mcl->mdls, mdl); mcl->cb->mdl_deleted(mdl, mcl->cb->user_data); mcap_mdl_unref(mdl); } if (gerr) g_error_free(gerr); update_mcl_state(mcl); return close; } static void restore_mdl(gpointer elem, gpointer data) { struct mcap_mdl *mdl = elem; if (mdl->state == MDL_DELETING) { if (mdl->dc) mdl->state = MDL_CONNECTED; else mdl->state = MDL_CLOSED; } else if (mdl->state == MDL_CLOSED) mdl->mcl->cb->mdl_closed(mdl, mdl->mcl->cb->user_data); } static void check_mdl_del_err(struct mcap_mdl *mdl, mcap_rsp *rsp) { if (rsp->rc != MCAP_ERROR_INVALID_MDL) { restore_mdl(mdl, NULL); return; } /* MDL does not exist in remote side, we can delete it */ mdl->mcl->mdls = g_slist_remove(mdl->mcl->mdls, mdl); mcap_mdl_unref(mdl); } static gboolean process_md_delete_mdl_rsp(struct mcap_mcl *mcl, mcap_rsp *rsp, uint32_t len) { struct mcap_mdl_op_cb *del = mcl->priv_data; struct mcap_mdl *mdl = del->mdl; mcap_mdl_notify_cb deleted_cb = del->cb.notify; gpointer user_data = del->user_data; mcap_md_req *cmdlast = (mcap_md_req *) mcl->lcmd; uint16_t mdlid = ntohs(cmdlast->mdl); GError *gerr = NULL; gboolean close; gboolean notify = FALSE; close = check_err_rsp(mcl, rsp, len, sizeof(mcap_rsp), &gerr); g_free(mcl->lcmd); mcl->lcmd = NULL; mcl->req = MCL_AVAILABLE; if (gerr) { if (mdl) check_mdl_del_err(mdl, rsp); else g_slist_foreach(mcl->mdls, restore_mdl, NULL); deleted_cb(gerr, user_data); g_error_free(gerr); return close; } if (mdlid == MCAP_ALL_MDLIDS) { g_slist_foreach(mcl->mdls, mcap_del_mdl, ¬ify); g_slist_free(mcl->mdls); mcl->mdls = NULL; mcl->state = MCL_CONNECTED; } else { mcl->mdls = g_slist_remove(mcl->mdls, mdl); update_mcl_state(mcl); mcap_del_mdl(mdl, ¬ify); } deleted_cb(gerr, user_data); return close; } static void post_process_rsp(struct mcap_mcl *mcl, struct mcap_mdl_op_cb *op) { if (mcl->priv_data != op) { /* * Queued MCAP request in some callback. * We should not delete the mcl private data */ free_mcap_mdl_op(op); } else { /* * This is not a queued request. It's safe * delete the mcl private data here. */ free_mcl_priv_data(mcl); } } static void proc_response(struct mcap_mcl *mcl, void *buf, uint32_t len) { struct mcap_mdl_op_cb *op = mcl->priv_data; mcap_rsp *rsp = buf; gboolean close; RELEASE_TIMER(mcl); switch (mcl->lcmd[0] + 1) { case MCAP_MD_CREATE_MDL_RSP: close = process_md_create_mdl_rsp(mcl, rsp, len); post_process_rsp(mcl, op); break; case MCAP_MD_RECONNECT_MDL_RSP: close = process_md_reconnect_mdl_rsp(mcl, rsp, len); post_process_rsp(mcl, op); break; case MCAP_MD_ABORT_MDL_RSP: close = process_md_abort_mdl_rsp(mcl, rsp, len); post_process_rsp(mcl, op); break; case MCAP_MD_DELETE_MDL_RSP: close = process_md_delete_mdl_rsp(mcl, rsp, len); post_process_rsp(mcl, op); break; default: DBG("Unknown cmd response received (op code = %d)", rsp->op); close = TRUE; break; } if (close) { mcl->mi->mcl_disconnected_cb(mcl, mcl->mi->user_data); mcap_cache_mcl(mcl); } } static void proc_cmd(struct mcap_mcl *mcl, uint8_t *cmd, uint32_t len) { GError *gerr = NULL; if (cmd[0] > MCAP_MD_SYNC_INFO_IND || (cmd[0] > MCAP_MD_DELETE_MDL_RSP && cmd[0] < MCAP_MD_SYNC_CAP_REQ)) { error("Unknown cmd received (op code = %d)", cmd[0]); mcap_send_cmd(mcl, MCAP_ERROR_RSP, MCAP_INVALID_OP_CODE, MCAP_MDLID_RESERVED, NULL, 0); return; } if (cmd[0] >= MCAP_MD_SYNC_CAP_REQ && cmd[0] <= MCAP_MD_SYNC_INFO_IND) { proc_sync_cmd(mcl, cmd, len); return; } if (!(mcl->ctrl & MCAP_CTRL_STD_OP)) { /* In case the remote device doesn't work correctly */ error("Remote device does not support opcodes, cmd ignored"); return; } if (mcl->req == MCL_WAITING_RSP) { if (cmd[0] & 0x01) { /* Request arrived when a response is expected */ if (mcl->role == MCL_INITIATOR) /* ignore */ return; /* Initiator will ignore our last request */ RELEASE_TIMER(mcl); mcl->req = MCL_AVAILABLE; g_set_error(&gerr, MCAP_ERROR, MCAP_ERROR_REQ_IGNORED, "Initiator sent a request with more priority"); mcap_notify_error(mcl, gerr); proc_req[mcl->state](mcl, cmd, len); return; } proc_response(mcl, cmd, len); } else if (cmd[0] & 0x01) proc_req[mcl->state](mcl, cmd, len); } static gboolean mdl_event_cb(GIOChannel *chan, GIOCondition cond, gpointer data) { struct mcap_mdl *mdl = data; gboolean notify; DBG("Close MDL %d", mdl->mdlid); notify = (mdl->state == MDL_CONNECTED); shutdown_mdl(mdl); update_mcl_state(mdl->mcl); if (notify) { /*Callback to upper layer */ mdl->mcl->cb->mdl_closed(mdl, mdl->mcl->cb->user_data); } return FALSE; } static void mcap_connect_mdl_cb(GIOChannel *chan, GError *conn_err, gpointer data) { struct mcap_mdl_op_cb *con = data; struct mcap_mdl *mdl = con->mdl; mcap_mdl_operation_cb cb = con->cb.op; gpointer user_data = con->user_data; DBG("mdl connect callback"); if (conn_err) { DBG("ERROR: mdl connect callback"); mdl->state = MDL_CLOSED; g_io_channel_unref(mdl->dc); mdl->dc = NULL; cb(mdl, conn_err, user_data); return; } mdl->state = MDL_CONNECTED; mdl->wid = g_io_add_watch_full(mdl->dc, G_PRIORITY_DEFAULT, G_IO_ERR | G_IO_HUP | G_IO_NVAL, (GIOFunc) mdl_event_cb, mcap_mdl_ref(mdl), (GDestroyNotify) mcap_mdl_unref); cb(mdl, conn_err, user_data); } gboolean mcap_connect_mdl(struct mcap_mdl *mdl, uint8_t mode, uint16_t dcpsm, mcap_mdl_operation_cb connect_cb, gpointer user_data, GDestroyNotify destroy, GError **err) { struct mcap_mdl_op_cb *con; if (mdl->state != MDL_WAITING) { g_set_error(err, MCAP_ERROR, MCAP_ERROR_INVALID_MDL, "%s", error2str(MCAP_INVALID_MDL)); return FALSE; } if ((mode != BT_IO_MODE_ERTM) && (mode != BT_IO_MODE_STREAMING)) { g_set_error(err, MCAP_ERROR, MCAP_ERROR_INVALID_ARGS, "Invalid MDL configuration"); return FALSE; } con = g_new0(struct mcap_mdl_op_cb, 1); con->mdl = mcap_mdl_ref(mdl); con->cb.op = connect_cb; con->destroy = destroy; con->user_data = user_data; mdl->dc = bt_io_connect(mcap_connect_mdl_cb, con, (GDestroyNotify) free_mcap_mdl_op, err, BT_IO_OPT_SOURCE_BDADDR, &mdl->mcl->mi->src, BT_IO_OPT_DEST_BDADDR, &mdl->mcl->addr, BT_IO_OPT_PSM, dcpsm, BT_IO_OPT_MTU, MCAP_DC_MTU, BT_IO_OPT_SEC_LEVEL, mdl->mcl->mi->sec, BT_IO_OPT_MODE, mode, BT_IO_OPT_INVALID); if (!mdl->dc) { DBG("MDL Connection error"); mdl->state = MDL_CLOSED; mcap_mdl_unref(con->mdl); g_free(con); return FALSE; } return TRUE; } static gboolean mcl_control_cb(GIOChannel *chan, GIOCondition cond, gpointer data) { GError *gerr = NULL; struct mcap_mcl *mcl = data; int sk, len; uint8_t buf[MCAP_CC_MTU]; if (cond & (G_IO_ERR | G_IO_HUP | G_IO_NVAL)) goto fail; sk = g_io_channel_unix_get_fd(chan); len = read(sk, buf, sizeof(buf)); if (len < 0) goto fail; proc_cmd(mcl, buf, (uint32_t) len); return TRUE; fail: if (mcl->state != MCL_IDLE) { if (mcl->req == MCL_WAITING_RSP) { /* notify error in pending callback */ g_set_error(&gerr, MCAP_ERROR, MCAP_ERROR_MCL_CLOSED, "MCL closed"); mcap_notify_error(mcl, gerr); g_error_free(gerr); } mcl->mi->mcl_disconnected_cb(mcl, mcl->mi->user_data); } mcap_cache_mcl(mcl); return FALSE; } static void mcap_connect_mcl_cb(GIOChannel *chan, GError *conn_err, gpointer user_data) { char dstaddr[18]; struct connect_mcl *con = user_data; struct mcap_mcl *aux, *mcl = con->mcl; mcap_mcl_connect_cb connect_cb = con->connect_cb; gpointer data = con->user_data; GError *gerr = NULL; mcl->ctrl &= ~MCAP_CTRL_CONN; if (conn_err) { if (mcl->ctrl & MCAP_CTRL_FREE) { mcap_mcl_release(mcl); mcl->mi->mcl_uncached_cb(mcl, mcl->mi->user_data); } connect_cb(NULL, conn_err, data); return; } ba2str(&mcl->addr, dstaddr); aux = find_mcl(mcl->mi->mcls, &mcl->addr); if (aux) { /* Double MCL connection case */ error("MCL error: Device %s is already connected", dstaddr); g_set_error(&gerr, MCAP_ERROR, MCAP_ERROR_ALREADY_EXISTS, "MCL %s is already connected", dstaddr); connect_cb(NULL, gerr, data); g_error_free(gerr); return; } mcl->state = MCL_CONNECTED; mcl->role = MCL_INITIATOR; mcl->req = MCL_AVAILABLE; mcl->ctrl |= MCAP_CTRL_STD_OP; mcap_sync_init(mcl); if (mcl->ctrl & MCAP_CTRL_CACHED) mcap_uncache_mcl(mcl); else { mcl->ctrl &= ~MCAP_CTRL_FREE; mcl->mi->mcls = g_slist_prepend(mcl->mi->mcls, mcap_mcl_ref(mcl)); } mcl->wid = g_io_add_watch_full(mcl->cc, G_PRIORITY_DEFAULT, G_IO_IN | G_IO_ERR | G_IO_HUP | G_IO_NVAL, (GIOFunc) mcl_control_cb, mcap_mcl_ref(mcl), (GDestroyNotify) mcap_mcl_unref); connect_cb(mcl, gerr, data); } static void set_mdl_properties(GIOChannel *chan, struct mcap_mdl *mdl) { struct mcap_mcl *mcl = mdl->mcl; mdl->state = MDL_CONNECTED; mdl->dc = g_io_channel_ref(chan); mdl->wid = g_io_add_watch_full(mdl->dc, G_PRIORITY_DEFAULT, G_IO_ERR | G_IO_HUP | G_IO_NVAL, (GIOFunc) mdl_event_cb, mcap_mdl_ref(mdl), (GDestroyNotify) mcap_mdl_unref); mcl->state = MCL_ACTIVE; mcl->cb->mdl_connected(mdl, mcl->cb->user_data); } static void mcl_io_destroy(gpointer data) { struct connect_mcl *con = data; mcap_mcl_unref(con->mcl); if (con->destroy) con->destroy(con->user_data); g_free(con); } gboolean mcap_create_mcl(struct mcap_instance *mi, const bdaddr_t *addr, uint16_t ccpsm, mcap_mcl_connect_cb connect_cb, gpointer user_data, GDestroyNotify destroy, GError **err) { struct mcap_mcl *mcl; struct connect_mcl *con; uint16_t val; mcl = find_mcl(mi->mcls, addr); if (mcl) { g_set_error(err, MCAP_ERROR, MCAP_ERROR_ALREADY_EXISTS, "MCL is already connected."); return FALSE; } mcl = find_mcl(mi->cached, addr); if (!mcl) { mcl = g_new0(struct mcap_mcl, 1); mcl->mi = mcap_instance_ref(mi); mcl->state = MCL_IDLE; bacpy(&mcl->addr, addr); set_default_cb(mcl); if (util_getrandom(&val, sizeof(val), 0) < 0) { mcap_instance_unref(mcl->mi); g_free(mcl->cb); g_free(mcl); return FALSE; } mcl->next_mdl = (val % MCAP_MDLID_FINAL) + 1; } mcl->ctrl |= MCAP_CTRL_CONN; con = g_new0(struct connect_mcl, 1); con->mcl = mcap_mcl_ref(mcl); con->connect_cb = connect_cb; con->destroy = destroy; con->user_data = user_data; mcl->cc = bt_io_connect(mcap_connect_mcl_cb, con, mcl_io_destroy, err, BT_IO_OPT_SOURCE_BDADDR, &mi->src, BT_IO_OPT_DEST_BDADDR, addr, BT_IO_OPT_PSM, ccpsm, BT_IO_OPT_MTU, MCAP_CC_MTU, BT_IO_OPT_SEC_LEVEL, mi->sec, BT_IO_OPT_MODE, BT_IO_MODE_ERTM, BT_IO_OPT_INVALID); if (!mcl->cc) { mcl->ctrl &= ~MCAP_CTRL_CONN; if (mcl->ctrl & MCAP_CTRL_FREE) { mcap_mcl_release(mcl); mcl->mi->mcl_uncached_cb(mcl, mcl->mi->user_data); } mcap_mcl_unref(con->mcl); g_free(con); return FALSE; } return TRUE; } static void connect_dc_event_cb(GIOChannel *chan, GError *gerr, gpointer user_data) { struct mcap_instance *mi = user_data; struct mcap_mcl *mcl; struct mcap_mdl *mdl; GError *err = NULL; bdaddr_t dst; GSList *l; if (gerr) return; bt_io_get(chan, &err, BT_IO_OPT_DEST_BDADDR, &dst, BT_IO_OPT_INVALID); if (err) { error("%s", err->message); g_error_free(err); goto drop; } mcl = find_mcl(mi->mcls, &dst); if (!mcl || mcl->state != MCL_PENDING) goto drop; for (l = mcl->mdls; l; l = l->next) { mdl = l->data; if (mdl->state == MDL_WAITING) { set_mdl_properties(chan, mdl); return; } } drop: g_io_channel_shutdown(chan, TRUE, NULL); } static void set_mcl_conf(GIOChannel *chan, struct mcap_mcl *mcl) { gboolean reconn; mcl->state = MCL_CONNECTED; mcl->role = MCL_ACCEPTOR; mcl->req = MCL_AVAILABLE; mcl->cc = g_io_channel_ref(chan); mcl->ctrl |= MCAP_CTRL_STD_OP; mcap_sync_init(mcl); reconn = (mcl->ctrl & MCAP_CTRL_CACHED); if (reconn) mcap_uncache_mcl(mcl); else mcl->mi->mcls = g_slist_prepend(mcl->mi->mcls, mcap_mcl_ref(mcl)); mcl->wid = g_io_add_watch_full(mcl->cc, G_PRIORITY_DEFAULT, G_IO_IN | G_IO_ERR | G_IO_HUP | G_IO_NVAL, (GIOFunc) mcl_control_cb, mcap_mcl_ref(mcl), (GDestroyNotify) mcap_mcl_unref); /* Callback to report new MCL */ if (reconn) mcl->mi->mcl_reconnected_cb(mcl, mcl->mi->user_data); else mcl->mi->mcl_connected_cb(mcl, mcl->mi->user_data); } static void connect_mcl_event_cb(GIOChannel *chan, GError *gerr, gpointer user_data) { struct mcap_instance *mi = user_data; struct mcap_mcl *mcl; bdaddr_t dst; char address[18], srcstr[18]; GError *err = NULL; uint16_t val; if (gerr) return; bt_io_get(chan, &err, BT_IO_OPT_DEST_BDADDR, &dst, BT_IO_OPT_DEST, address, BT_IO_OPT_INVALID); if (err) { error("%s", err->message); g_error_free(err); goto drop; } ba2str(&mi->src, srcstr); mcl = find_mcl(mi->mcls, &dst); if (mcl) { error("Control channel already created with %s on adapter %s", address, srcstr); goto drop; } mcl = find_mcl(mi->cached, &dst); if (!mcl) { mcl = g_new0(struct mcap_mcl, 1); mcl->mi = mcap_instance_ref(mi); bacpy(&mcl->addr, &dst); set_default_cb(mcl); if (util_getrandom(&val, sizeof(val), 0) < 0) { mcap_instance_unref(mcl->mi); g_free(mcl->cb); g_free(mcl); goto drop; } mcl->next_mdl = (val % MCAP_MDLID_FINAL) + 1; } set_mcl_conf(chan, mcl); return; drop: g_io_channel_shutdown(chan, TRUE, NULL); } struct mcap_instance *mcap_create_instance(const bdaddr_t *src, BtIOSecLevel sec, uint16_t ccpsm, uint16_t dcpsm, mcap_mcl_event_cb mcl_connected, mcap_mcl_event_cb mcl_reconnected, mcap_mcl_event_cb mcl_disconnected, mcap_mcl_event_cb mcl_uncached, mcap_info_ind_event_cb mcl_sync_info_ind, gpointer user_data, GError **gerr) { struct mcap_instance *mi; if (sec < BT_IO_SEC_MEDIUM) { g_set_error(gerr, MCAP_ERROR, MCAP_ERROR_INVALID_ARGS, "Security level can't be minor of %d", BT_IO_SEC_MEDIUM); return NULL; } if (!(mcl_connected && mcl_reconnected && mcl_disconnected && mcl_uncached)) { g_set_error(gerr, MCAP_ERROR, MCAP_ERROR_INVALID_ARGS, "The callbacks can't be null"); return NULL; } mi = g_new0(struct mcap_instance, 1); bacpy(&mi->src, src); mi->sec = sec; mi->mcl_connected_cb = mcl_connected; mi->mcl_reconnected_cb = mcl_reconnected; mi->mcl_disconnected_cb = mcl_disconnected; mi->mcl_uncached_cb = mcl_uncached; mi->mcl_sync_infoind_cb = mcl_sync_info_ind; mi->user_data = user_data; mi->csp_enabled = FALSE; /* Listen incoming connections in control channel */ mi->ccio = bt_io_listen(connect_mcl_event_cb, NULL, mi, NULL, gerr, BT_IO_OPT_SOURCE_BDADDR, &mi->src, BT_IO_OPT_PSM, ccpsm, BT_IO_OPT_MTU, MCAP_CC_MTU, BT_IO_OPT_SEC_LEVEL, sec, BT_IO_OPT_MODE, BT_IO_MODE_ERTM, BT_IO_OPT_INVALID); if (!mi->ccio) { error("%s", (*gerr)->message); g_free(mi); return NULL; } /* Listen incoming connections in data channels */ mi->dcio = bt_io_listen(connect_dc_event_cb, NULL, mi, NULL, gerr, BT_IO_OPT_SOURCE_BDADDR, &mi->src, BT_IO_OPT_PSM, dcpsm, BT_IO_OPT_MTU, MCAP_DC_MTU, BT_IO_OPT_SEC_LEVEL, sec, BT_IO_OPT_INVALID); if (!mi->dcio) { g_io_channel_shutdown(mi->ccio, TRUE, NULL); g_io_channel_unref(mi->ccio); mi->ccio = NULL; error("%s", (*gerr)->message); g_free(mi); return NULL; } /* Initialize random seed to generate mdlids for this instance */ srand(time(NULL)); return mcap_instance_ref(mi); } void mcap_release_instance(struct mcap_instance *mi) { GSList *l; if (!mi) return; if (mi->ccio) { g_io_channel_shutdown(mi->ccio, TRUE, NULL); g_io_channel_unref(mi->ccio); mi->ccio = NULL; } if (mi->dcio) { g_io_channel_shutdown(mi->dcio, TRUE, NULL); g_io_channel_unref(mi->dcio); mi->dcio = NULL; } for (l = mi->mcls; l; l = l->next) { mcap_mcl_release(l->data); mcap_mcl_unref(l->data); } g_slist_free(mi->mcls); mi->mcls = NULL; for (l = mi->cached; l; l = l->next) { mcap_mcl_release(l->data); mcap_mcl_unref(l->data); } g_slist_free(mi->cached); mi->cached = NULL; } struct mcap_instance *mcap_instance_ref(struct mcap_instance *mi) { mi->ref++; DBG("mcap_instance_ref(%p): ref=%d", mi, mi->ref); return mi; } void mcap_instance_unref(struct mcap_instance *mi) { mi->ref--; DBG("mcap_instance_unref(%p): ref=%d", mi, mi->ref); if (mi->ref > 0) return; mcap_release_instance(mi); g_free(mi); } uint16_t mcap_get_ctrl_psm(struct mcap_instance *mi, GError **err) { uint16_t lpsm; if (!(mi && mi->ccio)) { g_set_error(err, MCAP_ERROR, MCAP_ERROR_INVALID_ARGS, "Invalid MCAP instance"); return 0; } if (!bt_io_get(mi->ccio, err, BT_IO_OPT_PSM, &lpsm, BT_IO_OPT_INVALID)) return 0; return lpsm; } uint16_t mcap_get_data_psm(struct mcap_instance *mi, GError **err) { uint16_t lpsm; if (!(mi && mi->dcio)) { g_set_error(err, MCAP_ERROR, MCAP_ERROR_INVALID_ARGS, "Invalid MCAP instance"); return 0; } if (!bt_io_get(mi->dcio, err, BT_IO_OPT_PSM, &lpsm, BT_IO_OPT_INVALID)) return 0; return lpsm; } gboolean mcap_set_data_chan_mode(struct mcap_instance *mi, uint8_t mode, GError **err) { if (!(mi && mi->dcio)) { g_set_error(err, MCAP_ERROR, MCAP_ERROR_INVALID_ARGS, "Invalid MCAP instance"); return FALSE; } return bt_io_set(mi->dcio, err, BT_IO_OPT_MODE, mode, BT_IO_OPT_INVALID); } struct mcap_mdl *mcap_mdl_ref(struct mcap_mdl *mdl) { mdl->ref++; DBG("mcap_mdl_ref(%p): ref=%d", mdl, mdl->ref); return mdl; } void mcap_mdl_unref(struct mcap_mdl *mdl) { mdl->ref--; DBG("mcap_mdl_unref(%p): ref=%d", mdl, mdl->ref); if (mdl->ref > 0) return; free_mdl(mdl); } static int send_sync_cmd(struct mcap_mcl *mcl, const void *buf, uint32_t size) { int sock; if (mcl->cc == NULL) return -1; sock = g_io_channel_unix_get_fd(mcl->cc); return mcap_send_data(sock, buf, size); } static int send_unsupported_cap_req(struct mcap_mcl *mcl) { mcap_md_sync_cap_rsp *cmd; int sent; cmd = g_new0(mcap_md_sync_cap_rsp, 1); cmd->op = MCAP_MD_SYNC_CAP_RSP; cmd->rc = MCAP_REQUEST_NOT_SUPPORTED; sent = send_sync_cmd(mcl, cmd, sizeof(*cmd)); g_free(cmd); return sent; } static int send_unsupported_set_req(struct mcap_mcl *mcl) { mcap_md_sync_set_rsp *cmd; int sent; cmd = g_new0(mcap_md_sync_set_rsp, 1); cmd->op = MCAP_MD_SYNC_SET_RSP; cmd->rc = MCAP_REQUEST_NOT_SUPPORTED; sent = send_sync_cmd(mcl, cmd, sizeof(*cmd)); g_free(cmd); return sent; } static void reset_tmstamp(struct mcap_csp *csp, struct timespec *base_time, uint64_t new_tmstamp) { csp->base_tmstamp = new_tmstamp; if (base_time) csp->base_time = *base_time; else clock_gettime(CLK, &csp->base_time); } void mcap_sync_init(struct mcap_mcl *mcl) { if (!mcl->mi->csp_enabled) { mcl->csp = NULL; return; } mcl->csp = g_new0(struct mcap_csp, 1); mcl->csp->rem_req_acc = 10000; /* safe divisor */ mcl->csp->set_data = NULL; mcl->csp->csp_priv_data = NULL; reset_tmstamp(mcl->csp, NULL, 0); } void mcap_sync_stop(struct mcap_mcl *mcl) { if (!mcl->csp) return; if (mcl->csp->ind_timer) g_source_remove(mcl->csp->ind_timer); if (mcl->csp->set_timer) g_source_remove(mcl->csp->set_timer); if (mcl->csp->set_data) g_free(mcl->csp->set_data); if (mcl->csp->csp_priv_data) g_free(mcl->csp->csp_priv_data); mcl->csp->ind_timer = 0; mcl->csp->set_timer = 0; mcl->csp->set_data = NULL; mcl->csp->csp_priv_data = NULL; g_free(mcl->csp); mcl->csp = NULL; } static uint64_t time_us(struct timespec *tv) { return tv->tv_sec * 1000000ll + tv->tv_nsec / 1000ll; } static int64_t bt2us(int bt) { return bt * 312.5; } static int bt2ms(int bt) { return bt * 312.5 / 1000; } static int btoffset(uint32_t btclk1, uint32_t btclk2) { int offset = btclk2 - btclk1; if (offset <= -MCAP_BTCLOCK_HALF) offset += MCAP_BTCLOCK_FIELD; else if (offset > MCAP_BTCLOCK_HALF) offset -= MCAP_BTCLOCK_FIELD; return offset; } static int btdiff(uint32_t btclk1, uint32_t btclk2) { return btoffset(btclk1, btclk2); } static gboolean valid_btclock(uint32_t btclk) { return btclk <= MCAP_BTCLOCK_MAX; } /* This call may fail; either deal with retry or use read_btclock_retry */ static gboolean read_btclock(struct mcap_mcl *mcl, uint32_t *btclock, uint16_t *btaccuracy) { /* * FIXME: btd_adapter_read_clock(...) always return FALSE, current * code doesn't support CSP (Clock Synchronization Protocol). To avoid * build dependancy on struct 'btd_adapter', removing this code. */ return FALSE; } static gboolean read_btclock_retry(struct mcap_mcl *mcl, uint32_t *btclock, uint16_t *btaccuracy) { int retries = 5; while (--retries >= 0) { if (read_btclock(mcl, btclock, btaccuracy)) return TRUE; DBG("CSP: retrying to read bt clock..."); } return FALSE; } static gboolean get_btrole(struct mcap_mcl *mcl) { int sock, flags; socklen_t len; if (mcl->cc == NULL) return -1; sock = g_io_channel_unix_get_fd(mcl->cc); len = sizeof(flags); if (getsockopt(sock, SOL_L2CAP, L2CAP_LM, &flags, &len)) DBG("CSP: could not read role"); return flags & L2CAP_LM_MASTER; } uint64_t mcap_get_timestamp(struct mcap_mcl *mcl, struct timespec *given_time) { struct timespec now; uint64_t tmstamp; if (!mcl->csp) return MCAP_TMSTAMP_DONTSET; if (given_time) now = *given_time; else if (clock_gettime(CLK, &now) < 0) return MCAP_TMSTAMP_DONTSET; tmstamp = time_us(&now) - time_us(&mcl->csp->base_time) + mcl->csp->base_tmstamp; return tmstamp; } uint32_t mcap_get_btclock(struct mcap_mcl *mcl) { uint32_t btclock; uint16_t accuracy; if (!mcl->csp) return MCAP_BTCLOCK_IMMEDIATE; if (!read_btclock_retry(mcl, &btclock, &accuracy)) btclock = 0xffffffff; return btclock; } static gboolean initialize_caps(struct mcap_mcl *mcl) { struct timespec t1, t2; int latencies[SAMPLE_COUNT]; int latency, avg, dev; uint32_t btclock; uint16_t btaccuracy; int i; int retries; clock_getres(CLK, &t1); _caps.ts_res = time_us(&t1); if (_caps.ts_res < 1) _caps.ts_res = 1; _caps.ts_acc = 20; /* ppm, estimated */ /* A little exercise before measuing latency */ clock_gettime(CLK, &t1); read_btclock_retry(mcl, &btclock, &btaccuracy); /* Read clock a number of times and measure latency */ avg = 0; i = 0; retries = MAX_RETRIES; while (i < SAMPLE_COUNT && retries > 0) { clock_gettime(CLK, &t1); if (!read_btclock(mcl, &btclock, &btaccuracy)) { retries--; continue; } clock_gettime(CLK, &t2); latency = time_us(&t2) - time_us(&t1); latencies[i] = latency; avg += latency; i++; } if (retries <= 0) return FALSE; /* Calculate average and deviation */ avg /= SAMPLE_COUNT; dev = 0; for (i = 0; i < SAMPLE_COUNT; ++i) dev += abs(latencies[i] - avg); dev /= SAMPLE_COUNT; /* Calculate corrected average, without 'freak' latencies */ latency = 0; for (i = 0; i < SAMPLE_COUNT; ++i) { if (latencies[i] > (avg + dev * 6)) latency += avg; else latency += latencies[i]; } latency /= SAMPLE_COUNT; _caps.latency = latency; _caps.preempt_thresh = latency * 4; _caps.syncleadtime_ms = latency * 50 / 1000; csp_caps_initialized = TRUE; return TRUE; } static struct csp_caps *caps(struct mcap_mcl *mcl) { if (!csp_caps_initialized) if (!initialize_caps(mcl)) { /* Temporary failure in reading BT clock */ return NULL; } return &_caps; } static int send_sync_cap_rsp(struct mcap_mcl *mcl, uint8_t rspcode, uint8_t btclockres, uint16_t synclead, uint16_t tmstampres, uint16_t tmstampacc) { mcap_md_sync_cap_rsp *rsp; int sent; rsp = g_new0(mcap_md_sync_cap_rsp, 1); rsp->op = MCAP_MD_SYNC_CAP_RSP; rsp->rc = rspcode; rsp->btclock = btclockres; rsp->sltime = htons(synclead); rsp->timestnr = htons(tmstampres); rsp->timestna = htons(tmstampacc); sent = send_sync_cmd(mcl, rsp, sizeof(*rsp)); g_free(rsp); return sent; } static void proc_sync_cap_req(struct mcap_mcl *mcl, uint8_t *cmd, uint32_t len) { mcap_md_sync_cap_req *req; uint16_t required_accuracy; uint16_t our_accuracy; uint32_t btclock; uint16_t btres; if (len != sizeof(mcap_md_sync_cap_req)) { send_sync_cap_rsp(mcl, MCAP_INVALID_PARAM_VALUE, 0, 0, 0, 0); return; } if (!caps(mcl)) { send_sync_cap_rsp(mcl, MCAP_RESOURCE_UNAVAILABLE, 0, 0, 0, 0); return; } req = (mcap_md_sync_cap_req *) cmd; required_accuracy = ntohs(req->timest); our_accuracy = caps(mcl)->ts_acc; btres = 0; if (required_accuracy < our_accuracy || required_accuracy < 1) { send_sync_cap_rsp(mcl, MCAP_RESOURCE_UNAVAILABLE, 0, 0, 0, 0); return; } if (!read_btclock_retry(mcl, &btclock, &btres)) { send_sync_cap_rsp(mcl, MCAP_RESOURCE_UNAVAILABLE, 0, 0, 0, 0); return; } mcl->csp->remote_caps = 1; mcl->csp->rem_req_acc = required_accuracy; send_sync_cap_rsp(mcl, MCAP_SUCCESS, btres, caps(mcl)->syncleadtime_ms, caps(mcl)->ts_res, our_accuracy); } static int send_sync_set_rsp(struct mcap_mcl *mcl, uint8_t rspcode, uint32_t btclock, uint64_t timestamp, uint16_t tmstampres) { mcap_md_sync_set_rsp *rsp; int sent; rsp = g_new0(mcap_md_sync_set_rsp, 1); rsp->op = MCAP_MD_SYNC_SET_RSP; rsp->rc = rspcode; rsp->btclock = htonl(btclock); rsp->timestst = hton64(timestamp); rsp->timestsa = htons(tmstampres); sent = send_sync_cmd(mcl, rsp, sizeof(*rsp)); g_free(rsp); return sent; } static gboolean get_all_clocks(struct mcap_mcl *mcl, uint32_t *btclock, struct timespec *base_time, uint64_t *timestamp) { int latency; int retry = 5; uint16_t btres; struct timespec t0; if (!caps(mcl)) return FALSE; latency = caps(mcl)->preempt_thresh + 1; while (latency > caps(mcl)->preempt_thresh && --retry >= 0) { if (clock_gettime(CLK, &t0) < 0) return FALSE; if (!read_btclock(mcl, btclock, &btres)) continue; if (clock_gettime(CLK, base_time) < 0) return FALSE; /* * Tries to detect preemption between clock_gettime * and read_btclock by measuring transaction time */ latency = time_us(base_time) - time_us(&t0); } if (retry < 0) return FALSE; *timestamp = mcap_get_timestamp(mcl, base_time); return TRUE; } static gboolean sync_send_indication(gpointer user_data) { struct mcap_mcl *mcl; mcap_md_sync_info_ind *cmd; uint32_t btclock; uint64_t tmstamp; struct timespec base_time; int sent; if (!user_data) return FALSE; btclock = 0; mcl = user_data; if (!caps(mcl)) return FALSE; if (!get_all_clocks(mcl, &btclock, &base_time, &tmstamp)) return FALSE; cmd = g_new0(mcap_md_sync_info_ind, 1); cmd->op = MCAP_MD_SYNC_INFO_IND; cmd->btclock = htonl(btclock); cmd->timestst = hton64(tmstamp); cmd->timestsa = htons(caps(mcl)->latency); sent = send_sync_cmd(mcl, cmd, sizeof(*cmd)); g_free(cmd); return !sent; } static gboolean proc_sync_set_req_phase2(gpointer user_data) { struct mcap_mcl *mcl; struct sync_set_data *data; uint8_t update; uint32_t sched_btclock; uint64_t new_tmstamp; int ind_freq; int role; uint32_t btclock; uint64_t tmstamp; struct timespec base_time; uint16_t tmstampacc; gboolean reset; int delay; if (!user_data) return FALSE; mcl = user_data; if (!mcl->csp->set_data) return FALSE; btclock = 0; data = mcl->csp->set_data; update = data->update; sched_btclock = data->sched_btclock; new_tmstamp = data->timestamp; ind_freq = data->ind_freq; role = data->role; if (!caps(mcl)) { send_sync_set_rsp(mcl, MCAP_UNSPECIFIED_ERROR, 0, 0, 0); return FALSE; } if (!get_all_clocks(mcl, &btclock, &base_time, &tmstamp)) { send_sync_set_rsp(mcl, MCAP_UNSPECIFIED_ERROR, 0, 0, 0); return FALSE; } if (get_btrole(mcl) != role) { send_sync_set_rsp(mcl, MCAP_INVALID_OPERATION, 0, 0, 0); return FALSE; } reset = (new_tmstamp != MCAP_TMSTAMP_DONTSET); if (reset) { if (sched_btclock != MCAP_BTCLOCK_IMMEDIATE) { delay = bt2us(btdiff(sched_btclock, btclock)); if (delay >= 0 || ((new_tmstamp - delay) > 0)) { new_tmstamp += delay; DBG("CSP: reset w/ delay %dus, compensated", delay); } else DBG("CSP: reset w/ delay %dus, uncompensated", delay); } reset_tmstamp(mcl->csp, &base_time, new_tmstamp); tmstamp = new_tmstamp; } tmstampacc = caps(mcl)->latency + caps(mcl)->ts_acc; if (mcl->csp->ind_timer) { g_source_remove(mcl->csp->ind_timer); mcl->csp->ind_timer = 0; } if (update) { int when = ind_freq + caps(mcl)->syncleadtime_ms; mcl->csp->ind_timer = g_timeout_add(when, sync_send_indication, mcl); } send_sync_set_rsp(mcl, MCAP_SUCCESS, btclock, tmstamp, tmstampacc); /* First indication after set is immediate */ if (update) sync_send_indication(mcl); return FALSE; } static void proc_sync_set_req(struct mcap_mcl *mcl, uint8_t *cmd, uint32_t len) { mcap_md_sync_set_req *req; uint32_t sched_btclock, cur_btclock; uint16_t btres; uint8_t update; uint64_t timestamp; struct sync_set_data *set_data; int phase2_delay, ind_freq, when; if (len != sizeof(mcap_md_sync_set_req)) { send_sync_set_rsp(mcl, MCAP_INVALID_PARAM_VALUE, 0, 0, 0); return; } req = (mcap_md_sync_set_req *) cmd; sched_btclock = ntohl(req->btclock); update = req->timestui; timestamp = ntoh64(req->timestst); cur_btclock = 0; if (sched_btclock != MCAP_BTCLOCK_IMMEDIATE && !valid_btclock(sched_btclock)) { send_sync_set_rsp(mcl, MCAP_INVALID_PARAM_VALUE, 0, 0, 0); return; } if (update > 1) { send_sync_set_rsp(mcl, MCAP_INVALID_PARAM_VALUE, 0, 0, 0); return; } if (!mcl->csp->remote_caps) { /* Remote side did not ask our capabilities yet */ send_sync_set_rsp(mcl, MCAP_INVALID_PARAM_VALUE, 0, 0, 0); return; } if (!caps(mcl)) { send_sync_set_rsp(mcl, MCAP_UNSPECIFIED_ERROR, 0, 0, 0); return; } if (!read_btclock_retry(mcl, &cur_btclock, &btres)) { send_sync_set_rsp(mcl, MCAP_UNSPECIFIED_ERROR, 0, 0, 0); return; } if (sched_btclock == MCAP_BTCLOCK_IMMEDIATE) phase2_delay = 0; else { phase2_delay = btdiff(cur_btclock, sched_btclock); if (phase2_delay < 0) { /* can not reset in the past tense */ send_sync_set_rsp(mcl, MCAP_INVALID_PARAM_VALUE, 0, 0, 0); return; } /* Convert to miliseconds */ phase2_delay = bt2ms(phase2_delay); if (phase2_delay > 61*1000) { /* More than 60 seconds in the future */ send_sync_set_rsp(mcl, MCAP_INVALID_PARAM_VALUE, 0, 0, 0); return; } else if (phase2_delay < caps(mcl)->latency / 1000) { /* Too fast for us to do in time */ send_sync_set_rsp(mcl, MCAP_INVALID_PARAM_VALUE, 0, 0, 0); return; } } if (update) { /* * Indication frequency: required accuracy divided by ours * Converted to milisseconds */ ind_freq = (1000 * mcl->csp->rem_req_acc) / caps(mcl)->ts_acc; if (ind_freq < MAX(caps(mcl)->latency * 2 / 1000, 100)) { /* Too frequent, we can't handle */ send_sync_set_rsp(mcl, MCAP_INVALID_PARAM_VALUE, 0, 0, 0); return; } DBG("CSP: indication every %dms", ind_freq); } else ind_freq = 0; if (mcl->csp->ind_timer) { /* Old indications are no longer sent */ g_source_remove(mcl->csp->ind_timer); mcl->csp->ind_timer = 0; } if (!mcl->csp->set_data) mcl->csp->set_data = g_new0(struct sync_set_data, 1); set_data = (struct sync_set_data *) mcl->csp->set_data; set_data->update = update; set_data->sched_btclock = sched_btclock; set_data->timestamp = timestamp; set_data->ind_freq = ind_freq; set_data->role = get_btrole(mcl); /* * TODO is there some way to schedule a call based directly on * a BT clock value, instead of this estimation that uses * the SO clock? */ if (phase2_delay > 0) { when = phase2_delay + caps(mcl)->syncleadtime_ms; mcl->csp->set_timer = g_timeout_add(when, proc_sync_set_req_phase2, mcl); } else proc_sync_set_req_phase2(mcl); /* First indication is immediate */ if (update) sync_send_indication(mcl); } static void proc_sync_cap_rsp(struct mcap_mcl *mcl, uint8_t *cmd, uint32_t len) { mcap_md_sync_cap_rsp *rsp; uint8_t mcap_err; uint8_t btclockres; uint16_t synclead; uint16_t tmstampres; uint16_t tmstampacc; struct mcap_sync_cap_cbdata *cbdata; mcap_sync_cap_cb cb; gpointer user_data; if (mcl->csp->csp_req != MCAP_MD_SYNC_CAP_REQ) { DBG("CSP: got unexpected cap respose"); return; } if (!mcl->csp->csp_priv_data) { DBG("CSP: no priv data for cap respose"); return; } cbdata = mcl->csp->csp_priv_data; cb = cbdata->cb; user_data = cbdata->user_data; g_free(cbdata); mcl->csp->csp_priv_data = NULL; mcl->csp->csp_req = 0; if (len != sizeof(mcap_md_sync_cap_rsp)) { DBG("CSP: got corrupted cap respose"); return; } rsp = (mcap_md_sync_cap_rsp *) cmd; mcap_err = rsp->rc; btclockres = rsp->btclock; synclead = ntohs(rsp->sltime); tmstampres = ntohs(rsp->timestnr); tmstampacc = ntohs(rsp->timestna); if (!mcap_err) mcl->csp->local_caps = TRUE; cb(mcl, mcap_err, btclockres, synclead, tmstampres, tmstampacc, NULL, user_data); } static void proc_sync_set_rsp(struct mcap_mcl *mcl, uint8_t *cmd, uint32_t len) { mcap_md_sync_set_rsp *rsp; uint8_t mcap_err; uint32_t btclock; uint64_t timestamp; uint16_t accuracy; struct mcap_sync_set_cbdata *cbdata; mcap_sync_set_cb cb; gpointer user_data; if (mcl->csp->csp_req != MCAP_MD_SYNC_SET_REQ) { DBG("CSP: got unexpected set respose"); return; } if (!mcl->csp->csp_priv_data) { DBG("CSP: no priv data for set respose"); return; } cbdata = mcl->csp->csp_priv_data; cb = cbdata->cb; user_data = cbdata->user_data; g_free(cbdata); mcl->csp->csp_priv_data = NULL; mcl->csp->csp_req = 0; if (len != sizeof(mcap_md_sync_set_rsp)) { DBG("CSP: got corrupted set respose"); return; } rsp = (mcap_md_sync_set_rsp *) cmd; mcap_err = rsp->rc; btclock = ntohl(rsp->btclock); timestamp = ntoh64(rsp->timestst); accuracy = ntohs(rsp->timestsa); if (!mcap_err && !valid_btclock(btclock)) mcap_err = MCAP_ERROR_INVALID_ARGS; cb(mcl, mcap_err, btclock, timestamp, accuracy, NULL, user_data); } static void proc_sync_info_ind(struct mcap_mcl *mcl, uint8_t *cmd, uint32_t len) { mcap_md_sync_info_ind *req; struct sync_info_ind_data data; uint32_t btclock; if (!mcl->csp->ind_expected) { DBG("CSP: received unexpected info indication"); return; } if (len != sizeof(mcap_md_sync_info_ind)) return; req = (mcap_md_sync_info_ind *) cmd; btclock = ntohl(req->btclock); if (!valid_btclock(btclock)) return; data.btclock = btclock; data.timestamp = ntoh64(req->timestst); data.accuracy = ntohs(req->timestsa); if (mcl->mi->mcl_sync_infoind_cb) mcl->mi->mcl_sync_infoind_cb(mcl, &data); } void proc_sync_cmd(struct mcap_mcl *mcl, uint8_t *cmd, uint32_t len) { if (!mcl->mi->csp_enabled || !mcl->csp) { switch (cmd[0]) { case MCAP_MD_SYNC_CAP_REQ: send_unsupported_cap_req(mcl); break; case MCAP_MD_SYNC_SET_REQ: send_unsupported_set_req(mcl); break; } return; } switch (cmd[0]) { case MCAP_MD_SYNC_CAP_REQ: proc_sync_cap_req(mcl, cmd, len); break; case MCAP_MD_SYNC_CAP_RSP: proc_sync_cap_rsp(mcl, cmd, len); break; case MCAP_MD_SYNC_SET_REQ: proc_sync_set_req(mcl, cmd, len); break; case MCAP_MD_SYNC_SET_RSP: proc_sync_set_rsp(mcl, cmd, len); break; case MCAP_MD_SYNC_INFO_IND: proc_sync_info_ind(mcl, cmd, len); break; } } void mcap_sync_cap_req(struct mcap_mcl *mcl, uint16_t reqacc, mcap_sync_cap_cb cb, gpointer user_data, GError **err) { struct mcap_sync_cap_cbdata *cbdata; mcap_md_sync_cap_req *cmd; if (!mcl->mi->csp_enabled || !mcl->csp) { g_set_error(err, MCAP_CSP_ERROR, MCAP_ERROR_RESOURCE_UNAVAILABLE, "CSP not enabled for the instance"); return; } if (mcl->csp->csp_req) { g_set_error(err, MCAP_CSP_ERROR, MCAP_ERROR_RESOURCE_UNAVAILABLE, "Pending CSP request"); return; } mcl->csp->csp_req = MCAP_MD_SYNC_CAP_REQ; cmd = g_new0(mcap_md_sync_cap_req, 1); cmd->op = MCAP_MD_SYNC_CAP_REQ; cmd->timest = htons(reqacc); cbdata = g_new0(struct mcap_sync_cap_cbdata, 1); cbdata->cb = cb; cbdata->user_data = user_data; mcl->csp->csp_priv_data = cbdata; send_sync_cmd(mcl, cmd, sizeof(*cmd)); g_free(cmd); } void mcap_sync_set_req(struct mcap_mcl *mcl, uint8_t update, uint32_t btclock, uint64_t timestamp, mcap_sync_set_cb cb, gpointer user_data, GError **err) { mcap_md_sync_set_req *cmd; struct mcap_sync_set_cbdata *cbdata; if (!mcl->mi->csp_enabled || !mcl->csp) { g_set_error(err, MCAP_CSP_ERROR, MCAP_ERROR_RESOURCE_UNAVAILABLE, "CSP not enabled for the instance"); return; } if (!mcl->csp->local_caps) { g_set_error(err, MCAP_CSP_ERROR, MCAP_ERROR_RESOURCE_UNAVAILABLE, "Did not get CSP caps from peripheral yet"); return; } if (mcl->csp->csp_req) { g_set_error(err, MCAP_CSP_ERROR, MCAP_ERROR_RESOURCE_UNAVAILABLE, "Pending CSP request"); return; } mcl->csp->csp_req = MCAP_MD_SYNC_SET_REQ; cmd = g_new0(mcap_md_sync_set_req, 1); cmd->op = MCAP_MD_SYNC_SET_REQ; cmd->timestui = update; cmd->btclock = htonl(btclock); cmd->timestst = hton64(timestamp); mcl->csp->ind_expected = update; cbdata = g_new0(struct mcap_sync_set_cbdata, 1); cbdata->cb = cb; cbdata->user_data = user_data; mcl->csp->csp_priv_data = cbdata; send_sync_cmd(mcl, cmd, sizeof(*cmd)); g_free(cmd); } void mcap_enable_csp(struct mcap_instance *mi) { mi->csp_enabled = TRUE; } void mcap_disable_csp(struct mcap_instance *mi) { mi->csp_enabled = FALSE; } bluez-5.82/profiles/health/PaxHeaders/hdp_manager.h0000644000000000000000000000005014015011623017345 xustar0020 atime=1743516870 20 ctime=1743591284 bluez-5.82/profiles/health/hdp_manager.h0000644000000000000000000000035514015011623017031 0ustar00rootroot/* SPDX-License-Identifier: GPL-2.0-or-later */ /* * * BlueZ - Bluetooth protocol stack for Linux * * Copyright (C) 2010 GSyC/LibreSoft, Universidad Rey Juan Carlos. * */ int hdp_manager_init(void); void hdp_manager_exit(void); bluez-5.82/profiles/health/PaxHeaders/hdp.c0000644000000000000000000000005014214376354015666 xustar0020 atime=1743516870 20 ctime=1743591284 bluez-5.82/profiles/health/hdp.c0000644000000000000000000014654214214376354015363 0ustar00rootroot// SPDX-License-Identifier: GPL-2.0-or-later /* * * BlueZ - Bluetooth protocol stack for Linux * * Copyright (C) 2010 GSyC/LibreSoft, Universidad Rey Juan Carlos. * */ #ifdef HAVE_CONFIG_H #include #endif #define _GNU_SOURCE #include #include #include #include #include #include "lib/bluetooth.h" #include "lib/l2cap.h" #include "lib/sdp.h" #include "gdbus/gdbus.h" #include "src/dbus-common.h" #include "src/log.h" #include "src/error.h" #include "src/adapter.h" #include "src/device.h" #include "src/sdpd.h" #include "src/shared/timeout.h" #include "src/shared/util.h" #include "btio/btio.h" #include "hdp_types.h" #include "hdp_util.h" #include "hdp.h" #include "mcap.h" #define ECHO_TIMEOUT 1 /* second */ #define HDP_ECHO_LEN 15 static GSList *applications = NULL; static GSList *devices = NULL; static uint8_t next_app_id = HDP_MDEP_INITIAL; static GSList *adapters; static struct hdp_device *create_health_device(struct btd_device *device); static void free_echo_data(struct hdp_echo_data *edata); struct hdp_create_dc { DBusMessage *msg; struct hdp_application *app; struct hdp_device *dev; uint8_t config; uint8_t mdep; guint ref; mcap_mdl_operation_cb cb; }; struct hdp_tmp_dc_data { DBusMessage *msg; struct hdp_channel *hdp_chann; guint ref; mcap_mdl_operation_cb cb; }; struct hdp_echo_data { gboolean echo_done; /* Is a echo was already done */ gpointer buf; /* echo packet sent */ unsigned int tid; /* echo timeout */ }; static struct hdp_channel *hdp_channel_ref(struct hdp_channel *chan) { if (chan == NULL) return NULL; chan->ref++; DBG("(%p): ref=%d", chan, chan->ref); return chan; } static void free_health_channel(struct hdp_channel *chan) { if (chan->mdep == HDP_MDEP_ECHO) { free_echo_data(chan->edata); chan->edata = NULL; } mcap_mdl_unref(chan->mdl); hdp_application_unref(chan->app); health_device_unref(chan->dev); g_free(chan->path); g_free(chan); } static void hdp_channel_unref(struct hdp_channel *chan) { if (chan == NULL) return; chan->ref --; DBG("(%p): ref=%d", chan, chan->ref); if (chan->ref > 0) return; free_health_channel(chan); } static void free_hdp_create_dc(struct hdp_create_dc *dc_data) { dbus_message_unref(dc_data->msg); hdp_application_unref(dc_data->app); health_device_unref(dc_data->dev); g_free(dc_data); } static struct hdp_create_dc *hdp_create_data_ref(struct hdp_create_dc *dc_data) { dc_data->ref++; DBG("(%p): ref=%d", dc_data, dc_data->ref); return dc_data; } static void hdp_create_data_unref(struct hdp_create_dc *dc_data) { dc_data->ref--; DBG("(%p): ref=%d", dc_data, dc_data->ref); if (dc_data->ref > 0) return; free_hdp_create_dc(dc_data); } static void free_hdp_conn_dc(struct hdp_tmp_dc_data *data) { dbus_message_unref(data->msg); hdp_channel_unref(data->hdp_chann); g_free(data); } static struct hdp_tmp_dc_data *hdp_tmp_dc_data_ref(struct hdp_tmp_dc_data *data) { data->ref++; DBG("hdp_conn_data_ref(%p): ref=%d", data, data->ref); return data; } static void hdp_tmp_dc_data_unref(struct hdp_tmp_dc_data *data) { data->ref--; DBG("hdp_conn_data_unref(%p): ref=%d", data, data->ref); if (data->ref > 0) return; free_hdp_conn_dc(data); } static int cmp_app_id(gconstpointer a, gconstpointer b) { const struct hdp_application *app = a; const uint8_t *id = b; return app->id - *id; } static int cmp_adapter(gconstpointer a, gconstpointer b) { const struct hdp_adapter *hdp_adapter = a; const struct btd_adapter *adapter = b; if (hdp_adapter->btd_adapter == adapter) return 0; return -1; } static int cmp_device(gconstpointer a, gconstpointer b) { const struct hdp_device *hdp_device = a; const struct btd_device *device = b; if (hdp_device->dev == device) return 0; return -1; } static int cmp_dev_addr(gconstpointer a, gconstpointer dst) { const struct hdp_device *device = a; return bacmp(device_get_address(device->dev), dst); } static int cmp_dev_mcl(gconstpointer a, gconstpointer mcl) { const struct hdp_device *device = a; if (mcl == device->mcl) return 0; return -1; } static int cmp_chan_mdlid(gconstpointer a, gconstpointer b) { const struct hdp_channel *chan = a; const uint16_t *mdlid = b; return chan->mdlid - *mdlid; } static int cmp_chan_path(gconstpointer a, gconstpointer b) { const struct hdp_channel *chan = a; const char *path = b; return g_ascii_strcasecmp(chan->path, path); } static int cmp_chan_mdl(gconstpointer a, gconstpointer mdl) { const struct hdp_channel *chan = a; if (chan->mdl == mdl) return 0; return -1; } static uint8_t get_app_id(void) { uint8_t id = next_app_id; do { GSList *l = g_slist_find_custom(applications, &id, cmp_app_id); if (l == NULL) { next_app_id = (id % HDP_MDEP_FINAL) + 1; return id; } else id = (id % HDP_MDEP_FINAL) + 1; } while (id != next_app_id); /* No more ids available */ return 0; } static int cmp_app(gconstpointer a, gconstpointer b) { const struct hdp_application *app = a; return g_strcmp0(app->path, b); } static gboolean set_app_path(struct hdp_application *app) { app->id = get_app_id(); if (app->id == 0) return FALSE; app->path = g_strdup_printf(MANAGER_PATH "/health_app_%d", app->id); return TRUE; }; static void device_unref_mcl(struct hdp_device *hdp_device) { if (hdp_device->mcl == NULL) return; mcap_close_mcl(hdp_device->mcl, FALSE); mcap_mcl_unref(hdp_device->mcl); hdp_device->mcl = NULL; hdp_device->mcl_conn = FALSE; } static void free_health_device(struct hdp_device *device) { if (device->dev != NULL) { btd_device_unref(device->dev); device->dev = NULL; } device_unref_mcl(device); g_free(device); } static void update_adapter_cb(void *data, void *user_data); static void remove_application(struct hdp_application *app) { DBG("Application %s deleted", app->path); hdp_application_unref(app); g_slist_foreach(adapters, update_adapter_cb, NULL); } static void client_disconnected(DBusConnection *conn, void *user_data) { struct hdp_application *app = user_data; DBG("Client disconnected from the bus, deleting hdp application"); applications = g_slist_remove(applications, app); app->dbus_watcher = 0; /* Watcher shouldn't be freed in this case */ remove_application(app); } static DBusMessage *manager_create_application(DBusConnection *conn, DBusMessage *msg, void *user_data) { struct hdp_application *app; const char *name; DBusMessageIter iter; GError *err = NULL; dbus_message_iter_init(msg, &iter); app = hdp_get_app_config(&iter, &err); if (err != NULL) { g_error_free(err); return btd_error_invalid_args(msg); } name = dbus_message_get_sender(msg); if (name == NULL) { hdp_application_unref(app); return g_dbus_create_error(msg, ERROR_INTERFACE ".HealthError", "Can't get sender name"); } if (!set_app_path(app)) { hdp_application_unref(app); return g_dbus_create_error(msg, ERROR_INTERFACE ".HealthError", "Can't get a valid id for the application"); } app->oname = g_strdup(name); applications = g_slist_prepend(applications, app); app->dbus_watcher = g_dbus_add_disconnect_watch(btd_get_dbus_connection(), name, client_disconnected, app, NULL); g_slist_foreach(adapters, update_adapter_cb, NULL); DBG("Health application created with id %s", app->path); return g_dbus_create_reply(msg, DBUS_TYPE_OBJECT_PATH, &app->path, DBUS_TYPE_INVALID); } static DBusMessage *manager_destroy_application(DBusConnection *conn, DBusMessage *msg, void *user_data) { const char *path; struct hdp_application *app; GSList *l; if (!dbus_message_get_args(msg, NULL, DBUS_TYPE_OBJECT_PATH, &path, DBUS_TYPE_INVALID)) return btd_error_invalid_args(msg); l = g_slist_find_custom(applications, path, cmp_app); if (l == NULL) return g_dbus_create_error(msg, ERROR_INTERFACE ".InvalidArguments", "Invalid arguments in method call, " "no such application"); app = l->data; applications = g_slist_remove(applications, app); remove_application(app); return g_dbus_create_reply(msg, DBUS_TYPE_INVALID); } static void application_unref(void *data, void *user_data) { hdp_application_unref(data); } static void manager_path_unregister(gpointer data) { g_slist_foreach(applications, application_unref, NULL); g_slist_free(applications); applications = NULL; g_slist_foreach(adapters, update_adapter_cb, NULL); } static const GDBusMethodTable health_manager_methods[] = { { GDBUS_METHOD("CreateApplication", GDBUS_ARGS({ "config", "a{sv}" }), GDBUS_ARGS({ "application", "o" }), manager_create_application) }, { GDBUS_METHOD("DestroyApplication", GDBUS_ARGS({ "application", "o" }), NULL, manager_destroy_application) }, { } }; static gboolean channel_property_get_device(const GDBusPropertyTable *property, DBusMessageIter *iter, void *data) { struct hdp_channel *chan = data; const char *path = device_get_path(chan->dev->dev); dbus_message_iter_append_basic(iter, DBUS_TYPE_OBJECT_PATH, &path); return TRUE; } static gboolean channel_property_get_application( const GDBusPropertyTable *property, DBusMessageIter *iter, void *data) { struct hdp_channel *chan = data; const char *path = chan->app->path; dbus_message_iter_append_basic(iter, DBUS_TYPE_OBJECT_PATH, &path); return TRUE; } static gboolean channel_property_get_type(const GDBusPropertyTable *property, DBusMessageIter *iter, void *data) { struct hdp_channel *chan = data; const char *type; if (chan->config == HDP_RELIABLE_DC) type = "reliable"; else type = "streaming"; dbus_message_iter_append_basic(iter, DBUS_TYPE_STRING, &type); return TRUE; } static void hdp_tmp_dc_data_destroy(gpointer data) { struct hdp_tmp_dc_data *hdp_conn = data; hdp_tmp_dc_data_unref(hdp_conn); } static void abort_mdl_cb(GError *err, gpointer data) { if (err != NULL) error("Aborting error: %s", err->message); } static void hdp_mdl_reconn_cb(struct mcap_mdl *mdl, GError *err, gpointer data) { DBusConnection *conn = btd_get_dbus_connection(); struct hdp_tmp_dc_data *dc_data = data; DBusMessage *reply; int fd; if (err != NULL) { struct hdp_channel *chan = dc_data->hdp_chann; GError *gerr = NULL; error("%s", err->message); reply = g_dbus_create_error(dc_data->msg, ERROR_INTERFACE ".HealthError", "Cannot reconnect: %s", err->message); g_dbus_send_message(conn, reply); /* Send abort request because remote side */ /* is now in PENDING state */ if (!mcap_mdl_abort(chan->mdl, abort_mdl_cb, NULL, NULL, &gerr)) { error("%s", gerr->message); g_error_free(gerr); } return; } fd = mcap_mdl_get_fd(dc_data->hdp_chann->mdl); if (fd < 0) { reply = g_dbus_create_error(dc_data->msg, ERROR_INTERFACE ".HealthError", "Cannot get file descriptor"); g_dbus_send_message(conn, reply); return; } reply = g_dbus_create_reply(dc_data->msg, DBUS_TYPE_UNIX_FD, &fd, DBUS_TYPE_INVALID); g_dbus_send_message(conn, reply); g_dbus_emit_signal(conn, device_get_path(dc_data->hdp_chann->dev->dev), HEALTH_DEVICE, "ChannelConnected", DBUS_TYPE_OBJECT_PATH, &dc_data->hdp_chann->path, DBUS_TYPE_INVALID); } static void hdp_get_dcpsm_cb(uint16_t dcpsm, gpointer user_data, GError *err) { struct hdp_tmp_dc_data *hdp_conn = user_data; struct hdp_channel *hdp_chann = hdp_conn->hdp_chann; GError *gerr = NULL; uint8_t mode; if (err != NULL) { hdp_conn->cb(hdp_chann->mdl, err, hdp_conn); return; } if (hdp_chann->config == HDP_RELIABLE_DC) mode = BT_IO_MODE_ERTM; else mode = BT_IO_MODE_STREAMING; if (mcap_connect_mdl(hdp_chann->mdl, mode, dcpsm, hdp_conn->cb, hdp_tmp_dc_data_ref(hdp_conn), hdp_tmp_dc_data_destroy, &gerr)) return; hdp_conn->cb(hdp_chann->mdl, err, hdp_conn); g_error_free(gerr); hdp_tmp_dc_data_unref(hdp_conn); } static void device_reconnect_mdl_cb(struct mcap_mdl *mdl, GError *err, gpointer data) { DBusConnection *conn = btd_get_dbus_connection(); struct hdp_tmp_dc_data *dc_data = data; GError *gerr = NULL; DBusMessage *reply; if (err != NULL) { reply = g_dbus_create_error(dc_data->msg, ERROR_INTERFACE ".HealthError", "Cannot reconnect: %s", err->message); g_dbus_send_message(conn, reply); return; } dc_data->cb = hdp_mdl_reconn_cb; if (hdp_get_dcpsm(dc_data->hdp_chann->dev, hdp_get_dcpsm_cb, hdp_tmp_dc_data_ref(dc_data), hdp_tmp_dc_data_destroy, &gerr)) return; error("%s", gerr->message); reply = g_dbus_create_error(dc_data->msg, ERROR_INTERFACE ".HealthError", "Cannot reconnect: %s", gerr->message); g_dbus_send_message(conn, reply); hdp_tmp_dc_data_unref(dc_data); g_clear_error(&gerr); /* Send abort request because remote side is now in PENDING state */ if (!mcap_mdl_abort(mdl, abort_mdl_cb, NULL, NULL, &gerr)) { error("%s", gerr->message); g_error_free(gerr); } } static DBusMessage *channel_acquire_continue(struct hdp_tmp_dc_data *data, GError *err) { DBusMessage *reply; GError *gerr = NULL; int fd; if (err != NULL) { return g_dbus_create_error(data->msg, ERROR_INTERFACE ".HealthError", "%s", err->message); } fd = mcap_mdl_get_fd(data->hdp_chann->mdl); if (fd >= 0) return g_dbus_create_reply(data->msg, DBUS_TYPE_UNIX_FD, &fd, DBUS_TYPE_INVALID); hdp_tmp_dc_data_ref(data); if (mcap_reconnect_mdl(data->hdp_chann->mdl, device_reconnect_mdl_cb, data, hdp_tmp_dc_data_destroy, &gerr)) return NULL; reply = g_dbus_create_error(data->msg, ERROR_INTERFACE ".HealthError", "Cannot reconnect: %s", gerr->message); g_error_free(gerr); hdp_tmp_dc_data_unref(data); return reply; } static void channel_acquire_cb(gpointer data, GError *err) { DBusMessage *reply; reply = channel_acquire_continue(data, err); if (reply != NULL) g_dbus_send_message(btd_get_dbus_connection(), reply); } static DBusMessage *channel_acquire(DBusConnection *conn, DBusMessage *msg, void *user_data) { struct hdp_channel *chan = user_data; struct hdp_tmp_dc_data *dc_data; GError *gerr = NULL; DBusMessage *reply; dc_data = g_new0(struct hdp_tmp_dc_data, 1); dc_data->msg = dbus_message_ref(msg); dc_data->hdp_chann = hdp_channel_ref(chan); if (chan->dev->mcl_conn) { reply = channel_acquire_continue(hdp_tmp_dc_data_ref(dc_data), NULL); hdp_tmp_dc_data_unref(dc_data); return reply; } if (hdp_establish_mcl(chan->dev, channel_acquire_cb, hdp_tmp_dc_data_ref(dc_data), hdp_tmp_dc_data_destroy, &gerr)) return NULL; reply = g_dbus_create_error(msg, ERROR_INTERFACE ".HealthError", "%s", gerr->message); hdp_tmp_dc_data_unref(dc_data); g_error_free(gerr); return reply; } static void close_mdl(struct hdp_channel *hdp_chann) { int fd; fd = mcap_mdl_get_fd(hdp_chann->mdl); if (fd < 0) return; close(fd); } static DBusMessage *channel_release(DBusConnection *conn, DBusMessage *msg, void *user_data) { struct hdp_channel *hdp_chann = user_data; close_mdl(hdp_chann); return g_dbus_create_reply(msg, DBUS_TYPE_INVALID); } static void free_echo_data(struct hdp_echo_data *edata) { if (edata == NULL) return; if (edata->tid > 0) timeout_remove(edata->tid); if (edata->buf != NULL) g_free(edata->buf); g_free(edata); } static void health_channel_destroy(void *data) { struct hdp_channel *hdp_chan = data; struct hdp_device *dev = hdp_chan->dev; DBG("Destroy Health Channel %s", hdp_chan->path); if (g_slist_find(dev->channels, hdp_chan) == NULL) goto end; dev->channels = g_slist_remove(dev->channels, hdp_chan); if (hdp_chan->mdep != HDP_MDEP_ECHO) g_dbus_emit_signal(btd_get_dbus_connection(), device_get_path(dev->dev), HEALTH_DEVICE, "ChannelDeleted", DBUS_TYPE_OBJECT_PATH, &hdp_chan->path, DBUS_TYPE_INVALID); if (hdp_chan == dev->fr) { hdp_channel_unref(dev->fr); dev->fr = NULL; } end: hdp_channel_unref(hdp_chan); } static const GDBusMethodTable health_channels_methods[] = { { GDBUS_ASYNC_METHOD("Acquire", NULL, GDBUS_ARGS({ "fd", "h" }), channel_acquire) }, { GDBUS_METHOD("Release", NULL, NULL, channel_release) }, { } }; static const GDBusPropertyTable health_channels_properties[] = { { "Device", "o", channel_property_get_device }, { "Application", "o", channel_property_get_application }, { "Type", "s", channel_property_get_type }, { } }; static struct hdp_channel *create_channel(struct hdp_device *dev, uint8_t config, struct mcap_mdl *mdl, uint16_t mdlid, struct hdp_application *app, GError **err) { struct hdp_channel *hdp_chann; if (dev == NULL) { g_set_error(err, HDP_ERROR, HDP_UNSPECIFIED_ERROR, "HDP device uninitialized"); return NULL; } hdp_chann = g_new0(struct hdp_channel, 1); hdp_chann->config = config; hdp_chann->dev = health_device_ref(dev); hdp_chann->mdlid = mdlid; if (mdl != NULL) hdp_chann->mdl = mcap_mdl_ref(mdl); if (app != NULL) { hdp_chann->mdep = app->id; hdp_chann->app = hdp_application_ref(app); } else hdp_chann->edata = g_new0(struct hdp_echo_data, 1); hdp_chann->path = g_strdup_printf("%s/chan%d", device_get_path(hdp_chann->dev->dev), hdp_chann->mdlid); dev->channels = g_slist_append(dev->channels, hdp_channel_ref(hdp_chann)); if (hdp_chann->mdep == HDP_MDEP_ECHO) return hdp_channel_ref(hdp_chann); if (!g_dbus_register_interface(btd_get_dbus_connection(), hdp_chann->path, HEALTH_CHANNEL, health_channels_methods, NULL, health_channels_properties, hdp_chann, health_channel_destroy)) { g_set_error(err, HDP_ERROR, HDP_UNSPECIFIED_ERROR, "Can't register the channel interface"); health_channel_destroy(hdp_chann); return NULL; } return hdp_channel_ref(hdp_chann); } static void remove_channels(struct hdp_device *dev) { struct hdp_channel *chan; char *path; while (dev->channels != NULL) { chan = dev->channels->data; path = g_strdup(chan->path); if (!g_dbus_unregister_interface(btd_get_dbus_connection(), path, HEALTH_CHANNEL)) health_channel_destroy(chan); g_free(path); } } static void close_device_con(struct hdp_device *dev, gboolean cache) { if (dev->mcl == NULL) return; mcap_close_mcl(dev->mcl, cache); dev->mcl_conn = FALSE; if (cache) return; device_unref_mcl(dev); remove_channels(dev); if (!dev->sdp_present) { const char *path; path = device_get_path(dev->dev); g_dbus_unregister_interface(btd_get_dbus_connection(), path, HEALTH_DEVICE); } } static int send_echo_data(int sock, const void *buf, uint32_t size) { const uint8_t *buf_b = buf; uint32_t sent = 0; while (sent < size) { int n = write(sock, buf_b + sent, size - sent); if (n < 0) return -1; sent += n; } return 0; } static gboolean serve_echo(GIOChannel *io_chan, GIOCondition cond, gpointer data) { struct hdp_channel *chan = data; uint8_t buf[MCAP_DC_MTU]; int fd, len; if (cond & (G_IO_ERR | G_IO_HUP | G_IO_NVAL)) { hdp_channel_unref(chan); return FALSE; } if (chan->edata->echo_done) goto fail; chan->edata->echo_done = TRUE; fd = g_io_channel_unix_get_fd(io_chan); len = read(fd, buf, sizeof(buf)); if (len < 0) goto fail; if (send_echo_data(fd, buf, len) >= 0) return TRUE; fail: close_device_con(chan->dev, FALSE); hdp_channel_unref(chan); return FALSE; } static gboolean check_channel_conf(struct hdp_channel *chan) { GError *err = NULL; GIOChannel *io; uint8_t mode; uint16_t imtu, omtu; int fd; fd = mcap_mdl_get_fd(chan->mdl); if (fd < 0) return FALSE; io = g_io_channel_unix_new(fd); if (!bt_io_get(io, &err, BT_IO_OPT_MODE, &mode, BT_IO_OPT_IMTU, &imtu, BT_IO_OPT_OMTU, &omtu, BT_IO_OPT_INVALID)) { error("Error: %s", err->message); g_io_channel_unref(io); g_error_free(err); return FALSE; } g_io_channel_unref(io); switch (chan->config) { case HDP_RELIABLE_DC: if (mode != BT_IO_MODE_ERTM) return FALSE; break; case HDP_STREAMING_DC: if (mode != BT_IO_MODE_STREAMING) return FALSE; break; default: error("Error: Connected with unknown configuration"); return FALSE; } DBG("MDL imtu %d omtu %d Channel imtu %d omtu %d", imtu, omtu, chan->imtu, chan->omtu); if (chan->imtu == 0) chan->imtu = imtu; if (chan->omtu == 0) chan->omtu = omtu; if (chan->imtu != imtu || chan->omtu != omtu) return FALSE; return TRUE; } static void hdp_mcap_mdl_connected_cb(struct mcap_mdl *mdl, void *data) { struct hdp_device *dev = data; struct hdp_channel *chan; DBG(""); if (dev->ndc == NULL) return; chan = dev->ndc; if (chan->mdl == NULL) chan->mdl = mcap_mdl_ref(mdl); if (g_slist_find(dev->channels, chan) == NULL) dev->channels = g_slist_prepend(dev->channels, hdp_channel_ref(chan)); if (!check_channel_conf(chan)) { close_mdl(chan); goto end; } if (chan->mdep == HDP_MDEP_ECHO) { GIOChannel *io; int fd; fd = mcap_mdl_get_fd(chan->mdl); if (fd < 0) goto end; chan->edata->echo_done = FALSE; io = g_io_channel_unix_new(fd); g_io_add_watch(io, G_IO_ERR | G_IO_HUP | G_IO_NVAL | G_IO_IN, serve_echo, hdp_channel_ref(chan)); g_io_channel_unref(io); goto end; } g_dbus_emit_signal(btd_get_dbus_connection(), device_get_path(dev->dev), HEALTH_DEVICE, "ChannelConnected", DBUS_TYPE_OBJECT_PATH, &chan->path, DBUS_TYPE_INVALID); if (dev->fr != NULL) goto end; dev->fr = hdp_channel_ref(chan); g_dbus_emit_property_changed(btd_get_dbus_connection(), device_get_path(dev->dev), HEALTH_DEVICE, "MainChannel"); end: hdp_channel_unref(dev->ndc); dev->ndc = NULL; } static void hdp_mcap_mdl_closed_cb(struct mcap_mdl *mdl, void *data) { /* struct hdp_device *dev = data; */ DBG(""); /* Nothing to do */ } static void hdp_mcap_mdl_deleted_cb(struct mcap_mdl *mdl, void *data) { struct hdp_device *dev = data; struct hdp_channel *chan; char *path; GSList *l; DBG(""); l = g_slist_find_custom(dev->channels, mdl, cmp_chan_mdl); if (l == NULL) return; chan = l->data; path = g_strdup(chan->path); if (!g_dbus_unregister_interface(btd_get_dbus_connection(), path, HEALTH_CHANNEL)) health_channel_destroy(chan); g_free(path); } static void hdp_mcap_mdl_aborted_cb(struct mcap_mdl *mdl, void *data) { struct hdp_device *dev = data; DBG(""); if (dev->ndc == NULL) return; dev->ndc->mdl = mcap_mdl_ref(mdl); if (g_slist_find(dev->channels, dev->ndc) == NULL) dev->channels = g_slist_prepend(dev->channels, hdp_channel_ref(dev->ndc)); if (dev->ndc->mdep != HDP_MDEP_ECHO) g_dbus_emit_signal(btd_get_dbus_connection(), device_get_path(dev->dev), HEALTH_DEVICE, "ChannelConnected", DBUS_TYPE_OBJECT_PATH, &dev->ndc->path, DBUS_TYPE_INVALID); hdp_channel_unref(dev->ndc); dev->ndc = NULL; } static uint8_t hdp2l2cap_mode(uint8_t hdp_mode) { return hdp_mode == HDP_STREAMING_DC ? BT_IO_MODE_STREAMING : BT_IO_MODE_ERTM; } static uint8_t hdp_mcap_mdl_conn_req_cb(struct mcap_mcl *mcl, uint8_t mdepid, uint16_t mdlid, uint8_t *conf, void *data) { struct hdp_device *dev = data; struct hdp_application *app; GError *err = NULL; GSList *l; DBG("Data channel request"); if (mdepid == HDP_MDEP_ECHO) { switch (*conf) { case HDP_NO_PREFERENCE_DC: *conf = HDP_RELIABLE_DC; break; case HDP_RELIABLE_DC: break; case HDP_STREAMING_DC: return MCAP_CONFIGURATION_REJECTED; default: /* Special case defined in HDP spec 3.4. When an invalid * configuration is received we shall close the MCL when * we are still processing the callback. */ close_device_con(dev, FALSE); return MCAP_CONFIGURATION_REJECTED; /* not processed */ } if (!mcap_set_data_chan_mode(dev->hdp_adapter->mi, BT_IO_MODE_ERTM, &err)) { error("Error: %s", err->message); g_error_free(err); return MCAP_MDL_BUSY; } dev->ndc = create_channel(dev, *conf, NULL, mdlid, NULL, NULL); if (dev->ndc == NULL) return MCAP_MDL_BUSY; return MCAP_SUCCESS; } l = g_slist_find_custom(applications, &mdepid, cmp_app_id); if (l == NULL) return MCAP_INVALID_MDEP; app = l->data; /* Check if is the first dc if so, * only reliable configuration is allowed */ switch (*conf) { case HDP_NO_PREFERENCE_DC: if (app->role == HDP_SINK) return MCAP_CONFIGURATION_REJECTED; else if (dev->fr && app->chan_type_set) *conf = app->chan_type; else *conf = HDP_RELIABLE_DC; break; case HDP_STREAMING_DC: if (!dev->fr || app->role == HDP_SOURCE) return MCAP_CONFIGURATION_REJECTED; break; case HDP_RELIABLE_DC: if (app->role == HDP_SOURCE) return MCAP_CONFIGURATION_REJECTED; break; default: /* Special case defined in HDP spec 3.4. When an invalid * configuration is received we shall close the MCL when * we are still processing the callback. */ close_device_con(dev, FALSE); return MCAP_CONFIGURATION_REJECTED; /* not processed */ } l = g_slist_find_custom(dev->channels, &mdlid, cmp_chan_mdlid); if (l != NULL) { struct hdp_channel *chan = l->data; char *path; path = g_strdup(chan->path); g_dbus_unregister_interface(btd_get_dbus_connection(), path, HEALTH_CHANNEL); g_free(path); } if (!mcap_set_data_chan_mode(dev->hdp_adapter->mi, hdp2l2cap_mode(*conf), &err)) { error("Error: %s", err->message); g_error_free(err); return MCAP_MDL_BUSY; } dev->ndc = create_channel(dev, *conf, NULL, mdlid, app, NULL); if (dev->ndc == NULL) return MCAP_MDL_BUSY; return MCAP_SUCCESS; } static uint8_t hdp_mcap_mdl_reconn_req_cb(struct mcap_mdl *mdl, void *data) { struct hdp_device *dev = data; struct hdp_channel *chan; GError *err = NULL; GSList *l; l = g_slist_find_custom(dev->channels, mdl, cmp_chan_mdl); if (l == NULL) return MCAP_INVALID_MDL; chan = l->data; if (dev->fr == NULL && chan->config != HDP_RELIABLE_DC && chan->mdep != HDP_MDEP_ECHO) return MCAP_UNSPECIFIED_ERROR; if (!mcap_set_data_chan_mode(dev->hdp_adapter->mi, hdp2l2cap_mode(chan->config), &err)) { error("Error: %s", err->message); g_error_free(err); return MCAP_MDL_BUSY; } dev->ndc = hdp_channel_ref(chan); return MCAP_SUCCESS; } gboolean hdp_set_mcl_cb(struct hdp_device *device, GError **err) { gboolean ret; if (device->mcl == NULL) return FALSE; ret = mcap_mcl_set_cb(device->mcl, device, err, MCAP_MDL_CB_CONNECTED, hdp_mcap_mdl_connected_cb, MCAP_MDL_CB_CLOSED, hdp_mcap_mdl_closed_cb, MCAP_MDL_CB_DELETED, hdp_mcap_mdl_deleted_cb, MCAP_MDL_CB_ABORTED, hdp_mcap_mdl_aborted_cb, MCAP_MDL_CB_REMOTE_CONN_REQ, hdp_mcap_mdl_conn_req_cb, MCAP_MDL_CB_REMOTE_RECONN_REQ, hdp_mcap_mdl_reconn_req_cb, MCAP_MDL_CB_INVALID); if (ret) return TRUE; error("Can't set mcl callbacks, closing mcl"); close_device_con(device, TRUE); return FALSE; } static void mcl_connected(struct mcap_mcl *mcl, gpointer data) { struct hdp_device *hdp_device; bdaddr_t addr; GSList *l; mcap_mcl_get_addr(mcl, &addr); l = g_slist_find_custom(devices, &addr, cmp_dev_addr); if (l == NULL) { struct hdp_adapter *hdp_adapter = data; struct btd_device *device; device = btd_adapter_get_device(hdp_adapter->btd_adapter, &addr, BDADDR_BREDR); if (!device) return; hdp_device = create_health_device(device); if (!hdp_device) return; devices = g_slist_append(devices, hdp_device); } else hdp_device = l->data; hdp_device->mcl = mcap_mcl_ref(mcl); hdp_device->mcl_conn = TRUE; DBG("New mcl connected from %s", device_get_path(hdp_device->dev)); hdp_set_mcl_cb(hdp_device, NULL); } static void mcl_reconnected(struct mcap_mcl *mcl, gpointer data) { struct hdp_device *hdp_device; GSList *l; l = g_slist_find_custom(devices, mcl, cmp_dev_mcl); if (l == NULL) return; hdp_device = l->data; hdp_device->mcl_conn = TRUE; DBG("MCL reconnected %s", device_get_path(hdp_device->dev)); hdp_set_mcl_cb(hdp_device, NULL); } static void mcl_disconnected(struct mcap_mcl *mcl, gpointer data) { struct hdp_device *hdp_device; GSList *l; l = g_slist_find_custom(devices, mcl, cmp_dev_mcl); if (l == NULL) return; hdp_device = l->data; hdp_device->mcl_conn = FALSE; DBG("Mcl disconnected %s", device_get_path(hdp_device->dev)); } static void mcl_uncached(struct mcap_mcl *mcl, gpointer data) { struct hdp_device *hdp_device; const char *path; GSList *l; l = g_slist_find_custom(devices, mcl, cmp_dev_mcl); if (l == NULL) return; hdp_device = l->data; device_unref_mcl(hdp_device); if (hdp_device->sdp_present) return; /* Because remote device hasn't announced an HDP record */ /* the Bluetooth daemon won't notify when the device shall */ /* be removed. Then we have to remove the HealthDevice */ /* interface manually */ path = device_get_path(hdp_device->dev); g_dbus_unregister_interface(btd_get_dbus_connection(), path, HEALTH_DEVICE); DBG("Mcl uncached %s", path); } static void check_devices_mcl(void) { struct hdp_device *dev; GSList *l, *to_delete = NULL; for (l = devices; l; l = l->next) { dev = l->data; device_unref_mcl(dev); if (!dev->sdp_present) to_delete = g_slist_append(to_delete, dev); else remove_channels(dev); } for (l = to_delete; l; l = l->next) { const char *path; path = device_get_path(dev->dev); g_dbus_unregister_interface(btd_get_dbus_connection(), path, HEALTH_DEVICE); } g_slist_free(to_delete); } static void release_adapter_instance(struct hdp_adapter *hdp_adapter) { if (hdp_adapter->mi == NULL) return; check_devices_mcl(); mcap_release_instance(hdp_adapter->mi); mcap_instance_unref(hdp_adapter->mi); hdp_adapter->mi = NULL; } static gboolean update_adapter(struct hdp_adapter *hdp_adapter) { GError *err = NULL; const bdaddr_t *src; if (applications == NULL) { release_adapter_instance(hdp_adapter); goto update; } if (hdp_adapter->mi != NULL) goto update; src = btd_adapter_get_address(hdp_adapter->btd_adapter); hdp_adapter->mi = mcap_create_instance(src, BT_IO_SEC_MEDIUM, 0, 0, mcl_connected, mcl_reconnected, mcl_disconnected, mcl_uncached, NULL, /* CSP is not used by now */ hdp_adapter, &err); if (hdp_adapter->mi == NULL) { error("Error creating the MCAP instance: %s", err->message); g_error_free(err); return FALSE; } hdp_adapter->ccpsm = mcap_get_ctrl_psm(hdp_adapter->mi, &err); if (err != NULL) { error("Error getting MCAP control PSM: %s", err->message); goto fail; } hdp_adapter->dcpsm = mcap_get_data_psm(hdp_adapter->mi, &err); if (err != NULL) { error("Error getting MCAP data PSM: %s", err->message); goto fail; } update: if (hdp_update_sdp_record(hdp_adapter, applications)) return TRUE; error("Error updating the SDP record"); fail: release_adapter_instance(hdp_adapter); if (err != NULL) g_error_free(err); return FALSE; } static void update_adapter_cb(void *data, void *user_data) { update_adapter(data); } int hdp_adapter_register(struct btd_adapter *adapter) { struct hdp_adapter *hdp_adapter; hdp_adapter = g_new0(struct hdp_adapter, 1); hdp_adapter->btd_adapter = btd_adapter_ref(adapter); if(!update_adapter(hdp_adapter)) goto fail; adapters = g_slist_append(adapters, hdp_adapter); return 0; fail: btd_adapter_unref(hdp_adapter->btd_adapter); g_free(hdp_adapter); return -1; } void hdp_adapter_unregister(struct btd_adapter *adapter) { struct hdp_adapter *hdp_adapter; GSList *l; l = g_slist_find_custom(adapters, adapter, cmp_adapter); if (l == NULL) return; hdp_adapter = l->data; adapters = g_slist_remove(adapters, hdp_adapter); if (hdp_adapter->sdp_handler > 0) adapter_service_remove(adapter, hdp_adapter->sdp_handler); release_adapter_instance(hdp_adapter); btd_adapter_unref(hdp_adapter->btd_adapter); g_free(hdp_adapter); } static void delete_echo_channel_cb(GError *err, gpointer chan) { if (err != NULL && err->code != MCAP_INVALID_MDL) { /* TODO: Decide if more action is required here */ error("Error deleting echo channel: %s", err->message); return; } health_channel_destroy(chan); } static void delete_echo_channel(struct hdp_channel *chan) { GError *err = NULL; if (!chan->dev->mcl_conn) { error("Echo channel cannot be deleted: mcl closed"); return; } if (mcap_delete_mdl(chan->mdl, delete_echo_channel_cb, hdp_channel_ref(chan), (GDestroyNotify) hdp_channel_unref, &err)) return; hdp_channel_unref(chan); error("Error deleting the echo channel: %s", err->message); g_error_free(err); /* TODO: Decide if more action is required here */ } static void abort_echo_channel_cb(GError *err, gpointer data) { struct hdp_channel *chan = data; if (err != NULL && err->code != MCAP_ERROR_INVALID_OPERATION) { error("Aborting error: %s", err->message); if (err->code == MCAP_INVALID_MDL) { /* MDL is removed from MCAP so we can */ /* free the data channel without sending */ /* a MD_DELETE_MDL_REQ */ /* TODO review the above comment */ /* hdp_channel_unref(chan); */ } return; } delete_echo_channel(chan); } static void destroy_create_dc_data(gpointer data) { struct hdp_create_dc *dc_data = data; hdp_create_data_unref(dc_data); } static void *generate_echo_packet(void) { uint8_t *buf; buf = g_malloc(HDP_ECHO_LEN); if (!buf) return NULL; if (util_getrandom(buf, HDP_ECHO_LEN, 0) < 0) { g_free(buf); return NULL; } return buf; } static gboolean check_echo(GIOChannel *io_chan, GIOCondition cond, gpointer data) { struct hdp_tmp_dc_data *hdp_conn = data; struct hdp_echo_data *edata = hdp_conn->hdp_chann->edata; struct hdp_channel *chan = hdp_conn->hdp_chann; uint8_t buf[MCAP_DC_MTU]; DBusMessage *reply; gboolean value; int fd, len; if (cond & (G_IO_ERR | G_IO_HUP | G_IO_NVAL)) { value = FALSE; goto end; } fd = g_io_channel_unix_get_fd(io_chan); len = read(fd, buf, sizeof(buf)); if (len != HDP_ECHO_LEN) { value = FALSE; goto end; } value = (memcmp(buf, edata->buf, len) == 0); end: reply = g_dbus_create_reply(hdp_conn->msg, DBUS_TYPE_BOOLEAN, &value, DBUS_TYPE_INVALID); g_dbus_send_message(btd_get_dbus_connection(), reply); timeout_remove(edata->tid); edata->tid = 0; g_free(edata->buf); edata->buf = NULL; if (!value) close_device_con(chan->dev, FALSE); else delete_echo_channel(chan); hdp_tmp_dc_data_unref(hdp_conn); return FALSE; } static bool echo_timeout(gpointer data) { struct hdp_channel *chan = data; GIOChannel *io; int fd; error("Error: Echo request timeout"); chan->edata->tid = 0; fd = mcap_mdl_get_fd(chan->mdl); if (fd < 0) return FALSE; io = g_io_channel_unix_new(fd); g_io_channel_shutdown(io, TRUE, NULL); return FALSE; } static void hdp_echo_connect_cb(struct mcap_mdl *mdl, GError *err, gpointer data) { DBusConnection *conn = btd_get_dbus_connection(); struct hdp_tmp_dc_data *hdp_conn = data; struct hdp_echo_data *edata; GError *gerr = NULL; DBusMessage *reply; GIOChannel *io; int fd; if (err != NULL) { reply = g_dbus_create_error(hdp_conn->msg, ERROR_INTERFACE ".HealthError", "%s", err->message); g_dbus_send_message(conn, reply); /* Send abort request because remote */ /* side is now in PENDING state. */ if (!mcap_mdl_abort(hdp_conn->hdp_chann->mdl, abort_echo_channel_cb, hdp_channel_ref(hdp_conn->hdp_chann), (GDestroyNotify) hdp_channel_unref, &gerr)) { error("%s", gerr->message); g_error_free(gerr); hdp_channel_unref(hdp_conn->hdp_chann); } return; } fd = mcap_mdl_get_fd(hdp_conn->hdp_chann->mdl); if (fd < 0) { reply = g_dbus_create_error(hdp_conn->msg, ERROR_INTERFACE ".HealthError", "Can't write in echo channel"); g_dbus_send_message(conn, reply); delete_echo_channel(hdp_conn->hdp_chann); return; } edata = hdp_conn->hdp_chann->edata; edata->buf = generate_echo_packet(); send_echo_data(fd, edata->buf, HDP_ECHO_LEN); io = g_io_channel_unix_new(fd); g_io_add_watch(io, G_IO_ERR | G_IO_HUP | G_IO_NVAL | G_IO_IN, check_echo, hdp_tmp_dc_data_ref(hdp_conn)); edata->tid = timeout_add_seconds(ECHO_TIMEOUT, echo_timeout, hdp_channel_ref(hdp_conn->hdp_chann), (timeout_destroy_func_t) hdp_channel_unref); g_io_channel_unref(io); } static void delete_mdl_cb(GError *err, gpointer data) { if (err != NULL) error("Deleting error: %s", err->message); } static void abort_and_del_mdl_cb(GError *err, gpointer data) { struct mcap_mdl *mdl = data; GError *gerr = NULL; if (err != NULL) { error("%s", err->message); if (err->code == MCAP_INVALID_MDL) { /* MDL is removed from MCAP so we don't */ /* need to delete it. */ return; } } if (!mcap_delete_mdl(mdl, delete_mdl_cb, NULL, NULL, &gerr)) { error("%s", gerr->message); g_error_free(gerr); } } static void abort_mdl_connection_cb(GError *err, gpointer data) { struct hdp_tmp_dc_data *hdp_conn = data; struct hdp_channel *hdp_chann = hdp_conn->hdp_chann; if (err != NULL) error("Aborting error: %s", err->message); /* Connection operation has failed but we have to */ /* notify the channel created at MCAP level */ if (hdp_chann->mdep != HDP_MDEP_ECHO) g_dbus_emit_signal(btd_get_dbus_connection(), device_get_path(hdp_chann->dev->dev), HEALTH_DEVICE, "ChannelConnected", DBUS_TYPE_OBJECT_PATH, &hdp_chann->path, DBUS_TYPE_INVALID); } static void hdp_mdl_conn_cb(struct mcap_mdl *mdl, GError *err, gpointer data) { DBusConnection *conn = btd_get_dbus_connection(); struct hdp_tmp_dc_data *hdp_conn = data; struct hdp_channel *hdp_chann = hdp_conn->hdp_chann; struct hdp_device *dev = hdp_chann->dev; DBusMessage *reply; GError *gerr = NULL; if (err != NULL) { error("%s", err->message); reply = g_dbus_create_reply(hdp_conn->msg, DBUS_TYPE_OBJECT_PATH, &hdp_chann->path, DBUS_TYPE_INVALID); g_dbus_send_message(conn, reply); /* Send abort request because remote side */ /* is now in PENDING state */ if (!mcap_mdl_abort(hdp_chann->mdl, abort_mdl_connection_cb, hdp_tmp_dc_data_ref(hdp_conn), hdp_tmp_dc_data_destroy, &gerr)) { hdp_tmp_dc_data_unref(hdp_conn); error("%s", gerr->message); g_error_free(gerr); } return; } reply = g_dbus_create_reply(hdp_conn->msg, DBUS_TYPE_OBJECT_PATH, &hdp_chann->path, DBUS_TYPE_INVALID); g_dbus_send_message(conn, reply); g_dbus_emit_signal(conn, device_get_path(hdp_chann->dev->dev), HEALTH_DEVICE, "ChannelConnected", DBUS_TYPE_OBJECT_PATH, &hdp_chann->path, DBUS_TYPE_INVALID); if (!check_channel_conf(hdp_chann)) { close_mdl(hdp_chann); return; } if (dev->fr != NULL) return; dev->fr = hdp_channel_ref(hdp_chann); g_dbus_emit_property_changed(btd_get_dbus_connection(), device_get_path(dev->dev), HEALTH_DEVICE, "MainChannel"); } static void device_create_mdl_cb(struct mcap_mdl *mdl, uint8_t conf, GError *err, gpointer data) { DBusConnection *conn = btd_get_dbus_connection(); struct hdp_create_dc *user_data = data; struct hdp_tmp_dc_data *hdp_conn; struct hdp_channel *hdp_chan; GError *gerr = NULL; DBusMessage *reply; if (err != NULL) { reply = g_dbus_create_error(user_data->msg, ERROR_INTERFACE ".HealthError", "%s", err->message); g_dbus_send_message(conn, reply); return; } if (user_data->mdep != HDP_MDEP_ECHO && user_data->config == HDP_NO_PREFERENCE_DC) { if (user_data->dev->fr == NULL && conf != HDP_RELIABLE_DC) { g_set_error(&gerr, HDP_ERROR, HDP_CONNECTION_ERROR, "Data channel aborted, first data " "channel should be reliable"); goto fail; } else if (conf == HDP_NO_PREFERENCE_DC || conf > HDP_STREAMING_DC) { g_set_error(&gerr, HDP_ERROR, HDP_CONNECTION_ERROR, "Data channel aborted, " "configuration error"); goto fail; } } hdp_chan = create_channel(user_data->dev, conf, mdl, mcap_mdl_get_mdlid(mdl), user_data->app, &gerr); if (hdp_chan == NULL) goto fail; hdp_conn = g_new0(struct hdp_tmp_dc_data, 1); hdp_conn->msg = dbus_message_ref(user_data->msg); hdp_conn->hdp_chann = hdp_chan; hdp_conn->cb = user_data->cb; hdp_chan->mdep = user_data->mdep; if (hdp_get_dcpsm(hdp_chan->dev, hdp_get_dcpsm_cb, hdp_tmp_dc_data_ref(hdp_conn), hdp_tmp_dc_data_destroy, &gerr)) return; error("%s", gerr->message); g_clear_error(&gerr); reply = g_dbus_create_reply(hdp_conn->msg, DBUS_TYPE_OBJECT_PATH, &hdp_chan->path, DBUS_TYPE_INVALID); g_dbus_send_message(conn, reply); hdp_tmp_dc_data_unref(hdp_conn); /* Send abort request because remote side is now in PENDING state */ if (!mcap_mdl_abort(hdp_chan->mdl, abort_mdl_connection_cb, hdp_tmp_dc_data_ref(hdp_conn), hdp_tmp_dc_data_destroy, &gerr)) { hdp_tmp_dc_data_unref(hdp_conn); error("%s", gerr->message); g_error_free(gerr); } return; fail: reply = g_dbus_create_error(user_data->msg, ERROR_INTERFACE ".HealthError", "%s", gerr->message); g_dbus_send_message(conn, reply); g_clear_error(&gerr); /* Send abort request because remote side is now in PENDING */ /* state. Then we have to delete it because we couldn't */ /* register the HealthChannel interface */ if (!mcap_mdl_abort(mdl, abort_and_del_mdl_cb, mcap_mdl_ref(mdl), (GDestroyNotify) mcap_mdl_unref, &gerr)) { error("%s", gerr->message); g_error_free(gerr); mcap_mdl_unref(mdl); } } static void device_create_dc_cb(gpointer user_data, GError *err) { DBusConnection *conn = btd_get_dbus_connection(); struct hdp_create_dc *data = user_data; DBusMessage *reply; GError *gerr = NULL; if (err != NULL) { reply = g_dbus_create_error(data->msg, ERROR_INTERFACE ".HealthError", "%s", err->message); g_dbus_send_message(conn, reply); return; } if (data->dev->mcl == NULL) { g_set_error(&gerr, HDP_ERROR, HDP_CONNECTION_ERROR, "Mcl was closed"); goto fail; } hdp_create_data_ref(data); if (mcap_create_mdl(data->dev->mcl, data->mdep, data->config, device_create_mdl_cb, data, destroy_create_dc_data, &gerr)) return; hdp_create_data_unref(data); fail: reply = g_dbus_create_error(data->msg, ERROR_INTERFACE ".HealthError", "%s", gerr->message); g_error_free(gerr); g_dbus_send_message(conn, reply); } static DBusMessage *device_echo(DBusConnection *conn, DBusMessage *msg, void *user_data) { struct hdp_device *device = user_data; struct hdp_create_dc *data; DBusMessage *reply; GError *err = NULL; data = g_new0(struct hdp_create_dc, 1); data->dev = health_device_ref(device); data->mdep = HDP_MDEP_ECHO; data->config = HDP_RELIABLE_DC; data->msg = dbus_message_ref(msg); data->cb = hdp_echo_connect_cb; hdp_create_data_ref(data); if (device->mcl_conn && device->mcl) { if (mcap_create_mdl(device->mcl, data->mdep, data->config, device_create_mdl_cb, data, destroy_create_dc_data, &err)) return NULL; goto fail; } if (hdp_establish_mcl(data->dev, device_create_dc_cb, data, destroy_create_dc_data, &err)) return NULL; fail: reply = g_dbus_create_error(msg, ERROR_INTERFACE ".HealthError", "%s", err->message); g_error_free(err); hdp_create_data_unref(data); return reply; } static void device_get_mdep_cb(uint8_t mdep, gpointer data, GError *err) { DBusConnection *conn = btd_get_dbus_connection(); struct hdp_create_dc *dc_data, *user_data = data; DBusMessage *reply; GError *gerr = NULL; if (err != NULL) { reply = g_dbus_create_error(user_data->msg, ERROR_INTERFACE ".HealthError", "%s", err->message); g_dbus_send_message(conn, reply); return; } dc_data = hdp_create_data_ref(user_data); dc_data->mdep = mdep; if (user_data->dev->mcl_conn) { device_create_dc_cb(dc_data, NULL); hdp_create_data_unref(dc_data); return; } if (hdp_establish_mcl(dc_data->dev, device_create_dc_cb, dc_data, destroy_create_dc_data, &gerr)) return; reply = g_dbus_create_error(user_data->msg, ERROR_INTERFACE ".HealthError", "%s", gerr->message); hdp_create_data_unref(dc_data); g_error_free(gerr); g_dbus_send_message(conn, reply); } static DBusMessage *device_create_channel(DBusConnection *conn, DBusMessage *msg, void *user_data) { struct hdp_device *device = user_data; struct hdp_application *app; struct hdp_create_dc *data; char *app_path, *conf; DBusMessage *reply; GError *err = NULL; uint8_t config; GSList *l; if (!dbus_message_get_args(msg, NULL, DBUS_TYPE_OBJECT_PATH, &app_path, DBUS_TYPE_STRING, &conf, DBUS_TYPE_INVALID)) return btd_error_invalid_args(msg); l = g_slist_find_custom(applications, app_path, cmp_app); if (l == NULL) return btd_error_invalid_args(msg); app = l->data; if (g_ascii_strcasecmp("reliable", conf) == 0) config = HDP_RELIABLE_DC; else if (g_ascii_strcasecmp("streaming", conf) == 0) config = HDP_STREAMING_DC; else if (g_ascii_strcasecmp("any", conf) == 0) config = HDP_NO_PREFERENCE_DC; else return btd_error_invalid_args(msg); if (app->role == HDP_SINK && config != HDP_NO_PREFERENCE_DC) return btd_error_invalid_args(msg); if (app->role == HDP_SOURCE && config == HDP_NO_PREFERENCE_DC) return btd_error_invalid_args(msg); if (!device->fr && config == HDP_STREAMING_DC) return btd_error_invalid_args(msg); data = g_new0(struct hdp_create_dc, 1); data->dev = health_device_ref(device); data->config = config; data->app = hdp_application_ref(app); data->msg = dbus_message_ref(msg); data->cb = hdp_mdl_conn_cb; if (hdp_get_mdep(device, l->data, device_get_mdep_cb, hdp_create_data_ref(data), destroy_create_dc_data, &err)) return NULL; reply = g_dbus_create_error(msg, ERROR_INTERFACE ".HealthError", "%s", err->message); g_error_free(err); hdp_create_data_unref(data); return reply; } static void hdp_mdl_delete_cb(GError *err, gpointer data) { DBusConnection *conn = btd_get_dbus_connection(); struct hdp_tmp_dc_data *del_data = data; DBusMessage *reply; char *path; if (err != NULL && err->code != MCAP_INVALID_MDL) { reply = g_dbus_create_error(del_data->msg, ERROR_INTERFACE ".HealthError", "%s", err->message); g_dbus_send_message(conn, reply); return; } path = g_strdup(del_data->hdp_chann->path); g_dbus_unregister_interface(conn, path, HEALTH_CHANNEL); g_free(path); reply = g_dbus_create_reply(del_data->msg, DBUS_TYPE_INVALID); g_dbus_send_message(conn, reply); } static void hdp_continue_del_cb(gpointer user_data, GError *err) { DBusConnection *conn = btd_get_dbus_connection(); struct hdp_tmp_dc_data *del_data = user_data; GError *gerr = NULL; DBusMessage *reply; if (err != NULL) { reply = g_dbus_create_error(del_data->msg, ERROR_INTERFACE ".HealthError", "%s", err->message); g_dbus_send_message(conn, reply); return; } if (mcap_delete_mdl(del_data->hdp_chann->mdl, hdp_mdl_delete_cb, hdp_tmp_dc_data_ref(del_data), hdp_tmp_dc_data_destroy, &gerr)) return; reply = g_dbus_create_error(del_data->msg, ERROR_INTERFACE ".HealthError", "%s", gerr->message); hdp_tmp_dc_data_unref(del_data); g_error_free(gerr); g_dbus_send_message(conn, reply); } static DBusMessage *device_destroy_channel(DBusConnection *conn, DBusMessage *msg, void *user_data) { struct hdp_device *device = user_data; struct hdp_tmp_dc_data *del_data; struct hdp_channel *hdp_chan; DBusMessage *reply; GError *err = NULL; char *path; GSList *l; if (!dbus_message_get_args(msg, NULL, DBUS_TYPE_OBJECT_PATH, &path, DBUS_TYPE_INVALID)){ return btd_error_invalid_args(msg); } l = g_slist_find_custom(device->channels, path, cmp_chan_path); if (l == NULL) return btd_error_invalid_args(msg); hdp_chan = l->data; del_data = g_new0(struct hdp_tmp_dc_data, 1); del_data->msg = dbus_message_ref(msg); del_data->hdp_chann = hdp_channel_ref(hdp_chan); if (device->mcl_conn) { if (mcap_delete_mdl(hdp_chan->mdl, hdp_mdl_delete_cb, hdp_tmp_dc_data_ref(del_data), hdp_tmp_dc_data_destroy, &err)) return NULL; goto fail; } if (hdp_establish_mcl(device, hdp_continue_del_cb, hdp_tmp_dc_data_ref(del_data), hdp_tmp_dc_data_destroy, &err)) return NULL; fail: reply = g_dbus_create_error(msg, ERROR_INTERFACE ".HealthError", "%s", err->message); hdp_tmp_dc_data_unref(del_data); g_error_free(err); return reply; } static gboolean dev_property_exists_main_channel( const GDBusPropertyTable *property, void *data) { struct hdp_device *device = data; return device->fr != NULL; } static gboolean dev_property_get_main_channel( const GDBusPropertyTable *property, DBusMessageIter *iter, void *data) { struct hdp_device *device = data; if (device->fr == NULL) return FALSE; dbus_message_iter_append_basic(iter, DBUS_TYPE_OBJECT_PATH, &device->fr->path); return TRUE; } static void health_device_destroy(void *data) { struct hdp_device *device = data; DBG("Unregistered interface %s on path %s", HEALTH_DEVICE, device_get_path(device->dev)); remove_channels(device); if (device->ndc != NULL) { hdp_channel_unref(device->ndc); device->ndc = NULL; } devices = g_slist_remove(devices, device); health_device_unref(device); } static const GDBusMethodTable health_device_methods[] = { { GDBUS_ASYNC_METHOD("Echo", NULL, GDBUS_ARGS({ "value", "b" }), device_echo) }, { GDBUS_ASYNC_METHOD("CreateChannel", GDBUS_ARGS({ "application", "o" }, { "configuration", "s" }), GDBUS_ARGS({ "channel", "o" }), device_create_channel) }, { GDBUS_ASYNC_METHOD("DestroyChannel", GDBUS_ARGS({ "channel", "o" }), NULL, device_destroy_channel) }, { } }; static const GDBusSignalTable health_device_signals[] = { { GDBUS_SIGNAL("ChannelConnected", GDBUS_ARGS({ "channel", "o" })) }, { GDBUS_SIGNAL("ChannelDeleted", GDBUS_ARGS({ "channel", "o" })) }, { } }; static const GDBusPropertyTable health_device_properties[] = { { "MainChannel", "o", dev_property_get_main_channel, NULL, dev_property_exists_main_channel }, { } }; static struct hdp_device *create_health_device(struct btd_device *device) { struct btd_adapter *adapter = device_get_adapter(device); const char *path = device_get_path(device); struct hdp_device *dev; GSList *l; if (device == NULL) return NULL; dev = g_new0(struct hdp_device, 1); dev->dev = btd_device_ref(device); health_device_ref(dev); l = g_slist_find_custom(adapters, adapter, cmp_adapter); if (l == NULL) goto fail; dev->hdp_adapter = l->data; if (!g_dbus_register_interface(btd_get_dbus_connection(), path, HEALTH_DEVICE, health_device_methods, health_device_signals, health_device_properties, dev, health_device_destroy)) { error("D-Bus failed to register %s interface", HEALTH_DEVICE); goto fail; } DBG("Registered interface %s on path %s", HEALTH_DEVICE, path); return dev; fail: health_device_unref(dev); return NULL; } int hdp_device_register(struct btd_device *device) { struct hdp_device *hdev; GSList *l; l = g_slist_find_custom(devices, device, cmp_device); if (l != NULL) { hdev = l->data; hdev->sdp_present = TRUE; return 0; } hdev = create_health_device(device); if (hdev == NULL) return -1; hdev->sdp_present = TRUE; devices = g_slist_prepend(devices, hdev); return 0; } void hdp_device_unregister(struct btd_device *device) { struct hdp_device *hdp_dev; const char *path; GSList *l; l = g_slist_find_custom(devices, device, cmp_device); if (l == NULL) return; hdp_dev = l->data; path = device_get_path(hdp_dev->dev); g_dbus_unregister_interface(btd_get_dbus_connection(), path, HEALTH_DEVICE); } int hdp_manager_start(void) { DBG("Starting Health manager"); if (!g_dbus_register_interface(btd_get_dbus_connection(), MANAGER_PATH, HEALTH_MANAGER, health_manager_methods, NULL, NULL, NULL, manager_path_unregister)) { error("D-Bus failed to register %s interface", HEALTH_MANAGER); return -1; } return 0; } void hdp_manager_stop(void) { g_dbus_unregister_interface(btd_get_dbus_connection(), MANAGER_PATH, HEALTH_MANAGER); DBG("Stopped Health manager"); } struct hdp_device *health_device_ref(struct hdp_device *hdp_dev) { hdp_dev->ref++; DBG("(%p): ref=%d", hdp_dev, hdp_dev->ref); return hdp_dev; } void health_device_unref(struct hdp_device *hdp_dev) { hdp_dev->ref--; DBG("(%p): ref=%d", hdp_dev, hdp_dev->ref); if (hdp_dev->ref > 0) return; free_health_device(hdp_dev); } bluez-5.82/profiles/health/PaxHeaders/hdp_util.h0000644000000000000000000000005014015011623016710 xustar0020 atime=1743516870 20 ctime=1743591284 bluez-5.82/profiles/health/hdp_util.h0000644000000000000000000000260314015011623016372 0ustar00rootroot/* SPDX-License-Identifier: GPL-2.0-or-later */ /* * * BlueZ - Bluetooth protocol stack for Linux * * Copyright (C) 2010 GSyC/LibreSoft, Universidad Rey Juan Carlos. * */ #ifndef __HDP_UTIL_H__ #define __HDP_UTIL_H__ typedef void (*hdp_continue_mdep_f)(uint8_t mdep, gpointer user_data, GError *err); typedef void (*hdp_continue_dcpsm_f)(uint16_t dcpsm, gpointer user_data, GError *err); typedef void (*hdp_continue_proc_f)(gpointer user_data, GError *err); struct hdp_application *hdp_get_app_config(DBusMessageIter *iter, GError **err); gboolean hdp_update_sdp_record(struct hdp_adapter *adapter, GSList *app_list); gboolean hdp_get_mdep(struct hdp_device *device, struct hdp_application *app, hdp_continue_mdep_f func, gpointer data, GDestroyNotify destroy, GError **err); gboolean hdp_establish_mcl(struct hdp_device *device, hdp_continue_proc_f func, gpointer data, GDestroyNotify destroy, GError **err); gboolean hdp_get_dcpsm(struct hdp_device *device, hdp_continue_dcpsm_f func, gpointer data, GDestroyNotify destroy, GError **err); struct hdp_application *hdp_application_ref(struct hdp_application *app); void hdp_application_unref(struct hdp_application *app); struct hdp_device *health_device_ref(struct hdp_device *hdp_dev); void health_device_unref(struct hdp_device *hdp_dev); #endif /* __HDP_UTIL_H__ */ bluez-5.82/profiles/health/PaxHeaders/hdp_types.h0000644000000000000000000000005014015011623017077 xustar0020 atime=1743516870 20 ctime=1743591284 bluez-5.82/profiles/health/hdp_types.h0000644000000000000000000000636214015011623016567 0ustar00rootroot/* SPDX-License-Identifier: GPL-2.0-or-later */ /* * * BlueZ - Bluetooth protocol stack for Linux * * Copyright (C) 2010 GSyC/LibreSoft, Universidad Rey Juan Carlos. * */ #ifndef __HDP_TYPES_H__ #define __HDP_TYPES_H__ #define MANAGER_PATH "/org/bluez" #define HEALTH_MANAGER "org.bluez.HealthManager1" #define HEALTH_DEVICE "org.bluez.HealthDevice1" #define HEALTH_CHANNEL "org.bluez.HealthChannel1" #define HDP_VERSION 0x0100 #define HDP_SERVICE_NAME "Bluez HDP" #define HDP_SERVICE_DSC "A Bluez health device profile implementation" #define HDP_SERVICE_PROVIDER "Bluez" #define HDP_MDEP_ECHO 0x00 #define HDP_MDEP_INITIAL 0x01 #define HDP_MDEP_FINAL 0x7F #define HDP_ERROR g_quark_from_static_string("hdp-error-quark") #define HDP_NO_PREFERENCE_DC 0x00 #define HDP_RELIABLE_DC 0x01 #define HDP_STREAMING_DC 0x02 #define HDP_SINK_ROLE_AS_STRING "sink" #define HDP_SOURCE_ROLE_AS_STRING "source" typedef enum { HDP_SOURCE = 0x00, HDP_SINK = 0x01 } HdpRole; typedef enum { HDP_DIC_PARSE_ERROR, HDP_DIC_ENTRY_PARSE_ERROR, HDP_CONNECTION_ERROR, HDP_UNSPECIFIED_ERROR, HDP_UNKNOWN_ERROR } HdpError; enum data_specs { DATA_EXCHANGE_SPEC_11073 = 0x01 }; struct hdp_application { char *path; /* The path of the application */ uint16_t data_type; /* Data type handled for this application */ gboolean data_type_set; /* Flag for dictionary parsing */ uint8_t role; /* Role of this application */ gboolean role_set; /* Flag for dictionary parsing */ uint8_t chan_type; /* QoS preferred by source applications */ gboolean chan_type_set; /* Flag for dictionary parsing */ char *description; /* Options description for SDP record */ uint8_t id; /* The identification is also the mdepid */ char *oname; /* Name of the owner application */ guint dbus_watcher; /* Watch for clients disconnection */ int ref; /* Reference counter */ }; struct hdp_adapter { struct btd_adapter *btd_adapter; /* Bluetooth adapter */ struct mcap_instance *mi; /* Mcap instance in */ uint16_t ccpsm; /* Control channel psm */ uint16_t dcpsm; /* Data channel psm */ uint32_t sdp_handler; /* SDP record handler */ uint32_t record_state; /* Service record state */ }; struct hdp_device { struct btd_device *dev; /* Device reference */ struct hdp_adapter *hdp_adapter; /* hdp_adapater */ struct mcap_mcl *mcl; /* The mcap control channel */ gboolean mcl_conn; /* Mcl status */ gboolean sdp_present; /* Has an sdp record */ GSList *channels; /* Data Channel list */ struct hdp_channel *ndc; /* Data channel being negotiated */ struct hdp_channel *fr; /* First reliable data channel */ int ref; /* Reference counting */ }; struct hdp_echo_data; struct hdp_channel { struct hdp_device *dev; /* Device where this channel belongs */ struct hdp_application *app; /* Application */ struct mcap_mdl *mdl; /* The data channel reference */ char *path; /* The path of the channel */ uint8_t config; /* Channel configuration */ uint8_t mdep; /* Remote MDEP */ uint16_t mdlid; /* Data channel Id */ uint16_t imtu; /* Channel incoming MTU */ uint16_t omtu; /* Channel outgoing MTU */ struct hdp_echo_data *edata; /* private data used by echo channels */ int ref; /* Reference counter */ }; #endif /* __HDP_TYPES_H__ */ bluez-5.82/profiles/PaxHeaders/scanparam0000644000000000000000000000005014773213564015375 xustar0020 atime=1743591291 20 ctime=1743591284 bluez-5.82/profiles/scanparam/0000755000000000000000000000000014773213564015133 5ustar00rootrootbluez-5.82/profiles/scanparam/PaxHeaders/scan.c0000644000000000000000000000005014015011623016517 xustar0020 atime=1743516571 20 ctime=1743591284 bluez-5.82/profiles/scanparam/scan.c0000644000000000000000000001520614015011623016204 0ustar00rootroot// SPDX-License-Identifier: GPL-2.0-or-later /* * * BlueZ - Bluetooth protocol stack for Linux * * Copyright (C) 2012 Nordic Semiconductor Inc. * Copyright (C) 2012 Instituto Nokia de Tecnologia - INdT * * */ #ifdef HAVE_CONFIG_H #include #endif #include #include #include "lib/bluetooth.h" #include "lib/sdp.h" #include "lib/uuid.h" #include "src/log.h" #include "src/plugin.h" #include "src/adapter.h" #include "src/device.h" #include "src/profile.h" #include "src/service.h" #include "src/shared/util.h" #include "src/shared/att.h" #include "src/shared/queue.h" #include "src/shared/gatt-db.h" #include "src/shared/gatt-client.h" #include "attrib/att.h" #include "src/btd.h" #define SCAN_INTERVAL_WIN_UUID 0x2A4F #define SCAN_REFRESH_UUID 0x2A31 #define SERVER_REQUIRES_REFRESH 0x00 struct scan { struct btd_device *device; struct gatt_db *db; struct bt_gatt_client *client; struct gatt_db_attribute *attr; uint16_t iwhandle; guint refresh_cb_id; }; static void scan_free(struct scan *scan) { bt_gatt_client_unregister_notify(scan->client, scan->refresh_cb_id); gatt_db_unref(scan->db); bt_gatt_client_unref(scan->client); btd_device_unref(scan->device); g_free(scan); } static void write_scan_params(struct scan *scan) { uint8_t value[4]; /* Unless scan parameters are configured, use the known kernel default * parameters */ put_le16(btd_opts.defaults.le.scan_interval_autoconnect ? btd_opts.defaults.le.scan_interval_autoconnect : 0x60, &value[0]); put_le16(btd_opts.defaults.le.scan_win_autoconnect ? btd_opts.defaults.le.scan_win_autoconnect : 0x30, &value[2]); bt_gatt_client_write_without_response(scan->client, scan->iwhandle, false, value, sizeof(value)); } static void refresh_value_cb(uint16_t value_handle, const uint8_t *value, uint16_t length, void *user_data) { struct scan *scan = user_data; DBG("Server requires refresh: %d", value[3]); if (value[3] == SERVER_REQUIRES_REFRESH) write_scan_params(scan); } static void refresh_ccc_written_cb(uint16_t att_ecode, void *user_data) { if (att_ecode != 0) { error("Scan Refresh: notifications not enabled %s", att_ecode2str(att_ecode)); return; } DBG("Scan Refresh: notification enabled"); } static void handle_refresh(struct scan *scan, uint16_t value_handle) { DBG("Scan Refresh handle: 0x%04x", value_handle); scan->refresh_cb_id = bt_gatt_client_register_notify(scan->client, value_handle, refresh_ccc_written_cb, refresh_value_cb, scan, NULL); } static void handle_iwin(struct scan *scan, uint16_t value_handle) { scan->iwhandle = value_handle; DBG("Scan Interval Window handle: 0x%04x", scan->iwhandle); write_scan_params(scan); } static void handle_characteristic(struct gatt_db_attribute *attr, void *user_data) { struct scan *scan = user_data; uint16_t value_handle; bt_uuid_t uuid, scan_interval_wind_uuid, scan_refresh_uuid; if (!gatt_db_attribute_get_char_data(attr, NULL, &value_handle, NULL, NULL, &uuid)) { error("Failed to obtain characteristic data"); return; } bt_uuid16_create(&scan_interval_wind_uuid, SCAN_INTERVAL_WIN_UUID); bt_uuid16_create(&scan_refresh_uuid, SCAN_REFRESH_UUID); if (bt_uuid_cmp(&scan_interval_wind_uuid, &uuid) == 0) handle_iwin(scan, value_handle); else if (bt_uuid_cmp(&scan_refresh_uuid, &uuid) == 0) handle_refresh(scan, value_handle); else { char uuid_str[MAX_LEN_UUID_STR]; bt_uuid_to_string(&uuid, uuid_str, sizeof(uuid_str)); DBG("Unsupported characteristic: %s", uuid_str); } } static void foreach_scan_param_service(struct gatt_db_attribute *attr, void *user_data) { struct scan *scan = user_data; if (scan->attr) { error("More than one scan params service exists for this " "device"); return; } scan->attr = attr; gatt_db_service_foreach_char(scan->attr, handle_characteristic, scan); } static void scan_reset(struct scan *scan) { scan->attr = NULL; gatt_db_unref(scan->db); scan->db = NULL; bt_gatt_client_unref(scan->client); scan->client = NULL; } static int scan_param_accept(struct btd_service *service) { struct btd_device *device = btd_service_get_device(service); struct gatt_db *db = btd_device_get_gatt_db(device); struct bt_gatt_client *client = btd_device_get_gatt_client(device); bt_uuid_t scan_parameters_uuid; struct scan *scan = btd_service_get_user_data(service); char addr[18]; ba2str(device_get_address(device), addr); DBG("Scan Parameters Client Driver profile accept (%s)", addr); if (!scan) { error("Scan Parameters service not handled by profile"); return -1; } scan->db = gatt_db_ref(db); scan->client = bt_gatt_client_clone(client); bt_string_to_uuid(&scan_parameters_uuid, SCAN_PARAMETERS_UUID); gatt_db_foreach_service(db, &scan_parameters_uuid, foreach_scan_param_service, scan); if (!scan->attr) { error("Scan Parameters attribute not found"); scan_reset(scan); return -1; } btd_service_connecting_complete(service, 0); return 0; } static int scan_param_disconnect(struct btd_service *service) { struct scan *scan = btd_service_get_user_data(service); scan_reset(scan); btd_service_disconnecting_complete(service, 0); return 0; } static void scan_param_remove(struct btd_service *service) { struct btd_device *device = btd_service_get_device(service); struct scan *scan; char addr[18]; ba2str(device_get_address(device), addr); DBG("GAP profile remove (%s)", addr); scan = btd_service_get_user_data(service); if (!scan) { error("GAP service not handled by profile"); return; } scan_free(scan); } static int scan_param_probe(struct btd_service *service) { struct btd_device *device = btd_service_get_device(service); struct scan *scan; char addr[18]; ba2str(device_get_address(device), addr); DBG("Scan Parameters Client Driver profile probe (%s)", addr); /* Ignore, if we were probed for this device already */ scan = btd_service_get_user_data(service); if (scan) { error("Profile probed twice for the same service!"); return -1; } scan = g_new0(struct scan, 1); if (!scan) return -1; scan->device = btd_device_ref(device); btd_service_set_user_data(service, scan); return 0; } static struct btd_profile scan_profile = { .name = "Scan Parameters Client Driver", .remote_uuid = SCAN_PARAMETERS_UUID, .device_probe = scan_param_probe, .device_remove = scan_param_remove, .accept = scan_param_accept, .disconnect = scan_param_disconnect, }; static int scan_param_init(void) { return btd_profile_register(&scan_profile); } static void scan_param_exit(void) { btd_profile_unregister(&scan_profile); } BLUETOOTH_PLUGIN_DEFINE(scanparam, VERSION, BLUETOOTH_PLUGIN_PRIORITY_DEFAULT, scan_param_init, scan_param_exit) bluez-5.82/profiles/scanparam/PaxHeaders/scpp.h0000644000000000000000000000005014015011623016545 xustar0020 atime=1743516562 20 ctime=1743591278 bluez-5.82/profiles/scanparam/scpp.h0000644000000000000000000000106314015011623016226 0ustar00rootroot/* SPDX-License-Identifier: LGPL-2.1-or-later */ /* * * BlueZ - Bluetooth protocol stack for Linux * * Copyright (C) 2014 Intel Corporation. All rights reserved. * * */ struct bt_scpp; struct bt_scpp *bt_scpp_new(void *primary); struct bt_scpp *bt_scpp_ref(struct bt_scpp *scan); void bt_scpp_unref(struct bt_scpp *scan); bool bt_scpp_attach(struct bt_scpp *scan, void *gatt); void bt_scpp_detach(struct bt_scpp *scan); bool bt_scpp_set_interval(struct bt_scpp *scan, uint16_t value); bool bt_scpp_set_window(struct bt_scpp *scan, uint16_t value); bluez-5.82/profiles/scanparam/PaxHeaders/scpp.c0000644000000000000000000000005014214376354016560 xustar0020 atime=1743516568 20 ctime=1743591278 bluez-5.82/profiles/scanparam/scpp.c0000644000000000000000000001616514214376354016252 0ustar00rootroot// SPDX-License-Identifier: GPL-2.0-or-later /* * * BlueZ - Bluetooth protocol stack for Linux * * Copyright (C) 2012 Nordic Semiconductor Inc. * Copyright (C) 2012 Instituto Nokia de Tecnologia - INdT * * */ #ifdef HAVE_CONFIG_H #include #endif #include #include #include #include "src/log.h" #include "lib/bluetooth.h" #include "lib/sdp.h" #include "lib/uuid.h" #include "src/shared/util.h" #include "src/shared/queue.h" #include "attrib/att.h" #include "attrib/gattrib.h" #include "attrib/gatt.h" #include "profiles/scanparam/scpp.h" #define SCAN_INTERVAL_WIN_UUID 0x2A4F #define SCAN_REFRESH_UUID 0x2A31 #define SCAN_INTERVAL 0x0060 #define SCAN_WINDOW 0x0030 #define SERVER_REQUIRES_REFRESH 0x00 struct bt_scpp { int ref_count; GAttrib *attrib; struct gatt_primary *primary; uint16_t interval; uint16_t window; uint16_t iwhandle; uint16_t refresh_handle; guint refresh_cb_id; struct queue *gatt_op; }; static void discover_char(struct bt_scpp *scpp, GAttrib *attrib, uint16_t start, uint16_t end, bt_uuid_t *uuid, gatt_cb_t func, gpointer user_data) { unsigned int id; id = gatt_discover_char(attrib, start, end, uuid, func, user_data); queue_push_head(scpp->gatt_op, UINT_TO_PTR(id)); } static void discover_desc(struct bt_scpp *scpp, GAttrib *attrib, uint16_t start, uint16_t end, bt_uuid_t *uuid, gatt_cb_t func, gpointer user_data) { unsigned int id; id = gatt_discover_desc(attrib, start, end, uuid, func, user_data); queue_push_head(scpp->gatt_op, UINT_TO_PTR(id)); } static void write_char(struct bt_scpp *scan, GAttrib *attrib, uint16_t handle, const uint8_t *value, size_t vlen, GAttribResultFunc func, gpointer user_data) { unsigned int id; id = gatt_write_char(attrib, handle, value, vlen, func, user_data); queue_push_head(scan->gatt_op, UINT_TO_PTR(id)); } static void scpp_free(struct bt_scpp *scan) { bt_scpp_detach(scan); free(scan->primary); queue_destroy(scan->gatt_op, NULL); /* cleared in bt_scpp_detach */ g_free(scan); } struct bt_scpp *bt_scpp_new(void *primary) { struct bt_scpp *scan; scan = g_try_new0(struct bt_scpp, 1); if (!scan) return NULL; scan->interval = SCAN_INTERVAL; scan->window = SCAN_WINDOW; scan->gatt_op = queue_new(); if (primary) scan->primary = util_memdup(primary, sizeof(*scan->primary)); return bt_scpp_ref(scan); } struct bt_scpp *bt_scpp_ref(struct bt_scpp *scan) { if (!scan) return NULL; __sync_fetch_and_add(&scan->ref_count, 1); return scan; } void bt_scpp_unref(struct bt_scpp *scan) { if (!scan) return; if (__sync_sub_and_fetch(&scan->ref_count, 1)) return; scpp_free(scan); } static void write_scan_params(GAttrib *attrib, uint16_t handle, uint16_t interval, uint16_t window) { uint8_t value[4]; put_le16(interval, &value[0]); put_le16(window, &value[2]); gatt_write_cmd(attrib, handle, value, sizeof(value), NULL, NULL); } static void refresh_value_cb(const uint8_t *pdu, uint16_t len, gpointer user_data) { struct bt_scpp *scan = user_data; DBG("Server requires refresh: %d", pdu[3]); if (pdu[3] == SERVER_REQUIRES_REFRESH) write_scan_params(scan->attrib, scan->iwhandle, scan->interval, scan->window); } static void ccc_written_cb(guint8 status, const guint8 *pdu, guint16 plen, gpointer user_data) { struct bt_scpp *scan = user_data; if (status != 0) { error("Write Scan Refresh CCC failed: %s", att_ecode2str(status)); return; } DBG("Scan Refresh: notification enabled"); scan->refresh_cb_id = g_attrib_register(scan->attrib, ATT_OP_HANDLE_NOTIFY, scan->refresh_handle, refresh_value_cb, scan, NULL); } static void write_ccc(struct bt_scpp *scan, GAttrib *attrib, uint16_t handle, void *user_data) { uint8_t value[2]; put_le16(GATT_CLIENT_CHARAC_CFG_NOTIF_BIT, value); write_char(scan, attrib, handle, value, sizeof(value), ccc_written_cb, user_data); } static void discover_descriptor_cb(uint8_t status, GSList *descs, void *user_data) { struct bt_scpp *scan = user_data; struct gatt_desc *desc; if (status != 0) { error("Discover descriptors failed: %s", att_ecode2str(status)); return; } /* There will be only one descriptor on list and it will be CCC */ desc = descs->data; write_ccc(scan, scan->attrib, desc->handle, scan); } static void refresh_discovered_cb(uint8_t status, GSList *chars, void *user_data) { struct bt_scpp *scan = user_data; struct gatt_char *chr; uint16_t start, end; bt_uuid_t uuid; if (status) { error("Scan Refresh %s", att_ecode2str(status)); return; } if (!chars) { DBG("Scan Refresh not supported"); return; } chr = chars->data; DBG("Scan Refresh handle: 0x%04x", chr->value_handle); start = chr->value_handle + 1; end = scan->primary->range.end; if (start > end) return; scan->refresh_handle = chr->value_handle; bt_uuid16_create(&uuid, GATT_CLIENT_CHARAC_CFG_UUID); discover_desc(scan, scan->attrib, start, end, &uuid, discover_descriptor_cb, user_data); } static void iwin_discovered_cb(uint8_t status, GSList *chars, void *user_data) { struct bt_scpp *scan = user_data; struct gatt_char *chr; if (status) { error("Discover Scan Interval Window: %s", att_ecode2str(status)); return; } chr = chars->data; scan->iwhandle = chr->value_handle; DBG("Scan Interval Window handle: 0x%04x", scan->iwhandle); write_scan_params(scan->attrib, scan->iwhandle, scan->interval, scan->window); } bool bt_scpp_attach(struct bt_scpp *scan, void *attrib) { bt_uuid_t iwin_uuid, refresh_uuid; if (!scan || scan->attrib || !scan->primary) return false; scan->attrib = g_attrib_ref(attrib); if (scan->iwhandle) write_scan_params(scan->attrib, scan->iwhandle, scan->interval, scan->window); else { bt_uuid16_create(&iwin_uuid, SCAN_INTERVAL_WIN_UUID); discover_char(scan, scan->attrib, scan->primary->range.start, scan->primary->range.end, &iwin_uuid, iwin_discovered_cb, scan); } if (scan->refresh_handle) scan->refresh_cb_id = g_attrib_register(scan->attrib, ATT_OP_HANDLE_NOTIFY, scan->refresh_handle, refresh_value_cb, scan, NULL); else { bt_uuid16_create(&refresh_uuid, SCAN_REFRESH_UUID); discover_char(scan, scan->attrib, scan->primary->range.start, scan->primary->range.end, &refresh_uuid, refresh_discovered_cb, scan); } return true; } static void cancel_gatt_req(void *data, void *user_data) { unsigned int id = PTR_TO_UINT(data); struct bt_scpp *scan = user_data; g_attrib_cancel(scan->attrib, id); } void bt_scpp_detach(struct bt_scpp *scan) { if (!scan || !scan->attrib) return; if (scan->refresh_cb_id > 0) { g_attrib_unregister(scan->attrib, scan->refresh_cb_id); scan->refresh_cb_id = 0; } queue_foreach(scan->gatt_op, cancel_gatt_req, scan); g_attrib_unref(scan->attrib); scan->attrib = NULL; } bool bt_scpp_set_interval(struct bt_scpp *scan, uint16_t value) { if (!scan) return false; /* TODO: Check valid range */ scan->interval = value; return true; } bool bt_scpp_set_window(struct bt_scpp *scan, uint16_t value) { if (!scan) return false; /* TODO: Check valid range */ scan->window = value; return true; } bluez-5.82/profiles/PaxHeaders/input0000644000000000000000000000005014773213570014564 xustar0020 atime=1743591291 20 ctime=1743591288 bluez-5.82/profiles/input/0000755000000000000000000000000014773213570014322 5ustar00rootrootbluez-5.82/profiles/input/PaxHeaders/suspend-none.c0000644000000000000000000000005014015011623017403 xustar0020 atime=1743516569 20 ctime=1743591284 bluez-5.82/profiles/input/suspend-none.c0000644000000000000000000000067114015011623017070 0ustar00rootroot// SPDX-License-Identifier: GPL-2.0-or-later /* * * BlueZ - Bluetooth protocol stack for Linux * * Copyright (C) 2012 Nordic Semiconductor Inc. * Copyright (C) 2012 Instituto Nokia de Tecnologia - INdT * * */ #ifdef HAVE_CONFIG_H #include #endif #include "src/log.h" #include "suspend.h" int suspend_init(suspend_event suspend, resume_event resume) { DBG(""); return 0; } void suspend_exit(void) { DBG(""); } bluez-5.82/profiles/input/PaxHeaders/server.c0000644000000000000000000000005014015011623016273 xustar0020 atime=1743516556 20 ctime=1743591284 bluez-5.82/profiles/input/server.c0000644000000000000000000001603114015011623015755 0ustar00rootroot// SPDX-License-Identifier: GPL-2.0-or-later /* * * BlueZ - Bluetooth protocol stack for Linux * * Copyright (C) 2004-2010 Marcel Holtmann * * */ #ifdef HAVE_CONFIG_H #include #endif #include #include #include #include #include #include "lib/bluetooth.h" #include "lib/sdp.h" #include "lib/uuid.h" #include "src/log.h" #include "src/uuid-helper.h" #include "btio/btio.h" #include "src/adapter.h" #include "src/device.h" #include "src/profile.h" #include "sixaxis.h" #include "device.h" #include "server.h" struct confirm_data { bdaddr_t dst; GIOChannel *io; }; static GSList *servers = NULL; struct input_server { bdaddr_t src; GIOChannel *ctrl; GIOChannel *intr; struct confirm_data *confirm; }; static int server_cmp(gconstpointer s, gconstpointer user_data) { const struct input_server *server = s; const bdaddr_t *src = user_data; return bacmp(&server->src, src); } struct sixaxis_data { GIOChannel *chan; uint16_t psm; }; static void sixaxis_sdp_cb(struct btd_device *dev, int err, void *user_data) { struct sixaxis_data *data = user_data; const bdaddr_t *src; DBG("err %d (%s)", err, strerror(-err)); if (err < 0) goto fail; src = btd_adapter_get_address(device_get_adapter(dev)); if (input_device_set_channel(src, device_get_address(dev), data->psm, data->chan) < 0) goto fail; g_io_channel_unref(data->chan); g_free(data); return; fail: g_io_channel_shutdown(data->chan, TRUE, NULL); g_io_channel_unref(data->chan); g_free(data); } static void sixaxis_browse_sdp(const bdaddr_t *src, const bdaddr_t *dst, GIOChannel *chan, uint16_t psm) { struct btd_device *device; struct sixaxis_data *data; device = btd_adapter_find_device(adapter_find(src), dst, BDADDR_BREDR); if (!device) return; data = g_new0(struct sixaxis_data, 1); data->chan = g_io_channel_ref(chan); data->psm = psm; if (psm == L2CAP_PSM_HIDP_CTRL) device_discover_services(device); device_wait_for_svc_complete(device, sixaxis_sdp_cb, data); } static bool dev_is_sixaxis(const bdaddr_t *src, const bdaddr_t *dst) { struct btd_device *device; uint16_t vid, pid; const struct cable_pairing *cp; device = btd_adapter_find_device(adapter_find(src), dst, BDADDR_BREDR); if (!device) return false; vid = btd_device_get_vendor(device); pid = btd_device_get_product(device); cp = get_pairing(vid, pid, NULL); if (cp && (cp->type == CABLE_PAIRING_SIXAXIS || cp->type == CABLE_PAIRING_DS4)) return true; return false; } static void connect_event_cb(GIOChannel *chan, GError *err, gpointer data) { uint16_t psm; bdaddr_t src, dst; char address[18]; GError *gerr = NULL; int ret; if (err) { error("%s", err->message); return; } bt_io_get(chan, &gerr, BT_IO_OPT_SOURCE_BDADDR, &src, BT_IO_OPT_DEST_BDADDR, &dst, BT_IO_OPT_PSM, &psm, BT_IO_OPT_INVALID); if (gerr) { error("%s", gerr->message); g_error_free(gerr); g_io_channel_shutdown(chan, TRUE, NULL); return; } ba2str(&dst, address); DBG("Incoming connection from %s on PSM %d", address, psm); ret = input_device_set_channel(&src, &dst, psm, chan); if (ret == 0) return; if (ret == -ENOENT && dev_is_sixaxis(&src, &dst)) { sixaxis_browse_sdp(&src, &dst, chan, psm); return; } error("Refusing input device connect: %s (%d)", strerror(-ret), -ret); /* Send unplug virtual cable to unknown devices */ if (ret == -ENOENT && psm == L2CAP_PSM_HIDP_CTRL) { unsigned char unplug = 0x15; int sk = g_io_channel_unix_get_fd(chan); if (write(sk, &unplug, sizeof(unplug)) < 0) error("Unable to send virtual cable unplug"); } g_io_channel_shutdown(chan, TRUE, NULL); } static void auth_callback(DBusError *derr, void *user_data) { struct input_server *server = user_data; struct confirm_data *confirm = server->confirm; GError *err = NULL; if (derr) { error("Access denied: %s", derr->message); goto reject; } if (!input_device_exists(&server->src, &confirm->dst) && !dev_is_sixaxis(&server->src, &confirm->dst)) return; if (!bt_io_accept(confirm->io, connect_event_cb, server, NULL, &err)) { error("bt_io_accept: %s", err->message); g_error_free(err); goto reject; } g_io_channel_unref(confirm->io); g_free(server->confirm); server->confirm = NULL; return; reject: g_io_channel_shutdown(confirm->io, TRUE, NULL); g_io_channel_unref(confirm->io); server->confirm = NULL; input_device_close_channels(&server->src, &confirm->dst); g_free(confirm); } static void confirm_event_cb(GIOChannel *chan, gpointer user_data) { struct input_server *server = user_data; bdaddr_t src, dst; GError *err = NULL; char addr[18]; guint ret; DBG(""); bt_io_get(chan, &err, BT_IO_OPT_SOURCE_BDADDR, &src, BT_IO_OPT_DEST_BDADDR, &dst, BT_IO_OPT_INVALID); if (err) { error("%s", err->message); g_error_free(err); g_io_channel_shutdown(chan, TRUE, NULL); return; } ba2str(&dst, addr); if (server->confirm) { error("Refusing connection from %s: setup in progress", addr); goto drop; } if (!input_device_exists(&src, &dst) && !dev_is_sixaxis(&src, &dst)) { error("Refusing connection from %s: unknown device", addr); goto drop; } server->confirm = g_new0(struct confirm_data, 1); server->confirm->io = g_io_channel_ref(chan); bacpy(&server->confirm->dst, &dst); ret = btd_request_authorization(&src, &dst, HID_UUID, auth_callback, server); if (ret != 0) return; error("input: authorization for device %s failed", addr); g_io_channel_unref(server->confirm->io); g_free(server->confirm); server->confirm = NULL; drop: input_device_close_channels(&src, &dst); g_io_channel_shutdown(chan, TRUE, NULL); } int server_start(const bdaddr_t *src) { struct input_server *server; GError *err = NULL; BtIOSecLevel sec_level = input_get_classic_bonded_only() ? BT_IO_SEC_MEDIUM : BT_IO_SEC_LOW; server = g_new0(struct input_server, 1); bacpy(&server->src, src); server->ctrl = bt_io_listen(connect_event_cb, NULL, server, NULL, &err, BT_IO_OPT_SOURCE_BDADDR, src, BT_IO_OPT_PSM, L2CAP_PSM_HIDP_CTRL, BT_IO_OPT_SEC_LEVEL, sec_level, BT_IO_OPT_INVALID); if (!server->ctrl) { error("Failed to listen on control channel"); g_error_free(err); g_free(server); return -1; } server->intr = bt_io_listen(NULL, confirm_event_cb, server, NULL, &err, BT_IO_OPT_SOURCE_BDADDR, src, BT_IO_OPT_PSM, L2CAP_PSM_HIDP_INTR, BT_IO_OPT_SEC_LEVEL, sec_level, BT_IO_OPT_INVALID); if (!server->intr) { error("Failed to listen on interrupt channel"); g_io_channel_unref(server->ctrl); g_error_free(err); g_free(server); return -1; } servers = g_slist_append(servers, server); return 0; } void server_stop(const bdaddr_t *src) { struct input_server *server; GSList *l; l = g_slist_find_custom(servers, src, server_cmp); if (!l) return; server = l->data; g_io_channel_shutdown(server->intr, TRUE, NULL); g_io_channel_unref(server->intr); g_io_channel_shutdown(server->ctrl, TRUE, NULL); g_io_channel_unref(server->ctrl); servers = g_slist_remove(servers, server); g_free(server); } bluez-5.82/profiles/input/PaxHeaders/sixaxis.h0000644000000000000000000000005014015011623016462 xustar0020 atime=1743516556 20 ctime=1743591284 bluez-5.82/profiles/input/sixaxis.h0000644000000000000000000000341714015011623016150 0ustar00rootroot/* SPDX-License-Identifier: GPL-2.0-or-later */ /* * * BlueZ - Bluetooth protocol stack for Linux * * Copyright (C) 2009,2017 Bastien Nocera * Copyright (C) 2011 Antonio Ospite * Copyright (C) 2013 Szymon Janc * * */ #ifndef _SIXAXIS_H_ #define _SIXAXIS_H_ typedef enum { CABLE_PAIRING_UNSUPPORTED = 0, CABLE_PAIRING_SIXAXIS, CABLE_PAIRING_DS4, } CablePairingType; struct cable_pairing { const char *name; uint16_t source; uint16_t vid; uint16_t pid; uint16_t version; CablePairingType type; }; static inline const struct cable_pairing * get_pairing(uint16_t vid, uint16_t pid, const char *name) { static const struct cable_pairing devices[] = { { .name = "Sony PLAYSTATION(R)3 Controller", .source = 0x0002, .vid = 0x054c, .pid = 0x0268, .version = 0x0000, .type = CABLE_PAIRING_SIXAXIS, }, { .name = "SHANWAN PS3 GamePad", .source = 0x0002, .vid = 0x054c, .pid = 0x0268, .version = 0x0000, .type = CABLE_PAIRING_SIXAXIS, }, { .name = "Navigation Controller", .source = 0x0002, .vid = 0x054c, .pid = 0x042f, .version = 0x0000, .type = CABLE_PAIRING_SIXAXIS, }, { .name = "Wireless Controller", .source = 0x0002, .vid = 0x054c, .pid = 0x05c4, .version = 0x0001, .type = CABLE_PAIRING_DS4, }, { .name = "Wireless Controller", .source = 0x0002, .vid = 0x054c, .pid = 0x09cc, .version = 0x0001, .type = CABLE_PAIRING_DS4, }, }; guint i; for (i = 0; i < G_N_ELEMENTS(devices); i++) { if (devices[i].vid != vid) continue; if (devices[i].pid != pid) continue; if (name && strcmp(name, devices[i].name)) continue; return &devices[i]; } return NULL; } #endif /* _SIXAXIS_H_ */ bluez-5.82/profiles/input/PaxHeaders/hog-lib.h0000644000000000000000000000005014643061455016332 xustar0020 atime=1743516561 20 ctime=1743591278 bluez-5.82/profiles/input/hog-lib.h0000644000000000000000000000150114643061455016010 0ustar00rootroot/* SPDX-License-Identifier: LGPL-2.1-or-later */ /* * * BlueZ - Bluetooth protocol stack for Linux * * Copyright (C) 2014 Intel Corporation. All rights reserved. * * */ struct bt_hog; struct bt_hog *bt_hog_new_default(const char *name, uint16_t vendor, uint16_t product, uint16_t version, uint8_t type, struct gatt_db *db); struct bt_hog *bt_hog_new(int fd, const char *name, uint16_t vendor, uint16_t product, uint16_t version, uint8_t type, struct gatt_db *db); struct bt_hog *bt_hog_ref(struct bt_hog *hog); void bt_hog_unref(struct bt_hog *hog); bool bt_hog_attach(struct bt_hog *hog, void *gatt); void bt_hog_detach(struct bt_hog *hog, bool force); int bt_hog_set_control_point(struct bt_hog *hog, bool suspend); int bt_hog_send_report(struct bt_hog *hog, void *data, size_t size, int type); bluez-5.82/profiles/input/PaxHeaders/suspend.h0000644000000000000000000000005014015011623016453 xustar0020 atime=1743516561 20 ctime=1743591284 bluez-5.82/profiles/input/suspend.h0000644000000000000000000000060514015011623016135 0ustar00rootroot/* SPDX-License-Identifier: GPL-2.0-or-later */ /* * * BlueZ - Bluetooth protocol stack for Linux * * Copyright (C) 2012 Nordic Semiconductor Inc. * Copyright (C) 2012 Instituto Nokia de Tecnologia - INdT * * */ typedef void (*suspend_event) (void); typedef void (*resume_event) (void); int suspend_init(suspend_event suspend, resume_event resume); void suspend_exit(void); bluez-5.82/profiles/input/PaxHeaders/device.c0000644000000000000000000000005014766002272016241 xustar0020 atime=1743515578 20 ctime=1743591284 bluez-5.82/profiles/input/device.c0000644000000000000000000011324514766002272015730 0ustar00rootroot// SPDX-License-Identifier: GPL-2.0-or-later /* * * BlueZ - Bluetooth protocol stack for Linux * * Copyright (C) 2004-2010 Marcel Holtmann * Copyright (C) 2014 Google Inc. * * */ #ifdef HAVE_CONFIG_H #include #endif #define _GNU_SOURCE #include #include #include #include #include #include #include #include "lib/bluetooth.h" #include "lib/hidp.h" #include "lib/sdp.h" #include "lib/sdp_lib.h" #include "lib/uuid.h" #include "gdbus/gdbus.h" #include "btio/btio.h" #include "src/log.h" #include "src/adapter.h" #include "src/device.h" #include "src/profile.h" #include "src/service.h" #include "src/storage.h" #include "src/dbus-common.h" #include "src/error.h" #include "src/sdp-client.h" #include "src/shared/timeout.h" #include "src/shared/uhid.h" #include "src/shared/util.h" #include "src/shared/queue.h" #include "device.h" #include "hidp_defs.h" #define INPUT_INTERFACE "org.bluez.Input1" enum reconnect_mode_t { RECONNECT_NONE = 0, RECONNECT_DEVICE, RECONNECT_HOST, RECONNECT_ANY }; struct hidp_msg { uint8_t hdr; struct iovec *iov; }; struct input_device { struct btd_service *service; struct btd_device *device; char *path; bdaddr_t src; bdaddr_t dst; const sdp_record_t *rec; GIOChannel *ctrl_io; GIOChannel *intr_io; guint ctrl_watch; guint intr_watch; guint sec_watch; struct hidp_connadd_req *req; bool disable_sdp; enum reconnect_mode_t reconnect_mode; unsigned int reconnect_timer; uint32_t reconnect_attempt; struct bt_uhid *uhid; uint8_t report_req_pending; unsigned int report_req_timer; uint32_t report_rsp_id; bool virtual_cable_unplug; uint8_t type; unsigned int idle_timer; }; static int idle_timeout = 0; static uhid_state_t uhid_state = UHID_ENABLED; static bool classic_bonded_only = true; void input_set_idle_timeout(int timeout) { idle_timeout = timeout; } void input_set_userspace_hid(char *state) { if (!strcasecmp(state, "false") || !strcasecmp(state, "no") || !strcasecmp(state, "off")) uhid_state = UHID_DISABLED; else if (!strcasecmp(state, "true") || !strcasecmp(state, "yes") || !strcasecmp(state, "on")) uhid_state = UHID_ENABLED; else if (!strcasecmp(state, "persist")) uhid_state = UHID_PERSIST; else error("Unknown value '%s'", state); } uint8_t input_get_userspace_hid(void) { return uhid_state; } void input_set_classic_bonded_only(bool state) { classic_bonded_only = state; } bool input_get_classic_bonded_only(void) { return classic_bonded_only; } static void input_device_enter_reconnect_mode(struct input_device *idev); static int connection_disconnect(struct input_device *idev, uint32_t flags); static bool input_device_bonded(struct input_device *idev) { return device_is_bonded(idev->device, btd_device_get_bdaddr_type(idev->device)); } static void input_device_free(struct input_device *idev) { bt_uhid_unref(idev->uhid); btd_service_unref(idev->service); btd_device_unref(idev->device); g_free(idev->path); if (idev->ctrl_watch > 0) g_source_remove(idev->ctrl_watch); if (idev->intr_watch > 0) g_source_remove(idev->intr_watch); if (idev->sec_watch > 0) g_source_remove(idev->sec_watch); if (idev->intr_io) g_io_channel_unref(idev->intr_io); if (idev->ctrl_io) g_io_channel_unref(idev->ctrl_io); if (idev->req) { g_free(idev->req->rd_data); g_free(idev->req); } if (idev->idle_timer) timeout_remove(idev->idle_timer); if (idev->reconnect_timer > 0) timeout_remove(idev->reconnect_timer); if (idev->report_req_timer > 0) timeout_remove(idev->report_req_timer); g_free(idev); } static void virtual_cable_unplug(struct input_device *idev) { device_remove_bonding(idev->device, btd_device_get_bdaddr_type(idev->device)); idev->virtual_cable_unplug = false; } static int uhid_disconnect(struct input_device *idev, bool force) { int err; if (!bt_uhid_created(idev->uhid)) return 0; /* Force destroy the node if virtual cable unplug flag has been set */ if (idev->virtual_cable_unplug && !force) force = true; if (!force && uhid_state != UHID_PERSIST) force = true; err = bt_uhid_destroy(idev->uhid, force); if (err < 0) { error("bt_uhid_destroy: %s", strerror(-err)); return err; } if (!bt_uhid_created(idev->uhid)) bt_uhid_unregister_all(idev->uhid); return err; } static bool input_device_idle_timeout(gpointer user_data) { struct input_device *idev = user_data; idev->idle_timer = 0; DBG("path=%s", idev->path); uhid_disconnect(idev, true); connection_disconnect(idev, 0); return false; } static void input_device_idle_reset(struct input_device *idev) { timeout_remove(idev->idle_timer); if (idle_timeout) idev->idle_timer = timeout_add_seconds(idle_timeout, input_device_idle_timeout, idev, NULL); } static bool hidp_send_message(struct input_device *idev, GIOChannel *chan, uint8_t hdr, const uint8_t *data, size_t size) { int fd; ssize_t len; struct iovec iov[2]; if (!chan) { error("BT socket not connected"); return false; } iov[0].iov_base = &hdr; iov[0].iov_len = sizeof(hdr); if (data == NULL) size = 0; iov[1].iov_base = (void *)data; iov[1].iov_len = size; fd = g_io_channel_unix_get_fd(chan); len = writev(fd, iov, 2); if (len < 0) { error("BT socket write error: %s (%d)", strerror(errno), errno); return false; } if ((size_t) len < size + 1) { error("BT socket write error: partial write (%zd of %zu bytes)", len, size + 1); return false; } input_device_idle_reset(idev); return true; } static bool hidp_send_ctrl_message(struct input_device *idev, uint8_t hdr, const uint8_t *data, size_t size) { if (hdr == (HIDP_TRANS_HID_CONTROL | HIDP_CTRL_VIRTUAL_CABLE_UNPLUG)) idev->virtual_cable_unplug = true; return hidp_send_message(idev, idev->ctrl_io, hdr, data, size); } static bool hidp_send_intr_message(struct input_device *idev, uint8_t hdr, const uint8_t *data, size_t size) { return hidp_send_message(idev, idev->intr_io, hdr, data, size); } static bool uhid_send_get_report_reply(struct input_device *idev, const uint8_t *data, size_t size, uint32_t id, uint16_t err) { int ret; if (data == NULL) size = 0; if (!bt_uhid_created(idev->uhid)) { DBG("HID report (%zu bytes) dropped", size); return false; } ret = bt_uhid_get_report_reply(idev->uhid, id, 0, err, data, size); if (ret < 0) { error("bt_uhid_get_report_reply: %s (%d)", strerror(-ret), -ret); return false; } DBG("HID report (%zu bytes)", size); return true; } static bool uhid_send_set_report_reply(struct input_device *idev, uint32_t id, uint16_t err) { int ret; if (!bt_uhid_created(idev->uhid)) return false; ret = bt_uhid_set_report_reply(idev->uhid, id, err); if (ret < 0) { error("bt_uhid_set_report_reply: %s (%d)", strerror(-ret), -ret); return false; } return true; } static bool uhid_send_input_report(struct input_device *idev, const uint8_t *data, size_t size) { int err; if (data == NULL) size = 0; if (!bt_uhid_created(idev->uhid)) { DBG("HID report (%zu bytes) dropped", size); return false; } err = bt_uhid_input(idev->uhid, 0, data, size); if (err < 0) { error("bt_uhid_input: %s (%d)", strerror(-err), -err); return false; } DBG("HID report (%zu bytes)", size); return true; } static bool hidp_recv_intr_data(GIOChannel *chan, struct input_device *idev) { int fd; ssize_t len; uint8_t hdr; uint8_t data[UHID_DATA_MAX + 1]; fd = g_io_channel_unix_get_fd(chan); len = read(fd, data, sizeof(data)); if (len < 0) { error("BT socket read error: %s (%d)", strerror(errno), errno); return false; } if (len == 0) { DBG("BT socket read returned 0 bytes"); return true; } input_device_idle_reset(idev); hdr = data[0]; if (hdr != (HIDP_TRANS_DATA | HIDP_DATA_RTYPE_INPUT)) { DBG("unsupported HIDP protocol header 0x%02x", hdr); return true; } if (len < 2) { DBG("received empty HID report"); return true; } uhid_send_input_report(idev, data + 1, len - 1); return true; } static gboolean intr_watch_cb(GIOChannel *chan, GIOCondition cond, gpointer data) { struct input_device *idev = data; char address[18]; if (cond & G_IO_IN) { if (hidp_recv_intr_data(chan, idev) && (cond == G_IO_IN)) return TRUE; } ba2str(&idev->dst, address); DBG("Device %s disconnected", address); /* Checking for ctrl_watch avoids a double g_io_channel_shutdown since * it's likely that ctrl_watch_cb has been queued for dispatching in * this mainloop iteration */ if ((cond & (G_IO_HUP | G_IO_ERR)) && idev->ctrl_watch) g_io_channel_shutdown(chan, TRUE, NULL); idev->intr_watch = 0; if (idev->intr_io) { g_io_channel_unref(idev->intr_io); idev->intr_io = NULL; } /* Close control channel if the closing of interrupt channel is not * initiated by the other party */ if (idev->ctrl_io && !(cond & (G_IO_NVAL | G_IO_ERR))) g_io_channel_shutdown(idev->ctrl_io, TRUE, NULL); btd_service_disconnecting_complete(idev->service, 0); /* Enter the auto-reconnect mode if needed */ input_device_enter_reconnect_mode(idev); if (!idev->ctrl_io && idev->virtual_cable_unplug) virtual_cable_unplug(idev); /* If connection abruptly ended, uhid might be not yet disconnected */ uhid_disconnect(idev, false); return FALSE; } static void hidp_recv_ctrl_handshake(struct input_device *idev, uint8_t param) { bool pending_req_complete = false; uint8_t pending_req_type; DBG(""); pending_req_type = idev->report_req_pending & HIDP_HEADER_TRANS_MASK; switch (param) { case HIDP_HSHK_SUCCESSFUL: if (pending_req_type == HIDP_TRANS_SET_REPORT) { DBG("SET_REPORT successful"); pending_req_complete = true; } else DBG("Spurious HIDP_HSHK_SUCCESSFUL"); break; case HIDP_HSHK_NOT_READY: case HIDP_HSHK_ERR_INVALID_REPORT_ID: case HIDP_HSHK_ERR_UNSUPPORTED_REQUEST: case HIDP_HSHK_ERR_INVALID_PARAMETER: case HIDP_HSHK_ERR_UNKNOWN: case HIDP_HSHK_ERR_FATAL: if (pending_req_type == HIDP_TRANS_GET_REPORT) { DBG("GET_REPORT failed (%u)", param); uhid_send_get_report_reply(idev, NULL, 0, idev->report_rsp_id, EIO); pending_req_complete = true; } else if (pending_req_type == HIDP_TRANS_SET_REPORT) { DBG("SET_REPORT failed (%u)", param); uhid_send_set_report_reply(idev, idev->report_rsp_id, EIO); pending_req_complete = true; } else DBG("Spurious HIDP_HSHK_ERR"); if (param == HIDP_HSHK_ERR_FATAL) hidp_send_ctrl_message(idev, HIDP_TRANS_HID_CONTROL | HIDP_CTRL_SOFT_RESET, NULL, 0); break; default: hidp_send_ctrl_message(idev, HIDP_TRANS_HANDSHAKE | HIDP_HSHK_ERR_INVALID_PARAMETER, NULL, 0); break; } if (pending_req_complete) { idev->report_req_pending = 0; if (idev->report_req_timer > 0) { timeout_remove(idev->report_req_timer); idev->report_req_timer = 0; } uhid_send_set_report_reply(idev, idev->report_rsp_id, 0); idev->report_rsp_id = 0; } } static void hidp_recv_ctrl_hid_control(struct input_device *idev, uint8_t param) { DBG(""); if (param == HIDP_CTRL_VIRTUAL_CABLE_UNPLUG) connection_disconnect(idev, (1 << HIDP_VIRTUAL_CABLE_UNPLUG)); } static void hidp_recv_ctrl_data(struct input_device *idev, uint8_t param, const uint8_t *data, size_t size) { uint8_t pending_req_type; uint8_t pending_req_param; DBG(""); pending_req_type = idev->report_req_pending & HIDP_HEADER_TRANS_MASK; if (pending_req_type != HIDP_TRANS_GET_REPORT && pending_req_type != HIDP_TRANS_SET_REPORT) { DBG("Spurious DATA on control channel"); return; } pending_req_param = idev->report_req_pending & HIDP_HEADER_PARAM_MASK; if (pending_req_param != param) { DBG("Received DATA RTYPE doesn't match pending request RTYPE"); return; } switch (param) { case HIDP_DATA_RTYPE_FEATURE: case HIDP_DATA_RTYPE_INPUT: case HIDP_DATA_RTYPE_OUTPUT: if (pending_req_type == HIDP_TRANS_GET_REPORT) uhid_send_get_report_reply(idev, data + 1, size - 1, idev->report_rsp_id, 0); else uhid_send_set_report_reply(idev, idev->report_rsp_id, 0); break; case HIDP_DATA_RTYPE_OTHER: DBG("Received DATA_RTYPE_OTHER"); break; default: hidp_send_ctrl_message(idev, HIDP_TRANS_HANDSHAKE | HIDP_HSHK_ERR_INVALID_PARAMETER, NULL, 0); break; } idev->report_req_pending = 0; if (idev->report_req_timer > 0) { timeout_remove(idev->report_req_timer); idev->report_req_timer = 0; } idev->report_rsp_id = 0; } static bool hidp_recv_ctrl_message(GIOChannel *chan, struct input_device *idev) { int fd; ssize_t len; uint8_t hdr, type, param; uint8_t data[UHID_DATA_MAX + 1]; fd = g_io_channel_unix_get_fd(chan); len = read(fd, data, sizeof(data)); if (len < 0) { error("BT socket read error: %s (%d)", strerror(errno), errno); return false; } if (len == 0) { DBG("BT socket read returned 0 bytes"); return true; } input_device_idle_reset(idev); hdr = data[0]; type = hdr & HIDP_HEADER_TRANS_MASK; param = hdr & HIDP_HEADER_PARAM_MASK; switch (type) { case HIDP_TRANS_HANDSHAKE: hidp_recv_ctrl_handshake(idev, param); break; case HIDP_TRANS_HID_CONTROL: hidp_recv_ctrl_hid_control(idev, param); break; case HIDP_TRANS_DATA: hidp_recv_ctrl_data(idev, param, data, len); break; default: error("unsupported HIDP control message"); hidp_send_ctrl_message(idev, HIDP_TRANS_HANDSHAKE | HIDP_HSHK_ERR_UNSUPPORTED_REQUEST, NULL, 0); break; } return true; } static gboolean ctrl_watch_cb(GIOChannel *chan, GIOCondition cond, gpointer data) { struct input_device *idev = data; char address[18]; if (cond & G_IO_IN) { if (hidp_recv_ctrl_message(chan, idev) && (cond == G_IO_IN)) return TRUE; } ba2str(&idev->dst, address); DBG("Device %s disconnected", address); /* Checking for intr_watch avoids a double g_io_channel_shutdown since * it's likely that intr_watch_cb has been queued for dispatching in * this mainloop iteration */ if ((cond & (G_IO_HUP | G_IO_ERR)) && idev->intr_watch) g_io_channel_shutdown(chan, TRUE, NULL); idev->ctrl_watch = 0; if (idev->ctrl_io) { g_io_channel_unref(idev->ctrl_io); idev->ctrl_io = NULL; } /* Close interrupt channel */ if (idev->intr_io && !(cond & G_IO_NVAL)) g_io_channel_shutdown(idev->intr_io, TRUE, NULL); /* It's possible this is triggered while the intr channel is not even * connected yet, therefore we are still in the connecting state. */ if (btd_service_get_state(idev->service) == BTD_SERVICE_STATE_CONNECTING) btd_service_connecting_complete(idev->service, -EIO); if (!idev->intr_io && idev->virtual_cable_unplug) virtual_cable_unplug(idev); return FALSE; } #define REPORT_REQ_TIMEOUT 3 static bool hidp_report_req_timeout(gpointer data) { struct input_device *idev = data; uint8_t pending_req_type; const char *req_type_str; char address[18]; ba2str(&idev->dst, address); pending_req_type = idev->report_req_pending & HIDP_HEADER_TRANS_MASK; switch (pending_req_type) { case HIDP_TRANS_GET_REPORT: req_type_str = "GET_REPORT"; uhid_send_get_report_reply(idev, NULL, 0, idev->report_rsp_id, ETIMEDOUT); break; case HIDP_TRANS_SET_REPORT: req_type_str = "SET_REPORT"; uhid_send_set_report_reply(idev, idev->report_rsp_id, ETIMEDOUT); break; default: /* Should never happen */ req_type_str = "OTHER_TRANS"; break; } error("Device %s HIDP %s request timed out", address, req_type_str); idev->report_req_pending = 0; idev->report_req_timer = 0; idev->report_rsp_id = 0; return FALSE; } static void hidp_send_output(struct uhid_event *ev, void *user_data) { struct input_device *idev = user_data; uint8_t hdr = HIDP_TRANS_DATA | HIDP_DATA_RTYPE_OUTPUT; bool sent; DBG(""); sent = hidp_send_intr_message(idev, hdr, ev->u.output.data, ev->u.output.size); if (!sent) uhid_disconnect(idev, true); } static void hidp_send_set_report(struct uhid_event *ev, void *user_data) { struct input_device *idev = user_data; uint8_t hdr; bool sent; DBG(""); switch (ev->u.set_report.rtype) { case UHID_FEATURE_REPORT: hdr = HIDP_TRANS_SET_REPORT | HIDP_DATA_RTYPE_FEATURE; break; case UHID_INPUT_REPORT: hdr = HIDP_TRANS_SET_REPORT | HIDP_DATA_RTYPE_INPUT; break; case UHID_OUTPUT_REPORT: hdr = HIDP_TRANS_SET_REPORT | HIDP_DATA_RTYPE_OUTPUT; break; default: DBG("Unsupported HID report type %u", ev->u.set_report.rtype); return; } if (idev->report_req_pending) { DBG("Old GET_REPORT or SET_REPORT still pending"); uhid_send_set_report_reply(idev, ev->u.set_report.id, EBUSY); return; } sent = hidp_send_ctrl_message(idev, hdr, ev->u.set_report.data, ev->u.set_report.size); if (sent) { idev->report_req_pending = hdr; idev->report_req_timer = timeout_add_seconds(REPORT_REQ_TIMEOUT, hidp_report_req_timeout, idev, NULL); idev->report_rsp_id = ev->u.set_report.id; } else { uhid_send_set_report_reply(idev, ev->u.set_report.id, EIO); /* Force UHID_DESTROY on error */ uhid_disconnect(idev, true); } } static void hidp_send_get_report(struct uhid_event *ev, void *user_data) { struct input_device *idev = user_data; uint8_t hdr; bool sent; DBG(""); if (idev->report_req_pending) { DBG("Old GET_REPORT or SET_REPORT still pending"); uhid_send_get_report_reply(idev, NULL, 0, ev->u.get_report.id, EBUSY); return; } /* Send GET_REPORT on control channel */ switch (ev->u.get_report.rtype) { case UHID_FEATURE_REPORT: hdr = HIDP_TRANS_GET_REPORT | HIDP_DATA_RTYPE_FEATURE; break; case UHID_INPUT_REPORT: hdr = HIDP_TRANS_GET_REPORT | HIDP_DATA_RTYPE_INPUT; break; case UHID_OUTPUT_REPORT: hdr = HIDP_TRANS_GET_REPORT | HIDP_DATA_RTYPE_OUTPUT; break; default: DBG("Unsupported HID report type %u", ev->u.get_report.rtype); return; } sent = hidp_send_ctrl_message(idev, hdr, &ev->u.get_report.rnum, sizeof(ev->u.get_report.rnum)); if (sent) { idev->report_req_pending = hdr; idev->report_req_timer = timeout_add_seconds(REPORT_REQ_TIMEOUT, hidp_report_req_timeout, idev, NULL); idev->report_rsp_id = ev->u.get_report.id; } else { uhid_send_get_report_reply(idev, NULL, 0, ev->u.get_report.id, EIO); /* Force UHID_DESTROY on error */ uhid_disconnect(idev, true); } } static void epox_endian_quirk(unsigned char *data, int size) { /* USAGE_PAGE (Keyboard) 05 07 * USAGE_MINIMUM (0) 19 00 * USAGE_MAXIMUM (65280) 2A 00 FF <= must be FF 00 * LOGICAL_MINIMUM (0) 15 00 * LOGICAL_MAXIMUM (65280) 26 00 FF <= must be FF 00 */ unsigned char pattern[] = { 0x05, 0x07, 0x19, 0x00, 0x2a, 0x00, 0xff, 0x15, 0x00, 0x26, 0x00, 0xff }; unsigned int i; if (!data) return; for (i = 0; i < size - sizeof(pattern); i++) { if (!memcmp(data + i, pattern, sizeof(pattern))) { data[i + 5] = 0xff; data[i + 6] = 0x00; data[i + 10] = 0xff; data[i + 11] = 0x00; } } } static int create_hid_dev_name(const sdp_record_t *rec, struct hidp_connadd_req *req) { char sdesc[sizeof(req->name) / 2]; if (sdp_get_service_desc(rec, sdesc, sizeof(sdesc)) == 0) { char pname[sizeof(req->name) / 2]; if (sdp_get_provider_name(rec, pname, sizeof(pname)) == 0 && strncmp(sdesc, pname, 5) != 0) snprintf(req->name, sizeof(req->name), "%s %s", pname, sdesc); else snprintf(req->name, sizeof(req->name), "%s", sdesc); } else { return sdp_get_service_name(rec, req->name, sizeof(req->name)); } return 0; } /* See HID profile specification v1.0, "7.11.6 HIDDescriptorList" for details * on the attribute format. */ static int extract_hid_desc_data(const sdp_record_t *rec, struct hidp_connadd_req *req) { sdp_data_t *d; d = sdp_data_get(rec, SDP_ATTR_HID_DESCRIPTOR_LIST); if (!d) goto invalid_desc; if (!SDP_IS_SEQ(d->dtd)) goto invalid_desc; /* First HIDDescriptor */ d = d->val.dataseq; if (!SDP_IS_SEQ(d->dtd)) goto invalid_desc; /* ClassDescriptorType */ d = d->val.dataseq; if (d->dtd != SDP_UINT8) goto invalid_desc; /* ClassDescriptorData */ d = d->next; if (!d || !SDP_IS_TEXT_STR(d->dtd)) goto invalid_desc; req->rd_data = g_try_malloc0(d->unitSize); if (req->rd_data) { memcpy(req->rd_data, d->val.str, d->unitSize); req->rd_size = d->unitSize; epox_endian_quirk(req->rd_data, req->rd_size); } return 0; invalid_desc: error("Missing or invalid HIDDescriptorList SDP attribute"); return -EINVAL; } static int extract_hid_record(struct input_device *idev, struct hidp_connadd_req *req) { sdp_data_t *pdlist; uint8_t attr_val; int err; if (!idev->rec) return -ENOENT; err = create_hid_dev_name(idev->rec, req); if (err < 0) DBG("No valid Service Name or Service Description found"); pdlist = sdp_data_get(idev->rec, SDP_ATTR_HID_PARSER_VERSION); req->parser = pdlist ? pdlist->val.uint16 : 0x0100; pdlist = sdp_data_get(idev->rec, SDP_ATTR_HID_DEVICE_SUBCLASS); req->subclass = pdlist ? pdlist->val.uint8 : 0; pdlist = sdp_data_get(idev->rec, SDP_ATTR_HID_COUNTRY_CODE); req->country = pdlist ? pdlist->val.uint8 : 0; pdlist = sdp_data_get(idev->rec, SDP_ATTR_HID_VIRTUAL_CABLE); attr_val = pdlist ? pdlist->val.uint8 : 0; if (attr_val) req->flags |= (1 << HIDP_VIRTUAL_CABLE_UNPLUG); pdlist = sdp_data_get(idev->rec, SDP_ATTR_HID_BOOT_DEVICE); attr_val = pdlist ? pdlist->val.uint8 : 0; if (attr_val) req->flags |= (1 << HIDP_BOOT_PROTOCOL_MODE); err = extract_hid_desc_data(idev->rec, req); if (err < 0) return err; return 0; } static int ioctl_connadd(struct hidp_connadd_req *req) { int ctl, err = 0; ctl = socket(AF_BLUETOOTH, SOCK_RAW, BTPROTO_HIDP); if (ctl < 0) return -errno; if (ioctl(ctl, HIDPCONNADD, req) < 0) err = -errno; close(ctl); return err; } static bool ioctl_is_connected(struct input_device *idev) { struct hidp_conninfo ci; int ctl; /* Standard HID */ ctl = socket(AF_BLUETOOTH, SOCK_RAW, BTPROTO_HIDP); if (ctl < 0) { error("Can't open HIDP control socket"); return false; } memset(&ci, 0, sizeof(ci)); bacpy(&ci.bdaddr, &idev->dst); if (ioctl(ctl, HIDPGETCONNINFO, &ci) < 0) { error("Can't get HIDP connection info"); close(ctl); return false; } close(ctl); if (ci.state != BT_CONNECTED) return false; return true; } static int ioctl_disconnect(struct input_device *idev, uint32_t flags) { struct hidp_conndel_req req; struct hidp_conninfo ci; int ctl, err = 0; ctl = socket(AF_BLUETOOTH, SOCK_RAW, BTPROTO_HIDP); if (ctl < 0) { error("Can't open HIDP control socket"); return -errno; } memset(&ci, 0, sizeof(ci)); bacpy(&ci.bdaddr, &idev->dst); if ((ioctl(ctl, HIDPGETCONNINFO, &ci) < 0) || (ci.state != BT_CONNECTED)) { close(ctl); return -ENOTCONN; } memset(&req, 0, sizeof(req)); bacpy(&req.bdaddr, &idev->dst); req.flags = flags; if (ioctl(ctl, HIDPCONNDEL, &req) < 0) { err = -errno; error("Can't delete the HID device: %s (%d)", strerror(-err), -err); } close(ctl); return err; } static int uhid_connadd(struct input_device *idev, struct hidp_connadd_req *req) { int err; if (bt_uhid_created(idev->uhid)) return bt_uhid_replay(idev->uhid); err = bt_uhid_create(idev->uhid, req->name, &idev->src, &idev->dst, req->vendor, req->product, req->version, req->country, idev->type, req->rd_data, req->rd_size); if (err < 0) { error("bt_uhid_create: %s", strerror(-err)); return err; } bt_uhid_register(idev->uhid, UHID_OUTPUT, hidp_send_output, idev); bt_uhid_register(idev->uhid, UHID_GET_REPORT, hidp_send_get_report, idev); bt_uhid_register(idev->uhid, UHID_SET_REPORT, hidp_send_set_report, idev); return err; } static gboolean encrypt_notify(GIOChannel *io, GIOCondition condition, gpointer data) { struct input_device *idev = data; int err; DBG(""); if (idev->uhid) err = uhid_connadd(idev, idev->req); else err = ioctl_connadd(idev->req); if (err < 0) { error("ioctl_connadd(): %s (%d)", strerror(-err), -err); if (idev->ctrl_io) { g_io_channel_shutdown(idev->ctrl_io, FALSE, NULL); g_io_channel_unref(idev->ctrl_io); idev->ctrl_io = NULL; } if (idev->intr_io) { g_io_channel_shutdown(idev->intr_io, FALSE, NULL); g_io_channel_unref(idev->intr_io); idev->intr_io = NULL; } } idev->sec_watch = 0; g_free(idev->req->rd_data); g_free(idev->req); idev->req = NULL; return FALSE; } static int hidp_add_connection(struct input_device *idev) { struct hidp_connadd_req *req; GError *gerr = NULL; int err; req = g_new0(struct hidp_connadd_req, 1); req->ctrl_sock = g_io_channel_unix_get_fd(idev->ctrl_io); req->intr_sock = g_io_channel_unix_get_fd(idev->intr_io); req->flags = 0; req->idle_to = idle_timeout; err = extract_hid_record(idev, req); if (err < 0) { error("Could not parse HID SDP record: %s (%d)", strerror(-err), -err); goto cleanup; } req->vendor = btd_device_get_vendor(idev->device); req->product = btd_device_get_product(idev->device); req->version = btd_device_get_version(idev->device); if (device_name_known(idev->device)) device_get_name(idev->device, req->name, sizeof(req->name)); /* Make sure the device is bonded if required */ if (classic_bonded_only && !input_device_bonded(idev)) { error("Rejected connection from !bonded device %s", idev->path); goto cleanup; } /* Encryption is mandatory for keyboards */ /* Some platforms may choose to require encryption for all devices */ /* Note that this only matters for pre 2.1 devices as otherwise the */ /* device is encrypted by default by the lower layers */ if (classic_bonded_only || idev->type == BT_UHID_KEYBOARD) { if (!bt_io_set(idev->intr_io, &gerr, BT_IO_OPT_SEC_LEVEL, BT_IO_SEC_MEDIUM, BT_IO_OPT_INVALID)) { error("btio: %s", gerr->message); g_error_free(gerr); err = -EFAULT; goto cleanup; } idev->req = req; idev->sec_watch = g_io_add_watch(idev->intr_io, G_IO_OUT, encrypt_notify, idev); return 0; } if (idev->uhid) err = uhid_connadd(idev, req); else err = ioctl_connadd(req); cleanup: g_free(req->rd_data); g_free(req); return err; } static bool is_connected(struct input_device *idev) { if (idev->uhid) return (idev->intr_io != NULL && idev->ctrl_io != NULL); else return ioctl_is_connected(idev); } static int connection_disconnect(struct input_device *idev, uint32_t flags) { int sock; if (!is_connected(idev)) return -ENOTCONN; /* Standard HID disconnect * Intr channel must be disconnected before ctrl channel, so only * disconnect intr here, ctrl is disconnected in intr_watch_cb. */ if (idev->intr_io) { sock = g_io_channel_unix_get_fd(idev->intr_io); shutdown(sock, SHUT_WR); } if (flags & (1 << HIDP_VIRTUAL_CABLE_UNPLUG)) idev->virtual_cable_unplug = true; if (idev->uhid) return uhid_disconnect(idev, false); else return ioctl_disconnect(idev, flags); } static bool is_device_sdp_disable(const sdp_record_t *rec) { sdp_data_t *data; data = sdp_data_get(rec, SDP_ATTR_HID_SDP_DISABLE); return data && data->val.uint8; } static enum reconnect_mode_t hid_reconnection_mode(bool reconnect_initiate, bool normally_connectable) { if (!reconnect_initiate && !normally_connectable) return RECONNECT_NONE; else if (!reconnect_initiate && normally_connectable) return RECONNECT_HOST; else if (reconnect_initiate && !normally_connectable) return RECONNECT_DEVICE; else /* (reconnect_initiate && normally_connectable) */ return RECONNECT_ANY; } static void extract_hid_props(struct input_device *idev, const sdp_record_t *rec) { /* Extract HID connectability */ bool reconnect_initiate, normally_connectable; sdp_data_t *pdlist; /* HIDNormallyConnectable is optional and assumed FALSE if not * present. */ pdlist = sdp_data_get(rec, SDP_ATTR_HID_RECONNECT_INITIATE); reconnect_initiate = pdlist ? pdlist->val.uint8 : TRUE; pdlist = sdp_data_get(rec, SDP_ATTR_HID_NORMALLY_CONNECTABLE); normally_connectable = pdlist ? pdlist->val.uint8 : FALSE; /* Update local values */ idev->reconnect_mode = hid_reconnection_mode(reconnect_initiate, normally_connectable); } static void input_device_update_rec(struct input_device *idev) { struct btd_profile *p = btd_service_get_profile(idev->service); const sdp_record_t *rec; rec = btd_device_get_record(idev->device, p->remote_uuid); if (!rec || idev->rec == rec) return; idev->rec = rec; idev->disable_sdp = is_device_sdp_disable(rec); /* Initialize device properties */ extract_hid_props(idev, rec); if (idev->disable_sdp) device_set_refresh_discovery(idev->device, false); } static int input_device_connected(struct input_device *idev) { int err; if (idev->intr_io == NULL || idev->ctrl_io == NULL) return -ENOTCONN; /* Attempt to update SDP record if it had changed */ input_device_update_rec(idev); err = hidp_add_connection(idev); if (err < 0) return err; btd_service_connecting_complete(idev->service, 0); return 0; } static void interrupt_connect_cb(GIOChannel *chan, GError *conn_err, gpointer user_data) { struct input_device *idev = user_data; GIOCondition cond = G_IO_HUP | G_IO_ERR | G_IO_NVAL; int err; if (conn_err) { err = -EIO; goto failed; } err = input_device_connected(idev); if (err < 0) goto failed; if (idev->uhid) cond |= G_IO_IN; idev->intr_watch = g_io_add_watch(idev->intr_io, cond, intr_watch_cb, idev); return; failed: btd_service_connecting_complete(idev->service, err); /* So we guarantee the interrupt channel is closed before the * control channel (if we only do unref GLib will close it only * after returning control to the mainloop */ if (!conn_err) g_io_channel_shutdown(idev->intr_io, FALSE, NULL); g_io_channel_unref(idev->intr_io); idev->intr_io = NULL; if (idev->ctrl_io) { g_io_channel_unref(idev->ctrl_io); idev->ctrl_io = NULL; } } static void control_connect_cb(GIOChannel *chan, GError *conn_err, gpointer user_data) { struct input_device *idev = user_data; GIOCondition cond = G_IO_HUP | G_IO_ERR | G_IO_NVAL; GIOChannel *io; GError *err = NULL; if (conn_err) { error("%s", conn_err->message); goto failed; } /* Connect to the HID interrupt channel */ io = bt_io_connect(interrupt_connect_cb, idev, NULL, &err, BT_IO_OPT_SOURCE_BDADDR, &idev->src, BT_IO_OPT_DEST_BDADDR, &idev->dst, BT_IO_OPT_PSM, L2CAP_PSM_HIDP_INTR, BT_IO_OPT_SEC_LEVEL, BT_IO_SEC_LOW, BT_IO_OPT_INVALID); if (!io) { error("%s", err->message); g_error_free(err); goto failed; } idev->intr_io = io; if (idev->uhid) cond |= G_IO_IN; idev->ctrl_watch = g_io_add_watch(idev->ctrl_io, cond, ctrl_watch_cb, idev); return; failed: btd_service_connecting_complete(idev->service, -EIO); g_io_channel_unref(idev->ctrl_io); idev->ctrl_io = NULL; } static int dev_connect(struct input_device *idev) { GError *err = NULL; GIOChannel *io; BtIOSecLevel sec_level; if (idev->disable_sdp) bt_clear_cached_session(&idev->src, &idev->dst); /* encrypt connection if device is bonded */ if (input_device_bonded(idev)) sec_level = BT_IO_SEC_MEDIUM; else sec_level = BT_IO_SEC_LOW; io = bt_io_connect(control_connect_cb, idev, NULL, &err, BT_IO_OPT_SOURCE_BDADDR, &idev->src, BT_IO_OPT_DEST_BDADDR, &idev->dst, BT_IO_OPT_PSM, L2CAP_PSM_HIDP_CTRL, BT_IO_OPT_SEC_LEVEL, sec_level, BT_IO_OPT_INVALID); idev->ctrl_io = io; if (err == NULL) return 0; error("%s", err->message); g_error_free(err); return -EIO; } static bool input_device_auto_reconnect(gpointer user_data) { struct input_device *idev = user_data; DBG("path=%s, attempt=%d", idev->path, idev->reconnect_attempt); /* Stop the recurrent reconnection attempts if the device is * reconnected or is marked for removal. */ if (device_is_temporary(idev->device) || btd_device_is_connected(idev->device)) goto bail; /* Only attempt an auto-reconnect for at most 3 minutes (6 * 30s). */ if (idev->reconnect_attempt >= 6) goto bail; /* Check if the profile is already connected. */ if (idev->ctrl_io) goto bail; if (is_connected(idev)) goto bail; idev->reconnect_attempt++; dev_connect(idev); return TRUE; bail: idev->reconnect_timer = 0; return FALSE; } static const char * const _reconnect_mode_str[] = { "none", "device", "host", "any" }; static const char *reconnect_mode_to_string(const enum reconnect_mode_t mode) { return _reconnect_mode_str[mode]; } static void input_device_enter_reconnect_mode(struct input_device *idev) { DBG("path=%s reconnect_mode=%s", idev->path, reconnect_mode_to_string(idev->reconnect_mode)); /* Make sure the device is bonded if required */ if (classic_bonded_only && !input_device_bonded(idev)) return; /* Only attempt an auto-reconnect when the device is required to * accept reconnections from the host. */ if (idev->reconnect_mode != RECONNECT_ANY && idev->reconnect_mode != RECONNECT_HOST) return; /* If the device is temporary we are not required to reconnect * with the device. This is likely the case of a removing device. */ if (device_is_temporary(idev->device) || btd_device_is_connected(idev->device)) return; if (idev->reconnect_timer > 0) timeout_remove(idev->reconnect_timer); DBG("registering auto-reconnect"); idev->reconnect_attempt = 0; idev->reconnect_timer = timeout_add_seconds(30, input_device_auto_reconnect, idev, NULL); } int input_device_connect(struct btd_service *service) { struct input_device *idev; DBG(""); idev = btd_service_get_user_data(service); if (idev->ctrl_io) return -EBUSY; if (is_connected(idev)) return -EALREADY; return dev_connect(idev); } int input_device_disconnect(struct btd_service *service) { struct input_device *idev; int err, flags; DBG(""); idev = btd_service_get_user_data(service); flags = device_is_temporary(idev->device) ? (1 << HIDP_VIRTUAL_CABLE_UNPLUG) : 0; err = connection_disconnect(idev, flags); if (err < 0) return err; return 0; } static struct input_device *input_device_new(struct btd_service *service) { struct btd_device *device = btd_service_get_device(service); const char *path = device_get_path(device); struct btd_adapter *adapter = device_get_adapter(device); struct input_device *idev; idev = g_new0(struct input_device, 1); bacpy(&idev->src, btd_adapter_get_address(adapter)); bacpy(&idev->dst, device_get_address(device)); idev->service = btd_service_ref(service); idev->device = btd_device_ref(device); idev->path = g_strdup(path); idev->type = bt_uhid_icon_to_type(btd_device_get_icon(device)); input_device_update_rec(idev); return idev; } static gboolean property_get_reconnect_mode( const GDBusPropertyTable *property, DBusMessageIter *iter, void *data) { struct input_device *idev = data; const char *str_mode = reconnect_mode_to_string(idev->reconnect_mode); dbus_message_iter_append_basic(iter, DBUS_TYPE_STRING, &str_mode); return TRUE; } static const GDBusPropertyTable input_properties[] = { { "ReconnectMode", "s", property_get_reconnect_mode }, { } }; int input_device_register(struct btd_service *service) { struct btd_device *device = btd_service_get_device(service); const char *path = device_get_path(device); struct input_device *idev; DBG("%s", path); idev = input_device_new(service); if (!idev) return -EINVAL; if (uhid_state) { idev->uhid = bt_uhid_new_default(); if (!idev->uhid) { DBG("bt_uhid_new_default failed, switching to kernel " "mode"); uhid_state = UHID_DISABLED; } } if (g_dbus_register_interface(btd_get_dbus_connection(), idev->path, INPUT_INTERFACE, NULL, NULL, input_properties, idev, NULL) == FALSE) { error("Unable to register %s interface", INPUT_INTERFACE); input_device_free(idev); return -EINVAL; } btd_service_set_user_data(service, idev); device_set_wake_support(device, true); return 0; } static struct input_device *find_device(const bdaddr_t *src, const bdaddr_t *dst) { struct btd_device *device; struct btd_service *service; device = btd_adapter_find_device(adapter_find(src), dst, BDADDR_BREDR); if (device == NULL) return NULL; service = btd_device_get_service(device, HID_UUID); if (service == NULL) return NULL; return btd_service_get_user_data(service); } void input_device_unregister(struct btd_service *service) { struct btd_device *device = btd_service_get_device(service); const char *path = device_get_path(device); struct input_device *idev = btd_service_get_user_data(service); DBG("%s", path); g_dbus_unregister_interface(btd_get_dbus_connection(), idev->path, INPUT_INTERFACE); input_device_free(idev); } static int input_device_connadd(struct input_device *idev) { int err; err = input_device_connected(idev); if (err == 0) return 0; if (idev->ctrl_io) { g_io_channel_shutdown(idev->ctrl_io, FALSE, NULL); g_io_channel_unref(idev->ctrl_io); idev->ctrl_io = NULL; } if (idev->intr_io) { g_io_channel_shutdown(idev->intr_io, FALSE, NULL); g_io_channel_unref(idev->intr_io); idev->intr_io = NULL; } return err; } bool input_device_exists(const bdaddr_t *src, const bdaddr_t *dst) { if (find_device(src, dst)) return true; return false; } int input_device_set_channel(const bdaddr_t *src, const bdaddr_t *dst, int psm, GIOChannel *io) { struct input_device *idev = find_device(src, dst); GIOCondition cond = G_IO_HUP | G_IO_ERR | G_IO_NVAL; DBG("idev %p psm %d", idev, psm); if (!idev) return -ENOENT; if (uhid_state) cond |= G_IO_IN; switch (psm) { case L2CAP_PSM_HIDP_CTRL: if (idev->ctrl_io) return -EALREADY; idev->ctrl_io = g_io_channel_ref(io); idev->ctrl_watch = g_io_add_watch(idev->ctrl_io, cond, ctrl_watch_cb, idev); break; case L2CAP_PSM_HIDP_INTR: if (idev->intr_io) return -EALREADY; idev->intr_io = g_io_channel_ref(io); idev->intr_watch = g_io_add_watch(idev->intr_io, cond, intr_watch_cb, idev); break; } if (idev->intr_io && idev->ctrl_io) input_device_connadd(idev); return 0; } int input_device_close_channels(const bdaddr_t *src, const bdaddr_t *dst) { struct input_device *idev = find_device(src, dst); if (!idev) return -ENOENT; if (idev->intr_io) g_io_channel_shutdown(idev->intr_io, TRUE, NULL); if (idev->ctrl_io) g_io_channel_shutdown(idev->ctrl_io, TRUE, NULL); return 0; } bluez-5.82/profiles/input/PaxHeaders/hog.c0000644000000000000000000000005014711225434015554 xustar0020 atime=1743516561 20 ctime=1743591284 bluez-5.82/profiles/input/hog.c0000644000000000000000000001255614711225434015246 0ustar00rootroot// SPDX-License-Identifier: GPL-2.0-or-later /* * * BlueZ - Bluetooth protocol stack for Linux * * Copyright (C) 2012 Marcel Holtmann * Copyright (C) 2012 Nordic Semiconductor Inc. * Copyright (C) 2012 Instituto Nokia de Tecnologia - INdT * * */ #ifdef HAVE_CONFIG_H #include #endif #include #include #include #include #include #include #include #include #include "lib/bluetooth.h" #include "lib/sdp.h" #include "lib/uuid.h" #include "src/log.h" #include "src/adapter.h" #include "src/device.h" #include "src/profile.h" #include "src/service.h" #include "src/shared/util.h" #include "src/shared/uhid.h" #include "src/shared/queue.h" #include "src/shared/att.h" #include "src/shared/gatt-client.h" #include "src/plugin.h" #include "device.h" #include "suspend.h" #include "attrib/att.h" #include "attrib/gattrib.h" #include "attrib/gatt.h" #include "hog-lib.h" struct hog_device { struct btd_device *device; struct bt_hog *hog; uint8_t type; }; static gboolean suspend_supported = FALSE; static bool auto_sec = true; static struct queue *devices = NULL; void input_set_auto_sec(bool state) { auto_sec = state; } static void hog_device_accept(struct hog_device *dev, struct gatt_db *db) { char name[248]; uint16_t vendor, product, version, type; if (dev->hog) return; if (device_name_known(dev->device)) device_get_name(dev->device, name, sizeof(name)); else strcpy(name, "bluez-hog-device"); vendor = btd_device_get_vendor(dev->device); product = btd_device_get_product(dev->device); version = btd_device_get_version(dev->device); type = bt_uhid_icon_to_type(btd_device_get_icon(dev->device)); DBG("name=%s vendor=0x%X, product=0x%X, version=0x%X", name, vendor, product, version); dev->hog = bt_hog_new_default(name, vendor, product, version, type, db); } static struct hog_device *hog_device_new(struct btd_device *device) { struct hog_device *dev; dev = new0(struct hog_device, 1); dev->device = btd_device_ref(device); if (!devices) devices = queue_new(); queue_push_tail(devices, dev); return dev; } static void hog_device_free(void *data) { struct hog_device *dev = data; queue_remove(devices, dev); if (queue_isempty(devices)) { queue_destroy(devices, NULL); devices = NULL; } btd_device_unref(dev->device); bt_hog_unref(dev->hog); free(dev); } static void set_suspend(gpointer data, gpointer user_data) { struct hog_device *dev = data; gboolean suspend = GPOINTER_TO_INT(user_data); bt_hog_set_control_point(dev->hog, suspend); } static void suspend_callback(void) { gboolean suspend = TRUE; DBG("Suspending ..."); queue_foreach(devices, set_suspend, GINT_TO_POINTER(suspend)); } static void resume_callback(void) { gboolean suspend = FALSE; DBG("Resuming ..."); queue_foreach(devices, set_suspend, GINT_TO_POINTER(suspend)); } static int hog_probe(struct btd_service *service) { struct btd_device *device = btd_service_get_device(service); const char *path = device_get_path(device); struct hog_device *dev; DBG("path %s", path); dev = hog_device_new(device); if (!dev) return -EINVAL; btd_service_set_user_data(service, dev); device_set_wake_support(device, true); return 0; } static void hog_remove(struct btd_service *service) { struct hog_device *dev = btd_service_get_user_data(service); struct btd_device *device = btd_service_get_device(service); const char *path = device_get_path(device); DBG("path %s", path); hog_device_free(dev); } static int hog_accept(struct btd_service *service) { struct hog_device *dev = btd_service_get_user_data(service); struct btd_device *device = btd_service_get_device(service); struct gatt_db *db = btd_device_get_gatt_db(device); GAttrib *attrib = btd_device_get_attrib(device); if (!dev->hog) { hog_device_accept(dev, db); if (!dev->hog) return -EINVAL; } /* HOGP 1.0 Section 6.1 requires bonding */ if (!device_is_bonded(device, btd_device_get_bdaddr_type(device))) { struct bt_gatt_client *client; if (!auto_sec) return -ECONNREFUSED; client = btd_device_get_gatt_client(device); if (!bt_gatt_client_set_security(client, BT_ATT_SECURITY_MEDIUM)) return -ECONNREFUSED; } /* TODO: Replace GAttrib with bt_gatt_client */ bt_hog_attach(dev->hog, attrib); btd_service_connecting_complete(service, 0); return 0; } static int hog_disconnect(struct btd_service *service) { struct hog_device *dev = btd_service_get_user_data(service); if (input_get_userspace_hid() == UHID_PERSIST) bt_hog_detach(dev->hog, false); else bt_hog_detach(dev->hog, true); btd_service_disconnecting_complete(service, 0); return 0; } static struct btd_profile hog_profile = { .name = "input-hog", .remote_uuid = HOG_UUID, .device_probe = hog_probe, .device_remove = hog_remove, .accept = hog_accept, .disconnect = hog_disconnect, .auto_connect = true, }; static int hog_init(void) { int err; err = suspend_init(suspend_callback, resume_callback); if (err < 0) error("Loading suspend plugin failed: %s (%d)", strerror(-err), -err); else suspend_supported = TRUE; return btd_profile_register(&hog_profile); } static void hog_exit(void) { if (suspend_supported) suspend_exit(); btd_profile_unregister(&hog_profile); } BLUETOOTH_PLUGIN_DEFINE(hog, VERSION, BLUETOOTH_PLUGIN_PRIORITY_DEFAULT, hog_init, hog_exit) bluez-5.82/profiles/input/PaxHeaders/input.conf0000644000000000000000000000005014711225434016641 xustar0020 atime=1743516875 20 ctime=1743591288 bluez-5.82/profiles/input/input.conf0000644000000000000000000000200414711225434016316 0ustar00rootroot# Configuration file for the input service # This section contains options which are not specific to any # particular interface [General] # Set idle timeout (in seconds) before the connection will be disconnect and # the input device is removed. # Defaults: 0 (disabled) #IdleTimeout=0 # Enable HID protocol handling in userspace input profile # Possible values: # - persist: Use UHID in persistent mode (keyboard only) # - true: Use UHID instead # - false: User kernel HIDP # Defaults to true #UserspaceHID=true # Limit HID connections to bonded devices # The HID Profile does not specify that devices must be bonded, however some # platforms may want to make sure that input connections only come from bonded # device connections. Several older mice have been known for not supporting # pairing/encryption. # Defaults to true for security. #ClassicBondedOnly=true # LE upgrade security # Enables upgrades of security automatically if required. # Defaults to true to maximize device compatibility. #LEAutoSecurity=true bluez-5.82/profiles/input/PaxHeaders/device.h0000644000000000000000000000005014711225434016243 xustar0020 atime=1743516556 20 ctime=1743591284 bluez-5.82/profiles/input/device.h0000644000000000000000000000211414711225434015722 0ustar00rootroot/* SPDX-License-Identifier: GPL-2.0-or-later */ /* * * BlueZ - Bluetooth protocol stack for Linux * * Copyright (C) 2004-2010 Marcel Holtmann * * */ #define L2CAP_PSM_HIDP_CTRL 0x11 #define L2CAP_PSM_HIDP_INTR 0x13 typedef enum { UHID_DISABLED = 0, UHID_ENABLED, UHID_PERSIST } uhid_state_t; struct input_device; struct input_conn; void input_set_idle_timeout(int timeout); void input_set_userspace_hid(char *state); uint8_t input_get_userspace_hid(void); void input_set_classic_bonded_only(bool state); bool input_get_classic_bonded_only(void); void input_set_auto_sec(bool state); int input_device_register(struct btd_service *service); void input_device_unregister(struct btd_service *service); bool input_device_exists(const bdaddr_t *src, const bdaddr_t *dst); int input_device_set_channel(const bdaddr_t *src, const bdaddr_t *dst, int psm, GIOChannel *io); int input_device_close_channels(const bdaddr_t *src, const bdaddr_t *dst); int input_device_connect(struct btd_service *service); int input_device_disconnect(struct btd_service *service); bluez-5.82/profiles/input/PaxHeaders/hidp_defs.h0000644000000000000000000000005014015011623016717 xustar0020 atime=1743516558 20 ctime=1743591284 bluez-5.82/profiles/input/hidp_defs.h0000644000000000000000000000356214015011623016406 0ustar00rootroot/* SPDX-License-Identifier: GPL-2.0-or-later */ /* * * BlueZ - Bluetooth protocol stack for Linux * * Copyright (C) 2003-2014 Marcel Holtmann * Copyright (C) 2014 Google Inc. * * */ #ifndef __HIDP_DEFS_H #define __HIDP_DEFS_H /* HIDP header masks */ #define HIDP_HEADER_TRANS_MASK 0xf0 #define HIDP_HEADER_PARAM_MASK 0x0f /* HIDP transaction types */ #define HIDP_TRANS_HANDSHAKE 0x00 #define HIDP_TRANS_HID_CONTROL 0x10 #define HIDP_TRANS_GET_REPORT 0x40 #define HIDP_TRANS_SET_REPORT 0x50 #define HIDP_TRANS_GET_PROTOCOL 0x60 #define HIDP_TRANS_SET_PROTOCOL 0x70 #define HIDP_TRANS_GET_IDLE 0x80 #define HIDP_TRANS_SET_IDLE 0x90 #define HIDP_TRANS_DATA 0xa0 #define HIDP_TRANS_DATC 0xb0 /* HIDP handshake results */ #define HIDP_HSHK_SUCCESSFUL 0x00 #define HIDP_HSHK_NOT_READY 0x01 #define HIDP_HSHK_ERR_INVALID_REPORT_ID 0x02 #define HIDP_HSHK_ERR_UNSUPPORTED_REQUEST 0x03 #define HIDP_HSHK_ERR_INVALID_PARAMETER 0x04 #define HIDP_HSHK_ERR_UNKNOWN 0x0e #define HIDP_HSHK_ERR_FATAL 0x0f /* HIDP control operation parameters */ #define HIDP_CTRL_NOP 0x00 #define HIDP_CTRL_HARD_RESET 0x01 #define HIDP_CTRL_SOFT_RESET 0x02 #define HIDP_CTRL_SUSPEND 0x03 #define HIDP_CTRL_EXIT_SUSPEND 0x04 #define HIDP_CTRL_VIRTUAL_CABLE_UNPLUG 0x05 /* HIDP data transaction headers */ #define HIDP_DATA_RTYPE_MASK 0x03 #define HIDP_DATA_RSRVD_MASK 0x0c #define HIDP_DATA_RTYPE_OTHER 0x00 #define HIDP_DATA_RTYPE_INPUT 0x01 #define HIDP_DATA_RTYPE_OUTPUT 0x02 #define HIDP_DATA_RTYPE_FEATURE 0x03 /* HIDP protocol header parameters */ #define HIDP_PROTO_BOOT 0x00 #define HIDP_PROTO_REPORT 0x01 #define HIDP_VIRTUAL_CABLE_UNPLUG 0 #define HIDP_BOOT_PROTOCOL_MODE 1 #define HIDP_BLUETOOTH_VENDOR_ID 9 #define HIDP_WAITING_FOR_RETURN 10 #define HIDP_WAITING_FOR_SEND_ACK 11 #endif /* __HIDP_DEFS_H */ bluez-5.82/profiles/input/PaxHeaders/manager.c0000644000000000000000000000005014711225434016411 xustar0020 atime=1743516555 20 ctime=1743591284 bluez-5.82/profiles/input/manager.c0000644000000000000000000000611114711225434016071 0ustar00rootroot// SPDX-License-Identifier: GPL-2.0-or-later /* * * BlueZ - Bluetooth protocol stack for Linux * * Copyright (C) 2004-2010 Marcel Holtmann * * */ #ifdef HAVE_CONFIG_H #include #endif #include #include #include #include "lib/bluetooth.h" #include "lib/sdp.h" #include "lib/sdp_lib.h" #include "lib/uuid.h" #include "src/log.h" #include "src/plugin.h" #include "src/adapter.h" #include "src/device.h" #include "src/profile.h" #include "src/service.h" #include "device.h" #include "server.h" static int hid_server_probe(struct btd_profile *p, struct btd_adapter *adapter) { return server_start(btd_adapter_get_address(adapter)); } static void hid_server_remove(struct btd_profile *p, struct btd_adapter *adapter) { server_stop(btd_adapter_get_address(adapter)); } static struct btd_profile input_profile = { .name = "input-hid", .local_uuid = HID_UUID, .remote_uuid = HID_UUID, .auto_connect = true, .connect = input_device_connect, .disconnect = input_device_disconnect, .device_probe = input_device_register, .device_remove = input_device_unregister, .adapter_probe = hid_server_probe, .adapter_remove = hid_server_remove, }; static GKeyFile *load_config_file(const char *file) { GKeyFile *keyfile; GError *err = NULL; keyfile = g_key_file_new(); if (!g_key_file_load_from_file(keyfile, file, 0, &err)) { if (!g_error_matches(err, G_FILE_ERROR, G_FILE_ERROR_NOENT)) error("Parsing %s failed: %s", file, err->message); g_error_free(err); g_key_file_free(keyfile); return NULL; } return keyfile; } static int input_init(void) { GKeyFile *config; GError *err = NULL; config = load_config_file(CONFIGDIR "/input.conf"); if (config) { int idle_timeout; gboolean classic_bonded_only, auto_sec; char *uhid_enabled; idle_timeout = g_key_file_get_integer(config, "General", "IdleTimeout", &err); if (!err) { DBG("input.conf: IdleTimeout=%d", idle_timeout); input_set_idle_timeout(idle_timeout); } else g_clear_error(&err); uhid_enabled = g_key_file_get_string(config, "General", "UserspaceHID", &err); if (!err) { DBG("input.conf: UserspaceHID=%s", uhid_enabled); input_set_userspace_hid(uhid_enabled); free(uhid_enabled); } else g_clear_error(&err); classic_bonded_only = g_key_file_get_boolean(config, "General", "ClassicBondedOnly", &err); if (!err) { DBG("input.conf: ClassicBondedOnly=%s", classic_bonded_only ? "true" : "false"); input_set_classic_bonded_only(classic_bonded_only); } else g_clear_error(&err); auto_sec = g_key_file_get_boolean(config, "General", "LEAutoSecurity", &err); if (!err) { DBG("input.conf: LEAutoSecurity=%s", auto_sec ? "true" : "false"); input_set_auto_sec(auto_sec); } else g_clear_error(&err); } btd_profile_register(&input_profile); if (config) g_key_file_free(config); return 0; } static void input_exit(void) { btd_profile_unregister(&input_profile); } BLUETOOTH_PLUGIN_DEFINE(input, VERSION, BLUETOOTH_PLUGIN_PRIORITY_DEFAULT, input_init, input_exit) bluez-5.82/profiles/input/PaxHeaders/hog-lib.c0000644000000000000000000000005014667536076016341 xustar0020 atime=1743516562 20 ctime=1743591278 bluez-5.82/profiles/input/hog-lib.c0000644000000000000000000012137314667536076016031 0ustar00rootroot// SPDX-License-Identifier: GPL-2.0-or-later /* * * BlueZ - Bluetooth protocol stack for Linux * * Copyright (C) 2014 Intel Corporation. * Copyright (C) 2012 Marcel Holtmann * Copyright (C) 2012 Nordic Semiconductor Inc. * Copyright (C) 2012 Instituto Nokia de Tecnologia - INdT * * */ #ifdef HAVE_CONFIG_H #include #endif #include #include #include #include #include #include #include #include #include #include #include "lib/bluetooth.h" #include "lib/sdp.h" #include "lib/uuid.h" #include "src/shared/util.h" #include "src/shared/uhid.h" #include "src/shared/queue.h" #include "src/shared/att.h" #include "src/shared/gatt-db.h" #include "src/log.h" #include "attrib/att.h" #include "attrib/gattrib.h" #include "attrib/gatt.h" #include "btio/btio.h" #include "profiles/scanparam/scpp.h" #include "profiles/deviceinfo/dis.h" #include "profiles/battery/bas.h" #include "profiles/input/hog-lib.h" #define HOG_UUID16 0x1812 #define HOG_INFO_UUID 0x2A4A #define HOG_REPORT_MAP_UUID 0x2A4B #define HOG_REPORT_UUID 0x2A4D #define HOG_PROTO_MODE_UUID 0x2A4E #define HOG_CONTROL_POINT_UUID 0x2A4C #define HOG_REPORT_TYPE_INPUT 1 #define HOG_REPORT_TYPE_OUTPUT 2 #define HOG_REPORT_TYPE_FEATURE 3 #define HOG_PROTO_MODE_BOOT 0 #define HOG_PROTO_MODE_REPORT 1 #define HID_INFO_SIZE 4 #define ATT_NOTIFICATION_HEADER_SIZE 3 struct bt_hog { int ref_count; char *name; uint16_t vendor; uint16_t product; uint16_t version; uint8_t type; struct gatt_db_attribute *attr; struct gatt_primary *primary; GAttrib *attrib; GSList *reports; struct bt_uhid *uhid; int uhid_fd; uint64_t uhid_flags; uint16_t bcdhid; uint8_t bcountrycode; uint16_t proto_mode_handle; uint16_t ctrlpt_handle; uint8_t flags; unsigned int getrep_att; uint16_t getrep_id; unsigned int setrep_att; uint16_t setrep_id; unsigned int report_map_id; struct bt_scpp *scpp; struct bt_dis *dis; struct queue *bas; GSList *instances; struct queue *gatt_op; struct gatt_db *gatt_db; struct gatt_db_attribute *report_map_attr; }; struct report { struct bt_hog *hog; bool numbered; uint8_t id; uint8_t type; uint16_t handle; uint16_t value_handle; uint8_t properties; uint16_t ccc_handle; guint notifyid; uint16_t len; uint8_t *value; }; struct gatt_request { unsigned int id; struct bt_hog *hog; void *user_data; }; static struct gatt_request *create_request(struct bt_hog *hog, void *user_data) { struct gatt_request *req; req = new0(struct gatt_request, 1); if (!req) return NULL; req->user_data = user_data; req->hog = bt_hog_ref(hog); return req; } static bool set_and_store_gatt_req(struct bt_hog *hog, struct gatt_request *req, unsigned int id) { req->id = id; return queue_push_head(hog->gatt_op, req); } static void destroy_gatt_req(void *data) { struct gatt_request *req = data; bt_hog_unref(req->hog); free(req); } static void read_report_map(struct bt_hog *hog); static void remove_gatt_req(struct gatt_request *req, uint8_t status) { struct bt_hog *hog = req->hog; queue_remove(hog->gatt_op, req); if (!status && queue_isempty(hog->gatt_op)) { /* Report Map must be read last since that can result * in uhid being created and the driver may start to * use UHID_SET_REPORT which requires the report->id to * be known what attribute to send to. */ read_report_map(hog); } destroy_gatt_req(req); } static void write_char(struct bt_hog *hog, GAttrib *attrib, uint16_t handle, const uint8_t *value, size_t vlen, GAttribResultFunc func, gpointer user_data) { struct gatt_request *req; unsigned int id; req = create_request(hog, user_data); if (!req) return; id = gatt_write_char(attrib, handle, value, vlen, func, req); if (!id) { error("hog: Could not write char"); return; } if (!set_and_store_gatt_req(hog, req, id)) { error("hog: Failed to queue write char req"); g_attrib_cancel(attrib, id); free(req); } } static unsigned int read_char(struct bt_hog *hog, GAttrib *attrib, uint16_t handle, GAttribResultFunc func, gpointer user_data) { struct gatt_request *req; unsigned int id; req = create_request(hog, user_data); if (!req) return 0; id = gatt_read_char(attrib, handle, func, req); if (!id) { error("hog: Could not read char"); return 0; } if (!set_and_store_gatt_req(hog, req, id)) { error("hog: Failed to queue read char req"); g_attrib_cancel(attrib, id); free(req); return 0; } return id; } static void discover_desc(struct bt_hog *hog, GAttrib *attrib, uint16_t start, uint16_t end, gatt_cb_t func, gpointer user_data) { struct gatt_request *req; unsigned int id; req = create_request(hog, user_data); if (!req) return; id = gatt_discover_desc(attrib, start, end, NULL, func, req); if (!id) { error("hog: Could not discover descriptors"); return; } if (!set_and_store_gatt_req(hog, req, id)) { error("hog: Failed to queue discover descriptors req"); g_attrib_cancel(attrib, id); free(req); } } static void discover_char(struct bt_hog *hog, GAttrib *attrib, uint16_t start, uint16_t end, bt_uuid_t *uuid, gatt_cb_t func, gpointer user_data) { struct gatt_request *req; unsigned int id; req = create_request(hog, user_data); if (!req) return; id = gatt_discover_char(attrib, start, end, uuid, func, req); if (!id) { error("hog: Could not discover characteristic"); return; } if (!set_and_store_gatt_req(hog, req, id)) { error("hog: Failed to queue discover characteristic req"); g_attrib_cancel(attrib, id); free(req); } } static void discover_primary(struct bt_hog *hog, GAttrib *attrib, bt_uuid_t *uuid, gatt_cb_t func, gpointer user_data) { struct gatt_request *req; unsigned int id; req = create_request(hog, user_data); if (!req) return; id = gatt_discover_primary(attrib, uuid, func, req); if (!id) { error("hog: Could not send discover primary"); return; } if (!set_and_store_gatt_req(hog, req, id)) { error("hog: Failed to queue discover primary req"); g_attrib_cancel(attrib, id); free(req); } } static void find_included(struct bt_hog *hog, GAttrib *attrib, uint16_t start, uint16_t end, gatt_cb_t func, gpointer user_data) { struct gatt_request *req; unsigned int id; req = create_request(hog, user_data); if (!req) return; id = gatt_find_included(attrib, start, end, func, req); if (!id) { error("hog: Could not find included"); return; } if (!set_and_store_gatt_req(hog, req, id)) { error("hog: Failed to queue find included req"); g_attrib_cancel(attrib, id); free(req); } } static void report_value_cb(const guint8 *pdu, guint16 len, gpointer user_data) { struct report *report = user_data; struct bt_hog *hog = report->hog; int err; if (len < ATT_NOTIFICATION_HEADER_SIZE) { error("Malformed ATT notification"); return; } pdu += ATT_NOTIFICATION_HEADER_SIZE; len -= ATT_NOTIFICATION_HEADER_SIZE; err = bt_uhid_input(hog->uhid, report->numbered ? report->id : 0, pdu, len); if (err < 0) error("bt_uhid_input: %s (%d)", strerror(-err), -err); } static void report_notify_destroy(void *user_data) { struct report *report = user_data; DBG(""); report->notifyid = 0; } static void report_ccc_written_cb(guint8 status, const guint8 *pdu, guint16 plen, gpointer user_data) { struct gatt_request *req = user_data; struct report *report = req->user_data; struct bt_hog *hog = report->hog; if (status != 0) { error("Write report characteristic descriptor failed: %s", att_ecode2str(status)); goto remove; } if (report->notifyid) goto remove; report->notifyid = g_attrib_register(hog->attrib, ATT_OP_HANDLE_NOTIFY, report->value_handle, report_value_cb, report, report_notify_destroy); if (!report->notifyid) { error("Unable to register report notification: handle 0x%04x", report->value_handle); goto remove; } DBG("Report characteristic descriptor written: notifications enabled"); remove: remove_gatt_req(req, status); } static void write_ccc(struct bt_hog *hog, GAttrib *attrib, uint16_t handle, void *user_data) { uint8_t value[2]; put_le16(GATT_CLIENT_CHARAC_CFG_NOTIF_BIT, value); write_char(hog, attrib, handle, value, sizeof(value), report_ccc_written_cb, user_data); } static void ccc_read_cb(guint8 status, const guint8 *pdu, guint16 len, gpointer user_data) { struct gatt_request *req = user_data; struct report *report = req->user_data; if (status != 0) { error("Error reading CCC value: %s", att_ecode2str(status)); goto remove; } write_ccc(report->hog, report->hog->attrib, report->ccc_handle, report); remove: remove_gatt_req(req, status); } static const char *type_to_string(uint8_t type) { switch (type) { case HOG_REPORT_TYPE_INPUT: return "input"; case HOG_REPORT_TYPE_OUTPUT: return "output"; case HOG_REPORT_TYPE_FEATURE: return "feature"; } return NULL; } static void report_reference_cb(guint8 status, const guint8 *pdu, guint16 plen, gpointer user_data) { struct gatt_request *req = user_data; struct report *report = req->user_data; if (status != 0) { error("Read Report Reference descriptor failed: %s", att_ecode2str(status)); goto remove; } if (plen != 3) { error("Malformed ATT read response"); goto remove; } report->id = pdu[1]; report->type = pdu[2]; DBG("Report 0x%04x: id 0x%02x type %s", report->value_handle, report->id, type_to_string(report->type)); /* Enable notifications only for Input Reports */ if (report->type == HOG_REPORT_TYPE_INPUT) read_char(report->hog, report->hog->attrib, report->ccc_handle, ccc_read_cb, report); remove: remove_gatt_req(req, status); } static void external_report_reference_cb(guint8 status, const guint8 *pdu, guint16 plen, gpointer user_data); static void discover_external_cb(uint8_t status, GSList *descs, void *user_data) { struct gatt_request *req = user_data; struct bt_hog *hog = req->user_data; if (status != 0) { error("Discover external descriptors failed: %s", att_ecode2str(status)); goto remove; } for ( ; descs; descs = descs->next) { struct gatt_desc *desc = descs->data; read_char(hog, hog->attrib, desc->handle, external_report_reference_cb, hog); } remove: remove_gatt_req(req, status); } static void discover_external(struct bt_hog *hog, GAttrib *attrib, uint16_t start, uint16_t end, gpointer user_data) { bt_uuid_t uuid; if (start > end) return; bt_uuid16_create(&uuid, GATT_EXTERNAL_REPORT_REFERENCE); discover_desc(hog, attrib, start, end, discover_external_cb, user_data); } static void discover_report_cb(uint8_t status, GSList *descs, void *user_data) { struct gatt_request *req = user_data; struct report *report = req->user_data; struct bt_hog *hog = report->hog; if (status != 0) { error("Discover report descriptors failed: %s", att_ecode2str(status)); goto remove; } for ( ; descs; descs = descs->next) { struct gatt_desc *desc = descs->data; switch (desc->uuid16) { case GATT_CLIENT_CHARAC_CFG_UUID: report->ccc_handle = desc->handle; break; case GATT_REPORT_REFERENCE: read_char(hog, hog->attrib, desc->handle, report_reference_cb, report); break; } } remove: remove_gatt_req(req, status); } static void discover_report(struct bt_hog *hog, GAttrib *attrib, uint16_t start, uint16_t end, gpointer user_data) { if (start > end) return; discover_desc(hog, attrib, start, end, discover_report_cb, user_data); } static void report_read_cb(guint8 status, const guint8 *pdu, guint16 len, gpointer user_data) { struct gatt_request *req = user_data; struct report *report = req->user_data; if (status != 0) { error("Error reading Report value: %s", att_ecode2str(status)); goto remove; } if (report->value) free(report->value); report->value = util_memdup(pdu, len); report->len = len; remove: remove_gatt_req(req, status); } static int report_chrc_cmp(const void *data, const void *user_data) { const struct report *report = data; const struct gatt_char *decl = user_data; return report->handle - decl->handle; } static struct report *report_new(struct bt_hog *hog, struct gatt_char *chr) { struct report *report; GSList *l; if (!chr) return NULL; /* Skip if report already exists */ l = g_slist_find_custom(hog->reports, chr, report_chrc_cmp); if (l) return l->data; report = g_new0(struct report, 1); report->hog = hog; report->handle = chr->handle; report->value_handle = chr->value_handle; report->properties = chr->properties; hog->reports = g_slist_append(hog->reports, report); read_char(hog, hog->attrib, chr->value_handle, report_read_cb, report); return report; } static void external_service_char_cb(uint8_t status, GSList *chars, void *user_data) { struct gatt_request *req = user_data; struct bt_hog *hog = req->user_data; struct gatt_primary *primary = hog->primary; struct report *report; GSList *l; if (status != 0) { const char *str = att_ecode2str(status); DBG("Discover external service characteristic failed: %s", str); goto remove; } for (l = chars; l; l = g_slist_next(l)) { struct gatt_char *chr, *next; uint16_t start, end; chr = l->data; next = l->next ? l->next->data : NULL; if (!chr) continue; DBG("0x%04x UUID: %s properties: %02x", chr->handle, chr->uuid, chr->properties); report = report_new(hog, chr); start = chr->value_handle + 1; end = (next ? next->handle - 1 : primary->range.end); discover_report(hog, hog->attrib, start, end, report); } remove: remove_gatt_req(req, status); } static void external_report_reference_cb(guint8 status, const guint8 *pdu, guint16 plen, gpointer user_data) { struct gatt_request *req = user_data; struct bt_hog *hog = req->user_data; uint16_t uuid16; bt_uuid_t uuid; if (status != 0) { error("Read External Report Reference descriptor failed: %s", att_ecode2str(status)); goto remove; } if (plen != 3) { error("Malformed ATT read response"); goto remove; } uuid16 = get_le16(&pdu[1]); DBG("External report reference read, external report characteristic " "UUID: 0x%04x", uuid16); /* Do not discover if is not a Report */ if (uuid16 != HOG_REPORT_UUID) goto remove; bt_uuid16_create(&uuid, uuid16); discover_char(hog, hog->attrib, 0x0001, 0xffff, &uuid, external_service_char_cb, hog); remove: remove_gatt_req(req, status); } static int report_cmp(gconstpointer a, gconstpointer b) { const struct report *ra = a, *rb = b; /* sort by type first.. */ if (ra->type != rb->type) return ra->type - rb->type; /* skip id check in case of reports not being numbered */ if (!ra->numbered && !rb->numbered) return 0; /* ..then by id */ return ra->id - rb->id; } static struct report *find_report(struct bt_hog *hog, uint8_t type, uint8_t id) { struct report cmp; GSList *l; memset(&cmp, 0, sizeof(cmp)); cmp.type = type; cmp.id = id; switch (type) { case HOG_REPORT_TYPE_FEATURE: if (hog->flags & UHID_DEV_NUMBERED_FEATURE_REPORTS) cmp.numbered = true; break; case HOG_REPORT_TYPE_OUTPUT: if (hog->flags & UHID_DEV_NUMBERED_OUTPUT_REPORTS) cmp.numbered = true; break; case HOG_REPORT_TYPE_INPUT: if (hog->flags & UHID_DEV_NUMBERED_INPUT_REPORTS) cmp.numbered = true; break; } l = g_slist_find_custom(hog->reports, &cmp, report_cmp); return l ? l->data : NULL; } static struct report *find_report_by_rtype(struct bt_hog *hog, uint8_t rtype, uint8_t id) { uint8_t type; switch (rtype) { case UHID_FEATURE_REPORT: type = HOG_REPORT_TYPE_FEATURE; break; case UHID_OUTPUT_REPORT: type = HOG_REPORT_TYPE_OUTPUT; break; case UHID_INPUT_REPORT: type = HOG_REPORT_TYPE_INPUT; break; default: return NULL; } return find_report(hog, type, id); } static void output_written_cb(guint8 status, const guint8 *pdu, guint16 plen, gpointer user_data) { struct gatt_request *req = user_data; if (status != 0) error("Write output report failed: %s", att_ecode2str(status)); remove_gatt_req(req, status); } static void forward_report(struct uhid_event *ev, void *user_data) { struct bt_hog *hog = user_data; struct report *report; void *data; int size; report = find_report_by_rtype(hog, ev->u.output.rtype, ev->u.output.data[0]); if (!report) return; data = ev->u.output.data; size = ev->u.output.size; if (report->numbered && size > 0) { data++; --size; } DBG("Sending report type %d ID %d to handle 0x%X", report->type, report->id, report->value_handle); if (hog->attrib == NULL) return; if (report->properties & GATT_CHR_PROP_WRITE) write_char(hog, hog->attrib, report->value_handle, data, size, output_written_cb, hog); else if (report->properties & GATT_CHR_PROP_WRITE_WITHOUT_RESP) gatt_write_cmd(hog->attrib, report->value_handle, data, size, NULL, NULL); } static void set_numbered(void *data, void *user_data) { struct report *report = data; struct bt_hog *hog = user_data; switch (report->type) { case HOG_REPORT_TYPE_INPUT: if (hog->uhid_flags & UHID_DEV_NUMBERED_INPUT_REPORTS) report->numbered = true; break; case HOG_REPORT_TYPE_OUTPUT: if (hog->uhid_flags & UHID_DEV_NUMBERED_OUTPUT_REPORTS) report->numbered = true; break; case HOG_REPORT_TYPE_FEATURE: if (hog->uhid_flags & UHID_DEV_NUMBERED_FEATURE_REPORTS) report->numbered = true; break; } } static void start_flags(struct uhid_event *ev, void *user_data) { struct bt_hog *hog = user_data; hog->uhid_flags = ev->u.start.dev_flags; DBG("uHID device flags: 0x%16" PRIx64, hog->uhid_flags); if (hog->uhid_flags) g_slist_foreach(hog->reports, set_numbered, hog); } static void set_report_cb(guint8 status, const guint8 *pdu, guint16 plen, gpointer user_data) { struct bt_hog *hog = user_data; int err; hog->setrep_att = 0; if (status != 0) error("Error setting Report value: %s", att_ecode2str(status)); err = bt_uhid_set_report_reply(hog->uhid, hog->setrep_id, status); if (err < 0) error("bt_uhid_set_report_reply: %s", strerror(-err)); } static void uhid_destroy(struct bt_hog *hog, bool force) { int err; if (!hog->uhid) return; bt_uhid_unregister_all(hog->uhid); err = bt_uhid_destroy(hog->uhid, force); if (err < 0) { error("bt_uhid_destroy: %s", strerror(-err)); return; } } static void set_report(struct uhid_event *ev, void *user_data) { struct bt_hog *hog = user_data; struct report *report; void *data; int size; int err; /* Destroy input device if there is an attempt to communicate with it * while disconnected. */ if (hog->attrib == NULL) { uhid_destroy(hog, true); return; } /* uhid never sends reqs in parallel; if there's a req, it timed out */ if (hog->setrep_att) { g_attrib_cancel(hog->attrib, hog->setrep_att); hog->setrep_att = 0; } hog->setrep_id = ev->u.set_report.id; report = find_report_by_rtype(hog, ev->u.set_report.rtype, ev->u.set_report.rnum); if (!report) { err = ENOTSUP; goto fail; } data = ev->u.set_report.data; size = ev->u.set_report.size; if (report->numbered && size > 0) { data++; --size; } DBG("Sending report type %d ID %d to handle 0x%X", report->type, report->id, report->value_handle); hog->setrep_att = gatt_write_char(hog->attrib, report->value_handle, data, size, set_report_cb, hog); if (!hog->setrep_att) { err = ENOMEM; goto fail; } return; fail: /* cancel the request on failure */ set_report_cb(err, NULL, 0, hog); } static void report_reply(struct bt_hog *hog, uint8_t status, uint8_t id, uint16_t len, const uint8_t *data) { int err; hog->getrep_att = 0; err = bt_uhid_get_report_reply(hog->uhid, hog->getrep_id, id, status, data, len); if (err < 0) error("bt_uhid_get_report_reply: %s", strerror(-err)); } static void get_report_cb(guint8 status, const guint8 *pdu, guint16 len, gpointer user_data) { struct report *report = user_data; struct bt_hog *hog = report->hog; if (status != 0) { error("Error reading Report value: %s", att_ecode2str(status)); goto exit; } if (len == 0) { error("Error reading Report, length %d", len); status = EIO; goto exit; } if (pdu[0] != 0x0b) { error("Error reading Report, invalid response: %02x", pdu[0]); status = EPROTO; goto exit; } --len; ++pdu; exit: report_reply(hog, status, report->numbered ? report->id : 0, len, pdu); } static void get_report(struct uhid_event *ev, void *user_data) { struct bt_hog *hog = user_data; struct report *report; guint8 err; /* Destroy input device if there is an attempt to communicate with it * while disconnected. */ if (hog->attrib == NULL) { uhid_destroy(hog, true); return; } /* uhid never sends reqs in parallel; if there's a req, it timed out */ if (hog->getrep_att) { g_attrib_cancel(hog->attrib, hog->getrep_att); hog->getrep_att = 0; } hog->getrep_id = ev->u.get_report.id; report = find_report_by_rtype(hog, ev->u.get_report.rtype, ev->u.get_report.rnum); if (!report) { err = ENOTSUP; goto fail; } hog->getrep_att = gatt_read_char(hog->attrib, report->value_handle, get_report_cb, report); if (!hog->getrep_att) { err = ENOMEM; goto fail; } return; fail: /* reply with an error on failure */ report_reply(hog, err, 0, 0, NULL); } static void uhid_create(struct bt_hog *hog, uint8_t *report_map, size_t report_map_len) { uint8_t *value = report_map; size_t vlen = report_map_len; int err; GError *gerr = NULL; bdaddr_t src, dst; bt_io_get(g_attrib_get_channel(hog->attrib), &gerr, BT_IO_OPT_SOURCE_BDADDR, &src, BT_IO_OPT_DEST_BDADDR, &dst, BT_IO_OPT_INVALID); if (gerr) { error("Failed to connection details: %s", gerr->message); g_error_free(gerr); return; } err = bt_uhid_create(hog->uhid, hog->name, &src, &dst, hog->vendor, hog->product, hog->version, hog->bcountrycode, hog->type, value, vlen); if (err < 0) { error("bt_uhid_create: %s", strerror(-err)); return; } bt_uhid_register(hog->uhid, UHID_START, start_flags, hog); bt_uhid_register(hog->uhid, UHID_OUTPUT, forward_report, hog); bt_uhid_register(hog->uhid, UHID_GET_REPORT, get_report, hog); bt_uhid_register(hog->uhid, UHID_SET_REPORT, set_report, hog); DBG("HoG created uHID device"); } static void db_report_map_write_value_cb(struct gatt_db_attribute *attr, int err, void *user_data) { if (err) error("Error writing report map value to gatt db"); } static void report_map_read_cb(guint8 status, const guint8 *pdu, guint16 plen, gpointer user_data) { struct gatt_request *req = user_data; struct bt_hog *hog = req->user_data; uint8_t *value; ssize_t vlen; remove_gatt_req(req, status); if (status != 0) { error("Report Map read failed: %s", att_ecode2str(status)); return; } value = new0(uint8_t, plen); vlen = dec_read_resp(pdu, plen, value, plen); if (vlen < 0) { error("ATT protocol error"); goto done; } uhid_create(hog, value, vlen); /* Cache the report map if gatt_db is available */ if (hog->report_map_attr) { gatt_db_attribute_write(hog->report_map_attr, 0, value, vlen, 0, NULL, db_report_map_write_value_cb, NULL); } done: free(value); } static void read_report_map(struct bt_hog *hog) { uint16_t handle; if (!hog->report_map_attr || bt_uhid_created(hog->uhid) || hog->report_map_id) return; handle = gatt_db_attribute_get_handle(hog->report_map_attr); hog->report_map_id = read_char(hog, hog->attrib, handle, report_map_read_cb, hog); } static void info_read_cb(guint8 status, const guint8 *pdu, guint16 plen, gpointer user_data) { struct gatt_request *req = user_data; struct bt_hog *hog = req->user_data; uint8_t value[HID_INFO_SIZE]; ssize_t vlen; if (status != 0) { error("HID Information read failed: %s", att_ecode2str(status)); goto remove; } vlen = dec_read_resp(pdu, plen, value, sizeof(value)); if (vlen != 4) { error("ATT protocol error"); goto remove; } hog->bcdhid = get_le16(&value[0]); hog->bcountrycode = value[2]; hog->flags = value[3]; DBG("bcdHID: 0x%04X bCountryCode: 0x%02X Flags: 0x%02X", hog->bcdhid, hog->bcountrycode, hog->flags); remove: remove_gatt_req(req, status); } static void proto_mode_read_cb(guint8 status, const guint8 *pdu, guint16 plen, gpointer user_data) { struct gatt_request *req = user_data; struct bt_hog *hog = req->user_data; uint8_t value; ssize_t vlen; if (status != 0) { error("Protocol Mode characteristic read failed: %s", att_ecode2str(status)); goto remove; } vlen = dec_read_resp(pdu, plen, &value, sizeof(value)); if (vlen < 0) { error("ATT protocol error"); goto remove; } if (value == HOG_PROTO_MODE_BOOT) { uint8_t nval = HOG_PROTO_MODE_REPORT; DBG("HoG is operating in Boot Procotol Mode"); gatt_write_cmd(hog->attrib, hog->proto_mode_handle, &nval, sizeof(nval), NULL, NULL); } else if (value == HOG_PROTO_MODE_REPORT) DBG("HoG is operating in Report Protocol Mode"); remove: remove_gatt_req(req, status); } static void char_discovered_cb(uint8_t status, GSList *chars, void *user_data) { struct gatt_request *req = user_data; struct bt_hog *hog = req->user_data; struct gatt_primary *primary = hog->primary; bt_uuid_t report_uuid, report_map_uuid, info_uuid; bt_uuid_t proto_mode_uuid, ctrlpt_uuid; struct report *report; GSList *l; uint16_t info_handle = 0, proto_mode_handle = 0; DBG("HoG inspecting characteristics"); if (status != 0) { DBG("Discover all characteristics failed: %s", att_ecode2str(status)); goto remove; } bt_uuid16_create(&report_uuid, HOG_REPORT_UUID); bt_uuid16_create(&report_map_uuid, HOG_REPORT_MAP_UUID); bt_uuid16_create(&info_uuid, HOG_INFO_UUID); bt_uuid16_create(&proto_mode_uuid, HOG_PROTO_MODE_UUID); bt_uuid16_create(&ctrlpt_uuid, HOG_CONTROL_POINT_UUID); for (l = chars; l; l = g_slist_next(l)) { struct gatt_char *chr, *next; bt_uuid_t uuid; uint16_t start, end; chr = l->data; next = l->next ? l->next->data : NULL; if (!chr) continue; DBG("0x%04x UUID: %s properties: %02x", chr->handle, chr->uuid, chr->properties); bt_string_to_uuid(&uuid, chr->uuid); start = chr->value_handle + 1; end = (next ? next->handle - 1 : primary->range.end); if (bt_uuid_cmp(&uuid, &report_uuid) == 0) { report = report_new(hog, chr); discover_report(hog, hog->attrib, start, end, report); } else if (bt_uuid_cmp(&uuid, &report_map_uuid) == 0) { DBG("HoG discovering report map"); read_char(hog, hog->attrib, chr->value_handle, report_map_read_cb, hog); discover_external(hog, hog->attrib, start, end, hog); } else if (bt_uuid_cmp(&uuid, &info_uuid) == 0) info_handle = chr->value_handle; else if (bt_uuid_cmp(&uuid, &proto_mode_uuid) == 0) proto_mode_handle = chr->value_handle; else if (bt_uuid_cmp(&uuid, &ctrlpt_uuid) == 0) hog->ctrlpt_handle = chr->value_handle; } if (proto_mode_handle) { hog->proto_mode_handle = proto_mode_handle; read_char(hog, hog->attrib, proto_mode_handle, proto_mode_read_cb, hog); } if (info_handle) read_char(hog, hog->attrib, info_handle, info_read_cb, hog); remove: remove_gatt_req(req, status); } static void report_free(void *data) { struct report *report = data; free(report->value); g_free(report); } static bool cancel_gatt_req(const void *data, const void *user_data) { struct gatt_request *req = (void *) data; const struct bt_hog *hog = user_data; return g_attrib_cancel(hog->attrib, req->id); } static void hog_free(void *data) { struct bt_hog *hog = data; bt_hog_detach(hog, true); uhid_destroy(hog, true); queue_destroy(hog->bas, (void *) bt_bas_unref); g_slist_free_full(hog->instances, hog_free); bt_scpp_unref(hog->scpp); bt_dis_unref(hog->dis); bt_uhid_unref(hog->uhid); g_slist_free_full(hog->reports, report_free); g_free(hog->name); free(hog->primary); queue_destroy(hog->gatt_op, (void *) destroy_gatt_req); if (hog->gatt_db) gatt_db_unref(hog->gatt_db); g_free(hog); } struct bt_hog *bt_hog_new_default(const char *name, uint16_t vendor, uint16_t product, uint16_t version, uint8_t type, struct gatt_db *db) { return bt_hog_new(-1, name, vendor, product, version, type, db); } static void foreach_hog_report(struct gatt_db_attribute *attr, void *user_data) { struct report *report = user_data; struct bt_hog *hog = report->hog; const bt_uuid_t *uuid; bt_uuid_t ref_uuid, ccc_uuid; uint16_t handle; handle = gatt_db_attribute_get_handle(attr); uuid = gatt_db_attribute_get_type(attr); bt_uuid16_create(&ref_uuid, GATT_REPORT_REFERENCE); if (!bt_uuid_cmp(&ref_uuid, uuid)) { read_char(hog, hog->attrib, handle, report_reference_cb, report); return; } bt_uuid16_create(&ccc_uuid, GATT_CLIENT_CHARAC_CFG_UUID); if (!bt_uuid_cmp(&ccc_uuid, uuid)) report->ccc_handle = handle; } static int report_attr_cmp(const void *data, const void *user_data) { const struct report *report = data; const struct gatt_db_attribute *attr = user_data; return report->handle - gatt_db_attribute_get_handle(attr); } static struct report *report_add(struct bt_hog *hog, struct gatt_db_attribute *attr) { struct report *report; GSList *l; /* Skip if report already exists */ l = g_slist_find_custom(hog->reports, attr, report_attr_cmp); if (l) return l->data; report = g_new0(struct report, 1); report->hog = hog; gatt_db_attribute_get_char_data(attr, &report->handle, &report->value_handle, &report->properties, NULL, NULL); hog->reports = g_slist_append(hog->reports, report); read_char(hog, hog->attrib, report->value_handle, report_read_cb, report); return report; } static void foreach_hog_external(struct gatt_db_attribute *attr, void *user_data) { struct bt_hog *hog = user_data; const bt_uuid_t *uuid; bt_uuid_t ext_uuid; uint16_t handle; handle = gatt_db_attribute_get_handle(attr); uuid = gatt_db_attribute_get_type(attr); bt_uuid16_create(&ext_uuid, GATT_EXTERNAL_REPORT_REFERENCE); if (!bt_uuid_cmp(&ext_uuid, uuid)) read_char(hog, hog->attrib, handle, external_report_reference_cb, hog); } static void db_report_map_read_value_cb(struct gatt_db_attribute *attrib, int err, const uint8_t *value, size_t length, void *user_data) { struct iovec *map = user_data; if (err) { error("Error reading report map from gatt db %s", strerror(-err)); return; } if (!length) return; map->iov_len = length; map->iov_base = (void *) value; } static void foreach_hog_chrc(struct gatt_db_attribute *attr, void *user_data) { struct bt_hog *hog = user_data; bt_uuid_t uuid, report_uuid, report_map_uuid, info_uuid; bt_uuid_t proto_mode_uuid, ctrlpt_uuid; uint16_t handle, value_handle; struct iovec map = {}; gatt_db_attribute_get_char_data(attr, &handle, &value_handle, NULL, NULL, &uuid); bt_uuid16_create(&report_uuid, HOG_REPORT_UUID); if (!bt_uuid_cmp(&report_uuid, &uuid)) { struct report *report = report_add(hog, attr); gatt_db_service_foreach_desc(attr, foreach_hog_report, report); return; } bt_uuid16_create(&report_map_uuid, HOG_REPORT_MAP_UUID); if (!bt_uuid_cmp(&report_map_uuid, &uuid)) { if (hog->gatt_db) { /* Try to read the cache of report map if available */ hog->report_map_attr = gatt_db_get_attribute( hog->gatt_db, value_handle); gatt_db_attribute_read(hog->report_map_attr, 0, BT_ATT_OP_READ_REQ, NULL, db_report_map_read_value_cb, &map); } if (map.iov_len) { /* Report map found in the cache, straight to creating * UHID to optimize reconnection. */ uhid_create(hog, map.iov_base, map.iov_len); } gatt_db_service_foreach_desc(attr, foreach_hog_external, hog); } bt_uuid16_create(&info_uuid, HOG_INFO_UUID); if (!bt_uuid_cmp(&info_uuid, &uuid)) { read_char(hog, hog->attrib, value_handle, info_read_cb, hog); return; } bt_uuid16_create(&proto_mode_uuid, HOG_PROTO_MODE_UUID); if (!bt_uuid_cmp(&proto_mode_uuid, &uuid)) { hog->proto_mode_handle = value_handle; read_char(hog, hog->attrib, value_handle, proto_mode_read_cb, hog); } bt_uuid16_create(&ctrlpt_uuid, HOG_CONTROL_POINT_UUID); if (!bt_uuid_cmp(&ctrlpt_uuid, &uuid)) hog->ctrlpt_handle = value_handle; } static struct bt_hog *hog_new(int fd, const char *name, uint16_t vendor, uint16_t product, uint16_t version, uint8_t type, struct gatt_db_attribute *attr) { struct bt_uhid *uhid; struct bt_hog *hog; if (fd < 0) uhid = bt_uhid_new_default(); else uhid = bt_uhid_new(fd); if (!uhid) { DBG("Unable to create UHID"); return NULL; } hog = g_try_new0(struct bt_hog, 1); if (!hog) return NULL; hog->gatt_op = queue_new(); hog->bas = queue_new(); hog->uhid_fd = fd; hog->uhid = uhid; if (!hog->gatt_op || !hog->bas) { hog_free(hog); return NULL; } hog->name = g_strdup(name); hog->vendor = vendor; hog->product = product; hog->version = version; hog->type = type; hog->attr = attr; return hog; } static void hog_attach_instance(struct bt_hog *hog, struct gatt_db_attribute *attr) { struct bt_hog *instance; if (!hog->attr) { hog->attr = attr; return; } instance = hog_new(hog->uhid_fd, hog->name, hog->vendor, hog->product, hog->version, hog->type, attr); if (!instance) return; instance->gatt_db = gatt_db_ref(hog->gatt_db); hog->instances = g_slist_append(hog->instances, bt_hog_ref(instance)); } static void foreach_hog_service(struct gatt_db_attribute *attr, void *user_data) { struct bt_hog *hog = user_data; hog_attach_instance(hog, attr); } static void dis_notify(uint8_t source, uint16_t vendor, uint16_t product, uint16_t version, void *user_data) { struct bt_hog *hog = user_data; GSList *l; hog->vendor = vendor; hog->product = product; hog->version = version; for (l = hog->instances; l; l = l->next) { struct bt_hog *instance = l->data; instance->vendor = vendor; instance->product = product; instance->version = version; } } struct bt_hog *bt_hog_new(int fd, const char *name, uint16_t vendor, uint16_t product, uint16_t version, uint8_t type, struct gatt_db *db) { struct bt_hog *hog; hog = hog_new(fd, name, vendor, product, version, type, NULL); if (!hog) return NULL; hog->gatt_db = gatt_db_ref(db); if (db) { bt_uuid_t uuid; /* Handle the HID services */ bt_uuid16_create(&uuid, HOG_UUID16); gatt_db_foreach_service(db, &uuid, foreach_hog_service, hog); if (!hog->attr) { hog_free(hog); return NULL; } /* Try creating a DIS instance in case pid/vid are not set */ if (!vendor && !product) { hog->dis = bt_dis_new(db); bt_dis_set_notification(hog->dis, dis_notify, hog); } } return bt_hog_ref(hog); } struct bt_hog *bt_hog_ref(struct bt_hog *hog) { if (!hog) return NULL; __sync_fetch_and_add(&hog->ref_count, 1); return hog; } void bt_hog_unref(struct bt_hog *hog) { if (!hog) return; if (__sync_sub_and_fetch(&hog->ref_count, 1)) return; hog_free(hog); } static void find_included_cb(uint8_t status, GSList *services, void *user_data) { struct gatt_request *req = user_data; GSList *l; DBG(""); if (status) { DBG("Find included failed: %s", att_ecode2str(status)); goto remove; } for (l = services; l; l = l->next) { struct gatt_included *include = l->data; DBG("included: handle %x, uuid %s", include->handle, include->uuid); } remove: remove_gatt_req(req, status); } static void hog_attach_scpp(struct bt_hog *hog, struct gatt_primary *primary) { if (hog->scpp) { bt_scpp_attach(hog->scpp, hog->attrib); return; } hog->scpp = bt_scpp_new(primary); if (hog->scpp) bt_scpp_attach(hog->scpp, hog->attrib); } static void hog_attach_dis(struct bt_hog *hog, struct gatt_primary *primary) { if (hog->dis) { bt_dis_attach(hog->dis, hog->attrib); return; } hog->dis = bt_dis_new_primary(primary); if (hog->dis) { bt_dis_set_notification(hog->dis, dis_notify, hog); bt_dis_attach(hog->dis, hog->attrib); } } static void hog_attach_bas(struct bt_hog *hog, struct gatt_primary *primary) { struct bt_bas *instance; instance = bt_bas_new(primary); bt_bas_attach(instance, hog->attrib); queue_push_head(hog->bas, instance); } static void hog_attach_hog(struct bt_hog *hog, struct gatt_primary *primary) { struct bt_hog *instance; if (!hog->primary) { hog->primary = util_memdup(primary, sizeof(*primary)); discover_char(hog, hog->attrib, primary->range.start, primary->range.end, NULL, char_discovered_cb, hog); find_included(hog, hog->attrib, primary->range.start, primary->range.end, find_included_cb, hog); return; } instance = bt_hog_new(hog->uhid_fd, hog->name, hog->vendor, hog->product, hog->version, hog->type, hog->gatt_db); if (!instance) return; instance->primary = util_memdup(primary, sizeof(*primary)); find_included(instance, hog->attrib, primary->range.start, primary->range.end, find_included_cb, instance); bt_hog_attach(instance, hog->attrib); hog->instances = g_slist_append(hog->instances, instance); } static void primary_cb(uint8_t status, GSList *services, void *user_data) { struct gatt_request *req = user_data; struct bt_hog *hog = req->user_data; struct gatt_primary *primary; GSList *l; DBG(""); if (status) { DBG("Discover primary failed: %s", att_ecode2str(status)); goto remove; } if (!services) { DBG("No primary service found"); goto remove; } for (l = services; l; l = l->next) { primary = l->data; if (strcmp(primary->uuid, SCAN_PARAMETERS_UUID) == 0) { hog_attach_scpp(hog, primary); continue; } if (strcmp(primary->uuid, DEVICE_INFORMATION_UUID) == 0) { hog_attach_dis(hog, primary); continue; } if (strcmp(primary->uuid, BATTERY_UUID) == 0) { hog_attach_bas(hog, primary); continue; } if (strcmp(primary->uuid, HOG_UUID) == 0) hog_attach_hog(hog, primary); } remove: remove_gatt_req(req, status); } bool bt_hog_attach(struct bt_hog *hog, void *gatt) { GSList *l; if (hog->attrib) return false; hog->attrib = g_attrib_ref(gatt); if (!hog->attr && !hog->primary) { discover_primary(hog, hog->attrib, NULL, primary_cb, hog); return true; } if (hog->scpp) bt_scpp_attach(hog->scpp, gatt); if (hog->dis) bt_dis_attach(hog->dis, gatt); queue_foreach(hog->bas, (void *) bt_bas_attach, gatt); for (l = hog->instances; l; l = l->next) { struct bt_hog *instance = l->data; bt_hog_attach(instance, gatt); } if (!bt_uhid_created(hog->uhid)) { DBG("HoG discovering characteristics"); if (hog->attr) gatt_db_service_foreach_char(hog->attr, foreach_hog_chrc, hog); else discover_char(hog, hog->attrib, hog->primary->range.start, hog->primary->range.end, NULL, char_discovered_cb, hog); } if (!bt_uhid_created(hog->uhid)) return true; /* If UHID is already created, set up the report value handlers to * optimize reconnection. */ for (l = hog->reports; l; l = l->next) { struct report *r = l->data; if (r->notifyid) continue; r->notifyid = g_attrib_register(hog->attrib, ATT_OP_HANDLE_NOTIFY, r->value_handle, report_value_cb, r, report_notify_destroy); if (!r->notifyid) error("Unable to register report notification: " "handle 0x%04x", r->value_handle); } /* Attempt to replay get/set report messages since the driver might not * be aware the device has been disconnected in the meantime. */ bt_uhid_replay(hog->uhid); return true; } void bt_hog_detach(struct bt_hog *hog, bool force) { GSList *l; if (!hog) return; if (!hog->attrib) goto done; queue_foreach(hog->bas, (void *) bt_bas_detach, NULL); for (l = hog->instances; l; l = l->next) { struct bt_hog *instance = l->data; bt_hog_detach(instance, force); } for (l = hog->reports; l; l = l->next) { struct report *r = l->data; if (r->notifyid > 0) { g_attrib_unregister(hog->attrib, r->notifyid); r->notifyid = 0; } } if (hog->scpp) bt_scpp_detach(hog->scpp); if (hog->dis) bt_dis_detach(hog->dis); queue_remove_all(hog->gatt_op, cancel_gatt_req, hog, destroy_gatt_req); g_attrib_unref(hog->attrib); hog->attrib = NULL; done: uhid_destroy(hog, force); } int bt_hog_set_control_point(struct bt_hog *hog, bool suspend) { uint8_t value = suspend ? 0x00 : 0x01; if (hog->attrib == NULL) return -ENOTCONN; if (hog->ctrlpt_handle == 0) return -ENOTSUP; gatt_write_cmd(hog->attrib, hog->ctrlpt_handle, &value, sizeof(value), NULL, NULL); return 0; } int bt_hog_send_report(struct bt_hog *hog, void *data, size_t size, int type) { struct report *report; GSList *l; if (!hog) return -EINVAL; if (!hog->attrib) return -ENOTCONN; report = find_report(hog, type, 0); if (!report) return -ENOTSUP; DBG("hog: Write report, handle 0x%X", report->value_handle); if (report->properties & GATT_CHR_PROP_WRITE) write_char(hog, hog->attrib, report->value_handle, data, size, output_written_cb, hog); if (report->properties & GATT_CHR_PROP_WRITE_WITHOUT_RESP) gatt_write_cmd(hog->attrib, report->value_handle, data, size, NULL, NULL); for (l = hog->instances; l; l = l->next) { struct bt_hog *instance = l->data; bt_hog_send_report(instance, data, size, type); } return 0; } bluez-5.82/profiles/input/PaxHeaders/server.h0000644000000000000000000000005014015011623016300 xustar0020 atime=1743516556 20 ctime=1743591284 bluez-5.82/profiles/input/server.h0000644000000000000000000000040414015011623015757 0ustar00rootroot/* SPDX-License-Identifier: GPL-2.0-or-later */ /* * * BlueZ - Bluetooth protocol stack for Linux * * Copyright (C) 2004-2010 Marcel Holtmann * * */ int server_start(const bdaddr_t *src); void server_stop(const bdaddr_t *src); bluez-5.82/profiles/PaxHeaders/audio0000644000000000000000000000005014773213564014531 xustar0020 atime=1743591291 20 ctime=1743591284 bluez-5.82/profiles/audio/0000755000000000000000000000000014773213564014267 5ustar00rootrootbluez-5.82/profiles/audio/PaxHeaders/player.h0000644000000000000000000000005014711225434016242 xustar0020 atime=1743516514 20 ctime=1743591284 bluez-5.82/profiles/audio/player.h0000644000000000000000000001111714711225434015724 0ustar00rootroot/* SPDX-License-Identifier: GPL-2.0-or-later */ /* * * BlueZ - Bluetooth protocol stack for Linux * * Copyright (C) 2006-2007 Nokia Corporation * Copyright (C) 2004-2009 Marcel Holtmann * Copyright (C) 2012-2012 Intel Corporation * * */ typedef enum { PLAYER_ITEM_TYPE_AUDIO, PLAYER_ITEM_TYPE_VIDEO, PLAYER_ITEM_TYPE_FOLDER, PLAYER_ITEM_TYPE_INVALID, } player_item_type_t; typedef enum { PLAYER_FOLDER_TYPE_MIXED, PLAYER_FOLDER_TYPE_TITLES, PLAYER_FOLDER_TYPE_ALBUMS, PLAYER_FOLDER_TYPE_ARTISTS, PLAYER_FOLDER_TYPE_GENRES, PLAYER_FOLDER_TYPE_PLAYLISTS, PLAYER_FOLDER_TYPE_YEARS, PLAYER_FOLDER_TYPE_INVALID, } player_folder_type_t; struct media_player; struct media_item; struct media_player_callback { bool (*set_setting)(struct media_player *mp, const char *key, const char *value, void *user_data); int (*play)(struct media_player *mp, void *user_data); int (*pause)(struct media_player *mp, void *user_data); int (*stop)(struct media_player *mp, void *user_data); int (*next)(struct media_player *mp, void *user_data); int (*previous)(struct media_player *mp, void *user_data); int (*fast_forward)(struct media_player *mp, void *user_data); int (*rewind)(struct media_player *mp, void *user_data); int (*press)(struct media_player *mp, uint8_t avc_key, void *user_data); int (*hold)(struct media_player *mp, uint8_t avc_key, void *user_data); int (*release)(struct media_player *mp, void *user_data); int (*list_items)(struct media_player *mp, const char *name, uint32_t start, uint32_t end, void *user_data); int (*change_folder)(struct media_player *mp, const char *path, uint64_t uid, void *user_data); int (*search)(struct media_player *mp, const char *string, void *user_data); int (*play_item)(struct media_player *mp, const char *name, uint64_t uid, void *user_data); int (*add_to_nowplaying)(struct media_player *mp, const char *name, uint64_t uid, void *user_data); int (*total_items)(struct media_player *mp, const char *name, void *user_data); }; struct media_player *media_player_controller_create(const char *path, uint16_t id); const char *media_player_get_path(struct media_player *mp); void media_player_destroy(struct media_player *mp); void media_player_set_duration(struct media_player *mp, uint32_t duration); void media_player_set_position(struct media_player *mp, uint32_t position); void media_player_set_setting(struct media_player *mp, const char *key, const char *value); const char *media_player_get_status(struct media_player *mp); void media_player_set_status(struct media_player *mp, const char *status); void media_player_clear_metadata(struct media_player *mp); void media_player_set_metadata(struct media_player *mp, struct media_item *item, const char *key, void *data, size_t len); void media_player_metadata_changed(struct media_player *mp); void media_player_set_type(struct media_player *mp, const char *type); void media_player_set_subtype(struct media_player *mp, const char *subtype); void media_player_set_name(struct media_player *mp, const char *name); void media_player_set_browsable(struct media_player *mp, bool enabled); bool media_player_get_browsable(struct media_player *mp); void media_player_set_searchable(struct media_player *mp, bool enabled); void media_player_set_folder(struct media_player *mp, const char *path, uint32_t items); void media_player_set_playlist(struct media_player *mp, const char *name); struct media_item *media_player_set_playlist_item(struct media_player *mp, uint64_t uid); void media_player_clear_playlist(struct media_player *mp); void media_player_set_obex_port(struct media_player *mp, uint16_t port); struct media_item *media_player_create_folder(struct media_player *mp, const char *name, player_folder_type_t type, uint64_t uid); struct media_item *media_player_create_item(struct media_player *mp, const char *name, player_item_type_t type, uint64_t uid); void media_player_play_item_complete(struct media_player *mp, int err); void media_item_set_playable(struct media_item *item, bool value); void media_player_list_complete(struct media_player *mp, GSList *items, int err); void media_player_change_folder_complete(struct media_player *player, const char *path, uint64_t uid, int ret); void media_player_search_complete(struct media_player *mp, int ret); void media_player_total_items_complete(struct media_player *mp, uint32_t num_of_items); void media_player_set_callbacks(struct media_player *mp, const struct media_player_callback *cbs, void *user_data); bluez-5.82/profiles/audio/PaxHeaders/vcp.h0000644000000000000000000000005014766002272015541 xustar0020 atime=1743515578 20 ctime=1743591284 bluez-5.82/profiles/audio/vcp.h0000644000000000000000000000046014766002272015222 0ustar00rootroot/* SPDX-License-Identifier: GPL-2.0-or-later */ /* * * BlueZ - Bluetooth protocol stack for Linux * * Copyright (C) 2024 StreamUnlimited Engineering GmbH * * */ uint8_t bt_audio_vcp_get_volume(struct btd_device *device); bool bt_audio_vcp_set_volume(struct btd_device *device, uint8_t volume); bluez-5.82/profiles/audio/PaxHeaders/media.c0000644000000000000000000000005014766002272016023 xustar0020 atime=1743515578 20 ctime=1743591283 bluez-5.82/profiles/audio/media.c0000644000000000000000000024514514766002272015517 0ustar00rootroot// SPDX-License-Identifier: GPL-2.0-or-later /* * * BlueZ - Bluetooth protocol stack for Linux * * Copyright (C) 2006-2007 Nokia Corporation * Copyright (C) 2004-2009 Marcel Holtmann * Copyright (C) 2011 BMW Car IT GmbH. All rights reserved. * Copyright 2023 NXP * */ #ifdef HAVE_CONFIG_H #include #endif #define _GNU_SOURCE #include #include #include #include "lib/bluetooth.h" #include "lib/sdp.h" #include "lib/uuid.h" #include "lib/mgmt.h" #include "gdbus/gdbus.h" #include "src/plugin.h" #include "src/adapter.h" #include "src/device.h" #include "src/dbus-common.h" #include "src/profile.h" #include "src/service.h" #include "src/uuid-helper.h" #include "src/log.h" #include "src/error.h" #include "src/gatt-database.h" #include "src/shared/asha.h" #include "src/shared/util.h" #include "src/shared/queue.h" #include "src/shared/att.h" #include "src/shared/bap.h" #include "src/shared/bap-debug.h" #include "avdtp.h" #include "media.h" #include "transport.h" #include "a2dp.h" #ifdef HAVE_AVRCP #include "avrcp.h" #endif #define MEDIA_INTERFACE "org.bluez.Media1" #define MEDIA_ENDPOINT_INTERFACE "org.bluez.MediaEndpoint1" #define MEDIA_PLAYER_INTERFACE "org.mpris.MediaPlayer2.Player" #define REQUEST_TIMEOUT (3 * 1000) /* 3 seconds */ struct media_app { struct media_adapter *adapter; GDBusClient *client; DBusMessage *reg; char *sender; /* Application bus id */ char *path; /* Application object path */ struct queue *proxies; /* Application proxies */ struct queue *endpoints; /* Application endpoints */ #ifdef HAVE_AVRCP struct queue *players; /* Application players */ #endif int err; }; struct media_adapter { struct btd_adapter *btd_adapter; struct queue *apps; /* Application list */ GSList *endpoints; /* Endpoints list */ #ifdef HAVE_AVRCP GSList *players; /* Players list */ #endif }; struct endpoint_request { struct media_endpoint *endpoint; struct media_transport *transport; DBusMessage *msg; DBusPendingCall *call; media_endpoint_cb_t cb; GDestroyNotify destroy; void *user_data; }; struct media_endpoint { struct a2dp_sep *sep; struct bt_bap_pac *pac; struct bt_asha_device *asha; char *sender; /* Endpoint DBus bus id */ char *path; /* Endpoint object path */ char *uuid; /* Endpoint property UUID */ uint8_t codec; /* Endpoint codec */ uint16_t cid; /* Endpoint company ID */ uint16_t vid; /* Endpoint vendor codec ID */ bool delay_reporting;/* Endpoint delay_reporting */ struct bt_bap_pac_qos qos; /* Endpoint qos */ uint8_t *capabilities; /* Endpoint property capabilities */ size_t size; /* Endpoint capabilities size */ uint8_t *metadata; /* Endpoint property metadata */ size_t metadata_size; /* Endpoint metadata size */ guint hs_watch; guint ag_watch; guint watch; GSList *requests; struct media_adapter *adapter; GSList *transports; }; struct media_player { struct media_adapter *adapter; struct avrcp_player *player; char *sender; /* Player DBus bus id */ char *path; /* Player object path */ GHashTable *settings; /* Player settings */ GHashTable *track; /* Player current track */ guint watch; guint properties_watch; guint seek_watch; char *status; uint32_t position; uint32_t duration; int8_t volume; GTimer *timer; bool play; bool pause; bool next; bool previous; bool control; char *name; }; static GSList *adapters = NULL; static void endpoint_request_free(struct endpoint_request *request) { if (request->call) dbus_pending_call_unref(request->call); if (request->destroy) request->destroy(request->user_data); dbus_message_unref(request->msg); g_free(request); } static void media_endpoint_cancel(struct endpoint_request *request) { struct media_endpoint *endpoint = request->endpoint; DBG("Canceling %s: name = %s path = %s", dbus_message_get_member(request->msg), dbus_message_get_destination(request->msg), dbus_message_get_path(request->msg)); if (request->call) dbus_pending_call_cancel(request->call); endpoint->requests = g_slist_remove(endpoint->requests, request); if (request->cb) request->cb(endpoint, NULL, -1, request->user_data); endpoint_request_free(request); } static void media_endpoint_cancel_all(struct media_endpoint *endpoint) { while (endpoint->requests != NULL) media_endpoint_cancel(endpoint->requests->data); } static void media_endpoint_destroy(struct media_endpoint *endpoint) { DBG("sender=%s path=%s", endpoint->sender, endpoint->path); media_endpoint_cancel_all(endpoint); g_slist_free_full(endpoint->transports, (GDestroyNotify) media_transport_destroy); endpoint->transports = NULL; if (endpoint->pac) { bt_bap_remove_pac(endpoint->pac); endpoint->pac = NULL; } g_dbus_remove_watch(btd_get_dbus_connection(), endpoint->watch); g_free(endpoint->capabilities); g_free(endpoint->metadata); g_free(endpoint->sender); g_free(endpoint->path); g_free(endpoint->uuid); g_free(endpoint); } static struct media_endpoint *media_adapter_find_endpoint( struct media_adapter *adapter, const char *sender, const char *path, const char *uuid) { GSList *l; for (l = adapter->endpoints; l; l = l->next) { struct media_endpoint *endpoint = l->data; if (sender && g_strcmp0(endpoint->sender, sender) != 0) continue; if (path && g_strcmp0(endpoint->path, path) != 0) continue; if (uuid && strcasecmp(endpoint->uuid, uuid) != 0) continue; return endpoint; } return NULL; } static void media_endpoint_remove(void *data) { struct media_endpoint *endpoint = data; struct media_adapter *adapter = endpoint->adapter; if (endpoint->sep) { a2dp_remove_sep(endpoint->sep); return; } info("Endpoint unregistered: sender=%s path=%s", endpoint->sender, endpoint->path); adapter->endpoints = g_slist_remove(adapter->endpoints, endpoint); if (media_adapter_find_endpoint(adapter, NULL, NULL, endpoint->uuid) == NULL) btd_profile_remove_custom_prop(endpoint->uuid, "MediaEndpoints"); media_endpoint_destroy(endpoint); } static void media_endpoint_exit(DBusConnection *connection, void *user_data) { struct media_endpoint *endpoint = user_data; endpoint->watch = 0; media_endpoint_remove(endpoint); } static struct media_adapter *find_adapter(struct btd_device *device) { GSList *l; for (l = adapters; l; l = l->next) { struct media_adapter *adapter = l->data; if (adapter->btd_adapter == device_get_adapter(device)) return adapter; } return NULL; } static void endpoint_remove_transport(struct media_endpoint *endpoint, struct media_transport *transport) { if (!endpoint || !transport) return; endpoint->transports = g_slist_remove(endpoint->transports, transport); media_transport_destroy(transport); } static void clear_configuration(struct media_endpoint *endpoint, struct media_transport *transport) { DBusMessage *msg; const char *path; msg = dbus_message_new_method_call(endpoint->sender, endpoint->path, MEDIA_ENDPOINT_INTERFACE, "ClearConfiguration"); if (msg == NULL) { error("Couldn't allocate D-Bus message"); goto done; } path = media_transport_get_path(transport); dbus_message_append_args(msg, DBUS_TYPE_OBJECT_PATH, &path, DBUS_TYPE_INVALID); g_dbus_send_message(btd_get_dbus_connection(), msg); done: endpoint_remove_transport(endpoint, transport); } static void clear_endpoint(struct media_endpoint *endpoint) { media_endpoint_cancel_all(endpoint); while (endpoint->transports != NULL) clear_configuration(endpoint, endpoint->transports->data); } static void endpoint_reply(DBusPendingCall *call, void *user_data) { struct endpoint_request *request = user_data; struct media_endpoint *endpoint = request->endpoint; DBusMessage *reply; DBusMessageIter args, props; DBusError err; gboolean value; void *ret = NULL; int size = -1; /* steal_reply will always return non-NULL since the callback * is only called after a reply has been received */ reply = dbus_pending_call_steal_reply(call); dbus_error_init(&err); if (dbus_set_error_from_message(&err, reply)) { error("Endpoint replied with an error: %s", err.name); /* Clear endpoint configuration in case of NO_REPLY error */ if (dbus_error_has_name(&err, DBUS_ERROR_NO_REPLY)) { clear_endpoint(endpoint); dbus_message_unref(reply); dbus_error_free(&err); return; } if (dbus_message_is_method_call(request->msg, MEDIA_ENDPOINT_INTERFACE, "SetConfiguration")) endpoint_remove_transport(endpoint, request->transport); dbus_error_free(&err); goto done; } if (dbus_message_is_method_call(request->msg, MEDIA_ENDPOINT_INTERFACE, "SelectConfiguration")) { DBusMessageIter args, array; uint8_t *configuration; dbus_message_iter_init(reply, &args); dbus_message_iter_recurse(&args, &array); dbus_message_iter_get_fixed_array(&array, &configuration, &size); ret = configuration; goto done; } else if (dbus_message_is_method_call(request->msg, MEDIA_ENDPOINT_INTERFACE, "SelectProperties")) { dbus_message_iter_init(reply, &args); dbus_message_iter_recurse(&args, &props); ret = &props; goto done; } else if (!dbus_message_get_args(reply, &err, DBUS_TYPE_INVALID)) { error("Wrong reply signature: %s", err.message); dbus_error_free(&err); goto done; } size = 1; value = TRUE; ret = &value; done: dbus_message_unref(reply); if (request->cb) request->cb(endpoint, ret, size, request->user_data); endpoint->requests = g_slist_remove(endpoint->requests, request); endpoint_request_free(request); } static gboolean media_endpoint_async_call(DBusMessage *msg, struct media_endpoint *endpoint, struct media_transport *transport, media_endpoint_cb_t cb, void *user_data, GDestroyNotify destroy) { struct endpoint_request *request; request = g_new0(struct endpoint_request, 1); /* Timeout should be less than avdtp request timeout (4 seconds) */ if (g_dbus_send_message_with_reply(btd_get_dbus_connection(), msg, &request->call, REQUEST_TIMEOUT) == FALSE) { error("D-Bus send failed"); g_free(request); return FALSE; } dbus_pending_call_set_notify(request->call, endpoint_reply, request, NULL); request->endpoint = endpoint; request->transport = transport; request->msg = msg; request->cb = cb; request->destroy = destroy; request->user_data = user_data; endpoint->requests = g_slist_append(endpoint->requests, request); DBG("Calling %s: name = %s path = %s", dbus_message_get_member(msg), dbus_message_get_destination(msg), dbus_message_get_path(msg)); return TRUE; } static gboolean select_configuration(struct media_endpoint *endpoint, uint8_t *capabilities, size_t length, media_endpoint_cb_t cb, void *user_data, GDestroyNotify destroy) { DBusMessage *msg; msg = dbus_message_new_method_call(endpoint->sender, endpoint->path, MEDIA_ENDPOINT_INTERFACE, "SelectConfiguration"); if (msg == NULL) { error("Couldn't allocate D-Bus message"); return FALSE; } dbus_message_append_args(msg, DBUS_TYPE_ARRAY, DBUS_TYPE_BYTE, &capabilities, length, DBUS_TYPE_INVALID); return media_endpoint_async_call(msg, endpoint, NULL, cb, user_data, destroy); } static int transport_device_cmp(gconstpointer data, gconstpointer user_data) { struct media_transport *transport = (struct media_transport *) data; const struct btd_device *device = user_data; const struct btd_device *dev = media_transport_get_dev(transport); if (device == dev) return 0; return -1; } static struct media_transport *find_device_transport( struct media_endpoint *endpoint, struct btd_device *device) { GSList *match; match = g_slist_find_custom(endpoint->transports, device, transport_device_cmp); if (match == NULL) return NULL; return match->data; } struct a2dp_config_data { struct a2dp_setup *setup; a2dp_endpoint_config_t cb; }; int8_t media_player_get_device_volume(struct btd_device *device) { #ifdef HAVE_AVRCP struct avrcp_player *target_player; struct media_adapter *adapter; GSList *l; if (!device) return -1; target_player = avrcp_get_target_player_by_device(device); if (!target_player) goto done; adapter = find_adapter(device); if (!adapter) goto done; for (l = adapter->players; l; l = l->next) { struct media_player *mp = l->data; if (mp->player == target_player) return mp->volume; } done: #endif /* HAVE_AVRCP */ /* If media_player doesn't exists use device_volume */ return btd_device_get_volume(device); } static gboolean set_configuration(struct media_endpoint *endpoint, uint8_t *configuration, size_t size, media_endpoint_cb_t cb, void *user_data, GDestroyNotify destroy) { struct a2dp_config_data *data = user_data; struct btd_device *device = a2dp_setup_get_device(data->setup); DBusConnection *conn = btd_get_dbus_connection(); DBusMessage *msg; const char *path; DBusMessageIter iter; struct media_transport *transport; int8_t init_volume; transport = find_device_transport(endpoint, device); if (transport != NULL) return FALSE; transport = media_transport_create(device, a2dp_setup_remote_path(data->setup), configuration, size, endpoint, NULL); if (transport == NULL) return FALSE; init_volume = media_player_get_device_volume(device); media_transport_update_volume(transport, init_volume); msg = dbus_message_new_method_call(endpoint->sender, endpoint->path, MEDIA_ENDPOINT_INTERFACE, "SetConfiguration"); if (msg == NULL) { error("Couldn't allocate D-Bus message"); media_transport_destroy(transport); return FALSE; } endpoint->transports = g_slist_append(endpoint->transports, transport); dbus_message_iter_init_append(msg, &iter); path = media_transport_get_path(transport); dbus_message_iter_append_basic(&iter, DBUS_TYPE_OBJECT_PATH, &path); g_dbus_get_properties(conn, path, "org.bluez.MediaTransport1", &iter); return media_endpoint_async_call(msg, endpoint, transport, cb, user_data, destroy); } static void release_endpoint(struct media_endpoint *endpoint) { DBusMessage *msg; DBG("sender=%s path=%s", endpoint->sender, endpoint->path); /* already exit */ if (endpoint->watch == 0) goto done; clear_endpoint(endpoint); msg = dbus_message_new_method_call(endpoint->sender, endpoint->path, MEDIA_ENDPOINT_INTERFACE, "Release"); if (msg == NULL) { error("Couldn't allocate D-Bus message"); return; } g_dbus_send_message(btd_get_dbus_connection(), msg); done: media_endpoint_remove(endpoint); } static const char *get_name(struct a2dp_sep *sep, void *user_data) { struct media_endpoint *endpoint = user_data; return endpoint->sender; } static const char *get_path(struct a2dp_sep *sep, void *user_data) { struct media_endpoint *endpoint = user_data; return endpoint->path; } static size_t get_capabilities(struct a2dp_sep *sep, uint8_t **capabilities, void *user_data) { struct media_endpoint *endpoint = user_data; *capabilities = endpoint->capabilities; return endpoint->size; } struct a2dp_select_data { struct a2dp_setup *setup; a2dp_endpoint_select_t cb; }; static void select_cb(struct media_endpoint *endpoint, void *ret, int size, void *user_data) { struct a2dp_select_data *data = user_data; data->cb(data->setup, ret, size); } static int select_config(struct a2dp_sep *sep, uint8_t *capabilities, size_t length, struct a2dp_setup *setup, a2dp_endpoint_select_t cb, void *user_data) { struct media_endpoint *endpoint = user_data; struct a2dp_select_data *data; data = g_new0(struct a2dp_select_data, 1); data->setup = setup; data->cb = cb; if (select_configuration(endpoint, capabilities, length, select_cb, data, g_free) == TRUE) return 0; g_free(data); return -ENOMEM; } static void config_cb(struct media_endpoint *endpoint, void *ret, int size, void *user_data) { struct a2dp_config_data *data = user_data; gboolean *ret_value = ret; data->cb(data->setup, ret_value ? *ret_value : FALSE); } static int set_config(struct a2dp_sep *sep, uint8_t *configuration, size_t length, struct a2dp_setup *setup, a2dp_endpoint_config_t cb, void *user_data) { struct media_endpoint *endpoint = user_data; struct a2dp_config_data *data; data = g_new0(struct a2dp_config_data, 1); data->setup = setup; data->cb = cb; if (set_configuration(endpoint, configuration, length, config_cb, data, g_free) == TRUE) return 0; g_free(data); return -ENOMEM; } static void clear_config(struct a2dp_sep *sep, void *user_data) { struct media_endpoint *endpoint = user_data; clear_endpoint(endpoint); } static void set_delay(struct a2dp_sep *sep, uint16_t delay, void *user_data) { struct media_endpoint *endpoint = user_data; if (endpoint->transports == NULL) return; media_transport_update_delay(endpoint->transports->data, delay); } static struct a2dp_endpoint a2dp_endpoint = { .get_name = get_name, .get_path = get_path, .get_capabilities = get_capabilities, .select_configuration = select_config, .set_configuration = set_config, .clear_configuration = clear_config, .set_delay = set_delay }; static void a2dp_destroy_endpoint(void *user_data) { struct media_endpoint *endpoint = user_data; endpoint->sep = NULL; release_endpoint(endpoint); } static bool endpoint_init_a2dp_source(struct media_endpoint *endpoint, int *err) { endpoint->sep = a2dp_add_sep(endpoint->adapter->btd_adapter, AVDTP_SEP_TYPE_SOURCE, endpoint->codec, endpoint->delay_reporting, &a2dp_endpoint, endpoint, a2dp_destroy_endpoint, err); if (endpoint->sep == NULL) return false; return true; } static bool endpoint_init_a2dp_sink(struct media_endpoint *endpoint, int *err) { endpoint->sep = a2dp_add_sep(endpoint->adapter->btd_adapter, AVDTP_SEP_TYPE_SINK, endpoint->codec, endpoint->delay_reporting, &a2dp_endpoint, endpoint, a2dp_destroy_endpoint, err); if (endpoint->sep == NULL) return false; return true; } struct pac_select_data { struct bt_bap_pac *pac; bt_bap_pac_select_t cb; void *user_data; }; static int parse_array(DBusMessageIter *iter, struct iovec *iov) { DBusMessageIter array; if (!iov) return 0; dbus_message_iter_recurse(iter, &array); dbus_message_iter_get_fixed_array(&array, &iov->iov_base, (int *)&iov->iov_len); return 0; } static int parse_ucast_qos(DBusMessageIter *iter, struct bt_bap_qos *qos) { DBusMessageIter array; const char *key; struct bt_bap_io_qos io_qos; dbus_message_iter_recurse(iter, &array); memset(&io_qos, 0, sizeof(io_qos)); while (dbus_message_iter_get_arg_type(&array) == DBUS_TYPE_DICT_ENTRY) { DBusMessageIter value, entry; int var; dbus_message_iter_recurse(&array, &entry); dbus_message_iter_get_basic(&entry, &key); dbus_message_iter_next(&entry); dbus_message_iter_recurse(&entry, &value); var = dbus_message_iter_get_arg_type(&value); if (!strcasecmp(key, "CIG")) { if (var != DBUS_TYPE_BYTE) goto fail; dbus_message_iter_get_basic(&value, &qos->ucast.cig_id); } else if (!strcasecmp(key, "CIS")) { if (var != DBUS_TYPE_BYTE) goto fail; dbus_message_iter_get_basic(&value, &qos->ucast.cis_id); } else if (!strcasecmp(key, "Interval")) { if (var != DBUS_TYPE_UINT32) goto fail; dbus_message_iter_get_basic(&value, &io_qos.interval); } else if (!strcasecmp(key, "Framing")) { if (var != DBUS_TYPE_BYTE) goto fail; dbus_message_iter_get_basic(&value, &qos->ucast.framing); } else if (!strcasecmp(key, "PHY")) { if (var != DBUS_TYPE_BYTE) goto fail; dbus_message_iter_get_basic(&value, &io_qos.phy); } else if (!strcasecmp(key, "SDU")) { if (var != DBUS_TYPE_UINT16) goto fail; dbus_message_iter_get_basic(&value, &io_qos.sdu); } else if (!strcasecmp(key, "Retransmissions")) { if (var != DBUS_TYPE_BYTE) goto fail; dbus_message_iter_get_basic(&value, &io_qos.rtn); } else if (!strcasecmp(key, "Latency")) { if (var != DBUS_TYPE_UINT16) goto fail; dbus_message_iter_get_basic(&value, &io_qos.latency); } else if (!strcasecmp(key, "PresentationDelay")) { if (var != DBUS_TYPE_UINT32) goto fail; dbus_message_iter_get_basic(&value, &qos->ucast.delay); } else if (!strcasecmp(key, "TargetLatency")) { if (var != DBUS_TYPE_BYTE) goto fail; dbus_message_iter_get_basic(&value, &qos->ucast.target_latency); } dbus_message_iter_next(&array); } memcpy(&qos->ucast.io_qos, &io_qos, sizeof(io_qos)); return 0; fail: DBG("Failed parsing %s", key); return -EINVAL; } static int parse_select_properties(DBusMessageIter *props, struct iovec *caps, struct iovec *metadata, struct bt_bap_qos *qos) { const char *key; while (dbus_message_iter_get_arg_type(props) == DBUS_TYPE_DICT_ENTRY) { DBusMessageIter value, entry; int var; dbus_message_iter_recurse(props, &entry); dbus_message_iter_get_basic(&entry, &key); dbus_message_iter_next(&entry); dbus_message_iter_recurse(&entry, &value); var = dbus_message_iter_get_arg_type(&value); if (!strcasecmp(key, "Capabilities")) { if (var != DBUS_TYPE_ARRAY) goto fail; if (parse_array(&value, caps)) goto fail; } else if (!strcasecmp(key, "Metadata")) { if (var != DBUS_TYPE_ARRAY) goto fail; if (parse_array(&value, metadata)) goto fail; } else if (!strcasecmp(key, "QoS")) { if (var != DBUS_TYPE_ARRAY) goto fail; if (parse_ucast_qos(&value, qos)) goto fail; } dbus_message_iter_next(props); } return 0; fail: DBG("Failed parsing %s", key); return -EINVAL; } static void pac_select_cb(struct media_endpoint *endpoint, void *ret, int size, void *user_data) { struct pac_select_data *data = user_data; DBusMessageIter *iter = ret; int err; struct iovec caps, meta; struct bt_bap_qos qos; if (!ret) { err = -EPERM; goto done; } if (dbus_message_iter_get_arg_type(iter) != DBUS_TYPE_DICT_ENTRY) { DBG("Unexpected argument type: %c != %c", dbus_message_iter_get_arg_type(iter), DBUS_TYPE_DICT_ENTRY); err = -EINVAL; goto done; } memset(&qos, 0, sizeof(qos)); /* Mark CIG and CIS to be auto assigned */ qos.ucast.cig_id = BT_ISO_QOS_CIG_UNSET; qos.ucast.cis_id = BT_ISO_QOS_CIS_UNSET; memset(&caps, 0, sizeof(caps)); memset(&meta, 0, sizeof(meta)); err = parse_select_properties(iter, &caps, &meta, &qos); if (err < 0) DBG("Unable to parse properties"); done: data->cb(data->pac, err, &caps, &meta, &qos, data->user_data); } static int pac_select(struct bt_bap_pac *lpac, struct bt_bap_pac *rpac, uint32_t location, struct bt_bap_pac_qos *qos, bt_bap_pac_select_t cb, void *cb_data, void *user_data) { struct media_endpoint *endpoint = user_data; struct iovec *caps; struct iovec *metadata; const char *endpoint_path; struct pac_select_data *data; DBusMessage *msg; DBusMessageIter iter, dict; const char *key = "Capabilities"; uint32_t loc; bt_bap_pac_get_codec(rpac, NULL, &caps, &metadata); if (!caps) return -EINVAL; msg = dbus_message_new_method_call(endpoint->sender, endpoint->path, MEDIA_ENDPOINT_INTERFACE, "SelectProperties"); if (msg == NULL) { error("Couldn't allocate D-Bus message"); return -ENOMEM; } data = new0(struct pac_select_data, 1); data->pac = lpac; data->cb = cb; data->user_data = cb_data; dbus_message_iter_init_append(msg, &iter); dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY, "{sv}", &dict); endpoint_path = bt_bap_pac_get_user_data(rpac); if (endpoint_path) g_dbus_dict_append_entry(&dict, "Endpoint", DBUS_TYPE_OBJECT_PATH, &endpoint_path); g_dbus_dict_append_basic_array(&dict, DBUS_TYPE_STRING, &key, DBUS_TYPE_BYTE, &caps->iov_base, caps->iov_len); loc = bt_bap_pac_get_locations(rpac); if (loc) g_dbus_dict_append_entry(&dict, "Locations", DBUS_TYPE_UINT32, &loc); if (location) g_dbus_dict_append_entry(&dict, "ChannelAllocation", DBUS_TYPE_UINT32, &location); if (metadata) { key = "Metadata"; g_dbus_dict_append_basic_array(&dict, DBUS_TYPE_STRING, &key, DBUS_TYPE_BYTE, &metadata->iov_base, metadata->iov_len); } if (qos && qos->phy) { DBusMessageIter entry, variant, qos_dict; key = "QoS"; dbus_message_iter_open_container(&dict, DBUS_TYPE_DICT_ENTRY, NULL, &entry); dbus_message_iter_append_basic(&entry, DBUS_TYPE_STRING, &key); dbus_message_iter_open_container(&entry, DBUS_TYPE_VARIANT, "a{sv}", &variant); dbus_message_iter_open_container(&variant, DBUS_TYPE_ARRAY, "{sv}", &qos_dict); g_dbus_dict_append_entry(&qos_dict, "Framing", DBUS_TYPE_BYTE, &qos->framing); g_dbus_dict_append_entry(&qos_dict, "PHY", DBUS_TYPE_BYTE, &qos->phy); g_dbus_dict_append_entry(&qos_dict, "Retransmissions", DBUS_TYPE_BYTE, &qos->rtn); g_dbus_dict_append_entry(&qos_dict, "MaximumLatency", DBUS_TYPE_UINT16, &qos->latency); g_dbus_dict_append_entry(&qos_dict, "MinimumDelay", DBUS_TYPE_UINT32, &qos->pd_min); g_dbus_dict_append_entry(&qos_dict, "MaximumDelay", DBUS_TYPE_UINT32, &qos->pd_max); g_dbus_dict_append_entry(&qos_dict, "PreferredMinimumDelay", DBUS_TYPE_UINT32, &qos->ppd_min); g_dbus_dict_append_entry(&qos_dict, "PreferredMaximumDelay", DBUS_TYPE_UINT32, &qos->ppd_max); dbus_message_iter_close_container(&variant, &qos_dict); dbus_message_iter_close_container(&entry, &variant); dbus_message_iter_close_container(&dict, &entry); } dbus_message_iter_close_container(&iter, &dict); return media_endpoint_async_call(msg, endpoint, NULL, pac_select_cb, data, free); } static void pac_cancel_select(struct bt_bap_pac *lpac, bt_bap_pac_select_t cb, void *cb_data, void *user_data) { struct media_endpoint *endpoint = user_data; GSList *l = endpoint->requests; while (l) { struct endpoint_request *req = l->data; struct pac_select_data *data; if (req->cb != pac_select_cb) { l = g_slist_next(l); continue; } data = req->user_data; if (data->pac != lpac || data->cb != cb || data->user_data != cb_data) { l = g_slist_next(l); continue; } media_endpoint_cancel(req); l = endpoint->requests; } } struct pac_config_data { struct bt_bap_stream *stream; bt_bap_pac_config_t cb; void *user_data; }; static int transport_cmp(gconstpointer data, gconstpointer user_data) { const struct media_transport *transport = data; if (media_transport_get_stream((void *)transport) == user_data) return 0; return -1; } static struct media_transport *find_transport(struct media_endpoint *endpoint, void *stream) { GSList *match; match = g_slist_find_custom(endpoint->transports, stream, transport_cmp); if (match == NULL) return NULL; return match->data; } static void pac_config_cb(struct media_endpoint *endpoint, void *ret, int size, void *user_data) { struct pac_config_data *data = user_data; gboolean *ret_value = ret; struct media_transport *transport; /* If transport was cleared, configuration was cancelled */ transport = find_transport(endpoint, data->stream); if (!transport) return; data->cb(data->stream, ret_value ? 0 : -EINVAL); } static struct media_transport *pac_ucast_config(struct bt_bap_stream *stream, struct iovec *cfg, struct media_endpoint *endpoint) { struct bt_bap *bap = bt_bap_stream_get_session(stream); struct btd_service *service = bt_bap_get_user_data(bap); struct btd_device *device; const char *path; if (service) device = btd_service_get_device(service); else { struct bt_att *att = bt_bap_get_att(bap); int fd = bt_att_get_fd(att); device = btd_adapter_find_device_by_fd(fd); } if (!device) { error("Unable to find device"); return NULL; } path = bt_bap_stream_get_user_data(stream); return media_transport_create(device, path, cfg->iov_base, cfg->iov_len, endpoint, stream); } static struct media_transport *pac_bcast_config(struct bt_bap_stream *stream, struct iovec *cfg, struct media_endpoint *endpoint) { struct bt_bap *bap = bt_bap_stream_get_session(stream); struct btd_adapter *adapter = endpoint->adapter->btd_adapter; struct btd_device *device; const char *path; if (!adapter) return NULL; if (!strcmp(endpoint->uuid, BCAA_SERVICE_UUID)) device = NULL; else device = btd_service_get_device(bt_bap_get_user_data(bap)); path = bt_bap_stream_get_user_data(stream); return media_transport_create(device, path, cfg->iov_base, cfg->iov_len, endpoint, stream); } static int pac_config(struct bt_bap_stream *stream, struct iovec *cfg, struct bt_bap_qos *qos, bt_bap_pac_config_t cb, void *user_data) { struct media_endpoint *endpoint = user_data; DBusConnection *conn = btd_get_dbus_connection(); struct pac_config_data *data; struct media_transport *transport; DBusMessage *msg; DBusMessageIter iter; const char *path; DBG("endpoint %p stream %p", endpoint, stream); transport = find_transport(endpoint, stream); if (!transport) { switch (bt_bap_stream_get_type(stream)) { case BT_BAP_STREAM_TYPE_UCAST: transport = pac_ucast_config(stream, cfg, endpoint); break; case BT_BAP_STREAM_TYPE_BCAST: transport = pac_bcast_config(stream, cfg, endpoint); break; } if (!transport) return -EINVAL; endpoint->transports = g_slist_append(endpoint->transports, transport); } msg = dbus_message_new_method_call(endpoint->sender, endpoint->path, MEDIA_ENDPOINT_INTERFACE, "SetConfiguration"); if (msg == NULL) { error("Couldn't allocate D-Bus message"); endpoint_remove_transport(endpoint, transport); return FALSE; } data = new0(struct pac_config_data, 1); data->stream = stream; data->cb = cb; data->user_data = user_data; dbus_message_iter_init_append(msg, &iter); path = media_transport_get_path(transport); dbus_message_iter_append_basic(&iter, DBUS_TYPE_OBJECT_PATH, &path); g_dbus_get_properties(conn, path, "org.bluez.MediaTransport1", &iter); return media_endpoint_async_call(msg, endpoint, transport, pac_config_cb, data, free); } static void pac_clear(struct bt_bap_stream *stream, void *user_data) { struct media_endpoint *endpoint = user_data; struct media_transport *transport; DBG("endpoint %p stream %p", endpoint, stream); transport = find_transport(endpoint, stream); if (transport) clear_configuration(endpoint, transport); } static struct bt_bap_pac_ops pac_ops = { .select = pac_select, .cancel_select = pac_cancel_select, .config = pac_config, .clear = pac_clear, }; static void bap_debug(const char *str, void *user_data) { DBG("%s", str); } static bool endpoint_init_pac(struct media_endpoint *endpoint, uint8_t type, int *err) { struct btd_gatt_database *database; struct gatt_db *db; struct iovec data; struct iovec *metadata = NULL; char *name; if (!(g_dbus_get_flags() & G_DBUS_FLAG_ENABLE_EXPERIMENTAL)) { DBG("D-Bus experimental not enabled"); *err = -ENOTSUP; return false; } database = btd_adapter_get_database(endpoint->adapter->btd_adapter); if (!database) { error("Adapter database not found"); return false; } if (!bt_bap_debug_caps(endpoint->capabilities, endpoint->size, bap_debug, NULL)) { error("Unable to parse endpoint capabilities"); return false; } if (!bt_bap_debug_metadata(endpoint->metadata, endpoint->metadata_size, bap_debug, NULL)) { error("Unable to parse endpoint metadata"); return false; } db = btd_gatt_database_get_db(database); data.iov_base = endpoint->capabilities; data.iov_len = endpoint->size; if (asprintf(&name, "%s:%s", endpoint->sender, endpoint->path) < 0) { error("Could not allocate name for pac %s:%s", endpoint->sender, endpoint->path); free(name); return false; } /* TODO: Add support for metadata */ if (endpoint->metadata_size) { metadata = g_new0(struct iovec, 1); metadata->iov_base = endpoint->metadata; metadata->iov_len = endpoint->metadata_size; } endpoint->pac = bt_bap_add_vendor_pac(db, name, type, endpoint->codec, endpoint->cid, endpoint->vid, &endpoint->qos, &data, metadata); if (!endpoint->pac) { error("Unable to create PAC"); free(name); free(metadata); return false; } bt_bap_pac_set_ops(endpoint->pac, &pac_ops, endpoint); DBG("PAC %s registered", name); free(name); free(metadata); return true; } static bool endpoint_init_pac_sink(struct media_endpoint *endpoint, int *err) { return endpoint_init_pac(endpoint, BT_BAP_SINK, err); } static bool endpoint_init_pac_source(struct media_endpoint *endpoint, int *err) { return endpoint_init_pac(endpoint, BT_BAP_SOURCE, err); } static bool endpoint_init_broadcast_source(struct media_endpoint *endpoint, int *err) { return endpoint_init_pac(endpoint, BT_BAP_BCAST_SOURCE, err); } static bool endpoint_init_broadcast_sink(struct media_endpoint *endpoint, int *err) { return endpoint_init_pac(endpoint, BT_BAP_BCAST_SINK, err); } static bool endpoint_init_asha(struct media_endpoint *endpoint, int *err) { return true; } static bool endpoint_properties_exists(const char *uuid, struct btd_device *dev, void *user_data) { struct media_adapter *adapter; adapter = find_adapter(dev); if (adapter == NULL) return false; if (media_adapter_find_endpoint(adapter, NULL, NULL, uuid) == NULL) return false; return true; } static void append_endpoint(struct media_endpoint *endpoint, DBusMessageIter *dict) { DBusMessageIter entry, var, props; dbus_message_iter_open_container(dict, DBUS_TYPE_DICT_ENTRY, NULL, &entry); dbus_message_iter_append_basic(&entry, DBUS_TYPE_STRING, &endpoint->sender); dbus_message_iter_open_container(&entry, DBUS_TYPE_VARIANT, "a{sv}", &var); dbus_message_iter_open_container(&var, DBUS_TYPE_ARRAY, DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING DBUS_TYPE_STRING_AS_STRING DBUS_TYPE_VARIANT_AS_STRING DBUS_DICT_ENTRY_END_CHAR_AS_STRING, &props); dict_append_entry(&props, "Path", DBUS_TYPE_OBJECT_PATH, &endpoint->path); dict_append_entry(&props, "Codec", DBUS_TYPE_BYTE, &endpoint->codec); dict_append_array(&props, "Capabilities", DBUS_TYPE_BYTE, &endpoint->capabilities, endpoint->size); dbus_message_iter_close_container(&var, &props); dbus_message_iter_close_container(&entry, &var); dbus_message_iter_close_container(dict, &entry); } static bool endpoint_properties_get(const char *uuid, struct btd_device *dev, DBusMessageIter *iter, void *user_data) { struct media_adapter *adapter; DBusMessageIter dict; GSList *l; adapter = find_adapter(dev); if (adapter == NULL) return false; dbus_message_iter_open_container(iter, DBUS_TYPE_ARRAY, DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING DBUS_TYPE_STRING_AS_STRING DBUS_TYPE_VARIANT_AS_STRING DBUS_DICT_ENTRY_END_CHAR_AS_STRING, &dict); for (l = adapter->endpoints; l; l = l->next) { struct media_endpoint *endpoint = l->data; if (strcasecmp(endpoint->uuid, uuid) != 0) continue; append_endpoint(endpoint, &dict); } dbus_message_iter_close_container(iter, &dict); return true; } static bool a2dp_endpoint_supported(struct btd_adapter *adapter) { if (!btd_adapter_has_settings(adapter, MGMT_SETTING_BREDR)) return false; return true; } static bool experimental_endpoint_supported(struct btd_adapter *adapter) { if (!btd_adapter_has_exp_feature(adapter, EXP_FEAT_ISO_SOCKET)) return false; if (!btd_adapter_has_settings(adapter, MGMT_SETTING_CIS_CENTRAL | MGMT_SETTING_CIS_PERIPHERAL)) return false; return g_dbus_get_flags() & G_DBUS_FLAG_ENABLE_EXPERIMENTAL; } static bool experimental_broadcaster_ep_supported(struct btd_adapter *adapter) { if (!btd_adapter_has_exp_feature(adapter, EXP_FEAT_ISO_SOCKET)) return false; if (!btd_adapter_has_settings(adapter, MGMT_SETTING_ISO_BROADCASTER)) return false; return g_dbus_get_flags() & G_DBUS_FLAG_ENABLE_EXPERIMENTAL; } static bool experimental_bcast_sink_ep_supported(struct btd_adapter *adapter) { if (!btd_adapter_has_exp_feature(adapter, EXP_FEAT_ISO_SOCKET)) return false; if (!btd_adapter_has_settings(adapter, MGMT_SETTING_ISO_SYNC_RECEIVER)) return false; return g_dbus_get_flags() & G_DBUS_FLAG_ENABLE_EXPERIMENTAL; } static bool experimental_asha_supported(struct btd_adapter *adapter) { return g_dbus_get_flags() & G_DBUS_FLAG_ENABLE_EXPERIMENTAL; } static const struct media_endpoint_init { const char *uuid; bool (*func)(struct media_endpoint *endpoint, int *err); bool (*supported)(struct btd_adapter *adapter); } init_table[] = { { A2DP_SOURCE_UUID, endpoint_init_a2dp_source, a2dp_endpoint_supported }, { A2DP_SINK_UUID, endpoint_init_a2dp_sink, a2dp_endpoint_supported }, { PAC_SINK_UUID, endpoint_init_pac_sink, experimental_endpoint_supported }, { PAC_SOURCE_UUID, endpoint_init_pac_source, experimental_endpoint_supported }, { BCAA_SERVICE_UUID, endpoint_init_broadcast_source, experimental_broadcaster_ep_supported }, { BAA_SERVICE_UUID, endpoint_init_broadcast_sink, experimental_bcast_sink_ep_supported }, { ASHA_PROFILE_UUID, endpoint_init_asha, experimental_asha_supported }, }; static struct media_endpoint * media_endpoint_create(struct media_adapter *adapter, const char *sender, const char *path, const char *uuid, gboolean delay_reporting, uint8_t codec, uint16_t cid, uint16_t vid, struct bt_bap_pac_qos *qos, uint8_t *capabilities, int size, uint8_t *metadata, int metadata_size, int *err) { struct media_endpoint *endpoint; const struct media_endpoint_init *init; size_t i; bool succeeded = false; endpoint = g_new0(struct media_endpoint, 1); endpoint->sender = g_strdup(sender); endpoint->path = g_strdup(path); endpoint->uuid = g_strdup(uuid); endpoint->codec = codec; endpoint->cid = cid; endpoint->vid = vid; endpoint->delay_reporting = delay_reporting; if (qos) endpoint->qos = *qos; if (size > 0) { endpoint->capabilities = g_new(uint8_t, size); memcpy(endpoint->capabilities, capabilities, size); endpoint->size = size; } if (metadata_size > 0) { endpoint->metadata = g_new(uint8_t, metadata_size); memcpy(endpoint->metadata, metadata, metadata_size); endpoint->metadata_size = metadata_size; } endpoint->adapter = adapter; for (i = 0; i < ARRAY_SIZE(init_table); i++) { init = &init_table[i]; if (!init->supported(adapter->btd_adapter)) continue; if (!strcasecmp(init->uuid, uuid)) { succeeded = init->func(endpoint, err); break; } } if (!succeeded) { error("Unable initialize endpoint for UUID %s", uuid); media_endpoint_destroy(endpoint); return NULL; } endpoint->watch = g_dbus_add_disconnect_watch(btd_get_dbus_connection(), sender, media_endpoint_exit, endpoint, NULL); if (media_adapter_find_endpoint(adapter, NULL, NULL, uuid) == NULL) { btd_profile_add_custom_prop(uuid, "a{sv}", "MediaEndpoints", endpoint_properties_exists, endpoint_properties_get, NULL); } adapter->endpoints = g_slist_append(adapter->endpoints, endpoint); info("Endpoint registered: sender=%s path=%s", sender, path); if (err) *err = 0; return endpoint; } struct vendor { uint16_t cid; uint16_t vid; } __packed; static int parse_properties(DBusMessageIter *props, const char **uuid, gboolean *delay_reporting, uint8_t *codec, uint16_t *cid, uint16_t *vid, struct bt_bap_pac_qos *qos, uint8_t **capabilities, int *size, uint8_t **metadata, int *metadata_size) { gboolean has_uuid = FALSE; gboolean has_codec = FALSE; struct vendor vendor; while (dbus_message_iter_get_arg_type(props) == DBUS_TYPE_DICT_ENTRY) { const char *key; DBusMessageIter value, entry; int var; dbus_message_iter_recurse(props, &entry); dbus_message_iter_get_basic(&entry, &key); dbus_message_iter_next(&entry); dbus_message_iter_recurse(&entry, &value); var = dbus_message_iter_get_arg_type(&value); if (strcasecmp(key, "UUID") == 0) { if (var != DBUS_TYPE_STRING) return -EINVAL; dbus_message_iter_get_basic(&value, uuid); has_uuid = TRUE; } else if (strcasecmp(key, "Codec") == 0) { if (var != DBUS_TYPE_BYTE) return -EINVAL; dbus_message_iter_get_basic(&value, codec); has_codec = TRUE; } else if (strcasecmp(key, "Vendor") == 0) { if (var != DBUS_TYPE_UINT32) return -EINVAL; dbus_message_iter_get_basic(&value, &vendor); *cid = vendor.cid; *vid = vendor.vid; } else if (strcasecmp(key, "DelayReporting") == 0) { if (var != DBUS_TYPE_BOOLEAN) return -EINVAL; dbus_message_iter_get_basic(&value, delay_reporting); } else if (strcasecmp(key, "Capabilities") == 0) { DBusMessageIter array; if (var != DBUS_TYPE_ARRAY) return -EINVAL; dbus_message_iter_recurse(&value, &array); dbus_message_iter_get_fixed_array(&array, capabilities, size); } else if (strcasecmp(key, "Metadata") == 0) { DBusMessageIter array; if (var != DBUS_TYPE_ARRAY) return -EINVAL; dbus_message_iter_recurse(&value, &array); dbus_message_iter_get_fixed_array(&array, metadata, metadata_size); } else if (strcasecmp(key, "Framing") == 0) { if (var != DBUS_TYPE_BYTE) return -EINVAL; dbus_message_iter_get_basic(&value, &qos->framing); } else if (strcasecmp(key, "PHY") == 0) { if (var != DBUS_TYPE_BYTE) return -EINVAL; dbus_message_iter_get_basic(&value, &qos->phy); } else if (strcasecmp(key, "Retransmissions") == 0) { if (var != DBUS_TYPE_BYTE) return -EINVAL; dbus_message_iter_get_basic(&value, &qos->rtn); } else if (strcasecmp(key, "MinimumDelay") == 0) { if (var != DBUS_TYPE_UINT16) return -EINVAL; dbus_message_iter_get_basic(&value, &qos->pd_min); } else if (strcasecmp(key, "MaximumDelay") == 0) { if (var != DBUS_TYPE_UINT16) return -EINVAL; dbus_message_iter_get_basic(&value, &qos->pd_max); } else if (strcasecmp(key, "PreferredMinimumDelay") == 0) { if (var != DBUS_TYPE_UINT16) return -EINVAL; dbus_message_iter_get_basic(&value, &qos->ppd_min); } else if (strcasecmp(key, "PreferredMaximumDelay") == 0) { if (var != DBUS_TYPE_UINT16) return -EINVAL; dbus_message_iter_get_basic(&value, &qos->ppd_max); } else if (strcasecmp(key, "Locations") == 0) { if (var != DBUS_TYPE_UINT32) return -EINVAL; dbus_message_iter_get_basic(&value, &qos->location); } else if (strcasecmp(key, "Context") == 0) { if (var != DBUS_TYPE_UINT16) return -EINVAL; dbus_message_iter_get_basic(&value, &qos->context); } else if (strcasecmp(key, "SupportedContext") == 0) { if (var != DBUS_TYPE_UINT16) return -EINVAL; dbus_message_iter_get_basic(&value, &qos->supported_context); } dbus_message_iter_next(props); } return (has_uuid && has_codec) ? 0 : -EINVAL; } static DBusMessage *register_endpoint(DBusConnection *conn, DBusMessage *msg, void *data) { struct media_adapter *adapter = data; DBusMessageIter args, props; const char *sender, *path, *uuid; gboolean delay_reporting = FALSE; uint8_t codec = 0; uint16_t cid = 0; uint16_t vid = 0; struct bt_bap_pac_qos qos = {}; uint8_t *capabilities = NULL; uint8_t *metadata = NULL; int size = 0; int metadata_size = 0; int err; sender = dbus_message_get_sender(msg); dbus_message_iter_init(msg, &args); dbus_message_iter_get_basic(&args, &path); dbus_message_iter_next(&args); if (media_adapter_find_endpoint(adapter, sender, path, NULL) != NULL) return btd_error_already_exists(msg); dbus_message_iter_recurse(&args, &props); if (dbus_message_iter_get_arg_type(&props) != DBUS_TYPE_DICT_ENTRY) return btd_error_invalid_args(msg); if (parse_properties(&props, &uuid, &delay_reporting, &codec, &cid, &vid, &qos, &capabilities, &size, &metadata, &metadata_size) < 0) return btd_error_invalid_args(msg); if (media_endpoint_create(adapter, sender, path, uuid, delay_reporting, codec, cid, vid, &qos, capabilities, size, metadata, metadata_size, &err) == NULL) { if (err == -EPROTONOSUPPORT) return btd_error_not_supported(msg); else return btd_error_invalid_args(msg); } return g_dbus_create_reply(msg, DBUS_TYPE_INVALID); } static DBusMessage *unregister_endpoint(DBusConnection *conn, DBusMessage *msg, void *data) { struct media_adapter *adapter = data; struct media_endpoint *endpoint; const char *sender, *path; if (!dbus_message_get_args(msg, NULL, DBUS_TYPE_OBJECT_PATH, &path, DBUS_TYPE_INVALID)) return btd_error_invalid_args(msg); sender = dbus_message_get_sender(msg); endpoint = media_adapter_find_endpoint(adapter, sender, path, NULL); if (endpoint == NULL) return btd_error_does_not_exist(msg); media_endpoint_remove(endpoint); return g_dbus_create_reply(msg, DBUS_TYPE_INVALID); } #ifdef HAVE_AVRCP static struct media_player *media_adapter_find_player( struct media_adapter *adapter, const char *sender, const char *path) { GSList *l; for (l = adapter->players; l; l = l->next) { struct media_player *mp = l->data; if (sender && g_strcmp0(mp->sender, sender) != 0) continue; if (path && g_strcmp0(mp->path, path) != 0) continue; return mp; } return NULL; } static void release_player(struct media_player *mp) { DBusMessage *msg; DBG("sender=%s path=%s", mp->sender, mp->path); msg = dbus_message_new_method_call(mp->sender, mp->path, MEDIA_PLAYER_INTERFACE, "Release"); if (msg == NULL) { error("Couldn't allocate D-Bus message"); return; } g_dbus_send_message(btd_get_dbus_connection(), msg); } static void media_player_free(gpointer data) { DBusConnection *conn = btd_get_dbus_connection(); struct media_player *mp = data; struct media_adapter *adapter = mp->adapter; if (mp->player) { adapter->players = g_slist_remove(adapter->players, mp); release_player(mp); } g_dbus_remove_watch(conn, mp->watch); g_dbus_remove_watch(conn, mp->properties_watch); g_dbus_remove_watch(conn, mp->seek_watch); if (mp->track) g_hash_table_unref(mp->track); if (mp->settings) g_hash_table_unref(mp->settings); g_timer_destroy(mp->timer); g_free(mp->sender); g_free(mp->path); g_free(mp->status); g_free(mp->name); g_free(mp); } static void media_player_destroy(struct media_player *mp) { struct media_adapter *adapter = mp->adapter; DBG("sender=%s path=%s", mp->sender, mp->path); if (mp->player) { struct avrcp_player *player = mp->player; mp->player = NULL; adapter->players = g_slist_remove(adapter->players, mp); avrcp_unregister_player(player); return; } media_player_free(mp); } static void media_player_remove(void *data) { struct media_player *mp = data; info("Player unregistered: sender=%s path=%s", mp->sender, mp->path); media_player_destroy(mp); } static GList *media_player_list_settings(void *user_data) { struct media_player *mp = user_data; DBG(""); if (mp->settings == NULL) return NULL; return g_hash_table_get_keys(mp->settings); } static const char *media_player_get_setting(const char *key, void *user_data) { struct media_player *mp = user_data; DBG("%s", key); return g_hash_table_lookup(mp->settings, key); } static const char *media_player_get_player_name(void *user_data) { struct media_player *mp = user_data; if (!mp->name) return "Player"; return mp->name; } static void set_shuffle_setting(DBusMessageIter *iter, const char *value) { const char *key = "Shuffle"; dbus_bool_t val; DBusMessageIter var; dbus_message_iter_append_basic(iter, DBUS_TYPE_STRING, &key); dbus_message_iter_open_container(iter, DBUS_TYPE_VARIANT, DBUS_TYPE_BOOLEAN_AS_STRING, &var); val = strcasecmp(value, "off") != 0; dbus_message_iter_append_basic(&var, DBUS_TYPE_BOOLEAN, &val); dbus_message_iter_close_container(iter, &var); } static const char *repeat_to_loop_status(const char *value) { if (strcasecmp(value, "off") == 0) return "None"; else if (strcasecmp(value, "singletrack") == 0) return "Track"; else if (strcasecmp(value, "alltracks") == 0) return "Playlist"; else if (strcasecmp(value, "group") == 0) return "Playlist"; return NULL; } static void set_repeat_setting(DBusMessageIter *iter, const char *value) { const char *key = "LoopStatus"; const char *val; DBusMessageIter var; dbus_message_iter_append_basic(iter, DBUS_TYPE_STRING, &key); dbus_message_iter_open_container(iter, DBUS_TYPE_VARIANT, DBUS_TYPE_STRING_AS_STRING, &var); val = repeat_to_loop_status(value); dbus_message_iter_append_basic(&var, DBUS_TYPE_STRING, &val); dbus_message_iter_close_container(iter, &var); } static int media_player_set_setting(const char *key, const char *value, void *user_data) { struct media_player *mp = user_data; const char *iface = MEDIA_PLAYER_INTERFACE; DBusMessage *msg; DBusMessageIter iter; const char *curval; DBG("%s = %s", key, value); curval = g_hash_table_lookup(mp->settings, key); if (!curval) return -EINVAL; if (strcasecmp(curval, value) == 0) return 0; msg = dbus_message_new_method_call(mp->sender, mp->path, DBUS_INTERFACE_PROPERTIES, "Set"); if (msg == NULL) { error("Couldn't allocate D-Bus message"); return -ENOMEM; } dbus_message_iter_init_append(msg, &iter); dbus_message_iter_append_basic(&iter, DBUS_TYPE_STRING, &iface); if (strcasecmp(key, "Shuffle") == 0) set_shuffle_setting(&iter, value); else if (strcasecmp(key, "Repeat") == 0) set_repeat_setting(&iter, value); g_dbus_send_message(btd_get_dbus_connection(), msg); return 0; } static GList *media_player_list_metadata(void *user_data) { struct media_player *mp = user_data; DBG(""); if (mp->track == NULL) return NULL; return g_hash_table_get_keys(mp->track); } static uint64_t media_player_get_uid(void *user_data) { struct media_player *mp = user_data; DBG("%p", mp->track); if (mp->track == NULL) return UINT64_MAX; return 0; } static const char *media_player_get_metadata(const char *key, void *user_data) { struct media_player *mp = user_data; DBG("%s", key); if (mp->track == NULL) return NULL; return g_hash_table_lookup(mp->track, key); } static const char *media_player_get_status(void *user_data) { struct media_player *mp = user_data; return mp->status; } static uint32_t media_player_get_position(void *user_data) { struct media_player *mp = user_data; double timedelta; uint32_t sec, msec; if (mp->status == NULL || strcasecmp(mp->status, "Playing") != 0) return mp->position; timedelta = g_timer_elapsed(mp->timer, NULL); sec = (uint32_t) timedelta; msec = (uint32_t) ((timedelta - sec) * 1000); return mp->position + sec * 1000 + msec; } static uint32_t media_player_get_duration(void *user_data) { struct media_player *mp = user_data; return mp->duration; } static void media_player_set_volume(int8_t volume, struct btd_device *dev, void *user_data) { struct media_player *mp = user_data; if (mp->volume == volume) return; mp->volume = volume; } static bool media_player_send(struct media_player *mp, const char *name) { DBusMessage *msg; msg = dbus_message_new_method_call(mp->sender, mp->path, MEDIA_PLAYER_INTERFACE, name); if (msg == NULL) { error("Couldn't allocate D-Bus message"); return false; } g_dbus_send_message(btd_get_dbus_connection(), msg); return true; } static bool media_player_play(void *user_data) { struct media_player *mp = user_data; DBG(""); if (!mp->play || !mp->control) return false; return media_player_send(mp, "Play"); } static bool media_player_stop(void *user_data) { struct media_player *mp = user_data; DBG(""); if (!mp->control) return false; return media_player_send(mp, "Stop"); } static bool media_player_pause(void *user_data) { struct media_player *mp = user_data; DBG(""); if (!mp->pause || !mp->control) return false; return media_player_send(mp, "Pause"); } static bool media_player_next(void *user_data) { struct media_player *mp = user_data; DBG(""); if (!mp->next || !mp->control) return false; return media_player_send(mp, "Next"); } static bool media_player_previous(void *user_data) { struct media_player *mp = user_data; DBG(""); if (!mp->previous || !mp->control) return false; return media_player_send(mp, "Previous"); } static struct avrcp_player_cb player_cb = { .list_settings = media_player_list_settings, .get_setting = media_player_get_setting, .set_setting = media_player_set_setting, .list_metadata = media_player_list_metadata, .get_uid = media_player_get_uid, .get_metadata = media_player_get_metadata, .get_position = media_player_get_position, .get_duration = media_player_get_duration, .get_status = media_player_get_status, .get_name = media_player_get_player_name, .set_volume = media_player_set_volume, .play = media_player_play, .stop = media_player_stop, .pause = media_player_pause, .next = media_player_next, .previous = media_player_previous, }; static void media_player_exit(DBusConnection *connection, void *user_data) { struct media_player *mp = user_data; mp->watch = 0; media_player_remove(mp); } static gboolean set_status(struct media_player *mp, DBusMessageIter *iter) { const char *value; if (dbus_message_iter_get_arg_type(iter) != DBUS_TYPE_STRING) return FALSE; dbus_message_iter_get_basic(iter, &value); DBG("Status=%s", value); if (g_strcmp0(mp->status, value) == 0) return TRUE; mp->position = media_player_get_position(mp); g_timer_start(mp->timer); g_free(mp->status); mp->status = g_strdup(value); avrcp_player_event(mp->player, AVRCP_EVENT_STATUS_CHANGED, mp->status); return TRUE; } static gboolean set_position(struct media_player *mp, DBusMessageIter *iter) { uint64_t value; const char *status; if (dbus_message_iter_get_arg_type(iter) != DBUS_TYPE_INT64) return FALSE; dbus_message_iter_get_basic(iter, &value); value /= 1000; if (value > media_player_get_position(mp)) status = "forward-seek"; else status = "reverse-seek"; mp->position = value; g_timer_start(mp->timer); DBG("Position=%u", mp->position); if (!mp->position) { avrcp_player_event(mp->player, AVRCP_EVENT_TRACK_REACHED_START, NULL); return TRUE; } /* * If position is the maximum value allowed or greater than track's * duration, we send a track-reached-end event. */ if (mp->position == UINT32_MAX || mp->position >= mp->duration) { avrcp_player_event(mp->player, AVRCP_EVENT_TRACK_REACHED_END, NULL); return TRUE; } /* Send a status change to force resync the position */ avrcp_player_event(mp->player, AVRCP_EVENT_STATUS_CHANGED, status); return TRUE; } static void set_metadata(struct media_player *mp, const char *key, const char *value) { DBG("%s=%s", key, value); g_hash_table_replace(mp->track, g_strdup(key), g_strdup(value)); } static gboolean parse_string_metadata(struct media_player *mp, const char *key, DBusMessageIter *iter) { const char *value; if (dbus_message_iter_get_arg_type(iter) != DBUS_TYPE_STRING) return FALSE; dbus_message_iter_get_basic(iter, &value); set_metadata(mp, key, value); return TRUE; } static gboolean parse_array_metadata(struct media_player *mp, const char *key, DBusMessageIter *iter) { DBusMessageIter array; const char *value; if (dbus_message_iter_get_arg_type(iter) != DBUS_TYPE_ARRAY) return FALSE; dbus_message_iter_recurse(iter, &array); if (dbus_message_iter_get_arg_type(&array) == DBUS_TYPE_INVALID) return TRUE; if (dbus_message_iter_get_arg_type(&array) != DBUS_TYPE_STRING) return FALSE; dbus_message_iter_get_basic(&array, &value); set_metadata(mp, key, value); return TRUE; } static gboolean parse_int64_metadata(struct media_player *mp, const char *key, DBusMessageIter *iter) { uint64_t value; char valstr[20]; int type; type = dbus_message_iter_get_arg_type(iter); if (type == DBUS_TYPE_UINT64) warn("expected DBUS_TYPE_INT64 got DBUS_TYPE_UINT64"); else if (type != DBUS_TYPE_INT64) return FALSE; dbus_message_iter_get_basic(iter, &value); if (strcasecmp(key, "Duration") == 0) { value /= 1000; mp->duration = value; } snprintf(valstr, 20, "%" PRIu64, value); set_metadata(mp, key, valstr); return TRUE; } static gboolean parse_int32_metadata(struct media_player *mp, const char *key, DBusMessageIter *iter) { uint32_t value; char valstr[20]; int type; type = dbus_message_iter_get_arg_type(iter); if (type == DBUS_TYPE_UINT32) warn("expected DBUS_TYPE_INT32 got DBUS_TYPE_UINT32"); else if (type != DBUS_TYPE_INT32) return FALSE; dbus_message_iter_get_basic(iter, &value); snprintf(valstr, 20, "%u", value); set_metadata(mp, key, valstr); return TRUE; } static gboolean parse_player_metadata(struct media_player *mp, DBusMessageIter *iter) { DBusMessageIter dict; DBusMessageIter var; int ctype; gboolean title = FALSE; uint64_t uid; ctype = dbus_message_iter_get_arg_type(iter); if (ctype != DBUS_TYPE_ARRAY) return FALSE; dbus_message_iter_recurse(iter, &dict); if (mp->track != NULL) g_hash_table_unref(mp->track); mp->track = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, g_free); while ((ctype = dbus_message_iter_get_arg_type(&dict)) != DBUS_TYPE_INVALID) { DBusMessageIter entry; const char *key; if (ctype != DBUS_TYPE_DICT_ENTRY) return FALSE; dbus_message_iter_recurse(&dict, &entry); if (dbus_message_iter_get_arg_type(&entry) != DBUS_TYPE_STRING) return FALSE; dbus_message_iter_get_basic(&entry, &key); dbus_message_iter_next(&entry); if (dbus_message_iter_get_arg_type(&entry) != DBUS_TYPE_VARIANT) return FALSE; dbus_message_iter_recurse(&entry, &var); if (strcasecmp(key, "xesam:title") == 0) { if (!parse_string_metadata(mp, "Title", &var)) return FALSE; title = TRUE; } else if (strcasecmp(key, "xesam:artist") == 0) { if (!parse_array_metadata(mp, "Artist", &var)) return FALSE; } else if (strcasecmp(key, "xesam:album") == 0) { if (!parse_string_metadata(mp, "Album", &var)) return FALSE; } else if (strcasecmp(key, "xesam:genre") == 0) { if (!parse_array_metadata(mp, "Genre", &var)) return FALSE; } else if (strcasecmp(key, "mpris:length") == 0) { if (!parse_int64_metadata(mp, "Duration", &var)) return FALSE; } else if (strcasecmp(key, "xesam:trackNumber") == 0) { if (!parse_int32_metadata(mp, "TrackNumber", &var)) return FALSE; } else DBG("%s not supported, ignoring", key); dbus_message_iter_next(&dict); } if (title == FALSE) g_hash_table_insert(mp->track, g_strdup("Title"), g_strdup("")); mp->position = 0; g_timer_start(mp->timer); uid = media_player_get_uid(mp); avrcp_player_event(mp->player, AVRCP_EVENT_TRACK_CHANGED, &uid); avrcp_player_event(mp->player, AVRCP_EVENT_TRACK_REACHED_START, NULL); return TRUE; } static gboolean set_property(struct media_player *mp, const char *key, const char *value) { const char *curval; curval = g_hash_table_lookup(mp->settings, key); if (g_strcmp0(curval, value) == 0) return TRUE; DBG("%s=%s", key, value); g_hash_table_replace(mp->settings, g_strdup(key), g_strdup(value)); avrcp_player_event(mp->player, AVRCP_EVENT_SETTINGS_CHANGED, key); return TRUE; } static gboolean set_shuffle(struct media_player *mp, DBusMessageIter *iter) { dbus_bool_t value; const char *strvalue; if (dbus_message_iter_get_arg_type(iter) != DBUS_TYPE_BOOLEAN) return FALSE; dbus_message_iter_get_basic(iter, &value); strvalue = value ? "alltracks" : "off"; return set_property(mp, "Shuffle", strvalue); } static const char *loop_status_to_repeat(const char *value) { if (strcasecmp(value, "None") == 0) return "off"; else if (strcasecmp(value, "Track") == 0) return "singletrack"; else if (strcasecmp(value, "Playlist") == 0) return "alltracks"; return NULL; } static gboolean set_repeat(struct media_player *mp, DBusMessageIter *iter) { const char *value; if (dbus_message_iter_get_arg_type(iter) != DBUS_TYPE_STRING) return FALSE; dbus_message_iter_get_basic(iter, &value); value = loop_status_to_repeat(value); if (value == NULL) return FALSE; return set_property(mp, "Repeat", value); } static gboolean set_flag(struct media_player *mp, DBusMessageIter *iter, bool *var) { dbus_bool_t value; if (dbus_message_iter_get_arg_type(iter) != DBUS_TYPE_BOOLEAN) return FALSE; dbus_message_iter_get_basic(iter, &value); *var = value; return TRUE; } static gboolean set_name(struct media_player *mp, DBusMessageIter *iter) { const char *value; if (dbus_message_iter_get_arg_type(iter) != DBUS_TYPE_STRING) return FALSE; dbus_message_iter_get_basic(iter, &value); if (g_strcmp0(mp->name, value) == 0) return TRUE; g_free(mp->name); mp->name = g_strdup(value); return TRUE; } static gboolean set_player_property(struct media_player *mp, const char *key, DBusMessageIter *entry) { DBusMessageIter var; if (dbus_message_iter_get_arg_type(entry) != DBUS_TYPE_VARIANT) return FALSE; dbus_message_iter_recurse(entry, &var); if (strcasecmp(key, "PlaybackStatus") == 0) return set_status(mp, &var); if (strcasecmp(key, "Position") == 0) return set_position(mp, &var); if (strcasecmp(key, "Metadata") == 0) return parse_player_metadata(mp, &var); if (strcasecmp(key, "Shuffle") == 0) return set_shuffle(mp, &var); if (strcasecmp(key, "LoopStatus") == 0) return set_repeat(mp, &var); if (strcasecmp(key, "CanPlay") == 0) return set_flag(mp, &var, &mp->play); if (strcasecmp(key, "CanPause") == 0) return set_flag(mp, &var, &mp->pause); if (strcasecmp(key, "CanGoNext") == 0) return set_flag(mp, &var, &mp->next); if (strcasecmp(key, "CanGoPrevious") == 0) return set_flag(mp, &var, &mp->previous); if (strcasecmp(key, "CanControl") == 0) return set_flag(mp, &var, &mp->control); if (strcasecmp(key, "Identity") == 0) return set_name(mp, &var); DBG("%s not supported, ignoring", key); return TRUE; } static gboolean parse_player_properties(struct media_player *mp, DBusMessageIter *iter) { DBusMessageIter dict; int ctype; ctype = dbus_message_iter_get_arg_type(iter); if (ctype != DBUS_TYPE_ARRAY) return FALSE; dbus_message_iter_recurse(iter, &dict); while ((ctype = dbus_message_iter_get_arg_type(&dict)) != DBUS_TYPE_INVALID) { DBusMessageIter entry; const char *key; if (ctype != DBUS_TYPE_DICT_ENTRY) return FALSE; dbus_message_iter_recurse(&dict, &entry); if (dbus_message_iter_get_arg_type(&entry) != DBUS_TYPE_STRING) return FALSE; dbus_message_iter_get_basic(&entry, &key); dbus_message_iter_next(&entry); if (set_player_property(mp, key, &entry) == FALSE) return FALSE; dbus_message_iter_next(&dict); } return TRUE; } static gboolean properties_changed(DBusConnection *connection, DBusMessage *msg, void *user_data) { struct media_player *mp = user_data; DBusMessageIter iter; DBG("sender=%s path=%s", mp->sender, mp->path); dbus_message_iter_init(msg, &iter); dbus_message_iter_next(&iter); parse_player_properties(mp, &iter); return TRUE; } static gboolean position_changed(DBusConnection *connection, DBusMessage *msg, void *user_data) { struct media_player *mp = user_data; DBusMessageIter iter; DBG("sender=%s path=%s", mp->sender, mp->path); dbus_message_iter_init(msg, &iter); set_position(mp, &iter); return TRUE; } static struct media_player *media_player_create(struct media_adapter *adapter, const char *sender, const char *path, int *err) { DBusConnection *conn = btd_get_dbus_connection(); struct media_player *mp; mp = g_new0(struct media_player, 1); mp->adapter = adapter; mp->sender = g_strdup(sender); mp->path = g_strdup(path); mp->timer = g_timer_new(); mp->volume = -1; mp->watch = g_dbus_add_disconnect_watch(conn, sender, media_player_exit, mp, NULL); mp->properties_watch = g_dbus_add_properties_watch(conn, sender, path, MEDIA_PLAYER_INTERFACE, properties_changed, mp, NULL); mp->seek_watch = g_dbus_add_signal_watch(conn, sender, path, MEDIA_PLAYER_INTERFACE, "Seeked", position_changed, mp, NULL); mp->player = avrcp_register_player(adapter->btd_adapter, &player_cb, mp, media_player_free); if (!mp->player) { if (err) *err = -EPROTONOSUPPORT; media_player_destroy(mp); return NULL; } mp->settings = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, g_free); adapter->players = g_slist_append(adapter->players, mp); info("Player registered: sender=%s path=%s", sender, path); if (err) *err = 0; return mp; } #endif /* HAVE_AVRCP */ static DBusMessage *register_player(DBusConnection *conn, DBusMessage *msg, void *data) { #ifdef HAVE_AVRCP struct media_adapter *adapter = data; struct media_player *mp; DBusMessageIter args; const char *sender, *path; int err; sender = dbus_message_get_sender(msg); dbus_message_iter_init(msg, &args); dbus_message_iter_get_basic(&args, &path); dbus_message_iter_next(&args); if (media_adapter_find_player(adapter, sender, path) != NULL) return btd_error_already_exists(msg); mp = media_player_create(adapter, sender, path, &err); if (mp == NULL) { if (err == -EPROTONOSUPPORT) return btd_error_not_supported(msg); else return btd_error_invalid_args(msg); } if (parse_player_properties(mp, &args) == FALSE) { media_player_destroy(mp); return btd_error_invalid_args(msg); } return g_dbus_create_reply(msg, DBUS_TYPE_INVALID); #else return btd_error_not_supported(msg); #endif } static DBusMessage *unregister_player(DBusConnection *conn, DBusMessage *msg, void *data) { #ifdef HAVE_AVRCP struct media_adapter *adapter = data; struct media_player *player; const char *sender, *path; if (!dbus_message_get_args(msg, NULL, DBUS_TYPE_OBJECT_PATH, &path, DBUS_TYPE_INVALID)) return btd_error_invalid_args(msg); sender = dbus_message_get_sender(msg); player = media_adapter_find_player(adapter, sender, path); if (player == NULL) return btd_error_does_not_exist(msg); media_player_remove(player); return g_dbus_create_reply(msg, DBUS_TYPE_INVALID); #else return btd_error_not_supported(msg); #endif } static void app_free(void *data) { struct media_app *app = data; queue_destroy(app->proxies, NULL); queue_destroy(app->endpoints, media_endpoint_remove); #ifdef HAVE_AVRCP queue_destroy(app->players, media_player_remove); #endif if (app->client) { g_dbus_client_set_disconnect_watch(app->client, NULL, NULL); g_dbus_client_set_proxy_handlers(app->client, NULL, NULL, NULL, NULL); g_dbus_client_set_ready_watch(app->client, NULL, NULL); g_dbus_client_unref(app->client); } if (app->reg) dbus_message_unref(app->reg); g_free(app->sender); g_free(app->path); free(app); } static void client_disconnect_cb(DBusConnection *conn, void *user_data) { struct media_app *app = user_data; struct media_adapter *adapter = app->adapter; DBG("Client disconnected"); if (queue_remove(adapter->apps, app)) app_free(app); } static void app_register_endpoint(void *data, void *user_data) { struct media_app *app = user_data; GDBusProxy *proxy = data; const char *iface = g_dbus_proxy_get_interface(proxy); const char *path = g_dbus_proxy_get_path(proxy); const char *uuid; gboolean delay_reporting = FALSE; uint8_t codec; struct vendor vendor; struct bt_bap_pac_qos qos; uint8_t *capabilities = NULL; int size = 0; uint8_t *metadata = NULL; int metadata_size = 0; DBusMessageIter iter, array; struct media_endpoint *endpoint; if (app->err) return; if (strcmp(iface, MEDIA_ENDPOINT_INTERFACE)) return; /* Parse properties */ if (!g_dbus_proxy_get_property(proxy, "UUID", &iter)) goto fail; if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_STRING) goto fail; dbus_message_iter_get_basic(&iter, &uuid); if (!g_dbus_proxy_get_property(proxy, "Codec", &iter)) goto fail; if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_BYTE) goto fail; dbus_message_iter_get_basic(&iter, &codec); memset(&vendor, 0, sizeof(vendor)); if (g_dbus_proxy_get_property(proxy, "Vendor", &iter)) { if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_UINT32) goto fail; dbus_message_iter_get_basic(&iter, &vendor); } /* DelayReporting and Capabilities are considered optional */ if (g_dbus_proxy_get_property(proxy, "DelayReporting", &iter)) { if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_BOOLEAN) goto fail; dbus_message_iter_get_basic(&iter, &delay_reporting); } if (g_dbus_proxy_get_property(proxy, "Capabilities", &iter)) { if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_ARRAY) goto fail; dbus_message_iter_recurse(&iter, &array); dbus_message_iter_get_fixed_array(&array, &capabilities, &size); } if (g_dbus_proxy_get_property(proxy, "Metadata", &iter)) { if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_ARRAY) goto fail; dbus_message_iter_recurse(&iter, &array); dbus_message_iter_get_fixed_array(&array, &metadata, &metadata_size); } /* Parse QoS preferences */ memset(&qos, 0, sizeof(qos)); if (g_dbus_proxy_get_property(proxy, "Framing", &iter)) { if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_BYTE) goto fail; dbus_message_iter_get_basic(&iter, &qos.framing); } if (g_dbus_proxy_get_property(proxy, "PHY", &iter)) { if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_BYTE) goto fail; dbus_message_iter_get_basic(&iter, &qos.phy); } if (g_dbus_proxy_get_property(proxy, "MaximumLatency", &iter)) { if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_UINT16) goto fail; dbus_message_iter_get_basic(&iter, &qos.latency); } if (g_dbus_proxy_get_property(proxy, "MinimumDelay", &iter)) { if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_UINT32) goto fail; dbus_message_iter_get_basic(&iter, &qos.pd_min); } if (g_dbus_proxy_get_property(proxy, "MaximumDelay", &iter)) { if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_UINT32) goto fail; dbus_message_iter_get_basic(&iter, &qos.pd_max); } if (g_dbus_proxy_get_property(proxy, "PreferredMinimumDelay", &iter)) { if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_UINT32) goto fail; dbus_message_iter_get_basic(&iter, &qos.ppd_min); } if (g_dbus_proxy_get_property(proxy, "PreferredMaximumDelay", &iter)) { if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_UINT32) goto fail; dbus_message_iter_get_basic(&iter, &qos.ppd_min); } if (g_dbus_proxy_get_property(proxy, "Locations", &iter)) { if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_UINT32) goto fail; dbus_message_iter_get_basic(&iter, &qos.location); } if (g_dbus_proxy_get_property(proxy, "Context", &iter)) { if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_UINT16) goto fail; dbus_message_iter_get_basic(&iter, &qos.context); } if (g_dbus_proxy_get_property(proxy, "SupportedContext", &iter)) { if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_UINT16) goto fail; dbus_message_iter_get_basic(&iter, &qos.supported_context); } endpoint = media_endpoint_create(app->adapter, app->sender, path, uuid, delay_reporting, codec, vendor.cid, vendor.vid, &qos, capabilities, size, metadata, metadata_size, &app->err); if (!endpoint) { error("Unable to register endpoint %s:%s: %s", app->sender, path, strerror(-app->err)); return; } queue_push_tail(app->endpoints, endpoint); return; fail: app->err = -EINVAL; } static void app_register_player(void *data, void *user_data) { #ifdef HAVE_AVRCP struct media_app *app = user_data; GDBusProxy *proxy = data; const char *iface = g_dbus_proxy_get_interface(proxy); const char *path = g_dbus_proxy_get_path(proxy); struct media_player *player; DBusMessageIter iter; if (app->err) return; if (strcmp(iface, MEDIA_PLAYER_INTERFACE)) return; player = media_player_create(app->adapter, app->sender, path, &app->err); if (!player) return; if (g_dbus_proxy_get_property(proxy, "PlaybackStatus", &iter)) { if (!set_status(player, &iter)) goto fail; } if (g_dbus_proxy_get_property(proxy, "Position", &iter)) { if (!set_position(player, &iter)) goto fail; } if (g_dbus_proxy_get_property(proxy, "Metadata", &iter)) { if (!parse_player_metadata(player, &iter)) goto fail; } if (g_dbus_proxy_get_property(proxy, "Shuffle", &iter)) { if (!set_shuffle(player, &iter)) goto fail; } if (g_dbus_proxy_get_property(proxy, "LoopStatus", &iter)) { if (!set_repeat(player, &iter)) goto fail; } if (g_dbus_proxy_get_property(proxy, "CanPlay", &iter)) { if (!set_flag(player, &iter, &player->play)) goto fail; } if (g_dbus_proxy_get_property(proxy, "CanPause", &iter)) { if (!set_flag(player, &iter, &player->pause)) goto fail; } if (g_dbus_proxy_get_property(proxy, "CanGoNext", &iter)) { if (!set_flag(player, &iter, &player->next)) goto fail; } if (g_dbus_proxy_get_property(proxy, "CanGoPrevious", &iter)) { if (!set_flag(player, &iter, &player->previous)) goto fail; } if (g_dbus_proxy_get_property(proxy, "CanControl", &iter)) { if (!set_flag(player, &iter, &player->control)) goto fail; } if (g_dbus_proxy_get_property(proxy, "Identity", &iter)) { if (!set_name(player, &iter)) goto fail; } queue_push_tail(app->players, player); return; fail: app->err = -EINVAL; error("Unable to register player %s:%s: %s", app->sender, path, strerror(-app->err)); media_player_destroy(player); #endif /* HAVE_AVRCP */ } static void remove_app(void *data) { struct media_app *app = data; /* * Set callback to NULL to avoid potential race condition * when calling remove_app and GDBusClient unref. */ g_dbus_client_set_disconnect_watch(app->client, NULL, NULL); /* * Set proxy handlers to NULL, so that this gets called only once when * the first proxy that belongs to this service gets removed. */ g_dbus_client_set_proxy_handlers(app->client, NULL, NULL, NULL, NULL); queue_remove(app->adapter->apps, app); app_free(app); } static void client_ready_cb(GDBusClient *client, void *user_data) { struct media_app *app = user_data; DBusMessage *reply; bool fail = false; /* * Process received objects */ if (queue_isempty(app->proxies)) { error("No object received"); fail = true; reply = btd_error_failed(app->reg, "No object received"); goto reply; } if (app->err) { if (app->err == -EPROTONOSUPPORT) reply = btd_error_not_supported(app->reg); else reply = btd_error_invalid_args(app->reg); goto reply; } #ifdef HAVE_AVRCP if ((queue_isempty(app->endpoints) && queue_isempty(app->players))) { #else if (queue_isempty(app->endpoints)) { #endif error("No valid external Media objects found"); fail = true; reply = btd_error_failed(app->reg, "No valid media object found"); goto reply; } DBG("Media application registered: %s:%s", app->sender, app->path); reply = dbus_message_new_method_return(app->reg); reply: g_dbus_send_message(btd_get_dbus_connection(), reply); dbus_message_unref(app->reg); app->reg = NULL; if (fail) remove_app(app); } static void proxy_added_cb(GDBusProxy *proxy, void *user_data) { struct media_app *app = user_data; const char *iface, *path; if (app->err) return; queue_push_tail(app->proxies, proxy); iface = g_dbus_proxy_get_interface(proxy); path = g_dbus_proxy_get_path(proxy); DBG("Proxy added: %s, iface: %s", path, iface); app_register_endpoint(proxy, app); app_register_player(proxy, app); } static bool match_endpoint_by_path(const void *a, const void *b) { const struct media_endpoint *endpoint = a; const char *path = b; return !strcmp(endpoint->path, path); } #ifdef HAVE_AVRCP static bool match_player_by_path(const void *a, const void *b) { const struct media_player *player = a; const char *path = b; return !strcmp(player->path, path); } #endif static void proxy_removed_cb(GDBusProxy *proxy, void *user_data) { struct media_app *app = user_data; struct media_endpoint *endpoint; const char *iface, *path; iface = g_dbus_proxy_get_interface(proxy); path = g_dbus_proxy_get_path(proxy); if (!strcmp(iface, MEDIA_ENDPOINT_INTERFACE)) { endpoint = queue_remove_if(app->endpoints, match_endpoint_by_path, (void *) path); if (!endpoint) return; if (!g_slist_find(app->adapter->endpoints, endpoint)) return; DBG("Proxy removed - removing endpoint: %s", endpoint->path); media_endpoint_remove(endpoint); #ifdef HAVE_AVRCP } else if (!strcmp(iface, MEDIA_PLAYER_INTERFACE)) { struct media_player *player; player = queue_remove_if(app->players, match_player_by_path, (void *) path); if (!player) return; if (!g_slist_find(app->adapter->players, player)) return; DBG("Proxy removed - removing player: %s", player->path); media_player_remove(player); #endif } } static struct media_app *create_app(DBusConnection *conn, DBusMessage *msg, const char *path) { struct media_app *app; const char *sender = dbus_message_get_sender(msg); if (!path || !g_str_has_prefix(path, "/")) return NULL; app = new0(struct media_app, 1); app->client = g_dbus_client_new_full(conn, sender, path, path); if (!app->client) goto fail; app->sender = g_strdup(sender); if (!app->sender) goto fail; app->path = g_strdup(path); if (!app->path) goto fail; app->proxies = queue_new(); app->endpoints = queue_new(); #ifdef HAVE_AVRCP app->players = queue_new(); #endif app->reg = dbus_message_ref(msg); g_dbus_client_set_disconnect_watch(app->client, client_disconnect_cb, app); g_dbus_client_set_proxy_handlers(app->client, proxy_added_cb, proxy_removed_cb, NULL, app); g_dbus_client_set_ready_watch(app->client, client_ready_cb, app); return app; fail: app_free(app); return NULL; } struct match_data { const char *path; const char *sender; }; static bool match_app(const void *a, const void *b) { const struct media_app *app = a; const struct match_data *data = b; return g_strcmp0(app->path, data->path) == 0 && g_strcmp0(app->sender, data->sender) == 0; } static DBusMessage *register_app(DBusConnection *conn, DBusMessage *msg, void *user_data) { struct media_adapter *adapter = user_data; const char *sender = dbus_message_get_sender(msg); DBusMessageIter args; const char *path; struct media_app *app; struct match_data match_data; if (!dbus_message_iter_init(msg, &args)) return btd_error_invalid_args(msg); if (dbus_message_iter_get_arg_type(&args) != DBUS_TYPE_OBJECT_PATH) return btd_error_invalid_args(msg); dbus_message_iter_get_basic(&args, &path); match_data.path = path; match_data.sender = sender; if (queue_find(adapter->apps, match_app, &match_data)) return btd_error_already_exists(msg); dbus_message_iter_next(&args); if (dbus_message_iter_get_arg_type(&args) != DBUS_TYPE_ARRAY) return btd_error_invalid_args(msg); app = create_app(conn, msg, path); if (!app) return btd_error_failed(msg, "Failed to register application"); DBG("Registering application: %s:%s", sender, path); app->adapter = adapter; queue_push_tail(adapter->apps, app); return NULL; } static DBusMessage *unregister_app(DBusConnection *conn, DBusMessage *msg, void *user_data) { struct media_adapter *adapter = user_data; const char *sender = dbus_message_get_sender(msg); const char *path; DBusMessageIter args; struct media_app *app; struct match_data match_data; if (!dbus_message_iter_init(msg, &args)) return btd_error_invalid_args(msg); if (dbus_message_iter_get_arg_type(&args) != DBUS_TYPE_OBJECT_PATH) return btd_error_invalid_args(msg); dbus_message_iter_get_basic(&args, &path); match_data.path = path; match_data.sender = sender; app = queue_remove_if(adapter->apps, match_app, &match_data); if (!app) return btd_error_does_not_exist(msg); app_free(app); return dbus_message_new_method_return(msg); } static const GDBusMethodTable media_methods[] = { { GDBUS_METHOD("RegisterEndpoint", GDBUS_ARGS({ "endpoint", "o" }, { "properties", "a{sv}" }), NULL, register_endpoint) }, { GDBUS_METHOD("UnregisterEndpoint", GDBUS_ARGS({ "endpoint", "o" }), NULL, unregister_endpoint) }, { GDBUS_METHOD("RegisterPlayer", GDBUS_ARGS({ "player", "o" }, { "properties", "a{sv}" }), NULL, register_player) }, { GDBUS_METHOD("UnregisterPlayer", GDBUS_ARGS({ "player", "o" }), NULL, unregister_player) }, { GDBUS_ASYNC_METHOD("RegisterApplication", GDBUS_ARGS({ "application", "o" }, { "options", "a{sv}" }), NULL, register_app) }, { GDBUS_ASYNC_METHOD("UnregisterApplication", GDBUS_ARGS({ "application", "o" }), NULL, unregister_app) }, { }, }; static gboolean supported_uuids(const GDBusPropertyTable *property, DBusMessageIter *iter, void *data) { struct media_adapter *adapter = data; DBusMessageIter entry; size_t i; dbus_message_iter_open_container(iter, DBUS_TYPE_ARRAY, DBUS_TYPE_STRING_AS_STRING, &entry); for (i = 0; i < ARRAY_SIZE(init_table); i++) { const struct media_endpoint_init *init = &init_table[i]; if (init->supported(adapter->btd_adapter)) dbus_message_iter_append_basic(&entry, DBUS_TYPE_STRING, &init->uuid); } dbus_message_iter_close_container(iter, &entry); return TRUE; } static const GDBusPropertyTable media_properties[] = { { "SupportedUUIDs", "as", supported_uuids }, { } }; static void path_free(void *data) { struct media_adapter *adapter = data; GSList *l; queue_destroy(adapter->apps, app_free); for (l = adapter->endpoints; l;) { struct media_endpoint *endpoint = l->data; l = g_slist_next(l); release_endpoint(endpoint); } #ifdef HAVE_AVRCP for (l = adapter->players; l;) { struct media_player *mp = l->data; l = g_slist_next(l); media_player_destroy(mp); } #endif adapters = g_slist_remove(adapters, adapter); btd_adapter_unref(adapter->btd_adapter); g_free(adapter); } int media_register(struct btd_adapter *btd_adapter) { struct media_adapter *adapter; adapter = g_new0(struct media_adapter, 1); adapter->btd_adapter = btd_adapter_ref(btd_adapter); adapter->apps = queue_new(); if (!g_dbus_register_interface(btd_get_dbus_connection(), adapter_get_path(btd_adapter), MEDIA_INTERFACE, media_methods, NULL, media_properties, adapter, path_free)) { error("D-Bus failed to register %s path", adapter_get_path(btd_adapter)); path_free(adapter); return -1; } adapters = g_slist_append(adapters, adapter); return 0; } void media_unregister(struct btd_adapter *btd_adapter) { GSList *l; for (l = adapters; l; l = l->next) { struct media_adapter *adapter = l->data; if (adapter->btd_adapter == btd_adapter) { g_dbus_unregister_interface(btd_get_dbus_connection(), adapter_get_path(btd_adapter), MEDIA_INTERFACE); return; } } } struct a2dp_sep *media_endpoint_get_sep(struct media_endpoint *endpoint) { return endpoint->sep; } const char *media_endpoint_get_uuid(struct media_endpoint *endpoint) { return endpoint->uuid; } uint8_t media_endpoint_get_codec(struct media_endpoint *endpoint) { return endpoint->codec; } struct btd_adapter *media_endpoint_get_btd_adapter( struct media_endpoint *endpoint) { return endpoint->adapter->btd_adapter; } bool media_endpoint_is_broadcast(struct media_endpoint *endpoint) { if (!strcmp(endpoint->uuid, BCAA_SERVICE_UUID) || !strcmp(endpoint->uuid, BAA_SERVICE_UUID)) return true; return false; } const struct media_endpoint *media_endpoint_get_asha(void) { /* * Because ASHA does not require the application to register an * endpoint, we need a minimal media_endpoint for transport creation to * work, so let's create one */ static struct media_endpoint asha_endpoint = { .uuid = ASHA_PROFILE_UUID, .codec = 0x2, /* Currently on G.722 is defined by the spec */ }; return &asha_endpoint; } bluez-5.82/profiles/audio/PaxHeaders/micp.c0000644000000000000000000000005014766002272015674 xustar0020 atime=1743515578 20 ctime=1743591284 bluez-5.82/profiles/audio/micp.c0000644000000000000000000001540414766002272015361 0ustar00rootroot// SPDX-License-Identifier: GPL-2.0-or-later /* * * * BlueZ - Bluetooth protocol stack for Linux * * Copyright (C) 2023 NXP Semiconductors. All rights reserved. * * */ #ifdef HAVE_CONFIG_H #include #endif #define _GNU_SOURCE #include #include #include #include #include #include #include #include #include #include "gdbus/gdbus.h" #include "lib/bluetooth.h" #include "lib/hci.h" #include "lib/sdp.h" #include "lib/uuid.h" #include "src/dbus-common.h" #include "src/shared/util.h" #include "src/shared/att.h" #include "src/shared/queue.h" #include "src/shared/gatt-db.h" #include "src/shared/gatt-server.h" #include "src/shared/micp.h" #include "btio/btio.h" #include "src/plugin.h" #include "src/adapter.h" #include "src/gatt-database.h" #include "src/device.h" #include "src/profile.h" #include "src/service.h" #include "src/log.h" #include "src/error.h" #define MICS_UUID_STR "0000184D-0000-1000-8000-00805f9b34fb" struct micp_data { struct btd_device *device; struct btd_service *service; struct bt_micp *micp; unsigned int ready_id; }; static struct queue *sessions; static void micp_debug(const char *str, void *user_data) { DBG_IDX(0xffff, "%s", str); } static int micp_disconnect(struct btd_service *service) { return 0; } static struct micp_data *micp_data_new(struct btd_device *device) { struct micp_data *data; data = new0(struct micp_data, 1); g_assert(data); data->device = device; return data; } static void micp_data_add(struct micp_data *data) { DBG("data %p", data); if (queue_find(sessions, NULL, data)) { error("data %p allready added", data); return; } bt_micp_set_debug(data->micp, micp_debug, NULL, NULL); if (!sessions) sessions = queue_new(); queue_push_tail(sessions, data); if (data->service) btd_service_set_user_data(data->service, data); } static bool match_data(const void *data, const void *match_data) { const struct micp_data *mdata = data; const struct bt_micp *micp = match_data; return mdata->micp == micp; } static void micp_data_free(struct micp_data *data) { if (data->service) { btd_service_set_user_data(data->service, NULL); bt_micp_set_user_data(data->micp, NULL); } bt_micp_ready_unregister(data->micp, data->ready_id); bt_micp_unref(data->micp); free(data); } static void micp_data_remove(struct micp_data *data) { DBG("data %p", data); if (!queue_remove(sessions, data)) return; micp_data_free(data); if (queue_isempty(sessions)) { queue_destroy(sessions, NULL); sessions = NULL; } } static void micp_detached(struct bt_micp *micp, void *user_data) { struct micp_data *data; DBG("%p", micp); data = queue_find(sessions, match_data, micp); if (!data) { error("unable to find sessio"); return; } micp_data_remove(data); } static void micp_ready(struct bt_micp *micp, void *user_data) { DBG("micp %p\n", micp); } static void micp_attached(struct bt_micp *micp, void *user_data) { struct micp_data *data; struct bt_att *att; struct btd_device *device; DBG("%p", micp); data = queue_find(sessions, match_data, micp); if (data) return; att = bt_micp_get_att(micp); if (!att) return; device = btd_adapter_find_device_by_fd(bt_att_get_fd(att)); if (!device) { error("unable to find device"); return; } data = micp_data_new(device); g_assert(data); data->micp = micp; micp_data_add(data); } static int micp_probe(struct btd_service *service) { struct btd_device *device = btd_service_get_device(service); struct btd_adapter *adapter = device_get_adapter(device); struct btd_gatt_database *database = btd_adapter_get_database(adapter); struct micp_data *data = btd_service_get_user_data(service); char addr[18]; ba2str(device_get_address(device), addr); DBG("%s", addr); /*Ignore, if we probed for this device allready */ if (data) { error("Profile probed twice for this device"); return -EINVAL; } data = micp_data_new(device); data->service = service; data->micp = bt_micp_new(btd_gatt_database_get_db(database), btd_device_get_gatt_db(device)); if (!data->micp) { error("unable to create MICP instance"); free(data); return -EINVAL; } micp_data_add(data); data->ready_id = bt_micp_ready_register(data->micp, micp_ready, service, NULL); bt_micp_set_user_data(data->micp, service); return 0; } static void micp_remove(struct btd_service *service) { struct btd_device *device = btd_service_get_device(service); struct micp_data *data; char addr[18]; ba2str(device_get_address(device), addr); DBG("%s", addr); data = btd_service_get_user_data(service); if (!data) { error("MICP Service not handled by profile"); return; } micp_data_remove(data); } static int micp_accept(struct btd_service *service) { struct btd_device *device = btd_service_get_device(service); struct bt_gatt_client *client = btd_device_get_gatt_client(device); struct micp_data *data = btd_service_get_user_data(service); char addr[18]; ba2str(device_get_address(device), addr); DBG("%s", addr); if (!data) { error("MICP Service not handled by profile"); return -EINVAL; } if (!bt_micp_attach(data->micp, client)) { error("MICP unable to attach"); return -EINVAL; } btd_service_connecting_complete(service, 0); return 0; } static int micp_connect(struct btd_service *service) { struct btd_device *device = btd_service_get_device(service); char addr[18]; ba2str(device_get_address(device), addr); DBG("%s", addr); return 0; } static int micp_server_probe(struct btd_profile *p, struct btd_adapter *adapter) { struct btd_gatt_database *database = btd_adapter_get_database(adapter); DBG("MICP path %s", adapter_get_path(adapter)); bt_micp_add_db(btd_gatt_database_get_db(database)); return 0; } static void micp_server_remove(struct btd_profile *p, struct btd_adapter *adapter) { DBG("MICP remove adapter"); } static struct btd_profile micp_profile = { .name = "micp", .priority = BTD_PROFILE_PRIORITY_MEDIUM, .remote_uuid = MICS_UUID_STR, .device_probe = micp_probe, .device_remove = micp_remove, .accept = micp_accept, .connect = micp_connect, .disconnect = micp_disconnect, .adapter_probe = micp_server_probe, .adapter_remove = micp_server_remove, }; static unsigned int micp_id; static int micp_init(void) { if (!(g_dbus_get_flags() & G_DBUS_FLAG_ENABLE_EXPERIMENTAL)) { DBG("D-Bus experimental not enabled"); return -ENOTSUP; } btd_profile_register(&micp_profile); micp_id = bt_micp_register(micp_attached, micp_detached, NULL); return 0; } static void micp_exit(void) { if (g_dbus_get_flags() & G_DBUS_FLAG_ENABLE_EXPERIMENTAL) { btd_profile_unregister(&micp_profile); bt_micp_unregister(micp_id); } } BLUETOOTH_PLUGIN_DEFINE(micp, VERSION, BLUETOOTH_PLUGIN_PRIORITY_DEFAULT, micp_init, micp_exit) bluez-5.82/profiles/audio/PaxHeaders/source.c0000644000000000000000000000005014766002272016244 xustar0020 atime=1743515578 20 ctime=1743591284 bluez-5.82/profiles/audio/source.c0000644000000000000000000002232014766002272015724 0ustar00rootroot// SPDX-License-Identifier: GPL-2.0-or-later /* * * BlueZ - Bluetooth protocol stack for Linux * * Copyright (C) 2006-2010 Nokia Corporation * Copyright (C) 2004-2010 Marcel Holtmann * Copyright (C) 2009 Joao Paulo Rechi Vita * * */ #ifdef HAVE_CONFIG_H #include #endif #include #include #include #include #include #include "lib/bluetooth.h" #include "lib/sdp.h" #include "gdbus/gdbus.h" #include "src/log.h" #include "src/adapter.h" #include "src/device.h" #include "src/service.h" #include "src/error.h" #include "src/dbus-common.h" #include "src/shared/queue.h" #include "avdtp.h" #include "media.h" #include "a2dp.h" #include "source.h" struct source { struct btd_service *service; struct avdtp *session; struct avdtp_stream *stream; unsigned int cb_id; avdtp_session_state_t session_state; avdtp_state_t stream_state; source_state_t state; unsigned int connect_id; unsigned int disconnect_id; unsigned int avdtp_callback_id; }; struct source_state_callback { source_state_cb cb; struct btd_service *service; void *user_data; unsigned int id; }; static GSList *source_callbacks = NULL; static const char *str_state[] = { "SOURCE_STATE_DISCONNECTED", "SOURCE_STATE_CONNECTING", "SOURCE_STATE_CONNECTED", "SOURCE_STATE_PLAYING", }; static void source_set_state(struct source *source, source_state_t new_state) { struct btd_device *dev = btd_service_get_device(source->service); source_state_t old_state = source->state; GSList *l; source->state = new_state; DBG("State changed %s: %s -> %s", device_get_path(dev), str_state[old_state], str_state[new_state]); for (l = source_callbacks; l != NULL; l = l->next) { struct source_state_callback *cb = l->data; if (cb->service != source->service) continue; cb->cb(source->service, old_state, new_state, cb->user_data); } if (new_state != SOURCE_STATE_DISCONNECTED) return; if (source->session) { avdtp_unref(source->session); source->session = NULL; } } static void avdtp_state_callback(struct btd_device *dev, struct avdtp *session, avdtp_session_state_t old_state, avdtp_session_state_t new_state, void *user_data) { struct source *source = user_data; switch (new_state) { case AVDTP_SESSION_STATE_DISCONNECTED: source_set_state(source, SOURCE_STATE_DISCONNECTED); btd_service_disconnecting_complete(source->service, 0); break; case AVDTP_SESSION_STATE_CONNECTING: source_set_state(source, SOURCE_STATE_CONNECTING); break; case AVDTP_SESSION_STATE_CONNECTED: break; } source->session_state = new_state; } static void stream_state_changed(struct avdtp_stream *stream, avdtp_state_t old_state, avdtp_state_t new_state, struct avdtp_error *err, void *user_data) { struct btd_service *service = user_data; struct source *source = btd_service_get_user_data(service); if (err) return; switch (new_state) { case AVDTP_STATE_IDLE: if (source->connect_id > 0) { a2dp_cancel(source->connect_id); source->connect_id = 0; } if (source->disconnect_id > 0) { a2dp_cancel(source->disconnect_id); source->disconnect_id = 0; } if (source->session) { avdtp_unref(source->session); source->session = NULL; } source->stream = NULL; source->cb_id = 0; break; case AVDTP_STATE_OPEN: btd_service_connecting_complete(source->service, 0); source_set_state(source, SOURCE_STATE_CONNECTED); break; case AVDTP_STATE_STREAMING: source_set_state(source, SOURCE_STATE_PLAYING); break; case AVDTP_STATE_CONFIGURED: case AVDTP_STATE_CLOSING: case AVDTP_STATE_ABORTING: default: break; } source->stream_state = new_state; } static void stream_setup_complete(struct avdtp *session, struct a2dp_sep *sep, struct avdtp_stream *stream, int err, void *user_data) { struct source *source = user_data; source->connect_id = 0; if (stream) return; avdtp_unref(source->session); source->session = NULL; btd_service_connecting_complete(source->service, err); } static void select_complete(struct avdtp *session, struct a2dp_sep *sep, GSList *caps, int err, void *user_data) { struct source *source = user_data; int id; source->connect_id = 0; if (err) goto failed; if (caps == NULL) goto failed; id = a2dp_config(session, sep, stream_setup_complete, caps, source); if (id == 0) goto failed; source->connect_id = id; return; failed: btd_service_connecting_complete(source->service, -EIO); avdtp_unref(source->session); source->session = NULL; } static void discovery_complete(struct avdtp *session, GSList *seps, int err, void *user_data) { struct source *source = user_data; int id; source->connect_id = 0; if (err) { avdtp_unref(source->session); source->session = NULL; goto failed; } DBG("Discovery complete"); id = a2dp_select_capabilities(source->session, AVDTP_SEP_TYPE_SOURCE, NULL, select_complete, source); if (id == 0) { err = -EIO; goto failed; } source->connect_id = id; return; failed: btd_service_connecting_complete(source->service, err); avdtp_unref(source->session); source->session = NULL; } gboolean source_setup_stream(struct btd_service *service, struct avdtp *session) { struct source *source = btd_service_get_user_data(service); if (source->connect_id > 0 || source->disconnect_id > 0) return FALSE; if (!source->session) { if (session) source->session = avdtp_ref(session); else source->session = a2dp_avdtp_get( btd_service_get_device(service)); if (!source->session) { DBG("Unable to get a session"); return FALSE; } } source->connect_id = a2dp_discover(source->session, discovery_complete, source); if (source->connect_id == 0) { avdtp_unref(source->session); source->session = NULL; return FALSE; } return TRUE; } int source_connect(struct btd_service *service) { struct source *source = btd_service_get_user_data(service); if (source->connect_id > 0 || source->disconnect_id > 0) return -EBUSY; if (source->state == SOURCE_STATE_CONNECTING) return -EBUSY; if (source->stream_state >= AVDTP_STATE_OPEN) return -EALREADY; if (!source_setup_stream(service, NULL)) { DBG("Failed to create a stream"); return -EIO; } DBG("stream creation in progress"); return 0; } static void source_free(struct btd_service *service) { struct source *source = btd_service_get_user_data(service); if (source->cb_id) avdtp_stream_remove_cb(source->session, source->stream, source->cb_id); if (source->session) avdtp_unref(source->session); if (source->connect_id > 0) { btd_service_connecting_complete(source->service, -ECANCELED); a2dp_cancel(source->connect_id); source->connect_id = 0; } if (source->disconnect_id > 0) { btd_service_disconnecting_complete(source->service, -ECANCELED); a2dp_cancel(source->disconnect_id); source->disconnect_id = 0; } avdtp_remove_state_cb(source->avdtp_callback_id); btd_service_unref(source->service); g_free(source); } void source_unregister(struct btd_service *service) { struct btd_device *dev = btd_service_get_device(service); DBG("%s", device_get_path(dev)); source_free(service); } int source_init(struct btd_service *service) { struct btd_device *dev = btd_service_get_device(service); struct source *source; DBG("%s", device_get_path(dev)); source = g_new0(struct source, 1); source->service = btd_service_ref(service); source->avdtp_callback_id = avdtp_add_state_cb(dev, avdtp_state_callback, source); btd_service_set_user_data(service, source); return 0; } gboolean source_new_stream(struct btd_service *service, struct avdtp *session, struct avdtp_stream *stream) { struct source *source = btd_service_get_user_data(service); if (source->stream) return FALSE; if (!source->session) source->session = avdtp_ref(session); source->stream = stream; source->cb_id = avdtp_stream_add_cb(session, stream, stream_state_changed, service); return TRUE; } int source_disconnect(struct btd_service *service) { struct source *source = btd_service_get_user_data(service); if (!source->session) return -ENOTCONN; /* cancel pending connect */ if (source->connect_id > 0) { avdtp_unref(source->session); source->session = NULL; a2dp_cancel(source->connect_id); source->connect_id = 0; btd_service_disconnecting_complete(source->service, 0); return 0; } /* disconnect already ongoing */ if (source->disconnect_id > 0) return -EBUSY; if (!source->stream) return -ENOTCONN; return avdtp_close(source->session, source->stream, FALSE); } unsigned int source_add_state_cb(struct btd_service *service, source_state_cb cb, void *user_data) { struct source_state_callback *state_cb; static unsigned int id = 0; state_cb = g_new(struct source_state_callback, 1); state_cb->cb = cb; state_cb->service = service; state_cb->user_data = user_data; state_cb->id = ++id; source_callbacks = g_slist_append(source_callbacks, state_cb); return state_cb->id; } gboolean source_remove_state_cb(unsigned int id) { GSList *l; for (l = source_callbacks; l != NULL; l = l->next) { struct source_state_callback *cb = l->data; if (cb && cb->id == id) { source_callbacks = g_slist_remove(source_callbacks, cb); g_free(cb); return TRUE; } } return FALSE; } bluez-5.82/profiles/audio/PaxHeaders/transport.c0000644000000000000000000000005014766002272017000 xustar0020 atime=1743515578 20 ctime=1743591283 bluez-5.82/profiles/audio/transport.c0000644000000000000000000020252014766002272016462 0ustar00rootroot// SPDX-License-Identifier: GPL-2.0-or-later /* * * BlueZ - Bluetooth protocol stack for Linux * * Copyright (C) 2006-2007 Nokia Corporation * Copyright (C) 2004-2009 Marcel Holtmann * Copyright 2023-2025 NXP * * */ #ifdef HAVE_CONFIG_H #include #endif #define _GNU_SOURCE #include #include #include "lib/bluetooth.h" #include "lib/sdp.h" #include "lib/uuid.h" #include "gdbus/gdbus.h" #include "btio/btio.h" #include "src/adapter.h" #include "src/device.h" #include "src/dbus-common.h" #include "src/log.h" #include "src/error.h" #include "src/shared/util.h" #include "src/shared/queue.h" #include "src/shared/bap.h" #include "src/shared/bass.h" #include "src/shared/io.h" #ifdef HAVE_A2DP #include "avdtp.h" #include "a2dp.h" #include "sink.h" #include "source.h" #ifdef HAVE_AVRCP #include "avrcp.h" #endif #endif #ifdef HAVE_ASHA #include "asha.h" #endif #include "media.h" #include "transport.h" #include "vcp.h" #define MEDIA_TRANSPORT_INTERFACE "org.bluez.MediaTransport1" typedef enum { TRANSPORT_STATE_IDLE, /* Not acquired and suspended */ TRANSPORT_STATE_PENDING, /* Playing but not acquired */ /* Playing but not acquired, applicable only for transports * created by a broadcast sink */ TRANSPORT_STATE_BROADCASTING, TRANSPORT_STATE_REQUESTING, /* Acquire in progress */ TRANSPORT_STATE_ACTIVE, /* Acquired and playing */ TRANSPORT_STATE_SUSPENDING, /* Release in progress */ } transport_state_t; static const char *str_state[] = { "TRANSPORT_STATE_IDLE", "TRANSPORT_STATE_PENDING", "TRANSPORT_STATE_BROADCASTING", "TRANSPORT_STATE_REQUESTING", "TRANSPORT_STATE_ACTIVE", "TRANSPORT_STATE_SUSPENDING", }; struct media_request { DBusMessage *msg; guint id; }; struct media_owner { struct media_transport *transport; struct media_request *pending; char *name; guint watch; }; struct a2dp_transport { struct avdtp *session; uint16_t delay; int8_t volume; guint watch; guint resume_id; gboolean cancel_resume; guint cancel_id; }; struct bap_transport { struct bt_bap_stream *stream; unsigned int state_id; bool linked; struct bt_bap_qos qos; guint resume_id; }; struct media_transport_ops { const char *uuid; const GDBusPropertyTable *properties; void (*set_owner)(struct media_transport *transport, struct media_owner *owner); void (*remove_owner)(struct media_transport *transport, struct media_owner *owner); void *(*init)(struct media_transport *transport, void *stream); guint (*resume)(struct media_transport *transport, struct media_owner *owner); guint (*suspend)(struct media_transport *transport, struct media_owner *owner); void (*cancel)(struct media_transport *transport, guint id); void (*set_state)(struct media_transport *transport, transport_state_t state); void *(*get_stream)(struct media_transport *transport); int (*get_volume)(struct media_transport *transport); int (*set_volume)(struct media_transport *transport, int level); int (*set_delay)(struct media_transport *transport, uint16_t delay); void (*update_links)(const struct media_transport *transport); GDestroyNotify destroy; }; struct media_transport { char *path; /* Transport object path */ struct btd_device *device; /* Transport device */ struct btd_adapter *adapter; /* Transport adapter bcast*/ char *remote_endpoint; /* Transport remote SEP */ struct media_endpoint *endpoint; /* Transport endpoint */ struct media_owner *owner; /* Transport owner */ uint8_t *configuration; /* Transport configuration */ int size; /* Transport configuration size */ int fd; /* Transport file descriptor */ uint16_t imtu; /* Transport input mtu */ uint16_t omtu; /* Transport output mtu */ transport_state_t state; const struct media_transport_ops *ops; void *data; }; static GSList *transports = NULL; static const char *state2str(transport_state_t state) { switch (state) { case TRANSPORT_STATE_IDLE: case TRANSPORT_STATE_REQUESTING: return "idle"; case TRANSPORT_STATE_PENDING: return "pending"; case TRANSPORT_STATE_BROADCASTING: return "broadcasting"; case TRANSPORT_STATE_ACTIVE: case TRANSPORT_STATE_SUSPENDING: return "active"; } return NULL; } static gboolean state_in_use(transport_state_t state) { switch (state) { case TRANSPORT_STATE_IDLE: case TRANSPORT_STATE_PENDING: case TRANSPORT_STATE_BROADCASTING: return FALSE; case TRANSPORT_STATE_REQUESTING: case TRANSPORT_STATE_ACTIVE: case TRANSPORT_STATE_SUSPENDING: return TRUE; } return FALSE; } static struct media_transport * find_transport_by_bap_stream(const struct bt_bap_stream *stream) { GSList *l; for (l = transports; l; l = g_slist_next(l)) { struct media_transport *transport = l->data; const char *uuid = media_endpoint_get_uuid(transport->endpoint); struct bap_transport *bap; if (strcasecmp(uuid, PAC_SINK_UUID) && strcasecmp(uuid, PAC_SOURCE_UUID) && strcasecmp(uuid, BAA_SERVICE_UUID)) continue; bap = transport->data; if (bap->stream == stream) return transport; } return NULL; } static void transport_set_state(struct media_transport *transport, transport_state_t state) { transport_state_t old_state = transport->state; const char *str; if (old_state == state) return; transport->state = state; DBG("State changed %s: %s -> %s", transport->path, str_state[old_state], str_state[state]); str = state2str(state); if (g_strcmp0(str, state2str(old_state)) != 0) g_dbus_emit_property_changed(btd_get_dbus_connection(), transport->path, MEDIA_TRANSPORT_INTERFACE, "State"); /* Update transport specific data */ if (transport->ops && transport->ops->set_state) transport->ops->set_state(transport, state); } void media_transport_destroy(struct media_transport *transport) { char *path; path = g_strdup(transport->path); g_dbus_unregister_interface(btd_get_dbus_connection(), path, MEDIA_TRANSPORT_INTERFACE); g_free(path); } static struct media_request *media_request_create(DBusMessage *msg, guint id) { struct media_request *req; req = g_new0(struct media_request, 1); req->msg = dbus_message_ref(msg); req->id = id; DBG("Request created: method=%s id=%u", dbus_message_get_member(msg), id); return req; } static void media_request_reply(struct media_request *req, int err) { DBusMessage *reply; DBG("Request %s Reply %s", dbus_message_get_member(req->msg), strerror(err)); if (!err) reply = g_dbus_create_reply(req->msg, DBUS_TYPE_INVALID); else reply = g_dbus_create_error(req->msg, ERROR_INTERFACE ".Failed", "%s", strerror(err)); g_dbus_send_message(btd_get_dbus_connection(), reply); } static void media_owner_remove(struct media_owner *owner) { struct media_transport *transport = owner->transport; struct media_request *req = owner->pending; if (!req) return; DBG("Owner %s Request %s", owner->name, dbus_message_get_member(req->msg)); if (req->id && transport->ops && transport->ops->cancel) transport->ops->cancel(transport, req->id); owner->pending = NULL; if (req->msg) dbus_message_unref(req->msg); g_free(req); } static void media_owner_free(struct media_owner *owner) { struct media_transport *transport = owner->transport; DBG("Owner %s", owner->name); media_owner_remove(owner); if (transport) transport->owner = NULL; g_free(owner->name); g_free(owner); } static void linked_transport_remove_owner(void *data, void *user_data) { struct bt_bap_stream *stream = data; struct media_owner *owner = user_data; struct media_transport *transport; transport = find_transport_by_bap_stream(stream); if (!transport) { error("Unable to find transport"); return; } DBG("Transport %s Owner %s", transport->path, owner->name); transport->owner = NULL; } static guint media_transport_suspend(struct media_transport *transport, struct media_owner *owner) { if (!state_in_use(transport->state)) return 0; DBG("Transport %s Owner %s", transport->path, owner ? owner->name : ""); if (transport->ops && transport->ops->suspend) return transport->ops->suspend(transport, owner); return 0; } static void transport_bap_remove_owner(struct media_transport *transport, struct media_owner *owner) { struct bap_transport *bap = transport->data; if (bap && bap->linked) queue_foreach(bt_bap_stream_io_get_links(bap->stream), linked_transport_remove_owner, owner); } static void media_transport_remove_owner(struct media_transport *transport) { struct media_owner *owner = transport->owner; if (!transport->owner) return; DBG("Transport %s Owner %s", transport->path, owner->name); /* Reply if owner has a pending request */ if (owner->pending) media_request_reply(owner->pending, EIO); transport->owner = NULL; if (transport->ops && transport->ops->remove_owner) transport->ops->remove_owner(transport, owner); if (owner->watch) g_dbus_remove_watch(btd_get_dbus_connection(), owner->watch); media_owner_free(owner); media_transport_suspend(transport, NULL); } static gboolean media_transport_set_fd(struct media_transport *transport, int fd, uint16_t imtu, uint16_t omtu) { if (transport->fd == fd) return TRUE; transport->fd = fd; transport->imtu = imtu; transport->omtu = omtu; info("%s: fd(%d) ready", transport->path, fd); return TRUE; } #ifdef HAVE_A2DP static void *transport_a2dp_get_stream(struct media_transport *transport) { struct a2dp_sep *sep = media_endpoint_get_sep(transport->endpoint); if (!sep) return NULL; return a2dp_sep_get_stream(sep); } static void a2dp_suspend_complete(struct avdtp *session, int err, void *user_data) { struct media_owner *owner = user_data; struct media_transport *transport = owner->transport; struct a2dp_transport *a2dp = transport->data; struct a2dp_sep *sep = media_endpoint_get_sep(transport->endpoint); /* Release always succeeds */ if (owner->pending) { owner->pending->id = 0; media_request_reply(owner->pending, 0); media_owner_remove(owner); } a2dp_sep_unlock(sep, a2dp->session); transport_set_state(transport, TRANSPORT_STATE_IDLE); media_transport_remove_owner(transport); } static guint transport_a2dp_suspend(struct media_transport *transport, struct media_owner *owner) { struct a2dp_transport *a2dp = transport->data; struct media_endpoint *endpoint = transport->endpoint; struct a2dp_sep *sep = media_endpoint_get_sep(endpoint); if (owner != NULL) { if (a2dp->resume_id) { a2dp->cancel_resume = TRUE; return a2dp->resume_id; } return a2dp_suspend(a2dp->session, sep, a2dp_suspend_complete, owner); } transport_set_state(transport, TRANSPORT_STATE_IDLE); a2dp_sep_unlock(sep, a2dp->session); return 0; } static gboolean a2dp_cancel_resume_cb(void *user_data) { struct media_owner *owner = user_data; struct media_transport *transport = owner->transport; struct a2dp_transport *a2dp = transport->data; guint id; a2dp->cancel_id = 0; if (!owner->pending) goto fail; owner->pending->id = 0; /* The suspend fails e.g. if stream was closed/aborted. This happens if * SetConfiguration() was called while we were waiting for the START to * complete. * * We bail out from the Release() with error in that case. */ id = transport_a2dp_suspend(transport, owner); if (id) owner->pending->id = id; else goto fail; return FALSE; fail: media_transport_remove_owner(transport); return FALSE; } static void a2dp_resume_complete(struct avdtp *session, int err, void *user_data) { struct media_owner *owner = user_data; struct media_request *req = owner->pending; struct media_transport *transport = owner->transport; struct a2dp_transport *a2dp = transport->data; struct avdtp_stream *stream; int fd; uint16_t imtu, omtu; gboolean ret; a2dp->resume_id = 0; if (!req) goto fail; req->id = 0; if (err) goto fail; if (a2dp->cancel_resume) { DBG("cancel resume"); a2dp->cancel_id = g_idle_add(a2dp_cancel_resume_cb, owner); return; } stream = transport_a2dp_get_stream(transport); if (stream == NULL) goto fail; ret = avdtp_stream_get_transport(stream, &fd, &imtu, &omtu, NULL); if (ret == FALSE) goto fail; media_transport_set_fd(transport, fd, imtu, omtu); ret = g_dbus_send_reply(btd_get_dbus_connection(), req->msg, DBUS_TYPE_UNIX_FD, &fd, DBUS_TYPE_UINT16, &imtu, DBUS_TYPE_UINT16, &omtu, DBUS_TYPE_INVALID); if (ret == FALSE) goto fail; media_owner_remove(owner); transport_set_state(transport, TRANSPORT_STATE_ACTIVE); return; fail: media_transport_remove_owner(transport); } static guint transport_a2dp_resume(struct media_transport *transport, struct media_owner *owner) { struct a2dp_transport *a2dp = transport->data; struct media_endpoint *endpoint = transport->endpoint; struct a2dp_sep *sep = media_endpoint_get_sep(endpoint); guint id; if (a2dp->resume_id || a2dp->cancel_id) return 0; if (a2dp->session == NULL) { a2dp->session = a2dp_avdtp_get(transport->device); if (a2dp->session == NULL) return 0; } if (state_in_use(transport->state)) { id = a2dp_resume(a2dp->session, sep, a2dp_resume_complete, owner); goto done; } if (a2dp_sep_lock(sep, a2dp->session) == FALSE) return 0; id = a2dp_resume(a2dp->session, sep, a2dp_resume_complete, owner); if (id == 0) { a2dp_sep_unlock(sep, a2dp->session); return 0; } if (transport->state == TRANSPORT_STATE_IDLE) transport_set_state(transport, TRANSPORT_STATE_REQUESTING); done: a2dp->resume_id = id; a2dp->cancel_resume = FALSE; return id; } static void transport_a2dp_cancel(struct media_transport *transport, guint id) { struct a2dp_transport *a2dp = transport->data; /* a2dp_cancel() results to ABORT->IDLE->disconnect. For START we * instead wait the operation out. */ if (id == a2dp->resume_id) { a2dp->cancel_resume = TRUE; return; } a2dp_cancel(id); } static void transport_a2dp_remove_owner(struct media_transport *transport, struct media_owner *owner) { struct a2dp_transport *a2dp = transport->data; /* Cancel any pending operations for the owner */ if (a2dp->cancel_id) { g_source_remove(a2dp->cancel_id); a2dp->cancel_id = 0; } if (a2dp->resume_id) { a2dp_cancel(a2dp->resume_id); a2dp->resume_id = 0; } a2dp->cancel_resume = FALSE; } static int transport_a2dp_get_volume(struct media_transport *transport) { struct a2dp_transport *a2dp = transport->data; return a2dp->volume; } #ifdef HAVE_AVRCP static int transport_a2dp_src_set_volume(struct media_transport *transport, int level) { struct a2dp_transport *a2dp = transport->data; if (level < 0 || level > 127) return -EINVAL; if (a2dp->volume == level) return 0; return avrcp_set_volume(transport->device, level, false); } static int transport_a2dp_snk_set_volume(struct media_transport *transport, int level) { struct a2dp_transport *a2dp = transport->data; bool notify; if (level < 0 || level > 127) return -EINVAL; if (a2dp->volume == level) return 0; notify = a2dp->watch ? true : false; if (notify) { a2dp->volume = level; g_dbus_emit_property_changed(btd_get_dbus_connection(), transport->path, MEDIA_TRANSPORT_INTERFACE, "Volume"); } return avrcp_set_volume(transport->device, level, notify); } #endif static int transport_a2dp_snk_set_delay(struct media_transport *transport, uint16_t delay) { struct a2dp_transport *a2dp = transport->data; struct avdtp_stream *stream; if (a2dp->delay == delay) return 0; if (a2dp->session == NULL) { a2dp->session = a2dp_avdtp_get(transport->device); if (a2dp->session == NULL) return -EIO; } stream = media_transport_get_stream(transport); if (stream == NULL) return -EIO; if (a2dp->watch) { a2dp->delay = delay; g_dbus_emit_property_changed(btd_get_dbus_connection(), transport->path, MEDIA_TRANSPORT_INTERFACE, "Delay"); } return avdtp_delay_report(a2dp->session, stream, delay); } #endif /* HAVE_A2DP */ static void media_owner_exit(DBusConnection *connection, void *user_data) { struct media_owner *owner = user_data; DBG("Owner %s", owner->name); owner->watch = 0; media_owner_remove(owner); media_transport_remove_owner(owner->transport); } static void linked_transport_set_owner(void *data, void *user_data) { struct bt_bap_stream *stream = data; struct media_owner *owner = user_data; struct media_transport *transport; transport = find_transport_by_bap_stream(stream); if (!transport) { error("Unable to find transport"); return; } DBG("Transport %s Owner %s", transport->path, owner->name); transport->owner = owner; } static void transport_bap_set_owner(struct media_transport *transport, struct media_owner *owner) { struct bap_transport *bap = transport->data; if (bap && bap->linked) queue_foreach(bt_bap_stream_io_get_links(bap->stream), linked_transport_set_owner, owner); } static void media_transport_set_owner(struct media_transport *transport, struct media_owner *owner) { DBG("Transport %s Owner %s", transport->path, owner->name); transport->owner = owner; if (transport->ops && transport->ops->set_owner) transport->ops->set_owner(transport, owner); owner->transport = transport; owner->watch = g_dbus_add_disconnect_watch(btd_get_dbus_connection(), owner->name, media_owner_exit, owner, NULL); } static struct media_owner *media_owner_create(DBusMessage *msg) { struct media_owner *owner; owner = g_new0(struct media_owner, 1); owner->name = g_strdup(dbus_message_get_sender(msg)); DBG("Owner created: sender=%s", owner->name); return owner; } static void media_owner_add(struct media_owner *owner, struct media_request *req) { DBG("Owner %s Request %s", owner->name, dbus_message_get_member(req->msg)); owner->pending = req; } static void *transport_bap_get_stream(struct media_transport *transport) { struct bap_transport *bap = transport->data; return bap->stream; } static guint media_transport_resume(struct media_transport *transport, struct media_owner *owner) { DBG("Transport %s Owner %s", transport->path, owner ? owner->name : ""); if (transport->ops && transport->ops->resume) return transport->ops->resume(transport, owner); return 0; } static DBusMessage *acquire(DBusConnection *conn, DBusMessage *msg, void *data) { struct media_transport *transport = data; struct media_owner *owner; struct media_request *req = NULL; guint id; if (transport->owner != NULL) return btd_error_not_authorized(msg); if (transport->state >= TRANSPORT_STATE_REQUESTING) return btd_error_not_authorized(msg); owner = media_owner_create(msg); if (!strcmp(media_endpoint_get_uuid(transport->endpoint), BAA_SERVICE_UUID) || !strcmp(media_endpoint_get_uuid(transport->endpoint), BCAA_SERVICE_UUID)) { req = media_request_create(msg, 0x00); media_owner_add(owner, req); media_transport_set_owner(transport, owner); } id = media_transport_resume(transport, owner); if (id == 0) { media_owner_free(owner); return btd_error_not_authorized(msg); } if (!req) { req = media_request_create(msg, id); media_owner_add(owner, req); media_transport_set_owner(transport, owner); } return NULL; } static DBusMessage *try_acquire(DBusConnection *conn, DBusMessage *msg, void *data) { struct media_transport *transport = data; struct media_owner *owner; struct media_request *req; guint id; if (transport->owner != NULL) return btd_error_not_authorized(msg); if (transport->state >= TRANSPORT_STATE_REQUESTING) return btd_error_not_authorized(msg); if ((transport->state != TRANSPORT_STATE_PENDING) && (transport->state != TRANSPORT_STATE_BROADCASTING)) return btd_error_not_available(msg); owner = media_owner_create(msg); id = media_transport_resume(transport, owner); if (id == 0) { media_owner_free(owner); return btd_error_not_authorized(msg); } req = media_request_create(msg, id); media_owner_add(owner, req); media_transport_set_owner(transport, owner); return NULL; } static void bap_stop_complete(struct bt_bap_stream *stream, uint8_t code, uint8_t reason, void *user_data) { struct media_owner *owner = user_data; struct media_request *req; struct media_transport *transport; if (!owner) return; req = owner->pending; /* Release always succeeds */ if (req) { req->id = 0; media_request_reply(req, 0); media_owner_remove(owner); } transport = owner->transport; if (transport) { transport_set_state(transport, TRANSPORT_STATE_IDLE); media_transport_remove_owner(transport); } } static void bap_disable_complete(struct bt_bap_stream *stream, uint8_t code, uint8_t reason, void *user_data) { bap_stop_complete(stream, code, reason, user_data); } static DBusMessage *release(DBusConnection *conn, DBusMessage *msg, void *data) { struct media_transport *transport = data; struct media_owner *owner = transport->owner; const char *sender; struct media_request *req; guint id; sender = dbus_message_get_sender(msg); if (owner == NULL || g_strcmp0(owner->name, sender) != 0) return btd_error_not_authorized(msg); DBG("Owner %s", owner->name); if (owner->pending) { const char *member; member = dbus_message_get_member(owner->pending->msg); /* Cancel Acquire request if that exist */ if (g_str_equal(member, "Acquire")) { media_request_reply(owner->pending, ECANCELED); media_owner_remove(owner); } else { return btd_error_in_progress(msg); } } transport_set_state(transport, TRANSPORT_STATE_SUSPENDING); id = media_transport_suspend(transport, owner); if (id == 0) { media_transport_remove_owner(transport); return g_dbus_create_reply(msg, DBUS_TYPE_INVALID); } req = media_request_create(msg, id); media_owner_add(owner, req); return NULL; } static gboolean get_device(const GDBusPropertyTable *property, DBusMessageIter *iter, void *data) { struct media_transport *transport = data; const char *path; if (transport->device) path = device_get_path(transport->device); else path = adapter_get_path(transport->adapter); dbus_message_iter_append_basic(iter, DBUS_TYPE_OBJECT_PATH, &path); return TRUE; } static gboolean get_uuid(const GDBusPropertyTable *property, DBusMessageIter *iter, void *data) { struct media_transport *transport = data; const char *uuid = media_endpoint_get_uuid(transport->endpoint); dbus_message_iter_append_basic(iter, DBUS_TYPE_STRING, &uuid); return TRUE; } static gboolean get_codec(const GDBusPropertyTable *property, DBusMessageIter *iter, void *data) { struct media_transport *transport = data; uint8_t codec = media_endpoint_get_codec(transport->endpoint); dbus_message_iter_append_basic(iter, DBUS_TYPE_BYTE, &codec); return TRUE; } static gboolean get_configuration(const GDBusPropertyTable *property, DBusMessageIter *iter, void *data) { struct media_transport *transport = data; DBusMessageIter array; dbus_message_iter_open_container(iter, DBUS_TYPE_ARRAY, DBUS_TYPE_BYTE_AS_STRING, &array); dbus_message_iter_append_fixed_array(&array, DBUS_TYPE_BYTE, &transport->configuration, transport->size); dbus_message_iter_close_container(iter, &array); return TRUE; } static gboolean get_state(const GDBusPropertyTable *property, DBusMessageIter *iter, void *data) { struct media_transport *transport = data; const char *state = state2str(transport->state); dbus_message_iter_append_basic(iter, DBUS_TYPE_STRING, &state); return TRUE; } #ifdef HAVE_A2DP static gboolean delay_reporting_exists(const GDBusPropertyTable *property, void *data) { struct media_transport *transport = data; struct avdtp_stream *stream; stream = media_transport_get_stream(transport); if (stream == NULL) return FALSE; return avdtp_stream_has_delay_reporting(stream); } static gboolean get_delay_report(const GDBusPropertyTable *property, DBusMessageIter *iter, void *data) { struct media_transport *transport = data; struct a2dp_transport *a2dp = transport->data; dbus_message_iter_append_basic(iter, DBUS_TYPE_UINT16, &a2dp->delay); return TRUE; } static int media_transport_set_delay(struct media_transport *transport, uint16_t delay) { DBG("Transport %s delay %d", transport->path, delay); if (transport->ops && transport->ops->set_delay) return transport->ops->set_delay(transport, delay); return 0; } static void set_delay_report(const GDBusPropertyTable *property, DBusMessageIter *iter, GDBusPendingPropertySet id, void *data) { struct media_transport *transport = data; struct media_owner *owner = transport->owner; const char *sender; uint16_t arg; int err; if (owner != NULL) { /* If the transport is acquired, do not allow to modify * the delay anyone but the owner. */ sender = g_dbus_pending_property_get_sender(id); if (g_strcmp0(owner->name, sender) != 0) { g_dbus_pending_property_error(id, ERROR_INTERFACE ".NotAuthorized", "Operation Not Authorized"); return; } } if (dbus_message_iter_get_arg_type(iter) != DBUS_TYPE_UINT16) { g_dbus_pending_property_error(id, ERROR_INTERFACE ".InvalidArguments", "Expected UINT16"); return; } dbus_message_iter_get_basic(iter, &arg); err = media_transport_set_delay(transport, arg); if (err) { error("Unable to set delay: %s (%d)", strerror(-err), err); g_dbus_pending_property_error(id, ERROR_INTERFACE ".Failed", "Internal error %s (%d)", strerror(-err), err); return; } g_dbus_pending_property_success(id); } #endif /* HAVE_A2DP */ static gboolean volume_exists(const GDBusPropertyTable *property, void *data) { struct media_transport *transport = data; int volume; if (media_transport_get_volume(transport, &volume)) return FALSE; return volume >= 0; } int media_transport_get_volume(struct media_transport *transport, int *volume) { if (transport->ops && transport->ops->get_volume) { *volume = transport->ops->get_volume(transport); return 0; } return -EINVAL; } static gboolean get_volume(const GDBusPropertyTable *property, DBusMessageIter *iter, void *data) { struct media_transport *transport = data; int level; uint16_t volume; if (media_transport_get_volume(transport, &level)) return FALSE; volume = level; dbus_message_iter_append_basic(iter, DBUS_TYPE_UINT16, &volume); return TRUE; } static int media_transport_set_volume(struct media_transport *transport, int level) { DBG("Transport %s level %d", transport->path, level); if (transport->ops && transport->ops->set_volume) return transport->ops->set_volume(transport, level); return 0; } static void set_volume(const GDBusPropertyTable *property, DBusMessageIter *iter, GDBusPendingPropertySet id, void *data) { struct media_transport *transport = data; uint16_t arg; int err; if (dbus_message_iter_get_arg_type(iter) != DBUS_TYPE_UINT16) { g_dbus_pending_property_error(id, ERROR_INTERFACE ".InvalidArguments", "Expected UINT16"); return; } dbus_message_iter_get_basic(iter, &arg); err = media_transport_set_volume(transport, arg); if (err == -EINVAL) { g_dbus_pending_property_error(id, ERROR_INTERFACE ".InvalidArguments", "Invalid volume value"); return; } else if (err) { error("Unable to set volume: %s (%d)", strerror(-err), err); g_dbus_pending_property_error(id, ERROR_INTERFACE ".Failed", "Internal error %s (%d)", strerror(-err), err); return; } g_dbus_pending_property_success(id); } static gboolean endpoint_exists(const GDBusPropertyTable *property, void *data) { struct media_transport *transport = data; return transport->remote_endpoint != NULL; } static gboolean get_endpoint(const GDBusPropertyTable *property, DBusMessageIter *iter, void *data) { struct media_transport *transport = data; dbus_message_iter_append_basic(iter, DBUS_TYPE_OBJECT_PATH, &transport->remote_endpoint); return TRUE; } static DBusMessage *select_transport(DBusConnection *conn, DBusMessage *msg, void *data); static DBusMessage *unselect_transport(DBusConnection *conn, DBusMessage *msg, void *data); static const GDBusMethodTable transport_methods[] = { { GDBUS_ASYNC_METHOD("Acquire", NULL, GDBUS_ARGS({ "fd", "h" }, { "mtu_r", "q" }, { "mtu_w", "q" }), acquire) }, { GDBUS_ASYNC_METHOD("TryAcquire", NULL, GDBUS_ARGS({ "fd", "h" }, { "mtu_r", "q" }, { "mtu_w", "q" }), try_acquire) }, { GDBUS_ASYNC_METHOD("Release", NULL, NULL, release) }, { GDBUS_ASYNC_METHOD("Select", NULL, NULL, select_transport) }, { GDBUS_ASYNC_METHOD("Unselect", NULL, NULL, unselect_transport) }, { }, }; #ifdef HAVE_A2DP static const GDBusPropertyTable transport_a2dp_properties[] = { { "Device", "o", get_device }, { "UUID", "s", get_uuid }, { "Codec", "y", get_codec }, { "Configuration", "ay", get_configuration }, { "State", "s", get_state }, { "Delay", "q", get_delay_report, set_delay_report, delay_reporting_exists }, { "Volume", "q", get_volume, set_volume, volume_exists }, { "Endpoint", "o", get_endpoint, NULL, endpoint_exists, G_DBUS_PROPERTY_FLAG_EXPERIMENTAL }, { } }; #endif /* HAVE_A2DP */ static void append_io_qos(DBusMessageIter *dict, struct bt_bap_io_qos *qos) { dict_append_entry(dict, "Interval", DBUS_TYPE_UINT32, &qos->interval); dict_append_entry(dict, "Latency", DBUS_TYPE_UINT16, &qos->latency); dict_append_entry(dict, "SDU", DBUS_TYPE_UINT16, &qos->sdu); dict_append_entry(dict, "PHY", DBUS_TYPE_BYTE, &qos->phy); dict_append_entry(dict, "Retransmissions", DBUS_TYPE_BYTE, &qos->rtn); } static gboolean get_ucast_qos(const GDBusPropertyTable *property, DBusMessageIter *iter, void *data) { struct media_transport *transport = data; struct bap_transport *bap = transport->data; DBusMessageIter dict; dbus_message_iter_open_container(iter, DBUS_TYPE_ARRAY, DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING DBUS_TYPE_STRING_AS_STRING DBUS_TYPE_VARIANT_AS_STRING DBUS_DICT_ENTRY_END_CHAR_AS_STRING, &dict); dict_append_entry(&dict, "CIG", DBUS_TYPE_BYTE, &bap->qos.ucast.cig_id); dict_append_entry(&dict, "CIS", DBUS_TYPE_BYTE, &bap->qos.ucast.cis_id); dict_append_entry(&dict, "Framing", DBUS_TYPE_BYTE, &bap->qos.ucast.framing); dict_append_entry(&dict, "PresentationDelay", DBUS_TYPE_UINT32, &bap->qos.ucast.delay); append_io_qos(&dict, &bap->qos.ucast.io_qos); dbus_message_iter_close_container(iter, &dict); return TRUE; } static gboolean get_location(const GDBusPropertyTable *property, DBusMessageIter *iter, void *data) { struct media_transport *transport = data; struct bap_transport *bap = transport->data; uint32_t location = bt_bap_stream_get_location(bap->stream); dbus_message_iter_append_basic(iter, DBUS_TYPE_UINT32, &location); return TRUE; } static gboolean get_metadata(const GDBusPropertyTable *property, DBusMessageIter *iter, void *data) { struct media_transport *transport = data; struct bap_transport *bap = transport->data; struct iovec *meta = bt_bap_stream_get_metadata(bap->stream); DBusMessageIter array; dbus_message_iter_open_container(iter, DBUS_TYPE_ARRAY, DBUS_TYPE_BYTE_AS_STRING, &array); if (meta) dbus_message_iter_append_fixed_array(&array, DBUS_TYPE_BYTE, &meta->iov_base, meta->iov_len); dbus_message_iter_close_container(iter, &array); return TRUE; } static gboolean links_exists(const GDBusPropertyTable *property, void *data) { struct media_transport *transport = data; struct bap_transport *bap = transport->data; return bap->linked; } static void append_link(void *data, void *user_data) { struct bt_bap_stream *stream = data; DBusMessageIter *array = user_data; struct media_transport *transport; if (!stream) return; transport = find_transport_by_bap_stream(stream); if (!transport) { error("Unable to find transport"); return; } dbus_message_iter_append_basic(array, DBUS_TYPE_OBJECT_PATH, &transport->path); } static gboolean get_links(const GDBusPropertyTable *property, DBusMessageIter *iter, void *data) { struct media_transport *transport = data; struct bap_transport *bap = transport->data; struct queue *links = bt_bap_stream_io_get_links(bap->stream); DBusMessageIter array; dbus_message_iter_open_container(iter, DBUS_TYPE_ARRAY, DBUS_TYPE_OBJECT_PATH_AS_STRING, &array); queue_foreach(links, append_link, &array); dbus_message_iter_close_container(iter, &array); return TRUE; } static struct media_transport *find_transport_by_path(const char *path) { GSList *l; for (l = transports; l; l = g_slist_next(l)) { struct media_transport *transport = l->data; if (g_str_equal(path, transport->path)) return transport; } return NULL; } static void set_links(const GDBusPropertyTable *property, DBusMessageIter *iter, GDBusPendingPropertySet id, void *user_data) { struct media_transport *transport = user_data; struct bap_transport *bap = transport->data; DBusMessageIter array; if (dbus_message_iter_get_arg_type(iter) != DBUS_TYPE_ARRAY) { g_dbus_pending_property_error(id, ERROR_INTERFACE ".InvalidArguments", "Invalid arguments in method call"); return; } dbus_message_iter_recurse(iter, &array); while (dbus_message_iter_get_arg_type(&array) == DBUS_TYPE_OBJECT_PATH) { struct media_transport *link; struct bap_transport *bap_link; const char *path; dbus_message_iter_get_basic(&array, &path); link = find_transport_by_path(path); if (!link) { g_dbus_pending_property_error(id, ERROR_INTERFACE ".InvalidArguments", "Invalid arguments in method call"); return; } bap_link = link->data; /* Link stream */ bt_bap_stream_io_link(bap->stream, bap_link->stream); dbus_message_iter_next(&array); } g_dbus_pending_property_success(id); } static gboolean qos_ucast_exists(const GDBusPropertyTable *property, void *data) { struct media_transport *transport = data; struct bap_transport *bap = transport->data; return bap->qos.ucast.io_qos.phy != 0x00; } static const GDBusPropertyTable transport_bap_uc_properties[] = { { "Device", "o", get_device }, { "UUID", "s", get_uuid }, { "Codec", "y", get_codec }, { "Configuration", "ay", get_configuration }, { "State", "s", get_state }, { "QoS", "a{sv}", get_ucast_qos, NULL, qos_ucast_exists }, { "Endpoint", "o", get_endpoint, NULL, endpoint_exists }, { "Location", "u", get_location }, { "Metadata", "ay", get_metadata }, { "Links", "ao", get_links, NULL, links_exists }, { "Volume", "q", get_volume, set_volume, volume_exists }, { } }; static gboolean get_bcast_qos(const GDBusPropertyTable *property, DBusMessageIter *iter, void *data) { struct media_transport *transport = data; struct bap_transport *bap = transport->data; DBusMessageIter dict; dbus_message_iter_open_container(iter, DBUS_TYPE_ARRAY, DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING DBUS_TYPE_STRING_AS_STRING DBUS_TYPE_VARIANT_AS_STRING DBUS_DICT_ENTRY_END_CHAR_AS_STRING, &dict); dict_append_entry(&dict, "BIG", DBUS_TYPE_BYTE, &bap->qos.bcast.big); dict_append_entry(&dict, "BIS", DBUS_TYPE_BYTE, &bap->qos.bcast.bis); dict_append_entry(&dict, "SyncFactor", DBUS_TYPE_BYTE, &bap->qos.bcast.sync_factor); dict_append_entry(&dict, "Packing", DBUS_TYPE_BYTE, &bap->qos.bcast.packing); dict_append_entry(&dict, "Framing", DBUS_TYPE_BYTE, &bap->qos.bcast.framing); dict_append_entry(&dict, "Encryption", DBUS_TYPE_BYTE, &bap->qos.bcast.encryption); if (bap->qos.bcast.bcode) dict_append_array(&dict, "BCode", DBUS_TYPE_BYTE, &bap->qos.bcast.bcode->iov_base, bap->qos.bcast.bcode->iov_len); dict_append_entry(&dict, "Options", DBUS_TYPE_BYTE, &bap->qos.bcast.options); dict_append_entry(&dict, "Skip", DBUS_TYPE_UINT16, &bap->qos.bcast.skip); dict_append_entry(&dict, "SyncTimeout", DBUS_TYPE_UINT16, &bap->qos.bcast.sync_timeout); dict_append_entry(&dict, "SyncType", DBUS_TYPE_BYTE, &bap->qos.bcast.sync_cte_type); dict_append_entry(&dict, "MSE", DBUS_TYPE_BYTE, &bap->qos.bcast.mse); dict_append_entry(&dict, "Timeout", DBUS_TYPE_UINT16, &bap->qos.bcast.timeout); append_io_qos(&dict, &bap->qos.bcast.io_qos); dbus_message_iter_close_container(iter, &dict); return TRUE; } static gboolean qos_bcast_exists(const GDBusPropertyTable *property, void *data) { struct media_transport *transport = data; struct bap_transport *bap = transport->data; return bap->qos.bcast.io_qos.phy != 0x00; } static void bcast_qos_set(void *user_data, int err) { GDBusPendingPropertySet id = GPOINTER_TO_UINT(user_data); if (!err) g_dbus_pending_property_success(id); else g_dbus_pending_property_error(id, ERROR_INTERFACE ".Failed", "Failed to set Broadcast Code"); } static void set_bcast_qos(const GDBusPropertyTable *property, DBusMessageIter *dict, GDBusPendingPropertySet id, void *data) { DBusMessageIter array, entry, value; struct media_transport *transport = data; struct bap_transport *bap = transport->data; struct bt_bap_qos *bap_qos = bt_bap_stream_get_qos(bap->stream); char *key; dbus_message_iter_recurse(dict, &array); dbus_message_iter_recurse(&array, &entry); dbus_message_iter_get_basic(&entry, &key); dbus_message_iter_next(&entry); dbus_message_iter_recurse(&entry, &value); if (!strcasecmp(key, "BCode")) { uint8_t *val; int len; DBusMessageIter array; uint8_t empty_bcode[BT_BASS_BCAST_CODE_SIZE] = {0}; dbus_message_iter_recurse(&value, &array); dbus_message_iter_get_fixed_array(&array, &val, &len); if (len > BT_BASS_BCAST_CODE_SIZE) { g_dbus_pending_property_error(id, ERROR_INTERFACE ".InvalidArguments", "Invalid arguments in method call"); return; } if (!memcmp(val, empty_bcode, len)) { /* If the user did not provide a Broadcast Code * for the encrypted stream, request the code from * Broadcast Assistants, if any are available. */ bt_bap_req_bcode(bap->stream, bcast_qos_set, GUINT_TO_POINTER(id)); return; } bap_qos->bcast.bcode = util_iov_new(val, len); } bt_bap_stream_qos(bap->stream, bap_qos, NULL, NULL); g_dbus_pending_property_success(id); } static const GDBusPropertyTable transport_bap_bc_properties[] = { { "Device", "o", get_device }, { "UUID", "s", get_uuid }, { "Codec", "y", get_codec }, { "Configuration", "ay", get_configuration }, { "State", "s", get_state }, { "QoS", "a{sv}", get_bcast_qos, set_bcast_qos, qos_bcast_exists }, { "Endpoint", "o", get_endpoint, NULL, endpoint_exists }, { "Location", "u", get_location }, { "Metadata", "ay", get_metadata }, { "Links", "ao", get_links, set_links, NULL }, { } }; #ifdef HAVE_ASHA static gboolean get_asha_delay(const GDBusPropertyTable *property, DBusMessageIter *iter, void *data) { struct media_transport *transport = data; struct bt_asha_device *asha_dev = transport->data; uint16_t delay; // Delay property is in 1/10ths of ms, while ASHA RenderDelay is in ms delay = bt_asha_device_get_render_delay(asha_dev) * 10; dbus_message_iter_append_basic(iter, DBUS_TYPE_UINT16, &delay); return TRUE; } static const GDBusPropertyTable transport_asha_properties[] = { { "Device", "o", get_device }, { "Endpoint", "o", get_endpoint, NULL, endpoint_exists }, { "UUID", "s", get_uuid }, { "Codec", "y", get_codec }, { "State", "s", get_state }, { "Delay", "q", get_asha_delay }, { "Volume", "q", get_volume, set_volume, volume_exists }, { } }; #endif /* HAVE_ASHA */ #ifdef HAVE_A2DP static void transport_a2dp_destroy(void *data) { struct a2dp_transport *a2dp = data; if (a2dp->session) avdtp_unref(a2dp->session); free(a2dp); } static void transport_a2dp_src_destroy(void *data) { struct a2dp_transport *a2dp = data; if (a2dp->watch) sink_remove_state_cb(a2dp->watch); transport_a2dp_destroy(data); } static void transport_a2dp_snk_destroy(void *data) { struct a2dp_transport *a2dp = data; if (a2dp->watch) source_remove_state_cb(a2dp->watch); transport_a2dp_destroy(data); } #endif /* HAVE_A2DP */ static void media_transport_free(void *data) { struct media_transport *transport = data; transports = g_slist_remove(transports, transport); if (transport->owner) media_transport_remove_owner(transport); if (transport->ops && transport->ops->destroy) transport->ops->destroy(transport->data); g_free(transport->remote_endpoint); g_free(transport->configuration); g_free(transport->path); g_free(transport); } static void transport_update_playing(struct media_transport *transport, gboolean playing) { DBG("%s State=%s Playing=%d", transport->path, str_state[transport->state], playing); if (playing == FALSE) { if (!strcmp(media_endpoint_get_uuid(transport->endpoint), BAA_SERVICE_UUID)) { if ((transport->state == TRANSPORT_STATE_BROADCASTING) || (transport->state == TRANSPORT_STATE_ACTIVE)) transport_set_state(transport, TRANSPORT_STATE_IDLE); } else { if (transport->state == TRANSPORT_STATE_PENDING) transport_set_state(transport, TRANSPORT_STATE_IDLE); else if (transport->state == TRANSPORT_STATE_ACTIVE) { /* Remove owner */ if (transport->owner != NULL) media_transport_remove_owner(transport); } } } else if (transport->state == TRANSPORT_STATE_IDLE) { if (!strcmp(media_endpoint_get_uuid(transport->endpoint), BAA_SERVICE_UUID)) transport_set_state(transport, TRANSPORT_STATE_BROADCASTING); else transport_set_state(transport, TRANSPORT_STATE_PENDING); } } static DBusMessage *select_transport(DBusConnection *conn, DBusMessage *msg, void *data) { struct media_transport *transport = data; if (transport->owner != NULL) return btd_error_not_authorized(msg); if (transport->state >= TRANSPORT_STATE_REQUESTING) return btd_error_not_authorized(msg); if (!strcmp(media_endpoint_get_uuid(transport->endpoint), BAA_SERVICE_UUID)) { transport_update_playing(transport, TRUE); } return dbus_message_new_method_return(msg); } static DBusMessage *unselect_transport(DBusConnection *conn, DBusMessage *msg, void *data) { struct media_transport *transport = data; if (!strcmp(media_endpoint_get_uuid(transport->endpoint), BAA_SERVICE_UUID)) { transport_update_playing(transport, FALSE); } return dbus_message_new_method_return(msg); } #ifdef HAVE_A2DP static void sink_state_changed(struct btd_service *service, sink_state_t old_state, sink_state_t new_state, void *user_data) { struct media_transport *transport = user_data; if (new_state == SINK_STATE_PLAYING) transport_update_playing(transport, TRUE); else transport_update_playing(transport, FALSE); } static void source_state_changed(struct btd_service *service, source_state_t old_state, source_state_t new_state, void *user_data) { struct media_transport *transport = user_data; if (new_state == SOURCE_STATE_PLAYING) transport_update_playing(transport, TRUE); else transport_update_playing(transport, FALSE); } static void *transport_a2dp_src_init(struct media_transport *transport, void *stream) { struct btd_service *service; struct a2dp_transport *a2dp; service = btd_device_get_service(transport->device, A2DP_SINK_UUID); if (!service) return NULL; a2dp = new0(struct a2dp_transport, 1); a2dp->volume = -1; a2dp->watch = sink_add_state_cb(service, sink_state_changed, transport); return a2dp; } static void *transport_a2dp_snk_init(struct media_transport *transport, void *stream) { struct btd_service *service; struct a2dp_transport *a2dp; service = btd_device_get_service(transport->device, A2DP_SOURCE_UUID); if (!service) return NULL; a2dp = new0(struct a2dp_transport, 1); a2dp->volume = 127; a2dp->watch = source_add_state_cb(service, source_state_changed, transport); return a2dp; } #endif /* HAVE_A2DP */ static void bap_enable_complete(struct bt_bap_stream *stream, uint8_t code, uint8_t reason, void *user_data) { struct media_owner *owner = user_data; if (code) media_transport_remove_owner(owner->transport); } static void bap_resume_complete(struct media_transport *transport) { struct bap_transport *bap = transport->data; struct media_owner *owner = transport->owner; DBG("stream %p owner %p resume complete", bap->stream, owner); if (bap->resume_id) { g_source_remove(bap->resume_id); bap->resume_id = 0; } if (!owner) return; if (owner->pending) owner->pending->id = 0; if (transport->fd < 0) { media_transport_remove_owner(transport); return; } if (owner->pending) { gboolean ret; ret = g_dbus_send_reply(btd_get_dbus_connection(), owner->pending->msg, DBUS_TYPE_UNIX_FD, &transport->fd, DBUS_TYPE_UINT16, &transport->imtu, DBUS_TYPE_UINT16, &transport->omtu, DBUS_TYPE_INVALID); if (!ret) { media_transport_remove_owner(transport); return; } } media_owner_remove(owner); transport_set_state(transport, TRANSPORT_STATE_ACTIVE); } static void bap_update_links(const struct media_transport *transport); static bool match_link_transport(const void *data, const void *user_data) { const struct bt_bap_stream *stream = data; const struct media_transport *transport; transport = find_transport_by_bap_stream(stream); if (!transport) return false; bap_update_links(transport); return true; } static void transport_bap_update_links_uc( const struct media_transport *transport) { struct bap_transport *bap = transport->data; struct queue *links = bt_bap_stream_io_get_links(bap->stream); if (bap->linked == !queue_isempty(links)) return; bap->linked = !queue_isempty(links); /* Check if the links transport has been create yet */ if (bap->linked && !queue_find(links, match_link_transport, NULL)) { bap->linked = false; return; } g_dbus_emit_property_changed(btd_get_dbus_connection(), transport->path, MEDIA_TRANSPORT_INTERFACE, "Links"); DBG("stream %p linked %s", bap->stream, bap->linked ? "true" : "false"); } static void transport_bap_update_links_bc( const struct media_transport *transport) { struct bap_transport *bap = transport->data; struct queue *links = bt_bap_stream_io_get_links(bap->stream); if (!queue_isempty(links)) bap->linked = true; else bap->linked = false; g_dbus_emit_property_changed(btd_get_dbus_connection(), transport->path, MEDIA_TRANSPORT_INTERFACE, "Links"); DBG("stream %p linked %s", bap->stream, bap->linked ? "true" : "false"); } static void bap_update_links(const struct media_transport *transport) { if (transport->ops && transport->ops->update_links) transport->ops->update_links(transport); } static void bap_update_qos(const struct media_transport *transport) { struct bap_transport *bap = transport->data; struct bt_bap_qos *qos; qos = bt_bap_stream_get_qos(bap->stream); if (!memcmp(qos, &bap->qos, sizeof(struct bt_bap_qos))) return; bap->qos = *qos; g_dbus_emit_property_changed(btd_get_dbus_connection(), transport->path, MEDIA_TRANSPORT_INTERFACE, "QoS"); } static gboolean bap_resume_complete_cb(void *data) { struct media_transport *transport = data; struct bap_transport *bap = transport->data; bap->resume_id = 0; bap_resume_complete(transport); return FALSE; } static gboolean bap_resume_wait_cb(void *data) { struct media_transport *transport = data; struct bap_transport *bap = transport->data; struct media_owner *owner = transport->owner; /* bap_state_changed will call completion callback when ready */ DBG("stream %p owner %p resume wait", bap->stream, owner); bap->resume_id = 0; if (owner && owner->pending) owner->pending->id = 0; return FALSE; } static void bap_update_bcast_qos(const struct media_transport *transport) { struct bap_transport *bap = transport->data; struct bt_bap_qos *qos; qos = bt_bap_stream_get_qos(bap->stream); if (!memcmp(qos, &bap->qos, sizeof(struct bt_bap_qos))) return; bap->qos = *qos; g_dbus_emit_property_changed(btd_get_dbus_connection(), transport->path, MEDIA_TRANSPORT_INTERFACE, "QoS"); g_dbus_emit_property_changed(btd_get_dbus_connection(), transport->path, MEDIA_TRANSPORT_INTERFACE, "Codec"); g_dbus_emit_property_changed(btd_get_dbus_connection(), transport->path, MEDIA_TRANSPORT_INTERFACE, "Configuration"); } static guint transport_bap_resume(struct media_transport *transport, struct media_owner *owner) { struct bap_transport *bap = transport->data; struct iovec *meta; guint id; if (!bap->stream) return 0; if (bap->resume_id) return 0; bap_update_links(transport); switch (bt_bap_stream_get_state(bap->stream)) { case BT_BAP_STREAM_STATE_ENABLING: bap_enable_complete(bap->stream, 0x00, 0x00, owner); bap->resume_id = g_idle_add(bap_resume_wait_cb, transport); return bap->resume_id; case BT_BAP_STREAM_STATE_STREAMING: bap->resume_id = g_idle_add(bap_resume_complete_cb, transport); return bap->resume_id; } meta = bt_bap_stream_get_metadata(bap->stream); id = bt_bap_stream_enable(bap->stream, bap->linked, meta, bap_enable_complete, owner); if (!id) return 0; if (transport->state == TRANSPORT_STATE_IDLE) transport_set_state(transport, TRANSPORT_STATE_REQUESTING); return id; } static void update_links(void *data, void *user_data) { struct bt_bap_stream *link = data; struct media_transport *transport; transport = find_transport_by_bap_stream(link); if (!transport) { error("Unable to find transport"); return; } bap_update_links(transport); } static void transport_unlink(void *data, void *user_data) { struct bt_bap_stream *link = data; struct bt_bap_stream *stream = user_data; struct media_transport *transport; transport = find_transport_by_bap_stream(link); if (!transport) { error("Unable to find transport"); return; } bt_bap_stream_io_unlink(link, stream); bap_update_links(transport); /* Emit property changed for all remaining links */ queue_foreach(bt_bap_stream_io_get_links(link), update_links, NULL); } static guint transport_bap_suspend(struct media_transport *transport, struct media_owner *owner) { struct bap_transport *bap = transport->data; struct queue *links = bt_bap_stream_io_get_links(bap->stream); bt_bap_stream_func_t func = NULL; guint id; if (!bap->stream) return 0; if (owner) func = bap_disable_complete; else transport_set_state(transport, TRANSPORT_STATE_IDLE); if (bt_bap_stream_get_type(bap->stream) == BT_BAP_STREAM_TYPE_BCAST) /* Unlink stream from all its links */ queue_foreach(links, transport_unlink, bap->stream); bap_update_links(transport); id = bt_bap_stream_disable(bap->stream, bap->linked, func, owner); if (bt_bap_stream_get_type(bap->stream) == BT_BAP_STREAM_TYPE_BCAST) { if (transport->owner == owner) bap_disable_complete(bap->stream, 0x00, 0x00, owner); return 0; } return id; } static void transport_bap_cancel(struct media_transport *transport, guint id) { struct bap_transport *bap = transport->data; if (id == bap->resume_id && bap->resume_id) { g_source_remove(bap->resume_id); bap->resume_id = 0; return; } if (!bap->stream) return; bt_bap_stream_cancel(bap->stream, id); } static void link_set_state(void *data, void *user_data) { struct bt_bap_stream *stream = data; transport_state_t state = PTR_TO_UINT(user_data); struct media_transport *transport; transport = find_transport_by_bap_stream(stream); if (!transport) { error("Unable to find transport"); return; } transport_set_state(transport, state); } static void transport_bap_set_state(struct media_transport *transport, transport_state_t state) { struct bap_transport *bap = transport->data; if (!bap->linked) return; /* Update links */ queue_foreach(bt_bap_stream_io_get_links(bap->stream), link_set_state, UINT_TO_PTR(state)); } static void bap_state_changed(struct bt_bap_stream *stream, uint8_t old_state, uint8_t new_state, void *user_data) { struct media_transport *transport = user_data; struct bap_transport *bap = transport->data; struct media_owner *owner = transport->owner; struct io *io; GIOChannel *chan; GError *err = NULL; int fd; uint16_t imtu, omtu; if (bap->stream != stream) return; DBG("stream %p: %s(%u) -> %s(%u)", stream, bt_bap_stream_statestr(old_state), old_state, bt_bap_stream_statestr(new_state), new_state); switch (new_state) { case BT_BAP_STREAM_STATE_IDLE: case BT_BAP_STREAM_STATE_CONFIG: case BT_BAP_STREAM_STATE_QOS: /* If a request is pending wait it to complete */ if (owner && owner->pending) return; bap_update_links(transport); if (!media_endpoint_is_broadcast(transport->endpoint)) bap_update_qos(transport); else if (bt_bap_stream_io_dir(stream) != BT_BAP_BCAST_SOURCE) bap_update_bcast_qos(transport); transport_update_playing(transport, FALSE); return; case BT_BAP_STREAM_STATE_DISABLING: return; case BT_BAP_STREAM_STATE_ENABLING: if (!bt_bap_stream_get_io(stream)) return; break; case BT_BAP_STREAM_STATE_STREAMING: if ((bt_bap_stream_io_dir(stream) == BT_BAP_BCAST_SOURCE) || (bt_bap_stream_io_dir(stream) == BT_BAP_BCAST_SINK)) bap_update_bcast_qos(transport); break; case BT_BAP_STREAM_STATE_RELEASING: if (bt_bap_stream_io_dir(stream) == BT_BAP_BCAST_SINK) return; break; } io = bt_bap_stream_get_io(stream); if (!io) { error("Unable to get stream IO"); /* TODO: Fail if IO has not been established */ goto done; } fd = io_get_fd(io); if (fd < 0) { error("Unable to get IO fd"); goto done; } chan = g_io_channel_unix_new(fd); if (!bt_io_get(chan, &err, BT_IO_OPT_OMTU, &omtu, BT_IO_OPT_IMTU, &imtu, BT_IO_OPT_INVALID)) { error("%s", err->message); goto done; } g_io_channel_unref(chan); media_transport_set_fd(transport, fd, imtu, omtu); transport_update_playing(transport, TRUE); done: bap_resume_complete(transport); } static void bap_connecting(struct bt_bap_stream *stream, bool state, int fd, void *user_data) { struct media_transport *transport = user_data; struct bap_transport *bap = transport->data; if (bap->stream != stream) return; bap_update_links(transport); } static int transport_bap_get_volume(struct media_transport *transport) { return bt_audio_vcp_get_volume(transport->device); } static int transport_bap_set_volume(struct media_transport *transport, int volume) { if (volume < 0 || volume > 255) return -EINVAL; return bt_audio_vcp_set_volume(transport->device, volume) ? 0 : -EIO; } static void transport_bap_destroy(void *data) { struct bap_transport *bap = data; bt_bap_state_unregister(bt_bap_stream_get_session(bap->stream), bap->state_id); free(bap); } static void *transport_bap_init(struct media_transport *transport, void *stream) { struct bt_bap_qos *qos; struct bap_transport *bap; qos = bt_bap_stream_get_qos(stream); bap = new0(struct bap_transport, 1); bap->stream = stream; bap->qos = *qos; bap->state_id = bt_bap_state_register(bt_bap_stream_get_session(stream), bap_state_changed, bap_connecting, transport, NULL); return bap; } #ifdef HAVE_ASHA static void asha_transport_sync_state(struct media_transport *transport, struct bt_asha_device *asha_dev) { switch (bt_asha_device_get_state(asha_dev)) { case ASHA_STOPPED: transport_set_state(transport, TRANSPORT_STATE_IDLE); break; case ASHA_STARTING: transport_set_state(transport, TRANSPORT_STATE_REQUESTING); break; case ASHA_STARTED: transport_set_state(transport, TRANSPORT_STATE_ACTIVE); break; case ASHA_STOPPING: transport_set_state(transport, TRANSPORT_STATE_SUSPENDING); break; } } static void asha_transport_state_cb(int status, void *user_data) { struct media_owner *owner = user_data; struct media_transport *transport = owner->transport; struct bt_asha_device *asha_dev; enum bt_asha_state_t state; if (!transport) { DBG("Lost owner while connecting, bailing"); return; } asha_dev = transport->data; state = bt_asha_device_get_state(asha_dev); if (state == ASHA_STARTED) { int fd; uint16_t imtu, omtu; gboolean ret; fd = bt_asha_device_get_fd(asha_dev); imtu = bt_asha_device_get_imtu(asha_dev); omtu = bt_asha_device_get_omtu(asha_dev); media_transport_set_fd(transport, fd, imtu, omtu); owner->pending->id = 0; ret = g_dbus_send_reply(btd_get_dbus_connection(), owner->pending->msg, DBUS_TYPE_UNIX_FD, &fd, DBUS_TYPE_UINT16, &imtu, DBUS_TYPE_UINT16, &omtu, DBUS_TYPE_INVALID); if (!ret) { media_transport_remove_owner(transport); return; } media_owner_remove(owner); } else if (state == ASHA_STOPPED) { if (owner->pending) { owner->pending->id = 0; media_request_reply(owner->pending, 0); media_owner_remove(owner); } media_transport_remove_owner(transport); } asha_transport_sync_state(transport, asha_dev); } static guint transport_asha_resume(struct media_transport *transport, struct media_owner *owner) { struct bt_asha_device *asha_dev = transport->data; guint ret; ret = bt_asha_device_start(asha_dev, asha_transport_state_cb, owner); asha_transport_sync_state(transport, asha_dev); return ret > 0 ? ret : 0; } static guint transport_asha_suspend(struct media_transport *transport, struct media_owner *owner) { struct bt_asha_device *asha_dev = transport->data; guint ret = 0; if (owner) { ret = bt_asha_device_stop(asha_dev, asha_transport_state_cb, owner); asha_transport_sync_state(transport, asha_dev); } else { ret = bt_asha_device_stop(asha_dev, NULL, NULL); /* We won't have a callback to set the final state */ transport_set_state(transport, TRANSPORT_STATE_IDLE); } return ret; } static void transport_asha_cancel(struct media_transport *transport, guint id) { struct bt_asha_device *asha_dev = transport->data; enum bt_asha_state_t state = bt_asha_device_get_state(asha_dev); if (id != bt_asha_device_device_get_resume_id(asha_dev)) { /* Not current, ignore */ DBG("Ignoring cancel request for id %d", id); return; } if (state == ASHA_STARTING || state == ASHA_STARTED) { DBG("Cancel requested, stopping"); bt_asha_device_stop(asha_dev, NULL, NULL); /* We won't have a callback to set the final state */ transport_set_state(transport, TRANSPORT_STATE_IDLE); } else if (state == ASHA_STOPPING) { DBG("Cancel requested, resetting transport state"); /* We already dispatched a stop, just reset our state */ bt_asha_device_state_reset(asha_dev); transport_set_state(transport, TRANSPORT_STATE_IDLE); } } static int transport_asha_get_volume(struct media_transport *transport) { struct bt_asha_device *asha_dev = transport->data; int8_t volume; int scaled_volume; volume = bt_asha_device_get_volume(asha_dev); /* Convert -128-0 to 0-127 */ scaled_volume = ((((int) volume) + 128) * 127) / 128; return scaled_volume; } static int transport_asha_set_volume(struct media_transport *transport, int volume) { struct bt_asha_device *asha_dev = transport->data; int scaled_volume; if (volume < 0 || volume > 127) return -EINVAL; /* Convert 0-127 to -128-0 */ scaled_volume = ((((int) volume) * 128) / 127) - 128; return bt_asha_device_set_volume(asha_dev, scaled_volume) ? 0 : -EIO; } static void *transport_asha_init(struct media_transport *transport, void *data) { /* We just store the struct asha_device on the transport */ return data; } #endif /* HAVE_ASHA */ #define TRANSPORT_OPS(_uuid, _props, _set_owner, _remove_owner, _init, \ _resume, _suspend, _cancel, _set_state, _get_stream, \ _get_volume, _set_volume, _set_delay, _update_links, \ _destroy) \ { \ .uuid = _uuid, \ .properties = _props, \ .set_owner = _set_owner, \ .remove_owner = _remove_owner, \ .init = _init, \ .resume = _resume, \ .suspend = _suspend, \ .cancel = _cancel, \ .set_state = _set_state, \ .get_stream = _get_stream, \ .get_volume = _get_volume, \ .set_volume = _set_volume, \ .set_delay = _set_delay, \ .update_links = _update_links, \ .destroy = _destroy \ } #define A2DP_OPS(_uuid, _init, _set_volume, _set_delay, _destroy) \ TRANSPORT_OPS(_uuid, transport_a2dp_properties, NULL, \ transport_a2dp_remove_owner, _init, \ transport_a2dp_resume, transport_a2dp_suspend, \ transport_a2dp_cancel, NULL, \ transport_a2dp_get_stream, transport_a2dp_get_volume, \ _set_volume, _set_delay, NULL, _destroy) #define BAP_OPS(_uuid, _props, _set_owner, _remove_owner, _update_links, \ _set_state) \ TRANSPORT_OPS(_uuid, _props, _set_owner, _remove_owner,\ transport_bap_init, \ transport_bap_resume, transport_bap_suspend, \ transport_bap_cancel, _set_state, \ transport_bap_get_stream, transport_bap_get_volume, \ transport_bap_set_volume, NULL, \ _update_links, transport_bap_destroy) #define BAP_UC_OPS(_uuid) \ BAP_OPS(_uuid, transport_bap_uc_properties, \ transport_bap_set_owner, transport_bap_remove_owner, \ transport_bap_update_links_uc, transport_bap_set_state) #define BAP_BC_OPS(_uuid) \ BAP_OPS(_uuid, transport_bap_bc_properties, NULL, NULL, \ transport_bap_update_links_bc, NULL) #define ASHA_OPS(_uuid) \ TRANSPORT_OPS(_uuid, transport_asha_properties, NULL, NULL, \ transport_asha_init, \ transport_asha_resume, transport_asha_suspend, \ transport_asha_cancel, NULL, NULL, \ transport_asha_get_volume, transport_asha_set_volume, \ NULL, NULL, NULL) static const struct media_transport_ops transport_ops[] = { #ifdef HAVE_A2DP A2DP_OPS(A2DP_SOURCE_UUID, transport_a2dp_src_init, #ifdef HAVE_AVRCP transport_a2dp_src_set_volume, #else NULL, #endif NULL, transport_a2dp_src_destroy), A2DP_OPS(A2DP_SINK_UUID, transport_a2dp_snk_init, #ifdef HAVE_AVRCP transport_a2dp_snk_set_volume, #else NULL, #endif transport_a2dp_snk_set_delay, transport_a2dp_snk_destroy), #endif /* HAVE_A2DP */ BAP_UC_OPS(PAC_SOURCE_UUID), BAP_UC_OPS(PAC_SINK_UUID), BAP_BC_OPS(BCAA_SERVICE_UUID), BAP_BC_OPS(BAA_SERVICE_UUID), #ifdef HAVE_ASHA ASHA_OPS(ASHA_PROFILE_UUID), #endif /* HAVE_ASHA */ }; static const struct media_transport_ops * media_transport_find_ops(const char *uuid) { size_t i; for (i = 0; i < ARRAY_SIZE(transport_ops); i++) { const struct media_transport_ops *ops = &transport_ops[i]; if (!strcasecmp(uuid, ops->uuid)) return ops; } return NULL; } struct media_transport *media_transport_create(struct btd_device *device, const char *remote_endpoint, uint8_t *configuration, size_t size, void *data, void *stream) { struct media_endpoint *endpoint = data; struct media_transport *transport; const struct media_transport_ops *ops; static int fd = 0; transport = g_new0(struct media_transport, 1); if (device) transport->device = device; else transport->adapter = media_endpoint_get_btd_adapter(endpoint); transport->endpoint = endpoint; transport->configuration = util_memdup(configuration, size); transport->size = size; transport->remote_endpoint = g_strdup(remote_endpoint); if (device) transport->path = g_strdup_printf("%s/fd%d", remote_endpoint ? remote_endpoint : device_get_path(device), fd++); else transport->path = g_strdup_printf("%s/fd%d", remote_endpoint ? remote_endpoint : adapter_get_path(transport->adapter), fd++); transport->fd = -1; ops = media_transport_find_ops(media_endpoint_get_uuid(endpoint)); if (!ops) goto fail; transport->ops = ops; if (ops->init) { transport->data = ops->init(transport, stream); if (!transport->data) goto fail; } if (g_dbus_register_interface(btd_get_dbus_connection(), transport->path, MEDIA_TRANSPORT_INTERFACE, transport_methods, NULL, ops->properties, transport, media_transport_free) == FALSE) { error("Could not register transport %s", transport->path); goto fail; } transports = g_slist_append(transports, transport); return transport; fail: media_transport_free(transport); return NULL; } const char *media_transport_get_path(struct media_transport *transport) { return transport->path; } void *media_transport_get_stream(struct media_transport *transport) { if (transport->ops && transport->ops->get_stream) return transport->ops->get_stream(transport); return NULL; } void media_transport_update_delay(struct media_transport *transport, uint16_t delay) { struct a2dp_transport *a2dp = transport->data; /* Check if delay really changed */ if (a2dp->delay == delay) return; a2dp->delay = delay; g_dbus_emit_property_changed(btd_get_dbus_connection(), transport->path, MEDIA_TRANSPORT_INTERFACE, "Delay"); } struct btd_device *media_transport_get_dev(struct media_transport *transport) { return transport->device; } void media_transport_update_volume(struct media_transport *transport, int volume) { if (volume < 0) return; if (media_endpoint_get_sep(transport->endpoint)) { struct a2dp_transport *a2dp = transport->data; if (volume > 127) return; /* Check if volume really changed */ if (a2dp->volume == volume) return; a2dp->volume = volume; } g_dbus_emit_property_changed(btd_get_dbus_connection(), transport->path, MEDIA_TRANSPORT_INTERFACE, "Volume"); } int media_transport_get_device_volume(struct btd_device *dev) { GSList *l; if (dev == NULL) return -1; /* Attempt to locate the transport to get its volume */ for (l = transports; l; l = l->next) { struct media_transport *transport = l->data; if (transport->device != dev) continue; /* Volume is A2DP only */ if (media_endpoint_get_sep(transport->endpoint)) { int volume; if (!media_transport_get_volume(transport, &volume)) return volume; return -1; } } /* If transport volume doesn't exists use device_volume */ return btd_device_get_volume(dev); } void media_transport_update_device_volume(struct btd_device *dev, int volume) { GSList *l; if (dev == NULL || volume < 0) return; /* Attempt to locate the transport to set its volume */ for (l = transports; l; l = l->next) { struct media_transport *transport = l->data; const char *uuid = media_endpoint_get_uuid(transport->endpoint); if (transport->device != dev) continue; /* Volume is A2DP and BAP only */ if (media_endpoint_get_sep(transport->endpoint) || strcasecmp(uuid, PAC_SINK_UUID) || strcasecmp(uuid, PAC_SOURCE_UUID) || strcasecmp(uuid, BAA_SERVICE_UUID)) { media_transport_update_volume(transport, volume); break; } } btd_device_set_volume(dev, volume); } bluez-5.82/profiles/audio/PaxHeaders/sink.h0000644000000000000000000000005014015011623015700 xustar0020 atime=1743516509 20 ctime=1743591284 bluez-5.82/profiles/audio/sink.h0000644000000000000000000000212014015011623015354 0ustar00rootroot/* SPDX-License-Identifier: GPL-2.0-or-later */ /* * * BlueZ - Bluetooth protocol stack for Linux * * Copyright (C) 2006-2010 Nokia Corporation * Copyright (C) 2004-2010 Marcel Holtmann * * */ typedef enum { SINK_STATE_DISCONNECTED, SINK_STATE_CONNECTING, SINK_STATE_CONNECTED, SINK_STATE_PLAYING, } sink_state_t; typedef void (*sink_state_cb) (struct btd_service *service, sink_state_t old_state, sink_state_t new_state, void *user_data); struct btd_service; unsigned int sink_add_state_cb(struct btd_service *service, sink_state_cb cb, void *user_data); gboolean sink_remove_state_cb(unsigned int id); int sink_init(struct btd_service *service); void sink_unregister(struct btd_service *service); gboolean sink_is_active(struct btd_service *service); int sink_connect(struct btd_service *service); gboolean sink_new_stream(struct btd_service *service, struct avdtp *session, struct avdtp_stream *stream); gboolean sink_setup_stream(struct btd_service *service, struct avdtp *session); int sink_disconnect(struct btd_service *service); bluez-5.82/profiles/audio/PaxHeaders/a2dp.h0000644000000000000000000000005014015011623015562 xustar0020 atime=1743516503 20 ctime=1743591284 bluez-5.82/profiles/audio/a2dp.h0000644000000000000000000000577314015011623015257 0ustar00rootroot/* SPDX-License-Identifier: GPL-2.0-or-later */ /* * * BlueZ - Bluetooth protocol stack for Linux * * Copyright (C) 2006-2010 Nokia Corporation * Copyright (C) 2004-2010 Marcel Holtmann * Copyright (C) 2011 BMW Car IT GmbH. All rights reserved. * * */ struct a2dp_sep; struct a2dp_setup; typedef void (*a2dp_endpoint_select_t) (struct a2dp_setup *setup, void *ret, int size); typedef void (*a2dp_endpoint_config_t) (struct a2dp_setup *setup, gboolean ret); struct a2dp_endpoint { const char *(*get_name) (struct a2dp_sep *sep, void *user_data); const char *(*get_path) (struct a2dp_sep *sep, void *user_data); size_t (*get_capabilities) (struct a2dp_sep *sep, uint8_t **capabilities, void *user_data); int (*select_configuration) (struct a2dp_sep *sep, uint8_t *capabilities, size_t length, struct a2dp_setup *setup, a2dp_endpoint_select_t cb, void *user_data); int (*set_configuration) (struct a2dp_sep *sep, uint8_t *configuration, size_t length, struct a2dp_setup *setup, a2dp_endpoint_config_t cb, void *user_data); void (*clear_configuration) (struct a2dp_sep *sep, void *user_data); void (*set_delay) (struct a2dp_sep *sep, uint16_t delay, void *user_data); }; typedef void (*a2dp_discover_cb_t) (struct avdtp *session, GSList *seps, int err, void *user_data); typedef void (*a2dp_select_cb_t) (struct avdtp *session, struct a2dp_sep *sep, GSList *caps, int err, void *user_data); typedef void (*a2dp_config_cb_t) (struct avdtp *session, struct a2dp_sep *sep, struct avdtp_stream *stream, int err, void *user_data); typedef void (*a2dp_stream_cb_t) (struct avdtp *session, int err, void *user_data); struct a2dp_sep *a2dp_add_sep(struct btd_adapter *adapter, uint8_t type, uint8_t codec, gboolean delay_reporting, struct a2dp_endpoint *endpoint, void *user_data, GDestroyNotify destroy, int *err); void a2dp_remove_sep(struct a2dp_sep *sep); unsigned int a2dp_discover(struct avdtp *session, a2dp_discover_cb_t cb, void *user_data); unsigned int a2dp_select_capabilities(struct avdtp *session, uint8_t type, const char *sender, a2dp_select_cb_t cb, void *user_data); unsigned int a2dp_config(struct avdtp *session, struct a2dp_sep *sep, a2dp_config_cb_t cb, GSList *caps, void *user_data); unsigned int a2dp_resume(struct avdtp *session, struct a2dp_sep *sep, a2dp_stream_cb_t cb, void *user_data); unsigned int a2dp_suspend(struct avdtp *session, struct a2dp_sep *sep, a2dp_stream_cb_t cb, void *user_data); gboolean a2dp_cancel(unsigned int id); gboolean a2dp_sep_lock(struct a2dp_sep *sep, struct avdtp *session); gboolean a2dp_sep_unlock(struct a2dp_sep *sep, struct avdtp *session); struct avdtp_stream *a2dp_sep_get_stream(struct a2dp_sep *sep); struct btd_device *a2dp_setup_get_device(struct a2dp_setup *setup); const char *a2dp_setup_remote_path(struct a2dp_setup *setup); struct avdtp *a2dp_avdtp_get(struct btd_device *device); bluez-5.82/profiles/audio/PaxHeaders/media.h0000644000000000000000000000005014643061455016032 xustar0020 atime=1743516503 20 ctime=1743591283 bluez-5.82/profiles/audio/media.h0000644000000000000000000000171614643061455015520 0ustar00rootroot/* SPDX-License-Identifier: GPL-2.0-or-later */ /* * * BlueZ - Bluetooth protocol stack for Linux * * Copyright (C) 2006-2007 Nokia Corporation * Copyright (C) 2004-2009 Marcel Holtmann * * */ struct media_endpoint; typedef void (*media_endpoint_cb_t) (struct media_endpoint *endpoint, void *ret, int size, void *user_data); int media_register(struct btd_adapter *btd_adapter); void media_unregister(struct btd_adapter *btd_adapter); struct a2dp_sep *media_endpoint_get_sep(struct media_endpoint *endpoint); const char *media_endpoint_get_uuid(struct media_endpoint *endpoint); uint8_t media_endpoint_get_codec(struct media_endpoint *endpoint); struct btd_adapter *media_endpoint_get_btd_adapter( struct media_endpoint *endpoint); bool media_endpoint_is_broadcast(struct media_endpoint *endpoint); int8_t media_player_get_device_volume(struct btd_device *device); const struct media_endpoint *media_endpoint_get_asha(void); bluez-5.82/profiles/audio/PaxHeaders/vcp.c0000644000000000000000000000005014766002272015534 xustar0020 atime=1743515578 20 ctime=1743591284 bluez-5.82/profiles/audio/vcp.c0000644000000000000000000001650014766002272015217 0ustar00rootroot// SPDX-License-Identifier: GPL-2.0-or-later /* * * BlueZ - Bluetooth protocol stack for Linux * * Copyright (C) 2022 Intel Corporation. All rights reserved. * * */ #ifdef HAVE_CONFIG_H #include #endif #define _GNU_SOURCE #include #include #include #include #include #include #include #include #include #include "gdbus/gdbus.h" #include "lib/bluetooth.h" #include "lib/hci.h" #include "lib/sdp.h" #include "lib/uuid.h" #include "src/dbus-common.h" #include "src/shared/util.h" #include "src/shared/att.h" #include "src/shared/queue.h" #include "src/shared/gatt-db.h" #include "src/shared/gatt-client.h" #include "src/shared/gatt-server.h" #include "src/shared/vcp.h" #include "btio/btio.h" #include "src/plugin.h" #include "src/adapter.h" #include "src/gatt-database.h" #include "src/device.h" #include "src/profile.h" #include "src/service.h" #include "src/log.h" #include "src/error.h" #include "vcp.h" #include "transport.h" #define VCS_UUID_STR "00001844-0000-1000-8000-00805f9b34fb" #define MEDIA_ENDPOINT_INTERFACE "org.bluez.MediaEndpoint1" struct vcp_data { struct btd_device *device; struct btd_service *service; struct bt_vcp *vcp; }; static struct queue *sessions; static void vcp_debug(const char *str, void *user_data) { DBG_IDX(0xffff, "%s", str); } static int vcp_disconnect(struct btd_service *service) { struct vcp_data *data = btd_service_get_user_data(service); DBG(""); if (!data) { error("VCP service not handled by profile"); return -EINVAL; } bt_vcp_detach(data->vcp); btd_service_disconnecting_complete(service, 0); return 0; } static struct vcp_data *vcp_data_new(struct btd_device *device) { struct vcp_data *data; data = new0(struct vcp_data, 1); data->device = device; return data; } static bool match_data(const void *data, const void *match_data) { const struct vcp_data *vdata = data; const struct bt_vcp *vcp = match_data; return vdata->vcp == vcp; } static void vcp_volume_changed(struct bt_vcp *vcp, uint8_t volume) { struct vcp_data *data = queue_find(sessions, match_data, vcp); if (data) media_transport_update_device_volume(data->device, volume); } static void vcp_data_add(struct vcp_data *data) { DBG("data %p", data); if (queue_find(sessions, NULL, data)) { error("data %p already added", data); return; } bt_vcp_set_debug(data->vcp, vcp_debug, NULL, NULL); bt_vcp_set_volume_callback(data->vcp, vcp_volume_changed); if (!sessions) sessions = queue_new(); queue_push_tail(sessions, data); if (data->service) btd_service_set_user_data(data->service, data); } static bool match_device(const void *data, const void *match_data) { const struct vcp_data *vdata = data; const struct btd_device *device = match_data; return vdata->device == device; } static void vcp_data_free(struct vcp_data *data) { if (data->service) { btd_service_set_user_data(data->service, NULL); bt_vcp_set_user_data(data->vcp, NULL); } bt_vcp_unref(data->vcp); free(data); } static void vcp_data_remove(struct vcp_data *data) { DBG("data %p", data); if (!queue_remove(sessions, data)) return; vcp_data_free(data); if (queue_isempty(sessions)) { queue_destroy(sessions, NULL); sessions = NULL; } } uint8_t bt_audio_vcp_get_volume(struct btd_device *device) { struct vcp_data *data = queue_find(sessions, match_device, device); if (data) return bt_vcp_get_volume(data->vcp); return 0; } bool bt_audio_vcp_set_volume(struct btd_device *device, uint8_t volume) { struct vcp_data *data = queue_find(sessions, match_device, device); if (data) return bt_vcp_set_volume(data->vcp, volume); return FALSE; } static void vcp_remote_client_detached(struct bt_vcp *vcp, void *user_data) { struct vcp_data *data; DBG("%p", vcp); data = queue_find(sessions, match_data, vcp); if (!data) { error("Unable to find vcp session"); return; } vcp_data_remove(data); } static void vcp_remote_client_attached(struct bt_vcp *vcp, void *user_data) { struct vcp_data *data; struct bt_att *att; struct btd_device *device; DBG("%p", vcp); data = queue_find(sessions, match_data, vcp); if (data) return; att = bt_vcp_get_att(vcp); if (!att) return; device = btd_adapter_find_device_by_fd(bt_att_get_fd(att)); if (!device) { error("Unable to find device"); return; } data = vcp_data_new(device); data->vcp = vcp; vcp_data_add(data); } static int vcp_probe(struct btd_service *service) { struct btd_device *device = btd_service_get_device(service); struct btd_adapter *adapter = device_get_adapter(device); struct btd_gatt_database *database = btd_adapter_get_database(adapter); struct vcp_data *data = btd_service_get_user_data(service); char addr[18]; ba2str(device_get_address(device), addr); DBG("%s", addr); /* Ignore, if we were probed for this device already */ if (data) { error("Profile probed twice for the same device!"); return -EINVAL; } data = vcp_data_new(device); data->service = service; data->vcp = bt_vcp_new(btd_gatt_database_get_db(database), btd_device_get_gatt_db(device)); if (!data->vcp) { error("Unable to create VCP instance"); free(data); return -EINVAL; } vcp_data_add(data); bt_vcp_set_user_data(data->vcp, service); return 0; } static void vcp_remove(struct btd_service *service) { struct btd_device *device = btd_service_get_device(service); struct vcp_data *data; char addr[18]; ba2str(device_get_address(device), addr); DBG("%s", addr); data = btd_service_get_user_data(service); if (!data) { error("VCP service not handled by profile"); return; } vcp_data_remove(data); } static int vcp_accept(struct btd_service *service) { struct btd_device *device = btd_service_get_device(service); struct bt_gatt_client *client = btd_device_get_gatt_client(device); struct vcp_data *data = btd_service_get_user_data(service); char addr[18]; ba2str(device_get_address(device), addr); DBG("%s", addr); if (!data) { error("VCP service not handled by profile"); return -EINVAL; } if (!bt_vcp_attach(data->vcp, client)) { error("VCP unable to attach"); return -EINVAL; } btd_service_connecting_complete(service, 0); return 0; } static int vcp_server_probe(struct btd_profile *p, struct btd_adapter *adapter) { struct btd_gatt_database *database = btd_adapter_get_database(adapter); DBG("VCP path %s", adapter_get_path(adapter)); bt_vcp_add_db(btd_gatt_database_get_db(database)); return 0; } static void vcp_server_remove(struct btd_profile *p, struct btd_adapter *adapter) { DBG("VCP remove Adapter"); } static struct btd_profile vcp_profile = { .name = "vcp", .priority = BTD_PROFILE_PRIORITY_MEDIUM, .remote_uuid = VCS_UUID_STR, .device_probe = vcp_probe, .device_remove = vcp_remove, .accept = vcp_accept, .disconnect = vcp_disconnect, .adapter_probe = vcp_server_probe, .adapter_remove = vcp_server_remove, .experimental = true, }; static unsigned int vcp_id = 0; static int vcp_init(void) { int err; err = btd_profile_register(&vcp_profile); if (err) return err; vcp_id = bt_vcp_register(vcp_remote_client_attached, vcp_remote_client_detached, NULL); return 0; } static void vcp_exit(void) { btd_profile_unregister(&vcp_profile); bt_vcp_unregister(vcp_id); } BLUETOOTH_PLUGIN_DEFINE(vcp, VERSION, BLUETOOTH_PLUGIN_PRIORITY_DEFAULT, vcp_init, vcp_exit) bluez-5.82/profiles/audio/PaxHeaders/a2dp.c0000644000000000000000000000005014772767672015615 xustar0020 atime=1743515579 20 ctime=1743591284 bluez-5.82/profiles/audio/a2dp.c0000644000000000000000000023402314772767672015302 0ustar00rootroot// SPDX-License-Identifier: GPL-2.0-or-later /* * * BlueZ - Bluetooth protocol stack for Linux * * Copyright (C) 2006-2010 Nokia Corporation * Copyright (C) 2004-2010 Marcel Holtmann * Copyright (C) 2011 BMW Car IT GmbH. All rights reserved. * * */ #ifdef HAVE_CONFIG_H #include #endif #define _GNU_SOURCE #include #include #include #include #include #include #include "lib/bluetooth.h" #include "lib/sdp.h" #include "lib/sdp_lib.h" #include "lib/uuid.h" #include "gdbus/gdbus.h" #include "src/btd.h" #include "src/plugin.h" #include "src/adapter.h" #include "src/device.h" #include "src/dbus-common.h" #include "src/error.h" #include "src/profile.h" #include "src/service.h" #include "src/log.h" #include "src/sdpd.h" #include "src/textfile.h" #include "src/shared/queue.h" #include "src/shared/timeout.h" #include "src/shared/util.h" #include "btio/btio.h" #include "avdtp.h" #include "sink.h" #include "source.h" #include "a2dp.h" #include "a2dp-codecs.h" #include "media.h" /* The duration that streams without users are allowed to stay in * STREAMING state. */ #define SUSPEND_TIMEOUT 5 #define AVDTP_PSM 25 #define MEDIA_ENDPOINT_INTERFACE "org.bluez.MediaEndpoint1" struct a2dp_sep { struct a2dp_server *server; struct a2dp_endpoint *endpoint; uint8_t type; uint8_t codec; struct avdtp_local_sep *lsep; struct avdtp *session; struct avdtp_stream *stream; unsigned int suspend_timer; gboolean delay_reporting; gboolean locked; gboolean suspending; gboolean starting; void *user_data; GDestroyNotify destroy; }; struct a2dp_setup_cb { struct a2dp_setup *setup; a2dp_discover_cb_t discover_cb; a2dp_select_cb_t select_cb; a2dp_config_cb_t config_cb; a2dp_stream_cb_t resume_cb; a2dp_stream_cb_t suspend_cb; guint source_id; void *user_data; unsigned int id; }; struct a2dp_setup { struct a2dp_channel *chan; struct avdtp *session; struct queue *eps; struct a2dp_sep *sep; struct a2dp_remote_sep *rsep; struct avdtp_stream *stream; struct avdtp_error *err; avdtp_set_configuration_cb setconf_cb; GSList *seps; GSList *caps; gboolean reconfigure; gboolean start; GSList *cb; GIOChannel *io; guint id; int ref; }; struct a2dp_server { struct btd_adapter *adapter; GSList *sinks; GSList *sources; uint32_t source_record_id; uint32_t sink_record_id; gboolean sink_enabled; gboolean source_enabled; uint64_t seid_pool; GIOChannel *io; struct queue *seps; struct queue *channels; }; struct a2dp_remote_sep { struct a2dp_channel *chan; char *path; struct avdtp_remote_sep *sep; bool from_cache; }; struct a2dp_last_used { struct a2dp_sep *lsep; struct a2dp_remote_sep *rsep; }; struct a2dp_channel { struct a2dp_server *server; struct btd_device *device; GIOChannel *io; guint io_id; unsigned int state_id; unsigned int auth_id; struct avdtp *session; struct queue *seps; struct a2dp_last_used *last_used; }; static GSList *servers = NULL; static GSList *setups = NULL; static unsigned int cb_id = 0; static struct a2dp_setup *setup_ref(struct a2dp_setup *setup) { setup->ref++; DBG("%p: ref=%d", setup, setup->ref); return setup; } static bool match_by_session(const void *data, const void *user_data) { const struct a2dp_channel *chan = data; const struct avdtp *session = user_data; return chan->session == session; } static struct a2dp_channel *find_channel(struct avdtp *session) { GSList *l; for (l = servers; l; l = g_slist_next(l)) { struct a2dp_server *server = l->data; struct a2dp_channel *chan; chan = queue_find(server->channels, match_by_session, session); if (chan) return chan; } return NULL; } static struct a2dp_setup *setup_new(struct avdtp *session) { struct a2dp_setup *setup; struct a2dp_channel *chan; chan = find_channel(session); if (!chan) return NULL; setup = g_new0(struct a2dp_setup, 1); setup->session = avdtp_ref(session); setup->chan = find_channel(session); setups = g_slist_append(setups, setup); return setup; } static void setup_free(struct a2dp_setup *s) { DBG("%p", s); if (s->io) { g_io_channel_shutdown(s->io, TRUE, NULL); g_io_channel_unref(s->io); } if (s->id) g_source_remove(s->id); queue_destroy(s->eps, NULL); setups = g_slist_remove(setups, s); if (s->session) avdtp_unref(s->session); g_slist_free_full(s->cb, g_free); g_slist_free_full(s->caps, g_free); g_free(s->err); g_free(s); } static void setup_unref(struct a2dp_setup *setup) { setup->ref--; DBG("%p: ref=%d", setup, setup->ref); if (setup->ref > 0) return; setup_free(setup); } static struct a2dp_setup_cb *setup_cb_new(struct a2dp_setup *setup) { struct a2dp_setup_cb *cb; cb = g_new0(struct a2dp_setup_cb, 1); cb->setup = setup; cb->id = ++cb_id; return cb; } static struct a2dp_setup_cb *setup_cb_add(struct a2dp_setup *setup) { struct a2dp_setup_cb *cb; cb = setup_cb_new(setup); setup->cb = g_slist_append(setup->cb, cb); return cb; } static void setup_cb_free(struct a2dp_setup_cb *cb) { struct a2dp_setup *setup = cb->setup; if (cb->source_id) g_source_remove(cb->source_id); setup->cb = g_slist_remove(setup->cb, cb); setup_unref(cb->setup); g_free(cb); } static void setup_error_set(struct a2dp_setup *setup, struct avdtp_error *err) { if (!err) { g_free(setup->err); setup->err = NULL; } else { if (!setup->err) setup->err = g_new0(struct avdtp_error, 1); memcpy(setup->err, err, sizeof(struct avdtp_error)); } } static void setup_error_init(struct a2dp_setup *setup, uint8_t type, int id) { struct avdtp_error err; avdtp_error_init(&err, type, id); setup_error_set(setup, &err); } static void finalize_setup_errno(struct a2dp_setup *s, int err, GSourceFunc cb1, ...) { GSourceFunc finalize; va_list args; if (err < 0) setup_error_init(s, AVDTP_ERRNO, -err); va_start(args, cb1); finalize = cb1; setup_ref(s); while (finalize != NULL) { finalize(s); finalize = va_arg(args, GSourceFunc); } setup_unref(s); va_end(args); } static int error_to_errno(struct avdtp_error *err) { int perr; if (!err) return 0; if (avdtp_error_category(err) != AVDTP_ERRNO) return -EIO; perr = avdtp_error_posix_errno(err); switch (perr) { case EHOSTDOWN: case ECONNABORTED: case EBADE: case ECONNREFUSED: return -perr; default: /* * An unexpect error has occurred setup may be attempted again. */ return -EAGAIN; } } static gboolean finalize_config(gpointer data) { struct a2dp_setup *s = data; GSList *l; struct avdtp_stream *stream = s->err ? NULL : s->stream; for (l = s->cb; l != NULL; ) { struct a2dp_setup_cb *cb = l->data; l = l->next; if (!cb->config_cb) continue; cb->config_cb(s->session, s->sep, stream, error_to_errno(s->err), cb->user_data); setup_cb_free(cb); } return FALSE; } static gboolean finalize_resume(gpointer data) { struct a2dp_setup *s = data; GSList *l; for (l = s->cb; l != NULL; ) { struct a2dp_setup_cb *cb = l->data; l = l->next; if (!cb->resume_cb) continue; cb->resume_cb(s->session, error_to_errno(s->err), cb->user_data); setup_cb_free(cb); } return FALSE; } static gboolean finalize_suspend(gpointer data) { struct a2dp_setup *s = data; GSList *l; for (l = s->cb; l != NULL; ) { struct a2dp_setup_cb *cb = l->data; l = l->next; if (!cb->suspend_cb) continue; cb->suspend_cb(s->session, error_to_errno(s->err), cb->user_data); setup_cb_free(cb); } return FALSE; } static void finalize_select(struct a2dp_setup *s) { GSList *l; for (l = s->cb; l != NULL; ) { struct a2dp_setup_cb *cb = l->data; l = l->next; if (!cb->select_cb) continue; cb->select_cb(s->session, s->sep, s->caps, error_to_errno(s->err), cb->user_data); setup_cb_free(cb); } } static void finalize_discover(struct a2dp_setup *s) { GSList *l; for (l = s->cb; l != NULL; ) { struct a2dp_setup_cb *cb = l->data; l = l->next; if (!cb->discover_cb) continue; cb->discover_cb(s->session, s->seps, error_to_errno(s->err), cb->user_data); setup_cb_free(cb); } } static gboolean finalize_all(gpointer data) { struct a2dp_setup *s = data; struct avdtp_stream *stream = s->err ? NULL : s->stream; GSList *l; for (l = s->cb; l != NULL; ) { struct a2dp_setup_cb *cb = l->data; l = l->next; if (cb->discover_cb) { cb->discover_cb(s->session, s->seps, error_to_errno(s->err), cb->user_data); } else if (cb->select_cb) { cb->select_cb(s->session, s->sep, s->caps, error_to_errno(s->err), cb->user_data); } else if (cb->suspend_cb) { cb->suspend_cb(s->session, error_to_errno(s->err), cb->user_data); } else if (cb->resume_cb) { cb->resume_cb(s->session, error_to_errno(s->err), cb->user_data); } else if (cb->config_cb) { cb->config_cb(s->session, s->sep, stream, error_to_errno(s->err), cb->user_data); } else warn("setup_cb doesn't have any callback function"); setup_cb_free(cb); } return FALSE; } static struct a2dp_setup *find_setup_by_session(struct avdtp *session) { GSList *l; for (l = setups; l != NULL; l = l->next) { struct a2dp_setup *setup = l->data; if (setup->session == session) return setup; } return NULL; } static struct a2dp_setup *a2dp_setup_get(struct avdtp *session) { struct a2dp_setup *setup; setup = find_setup_by_session(session); if (!setup) { setup = setup_new(session); if (!setup) return NULL; } return setup_ref(setup); } static struct a2dp_setup *find_setup_by_stream(struct avdtp_stream *stream) { GSList *l; for (l = setups; l != NULL; l = l->next) { struct a2dp_setup *setup = l->data; if (setup->stream == stream) return setup; } return NULL; } static void stream_state_changed(struct avdtp_stream *stream, avdtp_state_t old_state, avdtp_state_t new_state, struct avdtp_error *err, void *user_data) { struct a2dp_sep *sep = user_data; if (new_state == AVDTP_STATE_OPEN) { struct a2dp_setup *setup; int err; setup = find_setup_by_stream(stream); if (!setup || !setup->start || setup->err) return; setup->start = FALSE; err = avdtp_start(setup->session, stream); if (err < 0 && err != -EINPROGRESS) { error("avdtp_start: %s (%d)", strerror(-err), -err); finalize_setup_errno(setup, err, finalize_resume, NULL); return; } sep->starting = TRUE; return; } if (new_state != AVDTP_STATE_IDLE) return; if (sep->suspend_timer) { timeout_remove(sep->suspend_timer); sep->suspend_timer = 0; } if (sep->session) { avdtp_unref(sep->session); sep->session = NULL; } sep->stream = NULL; if (sep->endpoint && sep->endpoint->clear_configuration) sep->endpoint->clear_configuration(sep, sep->user_data); } static gboolean auto_config(gpointer data) { struct a2dp_setup *setup = data; struct btd_device *dev = NULL; struct btd_service *service; /* Check if configuration was aborted */ if (setup->sep->stream == NULL) return FALSE; if (setup->err != NULL) goto done; dev = avdtp_get_device(setup->session); avdtp_stream_add_cb(setup->session, setup->stream, stream_state_changed, setup->sep); if (setup->sep->type == AVDTP_SEP_TYPE_SOURCE) { service = btd_device_get_service(dev, A2DP_SINK_UUID); sink_new_stream(service, setup->session, setup->stream); } else { service = btd_device_get_service(dev, A2DP_SOURCE_UUID); source_new_stream(service, setup->session, setup->stream); } done: if (setup->setconf_cb) setup->setconf_cb(setup->session, setup->stream, setup->err); finalize_config(setup); setup_error_set(setup, NULL); setup_unref(setup); return FALSE; } static void endpoint_setconf_cb(struct a2dp_setup *setup, gboolean ret) { if (ret == FALSE) setup_error_init(setup, AVDTP_MEDIA_CODEC, AVDTP_UNSUPPORTED_CONFIGURATION); auto_config(setup); setup_unref(setup); } static gboolean endpoint_match_codec_ind(struct avdtp *session, struct avdtp_media_codec_capability *codec, void *user_data) { struct a2dp_sep *sep = user_data; a2dp_vendor_codec_t *remote_codec; a2dp_vendor_codec_t *local_codec; uint8_t *capabilities; size_t length; if (codec->media_codec_type != A2DP_CODEC_VENDOR) return TRUE; if (sep->endpoint == NULL) return FALSE; length = sep->endpoint->get_capabilities(sep, &capabilities, sep->user_data); if (length < sizeof(a2dp_vendor_codec_t)) return FALSE; local_codec = (a2dp_vendor_codec_t *) capabilities; remote_codec = (a2dp_vendor_codec_t *) codec->data; if (A2DP_GET_VENDOR_ID(*remote_codec) != A2DP_GET_VENDOR_ID(*local_codec)) return FALSE; if (A2DP_GET_CODEC_ID(*remote_codec) != A2DP_GET_CODEC_ID(*local_codec)) return FALSE; DBG("vendor 0x%08x codec 0x%04x", A2DP_GET_VENDOR_ID(*remote_codec), A2DP_GET_CODEC_ID(*remote_codec)); return TRUE; } static void reverse_discover(struct avdtp *session, GSList *seps, int err, void *user_data) { DBG("err %d", err); } static gboolean endpoint_setconf_ind(struct avdtp *session, struct avdtp_local_sep *sep, struct avdtp_stream *stream, GSList *caps, avdtp_set_configuration_cb cb, void *user_data) { struct a2dp_sep *a2dp_sep = user_data; struct a2dp_setup *setup; if (a2dp_sep->type == AVDTP_SEP_TYPE_SINK) DBG("Sink %p: Set_Configuration_Ind", sep); else DBG("Source %p: Set_Configuration_Ind", sep); setup = a2dp_setup_get(session); if (!setup) return FALSE; a2dp_sep->stream = stream; setup->sep = a2dp_sep; setup->stream = stream; setup->setconf_cb = cb; for (; caps != NULL; caps = g_slist_next(caps)) { struct avdtp_service_capability *cap = caps->data; struct avdtp_media_codec_capability *codec; gboolean ret; if (cap->category == AVDTP_DELAY_REPORTING && !a2dp_sep->delay_reporting) { setup_error_init(setup, AVDTP_DELAY_REPORTING, AVDTP_UNSUPPORTED_CONFIGURATION); goto done; } if (cap->category != AVDTP_MEDIA_CODEC) continue; codec = (struct avdtp_media_codec_capability *) cap->data; if (codec->media_codec_type != a2dp_sep->codec) { setup_error_init(setup, AVDTP_MEDIA_CODEC, AVDTP_UNSUPPORTED_CONFIGURATION); goto done; } ret = a2dp_sep->endpoint->set_configuration(a2dp_sep, codec->data, cap->length - sizeof(*codec), setup_ref(setup), endpoint_setconf_cb, a2dp_sep->user_data); if (ret == 0) { /* Attempt to reverse discover if there are no remote * SEPs. */ if (queue_isempty(setup->chan->seps)) a2dp_discover(session, reverse_discover, NULL); return TRUE; } setup_error_init(setup, AVDTP_MEDIA_CODEC, AVDTP_UNSUPPORTED_CONFIGURATION); setup_unref(setup); break; } done: g_idle_add(auto_config, setup); return TRUE; } static gboolean endpoint_getcap_ind(struct avdtp *session, struct avdtp_local_sep *sep, gboolean get_all, GSList **caps, uint8_t *err, void *user_data) { struct a2dp_sep *a2dp_sep = user_data; struct avdtp_service_capability *media_transport, *media_codec; struct avdtp_media_codec_capability *codec_caps; uint8_t *capabilities; size_t length; if (a2dp_sep->type == AVDTP_SEP_TYPE_SINK) DBG("Sink %p: Get_Capability_Ind", sep); else DBG("Source %p: Get_Capability_Ind", sep); *caps = NULL; media_transport = avdtp_service_cap_new(AVDTP_MEDIA_TRANSPORT, NULL, 0); *caps = g_slist_append(*caps, media_transport); length = a2dp_sep->endpoint->get_capabilities(a2dp_sep, &capabilities, a2dp_sep->user_data); codec_caps = g_malloc0(sizeof(*codec_caps) + length); codec_caps->media_type = AVDTP_MEDIA_TYPE_AUDIO; codec_caps->media_codec_type = a2dp_sep->codec; memcpy(codec_caps->data, capabilities, length); media_codec = avdtp_service_cap_new(AVDTP_MEDIA_CODEC, codec_caps, sizeof(*codec_caps) + length); *caps = g_slist_append(*caps, media_codec); g_free(codec_caps); if (get_all) { struct avdtp_service_capability *delay_reporting; delay_reporting = avdtp_service_cap_new(AVDTP_DELAY_REPORTING, NULL, 0); *caps = g_slist_append(*caps, delay_reporting); } return TRUE; } static void endpoint_open_cb(struct a2dp_setup *setup, gboolean ret) { int err = error_to_errno(setup->err); if (ret == FALSE) { setup->stream = NULL; finalize_setup_errno(setup, -EPERM, finalize_config, NULL); goto done; } if (err == 0) err = avdtp_open(setup->session, setup->stream); if (err == 0) goto done; error("avdtp_open %s (%d)", strerror(-err), -err); setup->stream = NULL; finalize_setup_errno(setup, err, finalize_config, NULL); done: setup_unref(setup); } static bool match_remote_sep(const void *data, const void *user_data) { const struct a2dp_remote_sep *sep = data; const struct avdtp_remote_sep *rsep = user_data; return sep->sep == rsep; } static void store_remote_sep(void *data, void *user_data) { struct a2dp_remote_sep *sep = data; GKeyFile *key_file = user_data; char seid[4], value[256]; struct avdtp_service_capability *service = avdtp_get_codec(sep->sep); struct avdtp_media_codec_capability *codec; unsigned int i; ssize_t offset; if (!service) return; codec = (void *) service->data; sprintf(seid, "%02hhx", avdtp_get_seid(sep->sep)); offset = sprintf(value, "%02hhx:%02hhx:%02hhx:", avdtp_get_type(sep->sep), codec->media_codec_type, avdtp_get_delay_reporting(sep->sep)); for (i = 0; i < service->length - sizeof(*codec); i++) offset += sprintf(value + offset, "%02hhx", codec->data[i]); g_key_file_set_string(key_file, "Endpoints", seid, value); } static void store_remote_seps(struct a2dp_channel *chan) { struct btd_device *device = chan->device; char filename[PATH_MAX]; char dst_addr[18]; GKeyFile *key_file; GError *gerr = NULL; char *data; gsize length = 0; ba2str(device_get_address(device), dst_addr); create_filename(filename, PATH_MAX, "/%s/cache/%s", btd_adapter_get_storage_dir(device_get_adapter(device)), dst_addr); key_file = g_key_file_new(); if (!g_key_file_load_from_file(key_file, filename, 0, &gerr)) { error("Unable to load key file from %s: (%s)", filename, gerr->message); g_clear_error(&gerr); } data = g_key_file_get_string(key_file, "Endpoints", "LastUsed", NULL); /* Remove current endpoints since it might have changed */ g_key_file_remove_group(key_file, "Endpoints", NULL); queue_foreach(chan->seps, store_remote_sep, key_file); if (data) { g_key_file_set_string(key_file, "Endpoints", "LastUsed", data); g_free(data); } data = g_key_file_to_data(key_file, &length, NULL); if (!g_file_set_contents(filename, data, length, &gerr)) { error("Unable set contents for %s: (%s)", filename, gerr->message); g_error_free(gerr); } g_free(data); g_key_file_free(key_file); } static void invalidate_remote_cache(struct a2dp_setup *setup, struct avdtp_error *err) { if (err->category == AVDTP_ERRNO) return; /* Attempt to unregister Remote SEP if configuration fails and it was * loaded from cache. */ if (setup->rsep && setup->rsep->from_cache) { warn("Invalidating Remote SEP from cache"); avdtp_unregister_remote_sep(setup->session, setup->rsep->sep); /* Update cache */ store_remote_seps(setup->chan); /* Set error to -EAGAIN so the likes of policy plugin can * reattempt to connect. */ setup_error_init(setup, AVDTP_ERRNO, -EAGAIN); } } static void setconf_cfm(struct avdtp *session, struct avdtp_local_sep *sep, struct avdtp_stream *stream, struct avdtp_error *err, void *user_data) { struct a2dp_sep *a2dp_sep = user_data; struct a2dp_setup *setup; struct btd_device *dev; struct btd_service *service; int ret; if (a2dp_sep->type == AVDTP_SEP_TYPE_SINK) DBG("Sink %p: Set_Configuration_Cfm", sep); else DBG("Source %p: Set_Configuration_Cfm", sep); setup = find_setup_by_session(session); if (err) { if (setup) { setup_ref(setup); setup_error_set(setup, err); invalidate_remote_cache(setup, err); finalize_config(setup); setup_error_set(setup, NULL); setup_unref(setup); } return; } avdtp_stream_add_cb(session, stream, stream_state_changed, a2dp_sep); a2dp_sep->stream = stream; if (!setup) return; dev = avdtp_get_device(session); /* Notify D-Bus interface of the new stream */ if (a2dp_sep->type == AVDTP_SEP_TYPE_SOURCE) { service = btd_device_get_service(dev, A2DP_SINK_UUID); sink_new_stream(service, session, setup->stream); } else { service = btd_device_get_service(dev, A2DP_SOURCE_UUID); source_new_stream(service, session, setup->stream); } /* Notify Endpoint */ if (a2dp_sep->endpoint) { struct avdtp_service_capability *service; struct avdtp_media_codec_capability *codec; int err; service = avdtp_stream_get_codec(stream); codec = (struct avdtp_media_codec_capability *) service->data; err = a2dp_sep->endpoint->set_configuration(a2dp_sep, codec->data, service->length - sizeof(*codec), setup_ref(setup), endpoint_open_cb, a2dp_sep->user_data); if (err == 0) return; setup->stream = NULL; finalize_setup_errno(setup, -EPERM, finalize_config, NULL); setup_unref(setup); return; } ret = avdtp_open(session, stream); if (ret < 0) { error("avdtp_open %s (%d)", strerror(-ret), -ret); setup->stream = NULL; finalize_setup_errno(setup, ret, finalize_config, NULL); } } static gboolean getconf_ind(struct avdtp *session, struct avdtp_local_sep *sep, uint8_t *err, void *user_data) { struct a2dp_sep *a2dp_sep = user_data; if (a2dp_sep->type == AVDTP_SEP_TYPE_SINK) DBG("Sink %p: Get_Configuration_Ind", sep); else DBG("Source %p: Get_Configuration_Ind", sep); return TRUE; } static void getconf_cfm(struct avdtp *session, struct avdtp_local_sep *sep, struct avdtp_stream *stream, struct avdtp_error *err, void *user_data) { struct a2dp_sep *a2dp_sep = user_data; if (a2dp_sep->type == AVDTP_SEP_TYPE_SINK) DBG("Sink %p: Set_Configuration_Cfm", sep); else DBG("Source %p: Set_Configuration_Cfm", sep); } static void store_last_used(struct a2dp_channel *chan, uint8_t lseid, uint8_t rseid) { GKeyFile *key_file; GError *gerr = NULL; char filename[PATH_MAX]; char dst_addr[18]; char value[6]; char *data; size_t len = 0; ba2str(device_get_address(chan->device), dst_addr); create_filename(filename, PATH_MAX, "/%s/cache/%s", btd_adapter_get_storage_dir(device_get_adapter(chan->device)), dst_addr); key_file = g_key_file_new(); if (!g_key_file_load_from_file(key_file, filename, 0, &gerr)) { error("Unable to load key file from %s: (%s)", filename, gerr->message); g_clear_error(&gerr); } sprintf(value, "%02hhx:%02hhx", lseid, rseid); g_key_file_set_string(key_file, "Endpoints", "LastUsed", value); data = g_key_file_to_data(key_file, &len, NULL); if (!g_file_set_contents(filename, data, len, &gerr)) { error("Unable set contents for %s: (%s)", filename, gerr->message); g_error_free(gerr); } g_free(data); g_key_file_free(key_file); } static void add_last_used(struct a2dp_channel *chan, struct a2dp_sep *lsep, struct a2dp_remote_sep *rsep) { if (!chan->last_used) chan->last_used = new0(struct a2dp_last_used, 1); chan->last_used->lsep = lsep; chan->last_used->rsep = rsep; } static void update_last_used(struct a2dp_channel *chan, struct a2dp_sep *lsep, struct avdtp_stream *stream) { struct avdtp_remote_sep *rsep; struct a2dp_remote_sep *sep; rsep = avdtp_stream_get_remote_sep(stream); sep = queue_find(chan->seps, match_remote_sep, rsep); if (!sep) { error("Unable to find remote SEP"); return; } /* Check if already stored then skip */ if (chan->last_used && (chan->last_used->lsep == lsep && chan->last_used->rsep == sep)) return; add_last_used(chan, lsep, sep); store_last_used(chan, avdtp_sep_get_seid(lsep->lsep), avdtp_get_seid(rsep)); } static gboolean open_ind(struct avdtp *session, struct avdtp_local_sep *sep, struct avdtp_stream *stream, uint8_t *err, void *user_data) { struct a2dp_sep *a2dp_sep = user_data; struct a2dp_setup *setup; if (a2dp_sep->type == AVDTP_SEP_TYPE_SINK) DBG("Sink %p: Open_Ind", sep); else DBG("Source %p: Open_Ind", sep); setup = a2dp_setup_get(session); if (!setup) return FALSE; setup->stream = stream; if (!err && setup->chan) update_last_used(setup->chan, a2dp_sep, stream); if (setup->reconfigure) setup->reconfigure = FALSE; finalize_config(setup); return TRUE; } static void open_cfm(struct avdtp *session, struct avdtp_local_sep *sep, struct avdtp_stream *stream, struct avdtp_error *err, void *user_data) { struct a2dp_sep *a2dp_sep = user_data; struct a2dp_setup *setup; if (a2dp_sep->type == AVDTP_SEP_TYPE_SINK) DBG("Sink %p: Open_Cfm", sep); else DBG("Source %p: Open_Cfm", sep); setup = find_setup_by_session(session); if (!setup) return; if (setup->reconfigure) setup->reconfigure = FALSE; if (err) { setup->stream = NULL; setup_error_set(setup, err); if (setup->start) finalize_resume(setup); } else if (setup->chan) update_last_used(setup->chan, a2dp_sep, stream); finalize_config(setup); return; } static bool suspend_timeout(struct a2dp_sep *sep) { if (avdtp_suspend(sep->session, sep->stream) == 0) sep->suspending = TRUE; sep->suspend_timer = 0; avdtp_unref(sep->session); sep->session = NULL; return FALSE; } static gboolean start_ind(struct avdtp *session, struct avdtp_local_sep *sep, struct avdtp_stream *stream, uint8_t *err, void *user_data) { struct a2dp_sep *a2dp_sep = user_data; struct a2dp_setup *setup; if (a2dp_sep->type == AVDTP_SEP_TYPE_SINK) DBG("Sink %p: Start_Ind", sep); else DBG("Source %p: Start_Ind", sep); if (!a2dp_sep->locked) { a2dp_sep->session = avdtp_ref(session); a2dp_sep->suspend_timer = timeout_add_seconds(SUSPEND_TIMEOUT, (timeout_func_t) suspend_timeout, a2dp_sep, NULL); } if (!a2dp_sep->starting) return TRUE; a2dp_sep->starting = FALSE; setup = find_setup_by_session(session); if (setup) finalize_resume(setup); return TRUE; } static void start_cfm(struct avdtp *session, struct avdtp_local_sep *sep, struct avdtp_stream *stream, struct avdtp_error *err, void *user_data) { struct a2dp_sep *a2dp_sep = user_data; struct a2dp_setup *setup; if (a2dp_sep->type == AVDTP_SEP_TYPE_SINK) DBG("Sink %p: Start_Cfm", sep); else DBG("Source %p: Start_Cfm", sep); a2dp_sep->starting = FALSE; setup = find_setup_by_session(session); if (!setup) return; if (err) { setup->stream = NULL; setup_error_set(setup, err); } finalize_resume(setup); } static gboolean suspend_ind(struct avdtp *session, struct avdtp_local_sep *sep, struct avdtp_stream *stream, uint8_t *err, void *user_data) { struct a2dp_sep *a2dp_sep = user_data; struct a2dp_setup *setup; gboolean start; int start_err; if (a2dp_sep->type == AVDTP_SEP_TYPE_SINK) DBG("Sink %p: Suspend_Ind", sep); else DBG("Source %p: Suspend_Ind", sep); if (a2dp_sep->suspend_timer) { timeout_remove(a2dp_sep->suspend_timer); a2dp_sep->suspend_timer = 0; avdtp_unref(a2dp_sep->session); a2dp_sep->session = NULL; } if (!a2dp_sep->suspending) return TRUE; a2dp_sep->suspending = FALSE; setup = find_setup_by_session(session); if (!setup) return TRUE; start = setup->start; setup->start = FALSE; finalize_suspend(setup); if (!start) return TRUE; start_err = avdtp_start(session, a2dp_sep->stream); if (start_err < 0 && start_err != -EINPROGRESS) { error("avdtp_start: %s (%d)", strerror(-start_err), -start_err); finalize_setup_errno(setup, start_err, finalize_resume, NULL); } return TRUE; } static void suspend_cfm(struct avdtp *session, struct avdtp_local_sep *sep, struct avdtp_stream *stream, struct avdtp_error *err, void *user_data) { struct a2dp_sep *a2dp_sep = user_data; struct a2dp_setup *setup; gboolean start; int start_err; if (a2dp_sep->type == AVDTP_SEP_TYPE_SINK) DBG("Sink %p: Suspend_Cfm", sep); else DBG("Source %p: Suspend_Cfm", sep); a2dp_sep->suspending = FALSE; setup = find_setup_by_session(session); if (!setup) return; start = setup->start; setup->start = FALSE; if (err) { setup->stream = NULL; setup_error_set(setup, err); } finalize_suspend(setup); if (!start) return; if (err) { finalize_resume(setup); return; } start_err = avdtp_start(session, a2dp_sep->stream); if (start_err < 0 && start_err != -EINPROGRESS) { error("avdtp_start: %s (%d)", strerror(-start_err), -start_err); finalize_setup_errno(setup, start_err, finalize_suspend, NULL); } } static gboolean close_ind(struct avdtp *session, struct avdtp_local_sep *sep, struct avdtp_stream *stream, uint8_t *err, void *user_data) { struct a2dp_sep *a2dp_sep = user_data; struct a2dp_setup *setup; if (a2dp_sep->type == AVDTP_SEP_TYPE_SINK) DBG("Sink %p: Close_Ind", sep); else DBG("Source %p: Close_Ind", sep); setup = find_setup_by_session(session); if (!setup) return TRUE; finalize_setup_errno(setup, -ECONNRESET, finalize_suspend, finalize_resume, NULL); return TRUE; } static struct a2dp_remote_sep *find_remote_sep(struct a2dp_channel *chan, struct a2dp_sep *sep) { struct avdtp_remote_sep *rsep; if (!chan || !sep) return NULL; rsep = avdtp_find_remote_sep(chan->session, sep->lsep); return queue_find(chan->seps, match_remote_sep, rsep); } static gboolean a2dp_reconfigure(gpointer data) { struct a2dp_setup *setup = data; struct a2dp_sep *sep = setup->sep; int posix_err; struct avdtp_media_codec_capability *rsep_codec; struct avdtp_service_capability *cap; setup->id = 0; if (setup->err) { posix_err = error_to_errno(setup->err); goto failed; } if (!sep->lsep) { error("no valid local SEP"); posix_err = -EINVAL; goto failed; } if (setup->rsep) { cap = avdtp_get_codec(setup->rsep->sep); rsep_codec = (struct avdtp_media_codec_capability *) cap->data; /* Check that codec really match after closing */ if (sep->codec != rsep_codec->media_codec_type) setup->rsep = NULL; } if (!setup->rsep) setup->rsep = find_remote_sep(setup->chan, sep); if (!setup->rsep) { error("unable to find remote SEP"); posix_err = -EINVAL; goto failed; } posix_err = avdtp_set_configuration(setup->session, setup->rsep->sep, sep->lsep, setup->caps, &setup->stream); if (posix_err < 0) { error("avdtp_set_configuration: %s", strerror(-posix_err)); goto failed; } return FALSE; failed: finalize_setup_errno(setup, posix_err, finalize_config, NULL); return FALSE; } static bool setup_reconfigure(struct a2dp_setup *setup) { if (!setup->reconfigure || setup->id) return false; DBG("%p", setup); setup->id = g_idle_add(a2dp_reconfigure, setup); setup->reconfigure = FALSE; return true; } static struct a2dp_remote_sep *get_remote_sep(struct a2dp_channel *chan, struct avdtp_stream *stream) { struct avdtp_remote_sep *rsep; rsep = avdtp_stream_get_remote_sep(stream); return queue_find(chan->seps, match_remote_sep, rsep); } static void close_cfm(struct avdtp *session, struct avdtp_local_sep *sep, struct avdtp_stream *stream, struct avdtp_error *err, void *user_data) { struct a2dp_sep *a2dp_sep = user_data; struct a2dp_setup *setup; if (a2dp_sep->type == AVDTP_SEP_TYPE_SINK) DBG("Sink %p: Close_Cfm", sep); else DBG("Source %p: Close_Cfm", sep); setup = find_setup_by_session(session); if (!setup) return; if (err) { setup->stream = NULL; setup_error_set(setup, err); finalize_config(setup); return; } if (!setup->rsep) setup->rsep = get_remote_sep(setup->chan, stream); setup_reconfigure(setup); } static void abort_ind(struct avdtp *session, struct avdtp_local_sep *sep, struct avdtp_stream *stream, uint8_t *err, void *user_data) { struct a2dp_sep *a2dp_sep = user_data; struct a2dp_setup *setup; if (a2dp_sep->type == AVDTP_SEP_TYPE_SINK) DBG("Sink %p: Abort_Ind", sep); else DBG("Source %p: Abort_Ind", sep); a2dp_sep->stream = NULL; setup = find_setup_by_session(session); if (!setup) return; finalize_setup_errno(setup, -ECONNRESET, finalize_suspend, finalize_resume, finalize_config, NULL); return; } static void abort_cfm(struct avdtp *session, struct avdtp_local_sep *sep, struct avdtp_stream *stream, struct avdtp_error *err, void *user_data) { struct a2dp_sep *a2dp_sep = user_data; struct a2dp_setup *setup; if (a2dp_sep->type == AVDTP_SEP_TYPE_SINK) DBG("Sink %p: Abort_Cfm", sep); else DBG("Source %p: Abort_Cfm", sep); setup = find_setup_by_session(session); if (!setup) return; if (setup_reconfigure(setup)) return; setup_unref(setup); } static gboolean reconf_ind(struct avdtp *session, struct avdtp_local_sep *sep, uint8_t *err, void *user_data) { struct a2dp_sep *a2dp_sep = user_data; if (a2dp_sep->type == AVDTP_SEP_TYPE_SINK) DBG("Sink %p: ReConfigure_Ind", sep); else DBG("Source %p: ReConfigure_Ind", sep); return TRUE; } static gboolean endpoint_delayreport_ind(struct avdtp *session, struct avdtp_local_sep *sep, uint8_t rseid, uint16_t delay, uint8_t *err, void *user_data) { struct a2dp_sep *a2dp_sep = user_data; if (a2dp_sep->type == AVDTP_SEP_TYPE_SINK) DBG("Sink %p: DelayReport_Ind", sep); else DBG("Source %p: DelayReport_Ind", sep); if (a2dp_sep->endpoint == NULL || a2dp_sep->endpoint->set_delay == NULL) return FALSE; a2dp_sep->endpoint->set_delay(a2dp_sep, delay, a2dp_sep->user_data); return TRUE; } static void reconf_cfm(struct avdtp *session, struct avdtp_local_sep *sep, struct avdtp_stream *stream, struct avdtp_error *err, void *user_data) { struct a2dp_sep *a2dp_sep = user_data; struct a2dp_setup *setup; if (a2dp_sep->type == AVDTP_SEP_TYPE_SINK) DBG("Sink %p: ReConfigure_Cfm", sep); else DBG("Source %p: ReConfigure_Cfm", sep); setup = find_setup_by_session(session); if (!setup) return; if (err) { setup->stream = NULL; setup_error_set(setup, err); } finalize_config(setup); } static void delay_report_cfm(struct avdtp *session, struct avdtp_local_sep *sep, struct avdtp_stream *stream, struct avdtp_error *err, void *user_data) { struct a2dp_sep *a2dp_sep = user_data; if (a2dp_sep->type == AVDTP_SEP_TYPE_SINK) DBG("Sink %p: DelayReport_Cfm", sep); else DBG("Source %p: DelayReport_Cfm", sep); } static struct avdtp_sep_cfm cfm = { .set_configuration = setconf_cfm, .get_configuration = getconf_cfm, .open = open_cfm, .start = start_cfm, .suspend = suspend_cfm, .close = close_cfm, .abort = abort_cfm, .reconfigure = reconf_cfm, .delay_report = delay_report_cfm, }; static struct avdtp_sep_ind endpoint_ind = { .match_codec = endpoint_match_codec_ind, .get_capability = endpoint_getcap_ind, .set_configuration = endpoint_setconf_ind, .get_configuration = getconf_ind, .open = open_ind, .start = start_ind, .suspend = suspend_ind, .close = close_ind, .abort = abort_ind, .reconfigure = reconf_ind, .delayreport = endpoint_delayreport_ind, }; static sdp_record_t *a2dp_record(uint8_t type) { sdp_list_t *svclass_id, *pfseq, *apseq, *root; uuid_t root_uuid, l2cap_uuid, avdtp_uuid, a2dp_uuid; sdp_profile_desc_t profile[1]; sdp_list_t *aproto, *proto[2]; sdp_record_t *record; sdp_data_t *psm, *version, *features; uint16_t lp = AVDTP_UUID; uint16_t a2dp_ver = 0x0104, avdtp_ver = 0x0103, feat = 0x000f; record = sdp_record_alloc(); if (!record) return NULL; sdp_uuid16_create(&root_uuid, PUBLIC_BROWSE_GROUP); root = sdp_list_append(0, &root_uuid); sdp_set_browse_groups(record, root); if (type == AVDTP_SEP_TYPE_SOURCE) sdp_uuid16_create(&a2dp_uuid, AUDIO_SOURCE_SVCLASS_ID); else sdp_uuid16_create(&a2dp_uuid, AUDIO_SINK_SVCLASS_ID); svclass_id = sdp_list_append(0, &a2dp_uuid); sdp_set_service_classes(record, svclass_id); sdp_uuid16_create(&profile[0].uuid, ADVANCED_AUDIO_PROFILE_ID); profile[0].version = a2dp_ver; pfseq = sdp_list_append(0, &profile[0]); sdp_set_profile_descs(record, pfseq); sdp_uuid16_create(&l2cap_uuid, L2CAP_UUID); proto[0] = sdp_list_append(0, &l2cap_uuid); psm = sdp_data_alloc(SDP_UINT16, &lp); proto[0] = sdp_list_append(proto[0], psm); apseq = sdp_list_append(0, proto[0]); sdp_uuid16_create(&avdtp_uuid, AVDTP_UUID); proto[1] = sdp_list_append(0, &avdtp_uuid); version = sdp_data_alloc(SDP_UINT16, &avdtp_ver); proto[1] = sdp_list_append(proto[1], version); apseq = sdp_list_append(apseq, proto[1]); aproto = sdp_list_append(0, apseq); sdp_set_access_protos(record, aproto); features = sdp_data_alloc(SDP_UINT16, &feat); sdp_attr_add(record, SDP_ATTR_SUPPORTED_FEATURES, features); if (type == AVDTP_SEP_TYPE_SOURCE) sdp_set_info_attr(record, "Audio Source", 0, 0); else sdp_set_info_attr(record, "Audio Sink", 0, 0); free(psm); free(version); sdp_list_free(proto[0], 0); sdp_list_free(proto[1], 0); sdp_list_free(apseq, 0); sdp_list_free(pfseq, 0); sdp_list_free(aproto, 0); sdp_list_free(root, 0); sdp_list_free(svclass_id, 0); return record; } static struct a2dp_server *find_server(GSList *list, struct btd_adapter *a) { for (; list; list = list->next) { struct a2dp_server *server = list->data; if (server->adapter == a) return server; } return NULL; } static void remote_sep_free(void *data) { struct a2dp_remote_sep *sep = data; avdtp_remote_sep_set_destroy(sep->sep, NULL, NULL); free(sep->path); free(sep); } static void remove_remote_sep(void *data) { struct a2dp_remote_sep *sep = data; if (!sep->path) { remote_sep_free(sep); return; } g_dbus_unregister_interface(btd_get_dbus_connection(), sep->path, MEDIA_ENDPOINT_INTERFACE); } static void channel_free(void *data) { struct a2dp_channel *chan = data; struct a2dp_setup *setup; if (chan->auth_id > 0) btd_cancel_authorization(chan->auth_id); if (chan->io_id > 0) g_source_remove(chan->io_id); if (chan->io) { g_io_channel_shutdown(chan->io, TRUE, NULL); g_io_channel_unref(chan->io); } avdtp_remove_state_cb(chan->state_id); queue_destroy(chan->seps, remove_remote_sep); free(chan->last_used); setup = find_setup_by_session(chan->session); if (setup) { setup->chan = NULL; setup_ref(setup); /* Finalize pending commands before we NULL setup->session */ finalize_setup_errno(setup, -ENOTCONN, finalize_all, NULL); avdtp_unref(setup->session); setup->session = NULL; setup_unref(setup); } g_free(chan); } static void channel_remove(struct a2dp_channel *chan) { struct a2dp_server *server = chan->server; DBG("chan %p", chan); queue_remove(server->channels, chan); channel_free(chan); } static gboolean disconnect_cb(GIOChannel *io, GIOCondition cond, gpointer data) { struct a2dp_channel *chan = data; DBG("chan %p", chan); chan->io_id = 0; channel_remove(chan); return FALSE; } static void caps_add_codec(GSList **l, uint8_t codec, uint8_t *caps, size_t size) { struct avdtp_service_capability *media_transport, *media_codec; struct avdtp_media_codec_capability *cap; media_transport = avdtp_service_cap_new(AVDTP_MEDIA_TRANSPORT, NULL, 0); *l = g_slist_append(*l, media_transport); cap = g_malloc0(sizeof(*cap) + size); cap->media_type = AVDTP_MEDIA_TYPE_AUDIO; cap->media_codec_type = codec; memcpy(cap->data, caps, size); media_codec = avdtp_service_cap_new(AVDTP_MEDIA_CODEC, cap, sizeof(*cap) + size); *l = g_slist_append(*l, media_codec); g_free(cap); } struct client { const char *sender; const char *path; }; static int match_client(const void *data, const void *user_data) { struct a2dp_sep *sep = (void *) data; const struct a2dp_endpoint *endpoint = sep->endpoint; const struct client *client = user_data; if (strcmp(client->sender, endpoint->get_name(sep, sep->user_data))) return -1; return strcmp(client->path, endpoint->get_path(sep, sep->user_data)); } static struct a2dp_sep *find_sep(struct a2dp_server *server, uint8_t type, const char *sender, const char *path) { GSList *l; struct client client = { sender, path }; l = type == AVDTP_SEP_TYPE_SINK ? server->sources : server->sinks; l = g_slist_find_custom(l, &client, match_client); if (l) return l->data; return NULL; } static int parse_properties(DBusMessageIter *props, uint8_t **caps, int *size) { while (dbus_message_iter_get_arg_type(props) == DBUS_TYPE_DICT_ENTRY) { const char *key; DBusMessageIter value, entry; int var; dbus_message_iter_recurse(props, &entry); dbus_message_iter_get_basic(&entry, &key); dbus_message_iter_next(&entry); dbus_message_iter_recurse(&entry, &value); var = dbus_message_iter_get_arg_type(&value); if (strcasecmp(key, "Capabilities") == 0) { DBusMessageIter array; if (var != DBUS_TYPE_ARRAY) return -EINVAL; dbus_message_iter_recurse(&value, &array); dbus_message_iter_get_fixed_array(&array, caps, size); return 0; } dbus_message_iter_next(props); } return -EINVAL; } static void reconfig_cb(struct avdtp *session, struct a2dp_sep *sep, struct avdtp_stream *stream, int err, void *user_data) { DBusMessage *msg = user_data; if (err) g_dbus_send_message(btd_get_dbus_connection(), btd_error_failed(msg, strerror(-err))); else g_dbus_send_reply(btd_get_dbus_connection(), msg, DBUS_TYPE_INVALID); dbus_message_unref(msg); } static int a2dp_reconfig(struct a2dp_channel *chan, const char *sender, struct a2dp_sep *lsep, struct a2dp_remote_sep *rsep, uint8_t *caps, int size, void *user_data) { struct a2dp_setup *setup; struct a2dp_setup_cb *cb_data; GSList *l; int err; /* Check SEP not used by a different session */ if (lsep->stream && chan->session && !avdtp_has_stream(chan->session, lsep->stream)) return -EBUSY; setup = a2dp_setup_get(chan->session); if (!setup) return -ENOMEM; cb_data = setup_cb_add(setup); cb_data->config_cb = reconfig_cb; cb_data->user_data = user_data; setup->sep = lsep; setup->rsep = rsep; g_slist_free_full(setup->caps, g_free); setup->caps = NULL; caps_add_codec(&setup->caps, setup->sep->codec, caps, size); l = avdtp_get_type(rsep->sep) == AVDTP_SEP_TYPE_SINK ? chan->server->sources : chan->server->sinks; /* Check for existing stream and close it */ for (; l; l = g_slist_next(l)) { struct a2dp_sep *tmp = l->data; /* Attempt to reconfigure if a stream already exists */ if (tmp->stream) { /* Only allow switching sep from the same sender */ if (strcmp(sender, tmp->endpoint->get_name(tmp, tmp->user_data))) { err = -EPERM; goto fail; } /* Check if stream is for the channel */ if (!avdtp_has_stream(chan->session, tmp->stream)) continue; err = avdtp_close(chan->session, tmp->stream, FALSE); if (err < 0) { err = avdtp_abort(chan->session, tmp->stream); if (err < 0) { error("avdtp_abort: %s", strerror(-err)); goto fail; } } setup->reconfigure = TRUE; return 0; } } err = avdtp_set_configuration(setup->session, setup->rsep->sep, lsep->lsep, setup->caps, &setup->stream); if (err < 0) { error("avdtp_set_configuration: %s", strerror(-err)); goto fail; } return 0; fail: setup_cb_free(cb_data); return err; } static DBusMessage *set_configuration(DBusConnection *conn, DBusMessage *msg, void *data) { struct a2dp_remote_sep *rsep = data; struct a2dp_channel *chan = rsep->chan; struct a2dp_sep *lsep = NULL; struct avdtp_service_capability *service; struct avdtp_media_codec_capability *codec; DBusMessageIter args, props; const char *sender, *path; uint8_t *caps; int err, size = 0; sender = dbus_message_get_sender(msg); dbus_message_iter_init(msg, &args); dbus_message_iter_get_basic(&args, &path); dbus_message_iter_next(&args); lsep = find_sep(chan->server, avdtp_get_type(rsep->sep), sender, path); if (!lsep) return btd_error_invalid_args(msg); service = avdtp_get_codec(rsep->sep); codec = (struct avdtp_media_codec_capability *) service->data; /* Check if codec really matches */ if (!endpoint_match_codec_ind(chan->session, codec, lsep)) return btd_error_invalid_args(msg); dbus_message_iter_recurse(&args, &props); if (dbus_message_iter_get_arg_type(&props) != DBUS_TYPE_DICT_ENTRY) return btd_error_invalid_args(msg); if (parse_properties(&props, &caps, &size) < 0) return btd_error_invalid_args(msg); err = a2dp_reconfig(chan, sender, lsep, rsep, caps, size, dbus_message_ref(msg)); if (err < 0) { dbus_message_unref(msg); return btd_error_failed(msg, strerror(-err)); } return NULL; } static const GDBusMethodTable sep_methods[] = { { GDBUS_ASYNC_METHOD("SetConfiguration", GDBUS_ARGS({ "endpoint", "o" }, { "properties", "a{sv}" } ), NULL, set_configuration) }, { }, }; static gboolean get_uuid(const GDBusPropertyTable *property, DBusMessageIter *iter, void *data) { struct a2dp_remote_sep *sep = data; const char *uuid; switch (avdtp_get_type(sep->sep)) { case AVDTP_SEP_TYPE_SOURCE: uuid = A2DP_SOURCE_UUID; break; case AVDTP_SEP_TYPE_SINK: uuid = A2DP_SINK_UUID; break; default: uuid = ""; } dbus_message_iter_append_basic(iter, DBUS_TYPE_STRING, &uuid); return TRUE; } static gboolean get_codec(const GDBusPropertyTable *property, DBusMessageIter *iter, void *data) { struct a2dp_remote_sep *sep = data; struct avdtp_service_capability *cap = avdtp_get_codec(sep->sep); struct avdtp_media_codec_capability *codec = (void *) cap->data; dbus_message_iter_append_basic(iter, DBUS_TYPE_BYTE, &codec->media_codec_type); return TRUE; } static gboolean get_capabilities(const GDBusPropertyTable *property, DBusMessageIter *iter, void *data) { struct a2dp_remote_sep *sep = data; struct avdtp_service_capability *service = avdtp_get_codec(sep->sep); struct avdtp_media_codec_capability *codec = (void *) service->data; uint8_t *caps = codec->data; DBusMessageIter array; dbus_message_iter_open_container(iter, DBUS_TYPE_ARRAY, DBUS_TYPE_BYTE_AS_STRING, &array); dbus_message_iter_append_fixed_array(&array, DBUS_TYPE_BYTE, &caps, service->length - sizeof(*codec)); dbus_message_iter_close_container(iter, &array); return TRUE; } static gboolean get_device(const GDBusPropertyTable *property, DBusMessageIter *iter, void *data) { struct a2dp_remote_sep *sep = data; const char *path; path = device_get_path(sep->chan->device); dbus_message_iter_append_basic(iter, DBUS_TYPE_OBJECT_PATH, &path); return TRUE; } static gboolean get_delay_reporting(const GDBusPropertyTable *property, DBusMessageIter *iter, void *data) { struct a2dp_remote_sep *sep = data; dbus_bool_t delay_report; delay_report = avdtp_get_delay_reporting(sep->sep); dbus_message_iter_append_basic(iter, DBUS_TYPE_BOOLEAN, &delay_report); return TRUE; } static const GDBusPropertyTable sep_properties[] = { { "UUID", "s", get_uuid, NULL, NULL }, { "Codec", "y", get_codec, NULL, NULL }, { "Capabilities", "ay", get_capabilities, NULL, NULL }, { "Device", "o", get_device, NULL, NULL }, { "DelayReporting", "b", get_delay_reporting, NULL, NULL }, { } }; static void remote_sep_destroy(void *user_data) { struct a2dp_remote_sep *sep = user_data; if (queue_remove(sep->chan->seps, sep)) remove_remote_sep(sep); } static struct a2dp_remote_sep *register_remote_sep(void *data, void *user_data) { struct avdtp_remote_sep *rsep = data; struct a2dp_channel *chan = user_data; struct a2dp_remote_sep *sep; sep = queue_find(chan->seps, match_remote_sep, rsep); if (sep) return sep; if (!avdtp_get_codec(rsep)) { error("Unable to get remote sep codec"); return NULL; } sep = new0(struct a2dp_remote_sep, 1); sep->chan = chan; sep->sep = rsep; if (asprintf(&sep->path, "%s/sep%d", device_get_path(chan->device), avdtp_get_seid(rsep)) < 0) { error("Could not allocate path for remote sep %s/sep%d", device_get_path(chan->device), avdtp_get_seid(rsep)); sep->path = NULL; goto done; } if (g_dbus_register_interface(btd_get_dbus_connection(), sep->path, MEDIA_ENDPOINT_INTERFACE, sep_methods, NULL, sep_properties, sep, remote_sep_free) == FALSE) { error("Could not register remote sep %s", sep->path); free(sep->path); free(sep); return NULL; } DBG("Found remote SEP: %s", sep->path); avdtp_remote_sep_set_destroy(rsep, sep, remote_sep_destroy); done: queue_push_tail(chan->seps, sep); return sep; } static bool match_seid(const void *data, const void *user_data) { const struct a2dp_remote_sep *sep = data; const uint8_t *seid = user_data; return avdtp_get_seid(sep->sep) == *seid; } static int match_sep(const void *data, const void *user_data) { struct a2dp_sep *sep = (void *) data; const uint8_t *seid = user_data; return *seid - avdtp_sep_get_seid(sep->lsep); } static struct a2dp_sep *find_sep_by_seid(struct a2dp_server *server, uint8_t seid) { GSList *l; l = g_slist_find_custom(server->sources, &seid, match_sep); if (l) return l->data; l = g_slist_find_custom(server->sinks, &seid, match_sep); if (l) return l->data; return NULL; } static void load_remote_sep(struct a2dp_channel *chan, GKeyFile *key_file, char **seids) { struct a2dp_sep *lsep; struct a2dp_remote_sep *sep; struct avdtp_remote_sep *rsep; uint8_t lseid, rseid; char *value; bool update = false; if (!seids) return; for (; *seids; seids++) { uint8_t type; uint8_t codec; uint8_t delay_reporting; GSList *l = NULL; char caps[256]; uint8_t data[128]; int i, size; if (sscanf(*seids, "%02hhx", &rseid) != 1) continue; value = g_key_file_get_string(key_file, "Endpoints", *seids, NULL); if (!value) continue; /* Try loading with delay_reporting first */ if (sscanf(value, "%02hhx:%02hhx:%02hhx:%s", &type, &codec, &delay_reporting, caps) != 4) { /* Try old format */ if (sscanf(value, "%02hhx:%02hhx:%s", &type, &codec, caps) != 3) { warn("Unable to load Endpoint: seid %u", rseid); g_free(value); continue; } delay_reporting = false; } for (i = 0, size = strlen(caps); i < size; i += 2) { uint8_t *tmp = data + i / 2; if (sscanf(caps + i, "%02hhx", tmp) != 1) { warn("Unable to load Endpoint: seid %u", rseid); break; } } g_free(value); if (i != size) continue; caps_add_codec(&l, codec, data, size / 2); rsep = avdtp_register_remote_sep(chan->session, rseid, type, l, delay_reporting); if (!rsep) { warn("Unable to register Endpoint: seid %u", rseid); continue; } sep = register_remote_sep(rsep, chan); if (!sep) { avdtp_unregister_remote_sep(chan->session, rsep); update = true; continue; } sep->from_cache = true; } /* Update cache */ if (update) store_remote_seps(chan); value = g_key_file_get_string(key_file, "Endpoints", "LastUsed", NULL); if (!value) return; if (sscanf(value, "%02hhx:%02hhx", &lseid, &rseid) != 2) { warn("Unable to load LastUsed"); g_free(value); return; } g_free(value); lsep = find_sep_by_seid(chan->server, lseid); if (!lsep) { warn("Unable to load LastUsed: lseid %u not found", lseid); return; } sep = queue_find(chan->seps, match_seid, &rseid); if (!sep) { warn("Unable to load LastUsed: rseid %u not found", rseid); return; } DBG("LastUsed: lseid %u rseid %u", lseid, rseid); add_last_used(chan, lsep, sep); } static void load_remote_seps(struct a2dp_channel *chan) { struct btd_device *device = chan->device; char filename[PATH_MAX]; char dst_addr[18]; char **keys; GKeyFile *key_file; GError *gerr = NULL; ba2str(device_get_address(device), dst_addr); create_filename(filename, PATH_MAX, "/%s/cache/%s", btd_adapter_get_storage_dir(device_get_adapter(device)), dst_addr); key_file = g_key_file_new(); if (!g_key_file_load_from_file(key_file, filename, 0, &gerr)) { error("Unable to load key file from %s: (%s)", filename, gerr->message); g_error_free(gerr); } keys = g_key_file_get_keys(key_file, "Endpoints", NULL, NULL); load_remote_sep(chan, key_file, keys); g_strfreev(keys); g_key_file_free(key_file); } static void avdtp_state_cb(struct btd_device *dev, struct avdtp *session, avdtp_session_state_t old_state, avdtp_session_state_t new_state, void *user_data) { struct a2dp_channel *chan = user_data; switch (new_state) { case AVDTP_SESSION_STATE_DISCONNECTED: if (chan->session == session) channel_remove(chan); break; case AVDTP_SESSION_STATE_CONNECTING: break; case AVDTP_SESSION_STATE_CONNECTED: if (!chan->session) chan->session = session; load_remote_seps(chan); break; } } static struct a2dp_channel *channel_new(struct a2dp_server *server, struct btd_device *device, GIOChannel *io) { struct a2dp_channel *chan; chan = g_new0(struct a2dp_channel, 1); chan->server = server; chan->device = device; chan->seps = queue_new(); chan->state_id = avdtp_add_state_cb(device, avdtp_state_cb, chan); if (!queue_push_tail(server->channels, chan)) { g_free(chan); return NULL; } if (!io) return chan; chan->io = g_io_channel_ref(io); chan->io_id = g_io_add_watch(io, G_IO_ERR | G_IO_HUP | G_IO_NVAL, (GIOFunc) disconnect_cb, chan); return chan; } static bool match_by_device(const void *data, const void *user_data) { const struct a2dp_channel *chan = data; const struct btd_device *device = user_data; return chan->device == device; } struct avdtp *a2dp_avdtp_get(struct btd_device *device) { struct a2dp_server *server; struct a2dp_channel *chan; const struct queue_entry *entry; server = find_server(servers, device_get_adapter(device)); if (server == NULL) return NULL; chan = queue_find(server->channels, match_by_device, device); if (!chan) { chan = channel_new(server, device, NULL); if (!chan) return NULL; } if (chan->session) return avdtp_ref(chan->session); /* Check if there is any SEP available */ for (entry = queue_get_entries(server->seps); entry; entry = entry->next) { struct avdtp_local_sep *sep = entry->data; if (avdtp_sep_get_state(sep) == AVDTP_STATE_IDLE) goto found; } DBG("Unable to find any available SEP"); return NULL; found: chan->session = avdtp_new(chan->io, device, server->seps); if (!chan->session) { channel_remove(chan); return NULL; } return avdtp_ref(chan->session); } static void connect_cb(GIOChannel *io, GError *err, gpointer user_data) { struct a2dp_channel *chan = user_data; if (err) { error("%s", err->message); goto fail; } if (!chan->session) { chan->session = avdtp_new(chan->io, chan->device, chan->server->seps); if (!chan->session) { error("Unable to create AVDTP session"); goto fail; } } g_io_channel_unref(chan->io); chan->io = NULL; g_source_remove(chan->io_id); chan->io_id = 0; return; fail: channel_remove(chan); } static void auth_cb(DBusError *derr, void *user_data) { struct a2dp_channel *chan = user_data; GError *err = NULL; chan->auth_id = 0; if (derr && dbus_error_is_set(derr)) { error("Access denied: %s", derr->message); goto fail; } if (!bt_io_accept(chan->io, connect_cb, chan, NULL, &err)) { error("bt_io_accept: %s", err->message); g_error_free(err); goto fail; } return; fail: channel_remove(chan); } static void transport_cb(GIOChannel *io, GError *err, gpointer user_data) { struct a2dp_setup *setup = user_data; uint16_t omtu, imtu; if (!g_slist_find(setups, setup)) { warn("bt_io_accept: setup %p no longer valid", setup); g_io_channel_shutdown(io, TRUE, NULL); return; } if (err) { error("%s", err->message); goto drop; } bt_io_get(io, &err, BT_IO_OPT_OMTU, &omtu, BT_IO_OPT_IMTU, &imtu, BT_IO_OPT_INVALID); if (err) { error("%s", err->message); g_error_free(err); goto drop; } if (!avdtp_stream_set_transport(setup->stream, g_io_channel_unix_get_fd(io), imtu, omtu)) goto drop; g_io_channel_set_close_on_unref(io, FALSE); g_io_channel_unref(setup->io); setup->io = NULL; setup_unref(setup); return; drop: setup_unref(setup); g_io_channel_shutdown(io, TRUE, NULL); } static void confirm_cb(GIOChannel *io, gpointer data) { struct a2dp_server *server = data; struct a2dp_channel *chan; char address[18]; bdaddr_t src, dst; GError *err = NULL; struct btd_device *device; bt_io_get(io, &err, BT_IO_OPT_SOURCE_BDADDR, &src, BT_IO_OPT_DEST_BDADDR, &dst, BT_IO_OPT_DEST, address, BT_IO_OPT_INVALID); if (err) { error("%s", err->message); g_error_free(err); goto drop; } DBG("AVDTP: incoming connect from %s", address); device = btd_adapter_find_device(adapter_find(&src), &dst, BDADDR_BREDR); if (!device) goto drop; chan = queue_find(server->channels, match_by_device, device); if (chan) { struct a2dp_setup *setup; setup = find_setup_by_session(chan->session); if (!setup || !setup->stream) goto drop; if (setup->io || avdtp_stream_get_transport(setup->stream, NULL, NULL, NULL, NULL)) { error("transport channel already exists"); goto drop; } if (!bt_io_accept(io, transport_cb, setup, NULL, &err)) { error("bt_io_accept: %s", err->message); g_error_free(err); goto drop; } /* * Reference the channel so it can be shutdown properly * stopping bt_io_accept from calling the callback with invalid * setup pointer. */ setup->io = g_io_channel_ref(io); return; } chan = channel_new(server, device, io); if (!chan) goto drop; chan->auth_id = btd_request_authorization(&src, &dst, ADVANCED_AUDIO_UUID, auth_cb, chan); if (chan->auth_id == 0 && !chan->session) channel_remove(chan); return; drop: g_io_channel_shutdown(io, TRUE, NULL); } static bool a2dp_server_listen(struct a2dp_server *server) { GError *err = NULL; BtIOMode mode; if (server->io) return true; mode = btd_opts.avdtp.session_mode; server->io = bt_io_listen(NULL, confirm_cb, server, NULL, &err, BT_IO_OPT_SOURCE_BDADDR, btd_adapter_get_address(server->adapter), BT_IO_OPT_PSM, AVDTP_PSM, BT_IO_OPT_MODE, mode, BT_IO_OPT_SEC_LEVEL, BT_IO_SEC_MEDIUM, BT_IO_OPT_CENTRAL, true, /* Set Input MTU to 0 for auto-tune attempt */ BT_IO_OPT_IMTU, 0, BT_IO_OPT_INVALID); if (server->io) return true; error("%s", err->message); g_error_free(err); return false; } static struct a2dp_server *a2dp_server_register(struct btd_adapter *adapter) { struct a2dp_server *server; server = g_new0(struct a2dp_server, 1); server->adapter = btd_adapter_ref(adapter); server->seps = queue_new(); server->channels = queue_new(); servers = g_slist_append(servers, server); return server; } static void a2dp_unregister_sep(struct a2dp_sep *sep) { struct a2dp_server *server = sep->server; if (sep->destroy) { sep->destroy(sep->user_data); sep->endpoint = NULL; } avdtp_unregister_sep(server->seps, &server->seid_pool, sep->lsep); g_free(sep); if (!queue_isempty(server->seps)) return; if (server->io) { g_io_channel_shutdown(server->io, TRUE, NULL); g_io_channel_unref(server->io); server->io = NULL; } } static void a2dp_server_unregister(struct a2dp_server *server) { servers = g_slist_remove(servers, server); queue_destroy(server->channels, channel_free); queue_destroy(server->seps, NULL); if (server->io) { g_io_channel_shutdown(server->io, TRUE, NULL); g_io_channel_unref(server->io); } btd_adapter_unref(server->adapter); g_free(server); } struct a2dp_sep *a2dp_add_sep(struct btd_adapter *adapter, uint8_t type, uint8_t codec, gboolean delay_reporting, struct a2dp_endpoint *endpoint, void *user_data, GDestroyNotify destroy, int *err) { struct a2dp_server *server; struct a2dp_sep *sep; GSList **l; uint32_t *record_id; sdp_record_t *record; server = find_server(servers, adapter); if (server == NULL) { if (err) *err = -EPROTONOSUPPORT; return NULL; } if (type == AVDTP_SEP_TYPE_SINK && !server->sink_enabled) { if (err) *err = -EPROTONOSUPPORT; return NULL; } if (type == AVDTP_SEP_TYPE_SOURCE && !server->source_enabled) { if (err) *err = -EPROTONOSUPPORT; return NULL; } sep = g_new0(struct a2dp_sep, 1); sep->lsep = avdtp_register_sep(server->seps, &server->seid_pool, type, AVDTP_MEDIA_TYPE_AUDIO, codec, delay_reporting, &endpoint_ind, &cfm, sep); if (sep->lsep == NULL) { g_free(sep); if (err) *err = -EINVAL; return NULL; } sep->server = server; sep->endpoint = endpoint; sep->codec = codec; sep->type = type; sep->delay_reporting = delay_reporting; if (type == AVDTP_SEP_TYPE_SOURCE) { l = &server->sources; record_id = &server->source_record_id; } else { l = &server->sinks; record_id = &server->sink_record_id; } if (*record_id != 0) goto add; record = a2dp_record(type); if (!record) { error("Unable to allocate new service record"); a2dp_unregister_sep(sep); if (err) *err = -EINVAL; return NULL; } if (adapter_service_add(server->adapter, record) < 0) { error("Unable to register A2DP service record"); sdp_record_free(record); a2dp_unregister_sep(sep); if (err) *err = -EINVAL; return NULL; } if (!a2dp_server_listen(server)) { sdp_record_free(record); a2dp_unregister_sep(sep); if (err) *err = -EINVAL; return NULL; } *record_id = record->handle; add: *l = g_slist_append(*l, sep); sep->user_data = user_data; sep->destroy = destroy; if (err) *err = 0; return sep; } void a2dp_remove_sep(struct a2dp_sep *sep) { struct a2dp_server *server = sep->server; if (sep->type == AVDTP_SEP_TYPE_SOURCE) { if (g_slist_find(server->sources, sep) == NULL) return; server->sources = g_slist_remove(server->sources, sep); if (server->sources == NULL && server->source_record_id) { adapter_service_remove(server->adapter, server->source_record_id); server->source_record_id = 0; } } else { if (g_slist_find(server->sinks, sep) == NULL) return; server->sinks = g_slist_remove(server->sinks, sep); if (server->sinks == NULL && server->sink_record_id) { adapter_service_remove(server->adapter, server->sink_record_id); server->sink_record_id = 0; } } if (sep->locked) return; a2dp_unregister_sep(sep); } static void select_cb(struct a2dp_setup *setup, void *ret, int size) { struct avdtp_service_capability *service; struct avdtp_media_codec_capability *codec; int err; if (setup->err) goto done; if (size >= 0) { caps_add_codec(&setup->caps, setup->sep->codec, ret, size); goto done; } setup->sep = queue_pop_head(setup->eps); if (!setup->sep) { error("Unable to select a valid configuration"); goto done; } setup->rsep = find_remote_sep(setup->chan, setup->sep); service = avdtp_get_codec(setup->rsep->sep); codec = (struct avdtp_media_codec_capability *) service->data; err = setup->sep->endpoint->select_configuration(setup->sep, codec->data, service->length - sizeof(*codec), setup, select_cb, setup->sep->user_data); if (err == 0) return; done: finalize_select(setup); setup_unref(setup); } static struct queue *a2dp_find_eps(struct avdtp *session, GSList *list, const char *sender) { struct a2dp_channel *chan = find_channel(session); struct queue *seps = NULL; for (; list; list = list->next) { struct a2dp_sep *sep = list->data; struct avdtp_remote_sep *rsep; /* Use sender's endpoint if available */ if (sender) { const char *name; if (sep->endpoint == NULL) continue; name = sep->endpoint->get_name(sep, sep->user_data); if (g_strcmp0(sender, name) != 0) continue; } rsep = avdtp_find_remote_sep(session, sep->lsep); if (!rsep) continue; if (!seps) seps = queue_new(); /* Prepend last used so it is preferred over others */ if (chan->last_used && (chan->last_used->lsep == sep && chan->last_used->rsep->sep == rsep)) queue_push_head(seps, sep); else queue_push_tail(seps, sep); } return seps; } static struct queue *a2dp_select_eps(struct avdtp *session, uint8_t type, const char *sender) { struct a2dp_server *server; struct queue *seps; GSList *l; server = find_server(servers, avdtp_get_adapter(session)); if (!server) return NULL; l = type == AVDTP_SEP_TYPE_SINK ? server->sources : server->sinks; /* Check sender's seps first */ seps = a2dp_find_eps(session, l, sender); if (seps != NULL) return seps; return a2dp_find_eps(session, l, NULL); } static void foreach_register_remote_sep(void *data, void *user_data) { register_remote_sep(data, user_data); } static void discover_cb(struct avdtp *session, GSList *seps, struct avdtp_error *err, void *user_data) { struct a2dp_setup *setup = user_data; uint16_t version = avdtp_get_version(session); DBG("version 0x%04x err %p", version, err); setup->seps = seps; if (err) setup_error_set(setup, err); if (!err) { g_slist_foreach(seps, foreach_register_remote_sep, setup->chan); /* Only store version has been initialized as features like * Delay Reporting may not be queried if the version in * unknown. */ if (version) store_remote_seps(setup->chan); } finalize_discover(setup); } unsigned int a2dp_discover(struct avdtp *session, a2dp_discover_cb_t cb, void *user_data) { struct a2dp_setup *setup; struct a2dp_setup_cb *cb_data; setup = a2dp_setup_get(session); if (!setup) return 0; /* Don't add cb since avdtp_discover can end up disconnecting the * session causing the cb to be freed. */ cb_data = setup_cb_new(setup); cb_data->discover_cb = cb; cb_data->user_data = user_data; if (avdtp_discover(session, discover_cb, setup) == 0) { setup->cb = g_slist_append(setup->cb, cb_data); return cb_data->id; } setup_cb_free(cb_data); return 0; } unsigned int a2dp_select_capabilities(struct avdtp *session, uint8_t type, const char *sender, a2dp_select_cb_t cb, void *user_data) { struct a2dp_setup *setup; struct a2dp_setup_cb *cb_data; struct queue *eps; struct avdtp_service_capability *service; struct avdtp_media_codec_capability *codec; int err; eps = a2dp_select_eps(session, type, sender); if (!eps) { error("Unable to select SEP"); return 0; } setup = a2dp_setup_get(session); if (!setup) return 0; cb_data = setup_cb_add(setup); cb_data->select_cb = cb; cb_data->user_data = user_data; setup->eps = eps; setup->sep = queue_pop_head(eps); setup->rsep = find_remote_sep(setup->chan, setup->sep); if (setup->rsep == NULL) { error("Could not find remote sep"); goto fail; } service = avdtp_get_codec(setup->rsep->sep); codec = (struct avdtp_media_codec_capability *) service->data; err = setup->sep->endpoint->select_configuration(setup->sep, codec->data, service->length - sizeof(*codec), setup_ref(setup), select_cb, setup->sep->user_data); if (err == 0) return cb_data->id; setup_unref(setup); fail: setup_cb_free(cb_data); return 0; } unsigned int a2dp_config(struct avdtp *session, struct a2dp_sep *sep, a2dp_config_cb_t cb, GSList *caps, void *user_data) { struct a2dp_setup_cb *cb_data; GSList *l; struct a2dp_server *server; struct a2dp_setup *setup; struct a2dp_sep *tmp; struct avdtp_service_capability *cap; struct avdtp_media_codec_capability *codec_cap = NULL; int posix_err; server = find_server(servers, avdtp_get_adapter(session)); if (!server) return 0; for (l = caps; l != NULL; l = l->next) { cap = l->data; if (cap->category != AVDTP_MEDIA_CODEC) continue; codec_cap = (void *) cap->data; break; } if (!codec_cap) return 0; if (sep->codec != codec_cap->media_codec_type) return 0; DBG("a2dp_config: selected SEP %p", sep->lsep); setup = a2dp_setup_get(session); if (!setup) return 0; cb_data = setup_cb_add(setup); cb_data->config_cb = cb; cb_data->user_data = user_data; setup->sep = sep; setup->stream = sep->stream; /* Copy given caps if they are different than current caps */ if (setup->caps != caps) { g_slist_free_full(setup->caps, g_free); setup->caps = g_slist_copy(caps); } switch (avdtp_sep_get_state(sep->lsep)) { case AVDTP_STATE_IDLE: if (sep->type == AVDTP_SEP_TYPE_SOURCE) l = server->sources; else l = server->sinks; for (; l != NULL; l = l->next) { tmp = l->data; if (avdtp_has_stream(session, tmp->stream)) break; } if (l != NULL) { if (tmp->locked) goto failed; setup->reconfigure = TRUE; if (avdtp_close(session, tmp->stream, FALSE) < 0) { error("avdtp_close failed"); goto failed; } break; } setup->rsep = find_remote_sep(setup->chan, sep); if (setup->rsep == NULL) { error("No matching ACP and INT SEPs found"); goto failed; } posix_err = avdtp_set_configuration(session, setup->rsep->sep, sep->lsep, caps, &setup->stream); if (posix_err < 0) { error("avdtp_set_configuration: %s", strerror(-posix_err)); goto failed; } break; case AVDTP_STATE_OPEN: case AVDTP_STATE_STREAMING: if (avdtp_stream_has_capabilities(setup->stream, caps)) { DBG("Configuration match: resuming"); cb_data->source_id = g_idle_add(finalize_config, setup); } else if (!setup->reconfigure) { setup->reconfigure = TRUE; if (avdtp_close(session, sep->stream, FALSE) < 0) { error("avdtp_close failed"); goto failed; } } break; case AVDTP_STATE_CONFIGURED: case AVDTP_STATE_CLOSING: case AVDTP_STATE_ABORTING: default: error("SEP in bad state for requesting a new stream"); goto failed; } return cb_data->id; failed: setup_cb_free(cb_data); return 0; } unsigned int a2dp_resume(struct avdtp *session, struct a2dp_sep *sep, a2dp_stream_cb_t cb, void *user_data) { struct a2dp_setup_cb *cb_data; struct a2dp_setup *setup; setup = a2dp_setup_get(session); if (!setup) return 0; cb_data = setup_cb_add(setup); cb_data->resume_cb = cb; cb_data->user_data = user_data; if (setup->reconfigure) goto failed; setup->sep = sep; setup->stream = sep->stream; switch (avdtp_sep_get_state(sep->lsep)) { case AVDTP_STATE_IDLE: goto failed; break; case AVDTP_STATE_CONFIGURED: setup->start = TRUE; break; case AVDTP_STATE_OPEN: if (avdtp_start(session, sep->stream) < 0) { error("avdtp_start failed"); goto failed; } sep->starting = TRUE; break; case AVDTP_STATE_STREAMING: if (!sep->suspending && sep->suspend_timer) { timeout_remove(sep->suspend_timer); sep->suspend_timer = 0; avdtp_unref(sep->session); sep->session = NULL; } if (sep->suspending) setup->start = TRUE; else cb_data->source_id = g_idle_add(finalize_resume, setup); break; case AVDTP_STATE_CLOSING: case AVDTP_STATE_ABORTING: default: error("SEP in bad state for resume"); goto failed; } return cb_data->id; failed: setup_cb_free(cb_data); return 0; } unsigned int a2dp_suspend(struct avdtp *session, struct a2dp_sep *sep, a2dp_stream_cb_t cb, void *user_data) { struct a2dp_setup_cb *cb_data; struct a2dp_setup *setup; setup = a2dp_setup_get(session); if (!setup) return 0; cb_data = setup_cb_add(setup); cb_data->suspend_cb = cb; cb_data->user_data = user_data; if (setup->reconfigure) goto failed; setup->sep = sep; setup->stream = sep->stream; switch (avdtp_sep_get_state(sep->lsep)) { case AVDTP_STATE_IDLE: error("a2dp_suspend: no stream to suspend"); goto failed; break; case AVDTP_STATE_OPEN: cb_data->source_id = g_idle_add(finalize_suspend, setup); break; case AVDTP_STATE_STREAMING: if (avdtp_suspend(session, sep->stream) < 0) { error("avdtp_suspend failed"); goto failed; } sep->suspending = TRUE; break; case AVDTP_STATE_CONFIGURED: case AVDTP_STATE_CLOSING: case AVDTP_STATE_ABORTING: default: error("SEP in bad state for suspend"); goto failed; } return cb_data->id; failed: setup_cb_free(cb_data); return 0; } gboolean a2dp_cancel(unsigned int id) { GSList *ls; for (ls = setups; ls != NULL; ls = g_slist_next(ls)) { struct a2dp_setup *setup = ls->data; GSList *l; for (l = setup->cb; l != NULL; l = g_slist_next(l)) { struct a2dp_setup_cb *cb = l->data; if (cb->id != id) continue; setup_ref(setup); setup_cb_free(cb); if (!setup->cb) { DBG("aborting setup %p", setup); if (!avdtp_abort(setup->session, setup->stream)) return TRUE; } setup_unref(setup); return TRUE; } } return FALSE; } gboolean a2dp_sep_lock(struct a2dp_sep *sep, struct avdtp *session) { if (sep->locked) return FALSE; DBG("SEP %p locked", sep->lsep); sep->locked = TRUE; return TRUE; } gboolean a2dp_sep_unlock(struct a2dp_sep *sep, struct avdtp *session) { struct a2dp_server *server = sep->server; avdtp_state_t state; GSList *l; state = avdtp_sep_get_state(sep->lsep); sep->locked = FALSE; DBG("SEP %p unlocked", sep->lsep); if (sep->type == AVDTP_SEP_TYPE_SOURCE) l = server->sources; else l = server->sinks; /* Unregister sep if it was removed */ if (g_slist_find(l, sep) == NULL) { a2dp_unregister_sep(sep); return TRUE; } if (!sep->stream || state == AVDTP_STATE_IDLE) return TRUE; switch (state) { case AVDTP_STATE_OPEN: /* Set timer here */ break; case AVDTP_STATE_STREAMING: if (avdtp_suspend(session, sep->stream) == 0) sep->suspending = TRUE; break; case AVDTP_STATE_IDLE: case AVDTP_STATE_CONFIGURED: case AVDTP_STATE_CLOSING: case AVDTP_STATE_ABORTING: default: break; } return TRUE; } struct avdtp_stream *a2dp_sep_get_stream(struct a2dp_sep *sep) { return sep->stream; } struct btd_device *a2dp_setup_get_device(struct a2dp_setup *setup) { if (setup->session == NULL) return NULL; return avdtp_get_device(setup->session); } const char *a2dp_setup_remote_path(struct a2dp_setup *setup) { if (setup->rsep) { return setup->rsep->path; } return NULL; } static int a2dp_source_probe(struct btd_service *service) { struct btd_device *dev = btd_service_get_device(service); DBG("path %s", device_get_path(dev)); source_init(service); return 0; } static void a2dp_source_remove(struct btd_service *service) { source_unregister(service); } static int a2dp_sink_probe(struct btd_service *service) { struct btd_device *dev = btd_service_get_device(service); DBG("path %s", device_get_path(dev)); return sink_init(service); } static void a2dp_sink_remove(struct btd_service *service) { sink_unregister(service); } static int a2dp_source_connect(struct btd_service *service) { struct btd_device *dev = btd_service_get_device(service); struct btd_adapter *adapter = device_get_adapter(dev); struct a2dp_server *server; const char *path = device_get_path(dev); DBG("path %s", path); server = find_server(servers, adapter); if (!server || !server->sink_enabled) { DBG("Unexpected error: cannot find server"); return -EPROTONOSUPPORT; } /* Return protocol not available if no record/endpoint exists */ if (server->sink_record_id == 0) return -ENOPROTOOPT; return source_connect(service); } static int a2dp_source_disconnect(struct btd_service *service) { struct btd_device *dev = btd_service_get_device(service); const char *path = device_get_path(dev); DBG("path %s", path); return source_disconnect(service); } static int a2dp_sink_connect(struct btd_service *service) { struct btd_device *dev = btd_service_get_device(service); struct btd_adapter *adapter = device_get_adapter(dev); struct a2dp_server *server; const char *path = device_get_path(dev); DBG("path %s", path); server = find_server(servers, adapter); if (!server || !server->source_enabled) { DBG("Unexpected error: cannot find server"); return -EPROTONOSUPPORT; } /* Return protocol not available if no record/endpoint exists */ if (server->source_record_id == 0) return -ENOPROTOOPT; return sink_connect(service); } static int a2dp_sink_disconnect(struct btd_service *service) { struct btd_device *dev = btd_service_get_device(service); const char *path = device_get_path(dev); DBG("path %s", path); return sink_disconnect(service); } static int a2dp_source_server_probe(struct btd_profile *p, struct btd_adapter *adapter) { struct a2dp_server *server; DBG("path %s", adapter_get_path(adapter)); server = find_server(servers, adapter); if (server != NULL) goto done; server = a2dp_server_register(adapter); if (server == NULL) return -EPROTONOSUPPORT; done: server->source_enabled = TRUE; return 0; } static void a2dp_source_server_remove(struct btd_profile *p, struct btd_adapter *adapter) { struct a2dp_server *server; DBG("path %s", adapter_get_path(adapter)); server = find_server(servers, adapter); if (!server) return; g_slist_free_full(server->sources, (GDestroyNotify) a2dp_unregister_sep); if (server->source_record_id) { adapter_service_remove(server->adapter, server->source_record_id); server->source_record_id = 0; } if (server->sink_record_id) return; a2dp_server_unregister(server); } static int a2dp_sink_server_probe(struct btd_profile *p, struct btd_adapter *adapter) { struct a2dp_server *server; DBG("path %s", adapter_get_path(adapter)); server = find_server(servers, adapter); if (server != NULL) goto done; server = a2dp_server_register(adapter); if (server == NULL) return -EPROTONOSUPPORT; done: server->sink_enabled = TRUE; return 0; } static void a2dp_sink_server_remove(struct btd_profile *p, struct btd_adapter *adapter) { struct a2dp_server *server; DBG("path %s", adapter_get_path(adapter)); server = find_server(servers, adapter); if (!server) return; g_slist_free_full(server->sinks, (GDestroyNotify) a2dp_unregister_sep); if (server->sink_record_id) { adapter_service_remove(server->adapter, server->sink_record_id); server->sink_record_id = 0; } if (server->source_record_id) return; a2dp_server_unregister(server); } static int media_server_probe(struct btd_adapter *adapter) { DBG("path %s", adapter_get_path(adapter)); return media_register(adapter); } static void media_server_remove(struct btd_adapter *adapter) { DBG("path %s", adapter_get_path(adapter)); media_unregister(adapter); } static struct btd_profile a2dp_source_profile = { .name = "a2dp-source", .priority = BTD_PROFILE_PRIORITY_MEDIUM, .remote_uuid = A2DP_SOURCE_UUID, .device_probe = a2dp_source_probe, .device_remove = a2dp_source_remove, .auto_connect = true, .connect = a2dp_source_connect, .disconnect = a2dp_source_disconnect, .adapter_probe = a2dp_sink_server_probe, .adapter_remove = a2dp_sink_server_remove, }; static struct btd_profile a2dp_sink_profile = { .name = "a2dp-sink", .priority = BTD_PROFILE_PRIORITY_MEDIUM, .remote_uuid = A2DP_SINK_UUID, .device_probe = a2dp_sink_probe, .device_remove = a2dp_sink_remove, .auto_connect = true, .connect = a2dp_sink_connect, .disconnect = a2dp_sink_disconnect, .adapter_probe = a2dp_source_server_probe, .adapter_remove = a2dp_source_server_remove, }; static struct btd_adapter_driver media_driver = { .name = "media", .probe = media_server_probe, .remove = media_server_remove, }; static int a2dp_init(void) { btd_register_adapter_driver(&media_driver); btd_profile_register(&a2dp_source_profile); btd_profile_register(&a2dp_sink_profile); return 0; } static void a2dp_exit(void) { btd_unregister_adapter_driver(&media_driver); btd_profile_unregister(&a2dp_source_profile); btd_profile_unregister(&a2dp_sink_profile); } BLUETOOTH_PLUGIN_DEFINE(a2dp, VERSION, BLUETOOTH_PLUGIN_PRIORITY_DEFAULT, a2dp_init, a2dp_exit) bluez-5.82/profiles/audio/PaxHeaders/bass.c0000644000000000000000000000005014772767672015717 xustar0020 atime=1743515579 20 ctime=1743591284 bluez-5.82/profiles/audio/bass.c0000644000000000000000000012201414772767672015400 0ustar00rootroot// SPDX-License-Identifier: LGPL-2.1-or-later /* * * BlueZ - Bluetooth protocol stack for Linux * * Copyright 2023-2025 NXP * */ #ifdef HAVE_CONFIG_H #include #endif #define _GNU_SOURCE #include #include #include #include #include #include #include #include #include #include #include "gdbus/gdbus.h" #include "lib/bluetooth.h" #include "lib/uuid.h" #include "lib/iso.h" #include "src/dbus-common.h" #include "src/shared/util.h" #include "src/shared/att.h" #include "src/shared/queue.h" #include "src/shared/gatt-db.h" #include "src/shared/gatt-client.h" #include "src/shared/gatt-server.h" #include "src/adapter.h" #include "src/shared/bass.h" #include "src/shared/bap.h" #include "src/shared/ad.h" #include "btio/btio.h" #include "src/plugin.h" #include "src/gatt-database.h" #include "src/device.h" #include "src/profile.h" #include "src/service.h" #include "src/log.h" #include "src/error.h" #define BASS_UUID_STR "0000184f-0000-1000-8000-00805f9b34fb" #define BCAAS_UUID_STR "00001852-0000-1000-8000-00805f9b34fb" #define MEDIA_ASSISTANT_INTERFACE "org.bluez.MediaAssistant1" enum assistant_state { ASSISTANT_STATE_IDLE, /* Assistant object was created for * the stream */ ASSISTANT_STATE_PENDING, /* Assistant object was pushed */ ASSISTANT_STATE_REQUESTING, /* Remote device requires * Broadcast_Code */ ASSISTANT_STATE_ACTIVE, /* Remote device started receiving * stream */ }; static const char *const str_state[] = { "ASSISTANT_STATE_IDLE", "ASSISTANT_STATE_PENDING", "ASSISTANT_STATE_REQUESTING", "ASSISTANT_STATE_ACTIVE", }; struct bass_data { struct btd_device *device; struct btd_service *service; struct bt_bass *bass; unsigned int src_id; unsigned int cp_id; unsigned int bis_id; }; struct bass_assistant { struct btd_device *device; /* Broadcast source device */ struct bass_data *data; /* BASS session with peer device */ uint8_t sgrp; uint8_t bis; uint32_t bid; struct bt_bap_qos qos; struct iovec *meta; struct iovec *caps; enum assistant_state state; char *path; }; struct bass_delegator { struct btd_device *device; /* Broadcast source device */ struct btd_service *service; struct bt_bcast_src *src; struct bt_bap *bap; unsigned int state_id; unsigned int bcode_id; uint8_t *bcode; unsigned int timeout; struct queue *bcode_reqs; struct queue *setups; unsigned int io_id; GIOChannel *io; }; struct bass_setup { struct bass_delegator *dg; char *path; struct bt_bap_stream *stream; uint8_t bis; struct bt_bap_qos qos; struct iovec *meta; struct iovec *config; struct bt_bap_pac *lpac; }; struct bass_bcode_req { struct bass_setup *setup; bt_bap_bcode_reply_t cb; void *user_data; }; static struct queue *sessions; static struct queue *assistants; static struct queue *delegators; static const char *state2str(enum assistant_state state); static struct bass_data *bass_data_new(struct btd_device *device); static void bass_data_add(struct bass_data *data); static void bass_data_remove(struct bass_data *data); static void bis_probe(uint8_t bis, uint8_t sgrp, struct iovec *caps, struct iovec *meta, struct bt_bap_qos *qos, void *user_data); static void bis_remove(struct bt_bap *bap, void *user_data); static void bass_debug(const char *str, void *user_data) { DBG_IDX(0xffff, "%s", str); } static gboolean req_timeout(gpointer user_data) { struct bass_delegator *dg = user_data; struct bass_bcode_req *req; DBG("delegator %p", dg); dg->timeout = 0; while ((req = queue_pop_head(dg->bcode_reqs))) { if (req->cb) req->cb(req->user_data, -ETIMEDOUT); free(req); } return FALSE; } static bool delegator_match_bap(const void *data, const void *match_data) { const struct bass_delegator *dg = data; const struct bt_bap *bap = match_data; return dg->bap == bap; } static void setup_set_bcode(uint8_t *bcode, struct bass_setup *setup, bt_bap_bcode_reply_t cb, void *user_data) { struct bt_bap_qos *qos = bt_bap_stream_get_qos(setup->stream); /* Allocate Broadcast Code inside setup QoS */ util_iov_free(setup->qos.bcast.bcode, 1); setup->qos.bcast.bcode = util_iov_new(bcode, BT_BASS_BCAST_CODE_SIZE); /* Refresh stream bcode */ qos->bcast.bcode = setup->qos.bcast.bcode; if (cb) cb(user_data, 0); } static bool match_setup_stream(const void *data, const void *user_data) { const struct bass_setup *setup = data; const struct bt_bap_stream *stream = user_data; return setup->stream == stream; } static void bass_req_bcode(struct bt_bap_stream *stream, bt_bap_bcode_reply_t reply, void *reply_data, void *user_data) { struct bt_bap *bap = bt_bap_stream_get_session(stream); struct bass_delegator *dg; struct bass_bcode_req *req; struct bass_setup *setup; dg = queue_find(delegators, delegator_match_bap, bap); if (!dg) { reply(reply_data, -EINVAL); return; } setup = queue_find(dg->setups, match_setup_stream, stream); if (!setup) { reply(reply_data, -EINVAL); return; } if (dg->bcode) { /* Broadcast Code has already been received before. */ setup_set_bcode(dg->bcode, setup, reply, reply_data); return; } /* Create a request for the Broadcast Code. The request * will be considered handled when the Broadcast Code is * received from a Broadcast Assistant. */ req = new0(struct bass_bcode_req, 1); if (!req) return; req->setup = setup; req->cb = reply; req->user_data = reply_data; queue_push_tail(dg->bcode_reqs, req); /* Mark the encryption status as "Broadcast Code Required" * in the Broadcast Receive State characteristic and notify * Broadcast Assistants. */ bt_bass_set_enc(dg->src, BT_BASS_BIG_ENC_STATE_BCODE_REQ); /* Add timeout for Broadcast Assistants to provide the Code. */ if (!dg->timeout) dg->timeout = g_timeout_add_seconds(10, req_timeout, dg); } static bool delegator_match_device(const void *data, const void *match_data) { const struct bass_delegator *dg = data; const struct btd_device *device = match_data; return dg->device == device; } static int stream_get_bis(struct bt_bap_stream *stream) { char *path = bt_bap_stream_get_user_data(stream); const char *strbis; int bis; strbis = strstr(path, "/bis"); if (!strbis) return 0; if (sscanf(strbis, "/bis%d", &bis) < 0) return 0; return bis; } static void append_stream(void *data, void *user_data) { struct bt_bap_stream *stream = data; struct sockaddr_iso_bc *addr = user_data; uint8_t bis = stream_get_bis(stream); DBG("%d", bis); addr->bc_bis[addr->bc_num_bis] = bis; addr->bc_num_bis++; } static bool link_io_unset(const void *data, const void *match_data) { struct bt_bap_stream *link = (struct bt_bap_stream *)data; return !bt_bap_stream_get_io(link); } static void connect_cb(GIOChannel *io, GError *err, void *user_data) { struct bass_setup *setup = user_data; struct bt_bap_stream *stream; struct queue *links; int fd; DBG(""); if (!setup || !setup->stream) return; stream = setup->stream; links = bt_bap_stream_io_get_links(stream); /* Set fds for the stream and all its links. */ if (bt_bap_stream_get_io(stream)) stream = queue_find(links, link_io_unset, NULL); fd = g_io_channel_unix_get_fd(io); if (bt_bap_stream_set_io(stream, fd)) { g_io_channel_set_close_on_unref(io, FALSE); } } static bool link_enabled(const void *data, const void *match_data) { struct bt_bap_stream *stream = (struct bt_bap_stream *)data; uint8_t state = bt_bap_stream_get_state(stream); return ((state == BT_BAP_STREAM_STATE_ENABLING) || bt_bap_stream_get_io(stream)); } static void bap_state_changed(struct bt_bap_stream *stream, uint8_t old_state, uint8_t new_state, void *user_data) { struct bass_delegator *dg = user_data; int bis; struct bt_bap *bap = bt_bap_stream_get_session(stream); struct sockaddr_iso_bc iso_bc_addr = {0}; struct queue *links; GError *gerr = NULL; struct bt_bap_qos *bap_qos = bt_bap_stream_get_qos(stream); struct bt_iso_qos qos; struct bass_setup *setup = queue_find(dg->setups, match_setup_stream, stream); if (dg->bap != bap) return; bis = stream_get_bis(stream); DBG("stream %p: %s(%u) -> %s(%u)", stream, bt_bap_stream_statestr(old_state), old_state, bt_bap_stream_statestr(new_state), new_state); switch (new_state) { case BT_BAP_STREAM_STATE_ENABLING: links = bt_bap_stream_io_get_links(stream); if (bt_bap_stream_get_io(stream) || queue_find(links, link_enabled, NULL)) /* The first enabled link will create and set fds * for all links. * * If the stream io has already been set, the stream * will automatically be started once all state_changed * callbacks are notified. * * If there is any other linked stream that has already * been enabled, the stream fd will be set once it is * notified from kernel and the stream will be started. */ break; iso_bc_addr.bc_bdaddr_type = btd_device_get_bdaddr_type(dg->device); memcpy(&iso_bc_addr.bc_bdaddr, device_get_address(dg->device), sizeof(bdaddr_t)); append_stream(stream, &iso_bc_addr); queue_foreach(links, append_stream, &iso_bc_addr); bt_bap_qos_to_iso_qos(bap_qos, &qos); if (!bt_io_set(dg->io, &gerr, BT_IO_OPT_QOS, &qos, BT_IO_OPT_INVALID)) { error("bt_io_set: %s", gerr->message); g_error_free(gerr); break; } if (!bt_io_bcast_accept(dg->io, connect_cb, setup, NULL, &gerr, BT_IO_OPT_ISO_BC_NUM_BIS, iso_bc_addr.bc_num_bis, BT_IO_OPT_ISO_BC_BIS, iso_bc_addr.bc_bis, BT_IO_OPT_INVALID)) { error("bt_io_bcast_accept: %s", gerr->message); g_error_free(gerr); } break; case BT_BAP_STREAM_STATE_STREAMING: /* BAP stream was started. Mark BIS index as synced inside the * Broadcast Receive State characteristic and notify peers about * the update. */ bt_bass_set_bis_sync(dg->src, bis); break; case BT_BAP_STREAM_STATE_CONFIG: if (old_state == BT_BAP_STREAM_STATE_STREAMING) /* BAP stream was disabled. Clear BIS index from the * bitmask inside the Broadcast Receive State * characteristic and notify peers about the update. */ bt_bass_clear_bis_sync(dg->src, bis); break; case BT_BAP_STREAM_STATE_IDLE: bt_bass_clear_bis_sync(dg->src, bis); setup->stream = NULL; break; } } static void setup_configure_stream(struct bass_setup *setup) { setup->stream = bt_bap_stream_new(setup->dg->bap, setup->lpac, NULL, &setup->qos, setup->config); if (!setup->stream) return; if (asprintf(&setup->path, "%s/bis%d", device_get_path(setup->dg->device), setup->bis) < 0) return; bt_bap_stream_set_user_data(setup->stream, setup->path); bt_bap_stream_config(setup->stream, &setup->qos, setup->config, NULL, NULL); bt_bap_stream_metadata(setup->stream, setup->meta, NULL, NULL); } static void stream_unlink(void *data, void *user_data) { struct bt_bap_stream *link = data; struct bt_bap_stream *stream = user_data; bt_bap_stream_io_unlink(link, stream); } static void bass_remove_bis(struct bass_setup *setup) { struct queue *links = bt_bap_stream_io_get_links(setup->stream); queue_foreach(links, stream_unlink, setup->stream); bt_bap_stream_release(setup->stream, NULL, NULL); } static void setup_disable_streaming(void *data, void *user_data) { struct bass_setup *setup = data; struct queue *links = bt_bap_stream_io_get_links(setup->stream); if (!setup->stream) return; if (bt_bap_stream_get_state(setup->stream) != BT_BAP_STREAM_STATE_STREAMING) return; queue_foreach(links, stream_unlink, setup->stream); bt_bap_stream_disable(setup->stream, false, NULL, NULL); } static void bass_add_bis(struct bass_setup *setup) { queue_foreach(setup->dg->setups, setup_disable_streaming, NULL); setup_configure_stream(setup); } static void bis_handler(uint8_t bis, uint8_t sgrp, struct iovec *caps, struct iovec *meta, struct bt_bap_qos *qos, void *user_data) { struct bass_delegator *dg = user_data; struct bt_bap_pac *lpac; struct bass_setup *setup; /* Check if this stream caps match any local PAC */ bt_bap_verify_bis(dg->bap, bis, caps, &lpac); if (!lpac) return; setup = new0(struct bass_setup, 1); if (!setup) return; setup->dg = dg; setup->bis = bis; setup->lpac = lpac; setup->qos = *qos; setup->qos.bcast.bcode = util_iov_dup(qos->bcast.bcode, 1); setup->meta = util_iov_dup(meta, 1); setup->config = util_iov_dup(caps, 1); queue_push_tail(setup->dg->setups, setup); /* Only handle streams required by the Brodcast Assistant. */ if (!bt_bass_check_bis(dg->src, bis)) return; setup_configure_stream(setup); } static gboolean big_info_cb(GIOChannel *io, GIOCondition cond, gpointer user_data) { struct bass_delegator *dg = user_data; GError *err = NULL; struct bt_iso_base base; struct bt_iso_qos qos; struct iovec iov; struct bt_bap_qos bap_qos = {0}; dg->io_id = 0; bt_io_get(io, &err, BT_IO_OPT_BASE, &base, BT_IO_OPT_QOS, &qos, BT_IO_OPT_INVALID); if (err) { error("%s", err->message); g_error_free(err); return FALSE; } iov.iov_base = base.base; iov.iov_len = base.base_len; /* Create BAP QoS structure */ bt_bap_iso_qos_to_bap_qos(&qos, &bap_qos); bt_bap_parse_base(&iov, &bap_qos, bass_debug, bis_handler, dg); util_iov_free(bap_qos.bcast.bcode, 1); return FALSE; } static void confirm_cb(GIOChannel *io, void *user_data) { struct bass_delegator *dg = user_data; DBG(""); /* Close the listen io */ g_io_channel_shutdown(dg->io, TRUE, NULL); g_io_channel_unref(dg->io); g_io_channel_ref(io); dg->io = io; /* Update Broadcast Receive State characteristic value and notify * peers. */ if (bt_bass_set_pa_sync(dg->src, BT_BASS_SYNCHRONIZED_TO_PA)) DBG("Failed to update Broadcast Receive State characteristic"); /* Register BAP stream state changed callback. */ dg->state_id = bt_bap_state_register(dg->bap, bap_state_changed, NULL, dg, NULL); /* Register callback to handle Broadcast Code requests from * upper layers. */ dg->bcode_id = bt_bap_bcode_cb_register(dg->bap, bass_req_bcode, NULL, NULL); dg->io_id = g_io_add_watch(io, G_IO_OUT, big_info_cb, dg); } static void bap_attached(struct bt_bap *bap, void *user_data) { struct btd_service *service; struct btd_profile *p; struct btd_device *device; struct btd_adapter *adapter; struct bass_delegator *dg; struct bass_data *data; GError *err = NULL; DBG("%p", bap); service = bt_bap_get_user_data(bap); if (!service) return; p = btd_service_get_profile(service); if (!p) return; /* Only handle sessions with Broadcast Sources */ if (!g_str_equal(p->remote_uuid, BCAAS_UUID_STR)) return; device = btd_service_get_device(service); adapter = device_get_adapter(device); /* Create BASS session with the Broadcast Source */ data = bass_data_new(device); data->bis_id = bt_bap_bis_cb_register(bap, bis_probe, bis_remove, device, NULL); bass_data_add(data); dg = queue_find(delegators, delegator_match_device, device); if (!dg) /* Only probe devices added via Broadcast Assistants */ return; if (dg->service) /* Service has already been probed */ return; dg->service = service; dg->bap = bap; dg->io = bt_io_listen(NULL, confirm_cb, dg, NULL, &err, BT_IO_OPT_SOURCE_BDADDR, btd_adapter_get_address(adapter), BT_IO_OPT_SOURCE_TYPE, btd_adapter_get_address_type(adapter), BT_IO_OPT_DEST_BDADDR, device_get_address(device), BT_IO_OPT_DEST_TYPE, btd_device_get_bdaddr_type(device), BT_IO_OPT_MODE, BT_IO_MODE_ISO, BT_IO_OPT_QOS, &bap_sink_pa_qos, BT_IO_OPT_INVALID); if (!dg->io) { error("%s", err->message); g_error_free(err); return; } /* Take ownership for the service by setting the user data. */ btd_service_set_user_data(service, dg); } static void setup_free(void *data) { struct bass_setup *setup = data; DBG("setup %p", setup); util_iov_free(setup->qos.bcast.bcode, 1); util_iov_free(setup->meta, 1); util_iov_free(setup->config, 1); free(setup->path); /* Clear bis index from the bis sync bitmask, if it * has been previously set. */ bt_bass_clear_bis_sync(setup->dg->src, setup->bis); } static bool match_device(const void *data, const void *match_data) { const struct bass_data *bdata = data; const struct btd_device *device = match_data; return bdata->device == device; } static void bap_detached(struct bt_bap *bap, void *user_data) { struct btd_service *service; struct btd_profile *p; struct btd_device *device; struct bass_delegator *dg; struct bass_data *data; DBG("%p", bap); service = bt_bap_get_user_data(bap); if (!service) return; p = btd_service_get_profile(service); if (!p) return; /* Only handle sessions with Broadcast Sources */ if (!g_str_equal(p->remote_uuid, BCAAS_UUID_STR)) return; device = btd_service_get_device(service); /* Remove BASS session with the Broadcast Source device */ data = queue_find(sessions, match_device, device); if (data) { bt_bap_bis_cb_unregister(bap, data->bis_id); bass_data_remove(data); } dg = queue_remove_if(delegators, delegator_match_device, device); if (!dg) return; DBG("%p", dg); if (dg->io_id) g_source_remove(dg->io_id); if (dg->io) { g_io_channel_shutdown(dg->io, TRUE, NULL); g_io_channel_unref(dg->io); } queue_destroy(dg->setups, setup_free); /* Update Broadcast Receive State characteristic value and notify * peers. */ if (bt_bass_set_pa_sync(dg->src, BT_BASS_NOT_SYNCHRONIZED_TO_PA)) DBG("Failed to update Broadcast Receive State characteristic"); /* Unregister BAP stream state changed callback. */ bt_bap_state_unregister(dg->bap, dg->state_id); bt_bap_bcode_cb_unregister(dg->bap, dg->bcode_id); if (dg->timeout) g_source_remove(dg->timeout); queue_destroy(dg->bcode_reqs, free); free(dg->bcode); free(dg); btd_service_set_user_data(service, NULL); } static void assistant_set_state(struct bass_assistant *assistant, enum assistant_state state) { enum assistant_state old_state = assistant->state; const char *str; if (old_state == state) return; assistant->state = state; DBG("State changed %s: %s -> %s", assistant->path, str_state[old_state], str_state[state]); str = state2str(state); if (g_strcmp0(str, state2str(old_state)) != 0) g_dbus_emit_property_changed(btd_get_dbus_connection(), assistant->path, MEDIA_ASSISTANT_INTERFACE, "State"); } static int assistant_parse_qos(struct bass_assistant *assistant, DBusMessageIter *iter) { DBusMessageIter dict; const char *key; dbus_message_iter_recurse(iter, &dict); while (dbus_message_iter_get_arg_type(&dict) == DBUS_TYPE_DICT_ENTRY) { DBusMessageIter value, entry; int var; dbus_message_iter_recurse(&dict, &entry); dbus_message_iter_get_basic(&entry, &key); dbus_message_iter_next(&entry); dbus_message_iter_recurse(&entry, &value); var = dbus_message_iter_get_arg_type(&value); if (!strcasecmp(key, "BCode")) { DBusMessageIter array; struct iovec iov = {0}; if (var != DBUS_TYPE_ARRAY) return -EINVAL; dbus_message_iter_recurse(&value, &array); dbus_message_iter_get_fixed_array(&array, &iov.iov_base, (int *)&iov.iov_len); if (iov.iov_len != BT_BASS_BCAST_CODE_SIZE) { error("Invalid size for BCode: %zu != 16", iov.iov_len); return -EINVAL; } util_iov_free(assistant->qos.bcast.bcode, 1); assistant->qos.bcast.bcode = util_iov_dup(&iov, 1); return 0; } dbus_message_iter_next(&dict); } return 0; } static int assistant_parse_props(struct bass_assistant *assistant, DBusMessageIter *props) { DBusMessageIter value, entry, array; const char *key; while (dbus_message_iter_get_arg_type(props) == DBUS_TYPE_DICT_ENTRY) { dbus_message_iter_recurse(props, &entry); dbus_message_iter_get_basic(&entry, &key); dbus_message_iter_next(&entry); dbus_message_iter_recurse(&entry, &value); if (!strcasecmp(key, "Metadata")) { struct iovec iov; if (dbus_message_iter_get_arg_type(&value) != DBUS_TYPE_ARRAY) goto fail; dbus_message_iter_recurse(&value, &array); dbus_message_iter_get_fixed_array(&array, &iov.iov_base, (int *)&iov.iov_len); util_iov_free(assistant->meta, 1); assistant->meta = util_iov_dup(&iov, 1); DBG("Parsed Metadata"); } else if (!strcasecmp(key, "QoS")) { if (dbus_message_iter_get_arg_type(&value) != DBUS_TYPE_ARRAY) goto fail; if (assistant_parse_qos(assistant, &value)) goto fail; DBG("Parsed QoS"); } dbus_message_iter_next(props); } return 0; fail: DBG("Failed parsing %s", key); return -EINVAL; } static DBusMessage *push(DBusConnection *conn, DBusMessage *msg, void *user_data) { struct bass_assistant *assistant = user_data; struct bt_bass_bcast_audio_scan_cp_hdr hdr; struct bt_bass_add_src_params params; struct iovec iov = {0}; uint32_t bis_sync = 0; uint8_t meta_len = 0; int err; DBusMessageIter props, dict; DBG(""); dbus_message_iter_init(msg, &props); if (dbus_message_iter_get_arg_type(&props) != DBUS_TYPE_ARRAY) { DBG("Unable to parse properties"); return btd_error_invalid_args(msg); } dbus_message_iter_recurse(&props, &dict); if (assistant_parse_props(assistant, &dict)) { DBG("Unable to parse properties"); return btd_error_invalid_args(msg); } hdr.op = BT_BASS_ADD_SRC; if (device_get_le_address_type(assistant->device) == BDADDR_LE_PUBLIC) params.addr_type = BT_BASS_ADDR_PUBLIC; else params.addr_type = BT_BASS_ADDR_RANDOM; bacpy(¶ms.addr, device_get_address(assistant->device)); put_le24(assistant->bid, params.bid); params.pa_sync = PA_SYNC_NO_PAST; params.pa_interval = PA_INTERVAL_UNKNOWN; params.num_subgroups = assistant->sgrp + 1; util_iov_append(&iov, ¶ms, sizeof(params)); /* Metadata and the BIS index associated with the MediaAssistant * object will be set in the subgroup they belong to. For the other * subgroups, no metadata and no BIS index will be provided. */ for (uint8_t sgrp = 0; sgrp < assistant->sgrp; sgrp++) { util_iov_append(&iov, &bis_sync, sizeof(bis_sync)); util_iov_append(&iov, &meta_len, sizeof(meta_len)); } bis_sync = (1 << (assistant->bis - 1)); meta_len = assistant->meta->iov_len; util_iov_append(&iov, &bis_sync, sizeof(bis_sync)); util_iov_append(&iov, &meta_len, sizeof(meta_len)); util_iov_append(&iov, assistant->meta->iov_base, assistant->meta->iov_len); err = bt_bass_send(assistant->data->bass, &hdr, &iov); if (err) { DBG("Unable to send BASS Write Command"); return btd_error_failed(msg, strerror(-err)); } free(iov.iov_base); assistant_set_state(assistant, ASSISTANT_STATE_PENDING); return g_dbus_create_reply(msg, DBUS_TYPE_INVALID); } static const GDBusMethodTable assistant_methods[] = { {GDBUS_EXPERIMENTAL_ASYNC_METHOD("Push", GDBUS_ARGS({ "Props", "a{sv}" }), NULL, push)}, {}, }; static const char *state2str(enum assistant_state state) { switch (state) { case ASSISTANT_STATE_IDLE: return "idle"; case ASSISTANT_STATE_PENDING: return "pending"; case ASSISTANT_STATE_REQUESTING: return "requesting"; case ASSISTANT_STATE_ACTIVE: return "active"; } return NULL; } static gboolean get_state(const GDBusPropertyTable *property, DBusMessageIter *iter, void *data) { struct bass_assistant *assistant = data; const char *state = state2str(assistant->state); dbus_message_iter_append_basic(iter, DBUS_TYPE_STRING, &state); return TRUE; } static gboolean get_metadata(const GDBusPropertyTable *property, DBusMessageIter *iter, void *data) { struct bass_assistant *assistant = data; DBusMessageIter array; dbus_message_iter_open_container(iter, DBUS_TYPE_ARRAY, DBUS_TYPE_BYTE_AS_STRING, &array); if (assistant->meta) dbus_message_iter_append_fixed_array(&array, DBUS_TYPE_BYTE, &assistant->meta->iov_base, assistant->meta->iov_len); dbus_message_iter_close_container(iter, &array); return TRUE; } static gboolean get_qos(const GDBusPropertyTable *property, DBusMessageIter *iter, void *data) { struct bass_assistant *assistant = data; DBusMessageIter dict; uint8_t arr[BT_BASS_BCAST_CODE_SIZE] = {0}; uint8_t *bcode = arr; if (assistant->qos.bcast.bcode) memcpy(arr, assistant->qos.bcast.bcode->iov_base, BT_BASS_BCAST_CODE_SIZE); dbus_message_iter_open_container(iter, DBUS_TYPE_ARRAY, DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING DBUS_TYPE_STRING_AS_STRING DBUS_TYPE_VARIANT_AS_STRING DBUS_DICT_ENTRY_END_CHAR_AS_STRING, &dict); dict_append_entry(&dict, "Encryption", DBUS_TYPE_BYTE, &assistant->qos.bcast.encryption); dict_append_array(&dict, "BCode", DBUS_TYPE_BYTE, &bcode, BT_BASS_BCAST_CODE_SIZE); dbus_message_iter_close_container(iter, &dict); return TRUE; } static const GDBusPropertyTable assistant_properties[] = { { "State", "s", get_state }, { "Metadata", "ay", get_metadata, NULL, NULL, G_DBUS_PROPERTY_FLAG_EXPERIMENTAL }, { "QoS", "a{sv}", get_qos, NULL, NULL, G_DBUS_PROPERTY_FLAG_EXPERIMENTAL }, { } }; static void assistant_free(void *data) { struct bass_assistant *assistant = data; g_free(assistant->path); util_iov_free(assistant->meta, 1); util_iov_free(assistant->caps, 1); free(assistant); } static void src_ad_search_bid(void *data, void *user_data) { struct bt_ad_service_data *sd = data; struct bass_assistant *assistant = user_data; struct iovec iov; if (sd->uuid.type != BT_UUID16 || sd->uuid.value.u16 != BCAA_SERVICE) return; iov.iov_base = sd->data; iov.iov_len = sd->len; util_iov_pull_le24(&iov, &assistant->bid); } static struct bass_assistant *assistant_new(struct btd_adapter *adapter, struct btd_device *device, struct bass_data *data, uint8_t sgrp, uint8_t bis, struct bt_bap_qos *qos, struct iovec *meta, struct iovec *caps) { struct bass_assistant *assistant; char src_addr[18]; char dev_addr[18]; assistant = new0(struct bass_assistant, 1); if (!assistant) return NULL; DBG("assistant %p", assistant); assistant->device = device; assistant->data = data; assistant->sgrp = sgrp; assistant->bis = bis; assistant->qos = *qos; /* Create an internal copy for bcode */ assistant->qos.bcast.bcode = util_iov_dup(qos->bcast.bcode, 1); assistant->meta = util_iov_dup(meta, 1); assistant->caps = util_iov_dup(caps, 1); btd_device_foreach_service_data(assistant->device, src_ad_search_bid, assistant); ba2str(device_get_address(device), src_addr); ba2str(device_get_address(data->device), dev_addr); assistant->path = g_strdup_printf("%s/src_%s/dev_%s/bis%d", adapter_get_path(adapter), src_addr, dev_addr, bis); g_strdelimit(assistant->path, ":", '_'); if (!assistants) assistants = queue_new(); queue_push_tail(assistants, assistant); return assistant; } static void bis_probe(uint8_t bis, uint8_t sgrp, struct iovec *caps, struct iovec *meta, struct bt_bap_qos *qos, void *user_data) { struct btd_device *device = user_data; const struct queue_entry *entry; struct bt_bap *bap; struct bt_bap_pac *pac; struct bass_assistant *assistant; char addr[18]; for (entry = queue_get_entries(sessions); entry; entry = entry->next) { struct bass_data *data = entry->data; struct btd_adapter *adapter = device_get_adapter(data->device); if (!bt_bass_get_client(data->bass)) /* Only client sessions must be handled */ continue; bap = bt_bap_get_session(bt_bass_get_att(data->bass), NULL); if (!bap) continue; /* Check stream capabilities against peer caps. */ bt_bap_verify_bis(bap, bis, caps, &pac); if (!pac) /* Capabilities did not match. */ continue; ba2str(device_get_address(device), addr); DBG("%s data %p BIS %d", addr, data, bis); assistant = assistant_new(adapter, device, data, sgrp, bis, qos, meta, caps); if (g_dbus_register_interface(btd_get_dbus_connection(), assistant->path, MEDIA_ASSISTANT_INTERFACE, assistant_methods, NULL, assistant_properties, assistant, assistant_free) == FALSE) DBG("Could not register path %s", assistant->path); } } static bool assistant_match_device(const void *data, const void *match_data) { const struct bass_assistant *assistant = data; const struct btd_device *device = match_data; return (assistant->device == device); } static void unregister_assistant(void *data) { struct bass_assistant *assistant = data; DBG("%p", assistant); g_dbus_unregister_interface(btd_get_dbus_connection(), assistant->path, MEDIA_ASSISTANT_INTERFACE); } static void bis_remove(struct bt_bap *bap, void *user_data) { struct btd_device *device = user_data; queue_remove_all(assistants, assistant_match_device, device, unregister_assistant); } static struct bass_data *bass_data_new(struct btd_device *device) { struct bass_data *data; data = new0(struct bass_data, 1); data->device = device; return data; } static void bass_data_add(struct bass_data *data) { DBG("data %p", data); if (queue_find(sessions, NULL, data)) { error("data %p already added", data); return; } bt_bass_set_debug(data->bass, bass_debug, NULL, NULL); if (!sessions) sessions = queue_new(); queue_push_tail(sessions, data); if (data->service) btd_service_set_user_data(data->service, data); } static bool match_data(const void *data, const void *match_data) { const struct bass_data *bdata = data; const struct bt_bass *bass = match_data; return bdata->bass == bass; } static bool assistant_match_data(const void *data, const void *match_data) { const struct bass_assistant *assistant = data; const struct bass_data *bdata = match_data; return (assistant->data == bdata); } static void bass_data_free(struct bass_data *data) { if (data->service) { btd_service_set_user_data(data->service, NULL); bt_bass_set_user_data(data->bass, NULL); } bt_bass_src_unregister(data->bass, data->src_id); bt_bass_cp_handler_unregister(data->bass, data->cp_id); bt_bass_unref(data->bass); queue_remove_all(assistants, assistant_match_data, data, unregister_assistant); free(data); } static void bass_data_remove(struct bass_data *data) { DBG("data %p", data); if (!queue_remove(sessions, data)) return; bass_data_free(data); if (queue_isempty(sessions)) { queue_destroy(sessions, NULL); sessions = NULL; } } static void bass_detached(struct bt_bass *bass, void *user_data) { struct bass_data *data; DBG("%p", bass); data = queue_find(sessions, match_data, bass); if (!data) { error("Unable to find bass session"); return; } /* If there is a service it means there is BASS thus we can keep * instance allocated. */ if (data->service) return; bass_data_remove(data); } static int handle_add_src_req(struct bt_bcast_src *bcast_src, struct bt_bass_add_src_params *params, struct bass_data *data) { struct btd_adapter *adapter = device_get_adapter(data->device); struct btd_device *device; struct bass_delegator *dg; /* Create device for Broadcast Source using the parameters * provided by Broadcast Assistant. */ device = btd_adapter_get_device(adapter, ¶ms->addr, params->addr_type); if (!device) { DBG("Unable to get device"); return -EINVAL; } DBG("device %p", device); /* Probe Broadcast Source, if it has not already been * autonomously probed inside BAP. */ if (!btd_device_get_service(device, BCAAS_UUID_STR)) goto probe; return 0; probe: dg = new0(struct bass_delegator, 1); if (!dg) return -ENOMEM; dg->device = device; dg->src = bcast_src; dg->bcode_reqs = queue_new(); dg->setups = queue_new(); if (!delegators) delegators = queue_new(); queue_push_tail(delegators, dg); DBG("delegator %p", dg); /* Add Broadcast Audio Announcement Service UUID * to device and probe service. */ btd_device_add_uuid(device, BCAAS_UUID_STR); return 0; } static bool delegator_match_src(const void *data, const void *match_data) { const struct bass_delegator *dg = data; const struct bt_bcast_src *src = match_data; return dg->src == src; } static int handle_set_bcode_req(struct bt_bcast_src *bcast_src, struct bt_bass_set_bcast_code_params *params, struct bass_data *data) { struct bass_delegator *dg; struct bass_bcode_req *req; dg = queue_find(delegators, delegator_match_src, bcast_src); if (!dg) return -EINVAL; dg->bcode = new0(uint8_t, BT_BASS_BCAST_CODE_SIZE); memcpy(dg->bcode, params->bcast_code, BT_BASS_BCAST_CODE_SIZE); if (dg->timeout) { g_source_remove(dg->timeout); dg->timeout = 0; } /* Set the Broadcast Code for each stream that required it. */ while ((req = queue_pop_head(dg->bcode_reqs))) { setup_set_bcode(dg->bcode, req->setup, req->cb, req->user_data); free(req); } return 0; } static bool setup_match_bis(const void *data, const void *match_data) { const struct bass_setup *setup = data; const int bis = PTR_TO_INT(match_data); return setup->bis == bis; } static void bass_update_bis_sync(struct bass_delegator *dg, struct bt_bcast_src *bcast_src) { for (int bis = 1; bis < ISO_MAX_NUM_BIS; bis++) { struct bass_setup *setup = queue_find(dg->setups, setup_match_bis, INT_TO_PTR(bis)); uint8_t state; if (!setup) continue; state = bt_bap_stream_get_state(setup->stream); if (!setup->stream && bt_bass_check_bis(bcast_src, bis)) bass_add_bis(setup); else if (setup->stream && state == BT_BAP_STREAM_STATE_STREAMING && !bt_bass_check_bis(bcast_src, bis)) bass_remove_bis(setup); } } static int handle_mod_src_req(struct bt_bcast_src *bcast_src, struct bt_bass_mod_src_params *params, struct bass_data *data) { struct bass_delegator *dg; uint8_t sync_state; int err = 0; DBG(""); dg = queue_find(delegators, delegator_match_src, bcast_src); if (!dg) return -EINVAL; err = bt_bass_get_pa_sync(bcast_src, &sync_state); if (err) return err; switch (sync_state) { case BT_BASS_SYNCHRONIZED_TO_PA: if (params->pa_sync == PA_SYNC_NO_SYNC) { g_io_channel_shutdown(dg->io, TRUE, NULL); g_io_channel_unref(dg->io); dg->io = NULL; bt_bass_set_pa_sync(dg->src, BT_BASS_NOT_SYNCHRONIZED_TO_PA); } else { bass_update_bis_sync(dg, bcast_src); } break; case BT_BASS_NOT_SYNCHRONIZED_TO_PA: if (params->pa_sync == PA_SYNC_NO_PAST) { struct btd_adapter *adapter = device_get_adapter(dg->device); GError *err = NULL; dg->io = bt_io_listen(NULL, confirm_cb, dg, NULL, &err, BT_IO_OPT_SOURCE_BDADDR, btd_adapter_get_address(adapter), BT_IO_OPT_SOURCE_TYPE, btd_adapter_get_address_type(adapter), BT_IO_OPT_DEST_BDADDR, device_get_address(dg->device), BT_IO_OPT_DEST_TYPE, btd_device_get_bdaddr_type(dg->device), BT_IO_OPT_MODE, BT_IO_MODE_ISO, BT_IO_OPT_QOS, &bap_sink_pa_qos, BT_IO_OPT_INVALID); if (!dg->io) { error("%s", err->message); g_error_free(err); } } break; } return 0; } static int cp_handler(struct bt_bcast_src *bcast_src, uint8_t op, void *params, void *user_data) { struct bass_data *data = user_data; int err = 0; switch (op) { case BT_BASS_ADD_SRC: err = handle_add_src_req(bcast_src, params, data); break; case BT_BASS_SET_BCAST_CODE: err = handle_set_bcode_req(bcast_src, params, data); break; case BT_BASS_MOD_SRC: err = handle_mod_src_req(bcast_src, params, data); break; } return err; } static void bass_attached(struct bt_bass *bass, void *user_data) { struct bass_data *data; struct bt_att *att; struct btd_device *device; DBG("%p", bass); data = queue_find(sessions, match_data, bass); if (data) return; att = bt_bass_get_att(bass); if (!att) return; device = btd_adapter_find_device_by_fd(bt_att_get_fd(att)); if (!device) { error("Unable to find device"); return; } data = bass_data_new(device); data->bass = bass; data->cp_id = bt_bass_cp_handler_register(data->bass, cp_handler, NULL, data); bass_data_add(data); } static void bass_handle_bcode_req(struct bass_assistant *assistant, int id) { struct bt_bass_bcast_audio_scan_cp_hdr hdr; struct bt_bass_set_bcast_code_params params = {0}; struct iovec iov = {0}; int err; assistant_set_state(assistant, ASSISTANT_STATE_REQUESTING); hdr.op = BT_BASS_SET_BCAST_CODE; params.id = id; if (assistant->qos.bcast.bcode) memcpy(params.bcast_code, assistant->qos.bcast.bcode->iov_base, BT_BASS_BCAST_CODE_SIZE); iov.iov_base = malloc0(sizeof(params)); if (!iov.iov_base) return; util_iov_push_mem(&iov, sizeof(params), ¶ms); err = bt_bass_send(assistant->data->bass, &hdr, &iov); if (err) { DBG("Unable to send BASS Write Command"); return; } free(iov.iov_base); } static void bass_src_changed(uint8_t id, uint32_t bid, uint8_t enc, uint32_t bis_sync, void *user_data) { const struct queue_entry *entry; for (entry = queue_get_entries(assistants); entry; entry = entry->next) { struct bass_assistant *assistant = entry->data; uint32_t bis = 1 << (assistant->bis - 1); if (assistant->bid != bid) /* Only handle assistant objects * that match the source */ continue; switch (enc) { case BT_BASS_BIG_ENC_STATE_BCODE_REQ: if (assistant->state != ASSISTANT_STATE_PENDING) /* Only handle assistant objects that * have been pushed by the user */ break; /* Provide Broadcast Code to peer */ bass_handle_bcode_req(assistant, id); break; case BT_BASS_BIG_ENC_STATE_NO_ENC: if (assistant->state != ASSISTANT_STATE_PENDING) /* Only handle assistant objects that * have been pushed by the user */ break; /* Match BIS index */ if (bis & bis_sync) assistant_set_state(assistant, ASSISTANT_STATE_ACTIVE); break; case BT_BASS_BIG_ENC_STATE_DEC: /* Only handle assistant objects that * have requested a Broadcast Code */ if (assistant->state != ASSISTANT_STATE_REQUESTING) break; /* Match BIS index */ if (bis & bis_sync) assistant_set_state(assistant, ASSISTANT_STATE_ACTIVE); break; default: continue; } } } static int bass_probe(struct btd_service *service) { struct btd_device *device = btd_service_get_device(service); struct btd_adapter *adapter = device_get_adapter(device); struct btd_gatt_database *database = btd_adapter_get_database(adapter); struct bass_data *data = btd_service_get_user_data(service); char addr[18]; ba2str(device_get_address(device), addr); DBG("%s", addr); /* Ignore, if we were probed for this device already */ if (data) { error("Profile probed twice for the same device!"); return -EINVAL; } data = bass_data_new(device); data->service = service; data->bass = bt_bass_new(btd_gatt_database_get_db(database), btd_device_get_gatt_db(device), btd_adapter_get_address(adapter)); if (!data->bass) { error("Unable to create BASS instance"); free(data); return -EINVAL; } bass_data_add(data); bt_bass_set_user_data(data->bass, service); /* Register callback to be called when notifications for * Broadcast Receive State characteristics are received. */ data->src_id = bt_bass_src_register(data->bass, bass_src_changed, data, NULL); data->cp_id = bt_bass_cp_handler_register(data->bass, cp_handler, NULL, data); return 0; } static void bass_remove(struct btd_service *service) { struct btd_device *device = btd_service_get_device(service); struct bass_data *data; char addr[18]; ba2str(device_get_address(device), addr); DBG("%s", addr); data = btd_service_get_user_data(service); if (!data) { error("BASS service not handled by profile"); return; } bass_data_remove(data); } static int bass_accept(struct btd_service *service) { struct btd_device *device = btd_service_get_device(service); struct bt_gatt_client *client = btd_device_get_gatt_client(device); struct bass_data *data = btd_service_get_user_data(service); char addr[18]; ba2str(device_get_address(device), addr); DBG("%s", addr); if (!data) { error("BASS service not handled by profile"); return -EINVAL; } if (!bt_bass_attach(data->bass, client)) { error("BASS unable to attach"); return -EINVAL; } btd_service_connecting_complete(service, 0); return 0; } static int bass_disconnect(struct btd_service *service) { struct bass_data *data = btd_service_get_user_data(service); struct btd_device *device = btd_service_get_device(service); char addr[18]; ba2str(device_get_address(device), addr); DBG("%s", addr); bt_bass_detach(data->bass); btd_service_disconnecting_complete(service, 0); return 0; } static int bass_server_probe(struct btd_profile *p, struct btd_adapter *adapter) { struct btd_gatt_database *database = btd_adapter_get_database(adapter); DBG("BASS path %s", adapter_get_path(adapter)); bt_bass_add_db(btd_gatt_database_get_db(database), btd_adapter_get_address(adapter)); return 0; } static void bass_server_remove(struct btd_profile *p, struct btd_adapter *adapter) { DBG("BASS remove Adapter"); } static struct btd_profile bass_service = { .name = "bass", .priority = BTD_PROFILE_PRIORITY_MEDIUM, .remote_uuid = BASS_UUID_STR, .device_probe = bass_probe, .device_remove = bass_remove, .accept = bass_accept, .disconnect = bass_disconnect, .adapter_probe = bass_server_probe, .adapter_remove = bass_server_remove, .experimental = true, }; static unsigned int bass_id; static unsigned int bap_id; static int bass_init(void) { int err; err = btd_profile_register(&bass_service); if (err) return err; bass_id = bt_bass_register(bass_attached, bass_detached, NULL); bap_id = bt_bap_register(bap_attached, bap_detached, NULL); return 0; } static void bass_exit(void) { btd_profile_unregister(&bass_service); bt_bass_unregister(bass_id); bt_bap_unregister(bap_id); } BLUETOOTH_PLUGIN_DEFINE(bass, VERSION, BLUETOOTH_PLUGIN_PRIORITY_DEFAULT, bass_init, bass_exit) bluez-5.82/profiles/audio/PaxHeaders/asha.c0000644000000000000000000000005014643061455015662 xustar0020 atime=1743516590 20 ctime=1743591284 bluez-5.82/profiles/audio/asha.c0000644000000000000000000002773514643061455015361 0ustar00rootroot// SPDX-License-Identifier: GPL-2.0-or-later /* * * BlueZ - Bluetooth protocol stack for Linux * * Copyright (C) 2024 Asymptotic Inc. * * Author: Arun Raghavan * * */ #ifdef HAVE_CONFIG_H #include #endif #define _GNU_SOURCE #include #include #include #include #include #include #include #include "btio/btio.h" #include "gdbus/gdbus.h" #include "lib/bluetooth.h" #include "lib/l2cap.h" #include "lib/uuid.h" #include "src/dbus-common.h" #include "src/adapter.h" #include "src/device.h" #include "src/log.h" #include "src/plugin.h" #include "src/profile.h" #include "src/service.h" #include "src/shared/util.h" #include "profiles/audio/asha.h" #include "profiles/audio/media.h" #include "profiles/audio/transport.h" #define MEDIA_ENDPOINT_INTERFACE "org.bluez.MediaEndpoint1" /* 2 byte SDU length, 1 byte sequence number, and then 20ms of G.722 */ #define ASHA_MIN_MTU 163 /* The default of 672 does not work */ #define ASHA_CONNECTION_MTU 512 struct bt_asha_device { struct bt_asha *asha; struct btd_device *device; struct media_transport *transport; GIOChannel *io; uint16_t imtu, omtu; unsigned int resume_id; }; static char *make_endpoint_path(struct bt_asha_device *asha_dev) { char *path; int err; err = asprintf(&path, "%s/asha", device_get_path(asha_dev->device)); if (err < 0) { error("Could not allocate path for remote %s", device_get_path(asha_dev->device)); return NULL; } return path; } struct connect_data { struct bt_asha_device *asha_dev; bt_asha_cb_t cb; void *cb_user_data; }; static void connect_cb(GIOChannel *io, GError *err, gpointer user_data) { struct connect_data *conn_data = user_data; struct bt_asha_device *asha_dev = conn_data->asha_dev; GError *gerr = NULL; if (!bt_io_get(io, &gerr, BT_IO_OPT_IMTU, &asha_dev->imtu, BT_IO_OPT_OMTU, &asha_dev->omtu, BT_IO_OPT_INVALID)) { /* Let this be non-fatal? */ asha_dev->omtu = ASHA_MIN_MTU; asha_dev->imtu = ASHA_CONNECTION_MTU; error("Could not get L2CAP CoC socket MTU: %s", err->message); g_error_free(gerr); } asha_dev->io = io; bt_asha_start(asha_dev->asha, conn_data->cb, conn_data->cb_user_data); } static int asha_connect_socket(struct bt_asha_device *asha_dev, bt_asha_cb_t cb, void *user_data) { GError *gerr = NULL; const bdaddr_t *src_addr; struct connect_data *conn_data; if (asha_dev->asha->state != ASHA_STOPPED) { error("ASHA device connect failed. Bad state %d", asha_dev->asha->state); return 0; } conn_data = g_new0(struct connect_data, 1); conn_data->asha_dev = asha_dev; conn_data->cb = cb; conn_data->cb_user_data = user_data; src_addr = btd_adapter_get_address( device_get_adapter(asha_dev->device)); if (!bt_io_connect(connect_cb, conn_data, g_free, &gerr, BT_IO_OPT_MODE, BT_IO_MODE_LE_FLOWCTL, BT_IO_OPT_SOURCE_TYPE, BDADDR_LE_PUBLIC, BT_IO_OPT_SOURCE_BDADDR, src_addr, BT_IO_OPT_DEST_TYPE, BDADDR_LE_PUBLIC, BT_IO_OPT_DEST_BDADDR, device_get_address(asha_dev->device), BT_IO_OPT_PSM, asha_dev->asha->psm, BT_IO_OPT_OMTU, ASHA_MIN_MTU, BT_IO_OPT_IMTU, ASHA_CONNECTION_MTU, BT_IO_OPT_INVALID)) { error("Could not open L2CAP CoC socket: %s", gerr->message); g_error_free(gerr); goto error; } DBG("L2CAP CoC socket is open"); return 0; error: return -1; } unsigned int bt_asha_device_start(struct bt_asha_device *asha_dev, bt_asha_cb_t cb, void *user_data) { int ret; btd_device_set_conn_param(asha_dev->device, 0x0010 /* min interval = 1.25ms intervals => 20ms */, 0x0010 /* max interval = 1.25ms intervals => 20ms */, 0x000A /* 10 events' latency */, 0x0064 /* 1s timeout */); ret = asha_connect_socket(asha_dev, cb, user_data); if (ret < 0) return 0; else return (++asha_dev->resume_id); } unsigned int bt_asha_device_stop(struct bt_asha_device *asha_dev, bt_asha_cb_t cb, void *user_data) { bt_asha_stop(asha_dev->asha, cb, user_data); if (asha_dev->io) { g_io_channel_shutdown(asha_dev->io, TRUE, NULL); g_io_channel_unref(asha_dev->io); asha_dev->io = NULL; }; return asha_dev->resume_id; } void bt_asha_device_state_reset(struct bt_asha_device *asha_dev) { if (asha_dev->io) { g_io_channel_shutdown(asha_dev->io, TRUE, NULL); g_io_channel_unref(asha_dev->io); asha_dev->io = NULL; }; bt_asha_state_reset(asha_dev->asha); asha_dev->resume_id = 0; } unsigned int bt_asha_device_device_get_resume_id( struct bt_asha_device *asha_dev) { return asha_dev->resume_id; } enum bt_asha_state_t bt_asha_device_get_state( struct bt_asha_device *asha_dev) { return asha_dev->asha->state; } uint16_t bt_asha_device_get_render_delay(struct bt_asha_device *asha_dev) { return asha_dev->asha->render_delay; } int8_t bt_asha_device_get_volume(struct bt_asha_device *asha_dev) { return asha_dev->asha->volume; } bool bt_asha_device_set_volume(struct bt_asha_device *asha_dev, int8_t volume) { return bt_asha_set_volume(asha_dev->asha, volume); } int bt_asha_device_get_fd(struct bt_asha_device *asha_dev) { return g_io_channel_unix_get_fd(asha_dev->io); } uint16_t bt_asha_device_get_imtu(struct bt_asha_device *asha_dev) { return asha_dev->imtu; } uint16_t bt_asha_device_get_omtu(struct bt_asha_device *asha_dev) { return asha_dev->omtu; } static gboolean get_uuid(const GDBusPropertyTable *property, DBusMessageIter *iter, void *data) { const char *uuid; uuid = ASHA_PROFILE_UUID; dbus_message_iter_append_basic(iter, DBUS_TYPE_STRING, &uuid); return TRUE; } static gboolean get_side(const GDBusPropertyTable *property, DBusMessageIter *iter, void *data) { struct bt_asha_device *asha_dev = data; const char *side = asha_dev->asha->right_side ? "right" : "left"; /* Use a string in case we want to support more types in the future */ dbus_message_iter_append_basic(iter, DBUS_TYPE_STRING, &side); return TRUE; } static gboolean get_binaural(const GDBusPropertyTable *property, DBusMessageIter *iter, void *data) { struct bt_asha_device *asha_dev = data; dbus_bool_t binaural = asha_dev->asha->binaural; dbus_message_iter_append_basic(iter, DBUS_TYPE_BOOLEAN, &binaural); return TRUE; } static gboolean get_hisyncid(const GDBusPropertyTable *property, DBusMessageIter *iter, void *data) { struct bt_asha_device *asha_dev = data; DBusMessageIter array; uint8_t *hisyncid = asha_dev->asha->hisyncid; dbus_message_iter_open_container(iter, DBUS_TYPE_ARRAY, DBUS_TYPE_BYTE_AS_STRING, &array); dbus_message_iter_append_fixed_array(&array, DBUS_TYPE_BYTE, &hisyncid, sizeof(asha_dev->asha->hisyncid)); dbus_message_iter_close_container(iter, &array); return TRUE; } static gboolean get_codecs(const GDBusPropertyTable *property, DBusMessageIter *iter, void *data) { struct bt_asha_device *asha_dev = data; dbus_uint16_t codecs = asha_dev->asha->codec_ids; dbus_message_iter_append_basic(iter, DBUS_TYPE_UINT16, &codecs); return TRUE; } static gboolean get_device(const GDBusPropertyTable *property, DBusMessageIter *iter, void *data) { struct bt_asha_device *asha_dev = data; const char *path; path = device_get_path(asha_dev->device); dbus_message_iter_append_basic(iter, DBUS_TYPE_OBJECT_PATH, &path); return TRUE; } static gboolean get_transport(const GDBusPropertyTable *property, DBusMessageIter *iter, void *data) { struct bt_asha_device *asha_dev = data; const char *path; path = media_transport_get_path(asha_dev->transport); dbus_message_iter_append_basic(iter, DBUS_TYPE_OBJECT_PATH, &path); return TRUE; } static int asha_source_device_probe(struct btd_service *service) { struct bt_asha_device *asha_dev; struct btd_device *device = btd_service_get_device(service); char addr[18]; ba2str(device_get_address(device), addr); DBG("Probing ASHA device %s", addr); asha_dev = g_new0(struct bt_asha_device, 1); asha_dev->device = device; asha_dev->asha = bt_asha_new(); asha_dev->io = NULL; btd_service_set_user_data(service, asha_dev); return 0; } static void asha_source_device_remove(struct btd_service *service) { struct bt_asha_device *asha_dev; struct btd_device *device = btd_service_get_device(service); char addr[18]; ba2str(device_get_address(device), addr); DBG("Removing ASHA device %s", addr); asha_dev = btd_service_get_user_data(service); if (!asha_dev) { /* Can this actually happen? */ DBG("Not handlihng ASHA profile"); return; } bt_asha_free(asha_dev->asha); g_free(asha_dev); } static const GDBusMethodTable asha_ep_methods[] = { { }, }; static const GDBusPropertyTable asha_ep_properties[] = { { "UUID", "s", get_uuid, NULL, NULL, G_DBUS_PROPERTY_FLAG_EXPERIMENTAL }, { "Side", "s", get_side, NULL, NULL, G_DBUS_PROPERTY_FLAG_EXPERIMENTAL }, { "Binaural", "b", get_binaural, NULL, NULL, G_DBUS_PROPERTY_FLAG_EXPERIMENTAL }, { "HiSyncId", "ay", get_hisyncid, NULL, NULL, G_DBUS_PROPERTY_FLAG_EXPERIMENTAL }, { "Codecs", "q", get_codecs, NULL, NULL, G_DBUS_PROPERTY_FLAG_EXPERIMENTAL }, { "Device", "o", get_device, NULL, NULL, G_DBUS_PROPERTY_FLAG_EXPERIMENTAL }, { "Transport", "o", get_transport, NULL, NULL, G_DBUS_PROPERTY_FLAG_EXPERIMENTAL }, { } }; static void asha_source_endpoint_register(struct bt_asha_device *asha_dev) { char *path; const struct media_endpoint *asha_ep; path = make_endpoint_path(asha_dev); if (!path) goto error; if (g_dbus_register_interface(btd_get_dbus_connection(), path, MEDIA_ENDPOINT_INTERFACE, asha_ep_methods, NULL, asha_ep_properties, asha_dev, NULL) == FALSE) { error("Could not register remote ep %s", path); goto error; } asha_ep = media_endpoint_get_asha(); asha_dev->transport = media_transport_create(asha_dev->device, path, NULL, 0, (void *) asha_ep, asha_dev); error: if (path) free(path); } static void asha_source_endpoint_unregister(struct bt_asha_device *asha) { char *path; path = make_endpoint_path(asha); if (!path) goto error; g_dbus_unregister_interface(btd_get_dbus_connection(), path, MEDIA_ENDPOINT_INTERFACE); if (asha->transport) { media_transport_destroy(asha->transport); asha->transport = NULL; } error: if (path) free(path); } static int asha_source_accept(struct btd_service *service) { struct btd_device *device = btd_service_get_device(service); struct gatt_db *db = btd_device_get_gatt_db(device); struct bt_gatt_client *client = btd_device_get_gatt_client(device); struct bt_asha_device *asha_dev = btd_service_get_user_data(service); char addr[18]; ba2str(device_get_address(device), addr); DBG("Accepting ASHA connection on %s", addr); if (!asha_dev) { /* Can this actually happen? */ DBG("Not handling ASHA profile"); return -1; } if (!bt_asha_probe(asha_dev->asha, db, client)) return -1; asha_source_endpoint_register(asha_dev); btd_service_connecting_complete(service, 0); return 0; } static int asha_source_disconnect(struct btd_service *service) { struct btd_device *device = btd_service_get_device(service); struct bt_asha_device *asha_dev = btd_service_get_user_data(service); char addr[18]; ba2str(device_get_address(device), addr); DBG("Disconnecting ASHA on %s", addr); if (!asha_dev) { /* Can this actually happen? */ DBG("Not handlihng ASHA profile"); return -1; } asha_source_endpoint_unregister(asha_dev); bt_asha_reset(asha_dev->asha); btd_service_disconnecting_complete(service, 0); return 0; } static struct btd_profile asha_source_profile = { .name = "asha-source", .priority = BTD_PROFILE_PRIORITY_MEDIUM, .remote_uuid = ASHA_PROFILE_UUID, .experimental = true, .device_probe = asha_source_device_probe, .device_remove = asha_source_device_remove, .auto_connect = true, .accept = asha_source_accept, .disconnect = asha_source_disconnect, }; static int asha_init(void) { int err; err = btd_profile_register(&asha_source_profile); if (err) return err; return 0; } static void asha_exit(void) { btd_profile_unregister(&asha_source_profile); } BLUETOOTH_PLUGIN_DEFINE(asha, VERSION, BLUETOOTH_PLUGIN_PRIORITY_DEFAULT, asha_init, asha_exit) bluez-5.82/profiles/audio/PaxHeaders/bap.c0000644000000000000000000000005014766002272015506 xustar0020 atime=1743515578 20 ctime=1743591284 bluez-5.82/profiles/audio/bap.c0000644000000000000000000023122114766002272015170 0ustar00rootroot// SPDX-License-Identifier: GPL-2.0-or-later /* * * BlueZ - Bluetooth protocol stack for Linux * * Copyright (C) 2022 Intel Corporation. All rights reserved. * Copyright 2023-2025 NXP * * */ #ifdef HAVE_CONFIG_H #include #endif #define _GNU_SOURCE #include #include #include #include #include #include #include #include #include #include #include #include "gdbus/gdbus.h" #include "lib/bluetooth.h" #include "lib/hci.h" #include "lib/sdp.h" #include "lib/uuid.h" #include "lib/iso.h" #include "src/btd.h" #include "src/dbus-common.h" #include "src/shared/util.h" #include "src/shared/att.h" #include "src/shared/queue.h" #include "src/shared/gatt-db.h" #include "src/shared/gatt-client.h" #include "src/shared/gatt-server.h" #include "src/shared/bap.h" #include "btio/btio.h" #include "src/plugin.h" #include "src/adapter.h" #include "src/gatt-database.h" #include "src/device.h" #include "src/profile.h" #include "src/service.h" #include "src/log.h" #include "src/error.h" #define ISO_SOCKET_UUID "6fbaf188-05e0-496a-9885-d6ddfdb4e03e" #define PACS_UUID_STR "00001850-0000-1000-8000-00805f9b34fb" #define BCAAS_UUID_STR "00001852-0000-1000-8000-00805f9b34fb" #define MEDIA_ENDPOINT_INTERFACE "org.bluez.MediaEndpoint1" #define MEDIA_INTERFACE "org.bluez.Media1" struct bap_setup { struct bap_ep *ep; struct bap_data *data; struct bt_bap_stream *stream; struct bt_bap_qos qos; int (*qos_parser)(struct bap_setup *setup, const char *key, int var, DBusMessageIter *iter); GIOChannel *io; unsigned int io_id; bool recreate; bool cig_active; struct iovec *caps; struct iovec *metadata; unsigned int id; struct iovec *base; DBusMessage *msg; void (*destroy)(struct bap_setup *setup); }; struct bap_ep { char *path; struct bap_data *data; struct bt_bap_pac *lpac; struct bt_bap_pac *rpac; uint32_t locations; uint16_t supported_context; uint16_t context; struct queue *setups; }; struct bap_data { struct btd_device *device; struct btd_adapter *adapter; struct btd_service *service; struct bt_bap *bap; unsigned int ready_id; unsigned int state_id; unsigned int pac_id; struct queue *srcs; struct queue *snks; struct queue *bcast; struct queue *bcast_snks; struct queue *streams; GIOChannel *listen_io; unsigned int io_id; int selecting; void *user_data; }; static struct queue *sessions; static bool bap_data_set_user_data(struct bap_data *data, void *user_data) { if (!data) return false; data->user_data = user_data; return true; } static void bap_debug(const char *str, void *user_data) { DBG_IDX(0xffff, "%s", str); } static void ep_unregister(void *data) { struct bap_ep *ep = data; DBG("ep %p path %s", ep, ep->path); g_dbus_unregister_interface(btd_get_dbus_connection(), ep->path, MEDIA_ENDPOINT_INTERFACE); } static void setup_free(void *data); static void bap_data_free(struct bap_data *data) { if (data->listen_io) { g_io_channel_shutdown(data->listen_io, TRUE, NULL); g_io_channel_unref(data->listen_io); } if (data->io_id) g_source_remove(data->io_id); if (data->service && btd_service_get_user_data(data->service) == data) btd_service_set_user_data(data->service, NULL); queue_destroy(data->snks, ep_unregister); queue_destroy(data->srcs, ep_unregister); queue_destroy(data->bcast, ep_unregister); queue_destroy(data->streams, NULL); queue_destroy(data->bcast_snks, setup_free); bt_bap_ready_unregister(data->bap, data->ready_id); bt_bap_state_unregister(data->bap, data->state_id); bt_bap_pac_unregister(data->bap, data->pac_id); bt_bap_unref(data->bap); free(data); } static void bap_data_remove(struct bap_data *data) { DBG("data %p", data); if (!queue_remove(sessions, data)) return; bap_data_free(data); if (queue_isempty(sessions)) { queue_destroy(sessions, NULL); sessions = NULL; } } static void bap_remove(struct btd_service *service) { struct btd_device *device = btd_service_get_device(service); struct bap_data *data; char addr[18]; ba2str(device_get_address(device), addr); DBG("%s", addr); data = btd_service_get_user_data(service); if (!data) { error("BAP service not handled by profile"); return; } bap_data_remove(data); } static gboolean get_uuid(const GDBusPropertyTable *property, DBusMessageIter *iter, void *data) { struct bap_ep *ep = data; const char *uuid; if (queue_find(ep->data->snks, NULL, ep)) uuid = PAC_SINK_UUID; else if (queue_find(ep->data->srcs, NULL, ep)) uuid = PAC_SOURCE_UUID; else if ((queue_find(ep->data->bcast, NULL, ep) && (bt_bap_pac_get_type(ep->lpac) == BT_BAP_BCAST_SINK))) uuid = BCAA_SERVICE_UUID; else uuid = BAA_SERVICE_UUID; dbus_message_iter_append_basic(iter, DBUS_TYPE_STRING, &uuid); return TRUE; } static gboolean get_codec(const GDBusPropertyTable *property, DBusMessageIter *iter, void *data) { struct bap_ep *ep = data; uint8_t codec; /* For broadcast source, rpac is null so the codec * is retrieved from the lpac */ if (ep->rpac == NULL) bt_bap_pac_get_codec(ep->lpac, &codec, NULL, NULL); else bt_bap_pac_get_codec(ep->rpac, &codec, NULL, NULL); dbus_message_iter_append_basic(iter, DBUS_TYPE_BYTE, &codec); return TRUE; } static gboolean has_capabilities(const GDBusPropertyTable *property, void *data) { struct bap_ep *ep = data; struct iovec *d = NULL; bt_bap_pac_get_codec(ep->rpac, NULL, &d, NULL); if (d) return TRUE; return FALSE; } static gboolean get_capabilities(const GDBusPropertyTable *property, DBusMessageIter *iter, void *data) { struct bap_ep *ep = data; DBusMessageIter array; struct iovec *d; bt_bap_pac_get_codec(ep->rpac, NULL, &d, NULL); dbus_message_iter_open_container(iter, DBUS_TYPE_ARRAY, DBUS_TYPE_BYTE_AS_STRING, &array); dbus_message_iter_append_fixed_array(&array, DBUS_TYPE_BYTE, &d->iov_base, d->iov_len); dbus_message_iter_close_container(iter, &array); return TRUE; } static gboolean has_metadata(const GDBusPropertyTable *property, void *data) { struct bap_ep *ep = data; struct iovec *d = NULL; bt_bap_pac_get_codec(ep->rpac, NULL, NULL, &d); if (d) return TRUE; return FALSE; } static gboolean get_metadata(const GDBusPropertyTable *property, DBusMessageIter *iter, void *data) { struct bap_ep *ep = data; DBusMessageIter array; struct iovec *d; bt_bap_pac_get_codec(ep->rpac, NULL, NULL, &d); dbus_message_iter_open_container(iter, DBUS_TYPE_ARRAY, DBUS_TYPE_BYTE_AS_STRING, &array); dbus_message_iter_append_fixed_array(&array, DBUS_TYPE_BYTE, &d->iov_base, d->iov_len); dbus_message_iter_close_container(iter, &array); return TRUE; } static gboolean get_device(const GDBusPropertyTable *property, DBusMessageIter *iter, void *data) { struct bap_ep *ep = data; const char *path; if (bt_bap_pac_get_type(ep->lpac) == BT_BAP_BCAST_SOURCE) path = adapter_get_path(ep->data->adapter); else path = device_get_path(ep->data->device); dbus_message_iter_append_basic(iter, DBUS_TYPE_OBJECT_PATH, &path); return TRUE; } static gboolean get_locations(const GDBusPropertyTable *property, DBusMessageIter *iter, void *data) { struct bap_ep *ep = data; ep->locations = bt_bap_pac_get_locations(ep->rpac); dbus_message_iter_append_basic(iter, DBUS_TYPE_UINT32, &ep->locations); return TRUE; } static gboolean get_supported_context(const GDBusPropertyTable *property, DBusMessageIter *iter, void *data) { struct bap_ep *ep = data; ep->supported_context = bt_bap_pac_get_supported_context(ep->rpac); dbus_message_iter_append_basic(iter, DBUS_TYPE_UINT16, &ep->supported_context); return TRUE; } static gboolean get_context(const GDBusPropertyTable *property, DBusMessageIter *iter, void *data) { struct bap_ep *ep = data; ep->context = bt_bap_pac_get_context(ep->rpac); dbus_message_iter_append_basic(iter, DBUS_TYPE_UINT16, &ep->context); return TRUE; } static gboolean qos_exists(const GDBusPropertyTable *property, void *data) { struct bap_ep *ep = data; struct bt_bap_pac_qos *qos; qos = bt_bap_pac_get_qos(ep->rpac); if (!qos) return FALSE; return TRUE; } static gboolean get_qos(const GDBusPropertyTable *property, DBusMessageIter *iter, void *data) { struct bap_ep *ep = data; struct bt_bap_pac_qos *qos; DBusMessageIter dict; dbus_message_iter_open_container(iter, DBUS_TYPE_ARRAY, DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING DBUS_TYPE_STRING_AS_STRING DBUS_TYPE_VARIANT_AS_STRING DBUS_DICT_ENTRY_END_CHAR_AS_STRING, &dict); qos = bt_bap_pac_get_qos(ep->rpac); if (!qos) return FALSE; dict_append_entry(&dict, "Framing", DBUS_TYPE_BYTE, &qos->framing); dict_append_entry(&dict, "PHY", DBUS_TYPE_BYTE, &qos->phy); dict_append_entry(&dict, "Retransmissions", DBUS_TYPE_BYTE, &qos->rtn); dict_append_entry(&dict, "MaximumLatency", DBUS_TYPE_UINT16, &qos->latency); dict_append_entry(&dict, "MimimumDelay", DBUS_TYPE_UINT32, &qos->pd_min); dict_append_entry(&dict, "MaximumDelay", DBUS_TYPE_UINT32, &qos->pd_max); dict_append_entry(&dict, "PreferredMimimumDelay", DBUS_TYPE_UINT32, &qos->ppd_min); dict_append_entry(&dict, "PreferredMaximumDelay", DBUS_TYPE_UINT32, &qos->ppd_max); dbus_message_iter_close_container(iter, &dict); return TRUE; } static const GDBusPropertyTable ep_properties[] = { { "UUID", "s", get_uuid, NULL, NULL, G_DBUS_PROPERTY_FLAG_EXPERIMENTAL }, { "Codec", "y", get_codec, NULL, NULL, G_DBUS_PROPERTY_FLAG_EXPERIMENTAL }, { "Capabilities", "ay", get_capabilities, NULL, has_capabilities, G_DBUS_PROPERTY_FLAG_EXPERIMENTAL }, { "Metadata", "ay", get_metadata, NULL, has_metadata, G_DBUS_PROPERTY_FLAG_EXPERIMENTAL }, { "Device", "o", get_device, NULL, NULL, G_DBUS_PROPERTY_FLAG_EXPERIMENTAL }, { "Locations", "u", get_locations, NULL, NULL, G_DBUS_PROPERTY_FLAG_EXPERIMENTAL }, { "SupportedContext", "q", get_supported_context, NULL, NULL, G_DBUS_PROPERTY_FLAG_EXPERIMENTAL }, { "Context", "q", get_context, NULL, NULL, G_DBUS_PROPERTY_FLAG_EXPERIMENTAL }, { "QoS", "a{sv}", get_qos, NULL, qos_exists, G_DBUS_PROPERTY_FLAG_EXPERIMENTAL }, { } }; static int parse_array(DBusMessageIter *iter, struct iovec *iov) { DBusMessageIter array; if (!iov) return 0; dbus_message_iter_recurse(iter, &array); dbus_message_iter_get_fixed_array(&array, &iov->iov_base, (int *)&iov->iov_len); return 0; } static int parse_io_qos(const char *key, int var, DBusMessageIter *iter, struct bt_bap_io_qos *qos) { if (!strcasecmp(key, "Interval")) { if (var != DBUS_TYPE_UINT32) return -EINVAL; dbus_message_iter_get_basic(iter, &qos->interval); } else if (!strcasecmp(key, "PHY")) { if (var != DBUS_TYPE_BYTE) return -EINVAL; dbus_message_iter_get_basic(iter, &qos->phy); } else if (!strcasecmp(key, "SDU")) { if (var != DBUS_TYPE_UINT16) return -EINVAL; dbus_message_iter_get_basic(iter, &qos->sdu); } else if (!strcasecmp(key, "Retransmissions")) { if (var != DBUS_TYPE_BYTE) return -EINVAL; dbus_message_iter_get_basic(iter, &qos->rtn); } else if (!strcasecmp(key, "Latency")) { if (var != DBUS_TYPE_UINT16) return -EINVAL; dbus_message_iter_get_basic(iter, &qos->latency); } return 0; } static int setup_parse_ucast_qos(struct bap_setup *setup, const char *key, int var, DBusMessageIter *iter) { struct bt_bap_qos *qos = &setup->qos; if (!strcasecmp(key, "CIG")) { if (var != DBUS_TYPE_BYTE) return -EINVAL; dbus_message_iter_get_basic(iter, &qos->ucast.cig_id); } else if (!strcasecmp(key, "CIS")) { if (var != DBUS_TYPE_BYTE) return -EINVAL; dbus_message_iter_get_basic(iter, &qos->ucast.cis_id); } else if (!strcasecmp(key, "Framing")) { if (var != DBUS_TYPE_BYTE) return -EINVAL; dbus_message_iter_get_basic(iter, &qos->ucast.framing); } else if (!strcasecmp(key, "PresentationDelay")) { if (var != DBUS_TYPE_UINT32) return -EINVAL; dbus_message_iter_get_basic(iter, &qos->ucast.delay); } else if (!strcasecmp(key, "TargetLatency")) { if (var != DBUS_TYPE_BYTE) return -EINVAL; dbus_message_iter_get_basic(iter, &qos->ucast.target_latency); } else { int err; err = parse_io_qos(key, var, iter, &qos->ucast.io_qos); if (err) return err; } return 0; } static void setup_bcast_destroy(struct bap_setup *setup) { struct bt_bap_qos *qos = &setup->qos; util_iov_free(qos->bcast.bcode, 1); } static int setup_parse_bcast_qos(struct bap_setup *setup, const char *key, int var, DBusMessageIter *iter) { struct bt_bap_qos *qos = &setup->qos; if (!strcasecmp(key, "Encryption")) { if (var != DBUS_TYPE_BYTE) return -EINVAL; dbus_message_iter_get_basic(iter, &qos->bcast.encryption); } else if (!strcasecmp(key, "BIG")) { if (var != DBUS_TYPE_BYTE) return -EINVAL; dbus_message_iter_get_basic(iter, &qos->bcast.big); } else if (!strcasecmp(key, "Options")) { if (var != DBUS_TYPE_BYTE) return -EINVAL; dbus_message_iter_get_basic(iter, &qos->bcast.options); } else if (!strcasecmp(key, "Skip")) { if (var != DBUS_TYPE_UINT16) return -EINVAL; dbus_message_iter_get_basic(iter, &qos->bcast.skip); } else if (!strcasecmp(key, "SyncTimeout")) { if (var != DBUS_TYPE_UINT16) return -EINVAL; dbus_message_iter_get_basic(iter, &qos->bcast.sync_timeout); } else if (!strcasecmp(key, "SyncType")) { if (var != DBUS_TYPE_BYTE) return -EINVAL; dbus_message_iter_get_basic(iter, &qos->bcast.sync_cte_type); } else if (!strcasecmp(key, "SyncFactor")) { if (var != DBUS_TYPE_BYTE) return -EINVAL; dbus_message_iter_get_basic(iter, &qos->bcast.sync_factor); } else if (!strcasecmp(key, "MSE")) { if (var != DBUS_TYPE_BYTE) return -EINVAL; dbus_message_iter_get_basic(iter, &qos->bcast.mse); } else if (!strcasecmp(key, "Timeout")) { if (var != DBUS_TYPE_UINT16) return -EINVAL; dbus_message_iter_get_basic(iter, &qos->bcast.timeout); } else if (!strcasecmp(key, "PresentationDelay")) { if (var != DBUS_TYPE_UINT32) return -EINVAL; dbus_message_iter_get_basic(iter, &qos->bcast.delay); } else if (!strcasecmp(key, "BCode")) { struct iovec iov; if (var != DBUS_TYPE_ARRAY) return -EINVAL; memset(&iov, 0, sizeof(iov)); parse_array(iter, &iov); if (iov.iov_len != 16) { error("Invalid size for BCode: %zu != 16", iov.iov_len); return -EINVAL; } util_iov_free(qos->bcast.bcode, 1); qos->bcast.bcode = util_iov_dup(&iov, 1); } else { int err; err = parse_io_qos(key, var, iter, &qos->bcast.io_qos); if (err) return err; } return 0; } static int setup_parse_qos(struct bap_setup *setup, DBusMessageIter *iter) { DBusMessageIter array; const char *key; dbus_message_iter_recurse(iter, &array); while (dbus_message_iter_get_arg_type(&array) == DBUS_TYPE_DICT_ENTRY) { DBusMessageIter value, entry; int var, err; dbus_message_iter_recurse(&array, &entry); dbus_message_iter_get_basic(&entry, &key); dbus_message_iter_next(&entry); dbus_message_iter_recurse(&entry, &value); var = dbus_message_iter_get_arg_type(&value); err = setup->qos_parser(setup, key, var, &value); if (err) { DBG("Failed parsing %s", key); return err; } dbus_message_iter_next(&array); } return 0; } static int setup_parse_configuration(struct bap_setup *setup, DBusMessageIter *props) { const char *key; struct iovec iov; memset(&iov, 0, sizeof(iov)); while (dbus_message_iter_get_arg_type(props) == DBUS_TYPE_DICT_ENTRY) { DBusMessageIter value, entry; int var; dbus_message_iter_recurse(props, &entry); dbus_message_iter_get_basic(&entry, &key); dbus_message_iter_next(&entry); dbus_message_iter_recurse(&entry, &value); var = dbus_message_iter_get_arg_type(&value); if (!strcasecmp(key, "Capabilities")) { if (var != DBUS_TYPE_ARRAY) goto fail; if (parse_array(&value, &iov)) goto fail; util_iov_free(setup->caps, 1); setup->caps = util_iov_dup(&iov, 1); } else if (!strcasecmp(key, "Metadata")) { if (var != DBUS_TYPE_ARRAY) goto fail; if (parse_array(&value, &iov)) goto fail; util_iov_free(setup->metadata, 1); setup->metadata = util_iov_dup(&iov, 1); } else if (!strcasecmp(key, "QoS")) { if (var != DBUS_TYPE_ARRAY) goto fail; if (setup_parse_qos(setup, &value)) goto fail; } dbus_message_iter_next(props); } return 0; fail: DBG("Failed parsing %s", key); return -EINVAL; } static void qos_cb(struct bt_bap_stream *stream, uint8_t code, uint8_t reason, void *user_data) { struct bap_setup *setup = user_data; DBusMessage *reply; DBG("stream %p code 0x%02x reason 0x%02x", stream, code, reason); setup->id = 0; if (!setup->msg) return; if (!code) reply = dbus_message_new_method_return(setup->msg); else reply = btd_error_failed(setup->msg, "Unable to configure"); g_dbus_send_message(btd_get_dbus_connection(), reply); dbus_message_unref(setup->msg); setup->msg = NULL; } static void config_cb(struct bt_bap_stream *stream, uint8_t code, uint8_t reason, void *user_data) { struct bap_setup *setup = user_data; DBusMessage *reply; DBG("stream %p code 0x%02x reason 0x%02x", stream, code, reason); setup->id = 0; if (!code) { /* Check state is already set to config then proceed to qos */ if (bt_bap_stream_get_state(stream) == BT_BAP_STREAM_STATE_CONFIG) { setup->id = bt_bap_stream_qos(stream, &setup->qos, qos_cb, setup); if (!setup->id) { error("Failed to Configure QoS"); bt_bap_stream_release(stream, NULL, NULL); } } return; } if (!setup->msg) return; reply = btd_error_failed(setup->msg, "Unable to configure"); g_dbus_send_message(btd_get_dbus_connection(), reply); dbus_message_unref(setup->msg); setup->msg = NULL; } static void setup_io_close(void *data, void *user_data) { struct bap_setup *setup = data; int fd; if (setup->io_id) { g_source_remove(setup->io_id); setup->io_id = 0; } if (!setup->io) return; DBG("setup %p", setup); fd = g_io_channel_unix_get_fd(setup->io); close(fd); g_io_channel_unref(setup->io); setup->io = NULL; setup->cig_active = false; bt_bap_stream_io_connecting(setup->stream, -1); } static void ep_close(struct bap_ep *ep) { if (!ep) return; queue_foreach(ep->setups, setup_io_close, NULL); } static struct bap_setup *setup_new(struct bap_ep *ep) { struct bap_setup *setup; setup = new0(struct bap_setup, 1); setup->ep = ep; /* Broadcast Source has endpoints in bcast list, Broadcast Sink * does not have endpoints */ if (((ep != NULL) && queue_find(ep->data->bcast, NULL, ep)) || (ep == NULL)) { /* Mark BIG and BIS to be auto assigned */ setup->qos.bcast.big = BT_ISO_QOS_BIG_UNSET; setup->qos.bcast.bis = BT_ISO_QOS_BIS_UNSET; setup->qos.bcast.sync_factor = BT_ISO_SYNC_FACTOR; setup->qos.bcast.sync_timeout = BT_ISO_SYNC_TIMEOUT; setup->qos.bcast.timeout = BT_ISO_SYNC_TIMEOUT; setup->qos_parser = setup_parse_bcast_qos; setup->destroy = setup_bcast_destroy; } else { /* Mark CIG and CIS to be auto assigned */ setup->qos.ucast.cig_id = BT_ISO_QOS_CIG_UNSET; setup->qos.ucast.cis_id = BT_ISO_QOS_CIS_UNSET; setup->qos_parser = setup_parse_ucast_qos; } if (ep) { if (!ep->setups) ep->setups = queue_new(); queue_push_tail(ep->setups, setup); DBG("ep %p setup %p", ep, setup); } return setup; } static void setup_free(void *data) { struct bap_setup *setup = data; DBusMessage *reply; DBG("%p", setup); if (setup->stream && setup->id) { bt_bap_stream_cancel(setup->stream, setup->id); setup->id = 0; } if (setup->msg) { reply = btd_error_failed(setup->msg, "Canceled"); g_dbus_send_message(btd_get_dbus_connection(), reply); dbus_message_unref(setup->msg); setup->msg = NULL; } if (setup->ep) queue_remove(setup->ep->setups, setup); setup_io_close(setup, NULL); util_iov_free(setup->caps, 1); util_iov_free(setup->metadata, 1); util_iov_free(setup->base, 1); if (setup->destroy) setup->destroy(setup); free(setup); } static bool match_io_qos(const struct bt_bap_io_qos *io_qos, const struct bt_bap_io_qos *match) { if (io_qos->interval != match->interval) return false; if (io_qos->latency != match->latency) return false; if (io_qos->sdu != match->sdu) return false; if (io_qos->phy != match->phy) return false; if (io_qos->rtn != match->rtn) return false; return true; } static bool match_bcast_qos(const struct bt_bap_bcast_qos *qos, const struct bt_bap_bcast_qos *match) { if (qos->sync_factor != match->sync_factor) return false; if (qos->packing != match->packing) return false; if (qos->framing != match->framing) return false; if (qos->encryption != match->encryption) return false; if (qos->encryption && util_iov_memcmp(qos->bcode, match->bcode)) return false; if (qos->options != match->options) return false; if (qos->skip != match->skip) return false; if (qos->sync_timeout != match->sync_timeout) return false; if (qos->sync_cte_type != match->sync_cte_type) return false; if (qos->mse != match->mse) return false; if (qos->timeout != match->timeout) return false; if (qos->pa_sync != match->pa_sync) return false; return match_io_qos(&qos->io_qos, &match->io_qos); } static bool setup_mismatch_qos(const void *data, const void *user_data) { const struct bap_setup *setup = data; const struct bap_setup *match = user_data; /* Match setups that are part of the same BIG */ if (setup == match || setup->qos.bcast.big == BT_ISO_QOS_BIG_UNSET || setup->qos.bcast.big != match->qos.bcast.big) return false; return !match_bcast_qos(&setup->qos.bcast, &match->qos.bcast); } static DBusMessage *set_configuration(DBusConnection *conn, DBusMessage *msg, void *data) { struct bap_ep *ep = data; struct bap_setup *setup; const char *path; DBusMessageIter args, props; dbus_message_iter_init(msg, &args); dbus_message_iter_get_basic(&args, &path); dbus_message_iter_next(&args); dbus_message_iter_recurse(&args, &props); if (dbus_message_iter_get_arg_type(&props) != DBUS_TYPE_DICT_ENTRY) return btd_error_invalid_args(msg); /* Broadcast source supports multiple setups, each setup will be BIS * and will be configured with the set_configuration command * TO DO reconfiguration of a BIS. */ if (bt_bap_pac_get_type(ep->lpac) != BT_BAP_BCAST_SOURCE) ep_close(ep); setup = setup_new(ep); if (setup_parse_configuration(setup, &props) < 0) { DBG("Unable to parse configuration"); setup_free(setup); return btd_error_invalid_args(msg); } if (bt_bap_pac_get_type(ep->lpac) == BT_BAP_BCAST_SOURCE) /* All streams in a BIG should have the same QoS. * Check that the new configuration matches previous ones. */ if (queue_find(setup->ep->setups, setup_mismatch_qos, setup)) { setup_free(setup); return btd_error_invalid_args(msg); } setup->stream = bt_bap_stream_new(ep->data->bap, ep->lpac, ep->rpac, &setup->qos, setup->caps); bt_bap_stream_set_user_data(setup->stream, ep->path); setup->id = bt_bap_stream_config(setup->stream, &setup->qos, setup->caps, config_cb, setup); if (!setup->id) { DBG("Unable to config stream"); setup_free(setup); return btd_error_invalid_args(msg); } if (setup->metadata && setup->metadata->iov_len) bt_bap_stream_metadata(setup->stream, setup->metadata, NULL, NULL); switch (bt_bap_stream_get_type(setup->stream)) { case BT_BAP_STREAM_TYPE_UCAST: setup->msg = dbus_message_ref(msg); break; case BT_BAP_STREAM_TYPE_BCAST: /* No message sent over the air for broadcast */ setup->id = 0; if (ep->data->service) service_set_connecting(ep->data->service); return g_dbus_create_reply(msg, DBUS_TYPE_INVALID); } return NULL; } static bool stream_io_unset(const void *data, const void *user_data) { struct bt_bap_stream *stream = (struct bt_bap_stream *)data; return !bt_bap_stream_get_io(stream); } static void iso_bcast_confirm_cb(GIOChannel *io, GError *err, void *user_data) { struct bap_setup *setup = user_data; struct bt_bap_stream *stream = setup->stream; int fd; struct bap_data *bap_data = setup->data; DBG("BIG Sync completed"); /* The order of the BIS fds notified from kernel corresponds * to the order of the BISes that were enqueued before * calling bt_io_bcast_accept. */ if (bt_bap_stream_get_io(stream)) stream = queue_find(bt_bap_stream_io_get_links(stream), stream_io_unset, NULL); fd = g_io_channel_unix_get_fd(io); if (bt_bap_stream_set_io(stream, fd)) g_io_channel_set_close_on_unref(io, FALSE); if (!queue_find(bt_bap_stream_io_get_links(stream), stream_io_unset, NULL)) { /* All fds have been notified. Mark service as connected. */ btd_service_connecting_complete(bap_data->service, 0); g_io_channel_unref(bap_data->listen_io); g_io_channel_shutdown(bap_data->listen_io, TRUE, NULL); bap_data->listen_io = NULL; } } static void create_stream_for_bis(struct bap_data *bap_data, struct bt_bap_pac *lpac, struct bt_bap_qos *qos, struct iovec *caps, struct iovec *meta, char *path) { struct bap_setup *setup; setup = setup_new(NULL); setup->qos = *qos; /* Create an internal copy for bcode */ setup->qos.bcast.bcode = util_iov_dup(qos->bcast.bcode, 1); setup->data = bap_data; queue_push_tail(bap_data->bcast_snks, setup); /* Create and configure stream */ setup->stream = bt_bap_stream_new(bap_data->bap, lpac, NULL, &setup->qos, caps); bt_bap_stream_set_user_data(setup->stream, path); bt_bap_stream_config(setup->stream, &setup->qos, caps, NULL, NULL); bt_bap_stream_metadata(setup->stream, meta, NULL, NULL); } static void bis_handler(uint8_t bis, uint8_t sgrp, struct iovec *caps, struct iovec *meta, struct bt_bap_qos *qos, void *user_data) { struct bap_data *data = user_data; struct bt_bap_pac *lpac; char *path; bt_bap_bis_probe(data->bap, bis, sgrp, caps, meta, qos); /* Check if this BIS matches any local PAC */ bt_bap_verify_bis(data->bap, bis, caps, &lpac); if (!lpac) return; if (asprintf(&path, "%s/bis%d", device_get_path(data->device), bis) < 0) return; create_stream_for_bis(data, lpac, qos, caps, meta, path); } static gboolean big_info_report_cb(GIOChannel *io, GIOCondition cond, gpointer user_data) { GError *err = NULL; struct bap_data *data = user_data; struct bt_iso_base base; struct bt_iso_qos qos; struct iovec iov; struct bt_bap_qos bap_qos = {0}; DBG("BIG Info received"); bt_io_get(io, &err, BT_IO_OPT_BASE, &base, BT_IO_OPT_QOS, &qos, BT_IO_OPT_INVALID); if (err) { error("%s", err->message); g_error_free(err); g_io_channel_shutdown(io, TRUE, NULL); data->io_id = 0; return FALSE; } /* Close the listen io */ g_io_channel_shutdown(data->listen_io, TRUE, NULL); g_io_channel_unref(data->listen_io); data->listen_io = NULL; /* For short-lived PA, the sync is no longer needed at * this point, so the io can be closed. */ g_io_channel_shutdown(io, TRUE, NULL); /* Analyze received BASE data and create remote media endpoints for each * BIS matching our capabilities */ iov.iov_base = base.base; iov.iov_len = base.base_len; /* Create BAP QoS structure */ bt_bap_iso_qos_to_bap_qos(&qos, &bap_qos); bt_bap_parse_base(&iov, &bap_qos, bap_debug, bis_handler, data); util_iov_free(bap_qos.bcast.bcode, 1); service_set_connecting(data->service); data->io_id = 0; return FALSE; } static void iso_pa_sync_confirm_cb(GIOChannel *io, void *user_data) { struct bap_data *data = user_data; /* PA Sync was established, wait for BIG Info report so that the * encryption flag is also available. */ DBG("PA Sync done"); data->io_id = g_io_add_watch(io, G_IO_OUT, big_info_report_cb, user_data); } static bool match_data_bap_data(const void *data, const void *match_data) { const struct bap_data *bdata = data; const struct btd_adapter *adapter = match_data; return bdata->user_data == adapter; } static const GDBusMethodTable ep_methods[] = { { GDBUS_EXPERIMENTAL_ASYNC_METHOD("SetConfiguration", GDBUS_ARGS({ "endpoint", "o" }, { "Configuration", "a{sv}" } ), NULL, set_configuration) }, { }, }; static void ep_cancel_select(struct bap_ep *ep); static void ep_free(void *data) { struct bap_ep *ep = data; struct queue *setups = ep->setups; ep_cancel_select(ep); ep->setups = NULL; queue_destroy(setups, setup_free); free(ep->path); free(ep); } struct match_ep { struct bt_bap_pac *lpac; struct bt_bap_pac *rpac; }; static bool match_ep(const void *data, const void *user_data) { const struct bap_ep *ep = data; const struct match_ep *match = user_data; if (ep->lpac != match->lpac) return false; return ep->rpac == match->rpac; } static struct bap_ep *ep_register_bcast(struct bap_data *data, struct bt_bap_pac *lpac, struct bt_bap_pac *rpac) { struct btd_adapter *adapter = data->adapter; struct btd_device *device = data->device; struct bap_ep *ep; struct queue *queue; int i, err = 0; const char *suffix; struct match_ep match = { lpac, rpac }; switch (bt_bap_pac_get_type(lpac)) { case BT_BAP_BCAST_SOURCE: case BT_BAP_BCAST_SINK: queue = data->bcast; i = queue_length(data->bcast); suffix = "bcast"; break; default: return NULL; } ep = queue_find(queue, match_ep, &match); if (ep) return ep; ep = new0(struct bap_ep, 1); ep->data = data; ep->lpac = lpac; ep->rpac = rpac; if (device) ep->data->device = device; switch (bt_bap_pac_get_type(lpac)) { case BT_BAP_BCAST_SOURCE: err = asprintf(&ep->path, "%s/pac_%s%d", adapter_get_path(adapter), suffix, i); break; case BT_BAP_BCAST_SINK: err = asprintf(&ep->path, "%s/pac_%s%d", device_get_path(device), suffix, i); break; } if (err < 0) { error("Could not allocate path for remote pac %s/pac%d", adapter_get_path(adapter), i); free(ep); return NULL; } if (g_dbus_register_interface(btd_get_dbus_connection(), ep->path, MEDIA_ENDPOINT_INTERFACE, ep_methods, NULL, ep_properties, ep, ep_free) == FALSE) { error("Could not register remote ep %s", ep->path); ep_free(ep); return NULL; } /* * The broadcast source local endpoint has only lpac and broadcast * sink local endpoint has a rpac and a lpac */ if (rpac) bt_bap_pac_set_user_data(rpac, ep->path); DBG("ep %p lpac %p rpac %p path %s", ep, ep->lpac, ep->rpac, ep->path); queue_push_tail(queue, ep); return ep; } static void ep_update_properties(struct bap_ep *ep) { if (!ep->rpac) return; if (ep->locations != bt_bap_pac_get_locations(ep->rpac)) g_dbus_emit_property_changed(btd_get_dbus_connection(), ep->path, MEDIA_ENDPOINT_INTERFACE, "Locations"); if (ep->supported_context != bt_bap_pac_get_supported_context(ep->rpac)) g_dbus_emit_property_changed(btd_get_dbus_connection(), ep->path, MEDIA_ENDPOINT_INTERFACE, "SupportedContext"); if (ep->context != bt_bap_pac_get_context(ep->rpac)) g_dbus_emit_property_changed(btd_get_dbus_connection(), ep->path, MEDIA_ENDPOINT_INTERFACE, "Context"); } static struct bap_ep *ep_register(struct btd_service *service, struct bt_bap_pac *lpac, struct bt_bap_pac *rpac) { struct btd_device *device = btd_service_get_device(service); struct bap_data *data = btd_service_get_user_data(service); struct bap_ep *ep; struct queue *queue; int i, err; const char *suffix; struct match_ep match = { lpac, rpac }; switch (bt_bap_pac_get_type(rpac)) { case BT_BAP_SINK: queue = data->snks; i = queue_length(data->snks); suffix = "sink"; break; case BT_BAP_SOURCE: queue = data->srcs; i = queue_length(data->srcs); suffix = "source"; break; default: return NULL; } ep = queue_find(queue, match_ep, &match); if (ep) { ep_update_properties(ep); return ep; } ep = new0(struct bap_ep, 1); ep->data = data; ep->lpac = lpac; ep->rpac = rpac; err = asprintf(&ep->path, "%s/pac_%s%d", device_get_path(device), suffix, i); if (err < 0) { error("Could not allocate path for remote pac %s/pac%d", device_get_path(device), i); free(ep); return NULL; } if (g_dbus_register_interface(btd_get_dbus_connection(), ep->path, MEDIA_ENDPOINT_INTERFACE, ep_methods, NULL, ep_properties, ep, ep_free) == FALSE) { error("Could not register remote ep %s", ep->path); ep_free(ep); return NULL; } bt_bap_pac_set_user_data(rpac, ep->path); DBG("ep %p lpac %p rpac %p path %s", ep, ep->lpac, ep->rpac, ep->path); queue_push_tail(queue, ep); return ep; } static void setup_config(void *data, void *user_data) { struct bap_setup *setup = data; struct bap_ep *ep = setup->ep; DBG("setup %p caps %p metadata %p", setup, setup->caps, setup->metadata); /* TODO: Check if stream capabilities match add support for Latency * and PHY. */ if (!setup->stream) setup->stream = bt_bap_stream_new(ep->data->bap, ep->lpac, ep->rpac, &setup->qos, setup->caps); setup->id = bt_bap_stream_config(setup->stream, &setup->qos, setup->caps, config_cb, setup); if (!setup->id) { DBG("Unable to config stream"); setup_free(setup); return; } if (setup->metadata && setup->metadata->iov_len) bt_bap_stream_metadata(setup->stream, setup->metadata, NULL, NULL); bt_bap_stream_set_user_data(setup->stream, ep->path); } static void bap_config(void *data, void *user_data) { struct bap_ep *ep = data; queue_foreach(ep->setups, setup_config, NULL); } static void select_cb(struct bt_bap_pac *pac, int err, struct iovec *caps, struct iovec *metadata, struct bt_bap_qos *qos, void *user_data) { struct bap_ep *ep = user_data; struct bap_setup *setup; if (err) { error("err %d", err); ep->data->selecting--; goto done; } setup = setup_new(ep); setup->caps = util_iov_dup(caps, 1); setup->metadata = util_iov_dup(metadata, 1); setup->qos = *qos; DBG("selecting %d", ep->data->selecting); ep->data->selecting--; done: if (ep->data->selecting) return; queue_foreach(ep->data->srcs, bap_config, NULL); queue_foreach(ep->data->snks, bap_config, NULL); queue_foreach(ep->data->bcast, bap_config, NULL); } static bool pac_register(struct bt_bap_pac *lpac, struct bt_bap_pac *rpac, void *user_data) { struct btd_service *service = user_data; struct bap_ep *ep; DBG("lpac %p rpac %p", lpac, rpac); ep = ep_register(service, lpac, rpac); if (!ep) error("Unable to register endpoint for pac %p", rpac); return true; } static bool pac_select(struct bt_bap_pac *lpac, struct bt_bap_pac *rpac, void *user_data) { struct btd_service *service = user_data; struct bap_data *data = btd_service_get_user_data(service); struct match_ep match = { lpac, rpac }; struct queue *queue; struct bap_ep *ep; switch (bt_bap_pac_get_type(rpac)) { case BT_BAP_SINK: queue = data->snks; break; case BT_BAP_SOURCE: queue = data->srcs; break; default: return true; } ep = queue_find(queue, match_ep, &match); if (!ep) { error("Unable to find endpoint for pac %p", rpac); return true; } /* TODO: Cache LRU? */ if (btd_service_is_initiator(service)) bt_bap_select(lpac, rpac, &ep->data->selecting, select_cb, ep); return true; } static bool pac_cancel_select(struct bt_bap_pac *lpac, struct bt_bap_pac *rpac, void *user_data) { struct bap_ep *ep = user_data; bt_bap_cancel_select(lpac, select_cb, ep); return true; } static void ep_cancel_select(struct bap_ep *ep) { struct bt_bap *bap = ep->data->bap; bt_bap_foreach_pac(bap, BT_BAP_SOURCE, pac_cancel_select, ep); bt_bap_foreach_pac(bap, BT_BAP_SINK, pac_cancel_select, ep); } static bool pac_found_bcast(struct bt_bap_pac *lpac, struct bt_bap_pac *rpac, void *user_data) { struct bap_data *data = user_data; struct bap_ep *ep; DBG("lpac %p rpac %p", lpac, rpac); ep = ep_register_bcast(user_data, lpac, rpac); if (!ep) { error("Unable to register endpoint for pac %p", rpac); return true; } /* Mark the device as connetable if an Endpoint is registered */ if (data->device) btd_device_set_connectable(data->device, true); return true; } static void bap_ready(struct bt_bap *bap, void *user_data) { struct btd_service *service = user_data; DBG("bap %p", bap); /* Register all ep before selecting, so that sound server * knows all. */ bt_bap_foreach_pac(bap, BT_BAP_SOURCE, pac_register, service); bt_bap_foreach_pac(bap, BT_BAP_SINK, pac_register, service); bt_bap_foreach_pac(bap, BT_BAP_SOURCE, pac_select, service); bt_bap_foreach_pac(bap, BT_BAP_SINK, pac_select, service); } static bool match_setup_stream(const void *data, const void *user_data) { const struct bap_setup *setup = data; const struct bt_bap_stream *stream = user_data; return setup->stream == stream; } static bool match_ep_stream(const void *data, const void *user_data) { const struct bap_ep *ep = data; const struct bt_bap_stream *stream = user_data; return queue_find(ep->setups, match_setup_stream, stream); } static struct bap_setup *bap_find_setup_by_stream(struct bap_data *data, struct bt_bap_stream *stream) { struct bap_ep *ep = NULL; struct queue *queue = NULL; switch (bt_bap_stream_get_type(stream)) { case BT_BAP_STREAM_TYPE_UCAST: ep = queue_find(data->snks, match_ep_stream, stream); if (!ep) ep = queue_find(data->srcs, match_ep_stream, stream); break; case BT_BAP_STREAM_TYPE_BCAST: ep = queue_find(data->bcast, match_ep_stream, stream); break; } if (ep) queue = ep->setups; else queue = data->bcast_snks; return queue_find(queue, match_setup_stream, stream); } static void iso_connect_bcast_cb(GIOChannel *chan, GError *err, gpointer user_data) { struct bt_bap_stream *stream = user_data; int fd; if (err) { error("%s", err->message); bt_bap_stream_set_io(stream, -1); return; } DBG("ISO connected"); fd = g_io_channel_unix_get_fd(chan); if (bt_bap_stream_set_io(stream, fd)) { bt_bap_stream_start(stream, NULL, NULL); g_io_channel_set_close_on_unref(chan, FALSE); return; } error("Unable to set IO"); bt_bap_stream_set_io(stream, -1); } static void iso_connect_cb(GIOChannel *chan, GError *err, gpointer user_data) { struct bt_bap_stream *stream = user_data; int fd; if (err) { error("%s", err->message); bt_bap_stream_set_io(stream, -1); return; } DBG("ISO connected"); fd = g_io_channel_unix_get_fd(chan); if (bt_bap_stream_set_io(stream, fd)) { g_io_channel_set_close_on_unref(chan, FALSE); return; } error("Unable to set IO"); bt_bap_stream_set_io(stream, -1); } static void bap_iso_qos(struct bt_bap_qos *qos, struct bt_iso_io_qos *io) { if (!qos) return; io->interval = qos->ucast.io_qos.interval; io->latency = qos->ucast.io_qos.latency; io->sdu = qos->ucast.io_qos.sdu; io->phy = qos->ucast.io_qos.phy; io->rtn = qos->ucast.io_qos.rtn; } static bool match_stream_qos(const void *data, const void *user_data) { const struct bt_bap_stream *stream = data; const struct bt_iso_qos *iso_qos = user_data; struct bt_bap_qos *qos; qos = bt_bap_stream_get_qos((void *)stream); if (iso_qos->ucast.cig != qos->ucast.cig_id) return false; return iso_qos->ucast.cis == qos->ucast.cis_id; } static void iso_confirm_cb(GIOChannel *io, void *user_data) { struct bap_data *data = user_data; struct bt_bap_stream *stream; struct bt_iso_qos qos; char address[18]; GError *err = NULL; bt_io_get(io, &err, BT_IO_OPT_DEST, address, BT_IO_OPT_QOS, &qos, BT_IO_OPT_INVALID); if (err) { error("%s", err->message); g_error_free(err); goto drop; } DBG("ISO: incoming connect from %s (CIG 0x%02x CIS 0x%02x)", address, qos.ucast.cig, qos.ucast.cis); stream = queue_remove_if(data->streams, match_stream_qos, &qos); if (!stream) { error("No matching stream found"); goto drop; } if (!bt_io_accept(io, iso_connect_cb, stream, NULL, &err)) { error("bt_io_accept: %s", err->message); g_error_free(err); goto drop; } return; drop: g_io_channel_shutdown(io, TRUE, NULL); } static void setup_accept_io(struct bap_setup *setup, struct bt_bap_stream *stream, int fd, int defer) { char c; struct pollfd pfd; socklen_t len; if (fd < 0 || defer) return; /* Check if socket has DEFER_SETUP set */ len = sizeof(defer); if (getsockopt(fd, SOL_BLUETOOTH, BT_DEFER_SETUP, &defer, &len) < 0) /* Ignore errors since the fd may be connected already */ return; if (!defer) return; DBG("stream %p fd %d defer %s", stream, fd, defer ? "true" : "false"); memset(&pfd, 0, sizeof(pfd)); pfd.fd = fd; pfd.events = POLLOUT; if (poll(&pfd, 1, 0) < 0) { error("poll: %s (%d)", strerror(errno), errno); goto fail; } if (!(pfd.revents & POLLOUT)) { if (read(fd, &c, 1) < 0) { error("read: %s (%d)", strerror(errno), errno); goto fail; } } setup->cig_active = true; return; fail: close(fd); } struct cig_busy_data { struct btd_adapter *adapter; uint8_t cig; }; static bool match_cig_active(const void *data, const void *match_data) { const struct bap_setup *setup = data; const struct cig_busy_data *info = match_data; return (setup->qos.ucast.cig_id == info->cig) && setup->cig_active; } static bool cig_busy_ep(const void *data, const void *match_data) { const struct bap_ep *ep = data; const struct cig_busy_data *info = match_data; return queue_find(ep->setups, match_cig_active, info); } static bool cig_busy_session(const void *data, const void *match_data) { const struct bap_data *session = data; const struct cig_busy_data *info = match_data; if (device_get_adapter(session->device) != info->adapter) return false; return queue_find(session->snks, cig_busy_ep, match_data) || queue_find(session->srcs, cig_busy_ep, match_data); } static bool is_cig_busy(struct bap_data *data, uint8_t cig) { struct cig_busy_data info; if (cig == BT_ISO_QOS_CIG_UNSET) return false; info.adapter = device_get_adapter(data->device); info.cig = cig; return queue_find(sessions, cig_busy_session, &info); } static void setup_create_io(struct bap_data *data, struct bap_setup *setup, struct bt_bap_stream *stream, int defer); static gboolean setup_io_recreate(void *user_data) { struct bap_setup *setup = user_data; DBG("%p", setup); setup->io_id = 0; setup_create_io(setup->ep->data, setup, setup->stream, true); return FALSE; } static void setup_recreate(void *data, void *match_data) { struct bap_setup *setup = data; struct cig_busy_data *info = match_data; if (setup->qos.ucast.cig_id != info->cig || !setup->recreate || setup->io_id) return; setup->recreate = false; setup->io_id = g_idle_add(setup_io_recreate, setup); } static void recreate_cig_ep(void *data, void *match_data) { struct bap_ep *ep = data; queue_foreach(ep->setups, setup_recreate, match_data); } static void recreate_cig_session(void *data, void *match_data) { struct bap_data *session = data; struct cig_busy_data *info = match_data; if (device_get_adapter(session->device) != info->adapter) return; queue_foreach(session->snks, recreate_cig_ep, match_data); queue_foreach(session->srcs, recreate_cig_ep, match_data); } static void recreate_cig(struct bap_setup *setup) { struct bap_data *data = setup->ep->data; struct cig_busy_data info; info.adapter = device_get_adapter(data->device); info.cig = setup->qos.ucast.cig_id; DBG("adapter %p setup %p recreate CIG %d", info.adapter, setup, info.cig); if (setup->qos.ucast.cig_id == BT_ISO_QOS_CIG_UNSET) { recreate_cig_ep(setup->ep, &info); return; } queue_foreach(sessions, recreate_cig_session, &info); } static gboolean setup_io_disconnected(GIOChannel *io, GIOCondition cond, gpointer user_data) { struct bap_setup *setup = user_data; DBG("%p recreate %s", setup, setup->recreate ? "true" : "false"); setup->io_id = 0; setup_io_close(setup, NULL); /* Check if connecting recreate IO */ if (!is_cig_busy(setup->ep->data, setup->qos.ucast.cig_id)) recreate_cig(setup); return FALSE; } static void bap_connect_bcast_io_cb(GIOChannel *chan, GError *err, gpointer user_data) { struct bap_setup *setup = user_data; if (!setup->stream) return; iso_connect_bcast_cb(chan, err, setup->stream); } static void bap_connect_io_cb(GIOChannel *chan, GError *err, gpointer user_data) { struct bap_setup *setup = user_data; if (!setup->stream) return; iso_connect_cb(chan, err, setup->stream); } static void setup_connect_io(struct bap_data *data, struct bap_setup *setup, struct bt_bap_stream *stream, struct bt_iso_qos *qos, int defer) { struct btd_adapter *adapter = device_get_adapter(data->device); GIOChannel *io; GError *err = NULL; int fd; /* If IO already set skip creating it again */ if (bt_bap_stream_get_io(stream)) { DBG("setup %p stream %p has existing io", setup, stream); return; } if (bt_bap_stream_io_is_connecting(stream, &fd)) { setup_accept_io(setup, stream, fd, defer); return; } /* If IO channel still up or CIG is busy, wait for it to be * disconnected and then recreate. */ if (setup->io || is_cig_busy(data, setup->qos.ucast.cig_id)) { DBG("setup %p stream %p defer %s wait recreate", setup, stream, defer ? "true" : "false"); setup->recreate = true; return; } if (setup->io_id) { g_source_remove(setup->io_id); setup->io_id = 0; } DBG("setup %p stream %p defer %s", setup, stream, defer ? "true" : "false"); io = bt_io_connect(bap_connect_io_cb, setup, NULL, &err, BT_IO_OPT_SOURCE_BDADDR, btd_adapter_get_address(adapter), BT_IO_OPT_SOURCE_TYPE, btd_adapter_get_address_type(adapter), BT_IO_OPT_DEST_BDADDR, device_get_address(data->device), BT_IO_OPT_DEST_TYPE, device_get_le_address_type(data->device), BT_IO_OPT_MODE, BT_IO_MODE_ISO, BT_IO_OPT_QOS, qos, BT_IO_OPT_DEFER_TIMEOUT, defer, BT_IO_OPT_INVALID); if (!io) { error("%s", err->message); g_error_free(err); return; } setup->io_id = g_io_add_watch(io, G_IO_HUP | G_IO_ERR | G_IO_NVAL, setup_io_disconnected, setup); setup->io = io; setup->cig_active = !defer; bt_bap_stream_io_connecting(stream, g_io_channel_unix_get_fd(io)); } static void setup_connect_io_broadcast(struct bap_data *data, struct bap_setup *setup, struct bt_bap_stream *stream, struct bt_iso_qos *qos, int defer) { struct btd_adapter *adapter = data->user_data; GIOChannel *io = NULL; GError *err = NULL; bdaddr_t dst_addr = {0}; char addr[18]; struct bt_iso_base base; /* If IO already set and we are in the creation step, * skip creating it again */ if (bt_bap_stream_get_io(stream)) return; if (setup->io_id) { g_source_remove(setup->io_id); setup->io_id = 0; } base.base_len = setup->base->iov_len; memset(base.base, 0, 248); memcpy(base.base, setup->base->iov_base, setup->base->iov_len); ba2str(btd_adapter_get_address(adapter), addr); DBG("setup %p stream %p", setup, stream); io = bt_io_connect(bap_connect_bcast_io_cb, setup, NULL, &err, BT_IO_OPT_SOURCE_BDADDR, btd_adapter_get_address(adapter), BT_IO_OPT_SOURCE_TYPE, btd_adapter_get_address_type(adapter), BT_IO_OPT_DEST_BDADDR, &dst_addr, BT_IO_OPT_DEST_TYPE, BDADDR_LE_PUBLIC, BT_IO_OPT_MODE, BT_IO_MODE_ISO, BT_IO_OPT_QOS, qos, BT_IO_OPT_BASE, &base, BT_IO_OPT_DEFER_TIMEOUT, defer, BT_IO_OPT_INVALID); if (!io) { error("%s", err->message); g_error_free(err); return; } setup->io_id = g_io_add_watch(io, G_IO_HUP | G_IO_ERR | G_IO_NVAL, setup_io_disconnected, setup); setup->io = io; bt_bap_stream_io_connecting(stream, g_io_channel_unix_get_fd(io)); } static void setup_listen_io(struct bap_data *data, struct bt_bap_stream *stream, struct bt_iso_qos *qos) { struct btd_adapter *adapter = device_get_adapter(data->device); GIOChannel *io; GError *err = NULL; DBG("stream %p", stream); /* If IO already set skip creating it again */ if (bt_bap_stream_get_io(stream) || data->listen_io) return; io = bt_io_listen(NULL, iso_confirm_cb, data, NULL, &err, BT_IO_OPT_SOURCE_BDADDR, btd_adapter_get_address(adapter), BT_IO_OPT_SOURCE_TYPE, btd_adapter_get_address_type(adapter), BT_IO_OPT_DEST_BDADDR, BDADDR_ANY, BT_IO_OPT_DEST_TYPE, BDADDR_LE_PUBLIC, BT_IO_OPT_MODE, BT_IO_MODE_ISO, BT_IO_OPT_QOS, qos, BT_IO_OPT_INVALID); if (!io) { error("%s", err->message); g_error_free(err); return; } data->listen_io = io; } static int pa_sync(struct bap_data *data); static void pa_and_big_sync(struct bap_setup *setup); static void setup_accept_io_broadcast(struct bap_data *data, struct bap_setup *setup) { pa_and_big_sync(setup); } static void setup_create_ucast_io(struct bap_data *data, struct bap_setup *setup, struct bt_bap_stream *stream, int defer) { struct bt_bap_qos *qos[2] = {}; struct bt_iso_qos iso_qos; if (!bt_bap_stream_io_get_qos(stream, &qos[0], &qos[1])) { error("bt_bap_stream_get_qos_links: failed"); return; } memset(&iso_qos, 0, sizeof(iso_qos)); iso_qos.ucast.cig = qos[0] ? qos[0]->ucast.cig_id : qos[1]->ucast.cig_id; iso_qos.ucast.cis = qos[0] ? qos[0]->ucast.cis_id : qos[1]->ucast.cis_id; bap_iso_qos(qos[0], &iso_qos.ucast.in); bap_iso_qos(qos[1], &iso_qos.ucast.out); if (setup) setup_connect_io(data, setup, stream, &iso_qos, defer); else setup_listen_io(data, stream, &iso_qos); } static void setup_create_bcast_io(struct bap_data *data, struct bap_setup *setup, struct bt_bap_stream *stream, int defer) { struct bt_bap_qos *qos = &setup->qos; struct iovec *bcode = qos->bcast.bcode; struct bt_iso_qos iso_qos; memset(&iso_qos, 0, sizeof(iso_qos)); iso_qos.bcast.big = setup->qos.bcast.big; iso_qos.bcast.bis = setup->qos.bcast.bis; iso_qos.bcast.sync_factor = setup->qos.bcast.sync_factor; iso_qos.bcast.packing = setup->qos.bcast.packing; iso_qos.bcast.framing = setup->qos.bcast.framing; iso_qos.bcast.encryption = setup->qos.bcast.encryption; if (bcode && bcode->iov_base) memcpy(iso_qos.bcast.bcode, bcode->iov_base, bcode->iov_len); iso_qos.bcast.options = setup->qos.bcast.options; iso_qos.bcast.skip = setup->qos.bcast.skip; iso_qos.bcast.sync_timeout = setup->qos.bcast.sync_timeout; iso_qos.bcast.sync_cte_type = setup->qos.bcast.sync_cte_type; iso_qos.bcast.mse = setup->qos.bcast.mse; iso_qos.bcast.timeout = setup->qos.bcast.timeout; memcpy(&iso_qos.bcast.out, &setup->qos.bcast.io_qos, sizeof(struct bt_iso_io_qos)); if (bt_bap_stream_get_dir(stream) == BT_BAP_BCAST_SINK) setup_connect_io_broadcast(data, setup, stream, &iso_qos, defer); else setup_accept_io_broadcast(data, setup); } static void setup_create_io(struct bap_data *data, struct bap_setup *setup, struct bt_bap_stream *stream, int defer) { DBG("setup %p stream %p defer %s", setup, stream, defer ? "true" : "false"); if (!data->streams) data->streams = queue_new(); if (!queue_find(data->streams, NULL, stream)) queue_push_tail(data->streams, stream); switch (bt_bap_stream_get_type(stream)) { case BT_BAP_STREAM_TYPE_UCAST: setup_create_ucast_io(data, setup, stream, defer); break; case BT_BAP_STREAM_TYPE_BCAST: setup_create_bcast_io(data, setup, stream, defer); break; } } static void bap_state(struct bt_bap_stream *stream, uint8_t old_state, uint8_t new_state, void *user_data) { struct bap_data *data = user_data; struct bap_setup *setup; DBG("stream %p: %s(%u) -> %s(%u)", stream, bt_bap_stream_statestr(old_state), old_state, bt_bap_stream_statestr(new_state), new_state); /* Ignore transitions back to same state (ASCS allows some of these). * Of these we need to handle only the config->config case, which will * occur when reconfiguring the codec from initial config state. */ if (new_state == old_state && new_state != BT_BAP_STREAM_STATE_CONFIG) return; setup = bap_find_setup_by_stream(data, stream); switch (new_state) { case BT_BAP_STREAM_STATE_IDLE: /* Release stream if idle */ if (setup) setup_free(setup); else queue_remove(data->streams, stream); break; case BT_BAP_STREAM_STATE_CONFIG: if (setup && !setup->id) { setup_create_io(data, setup, stream, true); if (!setup->io) { error("Unable to create io"); if (old_state != BT_BAP_STREAM_STATE_RELEASING) bt_bap_stream_release(stream, NULL, NULL); return; } /* Wait QoS response to respond */ setup->id = bt_bap_stream_qos(stream, &setup->qos, qos_cb, setup); if (!setup->id) { error("Failed to Configure QoS"); bt_bap_stream_release(stream, NULL, NULL); } } break; case BT_BAP_STREAM_STATE_QOS: setup_create_io(data, setup, stream, true); break; case BT_BAP_STREAM_STATE_ENABLING: if (setup) setup_create_io(data, setup, stream, false); break; case BT_BAP_STREAM_STATE_STREAMING: break; } } /* This function will call setup_create_io on all BISes from a BIG. * The defer parameter will be set on true on all but the last one. * This is done to inform the kernel when to when to start the BIG. */ static bool create_io_bises(struct bap_setup *setup, uint8_t nb_bises, struct bap_data *data) { const struct queue_entry *entry; struct bap_setup *ent_setup; bool defer = true; uint8_t active_bis_cnt = 1; for (entry = queue_get_entries(setup->ep->setups); entry; entry = entry->next) { ent_setup = entry->data; if (bt_bap_stream_get_qos(ent_setup->stream)->bcast.big != bt_bap_stream_get_qos(setup->stream)->bcast.big) continue; if (active_bis_cnt == nb_bises) defer = false; setup_create_io(data, ent_setup, ent_setup->stream, defer); if (!ent_setup->io) { error("Unable to create io"); goto fail; } active_bis_cnt++; } return true; fail: /* Clear the io of the created sockets if one * socket creation fails. */ for (entry = queue_get_entries(setup->ep->setups); entry; entry = entry->next) { ent_setup = entry->data; if (bt_bap_stream_get_qos(ent_setup->stream)->bcast.big != bt_bap_stream_get_qos(setup->stream)->bcast.big) continue; if (setup->io) g_io_channel_unref(setup->io); } return false; } static void iterate_setup_update_base(void *data, void *user_data) { struct bap_setup *setup = data; struct bap_setup *data_setup = user_data; if ((setup->stream != data_setup->stream) && (setup->qos.bcast.big == data_setup->qos.bcast.big)) { if (setup->base) util_iov_free(setup->base, 1); setup->base = util_iov_dup(data_setup->base, 1); } } /* Function checks the state of all streams in the same BIG * as the parameter stream, so it can decide if any sockets need * to be created. Returns he number of streams that need a socket * from that BIG. */ static uint8_t get_streams_nb_by_state(struct bap_setup *setup) { const struct queue_entry *entry; struct bap_setup *ent_setup; uint8_t stream_cnt = 0; if (setup->qos.bcast.big == BT_ISO_QOS_BIG_UNSET) /* If BIG ID is unset this is a single BIS BIG. * return 1 as create one socket only for this BIS */ return 1; for (entry = queue_get_entries(setup->ep->setups); entry; entry = entry->next) { ent_setup = entry->data; /* Skip the curent stream form testing */ if (ent_setup == setup) { stream_cnt++; continue; } /* Test only BISes for the same BIG */ if (bt_bap_stream_get_qos(ent_setup->stream)->bcast.big != bt_bap_stream_get_qos(setup->stream)->bcast.big) continue; if (bt_bap_stream_get_state(ent_setup->stream) == BT_BAP_STREAM_STATE_STREAMING) /* If one stream in a multiple BIS BIG is in * streaming state this means that just the current * stream must have is socket created so return 1. */ return 1; else if (bt_bap_stream_get_state(ent_setup->stream) != BT_BAP_STREAM_STATE_ENABLING) /* Not all streams form a BIG have received transport * acquire, so wait for the other streams to. */ return 0; stream_cnt++; } /* Return the number of streams for the BIG * as all are ready to create sockets */ return stream_cnt; } static void bap_state_bcast_src(struct bt_bap_stream *stream, uint8_t old_state, uint8_t new_state, void *user_data) { struct bap_data *data = user_data; struct bap_setup *setup; bool defer = false; uint8_t nb_bises = 0; DBG("stream %p: %s(%u) -> %s(%u)", stream, bt_bap_stream_statestr(old_state), old_state, bt_bap_stream_statestr(new_state), new_state); /* Ignore transitions back to same state */ if (new_state == old_state) return; setup = bap_find_setup_by_stream(data, stream); switch (new_state) { case BT_BAP_STREAM_STATE_IDLE: /* Release stream if idle */ if (setup) setup_free(setup); else queue_remove(data->streams, stream); break; case BT_BAP_STREAM_STATE_CONFIG: // TO DO Reconfiguration break; /* Use the ENABLING state to know when a transport * linked to a stream has been acquired by a process * and in the case of a BIG with one BIS stream goes * in the ENABLING state waiting for the response * from the kernel that the BIG has been created * so it can go to the streaming state. * For the case of a BIG with multiple BISes, * the BIG is created when all BISes are acquired. * So we use the ENABLING state to verify that all * transports attached to that streams form BIG have * been acquired so we can create the BIG. */ case BT_BAP_STREAM_STATE_ENABLING: /* If the stream attached to a broadcast * source endpoint generate the base. */ if (setup->base == NULL) { setup->base = bt_bap_stream_get_base( setup->stream); /* Set the generated BASE on all setups * from the same BIG. */ queue_foreach(setup->ep->setups, iterate_setup_update_base, setup); } /* The kernel has 2 requirements when handling * multiple BIS connections for the same BIG: * 1 - setup_create_io for all but the last BIS * must be with defer true so we can inform the * kernel when to start the BIG. * 2 - The order in which the setup_create_io * are called must be in the order of BIS * indexes in BASE from first to last. * To address this requirement we will call * setup_create_io on all BISes only when all * transport acquire have been received and will * send it in the order of the BIS index * from BASE. */ nb_bises = get_streams_nb_by_state(setup); if (nb_bises == 1) { setup_create_io(data, setup, stream, defer); if (!setup->io) { error("Unable to create io"); if (old_state != BT_BAP_STREAM_STATE_RELEASING) bt_bap_stream_release(stream, NULL, NULL); } break; } else if (nb_bises == 0) break; if (!create_io_bises(setup, nb_bises, data)) { if (old_state != BT_BAP_STREAM_STATE_RELEASING) bt_bap_stream_release(stream, NULL, NULL); } break; } } static bool link_enabled(const void *data, const void *match_data) { struct bt_bap_stream *stream = (struct bt_bap_stream *)data; uint8_t state = bt_bap_stream_get_state(stream); return ((state == BT_BAP_STREAM_STATE_ENABLING) || bt_bap_stream_get_io(stream)); } static void bap_state_bcast_sink(struct bt_bap_stream *stream, uint8_t old_state, uint8_t new_state, void *user_data) { struct bap_data *data = user_data; struct bap_setup *setup; bool defer = false; DBG("stream %p: %s(%u) -> %s(%u)", stream, bt_bap_stream_statestr(old_state), old_state, bt_bap_stream_statestr(new_state), new_state); if (new_state == old_state && new_state != BT_BAP_STREAM_STATE_CONFIG) return; setup = bap_find_setup_by_stream(data, stream); if (!setup) return; switch (new_state) { case BT_BAP_STREAM_STATE_IDLE: /* Release stream if idle */ if (setup) setup_free(setup); else queue_remove(data->streams, stream); break; case BT_BAP_STREAM_STATE_CONFIG: if (!setup) break; if (old_state == BT_BAP_STREAM_STATE_STREAMING) setup_io_close(setup, NULL); break; case BT_BAP_STREAM_STATE_ENABLING: /* For a Broadcast Sink, the ENABLING state suggests that * the upper layer process requires the stream to start * receiving audio. This state is used to differentiate * between all configured streams and the ones that have * been enabled by the upper layer. * * Create stream io if not already created and if no * link has been enabled or started. * * The first enabled link will create and set fds for * all links. */ if (!bt_bap_stream_get_io(stream) && !queue_find(bt_bap_stream_io_get_links(stream), link_enabled, NULL)) setup_create_io(data, setup, stream, defer); break; } } static void pac_added(struct bt_bap_pac *pac, void *user_data) { struct btd_service *service = user_data; struct bap_data *data; DBG("pac %p", pac); if (btd_service_get_state(service) != BTD_SERVICE_STATE_CONNECTED) return; data = btd_service_get_user_data(service); bt_bap_foreach_pac(data->bap, BT_BAP_SOURCE, pac_register, service); bt_bap_foreach_pac(data->bap, BT_BAP_SINK, pac_register, service); bt_bap_foreach_pac(data->bap, BT_BAP_SOURCE, pac_select, service); bt_bap_foreach_pac(data->bap, BT_BAP_SINK, pac_select, service); } static void pac_added_broadcast(struct bt_bap_pac *pac, void *user_data) { struct bap_data *data = user_data; /* * If pac type is BT_BAP_BCAST_SOURCE locally create an endpoint * without a remote pac. * If pac type is BT_BAP_BCAST_SOURCE and remote then look for a * local broadcast sink pac locally before creating an endpoint. */ if (bt_bap_pac_bcast_is_local(data->bap, pac) && (bt_bap_pac_get_type(pac) == BT_BAP_BCAST_SOURCE)) pac_found_bcast(pac, NULL, user_data); else bt_bap_foreach_pac(data->bap, bt_bap_pac_get_type(pac), pac_found_bcast, data); } static bool ep_match_pac(const void *data, const void *match_data) { const struct bap_ep *ep = data; const struct bt_bap_pac *pac = match_data; return ep->rpac == pac || ep->lpac == pac; } static void pac_removed(struct bt_bap_pac *pac, void *user_data) { struct btd_service *service = user_data; struct bap_data *data; struct queue *queue; struct bap_ep *ep; DBG("pac %p", pac); if (btd_service_get_state(service) != BTD_SERVICE_STATE_CONNECTED) return; data = btd_service_get_user_data(service); switch (bt_bap_pac_get_type(pac)) { case BT_BAP_SINK: queue = data->srcs; break; case BT_BAP_SOURCE: queue = data->snks; break; default: return; } ep = queue_remove_if(queue, ep_match_pac, pac); if (!ep) return; ep_unregister(ep); } static void pac_removed_broadcast(struct bt_bap_pac *pac, void *user_data) { struct bap_data *data = user_data; struct queue *queue; struct bap_ep *ep; DBG("pac %p", pac); switch (bt_bap_pac_get_type(pac)) { case BT_BAP_SINK: queue = data->srcs; break; case BT_BAP_SOURCE: queue = data->snks; break; case BT_BAP_BCAST_SOURCE: queue = data->bcast; break; default: return; } ep = queue_remove_if(queue, ep_match_pac, pac); if (!ep) return; ep_unregister(ep); } static bool match_device(const void *data, const void *match_data) { const struct bap_data *bdata = data; const struct btd_device *device = match_data; return bdata->device == device; } static struct bap_data *bap_data_new(struct btd_device *device) { struct bap_data *data; data = new0(struct bap_data, 1); data->device = device; data->srcs = queue_new(); data->snks = queue_new(); data->bcast = queue_new(); return data; } static void bap_data_add(struct bap_data *data) { DBG("data %p", data); if (queue_find(sessions, NULL, data)) { error("data %p already added", data); return; } bt_bap_set_debug(data->bap, bap_debug, NULL, NULL); if (!sessions) sessions = queue_new(); queue_push_tail(sessions, data); if (data->service && !btd_service_get_user_data(data->service)) btd_service_set_user_data(data->service, data); } static bool match_data(const void *data, const void *match_data) { const struct bap_data *bdata = data; const struct bt_bap *bap = match_data; return bdata->bap == bap; } static bool io_get_qos(GIOChannel *io, struct bt_iso_qos *qos) { GError *err = NULL; bool ret; ret = bt_io_get(io, &err, BT_IO_OPT_QOS, qos, BT_IO_OPT_INVALID); if (!ret) { error("%s", err->message); g_error_free(err); } return ret; } static void bap_connecting(struct bt_bap_stream *stream, bool state, int fd, void *user_data) { struct bap_data *data = user_data; struct bap_setup *setup; struct bt_bap_qos *qos; GIOChannel *io; if (!state) return; setup = bap_find_setup_by_stream(data, stream); if (!setup) return; setup->recreate = false; qos = &setup->qos; if (!setup->io) { io = g_io_channel_unix_new(fd); setup->io_id = g_io_add_watch(io, G_IO_HUP | G_IO_ERR | G_IO_NVAL, setup_io_disconnected, setup); setup->io = io; } else io = setup->io; g_io_channel_set_close_on_unref(io, FALSE); /* Attempt to get CIG/CIS if they have not been set */ if (qos->ucast.cig_id == BT_ISO_QOS_CIG_UNSET || qos->ucast.cis_id == BT_ISO_QOS_CIS_UNSET) { struct bt_iso_qos iso_qos; if (!io_get_qos(io, &iso_qos)) { g_io_channel_unref(io); return; } qos->ucast.cig_id = iso_qos.ucast.cig; qos->ucast.cis_id = iso_qos.ucast.cis; } DBG("stream %p fd %d: CIG 0x%02x CIS 0x%02x", stream, fd, qos->ucast.cig_id, qos->ucast.cis_id); } static void bap_connecting_bcast(struct bt_bap_stream *stream, bool state, int fd, void *user_data) { struct bap_data *data = user_data; struct bap_setup *setup; GIOChannel *io; if (!state) return; setup = bap_find_setup_by_stream(data, stream); if (!setup) return; setup->recreate = false; if (!setup->io) { io = g_io_channel_unix_new(fd); setup->io_id = g_io_add_watch(io, G_IO_HUP | G_IO_ERR | G_IO_NVAL, setup_io_disconnected, setup); setup->io = io; } else io = setup->io; g_io_channel_set_close_on_unref(io, FALSE); /* Attempt to get BIG/BIS if they have not been set */ if (setup->qos.bcast.big == BT_ISO_QOS_BIG_UNSET || setup->qos.bcast.bis == BT_ISO_QOS_BIS_UNSET) { struct bt_iso_qos iso_qos; if (!io_get_qos(io, &iso_qos)) { g_io_channel_unref(io); return; } setup->qos.bcast.big = iso_qos.bcast.big; setup->qos.bcast.bis = iso_qos.bcast.bis; bt_bap_stream_qos(setup->stream, &setup->qos, NULL, NULL); } DBG("stream %p fd %d: BIG 0x%02x BIS 0x%02x", stream, fd, setup->qos.bcast.big, setup->qos.bcast.bis); } static void bap_attached(struct bt_bap *bap, void *user_data) { struct bap_data *data; struct bt_att *att; struct btd_device *device; DBG("%p", bap); data = queue_find(sessions, match_data, bap); if (data) return; att = bt_bap_get_att(bap); if (!att) return; device = btd_adapter_find_device_by_fd(bt_att_get_fd(att)); if (!device) { error("Unable to find device"); return; } data = bap_data_new(device); data->bap = bap; bap_data_add(data); data->state_id = bt_bap_state_register(data->bap, bap_state, bap_connecting, data, NULL); } static void bap_detached(struct bt_bap *bap, void *user_data) { struct bap_data *data; DBG("%p", bap); data = queue_find(sessions, match_data, bap); if (!data) { error("Unable to find bap session"); return; } /* If there is a service it means there is PACS thus we can keep * instance allocated. */ if (data->service) return; bap_data_remove(data); } static int pa_sync(struct bap_data *data) { GError *err = NULL; if (data->listen_io) { DBG("Already probed"); return -1; } DBG("Create PA sync with this source"); data->listen_io = bt_io_listen(NULL, iso_pa_sync_confirm_cb, data, NULL, &err, BT_IO_OPT_SOURCE_BDADDR, btd_adapter_get_address(data->adapter), BT_IO_OPT_SOURCE_TYPE, btd_adapter_get_address_type(data->adapter), BT_IO_OPT_DEST_BDADDR, device_get_address(data->device), BT_IO_OPT_DEST_TYPE, btd_device_get_bdaddr_type(data->device), BT_IO_OPT_MODE, BT_IO_MODE_ISO, BT_IO_OPT_QOS, &bap_sink_pa_qos, BT_IO_OPT_INVALID); if (!data->listen_io) { error("%s", err->message); g_error_free(err); } return 0; } static void append_setup(void *data, void *user_data) { struct bt_bap_stream *stream = data; struct sockaddr_iso_bc *addr = user_data; char *path = bt_bap_stream_get_user_data(stream); int bis = 1; int s_err; const char *strbis = NULL; strbis = strstr(path, "/bis"); if (!strbis) { DBG("bis index cannot be found"); return; } s_err = sscanf(strbis, "/bis%d", &bis); if (s_err == -1) { DBG("sscanf error"); return; } DBG("Do BIG Sync with BIS %d", bis); addr->bc_bis[addr->bc_num_bis] = bis; addr->bc_num_bis++; } static void setup_refresh_qos(void *data, void *user_data) { struct bt_bap_stream *stream = data; struct bap_data *bap_data = user_data; struct bap_setup *setup = bap_find_setup_by_stream(bap_data, stream); setup->qos = *bt_bap_stream_get_qos(stream); } static void iso_do_big_sync(GIOChannel *io, void *user_data) { GError *err = NULL; struct bap_setup *setup = user_data; struct bap_data *data = setup->data; struct sockaddr_iso_bc iso_bc_addr = {0}; struct bt_iso_qos qos; struct queue *links = bt_bap_stream_io_get_links(setup->stream); DBG("PA Sync done"); g_io_channel_unref(data->listen_io); g_io_channel_shutdown(data->listen_io, TRUE, NULL); data->listen_io = io; g_io_channel_ref(data->listen_io); /* Append each linked BIS to the BIG sync request */ append_setup(setup->stream, &iso_bc_addr); queue_foreach(links, append_setup, &iso_bc_addr); /* Refresh qos stored in setups */ setup->qos = *bt_bap_stream_get_qos(setup->stream); queue_foreach(links, setup_refresh_qos, data); /* Set the user requested QOS */ bt_bap_qos_to_iso_qos(&setup->qos, &qos); if (!bt_io_set(io, &err, BT_IO_OPT_QOS, &qos, BT_IO_OPT_INVALID)) { error("bt_io_set: %s", err->message); g_error_free(err); } if (!bt_io_bcast_accept(io, iso_bcast_confirm_cb, setup, NULL, &err, BT_IO_OPT_ISO_BC_NUM_BIS, iso_bc_addr.bc_num_bis, BT_IO_OPT_ISO_BC_BIS, iso_bc_addr.bc_bis, BT_IO_OPT_INVALID)) { error("bt_io_bcast_accept: %s", err->message); g_error_free(err); } } static void pa_and_big_sync(struct bap_setup *setup) { GError *err = NULL; struct bap_data *bap_data = setup->data; DBG("Create PA sync with this source"); bap_data->listen_io = bt_io_listen(NULL, iso_do_big_sync, setup, NULL, &err, BT_IO_OPT_SOURCE_BDADDR, btd_adapter_get_address(bap_data->adapter), BT_IO_OPT_DEST_BDADDR, device_get_address(bap_data->device), BT_IO_OPT_DEST_TYPE, btd_device_get_bdaddr_type(bap_data->device), BT_IO_OPT_MODE, BT_IO_MODE_ISO, BT_IO_OPT_QOS, &bap_sink_pa_qos, BT_IO_OPT_INVALID); if (!bap_data->listen_io) { error("%s", err->message); g_error_free(err); } } static int bap_bcast_probe(struct btd_service *service) { struct btd_device *device = btd_service_get_device(service); struct btd_adapter *adapter = device_get_adapter(device); struct btd_gatt_database *database = btd_adapter_get_database(adapter); struct bap_data *data; if (!btd_adapter_has_exp_feature(adapter, EXP_FEAT_ISO_SOCKET)) { error("BAP requires ISO Socket which is not enabled"); return -ENOTSUP; } data = bap_data_new(device); data->service = service; data->adapter = adapter; data->device = device; data->bap = bt_bap_new(btd_gatt_database_get_db(database), btd_gatt_database_get_db(database)); if (!data->bap) { error("Unable to create BAP instance"); free(data); return -EINVAL; } data->bcast_snks = queue_new(); bt_bap_set_user_data(data->bap, service); if (!bt_bap_attach(data->bap, NULL)) { error("BAP unable to attach"); return -EINVAL; } bap_data_add(data); data->ready_id = bt_bap_ready_register(data->bap, bap_ready, service, NULL); data->state_id = bt_bap_state_register(data->bap, bap_state_bcast_sink, bap_connecting_bcast, data, NULL); data->pac_id = bt_bap_pac_register(data->bap, pac_added_broadcast, pac_removed_broadcast, data, NULL); if (btd_service_get_user_data(service) == data) /* If the reference to the bap session has been set as service * user data, it means the broadcaster was autonomously probed. * Thus, the Broadcast Sink needs to create short lived PA sync * to discover streams. * * If the service user data does not match the bap session, it * means that the broadcaster was probed via a Broadcast * Assistant from the BASS plugin, where stream discovery and * configuration will also be handled. */ pa_sync(data); return 0; } static void bap_bcast_remove(struct btd_service *service) { struct btd_device *device = btd_service_get_device(service); struct bap_data *data; char addr[18]; ba2str(device_get_address(device), addr); DBG("%s", addr); data = queue_find(sessions, match_device, device); if (!data) { error("BAP service not handled by profile"); return; } bt_bap_bis_remove(data->bap); bap_data_remove(data); } static int bap_probe(struct btd_service *service) { struct btd_device *device = btd_service_get_device(service); struct btd_adapter *adapter = device_get_adapter(device); struct btd_gatt_database *database = btd_adapter_get_database(adapter); struct bap_data *data = btd_service_get_user_data(service); char addr[18]; ba2str(device_get_address(device), addr); DBG("%s", addr); if (!btd_adapter_has_exp_feature(adapter, EXP_FEAT_ISO_SOCKET)) { error("BAP requires ISO Socket which is not enabled"); return -ENOTSUP; } /* Ignore, if we were probed for this device already */ if (data) { error("Profile probed twice for the same device!"); return -EINVAL; } data = bap_data_new(device); data->service = service; data->bap = bt_bap_new(btd_gatt_database_get_db(database), btd_device_get_gatt_db(device)); if (!data->bap) { error("Unable to create BAP instance"); free(data); return -EINVAL; } bap_data_add(data); data->ready_id = bt_bap_ready_register(data->bap, bap_ready, service, NULL); data->state_id = bt_bap_state_register(data->bap, bap_state, bap_connecting, data, NULL); data->pac_id = bt_bap_pac_register(data->bap, pac_added, pac_removed, service, NULL); bt_bap_set_user_data(data->bap, service); return 0; } static int bap_accept(struct btd_service *service) { struct btd_device *device = btd_service_get_device(service); struct bt_gatt_client *client = btd_device_get_gatt_client(device); struct bap_data *data = btd_service_get_user_data(service); char addr[18]; ba2str(device_get_address(device), addr); DBG("%s", addr); if (!data) { error("BAP service not handled by profile"); return -EINVAL; } if (!bt_bap_attach(data->bap, client)) { error("BAP unable to attach"); return -EINVAL; } btd_service_connecting_complete(service, 0); return 0; } static bool ep_remove(const void *data, const void *match_data) { ep_unregister((void *)data); return true; } static int bap_disconnect(struct btd_service *service) { struct bap_data *data = btd_service_get_user_data(service); queue_remove_all(data->snks, ep_remove, NULL, NULL); queue_remove_all(data->srcs, ep_remove, NULL, NULL); bt_bap_detach(data->bap); btd_service_disconnecting_complete(service, 0); return 0; } static int bap_bcast_disconnect(struct btd_service *service) { struct btd_device *device = btd_service_get_device(service); struct bap_data *data; data = queue_find(sessions, match_device, device); if (!data) { error("BAP service not handled by profile"); return -EINVAL; } bt_bap_detach(data->bap); btd_service_disconnecting_complete(service, 0); return 0; } static int bap_adapter_probe(struct btd_profile *p, struct btd_adapter *adapter) { struct btd_gatt_database *database = btd_adapter_get_database(adapter); struct bap_data *data; char addr[18]; ba2str(btd_adapter_get_address(adapter), addr); DBG("%s", addr); if (!btd_kernel_experimental_enabled(ISO_SOCKET_UUID)) { error("BAP requires ISO Socket which is not enabled"); return -ENOTSUP; } data = bap_data_new(NULL); data->bap = bt_bap_new(btd_gatt_database_get_db(database), btd_gatt_database_get_db(database)); if (!data->bap) { error("Unable to create BAP instance"); free(data); return -EINVAL; } bap_data_add(data); if (!bt_bap_attach_broadcast(data->bap)) { error("BAP unable to attach"); return -EINVAL; } data->state_id = bt_bap_state_register(data->bap, bap_state_bcast_src, bap_connecting_bcast, data, NULL); data->pac_id = bt_bap_pac_register(data->bap, pac_added_broadcast, pac_removed_broadcast, data, NULL); bap_data_set_user_data(data, adapter); data->adapter = adapter; return 0; } static void bap_adapter_remove(struct btd_profile *p, struct btd_adapter *adapter) { struct bap_data *data = queue_find(sessions, match_data_bap_data, adapter); char addr[18]; ba2str(btd_adapter_get_address(adapter), addr); DBG("%s", addr); if (!data) { error("BAP service not handled by profile"); return; } bap_data_remove(data); } static struct btd_profile bap_profile = { .name = "bap", .priority = BTD_PROFILE_PRIORITY_MEDIUM, .remote_uuid = PACS_UUID_STR, .device_probe = bap_probe, .device_remove = bap_remove, .accept = bap_accept, .disconnect = bap_disconnect, .adapter_probe = bap_adapter_probe, .adapter_remove = bap_adapter_remove, .auto_connect = true, .experimental = true, }; static struct btd_profile bap_bcast_profile = { .name = "bcaa", .priority = BTD_PROFILE_PRIORITY_MEDIUM, .remote_uuid = BCAAS_UUID_STR, .device_probe = bap_bcast_probe, .device_remove = bap_bcast_remove, .disconnect = bap_bcast_disconnect, .auto_connect = false, .experimental = true, }; static unsigned int bap_id = 0; static int bap_init(void) { int err; err = btd_profile_register(&bap_profile); if (err) return err; err = btd_profile_register(&bap_bcast_profile); if (err) return err; bap_id = bt_bap_register(bap_attached, bap_detached, NULL); return 0; } static void bap_exit(void) { btd_profile_unregister(&bap_profile); bt_bap_unregister(bap_id); } BLUETOOTH_PLUGIN_DEFINE(bap, VERSION, BLUETOOTH_PLUGIN_PRIORITY_DEFAULT, bap_init, bap_exit) bluez-5.82/profiles/audio/PaxHeaders/transport.h0000644000000000000000000000005014766002272017005 xustar0020 atime=1743515578 20 ctime=1743591283 bluez-5.82/profiles/audio/transport.h0000644000000000000000000000234014766002272016465 0ustar00rootroot/* SPDX-License-Identifier: GPL-2.0-or-later */ /* * * BlueZ - Bluetooth protocol stack for Linux * * Copyright (C) 2006-2007 Nokia Corporation * Copyright (C) 2004-2009 Marcel Holtmann * * */ struct media_transport; struct media_transport *media_transport_create(struct btd_device *device, const char *remote_endpoint, uint8_t *configuration, size_t size, void *data, void *stream); void media_transport_destroy(struct media_transport *transport); const char *media_transport_get_path(struct media_transport *transport); void *media_transport_get_stream(struct media_transport *transport); struct btd_device *media_transport_get_dev(struct media_transport *transport); int media_transport_get_volume(struct media_transport *transport, int *volume); void media_transport_update_delay(struct media_transport *transport, uint16_t delay); void media_transport_update_volume(struct media_transport *transport, int volume); void transport_get_properties(struct media_transport *transport, DBusMessageIter *iter); int media_transport_get_device_volume(struct btd_device *dev); void media_transport_update_device_volume(struct btd_device *dev, int volume); bluez-5.82/profiles/audio/PaxHeaders/csip.c0000644000000000000000000000005014536422313015677 xustar0020 atime=1743516588 20 ctime=1743591284 bluez-5.82/profiles/audio/csip.c0000644000000000000000000002273414536422313015370 0ustar00rootroot// SPDX-License-Identifier: GPL-2.0-or-later /* * * BlueZ - Bluetooth protocol stack for Linux * * Copyright (C) 2022 Intel Corporation. All rights reserved. * * */ #ifdef HAVE_CONFIG_H #include #endif #define _GNU_SOURCE #include #include #include #include #include #include #include #include #include #include "gdbus/gdbus.h" #include "lib/bluetooth.h" #include "lib/hci.h" #include "lib/sdp.h" #include "lib/uuid.h" #include "src/dbus-common.h" #include "src/shared/util.h" #include "src/shared/att.h" #include "src/shared/queue.h" #include "src/shared/gatt-db.h" #include "src/shared/gatt-client.h" #include "src/shared/gatt-server.h" #include "src/shared/csip.h" #include "src/shared/crypto.h" #include "btio/btio.h" #include "src/plugin.h" #include "src/adapter.h" #include "src/gatt-database.h" #include "src/device.h" #include "src/profile.h" #include "src/service.h" #include "src/log.h" #include "src/error.h" #include "src/btd.h" #define CSIS_UUID_STR "00001846-0000-1000-8000-00805f9b34fb" struct csis_data { struct btd_adapter *adapter; struct bt_csip *csip; }; struct csip_data { struct btd_adapter *adapter; struct btd_device *device; struct btd_service *service; struct bt_csip *csip; unsigned int ready_id; }; static struct queue *sessions; static struct queue *servers; static void csip_debug(const char *str, void *user_data) { DBG_IDX(0xffff, "%s", str); } static struct csip_data *csip_data_new(struct btd_device *device) { struct csip_data *data; data = new0(struct csip_data, 1); data->device = device; return data; } static void csip_data_add(struct csip_data *data) { DBG("data %p", data); if (queue_find(sessions, NULL, data)) { error("data %p already added", data); return; } bt_csip_set_debug(data->csip, csip_debug, NULL, NULL); if (!sessions) sessions = queue_new(); queue_push_tail(sessions, data); if (data->service) btd_service_set_user_data(data->service, data); } static int csip_disconnect(struct btd_service *service) { struct csip_data *data = btd_service_get_user_data(service); bt_csip_detach(data->csip); btd_service_disconnecting_complete(service, 0); return 0; } static bool match_data(const void *data, const void *match_data) { const struct csip_data *vdata = data; const struct bt_csip *csip = match_data; return vdata->csip == csip; } static void csip_data_free(struct csip_data *data) { if (data->service) { btd_service_set_user_data(data->service, NULL); bt_csip_set_user_data(data->csip, NULL); } bt_csip_ready_unregister(data->csip, data->ready_id); bt_csip_unref(data->csip); free(data); } static void csip_data_remove(struct csip_data *data) { DBG("data %p", data); if (!queue_remove(sessions, data)) return; csip_data_free(data); if (queue_isempty(sessions)) { queue_destroy(sessions, NULL); sessions = NULL; } } static void csip_detached(struct bt_csip *csip, void *user_data) { struct csip_data *data; DBG("%p", csip); data = queue_find(sessions, match_data, csip); if (!data) { error("Unable to find csip session"); return; } /* If there is a service it means there is CSIS thus we can keep * instance allocated. */ if (data->service) return; csip_data_remove(data); } static void csip_attached(struct bt_csip *csip, void *user_data) { struct csip_data *data; struct bt_att *att; struct btd_device *device; DBG("%p", csip); data = queue_find(sessions, match_data, csip); if (data) return; att = bt_csip_get_att(csip); if (!att) return; device = btd_adapter_find_device_by_fd(bt_att_get_fd(att)); if (!device) { error("Unable to find device"); return; } data = csip_data_new(device); data->csip = csip; csip_data_add(data); } static int csip_accept(struct btd_service *service) { struct btd_device *device = btd_service_get_device(service); struct bt_gatt_client *client = btd_device_get_gatt_client(device); struct csip_data *data = btd_service_get_user_data(service); char addr[18]; ba2str(device_get_address(device), addr); DBG("%s", addr); if (!data) { error("CSIP service not handled by profile"); return -EINVAL; } if (!bt_csip_attach(data->csip, client)) { error("CSIP unable to attach"); return -EINVAL; } btd_service_connecting_complete(service, 0); return 0; } static void csip_ready(struct bt_csip *csip, void *user_data) { struct btd_service *service = user_data; struct btd_device *device = btd_service_get_device(service); uint8_t type, size, rank; uint8_t k[16]; DBG("csip %p", csip); if (!bt_csip_get_sirk(csip, &type, k, &size, &rank)) { error("Unable to read SIRK"); return; } btd_device_add_set(device, type == BT_CSIP_SIRK_ENCRYPT ? true : false, k, size, rank); } static int csip_probe(struct btd_service *service) { struct btd_device *device = btd_service_get_device(service); struct btd_adapter *adapter = device_get_adapter(device); struct btd_gatt_database *database = btd_adapter_get_database(adapter); struct csip_data *data = btd_service_get_user_data(service); char addr[18]; ba2str(device_get_address(device), addr); DBG("%s", addr); /* Ignore, if we were probed for this device already */ if (data) { error("Profile probed twice for the same device!"); return -EINVAL; } data = csip_data_new(device); data->service = service; data->csip = bt_csip_new(btd_gatt_database_get_db(database), btd_device_get_gatt_db(device)); if (!data->csip) { error("Unable to create CSIP instance"); free(data); return -EINVAL; } csip_data_add(data); data->ready_id = bt_csip_ready_register(data->csip, csip_ready, service, NULL); bt_csip_set_user_data(data->csip, service); return 0; } static void csip_remove(struct btd_service *service) { struct btd_device *device = btd_service_get_device(service); struct csip_data *data; char addr[18]; ba2str(device_get_address(device), addr); DBG("%s", addr); data = btd_service_get_user_data(service); if (!data) { error("CSIP service not handled by profile"); return; } csip_data_remove(data); } static struct btd_profile csip_profile = { .name = "csip", .priority = BTD_PROFILE_PRIORITY_MEDIUM, .remote_uuid = CSIS_UUID_STR, .device_probe = csip_probe, .device_remove = csip_remove, .accept = csip_accept, .disconnect = csip_disconnect, .experimental = true, }; static bool csis_encrypt(struct bt_att *att, uint8_t val[16]) { struct btd_device *device; struct bt_crypto *crypto; uint8_t ltk[16]; bool ret; device = btd_adapter_find_device_by_fd(bt_att_get_fd(att)); if (!device) { error("Unable to find device"); return false; } if (!btd_device_get_ltk(device, ltk, NULL, NULL)) { error("Unable to get device LTK"); return false; } crypto = bt_crypto_new(); if (!crypto) { error("Failed to open crypto"); return false; } ret = bt_crypto_sef(crypto, ltk, val, val); if (!ret) error("Failed to encrypt SIRK using LTK"); bt_crypto_unref(crypto); return ret; } static void csis_data_add(struct csis_data *data) { DBG("data %p", data); if (queue_find(servers, NULL, data)) { error("data %p already added", data); return; } bt_csip_set_debug(data->csip, csip_debug, NULL, NULL); bt_csip_set_sirk(data->csip, btd_opts.csis.encrypt, btd_opts.csis.sirk, btd_opts.csis.size, btd_opts.csis.rank, csis_encrypt); if (!servers) servers = queue_new(); queue_push_tail(servers, data); } static struct csis_data *csis_data_new(struct btd_adapter *adapter) { struct csis_data *data; data = new0(struct csis_data, 1); data->adapter = adapter; return data; } static int csis_server_probe(struct btd_profile *p, struct btd_adapter *adapter) { struct btd_gatt_database *database = btd_adapter_get_database(adapter); struct csis_data *data; DBG("path %s", adapter_get_path(adapter)); data = csis_data_new(adapter); data->csip = bt_csip_new(btd_gatt_database_get_db(database), NULL); if (!data->csip) { error("Unable to create CSIP instance"); free(data); return -EINVAL; } csis_data_add(data); return 0; } static bool match_csis(const void *data, const void *match_data) { const struct csis_data *csis = data; const struct btd_adapter *adapter = match_data; return csis->adapter == adapter; } static void csis_data_free(struct csis_data *data) { bt_csip_unref(data->csip); free(data); } static void csis_data_remove(struct csis_data *data) { DBG("data %p", data); csis_data_free(data); if (queue_isempty(servers)) { queue_destroy(servers, NULL); servers = NULL; } } static void csis_server_remove(struct btd_profile *p, struct btd_adapter *adapter) { struct csis_data *data; DBG("path %s", adapter_get_path(adapter)); data = queue_remove_if(servers, match_csis, adapter); if (!data) return; csis_data_remove(data); } static struct btd_profile csis_profile = { .name = "csis", .priority = BTD_PROFILE_PRIORITY_MEDIUM, .local_uuid = CSIS_UUID_STR, .adapter_probe = csis_server_probe, .adapter_remove = csis_server_remove, .experimental = true, }; static unsigned int csip_id; static int csip_init(void) { int err; err = btd_profile_register(&csis_profile); if (err) return err; err = btd_profile_register(&csip_profile); if (err) return err; csip_id = bt_csip_register(csip_attached, csip_detached, NULL); return 0; } static void csip_exit(void) { btd_profile_unregister(&csis_profile); btd_profile_unregister(&csip_profile); bt_csip_unregister(csip_id); } BLUETOOTH_PLUGIN_DEFINE(csip, VERSION, BLUETOOTH_PLUGIN_PRIORITY_DEFAULT, csip_init, csip_exit) bluez-5.82/profiles/audio/PaxHeaders/control.c0000644000000000000000000000005014015011623016407 xustar0020 atime=1743516535 20 ctime=1743591284 bluez-5.82/profiles/audio/control.c0000644000000000000000000002120614015011623016071 0ustar00rootroot// SPDX-License-Identifier: GPL-2.0-or-later /* * * BlueZ - Bluetooth protocol stack for Linux * * Copyright (C) 2006-2010 Nokia Corporation * Copyright (C) 2004-2010 Marcel Holtmann * Copyright (C) 2011 Texas Instruments, Inc. * * */ #ifdef HAVE_CONFIG_H #include #endif #include #include #include #include #include #include #include #include #include #include #include #include "lib/bluetooth.h" #include "lib/sdp.h" #include "lib/sdp_lib.h" #include "lib/uuid.h" #include "gdbus/gdbus.h" #include "src/adapter.h" #include "src/device.h" #include "src/profile.h" #include "src/service.h" #include "src/log.h" #include "src/error.h" #include "src/sdpd.h" #include "src/uuid-helper.h" #include "src/dbus-common.h" #include "avctp.h" #include "control.h" #include "player.h" static GSList *devices = NULL; struct control { struct btd_device *dev; struct avctp *session; struct btd_service *target; struct btd_service *remote; unsigned int avctp_id; const char *player; }; static void state_changed(struct btd_device *dev, avctp_state_t old_state, avctp_state_t new_state, int err, void *user_data) { struct control *control = user_data; DBusConnection *conn = btd_get_dbus_connection(); const char *path = device_get_path(dev); switch (new_state) { case AVCTP_STATE_DISCONNECTED: control->session = NULL; control->player = NULL; g_dbus_emit_property_changed(conn, path, AUDIO_CONTROL_INTERFACE, "Connected"); g_dbus_emit_property_changed(conn, path, AUDIO_CONTROL_INTERFACE, "Player"); break; case AVCTP_STATE_CONNECTING: if (control->session) break; control->session = avctp_get(dev); break; case AVCTP_STATE_CONNECTED: g_dbus_emit_property_changed(conn, path, AUDIO_CONTROL_INTERFACE, "Connected"); break; case AVCTP_STATE_BROWSING_CONNECTING: case AVCTP_STATE_BROWSING_CONNECTED: default: return; } } int control_connect(struct btd_service *service) { struct control *control = btd_service_get_user_data(service); if (control->session) return -EALREADY; control->session = avctp_connect(control->dev); if (!control->session) return -EIO; return 0; } int control_disconnect(struct btd_service *service) { struct control *control = btd_service_get_user_data(service); if (!control || !control->session) return -ENOTCONN; avctp_disconnect(control->session); return 0; } static DBusMessage *key_pressed(DBusConnection *conn, DBusMessage *msg, uint8_t op, bool hold, void *data) { struct control *control = data; int err; if (!control->session) return btd_error_not_connected(msg); if (!control->target) return btd_error_not_supported(msg); err = avctp_send_passthrough(control->session, op, hold); if (err < 0) return btd_error_failed(msg, strerror(-err)); return dbus_message_new_method_return(msg); } static DBusMessage *control_volume_up(DBusConnection *conn, DBusMessage *msg, void *data) { return key_pressed(conn, msg, AVC_VOLUME_UP, false, data); } static DBusMessage *control_volume_down(DBusConnection *conn, DBusMessage *msg, void *data) { return key_pressed(conn, msg, AVC_VOLUME_DOWN, false, data); } static DBusMessage *control_play(DBusConnection *conn, DBusMessage *msg, void *data) { return key_pressed(conn, msg, AVC_PLAY, false, data); } static DBusMessage *control_pause(DBusConnection *conn, DBusMessage *msg, void *data) { return key_pressed(conn, msg, AVC_PAUSE, false, data); } static DBusMessage *control_stop(DBusConnection *conn, DBusMessage *msg, void *data) { return key_pressed(conn, msg, AVC_STOP, false, data); } static DBusMessage *control_next(DBusConnection *conn, DBusMessage *msg, void *data) { return key_pressed(conn, msg, AVC_FORWARD, false, data); } static DBusMessage *control_previous(DBusConnection *conn, DBusMessage *msg, void *data) { return key_pressed(conn, msg, AVC_BACKWARD, false, data); } static DBusMessage *control_fast_forward(DBusConnection *conn, DBusMessage *msg, void *data) { return key_pressed(conn, msg, AVC_FAST_FORWARD, true, data); } static DBusMessage *control_rewind(DBusConnection *conn, DBusMessage *msg, void *data) { return key_pressed(conn, msg, AVC_REWIND, true, data); } static gboolean control_property_get_connected( const GDBusPropertyTable *property, DBusMessageIter *iter, void *data) { struct control *control = data; dbus_bool_t value = (control->session != NULL); dbus_message_iter_append_basic(iter, DBUS_TYPE_BOOLEAN, &value); return TRUE; } static gboolean control_player_exists(const GDBusPropertyTable *property, void *data) { struct control *control = data; return control->player != NULL; } static gboolean control_get_player(const GDBusPropertyTable *property, DBusMessageIter *iter, void *data) { struct control *control = data; if (!control->player) return FALSE; dbus_message_iter_append_basic(iter, DBUS_TYPE_OBJECT_PATH, &control->player); return TRUE; } static const GDBusMethodTable control_methods[] = { { GDBUS_DEPRECATED_METHOD("Play", NULL, NULL, control_play) }, { GDBUS_DEPRECATED_METHOD("Pause", NULL, NULL, control_pause) }, { GDBUS_DEPRECATED_METHOD("Stop", NULL, NULL, control_stop) }, { GDBUS_DEPRECATED_METHOD("Next", NULL, NULL, control_next) }, { GDBUS_DEPRECATED_METHOD("Previous", NULL, NULL, control_previous) }, { GDBUS_DEPRECATED_METHOD("VolumeUp", NULL, NULL, control_volume_up) }, { GDBUS_DEPRECATED_METHOD("VolumeDown", NULL, NULL, control_volume_down) }, { GDBUS_DEPRECATED_METHOD("FastForward", NULL, NULL, control_fast_forward) }, { GDBUS_DEPRECATED_METHOD("Rewind", NULL, NULL, control_rewind) }, { } }; static const GDBusPropertyTable control_properties[] = { { "Connected", "b", control_property_get_connected }, { "Player", "o", control_get_player, NULL, control_player_exists }, { } }; static void path_unregister(void *data) { struct control *control = data; DBG("Unregistered interface %s on path %s", AUDIO_CONTROL_INTERFACE, device_get_path(control->dev)); if (control->session) avctp_disconnect(control->session); avctp_remove_state_cb(control->avctp_id); if (control->target) { btd_service_set_user_data(control->target, NULL); btd_service_unref(control->target); } if (control->remote) { btd_service_set_user_data(control->remote, NULL); btd_service_unref(control->remote); } devices = g_slist_remove(devices, control); g_free(control); } void control_unregister(struct btd_service *service) { struct btd_device *dev = btd_service_get_device(service); g_dbus_unregister_interface(btd_get_dbus_connection(), device_get_path(dev), AUDIO_CONTROL_INTERFACE); } static struct control *find_control(struct btd_device *dev) { GSList *l; for (l = devices; l; l = l->next) { struct control *control = l->data; if (control->dev == dev) return control; } return NULL; } static struct control *control_init(struct btd_service *service) { struct control *control; struct btd_device *dev = btd_service_get_device(service); control = find_control(dev); if (control != NULL) return control; control = g_new0(struct control, 1); if (!g_dbus_register_interface(btd_get_dbus_connection(), device_get_path(dev), AUDIO_CONTROL_INTERFACE, control_methods, NULL, control_properties, control, path_unregister)) { g_free(control); return NULL; } DBG("Registered interface %s on path %s", AUDIO_CONTROL_INTERFACE, device_get_path(dev)); control->dev = dev; control->avctp_id = avctp_add_state_cb(dev, state_changed, control); devices = g_slist_prepend(devices, control); return control; } int control_init_target(struct btd_service *service) { struct control *control; control = control_init(service); if (control == NULL) return -EINVAL; control->target = btd_service_ref(service); btd_service_set_user_data(service, control); return 0; } int control_init_remote(struct btd_service *service) { struct control *control; control = control_init(service); if (control == NULL) return -EINVAL; control->remote = btd_service_ref(service); btd_service_set_user_data(service, control); return 0; } int control_set_player(struct btd_service *service, const char *path) { struct control *control = btd_service_get_user_data(service); if (!control->session) return -ENOTCONN; if (g_strcmp0(control->player, path) == 0) return -EALREADY; control->player = path; g_dbus_emit_property_changed(btd_get_dbus_connection(), device_get_path(control->dev), AUDIO_CONTROL_INTERFACE, "Player"); return 0; } bluez-5.82/profiles/audio/PaxHeaders/avrcp.h0000644000000000000000000000005014711225434016061 xustar0020 atime=1743516503 20 ctime=1743591284 bluez-5.82/profiles/audio/avrcp.h0000644000000000000000000000712314711225434015545 0ustar00rootroot/* SPDX-License-Identifier: GPL-2.0-or-later */ /* * * BlueZ - Bluetooth protocol stack for Linux * * Copyright (C) 2006-2010 Nokia Corporation * Copyright (C) 2004-2010 Marcel Holtmann * * */ /* player attributes */ #define AVRCP_ATTRIBUTE_ILEGAL 0x00 #define AVRCP_ATTRIBUTE_EQUALIZER 0x01 #define AVRCP_ATTRIBUTE_REPEAT_MODE 0x02 #define AVRCP_ATTRIBUTE_SHUFFLE 0x03 #define AVRCP_ATTRIBUTE_SCAN 0x04 #define AVRCP_ATTRIBUTE_LAST AVRCP_ATTRIBUTE_SCAN /* equalizer values */ #define AVRCP_EQUALIZER_OFF 0x01 #define AVRCP_EQUALIZER_ON 0x02 /* repeat mode values */ #define AVRCP_REPEAT_MODE_OFF 0x01 #define AVRCP_REPEAT_MODE_SINGLE 0x02 #define AVRCP_REPEAT_MODE_ALL 0x03 #define AVRCP_REPEAT_MODE_GROUP 0x04 /* shuffle values */ #define AVRCP_SHUFFLE_OFF 0x01 #define AVRCP_SHUFFLE_ALL 0x02 #define AVRCP_SHUFFLE_GROUP 0x03 /* scan values */ #define AVRCP_SCAN_OFF 0x01 #define AVRCP_SCAN_ALL 0x02 #define AVRCP_SCAN_GROUP 0x03 /* media attributes */ #define AVRCP_MEDIA_ATTRIBUTE_ILLEGAL 0x00 #define AVRCP_MEDIA_ATTRIBUTE_TITLE 0x01 #define AVRCP_MEDIA_ATTRIBUTE_ARTIST 0x02 #define AVRCP_MEDIA_ATTRIBUTE_ALBUM 0x03 #define AVRCP_MEDIA_ATTRIBUTE_TRACK 0x04 #define AVRCP_MEDIA_ATTRIBUTE_N_TRACKS 0x05 #define AVRCP_MEDIA_ATTRIBUTE_GENRE 0x06 #define AVRCP_MEDIA_ATTRIBUTE_DURATION 0x07 #define AVRCP_MEDIA_ATTRIBUTE_IMG_HANDLE 0x08 #define AVRCP_MEDIA_ATTRIBUTE_LAST AVRCP_MEDIA_ATTRIBUTE_IMG_HANDLE /* play status */ #define AVRCP_PLAY_STATUS_STOPPED 0x00 #define AVRCP_PLAY_STATUS_PLAYING 0x01 #define AVRCP_PLAY_STATUS_PAUSED 0x02 #define AVRCP_PLAY_STATUS_FWD_SEEK 0x03 #define AVRCP_PLAY_STATUS_REV_SEEK 0x04 #define AVRCP_PLAY_STATUS_ERROR 0xFF /* Notification events */ #define AVRCP_EVENT_STATUS_CHANGED 0x01 #define AVRCP_EVENT_TRACK_CHANGED 0x02 #define AVRCP_EVENT_TRACK_REACHED_END 0x03 #define AVRCP_EVENT_TRACK_REACHED_START 0x04 #define AVRCP_EVENT_PLAYBACK_POS_CHANGED 0x05 #define AVRCP_EVENT_SETTINGS_CHANGED 0x08 #define AVRCP_EVENT_NOW_PLAYING_CHANGED 0x09 #define AVRCP_EVENT_AVAILABLE_PLAYERS_CHANGED 0x0a #define AVRCP_EVENT_ADDRESSED_PLAYER_CHANGED 0x0b #define AVRCP_EVENT_UIDS_CHANGED 0x0c #define AVRCP_EVENT_VOLUME_CHANGED 0x0d #define AVRCP_EVENT_LAST AVRCP_EVENT_VOLUME_CHANGED struct avrcp_player_cb { GList *(*list_settings)(void *user_data); const char *(*get_setting)(const char *key, void *user_data); int (*set_setting)(const char *key, const char *value, void *user_data); uint64_t (*get_uid)(void *user_data); const char *(*get_metadata)(const char *key, void *user_data); GList *(*list_metadata)(void *user_data); const char *(*get_status)(void *user_data); uint32_t (*get_position)(void *user_data); uint32_t (*get_duration)(void *user_data); const char *(*get_name)(void *user_data); void (*set_volume)(int8_t volume, struct btd_device *dev, void *user_data); bool (*play)(void *user_data); bool (*stop)(void *user_data); bool (*pause)(void *user_data); bool (*next)(void *user_data); bool (*previous)(void *user_data); }; int avrcp_set_volume(struct btd_device *dev, int8_t volume, bool notify); struct avrcp_player *avrcp_register_player(struct btd_adapter *adapter, struct avrcp_player_cb *cb, void *user_data, GDestroyNotify destroy); void avrcp_unregister_player(struct avrcp_player *player); void avrcp_player_event(struct avrcp_player *player, uint8_t id, const void *data); size_t avrcp_handle_vendor_reject(uint8_t *code, uint8_t *operands); size_t avrcp_browsing_general_reject(uint8_t *operands); struct avrcp_player *avrcp_get_target_player_by_device(struct btd_device *dev); bluez-5.82/profiles/audio/PaxHeaders/asha.h0000644000000000000000000000005014643061455015667 xustar0020 atime=1743516509 20 ctime=1743591284 bluez-5.82/profiles/audio/asha.h0000644000000000000000000000222414643061455015350 0ustar00rootroot// SPDX-License-Identifier: GPL-2.0-or-later /* * * BlueZ - Bluetooth protocol stack for Linux * * Copyright (C) 2024 Asymptotic Inc. * * Author: Arun Raghavan * * */ #include #include #include "src/shared/asha.h" struct bt_asha_device; unsigned int bt_asha_device_start(struct bt_asha_device *asha_dev, bt_asha_cb_t cb, void *user_data); unsigned int bt_asha_device_stop(struct bt_asha_device *asha_dev, bt_asha_cb_t cb, void *user_data); void bt_asha_device_state_reset(struct bt_asha_device *asha_dev); unsigned int bt_asha_device_device_get_resume_id( struct bt_asha_device *asha_dev); uint16_t bt_asha_device_get_render_delay(struct bt_asha_device *asha_dev); enum bt_asha_state_t bt_asha_device_get_state( struct bt_asha_device *asha_dev); int bt_asha_device_get_fd(struct bt_asha_device *asha_dev); uint16_t bt_asha_device_get_omtu(struct bt_asha_device *asha_dev); uint16_t bt_asha_device_get_imtu(struct bt_asha_device *asha_dev); int8_t bt_asha_device_get_volume(struct bt_asha_device *asha_dev); bool bt_asha_device_set_volume(struct bt_asha_device *asha_dev, int8_t volume); bluez-5.82/profiles/audio/PaxHeaders/avctp.c0000644000000000000000000000005014572354773016074 xustar0020 atime=1743516537 20 ctime=1743591284 bluez-5.82/profiles/audio/avctp.c0000644000000000000000000014162114572354773015562 0ustar00rootroot// SPDX-License-Identifier: GPL-2.0-or-later /* * * BlueZ - Bluetooth protocol stack for Linux * * Copyright (C) 2006-2010 Nokia Corporation * Copyright (C) 2004-2010 Marcel Holtmann * Copyright (C) 2011 Texas Instruments, Inc. * * */ #ifdef HAVE_CONFIG_H #include #endif #include #include #include #include #include #include #include #include #include #include #include #include #include #include "lib/bluetooth.h" #include "lib/sdp.h" #include "lib/l2cap.h" #include "lib/uuid.h" #include "btio/btio.h" #include "src/adapter.h" #include "src/device.h" #include "src/log.h" #include "src/error.h" #include "src/shared/timeout.h" #include "src/shared/util.h" #include "avctp.h" #include "avrcp.h" /* AV/C Panel 1.23, page 76: * command with the pressed value is valid for two seconds */ #define AVC_PRESS_TIMEOUT 2 /* We need to send hold event before AVC_PRESS time runs out */ #define AVC_HOLD_TIMEOUT 1 #define CONTROL_TIMEOUT 10 #define BROWSING_TIMEOUT 10 #define PASSTHROUGH_QUEUE 0 #define CONTROL_QUEUE 1 #define QUIRK_NO_RELEASE 1 << 0 /* Message types */ #define AVCTP_COMMAND 0 #define AVCTP_RESPONSE 1 /* Packet types */ #define AVCTP_PACKET_SINGLE 0 #define AVCTP_PACKET_START 1 #define AVCTP_PACKET_CONTINUE 2 #define AVCTP_PACKET_END 3 #if __BYTE_ORDER == __LITTLE_ENDIAN struct avctp_header { uint8_t ipid:1; uint8_t cr:1; uint8_t packet_type:2; uint8_t transaction:4; uint16_t pid; } __attribute__ ((packed)); #define AVCTP_HEADER_LENGTH 3 struct avc_header { uint8_t code:4; uint8_t _hdr0:4; uint8_t subunit_id:3; uint8_t subunit_type:5; uint8_t opcode; } __attribute__ ((packed)); #elif __BYTE_ORDER == __BIG_ENDIAN struct avctp_header { uint8_t transaction:4; uint8_t packet_type:2; uint8_t cr:1; uint8_t ipid:1; uint16_t pid; } __attribute__ ((packed)); #define AVCTP_HEADER_LENGTH 3 struct avc_header { uint8_t _hdr0:4; uint8_t code:4; uint8_t subunit_type:5; uint8_t subunit_id:3; uint8_t opcode; } __attribute__ ((packed)); #else #error "Unknown byte order" #endif struct avctp_state_callback { avctp_state_cb cb; struct btd_device *dev; unsigned int id; void *user_data; }; struct avctp_server { struct btd_adapter *adapter; GIOChannel *control_io; GIOChannel *browsing_io; GSList *sessions; }; struct avctp_control_req { struct avctp_pending_req *p; uint8_t code; uint8_t subunit; uint8_t op; uint8_t *operands; uint16_t operand_count; avctp_rsp_cb func; void *user_data; }; struct avctp_browsing_req { struct avctp_pending_req *p; uint8_t *operands; uint16_t operand_count; avctp_browsing_rsp_cb func; void *user_data; }; typedef int (*avctp_process_cb) (void *data); struct avctp_pending_req { struct avctp_queue *queue; uint8_t transaction; unsigned int timeout; bool retry; int err; avctp_process_cb process; void *data; GDestroyNotify destroy; }; struct avctp_queue { struct avctp_channel *chan; struct avctp_pending_req *p; GQueue *queue; guint process_id; }; struct avctp_channel { struct avctp *session; GIOChannel *io; uint8_t transaction; guint watch; uint16_t imtu; uint16_t omtu; uint8_t *buffer; GSList *handlers; GSList *queues; GSList *processed; GDestroyNotify destroy; }; struct key_pressed { uint16_t op; unsigned int timer; bool hold; }; struct avctp { struct avctp_server *server; struct btd_device *device; avctp_state_t state; int uinput; guint auth_id; unsigned int passthrough_id; unsigned int unit_id; unsigned int subunit_id; struct avctp_channel *control; struct avctp_channel *browsing; struct avctp_passthrough_handler *handler; uint8_t key_quirks[256]; struct key_pressed key; bool initiator; }; struct avctp_passthrough_handler { avctp_passthrough_cb cb; void *user_data; unsigned int id; }; struct avctp_pdu_handler { uint8_t opcode; avctp_control_pdu_cb cb; void *user_data; unsigned int id; }; struct avctp_browsing_pdu_handler { avctp_browsing_pdu_cb cb; void *user_data; unsigned int id; GDestroyNotify destroy; }; static const struct { const char *name; uint8_t avc; uint16_t uinput; } key_map[] = { { "SELECT", AVC_SELECT, KEY_SELECT }, { "UP", AVC_UP, KEY_UP }, { "DOWN", AVC_DOWN, KEY_DOWN }, { "LEFT", AVC_LEFT, KEY_LEFT }, { "RIGHT", AVC_RIGHT, KEY_RIGHT }, { "ROOT MENU", AVC_ROOT_MENU, KEY_MENU }, { "CONTENTS MENU", AVC_CONTENTS_MENU, KEY_PROGRAM }, { "FAVORITE MENU", AVC_FAVORITE_MENU, KEY_FAVORITES }, { "EXIT", AVC_EXIT, KEY_EXIT }, { "ON DEMAND MENU", AVC_ON_DEMAND_MENU, KEY_MENU }, { "APPS MENU", AVC_APPS_MENU, KEY_MENU }, { "0", AVC_0, KEY_0 }, { "1", AVC_1, KEY_1 }, { "2", AVC_2, KEY_2 }, { "3", AVC_3, KEY_3 }, { "4", AVC_4, KEY_4 }, { "5", AVC_5, KEY_5 }, { "6", AVC_6, KEY_6 }, { "7", AVC_7, KEY_7 }, { "8", AVC_8, KEY_8 }, { "9", AVC_9, KEY_9 }, { "DOT", AVC_DOT, KEY_DOT }, { "ENTER", AVC_ENTER, KEY_ENTER }, { "CHANNEL UP", AVC_CHANNEL_UP, KEY_CHANNELUP }, { "CHANNEL DOWN", AVC_CHANNEL_DOWN, KEY_CHANNELDOWN }, { "CHANNEL PREVIOUS", AVC_CHANNEL_PREVIOUS, KEY_LAST }, { "INPUT SELECT", AVC_INPUT_SELECT, KEY_CONFIG }, { "INFO", AVC_INFO, KEY_INFO }, { "HELP", AVC_HELP, KEY_HELP }, { "POWER", AVC_POWER, KEY_POWER2 }, { "VOLUME UP", AVC_VOLUME_UP, KEY_VOLUMEUP }, { "VOLUME DOWN", AVC_VOLUME_DOWN, KEY_VOLUMEDOWN }, { "MUTE", AVC_MUTE, KEY_MUTE }, { "PLAY", AVC_PLAY, KEY_PLAYCD }, { "STOP", AVC_STOP, KEY_STOPCD }, { "PAUSE", AVC_PAUSE, KEY_PAUSECD }, { "FORWARD", AVC_FORWARD, KEY_NEXTSONG }, { "BACKWARD", AVC_BACKWARD, KEY_PREVIOUSSONG }, { "RECORD", AVC_RECORD, KEY_RECORD }, { "REWIND", AVC_REWIND, KEY_REWIND }, { "FAST FORWARD", AVC_FAST_FORWARD, KEY_FASTFORWARD }, { "LIST", AVC_LIST, KEY_LIST }, { "F1", AVC_F1, KEY_F1 }, { "F2", AVC_F2, KEY_F2 }, { "F3", AVC_F3, KEY_F3 }, { "F4", AVC_F4, KEY_F4 }, { "F5", AVC_F5, KEY_F5 }, { "F6", AVC_F6, KEY_F6 }, { "F7", AVC_F7, KEY_F7 }, { "F8", AVC_F8, KEY_F8 }, { "F9", AVC_F9, KEY_F9 }, { "RED", AVC_RED, KEY_RED }, { "GREEN", AVC_GREEN, KEY_GREEN }, { "BLUE", AVC_BLUE, KEY_BLUE }, { "YELLOW", AVC_YELLOW, KEY_YELLOW }, { NULL } }; static GSList *callbacks = NULL; static GSList *servers = NULL; static void auth_cb(DBusError *derr, void *user_data); static gboolean process_queue(gpointer user_data); static gboolean avctp_passthrough_rsp(struct avctp *session, uint8_t code, uint8_t subunit, uint8_t transaction, uint8_t *operands, size_t operand_count, void *user_data); static int send_event(int fd, uint16_t type, uint16_t code, int32_t value) { struct input_event event; memset(&event, 0, sizeof(event)); event.type = type; event.code = code; event.value = value; return write(fd, &event, sizeof(event)); } static void send_key(int fd, uint16_t key, int pressed) { if (fd < 0) return; send_event(fd, EV_KEY, key, pressed); send_event(fd, EV_SYN, SYN_REPORT, 0); } static bool auto_release(gpointer user_data) { struct avctp *session = user_data; session->key.timer = 0; DBG("AV/C: key press timeout"); send_key(session->uinput, session->key.op, 0); return FALSE; } static void handle_press(struct avctp *session, uint16_t op) { if (session->key.timer > 0) { g_source_remove(session->key.timer); /* Only auto release if keys are different */ if (session->key.op == op) goto done; send_key(session->uinput, session->key.op, 0); } session->key.op = op; send_key(session->uinput, op, 1); done: session->key.timer = timeout_add_seconds(AVC_PRESS_TIMEOUT, auto_release, session, NULL); } static void handle_release(struct avctp *session, uint16_t op) { if (session->key.timer > 0) { timeout_remove(session->key.timer); session->key.timer = 0; } send_key(session->uinput, op, 0); } static size_t handle_panel_passthrough(struct avctp *session, uint8_t transaction, uint8_t *code, uint8_t *subunit, uint8_t *operands, size_t operand_count, void *user_data) { struct avctp_passthrough_handler *handler = session->handler; const char *status; int pressed, i; if (*code != AVC_CTYPE_CONTROL || *subunit != AVC_SUBUNIT_PANEL) { *code = AVC_CTYPE_REJECTED; return operand_count; } if (operand_count == 0) goto done; if (operands[0] & 0x80) { status = "released"; pressed = 0; } else { status = "pressed"; pressed = 1; } if (session->key.timer == 0 && handler != NULL) { if (handler->cb(session, operands[0] & 0x7F, pressed, handler->user_data)) goto done; } for (i = 0; key_map[i].name != NULL; i++) { uint8_t key_quirks; if ((operands[0] & 0x7F) != key_map[i].avc) continue; DBG("AV/C: %s %s", key_map[i].name, status); key_quirks = session->key_quirks[key_map[i].avc]; if (key_quirks & QUIRK_NO_RELEASE) { if (!pressed) { DBG("AV/C: Ignoring release"); break; } DBG("AV/C: treating key press as press + release"); send_key(session->uinput, key_map[i].uinput, 1); send_key(session->uinput, key_map[i].uinput, 0); break; } if (pressed) handle_press(session, key_map[i].uinput); else handle_release(session, key_map[i].uinput); break; } if (key_map[i].name == NULL) { DBG("AV/C: unknown button 0x%02X %s", operands[0] & 0x7F, status); *code = AVC_CTYPE_NOT_IMPLEMENTED; return operand_count; } done: *code = AVC_CTYPE_ACCEPTED; return operand_count; } static size_t handle_unit_info(struct avctp *session, uint8_t transaction, uint8_t *code, uint8_t *subunit, uint8_t *operands, size_t operand_count, void *user_data) { if (*code != AVC_CTYPE_STATUS) { *code = AVC_CTYPE_REJECTED; return 0; } *code = AVC_CTYPE_STABLE; /* The first operand should be 0x07 for the UNITINFO response. * Neither AVRCP (section 22.1, page 117) nor AVC Digital * Interface Command Set (section 9.2.1, page 45) specs * explain this value but both use it */ if (operand_count >= 1) operands[0] = 0x07; if (operand_count >= 2) operands[1] = AVC_SUBUNIT_PANEL << 3; DBG("reply to AVC_OP_UNITINFO"); return operand_count; } static size_t handle_subunit_info(struct avctp *session, uint8_t transaction, uint8_t *code, uint8_t *subunit, uint8_t *operands, size_t operand_count, void *user_data) { if (*code != AVC_CTYPE_STATUS) { *code = AVC_CTYPE_REJECTED; return 0; } *code = AVC_CTYPE_STABLE; /* The first operand should be 0x07 for the UNITINFO response. * Neither AVRCP (section 22.1, page 117) nor AVC Digital * Interface Command Set (section 9.2.1, page 45) specs * explain this value but both use it */ if (operand_count >= 2) operands[1] = AVC_SUBUNIT_PANEL << 3; DBG("reply to AVC_OP_SUBUNITINFO"); return operand_count; } static struct avctp_pdu_handler *find_handler(GSList *list, uint8_t opcode) { for (; list; list = list->next) { struct avctp_pdu_handler *handler = list->data; if (handler->opcode == opcode) return handler; } return NULL; } static void pending_destroy(gpointer data, gpointer user_data) { struct avctp_pending_req *req = data; if (req->destroy) req->destroy(req->data); if (req->timeout > 0) timeout_remove(req->timeout); g_free(req); } static void avctp_queue_destroy(void *data) { struct avctp_queue *queue = data; if (queue->process_id > 0) g_source_remove(queue->process_id); if (queue->p) pending_destroy(queue->p, NULL); g_queue_foreach(queue->queue, pending_destroy, NULL); g_queue_free(queue->queue); g_free(queue); } static void avctp_channel_destroy(struct avctp_channel *chan) { g_io_channel_shutdown(chan->io, TRUE, NULL); g_io_channel_unref(chan->io); if (chan->watch) g_source_remove(chan->watch); if (chan->destroy) chan->destroy(chan); g_free(chan->buffer); g_slist_foreach(chan->processed, pending_destroy, NULL); g_slist_free(chan->processed); g_slist_free_full(chan->queues, avctp_queue_destroy); g_slist_free_full(chan->handlers, g_free); g_free(chan); } static void avctp_disconnected(struct avctp *session) { struct avctp_server *server; if (!session) return; if (session->browsing) avctp_channel_destroy(session->browsing); if (session->control) avctp_channel_destroy(session->control); if (session->auth_id != 0) { btd_cancel_authorization(session->auth_id); session->auth_id = 0; } if (session->key.timer > 0) timeout_remove(session->key.timer); if (session->uinput >= 0) { char address[18]; ba2str(device_get_address(session->device), address); DBG("AVCTP: closing uinput for %s", address); ioctl(session->uinput, UI_DEV_DESTROY); close(session->uinput); session->uinput = -1; } server = session->server; server->sessions = g_slist_remove(server->sessions, session); btd_device_unref(session->device); g_free(session); } static void avctp_set_state(struct avctp *session, avctp_state_t new_state, int err) { GSList *l; avctp_state_t old_state = session->state; session->state = new_state; for (l = callbacks; l != NULL; l = l->next) { struct avctp_state_callback *cb = l->data; if (cb->dev && cb->dev != session->device) continue; cb->cb(session->device, old_state, new_state, err, cb->user_data); } switch (new_state) { case AVCTP_STATE_DISCONNECTED: DBG("AVCTP Disconnected"); avctp_disconnected(session); break; case AVCTP_STATE_CONNECTING: DBG("AVCTP Connecting"); break; case AVCTP_STATE_CONNECTED: DBG("AVCTP Connected"); break; case AVCTP_STATE_BROWSING_CONNECTING: DBG("AVCTP Browsing Connecting"); break; case AVCTP_STATE_BROWSING_CONNECTED: DBG("AVCTP Browsing Connected"); break; default: error("Invalid AVCTP state %d", new_state); return; } } static uint8_t chan_get_transaction(struct avctp_channel *chan) { GSList *l, *tmp; uint8_t transaction; if (!chan->processed) goto done; tmp = g_slist_copy(chan->processed); /* Find first unused transaction id */ for (l = tmp; l; l = g_slist_next(l)) { struct avctp_pending_req *req = l->data; if (req->transaction == chan->transaction) { chan->transaction++; chan->transaction %= 16; tmp = g_slist_delete_link(tmp, l); l = tmp; } } g_slist_free(tmp); done: transaction = chan->transaction; chan->transaction++; chan->transaction %= 16; return transaction; } static int avctp_send(struct avctp_channel *control, uint8_t transaction, uint8_t cr, uint8_t code, uint8_t subunit, uint8_t opcode, uint8_t *operands, size_t operand_count) { struct avctp_header *avctp; struct avc_header *avc; struct msghdr msg; struct iovec iov[2]; int sk, err = 0; iov[0].iov_base = control->buffer; iov[0].iov_len = sizeof(*avctp) + sizeof(*avc); iov[1].iov_base = operands; iov[1].iov_len = operand_count; if (control->omtu < (iov[0].iov_len + iov[1].iov_len)) return -EOVERFLOW; sk = g_io_channel_unix_get_fd(control->io); memset(control->buffer, 0, iov[0].iov_len); avctp = (void *) control->buffer; avc = (void *) avctp + sizeof(*avctp); if (transaction > 16) transaction = chan_get_transaction(control); avctp->transaction = transaction; avctp->packet_type = AVCTP_PACKET_SINGLE; avctp->cr = cr; avctp->pid = htons(AV_REMOTE_SVCLASS_ID); avc->code = code; avc->subunit_type = subunit; avc->opcode = opcode; memset(&msg, 0, sizeof(msg)); msg.msg_iov = iov; msg.msg_iovlen = 2; if (sendmsg(sk, &msg, 0) < 0) err = -errno; return err; } static int avctp_browsing_send(struct avctp_queue *queue, uint8_t transaction, uint8_t cr, uint8_t *operands, size_t operand_count) { struct avctp_channel *browsing = queue->chan; struct avctp_header *avctp; struct msghdr msg; struct iovec iov[2]; int sk, err = 0; iov[0].iov_base = browsing->buffer; iov[0].iov_len = sizeof(*avctp); iov[1].iov_base = operands; iov[1].iov_len = operand_count; if (browsing->omtu < (iov[0].iov_len + iov[1].iov_len)) return -EOVERFLOW; sk = g_io_channel_unix_get_fd(browsing->io); memset(browsing->buffer, 0, iov[0].iov_len); avctp = (void *) browsing->buffer; avctp->transaction = transaction; avctp->packet_type = AVCTP_PACKET_SINGLE; avctp->cr = cr; avctp->pid = htons(AV_REMOTE_SVCLASS_ID); memset(&msg, 0, sizeof(msg)); msg.msg_iov = iov; msg.msg_iovlen = 2; if (sendmsg(sk, &msg, 0) < 0) err = -errno; return err; } static void control_req_destroy(void *data) { struct avctp_control_req *req = data; struct avctp_pending_req *p = req->p; struct avctp *session = p->queue->chan->session; if (p->err == 0 || req->func == NULL) goto done; req->func(session, AVC_CTYPE_REJECTED, req->subunit, p->transaction, NULL, 0, req->user_data); done: free(req->operands); g_free(req); } static void browsing_req_destroy(void *data) { struct avctp_browsing_req *req = data; struct avctp_pending_req *p = req->p; struct avctp *session = p->queue->chan->session; if (p->err == 0 || req->func == NULL) goto done; req->func(session, NULL, 0, req->user_data); done: free(req->operands); g_free(req); } static bool req_timeout(gpointer user_data) { struct avctp_queue *queue = user_data; struct avctp_pending_req *p = queue->p; DBG("transaction %u retry %s", p->transaction, p->retry ? "true" : "false"); p->timeout = 0; if (p->retry) { p->process(p->data); return FALSE; } p->err = -ETIMEDOUT; pending_destroy(p, NULL); queue->p = NULL; if (queue->process_id == 0) queue->process_id = g_idle_add(process_queue, queue); return FALSE; } static int process_passthrough(void *data) { struct avctp_control_req *req = data; struct avctp_pending_req *p = req->p; int ret; ret = avctp_send(p->queue->chan, p->transaction, AVCTP_COMMAND, req->code, req->subunit, req->op, req->operands, req->operand_count); if (ret < 0) return ret; p->timeout = timeout_add_seconds(AVC_PRESS_TIMEOUT, req_timeout, p->queue, NULL); return 0; } static int process_control(void *data) { struct avctp_control_req *req = data; struct avctp_pending_req *p = req->p; int ret; ret = avctp_send(p->queue->chan, p->transaction, AVCTP_COMMAND, req->code, req->subunit, req->op, req->operands, req->operand_count); if (ret < 0) return ret; p->retry = !p->retry; p->timeout = timeout_add_seconds(CONTROL_TIMEOUT, req_timeout, p->queue, NULL); return 0; } static int process_browsing(void *data) { struct avctp_browsing_req *req = data; struct avctp_pending_req *p = req->p; int ret; ret = avctp_browsing_send(p->queue, p->transaction, AVCTP_COMMAND, req->operands, req->operand_count); if (ret < 0) return ret; p->timeout = timeout_add_seconds(BROWSING_TIMEOUT, req_timeout, p->queue, NULL); return 0; } static gboolean process_queue(void *user_data) { struct avctp_queue *queue = user_data; struct avctp_pending_req *p = queue->p; queue->process_id = 0; if (p != NULL) return FALSE; while ((p = g_queue_pop_head(queue->queue))) { if (p->process(p->data) == 0) break; pending_destroy(p, NULL); } if (p == NULL) return FALSE; queue->p = p; return FALSE; } static void control_response(struct avctp_channel *control, struct avctp_header *avctp, struct avc_header *avc, uint8_t *operands, size_t operand_count) { struct avctp_pending_req *p; struct avctp_control_req *req; struct avctp_queue *queue; GSList *l; if (avc->opcode == AVC_OP_PASSTHROUGH) queue = g_slist_nth_data(control->queues, PASSTHROUGH_QUEUE); else queue = g_slist_nth_data(control->queues, CONTROL_QUEUE); p = queue->p; if (p && p->transaction == avctp->transaction) { req = p->data; if (req->op != avc->opcode) goto done; control->processed = g_slist_prepend(control->processed, p); if (p->timeout > 0) { timeout_remove(p->timeout); p->timeout = 0; } queue->p = NULL; if (queue->process_id == 0) queue->process_id = g_idle_add(process_queue, queue); } done: for (l = control->processed; l; l = l->next) { p = l->data; req = p->data; if (p->transaction != avctp->transaction) continue; if (req->op != avc->opcode) continue; if (req->func && req->func(control->session, avc->code, avc->subunit_type, p->transaction, operands, operand_count, req->user_data)) return; control->processed = g_slist_remove(control->processed, p); pending_destroy(p, NULL); return; } } static void browsing_response(struct avctp_channel *browsing, struct avctp_header *avctp, uint8_t *operands, size_t operand_count) { struct avctp_pending_req *p; struct avctp_browsing_req *req; struct avctp_queue *queue; GSList *l; queue = g_slist_nth_data(browsing->queues, 0); p = queue->p; if (p && p->transaction == avctp->transaction) { browsing->processed = g_slist_prepend(browsing->processed, p); if (p->timeout > 0) { timeout_remove(p->timeout); p->timeout = 0; } queue->p = NULL; if (queue->process_id == 0) queue->process_id = g_idle_add(process_queue, queue); } for (l = browsing->processed; l; l = l->next) { p = l->data; req = p->data; if (p->transaction != avctp->transaction) continue; if (req->func && req->func(browsing->session, operands, operand_count, req->user_data)) return; browsing->processed = g_slist_remove(browsing->processed, p); pending_destroy(p, NULL); return; } } static gboolean session_browsing_cb(GIOChannel *chan, GIOCondition cond, gpointer data) { struct avctp *session = data; struct avctp_channel *browsing = session->browsing; uint8_t *buf = browsing->buffer; uint8_t *operands; struct avctp_header *avctp; int sock, ret, packet_size, operand_count; struct avctp_browsing_pdu_handler *handler; if (cond & (G_IO_ERR | G_IO_HUP | G_IO_NVAL)) goto failed; sock = g_io_channel_unix_get_fd(chan); ret = read(sock, buf, browsing->imtu); if (ret <= 0) goto failed; avctp = (struct avctp_header *) buf; if (avctp->packet_type != AVCTP_PACKET_SINGLE) goto failed; operands = buf + AVCTP_HEADER_LENGTH; ret -= AVCTP_HEADER_LENGTH; operand_count = ret; if (avctp->cr == AVCTP_RESPONSE) { browsing_response(browsing, avctp, operands, operand_count); return TRUE; } packet_size = AVCTP_HEADER_LENGTH; avctp->cr = AVCTP_RESPONSE; handler = g_slist_nth_data(browsing->handlers, 0); if (handler == NULL) { DBG("handler not found"); packet_size += avrcp_browsing_general_reject(operands); goto send; } packet_size += handler->cb(session, avctp->transaction, operands, operand_count, handler->user_data); send: if (packet_size != 0) { ret = write(sock, buf, packet_size); if (ret != packet_size) goto failed; } return TRUE; failed: DBG("AVCTP Browsing: disconnected"); avctp_set_state(session, AVCTP_STATE_CONNECTED, 0); if (session->browsing) { avctp_channel_destroy(session->browsing); session->browsing = NULL; } return FALSE; } static gboolean session_cb(GIOChannel *chan, GIOCondition cond, gpointer data) { struct avctp *session = data; struct avctp_channel *control = session->control; uint8_t *buf = control->buffer; uint8_t *operands, code, subunit; struct avctp_header *avctp; struct avc_header *avc; int ret, packet_size, operand_count, sock; struct avctp_pdu_handler *handler; if (cond & (G_IO_ERR | G_IO_HUP | G_IO_NVAL)) goto failed; sock = g_io_channel_unix_get_fd(chan); ret = read(sock, buf, control->imtu); if (ret <= 0) goto failed; if (ret < AVCTP_HEADER_LENGTH) { error("Too small AVCTP packet"); goto failed; } avctp = (struct avctp_header *) buf; ret -= AVCTP_HEADER_LENGTH; if (ret < AVC_HEADER_LENGTH) { error("Too small AVC packet"); goto failed; } avc = (struct avc_header *) (buf + AVCTP_HEADER_LENGTH); ret -= AVC_HEADER_LENGTH; operands = (uint8_t *) avc + AVC_HEADER_LENGTH; operand_count = ret; if (avctp->cr == AVCTP_RESPONSE) { control_response(control, avctp, avc, operands, operand_count); return TRUE; } packet_size = AVCTP_HEADER_LENGTH + AVC_HEADER_LENGTH; avctp->cr = AVCTP_RESPONSE; if (avctp->packet_type != AVCTP_PACKET_SINGLE) { avc->code = AVC_CTYPE_NOT_IMPLEMENTED; goto done; } if (avctp->pid != htons(AV_REMOTE_SVCLASS_ID)) { avctp->ipid = 1; packet_size = AVCTP_HEADER_LENGTH; goto done; } handler = find_handler(control->handlers, avc->opcode); if (!handler) { DBG("handler not found for 0x%02x", avc->opcode); packet_size += avrcp_handle_vendor_reject(&code, operands); avc->code = code; goto done; } code = avc->code; subunit = avc->subunit_type; packet_size += handler->cb(session, avctp->transaction, &code, &subunit, operands, operand_count, handler->user_data); avc->code = code; avc->subunit_type = subunit; done: ret = write(sock, buf, packet_size); if (ret != packet_size) goto failed; return TRUE; failed: DBG("AVCTP session %p got disconnected", session); avctp_set_state(session, AVCTP_STATE_DISCONNECTED, -EIO); return FALSE; } static int uinput_create(struct btd_device *device, const char *name, const char *suffix) { struct uinput_user_dev dev; int fd, err, i; char src[18]; fd = open("/dev/uinput", O_RDWR); if (fd < 0) { fd = open("/dev/input/uinput", O_RDWR); if (fd < 0) { fd = open("/dev/misc/uinput", O_RDWR); if (fd < 0) { err = -errno; error("Can't open input device: %s (%d)", strerror(-err), -err); return err; } } } memset(&dev, 0, sizeof(dev)); if (name) { strncpy(dev.name, name, UINPUT_MAX_NAME_SIZE - 1); dev.name[UINPUT_MAX_NAME_SIZE - 1] = '\0'; } if (suffix) { int len, slen; len = strlen(dev.name); slen = strlen(suffix); /* If name + suffix don't fit, truncate the name, then add the * suffix. */ if (len + slen < UINPUT_MAX_NAME_SIZE - 1) { strcpy(dev.name + len, suffix); } else { len = UINPUT_MAX_NAME_SIZE - slen - 1; strncpy(dev.name + len, suffix, slen); dev.name[UINPUT_MAX_NAME_SIZE - 1] = '\0'; } } dev.id.bustype = BUS_BLUETOOTH; dev.id.vendor = btd_device_get_vendor(device); dev.id.product = btd_device_get_product(device); dev.id.version = btd_device_get_version(device); if (write(fd, &dev, sizeof(dev)) < 0) { err = -errno; error("Can't write device information: %s (%d)", strerror(-err), -err); close(fd); return err; } ioctl(fd, UI_SET_EVBIT, EV_KEY); ioctl(fd, UI_SET_EVBIT, EV_REL); ioctl(fd, UI_SET_EVBIT, EV_REP); ioctl(fd, UI_SET_EVBIT, EV_SYN); ba2strlc(btd_adapter_get_address(device_get_adapter(device)), src); ioctl(fd, UI_SET_PHYS, src); for (i = 0; key_map[i].name != NULL; i++) ioctl(fd, UI_SET_KEYBIT, key_map[i].uinput); if (ioctl(fd, UI_DEV_CREATE, NULL) < 0) { err = -errno; error("Can't create uinput device: %s (%d)", strerror(-err), -err); close(fd); return err; } send_event(fd, EV_REP, REP_DELAY, 300); return fd; } static void init_uinput(struct avctp *session) { char name[UINPUT_MAX_NAME_SIZE]; device_get_name(session->device, name, sizeof(name)); if (g_str_equal(name, "Nokia CK-20W")) { session->key_quirks[AVC_FORWARD] |= QUIRK_NO_RELEASE; session->key_quirks[AVC_BACKWARD] |= QUIRK_NO_RELEASE; session->key_quirks[AVC_PLAY] |= QUIRK_NO_RELEASE; session->key_quirks[AVC_PAUSE] |= QUIRK_NO_RELEASE; } session->uinput = uinput_create(session->device, name, " (AVRCP)"); if (session->uinput < 0) error("AVRCP: failed to init uinput for %s", name); else DBG("AVRCP: uinput initialized for %s", name); } static struct avctp_queue *avctp_queue_create(struct avctp_channel *chan) { struct avctp_queue *queue; queue = g_new0(struct avctp_queue, 1); queue->chan = chan; queue->queue = g_queue_new(); return queue; } static struct avctp_channel *avctp_channel_create(struct avctp *session, GIOChannel *io, int queues, GDestroyNotify destroy) { struct avctp_channel *chan; chan = g_new0(struct avctp_channel, 1); chan->session = session; chan->io = g_io_channel_ref(io); chan->destroy = destroy; while (queues--) { struct avctp_queue *queue; queue = avctp_queue_create(chan); chan->queues = g_slist_prepend(chan->queues, queue); } return chan; } static void handler_free(void *data) { struct avctp_browsing_pdu_handler *handler = data; if (handler->destroy) handler->destroy(handler->user_data); g_free(data); } static void avctp_destroy_browsing(void *data) { struct avctp_channel *chan = data; g_slist_free_full(chan->handlers, handler_free); chan->handlers = NULL; } static void avctp_connect_browsing_cb(GIOChannel *chan, GError *err, gpointer data) { struct avctp *session = data; struct avctp_channel *browsing = session->browsing; struct avctp_queue *queue; char address[18]; uint16_t imtu, omtu; GError *gerr = NULL; if (err) { error("Browsing: %s", err->message); goto fail; } bt_io_get(chan, &gerr, BT_IO_OPT_DEST, &address, BT_IO_OPT_IMTU, &imtu, BT_IO_OPT_OMTU, &omtu, BT_IO_OPT_INVALID); if (gerr) { error("%s", gerr->message); g_io_channel_shutdown(chan, TRUE, NULL); g_io_channel_unref(chan); g_error_free(gerr); goto fail; } DBG("AVCTP Browsing: connected to %s", address); if (browsing == NULL) { browsing = avctp_channel_create(session, chan, 1, avctp_destroy_browsing); session->browsing = browsing; } browsing->imtu = imtu; browsing->omtu = omtu; browsing->buffer = g_malloc0(MAX(imtu, omtu)); browsing->watch = g_io_add_watch(session->browsing->io, G_IO_IN | G_IO_ERR | G_IO_HUP | G_IO_NVAL, (GIOFunc) session_browsing_cb, session); avctp_set_state(session, AVCTP_STATE_BROWSING_CONNECTED, 0); /* Process any request that was pending the connection to complete */ queue = g_slist_nth_data(browsing->queues, 0); if (queue->process_id == 0 && !g_queue_is_empty(queue->queue)) queue->process_id = g_idle_add(process_queue, queue); return; fail: avctp_set_state(session, AVCTP_STATE_CONNECTED, 0); if (session->browsing) { avctp_channel_destroy(session->browsing); session->browsing = NULL; } } static void avctp_connect_cb(GIOChannel *chan, GError *err, gpointer data) { struct avctp *session = data; char address[18]; uint16_t imtu, omtu; GError *gerr = NULL; if (err) { avctp_set_state(session, AVCTP_STATE_DISCONNECTED, -EIO); error("%s", err->message); return; } bt_io_get(chan, &gerr, BT_IO_OPT_DEST, &address, BT_IO_OPT_IMTU, &imtu, BT_IO_OPT_OMTU, &omtu, BT_IO_OPT_INVALID); if (gerr) { avctp_set_state(session, AVCTP_STATE_DISCONNECTED, -EIO); error("%s", gerr->message); g_error_free(gerr); return; } DBG("AVCTP: connected to %s", address); if (session->control == NULL) session->control = avctp_channel_create(session, chan, 2, NULL); session->control->imtu = imtu; session->control->omtu = omtu; session->control->buffer = g_malloc0(MAX(imtu, omtu)); session->control->watch = g_io_add_watch(session->control->io, G_IO_IN | G_IO_ERR | G_IO_HUP | G_IO_NVAL, (GIOFunc) session_cb, session); session->passthrough_id = avctp_register_pdu_handler(session, AVC_OP_PASSTHROUGH, handle_panel_passthrough, NULL); session->unit_id = avctp_register_pdu_handler(session, AVC_OP_UNITINFO, handle_unit_info, NULL); session->subunit_id = avctp_register_pdu_handler(session, AVC_OP_SUBUNITINFO, handle_subunit_info, NULL); init_uinput(session); avctp_set_state(session, AVCTP_STATE_CONNECTED, 0); } static void auth_cb(DBusError *derr, void *user_data) { struct avctp *session = user_data; GError *err = NULL; session->auth_id = 0; if (session->control->watch > 0) { g_source_remove(session->control->watch); session->control->watch = 0; } if (derr && dbus_error_is_set(derr)) { error("Access denied: %s", derr->message); avctp_set_state(session, AVCTP_STATE_DISCONNECTED, -EIO); return; } if (!bt_io_accept(session->control->io, avctp_connect_cb, session, NULL, &err)) { error("bt_io_accept: %s", err->message); g_error_free(err); avctp_set_state(session, AVCTP_STATE_DISCONNECTED, -EIO); } } static struct avctp_server *find_server(GSList *list, struct btd_adapter *a) { for (; list; list = list->next) { struct avctp_server *server = list->data; if (server->adapter == a) return server; } return NULL; } static struct avctp *find_session(GSList *list, struct btd_device *device) { for (; list != NULL; list = g_slist_next(list)) { struct avctp *s = list->data; if (s->device == device) return s; } return NULL; } static struct avctp *avctp_get_internal(struct btd_device *device) { struct avctp_server *server; struct avctp *session; server = find_server(servers, device_get_adapter(device)); if (server == NULL) return NULL; session = find_session(server->sessions, device); if (session) return session; session = g_new0(struct avctp, 1); session->server = server; session->device = btd_device_ref(device); session->state = AVCTP_STATE_DISCONNECTED; session->uinput = -1; session->key.op = AVC_INVALID; server->sessions = g_slist_append(server->sessions, session); return session; } static void avctp_control_confirm(struct avctp *session, GIOChannel *chan, struct btd_device *dev) { const bdaddr_t *src; const bdaddr_t *dst; if (session->control != NULL) { error("Control: Refusing unexpected connect"); g_io_channel_shutdown(chan, TRUE, NULL); /* * Close AVCTP channel if remote tried connect * at the same time * AVRCP SPEC V1.5 4.1.1 Connection Establishment */ avctp_set_state(session, AVCTP_STATE_DISCONNECTED, -EAGAIN); return; } avctp_set_state(session, AVCTP_STATE_CONNECTING, 0); session->control = avctp_channel_create(session, chan, 2, NULL); src = btd_adapter_get_address(device_get_adapter(dev)); dst = device_get_address(dev); session->auth_id = btd_request_authorization(src, dst, AVRCP_REMOTE_UUID, auth_cb, session); if (session->auth_id == 0) goto drop; session->control->watch = g_io_add_watch(chan, G_IO_ERR | G_IO_HUP | G_IO_NVAL, session_cb, session); return; drop: avctp_set_state(session, AVCTP_STATE_DISCONNECTED, -EIO); } static void avctp_browsing_confirm(struct avctp *session, GIOChannel *chan, struct btd_device *dev) { GError *err = NULL; if (session->control == NULL || session->browsing != NULL) { error("Browsing: Refusing unexpected connect"); g_io_channel_shutdown(chan, TRUE, NULL); return; } if (bt_io_accept(chan, avctp_connect_browsing_cb, session, NULL, &err)) { avctp_set_state(session, AVCTP_STATE_BROWSING_CONNECTING, 0); session->browsing = avctp_channel_create(session, chan, 1, avctp_destroy_browsing); return; } error("Browsing: %s", err->message); g_error_free(err); return; } static void avctp_confirm_cb(GIOChannel *chan, gpointer data) { struct avctp *session; char address[18]; bdaddr_t src, dst; GError *err = NULL; uint16_t psm; struct btd_device *device; bt_io_get(chan, &err, BT_IO_OPT_SOURCE_BDADDR, &src, BT_IO_OPT_DEST_BDADDR, &dst, BT_IO_OPT_DEST, address, BT_IO_OPT_PSM, &psm, BT_IO_OPT_INVALID); if (err) { error("%s", err->message); g_error_free(err); g_io_channel_shutdown(chan, TRUE, NULL); return; } DBG("AVCTP: incoming connect from %s", address); device = btd_adapter_find_device(adapter_find(&src), &dst, BDADDR_BREDR); if (!device) return; session = avctp_get_internal(device); if (session == NULL) return; if (btd_device_get_service(device, AVRCP_REMOTE_UUID) == NULL) btd_device_add_uuid(device, AVRCP_REMOTE_UUID); if (btd_device_get_service(device, AVRCP_TARGET_UUID) == NULL) btd_device_add_uuid(device, AVRCP_TARGET_UUID); switch (psm) { case AVCTP_CONTROL_PSM: avctp_control_confirm(session, chan, device); break; case AVCTP_BROWSING_PSM: avctp_browsing_confirm(session, chan, device); break; } return; } static GIOChannel *avctp_server_socket(const bdaddr_t *src, gboolean central, uint8_t mode, uint16_t psm) { GError *err = NULL; GIOChannel *io; io = bt_io_listen(NULL, avctp_confirm_cb, NULL, NULL, &err, BT_IO_OPT_SOURCE_BDADDR, src, BT_IO_OPT_PSM, psm, BT_IO_OPT_SEC_LEVEL, BT_IO_SEC_MEDIUM, BT_IO_OPT_CENTRAL, central, BT_IO_OPT_MODE, mode, BT_IO_OPT_INVALID); if (!io) { error("%s", err->message); g_error_free(err); } return io; } int avctp_register(struct btd_adapter *adapter, bool central, bool *browsing) { struct avctp_server *server; const bdaddr_t *src = btd_adapter_get_address(adapter); server = g_new0(struct avctp_server, 1); server->control_io = avctp_server_socket(src, central, BT_IO_MODE_BASIC, AVCTP_CONTROL_PSM); if (!server->control_io) { g_free(server); return -1; } server->browsing_io = avctp_server_socket(src, central, BT_IO_MODE_ERTM, AVCTP_BROWSING_PSM); if (browsing) *browsing = server->browsing_io ? true : false; server->adapter = btd_adapter_ref(adapter); servers = g_slist_append(servers, server); return 0; } void avctp_unregister(struct btd_adapter *adapter) { struct avctp_server *server; server = find_server(servers, adapter); if (!server) return; while (server->sessions) avctp_disconnected(server->sessions->data); servers = g_slist_remove(servers, server); if (server->browsing_io) { g_io_channel_shutdown(server->browsing_io, TRUE, NULL); g_io_channel_unref(server->browsing_io); server->browsing_io = NULL; } g_io_channel_shutdown(server->control_io, TRUE, NULL); g_io_channel_unref(server->control_io); btd_adapter_unref(server->adapter); g_free(server); } static struct avctp_pending_req *pending_create(struct avctp_queue *queue, avctp_process_cb process, void *data, GDestroyNotify destroy) { struct avctp_pending_req *p; p = g_new0(struct avctp_pending_req, 1); p->queue = queue; p->transaction = chan_get_transaction(queue->chan); p->process = process; p->data = data; p->destroy = destroy; return p; } static int avctp_send_req(struct avctp *session, uint8_t code, uint8_t subunit, uint8_t opcode, uint8_t *operands, size_t operand_count, avctp_rsp_cb func, void *user_data) { struct avctp_channel *control = session->control; struct avctp_queue *queue; struct avctp_pending_req *p; struct avctp_control_req *req; if (control == NULL) return -ENOTCONN; /* If the request set a callback send it directly */ if (!func) return avctp_send(session->control, -1, AVCTP_COMMAND, code, subunit, opcode, operands, operand_count); req = g_new0(struct avctp_control_req, 1); req->code = code; req->subunit = subunit; req->op = opcode; req->func = func; req->operands = util_memdup(operands, operand_count); req->operand_count = operand_count; req->user_data = user_data; if (opcode == AVC_OP_PASSTHROUGH) { queue = g_slist_nth_data(control->queues, PASSTHROUGH_QUEUE); p = pending_create(queue, process_passthrough, req, control_req_destroy); } else { queue = g_slist_nth_data(control->queues, CONTROL_QUEUE); p = pending_create(queue, process_control, req, control_req_destroy); } req->p = p; g_queue_push_tail(queue->queue, p); if (queue->process_id == 0) queue->process_id = g_idle_add(process_queue, queue); return 0; } int avctp_send_browsing_req(struct avctp *session, uint8_t *operands, size_t operand_count, avctp_browsing_rsp_cb func, void *user_data) { struct avctp_channel *browsing = session->browsing; struct avctp_queue *queue; struct avctp_pending_req *p; struct avctp_browsing_req *req; if (browsing == NULL) return -ENOTCONN; req = g_new0(struct avctp_browsing_req, 1); req->func = func; req->operands = util_memdup(operands, operand_count); req->operand_count = operand_count; req->user_data = user_data; queue = g_slist_nth_data(browsing->queues, 0); p = pending_create(queue, process_browsing, req, browsing_req_destroy); req->p = p; g_queue_push_tail(queue->queue, p); /* Connection did not complete, delay process of the request */ if (browsing->watch == 0) return 0; if (queue->process_id == 0) queue->process_id = g_idle_add(process_queue, queue); return 0; } static const char *op2str(uint8_t op) { int i; for (i = 0; key_map[i].name != NULL; i++) { if ((op & 0x7F) == key_map[i].avc) return key_map[i].name; } return "UNKNOWN"; } static int avctp_passthrough_press(struct avctp *session, uint8_t op) { uint8_t operands[2]; DBG("%s", op2str(op)); /* Button pressed */ operands[0] = op & 0x7f; operands[1] = 0; return avctp_send_req(session, AVC_CTYPE_CONTROL, AVC_SUBUNIT_PANEL, AVC_OP_PASSTHROUGH, operands, sizeof(operands), avctp_passthrough_rsp, NULL); } static int avctp_passthrough_release(struct avctp *session, uint8_t op) { uint8_t operands[2]; DBG("%s", op2str(op)); /* Button released */ operands[0] = op | 0x80; operands[1] = 0; return avctp_send_req(session, AVC_CTYPE_CONTROL, AVC_SUBUNIT_PANEL, AVC_OP_PASSTHROUGH, operands, sizeof(operands), NULL, NULL); } static bool repeat_timeout(gpointer user_data) { struct avctp *session = user_data; avctp_passthrough_press(session, session->key.op); return TRUE; } static int release_pressed(struct avctp *session) { int ret = avctp_passthrough_release(session, session->key.op); if (session->key.timer > 0) timeout_remove(session->key.timer); session->key.timer = 0; session->key.op = AVC_INVALID; session->key.hold = false; return ret; } static bool hold_pressed(struct avctp *session, uint8_t op) { if (session->key.op != op || !session->key.hold) return FALSE; if (session->key.timer == 0) session->key.timer = timeout_add_seconds(AVC_HOLD_TIMEOUT, repeat_timeout, session, NULL); return TRUE; } static gboolean avctp_passthrough_rsp(struct avctp *session, uint8_t code, uint8_t subunit, uint8_t transaction, uint8_t *operands, size_t operand_count, void *user_data) { uint8_t op = operands[0]; if (code != AVC_CTYPE_ACCEPTED) return FALSE; if (hold_pressed(session, op)) return FALSE; if (op == session->key.op) release_pressed(session); return FALSE; } int avctp_send_passthrough(struct avctp *session, uint8_t op, bool hold) { if (op & 0x80) return -EINVAL; /* Release previously unreleased key */ if (session->key.op != AVC_INVALID && session->key.op != op) release_pressed(session); session->key.op = op; session->key.hold = hold; return avctp_passthrough_press(session, op); } int avctp_send_release_passthrough(struct avctp *session) { if (session->key.op != AVC_INVALID) return release_pressed(session); return 0; } int avctp_send_vendordep(struct avctp *session, uint8_t transaction, uint8_t code, uint8_t subunit, uint8_t *operands, size_t operand_count) { struct avctp_channel *control = session->control; if (control == NULL) return -ENOTCONN; return avctp_send(control, transaction, AVCTP_RESPONSE, code, subunit, AVC_OP_VENDORDEP, operands, operand_count); } int avctp_send_vendordep_req(struct avctp *session, uint8_t code, uint8_t subunit, uint8_t *operands, size_t operand_count, avctp_rsp_cb func, void *user_data) { return avctp_send_req(session, code, subunit, AVC_OP_VENDORDEP, operands, operand_count, func, user_data); } unsigned int avctp_add_state_cb(struct btd_device *dev, avctp_state_cb cb, void *user_data) { struct avctp_state_callback *state_cb; static unsigned int id = 0; state_cb = g_new(struct avctp_state_callback, 1); state_cb->cb = cb; state_cb->dev = dev; state_cb->id = ++id; state_cb->user_data = user_data; callbacks = g_slist_append(callbacks, state_cb); return state_cb->id; } gboolean avctp_remove_state_cb(unsigned int id) { GSList *l; for (l = callbacks; l != NULL; l = l->next) { struct avctp_state_callback *cb = l->data; if (cb && cb->id == id) { callbacks = g_slist_remove(callbacks, cb); g_free(cb); return TRUE; } } return FALSE; } unsigned int avctp_register_passthrough_handler(struct avctp *session, avctp_passthrough_cb cb, void *user_data) { struct avctp_channel *control = session->control; struct avctp_passthrough_handler *handler; static unsigned int id = 0; if (control == NULL || session->handler != NULL) return 0; handler = g_new(struct avctp_passthrough_handler, 1); handler->cb = cb; handler->user_data = user_data; handler->id = ++id; session->handler = handler; return handler->id; } bool avctp_unregister_passthrough_handler(unsigned int id) { GSList *l; for (l = servers; l; l = l->next) { struct avctp_server *server = l->data; GSList *s; for (s = server->sessions; s; s = s->next) { struct avctp *session = s->data; if (session->handler == NULL) continue; if (session->handler->id == id) { g_free(session->handler); session->handler = NULL; return true; } } } return false; } unsigned int avctp_register_pdu_handler(struct avctp *session, uint8_t opcode, avctp_control_pdu_cb cb, void *user_data) { struct avctp_channel *control = session->control; struct avctp_pdu_handler *handler; static unsigned int id = 0; if (control == NULL) return 0; handler = find_handler(control->handlers, opcode); if (handler) return 0; handler = g_new(struct avctp_pdu_handler, 1); handler->opcode = opcode; handler->cb = cb; handler->user_data = user_data; handler->id = ++id; control->handlers = g_slist_append(control->handlers, handler); return handler->id; } unsigned int avctp_register_browsing_pdu_handler(struct avctp *session, avctp_browsing_pdu_cb cb, void *user_data, GDestroyNotify destroy) { struct avctp_channel *browsing = session->browsing; struct avctp_browsing_pdu_handler *handler; static unsigned int id = 0; if (browsing == NULL) return 0; if (browsing->handlers != NULL) return 0; handler = g_new(struct avctp_browsing_pdu_handler, 1); handler->cb = cb; handler->user_data = user_data; handler->id = ++id; handler->destroy = destroy; browsing->handlers = g_slist_append(browsing->handlers, handler); return handler->id; } gboolean avctp_unregister_pdu_handler(unsigned int id) { GSList *l; for (l = servers; l; l = l->next) { struct avctp_server *server = l->data; GSList *s; for (s = server->sessions; s; s = s->next) { struct avctp *session = s->data; struct avctp_channel *control = session->control; GSList *h; if (control == NULL) continue; for (h = control->handlers; h; h = h->next) { struct avctp_pdu_handler *handler = h->data; if (handler->id != id) continue; control->handlers = g_slist_remove( control->handlers, handler); g_free(handler); return TRUE; } } } return FALSE; } gboolean avctp_unregister_browsing_pdu_handler(unsigned int id) { GSList *l; for (l = servers; l; l = l->next) { struct avctp_server *server = l->data; GSList *s; for (s = server->sessions; s; s = s->next) { struct avctp *session = s->data; struct avctp_channel *browsing = session->browsing; GSList *h; if (browsing == NULL) continue; for (h = browsing->handlers; h; h = h->next) { struct avctp_browsing_pdu_handler *handler = h->data; if (handler->id != id) continue; browsing->handlers = g_slist_remove( browsing->handlers, handler); g_free(handler); return TRUE; } } } return FALSE; } struct avctp *avctp_connect(struct btd_device *device) { struct avctp *session; GError *err = NULL; GIOChannel *io; const bdaddr_t *src; session = avctp_get_internal(device); if (!session) return NULL; if (session->state > AVCTP_STATE_DISCONNECTED) return session; avctp_set_state(session, AVCTP_STATE_CONNECTING, 0); src = btd_adapter_get_address(session->server->adapter); io = bt_io_connect(avctp_connect_cb, session, NULL, &err, BT_IO_OPT_SOURCE_BDADDR, src, BT_IO_OPT_DEST_BDADDR, device_get_address(session->device), BT_IO_OPT_SEC_LEVEL, BT_IO_SEC_MEDIUM, BT_IO_OPT_PSM, AVCTP_CONTROL_PSM, BT_IO_OPT_INVALID); if (err) { avctp_set_state(session, AVCTP_STATE_DISCONNECTED, -EIO); error("%s", err->message); g_error_free(err); return NULL; } session->control = avctp_channel_create(session, io, 2, NULL); session->initiator = true; g_io_channel_unref(io); return session; } int avctp_connect_browsing(struct avctp *session) { const bdaddr_t *src; GError *err = NULL; GIOChannel *io; if (session->state != AVCTP_STATE_CONNECTED) return -ENOTCONN; if (session->browsing != NULL) return 0; avctp_set_state(session, AVCTP_STATE_BROWSING_CONNECTING, 0); src = btd_adapter_get_address(session->server->adapter); io = bt_io_connect(avctp_connect_browsing_cb, session, NULL, &err, BT_IO_OPT_SOURCE_BDADDR, src, BT_IO_OPT_DEST_BDADDR, device_get_address(session->device), BT_IO_OPT_SEC_LEVEL, BT_IO_SEC_MEDIUM, BT_IO_OPT_PSM, AVCTP_BROWSING_PSM, BT_IO_OPT_MODE, BT_IO_MODE_ERTM, BT_IO_OPT_INVALID); if (err) { error("%s", err->message); g_error_free(err); return -EIO; } session->browsing = avctp_channel_create(session, io, 1, avctp_destroy_browsing); g_io_channel_unref(io); return 0; } void avctp_disconnect(struct avctp *session) { if (session->state == AVCTP_STATE_DISCONNECTED) return; avctp_set_state(session, AVCTP_STATE_DISCONNECTED, -EIO); } struct avctp *avctp_get(struct btd_device *device) { return avctp_get_internal(device); } bool avctp_is_initiator(struct avctp *session) { return session->initiator; } bool avctp_supports_avc(uint8_t avc) { int i; for (i = 0; key_map[i].name != NULL; i++) { if (key_map[i].avc == avc) return true; } return false; } bluez-5.82/profiles/audio/PaxHeaders/a2dp-codecs.h0000644000000000000000000000005014572354773017050 xustar0020 atime=1743515790 20 ctime=1743591284 bluez-5.82/profiles/audio/a2dp-codecs.h0000644000000000000000000003060014572354773016530 0ustar00rootroot/* SPDX-License-Identifier: LGPL-2.1-or-later */ /* * * BlueZ - Bluetooth protocol stack for Linux * * Copyright (C) 2006-2010 Nokia Corporation * Copyright (C) 2004-2010 Marcel Holtmann * Copyright (C) 2018 Pali Rohár * * */ #include #include #define A2DP_CODEC_SBC 0x00 #define A2DP_CODEC_MPEG12 0x01 #define A2DP_CODEC_MPEG24 0x02 #define A2DP_CODEC_ATRAC 0x04 #define A2DP_CODEC_VENDOR 0xFF #define SBC_SAMPLING_FREQ_16000 (1 << 3) #define SBC_SAMPLING_FREQ_32000 (1 << 2) #define SBC_SAMPLING_FREQ_44100 (1 << 1) #define SBC_SAMPLING_FREQ_48000 1 #define SBC_CHANNEL_MODE_MONO (1 << 3) #define SBC_CHANNEL_MODE_DUAL_CHANNEL (1 << 2) #define SBC_CHANNEL_MODE_STEREO (1 << 1) #define SBC_CHANNEL_MODE_JOINT_STEREO 1 #define SBC_BLOCK_LENGTH_4 (1 << 3) #define SBC_BLOCK_LENGTH_8 (1 << 2) #define SBC_BLOCK_LENGTH_12 (1 << 1) #define SBC_BLOCK_LENGTH_16 1 #define SBC_SUBBANDS_4 (1 << 1) #define SBC_SUBBANDS_8 1 #define SBC_ALLOCATION_SNR (1 << 1) #define SBC_ALLOCATION_LOUDNESS 1 #define SBC_MIN_BITPOOL 2 #define SBC_MAX_BITPOOL 250 /* Other settings: * Block length = 16 * Allocation method = Loudness * Subbands = 8 */ #define SBC_BITPOOL_MQ_MONO_44100 19 #define SBC_BITPOOL_MQ_MONO_48000 18 #define SBC_BITPOOL_MQ_JOINT_STEREO_44100 35 #define SBC_BITPOOL_MQ_JOINT_STEREO_48000 33 #define SBC_BITPOOL_HQ_MONO_44100 31 #define SBC_BITPOOL_HQ_MONO_48000 29 #define SBC_BITPOOL_HQ_JOINT_STEREO_44100 53 #define SBC_BITPOOL_HQ_JOINT_STEREO_48000 51 #define MPEG_CHANNEL_MODE_MONO (1 << 3) #define MPEG_CHANNEL_MODE_DUAL_CHANNEL (1 << 2) #define MPEG_CHANNEL_MODE_STEREO (1 << 1) #define MPEG_CHANNEL_MODE_JOINT_STEREO 1 #define MPEG_LAYER_MP1 (1 << 2) #define MPEG_LAYER_MP2 (1 << 1) #define MPEG_LAYER_MP3 1 #define MPEG_SAMPLING_FREQ_16000 (1 << 5) #define MPEG_SAMPLING_FREQ_22050 (1 << 4) #define MPEG_SAMPLING_FREQ_24000 (1 << 3) #define MPEG_SAMPLING_FREQ_32000 (1 << 2) #define MPEG_SAMPLING_FREQ_44100 (1 << 1) #define MPEG_SAMPLING_FREQ_48000 1 #define MPEG_BIT_RATE_INDEX_0 (1 << 0) #define MPEG_BIT_RATE_INDEX_1 (1 << 1) #define MPEG_BIT_RATE_INDEX_2 (1 << 2) #define MPEG_BIT_RATE_INDEX_3 (1 << 3) #define MPEG_BIT_RATE_INDEX_4 (1 << 4) #define MPEG_BIT_RATE_INDEX_5 (1 << 5) #define MPEG_BIT_RATE_INDEX_6 (1 << 6) #define MPEG_BIT_RATE_INDEX_7 (1 << 7) #define MPEG_BIT_RATE_INDEX_8 (1 << 8) #define MPEG_BIT_RATE_INDEX_9 (1 << 9) #define MPEG_BIT_RATE_INDEX_10 (1 << 10) #define MPEG_BIT_RATE_INDEX_11 (1 << 11) #define MPEG_BIT_RATE_INDEX_12 (1 << 12) #define MPEG_BIT_RATE_INDEX_13 (1 << 13) #define MPEG_BIT_RATE_INDEX_14 (1 << 14) #define MPEG_MP1_BIT_RATE_32000 MPEG_BIT_RATE_INDEX_1 #define MPEG_MP1_BIT_RATE_64000 MPEG_BIT_RATE_INDEX_2 #define MPEG_MP1_BIT_RATE_96000 MPEG_BIT_RATE_INDEX_3 #define MPEG_MP1_BIT_RATE_128000 MPEG_BIT_RATE_INDEX_4 #define MPEG_MP1_BIT_RATE_160000 MPEG_BIT_RATE_INDEX_5 #define MPEG_MP1_BIT_RATE_192000 MPEG_BIT_RATE_INDEX_6 #define MPEG_MP1_BIT_RATE_224000 MPEG_BIT_RATE_INDEX_7 #define MPEG_MP1_BIT_RATE_256000 MPEG_BIT_RATE_INDEX_8 #define MPEG_MP1_BIT_RATE_288000 MPEG_BIT_RATE_INDEX_9 #define MPEG_MP1_BIT_RATE_320000 MPEG_BIT_RATE_INDEX_10 #define MPEG_MP1_BIT_RATE_352000 MPEG_BIT_RATE_INDEX_11 #define MPEG_MP1_BIT_RATE_384000 MPEG_BIT_RATE_INDEX_12 #define MPEG_MP1_BIT_RATE_416000 MPEG_BIT_RATE_INDEX_13 #define MPEG_MP1_BIT_RATE_448000 MPEG_BIT_RATE_INDEX_14 #define MPEG_MP2_BIT_RATE_32000 MPEG_BIT_RATE_INDEX_1 #define MPEG_MP2_BIT_RATE_48000 MPEG_BIT_RATE_INDEX_2 #define MPEG_MP2_BIT_RATE_56000 MPEG_BIT_RATE_INDEX_3 #define MPEG_MP2_BIT_RATE_64000 MPEG_BIT_RATE_INDEX_4 #define MPEG_MP2_BIT_RATE_80000 MPEG_BIT_RATE_INDEX_5 #define MPEG_MP2_BIT_RATE_96000 MPEG_BIT_RATE_INDEX_6 #define MPEG_MP2_BIT_RATE_112000 MPEG_BIT_RATE_INDEX_7 #define MPEG_MP2_BIT_RATE_128000 MPEG_BIT_RATE_INDEX_8 #define MPEG_MP2_BIT_RATE_160000 MPEG_BIT_RATE_INDEX_9 #define MPEG_MP2_BIT_RATE_192000 MPEG_BIT_RATE_INDEX_10 #define MPEG_MP2_BIT_RATE_224000 MPEG_BIT_RATE_INDEX_11 #define MPEG_MP2_BIT_RATE_256000 MPEG_BIT_RATE_INDEX_12 #define MPEG_MP2_BIT_RATE_320000 MPEG_BIT_RATE_INDEX_13 #define MPEG_MP2_BIT_RATE_384000 MPEG_BIT_RATE_INDEX_14 #define MPEG_MP3_BIT_RATE_32000 MPEG_BIT_RATE_INDEX_1 #define MPEG_MP3_BIT_RATE_40000 MPEG_BIT_RATE_INDEX_2 #define MPEG_MP3_BIT_RATE_48000 MPEG_BIT_RATE_INDEX_3 #define MPEG_MP3_BIT_RATE_56000 MPEG_BIT_RATE_INDEX_4 #define MPEG_MP3_BIT_RATE_64000 MPEG_BIT_RATE_INDEX_5 #define MPEG_MP3_BIT_RATE_80000 MPEG_BIT_RATE_INDEX_6 #define MPEG_MP3_BIT_RATE_96000 MPEG_BIT_RATE_INDEX_7 #define MPEG_MP3_BIT_RATE_112000 MPEG_BIT_RATE_INDEX_8 #define MPEG_MP3_BIT_RATE_128000 MPEG_BIT_RATE_INDEX_9 #define MPEG_MP3_BIT_RATE_160000 MPEG_BIT_RATE_INDEX_10 #define MPEG_MP3_BIT_RATE_192000 MPEG_BIT_RATE_INDEX_11 #define MPEG_MP3_BIT_RATE_224000 MPEG_BIT_RATE_INDEX_12 #define MPEG_MP3_BIT_RATE_256000 MPEG_BIT_RATE_INDEX_13 #define MPEG_MP3_BIT_RATE_320000 MPEG_BIT_RATE_INDEX_14 #define MPEG_BIT_RATE_FREE MPEG_BIT_RATE_INDEX_0 #define MPEG_GET_BITRATE(a) ((uint16_t)(a).bitrate1 << 8 | (a).bitrate2) #define MPEG_SET_BITRATE(a, b) \ do { \ (a).bitrate1 = ((b) >> 8) & 0x7f; \ (a).bitrate2 = (b) & 0xff; \ } while (0) #define AAC_OBJECT_TYPE_MPEG2_AAC_LC 0x80 #define AAC_OBJECT_TYPE_MPEG4_AAC_LC 0x40 #define AAC_OBJECT_TYPE_MPEG4_AAC_LTP 0x20 #define AAC_OBJECT_TYPE_MPEG4_AAC_SCA 0x10 #define AAC_SAMPLING_FREQ_8000 0x0800 #define AAC_SAMPLING_FREQ_11025 0x0400 #define AAC_SAMPLING_FREQ_12000 0x0200 #define AAC_SAMPLING_FREQ_16000 0x0100 #define AAC_SAMPLING_FREQ_22050 0x0080 #define AAC_SAMPLING_FREQ_24000 0x0040 #define AAC_SAMPLING_FREQ_32000 0x0020 #define AAC_SAMPLING_FREQ_44100 0x0010 #define AAC_SAMPLING_FREQ_48000 0x0008 #define AAC_SAMPLING_FREQ_64000 0x0004 #define AAC_SAMPLING_FREQ_88200 0x0002 #define AAC_SAMPLING_FREQ_96000 0x0001 #define AAC_CHANNELS_1 0x02 #define AAC_CHANNELS_2 0x01 #define AAC_GET_BITRATE(a) ((a).bitrate1 << 16 | \ (a).bitrate2 << 8 | (a).bitrate3) #define AAC_GET_FREQUENCY(a) ((a).frequency1 << 4 | (a).frequency2) #define AAC_SET_BITRATE(a, b) \ do { \ (a).bitrate1 = (b >> 16) & 0x7f; \ (a).bitrate2 = (b >> 8) & 0xff; \ (a).bitrate3 = b & 0xff; \ } while (0) #define AAC_SET_FREQUENCY(a, f) \ do { \ (a).frequency1 = (f >> 4) & 0xff; \ (a).frequency2 = f & 0x0f; \ } while (0) #define AAC_INIT_BITRATE(b) \ .bitrate1 = (b >> 16) & 0x7f, \ .bitrate2 = (b >> 8) & 0xff, \ .bitrate3 = b & 0xff, #define AAC_INIT_FREQUENCY(f) \ .frequency1 = (f >> 4) & 0xff, \ .frequency2 = f & 0x0f, #define APTX_VENDOR_ID 0x0000004f #define APTX_CODEC_ID 0x0001 #define APTX_CHANNEL_MODE_MONO 0x01 #define APTX_CHANNEL_MODE_STEREO 0x02 #define APTX_SAMPLING_FREQ_16000 0x08 #define APTX_SAMPLING_FREQ_32000 0x04 #define APTX_SAMPLING_FREQ_44100 0x02 #define APTX_SAMPLING_FREQ_48000 0x01 #define FASTSTREAM_VENDOR_ID 0x0000000a #define FASTSTREAM_CODEC_ID 0x0001 #define FASTSTREAM_DIRECTION_SINK 0x1 #define FASTSTREAM_DIRECTION_SOURCE 0x2 #define FASTSTREAM_SINK_SAMPLING_FREQ_44100 0x2 #define FASTSTREAM_SINK_SAMPLING_FREQ_48000 0x1 #define FASTSTREAM_SOURCE_SAMPLING_FREQ_16000 0x2 #define APTX_LL_VENDOR_ID 0x0000000a #define APTX_LL_CODEC_ID 0x0002 /* Default parameters for aptX Low Latency encoder */ /* Target codec buffer level = 180 */ #define APTX_LL_TARGET_LEVEL2 0xb4 #define APTX_LL_TARGET_LEVEL1 0x00 /* Initial codec buffer level = 360 */ #define APTX_LL_INITIAL_LEVEL2 0x68 #define APTX_LL_INITIAL_LEVEL1 0x01 /* SRA max rate 0.005 * 10000 = 50 */ #define APTX_LL_SRA_MAX_RATE 0x32 /* SRA averaging time = 1s */ #define APTX_LL_SRA_AVG_TIME 0x01 /* Good working codec buffer level = 180 */ #define APTX_LL_GOOD_WORKING_LEVEL2 0xB4 #define APTX_LL_GOOD_WORKING_LEVEL1 0x00 #define APTX_HD_VENDOR_ID 0x000000D7 #define APTX_HD_CODEC_ID 0x0024 #define LDAC_VENDOR_ID 0x0000012d #define LDAC_CODEC_ID 0x00aa #define LDAC_SAMPLING_FREQ_44100 0x20 #define LDAC_SAMPLING_FREQ_48000 0x10 #define LDAC_SAMPLING_FREQ_88200 0x08 #define LDAC_SAMPLING_FREQ_96000 0x04 #define LDAC_SAMPLING_FREQ_176400 0x02 #define LDAC_SAMPLING_FREQ_192000 0x01 #define LDAC_CHANNEL_MODE_MONO 0x04 #define LDAC_CHANNEL_MODE_DUAL 0x02 #define LDAC_CHANNEL_MODE_STEREO 0x01 #define OPUS_G_VENDOR_ID 0x000000e0 #define OPUS_G_CODEC_ID 0x0001 #define OPUS_G_FREQUENCY_48000 0x80 #define OPUS_G_DURATION_100 0x08 #define OPUS_G_DURATION_200 0x10 #define OPUS_G_CHANNELS_MONO 0x01 #define OPUS_G_CHANNELS_STEREO 0x02 #define OPUS_G_CHANNELS_DUAL 0x04 typedef struct { uint8_t vendor_id4; uint8_t vendor_id3; uint8_t vendor_id2; uint8_t vendor_id1; uint8_t codec_id2; uint8_t codec_id1; } __attribute__ ((packed)) a2dp_vendor_codec_t; #define A2DP_GET_VENDOR_ID(a) ( \ (((uint32_t)(a).vendor_id4) << 0) | \ (((uint32_t)(a).vendor_id3) << 8) | \ (((uint32_t)(a).vendor_id2) << 16) | \ (((uint32_t)(a).vendor_id1) << 24) \ ) #define A2DP_GET_CODEC_ID(a) ((a).codec_id2 | (((uint16_t)(a).codec_id1) << 8)) #define A2DP_SET_VENDOR_ID_CODEC_ID(v, c) ((a2dp_vendor_codec_t){ \ .vendor_id4 = (((v) >> 0) & 0xff), \ .vendor_id3 = (((v) >> 8) & 0xff), \ .vendor_id2 = (((v) >> 16) & 0xff), \ .vendor_id1 = (((v) >> 24) & 0xff), \ .codec_id2 = (((c) >> 0) & 0xff), \ .codec_id1 = (((c) >> 8) & 0xff), \ }) typedef struct { uint8_t reserved; uint8_t target_level2; uint8_t target_level1; uint8_t initial_level2; uint8_t initial_level1; uint8_t sra_max_rate; uint8_t sra_avg_time; uint8_t good_working_level2; uint8_t good_working_level1; } __attribute__ ((packed)) a2dp_aptx_ll_new_caps_t; typedef struct { a2dp_vendor_codec_t info; uint8_t frequency; uint8_t channel_mode; } __attribute__ ((packed)) a2dp_ldac_t; #if defined(__BYTE_ORDER) && defined(__LITTLE_ENDIAN) && \ __BYTE_ORDER == __LITTLE_ENDIAN typedef struct { uint8_t channel_mode:4; uint8_t frequency:4; uint8_t allocation_method:2; uint8_t subbands:2; uint8_t block_length:4; uint8_t min_bitpool; uint8_t max_bitpool; } __attribute__ ((packed)) a2dp_sbc_t; typedef struct { uint8_t channel_mode:4; uint8_t crc:1; uint8_t layer:3; uint8_t frequency:6; uint8_t mpf:1; uint8_t rfa:1; uint8_t bitrate1:7; uint8_t vbr:1; uint8_t bitrate2; } __attribute__ ((packed)) a2dp_mpeg_t; typedef struct { uint8_t object_type; uint8_t frequency1; uint8_t rfa:2; uint8_t channels:2; uint8_t frequency2:4; uint8_t bitrate1:7; uint8_t vbr:1; uint8_t bitrate2; uint8_t bitrate3; } __attribute__ ((packed)) a2dp_aac_t; typedef struct { a2dp_vendor_codec_t info; uint8_t channel_mode:4; uint8_t frequency:4; } __attribute__ ((packed)) a2dp_aptx_t; typedef struct { a2dp_vendor_codec_t info; uint8_t direction; uint8_t sink_frequency:4; uint8_t source_frequency:4; } __attribute__ ((packed)) a2dp_faststream_t; typedef struct { a2dp_aptx_t aptx; uint8_t bidirect_link:1; uint8_t has_new_caps:1; uint8_t reserved:6; a2dp_aptx_ll_new_caps_t new_caps[0]; } __attribute__ ((packed)) a2dp_aptx_ll_t; #elif defined(__BYTE_ORDER) && defined(__BIG_ENDIAN) && \ __BYTE_ORDER == __BIG_ENDIAN typedef struct { uint8_t frequency:4; uint8_t channel_mode:4; uint8_t block_length:4; uint8_t subbands:2; uint8_t allocation_method:2; uint8_t min_bitpool; uint8_t max_bitpool; } __attribute__ ((packed)) a2dp_sbc_t; typedef struct { uint8_t layer:3; uint8_t crc:1; uint8_t channel_mode:4; uint8_t rfa:1; uint8_t mpf:1; uint8_t frequency:6; uint8_t vbr:1; uint8_t bitrate1:7; uint8_t bitrate2; } __attribute__ ((packed)) a2dp_mpeg_t; typedef struct { uint8_t object_type; uint8_t frequency1; uint8_t frequency2:4; uint8_t channels:2; uint8_t rfa:2; uint8_t vbr:1; uint8_t bitrate1:7; uint8_t bitrate2; uint8_t bitrate3; } __attribute__ ((packed)) a2dp_aac_t; typedef struct { a2dp_vendor_codec_t info; uint8_t frequency:4; uint8_t channel_mode:4; } __attribute__ ((packed)) a2dp_aptx_t; typedef struct { a2dp_vendor_codec_t info; uint8_t direction; uint8_t source_frequency:4; uint8_t sink_frequency:4; } __attribute__ ((packed)) a2dp_faststream_t; typedef struct { a2dp_aptx_t aptx; uint8_t reserved:6; uint8_t has_new_caps:1; uint8_t bidirect_link:1; a2dp_aptx_ll_new_caps_t new_caps[0]; } __attribute__ ((packed)) a2dp_aptx_ll_t; #else #error "Unknown byte order" #endif typedef struct { a2dp_aptx_t aptx; uint8_t reserved0; uint8_t reserved1; uint8_t reserved2; uint8_t reserved3; } __attribute__ ((packed)) a2dp_aptx_hd_t; typedef struct { a2dp_vendor_codec_t info; uint8_t data; } __attribute__ ((packed)) a2dp_opus_g_t; bluez-5.82/profiles/audio/PaxHeaders/avrcp.c0000644000000000000000000000005014772767672016102 xustar0020 atime=1743515579 20 ctime=1743591284 bluez-5.82/profiles/audio/avrcp.c0000644000000000000000000035011614772767672015571 0ustar00rootroot// SPDX-License-Identifier: GPL-2.0-or-later /* * * BlueZ - Bluetooth protocol stack for Linux * * Copyright (C) 2006-2010 Nokia Corporation * Copyright (C) 2004-2010 Marcel Holtmann * Copyright (C) 2011 Texas Instruments, Inc. * * */ #ifdef HAVE_CONFIG_H #include #endif #define _GNU_SOURCE #include #include #include #include #include #include #include #include #include #include #include #include #include #include "bluetooth/bluetooth.h" #include "bluetooth/sdp.h" #include "bluetooth/sdp_lib.h" #include "lib/uuid.h" #include "gdbus/gdbus.h" #include "src/plugin.h" #include "src/adapter.h" #include "src/device.h" #include "src/profile.h" #include "src/service.h" #include "src/log.h" #include "src/error.h" #include "src/sdpd.h" #include "src/dbus-common.h" #include "src/shared/timeout.h" #include "src/shared/util.h" #include "src/btd.h" #include "avctp.h" #include "avrcp.h" #include "control.h" #include "media.h" #include "player.h" #include "transport.h" /* Company IDs for vendor dependent commands */ #define IEEEID_BTSIG 0x001958 /* Status codes */ #define AVRCP_STATUS_INVALID_COMMAND 0x00 #define AVRCP_STATUS_INVALID_PARAM 0x01 #define AVRCP_STATUS_PARAM_NOT_FOUND 0x02 #define AVRCP_STATUS_INTERNAL_ERROR 0x03 #define AVRCP_STATUS_SUCCESS 0x04 #define AVRCP_STATUS_UID_CHANGED 0x05 #define AVRCP_STATUS_DOES_NOT_EXIST 0x09 #define AVRCP_STATUS_INVALID_SCOPE 0x0a #define AVRCP_STATUS_OUT_OF_BOUNDS 0x0b #define AVRCP_STATUS_INVALID_PLAYER_ID 0x11 #define AVRCP_STATUS_PLAYER_NOT_BROWSABLE 0x12 #define AVRCP_STATUS_NO_AVAILABLE_PLAYERS 0x15 #define AVRCP_STATUS_ADDRESSED_PLAYER_CHANGED 0x16 /* Packet types */ #define AVRCP_PACKET_TYPE_SINGLE 0x00 #define AVRCP_PACKET_TYPE_START 0x01 #define AVRCP_PACKET_TYPE_CONTINUING 0x02 #define AVRCP_PACKET_TYPE_END 0x03 /* PDU types for metadata transfer */ #define AVRCP_GET_CAPABILITIES 0x10 #define AVRCP_LIST_PLAYER_ATTRIBUTES 0X11 #define AVRCP_LIST_PLAYER_VALUES 0x12 #define AVRCP_GET_CURRENT_PLAYER_VALUE 0x13 #define AVRCP_SET_PLAYER_VALUE 0x14 #define AVRCP_GET_PLAYER_ATTRIBUTE_TEXT 0x15 #define AVRCP_GET_PLAYER_VALUE_TEXT 0x16 #define AVRCP_DISPLAYABLE_CHARSET 0x17 #define AVRCP_CT_BATTERY_STATUS 0x18 #define AVRCP_GET_ELEMENT_ATTRIBUTES 0x20 #define AVRCP_GET_PLAY_STATUS 0x30 #define AVRCP_REGISTER_NOTIFICATION 0x31 #define AVRCP_REQUEST_CONTINUING 0x40 #define AVRCP_ABORT_CONTINUING 0x41 #define AVRCP_SET_ABSOLUTE_VOLUME 0x50 #define AVRCP_SET_ADDRESSED_PLAYER 0x60 #define AVRCP_SET_BROWSED_PLAYER 0x70 #define AVRCP_GET_FOLDER_ITEMS 0x71 #define AVRCP_CHANGE_PATH 0x72 #define AVRCP_GET_ITEM_ATTRIBUTES 0x73 #define AVRCP_PLAY_ITEM 0x74 #define AVRCP_GET_TOTAL_NUMBER_OF_ITEMS 0x75 #define AVRCP_SEARCH 0x80 #define AVRCP_ADD_TO_NOW_PLAYING 0x90 #define AVRCP_GENERAL_REJECT 0xA0 /* Capabilities for AVRCP_GET_CAPABILITIES pdu */ #define CAP_COMPANY_ID 0x02 #define CAP_EVENTS_SUPPORTED 0x03 #define AVRCP_REGISTER_NOTIFICATION_PARAM_LENGTH 5 #define AVRCP_GET_CAPABILITIES_PARAM_LENGTH 1 #define AVRCP_FEATURE_CATEGORY_1 0x0001 #define AVRCP_FEATURE_CATEGORY_2 0x0002 #define AVRCP_FEATURE_CATEGORY_3 0x0004 #define AVRCP_FEATURE_CATEGORY_4 0x0008 #define AVRCP_FEATURE_TG_PLAYER_SETTINGS 0x0010 #define AVRCP_FEATURE_TG_GROUP_NAVIGATION 0x0020 #define AVRCP_FEATURE_BROWSING 0x0040 #define AVRCP_FEATURE_TG_MULTIPLE_PLAYER 0x0080 #define AVRCP_FEATURE_TG_COVERT_ART 0x0100 #define AVRCP_FEATURE_CT_GET_IMAGE_PROP 0x0080 #define AVRCP_FEATURE_CT_GET_IMAGE 0x0100 #define AVRCP_FEATURE_CT_GET_THUMBNAIL 0x0200 #define AVRCP_BATTERY_STATUS_NORMAL 0 #define AVRCP_BATTERY_STATUS_WARNING 1 #define AVRCP_BATTERY_STATUS_CRITICAL 2 #define AVRCP_BATTERY_STATUS_EXTERNAL 3 #define AVRCP_BATTERY_STATUS_FULL_CHARGE 4 #define AVRCP_CHARSET_UTF8 106 #define AVRCP_BROWSING_TIMEOUT 1 #define AVRCP_CT_VERSION 0x0106 #define AVRCP_TG_VERSION 0x0106 #define AVRCP_SCOPE_MEDIA_PLAYER_LIST 0x00 #define AVRCP_SCOPE_MEDIA_PLAYER_VFS 0x01 #define AVRCP_SCOPE_SEARCH 0x02 #define AVRCP_SCOPE_NOW_PLAYING 0x03 #if __BYTE_ORDER == __LITTLE_ENDIAN struct avrcp_header { uint8_t company_id[3]; uint8_t pdu_id; uint8_t packet_type:2; uint8_t rsvd:6; uint16_t params_len; uint8_t params[0]; } __attribute__ ((packed)); #define AVRCP_HEADER_LENGTH 7 #elif __BYTE_ORDER == __BIG_ENDIAN struct avrcp_header { uint8_t company_id[3]; uint8_t pdu_id; uint8_t rsvd:6; uint8_t packet_type:2; uint16_t params_len; uint8_t params[0]; } __attribute__ ((packed)); #define AVRCP_HEADER_LENGTH 7 #else #error "Unknown byte order" #endif #define AVRCP_MTU (AVC_MTU - AVC_HEADER_LENGTH) #define AVRCP_PDU_MTU (AVRCP_MTU - AVRCP_HEADER_LENGTH) struct avrcp_browsing_header { uint8_t pdu_id; uint16_t param_len; uint8_t params[0]; } __attribute__ ((packed)); #define AVRCP_BROWSING_HEADER_LENGTH 3 struct get_folder_items_rsp { uint8_t status; uint16_t uid_counter; uint16_t num_items; uint8_t data[0]; } __attribute__ ((packed)); struct folder_item { uint8_t type; uint16_t len; uint8_t data[0]; } __attribute__ ((packed)); struct player_item { uint16_t player_id; uint8_t type; uint32_t subtype; uint8_t status; uint8_t features[16]; uint16_t charset; uint16_t namelen; char name[0]; } __attribute__ ((packed)); struct get_total_number_of_items_rsp { uint8_t status; uint16_t uid_counter; uint32_t num_items; } __attribute__ ((packed)); struct avrcp_server { struct btd_adapter *adapter; bool browsing; uint32_t tg_record_id; uint32_t ct_record_id; GSList *players; GSList *sessions; }; struct pending_pdu { uint8_t pdu_id; GList *attr_ids; uint16_t offset; }; struct pending_list_items { GSList *items; uint32_t start; uint32_t end; uint64_t total; }; struct avrcp_player { struct avrcp_server *server; GSList *sessions; uint16_t id; uint8_t scope; uint64_t uid; uint16_t uid_counter; bool browsed; bool addressed; uint8_t *features; char *path; guint changed_id; struct pending_list_items *p; char *change_path; uint64_t change_uid; struct avrcp_player_cb *cb; void *user_data; GDestroyNotify destroy; }; struct avrcp_data { struct avrcp_player *player; uint16_t version; int features; uint16_t obex_port; GSList *players; }; struct avrcp { struct avrcp_server *server; struct avctp *conn; struct btd_device *dev; struct avrcp_data *target; struct avrcp_data *controller; const struct passthrough_handler *passthrough_handlers; const struct control_pdu_handler *control_handlers; unsigned int passthrough_id; unsigned int control_id; unsigned int browsing_id; unsigned int browsing_timer; uint16_t supported_events; uint16_t registered_events; uint8_t transaction; uint8_t transaction_events[AVRCP_EVENT_LAST + 1]; struct pending_pdu *pending_pdu; }; struct passthrough_handler { uint8_t op; bool (*func) (struct avrcp *session); }; struct control_pdu_handler { uint8_t pdu_id; uint8_t code; uint8_t (*func) (struct avrcp *session, struct avrcp_header *pdu, uint8_t transaction); }; static const struct { uint8_t feature_bit; uint8_t avc; } passthrough_map[] = { { 0, AVC_SELECT }, { 1, AVC_UP }, { 2, AVC_DOWN }, { 3, AVC_LEFT }, { 4, AVC_RIGHT }, { 5, AVC_RIGHT_UP }, { 6, AVC_RIGHT_DOWN }, { 7, AVC_LEFT_UP }, { 8, AVC_LEFT_DOWN }, { 9, AVC_ROOT_MENU }, { 10, AVC_SETUP_MENU }, { 11, AVC_CONTENTS_MENU }, { 12, AVC_FAVORITE_MENU }, { 13, AVC_EXIT }, { 14, AVC_0 }, { 15, AVC_1 }, { 16, AVC_2 }, { 17, AVC_3 }, { 18, AVC_4 }, { 19, AVC_5 }, { 20, AVC_6 }, { 21, AVC_7 }, { 22, AVC_8 }, { 23, AVC_9 }, { 24, AVC_DOT }, { 25, AVC_ENTER }, { 26, AVC_CLEAR }, { 27, AVC_CHANNEL_UP }, { 28, AVC_CHANNEL_DOWN }, { 29, AVC_CHANNEL_PREVIOUS }, { 30, AVC_SOUND_SELECT }, { 31, AVC_INPUT_SELECT }, { 32, AVC_INFO }, { 33, AVC_HELP }, { 34, AVC_PAGE_UP }, { 35, AVC_PAGE_DOWN }, { 36, AVC_POWER }, { 37, AVC_VOLUME_UP }, { 38, AVC_VOLUME_DOWN }, { 39, AVC_MUTE }, { 40, AVC_PLAY }, { 41, AVC_STOP }, { 42, AVC_PAUSE }, { 43, AVC_RECORD }, { 44, AVC_REWIND }, { 45, AVC_FAST_FORWARD }, { 46, AVC_EJECT }, { 47, AVC_FORWARD }, { 48, AVC_BACKWARD }, { 49, AVC_ANGLE }, { 50, AVC_SUBPICTURE }, { 51, AVC_F1 }, { 52, AVC_F2 }, { 53, AVC_F3 }, { 54, AVC_F4 }, { 55, AVC_F5 }, { 56, AVC_VENDOR_UNIQUE }, { 0xff, 0xff } }; static GSList *servers = NULL; static unsigned int avctp_id = 0; /* Default feature bit mask for media player */ static uint8_t default_features[16]; /* Company IDs supported by this device */ static const uint32_t company_ids[] = { IEEEID_BTSIG, }; static void avrcp_register_notification(struct avrcp *session, uint8_t event); static GList *player_list_settings(struct avrcp_player *player); static void avrcp_browsing_record(sdp_record_t *record, sdp_data_t *version) { sdp_list_t *apseq; uuid_t l2cap, avctp; sdp_list_t *aproto, *proto[2]; sdp_data_t *psm; uint16_t ap = AVCTP_BROWSING_PSM; sdp_uuid16_create(&l2cap, L2CAP_UUID); proto[0] = sdp_list_append(NULL, &l2cap); psm = sdp_data_alloc(SDP_UINT16, &ap); proto[0] = sdp_list_append(proto[0], psm); apseq = sdp_list_append(NULL, proto[0]); sdp_uuid16_create(&avctp, AVCTP_UUID); proto[1] = sdp_list_append(NULL, &avctp); proto[1] = sdp_list_append(proto[1], version); apseq = sdp_list_append(apseq, proto[1]); aproto = sdp_list_append(NULL, apseq); sdp_set_add_access_protos(record, aproto); free(psm); sdp_list_free(proto[0], NULL); sdp_list_free(proto[1], NULL); sdp_list_free(apseq, NULL); sdp_list_free(aproto, NULL); } static sdp_record_t *avrcp_ct_record(bool browsing) { sdp_list_t *svclass_id, *pfseq, *apseq, *root; uuid_t root_uuid, l2cap, avctp, avrct, avrctr; sdp_profile_desc_t profile[1]; sdp_list_t *aproto, *proto[2]; sdp_record_t *record; sdp_data_t *psm[2], *version, *features; uint16_t lp = AVCTP_CONTROL_PSM; uint16_t avctp_ver = 0x0104; uint16_t feat = ( AVRCP_FEATURE_CATEGORY_1 | AVRCP_FEATURE_CATEGORY_2 | AVRCP_FEATURE_CATEGORY_3 | AVRCP_FEATURE_CATEGORY_4 | AVRCP_FEATURE_CT_GET_IMAGE_PROP | AVRCP_FEATURE_CT_GET_IMAGE | AVRCP_FEATURE_CT_GET_THUMBNAIL); record = sdp_record_alloc(); if (!record) return NULL; sdp_uuid16_create(&root_uuid, PUBLIC_BROWSE_GROUP); root = sdp_list_append(NULL, &root_uuid); sdp_set_browse_groups(record, root); /* Service Class ID List */ sdp_uuid16_create(&avrct, AV_REMOTE_SVCLASS_ID); svclass_id = sdp_list_append(NULL, &avrct); sdp_uuid16_create(&avrctr, AV_REMOTE_CONTROLLER_SVCLASS_ID); svclass_id = sdp_list_append(svclass_id, &avrctr); sdp_set_service_classes(record, svclass_id); /* Protocol Descriptor List */ sdp_uuid16_create(&l2cap, L2CAP_UUID); proto[0] = sdp_list_append(NULL, &l2cap); psm[0] = sdp_data_alloc(SDP_UINT16, &lp); proto[0] = sdp_list_append(proto[0], psm[0]); apseq = sdp_list_append(NULL, proto[0]); sdp_uuid16_create(&avctp, AVCTP_UUID); proto[1] = sdp_list_append(NULL, &avctp); version = sdp_data_alloc(SDP_UINT16, &avctp_ver); proto[1] = sdp_list_append(proto[1], version); apseq = sdp_list_append(apseq, proto[1]); aproto = sdp_list_append(NULL, apseq); sdp_set_access_protos(record, aproto); /* Additional Protocol Descriptor List */ if (browsing) { feat |= AVRCP_FEATURE_BROWSING; avrcp_browsing_record(record, version); } /* Bluetooth Profile Descriptor List */ sdp_uuid16_create(&profile[0].uuid, AV_REMOTE_PROFILE_ID); profile[0].version = AVRCP_CT_VERSION; pfseq = sdp_list_append(NULL, &profile[0]); sdp_set_profile_descs(record, pfseq); features = sdp_data_alloc(SDP_UINT16, &feat); sdp_attr_add(record, SDP_ATTR_SUPPORTED_FEATURES, features); sdp_set_info_attr(record, "AVRCP CT", NULL, NULL); free(psm[0]); free(version); sdp_list_free(proto[0], NULL); sdp_list_free(proto[1], NULL); sdp_list_free(apseq, NULL); sdp_list_free(pfseq, NULL); sdp_list_free(aproto, NULL); sdp_list_free(root, NULL); sdp_list_free(svclass_id, NULL); return record; } static sdp_record_t *avrcp_tg_record(bool browsing) { sdp_list_t *svclass_id, *pfseq, *apseq, *root; uuid_t root_uuid, l2cap, avctp, avrtg; sdp_profile_desc_t profile[1]; sdp_list_t *aproto_control, *proto_control[2]; sdp_record_t *record; sdp_data_t *psm_control, *version, *features; uint16_t lp = AVCTP_CONTROL_PSM; uint16_t avctp_ver = 0x0104; uint16_t feat = ( AVRCP_FEATURE_CATEGORY_1 | AVRCP_FEATURE_CATEGORY_2 | AVRCP_FEATURE_CATEGORY_3 | AVRCP_FEATURE_CATEGORY_4 | AVRCP_FEATURE_TG_PLAYER_SETTINGS); record = sdp_record_alloc(); if (!record) return NULL; sdp_uuid16_create(&root_uuid, PUBLIC_BROWSE_GROUP); root = sdp_list_append(NULL, &root_uuid); sdp_set_browse_groups(record, root); /* Service Class ID List */ sdp_uuid16_create(&avrtg, AV_REMOTE_TARGET_SVCLASS_ID); svclass_id = sdp_list_append(NULL, &avrtg); sdp_set_service_classes(record, svclass_id); /* Protocol Descriptor List */ sdp_uuid16_create(&l2cap, L2CAP_UUID); proto_control[0] = sdp_list_append(NULL, &l2cap); psm_control = sdp_data_alloc(SDP_UINT16, &lp); proto_control[0] = sdp_list_append(proto_control[0], psm_control); apseq = sdp_list_append(NULL, proto_control[0]); sdp_uuid16_create(&avctp, AVCTP_UUID); proto_control[1] = sdp_list_append(NULL, &avctp); version = sdp_data_alloc(SDP_UINT16, &avctp_ver); proto_control[1] = sdp_list_append(proto_control[1], version); apseq = sdp_list_append(apseq, proto_control[1]); aproto_control = sdp_list_append(NULL, apseq); sdp_set_access_protos(record, aproto_control); /* Additional Protocol Descriptor List */ if (browsing) { feat |= AVRCP_FEATURE_BROWSING; avrcp_browsing_record(record, version); } /* Bluetooth Profile Descriptor List */ sdp_uuid16_create(&profile[0].uuid, AV_REMOTE_PROFILE_ID); profile[0].version = AVRCP_TG_VERSION; pfseq = sdp_list_append(NULL, &profile[0]); sdp_set_profile_descs(record, pfseq); features = sdp_data_alloc(SDP_UINT16, &feat); sdp_attr_add(record, SDP_ATTR_SUPPORTED_FEATURES, features); sdp_set_info_attr(record, "AVRCP TG", NULL, NULL); free(psm_control); free(version); sdp_list_free(proto_control[0], NULL); sdp_list_free(proto_control[1], NULL); sdp_list_free(apseq, NULL); sdp_list_free(aproto_control, NULL); sdp_list_free(pfseq, NULL); sdp_list_free(root, NULL); sdp_list_free(svclass_id, NULL); return record; } static void populate_default_features(void) { int i; for (i = 0; passthrough_map[i].feature_bit != 0xff; i++) { if (avctp_supports_avc(passthrough_map[i].avc)) { uint8_t bit = passthrough_map[i].feature_bit; default_features[bit >> 3] |= (1 << (bit & 7)); } } /* supports at least AVRCP 1.4 */ default_features[7] |= (1 << 2); /* supports GetTotalNumberOfItems browsing command */ default_features[8] |= (1 << 3); } static unsigned int attr_get_max_val(uint8_t attr) { switch (attr) { case AVRCP_ATTRIBUTE_EQUALIZER: return AVRCP_EQUALIZER_ON; case AVRCP_ATTRIBUTE_REPEAT_MODE: return AVRCP_REPEAT_MODE_GROUP; case AVRCP_ATTRIBUTE_SHUFFLE: return AVRCP_SHUFFLE_GROUP; case AVRCP_ATTRIBUTE_SCAN: return AVRCP_SCAN_GROUP; } return 0; } static const char *battery_status_to_str(uint8_t status) { switch (status) { case AVRCP_BATTERY_STATUS_NORMAL: return "normal"; case AVRCP_BATTERY_STATUS_WARNING: return "warning"; case AVRCP_BATTERY_STATUS_CRITICAL: return "critical"; case AVRCP_BATTERY_STATUS_EXTERNAL: return "external"; case AVRCP_BATTERY_STATUS_FULL_CHARGE: return "fullcharge"; } return NULL; } /* * get_company_id: * * Get three-byte Company_ID from incoming AVRCP message */ static uint32_t get_company_id(const uint8_t cid[3]) { return cid[0] << 16 | cid[1] << 8 | cid[2]; } /* * set_company_id: * * Set three-byte Company_ID into outgoing AVRCP message */ static void set_company_id(uint8_t cid[3], uint32_t cid_in) { cid[0] = (cid_in & 0xff0000) >> 16; cid[1] = (cid_in & 0x00ff00) >> 8; cid[2] = (cid_in & 0x0000ff); } static const char *attr_to_str(uint8_t attr) { switch (attr) { case AVRCP_ATTRIBUTE_EQUALIZER: return "Equalizer"; case AVRCP_ATTRIBUTE_REPEAT_MODE: return "Repeat"; case AVRCP_ATTRIBUTE_SHUFFLE: return "Shuffle"; case AVRCP_ATTRIBUTE_SCAN: return "Scan"; } return NULL; } static int attrval_to_val(uint8_t attr, const char *value) { int ret; switch (attr) { case AVRCP_ATTRIBUTE_EQUALIZER: if (!strcmp(value, "off")) ret = AVRCP_EQUALIZER_OFF; else if (!strcmp(value, "on")) ret = AVRCP_EQUALIZER_ON; else ret = -EINVAL; return ret; case AVRCP_ATTRIBUTE_REPEAT_MODE: if (!strcmp(value, "off")) ret = AVRCP_REPEAT_MODE_OFF; else if (!strcmp(value, "singletrack")) ret = AVRCP_REPEAT_MODE_SINGLE; else if (!strcmp(value, "alltracks")) ret = AVRCP_REPEAT_MODE_ALL; else if (!strcmp(value, "group")) ret = AVRCP_REPEAT_MODE_GROUP; else ret = -EINVAL; return ret; case AVRCP_ATTRIBUTE_SHUFFLE: if (!strcmp(value, "off")) ret = AVRCP_SHUFFLE_OFF; else if (!strcmp(value, "alltracks")) ret = AVRCP_SHUFFLE_ALL; else if (!strcmp(value, "group")) ret = AVRCP_SHUFFLE_GROUP; else ret = -EINVAL; return ret; case AVRCP_ATTRIBUTE_SCAN: if (!strcmp(value, "off")) ret = AVRCP_SCAN_OFF; else if (!strcmp(value, "alltracks")) ret = AVRCP_SCAN_ALL; else if (!strcmp(value, "group")) ret = AVRCP_SCAN_GROUP; else ret = -EINVAL; return ret; } return -EINVAL; } static int attr_to_val(const char *str) { if (!strcasecmp(str, "Equalizer")) return AVRCP_ATTRIBUTE_EQUALIZER; else if (!strcasecmp(str, "Repeat")) return AVRCP_ATTRIBUTE_REPEAT_MODE; else if (!strcasecmp(str, "Shuffle")) return AVRCP_ATTRIBUTE_SHUFFLE; else if (!strcasecmp(str, "Scan")) return AVRCP_ATTRIBUTE_SCAN; return -EINVAL; } static int player_get_setting(struct avrcp_player *player, uint8_t id) { const char *key; const char *value; if (player == NULL) return -ENOENT; key = attr_to_str(id); if (key == NULL) return -EINVAL; value = player->cb->get_setting(key, player->user_data); if (value == NULL) return -EINVAL; return attrval_to_val(id, value); } static int play_status_to_val(const char *status) { if (!strcasecmp(status, "stopped")) return AVRCP_PLAY_STATUS_STOPPED; else if (!strcasecmp(status, "playing")) return AVRCP_PLAY_STATUS_PLAYING; else if (!strcasecmp(status, "paused")) return AVRCP_PLAY_STATUS_PAUSED; else if (!strcasecmp(status, "forward-seek")) return AVRCP_PLAY_STATUS_FWD_SEEK; else if (!strcasecmp(status, "reverse-seek")) return AVRCP_PLAY_STATUS_REV_SEEK; else if (!strcasecmp(status, "error")) return AVRCP_PLAY_STATUS_ERROR; return -EINVAL; } static uint16_t player_settings_changed(struct avrcp_player *player, struct avrcp_header *pdu) { GList *settings = player_list_settings(player); int size = 2; for (; settings; settings = settings->next) { const char *key = settings->data; int attr; int val; attr = attr_to_val(key); if (attr < 0) continue; val = player_get_setting(player, attr); if (val < 0) continue; pdu->params[size++] = attr; pdu->params[size++] = val; } g_list_free(settings); pdu->params[1] = (size - 2) >> 1; return size; } void avrcp_player_event(struct avrcp_player *player, uint8_t id, const void *data) { uint8_t buf[AVRCP_HEADER_LENGTH + 9]; struct avrcp_header *pdu = (void *) buf; uint8_t code; uint16_t size; GSList *l; if (player->sessions == NULL) return; memset(buf, 0, sizeof(buf)); set_company_id(pdu->company_id, IEEEID_BTSIG); pdu->pdu_id = AVRCP_REGISTER_NOTIFICATION; DBG("id=%u", id); if (id != AVRCP_EVENT_ADDRESSED_PLAYER_CHANGED && player->changed_id) { code = AVC_CTYPE_REJECTED; size = 1; pdu->params[0] = AVRCP_STATUS_ADDRESSED_PLAYER_CHANGED; goto done; } code = AVC_CTYPE_CHANGED; pdu->params[0] = id; switch (id) { case AVRCP_EVENT_STATUS_CHANGED: size = 2; pdu->params[1] = play_status_to_val(data); break; case AVRCP_EVENT_TRACK_CHANGED: size = 9; memcpy(&pdu->params[1], data, sizeof(uint64_t)); break; case AVRCP_EVENT_TRACK_REACHED_END: case AVRCP_EVENT_TRACK_REACHED_START: size = 1; break; case AVRCP_EVENT_SETTINGS_CHANGED: size = player_settings_changed(player, pdu); break; case AVRCP_EVENT_ADDRESSED_PLAYER_CHANGED: size = 5; memcpy(&pdu->params[1], &player->id, sizeof(uint16_t)); memcpy(&pdu->params[3], &player->uid_counter, sizeof(uint16_t)); break; case AVRCP_EVENT_AVAILABLE_PLAYERS_CHANGED: size = 1; break; default: error("Unknown event %u", id); return; } done: pdu->params_len = cpu_to_be16(size); for (l = player->sessions; l; l = l->next) { struct avrcp *session = l->data; int err; if (!(session->registered_events & (1 << id))) continue; err = avctp_send_vendordep(session->conn, session->transaction_events[id], code, AVC_SUBUNIT_PANEL, buf, size + AVRCP_HEADER_LENGTH); if (err < 0) continue; /* Unregister event as per AVRCP 1.3 spec, section 5.4.2 */ session->registered_events ^= 1 << id; } return; } static const char *metadata_to_str(uint32_t id) { switch (id) { case AVRCP_MEDIA_ATTRIBUTE_TITLE: return "Title"; case AVRCP_MEDIA_ATTRIBUTE_ARTIST: return "Artist"; case AVRCP_MEDIA_ATTRIBUTE_ALBUM: return "Album"; case AVRCP_MEDIA_ATTRIBUTE_GENRE: return "Genre"; case AVRCP_MEDIA_ATTRIBUTE_TRACK: return "TrackNumber"; case AVRCP_MEDIA_ATTRIBUTE_N_TRACKS: return "NumberOfTracks"; case AVRCP_MEDIA_ATTRIBUTE_DURATION: return "Duration"; case AVRCP_MEDIA_ATTRIBUTE_IMG_HANDLE: return "ImgHandle"; } return NULL; } static const char *player_get_metadata(struct avrcp_player *player, uint32_t id) { const char *key; key = metadata_to_str(id); if (key == NULL) return NULL; if (player != NULL) return player->cb->get_metadata(key, player->user_data); if (id == AVRCP_MEDIA_ATTRIBUTE_TITLE) return ""; return NULL; } static uint16_t player_write_media_attribute(struct avrcp_player *player, uint32_t id, uint8_t *buf, uint16_t *pos, uint16_t *offset) { uint16_t len; uint16_t attr_len; const char *value = NULL; DBG("%u", id); value = player_get_metadata(player, id); if (value == NULL) { *offset = 0; return 0; } attr_len = strlen(value); value = ((char *) value) + *offset; len = attr_len - *offset; if (len > AVRCP_PDU_MTU - *pos) { len = AVRCP_PDU_MTU - *pos; *offset += len; } else { *offset = 0; } memcpy(&buf[*pos], value, len); *pos += len; return attr_len; } struct avrcp_media_attribute_hdr { uint32_t id; uint16_t charset; uint16_t len; } __attribute__ ((packed)); static GList *player_fill_media_attribute(struct avrcp_player *player, GList *attr_ids, uint8_t *buf, uint16_t *pos, uint16_t *offset) { struct avrcp_media_attribute_hdr *hdr = NULL; GList *l; for (l = attr_ids; l != NULL; l = g_list_delete_link(l, l)) { uint32_t attr = GPOINTER_TO_UINT(l->data); uint16_t attr_len; if (*offset == 0) { if (*pos + sizeof(*hdr) >= AVRCP_PDU_MTU) break; hdr = (void *) &buf[*pos]; hdr->id = cpu_to_be32(attr); /* Always use UTF-8 */ hdr->charset = cpu_to_be16(AVRCP_CHARSET_UTF8); *pos += sizeof(*hdr); } attr_len = player_write_media_attribute(player, attr, buf, pos, offset); if (hdr != NULL) hdr->len = cpu_to_be16(attr_len); if (*offset > 0) break; } return l; } static struct pending_pdu *pending_pdu_new(uint8_t pdu_id, GList *attr_ids, unsigned int offset) { struct pending_pdu *pending = g_new(struct pending_pdu, 1); pending->pdu_id = pdu_id; pending->attr_ids = attr_ids; pending->offset = offset; return pending; } static gboolean session_abort_pending_pdu(struct avrcp *session) { if (session->pending_pdu == NULL) return FALSE; g_list_free(session->pending_pdu->attr_ids); g_free(session->pending_pdu); session->pending_pdu = NULL; return TRUE; } static const char *attrval_to_str(uint8_t attr, uint8_t value) { switch (attr) { case AVRCP_ATTRIBUTE_EQUALIZER: switch (value) { case AVRCP_EQUALIZER_ON: return "on"; case AVRCP_EQUALIZER_OFF: return "off"; } break; case AVRCP_ATTRIBUTE_REPEAT_MODE: switch (value) { case AVRCP_REPEAT_MODE_OFF: return "off"; case AVRCP_REPEAT_MODE_SINGLE: return "singletrack"; case AVRCP_REPEAT_MODE_ALL: return "alltracks"; case AVRCP_REPEAT_MODE_GROUP: return "group"; } break; /* Shuffle and scan have the same values */ case AVRCP_ATTRIBUTE_SHUFFLE: case AVRCP_ATTRIBUTE_SCAN: switch (value) { case AVRCP_SCAN_OFF: return "off"; case AVRCP_SCAN_ALL: return "alltracks"; case AVRCP_SCAN_GROUP: return "group"; } break; } return NULL; } static int player_set_setting(struct avrcp_player *player, uint8_t id, uint8_t val) { const char *key, *value; key = attr_to_str(id); if (key == NULL) return -EINVAL; value = attrval_to_str(id, val); if (value == NULL) return -EINVAL; if (player == NULL) return -ENOENT; return player->cb->set_setting(key, value, player->user_data); } static uint8_t avrcp_handle_get_capabilities(struct avrcp *session, struct avrcp_header *pdu, uint8_t transaction) { uint16_t len = be16_to_cpu(pdu->params_len); unsigned int i; if (len != 1) goto err; DBG("id=%u", pdu->params[0]); switch (pdu->params[0]) { case CAP_COMPANY_ID: for (i = 0; i < G_N_ELEMENTS(company_ids); i++) { set_company_id(&pdu->params[2 + i * 3], company_ids[i]); } pdu->params_len = cpu_to_be16(2 + (3 * G_N_ELEMENTS(company_ids))); pdu->params[1] = G_N_ELEMENTS(company_ids); return AVC_CTYPE_STABLE; case CAP_EVENTS_SUPPORTED: pdu->params[1] = 0; for (i = 1; i <= AVRCP_EVENT_LAST; i++) { if (session->supported_events & (1 << i)) { pdu->params[1]++; pdu->params[pdu->params[1] + 1] = i; } } pdu->params_len = cpu_to_be16(2 + pdu->params[1]); return AVC_CTYPE_STABLE; } err: pdu->params_len = cpu_to_be16(1); pdu->params[0] = AVRCP_STATUS_INVALID_PARAM; return AVC_CTYPE_REJECTED; } static struct avrcp_player *target_get_player(struct avrcp *session) { if (!session->target) return NULL; return session->target->player; } static uint8_t avrcp_handle_list_player_attributes(struct avrcp *session, struct avrcp_header *pdu, uint8_t transaction) { struct avrcp_player *player = target_get_player(session); uint16_t len = be16_to_cpu(pdu->params_len); unsigned int i; if (len != 0) { pdu->params_len = cpu_to_be16(1); pdu->params[0] = AVRCP_STATUS_INVALID_PARAM; return AVC_CTYPE_REJECTED; } if (!player) goto done; for (i = 1; i <= AVRCP_ATTRIBUTE_SCAN; i++) { if (player_get_setting(player, i) < 0) continue; len++; pdu->params[len] = i; } done: pdu->params[0] = len; pdu->params_len = cpu_to_be16(len + 1); return AVC_CTYPE_STABLE; } static uint8_t avrcp_handle_list_player_values(struct avrcp *session, struct avrcp_header *pdu, uint8_t transaction) { struct avrcp_player *player = target_get_player(session); uint16_t len = be16_to_cpu(pdu->params_len); unsigned int i; if (len != 1) goto err; if (player_get_setting(player, pdu->params[0]) < 0) goto err; len = attr_get_max_val(pdu->params[0]); for (i = 1; i <= len; i++) pdu->params[i] = i; pdu->params[0] = len; pdu->params_len = cpu_to_be16(len + 1); return AVC_CTYPE_STABLE; err: pdu->params_len = cpu_to_be16(1); pdu->params[0] = AVRCP_STATUS_INVALID_PARAM; return AVC_CTYPE_REJECTED; } static uint32_t str_to_metadata(const char *str) { if (strcasecmp(str, "Title") == 0) return AVRCP_MEDIA_ATTRIBUTE_TITLE; else if (strcasecmp(str, "Artist") == 0) return AVRCP_MEDIA_ATTRIBUTE_ARTIST; else if (strcasecmp(str, "Album") == 0) return AVRCP_MEDIA_ATTRIBUTE_ALBUM; else if (strcasecmp(str, "Genre") == 0) return AVRCP_MEDIA_ATTRIBUTE_GENRE; else if (strcasecmp(str, "TrackNumber") == 0) return AVRCP_MEDIA_ATTRIBUTE_TRACK; else if (strcasecmp(str, "NumberOfTracks") == 0) return AVRCP_MEDIA_ATTRIBUTE_N_TRACKS; else if (strcasecmp(str, "Duration") == 0) return AVRCP_MEDIA_ATTRIBUTE_DURATION; else if (strcasecmp(str, "ImgHandle") == 0) return AVRCP_MEDIA_ATTRIBUTE_IMG_HANDLE; return 0; } static GList *player_list_metadata(struct avrcp_player *player) { GList *l, *attrs = NULL; if (player == NULL) return g_list_prepend(NULL, GUINT_TO_POINTER(AVRCP_MEDIA_ATTRIBUTE_TITLE)); l = player->cb->list_metadata(player->user_data); for (; l; l = l->next) { const char *key = l->data; attrs = g_list_append(attrs, GUINT_TO_POINTER(str_to_metadata(key))); } if (attrs == NULL) return g_list_prepend(NULL, GUINT_TO_POINTER(AVRCP_MEDIA_ATTRIBUTE_TITLE)); return attrs; } static uint8_t avrcp_handle_get_element_attributes(struct avrcp *session, struct avrcp_header *pdu, uint8_t transaction) { struct avrcp_player *player = target_get_player(session); uint16_t len = be16_to_cpu(pdu->params_len); uint64_t identifier = get_le64(&pdu->params[0]); uint16_t pos; uint8_t nattr; GList *attr_ids; uint16_t offset; if (len < 9 || identifier != 0) goto err; nattr = pdu->params[8]; if (len < nattr * sizeof(uint32_t) + 1) goto err; if (!nattr) { /* * Return all available information, at least * title must be returned if there's a track selected. */ attr_ids = player_list_metadata(player); len = g_list_length(attr_ids); } else { unsigned int i; for (i = 0, len = 0, attr_ids = NULL; i < nattr; i++) { uint32_t id; id = get_be32(&pdu->params[9] + (i * sizeof(id))); /* Don't add invalid attributes */ if (id == AVRCP_MEDIA_ATTRIBUTE_ILLEGAL || id > AVRCP_MEDIA_ATTRIBUTE_LAST) continue; len++; attr_ids = g_list_prepend(attr_ids, GUINT_TO_POINTER(id)); } attr_ids = g_list_reverse(attr_ids); } if (!len) goto err; session_abort_pending_pdu(session); pos = 1; offset = 0; attr_ids = player_fill_media_attribute(player, attr_ids, pdu->params, &pos, &offset); if (attr_ids != NULL) { session->pending_pdu = pending_pdu_new(pdu->pdu_id, attr_ids, offset); pdu->packet_type = AVRCP_PACKET_TYPE_START; } pdu->params[0] = len; pdu->params_len = cpu_to_be16(pos); return AVC_CTYPE_STABLE; err: pdu->params_len = cpu_to_be16(1); pdu->params[0] = AVRCP_STATUS_INVALID_PARAM; return AVC_CTYPE_REJECTED; } static uint8_t avrcp_handle_get_current_player_value(struct avrcp *session, struct avrcp_header *pdu, uint8_t transaction) { struct avrcp_player *player = target_get_player(session); uint16_t len = be16_to_cpu(pdu->params_len); uint8_t *settings; unsigned int i; if (len <= 1 || pdu->params[0] != len - 1) goto err; /* * Save a copy of requested settings because we can override them * while responding */ settings = util_memdup(&pdu->params[1], pdu->params[0]); len = 0; /* * From sec. 5.7 of AVRCP 1.3 spec, we should igore non-existent IDs * and send a response with the existent ones. Only if all IDs are * non-existent we should send an error. */ for (i = 0; i < pdu->params[0]; i++) { int val; if (settings[i] < AVRCP_ATTRIBUTE_EQUALIZER || settings[i] > AVRCP_ATTRIBUTE_SCAN) { DBG("Ignoring %u", settings[i]); continue; } val = player_get_setting(player, settings[i]); if (val < 0) continue; pdu->params[++len] = settings[i]; pdu->params[++len] = val; } free(settings); if (len) { pdu->params[0] = len / 2; pdu->params_len = cpu_to_be16(len + 1); return AVC_CTYPE_STABLE; } error("No valid attributes in request"); err: pdu->params_len = cpu_to_be16(1); pdu->params[0] = AVRCP_STATUS_INVALID_PARAM; return AVC_CTYPE_REJECTED; } static uint8_t avrcp_handle_set_player_value(struct avrcp *session, struct avrcp_header *pdu, uint8_t transaction) { struct avrcp_player *player = target_get_player(session); uint16_t len = be16_to_cpu(pdu->params_len); unsigned int i; uint8_t *param; if (len < 3 || len > 2 * pdu->params[0] + 1U || player == NULL) goto err; /* * From sec. 5.7 of AVRCP 1.3 spec, we should igore non-existent IDs * and set the existent ones. Sec. 5.2.4 is not clear however how to * indicate that a certain ID was not accepted. If at least one * attribute is valid, we respond with no parameters. Otherwise an * AVRCP_STATUS_INVALID_PARAM is sent. */ for (len = 0, i = 0, param = &pdu->params[1]; i < pdu->params[0]; i++, param += 2) { if (player_set_setting(player, param[0], param[1]) < 0) continue; len++; } if (len) { pdu->params_len = 0; return AVC_CTYPE_ACCEPTED; } err: pdu->params_len = cpu_to_be16(1); pdu->params[0] = AVRCP_STATUS_INVALID_PARAM; return AVC_CTYPE_REJECTED; } static uint8_t avrcp_handle_displayable_charset(struct avrcp *session, struct avrcp_header *pdu, uint8_t transaction) { uint16_t len = be16_to_cpu(pdu->params_len); if (len < 3) { pdu->params_len = cpu_to_be16(1); pdu->params[0] = AVRCP_STATUS_INVALID_PARAM; return AVC_CTYPE_REJECTED; } /* * We acknowledge the commands, but we always use UTF-8 for * encoding since CT is obliged to support it. */ pdu->params_len = 0; return AVC_CTYPE_STABLE; } static uint8_t avrcp_handle_ct_battery_status(struct avrcp *session, struct avrcp_header *pdu, uint8_t transaction) { uint16_t len = be16_to_cpu(pdu->params_len); const char *valstr; if (len != 1) goto err; valstr = battery_status_to_str(pdu->params[0]); if (valstr == NULL) goto err; pdu->params_len = 0; return AVC_CTYPE_STABLE; err: pdu->params_len = cpu_to_be16(1); pdu->params[0] = AVRCP_STATUS_INVALID_PARAM; return AVC_CTYPE_REJECTED; } static uint32_t player_get_position(struct avrcp_player *player) { if (player == NULL) return 0; return player->cb->get_position(player->user_data); } static uint32_t player_get_duration(struct avrcp_player *player) { uint32_t num; if (player == NULL) return UINT32_MAX; num = player->cb->get_duration(player->user_data); if (num == 0) return UINT32_MAX; return num; } static uint8_t player_get_status(struct avrcp_player *player) { const char *value; if (player == NULL) return AVRCP_PLAY_STATUS_STOPPED; value = player->cb->get_status(player->user_data); if (value == NULL) return AVRCP_PLAY_STATUS_STOPPED; return play_status_to_val(value); } static uint16_t player_get_id(struct avrcp_player *player) { if (player == NULL) return 0x0000; return player->id; } static uint16_t player_get_uid_counter(struct avrcp_player *player) { if (player == NULL) return 0x0000; return player->uid_counter; } static uint8_t avrcp_handle_get_play_status(struct avrcp *session, struct avrcp_header *pdu, uint8_t transaction) { struct avrcp_player *player = target_get_player(session); uint16_t len = be16_to_cpu(pdu->params_len); uint32_t position; uint32_t duration; if (len != 0) { pdu->params_len = cpu_to_be16(1); pdu->params[0] = AVRCP_STATUS_INVALID_PARAM; return AVC_CTYPE_REJECTED; } position = player_get_position(player); duration = player_get_duration(player); put_be32(duration, &pdu->params[0]); put_be32(position, &pdu->params[4]); pdu->params[8] = player_get_status(player); pdu->params_len = cpu_to_be16(9); return AVC_CTYPE_STABLE; } static uint64_t player_get_uid(struct avrcp_player *player) { if (player == NULL) return UINT64_MAX; return player->cb->get_uid(player->user_data); } static GList *player_list_settings(struct avrcp_player *player) { if (player == NULL) return NULL; return player->cb->list_settings(player->user_data); } static bool avrcp_handle_play(struct avrcp *session) { struct avrcp_player *player = target_get_player(session); if (player == NULL) return false; return player->cb->play(player->user_data); } static bool avrcp_handle_stop(struct avrcp *session) { struct avrcp_player *player = target_get_player(session); if (player == NULL) return false; return player->cb->stop(player->user_data); } static bool avrcp_handle_pause(struct avrcp *session) { struct avrcp_player *player = target_get_player(session); if (player == NULL) return false; return player->cb->pause(player->user_data); } static bool avrcp_handle_next(struct avrcp *session) { struct avrcp_player *player = target_get_player(session); if (player == NULL) return false; return player->cb->next(player->user_data); } static bool avrcp_handle_previous(struct avrcp *session) { struct avrcp_player *player = target_get_player(session); if (player == NULL) return false; return player->cb->previous(player->user_data); } static const struct passthrough_handler passthrough_handlers[] = { { AVC_PLAY, avrcp_handle_play }, { AVC_STOP, avrcp_handle_stop }, { AVC_PAUSE, avrcp_handle_pause }, { AVC_FORWARD, avrcp_handle_next }, { AVC_BACKWARD, avrcp_handle_previous }, { }, }; static bool handle_passthrough(struct avctp *conn, uint8_t op, bool pressed, void *user_data) { struct avrcp *session = user_data; const struct passthrough_handler *handler; for (handler = session->passthrough_handlers; handler->func; handler++) { if (handler->op == op) break; } if (handler->func == NULL) return false; /* Do not trigger handler on release */ if (!pressed) return true; return handler->func(session); } static uint8_t avrcp_handle_register_notification(struct avrcp *session, struct avrcp_header *pdu, uint8_t transaction) { struct avrcp_player *player = target_get_player(session); struct btd_device *dev = session->dev; uint16_t len = be16_to_cpu(pdu->params_len); uint64_t uid; int8_t volume; /* * 1 byte for EventID, 4 bytes for Playback interval but the latest * one is applicable only for EVENT_PLAYBACK_POS_CHANGED. See AVRCP * 1.3 spec, section 5.4.2. */ if (len != 5) goto err; /* Check if event is supported otherwise reject */ if (!(session->supported_events & (1 << pdu->params[0]))) goto err; switch (pdu->params[0]) { case AVRCP_EVENT_STATUS_CHANGED: len = 2; pdu->params[1] = player_get_status(player); break; case AVRCP_EVENT_TRACK_CHANGED: len = 9; uid = player_get_uid(player); memcpy(&pdu->params[1], &uid, sizeof(uint64_t)); break; case AVRCP_EVENT_TRACK_REACHED_END: case AVRCP_EVENT_TRACK_REACHED_START: len = 1; break; case AVRCP_EVENT_SETTINGS_CHANGED: len = player_settings_changed(player, pdu); break; case AVRCP_EVENT_ADDRESSED_PLAYER_CHANGED: len = 5; bt_put_be16(player_get_id(player), &pdu->params[1]); bt_put_be16(player_get_uid_counter(player), &pdu->params[3]); break; case AVRCP_EVENT_AVAILABLE_PLAYERS_CHANGED: len = 1; break; case AVRCP_EVENT_VOLUME_CHANGED: volume = media_transport_get_device_volume(dev); if (volume < 0) goto err; pdu->params[1] = volume; len = 2; break; default: /* All other events are not supported yet */ goto err; } /* Register event and save the transaction used */ session->registered_events |= (1 << pdu->params[0]); session->transaction_events[pdu->params[0]] = transaction; pdu->params_len = cpu_to_be16(len); return AVC_CTYPE_INTERIM; err: pdu->params_len = cpu_to_be16(1); pdu->params[0] = AVRCP_STATUS_INVALID_PARAM; return AVC_CTYPE_REJECTED; } static uint8_t avrcp_handle_request_continuing(struct avrcp *session, struct avrcp_header *pdu, uint8_t transaction) { struct avrcp_player *player = target_get_player(session); uint16_t len = be16_to_cpu(pdu->params_len); struct pending_pdu *pending; if (len != 1 || session->pending_pdu == NULL) goto err; pending = session->pending_pdu; if (pending->pdu_id != pdu->params[0]) goto err; len = 0; pending->attr_ids = player_fill_media_attribute(player, pending->attr_ids, pdu->params, &len, &pending->offset); pdu->pdu_id = pending->pdu_id; if (pending->attr_ids == NULL) { g_free(session->pending_pdu); session->pending_pdu = NULL; pdu->packet_type = AVRCP_PACKET_TYPE_END; } else { pdu->packet_type = AVRCP_PACKET_TYPE_CONTINUING; } pdu->params_len = cpu_to_be16(len); return AVC_CTYPE_STABLE; err: pdu->params_len = cpu_to_be16(1); pdu->params[0] = AVRCP_STATUS_INVALID_PARAM; return AVC_CTYPE_REJECTED; } static uint8_t avrcp_handle_abort_continuing(struct avrcp *session, struct avrcp_header *pdu, uint8_t transaction) { uint16_t len = be16_to_cpu(pdu->params_len); struct pending_pdu *pending; if (len != 1 || session->pending_pdu == NULL) goto err; pending = session->pending_pdu; if (pending->pdu_id != pdu->params[0]) goto err; session_abort_pending_pdu(session); pdu->params_len = 0; return AVC_CTYPE_ACCEPTED; err: pdu->params_len = cpu_to_be16(1); pdu->params[0] = AVRCP_STATUS_INVALID_PARAM; return AVC_CTYPE_REJECTED; } /* SetAbsoluteVolume requires at least version 1.4 and a category-2 */ static bool avrcp_volume_supported(struct avrcp_data *data) { if (!data || data->version < 0x0104) return false; if (btd_opts.avrcp.volume_category && !(data->features & AVRCP_FEATURE_CATEGORY_2)) return false; return true; } static uint8_t avrcp_handle_set_absolute_volume(struct avrcp *session, struct avrcp_header *pdu, uint8_t transaction) { uint16_t len = be16_to_cpu(pdu->params_len); int8_t volume; if (len != 1) goto err; if (!avrcp_volume_supported(session->target)) { error("SetAbsoluteVolume not supported"); goto err; } volume = pdu->params[0] = pdu->params[0] & 0x7F; media_transport_update_device_volume(session->dev, volume); return AVC_CTYPE_ACCEPTED; err: pdu->params_len = cpu_to_be16(1); pdu->params[0] = AVRCP_STATUS_INVALID_PARAM; return AVC_CTYPE_REJECTED; } static struct avrcp_player *find_tg_player(struct avrcp *session, uint16_t id) { struct avrcp_server *server = session->server; GSList *l; for (l = server->players; l; l = l->next) { struct avrcp_player *player = l->data; if (player->id == id) return player; } return NULL; } static gboolean notify_addressed_player_changed(gpointer user_data) { struct avrcp_player *player = user_data; uint8_t events[6] = { AVRCP_EVENT_STATUS_CHANGED, AVRCP_EVENT_TRACK_CHANGED, AVRCP_EVENT_TRACK_REACHED_START, AVRCP_EVENT_TRACK_REACHED_END, AVRCP_EVENT_SETTINGS_CHANGED, AVRCP_EVENT_PLAYBACK_POS_CHANGED }; uint8_t i; /* * Set changed_id to an non-zero value to indicate addreddsed player * changed. */ player->changed_id = 1; avrcp_player_event(player, AVRCP_EVENT_ADDRESSED_PLAYER_CHANGED, NULL); /* * TG shall complete all player specific * notifications with AV/C C-Type REJECTED * with error code as Addressed Player Changed. */ for (i = 0; i < sizeof(events); i++) avrcp_player_event(player, events[i], NULL); player->changed_id = 0; return FALSE; } static uint8_t avrcp_handle_set_addressed_player(struct avrcp *session, struct avrcp_header *pdu, uint8_t transaction) { struct avrcp_player *player; uint16_t len = be16_to_cpu(pdu->params_len); uint16_t player_id = 0; uint8_t status; if (len < 1) { status = AVRCP_STATUS_INVALID_PARAM; goto err; } player_id = bt_get_be16(&pdu->params[0]); player = find_tg_player(session, player_id); pdu->packet_type = AVRCP_PACKET_TYPE_SINGLE; if (player) { player->addressed = true; status = AVRCP_STATUS_SUCCESS; pdu->params_len = cpu_to_be16(len); pdu->params[0] = status; } else { status = AVRCP_STATUS_INVALID_PLAYER_ID; goto err; } /* Don't emit player changed immediately since PTS expect the * response of SetAddressedPlayer before the event. */ player->changed_id = g_idle_add(notify_addressed_player_changed, player); return AVC_CTYPE_ACCEPTED; err: pdu->params_len = cpu_to_be16(sizeof(status)); pdu->params[0] = status; return AVC_CTYPE_REJECTED; } static const struct control_pdu_handler control_handlers[] = { { AVRCP_GET_CAPABILITIES, AVC_CTYPE_STATUS, avrcp_handle_get_capabilities }, { AVRCP_LIST_PLAYER_ATTRIBUTES, AVC_CTYPE_STATUS, avrcp_handle_list_player_attributes }, { AVRCP_LIST_PLAYER_VALUES, AVC_CTYPE_STATUS, avrcp_handle_list_player_values }, { AVRCP_GET_ELEMENT_ATTRIBUTES, AVC_CTYPE_STATUS, avrcp_handle_get_element_attributes }, { AVRCP_GET_CURRENT_PLAYER_VALUE, AVC_CTYPE_STATUS, avrcp_handle_get_current_player_value }, { AVRCP_SET_PLAYER_VALUE, AVC_CTYPE_CONTROL, avrcp_handle_set_player_value }, { AVRCP_GET_PLAYER_ATTRIBUTE_TEXT, AVC_CTYPE_STATUS, NULL }, { AVRCP_GET_PLAYER_VALUE_TEXT, AVC_CTYPE_STATUS, NULL }, { AVRCP_DISPLAYABLE_CHARSET, AVC_CTYPE_STATUS, avrcp_handle_displayable_charset }, { AVRCP_CT_BATTERY_STATUS, AVC_CTYPE_STATUS, avrcp_handle_ct_battery_status }, { AVRCP_GET_PLAY_STATUS, AVC_CTYPE_STATUS, avrcp_handle_get_play_status }, { AVRCP_REGISTER_NOTIFICATION, AVC_CTYPE_NOTIFY, avrcp_handle_register_notification }, { AVRCP_SET_ABSOLUTE_VOLUME, AVC_CTYPE_CONTROL, avrcp_handle_set_absolute_volume }, { AVRCP_REQUEST_CONTINUING, AVC_CTYPE_CONTROL, avrcp_handle_request_continuing }, { AVRCP_ABORT_CONTINUING, AVC_CTYPE_CONTROL, avrcp_handle_abort_continuing }, { AVRCP_SET_ADDRESSED_PLAYER, AVC_CTYPE_CONTROL, avrcp_handle_set_addressed_player }, { }, }; /* handle vendordep pdu inside an avctp packet */ static size_t handle_vendordep_pdu(struct avctp *conn, uint8_t transaction, uint8_t *code, uint8_t *subunit, uint8_t *operands, size_t operand_count, void *user_data) { struct avrcp *session = user_data; const struct control_pdu_handler *handler; struct avrcp_header *pdu = (void *) operands; uint32_t company_id = get_company_id(pdu->company_id); if (company_id != IEEEID_BTSIG) { *code = AVC_CTYPE_NOT_IMPLEMENTED; return 0; } DBG("AVRCP PDU 0x%02X, company 0x%06X len 0x%04X", pdu->pdu_id, company_id, be16_to_cpu(pdu->params_len)); pdu->packet_type = 0; pdu->rsvd = 0; if (operand_count < AVRCP_HEADER_LENGTH) { pdu->params[0] = AVRCP_STATUS_INVALID_COMMAND; goto err_metadata; } operands += sizeof(*pdu); operand_count -= sizeof(*pdu); if (be16_to_cpu(pdu->params_len) != operand_count) { DBG("AVRCP PDU parameters length don't match"); pdu->params[0] = AVRCP_STATUS_PARAM_NOT_FOUND; goto err_metadata; } for (handler = session->control_handlers; handler->pdu_id; handler++) { if (handler->pdu_id == pdu->pdu_id) break; } if (handler->pdu_id != pdu->pdu_id || handler->code != *code) { pdu->params[0] = AVRCP_STATUS_INVALID_COMMAND; goto err_metadata; } if (!handler->func) { pdu->params[0] = AVRCP_STATUS_INVALID_PARAM; goto err_metadata; } *code = handler->func(session, pdu, transaction); if (*code != AVC_CTYPE_REJECTED && pdu->pdu_id != AVRCP_GET_ELEMENT_ATTRIBUTES && pdu->pdu_id != AVRCP_REQUEST_CONTINUING && pdu->pdu_id != AVRCP_ABORT_CONTINUING) session_abort_pending_pdu(session); return AVRCP_HEADER_LENGTH + be16_to_cpu(pdu->params_len); err_metadata: pdu->params_len = cpu_to_be16(1); *code = AVC_CTYPE_REJECTED; return AVRCP_HEADER_LENGTH + 1; } static void avrcp_handle_media_player_list(struct avrcp *session, struct avrcp_browsing_header *pdu, uint32_t start_item, uint32_t end_item) { struct avrcp_player *player = session->target->player; struct get_folder_items_rsp *rsp; const char *name = NULL; GSList *l; rsp = (void *)pdu->params; rsp->status = AVRCP_STATUS_SUCCESS; rsp->uid_counter = cpu_to_be16(player_get_uid_counter(player)); rsp->num_items = 0; pdu->param_len = sizeof(*rsp); for (l = g_slist_nth(session->server->players, start_item); l; l = g_slist_next(l)) { struct avrcp_player *player = l->data; struct folder_item *folder; struct player_item *item; uint16_t namelen; if (rsp->num_items == (end_item - start_item) + 1) break; folder = (void *)&pdu->params[pdu->param_len]; folder->type = 0x01; /* Media Player */ pdu->param_len += sizeof(*folder); item = (void *)folder->data; item->player_id = cpu_to_be16(player->id); item->type = 0x01; /* Audio */ item->subtype = cpu_to_be32(0x00000001); /* Audio Book */ item->status = player_get_status(player); /* Assign Default Feature Bit Mask */ memcpy(&item->features, &default_features, sizeof(default_features)); item->charset = cpu_to_be16(AVRCP_CHARSET_UTF8); name = player->cb->get_name(player->user_data); namelen = strlen(name); item->namelen = cpu_to_be16(namelen); memcpy(item->name, name, namelen); folder->len = cpu_to_be16(sizeof(*item) + namelen); pdu->param_len += sizeof(*item) + namelen; rsp->num_items++; } /* If no player could be found respond with an error */ if (!rsp->num_items) goto failed; rsp->num_items = cpu_to_be16(rsp->num_items); pdu->param_len = cpu_to_be16(pdu->param_len); return; failed: pdu->params[0] = AVRCP_STATUS_OUT_OF_BOUNDS; pdu->param_len = cpu_to_be16(1); } static void avrcp_handle_get_folder_items(struct avrcp *session, struct avrcp_browsing_header *pdu, uint8_t transaction) { uint32_t start_item = 0; uint32_t end_item = 0; uint8_t scope; uint8_t status = AVRCP_STATUS_SUCCESS; if (be16_to_cpu(pdu->param_len) < 10) { status = AVRCP_STATUS_INVALID_PARAM; goto failed; } scope = pdu->params[0]; start_item = bt_get_be32(&pdu->params[1]); end_item = bt_get_be32(&pdu->params[5]); DBG("scope 0x%02x start_item 0x%08x end_item 0x%08x", scope, start_item, end_item); if (end_item < start_item) { status = AVRCP_STATUS_INVALID_PARAM; goto failed; } switch (scope) { case AVRCP_SCOPE_MEDIA_PLAYER_LIST: avrcp_handle_media_player_list(session, pdu, start_item, end_item); break; case AVRCP_SCOPE_MEDIA_PLAYER_VFS: case AVRCP_SCOPE_SEARCH: case AVRCP_SCOPE_NOW_PLAYING: default: status = AVRCP_STATUS_INVALID_SCOPE; goto failed; } return; failed: pdu->params[0] = status; pdu->param_len = cpu_to_be16(1); } static void avrcp_handle_media_player_list_num_items(struct avrcp *session, struct avrcp_browsing_header *pdu) { struct avrcp_player *player = target_get_player(session); struct get_total_number_of_items_rsp *rsp; rsp = (void *)pdu->params; rsp->status = AVRCP_STATUS_SUCCESS; rsp->uid_counter = cpu_to_be16(player_get_uid_counter(player)); rsp->num_items = cpu_to_be32(g_slist_length(session->server->players)); pdu->param_len = cpu_to_be16(sizeof(*rsp)); } static void avrcp_handle_get_total_number_of_items(struct avrcp *session, struct avrcp_browsing_header *pdu, uint8_t transaction) { uint8_t scope; uint8_t status = AVRCP_STATUS_SUCCESS; if (be16_to_cpu(pdu->param_len) != 1) { status = AVRCP_STATUS_INVALID_PARAM; goto failed; } scope = pdu->params[0]; switch (scope) { case AVRCP_SCOPE_MEDIA_PLAYER_LIST: avrcp_handle_media_player_list_num_items(session, pdu); break; case AVRCP_SCOPE_MEDIA_PLAYER_VFS: case AVRCP_SCOPE_SEARCH: case AVRCP_SCOPE_NOW_PLAYING: default: status = AVRCP_STATUS_INVALID_SCOPE; goto failed; } return; failed: pdu->params[0] = status; pdu->param_len = cpu_to_be16(1); } static const struct browsing_pdu_handler { uint8_t pdu_id; void (*func) (struct avrcp *session, struct avrcp_browsing_header *pdu, uint8_t transaction); } browsing_handlers[] = { { AVRCP_GET_FOLDER_ITEMS, avrcp_handle_get_folder_items }, { AVRCP_GET_TOTAL_NUMBER_OF_ITEMS, avrcp_handle_get_total_number_of_items }, { }, }; size_t avrcp_browsing_general_reject(uint8_t *operands) { struct avrcp_browsing_header *pdu = (void *) operands; uint8_t status; pdu->pdu_id = AVRCP_GENERAL_REJECT; status = AVRCP_STATUS_INVALID_COMMAND; pdu->param_len = cpu_to_be16(sizeof(status)); memcpy(pdu->params, &status, (sizeof(status))); return AVRCP_BROWSING_HEADER_LENGTH + sizeof(status); } static size_t handle_browsing_pdu(struct avctp *conn, uint8_t transaction, uint8_t *operands, size_t operand_count, void *user_data) { struct avrcp *session = user_data; const struct browsing_pdu_handler *handler; struct avrcp_browsing_header *pdu = (void *) operands; DBG("AVRCP Browsing PDU 0x%02X, len 0x%04X", pdu->pdu_id, be16_to_cpu(pdu->param_len)); for (handler = browsing_handlers; handler->pdu_id; handler++) { if (handler->pdu_id == pdu->pdu_id) goto done; } return avrcp_browsing_general_reject(operands); done: session->transaction = transaction; handler->func(session, pdu, transaction); return AVRCP_BROWSING_HEADER_LENGTH + be16_to_cpu(pdu->param_len); } size_t avrcp_handle_vendor_reject(uint8_t *code, uint8_t *operands) { struct avrcp_header *pdu = (void *) operands; uint32_t company_id = get_company_id(pdu->company_id); *code = AVC_CTYPE_REJECTED; pdu->params_len = cpu_to_be16(1); pdu->params[0] = AVRCP_STATUS_INTERNAL_ERROR; DBG("rejecting AVRCP PDU 0x%02X, company 0x%06X len 0x%04X", pdu->pdu_id, company_id, be16_to_cpu(pdu->params_len)); return AVRCP_HEADER_LENGTH + 1; } static struct avrcp_server *find_server(GSList *list, struct btd_adapter *a) { for (; list; list = list->next) { struct avrcp_server *server = list->data; if (server->adapter == a) return server; } return NULL; } static const char *status_to_string(uint8_t status) { switch (status) { case AVRCP_PLAY_STATUS_STOPPED: return "stopped"; case AVRCP_PLAY_STATUS_PLAYING: return "playing"; case AVRCP_PLAY_STATUS_PAUSED: return "paused"; case AVRCP_PLAY_STATUS_FWD_SEEK: return "forward-seek"; case AVRCP_PLAY_STATUS_REV_SEEK: return "reverse-seek"; case AVRCP_PLAY_STATUS_ERROR: return "error"; default: return NULL; } } static gboolean avrcp_get_play_status_rsp(struct avctp *conn, uint8_t code, uint8_t subunit, uint8_t transaction, uint8_t *operands, size_t operand_count, void *user_data) { struct avrcp *session = user_data; struct avrcp_player *player = session->controller->player; struct media_player *mp = player->user_data; struct avrcp_header *pdu = (void *) operands; uint32_t duration; uint32_t position; uint8_t status; if (pdu == NULL || code == AVC_CTYPE_REJECTED || be16_to_cpu(pdu->params_len) != 9) return FALSE; duration = get_be32(pdu->params); media_player_set_duration(mp, duration); position = get_be32(pdu->params + 4); media_player_set_position(mp, position); status = get_u8(pdu->params + 8); media_player_set_status(mp, status_to_string(status)); return FALSE; } static void avrcp_get_play_status(struct avrcp *session) { uint8_t buf[AVRCP_HEADER_LENGTH]; struct avrcp_header *pdu = (void *) buf; memset(buf, 0, sizeof(buf)); set_company_id(pdu->company_id, IEEEID_BTSIG); pdu->pdu_id = AVRCP_GET_PLAY_STATUS; pdu->packet_type = AVRCP_PACKET_TYPE_SINGLE; avctp_send_vendordep_req(session->conn, AVC_CTYPE_STATUS, AVC_SUBUNIT_PANEL, buf, sizeof(buf), avrcp_get_play_status_rsp, session); } static const char *status_to_str(uint8_t status) { switch (status) { case AVRCP_STATUS_INVALID_COMMAND: return "Invalid Command"; case AVRCP_STATUS_INVALID_PARAM: return "Invalid Parameter"; case AVRCP_STATUS_INTERNAL_ERROR: return "Internal Error"; case AVRCP_STATUS_SUCCESS: return "Success"; default: return "Unknown"; } } static gboolean avrcp_player_value_rsp(struct avctp *conn, uint8_t code, uint8_t subunit, uint8_t transaction, uint8_t *operands, size_t operand_count, void *user_data) { struct avrcp *session = user_data; struct avrcp_player *player = session->controller->player; struct media_player *mp = player->user_data; struct avrcp_header *pdu = (void *) operands; uint8_t count; int i; if (pdu == NULL) { media_player_set_setting(mp, "Error", "Timeout"); return FALSE; } if (code == AVC_CTYPE_REJECTED) { media_player_set_setting(mp, "Error", status_to_str(pdu->params[0])); return FALSE; } count = pdu->params[0]; if (pdu->params_len < count * 2) return FALSE; for (i = 1; count > 0; count--, i += 2) { const char *key; const char *value; key = attr_to_str(pdu->params[i]); if (key == NULL) continue; value = attrval_to_str(pdu->params[i], pdu->params[i + 1]); if (value == NULL) continue; media_player_set_setting(mp, key, value); } return FALSE; } static void avrcp_get_current_player_value(struct avrcp *session, uint8_t *attrs, uint8_t count) { uint8_t buf[AVRCP_HEADER_LENGTH + AVRCP_ATTRIBUTE_LAST + 1]; struct avrcp_header *pdu = (void *) buf; uint16_t length = AVRCP_HEADER_LENGTH + count + 1; memset(buf, 0, sizeof(buf)); set_company_id(pdu->company_id, IEEEID_BTSIG); pdu->pdu_id = AVRCP_GET_CURRENT_PLAYER_VALUE; pdu->packet_type = AVRCP_PACKET_TYPE_SINGLE; pdu->params_len = cpu_to_be16(count + 1); pdu->params[0] = count; memcpy(pdu->params + 1, attrs, count); avctp_send_vendordep_req(session->conn, AVC_CTYPE_STATUS, AVC_SUBUNIT_PANEL, buf, length, avrcp_player_value_rsp, session); } static gboolean avrcp_list_player_attributes_rsp(struct avctp *conn, uint8_t code, uint8_t subunit, uint8_t transaction, uint8_t *operands, size_t operand_count, void *user_data) { uint8_t attrs[AVRCP_ATTRIBUTE_LAST]; struct avrcp *session = user_data; struct avrcp_header *pdu = (void *) operands; uint8_t len, count = 0; int i; if (code == AVC_CTYPE_REJECTED || code == AVC_CTYPE_NOT_IMPLEMENTED) return FALSE; len = pdu->params[0]; if (be16_to_cpu(pdu->params_len) < count) { error("Invalid parameters"); return FALSE; } for (i = 0; len > 0; len--, i++) { /* Don't query invalid attributes */ if (pdu->params[i + 1] == AVRCP_ATTRIBUTE_ILEGAL || pdu->params[i + 1] > AVRCP_ATTRIBUTE_LAST) continue; attrs[count++] = pdu->params[i + 1]; } avrcp_get_current_player_value(session, attrs, count); return FALSE; } static void avrcp_list_player_attributes(struct avrcp *session) { uint8_t buf[AVRCP_HEADER_LENGTH]; struct avrcp_header *pdu = (void *) buf; memset(buf, 0, sizeof(buf)); set_company_id(pdu->company_id, IEEEID_BTSIG); pdu->pdu_id = AVRCP_LIST_PLAYER_ATTRIBUTES; pdu->packet_type = AVRCP_PACKET_TYPE_SINGLE; avctp_send_vendordep_req(session->conn, AVC_CTYPE_STATUS, AVC_SUBUNIT_PANEL, buf, sizeof(buf), avrcp_list_player_attributes_rsp, session); } static void avrcp_parse_attribute_list(struct avrcp_player *player, struct media_item *item, uint8_t *operands, uint8_t count) { struct media_player *mp = player->user_data; int i; for (i = 0; count > 0; count--) { uint32_t id; uint16_t charset, len; id = get_be32(&operands[i]); i += sizeof(uint32_t); charset = get_be16(&operands[i]); i += sizeof(uint16_t); len = get_be16(&operands[i]); i += sizeof(uint16_t); if (charset == 106) { const char *key = metadata_to_str(id); if (key != NULL) media_player_set_metadata(mp, item, metadata_to_str(id), &operands[i], len); } i += len; } } static gboolean avrcp_get_element_attributes_rsp(struct avctp *conn, uint8_t code, uint8_t subunit, uint8_t transaction, uint8_t *operands, size_t operand_count, void *user_data) { struct avrcp *session = user_data; struct avrcp_player *player = session->controller->player; struct avrcp_header *pdu = (void *) operands; struct media_player *mp = player->user_data; struct media_item *item; uint8_t count; if (code == AVC_CTYPE_REJECTED) return FALSE; count = pdu->params[0]; if (be16_to_cpu(pdu->params_len) - 1 < count * 8) { error("Invalid parameters"); return FALSE; } media_player_clear_metadata(mp); item = media_player_set_playlist_item(mp, player->uid); avrcp_parse_attribute_list(player, item, &pdu->params[1], count); media_player_metadata_changed(mp); avrcp_get_play_status(session); return FALSE; } static void avrcp_get_element_attributes(struct avrcp *session) { uint8_t buf[AVRCP_HEADER_LENGTH + 9]; struct avrcp_header *pdu = (void *) buf; uint16_t length; memset(buf, 0, sizeof(buf)); set_company_id(pdu->company_id, IEEEID_BTSIG); pdu->pdu_id = AVRCP_GET_ELEMENT_ATTRIBUTES; pdu->params_len = cpu_to_be16(9); pdu->packet_type = AVRCP_PACKET_TYPE_SINGLE; length = AVRCP_HEADER_LENGTH + be16_to_cpu(pdu->params_len); avctp_send_vendordep_req(session->conn, AVC_CTYPE_STATUS, AVC_SUBUNIT_PANEL, buf, length, avrcp_get_element_attributes_rsp, session); } static const char *type_to_string(uint8_t type) { switch (type & 0x0F) { case 0x01: return "Audio"; case 0x02: return "Video"; case 0x03: return "Audio, Video"; case 0x04: return "Audio Broadcasting"; case 0x05: return "Audio, Audio Broadcasting"; case 0x06: return "Video, Audio Broadcasting"; case 0x07: return "Audio, Video, Audio Broadcasting"; case 0x08: return "Video Broadcasting"; case 0x09: return "Audio, Video Broadcasting"; case 0x0A: return "Video, Video Broadcasting"; case 0x0B: return "Audio, Video, Video Broadcasting"; case 0x0C: return "Audio Broadcasting, Video Broadcasting"; case 0x0D: return "Audio, Audio Broadcasting, Video Broadcasting"; case 0x0E: return "Video, Audio Broadcasting, Video Broadcasting"; case 0x0F: return "Audio, Video, Audio Broadcasting, Video Broadcasting"; } return "None"; } static const char *subtype_to_string(uint32_t subtype) { switch (subtype & 0x03) { case 0x01: return "Audio Book"; case 0x02: return "Podcast"; case 0x03: return "Audio Book, Podcast"; } return "None"; } static struct media_item *parse_media_element(struct avrcp *session, uint8_t *operands, uint16_t len) { struct avrcp_player *player; struct media_player *mp; struct media_item *item; uint16_t namelen, namesize; char name[255]; uint64_t uid; uint8_t count; if (len < 13) return NULL; uid = get_be64(&operands[0]); memset(name, 0, sizeof(name)); namesize = get_be16(&operands[11]); namelen = MIN(namesize, sizeof(name) - 1); if (namelen > 0) memcpy(name, &operands[13], namelen); count = operands[13 + namesize]; player = session->controller->player; mp = player->user_data; item = media_player_create_item(mp, name, PLAYER_ITEM_TYPE_AUDIO, uid); if (item == NULL) return NULL; media_item_set_playable(item, true); avrcp_parse_attribute_list(player, item, &operands[14 + namesize], count); return item; } static struct media_item *parse_media_folder(struct avrcp *session, uint8_t *operands, uint16_t len) { struct avrcp_player *player = session->controller->player; struct media_player *mp = player->user_data; struct media_item *item; uint16_t namelen; char name[255]; uint64_t uid; uint8_t type; uint8_t playable; if (len < 12) return NULL; uid = get_be64(&operands[0]); type = operands[8]; playable = operands[9]; memset(name, 0, sizeof(name)); namelen = MIN(get_be16(&operands[12]), sizeof(name) - 1); if (namelen > 0) memcpy(name, &operands[14], namelen); item = media_player_create_folder(mp, name, type, uid); if (!item) return NULL; media_item_set_playable(item, playable & 0x01); return item; } static void avrcp_list_items(struct avrcp *session, uint32_t start, uint32_t end); static gboolean avrcp_list_items_rsp(struct avctp *conn, uint8_t *operands, size_t operand_count, void *user_data) { struct avrcp_browsing_header *pdu = (void *) operands; struct avrcp *session = user_data; struct avrcp_player *player = session->controller->player; struct pending_list_items *p = player->p; uint16_t count; uint64_t items; size_t i; int err = 0; if (player->p == NULL) { media_player_list_complete(player->user_data, NULL, -EINVAL); return FALSE; } if (pdu == NULL) { err = -ETIMEDOUT; goto done; } /* AVRCP 1.5 - Page 76: * If the TG receives a GetFolderItems command for an empty folder then * the TG shall return the error (= Range Out of Bounds) in the status * field of the GetFolderItems response. */ if (pdu->params[0] == AVRCP_STATUS_OUT_OF_BOUNDS) goto done; if (pdu->params[0] != AVRCP_STATUS_SUCCESS || operand_count < 5) { err = -EINVAL; goto done; } count = get_be16(&operands[6]); if (count == 0) goto done; for (i = 8; count && i + 3 < operand_count; count--) { struct media_item *item; uint8_t type; uint16_t len; type = operands[i++]; len = get_be16(&operands[i]); i += 2; if (type != 0x03 && type != 0x02) { i += len; continue; } if (i + len > operand_count) { error("Invalid item length"); break; } if (type == 0x03) item = parse_media_element(session, &operands[i], len); else item = parse_media_folder(session, &operands[i], len); if (item) p->items = g_slist_append(p->items, item); i += len; } items = g_slist_length(p->items); DBG("start %u end %u items %" PRIu64 " total %" PRIu64 "", p->start, p->end, items, p->total); if (items < p->total) { avrcp_list_items(session, p->start + items, p->end); return FALSE; } done: media_player_list_complete(player->user_data, p->items, err); g_slist_free(p->items); g_free(p); player->p = NULL; return FALSE; } static void avrcp_list_items(struct avrcp *session, uint32_t start, uint32_t end) { uint8_t buf[AVRCP_BROWSING_HEADER_LENGTH + 10 + AVRCP_MEDIA_ATTRIBUTE_LAST * sizeof(uint32_t)]; struct avrcp_player *player = session->controller->player; struct avrcp_browsing_header *pdu = (void *) buf; uint16_t length = AVRCP_BROWSING_HEADER_LENGTH + 10; memset(buf, 0, sizeof(buf)); pdu->pdu_id = AVRCP_GET_FOLDER_ITEMS; pdu->param_len = cpu_to_be16(10 + (6 * sizeof(uint32_t))); pdu->params[0] = player->scope; put_be32(start, &pdu->params[1]); put_be32(end, &pdu->params[5]); pdu->params[9] = 6; /* Only the title (0x01) is mandatory. This can be extended to * support AVRCP_MEDIA_ATTRIBUTE_* attributes */ put_be32(AVRCP_MEDIA_ATTRIBUTE_TITLE, &pdu->params[10]); put_be32(AVRCP_MEDIA_ATTRIBUTE_ARTIST, &pdu->params[14]); put_be32(AVRCP_MEDIA_ATTRIBUTE_ALBUM, &pdu->params[18]); put_be32(AVRCP_MEDIA_ATTRIBUTE_TRACK, &pdu->params[22]); put_be32(AVRCP_MEDIA_ATTRIBUTE_DURATION, &pdu->params[26]); put_be32(AVRCP_MEDIA_ATTRIBUTE_IMG_HANDLE, &pdu->params[30]); length += 6 * sizeof(uint32_t); avctp_send_browsing_req(session->conn, buf, length, avrcp_list_items_rsp, session); } static gboolean avrcp_change_path_rsp(struct avctp *conn, uint8_t *operands, size_t operand_count, void *user_data) { struct avrcp_browsing_header *pdu = (void *) operands; struct avrcp *session = user_data; struct avrcp_player *player = session->controller->player; struct media_player *mp = player->user_data; int ret; if (pdu == NULL) { ret = -ETIMEDOUT; goto done; } if (pdu->params[0] != AVRCP_STATUS_SUCCESS) { ret = -EINVAL; goto done; } ret = get_be32(&pdu->params[1]); done: if (ret < 0) { g_free(player->change_path); player->change_path = NULL; } else { g_free(player->path); player->path = player->change_path; player->change_path = NULL; } media_player_change_folder_complete(mp, player->path, player->change_uid, ret); player->change_uid = 0; return FALSE; } static gboolean avrcp_set_browsed_player_rsp(struct avctp *conn, uint8_t *operands, size_t operand_count, void *user_data) { struct avrcp *session = user_data; struct avrcp_player *player = session->controller->player; struct media_player *mp = player->user_data; struct avrcp_browsing_header *pdu = (void *) operands; uint32_t items; char **folders; uint8_t depth, count; size_t i; if (pdu == NULL || pdu->params[0] != AVRCP_STATUS_SUCCESS || operand_count < 13) return FALSE; player->uid_counter = get_be16(&pdu->params[1]); player->browsed = true; items = get_be32(&pdu->params[3]); depth = pdu->params[9]; folders = g_new0(char *, depth + 2); folders[0] = g_strdup("/Filesystem"); for (i = 10, count = 1; count - 1 < depth && i < operand_count; count++) { uint8_t len; len = pdu->params[i++]; if (!len) continue; if (i + len > operand_count) { error("Invalid folder length"); break; } folders[count] = util_memdup(&pdu->params[i], len); i += len; } player->path = g_build_pathv("/", folders); g_strfreev(folders); media_player_set_folder(mp, player->path, items); return FALSE; } static void avrcp_set_browsed_player(struct avrcp *session, struct avrcp_player *player) { uint8_t buf[AVRCP_BROWSING_HEADER_LENGTH + 2]; struct avrcp_browsing_header *pdu = (void *) buf; uint16_t id; memset(buf, 0, sizeof(buf)); pdu->pdu_id = AVRCP_SET_BROWSED_PLAYER; id = cpu_to_be16(player->id); memcpy(pdu->params, &id, 2); pdu->param_len = cpu_to_be16(2); avctp_send_browsing_req(session->conn, buf, sizeof(buf), avrcp_set_browsed_player_rsp, session); } static gboolean avrcp_get_item_attributes_rsp(struct avctp *conn, uint8_t *operands, size_t operand_count, void *user_data) { struct avrcp *session = user_data; struct avrcp_player *player = session->controller->player; struct avrcp_browsing_header *pdu = (void *) operands; struct media_player *mp = player->user_data; struct media_item *item; uint8_t count; if (pdu == NULL) { avrcp_get_element_attributes(session); return FALSE; } if (pdu->params[0] != AVRCP_STATUS_SUCCESS || operand_count < 4) { avrcp_get_element_attributes(session); return FALSE; } count = pdu->params[1]; if (be16_to_cpu(pdu->param_len) - 1 < count * 8) { error("Invalid parameters"); return FALSE; } media_player_clear_metadata(mp); item = media_player_set_playlist_item(mp, player->uid); avrcp_parse_attribute_list(player, item, &pdu->params[2], count); media_player_metadata_changed(mp); avrcp_get_play_status(session); return FALSE; } static void avrcp_get_item_attributes(struct avrcp *session, uint64_t uid) { struct avrcp_player *player = session->controller->player; uint8_t buf[AVRCP_BROWSING_HEADER_LENGTH + 12]; struct avrcp_browsing_header *pdu = (void *) buf; memset(buf, 0, sizeof(buf)); pdu->pdu_id = AVRCP_GET_ITEM_ATTRIBUTES; pdu->params[0] = 0x03; put_be64(uid, &pdu->params[1]); put_be16(player->uid_counter, &pdu->params[9]); pdu->param_len = cpu_to_be16(12); avctp_send_browsing_req(session->conn, buf, sizeof(buf), avrcp_get_item_attributes_rsp, session); } static void avrcp_player_parse_features(struct avrcp_player *player, uint8_t *features) { struct media_player *mp = player->user_data; player->features = util_memdup(features, 16); if (features[7] & 0x08) { media_player_set_browsable(mp, true); media_player_create_folder(mp, "/Filesystem", PLAYER_FOLDER_TYPE_MIXED, 0); } if (features[7] & 0x10) media_player_set_searchable(mp, true); if (features[8] & 0x02) { media_player_create_folder(mp, "/NowPlaying", PLAYER_FOLDER_TYPE_MIXED, 0); media_player_set_playlist(mp, "/NowPlaying"); } } static void avrcp_set_player_value(struct avrcp *session, uint8_t attr, uint8_t val) { uint8_t buf[AVRCP_HEADER_LENGTH + 3]; struct avrcp_header *pdu = (void *) buf; uint8_t length; memset(buf, 0, sizeof(buf)); set_company_id(pdu->company_id, IEEEID_BTSIG); pdu->pdu_id = AVRCP_SET_PLAYER_VALUE; pdu->packet_type = AVRCP_PACKET_TYPE_SINGLE; pdu->params[0] = 1; pdu->params[1] = attr; pdu->params[2] = val; pdu->params_len = cpu_to_be16(3); length = AVRCP_HEADER_LENGTH + be16_to_cpu(pdu->params_len); avctp_send_vendordep_req(session->conn, AVC_CTYPE_CONTROL, AVC_SUBUNIT_PANEL, buf, length, avrcp_player_value_rsp, session); } static gboolean avrcp_set_addressed_player_rsp(struct avctp *conn, uint8_t code, uint8_t subunit, uint8_t transaction, uint8_t *operands, size_t operand_count, void *user_data) { struct avrcp *session = user_data; struct avrcp_player *player = session->controller->player; struct avrcp_header *pdu = (void *) operands; if (!pdu || code != AVC_CTYPE_ACCEPTED) return FALSE; player->addressed = true; return FALSE; } static void avrcp_set_addressed_player(struct avrcp *session, struct avrcp_player *player) { uint8_t buf[AVRCP_HEADER_LENGTH + 2]; struct avrcp_header *pdu = (void *) buf; uint16_t id; memset(buf, 0, sizeof(buf)); set_company_id(pdu->company_id, IEEEID_BTSIG); pdu->pdu_id = AVRCP_SET_ADDRESSED_PLAYER; pdu->packet_type = AVRCP_PACKET_TYPE_SINGLE; id = cpu_to_be16(player->id); memcpy(pdu->params, &id, 2); pdu->params_len = cpu_to_be16(2); avctp_send_vendordep_req(session->conn, AVC_CTYPE_CONTROL, AVC_SUBUNIT_PANEL, buf, sizeof(buf), avrcp_set_addressed_player_rsp, session); } static void set_addressed_player(struct avrcp *session, struct avrcp_player *player) { if (!player || !player->id || player->addressed || session->controller->version < 0x0104) return; /* Set player as addressed */ avrcp_set_addressed_player(session, player); } static void set_browsed_player(struct avrcp *session, struct avrcp_player *player) { if (!player || !player->id || player->browsed) return; if (media_player_get_browsable(player->user_data)) avrcp_set_browsed_player(session, player); } static void set_ct_player(struct avrcp *session, struct avrcp_player *player) { struct btd_service *service; if (session->controller->player == player) goto done; session->controller->player = player; service = btd_device_get_service(session->dev, AVRCP_TARGET_UUID); control_set_player(service, player ? media_player_get_path(player->user_data) : NULL); done: set_addressed_player(session, player); set_browsed_player(session, player); } static bool ct_set_setting(struct media_player *mp, const char *key, const char *value, void *user_data) { struct avrcp_player *player = user_data; int attr; int val; struct avrcp *session; session = player->sessions->data; if (session == NULL) return false; if (session->controller->version < 0x0103) return false; set_ct_player(session, player); attr = attr_to_val(key); if (attr < 0) return false; val = attrval_to_val(attr, value); if (val < 0) return false; avrcp_set_player_value(session, attr, val); return true; } static int ct_press(struct avrcp_player *player, uint8_t op) { struct avrcp *session = player->sessions->data; if (session == NULL) return -ENOTCONN; set_ct_player(session, player); return avctp_send_passthrough(session->conn, op, false); } static int ct_hold(struct avrcp_player *player, uint8_t op) { struct avrcp *session = player->sessions->data; if (session == NULL) return -ENOTCONN; set_ct_player(session, player); return avctp_send_passthrough(session->conn, op, true); } static int ct_release(struct avrcp_player *player) { struct avrcp *session = player->sessions->data; if (session == NULL) return -ENOTCONN; set_ct_player(session, player); return avctp_send_release_passthrough(session->conn); } static int ct_play(struct media_player *mp, void *user_data) { struct avrcp_player *player = user_data; return ct_press(player, AVC_PLAY); } static int ct_pause(struct media_player *mp, void *user_data) { struct avrcp_player *player = user_data; return ct_press(player, AVC_PAUSE); } static int ct_stop(struct media_player *mp, void *user_data) { struct avrcp_player *player = user_data; return ct_press(player, AVC_STOP); } static int ct_next(struct media_player *mp, void *user_data) { struct avrcp_player *player = user_data; return ct_press(player, AVC_FORWARD); } static int ct_previous(struct media_player *mp, void *user_data) { struct avrcp_player *player = user_data; return ct_press(player, AVC_BACKWARD); } static int ct_fast_forward(struct media_player *mp, void *user_data) { struct avrcp_player *player = user_data; return ct_hold(player, AVC_FAST_FORWARD); } static int ct_rewind(struct media_player *mp, void *user_data) { struct avrcp_player *player = user_data; return ct_hold(player, AVC_REWIND); } static int ct_press_key(struct media_player *mp, uint8_t avc_key, void *user_data) { struct avrcp_player *player = user_data; return ct_press(player, avc_key); } static int ct_hold_key(struct media_player *mp, uint8_t avc_key, void *user_data) { struct avrcp_player *player = user_data; return ct_hold(player, avc_key); } static int ct_release_key(struct media_player *mp, void *user_data) { struct avrcp_player *player = user_data; return ct_release(player); } static int ct_list_items(struct media_player *mp, const char *name, uint32_t start, uint32_t end, void *user_data) { struct avrcp_player *player = user_data; struct avrcp *session; struct pending_list_items *p; if (player->p != NULL) return -EBUSY; session = player->sessions->data; set_ct_player(session, player); if (g_str_has_prefix(name, "/NowPlaying")) player->scope = 0x03; else if (g_str_has_suffix(name, "/search")) player->scope = 0x02; else player->scope = 0x01; avrcp_list_items(session, start, end); p = g_new0(struct pending_list_items, 1); p->start = start; p->end = end; p->total = (uint64_t) (p->end - p->start) + 1; player->p = p; return 0; } static void avrcp_change_path(struct avrcp *session, uint8_t direction, uint64_t uid) { struct avrcp_player *player = session->controller->player; uint8_t buf[AVRCP_BROWSING_HEADER_LENGTH + 11]; struct avrcp_browsing_header *pdu = (void *) buf; memset(buf, 0, sizeof(buf)); put_be16(player->uid_counter, &pdu->params[0]); pdu->params[2] = direction; put_be64(uid, &pdu->params[3]); pdu->pdu_id = AVRCP_CHANGE_PATH; pdu->param_len = cpu_to_be16(11); avctp_send_browsing_req(session->conn, buf, sizeof(buf), avrcp_change_path_rsp, session); } static int ct_change_folder(struct media_player *mp, const char *path, uint64_t uid, void *user_data) { struct avrcp_player *player = user_data; struct avrcp *session; uint8_t direction; session = player->sessions->data; set_ct_player(session, player); player->change_path = g_strdup(path); player->change_uid = uid; direction = g_str_has_prefix(path, player->path) ? 0x01 : 0x00; avrcp_change_path(session, direction, uid); return 0; } static gboolean avrcp_search_rsp(struct avctp *conn, uint8_t *operands, size_t operand_count, void *user_data) { struct avrcp_browsing_header *pdu = (void *) operands; struct avrcp *session = (void *) user_data; struct avrcp_player *player = session->controller->player; struct media_player *mp = player->user_data; int ret; if (pdu == NULL) { ret = -ETIMEDOUT; goto done; } if (pdu->params[0] != AVRCP_STATUS_SUCCESS || operand_count < 7) { ret = -EINVAL; goto done; } player->uid_counter = get_be16(&pdu->params[1]); ret = get_be32(&pdu->params[3]); done: media_player_search_complete(mp, ret); return FALSE; } static void avrcp_search(struct avrcp *session, const char *string) { uint8_t buf[AVRCP_BROWSING_HEADER_LENGTH + 255]; struct avrcp_browsing_header *pdu = (void *) buf; uint16_t len, stringlen; memset(buf, 0, sizeof(buf)); len = AVRCP_BROWSING_HEADER_LENGTH + 4; stringlen = strnlen(string, sizeof(buf) - len); len += stringlen; put_be16(AVRCP_CHARSET_UTF8, &pdu->params[0]); put_be16(stringlen, &pdu->params[2]); memcpy(&pdu->params[4], string, stringlen); pdu->pdu_id = AVRCP_SEARCH; pdu->param_len = cpu_to_be16(len - AVRCP_BROWSING_HEADER_LENGTH); avctp_send_browsing_req(session->conn, buf, len, avrcp_search_rsp, session); } static int ct_search(struct media_player *mp, const char *string, void *user_data) { struct avrcp_player *player = user_data; struct avrcp *session; session = player->sessions->data; set_ct_player(session, player); avrcp_search(session, string); return 0; } static gboolean avrcp_play_item_rsp(struct avctp *conn, uint8_t code, uint8_t subunit, uint8_t transaction, uint8_t *operands, size_t operand_count, void *user_data) { struct avrcp_header *pdu = (void *) operands; struct avrcp *session = (void *) user_data; struct avrcp_player *player = session->controller->player; struct media_player *mp = player->user_data; int ret = 0; if (pdu == NULL) { ret = -ETIMEDOUT; goto done; } if (pdu->params[0] != AVRCP_STATUS_SUCCESS) { switch (pdu->params[0]) { case AVRCP_STATUS_UID_CHANGED: case AVRCP_STATUS_DOES_NOT_EXIST: ret = -ENOENT; break; default: ret = -EINVAL; break; } goto done; } done: media_player_play_item_complete(mp, ret); return FALSE; } static void avrcp_play_item(struct avrcp *session, uint64_t uid) { uint8_t buf[AVRCP_HEADER_LENGTH + 11]; struct avrcp_player *player = session->controller->player; struct avrcp_header *pdu = (void *) buf; uint16_t length; memset(buf, 0, sizeof(buf)); set_company_id(pdu->company_id, IEEEID_BTSIG); pdu->pdu_id = AVRCP_PLAY_ITEM; pdu->params_len = cpu_to_be16(11); pdu->packet_type = AVRCP_PACKET_TYPE_SINGLE; pdu->params[0] = player->scope; put_be64(uid, &pdu->params[1]); put_be16(player->uid_counter, &pdu->params[9]); length = AVRCP_HEADER_LENGTH + be16_to_cpu(pdu->params_len); avctp_send_vendordep_req(session->conn, AVC_CTYPE_CONTROL, AVC_SUBUNIT_PANEL, buf, length, avrcp_play_item_rsp, session); } static int ct_play_item(struct media_player *mp, const char *name, uint64_t uid, void *user_data) { struct avrcp_player *player = user_data; struct avrcp *session; if (player->p != NULL) return -EBUSY; session = player->sessions->data; set_ct_player(session, player); if (g_strrstr(name, "/NowPlaying")) player->scope = 0x03; else if (g_strrstr(name, "/Search")) player->scope = 0x02; else player->scope = 0x01; avrcp_play_item(session, uid); return 0; } static void avrcp_add_to_nowplaying(struct avrcp *session, uint64_t uid) { uint8_t buf[AVRCP_HEADER_LENGTH + 11]; struct avrcp_player *player = session->controller->player; struct avrcp_header *pdu = (void *) buf; uint16_t length; memset(buf, 0, sizeof(buf)); set_company_id(pdu->company_id, IEEEID_BTSIG); pdu->pdu_id = AVRCP_ADD_TO_NOW_PLAYING; pdu->params_len = cpu_to_be16(11); pdu->packet_type = AVRCP_PACKET_TYPE_SINGLE; pdu->params[0] = player->scope; put_be64(uid, &pdu->params[1]); put_be16(player->uid_counter, &pdu->params[9]); length = AVRCP_HEADER_LENGTH + be16_to_cpu(pdu->params_len); avctp_send_vendordep_req(session->conn, AVC_CTYPE_CONTROL, AVC_SUBUNIT_PANEL, buf, length, NULL, session); } static int ct_add_to_nowplaying(struct media_player *mp, const char *name, uint64_t uid, void *user_data) { struct avrcp_player *player = user_data; struct avrcp *session; if (player->p != NULL) return -EBUSY; session = player->sessions->data; if (g_strrstr(name, "/NowPlaying")) player->scope = 0x03; else player->scope = 0x01; set_ct_player(session, player); avrcp_add_to_nowplaying(session, uid); return 0; } static gboolean avrcp_get_total_numberofitems_rsp(struct avctp *conn, uint8_t *operands, size_t operand_count, void *user_data) { struct avrcp_browsing_header *pdu = (void *) operands; struct avrcp *session = user_data; struct avrcp_player *player = session->controller->player; struct media_player *mp = player->user_data; uint32_t num_of_items = 0; if (pdu == NULL) return -ETIMEDOUT; if (pdu->params[0] != AVRCP_STATUS_SUCCESS || operand_count < 7) return -EINVAL; if (pdu->params[0] == AVRCP_STATUS_OUT_OF_BOUNDS) goto done; player->uid_counter = get_be16(&pdu->params[1]); num_of_items = get_be32(&pdu->params[3]); if (!num_of_items) return -EINVAL; done: media_player_total_items_complete(mp, num_of_items); return FALSE; } static void avrcp_get_total_numberofitems(struct avrcp *session) { uint8_t buf[AVRCP_BROWSING_HEADER_LENGTH + 7]; struct avrcp_player *player = session->controller->player; struct avrcp_browsing_header *pdu = (void *) buf; memset(buf, 0, sizeof(buf)); pdu->pdu_id = AVRCP_GET_TOTAL_NUMBER_OF_ITEMS; pdu->param_len = cpu_to_be16(7 + sizeof(uint32_t)); pdu->params[0] = player->scope; avctp_send_browsing_req(session->conn, buf, sizeof(buf), avrcp_get_total_numberofitems_rsp, session); } static int ct_get_total_numberofitems(struct media_player *mp, const char *name, void *user_data) { struct avrcp_player *player = user_data; struct avrcp *session; session = player->sessions->data; set_ct_player(session, player); if (session->controller->version != 0x0106) { error("version not supported"); return -1; } if (g_str_has_prefix(name, "/NowPlaying")) player->scope = 0x03; else if (g_str_has_suffix(name, "/search")) player->scope = 0x02; else player->scope = 0x01; avrcp_get_total_numberofitems(session); return 0; } static const struct media_player_callback ct_cbs = { .set_setting = ct_set_setting, .play = ct_play, .pause = ct_pause, .stop = ct_stop, .next = ct_next, .previous = ct_previous, .fast_forward = ct_fast_forward, .rewind = ct_rewind, .press = ct_press_key, .hold = ct_hold_key, .release = ct_release_key, .list_items = ct_list_items, .change_folder = ct_change_folder, .search = ct_search, .play_item = ct_play_item, .add_to_nowplaying = ct_add_to_nowplaying, .total_items = ct_get_total_numberofitems, }; static struct avrcp_player *create_ct_player(struct avrcp *session, uint16_t id) { struct avrcp_player *player; struct media_player *mp; const char *path; player = g_new0(struct avrcp_player, 1); player->id = id; player->sessions = g_slist_prepend(player->sessions, session); path = device_get_path(session->dev); mp = media_player_controller_create(path, id); if (mp == NULL) { g_free(player); return NULL; } media_player_set_obex_port(mp, session->controller->obex_port); media_player_set_callbacks(mp, &ct_cbs, player); player->user_data = mp; player->destroy = (GDestroyNotify) media_player_destroy; if (session->controller->player == NULL) set_ct_player(session, player); session->controller->players = g_slist_prepend( session->controller->players, player); return player; } static struct avrcp_player *find_ct_player(struct avrcp *session, uint16_t id) { GSList *l; for (l = session->controller->players; l; l = l->next) { struct avrcp_player *player = l->data; if (player->id == 0) { player->id = id; return player; } if (player->id == id) return player; } return NULL; } static struct avrcp_player * avrcp_parse_media_player_item(struct avrcp *session, uint8_t *operands, uint16_t len) { struct avrcp_player *player; struct media_player *mp; uint16_t id, namelen; uint32_t subtype; const char *curval, *strval; char name[255]; if (len < 28) return NULL; id = get_be16(&operands[0]); player = find_ct_player(session, id); if (player == NULL) { player = create_ct_player(session, id); if (player == NULL) return NULL; } else if (player->features != NULL) return player; mp = player->user_data; media_player_set_type(mp, type_to_string(operands[2])); subtype = get_be32(&operands[3]); media_player_set_subtype(mp, subtype_to_string(subtype)); curval = media_player_get_status(mp); strval = status_to_string(operands[7]); if (g_strcmp0(curval, strval) != 0) { media_player_set_status(mp, strval); avrcp_get_play_status(session); } avrcp_player_parse_features(player, &operands[8]); namelen = get_be16(&operands[26]); if (namelen > 0 && namelen + 28 == len) { namelen = MIN(namelen, sizeof(name) - 1); memcpy(name, &operands[28], namelen); name[namelen] = '\0'; media_player_set_name(mp, name); } if (player->addressed) set_browsed_player(session, player); return player; } static void player_destroy(gpointer data) { struct avrcp_player *player = data; if (player->destroy) player->destroy(player->user_data); if (player->changed_id > 0) g_source_remove(player->changed_id); g_slist_free(player->sessions); g_free(player->path); g_free(player->change_path); free(player->features); g_free(player); } static void player_remove(gpointer data) { struct avrcp_player *player = data; GSList *l; /* Don't remove reserved player */ if (!player->id) return; for (l = player->sessions; l; l = l->next) { struct avrcp *session = l->data; struct avrcp_data *controller = session->controller; controller->players = g_slist_remove(controller->players, player); /* Check if current player is being removed */ if (controller->player == player) set_ct_player(session, g_slist_nth_data( controller->players, 0)); } player_destroy(player); } static gboolean avrcp_get_media_player_list_rsp(struct avctp *conn, uint8_t *operands, size_t operand_count, void *user_data) { struct avrcp_browsing_header *pdu = (void *) operands; struct avrcp *session = user_data; uint16_t count; size_t i; GSList *removed; if (pdu == NULL || pdu->params[0] != AVRCP_STATUS_SUCCESS || operand_count < 5) return FALSE; removed = g_slist_copy(session->controller->players); count = get_be16(&operands[6]); for (i = 8; count && i < operand_count; count--) { struct avrcp_player *player; uint8_t type; uint16_t len; type = operands[i++]; len = get_be16(&operands[i]); i += 2; if (type != 0x01) { i += len; continue; } if (i + len > operand_count) { error("Invalid player item length"); return FALSE; } player = avrcp_parse_media_player_item(session, &operands[i], len); if (player) removed = g_slist_remove(removed, player); i += len; } g_slist_free_full(removed, player_remove); /* There should always be an active player */ if (!session->controller->player) create_ct_player(session, 0); return FALSE; } static void avrcp_get_media_player_list(struct avrcp *session) { uint8_t buf[AVRCP_BROWSING_HEADER_LENGTH + 10]; struct avrcp_browsing_header *pdu = (void *) buf; memset(buf, 0, sizeof(buf)); pdu->pdu_id = AVRCP_GET_FOLDER_ITEMS; put_be32(0, &pdu->params[1]); put_be32(UINT32_MAX, &pdu->params[5]); pdu->param_len = cpu_to_be16(10); avctp_send_browsing_req(session->conn, buf, sizeof(buf), avrcp_get_media_player_list_rsp, session); } static void avrcp_volume_changed(struct avrcp *session, struct avrcp_header *pdu) { struct avrcp_player *player = target_get_player(session); int8_t volume; if (!avrcp_volume_supported(session->controller)) { error("EVENT_VOLUME_CHANGED not supported"); return; } volume = pdu->params[1] & 0x7F; /* Always attempt to update the transport volume */ media_transport_update_device_volume(session->dev, volume); if (player) player->cb->set_volume(volume, session->dev, player->user_data); } static void avrcp_status_changed(struct avrcp *session, struct avrcp_header *pdu) { struct avrcp_player *player = session->controller->player; struct media_player *mp = player->user_data; uint8_t value; const char *curval, *strval; value = pdu->params[1]; curval = media_player_get_status(mp); strval = status_to_string(value); if (g_strcmp0(curval, strval) == 0) return; media_player_set_status(mp, strval); avrcp_get_play_status(session); } static void avrcp_track_changed(struct avrcp *session, struct avrcp_header *pdu) { if (session->browsing_id) { struct avrcp_player *player = session->controller->player; player->uid = get_be64(&pdu->params[1]); avrcp_get_item_attributes(session, player->uid); } else avrcp_get_element_attributes(session); } static void avrcp_playback_pos_changed(struct avrcp *session, struct avrcp_header *pdu) { struct avrcp_player *player = session->controller->player; struct media_player *mp = player->user_data; uint32_t position; position = get_be32(&pdu->params[1]); media_player_set_position(mp, position); } static void avrcp_setting_changed(struct avrcp *session, struct avrcp_header *pdu) { struct avrcp_player *player = session->controller->player; struct media_player *mp = player->user_data; uint8_t count = pdu->params[1]; int i; for (i = 2; count > 0; count--, i += 2) { const char *key; const char *value; key = attr_to_str(pdu->params[i]); if (key == NULL) continue; value = attrval_to_str(pdu->params[i], pdu->params[i + 1]); if (value == NULL) continue; media_player_set_setting(mp, key, value); } } static void avrcp_now_playing_changed(struct avrcp *session, struct avrcp_header *pdu) { struct avrcp_player *player = session->controller->player; struct media_player *mp = player->user_data; DBG("NowPlaying changed"); media_player_clear_playlist(mp); } static void avrcp_available_players_changed(struct avrcp *session, struct avrcp_header *pdu) { avrcp_get_media_player_list(session); } static void avrcp_addressed_player_changed(struct avrcp *session, struct avrcp_header *pdu) { struct avrcp_player *player = session->controller->player; uint16_t id = get_be16(&pdu->params[1]); if (player != NULL && player->id == id) return; player = find_ct_player(session, id); if (player == NULL) { player = create_ct_player(session, id); if (player == NULL) return; } player->addressed = true; player->uid_counter = get_be16(&pdu->params[3]); set_ct_player(session, player); if (player->features != NULL) return; avrcp_get_media_player_list(session); } static void avrcp_uids_changed(struct avrcp *session, struct avrcp_header *pdu) { struct avrcp_player *player = session->controller->player; player->uid_counter = get_be16(&pdu->params[1]); } static gboolean avrcp_handle_event(struct avctp *conn, uint8_t code, uint8_t subunit, uint8_t transaction, uint8_t *operands, size_t operand_count, void *user_data) { struct avrcp *session = user_data; struct avrcp_header *pdu = (void *) operands; uint8_t event; if (!pdu) return FALSE; if ((code != AVC_CTYPE_INTERIM && code != AVC_CTYPE_CHANGED)) { if (pdu->params[0] == AVRCP_STATUS_ADDRESSED_PLAYER_CHANGED && code == AVC_CTYPE_REJECTED) { int i; /* Lookup event by transaction */ for (i = 0; i <= AVRCP_EVENT_LAST; i++) { if (session->transaction_events[i] == transaction) { event = i; goto changed; } } } return FALSE; } event = pdu->params[0]; if (code == AVC_CTYPE_CHANGED) { goto changed; } switch (event) { case AVRCP_EVENT_VOLUME_CHANGED: avrcp_volume_changed(session, pdu); break; case AVRCP_EVENT_STATUS_CHANGED: avrcp_status_changed(session, pdu); break; case AVRCP_EVENT_TRACK_CHANGED: avrcp_track_changed(session, pdu); break; case AVRCP_EVENT_PLAYBACK_POS_CHANGED: avrcp_playback_pos_changed(session, pdu); break; case AVRCP_EVENT_SETTINGS_CHANGED: avrcp_setting_changed(session, pdu); break; case AVRCP_EVENT_NOW_PLAYING_CHANGED: avrcp_now_playing_changed(session, pdu); break; case AVRCP_EVENT_AVAILABLE_PLAYERS_CHANGED: avrcp_available_players_changed(session, pdu); break; case AVRCP_EVENT_ADDRESSED_PLAYER_CHANGED: avrcp_addressed_player_changed(session, pdu); break; case AVRCP_EVENT_UIDS_CHANGED: avrcp_uids_changed(session, pdu); break; default: if (event > AVRCP_EVENT_LAST) { warn("Unsupported event: %u", event); return FALSE; } break; } session->registered_events |= (1 << event); session->transaction_events[event] = transaction; return TRUE; changed: session->registered_events ^= (1 << event); session->transaction_events[event] = 0; avrcp_register_notification(session, event); return FALSE; } static void avrcp_register_notification(struct avrcp *session, uint8_t event) { uint8_t buf[AVRCP_HEADER_LENGTH + AVRCP_REGISTER_NOTIFICATION_PARAM_LENGTH]; struct avrcp_header *pdu = (void *) buf; uint8_t length; memset(buf, 0, sizeof(buf)); set_company_id(pdu->company_id, IEEEID_BTSIG); pdu->pdu_id = AVRCP_REGISTER_NOTIFICATION; pdu->packet_type = AVRCP_PACKET_TYPE_SINGLE; pdu->params[0] = event; /* * Set maximum interval possible for position changed as we only * use it to resync. */ if (event == AVRCP_EVENT_PLAYBACK_POS_CHANGED) bt_put_be32(UINT32_MAX / 1000, &pdu->params[1]); pdu->params_len = cpu_to_be16(AVRCP_REGISTER_NOTIFICATION_PARAM_LENGTH); length = AVRCP_HEADER_LENGTH + be16_to_cpu(pdu->params_len); avctp_send_vendordep_req(session->conn, AVC_CTYPE_NOTIFY, AVC_SUBUNIT_PANEL, buf, length, avrcp_handle_event, session); } static gboolean avrcp_get_capabilities_resp(struct avctp *conn, uint8_t code, uint8_t subunit, uint8_t transaction, uint8_t *operands, size_t operand_count, void *user_data) { struct avrcp *session = user_data; struct avrcp_header *pdu = (void *) operands; uint16_t events = 0; uint8_t count; if (code == AVC_CTYPE_REJECTED || code == AVC_CTYPE_NOT_IMPLEMENTED || pdu == NULL || pdu->params[0] != CAP_EVENTS_SUPPORTED) return FALSE; /* Connect browsing if pending */ if (session->browsing_timer > 0) { timeout_remove(session->browsing_timer); session->browsing_timer = 0; avctp_connect_browsing(session->conn); } count = pdu->params[1]; for (; count > 0; count--) { uint8_t event = pdu->params[1 + count]; events |= (1 << event); switch (event) { case AVRCP_EVENT_STATUS_CHANGED: case AVRCP_EVENT_TRACK_CHANGED: case AVRCP_EVENT_PLAYBACK_POS_CHANGED: case AVRCP_EVENT_SETTINGS_CHANGED: case AVRCP_EVENT_NOW_PLAYING_CHANGED: case AVRCP_EVENT_ADDRESSED_PLAYER_CHANGED: case AVRCP_EVENT_UIDS_CHANGED: case AVRCP_EVENT_AVAILABLE_PLAYERS_CHANGED: /* These events above require a player */ if (!session->controller || !session->controller->player) break; /* fall through */ case AVRCP_EVENT_VOLUME_CHANGED: avrcp_register_notification(session, event); break; } } if (!session->controller || !session->controller->player) return FALSE; /* Skip if player status/metadata if only volume changes is supported */ if (events == (1 << AVRCP_EVENT_VOLUME_CHANGED)) return FALSE; if ((session->controller->features & AVRCP_FEATURE_TG_PLAYER_SETTINGS) && !(events & (1 << AVRCP_EVENT_SETTINGS_CHANGED))) avrcp_list_player_attributes(session); if (!(events & (1 << AVRCP_EVENT_STATUS_CHANGED))) avrcp_get_play_status(session); if (!(events & (1 << AVRCP_EVENT_STATUS_CHANGED))) avrcp_get_element_attributes(session); return FALSE; } static void avrcp_get_capabilities(struct avrcp *session) { uint8_t buf[AVRCP_HEADER_LENGTH + AVRCP_GET_CAPABILITIES_PARAM_LENGTH]; struct avrcp_header *pdu = (void *) buf; memset(buf, 0, sizeof(buf)); set_company_id(pdu->company_id, IEEEID_BTSIG); pdu->pdu_id = AVRCP_GET_CAPABILITIES; pdu->packet_type = AVRCP_PACKET_TYPE_SINGLE; pdu->params[0] = CAP_EVENTS_SUPPORTED; pdu->params_len = cpu_to_be16(AVRCP_GET_CAPABILITIES_PARAM_LENGTH); avctp_send_vendordep_req(session->conn, AVC_CTYPE_STATUS, AVC_SUBUNIT_PANEL, buf, sizeof(buf), avrcp_get_capabilities_resp, session); } static struct avrcp *find_session(GSList *list, struct btd_device *dev) { for (; list; list = list->next) { struct avrcp *session = list->data; if (session->dev == dev) return session; } return NULL; } static void destroy_browsing(void *data) { struct avrcp *session = data; session->browsing_id = 0; } static void session_init_browsing(struct avrcp *session) { if (session->browsing_timer > 0) { timeout_remove(session->browsing_timer); session->browsing_timer = 0; } session->browsing_id = avctp_register_browsing_pdu_handler( session->conn, handle_browsing_pdu, session, destroy_browsing); } static struct avrcp_data *data_init(struct avrcp *session, const char *uuid) { struct avrcp_data *data; const sdp_record_t *rec; sdp_list_t *list, *protos; sdp_profile_desc_t *desc; int port = 0; data = g_new0(struct avrcp_data, 1); rec = btd_device_get_record(session->dev, uuid); if (rec == NULL) return data; if (sdp_get_profile_descs(rec, &list) == 0) { desc = list->data; data->version = desc->version; } sdp_get_int_attr(rec, SDP_ATTR_SUPPORTED_FEATURES, &data->features); sdp_list_free(list, free); if ((g_strcmp0(uuid, AVRCP_TARGET_UUID) != 0) || !(data->features & AVRCP_FEATURE_TG_COVERT_ART) || (sdp_get_add_access_protos(rec, &protos) != 0)) return data; /* Get the PSM port from the Additional Protocol Descriptor list * entry containing OBEX UUID */ for (list = protos; list; list = list->next) { sdp_list_t *p; for (p = list->data; p; p = p->next) { sdp_data_t *seq = p->data; if ((sdp_uuid_to_proto(&seq->val.uuid) == OBEX_UUID) && SDP_IS_UUID(seq->dtd)) { port = sdp_get_proto_port(list, L2CAP_UUID); goto done; } } } done: if (port > 0) data->obex_port = port; sdp_list_foreach(protos, (sdp_list_func_t) sdp_list_free, NULL); sdp_list_free(protos, NULL); return data; } static bool connect_browsing(gpointer user_data) { struct avrcp *session = user_data; session->browsing_timer = 0; avctp_connect_browsing(session->conn); return FALSE; } static void avrcp_connect_browsing(struct avrcp *session) { /* Immediately connect browsing channel if initiator otherwise delay * it to avoid possible collisions */ if (avctp_is_initiator(session->conn)) { avctp_connect_browsing(session->conn); return; } if (session->browsing_timer > 0) return; session->browsing_timer = timeout_add_seconds(AVRCP_BROWSING_TIMEOUT, connect_browsing, session, NULL); } static void target_init(struct avrcp *session) { struct avrcp_server *server = session->server; struct avrcp_data *target; struct avrcp_player *player; struct btd_service *service; if (session->target != NULL) return; target = data_init(session, AVRCP_REMOTE_UUID); session->target = target; DBG("%p version 0x%04x", target, target->version); service = btd_device_get_service(session->dev, AVRCP_REMOTE_UUID); btd_service_connecting_complete(service, 0); player = g_slist_nth_data(server->players, 0); if (player != NULL) { int8_t init_volume; target->player = player; player->sessions = g_slist_prepend(player->sessions, session); init_volume = media_player_get_device_volume(session->dev); media_transport_update_device_volume(session->dev, init_volume); } session->supported_events |= (1 << AVRCP_EVENT_STATUS_CHANGED) | (1 << AVRCP_EVENT_TRACK_CHANGED) | (1 << AVRCP_EVENT_TRACK_REACHED_START) | (1 << AVRCP_EVENT_TRACK_REACHED_END) | (1 << AVRCP_EVENT_SETTINGS_CHANGED); if (target->version < 0x0104) return; if (avrcp_volume_supported(target)) session->supported_events |= (1 << AVRCP_EVENT_VOLUME_CHANGED); session->supported_events |= (1 << AVRCP_EVENT_ADDRESSED_PLAYER_CHANGED) | (1 << AVRCP_EVENT_AVAILABLE_PLAYERS_CHANGED); /* Only check capabilities if controller is not supported */ if (session->controller == NULL) avrcp_get_capabilities(session); if (!(target->features & AVRCP_FEATURE_BROWSING)) return; avrcp_connect_browsing(session); } static void controller_init(struct avrcp *session) { struct avrcp_player *player; struct btd_service *service; struct avrcp_data *controller; if (session->controller != NULL) return; controller = data_init(session, AVRCP_TARGET_UUID); session->controller = controller; DBG("%p version 0x%04x", controller, controller->version); if (controller->obex_port) DBG("%p OBEX PSM 0x%04x", controller, controller->obex_port); service = btd_device_get_service(session->dev, AVRCP_TARGET_UUID); btd_service_connecting_complete(service, 0); /* Only create player if category 1 is supported */ if (controller->features & AVRCP_FEATURE_CATEGORY_1) { player = create_ct_player(session, 0); if (player == NULL) return; } if (controller->version < 0x0103) return; avrcp_get_capabilities(session); if (controller->version < 0x0104) return; if (!(controller->features & AVRCP_FEATURE_BROWSING)) return; avrcp_connect_browsing(session); } static void session_init_control(struct avrcp *session) { session->passthrough_id = avctp_register_passthrough_handler( session->conn, handle_passthrough, session); session->passthrough_handlers = passthrough_handlers; session->control_id = avctp_register_pdu_handler(session->conn, AVC_OP_VENDORDEP, handle_vendordep_pdu, session); session->control_handlers = control_handlers; if (btd_device_get_service(session->dev, AVRCP_TARGET_UUID) != NULL) controller_init(session); if (btd_device_get_service(session->dev, AVRCP_REMOTE_UUID) != NULL) target_init(session); } static void controller_destroy(struct avrcp *session) { struct avrcp_data *controller = session->controller; DBG("%p", controller); g_slist_free_full(controller->players, player_destroy); g_free(controller); } static void target_destroy(struct avrcp *session) { struct avrcp_data *target = session->target; struct avrcp_player *player = target->player; DBG("%p", target); if (player != NULL) player->sessions = g_slist_remove(player->sessions, session); g_free(target); } static void session_destroy(struct avrcp *session, int err) { struct avrcp_server *server = session->server; struct btd_service *service; server->sessions = g_slist_remove(server->sessions, session); session_abort_pending_pdu(session); service = btd_device_get_service(session->dev, AVRCP_TARGET_UUID); if (service != NULL) { if (session->control_id == 0) btd_service_connecting_complete(service, err); else btd_service_disconnecting_complete(service, 0); } service = btd_device_get_service(session->dev, AVRCP_REMOTE_UUID); if (service != NULL) { if (session->control_id == 0) btd_service_connecting_complete(service, err); else btd_service_disconnecting_complete(service, 0); } if (session->browsing_timer > 0) timeout_remove(session->browsing_timer); if (session->controller != NULL) controller_destroy(session); if (session->target != NULL) target_destroy(session); if (session->passthrough_id > 0) avctp_unregister_passthrough_handler(session->passthrough_id); if (session->control_id > 0) avctp_unregister_pdu_handler(session->control_id); if (session->browsing_id > 0) avctp_unregister_browsing_pdu_handler(session->browsing_id); g_free(session); } static struct avrcp *session_create(struct avrcp_server *server, struct btd_device *device) { struct avrcp *session; session = g_new0(struct avrcp, 1); session->server = server; session->conn = avctp_connect(device); session->dev = device; server->sessions = g_slist_append(server->sessions, session); return session; } static void state_changed(struct btd_device *device, avctp_state_t old_state, avctp_state_t new_state, int err, void *user_data) { struct avrcp_server *server; struct avrcp *session; server = find_server(servers, device_get_adapter(device)); if (!server) return; session = find_session(server->sessions, device); switch (new_state) { case AVCTP_STATE_DISCONNECTED: if (session == NULL) break; session_destroy(session, err); break; case AVCTP_STATE_CONNECTING: if (session != NULL) break; session_create(server, device); break; case AVCTP_STATE_CONNECTED: if (session == NULL || session->control_id > 0) break; session_init_control(session); break; case AVCTP_STATE_BROWSING_CONNECTED: if (session == NULL || session->browsing_id > 0) break; session_init_browsing(session); break; case AVCTP_STATE_BROWSING_CONNECTING: default: return; } } static struct avrcp_server *avrcp_server_register(struct btd_adapter *adapter) { struct avrcp_server *server; bool browsing; if (avctp_register(adapter, TRUE, &browsing) < 0) return NULL; server = g_new0(struct avrcp_server, 1); server->adapter = btd_adapter_ref(adapter); server->browsing = browsing; servers = g_slist_append(servers, server); if (!avctp_id) avctp_id = avctp_add_state_cb(NULL, state_changed, NULL); return server; } static void avrcp_server_unregister(struct avrcp_server *server) { g_slist_free_full(server->sessions, g_free); g_slist_free_full(server->players, player_destroy); servers = g_slist_remove(servers, server); avctp_unregister(server->adapter); btd_adapter_unref(server->adapter); g_free(server); if (servers) return; if (avctp_id) { avctp_remove_state_cb(avctp_id); avctp_id = 0; } } struct avrcp_player *avrcp_register_player(struct btd_adapter *adapter, struct avrcp_player_cb *cb, void *user_data, GDestroyNotify destroy) { struct avrcp_server *server; struct avrcp_player *player; GSList *l; static uint16_t id = 0; server = find_server(servers, adapter); if (!server) return NULL; player = g_new0(struct avrcp_player, 1); player->id = ++id; player->server = server; player->cb = cb; player->user_data = user_data; player->destroy = destroy; server->players = g_slist_append(server->players, player); /* Assign player to session without current player */ for (l = server->sessions; l; l = l->next) { struct avrcp *session = l->data; struct avrcp_data *target = session->target; if (target == NULL) continue; if (target->player == NULL) { target->player = player; player->sessions = g_slist_append(player->sessions, session); } } avrcp_player_event(player, AVRCP_EVENT_AVAILABLE_PLAYERS_CHANGED, NULL); return player; } void avrcp_unregister_player(struct avrcp_player *player) { struct avrcp_server *server = player->server; GSList *l; server->players = g_slist_remove(server->players, player); /* Remove player from sessions using it */ for (l = player->sessions; l; l = l->next) { struct avrcp *session = l->data; struct avrcp_data *target = session->target; if (target == NULL) continue; if (target->player != player) continue; target->player = g_slist_nth_data(server->players, 0); if (target->player) { target->player->sessions = g_slist_append( target->player->sessions, session); notify_addressed_player_changed(player); } } avrcp_player_event(player, AVRCP_EVENT_AVAILABLE_PLAYERS_CHANGED, NULL); player_destroy(player); } static gboolean avrcp_handle_set_volume(struct avctp *conn, uint8_t code, uint8_t subunit, uint8_t transaction, uint8_t *operands, size_t operand_count, void *user_data) { struct avrcp *session = user_data; struct avrcp_player *player = target_get_player(session); struct avrcp_header *pdu = (void *) operands; int8_t volume; if (code == AVC_CTYPE_REJECTED || code == AVC_CTYPE_NOT_IMPLEMENTED || pdu == NULL) return FALSE; volume = pdu->params[0] & 0x7F; /* Always attempt to update the transport volume */ media_transport_update_device_volume(session->dev, volume); if (player != NULL) player->cb->set_volume(volume, session->dev, player->user_data); return FALSE; } static int avrcp_event(struct avrcp *session, uint8_t id, const void *data) { uint8_t buf[AVRCP_HEADER_LENGTH + 2]; struct avrcp_header *pdu = (void *) buf; uint8_t code; uint16_t size; int err; /* Verify that the event is registered */ if (!(session->registered_events & (1 << id))) return -ENOENT; memset(buf, 0, sizeof(buf)); set_company_id(pdu->company_id, IEEEID_BTSIG); pdu->pdu_id = AVRCP_REGISTER_NOTIFICATION; code = AVC_CTYPE_CHANGED; pdu->params[0] = id; DBG("id=%u", id); switch (id) { case AVRCP_EVENT_VOLUME_CHANGED: size = 2; memcpy(&pdu->params[1], data, sizeof(uint8_t)); break; default: error("Unknown event %u", id); return -EINVAL; } pdu->params_len = cpu_to_be16(size); err = avctp_send_vendordep(session->conn, session->transaction_events[id], code, AVC_SUBUNIT_PANEL, buf, size + AVRCP_HEADER_LENGTH); if (err < 0) return err; /* Unregister event as per AVRCP 1.3 spec, section 5.4.2 */ session->registered_events ^= 1 << id; return err; } static bool avrcp_event_registered(struct avrcp *session, uint8_t event) { return session->registered_events & (1 << event); } int avrcp_set_volume(struct btd_device *dev, int8_t volume, bool notify) { struct avrcp_server *server; struct avrcp *session; uint8_t buf[AVRCP_HEADER_LENGTH + 1]; struct avrcp_header *pdu = (void *) buf; if (volume < 0) return -EINVAL; server = find_server(servers, device_get_adapter(dev)); if (server == NULL) return -EINVAL; session = find_session(server->sessions, dev); if (session == NULL) return -ENOTCONN; if (notify) { if (!avrcp_volume_supported(session->target)) { error("EVENT_VOLUME_CHANGED not supported"); return -ENOTSUP; } return avrcp_event(session, AVRCP_EVENT_VOLUME_CHANGED, &volume); } if (btd_opts.avrcp.volume_without_target) { /* If there is no target profile (we didn't create a controller * for it), allow the call to pass through if the remote * controller registered for a volume changed event. */ if (!session->controller && !avrcp_event_registered(session, AVRCP_EVENT_VOLUME_CHANGED)) return -ENOTSUP; } else if (!avrcp_volume_supported(session->controller)) { error("SetAbsoluteVolume not supported"); return -ENOTSUP; } memset(buf, 0, sizeof(buf)); set_company_id(pdu->company_id, IEEEID_BTSIG); pdu->pdu_id = AVRCP_SET_ABSOLUTE_VOLUME; pdu->params[0] = volume; pdu->params_len = cpu_to_be16(1); return avctp_send_vendordep_req(session->conn, AVC_CTYPE_CONTROL, AVC_SUBUNIT_PANEL, buf, sizeof(buf), avrcp_handle_set_volume, session); } struct avrcp_player *avrcp_get_target_player_by_device(struct btd_device *dev) { struct avrcp_server *server; struct avrcp *session; struct avrcp_data *target; server = find_server(servers, device_get_adapter(dev)); if (server == NULL) return NULL; session = find_session(server->sessions, dev); if (session == NULL) return NULL; target = session->target; if (target == NULL) return NULL; return target->player; } static int avrcp_connect(struct btd_service *service) { struct btd_device *dev = btd_service_get_device(service); const char *path = device_get_path(dev); DBG("path %s", path); return control_connect(service); } static int avrcp_disconnect(struct btd_service *service) { struct btd_device *dev = btd_service_get_device(service); const char *path = device_get_path(dev); DBG("path %s", path); return control_disconnect(service); } static int avrcp_target_probe(struct btd_service *service) { struct btd_device *dev = btd_service_get_device(service); DBG("path %s", device_get_path(dev)); return control_init_target(service); } static void avrcp_target_remove(struct btd_service *service) { control_unregister(service); } static void avrcp_target_server_remove(struct btd_profile *p, struct btd_adapter *adapter) { struct avrcp_server *server; DBG("path %s", adapter_get_path(adapter)); server = find_server(servers, adapter); if (!server) return; if (server->tg_record_id != 0) { adapter_service_remove(adapter, server->tg_record_id); server->tg_record_id = 0; } if (server->ct_record_id == 0) avrcp_server_unregister(server); } static int avrcp_target_server_probe(struct btd_profile *p, struct btd_adapter *adapter) { sdp_record_t *record; struct avrcp_server *server; DBG("path %s", adapter_get_path(adapter)); server = find_server(servers, adapter); if (server != NULL) goto done; server = avrcp_server_register(adapter); if (server == NULL) return -EPROTONOSUPPORT; done: record = avrcp_tg_record(server->browsing); if (!record) { error("Unable to allocate new service record"); avrcp_target_server_remove(p, adapter); return -1; } if (adapter_service_add(adapter, record) < 0) { error("Unable to register AVRCP target service record"); avrcp_target_server_remove(p, adapter); sdp_record_free(record); return -1; } server->tg_record_id = record->handle; return 0; } static struct btd_profile avrcp_target_profile = { .name = "audio-avrcp-target", .remote_uuid = AVRCP_TARGET_UUID, .device_probe = avrcp_target_probe, .device_remove = avrcp_target_remove, .connect = avrcp_connect, .disconnect = avrcp_disconnect, .adapter_probe = avrcp_target_server_probe, .adapter_remove = avrcp_target_server_remove, }; static int avrcp_controller_probe(struct btd_service *service) { struct btd_device *dev = btd_service_get_device(service); DBG("path %s", device_get_path(dev)); return control_init_remote(service); } static void avrcp_controller_remove(struct btd_service *service) { control_unregister(service); } static void avrcp_controller_server_remove(struct btd_profile *p, struct btd_adapter *adapter) { struct avrcp_server *server; DBG("path %s", adapter_get_path(adapter)); server = find_server(servers, adapter); if (!server) return; if (server->ct_record_id != 0) { adapter_service_remove(adapter, server->ct_record_id); server->ct_record_id = 0; } if (server->tg_record_id == 0) avrcp_server_unregister(server); } static int avrcp_controller_server_probe(struct btd_profile *p, struct btd_adapter *adapter) { sdp_record_t *record; struct avrcp_server *server; DBG("path %s", adapter_get_path(adapter)); server = find_server(servers, adapter); if (server != NULL) goto done; server = avrcp_server_register(adapter); if (server == NULL) return -EPROTONOSUPPORT; done: record = avrcp_ct_record(server->browsing); if (!record) { error("Unable to allocate new service record"); avrcp_controller_server_remove(p, adapter); return -1; } if (adapter_service_add(adapter, record) < 0) { error("Unable to register AVRCP service record"); avrcp_controller_server_remove(p, adapter); sdp_record_free(record); return -1; } server->ct_record_id = record->handle; return 0; } static struct btd_profile avrcp_controller_profile = { .name = "avrcp-controller", .remote_uuid = AVRCP_REMOTE_UUID, .device_probe = avrcp_controller_probe, .device_remove = avrcp_controller_remove, .connect = avrcp_connect, .disconnect = avrcp_disconnect, .adapter_probe = avrcp_controller_server_probe, .adapter_remove = avrcp_controller_server_remove, }; static int avrcp_init(void) { btd_profile_register(&avrcp_controller_profile); btd_profile_register(&avrcp_target_profile); populate_default_features(); return 0; } static void avrcp_exit(void) { btd_profile_unregister(&avrcp_controller_profile); btd_profile_unregister(&avrcp_target_profile); } BLUETOOTH_PLUGIN_DEFINE(avrcp, VERSION, BLUETOOTH_PLUGIN_PRIORITY_DEFAULT, avrcp_init, avrcp_exit) bluez-5.82/profiles/audio/PaxHeaders/source.h0000644000000000000000000000005014015011623016234 xustar0020 atime=1743516509 20 ctime=1743591284 bluez-5.82/profiles/audio/source.h0000644000000000000000000000215714015011623015722 0ustar00rootroot/* SPDX-License-Identifier: GPL-2.0-or-later */ /* * * BlueZ - Bluetooth protocol stack for Linux * * Copyright (C) 2006-2010 Nokia Corporation * Copyright (C) 2004-2010 Marcel Holtmann * Copyright (C) 2009 Joao Paulo Rechi Vita * * */ typedef enum { SOURCE_STATE_DISCONNECTED, SOURCE_STATE_CONNECTING, SOURCE_STATE_CONNECTED, SOURCE_STATE_PLAYING, } source_state_t; typedef void (*source_state_cb) (struct btd_service *service, source_state_t old_state, source_state_t new_state, void *user_data); struct btd_service; unsigned int source_add_state_cb(struct btd_service *service, source_state_cb cb, void *user_data); gboolean source_remove_state_cb(unsigned int id); int source_init(struct btd_service *service); void source_unregister(struct btd_service *service); int source_connect(struct btd_service *service); gboolean source_new_stream(struct btd_service *service, struct avdtp *session, struct avdtp_stream *stream); gboolean source_setup_stream(struct btd_service *service, struct avdtp *session); int source_disconnect(struct btd_service *service); bluez-5.82/profiles/audio/PaxHeaders/mcp.c0000644000000000000000000000005014643061455015525 xustar0020 atime=1743516583 20 ctime=1743591284 bluez-5.82/profiles/audio/mcp.c0000644000000000000000000002263114643061455015212 0ustar00rootroot// SPDX-License-Identifier: GPL-2.0-or-later /* * * BlueZ - Bluetooth protocol stack for Linux * * Copyright (C) 2020 Intel Corporation. All rights reserved. * * */ #ifdef HAVE_CONFIG_H #include #endif #define _GNU_SOURCE #include #include #include #include #include #include #include #include #include #include #include "gdbus/gdbus.h" #include "lib/bluetooth.h" #include "lib/hci.h" #include "lib/sdp.h" #include "lib/uuid.h" #include "src/dbus-common.h" #include "src/shared/util.h" #include "src/shared/att.h" #include "src/shared/queue.h" #include "src/shared/gatt-db.h" #include "src/shared/gatt-client.h" #include "src/shared/gatt-server.h" #include "src/shared/mcp.h" #include "src/shared/mcs.h" #include "btio/btio.h" #include "src/plugin.h" #include "src/adapter.h" #include "src/gatt-database.h" #include "src/device.h" #include "src/profile.h" #include "src/service.h" #include "src/log.h" #include "src/error.h" #include "player.h" #define GMCS_UUID_STR "00001849-0000-1000-8000-00805f9b34fb" struct mcp_data { struct btd_device *device; struct btd_service *service; struct bt_mcp *mcp; unsigned int state_id; struct media_player *mp; }; static void mcp_debug(const char *str, void *user_data) { DBG_IDX(0xffff, "%s", str); } static char *name2utf8(const uint8_t *name, uint16_t len) { char utf8_name[HCI_MAX_NAME_LENGTH + 2]; int i; if (g_utf8_validate((const char *) name, len, NULL)) return g_strndup((char *) name, len); len = MIN(len, sizeof(utf8_name) - 1); memset(utf8_name, 0, sizeof(utf8_name)); strncpy(utf8_name, (char *) name, len); /* Assume ASCII, and replace all non-ASCII with spaces */ for (i = 0; utf8_name[i] != '\0'; i++) { if (!isascii(utf8_name[i])) utf8_name[i] = ' '; } /* Remove leading and trailing whitespace characters */ g_strstrip(utf8_name); return g_strdup(utf8_name); } static const char *mcp_status_val_to_string(uint8_t status) { switch (status) { case BT_MCS_STATUS_PLAYING: return "playing"; case BT_MCS_STATUS_PAUSED: return "paused"; case BT_MCS_STATUS_INACTIVE: return "stopped"; case BT_MCS_STATUS_SEEKING: /* TODO: find a way for fwd/rvs seeking, probably by storing * control point operation sent before */ return "forward-seek"; default: return "error"; } } static struct mcp_data *mcp_data_new(struct btd_device *device) { struct mcp_data *data; data = new0(struct mcp_data, 1); data->device = device; return data; } static void cb_player_name(struct bt_mcp *mcp, const uint8_t *value, uint16_t length) { char *name; struct media_player *mp = bt_mcp_get_user_data(mcp); name = name2utf8(value, length); DBG("Media Player Name %s", (const char *)name); media_player_set_name(mp, name); g_free(name); } static void cb_track_changed(struct bt_mcp *mcp) { DBG("Track Changed"); /* Since track changed has happened * track title notification is expected */ } static void cb_track_title(struct bt_mcp *mcp, const uint8_t *value, uint16_t length) { char *name; uint16_t len; struct media_player *mp = bt_mcp_get_user_data(mcp); name = name2utf8(value, length); len = strlen(name); DBG("Track Title %s", (const char *)name); media_player_set_metadata(mp, NULL, "Title", name, len); media_player_metadata_changed(mp); g_free(name); } static void cb_track_duration(struct bt_mcp *mcp, int32_t duration) { struct media_player *mp = bt_mcp_get_user_data(mcp); unsigned char buf[10]; /* MCP defines duration is int32 but api takes it as uint32 */ snprintf((char *)buf, 10, "%d", duration); media_player_set_metadata(mp, NULL, "Duration", buf, sizeof(buf)); media_player_metadata_changed(mp); } static void cb_track_position(struct bt_mcp *mcp, int32_t duration) { struct media_player *mp = bt_mcp_get_user_data(mcp); /* MCP defines duration is int32 but api takes it as uint32 */ media_player_set_position(mp, duration); } static void cb_media_state(struct bt_mcp *mcp, uint8_t status) { struct media_player *mp = bt_mcp_get_user_data(mcp); media_player_set_status(mp, mcp_status_val_to_string(status)); } static const struct bt_mcp_event_callback cbs = { .player_name = &cb_player_name, .track_changed = &cb_track_changed, .track_title = &cb_track_title, .track_duration = &cb_track_duration, .track_position = &cb_track_position, .playback_speed = NULL, .seeking_speed = NULL, .play_order = NULL, .play_order_supported = NULL, .media_state = &cb_media_state, .content_control_id = NULL, }; static int ct_play(struct media_player *mp, void *user_data) { struct bt_mcp *mcp = user_data; return bt_mcp_play(mcp); } static int ct_pause(struct media_player *mp, void *user_data) { struct bt_mcp *mcp = user_data; return bt_mcp_pause(mcp); } static int ct_stop(struct media_player *mp, void *user_data) { struct bt_mcp *mcp = user_data; return bt_mcp_stop(mcp); } static int ct_next(struct media_player *mp, void *user_data) { struct bt_mcp *mcp = user_data; return bt_mcp_next_track(mcp); } static int ct_previous(struct media_player *mp, void *user_data) { struct bt_mcp *mcp = user_data; return bt_mcp_previous_track(mcp); } static const struct media_player_callback ct_cbs = { .set_setting = NULL, .play = &ct_play, .pause = &ct_pause, .stop = &ct_stop, .next = &ct_next, .previous = &ct_previous, .fast_forward = NULL, .rewind = NULL, .press = NULL, .hold = NULL, .release = NULL, .list_items = NULL, .change_folder = NULL, .search = NULL, .play_item = NULL, .add_to_nowplaying = NULL, .total_items = NULL, }; static int mcp_probe(struct btd_service *service) { struct btd_device *device = btd_service_get_device(service); struct btd_adapter *adapter = device_get_adapter(device); struct btd_gatt_database *database = btd_adapter_get_database(adapter); struct mcp_data *data = btd_service_get_user_data(service); char addr[18]; ba2str(device_get_address(device), addr); DBG("%s", addr); /* Ignore, if we were probed for this device already */ if (data) { error("Profile probed twice for the same device!"); return -EINVAL; } data = mcp_data_new(device); data->service = service; data->mcp = bt_mcp_new(btd_gatt_database_get_db(database), btd_device_get_gatt_db(device)); bt_mcp_set_debug(data->mcp, mcp_debug, NULL, NULL); btd_service_set_user_data(service, data); return 0; } static void mcp_data_free(struct mcp_data *data) { DBG(""); if (data->service) { btd_service_set_user_data(data->service, NULL); bt_mcp_set_user_data(data->mcp, NULL); } if (data->mp) { media_player_destroy(data->mp); data->mp = NULL; } bt_mcp_unref(data->mcp); free(data); } static void mcp_data_remove(struct mcp_data *data) { DBG("data %p", data); mcp_data_free(data); } static void mcp_remove(struct btd_service *service) { struct btd_device *device = btd_service_get_device(service); struct mcp_data *data; char addr[18]; ba2str(device_get_address(device), addr); DBG("%s", addr); data = btd_service_get_user_data(service); if (!data) { error("MCP service not handled by profile"); return; } mcp_data_remove(data); } static int mcp_accept(struct btd_service *service) { struct btd_device *device = btd_service_get_device(service); struct bt_gatt_client *client = btd_device_get_gatt_client(device); struct mcp_data *data = btd_service_get_user_data(service); char addr[18]; ba2str(device_get_address(device), addr); DBG("%s", addr); bt_mcp_attach(data->mcp, client); data->mp = media_player_controller_create(device_get_path(device), 0); if (data->mp == NULL) { DBG("Unable to create Media Player"); return -EINVAL; } media_player_set_callbacks(data->mp, &ct_cbs, data->mcp); bt_mcp_set_user_data(data->mcp, data->mp); bt_mcp_set_event_callbacks(data->mcp, &cbs, data->mp); btd_service_connecting_complete(service, 0); return 0; } static int mcp_connect(struct btd_service *service) { struct btd_device *device = btd_service_get_device(service); char addr[18]; ba2str(device_get_address(device), addr); DBG("%s", addr); return 0; } static int mcp_disconnect(struct btd_service *service) { struct btd_device *device = btd_service_get_device(service); struct mcp_data *data = btd_service_get_user_data(service); char addr[18]; ba2str(device_get_address(device), addr); DBG("%s", addr); if (data->mp) { media_player_destroy(data->mp); data->mp = NULL; } bt_mcp_detach(data->mcp); btd_service_disconnecting_complete(service, 0); return 0; } static int media_control_server_probe(struct btd_profile *p, struct btd_adapter *adapter) { struct btd_gatt_database *database = btd_adapter_get_database(adapter); bt_mcp_register(btd_gatt_database_get_db(database)); return 0; } static void media_control_server_remove(struct btd_profile *p, struct btd_adapter *adapter) { } static struct btd_profile mcp_profile = { .name = "mcp", .priority = BTD_PROFILE_PRIORITY_MEDIUM, .remote_uuid = GMCS_UUID_STR, .device_probe = mcp_probe, .device_remove = mcp_remove, .accept = mcp_accept, .connect = mcp_connect, .disconnect = mcp_disconnect, .adapter_probe = media_control_server_probe, .adapter_remove = media_control_server_remove, .experimental = true, }; static int mcp_init(void) { return btd_profile_register(&mcp_profile); } static void mcp_exit(void) { btd_profile_unregister(&mcp_profile); } BLUETOOTH_PLUGIN_DEFINE(mcp, VERSION, BLUETOOTH_PLUGIN_PRIORITY_DEFAULT, mcp_init, mcp_exit) bluez-5.82/profiles/audio/PaxHeaders/avdtp.h0000644000000000000000000000005014711225434016064 xustar0020 atime=1743516503 20 ctime=1743591284 bluez-5.82/profiles/audio/avdtp.h0000644000000000000000000002445114711225434015553 0ustar00rootroot/* SPDX-License-Identifier: GPL-2.0-or-later */ /* * * BlueZ - Bluetooth protocol stack for Linux * * Copyright (C) 2006-2010 Nokia Corporation * Copyright (C) 2004-2010 Marcel Holtmann * * */ typedef enum { AVDTP_SESSION_STATE_DISCONNECTED, AVDTP_SESSION_STATE_CONNECTING, AVDTP_SESSION_STATE_CONNECTED } avdtp_session_state_t; struct avdtp; struct avdtp_server; struct avdtp_stream; struct avdtp_local_sep; struct avdtp_remote_sep; struct avdtp_error { uint8_t category; union { uint8_t error_code; int posix_errno; } err; }; /* SEP capability categories */ #define AVDTP_MEDIA_TRANSPORT 0x01 #define AVDTP_REPORTING 0x02 #define AVDTP_RECOVERY 0x03 #define AVDTP_CONTENT_PROTECTION 0x04 #define AVDTP_HEADER_COMPRESSION 0x05 #define AVDTP_MULTIPLEXING 0x06 #define AVDTP_MEDIA_CODEC 0x07 #define AVDTP_DELAY_REPORTING 0x08 #define AVDTP_ERRNO 0xff /* AVDTP error definitions */ #define AVDTP_BAD_HEADER_FORMAT 0x01 #define AVDTP_BAD_LENGTH 0x11 #define AVDTP_BAD_ACP_SEID 0x12 #define AVDTP_SEP_IN_USE 0x13 #define AVDTP_SEP_NOT_IN_USE 0x14 #define AVDTP_BAD_SERV_CATEGORY 0x17 #define AVDTP_BAD_PAYLOAD_FORMAT 0x18 #define AVDTP_NOT_SUPPORTED_COMMAND 0x19 #define AVDTP_INVALID_CAPABILITIES 0x1A #define AVDTP_BAD_RECOVERY_TYPE 0x22 #define AVDTP_BAD_MEDIA_TRANSPORT_FORMAT 0x23 #define AVDTP_BAD_RECOVERY_FORMAT 0x25 #define AVDTP_BAD_ROHC_FORMAT 0x26 #define AVDTP_BAD_CP_FORMAT 0x27 #define AVDTP_BAD_MULTIPLEXING_FORMAT 0x28 #define AVDTP_UNSUPPORTED_CONFIGURATION 0x29 #define AVDTP_BAD_STATE 0x31 /* SEP types definitions */ #define AVDTP_SEP_TYPE_SOURCE 0x00 #define AVDTP_SEP_TYPE_SINK 0x01 /* Media types definitions */ #define AVDTP_MEDIA_TYPE_AUDIO 0x00 #define AVDTP_MEDIA_TYPE_VIDEO 0x01 #define AVDTP_MEDIA_TYPE_MULTIMEDIA 0x02 typedef enum { AVDTP_STATE_IDLE, AVDTP_STATE_CONFIGURED, AVDTP_STATE_OPEN, AVDTP_STATE_STREAMING, AVDTP_STATE_CLOSING, AVDTP_STATE_ABORTING, } avdtp_state_t; struct avdtp_service_capability { uint8_t category; uint8_t length; uint8_t data[0]; } __attribute__ ((packed)); #if __BYTE_ORDER == __LITTLE_ENDIAN struct avdtp_media_codec_capability { uint8_t rfa0:4; uint8_t media_type:4; uint8_t media_codec_type; uint8_t data[0]; } __attribute__ ((packed)); #elif __BYTE_ORDER == __BIG_ENDIAN struct avdtp_media_codec_capability { uint8_t media_type:4; uint8_t rfa0:4; uint8_t media_codec_type; uint8_t data[0]; } __attribute__ ((packed)); #else #error "Unknown byte order" #endif typedef void (*avdtp_session_state_cb) (struct btd_device *dev, struct avdtp *session, avdtp_session_state_t old_state, avdtp_session_state_t new_state, void *user_data); typedef void (*avdtp_stream_state_cb) (struct avdtp_stream *stream, avdtp_state_t old_state, avdtp_state_t new_state, struct avdtp_error *err, void *user_data); typedef void (*avdtp_set_configuration_cb) (struct avdtp *session, struct avdtp_stream *stream, struct avdtp_error *err); /* Callbacks for when a reply is received to a command that we sent */ struct avdtp_sep_cfm { void (*set_configuration) (struct avdtp *session, struct avdtp_local_sep *lsep, struct avdtp_stream *stream, struct avdtp_error *err, void *user_data); void (*get_configuration) (struct avdtp *session, struct avdtp_local_sep *lsep, struct avdtp_stream *stream, struct avdtp_error *err, void *user_data); void (*open) (struct avdtp *session, struct avdtp_local_sep *lsep, struct avdtp_stream *stream, struct avdtp_error *err, void *user_data); void (*start) (struct avdtp *session, struct avdtp_local_sep *lsep, struct avdtp_stream *stream, struct avdtp_error *err, void *user_data); void (*suspend) (struct avdtp *session, struct avdtp_local_sep *lsep, struct avdtp_stream *stream, struct avdtp_error *err, void *user_data); void (*close) (struct avdtp *session, struct avdtp_local_sep *lsep, struct avdtp_stream *stream, struct avdtp_error *err, void *user_data); void (*abort) (struct avdtp *session, struct avdtp_local_sep *lsep, struct avdtp_stream *stream, struct avdtp_error *err, void *user_data); void (*reconfigure) (struct avdtp *session, struct avdtp_local_sep *lsep, struct avdtp_stream *stream, struct avdtp_error *err, void *user_data); void (*delay_report) (struct avdtp *session, struct avdtp_local_sep *lsep, struct avdtp_stream *stream, struct avdtp_error *err, void *user_data); }; /* Callbacks for indicating when we received a new command. The return value * indicates whether the command should be rejected or accepted */ struct avdtp_sep_ind { gboolean (*match_codec) (struct avdtp *session, struct avdtp_media_codec_capability *codec, void *user_data); gboolean (*get_capability) (struct avdtp *session, struct avdtp_local_sep *sep, gboolean get_all, GSList **caps, uint8_t *err, void *user_data); gboolean (*set_configuration) (struct avdtp *session, struct avdtp_local_sep *lsep, struct avdtp_stream *stream, GSList *caps, avdtp_set_configuration_cb cb, void *user_data); gboolean (*get_configuration) (struct avdtp *session, struct avdtp_local_sep *lsep, uint8_t *err, void *user_data); gboolean (*open) (struct avdtp *session, struct avdtp_local_sep *lsep, struct avdtp_stream *stream, uint8_t *err, void *user_data); gboolean (*start) (struct avdtp *session, struct avdtp_local_sep *lsep, struct avdtp_stream *stream, uint8_t *err, void *user_data); gboolean (*suspend) (struct avdtp *session, struct avdtp_local_sep *sep, struct avdtp_stream *stream, uint8_t *err, void *user_data); gboolean (*close) (struct avdtp *session, struct avdtp_local_sep *sep, struct avdtp_stream *stream, uint8_t *err, void *user_data); void (*abort) (struct avdtp *session, struct avdtp_local_sep *sep, struct avdtp_stream *stream, uint8_t *err, void *user_data); gboolean (*reconfigure) (struct avdtp *session, struct avdtp_local_sep *lsep, uint8_t *err, void *user_data); gboolean (*delayreport) (struct avdtp *session, struct avdtp_local_sep *lsep, uint8_t rseid, uint16_t delay, uint8_t *err, void *user_data); }; typedef void (*avdtp_discover_cb_t) (struct avdtp *session, GSList *seps, struct avdtp_error *err, void *user_data); void avdtp_unref(struct avdtp *session); struct avdtp *avdtp_ref(struct avdtp *session); struct avdtp_service_capability *avdtp_service_cap_new(uint8_t category, void *data, int size); struct avdtp_remote_sep *avdtp_register_remote_sep(struct avdtp *session, uint8_t seid, uint8_t type, GSList *caps, bool delay_reporting); int avdtp_unregister_remote_sep(struct avdtp *session, struct avdtp_remote_sep *rsep); typedef void (*avdtp_remote_sep_destroy_t)(void *user_data); void avdtp_remote_sep_set_destroy(struct avdtp_remote_sep *sep, void *user_data, avdtp_remote_sep_destroy_t destroy); uint8_t avdtp_get_seid(struct avdtp_remote_sep *sep); uint8_t avdtp_get_type(struct avdtp_remote_sep *sep); struct avdtp_service_capability *avdtp_get_codec(struct avdtp_remote_sep *sep); bool avdtp_get_delay_reporting(struct avdtp_remote_sep *sep); int avdtp_discover(struct avdtp *session, avdtp_discover_cb_t cb, void *user_data); gboolean avdtp_has_stream(struct avdtp *session, struct avdtp_stream *stream); unsigned int avdtp_stream_add_cb(struct avdtp *session, struct avdtp_stream *stream, avdtp_stream_state_cb cb, void *data); gboolean avdtp_stream_remove_cb(struct avdtp *session, struct avdtp_stream *stream, unsigned int id); gboolean avdtp_stream_set_transport(struct avdtp_stream *stream, int fd, size_t imtu, size_t omtu); gboolean avdtp_stream_get_transport(struct avdtp_stream *stream, int *sock, uint16_t *imtu, uint16_t *omtu, GSList **caps); struct avdtp_service_capability *avdtp_stream_get_codec( struct avdtp_stream *stream); gboolean avdtp_stream_has_capabilities(struct avdtp_stream *stream, GSList *caps); gboolean avdtp_stream_has_delay_reporting(struct avdtp_stream *stream); struct avdtp_remote_sep *avdtp_stream_get_remote_sep( struct avdtp_stream *stream); unsigned int avdtp_add_state_cb(struct btd_device *dev, avdtp_session_state_cb cb, void *user_data); gboolean avdtp_remove_state_cb(unsigned int id); int avdtp_set_configuration(struct avdtp *session, struct avdtp_remote_sep *rsep, struct avdtp_local_sep *lsep, GSList *caps, struct avdtp_stream **stream); int avdtp_get_configuration(struct avdtp *session, struct avdtp_stream *stream); int avdtp_open(struct avdtp *session, struct avdtp_stream *stream); int avdtp_start(struct avdtp *session, struct avdtp_stream *stream); int avdtp_suspend(struct avdtp *session, struct avdtp_stream *stream); int avdtp_close(struct avdtp *session, struct avdtp_stream *stream, gboolean immediate); int avdtp_abort(struct avdtp *session, struct avdtp_stream *stream); int avdtp_delay_report(struct avdtp *session, struct avdtp_stream *stream, uint16_t delay); struct avdtp_local_sep *avdtp_register_sep(struct queue *lseps, uint64_t *seid_pool, uint8_t type, uint8_t media_type, uint8_t codec_type, gboolean delay_reporting, struct avdtp_sep_ind *ind, struct avdtp_sep_cfm *cfm, void *user_data); /* Find a matching pair of local and remote SEP ID's */ struct avdtp_remote_sep *avdtp_find_remote_sep(struct avdtp *session, struct avdtp_local_sep *lsep); int avdtp_unregister_sep(struct queue *lseps, uint64_t *seid_pool, struct avdtp_local_sep *sep); avdtp_state_t avdtp_sep_get_state(struct avdtp_local_sep *sep); uint8_t avdtp_sep_get_seid(struct avdtp_local_sep *sep); void avdtp_error_init(struct avdtp_error *err, uint8_t type, int id); const char *avdtp_strerror(struct avdtp_error *err); uint8_t avdtp_error_category(struct avdtp_error *err); int avdtp_error_error_code(struct avdtp_error *err); int avdtp_error_posix_errno(struct avdtp_error *err); struct btd_adapter *avdtp_get_adapter(struct avdtp *session); struct btd_device *avdtp_get_device(struct avdtp *session); struct avdtp_server *avdtp_get_server(struct avdtp_local_sep *lsep); struct avdtp *avdtp_new(GIOChannel *chan, struct btd_device *device, struct queue *lseps); uint16_t avdtp_get_version(struct avdtp *session); bluez-5.82/profiles/audio/PaxHeaders/player.c0000644000000000000000000000005014711225434016235 xustar0020 atime=1743516513 20 ctime=1743591284 bluez-5.82/profiles/audio/player.c0000644000000000000000000013665414711225434015735 0ustar00rootroot// SPDX-License-Identifier: GPL-2.0-or-later /* * * BlueZ - Bluetooth protocol stack for Linux * * Copyright (C) 2006-2007 Nokia Corporation * Copyright (C) 2004-2009 Marcel Holtmann * Copyright (C) 2012-2012 Intel Corporation * */ #ifdef HAVE_CONFIG_H #include #endif #define _GNU_SOURCE #include #include #include #include #include #include #include #include #include #include "gdbus/gdbus.h" #include "src/log.h" #include "src/dbus-common.h" #include "src/error.h" #include "player.h" #define MEDIA_PLAYER_INTERFACE "org.bluez.MediaPlayer1" #define MEDIA_FOLDER_INTERFACE "org.bluez.MediaFolder1" #define MEDIA_ITEM_INTERFACE "org.bluez.MediaItem1" struct player_callback { const struct media_player_callback *cbs; void *user_data; }; struct pending_req { GDBusPendingPropertySet id; const char *key; const char *value; }; struct media_item { struct media_player *player; char *path; /* Item object path */ char *name; /* Item name */ player_item_type_t type; /* Item type */ player_folder_type_t folder_type; /* Folder type */ bool playable; /* Item playable flag */ uint64_t uid; /* Item uid */ GHashTable *metadata; /* Item metadata */ }; struct media_folder { struct media_folder *parent; struct media_item *item; /* Folder item */ uint32_t number_of_items;/* Number of items */ GSList *subfolders; GSList *items; DBusMessage *msg; }; struct media_player { char *device; /* Device path */ char *name; /* Player name */ char *type; /* Player type */ char *subtype; /* Player subtype */ bool browsable; /* Player browsing feature */ bool searchable; /* Player searching feature */ struct media_folder *scope; /* Player current scope */ struct media_folder *folder; /* Player current folder */ struct media_folder *search; /* Player search folder */ struct media_folder *playlist; /* Player current playlist */ char *path; /* Player object path */ GHashTable *settings; /* Player settings */ GHashTable *track; /* Player current track */ char *status; uint32_t position; GTimer *progress; struct player_callback *cb; GSList *pending; GSList *folders; uint16_t obex_port; }; static void append_track(void *key, void *value, void *user_data) { DBusMessageIter *dict = user_data; const char *strkey = key; if (strcasecmp(strkey, "Duration") == 0 || strcasecmp(strkey, "TrackNumber") == 0 || strcasecmp(strkey, "NumberOfTracks") == 0) { uint32_t num = atoi(value); dict_append_entry(dict, key, DBUS_TYPE_UINT32, &num); } else if (strcasecmp(strkey, "Item") == 0) { dict_append_entry(dict, key, DBUS_TYPE_OBJECT_PATH, &value); } else { dict_append_entry(dict, key, DBUS_TYPE_STRING, &value); } } static struct pending_req *find_pending(struct media_player *mp, const char *key) { GSList *l; for (l = mp->pending; l; l = l->next) { struct pending_req *p = l->data; if (strcasecmp(key, p->key) == 0) return p; } return NULL; } static struct pending_req *pending_new(GDBusPendingPropertySet id, const char *key, const char *value) { struct pending_req *p; p = g_new0(struct pending_req, 1); p->id = id; p->key = key; p->value = value; return p; } static uint32_t media_player_get_position(struct media_player *mp) { double timedelta; uint32_t sec, msec; if (g_strcmp0(mp->status, "playing") != 0 || mp->position == UINT32_MAX) return mp->position; timedelta = g_timer_elapsed(mp->progress, NULL); sec = (uint32_t) timedelta; msec = (uint32_t) ((timedelta - sec) * 1000); return mp->position + sec * 1000 + msec; } static gboolean get_position(const GDBusPropertyTable *property, DBusMessageIter *iter, void *data) { struct media_player *mp = data; uint32_t position; position = media_player_get_position(mp); dbus_message_iter_append_basic(iter, DBUS_TYPE_UINT32, &position); return TRUE; } static gboolean status_exists(const GDBusPropertyTable *property, void *data) { struct media_player *mp = data; return mp->status != NULL; } static gboolean get_status(const GDBusPropertyTable *property, DBusMessageIter *iter, void *data) { struct media_player *mp = data; if (mp->status == NULL) return FALSE; dbus_message_iter_append_basic(iter, DBUS_TYPE_STRING, &mp->status); return TRUE; } static gboolean setting_exists(const GDBusPropertyTable *property, void *data) { struct media_player *mp = data; const char *value; value = g_hash_table_lookup(mp->settings, property->name); return value ? TRUE : FALSE; } static gboolean get_setting(const GDBusPropertyTable *property, DBusMessageIter *iter, void *data) { struct media_player *mp = data; const char *value; value = g_hash_table_lookup(mp->settings, property->name); if (value == NULL) return FALSE; dbus_message_iter_append_basic(iter, DBUS_TYPE_STRING, &value); return TRUE; } static void player_set_setting(struct media_player *mp, GDBusPendingPropertySet id, const char *key, const char *value) { struct player_callback *cb = mp->cb; struct pending_req *p; if (cb == NULL || cb->cbs->set_setting == NULL) { g_dbus_pending_property_error(id, ERROR_INTERFACE ".NotSupported", "Operation is not supported"); return; } p = find_pending(mp, key); if (p != NULL) { g_dbus_pending_property_error(id, ERROR_INTERFACE ".InProgress", "Operation already in progress"); return; } if (!cb->cbs->set_setting(mp, key, value, cb->user_data)) { g_dbus_pending_property_error(id, ERROR_INTERFACE ".InvalidArguments", "Invalid arguments in method call"); return; } p = pending_new(id, key, value); mp->pending = g_slist_append(mp->pending, p); } static void set_setting(const GDBusPropertyTable *property, DBusMessageIter *iter, GDBusPendingPropertySet id, void *data) { struct media_player *mp = data; const char *value, *current; if (dbus_message_iter_get_arg_type(iter) != DBUS_TYPE_STRING) { g_dbus_pending_property_error(id, ERROR_INTERFACE ".InvalidArguments", "Invalid arguments in method call"); return; } dbus_message_iter_get_basic(iter, &value); current = g_hash_table_lookup(mp->settings, property->name); if (g_strcmp0(current, value) == 0) { g_dbus_pending_property_success(id); return; } player_set_setting(mp, id, property->name, value); } static gboolean track_exists(const GDBusPropertyTable *property, void *data) { struct media_player *mp = data; return g_hash_table_size(mp->track) != 0; } static gboolean get_track(const GDBusPropertyTable *property, DBusMessageIter *iter, void *data) { struct media_player *mp = data; DBusMessageIter dict; dbus_message_iter_open_container(iter, DBUS_TYPE_ARRAY, DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING DBUS_TYPE_STRING_AS_STRING DBUS_TYPE_VARIANT_AS_STRING DBUS_DICT_ENTRY_END_CHAR_AS_STRING, &dict); g_hash_table_foreach(mp->track, append_track, &dict); dbus_message_iter_close_container(iter, &dict); return TRUE; } static gboolean get_device(const GDBusPropertyTable *property, DBusMessageIter *iter, void *data) { struct media_player *mp = data; dbus_message_iter_append_basic(iter, DBUS_TYPE_OBJECT_PATH, &mp->device); return TRUE; } static gboolean name_exists(const GDBusPropertyTable *property, void *data) { struct media_player *mp = data; return mp->name != NULL; } static gboolean get_name(const GDBusPropertyTable *property, DBusMessageIter *iter, void *data) { struct media_player *mp = data; if (mp->name == NULL) return FALSE; dbus_message_iter_append_basic(iter, DBUS_TYPE_STRING, &mp->name); return TRUE; } static gboolean type_exists(const GDBusPropertyTable *property, void *data) { struct media_player *mp = data; return mp->type != NULL; } static gboolean get_type(const GDBusPropertyTable *property, DBusMessageIter *iter, void *data) { struct media_player *mp = data; if (mp->type == NULL) return FALSE; dbus_message_iter_append_basic(iter, DBUS_TYPE_STRING, &mp->type); return TRUE; } static gboolean subtype_exists(const GDBusPropertyTable *property, void *data) { struct media_player *mp = data; return mp->subtype != NULL; } static gboolean get_subtype(const GDBusPropertyTable *property, DBusMessageIter *iter, void *data) { struct media_player *mp = data; if (mp->subtype == NULL) return FALSE; dbus_message_iter_append_basic(iter, DBUS_TYPE_STRING, &mp->subtype); return TRUE; } static gboolean browsable_exists(const GDBusPropertyTable *property, void *data) { struct media_player *mp = data; return mp->folder != NULL; } static gboolean get_browsable(const GDBusPropertyTable *property, DBusMessageIter *iter, void *data) { struct media_player *mp = data; dbus_bool_t value; if (mp->folder == NULL) return FALSE; value = mp->browsable; dbus_message_iter_append_basic(iter, DBUS_TYPE_BOOLEAN, &value); return TRUE; } static gboolean searchable_exists(const GDBusPropertyTable *property, void *data) { struct media_player *mp = data; return mp->folder != NULL; } static gboolean get_searchable(const GDBusPropertyTable *property, DBusMessageIter *iter, void *data) { struct media_player *mp = data; dbus_bool_t value; if (mp->folder == NULL) return FALSE; value = mp->searchable; dbus_message_iter_append_basic(iter, DBUS_TYPE_BOOLEAN, &value); return TRUE; } static gboolean playlist_exists(const GDBusPropertyTable *property, void *data) { struct media_player *mp = data; return mp->playlist != NULL; } static gboolean get_playlist(const GDBusPropertyTable *property, DBusMessageIter *iter, void *data) { struct media_player *mp = data; struct media_folder *playlist = mp->playlist; if (playlist == NULL) return FALSE; dbus_message_iter_append_basic(iter, DBUS_TYPE_OBJECT_PATH, &playlist->item->path); return TRUE; } static gboolean obexport_exists(const GDBusPropertyTable *property, void *data) { struct media_player *mp = data; return mp->obex_port != 0; } static gboolean get_obexport(const GDBusPropertyTable *property, DBusMessageIter *iter, void *data) { struct media_player *mp = data; if (mp->obex_port == 0) return FALSE; dbus_message_iter_append_basic(iter, DBUS_TYPE_UINT16, &mp->obex_port); return TRUE; } static DBusMessage *media_player_play(DBusConnection *conn, DBusMessage *msg, void *data) { struct media_player *mp = data; struct player_callback *cb = mp->cb; int err; if (cb->cbs->play == NULL) return btd_error_not_supported(msg); err = cb->cbs->play(mp, cb->user_data); if (err < 0) return btd_error_failed(msg, strerror(-err)); return g_dbus_create_reply(msg, DBUS_TYPE_INVALID); } static DBusMessage *media_player_pause(DBusConnection *conn, DBusMessage *msg, void *data) { struct media_player *mp = data; struct player_callback *cb = mp->cb; int err; if (cb->cbs->pause == NULL) return btd_error_not_supported(msg); err = cb->cbs->pause(mp, cb->user_data); if (err < 0) return btd_error_failed(msg, strerror(-err)); return g_dbus_create_reply(msg, DBUS_TYPE_INVALID); } static DBusMessage *media_player_stop(DBusConnection *conn, DBusMessage *msg, void *data) { struct media_player *mp = data; struct player_callback *cb = mp->cb; int err; if (cb->cbs->stop == NULL) return btd_error_not_supported(msg); err = cb->cbs->stop(mp, cb->user_data); if (err < 0) return btd_error_failed(msg, strerror(-err)); return g_dbus_create_reply(msg, DBUS_TYPE_INVALID); } static DBusMessage *media_player_next(DBusConnection *conn, DBusMessage *msg, void *data) { struct media_player *mp = data; struct player_callback *cb = mp->cb; int err; if (cb->cbs->next == NULL) return btd_error_not_supported(msg); err = cb->cbs->next(mp, cb->user_data); if (err < 0) return btd_error_failed(msg, strerror(-err)); return g_dbus_create_reply(msg, DBUS_TYPE_INVALID); } static DBusMessage *media_player_previous(DBusConnection *conn, DBusMessage *msg, void *data) { struct media_player *mp = data; struct player_callback *cb = mp->cb; int err; if (cb->cbs->previous == NULL) return btd_error_not_supported(msg); err = cb->cbs->previous(mp, cb->user_data); if (err < 0) return btd_error_failed(msg, strerror(-err)); return g_dbus_create_reply(msg, DBUS_TYPE_INVALID); } static DBusMessage *media_player_fast_forward(DBusConnection *conn, DBusMessage *msg, void *data) { struct media_player *mp = data; struct player_callback *cb = mp->cb; int err; if (cb->cbs->fast_forward == NULL) return btd_error_not_supported(msg); err = cb->cbs->fast_forward(mp, cb->user_data); if (err < 0) return btd_error_failed(msg, strerror(-err)); return g_dbus_create_reply(msg, DBUS_TYPE_INVALID); } static DBusMessage *media_player_rewind(DBusConnection *conn, DBusMessage *msg, void *data) { struct media_player *mp = data; struct player_callback *cb = mp->cb; int err; if (cb->cbs->rewind == NULL) return btd_error_not_supported(msg); err = cb->cbs->rewind(mp, cb->user_data); if (err < 0) return btd_error_failed(msg, strerror(-err)); return g_dbus_create_reply(msg, DBUS_TYPE_INVALID); } static DBusMessage *media_player_press(DBusConnection *conn, DBusMessage *msg, void *data) { struct media_player *mp = data; struct player_callback *cb = mp->cb; int err; uint8_t avc_key; if (cb->cbs->press == NULL) return btd_error_not_supported(msg); if (!dbus_message_get_args(msg, NULL, DBUS_TYPE_BYTE, &avc_key, DBUS_TYPE_INVALID)) return btd_error_invalid_args(msg); err = cb->cbs->press(mp, avc_key, cb->user_data); if (err < 0) return btd_error_failed(msg, strerror(-err)); return g_dbus_create_reply(msg, DBUS_TYPE_INVALID); } static DBusMessage *media_player_hold(DBusConnection *conn, DBusMessage *msg, void *data) { struct media_player *mp = data; struct player_callback *cb = mp->cb; int err; uint8_t avc_key; if (cb->cbs->hold == NULL) return btd_error_not_supported(msg); if (!dbus_message_get_args(msg, NULL, DBUS_TYPE_BYTE, &avc_key, DBUS_TYPE_INVALID)) return btd_error_invalid_args(msg); err = cb->cbs->hold(mp, avc_key, cb->user_data); if (err < 0) return btd_error_failed(msg, strerror(-err)); return g_dbus_create_reply(msg, DBUS_TYPE_INVALID); } static DBusMessage *media_player_release(DBusConnection *conn, DBusMessage *msg, void *data) { struct media_player *mp = data; struct player_callback *cb = mp->cb; int err; if (cb->cbs->release == NULL) return btd_error_not_supported(msg); err = cb->cbs->release(mp, cb->user_data); if (err < 0) return btd_error_failed(msg, strerror(-err)); return g_dbus_create_reply(msg, DBUS_TYPE_INVALID); } static void parse_folder_list(gpointer data, gpointer user_data) { struct media_item *item = data; DBusMessageIter *array = user_data; DBusMessageIter entry; dbus_message_iter_open_container(array, DBUS_TYPE_DICT_ENTRY, NULL, &entry); dbus_message_iter_append_basic(&entry, DBUS_TYPE_OBJECT_PATH, &item->path); g_dbus_get_properties(btd_get_dbus_connection(), item->path, MEDIA_ITEM_INTERFACE, &entry); dbus_message_iter_close_container(array, &entry); } void media_player_list_complete(struct media_player *mp, GSList *items, int err) { struct media_folder *folder = mp->scope; DBusMessage *reply; DBusMessageIter iter, array; if (folder == NULL || folder->msg == NULL) return; if (err < 0) { reply = btd_error_failed(folder->msg, strerror(-err)); goto done; } reply = dbus_message_new_method_return(folder->msg); dbus_message_iter_init_append(reply, &iter); dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY, DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING DBUS_TYPE_OBJECT_PATH_AS_STRING DBUS_TYPE_ARRAY_AS_STRING DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING DBUS_TYPE_STRING_AS_STRING DBUS_TYPE_VARIANT_AS_STRING DBUS_DICT_ENTRY_END_CHAR_AS_STRING DBUS_DICT_ENTRY_END_CHAR_AS_STRING, &array); g_slist_foreach(items, parse_folder_list, &array); dbus_message_iter_close_container(&iter, &array); done: g_dbus_send_message(btd_get_dbus_connection(), reply); dbus_message_unref(folder->msg); folder->msg = NULL; } static struct media_item * media_player_create_subfolder(struct media_player *mp, const char *name, uint64_t uid) { struct media_folder *folder = mp->scope; struct media_item *item; char *path; path = g_strdup_printf("%s/%s", folder->item->name, name); DBG("%s", path); item = media_player_create_item(mp, path, PLAYER_ITEM_TYPE_FOLDER, uid); g_free(path); return item; } void media_player_search_complete(struct media_player *mp, int ret) { struct media_folder *folder = mp->scope; struct media_folder *search = mp->search; DBusMessage *reply; if (folder == NULL || folder->msg == NULL) return; if (ret < 0) { reply = btd_error_failed(folder->msg, strerror(-ret)); goto done; } if (search == NULL) { search = g_new0(struct media_folder, 1); search->item = media_player_create_subfolder(mp, "search", 0); mp->search = search; mp->folders = g_slist_prepend(mp->folders, search); } search->number_of_items = ret; reply = g_dbus_create_reply(folder->msg, DBUS_TYPE_OBJECT_PATH, &search->item->path, DBUS_TYPE_INVALID); done: g_dbus_send_message(btd_get_dbus_connection(), reply); dbus_message_unref(folder->msg); folder->msg = NULL; } void media_player_total_items_complete(struct media_player *mp, uint32_t num_of_items) { struct media_folder *folder = mp->scope; if (folder == NULL || folder->msg == NULL) return; if (folder->number_of_items != num_of_items) { folder->number_of_items = num_of_items; g_dbus_emit_property_changed(btd_get_dbus_connection(), mp->path, MEDIA_FOLDER_INTERFACE, "NumberOfItems"); } } static const GDBusMethodTable media_player_methods[] = { { GDBUS_METHOD("Play", NULL, NULL, media_player_play) }, { GDBUS_METHOD("Pause", NULL, NULL, media_player_pause) }, { GDBUS_METHOD("Stop", NULL, NULL, media_player_stop) }, { GDBUS_METHOD("Next", NULL, NULL, media_player_next) }, { GDBUS_METHOD("Previous", NULL, NULL, media_player_previous) }, { GDBUS_METHOD("FastForward", NULL, NULL, media_player_fast_forward) }, { GDBUS_METHOD("Rewind", NULL, NULL, media_player_rewind) }, { GDBUS_METHOD("Press", GDBUS_ARGS({"avc_key", "y"}), NULL, media_player_press) }, { GDBUS_METHOD("Hold", GDBUS_ARGS({"avc_key", "y"}), NULL, media_player_hold) }, { GDBUS_METHOD("Release", NULL, NULL, media_player_release) }, { } }; static const GDBusSignalTable media_player_signals[] = { { } }; static const GDBusPropertyTable media_player_properties[] = { { "Name", "s", get_name, NULL, name_exists }, { "Type", "s", get_type, NULL, type_exists }, { "Subtype", "s", get_subtype, NULL, subtype_exists }, { "Position", "u", get_position, NULL, NULL }, { "Status", "s", get_status, NULL, status_exists }, { "Equalizer", "s", get_setting, set_setting, setting_exists }, { "Repeat", "s", get_setting, set_setting, setting_exists }, { "Shuffle", "s", get_setting, set_setting, setting_exists }, { "Scan", "s", get_setting, set_setting, setting_exists }, { "Track", "a{sv}", get_track, NULL, track_exists }, { "Device", "o", get_device, NULL, NULL }, { "Browsable", "b", get_browsable, NULL, browsable_exists }, { "Searchable", "b", get_searchable, NULL, searchable_exists }, { "Playlist", "o", get_playlist, NULL, playlist_exists }, { "ObexPort", "q", get_obexport, NULL, obexport_exists, G_DBUS_PROPERTY_FLAG_EXPERIMENTAL }, { } }; static DBusMessage *media_folder_search(DBusConnection *conn, DBusMessage *msg, void *data) { struct media_player *mp = data; struct media_folder *folder = mp->scope; struct player_callback *cb = mp->cb; DBusMessageIter iter; const char *string; int err; dbus_message_iter_init(msg, &iter); if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_STRING) return btd_error_invalid_args(msg); dbus_message_iter_get_basic(&iter, &string); if (!mp->searchable || folder != mp->folder || !cb->cbs->search) return btd_error_not_supported(msg); if (folder->msg != NULL) return btd_error_failed(msg, strerror(EINVAL)); err = cb->cbs->search(mp, string, cb->user_data); if (err < 0) return btd_error_failed(msg, strerror(-err)); folder->msg = dbus_message_ref(msg); return NULL; } static int parse_filters(struct media_player *player, DBusMessageIter *iter, uint32_t *start, uint32_t *end) { struct media_folder *folder = player->scope; DBusMessageIter dict; int ctype; *start = 0; *end = folder->number_of_items ? folder->number_of_items - 1 : UINT32_MAX; ctype = dbus_message_iter_get_arg_type(iter); if (ctype != DBUS_TYPE_ARRAY) return FALSE; dbus_message_iter_recurse(iter, &dict); while ((ctype = dbus_message_iter_get_arg_type(&dict)) != DBUS_TYPE_INVALID) { DBusMessageIter entry, var; const char *key; if (ctype != DBUS_TYPE_DICT_ENTRY) return -EINVAL; dbus_message_iter_recurse(&dict, &entry); if (dbus_message_iter_get_arg_type(&entry) != DBUS_TYPE_STRING) return -EINVAL; dbus_message_iter_get_basic(&entry, &key); dbus_message_iter_next(&entry); if (dbus_message_iter_get_arg_type(&entry) != DBUS_TYPE_VARIANT) return -EINVAL; dbus_message_iter_recurse(&entry, &var); if (dbus_message_iter_get_arg_type(&var) != DBUS_TYPE_UINT32) return -EINVAL; if (strcasecmp(key, "Start") == 0) dbus_message_iter_get_basic(&var, start); else if (strcasecmp(key, "End") == 0) dbus_message_iter_get_basic(&var, end); dbus_message_iter_next(&dict); } if (folder->number_of_items > 0 && *end > folder->number_of_items) *end = folder->number_of_items; return 0; } static DBusMessage *media_folder_list_items(DBusConnection *conn, DBusMessage *msg, void *data) { struct media_player *mp = data; struct media_folder *folder = mp->scope; struct player_callback *cb = mp->cb; DBusMessageIter iter; uint32_t start, end; int err; dbus_message_iter_init(msg, &iter); if (parse_filters(mp, &iter, &start, &end) < 0) return btd_error_invalid_args(msg); if (cb->cbs->list_items == NULL) return btd_error_not_supported(msg); if (folder->msg != NULL) return btd_error_failed(msg, strerror(EBUSY)); err = cb->cbs->list_items(mp, folder->item->name, start, end, cb->user_data); if (err < 0) return btd_error_failed(msg, strerror(-err)); folder->msg = dbus_message_ref(msg); return NULL; } static void media_item_free(struct media_item *item) { if (item->metadata != NULL) g_hash_table_unref(item->metadata); g_free(item->path); g_free(item->name); g_free(item); } static void media_item_destroy(void *data) { struct media_item *item = data; DBG("%s", item->path); g_dbus_unregister_interface(btd_get_dbus_connection(), item->path, MEDIA_ITEM_INTERFACE); media_item_free(item); } static void media_folder_destroy(void *data) { struct media_folder *folder = data; g_slist_free_full(folder->subfolders, media_folder_destroy); g_slist_free_full(folder->items, media_item_destroy); if (folder->msg != NULL) dbus_message_unref(folder->msg); media_item_destroy(folder->item); g_free(folder); } static void media_player_change_scope(struct media_player *mp, struct media_folder *folder) { struct player_callback *cb = mp->cb; int err; if (mp->scope == folder) return; DBG("%s", folder->item->name); /* Skip setting current folder if folder is current playlist/search */ if (folder == mp->playlist || folder == mp->search) goto cleanup; mp->folder = folder; /* Skip item cleanup if scope is the current playlist */ if (mp->scope == mp->playlist) goto done; cleanup: g_slist_free_full(mp->scope->items, media_item_destroy); mp->scope->items = NULL; /* Destroy search folder if it exists and is not being set as scope */ if (mp->search != NULL && folder != mp->search) { mp->folders = g_slist_remove(mp->folders, mp->search); media_folder_destroy(mp->search); mp->search = NULL; } done: mp->scope = folder; if (cb->cbs->total_items) { err = cb->cbs->total_items(mp, folder->item->name, cb->user_data); if (err < 0) DBG("Failed to get total num of items"); } else { g_dbus_emit_property_changed(btd_get_dbus_connection(), mp->path, MEDIA_FOLDER_INTERFACE, "NumberOfItems"); } g_dbus_emit_property_changed(btd_get_dbus_connection(), mp->path, MEDIA_FOLDER_INTERFACE, "Name"); } static struct media_folder *find_folder(GSList *folders, const char *pattern) { GSList *l; for (l = folders; l; l = l->next) { struct media_folder *folder = l->data; if (g_str_equal(folder->item->name, pattern)) return folder; if (g_str_equal(folder->item->path, pattern)) return folder; folder = find_folder(folder->subfolders, pattern); if (folder != NULL) return folder; } return NULL; } static struct media_folder *media_player_find_folder(struct media_player *mp, const char *pattern) { return find_folder(mp->folders, pattern); } static DBusMessage *media_folder_change_folder(DBusConnection *conn, DBusMessage *msg, void *data) { struct media_player *mp = data; struct media_folder *folder = mp->scope; struct player_callback *cb = mp->cb; const char *path; int err; if (!dbus_message_get_args(msg, NULL, DBUS_TYPE_OBJECT_PATH, &path, DBUS_TYPE_INVALID)) return btd_error_invalid_args(msg); if (folder->msg != NULL) return btd_error_failed(msg, strerror(EBUSY)); folder = media_player_find_folder(mp, path); if (folder == NULL) return btd_error_invalid_args(msg); if (mp->scope == folder) return g_dbus_create_reply(msg, DBUS_TYPE_INVALID); if (folder == mp->playlist || folder == mp->folder || folder == mp->search) { media_player_change_scope(mp, folder); return g_dbus_create_reply(msg, DBUS_TYPE_INVALID); } /* * ChangePath can only navigate one level up/down so check if folder * is direct child or parent of the current folder otherwise fail. */ if (!g_slist_find(mp->folder->subfolders, folder) && !g_slist_find(folder->subfolders, mp->folder)) return btd_error_invalid_args(msg); if (cb->cbs->change_folder == NULL) return btd_error_not_supported(msg); err = cb->cbs->change_folder(mp, folder->item->name, folder->item->uid, cb->user_data); if (err < 0) return btd_error_failed(msg, strerror(-err)); mp->scope->msg = dbus_message_ref(msg); return NULL; } static gboolean folder_name_exists(const GDBusPropertyTable *property, void *data) { struct media_player *mp = data; struct media_folder *folder = mp->scope; if (folder == NULL || folder->item == NULL) return FALSE; return folder->item->name != NULL; } static gboolean get_folder_name(const GDBusPropertyTable *property, DBusMessageIter *iter, void *data) { struct media_player *mp = data; struct media_folder *folder = mp->scope; if (folder == NULL || folder->item == NULL) return FALSE; DBG("%s", folder->item->name); dbus_message_iter_append_basic(iter, DBUS_TYPE_STRING, &folder->item->name); return TRUE; } static gboolean items_exists(const GDBusPropertyTable *property, void *data) { struct media_player *mp = data; return mp->scope != NULL; } static gboolean get_items(const GDBusPropertyTable *property, DBusMessageIter *iter, void *data) { struct media_player *mp = data; struct media_folder *folder = mp->scope; if (folder == NULL) return FALSE; DBG("%u", folder->number_of_items); dbus_message_iter_append_basic(iter, DBUS_TYPE_UINT32, &folder->number_of_items); return TRUE; } static const GDBusMethodTable media_folder_methods[] = { { GDBUS_ASYNC_METHOD("Search", GDBUS_ARGS({ "string", "s" }, { "filter", "a{sv}" }), GDBUS_ARGS({ "folder", "o" }), media_folder_search) }, { GDBUS_ASYNC_METHOD("ListItems", GDBUS_ARGS({ "filter", "a{sv}" }), GDBUS_ARGS({ "items", "a{oa{sv}}" }), media_folder_list_items) }, { GDBUS_ASYNC_METHOD("ChangeFolder", GDBUS_ARGS({ "folder", "o" }), NULL, media_folder_change_folder) }, { } }; static const GDBusPropertyTable media_folder_properties[] = { { "Name", "s", get_folder_name, NULL, folder_name_exists }, { "NumberOfItems", "u", get_items, NULL, items_exists }, { } }; static void media_player_set_scope(struct media_player *mp, struct media_folder *folder) { if (mp->scope == NULL) { if (!g_dbus_register_interface(btd_get_dbus_connection(), mp->path, MEDIA_FOLDER_INTERFACE, media_folder_methods, NULL, media_folder_properties, mp, NULL)) { error("D-Bus failed to register %s on %s path", MEDIA_FOLDER_INTERFACE, mp->path); return; } mp->scope = folder; return; } return media_player_change_scope(mp, folder); } static struct media_folder * media_player_find_folder_by_uid(struct media_player *mp, uint64_t uid) { struct media_folder *folder = mp->scope; struct media_folder *parent = folder->parent; GSList *l; if (parent && parent->item->uid == uid) return parent; for (l = folder->subfolders; l; l = l->next) { struct media_folder *folder = l->data; if (folder->item->uid == uid) return folder; } return NULL; } static void media_player_set_folder_by_uid(struct media_player *mp, uint64_t uid, uint32_t number_of_items) { struct media_folder *folder; DBG("uid %" PRIu64 " number of items %u", uid, number_of_items); folder = media_player_find_folder_by_uid(mp, uid); if (folder == NULL) { error("Unknown folder: %" PRIu64, uid); return; } folder->number_of_items = number_of_items; media_player_set_scope(mp, folder); } void media_player_change_folder_complete(struct media_player *mp, const char *path, uint64_t uid, int ret) { struct media_folder *folder = mp->scope; DBusMessage *reply; if (folder == NULL || folder->msg == NULL) return; if (ret < 0) { reply = btd_error_failed(folder->msg, strerror(-ret)); goto done; } media_player_set_folder_by_uid(mp, uid, ret); reply = g_dbus_create_reply(folder->msg, DBUS_TYPE_INVALID); done: g_dbus_send_message(btd_get_dbus_connection(), reply); dbus_message_unref(folder->msg); folder->msg = NULL; } void media_player_destroy(struct media_player *mp) { DBG("%s", mp->path); g_dbus_unregister_interface(btd_get_dbus_connection(), mp->path, MEDIA_PLAYER_INTERFACE); if (mp->track) g_hash_table_unref(mp->track); if (mp->settings) g_hash_table_unref(mp->settings); if (mp->scope) g_dbus_unregister_interface(btd_get_dbus_connection(), mp->path, MEDIA_FOLDER_INTERFACE); g_slist_free_full(mp->pending, g_free); g_slist_free_full(mp->folders, media_folder_destroy); g_timer_destroy(mp->progress); g_free(mp->cb); g_free(mp->status); g_free(mp->path); g_free(mp->device); g_free(mp->subtype); g_free(mp->type); g_free(mp->name); g_free(mp); } struct media_player *media_player_controller_create(const char *path, uint16_t id) { struct media_player *mp; mp = g_new0(struct media_player, 1); mp->device = g_strdup(path); mp->path = g_strdup_printf("%s/player%u", path, id); mp->settings = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, g_free); mp->track = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, g_free); mp->progress = g_timer_new(); if (!g_dbus_register_interface(btd_get_dbus_connection(), mp->path, MEDIA_PLAYER_INTERFACE, media_player_methods, media_player_signals, media_player_properties, mp, NULL)) { error("D-Bus failed to register %s path", mp->path); media_player_destroy(mp); return NULL; } DBG("%s", mp->path); return mp; } const char *media_player_get_path(struct media_player *mp) { return mp->path; } void media_player_set_duration(struct media_player *mp, uint32_t duration) { char *value, *curval; DBG("%u", duration); /* Only update duration if track exists */ if (g_hash_table_size(mp->track) == 0) return; /* Ignore if duration is already set */ curval = g_hash_table_lookup(mp->track, "Duration"); if (curval != NULL) return; value = g_strdup_printf("%u", duration); g_hash_table_replace(mp->track, g_strdup("Duration"), value); g_dbus_emit_property_changed_full(btd_get_dbus_connection(), mp->path, MEDIA_PLAYER_INTERFACE, "Track", G_DBUS_PROPERTY_CHANGED_FLAG_FLUSH); } void media_player_set_position(struct media_player *mp, uint32_t position) { DBG("%u", position); /* Only update duration if track exists */ if (g_hash_table_size(mp->track) == 0) return; mp->position = position; g_timer_start(mp->progress); g_dbus_emit_property_changed(btd_get_dbus_connection(), mp->path, MEDIA_PLAYER_INTERFACE, "Position"); } void media_player_set_setting(struct media_player *mp, const char *key, const char *value) { char *curval; struct pending_req *p; DBG("%s: %s", key, value); if (strcasecmp(key, "Error") == 0) { p = g_slist_nth_data(mp->pending, 0); if (p == NULL) return; g_dbus_pending_property_error(p->id, ERROR_INTERFACE ".Failed", value); goto send; } curval = g_hash_table_lookup(mp->settings, key); if (g_strcmp0(curval, value) == 0) goto done; g_hash_table_replace(mp->settings, g_strdup(key), g_strdup(value)); g_dbus_emit_property_changed(btd_get_dbus_connection(), mp->path, MEDIA_PLAYER_INTERFACE, key); done: p = find_pending(mp, key); if (p == NULL) return; if (strcasecmp(value, p->value) == 0) g_dbus_pending_property_success(p->id); else g_dbus_pending_property_error(p->id, ERROR_INTERFACE ".NotSupported", "Operation is not supported"); send: mp->pending = g_slist_remove(mp->pending, p); g_free(p); return; } const char *media_player_get_status(struct media_player *mp) { return mp->status; } void media_player_set_status(struct media_player *mp, const char *status) { DBG("%s", status); if (g_strcmp0(mp->status, status) == 0) return; g_free(mp->status); mp->status = g_strdup(status); g_dbus_emit_property_changed(btd_get_dbus_connection(), mp->path, MEDIA_PLAYER_INTERFACE, "Status"); mp->position = media_player_get_position(mp); g_timer_start(mp->progress); } static gboolean remove_metadata(void *key, void *value, void *user_data) { if (!strcmp(key, "Duration")) return FALSE; return strcmp(key, "Item") ? TRUE : FALSE; } void media_player_clear_metadata(struct media_player *mp) { if (!mp) return; g_hash_table_foreach_remove(mp->track, remove_metadata, NULL); } void media_player_set_metadata(struct media_player *mp, struct media_item *item, const char *key, void *data, size_t len) { char *value, *curval; GHashTable *metadata = item ? item->metadata : mp->track; value = g_strndup(data, len); DBG("%s: %s", key, value); curval = g_hash_table_lookup(metadata, key); if (g_strcmp0(curval, value) == 0) { g_free(value); return; } g_hash_table_replace(metadata, g_strdup(key), value); } void media_player_metadata_changed(struct media_player *mp) { char *item; if (!mp) return; g_dbus_emit_property_changed_full(btd_get_dbus_connection(), mp->path, MEDIA_PLAYER_INTERFACE, "Track", G_DBUS_PROPERTY_CHANGED_FLAG_FLUSH); item = g_hash_table_lookup(mp->track, "Item"); if (item == NULL) return; g_dbus_emit_property_changed_full(btd_get_dbus_connection(), item, MEDIA_ITEM_INTERFACE, "Metadata", G_DBUS_PROPERTY_CHANGED_FLAG_FLUSH); } void media_player_set_type(struct media_player *mp, const char *type) { if (g_strcmp0(mp->type, type) == 0) return; DBG("%s", type); mp->type = g_strdup(type); g_dbus_emit_property_changed(btd_get_dbus_connection(), mp->path, MEDIA_PLAYER_INTERFACE, "Type"); } void media_player_set_subtype(struct media_player *mp, const char *subtype) { if (g_strcmp0(mp->subtype, subtype) == 0) return; DBG("%s", subtype); mp->subtype = g_strdup(subtype); g_dbus_emit_property_changed(btd_get_dbus_connection(), mp->path, MEDIA_PLAYER_INTERFACE, "Subtype"); } void media_player_set_name(struct media_player *mp, const char *name) { if (g_strcmp0(mp->name, name) == 0) return; DBG("%s", name); mp->name = g_strdup(name); g_dbus_emit_property_changed(btd_get_dbus_connection(), mp->path, MEDIA_PLAYER_INTERFACE, "Name"); } void media_player_set_browsable(struct media_player *mp, bool enabled) { if (mp->browsable == enabled) return; DBG("%s", enabled ? "true" : "false"); mp->browsable = enabled; g_dbus_emit_property_changed(btd_get_dbus_connection(), mp->path, MEDIA_PLAYER_INTERFACE, "Browsable"); } bool media_player_get_browsable(struct media_player *mp) { return mp->browsable; } void media_player_set_searchable(struct media_player *mp, bool enabled) { if (mp->searchable == enabled) return; DBG("%s", enabled ? "true" : "false"); mp->searchable = enabled; g_dbus_emit_property_changed(btd_get_dbus_connection(), mp->path, MEDIA_PLAYER_INTERFACE, "Searchable"); } void media_player_set_folder(struct media_player *mp, const char *name, uint32_t number_of_items) { struct media_folder *folder; DBG("%s number of items %u", name, number_of_items); folder = media_player_find_folder(mp, name); if (folder == NULL) { error("Unknown folder: %s", name); return; } folder->number_of_items = number_of_items; media_player_set_scope(mp, folder); } void media_player_set_playlist(struct media_player *mp, const char *name) { struct media_folder *folder; DBG("%s", name); folder = media_player_find_folder(mp, name); if (folder == NULL) { error("Unknown folder: %s", name); return; } mp->playlist = folder; g_dbus_emit_property_changed(btd_get_dbus_connection(), mp->path, MEDIA_PLAYER_INTERFACE, "Playlist"); } static struct media_item *media_folder_find_item(struct media_folder *folder, uint64_t uid) { GSList *l; if (uid == 0) return NULL; for (l = folder->items; l; l = l->next) { struct media_item *item = l->data; if (item->uid == uid) return item; } return NULL; } static DBusMessage *media_item_play(DBusConnection *conn, DBusMessage *msg, void *data) { struct media_item *item = data; struct media_player *mp = item->player; struct media_folder *folder = mp->scope; struct player_callback *cb = mp->cb; const char *path; int err; if (!item->playable || !cb->cbs->play_item) return btd_error_not_supported(msg); if (folder->msg) return btd_error_failed(msg, strerror(EBUSY)); path = mp->search && folder == mp->search ? "/Search" : item->path; err = cb->cbs->play_item(mp, path, item->uid, cb->user_data); if (err < 0) return btd_error_failed(msg, strerror(-err)); folder->msg = dbus_message_ref(msg); return NULL; } static DBusMessage *media_item_add_to_nowplaying(DBusConnection *conn, DBusMessage *msg, void *data) { struct media_item *item = data; struct media_player *mp = item->player; struct player_callback *cb = mp->cb; int err; if (!item->playable || !cb->cbs->play_item) return btd_error_not_supported(msg); err = cb->cbs->add_to_nowplaying(mp, item->path, item->uid, cb->user_data); if (err < 0) return btd_error_failed(msg, strerror(-err)); return g_dbus_create_reply(msg, DBUS_TYPE_INVALID); } static gboolean get_player(const GDBusPropertyTable *property, DBusMessageIter *iter, void *data) { struct media_item *item = data; dbus_message_iter_append_basic(iter, DBUS_TYPE_OBJECT_PATH, &item->player->path); return TRUE; } static gboolean item_name_exists(const GDBusPropertyTable *property, void *data) { struct media_item *item = data; return item->name != NULL; } static gboolean get_item_name(const GDBusPropertyTable *property, DBusMessageIter *iter, void *data) { struct media_item *item = data; if (item->name == NULL) return FALSE; dbus_message_iter_append_basic(iter, DBUS_TYPE_STRING, &item->name); return TRUE; } static const char *type_to_string(uint8_t type) { switch (type) { case PLAYER_ITEM_TYPE_AUDIO: return "audio"; case PLAYER_ITEM_TYPE_VIDEO: return "video"; case PLAYER_ITEM_TYPE_FOLDER: return "folder"; } return NULL; } static gboolean get_item_type(const GDBusPropertyTable *property, DBusMessageIter *iter, void *data) { struct media_item *item = data; const char *string; string = type_to_string(item->type); dbus_message_iter_append_basic(iter, DBUS_TYPE_STRING, &string); return TRUE; } static gboolean get_playable(const GDBusPropertyTable *property, DBusMessageIter *iter, void *data) { struct media_item *item = data; dbus_bool_t value; value = item->playable; dbus_message_iter_append_basic(iter, DBUS_TYPE_BOOLEAN, &value); return TRUE; } static const char *folder_type_to_string(uint8_t type) { switch (type) { case PLAYER_FOLDER_TYPE_MIXED: return "mixed"; case PLAYER_FOLDER_TYPE_TITLES: return "titles"; case PLAYER_FOLDER_TYPE_ALBUMS: return "albums"; case PLAYER_FOLDER_TYPE_ARTISTS: return "artists"; case PLAYER_FOLDER_TYPE_GENRES: return "genres"; case PLAYER_FOLDER_TYPE_PLAYLISTS: return "playlists"; case PLAYER_FOLDER_TYPE_YEARS: return "years"; } return NULL; } static gboolean folder_type_exists(const GDBusPropertyTable *property, void *data) { struct media_item *item = data; return folder_type_to_string(item->folder_type) != NULL; } static gboolean get_folder_type(const GDBusPropertyTable *property, DBusMessageIter *iter, void *data) { struct media_item *item = data; const char *string; string = folder_type_to_string(item->folder_type); if (string == NULL) return FALSE; dbus_message_iter_append_basic(iter, DBUS_TYPE_STRING, &string); return TRUE; } static gboolean metadata_exists(const GDBusPropertyTable *property, void *data) { struct media_item *item = data; return item->metadata != NULL; } static void append_metadata(void *key, void *value, void *user_data) { DBusMessageIter *dict = user_data; const char *strkey = key; if (strcasecmp(strkey, "Item") == 0) return; if (strcasecmp(strkey, "Duration") == 0 || strcasecmp(strkey, "TrackNumber") == 0 || strcasecmp(strkey, "NumberOfTracks") == 0) { uint32_t num = atoi(value); dict_append_entry(dict, key, DBUS_TYPE_UINT32, &num); } else { dict_append_entry(dict, key, DBUS_TYPE_STRING, &value); } } static gboolean get_metadata(const GDBusPropertyTable *property, DBusMessageIter *iter, void *data) { struct media_item *item = data; DBusMessageIter dict; dbus_message_iter_open_container(iter, DBUS_TYPE_ARRAY, DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING DBUS_TYPE_STRING_AS_STRING DBUS_TYPE_VARIANT_AS_STRING DBUS_DICT_ENTRY_END_CHAR_AS_STRING, &dict); if (g_hash_table_size(item->metadata) > 0) g_hash_table_foreach(item->metadata, append_metadata, &dict); else if (item->name != NULL) dict_append_entry(&dict, "Title", DBUS_TYPE_STRING, &item->name); dbus_message_iter_close_container(iter, &dict); return TRUE; } static const GDBusMethodTable media_item_methods[] = { { GDBUS_ASYNC_METHOD("Play", NULL, NULL, media_item_play) }, { GDBUS_METHOD("AddtoNowPlaying", NULL, NULL, media_item_add_to_nowplaying) }, { } }; static const GDBusPropertyTable media_item_properties[] = { { "Player", "o", get_player, NULL, NULL }, { "Name", "s", get_item_name, NULL, item_name_exists }, { "Type", "s", get_item_type, NULL, NULL }, { "FolderType", "s", get_folder_type, NULL, folder_type_exists }, { "Playable", "b", get_playable, NULL, NULL }, { "Metadata", "a{sv}", get_metadata, NULL, metadata_exists }, { } }; void media_player_play_item_complete(struct media_player *mp, int err) { struct media_folder *folder = mp->scope; DBusMessage *reply; if (folder == NULL || folder->msg == NULL) return; if (err < 0) { reply = btd_error_failed(folder->msg, strerror(-err)); goto done; } reply = g_dbus_create_reply(folder->msg, DBUS_TYPE_INVALID); done: g_dbus_send_message(btd_get_dbus_connection(), reply); dbus_message_unref(folder->msg); folder->msg = NULL; } void media_item_set_playable(struct media_item *item, bool value) { if (item->playable == value) return; item->playable = value; g_dbus_emit_property_changed(btd_get_dbus_connection(), item->path, MEDIA_ITEM_INTERFACE, "Playable"); } static struct media_item *media_folder_create_item(struct media_player *mp, struct media_folder *folder, const char *name, player_item_type_t type, uint64_t uid) { struct media_item *item; const char *strtype; item = media_folder_find_item(folder, uid); if (item != NULL) return item; strtype = type_to_string(type); if (strtype == NULL) return NULL; DBG("%s type %s uid %" PRIu64 "", name, strtype, uid); item = g_new0(struct media_item, 1); item->player = mp; item->uid = uid; if (!uid && name[0] == '/') item->path = g_strdup_printf("%s%s", mp->path, name); else item->path = g_strdup_printf("%s/item%" PRIu64 "", folder->item->path, uid); item->name = g_strdup(name); item->type = type; item->folder_type = PLAYER_FOLDER_TYPE_INVALID; if (!g_dbus_register_interface(btd_get_dbus_connection(), item->path, MEDIA_ITEM_INTERFACE, media_item_methods, NULL, media_item_properties, item, NULL)) { error("D-Bus failed to register %s on %s path", MEDIA_ITEM_INTERFACE, item->path); media_item_free(item); return NULL; } if (type != PLAYER_ITEM_TYPE_FOLDER) { folder->items = g_slist_prepend(folder->items, item); item->metadata = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, g_free); } DBG("%s", item->path); return item; } struct media_item *media_player_create_item(struct media_player *mp, const char *name, player_item_type_t type, uint64_t uid) { return media_folder_create_item(mp, mp->scope, name, type, uid); } struct media_item *media_player_create_folder(struct media_player *mp, const char *name, player_folder_type_t type, uint64_t uid) { struct media_folder *folder; struct media_item *item; if (uid > 0) folder = media_player_find_folder_by_uid(mp, uid); else folder = media_player_find_folder(mp, name); if (folder != NULL) return folder->item; if (uid > 0) item = media_player_create_subfolder(mp, name, uid); else item = media_player_create_item(mp, name, PLAYER_ITEM_TYPE_FOLDER, uid); if (item == NULL) return NULL; folder = g_new0(struct media_folder, 1); folder->item = item; item->folder_type = type; if (mp->folder != NULL) goto done; mp->folder = folder; done: if (uid > 0) { folder->parent = mp->folder; mp->folder->subfolders = g_slist_prepend( mp->folder->subfolders, folder); } else mp->folders = g_slist_prepend(mp->folders, folder); return item; } void media_player_set_callbacks(struct media_player *mp, const struct media_player_callback *cbs, void *user_data) { struct player_callback *cb; if (mp->cb) g_free(mp->cb); cb = g_new0(struct player_callback, 1); cb->cbs = cbs; cb->user_data = user_data; mp->cb = cb; } struct media_item *media_player_set_playlist_item(struct media_player *mp, uint64_t uid) { struct media_folder *folder = mp->playlist; struct media_item *item; char *path; DBG("%" PRIu64 "", uid); if (folder == NULL || uid == 0) return NULL; item = media_folder_create_item(mp, folder, NULL, PLAYER_ITEM_TYPE_AUDIO, uid); if (item == NULL) return NULL; media_item_set_playable(item, true); if (mp->track != item->metadata) { g_hash_table_unref(mp->track); mp->track = g_hash_table_ref(item->metadata); } path = g_hash_table_lookup(mp->track, "Item"); if (path && !strcmp(path, item->path)) return item; g_hash_table_replace(mp->track, g_strdup("Item"), g_strdup(item->path)); return item; } void media_player_clear_playlist(struct media_player *mp) { if (mp->playlist) { g_slist_free_full(mp->playlist->items, media_item_destroy); mp->playlist->items = NULL; } g_dbus_emit_property_changed(btd_get_dbus_connection(), mp->path, MEDIA_PLAYER_INTERFACE, "Playlist"); } void media_player_set_obex_port(struct media_player *mp, uint16_t port) { mp->obex_port = port; } bluez-5.82/profiles/audio/PaxHeaders/ccp.c0000644000000000000000000000005014621503015015500 xustar0020 atime=1743516587 20 ctime=1743591284 bluez-5.82/profiles/audio/ccp.c0000644000000000000000000001152414621503015015164 0ustar00rootroot// SPDX-License-Identifier: GPL-2.0-or-later /* * * BlueZ - Bluetooth protocol stack for Linux * * Copyright (C) 2024 Intel Corporation. All rights reserved. * * */ #ifdef HAVE_CONFIG_H #include #endif #define _GNU_SOURCE #include #include #include #include #include #include #include #include #include #include #include "gdbus/gdbus.h" #include "lib/bluetooth.h" #include "lib/hci.h" #include "lib/sdp.h" #include "lib/uuid.h" #include "src/dbus-common.h" #include "src/shared/util.h" #include "src/shared/att.h" #include "src/shared/queue.h" #include "src/shared/gatt-db.h" #include "src/shared/gatt-client.h" #include "src/shared/gatt-server.h" #include "src/shared/ccp.h" #include "btio/btio.h" #include "src/plugin.h" #include "src/adapter.h" #include "src/gatt-database.h" #include "src/device.h" #include "src/profile.h" #include "src/service.h" #include "src/log.h" #include "src/error.h" #define GTBS_UUID_STR "0000184C-0000-1000-8000-00805f9b34fb" struct ccp_data { struct btd_device *device; struct btd_service *service; struct bt_ccp *ccp; unsigned int state_id; }; static void ccp_debug(const char *str, void *user_data) { DBG_IDX(0xffff, "%s", str); } static struct ccp_data *ccp_data_new(struct btd_device *device) { struct ccp_data *data; data = new0(struct ccp_data, 1); data->device = device; return data; } static int ccp_probe(struct btd_service *service) { struct btd_device *device = btd_service_get_device(service); struct btd_adapter *adapter = device_get_adapter(device); struct btd_gatt_database *database = btd_adapter_get_database(adapter); struct ccp_data *data = btd_service_get_user_data(service); char addr[18]; ba2str(device_get_address(device), addr); DBG("%s", addr); if (data) { error("Profile probed twice for the same device!"); return -EINVAL; } data = ccp_data_new(device); data->service = service; data->ccp = bt_ccp_new(btd_gatt_database_get_db(database), btd_device_get_gatt_db(device)); bt_ccp_set_debug(data->ccp, ccp_debug, NULL, NULL); btd_service_set_user_data(service, data); return 0; } static void ccp_data_free(struct ccp_data *data) { if (data->service) { btd_service_set_user_data(data->service, NULL); bt_ccp_set_user_data(data->ccp, NULL); } bt_ccp_unref(data->ccp); free(data); } static void ccp_data_remove(struct ccp_data *data) { DBG("data %p", data); ccp_data_free(data); } static void ccp_remove(struct btd_service *service) { struct btd_device *device = btd_service_get_device(service); struct ccp_data *data; char addr[18]; ba2str(device_get_address(device), addr); DBG("%s", addr); data = btd_service_get_user_data(service); if (!data) { error("CCP service not handled by profile"); return; } ccp_data_remove(data); } static int ccp_accept(struct btd_service *service) { struct btd_device *device = btd_service_get_device(service); struct bt_gatt_client *client = btd_device_get_gatt_client(device); struct ccp_data *data = btd_service_get_user_data(service); char addr[18]; ba2str(device_get_address(device), addr); DBG("%s", addr); if (!bt_ccp_attach(data->ccp, client)) { error("CCP unable to attach"); return -EINVAL; } /* TODO: register telephony operations here */ btd_service_connecting_complete(service, 0); return 0; } static int ccp_connect(struct btd_service *service) { struct btd_device *device = btd_service_get_device(service); char addr[18]; ba2str(device_get_address(device), addr); DBG("%s", addr); return 0; } static int ccp_disconnect(struct btd_service *service) { struct btd_device *device = btd_service_get_device(service); struct ccp_data *data = btd_service_get_user_data(service); char addr[18]; ba2str(device_get_address(device), addr); DBG("%s", addr); bt_ccp_detach(data->ccp); btd_service_disconnecting_complete(service, 0); return 0; } static int ccp_server_probe(struct btd_profile *p, struct btd_adapter *adapter) { struct btd_gatt_database *database = btd_adapter_get_database(adapter); bt_ccp_register(btd_gatt_database_get_db(database)); return 0; } static void ccp_server_remove(struct btd_profile *p, struct btd_adapter *adapter) { DBG("CCP remove adapter"); } static struct btd_profile ccp_profile = { .name = "ccp", .priority = BTD_PROFILE_PRIORITY_MEDIUM, .remote_uuid = GTBS_UUID_STR, .device_probe = ccp_probe, .device_remove = ccp_remove, .accept = ccp_accept, .connect = ccp_connect, .disconnect = ccp_disconnect, .adapter_probe = ccp_server_probe, .adapter_remove = ccp_server_remove, .testing = true, }; static int ccp_init(void) { return btd_profile_register(&ccp_profile); } static void ccp_exit(void) { btd_profile_unregister(&ccp_profile); } BLUETOOTH_PLUGIN_DEFINE(ccp, VERSION, BLUETOOTH_PLUGIN_PRIORITY_DEFAULT, ccp_init, ccp_exit) bluez-5.82/profiles/audio/PaxHeaders/sink.c0000644000000000000000000000005014766002272015710 xustar0020 atime=1743515578 20 ctime=1743591284 bluez-5.82/profiles/audio/sink.c0000644000000000000000000002205214766002272015372 0ustar00rootroot// SPDX-License-Identifier: GPL-2.0-or-later /* * * BlueZ - Bluetooth protocol stack for Linux * * Copyright (C) 2006-2010 Nokia Corporation * Copyright (C) 2004-2010 Marcel Holtmann * * */ #ifdef HAVE_CONFIG_H #include #endif #include #include #include #include #include #include "lib/bluetooth.h" #include "lib/sdp.h" #include "gdbus/gdbus.h" #include "src/log.h" #include "src/adapter.h" #include "src/device.h" #include "src/service.h" #include "src/error.h" #include "src/dbus-common.h" #include "src/shared/queue.h" #include "avdtp.h" #include "media.h" #include "a2dp.h" #include "sink.h" #define STREAM_SETUP_RETRY_TIMER 2 struct sink { struct btd_service *service; struct avdtp *session; struct avdtp_stream *stream; unsigned int cb_id; avdtp_session_state_t session_state; avdtp_state_t stream_state; sink_state_t state; unsigned int connect_id; unsigned int disconnect_id; unsigned int avdtp_callback_id; }; struct sink_state_callback { sink_state_cb cb; struct btd_service *service; void *user_data; unsigned int id; }; static GSList *sink_callbacks = NULL; static const char *str_state[] = { "SINK_STATE_DISCONNECTED", "SINK_STATE_CONNECTING", "SINK_STATE_CONNECTED", "SINK_STATE_PLAYING", }; static void sink_set_state(struct sink *sink, sink_state_t new_state) { struct btd_service *service = sink->service; struct btd_device *dev = btd_service_get_device(service); sink_state_t old_state = sink->state; GSList *l; sink->state = new_state; DBG("State changed %s: %s -> %s", device_get_path(dev), str_state[old_state], str_state[new_state]); for (l = sink_callbacks; l != NULL; l = l->next) { struct sink_state_callback *cb = l->data; if (cb->service != service) continue; cb->cb(service, old_state, new_state, cb->user_data); } if (new_state != SINK_STATE_DISCONNECTED) return; if (sink->session) { avdtp_unref(sink->session); sink->session = NULL; } } static void avdtp_state_callback(struct btd_device *dev, struct avdtp *session, avdtp_session_state_t old_state, avdtp_session_state_t new_state, void *user_data) { struct sink *sink = user_data; switch (new_state) { case AVDTP_SESSION_STATE_DISCONNECTED: sink_set_state(sink, SINK_STATE_DISCONNECTED); btd_service_disconnecting_complete(sink->service, 0); break; case AVDTP_SESSION_STATE_CONNECTING: sink_set_state(sink, SINK_STATE_CONNECTING); break; case AVDTP_SESSION_STATE_CONNECTED: break; } sink->session_state = new_state; } static void stream_state_changed(struct avdtp_stream *stream, avdtp_state_t old_state, avdtp_state_t new_state, struct avdtp_error *err, void *user_data) { struct btd_service *service = user_data; struct sink *sink = btd_service_get_user_data(service); if (err) return; switch (new_state) { case AVDTP_STATE_IDLE: if (sink->connect_id > 0) { a2dp_cancel(sink->connect_id); sink->connect_id = 0; } if (sink->disconnect_id > 0) { a2dp_cancel(sink->disconnect_id); sink->disconnect_id = 0; } if (sink->session) { avdtp_unref(sink->session); sink->session = NULL; } sink->stream = NULL; sink->cb_id = 0; break; case AVDTP_STATE_OPEN: btd_service_connecting_complete(sink->service, 0); sink_set_state(sink, SINK_STATE_CONNECTED); break; case AVDTP_STATE_STREAMING: sink_set_state(sink, SINK_STATE_PLAYING); break; case AVDTP_STATE_CONFIGURED: case AVDTP_STATE_CLOSING: case AVDTP_STATE_ABORTING: default: break; } sink->stream_state = new_state; } static void stream_setup_complete(struct avdtp *session, struct a2dp_sep *sep, struct avdtp_stream *stream, int err, void *user_data) { struct sink *sink = user_data; sink->connect_id = 0; if (stream) return; avdtp_unref(sink->session); sink->session = NULL; btd_service_connecting_complete(sink->service, err); } static void select_complete(struct avdtp *session, struct a2dp_sep *sep, GSList *caps, int err, void *user_data) { struct sink *sink = user_data; int id; sink->connect_id = 0; if (err) goto failed; id = a2dp_config(session, sep, stream_setup_complete, caps, sink); if (id == 0) goto failed; sink->connect_id = id; return; failed: btd_service_connecting_complete(sink->service, -EIO); avdtp_unref(sink->session); sink->session = NULL; } static void discovery_complete(struct avdtp *session, GSList *seps, int err, void *user_data) { struct sink *sink = user_data; int id; sink->connect_id = 0; if (err) { avdtp_unref(sink->session); sink->session = NULL; goto failed; } DBG("Discovery complete"); id = a2dp_select_capabilities(sink->session, AVDTP_SEP_TYPE_SINK, NULL, select_complete, sink); if (id == 0) { err = -EIO; goto failed; } sink->connect_id = id; return; failed: btd_service_connecting_complete(sink->service, err); avdtp_unref(sink->session); sink->session = NULL; } gboolean sink_setup_stream(struct btd_service *service, struct avdtp *session) { struct sink *sink = btd_service_get_user_data(service); if (sink->connect_id > 0 || sink->disconnect_id > 0) return FALSE; if (!sink->session) { if (session) sink->session = avdtp_ref(session); else sink->session = a2dp_avdtp_get( btd_service_get_device(service)); if (!sink->session) { DBG("Unable to get a session"); return FALSE; } } sink->connect_id = a2dp_discover(sink->session, discovery_complete, sink); if (sink->connect_id == 0) { avdtp_unref(sink->session); sink->session = NULL; return FALSE; } return TRUE; } int sink_connect(struct btd_service *service) { struct sink *sink = btd_service_get_user_data(service); if (sink->connect_id > 0 || sink->disconnect_id > 0) return -EBUSY; if (sink->state == SINK_STATE_CONNECTING) return -EBUSY; if (sink->stream_state >= AVDTP_STATE_OPEN) return -EALREADY; if (!sink_setup_stream(service, NULL)) { DBG("Failed to create a stream"); return -EIO; } DBG("stream creation in progress"); return 0; } static void sink_free(struct btd_service *service) { struct sink *sink = btd_service_get_user_data(service); if (sink->cb_id) avdtp_stream_remove_cb(sink->session, sink->stream, sink->cb_id); if (sink->session) { avdtp_unref(sink->session); sink->session = NULL; } if (sink->connect_id > 0) { btd_service_connecting_complete(sink->service, -ECANCELED); a2dp_cancel(sink->connect_id); sink->connect_id = 0; } if (sink->disconnect_id > 0) { btd_service_disconnecting_complete(sink->service, -ECANCELED); a2dp_cancel(sink->disconnect_id); sink->disconnect_id = 0; } avdtp_remove_state_cb(sink->avdtp_callback_id); btd_service_unref(sink->service); g_free(sink); } void sink_unregister(struct btd_service *service) { struct btd_device *dev = btd_service_get_device(service); DBG("%s", device_get_path(dev)); sink_free(service); } int sink_init(struct btd_service *service) { struct btd_device *dev = btd_service_get_device(service); struct sink *sink; DBG("%s", device_get_path(dev)); sink = g_new0(struct sink, 1); sink->service = btd_service_ref(service); sink->avdtp_callback_id = avdtp_add_state_cb(dev, avdtp_state_callback, sink); btd_service_set_user_data(service, sink); return 0; } gboolean sink_is_active(struct btd_service *service) { struct sink *sink = btd_service_get_user_data(service); if (sink->session) return TRUE; return FALSE; } gboolean sink_new_stream(struct btd_service *service, struct avdtp *session, struct avdtp_stream *stream) { struct sink *sink = btd_service_get_user_data(service); if (sink->stream) return FALSE; if (!sink->session) sink->session = avdtp_ref(session); sink->stream = stream; sink->cb_id = avdtp_stream_add_cb(session, stream, stream_state_changed, service); return TRUE; } int sink_disconnect(struct btd_service *service) { struct sink *sink = btd_service_get_user_data(service); if (!sink->session) return -ENOTCONN; /* cancel pending connect */ if (sink->connect_id > 0) { avdtp_unref(sink->session); sink->session = NULL; a2dp_cancel(sink->connect_id); sink->connect_id = 0; btd_service_disconnecting_complete(sink->service, 0); return 0; } /* disconnect already ongoing */ if (sink->disconnect_id > 0) return -EBUSY; if (!sink->stream) return -ENOTCONN; return avdtp_close(sink->session, sink->stream, FALSE); } unsigned int sink_add_state_cb(struct btd_service *service, sink_state_cb cb, void *user_data) { struct sink_state_callback *state_cb; static unsigned int id = 0; state_cb = g_new(struct sink_state_callback, 1); state_cb->cb = cb; state_cb->service = service; state_cb->user_data = user_data; state_cb->id = ++id; sink_callbacks = g_slist_append(sink_callbacks, state_cb); return state_cb->id; } gboolean sink_remove_state_cb(unsigned int id) { GSList *l; for (l = sink_callbacks; l != NULL; l = l->next) { struct sink_state_callback *cb = l->data; if (cb && cb->id == id) { sink_callbacks = g_slist_remove(sink_callbacks, cb); g_free(cb); return TRUE; } } return FALSE; } bluez-5.82/profiles/audio/PaxHeaders/avctp.h0000644000000000000000000000005014131623652016063 xustar0020 atime=1743516536 20 ctime=1743591284 bluez-5.82/profiles/audio/avctp.h0000644000000000000000000001275114131623652015552 0ustar00rootroot/* SPDX-License-Identifier: GPL-2.0-or-later */ /* * * BlueZ - Bluetooth protocol stack for Linux * * Copyright (C) 2006-2010 Nokia Corporation * Copyright (C) 2004-2010 Marcel Holtmann * * */ #define AVCTP_CONTROL_PSM 23 #define AVCTP_BROWSING_PSM 27 #define AVC_MTU 512 #define AVC_HEADER_LENGTH 3 /* ctype entries */ #define AVC_CTYPE_CONTROL 0x0 #define AVC_CTYPE_STATUS 0x1 #define AVC_CTYPE_NOTIFY 0x3 #define AVC_CTYPE_NOT_IMPLEMENTED 0x8 #define AVC_CTYPE_ACCEPTED 0x9 #define AVC_CTYPE_REJECTED 0xA #define AVC_CTYPE_STABLE 0xC #define AVC_CTYPE_CHANGED 0xD #define AVC_CTYPE_INTERIM 0xF /* opcodes */ #define AVC_OP_VENDORDEP 0x00 #define AVC_OP_UNITINFO 0x30 #define AVC_OP_SUBUNITINFO 0x31 #define AVC_OP_PASSTHROUGH 0x7c /* subunits of interest */ #define AVC_SUBUNIT_PANEL 0x09 /* operands in passthrough commands */ #define AVC_SELECT 0x00 #define AVC_UP 0x01 #define AVC_DOWN 0x02 #define AVC_LEFT 0x03 #define AVC_RIGHT 0x04 #define AVC_RIGHT_UP 0x05 #define AVC_RIGHT_DOWN 0x06 #define AVC_LEFT_UP 0x07 #define AVC_LEFT_DOWN 0x08 #define AVC_ROOT_MENU 0x09 #define AVC_SETUP_MENU 0x0a #define AVC_CONTENTS_MENU 0x0b #define AVC_FAVORITE_MENU 0x0c #define AVC_EXIT 0x0d #define AVC_ON_DEMAND_MENU 0x0e #define AVC_APPS_MENU 0x0f #define AVC_0 0x20 #define AVC_1 0x21 #define AVC_2 0x22 #define AVC_3 0x23 #define AVC_4 0x24 #define AVC_5 0x25 #define AVC_6 0x26 #define AVC_7 0x27 #define AVC_8 0x28 #define AVC_9 0x29 #define AVC_DOT 0x2a #define AVC_ENTER 0x2b #define AVC_CLEAR 0x2c #define AVC_CHANNEL_UP 0x30 #define AVC_CHANNEL_DOWN 0x31 #define AVC_CHANNEL_PREVIOUS 0x32 #define AVC_SOUND_SELECT 0x33 #define AVC_INPUT_SELECT 0x34 #define AVC_INFO 0x35 #define AVC_HELP 0x36 #define AVC_PAGE_UP 0x37 #define AVC_PAGE_DOWN 0x38 #define AVC_LOCK 0x3a #define AVC_POWER 0x40 #define AVC_VOLUME_UP 0x41 #define AVC_VOLUME_DOWN 0x42 #define AVC_MUTE 0x43 #define AVC_PLAY 0x44 #define AVC_STOP 0x45 #define AVC_PAUSE 0x46 #define AVC_RECORD 0x47 #define AVC_REWIND 0x48 #define AVC_FAST_FORWARD 0x49 #define AVC_EJECT 0x4a #define AVC_FORWARD 0x4b #define AVC_BACKWARD 0x4c #define AVC_LIST 0x4d #define AVC_ANGLE 0x50 #define AVC_SUBPICTURE 0x51 #define AVC_F1 0x71 #define AVC_F2 0x72 #define AVC_F3 0x73 #define AVC_F4 0x74 #define AVC_F5 0x75 #define AVC_F6 0x76 #define AVC_F7 0x77 #define AVC_F8 0x78 #define AVC_F9 0x79 #define AVC_RED 0x7a #define AVC_GREEN 0x7b #define AVC_BLUE 0x7c #define AVC_YELLOW 0x7c #define AVC_VENDOR_UNIQUE 0x7e #define AVC_INVALID 0xff struct avctp; typedef enum { AVCTP_STATE_DISCONNECTED = 0, AVCTP_STATE_CONNECTING, AVCTP_STATE_CONNECTED, AVCTP_STATE_BROWSING_CONNECTING, AVCTP_STATE_BROWSING_CONNECTED } avctp_state_t; typedef void (*avctp_state_cb) (struct btd_device *dev, avctp_state_t old_state, avctp_state_t new_state, int err, void *user_data); typedef bool (*avctp_passthrough_cb) (struct avctp *session, uint8_t op, bool pressed, void *user_data); typedef size_t (*avctp_control_pdu_cb) (struct avctp *session, uint8_t transaction, uint8_t *code, uint8_t *subunit, uint8_t *operands, size_t operand_count, void *user_data); typedef gboolean (*avctp_rsp_cb) (struct avctp *session, uint8_t code, uint8_t subunit, uint8_t transaction, uint8_t *operands, size_t operand_count, void *user_data); typedef gboolean (*avctp_browsing_rsp_cb) (struct avctp *session, uint8_t *operands, size_t operand_count, void *user_data); typedef size_t (*avctp_browsing_pdu_cb) (struct avctp *session, uint8_t transaction, uint8_t *operands, size_t operand_count, void *user_data); unsigned int avctp_add_state_cb(struct btd_device *dev, avctp_state_cb cb, void *user_data); gboolean avctp_remove_state_cb(unsigned int id); int avctp_register(struct btd_adapter *adapter, bool central, bool *browsing); void avctp_unregister(struct btd_adapter *adapter); struct avctp *avctp_connect(struct btd_device *device); struct avctp *avctp_get(struct btd_device *device); bool avctp_is_initiator(struct avctp *session); int avctp_connect_browsing(struct avctp *session); void avctp_disconnect(struct avctp *session); unsigned int avctp_register_passthrough_handler(struct avctp *session, avctp_passthrough_cb cb, void *user_data); bool avctp_unregister_passthrough_handler(unsigned int id); unsigned int avctp_register_pdu_handler(struct avctp *session, uint8_t opcode, avctp_control_pdu_cb cb, void *user_data); gboolean avctp_unregister_pdu_handler(unsigned int id); unsigned int avctp_register_browsing_pdu_handler(struct avctp *session, avctp_browsing_pdu_cb cb, void *user_data, GDestroyNotify destroy); gboolean avctp_unregister_browsing_pdu_handler(unsigned int id); int avctp_send_passthrough(struct avctp *session, uint8_t op, bool hold); int avctp_send_release_passthrough(struct avctp *session); int avctp_send_vendordep(struct avctp *session, uint8_t transaction, uint8_t code, uint8_t subunit, uint8_t *operands, size_t operand_count); int avctp_send_vendordep_req(struct avctp *session, uint8_t code, uint8_t subunit, uint8_t *operands, size_t operand_count, avctp_rsp_cb func, void *user_data); int avctp_send_browsing_req(struct avctp *session, uint8_t *operands, size_t operand_count, avctp_browsing_rsp_cb func, void *user_data); bool avctp_supports_avc(uint8_t avc); bluez-5.82/profiles/audio/PaxHeaders/avdtp.c0000644000000000000000000000005014772767672016105 xustar0020 atime=1743515579 20 ctime=1743591284 bluez-5.82/profiles/audio/avdtp.c0000644000000000000000000026610514772767672015600 0ustar00rootroot// SPDX-License-Identifier: GPL-2.0-or-later /* * * BlueZ - Bluetooth protocol stack for Linux * * Copyright (C) 2006-2010 Nokia Corporation * Copyright (C) 2004-2010 Marcel Holtmann * * */ #ifdef HAVE_CONFIG_H #include #endif #include #include #include #include #include #include #include #include #include "lib/bluetooth.h" #include "lib/sdp.h" #include "lib/sdp_lib.h" #include "lib/uuid.h" #include "btio/btio.h" #include "src/btd.h" #include "src/log.h" #include "src/shared/timeout.h" #include "src/shared/util.h" #include "src/shared/queue.h" #include "src/adapter.h" #include "src/device.h" #include "avdtp.h" #include "sink.h" #include "source.h" #define AVDTP_PSM 25 #define MAX_SEID 0x3E #ifndef MAX # define MAX(x, y) ((x) > (y) ? (x) : (y)) #endif #define AVDTP_DISCOVER 0x01 #define AVDTP_GET_CAPABILITIES 0x02 #define AVDTP_SET_CONFIGURATION 0x03 #define AVDTP_GET_CONFIGURATION 0x04 #define AVDTP_RECONFIGURE 0x05 #define AVDTP_OPEN 0x06 #define AVDTP_START 0x07 #define AVDTP_CLOSE 0x08 #define AVDTP_SUSPEND 0x09 #define AVDTP_ABORT 0x0A #define AVDTP_SECURITY_CONTROL 0x0B #define AVDTP_GET_ALL_CAPABILITIES 0x0C #define AVDTP_DELAY_REPORT 0x0D #define AVDTP_PKT_TYPE_SINGLE 0x00 #define AVDTP_PKT_TYPE_START 0x01 #define AVDTP_PKT_TYPE_CONTINUE 0x02 #define AVDTP_PKT_TYPE_END 0x03 #define AVDTP_MSG_TYPE_COMMAND 0x00 #define AVDTP_MSG_TYPE_GEN_REJECT 0x01 #define AVDTP_MSG_TYPE_ACCEPT 0x02 #define AVDTP_MSG_TYPE_REJECT 0x03 #define REQ_TIMEOUT 6 #define SUSPEND_TIMEOUT 10 #define ABORT_TIMEOUT 2 #define DISCONNECT_TIMEOUT 1 #define START_TIMEOUT 1 #if __BYTE_ORDER == __LITTLE_ENDIAN struct avdtp_common_header { uint8_t message_type:2; uint8_t packet_type:2; uint8_t transaction:4; } __attribute__ ((packed)); struct avdtp_single_header { uint8_t message_type:2; uint8_t packet_type:2; uint8_t transaction:4; uint8_t signal_id:6; uint8_t rfa0:2; } __attribute__ ((packed)); struct avdtp_start_header { uint8_t message_type:2; uint8_t packet_type:2; uint8_t transaction:4; uint8_t no_of_packets; uint8_t signal_id:6; uint8_t rfa0:2; } __attribute__ ((packed)); struct avdtp_continue_header { uint8_t message_type:2; uint8_t packet_type:2; uint8_t transaction:4; } __attribute__ ((packed)); struct seid_info { uint8_t rfa0:1; uint8_t inuse:1; uint8_t seid:6; uint8_t rfa2:3; uint8_t type:1; uint8_t media_type:4; } __attribute__ ((packed)); struct seid { uint8_t rfa0:2; uint8_t seid:6; } __attribute__ ((packed)); #elif __BYTE_ORDER == __BIG_ENDIAN struct avdtp_common_header { uint8_t transaction:4; uint8_t packet_type:2; uint8_t message_type:2; } __attribute__ ((packed)); struct avdtp_single_header { uint8_t transaction:4; uint8_t packet_type:2; uint8_t message_type:2; uint8_t rfa0:2; uint8_t signal_id:6; } __attribute__ ((packed)); struct avdtp_start_header { uint8_t transaction:4; uint8_t packet_type:2; uint8_t message_type:2; uint8_t no_of_packets; uint8_t rfa0:2; uint8_t signal_id:6; } __attribute__ ((packed)); struct avdtp_continue_header { uint8_t transaction:4; uint8_t packet_type:2; uint8_t message_type:2; } __attribute__ ((packed)); struct seid_info { uint8_t seid:6; uint8_t inuse:1; uint8_t rfa0:1; uint8_t media_type:4; uint8_t type:1; uint8_t rfa2:3; } __attribute__ ((packed)); struct seid { uint8_t seid:6; uint8_t rfa0:2; } __attribute__ ((packed)); #else #error "Unknown byte order" #endif /* packets */ struct discover_resp { struct seid_info seps[0]; } __attribute__ ((packed)); struct getcap_resp { uint8_t caps[0]; } __attribute__ ((packed)); struct start_req { union { struct seid required[1]; struct seid seids[0]; }; } __attribute__ ((packed)); struct suspend_req { union { struct seid required[1]; struct seid seids[0]; }; } __attribute__ ((packed)); struct seid_rej { uint8_t error; } __attribute__ ((packed)); struct conf_rej { uint8_t category; uint8_t error; } __attribute__ ((packed)); #if __BYTE_ORDER == __LITTLE_ENDIAN struct seid_req { uint8_t rfa0:2; uint8_t acp_seid:6; } __attribute__ ((packed)); struct setconf_req { uint8_t rfa0:2; uint8_t acp_seid:6; uint8_t rfa1:2; uint8_t int_seid:6; uint8_t caps[0]; } __attribute__ ((packed)); struct stream_rej { uint8_t rfa0:2; uint8_t acp_seid:6; uint8_t error; } __attribute__ ((packed)); struct reconf_req { uint8_t rfa0:2; uint8_t acp_seid:6; uint8_t serv_cap; uint8_t serv_cap_len; uint8_t caps[0]; } __attribute__ ((packed)); struct delay_req { uint8_t rfa0:2; uint8_t acp_seid:6; uint16_t delay; } __attribute__ ((packed)); #elif __BYTE_ORDER == __BIG_ENDIAN struct seid_req { uint8_t acp_seid:6; uint8_t rfa0:2; } __attribute__ ((packed)); struct setconf_req { uint8_t acp_seid:6; uint8_t rfa0:2; uint8_t int_seid:6; uint8_t rfa1:2; uint8_t caps[0]; } __attribute__ ((packed)); struct stream_rej { uint8_t acp_seid:6; uint8_t rfa0:2; uint8_t error; } __attribute__ ((packed)); struct reconf_req { uint8_t acp_seid:6; uint8_t rfa0:2; uint8_t serv_cap; uint8_t serv_cap_len; uint8_t caps[0]; } __attribute__ ((packed)); struct delay_req { uint8_t acp_seid:6; uint8_t rfa0:2; uint16_t delay; } __attribute__ ((packed)); #else #error "Unknown byte order" #endif struct in_buf { gboolean active; int no_of_packets; uint8_t transaction; uint8_t signal_id; uint8_t buf[1024]; uint8_t data_size; }; struct pending_req { uint8_t transaction; uint8_t signal_id; void *data; size_t data_size; struct avdtp_stream *stream; /* Set if the request targeted a stream */ unsigned int timeout; gboolean collided; }; struct avdtp_remote_sep { uint8_t seid; uint8_t type; uint8_t media_type; struct avdtp_service_capability *codec; gboolean delay_reporting; bool discovered; GSList *caps; /* of type struct avdtp_service_capability */ struct avdtp_stream *stream; avdtp_remote_sep_destroy_t destroy; void *user_data; }; struct avdtp_local_sep { avdtp_state_t state; struct avdtp_stream *stream; struct seid_info info; uint8_t codec; gboolean delay_reporting; GSList *caps; struct avdtp_sep_ind *ind; struct avdtp_sep_cfm *cfm; void *user_data; }; struct stream_callback { avdtp_stream_state_cb cb; void *user_data; unsigned int id; }; struct avdtp_state_callback { avdtp_session_state_cb cb; struct btd_device *dev; unsigned int id; void *user_data; }; struct discover_callback { unsigned int id; avdtp_discover_cb_t cb; void *user_data; }; struct avdtp_stream { GIOChannel *io; uint16_t imtu; uint16_t omtu; struct avdtp *session; struct avdtp_local_sep *lsep; uint8_t rseid; GSList *caps; GSList *callbacks; struct avdtp_service_capability *codec; guint io_id; /* Transport GSource ID */ unsigned int timer; /* Waiting for other side to close or open * the transport channel */ gboolean open_acp; /* If we are in ACT role for Open */ gboolean close_int; /* If we are in INT role for Close */ gboolean abort_int; /* If we are in INT role for Abort */ unsigned int start_timer; /* Wait START command timer */ gboolean delay_reporting; uint16_t delay; /* AVDTP 1.3 Delay Reporting feature */ gboolean starting; /* only valid while sep state == OPEN */ }; /* Structure describing an AVDTP connection between two devices */ struct avdtp { unsigned int ref; uint16_t version; struct queue *lseps; struct btd_device *device; avdtp_session_state_t state; GIOChannel *io; guint io_id; GSList *seps; /* Elements of type struct avdtp_remote_sep * */ GSList *streams; /* Elements of type struct avdtp_stream * */ GSList *req_queue; /* Elements of type struct pending_req * */ GSList *prio_queue; /* Same as req_queue but is processed before it */ struct avdtp_stream *pending_open; GIOChannel *pending_open_io; uint32_t phy; uint16_t imtu; uint16_t omtu; struct in_buf in_resp; struct in_buf in_cmd; char *buf; struct discover_callback *discover; struct pending_req *req; unsigned int dc_timer; int dc_timeout; /* Attempt stream setup instead of disconnecting */ gboolean stream_setup; }; static GSList *state_callbacks = NULL; static int send_request(struct avdtp *session, gboolean priority, struct avdtp_stream *stream, uint8_t signal_id, void *buffer, size_t size); static gboolean avdtp_parse_resp(struct avdtp *session, struct avdtp_stream *stream, uint8_t transaction, uint8_t signal_id, void *buf, int size); static gboolean avdtp_parse_rej(struct avdtp *session, struct avdtp_stream *stream, uint8_t transaction, uint8_t signal_id, void *buf, int size); static int process_queue(struct avdtp *session); static void avdtp_sep_set_state(struct avdtp *session, struct avdtp_local_sep *sep, avdtp_state_t state); static const char *avdtp_statestr(avdtp_state_t state) { switch (state) { case AVDTP_STATE_IDLE: return "IDLE"; case AVDTP_STATE_CONFIGURED: return "CONFIGURED"; case AVDTP_STATE_OPEN: return "OPEN"; case AVDTP_STATE_STREAMING: return "STREAMING"; case AVDTP_STATE_CLOSING: return "CLOSING"; case AVDTP_STATE_ABORTING: return "ABORTING"; default: return ""; } } static gboolean try_send(int sk, void *data, size_t len) { int err; do { err = send(sk, data, len, 0); } while (err < 0 && errno == EINTR); if (err < 0) { error("send: %s (%d)", strerror(errno), errno); return FALSE; } else if ((size_t) err != len) { error("try_send: complete buffer not sent (%d/%zu bytes)", err, len); return FALSE; } return TRUE; } static gboolean avdtp_send(struct avdtp *session, uint8_t transaction, uint8_t message_type, uint8_t signal_id, void *data, size_t len) { unsigned int cont_fragments, sent; struct avdtp_start_header start; struct avdtp_continue_header cont; int sock; if (session->io == NULL) { error("avdtp_send: session is closed"); return FALSE; } sock = g_io_channel_unix_get_fd(session->io); /* Single packet - no fragmentation */ if (sizeof(struct avdtp_single_header) + len <= session->omtu) { struct avdtp_single_header single; memset(&single, 0, sizeof(single)); single.transaction = transaction; single.packet_type = AVDTP_PKT_TYPE_SINGLE; single.message_type = message_type; single.signal_id = signal_id; memcpy(session->buf, &single, sizeof(single)); if (data) memcpy(session->buf + sizeof(single), data, len); return try_send(sock, session->buf, sizeof(single) + len); } /* Check if there is enough space to start packet */ if (session->omtu < sizeof(start)) { error("No enough space to fragment packet"); return FALSE; } /* Count the number of needed fragments */ cont_fragments = (len - (session->omtu - sizeof(start))) / (session->omtu - sizeof(cont)) + 1; DBG("%zu bytes split into %d fragments", len, cont_fragments + 1); /* Send the start packet */ memset(&start, 0, sizeof(start)); start.transaction = transaction; start.packet_type = AVDTP_PKT_TYPE_START; start.message_type = message_type; start.no_of_packets = cont_fragments + 1; start.signal_id = signal_id; memcpy(session->buf, &start, sizeof(start)); memcpy(session->buf + sizeof(start), data, session->omtu - sizeof(start)); if (!try_send(sock, session->buf, session->omtu)) return FALSE; DBG("first packet with %zu bytes sent", session->omtu - sizeof(start)); sent = session->omtu - sizeof(start); /* Send the continue fragments and the end packet */ while (sent < len) { int left, to_copy; left = len - sent; if (left + sizeof(cont) > session->omtu) { cont.packet_type = AVDTP_PKT_TYPE_CONTINUE; to_copy = session->omtu - sizeof(cont); DBG("sending continue with %d bytes", to_copy); } else { cont.packet_type = AVDTP_PKT_TYPE_END; to_copy = left; DBG("sending end with %d bytes", to_copy); } cont.transaction = transaction; cont.message_type = message_type; memcpy(session->buf, &cont, sizeof(cont)); memcpy(session->buf + sizeof(cont), data + sent, to_copy); if (!try_send(sock, session->buf, to_copy + sizeof(cont))) return FALSE; sent += to_copy; } return TRUE; } static void pending_req_free(void *data) { struct pending_req *req = data; if (req->timeout) timeout_remove(req->timeout); free(req->data); g_free(req); } static void close_stream(struct avdtp_stream *stream) { int sock; if (stream->io == NULL) return; sock = g_io_channel_unix_get_fd(stream->io); shutdown(sock, SHUT_RDWR); g_io_channel_shutdown(stream->io, FALSE, NULL); g_io_channel_unref(stream->io); stream->io = NULL; } static bool stream_close_timeout(gpointer user_data) { struct avdtp_stream *stream = user_data; DBG("Timed out waiting for peer to close the transport channel"); stream->timer = 0; close_stream(stream); return FALSE; } static bool stream_open_timeout(gpointer user_data) { struct avdtp_stream *stream = user_data; DBG("Timed out waiting for peer to open the transport channel"); stream->timer = 0; stream->session->pending_open = NULL; if (stream->session->pending_open_io) { g_io_channel_unref(stream->session->pending_open_io); stream->session->pending_open_io = NULL; } avdtp_abort(stream->session, stream); return FALSE; } static void stream_set_timer(struct avdtp_stream *stream, guint timeout, timeout_func_t func) { if (stream->timer) timeout_remove(stream->timer); stream->timer = timeout_add_seconds(timeout, func, stream, NULL); } static void stream_set_pending_open(struct avdtp_stream *stream, GIOChannel *io) { stream->open_acp = TRUE; stream->session->pending_open = stream; stream->session->pending_open_io = io; stream_set_timer(stream, REQ_TIMEOUT, stream_open_timeout); } void avdtp_error_init(struct avdtp_error *err, uint8_t category, int id) { err->category = category; if (category == AVDTP_ERRNO) err->err.posix_errno = id; else err->err.error_code = id; } uint8_t avdtp_error_category(struct avdtp_error *err) { return err->category; } int avdtp_error_error_code(struct avdtp_error *err) { assert(err->category != AVDTP_ERRNO); return err->err.error_code; } int avdtp_error_posix_errno(struct avdtp_error *err) { assert(err->category == AVDTP_ERRNO); return err->err.posix_errno; } static struct avdtp_stream *find_stream_by_rseid(struct avdtp *session, uint8_t rseid) { GSList *l; for (l = session->streams; l != NULL; l = g_slist_next(l)) { struct avdtp_stream *stream = l->data; if (stream->rseid == rseid) return stream; } return NULL; } static struct avdtp_remote_sep *find_remote_sep(GSList *seps, uint8_t seid) { GSList *l; for (l = seps; l != NULL; l = g_slist_next(l)) { struct avdtp_remote_sep *sep = l->data; if (sep->seid == seid) return sep; } return NULL; } static void avdtp_set_state(struct avdtp *session, avdtp_session_state_t new_state) { GSList *l; avdtp_session_state_t old_state = session->state; session->state = new_state; for (l = state_callbacks; l != NULL;) { struct avdtp_state_callback *cb = l->data; l = g_slist_next(l); if (session->device != cb->dev) continue; cb->cb(cb->dev, session, old_state, new_state, cb->user_data); } } static void stream_free(void *data) { struct avdtp_stream *stream = data; struct avdtp_remote_sep *rsep; stream->lsep->info.inuse = 0; stream->lsep->stream = NULL; rsep = find_remote_sep(stream->session->seps, stream->rseid); if (rsep) rsep->stream = NULL; if (stream->timer) timeout_remove(stream->timer); if (stream->io) close_stream(stream); if (stream->io_id) g_source_remove(stream->io_id); g_slist_free_full(stream->callbacks, g_free); g_slist_free_full(stream->caps, g_free); g_free(stream); } static gboolean transport_cb(GIOChannel *chan, GIOCondition cond, gpointer data) { struct avdtp_stream *stream = data; struct avdtp_local_sep *sep = stream->lsep; if (stream->close_int && sep->cfm && sep->cfm->close) sep->cfm->close(stream->session, sep, stream, NULL, sep->user_data); if (!(cond & G_IO_NVAL)) close_stream(stream); stream->io_id = 0; if (!stream->abort_int) avdtp_sep_set_state(stream->session, sep, AVDTP_STATE_IDLE); return FALSE; } static int get_send_buffer_size(int sk) { int size; socklen_t optlen = sizeof(size); if (getsockopt(sk, SOL_SOCKET, SO_SNDBUF, &size, &optlen) < 0) { int err = -errno; error("getsockopt(SO_SNDBUF) failed: %s (%d)", strerror(-err), -err); return err; } /* * Doubled value is returned by getsockopt since kernel uses that * space for its own purposes (see man 7 socket, bookkeeping overhead * for SO_SNDBUF). */ return size / 2; } static int set_send_buffer_size(int sk, int size) { socklen_t optlen = sizeof(size); if (setsockopt(sk, SOL_SOCKET, SO_SNDBUF, &size, optlen) < 0) { int err = -errno; error("setsockopt(SO_SNDBUF) failed: %s (%d)", strerror(-err), -err); return err; } return 0; } static void handle_transport_connect(struct avdtp *session, GIOChannel *io, uint16_t imtu, uint16_t omtu) { struct avdtp_stream *stream = session->pending_open; struct avdtp_local_sep *sep = stream->lsep; int sk, buf_size, min_buf_size; GError *err = NULL; session->pending_open = NULL; if (stream->timer) { timeout_remove(stream->timer); stream->timer = 0; } if (io == NULL) { if (!stream->open_acp && sep->cfm && sep->cfm->open) { struct avdtp_error err; avdtp_error_init(&err, AVDTP_ERRNO, EIO); sep->cfm->open(session, sep, NULL, &err, sep->user_data); } return; } if (stream->io == NULL) stream->io = g_io_channel_ref(io); stream->omtu = omtu; stream->imtu = imtu; /* Apply special settings only if local SEP is of type SRC */ if (sep->info.type != AVDTP_SEP_TYPE_SOURCE) goto proceed; bt_io_set(stream->io, &err, BT_IO_OPT_FLUSHABLE, TRUE, BT_IO_OPT_INVALID); if (err != NULL) { error("Enabling flushable packets failed: %s", err->message); g_error_free(err); } else DBG("Flushable packets enabled"); sk = g_io_channel_unix_get_fd(stream->io); buf_size = get_send_buffer_size(sk); if (buf_size < 0) goto proceed; DBG("sk %d, omtu %d, send buffer size %d", sk, omtu, buf_size); min_buf_size = omtu * 2; if (buf_size < min_buf_size) { DBG("send buffer size to be increassed to %d", min_buf_size); set_send_buffer_size(sk, min_buf_size); } proceed: if (!stream->open_acp && sep->cfm && sep->cfm->open) sep->cfm->open(session, sep, stream, NULL, sep->user_data); avdtp_sep_set_state(session, sep, AVDTP_STATE_OPEN); stream->io_id = g_io_add_watch(io, G_IO_ERR | G_IO_HUP | G_IO_NVAL, (GIOFunc) transport_cb, stream); /* Release pending IO */ if (session->pending_open_io) { g_io_channel_unref(session->pending_open_io); session->pending_open_io = NULL; } } static int pending_req_cmp(gconstpointer a, gconstpointer b) { const struct pending_req *req = a; const struct avdtp_stream *stream = b; if (req->stream == stream) return 0; return -1; } static void cleanup_queue(struct avdtp *session, struct avdtp_stream *stream) { GSList *l; struct pending_req *req; while ((l = g_slist_find_custom(session->prio_queue, stream, pending_req_cmp))) { req = l->data; pending_req_free(req); session->prio_queue = g_slist_remove(session->prio_queue, req); } while ((l = g_slist_find_custom(session->req_queue, stream, pending_req_cmp))) { req = l->data; pending_req_free(req); session->req_queue = g_slist_remove(session->req_queue, req); } } static void handle_unanswered_req(struct avdtp *session, struct avdtp_stream *stream) { struct pending_req *req; struct avdtp_local_sep *lsep; struct avdtp_error err; if (session->req->signal_id == AVDTP_ABORT) { /* Avoid freeing the Abort request here */ DBG("handle_unanswered_req: Abort req, returning"); session->req->stream = NULL; return; } req = session->req; session->req = NULL; avdtp_error_init(&err, AVDTP_ERRNO, EIO); lsep = stream->lsep; switch (req->signal_id) { case AVDTP_RECONFIGURE: error("No reply to Reconfigure request"); if (lsep && lsep->cfm && lsep->cfm->reconfigure) lsep->cfm->reconfigure(session, lsep, stream, &err, lsep->user_data); break; case AVDTP_OPEN: error("No reply to Open request"); if (lsep && lsep->cfm && lsep->cfm->open) lsep->cfm->open(session, lsep, stream, &err, lsep->user_data); break; case AVDTP_START: error("No reply to Start request"); if (lsep && lsep->cfm && lsep->cfm->start) lsep->cfm->start(session, lsep, stream, &err, lsep->user_data); break; case AVDTP_SUSPEND: error("No reply to Suspend request"); if (lsep && lsep->cfm && lsep->cfm->suspend) lsep->cfm->suspend(session, lsep, stream, &err, lsep->user_data); break; case AVDTP_CLOSE: error("No reply to Close request"); if (lsep && lsep->cfm && lsep->cfm->close) lsep->cfm->close(session, lsep, stream, &err, lsep->user_data); break; case AVDTP_SET_CONFIGURATION: error("No reply to SetConfiguration request"); if (lsep && lsep->cfm && lsep->cfm->set_configuration) lsep->cfm->set_configuration(session, lsep, stream, &err, lsep->user_data); } pending_req_free(req); } static void avdtp_sep_set_state(struct avdtp *session, struct avdtp_local_sep *sep, avdtp_state_t state) { struct avdtp_stream *stream = sep->stream; avdtp_state_t old_state; struct avdtp_error err, *err_ptr = NULL; GSList *l; if (!stream) { error("Error changing sep state: stream not available"); return; } if (sep->state == state) { avdtp_error_init(&err, AVDTP_ERRNO, EIO); DBG("stream state change failed: %s", avdtp_strerror(&err)); err_ptr = &err; } else { err_ptr = NULL; DBG("stream state changed: %s -> %s", avdtp_statestr(sep->state), avdtp_statestr(state)); } old_state = sep->state; sep->state = state; switch (state) { case AVDTP_STATE_CONFIGURED: if (sep->info.type == AVDTP_SEP_TYPE_SINK) avdtp_delay_report(session, stream, stream->delay); break; case AVDTP_STATE_OPEN: stream->starting = FALSE; break; case AVDTP_STATE_STREAMING: if (stream->start_timer) { timeout_remove(stream->start_timer); stream->start_timer = 0; } stream->open_acp = FALSE; break; case AVDTP_STATE_CLOSING: case AVDTP_STATE_ABORTING: if (stream->start_timer) { timeout_remove(stream->start_timer); stream->start_timer = 0; } break; case AVDTP_STATE_IDLE: if (stream->start_timer) { timeout_remove(stream->start_timer); stream->start_timer = 0; } if (session->pending_open == stream) handle_transport_connect(session, NULL, 0, 0); if (session->req && session->req->stream == stream) handle_unanswered_req(session, stream); /* Remove pending commands for this stream from the queue */ cleanup_queue(session, stream); session->streams = g_slist_remove(session->streams, stream); break; default: break; } l = stream->callbacks; while (l != NULL) { struct stream_callback *cb = l->data; l = g_slist_next(l); cb->cb(stream, old_state, state, err_ptr, cb->user_data); } if (state == AVDTP_STATE_IDLE) stream_free(stream); } static void sep_free(gpointer data) { struct avdtp_remote_sep *sep = data; if (sep->destroy) sep->destroy(sep->user_data); g_slist_free_full(sep->caps, g_free); g_free(sep); } static void remove_disappeared(void *data, void *user_data) { struct avdtp_remote_sep *sep = data; struct avdtp *session = user_data; if (sep->discovered) return; DBG("seid %d disappeared", sep->seid); session->seps = g_slist_remove(session->seps, sep); sep_free(sep); } static void finalize_discovery(struct avdtp *session, int err) { struct discover_callback *discover = session->discover; struct avdtp_error avdtp_err; if (!discover) return; avdtp_error_init(&avdtp_err, AVDTP_ERRNO, err); if (discover->id > 0) g_source_remove(discover->id); if (!err) g_slist_foreach(session->seps, remove_disappeared, session); if (discover->cb) discover->cb(session, session->seps, err ? &avdtp_err : NULL, discover->user_data); g_free(discover); session->discover = NULL; } static void release_stream(struct avdtp_stream *stream, struct avdtp *session) { struct avdtp_local_sep *sep = stream->lsep; if (sep->cfm && sep->cfm->abort && (sep->state != AVDTP_STATE_ABORTING || stream->abort_int)) sep->cfm->abort(session, sep, stream, NULL, sep->user_data); avdtp_sep_set_state(session, sep, AVDTP_STATE_IDLE); } static void remove_disconnect_timer(struct avdtp *session) { if (!session->dc_timer) return; timeout_remove(session->dc_timer); session->dc_timer = 0; session->stream_setup = FALSE; /* Release disconnect timer reference */ avdtp_unref(session); } static void avdtp_free(void *data) { struct avdtp *session = data; DBG("%p", session); g_slist_free_full(session->streams, stream_free); if (session->io) { g_io_channel_shutdown(session->io, FALSE, NULL); g_io_channel_unref(session->io); } if (session->io_id) { g_source_remove(session->io_id); session->io_id = 0; } if (session->req) pending_req_free(session->req); g_slist_free_full(session->req_queue, pending_req_free); g_slist_free_full(session->prio_queue, pending_req_free); g_slist_free_full(session->seps, sep_free); g_free(session->buf); btd_device_unref(session->device); g_free(session); } static void connection_lost(struct avdtp *session, int err) { char address[18]; session = avdtp_ref(session); ba2str(device_get_address(session->device), address); DBG("Disconnected from %s", address); g_slist_foreach(session->streams, (GFunc) release_stream, session); session->streams = NULL; finalize_discovery(session, err); avdtp_set_state(session, AVDTP_SESSION_STATE_DISCONNECTED); avdtp_unref(session); } static bool disconnect_timeout(gpointer user_data) { struct avdtp *session = user_data; struct btd_service *service; gboolean stream_setup; session->dc_timer = 0; stream_setup = session->stream_setup; session->stream_setup = FALSE; service = btd_device_get_service(session->device, A2DP_SINK_UUID); if (service && stream_setup) { sink_setup_stream(service, session); goto done; } service = btd_device_get_service(session->device, A2DP_SOURCE_UUID); if (service && stream_setup) { source_setup_stream(service, session); goto done; } connection_lost(session, ETIMEDOUT); done: /* Release disconnect timer reference */ avdtp_unref(session); return FALSE; } static void set_disconnect_timer(struct avdtp *session) { /* Take a ref while disconnect timer is active */ avdtp_ref(session); DBG("timeout %d", session->dc_timeout); session->dc_timer = timeout_add_seconds(session->dc_timeout, disconnect_timeout, session, NULL); } void avdtp_unref(struct avdtp *session) { if (!session) return; session->ref--; DBG("%p: ref=%d", session, session->ref); if (session->ref > 0) return; switch (session->state) { case AVDTP_SESSION_STATE_CONNECTED: /* Only set disconnect timer if there are local endpoints * otherwise disconnect immediately. */ if (queue_isempty(session->lseps)) connection_lost(session, ECONNRESET); else set_disconnect_timer(session); break; case AVDTP_SESSION_STATE_CONNECTING: connection_lost(session, ECONNABORTED); break; case AVDTP_SESSION_STATE_DISCONNECTED: default: avdtp_free(session); break; } } struct avdtp *avdtp_ref(struct avdtp *session) { session->ref++; DBG("%p: ref=%d", session, session->ref); remove_disconnect_timer(session); return session; } static bool match_by_seid(const void *data, const void *user_data) { const struct avdtp_local_sep *sep = data; uint8_t seid = PTR_TO_UINT(user_data); return sep->info.seid == seid; } static struct avdtp_local_sep *find_local_sep_by_seid(struct avdtp *session, uint8_t seid) { return queue_find(session->lseps, match_by_seid, INT_TO_PTR(seid)); } struct avdtp_remote_sep *avdtp_find_remote_sep(struct avdtp *session, struct avdtp_local_sep *lsep) { GSList *l; if (lsep->info.inuse) return NULL; for (l = session->seps; l != NULL; l = g_slist_next(l)) { struct avdtp_remote_sep *sep = l->data; struct avdtp_service_capability *cap; struct avdtp_media_codec_capability *codec_data; /* Type must be different: source <-> sink */ if (sep->type == lsep->info.type) continue; if (sep->media_type != lsep->info.media_type) continue; if (!sep->codec) continue; cap = sep->codec; codec_data = (void *) cap->data; if (codec_data->media_codec_type != lsep->codec) continue; if (lsep->ind && lsep->ind->match_codec) if (!lsep->ind->match_codec(session, codec_data, lsep->user_data)) continue; if (sep->stream == NULL) return sep; } return NULL; } static GSList *caps_to_list(uint8_t *data, size_t size, struct avdtp_service_capability **codec, gboolean *delay_reporting, uint8_t *err) { struct avdtp_service_capability *cap; GSList *caps; if (delay_reporting) *delay_reporting = FALSE; if (size < sizeof(*cap)) return NULL; for (caps = NULL; size >= sizeof(*cap);) { struct avdtp_service_capability *cpy; cap = (struct avdtp_service_capability *)data; /* Verify that the Media Transport capability's length = 0. * Reject otherwise */ if (cap->category == AVDTP_MEDIA_TRANSPORT && cap->length != 0) { error("Invalid media transport in getcap resp"); if (err) *err = AVDTP_BAD_MEDIA_TRANSPORT_FORMAT; break; } if (sizeof(*cap) + cap->length > size) { error("Invalid capability data in getcap resp"); break; } if (cap->category == AVDTP_MEDIA_CODEC && cap->length < sizeof(**codec)) { error("Invalid codec data in getcap resp"); break; } cpy = util_malloc(sizeof(*cpy) + cap->length); memcpy(cpy, cap, sizeof(*cap) + cap->length); size -= sizeof(*cap) + cap->length; data += sizeof(*cap) + cap->length; caps = g_slist_append(caps, cpy); switch (cap->category) { case AVDTP_MEDIA_CODEC: if (codec) *codec = cpy; break; case AVDTP_DELAY_REPORTING: if (delay_reporting) *delay_reporting = TRUE; break; } } return caps; } static gboolean avdtp_unknown_cmd(struct avdtp *session, uint8_t transaction, uint8_t signal_id) { return avdtp_send(session, transaction, AVDTP_MSG_TYPE_GEN_REJECT, signal_id, NULL, 0); } static void copy_seps(void *data, void *user_data) { struct avdtp_local_sep *sep = data; struct seid_info **p = user_data; memcpy(*p, &sep->info, sizeof(struct seid_info)); *p = *p + 1; } static gboolean avdtp_discover_cmd(struct avdtp *session, uint8_t transaction, void *buf, int size) { unsigned int rsp_size, sep_count; struct seid_info *seps, *p; gboolean ret; sep_count = queue_length(session->lseps); if (sep_count == 0) { uint8_t err = AVDTP_NOT_SUPPORTED_COMMAND; return avdtp_send(session, transaction, AVDTP_MSG_TYPE_REJECT, AVDTP_DISCOVER, &err, sizeof(err)); } rsp_size = sep_count * sizeof(struct seid_info); seps = g_new0(struct seid_info, sep_count); p = seps; queue_foreach(session->lseps, copy_seps, &p); ret = avdtp_send(session, transaction, AVDTP_MSG_TYPE_ACCEPT, AVDTP_DISCOVER, seps, rsp_size); g_free(seps); return ret; } static gboolean avdtp_getcap_cmd(struct avdtp *session, uint8_t transaction, struct seid_req *req, unsigned int size, gboolean get_all) { GSList *l, *caps; struct avdtp_local_sep *sep = NULL; unsigned int rsp_size; uint8_t err, buf[1024], *ptr = buf; uint8_t cmd; cmd = get_all ? AVDTP_GET_ALL_CAPABILITIES : AVDTP_GET_CAPABILITIES; if (size < sizeof(struct seid_req)) { err = AVDTP_BAD_LENGTH; goto failed; } sep = find_local_sep_by_seid(session, req->acp_seid); if (!sep) { err = AVDTP_BAD_ACP_SEID; goto failed; } if (!sep->ind->get_capability(session, sep, get_all, &caps, &err, sep->user_data)) goto failed; for (l = caps, rsp_size = 0; l != NULL; l = g_slist_next(l)) { struct avdtp_service_capability *cap = l->data; if (rsp_size + cap->length + 2 > sizeof(buf)) break; memcpy(ptr, cap, cap->length + 2); rsp_size += cap->length + 2; ptr += cap->length + 2; g_free(cap); } g_slist_free(caps); return avdtp_send(session, transaction, AVDTP_MSG_TYPE_ACCEPT, cmd, buf, rsp_size); failed: return avdtp_send(session, transaction, AVDTP_MSG_TYPE_REJECT, cmd, &err, sizeof(err)); } static void setconf_cb(struct avdtp *session, struct avdtp_stream *stream, struct avdtp_error *err) { struct conf_rej rej; struct avdtp_local_sep *sep; if (err != NULL) { rej.error = AVDTP_UNSUPPORTED_CONFIGURATION; rej.category = err->err.error_code; avdtp_send(session, session->in_cmd.transaction, AVDTP_MSG_TYPE_REJECT, AVDTP_SET_CONFIGURATION, &rej, sizeof(rej)); stream_free(stream); return; } if (!avdtp_send(session, session->in_cmd.transaction, AVDTP_MSG_TYPE_ACCEPT, AVDTP_SET_CONFIGURATION, NULL, 0)) { stream_free(stream); return; } sep = stream->lsep; sep->stream = stream; sep->info.inuse = 1; session->streams = g_slist_append(session->streams, stream); avdtp_sep_set_state(session, sep, AVDTP_STATE_CONFIGURED); } static gboolean avdtp_setconf_cmd(struct avdtp *session, uint8_t transaction, struct setconf_req *req, unsigned int size) { struct conf_rej rej; struct avdtp_local_sep *sep; struct avdtp_stream *stream; uint8_t err = 0, category = 0x00; struct btd_service *service; if (size < sizeof(struct setconf_req)) { error("Too short getcap request"); return FALSE; } sep = find_local_sep_by_seid(session, req->acp_seid); if (!sep) { err = AVDTP_BAD_ACP_SEID; goto failed; } if (sep->stream) { err = AVDTP_SEP_IN_USE; goto failed; } switch (sep->info.type) { case AVDTP_SEP_TYPE_SOURCE: service = btd_device_get_service(session->device, A2DP_SINK_UUID); if (service == NULL) { btd_device_add_uuid(session->device, A2DP_SINK_UUID); service = btd_device_get_service(session->device, A2DP_SINK_UUID); if (service == NULL) { error("Unable to get a audio sink object"); err = AVDTP_BAD_STATE; goto failed; } } break; case AVDTP_SEP_TYPE_SINK: service = btd_device_get_service(session->device, A2DP_SOURCE_UUID); if (service == NULL) { btd_device_add_uuid(session->device, A2DP_SOURCE_UUID); service = btd_device_get_service(session->device, A2DP_SOURCE_UUID); if (service == NULL) { error("Unable to get a audio source object"); err = AVDTP_BAD_STATE; goto failed; } } break; } stream = g_new0(struct avdtp_stream, 1); stream->session = session; stream->lsep = sep; stream->rseid = req->int_seid; stream->caps = caps_to_list(req->caps, size - sizeof(struct setconf_req), &stream->codec, &stream->delay_reporting, &err); if (err) goto failed_stream; if (!stream->caps || !stream->codec) { err = AVDTP_UNSUPPORTED_CONFIGURATION; category = 0x00; goto failed_stream; } if (stream->delay_reporting && session->version < 0x0103) session->version = 0x0103; if (sep->ind && sep->ind->set_configuration) { if (!sep->ind->set_configuration(session, sep, stream, stream->caps, setconf_cb, sep->user_data)) { err = AVDTP_UNSUPPORTED_CONFIGURATION; category = 0x00; goto failed_stream; } } else { if (!avdtp_send(session, transaction, AVDTP_MSG_TYPE_ACCEPT, AVDTP_SET_CONFIGURATION, NULL, 0)) { stream_free(stream); return FALSE; } sep->stream = stream; sep->info.inuse = 1; session->streams = g_slist_append(session->streams, stream); avdtp_sep_set_state(session, sep, AVDTP_STATE_CONFIGURED); } return TRUE; failed_stream: stream_free(stream); failed: rej.error = err; rej.category = category; return avdtp_send(session, transaction, AVDTP_MSG_TYPE_REJECT, AVDTP_SET_CONFIGURATION, &rej, sizeof(rej)); } static gboolean avdtp_getconf_cmd(struct avdtp *session, uint8_t transaction, struct seid_req *req, int size) { GSList *l; struct avdtp_local_sep *sep = NULL; int rsp_size; uint8_t err; uint8_t buf[1024]; uint8_t *ptr = buf; if (size < (int) sizeof(struct seid_req)) { error("Too short getconf request"); return FALSE; } memset(buf, 0, sizeof(buf)); sep = find_local_sep_by_seid(session, req->acp_seid); if (!sep) { err = AVDTP_BAD_ACP_SEID; goto failed; } if (!sep->stream || !sep->stream->caps) { err = AVDTP_UNSUPPORTED_CONFIGURATION; goto failed; } for (l = sep->stream->caps, rsp_size = 0; l != NULL; l = g_slist_next(l)) { struct avdtp_service_capability *cap = l->data; if (rsp_size + cap->length + 2 > (int) sizeof(buf)) break; memcpy(ptr, cap, cap->length + 2); rsp_size += cap->length + 2; ptr += cap->length + 2; } return avdtp_send(session, transaction, AVDTP_MSG_TYPE_ACCEPT, AVDTP_GET_CONFIGURATION, buf, rsp_size); failed: return avdtp_send(session, transaction, AVDTP_MSG_TYPE_REJECT, AVDTP_GET_CONFIGURATION, &err, sizeof(err)); } static gboolean avdtp_reconf_cmd(struct avdtp *session, uint8_t transaction, struct seid_req *req, int size) { struct conf_rej rej; rej.error = AVDTP_NOT_SUPPORTED_COMMAND; rej.category = 0x00; return avdtp_send(session, transaction, AVDTP_MSG_TYPE_REJECT, AVDTP_RECONFIGURE, &rej, sizeof(rej)); } static void check_seid_collision(struct pending_req *req, uint8_t id) { struct seid_req *seid = req->data; if (seid->acp_seid == id) req->collided = TRUE; } static void check_start_collision(struct pending_req *req, uint8_t id) { struct start_req *start = req->data; int count = 1 + req->data_size - sizeof(struct start_req); int i; for (i = 0; i < count; i++) { struct seid seid = start->seids[i]; if (seid.seid == id) { req->collided = TRUE; return; } } } static void check_suspend_collision(struct pending_req *req, uint8_t id) { struct suspend_req *suspend = req->data; int count = 1 + req->data_size - sizeof(struct suspend_req); int i; for (i = 0; i < count; i++) { struct seid seid = suspend->seids[i]; if (seid.seid == id) { req->collided = TRUE; return; } } } static void avdtp_check_collision(struct avdtp *session, uint8_t cmd, struct avdtp_stream *stream) { struct pending_req *req = session->req; if (req == NULL || (req->signal_id != cmd && cmd != AVDTP_ABORT)) return; if (cmd == AVDTP_ABORT) cmd = req->signal_id; switch (cmd) { case AVDTP_OPEN: case AVDTP_CLOSE: check_seid_collision(req, stream->rseid); break; case AVDTP_START: check_start_collision(req, stream->rseid); break; case AVDTP_SUSPEND: check_suspend_collision(req, stream->rseid); break; } } static gboolean avdtp_open_cmd(struct avdtp *session, uint8_t transaction, struct seid_req *req, unsigned int size) { struct avdtp_local_sep *sep; struct avdtp_stream *stream; uint8_t err; if (size < sizeof(struct seid_req)) { error("Too short abort request"); return FALSE; } sep = find_local_sep_by_seid(session, req->acp_seid); if (!sep) { err = AVDTP_BAD_ACP_SEID; goto failed; } if (sep->state != AVDTP_STATE_CONFIGURED) { err = AVDTP_BAD_STATE; goto failed; } stream = sep->stream; /* Check if the stream is pending and there is an IO set already */ if (stream == session->pending_open && session->pending_open_io) { handle_transport_connect(session, session->pending_open_io, stream->imtu, stream->omtu); return avdtp_send(session, transaction, AVDTP_MSG_TYPE_ACCEPT, AVDTP_OPEN, NULL, 0); } if (sep->ind && sep->ind->open && !session->pending_open) { if (!sep->ind->open(session, sep, stream, &err, sep->user_data)) goto failed; } avdtp_check_collision(session, AVDTP_OPEN, stream); if (!avdtp_send(session, transaction, AVDTP_MSG_TYPE_ACCEPT, AVDTP_OPEN, NULL, 0)) return FALSE; if (!session->pending_open) stream_set_pending_open(stream, NULL); return TRUE; failed: return avdtp_send(session, transaction, AVDTP_MSG_TYPE_REJECT, AVDTP_OPEN, &err, sizeof(err)); } static gboolean avdtp_start_cmd(struct avdtp *session, uint8_t transaction, struct start_req *req, unsigned int size) { struct avdtp_local_sep *sep; struct avdtp_stream *stream; struct stream_rej rej; uint8_t err, failed_seid; int seid_count, i; if (size < sizeof(struct start_req)) { error("Too short start request"); return FALSE; } seid_count = 1 + size - sizeof(struct start_req); for (i = 0; i < seid_count; i++) { struct seid seid = req->seids[i]; failed_seid = seid.seid; sep = find_local_sep_by_seid(session, seid.seid); if (!sep || !sep->stream) { err = AVDTP_BAD_ACP_SEID; goto failed; } stream = sep->stream; /* Also reject start cmd if state is not open */ if (sep->state != AVDTP_STATE_OPEN) { err = AVDTP_BAD_STATE; goto failed; } stream->starting = TRUE; if (sep->ind && sep->ind->start) { if (!sep->ind->start(session, sep, stream, &err, sep->user_data)) goto failed; } avdtp_check_collision(session, AVDTP_START, stream); avdtp_sep_set_state(session, sep, AVDTP_STATE_STREAMING); } return avdtp_send(session, transaction, AVDTP_MSG_TYPE_ACCEPT, AVDTP_START, NULL, 0); failed: DBG("Rejecting (%d)", err); memset(&rej, 0, sizeof(rej)); rej.acp_seid = failed_seid; rej.error = err; return avdtp_send(session, transaction, AVDTP_MSG_TYPE_REJECT, AVDTP_START, &rej, sizeof(rej)); } static gboolean avdtp_close_cmd(struct avdtp *session, uint8_t transaction, struct seid_req *req, unsigned int size) { struct avdtp_local_sep *sep; struct avdtp_stream *stream; uint8_t err; if (size < sizeof(struct seid_req)) { error("Too short close request"); return FALSE; } sep = find_local_sep_by_seid(session, req->acp_seid); if (!sep || !sep->stream) { err = AVDTP_BAD_ACP_SEID; goto failed; } if (sep->state != AVDTP_STATE_OPEN && sep->state != AVDTP_STATE_STREAMING) { err = AVDTP_BAD_STATE; goto failed; } stream = sep->stream; if (sep->ind && sep->ind->close) { if (!sep->ind->close(session, sep, stream, &err, sep->user_data)) goto failed; } avdtp_check_collision(session, AVDTP_CLOSE, stream); avdtp_sep_set_state(session, sep, AVDTP_STATE_CLOSING); session->dc_timeout = DISCONNECT_TIMEOUT; if (!avdtp_send(session, transaction, AVDTP_MSG_TYPE_ACCEPT, AVDTP_CLOSE, NULL, 0)) return FALSE; stream->timer = timeout_add_seconds(REQ_TIMEOUT, stream_close_timeout, stream, NULL); return TRUE; failed: return avdtp_send(session, transaction, AVDTP_MSG_TYPE_REJECT, AVDTP_CLOSE, &err, sizeof(err)); } static gboolean avdtp_suspend_cmd(struct avdtp *session, uint8_t transaction, struct suspend_req *req, unsigned int size) { struct avdtp_local_sep *sep; struct avdtp_stream *stream; struct stream_rej rej; uint8_t err, failed_seid; int seid_count, i; if (size < sizeof(struct suspend_req)) { error("Too short suspend request"); return FALSE; } seid_count = 1 + size - sizeof(struct suspend_req); for (i = 0; i < seid_count; i++) { struct seid seid = req->seids[i]; failed_seid = seid.seid; sep = find_local_sep_by_seid(session, seid.seid); if (!sep || !sep->stream) { err = AVDTP_BAD_ACP_SEID; goto failed; } stream = sep->stream; if (sep->state != AVDTP_STATE_STREAMING) { err = AVDTP_BAD_STATE; goto failed; } if (sep->ind && sep->ind->suspend) { if (!sep->ind->suspend(session, sep, stream, &err, sep->user_data)) goto failed; } avdtp_check_collision(session, AVDTP_SUSPEND, stream); avdtp_sep_set_state(session, sep, AVDTP_STATE_OPEN); } return avdtp_send(session, transaction, AVDTP_MSG_TYPE_ACCEPT, AVDTP_SUSPEND, NULL, 0); failed: memset(&rej, 0, sizeof(rej)); rej.acp_seid = failed_seid; rej.error = err; return avdtp_send(session, transaction, AVDTP_MSG_TYPE_REJECT, AVDTP_SUSPEND, &rej, sizeof(rej)); } static gboolean avdtp_abort_cmd(struct avdtp *session, uint8_t transaction, struct seid_req *req, unsigned int size) { struct avdtp_local_sep *sep; uint8_t err; gboolean ret; if (size < sizeof(struct seid_req)) { error("Too short abort request"); return FALSE; } sep = find_local_sep_by_seid(session, req->acp_seid); if (!sep || !sep->stream) return TRUE; if (sep->ind && sep->ind->abort) sep->ind->abort(session, sep, sep->stream, &err, sep->user_data); avdtp_check_collision(session, AVDTP_ABORT, sep->stream); ret = avdtp_send(session, transaction, AVDTP_MSG_TYPE_ACCEPT, AVDTP_ABORT, NULL, 0); if (ret) { avdtp_sep_set_state(session, sep, AVDTP_STATE_ABORTING); session->dc_timeout = DISCONNECT_TIMEOUT; } return ret; } static gboolean avdtp_secctl_cmd(struct avdtp *session, uint8_t transaction, struct seid_req *req, int size) { return avdtp_unknown_cmd(session, transaction, AVDTP_SECURITY_CONTROL); } static gboolean avdtp_delayreport_cmd(struct avdtp *session, uint8_t transaction, struct delay_req *req, unsigned int size) { struct avdtp_local_sep *sep; struct avdtp_stream *stream; uint8_t err; if (size < sizeof(struct delay_req)) { error("Too short delay report request"); return FALSE; } sep = find_local_sep_by_seid(session, req->acp_seid); if (!sep || !sep->stream) { err = AVDTP_BAD_ACP_SEID; goto failed; } stream = sep->stream; if (sep->state != AVDTP_STATE_CONFIGURED && sep->state != AVDTP_STATE_OPEN && sep->state != AVDTP_STATE_STREAMING) { err = AVDTP_BAD_STATE; goto failed; } stream->delay = ntohs(req->delay); if (sep->ind && sep->ind->delayreport) { if (!sep->ind->delayreport(session, sep, stream->rseid, stream->delay, &err, sep->user_data)) goto failed; } return avdtp_send(session, transaction, AVDTP_MSG_TYPE_ACCEPT, AVDTP_DELAY_REPORT, NULL, 0); failed: return avdtp_send(session, transaction, AVDTP_MSG_TYPE_REJECT, AVDTP_DELAY_REPORT, &err, sizeof(err)); } static gboolean avdtp_parse_cmd(struct avdtp *session, uint8_t transaction, uint8_t signal_id, void *buf, int size) { /* Reset disconnect timer if command is received */ if (session->dc_timer) { timeout_remove(session->dc_timer); session->dc_timer = timeout_add_seconds(session->dc_timeout, disconnect_timeout, session, NULL); } switch (signal_id) { case AVDTP_DISCOVER: DBG("Received DISCOVER_CMD"); return avdtp_discover_cmd(session, transaction, buf, size); case AVDTP_GET_CAPABILITIES: DBG("Received GET_CAPABILITIES_CMD"); return avdtp_getcap_cmd(session, transaction, buf, size, FALSE); case AVDTP_GET_ALL_CAPABILITIES: DBG("Received GET_ALL_CAPABILITIES_CMD"); return avdtp_getcap_cmd(session, transaction, buf, size, TRUE); case AVDTP_SET_CONFIGURATION: DBG("Received SET_CONFIGURATION_CMD"); return avdtp_setconf_cmd(session, transaction, buf, size); case AVDTP_GET_CONFIGURATION: DBG("Received GET_CONFIGURATION_CMD"); return avdtp_getconf_cmd(session, transaction, buf, size); case AVDTP_RECONFIGURE: DBG("Received RECONFIGURE_CMD"); return avdtp_reconf_cmd(session, transaction, buf, size); case AVDTP_OPEN: DBG("Received OPEN_CMD"); return avdtp_open_cmd(session, transaction, buf, size); case AVDTP_START: DBG("Received START_CMD"); return avdtp_start_cmd(session, transaction, buf, size); case AVDTP_CLOSE: DBG("Received CLOSE_CMD"); return avdtp_close_cmd(session, transaction, buf, size); case AVDTP_SUSPEND: DBG("Received SUSPEND_CMD"); return avdtp_suspend_cmd(session, transaction, buf, size); case AVDTP_ABORT: DBG("Received ABORT_CMD"); return avdtp_abort_cmd(session, transaction, buf, size); case AVDTP_SECURITY_CONTROL: DBG("Received SECURITY_CONTROL_CMD"); return avdtp_secctl_cmd(session, transaction, buf, size); case AVDTP_DELAY_REPORT: DBG("Received DELAY_REPORT_CMD"); return avdtp_delayreport_cmd(session, transaction, buf, size); default: DBG("Received unknown request id %u", signal_id); return avdtp_unknown_cmd(session, transaction, signal_id); } } enum avdtp_parse_result { PARSE_ERROR, PARSE_FRAGMENT, PARSE_SUCCESS }; static enum avdtp_parse_result avdtp_parse_data(struct avdtp *session, void *buf, size_t size) { struct avdtp_common_header *header = buf; struct avdtp_single_header *single = (void *) session->buf; struct avdtp_start_header *start = (void *) session->buf; void *payload; gsize payload_size; struct in_buf *in; if (header->message_type == AVDTP_MSG_TYPE_COMMAND) in = &session->in_cmd; else in = &session->in_resp; switch (header->packet_type) { case AVDTP_PKT_TYPE_SINGLE: if (size < sizeof(*single)) { error("Received too small single packet (%zu bytes)", size); return PARSE_ERROR; } if (in->active) { error("SINGLE: Invalid AVDTP packet fragmentation"); return PARSE_ERROR; } payload = session->buf + sizeof(*single); payload_size = size - sizeof(*single); in->active = TRUE; in->data_size = 0; in->no_of_packets = 1; in->transaction = header->transaction; in->signal_id = single->signal_id; break; case AVDTP_PKT_TYPE_START: if (size < sizeof(*start)) { error("Received too small start packet (%zu bytes)", size); return PARSE_ERROR; } if (in->active) { error("START: Invalid AVDTP packet fragmentation"); return PARSE_ERROR; } in->active = TRUE; in->data_size = 0; in->transaction = header->transaction; in->no_of_packets = start->no_of_packets; in->signal_id = start->signal_id; payload = session->buf + sizeof(*start); payload_size = size - sizeof(*start); break; case AVDTP_PKT_TYPE_CONTINUE: if (size < sizeof(struct avdtp_continue_header)) { error("Received too small continue packet (%zu bytes)", size); return PARSE_ERROR; } if (!in->active) { error("CONTINUE: Invalid AVDTP packet fragmentation"); return PARSE_ERROR; } if (in->transaction != header->transaction) { error("Continue transaction id doesn't match"); return PARSE_ERROR; } if (in->no_of_packets <= 1) { error("Too few continue packets"); return PARSE_ERROR; } payload = session->buf + sizeof(struct avdtp_continue_header); payload_size = size - sizeof(struct avdtp_continue_header); break; case AVDTP_PKT_TYPE_END: if (size < sizeof(struct avdtp_continue_header)) { error("Received too small end packet (%zu bytes)", size); return PARSE_ERROR; } if (!in->active) { error("END: Invalid AVDTP packet fragmentation"); return PARSE_ERROR; } if (in->transaction != header->transaction) { error("End transaction id doesn't match"); return PARSE_ERROR; } if (in->no_of_packets > 1) { error("Got an end packet too early"); return PARSE_ERROR; } payload = session->buf + sizeof(struct avdtp_continue_header); payload_size = size - sizeof(struct avdtp_continue_header); break; default: error("Invalid AVDTP packet type 0x%02X", header->packet_type); return PARSE_ERROR; } if (in->data_size + payload_size > sizeof(in->buf)) { error("Not enough incoming buffer space!"); return PARSE_ERROR; } memcpy(in->buf + in->data_size, payload, payload_size); in->data_size += payload_size; if (in->no_of_packets > 1) { in->no_of_packets--; DBG("Received AVDTP fragment. %d to go", in->no_of_packets); return PARSE_FRAGMENT; } in->active = FALSE; return PARSE_SUCCESS; } static gboolean session_cb(GIOChannel *chan, GIOCondition cond, gpointer data) { struct avdtp *session = data; struct avdtp_common_header *header; ssize_t size; int fd; DBG(""); if (cond & G_IO_NVAL) return FALSE; header = (void *) session->buf; if (cond & (G_IO_HUP | G_IO_ERR)) goto failed; fd = g_io_channel_unix_get_fd(chan); size = read(fd, session->buf, session->imtu); if (size < 0) { error("IO Channel read error"); goto failed; } if ((size_t) size < sizeof(struct avdtp_common_header)) { error("Received too small packet (%zu bytes)", size); goto failed; } switch (avdtp_parse_data(session, session->buf, size)) { case PARSE_ERROR: goto failed; case PARSE_FRAGMENT: return TRUE; case PARSE_SUCCESS: break; } if (header->message_type == AVDTP_MSG_TYPE_COMMAND) { if (!avdtp_parse_cmd(session, session->in_cmd.transaction, session->in_cmd.signal_id, session->in_cmd.buf, session->in_cmd.data_size)) { error("Unable to handle command. Disconnecting"); goto failed; } if (session->req && session->req->collided) { DBG("Collision detected"); goto next; } return TRUE; } if (session->req == NULL) { error("No pending request, ignoring message"); return TRUE; } if (header->transaction != session->req->transaction) { error("Transaction label doesn't match"); return TRUE; } if (session->in_resp.signal_id != session->req->signal_id) { error("Response signal doesn't match"); return TRUE; } timeout_remove(session->req->timeout); session->req->timeout = 0; switch (header->message_type) { case AVDTP_MSG_TYPE_ACCEPT: if (!avdtp_parse_resp(session, session->req->stream, session->in_resp.transaction, session->in_resp.signal_id, session->in_resp.buf, session->in_resp.data_size)) { error("Unable to parse accept response"); goto failed; } break; case AVDTP_MSG_TYPE_REJECT: if (!avdtp_parse_rej(session, session->req->stream, session->in_resp.transaction, session->in_resp.signal_id, session->in_resp.buf, session->in_resp.data_size)) { error("Unable to parse reject response"); goto failed; } break; case AVDTP_MSG_TYPE_GEN_REJECT: error("Received a General Reject message"); break; default: error("Unknown message type 0x%02X", header->message_type); break; } next: pending_req_free(session->req); session->req = NULL; process_queue(session); return TRUE; failed: connection_lost(session, EIO); return FALSE; } static uint16_t get_version(struct avdtp *session) { const sdp_record_t *rec; sdp_list_t *protos; sdp_data_t *proto_desc; uint16_t ver = 0x0000; rec = btd_device_get_record(session->device, A2DP_SINK_UUID); if (!rec) rec = btd_device_get_record(session->device, A2DP_SOURCE_UUID); if (!rec) return ver; if (sdp_get_access_protos(rec, &protos) < 0) return ver; proto_desc = sdp_get_proto_desc(protos, AVDTP_UUID); if (proto_desc && proto_desc->dtd == SDP_UINT16) ver = proto_desc->val.uint16; sdp_list_foreach(protos, (sdp_list_func_t) sdp_list_free, NULL); sdp_list_free(protos, NULL); return ver; } static void avdtp_connect_cb(GIOChannel *chan, GError *err, gpointer user_data) { struct avdtp *session = user_data; char address[18]; int err_no = EIO; if (err) { err_no = err->code; error("%s", err->message); goto failed; } if (!session->io) session->io = g_io_channel_ref(chan); /* Check if kernel supports reading packet types */ bt_io_get(chan, NULL, BT_IO_OPT_PHY, &session->phy, BT_IO_OPT_INVALID); bt_io_get(chan, &err, BT_IO_OPT_OMTU, &session->omtu, BT_IO_OPT_IMTU, &session->imtu, BT_IO_OPT_INVALID); if (err) { err_no = err->code; error("%s", err->message); goto failed; } ba2str(device_get_address(session->device), address); DBG("AVDTP: connected %s channel to %s", session->pending_open ? "transport" : "signaling", address); if (session->state == AVDTP_SESSION_STATE_CONNECTING) { DBG("AVDTP imtu=%u, omtu=%u", session->imtu, session->omtu); session->buf = g_malloc0(MAX(session->imtu, session->omtu)); avdtp_set_state(session, AVDTP_SESSION_STATE_CONNECTED); if (session->io_id) g_source_remove(session->io_id); session->io_id = g_io_add_watch(chan, G_IO_IN | G_IO_ERR | G_IO_HUP | G_IO_NVAL, (GIOFunc) session_cb, session); if (session->stream_setup) set_disconnect_timer(session); } else if (session->pending_open) handle_transport_connect(session, chan, session->imtu, session->omtu); else goto failed; process_queue(session); return; failed: if (session->pending_open) { struct avdtp_stream *stream = session->pending_open; handle_transport_connect(session, NULL, 0, 0); if (avdtp_abort(session, stream) < 0) avdtp_sep_set_state(session, stream->lsep, AVDTP_STATE_IDLE); } else connection_lost(session, err_no); } struct avdtp *avdtp_new(GIOChannel *chan, struct btd_device *device, struct queue *lseps) { struct avdtp *session; session = g_new0(struct avdtp, 1); session->device = btd_device_ref(device); /* We don't use avdtp_set_state() here since this isn't a state change * but just setting of the initial state */ session->state = AVDTP_SESSION_STATE_DISCONNECTED; session->lseps = lseps; session->version = get_version(session); if (!chan) return session; avdtp_set_state(session, AVDTP_SESSION_STATE_CONNECTING); btd_device_add_uuid(device, ADVANCED_AUDIO_UUID); session->io = g_io_channel_ref(chan); session->io_id = g_io_add_watch(chan, G_IO_ERR | G_IO_HUP | G_IO_NVAL, (GIOFunc) session_cb, session); /* This is so that avdtp_connect_cb will know to do the right thing * with respect to the disconnect timer */ session->stream_setup = TRUE; session->dc_timeout = DISCONNECT_TIMEOUT; avdtp_connect_cb(chan, NULL, session); return session; } uint16_t avdtp_get_version(struct avdtp *session) { return session->version; } static GIOChannel *l2cap_connect(struct avdtp *session, BtIOMode mode) { GError *err = NULL; GIOChannel *io; const bdaddr_t *src; src = btd_adapter_get_address(device_get_adapter(session->device)); if (session->phy) io = bt_io_connect(avdtp_connect_cb, session, NULL, &err, BT_IO_OPT_SOURCE_BDADDR, src, BT_IO_OPT_DEST_BDADDR, device_get_address(session->device), BT_IO_OPT_PSM, AVDTP_PSM, BT_IO_OPT_MODE, mode, BT_IO_OPT_SEC_LEVEL, BT_IO_SEC_MEDIUM, /* Set Input MTU to 0 to auto-tune */ BT_IO_OPT_IMTU, 0, BT_IO_OPT_INVALID); else io = bt_io_connect(avdtp_connect_cb, session, NULL, &err, BT_IO_OPT_SOURCE_BDADDR, src, BT_IO_OPT_DEST_BDADDR, device_get_address(session->device), BT_IO_OPT_PSM, AVDTP_PSM, BT_IO_OPT_MODE, mode, BT_IO_OPT_SEC_LEVEL, BT_IO_SEC_MEDIUM, BT_IO_OPT_INVALID); if (!io) { error("%s", err->message); g_error_free(err); return NULL; } return io; } static void queue_request(struct avdtp *session, struct pending_req *req, gboolean priority) { if (priority) session->prio_queue = g_slist_append(session->prio_queue, req); else session->req_queue = g_slist_append(session->req_queue, req); } static uint8_t req_get_seid(struct pending_req *req) { if (req->signal_id == AVDTP_DISCOVER) return 0; return ((struct seid_req *) (req->data))->acp_seid; } static int cancel_request(struct avdtp *session, int err) { struct pending_req *req; struct seid_req sreq; struct avdtp_local_sep *lsep; struct avdtp_stream *stream; uint8_t seid; struct avdtp_error averr; req = session->req; session->req = NULL; avdtp_error_init(&averr, AVDTP_ERRNO, err); seid = req_get_seid(req); if (seid) stream = find_stream_by_rseid(session, seid); else stream = NULL; if (stream) lsep = stream->lsep; else lsep = NULL; switch (req->signal_id) { case AVDTP_RECONFIGURE: error("Reconfigure: %s (%d)", strerror(err), err); if (lsep && lsep->cfm && lsep->cfm->reconfigure) lsep->cfm->reconfigure(session, lsep, stream, &averr, lsep->user_data); break; case AVDTP_OPEN: error("Open: %s (%d)", strerror(err), err); if (lsep && lsep->cfm && lsep->cfm->open) lsep->cfm->open(session, lsep, stream, &averr, lsep->user_data); break; case AVDTP_START: error("Start: %s (%d)", strerror(err), err); if (lsep && lsep->cfm && lsep->cfm->start) { lsep->cfm->start(session, lsep, stream, &averr, lsep->user_data); if (stream) stream->starting = FALSE; } break; case AVDTP_SUSPEND: error("Suspend: %s (%d)", strerror(err), err); if (lsep && lsep->cfm && lsep->cfm->suspend) lsep->cfm->suspend(session, lsep, stream, &averr, lsep->user_data); break; case AVDTP_CLOSE: error("Close: %s (%d)", strerror(err), err); if (lsep && lsep->cfm && lsep->cfm->close) { lsep->cfm->close(session, lsep, stream, &averr, lsep->user_data); if (stream) stream->close_int = FALSE; } break; case AVDTP_SET_CONFIGURATION: error("SetConfiguration: %s (%d)", strerror(err), err); if (lsep && lsep->cfm && lsep->cfm->set_configuration) lsep->cfm->set_configuration(session, lsep, stream, &averr, lsep->user_data); break; case AVDTP_DISCOVER: error("Discover: %s (%d)", strerror(err), err); goto failed; case AVDTP_GET_CAPABILITIES: error("GetCapabilities: %s (%d)", strerror(err), err); goto failed; case AVDTP_ABORT: error("Abort: %s (%d)", strerror(err), err); goto failed; } if (!stream) goto failed; memset(&sreq, 0, sizeof(sreq)); sreq.acp_seid = seid; err = send_request(session, TRUE, stream, AVDTP_ABORT, &sreq, sizeof(sreq)); if (err < 0) { error("Unable to send abort request"); goto failed; } stream->abort_int = TRUE; goto done; failed: connection_lost(session, err); done: pending_req_free(req); return err; } static bool request_timeout(gpointer user_data) { struct avdtp *session = user_data; cancel_request(session, ETIMEDOUT); return FALSE; } static int send_req(struct avdtp *session, gboolean priority, struct pending_req *req) { static int transaction = 0; int err, timeout; if (session->state == AVDTP_SESSION_STATE_DISCONNECTED) { BtIOMode mode = btd_opts.avdtp.session_mode; session->io = l2cap_connect(session, mode); if (!session->io) { /* Report disconnection anyways, as the other layers * are using this state for cleanup. */ avdtp_set_state(session, AVDTP_SESSION_STATE_DISCONNECTED); err = -EIO; goto failed; } avdtp_set_state(session, AVDTP_SESSION_STATE_CONNECTING); } if (session->state < AVDTP_SESSION_STATE_CONNECTED || session->req != NULL) { queue_request(session, req, priority); return 0; } req->transaction = transaction++; transaction %= 16; /* FIXME: Should we retry to send if the buffer was not totally sent or in case of EINTR? */ if (!avdtp_send(session, req->transaction, AVDTP_MSG_TYPE_COMMAND, req->signal_id, req->data, req->data_size)) { err = -EIO; goto failed; } session->req = req; switch (req->signal_id) { case AVDTP_ABORT: timeout = ABORT_TIMEOUT; break; case AVDTP_SUSPEND: timeout = SUSPEND_TIMEOUT; break; default: timeout = REQ_TIMEOUT; } req->timeout = timeout_add_seconds(timeout, request_timeout, session, NULL); return 0; failed: free(req->data); g_free(req); return err; } static int send_request(struct avdtp *session, gboolean priority, struct avdtp_stream *stream, uint8_t signal_id, void *buffer, size_t size) { struct pending_req *req; if (stream && stream->abort_int && signal_id != AVDTP_ABORT) { DBG("Unable to send requests while aborting"); return -EINVAL; } req = g_new0(struct pending_req, 1); req->signal_id = signal_id; req->data = util_memdup(buffer, size); req->data_size = size; req->stream = stream; return send_req(session, priority, req); } static gboolean avdtp_discover_resp(struct avdtp *session, struct discover_resp *resp, int size) { int sep_count, i; uint8_t getcap_cmd; int ret = 0; gboolean getcap_pending = FALSE; if (session->version >= 0x0103) getcap_cmd = AVDTP_GET_ALL_CAPABILITIES; else getcap_cmd = AVDTP_GET_CAPABILITIES; sep_count = size / sizeof(struct seid_info); for (i = 0; i < sep_count; i++) { struct avdtp_remote_sep *sep; struct avdtp_stream *stream; struct seid_req req; DBG("seid %d type %d media %d in use %d", resp->seps[i].seid, resp->seps[i].type, resp->seps[i].media_type, resp->seps[i].inuse); stream = find_stream_by_rseid(session, resp->seps[i].seid); sep = find_remote_sep(session->seps, resp->seps[i].seid); if (sep && sep->type == resp->seps[i].type && sep->media_type == resp->seps[i].media_type && sep->codec) { sep->discovered = true; continue; } if (resp->seps[i].inuse && !stream) continue; sep = g_new0(struct avdtp_remote_sep, 1); session->seps = g_slist_append(session->seps, sep); sep->stream = stream; sep->seid = resp->seps[i].seid; sep->type = resp->seps[i].type; sep->media_type = resp->seps[i].media_type; sep->discovered = true; memset(&req, 0, sizeof(req)); req.acp_seid = sep->seid; ret = send_request(session, TRUE, NULL, getcap_cmd, &req, sizeof(req)); if (ret < 0) { session->seps = g_slist_remove(session->seps, sep); g_free(sep); break; } getcap_pending = TRUE; } if (!getcap_pending) finalize_discovery(session, -ret); return TRUE; } static gboolean avdtp_get_capabilities_resp(struct avdtp *session, struct getcap_resp *resp, unsigned int size) { struct avdtp_remote_sep *sep; uint8_t seid; /* Check for minimum required packet size includes: * 1. getcap resp header * 2. media transport capability (2 bytes) * 3. media codec capability type + length (2 bytes) * 4. the actual media codec elements * */ if (size < (sizeof(struct getcap_resp) + 4 + sizeof(struct avdtp_media_codec_capability))) { error("Too short getcap resp packet"); return FALSE; } seid = ((struct seid_req *) session->req->data)->acp_seid; sep = find_remote_sep(session->seps, seid); DBG("seid %d type %d media %d", sep->seid, sep->type, sep->media_type); if (sep->caps) { g_slist_free_full(sep->caps, g_free); sep->caps = NULL; sep->codec = NULL; sep->delay_reporting = FALSE; } sep->caps = caps_to_list(resp->caps, size - sizeof(struct getcap_resp), &sep->codec, &sep->delay_reporting, NULL); return TRUE; } static gboolean avdtp_set_configuration_resp(struct avdtp *session, struct avdtp_stream *stream, struct avdtp_single_header *resp, int size) { struct avdtp_local_sep *sep = stream->lsep; if (sep->cfm && sep->cfm->set_configuration) sep->cfm->set_configuration(session, sep, stream, NULL, sep->user_data); avdtp_sep_set_state(session, sep, AVDTP_STATE_CONFIGURED); return TRUE; } static gboolean avdtp_reconfigure_resp(struct avdtp *session, struct avdtp_stream *stream, struct avdtp_single_header *resp, int size) { return TRUE; } static gboolean avdtp_open_resp(struct avdtp *session, struct avdtp_stream *stream, struct seid_rej *resp, int size) { struct avdtp_local_sep *sep = stream->lsep; BtIOMode mode = btd_opts.avdtp.stream_mode; stream->io = l2cap_connect(session, mode); if (!stream->io) { avdtp_sep_set_state(session, sep, AVDTP_STATE_IDLE); return FALSE; } session->pending_open = stream; return TRUE; } static gboolean avdtp_start_resp(struct avdtp *session, struct avdtp_stream *stream, struct seid_rej *resp, int size) { struct avdtp_local_sep *sep = stream->lsep; if (sep->cfm && sep->cfm->start) sep->cfm->start(session, sep, stream, NULL, sep->user_data); /* We might be in STREAMING already if both sides send START_CMD at the * same time and the one in SNK role doesn't reject it as it should */ if (sep->state != AVDTP_STATE_STREAMING) avdtp_sep_set_state(session, sep, AVDTP_STATE_STREAMING); return TRUE; } static gboolean avdtp_close_resp(struct avdtp *session, struct avdtp_stream *stream, struct seid_rej *resp, int size) { struct avdtp_local_sep *sep = stream->lsep; avdtp_sep_set_state(session, sep, AVDTP_STATE_CLOSING); close_stream(stream); return TRUE; } static gboolean avdtp_suspend_resp(struct avdtp *session, struct avdtp_stream *stream, void *data, int size) { struct avdtp_local_sep *sep = stream->lsep; avdtp_sep_set_state(session, sep, AVDTP_STATE_OPEN); if (sep->cfm && sep->cfm->suspend) sep->cfm->suspend(session, sep, stream, NULL, sep->user_data); return TRUE; } static gboolean avdtp_abort_resp(struct avdtp *session, struct avdtp_stream *stream, struct seid_rej *resp, int size) { struct avdtp_local_sep *sep = stream->lsep; avdtp_sep_set_state(session, sep, AVDTP_STATE_ABORTING); if (sep->cfm && sep->cfm->abort) sep->cfm->abort(session, sep, stream, NULL, sep->user_data); avdtp_sep_set_state(session, sep, AVDTP_STATE_IDLE); return TRUE; } static gboolean avdtp_delay_report_resp(struct avdtp *session, struct avdtp_stream *stream, void *data, int size) { struct avdtp_local_sep *sep = stream->lsep; if (sep->cfm && sep->cfm->delay_report) sep->cfm->delay_report(session, sep, stream, NULL, sep->user_data); return TRUE; } static gboolean avdtp_parse_resp(struct avdtp *session, struct avdtp_stream *stream, uint8_t transaction, uint8_t signal_id, void *buf, int size) { struct pending_req *next; const char *get_all = ""; if (session->prio_queue) next = session->prio_queue->data; else if (session->req_queue) next = session->req_queue->data; else next = NULL; switch (signal_id) { case AVDTP_DISCOVER: DBG("DISCOVER request succeeded"); return avdtp_discover_resp(session, buf, size); case AVDTP_GET_ALL_CAPABILITIES: get_all = "ALL_"; /* fall through */ case AVDTP_GET_CAPABILITIES: DBG("GET_%sCAPABILITIES request succeeded", get_all); if (!avdtp_get_capabilities_resp(session, buf, size)) return FALSE; if (!(next && (next->signal_id == AVDTP_GET_CAPABILITIES || next->signal_id == AVDTP_GET_ALL_CAPABILITIES))) finalize_discovery(session, 0); return TRUE; } /* The remaining commands require an existing stream so bail out * here if the stream got unexpectedly disconnected */ if (!stream) { DBG("AVDTP: stream was closed while waiting for reply"); return TRUE; } switch (signal_id) { case AVDTP_SET_CONFIGURATION: DBG("SET_CONFIGURATION request succeeded"); return avdtp_set_configuration_resp(session, stream, buf, size); case AVDTP_RECONFIGURE: DBG("RECONFIGURE request succeeded"); return avdtp_reconfigure_resp(session, stream, buf, size); case AVDTP_OPEN: DBG("OPEN request succeeded"); return avdtp_open_resp(session, stream, buf, size); case AVDTP_SUSPEND: DBG("SUSPEND request succeeded"); return avdtp_suspend_resp(session, stream, buf, size); case AVDTP_START: DBG("START request succeeded"); return avdtp_start_resp(session, stream, buf, size); case AVDTP_CLOSE: DBG("CLOSE request succeeded"); return avdtp_close_resp(session, stream, buf, size); case AVDTP_ABORT: DBG("ABORT request succeeded"); return avdtp_abort_resp(session, stream, buf, size); case AVDTP_DELAY_REPORT: DBG("DELAY_REPORT request succeeded"); return avdtp_delay_report_resp(session, stream, buf, size); } error("Unknown signal id in accept response: %u", signal_id); return TRUE; } static gboolean seid_rej_to_err(struct seid_rej *rej, unsigned int size, struct avdtp_error *err) { if (size < sizeof(struct seid_rej)) { error("Too small packet for seid_rej"); return FALSE; } avdtp_error_init(err, 0x00, rej->error); return TRUE; } static gboolean conf_rej_to_err(struct conf_rej *rej, unsigned int size, struct avdtp_error *err) { if (size < sizeof(struct conf_rej)) { error("Too small packet for conf_rej"); return FALSE; } avdtp_error_init(err, rej->category, rej->error); return TRUE; } static gboolean stream_rej_to_err(struct stream_rej *rej, unsigned int size, struct avdtp_error *err, uint8_t *acp_seid) { if (size < sizeof(struct stream_rej)) { error("Too small packet for stream_rej"); return FALSE; } avdtp_error_init(err, 0x00, rej->error); if (acp_seid) *acp_seid = rej->acp_seid; return TRUE; } static gboolean avdtp_parse_rej(struct avdtp *session, struct avdtp_stream *stream, uint8_t transaction, uint8_t signal_id, void *buf, int size) { struct avdtp_error err; uint8_t acp_seid; struct avdtp_local_sep *sep = stream ? stream->lsep : NULL; switch (signal_id) { case AVDTP_DISCOVER: if (!seid_rej_to_err(buf, size, &err)) return FALSE; error("DISCOVER request rejected: %s (%d)", avdtp_strerror(&err), err.err.error_code); return TRUE; case AVDTP_GET_CAPABILITIES: case AVDTP_GET_ALL_CAPABILITIES: if (!seid_rej_to_err(buf, size, &err)) return FALSE; error("GET_CAPABILITIES request rejected: %s (%d)", avdtp_strerror(&err), err.err.error_code); return TRUE; case AVDTP_OPEN: if (!seid_rej_to_err(buf, size, &err)) return FALSE; error("OPEN request rejected: %s (%d)", avdtp_strerror(&err), err.err.error_code); if (sep && sep->cfm && sep->cfm->open) sep->cfm->open(session, sep, stream, &err, sep->user_data); return TRUE; case AVDTP_SET_CONFIGURATION: if (!conf_rej_to_err(buf, size, &err)) return FALSE; error("SET_CONFIGURATION request rejected: %s (%d)", avdtp_strerror(&err), err.err.error_code); if (sep && sep->cfm && sep->cfm->set_configuration) sep->cfm->set_configuration(session, sep, stream, &err, sep->user_data); return TRUE; case AVDTP_RECONFIGURE: if (!conf_rej_to_err(buf, size, &err)) return FALSE; error("RECONFIGURE request rejected: %s (%d)", avdtp_strerror(&err), err.err.error_code); if (sep && sep->cfm && sep->cfm->reconfigure) sep->cfm->reconfigure(session, sep, stream, &err, sep->user_data); return TRUE; case AVDTP_START: if (!stream_rej_to_err(buf, size, &err, &acp_seid)) return FALSE; error("START request rejected: %s (%d)", avdtp_strerror(&err), err.err.error_code); if (sep && sep->cfm && sep->cfm->start) { sep->cfm->start(session, sep, stream, &err, sep->user_data); stream->starting = FALSE; } return TRUE; case AVDTP_SUSPEND: if (!stream_rej_to_err(buf, size, &err, &acp_seid)) return FALSE; error("SUSPEND request rejected: %s (%d)", avdtp_strerror(&err), err.err.error_code); if (sep && sep->cfm && sep->cfm->suspend) sep->cfm->suspend(session, sep, stream, &err, sep->user_data); return TRUE; case AVDTP_CLOSE: if (!stream_rej_to_err(buf, size, &err, &acp_seid)) return FALSE; error("CLOSE request rejected: %s (%d)", avdtp_strerror(&err), err.err.error_code); if (sep && sep->cfm && sep->cfm->close) { sep->cfm->close(session, sep, stream, &err, sep->user_data); stream->close_int = FALSE; } return TRUE; case AVDTP_ABORT: if (!stream_rej_to_err(buf, size, &err, &acp_seid)) return FALSE; error("ABORT request rejected: %s (%d)", avdtp_strerror(&err), err.err.error_code); if (sep && sep->cfm && sep->cfm->abort) sep->cfm->abort(session, sep, stream, &err, sep->user_data); return FALSE; case AVDTP_DELAY_REPORT: if (!stream_rej_to_err(buf, size, &err, &acp_seid)) return FALSE; error("DELAY_REPORT request rejected: %s (%d)", avdtp_strerror(&err), err.err.error_code); if (sep && sep->cfm && sep->cfm->delay_report) sep->cfm->delay_report(session, sep, stream, &err, sep->user_data); return TRUE; default: error("Unknown reject response signal id: %u", signal_id); return TRUE; } } struct avdtp_service_capability *avdtp_stream_get_codec( struct avdtp_stream *stream) { GSList *l; for (l = stream->caps; l; l = l->next) { struct avdtp_service_capability *cap = l->data; if (cap->category == AVDTP_MEDIA_CODEC) return cap; } return NULL; } static gboolean avdtp_stream_has_capability(struct avdtp_stream *stream, struct avdtp_service_capability *cap) { GSList *l; struct avdtp_service_capability *stream_cap; for (l = stream->caps; l; l = g_slist_next(l)) { stream_cap = l->data; if (stream_cap->category != cap->category || stream_cap->length != cap->length) continue; if (memcmp(stream_cap->data, cap->data, cap->length) == 0) return TRUE; } return FALSE; } gboolean avdtp_stream_has_capabilities(struct avdtp_stream *stream, GSList *caps) { for (; caps; caps = g_slist_next(caps)) { struct avdtp_service_capability *cap = caps->data; if (!avdtp_stream_has_capability(stream, cap)) return FALSE; } return TRUE; } gboolean avdtp_stream_has_delay_reporting(struct avdtp_stream *stream) { return stream->delay_reporting; } struct avdtp_remote_sep *avdtp_stream_get_remote_sep( struct avdtp_stream *stream) { GSList *l; for (l = stream->session->seps; l; l = l->next) { struct avdtp_remote_sep *sep = l->data; if (sep->seid == stream->rseid) return sep; } return NULL; } gboolean avdtp_stream_set_transport(struct avdtp_stream *stream, int fd, size_t imtu, size_t omtu) { GIOChannel *io = g_io_channel_unix_new(fd); if (stream != stream->session->pending_open) { uint8_t err; if (stream->session->pending_open) goto failed; /* Attempt to Open there is no pending stream set yet */ if (stream->lsep->ind && stream->lsep->ind->open) { if (!stream->lsep->ind->open(stream->session, stream->lsep, stream, &err, stream->lsep->user_data)) goto failed; } stream_set_pending_open(stream, io); stream->imtu = imtu; stream->omtu = omtu; return TRUE; } handle_transport_connect(stream->session, io, imtu, omtu); g_io_channel_unref(io); return TRUE; failed: g_io_channel_unref(io); return FALSE; } gboolean avdtp_stream_get_transport(struct avdtp_stream *stream, int *sock, uint16_t *imtu, uint16_t *omtu, GSList **caps) { if (stream->io == NULL) return FALSE; if (sock) *sock = g_io_channel_unix_get_fd(stream->io); if (omtu) *omtu = stream->omtu; if (imtu) *imtu = stream->imtu; if (caps) *caps = stream->caps; return TRUE; } static int process_queue(struct avdtp *session) { GSList **queue, *l; struct pending_req *req; if (session->req) return 0; if (session->prio_queue) queue = &session->prio_queue; else queue = &session->req_queue; if (!*queue) return 0; l = *queue; req = l->data; *queue = g_slist_remove(*queue, req); return send_req(session, FALSE, req); } uint8_t avdtp_get_seid(struct avdtp_remote_sep *sep) { return sep->seid; } uint8_t avdtp_get_type(struct avdtp_remote_sep *sep) { return sep->type; } struct avdtp_service_capability *avdtp_get_codec(struct avdtp_remote_sep *sep) { return sep->codec; } bool avdtp_get_delay_reporting(struct avdtp_remote_sep *sep) { return sep->delay_reporting; } struct avdtp_service_capability *avdtp_service_cap_new(uint8_t category, void *data, int length) { struct avdtp_service_capability *cap; if (category < AVDTP_MEDIA_TRANSPORT || category > AVDTP_DELAY_REPORTING) return NULL; cap = g_malloc(sizeof(struct avdtp_service_capability) + length); cap->category = category; cap->length = length; if (data) memcpy(cap->data, data, length); return cap; } struct avdtp_remote_sep *avdtp_register_remote_sep(struct avdtp *session, uint8_t seid, uint8_t type, GSList *caps, bool delay_reporting) { struct avdtp_remote_sep *sep; GSList *l; sep = find_remote_sep(session->seps, seid); if (sep) return sep; sep = g_new0(struct avdtp_remote_sep, 1); session->seps = g_slist_append(session->seps, sep); sep->seid = seid; sep->type = type; sep->media_type = AVDTP_MEDIA_TYPE_AUDIO; sep->caps = caps; sep->delay_reporting = delay_reporting; for (l = caps; l; l = g_slist_next(l)) { struct avdtp_service_capability *cap = l->data; if (cap->category == AVDTP_MEDIA_CODEC) sep->codec = cap; } DBG("seid %d type %d media %d delay_reporting %s", sep->seid, sep->type, sep->media_type, sep->delay_reporting ? "true" : "false"); return sep; } int avdtp_unregister_remote_sep(struct avdtp *session, struct avdtp_remote_sep *rsep) { if (!session || !rsep) return -EINVAL; session->seps = g_slist_remove(session->seps, rsep); sep_free(rsep); return 0; } void avdtp_remote_sep_set_destroy(struct avdtp_remote_sep *sep, void *user_data, avdtp_remote_sep_destroy_t destroy) { if (!sep) return; sep->user_data = user_data; sep->destroy = destroy; } static gboolean process_discover(gpointer data) { struct avdtp *session = data; session->discover->id = 0; finalize_discovery(session, 0); return FALSE; } int avdtp_discover(struct avdtp *session, avdtp_discover_cb_t cb, void *user_data) { int err; if (session->discover) return -EBUSY; session->discover = g_new0(struct discover_callback, 1); if (session->seps) { struct avdtp_remote_sep *sep = session->seps->data; /* Check that SEP have been discovered as it may be loaded from * cache. */ if (sep->discovered) { session->discover->cb = cb; session->discover->user_data = user_data; session->discover->id = g_idle_add(process_discover, session); return 0; } } err = send_request(session, FALSE, NULL, AVDTP_DISCOVER, NULL, 0); if (err == 0) { session->discover->cb = cb; session->discover->user_data = user_data; } else if (session->discover) { g_free(session->discover); session->discover = NULL; } return err; } gboolean avdtp_stream_remove_cb(struct avdtp *session, struct avdtp_stream *stream, unsigned int id) { GSList *l; struct stream_callback *cb; if (!stream) return FALSE; for (cb = NULL, l = stream->callbacks; l != NULL; l = l->next) { struct stream_callback *tmp = l->data; if (tmp && tmp->id == id) { cb = tmp; break; } } if (!cb) return FALSE; stream->callbacks = g_slist_remove(stream->callbacks, cb); g_free(cb); return TRUE; } unsigned int avdtp_stream_add_cb(struct avdtp *session, struct avdtp_stream *stream, avdtp_stream_state_cb cb, void *data) { struct stream_callback *stream_cb; static unsigned int id = 0; stream_cb = g_new(struct stream_callback, 1); stream_cb->cb = cb; stream_cb->user_data = data; stream_cb->id = ++id; stream->callbacks = g_slist_append(stream->callbacks, stream_cb); return stream_cb->id; } int avdtp_get_configuration(struct avdtp *session, struct avdtp_stream *stream) { struct seid_req req; if (session->state < AVDTP_SESSION_STATE_CONNECTED) return -EINVAL; memset(&req, 0, sizeof(req)); req.acp_seid = stream->rseid; return send_request(session, FALSE, stream, AVDTP_GET_CONFIGURATION, &req, sizeof(req)); } static void copy_capabilities(gpointer data, gpointer user_data) { struct avdtp_service_capability *src_cap = data; struct avdtp_service_capability *dst_cap; GSList **l = user_data; dst_cap = avdtp_service_cap_new(src_cap->category, src_cap->data, src_cap->length); *l = g_slist_append(*l, dst_cap); } int avdtp_set_configuration(struct avdtp *session, struct avdtp_remote_sep *rsep, struct avdtp_local_sep *lsep, GSList *caps, struct avdtp_stream **stream) { struct setconf_req *req; struct avdtp_stream *new_stream; unsigned char *ptr; int err, caps_len; struct avdtp_service_capability *cap; GSList *l; if (session->state != AVDTP_SESSION_STATE_CONNECTED) return -ENOTCONN; if (!(lsep && rsep)) return -EINVAL; if (lsep->stream) return -EBUSY; DBG("%p: int_seid=%u, acp_seid=%u", session, lsep->info.seid, rsep->seid); new_stream = g_new0(struct avdtp_stream, 1); new_stream->session = session; new_stream->lsep = lsep; new_stream->rseid = rsep->seid; if (rsep->delay_reporting && lsep->delay_reporting) { struct avdtp_service_capability *delay_reporting; delay_reporting = avdtp_service_cap_new(AVDTP_DELAY_REPORTING, NULL, 0); caps = g_slist_append(caps, delay_reporting); new_stream->delay_reporting = TRUE; } g_slist_foreach(caps, copy_capabilities, &new_stream->caps); /* Calculate total size of request */ for (l = caps, caps_len = 0; l != NULL; l = g_slist_next(l)) { cap = l->data; caps_len += cap->length + 2; } req = g_malloc0(sizeof(struct setconf_req) + caps_len); req->int_seid = lsep->info.seid; req->acp_seid = rsep->seid; /* Copy the capabilities into the request */ for (l = caps, ptr = req->caps; l != NULL; l = g_slist_next(l)) { cap = l->data; memcpy(ptr, cap, cap->length + 2); ptr += cap->length + 2; } err = send_request(session, FALSE, new_stream, AVDTP_SET_CONFIGURATION, req, sizeof(struct setconf_req) + caps_len); if (err < 0) stream_free(new_stream); else { lsep->info.inuse = 1; lsep->stream = new_stream; rsep->stream = new_stream; session->streams = g_slist_append(session->streams, new_stream); if (stream) *stream = new_stream; session->dc_timeout = DISCONNECT_TIMEOUT; } g_free(req); return err; } int avdtp_open(struct avdtp *session, struct avdtp_stream *stream) { struct seid_req req; if (!g_slist_find(session->streams, stream)) return -EINVAL; if (stream->lsep->state > AVDTP_STATE_CONFIGURED) return -EINVAL; memset(&req, 0, sizeof(req)); req.acp_seid = stream->rseid; return send_request(session, FALSE, stream, AVDTP_OPEN, &req, sizeof(req)); } static bool start_timeout(gpointer user_data) { struct avdtp_stream *stream = user_data; struct avdtp *session = stream->session; stream->open_acp = FALSE; if (avdtp_start(session, stream) < 0) error("wait_timeout: avdtp_start failed"); stream->start_timer = 0; return FALSE; } int avdtp_start(struct avdtp *session, struct avdtp_stream *stream) { struct start_req req; int ret; if (!g_slist_find(session->streams, stream)) return -EINVAL; if (stream->lsep->state != AVDTP_STATE_OPEN) return -EINVAL; /* Recommendation 12: * If the RD has configured and opened a stream it is also responsible * to start the streaming via GAVDP_START. */ if (stream->open_acp) { /* If timer already active wait it */ if (stream->start_timer) return 0; stream->start_timer = timeout_add_seconds(START_TIMEOUT, start_timeout, stream, NULL); return 0; } if (stream->close_int == TRUE) { error("avdtp_start: rejecting start since close is initiated"); return -EINVAL; } if (stream->starting == TRUE) { DBG("stream already started"); return -EINPROGRESS; } memset(&req, 0, sizeof(req)); req.required->seid = stream->rseid; ret = send_request(session, FALSE, stream, AVDTP_START, &req, sizeof(req)); if (ret == 0) stream->starting = TRUE; return ret; } int avdtp_close(struct avdtp *session, struct avdtp_stream *stream, gboolean immediate) { struct seid_req req; int ret; if (!g_slist_find(session->streams, stream)) return -EINVAL; if (stream->lsep->state < AVDTP_STATE_OPEN) return -EINVAL; if (stream->close_int == TRUE) { error("avdtp_close: rejecting since close is already initiated"); return -EINVAL; } if (immediate && session->req && stream == session->req->stream) return avdtp_abort(session, stream); memset(&req, 0, sizeof(req)); req.acp_seid = stream->rseid; ret = send_request(session, FALSE, stream, AVDTP_CLOSE, &req, sizeof(req)); if (ret == 0) { stream->close_int = TRUE; session->dc_timeout = 0; } return ret; } int avdtp_suspend(struct avdtp *session, struct avdtp_stream *stream) { struct seid_req req; if (!g_slist_find(session->streams, stream)) return -EINVAL; if (stream->lsep->state <= AVDTP_STATE_OPEN || stream->close_int) return -EINVAL; memset(&req, 0, sizeof(req)); req.acp_seid = stream->rseid; return send_request(session, FALSE, stream, AVDTP_SUSPEND, &req, sizeof(req)); } int avdtp_abort(struct avdtp *session, struct avdtp_stream *stream) { struct seid_req req; int ret; if (!stream && session->discover) { /* Don't call cb since it being aborted */ session->discover->cb = NULL; finalize_discovery(session, ECANCELED); return -EALREADY; } if (!g_slist_find(session->streams, stream)) return -EINVAL; if (stream->lsep->state == AVDTP_STATE_ABORTING) return -EINVAL; avdtp_sep_set_state(session, stream->lsep, AVDTP_STATE_ABORTING); if (session->req && stream == session->req->stream) return cancel_request(session, ECANCELED); memset(&req, 0, sizeof(req)); req.acp_seid = stream->rseid; ret = send_request(session, TRUE, stream, AVDTP_ABORT, &req, sizeof(req)); if (ret == 0) { stream->abort_int = TRUE; session->dc_timeout = 0; } return ret; } int avdtp_delay_report(struct avdtp *session, struct avdtp_stream *stream, uint16_t delay) { struct delay_req req; if (!g_slist_find(session->streams, stream)) return -EINVAL; if (stream->lsep->state != AVDTP_STATE_CONFIGURED && stream->lsep->state != AVDTP_STATE_OPEN && stream->lsep->state != AVDTP_STATE_STREAMING) return -EINVAL; if (!stream->delay_reporting || session->version < 0x0103) return -ENOTSUP; stream->delay = delay; memset(&req, 0, sizeof(req)); req.acp_seid = stream->rseid; req.delay = htons(delay); return send_request(session, TRUE, stream, AVDTP_DELAY_REPORT, &req, sizeof(req)); } struct avdtp_local_sep *avdtp_register_sep(struct queue *lseps, uint64_t *seid_pool, uint8_t type, uint8_t media_type, uint8_t codec_type, gboolean delay_reporting, struct avdtp_sep_ind *ind, struct avdtp_sep_cfm *cfm, void *user_data) { struct avdtp_local_sep *sep; uint8_t seid = util_get_uid(seid_pool, MAX_SEID); if (!seid) return NULL; sep = g_new0(struct avdtp_local_sep, 1); sep->state = AVDTP_STATE_IDLE; sep->info.seid = seid; sep->info.type = type; sep->info.media_type = media_type; sep->codec = codec_type; sep->ind = ind; sep->cfm = cfm; sep->user_data = user_data; sep->delay_reporting = delay_reporting; DBG("SEP %p registered: type:%d codec:%d seid_pool:%p seid:%d", sep, sep->info.type, sep->codec, seid_pool, sep->info.seid); if (!queue_push_tail(lseps, sep)) { g_free(sep); util_clear_uid(seid_pool, seid); return NULL; } return sep; } int avdtp_unregister_sep(struct queue *lseps, uint64_t *seid_pool, struct avdtp_local_sep *sep) { if (!sep) return -EINVAL; if (sep->stream) release_stream(sep->stream, sep->stream->session); DBG("SEP %p unregistered: type:%d codec:%d seid_pool:%p seid:%d", sep, sep->info.type, sep->codec, seid_pool, sep->info.seid); util_clear_uid(seid_pool, sep->info.seid); queue_remove(lseps, sep); g_free(sep); return 0; } const char *avdtp_strerror(struct avdtp_error *err) { if (err->category == AVDTP_ERRNO) return strerror(err->err.posix_errno); switch(err->err.error_code) { case AVDTP_BAD_HEADER_FORMAT: return "Bad Header Format"; case AVDTP_BAD_LENGTH: return "Bad Packet Length"; case AVDTP_BAD_ACP_SEID: return "Bad Acceptor SEID"; case AVDTP_SEP_IN_USE: return "Stream End Point in Use"; case AVDTP_SEP_NOT_IN_USE: return "Stream End Point Not in Use"; case AVDTP_BAD_SERV_CATEGORY: return "Bad Service Category"; case AVDTP_BAD_PAYLOAD_FORMAT: return "Bad Payload format"; case AVDTP_NOT_SUPPORTED_COMMAND: return "Command Not Supported"; case AVDTP_INVALID_CAPABILITIES: return "Invalid Capabilities"; case AVDTP_BAD_RECOVERY_TYPE: return "Bad Recovery Type"; case AVDTP_BAD_MEDIA_TRANSPORT_FORMAT: return "Bad Media Transport Format"; case AVDTP_BAD_RECOVERY_FORMAT: return "Bad Recovery Format"; case AVDTP_BAD_ROHC_FORMAT: return "Bad Header Compression Format"; case AVDTP_BAD_CP_FORMAT: return "Bad Content Protection Format"; case AVDTP_BAD_MULTIPLEXING_FORMAT: return "Bad Multiplexing Format"; case AVDTP_UNSUPPORTED_CONFIGURATION: return "Configuration not supported"; case AVDTP_BAD_STATE: return "Bad State"; default: return "Unknown error"; } } avdtp_state_t avdtp_sep_get_state(struct avdtp_local_sep *sep) { return sep->state; } uint8_t avdtp_sep_get_seid(struct avdtp_local_sep *sep) { return sep->info.seid; } struct btd_adapter *avdtp_get_adapter(struct avdtp *session) { if (!session) return NULL; return device_get_adapter(session->device); } struct btd_device *avdtp_get_device(struct avdtp *session) { if (!session) return NULL; return session->device; } gboolean avdtp_has_stream(struct avdtp *session, struct avdtp_stream *stream) { if (!session || !stream) return FALSE; return g_slist_find(session->streams, stream) ? TRUE : FALSE; } unsigned int avdtp_add_state_cb(struct btd_device *dev, avdtp_session_state_cb cb, void *user_data) { struct avdtp_state_callback *state_cb; static unsigned int id = 0; state_cb = g_new(struct avdtp_state_callback, 1); state_cb->cb = cb; state_cb->dev = dev; state_cb->id = ++id; state_cb->user_data = user_data; state_callbacks = g_slist_append(state_callbacks, state_cb); return state_cb->id; } gboolean avdtp_remove_state_cb(unsigned int id) { GSList *l; for (l = state_callbacks; l != NULL; l = l->next) { struct avdtp_state_callback *cb = l->data; if (cb && cb->id == id) { state_callbacks = g_slist_remove(state_callbacks, cb); g_free(cb); return TRUE; } } return FALSE; } bluez-5.82/profiles/audio/PaxHeaders/control.h0000644000000000000000000000005014015011623016414 xustar0020 atime=1743516536 20 ctime=1743591284 bluez-5.82/profiles/audio/control.h0000644000000000000000000000120714015011623016075 0ustar00rootroot/* SPDX-License-Identifier: GPL-2.0-or-later */ /* * * BlueZ - Bluetooth protocol stack for Linux * * Copyright (C) 2006-2010 Nokia Corporation * Copyright (C) 2004-2010 Marcel Holtmann * * */ #define AUDIO_CONTROL_INTERFACE "org.bluez.MediaControl1" struct btd_service; int control_init_target(struct btd_service *service); int control_init_remote(struct btd_service *service); void control_unregister(struct btd_service *service); int control_connect(struct btd_service *service); int control_disconnect(struct btd_service *service); int control_set_player(struct btd_service *service, const char *path); bluez-5.82/profiles/PaxHeaders/midi0000644000000000000000000000005014773213564014352 xustar0020 atime=1743591291 20 ctime=1743591284 bluez-5.82/profiles/midi/0000755000000000000000000000000014773213564014110 5ustar00rootrootbluez-5.82/profiles/midi/PaxHeaders/midi.c0000644000000000000000000000005014471706460015512 xustar0020 atime=1743516870 20 ctime=1743591284 bluez-5.82/profiles/midi/midi.c0000644000000000000000000003116014471706460015174 0ustar00rootroot// SPDX-License-Identifier: GPL-2.0-or-later /* * * BlueZ - Bluetooth protocol stack for Linux * * Copyright (C) 2015,2016 Felipe F. Tonello * Copyright (C) 2016 ROLI Ltd. * */ #ifdef HAVE_CONFIG_H #include #endif #include #include #include "lib/bluetooth.h" #include "lib/sdp.h" #include "lib/uuid.h" #include "src/plugin.h" #include "src/adapter.h" #include "src/device.h" #include "src/profile.h" #include "src/service.h" #include "src/shared/util.h" #include "src/shared/att.h" #include "src/shared/queue.h" #include "src/shared/gatt-db.h" #include "src/shared/gatt-client.h" #include "src/shared/io.h" #include "src/log.h" #include "attrib/att.h" #include "libmidi.h" struct midi { struct btd_device *dev; struct gatt_db *db; struct bt_gatt_client *client; unsigned int io_cb_id; struct io *io; uint16_t midi_io_handle; /* ALSA handlers */ snd_seq_t *seq_handle; int seq_client_id; int seq_port_id; /* MIDI parser*/ struct midi_read_parser midi_in; struct midi_write_parser midi_out; }; static void foreach_cb(const struct midi_write_parser *parser, void *user_data) { struct midi *midi = user_data; bt_gatt_client_write_without_response(midi->client, midi->midi_io_handle, false, midi_write_data(parser), midi_write_data_size(parser)); } static bool midi_write_cb(struct io *io, void *user_data) { struct midi *midi = user_data; int err; do { snd_seq_event_t *event = NULL; err = snd_seq_event_input(midi->seq_handle, &event); if (err < 0 || !event) break; midi_read_ev(&midi->midi_out, event, foreach_cb, midi); } while (err > 0); if (midi_write_has_data(&midi->midi_out)) bt_gatt_client_write_without_response(midi->client, midi->midi_io_handle, false, midi_write_data(&midi->midi_out), midi_write_data_size(&midi->midi_out)); midi_write_reset(&midi->midi_out); return true; } static void midi_io_value_cb(uint16_t value_handle, const uint8_t *value, uint16_t length, void *user_data) { struct midi *midi = user_data; snd_seq_event_t ev; unsigned int i = 0; if (length < 3) { warn("MIDI I/O: Wrong packet format: length is %u bytes but it should " "be at least 3 bytes", length); return; } snd_seq_ev_clear(&ev); snd_seq_ev_set_source(&ev, midi->seq_port_id); snd_seq_ev_set_subs(&ev); snd_seq_ev_set_direct(&ev); midi_read_reset(&midi->midi_in); while (i < length) { size_t count = midi_read_raw(&midi->midi_in, value + i, length - i, &ev); if (count == 0) goto _err; if (ev.type != SND_SEQ_EVENT_NONE) snd_seq_event_output_direct(midi->seq_handle, &ev); i += count; } return; _err: error("Wrong BLE-MIDI message"); } static void midi_io_ccc_written_cb(uint16_t att_ecode, void *user_data) { if (att_ecode != 0) { error("MIDI I/O: notifications not enabled %s", att_ecode2str(att_ecode)); return; } DBG("MIDI I/O: notification enabled"); } static void midi_io_initial_read_cb(bool success, uint8_t att_ecode, const uint8_t *value, uint16_t length, void *user_data) { struct midi *midi = user_data; if (!success) { error("MIDI I/O: Failed to read initial request"); return; } /* request notify */ midi->io_cb_id = bt_gatt_client_register_notify(midi->client, midi->midi_io_handle, midi_io_ccc_written_cb, midi_io_value_cb, midi, NULL); } static void handle_midi_io(struct midi *midi, uint16_t value_handle) { DBG("MIDI I/O handle: 0x%04x", value_handle); midi->midi_io_handle = value_handle; /* * The BLE-MIDI 1.0 spec specifies that The Central shall attempt to * read the MIDI I/O characteristic of the Peripheral right after * estrablhishing a connection with the accessory. */ if (!bt_gatt_client_read_value(midi->client, value_handle, midi_io_initial_read_cb, midi, NULL)) DBG("MIDI I/O: Failed to send request to read initial value"); } static void handle_characteristic(struct gatt_db_attribute *attr, void *user_data) { struct midi *midi = user_data; uint16_t value_handle; bt_uuid_t uuid, midi_io_uuid; if (!gatt_db_attribute_get_char_data(attr, NULL, &value_handle, NULL, NULL, &uuid)) { error("Failed to obtain characteristic data"); return; } bt_string_to_uuid(&midi_io_uuid, MIDI_IO_UUID); if (bt_uuid_cmp(&midi_io_uuid, &uuid) == 0) handle_midi_io(midi, value_handle); else { char uuid_str[MAX_LEN_UUID_STR]; bt_uuid_to_string(&uuid, uuid_str, sizeof(uuid_str)); DBG("Unsupported characteristic: %s", uuid_str); } } static void foreach_midi_service(struct gatt_db_attribute *attr, void *user_data) { struct midi *midi = user_data; gatt_db_service_foreach_char(attr, handle_characteristic, midi); } static int midi_device_probe(struct btd_service *service) { struct btd_device *device = btd_service_get_device(service); struct midi *midi; char addr[18]; ba2str(device_get_address(device), addr); DBG("MIDI GATT Driver profile probe (%s)", addr); /* Ignore, if we were probed for this device already */ midi = btd_service_get_user_data(service); if (midi) { error("Profile probed twice for the same device!"); return -EADDRINUSE; } midi = g_new0(struct midi, 1); if (!midi) return -ENOMEM; midi->dev = btd_device_ref(device); btd_service_set_user_data(service, midi); return 0; } static void midi_device_remove(struct btd_service *service) { struct btd_device *device = btd_service_get_device(service); struct midi *midi; char addr[18]; ba2str(device_get_address(device), addr); DBG("MIDI GATT Driver profile remove (%s)", addr); midi = btd_service_get_user_data(service); if (!midi) { error("MIDI Service not handled by profile"); return; } if (midi->seq_handle) { midi_read_free(&midi->midi_in); midi_write_free(&midi->midi_out); io_destroy(midi->io); snd_seq_delete_simple_port(midi->seq_handle, midi->seq_port_id); midi->seq_port_id = 0; snd_seq_close(midi->seq_handle); } btd_device_unref(midi->dev); g_free(midi); } static int midi_accept(struct btd_service *service) { struct btd_device *device = btd_service_get_device(service); struct gatt_db *db = btd_device_get_gatt_db(device); struct bt_gatt_client *client = btd_device_get_gatt_client(device); bt_uuid_t midi_uuid; struct pollfd pfd; struct midi *midi; char addr[18]; char device_name[MAX_NAME_LENGTH + 11]; /* 11 = " Bluetooth\0"*/ int err; snd_seq_client_info_t *info; ba2str(device_get_address(device), addr); DBG("MIDI GATT Driver profile accept (%s)", addr); midi = btd_service_get_user_data(service); if (!midi) { error("MIDI Service not handled by profile"); return -ENODEV; } /* Port Name */ memset(device_name, 0, sizeof(device_name)); if (device_name_known(device)) device_get_name(device, device_name, sizeof(device_name)); else strncpy(device_name, addr, sizeof(device_name)); /* ALSA Sequencer Client and Port Setup */ err = snd_seq_open(&midi->seq_handle, "default", SND_SEQ_OPEN_DUPLEX, 0); if (err < 0) { error("Could not open ALSA Sequencer: %s (%d)", snd_strerror(err), err); return err; } err = snd_seq_nonblock(midi->seq_handle, SND_SEQ_NONBLOCK); if (err < 0) { error("Could not set nonblock mode: %s (%d)", snd_strerror(err), err); goto _err_handle; } err = snd_seq_set_client_name(midi->seq_handle, device_name); if (err < 0) { error("Could not configure ALSA client: %s (%d)", snd_strerror(err), err); goto _err_handle; } err = snd_seq_client_id(midi->seq_handle); if (err < 0) { error("Could retreive ALSA client: %s (%d)", snd_strerror(err), err); goto _err_handle; } midi->seq_client_id = err; err = snd_seq_create_simple_port(midi->seq_handle, strcat(device_name, " Bluetooth"), SND_SEQ_PORT_CAP_READ | SND_SEQ_PORT_CAP_WRITE | SND_SEQ_PORT_CAP_SUBS_READ | SND_SEQ_PORT_CAP_SUBS_WRITE, SND_SEQ_PORT_TYPE_MIDI_GENERIC | SND_SEQ_PORT_TYPE_HARDWARE); if (err < 0) { error("Could not create ALSA port: %s (%d)", snd_strerror(err), err); goto _err_handle; } midi->seq_port_id = err; snd_seq_client_info_alloca(&info); err = snd_seq_get_client_info(midi->seq_handle, info); if (err < 0) goto _err_port; /* list of relevant sequencer events */ snd_seq_client_info_event_filter_add(info, SND_SEQ_EVENT_NOTEOFF); snd_seq_client_info_event_filter_add(info, SND_SEQ_EVENT_NOTEON); snd_seq_client_info_event_filter_add(info, SND_SEQ_EVENT_KEYPRESS); snd_seq_client_info_event_filter_add(info, SND_SEQ_EVENT_CONTROLLER); snd_seq_client_info_event_filter_add(info, SND_SEQ_EVENT_PGMCHANGE); snd_seq_client_info_event_filter_add(info, SND_SEQ_EVENT_CHANPRESS); snd_seq_client_info_event_filter_add(info, SND_SEQ_EVENT_PITCHBEND); snd_seq_client_info_event_filter_add(info, SND_SEQ_EVENT_SYSEX); snd_seq_client_info_event_filter_add(info, SND_SEQ_EVENT_QFRAME); snd_seq_client_info_event_filter_add(info, SND_SEQ_EVENT_SONGPOS); snd_seq_client_info_event_filter_add(info, SND_SEQ_EVENT_SONGSEL); snd_seq_client_info_event_filter_add(info, SND_SEQ_EVENT_TUNE_REQUEST); snd_seq_client_info_event_filter_add(info, SND_SEQ_EVENT_CLOCK); snd_seq_client_info_event_filter_add(info, SND_SEQ_EVENT_START); snd_seq_client_info_event_filter_add(info, SND_SEQ_EVENT_CONTINUE); snd_seq_client_info_event_filter_add(info, SND_SEQ_EVENT_STOP); snd_seq_client_info_event_filter_add(info, SND_SEQ_EVENT_SENSING); snd_seq_client_info_event_filter_add(info, SND_SEQ_EVENT_RESET); snd_seq_client_info_event_filter_add(info, SND_SEQ_EVENT_CONTROL14); snd_seq_client_info_event_filter_add(info, SND_SEQ_EVENT_NONREGPARAM); snd_seq_client_info_event_filter_add(info, SND_SEQ_EVENT_REGPARAM); err = snd_seq_set_client_info(midi->seq_handle, info); if (err < 0) goto _err_port; /* Input file descriptors */ snd_seq_poll_descriptors(midi->seq_handle, &pfd, 1, POLLIN); midi->io = io_new(pfd.fd); if (!midi->io) { error("Could not allocate I/O eventloop"); goto _err_port; } io_set_read_handler(midi->io, midi_write_cb, midi, NULL); midi->db = gatt_db_ref(db); midi->client = bt_gatt_client_ref(client); err = midi_read_init(&midi->midi_in); if (err < 0) { error("Could not initialise MIDI input parser"); goto _err_port; } err = midi_write_init(&midi->midi_out, bt_gatt_client_get_mtu(midi->client) - 3); if (err < 0) { error("Could not initialise MIDI output parser"); goto _err_midi; } bt_string_to_uuid(&midi_uuid, MIDI_UUID); gatt_db_foreach_service(db, &midi_uuid, foreach_midi_service, midi); btd_service_connecting_complete(service, 0); return 0; _err_midi: midi_read_free(&midi->midi_in); _err_port: snd_seq_delete_simple_port(midi->seq_handle, midi->seq_port_id); _err_handle: snd_seq_close(midi->seq_handle); midi->seq_handle = NULL; btd_service_connecting_complete(service, err); return err; } static int midi_disconnect(struct btd_service *service) { struct btd_device *device = btd_service_get_device(service); struct midi *midi; char addr[18]; ba2str(device_get_address(device), addr); DBG("MIDI GATT Driver profile disconnect (%s)", addr); midi = btd_service_get_user_data(service); if (!midi) { error("MIDI Service not handled by profile"); return -ENODEV; } midi_read_free(&midi->midi_in); midi_write_free(&midi->midi_out); io_destroy(midi->io); snd_seq_delete_simple_port(midi->seq_handle, midi->seq_port_id); midi->seq_port_id = 0; snd_seq_close(midi->seq_handle); midi->seq_handle = NULL; /* Clean-up any old client/db */ bt_gatt_client_unregister_notify(midi->client, midi->io_cb_id); bt_gatt_client_unref(midi->client); gatt_db_unref(midi->db); btd_service_disconnecting_complete(service, 0); return 0; } static struct btd_profile midi_profile = { .name = "MIDI GATT Driver", .remote_uuid = MIDI_UUID, .priority = BTD_PROFILE_PRIORITY_HIGH, .auto_connect = true, .device_probe = midi_device_probe, .device_remove = midi_device_remove, .accept = midi_accept, .disconnect = midi_disconnect, }; static int midi_init(void) { return btd_profile_register(&midi_profile); } static void midi_exit(void) { btd_profile_unregister(&midi_profile); } BLUETOOTH_PLUGIN_DEFINE(midi, VERSION, BLUETOOTH_PLUGIN_PRIORITY_HIGH, midi_init, midi_exit); bluez-5.82/profiles/midi/PaxHeaders/libmidi.h0000644000000000000000000000005014015011623016166 xustar0020 atime=1743516870 20 ctime=1743591284 bluez-5.82/profiles/midi/libmidi.h0000644000000000000000000000636714015011623015663 0ustar00rootroot/* SPDX-License-Identifier: GPL-2.0-or-later */ /* * * BlueZ - Bluetooth protocol stack for Linux * * Copyright (C) 2015,2016 Felipe F. Tonello * Copyright (C) 2016 ROLI Ltd. * */ #ifndef LIBMIDI_H #define LIBMIDI_H #include #include #include #define MIDI_UUID "03B80E5A-EDE8-4B33-A751-6CE34EC4C700" #define MIDI_IO_UUID "7772E5DB-3868-4112-A1A9-F2669D106BF3" #define MIDI_MAX_TIMESTAMP 8191 #define MIDI_MSG_MAX_SIZE 12 #define MIDI_SYSEX_MAX_SIZE (4 * 1024) struct midi_buffer { uint8_t *data; size_t len; }; /* MIDI I/O Write parser */ struct midi_write_parser { int64_t rtime; /* last writer's real time */ snd_seq_event_type_t rstatus; /* running status event type */ struct midi_buffer midi_stream; /* MIDI I/O byte stream */ size_t stream_size; /* what is the maximum size of the midi_stream array */ snd_midi_event_t *midi_ev; /* midi<->seq event */ }; int midi_write_init(struct midi_write_parser *parser, size_t buffer_size); static inline void midi_write_free(struct midi_write_parser *parser) { free(parser->midi_stream.data); snd_midi_event_free(parser->midi_ev); } static inline void midi_write_reset(struct midi_write_parser *parser) { parser->rstatus = SND_SEQ_EVENT_NONE; parser->midi_stream.len = 0; } static inline bool midi_write_has_data(const struct midi_write_parser *parser) { return parser->midi_stream.len > 0; } static inline const uint8_t * midi_write_data(const struct midi_write_parser *parser) { return parser->midi_stream.data; } static inline size_t midi_write_data_size(const struct midi_write_parser *parser) { return parser->midi_stream.len; } typedef void (*midi_read_ev_cb)(const struct midi_write_parser *parser, void *); /* It creates BLE-MIDI raw packets from the a sequencer event. If the packet is full, then it calls write_cb and resets its internal state as many times as necessary. */ void midi_read_ev(struct midi_write_parser *parser, const snd_seq_event_t *ev, midi_read_ev_cb write_cb, void *user_data); /* MIDI I/O Read parser */ struct midi_read_parser { uint8_t rstatus; /* running status byte */ int64_t rtime; /* last reader's real time */ int16_t timestamp; /* last MIDI-BLE timestamp */ int8_t timestamp_low; /* MIDI-BLE timestampLow from the current packet */ int8_t timestamp_high; /* MIDI-BLE timestampHigh from the current packet */ struct midi_buffer sysex_stream; /* SysEx stream */ snd_midi_event_t *midi_ev; /* midi<->seq event */ }; int midi_read_init(struct midi_read_parser *parser); static inline void midi_read_free(struct midi_read_parser *parser) { free(parser->sysex_stream.data); snd_midi_event_free(parser->midi_ev); } static inline void midi_read_reset(struct midi_read_parser *parser) { parser->rstatus = 0; parser->timestamp_low = 0; parser->timestamp_high = 0; } /* Parses raw BLE-MIDI messages and populates a sequencer event representing the current MIDI message. It returns how much raw data was processed. */ size_t midi_read_raw(struct midi_read_parser *parser, const uint8_t *data, size_t size, snd_seq_event_t *ev /* OUT */); #endif /* LIBMIDI_H */ bluez-5.82/profiles/midi/PaxHeaders/libmidi.c0000644000000000000000000000005014015011623016161 xustar0020 atime=1743516870 20 ctime=1743591284 bluez-5.82/profiles/midi/libmidi.c0000644000000000000000000003106114015011623015643 0ustar00rootroot// SPDX-License-Identifier: GPL-2.0-or-later /* * * BlueZ - Bluetooth protocol stack for Linux * * Copyright (C) 2015,2016 Felipe F. Tonello * Copyright (C) 2016 ROLI Ltd. * */ #include /* Avoid linkage problem on unit-tests */ #ifndef MIDI_TEST #include "src/backtrace.h" #define MIDI_ASSERT(_expr) btd_assert(_expr) #else #define MIDI_ASSERT(_expr) g_assert(_expr) #endif #include "libmidi.h" inline static void buffer_append_byte(struct midi_buffer *buffer, const uint8_t byte) { buffer->data[buffer->len++] = byte; } inline static void buffer_append_data(struct midi_buffer *buffer, const uint8_t *data, size_t size) { memcpy(buffer->data + buffer->len, data, size); buffer->len += size; } inline static uint8_t buffer_reverse_get(struct midi_buffer *buffer, size_t i) { MIDI_ASSERT(buffer->len > i); return buffer->data[buffer->len - (i + 1)]; } inline static void buffer_reverse_set(struct midi_buffer *buffer, size_t i, const uint8_t byte) { MIDI_ASSERT(buffer->len > i); buffer->data[buffer->len - (i + 1)] = byte; } inline static size_t parser_get_available_size(struct midi_write_parser *parser) { return parser->stream_size - parser->midi_stream.len; } inline static uint8_t sysex_get(const snd_seq_event_t *ev, size_t i) { MIDI_ASSERT(ev->data.ext.len > i); return ((uint8_t*)ev->data.ext.ptr)[i]; } inline static void append_timestamp_high_maybe(struct midi_write_parser *parser) { uint8_t timestamp_high = 0x80; if (midi_write_has_data(parser)) return; /* Make sure timesampt_high is assigned a non-zero value */ do { /* convert µs to ms */ parser->rtime = g_get_monotonic_time() / 1000; timestamp_high |= (parser->rtime & 0x1F80) >> 7; } while (timestamp_high == 0x80); /* set timestampHigh */ buffer_append_byte(&parser->midi_stream, timestamp_high); } inline static void append_timestamp_low(struct midi_write_parser *parser) { const uint8_t timestamp_low = 0x80 | (parser->rtime & 0x7F); buffer_append_byte(&parser->midi_stream, timestamp_low); } int midi_write_init(struct midi_write_parser *parser, size_t buffer_size) { int err; parser->rtime = 0; parser->rstatus = SND_SEQ_EVENT_NONE; parser->stream_size = buffer_size; parser->midi_stream.data = malloc(buffer_size); if (!parser->midi_stream.data) return -ENOMEM; parser->midi_stream.len = 0; err = snd_midi_event_new(buffer_size, &parser->midi_ev); if (err < 0) free(parser->midi_stream.data); return err; } int midi_read_init(struct midi_read_parser *parser) { int err; parser->rstatus = 0; parser->rtime = -1; parser->timestamp = 0; parser->timestamp_low = 0; parser->timestamp_high = 0; parser->sysex_stream.data = malloc(MIDI_SYSEX_MAX_SIZE); if (!parser->sysex_stream.data) return -ENOMEM; parser->sysex_stream.len = 0; err = snd_midi_event_new(MIDI_MSG_MAX_SIZE, &parser->midi_ev); if (err < 0) free(parser->sysex_stream.data); return err; } /* Algorithm: 1) check initial timestampLow: if used_sysex == 0, then tsLow = 1, else tsLow = 0 2) calculate sysex size of current packet: 2a) first check special case: if midi->out_length - 1 (tsHigh) - tsLow == sysex_length - used_sysex size is: min(midi->out_length - 1 - tsLow, sysex_length - used_sysex - 1) 2b) else size is: min(midi->out_length - 1 - tsLow, sysex_length - used_sysex) 3) check if packet contains F7: fill respective tsLow byte */ static void read_ev_sysex(struct midi_write_parser *parser, const snd_seq_event_t *ev, midi_read_ev_cb write_cb, void *user_data) { unsigned int used_sysex = 0; /* We need at least 2 bytes (timestampLow + F0) */ if (parser_get_available_size(parser) < 2) { /* send current message and start new one */ write_cb(parser, user_data); midi_write_reset(parser); append_timestamp_high_maybe(parser); } /* timestampLow on initial F0 */ if (sysex_get(ev, 0) == 0xF0) append_timestamp_low(parser); do { unsigned int size_of_sysex; append_timestamp_high_maybe(parser); size_of_sysex = MIN(parser_get_available_size(parser), ev->data.ext.len - used_sysex); if (parser_get_available_size(parser) == ev->data.ext.len - used_sysex) size_of_sysex--; buffer_append_data(&parser->midi_stream, ev->data.ext.ptr + used_sysex, size_of_sysex); used_sysex += size_of_sysex; if (parser_get_available_size(parser) <= 1 && buffer_reverse_get(&parser->midi_stream, 0) != 0xF7) { write_cb(parser, user_data); midi_write_reset(parser); } } while (used_sysex < ev->data.ext.len); /* check for F7 and update respective timestampLow byte */ if (midi_write_has_data(parser) && buffer_reverse_get(&parser->midi_stream, 0) == 0xF7) { /* remove 0xF7 from buffer, append timestamp and add 0xF7 back again */ parser->midi_stream.len--; append_timestamp_low(parser); buffer_append_byte(&parser->midi_stream, 0xF7); } } static void read_ev_others(struct midi_write_parser *parser, const snd_seq_event_t *ev, midi_read_ev_cb write_cb, void *user_data) { int length; /* check for running status */ if (parser->rstatus != ev->type) { snd_midi_event_reset_decode(parser->midi_ev); append_timestamp_low(parser); } /* each midi message has timestampLow byte to follow */ length = snd_midi_event_decode(parser->midi_ev, parser->midi_stream.data + parser->midi_stream.len, parser_get_available_size(parser), ev); if (length == -ENOMEM) { /* remove previously added timestampLow */ if (parser->rstatus != ev->type) parser->midi_stream.len--; write_cb(parser, user_data); /* cleanup state for next packet */ snd_midi_event_reset_decode(parser->midi_ev); midi_write_reset(parser); append_timestamp_high_maybe(parser); append_timestamp_low(parser); length = snd_midi_event_decode(parser->midi_ev, parser->midi_stream.data + parser->midi_stream.len, parser_get_available_size(parser), ev); } if (length > 0) parser->midi_stream.len += length; } void midi_read_ev(struct midi_write_parser *parser, const snd_seq_event_t *ev, midi_read_ev_cb write_cb, void *user_data) { MIDI_ASSERT(write_cb); append_timestamp_high_maybe(parser); /* SysEx is special case: SysEx has two timestampLow bytes, before F0 and F7 */ if (ev->type == SND_SEQ_EVENT_SYSEX) read_ev_sysex(parser, ev, write_cb, user_data); else read_ev_others(parser, ev, write_cb, user_data); parser->rstatus = ev->type; if (parser_get_available_size(parser) == 0) { write_cb(parser, user_data); midi_write_reset(parser); } } static void update_ev_timestamp(struct midi_read_parser *parser, snd_seq_event_t *ev, uint16_t ts_low) { int delta_timestamp; int delta_rtime; int64_t rtime_current; uint16_t timestamp; /* time_low overwflow results on time_high to increment by one */ if (parser->timestamp_low > ts_low) parser->timestamp_high++; timestamp = (parser->timestamp_high << 7) | parser->timestamp_low; rtime_current = g_get_monotonic_time() / 1000; /* convert µs to ms */ delta_timestamp = timestamp - (int)parser->timestamp; delta_rtime = rtime_current - parser->rtime; if (delta_rtime > MIDI_MAX_TIMESTAMP) parser->rtime = rtime_current; else { /* If delta_timestamp is way to big than delta_rtime, this means that the device sent a message in the past, so we have to compensate for this. */ if (delta_timestamp > 7000 && delta_rtime < 1000) delta_timestamp = 0; /* check if timestamp did overflow */ if (delta_timestamp < 0) { /* same timestamp in the past problem */ if ((delta_timestamp + MIDI_MAX_TIMESTAMP) > 7000 && delta_rtime < 1000) delta_timestamp = 0; else delta_timestamp = delta_timestamp + MIDI_MAX_TIMESTAMP; } parser->rtime += delta_timestamp; } parser->timestamp += delta_timestamp; if (parser->timestamp > MIDI_MAX_TIMESTAMP) parser->timestamp %= MIDI_MAX_TIMESTAMP + 1; /* set event timestamp */ /* TODO: update event timestamp here! */ } static size_t handle_end_of_sysex(struct midi_read_parser *parser, snd_seq_event_t *ev, const uint8_t *data, size_t sysex_length) { uint8_t time_low; /* At this time, timestampLow is copied as the last byte, instead of 0xF7 */ buffer_append_data(&parser->sysex_stream, data, sysex_length); time_low = buffer_reverse_get(&parser->sysex_stream, 0) & 0x7F; /* Remove timestamp byte */ buffer_reverse_set(&parser->sysex_stream, 0, 0xF7); /* Update event */ update_ev_timestamp(parser, ev, time_low); snd_seq_ev_set_sysex(ev, parser->sysex_stream.len, parser->sysex_stream.data); return sysex_length + 1; /* +1 because of timestampLow */ } /* Searches the end of a SysEx message that contains a timestampLow * before the SysEx end byte. Returns the number of bytes of valid * SysEx payload in the buffer. */ static size_t sysex_eox_len(const uint8_t *data, size_t size) { size_t i = 0; MIDI_ASSERT(size > 0); if (data[i] == 0xF0) i++; /* Search for timestamp low */ while (i < size) { if ((data[i] & 0x80)) { i++; break; } i++; } return (i < size && data[i] == 0xF7) ? i : 0; } size_t midi_read_raw(struct midi_read_parser *parser, const uint8_t *data, size_t size, snd_seq_event_t *ev /* OUT */) { size_t midi_size = 0; size_t i = 0; bool err = false; if (parser->timestamp_high == 0) parser->timestamp_high = data[i++] & 0x3F; snd_midi_event_reset_encode(parser->midi_ev); /* timestamp byte */ if (data[i] & 0x80) { update_ev_timestamp(parser, ev, data[i] & 0x7F); /* check for wrong BLE-MIDI message size */ if (++i == size) { err = true; goto _finish; } } /* cleanup sysex_stream if message is broken or is a new SysEx */ if (data[i] >= 0x80 && data[i] != 0xF7 && parser->sysex_stream.len > 0) parser->sysex_stream.len = 0; switch (data[i]) { case 0xF8 ... 0XFF: /* System Real-Time Messages */ midi_size = 1; break; /* System Common Messages */ case 0xF0: /* SysEx Start */ { size_t sysex_length; /* cleanup Running Status Message */ parser->rstatus = 0; sysex_length = sysex_eox_len(data + i, size - i); /* Search for End of SysEx message in one BLE message */ if (sysex_length > 0) { midi_size = handle_end_of_sysex(parser, ev, data + i, sysex_length); } else { buffer_append_data(&parser->sysex_stream, data + i, size - i); err = true; /* Not an actual error, just incomplete message */ midi_size = size - i; } goto _finish; } case 0xF1: case 0xF3: midi_size = 2; break; case 0xF2: midi_size = 3; break; case 0xF4: case 0xF5: /* Ignore */ i++; err = true; goto _finish; break; case 0xF6: midi_size = 1; break; case 0xF7: /* SysEx End */ buffer_append_byte(&parser->sysex_stream, 0xF7); snd_seq_ev_set_sysex(ev, parser->sysex_stream.len, parser->sysex_stream.data); midi_size = 1; /* timestampLow was alredy processed */ goto _finish; case 0x80 ... 0xEF: /* * Channel Voice Messages, Channel Mode Messages * and Control Change Messages. */ parser->rstatus = data[i]; midi_size = (data[i] >= 0xC0 && data[i] <= 0xDF) ? 2 : 3; break; case 0x00 ... 0x7F: /* Check for SysEx messages */ if (parser->sysex_stream.len > 0) { size_t sysex_length; sysex_length = sysex_eox_len(data + i, size - i); if (sysex_length > 0) { midi_size = handle_end_of_sysex(parser, ev, data + i, sysex_length); } else { buffer_append_data(&parser->sysex_stream, data + i, size - i); err = true; /* Not an actual error, just incomplete message */ midi_size = size - i; } goto _finish; } /* Running State Message was not set */ if (parser->rstatus == 0) { midi_size = 1; err = true; goto _finish; } snd_midi_event_encode_byte(parser->midi_ev, parser->rstatus, ev); midi_size = (parser->rstatus >= 0xC0 && parser->rstatus <= 0xDF) ? 1 : 2; break; } if ((i + midi_size) > size) { err = true; goto _finish; } snd_midi_event_encode(parser->midi_ev, data + i, midi_size, ev); _finish: if (err) ev->type = SND_SEQ_EVENT_NONE; return i + midi_size; } bluez-5.82/profiles/PaxHeaders/battery0000644000000000000000000000005014773213564015102 xustar0020 atime=1743591291 20 ctime=1743591284 bluez-5.82/profiles/battery/0000755000000000000000000000000014773213564014640 5ustar00rootrootbluez-5.82/profiles/battery/PaxHeaders/battery.c0000644000000000000000000000005014766002272016767 xustar0020 atime=1743515578 20 ctime=1743591284 bluez-5.82/profiles/battery/battery.c0000644000000000000000000001717514766002272016463 0ustar00rootroot// SPDX-License-Identifier: GPL-2.0-or-later /* * * BlueZ - Bluetooth protocol stack for Linux * * Copyright (C) 2012 Instituto Nokia de Tecnologia - INdT * Copyright (C) 2014 Google Inc. * Copyright (C) 2017 Red Hat Inc. * */ #ifdef HAVE_CONFIG_H #include #endif #include #include #include #include #include #include #include #include #include "lib/bluetooth.h" #include "lib/hci.h" #include "lib/sdp.h" #include "lib/uuid.h" #include "src/shared/util.h" #include "src/shared/att.h" #include "src/shared/queue.h" #include "src/shared/gatt-db.h" #include "src/shared/gatt-client.h" #include "src/plugin.h" #include "src/adapter.h" #include "src/device.h" #include "src/profile.h" #include "src/service.h" #include "src/log.h" #include "src/battery.h" #include "attrib/att.h" #define BATTERY_INTERFACE "org.bluez.Battery1" #define BATT_UUID16 0x180f /* Generic Attribute/Access Service */ struct batt { struct btd_battery *battery; struct btd_device *device; struct gatt_db *db; struct bt_gatt_client *client; struct gatt_db_attribute *attr; unsigned int batt_level_cb_id; uint16_t batt_level_io_handle; uint8_t *initial_value; uint8_t percentage; }; static void batt_free(struct batt *batt) { gatt_db_unref(batt->db); bt_gatt_client_unref(batt->client); btd_device_unref(batt->device); free(batt->initial_value); if (batt->battery) btd_battery_unregister(batt->battery); g_free(batt); } static void batt_reset(struct batt *batt) { batt->attr = NULL; batt->percentage = -1; gatt_db_unref(batt->db); batt->db = NULL; bt_gatt_client_unref(batt->client); batt->client = NULL; g_free (batt->initial_value); batt->initial_value = NULL; if (batt->battery) { btd_battery_unregister(batt->battery); batt->battery = NULL; } } static void parse_battery_level(struct batt *batt, const uint8_t *value) { uint8_t percentage; percentage = value[0]; if (batt->percentage != percentage) { batt->percentage = percentage; DBG("Battery Level updated: %d%%", percentage); if (!batt->battery) { warn("Trying to update an unregistered battery"); return; } btd_battery_update(batt->battery, batt->percentage); } } static void batt_io_value_cb(uint16_t value_handle, const uint8_t *value, uint16_t length, void *user_data) { struct batt *batt = user_data; if (value_handle == batt->batt_level_io_handle) { parse_battery_level(batt, value); } else { g_assert_not_reached(); } } static void batt_io_ccc_written_cb(uint16_t att_ecode, void *user_data) { struct batt *batt = user_data; if (att_ecode != 0) { error("Battery Level: notifications not enabled %s", att_ecode2str(att_ecode)); return; } batt->battery = btd_battery_register(device_get_path(batt->device), "GATT Battery Service", NULL); if (!batt->battery) { batt_reset(batt); return; } parse_battery_level(batt, batt->initial_value); g_free (batt->initial_value); batt->initial_value = NULL; DBG("Battery Level: notification enabled"); } static void read_initial_battery_level_cb(bool success, uint8_t att_ecode, const uint8_t *value, uint16_t length, void *user_data) { struct batt *batt = user_data; if (!success) { DBG("Reading battery level failed with ATT errror: %u", att_ecode); return; } if (!length) return; batt->initial_value = util_memdup(value, length); /* request notify */ batt->batt_level_cb_id = bt_gatt_client_register_notify(batt->client, batt->batt_level_io_handle, batt_io_ccc_written_cb, batt_io_value_cb, batt, NULL); } static void handle_battery_level(struct batt *batt, uint16_t value_handle) { batt->batt_level_io_handle = value_handle; if (!bt_gatt_client_read_value(batt->client, batt->batt_level_io_handle, read_initial_battery_level_cb, batt, NULL)) DBG("Failed to send request to read battery level"); } static bool uuid_cmp(uint16_t u16, const bt_uuid_t *uuid) { bt_uuid_t lhs; bt_uuid16_create(&lhs, u16); return bt_uuid_cmp(&lhs, uuid) == 0; } static void handle_characteristic(struct gatt_db_attribute *attr, void *user_data) { struct batt *batt = user_data; uint16_t value_handle; bt_uuid_t uuid; if (!gatt_db_attribute_get_char_data(attr, NULL, &value_handle, NULL, NULL, &uuid)) { error("Failed to obtain characteristic data"); return; } if (uuid_cmp(GATT_CHARAC_BATTERY_LEVEL, &uuid)) { handle_battery_level(batt, value_handle); } else { char uuid_str[MAX_LEN_UUID_STR]; bt_uuid_to_string(&uuid, uuid_str, sizeof(uuid_str)); DBG("Unsupported characteristic: %s", uuid_str); } } static void handle_batt_service(struct batt *batt) { gatt_db_service_foreach_char(batt->attr, handle_characteristic, batt); } static int batt_probe(struct btd_service *service) { struct btd_device *device = btd_service_get_device(service); struct batt *batt = btd_service_get_user_data(service); char addr[18]; ba2str(device_get_address(device), addr); DBG("BATT profile probe (%s)", addr); /* Ignore, if we were probed for this device already */ if (batt) { error("Profile probed twice for the same device!"); return -1; } batt = g_new0(struct batt, 1); if (!batt) return -1; batt->percentage = -1; batt->device = btd_device_ref(device); btd_service_set_user_data(service, batt); return 0; } static void batt_remove(struct btd_service *service) { struct btd_device *device = btd_service_get_device(service); struct batt *batt; char addr[18]; ba2str(device_get_address(device), addr); DBG("BATT profile remove (%s)", addr); batt = btd_service_get_user_data(service); if (!batt) { error("BATT service not handled by profile"); return; } batt_free(batt); } static void foreach_batt_service(struct gatt_db_attribute *attr, void *user_data) { struct batt *batt = user_data; if (batt->attr) { error("More than one BATT service exists for this device"); return; } batt->attr = attr; handle_batt_service(batt); } static int batt_accept(struct btd_service *service) { struct btd_device *device = btd_service_get_device(service); struct gatt_db *db = btd_device_get_gatt_db(device); struct bt_gatt_client *client = btd_device_get_gatt_client(device); struct batt *batt = btd_service_get_user_data(service); char addr[18]; bt_uuid_t batt_uuid; ba2str(device_get_address(device), addr); DBG("BATT profile accept (%s)", addr); if (!batt) { error("BATT service not handled by profile"); return -1; } batt->db = gatt_db_ref(db); batt->client = bt_gatt_client_clone(client); /* Handle the BATT services */ bt_uuid16_create(&batt_uuid, BATT_UUID16); gatt_db_foreach_service(db, &batt_uuid, foreach_batt_service, batt); if (!batt->attr) { error("BATT attribute not found"); batt_reset(batt); return -1; } btd_service_connecting_complete(service, 0); return 0; } static int batt_disconnect(struct btd_service *service) { struct batt *batt = btd_service_get_user_data(service); batt_reset(batt); btd_service_disconnecting_complete(service, 0); return 0; } static struct btd_profile batt_profile = { .name = "batt-profile", .remote_uuid = BATTERY_UUID, .device_probe = batt_probe, .device_remove = batt_remove, .accept = batt_accept, .disconnect = batt_disconnect, }; static int batt_init(void) { return btd_profile_register(&batt_profile); } static void batt_exit(void) { btd_profile_unregister(&batt_profile); } BLUETOOTH_PLUGIN_DEFINE(battery, VERSION, BLUETOOTH_PLUGIN_PRIORITY_DEFAULT, batt_init, batt_exit) bluez-5.82/profiles/battery/PaxHeaders/bas.h0000644000000000000000000000005014015011623016052 xustar0020 atime=1743516562 20 ctime=1743591278 bluez-5.82/profiles/battery/bas.h0000644000000000000000000000064214015011623015535 0ustar00rootroot/* SPDX-License-Identifier: LGPL-2.1-or-later */ /* * * BlueZ - Bluetooth protocol stack for Linux * * Copyright (C) 2014 Intel Corporation. All rights reserved. * * */ struct bt_bas; struct bt_bas *bt_bas_new(void *primary); struct bt_bas *bt_bas_ref(struct bt_bas *bas); void bt_bas_unref(struct bt_bas *bas); bool bt_bas_attach(struct bt_bas *bas, void *gatt); void bt_bas_detach(struct bt_bas *bas); bluez-5.82/profiles/battery/PaxHeaders/bas.c0000644000000000000000000000005014214376354016065 xustar0020 atime=1743516567 20 ctime=1743591278 bluez-5.82/profiles/battery/bas.c0000644000000000000000000001524314214376354015553 0ustar00rootroot// SPDX-License-Identifier: LGPL-2.1-or-later /* * * BlueZ - Bluetooth protocol stack for Linux * * Copyright (C) 2014 Intel Corporation. All rights reserved. * * */ #ifdef HAVE_CONFIG_H #include #endif #include #include #include #include "src/log.h" #include "lib/bluetooth.h" #include "lib/sdp.h" #include "lib/uuid.h" #include "src/shared/util.h" #include "src/shared/queue.h" #include "attrib/gattrib.h" #include "attrib/att.h" #include "attrib/gatt.h" #include "profiles/battery/bas.h" #define ATT_NOTIFICATION_HEADER_SIZE 3 #define ATT_READ_RESPONSE_HEADER_SIZE 1 struct bt_bas { int ref_count; GAttrib *attrib; struct gatt_primary *primary; uint16_t handle; uint16_t ccc_handle; guint id; struct queue *gatt_op; }; struct gatt_request { unsigned int id; struct bt_bas *bas; void *user_data; }; static void destroy_gatt_req(struct gatt_request *req) { queue_remove(req->bas->gatt_op, req); bt_bas_unref(req->bas); free(req); } static void bas_free(struct bt_bas *bas) { bt_bas_detach(bas); free(bas->primary); queue_destroy(bas->gatt_op, (void *) destroy_gatt_req); free(bas); } struct bt_bas *bt_bas_new(void *primary) { struct bt_bas *bas; bas = new0(struct bt_bas, 1); bas->gatt_op = queue_new(); if (primary) bas->primary = util_memdup(primary, sizeof(*bas->primary)); return bt_bas_ref(bas); } struct bt_bas *bt_bas_ref(struct bt_bas *bas) { if (!bas) return NULL; __sync_fetch_and_add(&bas->ref_count, 1); return bas; } void bt_bas_unref(struct bt_bas *bas) { if (!bas) return; if (__sync_sub_and_fetch(&bas->ref_count, 1)) return; bas_free(bas); } static struct gatt_request *create_request(struct bt_bas *bas, void *user_data) { struct gatt_request *req; req = new0(struct gatt_request, 1); req->user_data = user_data; req->bas = bt_bas_ref(bas); return req; } static void set_and_store_gatt_req(struct bt_bas *bas, struct gatt_request *req, unsigned int id) { req->id = id; queue_push_head(bas->gatt_op, req); } static void write_char(struct bt_bas *bas, GAttrib *attrib, uint16_t handle, const uint8_t *value, size_t vlen, GAttribResultFunc func, gpointer user_data) { struct gatt_request *req; unsigned int id; req = create_request(bas, user_data); id = gatt_write_char(attrib, handle, value, vlen, func, req); set_and_store_gatt_req(bas, req, id); } static void read_char(struct bt_bas *bas, GAttrib *attrib, uint16_t handle, GAttribResultFunc func, gpointer user_data) { struct gatt_request *req; unsigned int id; req = create_request(bas, user_data); id = gatt_read_char(attrib, handle, func, req); set_and_store_gatt_req(bas, req, id); } static void discover_char(struct bt_bas *bas, GAttrib *attrib, uint16_t start, uint16_t end, bt_uuid_t *uuid, gatt_cb_t func, gpointer user_data) { struct gatt_request *req; unsigned int id; req = create_request(bas, user_data); id = gatt_discover_char(attrib, start, end, uuid, func, req); set_and_store_gatt_req(bas, req, id); } static void discover_desc(struct bt_bas *bas, GAttrib *attrib, uint16_t start, uint16_t end, bt_uuid_t *uuid, gatt_cb_t func, gpointer user_data) { struct gatt_request *req; unsigned int id; req = create_request(bas, user_data); id = gatt_discover_desc(attrib, start, end, uuid, func, req); set_and_store_gatt_req(bas, req, id); } static void notification_cb(const guint8 *pdu, guint16 len, gpointer user_data) { DBG("Battery Level at %u", pdu[ATT_NOTIFICATION_HEADER_SIZE]); } static void read_value_cb(guint8 status, const guint8 *pdu, guint16 len, gpointer user_data) { DBG("Battery Level at %u", pdu[ATT_READ_RESPONSE_HEADER_SIZE]); } static void ccc_written_cb(guint8 status, const guint8 *pdu, guint16 plen, gpointer user_data) { struct gatt_request *req = user_data; struct bt_bas *bas = req->user_data; destroy_gatt_req(req); if (status != 0) { error("Write Scan Refresh CCC failed: %s", att_ecode2str(status)); return; } DBG("Battery Level: notification enabled"); bas->id = g_attrib_register(bas->attrib, ATT_OP_HANDLE_NOTIFY, bas->handle, notification_cb, bas, NULL); } static void write_ccc(struct bt_bas *bas, GAttrib *attrib, uint16_t handle, void *user_data) { uint8_t value[2]; put_le16(GATT_CLIENT_CHARAC_CFG_NOTIF_BIT, value); write_char(bas, attrib, handle, value, sizeof(value), ccc_written_cb, user_data); } static void ccc_read_cb(guint8 status, const guint8 *pdu, guint16 len, gpointer user_data) { struct gatt_request *req = user_data; struct bt_bas *bas = req->user_data; destroy_gatt_req(req); if (status != 0) { error("Error reading CCC value: %s", att_ecode2str(status)); return; } write_ccc(bas, bas->attrib, bas->ccc_handle, bas); } static void discover_descriptor_cb(uint8_t status, GSList *descs, void *user_data) { struct gatt_request *req = user_data; struct bt_bas *bas = req->user_data; struct gatt_desc *desc; destroy_gatt_req(req); if (status != 0) { error("Discover descriptors failed: %s", att_ecode2str(status)); return; } /* There will be only one descriptor on list and it will be CCC */ desc = descs->data; bas->ccc_handle = desc->handle; read_char(bas, bas->attrib, desc->handle, ccc_read_cb, bas); } static void bas_discovered_cb(uint8_t status, GSList *chars, void *user_data) { struct gatt_request *req = user_data; struct bt_bas *bas = req->user_data; struct gatt_char *chr; uint16_t start, end; bt_uuid_t uuid; destroy_gatt_req(req); if (status) { error("Battery: %s", att_ecode2str(status)); return; } chr = chars->data; bas->handle = chr->value_handle; DBG("Battery handle: 0x%04x", bas->handle); read_char(bas, bas->attrib, bas->handle, read_value_cb, bas); start = chr->value_handle + 1; end = bas->primary->range.end; bt_uuid16_create(&uuid, GATT_CLIENT_CHARAC_CFG_UUID); discover_desc(bas, bas->attrib, start, end, &uuid, discover_descriptor_cb, bas); } bool bt_bas_attach(struct bt_bas *bas, void *attrib) { if (!bas || bas->attrib || !bas->primary) return false; bas->attrib = g_attrib_ref(attrib); if (bas->handle > 0) return true; discover_char(bas, bas->attrib, bas->primary->range.start, bas->primary->range.end, NULL, bas_discovered_cb, bas); return true; } static void cancel_gatt_req(struct gatt_request *req) { if (g_attrib_cancel(req->bas->attrib, req->id)) destroy_gatt_req(req); } void bt_bas_detach(struct bt_bas *bas) { if (!bas || !bas->attrib) return; if (bas->id > 0) { g_attrib_unregister(bas->attrib, bas->id); bas->id = 0; } queue_foreach(bas->gatt_op, (void *) cancel_gatt_req, NULL); g_attrib_unref(bas->attrib); bas->attrib = NULL; } bluez-5.82/profiles/PaxHeaders/gap0000644000000000000000000000005014773213564014177 xustar0020 atime=1743591291 20 ctime=1743591284 bluez-5.82/profiles/gap/0000755000000000000000000000000014773213564013735 5ustar00rootrootbluez-5.82/profiles/gap/PaxHeaders/gas.c0000644000000000000000000000005014643061455015166 xustar0020 atime=1743516569 20 ctime=1743591284 bluez-5.82/profiles/gap/gas.c0000644000000000000000000002153714643061455014657 0ustar00rootroot// SPDX-License-Identifier: GPL-2.0-or-later /* * * BlueZ - Bluetooth protocol stack for Linux * * Copyright (C) 2012 Instituto Nokia de Tecnologia - INdT * Copyright (C) 2014 Google Inc. * */ #ifdef HAVE_CONFIG_H #include #endif #define _GNU_SOURCE #include #include #include #include #include #include #include #include #include "lib/bluetooth.h" #include "lib/hci.h" #include "lib/sdp.h" #include "lib/uuid.h" #include "src/shared/util.h" #include "src/shared/att.h" #include "src/shared/queue.h" #include "src/shared/gatt-db.h" #include "src/shared/gatt-client.h" #include "src/plugin.h" #include "src/adapter.h" #include "src/device.h" #include "src/profile.h" #include "src/service.h" #include "src/log.h" #define GAP_UUID16 0x1800 /* Generic Attribute/Access Service */ struct gas { struct btd_device *device; struct gatt_db *db; struct bt_gatt_client *client; struct gatt_db_attribute *attr; }; static void gas_reset(struct gas *gas) { gas->attr = NULL; gatt_db_unref(gas->db); gas->db = NULL; bt_gatt_client_unref(gas->client); gas->client = NULL; } static void gas_free(struct gas *gas) { gas_reset(gas); btd_device_unref(gas->device); g_free(gas); } static char *name2utf8(const uint8_t *name, uint16_t len) { char utf8_name[HCI_MAX_NAME_LENGTH + 2]; int i; if (g_utf8_validate((const char *) name, len, NULL)) return g_strndup((char *) name, len); len = MIN(len, sizeof(utf8_name) - 1); memset(utf8_name, 0, sizeof(utf8_name)); strncpy(utf8_name, (char *) name, len); /* Assume ASCII, and replace all non-ASCII with spaces */ for (i = 0; utf8_name[i] != '\0'; i++) { if (!isascii(utf8_name[i])) utf8_name[i] = ' '; } /* Remove leading and trailing whitespace characters */ g_strstrip(utf8_name); return g_strdup(utf8_name); } static void read_device_name_cb(bool success, uint8_t att_ecode, const uint8_t *value, uint16_t length, void *user_data) { struct gas *gas = user_data; char *name; if (!success) { DBG("Reading device name failed with ATT errror: %u", att_ecode); return; } if (!length) return; name = name2utf8(value, length); DBG("GAP Device Name: %s", name); btd_device_device_set_name(gas->device, name); g_free(name); } static void handle_device_name(struct gas *gas, uint16_t value_handle) { if (!bt_gatt_client_read_long_value(gas->client, value_handle, 0, read_device_name_cb, gas, NULL)) DBG("Failed to send request to read device name"); } static void read_appearance_cb(bool success, uint8_t att_ecode, const uint8_t *value, uint16_t length, void *user_data) { struct gas *gas = user_data; uint16_t appearance; if (!success) { DBG("Reading appearance failed with ATT error: %u", att_ecode); return; } /* The appearance value is a 16-bit unsigned integer */ if (length != 2) { DBG("Malformed appearance value"); return; } appearance = get_le16(value); DBG("GAP Appearance: 0x%04x", appearance); device_set_appearance(gas->device, appearance); } static void handle_appearance(struct gas *gas, uint16_t value_handle) { uint16_t value; if (!device_get_appearance(gas->device, &value)) return; if (!bt_gatt_client_read_value(gas->client, value_handle, read_appearance_cb, gas, NULL)) DBG("Failed to send request to read appearance"); } static void read_ppcp_cb(bool success, uint8_t att_ecode, const uint8_t *value, uint16_t length, void *user_data) { struct gas *gas = user_data; uint16_t min_interval, max_interval, latency, timeout, max_latency; if (!success) { DBG("Reading PPCP failed with ATT error: %u", att_ecode); return; } if (length != 8) { DBG("Malformed PPCP value"); return; } min_interval = get_le16(&value[0]); max_interval = get_le16(&value[2]); latency = get_le16(&value[4]); timeout = get_le16(&value[6]); /* 0xffff indicates no specific min/max */ if (min_interval == 0xffff) min_interval = 0x0018; /* 30.0ms */ if (max_interval == 0xffff) max_interval = 0x0028; /* 50.0ms */ DBG("GAP Peripheral Preferred Connection Parameters:"); DBG("\tMinimum connection interval: %u", min_interval); DBG("\tMaximum connection interval: %u", max_interval); DBG("\tSlave latency: %u", latency); DBG("\tConnection Supervision timeout multiplier: %u", timeout); /* avoid persisting connection parameters that are not valid */ if (min_interval > max_interval || min_interval < 6 || max_interval > 3200) { warn("GAS PPCP: Invalid Connection Parameters values"); return; } if (timeout < 10 || timeout > 3200) { warn("GAS PPCP: Invalid Connection Parameters values"); return; } if (max_interval >= timeout * 8) { warn("GAS PPCP: Invalid Connection Parameters values"); return; } max_latency = (timeout * 4 / max_interval) - 1; if (latency > 499 || latency > max_latency) { warn("GAS PPCP: Invalid Connection Parameters values"); return; } btd_device_set_conn_param(gas->device, min_interval, max_interval, latency, timeout); } static void handle_ppcp(struct gas *gas, uint16_t value_handle) { if (!bt_gatt_client_read_value(gas->client, value_handle, read_ppcp_cb, gas, NULL)) DBG("Failed to send request to read PPCP"); } static inline bool uuid_cmp(uint16_t u16, const bt_uuid_t *uuid) { bt_uuid_t lhs; bt_uuid16_create(&lhs, u16); return bt_uuid_cmp(&lhs, uuid) == 0; } static void handle_characteristic(struct gatt_db_attribute *attr, void *user_data) { struct gas *gas = user_data; uint16_t value_handle; bt_uuid_t uuid; if (!gatt_db_attribute_get_char_data(attr, NULL, &value_handle, NULL, NULL, &uuid)) { error("Failed to obtain characteristic data"); return; } if (uuid_cmp(GATT_CHARAC_DEVICE_NAME, &uuid)) handle_device_name(gas, value_handle); else if (uuid_cmp(GATT_CHARAC_APPEARANCE, &uuid)) handle_appearance(gas, value_handle); else if (uuid_cmp(GATT_CHARAC_PERIPHERAL_PREF_CONN, &uuid)) handle_ppcp(gas, value_handle); else { char uuid_str[MAX_LEN_UUID_STR]; /* TODO: Support peripheral privacy feature */ bt_uuid_to_string(&uuid, uuid_str, sizeof(uuid_str)); DBG("Unsupported characteristic: %s", uuid_str); } } static int gap_probe(struct btd_service *service) { struct btd_device *device = btd_service_get_device(service); struct gas *gas = btd_service_get_user_data(service); char addr[18]; ba2str(device_get_address(device), addr); DBG("GAP profile probe (%s)", addr); /* Ignore, if we were probed for this device already */ if (gas) { error("Profile probed twice for the same device!"); return -1; } gas = g_new0(struct gas, 1); if (!gas) return -1; gas->device = btd_device_ref(device); btd_service_set_user_data(service, gas); return 0; } static void gap_remove(struct btd_service *service) { struct btd_device *device = btd_service_get_device(service); struct gas *gas; char addr[18]; ba2str(device_get_address(device), addr); DBG("GAP profile remove (%s)", addr); gas = btd_service_get_user_data(service); if (!gas) { error("GAP service not handled by profile"); return; } gas_free(gas); } static void foreach_gap_service(struct gatt_db_attribute *attr, void *user_data) { struct gas *gas = user_data; if (gas->attr) { error("More than one GAP service exists for this device"); return; } gas->attr = attr; gatt_db_service_foreach_char(gas->attr, handle_characteristic, gas); } static int gap_accept(struct btd_service *service) { struct btd_device *device = btd_service_get_device(service); struct gatt_db *db = btd_device_get_gatt_db(device); struct bt_gatt_client *client = btd_device_get_gatt_client(device); struct gas *gas = btd_service_get_user_data(service); char addr[18]; bt_uuid_t gap_uuid; int err = 0; ba2str(device_get_address(device), addr); DBG("GAP profile accept (%s)", addr); if (!gas) { error("GAP service not handled by profile"); err = -1; goto _finish; } /* Check if attribute already has been discovered */ if (gas->attr) goto _finish; gas->db = gatt_db_ref(db); gas->client = bt_gatt_client_clone(client); /* Handle the GAP services */ bt_uuid16_create(&gap_uuid, GAP_UUID16); gatt_db_foreach_service(db, &gap_uuid, foreach_gap_service, gas); if (!gas->attr) { error("GAP attribute not found"); gas_reset(gas); err = -1; } _finish: btd_service_connecting_complete(service, err); return err; } static int gap_disconnect(struct btd_service *service) { btd_service_disconnecting_complete(service, 0); return 0; } static struct btd_profile gap_profile = { .name = "gap-profile", .remote_uuid = GAP_UUID, .device_probe = gap_probe, .device_remove = gap_remove, .accept = gap_accept, .disconnect = gap_disconnect, }; static int gap_init(void) { return btd_profile_register(&gap_profile); } static void gap_exit(void) { btd_profile_unregister(&gap_profile); } BLUETOOTH_PLUGIN_DEFINE(gap, VERSION, BLUETOOTH_PLUGIN_PRIORITY_DEFAULT, gap_init, gap_exit) bluez-5.82/profiles/PaxHeaders/sap0000644000000000000000000000005014773213564014213 xustar0020 atime=1743591291 20 ctime=1743591284 bluez-5.82/profiles/sap/0000755000000000000000000000000014773213564013751 5ustar00rootrootbluez-5.82/profiles/sap/PaxHeaders/server.c0000644000000000000000000000005014131623652015731 xustar0020 atime=1743516870 20 ctime=1743591284 bluez-5.82/profiles/sap/server.c0000644000000000000000000010047414131623652015420 0ustar00rootroot// SPDX-License-Identifier: GPL-2.0-or-later /* * BlueZ - Bluetooth protocol stack for Linux * * Copyright (C) 2010 Instituto Nokia de Tecnologia - INdT * Copyright (C) 2010 ST-Ericsson SA * Copyright (C) 2011 Tieto Poland * * Author: Marek Skowron for ST-Ericsson. * Author: Waldemar Rymarkiewicz * for ST-Ericsson. * */ #ifdef HAVE_CONFIG_H #include #endif #include #include #include "lib/sdp.h" #include "lib/sdp_lib.h" #include "lib/uuid.h" #include "gdbus/gdbus.h" #include "btio/btio.h" #include "src/adapter.h" #include "src/sdpd.h" #include "src/log.h" #include "src/error.h" #include "src/dbus-common.h" #include "src/shared/timeout.h" #include "src/shared/util.h" #include "sap.h" #include "server.h" #define SAP_SERVER_INTERFACE "org.bluez.SimAccess1" #define SAP_SERVER_CHANNEL 8 #define PADDING4(x) ((4 - ((x) & 0x03)) & 0x03) #define PARAMETER_SIZE(x) (sizeof(struct sap_parameter) + x + PADDING4(x)) #define SAP_NO_REQ 0xFF #define SAP_DISCONNECTION_TYPE_CLIENT 0xFF #define SAP_TIMER_GRACEFUL_DISCONNECT 30 #define SAP_TIMER_NO_ACTIVITY 30 enum { SAP_STATE_DISCONNECTED, SAP_STATE_CONNECT_IN_PROGRESS, SAP_STATE_CONNECT_MODEM_BUSY, SAP_STATE_CONNECTED, SAP_STATE_GRACEFUL_DISCONNECT, SAP_STATE_IMMEDIATE_DISCONNECT, SAP_STATE_CLIENT_DISCONNECT }; struct sap_connection { GIOChannel *io; uint32_t state; uint8_t processing_req; unsigned int timer_id; }; struct sap_server { struct btd_adapter *adapter; uint32_t record_id; GIOChannel *listen_io; struct sap_connection *conn; }; static void start_guard_timer(struct sap_server *server, guint interval); static void stop_guard_timer(struct sap_server *server); static bool guard_timeout(gpointer data); static size_t add_result_parameter(uint8_t result, struct sap_parameter *param) { param->id = SAP_PARAM_ID_RESULT_CODE; param->len = htons(SAP_PARAM_ID_RESULT_CODE_LEN); *param->val = result; return PARAMETER_SIZE(SAP_PARAM_ID_RESULT_CODE_LEN); } static int is_power_sim_off_req_allowed(uint8_t processing_req) { switch (processing_req) { case SAP_NO_REQ: case SAP_TRANSFER_APDU_REQ: case SAP_TRANSFER_ATR_REQ: case SAP_POWER_SIM_ON_REQ: case SAP_RESET_SIM_REQ: case SAP_TRANSFER_CARD_READER_STATUS_REQ: return 1; default: return 0; } } static int is_reset_sim_req_allowed(uint8_t processing_req) { switch (processing_req) { case SAP_NO_REQ: case SAP_TRANSFER_APDU_REQ: case SAP_TRANSFER_ATR_REQ: case SAP_TRANSFER_CARD_READER_STATUS_REQ: return 1; default: return 0; } } static int check_msg(struct sap_message *msg) { switch (msg->id) { case SAP_CONNECT_REQ: if (msg->nparam != 0x01) return -EBADMSG; if (msg->param->id != SAP_PARAM_ID_MAX_MSG_SIZE) return -EBADMSG; if (ntohs(msg->param->len) != SAP_PARAM_ID_MAX_MSG_SIZE_LEN) return -EBADMSG; break; case SAP_TRANSFER_APDU_REQ: if (msg->nparam != 0x01) return -EBADMSG; if (msg->param->id != SAP_PARAM_ID_COMMAND_APDU) if (msg->param->id != SAP_PARAM_ID_COMMAND_APDU7816) return -EBADMSG; if (msg->param->len == 0x00) return -EBADMSG; break; case SAP_SET_TRANSPORT_PROTOCOL_REQ: if (msg->nparam != 0x01) return -EBADMSG; if (msg->param->id != SAP_PARAM_ID_TRANSPORT_PROTOCOL) return -EBADMSG; if (ntohs(msg->param->len) != SAP_PARAM_ID_TRANSPORT_PROTO_LEN) return -EBADMSG; if (*msg->param->val != SAP_TRANSPORT_PROTOCOL_T0) if (*msg->param->val != SAP_TRANSPORT_PROTOCOL_T1) return -EBADMSG; break; case SAP_DISCONNECT_REQ: case SAP_TRANSFER_ATR_REQ: case SAP_POWER_SIM_OFF_REQ: case SAP_POWER_SIM_ON_REQ: case SAP_RESET_SIM_REQ: case SAP_TRANSFER_CARD_READER_STATUS_REQ: if (msg->nparam != 0x00) return -EBADMSG; break; } return 0; } static sdp_record_t *create_sap_record(uint8_t channel) { sdp_list_t *apseq, *aproto, *profiles, *proto[2], *root, *svclass_id; uuid_t sap_uuid, gt_uuid, root_uuid, l2cap, rfcomm; sdp_profile_desc_t profile; sdp_record_t *record; sdp_data_t *ch; record = sdp_record_alloc(); if (!record) return NULL; sdp_uuid16_create(&root_uuid, PUBLIC_BROWSE_GROUP); root = sdp_list_append(NULL, &root_uuid); sdp_set_browse_groups(record, root); sdp_list_free(root, NULL); sdp_uuid16_create(&sap_uuid, SAP_SVCLASS_ID); svclass_id = sdp_list_append(NULL, &sap_uuid); sdp_uuid16_create(>_uuid, GENERIC_TELEPHONY_SVCLASS_ID); svclass_id = sdp_list_append(svclass_id, >_uuid); sdp_set_service_classes(record, svclass_id); sdp_list_free(svclass_id, NULL); sdp_uuid16_create(&profile.uuid, SAP_PROFILE_ID); profile.version = SAP_VERSION; profiles = sdp_list_append(NULL, &profile); sdp_set_profile_descs(record, profiles); sdp_list_free(profiles, NULL); sdp_uuid16_create(&l2cap, L2CAP_UUID); proto[0] = sdp_list_append(NULL, &l2cap); apseq = sdp_list_append(NULL, proto[0]); sdp_uuid16_create(&rfcomm, RFCOMM_UUID); proto[1] = sdp_list_append(NULL, &rfcomm); ch = sdp_data_alloc(SDP_UINT8, &channel); proto[1] = sdp_list_append(proto[1], ch); apseq = sdp_list_append(apseq, proto[1]); aproto = sdp_list_append(NULL, apseq); sdp_set_access_protos(record, aproto); sdp_set_info_attr(record, "SIM Access Server", NULL, NULL); sdp_data_free(ch); sdp_list_free(proto[0], NULL); sdp_list_free(proto[1], NULL); sdp_list_free(apseq, NULL); sdp_list_free(aproto, NULL); return record; } static int send_message(struct sap_connection *conn, void *buf, size_t size) { size_t written = 0; GError *gerr = NULL; GIOStatus gstatus; SAP_VDBG("conn %p, size %zu", conn, size); gstatus = g_io_channel_write_chars(conn->io, buf, size, &written, &gerr); if (gstatus != G_IO_STATUS_NORMAL) { if (gerr) g_error_free(gerr); error("write error (0x%02x).", gstatus); return -EIO; } if (written != size) { error("written %zu bytes out of %zu", written, size); return -EIO; } return written; } static int disconnect_ind(struct sap_connection *conn, uint8_t disc_type) { char buf[SAP_BUF_SIZE]; struct sap_message *msg = (struct sap_message *) buf; struct sap_parameter *param = (struct sap_parameter *) msg->param; size_t size = sizeof(struct sap_message); DBG("data %p state %d disc_type 0x%02x", conn, conn->state, disc_type); memset(buf, 0, sizeof(buf)); msg->id = SAP_DISCONNECT_IND; msg->nparam = 0x01; /* Add disconnection type param. */ param->id = SAP_PARAM_ID_DISCONNECT_IND; param->len = htons(SAP_PARAM_ID_DISCONNECT_IND_LEN); *param->val = disc_type; size += PARAMETER_SIZE(SAP_PARAM_ID_DISCONNECT_IND_LEN); return send_message(conn, buf, size); } static int sap_error_rsp(struct sap_connection *conn) { struct sap_message msg; memset(&msg, 0, sizeof(msg)); msg.id = SAP_ERROR_RESP; error("SAP error (state %d pr 0x%02x).", conn->state, conn->processing_req); return send_message(conn, &msg, sizeof(msg)); } static void connect_req(struct sap_server *server, struct sap_parameter *param) { struct sap_connection *conn = server->conn; uint16_t maxmsgsize; DBG("conn %p state %d", conn, conn->state); if (!param) goto error_rsp; if (conn->state != SAP_STATE_DISCONNECTED) goto error_rsp; stop_guard_timer(server); maxmsgsize = get_be16(¶m->val); DBG("Connect MaxMsgSize: 0x%04x", maxmsgsize); conn->state = SAP_STATE_CONNECT_IN_PROGRESS; if (maxmsgsize <= SAP_BUF_SIZE) { conn->processing_req = SAP_CONNECT_REQ; sap_connect_req(server, maxmsgsize); } else { sap_connect_rsp(server, SAP_STATUS_MAX_MSG_SIZE_NOT_SUPPORTED); } return; error_rsp: sap_error_rsp(conn); } static int disconnect_req(struct sap_server *server, uint8_t disc_type) { struct sap_connection *conn = server->conn; DBG("conn %p state %d disc_type 0x%02x", conn, conn->state, disc_type); switch (disc_type) { case SAP_DISCONNECTION_TYPE_GRACEFUL: if (conn->state == SAP_STATE_DISCONNECTED || conn->state == SAP_STATE_CONNECT_IN_PROGRESS || conn->state == SAP_STATE_CONNECT_MODEM_BUSY) return -EPERM; if (conn->state == SAP_STATE_CONNECTED) { conn->state = SAP_STATE_GRACEFUL_DISCONNECT; conn->processing_req = SAP_NO_REQ; disconnect_ind(conn, disc_type); /* Timer will disconnect if client won't do.*/ start_guard_timer(server, SAP_TIMER_GRACEFUL_DISCONNECT); } return 0; case SAP_DISCONNECTION_TYPE_IMMEDIATE: if (conn->state == SAP_STATE_DISCONNECTED || conn->state == SAP_STATE_CONNECT_IN_PROGRESS || conn->state == SAP_STATE_CONNECT_MODEM_BUSY) return -EPERM; if (conn->state == SAP_STATE_CONNECTED || conn->state == SAP_STATE_GRACEFUL_DISCONNECT) { conn->state = SAP_STATE_IMMEDIATE_DISCONNECT; conn->processing_req = SAP_NO_REQ; stop_guard_timer(server); disconnect_ind(conn, disc_type); sap_disconnect_req(server, 0); } return 0; case SAP_DISCONNECTION_TYPE_CLIENT: if (conn->state != SAP_STATE_CONNECTED && conn->state != SAP_STATE_GRACEFUL_DISCONNECT) { sap_error_rsp(conn); return -EPERM; } conn->state = SAP_STATE_CLIENT_DISCONNECT; conn->processing_req = SAP_NO_REQ; stop_guard_timer(server); sap_disconnect_req(server, 0); return 0; default: error("Unknown disconnection type (0x%02x).", disc_type); return -EINVAL; } } static void transfer_apdu_req(struct sap_server *server, struct sap_parameter *param) { struct sap_connection *conn = server->conn; SAP_VDBG("conn %p state %d", conn, conn->state); if (!param) goto error_rsp; param->len = ntohs(param->len); if (conn->state != SAP_STATE_CONNECTED && conn->state != SAP_STATE_GRACEFUL_DISCONNECT) goto error_rsp; if (conn->processing_req != SAP_NO_REQ) goto error_rsp; conn->processing_req = SAP_TRANSFER_APDU_REQ; sap_transfer_apdu_req(server, param); return; error_rsp: sap_error_rsp(conn); } static void transfer_atr_req(struct sap_server *server) { struct sap_connection *conn = server->conn; DBG("conn %p state %d", conn, conn->state); if (conn->state != SAP_STATE_CONNECTED) goto error_rsp; if (conn->processing_req != SAP_NO_REQ) goto error_rsp; conn->processing_req = SAP_TRANSFER_ATR_REQ; sap_transfer_atr_req(server); return; error_rsp: sap_error_rsp(conn); } static void power_sim_off_req(struct sap_server *server) { struct sap_connection *conn = server->conn; DBG("conn %p state %d", conn, conn->state); if (conn->state != SAP_STATE_CONNECTED) goto error_rsp; if (!is_power_sim_off_req_allowed(conn->processing_req)) goto error_rsp; conn->processing_req = SAP_POWER_SIM_OFF_REQ; sap_power_sim_off_req(server); return; error_rsp: sap_error_rsp(conn); } static void power_sim_on_req(struct sap_server *server) { struct sap_connection *conn = server->conn; DBG("conn %p state %d", conn, conn->state); if (conn->state != SAP_STATE_CONNECTED) goto error_rsp; if (conn->processing_req != SAP_NO_REQ) goto error_rsp; conn->processing_req = SAP_POWER_SIM_ON_REQ; sap_power_sim_on_req(server); return; error_rsp: sap_error_rsp(conn); } static void reset_sim_req(struct sap_server *server) { struct sap_connection *conn = server->conn; DBG("conn %p state %d", conn, conn->state); if (conn->state != SAP_STATE_CONNECTED) goto error_rsp; if (!is_reset_sim_req_allowed(conn->processing_req)) goto error_rsp; conn->processing_req = SAP_RESET_SIM_REQ; sap_reset_sim_req(server); return; error_rsp: sap_error_rsp(conn); } static void transfer_card_reader_status_req(struct sap_server *server) { struct sap_connection *conn = server->conn; DBG("conn %p state %d", conn, conn->state); if (conn->state != SAP_STATE_CONNECTED) goto error_rsp; if (conn->processing_req != SAP_NO_REQ) goto error_rsp; conn->processing_req = SAP_TRANSFER_CARD_READER_STATUS_REQ; sap_transfer_card_reader_status_req(server); return; error_rsp: sap_error_rsp(conn); } static void set_transport_protocol_req(struct sap_server *server, struct sap_parameter *param) { struct sap_connection *conn = server->conn; if (!param) goto error_rsp; DBG("conn %p state %d param %p", conn, conn->state, param); if (conn->state != SAP_STATE_CONNECTED) goto error_rsp; if (conn->processing_req != SAP_NO_REQ) goto error_rsp; conn->processing_req = SAP_SET_TRANSPORT_PROTOCOL_REQ; sap_set_transport_protocol_req(server, param); return; error_rsp: sap_error_rsp(conn); } static void start_guard_timer(struct sap_server *server, guint interval) { struct sap_connection *conn = server->conn; if (!conn) return; if (!conn->timer_id) conn->timer_id = timeout_add_seconds(interval, guard_timeout, server, NULL); else error("Timer is already active."); } static void stop_guard_timer(struct sap_server *server) { struct sap_connection *conn = server->conn; if (conn && conn->timer_id) { timeout_remove(conn->timer_id); conn->timer_id = 0; } } static bool guard_timeout(gpointer data) { struct sap_server *server = data; struct sap_connection *conn = server->conn; if (!conn) return FALSE; DBG("conn %p state %d pr 0x%02x", conn, conn->state, conn->processing_req); conn->timer_id = 0; switch (conn->state) { case SAP_STATE_DISCONNECTED: /* Client opened RFCOMM channel but didn't send CONNECT_REQ, * in fixed time or client disconnected SAP connection but * didn't closed RFCOMM channel in fixed time.*/ if (conn->io) { g_io_channel_shutdown(conn->io, TRUE, NULL); g_io_channel_unref(conn->io); conn->io = NULL; } break; case SAP_STATE_GRACEFUL_DISCONNECT: /* Client didn't disconnect SAP connection in fixed time, * so close SAP connection immediately. */ disconnect_req(server, SAP_DISCONNECTION_TYPE_IMMEDIATE); break; default: error("Unexpected state (%d).", conn->state); break; } return FALSE; } static void sap_set_connected(struct sap_server *server) { server->conn->state = SAP_STATE_CONNECTED; g_dbus_emit_property_changed(btd_get_dbus_connection(), adapter_get_path(server->adapter), SAP_SERVER_INTERFACE, "Connected"); } int sap_connect_rsp(void *sap_device, uint8_t status) { struct sap_server *server = sap_device; struct sap_connection *conn = server->conn; char buf[SAP_BUF_SIZE]; struct sap_message *msg = (struct sap_message *) buf; struct sap_parameter *param = (struct sap_parameter *) msg->param; size_t size = sizeof(struct sap_message); if (!conn) return -EINVAL; DBG("state %d pr 0x%02x status 0x%02x", conn->state, conn->processing_req, status); if (conn->state != SAP_STATE_CONNECT_IN_PROGRESS) return -EPERM; memset(buf, 0, sizeof(buf)); msg->id = SAP_CONNECT_RESP; msg->nparam = 0x01; /* Add connection status */ param->id = SAP_PARAM_ID_CONN_STATUS; param->len = htons(SAP_PARAM_ID_CONN_STATUS_LEN); *param->val = status; size += PARAMETER_SIZE(SAP_PARAM_ID_CONN_STATUS_LEN); switch (status) { case SAP_STATUS_OK: sap_set_connected(server); break; case SAP_STATUS_OK_ONGOING_CALL: DBG("ongoing call. Wait for reset indication!"); conn->state = SAP_STATE_CONNECT_MODEM_BUSY; break; case SAP_STATUS_MAX_MSG_SIZE_NOT_SUPPORTED: /* Add MaxMsgSize */ msg->nparam++; param = (struct sap_parameter *) &buf[size]; param->id = SAP_PARAM_ID_MAX_MSG_SIZE; param->len = htons(SAP_PARAM_ID_MAX_MSG_SIZE_LEN); put_be16(SAP_BUF_SIZE, ¶m->val); size += PARAMETER_SIZE(SAP_PARAM_ID_MAX_MSG_SIZE_LEN); /* fall through */ default: conn->state = SAP_STATE_DISCONNECTED; /* Timer will shutdown channel if client doesn't send * CONNECT_REQ or doesn't shutdown channel itself.*/ start_guard_timer(server, SAP_TIMER_NO_ACTIVITY); break; } conn->processing_req = SAP_NO_REQ; return send_message(conn, buf, size); } int sap_disconnect_rsp(void *sap_device) { struct sap_server *server = sap_device; struct sap_connection *conn = server->conn; struct sap_message msg; if (!conn) return -EINVAL; DBG("state %d pr 0x%02x", conn->state, conn->processing_req); switch (conn->state) { case SAP_STATE_CLIENT_DISCONNECT: memset(&msg, 0, sizeof(msg)); msg.id = SAP_DISCONNECT_RESP; conn->state = SAP_STATE_DISCONNECTED; conn->processing_req = SAP_NO_REQ; /* Timer will close channel if client doesn't do it.*/ start_guard_timer(server, SAP_TIMER_NO_ACTIVITY); return send_message(conn, &msg, sizeof(msg)); case SAP_STATE_IMMEDIATE_DISCONNECT: conn->state = SAP_STATE_DISCONNECTED; conn->processing_req = SAP_NO_REQ; if (conn->io) { g_io_channel_shutdown(conn->io, TRUE, NULL); g_io_channel_unref(conn->io); conn->io = NULL; } return 0; default: break; } return 0; } int sap_transfer_apdu_rsp(void *sap_device, uint8_t result, uint8_t *apdu, uint16_t length) { struct sap_server *server = sap_device; struct sap_connection *conn = server->conn; char buf[SAP_BUF_SIZE]; struct sap_message *msg = (struct sap_message *) buf; struct sap_parameter *param = (struct sap_parameter *) msg->param; size_t size = sizeof(struct sap_message); if (!conn) return -EINVAL; SAP_VDBG("state %d pr 0x%02x", conn->state, conn->processing_req); if (conn->processing_req != SAP_TRANSFER_APDU_REQ) return 0; if (result == SAP_RESULT_OK && (!apdu || (apdu && length == 0x00))) return -EINVAL; memset(buf, 0, sizeof(buf)); msg->id = SAP_TRANSFER_APDU_RESP; msg->nparam = 0x01; size += add_result_parameter(result, param); /* Add APDU response. */ if (result == SAP_RESULT_OK) { msg->nparam++; param = (struct sap_parameter *) &buf[size]; param->id = SAP_PARAM_ID_RESPONSE_APDU; param->len = htons(length); size += PARAMETER_SIZE(length); if (size > SAP_BUF_SIZE) return -EOVERFLOW; memcpy(param->val, apdu, length); } conn->processing_req = SAP_NO_REQ; return send_message(conn, buf, size); } int sap_transfer_atr_rsp(void *sap_device, uint8_t result, uint8_t *atr, uint16_t length) { struct sap_server *server = sap_device; struct sap_connection *conn = server->conn; char buf[SAP_BUF_SIZE]; struct sap_message *msg = (struct sap_message *) buf; struct sap_parameter *param = (struct sap_parameter *) msg->param; size_t size = sizeof(struct sap_message); if (!conn) return -EINVAL; DBG("result 0x%02x state %d pr 0x%02x len %d", result, conn->state, conn->processing_req, length); if (conn->processing_req != SAP_TRANSFER_ATR_REQ) return 0; if (result == SAP_RESULT_OK && (!atr || (atr && length == 0x00))) return -EINVAL; memset(buf, 0, sizeof(buf)); msg->id = SAP_TRANSFER_ATR_RESP; msg->nparam = 0x01; size += add_result_parameter(result, param); /* Add ATR response */ if (result == SAP_RESULT_OK) { msg->nparam++; param = (struct sap_parameter *) &buf[size]; param->id = SAP_PARAM_ID_ATR; param->len = htons(length); size += PARAMETER_SIZE(length); if (size > SAP_BUF_SIZE) return -EOVERFLOW; memcpy(param->val, atr, length); } conn->processing_req = SAP_NO_REQ; return send_message(conn, buf, size); } int sap_power_sim_off_rsp(void *sap_device, uint8_t result) { struct sap_server *server = sap_device; struct sap_connection *conn = server->conn; char buf[SAP_BUF_SIZE]; struct sap_message *msg = (struct sap_message *) buf; size_t size = sizeof(struct sap_message); if (!conn) return -EINVAL; DBG("state %d pr 0x%02x", conn->state, conn->processing_req); if (conn->processing_req != SAP_POWER_SIM_OFF_REQ) return 0; memset(buf, 0, sizeof(buf)); msg->id = SAP_POWER_SIM_OFF_RESP; msg->nparam = 0x01; size += add_result_parameter(result, msg->param); conn->processing_req = SAP_NO_REQ; return send_message(conn, buf, size); } int sap_power_sim_on_rsp(void *sap_device, uint8_t result) { struct sap_server *server = sap_device; struct sap_connection *conn = server->conn; char buf[SAP_BUF_SIZE]; struct sap_message *msg = (struct sap_message *) buf; size_t size = sizeof(struct sap_message); if (!conn) return -EINVAL; DBG("state %d pr 0x%02x", conn->state, conn->processing_req); if (conn->processing_req != SAP_POWER_SIM_ON_REQ) return 0; memset(buf, 0, sizeof(buf)); msg->id = SAP_POWER_SIM_ON_RESP; msg->nparam = 0x01; size += add_result_parameter(result, msg->param); conn->processing_req = SAP_NO_REQ; return send_message(conn, buf, size); } int sap_reset_sim_rsp(void *sap_device, uint8_t result) { struct sap_server *server = sap_device; struct sap_connection *conn = server->conn; char buf[SAP_BUF_SIZE]; struct sap_message *msg = (struct sap_message *) buf; size_t size = sizeof(struct sap_message); if (!conn) return -EINVAL; DBG("state %d pr 0x%02x result 0x%02x", conn->state, conn->processing_req, result); if (conn->processing_req != SAP_RESET_SIM_REQ) return 0; memset(buf, 0, sizeof(buf)); msg->id = SAP_RESET_SIM_RESP; msg->nparam = 0x01; size += add_result_parameter(result, msg->param); conn->processing_req = SAP_NO_REQ; return send_message(conn, buf, size); } int sap_transfer_card_reader_status_rsp(void *sap_device, uint8_t result, uint8_t status) { struct sap_server *server = sap_device; struct sap_connection *conn = server->conn; char buf[SAP_BUF_SIZE]; struct sap_message *msg = (struct sap_message *) buf; struct sap_parameter *param = (struct sap_parameter *) msg->param; size_t size = sizeof(struct sap_message); if (!conn) return -EINVAL; DBG("state %d pr 0x%02x result 0x%02x", conn->state, conn->processing_req, result); if (conn->processing_req != SAP_TRANSFER_CARD_READER_STATUS_REQ) return 0; memset(buf, 0, sizeof(buf)); msg->id = SAP_TRANSFER_CARD_READER_STATUS_RESP; msg->nparam = 0x01; size += add_result_parameter(result, param); /* Add card reader status. */ if (result == SAP_RESULT_OK) { msg->nparam++; param = (struct sap_parameter *) &buf[size]; param->id = SAP_PARAM_ID_CARD_READER_STATUS; param->len = htons(SAP_PARAM_ID_CARD_READER_STATUS_LEN); *param->val = status; size += PARAMETER_SIZE(SAP_PARAM_ID_CARD_READER_STATUS_LEN); } conn->processing_req = SAP_NO_REQ; return send_message(conn, buf, size); } int sap_transport_protocol_rsp(void *sap_device, uint8_t result) { struct sap_server *server = sap_device; struct sap_connection *conn = server->conn; char buf[SAP_BUF_SIZE]; struct sap_message *msg = (struct sap_message *) buf; size_t size = sizeof(struct sap_message); if (!conn) return -EINVAL; DBG("state %d pr 0x%02x result 0x%02x", conn->state, conn->processing_req, result); if (conn->processing_req != SAP_SET_TRANSPORT_PROTOCOL_REQ) return 0; memset(buf, 0, sizeof(buf)); msg->id = SAP_SET_TRANSPORT_PROTOCOL_RESP; msg->nparam = 0x01; size += add_result_parameter(result, msg->param); conn->processing_req = SAP_NO_REQ; return send_message(conn, buf, size); } int sap_status_ind(void *sap_device, uint8_t status_change) { struct sap_server *server = sap_device; struct sap_connection *conn = server->conn; char buf[SAP_BUF_SIZE]; struct sap_message *msg = (struct sap_message *) buf; struct sap_parameter *param = (struct sap_parameter *) msg->param; size_t size = sizeof(struct sap_message); if (!conn) return -EINVAL; DBG("state %d pr 0x%02x sc 0x%02x", conn->state, conn->processing_req, status_change); switch (conn->state) { case SAP_STATE_CONNECT_MODEM_BUSY: if (status_change != SAP_STATUS_CHANGE_CARD_RESET) break; /* Change state to connected after ongoing call ended */ sap_set_connected(server); /* fall through */ case SAP_STATE_CONNECTED: case SAP_STATE_GRACEFUL_DISCONNECT: memset(buf, 0, sizeof(buf)); msg->id = SAP_STATUS_IND; msg->nparam = 0x01; /* Add status change. */ param->id = SAP_PARAM_ID_STATUS_CHANGE; param->len = htons(SAP_PARAM_ID_STATUS_CHANGE_LEN); *param->val = status_change; size += PARAMETER_SIZE(SAP_PARAM_ID_STATUS_CHANGE_LEN); return send_message(conn, buf, size); case SAP_STATE_DISCONNECTED: case SAP_STATE_CONNECT_IN_PROGRESS: case SAP_STATE_IMMEDIATE_DISCONNECT: case SAP_STATE_CLIENT_DISCONNECT: break; } return 0; } int sap_disconnect_ind(void *sap_device, uint8_t disc_type) { return disconnect_req(sap_device, SAP_DISCONNECTION_TYPE_IMMEDIATE); } static int handle_cmd(struct sap_server *server, void *buf, size_t size) { struct sap_connection *conn = server->conn; struct sap_message *msg = buf; if (!conn) return -EINVAL; if (size < sizeof(struct sap_message)) goto error_rsp; if (msg->nparam != 0 && size < (sizeof(struct sap_message) + sizeof(struct sap_parameter) + 4)) goto error_rsp; if (check_msg(msg) < 0) goto error_rsp; switch (msg->id) { case SAP_CONNECT_REQ: connect_req(server, msg->param); return 0; case SAP_DISCONNECT_REQ: disconnect_req(server, SAP_DISCONNECTION_TYPE_CLIENT); return 0; case SAP_TRANSFER_APDU_REQ: transfer_apdu_req(server, msg->param); return 0; case SAP_TRANSFER_ATR_REQ: transfer_atr_req(server); return 0; case SAP_POWER_SIM_OFF_REQ: power_sim_off_req(server); return 0; case SAP_POWER_SIM_ON_REQ: power_sim_on_req(server); return 0; case SAP_RESET_SIM_REQ: reset_sim_req(server); return 0; case SAP_TRANSFER_CARD_READER_STATUS_REQ: transfer_card_reader_status_req(server); return 0; case SAP_SET_TRANSPORT_PROTOCOL_REQ: set_transport_protocol_req(server, msg->param); return 0; default: DBG("Unknown SAP message id 0x%02x.", msg->id); break; } error_rsp: DBG("Invalid SAP message format."); sap_error_rsp(conn); return -EBADMSG; } static void sap_server_remove_conn(struct sap_server *server) { struct sap_connection *conn = server->conn; DBG("conn %p", conn); if (!conn) return; if (conn->io) { g_io_channel_shutdown(conn->io, TRUE, NULL); g_io_channel_unref(conn->io); } g_free(conn); server->conn = NULL; } static gboolean sap_io_cb(GIOChannel *io, GIOCondition cond, gpointer data) { char buf[SAP_BUF_SIZE]; size_t bytes_read = 0; GError *gerr = NULL; GIOStatus gstatus; SAP_VDBG("conn %p io %p", conn, io); if (cond & G_IO_NVAL) { DBG("ERR (G_IO_NVAL) on rfcomm socket."); return FALSE; } if (cond & G_IO_ERR) { DBG("ERR (G_IO_ERR) on rfcomm socket."); return FALSE; } if (cond & G_IO_HUP) { DBG("HUP on rfcomm socket."); return FALSE; } gstatus = g_io_channel_read_chars(io, buf, sizeof(buf) - 1, &bytes_read, &gerr); if (gstatus != G_IO_STATUS_NORMAL) { if (gerr) g_error_free(gerr); return TRUE; } if (handle_cmd(data, buf, bytes_read) < 0) error("SAP protocol processing failure."); return TRUE; } static void sap_io_destroy(void *data) { struct sap_server *server = data; struct sap_connection *conn = server->conn; DBG("conn %p", conn); if (!conn || !conn->io) return; stop_guard_timer(server); if (conn->state != SAP_STATE_CONNECT_IN_PROGRESS && conn->state != SAP_STATE_CONNECT_MODEM_BUSY) g_dbus_emit_property_changed(btd_get_dbus_connection(), adapter_get_path(server->adapter), SAP_SERVER_INTERFACE, "Connected"); if (conn->state == SAP_STATE_CONNECT_IN_PROGRESS || conn->state == SAP_STATE_CONNECT_MODEM_BUSY || conn->state == SAP_STATE_CONNECTED || conn->state == SAP_STATE_GRACEFUL_DISCONNECT) sap_disconnect_req(server, 1); sap_server_remove_conn(server); } static void sap_connect_cb(GIOChannel *io, GError *gerr, gpointer data) { struct sap_server *server = data; struct sap_connection *conn = server->conn; DBG("conn %p, io %p", conn, io); if (!conn) return; /* Timer will shutdown the channel in case of lack of client activity */ start_guard_timer(server, SAP_TIMER_NO_ACTIVITY); g_io_add_watch_full(io, G_PRIORITY_DEFAULT, G_IO_IN | G_IO_ERR | G_IO_HUP | G_IO_NVAL, sap_io_cb, server, sap_io_destroy); } static void connect_auth_cb(DBusError *derr, void *data) { struct sap_server *server = data; struct sap_connection *conn = server->conn; GError *gerr = NULL; DBG("conn %p", conn); if (!conn) return; if (derr && dbus_error_is_set(derr)) { error("Access has been denied (%s)", derr->message); sap_server_remove_conn(server); return; } if (!bt_io_accept(conn->io, sap_connect_cb, server, NULL, &gerr)) { error("bt_io_accept: %s", gerr->message); g_error_free(gerr); sap_server_remove_conn(server); return; } DBG("Access has been granted."); } static void connect_confirm_cb(GIOChannel *io, gpointer data) { struct sap_server *server = data; struct sap_connection *conn = server->conn; GError *gerr = NULL; bdaddr_t src, dst; char dstaddr[18]; guint ret; DBG("conn %p io %p", conn, io); if (!io) return; if (conn) { DBG("Another SAP connection already exists."); g_io_channel_shutdown(io, TRUE, NULL); return; } conn = g_try_new0(struct sap_connection, 1); if (!conn) { error("Can't allocate memory for incoming SAP connection."); g_io_channel_shutdown(io, TRUE, NULL); return; } g_io_channel_set_encoding(io, NULL, NULL); g_io_channel_set_buffered(io, FALSE); server->conn = conn; conn->io = g_io_channel_ref(io); conn->state = SAP_STATE_DISCONNECTED; bt_io_get(io, &gerr, BT_IO_OPT_SOURCE_BDADDR, &src, BT_IO_OPT_DEST_BDADDR, &dst, BT_IO_OPT_INVALID); if (gerr) { error("%s", gerr->message); g_error_free(gerr); sap_server_remove_conn(server); return; } ba2str(&dst, dstaddr); ret = btd_request_authorization(&src, &dst, SAP_UUID, connect_auth_cb, server); if (ret == 0) { error("Authorization failure"); sap_server_remove_conn(server); return; } DBG("Authorizing incoming SAP connection from %s", dstaddr); } static DBusMessage *disconnect(DBusConnection *conn, DBusMessage *msg, void *data) { struct sap_server *server = data; if (!server) return btd_error_failed(msg, "Server internal error."); DBG("conn %p", server->conn); if (!server->conn) return btd_error_failed(msg, "Client already disconnected"); if (disconnect_req(server, SAP_DISCONNECTION_TYPE_GRACEFUL) < 0) return btd_error_failed(msg, "There is no active connection"); return dbus_message_new_method_return(msg); } static gboolean server_property_get_connected( const GDBusPropertyTable *property, DBusMessageIter *iter, void *data) { struct sap_server *server = data; struct sap_connection *conn = server->conn; dbus_bool_t connected; if (!conn) { connected = FALSE; goto append; } connected = (conn->state == SAP_STATE_CONNECTED || conn->state == SAP_STATE_GRACEFUL_DISCONNECT); append: dbus_message_iter_append_basic(iter, DBUS_TYPE_BOOLEAN, &connected); return TRUE; } static const GDBusMethodTable server_methods[] = { { GDBUS_METHOD("Disconnect", NULL, NULL, disconnect) }, { } }; static const GDBusPropertyTable server_properties[] = { { "Connected", "b", server_property_get_connected }, { } }; static void server_remove(struct sap_server *server) { if (!server) return; sap_server_remove_conn(server); adapter_service_remove(server->adapter, server->record_id); if (server->listen_io) { g_io_channel_shutdown(server->listen_io, TRUE, NULL); g_io_channel_unref(server->listen_io); server->listen_io = NULL; } btd_adapter_unref(server->adapter); g_free(server); } static void destroy_sap_interface(void *data) { struct sap_server *server = data; DBG("Unregistered interface %s on path %s", SAP_SERVER_INTERFACE, adapter_get_path(server->adapter)); server_remove(server); } int sap_server_register(struct btd_adapter *adapter) { sdp_record_t *record = NULL; GError *gerr = NULL; GIOChannel *io; struct sap_server *server; if (sap_init() < 0) { error("Sap driver initialization failed."); return -1; } record = create_sap_record(SAP_SERVER_CHANNEL); if (!record) { error("Creating SAP SDP record failed."); goto sdp_err; } if (adapter_service_add(adapter, record) < 0) { error("Adding SAP SDP record to the SDP server failed."); sdp_record_free(record); goto sdp_err; } server = g_new0(struct sap_server, 1); server->adapter = btd_adapter_ref(adapter); server->record_id = record->handle; io = bt_io_listen(NULL, connect_confirm_cb, server, NULL, &gerr, BT_IO_OPT_SOURCE_BDADDR, btd_adapter_get_address(adapter), BT_IO_OPT_CHANNEL, SAP_SERVER_CHANNEL, BT_IO_OPT_SEC_LEVEL, BT_IO_SEC_HIGH, BT_IO_OPT_CENTRAL, TRUE, BT_IO_OPT_INVALID); if (!io) { error("Can't listen at channel %d.", SAP_SERVER_CHANNEL); g_error_free(gerr); goto server_err; } server->listen_io = io; if (!g_dbus_register_interface(btd_get_dbus_connection(), adapter_get_path(server->adapter), SAP_SERVER_INTERFACE, server_methods, NULL, server_properties, server, destroy_sap_interface)) { error("D-Bus failed to register %s interface", SAP_SERVER_INTERFACE); goto server_err; } DBG("server %p, listen socket 0x%02x", server, g_io_channel_unix_get_fd(io)); return 0; server_err: server_remove(server); sdp_err: sap_exit(); return -1; } void sap_server_unregister(const char *path) { g_dbus_unregister_interface(btd_get_dbus_connection(), path, SAP_SERVER_INTERFACE); sap_exit(); } bluez-5.82/profiles/sap/PaxHeaders/main.c0000644000000000000000000000005014015011623015335 xustar0020 atime=1743516870 20 ctime=1743591284 bluez-5.82/profiles/sap/main.c0000644000000000000000000000100214015011623015007 0ustar00rootroot// SPDX-License-Identifier: GPL-2.0-or-later /* * BlueZ - Bluetooth protocol stack for Linux * * Copyright (C) 2010 Instituto Nokia de Tecnologia - INdT * */ #ifdef HAVE_CONFIG_H #include #endif #include #include "gdbus/gdbus.h" #include "src/plugin.h" #include "manager.h" static int sap_init(void) { return sap_manager_init(); } static void sap_exit(void) { sap_manager_exit(); } BLUETOOTH_PLUGIN_DEFINE(sap, VERSION, BLUETOOTH_PLUGIN_PRIORITY_DEFAULT, sap_init, sap_exit) bluez-5.82/profiles/sap/PaxHeaders/manager.h0000644000000000000000000000005014015011623016030 xustar0020 atime=1743516870 20 ctime=1743591284 bluez-5.82/profiles/sap/manager.h0000644000000000000000000000034214015011623015510 0ustar00rootroot/* SPDX-License-Identifier: GPL-2.0-or-later */ /* * BlueZ - Bluetooth protocol stack for Linux * * Copyright (C) 2010 Instituto Nokia de Tecnologia - INdT * */ int sap_manager_init(void); void sap_manager_exit(void); bluez-5.82/profiles/sap/PaxHeaders/sap-dummy.c0000644000000000000000000000005014015011623016325 xustar0020 atime=1743516870 20 ctime=1743591284 bluez-5.82/profiles/sap/sap-dummy.c0000644000000000000000000002112414015011623016006 0ustar00rootroot// SPDX-License-Identifier: GPL-2.0-or-later /* * BlueZ - Bluetooth protocol stack for Linux * * Copyright (C) 2010 ST-Ericsson SA * Copyright (C) 2011 Tieto Poland * * Author: Waldemar Rymarkiewicz * for ST-Ericsson * */ #ifdef HAVE_CONFIG_H #include #endif #include #include #include "gdbus/gdbus.h" #include "src/dbus-common.h" #include "src/error.h" #include "src/log.h" #include "sap.h" #define SAP_DUMMY_IFACE "org.bluez.SimAccessTest1" #define SAP_DUMMY_PATH "/org/bluez/test" enum { SIM_DISCONNECTED = 0x00, SIM_CONNECTED = 0x01, SIM_POWERED_OFF = 0x02, SIM_MISSING = 0x03 }; static unsigned int init_cnt = 0; static int sim_card_conn_status = SIM_DISCONNECTED; static void *sap_data = NULL; /* SAP server private data. */ static gboolean ongoing_call_status = FALSE; static int max_msg_size_supported = 512; void sap_connect_req(void *sap_device, uint16_t maxmsgsize) { DBG("status: %d", sim_card_conn_status); if (sim_card_conn_status != SIM_DISCONNECTED) { sap_connect_rsp(sap_device, SAP_STATUS_CONNECTION_FAILED); return; } if (max_msg_size_supported > maxmsgsize) { sap_connect_rsp(sap_device, SAP_STATUS_MAX_MSG_SIZE_TOO_SMALL); return; } if (max_msg_size_supported < maxmsgsize) { sap_connect_rsp(sap_device, SAP_STATUS_MAX_MSG_SIZE_NOT_SUPPORTED); return; } if (ongoing_call_status) { sap_connect_rsp(sap_device, SAP_STATUS_OK_ONGOING_CALL); return; } sim_card_conn_status = SIM_CONNECTED; sap_data = sap_device; sap_connect_rsp(sap_device, SAP_STATUS_OK); sap_status_ind(sap_device, SAP_STATUS_CHANGE_CARD_RESET); } void sap_disconnect_req(void *sap_device, uint8_t linkloss) { sim_card_conn_status = SIM_DISCONNECTED; sap_data = NULL; ongoing_call_status = FALSE; DBG("status: %d", sim_card_conn_status); if (linkloss) return; sap_disconnect_rsp(sap_device); } void sap_transfer_apdu_req(void *sap_device, struct sap_parameter *param) { char apdu[] = "APDU response!"; DBG("status: %d", sim_card_conn_status); switch (sim_card_conn_status) { case SIM_MISSING: sap_transfer_apdu_rsp(sap_device, SAP_RESULT_ERROR_CARD_REMOVED, NULL, 0); break; case SIM_POWERED_OFF: sap_transfer_apdu_rsp(sap_device, SAP_RESULT_ERROR_POWERED_OFF, NULL, 0); break; case SIM_DISCONNECTED: sap_transfer_apdu_rsp(sap_device, SAP_RESULT_ERROR_NOT_ACCESSIBLE, NULL, 0); break; case SIM_CONNECTED: sap_transfer_apdu_rsp(sap_device, SAP_RESULT_OK, (uint8_t *)apdu, sizeof(apdu)); break; } } void sap_transfer_atr_req(void *sap_device) { char atr[] = "ATR response!"; DBG("status: %d", sim_card_conn_status); switch (sim_card_conn_status) { case SIM_MISSING: sap_transfer_atr_rsp(sap_device, SAP_RESULT_ERROR_CARD_REMOVED, NULL, 0); break; case SIM_POWERED_OFF: sap_transfer_atr_rsp(sap_device, SAP_RESULT_ERROR_POWERED_OFF, NULL, 0); break; case SIM_DISCONNECTED: sap_transfer_atr_rsp(sap_device, SAP_RESULT_ERROR_NO_REASON, NULL, 0); break; case SIM_CONNECTED: sap_transfer_atr_rsp(sap_device, SAP_RESULT_OK, (uint8_t *)atr, sizeof(atr)); break; } } void sap_power_sim_off_req(void *sap_device) { DBG("status: %d", sim_card_conn_status); switch (sim_card_conn_status) { case SIM_MISSING: sap_power_sim_off_rsp(sap_device, SAP_RESULT_ERROR_CARD_REMOVED); break; case SIM_POWERED_OFF: sap_power_sim_off_rsp(sap_device, SAP_RESULT_ERROR_POWERED_OFF); break; case SIM_DISCONNECTED: sap_power_sim_off_rsp(sap_device, SAP_RESULT_ERROR_NO_REASON); break; case SIM_CONNECTED: sap_power_sim_off_rsp(sap_device, SAP_RESULT_OK); sim_card_conn_status = SIM_POWERED_OFF; break; } } void sap_power_sim_on_req(void *sap_device) { DBG("status: %d", sim_card_conn_status); switch (sim_card_conn_status) { case SIM_MISSING: sap_power_sim_on_rsp(sap_device, SAP_RESULT_ERROR_CARD_REMOVED); break; case SIM_POWERED_OFF: sap_power_sim_on_rsp(sap_device, SAP_RESULT_OK); sim_card_conn_status = SIM_CONNECTED; break; case SIM_DISCONNECTED: sap_power_sim_on_rsp(sap_device, SAP_RESULT_ERROR_NOT_ACCESSIBLE); break; case SIM_CONNECTED: sap_power_sim_on_rsp(sap_device, SAP_RESULT_ERROR_NO_REASON); break; } } void sap_reset_sim_req(void *sap_device) { DBG("status: %d", sim_card_conn_status); switch (sim_card_conn_status) { case SIM_MISSING: sap_reset_sim_rsp(sap_device, SAP_RESULT_ERROR_CARD_REMOVED); break; case SIM_POWERED_OFF: sap_reset_sim_rsp(sap_device, SAP_RESULT_ERROR_POWERED_OFF); break; case SIM_DISCONNECTED: sap_reset_sim_rsp(sap_device, SAP_RESULT_ERROR_NO_REASON); break; case SIM_CONNECTED: sap_reset_sim_rsp(sap_device, SAP_RESULT_OK); break; } } void sap_transfer_card_reader_status_req(void *sap_device) { DBG("status: %d", sim_card_conn_status); if (sim_card_conn_status != SIM_CONNECTED) { sap_transfer_card_reader_status_rsp(sap_device, SAP_RESULT_ERROR_NO_REASON, 0xF1); return; } sap_transfer_card_reader_status_rsp(sap_device, SAP_RESULT_OK, 0xF1); } void sap_set_transport_protocol_req(void *sap_device, struct sap_parameter *param) { sap_transport_protocol_rsp(sap_device, SAP_RESULT_NOT_SUPPORTED); } static DBusMessage *ongoing_call(DBusConnection *conn, DBusMessage *msg, void *data) { dbus_bool_t ongoing; if (!dbus_message_get_args(msg, NULL, DBUS_TYPE_BOOLEAN, &ongoing, DBUS_TYPE_INVALID)) return btd_error_invalid_args(msg); if (ongoing_call_status && !ongoing) { /* An ongoing call has finished. Continue connection.*/ sap_status_ind(sap_data, SAP_STATUS_CHANGE_CARD_RESET); ongoing_call_status = FALSE; } else if (!ongoing_call_status && ongoing) { /* An ongoing call has started.*/ ongoing_call_status = TRUE; } DBG("OngoingCall status set to %d", ongoing_call_status); return dbus_message_new_method_return(msg); } static DBusMessage *max_msg_size(DBusConnection *conn, DBusMessage *msg, void *data) { dbus_uint32_t size; if (sim_card_conn_status == SIM_CONNECTED) return btd_error_failed(msg, "Can't change msg size when connected."); if (!dbus_message_get_args(msg, NULL, DBUS_TYPE_UINT32, &size, DBUS_TYPE_INVALID)) return btd_error_invalid_args(msg); max_msg_size_supported = size; DBG("MaxMessageSize set to %d", max_msg_size_supported); return dbus_message_new_method_return(msg); } static DBusMessage *disconnect_immediate(DBusConnection *conn, DBusMessage *msg, void *data) { if (sim_card_conn_status == SIM_DISCONNECTED) return btd_error_failed(msg, "Already disconnected."); sim_card_conn_status = SIM_DISCONNECTED; sap_disconnect_ind(sap_data, SAP_DISCONNECTION_TYPE_IMMEDIATE); return dbus_message_new_method_return(msg); } static DBusMessage *card_status(DBusConnection *conn, DBusMessage *msg, void *data) { dbus_uint32_t status; DBG("status %d", sim_card_conn_status); if (sim_card_conn_status != SIM_CONNECTED) return btd_error_failed(msg, "Can't change msg size when not connected."); if (!dbus_message_get_args(msg, NULL, DBUS_TYPE_UINT32, &status, DBUS_TYPE_INVALID)) return btd_error_invalid_args(msg); switch (status) { case 0: /* card removed */ sim_card_conn_status = SIM_MISSING; sap_status_ind(sap_data, SAP_STATUS_CHANGE_CARD_REMOVED); break; case 1: /* card inserted */ if (sim_card_conn_status == SIM_MISSING) { sim_card_conn_status = SIM_CONNECTED; sap_status_ind(sap_data, SAP_STATUS_CHANGE_CARD_INSERTED); } break; case 2: /* card not longer available*/ sim_card_conn_status = SIM_POWERED_OFF; sap_status_ind(sap_data, SAP_STATUS_CHANGE_CARD_NOT_ACCESSIBLE); break; default: return btd_error_failed(msg, "Unknown card status. Use 0, 1 or 2."); } DBG("Card status changed to %d", status); return dbus_message_new_method_return(msg); } static const GDBusMethodTable dummy_methods[] = { { GDBUS_EXPERIMENTAL_METHOD("OngoingCall", GDBUS_ARGS({ "ongoing", "b" }), NULL, ongoing_call) }, { GDBUS_EXPERIMENTAL_METHOD("MaxMessageSize", GDBUS_ARGS({ "size", "u" }), NULL, max_msg_size) }, { GDBUS_EXPERIMENTAL_METHOD("DisconnectImmediate", NULL, NULL, disconnect_immediate) }, { GDBUS_EXPERIMENTAL_METHOD("CardStatus", GDBUS_ARGS({ "status", "" }), NULL, card_status) }, { } }; int sap_init(void) { if (init_cnt++) return 0; if (g_dbus_register_interface(btd_get_dbus_connection(), SAP_DUMMY_PATH, SAP_DUMMY_IFACE, dummy_methods, NULL, NULL, NULL, NULL) == FALSE) { init_cnt--; return -1; } return 0; } void sap_exit(void) { if (--init_cnt) return; g_dbus_unregister_interface(btd_get_dbus_connection(), SAP_DUMMY_PATH, SAP_DUMMY_IFACE); } bluez-5.82/profiles/sap/PaxHeaders/sap.h0000644000000000000000000000005014015011623015201 xustar0020 atime=1743516870 20 ctime=1743591284 bluez-5.82/profiles/sap/sap.h0000644000000000000000000001210414015011623014660 0ustar00rootroot/* SPDX-License-Identifier: GPL-2.0-or-later */ /* * BlueZ - Bluetooth protocol stack for Linux * * Copyright (C) 2010 Instituto Nokia de Tecnologia - INdT * Copyright (C) 2010 ST-Ericsson SA * * Author: Marek Skowron for ST-Ericsson. * Author: Waldemar Rymarkiewicz * for ST-Ericsson. * */ #include #include #ifdef SAP_DEBUG #define SAP_VDBG(fmt, arg...) DBG(fmt, arg) #else #define SAP_VDBG(fmt...) #endif #define SAP_VERSION 0x0101 /* Connection Status - SAP v1.1 section 5.2.2 */ enum sap_status { SAP_STATUS_OK = 0x00, SAP_STATUS_CONNECTION_FAILED = 0x01, SAP_STATUS_MAX_MSG_SIZE_NOT_SUPPORTED = 0x02, SAP_STATUS_MAX_MSG_SIZE_TOO_SMALL = 0x03, SAP_STATUS_OK_ONGOING_CALL = 0x04 }; /* Disconnection Type - SAP v1.1 section 5.2.3 */ enum sap_disconnection_type { SAP_DISCONNECTION_TYPE_GRACEFUL = 0x00, SAP_DISCONNECTION_TYPE_IMMEDIATE = 0x01 }; /* Result codes - SAP v1.1 section 5.2.4 */ enum sap_result { SAP_RESULT_OK = 0x00, SAP_RESULT_ERROR_NO_REASON = 0x01, SAP_RESULT_ERROR_NOT_ACCESSIBLE = 0x02, SAP_RESULT_ERROR_POWERED_OFF = 0x03, SAP_RESULT_ERROR_CARD_REMOVED = 0x04, SAP_RESULT_ERROR_POWERED_ON = 0x05, SAP_RESULT_ERROR_NO_DATA = 0x06, SAP_RESULT_NOT_SUPPORTED = 0x07 }; /* Status Change - SAP v1.1 section 5.2.8 */ enum sap_status_change { SAP_STATUS_CHANGE_UNKNOWN_ERROR = 0x00, SAP_STATUS_CHANGE_CARD_RESET = 0x01, SAP_STATUS_CHANGE_CARD_NOT_ACCESSIBLE = 0x02, SAP_STATUS_CHANGE_CARD_REMOVED = 0x03, SAP_STATUS_CHANGE_CARD_INSERTED = 0x04, SAP_STATUS_CHANGE_CARD_RECOVERED = 0x05 }; /* Message format - SAP v1.1 section 5.1 */ struct sap_parameter { uint8_t id; uint8_t reserved; uint16_t len; uint8_t val[]; /* * Padding bytes 0-3 bytes */ } __attribute__((packed)); struct sap_message { uint8_t id; uint8_t nparam; uint16_t reserved; struct sap_parameter param[]; } __attribute__((packed)); #define SAP_BUF_SIZE 512 #define SAP_MSG_HEADER_SIZE 4 enum sap_protocol { SAP_CONNECT_REQ = 0x00, SAP_CONNECT_RESP = 0x01, SAP_DISCONNECT_REQ = 0x02, SAP_DISCONNECT_RESP = 0x03, SAP_DISCONNECT_IND = 0x04, SAP_TRANSFER_APDU_REQ = 0x05, SAP_TRANSFER_APDU_RESP = 0x06, SAP_TRANSFER_ATR_REQ = 0x07, SAP_TRANSFER_ATR_RESP = 0x08, SAP_POWER_SIM_OFF_REQ = 0x09, SAP_POWER_SIM_OFF_RESP = 0x0A, SAP_POWER_SIM_ON_REQ = 0x0B, SAP_POWER_SIM_ON_RESP = 0x0C, SAP_RESET_SIM_REQ = 0x0D, SAP_RESET_SIM_RESP = 0x0E, SAP_TRANSFER_CARD_READER_STATUS_REQ = 0x0F, SAP_TRANSFER_CARD_READER_STATUS_RESP = 0x10, SAP_STATUS_IND = 0x11, SAP_ERROR_RESP = 0x12, SAP_SET_TRANSPORT_PROTOCOL_REQ = 0x13, SAP_SET_TRANSPORT_PROTOCOL_RESP = 0x14 }; /* Parameters Ids - SAP 1.1 section 5.2 */ enum sap_param_id { SAP_PARAM_ID_MAX_MSG_SIZE = 0x00, SAP_PARAM_ID_CONN_STATUS = 0x01, SAP_PARAM_ID_RESULT_CODE = 0x02, SAP_PARAM_ID_DISCONNECT_IND = 0x03, SAP_PARAM_ID_COMMAND_APDU = 0x04, SAP_PARAM_ID_COMMAND_APDU7816 = 0x10, SAP_PARAM_ID_RESPONSE_APDU = 0x05, SAP_PARAM_ID_ATR = 0x06, SAP_PARAM_ID_CARD_READER_STATUS = 0x07, SAP_PARAM_ID_STATUS_CHANGE = 0x08, SAP_PARAM_ID_TRANSPORT_PROTOCOL = 0x09 }; #define SAP_PARAM_ID_MAX_MSG_SIZE_LEN 0x02 #define SAP_PARAM_ID_CONN_STATUS_LEN 0x01 #define SAP_PARAM_ID_RESULT_CODE_LEN 0x01 #define SAP_PARAM_ID_DISCONNECT_IND_LEN 0x01 #define SAP_PARAM_ID_CARD_READER_STATUS_LEN 0x01 #define SAP_PARAM_ID_STATUS_CHANGE_LEN 0x01 #define SAP_PARAM_ID_TRANSPORT_PROTO_LEN 0x01 /* Transport Protocol - SAP v1.1 section 5.2.9 */ enum sap_transport_protocol { SAP_TRANSPORT_PROTOCOL_T0 = 0x00, SAP_TRANSPORT_PROTOCOL_T1 = 0x01 }; /*SAP driver init and exit routines. Implemented by sap-*.c */ int sap_init(void); void sap_exit(void); /* SAP requests implemented by sap-*.c */ void sap_connect_req(void *sap_device, uint16_t maxmsgsize); void sap_disconnect_req(void *sap_device, uint8_t linkloss); void sap_transfer_apdu_req(void *sap_device, struct sap_parameter *param); void sap_transfer_atr_req(void *sap_device); void sap_power_sim_off_req(void *sap_device); void sap_power_sim_on_req(void *sap_device); void sap_reset_sim_req(void *sap_device); void sap_transfer_card_reader_status_req(void *sap_device); void sap_set_transport_protocol_req(void *sap_device, struct sap_parameter *param); /*SAP responses to SAP requests. Implemented by server.c */ int sap_connect_rsp(void *sap_device, uint8_t status); int sap_disconnect_rsp(void *sap_device); int sap_transfer_apdu_rsp(void *sap_device, uint8_t result, uint8_t *sap_apdu_resp, uint16_t length); int sap_transfer_atr_rsp(void *sap_device, uint8_t result, uint8_t *sap_atr, uint16_t length); int sap_power_sim_off_rsp(void *sap_device, uint8_t result); int sap_power_sim_on_rsp(void *sap_device, uint8_t result); int sap_reset_sim_rsp(void *sap_device, uint8_t result); int sap_transfer_card_reader_status_rsp(void *sap_device, uint8_t result, uint8_t status); int sap_transport_protocol_rsp(void *sap_device, uint8_t result); /* Event indication. Implemented by server.c*/ int sap_status_ind(void *sap_device, uint8_t status_change); int sap_disconnect_ind(void *sap_device, uint8_t disc_type); bluez-5.82/profiles/sap/PaxHeaders/manager.c0000644000000000000000000000005014015011623016023 xustar0020 atime=1743516870 20 ctime=1743591284 bluez-5.82/profiles/sap/manager.c0000644000000000000000000000212414015011623015503 0ustar00rootroot// SPDX-License-Identifier: GPL-2.0-or-later /* * BlueZ - Bluetooth protocol stack for Linux * * Copyright (C) 2010 Instituto Nokia de Tecnologia - INdT * */ #ifdef HAVE_CONFIG_H #include #endif #include #include "lib/bluetooth.h" #include "lib/sdp.h" #include "src/log.h" #include "src/adapter.h" #include "src/device.h" #include "src/profile.h" #include "src/service.h" #include "manager.h" #include "server.h" static int sap_server_probe(struct btd_profile *p, struct btd_adapter *adapter) { DBG("path %s", adapter_get_path(adapter)); return sap_server_register(adapter); } static void sap_server_remove(struct btd_profile *p, struct btd_adapter *adapter) { const char *path = adapter_get_path(adapter); DBG("path %s", path); sap_server_unregister(path); } static struct btd_profile sap_profile = { .name = "sap-server", .adapter_probe = sap_server_probe, .adapter_remove = sap_server_remove, }; int sap_manager_init(void) { btd_profile_register(&sap_profile); return 0; } void sap_manager_exit(void) { btd_profile_unregister(&sap_profile); } bluez-5.82/profiles/sap/PaxHeaders/server.h0000644000000000000000000000005014015011623015724 xustar0020 atime=1743516870 20 ctime=1743591284 bluez-5.82/profiles/sap/server.h0000644000000000000000000000036714015011623015413 0ustar00rootroot/* SPDX-License-Identifier: GPL-2.0-or-later */ /* * BlueZ - Bluetooth protocol stack for Linux * * Copyright (C) 2010 ST-Ericsson SA * */ int sap_server_register(struct btd_adapter *adapter); void sap_server_unregister(const char *path); bluez-5.82/PaxHeaders/android0000644000000000000000000000005014773213571013223 xustar0020 atime=1743591291 20 ctime=1743591289 bluez-5.82/android/0000755000000000000000000000000014773213571012761 5ustar00rootrootbluez-5.82/android/PaxHeaders/pts-map.txt0000644000000000000000000000005012537515745015426 xustar0020 atime=1743516876 20 ctime=1743591289 bluez-5.82/android/pts-map.txt0000644000000000000000000000440512537515745015112 0ustar00rootrootPTS test results for MAP PTS version: 6.1 Tested: 29-May-2015 Android version: 5.1 Results: PASS test passed FAIL test failed INC test is inconclusive N/A test is disabled due to PICS setup ------------------------------------------------------------------------------- Test Name Result Notes ------------------------------------------------------------------------------- TC_MCE_MSM_BV_01_I N/A TC_MCE_MSM_BV_02_I N/A TC_MCE_MSM_BV_03_I N/A TC_MCE_MSM_BV_04_I N/A TC_MCE_MSM_BV_13_I N/A TC_MCE_MSM_BV_14_I N/A TC_MCE_MNR_BV_01_I N/A TC_MCE_MNR_BV_02_I N/A TC_MCE_MMB_BV_01_I N/A TC_MCE_MMB_BV_02_I N/A TC_MCE_MMB_BV_03_I N/A TC_MCE_MMB_BV_19_I N/A TC_MCE_MMB_BV_04_I N/A TC_MCE_MMB_BV_17_I N/A TC_MCE_MMB_BV_06_I N/A TC_MCE_MMB_BV_07_I N/A TC_MCE_MMB_BV_08_I N/A TC_MCE_MMD_BV_01_I N/A TC_MCE_MMU_BV_01_I N/A TC_MCE_MMN_BV_01_I N/A TC_MCE_MMN_BV_03_I N/A TC_MCE_MMI_BV_01_I N/A TC_MCE_MFB_BV_01_I N/A TC_MCE_MFB_BV_03_I N/A TC_MCE_MFB_BV_04_I N/A TC_MCE_BC_BV_02_I N/A TC_MCE_BC_BV_04_I N/A TC_MCE_CON_BV_01_I N/A TC_MCE_CON_BV_02_I N/A TC_MCE_ROB_BV_01_I N/A TC_MCE_SRM_BV_03_I N/A TC_MCE_SRM_BV_07_I N/A TC_MCE_SRMP_BI_01_I N/A TC_MCE_SRMP_BV_01_I N/A TC_MCE_SRMP_BV_04_I N/A TC_MCE_SRMP_BV_05_I N/A TC_MCE_SRMP_BV_06_I N/A TC_MSE_MSM_BV_05_I PASS TC_MSE_MSM_BV_06_I PASS TC_MSE_MSM_BV_07_I PASS TC_MSE_MSM_BV_08_I PASS TC_MSE_MSM_BV_09_I N/A TC_MSE_MSM_BV_10_I N/A TC_MSE_MSM_BV_11_I N/A TC_MSE_MSM_BV_12_I N/A TC_MSE_MNR_BV_03_I PASS TC_MSE_MNR_BV_04_I PASS TC_MSE_MMB_BV_09_I PASS TC_MSE_MMB_BV_10_I PASS TC_MSE_MMB_BV_11_I PASS TC_MSE_MMB_BV_20_I PASS TC_MSE_MMB_BV_12_I N/A TC_MSE_MMB_BV_18_I PASS TC_MSE_MMB_BV_13_I PASS TC_MSE_MMB_BV_14_I PASS TC_MSE_MMB_BV_15_I PASS TC_MSE_MMB_BV_16_I PASS TC_MSE_MMD_BV_02_I PASS TC_MSE_MMU_BV_02_I PASS TC_MSE_MMU_BV_03_I PASS TC_MSE_MMN_BV_02_I INC JIRA #BA-380 TC_MSE_MMN_BV_04_I N/A TC_MSE_MMI_BV_02_I N/A TC_MSE_MFB_BV_02_I N/A TC_MSE_MFB_BV_05_I N/A TC_MSE_BC_BV_01_I N/A TC_MSE_BC_BV_03_I N/A TC_MSE_CON_BV_01_I N/A TC_MSE_CON_BV_02_I N/A TC_MSE_ROB_BV_01_I N/A TC_MSE_ROB_BV_02_I N/A TC_MSE_SRM_BI_02_I N/A TC_MSE_SRM_BI_03_I N/A TC_MSE_SRM_BI_05_I N/A TC_MSE_SRM_BV_04_I N/A TC_MSE_SRM_BV_08_I N/A TC_MSE_SRMP_BI_02_I N/A TC_MSE_SRMP_BV_02_I N/A TC_MSE_SRMP_BV_03_I N/A ------------------------------------------------------------------------------- bluez-5.82/android/PaxHeaders/hal-ipc-api.txt0000644000000000000000000000005012572170650016120 xustar0020 atime=1743516875 20 ctime=1743591288 bluez-5.82/android/hal-ipc-api.txt0000644000000000000000000024753512572170650015621 0ustar00rootrootAndroid HAL protocol for Bluetooth ================================== The Android HAL daemon for Bluetooth functionality implements the Unix socket server protocol around /run/bluetooth/daemon (tentative location) or Linux abstract sockets (tentative name). The daemon is single threaded and uses a mainloop for scheduling and general operation. The protocol is SOCK_SEQPACKET based and follows a strict PDU specification with a generic header and initial registration exchange. The communication is driven from the HAL with command/response exchanges. The daemon will use notification to signal events. The protocol is single PDU exchanged based, meaning every command requires a response. Notification does not require any confirmation. Not handling this PDU exchange leads to a disconnection of the socket. Command/response and notification use separate sockets. First connected socket is used for command/response, second for notification. All services are multi-plexed over same pair of sockets. Separation is done to ease implementation of simple HAL library with dedicated thread for handling notification. This strict protocol requirement is done to match C based callbacks and callout functions that are running in a thread inside the HAL and might block. .--Android--. .--Android--. | daemon | | HAL | | | Command | | | | <-------------------------- | | | | | | | | --------------------------> | | | | Response | | | | | | | | | | | | Notification | | | | --------------------------> | | | | | | '-----------' '-----------' Every packet will follow the basic header to support simple multi-plexing over the same socket. It will also support a basic control channel with service id 0. 0 8 16 24 31 +--------------+--------------+--------------+--------------+ | Service ID | Opcode | Data Length | +--------------+--------------+-----------------------------+ | | The unique service ID is assigned by this specification for each HAL. As general rule of thumb, the opcode for command matches the opcode for a response. Or the opcode 0x00 for an error is returned. Notification opcodes start from 0x81. Opcode 0x80 is reserved and shall not be used. All command/response opcodes have the least significant bit not set. And all notifications have the least significant bit set. The HAL modules only have the job to map the callback and event functions to the protocol. They do not need to do anything else. Below is an example of a sample transaction for the Bluetooth Core HAL and enabling of an adapter. HAL Daemon ---------------------------------------------------- call enable() --> command 0x01 return enable() <-- response 0x01 call adapter_state_changed() <-- notification 0x81 return adapter_state_changed() When the Android hardware framework calls into the Bluetooth Core HAL and executes the enable() callback, the HAL module sends the enable command with opcode 0x01 to the daemon. As soon as the daemon responds, the callback will return with the appropriate result. After the daemon switched on the adapter, it will send a notification with opcode 0x81 to the HAL module. The Bluetooth Core HAL and Bluetooth Socket HAL are guaranteed to be available from the daemon. All other HAL modules are optional. When the Bluetooth Core HAL init() function is called, it should open the socket and register both "bluetooth" and "socket" service modules. It is required to register "socket" service at the same time since the HAL module does not have its own init() function. It is possible to send Configure command before registering any services to customize stack. This step is optional. When new profiles are initiated, the get_profile_interface() callback will load the profile and during init() of the profile, it should register the specific service. Bluetooth main thread Daemon ------------------------------------------------------- init() --> open command socket --> open notification socket --> register module "bluetooth" --> register module "socket" get_profile_interface() --> return profile struct --> continue on Handsfree thread Handsfree thread Daemon -------------------------------------------------------- init() --> register module handsfree Error response is common for all services and has fixed structure: Opcode 0x00 - Error response Response parameters: Status (1 octet) Valid status values: 0x01 = Fail 0x02 = Not ready 0x03 = No memory 0x04 = Busy 0x05 = Done (already completed) 0x06 = Unsupported 0x07 = Parameter invalid 0x08 = Unhandled 0x09 = Authentication failure 0x0a = Remote device down 0x0b = Authentication rejected Core Service (ID 0) =================== Opcode 0x00 - Error response Opcode 0x01 - Register module command/response Command parameters: Service id (1 octet) Mode (1 octet) Max Clients (4 octets) Response parameters: In case a command is sent for an undeclared service ID, it will be rejected. Also there will be no notifications for undeclared service ID. Valid Mode values: 0x00 = Default Mode 0xXX = as defined by service In case of an error, the error response will be returned. Opcode 0x02 - Unregister module command/response Command parameters: Service id (1 octet) Response parameters: In case of an error, the error response will be returned. Opcode 0x03 - Configuration Command parameters: Num options (1 octet) Option Type # (1 octet) Option Length # (2 octets) Option Value # (variable) Response parameters: Valid configure option types: 0x00 = Vendor 0x01 = Model 0x02 = Name 0x03 = Serial Number 0x04 = System ID 0x05 = PnP ID 0x06 = Firmware Rev 0x07 = Hardware Rev In case of an error, the error response will be returned. Bluetooth Core HAL (ID 1) ========================= Android HAL name: "bluetooth" (BT_HARDWARE_MODULE_ID) Service modes: 0x00 = Enable BR/EDR/LE if supported (default) 0x01 = Enable BR/EDR only 0x02 = Enable LE only Commands and responses: Opcode 0x00 - Error response Opcode 0x01 - Enable command/response Command parameters: Response parameters: In case of an error, the error response will be returned. Opcode 0x02 - Disable command/response Command parameters: Response parameters: In case of an error, the error response will be returned. Opcode 0x03 - Get Adapter Properties command/response Command parameters: Response parameters: In case of an error, the error response will be returned. Opcode 0x04 - Get Adapter Property command/response Command parameters: Property type (1 octet) Response parameters: In case of an error, the error response will be returned. Opcode 0x05 - Set Adapter Property command/response Command parameters: Property type (1 octet) Property length (2 octets) Property value (variable) Response parameters: In case of an error, the error response will be returned. Opcode 0x06 - Get Remote Device Properties command/response Command parameters: Remote address (6 octets) Response parameters: In case of an error, the error response will be returned. Opcode 0x07 - Get Remote Device Property command/response Command parameters: Remote address (6 octets) Property type (1 octet) Response parameters: In case of an error, the error response will be returned. Opcode 0x08 - Set Remote Device Property command/response Command parameters: Remote address (6 octets) Property type (1 octet) Property length (2 octets) Property value (variable) Response parameters: In case of an error, the error response will be returned. Opcode 0x09 - Get Remote Service Record command/response Command parameters: Remote address (6 octets) UUID (16 octets) Response parameters: In case of an error, the error response will be returned. Opcode 0x0a - Get Remote Services command/response Command parameters: Remote address (6 octets) Response parameters: In case of an error, the error response will be returned. Opcode 0x0b - Start Discovery command/response Command parameters: Response parameters: In case of an error, the error response will be returned. Opcode 0x0c - Cancel Discovery command/response Command parameters: Response parameters: In case of an error, the error response will be returned. Opcode 0x0d - Create Bond command/response Command parameters: Remote address (6 octets) Transport (1 octet) Response parameters: In case of an error, the error response will be returned. Opcode 0x0e - Remove Bond command/response Command parameters: Remote address (6 octets) Response parameters: In case of an error, the error response will be returned. Opcode 0x0f - Cancel Bond command/response Command parameters: Remote address (6 octets) Response parameters: In case of an error, the error response will be returned. Opcode 0x10 - PIN Reply command/response Command parameters: Remote address (6 octets) Accept (1 octet) PIN length (1 octet) PIN code (16 octets) Response parameters: In case of an error, the error response will be returned. Opcode 0x11 - SSP Reply command/response Command parameters: Remote address (6 octets) SSP variant (1 octet) Accept (1 octet) Passkey (4 octets) Response parameters: Valid SSP variant values: 0x00 = Passkey Confirmation 0x01 = Passkey Entry 0x02 = Consent (for Just Works) 0x03 = Passkey Notification In case of an error, the error response will be returned. Opcode 0x12 - DUT Mode Configure command/response Command parameters: Enable (1 octet) Response parameters: In case of an error, the error response will be returned. Opcode 0x13 - DUT Mode Send command/response Command parameters: Opcode (2 octets) Length (1 octet) Data (variable) Response parameters: In case of an error, the error response will be returned. Opcode 0x14 - LE Test Mode command/response Command parameters: Opcode (2 octets) Length (1 octet) Data (variable) Response parameters: In case of an error, the error response will be returned. Notifications: Opcode 0x81 - Adapter State Changed notification Notifications parameters: State (1 octet) Valid state values: 0x00 = Off 0x01 = On Opcode 0x82 - Adapter Properties Changed notification Notification parameters: Status (1 octet) Num properties (1 octet) Type # (1 octet) Length # (2 octets) Value # (variable) ... Opcode 0x83 - Remote Device Properties notification Notification parameters: Status (1 octet) Remote address (6 octets) Num properties (1 octet) Type # (1 octet) Length # (2 octets) Value # (variable) ... Opcode 0x84 - Device Found notification Notification parameters: Num properties (1 octet) Type # (1 octet) Length # (2 octets) Value # (variable) ... Opcode 0x85 - Discovery State Changed notification Notifications parameters: State (1 octet) Opcode 0x86 - PIN Request notification Notification parameters: Remote address (6 octets) Remote name (249 octets) Class of device (4 octets) Opcode 0x87 - SSP Request notification Notification parameters: Remote address (6 octets) Remote name (249 octets) Class of device (4 octets) Pairing variant (1 octet) Passkey (4 octets) Opcode 0x88 - Bond State Changed notification Notification parameters: Status (1 octet) Remote address (6 octets) Bond state (1 octet) Valid bond state values: 0x00 = None 0x01 = Bonding 0x02 = Bonded Opcode 0x89 - ACL State Changed notification Notification parameters: Status (1 octet) Remote address (6 octets) ACL state (1 octet) Opcode 0x8a - DUT Mode Receive notification Notification parameters: Opcode (2 octets) Length (1 octet) Data (variable) Opcode 0x8b - LE Test Mode notification Notification parameters: Status (1 octet) Num packets (2 octets) Bluetooth Socket HAL (ID 2) =========================== Android HAL name:: "socket" (BT_PROFILE_SOCKETS_ID) Commands and responses: Opcode 0x00 - Error response Opcode 0x01 - Listen command/response Command parameters: Socket type (1 octet) Service name (256 octets) Service UUID (16 octets) Channel (4 octets) Socket flags (1 octet) Response parameters: File descriptor (inline) Valid socket types: 0x01 = RFCOMM 0x02 = SCO 0x03 = L2CAP Valid socket flags: 0x01 = Encrypt 0x02 = Auth In case of an error, the error response will be returned. Opcode 0x02 - Connect command/response Command parameters: Remote address (6 octets) Socket type (1 octet) Service UUID (16 octets) Channel (4 octets) Socket flags (1 octet) Response parameters: File descriptor (inline) Valid socket types: 0x01 = RFCOMM 0x02 = SCO 0x03 = L2CAP Valid socket flags: 0x01 = Encrypt 0x02 = Auth In case of an error, the error response will be returned. Bluetooth HID Host HAL (ID 3) ============================ Android HAL name: "hidhost" (BT_PROFILE_HIDHOST_ID) Commands and responses: Opcode 0x00 - Error response Opcode 0x01 - Connect command/response Command parameters: Remote address (6 octets) Response parameters: In case of an error, the error response will be returned. Opcode 0x02 - Disconnect command/response Command parameters: Remote address (6 octets) Response parameters: In case of an error, the error response will be returned. Opcode 0x03 - Virtual Unplug command/response Command parameters: Remote address (6 octets) Response parameters: In case of an error, the error response will be returned. Opcode 0x04 - Set Info command/response Command parameters: Remote address (6 octets) Attribute mask (2 octets) Subclass (1 octet) Application ID (1 octet) Vendor ID (2 octets) Product ID (2 octets) Version (2 octets) Country code (1 octet) Descriptor length (2 octet) Descriptor value (884 octets) Response parameters: In case of an error, the error response will be returned. Opcode 0x05 - Get Protocol command/response Command parameters: Remote address (6 octets) Protocol mode (1 octet) Response parameters: Valid protocol modes: 0x00 = Report 0x01 = Boot 0xff = Unsupported In case of an error, the error response will be returned. Opcode 0x06 - Set Protocol command/response Command parameters: Remote address (6 octets) Protocol mode (1 octet) Response parameters: Valid protocol modes: 0x00 = Report 0x01 = Boot 0xff = Unsupported In case of an error, the error response will be returned. Opcode 0x07 - Get Report command/response Command parameters: Remote address (6 octets) Report type (1 octet) Report ID (1 octet) Buffer size (2 octet) Response parameters: Valid report types: 0x01 = Input 0x02 = Output 0x03 = Feature In case of an error, the error response will be returned. Opcode 0x08 - Set Report command/response Command parameters: Remote address (6 octets) Report type (1 octet) Report length (2 octets) Report data (Report length) Response parameters: Valid report types: 0x01 = Input 0x02 = Output 0x03 = Feature In case of an error, the error response will be returned. Opcode 0x09 - Send Data command/response Command parameters: Remote address (6 octets) Data length (2 octets) Data (Data length) Response parameters: In case of an error, the error response will be returned. Notifications: Status is common for many notifications and has fixed value range: Status values: 0x00 = Ok 0x01 = Handshake - Device not ready 0x02 = Handshake - Invalid report ID 0x03 = Handshake - Transaction not SPT 0x04 = Handshake - Invalid parameter 0x05 = Handshake - Generic error 0x06 = General error 0x07 = SDP error 0x08 = Set protocol error 0x09 = Device database full 0x0a = Device type not supported 0x0b = No resources 0x0c = Authentication failed 0x0d = HDL Opcode 0x81 - Connection State notification Notification parameters: Remote address (6 octets) Connection State (1 octets) Valid connection states: 0x00 = Connected 0x01 = Connecting 0x02 = Disconnected 0x03 = Disconnecting 0x04 = Failed - Mouse from host 0x05 = Failed - Keyboard from host 0x06 = Failed - Too many devices 0x07 = Failed - No HID driver 0x08 = Failed - generic 0x09 = Unknown Opcode 0x82 - HID Info notification Notification parameters: Remote address (6 octets) Attribute mask (2 octets) Subclass (1 octet) Application ID (1 octet) Vendor ID (2 octets) Product ID (2 octets) Version (2 octets) Country code (1 octet) Descriptor length (2 octet) Descriptor value (884 octets) Opcode 0x83 - Protocol Mode notification Notification parameters: Remote address (6 octets) Status (1 octet) Protocol mode (1 octet) Valid protocol modes: 0x00 = Report 0x01 = Boot 0xff = Unsupported Opcode 0x84 - Idle Time notification Notification parameters: Remote address (6 octets) Status (1 octet) Idle time (2 octets) Opcode 0x85 - Get Report notification Notification parameters: Remote address (6 octets) Status (1 octet) Report length (2 octets) Report data (variable) Opcode 0x86 - Virtual Unplug notification Notification parameters: Remote address (6 octets) Status (1 octet) Opcode 0x87 - Handshake notification Notification parameters: Remote address (6 octets) Status (1 octet) Only status values from 0x00 to 0x05 are valid as handshake status. Bluetooth PAN HAL (ID 4) ======================== Android HAL name: "pan" (BT_PROFILE_PAN_ID) Commands and responses: Opcode 0x00 - Error response Opcode 0x01 - Enable command/response Command parameters: Local role (1 octet) Response parameters: Valid role values: 0x00 = None 0x01 = NAP 0x02 = PANU In case of an error, the error response will be returned. Opcode 0x02 - Get Local Role command/response Command parameters: Response parameters: Local role (1 octet) Valid role values: 0x00 = None 0x01 = NAP 0x02 = PANU In case of an error, the error response will be returned. Opcode 0x03 - Connect command/response Command parameters: Remote address (6 octets) Local role (1 octet) Remote role (1 octet) Response parameters: Valid role values: 0x01 = NAP 0x02 = PANU In case of an error, the error response will be returned. Opcode 0x04 - Disconnect command/response Command parameters: Remote address (6 octets) Response parameters: In case of an error, the error response will be returned. Notifications: Opcode 0x81 - Control State notification Notification parameters: Control state (1 octet) Status (1 octet) Local role (1 octet) Interface name (17 octet) Valid control states: 0x00 = Enabled 0x01 = Disabled Valid role values: 0x00 = None 0x01 = NAP 0x02 = PANU Opcode 0x82 - Connection State notification Notification parameters: Connection state (1 octet) Status (1 octet) Remote address (6 octets) Local role (1 octet) Remote role (1 octet) Valid connection states: 0x00 = Connected 0x01 = Connecting 0x02 = Disconnected 0x03 = Disconnecting Valid role values: 0x01 = NAP 0x02 = PANU Bluetooth Handsfree HAL (ID 5) ============================== Android HAL name: "handsfree" (BT_PROFILE_HANDSFREE_ID) Service modes: 0x00 = Headset Profile only mode (default) 0x01 = Handsfree Profile (narrowband speech) 0x02 = Handsfree Profile (narrowband and wideband speech) Commands and responses: Opcode 0x00 - Error response Opcode 0x01 - Connect command/response Command parameters: Remote address (6 octets) Response parameters: In case of an error, the error response will be returned. Opcode 0x02 - Disconnect command/response Command parameters: Remote address (6 octets) Response parameters: In case of an error, the error response will be returned. Opcode 0x03 - Connect Audio command/response Command parameters: Remote address (6 octets) Response parameters: In case of an error, the error response will be returned. Opcode 0x04 - Disconnect Audio command/response Command parameters: Remote address (6 octets) Response parameters: In case of an error, the error response will be returned. Opcode 0x05 - Start Voice Recognition command/response Command parameters: Remote address (6 octets) Response parameters: In case of an error, the error response will be returned. Opcode 0x06 - Stop Voice Recognition command/response Command parameters: Remote address (6 octets) Response parameters: In case of an error, the error response will be returned. Opcode 0x07 - Volume Control command/response Command parameters: Volume type (1 octet) Volume (1 octet) Remote address (6 octets) Response parameters: Valid volume types: 0x00 = Speaker 0x01 = Microphone In case of an error, the error response will be returned. Opcode 0x08 - Device Status Notification command/response Command parameters: Network state (1 octet) Service type (1 octet) Signal strength (1 octet) Battery level (1 octet) Response parameters: Valid network states: 0x00 = Not available 0x01 = Available Valid service types: 0x00 = Home network 0x01 = Roaming network In case of an error, the error response will be returned. Opcode 0x09 - COPS Response command/response Command parameters: COPS command response (string) Remote address (6 octets) Response parameters: In case of an error, the error response will be returned. Opcode 0x0a - CIND Response command/response Command parameters: Service (1 octet) Number of active calls (1 octet) Number of held calls (1 octet) Call setup state (1 octet) Signal strength (1 octet) Roaming indicator (1 octet) Battery level (1 octet) Remote address (6 octets) Response parameters: Valid call setup states: 0x00 = Active 0x01 = Held 0x02 = Dialing 0x03 = Alerting 0x04 = Incoming 0x05 = Waiting 0x06 = Idle In case of an error, the error response will be returned. Opcode 0x0b - Formatted AT Response command/response Command parameters: Pre-formatted AT response (string) Remote address (6 octets) Response parameters: In case of an error, the error response will be returned. Opcode 0x0c - AT Response command/response Command parameters: Response code (1 octet) Error code (1 octet) Remote address (6 octets) Response parameters: Valid response codes: 0x00 = ERROR 0x01 = OK In case of an error, the error response will be returned. Opcode 0x0d - CLCC Response command/response Command parameters: Call index (1 octet) Call direction (1 octet) Call state (1 octet) Call mode (1 octet) Call multiparty type (1 octet) Call number type (1 octet) Call number (string) Remote address (6 octets) Response parameters: Valid call directions: 0x00 = Outgoing 0x01 = Incoming Valid call states: 0x00 = Active 0x01 = Held 0x02 = Dialing 0x03 = Alerting 0x04 = Incoming 0x05 = Waiting 0x06 = Idle Valid call modes: 0x00 = Voice 0x01 = Data 0x02 = Fax Valid multiparty types: 0x00 = Single call 0x01 = Multiparty call Valid number types: 0x81 = Unknown 0x91 = International In case of an error, the error response will be returned. Opcode 0x0e - Phone Status Change command/response Command parameters: Number of active calls (1 octet) Number of held calls (1 octet) Call setup state (1 octet) Call number type (1 octet) Call number (string) Response parameters: Valid call setup states: 0x00 = Active 0x01 = Held 0x02 = Dialing 0x03 = Alerting 0x04 = Incoming 0x05 = Waiting 0x06 = Idle Valid number types: 0x81 = Unknown 0x91 = International In case of an error, the error response will be returned. Opcode 0x0f - Configure WBS command/response Command parameters: Remote address (6 octets) Config (1 octet) Response parameters: Valid config values: 0x00 = None 0x01 = No 0x02 = Yes In case of an error, the error response will be returned. Notifications: Opcode 0x81 - Connection State notification Notification parameters: Connection state (1 octet) Remote address (6 octets) Valid connection states: 0x00 = Disconnected 0x01 = Connecting 0x02 = Connected 0x03 = SLC connected 0x04 = Disconnecting Opcode 0x82 - Audio State notification Notification parameters: Audio state (1 octet) Remote address (6 octets) Valid audio states: 0x00 = Disconnected 0x01 = Connecting 0x02 = Connected 0x03 = Disconnecting Opcode 0x83 - Voice Recognition Command notification Notification parameters: Voice recognition state (1 octet) Remote address (6 octets) Valid voice recognition states: 0x00 = Stopped 0x01 = Started Opcode 0x84 - Answer Call Command notification Notification parameters: Remote address (6 octets) Opcode 0x85 - Hangup Call Command notification Notification parameters: Remote address (6 octets) Opcode 0x86 - Volume Command notification Notification parameters: Volume type (1 octet) Volume (1 octet) Remote address (6 octets) Valid volume types: 0x00 = Speaker 0x01 = Microphone Opcode 0x87 - Dial Call Command notification Notification parameters: Remote address (6 octets) Number (string) Opcode 0x88 - DTMF Command notification Notification parameters: Tone (1 octet) Remote address (6 octets) Opcode 0x89 - NREC Command notification Notification parameters: NREC types (1 octet) Remote address (6 octets) Valid NREC types: 0x00 = Stop 0x01 = Start Opcode 0x8a - CHLD Command notification Notification parameters: NREC types (1 octet) Remote address (6 octets) Valid CHLD types: 0x00 = Release and hold 0x01 = Release active and accept held 0x02 = Hold active and accept held 0x03 = Add held call to conference Opcode 0x8b - CNUM Command notification Notification parameters: Remote address (6 octets) Opcode 0x8c - CIND Command notification Notification parameters: Remote address (6 octets) Opcode 0x8d - COPS Command notification Notification parameters: Remote address (6 octets) Opcode 0x8e - CLCC Command notification Notification parameters: Remote address (6 octets) Opcode 0x8f - Unknown AT Command notification Notification parameters: Remote address (6 octets) AT command (string) Opcode 0x90 - Key Pressed Command notification Notification parameters: Remote address (6 octets) Opcode 0x91 - WBS Command notification Notification parameters: WBS types (1 octet) Remote address (6 octets) Valid WBS types: 0x00 = None 0x01 = No 0x02 = Yes Bluetooth Advanced Audio HAL (ID 6) Bluetooth Advanced Audio Sink HAL (ID 13) ========================================= Android HAL name: "a2dp" (BT_PROFILE_ADVANCED_AUDIO_ID) Android HAL name: "a2dp_sink" (BT_PROFILE_ADVANCED_AUDIO__SINK_ID) Commands and responses: Opcode 0x00 - Error response Opcode 0x01 - Connect command/response Command parameters: Remote address (6 octets) Response parameters: In case of an error, the error response will be returned. Opcode 0x02 - Disconnect command/response Command parameters: Remote address (6 octets) Response parameters: In case of an error, the error response will be returned. Notifications: Opcode 0x81 - Connection State notification Notification parameters: Connection state (1 octet) Remote address (6 octets) Valid connection states: 0x00 = Disconnected 0x01 = Connecting 0x02 = Connected 0x03 = Disconnecting Opcode 0x82 - Audio State notification Notification parameters: Audio state (1 octet) Remote address (6 octets) Valid connection states: 0x00 = Remote suspend 0x01 = Stopped 0x02 = Started Opcode 0x83 - Audio Configuration notification Notification parameters: Remote address (6 octets) Sample Rate in Hz (4 octets) Channel Count (1 octet) Valid channel count: 0x01 = Mono 0x02 = Stereo Bluetooth Health HAL (ID 7) =========================== Android HAL name: "health" (BT_PROFILE_HEALTH_ID) Commands and responses: Opcode 0x00 - Error response Opcode 0x01 - Register Application command/response Command parameters: Number of MDEP (1 octet) Application name offset (2 octets) Provider name offset (2 octets) Service name offset (2 octets) Service description offset (2 octets) Data length (2 octets) Data (data length) Response parameters: Application ID (2 octets) Strings are null terminated. In case of an error, the error response will be returned. Opcode 0x02 - Register Application MDEP data command/response Command parameters: Application ID (2 octets) MDEP Role (1 octet) Data type (2 octets) Channel type (1 octet) MDEP description length (2 octets) MDEP description (MDEP desciption length) Response parameters: In case of an error, the error response will be returned. Opcode 0x03 - Unregister Application command/response Command parameters: Application ID (2 octets) Response parameters: In case of an error, the error response will be returned. Opcode 0x04 - Connect Channel command/response Command parameters: Application ID (2 octets) Remote address (6 octets) MDEP index (1 octet) Response parameters: Channel ID (2 octets) In case of an error, the error response will be returned. Opcode 0x05 - Destroy Channel command/response Command parameters: Channel ID (2 octets) Response parameters: In case of an error, the error response will be returned. Notifications: Opcode 0x81 - Application Registration State notification Notification parameters: Application ID (2 octets) Application state (1 octet) Valid application states: 0x00 = Registration success 0x01 = Registration failed 0x02 = Deregistration success 0x03 = Deregistration failed Opcode 0x82 - Channel State notification Notification parameters: Application ID (2 octets) Remote address (6 octets) MDEP index (1 octet) Channel ID (2 octets) Channel state (1 octet) File descriptor (inline) Valid channel states: 0x00 = Connecting 0x01 = Connected 0x02 = Disconnecting 0x03 = Disconnected 0x04 = Destroyed Bluetooth Remote Control Target HAL (ID 8) =================================== Android HAL name: "avrcp" (BT_PROFILE_AV_RC_ID) Commands and responses: Opcode 0x00 - Error response Opcode 0x01 - Get Play Status Response command/response Command parameters: Status (1 octet) Duration (4 octets) Position (4 octets) In case of an error, the error response will be returned. Valid status values: 0x00 = Stopped 0x01 = Playing 0x02 = Paused 0x03 = Fwd seek 0x04 = Rev seek 0xff = Error Opcode 0x02 - List Player Attributes Response command/response Command parameters: Number of attributes (1 octet) Attribute # (1 octet) ... In case of an error, the error response will be returned. Valid attributes: 0x01 = Equalizer 0x02 = Repead 0x03 = Shuffle 0x04 = Scan Opcode 0x03 - List Player Values Response command/response Command parameters: Number of values (1 octet) Value # (1 octet) ... In case of an error, the error response will be returned. Opcode 0x04 - Get Player Values Response command/response Command parameters: Number of attributes (1 octet) Attribute # (1 octet) Value # (1 octet) ... In case of an error, the error response will be returned. Valid attributes: Same as in List Player Attributes Opcode 0x05 - Get Player Attributes Text Response command/response Command parameters: Number of attributes (1 octet) Attribute # (1 octet) Attribute # text length (1 octet) Attribute # text (variable) ... In case of an error, the error response will be returned. Valid attributes: Same as in List Player Attributes Opcode 0x06 - Get Player Values Text Response command/response Command parameters: Number of values (1 octet) Value # (1 octet) Value # text length (1 octet) Value # text (variable) ... In case of an error, the error response will be returned. Opcode 0x07 - Get Element Attributes Text Response command/response Command parameters: Number of elements (1 octet) Element # (1 octet) Element # text length (1 octet) Element # text (variable) ... In case of an error, the error response will be returned. Valid elements: 0x01 = Title 0x02 = Artist 0x03 = Album 0x04 = Track Number 0x05 = Number of Tracks 0x06 = Genre 0x06 = Duration Opcode 0x08 - Set Player Attributes Value Response command/response Command parameters: Status (1 octet) In case of an error, the error response will be returned. Valid status values: Same as in Get Play Status Response Opcode 0x09 - Register Notification Response command/response Command parameters: Event (1 octet) Type (1 octet) Data length (1 octet) Data (variable) In case of an error, the error response will be returned. Valid event values: 0x01 = Status Changed 0x02 = Track Changed 0x03 = Track Reached End 0x04 = Track Reached Start 0x05 = Position Changed 0x08 = Setting Changed Valid type values : 0x00 = Interim 0x01 = Changed Opcode 0x0a - Set Volume command/response Command parameters: Value (1 octet) In case of an error, the error response will be returned. Notifications: Opcode 0x81 - Remote Features notification Notification parameters: Remote address (6 octets) Features (1 octet) Valid features values : 0x00 = None 0x01 = Metadata 0x02 = Absolute Volume 0x03 = Browse Opcode 0x82 - Get Play Status notification Notification parameters: Opcode 0x83 - List Player Attributes notification Notification parameters: Opcode 0x84 - List Player Values notification Notification parameters: Attribute (1 octet) Valid attribute values: Same as in List Player Attributes Opcode 0x85 - Get Player Values notification Notification parameters: Number of attributes (1 octet) Attribute # (1 octet) ... Valid attribute values: Same as in List Player Attributes Opcode 0x86 - Get Player Attributes Text notification Notification parameters: Number of attributes (1 octet) Attribute # (1 octet) ... Valid attribute values: Same as in List Player Attributes Opcode 0x87 - Get Player Values Text notification Notification parameters: Attribute (1 octet) Number of values (1 octet) Value # (1 octet) ... Valid attribute values: Same as in List Player Attributes Opcode 0x88 - Set Player Values notification Notification parameters: Number of attributes (1 octet) Attribute # (1 octet) Value # (1 octet) ... Valid attribute values: Same as in List Player Attributes Opcode 0x89 - Get Element Attributes notification Notification parameters: Number of attributes (1 octet) Attribute # (1 octet) ... Valid attribute values: Same as in Get Element Attribute Opcode 0x8a - Register Notification notification Notification parameters: Event (1 octet) Parameter (4 octets) Valid event values: Same as in Register Notification Opcode 0x8b - Volume Changed notification Notification parameters: Volume (1 octet) Type (1 octet) Valid type values: Same as in Register Notification Opcode 0x8c - Passthrough Command notification Notification parameters: ID (1 octet) State (1 octet) Bluetooth GATT HAL (ID 9) ========================= Android HAL name: "gatt" (BT_PROFILE_GATT_ID) Structures: GATT Service ID: UUID (16 octets) Instance ID (1 octet) Is Primary (1 octet) GATT Included Service ID: UUID (16 octets) Instance ID (1 octet) Is Primary (1 octet) GATT Characteristic ID: UUID (16 octets) Instance ID (1 octet) GATT Descriptor ID: UUID (16 octets) Instance ID (1 octet) Commands and responses: Opcode 0x00 - Error response Opcode 0x01 - Client Register command/response Command parameters: Service UUID (16 octets) Response parameters: In case of an error, the error response will be returned. Opcode 0x02 - Client Unregister command/response Command parameters: Client Interface (4 octets) Response parameters: In case of an error, the error response will be returned. Opcode 0x03 - Client Scan command/response Command parameters: Client Interface (4 octets) Start (1 octet) Response parameters: In case of an error, the error response will be returned. Opcode 0x04 - Client Connect Device command/response Command parameters: Client Interface (4 octets) Remote address (6 octets) Is Direct (1 octet) Transport (4 octets) Response parameters: Valid transport value: 0x00 = Auto 0x01 = BR/EDR 0x02 = LE In case of an error, the error response will be returned. Opcode 0x05 - Client Disconnect Device command/response Command parameters: Client Interface (4 octets) Remote address (6 octets) Connection ID (4 octets) Response parameters: In case of an error, the error response will be returned. Opcode 0x06 - Client Listen command/response Command parameters: Client Interface (4 octets) Start (1 octet) Response parameters: In case of an error, the error response will be returned. Opcode 0x07 - Client Refresh command/response Command parameters: Client Interface (4 octets) Remote address (6 octets) Response parameters: In case of an error, the error response will be returned. Opcode 0x08 - Client Search Service command/response Command parameters: Connection ID (4 octets) Filtered (1 octet) Filter UUID (16 octets) Response parameters: Filter UUID shall only be present when Filtered is non-zero. In case of an error, the error response will be returned. Opcode 0x09 - Client Get Included Service command/response Command parameters: Connection ID (4 octets) GATT Service ID (18 octets) Continuation (1 octet) GATT Included Service ID (18 octets) ... Response parameters: GATT Included Service ID shall only be present when Continuation is non-zero. In case of an error, the error response will be returned. Opcode 0x0a - Client Get Characteristic command/response Command parameters: Connection ID (4 octets) GATT Service ID (18 octets) Continuation (1 octet) GATT Characteristic ID (17 octets) ... Response parameters: GATT Characteristic ID shall only be present when Continuation is non-zero. In case of an error, the error response will be returned. Opcode 0x0b - Client Get Descriptor command/response Command parameters: Connection ID (4 octets) GATT Service ID (18 octets) GATT Characteristic ID (17 octets) Continuation (1 octet) GATT Descriptor ID (17 octets) ... Response parameters: GATT Descriptor ID shall only be present when Continuation is non-zero. In case of an error, the error response will be returned. Opcode 0x0c - Client Read Characteristic command/response Command parameters: Connection ID (4 octets) GATT Service ID (18 octets) GATT Characteristic ID (17 octets) Authorization (4 octets) Response parameters: In case of an error, the error response will be returned. Opcode 0x0d - Client Write Characteristic command/response Command parameters: Connection ID (4 octets) GATT Service ID (18 octets) GATT Characteristic ID (17 octets) Write Type (4 octets) Length (4 octets) Authorization Req. (4 octets) Value (variable) Response parameters: Valid Write Type: 0x01 = No response 0x02 = Default 0x03 = Prepare 0x04 = Signed In case of an error, the error response will be returned. Opcode 0x0e - Client Read Descriptor command/response Command parameters: Connection ID (4 octets) GATT Service ID (18 octets) GATT Characteristic ID (17 octets) GATT Descriptor ID (17 octets) Authorization Req. (4 octets) Response parameters: In case of an error, the error response will be returned. Opcode 0x0f - Client Write Descriptor command/response Command parameters: Connection ID (4 octets) GATT Service ID (18 octets) GATT Characteristic ID (17 octets) GATT Descriptor ID (17 octets) Write Type (4 octets) Length (4 octets) Authorization Req. (4 octets) Value (variable) Response parameters: Valid Write Type: 0x01 = No response 0x02 = Default 0x03 = Prepare 0x04 = Signed In case of an error, the error response will be returned. Opcode 0x10 - Client Execute Write command/response Command parameters: Connection ID (4 octets) Execute (4 octets) Response parameters: In case of an error, the error response will be returned. Opcode 0x11 - Client Register For Notification command/response Command parameters: Client Interface (4 octets) Remote address (6 octets) GATT Service ID (18 octets) GATT Characteristic ID (17 octets) Response parameters: In case of an error, the error response will be returned. Opcode 0x12 - Client Deregister For Notification command/response Command parameters: Client Interface (4 octets) Remote address (6 octets) GATT Service ID (18 octets) GATT Characteristic ID (17 octets) Response parameters: In case of an error, the error response will be returned. Opcode 0x13 - Client Read Remote RSSI command/response Command parameters: Client Interface (4 octets) Remote address (6 octets) Response parameters: In case of an error, the error response will be returned. Opcode 0x14 - Client Get Device Type command/response Command parameters: Remote address (6 octets) Response parameters: Device Type Valid Device Type: 0x01 = BREDR 0x02 = BLE 0x03 = DUAL In case of an error, the error response will be returned. Opcode 0x15 - Client Set Advertising data command/response Command parameters: Server Interface (4 octets) Set Scan Resp. (1 octet) Include Name (1 octet) Include TX Power (1 octet) Min. Interval (4 octets) Max. Interval (4 octets) Appearance (4 octets) Manufacturer Len. (2 octets) Manufacturer Data (variable) Response parameters: In case of an error, the error response will be returned. Opcode 0x16 - Client Test Command command/response Command parameters: Command (4 octets) Address (6 octets) UUID (16 octets) U1 (2 octets) U2 (2 octets) U3 (2 octets) U4 (2 octets) U5 (2 octets) Response parameters: In case of an error, the error response will be returned. Opcode 0x17 - Server Register command/response Command parameters: UUID (16 octets) Response parameters: In case of an error, the error response will be returned. Opcode 0x18 - Server Unregister command/response Command parameters: Server (4 octets) Response parameters: In case of an error, the error response will be returned. Opcode 0x19 - Server Connect Peripheral command/response Command parameters: Server (4 octets) Remote address (6 octets) Is Direct (1 octet) Transport (4 octets) Response parameters: In case of an error, the error response will be returned. Opcode 0x1a - Server Disconnect Peripheral command/response Command parameters: Server (4 octets) Remote address (6 octets) Connection ID (1 octet) Response parameters: In case of an error, the error response will be returned. Opcode 0x1b - Server Add Service command/response Command parameters: Server (4 octets) GATT Service ID (18 octets) Number of Handles (4 octet) Response parameters: Valid GATT Service ID: UUID (16 octets) Instance ID (1 octet) Is Primary (1 octet) In case of an error, the error response will be returned. Opcode 0x1c - Server Add Included Service command/response Command parameters: Server (4 octets) Service handle (4 octets) Included handle (4 octets) Response parameters: In case of an error, the error response will be returned. Opcode 0x1d - Server Add Characteristic command/response Command parameters: Server (4 octets) Service handle (4 octets) UUID (16 octets) Properties (4 octets) Permissions (4 octets) Response parameters: In case of an error, the error response will be returned. Opcode 0x1e - Server Add Descriptor command/response Command parameters: Server (4 octets) Service handle (4 octets) UUID (16 octets) Permissions (4 octets) Response parameters: In case of an error, the error response will be returned. Opcode 0x1f - Server Start Service command/response Command parameters: Server (4 octets) Service handle (4 octets) Transport (4 octets) Response parameters: In case of an error, the error response will be returned. Opcode 0x20 - Server Stop Service command/response Command parameters: Server (4 octets) Service handle (4 octets) Response parameters: In case of an error, the error response will be returned. Opcode 0x21 - Server Delete Service command/response Command parameters: Server (4 octets) Service handle (4 octets) Response parameters: In case of an error, the error response will be returned. Opcode 0x22 - Server Send Indication command/response Command parameters: Server (4 octets) Attribute handle (4 octets) Connection ID (4 octets) Length (4 octets) Confirmation (4 octets) Value (variable) Response parameters: In case of an error, the error response will be returned. Opcode 0x23 - Server Send Response command/response Command parameters: Connection ID (4 octets) Transaction ID (4 octets) Handle (2 octets) Offset (2 octets) Auth Request (1 octect) Status (4 octets) GATT Response (4 octets) Response parameters: Valid GATT Response: GATT Value (607 octets) Handle (2 octets) Valid GATT Value: Value (600 octets) Handle (2 octets) Offset (2 octets) Length (2 octets) Authentication Request (1 octet) In case of an error, the error response will be returned. Opcode 0x24 - Client Scan Filter Params Setup command/response Command parameters: Client Interface (4 octets) Action (4 octets) Filter Index (4 octets) Features (4 octets) List Type (4 octets) Filter Type (4 octets) RSSI High Threshold (4 octets) RSSI Low Threshold (4 octets) Delivery Mode (4 octets) Found Timeout (4 octets) Lost Timeout (4 octets) Found Timeout Count (4 octets) Response parameters: In case of an error, the error response will be returned. Opcode 0x25 - Client Scan Filter Add Remove command/response Command parameters: Client Interface (4 octets) Action (4 octets) Filter Type (4 octets) Filter Index (4 octets) Company ID (4 octets) Company ID Mask (4 octets) UUID (16 octets) UUID Mask (16 octets) Address (6 octets) Address Type (1 octet) Data Length (4 octets) Data (variable) Mask Length (4 octets) Mask (variable) Response parameters: In case of an error, the error response will be returned. Opcode 0x26 - Client Scan Filter Clear command/response Command parameters: Client Interface (4 octets) Filter Index (4 octets) Response parameters: In case of an error, the error response will be returned. Opcode 0x27 - Client Scan Filter Enable command/response Command parameters: Client Interface (4 octets) Enable (1 octet) Response parameters: In case of an error, the error response will be returned. Opcode 0x28 - Client Configure MTU command/response Command parameters: Connection ID (4 octets) MTU (4 octets) Response parameters: In case of an error, the error response will be returned. Opcode 0x29 - Client Connection Parameter Update command/response Command parameters: Address (6 octets) Min Interval (4 octets) Max Interval (4 octets) Latency (4 octets) Timeoutl (4 octets) Response parameters: In case of an error, the error response will be returned. Opcode 0x2a - Client Set Scan Parameters command/response Command parameters: Scan Interval (4 octets) Scan Window (4 octets) Response parameters: In case of an error, the error response will be returned. Opcode 0x2b - Client Setup Multi Advertising command/response Command parameters: Client ID (4 octets) Min Interval (4 octets) Max Interval (4 octets) ADV Type (4 octets) Channel Map (4 octets) TX Power (4 octets) Timeout (4 octets) Response parameters: In case of an error, the error response will be returned. Opcode 0x2c - Client Update Multi Advertising command/response Command parameters: Client ID (4 octets) Min Interval (4 octets) Max Interval (4 octets) ADV Type (4 octets) Channel Map (4 octets) TX Power (4 octets) Timeout (4 octets) Response parameters: In case of an error, the error response will be returned. Opcode 0x2d - Client Setup Multi Advertising Instance command/response Command parameters: Client ID (4 octets) Set Scan Response (1 octet) Include Name (1 octet) Include TX Power (1 octet) Appearance (4 octets) Manufacturer Data Length (4 octets) Manufacturer Data (variable) Service Data Length (4 octets) Service Data (variable) Service UUID Length (4 octets) Service UUID (variable) Response parameters: In case of an error, the error response will be returned. Opcode 0x2e - Client Disable Multi Advertising Instance command/response Command parameters: Client ID (4 octets) Response parameters: In case of an error, the error response will be returned. Opcode 0x2f - Client Configure Batchscan command/response Command parameters: Client ID (4 octets) Full Max (4 octets) Trunc Max (4 octets) Notify Threshold (4 octets) Response parameters: In case of an error, the error response will be returned. Opcode 0x30 - Client Enable Batchscan command/response Command parameters: Client ID (4 octets) Scan Mode (4 octets) Scan Interval (4 octets) Scan Window (4 octets) Address Type (4 octets) Discard Rule (4 octets) Response parameters: In case of an error, the error response will be returned. Opcode 0x31 - Client Disable Batchscan command/response Command parameters: Client ID (4 octets) Response parameters: In case of an error, the error response will be returned. Opcode 0x32 - Client Read Batchscan Resports command/response Command parameters: Client ID (4 octets) Scan Mode (4 octets) Response parameters: In case of an error, the error response will be returned. Notifications: Opcode 0x81 - Client Register notification Notification parameters: Status (4 octets) Client Interface (4 octets) UUID (16 octets) Opcode 0x82 - Client Scan Result notification Notification parameters: Address (6 octets) RSSI (4 octets) Length (2 octets) Data (variable) Opcode 0x83 - Client Connect Device notification Notification parameters: Connection ID (4 octets) Status (4 octets) Client Interface (4 octets) Address (6 octets) Opcode 0x84 - Client Disconnect Device notification Notification parameters: Connection ID (4 octets) Status (4 octets) Client Interface (4 octets) Address (6 octets) Opcode 0x85 - Client Search Complete notification Notification parameters: Connection ID (4 octets) Status (4 octets) Opcode 0x86 - Client Search Result notification Notification parameters: Connection ID (4 octets) GATT Service ID (18 octets) Opcode 0x87 - Client Get Characteristic notification Notification parameters: Connection ID (4 octets) Status (4 octets) GATT Service ID (18 octets) GATT Characteristic ID (17 octets) Char Prop. (4 octets) Opcode 0x88 - Client Get Descriptor notification Notification parameters: Connection ID (4 octets) Status (4 octets) GATT Service ID (18 octets) GATT Characteristic ID (17 octets) GATT Descriptor ID (17 octets) Opcode 0x89 - Client Get Included Service notification Notification parameters: Connection ID (4 octets) Status (4 octets) GATT Service ID (18 octets) GATT Included Service ID (18 octets) Opcode 0x8a - Client Register For Notification notification Notification parameters: Connection ID (4 octets) Registered (4 octets) Status (4 octets) GATT Service ID (18 octets) GATT Characteristic ID (17 octets) Opcode 0x8b - Client Notify notification Notification parameters: Connection ID (4 octets) Address (6 octets) GATT Service ID (18 octets) GATT Characteristic ID (17 octets) Is Notify (1 octet) Length (2 octets) Value (variable) Opcode 0x8c - Client Read Characteristic notification Notification parameters: Connection ID (4 octets) Status (4 octets) GATT Read Parameters (variable) Valid GATT Read Parameters: GATT Service ID (18 octets) GATT Characteristic ID (17 octets) GATT Descriptor ID (17 octets) Value Type (4 octets) Status (1 octet) Length (2 octets) Value (variable) Opcode 0x8d - Client Write Characteristic notification Notification parameters: Connection ID (4 octets) Status (4 octets) GATT Write Parameters (53 octets) Valid GATT Write Parameters: GATT Service ID (18 octets) GATT Characteristic ID (17 octets) GATT Description ID (17 octets) Status (1 octet) Opcode 0x8e - Client Read Descriptor notification Notification parameters: Connection ID (4 octets) Status (4 octets) GATT Read Parameters (variable) Valid GATT Read Parameters: As described in Read Characteristic Opcode 0x8f - Client Write Descriptor notification Notification parameters: Connection ID (4 octets) Status (4 octets) GATT Write Parameters (53 octets) Valid GATT Write Parameters: As described in Write Characteristic Opcode 0x90 - Client Execute Write notification Notification parameters: Connection ID (4 octets) Status (4 octets) Opcode 0x91 - Client Read Remote RSSI notification Notification parameters: Client (4 octets) Address (6 octets) RSSI (4 octets) Status (4 octets) Opcode 0x92 - Client Listen notification Notification parameters: Status (4 octets) Server Interface (4 octets) Opcode 0x93 - Server Register notification Notification parameters: Status (4 octets) Server (4 octets) UUID (16 octets) Opcode 0x94 - Server Connection notification Notification parameters: Connection ID (4 octets) Server (4 octets) Connected (4 octets) Address (6 octets) Opcode 0x95 - Server Service Added notification Notification parameters: Status (4 octets) Server (4 octets) GATT Service ID (18 octets) Service Handle (4 octets) Opcode 0x96 - Server Included Service Added notification Notification patemeters: Status (4 octets) Server (4 octets) Service Handle (4 octets) Included Service Handle (4 octets) Opcode 0x97 - Server Characteristic Added notification Notification parameters: Status (4 octets) Server (4 octets) UUID (16 octets) Service Handle (4 octets) Characteristic Handle (4 octets) Opcode 0x98 - Server Descriptor Added notification Notification parameters: Status (4 octets) Server (4 octets) UUID (6 octets) Service Handle (4 octets) Descriptor Handle (4 octets) Opcode 0x99 - Server Service Started notification Notification parameters: Status (4 octets) Server (4 octets) Service Handle (4 octets) Opcode 0x9a - Server Service Stopped notification Notification parameters: Status (4 octets) Server (4 octets) Service Handle (4 octets) Opcode 0x9b - Server Service Deleted notification Notification parameters: Status (4 octets) Server (4 octets) Service Handle (4 octets) Opcode 0x9c - Server Request Read notification Notification parameters: Connection ID (4 octets) Trans ID (4 octets) Address (6 octets) Attribute Handle (4 octets) Offset (4 octets) Is Long (1 octet) Opcode 0x9d - Server Request Write notification Notification parameters: Connection ID (4 octets) Trans ID (4 octets) Address (6 octets) Attribute Handle (4 octets) Offset (4 octets) Length (4 octets) Need Response (4 octets) Is Prepare (1 octet) Value (variable) Opcode 0x9e - Server Request Execute Write notification Notification parameters: Connection ID (4 octets) Trans ID (4 octets) Address (6 octets) Execute Write (4 octets) Opcode 0x9f - Server Response Confirmation notification Notification parameters: Status (4 octets) Handle (4 octets) Opcode 0xa0 - Client Configure MTU notification Notification parameters: Connection ID (4 octets) Status (4 octets) MTU (4 octets) Opcode 0xa1 - Client Filter Configuration notification Notification parameters: Action (4 octets) Client ID (4 octets) Status (4 octets) Filter Type (4 octets) Available Space (4 octets) Opcode 0xa2 - Client Filter Parameters notification Notification parameters: Action (4 octets) Client ID (4 octets) Status (4 octets) Available Space (4 octets) Opcode 0xa3 - Client Filter Status notification Notification parameters: Enable (4 octets) Client ID (4 octets) Status (4 octets) Opcode 0xa4 - Client Multi Advertising Enable notification Notification parameters: Client ID (4 octets) Status (4 octets) Opcode 0xa5 - Client Multi Advertising Update notification Notification parameters: Client ID (4 octets) Status (4 octets) Opcode 0xa6 - Client Multi Advertising Data notification Notification parameters: Client ID (4 octets) Status (4 octets) Opcode 0xa7 - Client Multi Advertising Disable notification Notification parameters: Client ID (4 octets) Status (4 octets) Opcode 0xa8 - Client Congestion notification Notification parameters: Connection ID (4 octets) Congested (1 octet) Opcode 0xa9 - Client Configure Batchscan notification Notification parameters: Client ID (4 octets) Status (4 octets) Opcode 0xaa - Client Enable Batchscan notification Notification parameters: Action (4 octets) Client ID (4 octets) Status (4 octets) Opcode 0xab - Client Batchscan Reports notification Notification parameters: Client ID (4 octets) Status (4 octets) Report Format (4 octets) Num Reports (4 octets) Data Length (4 octets) Data (variable) Opcode 0xac - Client Batchscan Threshold notification Notification parameters: Client ID (4 octets) Opcode 0xad - Client Track ADV notification Notification parameters: Client ID (4 octets) Filter Index (4 octets) Address Type (4 octets) Address (6 octets) State (4 octets) Opcode 0xae - Server Indication Sent notification Notification parameters: Connection ID (4 octets) Status (4 octets) Opcode 0xaf - Server Congestion notification Notification parameters: Connection ID (4 octets) Congested (1 octet) Opcode 0xb0 - Server MTU Changed notification Notification parameters: Connection ID (4 octets) MTU (4 octets) Bluetooth Handsfree Client HAL (ID 10) ====================================== Android HAL name: "hf_client" (BT_PROFILE_HANDSFREE_CLIENT_ID) Commands and response: Opcode 0x00 - Error response Opcode 0x01 - Connect command/respose Command parameters: Remote address (6 octects) Response parameters: In case of an error, the error response will be returned. Opcode 0x02 - Disonnect command/response Command parameters: Remote address (6 octetcs) Response parameters: In case of an error, the error response will be returned. Opcode 0x03 - Connect Audio command/response Command parameters: Remote address (6 octets) Response parameters: In case of an error, the error response will be returned. Opcode 0x04 - Disconnect Audio command/response Command parameters: Remote address (6 octets) Response parameters: In case of an error, the error response will be returned. Opcode 0x05 - Start Voice Recognition command/response Command parameters: Response parameters: In case of an error, the error response will be returned. Opcode 0x06 - Stop Voice Recognition command/response Command parameters: Response parameters: In case of an error, the error response will be returned. Opcode 0x07 - Volume Control command/response Command parameters: Volume type (1 octet) Volume (1 octet) Response parameters: Valid volume types: 0x00 = Speaker 0x01 = Microphone In case of an error, the error response will be returned. Opcode 0x08 - Dial command/response Command parameters: Number (string) Response parameters: In case of an error, the error response will be returned. Opcode 0x09 - Dial Memory command/response Command parameters: Location (4 octet) Response parameters: In case of an error, the error response will be returned. Opcode 0x10 - Handle Call Action command/response Command parameters: Action (1 octet) Call Index (1 octet) Response parameters: Valid actions: 0x00 = CHLD_0 0x01 = CHLD_1 0x02 = CHLD_2 0x03 = CHLD_3 0x04 = CHLD_4 0x05 = CHLD_1x 0x06 = CHLD_2x 0x07 = ATA 0x08 = CHUP 0x09 = BTRH_0 0x10 = BTRH_1 0x11 = BTRH_2 In case of an error, the error response will be returned. Opcode 0x11 - Query Current Calls commad/response Command parameters: Response parameters: In case of an error, the error response will be returned. Opcode 0x12 - Query Current Operator Name Command parameters: Response parameters: In case of an error, the error response will be returned. Opcode 0x13 - Retrieve Subscriber Info command/response Command parameters: Response parameters: In case of an error, the error response will be returned. Opcode 0x14 - Send DTMF Tone command/response Command parameters: Tone (1 octet) Response parameters: In case of an error, the error response will be returned. Opcode 0x15 - Request Last Voice Tag Number command/response Command parameters: Response parameters: In case of an error, the error response will be returned. Notifications: Opcode 0x81 - Connection State Changed notification Notification parameters: State (1 octet) Peer Features (4 octets) CHLD Features (4 octets) Address (6 octets) Valid State values: 0x00 = Disconnected 0x01 = Connecting 0x02 = Connected 0x03 = SLC Connected 0x04 = Disconnecting Peer Features is a bitmask of the supported features. Currently available bits: 0 Three way calling 1 Echo cancellation and/or noise reduction 2 Voice recognition 3 In band ring tone 4 Attach a number to a voice tag 5 Ability to reject a call 6 Enhanced call status 7 Enhanced call control 8 Extended Error Result Codes 9 Codec negotiations 10-31 Reserved for future use CHLD Features is a bitmask of the supported features. Currently available bits: 0 Release waiting call or held calls 1 Release active calls and accept other call 2 Release specified active call only 3 Place all active calls on hold and accept other call 4 Request private mode with secified call 5 Add a held call to the multiparty 6 Connect two calls and leave multiparty 7-31 Reserved for future use Note: Peer and CHLD Features are valid only in SCL Connected state Opcode 0x82 - Audio State Changed notification Notification parameters: State (1 octet) Address (6 octets) Valid State values: 0x00 = Disconnected 0x01 = Connecting 0x02 = Connected 0x03 = Connected mSBC Opcode 0x83 - Voice Recognition State Changed notification Notification parameters: State (1 octet) Valid State values: 0x00 = VR Stopped 0x01 = VR Started Opcode 0x84 - Network State Changed notification Notification parameters: State (1 octet) Valid State values: 0x00 = Network Not Available 0x01 = Network Available Opcode 0x85 - Network Roaming Type Changed notification Notification parameters: Type (1 octet) Valid Type values: 0x00 = Home 0x01 = Roaming Opcode 0x86 - Network Signal Strength notification Notification parameters: Signal Strength (1 octet) Opcode 0x87 - Battery Level notification Notification parameters: Battery Level (1 octet) Opcode 0x88 - Current Operator Name notification Notification parameters: Name (string) Opcode 0x89 - Call Indicatior notification Notification parameters: Call (1 octet) Valid Call values: 0x00 = No Call In Progress 0x01 = Call In Progress Opcode 0x8a - Call Setup Indicator notification Notification parameters: Call Setup (1 octet) Valid Call Setup values: 0x00 = None 0x01 = Incoming 0x02 = Outgoing 0x03 = Alerting Opcode 0x8b - Call Held Indicator notification Notification parameters: Call Held (1 octet) Valid Call Held values: 0x00 = None 0x01 = Hold and Active 0x02 = Hold Opcode 0x8c - Resposne and Hold Status notification Notification parameters: Status (1 octet) Valid Status values: 0x00 = Held 0x01 = Accept 0x02 = Reject Opcode 0x8d - Calling Line Identification notification Notification parameters: Number (string) Note: This will be called only on incoming call if number is provided. Opcode 0x8e - Call Waiting notification Notification parameters: Nunmber (string) Opcode 0x8f - Current Calls List notification Notification parameters: Index (1 octet) Direction (1 octet) Call State (1 octet) Multiparty (1 octet) Number (string) Valid Direction values: 0x00 = Outgoing 0x01 = Incoming Valid Call Sate values: 0x00 = Active 0x01 = Held 0x02 = Dialing 0x03 = Alerting 0x04 = Incoming 0x05 = Waiting 0x06 = Call held by Response and Hold Valid Multiparty values: 0x00 = Single Call 0x01 = Multiparty (conference) Call Note: Number might be empty Opcode 0x90 - Volume Changed notification Notification parameters: Type (1 octet) Volume (1 octet) Valid Type values: 0x00 = Speaker 0x01 = Microphone Opcode 0x91 - Command Complete Callback notification Notification parameters: Type (1 octet) CME (1 octet) Valid Type values: 0x00 = OK 0x01 = Error 0x02 = Error no carrier 0x03 = Error busy 0x04 = Error no answer 0x05 = Error delayed 0x06 = Error blacklisted 0x07 = Error CME Note: CME parameter is valid only for Error CME type Opcode 0x92 - Subscriber Service Info Callback notification Notification parameters: Name (string) Type (1 octet) Valid Type values: 0x00 = Service unknown 0x01 = Service voice 0x02 = Service fax Opcode 0x93 - In Band Ring Settings Callback notification Notification parameters: State (1 octet) Valid State values: 0x00 = In band ringtone not provided 0x01 = In band ringtone provided Opcode 0x94 - Last Voice Call Tag Number Callback notification Notification parameters: Number (string) Opcode 0x95 - Ring Indication notification Notification parameters: Bluetooth Map Client HAL (ID 11) ========================= Android HAL name: "map_client" (BT_PROFILE_MAP_CLIENT_ID) Commands and responses: Opcode 0x00 - Error response Opcode 0x01 - Get Remote MAS Instances Command parameters: Remote address (6 octets) Response parameters: In case of an error, the error response will be returned. Notifications: Opcode 0x81 - Remote MAS Instances notification Notification parameters: Status (1 octet) Remote address (6 octets) Number of instances (4 octets) Instance ID # (4 octets) Channel # (4 octets) Message types (4 octets) Name # (string) Bluetooth Remote Control Controller HAL (ID 12) =================================== Android HAL name: "avrcp-ctrl" (BT_PROFILE_AV_RC_CTRL_ID) Commands and responses: Opcode 0x00 - Error response Opcode 0x01 - Send Pass Through command/response Command parameters: Remote Address (6 octets) Key Code (1 octet) Key State (1 octet) In case of an error, the error response will be returned. Notifications: Opcode 0x81 - Passthrough Response Notification Notification parameters: ID (1 octet) Key State (1 octet) Opcode 0x82 - Connection State Notification Notification parameters: State (1 octet) Remote Address (6 octets) bluez-5.82/android/PaxHeaders/hidhost.c0000644000000000000000000000005014643061455015104 xustar0020 atime=1743516865 20 ctime=1743591278 bluez-5.82/android/hidhost.c0000644000000000000000000010415414643061455014572 0ustar00rootroot// SPDX-License-Identifier: LGPL-2.1-or-later /* * * BlueZ - Bluetooth protocol stack for Linux * * Copyright (C) 2013-2014 Intel Corporation. All rights reserved. * * */ #ifdef HAVE_CONFIG_H #include #endif #include #include #include #include #include #include #include #include "btio/btio.h" #include "lib/bluetooth.h" #include "lib/sdp.h" #include "lib/sdp_lib.h" #include "lib/uuid.h" #include "src/shared/mgmt.h" #include "src/shared/util.h" #include "src/shared/uhid.h" #include "src/shared/queue.h" #include "src/shared/att.h" #include "src/shared/gatt-db.h" #include "src/sdp-client.h" #include "src/uuid-helper.h" #include "src/log.h" #include "profiles/input/hog-lib.h" #include "hal-msg.h" #include "ipc-common.h" #include "ipc.h" #include "bluetooth.h" #include "gatt.h" #include "hidhost.h" #include "utils.h" #define L2CAP_PSM_HIDP_CTRL 0x11 #define L2CAP_PSM_HIDP_INTR 0x13 /* HID message types */ #define HID_MSG_HANDSHAKE 0x00 #define HID_MSG_CONTROL 0x10 #define HID_MSG_GET_REPORT 0x40 #define HID_MSG_SET_REPORT 0x50 #define HID_MSG_GET_PROTOCOL 0x60 #define HID_MSG_SET_PROTOCOL 0x70 #define HID_MSG_DATA 0xa0 #define HID_MSG_TYPE_MASK 0xf0 /* HID data types */ #define HID_DATA_TYPE_INPUT 0x01 #define HID_DATA_TYPE_OUTPUT 0x02 #define HID_DATA_TYPE_FEATURE 0x03 /* HID protocol header parameters */ #define HID_PROTO_BOOT 0x00 #define HID_PROTO_REPORT 0x01 /* HID GET REPORT Size Field */ #define HID_GET_REPORT_SIZE_FIELD 0x08 /* HID Virtual Cable Unplug */ #define HID_VIRTUAL_CABLE_UNPLUG 0x05 static bdaddr_t adapter_addr; static GIOChannel *ctrl_io = NULL; static GIOChannel *intr_io = NULL; static GSList *devices = NULL; static unsigned int hog_app = 0; static struct ipc *hal_ipc = NULL; struct hid_device { bdaddr_t dst; uint8_t state; uint8_t subclass; uint16_t vendor; uint16_t product; uint16_t version; uint8_t country; int rd_size; void *rd_data; uint8_t boot_dev; GIOChannel *ctrl_io; GIOChannel *intr_io; guint ctrl_watch; guint intr_watch; struct bt_uhid *uhid; uint8_t last_hid_msg; struct bt_hog *hog; int sec_level; }; static int device_cmp(gconstpointer s, gconstpointer user_data) { const struct hid_device *dev = s; const bdaddr_t *dst = user_data; return bacmp(&dev->dst, dst); } static void hid_device_free(void *data) { struct hid_device *dev = data; if (dev->ctrl_watch > 0) g_source_remove(dev->ctrl_watch); if (dev->intr_watch > 0) g_source_remove(dev->intr_watch); if (dev->intr_io) g_io_channel_unref(dev->intr_io); if (dev->ctrl_io) g_io_channel_unref(dev->ctrl_io); if (dev->uhid) bt_uhid_unref(dev->uhid); if (dev->hog) bt_hog_unref(dev->hog); g_free(dev->rd_data); g_free(dev); } static void hid_device_remove(struct hid_device *dev) { devices = g_slist_remove(devices, dev); hid_device_free(dev); } static struct hid_device *hid_device_new(const bdaddr_t *addr) { struct hid_device *dev; dev = g_new0(struct hid_device, 1); bacpy(&dev->dst, addr); dev->state = HAL_HIDHOST_STATE_DISCONNECTED; dev->sec_level = BT_IO_SEC_LOW; devices = g_slist_append(devices, dev); return dev; } static bool hex2buf(const uint8_t *hex, uint8_t *buf, int buf_size) { int i, j; char c; uint8_t b; for (i = 0, j = 0; i < buf_size; i++, j++) { c = toupper(hex[j]); if (c >= '0' && c <= '9') b = c - '0'; else if (c >= 'A' && c <= 'F') b = 10 + c - 'A'; else return false; j++; c = toupper(hex[j]); if (c >= '0' && c <= '9') b = b * 16 + c - '0'; else if (c >= 'A' && c <= 'F') b = b * 16 + 10 + c - 'A'; else return false; buf[i] = b; } return true; } static void handle_uhid_output(struct uhid_event *event, void *user_data) { struct uhid_output_req *output = &event->u.output; struct hid_device *dev = user_data; int fd, req_size; uint8_t *req; if (!dev->ctrl_io) return; req_size = 1 + output->size; req = malloc0(req_size); if (!req) return; req[0] = HID_MSG_SET_REPORT | output->rtype; memcpy(req + 1, output->data, req_size - 1); fd = g_io_channel_unix_get_fd(dev->ctrl_io); if (write(fd, req, req_size) < 0) error("hidhost: error writing set_report: %s (%d)", strerror(errno), errno); free(req); } static gboolean intr_io_watch_cb(GIOChannel *chan, gpointer data) { struct hid_device *dev = data; uint8_t buf[UHID_DATA_MAX]; struct uhid_event ev; int fd, bread, err; /* Wait uHID if not ready */ if (!dev->uhid) return TRUE; fd = g_io_channel_unix_get_fd(chan); bread = read(fd, buf, sizeof(buf)); if (bread < 0) { error("hidhost: read from interrupt failed: %s(%d)", strerror(errno), -errno); return TRUE; } /* Discard non-data packets */ if (bread == 0 || buf[0] != (HID_MSG_DATA | HID_DATA_TYPE_INPUT)) return TRUE; /* send data to uHID device skipping HIDP header byte */ memset(&ev, 0, sizeof(ev)); ev.type = UHID_INPUT; ev.u.input.size = bread - 1; memcpy(ev.u.input.data, &buf[1], ev.u.input.size); err = bt_uhid_send(dev->uhid, &ev); if (err < 0) DBG("bt_uhid_send: %s (%d)", strerror(-err), -err); return TRUE; } static void bt_hid_notify_state(struct hid_device *dev, uint8_t state) { struct hal_ev_hidhost_conn_state ev; char address[18]; if (dev->state == state) return; dev->state = state; ba2str(&dev->dst, address); DBG("device %s state %u", address, state); bdaddr2android(&dev->dst, ev.bdaddr); ev.state = state; ipc_send_notif(hal_ipc, HAL_SERVICE_ID_HIDHOST, HAL_EV_HIDHOST_CONN_STATE, sizeof(ev), &ev); } static gboolean intr_watch_cb(GIOChannel *chan, GIOCondition cond, gpointer data) { struct hid_device *dev = data; if (cond & (G_IO_HUP | G_IO_ERR | G_IO_NVAL)) goto error; if (cond & G_IO_IN) return intr_io_watch_cb(chan, data); error: bt_hid_notify_state(dev, HAL_HIDHOST_STATE_DISCONNECTED); /* * Checking for ctrl_watch avoids a double g_io_channel_shutdown since * it's likely that ctrl_watch_cb has been queued for dispatching in * this mainloop iteration */ if ((cond & (G_IO_HUP | G_IO_ERR)) && dev->ctrl_watch) g_io_channel_shutdown(chan, TRUE, NULL); /* Close control channel */ if (dev->ctrl_io && !(cond & G_IO_NVAL)) g_io_channel_shutdown(dev->ctrl_io, TRUE, NULL); hid_device_remove(dev); return FALSE; } static void bt_hid_notify_proto_mode(struct hid_device *dev, uint8_t *buf, int len) { struct hal_ev_hidhost_proto_mode ev; char address[18]; ba2str(&dev->dst, address); DBG("device %s", address); memset(&ev, 0, sizeof(ev)); bdaddr2android(&dev->dst, ev.bdaddr); if (buf[0] == HID_MSG_DATA) { ev.status = HAL_HIDHOST_STATUS_OK; if (buf[1] == HID_PROTO_REPORT) ev.mode = HAL_HIDHOST_REPORT_PROTOCOL; else if (buf[1] == HID_PROTO_BOOT) ev.mode = HAL_HIDHOST_BOOT_PROTOCOL; else ev.mode = HAL_HIDHOST_UNSUPPORTED_PROTOCOL; } else { ev.status = buf[0]; ev.mode = HAL_HIDHOST_UNSUPPORTED_PROTOCOL; } ipc_send_notif(hal_ipc, HAL_SERVICE_ID_HIDHOST, HAL_EV_HIDHOST_PROTO_MODE, sizeof(ev), &ev); } static void bt_hid_notify_get_report(struct hid_device *dev, uint8_t *buf, int len) { struct hal_ev_hidhost_get_report *ev; int ev_len; char address[18]; ba2str(&dev->dst, address); DBG("device %s", address); ev_len = sizeof(*ev); if (!((buf[0] == (HID_MSG_DATA | HID_DATA_TYPE_INPUT)) || (buf[0] == (HID_MSG_DATA | HID_DATA_TYPE_OUTPUT)) || (buf[0] == (HID_MSG_DATA | HID_DATA_TYPE_FEATURE)))) { ev = g_malloc0(ev_len); ev->status = buf[0]; bdaddr2android(&dev->dst, ev->bdaddr); goto send; } /* * Report porotocol mode reply contains id after hdr, in boot * protocol mode id doesn't exist */ ev_len += (dev->boot_dev) ? (len - 1) : (len - 2); ev = g_malloc0(ev_len); ev->status = HAL_HIDHOST_STATUS_OK; bdaddr2android(&dev->dst, ev->bdaddr); /* * Report porotocol mode reply contains id after hdr, in boot * protocol mode id doesn't exist */ if (dev->boot_dev) { ev->len = len - 1; memcpy(ev->data, buf + 1, ev->len); } else { ev->len = len - 2; memcpy(ev->data, buf + 2, ev->len); } send: ipc_send_notif(hal_ipc, HAL_SERVICE_ID_HIDHOST, HAL_EV_HIDHOST_GET_REPORT, ev_len, ev); g_free(ev); } static void bt_hid_notify_handshake(struct hid_device *dev, uint8_t *buf, int len) { struct hal_ev_hidhost_handshake ev; bdaddr2android(&dev->dst, ev.bdaddr); /* crop result code to handshake status range from HAL */ ev.status = buf[0]; if (ev.status > HAL_HIDHOST_HS_ERROR) ev.status = HAL_HIDHOST_HS_ERROR; ipc_send_notif(hal_ipc, HAL_SERVICE_ID_HIDHOST, HAL_EV_HIDHOST_HANDSHAKE, sizeof(ev), &ev); } static void bt_hid_notify_virtual_unplug(struct hid_device *dev, uint8_t *buf, int len) { struct hal_ev_hidhost_virtual_unplug ev; char address[18]; ba2str(&dev->dst, address); DBG("device %s", address); bdaddr2android(&dev->dst, ev.bdaddr); ev.status = HAL_HIDHOST_GENERAL_ERROR; /* Wait either channels to HUP */ if (dev->intr_io && dev->ctrl_io) { g_io_channel_shutdown(dev->intr_io, TRUE, NULL); g_io_channel_shutdown(dev->ctrl_io, TRUE, NULL); bt_hid_notify_state(dev, HAL_HIDHOST_STATE_DISCONNECTING); ev.status = HAL_HIDHOST_STATUS_OK; } ipc_send_notif(hal_ipc, HAL_SERVICE_ID_HIDHOST, HAL_EV_HIDHOST_VIRTUAL_UNPLUG, sizeof(ev), &ev); } static gboolean ctrl_io_watch_cb(GIOChannel *chan, gpointer data) { struct hid_device *dev = data; int fd, bread; uint8_t buf[UHID_DATA_MAX]; DBG(""); fd = g_io_channel_unix_get_fd(chan); bread = read(fd, buf, sizeof(buf)); if (bread < 0) { error("hidhost: read from control failed: %s(%d)", strerror(errno), -errno); return TRUE; } switch (dev->last_hid_msg) { case HID_MSG_GET_PROTOCOL: case HID_MSG_SET_PROTOCOL: bt_hid_notify_proto_mode(dev, buf, bread); break; case HID_MSG_GET_REPORT: bt_hid_notify_get_report(dev, buf, bread); break; } switch (buf[0] & HID_MSG_TYPE_MASK) { case HID_MSG_HANDSHAKE: bt_hid_notify_handshake(dev, buf, bread); break; case HID_MSG_CONTROL: if ((buf[0] & ~HID_MSG_TYPE_MASK) == HID_VIRTUAL_CABLE_UNPLUG) bt_hid_notify_virtual_unplug(dev, buf, bread); break; default: break; } /* reset msg type request */ dev->last_hid_msg = 0; return TRUE; } static gboolean ctrl_watch_cb(GIOChannel *chan, GIOCondition cond, gpointer data) { struct hid_device *dev = data; if (cond & (G_IO_HUP | G_IO_ERR | G_IO_NVAL)) goto error; if (cond & G_IO_IN) return ctrl_io_watch_cb(chan, data); error: bt_hid_notify_state(dev, HAL_HIDHOST_STATE_DISCONNECTED); /* * Checking for intr_watch avoids a double g_io_channel_shutdown since * it's likely that intr_watch_cb has been queued for dispatching in * this mainloop iteration */ if ((cond & (G_IO_HUP | G_IO_ERR)) && dev->intr_watch) g_io_channel_shutdown(chan, TRUE, NULL); if (dev->intr_io && !(cond & G_IO_NVAL)) g_io_channel_shutdown(dev->intr_io, TRUE, NULL); hid_device_remove(dev); return FALSE; } static void bt_hid_set_info(struct hid_device *dev) { struct hal_ev_hidhost_info ev; DBG(""); bdaddr2android(&dev->dst, ev.bdaddr); ev.attr = 0; /* TODO: Check what is this field */ ev.subclass = dev->subclass; ev.app_id = 0; /* TODO: Check what is this field */ ev.vendor = dev->vendor; ev.product = dev->product; ev.version = dev->version; ev.country = dev->country; ev.descr_len = dev->rd_size; memset(ev.descr, 0, sizeof(ev.descr)); memcpy(ev.descr, dev->rd_data, ev.descr_len); ipc_send_notif(hal_ipc, HAL_SERVICE_ID_HIDHOST, HAL_EV_HIDHOST_INFO, sizeof(ev), &ev); } static int uhid_create(struct hid_device *dev) { struct uhid_event ev; int err; dev->uhid = bt_uhid_new_default(); if (!dev->uhid) { err = -errno; error("hidhost: Failed to create bt_uhid instance"); return err; } memset(&ev, 0, sizeof(ev)); ev.type = UHID_CREATE; strcpy((char *) ev.u.create.name, "bluez-input-device"); ev.u.create.bus = BUS_BLUETOOTH; ev.u.create.vendor = dev->vendor; ev.u.create.product = dev->product; ev.u.create.version = dev->version; ev.u.create.country = dev->country; ev.u.create.rd_size = dev->rd_size; ev.u.create.rd_data = dev->rd_data; err = bt_uhid_send(dev->uhid, &ev); if (err < 0) { error("hidhost: Failed to create uHID device: %s", strerror(-err)); bt_uhid_unref(dev->uhid); dev->uhid = NULL; return err; } bt_uhid_register(dev->uhid, UHID_OUTPUT, handle_uhid_output, dev); bt_hid_set_info(dev); return 0; } static void interrupt_connect_cb(GIOChannel *chan, GError *conn_err, gpointer user_data) { struct hid_device *dev = user_data; uint8_t state; DBG(""); if (conn_err) { error("hidhost: Failed to connect interrupt channel (%s)", conn_err->message); state = HAL_HIDHOST_STATE_FAILED; goto failed; } if (uhid_create(dev) < 0) { state = HAL_HIDHOST_STATE_NO_HID; goto failed; } dev->intr_watch = g_io_add_watch(dev->intr_io, G_IO_IN | G_IO_HUP | G_IO_ERR | G_IO_NVAL, intr_watch_cb, dev); bt_hid_notify_state(dev, HAL_HIDHOST_STATE_CONNECTED); return; failed: bt_hid_notify_state(dev, state); hid_device_remove(dev); } static void control_connect_cb(GIOChannel *chan, GError *conn_err, gpointer user_data) { struct hid_device *dev = user_data; GError *err = NULL; DBG(""); if (conn_err) { bt_hid_notify_state(dev, HAL_HIDHOST_STATE_DISCONNECTED); error("hidhost: Failed to connect control channel (%s)", conn_err->message); goto failed; } /* Connect to the HID interrupt channel */ dev->intr_io = bt_io_connect(interrupt_connect_cb, dev, NULL, &err, BT_IO_OPT_SOURCE_BDADDR, &adapter_addr, BT_IO_OPT_DEST_BDADDR, &dev->dst, BT_IO_OPT_PSM, L2CAP_PSM_HIDP_INTR, BT_IO_OPT_SEC_LEVEL, dev->sec_level, BT_IO_OPT_INVALID); if (!dev->intr_io) { error("hidhost: Failed to connect interrupt channel (%s)", err->message); g_error_free(err); goto failed; } dev->ctrl_watch = g_io_add_watch(dev->ctrl_io, G_IO_IN | G_IO_HUP | G_IO_ERR | G_IO_NVAL, ctrl_watch_cb, dev); return; failed: hid_device_remove(dev); } static void hid_sdp_search_cb(sdp_list_t *recs, int err, gpointer data) { struct hid_device *dev = data; sdp_list_t *list; GError *gerr = NULL; DBG(""); if (err < 0) { error("hidhost: Unable to get SDP record: %s", strerror(-err)); goto fail; } if (!recs || !recs->data) { error("hidhost: No SDP records found"); goto fail; } for (list = recs; list != NULL; list = list->next) { sdp_record_t *rec = list->data; sdp_data_t *data; data = sdp_data_get(rec, SDP_ATTR_HID_COUNTRY_CODE); if (data) dev->country = data->val.uint8; data = sdp_data_get(rec, SDP_ATTR_HID_DEVICE_SUBCLASS); if (data) { dev->subclass = data->val.uint8; /* Encryption is mandatory for keyboards */ if (dev->subclass & 0x40) dev->sec_level = BT_IO_SEC_MEDIUM; } data = sdp_data_get(rec, SDP_ATTR_HID_BOOT_DEVICE); if (data) dev->boot_dev = data->val.uint8; data = sdp_data_get(rec, SDP_ATTR_HID_DESCRIPTOR_LIST); if (data) { if (!SDP_IS_SEQ(data->dtd)) goto fail; /* First HIDDescriptor */ data = data->val.dataseq; if (!SDP_IS_SEQ(data->dtd)) goto fail; /* ClassDescriptorType */ data = data->val.dataseq; if (data->dtd != SDP_UINT8) goto fail; /* ClassDescriptorData */ data = data->next; if (!data || !SDP_IS_TEXT_STR(data->dtd)) goto fail; dev->rd_size = data->unitSize; dev->rd_data = util_memdup(data->val.str, data->unitSize); } } if (dev->ctrl_io) { /* Raise the security level for this device if needed. */ if ((dev->sec_level > BT_IO_SEC_LOW) && !bt_io_set(dev->ctrl_io, &gerr, BT_IO_OPT_SEC_LEVEL, dev->sec_level, BT_IO_OPT_INVALID)) { error("hidhost: Cannot raise security level: %s", gerr->message); g_error_free(gerr); goto fail; } if (uhid_create(dev) < 0) goto fail; return; } dev->ctrl_io = bt_io_connect(control_connect_cb, dev, NULL, &gerr, BT_IO_OPT_SOURCE_BDADDR, &adapter_addr, BT_IO_OPT_DEST_BDADDR, &dev->dst, BT_IO_OPT_PSM, L2CAP_PSM_HIDP_CTRL, BT_IO_OPT_SEC_LEVEL, dev->sec_level, BT_IO_OPT_INVALID); if (gerr) { error("hidhost: Failed to connect control channel (%s)", gerr->message); g_error_free(gerr); goto fail; } return; fail: bt_hid_notify_state(dev, HAL_HIDHOST_STATE_DISCONNECTED); hid_device_remove(dev); } static void hid_sdp_did_search_cb(sdp_list_t *recs, int err, gpointer data) { struct hid_device *dev = data; sdp_list_t *list; uuid_t uuid; DBG(""); if (err < 0) { error("hidhost: Unable to get Device ID SDP record: %s", strerror(-err)); goto fail; } if (!recs || !recs->data) { error("hidhost: No Device ID SDP records found"); goto fail; } for (list = recs; list; list = list->next) { sdp_record_t *rec = list->data; sdp_data_t *data; data = sdp_data_get(rec, SDP_ATTR_VENDOR_ID); if (data) dev->vendor = data->val.uint16; data = sdp_data_get(rec, SDP_ATTR_PRODUCT_ID); if (data) dev->product = data->val.uint16; data = sdp_data_get(rec, SDP_ATTR_VERSION); if (data) dev->version = data->val.uint16; } sdp_uuid16_create(&uuid, HID_SVCLASS_ID); if (bt_search_service(&adapter_addr, &dev->dst, &uuid, hid_sdp_search_cb, dev, NULL, 0) < 0) { error("hidhost: Failed to search SDP details"); goto fail; } return; fail: bt_hid_notify_state(dev, HAL_HIDHOST_STATE_DISCONNECTED); hid_device_remove(dev); } static void hog_conn_cb(const bdaddr_t *addr, int err, void *attrib) { GSList *l; struct hid_device *dev; l = g_slist_find_custom(devices, addr, device_cmp); dev = l ? l->data : NULL; if (err < 0) { if (!dev) return; if (dev->hog) { bt_hid_notify_state(dev, HAL_HIDHOST_STATE_DISCONNECTED); bt_hog_detach(dev->hog, true); return; } goto fail; } if (!dev) dev = hid_device_new(addr); if (!dev->hog) { /* TODO: Get device details and primary */ dev->hog = bt_hog_new_default("bluez-input-device", dev->vendor, dev->product, dev->version, BT_UHID_NONE, NULL); if (!dev->hog) { error("HoG: unable to create session"); goto fail; } } if (!bt_hog_attach(dev->hog, attrib)) { error("HoG: unable to attach"); goto fail; } if (!bt_gatt_set_security(addr, BT_IO_SEC_MEDIUM)) { error("Failed to set security level"); goto fail; } DBG(""); bt_hid_notify_state(dev, HAL_HIDHOST_STATE_CONNECTED); if (!bt_gatt_add_autoconnect(hog_app, &dev->dst)) error("hidhost: Could not add to autoconnect list"); return; fail: bt_hid_notify_state(dev, HAL_HIDHOST_STATE_DISCONNECTED); hid_device_remove(dev); } static bool hog_connect(struct hid_device *dev) { DBG(""); if (hog_app) return bt_gatt_connect_app(hog_app, &dev->dst); hog_app = bt_gatt_register_app(HOG_UUID, GATT_CLIENT, hog_conn_cb); if (!hog_app) { error("hidhost: bt_gatt_register_app failed"); return false; } return bt_gatt_connect_app(hog_app, &dev->dst); } static void bt_hid_connect(const void *buf, uint16_t len) { const struct hal_cmd_hidhost_connect *cmd = buf; struct hid_device *dev; uint8_t status; char addr[18]; bdaddr_t dst; GSList *l; uuid_t uuid; DBG(""); android2bdaddr(&cmd->bdaddr, &dst); l = g_slist_find_custom(devices, &dst, device_cmp); if (l) dev = l->data; else dev = hid_device_new(&dst); if (dev->state != HAL_HIDHOST_STATE_DISCONNECTED) goto done; ba2str(&dev->dst, addr); DBG("connecting to %s", addr); if (bt_device_last_seen_bearer(&dev->dst) != BDADDR_BREDR) { if (!hog_connect(dev)) { status = HAL_STATUS_FAILED; hid_device_remove(dev); goto failed; } goto done; } sdp_uuid16_create(&uuid, PNP_INFO_SVCLASS_ID); if (bt_search_service(&adapter_addr, &dev->dst, &uuid, hid_sdp_did_search_cb, dev, NULL, 0) < 0) { error("hidhost: Failed to search DeviceID SDP details"); hid_device_remove(dev); status = HAL_STATUS_FAILED; goto failed; } done: if (dev->state == HAL_HIDHOST_STATE_DISCONNECTED) bt_hid_notify_state(dev, HAL_HIDHOST_STATE_CONNECTING); status = HAL_STATUS_SUCCESS; failed: ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_HIDHOST, HAL_OP_HIDHOST_CONNECT, status); } static bool hog_disconnect(struct hid_device *dev) { DBG(""); if (dev->state == HAL_HIDHOST_STATE_DISCONNECTED) return false; bt_hid_notify_state(dev, HAL_HIDHOST_STATE_DISCONNECTING); if (!bt_gatt_disconnect_app(hog_app, &dev->dst)) { bt_hid_notify_state(dev, HAL_HIDHOST_STATE_DISCONNECTED); hid_device_remove(dev); } return true; } static void bt_hid_disconnect(const void *buf, uint16_t len) { const struct hal_cmd_hidhost_disconnect *cmd = buf; struct hid_device *dev; uint8_t status; GSList *l; bdaddr_t dst; DBG(""); android2bdaddr(&cmd->bdaddr, &dst); l = g_slist_find_custom(devices, &dst, device_cmp); if (!l) { status = HAL_STATUS_FAILED; goto failed; } dev = l->data; if (bt_is_device_le(&dst)) { if (!hog_disconnect(dev)) { status = HAL_STATUS_FAILED; goto failed; } goto done; } /* Wait either channels to HUP */ if (dev->intr_io) g_io_channel_shutdown(dev->intr_io, TRUE, NULL); if (dev->ctrl_io) g_io_channel_shutdown(dev->ctrl_io, TRUE, NULL); bt_hid_notify_state(dev, HAL_HIDHOST_STATE_DISCONNECTING); done: status = HAL_STATUS_SUCCESS; failed: ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_HIDHOST, HAL_OP_HIDHOST_DISCONNECT, status); } static bool bt_hid_write_virtual_unplug(GIOChannel *chan) { uint8_t hdr = HID_MSG_CONTROL | HID_VIRTUAL_CABLE_UNPLUG; int fd = g_io_channel_unix_get_fd(chan); if (write(fd, &hdr, sizeof(hdr)) == sizeof(hdr)) return true; error("hidhost: Error writing virtual unplug command: %s (%d)", strerror(errno), errno); return false; } static void bt_hid_virtual_unplug(const void *buf, uint16_t len) { const struct hal_cmd_hidhost_virtual_unplug *cmd = buf; struct hid_device *dev; GSList *l; uint8_t status; bdaddr_t dst; DBG(""); android2bdaddr(&cmd->bdaddr, &dst); l = g_slist_find_custom(devices, &dst, device_cmp); if (!l) { status = HAL_STATUS_FAILED; goto failed; } dev = l->data; if (!(dev->ctrl_io)) { status = HAL_STATUS_FAILED; goto failed; } if (!bt_hid_write_virtual_unplug(dev->ctrl_io)) { status = HAL_STATUS_FAILED; goto failed; } /* Wait either channels to HUP */ if (dev->intr_io) g_io_channel_shutdown(dev->intr_io, TRUE, NULL); if (dev->ctrl_io) g_io_channel_shutdown(dev->ctrl_io, TRUE, NULL); bt_hid_notify_state(dev, HAL_HIDHOST_STATE_DISCONNECTING); status = HAL_STATUS_SUCCESS; failed: ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_HIDHOST, HAL_OP_HIDHOST_VIRTUAL_UNPLUG, status); } static void bt_hid_info(const void *buf, uint16_t len) { const struct hal_cmd_hidhost_set_info *cmd = buf; if (len != sizeof(*cmd) + cmd->descr_len) { error("Invalid hid set info size (%u bytes), terminating", len); raise(SIGTERM); return; } /* * Data from hal_cmd_hidhost_set_info is usefull only when we create * UHID device. Once device is created all the transactions will be * done through the fd. There is no way to use this information * once device is created with HID internals. */ DBG("Not supported"); ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_HIDHOST, HAL_OP_HIDHOST_SET_INFO, HAL_STATUS_UNSUPPORTED); } static void bt_hid_get_protocol(const void *buf, uint16_t len) { const struct hal_cmd_hidhost_get_protocol *cmd = buf; struct hid_device *dev; GSList *l; bdaddr_t dst; int fd; uint8_t hdr; uint8_t status; DBG(""); switch (cmd->mode) { case HAL_HIDHOST_REPORT_PROTOCOL: case HAL_HIDHOST_BOOT_PROTOCOL: break; default: status = HAL_STATUS_INVALID; goto failed; } android2bdaddr(&cmd->bdaddr, &dst); l = g_slist_find_custom(devices, &dst, device_cmp); if (!l) { status = HAL_STATUS_FAILED; goto failed; } dev = l->data; hdr = HID_MSG_GET_PROTOCOL | cmd->mode; fd = g_io_channel_unix_get_fd(dev->ctrl_io); if (write(fd, &hdr, sizeof(hdr)) < 0) { error("hidhost: Error writing device_get_protocol: %s (%d)", strerror(errno), errno); status = HAL_STATUS_FAILED; goto failed; } dev->last_hid_msg = HID_MSG_GET_PROTOCOL; status = HAL_STATUS_SUCCESS; failed: ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_HIDHOST, HAL_OP_HIDHOST_GET_PROTOCOL, status); } static void bt_hid_set_protocol(const void *buf, uint16_t len) { const struct hal_cmd_hidhost_set_protocol *cmd = buf; struct hid_device *dev; GSList *l; bdaddr_t dst; int fd; uint8_t hdr; uint8_t status; DBG(""); switch (cmd->mode) { case HAL_HIDHOST_REPORT_PROTOCOL: case HAL_HIDHOST_BOOT_PROTOCOL: break; default: status = HAL_STATUS_INVALID; goto failed; } android2bdaddr(&cmd->bdaddr, &dst); l = g_slist_find_custom(devices, &dst, device_cmp); if (!l) { status = HAL_STATUS_FAILED; goto failed; } dev = l->data; hdr = HID_MSG_SET_PROTOCOL | cmd->mode; fd = g_io_channel_unix_get_fd(dev->ctrl_io); if (write(fd, &hdr, sizeof(hdr)) < 0) { error("hidhost: error writing device_set_protocol: %s (%d)", strerror(errno), errno); status = HAL_STATUS_FAILED; goto failed; } dev->last_hid_msg = HID_MSG_SET_PROTOCOL; status = HAL_STATUS_SUCCESS; failed: ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_HIDHOST, HAL_OP_HIDHOST_SET_PROTOCOL, status); } static void bt_hid_get_report(const void *buf, uint16_t len) { const struct hal_cmd_hidhost_get_report *cmd = buf; struct hid_device *dev; GSList *l; bdaddr_t dst; int fd; uint8_t *req; uint8_t req_size; uint8_t status; DBG(""); switch (cmd->type) { case HAL_HIDHOST_INPUT_REPORT: case HAL_HIDHOST_OUTPUT_REPORT: case HAL_HIDHOST_FEATURE_REPORT: break; default: status = HAL_STATUS_INVALID; goto failed; } android2bdaddr(&cmd->bdaddr, &dst); l = g_slist_find_custom(devices, &dst, device_cmp); if (!l) { status = HAL_STATUS_FAILED; goto failed; } dev = l->data; req_size = (cmd->buf_size > 0) ? 4 : 2; req = g_try_malloc0(req_size); if (!req) { status = HAL_STATUS_NOMEM; goto failed; } req[0] = HID_MSG_GET_REPORT | cmd->type; req[1] = cmd->id; if (cmd->buf_size > 0) { req[0] = req[0] | HID_GET_REPORT_SIZE_FIELD; put_le16(cmd->buf_size, &req[2]); } fd = g_io_channel_unix_get_fd(dev->ctrl_io); if (write(fd, req, req_size) < 0) { error("hidhost: error writing hid_get_report: %s (%d)", strerror(errno), errno); g_free(req); status = HAL_STATUS_FAILED; goto failed; } dev->last_hid_msg = HID_MSG_GET_REPORT; g_free(req); status = HAL_STATUS_SUCCESS; failed: ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_HIDHOST, HAL_OP_HIDHOST_GET_REPORT, status); } static void bt_hid_set_report(const void *buf, uint16_t len) { const struct hal_cmd_hidhost_set_report *cmd = buf; struct hid_device *dev; GSList *l; bdaddr_t dst; int fd; uint8_t *req = NULL; uint8_t req_size; uint8_t status; DBG(""); if (len != sizeof(*cmd) + cmd->len) { error("Invalid hid set report size (%u bytes), terminating", len); raise(SIGTERM); return; } switch (cmd->type) { case HAL_HIDHOST_INPUT_REPORT: case HAL_HIDHOST_OUTPUT_REPORT: case HAL_HIDHOST_FEATURE_REPORT: break; default: status = HAL_STATUS_INVALID; goto failed; } android2bdaddr(&cmd->bdaddr, &dst); l = g_slist_find_custom(devices, &dst, device_cmp); if (!l) { status = HAL_STATUS_FAILED; goto failed; } dev = l->data; if (!dev->ctrl_io && !dev->hog) { status = HAL_STATUS_FAILED; goto failed; } req_size = 1 + (cmd->len / 2); req = g_try_malloc0(req_size); if (!req) { status = HAL_STATUS_NOMEM; goto failed; } req[0] = HID_MSG_SET_REPORT | cmd->type; /* * Report data coming to HAL is in ascii format, HAL sends * data in hex to daemon, so convert to binary. */ if (!hex2buf(cmd->data, req + 1, req_size - 1)) { status = HAL_STATUS_INVALID; goto failed; } if (dev->hog) { if (bt_hog_send_report(dev->hog, req + 1, req_size - 1, cmd->type) < 0) { status = HAL_STATUS_FAILED; goto failed; } goto done; } fd = g_io_channel_unix_get_fd(dev->ctrl_io); if (write(fd, req, req_size) < 0) { error("hidhost: error writing hid_set_report: %s (%d)", strerror(errno), errno); status = HAL_STATUS_FAILED; goto failed; } dev->last_hid_msg = HID_MSG_SET_REPORT; done: status = HAL_STATUS_SUCCESS; failed: g_free(req); ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_HIDHOST, HAL_OP_HIDHOST_SET_REPORT, status); } static void bt_hid_send_data(const void *buf, uint16_t len) { const struct hal_cmd_hidhost_send_data *cmd = buf; struct hid_device *dev; GSList *l; bdaddr_t dst; int fd; uint8_t *req = NULL; uint8_t req_size; uint8_t status; DBG(""); if (len != sizeof(*cmd) + cmd->len) { error("Invalid hid send data size (%u bytes), terminating", len); raise(SIGTERM); return; } android2bdaddr(&cmd->bdaddr, &dst); l = g_slist_find_custom(devices, &dst, device_cmp); if (!l) { status = HAL_STATUS_FAILED; goto failed; } dev = l->data; if (!(dev->intr_io)) { status = HAL_STATUS_FAILED; goto failed; } req_size = 1 + (cmd->len / 2); req = g_try_malloc0(req_size); if (!req) { status = HAL_STATUS_NOMEM; goto failed; } req[0] = HID_MSG_DATA | HID_DATA_TYPE_OUTPUT; /* * Report data coming to HAL is in ascii format, HAL sends * data in hex to daemon, so convert to binary. */ if (!hex2buf(cmd->data, req + 1, req_size - 1)) { status = HAL_STATUS_INVALID; goto failed; } fd = g_io_channel_unix_get_fd(dev->intr_io); if (write(fd, req, req_size) < 0) { error("hidhost: error writing data to HID device: %s (%d)", strerror(errno), errno); status = HAL_STATUS_FAILED; goto failed; } status = HAL_STATUS_SUCCESS; failed: g_free(req); ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_HIDHOST, HAL_OP_HIDHOST_SEND_DATA, status); } static const struct ipc_handler cmd_handlers[] = { /* HAL_OP_HIDHOST_CONNECT */ { bt_hid_connect, false, sizeof(struct hal_cmd_hidhost_connect) }, /* HAL_OP_HIDHOST_DISCONNECT */ { bt_hid_disconnect, false, sizeof(struct hal_cmd_hidhost_disconnect) }, /* HAL_OP_HIDHOST_VIRTUAL_UNPLUG */ { bt_hid_virtual_unplug, false, sizeof(struct hal_cmd_hidhost_virtual_unplug) }, /* HAL_OP_HIDHOST_SET_INFO */ { bt_hid_info, true, sizeof(struct hal_cmd_hidhost_set_info) }, /* HAL_OP_HIDHOST_GET_PROTOCOL */ { bt_hid_get_protocol, false, sizeof(struct hal_cmd_hidhost_get_protocol) }, /* HAL_OP_HIDHOST_SET_PROTOCOL */ { bt_hid_set_protocol, false, sizeof(struct hal_cmd_hidhost_get_protocol) }, /* HAL_OP_HIDHOST_GET_REPORT */ { bt_hid_get_report, false, sizeof(struct hal_cmd_hidhost_get_report) }, /* HAL_OP_HIDHOST_SET_REPORT */ { bt_hid_set_report, true, sizeof(struct hal_cmd_hidhost_set_report) }, /* HAL_OP_HIDHOST_SEND_DATA */ { bt_hid_send_data, true, sizeof(struct hal_cmd_hidhost_send_data) }, }; static void connect_cb(GIOChannel *chan, GError *err, gpointer user_data) { struct hid_device *dev; bdaddr_t dst; char address[18]; uint16_t psm; GError *gerr = NULL; GSList *l; uuid_t uuid; if (err) { error("hidhost: Connect failed (%s)", err->message); return; } bt_io_get(chan, &gerr, BT_IO_OPT_DEST_BDADDR, &dst, BT_IO_OPT_PSM, &psm, BT_IO_OPT_INVALID); if (gerr) { error("hidhost: Failed to read remote address (%s)", gerr->message); g_io_channel_shutdown(chan, TRUE, NULL); g_error_free(gerr); return; } ba2str(&dst, address); DBG("Incoming connection from %s on PSM %d", address, psm); if (!bt_device_is_bonded(&dst)) { warn("hidhost: Rejecting connection from unknown device %s", address); if (psm == L2CAP_PSM_HIDP_CTRL) bt_hid_write_virtual_unplug(chan); g_io_channel_shutdown(chan, TRUE, NULL); return; } switch (psm) { case L2CAP_PSM_HIDP_CTRL: l = g_slist_find_custom(devices, &dst, device_cmp); if (l) return; dev = hid_device_new(&dst); dev->ctrl_io = g_io_channel_ref(chan); sdp_uuid16_create(&uuid, PNP_INFO_SVCLASS_ID); if (bt_search_service(&adapter_addr, &dev->dst, &uuid, hid_sdp_did_search_cb, dev, NULL, 0) < 0) { error("hidhost: Failed to search DID SDP details"); hid_device_remove(dev); return; } dev->ctrl_watch = g_io_add_watch(dev->ctrl_io, G_IO_HUP | G_IO_ERR | G_IO_NVAL, ctrl_watch_cb, dev); bt_hid_notify_state(dev, HAL_HIDHOST_STATE_CONNECTING); break; case L2CAP_PSM_HIDP_INTR: l = g_slist_find_custom(devices, &dst, device_cmp); if (!l) return; dev = l->data; dev->intr_io = g_io_channel_ref(chan); dev->intr_watch = g_io_add_watch(dev->intr_io, G_IO_IN | G_IO_HUP | G_IO_ERR | G_IO_NVAL, intr_watch_cb, dev); bt_hid_notify_state(dev, HAL_HIDHOST_STATE_CONNECTED); break; } } static void hid_unpaired_cb(const bdaddr_t *addr) { GSList *l; struct hid_device *dev; char address[18]; l = g_slist_find_custom(devices, addr, device_cmp); if (!l) return; dev = l->data; ba2str(addr, address); DBG("Unpaired device %s", address); if (hog_app) bt_gatt_remove_autoconnect(hog_app, addr); hid_device_remove(dev); } bool bt_hid_register(struct ipc *ipc, const bdaddr_t *addr, uint8_t mode) { GError *err = NULL; DBG(""); if (!bt_unpaired_register(hid_unpaired_cb)) { error("hidhost: Could not register unpaired callback"); return false; } bacpy(&adapter_addr, addr); ctrl_io = bt_io_listen(connect_cb, NULL, NULL, NULL, &err, BT_IO_OPT_SOURCE_BDADDR, &adapter_addr, BT_IO_OPT_PSM, L2CAP_PSM_HIDP_CTRL, BT_IO_OPT_SEC_LEVEL, BT_IO_SEC_LOW, BT_IO_OPT_INVALID); if (!ctrl_io) { error("hidhost: Failed to listen on control channel: %s", err->message); g_error_free(err); return false; } intr_io = bt_io_listen(connect_cb, NULL, NULL, NULL, &err, BT_IO_OPT_SOURCE_BDADDR, &adapter_addr, BT_IO_OPT_PSM, L2CAP_PSM_HIDP_INTR, BT_IO_OPT_SEC_LEVEL, BT_IO_SEC_LOW, BT_IO_OPT_INVALID); if (!intr_io) { error("hidhost: Failed to listen on interrupt channel: %s", err->message); g_error_free(err); g_io_channel_shutdown(ctrl_io, TRUE, NULL); g_io_channel_unref(ctrl_io); ctrl_io = NULL; return false; } hal_ipc = ipc; ipc_register(hal_ipc, HAL_SERVICE_ID_HIDHOST, cmd_handlers, G_N_ELEMENTS(cmd_handlers)); return true; } void bt_hid_unregister(void) { DBG(""); if (hog_app > 0) bt_gatt_unregister_app(hog_app); g_slist_free_full(devices, hid_device_free); devices = NULL; if (ctrl_io) { g_io_channel_shutdown(ctrl_io, TRUE, NULL); g_io_channel_unref(ctrl_io); ctrl_io = NULL; } if (intr_io) { g_io_channel_shutdown(intr_io, TRUE, NULL); g_io_channel_unref(intr_io); intr_io = NULL; } ipc_unregister(hal_ipc, HAL_SERVICE_ID_HIDHOST); hal_ipc = NULL; bt_unpaired_unregister(hid_unpaired_cb); } bluez-5.82/android/PaxHeaders/tester-hdp.c0000644000000000000000000000005014015011623015502 xustar0020 atime=1743516864 20 ctime=1743591278 bluez-5.82/android/tester-hdp.c0000644000000000000000000004345514015011623015176 0ustar00rootroot// SPDX-License-Identifier: Apache-2.0 /* * Copyright (C) 2014 Intel Corporation * */ #define _GNU_SOURCE #include #include #include "emulator/bthost.h" #include "lib/bluetooth.h" #include "android/utils.h" #include "src/shared/tester.h" #include "src/shared/queue.h" #include "tester-main.h" typedef enum { HDP_APP_SINK_RELIABLE, HDP_APP_SINK_STREAM, HDP_APP_SOURCE_RELIABLE, HDP_APP_SOURCE_STREAM, } hdp_app_reg_type; #define hdp_rsp_pdu 0x07, \ 0x00, 0x00, \ 0x01, 0xc8, \ 0x01, 0xc5, \ 0x36, 0x01, 0xc2, 0x36, 0x01, 0xbf, 0x09, 0x00, 0x00, \ 0x0a, 0x00, 0x01, 0x00, 0x00, 0x09, 0x00, 0x01, 0x35, \ 0x03, 0x19, 0x14, 0x01, 0x09, 0x00, 0x04, 0x35, 0x10, \ 0x35, 0x06, 0x19, 0x01, 0x00, 0x09, 0x10, 0x01, 0x35, \ 0x06, 0x19, 0x00, 0x1e, 0x09, 0x01, 0x00, 0x09, 0x00, \ 0x09, 0x35, 0x08, 0x35, 0x06, 0x19, 0x14, 0x00, 0x09, \ 0x01, 0x01, 0x09, 0x00, 0x0d, 0x35, 0x0f, 0x35, 0x0d, \ 0x35, 0x06, 0x19, 0x01, 0x00, 0x09, 0x10, 0x03, 0x35, \ 0x03, 0x19, 0x00, 0x1f, 0x09, 0x01, 0x00, 0x25, 0x03, \ 0x48, 0x44, 0x50, 0x09, 0x01, 0x01, 0x25, 0x28, 0x43, \ 0x6f, 0x6c, 0x6c, 0x65, 0x63, 0x74, 0x2c, 0x20, 0x64, \ 0x69, 0x73, 0x70, 0x6c, 0x61, 0x79, 0x2c, 0x20, 0x61, \ 0x6e, 0x64, 0x20, 0x72, 0x65, 0x8b, 0x6c, 0x61, 0x79, \ 0x20, 0x68, 0x65, 0x61, 0x6c, 0x74, 0x68, 0x20, 0x64, \ 0x61, 0x74, 0x61, 0x09, 0x01, 0x02, 0x25, 0x0d, 0x42, \ 0x4c, 0x55, 0x45, 0x54, 0x4f, 0x4f, 0x54, 0x48, 0x20, \ 0x53, 0x49, 0x47, 0x09, 0x02, 0x00, 0x36, 0x01, 0x22, \ 0x35, 0x18, 0x08, 0x01, 0x09, 0x10, 0x04, 0x08, 0x00, \ 0x25, 0x0f, 0x50, 0x75, 0x6c, 0x73, 0x65, 0x20, 0x4f, \ 0x78, 0x69, 0x6d, 0x65, 0x74, 0x65, 0x72, 0x0d, 0x35, \ 0x20, 0x08, 0x02, 0x09, 0x10, 0x07, 0x08, 0x00, 0x25, \ 0x17, 0x42, 0x6c, 0x6f, 0x6f, 0x64, 0x20, 0x50, 0x72, \ 0x65, 0x73, 0x73, 0x75, 0x72, 0x65, 0x20, 0x4d, 0x6f, \ 0x6e, 0x69, 0x74, 0x6f, 0x72, 0x0d, 0x35, 0x1a, 0x08, \ 0x03, 0x09, 0x10, 0x08, 0x08, 0x00, 0x25, 0x11, 0x42, \ 0x6f, 0x64, 0x79, 0x20, 0x54, 0x68, 0x65, 0x72, 0x6d, \ 0x6f, 0x6d, 0x65, 0x74, 0x65, 0x72, 0x0d, 0x35, 0x1e, \ 0x08, 0x04, 0x09, 0x10, 0x0f, 0x08, 0x00, 0x25, 0x15, \ 0x42, 0x6f, 0x64, 0x79, 0x20, 0x57, 0x65, 0x69, 0x67, \ 0x68, 0x74, 0x20, 0x53, 0x63, 0x61, 0x6c, 0x65, 0x09, \ 0x09, 0x09, 0x0d, 0x35, 0x17, 0x08, 0x05, 0x09, 0x10, \ 0x11, 0x08, 0x00, 0x25, 0x0e, 0x47, 0x6c, 0x75, 0x63, \ 0x6f, 0x73, 0x65, 0x20, 0x4d, 0x65, 0x74, 0x65, 0x72, \ 0x0d, 0x35, 0x18, 0x08, 0x06, 0x09, 0x10, 0x04, 0x08, \ 0x01, 0x25, 0x0f, 0x50, 0x75, 0x6c, 0x73, 0x65, 0x20, \ 0x4f, 0x78, 0x69, 0x6d, 0x65, 0x74, 0x65, 0x72, 0x0d, \ 0x35, 0x20, 0x08, 0x07, 0x09, 0x10, 0x07, 0x08, 0x01, \ 0x25, 0x17, 0x42, 0x6c, 0x6f, 0x6f, 0x64, 0x20, 0x50, \ 0x72, 0x65, 0x73, 0x73, 0x75, 0x72, 0x65, 0x20, 0x4d, \ 0x6f, 0x6e, 0x69, 0x74, 0x6f, 0x72, 0x0d, 0x35, 0x1a, \ 0x08, 0x08, 0x09, 0x10, 0x08, 0x08, 0x01, 0x25, 0x11, \ 0x42, 0x6f, 0x64, 0x79, 0x20, 0x54, 0x68, 0x65, 0x72, \ 0x6d, 0x6f, 0x6d, 0x65, 0x74, 0x65, 0x72, 0x0d, 0x35, \ 0x1e, 0x08, 0x09, 0x09, 0x10, 0x0f, 0x08, 0x01, 0x25, \ 0x15, 0x42, 0x6f, 0x64, 0x79, 0x20, 0x57, 0x65, 0x69, \ 0x67, 0x68, 0x74, 0x20, 0x53, 0x63, 0x61, 0x6c, 0x65, \ 0x09, 0x09, 0x09, 0x0d, 0x35, 0x17, 0x08, 0x0a, 0x09, \ 0x10, 0x11, 0x08, 0x01, 0x25, 0x0e, 0x47, 0x6c, 0x75, \ 0x63, 0x6f, 0x73, 0x65, 0x20, 0x4d, 0x65, 0x74, 0x65, \ 0x72, 0x0d, 0x09, 0x03, 0x01, 0x08, 0x01, 0x09, 0x03, \ 0x02, 0x08, 0x00, \ 0x00 static const struct pdu_set sdp_pdus[] = { { end_pdu, raw_pdu(hdp_rsp_pdu) }, { end_pdu, end_pdu }, }; static struct emu_l2cap_cid_data sdp_cid_data = { .pdu = sdp_pdus, .is_sdp = TRUE, }; static struct emu_l2cap_cid_data ctrl_cid_data; static struct emu_l2cap_cid_data data_cid_data; static struct queue *list; /* List of hdp test cases */ static bthl_reg_param_t *create_app(hdp_app_reg_type type) { bthl_reg_param_t *reg; bthl_mdep_cfg_t mdep1, mdep2; reg = malloc(sizeof(bthl_reg_param_t)); reg->application_name = "bluez-android"; reg->provider_name = "Bluez"; reg->srv_name = "bluez-hdp"; reg->srv_desp = "health-device-profile"; mdep1.data_type = 4100; mdep1.mdep_description = "pulse-oximeter"; mdep2.data_type = 4100; mdep2.mdep_description = "pulse-oximeter"; switch (type) { case HDP_APP_SINK_RELIABLE: reg->number_of_mdeps = 1; mdep1.mdep_role = BTHL_MDEP_ROLE_SINK; mdep1.channel_type = BTHL_CHANNEL_TYPE_RELIABLE; reg->mdep_cfg = malloc(reg->number_of_mdeps * sizeof(bthl_mdep_cfg_t)); reg->mdep_cfg[0] = mdep1; break; case HDP_APP_SINK_STREAM: reg->number_of_mdeps = 2; mdep1.mdep_role = BTHL_MDEP_ROLE_SINK; mdep1.channel_type = BTHL_CHANNEL_TYPE_RELIABLE; mdep2.mdep_role = BTHL_MDEP_ROLE_SINK; mdep2.channel_type = BTHL_CHANNEL_TYPE_STREAMING; reg->mdep_cfg = malloc(reg->number_of_mdeps * sizeof(bthl_mdep_cfg_t)); reg->mdep_cfg[0] = mdep1; reg->mdep_cfg[1] = mdep2; break; case HDP_APP_SOURCE_RELIABLE: reg->number_of_mdeps = 1; mdep1.mdep_role = BTHL_MDEP_ROLE_SOURCE; mdep1.channel_type = BTHL_CHANNEL_TYPE_RELIABLE; reg->mdep_cfg = malloc(reg->number_of_mdeps * sizeof(bthl_mdep_cfg_t)); reg->mdep_cfg[0] = mdep1; break; case HDP_APP_SOURCE_STREAM: reg->number_of_mdeps = 2; mdep1.mdep_role = BTHL_MDEP_ROLE_SOURCE; mdep1.channel_type = BTHL_CHANNEL_TYPE_RELIABLE; mdep2.mdep_role = BTHL_MDEP_ROLE_SOURCE; mdep2.channel_type = BTHL_CHANNEL_TYPE_STREAMING; reg->mdep_cfg = malloc(reg->number_of_mdeps * sizeof(bthl_mdep_cfg_t)); reg->mdep_cfg[0] = mdep1; reg->mdep_cfg[1] = mdep2; break; } return reg; } static void hdp_register_sink_reliable_app_action(void) { struct test_data *data = tester_get_data(); struct step *step = g_new0(struct step, 1); int app_id = 0; bthl_reg_param_t *reg; reg = create_app(HDP_APP_SINK_RELIABLE); step->action_status = data->if_hdp->register_application(reg, &app_id); schedule_action_verification(step); free(reg->mdep_cfg); free(reg); } static void hdp_register_sink_stream_app_action(void) { struct test_data *data = tester_get_data(); struct step *step = g_new0(struct step, 1); int app_id = 0; bthl_reg_param_t *reg; reg = create_app(HDP_APP_SINK_STREAM); step->action_status = data->if_hdp->register_application(reg, &app_id); schedule_action_verification(step); free(reg->mdep_cfg); free(reg); } static void hdp_register_source_reliable_app_action(void) { struct test_data *data = tester_get_data(); struct step *step = g_new0(struct step, 1); int app_id = 0; bthl_reg_param_t *reg; reg = create_app(HDP_APP_SOURCE_RELIABLE); step->action_status = data->if_hdp->register_application(reg, &app_id); schedule_action_verification(step); free(reg->mdep_cfg); free(reg); } static void hdp_register_source_stream_app_action(void) { struct test_data *data = tester_get_data(); struct step *step = g_new0(struct step, 1); int app_id = 0; bthl_reg_param_t *reg; reg = create_app(HDP_APP_SOURCE_STREAM); step->action_status = data->if_hdp->register_application(reg, &app_id); schedule_action_verification(step); free(reg->mdep_cfg); free(reg); } static void hdp_unregister_app_action(void) { struct test_data *data = tester_get_data(); struct step *step = g_new0(struct step, 1); step->action_status = data->if_hdp->unregister_application(1); schedule_action_verification(step); } static void mcap_ctrl_cid_hook_cb(const void *data, uint16_t len, void *user_data) { struct test_data *t_data = tester_get_data(); struct bthost *bthost = hciemu_client_get_host(t_data->hciemu); struct emu_l2cap_cid_data *cid_data = user_data; uint8_t crt_rsp[5], del_rsp[4], config; uint8_t opcode = ((uint8_t *) data)[0]; static bool reliable = false; switch (opcode) { case 0x01: /* MD_CREATE_MDL_REQ */ crt_rsp[0] = 0x02; /* MD_CREATE_MDL_RSP */ crt_rsp[1] = 0x00; /* Response code - Success */ crt_rsp[2] = ((uint8_t *) data)[1]; /* mdlid */ crt_rsp[3] = ((uint8_t *) data)[2]; config = ((uint8_t *) data)[4]; if (config == 0x00) { if (!reliable) { crt_rsp[4] = 0x01; reliable = true; } else { crt_rsp[4] = 0x02; reliable = false; } } else { crt_rsp[4] = config; } bthost_send_cid(bthost, cid_data->handle, cid_data->cid, crt_rsp, sizeof(crt_rsp)); break; case 0x03: /* MD_RECONNECT_MDL_REQ */ case 0x05: /* MD_ABORT_MDL_REQ */ break; case 0x07: /* MD_DELETE_MDL_REQ */ del_rsp[0] = 0x08; /* MD_DELETE_MDL_RSP */ del_rsp[1] = 0x00; /* Response code - Success */ del_rsp[2] = ((uint8_t *) data)[1]; /* mdlid */ del_rsp[3] = ((uint8_t *) data)[2]; bthost_send_cid(bthost, cid_data->handle, cid_data->cid, del_rsp, sizeof(del_rsp)); break; } } static void mcap_ctrl_connect_cb(uint16_t handle, uint16_t cid, void *user_data) { struct test_data *data = tester_get_data(); struct bthost *bthost = hciemu_client_get_host(data->hciemu); struct emu_l2cap_cid_data *cid_data = user_data; cid_data->handle = handle; cid_data->cid = cid; bthost_add_cid_hook(bthost, handle, cid, mcap_ctrl_cid_hook_cb, cid_data); } /* Emulate SDP (PSM = 1) */ static struct emu_set_l2cap_data l2cap_setup_sdp_data = { .psm = 1, .func = tester_generic_connect_cb, .user_data = &sdp_cid_data, }; /* Emulate Control Channel (PSM = 0x1001) */ static struct emu_set_l2cap_data l2cap_setup_cc_data = { .psm = 0x1001, .func = mcap_ctrl_connect_cb, .user_data = &ctrl_cid_data, }; /* Emulate Data Channel (PSM = 0x1003) */ static struct emu_set_l2cap_data l2cap_setup_dc_data = { .psm = 0x1003, .func = tester_generic_connect_cb, .user_data = &data_cid_data, }; static void hdp_connect_source_reliable_action(void) { struct test_data *data = tester_get_data(); const uint8_t *hid_addr = hciemu_get_client_bdaddr(data->hciemu); struct step *step = g_new0(struct step, 1); bt_bdaddr_t bdaddr; int app_id, channel_id, mdep_cfg_index; bdaddr2android((const bdaddr_t *) hid_addr, &bdaddr); app_id = 1; mdep_cfg_index = 0; channel_id = 0; step->action_status = data->if_hdp->connect_channel(app_id, &bdaddr, mdep_cfg_index, &channel_id); schedule_action_verification(step); } static void hdp_destroy_source_reliable_action(void) { struct test_data *data = tester_get_data(); struct step *step = g_new0(struct step, 1); step->action_status = data->if_hdp->destroy_channel(1); schedule_action_verification(step); } static void hdp_connect_sink_reliable_action(void) { struct test_data *data = tester_get_data(); const uint8_t *hid_addr = hciemu_get_client_bdaddr(data->hciemu); struct step *step = g_new0(struct step, 1); bt_bdaddr_t bdaddr; int app_id, channel_id, mdep_cfg_index; bdaddr2android((const bdaddr_t *) hid_addr, &bdaddr); app_id = 1; mdep_cfg_index = 0; channel_id = 0; step->action_status = data->if_hdp->connect_channel(app_id, &bdaddr, mdep_cfg_index, &channel_id); schedule_action_verification(step); } static void hdp_connect_sink_stream_action(void) { struct test_data *data = tester_get_data(); const uint8_t *hid_addr = hciemu_get_client_bdaddr(data->hciemu); struct step *step = g_new0(struct step, 1); bt_bdaddr_t bdaddr; int app_id, channel_id, mdep_cfg_index; bdaddr2android((const bdaddr_t *) hid_addr, &bdaddr); app_id = 1; mdep_cfg_index = 1; channel_id = 0; step->action_status = data->if_hdp->connect_channel(app_id, &bdaddr, mdep_cfg_index, &channel_id); schedule_action_verification(step); } static void hdp_destroy_sink_reliable_action(void) { struct test_data *data = tester_get_data(); struct step *step = g_new0(struct step, 1); step->action_status = data->if_hdp->destroy_channel(1); schedule_action_verification(step); } static void hdp_destroy_sink_stream_action(void) { struct test_data *data = tester_get_data(); struct step *step = g_new0(struct step, 1); step->action_status = data->if_hdp->destroy_channel(2); schedule_action_verification(step); } static struct test_case test_cases[] = { TEST_CASE_BREDRLE("HDP Init", ACTION_SUCCESS(dummy_action, NULL), ), TEST_CASE_BREDRLE("HDP Register Sink Reliable Application", ACTION_SUCCESS(hdp_register_sink_reliable_app_action, NULL), CALLBACK_HDP_APP_REG_STATE(CB_HDP_APP_REG_STATE, 1, BTHL_APP_REG_STATE_REG_SUCCESS), ), TEST_CASE_BREDRLE("HDP Register Sink Stream Application", ACTION_SUCCESS(hdp_register_sink_stream_app_action, NULL), CALLBACK_HDP_APP_REG_STATE(CB_HDP_APP_REG_STATE, 1, BTHL_APP_REG_STATE_REG_SUCCESS), ), TEST_CASE_BREDRLE("HDP Register Source Reliable Application", ACTION_SUCCESS(hdp_register_source_reliable_app_action, NULL), CALLBACK_HDP_APP_REG_STATE(CB_HDP_APP_REG_STATE, 1, BTHL_APP_REG_STATE_REG_SUCCESS), ), TEST_CASE_BREDRLE("HDP Register Source Stream Application", ACTION_SUCCESS(hdp_register_source_stream_app_action, NULL), CALLBACK_HDP_APP_REG_STATE(CB_HDP_APP_REG_STATE, 1, BTHL_APP_REG_STATE_REG_SUCCESS), ), TEST_CASE_BREDRLE("HDP Unegister Application", ACTION_SUCCESS(hdp_register_source_stream_app_action, NULL), CALLBACK_HDP_APP_REG_STATE(CB_HDP_APP_REG_STATE, 1, BTHL_APP_REG_STATE_REG_SUCCESS), ACTION_SUCCESS(hdp_unregister_app_action, NULL), CALLBACK_HDP_APP_REG_STATE(CB_HDP_APP_REG_STATE, 1, BTHL_APP_REG_STATE_DEREG_SUCCESS), ), TEST_CASE_BREDRLE("HDP Connect Source Reliable Channel", ACTION_SUCCESS(set_default_ssp_request_handler, NULL), ACTION_SUCCESS(bluetooth_enable_action, NULL), CALLBACK_STATE(CB_BT_ADAPTER_STATE_CHANGED, BT_STATE_ON), ACTION_SUCCESS(emu_setup_powered_remote_action, NULL), ACTION_SUCCESS(emu_set_ssp_mode_action, NULL), ACTION_SUCCESS(emu_add_l2cap_server_action, &l2cap_setup_sdp_data), ACTION_SUCCESS(emu_add_l2cap_server_action, &l2cap_setup_cc_data), ACTION_SUCCESS(emu_add_l2cap_server_action, &l2cap_setup_dc_data), ACTION_SUCCESS(hdp_register_source_reliable_app_action, NULL), CALLBACK_HDP_APP_REG_STATE(CB_HDP_APP_REG_STATE, 1, BTHL_APP_REG_STATE_REG_SUCCESS), ACTION_SUCCESS(hdp_connect_source_reliable_action, NULL), CALLBACK_HDP_CHANNEL_STATE(CB_HDP_CHANNEL_STATE, 1, 1, 0, BTHL_CONN_STATE_CONNECTING), CALLBACK_HDP_CHANNEL_STATE(CB_HDP_CHANNEL_STATE, 1, 1, 0, BTHL_CONN_STATE_CONNECTED), ), TEST_CASE_BREDRLE("HDP Destroy Source Reliable Channel", ACTION_SUCCESS(set_default_ssp_request_handler, NULL), ACTION_SUCCESS(bluetooth_enable_action, NULL), CALLBACK_STATE(CB_BT_ADAPTER_STATE_CHANGED, BT_STATE_ON), ACTION_SUCCESS(emu_setup_powered_remote_action, NULL), ACTION_SUCCESS(emu_set_ssp_mode_action, NULL), ACTION_SUCCESS(emu_add_l2cap_server_action, &l2cap_setup_sdp_data), ACTION_SUCCESS(emu_add_l2cap_server_action, &l2cap_setup_cc_data), ACTION_SUCCESS(emu_add_l2cap_server_action, &l2cap_setup_dc_data), ACTION_SUCCESS(hdp_register_source_reliable_app_action, NULL), CALLBACK_HDP_APP_REG_STATE(CB_HDP_APP_REG_STATE, 1, BTHL_APP_REG_STATE_REG_SUCCESS), ACTION_SUCCESS(hdp_connect_source_reliable_action, NULL), CALLBACK_HDP_CHANNEL_STATE(CB_HDP_CHANNEL_STATE, 1, 1, 0, BTHL_CONN_STATE_CONNECTING), CALLBACK_HDP_CHANNEL_STATE(CB_HDP_CHANNEL_STATE, 1, 1, 0, BTHL_CONN_STATE_CONNECTED), ACTION_SUCCESS(hdp_destroy_source_reliable_action, NULL), CALLBACK_HDP_CHANNEL_STATE(CB_HDP_CHANNEL_STATE, 1, 1, 0, BTHL_CONN_STATE_DESTROYED), ), TEST_CASE_BREDRLE("HDP Connect Sink Streaming Channel", ACTION_SUCCESS(set_default_ssp_request_handler, NULL), ACTION_SUCCESS(bluetooth_enable_action, NULL), CALLBACK_STATE(CB_BT_ADAPTER_STATE_CHANGED, BT_STATE_ON), ACTION_SUCCESS(emu_setup_powered_remote_action, NULL), ACTION_SUCCESS(emu_set_ssp_mode_action, NULL), ACTION_SUCCESS(emu_add_l2cap_server_action, &l2cap_setup_sdp_data), ACTION_SUCCESS(emu_add_l2cap_server_action, &l2cap_setup_cc_data), ACTION_SUCCESS(emu_add_l2cap_server_action, &l2cap_setup_dc_data), ACTION_SUCCESS(hdp_register_sink_stream_app_action, NULL), CALLBACK_HDP_APP_REG_STATE(CB_HDP_APP_REG_STATE, 1, BTHL_APP_REG_STATE_REG_SUCCESS), ACTION_SUCCESS(hdp_connect_sink_reliable_action, NULL), CALLBACK_HDP_CHANNEL_STATE(CB_HDP_CHANNEL_STATE, 1, 1, 0, BTHL_CONN_STATE_CONNECTING), CALLBACK_HDP_CHANNEL_STATE(CB_HDP_CHANNEL_STATE, 1, 1, 0, BTHL_CONN_STATE_CONNECTED), ACTION_SUCCESS(hdp_connect_sink_stream_action, NULL), CALLBACK_HDP_CHANNEL_STATE(CB_HDP_CHANNEL_STATE, 1, 2, 1, BTHL_CONN_STATE_CONNECTED), ), TEST_CASE_BREDRLE("HDP Destroy Sink Streaming Channel", ACTION_SUCCESS(set_default_ssp_request_handler, NULL), ACTION_SUCCESS(bluetooth_enable_action, NULL), CALLBACK_STATE(CB_BT_ADAPTER_STATE_CHANGED, BT_STATE_ON), ACTION_SUCCESS(emu_setup_powered_remote_action, NULL), ACTION_SUCCESS(emu_set_ssp_mode_action, NULL), ACTION_SUCCESS(emu_add_l2cap_server_action, &l2cap_setup_sdp_data), ACTION_SUCCESS(emu_add_l2cap_server_action, &l2cap_setup_cc_data), ACTION_SUCCESS(emu_add_l2cap_server_action, &l2cap_setup_dc_data), ACTION_SUCCESS(hdp_register_sink_stream_app_action, NULL), CALLBACK_HDP_APP_REG_STATE(CB_HDP_APP_REG_STATE, 1, BTHL_APP_REG_STATE_REG_SUCCESS), ACTION_SUCCESS(hdp_connect_sink_reliable_action, NULL), CALLBACK_HDP_CHANNEL_STATE(CB_HDP_CHANNEL_STATE, 1, 1, 0, BTHL_CONN_STATE_CONNECTING), CALLBACK_HDP_CHANNEL_STATE(CB_HDP_CHANNEL_STATE, 1, 1, 0, BTHL_CONN_STATE_CONNECTED), ACTION_SUCCESS(hdp_connect_sink_stream_action, NULL), CALLBACK_HDP_CHANNEL_STATE(CB_HDP_CHANNEL_STATE, 1, 2, 1, BTHL_CONN_STATE_CONNECTED), ACTION_SUCCESS(hdp_destroy_sink_reliable_action, NULL), CALLBACK_HDP_CHANNEL_STATE(CB_HDP_CHANNEL_STATE, 1, 1, 0, BTHL_CONN_STATE_DESTROYED), ACTION_SUCCESS(hdp_destroy_sink_stream_action, NULL), CALLBACK_HDP_CHANNEL_STATE(CB_HDP_CHANNEL_STATE, 1, 2, 1, BTHL_CONN_STATE_DESTROYED), ), }; struct queue *get_hdp_tests(void) { uint16_t i = 0; list = queue_new(); for (; i < sizeof(test_cases) / sizeof(test_cases[0]); ++i) queue_push_tail(list, &test_cases[i]); return list; } void remove_hdp_tests(void) { queue_destroy(list, NULL); } bluez-5.82/android/PaxHeaders/pts-a2dp.txt0000644000000000000000000000005012537515745015477 xustar0020 atime=1743516876 20 ctime=1743591289 bluez-5.82/android/pts-a2dp.txt0000644000000000000000000000400712537515745015161 0ustar00rootrootPTS test results for A2DP PTS version: 6.1 Tested 21-May-2015 Android version: 5.1 Results: PASS test passed FAIL test failed INC test is inconclusive N/A test is disabled due to PICS setup Source (SRC) ------------------------------------------------------------------------------- Test Name Result Notes ------------------------------------------------------------------------------- TC_SRC_CC_BV_09_I PASS Start streaming TC_SRC_CC_BV_10_I PASS Start streaming TC_SRC_REL_BV_01_I PASS When requested disconnect from IUT TC_SRC_REL_BV_02_I PASS Start streaming TC_SRC_SET_BV_01_I PASS TC_SRC_SET_BV_02_I PASS TC_SRC_SET_BV_03_I PASS Start streaming TC_SRC_SET_BV_04_I PASS Start streaming TC_SRC_SET_BV_05_I PASS Start streaming IUT must be moved out of range Initiate connection TC_SRC_SET_BV_06_I PASS PTS issue#13469 IUT must be moved out of range To pass set TSPX_no_avrcp to TRUE TC_SRC_SUS_BV_01_I PASS Start streaming Stop streaming Start streaming TC_SRC_SUS_BV_02_I PASS Start streaming TC_SRC_SDP_BV_01_I PASS TC_SRC_AS_BV_01_I PASS Requires checking if the output on the IUT is correct TC_SRC_AS_BV_02_I N/A TC_SRC_AS_BV_03_I N/A TC_SRC_SYN_BV_02_I N/A ------------------------------------------------------------------------------- Sink (SNK) ------------------------------------------------------------------------------- Test Name Result Notes ------------------------------------------------------------------------------- TC_SNK_CC_BV_01_I N/A TC_SNK_CC_BV_02_I N/A TC_SNK_CC_BV_03_I N/A TC_SNK_CC_BV_04_I N/A TC_SNK_CC_BV_05_I N/A TC_SNK_CC_BV_06_I N/A TC_SNK_CC_BV_07_I N/A TC_SNK_CC_BV_08_I N/A TC_SNK_REL_BV_01_I N/A TC_SNK_REL_BV_02_I N/A TC_SNK_SET_BV_01_I N/A TC_SNK_SET_BV_02_I N/A TC_SNK_SET_BV_03_I N/A TC_SNK_SET_BV_04_I N/A TC_SNK_SET_BV_05_I N/A TC_SNK_SET_BV_06_I N/A TC_SNK_SUS_BV_01_I N/A TC_SNK_SUS_BV_02_I N/A TC_SNK_SDP_BV_02_I N/A TC_SNK_AS_BV_01_I N/A TC_SNK_AS_BV_02_I N/A TC_SNK_SYN_BV_01_I N/A ------------------------------------------------------------------------------- bluez-5.82/android/PaxHeaders/pts-avdtp.txt0000644000000000000000000000005012537515745015767 xustar0020 atime=1743516876 20 ctime=1743591289 bluez-5.82/android/pts-avdtp.txt0000644000000000000000000002263512537515745015460 0ustar00rootrootPTS test results for AVDTP PTS version: 6.1 Tested: 22-May-2015 Android version: 5.1 Results: PASS test passed FAIL test failed INC test is inconclusive N/A test is disabled due to PICS setup ------------------------------------------------------------------------------- Test Name Result Notes ------------------------------------------------------------------------------- TC_ACP_SNK_L2C_EM_BV_02_C N/A TC_ACP_SNK_SIG_FRA_BV_01_C N/A TC_ACP_SNK_SIG_FRA_BV_02_C N/A TC_ACP_SNK_SIG_SEC_BI_01_C N/A TC_ACP_SNK_SIG_SEC_BV_02_C N/A TC_ACP_SNK_SIG_SMG_BV_06_C PASS avdtptest -d SINK -l TC_ACP_SNK_SIG_SMG_BV_08_C PASS avdtptest -d SINK -l TC_ACP_SNK_SIG_SMG_BV_10_C PASS avdtptest -d SINK -l TC_ACP_SNK_SIG_SMG_BV_12_C PASS avdtptest -d SINK -l TC_ACP_SNK_SIG_SMG_BV_14_C N/A TC_ACP_SNK_SIG_SMG_BV_16_C PASS avdtptest -d SINK -l TC_ACP_SNK_SIG_SMG_BV_18_C PASS avdtptest -d SINK -l TC_ACP_SNK_SIG_SMG_BV_20_C PASS avdtptest -d SINK -l TC_ACP_SNK_SIG_SMG_BV_22_C PASS avdtptest -d SINK -l TC_ACP_SNK_SIG_SMG_BV_24_C PASS avdtptest -d SINK -l TC_ACP_SNK_SIG_SMG_BV_26_C PASS avdtptest -d SINK -l TC_ACP_SNK_SIG_SMG_BV_27_C PASS avdtptest -d SINK -l TC_ACP_SNK_SIG_SMG_ESR05_BV_14_C N/A TC_ACP_SNK_SIG_SMG_BI_02_C N/A TC_ACP_SNK_SIG_SMG_BI_03_C N/A TC_ACP_SNK_SIG_SMG_BI_05_C PASS avdtptest -d SINK -l TC_ACP_SNK_SIG_SMG_BI_06_C N/A TC_ACP_SNK_SIG_SMG_BI_08_C PASS avdtptest -d SINK -l TC_ACP_SNK_SIG_SMG_BI_09_C N/A TC_ACP_SNK_SIG_SMG_BI_11_C PASS avdtptest -d SINK -l TC_ACP_SNK_SIG_SMG_BI_12_C N/A TC_ACP_SNK_SIG_SMG_BI_14_C N/A TC_ACP_SNK_SIG_SMG_BI_15_C N/A TC_ACP_SNK_SIG_SMG_BI_17_C PASS avdtptest -d SINK -l TC_ACP_SNK_SIG_SMG_BI_18_C N/A TC_ACP_SNK_SIG_SMG_BI_20_C PASS avdtptest -d SINK -l TC_ACP_SNK_SIG_SMG_BI_21_C N/A TC_ACP_SNK_SIG_SMG_BI_23_C PASS avdtptest -d SINK -l TC_ACP_SNK_SIG_SMG_BI_24_C N/A TC_ACP_SNK_SIG_SMG_BI_26_C PASS avdtptest -d SINK -l TC_ACP_SNK_SIG_SMG_BI_27_C N/A TC_ACP_SNK_SIG_SMG_BI_28_C N/A TC_ACP_SNK_SIG_SMG_BI_29_C N/A TC_ACP_SNK_SIG_SMG_BI_33_C PASS avdtptest -d SINK -l TC_ACP_SNK_SIG_SMG_BI_34_C N/A TC_ACP_SNK_SIG_SMG_ESR04_BI_28_C PASS avdtptest -d SINK -l TC_ACP_SNK_SIG_SMG_ESR05_BI_15_C N/A TC_ACP_SNK_SIG_SYN_BV_01_C PASS avdtptest -d SINK -l TC_ACP_SNK_SIG_SYN_BV_03_C PASS avdtptest -d SINK -l TC_ACP_SNK_TRA_BTR_BI_01_C PASS avdtptest -d SINK -l TC_ACP_SNK_TRA_BTR_BV_02_C PASS avdtptest -d SINK -l TC_ACP_SNK_TRA_MUX_BI_01_C N/A TC_ACP_SNK_TRA_MUX_BV_05_C N/A TC_ACP_SNK_TRA_MUX_BV_06_C N/A TC_ACP_SNK_TRA_REC_BI_01_C N/A TC_ACP_SNK_TRA_REC_BV_01_C N/A TC_ACP_SNK_TRA_REC_BV_02_C N/A TC_ACP_SNK_TRA_REP_BI_01_C N/A TC_ACP_SNK_TRA_REP_BV_01_C N/A TC_ACP_SNK_TRA_REP_BV_02_C N/A TC_ACP_SNK_TRA_REP_ESR02_BI_01_C N/A TC_ACP_SNK_TRA_RHC_BI_01_C N/A TC_ACP_SNK_TRA_RHC_BV_01_C N/A TC_ACP_SNK_TRA_RHC_BV_02_C N/A TC_ACP_SRC_L2C_EM_BV_02_C N/A TC_ACP_SRC_SIG_FRA_BV_01_C N/A TC_ACP_SRC_SIG_FRA_BV_02_C N/A TC_ACP_SRC_SIG_SEC_BI_01_C N/A TC_ACP_SRC_SIG_SEC_BV_02_C N/A TC_ACP_SRC_SIG_SMG_BV_06_C PASS avdtptest -d SRC -l TC_ACP_SRC_SIG_SMG_BV_08_C PASS avdtptest -d SRC -l TC_ACP_SRC_SIG_SMG_BV_10_C PASS avdtptest -d SRC -l TC_ACP_SRC_SIG_SMG_BV_12_C PASS avdtptest -d SRC -l TC_ACP_SRC_SIG_SMG_BV_14_C N/A TC_ACP_SRC_SIG_SMG_BV_16_C PASS avdtptest -d SRC -l TC_ACP_SRC_SIG_SMG_BV_18_C PASS avdtptest -d SRC -l TC_ACP_SRC_SIG_SMG_BV_20_C PASS avdtptest -d SRC -l TC_ACP_SRC_SIG_SMG_BV_22_C PASS avdtptest -d SRC -l TC_ACP_SRC_SIG_SMG_BV_24_C PASS avdtptest -d SRC -l TC_ACP_SRC_SIG_SMG_BV_26_C PASS avdtptest -d SRC -l TC_ACP_SRC_SIG_SMG_BV_27_C PASS avdtptest -d SRC -l TC_ACP_SRC_SIG_SMG_ESR05_BV_14_C N/A TC_ACP_SRC_SIG_SMG_BI_02_C PASS avdtptest -d SRC -l TC_ACP_SRC_SIG_SMG_BI_03_C N/A TC_ACP_SRC_SIG_SMG_BI_05_C PASS avdtptest -d SRC -l TC_ACP_SRC_SIG_SMG_BI_06_C N/A TC_ACP_SRC_SIG_SMG_BI_08_C PASS avdtptest -d SRC -l TC_ACP_SRC_SIG_SMG_BI_09_C N/A TC_ACP_SRC_SIG_SMG_BI_11_C PASS avdtptest -d SRC -l TC_ACP_SRC_SIG_SMG_BI_12_C N/A TC_ACP_SRC_SIG_SMG_BI_14_C N/A TC_ACP_SRC_SIG_SMG_BI_15_C N/A TC_ACP_SRC_SIG_SMG_BI_17_C PASS avdtptest -d SRC -l TC_ACP_SRC_SIG_SMG_BI_18_C N/A TC_ACP_SRC_SIG_SMG_BI_20_C PASS avdtptest -d SRC -l TC_ACP_SRC_SIG_SMG_BI_21_C N/A TC_ACP_SRC_SIG_SMG_BI_23_C PASS avdtptest -d SRC -l TC_ACP_SRC_SIG_SMG_BI_24_C N/A TC_ACP_SRC_SIG_SMG_BI_26_C PASS avdtptest -d SRC -l TC_ACP_SRC_SIG_SMG_BI_27_C N/A TC_ACP_SRC_SIG_SMG_BI_28_C N/A TC_ACP_SRC_SIG_SMG_BI_29_C N/A TC_ACP_SRC_SIG_SMG_BI_33_C PASS avdtptest -d SRC -l TC_ACP_SRC_SIG_SMG_BI_34_C N/A TC_ACP_SRC_SIG_SMG_ESR04_BI_28_C PASS avdtptest -d SRC -l TC_ACP_SRC_SIG_SMG_ESR05_BI_15_C N/A TC_ACP_SRC_SIG_SYN_BV_05_C PASS avdtptest -d SRC -l TC_ACP_SRC_SIG_SYN_BV_06_C PASS avdtptest -d SRC -l TC_ACP_SRC_TRA_BTR_BI_01_C PASS avdtptest -d SRC -l TC_ACP_SRC_TRA_BTR_BV_01_C PASS avdtptest -d SRC -l -p -s start TC_ACP_SRC_TRA_MUX_BI_01_C N/A TC_ACP_SRC_TRA_MUX_BV_05_C N/A TC_ACP_SRC_TRA_MUX_BV_06_C N/A TC_ACP_SRC_TRA_REC_BI_01_C N/A TC_ACP_SRC_TRA_REC_BV_01_C N/A TC_ACP_SRC_TRA_REC_BV_02_C N/A TC_ACP_SRC_TRA_REP_BI_01_C N/A TC_ACP_SRC_TRA_REP_BV_01_C N/A TC_ACP_SRC_TRA_REP_BV_02_C N/A TC_ACP_SRC_TRA_REP_ESR02_BI_01_C N/A TC_ACP_SRC_TRA_RHC_BI_01_C N/A TC_ACP_SRC_TRA_RHC_BV_01_C N/A TC_ACP_SRC_TRA_RHC_BV_02_C N/A TC_INT_SNK_L2C_BM_BV_03_C N/A TC_INT_SNK_L2C_BM_BV_06_C N/A TC_INT_SNK_SIG_FRA_BV_01_C N/A TC_INT_SNK_SIG_FRA_BV_02_C N/A TC_INT_SNK_SIG_SEC_BV_01_C N/A TC_INT_SNK_SIG_SMG_BV_05_C PASS avdtptest -d SINK -l -p TC_INT_SNK_SIG_SMG_BV_07_C PASS avdtptest -d SINK -l -p -v 0x0100 TC_INT_SNK_SIG_SMG_BV_09_C PASS avdtptest -d SINK -l -p TC_INT_SNK_SIG_SMG_BV_11_C PASS avdtptest -d SINK -l -s getconf TC_INT_SNK_SIG_SMG_BV_13_C N/A TC_INT_SNK_SIG_SMG_BV_15_C PASS avdtptest -d SINK -l -p TC_INT_SNK_SIG_SMG_BV_19_C PASS avdtptest -d SINK -l -s close TC_INT_SNK_SIG_SMG_BV_23_C PASS avdtptest -d SINK -l -p -s abort TC_INT_SNK_SIG_SMG_BV_25_C PASS avdtptest -d SINK -l -p TC_INT_SNK_SIG_SMG_BV_28_C PASS avdtptest -d SINK -l -p TC_INT_SNK_SIG_SMG_BV_31_C PASS avdtptest -d SINK -l -p -v 0x0100 TC_INT_SNK_SIG_SMG_ESR05_BV_13_C N/A TC_INT_SNK_SIG_SMG_BI_01_C N/A TC_INT_SNK_SIG_SMG_BI_04_C N/A TC_INT_SNK_SIG_SMG_BI_07_C N/A TC_INT_SNK_SIG_SMG_BI_10_C N/A TC_INT_SNK_SIG_SMG_BI_13_C N/A TC_INT_SNK_SIG_SMG_BI_16_C N/A TC_INT_SNK_SIG_SMG_BI_19_C N/A TC_INT_SNK_SIG_SMG_BI_22_C N/A TC_INT_SNK_SIG_SMG_BI_25_C N/A TC_INT_SNK_SIG_SMG_BI_30_C PASS avdtptest -d SINK -l -p -v 0x0100 TC_INT_SNK_SIG_SMG_BI_32_C N/A TC_INT_SNK_SIG_SMG_BI_35_C PASS avdtptest -d SINK -l -p TC_INT_SNK_SIG_SMG_BI_36_C PASS avdtptest -d SINK -l -p TC_INT_SNK_SIG_SMG_ESR05_BI_13_C N/A TC_INT_SNK_SIG_SYN_BV_01_C PASS avdtptest -d SINK -l -p TC_INT_SNK_SIG_SYN_BV_02_C PASS avdtptest -d SINK -l -p TC_INT_SNK_SIG_SYN_BV_03_C PASS avdtptest -d SINK -l TC_INT_SNK_SIG_SYN_BV_04_C PASS avdtptest -d SINK -l -p TC_INT_SNK_TRA_BTR_BI_01_C PASS avdtptest -d SINK -l TC_INT_SNK_TRA_BTR_BV_02_C PASS avdtptest -d SINK -l TC_INT_SNK_TRA_MUX_BI_01_C N/A TC_INT_SNK_TRA_MUX_BV_05_C N/A TC_INT_SNK_TRA_MUX_BV_06_C N/A TC_INT_SNK_TRA_REC_BI_01_C N/A TC_INT_SNK_TRA_REC_BV_01_C N/A TC_INT_SNK_TRA_REC_BV_02_C N/A TC_INT_SNK_TRA_REP_BI_01_C N/A TC_INT_SNK_TRA_REP_BV_01_C N/A TC_INT_SNK_TRA_REP_BV_02_C N/A TC_INT_SNK_TRA_REP_ESR02_BI_01_C N/A TC_INT_SNK_TRA_RHC_BI_01_C N/A TC_INT_SNK_TRA_RHC_BV_01_C N/A TC_INT_SNK_TRA_RHC_BV_02_C N/A TC_INT_SRC_L2C_BM_BV_03_C N/A TC_INT_SRC_L2C_BM_BV_06_C N/A TC_INT_SRC_SIG_FRA_BV_01_C N/A TC_INT_SRC_SIG_FRA_BV_02_C N/A TC_INT_SRC_SIG_SEC_BV_01_C N/A TC_INT_SRC_SIG_SMG_BV_05_C PASS avdtptest -d SRC -l -p TC_INT_SRC_SIG_SMG_BV_07_C PASS avdtptest -d SRC -l -p -v 0x0100 TC_INT_SRC_SIG_SMG_BV_09_C PASS avdtptest -d SRC -l -p TC_INT_SRC_SIG_SMG_BV_11_C PASS avdtptest -d SRC -l -s getconf TC_INT_SRC_SIG_SMG_BV_13_C N/A TC_INT_SRC_SIG_SMG_BV_15_C PASS avdtptest -d SRC -l -p TC_INT_SRC_SIG_SMG_BV_17_C PASS avdtptest -d SRC -l -p -s start TC_INT_SRC_SIG_SMG_BV_19_C PASS avdtptest -d SRC -l -s close TC_INT_SRC_SIG_SMG_BV_21_C PASS avdtptest -d SRC -l -s suspend TC_INT_SRC_SIG_SMG_BV_23_C PASS avdtptest -d SRC -l -p -s abort TC_INT_SRC_SIG_SMG_BV_25_C PASS avdtptest -d SRC -l -p TC_INT_SRC_SIG_SMG_BV_28_C PASS avdtptest -d SRC -l -p TC_INT_SRC_SIG_SMG_BV_31_C PASS avdtptest -d SRC -l -p -v 0x0100 TC_INT_SRC_SIG_SMG_ESR05_BV_13_C N/A TC_INT_SRC_SIG_SMG_BI_01_C N/A TC_INT_SRC_SIG_SMG_BI_04_C N/A TC_INT_SRC_SIG_SMG_BI_07_C N/A TC_INT_SRC_SIG_SMG_BI_10_C N/A TC_INT_SRC_SIG_SMG_BI_13_C N/A TC_INT_SRC_SIG_SMG_BI_16_C N/A TC_INT_SRC_SIG_SMG_BI_19_C N/A TC_INT_SRC_SIG_SMG_BI_22_C N/A TC_INT_SRC_SIG_SMG_BI_25_C N/A TC_INT_SRC_SIG_SMG_BI_30_C PASS avdtptest -d SRC -l -p -v 0x0100 TC_INT_SRC_SIG_SMG_BI_32_C N/A TC_INT_SRC_SIG_SMG_BI_35_C PASS avdtptest -d SRC -l -p TC_INT_SRC_SIG_SMG_BI_36_C PASS avdtptest -d SRC -l -p TC_INT_SRC_SIG_SMG_ESR05_BI_13_C N/A TC_INT_SRC_SIG_SYN_BV_05_C PASS avdtptest -d SRC -l TC_INT_SRC_SIG_SYN_BV_06_C PASS avdtptest -d SRC -l TC_INT_SRC_TRA_BTR_BI_01_C PASS avdtptest -d SRC -l TC_INT_SRC_TRA_BTR_BV_01_C PASS avdtptest -d SRC -l -p -s start TC_INT_SRC_TRA_MUX_BI_01_C N/A TC_INT_SRC_TRA_MUX_BV_05_C N/A TC_INT_SRC_TRA_MUX_BV_06_C N/A TC_INT_SRC_TRA_REC_BI_01_C N/A TC_INT_SRC_TRA_REC_BV_01_C N/A TC_INT_SRC_TRA_REC_BV_02_C N/A TC_INT_SRC_TRA_REP_BI_01_C N/A TC_INT_SRC_TRA_REP_BV_01_C N/A TC_INT_SRC_TRA_REP_BV_02_C N/A TC_INT_SRC_TRA_REP_ESR02_BI_01_C N/A TC_INT_SRC_TRA_RHC_BI_01_C N/A TC_INT_SRC_TRA_RHC_BV_01_C N/A TC_INT_SRC_TRA_RHC_BV_02_C N/A ------------------------------------------------------------------------------- bluez-5.82/android/PaxHeaders/pts-iopt.txt0000644000000000000000000000005012537515745015624 xustar0020 atime=1743516876 20 ctime=1743591289 bluez-5.82/android/pts-iopt.txt0000644000000000000000000000172512537515745015312 0ustar00rootrootPTS test results for IOPT PTS version: 6.1 Tested: 21-May-2015 Android version: 5.1 Results: PASS test passed FAIL test failed INC test is inconclusive N/A test is disabled due to PICS setup ------------------------------------------------------------------------------- Test Name Result Notes ------------------------------------------------------------------------------- TC_COD_BV_01_I PASS IUT must be discoverable TC_COD_BV_02_I N/A PTS issue#13473 TC_SDSS_BV_02_I PASS Note: HDP sink record should be registered before test run, e.g. register health app via HDPSample.apk TC_SDAS_BV_03_I PASS Note: HDP sink record should be registered before test run, e.g. register health app via HDPSample.apk TC_SDR_BV_04_I PASS For every asked to check PTS bt profile: haltest: bluetooth get_remote_service_record Note: 0000xxxx - acceptable 16bit uuid format ------------------------------------------------------------------------------- bluez-5.82/android/PaxHeaders/pts-opp.txt0000644000000000000000000000005012537515745015447 xustar0020 atime=1743516876 20 ctime=1743591289 bluez-5.82/android/pts-opp.txt0000644000000000000000000000631212537515745015132 0ustar00rootrootPTS test results for OPP PTS version: 6.1 Tested: 20-May-2015 Android version: 5.1 Results: PASS test passed FAIL test failed INC test is inconclusive N/A test is disabled due to PICS setup NONE test result is none ------------------------------------------------------------------------------- Test Name Result Notes ------------------------------------------------------------------------------- TC_CLIENT_BC_BV_02_I N/A TC_CLIENT_BC_BV_04_I N/A TC_CLIENT_BCE_BV_01_I N/A TC_CLIENT_BCE_BV_03_I N/A TC_CLIENT_BCE_BV_04_I N/A TC_CLIENT_BCE_BV_05_I N/A TC_CLIENT_BCE_BV_06_I N/A TC_CLIENT_BCE_BV_07_I N/A TC_CLIENT_BCP_BV_01_I N/A TC_CLIENT_BCP_BV_02_I N/A TC_CLIENT_BCP_BV_03_I N/A TC_CLIENT_BCP_BV_04_I N/A TC_CLIENT_BCP_BV_05_I N/A TC_CLIENT_CON_BV_01_C N/A TC_CLIENT_OPH_BI_01_C PASS TC_CLIENT_OPH_BV_01_I PASS TC_CLIENT_OPH_BV_02_I N/A TC_CLIENT_OPH_BV_03_I PASS TC_CLIENT_OPH_BV_04_I N/A TC_CLIENT_OPH_BV_05_I PASS TC_CLIENT_OPH_BV_07_I N/A TC_CLIENT_OPH_BV_08_I N/A TC_CLIENT_OPH_BV_09_I N/A TC_CLIENT_OPH_BV_10_I N/A TC_CLIENT_OPH_BV_11_I N/A TC_CLIENT_OPH_BV_12_I N/A TC_CLIENT_OPH_BV_13_I N/A TC_CLIENT_OPH_BV_14_I N/A TC_CLIENT_OPH_BV_15_I N/A TC_CLIENT_OPH_BV_16_I N/A TC_CLIENT_OPH_BV_17_I N/A TC_CLIENT_OPH_BV_18_I N/A TC_CLIENT_OPH_BV_19_I PASS Send file other than vCard TC_CLIENT_OPH_BV_20_I PASS TC_CLIENT_OPH_BV_22_I PASS Send file greater than 2 MB TC_CLIENT_OPH_BV_23_I PASS Send two vCards in a single operation TC_CLIENT_OPH_BV_24_I N/A TC_CLIENT_OPH_BV_25_I N/A TC_CLIENT_OPH_BV_26_I N/A TC_CLIENT_SRM_BV_01_C N/A TC_CLIENT_SRM_BV_03_C N/A TC_CLIENT_SRM_BV_05_C N/A TC_CLIENT_SRM_BV_07_C N/A TC_CLIENT_SRMP_BI_01_C N/A TC_CLIENT_SRMP_BV_01_C N/A TC_CLIENT_SRMP_BV_04_C N/A TC_CLIENT_SRMP_BV_05_C N/A TC_CLIENT_SRMP_BV_06_C N/A TC_SERVER_BC_BV_01_I N/A TC_SERVER_BC_BV_03_I N/A TC_SERVER_BCE_BV_01_I N/A TC_SERVER_BCE_BV_03_I N/A TC_SERVER_BCE_BV_04_I N/A TC_SERVER_BCE_BV_05_I N/A TC_SERVER_BCE_BV_06_I N/A TC_SERVER_BCE_BV_07_I N/A TC_SERVER_BCP_BV_01_I N/A TC_SERVER_BCP_BV_02_I PASS TC_SERVER_BCP_BV_03_I N/A TC_SERVER_BCP_BV_04_I N/A TC_SERVER_BCP_BV_05_I N/A TC_SERVER_CON_BV_02_C N/A TC_SERVER_OPH_BV_01_I PASS TC_SERVER_OPH_BV_02_I PASS TC_SERVER_OPH_BV_03_I PASS TC_SERVER_OPH_BV_04_I PASS TC_SERVER_OPH_BV_05_I PASS TC_SERVER_OPH_BV_07_I N/A TC_SERVER_OPH_BV_08_I N/A TC_SERVER_OPH_BV_09_I N/A TC_SERVER_OPH_BV_10_I PASS TC_SERVER_OPH_BV_11_I N/A TC_SERVER_OPH_BV_12_I N/A TC_SERVER_OPH_BV_13_I N/A TC_SERVER_OPH_BV_14_I PASS TC_SERVER_OPH_BV_15_I N/A TC_SERVER_OPH_BV_16_I N/A TC_SERVER_OPH_BV_17_I N/A TC_SERVER_OPH_BV_18_I PASS TC_SERVER_OPH_BV_19_I PASS TC_SERVER_OPH_BV_21_I N/A TC_SERVER_OPH_BV_22_I PASS TC_SERVER_OPH_BV_23_I PASS TC_SERVER_OPH_BV_24_I N/A TC_SERVER_OPH_BV_25_I N/A TC_SERVER_OPH_BV_26_I N/A TC_SERVER_ROB_BV_01_I N/A TC_SERVER_ROB_BV_02_I N/A TC_SERVER_SRM_BI_03_I N/A TC_SERVER_SRM_BI_05_I N/A TC_SERVER_SRM_BV_04_I N/A TC_SERVER_SRM_BV_08_I N/A TC_SERVER_SRMP_BV_02_I N/A TC_SERVER_SRMP_BV_03_I N/A TC_CLIENT_OPH_BV_27_I N/A TC_CLIENT_OPH_BV_34_I PASS TC_SERVER_OPH_BV_27_I N/A TC_SERVER_OPH_BV_30_I N/A TC_SERVER_OPH_BV_31_I N/A TC_SERVER_OPH_BV_32_I N/A TC_SERVER_OPH_BV_33_I N/A TC_SERVER_OPH_BV_34_I PASS ------------------------------------------------------------------------------- bluez-5.82/android/PaxHeaders/pics-mcap.txt0000644000000000000000000000005012537515745015721 xustar0020 atime=1743516876 20 ctime=1743591289 bluez-5.82/android/pics-mcap.txt0000644000000000000000000001535612537515745015414 0ustar00rootrootMCAP PICS for the PTS tool. PTS version: 6.1 * - different than PTS defaults # - not yet implemented/supported M - mandatory O - optional Protocols ------------------------------------------------------------------------------- Parameter Name Selected Description ------------------------------------------------------------------------------- TSPC_MCAP_1_A_1 True (*) Supports Standard Op Codes (C.1) TSPC_MCAP_1_A_2 True (*) Supports Clock Synchronization Protocol (C.1) ------------------------------------------------------------------------------- C.1: Support for at least one of the defined protocols is Mandatory. ------------------------------------------------------------------------------- Roles ------------------------------------------------------------------------------- Parameter Name Selected Description ------------------------------------------------------------------------------- TSPC_MCAP_1_1 True (*) Supports Source Role (C.1) TSPC_MCAP_1_2 True (*) Supports Sink Role (C.1) TSPC_MCAP_1_3 False Supports Sync-Slave Role (C.2) TSPC_MCAP_1_4 False Supports Sync-Master Role (C.3) ------------------------------------------------------------------------------- C.1: If support for TSPC_MCAP_1_A_1 is supported, at least one of the defined roles is Mandatory otherwise Excluded. C.2: Mandatory if TSPC_MCAP_1_A_2 is supported, otherwise Excluded. C.3: Optional if TSPC_MCAP_1_A_2 is supported, otherwise Excluded. ------------------------------------------------------------------------------- L2CAP Features - Source ------------------------------------------------------------------------------- Parameter Name Selected Description ------------------------------------------------------------------------------- TSPC_MCAP_2_1 True (*) Supports L2CAP Control Channel (M) TSPC_MCAP_2_2 True (*) Supports at least one L2CAP Data Channel (M) TSPC_MCAP_2_3 True (*) Maximum number of simultaneous L2CAP Data Channels supported (DCmax) per MCL (M) TSPC_MCAP_2_4 False Can support multiple simultaneous MCLs with Standard Op Codes (O) ------------------------------------------------------------------------------- Connection Management - Source ------------------------------------------------------------------------------- Parameter Name Selected Description ------------------------------------------------------------------------------- TSPC_MCAP_3_1 This row intentionally left blank TSPC_MCAP_3_2 True (*) Initiate creation of Control and Data Channels (C.1) TSPC_MCAP_3_3 True (*) Accept creation of Control and Data Channels (C.1) TSPC_MCAP_3_4 True (*) Initiate Disconnection of MCL (M) TSPC_MCAP_3_5 True (*) Accept Disconnection of MCL (M) TSPC_MCAP_3_6 True (*) Initiate Disconnection of MDL (M) TSPC_MCAP_3_7 True (*) Accept Disconnection of MDL (M) TSPC_MCAP_3_8 False Initiate Reconnection of MDL (O) TSPC_MCAP_3_9 False Accept Reconnection of MDL (C.2) TSPC_MCAP_3_10 False Initiate Deletion of MDL (O) TSPC_MCAP_3_11 True (*) Accept Deletion of MDL (M) TSPC_MCAP_3_12 False Initiate Delete of All MDLs using 0xFFFF (O) TSPC_MCAP_3_13 True (*) Accept Delete of All MDLs using 0xFFFF (M) TSPC_MCAP_3_14 False Send MDL Abort request (O) TSPC_MCAP_3_15 True (*) Accept MDL Abort request (M) ------------------------------------------------------------------------------- C.1: Support for at least one of TSPC_MCAP_3_2 or TSPC_MCAP_3_3 is Mandatory. C.2: Mandatory if TSPC_MCAP_3_3 is supported, otherwise Excluded. ------------------------------------------------------------------------------- L2CAP Features - Sink ------------------------------------------------------------------------------- Parameter Name Selected Description ------------------------------------------------------------------------------- TSPC_MCAP_4_1 True (*) Supports L2CAP Control Channel (M) TSPC_MCAP_4_2 True (*) Supports at least one L2CAP Data Channel (M) TSPC_MCAP_4_3 True (*) Maximum number of simultaneous L2CAP Data Channels supported (DCmax) per MCL (M) TSPC_MCAP_4_4 False Can support multiple simultaneous MCLs with Standard Op Codes (O) ------------------------------------------------------------------------------- Connection Management - Sink ------------------------------------------------------------------------------- Parameter Name Selected Description ------------------------------------------------------------------------------- TSPC_MCAP_5_1 This row intentionally left blank TSPC_MCAP_5_2 True (*) Initiate creation of Control and Data Channels (M) TSPC_MCAP_5_3 True (*) Accept creation of Control and Data Channels (M) TSPC_MCAP_5_4 True (*) Initiate Disconnection of MCL (M) TSPC_MCAP_5_5 True (*) Accept Disconnection of MCL (M) TSPC_MCAP_5_6 True (*) Initiate Disconnection of MDL (M) TSPC_MCAP_5_7 True (*) Accept Disconnection of MDL (M) TSPC_MCAP_5_8 False Initiate Reconnection of MDL (O) TSPC_MCAP_5_9 True (*) Accept Reconnection of MDL (M) TSPC_MCAP_5_10 False Initiate Deletion of MDL (O) TSPC_MCAP_5_11 True (*) Accept Deletion of MDL (M) TSPC_MCAP_5_12 False Initiate Delete of All MDLs using 0xFFFF (O) TSPC_MCAP_5_13 True (*) Accept Delete of All MDLs using 0xFFFF (M) TSPC_MCAP_5_14 False Send MDL Abort request (O) TSPC_MCAP_5_15 True (*) Accept MDL Abort request (M) ------------------------------------------------------------------------------- Clock Synchronization Features - Sync-Slave ------------------------------------------------------------------------------- Parameter Name Selected Description ------------------------------------------------------------------------------- TSPC_MCAP_6_1 False (*) Accept MD_SYNC_CAP_REQ and MD_SYNC_SET_REQ and Initiate MD_SYNC_INFO_IND (C.1) TSPC_MCAP_6_2 This row intentionally left blank TSPC_MCAP_6_3 False Can support multiple simultaneous MCLs with CSP (O) TSPC_MCAP_6_4 False Can access Bluetooth Clock (O) ------------------------------------------------------------------------------- C.1: Mandatory if MCAP TSPC_MCAP_1_A_2 is supported, otherwise Excluded. ------------------------------------------------------------------------------- Clock Synchronization Features - Sync-Master ------------------------------------------------------------------------------- Parameter Name Selected Description ------------------------------------------------------------------------------- TSPC_MCAP_7_1 This row intentionally left blank TSPC_MCAP_7_2 False (*) Initiate MD_SYNC_CAP_REQ and MD_SYNC_SET_REQ and Accept MD_SYNC_INFO_IND (C.1) TSPC_MCAP_7_3 False Can support multiple simultaneous MCLs with CSP (O) TSPC_MCAP_7_4 False (*) Can access Bluetooth Clock (O) ------------------------------------------------------------------------------- C.1: Mandatory to support IF TSPC_MCAP_1_A_2 is supported. ------------------------------------------------------------------------------- bluez-5.82/android/PaxHeaders/hardware0000644000000000000000000000005014773213556015023 xustar0020 atime=1743591291 20 ctime=1743591278 bluez-5.82/android/hardware/0000755000000000000000000000000014773213556014561 5ustar00rootrootbluez-5.82/android/hardware/PaxHeaders/bt_hf.h0000644000000000000000000000005014015011623016307 xustar0020 atime=1743516862 20 ctime=1743591276 bluez-5.82/android/hardware/bt_hf.h0000644000000000000000000002225514015011623015776 0ustar00rootroot/* SPDX-License-Identifier: Apache-2.0 */ /* * Copyright (C) 2012 The Android Open Source Project * */ #ifndef ANDROID_INCLUDE_BT_HF_H #define ANDROID_INCLUDE_BT_HF_H __BEGIN_DECLS /* AT response code - OK/Error */ typedef enum { BTHF_AT_RESPONSE_ERROR = 0, BTHF_AT_RESPONSE_OK } bthf_at_response_t; typedef enum { BTHF_CONNECTION_STATE_DISCONNECTED = 0, BTHF_CONNECTION_STATE_CONNECTING, BTHF_CONNECTION_STATE_CONNECTED, BTHF_CONNECTION_STATE_SLC_CONNECTED, BTHF_CONNECTION_STATE_DISCONNECTING } bthf_connection_state_t; typedef enum { BTHF_AUDIO_STATE_DISCONNECTED = 0, BTHF_AUDIO_STATE_CONNECTING, BTHF_AUDIO_STATE_CONNECTED, BTHF_AUDIO_STATE_DISCONNECTING } bthf_audio_state_t; typedef enum { BTHF_VR_STATE_STOPPED = 0, BTHF_VR_STATE_STARTED } bthf_vr_state_t; typedef enum { BTHF_VOLUME_TYPE_SPK = 0, BTHF_VOLUME_TYPE_MIC } bthf_volume_type_t; /* Noise Reduction and Echo Cancellation */ typedef enum { BTHF_NREC_STOP, BTHF_NREC_START } bthf_nrec_t; /* WBS codec setting */ typedef enum { BTHF_WBS_NONE, BTHF_WBS_NO, BTHF_WBS_YES }bthf_wbs_config_t; /* CHLD - Call held handling */ typedef enum { BTHF_CHLD_TYPE_RELEASEHELD, // Terminate all held or set UDUB("busy") to a waiting call BTHF_CHLD_TYPE_RELEASEACTIVE_ACCEPTHELD, // Terminate all active calls and accepts a waiting/held call BTHF_CHLD_TYPE_HOLDACTIVE_ACCEPTHELD, // Hold all active calls and accepts a waiting/held call BTHF_CHLD_TYPE_ADDHELDTOCONF, // Add all held calls to a conference } bthf_chld_type_t; /** Callback for connection state change. * state will have one of the values from BtHfConnectionState */ typedef void (* bthf_connection_state_callback)(bthf_connection_state_t state, bt_bdaddr_t *bd_addr); /** Callback for audio connection state change. * state will have one of the values from BtHfAudioState */ typedef void (* bthf_audio_state_callback)(bthf_audio_state_t state, bt_bdaddr_t *bd_addr); /** Callback for VR connection state change. * state will have one of the values from BtHfVRState */ typedef void (* bthf_vr_cmd_callback)(bthf_vr_state_t state, bt_bdaddr_t *bd_addr); /** Callback for answer incoming call (ATA) */ typedef void (* bthf_answer_call_cmd_callback)(bt_bdaddr_t *bd_addr); /** Callback for disconnect call (AT+CHUP) */ typedef void (* bthf_hangup_call_cmd_callback)(bt_bdaddr_t *bd_addr); /** Callback for disconnect call (AT+CHUP) * type will denote Speaker/Mic gain (BtHfVolumeControl). */ typedef void (* bthf_volume_cmd_callback)(bthf_volume_type_t type, int volume, bt_bdaddr_t *bd_addr); /** Callback for dialing an outgoing call * If number is NULL, redial */ typedef void (* bthf_dial_call_cmd_callback)(char *number, bt_bdaddr_t *bd_addr); /** Callback for sending DTMF tones * tone contains the dtmf character to be sent */ typedef void (* bthf_dtmf_cmd_callback)(char tone, bt_bdaddr_t *bd_addr); /** Callback for enabling/disabling noise reduction/echo cancellation * value will be 1 to enable, 0 to disable */ typedef void (* bthf_nrec_cmd_callback)(bthf_nrec_t nrec, bt_bdaddr_t *bd_addr); /** Callback for AT+BCS and event from BAC * WBS enable, WBS disable */ typedef void (* bthf_wbs_callback)(bthf_wbs_config_t wbs, bt_bdaddr_t *bd_addr); /** Callback for call hold handling (AT+CHLD) * value will contain the call hold command (0, 1, 2, 3) */ typedef void (* bthf_chld_cmd_callback)(bthf_chld_type_t chld, bt_bdaddr_t *bd_addr); /** Callback for CNUM (subscriber number) */ typedef void (* bthf_cnum_cmd_callback)(bt_bdaddr_t *bd_addr); /** Callback for indicators (CIND) */ typedef void (* bthf_cind_cmd_callback)(bt_bdaddr_t *bd_addr); /** Callback for operator selection (COPS) */ typedef void (* bthf_cops_cmd_callback)(bt_bdaddr_t *bd_addr); /** Callback for call list (AT+CLCC) */ typedef void (* bthf_clcc_cmd_callback) (bt_bdaddr_t *bd_addr); /** Callback for unknown AT command recd from HF * at_string will contain the unparsed AT string */ typedef void (* bthf_unknown_at_cmd_callback)(char *at_string, bt_bdaddr_t *bd_addr); /** Callback for keypressed (HSP) event. */ typedef void (* bthf_key_pressed_cmd_callback)(bt_bdaddr_t *bd_addr); /** BT-HF callback structure. */ typedef struct { /** set to sizeof(BtHfCallbacks) */ size_t size; bthf_connection_state_callback connection_state_cb; bthf_audio_state_callback audio_state_cb; bthf_vr_cmd_callback vr_cmd_cb; bthf_answer_call_cmd_callback answer_call_cmd_cb; bthf_hangup_call_cmd_callback hangup_call_cmd_cb; bthf_volume_cmd_callback volume_cmd_cb; bthf_dial_call_cmd_callback dial_call_cmd_cb; bthf_dtmf_cmd_callback dtmf_cmd_cb; bthf_nrec_cmd_callback nrec_cmd_cb; bthf_wbs_callback wbs_cb; bthf_chld_cmd_callback chld_cmd_cb; bthf_cnum_cmd_callback cnum_cmd_cb; bthf_cind_cmd_callback cind_cmd_cb; bthf_cops_cmd_callback cops_cmd_cb; bthf_clcc_cmd_callback clcc_cmd_cb; bthf_unknown_at_cmd_callback unknown_at_cmd_cb; bthf_key_pressed_cmd_callback key_pressed_cmd_cb; } bthf_callbacks_t; /** Network Status */ typedef enum { BTHF_NETWORK_STATE_NOT_AVAILABLE = 0, BTHF_NETWORK_STATE_AVAILABLE } bthf_network_state_t; /** Service type */ typedef enum { BTHF_SERVICE_TYPE_HOME = 0, BTHF_SERVICE_TYPE_ROAMING } bthf_service_type_t; typedef enum { BTHF_CALL_STATE_ACTIVE = 0, BTHF_CALL_STATE_HELD, BTHF_CALL_STATE_DIALING, BTHF_CALL_STATE_ALERTING, BTHF_CALL_STATE_INCOMING, BTHF_CALL_STATE_WAITING, BTHF_CALL_STATE_IDLE } bthf_call_state_t; typedef enum { BTHF_CALL_DIRECTION_OUTGOING = 0, BTHF_CALL_DIRECTION_INCOMING } bthf_call_direction_t; typedef enum { BTHF_CALL_TYPE_VOICE = 0, BTHF_CALL_TYPE_DATA, BTHF_CALL_TYPE_FAX } bthf_call_mode_t; typedef enum { BTHF_CALL_MPTY_TYPE_SINGLE = 0, BTHF_CALL_MPTY_TYPE_MULTI } bthf_call_mpty_type_t; typedef enum { BTHF_CALL_ADDRTYPE_UNKNOWN = 0x81, BTHF_CALL_ADDRTYPE_INTERNATIONAL = 0x91 } bthf_call_addrtype_t; /** Represents the standard BT-HF interface. */ typedef struct { /** set to sizeof(BtHfInterface) */ size_t size; /** * Register the BtHf callbacks */ bt_status_t (*init)( bthf_callbacks_t* callbacks, int max_hf_clients); /** connect to headset */ bt_status_t (*connect)( bt_bdaddr_t *bd_addr ); /** dis-connect from headset */ bt_status_t (*disconnect)( bt_bdaddr_t *bd_addr ); /** create an audio connection */ bt_status_t (*connect_audio)( bt_bdaddr_t *bd_addr ); /** close the audio connection */ bt_status_t (*disconnect_audio)( bt_bdaddr_t *bd_addr ); /** start voice recognition */ bt_status_t (*start_voice_recognition)( bt_bdaddr_t *bd_addr ); /** stop voice recognition */ bt_status_t (*stop_voice_recognition)( bt_bdaddr_t *bd_addr ); /** volume control */ bt_status_t (*volume_control) (bthf_volume_type_t type, int volume, bt_bdaddr_t *bd_addr ); /** Combined device status change notification */ bt_status_t (*device_status_notification)(bthf_network_state_t ntk_state, bthf_service_type_t svc_type, int signal, int batt_chg); /** Response for COPS command */ bt_status_t (*cops_response)(const char *cops, bt_bdaddr_t *bd_addr ); /** Response for CIND command */ bt_status_t (*cind_response)(int svc, int num_active, int num_held, bthf_call_state_t call_setup_state, int signal, int roam, int batt_chg, bt_bdaddr_t *bd_addr ); /** Pre-formatted AT response, typically in response to unknown AT cmd */ bt_status_t (*formatted_at_response)(const char *rsp, bt_bdaddr_t *bd_addr ); /** ok/error response * ERROR (0) * OK (1) */ bt_status_t (*at_response) (bthf_at_response_t response_code, int error_code, bt_bdaddr_t *bd_addr ); /** response for CLCC command * Can be iteratively called for each call index * Call index of 0 will be treated as NULL termination (Completes response) */ bt_status_t (*clcc_response) (int index, bthf_call_direction_t dir, bthf_call_state_t state, bthf_call_mode_t mode, bthf_call_mpty_type_t mpty, const char *number, bthf_call_addrtype_t type, bt_bdaddr_t *bd_addr ); /** notify of a call state change * Each update notifies * 1. Number of active/held/ringing calls * 2. call_state: This denotes the state change that triggered this msg * This will take one of the values from BtHfCallState * 3. number & type: valid only for incoming & waiting call */ bt_status_t (*phone_state_change) (int num_active, int num_held, bthf_call_state_t call_setup_state, const char *number, bthf_call_addrtype_t type); /** Closes the interface. */ void (*cleanup)( void ); /** configureation for the SCO codec */ bt_status_t (*configure_wbs)( bt_bdaddr_t *bd_addr ,bthf_wbs_config_t config ); } bthf_interface_t; __END_DECLS #endif /* ANDROID_INCLUDE_BT_HF_H */ bluez-5.82/android/hardware/PaxHeaders/bt_hh.h0000644000000000000000000000005014015011623016311 xustar0020 atime=1743516862 20 ctime=1743591276 bluez-5.82/android/hardware/bt_hh.h0000644000000000000000000001305414015011623015775 0ustar00rootroot/* SPDX-License-Identifier: Apache-2.0 */ /* * Copyright (C) 2012 The Android Open Source Project * */ #ifndef ANDROID_INCLUDE_BT_HH_H #define ANDROID_INCLUDE_BT_HH_H #include __BEGIN_DECLS #define BTHH_MAX_DSC_LEN 884 /* HH connection states */ typedef enum { BTHH_CONN_STATE_CONNECTED = 0, BTHH_CONN_STATE_CONNECTING, BTHH_CONN_STATE_DISCONNECTED, BTHH_CONN_STATE_DISCONNECTING, BTHH_CONN_STATE_FAILED_MOUSE_FROM_HOST, BTHH_CONN_STATE_FAILED_KBD_FROM_HOST, BTHH_CONN_STATE_FAILED_TOO_MANY_DEVICES, BTHH_CONN_STATE_FAILED_NO_BTHID_DRIVER, BTHH_CONN_STATE_FAILED_GENERIC, BTHH_CONN_STATE_UNKNOWN } bthh_connection_state_t; typedef enum { BTHH_OK = 0, BTHH_HS_HID_NOT_READY, /* handshake error : device not ready */ BTHH_HS_INVALID_RPT_ID, /* handshake error : invalid report ID */ BTHH_HS_TRANS_NOT_SPT, /* handshake error : transaction not spt */ BTHH_HS_INVALID_PARAM, /* handshake error : invalid paremter */ BTHH_HS_ERROR, /* handshake error : unspecified HS error */ BTHH_ERR, /* general BTA HH error */ BTHH_ERR_SDP, /* SDP error */ BTHH_ERR_PROTO, /* SET_Protocol error, only used in BTA_HH_OPEN_EVT callback */ BTHH_ERR_DB_FULL, /* device database full error, used */ BTHH_ERR_TOD_UNSPT, /* type of device not supported */ BTHH_ERR_NO_RES, /* out of system resources */ BTHH_ERR_AUTH_FAILED, /* authentication fail */ BTHH_ERR_HDL }bthh_status_t; /* Protocol modes */ typedef enum { BTHH_REPORT_MODE = 0x00, BTHH_BOOT_MODE = 0x01, BTHH_UNSUPPORTED_MODE = 0xff }bthh_protocol_mode_t; /* Report types */ typedef enum { BTHH_INPUT_REPORT = 1, BTHH_OUTPUT_REPORT, BTHH_FEATURE_REPORT }bthh_report_type_t; typedef struct { int attr_mask; uint8_t sub_class; uint8_t app_id; int vendor_id; int product_id; int version; uint8_t ctry_code; int dl_len; uint8_t dsc_list[BTHH_MAX_DSC_LEN]; } bthh_hid_info_t; /** Callback for connection state change. * state will have one of the values from bthh_connection_state_t */ typedef void (* bthh_connection_state_callback)(bt_bdaddr_t *bd_addr, bthh_connection_state_t state); /** Callback for vitual unplug api. * the status of the vitual unplug */ typedef void (* bthh_virtual_unplug_callback)(bt_bdaddr_t *bd_addr, bthh_status_t hh_status); /** Callback for get hid info * hid_info will contain attr_mask, sub_class, app_id, vendor_id, product_id, version, ctry_code, len */ typedef void (* bthh_hid_info_callback)(bt_bdaddr_t *bd_addr, bthh_hid_info_t hid_info); /** Callback for get protocol api. * the protocol mode is one of the value from bthh_protocol_mode_t */ typedef void (* bthh_protocol_mode_callback)(bt_bdaddr_t *bd_addr, bthh_status_t hh_status, bthh_protocol_mode_t mode); /** Callback for get/set_idle_time api. */ typedef void (* bthh_idle_time_callback)(bt_bdaddr_t *bd_addr, bthh_status_t hh_status, int idle_rate); /** Callback for get report api. * if staus is ok rpt_data contains the report data */ typedef void (* bthh_get_report_callback)(bt_bdaddr_t *bd_addr, bthh_status_t hh_status, uint8_t* rpt_data, int rpt_size); /** Callback for set_report/set_protocol api and if error * occurs for get_report/get_protocol api. */ typedef void (* bthh_handshake_callback)(bt_bdaddr_t *bd_addr, bthh_status_t hh_status); /** BT-HH callback structure. */ typedef struct { /** set to sizeof(BtHfCallbacks) */ size_t size; bthh_connection_state_callback connection_state_cb; bthh_hid_info_callback hid_info_cb; bthh_protocol_mode_callback protocol_mode_cb; bthh_idle_time_callback idle_time_cb; bthh_get_report_callback get_report_cb; bthh_virtual_unplug_callback virtual_unplug_cb; bthh_handshake_callback handshake_cb; } bthh_callbacks_t; /** Represents the standard BT-HH interface. */ typedef struct { /** set to sizeof(BtHhInterface) */ size_t size; /** * Register the BtHh callbacks */ bt_status_t (*init)( bthh_callbacks_t* callbacks ); /** connect to hid device */ bt_status_t (*connect)( bt_bdaddr_t *bd_addr); /** dis-connect from hid device */ bt_status_t (*disconnect)( bt_bdaddr_t *bd_addr ); /** Virtual UnPlug (VUP) the specified HID device */ bt_status_t (*virtual_unplug)(bt_bdaddr_t *bd_addr); /** Set the HID device descriptor for the specified HID device. */ bt_status_t (*set_info)(bt_bdaddr_t *bd_addr, bthh_hid_info_t hid_info ); /** Get the HID proto mode. */ bt_status_t (*get_protocol) (bt_bdaddr_t *bd_addr, bthh_protocol_mode_t protocolMode); /** Set the HID proto mode. */ bt_status_t (*set_protocol)(bt_bdaddr_t *bd_addr, bthh_protocol_mode_t protocolMode); /** Send a GET_REPORT to HID device. */ bt_status_t (*get_report)(bt_bdaddr_t *bd_addr, bthh_report_type_t reportType, uint8_t reportId, int bufferSize); /** Send a SET_REPORT to HID device. */ bt_status_t (*set_report)(bt_bdaddr_t *bd_addr, bthh_report_type_t reportType, char* report); /** Send data to HID device. */ bt_status_t (*send_data)(bt_bdaddr_t *bd_addr, char* data); /** Closes the interface. */ void (*cleanup)( void ); } bthh_interface_t; __END_DECLS #endif /* ANDROID_INCLUDE_BT_HH_H */ bluez-5.82/android/hardware/PaxHeaders/bt_av.h0000644000000000000000000000005014015011623016320 xustar0020 atime=1743516862 20 ctime=1743591276 bluez-5.82/android/hardware/bt_av.h0000644000000000000000000000531014015011623016000 0ustar00rootroot/* SPDX-License-Identifier: Apache-2.0 */ /* * Copyright (C) 2012 The Android Open Source Project * */ #ifndef ANDROID_INCLUDE_BT_AV_H #define ANDROID_INCLUDE_BT_AV_H __BEGIN_DECLS /* Bluetooth AV connection states */ typedef enum { BTAV_CONNECTION_STATE_DISCONNECTED = 0, BTAV_CONNECTION_STATE_CONNECTING, BTAV_CONNECTION_STATE_CONNECTED, BTAV_CONNECTION_STATE_DISCONNECTING } btav_connection_state_t; /* Bluetooth AV datapath states */ typedef enum { BTAV_AUDIO_STATE_REMOTE_SUSPEND = 0, BTAV_AUDIO_STATE_STOPPED, BTAV_AUDIO_STATE_STARTED, } btav_audio_state_t; /** Callback for connection state change. * state will have one of the values from btav_connection_state_t */ typedef void (* btav_connection_state_callback)(btav_connection_state_t state, bt_bdaddr_t *bd_addr); /** Callback for audiopath state change. * state will have one of the values from btav_audio_state_t */ typedef void (* btav_audio_state_callback)(btav_audio_state_t state, bt_bdaddr_t *bd_addr); /** Callback for audio configuration change. * Used only for the A2DP sink interface. * state will have one of the values from btav_audio_state_t * sample_rate: sample rate in Hz * channel_count: number of channels (1 for mono, 2 for stereo) */ typedef void (* btav_audio_config_callback)(bt_bdaddr_t *bd_addr, uint32_t sample_rate, uint8_t channel_count); /** BT-AV callback structure. */ typedef struct { /** set to sizeof(btav_callbacks_t) */ size_t size; btav_connection_state_callback connection_state_cb; btav_audio_state_callback audio_state_cb; btav_audio_config_callback audio_config_cb; } btav_callbacks_t; /** * NOTE: * * 1. AVRCP 1.0 shall be supported initially. AVRCP passthrough commands * shall be handled internally via uinput * * 2. A2DP data path shall be handled via a socket pipe between the AudioFlinger * android_audio_hw library and the Bluetooth stack. * */ /** Represents the standard BT-AV interface. * Used for both the A2DP source and sink interfaces. */ typedef struct { /** set to sizeof(btav_interface_t) */ size_t size; /** * Register the BtAv callbacks */ bt_status_t (*init)( btav_callbacks_t* callbacks ); /** connect to headset */ bt_status_t (*connect)( bt_bdaddr_t *bd_addr ); /** dis-connect from headset */ bt_status_t (*disconnect)( bt_bdaddr_t *bd_addr ); /** Closes the interface. */ void (*cleanup)( void ); } btav_interface_t; __END_DECLS #endif /* ANDROID_INCLUDE_BT_AV_H */ bluez-5.82/android/hardware/PaxHeaders/bt_mce.h0000644000000000000000000000005014015011623016456 xustar0020 atime=1743516862 20 ctime=1743591276 bluez-5.82/android/hardware/bt_mce.h0000644000000000000000000000211714015011623016140 0ustar00rootroot/* SPDX-License-Identifier: Apache-2.0 */ /* * Copyright (C) 2014 The Android Open Source Project * */ #ifndef ANDROID_INCLUDE_BT_MCE_H #define ANDROID_INCLUDE_BT_MCE_H __BEGIN_DECLS /** MAS instance description */ typedef struct { int id; int scn; int msg_types; char *p_name; } btmce_mas_instance_t; /** callback for get_remote_mas_instances */ typedef void (*btmce_remote_mas_instances_callback)(bt_status_t status, bt_bdaddr_t *bd_addr, int num_instances, btmce_mas_instance_t *instances); typedef struct { /** set to sizeof(btmce_callbacks_t) */ size_t size; btmce_remote_mas_instances_callback remote_mas_instances_cb; } btmce_callbacks_t; typedef struct { /** set to size of this struct */ size_t size; /** register BT MCE callbacks */ bt_status_t (*init)(btmce_callbacks_t *callbacks); /** search for MAS instances on remote device */ bt_status_t (*get_remote_mas_instances)(bt_bdaddr_t *bd_addr); } btmce_interface_t; __END_DECLS #endif /* ANDROID_INCLUDE_BT_MCE_H */ bluez-5.82/android/hardware/PaxHeaders/bt_sock.h0000644000000000000000000000005014015011623016651 xustar0020 atime=1743516862 20 ctime=1743591276 bluez-5.82/android/hardware/bt_sock.h0000644000000000000000000000251514015011623016335 0ustar00rootroot/* SPDX-License-Identifier: Apache-2.0 */ /* * Copyright (C) 2012 The Android Open Source Project * */ #ifndef ANDROID_INCLUDE_BT_SOCK_H #define ANDROID_INCLUDE_BT_SOCK_H __BEGIN_DECLS #define BTSOCK_FLAG_ENCRYPT 1 #define BTSOCK_FLAG_AUTH (1 << 1) typedef enum { BTSOCK_RFCOMM = 1, BTSOCK_SCO = 2, BTSOCK_L2CAP = 3 } btsock_type_t; /** Represents the standard BT SOCKET interface. */ typedef struct { short size; bt_bdaddr_t bd_addr; int channel; int status; } __attribute__((packed)) sock_connect_signal_t; typedef struct { /** set to size of this struct*/ size_t size; /** * listen to a rfcomm uuid or channel. It returns the socket fd from which * btsock_connect_signal can be read out when a remote device connected */ bt_status_t (*listen)(btsock_type_t type, const char* service_name, const uint8_t* service_uuid, int channel, int* sock_fd, int flags); /* * connect to a rfcomm uuid channel of remote device, It returns the socket fd from which * the btsock_connect_signal and a new socket fd to be accepted can be read out when connected */ bt_status_t (*connect)(const bt_bdaddr_t *bd_addr, btsock_type_t type, const uint8_t* uuid, int channel, int* sock_fd, int flags); } btsock_interface_t; __END_DECLS #endif /* ANDROID_INCLUDE_BT_SOCK_H */ bluez-5.82/android/hardware/PaxHeaders/bt_hl.h0000644000000000000000000000005014015011623016315 xustar0020 atime=1743516862 20 ctime=1743591276 bluez-5.82/android/hardware/bt_hl.h0000644000000000000000000000616714015011623016010 0ustar00rootroot/* SPDX-License-Identifier: Apache-2.0 */ /* * Copyright (C) 2012 The Android Open Source Project * */ #ifndef ANDROID_INCLUDE_BT_HL_H #define ANDROID_INCLUDE_BT_HL_H __BEGIN_DECLS /* HL connection states */ typedef enum { BTHL_MDEP_ROLE_SOURCE, BTHL_MDEP_ROLE_SINK } bthl_mdep_role_t; typedef enum { BTHL_APP_REG_STATE_REG_SUCCESS, BTHL_APP_REG_STATE_REG_FAILED, BTHL_APP_REG_STATE_DEREG_SUCCESS, BTHL_APP_REG_STATE_DEREG_FAILED } bthl_app_reg_state_t; typedef enum { BTHL_CHANNEL_TYPE_RELIABLE, BTHL_CHANNEL_TYPE_STREAMING, BTHL_CHANNEL_TYPE_ANY } bthl_channel_type_t; /* HL connection states */ typedef enum { BTHL_CONN_STATE_CONNECTING, BTHL_CONN_STATE_CONNECTED, BTHL_CONN_STATE_DISCONNECTING, BTHL_CONN_STATE_DISCONNECTED, BTHL_CONN_STATE_DESTROYED } bthl_channel_state_t; typedef struct { bthl_mdep_role_t mdep_role; int data_type; bthl_channel_type_t channel_type; const char *mdep_description; /* MDEP description to be used in the SDP (optional); null terminated */ } bthl_mdep_cfg_t; typedef struct { const char *application_name; const char *provider_name; /* provider name to be used in the SDP (optional); null terminated */ const char *srv_name; /* service name to be used in the SDP (optional); null terminated*/ const char *srv_desp; /* service description to be used in the SDP (optional); null terminated */ int number_of_mdeps; bthl_mdep_cfg_t *mdep_cfg; /* Dynamic array */ } bthl_reg_param_t; /** Callback for application registration status. * state will have one of the values from bthl_app_reg_state_t */ typedef void (* bthl_app_reg_state_callback)(int app_id, bthl_app_reg_state_t state); /** Callback for channel connection state change. * state will have one of the values from * bthl_connection_state_t and fd (file descriptor) */ typedef void (* bthl_channel_state_callback)(int app_id, bt_bdaddr_t *bd_addr, int mdep_cfg_index, int channel_id, bthl_channel_state_t state, int fd); /** BT-HL callback structure. */ typedef struct { /** set to sizeof(bthl_callbacks_t) */ size_t size; bthl_app_reg_state_callback app_reg_state_cb; bthl_channel_state_callback channel_state_cb; } bthl_callbacks_t; /** Represents the standard BT-HL interface. */ typedef struct { /** set to sizeof(bthl_interface_t) */ size_t size; /** * Register the Bthl callbacks */ bt_status_t (*init)( bthl_callbacks_t* callbacks ); /** Register HL application */ bt_status_t (*register_application) ( bthl_reg_param_t *p_reg_param, int *app_id); /** Unregister HL application */ bt_status_t (*unregister_application) (int app_id); /** connect channel */ bt_status_t (*connect_channel)(int app_id, bt_bdaddr_t *bd_addr, int mdep_cfg_index, int *channel_id); /** destroy channel */ bt_status_t (*destroy_channel)(int channel_id); /** Close the Bthl callback **/ void (*cleanup)(void); } bthl_interface_t; __END_DECLS #endif /* ANDROID_INCLUDE_BT_HL_H */ bluez-5.82/android/hardware/PaxHeaders/bt_gatt_client.h0000644000000000000000000000005014015011623020207 xustar0020 atime=1743516862 20 ctime=1743591276 bluez-5.82/android/hardware/bt_gatt_client.h0000644000000000000000000004165214015011623017700 0ustar00rootroot/* SPDX-License-Identifier: Apache-2.0 */ /* * Copyright (C) 2013 The Android Open Source Project * */ #ifndef ANDROID_INCLUDE_BT_GATT_CLIENT_H #define ANDROID_INCLUDE_BT_GATT_CLIENT_H #include #include "bt_gatt_types.h" __BEGIN_DECLS /** * Buffer sizes for maximum attribute length and maximum read/write * operation buffer size. */ #define BTGATT_MAX_ATTR_LEN 600 /** Buffer type for unformatted reads/writes */ typedef struct { uint8_t value[BTGATT_MAX_ATTR_LEN]; uint16_t len; } btgatt_unformatted_value_t; /** Parameters for GATT read operations */ typedef struct { btgatt_srvc_id_t srvc_id; btgatt_gatt_id_t char_id; btgatt_gatt_id_t descr_id; btgatt_unformatted_value_t value; uint16_t value_type; uint8_t status; } btgatt_read_params_t; /** Parameters for GATT write operations */ typedef struct { btgatt_srvc_id_t srvc_id; btgatt_gatt_id_t char_id; btgatt_gatt_id_t descr_id; uint8_t status; } btgatt_write_params_t; /** Attribute change notification parameters */ typedef struct { uint8_t value[BTGATT_MAX_ATTR_LEN]; bt_bdaddr_t bda; btgatt_srvc_id_t srvc_id; btgatt_gatt_id_t char_id; uint16_t len; uint8_t is_notify; } btgatt_notify_params_t; typedef struct { bt_bdaddr_t *bda1; bt_uuid_t *uuid1; uint16_t u1; uint16_t u2; uint16_t u3; uint16_t u4; uint16_t u5; } btgatt_test_params_t; /** BT-GATT Client callback structure. */ /** Callback invoked in response to register_client */ typedef void (*register_client_callback)(int status, int client_if, bt_uuid_t *app_uuid); /** Callback for scan results */ typedef void (*scan_result_callback)(bt_bdaddr_t* bda, int rssi, uint8_t* adv_data); /** GATT open callback invoked in response to open */ typedef void (*connect_callback)(int conn_id, int status, int client_if, bt_bdaddr_t* bda); /** Callback invoked in response to close */ typedef void (*disconnect_callback)(int conn_id, int status, int client_if, bt_bdaddr_t* bda); /** * Invoked in response to search_service when the GATT service search * has been completed. */ typedef void (*search_complete_callback)(int conn_id, int status); /** Reports GATT services on a remote device */ typedef void (*search_result_callback)( int conn_id, btgatt_srvc_id_t *srvc_id); /** GATT characteristic enumeration result callback */ typedef void (*get_characteristic_callback)(int conn_id, int status, btgatt_srvc_id_t *srvc_id, btgatt_gatt_id_t *char_id, int char_prop); /** GATT descriptor enumeration result callback */ typedef void (*get_descriptor_callback)(int conn_id, int status, btgatt_srvc_id_t *srvc_id, btgatt_gatt_id_t *char_id, btgatt_gatt_id_t *descr_id); /** GATT included service enumeration result callback */ typedef void (*get_included_service_callback)(int conn_id, int status, btgatt_srvc_id_t *srvc_id, btgatt_srvc_id_t *incl_srvc_id); /** Callback invoked in response to [de]register_for_notification */ typedef void (*register_for_notification_callback)(int conn_id, int registered, int status, btgatt_srvc_id_t *srvc_id, btgatt_gatt_id_t *char_id); /** * Remote device notification callback, invoked when a remote device sends * a notification or indication that a client has registered for. */ typedef void (*notify_callback)(int conn_id, btgatt_notify_params_t *p_data); /** Reports result of a GATT read operation */ typedef void (*read_characteristic_callback)(int conn_id, int status, btgatt_read_params_t *p_data); /** GATT write characteristic operation callback */ typedef void (*write_characteristic_callback)(int conn_id, int status, btgatt_write_params_t *p_data); /** GATT execute prepared write callback */ typedef void (*execute_write_callback)(int conn_id, int status); /** Callback invoked in response to read_descriptor */ typedef void (*read_descriptor_callback)(int conn_id, int status, btgatt_read_params_t *p_data); /** Callback invoked in response to write_descriptor */ typedef void (*write_descriptor_callback)(int conn_id, int status, btgatt_write_params_t *p_data); /** Callback triggered in response to read_remote_rssi */ typedef void (*read_remote_rssi_callback)(int client_if, bt_bdaddr_t* bda, int rssi, int status); /** * Callback indicating the status of a listen() operation */ typedef void (*listen_callback)(int status, int server_if); /** Callback invoked when the MTU for a given connection changes */ typedef void (*configure_mtu_callback)(int conn_id, int status, int mtu); /** Callback invoked when a scan filter configuration command has completed */ typedef void (*scan_filter_cfg_callback)(int action, int client_if, int status, int filt_type, int avbl_space); /** Callback invoked when scan param has been added, cleared, or deleted */ typedef void (*scan_filter_param_callback)(int action, int client_if, int status, int avbl_space); /** Callback invoked when a scan filter configuration command has completed */ typedef void (*scan_filter_status_callback)(int enable, int client_if, int status); /** Callback invoked when multi-adv enable operation has completed */ typedef void (*multi_adv_enable_callback)(int client_if, int status); /** Callback invoked when multi-adv param update operation has completed */ typedef void (*multi_adv_update_callback)(int client_if, int status); /** Callback invoked when multi-adv instance data set operation has completed */ typedef void (*multi_adv_data_callback)(int client_if, int status); /** Callback invoked when multi-adv disable operation has completed */ typedef void (*multi_adv_disable_callback)(int client_if, int status); /** * Callback notifying an application that a remote device connection is currently congested * and cannot receive any more data. An application should avoid sending more data until * a further callback is received indicating the congestion status has been cleared. */ typedef void (*congestion_callback)(int conn_id, bool congested); /** Callback invoked when batchscan storage config operation has completed */ typedef void (*batchscan_cfg_storage_callback)(int client_if, int status); /** Callback invoked when batchscan enable / disable operation has completed */ typedef void (*batchscan_enable_disable_callback)(int action, int client_if, int status); /** Callback invoked when batchscan reports are obtained */ typedef void (*batchscan_reports_callback)(int client_if, int status, int report_format, int num_records, int data_len, uint8_t* rep_data); /** Callback invoked when batchscan storage threshold limit is crossed */ typedef void (*batchscan_threshold_callback)(int client_if); /** Track ADV VSE callback invoked when tracked device is found or lost */ typedef void (*track_adv_event_callback)(int client_if, int filt_index, int addr_type, bt_bdaddr_t* bda, int adv_state); typedef struct { register_client_callback register_client_cb; scan_result_callback scan_result_cb; connect_callback open_cb; disconnect_callback close_cb; search_complete_callback search_complete_cb; search_result_callback search_result_cb; get_characteristic_callback get_characteristic_cb; get_descriptor_callback get_descriptor_cb; get_included_service_callback get_included_service_cb; register_for_notification_callback register_for_notification_cb; notify_callback notify_cb; read_characteristic_callback read_characteristic_cb; write_characteristic_callback write_characteristic_cb; read_descriptor_callback read_descriptor_cb; write_descriptor_callback write_descriptor_cb; execute_write_callback execute_write_cb; read_remote_rssi_callback read_remote_rssi_cb; listen_callback listen_cb; configure_mtu_callback configure_mtu_cb; scan_filter_cfg_callback scan_filter_cfg_cb; scan_filter_param_callback scan_filter_param_cb; scan_filter_status_callback scan_filter_status_cb; multi_adv_enable_callback multi_adv_enable_cb; multi_adv_update_callback multi_adv_update_cb; multi_adv_data_callback multi_adv_data_cb; multi_adv_disable_callback multi_adv_disable_cb; congestion_callback congestion_cb; batchscan_cfg_storage_callback batchscan_cfg_storage_cb; batchscan_enable_disable_callback batchscan_enb_disable_cb; batchscan_reports_callback batchscan_reports_cb; batchscan_threshold_callback batchscan_threshold_cb; track_adv_event_callback track_adv_event_cb; } btgatt_client_callbacks_t; /** Represents the standard BT-GATT client interface. */ typedef struct { /** Registers a GATT client application with the stack */ bt_status_t (*register_client)( bt_uuid_t *uuid ); /** Unregister a client application from the stack */ bt_status_t (*unregister_client)(int client_if ); /** Start or stop LE device scanning */ bt_status_t (*scan)( bool start ); /** Create a connection to a remote LE or dual-mode device */ bt_status_t (*connect)( int client_if, const bt_bdaddr_t *bd_addr, bool is_direct, int transport ); /** Disconnect a remote device or cancel a pending connection */ bt_status_t (*disconnect)( int client_if, const bt_bdaddr_t *bd_addr, int conn_id); /** Start or stop advertisements to listen for incoming connections */ bt_status_t (*listen)(int client_if, bool start); /** Clear the attribute cache for a given device */ bt_status_t (*refresh)( int client_if, const bt_bdaddr_t *bd_addr ); /** * Enumerate all GATT services on a connected device. * Optionally, the results can be filtered for a given UUID. */ bt_status_t (*search_service)(int conn_id, bt_uuid_t *filter_uuid ); /** * Enumerate included services for a given service. * Set start_incl_srvc_id to NULL to get the first included service. */ bt_status_t (*get_included_service)( int conn_id, btgatt_srvc_id_t *srvc_id, btgatt_srvc_id_t *start_incl_srvc_id); /** * Enumerate characteristics for a given service. * Set start_char_id to NULL to get the first characteristic. */ bt_status_t (*get_characteristic)( int conn_id, btgatt_srvc_id_t *srvc_id, btgatt_gatt_id_t *start_char_id); /** * Enumerate descriptors for a given characteristic. * Set start_descr_id to NULL to get the first descriptor. */ bt_status_t (*get_descriptor)( int conn_id, btgatt_srvc_id_t *srvc_id, btgatt_gatt_id_t *char_id, btgatt_gatt_id_t *start_descr_id); /** Read a characteristic on a remote device */ bt_status_t (*read_characteristic)( int conn_id, btgatt_srvc_id_t *srvc_id, btgatt_gatt_id_t *char_id, int auth_req ); /** Write a remote characteristic */ bt_status_t (*write_characteristic)(int conn_id, btgatt_srvc_id_t *srvc_id, btgatt_gatt_id_t *char_id, int write_type, int len, int auth_req, char* p_value); /** Read the descriptor for a given characteristic */ bt_status_t (*read_descriptor)(int conn_id, btgatt_srvc_id_t *srvc_id, btgatt_gatt_id_t *char_id, btgatt_gatt_id_t *descr_id, int auth_req); /** Write a remote descriptor for a given characteristic */ bt_status_t (*write_descriptor)( int conn_id, btgatt_srvc_id_t *srvc_id, btgatt_gatt_id_t *char_id, btgatt_gatt_id_t *descr_id, int write_type, int len, int auth_req, char* p_value); /** Execute a prepared write operation */ bt_status_t (*execute_write)(int conn_id, int execute); /** * Register to receive notifications or indications for a given * characteristic */ bt_status_t (*register_for_notification)( int client_if, const bt_bdaddr_t *bd_addr, btgatt_srvc_id_t *srvc_id, btgatt_gatt_id_t *char_id); /** Deregister a previous request for notifications/indications */ bt_status_t (*deregister_for_notification)( int client_if, const bt_bdaddr_t *bd_addr, btgatt_srvc_id_t *srvc_id, btgatt_gatt_id_t *char_id); /** Request RSSI for a given remote device */ bt_status_t (*read_remote_rssi)( int client_if, const bt_bdaddr_t *bd_addr); /** Setup scan filter params */ bt_status_t (*scan_filter_param_setup)(int client_if, int action, int filt_index, int feat_seln, int list_logic_type, int filt_logic_type, int rssi_high_thres, int rssi_low_thres, int dely_mode, int found_timeout, int lost_timeout, int found_timeout_cnt); /** Configure a scan filter condition */ bt_status_t (*scan_filter_add_remove)(int client_if, int action, int filt_type, int filt_index, int company_id, int company_id_mask, const bt_uuid_t *p_uuid, const bt_uuid_t *p_uuid_mask, const bt_bdaddr_t *bd_addr, char addr_type, int data_len, char* p_data, int mask_len, char* p_mask); /** Clear all scan filter conditions for specific filter index*/ bt_status_t (*scan_filter_clear)(int client_if, int filt_index); /** Enable / disable scan filter feature*/ bt_status_t (*scan_filter_enable)(int client_if, bool enable); /** Determine the type of the remote device (LE, BR/EDR, Dual-mode) */ int (*get_device_type)( const bt_bdaddr_t *bd_addr ); /** Set the advertising data or scan response data */ bt_status_t (*set_adv_data)(int client_if, bool set_scan_rsp, bool include_name, bool include_txpower, int min_interval, int max_interval, int appearance, uint16_t manufacturer_len, char* manufacturer_data, uint16_t service_data_len, char* service_data, uint16_t service_uuid_len, char* service_uuid); /** Configure the MTU for a given connection */ bt_status_t (*configure_mtu)(int conn_id, int mtu); /** Request a connection parameter update */ bt_status_t (*conn_parameter_update)(const bt_bdaddr_t *bd_addr, int min_interval, int max_interval, int latency, int timeout); /** Sets the LE scan interval and window in units of N*0.625 msec */ bt_status_t (*set_scan_parameters)(int scan_interval, int scan_window); /* Setup the parameters as per spec, user manual specified values and enable multi ADV */ bt_status_t (*multi_adv_enable)(int client_if, int min_interval,int max_interval,int adv_type, int chnl_map, int tx_power, int timeout_s); /* Update the parameters as per spec, user manual specified values and restart multi ADV */ bt_status_t (*multi_adv_update)(int client_if, int min_interval,int max_interval,int adv_type, int chnl_map, int tx_power, int timeout_s); /* Setup the data for the specified instance */ bt_status_t (*multi_adv_set_inst_data)(int client_if, bool set_scan_rsp, bool include_name, bool incl_txpower, int appearance, int manufacturer_len, char* manufacturer_data, int service_data_len, char* service_data, int service_uuid_len, char* service_uuid); /* Disable the multi adv instance */ bt_status_t (*multi_adv_disable)(int client_if); /* Configure the batchscan storage */ bt_status_t (*batchscan_cfg_storage)(int client_if, int batch_scan_full_max, int batch_scan_trunc_max, int batch_scan_notify_threshold); /* Enable batchscan */ bt_status_t (*batchscan_enb_batch_scan)(int client_if, int scan_mode, int scan_interval, int scan_window, int addr_type, int discard_rule); /* Disable batchscan */ bt_status_t (*batchscan_dis_batch_scan)(int client_if); /* Read out batchscan reports */ bt_status_t (*batchscan_read_reports)(int client_if, int scan_mode); /** Test mode interface */ bt_status_t (*test_command)( int command, btgatt_test_params_t* params); } btgatt_client_interface_t; __END_DECLS #endif /* ANDROID_INCLUDE_BT_GATT_CLIENT_H */ bluez-5.82/android/hardware/PaxHeaders/bt_gatt.h0000644000000000000000000000005014015011623016651 xustar0020 atime=1743516862 20 ctime=1743591276 bluez-5.82/android/hardware/bt_gatt.h0000644000000000000000000000224114015011623016331 0ustar00rootroot/* SPDX-License-Identifier: Apache-2.0 */ /* * Copyright (C) 2013 The Android Open Source Project * */ #ifndef ANDROID_INCLUDE_BT_GATT_H #define ANDROID_INCLUDE_BT_GATT_H #include #include "bt_gatt_client.h" #include "bt_gatt_server.h" __BEGIN_DECLS /** BT-GATT callbacks */ typedef struct { /** Set to sizeof(btgatt_callbacks_t) */ size_t size; /** GATT Client callbacks */ const btgatt_client_callbacks_t* client; /** GATT Server callbacks */ const btgatt_server_callbacks_t* server; } btgatt_callbacks_t; /** Represents the standard Bluetooth GATT interface. */ typedef struct { /** Set to sizeof(btgatt_interface_t) */ size_t size; /** * Initializes the interface and provides callback routines */ bt_status_t (*init)( const btgatt_callbacks_t* callbacks ); /** Closes the interface */ void (*cleanup)( void ); /** Pointer to the GATT client interface methods.*/ const btgatt_client_interface_t* client; /** Pointer to the GATT server interface methods.*/ const btgatt_server_interface_t* server; } btgatt_interface_t; __END_DECLS #endif /* ANDROID_INCLUDE_BT_GATT_H */ bluez-5.82/android/hardware/PaxHeaders/bt_hf_client.h0000644000000000000000000000005014015011623017645 xustar0020 atime=1743516862 20 ctime=1743591276 bluez-5.82/android/hardware/bt_hf_client.h0000644000000000000000000003103414015011623017327 0ustar00rootroot/* SPDX-License-Identifier: Apache-2.0 */ /* * Copyright (C) 2012-2014 The Android Open Source Project * */ #ifndef ANDROID_INCLUDE_BT_HF_CLIENT_H #define ANDROID_INCLUDE_BT_HF_CLIENT_H __BEGIN_DECLS typedef enum { BTHF_CLIENT_CONNECTION_STATE_DISCONNECTED = 0, BTHF_CLIENT_CONNECTION_STATE_CONNECTING, BTHF_CLIENT_CONNECTION_STATE_CONNECTED, BTHF_CLIENT_CONNECTION_STATE_SLC_CONNECTED, BTHF_CLIENT_CONNECTION_STATE_DISCONNECTING } bthf_client_connection_state_t; typedef enum { BTHF_CLIENT_AUDIO_STATE_DISCONNECTED = 0, BTHF_CLIENT_AUDIO_STATE_CONNECTING, BTHF_CLIENT_AUDIO_STATE_CONNECTED, BTHF_CLIENT_AUDIO_STATE_CONNECTED_MSBC, } bthf_client_audio_state_t; typedef enum { BTHF_CLIENT_VR_STATE_STOPPED = 0, BTHF_CLIENT_VR_STATE_STARTED } bthf_client_vr_state_t; typedef enum { BTHF_CLIENT_VOLUME_TYPE_SPK = 0, BTHF_CLIENT_VOLUME_TYPE_MIC } bthf_client_volume_type_t; typedef enum { BTHF_CLIENT_NETWORK_STATE_NOT_AVAILABLE = 0, BTHF_CLIENT_NETWORK_STATE_AVAILABLE } bthf_client_network_state_t; typedef enum { BTHF_CLIENT_SERVICE_TYPE_HOME = 0, BTHF_CLIENT_SERVICE_TYPE_ROAMING } bthf_client_service_type_t; typedef enum { BTHF_CLIENT_CALL_STATE_ACTIVE = 0, BTHF_CLIENT_CALL_STATE_HELD, BTHF_CLIENT_CALL_STATE_DIALING, BTHF_CLIENT_CALL_STATE_ALERTING, BTHF_CLIENT_CALL_STATE_INCOMING, BTHF_CLIENT_CALL_STATE_WAITING, BTHF_CLIENT_CALL_STATE_HELD_BY_RESP_HOLD, } bthf_client_call_state_t; typedef enum { BTHF_CLIENT_CALL_NO_CALLS_IN_PROGRESS = 0, BTHF_CLIENT_CALL_CALLS_IN_PROGRESS } bthf_client_call_t; typedef enum { BTHF_CLIENT_CALLSETUP_NONE = 0, BTHF_CLIENT_CALLSETUP_INCOMING, BTHF_CLIENT_CALLSETUP_OUTGOING, BTHF_CLIENT_CALLSETUP_ALERTING } bthf_client_callsetup_t; typedef enum { BTHF_CLIENT_CALLHELD_NONE = 0, BTHF_CLIENT_CALLHELD_HOLD_AND_ACTIVE, BTHF_CLIENT_CALLHELD_HOLD, } bthf_client_callheld_t; typedef enum { BTHF_CLIENT_RESP_AND_HOLD_HELD = 0, BTRH_CLIENT_RESP_AND_HOLD_ACCEPT, BTRH_CLIENT_RESP_AND_HOLD_REJECT, } bthf_client_resp_and_hold_t; typedef enum { BTHF_CLIENT_CALL_DIRECTION_OUTGOING = 0, BTHF_CLIENT_CALL_DIRECTION_INCOMING } bthf_client_call_direction_t; typedef enum { BTHF_CLIENT_CALL_MPTY_TYPE_SINGLE = 0, BTHF_CLIENT_CALL_MPTY_TYPE_MULTI } bthf_client_call_mpty_type_t; typedef enum { BTHF_CLIENT_CMD_COMPLETE_OK = 0, BTHF_CLIENT_CMD_COMPLETE_ERROR, BTHF_CLIENT_CMD_COMPLETE_ERROR_NO_CARRIER, BTHF_CLIENT_CMD_COMPLETE_ERROR_BUSY, BTHF_CLIENT_CMD_COMPLETE_ERROR_NO_ANSWER, BTHF_CLIENT_CMD_COMPLETE_ERROR_DELAYED, BTHF_CLIENT_CMD_COMPLETE_ERROR_BLACKLISTED, BTHF_CLIENT_CMD_COMPLETE_ERROR_CME } bthf_client_cmd_complete_t; typedef enum { BTHF_CLIENT_CALL_ACTION_CHLD_0 = 0, BTHF_CLIENT_CALL_ACTION_CHLD_1, BTHF_CLIENT_CALL_ACTION_CHLD_2, BTHF_CLIENT_CALL_ACTION_CHLD_3, BTHF_CLIENT_CALL_ACTION_CHLD_4, BTHF_CLIENT_CALL_ACTION_CHLD_1x, BTHF_CLIENT_CALL_ACTION_CHLD_2x, BTHF_CLIENT_CALL_ACTION_ATA, BTHF_CLIENT_CALL_ACTION_CHUP, BTHF_CLIENT_CALL_ACTION_BTRH_0, BTHF_CLIENT_CALL_ACTION_BTRH_1, BTHF_CLIENT_CALL_ACTION_BTRH_2, } bthf_client_call_action_t; typedef enum { BTHF_CLIENT_SERVICE_UNKNOWN = 0, BTHF_CLIENT_SERVICE_VOICE, BTHF_CLIENT_SERVICE_FAX } bthf_client_subscriber_service_type_t; typedef enum { BTHF_CLIENT_IN_BAND_RINGTONE_NOT_PROVIDED = 0, BTHF_CLIENT_IN_BAND_RINGTONE_PROVIDED, } bthf_client_in_band_ring_state_t; /* Peer features masks */ #define BTHF_CLIENT_PEER_FEAT_3WAY 0x00000001 /* Three-way calling */ #define BTHF_CLIENT_PEER_FEAT_ECNR 0x00000002 /* Echo cancellation and/or noise reduction */ #define BTHF_CLIENT_PEER_FEAT_VREC 0x00000004 /* Voice recognition */ #define BTHF_CLIENT_PEER_FEAT_INBAND 0x00000008 /* In-band ring tone */ #define BTHF_CLIENT_PEER_FEAT_VTAG 0x00000010 /* Attach a phone number to a voice tag */ #define BTHF_CLIENT_PEER_FEAT_REJECT 0x00000020 /* Ability to reject incoming call */ #define BTHF_CLIENT_PEER_FEAT_ECS 0x00000040 /* Enhanced Call Status */ #define BTHF_CLIENT_PEER_FEAT_ECC 0x00000080 /* Enhanced Call Control */ #define BTHF_CLIENT_PEER_FEAT_EXTERR 0x00000100 /* Extended error codes */ #define BTHF_CLIENT_PEER_FEAT_CODEC 0x00000200 /* Codec Negotiation */ /* Peer call handling features masks */ #define BTHF_CLIENT_CHLD_FEAT_REL 0x00000001 /* 0 Release waiting call or held calls */ #define BTHF_CLIENT_CHLD_FEAT_REL_ACC 0x00000002 /* 1 Release active calls and accept other (waiting or held) cal */ #define BTHF_CLIENT_CHLD_FEAT_REL_X 0x00000004 /* 1x Release specified active call only */ #define BTHF_CLIENT_CHLD_FEAT_HOLD_ACC 0x00000008 /* 2 Active calls on hold and accept other (waiting or held) call */ #define BTHF_CLIENT_CHLD_FEAT_PRIV_X 0x00000010 /* 2x Request private mode with specified call (put the rest on hold) */ #define BTHF_CLIENT_CHLD_FEAT_MERGE 0x00000020 /* 3 Add held call to multiparty */ #define BTHF_CLIENT_CHLD_FEAT_MERGE_DETACH 0x00000040 /* 4 Connect two calls and leave (disconnect from) multiparty */ /** Callback for connection state change. * state will have one of the values from BtHfConnectionState * peer/chld_features are valid only for BTHF_CLIENT_CONNECTION_STATE_SLC_CONNECTED state */ typedef void (* bthf_client_connection_state_callback)(bthf_client_connection_state_t state, unsigned int peer_feat, unsigned int chld_feat, bt_bdaddr_t *bd_addr); /** Callback for audio connection state change. * state will have one of the values from BtHfAudioState */ typedef void (* bthf_client_audio_state_callback)(bthf_client_audio_state_t state, bt_bdaddr_t *bd_addr); /** Callback for VR connection state change. * state will have one of the values from BtHfVRState */ typedef void (* bthf_client_vr_cmd_callback)(bthf_client_vr_state_t state); /** Callback for network state change */ typedef void (* bthf_client_network_state_callback) (bthf_client_network_state_t state); /** Callback for network roaming status change */ typedef void (* bthf_client_network_roaming_callback) (bthf_client_service_type_t type); /** Callback for signal strength indication */ typedef void (* bthf_client_network_signal_callback) (int signal_strength); /** Callback for battery level indication */ typedef void (* bthf_client_battery_level_callback) (int battery_level); /** Callback for current operator name */ typedef void (* bthf_client_current_operator_callback) (const char *name); /** Callback for call indicator */ typedef void (* bthf_client_call_callback) (bthf_client_call_t call); /** Callback for callsetup indicator */ typedef void (* bthf_client_callsetup_callback) (bthf_client_callsetup_t callsetup); /** Callback for callheld indicator */ typedef void (* bthf_client_callheld_callback) (bthf_client_callheld_t callheld); /** Callback for response and hold */ typedef void (* bthf_client_resp_and_hold_callback) (bthf_client_resp_and_hold_t resp_and_hold); /** Callback for Calling Line Identification notification * Will be called only when there is an incoming call and number is provided. */ typedef void (* bthf_client_clip_callback) (const char *number); /** * Callback for Call Waiting notification */ typedef void (* bthf_client_call_waiting_callback) (const char *number); /** * Callback for listing current calls. Can be called multiple time. * If number is unknown NULL is passed. */ typedef void (*bthf_client_current_calls) (int index, bthf_client_call_direction_t dir, bthf_client_call_state_t state, bthf_client_call_mpty_type_t mpty, const char *number); /** Callback for audio volume change */ typedef void (*bthf_client_volume_change_callback) (bthf_client_volume_type_t type, int volume); /** Callback for command complete event * cme is valid only for BTHF_CLIENT_CMD_COMPLETE_ERROR_CME type */ typedef void (*bthf_client_cmd_complete_callback) (bthf_client_cmd_complete_t type, int cme); /** Callback for subscriber information */ typedef void (* bthf_client_subscriber_info_callback) (const char *name, bthf_client_subscriber_service_type_t type); /** Callback for in-band ring tone settings */ typedef void (* bthf_client_in_band_ring_tone_callback) (bthf_client_in_band_ring_state_t state); /** * Callback for requested number from AG */ typedef void (* bthf_client_last_voice_tag_number_callback) (const char *number); /** * Callback for sending ring indication to app */ typedef void (* bthf_client_ring_indication_callback) (void); /** BT-HF callback structure. */ typedef struct { /** set to sizeof(BtHfClientCallbacks) */ size_t size; bthf_client_connection_state_callback connection_state_cb; bthf_client_audio_state_callback audio_state_cb; bthf_client_vr_cmd_callback vr_cmd_cb; bthf_client_network_state_callback network_state_cb; bthf_client_network_roaming_callback network_roaming_cb; bthf_client_network_signal_callback network_signal_cb; bthf_client_battery_level_callback battery_level_cb; bthf_client_current_operator_callback current_operator_cb; bthf_client_call_callback call_cb; bthf_client_callsetup_callback callsetup_cb; bthf_client_callheld_callback callheld_cb; bthf_client_resp_and_hold_callback resp_and_hold_cb; bthf_client_clip_callback clip_cb; bthf_client_call_waiting_callback call_waiting_cb; bthf_client_current_calls current_calls_cb; bthf_client_volume_change_callback volume_change_cb; bthf_client_cmd_complete_callback cmd_complete_cb; bthf_client_subscriber_info_callback subscriber_info_cb; bthf_client_in_band_ring_tone_callback in_band_ring_tone_cb; bthf_client_last_voice_tag_number_callback last_voice_tag_number_callback; bthf_client_ring_indication_callback ring_indication_cb; } bthf_client_callbacks_t; /** Represents the standard BT-HF interface. */ typedef struct { /** set to sizeof(BtHfClientInterface) */ size_t size; /** * Register the BtHf callbacks */ bt_status_t (*init)(bthf_client_callbacks_t* callbacks); /** connect to audio gateway */ bt_status_t (*connect)(bt_bdaddr_t *bd_addr); /** disconnect from audio gateway */ bt_status_t (*disconnect)(bt_bdaddr_t *bd_addr); /** create an audio connection */ bt_status_t (*connect_audio)(bt_bdaddr_t *bd_addr); /** close the audio connection */ bt_status_t (*disconnect_audio)(bt_bdaddr_t *bd_addr); /** start voice recognition */ bt_status_t (*start_voice_recognition)(void); /** stop voice recognition */ bt_status_t (*stop_voice_recognition)(void); /** volume control */ bt_status_t (*volume_control) (bthf_client_volume_type_t type, int volume); /** place a call with number a number * if number is NULL last called number is called (aka re-dial)*/ bt_status_t (*dial) (const char *number); /** place a call with number specified by location (speed dial) */ bt_status_t (*dial_memory) (int location); /** perform specified call related action * idx is limited only for enhanced call control related action */ bt_status_t (*handle_call_action) (bthf_client_call_action_t action, int idx); /** query list of current calls */ bt_status_t (*query_current_calls) (void); /** query name of current selected operator */ bt_status_t (*query_current_operator_name) (void); /** Retrieve subscriber information */ bt_status_t (*retrieve_subscriber_info) (void); /** Send DTMF code*/ bt_status_t (*send_dtmf) (char code); /** Request a phone number from AG corresponding to last voice tag recorded */ bt_status_t (*request_last_voice_tag_number) (void); /** Closes the interface. */ void (*cleanup)(void); /** Send AT Command. */ bt_status_t (*send_at_cmd) (int cmd, int val1, int val2, const char *arg); } bthf_client_interface_t; __END_DECLS #endif /* ANDROID_INCLUDE_BT_HF_CLIENT_H */ bluez-5.82/android/hardware/PaxHeaders/hardware.h0000644000000000000000000000005014015011623017022 xustar0020 atime=1743516862 20 ctime=1743591276 bluez-5.82/android/hardware/hardware.h0000644000000000000000000001605014015011623016505 0ustar00rootroot/* SPDX-License-Identifier: Apache-2.0 */ /* * Copyright (C) 2008 The Android Open Source Project * */ #ifndef ANDROID_INCLUDE_HARDWARE_HARDWARE_H #define ANDROID_INCLUDE_HARDWARE_HARDWARE_H #include #include __BEGIN_DECLS /* * Value for the hw_module_t.tag field */ #define MAKE_TAG_CONSTANT(A,B,C,D) (((A) << 24) | ((B) << 16) | ((C) << 8) | (D)) #define HARDWARE_MODULE_TAG MAKE_TAG_CONSTANT('H', 'W', 'M', 'T') #define HARDWARE_DEVICE_TAG MAKE_TAG_CONSTANT('H', 'W', 'D', 'T') #define HARDWARE_MAKE_API_VERSION(maj,min) \ ((((maj) & 0xff) << 8) | ((min) & 0xff)) #define HARDWARE_MAKE_API_VERSION_2(maj,min,hdr) \ ((((maj) & 0xff) << 24) | (((min) & 0xff) << 16) | ((hdr) & 0xffff)) #define HARDWARE_API_VERSION_2_MAJ_MIN_MASK 0xffff0000 #define HARDWARE_API_VERSION_2_HEADER_MASK 0x0000ffff /* * The current HAL API version. * * All module implementations must set the hw_module_t.hal_api_version field * to this value when declaring the module with HAL_MODULE_INFO_SYM. * * Note that previous implementations have always set this field to 0. * Therefore, libhardware HAL API will always consider versions 0.0 and 1.0 * to be 100% binary compatible. * */ #define HARDWARE_HAL_API_VERSION HARDWARE_MAKE_API_VERSION(1, 0) /* * Helper macros for module implementors. * * The derived modules should provide convenience macros for supported * versions so that implementations can explicitly specify module/device * versions at definition time. * * Use this macro to set the hw_module_t.module_api_version field. */ #define HARDWARE_MODULE_API_VERSION(maj,min) HARDWARE_MAKE_API_VERSION(maj,min) #define HARDWARE_MODULE_API_VERSION_2(maj,min,hdr) HARDWARE_MAKE_API_VERSION_2(maj,min,hdr) /* * Use this macro to set the hw_device_t.version field */ #define HARDWARE_DEVICE_API_VERSION(maj,min) HARDWARE_MAKE_API_VERSION(maj,min) #define HARDWARE_DEVICE_API_VERSION_2(maj,min,hdr) HARDWARE_MAKE_API_VERSION_2(maj,min,hdr) struct hw_module_t; struct hw_module_methods_t; struct hw_device_t; /** * Every hardware module must have a data structure named HAL_MODULE_INFO_SYM * and the fields of this data structure must begin with hw_module_t * followed by module specific information. */ typedef struct hw_module_t { /** tag must be initialized to HARDWARE_MODULE_TAG */ uint32_t tag; /** * The API version of the implemented module. The module owner is * responsible for updating the version when a module interface has * changed. * * The derived modules such as gralloc and audio own and manage this field. * The module user must interpret the version field to decide whether or * not to inter-operate with the supplied module implementation. * For example, SurfaceFlinger is responsible for making sure that * it knows how to manage different versions of the gralloc-module API, * and AudioFlinger must know how to do the same for audio-module API. * * The module API version should include a major and a minor component. * For example, version 1.0 could be represented as 0x0100. This format * implies that versions 0x0100-0x01ff are all API-compatible. * * In the future, libhardware will expose a hw_get_module_version() * (or equivalent) function that will take minimum/maximum supported * versions as arguments and would be able to reject modules with * versions outside of the supplied range. */ uint16_t module_api_version; #define version_major module_api_version /** * version_major/version_minor defines are supplied here for temporary * source code compatibility. They will be removed in the next version. * ALL clients must convert to the new version format. */ /** * The API version of the HAL module interface. This is meant to * version the hw_module_t, hw_module_methods_t, and hw_device_t * structures and definitions. * * The HAL interface owns this field. Module users/implementations * must NOT rely on this value for version information. * * Presently, 0 is the only valid value. */ uint16_t hal_api_version; #define version_minor hal_api_version /** Identifier of module */ const char *id; /** Name of this module */ const char *name; /** Author/owner/implementor of the module */ const char *author; /** Modules methods */ struct hw_module_methods_t* methods; /** module's dso */ void* dso; /** padding to 128 bytes, reserved for future use */ uint32_t reserved[32-7]; } hw_module_t; typedef struct hw_module_methods_t { /** Open a specific device */ int (*open)(const struct hw_module_t* module, const char* id, struct hw_device_t** device); } hw_module_methods_t; /** * Every device data structure must begin with hw_device_t * followed by module specific public methods and attributes. */ typedef struct hw_device_t { /** tag must be initialized to HARDWARE_DEVICE_TAG */ uint32_t tag; /** * Version of the module-specific device API. This value is used by * the derived-module user to manage different device implementations. * * The module user is responsible for checking the module_api_version * and device version fields to ensure that the user is capable of * communicating with the specific module implementation. * * One module can support multiple devices with different versions. This * can be useful when a device interface changes in an incompatible way * but it is still necessary to support older implementations at the same * time. One such example is the Camera 2.0 API. * * This field is interpreted by the module user and is ignored by the * HAL interface itself. */ uint32_t version; /** reference to the module this device belongs to */ struct hw_module_t* module; /** padding reserved for future use */ uint32_t reserved[12]; /** Close this device */ int (*close)(struct hw_device_t* device); } hw_device_t; /** * Name of the hal_module_info */ #define HAL_MODULE_INFO_SYM HMI /** * Name of the hal_module_info as a string */ #define HAL_MODULE_INFO_SYM_AS_STR "HMI" /** * Get the module info associated with a module by id. * * @return: 0 == success, <0 == error and *module == NULL */ int hw_get_module(const char *id, const struct hw_module_t **module); /** * Get the module info associated with a module instance by class 'class_id' * and instance 'inst'. * * Some modules types necessitate multiple instances. For example audio supports * multiple concurrent interfaces and thus 'audio' is the module class * and 'primary' or 'a2dp' are module interfaces. This implies that the files * providing these modules would be named audio.primary..so and * audio.a2dp..so * * @return: 0 == success, <0 == error and *module == NULL */ int hw_get_module_by_class(const char *class_id, const char *inst, const struct hw_module_t **module); __END_DECLS #endif /* ANDROID_INCLUDE_HARDWARE_HARDWARE_H */ bluez-5.82/android/hardware/PaxHeaders/bt_pan.h0000644000000000000000000000005014015011623016470 xustar0020 atime=1743516862 20 ctime=1743591276 bluez-5.82/android/hardware/bt_pan.h0000644000000000000000000000451114015011623016152 0ustar00rootroot/* SPDX-License-Identifier: Apache-2.0 */ /* * Copyright (C) 2012 The Android Open Source Project * */ #ifndef ANDROID_INCLUDE_BT_PAN_H #define ANDROID_INCLUDE_BT_PAN_H __BEGIN_DECLS #define BTPAN_ROLE_NONE 0 #define BTPAN_ROLE_PANNAP 1 #define BTPAN_ROLE_PANU 2 typedef enum { BTPAN_STATE_CONNECTED = 0, BTPAN_STATE_CONNECTING = 1, BTPAN_STATE_DISCONNECTED = 2, BTPAN_STATE_DISCONNECTING = 3 } btpan_connection_state_t; typedef enum { BTPAN_STATE_ENABLED = 0, BTPAN_STATE_DISABLED = 1 } btpan_control_state_t; /** * Callback for pan connection state */ typedef void (*btpan_connection_state_callback)(btpan_connection_state_t state, bt_status_t error, const bt_bdaddr_t *bd_addr, int local_role, int remote_role); typedef void (*btpan_control_state_callback)(btpan_control_state_t state, int local_role, bt_status_t error, const char* ifname); typedef struct { size_t size; btpan_control_state_callback control_state_cb; btpan_connection_state_callback connection_state_cb; } btpan_callbacks_t; typedef struct { /** set to size of this struct*/ size_t size; /** * Initialize the pan interface and register the btpan callbacks */ bt_status_t (*init)(const btpan_callbacks_t* callbacks); /* * enable the pan service by specified role. The result state of * enabl will be returned by btpan_control_state_callback. when pan-nap is enabled, * the state of connecting panu device will be notified by btpan_connection_state_callback */ bt_status_t (*enable)(int local_role); /* * get current pan local role */ int (*get_local_role)(void); /** * start bluetooth pan connection to the remote device by specified pan role. The result state will be * returned by btpan_connection_state_callback */ bt_status_t (*connect)(const bt_bdaddr_t *bd_addr, int local_role, int remote_role); /** * stop bluetooth pan connection. The result state will be returned by btpan_connection_state_callback */ bt_status_t (*disconnect)(const bt_bdaddr_t *bd_addr); /** * Cleanup the pan interface */ void (*cleanup)(void); } btpan_interface_t; __END_DECLS #endif /* ANDROID_INCLUDE_BT_PAN_H */ bluez-5.82/android/hardware/PaxHeaders/bluetooth.h0000644000000000000000000000005014015011623017232 xustar0020 atime=1743516862 20 ctime=1743591276 bluez-5.82/android/hardware/bluetooth.h0000644000000000000000000004323614015011623016723 0ustar00rootroot/* SPDX-License-Identifier: Apache-2.0 */ /* * Copyright (C) 2012 The Android Open Source Project * */ #ifndef ANDROID_INCLUDE_BLUETOOTH_H #define ANDROID_INCLUDE_BLUETOOTH_H #include #include #include #include #include __BEGIN_DECLS /** * The Bluetooth Hardware Module ID */ #define BT_HARDWARE_MODULE_ID "bluetooth" #define BT_STACK_MODULE_ID "bluetooth" #define BT_STACK_TEST_MODULE_ID "bluetooth_test" /* Bluetooth profile interface IDs */ #define BT_PROFILE_HANDSFREE_ID "handsfree" #define BT_PROFILE_HANDSFREE_CLIENT_ID "handsfree_client" #define BT_PROFILE_ADVANCED_AUDIO_ID "a2dp" #define BT_PROFILE_ADVANCED_AUDIO_SINK_ID "a2dp_sink" #define BT_PROFILE_HEALTH_ID "health" #define BT_PROFILE_SOCKETS_ID "socket" #define BT_PROFILE_HIDHOST_ID "hidhost" #define BT_PROFILE_PAN_ID "pan" #define BT_PROFILE_MAP_CLIENT_ID "map_client" #define BT_PROFILE_GATT_ID "gatt" #define BT_PROFILE_AV_RC_ID "avrcp" #define BT_PROFILE_AV_RC_CTRL_ID "avrcp_ctrl" /** Bluetooth Address */ typedef struct { uint8_t address[6]; } __attribute__((packed))bt_bdaddr_t; /** Bluetooth Device Name */ typedef struct { uint8_t name[249]; } __attribute__((packed))bt_bdname_t; /** Bluetooth Adapter Visibility Modes*/ typedef enum { BT_SCAN_MODE_NONE, BT_SCAN_MODE_CONNECTABLE, BT_SCAN_MODE_CONNECTABLE_DISCOVERABLE } bt_scan_mode_t; /** Bluetooth Adapter State */ typedef enum { BT_STATE_OFF, BT_STATE_ON } bt_state_t; /** Bluetooth Error Status */ /** We need to build on this */ typedef enum { BT_STATUS_SUCCESS, BT_STATUS_FAIL, BT_STATUS_NOT_READY, BT_STATUS_NOMEM, BT_STATUS_BUSY, BT_STATUS_DONE, /* request already completed */ BT_STATUS_UNSUPPORTED, BT_STATUS_PARM_INVALID, BT_STATUS_UNHANDLED, BT_STATUS_AUTH_FAILURE, BT_STATUS_RMT_DEV_DOWN, BT_STATUS_AUTH_REJECTED } bt_status_t; /** Bluetooth PinKey Code */ typedef struct { uint8_t pin[16]; } __attribute__((packed))bt_pin_code_t; typedef struct { uint8_t status; uint8_t ctrl_state; /* stack reported state */ uint64_t tx_time; /* in ms */ uint64_t rx_time; /* in ms */ uint64_t idle_time; /* in ms */ uint64_t energy_used; /* a product of mA, V and ms */ } __attribute__((packed))bt_activity_energy_info; /** Bluetooth Adapter Discovery state */ typedef enum { BT_DISCOVERY_STOPPED, BT_DISCOVERY_STARTED } bt_discovery_state_t; /** Bluetooth ACL connection state */ typedef enum { BT_ACL_STATE_CONNECTED, BT_ACL_STATE_DISCONNECTED } bt_acl_state_t; /** Bluetooth 128-bit UUID */ typedef struct { uint8_t uu[16]; } bt_uuid_t; /** Bluetooth SDP service record */ typedef struct { bt_uuid_t uuid; uint16_t channel; char name[256]; // what's the maximum length } bt_service_record_t; /** Bluetooth Remote Version info */ typedef struct { int version; int sub_ver; int manufacturer; } bt_remote_version_t; typedef struct { uint8_t local_privacy_enabled; uint8_t max_adv_instance; uint8_t rpa_offload_supported; uint8_t max_irk_list_size; uint8_t max_adv_filter_supported; uint8_t scan_result_storage_size_lobyte; uint8_t scan_result_storage_size_hibyte; uint8_t activity_energy_info_supported; }bt_local_le_features_t; /* Bluetooth Adapter and Remote Device property types */ typedef enum { /* Properties common to both adapter and remote device */ /** * Description - Bluetooth Device Name * Access mode - Adapter name can be GET/SET. Remote device can be GET * Data type - bt_bdname_t */ BT_PROPERTY_BDNAME = 0x1, /** * Description - Bluetooth Device Address * Access mode - Only GET. * Data type - bt_bdaddr_t */ BT_PROPERTY_BDADDR, /** * Description - Bluetooth Service 128-bit UUIDs * Access mode - Only GET. * Data type - Array of bt_uuid_t (Array size inferred from property length). */ BT_PROPERTY_UUIDS, /** * Description - Bluetooth Class of Device as found in Assigned Numbers * Access mode - Only GET. * Data type - uint32_t. */ BT_PROPERTY_CLASS_OF_DEVICE, /** * Description - Device Type - BREDR, BLE or DUAL Mode * Access mode - Only GET. * Data type - bt_device_type_t */ BT_PROPERTY_TYPE_OF_DEVICE, /** * Description - Bluetooth Service Record * Access mode - Only GET. * Data type - bt_service_record_t */ BT_PROPERTY_SERVICE_RECORD, /* Properties unique to adapter */ /** * Description - Bluetooth Adapter scan mode * Access mode - GET and SET * Data type - bt_scan_mode_t. */ BT_PROPERTY_ADAPTER_SCAN_MODE, /** * Description - List of bonded devices * Access mode - Only GET. * Data type - Array of bt_bdaddr_t of the bonded remote devices * (Array size inferred from property length). */ BT_PROPERTY_ADAPTER_BONDED_DEVICES, /** * Description - Bluetooth Adapter Discovery timeout (in seconds) * Access mode - GET and SET * Data type - uint32_t */ BT_PROPERTY_ADAPTER_DISCOVERY_TIMEOUT, /* Properties unique to remote device */ /** * Description - User defined friendly name of the remote device * Access mode - GET and SET * Data type - bt_bdname_t. */ BT_PROPERTY_REMOTE_FRIENDLY_NAME, /** * Description - RSSI value of the inquired remote device * Access mode - Only GET. * Data type - int32_t. */ BT_PROPERTY_REMOTE_RSSI, /** * Description - Remote version info * Access mode - SET/GET. * Data type - bt_remote_version_t. */ BT_PROPERTY_REMOTE_VERSION_INFO, /** * Description - Local LE features * Access mode - GET. * Data type - bt_local_le_features_t. */ BT_PROPERTY_LOCAL_LE_FEATURES, BT_PROPERTY_REMOTE_DEVICE_TIMESTAMP = 0xFF, } bt_property_type_t; /** Bluetooth Adapter Property data structure */ typedef struct { bt_property_type_t type; int len; void *val; } bt_property_t; /** Bluetooth Device Type */ typedef enum { BT_DEVICE_DEVTYPE_BREDR = 0x1, BT_DEVICE_DEVTYPE_BLE, BT_DEVICE_DEVTYPE_DUAL } bt_device_type_t; /** Bluetooth Bond state */ typedef enum { BT_BOND_STATE_NONE, BT_BOND_STATE_BONDING, BT_BOND_STATE_BONDED } bt_bond_state_t; /** Bluetooth SSP Bonding Variant */ typedef enum { BT_SSP_VARIANT_PASSKEY_CONFIRMATION, BT_SSP_VARIANT_PASSKEY_ENTRY, BT_SSP_VARIANT_CONSENT, BT_SSP_VARIANT_PASSKEY_NOTIFICATION } bt_ssp_variant_t; #define BT_MAX_NUM_UUIDS 32 /** Bluetooth Interface callbacks */ /** Bluetooth Enable/Disable Callback. */ typedef void (*adapter_state_changed_callback)(bt_state_t state); /** GET/SET Adapter Properties callback */ /* TODO: For the GET/SET property APIs/callbacks, we may need a session * identifier to associate the call with the callback. This would be needed * whenever more than one simultaneous instance of the same adapter_type * is get/set. * * If this is going to be handled in the Java framework, then we do not need * to manage sessions here. */ typedef void (*adapter_properties_callback)(bt_status_t status, int num_properties, bt_property_t *properties); /** GET/SET Remote Device Properties callback */ /** TODO: For remote device properties, do not see a need to get/set * multiple properties - num_properties shall be 1 */ typedef void (*remote_device_properties_callback)(bt_status_t status, bt_bdaddr_t *bd_addr, int num_properties, bt_property_t *properties); /** New device discovered callback */ /** If EIR data is not present, then BD_NAME and RSSI shall be NULL and -1 * respectively */ typedef void (*device_found_callback)(int num_properties, bt_property_t *properties); /** Discovery state changed callback */ typedef void (*discovery_state_changed_callback)(bt_discovery_state_t state); /** Bluetooth Legacy PinKey Request callback */ typedef void (*pin_request_callback)(bt_bdaddr_t *remote_bd_addr, bt_bdname_t *bd_name, uint32_t cod); /** Bluetooth SSP Request callback - Just Works & Numeric Comparison*/ /** pass_key - Shall be 0 for BT_SSP_PAIRING_VARIANT_CONSENT & * BT_SSP_PAIRING_PASSKEY_ENTRY */ /* TODO: Passkey request callback shall not be needed for devices with display * capability. We still need support this in the stack for completeness */ typedef void (*ssp_request_callback)(bt_bdaddr_t *remote_bd_addr, bt_bdname_t *bd_name, uint32_t cod, bt_ssp_variant_t pairing_variant, uint32_t pass_key); /** Bluetooth Bond state changed callback */ /* Invoked in response to create_bond, cancel_bond or remove_bond */ typedef void (*bond_state_changed_callback)(bt_status_t status, bt_bdaddr_t *remote_bd_addr, bt_bond_state_t state); /** Bluetooth ACL connection state changed callback */ typedef void (*acl_state_changed_callback)(bt_status_t status, bt_bdaddr_t *remote_bd_addr, bt_acl_state_t state); typedef enum { ASSOCIATE_JVM, DISASSOCIATE_JVM } bt_cb_thread_evt; /** Thread Associate/Disassociate JVM Callback */ /* Callback that is invoked by the callback thread to allow upper layer to attach/detach to/from * the JVM */ typedef void (*callback_thread_event)(bt_cb_thread_evt evt); /** Bluetooth Test Mode Callback */ /* Receive any HCI event from controller. Must be in DUT Mode for this callback to be received */ typedef void (*dut_mode_recv_callback)(uint16_t opcode, uint8_t *buf, uint8_t len); /* LE Test mode callbacks * This callback shall be invoked whenever the le_tx_test, le_rx_test or le_test_end is invoked * The num_packets is valid only for le_test_end command */ typedef void (*le_test_mode_callback)(bt_status_t status, uint16_t num_packets); /** Callback invoked when energy details are obtained */ /* Ctrl_state-Current controller state-Active-1,scan-2,or idle-3 state as defined by HCI spec. * If the ctrl_state value is 0, it means the API call failed * Time values-In milliseconds as returned by the controller * Energy used-Value as returned by the controller * Status-Provides the status of the read_energy_info API call */ typedef void (*energy_info_callback)(bt_activity_energy_info *energy_info); /** TODO: Add callbacks for Link Up/Down and other generic * notifications/callbacks */ /** Bluetooth DM callback structure. */ typedef struct { /** set to sizeof(bt_callbacks_t) */ size_t size; adapter_state_changed_callback adapter_state_changed_cb; adapter_properties_callback adapter_properties_cb; remote_device_properties_callback remote_device_properties_cb; device_found_callback device_found_cb; discovery_state_changed_callback discovery_state_changed_cb; pin_request_callback pin_request_cb; ssp_request_callback ssp_request_cb; bond_state_changed_callback bond_state_changed_cb; acl_state_changed_callback acl_state_changed_cb; callback_thread_event thread_evt_cb; dut_mode_recv_callback dut_mode_recv_cb; le_test_mode_callback le_test_mode_cb; energy_info_callback energy_info_cb; } bt_callbacks_t; typedef void (*alarm_cb)(void *data); typedef bool (*set_wake_alarm_callout)(uint64_t delay_millis, bool should_wake, alarm_cb cb, void *data); typedef int (*acquire_wake_lock_callout)(const char *lock_name); typedef int (*release_wake_lock_callout)(const char *lock_name); /** The set of functions required by bluedroid to set wake alarms and * grab wake locks. This struct is passed into the stack through the * |set_os_callouts| function on |bt_interface_t|. */ typedef struct { /* set to sizeof(bt_os_callouts_t) */ size_t size; set_wake_alarm_callout set_wake_alarm; acquire_wake_lock_callout acquire_wake_lock; release_wake_lock_callout release_wake_lock; } bt_os_callouts_t; /** NOTE: By default, no profiles are initialized at the time of init/enable. * Whenever the application invokes the 'init' API of a profile, then one of * the following shall occur: * * 1.) If Bluetooth is not enabled, then the Bluetooth core shall mark the * profile as enabled. Subsequently, when the application invokes the * Bluetooth 'enable', as part of the enable sequence the profile that were * marked shall be enabled by calling appropriate stack APIs. The * 'adapter_properties_cb' shall return the list of UUIDs of the * enabled profiles. * * 2.) If Bluetooth is enabled, then the Bluetooth core shall invoke the stack * profile API to initialize the profile and trigger a * 'adapter_properties_cb' with the current list of UUIDs including the * newly added profile's UUID. * * The reverse shall occur whenever the profile 'cleanup' APIs are invoked */ /** Represents the standard Bluetooth DM interface. */ typedef struct { /** set to sizeof(bt_interface_t) */ size_t size; /** * Opens the interface and provides the callback routines * to the implemenation of this interface. */ int (*init)(bt_callbacks_t* callbacks ); /** Enable Bluetooth. */ int (*enable)(void); /** Disable Bluetooth. */ int (*disable)(void); /** Closes the interface. */ void (*cleanup)(void); /** Get all Bluetooth Adapter properties at init */ int (*get_adapter_properties)(void); /** Get Bluetooth Adapter property of 'type' */ int (*get_adapter_property)(bt_property_type_t type); /** Set Bluetooth Adapter property of 'type' */ /* Based on the type, val shall be one of * bt_bdaddr_t or bt_bdname_t or bt_scanmode_t etc */ int (*set_adapter_property)(const bt_property_t *property); /** Get all Remote Device properties */ int (*get_remote_device_properties)(bt_bdaddr_t *remote_addr); /** Get Remote Device property of 'type' */ int (*get_remote_device_property)(bt_bdaddr_t *remote_addr, bt_property_type_t type); /** Set Remote Device property of 'type' */ int (*set_remote_device_property)(bt_bdaddr_t *remote_addr, const bt_property_t *property); /** Get Remote Device's service record for the given UUID */ int (*get_remote_service_record)(bt_bdaddr_t *remote_addr, bt_uuid_t *uuid); /** Start SDP to get remote services */ int (*get_remote_services)(bt_bdaddr_t *remote_addr); /** Start Discovery */ int (*start_discovery)(void); /** Cancel Discovery */ int (*cancel_discovery)(void); /** Create Bluetooth Bonding */ int (*create_bond)(const bt_bdaddr_t *bd_addr, int transport); /** Remove Bond */ int (*remove_bond)(const bt_bdaddr_t *bd_addr); /** Cancel Bond */ int (*cancel_bond)(const bt_bdaddr_t *bd_addr); /** * Get the connection status for a given remote device. * return value of 0 means the device is not connected, * non-zero return status indicates an active connection. */ int (*get_connection_state)(const bt_bdaddr_t *bd_addr); /** BT Legacy PinKey Reply */ /** If accept==FALSE, then pin_len and pin_code shall be 0x0 */ int (*pin_reply)(const bt_bdaddr_t *bd_addr, uint8_t accept, uint8_t pin_len, bt_pin_code_t *pin_code); /** BT SSP Reply - Just Works, Numeric Comparison and Passkey * passkey shall be zero for BT_SSP_VARIANT_PASSKEY_COMPARISON & * BT_SSP_VARIANT_CONSENT * For BT_SSP_VARIANT_PASSKEY_ENTRY, if accept==FALSE, then passkey * shall be zero */ int (*ssp_reply)(const bt_bdaddr_t *bd_addr, bt_ssp_variant_t variant, uint8_t accept, uint32_t passkey); /** Get Bluetooth profile interface */ const void* (*get_profile_interface) (const char *profile_id); /** Bluetooth Test Mode APIs - Bluetooth must be enabled for these APIs */ /* Configure DUT Mode - Use this mode to enter/exit DUT mode */ int (*dut_mode_configure)(uint8_t enable); /* Send any test HCI (vendor-specific) command to the controller. Must be in DUT Mode */ int (*dut_mode_send)(uint16_t opcode, uint8_t *buf, uint8_t len); /** BLE Test Mode APIs */ /* opcode MUST be one of: LE_Receiver_Test, LE_Transmitter_Test, LE_Test_End */ int (*le_test_mode)(uint16_t opcode, uint8_t *buf, uint8_t len); /* enable or disable bluetooth HCI snoop log */ int (*config_hci_snoop_log)(uint8_t enable); /** Sets the OS call-out functions that bluedroid needs for alarms and wake locks. * This should be called immediately after a successful |init|. */ int (*set_os_callouts)(bt_os_callouts_t *callouts); /** Read Energy info details - return value indicates BT_STATUS_SUCCESS or BT_STATUS_NOT_READY * Success indicates that the VSC command was sent to controller */ int (*read_energy_info)(); } bt_interface_t; /** TODO: Need to add APIs for Service Discovery, Service authorization and * connection management. Also need to add APIs for configuring * properties of remote bonded devices such as name, UUID etc. */ typedef struct { struct hw_device_t common; const bt_interface_t* (*get_bluetooth_interface)(); } bluetooth_device_t; typedef bluetooth_device_t bluetooth_module_t; __END_DECLS #endif /* ANDROID_INCLUDE_BLUETOOTH_H */ bluez-5.82/android/hardware/PaxHeaders/audio_effect.h0000644000000000000000000000005014015011623017642 xustar0020 atime=1743516862 20 ctime=1743591276 bluez-5.82/android/hardware/audio_effect.h0000644000000000000000000014663114015011623017336 0ustar00rootroot/* SPDX-License-Identifier: Apache-2.0 */ /* * Copyright (C) 2011 The Android Open Source Project * */ #ifndef ANDROID_AUDIO_EFFECT_H #define ANDROID_AUDIO_EFFECT_H #include #include #include #include #include #include __BEGIN_DECLS ///////////////////////////////////////////////// // Common Definitions ///////////////////////////////////////////////// // //--- Effect descriptor structure effect_descriptor_t // // Unique effect ID (can be generated from the following site: // http://www.itu.int/ITU-T/asn1/uuid.html) // This format is used for both "type" and "uuid" fields of the effect descriptor structure. // - When used for effect type and the engine is implementing and effect corresponding to a standard // OpenSL ES interface, this ID must be the one defined in OpenSLES_IID.h for that interface. // - When used as uuid, it should be a unique UUID for this particular implementation. typedef struct effect_uuid_s { uint32_t timeLow; uint16_t timeMid; uint16_t timeHiAndVersion; uint16_t clockSeq; uint8_t node[6]; } effect_uuid_t; // Maximum length of character strings in structures defines by this API. #define EFFECT_STRING_LEN_MAX 64 // NULL UUID definition (matches SL_IID_NULL_) #define EFFECT_UUID_INITIALIZER { 0xec7178ec, 0xe5e1, 0x4432, 0xa3f4, \ { 0x46, 0x57, 0xe6, 0x79, 0x52, 0x10 } } static const effect_uuid_t EFFECT_UUID_NULL_ = EFFECT_UUID_INITIALIZER; static const effect_uuid_t * const EFFECT_UUID_NULL = &EFFECT_UUID_NULL_; static const char * const EFFECT_UUID_NULL_STR = "ec7178ec-e5e1-4432-a3f4-4657e6795210"; // The effect descriptor contains necessary information to facilitate the enumeration of the effect // engines present in a library. typedef struct effect_descriptor_s { effect_uuid_t type; // UUID of to the OpenSL ES interface implemented by this effect effect_uuid_t uuid; // UUID for this particular implementation uint32_t apiVersion; // Version of the effect control API implemented uint32_t flags; // effect engine capabilities/requirements flags (see below) uint16_t cpuLoad; // CPU load indication (see below) uint16_t memoryUsage; // Data Memory usage (see below) char name[EFFECT_STRING_LEN_MAX]; // human readable effect name char implementor[EFFECT_STRING_LEN_MAX]; // human readable effect implementor name } effect_descriptor_t; // CPU load and memory usage indication: each effect implementation must provide an indication of // its CPU and memory usage for the audio effect framework to limit the number of effects // instantiated at a given time on a given platform. // The CPU load is expressed in 0.1 MIPS units as estimated on an ARM9E core (ARMv5TE) with 0 WS. // The memory usage is expressed in KB and includes only dynamically allocated memory // Definitions for flags field of effect descriptor. // +---------------------------+-----------+----------------------------------- // | description | bits | values // +---------------------------+-----------+----------------------------------- // | connection mode | 0..2 | 0 insert: after track process // | | | 1 auxiliary: connect to track auxiliary // | | | output and use send level // | | | 2 replace: replaces track process function; // | | | must implement SRC, volume and mono to stereo. // | | | 3 pre processing: applied below audio HAL on input // | | | 4 post processing: applied below audio HAL on output // | | | 5 - 7 reserved // +---------------------------+-----------+----------------------------------- // | insertion preference | 3..5 | 0 none // | | | 1 first of the chain // | | | 2 last of the chain // | | | 3 exclusive (only effect in the insert chain) // | | | 4..7 reserved // +---------------------------+-----------+----------------------------------- // | Volume management | 6..8 | 0 none // | | | 1 implements volume control // | | | 2 requires volume indication // | | | 4 reserved // +---------------------------+-----------+----------------------------------- // | Device indication | 9..11 | 0 none // | | | 1 requires device updates // | | | 2, 4 reserved // +---------------------------+-----------+----------------------------------- // | Sample input mode | 12..13 | 1 direct: process() function or EFFECT_CMD_SET_CONFIG // | | | command must specify a buffer descriptor // | | | 2 provider: process() function uses the // | | | bufferProvider indicated by the // | | | EFFECT_CMD_SET_CONFIG command to request input. // | | | buffers. // | | | 3 both: both input modes are supported // +---------------------------+-----------+----------------------------------- // | Sample output mode | 14..15 | 1 direct: process() function or EFFECT_CMD_SET_CONFIG // | | | command must specify a buffer descriptor // | | | 2 provider: process() function uses the // | | | bufferProvider indicated by the // | | | EFFECT_CMD_SET_CONFIG command to request output // | | | buffers. // | | | 3 both: both output modes are supported // +---------------------------+-----------+----------------------------------- // | Hardware acceleration | 16..17 | 0 No hardware acceleration // | | | 1 non tunneled hw acceleration: the process() function // | | | reads the samples, send them to HW accelerated // | | | effect processor, reads back the processed samples // | | | and returns them to the output buffer. // | | | 2 tunneled hw acceleration: the process() function is // | | | transparent. The effect interface is only used to // | | | control the effect engine. This mode is relevant for // | | | global effects actually applied by the audio // | | | hardware on the output stream. // +---------------------------+-----------+----------------------------------- // | Audio Mode indication | 18..19 | 0 none // | | | 1 requires audio mode updates // | | | 2..3 reserved // +---------------------------+-----------+----------------------------------- // | Audio source indication | 20..21 | 0 none // | | | 1 requires audio source updates // | | | 2..3 reserved // +---------------------------+-----------+----------------------------------- // | Effect offload supported | 22 | 0 The effect cannot be offloaded to an audio DSP // | | | 1 The effect can be offloaded to an audio DSP // +---------------------------+-----------+----------------------------------- // Insert mode #define EFFECT_FLAG_TYPE_SHIFT 0 #define EFFECT_FLAG_TYPE_SIZE 3 #define EFFECT_FLAG_TYPE_MASK (((1 << EFFECT_FLAG_TYPE_SIZE) -1) \ << EFFECT_FLAG_TYPE_SHIFT) #define EFFECT_FLAG_TYPE_INSERT (0 << EFFECT_FLAG_TYPE_SHIFT) #define EFFECT_FLAG_TYPE_AUXILIARY (1 << EFFECT_FLAG_TYPE_SHIFT) #define EFFECT_FLAG_TYPE_REPLACE (2 << EFFECT_FLAG_TYPE_SHIFT) #define EFFECT_FLAG_TYPE_PRE_PROC (3 << EFFECT_FLAG_TYPE_SHIFT) #define EFFECT_FLAG_TYPE_POST_PROC (4 << EFFECT_FLAG_TYPE_SHIFT) // Insert preference #define EFFECT_FLAG_INSERT_SHIFT (EFFECT_FLAG_TYPE_SHIFT + EFFECT_FLAG_TYPE_SIZE) #define EFFECT_FLAG_INSERT_SIZE 3 #define EFFECT_FLAG_INSERT_MASK (((1 << EFFECT_FLAG_INSERT_SIZE) -1) \ << EFFECT_FLAG_INSERT_SHIFT) #define EFFECT_FLAG_INSERT_ANY (0 << EFFECT_FLAG_INSERT_SHIFT) #define EFFECT_FLAG_INSERT_FIRST (1 << EFFECT_FLAG_INSERT_SHIFT) #define EFFECT_FLAG_INSERT_LAST (2 << EFFECT_FLAG_INSERT_SHIFT) #define EFFECT_FLAG_INSERT_EXCLUSIVE (3 << EFFECT_FLAG_INSERT_SHIFT) // Volume control #define EFFECT_FLAG_VOLUME_SHIFT (EFFECT_FLAG_INSERT_SHIFT + EFFECT_FLAG_INSERT_SIZE) #define EFFECT_FLAG_VOLUME_SIZE 3 #define EFFECT_FLAG_VOLUME_MASK (((1 << EFFECT_FLAG_VOLUME_SIZE) -1) \ << EFFECT_FLAG_VOLUME_SHIFT) #define EFFECT_FLAG_VOLUME_CTRL (1 << EFFECT_FLAG_VOLUME_SHIFT) #define EFFECT_FLAG_VOLUME_IND (2 << EFFECT_FLAG_VOLUME_SHIFT) #define EFFECT_FLAG_VOLUME_NONE (0 << EFFECT_FLAG_VOLUME_SHIFT) // Device indication #define EFFECT_FLAG_DEVICE_SHIFT (EFFECT_FLAG_VOLUME_SHIFT + EFFECT_FLAG_VOLUME_SIZE) #define EFFECT_FLAG_DEVICE_SIZE 3 #define EFFECT_FLAG_DEVICE_MASK (((1 << EFFECT_FLAG_DEVICE_SIZE) -1) \ << EFFECT_FLAG_DEVICE_SHIFT) #define EFFECT_FLAG_DEVICE_IND (1 << EFFECT_FLAG_DEVICE_SHIFT) #define EFFECT_FLAG_DEVICE_NONE (0 << EFFECT_FLAG_DEVICE_SHIFT) // Sample input modes #define EFFECT_FLAG_INPUT_SHIFT (EFFECT_FLAG_DEVICE_SHIFT + EFFECT_FLAG_DEVICE_SIZE) #define EFFECT_FLAG_INPUT_SIZE 2 #define EFFECT_FLAG_INPUT_MASK (((1 << EFFECT_FLAG_INPUT_SIZE) -1) \ << EFFECT_FLAG_INPUT_SHIFT) #define EFFECT_FLAG_INPUT_DIRECT (1 << EFFECT_FLAG_INPUT_SHIFT) #define EFFECT_FLAG_INPUT_PROVIDER (2 << EFFECT_FLAG_INPUT_SHIFT) #define EFFECT_FLAG_INPUT_BOTH (3 << EFFECT_FLAG_INPUT_SHIFT) // Sample output modes #define EFFECT_FLAG_OUTPUT_SHIFT (EFFECT_FLAG_INPUT_SHIFT + EFFECT_FLAG_INPUT_SIZE) #define EFFECT_FLAG_OUTPUT_SIZE 2 #define EFFECT_FLAG_OUTPUT_MASK (((1 << EFFECT_FLAG_OUTPUT_SIZE) -1) \ << EFFECT_FLAG_OUTPUT_SHIFT) #define EFFECT_FLAG_OUTPUT_DIRECT (1 << EFFECT_FLAG_OUTPUT_SHIFT) #define EFFECT_FLAG_OUTPUT_PROVIDER (2 << EFFECT_FLAG_OUTPUT_SHIFT) #define EFFECT_FLAG_OUTPUT_BOTH (3 << EFFECT_FLAG_OUTPUT_SHIFT) // Hardware acceleration mode #define EFFECT_FLAG_HW_ACC_SHIFT (EFFECT_FLAG_OUTPUT_SHIFT + EFFECT_FLAG_OUTPUT_SIZE) #define EFFECT_FLAG_HW_ACC_SIZE 2 #define EFFECT_FLAG_HW_ACC_MASK (((1 << EFFECT_FLAG_HW_ACC_SIZE) -1) \ << EFFECT_FLAG_HW_ACC_SHIFT) #define EFFECT_FLAG_HW_ACC_SIMPLE (1 << EFFECT_FLAG_HW_ACC_SHIFT) #define EFFECT_FLAG_HW_ACC_TUNNEL (2 << EFFECT_FLAG_HW_ACC_SHIFT) // Audio mode indication #define EFFECT_FLAG_AUDIO_MODE_SHIFT (EFFECT_FLAG_HW_ACC_SHIFT + EFFECT_FLAG_HW_ACC_SIZE) #define EFFECT_FLAG_AUDIO_MODE_SIZE 2 #define EFFECT_FLAG_AUDIO_MODE_MASK (((1 << EFFECT_FLAG_AUDIO_MODE_SIZE) -1) \ << EFFECT_FLAG_AUDIO_MODE_SHIFT) #define EFFECT_FLAG_AUDIO_MODE_IND (1 << EFFECT_FLAG_AUDIO_MODE_SHIFT) #define EFFECT_FLAG_AUDIO_MODE_NONE (0 << EFFECT_FLAG_AUDIO_MODE_SHIFT) // Audio source indication #define EFFECT_FLAG_AUDIO_SOURCE_SHIFT (EFFECT_FLAG_AUDIO_MODE_SHIFT + EFFECT_FLAG_AUDIO_MODE_SIZE) #define EFFECT_FLAG_AUDIO_SOURCE_SIZE 2 #define EFFECT_FLAG_AUDIO_SOURCE_MASK (((1 << EFFECT_FLAG_AUDIO_SOURCE_SIZE) -1) \ << EFFECT_FLAG_AUDIO_SOURCE_SHIFT) #define EFFECT_FLAG_AUDIO_SOURCE_IND (1 << EFFECT_FLAG_AUDIO_SOURCE_SHIFT) #define EFFECT_FLAG_AUDIO_SOURCE_NONE (0 << EFFECT_FLAG_AUDIO_SOURCE_SHIFT) // Effect offload indication #define EFFECT_FLAG_OFFLOAD_SHIFT (EFFECT_FLAG_AUDIO_SOURCE_SHIFT + \ EFFECT_FLAG_AUDIO_SOURCE_SIZE) #define EFFECT_FLAG_OFFLOAD_SIZE 1 #define EFFECT_FLAG_OFFLOAD_MASK (((1 << EFFECT_FLAG_OFFLOAD_SIZE) -1) \ << EFFECT_FLAG_OFFLOAD_SHIFT) #define EFFECT_FLAG_OFFLOAD_SUPPORTED (1 << EFFECT_FLAG_OFFLOAD_SHIFT) #define EFFECT_MAKE_API_VERSION(M, m) (((M)<<16) | ((m) & 0xFFFF)) #define EFFECT_API_VERSION_MAJOR(v) ((v)>>16) #define EFFECT_API_VERSION_MINOR(v) ((m) & 0xFFFF) ///////////////////////////////////////////////// // Effect control interface ///////////////////////////////////////////////// // Effect control interface version 2.0 #define EFFECT_CONTROL_API_VERSION EFFECT_MAKE_API_VERSION(2,0) // Effect control interface structure: effect_interface_s // The effect control interface is exposed by each effect engine implementation. It consists of // a set of functions controlling the configuration, activation and process of the engine. // The functions are grouped in a structure of type effect_interface_s. // // Effect control interface handle: effect_handle_t // The effect_handle_t serves two purposes regarding the implementation of the effect engine: // - 1 it is the address of a pointer to an effect_interface_s structure where the functions // of the effect control API for a particular effect are located. // - 2 it is the address of the context of a particular effect instance. // A typical implementation in the effect library would define a structure as follows: // struct effect_module_s { // const struct effect_interface_s *itfe; // effect_config_t config; // effect_context_t context; // } // The implementation of EffectCreate() function would then allocate a structure of this // type and return its address as effect_handle_t typedef struct effect_interface_s **effect_handle_t; // Forward definition of type audio_buffer_t typedef struct audio_buffer_s audio_buffer_t; // Effect control interface definition struct effect_interface_s { //////////////////////////////////////////////////////////////////////////////// // // Function: process // // Description: Effect process function. Takes input samples as specified // (count and location) in input buffer descriptor and output processed // samples as specified in output buffer descriptor. If the buffer descriptor // is not specified the function must use either the buffer or the // buffer provider function installed by the EFFECT_CMD_SET_CONFIG command. // The effect framework will call the process() function after the EFFECT_CMD_ENABLE // command is received and until the EFFECT_CMD_DISABLE is received. When the engine // receives the EFFECT_CMD_DISABLE command it should turn off the effect gracefully // and when done indicate that it is OK to stop calling the process() function by // returning the -ENODATA status. // // NOTE: the process() function implementation should be "real-time safe" that is // it should not perform blocking calls: malloc/free, sleep, read/write/open/close, // pthread_cond_wait/pthread_mutex_lock... // // Input: // self: handle to the effect interface this function // is called on. // inBuffer: buffer descriptor indicating where to read samples to process. // If NULL, use the configuration passed by EFFECT_CMD_SET_CONFIG command. // // outBuffer: buffer descriptor indicating where to write processed samples. // If NULL, use the configuration passed by EFFECT_CMD_SET_CONFIG command. // // Output: // returned value: 0 successful operation // -ENODATA the engine has finished the disable phase and the framework // can stop calling process() // -EINVAL invalid interface handle or // invalid input/output buffer description //////////////////////////////////////////////////////////////////////////////// int32_t (*process)(effect_handle_t self, audio_buffer_t *inBuffer, audio_buffer_t *outBuffer); //////////////////////////////////////////////////////////////////////////////// // // Function: command // // Description: Send a command and receive a response to/from effect engine. // // Input: // self: handle to the effect interface this function // is called on. // cmdCode: command code: the command can be a standardized command defined in // effect_command_e (see below) or a proprietary command. // cmdSize: size of command in bytes // pCmdData: pointer to command data // pReplyData: pointer to reply data // // Input/Output: // replySize: maximum size of reply data as input // actual size of reply data as output // // Output: // returned value: 0 successful operation // -EINVAL invalid interface handle or // invalid command/reply size or format according to command code // The return code should be restricted to indicate problems related to the this // API specification. Status related to the execution of a particular command should be // indicated as part of the reply field. // // *pReplyData updated with command response // //////////////////////////////////////////////////////////////////////////////// int32_t (*command)(effect_handle_t self, uint32_t cmdCode, uint32_t cmdSize, void *pCmdData, uint32_t *replySize, void *pReplyData); //////////////////////////////////////////////////////////////////////////////// // // Function: get_descriptor // // Description: Returns the effect descriptor // // Input: // self: handle to the effect interface this function // is called on. // // Input/Output: // pDescriptor: address where to return the effect descriptor. // // Output: // returned value: 0 successful operation. // -EINVAL invalid interface handle or invalid pDescriptor // *pDescriptor: updated with the effect descriptor. // //////////////////////////////////////////////////////////////////////////////// int32_t (*get_descriptor)(effect_handle_t self, effect_descriptor_t *pDescriptor); //////////////////////////////////////////////////////////////////////////////// // // Function: process_reverse // // Description: Process reverse stream function. This function is used to pass // a reference stream to the effect engine. If the engine does not need a reference // stream, this function pointer can be set to NULL. // This function would typically implemented by an Echo Canceler. // // Input: // self: handle to the effect interface this function // is called on. // inBuffer: buffer descriptor indicating where to read samples to process. // If NULL, use the configuration passed by EFFECT_CMD_SET_CONFIG_REVERSE command. // // outBuffer: buffer descriptor indicating where to write processed samples. // If NULL, use the configuration passed by EFFECT_CMD_SET_CONFIG_REVERSE command. // If the buffer and buffer provider in the configuration received by // EFFECT_CMD_SET_CONFIG_REVERSE are also NULL, do not return modified reverse // stream data // // Output: // returned value: 0 successful operation // -ENODATA the engine has finished the disable phase and the framework // can stop calling process_reverse() // -EINVAL invalid interface handle or // invalid input/output buffer description //////////////////////////////////////////////////////////////////////////////// int32_t (*process_reverse)(effect_handle_t self, audio_buffer_t *inBuffer, audio_buffer_t *outBuffer); }; // //--- Standardized command codes for command() function // enum effect_command_e { EFFECT_CMD_INIT, // initialize effect engine EFFECT_CMD_SET_CONFIG, // configure effect engine (see effect_config_t) EFFECT_CMD_RESET, // reset effect engine EFFECT_CMD_ENABLE, // enable effect process EFFECT_CMD_DISABLE, // disable effect process EFFECT_CMD_SET_PARAM, // set parameter immediately (see effect_param_t) EFFECT_CMD_SET_PARAM_DEFERRED, // set parameter deferred EFFECT_CMD_SET_PARAM_COMMIT, // commit previous set parameter deferred EFFECT_CMD_GET_PARAM, // get parameter EFFECT_CMD_SET_DEVICE, // set audio device (see audio.h, audio_devices_t) EFFECT_CMD_SET_VOLUME, // set volume EFFECT_CMD_SET_AUDIO_MODE, // set the audio mode (normal, ring, ...) EFFECT_CMD_SET_CONFIG_REVERSE, // configure effect engine reverse stream(see effect_config_t) EFFECT_CMD_SET_INPUT_DEVICE, // set capture device (see audio.h, audio_devices_t) EFFECT_CMD_GET_CONFIG, // read effect engine configuration EFFECT_CMD_GET_CONFIG_REVERSE, // read configure effect engine reverse stream configuration EFFECT_CMD_GET_FEATURE_SUPPORTED_CONFIGS,// get all supported configurations for a feature. EFFECT_CMD_GET_FEATURE_CONFIG, // get current feature configuration EFFECT_CMD_SET_FEATURE_CONFIG, // set current feature configuration EFFECT_CMD_SET_AUDIO_SOURCE, // set the audio source (see audio.h, audio_source_t) EFFECT_CMD_OFFLOAD, // set if effect thread is an offload one, // send the ioHandle of the effect thread EFFECT_CMD_FIRST_PROPRIETARY = 0x10000 // first proprietary command code }; //================================================================================================== // command: EFFECT_CMD_INIT //-------------------------------------------------------------------------------------------------- // description: // Initialize effect engine: All configurations return to default //-------------------------------------------------------------------------------------------------- // command format: // size: 0 // data: N/A //-------------------------------------------------------------------------------------------------- // reply format: // size: sizeof(int) // data: status //================================================================================================== // command: EFFECT_CMD_SET_CONFIG //-------------------------------------------------------------------------------------------------- // description: // Apply new audio parameters configurations for input and output buffers //-------------------------------------------------------------------------------------------------- // command format: // size: sizeof(effect_config_t) // data: effect_config_t //-------------------------------------------------------------------------------------------------- // reply format: // size: sizeof(int) // data: status //================================================================================================== // command: EFFECT_CMD_RESET //-------------------------------------------------------------------------------------------------- // description: // Reset the effect engine. Keep configuration but resets state and buffer content //-------------------------------------------------------------------------------------------------- // command format: // size: 0 // data: N/A //-------------------------------------------------------------------------------------------------- // reply format: // size: 0 // data: N/A //================================================================================================== // command: EFFECT_CMD_ENABLE //-------------------------------------------------------------------------------------------------- // description: // Enable the process. Called by the framework before the first call to process() //-------------------------------------------------------------------------------------------------- // command format: // size: 0 // data: N/A //-------------------------------------------------------------------------------------------------- // reply format: // size: sizeof(int) // data: status //================================================================================================== // command: EFFECT_CMD_DISABLE //-------------------------------------------------------------------------------------------------- // description: // Disable the process. Called by the framework after the last call to process() //-------------------------------------------------------------------------------------------------- // command format: // size: 0 // data: N/A //-------------------------------------------------------------------------------------------------- // reply format: // size: sizeof(int) // data: status //================================================================================================== // command: EFFECT_CMD_SET_PARAM //-------------------------------------------------------------------------------------------------- // description: // Set a parameter and apply it immediately //-------------------------------------------------------------------------------------------------- // command format: // size: sizeof(effect_param_t) + size of param and value // data: effect_param_t + param + value. See effect_param_t definition below for value offset //-------------------------------------------------------------------------------------------------- // reply format: // size: sizeof(int) // data: status //================================================================================================== // command: EFFECT_CMD_SET_PARAM_DEFERRED //-------------------------------------------------------------------------------------------------- // description: // Set a parameter but apply it only when receiving EFFECT_CMD_SET_PARAM_COMMIT command //-------------------------------------------------------------------------------------------------- // command format: // size: sizeof(effect_param_t) + size of param and value // data: effect_param_t + param + value. See effect_param_t definition below for value offset //-------------------------------------------------------------------------------------------------- // reply format: // size: 0 // data: N/A //================================================================================================== // command: EFFECT_CMD_SET_PARAM_COMMIT //-------------------------------------------------------------------------------------------------- // description: // Apply all previously received EFFECT_CMD_SET_PARAM_DEFERRED commands //-------------------------------------------------------------------------------------------------- // command format: // size: 0 // data: N/A //-------------------------------------------------------------------------------------------------- // reply format: // size: sizeof(int) // data: status //================================================================================================== // command: EFFECT_CMD_GET_PARAM //-------------------------------------------------------------------------------------------------- // description: // Get a parameter value //-------------------------------------------------------------------------------------------------- // command format: // size: sizeof(effect_param_t) + size of param // data: effect_param_t + param //-------------------------------------------------------------------------------------------------- // reply format: // size: sizeof(effect_param_t) + size of param and value // data: effect_param_t + param + value. See effect_param_t definition below for value offset //================================================================================================== // command: EFFECT_CMD_SET_DEVICE //-------------------------------------------------------------------------------------------------- // description: // Set the rendering device the audio output path is connected to. See audio.h, audio_devices_t // for device values. // The effect implementation must set EFFECT_FLAG_DEVICE_IND flag in its descriptor to receive this // command when the device changes //-------------------------------------------------------------------------------------------------- // command format: // size: sizeof(uint32_t) // data: uint32_t //-------------------------------------------------------------------------------------------------- // reply format: // size: 0 // data: N/A //================================================================================================== // command: EFFECT_CMD_SET_VOLUME //-------------------------------------------------------------------------------------------------- // description: // Set and get volume. Used by audio framework to delegate volume control to effect engine. // The effect implementation must set EFFECT_FLAG_VOLUME_IND or EFFECT_FLAG_VOLUME_CTRL flag in // its descriptor to receive this command before every call to process() function // If EFFECT_FLAG_VOLUME_CTRL flag is set in the effect descriptor, the effect engine must return // the volume that should be applied before the effect is processed. The overall volume (the volume // actually applied by the effect engine multiplied by the returned value) should match the value // indicated in the command. //-------------------------------------------------------------------------------------------------- // command format: // size: n * sizeof(uint32_t) // data: volume for each channel defined in effect_config_t for output buffer expressed in // 8.24 fixed point format //-------------------------------------------------------------------------------------------------- // reply format: // size: n * sizeof(uint32_t) / 0 // data: - if EFFECT_FLAG_VOLUME_CTRL is set in effect descriptor: // volume for each channel defined in effect_config_t for output buffer expressed in // 8.24 fixed point format // - if EFFECT_FLAG_VOLUME_CTRL is not set in effect descriptor: // N/A // It is legal to receive a null pointer as pReplyData in which case the effect framework has // delegated volume control to another effect //================================================================================================== // command: EFFECT_CMD_SET_AUDIO_MODE //-------------------------------------------------------------------------------------------------- // description: // Set the audio mode. The effect implementation must set EFFECT_FLAG_AUDIO_MODE_IND flag in its // descriptor to receive this command when the audio mode changes. //-------------------------------------------------------------------------------------------------- // command format: // size: sizeof(uint32_t) // data: audio_mode_t //-------------------------------------------------------------------------------------------------- // reply format: // size: 0 // data: N/A //================================================================================================== // command: EFFECT_CMD_SET_CONFIG_REVERSE //-------------------------------------------------------------------------------------------------- // description: // Apply new audio parameters configurations for input and output buffers of reverse stream. // An example of reverse stream is the echo reference supplied to an Acoustic Echo Canceler. //-------------------------------------------------------------------------------------------------- // command format: // size: sizeof(effect_config_t) // data: effect_config_t //-------------------------------------------------------------------------------------------------- // reply format: // size: sizeof(int) // data: status //================================================================================================== // command: EFFECT_CMD_SET_INPUT_DEVICE //-------------------------------------------------------------------------------------------------- // description: // Set the capture device the audio input path is connected to. See audio.h, audio_devices_t // for device values. // The effect implementation must set EFFECT_FLAG_DEVICE_IND flag in its descriptor to receive this // command when the device changes //-------------------------------------------------------------------------------------------------- // command format: // size: sizeof(uint32_t) // data: uint32_t //-------------------------------------------------------------------------------------------------- // reply format: // size: 0 // data: N/A //================================================================================================== // command: EFFECT_CMD_GET_CONFIG //-------------------------------------------------------------------------------------------------- // description: // Read audio parameters configurations for input and output buffers //-------------------------------------------------------------------------------------------------- // command format: // size: 0 // data: N/A //-------------------------------------------------------------------------------------------------- // reply format: // size: sizeof(effect_config_t) // data: effect_config_t //================================================================================================== // command: EFFECT_CMD_GET_CONFIG_REVERSE //-------------------------------------------------------------------------------------------------- // description: // Read audio parameters configurations for input and output buffers of reverse stream //-------------------------------------------------------------------------------------------------- // command format: // size: 0 // data: N/A //-------------------------------------------------------------------------------------------------- // reply format: // size: sizeof(effect_config_t) // data: effect_config_t //================================================================================================== // command: EFFECT_CMD_GET_FEATURE_SUPPORTED_CONFIGS //-------------------------------------------------------------------------------------------------- // description: // Queries for supported configurations for a particular feature (e.g. get the supported // combinations of main and auxiliary channels for a noise suppressor). // The command parameter is the feature identifier (See effect_feature_e for a list of defined // features) followed by the maximum number of configuration descriptor to return. // The reply is composed of: // - status (uint32_t): // - 0 if feature is supported // - -ENOSYS if the feature is not supported, // - -ENOMEM if the feature is supported but the total number of supported configurations // exceeds the maximum number indicated by the caller. // - total number of supported configurations (uint32_t) // - an array of configuration descriptors. // The actual number of descriptors returned must not exceed the maximum number indicated by // the caller. //-------------------------------------------------------------------------------------------------- // command format: // size: 2 x sizeof(uint32_t) // data: effect_feature_e + maximum number of configurations to return //-------------------------------------------------------------------------------------------------- // reply format: // size: 2 x sizeof(uint32_t) + n x sizeof () // data: status + total number of configurations supported + array of n config descriptors //================================================================================================== // command: EFFECT_CMD_GET_FEATURE_CONFIG //-------------------------------------------------------------------------------------------------- // description: // Retrieves current configuration for a given feature. // The reply status is: // - 0 if feature is supported // - -ENOSYS if the feature is not supported, //-------------------------------------------------------------------------------------------------- // command format: // size: sizeof(uint32_t) // data: effect_feature_e //-------------------------------------------------------------------------------------------------- // reply format: // size: sizeof(uint32_t) + sizeof () // data: status + config descriptor //================================================================================================== // command: EFFECT_CMD_SET_FEATURE_CONFIG //-------------------------------------------------------------------------------------------------- // description: // Sets current configuration for a given feature. // The reply status is: // - 0 if feature is supported // - -ENOSYS if the feature is not supported, // - -EINVAL if the configuration is invalid //-------------------------------------------------------------------------------------------------- // command format: // size: sizeof(uint32_t) + sizeof () // data: effect_feature_e + config descriptor //-------------------------------------------------------------------------------------------------- // reply format: // size: sizeof(uint32_t) // data: status //================================================================================================== // command: EFFECT_CMD_SET_AUDIO_SOURCE //-------------------------------------------------------------------------------------------------- // description: // Set the audio source the capture path is configured for (Camcorder, voice recognition...). // See audio.h, audio_source_t for values. //-------------------------------------------------------------------------------------------------- // command format: // size: sizeof(uint32_t) // data: uint32_t //-------------------------------------------------------------------------------------------------- // reply format: // size: 0 // data: N/A //================================================================================================== // command: EFFECT_CMD_OFFLOAD //-------------------------------------------------------------------------------------------------- // description: // 1.indicate if the playback thread the effect is attached to is offloaded or not // 2.update the io handle of the playback thread the effect is attached to //-------------------------------------------------------------------------------------------------- // command format: // size: sizeof(effect_offload_param_t) // data: effect_offload_param_t //-------------------------------------------------------------------------------------------------- // reply format: // size: sizeof(uint32_t) // data: uint32_t //-------------------------------------------------------------------------------------------------- // command: EFFECT_CMD_FIRST_PROPRIETARY //-------------------------------------------------------------------------------------------------- // description: // All proprietary effect commands must use command codes above this value. The size and format of // command and response fields is free in this case //================================================================================================== // Audio buffer descriptor used by process(), bufferProvider() functions and buffer_config_t // structure. Multi-channel audio is always interleaved. The channel order is from LSB to MSB with // regard to the channel mask definition in audio.h, audio_channel_mask_t e.g : // Stereo: left, right // 5 point 1: front left, front right, front center, low frequency, back left, back right // The buffer size is expressed in frame count, a frame being composed of samples for all // channels at a given time. Frame size for unspecified format (AUDIO_FORMAT_OTHER) is 8 bit by // definition struct audio_buffer_s { size_t frameCount; // number of frames in buffer union { void* raw; // raw pointer to start of buffer int32_t* s32; // pointer to signed 32 bit data at start of buffer int16_t* s16; // pointer to signed 16 bit data at start of buffer uint8_t* u8; // pointer to unsigned 8 bit data at start of buffer }; }; // The buffer_provider_s structure contains functions that can be used // by the effect engine process() function to query and release input // or output audio buffer. // The getBuffer() function is called to retrieve a buffer where data // should read from or written to by process() function. // The releaseBuffer() function MUST be called when the buffer retrieved // with getBuffer() is not needed anymore. // The process function should use the buffer provider mechanism to retrieve // input or output buffer if the inBuffer or outBuffer passed as argument is NULL // and the buffer configuration (buffer_config_t) given by the EFFECT_CMD_SET_CONFIG // command did not specify an audio buffer. typedef int32_t (* buffer_function_t)(void *cookie, audio_buffer_t *buffer); typedef struct buffer_provider_s { buffer_function_t getBuffer; // retrieve next buffer buffer_function_t releaseBuffer; // release used buffer void *cookie; // for use by client of buffer provider functions } buffer_provider_t; // The buffer_config_s structure specifies the input or output audio format // to be used by the effect engine. It is part of the effect_config_t // structure that defines both input and output buffer configurations and is // passed by the EFFECT_CMD_SET_CONFIG or EFFECT_CMD_SET_CONFIG_REVERSE command. typedef struct buffer_config_s { audio_buffer_t buffer; // buffer for use by process() function if not passed explicitly uint32_t samplingRate; // sampling rate uint32_t channels; // channel mask (see audio_channel_mask_t in audio.h) buffer_provider_t bufferProvider; // buffer provider uint8_t format; // Audio format (see audio_format_t in audio.h) uint8_t accessMode; // read/write or accumulate in buffer (effect_buffer_access_e) uint16_t mask; // indicates which of the above fields is valid } buffer_config_t; // Values for "accessMode" field of buffer_config_t: // overwrite, read only, accumulate (read/modify/write) enum effect_buffer_access_e { EFFECT_BUFFER_ACCESS_WRITE, EFFECT_BUFFER_ACCESS_READ, EFFECT_BUFFER_ACCESS_ACCUMULATE }; // feature identifiers for EFFECT_CMD_GET_FEATURE_SUPPORTED_CONFIGS command enum effect_feature_e { EFFECT_FEATURE_AUX_CHANNELS, // supports auxiliary channels (e.g. dual mic noise suppressor) EFFECT_FEATURE_CNT }; // EFFECT_FEATURE_AUX_CHANNELS feature configuration descriptor. Describe a combination // of main and auxiliary channels supported typedef struct channel_config_s { audio_channel_mask_t main_channels; // channel mask for main channels audio_channel_mask_t aux_channels; // channel mask for auxiliary channels } channel_config_t; // Values for bit field "mask" in buffer_config_t. If a bit is set, the corresponding field // in buffer_config_t must be taken into account when executing the EFFECT_CMD_SET_CONFIG command #define EFFECT_CONFIG_BUFFER 0x0001 // buffer field must be taken into account #define EFFECT_CONFIG_SMP_RATE 0x0002 // samplingRate field must be taken into account #define EFFECT_CONFIG_CHANNELS 0x0004 // channels field must be taken into account #define EFFECT_CONFIG_FORMAT 0x0008 // format field must be taken into account #define EFFECT_CONFIG_ACC_MODE 0x0010 // accessMode field must be taken into account #define EFFECT_CONFIG_PROVIDER 0x0020 // bufferProvider field must be taken into account #define EFFECT_CONFIG_ALL (EFFECT_CONFIG_BUFFER | EFFECT_CONFIG_SMP_RATE | \ EFFECT_CONFIG_CHANNELS | EFFECT_CONFIG_FORMAT | \ EFFECT_CONFIG_ACC_MODE | EFFECT_CONFIG_PROVIDER) // effect_config_s structure describes the format of the pCmdData argument of EFFECT_CMD_SET_CONFIG // command to configure audio parameters and buffers for effect engine input and output. typedef struct effect_config_s { buffer_config_t inputCfg; buffer_config_t outputCfg; } effect_config_t; // effect_param_s structure describes the format of the pCmdData argument of EFFECT_CMD_SET_PARAM // command and pCmdData and pReplyData of EFFECT_CMD_GET_PARAM command. // psize and vsize represent the actual size of parameter and value. // // NOTE: the start of value field inside the data field is always on a 32 bit boundary: // // +-----------+ // | status | sizeof(int) // +-----------+ // | psize | sizeof(int) // +-----------+ // | vsize | sizeof(int) // +-----------+ // | | | | // ~ parameter ~ > psize | // | | | > ((psize - 1)/sizeof(int) + 1) * sizeof(int) // +-----------+ | // | padding | | // +-----------+ // | | | // ~ value ~ > vsize // | | | // +-----------+ typedef struct effect_param_s { int32_t status; // Transaction status (unused for command, used for reply) uint32_t psize; // Parameter size uint32_t vsize; // Value size char data[]; // Start of Parameter + Value data } effect_param_t; // structure used by EFFECT_CMD_OFFLOAD command typedef struct effect_offload_param_s { bool isOffload; // true if the playback thread the effect is attached to is offloaded int ioHandle; // io handle of the playback thread the effect is attached to } effect_offload_param_t; ///////////////////////////////////////////////// // Effect library interface ///////////////////////////////////////////////// // Effect library interface version 3.0 // Note that EffectsFactory.c only checks the major version component, so changes to the minor // number can only be used for fully backwards compatible changes #define EFFECT_LIBRARY_API_VERSION EFFECT_MAKE_API_VERSION(3,0) #define AUDIO_EFFECT_LIBRARY_TAG ((('A') << 24) | (('E') << 16) | (('L') << 8) | ('T')) // Every effect library must have a data structure named AUDIO_EFFECT_LIBRARY_INFO_SYM // and the fields of this data structure must begin with audio_effect_library_t typedef struct audio_effect_library_s { // tag must be initialized to AUDIO_EFFECT_LIBRARY_TAG uint32_t tag; // Version of the effect library API : 0xMMMMmmmm MMMM: Major, mmmm: minor uint32_t version; // Name of this library const char *name; // Author/owner/implementor of the library const char *implementor; //////////////////////////////////////////////////////////////////////////////// // // Function: create_effect // // Description: Creates an effect engine of the specified implementation uuid and // returns an effect control interface on this engine. The function will allocate the // resources for an instance of the requested effect engine and return // a handle on the effect control interface. // // Input: // uuid: pointer to the effect uuid. // sessionId: audio session to which this effect instance will be attached. All effects // created with the same session ID are connected in series and process the same signal // stream. Knowing that two effects are part of the same effect chain can help the // library implement some kind of optimizations. // ioId: identifies the output or input stream this effect is directed to at audio HAL. // For future use especially with tunneled HW accelerated effects // // Input/Output: // pHandle: address where to return the effect interface handle. // // Output: // returned value: 0 successful operation. // -ENODEV library failed to initialize // -EINVAL invalid pEffectUuid or pHandle // -ENOENT no effect with this uuid found // *pHandle: updated with the effect interface handle. // //////////////////////////////////////////////////////////////////////////////// int32_t (*create_effect)(const effect_uuid_t *uuid, int32_t sessionId, int32_t ioId, effect_handle_t *pHandle); //////////////////////////////////////////////////////////////////////////////// // // Function: release_effect // // Description: Releases the effect engine whose handle is given as argument. // All resources allocated to this particular instance of the effect are // released. // // Input: // handle: handle on the effect interface to be released. // // Output: // returned value: 0 successful operation. // -ENODEV library failed to initialize // -EINVAL invalid interface handle // //////////////////////////////////////////////////////////////////////////////// int32_t (*release_effect)(effect_handle_t handle); //////////////////////////////////////////////////////////////////////////////// // // Function: get_descriptor // // Description: Returns the descriptor of the effect engine which implementation UUID is // given as argument. // // Input/Output: // uuid: pointer to the effect uuid. // pDescriptor: address where to return the effect descriptor. // // Output: // returned value: 0 successful operation. // -ENODEV library failed to initialize // -EINVAL invalid pDescriptor or uuid // *pDescriptor: updated with the effect descriptor. // //////////////////////////////////////////////////////////////////////////////// int32_t (*get_descriptor)(const effect_uuid_t *uuid, effect_descriptor_t *pDescriptor); } audio_effect_library_t; // Name of the hal_module_info #define AUDIO_EFFECT_LIBRARY_INFO_SYM AELI // Name of the hal_module_info as a string #define AUDIO_EFFECT_LIBRARY_INFO_SYM_AS_STR "AELI" __END_DECLS #endif // ANDROID_AUDIO_EFFECT_H bluez-5.82/android/hardware/PaxHeaders/audio.h0000644000000000000000000000005014015011623016326 xustar0020 atime=1743516862 20 ctime=1743591275 bluez-5.82/android/hardware/audio.h0000644000000000000000000006360414015011623016020 0ustar00rootroot/* SPDX-License-Identifier: Apache-2.0 */ /* * Copyright (C) 2011 The Android Open Source Project * */ #ifndef ANDROID_AUDIO_HAL_INTERFACE_H #define ANDROID_AUDIO_HAL_INTERFACE_H #include #include #include #include #include #include #include __BEGIN_DECLS /** * The id of this module */ #define AUDIO_HARDWARE_MODULE_ID "audio" /** * Name of the audio devices to open */ #define AUDIO_HARDWARE_INTERFACE "audio_hw_if" /* Use version 0.1 to be compatible with first generation of audio hw module with version_major * hardcoded to 1. No audio module API change. */ #define AUDIO_MODULE_API_VERSION_0_1 HARDWARE_MODULE_API_VERSION(0, 1) #define AUDIO_MODULE_API_VERSION_CURRENT AUDIO_MODULE_API_VERSION_0_1 /* First generation of audio devices had version hardcoded to 0. all devices with versions < 1.0 * will be considered of first generation API. */ #define AUDIO_DEVICE_API_VERSION_0_0 HARDWARE_DEVICE_API_VERSION(0, 0) #define AUDIO_DEVICE_API_VERSION_1_0 HARDWARE_DEVICE_API_VERSION(1, 0) #define AUDIO_DEVICE_API_VERSION_2_0 HARDWARE_DEVICE_API_VERSION(2, 0) #define AUDIO_DEVICE_API_VERSION_3_0 HARDWARE_DEVICE_API_VERSION(3, 0) #define AUDIO_DEVICE_API_VERSION_CURRENT AUDIO_DEVICE_API_VERSION_3_0 /* Minimal audio HAL version supported by the audio framework */ #define AUDIO_DEVICE_API_VERSION_MIN AUDIO_DEVICE_API_VERSION_2_0 /** * List of known audio HAL modules. This is the base name of the audio HAL * library composed of the "audio." prefix, one of the base names below and * a suffix specific to the device. * e.g: audio.primary.goldfish.so or audio.a2dp.default.so */ #define AUDIO_HARDWARE_MODULE_ID_PRIMARY "primary" #define AUDIO_HARDWARE_MODULE_ID_A2DP "a2dp" #define AUDIO_HARDWARE_MODULE_ID_USB "usb" #define AUDIO_HARDWARE_MODULE_ID_REMOTE_SUBMIX "r_submix" #define AUDIO_HARDWARE_MODULE_ID_CODEC_OFFLOAD "codec_offload" /**************************************/ /** * standard audio parameters that the HAL may need to handle */ /** * audio device parameters */ /* BT SCO Noise Reduction + Echo Cancellation parameters */ #define AUDIO_PARAMETER_KEY_BT_NREC "bt_headset_nrec" #define AUDIO_PARAMETER_VALUE_ON "on" #define AUDIO_PARAMETER_VALUE_OFF "off" /* TTY mode selection */ #define AUDIO_PARAMETER_KEY_TTY_MODE "tty_mode" #define AUDIO_PARAMETER_VALUE_TTY_OFF "tty_off" #define AUDIO_PARAMETER_VALUE_TTY_VCO "tty_vco" #define AUDIO_PARAMETER_VALUE_TTY_HCO "tty_hco" #define AUDIO_PARAMETER_VALUE_TTY_FULL "tty_full" /* Hearing Aid Compatibility - Telecoil (HAC-T) mode on/off Strings must be in sync with CallFeaturesSetting.java */ #define AUDIO_PARAMETER_KEY_HAC "HACSetting" #define AUDIO_PARAMETER_VALUE_HAC_ON "ON" #define AUDIO_PARAMETER_VALUE_HAC_OFF "OFF" /* A2DP sink address set by framework */ #define AUDIO_PARAMETER_A2DP_SINK_ADDRESS "a2dp_sink_address" /* A2DP source address set by framework */ #define AUDIO_PARAMETER_A2DP_SOURCE_ADDRESS "a2dp_source_address" /* Screen state */ #define AUDIO_PARAMETER_KEY_SCREEN_STATE "screen_state" /* Bluetooth SCO wideband */ #define AUDIO_PARAMETER_KEY_BT_SCO_WB "bt_wbs" /** * audio stream parameters */ #define AUDIO_PARAMETER_STREAM_ROUTING "routing" /* audio_devices_t */ #define AUDIO_PARAMETER_STREAM_FORMAT "format" /* audio_format_t */ #define AUDIO_PARAMETER_STREAM_CHANNELS "channels" /* audio_channel_mask_t */ #define AUDIO_PARAMETER_STREAM_FRAME_COUNT "frame_count" /* size_t */ #define AUDIO_PARAMETER_STREAM_INPUT_SOURCE "input_source" /* audio_source_t */ #define AUDIO_PARAMETER_STREAM_SAMPLING_RATE "sampling_rate" /* uint32_t */ #define AUDIO_PARAMETER_DEVICE_DISCONNECT "disconnect" /* audio_devices_t */ /* Query supported formats. The response is a '|' separated list of strings from * audio_format_t enum e.g: "sup_formats=AUDIO_FORMAT_PCM_16_BIT" */ #define AUDIO_PARAMETER_STREAM_SUP_FORMATS "sup_formats" /* Query supported channel masks. The response is a '|' separated list of strings from * audio_channel_mask_t enum e.g: "sup_channels=AUDIO_CHANNEL_OUT_STEREO|AUDIO_CHANNEL_OUT_MONO" */ #define AUDIO_PARAMETER_STREAM_SUP_CHANNELS "sup_channels" /* Query supported sampling rates. The response is a '|' separated list of integer values e.g: * "sup_sampling_rates=44100|48000" */ #define AUDIO_PARAMETER_STREAM_SUP_SAMPLING_RATES "sup_sampling_rates" /* Get the HW synchronization source used for an output stream. * Return a valid source (positive integer) or AUDIO_HW_SYNC_INVALID if an error occurs * or no HW sync source is used. */ #define AUDIO_PARAMETER_STREAM_HW_AV_SYNC "hw_av_sync" /** * audio codec parameters */ #define AUDIO_OFFLOAD_CODEC_PARAMS "music_offload_codec_param" #define AUDIO_OFFLOAD_CODEC_BIT_PER_SAMPLE "music_offload_bit_per_sample" #define AUDIO_OFFLOAD_CODEC_BIT_RATE "music_offload_bit_rate" #define AUDIO_OFFLOAD_CODEC_AVG_BIT_RATE "music_offload_avg_bit_rate" #define AUDIO_OFFLOAD_CODEC_ID "music_offload_codec_id" #define AUDIO_OFFLOAD_CODEC_BLOCK_ALIGN "music_offload_block_align" #define AUDIO_OFFLOAD_CODEC_SAMPLE_RATE "music_offload_sample_rate" #define AUDIO_OFFLOAD_CODEC_ENCODE_OPTION "music_offload_encode_option" #define AUDIO_OFFLOAD_CODEC_NUM_CHANNEL "music_offload_num_channels" #define AUDIO_OFFLOAD_CODEC_DOWN_SAMPLING "music_offload_down_sampling" #define AUDIO_OFFLOAD_CODEC_DELAY_SAMPLES "delay_samples" #define AUDIO_OFFLOAD_CODEC_PADDING_SAMPLES "padding_samples" /**************************************/ /* common audio stream parameters and operations */ struct audio_stream { /** * Return the sampling rate in Hz - eg. 44100. */ uint32_t (*get_sample_rate)(const struct audio_stream *stream); /* currently unused - use set_parameters with key * AUDIO_PARAMETER_STREAM_SAMPLING_RATE */ int (*set_sample_rate)(struct audio_stream *stream, uint32_t rate); /** * Return size of input/output buffer in bytes for this stream - eg. 4800. * It should be a multiple of the frame size. See also get_input_buffer_size. */ size_t (*get_buffer_size)(const struct audio_stream *stream); /** * Return the channel mask - * e.g. AUDIO_CHANNEL_OUT_STEREO or AUDIO_CHANNEL_IN_STEREO */ audio_channel_mask_t (*get_channels)(const struct audio_stream *stream); /** * Return the audio format - e.g. AUDIO_FORMAT_PCM_16_BIT */ audio_format_t (*get_format)(const struct audio_stream *stream); /* currently unused - use set_parameters with key * AUDIO_PARAMETER_STREAM_FORMAT */ int (*set_format)(struct audio_stream *stream, audio_format_t format); /** * Put the audio hardware input/output into standby mode. * Driver should exit from standby mode at the next I/O operation. * Returns 0 on success and <0 on failure. */ int (*standby)(struct audio_stream *stream); /** dump the state of the audio input/output device */ int (*dump)(const struct audio_stream *stream, int fd); /** Return the set of device(s) which this stream is connected to */ audio_devices_t (*get_device)(const struct audio_stream *stream); /** * Currently unused - set_device() corresponds to set_parameters() with key * AUDIO_PARAMETER_STREAM_ROUTING for both input and output. * AUDIO_PARAMETER_STREAM_INPUT_SOURCE is an additional information used by * input streams only. */ int (*set_device)(struct audio_stream *stream, audio_devices_t device); /** * set/get audio stream parameters. The function accepts a list of * parameter key value pairs in the form: key1=value1;key2=value2;... * * Some keys are reserved for standard parameters (See AudioParameter class) * * If the implementation does not accept a parameter change while * the output is active but the parameter is acceptable otherwise, it must * return -ENOSYS. * * The audio flinger will put the stream in standby and then change the * parameter value. */ int (*set_parameters)(struct audio_stream *stream, const char *kv_pairs); /* * Returns a pointer to a heap allocated string. The caller is responsible * for freeing the memory for it using free(). */ char * (*get_parameters)(const struct audio_stream *stream, const char *keys); int (*add_audio_effect)(const struct audio_stream *stream, effect_handle_t effect); int (*remove_audio_effect)(const struct audio_stream *stream, effect_handle_t effect); }; typedef struct audio_stream audio_stream_t; /* type of asynchronous write callback events. Mutually exclusive */ typedef enum { STREAM_CBK_EVENT_WRITE_READY, /* non blocking write completed */ STREAM_CBK_EVENT_DRAIN_READY /* drain completed */ } stream_callback_event_t; typedef int (*stream_callback_t)(stream_callback_event_t event, void *param, void *cookie); /* type of drain requested to audio_stream_out->drain(). Mutually exclusive */ typedef enum { AUDIO_DRAIN_ALL, /* drain() returns when all data has been played */ AUDIO_DRAIN_EARLY_NOTIFY /* drain() returns a short time before all data from the current track has been played to give time for gapless track switch */ } audio_drain_type_t; /** * audio_stream_out is the abstraction interface for the audio output hardware. * * It provides information about various properties of the audio output * hardware driver. */ struct audio_stream_out { /** * Common methods of the audio stream out. This *must* be the first member of audio_stream_out * as users of this structure will cast a audio_stream to audio_stream_out pointer in contexts * where it's known the audio_stream references an audio_stream_out. */ struct audio_stream common; /** * Return the audio hardware driver estimated latency in milliseconds. */ uint32_t (*get_latency)(const struct audio_stream_out *stream); /** * Use this method in situations where audio mixing is done in the * hardware. This method serves as a direct interface with hardware, * allowing you to directly set the volume as apposed to via the framework. * This method might produce multiple PCM outputs or hardware accelerated * codecs, such as MP3 or AAC. */ int (*set_volume)(struct audio_stream_out *stream, float left, float right); /** * Write audio buffer to driver. Returns number of bytes written, or a * negative status_t. If at least one frame was written successfully prior to the error, * it is suggested that the driver return that successful (short) byte count * and then return an error in the subsequent call. * * If set_callback() has previously been called to enable non-blocking mode * the write() is not allowed to block. It must write only the number of * bytes that currently fit in the driver/hardware buffer and then return * this byte count. If this is less than the requested write size the * callback function must be called when more space is available in the * driver/hardware buffer. */ ssize_t (*write)(struct audio_stream_out *stream, const void* buffer, size_t bytes); /* return the number of audio frames written by the audio dsp to DAC since * the output has exited standby */ int (*get_render_position)(const struct audio_stream_out *stream, uint32_t *dsp_frames); /** * get the local time at which the next write to the audio driver will be presented. * The units are microseconds, where the epoch is decided by the local audio HAL. */ int (*get_next_write_timestamp)(const struct audio_stream_out *stream, int64_t *timestamp); /** * set the callback function for notifying completion of non-blocking * write and drain. * Calling this function implies that all future write() and drain() * must be non-blocking and use the callback to signal completion. */ int (*set_callback)(struct audio_stream_out *stream, stream_callback_t callback, void *cookie); /** * Notifies to the audio driver to stop playback however the queued buffers are * retained by the hardware. Useful for implementing pause/resume. Empty implementation * if not supported however should be implemented for hardware with non-trivial * latency. In the pause state audio hardware could still be using power. User may * consider calling suspend after a timeout. * * Implementation of this function is mandatory for offloaded playback. */ int (*pause)(struct audio_stream_out* stream); /** * Notifies to the audio driver to resume playback following a pause. * Returns error if called without matching pause. * * Implementation of this function is mandatory for offloaded playback. */ int (*resume)(struct audio_stream_out* stream); /** * Requests notification when data buffered by the driver/hardware has * been played. If set_callback() has previously been called to enable * non-blocking mode, the drain() must not block, instead it should return * quickly and completion of the drain is notified through the callback. * If set_callback() has not been called, the drain() must block until * completion. * If type==AUDIO_DRAIN_ALL, the drain completes when all previously written * data has been played. * If type==AUDIO_DRAIN_EARLY_NOTIFY, the drain completes shortly before all * data for the current track has played to allow time for the framework * to perform a gapless track switch. * * Drain must return immediately on stop() and flush() call * * Implementation of this function is mandatory for offloaded playback. */ int (*drain)(struct audio_stream_out* stream, audio_drain_type_t type ); /** * Notifies to the audio driver to flush the queued data. Stream must already * be paused before calling flush(). * * Implementation of this function is mandatory for offloaded playback. */ int (*flush)(struct audio_stream_out* stream); /** * Return a recent count of the number of audio frames presented to an external observer. * This excludes frames which have been written but are still in the pipeline. * The count is not reset to zero when output enters standby. * Also returns the value of CLOCK_MONOTONIC as of this presentation count. * The returned count is expected to be 'recent', * but does not need to be the most recent possible value. * However, the associated time should correspond to whatever count is returned. * Example: assume that N+M frames have been presented, where M is a 'small' number. * Then it is permissible to return N instead of N+M, * and the timestamp should correspond to N rather than N+M. * The terms 'recent' and 'small' are not defined. * They reflect the quality of the implementation. * * 3.0 and higher only. */ int (*get_presentation_position)(const struct audio_stream_out *stream, uint64_t *frames, struct timespec *timestamp); }; typedef struct audio_stream_out audio_stream_out_t; struct audio_stream_in { /** * Common methods of the audio stream in. This *must* be the first member of audio_stream_in * as users of this structure will cast a audio_stream to audio_stream_in pointer in contexts * where it's known the audio_stream references an audio_stream_in. */ struct audio_stream common; /** set the input gain for the audio driver. This method is for * for future use */ int (*set_gain)(struct audio_stream_in *stream, float gain); /** Read audio buffer in from audio driver. Returns number of bytes read, or a * negative status_t. If at least one frame was read prior to the error, * read should return that byte count and then return an error in the subsequent call. */ ssize_t (*read)(struct audio_stream_in *stream, void* buffer, size_t bytes); /** * Return the amount of input frames lost in the audio driver since the * last call of this function. * Audio driver is expected to reset the value to 0 and restart counting * upon returning the current value by this function call. * Such loss typically occurs when the user space process is blocked * longer than the capacity of audio driver buffers. * * Unit: the number of input audio frames */ uint32_t (*get_input_frames_lost)(struct audio_stream_in *stream); }; typedef struct audio_stream_in audio_stream_in_t; /** * return the frame size (number of bytes per sample). * * Deprecated: use audio_stream_out_frame_size() or audio_stream_in_frame_size() instead. */ __attribute__((__deprecated__)) static inline size_t audio_stream_frame_size(const struct audio_stream *s) { size_t chan_samp_sz; audio_format_t format = s->get_format(s); if (audio_is_linear_pcm(format)) { chan_samp_sz = audio_bytes_per_sample(format); return popcount(s->get_channels(s)) * chan_samp_sz; } return sizeof(int8_t); } /** * return the frame size (number of bytes per sample) of an output stream. */ static inline size_t audio_stream_out_frame_size(const struct audio_stream_out *s) { size_t chan_samp_sz; audio_format_t format = s->common.get_format(&s->common); if (audio_is_linear_pcm(format)) { chan_samp_sz = audio_bytes_per_sample(format); return audio_channel_count_from_out_mask(s->common.get_channels(&s->common)) * chan_samp_sz; } return sizeof(int8_t); } /** * return the frame size (number of bytes per sample) of an input stream. */ static inline size_t audio_stream_in_frame_size(const struct audio_stream_in *s) { size_t chan_samp_sz; audio_format_t format = s->common.get_format(&s->common); if (audio_is_linear_pcm(format)) { chan_samp_sz = audio_bytes_per_sample(format); return audio_channel_count_from_in_mask(s->common.get_channels(&s->common)) * chan_samp_sz; } return sizeof(int8_t); } /**********************************************************************/ /** * Every hardware module must have a data structure named HAL_MODULE_INFO_SYM * and the fields of this data structure must begin with hw_module_t * followed by module specific information. */ struct audio_module { struct hw_module_t common; }; struct audio_hw_device { /** * Common methods of the audio device. This *must* be the first member of audio_hw_device * as users of this structure will cast a hw_device_t to audio_hw_device pointer in contexts * where it's known the hw_device_t references an audio_hw_device. */ struct hw_device_t common; /** * used by audio flinger to enumerate what devices are supported by * each audio_hw_device implementation. * * Return value is a bitmask of 1 or more values of audio_devices_t * * NOTE: audio HAL implementations starting with * AUDIO_DEVICE_API_VERSION_2_0 do not implement this function. * All supported devices should be listed in audio_policy.conf * file and the audio policy manager must choose the appropriate * audio module based on information in this file. */ uint32_t (*get_supported_devices)(const struct audio_hw_device *dev); /** * check to see if the audio hardware interface has been initialized. * returns 0 on success, -ENODEV on failure. */ int (*init_check)(const struct audio_hw_device *dev); /** set the audio volume of a voice call. Range is between 0.0 and 1.0 */ int (*set_voice_volume)(struct audio_hw_device *dev, float volume); /** * set the audio volume for all audio activities other than voice call. * Range between 0.0 and 1.0. If any value other than 0 is returned, * the software mixer will emulate this capability. */ int (*set_master_volume)(struct audio_hw_device *dev, float volume); /** * Get the current master volume value for the HAL, if the HAL supports * master volume control. AudioFlinger will query this value from the * primary audio HAL when the service starts and use the value for setting * the initial master volume across all HALs. HALs which do not support * this method may leave it set to NULL. */ int (*get_master_volume)(struct audio_hw_device *dev, float *volume); /** * set_mode is called when the audio mode changes. AUDIO_MODE_NORMAL mode * is for standard audio playback, AUDIO_MODE_RINGTONE when a ringtone is * playing, and AUDIO_MODE_IN_CALL when a call is in progress. */ int (*set_mode)(struct audio_hw_device *dev, audio_mode_t mode); /* mic mute */ int (*set_mic_mute)(struct audio_hw_device *dev, bool state); int (*get_mic_mute)(const struct audio_hw_device *dev, bool *state); /* set/get global audio parameters */ int (*set_parameters)(struct audio_hw_device *dev, const char *kv_pairs); /* * Returns a pointer to a heap allocated string. The caller is responsible * for freeing the memory for it using free(). */ char * (*get_parameters)(const struct audio_hw_device *dev, const char *keys); /* Returns audio input buffer size according to parameters passed or * 0 if one of the parameters is not supported. * See also get_buffer_size which is for a particular stream. */ size_t (*get_input_buffer_size)(const struct audio_hw_device *dev, const struct audio_config *config); /** This method creates and opens the audio hardware output stream. * The "address" parameter qualifies the "devices" audio device type if needed. * The format format depends on the device type: * - Bluetooth devices use the MAC address of the device in the form "00:11:22:AA:BB:CC" * - USB devices use the ALSA card and device numbers in the form "card=X;device=Y" * - Other devices may use a number or any other string. */ int (*open_output_stream)(struct audio_hw_device *dev, audio_io_handle_t handle, audio_devices_t devices, audio_output_flags_t flags, struct audio_config *config, struct audio_stream_out **stream_out, const char *address); void (*close_output_stream)(struct audio_hw_device *dev, struct audio_stream_out* stream_out); /** This method creates and opens the audio hardware input stream */ int (*open_input_stream)(struct audio_hw_device *dev, audio_io_handle_t handle, audio_devices_t devices, struct audio_config *config, struct audio_stream_in **stream_in, audio_input_flags_t flags, const char *address, audio_source_t source); void (*close_input_stream)(struct audio_hw_device *dev, struct audio_stream_in *stream_in); /** This method dumps the state of the audio hardware */ int (*dump)(const struct audio_hw_device *dev, int fd); /** * set the audio mute status for all audio activities. If any value other * than 0 is returned, the software mixer will emulate this capability. */ int (*set_master_mute)(struct audio_hw_device *dev, bool mute); /** * Get the current master mute status for the HAL, if the HAL supports * master mute control. AudioFlinger will query this value from the primary * audio HAL when the service starts and use the value for setting the * initial master mute across all HALs. HALs which do not support this * method may leave it set to NULL. */ int (*get_master_mute)(struct audio_hw_device *dev, bool *mute); /** * Routing control */ /* Creates an audio patch between several source and sink ports. * The handle is allocated by the HAL and should be unique for this * audio HAL module. */ int (*create_audio_patch)(struct audio_hw_device *dev, unsigned int num_sources, const struct audio_port_config *sources, unsigned int num_sinks, const struct audio_port_config *sinks, audio_patch_handle_t *handle); /* Release an audio patch */ int (*release_audio_patch)(struct audio_hw_device *dev, audio_patch_handle_t handle); /* Fills the list of supported attributes for a given audio port. * As input, "port" contains the information (type, role, address etc...) * needed by the HAL to identify the port. * As output, "port" contains possible attributes (sampling rates, formats, * channel masks, gain controllers...) for this port. */ int (*get_audio_port)(struct audio_hw_device *dev, struct audio_port *port); /* Set audio port configuration */ int (*set_audio_port_config)(struct audio_hw_device *dev, const struct audio_port_config *config); }; typedef struct audio_hw_device audio_hw_device_t; /** convenience API for opening and closing a supported device */ static inline int audio_hw_device_open(const struct hw_module_t* module, struct audio_hw_device** device) { return module->methods->open(module, AUDIO_HARDWARE_INTERFACE, (struct hw_device_t**)device); } static inline int audio_hw_device_close(struct audio_hw_device* device) { return device->common.close(&device->common); } __END_DECLS #endif // ANDROID_AUDIO_INTERFACE_H bluez-5.82/android/hardware/PaxHeaders/bt_rc.h0000644000000000000000000000005014015011623016316 xustar0020 atime=1743516862 20 ctime=1743591276 bluez-5.82/android/hardware/bt_rc.h0000644000000000000000000002526514015011623016011 0ustar00rootroot/* SPDX-License-Identifier: Apache-2.0 */ /* * Copyright (C) 2012 The Android Open Source Project * */ #ifndef ANDROID_INCLUDE_BT_RC_H #define ANDROID_INCLUDE_BT_RC_H __BEGIN_DECLS /* Macros */ #define BTRC_MAX_ATTR_STR_LEN 255 #define BTRC_UID_SIZE 8 #define BTRC_MAX_APP_SETTINGS 8 #define BTRC_MAX_FOLDER_DEPTH 4 #define BTRC_MAX_APP_ATTR_SIZE 16 #define BTRC_MAX_ELEM_ATTR_SIZE 7 typedef uint8_t btrc_uid_t[BTRC_UID_SIZE]; typedef enum { BTRC_FEAT_NONE = 0x00, /* AVRCP 1.0 */ BTRC_FEAT_METADATA = 0x01, /* AVRCP 1.3 */ BTRC_FEAT_ABSOLUTE_VOLUME = 0x02, /* Supports TG role and volume sync */ BTRC_FEAT_BROWSE = 0x04, /* AVRCP 1.4 and up, with Browsing support */ } btrc_remote_features_t; typedef enum { BTRC_PLAYSTATE_STOPPED = 0x00, /* Stopped */ BTRC_PLAYSTATE_PLAYING = 0x01, /* Playing */ BTRC_PLAYSTATE_PAUSED = 0x02, /* Paused */ BTRC_PLAYSTATE_FWD_SEEK = 0x03, /* Fwd Seek*/ BTRC_PLAYSTATE_REV_SEEK = 0x04, /* Rev Seek*/ BTRC_PLAYSTATE_ERROR = 0xFF, /* Error */ } btrc_play_status_t; typedef enum { BTRC_EVT_PLAY_STATUS_CHANGED = 0x01, BTRC_EVT_TRACK_CHANGE = 0x02, BTRC_EVT_TRACK_REACHED_END = 0x03, BTRC_EVT_TRACK_REACHED_START = 0x04, BTRC_EVT_PLAY_POS_CHANGED = 0x05, BTRC_EVT_APP_SETTINGS_CHANGED = 0x08, } btrc_event_id_t; typedef enum { BTRC_NOTIFICATION_TYPE_INTERIM = 0, BTRC_NOTIFICATION_TYPE_CHANGED = 1, } btrc_notification_type_t; typedef enum { BTRC_PLAYER_ATTR_EQUALIZER = 0x01, BTRC_PLAYER_ATTR_REPEAT = 0x02, BTRC_PLAYER_ATTR_SHUFFLE = 0x03, BTRC_PLAYER_ATTR_SCAN = 0x04, } btrc_player_attr_t; typedef enum { BTRC_MEDIA_ATTR_TITLE = 0x01, BTRC_MEDIA_ATTR_ARTIST = 0x02, BTRC_MEDIA_ATTR_ALBUM = 0x03, BTRC_MEDIA_ATTR_TRACK_NUM = 0x04, BTRC_MEDIA_ATTR_NUM_TRACKS = 0x05, BTRC_MEDIA_ATTR_GENRE = 0x06, BTRC_MEDIA_ATTR_PLAYING_TIME = 0x07, } btrc_media_attr_t; typedef enum { BTRC_PLAYER_VAL_OFF_REPEAT = 0x01, BTRC_PLAYER_VAL_SINGLE_REPEAT = 0x02, BTRC_PLAYER_VAL_ALL_REPEAT = 0x03, BTRC_PLAYER_VAL_GROUP_REPEAT = 0x04 } btrc_player_repeat_val_t; typedef enum { BTRC_PLAYER_VAL_OFF_SHUFFLE = 0x01, BTRC_PLAYER_VAL_ALL_SHUFFLE = 0x02, BTRC_PLAYER_VAL_GROUP_SHUFFLE = 0x03 } btrc_player_shuffle_val_t; typedef enum { BTRC_STS_BAD_CMD = 0x00, /* Invalid command */ BTRC_STS_BAD_PARAM = 0x01, /* Invalid parameter */ BTRC_STS_NOT_FOUND = 0x02, /* Specified parameter is wrong or not found */ BTRC_STS_INTERNAL_ERR = 0x03, /* Internal Error */ BTRC_STS_NO_ERROR = 0x04 /* Operation Success */ } btrc_status_t; typedef struct { uint8_t num_attr; uint8_t attr_ids[BTRC_MAX_APP_SETTINGS]; uint8_t attr_values[BTRC_MAX_APP_SETTINGS]; } btrc_player_settings_t; typedef union { btrc_play_status_t play_status; btrc_uid_t track; /* queue position in NowPlaying */ uint32_t song_pos; btrc_player_settings_t player_setting; } btrc_register_notification_t; typedef struct { uint8_t id; /* can be attr_id or value_id */ uint8_t text[BTRC_MAX_ATTR_STR_LEN]; } btrc_player_setting_text_t; typedef struct { uint32_t attr_id; uint8_t text[BTRC_MAX_ATTR_STR_LEN]; } btrc_element_attr_val_t; /** Callback for the controller's supported feautres */ typedef void (* btrc_remote_features_callback)(bt_bdaddr_t *bd_addr, btrc_remote_features_t features); /** Callback for play status request */ typedef void (* btrc_get_play_status_callback)(); /** Callback for list player application attributes (Shuffle, Repeat,...) */ typedef void (* btrc_list_player_app_attr_callback)(); /** Callback for list player application attributes (Shuffle, Repeat,...) */ typedef void (* btrc_list_player_app_values_callback)(btrc_player_attr_t attr_id); /** Callback for getting the current player application settings value ** num_attr: specifies the number of attribute ids contained in p_attrs */ typedef void (* btrc_get_player_app_value_callback) (uint8_t num_attr, btrc_player_attr_t *p_attrs); /** Callback for getting the player application settings attributes' text ** num_attr: specifies the number of attribute ids contained in p_attrs */ typedef void (* btrc_get_player_app_attrs_text_callback) (uint8_t num_attr, btrc_player_attr_t *p_attrs); /** Callback for getting the player application settings values' text ** num_attr: specifies the number of value ids contained in p_vals */ typedef void (* btrc_get_player_app_values_text_callback) (uint8_t attr_id, uint8_t num_val, uint8_t *p_vals); /** Callback for setting the player application settings values */ typedef void (* btrc_set_player_app_value_callback) (btrc_player_settings_t *p_vals); /** Callback to fetch the get element attributes of the current song ** num_attr: specifies the number of attributes requested in p_attrs */ typedef void (* btrc_get_element_attr_callback) (uint8_t num_attr, btrc_media_attr_t *p_attrs); /** Callback for register notification (Play state change/track change/...) ** param: Is only valid if event_id is BTRC_EVT_PLAY_POS_CHANGED */ typedef void (* btrc_register_notification_callback) (btrc_event_id_t event_id, uint32_t param); /* AVRCP 1.4 Enhancements */ /** Callback for volume change on CT ** volume: Current volume setting on the CT (0-127) */ typedef void (* btrc_volume_change_callback) (uint8_t volume, uint8_t ctype); /** Callback for passthrough commands */ typedef void (* btrc_passthrough_cmd_callback) (int id, int key_state); /** BT-RC Target callback structure. */ typedef struct { /** set to sizeof(BtRcCallbacks) */ size_t size; btrc_remote_features_callback remote_features_cb; btrc_get_play_status_callback get_play_status_cb; btrc_list_player_app_attr_callback list_player_app_attr_cb; btrc_list_player_app_values_callback list_player_app_values_cb; btrc_get_player_app_value_callback get_player_app_value_cb; btrc_get_player_app_attrs_text_callback get_player_app_attrs_text_cb; btrc_get_player_app_values_text_callback get_player_app_values_text_cb; btrc_set_player_app_value_callback set_player_app_value_cb; btrc_get_element_attr_callback get_element_attr_cb; btrc_register_notification_callback register_notification_cb; btrc_volume_change_callback volume_change_cb; btrc_passthrough_cmd_callback passthrough_cmd_cb; } btrc_callbacks_t; /** Represents the standard BT-RC AVRCP Target interface. */ typedef struct { /** set to sizeof(BtRcInterface) */ size_t size; /** * Register the BtRc callbacks */ bt_status_t (*init)( btrc_callbacks_t* callbacks ); /** Respose to GetPlayStatus request. Contains the current ** 1. Play status ** 2. Song duration/length ** 3. Song position */ bt_status_t (*get_play_status_rsp)( btrc_play_status_t play_status, uint32_t song_len, uint32_t song_pos); /** Lists the support player application attributes (Shuffle/Repeat/...) ** num_attr: Specifies the number of attributes contained in the pointer p_attrs */ bt_status_t (*list_player_app_attr_rsp)( int num_attr, btrc_player_attr_t *p_attrs); /** Lists the support player application attributes (Shuffle Off/On/Group) ** num_val: Specifies the number of values contained in the pointer p_vals */ bt_status_t (*list_player_app_value_rsp)( int num_val, uint8_t *p_vals); /** Returns the current application attribute values for each of the specified attr_id */ bt_status_t (*get_player_app_value_rsp)( btrc_player_settings_t *p_vals); /** Returns the application attributes text ("Shuffle"/"Repeat"/...) ** num_attr: Specifies the number of attributes' text contained in the pointer p_attrs */ bt_status_t (*get_player_app_attr_text_rsp)( int num_attr, btrc_player_setting_text_t *p_attrs); /** Returns the application attributes text ("Shuffle"/"Repeat"/...) ** num_attr: Specifies the number of attribute values' text contained in the pointer p_vals */ bt_status_t (*get_player_app_value_text_rsp)( int num_val, btrc_player_setting_text_t *p_vals); /** Returns the current songs' element attributes text ("Title"/"Album"/"Artist") ** num_attr: Specifies the number of attributes' text contained in the pointer p_attrs */ bt_status_t (*get_element_attr_rsp)( uint8_t num_attr, btrc_element_attr_val_t *p_attrs); /** Response to set player attribute request ("Shuffle"/"Repeat") ** rsp_status: Status of setting the player attributes for the current media player */ bt_status_t (*set_player_app_value_rsp)(btrc_status_t rsp_status); /* Response to the register notification request (Play state change/track change/...). ** event_id: Refers to the event_id this notification change corresponds too ** type: Response type - interim/changed ** p_params: Based on the event_id, this parameter should be populated */ bt_status_t (*register_notification_rsp)(btrc_event_id_t event_id, btrc_notification_type_t type, btrc_register_notification_t *p_param); /* AVRCP 1.4 enhancements */ /**Send current volume setting to remote side. Support limited to SetAbsoluteVolume ** This can be enhanced to support Relative Volume (AVRCP 1.0). ** With RelateVolume, we will send VOLUME_UP/VOLUME_DOWN opposed to absolute volume level ** volume: Should be in the range 0-127. bit7 is reseved and cannot be set */ bt_status_t (*set_volume)(uint8_t volume); /** Closes the interface. */ void (*cleanup)( void ); } btrc_interface_t; typedef void (* btrc_passthrough_rsp_callback) (int id, int key_state); typedef void (* btrc_connection_state_callback) (bool state, bt_bdaddr_t *bd_addr); /** BT-RC Controller callback structure. */ typedef struct { /** set to sizeof(BtRcCallbacks) */ size_t size; btrc_passthrough_rsp_callback passthrough_rsp_cb; btrc_connection_state_callback connection_state_cb; } btrc_ctrl_callbacks_t; /** Represents the standard BT-RC AVRCP Controller interface. */ typedef struct { /** set to sizeof(BtRcInterface) */ size_t size; /** * Register the BtRc callbacks */ bt_status_t (*init)( btrc_ctrl_callbacks_t* callbacks ); /** send pass through command to target */ bt_status_t (*send_pass_through_cmd) ( bt_bdaddr_t *bd_addr, uint8_t key_code, uint8_t key_state ); /** Closes the interface. */ void (*cleanup)( void ); } btrc_ctrl_interface_t; __END_DECLS #endif /* ANDROID_INCLUDE_BT_RC_H */ bluez-5.82/android/hardware/PaxHeaders/hardware.c0000644000000000000000000000005014015011623017015 xustar0020 atime=1743516864 20 ctime=1743591278 bluez-5.82/android/hardware/hardware.c0000644000000000000000000000557314015011623016510 0ustar00rootroot// SPDX-License-Identifier: Apache-2.0 /* * Copyright (C) 2008 The Android Open Source Project * */ #define _GNU_SOURCE #include #include #include #include #include #include #include #define LOG_TAG "HAL" #define LOG_INFO " I" #define LOG_WARN " W" #define LOG_ERROR " E" #define LOG_DEBUG " D" #define ALOG(pri, tag, fmt, arg...) fprintf(stderr, tag pri": " fmt"\n", ##arg) #define info(fmt, arg...) ALOG(LOG_INFO, LOG_TAG, fmt, ##arg) #define warn(fmt, arg...) ALOG(LOG_WARN, LOG_TAG, fmt, ##arg) #define error(fmt, arg...) ALOG(LOG_ERROR, LOG_TAG, fmt, ##arg) /** * Load the file defined by the variant and if successful * return the dlopen handle and the hmi. * @return 0 = success, !0 = failure. */ static int load(const char *id, const char *path, const struct hw_module_t **pHmi) { int status; void *handle; struct hw_module_t *hmi; const char *sym = HAL_MODULE_INFO_SYM_AS_STR; /* * load the symbols resolving undefined symbols before * dlopen returns. Since RTLD_GLOBAL is not or'd in with * RTLD_NOW the external symbols will not be global */ handle = dlopen(path, RTLD_NOW); if (handle == NULL) { char const *err_str = dlerror(); error("load: module=%s\n%s", path, err_str?err_str:"unknown"); status = -EINVAL; goto done; } /* Get the address of the struct hal_module_info. */ hmi = (struct hw_module_t *)dlsym(handle, sym); if (hmi == NULL) { error("load: couldn't find symbol %s", sym); status = -EINVAL; goto done; } /* Check that the id matches */ if (strcmp(id, hmi->id) != 0) { error("load: id=%s != hmi->id=%s", id, hmi->id); status = -EINVAL; goto done; } hmi->dso = handle; *pHmi = hmi; info("loaded HAL id=%s path=%s hmi=%p handle=%p", id, path, *pHmi, handle); return 0; done: hmi = NULL; if (handle != NULL) { dlclose(handle); handle = NULL; } return status; } int hw_get_module_by_class(const char *class_id, const char *inst, const struct hw_module_t **module) { char path[PATH_MAX]; char name[PATH_MAX/2]; if (inst) snprintf(name, sizeof(name), "%s.%s", class_id, inst); else snprintf(name, sizeof(name), "%s", class_id); /* * Here we rely on the fact that calling dlopen multiple times on * the same .so will simply increment a refcount (and not load * a new copy of the library). * We also assume that dlopen() is thread-safe. */ snprintf(path, sizeof(path), "%s/%s.default.so", PLUGINDIR, name); return load(class_id, path, module); } int hw_get_module(const char *id, const struct hw_module_t **module) { return hw_get_module_by_class(id, NULL, module); } bluez-5.82/android/hardware/PaxHeaders/bt_gatt_types.h0000644000000000000000000000005014015011623020075 xustar0020 atime=1743516862 20 ctime=1743591276 bluez-5.82/android/hardware/bt_gatt_types.h0000644000000000000000000000162214015011623017557 0ustar00rootroot/* SPDX-License-Identifier: Apache-2.0 */ /* * Copyright (C) 2013 The Android Open Source Project * */ #ifndef ANDROID_INCLUDE_BT_GATT_TYPES_H #define ANDROID_INCLUDE_BT_GATT_TYPES_H #include #include __BEGIN_DECLS /** * GATT Service types */ #define BTGATT_SERVICE_TYPE_PRIMARY 0 #define BTGATT_SERVICE_TYPE_SECONDARY 1 /** GATT ID adding instance id tracking to the UUID */ typedef struct { bt_uuid_t uuid; uint8_t inst_id; } btgatt_gatt_id_t; /** GATT Service ID also identifies the service type (primary/secondary) */ typedef struct { btgatt_gatt_id_t id; uint8_t is_primary; } btgatt_srvc_id_t; /** Preferred physical Transport for GATT connection */ typedef enum { GATT_TRANSPORT_AUTO, GATT_TRANSPORT_BREDR, GATT_TRANSPORT_LE } btgatt_transport_t; __END_DECLS #endif /* ANDROID_INCLUDE_BT_GATT_TYPES_H */ bluez-5.82/android/hardware/PaxHeaders/bt_gatt_server.h0000644000000000000000000000005014015011623020237 xustar0020 atime=1743516862 20 ctime=1743591276 bluez-5.82/android/hardware/bt_gatt_server.h0000644000000000000000000001632114015011623017723 0ustar00rootroot/* SPDX-License-Identifier: Apache-2.0 */ /* * Copyright (C) 2013 The Android Open Source Project * */ #ifndef ANDROID_INCLUDE_BT_GATT_SERVER_H #define ANDROID_INCLUDE_BT_GATT_SERVER_H #include #include "bt_gatt_types.h" __BEGIN_DECLS /** GATT value type used in response to remote read requests */ typedef struct { uint8_t value[BTGATT_MAX_ATTR_LEN]; uint16_t handle; uint16_t offset; uint16_t len; uint8_t auth_req; } btgatt_value_t; /** GATT remote read request response type */ typedef union { btgatt_value_t attr_value; uint16_t handle; } btgatt_response_t; /** BT-GATT Server callback structure. */ /** Callback invoked in response to register_server */ typedef void (*register_server_callback)(int status, int server_if, bt_uuid_t *app_uuid); /** Callback indicating that a remote device has connected or been disconnected */ typedef void (*connection_callback)(int conn_id, int server_if, int connected, bt_bdaddr_t *bda); /** Callback invoked in response to create_service */ typedef void (*service_added_callback)(int status, int server_if, btgatt_srvc_id_t *srvc_id, int srvc_handle); /** Callback indicating that an included service has been added to a service */ typedef void (*included_service_added_callback)(int status, int server_if, int srvc_handle, int incl_srvc_handle); /** Callback invoked when a characteristic has been added to a service */ typedef void (*characteristic_added_callback)(int status, int server_if, bt_uuid_t *uuid, int srvc_handle, int char_handle); /** Callback invoked when a descriptor has been added to a characteristic */ typedef void (*descriptor_added_callback)(int status, int server_if, bt_uuid_t *uuid, int srvc_handle, int descr_handle); /** Callback invoked in response to start_service */ typedef void (*service_started_callback)(int status, int server_if, int srvc_handle); /** Callback invoked in response to stop_service */ typedef void (*service_stopped_callback)(int status, int server_if, int srvc_handle); /** Callback triggered when a service has been deleted */ typedef void (*service_deleted_callback)(int status, int server_if, int srvc_handle); /** * Callback invoked when a remote device has requested to read a characteristic * or descriptor. The application must respond by calling send_response */ typedef void (*request_read_callback)(int conn_id, int trans_id, bt_bdaddr_t *bda, int attr_handle, int offset, bool is_long); /** * Callback invoked when a remote device has requested to write to a * characteristic or descriptor. */ typedef void (*request_write_callback)(int conn_id, int trans_id, bt_bdaddr_t *bda, int attr_handle, int offset, int length, bool need_rsp, bool is_prep, uint8_t* value); /** Callback invoked when a previously prepared write is to be executed */ typedef void (*request_exec_write_callback)(int conn_id, int trans_id, bt_bdaddr_t *bda, int exec_write); /** * Callback triggered in response to send_response if the remote device * sends a confirmation. */ typedef void (*response_confirmation_callback)(int status, int handle); /** * Callback confirming that a notification or indication has been sent * to a remote device. */ typedef void (*indication_sent_callback)(int conn_id, int status); /** * Callback notifying an application that a remote device connection is currently congested * and cannot receive any more data. An application should avoid sending more data until * a further callback is received indicating the congestion status has been cleared. */ typedef void (*congestion_callback)(int conn_id, bool congested); /** Callback invoked when the MTU for a given connection changes */ typedef void (*mtu_changed_callback)(int conn_id, int mtu); typedef struct { register_server_callback register_server_cb; connection_callback connection_cb; service_added_callback service_added_cb; included_service_added_callback included_service_added_cb; characteristic_added_callback characteristic_added_cb; descriptor_added_callback descriptor_added_cb; service_started_callback service_started_cb; service_stopped_callback service_stopped_cb; service_deleted_callback service_deleted_cb; request_read_callback request_read_cb; request_write_callback request_write_cb; request_exec_write_callback request_exec_write_cb; response_confirmation_callback response_confirmation_cb; indication_sent_callback indication_sent_cb; congestion_callback congestion_cb; mtu_changed_callback mtu_changed_cb; } btgatt_server_callbacks_t; /** Represents the standard BT-GATT server interface. */ typedef struct { /** Registers a GATT server application with the stack */ bt_status_t (*register_server)( bt_uuid_t *uuid ); /** Unregister a server application from the stack */ bt_status_t (*unregister_server)(int server_if ); /** Create a connection to a remote peripheral */ bt_status_t (*connect)(int server_if, const bt_bdaddr_t *bd_addr, bool is_direct, int transport); /** Disconnect an established connection or cancel a pending one */ bt_status_t (*disconnect)(int server_if, const bt_bdaddr_t *bd_addr, int conn_id ); /** Create a new service */ bt_status_t (*add_service)( int server_if, btgatt_srvc_id_t *srvc_id, int num_handles); /** Assign an included service to it's parent service */ bt_status_t (*add_included_service)( int server_if, int service_handle, int included_handle); /** Add a characteristic to a service */ bt_status_t (*add_characteristic)( int server_if, int service_handle, bt_uuid_t *uuid, int properties, int permissions); /** Add a descriptor to a given service */ bt_status_t (*add_descriptor)(int server_if, int service_handle, bt_uuid_t *uuid, int permissions); /** Starts a local service */ bt_status_t (*start_service)(int server_if, int service_handle, int transport); /** Stops a local service */ bt_status_t (*stop_service)(int server_if, int service_handle); /** Delete a local service */ bt_status_t (*delete_service)(int server_if, int service_handle); /** Send value indication to a remote device */ bt_status_t (*send_indication)(int server_if, int attribute_handle, int conn_id, int len, int confirm, char* p_value); /** Send a response to a read/write operation */ bt_status_t (*send_response)(int conn_id, int trans_id, int status, btgatt_response_t *response); } btgatt_server_interface_t; __END_DECLS #endif /* ANDROID_INCLUDE_BT_GATT_CLIENT_H */ bluez-5.82/android/PaxHeaders/pts-pan.txt0000644000000000000000000000005012537515745015427 xustar0020 atime=1743516876 20 ctime=1743591289 bluez-5.82/android/pts-pan.txt0000644000000000000000000000436212537515745015115 0ustar00rootrootPTS test results for PAN PTS version: 6.1 Tested: 11-May-2015 Android version: 5.1 Results: PASS test passed FAIL test failed INC test is inconclusive N/A test is disabled due to PICS setup -------------------------------------------------------------------------------- Test Name Result Notes -------------------------------------------------------------------------------- TC_BNEP_BROADCAST_0_BV_01_C N/A TC_BNEP_BROADCAST_0_BV_02_C N/A TC_BNEP_MULTICAST_0_BV_03_C N/A TC_BNEP_MULTICAST_0_BV_04_C N/A TC_BNEP_FORWARD_UNICAST_BV_05_C N/A TC_BNEP_FORWARD_UNICAST_BV_06_C N/A TC_BNEP_EXTENSION_0_BV_07_C_TESTER_1 N/A TC_BNEP_EXTENSION_0_BV_07_C_TESTER_2 N/A TC_BNEP_FORWARD_BV_08_C_TESTER_1 N/A TC_BNEP_FORWARD_BV_08_C_TESTER_2 N/A TC_BNEP_FORWARD_BROADCAST_BV_09_C_TESTER_1 N/A TC_BNEP_FORWARD_BROADCAST_BV_09_C_TESTER_2 N/A TC_BNEP_FORWARD_BROADCAST_BV_09_C_TESTER_3 N/A TC_BNEP_FILTER_BV_10_C_TESTER_1 N/A TC_BNEP_FILTER_BV_10_C_TESTER_2 N/A TC_BNEP_FILTER_BV_11_C_TESTER_1 N/A TC_BNEP_FILTER_BV_11_C_TESTER_2 N/A TC_BNEP_FILTER_BV_12_C_TESTER_1 N/A TC_BNEP_FILTER_BV_12_C_TESTER_2 N/A TC_BNEP_FILTER_BV_13_C_TESTER_1 N/A TC_BNEP_FILTER_BV_13_C_TESTER_2 N/A TC_BNEP_FILTER_BV_14_C_TESTER_1 N/A TC_BNEP_FILTER_BV_14_C_TESTER_2 N/A TC_BNEP_FILTER_BV_15_C_TESTER_1 N/A TC_BNEP_FILTER_BV_15_C_TESTER_2 N/A TC_BRIDGE_TX_BV_01_I PASS TC_BRIDGE_RX_BV_02_I PASS To initiate general ethernet use for e.g. ping. TC_IPv4_AUTONET_BV_01_I PASS To initiate general ethernet use for e.g. ping. TC_IPv6_AUTONET_BV_02_I N/A TC_IP_DHCP_BV_03_I PASS TC_IP_LLMNR_BV_01_I N/A TC_IP_LLMNR_BV_02_I N/A TC_IP_DNS_BV_01_I N/A TC_IP_APP_BV_03_I N/A TC_IP_APP_BV_05_I INC PTS issue #13436 ip neighbour add lladdr dev bt-pan Note: ARP record should be add immediately after establish of bt-pan, because PTS treat other than ICMP frames as error. TC_MISC_ROLE_BV_01_C N/A TC_MISC_ROLE_BV_02_C N/A TC_MISC_UUID_BV_01_C PASS TC_MISC_UUID_BV_02_C PASS TC_SDP_NAP_BV_01_C PASS TC_SDP_GN_BV_01_C N/A TC_SDP_PANU_BV_01_C N/A -------------------------------------------------------------------------------- bluez-5.82/android/PaxHeaders/bluetooth.c0000644000000000000000000000005014131623652015442 xustar0020 atime=1743516865 20 ctime=1743591278 bluez-5.82/android/bluetooth.c0000644000000000000000000036740214131623652015137 0ustar00rootroot// SPDX-License-Identifier: LGPL-2.1-or-later /* * * BlueZ - Bluetooth protocol stack for Linux * * Copyright (C) 2013-2014 Intel Corporation. All rights reserved. * * */ #ifdef HAVE_CONFIG_H #include #endif #include #include #include #include #include #include #include #include #include "lib/bluetooth.h" #include "lib/sdp.h" #include "lib/mgmt.h" #include "lib/uuid.h" #include "src/shared/util.h" #include "src/shared/mgmt.h" #include "src/shared/queue.h" #include "src/shared/ad.h" #include "src/eir.h" #include "lib/sdp.h" #include "lib/sdp_lib.h" #include "src/sdp-client.h" #include "src/sdpd.h" #include "src/log.h" #include "hal-msg.h" #include "ipc-common.h" #include "ipc.h" #include "utils.h" #include "bluetooth.h" #define DUT_MODE_FILE "/sys/kernel/debug/bluetooth/hci%u/dut_mode" #define SETTINGS_FILE ANDROID_STORAGEDIR"/settings" #define DEVICES_FILE ANDROID_STORAGEDIR"/devices" #define CACHE_FILE ANDROID_STORAGEDIR"/cache" #define ADAPTER_MAJOR_CLASS 0x02 /* Phone */ #define ADAPTER_MINOR_CLASS 0x03 /* Smartphone */ /* Default to DisplayYesNo */ #define DEFAULT_IO_CAPABILITY 0x01 /* Default discoverable timeout 120sec as in Android */ #define DEFAULT_DISCOVERABLE_TIMEOUT 120 #define DEVICES_CACHE_MAX 300 #define BASELEN_PROP_CHANGED (sizeof(struct hal_ev_adapter_props_changed) \ + sizeof(struct hal_property)) #define BASELEN_REMOTE_DEV_PROP (sizeof(struct hal_ev_remote_device_props) \ + sizeof(struct hal_property)) #define SCAN_TYPE_NONE 0 #define SCAN_TYPE_BREDR (1 << BDADDR_BREDR) #define SCAN_TYPE_LE ((1 << BDADDR_LE_PUBLIC) | (1 << BDADDR_LE_RANDOM)) #define SCAN_TYPE_DUAL (SCAN_TYPE_BREDR | SCAN_TYPE_LE) #define BDADDR_LE (BDADDR_LE_RANDOM | BDADDR_LE_PUBLIC) struct device { bdaddr_t bdaddr; uint8_t bdaddr_type; bdaddr_t rpa; uint8_t rpa_type; bool le; bool bredr; bool pairing; bool bredr_paired; bool bredr_bonded; bool le_paired; bool le_bonded; bool in_white_list; bool connected; char *name; char *friendly_name; uint32_t class; int32_t rssi; time_t bredr_seen; time_t le_seen; GSList *uuids; bool found; /* if device is found in current discovery session */ unsigned int confirm_id; /* mgtm command id if command pending */ bool valid_remote_csrk; bool remote_csrk_auth; uint8_t remote_csrk[16]; uint32_t remote_sign_cnt; bool valid_local_csrk; bool local_csrk_auth; uint8_t local_csrk[16]; uint32_t local_sign_cnt; uint16_t gatt_ccc; }; struct browse_req { bdaddr_t bdaddr; GSList *uuids; int search_uuid; int reconnect_attempt; }; static struct { uint16_t index; bdaddr_t bdaddr; uint32_t dev_class; char *name; uint8_t max_advert_instance; uint8_t rpa_offload_supported; uint8_t max_irk_list_size; uint8_t max_scan_filters_supported; uint16_t scan_result_storage_size; uint8_t activity_energy_info_supported; uint32_t current_settings; uint32_t supported_settings; bool le_scanning; uint8_t cur_discovery_type; uint8_t exp_discovery_type; uint32_t discoverable_timeout; GSList *uuids; } adapter = { .index = MGMT_INDEX_NONE, .dev_class = 0, .name = NULL, .max_advert_instance = 0, .rpa_offload_supported = 0, .max_irk_list_size = 0, .max_scan_filters_supported = 0, .scan_result_storage_size = 0, .activity_energy_info_supported = 0, .current_settings = 0, .supported_settings = 0, .cur_discovery_type = SCAN_TYPE_NONE, .exp_discovery_type = SCAN_TYPE_NONE, .discoverable_timeout = DEFAULT_DISCOVERABLE_TIMEOUT, .uuids = NULL, }; static const uint16_t uuid_list[] = { L2CAP_UUID, PNP_INFO_SVCLASS_ID, PUBLIC_BROWSE_GROUP, 0 }; static uint16_t option_index = MGMT_INDEX_NONE; static struct mgmt *mgmt_if = NULL; static GSList *bonded_devices = NULL; static GSList *cached_devices = NULL; static bt_le_device_found gatt_device_found_cb = NULL; static bt_le_discovery_stopped gatt_discovery_stopped_cb = NULL; /* This list contains addresses which are asked for records */ static GSList *browse_reqs; static struct ipc *hal_ipc = NULL; static bool kernel_conn_control = false; static struct queue *unpaired_cb_list = NULL; static struct queue *paired_cb_list = NULL; static void get_device_android_addr(struct device *dev, uint8_t *addr) { /* * If RPA is set it means that IRK was received and ID address is being * used. Android Framework is still using old RPA and it needs to be * used in notifications. */ if (bacmp(&dev->rpa, BDADDR_ANY)) bdaddr2android(&dev->rpa, addr); else bdaddr2android(&dev->bdaddr, addr); } static void mgmt_debug(const char *str, void *user_data) { const char *prefix = user_data; info("%s%s", prefix, str); } static void store_adapter_config(void) { GKeyFile *key_file; gsize length = 0; char addr[18]; char *data; key_file = g_key_file_new(); g_key_file_load_from_file(key_file, SETTINGS_FILE, 0, NULL); ba2str(&adapter.bdaddr, addr); g_key_file_set_string(key_file, "General", "Address", addr); if (adapter.name) g_key_file_set_string(key_file, "General", "Name", adapter.name); g_key_file_set_integer(key_file, "General", "DiscoverableTimeout", adapter.discoverable_timeout); data = g_key_file_to_data(key_file, &length, NULL); g_file_set_contents(SETTINGS_FILE, data, length, NULL); g_free(data); g_key_file_free(key_file); } static void load_adapter_config(void) { GError *gerr = NULL; GKeyFile *key_file; char *str; key_file = g_key_file_new(); g_key_file_load_from_file(key_file, SETTINGS_FILE, 0, NULL); str = g_key_file_get_string(key_file, "General", "Address", NULL); if (!str) { g_key_file_free(key_file); return; } str2ba(str, &adapter.bdaddr); g_free(str); adapter.name = g_key_file_get_string(key_file, "General", "Name", NULL); adapter.discoverable_timeout = g_key_file_get_integer(key_file, "General", "DiscoverableTimeout", &gerr); if (gerr) { adapter.discoverable_timeout = DEFAULT_DISCOVERABLE_TIMEOUT; g_clear_error(&gerr); } g_key_file_free(key_file); } static void store_device_info(struct device *dev, const char *path) { GKeyFile *key_file; char addr[18]; gsize length = 0; char **uuids = NULL; char *str; ba2str(&dev->bdaddr, addr); key_file = g_key_file_new(); g_key_file_load_from_file(key_file, path, 0, NULL); g_key_file_set_boolean(key_file, addr, "BREDR", dev->bredr); if (dev->le) g_key_file_set_integer(key_file, addr, "AddressType", dev->bdaddr_type); g_key_file_set_string(key_file, addr, "Name", dev->name); if (dev->friendly_name) g_key_file_set_string(key_file, addr, "FriendlyName", dev->friendly_name); else g_key_file_remove_key(key_file, addr, "FriendlyName", NULL); if (dev->class) g_key_file_set_integer(key_file, addr, "Class", dev->class); else g_key_file_remove_key(key_file, addr, "Class", NULL); if (dev->bredr_seen > dev->le_seen) g_key_file_set_integer(key_file, addr, "Timestamp", dev->bredr_seen); else g_key_file_set_integer(key_file, addr, "Timestamp", dev->le_seen); if (dev->uuids) { GSList *l; int i; uuids = g_new0(char *, g_slist_length(dev->uuids) + 1); for (i = 0, l = dev->uuids; l; l = g_slist_next(l), i++) { int j; uint8_t *u = l->data; char *uuid_str = g_malloc0(33); for (j = 0; j < 16; j++) sprintf(uuid_str + (j * 2), "%2.2X", u[j]); uuids[i] = uuid_str; } g_key_file_set_string_list(key_file, addr, "Services", (const char **)uuids, i); } else { g_key_file_remove_key(key_file, addr, "Services", NULL); } str = g_key_file_to_data(key_file, &length, NULL); g_file_set_contents(path, str, length, NULL); g_free(str); g_key_file_free(key_file); g_strfreev(uuids); } static void remove_device_info(struct device *dev, const char *path) { GKeyFile *key_file; gsize length = 0; char addr[18]; char *str; ba2str(&dev->bdaddr, addr); key_file = g_key_file_new(); g_key_file_load_from_file(key_file, path, 0, NULL); g_key_file_remove_group(key_file, addr, NULL); str = g_key_file_to_data(key_file, &length, NULL); g_file_set_contents(path, str, length, NULL); g_free(str); g_key_file_free(key_file); } static int device_match(gconstpointer a, gconstpointer b) { const struct device *dev = a; const bdaddr_t *bdaddr = b; /* Android is using RPA even if IRK was received and ID addr resolved */ if (!bacmp(&dev->rpa, bdaddr)) return 0; return bacmp(&dev->bdaddr, bdaddr); } static struct device *find_device(const bdaddr_t *bdaddr) { GSList *l; l = g_slist_find_custom(bonded_devices, bdaddr, device_match); if (l) return l->data; l = g_slist_find_custom(cached_devices, bdaddr, device_match); if (l) return l->data; return NULL; } static void free_device(struct device *dev) { if (dev->confirm_id) mgmt_cancel(mgmt_if, dev->confirm_id); g_free(dev->name); g_free(dev->friendly_name); g_slist_free_full(dev->uuids, g_free); g_free(dev); } static void cache_device(struct device *new_dev) { struct device *dev; GSList *l; l = g_slist_find(cached_devices, new_dev); if (l) { cached_devices = g_slist_remove(cached_devices, new_dev); goto cache; } if (g_slist_length(cached_devices) < DEVICES_CACHE_MAX) goto cache; l = g_slist_last(cached_devices); dev = l->data; cached_devices = g_slist_remove(cached_devices, dev); remove_device_info(dev, CACHE_FILE); free_device(dev); cache: cached_devices = g_slist_prepend(cached_devices, new_dev); store_device_info(new_dev, CACHE_FILE); } static struct device *create_device(const bdaddr_t *bdaddr, uint8_t bdaddr_type) { struct device *dev; char addr[18]; ba2str(bdaddr, addr); DBG("%s", addr); dev = g_new0(struct device, 1); bacpy(&dev->bdaddr, bdaddr); if (bdaddr_type == BDADDR_BREDR) { dev->bredr = true; dev->bredr_seen = time(NULL); } else { dev->le = true; dev->bdaddr_type = bdaddr_type; dev->le_seen = time(NULL); } /* * Use address for name, will be change if one is present * eg. in EIR or set by set_property. */ dev->name = g_strdup(addr); return dev; } static struct device *get_device(const bdaddr_t *bdaddr, uint8_t type) { struct device *dev; dev = find_device(bdaddr); if (dev) return dev; dev = create_device(bdaddr, type); cache_device(dev); return dev; } static struct device *find_device_android(const uint8_t *addr) { bdaddr_t bdaddr; android2bdaddr(addr, &bdaddr); return find_device(&bdaddr); } static struct device *get_device_android(const uint8_t *addr) { bdaddr_t bdaddr; android2bdaddr(addr, &bdaddr); return get_device(&bdaddr, BDADDR_BREDR); } static void send_adapter_property(uint8_t type, uint16_t len, const void *val) { uint8_t buf[BASELEN_PROP_CHANGED + len]; struct hal_ev_adapter_props_changed *ev = (void *) buf; ev->status = HAL_STATUS_SUCCESS; ev->num_props = 1; ev->props[0].type = type; ev->props[0].len = len; memcpy(ev->props[0].val, val, len); ipc_send_notif(hal_ipc, HAL_SERVICE_ID_BLUETOOTH, HAL_EV_ADAPTER_PROPS_CHANGED, sizeof(buf), buf); } static void adapter_name_changed(const uint8_t *name) { /* Android expects string value without NULL terminator */ send_adapter_property(HAL_PROP_ADAPTER_NAME, strlen((const char *) name), name); } static void adapter_set_name(const uint8_t *name) { if (!g_strcmp0(adapter.name, (const char *) name)) return; DBG("%s", name); g_free(adapter.name); adapter.name = g_strdup((const char *) name); store_adapter_config(); adapter_name_changed(name); } static void mgmt_local_name_changed_event(uint16_t index, uint16_t length, const void *param, void *user_data) { const struct mgmt_cp_set_local_name *rp = param; if (length < sizeof(*rp)) { error("Wrong size of local name changed parameters"); return; } adapter_set_name(rp->name); /* TODO Update services if needed */ } static void powered_changed(void) { struct hal_ev_adapter_state_changed ev; ev.state = (adapter.current_settings & MGMT_SETTING_POWERED) ? HAL_POWER_ON : HAL_POWER_OFF; DBG("%u", ev.state); ipc_send_notif(hal_ipc, HAL_SERVICE_ID_BLUETOOTH, HAL_EV_ADAPTER_STATE_CHANGED, sizeof(ev), &ev); } static uint8_t settings2scan_mode(void) { bool connectable, discoverable; connectable = adapter.current_settings & MGMT_SETTING_CONNECTABLE; discoverable = adapter.current_settings & MGMT_SETTING_DISCOVERABLE; if (connectable && discoverable) return HAL_ADAPTER_SCAN_MODE_CONN_DISC; if (connectable) return HAL_ADAPTER_SCAN_MODE_CONN; return HAL_ADAPTER_SCAN_MODE_NONE; } static void scan_mode_changed(void) { uint8_t mode; mode = settings2scan_mode(); DBG("mode %u", mode); send_adapter_property(HAL_PROP_ADAPTER_SCAN_MODE, sizeof(mode), &mode); } static void adapter_class_changed(void) { send_adapter_property(HAL_PROP_ADAPTER_CLASS, sizeof(adapter.dev_class), &adapter.dev_class); } static void settings_changed(uint32_t settings) { uint32_t changed_mask; uint32_t scan_mode_mask; changed_mask = adapter.current_settings ^ settings; adapter.current_settings = settings; DBG("0x%08x", changed_mask); if (changed_mask & MGMT_SETTING_POWERED) powered_changed(); scan_mode_mask = MGMT_SETTING_CONNECTABLE | MGMT_SETTING_DISCOVERABLE; /* * Only when powered, the connectable and discoverable * state changes should be communicated. */ if (adapter.current_settings & MGMT_SETTING_POWERED) if (changed_mask & scan_mode_mask) scan_mode_changed(); } static void new_settings_callback(uint16_t index, uint16_t length, const void *param, void *user_data) { uint32_t settings; if (length < sizeof(settings)) { error("Wrong size of new settings parameters"); return; } settings = get_le32(param); DBG("settings: 0x%8.8x -> 0x%8.8x", adapter.current_settings, settings); if (settings == adapter.current_settings) return; settings_changed(settings); } static void mgmt_dev_class_changed_event(uint16_t index, uint16_t length, const void *param, void *user_data) { const struct mgmt_cod *rp = param; uint32_t dev_class; if (length < sizeof(*rp)) { error("Wrong size of class of device changed parameters"); return; } dev_class = rp->val[0] | (rp->val[1] << 8) | (rp->val[2] << 16); if (dev_class == adapter.dev_class) return; DBG("Class: 0x%06x", dev_class); adapter.dev_class = dev_class; adapter_class_changed(); /* TODO: Gatt attrib set*/ } void bt_store_gatt_ccc(const bdaddr_t *dst, uint16_t value) { struct device *dev; GKeyFile *key_file; gsize length = 0; char addr[18]; char *data; dev = find_device(dst); if (!dev) return; key_file = g_key_file_new(); if (!g_key_file_load_from_file(key_file, DEVICES_FILE, 0, NULL)) { g_key_file_free(key_file); return; } ba2str(&dev->bdaddr, addr); DBG("%s Gatt CCC %d", addr, value); g_key_file_set_integer(key_file, addr, "GattCCC", value); data = g_key_file_to_data(key_file, &length, NULL); g_file_set_contents(DEVICES_FILE, data, length, NULL); g_free(data); g_key_file_free(key_file); dev->gatt_ccc = value; } uint16_t bt_get_gatt_ccc(const bdaddr_t *addr) { struct device *dev; dev = find_device(addr); if (!dev) return 0; return dev->gatt_ccc; } static void store_link_key(const bdaddr_t *dst, const uint8_t *key, uint8_t type, uint8_t pin_length) { GKeyFile *key_file; char key_str[33]; gsize length = 0; char addr[18]; char *data; int i; key_file = g_key_file_new(); if (!g_key_file_load_from_file(key_file, DEVICES_FILE, 0, NULL)) { g_key_file_free(key_file); return; } ba2str(dst, addr); DBG("%s type %u pin_len %u", addr, type, pin_length); for (i = 0; i < 16; i++) sprintf(key_str + (i * 2), "%2.2X", key[i]); g_key_file_set_string(key_file, addr, "LinkKey", key_str); g_key_file_set_integer(key_file, addr, "LinkKeyType", type); g_key_file_set_integer(key_file, addr, "LinkKeyPinLength", pin_length); data = g_key_file_to_data(key_file, &length, NULL); g_file_set_contents(DEVICES_FILE, data, length, NULL); g_free(data); g_key_file_free(key_file); } static void send_bond_state_change(struct device *dev, uint8_t status, uint8_t state) { struct hal_ev_bond_state_changed ev; ev.status = status; ev.state = state; get_device_android_addr(dev, ev.bdaddr); ipc_send_notif(hal_ipc, HAL_SERVICE_ID_BLUETOOTH, HAL_EV_BOND_STATE_CHANGED, sizeof(ev), &ev); } static void update_bredr_state(struct device *dev, bool pairing, bool paired, bool bonded) { if (pairing == dev->pairing && paired == dev->bredr_paired && bonded == dev->bredr_bonded) return; /* avoid unpairing device on incoming pairing request */ if (pairing && dev->bredr_paired) goto done; /* avoid unpairing device if pairing failed */ if (!pairing && !paired && dev->pairing && dev->bredr_paired) goto done; if (paired && !dev->le_paired && !dev->bredr_paired) { cached_devices = g_slist_remove(cached_devices, dev); bonded_devices = g_slist_prepend(bonded_devices, dev); remove_device_info(dev, CACHE_FILE); store_device_info(dev, DEVICES_FILE); } else if (!paired && !dev->le_paired) { bonded_devices = g_slist_remove(bonded_devices, dev); remove_device_info(dev, DEVICES_FILE); cache_device(dev); } dev->bredr_paired = paired; if (dev->bredr_paired) dev->bredr_bonded = dev->bredr_bonded || bonded; else dev->bredr_bonded = false; done: dev->pairing = pairing; } static void update_le_state(struct device *dev, bool pairing, bool paired, bool bonded) { if (pairing == dev->pairing && paired == dev->le_paired && bonded == dev->le_bonded) return; /* avoid unpairing device on incoming pairing request */ if (pairing && dev->le_paired) goto done; /* avoid unpairing device if pairing failed */ if (!pairing && !paired && dev->pairing && dev->le_paired) goto done; if (paired && !dev->bredr_paired && !dev->le_paired) { cached_devices = g_slist_remove(cached_devices, dev); bonded_devices = g_slist_prepend(bonded_devices, dev); remove_device_info(dev, CACHE_FILE); store_device_info(dev, DEVICES_FILE); } else if (!paired && !dev->bredr_paired) { bonded_devices = g_slist_remove(bonded_devices, dev); remove_device_info(dev, DEVICES_FILE); dev->valid_local_csrk = false; dev->valid_remote_csrk = false; dev->local_sign_cnt = 0; dev->remote_sign_cnt = 0; memset(dev->local_csrk, 0, sizeof(dev->local_csrk)); memset(dev->remote_csrk, 0, sizeof(dev->remote_csrk)); cache_device(dev); } dev->le_paired = paired; if (dev->le_paired) dev->le_bonded = dev->le_bonded || bonded; else dev->le_bonded = false; done: dev->pairing = pairing; } static uint8_t device_bond_state(struct device *dev) { if (dev->pairing) return HAL_BOND_STATE_BONDING; /* * We are checking for paired here instead of bonded as HAL API is * using BOND state also if there was no bonding pairing. */ if (dev->bredr_paired || dev->le_paired) return HAL_BOND_STATE_BONDED; return HAL_BOND_STATE_NONE; } static void update_bond_state(struct device *dev, uint8_t status, uint8_t old_bond, uint8_t new_bond) { if (old_bond == new_bond) return; /* * When internal bond state changes from bond to non-bond or other way, * BfA needs to send bonding state to Android in the middle. Otherwise * Android will not handle it correctly */ if ((old_bond == HAL_BOND_STATE_NONE && new_bond == HAL_BOND_STATE_BONDED) || (old_bond == HAL_BOND_STATE_BONDED && new_bond == HAL_BOND_STATE_NONE)) send_bond_state_change(dev, HAL_STATUS_SUCCESS, HAL_BOND_STATE_BONDING); send_bond_state_change(dev, status, new_bond); } static void send_paired_notification(void *data, void *user_data) { bt_paired_device_cb cb = data; struct device *dev = user_data; cb(&dev->bdaddr); } static void update_device_state(struct device *dev, uint8_t addr_type, uint8_t status, bool pairing, bool paired, bool bonded) { uint8_t old_bond, new_bond; old_bond = device_bond_state(dev); if (addr_type == BDADDR_BREDR) update_bredr_state(dev, pairing, paired, bonded); else update_le_state(dev, pairing, paired, bonded); new_bond = device_bond_state(dev); update_bond_state(dev, status, old_bond, new_bond); } static void send_device_property(struct device *dev, uint8_t type, uint16_t len, const void *val) { uint8_t buf[BASELEN_REMOTE_DEV_PROP + len]; struct hal_ev_remote_device_props *ev = (void *) buf; ev->status = HAL_STATUS_SUCCESS; get_device_android_addr(dev, ev->bdaddr); ev->num_props = 1; ev->props[0].type = type; ev->props[0].len = len; memcpy(ev->props[0].val, val, len); ipc_send_notif(hal_ipc, HAL_SERVICE_ID_BLUETOOTH, HAL_EV_REMOTE_DEVICE_PROPS, sizeof(buf), buf); } static void send_device_uuids_notif(struct device *dev) { uint8_t buf[sizeof(uint128_t) * g_slist_length(dev->uuids)]; uint8_t *ptr = buf; GSList *l; for (l = dev->uuids; l; l = g_slist_next(l)) { memcpy(ptr, l->data, sizeof(uint128_t)); ptr += sizeof(uint128_t); } send_device_property(dev, HAL_PROP_DEVICE_UUIDS, sizeof(buf), buf); } static void set_device_uuids(struct device *dev, GSList *uuids) { g_slist_free_full(dev->uuids, g_free); dev->uuids = uuids; if (dev->le_paired || dev->bredr_paired) store_device_info(dev, DEVICES_FILE); else store_device_info(dev, CACHE_FILE); send_device_uuids_notif(dev); } static void browse_req_free(struct browse_req *req) { g_slist_free_full(req->uuids, g_free); g_free(req); } static int uuid_128_cmp(gconstpointer a, gconstpointer b) { return memcmp(a, b, sizeof(uint128_t)); } static void update_records(struct browse_req *req, sdp_list_t *recs) { for (; recs; recs = recs->next) { sdp_record_t *rec = (sdp_record_t *) recs->data; sdp_list_t *svcclass = NULL; uuid_t uuid128; uuid_t *tmp; uint8_t *new_uuid; if (!rec) break; if (sdp_get_service_classes(rec, &svcclass) < 0) continue; if (!svcclass) continue; tmp = svcclass->data; switch (tmp->type) { case SDP_UUID16: sdp_uuid16_to_uuid128(&uuid128, tmp); break; case SDP_UUID32: sdp_uuid32_to_uuid128(&uuid128, tmp); break; case SDP_UUID128: memcpy(&uuid128, tmp, sizeof(uuid_t)); break; default: sdp_list_free(svcclass, free); continue; } new_uuid = g_malloc(16);/* size of 128 bit uuid */ memcpy(new_uuid, &uuid128.value.uuid128, sizeof(uuid128.value.uuid128)); /* Check if uuid is already added */ if (g_slist_find_custom(req->uuids, new_uuid, uuid_128_cmp)) g_free(new_uuid); else req->uuids = g_slist_append(req->uuids, new_uuid); sdp_list_free(svcclass, free); } } static void browse_cb(sdp_list_t *recs, int err, gpointer user_data) { struct browse_req *req = user_data; struct device *dev; uuid_t uuid; /* * If we have a valid response and req->search_uuid == 2, then L2CAP * UUID & PNP searching was successful -- we are done */ if (err < 0 || req->search_uuid == 2) { if (err == -ECONNRESET && req->reconnect_attempt < 1) { req->search_uuid--; req->reconnect_attempt++; } else { goto done; } } update_records(req, recs); /* Search for mandatory uuids */ if (uuid_list[req->search_uuid]) { sdp_uuid16_create(&uuid, uuid_list[req->search_uuid++]); bt_search_service(&adapter.bdaddr, &req->bdaddr, &uuid, browse_cb, user_data, NULL, 0); return; } done: dev = find_device(&req->bdaddr); if (dev) { set_device_uuids(dev, req->uuids); req->uuids = NULL; } browse_reqs = g_slist_remove(browse_reqs, req); browse_req_free(req); } static int req_cmp(gconstpointer a, gconstpointer b) { const struct browse_req *req = a; const bdaddr_t *bdaddr = b; return bacmp(&req->bdaddr, bdaddr); } static uint8_t browse_remote_sdp(const bdaddr_t *addr) { struct browse_req *req; uuid_t uuid; if (g_slist_find_custom(browse_reqs, addr, req_cmp)) return HAL_STATUS_SUCCESS; req = g_new0(struct browse_req, 1); bacpy(&req->bdaddr, addr); sdp_uuid16_create(&uuid, uuid_list[req->search_uuid++]); if (bt_search_service(&adapter.bdaddr, &req->bdaddr, &uuid, browse_cb, req, NULL , 0) < 0) { browse_req_free(req); return HAL_STATUS_FAILED; } browse_reqs = g_slist_append(browse_reqs, req); return HAL_STATUS_SUCCESS; } static void send_remote_sdp_rec_notify(bt_uuid_t *uuid, int channel, char *name, uint8_t name_len, uint8_t status, bdaddr_t *bdaddr) { struct hal_prop_device_service_rec *prop; uint8_t buf[BASELEN_REMOTE_DEV_PROP + name_len + sizeof(*prop)]; struct hal_ev_remote_device_props *ev = (void *) buf; size_t prop_len = sizeof(*prop) + name_len; memset(buf, 0, sizeof(buf)); if (uuid && status == HAL_STATUS_SUCCESS) { prop = (void *) &ev->props[0].val; prop->name_len = name_len; prop->channel = (uint16_t)channel; memcpy(prop->name, name, name_len); memcpy(prop->uuid, &uuid->value.u128, sizeof(prop->uuid)); } ev->num_props = 1; ev->status = status; ev->props[0].len = prop_len; bdaddr2android(bdaddr, ev->bdaddr); ev->props[0].type = HAL_PROP_DEVICE_SERVICE_REC; ipc_send_notif(hal_ipc, HAL_SERVICE_ID_BLUETOOTH, HAL_EV_REMOTE_DEVICE_PROPS, sizeof(buf), buf); } static void find_remote_sdp_rec_cb(sdp_list_t *recs, int err, gpointer user_data) { bdaddr_t *addr = user_data; uint8_t name_len; uint8_t status; char name_buf[256]; int channel; bt_uuid_t uuid; uuid_t uuid128_sdp; sdp_list_t *protos; sdp_record_t *sdp_rec; if (err < 0) { error("error while search remote sdp records"); status = HAL_STATUS_FAILED; send_remote_sdp_rec_notify(NULL, 0, NULL, 0, status, addr); goto done; } if (!recs) { info("No service records found on remote"); status = HAL_STATUS_SUCCESS; send_remote_sdp_rec_notify(NULL, 0, NULL, 0, status, addr); goto done; } for ( ; recs; recs = recs->next) { sdp_rec = recs->data; switch (sdp_rec->svclass.type) { case SDP_UUID16: sdp_uuid16_to_uuid128(&uuid128_sdp, &sdp_rec->svclass); break; case SDP_UUID32: sdp_uuid32_to_uuid128(&uuid128_sdp, &sdp_rec->svclass); break; case SDP_UUID128: break; default: error("wrong sdp uuid type"); goto done; } if (!sdp_get_access_protos(sdp_rec, &protos)) { channel = sdp_get_proto_port(protos, RFCOMM_UUID); sdp_list_foreach(protos, (sdp_list_func_t) sdp_list_free, NULL); sdp_list_free(protos, NULL); } else channel = -1; if (channel < 0) { error("can't get channel for sdp record"); channel = 0; } if (!sdp_get_service_name(sdp_rec, name_buf, sizeof(name_buf))) name_len = strlen(name_buf); else name_len = 0; uuid.type = BT_UUID128; memcpy(&uuid.value.u128, uuid128_sdp.value.uuid128.data, sizeof(uuid.value.u128)); status = HAL_STATUS_SUCCESS; send_remote_sdp_rec_notify(&uuid, channel, name_buf, name_len, status, addr); } done: g_free(addr); } static uint8_t find_remote_sdp_rec(const bdaddr_t *addr, const uint8_t *find_uuid) { bdaddr_t *bdaddr; uuid_t uuid; /* from android we always get full 128bit length uuid */ sdp_uuid128_create(&uuid, find_uuid); bdaddr = g_new(bdaddr_t, 1); if (!bdaddr) return HAL_STATUS_NOMEM; memcpy(bdaddr, addr, sizeof(*bdaddr)); if (bt_search_service(&adapter.bdaddr, addr, &uuid, find_remote_sdp_rec_cb, bdaddr, NULL, 0) < 0) { g_free(bdaddr); return HAL_STATUS_FAILED; } return HAL_STATUS_SUCCESS; } static void new_link_key_callback(uint16_t index, uint16_t length, const void *param, void *user_data) { const struct mgmt_ev_new_link_key *ev = param; const struct mgmt_addr_info *addr = &ev->key.addr; struct device *dev; char dst[18]; if (length < sizeof(*ev)) { error("Too small new link key event"); return; } ba2str(&addr->bdaddr, dst); DBG("new key for %s type %u pin_len %u", dst, ev->key.type, ev->key.pin_len); if (ev->key.pin_len > 16) { error("Invalid PIN length (%u) in new_key event", ev->key.pin_len); return; } dev = get_device(&ev->key.addr.bdaddr, ev->key.addr.type); if (!dev) return; update_device_state(dev, ev->key.addr.type, HAL_STATUS_SUCCESS, false, true, !!ev->store_hint); if (ev->store_hint) { const struct mgmt_link_key_info *key = &ev->key; store_link_key(&addr->bdaddr, key->val, key->type, key->pin_len); } browse_remote_sdp(&addr->bdaddr); } static uint8_t get_device_name(struct device *dev) { send_device_property(dev, HAL_PROP_DEVICE_NAME, strlen(dev->name), dev->name); return HAL_STATUS_SUCCESS; } static void pin_code_request_callback(uint16_t index, uint16_t length, const void *param, void *user_data) { const struct mgmt_ev_pin_code_request *ev = param; struct hal_ev_pin_request hal_ev; struct device *dev; char dst[18]; if (length < sizeof(*ev)) { error("Too small PIN code request event"); return; } ba2str(&ev->addr.bdaddr, dst); dev = get_device(&ev->addr.bdaddr, BDADDR_BREDR); /* * Workaround for Android Bluetooth.apk issue: send remote * device property */ get_device_name(dev); update_device_state(dev, ev->addr.type, HAL_STATUS_SUCCESS, true, false, false); DBG("%s type %u secure %u", dst, ev->addr.type, ev->secure); /* Name already sent in remote device prop */ memset(&hal_ev, 0, sizeof(hal_ev)); get_device_android_addr(dev, hal_ev.bdaddr); hal_ev.class_of_dev = dev->class; ipc_send_notif(hal_ipc, HAL_SERVICE_ID_BLUETOOTH, HAL_EV_PIN_REQUEST, sizeof(hal_ev), &hal_ev); } static void send_ssp_request(struct device *dev, uint8_t variant, uint32_t passkey) { struct hal_ev_ssp_request ev; memset(&ev, 0, sizeof(ev)); get_device_android_addr(dev, ev.bdaddr); memcpy(ev.name, dev->name, strlen(dev->name)); ev.class_of_dev = dev->class; ev.pairing_variant = variant; ev.passkey = passkey; ipc_send_notif(hal_ipc, HAL_SERVICE_ID_BLUETOOTH, HAL_EV_SSP_REQUEST, sizeof(ev), &ev); } static void user_confirm_request_callback(uint16_t index, uint16_t length, const void *param, void *user_data) { const struct mgmt_ev_user_confirm_request *ev = param; struct device *dev; char dst[18]; if (length < sizeof(*ev)) { error("Too small user confirm request event"); return; } ba2str(&ev->addr.bdaddr, dst); DBG("%s confirm_hint %u", dst, ev->confirm_hint); dev = get_device(&ev->addr.bdaddr, ev->addr.type); if (!dev) return; update_device_state(dev, ev->addr.type, HAL_STATUS_SUCCESS, true, false, false); if (ev->confirm_hint) send_ssp_request(dev, HAL_SSP_VARIANT_CONSENT, 0); else send_ssp_request(dev, HAL_SSP_VARIANT_CONFIRM, ev->value); } static void user_passkey_request_callback(uint16_t index, uint16_t length, const void *param, void *user_data) { const struct mgmt_ev_user_passkey_request *ev = param; struct device *dev; char dst[18]; if (length < sizeof(*ev)) { error("Too small passkey request event"); return; } ba2str(&ev->addr.bdaddr, dst); DBG("%s", dst); dev = get_device(&ev->addr.bdaddr, ev->addr.type); if (!dev) return; update_device_state(dev, ev->addr.type, HAL_STATUS_SUCCESS, true, false, false); send_ssp_request(dev, HAL_SSP_VARIANT_ENTRY, 0); } static void user_passkey_notify_callback(uint16_t index, uint16_t length, const void *param, void *user_data) { const struct mgmt_ev_passkey_notify *ev = param; struct device *dev; char dst[18]; if (length < sizeof(*ev)) { error("Too small passkey notify event"); return; } ba2str(&ev->addr.bdaddr, dst); DBG("%s entered %u", dst, ev->entered); /* HAL seems to not support entered characters */ if (ev->entered) return; dev = find_device(&ev->addr.bdaddr); if (!dev) return; update_device_state(dev, ev->addr.type, HAL_STATUS_SUCCESS, true, false, false); send_ssp_request(dev, HAL_SSP_VARIANT_NOTIF, ev->passkey); } static void clear_device_found(gpointer data, gpointer user_data) { struct device *dev = data; dev->found = false; } static uint8_t get_supported_discovery_type(void) { uint8_t type = SCAN_TYPE_NONE; if (adapter.current_settings & MGMT_SETTING_BREDR) type |= SCAN_TYPE_BREDR; if (adapter.current_settings & MGMT_SETTING_LE) type |= SCAN_TYPE_LE; return type; } static bool start_discovery(uint8_t type) { struct mgmt_cp_start_discovery cp; cp.type = get_supported_discovery_type() & type; DBG("type=0x%x", cp.type); if (cp.type == SCAN_TYPE_NONE) return false; if (mgmt_send(mgmt_if, MGMT_OP_START_DISCOVERY, adapter.index, sizeof(cp), &cp, NULL, NULL, NULL) > 0) return true; error("Failed to start discovery"); return false; } /* * Send discovery state change event only if it is related to dual type * discovery session (triggered by start/cancel discovery commands) */ static void check_discovery_state(uint8_t new_type, uint8_t old_type) { struct hal_ev_discovery_state_changed ev; DBG("%u %u", new_type, old_type); if (new_type == get_supported_discovery_type()) { g_slist_foreach(bonded_devices, clear_device_found, NULL); g_slist_foreach(cached_devices, clear_device_found, NULL); ev.state = HAL_DISCOVERY_STATE_STARTED; goto done; } if (old_type != get_supported_discovery_type()) return; ev.state = HAL_DISCOVERY_STATE_STOPPED; done: ipc_send_notif(hal_ipc, HAL_SERVICE_ID_BLUETOOTH, HAL_EV_DISCOVERY_STATE_CHANGED, sizeof(ev), &ev); } static void mgmt_discovering_event(uint16_t index, uint16_t length, const void *param, void *user_data) { const struct mgmt_ev_discovering *ev = param; uint8_t type; if (length < sizeof(*ev)) { error("Too small discovering event"); return; } DBG("type %u discovering %u", ev->type, ev->discovering); if (!!adapter.cur_discovery_type == !!ev->discovering) return; type = ev->discovering ? ev->type : SCAN_TYPE_NONE; check_discovery_state(type, adapter.cur_discovery_type); adapter.cur_discovery_type = type; if (ev->discovering) { adapter.exp_discovery_type = adapter.le_scanning ? SCAN_TYPE_LE : SCAN_TYPE_NONE; return; } /* One shot notification about discovery stopped */ if (gatt_discovery_stopped_cb) { gatt_discovery_stopped_cb(); gatt_discovery_stopped_cb = NULL; } type = adapter.exp_discovery_type; adapter.exp_discovery_type = adapter.le_scanning ? SCAN_TYPE_LE : SCAN_TYPE_NONE; if (type != SCAN_TYPE_NONE) start_discovery(type); } static void confirm_device_name_cb(uint8_t status, uint16_t length, const void *param, void *user_data) { const struct mgmt_rp_confirm_name *rp = param; struct device *dev; DBG("Confirm name status: %s (0x%02x)", mgmt_errstr(status), status); if (length < sizeof(*rp)) { error("Wrong size of confirm name response"); return; } dev = find_device(&rp->addr.bdaddr); if (!dev) return; dev->confirm_id = 0; } static unsigned int confirm_device_name(const bdaddr_t *addr, uint8_t addr_type, bool resolve_name) { struct mgmt_cp_confirm_name cp; unsigned int res; memset(&cp, 0, sizeof(cp)); bacpy(&cp.addr.bdaddr, addr); cp.addr.type = addr_type; if (!resolve_name) cp.name_known = 1; res = mgmt_send(mgmt_if, MGMT_OP_CONFIRM_NAME, adapter.index, sizeof(cp), &cp, confirm_device_name_cb, NULL, NULL); if (!res) error("Failed to send confirm name request"); return res; } static int fill_hal_prop(void *buf, uint8_t type, uint16_t len, const void *val) { struct hal_property *prop = buf; prop->type = type; prop->len = len; if (len) memcpy(prop->val, val, len); return sizeof(*prop) + len; } static uint8_t get_device_android_type(struct device *dev) { if (dev->bredr && dev->le) return HAL_TYPE_DUAL; if (dev->le) return HAL_TYPE_LE; return HAL_TYPE_BREDR; } uint8_t bt_get_device_android_type(const bdaddr_t *addr) { struct device *dev; dev = get_device(addr, BDADDR_BREDR); return get_device_android_type(dev); } bool bt_is_device_le(const bdaddr_t *addr) { struct device *dev; dev = find_device(addr); if (!dev) return false; return dev->le; } const bdaddr_t *bt_get_id_addr(const bdaddr_t *addr, uint8_t *type) { struct device *dev; dev = find_device(addr); if (!dev) return NULL; if (type) *type = dev->bdaddr_type; return &dev->bdaddr; } const char *bt_get_adapter_name(void) { return adapter.name; } bool bt_device_is_bonded(const bdaddr_t *bdaddr) { if (g_slist_find_custom(bonded_devices, bdaddr, device_match)) return true; return false; } bool bt_device_set_uuids(const bdaddr_t *addr, GSList *uuids) { struct device *dev; dev = find_device(addr); if (!dev) return false; set_device_uuids(dev, uuids); return true; } bool bt_kernel_conn_control(void) { return kernel_conn_control; } bool bt_auto_connect_add(const bdaddr_t *addr) { struct mgmt_cp_add_device cp; struct device *dev; if (!kernel_conn_control) return false; dev = find_device(addr); if (!dev) return false; if (dev->bdaddr_type == BDADDR_BREDR) { DBG("auto-connection feature is not available for BR/EDR"); return false; } if (dev->in_white_list) { DBG("Device already in white list"); return true; } memset(&cp, 0, sizeof(cp)); bacpy(&cp.addr.bdaddr, addr); cp.addr.type = dev->bdaddr_type; cp.action = 0x02; if (mgmt_send(mgmt_if, MGMT_OP_ADD_DEVICE, adapter.index, sizeof(cp), &cp, NULL, NULL, NULL) > 0) { dev->in_white_list = true; return true; } error("Failed to add device"); return false; } void bt_auto_connect_remove(const bdaddr_t *addr) { struct mgmt_cp_remove_device cp; struct device *dev; if (!kernel_conn_control) return; dev = find_device(addr); if (!dev) return; if (dev->bdaddr_type == BDADDR_BREDR) { DBG("auto-connection feature is not available for BR/EDR"); return; } if (!dev->in_white_list) { DBG("Device already removed from white list"); return; } memset(&cp, 0, sizeof(cp)); bacpy(&cp.addr.bdaddr, addr); cp.addr.type = dev->bdaddr_type; if (mgmt_send(mgmt_if, MGMT_OP_REMOVE_DEVICE, adapter.index, sizeof(cp), &cp, NULL, NULL, NULL) > 0) { dev->in_white_list = false; return; } error("Failed to remove device"); } static bool match_by_value(const void *data, const void *user_data) { return data == user_data; } bool bt_unpaired_register(bt_unpaired_device_cb cb) { if (queue_find(unpaired_cb_list, match_by_value, cb)) return false; return queue_push_head(unpaired_cb_list, cb); } void bt_unpaired_unregister(bt_unpaired_device_cb cb) { queue_remove(unpaired_cb_list, cb); } bool bt_paired_register(bt_paired_device_cb cb) { if (queue_find(paired_cb_list, match_by_value, cb)) return false; return queue_push_head(paired_cb_list, cb); } void bt_paired_unregister(bt_paired_device_cb cb) { queue_remove(paired_cb_list, cb); } bool bt_is_pairing(const bdaddr_t *addr) { struct device *dev; dev = find_device(addr); if (!dev) return false; return dev->pairing; } static bool rssi_above_threshold(int old, int new) { /* only 8 dBm or more */ return abs(old - new) >= 8; } static void update_new_device(struct device *dev, int8_t rssi, const struct eir_data *eir) { uint8_t buf[IPC_MTU]; struct hal_ev_device_found *ev = (void *) buf; uint8_t android_bdaddr[6]; uint8_t android_type; size_t size; memset(buf, 0, sizeof(buf)); if (adapter.cur_discovery_type) dev->found = true; size = sizeof(*ev); get_device_android_addr(dev, android_bdaddr); size += fill_hal_prop(buf + size, HAL_PROP_DEVICE_ADDR, sizeof(android_bdaddr), android_bdaddr); ev->num_props++; android_type = get_device_android_type(dev); size += fill_hal_prop(buf + size, HAL_PROP_DEVICE_TYPE, sizeof(android_type), &android_type); ev->num_props++; if (eir->class) dev->class = eir->class; if (dev->class) { size += fill_hal_prop(buf + size, HAL_PROP_DEVICE_CLASS, sizeof(dev->class), &dev->class); ev->num_props++; } if (rssi && rssi_above_threshold(dev->rssi, rssi)) dev->rssi = rssi; if (dev->rssi) { size += fill_hal_prop(buf + size, HAL_PROP_DEVICE_RSSI, sizeof(dev->rssi), &dev->rssi); ev->num_props++; } if (eir->name && strlen(eir->name)) { g_free(dev->name); dev->name = g_strdup(eir->name); } if (dev->name) { size += fill_hal_prop(buf + size, HAL_PROP_DEVICE_NAME, strlen(dev->name), dev->name); ev->num_props++; /* when updating name also send stored friendly name */ if (dev->friendly_name) { size += fill_hal_prop(buf + size, HAL_PROP_DEVICE_FRIENDLY_NAME, strlen(dev->friendly_name), dev->friendly_name); ev->num_props++; } } ipc_send_notif(hal_ipc, HAL_SERVICE_ID_BLUETOOTH, HAL_EV_DEVICE_FOUND, size, buf); } static void update_device(struct device *dev, int8_t rssi, const struct eir_data *eir) { uint8_t buf[IPC_MTU]; struct hal_ev_remote_device_props *ev = (void *) buf; uint8_t old_type, new_type; size_t size; memset(buf, 0, sizeof(buf)); size = sizeof(*ev); ev->status = HAL_STATUS_SUCCESS; get_device_android_addr(dev, ev->bdaddr); old_type = get_device_android_type(dev); new_type = get_device_android_type(dev); if (old_type != new_type) { size += fill_hal_prop(buf + size, HAL_PROP_DEVICE_TYPE, sizeof(new_type), &new_type); ev->num_props++; } if (eir->class && dev->class != eir->class) { dev->class = eir->class; size += fill_hal_prop(buf + size, HAL_PROP_DEVICE_CLASS, sizeof(dev->class), &dev->class); ev->num_props++; } if (rssi && rssi_above_threshold(dev->rssi, rssi)) { dev->rssi = rssi; size += fill_hal_prop(buf + size, HAL_PROP_DEVICE_RSSI, sizeof(dev->rssi), &dev->rssi); ev->num_props++; } if (eir->name && strlen(eir->name) && strcmp(dev->name, eir->name)) { g_free(dev->name); dev->name = g_strdup(eir->name); size += fill_hal_prop(buf + size, HAL_PROP_DEVICE_NAME, strlen(dev->name), dev->name); ev->num_props++; /* when updating name also send stored friendly name */ if (dev->friendly_name) { size += fill_hal_prop(buf + size, HAL_PROP_DEVICE_FRIENDLY_NAME, strlen(dev->friendly_name), dev->friendly_name); ev->num_props++; } } if (ev->num_props) ipc_send_notif(hal_ipc, HAL_SERVICE_ID_BLUETOOTH, HAL_EV_REMOTE_DEVICE_PROPS, size, buf); } static bool is_new_device(const struct device *dev, unsigned int flags, uint8_t bdaddr_type) { if (dev->found) return false; if (dev->bredr_paired || dev->le_paired) return false; if (bdaddr_type != BDADDR_BREDR && !(flags & (EIR_LIM_DISC | EIR_GEN_DISC))) return false; return true; } static void update_found_device(const bdaddr_t *bdaddr, uint8_t bdaddr_type, int8_t rssi, bool confirm, bool connectable, const uint8_t *data, uint8_t data_len) { struct eir_data eir; struct device *dev; memset(&eir, 0, sizeof(eir)); eir_parse(&eir, data, data_len); dev = get_device(bdaddr, bdaddr_type); if (bdaddr_type == BDADDR_BREDR) { dev->bredr = true; dev->bredr_seen = time(NULL); } else { dev->le = true; dev->bdaddr_type = bdaddr_type; dev->le_seen = time(NULL); } /* * Device found event needs to be send also for known device if this is * new discovery session. Otherwise framework will ignore it. */ if (is_new_device(dev, eir.flags, bdaddr_type)) update_new_device(dev, rssi, &eir); else update_device(dev, rssi, &eir); eir_data_free(&eir); /* Notify Gatt if its registered for LE events */ if (bdaddr_type != BDADDR_BREDR && gatt_device_found_cb) { const bdaddr_t *addr; /* * If RPA is set it means that IRK was received and ID address * is being used. Android Framework is still using old RPA and * it needs to be used also in GATT notifications. Also GATT * HAL implementation is using RPA for devices matching. */ if (bacmp(&dev->rpa, BDADDR_ANY)) addr = &dev->rpa; else addr = &dev->bdaddr; gatt_device_found_cb(addr, rssi, data_len, data, connectable, dev->le_bonded); } if (!dev->bredr_paired && !dev->le_paired) cache_device(dev); if (confirm) { char addr[18]; bool resolve_name = true; ba2str(bdaddr, addr); /* * Don't need to confirm name if we have it already in cache * Just check if device name is different than bdaddr */ if (g_strcmp0(dev->name, addr)) { get_device_name(dev); resolve_name = false; } info("Device %s needs name confirmation (resolve_name=%d)", addr, resolve_name); dev->confirm_id = confirm_device_name(bdaddr, bdaddr_type, resolve_name); } } static void mgmt_device_found_event(uint16_t index, uint16_t length, const void *param, void *user_data) { const struct mgmt_ev_device_found *ev = param; const uint8_t *eir; uint16_t eir_len; uint32_t flags; bool confirm_name; bool connectable; char addr[18]; if (length < sizeof(*ev)) { error("Too short device found event (%u bytes)", length); return; } eir_len = le16_to_cpu(ev->eir_len); if (length != sizeof(*ev) + eir_len) { error("Device found event size mismatch (%u != %zu)", length, sizeof(*ev) + eir_len); return; } if (eir_len == 0) eir = NULL; else eir = ev->eir; flags = le32_to_cpu(ev->flags); ba2str(&ev->addr.bdaddr, addr); DBG("hci%u addr %s, rssi %d flags 0x%04x eir_len %u", index, addr, ev->rssi, flags, eir_len); confirm_name = flags & MGMT_DEV_FOUND_CONFIRM_NAME; connectable = !(flags & MGMT_DEV_FOUND_NOT_CONNECTABLE); update_found_device(&ev->addr.bdaddr, ev->addr.type, ev->rssi, confirm_name, connectable, eir, eir_len); } static void mgmt_device_connected_event(uint16_t index, uint16_t length, const void *param, void *user_data) { const struct mgmt_ev_device_connected *ev = param; struct hal_ev_acl_state_changed hal_ev; struct device *dev; char addr[18]; if (length < sizeof(*ev)) { error("Too short device connected event (%u bytes)", length); return; } ba2str(&ev->addr.bdaddr, addr); DBG("%s type %u", addr, ev->addr.type); update_found_device(&ev->addr.bdaddr, ev->addr.type, 0, false, false, &ev->eir[0], le16_to_cpu(ev->eir_len)); hal_ev.status = HAL_STATUS_SUCCESS; hal_ev.state = HAL_ACL_STATE_CONNECTED; dev = find_device(&ev->addr.bdaddr); if (!dev) return; dev->connected = true; get_device_android_addr(dev, hal_ev.bdaddr); ipc_send_notif(hal_ipc, HAL_SERVICE_ID_BLUETOOTH, HAL_EV_ACL_STATE_CHANGED, sizeof(hal_ev), &hal_ev); } static bool device_is_paired(struct device *dev, uint8_t addr_type) { if (addr_type == BDADDR_BREDR) return dev->bredr_paired; return dev->le_paired; } static bool device_is_bonded(struct device *dev) { return dev->bredr_bonded || dev->le_bonded; } static void mgmt_device_disconnected_event(uint16_t index, uint16_t length, const void *param, void *user_data) { const struct mgmt_ev_device_disconnected *ev = param; struct hal_ev_acl_state_changed hal_ev; struct device *dev; uint8_t type = ev->addr.type; if (length < sizeof(*ev)) { error("Too short device disconnected event (%u bytes)", length); return; } dev = find_device(&ev->addr.bdaddr); if (!dev) return; hal_ev.status = HAL_STATUS_SUCCESS; hal_ev.state = HAL_ACL_STATE_DISCONNECTED; get_device_android_addr(dev, hal_ev.bdaddr); ipc_send_notif(hal_ipc, HAL_SERVICE_ID_BLUETOOTH, HAL_EV_ACL_STATE_CHANGED, sizeof(hal_ev), &hal_ev); if (device_is_paired(dev, type) && !device_is_bonded(dev)) update_device_state(dev, type, HAL_STATUS_SUCCESS, false, false, false); dev->connected = false; } static uint8_t status_mgmt2hal(uint8_t mgmt) { switch (mgmt) { case MGMT_STATUS_SUCCESS: return HAL_STATUS_SUCCESS; case MGMT_STATUS_NO_RESOURCES: return HAL_STATUS_NOMEM; case MGMT_STATUS_BUSY: return HAL_STATUS_BUSY; case MGMT_STATUS_NOT_SUPPORTED: return HAL_STATUS_UNSUPPORTED; case MGMT_STATUS_INVALID_PARAMS: return HAL_STATUS_INVALID; case MGMT_STATUS_AUTH_FAILED: return HAL_STATUS_AUTH_FAILURE; case MGMT_STATUS_NOT_CONNECTED: return HAL_STATUS_REMOTE_DEVICE_DOWN; default: return HAL_STATUS_FAILED; } } static void mgmt_connect_failed_event(uint16_t index, uint16_t length, const void *param, void *user_data) { const struct mgmt_ev_connect_failed *ev = param; struct device *dev; if (length < sizeof(*ev)) { error("Too short connect failed event (%u bytes)", length); return; } DBG(""); dev = find_device(&ev->addr.bdaddr); if (!dev) return; /* * In case security mode 3 pairing we will get connect failed event * in case e.g wrong PIN code entered. Let's check if device is * bonding, if so update bond state */ if (!dev->pairing) return; update_device_state(dev, ev->addr.type, status_mgmt2hal(ev->status), false, false, false); } static void mgmt_auth_failed_event(uint16_t index, uint16_t length, const void *param, void *user_data) { const struct mgmt_ev_auth_failed *ev = param; struct device *dev; if (length < sizeof(*ev)) { error("Too small auth failed mgmt event (%u bytes)", length); return; } DBG(""); dev = find_device(&ev->addr.bdaddr); if (!dev) return; if (!dev->pairing) return; update_device_state(dev, ev->addr.type, status_mgmt2hal(ev->status), false, false, false); } static void mgmt_device_unpaired_event(uint16_t index, uint16_t length, const void *param, void *user_data) { const struct mgmt_ev_device_unpaired *ev = param; struct device *dev; if (length < sizeof(*ev)) { error("Too small device unpaired event (%u bytes)", length); return; } DBG(""); /* TODO should device be disconnected ? */ dev = find_device(&ev->addr.bdaddr); if (!dev) return; update_device_state(dev, ev->addr.type, HAL_STATUS_SUCCESS, false, false, false); /* Unpaired device is removed from the white list */ dev->in_white_list = false; } static void store_ltk(const bdaddr_t *dst, uint8_t bdaddr_type, bool master, const uint8_t *key, uint8_t key_type, uint8_t enc_size, uint16_t ediv, uint64_t rand) { const char *key_s, *keytype_s, *encsize_s, *ediv_s, *rand_s; GKeyFile *key_file; char key_str[33]; gsize length = 0; char addr[18]; char *data; int i; key_file = g_key_file_new(); if (!g_key_file_load_from_file(key_file, DEVICES_FILE, 0, NULL)) { g_key_file_free(key_file); return; } ba2str(dst, addr); key_s = master ? "LongTermKey" : "SlaveLongTermKey"; keytype_s = master ? "LongTermKeyType" : "SlaveLongTermKeyType"; encsize_s = master ? "LongTermKeyEncSize" : "SlaveLongTermKeyEncSize"; ediv_s = master ? "LongTermKeyEDiv" : "SlaveLongTermKeyEDiv"; rand_s = master ? "LongTermKeyRand" : "SlaveLongTermKeyRand"; for (i = 0; i < 16; i++) sprintf(key_str + (i * 2), "%2.2X", key[i]); g_key_file_set_string(key_file, addr, key_s, key_str); g_key_file_set_integer(key_file, addr, keytype_s, key_type); g_key_file_set_integer(key_file, addr, encsize_s, enc_size); g_key_file_set_integer(key_file, addr, ediv_s, ediv); g_key_file_set_uint64(key_file, addr, rand_s, rand); data = g_key_file_to_data(key_file, &length, NULL); g_file_set_contents(DEVICES_FILE, data, length, NULL); g_free(data); g_key_file_free(key_file); } static void new_long_term_key_event(uint16_t index, uint16_t length, const void *param, void *user_data) { const struct mgmt_ev_new_long_term_key *ev = param; struct device *dev; char dst[18]; if (length < sizeof(*ev)) { error("Too small long term key event (%u bytes)", length); return; } ba2str(&ev->key.addr.bdaddr, dst); DBG("new LTK for %s type %u enc_size %u store_hint %u", dst, ev->key.type, ev->key.enc_size, ev->store_hint); dev = find_device(&ev->key.addr.bdaddr); if (!dev) return; update_device_state(dev, ev->key.addr.type, HAL_STATUS_SUCCESS, false, true, !!ev->store_hint); if (ev->store_hint) { const struct mgmt_ltk_info *key = &ev->key; uint16_t ediv; uint64_t rand; ediv = le16_to_cpu(key->ediv); rand = le64_to_cpu(key->rand); store_ltk(&key->addr.bdaddr, key->addr.type, key->central, key->val, key->type, key->enc_size, ediv, rand); } /* TODO browse services here? */ } static void store_csrk(struct device *dev) { GKeyFile *key_file; char key_str[33]; char addr[18]; int i; gsize length = 0; char *data; ba2str(&dev->bdaddr, addr); key_file = g_key_file_new(); if (!g_key_file_load_from_file(key_file, DEVICES_FILE, 0, NULL)) { g_key_file_free(key_file); return; } if (dev->valid_local_csrk) { for (i = 0; i < 16; i++) sprintf(key_str + (i * 2), "%2.2X", dev->local_csrk[i]); g_key_file_set_string(key_file, addr, "LocalCSRK", key_str); g_key_file_set_boolean(key_file, addr, "LocalCSRKAuthenticated", dev->local_csrk_auth); } if (dev->valid_remote_csrk) { for (i = 0; i < 16; i++) sprintf(key_str + (i * 2), "%2.2X", dev->remote_csrk[i]); g_key_file_set_string(key_file, addr, "RemoteCSRK", key_str); g_key_file_set_boolean(key_file, addr, "RemoteCSRKAuthenticated", dev->remote_csrk_auth); } data = g_key_file_to_data(key_file, &length, NULL); g_file_set_contents(DEVICES_FILE, data, length, NULL); g_free(data); g_key_file_free(key_file); } static void new_csrk_callback(uint16_t index, uint16_t length, const void *param, void *user_data) { const struct mgmt_ev_new_csrk *ev = param; struct device *dev; char dst[18]; if (length < sizeof(*ev)) { error("Too small csrk event (%u bytes)", length); return; } ba2str(&ev->key.addr.bdaddr, dst); dev = get_device(&ev->key.addr.bdaddr, ev->key.addr.type); if (!dev) return; switch (ev->key.type) { case 0x00: case 0x02: memcpy(dev->local_csrk, ev->key.val, 16); dev->local_sign_cnt = 0; dev->valid_local_csrk = true; dev->local_csrk_auth = ev->key.type == 0x02; break; case 0x01: case 0x03: memcpy(dev->remote_csrk, ev->key.val, 16); dev->remote_sign_cnt = 0; dev->valid_remote_csrk = true; dev->remote_csrk_auth = ev->key.type == 0x03; break; default: error("Unknown CSRK key type 02%02x", ev->key.type); return; } update_device_state(dev, ev->key.addr.type, HAL_STATUS_SUCCESS, false, true, !!ev->store_hint); if (ev->store_hint) store_csrk(dev); } static void store_irk(struct device *dev, const uint8_t *val) { GKeyFile *key_file; char key_str[33]; char addr[18]; int i; gsize length = 0; char *data; ba2str(&dev->bdaddr, addr); key_file = g_key_file_new(); if (!g_key_file_load_from_file(key_file, DEVICES_FILE, 0, NULL)) { g_key_file_free(key_file); return; } for (i = 0; i < 16; i++) sprintf(key_str + (i * 2), "%2.2X", val[i]); g_key_file_set_string(key_file, addr, "IdentityResolvingKey", key_str); data = g_key_file_to_data(key_file, &length, NULL); g_file_set_contents(DEVICES_FILE, data, length, NULL); g_free(data); g_key_file_free(key_file); } static void new_irk_callback(uint16_t index, uint16_t length, const void *param, void *user_data) { const struct mgmt_ev_new_irk *ev = param; const struct mgmt_addr_info *addr = &ev->key.addr; struct device *dev; char dst[18], rpa[18]; if (length < sizeof(*ev)) { error("To small New Irk Event (%u bytes)", length); return; } ba2str(&ev->key.addr.bdaddr, dst); ba2str(&ev->rpa, rpa); DBG("new IRK for %s, RPA %s", dst, rpa); if (!bacmp(&ev->rpa, BDADDR_ANY)) { dev = get_device(&addr->bdaddr, addr->type); if (!dev) return; } else { dev = find_device(&addr->bdaddr); if (dev && dev->bredr_paired) { bacpy(&dev->rpa, &addr->bdaddr); dev->rpa_type = addr->type; /* TODO merge properties ie. UUIDs */ } else { dev = find_device(&ev->rpa); if (!dev) return; /* don't leave garbage in cache file */ remove_device_info(dev, CACHE_FILE); /* * RPA resolution is transparent for Android Framework * ie. device is still access by RPA so it need to be * keep. After bluetoothd restart device is advertised * to Android with IDA and RPA is not set. */ bacpy(&dev->rpa, &dev->bdaddr); dev->rpa_type = dev->bdaddr_type; bacpy(&dev->bdaddr, &addr->bdaddr); dev->bdaddr_type = addr->type; } } update_device_state(dev, ev->key.addr.type, HAL_STATUS_SUCCESS, false, true, !!ev->store_hint); if (ev->store_hint) store_irk(dev, ev->key.val); } static void register_mgmt_handlers(void) { mgmt_register(mgmt_if, MGMT_EV_NEW_SETTINGS, adapter.index, new_settings_callback, NULL, NULL); mgmt_register(mgmt_if, MGMT_EV_CLASS_OF_DEV_CHANGED, adapter.index, mgmt_dev_class_changed_event, NULL, NULL); mgmt_register(mgmt_if, MGMT_EV_LOCAL_NAME_CHANGED, adapter.index, mgmt_local_name_changed_event, NULL, NULL); mgmt_register(mgmt_if, MGMT_EV_NEW_LINK_KEY, adapter.index, new_link_key_callback, NULL, NULL); mgmt_register(mgmt_if, MGMT_EV_PIN_CODE_REQUEST, adapter.index, pin_code_request_callback, NULL, NULL); mgmt_register(mgmt_if, MGMT_EV_USER_CONFIRM_REQUEST, adapter.index, user_confirm_request_callback, NULL, NULL); mgmt_register(mgmt_if, MGMT_EV_USER_PASSKEY_REQUEST, adapter.index, user_passkey_request_callback, NULL, NULL); mgmt_register(mgmt_if, MGMT_EV_PASSKEY_NOTIFY, adapter.index, user_passkey_notify_callback, NULL, NULL); mgmt_register(mgmt_if, MGMT_EV_DISCOVERING, adapter.index, mgmt_discovering_event, NULL, NULL); mgmt_register(mgmt_if, MGMT_EV_DEVICE_FOUND, adapter.index, mgmt_device_found_event, NULL, NULL); mgmt_register(mgmt_if, MGMT_EV_DEVICE_CONNECTED, adapter.index, mgmt_device_connected_event, NULL, NULL); mgmt_register(mgmt_if, MGMT_EV_DEVICE_DISCONNECTED, adapter.index, mgmt_device_disconnected_event, NULL, NULL); mgmt_register(mgmt_if, MGMT_EV_CONNECT_FAILED, adapter.index, mgmt_connect_failed_event, NULL, NULL); mgmt_register(mgmt_if, MGMT_EV_AUTH_FAILED, adapter.index, mgmt_auth_failed_event, NULL, NULL); mgmt_register(mgmt_if, MGMT_EV_DEVICE_UNPAIRED, adapter.index, mgmt_device_unpaired_event, NULL, NULL); mgmt_register(mgmt_if, MGMT_EV_NEW_LONG_TERM_KEY, adapter.index, new_long_term_key_event, NULL, NULL); mgmt_register(mgmt_if, MGMT_EV_NEW_CSRK, adapter.index, new_csrk_callback, NULL, NULL); mgmt_register(mgmt_if, MGMT_EV_NEW_IRK, adapter.index, new_irk_callback, NULL, NULL); } static void load_link_keys_complete(uint8_t status, uint16_t length, const void *param, void *user_data) { bt_bluetooth_ready cb = user_data; int err; if (status) { error("Failed to load link keys for index %u: %s (0x%02x)", adapter.index, mgmt_errstr(status), status); err = -EIO; goto failed; } DBG("status %u", status); cb(0, &adapter.bdaddr); return; failed: cb(err, NULL); } static void load_link_keys(GSList *keys, bt_bluetooth_ready cb) { struct mgmt_cp_load_link_keys *cp; struct mgmt_link_key_info *key; size_t key_count, cp_size; unsigned int id; key_count = g_slist_length(keys); DBG("keys %zu ", key_count); cp_size = sizeof(*cp) + (key_count * sizeof(*key)); cp = g_malloc0(cp_size); /* * Even if the list of stored keys is empty, it is important to * load an empty list into the kernel. That way it is ensured * that no old keys from a previous daemon are present. */ cp->key_count = cpu_to_le16(key_count); for (key = cp->keys; keys != NULL; keys = g_slist_next(keys), key++) memcpy(key, keys->data, sizeof(*key)); id = mgmt_send(mgmt_if, MGMT_OP_LOAD_LINK_KEYS, adapter.index, cp_size, cp, load_link_keys_complete, cb, NULL); g_free(cp); if (id == 0) { error("Failed to load link keys"); cb(-EIO, NULL); } } static void load_ltk_complete(uint8_t status, uint16_t length, const void *param, void *user_data) { if (status == MGMT_STATUS_SUCCESS) return; info("Failed to load LTKs: %s (0x%02x)", mgmt_errstr(status), status); } static void load_ltks(GSList *ltks) { struct mgmt_cp_load_long_term_keys *cp; struct mgmt_ltk_info *ltk; size_t ltk_count, cp_size; GSList *l; ltk_count = g_slist_length(ltks); DBG("ltks %zu", ltk_count); cp_size = sizeof(*cp) + (ltk_count * sizeof(*ltk)); cp = g_malloc0(cp_size); /* * Even if the list of stored keys is empty, it is important to load * an empty list into the kernel. That way it is ensured that no old * keys from a previous daemon are present. */ cp->key_count = cpu_to_le16(ltk_count); for (l = ltks, ltk = cp->keys; l != NULL; l = g_slist_next(l), ltk++) memcpy(ltk, l->data, sizeof(*ltk)); if (mgmt_send(mgmt_if, MGMT_OP_LOAD_LONG_TERM_KEYS, adapter.index, cp_size, cp, load_ltk_complete, NULL, NULL) == 0) error("Failed to load LTKs"); g_free(cp); } static void load_irk_complete(uint8_t status, uint16_t length, const void *param, void *user_data) { if (status == MGMT_STATUS_SUCCESS) return; info("Failed to load IRKs: %s (0x%02x)", mgmt_errstr(status), status); } static void load_irks(GSList *irks) { struct mgmt_cp_load_irks *cp; struct mgmt_irk_info *irk; size_t irk_count, cp_size; GSList *l; irk_count = g_slist_length(irks); DBG("irks %zu", irk_count); cp_size = sizeof(*cp) + (irk_count * sizeof(*irk)); cp = g_malloc0(cp_size); cp->irk_count = cpu_to_le16(irk_count); for (l = irks, irk = cp->irks; l != NULL; l = g_slist_next(l), irk++) memcpy(irk, irks->data, sizeof(*irk)); if (mgmt_send(mgmt_if, MGMT_OP_LOAD_IRKS, adapter.index, cp_size, cp, load_irk_complete, NULL, NULL) == 0) error("Failed to load IRKs"); g_free(cp); } static uint8_t get_adapter_uuids(void) { struct hal_ev_adapter_props_changed *ev; GSList *list = adapter.uuids; size_t uuid_count = g_slist_length(list); size_t len = uuid_count * sizeof(uint128_t); uint8_t buf[BASELEN_PROP_CHANGED + len]; uint8_t *p; memset(buf, 0, sizeof(buf)); ev = (void *) buf; ev->num_props = 1; ev->status = HAL_STATUS_SUCCESS; ev->props[0].type = HAL_PROP_ADAPTER_UUIDS; ev->props[0].len = len; p = ev->props->val; for (; list; list = g_slist_next(list)) { uuid_t *uuid = list->data; memcpy(p, &uuid->value.uuid128, sizeof(uint128_t)); p += sizeof(uint128_t); } ipc_send_notif(hal_ipc, HAL_SERVICE_ID_BLUETOOTH, HAL_EV_ADAPTER_PROPS_CHANGED, sizeof(buf), ev); return HAL_STATUS_SUCCESS; } static void remove_uuid_complete(uint8_t status, uint16_t length, const void *param, void *user_data) { if (status != MGMT_STATUS_SUCCESS) { error("Failed to remove UUID: %s (0x%02x)", mgmt_errstr(status), status); return; } mgmt_dev_class_changed_event(adapter.index, length, param, NULL); get_adapter_uuids(); } static void remove_uuid(uuid_t *uuid) { uint128_t uint128; struct mgmt_cp_remove_uuid cp; ntoh128((uint128_t *) uuid->value.uuid128.data, &uint128); htob128(&uint128, (uint128_t *) cp.uuid); mgmt_send(mgmt_if, MGMT_OP_REMOVE_UUID, adapter.index, sizeof(cp), &cp, remove_uuid_complete, NULL, NULL); } static void add_uuid_complete(uint8_t status, uint16_t length, const void *param, void *user_data) { if (status != MGMT_STATUS_SUCCESS) { error("Failed to add UUID: %s (0x%02x)", mgmt_errstr(status), status); return; } mgmt_dev_class_changed_event(adapter.index, length, param, NULL); get_adapter_uuids(); } static void add_uuid(uint8_t svc_hint, uuid_t *uuid) { uint128_t uint128; struct mgmt_cp_add_uuid cp; ntoh128((uint128_t *) uuid->value.uuid128.data, &uint128); htob128(&uint128, (uint128_t *) cp.uuid); cp.svc_hint = svc_hint; mgmt_send(mgmt_if, MGMT_OP_ADD_UUID, adapter.index, sizeof(cp), &cp, add_uuid_complete, NULL, NULL); } int bt_adapter_add_record(sdp_record_t *rec, uint8_t svc_hint) { uuid_t *uuid; uuid = sdp_uuid_to_uuid128(&rec->svclass); if (g_slist_find_custom(adapter.uuids, uuid, sdp_uuid_cmp)) { char uuid_str[32]; sdp_uuid2strn(uuid, uuid_str, sizeof(uuid_str)); DBG("UUID %s already added", uuid_str); bt_free(uuid); return -EALREADY; } adapter.uuids = g_slist_prepend(adapter.uuids, uuid); add_uuid(svc_hint, uuid); return add_record_to_server(&adapter.bdaddr, rec); } void bt_adapter_remove_record(uint32_t handle) { sdp_record_t *rec; GSList *uuid_found; rec = sdp_record_find(handle); if (!rec) return; uuid_found = g_slist_find_custom(adapter.uuids, &rec->svclass, sdp_uuid_cmp); if (uuid_found) { uuid_t *uuid = uuid_found->data; remove_uuid(uuid); adapter.uuids = g_slist_remove(adapter.uuids, uuid); free(uuid); } remove_record_from_server(handle); } static void set_mode_complete(uint8_t status, uint16_t length, const void *param, void *user_data) { if (status != MGMT_STATUS_SUCCESS) { error("Failed to set mode: %s (0x%02x)", mgmt_errstr(status), status); return; } /* * The parameters are identical and also the task that is * required in both cases. So it is safe to just call the * event handling functions here. */ new_settings_callback(adapter.index, length, param, NULL); } static bool set_mode(uint16_t opcode, uint8_t mode) { struct mgmt_mode cp; memset(&cp, 0, sizeof(cp)); cp.val = mode; DBG("opcode=0x%x mode=0x%x", opcode, mode); if (mgmt_send(mgmt_if, opcode, adapter.index, sizeof(cp), &cp, set_mode_complete, NULL, NULL) > 0) return true; error("Failed to set mode"); return false; } static void set_io_capability(void) { struct mgmt_cp_set_io_capability cp; memset(&cp, 0, sizeof(cp)); cp.io_capability = DEFAULT_IO_CAPABILITY; if (mgmt_send(mgmt_if, MGMT_OP_SET_IO_CAPABILITY, adapter.index, sizeof(cp), &cp, NULL, NULL, NULL) == 0) error("Failed to set IO capability"); } static void set_device_id(void) { struct mgmt_cp_set_device_id cp; memset(&cp, 0, sizeof(cp)); cp.source = cpu_to_le16(bt_config_get_pnp_source()); cp.vendor = cpu_to_le16(bt_config_get_pnp_vendor()); cp.product = cpu_to_le16(bt_config_get_pnp_product()); cp.version = cpu_to_le16(bt_config_get_pnp_version()); if (mgmt_send(mgmt_if, MGMT_OP_SET_DEVICE_ID, adapter.index, sizeof(cp), &cp, NULL, NULL, NULL) == 0) error("Failed to set device id"); register_device_id(bt_config_get_pnp_source(), bt_config_get_pnp_vendor(), bt_config_get_pnp_product(), bt_config_get_pnp_version()); bt_adapter_add_record(sdp_record_find(0x10000), 0x00); } static void set_adapter_name_complete(uint8_t status, uint16_t length, const void *param, void *user_data) { const struct mgmt_cp_set_local_name *rp = param; if (status != MGMT_STATUS_SUCCESS) { error("Failed to set name: %s (0x%02x)", mgmt_errstr(status), status); return; } adapter_set_name(rp->name); } static uint8_t set_adapter_name(const uint8_t *name, uint16_t len) { struct mgmt_cp_set_local_name cp; memset(&cp, 0, sizeof(cp)); memcpy(cp.name, name, len); if (mgmt_send(mgmt_if, MGMT_OP_SET_LOCAL_NAME, adapter.index, sizeof(cp), &cp, set_adapter_name_complete, NULL, NULL) > 0) return HAL_STATUS_SUCCESS; error("Failed to set name"); return HAL_STATUS_FAILED; } static uint8_t set_adapter_discoverable_timeout(const void *buf, uint16_t len) { const uint32_t *timeout = buf; if (len != sizeof(*timeout)) { error("Invalid set disc timeout size (%u bytes), terminating", len); raise(SIGTERM); return HAL_STATUS_FAILED; } /* * Android handles discoverable timeout in Settings app. * There is no need to use kernel feature for that. * Just need to store this value here */ memcpy(&adapter.discoverable_timeout, timeout, sizeof(uint32_t)); store_adapter_config(); send_adapter_property(HAL_PROP_ADAPTER_DISC_TIMEOUT, sizeof(adapter.discoverable_timeout), &adapter.discoverable_timeout); return HAL_STATUS_SUCCESS; } static void clear_uuids(void) { struct mgmt_cp_remove_uuid cp; memset(&cp, 0, sizeof(cp)); mgmt_send(mgmt_if, MGMT_OP_REMOVE_UUID, adapter.index, sizeof(cp), &cp, NULL, NULL, NULL); } static struct device *create_device_from_info(GKeyFile *key_file, const char *peer) { struct device *dev; uint8_t type; bdaddr_t bdaddr; char **uuids; char *str; /* BREDR if not present */ type = g_key_file_get_integer(key_file, peer, "AddressType", NULL); str2ba(peer, &bdaddr); dev = create_device(&bdaddr, type); if (type != BDADDR_BREDR) dev->bredr = g_key_file_get_boolean(key_file, peer, "BREDR", NULL); str = g_key_file_get_string(key_file, peer, "LocalCSRK", NULL); if (str) { int i; dev->valid_local_csrk = true; for (i = 0; i < 16; i++) sscanf(str + (i * 2), "%02hhX", &dev->local_csrk[i]); g_free(str); dev->local_sign_cnt = g_key_file_get_integer(key_file, peer, "LocalCSRKSignCounter", NULL); dev->local_csrk_auth = g_key_file_get_boolean(key_file, peer, "LocalCSRKAuthenticated", NULL); } str = g_key_file_get_string(key_file, peer, "RemoteCSRK", NULL); if (str) { int i; dev->valid_remote_csrk = true; for (i = 0; i < 16; i++) sscanf(str + (i * 2), "%02hhX", &dev->remote_csrk[i]); g_free(str); dev->remote_sign_cnt = g_key_file_get_integer(key_file, peer, "RemoteCSRKSignCounter", NULL); dev->remote_csrk_auth = g_key_file_get_boolean(key_file, peer, "RemoteCSRKAuthenticated", NULL); } str = g_key_file_get_string(key_file, peer, "GattCCC", NULL); if (str) { dev->gatt_ccc = atoi(str); g_free(str); } str = g_key_file_get_string(key_file, peer, "Name", NULL); if (str) { g_free(dev->name); dev->name = str; } str = g_key_file_get_string(key_file, peer, "FriendlyName", NULL); if (str) { g_free(dev->friendly_name); dev->friendly_name = str; } dev->class = g_key_file_get_integer(key_file, peer, "Class", NULL); if (dev->bredr) dev->bredr_seen = g_key_file_get_integer(key_file, peer, "Timestamp", NULL); else dev->le_seen = g_key_file_get_integer(key_file, peer, "Timestamp", NULL); uuids = g_key_file_get_string_list(key_file, peer, "Services", NULL, NULL); if (uuids) { char **uuid; for (uuid = uuids; *uuid; uuid++) { uint8_t *u = g_malloc0(16); int i; for (i = 0; i < 16; i++) sscanf((*uuid) + (i * 2), "%02hhX", &u[i]); dev->uuids = g_slist_append(dev->uuids, u); } g_strfreev(uuids); } return dev; } static struct mgmt_link_key_info *get_key_info(GKeyFile *key_file, const char *peer) { struct mgmt_link_key_info *info = NULL; char *str; unsigned int i; str = g_key_file_get_string(key_file, peer, "LinkKey", NULL); if (!str || strlen(str) != 32) goto failed; info = g_new0(struct mgmt_link_key_info, 1); str2ba(peer, &info->addr.bdaddr); for (i = 0; i < sizeof(info->val); i++) sscanf(str + (i * 2), "%02hhX", &info->val[i]); info->type = g_key_file_get_integer(key_file, peer, "LinkKeyType", NULL); info->pin_len = g_key_file_get_integer(key_file, peer, "LinkKeyPinLength", NULL); failed: g_free(str); return info; } static struct mgmt_ltk_info *get_ltk_info(GKeyFile *key_file, const char *peer, bool master) { const char *key_s, *keytype_s, *encsize_s, *ediv_s, *rand_s; struct mgmt_ltk_info *info = NULL; char *key; unsigned int i; key_s = master ? "LongTermKey" : "SlaveLongTermKey"; keytype_s = master ? "LongTermKeyType" : "SlaveLongTermKeyType"; encsize_s = master ? "LongTermKeyEncSize" : "SlaveLongTermKeyEncSize"; ediv_s = master ? "LongTermKeyEDiv" : "SlaveLongTermKeyEDiv"; rand_s = master ? "LongTermKeyRand" : "SlaveLongTermKeyRand"; key = g_key_file_get_string(key_file, peer, key_s, NULL); if (!key || strlen(key) != 32) goto failed; info = g_new0(struct mgmt_ltk_info, 1); str2ba(peer, &info->addr.bdaddr); info->addr.type = g_key_file_get_integer(key_file, peer, "AddressType", NULL); for (i = 0; i < sizeof(info->val); i++) sscanf(key + (i * 2), "%02hhX", &info->val[i]); info->type = g_key_file_get_integer(key_file, peer, keytype_s, NULL); info->enc_size = g_key_file_get_integer(key_file, peer, encsize_s, NULL); info->rand = g_key_file_get_uint64(key_file, peer, rand_s, NULL); info->rand = cpu_to_le64(info->rand); info->ediv = g_key_file_get_integer(key_file, peer, ediv_s, NULL); info->ediv = cpu_to_le16(info->ediv); info->central = master; failed: g_free(key); return info; } static struct mgmt_irk_info *get_irk_info(GKeyFile *key_file, const char *peer) { struct mgmt_irk_info *info = NULL; unsigned int i; char *str; str = g_key_file_get_string(key_file, peer, "IdentityResolvingKey", NULL); if (!str || strlen(str) != 32) goto failed; info = g_new0(struct mgmt_irk_info, 1); str2ba(peer, &info->addr.bdaddr); info->addr.type = g_key_file_get_integer(key_file, peer, "AddressType", NULL); for (i = 0; i < sizeof(info->val); i++) sscanf(str + (i * 2), "%02hhX", &info->val[i]); failed: g_free(str); return info; } static time_t device_timestamp(const struct device *dev) { if (dev->bredr && dev->le) { if (dev->le_seen > dev->bredr_seen) return dev->le_seen; return dev->bredr_seen; } if (dev->bredr) return dev->bredr_seen; return dev->le_seen; } static int device_timestamp_cmp(gconstpointer a, gconstpointer b) { const struct device *deva = a; const struct device *devb = b; return device_timestamp(deva) < device_timestamp(devb); } static void load_devices_cache(void) { GKeyFile *key_file; gchar **devs; gsize len = 0; unsigned int i; key_file = g_key_file_new(); g_key_file_load_from_file(key_file, CACHE_FILE, 0, NULL); devs = g_key_file_get_groups(key_file, &len); for (i = 0; i < len; i++) { struct device *dev; dev = create_device_from_info(key_file, devs[i]); cached_devices = g_slist_prepend(cached_devices, dev); } cached_devices = g_slist_sort(cached_devices, device_timestamp_cmp); g_strfreev(devs); g_key_file_free(key_file); } static void load_devices_info(bt_bluetooth_ready cb) { GKeyFile *key_file; gchar **devs; gsize len = 0; unsigned int i; GSList *keys = NULL; GSList *ltks = NULL; GSList *irks = NULL; key_file = g_key_file_new(); g_key_file_load_from_file(key_file, DEVICES_FILE, 0, NULL); devs = g_key_file_get_groups(key_file, &len); for (i = 0; i < len; i++) { struct mgmt_link_key_info *key_info; struct mgmt_ltk_info *ltk_info; struct mgmt_irk_info *irk_info; struct mgmt_ltk_info *slave_ltk_info; struct device *dev; dev = create_device_from_info(key_file, devs[i]); key_info = get_key_info(key_file, devs[i]); irk_info = get_irk_info(key_file, devs[i]); ltk_info = get_ltk_info(key_file, devs[i], true); slave_ltk_info = get_ltk_info(key_file, devs[i], false); /* * Skip devices that have no permanent keys * (CSRKs are loaded by create_device_from_info()) */ if (!dev->valid_local_csrk && !dev->valid_remote_csrk && !key_info && !ltk_info && !slave_ltk_info && !irk_info) { error("Failed to load keys for %s, skipping", devs[i]); free_device(dev); continue; } if (key_info) { keys = g_slist_prepend(keys, key_info); dev->bredr_paired = true; dev->bredr_bonded = true; } if (irk_info) irks = g_slist_prepend(irks, irk_info); if (ltk_info) ltks = g_slist_prepend(ltks, ltk_info); if (slave_ltk_info) ltks = g_slist_prepend(ltks, slave_ltk_info); if (dev->valid_local_csrk || dev->valid_remote_csrk || irk_info || ltk_info || slave_ltk_info) { dev->le_paired = true; dev->le_bonded = true; } bonded_devices = g_slist_prepend(bonded_devices, dev); } load_ltks(ltks); g_slist_free_full(ltks, g_free); load_irks(irks); g_slist_free_full(irks, g_free); if (adapter.supported_settings & MGMT_SETTING_BREDR) load_link_keys(keys, cb); else cb(0, &adapter.bdaddr); g_slist_free_full(keys, g_free); g_strfreev(devs); g_key_file_free(key_file); } static void set_adapter_class(void) { struct mgmt_cp_set_dev_class cp; memset(&cp, 0, sizeof(cp)); /* * kernel assign the major and minor numbers straight to dev_class[0] * and dev_class[1] without considering the proper bit shifting. */ cp.major = ADAPTER_MAJOR_CLASS & 0x1f; cp.minor = ADAPTER_MINOR_CLASS << 2; if (mgmt_send(mgmt_if, MGMT_OP_SET_DEV_CLASS, adapter.index, sizeof(cp), &cp, NULL, NULL, NULL) > 0) return; error("Failed to set class of device"); } static void enable_mps(void) { uuid_t uuid, *uuid128; sdp_uuid16_create(&uuid, MPS_SVCLASS_ID); uuid128 = sdp_uuid_to_uuid128(&uuid); if (!uuid128) return; register_mps(true); adapter.uuids = g_slist_prepend(adapter.uuids, uuid128); add_uuid(0, uuid128); } static void clear_auto_connect_list_complete(uint8_t status, uint16_t length, const void *param, void *user_data) { if (status != MGMT_STATUS_SUCCESS) error("Failed to clear auto connect list: %s (0x%02x)", mgmt_errstr(status), status); } static void clear_auto_connect_list(void) { struct mgmt_cp_remove_device cp; if (!kernel_conn_control) return; memset(&cp, 0, sizeof(cp)); if (mgmt_send(mgmt_if, MGMT_OP_REMOVE_DEVICE, adapter.index, sizeof(cp), &cp, clear_auto_connect_list_complete, NULL, NULL) > 0) return; error("Could not clear auto connect list"); } static void read_adv_features_complete(uint8_t status, uint16_t length, const void *param, void *user_data) { const struct mgmt_rp_read_adv_features *rp = param; bt_bluetooth_ready cb = user_data; int err; if (status) { error("Failed to read advertising features for index %u: %s (0x%02x)", adapter.index, mgmt_errstr(status), status); err = -EIO; goto failed; } if (length < sizeof(*rp)) { error("Too small read advertising features response"); err = -EIO; goto failed; } adapter.max_advert_instance = rp->max_instances; info("Max LE advertising instances: %d", adapter.max_advert_instance); load_devices_info(cb); return; failed: cb(err, NULL); } static void read_info_complete(uint8_t status, uint16_t length, const void *param, void *user_data) { const struct mgmt_rp_read_info *rp = param; bt_bluetooth_ready cb = user_data; uint32_t missing_settings; int err; DBG(""); if (status) { error("Failed to read info for index %u: %s (0x%02x)", adapter.index, mgmt_errstr(status), status); err = -EIO; goto failed; } if (length < sizeof(*rp)) { error("Too small read info complete response"); err = -EIO; goto failed; } if (!bacmp(&rp->bdaddr, BDADDR_ANY)) { error("No Bluetooth address"); err = -ENODEV; goto failed; } load_adapter_config(); if (!bacmp(&adapter.bdaddr, BDADDR_ANY)) { bacpy(&adapter.bdaddr, &rp->bdaddr); store_adapter_config(); } else if (bacmp(&adapter.bdaddr, &rp->bdaddr)) { error("Bluetooth address mismatch"); err = -ENODEV; goto failed; } if (adapter.name && g_strcmp0(adapter.name, (const char *) rp->name)) set_adapter_name((uint8_t *)adapter.name, strlen(adapter.name)); set_adapter_class(); /* Store adapter information */ adapter.dev_class = rp->dev_class[0] | (rp->dev_class[1] << 8) | (rp->dev_class[2] << 16); adapter.supported_settings = le32_to_cpu(rp->supported_settings); adapter.current_settings = le32_to_cpu(rp->current_settings); /* TODO: Register all event notification handlers */ register_mgmt_handlers(); clear_uuids(); clear_auto_connect_list(); set_io_capability(); set_device_id(); enable_mps(); missing_settings = adapter.current_settings ^ adapter.supported_settings; if (missing_settings & MGMT_SETTING_SSP) set_mode(MGMT_OP_SET_SSP, 0x01); if (missing_settings & MGMT_SETTING_BONDABLE) set_mode(MGMT_OP_SET_BONDABLE, 0x01); if (adapter.supported_settings & MGMT_SETTING_LE) { if (mgmt_send(mgmt_if, MGMT_OP_READ_ADV_FEATURES, adapter.index, 0, NULL, read_adv_features_complete, cb, NULL) == 0) { error("Cannot get LE adv features"); err = -EIO; goto failed; } } else { load_devices_info(cb); } load_devices_cache(); return; failed: cb(err, NULL); } static void mgmt_index_added_event(uint16_t index, uint16_t length, const void *param, void *user_data) { bt_bluetooth_ready cb = user_data; DBG("index %u", index); if (adapter.index != MGMT_INDEX_NONE) { DBG("skip event for index %u", index); return; } if (option_index != MGMT_INDEX_NONE && option_index != index) { DBG("skip event for index %u (option %u)", index, option_index); return; } adapter.index = index; if (mgmt_send(mgmt_if, MGMT_OP_READ_INFO, index, 0, NULL, read_info_complete, cb, NULL) == 0) { cb(-EIO, NULL); return; } } static void mgmt_index_removed_event(uint16_t index, uint16_t length, const void *param, void *user_data) { DBG("index %u", index); if (index != adapter.index) return; error("Adapter was removed. Exiting."); raise(SIGTERM); } static void read_index_list_complete(uint8_t status, uint16_t length, const void *param, void *user_data) { const struct mgmt_rp_read_index_list *rp = param; bt_bluetooth_ready cb = user_data; uint16_t num; int i; DBG(""); if (status) { error("%s: Failed to read index list: %s (0x%02x)", __func__, mgmt_errstr(status), status); goto failed; } if (length < sizeof(*rp)) { error("%s: Wrong size of read index list response", __func__); goto failed; } num = le16_to_cpu(rp->num_controllers); DBG("Number of controllers: %u", num); if (num * sizeof(uint16_t) + sizeof(*rp) != length) { error("%s: Incorrect pkt size for index list rsp", __func__); goto failed; } if (adapter.index != MGMT_INDEX_NONE) return; for (i = 0; i < num; i++) { uint16_t index = le16_to_cpu(rp->index[i]); if (option_index != MGMT_INDEX_NONE && option_index != index) continue; if (mgmt_send(mgmt_if, MGMT_OP_READ_INFO, index, 0, NULL, read_info_complete, cb, NULL) == 0) goto failed; adapter.index = index; return; } return; failed: cb(-EIO, NULL); } static void read_version_complete(uint8_t status, uint16_t length, const void *param, void *user_data) { const struct mgmt_rp_read_version *rp = param; uint8_t mgmt_version, mgmt_revision; bt_bluetooth_ready cb = user_data; DBG(""); if (status) { error("Failed to read version information: %s (0x%02x)", mgmt_errstr(status), status); goto failed; } if (length < sizeof(*rp)) { error("Wrong size response"); goto failed; } mgmt_version = rp->version; mgmt_revision = le16_to_cpu(rp->revision); info("Bluetooth management interface %u.%u initialized", mgmt_version, mgmt_revision); if (MGMT_VERSION(mgmt_version, mgmt_revision) < MGMT_VERSION(1, 3)) { error("Version 1.3 or later of management interface required"); goto failed; } /* Starting from mgmt 1.7, kernel can handle connection control */ if (MGMT_VERSION(mgmt_version, mgmt_revision) >= MGMT_VERSION(1, 7)) { info("Kernel connection control will be used"); kernel_conn_control = true; } mgmt_register(mgmt_if, MGMT_EV_INDEX_ADDED, MGMT_INDEX_NONE, mgmt_index_added_event, cb, NULL); mgmt_register(mgmt_if, MGMT_EV_INDEX_REMOVED, MGMT_INDEX_NONE, mgmt_index_removed_event, NULL, NULL); if (mgmt_send(mgmt_if, MGMT_OP_READ_INDEX_LIST, MGMT_INDEX_NONE, 0, NULL, read_index_list_complete, cb, NULL) > 0) return; error("Failed to read controller index list"); failed: cb(-EIO, NULL); } bool bt_bluetooth_start(int index, bool mgmt_dbg, bt_bluetooth_ready cb) { DBG("index %d", index); mgmt_if = mgmt_new_default(); if (!mgmt_if) { error("Failed to access management interface"); return false; } if (mgmt_dbg) mgmt_set_debug(mgmt_if, mgmt_debug, "mgmt_if: ", NULL); if (mgmt_send(mgmt_if, MGMT_OP_READ_VERSION, MGMT_INDEX_NONE, 0, NULL, read_version_complete, cb, NULL) == 0) { error("Error sending READ_VERSION mgmt command"); mgmt_unref(mgmt_if); mgmt_if = NULL; return false; } if (index >= 0) option_index = index; return true; } static void shutdown_complete(uint8_t status, uint16_t length, const void *param, void *user_data) { bt_bluetooth_stopped cb = user_data; if (status != MGMT_STATUS_SUCCESS) error("Clean controller shutdown failed"); cb(); } bool bt_bluetooth_stop(bt_bluetooth_stopped cb) { struct mgmt_mode cp; if (adapter.index == MGMT_INDEX_NONE) return false; info("Switching controller off"); memset(&cp, 0, sizeof(cp)); return mgmt_send(mgmt_if, MGMT_OP_SET_POWERED, adapter.index, sizeof(cp), &cp, shutdown_complete, (void *)cb, NULL) > 0; } void bt_bluetooth_cleanup(void) { g_free(adapter.name); adapter.name = NULL; mgmt_unref(mgmt_if); mgmt_if = NULL; } static bool set_discoverable(uint8_t mode, uint16_t timeout) { struct mgmt_cp_set_discoverable cp; memset(&cp, 0, sizeof(cp)); cp.val = mode; cp.timeout = cpu_to_le16(timeout); DBG("mode %u timeout %u", mode, timeout); if (mgmt_send(mgmt_if, MGMT_OP_SET_DISCOVERABLE, adapter.index, sizeof(cp), &cp, set_mode_complete, NULL, NULL) > 0) return true; error("Failed to set mode discoverable"); return false; } static uint8_t get_adapter_address(void) { uint8_t buf[6]; bdaddr2android(&adapter.bdaddr, buf); send_adapter_property(HAL_PROP_ADAPTER_ADDR, sizeof(buf), buf); return HAL_STATUS_SUCCESS; } static uint8_t get_adapter_name(void) { if (!adapter.name) return HAL_STATUS_FAILED; adapter_name_changed((uint8_t *) adapter.name); return HAL_STATUS_SUCCESS; } static uint8_t get_adapter_class(void) { DBG(""); adapter_class_changed(); return HAL_STATUS_SUCCESS; } static uint8_t settings2type(void) { bool bredr, le; bredr = adapter.current_settings & MGMT_SETTING_BREDR; le = adapter.current_settings & MGMT_SETTING_LE; if (bredr && le) return HAL_TYPE_DUAL; if (bredr && !le) return HAL_TYPE_BREDR; if (!bredr && le) return HAL_TYPE_LE; return 0; } static uint8_t get_adapter_type(void) { uint8_t type; DBG(""); type = settings2type(); if (!type) return HAL_STATUS_FAILED; send_adapter_property(HAL_PROP_ADAPTER_TYPE, sizeof(type), &type); return HAL_STATUS_SUCCESS; } static uint8_t get_adapter_service_rec(void) { DBG("Not implemented"); /* TODO: Add implementation */ return HAL_STATUS_FAILED; } static uint8_t get_adapter_scan_mode(void) { DBG(""); scan_mode_changed(); return HAL_STATUS_SUCCESS; } static uint8_t get_adapter_bonded_devices(void) { uint8_t buf[sizeof(bdaddr_t) * g_slist_length(bonded_devices)]; int i = 0; GSList *l; DBG(""); for (l = bonded_devices; l; l = g_slist_next(l)) { struct device *dev = l->data; get_device_android_addr(dev, buf + (i * sizeof(bdaddr_t))); i++; } send_adapter_property(HAL_PROP_ADAPTER_BONDED_DEVICES, i * sizeof(bdaddr_t), buf); return HAL_STATUS_SUCCESS; } static uint8_t get_adapter_discoverable_timeout(void) { send_adapter_property(HAL_PROP_ADAPTER_DISC_TIMEOUT, sizeof(adapter.discoverable_timeout), &adapter.discoverable_timeout); return HAL_STATUS_SUCCESS; } static void prepare_le_features(uint8_t *le_features) { le_features[0] = !!(adapter.current_settings & MGMT_SETTING_PRIVACY); le_features[1] = adapter.max_advert_instance; le_features[2] = adapter.rpa_offload_supported; le_features[3] = adapter.max_irk_list_size; le_features[4] = adapter.max_scan_filters_supported; /* lo byte */ le_features[5] = adapter.scan_result_storage_size; /* hi byte */ le_features[6] = adapter.scan_result_storage_size >> 8; le_features[7] = adapter.activity_energy_info_supported; } static uint8_t get_adapter_le_features(void) { uint8_t le_features[8]; prepare_le_features(le_features); send_adapter_property(HAL_PROP_ADAPTER_LOCAL_LE_FEAT, sizeof(le_features), le_features); return HAL_STATUS_SUCCESS; } static void handle_get_adapter_prop_cmd(const void *buf, uint16_t len) { const struct hal_cmd_get_adapter_prop *cmd = buf; uint8_t status; switch (cmd->type) { case HAL_PROP_ADAPTER_ADDR: status = get_adapter_address(); break; case HAL_PROP_ADAPTER_NAME: status = get_adapter_name(); break; case HAL_PROP_ADAPTER_UUIDS: status = get_adapter_uuids(); break; case HAL_PROP_ADAPTER_CLASS: status = get_adapter_class(); break; case HAL_PROP_ADAPTER_TYPE: status = get_adapter_type(); break; case HAL_PROP_ADAPTER_SERVICE_REC: status = get_adapter_service_rec(); break; case HAL_PROP_ADAPTER_SCAN_MODE: status = get_adapter_scan_mode(); break; case HAL_PROP_ADAPTER_BONDED_DEVICES: status = get_adapter_bonded_devices(); break; case HAL_PROP_ADAPTER_DISC_TIMEOUT: status = get_adapter_discoverable_timeout(); break; case HAL_PROP_ADAPTER_LOCAL_LE_FEAT: status = get_adapter_le_features(); break; default: status = HAL_STATUS_FAILED; break; } if (status != HAL_STATUS_SUCCESS) error("Failed to get adapter property (type %u status %u)", cmd->type, status); ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_BLUETOOTH, HAL_OP_GET_ADAPTER_PROP, status); } static void get_adapter_properties(void) { uint8_t buf[IPC_MTU]; struct hal_ev_adapter_props_changed *ev = (void *) buf; uint8_t bonded[g_slist_length(bonded_devices) * sizeof(bdaddr_t)]; uint128_t uuids[g_slist_length(adapter.uuids)]; uint8_t android_bdaddr[6]; uint8_t le_features[8]; uint8_t type, mode; size_t size, i; GSList *l; size = sizeof(*ev); ev->status = HAL_STATUS_SUCCESS; ev->num_props = 0; bdaddr2android(&adapter.bdaddr, &android_bdaddr); size += fill_hal_prop(buf + size, HAL_PROP_ADAPTER_ADDR, sizeof(android_bdaddr), android_bdaddr); ev->num_props++; if (adapter.name) { size += fill_hal_prop(buf + size, HAL_PROP_ADAPTER_NAME, strlen(adapter.name), adapter.name); ev->num_props++; } size += fill_hal_prop(buf + size, HAL_PROP_ADAPTER_CLASS, sizeof(adapter.dev_class), &adapter.dev_class); ev->num_props++; type = settings2type(); if (type) { size += fill_hal_prop(buf + size, HAL_PROP_ADAPTER_TYPE, sizeof(type), &type); ev->num_props++; } mode = settings2scan_mode(); size += fill_hal_prop(buf + size, HAL_PROP_ADAPTER_SCAN_MODE, sizeof(mode), &mode); ev->num_props++; size += fill_hal_prop(buf + size, HAL_PROP_ADAPTER_DISC_TIMEOUT, sizeof(adapter.discoverable_timeout), &adapter.discoverable_timeout); ev->num_props++; for (i = 0, l = bonded_devices; l; l = g_slist_next(l), i++) { struct device *dev = l->data; get_device_android_addr(dev, bonded + (i * sizeof(bdaddr_t))); } size += fill_hal_prop(buf + size, HAL_PROP_ADAPTER_BONDED_DEVICES, sizeof(bonded), bonded); ev->num_props++; for (i = 0, l = adapter.uuids; l; l = g_slist_next(l), i++) { uuid_t *uuid = l->data; memcpy(&uuids[i], &uuid->value.uuid128, sizeof(uint128_t)); } size += fill_hal_prop(buf + size, HAL_PROP_ADAPTER_UUIDS, sizeof(uuids), uuids); ev->num_props++; prepare_le_features(le_features); size += fill_hal_prop(buf + size, HAL_PROP_ADAPTER_LOCAL_LE_FEAT, sizeof(le_features), le_features); ev->num_props++; ipc_send_notif(hal_ipc, HAL_SERVICE_ID_BLUETOOTH, HAL_EV_ADAPTER_PROPS_CHANGED, size, buf); } static void cancel_pending_confirm_name(gpointer data, gpointer user_data) { struct device *dev = data; mgmt_cancel(mgmt_if, dev->confirm_id); dev->confirm_id = 0; } static bool stop_discovery(uint8_t type) { struct mgmt_cp_stop_discovery cp; cp.type = get_supported_discovery_type() & type; DBG("type=0x%x", cp.type); if (cp.type == SCAN_TYPE_NONE) return false; /* Lets drop all confirm name request as we don't need it anymore */ g_slist_foreach(cached_devices, cancel_pending_confirm_name, NULL); if (mgmt_send(mgmt_if, MGMT_OP_STOP_DISCOVERY, adapter.index, sizeof(cp), &cp, NULL, NULL, NULL) > 0) return true; error("Failed to stop discovery"); return false; } struct adv_user_data { bt_le_set_advertising_done cb; void *user_data; }; static void set_advertising_cb(uint8_t status, uint16_t length, const void *param, void *user_data) { struct adv_user_data *data = user_data; DBG(""); if (status) error("Failed to set adverising %s (0x%02x))", mgmt_errstr(status), status); data->cb(status, data->user_data); } bool bt_le_set_advertising(bool advertising, bt_le_set_advertising_done cb, void *user_data) { struct adv_user_data *data; uint8_t adv = advertising ? 0x01 : 0x00; data = new0(struct adv_user_data, 1); data->cb = cb; data->user_data = user_data; if (mgmt_send(mgmt_if, MGMT_OP_SET_ADVERTISING, adapter.index, sizeof(adv), &adv, set_advertising_cb, data, free) > 0) return true; error("Failed to set advertising"); free(data); return false; } struct addrm_adv_user_data { bt_le_addrm_advertising_done cb; void *user_data; }; static void add_advertising_cb(uint8_t status, uint16_t length, const void *param, void *user_data) { struct addrm_adv_user_data *data = user_data; DBG(""); if (status) error("Failed to add advertising %s (0x%02x))", mgmt_errstr(status), status); data->cb(status, data->user_data); } bool bt_le_add_advertising(struct adv_instance *adv, bt_le_addrm_advertising_done cb, void *user_data) { struct mgmt_cp_add_advertising *cp; struct addrm_adv_user_data *cb_data; size_t len; size_t adv_data_len = 0; size_t sr_data_len = 0; uint8_t *dst, *adv_data, *sr_data; bool ok = false; /* These accept NULL and return NULL */ adv_data = bt_ad_generate(adv->ad, &adv_data_len); sr_data = bt_ad_generate(adv->sr, &sr_data_len); len = sizeof(*cp) + adv_data_len + sr_data_len; cp = malloc0(len); if (!cp) goto out; cp->instance = adv->instance; cp->timeout = adv->timeout; /* XXX: how should we set duration? (kernel defaults to 2s) */ switch (adv->type) { case ANDROID_ADVERTISING_EVENT_TYPE_CONNECTABLE: cp->flags |= MGMT_ADV_FLAG_CONNECTABLE; break; case ANDROID_ADVERTISING_EVENT_TYPE_SCANNABLE: case ANDROID_ADVERTISING_EVENT_TYPE_NON_CONNECTABLE: default: break; } if (adv->include_tx_power) cp->flags |= MGMT_ADV_FLAG_TX_POWER; dst = cp->data; if (adv_data) { cp->adv_data_len = adv_data_len; memcpy(dst, adv_data, adv_data_len); dst += adv_data_len; } if (sr_data) { cp->scan_rsp_len = sr_data_len; memcpy(dst, sr_data, sr_data_len); dst += sr_data_len; } DBG("lens: adv=%u sr=%u total=%zu", cp->adv_data_len, cp->scan_rsp_len, len); cb_data = new0(__typeof__(*cb_data), 1); cb_data->cb = cb; cb_data->user_data = user_data; ok = (mgmt_send(mgmt_if, MGMT_OP_ADD_ADVERTISING, adapter.index, len, cp, add_advertising_cb, cb_data, free) > 0); if (!ok) free(cb_data); out: free(adv_data); free(sr_data); free(cp); return ok; } static void remove_advertising_cb(uint8_t status, uint16_t length, const void *param, void *user_data) { struct addrm_adv_user_data *data = user_data; DBG(""); if (status) error("Failed to remove advertising %s (0x%02x))", mgmt_errstr(status), status); data->cb(status, data->user_data); } bool bt_le_remove_advertising(struct adv_instance *adv, bt_le_addrm_advertising_done cb, void *user_data) { struct mgmt_cp_remove_advertising cp = { .instance = adv->instance, }; struct addrm_adv_user_data *cb_data; bool ok; cb_data = new0(__typeof__(*cb_data), 1); cb_data->cb = cb; cb_data->user_data = user_data; ok = (mgmt_send(mgmt_if, MGMT_OP_REMOVE_ADVERTISING, adapter.index, sizeof(cp), &cp, remove_advertising_cb, cb_data, free) > 0); if (!ok) free(cb_data); return ok; } bool bt_le_register(bt_le_device_found cb) { if (gatt_device_found_cb) return false; gatt_device_found_cb = cb; return true; } void bt_le_unregister(void) { gatt_device_found_cb = NULL; } bool bt_le_discovery_stop(bt_le_discovery_stopped cb) { if (!(adapter.current_settings & MGMT_SETTING_POWERED)) return false; adapter.le_scanning = false; if (adapter.cur_discovery_type != SCAN_TYPE_LE) { if (cb) cb(); return true; } if (!stop_discovery(SCAN_TYPE_LE)) return false; gatt_discovery_stopped_cb = cb; adapter.exp_discovery_type = SCAN_TYPE_NONE; return true; } bool bt_le_discovery_start(void) { if (!(adapter.current_settings & MGMT_SETTING_POWERED)) return false; adapter.le_scanning = true; /* * If core is discovering - just set expected next scan type. * It will be triggered in case current scan session is almost done * i.e. we missed LE phase in interleaved scan, or we're trying to * connect to device that was already discovered. */ if (adapter.cur_discovery_type != SCAN_TYPE_NONE) { adapter.exp_discovery_type = SCAN_TYPE_LE; return true; } if (start_discovery(SCAN_TYPE_LE)) return true; return false; } struct read_rssi_user_data { bt_read_device_rssi_done cb; void *user_data; }; static void read_device_rssi_cb(uint8_t status, uint16_t length, const void *param, void *user_data) { const struct mgmt_rp_get_conn_info *rp = param; struct read_rssi_user_data *data = user_data; DBG(""); if (status) error("Failed to get conn info: %s (0x%02x))", mgmt_errstr(status), status); if (length < sizeof(*rp)) { error("Wrong size of get conn info response"); return; } data->cb(status, &rp->addr.bdaddr, rp->rssi, data->user_data); } bool bt_read_device_rssi(const bdaddr_t *addr, bt_read_device_rssi_done cb, void *user_data) { struct device *dev; struct read_rssi_user_data *data; struct mgmt_cp_get_conn_info cp; dev = find_device(addr); if (!dev) return false; memcpy(&cp.addr.bdaddr, addr, sizeof(cp.addr.bdaddr)); cp.addr.type = dev->bredr ? BDADDR_BREDR : dev->bdaddr_type; data = new0(struct read_rssi_user_data, 1); data->cb = cb; data->user_data = user_data; if (!mgmt_send(mgmt_if, MGMT_OP_GET_CONN_INFO, adapter.index, sizeof(cp), &cp, read_device_rssi_cb, data, free)) { free(data); error("Failed to get conn info"); return false; } return true; } bool bt_get_csrk(const bdaddr_t *addr, bool local, uint8_t key[16], uint32_t *sign_cnt, bool *authenticated) { struct device *dev; dev = find_device(addr); if (!dev) return false; if (local && dev->valid_local_csrk) { if (key) memcpy(key, dev->local_csrk, 16); if (sign_cnt) *sign_cnt = dev->local_sign_cnt; if (authenticated) *authenticated = dev->local_csrk_auth; } else if (!local && dev->valid_remote_csrk) { if (key) memcpy(key, dev->remote_csrk, 16); if (sign_cnt) *sign_cnt = dev->remote_sign_cnt; if (authenticated) *authenticated = dev->remote_csrk_auth; } else { return false; } return true; } static void store_sign_counter(struct device *dev, bool local) { const char *sign_cnt_s; uint32_t sign_cnt; GKeyFile *key_file; gsize length = 0; char addr[18]; char *data; key_file = g_key_file_new(); if (!g_key_file_load_from_file(key_file, DEVICES_FILE, 0, NULL)) { g_key_file_free(key_file); return; } ba2str(&dev->bdaddr, addr); sign_cnt_s = local ? "LocalCSRKSignCounter" : "RemoteCSRKSignCounter"; sign_cnt = local ? dev->local_sign_cnt : dev->remote_sign_cnt; g_key_file_set_integer(key_file, addr, sign_cnt_s, sign_cnt); data = g_key_file_to_data(key_file, &length, NULL); g_file_set_contents(DEVICES_FILE, data, length, NULL); g_free(data); g_key_file_free(key_file); } void bt_update_sign_counter(const bdaddr_t *addr, bool local, uint32_t val) { struct device *dev; dev = find_device(addr); if (!dev) return; if (local) dev->local_sign_cnt = val; else dev->remote_sign_cnt = val; store_sign_counter(dev, local); } static uint8_t set_adapter_scan_mode(const void *buf, uint16_t len) { const uint8_t *mode = buf; bool conn, disc, cur_conn, cur_disc; if (len != sizeof(*mode)) { error("Invalid set scan mode size (%u bytes), terminating", len); raise(SIGTERM); return HAL_STATUS_FAILED; } cur_conn = adapter.current_settings & MGMT_SETTING_CONNECTABLE; cur_disc = adapter.current_settings & MGMT_SETTING_DISCOVERABLE; DBG("connectable %u discoverable %d mode %u", cur_conn, cur_disc, *mode); switch (*mode) { case HAL_ADAPTER_SCAN_MODE_NONE: if (!cur_conn && !cur_disc) goto done; conn = false; disc = false; break; case HAL_ADAPTER_SCAN_MODE_CONN: if (cur_conn && !cur_disc) goto done; conn = true; disc = false; break; case HAL_ADAPTER_SCAN_MODE_CONN_DISC: if (cur_conn && cur_disc) goto done; conn = true; disc = true; break; default: return HAL_STATUS_FAILED; } if (cur_conn != conn) { if (!set_mode(MGMT_OP_SET_CONNECTABLE, conn ? 0x01 : 0x00)) return HAL_STATUS_FAILED; } if (cur_disc != disc && conn) { if (!set_discoverable(disc ? 0x01 : 0x00, 0)) return HAL_STATUS_FAILED; } return HAL_STATUS_SUCCESS; done: /* Android expects property changed callback */ scan_mode_changed(); return HAL_STATUS_SUCCESS; } static void handle_set_adapter_prop_cmd(const void *buf, uint16_t len) { const struct hal_cmd_set_adapter_prop *cmd = buf; uint8_t status; if (len != sizeof(*cmd) + cmd->len) { error("Invalid set adapter prop cmd (0x%x), terminating", cmd->type); raise(SIGTERM); return; } switch (cmd->type) { case HAL_PROP_ADAPTER_SCAN_MODE: status = set_adapter_scan_mode(cmd->val, cmd->len); break; case HAL_PROP_ADAPTER_NAME: status = set_adapter_name(cmd->val, cmd->len); break; case HAL_PROP_ADAPTER_DISC_TIMEOUT: status = set_adapter_discoverable_timeout(cmd->val, cmd->len); break; default: DBG("Unhandled property type 0x%x", cmd->type); status = HAL_STATUS_FAILED; break; } if (status != HAL_STATUS_SUCCESS) error("Failed to set adapter property (type %u status %u)", cmd->type, status); ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_BLUETOOTH, HAL_OP_SET_ADAPTER_PROP, status); } static void pair_device_complete(uint8_t status, uint16_t length, const void *param, void *user_data) { const struct mgmt_rp_pair_device *rp = param; struct device *dev; DBG("status %u", status); dev = find_device(&rp->addr.bdaddr); if (!dev) return; /* * Update pairing and paired status. Bonded status will be updated once * any link key come */ update_device_state(dev, rp->addr.type, status_mgmt2hal(status), false, !status, false); if (status == MGMT_STATUS_SUCCESS) queue_foreach(paired_cb_list, send_paired_notification, dev); } static uint8_t select_device_bearer(struct device *dev) { uint8_t res; if (dev->bredr && dev->le) { if (dev->le_seen > dev->bredr_seen) res = dev->bdaddr_type; else res = BDADDR_BREDR; } else { res = dev->bredr ? BDADDR_BREDR : dev->bdaddr_type; } DBG("Selected bearer %d", res); return res; } uint8_t bt_device_last_seen_bearer(const bdaddr_t *bdaddr) { struct device *dev; dev = find_device(bdaddr); if (!dev) return BDADDR_BREDR; return select_device_bearer(dev); } static void handle_create_bond_cmd(const void *buf, uint16_t len) { const struct hal_cmd_create_bond *cmd = buf; struct device *dev; uint8_t status; struct mgmt_cp_pair_device cp; dev = get_device_android(cmd->bdaddr); cp.io_cap = DEFAULT_IO_CAPABILITY; cp.addr.type = select_device_bearer(dev); bacpy(&cp.addr.bdaddr, &dev->bdaddr); /* TODO: Handle transport parameter */ if (cmd->transport > BT_TRANSPORT_LE) { status = HAL_STATUS_INVALID; goto fail; } if (device_is_paired(dev, cp.addr.type)) { status = HAL_STATUS_FAILED; goto fail; } if (mgmt_send(mgmt_if, MGMT_OP_PAIR_DEVICE, adapter.index, sizeof(cp), &cp, pair_device_complete, NULL, NULL) == 0) { status = HAL_STATUS_FAILED; goto fail; } status = HAL_STATUS_SUCCESS; update_device_state(dev, cp.addr.type, HAL_STATUS_SUCCESS, true, false, false); fail: ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_BLUETOOTH, HAL_OP_CREATE_BOND, status); } static void handle_cancel_bond_cmd(const void *buf, uint16_t len) { const struct hal_cmd_cancel_bond *cmd = buf; struct mgmt_addr_info cp; struct device *dev; uint8_t status; dev = find_device_android(cmd->bdaddr); if (!dev) { status = HAL_STATUS_FAILED; goto failed; } cp.type = select_device_bearer(dev); bacpy(&cp.bdaddr, &dev->bdaddr); if (mgmt_reply(mgmt_if, MGMT_OP_CANCEL_PAIR_DEVICE, adapter.index, sizeof(cp), &cp, NULL, NULL, NULL) == 0) { status = HAL_STATUS_FAILED; goto failed; } status = HAL_STATUS_SUCCESS; failed: ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_BLUETOOTH, HAL_OP_CANCEL_BOND, status); } static void send_unpaired_notification(void *data, void *user_data) { bt_unpaired_device_cb cb = data; struct mgmt_addr_info *addr = user_data; cb(&addr->bdaddr); } static void unpair_device_complete(uint8_t status, uint16_t length, const void *param, void *user_data) { const struct mgmt_rp_unpair_device *rp = param; struct device *dev; DBG("status %u", status); if (status != MGMT_STATUS_SUCCESS && status != MGMT_STATUS_NOT_PAIRED) return; dev = find_device(&rp->addr.bdaddr); if (!dev) return; update_device_state(dev, rp->addr.type, HAL_STATUS_SUCCESS, false, false, false); /* Cast rp->addr to (void *) since queue_foreach don't take const */ if (!dev->le_paired && !dev->bredr_paired) queue_foreach(unpaired_cb_list, send_unpaired_notification, (void *)&rp->addr); } static void handle_remove_bond_cmd(const void *buf, uint16_t len) { const struct hal_cmd_remove_bond *cmd = buf; struct mgmt_cp_unpair_device cp; struct device *dev; uint8_t status; dev = find_device_android(cmd->bdaddr); if (!dev) { status = HAL_STATUS_FAILED; goto failed; } cp.disconnect = 1; bacpy(&cp.addr.bdaddr, &dev->bdaddr); if (dev->le_paired) { cp.addr.type = dev->bdaddr_type; if (mgmt_send(mgmt_if, MGMT_OP_UNPAIR_DEVICE, adapter.index, sizeof(cp), &cp, unpair_device_complete, NULL, NULL) == 0) { status = HAL_STATUS_FAILED; goto failed; } } if (dev->bredr_paired) { cp.addr.type = BDADDR_BREDR; if (mgmt_send(mgmt_if, MGMT_OP_UNPAIR_DEVICE, adapter.index, sizeof(cp), &cp, unpair_device_complete, NULL, NULL) == 0) { status = HAL_STATUS_FAILED; goto failed; } } status = HAL_STATUS_SUCCESS; failed: ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_BLUETOOTH, HAL_OP_REMOVE_BOND, status); } static void handle_pin_reply_cmd(const void *buf, uint16_t len) { const struct hal_cmd_pin_reply *cmd = buf; uint8_t status; bdaddr_t bdaddr; char addr[18]; android2bdaddr(cmd->bdaddr, &bdaddr); ba2str(&bdaddr, addr); DBG("%s accept %u pin_len %u", addr, cmd->accept, cmd->pin_len); if (!cmd->accept && cmd->pin_len) { status = HAL_STATUS_INVALID; goto failed; } if (cmd->accept) { struct mgmt_cp_pin_code_reply rp; memset(&rp, 0, sizeof(rp)); bacpy(&rp.addr.bdaddr, &bdaddr); rp.addr.type = BDADDR_BREDR; rp.pin_len = cmd->pin_len; memcpy(rp.pin_code, cmd->pin_code, rp.pin_len); if (mgmt_reply(mgmt_if, MGMT_OP_PIN_CODE_REPLY, adapter.index, sizeof(rp), &rp, NULL, NULL, NULL) == 0) { status = HAL_STATUS_FAILED; goto failed; } } else { struct mgmt_cp_pin_code_neg_reply rp; bacpy(&rp.addr.bdaddr, &bdaddr); rp.addr.type = BDADDR_BREDR; if (mgmt_reply(mgmt_if, MGMT_OP_PIN_CODE_NEG_REPLY, adapter.index, sizeof(rp), &rp, NULL, NULL, NULL) == 0) { status = HAL_STATUS_FAILED; goto failed; } } status = HAL_STATUS_SUCCESS; failed: ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_BLUETOOTH, HAL_OP_PIN_REPLY, status); } static uint8_t user_confirm_reply(const bdaddr_t *bdaddr, uint8_t type, bool accept) { struct mgmt_addr_info cp; uint16_t opcode; if (accept) opcode = MGMT_OP_USER_CONFIRM_REPLY; else opcode = MGMT_OP_USER_CONFIRM_NEG_REPLY; bacpy(&cp.bdaddr, bdaddr); cp.type = type; if (mgmt_reply(mgmt_if, opcode, adapter.index, sizeof(cp), &cp, NULL, NULL, NULL) > 0) return HAL_STATUS_SUCCESS; return HAL_STATUS_FAILED; } static uint8_t user_passkey_reply(const bdaddr_t *bdaddr, uint8_t type, bool accept, uint32_t passkey) { unsigned int id; if (accept) { struct mgmt_cp_user_passkey_reply cp; memset(&cp, 0, sizeof(cp)); bacpy(&cp.addr.bdaddr, bdaddr); cp.addr.type = type; cp.passkey = cpu_to_le32(passkey); id = mgmt_reply(mgmt_if, MGMT_OP_USER_PASSKEY_REPLY, adapter.index, sizeof(cp), &cp, NULL, NULL, NULL); } else { struct mgmt_cp_user_passkey_neg_reply cp; memset(&cp, 0, sizeof(cp)); bacpy(&cp.addr.bdaddr, bdaddr); cp.addr.type = type; id = mgmt_reply(mgmt_if, MGMT_OP_USER_PASSKEY_NEG_REPLY, adapter.index, sizeof(cp), &cp, NULL, NULL, NULL); } if (id == 0) return HAL_STATUS_FAILED; return HAL_STATUS_SUCCESS; } static void handle_ssp_reply_cmd(const void *buf, uint16_t len) { const struct hal_cmd_ssp_reply *cmd = buf; struct device *dev; uint8_t status; char addr[18]; /* TODO should parameters sanity be verified here? */ dev = find_device_android(cmd->bdaddr); if (!dev) return; ba2str(&dev->bdaddr, addr); DBG("%s variant %u accept %u", addr, cmd->ssp_variant, cmd->accept); switch (cmd->ssp_variant) { case HAL_SSP_VARIANT_CONFIRM: case HAL_SSP_VARIANT_CONSENT: status = user_confirm_reply(&dev->bdaddr, select_device_bearer(dev), cmd->accept); break; case HAL_SSP_VARIANT_ENTRY: status = user_passkey_reply(&dev->bdaddr, select_device_bearer(dev), cmd->accept, cmd->passkey); break; case HAL_SSP_VARIANT_NOTIF: status = HAL_STATUS_SUCCESS; break; default: status = HAL_STATUS_INVALID; break; } ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_BLUETOOTH, HAL_OP_SSP_REPLY, status); } static void handle_get_remote_services_cmd(const void *buf, uint16_t len) { const struct hal_cmd_get_remote_services *cmd = buf; uint8_t status; bdaddr_t addr; android2bdaddr(&cmd->bdaddr, &addr); status = browse_remote_sdp(&addr); ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_BLUETOOTH, HAL_OP_GET_REMOTE_SERVICES, status); } static uint8_t get_device_uuids(struct device *dev) { send_device_uuids_notif(dev); return HAL_STATUS_SUCCESS; } static uint8_t get_device_class(struct device *dev) { send_device_property(dev, HAL_PROP_DEVICE_CLASS, sizeof(dev->class), &dev->class); return HAL_STATUS_SUCCESS; } static uint8_t get_device_type(struct device *dev) { uint8_t type = get_device_android_type(dev); send_device_property(dev, HAL_PROP_DEVICE_TYPE, sizeof(type), &type); return HAL_STATUS_SUCCESS; } static uint8_t get_device_service_rec(struct device *dev) { DBG("Not implemented"); /* TODO */ return HAL_STATUS_FAILED; } static uint8_t get_device_friendly_name(struct device *dev) { if (!dev->friendly_name) return HAL_STATUS_FAILED; send_device_property(dev, HAL_PROP_DEVICE_FRIENDLY_NAME, strlen(dev->friendly_name), dev->friendly_name); return HAL_STATUS_SUCCESS; } static uint8_t get_device_rssi(struct device *dev) { if (!dev->rssi) return HAL_STATUS_FAILED; send_device_property(dev, HAL_PROP_DEVICE_RSSI, sizeof(dev->rssi), &dev->rssi); return HAL_STATUS_SUCCESS; } static uint8_t get_device_version_info(struct device *dev) { DBG("Not implemented"); /* TODO */ return HAL_STATUS_FAILED; } static uint8_t get_device_timestamp(struct device *dev) { uint32_t timestamp; timestamp = device_timestamp(dev); send_device_property(dev, HAL_PROP_DEVICE_TIMESTAMP, sizeof(timestamp), ×tamp); return HAL_STATUS_SUCCESS; } static void get_remote_device_props(struct device *dev) { uint8_t buf[IPC_MTU]; struct hal_ev_remote_device_props *ev = (void *) buf; uint128_t uuids[g_slist_length(dev->uuids)]; uint8_t android_type; uint32_t timestamp; size_t size, i; GSList *l; memset(buf, 0, sizeof(buf)); size = sizeof(*ev); ev->status = HAL_STATUS_SUCCESS; get_device_android_addr(dev, ev->bdaddr); android_type = get_device_android_type(dev); size += fill_hal_prop(buf + size, HAL_PROP_DEVICE_TYPE, sizeof(android_type), &android_type); ev->num_props++; size += fill_hal_prop(buf + size, HAL_PROP_DEVICE_CLASS, sizeof(dev->class), &dev->class); ev->num_props++; if (dev->rssi) { size += fill_hal_prop(buf + size, HAL_PROP_DEVICE_RSSI, sizeof(dev->rssi), &dev->rssi); ev->num_props++; } size += fill_hal_prop(buf + size, HAL_PROP_DEVICE_NAME, strlen(dev->name), dev->name); ev->num_props++; if (dev->friendly_name) { size += fill_hal_prop(buf + size, HAL_PROP_DEVICE_FRIENDLY_NAME, strlen(dev->friendly_name), dev->friendly_name); ev->num_props++; } for (i = 0, l = dev->uuids; l; l = g_slist_next(l), i++) memcpy(&uuids[i], l->data, sizeof(uint128_t)); size += fill_hal_prop(buf + size, HAL_PROP_DEVICE_UUIDS, sizeof(uuids), uuids); ev->num_props++; timestamp = get_device_timestamp(dev); size += fill_hal_prop(buf + size, HAL_PROP_DEVICE_TIMESTAMP, sizeof(timestamp), ×tamp); ev->num_props++; ipc_send_notif(hal_ipc, HAL_SERVICE_ID_BLUETOOTH, HAL_EV_REMOTE_DEVICE_PROPS, size, buf); } static void send_bonded_devices_props(void) { GSList *l; for (l = bonded_devices; l; l = g_slist_next(l)) { struct device *dev = l->data; get_remote_device_props(dev); } } static void handle_enable_cmd(const void *buf, uint16_t len) { uint8_t status; /* * Framework expects all properties to be emitted while enabling * adapter */ get_adapter_properties(); /* Sent also properties of bonded devices */ send_bonded_devices_props(); if (adapter.current_settings & MGMT_SETTING_POWERED) { status = HAL_STATUS_SUCCESS; goto reply; } if (!set_mode(MGMT_OP_SET_POWERED, 0x01)) { status = HAL_STATUS_FAILED; goto reply; } status = HAL_STATUS_SUCCESS; reply: ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_BLUETOOTH, HAL_OP_ENABLE, status); } static void handle_disable_cmd(const void *buf, uint16_t len) { uint8_t status; if (!(adapter.current_settings & MGMT_SETTING_POWERED)) { status = HAL_STATUS_SUCCESS; goto reply; } /* Cancel all pending requests. Need it in case of ongoing paring */ mgmt_cancel_index(mgmt_if, adapter.index); if (!set_mode(MGMT_OP_SET_POWERED, 0x00)) { status = HAL_STATUS_FAILED; goto reply; } status = HAL_STATUS_SUCCESS; reply: ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_BLUETOOTH, HAL_OP_DISABLE, status); } static void handle_get_adapter_props_cmd(const void *buf, uint16_t len) { get_adapter_properties(); ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_BLUETOOTH, HAL_OP_GET_ADAPTER_PROPS, HAL_STATUS_SUCCESS); } static void handle_get_remote_device_props_cmd(const void *buf, uint16_t len) { const struct hal_cmd_get_remote_device_props *cmd = buf; struct device *dev; uint8_t status; dev = find_device_android(cmd->bdaddr); if (!dev) { status = HAL_STATUS_INVALID; goto failed; } get_remote_device_props(dev); status = HAL_STATUS_SUCCESS; failed: ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_BLUETOOTH, HAL_OP_GET_REMOTE_DEVICE_PROPS, status); } static void handle_get_remote_device_prop_cmd(const void *buf, uint16_t len) { const struct hal_cmd_get_remote_device_prop *cmd = buf; struct device *dev; uint8_t status; dev = find_device_android(cmd->bdaddr); if (!dev) { status = HAL_STATUS_INVALID; goto failed; } switch (cmd->type) { case HAL_PROP_DEVICE_NAME: status = get_device_name(dev); break; case HAL_PROP_DEVICE_UUIDS: status = get_device_uuids(dev); break; case HAL_PROP_DEVICE_CLASS: status = get_device_class(dev); break; case HAL_PROP_DEVICE_TYPE: status = get_device_type(dev); break; case HAL_PROP_DEVICE_SERVICE_REC: status = get_device_service_rec(dev); break; case HAL_PROP_DEVICE_FRIENDLY_NAME: status = get_device_friendly_name(dev); break; case HAL_PROP_DEVICE_RSSI: status = get_device_rssi(dev); break; case HAL_PROP_DEVICE_VERSION_INFO: status = get_device_version_info(dev); break; case HAL_PROP_DEVICE_TIMESTAMP: status = get_device_timestamp(dev); break; default: status = HAL_STATUS_FAILED; break; } if (status != HAL_STATUS_SUCCESS) error("Failed to get device property (type %u status %u)", cmd->type, status); failed: ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_BLUETOOTH, HAL_OP_GET_REMOTE_DEVICE_PROP, status); } static uint8_t set_device_friendly_name(struct device *dev, const uint8_t *val, uint16_t len) { DBG(""); g_free(dev->friendly_name); dev->friendly_name = g_strndup((const char *) val, len); if (dev->bredr_paired || dev->le_paired) store_device_info(dev, DEVICES_FILE); else store_device_info(dev, CACHE_FILE); return HAL_STATUS_SUCCESS; } static uint8_t set_device_version_info(struct device *dev) { DBG("Not implemented"); /* TODO */ return HAL_STATUS_FAILED; } static void handle_set_remote_device_prop_cmd(const void *buf, uint16_t len) { const struct hal_cmd_set_remote_device_prop *cmd = buf; struct device *dev; uint8_t status; if (len != sizeof(*cmd) + cmd->len) { error("Invalid set remote device prop cmd (0x%x), terminating", cmd->type); raise(SIGTERM); return; } dev = find_device_android(cmd->bdaddr); if (!dev) { status = HAL_STATUS_INVALID; goto failed; } switch (cmd->type) { case HAL_PROP_DEVICE_FRIENDLY_NAME: status = set_device_friendly_name(dev, cmd->val, cmd->len); break; case HAL_PROP_DEVICE_VERSION_INFO: status = set_device_version_info(dev); break; default: status = HAL_STATUS_FAILED; break; } if (status != HAL_STATUS_SUCCESS) error("Failed to set device property (type %u status %u)", cmd->type, status); failed: ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_BLUETOOTH, HAL_OP_SET_REMOTE_DEVICE_PROP, status); } static void handle_get_remote_service_rec_cmd(const void *buf, uint16_t len) { const struct hal_cmd_get_remote_service_rec *cmd = buf; uint8_t status; bdaddr_t addr; android2bdaddr(&cmd->bdaddr, &addr); status = find_remote_sdp_rec(&addr, cmd->uuid); ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_BLUETOOTH, HAL_OP_GET_REMOTE_SERVICE_REC, status); } static void handle_start_discovery_cmd(const void *buf, uint16_t len) { uint8_t status; if (!(adapter.current_settings & MGMT_SETTING_POWERED)) { status = HAL_STATUS_NOT_READY; goto failed; } switch (adapter.cur_discovery_type) { case SCAN_TYPE_DUAL: case SCAN_TYPE_BREDR: break; case SCAN_TYPE_NONE: if (!start_discovery(SCAN_TYPE_DUAL)) { status = HAL_STATUS_FAILED; goto failed; } break; case SCAN_TYPE_LE: if (get_supported_discovery_type() == SCAN_TYPE_LE) break; if (!stop_discovery(SCAN_TYPE_LE)) { status = HAL_STATUS_FAILED; goto failed; } adapter.exp_discovery_type = SCAN_TYPE_DUAL; break; } status = HAL_STATUS_SUCCESS; failed: ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_BLUETOOTH, HAL_OP_START_DISCOVERY, status); } static void handle_cancel_discovery_cmd(const void *buf, uint16_t len) { uint8_t status; if (!(adapter.current_settings & MGMT_SETTING_POWERED)) { status = HAL_STATUS_NOT_READY; goto failed; } switch (adapter.cur_discovery_type) { case SCAN_TYPE_NONE: break; case SCAN_TYPE_LE: if (get_supported_discovery_type() != SCAN_TYPE_LE) break; if (adapter.exp_discovery_type == SCAN_TYPE_LE) { status = HAL_STATUS_BUSY; goto failed; } if (!stop_discovery(SCAN_TYPE_LE)) { status = HAL_STATUS_FAILED; goto failed; } break; case SCAN_TYPE_DUAL: case SCAN_TYPE_BREDR: if (!stop_discovery(SCAN_TYPE_DUAL)) { status = HAL_STATUS_FAILED; goto failed; } if (adapter.exp_discovery_type != SCAN_TYPE_LE) adapter.exp_discovery_type = SCAN_TYPE_NONE; break; } status = HAL_STATUS_SUCCESS; failed: ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_BLUETOOTH, HAL_OP_CANCEL_DISCOVERY, status); } static void handle_dut_mode_conf_cmd(const void *buf, uint16_t len) { const struct hal_cmd_dut_mode_conf *cmd = buf; char path[FILENAME_MAX]; uint8_t status; int fd, ret; DBG("enable %u", cmd->enable); snprintf(path, sizeof(path), DUT_MODE_FILE, adapter.index); fd = open(path, O_WRONLY); if (fd < 0) { status = HAL_STATUS_FAILED; goto failed; } if (cmd->enable) ret = write(fd, "1", sizeof("1")); else ret = write(fd, "0", sizeof("0")); if (ret < 0) status = HAL_STATUS_FAILED; else status = HAL_STATUS_SUCCESS; close(fd); failed: ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_BLUETOOTH, HAL_OP_DUT_MODE_CONF, status); } static void handle_dut_mode_send_cmd(const void *buf, uint16_t len) { const struct hal_cmd_dut_mode_send *cmd = buf; if (len != sizeof(*cmd) + cmd->len) { error("Invalid dut mode send cmd, terminating"); raise(SIGTERM); return; } error("dut_mode_send not supported (cmd opcode %u)", cmd->opcode); /* TODO */ ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_BLUETOOTH, HAL_OP_DUT_MODE_SEND, HAL_STATUS_FAILED); } static void handle_le_test_mode_cmd(const void *buf, uint16_t len) { const struct hal_cmd_le_test_mode *cmd = buf; if (len != sizeof(*cmd) + cmd->len) { error("Invalid le test mode cmd, terminating"); raise(SIGTERM); return; } error("le_test_mode not supported (cmd opcode %u)", cmd->opcode); /* TODO */ ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_BLUETOOTH, HAL_OP_LE_TEST_MODE, HAL_STATUS_FAILED); } static void handle_get_connection_state(const void *buf, uint16_t len) { const struct hal_cmd_get_connection_state *cmd = buf; struct hal_rsp_get_connection_state rsp; struct device *dev; char address[18]; bdaddr_t bdaddr; android2bdaddr(cmd->bdaddr, &bdaddr); ba2str(&bdaddr, address); dev = find_device_android(cmd->bdaddr); if (dev && dev->connected) rsp.connection_state = 1; else rsp.connection_state = 0; DBG("%s %u", address, rsp.connection_state); ipc_send_rsp_full(hal_ipc, HAL_SERVICE_ID_BLUETOOTH, HAL_OP_GET_CONNECTION_STATE, sizeof(rsp), &rsp, -1); } static void handle_read_energy_info(const void *buf, uint16_t len) { DBG(""); /* TODO */ ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_BLUETOOTH, HAL_OP_READ_ENERGY_INFO, HAL_STATUS_UNSUPPORTED); } static const struct ipc_handler cmd_handlers[] = { /* HAL_OP_ENABLE */ { handle_enable_cmd, false, 0 }, /* HAL_OP_DISABLE */ { handle_disable_cmd, false, 0 }, /* HAL_OP_GET_ADAPTER_PROPS */ { handle_get_adapter_props_cmd, false, 0 }, /* HAL_OP_GET_ADAPTER_PROP */ { handle_get_adapter_prop_cmd, false, sizeof(struct hal_cmd_get_adapter_prop) }, /* HAL_OP_SET_ADAPTER_PROP */ { handle_set_adapter_prop_cmd, true, sizeof(struct hal_cmd_set_adapter_prop) }, /* HAL_OP_GET_REMOTE_DEVICE_PROPS */ { handle_get_remote_device_props_cmd, false, sizeof(struct hal_cmd_get_remote_device_props) }, /* HAL_OP_GET_REMOTE_DEVICE_PROP */ { handle_get_remote_device_prop_cmd, false, sizeof(struct hal_cmd_get_remote_device_prop) }, /* HAL_OP_SET_REMOTE_DEVICE_PROP */ { handle_set_remote_device_prop_cmd, true, sizeof(struct hal_cmd_set_remote_device_prop) }, /* HAL_OP_GET_REMOTE_SERVICE_REC */ { handle_get_remote_service_rec_cmd, false, sizeof(struct hal_cmd_get_remote_service_rec) }, /* HAL_OP_GET_REMOTE_SERVICES */ { handle_get_remote_services_cmd, false, sizeof(struct hal_cmd_get_remote_services) }, /* HAL_OP_START_DISCOVERY */ { handle_start_discovery_cmd, false, 0 }, /* HAL_OP_CANCEL_DISCOVERY */ { handle_cancel_discovery_cmd, false, 0 }, /* HAL_OP_CREATE_BOND */ { handle_create_bond_cmd, false, sizeof(struct hal_cmd_create_bond) }, /* HAL_OP_REMOVE_BOND */ { handle_remove_bond_cmd, false, sizeof(struct hal_cmd_remove_bond) }, /* HAL_OP_CANCEL_BOND */ {handle_cancel_bond_cmd, false, sizeof(struct hal_cmd_cancel_bond) }, /* HAL_OP_PIN_REPLY */ { handle_pin_reply_cmd, false, sizeof(struct hal_cmd_pin_reply) }, /* HAL_OP_SSP_REPLY */ { handle_ssp_reply_cmd, false, sizeof(struct hal_cmd_ssp_reply) }, /* HAL_OP_DUT_MODE_CONF */ { handle_dut_mode_conf_cmd, false, sizeof(struct hal_cmd_dut_mode_conf) }, /* HAL_OP_DUT_MODE_SEND */ { handle_dut_mode_send_cmd, true, sizeof(struct hal_cmd_dut_mode_send) }, /* HAL_OP_LE_TEST_MODE */ { handle_le_test_mode_cmd, true, sizeof(struct hal_cmd_le_test_mode) }, /* HAL_OP_GET_CONNECTION_STATE */ { handle_get_connection_state, false, sizeof(struct hal_cmd_get_connection_state) }, /* HAL_OP_READ_ENERGY_INFO */ { handle_read_energy_info, false, 0 }, }; bool bt_bluetooth_register(struct ipc *ipc, uint8_t mode) { uint32_t missing_settings; DBG("mode 0x%x", mode); unpaired_cb_list = queue_new(); paired_cb_list = queue_new(); missing_settings = adapter.current_settings ^ adapter.supported_settings; switch (mode) { case HAL_MODE_DEFAULT: if (missing_settings & MGMT_SETTING_BREDR) set_mode(MGMT_OP_SET_BREDR, 0x01); if (missing_settings & MGMT_SETTING_LE) set_mode(MGMT_OP_SET_LE, 0x01); break; case HAL_MODE_LE: /* Fail if controller does not support LE */ if (!(adapter.supported_settings & MGMT_SETTING_LE)) { error("LE Mode not supported by controller"); goto failed; } /* If LE it is not yet enabled then enable it */ if (!(adapter.current_settings & MGMT_SETTING_LE)) set_mode(MGMT_OP_SET_LE, 0x01); /* Disable BR/EDR if it is enabled */ if (adapter.current_settings & MGMT_SETTING_BREDR) set_mode(MGMT_OP_SET_BREDR, 0x00); break; case HAL_MODE_BREDR: /* Fail if controller does not support BR/EDR */ if (!(adapter.supported_settings & MGMT_SETTING_BREDR)) { error("BR/EDR Mode not supported"); goto failed; } /* Enable BR/EDR if it is not enabled */ if (missing_settings & MGMT_SETTING_BREDR) set_mode(MGMT_OP_SET_BREDR, 0x01); /* * According to Core Spec 4.0 host should not disable LE in * controller if it was enabled (Vol 2. Part E. 7.3.79). * Core Spec 4.1 removed this limitation and chips seem to be * handling this just fine anyway. */ if (adapter.current_settings & MGMT_SETTING_LE) set_mode(MGMT_OP_SET_LE, 0x00); break; default: error("Unknown mode 0x%x", mode); goto failed; } /* Requested mode is set now, let's enable secure connection */ if (missing_settings & MGMT_SETTING_SECURE_CONN) set_mode(MGMT_OP_SET_SECURE_CONN, 0x01); /* Set initial default name */ if (!adapter.name) { adapter.name = g_strdup(bt_config_get_model()); set_adapter_name((uint8_t *)adapter.name, strlen(adapter.name)); } hal_ipc = ipc; ipc_register(hal_ipc, HAL_SERVICE_ID_BLUETOOTH, cmd_handlers, G_N_ELEMENTS(cmd_handlers)); return true; failed: queue_destroy(unpaired_cb_list, NULL); unpaired_cb_list = NULL; queue_destroy(paired_cb_list, NULL); paired_cb_list = NULL; return false; } void bt_bluetooth_unregister(void) { DBG(""); g_slist_free_full(bonded_devices, (GDestroyNotify) free_device); bonded_devices = NULL; g_slist_free_full(cached_devices, (GDestroyNotify) free_device); cached_devices = NULL; ipc_unregister(hal_ipc, HAL_SERVICE_ID_CORE); hal_ipc = NULL; queue_destroy(unpaired_cb_list, NULL); unpaired_cb_list = NULL; queue_destroy(paired_cb_list, NULL); paired_cb_list = NULL; } bluez-5.82/android/PaxHeaders/health.h0000644000000000000000000000005014015011623014675 xustar0020 atime=1743516867 20 ctime=1743591279 bluez-5.82/android/health.h0000644000000000000000000000044314015011623014357 0ustar00rootroot/* SPDX-License-Identifier: LGPL-2.1-or-later */ /* * * BlueZ - Bluetooth protocol stack for Linux * * Copyright (C) 2014 Intel Corporation. All rights reserved. * * */ bool bt_health_register(struct ipc *ipc, const bdaddr_t *addr, uint8_t mode); void bt_health_unregister(void); bluez-5.82/android/PaxHeaders/pixit-opp.txt0000644000000000000000000000005012537515745015776 xustar0020 atime=1743516876 20 ctime=1743591289 bluez-5.82/android/pixit-opp.txt0000644000000000000000000000156412537515745015465 0ustar00rootrootOPP PIXIT for the PTS tool. PTS version: 6.1 * - different than PTS defaults & - should be set to IUT Bluetooth address Required PIXIT settings ------------------------------------------------------------------------------- Parameter Name Value ------------------------------------------------------------------------------- TSPX_supported_extension jpg (*) TSPX_unsupported_extension pts TSPX_client_class_of_device 100104 TSPX_server_class_of_device 100104 TSPX_auth_password 0000 TSPX_auth_user_id PTS TSPX_l2cap_psm 1003 TSPX_rfcomm_channel 8 TSPX_no_confirmations FALSE TSPX_bd_addr_iut 112233445566 (*&) TSPX_delete_link_key FALSE TSPX_pin_code 0000 TSPX_security_enabled FALSE TSPX_time_guard 300000 TSPX_use_implicit_send TRUE ------------------------------------------------------------------------------- bluez-5.82/android/PaxHeaders/gatt.h0000644000000000000000000000005014015011623014367 xustar0020 atime=1743516866 20 ctime=1743591279 bluez-5.82/android/gatt.h0000644000000000000000000000160214015011623014047 0ustar00rootroot/* SPDX-License-Identifier: LGPL-2.1-or-later */ /* * * BlueZ - Bluetooth protocol stack for Linux * * Copyright (C) 2014 Intel Corporation. All rights reserved. * * */ bool bt_gatt_register(struct ipc *ipc, const bdaddr_t *addr); void bt_gatt_unregister(void); typedef enum { GATT_CLIENT, GATT_SERVER, } gatt_type_t; typedef void (*gatt_conn_cb_t)(const bdaddr_t *addr, int err, void *attrib); unsigned int bt_gatt_register_app(const char *uuid, gatt_type_t type, gatt_conn_cb_t func); bool bt_gatt_unregister_app(unsigned int id); bool bt_gatt_connect_app(unsigned int id, const bdaddr_t *addr); bool bt_gatt_disconnect_app(unsigned int id, const bdaddr_t *addr); bool bt_gatt_set_security(const bdaddr_t *bdaddr, int sec_level); bool bt_gatt_add_autoconnect(unsigned int id, const bdaddr_t *addr); void bt_gatt_remove_autoconnect(unsigned int id, const bdaddr_t *addr); bluez-5.82/android/PaxHeaders/pts-hogp.txt0000644000000000000000000000005012537515745015606 xustar0020 atime=1743516876 20 ctime=1743591289 bluez-5.82/android/pts-hogp.txt0000644000000000000000000000552112537515745015272 0ustar00rootrootPTS test results for HoG PTS version: 6.1 Tested: 20-May-2015 Android version: 5.1 Results: PASS test passed FAIL test failed INC test is inconclusive N/A test is disabled due to PICS setup ------------------------------------------------------------------------------- Test Name Result Notes ------------------------------------------------------------------------------- TC_HGDS_HH_BV_01_I PASS TC_HGDS_HH_BV_02_I PASS TC_HGDS_HH_BV_03_I PASS TC_HGDS_HD_BV_01_I N/A TC_HGDS_HD_BV_02_I N/A TC_HGDR_RH_BV_01_I PASS TC_HGDC_RH_BV_01_I PASS TC_HGDC_RH_BV_02_I PASS TC_HGDC_RH_BV_03_I PASS TC_HGDC_RH_BV_04_I PASS TC_HGDC_RH_BV_05_I PASS TC_HGDC_RH_BV_06_I PASS TC_HGDC_RH_BV_07_I PASS TC_HGDC_HH_BV_08_I PASS TC_HGDC_HH_BV_14_I PASS TC_HGDC_HH_BV_15_I PASS TC_HGDC_HH_BV_16_I PASS TC_HGDC_BH_BV_09_I N/A TC_HGDC_BH_BV_10_I N/A TC_HGDC_BH_BV_11_I N/A TC_HGDC_BH_BV_12_I N/A TC_HGDC_BH_BV_13_I N/A TC_HGRF_RH_BV_01_I PASS TC_HGRF_RH_BV_02_I PASS TC_HGRF_RH_BV_03_I PASS TC_HGRF_RH_BV_04_I PASS TC_HGRF_RH_BV_05_I PASS TC_HGRF_RH_BV_19_I PASS TC_HGRF_RH_BV_06_I PASS TC_HGRF_RH_BV_07_I PASS TC_HGRF_RH_BV_08_I PASS TC_HGRF_RH_BV_09_I PASS TC_HGRF_HH_BV_10_I PASS TC_HGRF_HH_BV_11_I PASS TC_HGRF_HH_BV_12_I PASS TC_HGRF_BH_BV_13_I N/A TC_HGRF_BH_BV_14_I N/A TC_HGRF_BH_BV_15_I N/A TC_HGRF_BH_BV_16_I N/A TC_HGRF_BH_BV_17_I N/A TC_HGRF_HH_BV_18_I N/A TC_HGWF_RH_BV_01_I PASS haltest: hidhost connect hidhost set_report BTHH_INPUT_REPORT AAB3F8A6CD hidhost disconnect TC_HGWF_RH_BV_02_I PASS haltest: hidhost connect hidhost set_report BTHH_OUTPUT_REPORT EF907856341200 hidhost disconnect TC_HGWF_RH_BV_03_I PASS haltest: hidhost connect hidhost set_report BTHH_OUTPUT_REPORT EF907856341200 hidhost disconnect TC_HGWF_RH_BV_04_I PASS haltest: hidhost connect hidhost set_report BTHH_FEATURE_REPORT EA453F2D87 hidhost disconnect TC_HGWF_RH_BV_05_I N/A TC_HGWF_RH_BV_06_I N/A TC_HGWF_RH_BV_07_I N/A TC_HGWF_BH_BV_08_I N/A TC_HGWF_BH_BV_09_I N/A TC_HGWF_BH_BV_10_I N/A TC_HGWF_BH_BV_11_I N/A TC_HGCF_RH_BV_01_I PASS TC_HGCF_RH_BV_02_I PASS haltest: hidhost connect gattc search_service 1 gattc get_characteristic 1 {1812,2,1} gattc get_descriptor 1 {1812,2,1} {2a4d,5} gattc write_descriptor 1 {1812,2,1} {2a4d,5} {2902,1} 2 0x0000 0 gattc get_characteristic 1 {1812,5,1} gattc get_descriptor 1 {1812,5,1} {2a4d,4} gattc write_descriptor 1 {1812,5,1} {2a4d,4} {2902,1} 2 0x0000 0 hidhost disconnect TC_HGCF_BH_BV_03_I N/A TC_HGCF_BH_BV_04_I N/A TC_HGCF_BH_BV_05_I N/A TC_HGCF_BH_BV_06_I N/A TC_HGNF_RH_BV_01_I PASS TC_HGNF_RH_BI_01_I PASS TC_HGNF_RH_BI_01_I PASS TC_HGNF_BH_BV_02_I N/A TC_HGNF_BH_BV_03_I N/A TC_HGNF_BH_BI_01_I N/A ------------------------------------------------------------------------------- bluez-5.82/android/PaxHeaders/pics-gatt.txt0000644000000000000000000000005012537515745015740 xustar0020 atime=1743516876 20 ctime=1743591289 bluez-5.82/android/pics-gatt.txt0000644000000000000000000004101612537515745015423 0ustar00rootrootGATT PICS for the PTS tool. PTS version: 6.1 * - different than PTS defaults M - mandatory O - optional Generic Attribute Profile Role ------------------------------------------------------------------------------- Parameter Name Selected Description ------------------------------------------------------------------------------- TSPC_GATT_1_1 True Generic Attribute Profile Client (C.1) TSPC_GATT_1_2 True Generic Attribute Profile Server (C.2) TSPC_GATT_1A_1 True Complete GATT client (C.3) TSPC_GATT_1A_2 True Complete GATT server (C.4) ------------------------------------------------------------------------------- C.1: Optional to support IF TSPC_GATT_2_2; else IF TSPC_GATT_2_1 it is mandatory to support at least one of TSPC_GATT_1_1 OR TSPC_GATT_1_2 C.2: Mandatory to support IF TSPC_GATT_2_2; else IF TSPC_GATT_2_1 it is mandatory to support at least one of TSPC_GATT_1_1 OR TSPC_GATT_1_2 C.3: Optional IF TSPC_GATT_1_1 is supported, otherwise Excluded C.4: Optional IF TSPC_GATT_1_2 is supported, otherwise Excluded ------------------------------------------------------------------------------- ATT Bearer Transport ------------------------------------------------------------------------------- Parameter Name Selected Description ------------------------------------------------------------------------------- TSPC_GATT_2_1 True Attribute Protocol Supported over BR/EDR (L2CAP fixed channel support) (C.1) TSPC_GATT_2_2 True Attribute Protocol Supported over LE (C.2) ------------------------------------------------------------------------------- C.1: Mandatory IF (SUM ICS 12/2 OR SUM ICS 12/9) is supported, otherwise Excluded C.2: Mandatory IF (SUM ICS 12/7 OR SUM ICS 12/9) is supported, otherwise Excluded ------------------------------------------------------------------------------- Generic Attribute Profile Support ------------------------------------------------------------------------------- Parameter Name Selected Description ------------------------------------------------------------------------------- TSPC_GATT_3_1 True Client: Exchange MTU (C.2) TSPC_GATT_3_2 True Client: Discover All Primary Services (C.1) TSPC_GATT_3_3 True Client: Discover Primary Services Service UUID (C.1) TSPC_GATT_3_4 True Client: Find Included Services (C.1) TSPC_GATT_3_5 True Client: Discover All characteristics of a Service (C.1) TSPC_GATT_3_6 True Client: Discover Characteristics by UUID (C.1) TSPC_GATT_3_7 True Client: Discover All Characteristic Descriptors (C.1) TSPC_GATT_3_8 True Client: Read Characteristic Value (C.1) TSPC_GATT_3_9 True Client: Read using Characteristic UUID (C.1) TSPC_GATT_3_10 True Client: Read Long Characteristic Values (C.1) TSPC_GATT_3_11 False (*) Client: Read Multiple Characteristic Values (C.1) TSPC_GATT_3_12 True Client: Write without Response (C.1) TSPC_GATT_3_13 True Client: Signed Write Without Response (C.1) TSPC_GATT_3_14 True Client: Write Characteristic Value (C.1) TSPC_GATT_3_15 True Client: Write Long Characteristic Values (C.1) TSPC_GATT_3_16 True Client: Characteristic Value Reliable Writes (C.1) TSPC_GATT_3_17 True Client: Notifications (C.1) TSPC_GATT_3_18 True Client: Indications (M) TSPC_GATT_3_19 True Client: Read Characteristic Descriptors (C.1) TSPC_GATT_3_20 True Client: Read long Characteristic Descriptors (C.1) TSPC_GATT_3_21 True Client: Write Characteristic Descriptors (C.1) TSPC_GATT_3_22 True Client: Write Long Characteristic Descriptors (C.1) TSPC_GATT_3_23 True Client: Service Changed Characteristic (M) TSPC_GATT_3B_1 True Client: Primary Service Declaration (M) TSPC_GATT_3B_2 True Client: Secondary Service Declaration (M) TSPC_GATT_3B_3 True Client: Include Declaration (M) TSPC_GATT_3B_4 True Client: Characteristic Declaration (M) TSPC_GATT_3B_5 True Client: Characteristic Value Declaration (M) TSPC_GATT_3B_6 True Client: Characteristic Extended Properties (M) TSPC_GATT_3B_7 True Client: Characteristic User Description Descriptor (M) TSPC_GATT_3B_8 True Client: Client Characteristic Configuration Descriptor (M) TSPC_GATT_3B_9 True Client: Server Characteristic Configuration Descriptor (M) TSPC_GATT_3B_10 True Client: Characteristic Format Descriptor (M) TSPC_GATT_3B_11 True Client: Characteristic Aggregate Format Descriptor (M) TSPC_GATT_3B_12 True Client: Characteristic Format: Boolean (M) TSPC_GATT_3B_13 True Client: Characteristic Format: 2Bit (M) TSPC_GATT_3B_14 True Client: Characteristic Format: nibble (M) TSPC_GATT_3B_15 True Client: Characteristic Format: Uint8 (M) TSPC_GATT_3B_16 True Client: Characteristic Format: Uint12 (M) TSPC_GATT_3B_17 True Client: Characteristic Format: Uint16 (M) TSPC_GATT_3B_18 True Client: Characteristic Format: Uint24 (M) TSPC_GATT_3B_19 True Client: Characteristic Format: Uint32 (M) TSPC_GATT_3B_20 True Client: Characteristic Format: Uint48 (M) TSPC_GATT_3B_21 True Client: Characteristic Format: Uint64 (M) TSPC_GATT_3B_22 True Client: Characteristic Format: Uint128 (M) TSPC_GATT_3B_23 True Client: Characteristic Format: Sint8 (M) TSPC_GATT_3B_24 True Client: Characteristic Format: Sint12 (M) TSPC_GATT_3B_25 True Client: Characteristic Format: Sint16 (M) TSPC_GATT_3B_26 True Client: Characteristic Format: Sint24 (M) TSPC_GATT_3B_27 True Client: Characteristic Format: Sint32 (M) TSPC_GATT_3B_28 True Client: Characteristic Format: Sint48 (M) TSPC_GATT_3B_29 True Client: Characteristic Format: Sint64 (M) TSPC_GATT_3B_30 True Client: Characteristic Format: Sint128 (M) TSPC_GATT_3B_31 True Client: Characteristic Format: Float32 (M) TSPC_GATT_3B_32 True Client: Characteristic Format: Float64 (M) TSPC_GATT_3B_33 True Client: Characteristic Format: SFLOAT (M) TSPC_GATT_3B_34 True Client: Characteristic Format: FLOAT (M) TSPC_GATT_3B_35 True Client: Characteristic Format: Duint16 (M) TSPC_GATT_3B_36 True Client: Characteristic Format: utf8s (M) TSPC_GATT_3B_37 True Client: Characteristic Format: utf16s (M) TSPC_GATT_3B_38 True Client: Characteristic Format: struct (M) ------------------------------------------------------------------------------- C.1: Mandatory IF TSPC_GATT_1_3 is supported, otherwise Optional C.2: Mandatory IF TSPC_GATT_1_3 AND TSPC_GATT_2_2 is supported, otherwise Excluded ------------------------------------------------------------------------------- Generic Attribute Profile Support, by Server ------------------------------------------------------------------------------- Parameter Name Selected Description ------------------------------------------------------------------------------- TSPC_GATT_4_1 True Server: Exchange MTU (C.4) TSPC_GATT_4_2 True Server: Discover All Primary Services (M) TSPC_GATT_4_3 True Server: Discover Primary Services Service UUID (M) TSPC_GATT_4_4 True Server: Find Included Services (M) TSPC_GATT_4_5 True Server: Discover All characteristics of a Service (M) TSPC_GATT_4_6 True Server: Discover Characteristics by UUID (M) TSPC_GATT_4_7 True Server: Discover All Characteristic Descriptors (M) TSPC_GATT_4_8 True Server: Read Characteristic Value (M) TSPC_GATT_4_9 True Server: Read using Characteristic UUID (M) TSPC_GATT_4_10 True Server: Read Long Characteristic Values (C.4) TSPC_GATT_4_11 False (*) Server: Read Multiple Characteristic Values (C.4) TSPC_GATT_4_12 True Server: Write without Response (C.2) TSPC_GATT_4_13 True Server: Signed Write Without Response (C.4) TSPC_GATT_4_14 True Server: Write Characteristic Value (C.3) TSPC_GATT_4_15 True Server: Write Long Characteristic Values (C.4) TSPC_GATT_4_16 True Server: Characteristic Value Reliable Writes (C.4) TSPC_GATT_4_17 True Server: Notifications (C.4) TSPC_GATT_4_18 True Server: Indications (C.1) TSPC_GATT_4_19 True Server: Read Characteristic Descriptors (C.4) TSPC_GATT_4_20 True Server: Read long Characteristic Descriptors (C.4) TSPC_GATT_4_21 True Server: Write Characteristic Descriptors (C.4) TSPC_GATT_4_22 True Server: Write Long Characteristic Descriptors (C.4) TSPC_GATT_4_23 True Server: Service Changed Characteristic (C.1) ------------------------------------------------------------------------------- C.1: Mandatory IF service definitions on the server can be added, changed, or removed, otherwise Optional C.2: Mandatory IF GATT TSPC_GATT_4_13 is supported, otherwise Optional C.3: Mandatory IF GATT TSPC_GATT_4_15 is supported, otherwise Optional C.4: Mandatory IF GATT TSPC_GATT_1_4 is supported, otherwise Optional ------------------------------------------------------------------------------- Profile Attribute Types and Characteristic Formats ------------------------------------------------------------------------------- Parameter Name Selected Description ------------------------------------------------------------------------------- TSPC_GATT_4B_1 True Server: Primary Service Declaration (M) TSPC_GATT_4B_2 True Server: Secondary Service Declaration (M) TSPC_GATT_4B_3 True Server: Include Declaration (M) TSPC_GATT_4B_4 True Server: Characteristic Declaration (M) TSPC_GATT_4B_5 True Server: Characteristic Value Declaration (M) TSPC_GATT_4B_6 True Server: Characteristic Extended Properties (M) TSPC_GATT_4B_7 True Server: Characteristic User Description Descriptor (M) TSPC_GATT_4B_8 True Server: Client Characteristic Configuration Descriptor (M) TSPC_GATT_4B_9 True Server: Server Characteristic Configuration Descriptor (M) TSPC_GATT_4B_10 True Server: Characteristic Format Descriptor (M) TSPC_GATT_4B_11 True Server: Characteristic Aggregate Format Descriptor (M) TSPC_GATT_4B_12 True Server: Characteristic Format: Boolean (M) TSPC_GATT_4B_13 True Server: Characteristic Format: 2Bit (M) TSPC_GATT_4B_14 True Server: Characteristic Format: nibble (M) TSPC_GATT_4B_15 True Server: Characteristic Format: Uint8 (M) TSPC_GATT_4B_16 True Server: Characteristic Format: Uint12 (M) TSPC_GATT_4B_17 True Server: Characteristic Format: Uint16 (M) TSPC_GATT_4B_18 True Server: Characteristic Format: Uint24 (M) TSPC_GATT_4B_19 True Server: Characteristic Format: Uint32 (M) TSPC_GATT_4B_20 True Server: Characteristic Format: Uint48 (M) TSPC_GATT_4B_21 True Server: Characteristic Format: Uint64 (M) TSPC_GATT_4B_22 True Server: Characteristic Format: Uint128 (M) TSPC_GATT_4B_23 True Server: Characteristic Format: Sint8 (M) TSPC_GATT_4B_24 True Server: Characteristic Format: Sint12 (M) TSPC_GATT_4B_25 True Server: Characteristic Format: Sint16 (M) TSPC_GATT_4B_26 True Server: Characteristic Format: Sint24 (M) TSPC_GATT_4B_27 True Server: Characteristic Format: Sint32 (M) TSPC_GATT_4B_28 True Server: Characteristic Format: Sint48 (M) TSPC_GATT_4B_29 True Server: Characteristic Format: Sint64 (M) TSPC_GATT_4B_30 True Server: Characteristic Format: Sint128 (M) TSPC_GATT_4B_31 True Server: Characteristic Format: Float32 (M) TSPC_GATT_4B_32 True Server: Characteristic Format: Float64 (M) TSPC_GATT_4B_33 True Server: Characteristic Format: SFLOAT (M) TSPC_GATT_4B_34 True Server: Characteristic Format: FLOAT (M) TSPC_GATT_4B_35 True Server: Characteristic Format: Duint16 (M) TSPC_GATT_4B_36 True Server: Characteristic Format: utf8s (M) TSPC_GATT_4B_37 True Server: Characteristic Format: utf16s (M) TSPC_GATT_4B_38 True Server: Characteristic Format: struct (M) ------------------------------------------------------------------------------- Generic Attribute Profile Service ------------------------------------------------------------------------------- Parameter Name Selected Description ------------------------------------------------------------------------------- TSPC_GATT_6_2 True Discover GATT Services using Service Discovery Profile (C.1) TSPC_GATT_6_3 True Publish SDP record for GATT services support via BR/EDR (C.2) ------------------------------------------------------------------------------- C.1: Mandatory IF TSPC_GATT_1_1 is supported, otherwise Excluded C.2: Mandatory IF TSPC_GATT_1_2 is supported, otherwise Excluded ------------------------------------------------------------------------------- Attribute Protocol Transport Security ------------------------------------------------------------------------------- Parameter Name Selected Description ------------------------------------------------------------------------------- TSPC_GATT_7_1 True Security Mode 4 (C.1) TSPC_GATT_7_2 True LE Security Mode 1 (C.2) TSPC_GATT_7_3 True LE Security Mode 2 (C.2) TSPC_GATT_7_4 True LE Authentication Procedure (C.2) TSPC_GATT_7_5 True LE connection data signing procedure (C.2) TSPC_GATT_7_6 True LE Authenticate signed data procedure (C.2) TSPC_GATT_7_7 True LE Authorization Procedure (C.2) ------------------------------------------------------------------------------- C.1: Mandatory IF TSPC_GATT_2_1 is supported, otherwise Excluded C.2: Optional IF TSPC_GATT_2_2 is supported, otherwise Excluded ------------------------------------------------------------------------------- Attribute Protocol Client Messages ------------------------------------------------------------------------------- Parameter Name Selected Description ------------------------------------------------------------------------------- TSPC_ATT_3_1 True Attribute Error Response (M) TSPC_ATT_3_2 True Exchange MTU Request (O) TSPC_ATT_3_4 True Find Information Request (O) TSPC_ATT_3_6 True Find by Type Value Request (O) TSPC_ATT_3_8 True Read by Type Request (O) TSPC_ATT_3_10 True Read Request (O) TSPC_ATT_3_12 True Read Blob Request (O) TSPC_ATT_3_14 False (*) Read Multiple Request (O) TSPC_ATT_3_16 True Read by Group Type Request (O) TSPC_ATT_3_17 True Read by Group Type Response (C.6) TSPC_ATT_3_18 True Write Request (O) TSPC_ATT_3_20 True Write Command (O) TSPC_ATT_3_21 True Signed Write Command (O) TSPC_ATT_3_22 True Prepare Write Request (O) TSPC_ATT_3_24 True Execute Write Request (C.8) TSPC_ATT_3_26 True Handle Value Notification (M) TSPC_ATT_3_28 True Handle Value Confirmation (M) ------------------------------------------------------------------------------- C.6: Mandatory IF TSPC_ATT_3_16 is supported, otherwise Excluded C.8: Mandatory IF TSPC_ATT_3_22 is supported, otherwise Excluded ------------------------------------------------------------------------------- Attribute Protocol Server Messages ------------------------------------------------------------------------------- Parameter Name Selected Description ------------------------------------------------------------------------------- TSPC_ATT_4_1 True Attribute Error Response (M) TSPC_ATT_4_3 True Exchange MTU Response (M) TSPC_ATT_4_5 True Find Information Response (M) TSPC_ATT_4_7 True Find by Type Value Response (M) TSPC_ATT_4_8 True Read by Type Request (M) TSPC_ATT_4_9 True Read by Type Response (M) TSPC_ATT_4_11 True Read Response (M) TSPC_ATT_4_15 False (*) Read Multiple Response (C.2) TSPC_ATT_4_17 True Read by Group Type Response (M) TSPC_ATT_4_19 True Write Response (C.3) TSPC_ATT_4_20 True Write Command (O) TSPC_ATT_4_21 True Signed Write Command (O) TSPC_ATT_4_23 True Prepare Write Response (C.4) TSPC_ATT_4_25 True Execute Write Response (C.4) TSPC_ATT_4_26 True Handle Value Notification (O) TSPC_ATT_4_27 True Handle Value Indication (O) ------------------------------------------------------------------------------- C.2: Mandatory IF TSPC_ATT_4_14 is supported, otherwise Excluded C.3: Mandatory IF TSPC_ATT_4_18 is supported, otherwise Excluded C.4: Mandatory IF TSPC_ATT_4_22 is supported, otherwise Excluded C.5: Mandatory IF TSPC_ATT_4_27 is supported, otherwise Excluded ------------------------------------------------------------------------------- Attribute Protocol Transport ------------------------------------------------------------------------------- Parameter Name Selected Description ------------------------------------------------------------------------------- TSPC_ATT_5_2 True LE Security Mode 1 (C.2) TSPC_ATT_5_4 True LE Authentication Procedure (C.2) TSPC_ATT_5_7 True LE Authorization Procedure (C.2) ------------------------------------------------------------------------------- C.2: Optional to support if 2/2 (Attribute Protocol Supported over LE), otherwise Excluded ------------------------------------------------------------------------------- Device Configuration ------------------------------------------------------------------------------- Parameter Name Selected Description ------------------------------------------------------------------------------- TSPC_GAP_0_2 True LE (C.2) ------------------------------------------------------------------------------- C.2: Mandatory IF (SUM ICS 34/2 (LE GAP) AND NOT SUM ICS 32/3 (BR/EDR GAP)) is supported, otherwise Excluded ------------------------------------------------------------------------------- bluez-5.82/android/PaxHeaders/pics-rfcomm.txt0000644000000000000000000000005012537515745016264 xustar0020 atime=1743516875 20 ctime=1743591288 bluez-5.82/android/pics-rfcomm.txt0000644000000000000000000000572212537515745015753 0ustar00rootrootRFCOMM PICS for the PTS tool. PTS version: 6.1 * - different than PTS defaults # - not yet implemented/supported M - mandatory O - optional Protocol Version ------------------------------------------------------------------------------- Parameter Name Selected Description ------------------------------------------------------------------------------- TSPC_RFCOMM_0_1 False RFCOMM 1.1 with TS 07.10 (C.1) TSPC_RFCOMM_0_2 True (*) RFCOMM 1.2 with TS 07.10 (C.1) ------------------------------------------------------------------------------- C.1: Mandatory to support one and only one of the protocol versions. ------------------------------------------------------------------------------- Supported Procedures ------------------------------------------------------------------------------- Parameter Name Selected Description ------------------------------------------------------------------------------- TSPC_RFCOMM_1_1 True (*) Initialize RFCOMM Session (C.2) TSPC_RFCOMM_1_2 True (*) Respond to Initialization of an RFCOMM Session (C.1) TSPC_RFCOMM_1_3 True Shutdown RFCOMM Session (M) TSPC_RFCOMM_1_4 True Respond to a Shutdown of an RFCOMM Session (M) TSPC_RFCOMM_1_5 True (*) Establish DLC (C.2) TSPC_RFCOMM_1_6 True (*) Respond to Establishment of a DLC (C.1) TSPC_RFCOMM_1_7 True Disconnect DLC (M) TSPC_RFCOMM_1_8 True Respond to Disconnection of a DLC (M) TSPC_RFCOMM_1_9 True Respond to and send MSC Command (M) TSPC_RFCOMM_1_10 True Initiate Transfer Information (M) TSPC_RFCOMM_1_11 True Respond to Test Command (M) TSPC_RFCOMM_1_12 True (*) Send Test Command (O) TSPC_RFCOMM_1_13 True React to Aggregate Flow Control (M) TSPC_RFCOMM_1_14 True Respond to RLS Command (M) TSPC_RFCOMM_1_15 False Send RLS Command (O) TSPC_RFCOMM_1_16 True Respond to PN Command (M) TSPC_RFCOMM_1_17 True (*) Send PN Command (C.3) TSPC_RFCOMM_1_18 True (*) Send Non-Supported Command (NSC) response (C.4) TSPC_RFCOMM_1_19 True Respond to RPN Command (M) TSPC_RFCOMM_1_20 True (*) Send RPN Command (O) TSPC_RFCOMM_1_21 True (*) Closing Multiplexer by First Sending a DISC Command (O) TSPC_RFCOMM_1_22 True Support of Credit Based Flow Control (M) ------------------------------------------------------------------------------- C.1: Mandatory if SPP 1/2 (Device B) is supported, otherwise Excluded. C.2: Mandatory if SPP 1/1 (Device A) is supported, otherwise Excluded. C.3: Mandatory if SPP 1/1 (Device A) is supported, otherwise Optional. C.4: Mandatory to support if TSPC_RFCOMM_0_2 is supported, otherwise Optional. ------------------------------------------------------------------------------- ------------------------------------------------------------------------------- Parameter Name Selected Description ------------------------------------------------------------------------------- TSPC_SPP_2_1 False Channel the tester should use when connecting to the IUT (Default "") ------------------------------------------------------------------------------- bluez-5.82/android/PaxHeaders/tester-main.c0000644000000000000000000000005014711225433015664 xustar0020 atime=1743516864 20 ctime=1743591278 bluez-5.82/android/tester-main.c0000644000000000000000000024727114711225433015362 0ustar00rootroot// SPDX-License-Identifier: Apache-2.0 /* * Copyright (C) 2014 Intel Corporation * */ #define _GNU_SOURCE #include #include #include #include #include #include #include #include "lib/bluetooth.h" #include "lib/mgmt.h" #include "src/shared/util.h" #include "src/shared/tester.h" #include "src/shared/mgmt.h" #include "src/shared/queue.h" #include "emulator/bthost.h" #include "monitor/bt.h" #include "tester-main.h" static char exec_dir[PATH_MAX + 1]; static gint scheduled_cbacks_num; #define EMULATOR_SIGNAL_TIMEOUT 2 /* in seconds */ #define EMULATOR_SIGNAL "emulator_started" #define BT_TRANSPORT_UNKNOWN 0x00 static struct { uint16_t cb_num; const char *str; } cb_table[] = { DBG_CB(CB_BT_NONE), DBG_CB(CB_BT_ADAPTER_STATE_CHANGED), DBG_CB(CB_BT_ADAPTER_PROPERTIES), DBG_CB(CB_BT_REMOTE_DEVICE_PROPERTIES), DBG_CB(CB_BT_DEVICE_FOUND), DBG_CB(CB_BT_DISCOVERY_STATE_CHANGED), DBG_CB(CB_BT_PIN_REQUEST), DBG_CB(CB_BT_SSP_REQUEST), DBG_CB(CB_BT_BOND_STATE_CHANGED), DBG_CB(CB_BT_ACL_STATE_CHANGED), DBG_CB(CB_BT_THREAD_EVT), DBG_CB(CB_BT_DUT_MODE_RECV), DBG_CB(CB_BT_LE_TEST_MODE), /* Hidhost cb */ DBG_CB(CB_HH_CONNECTION_STATE), DBG_CB(CB_HH_HID_INFO), DBG_CB(CB_HH_PROTOCOL_MODE), DBG_CB(CB_HH_IDLE_TIME), DBG_CB(CB_HH_GET_REPORT), DBG_CB(CB_HH_VIRTUAL_UNPLUG), /* PAN cb */ DBG_CB(CB_PAN_CONTROL_STATE), DBG_CB(CB_PAN_CONNECTION_STATE), /* HDP cb */ DBG_CB(CB_HDP_APP_REG_STATE), DBG_CB(CB_HDP_CHANNEL_STATE), /* A2DP cb */ DBG_CB(CB_A2DP_CONN_STATE), DBG_CB(CB_A2DP_AUDIO_STATE), /* AVRCP */ DBG_CB(CB_AVRCP_PLAY_STATUS_REQ), DBG_CB(CB_AVRCP_PLAY_STATUS_RSP), DBG_CB(CB_AVRCP_REG_NOTIF_REQ), DBG_CB(CB_AVRCP_REG_NOTIF_RSP), DBG_CB(CB_AVRCP_GET_ATTR_REQ), DBG_CB(CB_AVRCP_GET_ATTR_RSP), /* Gatt client */ DBG_CB(CB_GATTC_REGISTER_CLIENT), DBG_CB(CB_GATTC_SCAN_RESULT), DBG_CB(CB_GATTC_OPEN), DBG_CB(CB_GATTC_CLOSE), DBG_CB(CB_GATTC_SEARCH_COMPLETE), DBG_CB(CB_GATTC_SEARCH_RESULT), DBG_CB(CB_GATTC_GET_CHARACTERISTIC), DBG_CB(CB_GATTC_GET_DESCRIPTOR), DBG_CB(CB_GATTC_GET_INCLUDED_SERVICE), DBG_CB(CB_GATTC_REGISTER_FOR_NOTIFICATION), DBG_CB(CB_GATTC_NOTIFY), DBG_CB(CB_GATTC_READ_CHARACTERISTIC), DBG_CB(CB_GATTC_WRITE_CHARACTERISTIC), DBG_CB(CB_GATTC_READ_DESCRIPTOR), DBG_CB(CB_GATTC_WRITE_DESCRIPTOR), DBG_CB(CB_GATTC_EXECUTE_WRITE), DBG_CB(CB_GATTC_READ_REMOTE_RSSI), DBG_CB(CB_GATTC_LISTEN), /* Gatt server */ DBG_CB(CB_GATTS_REGISTER_SERVER), DBG_CB(CB_GATTS_CONNECTION), DBG_CB(CB_GATTS_SERVICE_ADDED), DBG_CB(CB_GATTS_INCLUDED_SERVICE_ADDED), DBG_CB(CB_GATTS_CHARACTERISTIC_ADDED), DBG_CB(CB_GATTS_DESCRIPTOR_ADDED), DBG_CB(CB_GATTS_SERVICE_STARTED), DBG_CB(CB_GATTS_SERVICE_STOPPED), DBG_CB(CB_GATTS_SERVICE_DELETED), DBG_CB(CB_GATTS_REQUEST_READ), DBG_CB(CB_GATTS_REQUEST_WRITE), DBG_CB(CB_GATTS_REQUEST_EXEC_WRITE), DBG_CB(CB_GATTS_RESPONSE_CONFIRMATION), DBG_CB(CB_GATTS_INDICATION_SEND), /* Map client */ DBG_CB(CB_MAP_CLIENT_REMOTE_MAS_INSTANCES), /* Emulator callbacks */ DBG_CB(CB_EMU_CONFIRM_SEND_DATA), DBG_CB(CB_EMU_ENCRYPTION_ENABLED), DBG_CB(CB_EMU_ENCRYPTION_DISABLED), DBG_CB(CB_EMU_CONNECTION_REJECTED), DBG_CB(CB_EMU_VALUE_INDICATION), DBG_CB(CB_EMU_VALUE_NOTIFICATION), DBG_CB(CB_EMU_READ_RESPONSE), DBG_CB(CB_EMU_WRITE_RESPONSE), DBG_CB(CB_EMU_ATT_ERROR), }; static gboolean check_callbacks_called(gpointer user_data) { /* * Wait for all callbacks scheduled in current test context to execute * in main loop. This will avoid late callback calls after test case has * already failed or timed out. */ if (g_atomic_int_get(&scheduled_cbacks_num) == 0) { tester_teardown_complete(); return FALSE; } else if (scheduled_cbacks_num < 0) { tester_warn("Unscheduled callback called!"); return FALSE; } return TRUE; } static void check_daemon_term(void) { int status; pid_t pid; struct test_data *data = tester_get_data(); if (!data) return; pid = waitpid(data->bluetoothd_pid, &status, WNOHANG); if (pid != data->bluetoothd_pid) return; data->bluetoothd_pid = 0; if (WIFEXITED(status) && (WEXITSTATUS(status) == EXIT_SUCCESS)) { g_idle_add(check_callbacks_called, NULL); return; } tester_warn("Unexpected Daemon shutdown with status %d", status); } static gboolean signal_handler(GIOChannel *channel, GIOCondition cond, gpointer user_data) { struct signalfd_siginfo si; ssize_t result; int fd; if (cond & (G_IO_NVAL | G_IO_ERR | G_IO_HUP)) return FALSE; fd = g_io_channel_unix_get_fd(channel); result = read(fd, &si, sizeof(si)); if (result != sizeof(si)) return FALSE; switch (si.ssi_signo) { case SIGCHLD: check_daemon_term(); break; } return TRUE; } static guint setup_signalfd(void) { GIOChannel *channel; guint source; sigset_t mask; int fd; sigemptyset(&mask); sigaddset(&mask, SIGCHLD); if (sigprocmask(SIG_BLOCK, &mask, NULL) < 0) return 0; fd = signalfd(-1, &mask, 0); if (fd < 0) return 0; channel = g_io_channel_unix_new(fd); g_io_channel_set_close_on_unref(channel, TRUE); g_io_channel_set_encoding(channel, NULL, NULL); g_io_channel_set_buffered(channel, FALSE); source = g_io_add_watch(channel, G_IO_IN | G_IO_HUP | G_IO_ERR | G_IO_NVAL, signal_handler, NULL); g_io_channel_unref(channel); return source; } static void test_post_teardown(const void *test_data) { struct test_data *data = tester_get_data(); /* remove hook for encryption change */ hciemu_del_hook(data->hciemu, HCIEMU_HOOK_POST_EVT, 0x08); hciemu_unref(data->hciemu); data->hciemu = NULL; g_source_remove(data->signalfd); data->signalfd = 0; } static void bluetoothd_start(int hci_index) { char prg_name[PATH_MAX + 1 + 11]; char index[8]; char *prg_argv[5]; snprintf(prg_name, sizeof(prg_name), "%s/%s", exec_dir, "bluetoothd"); snprintf(index, sizeof(index), "%d", hci_index); prg_argv[0] = prg_name; prg_argv[1] = "-i"; prg_argv[2] = index; prg_argv[3] = "-d"; prg_argv[4] = NULL; if (!tester_use_debug()) fclose(stderr); execve(prg_argv[0], prg_argv, NULL); } static void emulator(int pipe, int hci_index) { static const char SYSTEM_SOCKET_PATH[] = "\0android_system"; char buf[1024]; struct sockaddr_un addr; struct timeval tv; int fd; ssize_t len; fd = socket(PF_LOCAL, SOCK_DGRAM | SOCK_CLOEXEC, 0); if (fd < 0) goto failed; tv.tv_sec = EMULATOR_SIGNAL_TIMEOUT; tv.tv_usec = 0; setsockopt(fd, SOL_SOCKET, SO_RCVTIMEO, (char *)&tv, sizeof(tv)); memset(&addr, 0, sizeof(addr)); addr.sun_family = AF_UNIX; memcpy(addr.sun_path, SYSTEM_SOCKET_PATH, sizeof(SYSTEM_SOCKET_PATH)); if (bind(fd, (struct sockaddr *) &addr, sizeof(addr)) < 0) { perror("Failed to bind system socket"); goto failed; } len = write(pipe, EMULATOR_SIGNAL, sizeof(EMULATOR_SIGNAL)); if (len != sizeof(EMULATOR_SIGNAL)) goto failed; memset(buf, 0, sizeof(buf)); len = read(fd, buf, sizeof(buf)); if (len <= 0 || strcmp(buf, "bluetooth.start=daemon")) goto failed; close(pipe); close(fd); return bluetoothd_start(hci_index); failed: close(pipe); if (fd >= 0) close(fd); } static void mgmt_debug(const char *str, void *user_data) { const char *prefix = user_data; tester_print("%s%s", prefix, str); } static bool hciemu_post_encr_hook(const void *data, uint16_t len, void *user_data) { struct step *step; /* * Expected data: status (1 octet) + conn. handle (2 octets) + * encryption flag (1 octet) */ if (len < 4) return true; step = g_new0(struct step, 1); step->callback = ((uint8_t *)data)[3] ? CB_EMU_ENCRYPTION_ENABLED : CB_EMU_ENCRYPTION_DISABLED; schedule_callback_verification(step); return true; } static void read_info_callback(uint8_t status, uint16_t length, const void *param, void *user_data) { struct test_data *data = tester_get_data(); const struct mgmt_rp_read_info *rp = param; char addr[18]; uint16_t manufacturer; uint32_t supported_settings, current_settings; tester_print("Read Info callback"); tester_print(" Status: 0x%02x", status); if (status || !param) { tester_pre_setup_failed(); return; } ba2str(&rp->bdaddr, addr); manufacturer = btohs(rp->manufacturer); supported_settings = btohl(rp->supported_settings); current_settings = btohl(rp->current_settings); tester_print(" Address: %s", addr); tester_print(" Version: 0x%02x", rp->version); tester_print(" Manufacturer: 0x%04x", manufacturer); tester_print(" Supported settings: 0x%08x", supported_settings); tester_print(" Current settings: 0x%08x", current_settings); tester_print(" Class: 0x%02x%02x%02x", rp->dev_class[2], rp->dev_class[1], rp->dev_class[0]); tester_print(" Name: %s", rp->name); tester_print(" Short name: %s", rp->short_name); if (strcmp(hciemu_get_address(data->hciemu), addr)) { tester_pre_setup_failed(); return; } /* set hook for encryption change */ hciemu_add_hook(data->hciemu, HCIEMU_HOOK_POST_EVT, 0x08, hciemu_post_encr_hook, NULL); tester_pre_setup_complete(); } static void index_added_callback(uint16_t index, uint16_t length, const void *param, void *user_data) { struct test_data *data = tester_get_data(); tester_print("Index Added callback"); tester_print(" Index: 0x%04x", index); data->mgmt_index = index; mgmt_send(data->mgmt, MGMT_OP_READ_INFO, data->mgmt_index, 0, NULL, read_info_callback, NULL, NULL); } static void index_removed_callback(uint16_t index, uint16_t length, const void *param, void *user_data) { struct test_data *data = tester_get_data(); tester_print("Index Removed callback"); tester_print(" Index: 0x%04x", index); if (index != data->mgmt_index) return; mgmt_unregister_index(data->mgmt, data->mgmt_index); mgmt_unref(data->mgmt); data->mgmt = NULL; tester_post_teardown_complete(); } static void read_index_list_callback(uint8_t status, uint16_t length, const void *param, void *user_data) { struct test_data *data = tester_get_data(); tester_print("Read Index List callback"); tester_print(" Status: 0x%02x", status); if (status || !param) { tester_pre_setup_failed(); return; } mgmt_register(data->mgmt, MGMT_EV_INDEX_ADDED, MGMT_INDEX_NONE, index_added_callback, NULL, NULL); mgmt_register(data->mgmt, MGMT_EV_INDEX_REMOVED, MGMT_INDEX_NONE, index_removed_callback, NULL, NULL); data->hciemu = hciemu_new(data->hciemu_type); if (!data->hciemu) { tester_warn("Failed to setup HCI emulation"); tester_pre_setup_failed(); return; } tester_print("New hciemu instance created"); } static void test_pre_setup(const void *test_data) { struct test_data *data = tester_get_data(); data->signalfd = setup_signalfd(); if (!data->signalfd) { tester_warn("Failed to setup signalfd"); tester_pre_setup_failed(); return; } data->mgmt = mgmt_new_default(); if (!data->mgmt) { tester_warn("Failed to setup management interface"); tester_pre_setup_failed(); return; } if (!tester_use_debug()) fclose(stderr); else mgmt_set_debug(data->mgmt, mgmt_debug, "mgmt: ", NULL); mgmt_send(data->mgmt, MGMT_OP_READ_INDEX_LIST, MGMT_INDEX_NONE, 0, NULL, read_index_list_callback, NULL, NULL); } static bool match_property(bt_property_t *exp_prop, bt_property_t *rec_prop, int prop_num) { if (exp_prop->type && (exp_prop->type != rec_prop->type)) return 0; if (exp_prop->len && (exp_prop->len != rec_prop->len)) { tester_debug("Property [%d] len don't match! received=%d, " "expected=%d", prop_num, rec_prop->len, exp_prop->len); return 0; } if (exp_prop->val && memcmp(exp_prop->val, rec_prop->val, exp_prop->len)) { tester_debug("Property [%d] value don't match!", prop_num); return 0; } return 1; } static bool match_mas_inst(btmce_mas_instance_t *exp_inst, btmce_mas_instance_t *rec_inst, int inst_num) { if (exp_inst->id && (exp_inst->id != rec_inst->id)) { tester_debug("MAS inst. [%d] id missmatch %d vs %d", inst_num, rec_inst->id, exp_inst->id); return 0; } if (exp_inst->scn && (exp_inst->scn != rec_inst->scn)) { tester_debug("MAS inst. [%d] scn missmatch %d vs %d", inst_num, rec_inst->scn, exp_inst->scn); return 0; } if (exp_inst->msg_types && (exp_inst->msg_types != rec_inst->msg_types)) { tester_debug("Mas inst. [%d] mesg type missmatch %d vs %d", inst_num, rec_inst->scn, exp_inst->scn); return 0; } if (exp_inst->p_name && memcmp(exp_inst->p_name, rec_inst->p_name, strlen(exp_inst->p_name))) { tester_debug("Mas inst. [%d] name don't match!", inst_num); return 0; } return 1; } static int verify_property(bt_property_t *exp_props, int exp_num_props, bt_property_t *rec_props, int rec_num_props) { int i, j; int exp_prop_to_find = exp_num_props; if (rec_num_props == 0) return 1; if (exp_num_props == 0) { tester_debug("Wrong number of expected properties given"); tester_test_failed(); return 1; } /* Get first exp prop to match and search for it */ for (i = 0; i < exp_num_props; i++) { for (j = 0; j < rec_num_props; j++) { if (match_property(&exp_props[i], &rec_props[j], i)) { exp_prop_to_find--; break; } } } return exp_prop_to_find; } static int verify_mas_inst(btmce_mas_instance_t *exp_inst, int exp_num_inst, btmce_mas_instance_t *rec_inst, int rec_num_inst) { int i, j; int exp_inst_to_find = exp_num_inst; if (rec_num_inst == 0) return 1; if (exp_num_inst == 0) { tester_debug("Wrong number of expected MAS instances given"); tester_test_failed(); return 1; } for (i = 0; i < exp_num_inst; i++) { for (j = 0; j < rec_num_inst; j++) { if (match_mas_inst(&exp_inst[i], &rec_inst[i], i)) { exp_inst_to_find--; break; } } } return exp_inst_to_find; } /* * Check each test case step if test case expected * data is set and match it with expected result. */ static bool verify_gatt_ids(btgatt_gatt_id_t *a, btgatt_gatt_id_t *b) { if (memcmp(&a->uuid, &b->uuid, sizeof(bt_uuid_t))) return false; if (a->inst_id != b->inst_id) return false; return true; } static bool verify_services(btgatt_srvc_id_t *a, btgatt_srvc_id_t *b) { if (a->is_primary != b->is_primary) return false; return verify_gatt_ids(&a->id, &b->id); } static bool match_data(struct step *step) { struct test_data *data = tester_get_data(); const struct step *exp; exp = queue_peek_head(data->steps); if (!exp) { /* Can occure while test passed already */ tester_debug("Cannot get step to match"); return false; } if (exp->action_status != step->action_status) { tester_debug("Action status don't match"); return false; } if (!exp->callback && !step->callback) return true; if (exp->callback != step->callback) { tester_debug("Callback type mismatch: %s vs %s", cb_table[step->callback].str, cb_table[exp->callback].str); return false; } if (exp->callback_result.state != step->callback_result.state) { tester_debug("Callback state mismatch: %d vs %d", step->callback_result.state, exp->callback_result.state); return false; } if (exp->callback_result.status != step->callback_result.status) { tester_debug("Callback status mismatch: %d vs %d", step->callback_result.status, exp->callback_result.status); return false; } if (exp->callback_result.mode != step->callback_result.mode) { tester_debug("Callback mode mismatch: %02x vs %02x", step->callback_result.mode, exp->callback_result.mode); return false; } if (exp->callback_result.report_size != step->callback_result.report_size) { tester_debug("Callback report size mismatch: %d vs %d", step->callback_result.report_size, exp->callback_result.report_size); return false; } if (exp->callback_result.ctrl_state != step->callback_result.ctrl_state) { tester_debug("Callback ctrl state mismatch: %d vs %d", step->callback_result.ctrl_state, exp->callback_result.ctrl_state); return false; } if (exp->callback_result.conn_state != step->callback_result.conn_state) { tester_debug("Callback connection state mismatch: %d vs %d", step->callback_result.conn_state, exp->callback_result.conn_state); return false; } if (exp->callback_result.local_role != step->callback_result.local_role) { tester_debug("Callback local_role mismatch: %d vs %d", step->callback_result.local_role, exp->callback_result.local_role); return false; } if (exp->callback_result.remote_role != step->callback_result.remote_role) { tester_debug("Callback remote_role mismatch: %d vs %d", step->callback_result.remote_role, exp->callback_result.remote_role); return false; } if (exp->callback_result.app_id != step->callback_result.app_id) { tester_debug("Callback app_id mismatch: %d vs %d", step->callback_result.app_id, exp->callback_result.app_id); return false; } if (exp->callback_result.channel_id != step->callback_result.channel_id) { tester_debug("Callback channel_id mismatch: %d vs %d", step->callback_result.channel_id, exp->callback_result.channel_id); return false; } if (exp->callback_result.mdep_cfg_index != step->callback_result.mdep_cfg_index) { tester_debug("Callback mdep_cfg_index mismatch: %d vs %d", step->callback_result.mdep_cfg_index, exp->callback_result.mdep_cfg_index); return false; } if (exp->callback_result.app_state != step->callback_result.app_state) { tester_debug("Callback app_state mismatch: %d vs %d", step->callback_result.app_state, exp->callback_result.app_state); return false; } if (exp->callback_result.channel_state != step->callback_result.channel_state) { tester_debug("Callback channel_state mismatch: %d vs %d", step->callback_result.channel_state, exp->callback_result.channel_state); return false; } if (exp->callback_result.av_conn_state != step->callback_result.av_conn_state) { tester_debug("Callback av conn state mismatch: 0x%x vs 0x%x", step->callback_result.av_conn_state, exp->callback_result.av_conn_state); return false; } if (exp->callback_result.av_audio_state != step->callback_result.av_audio_state) { tester_debug("Callback av audio state mismatch: 0x%x vs 0x%x", step->callback_result.av_audio_state, exp->callback_result.av_audio_state); return false; } if (exp->callback_result.song_length != step->callback_result.song_length) { tester_debug("Callback song_length mismatch: 0x%x vs 0x%x", step->callback_result.song_length, exp->callback_result.song_length); return false; } if (exp->callback_result.song_position != step->callback_result.song_position) { tester_debug("Callback song_position mismatch: 0x%x vs 0x%x", step->callback_result.song_position, exp->callback_result.song_position); return false; } if (exp->callback_result.play_status != step->callback_result.play_status) { tester_debug("Callback play_status mismatch: 0x%x vs 0x%x", step->callback_result.play_status, exp->callback_result.play_status); return false; } if (exp->callback_result.rc_index != step->callback_result.rc_index) { tester_debug("Callback rc_index mismatch"); return false; } if (exp->callback_result.num_of_attrs != step->callback_result.num_of_attrs) { tester_debug("Callback rc num of attrs mismatch"); return false; } if (exp->callback_result.attrs) { if (memcmp(step->callback_result.attrs, exp->callback_result.attrs, exp->callback_result.num_of_attrs * sizeof(btrc_element_attr_val_t))) { tester_debug("Callback rc element attributes doesn't match"); return false; } } if (exp->callback_result.pairing_variant != step->callback_result.pairing_variant) { tester_debug("Callback pairing result mismatch: %d vs %d", step->callback_result.pairing_variant, exp->callback_result.pairing_variant); return false; } if (exp->callback_result.adv_data != step->callback_result.adv_data) { tester_debug("Callback adv. data status mismatch: %d vs %d", step->callback_result.adv_data, exp->callback_result.adv_data); return false; } if (exp->callback_result.conn_id != step->callback_result.conn_id) { tester_debug("Callback conn_id mismatch: %d vs %d", step->callback_result.conn_id, exp->callback_result.conn_id); return false; } if (exp->callback_result.gatt_app_id != step->callback_result.gatt_app_id) { tester_debug("Callback gatt_app_id mismatch: %d vs %d", step->callback_result.gatt_app_id, exp->callback_result.gatt_app_id); return false; } if (exp->callback_result.properties && verify_property(exp->callback_result.properties, exp->callback_result.num_properties, step->callback_result.properties, step->callback_result.num_properties)) { tester_debug("Gatt properties don't match"); return false; } if (exp->callback_result.service && !verify_services(step->callback_result.service, exp->callback_result.service)) { tester_debug("Gatt service doesn't match"); return false; } if (exp->callback_result.characteristic) { btgatt_gatt_id_t *a; btgatt_gatt_id_t *b; a = step->callback_result.characteristic; b = exp->callback_result.characteristic; if (!verify_gatt_ids(a, b)) { tester_debug("Gatt char doesn't match"); return false; } } if (exp->callback_result.char_prop != step->callback_result.char_prop) { tester_debug("Gatt char prop mismatch: %d vs %d", step->callback_result.char_prop, exp->callback_result.char_prop); return false; } if (exp->callback_result.descriptor) { btgatt_gatt_id_t *a; btgatt_gatt_id_t *b; a = step->callback_result.descriptor; b = exp->callback_result.descriptor; if (!verify_gatt_ids(a, b)) { tester_debug("Gatt desc doesn't match"); return false; } } if (exp->callback_result.included) { if (!verify_services(step->callback_result.included, exp->callback_result.included)) { tester_debug("Gatt include srvc doesn't match"); return false; } } if (exp->callback_result.read_params) { if (memcmp(step->callback_result.read_params, exp->callback_result.read_params, sizeof(btgatt_read_params_t))) { tester_debug("Gatt read_param doesn't match"); return false; } } if (exp->callback_result.write_params) { if (memcmp(step->callback_result.write_params, exp->callback_result.write_params, sizeof(btgatt_write_params_t))) { tester_debug("Gatt write_param doesn't match"); return false; } if (exp->callback_result.notification_registered != step->callback_result.notification_registered) { tester_debug("Gatt registered flag mismatch"); return false; } if (exp->callback_result.notify_params) { if (memcmp(step->callback_result.notify_params, exp->callback_result.notify_params, sizeof(btgatt_notify_params_t))) { tester_debug("Gatt notify_param doesn't match"); return false; } } } if (exp->callback_result.connected != step->callback_result.connected) { tester_debug("Gatt server conn status mismatch: %d vs %d", step->callback_result.connected, exp->callback_result.connected); return false; } if (exp->callback_result.attr_handle && step->callback_result.attr_handle) if (*exp->callback_result.attr_handle != *step->callback_result.attr_handle) { tester_debug("Gatt attribute handle mismatch: %d vs %d", *step->callback_result.attr_handle, *exp->callback_result.attr_handle); return false; } if (exp->callback_result.srvc_handle && step->callback_result.srvc_handle) if (*exp->callback_result.srvc_handle != *step->callback_result.srvc_handle) { tester_debug("Gatt service handle mismatch: %d vs %d", *step->callback_result.srvc_handle, *exp->callback_result.srvc_handle); return false; } if (exp->callback_result.inc_srvc_handle && step->callback_result.inc_srvc_handle) if (*exp->callback_result.inc_srvc_handle != *step->callback_result.inc_srvc_handle) { tester_debug("Gatt inc. srvc handle mismatch: %d vs %d", *step->callback_result.inc_srvc_handle, *exp->callback_result.inc_srvc_handle); return false; } if (exp->callback_result.uuid && step->callback_result.uuid) if (memcmp(exp->callback_result.uuid, step->callback_result.uuid, sizeof(*exp->callback_result.uuid))) { tester_debug("Uuid mismatch"); return false; } if (exp->callback_result.trans_id != step->callback_result.trans_id) { tester_debug("Gatt trans id mismatch: %d vs %d", exp->callback_result.trans_id, step->callback_result.trans_id); return false; } if (exp->callback_result.offset != step->callback_result.offset) { tester_debug("Gatt offset mismatch: %d vs %d", exp->callback_result.offset, step->callback_result.offset); return false; } if (exp->callback_result.is_long != step->callback_result.is_long) { tester_debug("Gatt is long attr value flag mismatch: %d vs %d", exp->callback_result.is_long, step->callback_result.is_long); return false; } if (exp->callback_result.length > 0) { if (exp->callback_result.length != step->callback_result.length) { tester_debug("Gatt attr length mismatch: %d vs %d", exp->callback_result.length, step->callback_result.length); return false; } if (!exp->callback_result.value || !step->callback_result.value) { tester_debug("Gatt attr values are wrong set"); return false; } if (!memcmp(exp->callback_result.value, step->callback_result.value, exp->callback_result.length)) { tester_debug("Gatt attr value mismatch"); return false; } } if (exp->callback_result.need_rsp != step->callback_result.need_rsp) { tester_debug("Gatt need response value flag mismatch: %d vs %d", exp->callback_result.need_rsp, step->callback_result.need_rsp); return false; } if (exp->callback_result.is_prep != step->callback_result.is_prep) { tester_debug("Gatt is prepared value flag mismatch: %d vs %d", exp->callback_result.is_prep, step->callback_result.is_prep); return false; } if (exp->callback_result.num_mas_instances != step->callback_result.num_mas_instances) { tester_debug("Mas instance count mismatch: %d vs %d", exp->callback_result.num_mas_instances, step->callback_result.num_mas_instances); return false; } if (exp->callback_result.mas_instances && verify_mas_inst(exp->callback_result.mas_instances, exp->callback_result.num_mas_instances, step->callback_result.mas_instances, step->callback_result.num_mas_instances)) { tester_debug("Mas instances don't match"); return false; } if (exp->callback_result.error != step->callback_result.error) { tester_debug("Err mismatch: %d vs %d", exp->callback_result.error, step->callback_result.error); return false; } if (exp->store_srvc_handle) memcpy(exp->store_srvc_handle, step->callback_result.srvc_handle, sizeof(*exp->store_srvc_handle)); if (exp->store_char_handle) memcpy(exp->store_char_handle, step->callback_result.char_handle, sizeof(*exp->store_char_handle)); return true; } static void init_test_steps(struct test_data *data) { const struct test_case *test_steps = data->test_data; int i = 0; for (i = 0; i < test_steps->step_num; i++) queue_push_tail(data->steps, (void *) &(test_steps->step[i])); tester_print("tester: Number of test steps=%d", queue_length(data->steps)); } /* * Each test case step should be verified, if match with * expected result tester should go to next test step. */ static void verify_step(struct step *step, queue_destroy_func_t cleanup_cb) { struct test_data *data = tester_get_data(); const struct test_case *test_steps = data->test_data; struct step *next_step; tester_debug("tester: STEP[%d] check", test_steps->step_num-queue_length(data->steps) + 1); if (step && !match_data(step)) { if (cleanup_cb) cleanup_cb(step); return; } queue_pop_head(data->steps); if (cleanup_cb) cleanup_cb(step); tester_debug("tester: STEP[%d] pass", test_steps->step_num-queue_length(data->steps)); if (queue_isempty(data->steps)) { tester_print("tester: All steps done, passing"); tester_test_passed(); return; } /* goto next step action if declared in step */ next_step = queue_peek_head(data->steps); if (next_step->action) next_step->action(); } /* * NOTICE: * Its mandatory for callback to set proper step.callback value so that * step verification could pass and move to next test step */ static void free_properties(struct step *step) { bt_property_t *properties = step->callback_result.properties; int num_properties = step->callback_result.num_properties; int i; for (i = 0; i < num_properties; i++) g_free(properties[i].val); g_free(properties); } static void free_mas_instances(struct step *step) { btmce_mas_instance_t *mas_instances; int num_instances; int i; mas_instances = step->callback_result.mas_instances; num_instances = step->callback_result.num_mas_instances; for (i = 0; i < num_instances; i++) g_free(mas_instances[i].p_name); g_free(mas_instances); } static void destroy_callback_step(void *data) { struct step *step = data; if (step->callback_result.properties) free_properties(step); if (step->callback_result.service) free(step->callback_result.service); if (step->callback_result.characteristic) free(step->callback_result.characteristic); if (step->callback_result.descriptor) free(step->callback_result.descriptor); if (step->callback_result.included) free(step->callback_result.included); if (step->callback_result.read_params) free(step->callback_result.read_params); if (step->callback_result.write_params) free(step->callback_result.write_params); if (step->callback_result.notify_params) free(step->callback_result.notify_params); if (step->callback_result.srvc_handle) free(step->callback_result.srvc_handle); if (step->callback_result.inc_srvc_handle) free(step->callback_result.inc_srvc_handle); if (step->callback_result.uuid) free(step->callback_result.uuid); if (step->callback_result.char_handle) free(step->callback_result.char_handle); if (step->callback_result.desc_handle) free(step->callback_result.desc_handle); if (step->callback_result.attr_handle) free(step->callback_result.attr_handle); if (step->callback_result.value) free(step->callback_result.value); if (step->callback_result.mas_instances) free_mas_instances(step); g_free(step); g_atomic_int_dec_and_test(&scheduled_cbacks_num); } static gboolean verify_action(gpointer user_data) { struct step *step = user_data; verify_step(step, g_free); return FALSE; } static gboolean verify_callback(gpointer user_data) { struct test_data *data = tester_get_data(); struct step *step = user_data; /* Return if callback came when all steps are already verified */ if (queue_isempty(data->steps)) { destroy_callback_step(step); return FALSE; } /* * TODO: This may call action from next step before callback data * from previous step was freed. */ verify_step(step, destroy_callback_step); return FALSE; } void schedule_callback_verification(struct step *step) { g_atomic_int_inc(&scheduled_cbacks_num); g_idle_add(verify_callback, step); } void schedule_action_verification(struct step *step) { g_idle_add_full(G_PRIORITY_HIGH_IDLE, verify_action, step, NULL); } static void adapter_state_changed_cb(bt_state_t state) { struct step *step = g_new0(struct step, 1); step->callback_result.state = state; step->callback = CB_BT_ADAPTER_STATE_CHANGED; schedule_callback_verification(step); } static bt_property_t *copy_properties(int num_properties, bt_property_t *properties) { int i; bt_property_t *props = g_new0(bt_property_t, num_properties); for (i = 0; i < num_properties; i++) { props[i].type = properties[i].type; props[i].len = properties[i].len; props[i].val = util_memdup(properties[i].val, properties[i].len); } return props; } static bt_property_t *repack_properties(int num_properties, bt_property_t **properties) { int i; bt_property_t *props = g_new0(bt_property_t, num_properties); for (i = 0; i < num_properties; i++) { props[i].type = properties[i]->type; props[i].len = properties[i]->len; props[i].val = util_memdup(properties[i]->val, properties[i]->len); } return props; } static bt_property_t *create_property(bt_property_type_t type, void *val, int len) { bt_property_t *prop = g_new0(bt_property_t, 1); prop->type = type; prop->len = len; prop->val = util_memdup(val, len); return prop; } static void adapter_properties_cb(bt_status_t status, int num_properties, bt_property_t *properties) { struct step *step = g_new0(struct step, 1); step->callback_result.status = status; step->callback_result.num_properties = num_properties; step->callback_result.properties = copy_properties(num_properties, properties); step->callback = CB_BT_ADAPTER_PROPERTIES; schedule_callback_verification(step); } static void discovery_state_changed_cb(bt_discovery_state_t state) { struct step *step = g_new0(struct step, 1); step->callback = CB_BT_DISCOVERY_STATE_CHANGED; step->callback_result.state = state; schedule_callback_verification(step); } static void device_found_cb(int num_properties, bt_property_t *properties) { struct step *step = g_new0(struct step, 1); step->callback_result.num_properties = num_properties; step->callback_result.properties = copy_properties(num_properties, properties); step->callback = CB_BT_DEVICE_FOUND; schedule_callback_verification(step); } static void remote_device_properties_cb(bt_status_t status, bt_bdaddr_t *bd_addr, int num_properties, bt_property_t *properties) { struct step *step = g_new0(struct step, 1); step->callback_result.num_properties = num_properties; step->callback_result.properties = copy_properties(num_properties, properties); step->callback = CB_BT_REMOTE_DEVICE_PROPERTIES; schedule_callback_verification(step); } static void bond_state_changed_cb(bt_status_t status, bt_bdaddr_t *remote_bd_addr, bt_bond_state_t state) { struct step *step = g_new0(struct step, 1); step->callback_result.status = status; step->callback_result.state = state; /* Utilize property verification mechanism for bdaddr */ step->callback_result.num_properties = 1; step->callback_result.properties = create_property(BT_PROPERTY_BDADDR, remote_bd_addr, sizeof(*remote_bd_addr)); step->callback = CB_BT_BOND_STATE_CHANGED; schedule_callback_verification(step); } static void pin_request_cb(bt_bdaddr_t *remote_bd_addr, bt_bdname_t *bd_name, uint32_t cod) { struct step *step = g_new0(struct step, 1); bt_property_t *props[3]; step->callback = CB_BT_PIN_REQUEST; /* Utilize property verification mechanism for those */ props[0] = create_property(BT_PROPERTY_BDADDR, remote_bd_addr, sizeof(*remote_bd_addr)); props[1] = create_property(BT_PROPERTY_BDNAME, bd_name->name, strlen((char *) bd_name->name)); props[2] = create_property(BT_PROPERTY_CLASS_OF_DEVICE, &cod, sizeof(cod)); step->callback_result.num_properties = 3; step->callback_result.properties = repack_properties(3, props); g_free(props[0]->val); g_free(props[0]); g_free(props[1]->val); g_free(props[1]); g_free(props[2]->val); g_free(props[2]); schedule_callback_verification(step); } static void ssp_request_cb(bt_bdaddr_t *remote_bd_addr, bt_bdname_t *bd_name, uint32_t cod, bt_ssp_variant_t pairing_variant, uint32_t pass_key) { struct step *step = g_new0(struct step, 1); bt_property_t *props[3]; step->callback = CB_BT_SSP_REQUEST; /* Utilize property verification mechanism for those */ props[0] = create_property(BT_PROPERTY_BDADDR, remote_bd_addr, sizeof(*remote_bd_addr)); props[1] = create_property(BT_PROPERTY_BDNAME, bd_name->name, strlen((char *) bd_name->name)); props[2] = create_property(BT_PROPERTY_CLASS_OF_DEVICE, &cod, sizeof(cod)); step->callback_result.num_properties = 3; step->callback_result.properties = repack_properties(3, props); g_free(props[0]->val); g_free(props[0]); g_free(props[1]->val); g_free(props[1]); g_free(props[2]->val); g_free(props[2]); schedule_callback_verification(step); } static void acl_state_changed_cb(bt_status_t status, bt_bdaddr_t *remote_bd_addr, bt_acl_state_t state) { struct step *step = g_new0(struct step, 1); step->callback = CB_BT_ACL_STATE_CHANGED; step->callback_result.status = status; step->callback_result.state = state; schedule_callback_verification(step); } static bt_callbacks_t bt_callbacks = { .size = sizeof(bt_callbacks), .adapter_state_changed_cb = adapter_state_changed_cb, .adapter_properties_cb = adapter_properties_cb, .remote_device_properties_cb = remote_device_properties_cb, .device_found_cb = device_found_cb, .discovery_state_changed_cb = discovery_state_changed_cb, .pin_request_cb = pin_request_cb, .ssp_request_cb = ssp_request_cb, .bond_state_changed_cb = bond_state_changed_cb, .acl_state_changed_cb = acl_state_changed_cb, .thread_evt_cb = NULL, .dut_mode_recv_cb = NULL, .le_test_mode_cb = NULL, .energy_info_cb = NULL, }; static void hidhost_connection_state_cb(bt_bdaddr_t *bd_addr, bthh_connection_state_t state) { struct step *step = g_new0(struct step, 1); step->callback = CB_HH_CONNECTION_STATE; step->callback_result.state = state; schedule_callback_verification(step); } static void hidhost_virtual_unplug_cb(bt_bdaddr_t *bd_addr, bthh_status_t status) { struct step *step = g_new0(struct step, 1); step->callback = CB_HH_VIRTUAL_UNPLUG; step->callback_result.status = status; schedule_callback_verification(step); } static void hidhost_protocol_mode_cb(bt_bdaddr_t *bd_addr, bthh_status_t status, bthh_protocol_mode_t mode) { struct step *step = g_new0(struct step, 1); step->callback = CB_HH_PROTOCOL_MODE; step->callback_result.status = status; step->callback_result.mode = mode; /* TODO: add bdaddr to verify? */ schedule_callback_verification(step); } static void hidhost_hid_info_cb(bt_bdaddr_t *bd_addr, bthh_hid_info_t hid) { struct step *step = g_new0(struct step, 1); step->callback = CB_HH_HID_INFO; schedule_callback_verification(step); } static void hidhost_get_report_cb(bt_bdaddr_t *bd_addr, bthh_status_t status, uint8_t *report, int size) { struct step *step = g_new0(struct step, 1); step->callback = CB_HH_GET_REPORT; step->callback_result.status = status; step->callback_result.report_size = size; schedule_callback_verification(step); } static bthh_callbacks_t bthh_callbacks = { .size = sizeof(bthh_callbacks), .connection_state_cb = hidhost_connection_state_cb, .hid_info_cb = hidhost_hid_info_cb, .protocol_mode_cb = hidhost_protocol_mode_cb, .idle_time_cb = NULL, .get_report_cb = hidhost_get_report_cb, .virtual_unplug_cb = hidhost_virtual_unplug_cb, .handshake_cb = NULL, }; static void gattc_register_client_cb(int status, int client_if, bt_uuid_t *app_uuid) { struct step *step = g_new0(struct step, 1); step->callback = CB_GATTC_REGISTER_CLIENT; step->callback_result.status = status; schedule_callback_verification(step); } static void gattc_scan_result_cb(bt_bdaddr_t *bda, int rssi, uint8_t *adv_data) { struct step *step = g_new0(struct step, 1); bt_property_t *props[2]; step->callback = CB_GATTC_SCAN_RESULT; step->callback_result.adv_data = adv_data ? TRUE : FALSE; /* Utilize property verification mechanism for those */ props[0] = create_property(BT_PROPERTY_BDADDR, bda, sizeof(*bda)); props[1] = create_property(BT_PROPERTY_REMOTE_RSSI, &rssi, sizeof(rssi)); step->callback_result.num_properties = 2; step->callback_result.properties = repack_properties(2, props); g_free(props[0]->val); g_free(props[0]); g_free(props[1]->val); g_free(props[1]); schedule_callback_verification(step); } static void gattc_connect_cb(int conn_id, int status, int client_if, bt_bdaddr_t *bda) { struct step *step = g_new0(struct step, 1); bt_property_t *props[1]; step->callback = CB_GATTC_OPEN; step->callback_result.status = status; step->callback_result.conn_id = conn_id; step->callback_result.gatt_app_id = client_if; /* Utilize property verification mechanism for bdaddr */ props[0] = create_property(BT_PROPERTY_BDADDR, bda, sizeof(*bda)); step->callback_result.num_properties = 1; step->callback_result.properties = repack_properties(1, props); g_free(props[0]->val); g_free(props[0]); schedule_callback_verification(step); } static void gattc_disconnect_cb(int conn_id, int status, int client_if, bt_bdaddr_t *bda) { struct step *step = g_new0(struct step, 1); bt_property_t *props[1]; step->callback = CB_GATTC_CLOSE; step->callback_result.status = status; step->callback_result.conn_id = conn_id; step->callback_result.gatt_app_id = client_if; /* Utilize property verification mechanism for bdaddr */ props[0] = create_property(BT_PROPERTY_BDADDR, bda, sizeof(*bda)); step->callback_result.num_properties = 1; step->callback_result.properties = repack_properties(1, props); g_free(props[0]->val); g_free(props[0]); schedule_callback_verification(step); } static void gattc_listen_cb(int status, int server_if) { struct step *step = g_new0(struct step, 1); step->callback = CB_GATTC_LISTEN; step->callback_result.status = status; schedule_callback_verification(step); } static void gattc_search_result_cb(int conn_id, btgatt_srvc_id_t *srvc_id) { struct step *step = g_new0(struct step, 1); step->callback = CB_GATTC_SEARCH_RESULT; step->callback_result.conn_id = conn_id; step->callback_result.service = util_memdup(srvc_id, sizeof(*srvc_id)); schedule_callback_verification(step); } static void gattc_search_complete_cb(int conn_id, int status) { struct step *step = g_new0(struct step, 1); step->callback = CB_GATTC_SEARCH_COMPLETE; step->callback_result.conn_id = conn_id; schedule_callback_verification(step); } static void gattc_get_characteristic_cb(int conn_id, int status, btgatt_srvc_id_t *srvc_id, btgatt_gatt_id_t *char_id, int char_prop) { struct step *step = g_new0(struct step, 1); step->callback = CB_GATTC_GET_CHARACTERISTIC; step->callback_result.status = status; step->callback_result.conn_id = conn_id; step->callback_result.service = util_memdup(srvc_id, sizeof(*srvc_id)); step->callback_result.characteristic = util_memdup(char_id, sizeof(*char_id)); step->callback_result.char_prop = char_prop; schedule_callback_verification(step); } static void gattc_get_descriptor_cb(int conn_id, int status, btgatt_srvc_id_t *srvc_id, btgatt_gatt_id_t *char_id, btgatt_gatt_id_t *descr_id) { struct step *step = g_new0(struct step, 1); step->callback = CB_GATTC_GET_DESCRIPTOR; step->callback_result.status = status; step->callback_result.conn_id = conn_id; step->callback_result.service = util_memdup(srvc_id, sizeof(*srvc_id)); step->callback_result.characteristic = util_memdup(char_id, sizeof(*char_id)); step->callback_result.descriptor = util_memdup(descr_id, sizeof(*descr_id)); schedule_callback_verification(step); } static void gattc_get_included_service_cb(int conn_id, int status, btgatt_srvc_id_t *srvc_id, btgatt_srvc_id_t *incl_srvc_id) { struct step *step = g_new0(struct step, 1); step->callback = CB_GATTC_GET_INCLUDED_SERVICE; step->callback_result.status = status; step->callback_result.conn_id = conn_id; step->callback_result.service = util_memdup(srvc_id, sizeof(*srvc_id)); step->callback_result.included = util_memdup(incl_srvc_id, sizeof(*srvc_id)); schedule_callback_verification(step); } static void gattc_read_characteristic_cb(int conn_id, int status, btgatt_read_params_t *p_data) { struct step *step = g_new0(struct step, 1); step->callback = CB_GATTC_READ_CHARACTERISTIC; step->callback_result.status = status; step->callback_result.conn_id = conn_id; step->callback_result.read_params = util_memdup(p_data, sizeof(*p_data)); schedule_callback_verification(step); } static void gattc_read_descriptor_cb(int conn_id, int status, btgatt_read_params_t *p_data) { struct step *step = g_new0(struct step, 1); step->callback = CB_GATTC_READ_DESCRIPTOR; step->callback_result.status = status; step->callback_result.conn_id = conn_id; step->callback_result.read_params = util_memdup(p_data, sizeof(*p_data)); schedule_callback_verification(step); } static void gattc_write_characteristic_cb(int conn_id, int status, btgatt_write_params_t *p_data) { struct step *step = g_new0(struct step, 1); step->callback = CB_GATTC_WRITE_CHARACTERISTIC; step->callback_result.status = status; step->callback_result.conn_id = conn_id; step->callback_result.write_params = util_memdup(p_data, sizeof(*p_data)); schedule_callback_verification(step); } static void gattc_write_descriptor_cb(int conn_id, int status, btgatt_write_params_t *p_data) { struct step *step = g_new0(struct step, 1); step->callback = CB_GATTC_WRITE_DESCRIPTOR; step->callback_result.status = status; step->callback_result.conn_id = conn_id; step->callback_result.write_params = util_memdup(p_data, sizeof(*p_data)); schedule_callback_verification(step); } static void gattc_register_for_notification_cb(int conn_id, int registered, int status, btgatt_srvc_id_t *srvc_id, btgatt_gatt_id_t *char_id) { struct step *step = g_new0(struct step, 1); step->callback = CB_GATTC_REGISTER_FOR_NOTIFICATION; step->callback_result.status = status; step->callback_result.conn_id = conn_id; step->callback_result.service = util_memdup(srvc_id, sizeof(*srvc_id)); step->callback_result.characteristic = util_memdup(char_id, sizeof(*char_id)); step->callback_result.notification_registered = registered; schedule_callback_verification(step); } static void gattc_notif_cb(int conn_id, btgatt_notify_params_t *p_data) { struct step *step = g_new0(struct step, 1); step->callback = CB_GATTC_NOTIFY; step->callback_result.conn_id = conn_id; step->callback_result.notify_params = util_memdup(p_data, sizeof(*p_data)); schedule_callback_verification(step); } static const btgatt_client_callbacks_t btgatt_client_callbacks = { .register_client_cb = gattc_register_client_cb, .scan_result_cb = gattc_scan_result_cb, .open_cb = gattc_connect_cb, .close_cb = gattc_disconnect_cb, .search_complete_cb = gattc_search_complete_cb, .search_result_cb = gattc_search_result_cb, .get_characteristic_cb = gattc_get_characteristic_cb, .get_descriptor_cb = gattc_get_descriptor_cb, .get_included_service_cb = gattc_get_included_service_cb, .register_for_notification_cb = gattc_register_for_notification_cb, .notify_cb = gattc_notif_cb, .read_characteristic_cb = gattc_read_characteristic_cb, .write_characteristic_cb = gattc_write_characteristic_cb, .read_descriptor_cb = gattc_read_descriptor_cb, .write_descriptor_cb = gattc_write_descriptor_cb, .execute_write_cb = NULL, .read_remote_rssi_cb = NULL, .listen_cb = gattc_listen_cb }; static void gatts_register_server_cb(int status, int server_if, bt_uuid_t *app_uuid) { struct step *step = g_new0(struct step, 1); step->callback = CB_GATTS_REGISTER_SERVER; step->callback_result.status = status; schedule_callback_verification(step); } static void gatts_connection_cb(int conn_id, int server_if, int connected, bt_bdaddr_t *bda) { struct step *step = g_new0(struct step, 1); bt_property_t *props[1]; step->callback = CB_GATTS_CONNECTION; step->callback_result.conn_id = conn_id; step->callback_result.gatt_app_id = server_if; step->callback_result.connected = connected; /* Utilize property verification mechanism for bdaddr */ props[0] = create_property(BT_PROPERTY_BDADDR, bda, sizeof(*bda)); step->callback_result.num_properties = 1; step->callback_result.properties = repack_properties(1, props); g_free(props[0]->val); g_free(props[0]); schedule_callback_verification(step); } static void gatts_service_added_cb(int status, int server_if, btgatt_srvc_id_t *srvc_id, int srvc_handle) { struct step *step = g_new0(struct step, 1); step->callback = CB_GATTS_SERVICE_ADDED; step->callback_result.status = status; step->callback_result.gatt_app_id = server_if; step->callback_result.service = util_memdup(srvc_id, sizeof(*srvc_id)); step->callback_result.srvc_handle = util_memdup(&srvc_handle, sizeof(srvc_handle)); schedule_callback_verification(step); } static void gatts_included_service_added_cb(int status, int server_if, int srvc_handle, int inc_srvc_handle) { struct step *step = g_new0(struct step, 1); step->callback = CB_GATTS_INCLUDED_SERVICE_ADDED; step->callback_result.status = status; step->callback_result.gatt_app_id = server_if; step->callback_result.srvc_handle = util_memdup(&srvc_handle, sizeof(srvc_handle)); step->callback_result.inc_srvc_handle = util_memdup(&inc_srvc_handle, sizeof(inc_srvc_handle)); schedule_callback_verification(step); } static void gatts_characteristic_added_cb(int status, int server_if, bt_uuid_t *uuid, int srvc_handle, int char_handle) { struct step *step = g_new0(struct step, 1); step->callback = CB_GATTS_CHARACTERISTIC_ADDED; step->callback_result.status = status; step->callback_result.gatt_app_id = server_if; step->callback_result.srvc_handle = util_memdup(&srvc_handle, sizeof(srvc_handle)); step->callback_result.uuid = util_memdup(uuid, sizeof(*uuid)); step->callback_result.char_handle = util_memdup(&char_handle, sizeof(char_handle)); schedule_callback_verification(step); } static void gatts_descriptor_added_cb(int status, int server_if, bt_uuid_t *uuid, int srvc_handle, int desc_handle) { struct step *step = g_new0(struct step, 1); step->callback = CB_GATTS_DESCRIPTOR_ADDED; step->callback_result.status = status; step->callback_result.gatt_app_id = server_if; step->callback_result.srvc_handle = util_memdup(&srvc_handle, sizeof(srvc_handle)); step->callback_result.uuid = util_memdup(uuid, sizeof(*uuid)); step->callback_result.desc_handle = util_memdup(&desc_handle, sizeof(desc_handle)); schedule_callback_verification(step); } static void gatts_service_started_cb(int status, int server_if, int srvc_handle) { struct step *step = g_new0(struct step, 1); step->callback = CB_GATTS_SERVICE_STARTED; step->callback_result.status = status; step->callback_result.gatt_app_id = server_if; step->callback_result.srvc_handle = util_memdup(&srvc_handle, sizeof(srvc_handle)); schedule_callback_verification(step); } static void gatts_service_stopped_cb(int status, int server_if, int srvc_handle) { struct step *step = g_new0(struct step, 1); step->callback = CB_GATTS_SERVICE_STOPPED; step->callback_result.status = status; step->callback_result.gatt_app_id = server_if; step->callback_result.srvc_handle = util_memdup(&srvc_handle, sizeof(srvc_handle)); schedule_callback_verification(step); } static void gatts_service_deleted_cb(int status, int server_if, int srvc_handle) { struct step *step = g_new0(struct step, 1); step->callback = CB_GATTS_SERVICE_DELETED; step->callback_result.status = status; step->callback_result.gatt_app_id = server_if; step->callback_result.srvc_handle = util_memdup(&srvc_handle, sizeof(srvc_handle)); schedule_callback_verification(step); } static void gatts_request_read_cb(int conn_id, int trans_id, bt_bdaddr_t *bda, int attr_handle, int offset, bool is_long) { struct step *step = g_new0(struct step, 1); bt_property_t *props[1]; step->callback = CB_GATTS_REQUEST_READ; step->callback_result.conn_id = conn_id; step->callback_result.trans_id = trans_id; step->callback_result.attr_handle = util_memdup(&attr_handle, sizeof(attr_handle)); step->callback_result.offset = offset; step->callback_result.is_long = is_long; /* Utilize property verification mechanism for bdaddr */ props[0] = create_property(BT_PROPERTY_BDADDR, bda, sizeof(*bda)); step->callback_result.num_properties = 1; step->callback_result.properties = repack_properties(1, props); g_free(props[0]->val); g_free(props[0]); schedule_callback_verification(step); } static void gatts_request_write_cb(int conn_id, int trans_id, bt_bdaddr_t *bda, int attr_handle, int offset, int length, bool need_rsp, bool is_prep, uint8_t *value) { struct step *step = g_new0(struct step, 1); bt_property_t *props[1]; step->callback = CB_GATTS_REQUEST_WRITE; step->callback_result.conn_id = conn_id; step->callback_result.trans_id = trans_id; step->callback_result.attr_handle = util_memdup(&attr_handle, sizeof(attr_handle)); step->callback_result.offset = offset; step->callback_result.length = length; step->callback_result.need_rsp = need_rsp; step->callback_result.is_prep = is_prep; step->callback_result.value = util_memdup(&value, length); /* Utilize property verification mechanism for bdaddr */ props[0] = create_property(BT_PROPERTY_BDADDR, bda, sizeof(*bda)); step->callback_result.num_properties = 1; step->callback_result.properties = repack_properties(1, props); g_free(props[0]->val); g_free(props[0]); schedule_callback_verification(step); } static void gatts_indication_send_cb(int conn_id, int status) { struct step *step = g_new0(struct step, 1); step->callback = CB_GATTS_INDICATION_SEND; step->callback_result.conn_id = conn_id; step->callback_result.status = status; schedule_callback_verification(step); } static const btgatt_server_callbacks_t btgatt_server_callbacks = { .register_server_cb = gatts_register_server_cb, .connection_cb = gatts_connection_cb, .service_added_cb = gatts_service_added_cb, .included_service_added_cb = gatts_included_service_added_cb, .characteristic_added_cb = gatts_characteristic_added_cb, .descriptor_added_cb = gatts_descriptor_added_cb, .service_started_cb = gatts_service_started_cb, .service_stopped_cb = gatts_service_stopped_cb, .service_deleted_cb = gatts_service_deleted_cb, .request_read_cb = gatts_request_read_cb, .request_write_cb = gatts_request_write_cb, .request_exec_write_cb = NULL, .response_confirmation_cb = NULL, .indication_sent_cb = gatts_indication_send_cb, .congestion_cb = NULL, }; static const btgatt_callbacks_t btgatt_callbacks = { .size = sizeof(btgatt_callbacks), .client = &btgatt_client_callbacks, .server = &btgatt_server_callbacks }; static void pan_control_state_cb(btpan_control_state_t state, int local_role, bt_status_t error, const char *ifname) { struct step *step = g_new0(struct step, 1); step->callback = CB_PAN_CONTROL_STATE; step->callback_result.state = error; step->callback_result.ctrl_state = state; step->callback_result.local_role = local_role; schedule_callback_verification(step); } static void pan_connection_state_cb(btpan_connection_state_t state, bt_status_t error, const bt_bdaddr_t *bd_addr, int local_role, int remote_role) { struct step *step = g_new0(struct step, 1); step->callback = CB_PAN_CONNECTION_STATE; step->callback_result.state = error; step->callback_result.conn_state = state; step->callback_result.local_role = local_role; step->callback_result.remote_role = remote_role; schedule_callback_verification(step); } static btpan_callbacks_t btpan_callbacks = { .size = sizeof(btpan_callbacks), .control_state_cb = pan_control_state_cb, .connection_state_cb = pan_connection_state_cb, }; static void hdp_app_reg_state_cb(int app_id, bthl_app_reg_state_t state) { struct step *step = g_new0(struct step, 1); step->callback = CB_HDP_APP_REG_STATE; step->callback_result.app_id = app_id; step->callback_result.app_state = state; schedule_callback_verification(step); } static void hdp_channel_state_cb(int app_id, bt_bdaddr_t *bd_addr, int mdep_cfg_index, int channel_id, bthl_channel_state_t state, int fd) { struct step *step = g_new0(struct step, 1); step->callback = CB_HDP_CHANNEL_STATE; step->callback_result.app_id = app_id; step->callback_result.channel_id = channel_id; step->callback_result.mdep_cfg_index = mdep_cfg_index; step->callback_result.channel_state = state; schedule_callback_verification(step); } static bthl_callbacks_t bthl_callbacks = { .size = sizeof(bthl_callbacks), .app_reg_state_cb = hdp_app_reg_state_cb, .channel_state_cb = hdp_channel_state_cb, }; static void a2dp_connection_state_cb(btav_connection_state_t state, bt_bdaddr_t *bd_addr) { struct step *step = g_new0(struct step, 1); step->callback = CB_A2DP_CONN_STATE; step->callback_result.av_conn_state = state; schedule_callback_verification(step); } static void a2dp_audio_state_cb(btav_audio_state_t state, bt_bdaddr_t *bd_addr) { struct step *step = g_new0(struct step, 1); step->callback = CB_A2DP_AUDIO_STATE; step->callback_result.av_audio_state = state; schedule_callback_verification(step); } static btav_callbacks_t bta2dp_callbacks = { .size = sizeof(bta2dp_callbacks), .connection_state_cb = a2dp_connection_state_cb, .audio_state_cb = a2dp_audio_state_cb, }; static void avrcp_get_play_status_cb(void) { struct step *step = g_new0(struct step, 1); step->callback = CB_AVRCP_PLAY_STATUS_REQ; schedule_callback_verification(step); } static void avrcp_register_notification_cb(btrc_event_id_t event_id, uint32_t param) { struct step *step = g_new0(struct step, 1); step->callback = CB_AVRCP_REG_NOTIF_REQ; schedule_callback_verification(step); } static void avrcp_get_element_attr_cb(uint8_t num_attr, btrc_media_attr_t *p_attrs) { struct step *step = g_new0(struct step, 1); step->callback = CB_AVRCP_GET_ATTR_REQ; schedule_callback_verification(step); } static btrc_callbacks_t btavrcp_callbacks = { .size = sizeof(btavrcp_callbacks), .get_play_status_cb = avrcp_get_play_status_cb, .register_notification_cb = avrcp_register_notification_cb, .get_element_attr_cb = avrcp_get_element_attr_cb, }; static btmce_mas_instance_t *copy_mas_instances(int num_instances, btmce_mas_instance_t *instances) { int i; btmce_mas_instance_t *inst; inst = g_new0(btmce_mas_instance_t, num_instances); for (i = 0; i < num_instances; i++) { inst[i].id = instances[i].id; inst[i].scn = instances[i].scn; inst[i].msg_types = instances[i].msg_types; inst[i].p_name = util_memdup(instances[i].p_name, strlen(instances[i].p_name)); } return inst; } static void map_client_get_remote_mas_instances_cb(bt_status_t status, bt_bdaddr_t *bd_addr, int num_instances, btmce_mas_instance_t *instances) { struct step *step = g_new0(struct step, 1); step->callback_result.status = status; step->callback_result.num_mas_instances = num_instances; step->callback_result.mas_instances = copy_mas_instances(num_instances, instances); step->callback = CB_MAP_CLIENT_REMOTE_MAS_INSTANCES; schedule_callback_verification(step); } static btmce_callbacks_t btmap_client_callbacks = { .size = sizeof(btmap_client_callbacks), .remote_mas_instances_cb = map_client_get_remote_mas_instances_cb, }; static bool setup_base(struct test_data *data) { const hw_module_t *module; hw_device_t *device; int signal_fd[2]; char buf[1024]; pid_t pid; int len; int err; if (pipe(signal_fd)) return false; pid = fork(); if (pid < 0) { close(signal_fd[0]); close(signal_fd[1]); return false; } if (pid == 0) { if (!tester_use_debug()) fclose(stderr); close(signal_fd[0]); emulator(signal_fd[1], data->mgmt_index); exit(0); } close(signal_fd[1]); data->bluetoothd_pid = pid; len = read(signal_fd[0], buf, sizeof(buf)); if (len <= 0 || strcmp(buf, EMULATOR_SIGNAL)) { close(signal_fd[0]); return false; } close(signal_fd[0]); err = hw_get_module_by_class(AUDIO_HARDWARE_MODULE_ID, AUDIO_HARDWARE_MODULE_ID_A2DP, &module); if (err) return false; err = audio_hw_device_open(module, &data->audio); if (err) return false; err = hw_get_module(BT_HARDWARE_MODULE_ID, &module); if (err) return false; err = module->methods->open(module, BT_HARDWARE_MODULE_ID, &device); if (err) return false; data->device = device; data->if_bluetooth = ((bluetooth_device_t *) device)->get_bluetooth_interface(); if (!data->if_bluetooth) return false; if (!(data->steps = queue_new())) return false; data->pdus = queue_new(); if (!data->pdus) { queue_destroy(data->steps, NULL); return false; } return true; } static void setup(const void *test_data) { struct test_data *data = tester_get_data(); bt_status_t status; if (!setup_base(data)) { tester_setup_failed(); return; } status = data->if_bluetooth->init(&bt_callbacks); if (status != BT_STATUS_SUCCESS) { data->if_bluetooth = NULL; tester_setup_failed(); return; } tester_setup_complete(); } static void setup_socket(const void *test_data) { struct test_data *data = tester_get_data(); bt_status_t status; const void *sock; if (!setup_base(data)) { tester_setup_failed(); return; } status = data->if_bluetooth->init(&bt_callbacks); if (status != BT_STATUS_SUCCESS) { data->if_bluetooth = NULL; tester_setup_failed(); return; } sock = data->if_bluetooth->get_profile_interface(BT_PROFILE_SOCKETS_ID); if (!sock) { tester_setup_failed(); return; } data->if_sock = sock; tester_setup_complete(); } static void setup_hidhost(const void *test_data) { struct test_data *data = tester_get_data(); bt_status_t status; const void *hid; if (!setup_base(data)) { tester_setup_failed(); return; } status = data->if_bluetooth->init(&bt_callbacks); if (status != BT_STATUS_SUCCESS) { data->if_bluetooth = NULL; tester_setup_failed(); return; } hid = data->if_bluetooth->get_profile_interface(BT_PROFILE_HIDHOST_ID); if (!hid) { tester_setup_failed(); return; } data->if_hid = hid; status = data->if_hid->init(&bthh_callbacks); if (status != BT_STATUS_SUCCESS) { data->if_hid = NULL; tester_setup_failed(); return; } tester_setup_complete(); } static void setup_pan(const void *test_data) { struct test_data *data = tester_get_data(); bt_status_t status; const void *pan; if (!setup_base(data)) { tester_setup_failed(); return; } status = data->if_bluetooth->init(&bt_callbacks); if (status != BT_STATUS_SUCCESS) { data->if_bluetooth = NULL; tester_setup_failed(); return; } pan = data->if_bluetooth->get_profile_interface(BT_PROFILE_PAN_ID); if (!pan) { tester_setup_failed(); return; } data->if_pan = pan; status = data->if_pan->init(&btpan_callbacks); if (status != BT_STATUS_SUCCESS) { data->if_pan = NULL; tester_setup_failed(); return; } tester_setup_complete(); } static void setup_hdp(const void *test_data) { struct test_data *data = tester_get_data(); bt_status_t status; const void *hdp; if (!setup_base(data)) { tester_setup_failed(); return; } status = data->if_bluetooth->init(&bt_callbacks); if (status != BT_STATUS_SUCCESS) { data->if_bluetooth = NULL; tester_setup_failed(); return; } hdp = data->if_bluetooth->get_profile_interface(BT_PROFILE_HEALTH_ID); if (!hdp) { tester_setup_failed(); return; } data->if_hdp = hdp; status = data->if_hdp->init(&bthl_callbacks); if (status != BT_STATUS_SUCCESS) { data->if_hdp = NULL; tester_setup_failed(); return; } tester_setup_complete(); } static void setup_a2dp(const void *test_data) { struct test_data *data = tester_get_data(); const bt_interface_t *if_bt; bt_status_t status; const void *a2dp; if (!setup_base(data)) { tester_setup_failed(); return; } if_bt = data->if_bluetooth; status = if_bt->init(&bt_callbacks); if (status != BT_STATUS_SUCCESS) { data->if_bluetooth = NULL; tester_setup_failed(); return; } a2dp = if_bt->get_profile_interface(BT_PROFILE_ADVANCED_AUDIO_ID); if (!a2dp) { tester_setup_failed(); return; } data->if_a2dp = a2dp; status = data->if_a2dp->init(&bta2dp_callbacks); if (status != BT_STATUS_SUCCESS) { data->if_a2dp = NULL; tester_setup_failed(); return; } tester_setup_complete(); } static void setup_avrcp(const void *test_data) { struct test_data *data = tester_get_data(); const bt_interface_t *if_bt; bt_status_t status; const void *a2dp, *avrcp; if (!setup_base(data)) { tester_setup_failed(); return; } if_bt = data->if_bluetooth; status = if_bt->init(&bt_callbacks); if (status != BT_STATUS_SUCCESS) { data->if_bluetooth = NULL; tester_setup_failed(); return; } a2dp = if_bt->get_profile_interface(BT_PROFILE_ADVANCED_AUDIO_ID); if (!a2dp) { tester_setup_failed(); return; } data->if_a2dp = a2dp; status = data->if_a2dp->init(&bta2dp_callbacks); if (status != BT_STATUS_SUCCESS) { data->if_a2dp = NULL; tester_setup_failed(); return; } avrcp = if_bt->get_profile_interface(BT_PROFILE_AV_RC_ID); if (!avrcp) { tester_setup_failed(); return; } data->if_avrcp = avrcp; status = data->if_avrcp->init(&btavrcp_callbacks); if (status != BT_STATUS_SUCCESS) { data->if_avrcp = NULL; tester_setup_failed(); return; } tester_setup_complete(); } static void setup_gatt(const void *test_data) { struct test_data *data = tester_get_data(); bt_status_t status; const void *gatt; if (!setup_base(data)) { tester_setup_failed(); return; } status = data->if_bluetooth->init(&bt_callbacks); if (status != BT_STATUS_SUCCESS) { data->if_bluetooth = NULL; tester_setup_failed(); return; } gatt = data->if_bluetooth->get_profile_interface(BT_PROFILE_GATT_ID); if (!gatt) { tester_setup_failed(); return; } data->if_gatt = gatt; status = data->if_gatt->init(&btgatt_callbacks); if (status != BT_STATUS_SUCCESS) { data->if_gatt = NULL; tester_setup_failed(); return; } tester_setup_complete(); } static void setup_map_client(const void *test_data) { struct test_data *data = tester_get_data(); bt_status_t status; const void *map_client; if (!setup_base(data)) { tester_setup_failed(); return; } status = data->if_bluetooth->init(&bt_callbacks); if (status != BT_STATUS_SUCCESS) { data->if_bluetooth = NULL; tester_setup_failed(); return; } map_client = data->if_bluetooth->get_profile_interface( BT_PROFILE_MAP_CLIENT_ID); if (!map_client) { tester_setup_failed(); return; } data->if_map_client = map_client; status = data->if_map_client->init(&btmap_client_callbacks); if (status != BT_STATUS_SUCCESS) { data->if_map_client = NULL; tester_setup_failed(); return; } tester_setup_complete(); } static void teardown(const void *test_data) { struct test_data *data = tester_get_data(); queue_destroy(data->steps, NULL); data->steps = NULL; queue_destroy(data->pdus, NULL); data->pdus = NULL; /* no cleanup for map_client */ data->if_map_client = NULL; if (data->if_gatt) { data->if_gatt->cleanup(); data->if_gatt = NULL; } if (data->if_hid) { data->if_hid->cleanup(); data->if_hid = NULL; } if (data->if_pan) { data->if_pan->cleanup(); data->if_pan = NULL; } if (data->if_hdp) { data->if_hdp->cleanup(); data->if_hdp = NULL; } if (data->if_stream) { data->audio->close_output_stream(data->audio, data->if_stream); data->if_stream = NULL; } if (data->if_a2dp) { data->if_a2dp->cleanup(); data->if_a2dp = NULL; } if (data->if_avrcp) { data->if_avrcp->cleanup(); data->if_avrcp = NULL; } if (data->if_bluetooth) { data->if_bluetooth->cleanup(); data->if_bluetooth = NULL; } data->device->close(data->device); audio_hw_device_close(data->audio); /* * Ssp_request_cb pointer can be set do default_ssp_req_cb. * Set it back to ssp_request_cb */ bt_callbacks.ssp_request_cb = ssp_request_cb; if (!data->bluetoothd_pid) tester_teardown_complete(); } static void emu_connectable_complete(uint16_t opcode, uint8_t status, const void *param, uint8_t len, void *user_data) { struct step *step; struct test_data *data = user_data; switch (opcode) { case BT_HCI_CMD_WRITE_SCAN_ENABLE: break; case BT_HCI_CMD_LE_SET_ADV_ENABLE: /* * For BREDRLE emulator we want to verify step after scan * enable and not after le_set_adv_enable */ if (data->hciemu_type == HCIEMU_TYPE_BREDRLE) return; break; default: return; } step = g_new0(struct step, 1); if (status) { tester_warn("Emulated remote setup failed."); step->action_status = BT_STATUS_FAIL; } else { tester_debug("Emulated remote setup done."); step->action_status = BT_STATUS_SUCCESS; } schedule_action_verification(step); } void emu_setup_powered_remote_action(void) { struct test_data *data = tester_get_data(); struct bthost *bthost; bthost = hciemu_client_get_host(data->hciemu); bthost_set_cmd_complete_cb(bthost, emu_connectable_complete, data); if ((data->hciemu_type == HCIEMU_TYPE_LE) || (data->hciemu_type == HCIEMU_TYPE_BREDRLE)) { uint8_t adv[3]; adv[0] = 0x02; /* Field length */ adv[1] = 0x01; /* Flags */ adv[2] = 0x02; /* Flags value */ bthost_set_adv_data(bthost, adv, sizeof(adv)); bthost_set_adv_enable(bthost, 0x01); } if (data->hciemu_type != HCIEMU_TYPE_LE) bthost_write_scan_enable(bthost, 0x03); } void emu_set_pin_code_action(void) { struct test_data *data = tester_get_data(); struct step *current_data_step = queue_peek_head(data->steps); struct bt_action_data *action_data = current_data_step->set_data; struct bthost *bthost; struct step *step = g_new0(struct step, 1); bthost = hciemu_client_get_host(data->hciemu); bthost_set_pin_code(bthost, action_data->pin->pin, action_data->pin_len); step->action_status = BT_STATUS_SUCCESS; tester_print("Setting emu pin done."); schedule_action_verification(step); } void emu_set_ssp_mode_action(void) { struct test_data *data = tester_get_data(); struct bthost *bthost; struct step *step = g_new0(struct step, 1); bthost = hciemu_client_get_host(data->hciemu); bthost_write_ssp_mode(bthost, 0x01); step->action_status = BT_STATUS_SUCCESS; schedule_action_verification(step); } void emu_set_connect_cb_action(void) { struct test_data *data = tester_get_data(); struct bthost *bthost = hciemu_client_get_host(data->hciemu); struct step *current_data_step = queue_peek_head(data->steps); void *cb = current_data_step->set_data; struct step *step = g_new0(struct step, 1); bthost_set_connect_cb(bthost, cb, data); step->action_status = BT_STATUS_SUCCESS; schedule_action_verification(step); } void emu_remote_connect_hci_action(void) { struct test_data *data = tester_get_data(); struct bthost *bthost = hciemu_client_get_host(data->hciemu); struct step *current_data_step = queue_peek_head(data->steps); struct bt_action_data *action_data = current_data_step->set_data; struct step *step = g_new0(struct step, 1); const uint8_t *master_addr; master_addr = hciemu_get_central_bdaddr(data->hciemu); tester_print("Trying to connect hci"); if (action_data) bthost_hci_connect(bthost, master_addr, action_data->bearer_type); else bthost_hci_connect(bthost, master_addr, BDADDR_BREDR); step->action_status = BT_STATUS_SUCCESS; schedule_action_verification(step); } void emu_remote_disconnect_hci_action(void) { struct test_data *data = tester_get_data(); struct bthost *bthost = hciemu_client_get_host(data->hciemu); struct step *current_data_step = queue_peek_head(data->steps); uint16_t *handle = current_data_step->set_data; struct step *step = g_new0(struct step, 1); if (handle) { bthost_hci_disconnect(bthost, *handle, 0x13); step->action_status = BT_STATUS_SUCCESS; } else { step->action_status = BT_STATUS_FAIL; } schedule_action_verification(step); } void emu_set_io_cap(void) { struct test_data *data = tester_get_data(); struct bthost *bthost; struct step *current_data_step = queue_peek_head(data->steps); struct bt_action_data *action_data = current_data_step->set_data; struct step *step = g_new0(struct step, 1); bthost = hciemu_client_get_host(data->hciemu); if (action_data) bthost_set_io_capability(bthost, action_data->io_cap); else bthost_set_io_capability(bthost, 0x01); step->action_status = BT_STATUS_SUCCESS; schedule_action_verification(step); } void emu_add_l2cap_server_action(void) { struct test_data *data = tester_get_data(); struct step *current_data_step = queue_peek_head(data->steps); struct emu_set_l2cap_data *l2cap_data = current_data_step->set_data; struct bthost *bthost; struct step *step = g_new0(struct step, 1); if (!l2cap_data) { tester_warn("Invalid l2cap_data params"); step->action_status = BT_STATUS_FAIL; goto done; } bthost = hciemu_client_get_host(data->hciemu); bthost_add_l2cap_server(bthost, l2cap_data->psm, l2cap_data->func, NULL, l2cap_data->user_data); step->action_status = BT_STATUS_SUCCESS; done: schedule_action_verification(step); } static void print_data(const char *str, void *user_data) { tester_debug("tester: %s", str); } static void emu_generic_cid_hook_cb(const void *data, uint16_t len, void *user_data) { struct test_data *t_data = tester_get_data(); struct emu_l2cap_cid_data *cid_data = user_data; const struct pdu_set *pdus = cid_data->pdu; struct bthost *bthost = hciemu_client_get_host(t_data->hciemu); int i; for (i = 0; pdus[i].rsp.iov_base; i++) { if (pdus[i].req.iov_base) { if (pdus[i].req.iov_len != len) continue; if (memcmp(pdus[i].req.iov_base, data, len)) continue; } if (pdus[i].rsp.iov_base) { util_hexdump('>', pdus[i].rsp.iov_base, pdus[i].rsp.iov_len, print_data, NULL); /* if its sdp pdu use transaction ID from request */ if (cid_data->is_sdp) { struct iovec rsp[3]; rsp[0].iov_base = pdus[i].rsp.iov_base; rsp[0].iov_len = 1; rsp[1].iov_base = ((uint8_t *) data) + 1; rsp[1].iov_len = 2; rsp[2].iov_base = pdus[i].rsp.iov_base + 3; rsp[2].iov_len = pdus[i].rsp.iov_len - 3; bthost_send_cid_v(bthost, cid_data->handle, cid_data->cid, rsp, 3); } else { bthost_send_cid_v(bthost, cid_data->handle, cid_data->cid, &pdus[i].rsp, 1); } } } } void tester_handle_l2cap_data_exchange(struct emu_l2cap_cid_data *cid_data) { struct test_data *t_data = tester_get_data(); struct bthost *bthost = hciemu_client_get_host(t_data->hciemu); bthost_add_cid_hook(bthost, cid_data->handle, cid_data->cid, emu_generic_cid_hook_cb, cid_data); } void tester_generic_connect_cb(uint16_t handle, uint16_t cid, void *user_data) { struct emu_l2cap_cid_data *cid_data = user_data; cid_data->handle = handle; cid_data->cid = cid; tester_handle_l2cap_data_exchange(cid_data); } static void rfcomm_connect_cb(uint16_t handle, uint16_t cid, void *user_data, bool status) { struct step *step = g_new0(struct step, 1); tester_print("Connect handle %d, cid %d cb status: %d", handle, cid, status); step->action_status = BT_STATUS_SUCCESS; schedule_action_verification(step); } void emu_add_rfcomm_server_action(void) { struct test_data *data = tester_get_data(); struct step *current_data_step = queue_peek_head(data->steps); struct bt_action_data *rfcomm_data = current_data_step->set_data; struct bthost *bthost; struct step *step; if (!rfcomm_data) { tester_warn("Invalid l2cap_data params"); return; } step = g_new0(struct step, 1); bthost = hciemu_client_get_host(data->hciemu); bthost_add_rfcomm_server(bthost, rfcomm_data->channel, rfcomm_connect_cb, data); step->action_status = BT_STATUS_SUCCESS; schedule_action_verification(step); } void dummy_action(void) { struct step *step = g_new0(struct step, 1); step->action = dummy_action; schedule_action_verification(step); } void bluetooth_enable_action(void) { struct test_data *data = tester_get_data(); struct step *step = g_new0(struct step, 1); step->action_status = data->if_bluetooth->enable(); schedule_action_verification(step); } void bluetooth_disable_action(void) { struct test_data *data = tester_get_data(); struct step *step = g_new0(struct step, 1); step->action_status = data->if_bluetooth->disable(); schedule_action_verification(step); } void bt_set_property_action(void) { struct test_data *data = tester_get_data(); struct step *step; struct step *current_data_step = queue_peek_head(data->steps); bt_property_t *prop; if (!current_data_step->set_data) { tester_debug("BT property not set for step"); tester_test_failed(); return; } step = g_new0(struct step, 1); prop = (bt_property_t *)current_data_step->set_data; step->action_status = data->if_bluetooth->set_adapter_property(prop); schedule_action_verification(step); } void bt_get_property_action(void) { struct test_data *data = tester_get_data(); struct step *step; struct step *current_data_step = queue_peek_head(data->steps); bt_property_t *prop; if (!current_data_step->set_data) { tester_debug("BT property to get not defined"); tester_test_failed(); return; } step = g_new0(struct step, 1); prop = (bt_property_t *)current_data_step->set_data; step->action_status = data->if_bluetooth->get_adapter_property( prop->type); schedule_action_verification(step); } void bt_start_discovery_action(void) { struct test_data *data = tester_get_data(); struct step *step = g_new0(struct step, 1); step->action_status = data->if_bluetooth->start_discovery(); schedule_action_verification(step); } void bt_cancel_discovery_action(void) { struct test_data *data = tester_get_data(); struct step *step = g_new0(struct step, 1); step->action_status = data->if_bluetooth->cancel_discovery(); schedule_action_verification(step); } void bt_get_device_props_action(void) { struct test_data *data = tester_get_data(); struct step *current_data_step = queue_peek_head(data->steps); struct step *step; if (!current_data_step->set_data) { tester_debug("bdaddr not defined"); tester_test_failed(); return; } step = g_new0(struct step, 1); step->action_status = data->if_bluetooth->get_remote_device_properties( current_data_step->set_data); schedule_action_verification(step); } void bt_get_device_prop_action(void) { struct test_data *data = tester_get_data(); struct step *current_data_step = queue_peek_head(data->steps); struct bt_action_data *action_data = current_data_step->set_data; struct step *step; if (!action_data) { tester_warn("No arguments for 'get remote device prop' req."); tester_test_failed(); return; } step = g_new0(struct step, 1); step->action_status = data->if_bluetooth->get_remote_device_property( action_data->addr, action_data->prop_type); schedule_action_verification(step); } void bt_set_device_prop_action(void) { struct test_data *data = tester_get_data(); struct step *current_data_step = queue_peek_head(data->steps); struct bt_action_data *action_data = current_data_step->set_data; struct step *step; if (!action_data) { tester_warn("No arguments for 'set remote device prop' req."); tester_test_failed(); return; } step = g_new0(struct step, 1); step->action_status = data->if_bluetooth->set_remote_device_property( action_data->addr, action_data->prop); schedule_action_verification(step); } void bt_create_bond_action(void) { struct test_data *data = tester_get_data(); struct step *current_data_step = queue_peek_head(data->steps); struct bt_action_data *action_data = current_data_step->set_data; struct step *step; if (!action_data || !action_data->addr) { tester_warn("Bad arguments for 'create bond' req."); tester_test_failed(); return; } step = g_new0(struct step, 1); step->action_status = data->if_bluetooth->create_bond(action_data->addr, action_data->transport_type ? action_data->transport_type : BT_TRANSPORT_UNKNOWN); schedule_action_verification(step); } void bt_pin_reply_accept_action(void) { struct test_data *data = tester_get_data(); struct step *current_data_step = queue_peek_head(data->steps); struct bt_action_data *action_data = current_data_step->set_data; struct step *step; if (!action_data || !action_data->addr || !action_data->pin) { tester_warn("Bad arguments for 'pin reply' req."); tester_test_failed(); return; } step = g_new0(struct step, 1); step->action_status = data->if_bluetooth->pin_reply(action_data->addr, TRUE, action_data->pin_len, action_data->pin); schedule_action_verification(step); } void bt_ssp_reply_accept_action(void) { struct test_data *data = tester_get_data(); struct step *current_data_step = queue_peek_head(data->steps); struct bt_action_data *action_data = current_data_step->set_data; struct step *step = g_new0(struct step, 1); step->action_status = data->if_bluetooth->ssp_reply(action_data->addr, action_data->ssp_variant, action_data->accept, 0); schedule_action_verification(step); } void bt_cancel_bond_action(void) { struct test_data *data = tester_get_data(); struct step *current_data_step = queue_peek_head(data->steps); bt_bdaddr_t *addr = current_data_step->set_data; struct step *step = g_new0(struct step, 1); step->action_status = data->if_bluetooth->cancel_bond(addr); schedule_action_verification(step); } void bt_remove_bond_action(void) { struct test_data *data = tester_get_data(); struct step *current_data_step = queue_peek_head(data->steps); bt_bdaddr_t *addr = current_data_step->set_data; struct step *step = g_new0(struct step, 1); step->action_status = data->if_bluetooth->remove_bond(addr); schedule_action_verification(step); } static void default_ssp_req_cb(bt_bdaddr_t *remote_bd_addr, bt_bdname_t *name, uint32_t cod, bt_ssp_variant_t pairing_variant, uint32_t pass_key) { struct test_data *t_data = tester_get_data(); t_data->if_bluetooth->ssp_reply(remote_bd_addr, pairing_variant, true, pass_key); } void set_default_ssp_request_handler(void) { struct step *step = g_new0(struct step, 1); bt_callbacks.ssp_request_cb = default_ssp_req_cb; step->action_status = BT_STATUS_SUCCESS; schedule_action_verification(step); } static void generic_test_function(const void *test_data) { struct test_data *data = tester_get_data(); struct step *first_step; init_test_steps(data); /* first step action */ first_step = queue_peek_head(data->steps); if (!first_step->action) { tester_print("tester: No initial action declared"); tester_test_failed(); return; } first_step->action(); } #define test(data, test_setup, test, test_teardown) \ do { \ struct test_data *user; \ user = g_malloc0(sizeof(struct test_data)); \ if (!user) \ break; \ user->hciemu_type = data->emu_type; \ user->test_data = data; \ tester_add_full(data->title, data, test_pre_setup, \ test_setup, test, test_teardown, \ test_post_teardown, 3, user, g_free); \ } while (0) static void tester_testcases_cleanup(void) { remove_bluetooth_tests(); remove_socket_tests(); remove_hidhost_tests(); remove_gatt_tests(); remove_a2dp_tests(); remove_avrcp_tests(); remove_hdp_tests(); remove_pan_tests(); } static void add_bluetooth_tests(void *data, void *user_data) { struct test_case *tc = data; test(tc, setup, generic_test_function, teardown); } static void add_socket_tests(void *data, void *user_data) { struct test_case *tc = data; test(tc, setup_socket, generic_test_function, teardown); } static void add_hidhost_tests(void *data, void *user_data) { struct test_case *tc = data; test(tc, setup_hidhost, generic_test_function, teardown); } static void add_pan_tests(void *data, void *user_data) { struct test_case *tc = data; test(tc, setup_pan, generic_test_function, teardown); } static void add_hdp_tests(void *data, void *user_data) { struct test_case *tc = data; test(tc, setup_hdp, generic_test_function, teardown); } static void add_a2dp_tests(void *data, void *user_data) { struct test_case *tc = data; test(tc, setup_a2dp, generic_test_function, teardown); } static void add_avrcp_tests(void *data, void *user_data) { struct test_case *tc = data; test(tc, setup_avrcp, generic_test_function, teardown); } static void add_gatt_tests(void *data, void *user_data) { struct test_case *tc = data; test(tc, setup_gatt, generic_test_function, teardown); } static void add_map_client_tests(void *data, void *user_data) { struct test_case *tc = data; test(tc, setup_map_client, generic_test_function, teardown); } int main(int argc, char *argv[]) { snprintf(exec_dir, sizeof(exec_dir), "%s", dirname(argv[0])); tester_init(&argc, &argv); queue_foreach(get_bluetooth_tests(), add_bluetooth_tests, NULL); queue_foreach(get_socket_tests(), add_socket_tests, NULL); queue_foreach(get_hidhost_tests(), add_hidhost_tests, NULL); queue_foreach(get_pan_tests(), add_pan_tests, NULL); queue_foreach(get_hdp_tests(), add_hdp_tests, NULL); queue_foreach(get_a2dp_tests(), add_a2dp_tests, NULL); queue_foreach(get_avrcp_tests(), add_avrcp_tests, NULL); queue_foreach(get_gatt_tests(), add_gatt_tests, NULL); queue_foreach(get_map_client_tests(), add_map_client_tests, NULL); if (tester_run()) return 1; tester_testcases_cleanup(); return 0; } bluez-5.82/android/PaxHeaders/pixit-hogp.txt0000644000000000000000000000005012537515745016135 xustar0020 atime=1743516876 20 ctime=1743591289 bluez-5.82/android/pixit-hogp.txt0000644000000000000000000000175112537515745015622 0ustar00rootrootHOGP PIXIT for the PTS tool. PTS version: 6.1 * - different than PTS defaults & - should be set to IUT Bluetooth address Required PIXIT settings ------------------------------------------------------------------------------- Parameter Name Value ------------------------------------------------------------------------------- TSPX_bd_addr_iut 112233445566 (*&) TSPX_time_guard 180000 TSPX_use_implicit_send TRUE TSPX_tester_database_file [Path to HOGP Test Database] TSPX_mtu_size 23 TSPX_secure_simple_pairing_pass_key_confirmation FALSE TSPX_delete_link_key FALSE TSPX_pin_code 0000 TSPX_use_dynamic_pin FALSE TSPX_delete_ltk FALSE TSPX_security_enabled TRUE TSPX_input_report_data CDA6F8B3AA TSPX_output_report_data 001234567890EF TSPX_feature_report_data 872D3F45EA TSPX_tester_appearance 03C0 TSPX_iut_use_resolvable_random_address FALSE ------------------------------------------------------------------------------- bluez-5.82/android/PaxHeaders/pts-did.txt0000644000000000000000000000005012537515745015411 xustar0020 atime=1743516876 20 ctime=1743591289 bluez-5.82/android/pts-did.txt0000644000000000000000000000117012537515745015071 0ustar00rootrootPTS test results for DID PTS version: 6.1 Tested: 04-May-2015 Android version: 5.1 Results: PASS test passed FAIL test failed INC test is inconclusive N/A test is disabled due to PICS setup ------------------------------------------------------------------------------- Test Name Result Notes ------------------------------------------------------------------------------- TC_SDI_BV_1_I PASS IUT must be discoverable TC_SDI_BV_2_I PASS IUT must be discoverable TC_SDI_BV_3_I PASS IUT must be discoverable TC_SDI_BV_4_I PASS IUT must be discoverable ------------------------------------------------------------------------------- bluez-5.82/android/PaxHeaders/pixit-mcap.txt0000644000000000000000000000005012537515745016120 xustar0020 atime=1743516876 20 ctime=1743591289 bluez-5.82/android/pixit-mcap.txt0000644000000000000000000000236012537515745015602 0ustar00rootrootMCAP PIXIT for the PTS tool. PTS version: 6.1 * - different than PTS defaults & - should be set to IUT Bluetooth address Required PIXIT settings ------------------------------------------------------------------------------- Parameter Name Value ------------------------------------------------------------------------------- TSPX_bd_addr_iut 112233445566 (*&) TSPX_delete_link_key FALSE TSPX_MCAP_DC_max 1 (*) TSPX_MCAP_l2cap_psm_control 1003 TSPX_MCAP_l2cap_psm_control_B TSPX_MCAP_l2cap_psm_data 1005 TSPX_MCAP_l2cap_psm_data_B TSPX_MCAP_bluetooth_clock_access_resolution 55 TSPX_MCAP_create_mdl_configuration TSPX_MCAP_data_channel_setup_mode TSPX_MCAP_iut_initiate_connection FALSE TSPX_MCAP_mdep_id 12 TSPX_MCAP_profile_name TSPX_MCAP_sync_lead_time 0BB8 TSPX_tester_role TSPX_MCAP_timestamp_native_accuracy 1400 TSPX_MCAP_timestamp_native_resolution 2233 TSPX_MCAP_timestamp_required_accuracy 6400 TSPX_host_class_of_device 100108 TSPX_pin_code 0000 TSPX_security_enabled TRUE (*) TSPX_time_guard 6000000 TSPX_use_dynamic_pin FALSE TSPX_use_implicit_send TRUE TSPX_verbose_implicit_send TRUE ------------------------------------------------------------------------------- bluez-5.82/android/PaxHeaders/pixit-spp.txt0000644000000000000000000000005012537515745016002 xustar0020 atime=1743516876 20 ctime=1743591289 bluez-5.82/android/pixit-spp.txt0000644000000000000000000000120612537515745015462 0ustar00rootrootSPP PIXIT for the PTS tool. PTS version: 6.1 * - different than PTS defaults & - should be set to IUT Bluetooth address Required PIXIT settings ------------------------------------------------------------------------------- Parameter Name Value ------------------------------------------------------------------------------- TSPX_bd_addr_iut 112233445566 (*&) TSPX_security_enabled TRUE TSPX_pin_code 0000 TSPX_time_guard 300000 TSPX_delete_link_key FALSE TSPX_stop_immediately_when_fail TRUE TSPX_class_of_device_tester 200408 ------------------------------------------------------------------------------- bluez-5.82/android/PaxHeaders/pan.h0000644000000000000000000000005014015011623014206 xustar0020 atime=1743516866 20 ctime=1743591279 bluez-5.82/android/pan.h0000644000000000000000000000044214015011623013667 0ustar00rootroot/* SPDX-License-Identifier: LGPL-2.1-or-later */ /* * * BlueZ - Bluetooth protocol stack for Linux * * Copyright (C) 2013-2014 Intel Corporation. All rights reserved. * * */ bool bt_pan_register(struct ipc *ipc, const bdaddr_t *addr, uint8_t mode); void bt_pan_unregister(void); bluez-5.82/android/PaxHeaders/hal-map-client.c0000644000000000000000000000005014015011623016216 xustar0020 atime=1743516862 20 ctime=1743591276 bluez-5.82/android/hal-map-client.c0000644000000000000000000000700714015011623015703 0ustar00rootroot// SPDX-License-Identifier: Apache-2.0 /* * Copyright (C) 2014 Intel Corporation * */ #include #include #include #include "hal-log.h" #include "hal.h" #include "hal-msg.h" #include "hal-ipc.h" static const btmce_callbacks_t *cbs = NULL; static bool interface_ready(void) { return cbs != NULL; } /* Event Handlers */ static void remote_mas_instances_to_hal(btmce_mas_instance_t *send_instance, struct hal_map_client_mas_instance *instance, int num_instances, uint16_t len) { void *buf = instance; char *name; int i; DBG(""); for (i = 0; i < num_instances; i++) { name = (char *) instance->name; if (sizeof(*instance) + instance->name_len > len || (instance->name_len != 0 && name[instance->name_len - 1] != '\0')) { error("invalid remote mas instance %d, aborting", i); exit(EXIT_FAILURE); } send_instance[i].id = instance->id; send_instance[i].msg_types = instance->msg_types; send_instance[i].scn = instance->scn; send_instance[i].p_name = name; len -= sizeof(*instance) + instance->name_len; buf += sizeof(*instance) + instance->name_len; instance = buf; } if (!len) return; error("invalid remote mas instances (%u bytes left), aborting", len); exit(EXIT_FAILURE); } static void handle_remote_mas_instances(void *buf, uint16_t len, int fd) { struct hal_ev_map_client_remote_mas_instances *ev = buf; btmce_mas_instance_t instances[ev->num_instances]; DBG(""); len -= sizeof(*ev); remote_mas_instances_to_hal(instances, ev->instances, ev->num_instances, len); if (cbs->remote_mas_instances_cb) cbs->remote_mas_instances_cb(ev->status, (bt_bdaddr_t *) ev->bdaddr, ev->num_instances, instances); } /* * handlers will be called from notification thread context, * index in table equals to 'opcode - HAL_MINIMUM_EVENT' */ static const struct hal_ipc_handler ev_handlers[] = { /* HAL_EV_MCE_REMOTE_MAS_INSTANCES */ { handle_remote_mas_instances, true, sizeof(struct hal_ev_map_client_remote_mas_instances) } }; /* API */ static bt_status_t get_remote_mas_instances(bt_bdaddr_t *bd_addr) { struct hal_cmd_map_client_get_instances cmd; if (!interface_ready()) return BT_STATUS_NOT_READY; memcpy(cmd.bdaddr, bd_addr, sizeof(*bd_addr)); return hal_ipc_cmd(HAL_SERVICE_ID_MAP_CLIENT, HAL_OP_MAP_CLIENT_GET_INSTANCES, sizeof(cmd), &cmd, NULL, NULL, NULL); } static bt_status_t init(btmce_callbacks_t *callbacks) { struct hal_cmd_register_module cmd; int ret; DBG(""); /* * Interface ready check was removed because there is no cleanup * function to unregister and clear callbacks. MAP client testers may * restart bluetooth, unregister this profile and try to reuse it. * This situation make service unregistered but callbacks are still * set - interface is ready. On android devices there is no need to * re-init MAP client profile while bluetooth is loaded. */ cbs = callbacks; hal_ipc_register(HAL_SERVICE_ID_MAP_CLIENT, ev_handlers, sizeof(ev_handlers)/sizeof(ev_handlers[0])); cmd.service_id = HAL_SERVICE_ID_MAP_CLIENT; cmd.mode = HAL_MODE_DEFAULT; cmd.max_clients = 1; ret = hal_ipc_cmd(HAL_SERVICE_ID_CORE, HAL_OP_REGISTER_MODULE, sizeof(cmd), &cmd, 0, NULL, NULL); if (ret != BT_STATUS_SUCCESS) { cbs = NULL; hal_ipc_unregister(HAL_SERVICE_ID_MAP_CLIENT); } return ret; } static btmce_interface_t iface = { .size = sizeof(iface), .init = init, .get_remote_mas_instances = get_remote_mas_instances }; btmce_interface_t *bt_get_map_client_interface(void) { return &iface; } bluez-5.82/android/PaxHeaders/a2dp.h0000644000000000000000000000005014015011623014256 xustar0020 atime=1743516866 20 ctime=1743591278 bluez-5.82/android/a2dp.h0000644000000000000000000000044414015011623013741 0ustar00rootroot/* SPDX-License-Identifier: LGPL-2.1-or-later */ /* * * BlueZ - Bluetooth protocol stack for Linux * * Copyright (C) 2013-2014 Intel Corporation. All rights reserved. * * */ bool bt_a2dp_register(struct ipc *ipc, const bdaddr_t *addr, uint8_t mode); void bt_a2dp_unregister(void); bluez-5.82/android/PaxHeaders/pan.c0000644000000000000000000000005014015011623014201 xustar0020 atime=1743516866 20 ctime=1743591279 bluez-5.82/android/pan.c0000644000000000000000000004720614015011623013673 0ustar00rootroot// SPDX-License-Identifier: LGPL-2.1-or-later /* * * BlueZ - Bluetooth protocol stack for Linux * * Copyright (C) 2013-2014 Intel Corporation. All rights reserved. * * */ #ifdef HAVE_CONFIG_H #include #endif #define _GNU_SOURCE #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "btio/btio.h" #include "lib/bluetooth.h" #include "lib/bnep.h" #include "lib/sdp.h" #include "lib/sdp_lib.h" #include "src/uuid-helper.h" #include "profiles/network/bnep.h" #include "src/log.h" #include "hal-msg.h" #include "ipc-common.h" #include "ipc.h" #include "utils.h" #include "bluetooth.h" #include "pan.h" #define SVC_HINT_NETWORKING 0x02 #define BNEP_BRIDGE "bt-pan" #define BNEP_PANU_INTERFACE "bt-pan" #define BNEP_NAP_INTERFACE "bt-pan%d" struct pan_device { char iface[16]; bdaddr_t dst; uint8_t conn_state; uint8_t role; GIOChannel *io; struct bnep *session; guint watch; }; static bdaddr_t adapter_addr; static GSList *devices = NULL; static uint8_t local_role = HAL_PAN_ROLE_NONE; static uint32_t nap_rec_id = 0; static uint32_t panu_rec_id = 0; static GIOChannel *nap_io = NULL; static bool nap_bridge_mode = false; static struct ipc *hal_ipc = NULL; static int set_forward_delay(int sk) { unsigned long args[4] = { BRCTL_SET_BRIDGE_FORWARD_DELAY, 0 , 0, 0 }; struct ifreq ifr; memset(&ifr, 0, sizeof(ifr)); strncpy(ifr.ifr_name, BNEP_BRIDGE, IFNAMSIZ - 1); ifr.ifr_data = (char *) args; if (ioctl(sk, SIOCDEVPRIVATE, &ifr) < 0) { error("pan: setting forward delay failed: %d (%s)", errno, strerror(errno)); return -1; } return 0; } static int nap_create_bridge(void) { int sk, err; DBG("%s", BNEP_BRIDGE); if (nap_bridge_mode) return 0; sk = socket(AF_INET, SOCK_STREAM | SOCK_CLOEXEC, 0); if (sk < 0) return -EOPNOTSUPP; if (ioctl(sk, SIOCBRADDBR, BNEP_BRIDGE) < 0) { err = -errno; if (err != -EEXIST) { close(sk); return -EOPNOTSUPP; } } err = set_forward_delay(sk); if (err < 0) ioctl(sk, SIOCBRDELBR, BNEP_BRIDGE); close(sk); nap_bridge_mode = err == 0; return err; } static int bridge_if_down(void) { struct ifreq ifr; int sk, err; sk = socket(AF_INET, SOCK_DGRAM, 0); memset(&ifr, 0, sizeof(ifr)); strncpy(ifr.ifr_name, BNEP_BRIDGE, IF_NAMESIZE - 1); ifr.ifr_flags &= ~IFF_UP; /* Bring down the interface */ err = ioctl(sk, SIOCSIFFLAGS, (caddr_t) &ifr); close(sk); if (err < 0) { error("pan: Could not bring down %s", BNEP_BRIDGE); return err; } return 0; } static int nap_remove_bridge(void) { int sk, err; DBG("%s", BNEP_BRIDGE); if (!nap_bridge_mode) return 0; bridge_if_down(); sk = socket(AF_INET, SOCK_STREAM | SOCK_CLOEXEC, 0); if (sk < 0) return -EOPNOTSUPP; err = ioctl(sk, SIOCBRDELBR, BNEP_BRIDGE); if (err < 0) err = -errno; close(sk); if (err < 0) return err; nap_bridge_mode = false; return 0; } static int device_cmp(gconstpointer s, gconstpointer user_data) { const struct pan_device *dev = s; const bdaddr_t *dst = user_data; return bacmp(&dev->dst, dst); } static void pan_device_free(void *data) { struct pan_device *dev = data; if (dev->watch > 0) { bnep_server_delete(BNEP_BRIDGE, dev->iface, &dev->dst); g_source_remove(dev->watch); } if (dev->io) { g_io_channel_shutdown(dev->io, FALSE, NULL); g_io_channel_unref(dev->io); } if (dev->session) bnep_free(dev->session); g_free(dev); } static void pan_device_remove(struct pan_device *dev) { devices = g_slist_remove(devices, dev); if (g_slist_length(devices) == 0) { local_role = HAL_PAN_ROLE_NONE; nap_remove_bridge(); } pan_device_free(dev); } static void bt_pan_notify_conn_state(struct pan_device *dev, uint8_t state) { struct hal_ev_pan_conn_state ev; char addr[18]; if (dev->conn_state == state) return; dev->conn_state = state; ba2str(&dev->dst, addr); DBG("device %s state %u", addr, state); bdaddr2android(&dev->dst, ev.bdaddr); ev.state = state; ev.local_role = local_role; ev.remote_role = dev->role; ev.status = HAL_STATUS_SUCCESS; ipc_send_notif(hal_ipc, HAL_SERVICE_ID_PAN, HAL_EV_PAN_CONN_STATE, sizeof(ev), &ev); if (dev->conn_state == HAL_PAN_STATE_DISCONNECTED) pan_device_remove(dev); } static void bt_pan_notify_ctrl_state(struct pan_device *dev, uint8_t state, uint8_t status) { struct hal_ev_pan_ctrl_state ev; DBG(""); ev.state = state; ev.local_role = local_role; ev.status = status; memset(ev.name, 0, sizeof(ev.name)); if (local_role == HAL_PAN_ROLE_NAP) memcpy(ev.name, BNEP_BRIDGE, sizeof(BNEP_BRIDGE)); else if (local_role == HAL_PAN_ROLE_PANU) memcpy(ev.name, dev->iface, sizeof(dev->iface)); ipc_send_notif(hal_ipc, HAL_SERVICE_ID_PAN, HAL_EV_PAN_CTRL_STATE, sizeof(ev), &ev); } static void bnep_disconn_cb(void *data) { struct pan_device *dev = data; DBG("%s disconnected", dev->iface); bt_pan_notify_conn_state(dev, HAL_PAN_STATE_DISCONNECTED); } static void bnep_conn_cb(char *iface, int err, void *data) { struct pan_device *dev = data; DBG(""); if (err < 0) { error("bnep connect req failed: %s", strerror(-err)); bt_pan_notify_conn_state(dev, HAL_PAN_STATE_DISCONNECTED); return; } memcpy(dev->iface, iface, sizeof(dev->iface)); DBG("%s connected", dev->iface); bt_pan_notify_ctrl_state(dev, HAL_PAN_CTRL_ENABLED, HAL_STATUS_SUCCESS); bt_pan_notify_conn_state(dev, HAL_PAN_STATE_CONNECTED); } static void connect_cb(GIOChannel *chan, GError *err, gpointer data) { struct pan_device *dev = data; uint16_t l_role, r_role; int perr, sk; DBG(""); if (err) { error("%s", err->message); goto fail; } l_role = (local_role == HAL_PAN_ROLE_NAP) ? BNEP_SVC_NAP : BNEP_SVC_PANU; r_role = (dev->role == HAL_PAN_ROLE_NAP) ? BNEP_SVC_NAP : BNEP_SVC_PANU; sk = g_io_channel_unix_get_fd(dev->io); dev->session = bnep_new(sk, l_role, r_role, BNEP_PANU_INTERFACE); if (!dev->session) goto fail; perr = bnep_connect(dev->session, bnep_conn_cb, bnep_disconn_cb, dev, dev); if (perr < 0) { error("bnep connect req failed: %s", strerror(-perr)); goto fail; } if (dev->io) { g_io_channel_unref(dev->io); dev->io = NULL; } return; fail: bt_pan_notify_conn_state(dev, HAL_PAN_STATE_DISCONNECTED); } static void bt_pan_connect(const void *buf, uint16_t len) { const struct hal_cmd_pan_connect *cmd = buf; struct pan_device *dev; uint8_t status; bdaddr_t dst; char addr[18]; GSList *l; GError *gerr = NULL; DBG(""); switch (cmd->local_role) { case HAL_PAN_ROLE_NAP: if (cmd->remote_role != HAL_PAN_ROLE_PANU) { status = HAL_STATUS_UNSUPPORTED; goto failed; } break; case HAL_PAN_ROLE_PANU: if (cmd->remote_role != HAL_PAN_ROLE_NAP && cmd->remote_role != HAL_PAN_ROLE_PANU) { status = HAL_STATUS_UNSUPPORTED; goto failed; } break; default: status = HAL_STATUS_UNSUPPORTED; goto failed; } android2bdaddr(&cmd->bdaddr, &dst); l = g_slist_find_custom(devices, &dst, device_cmp); if (l) { status = HAL_STATUS_FAILED; goto failed; } dev = g_new0(struct pan_device, 1); bacpy(&dev->dst, &dst); local_role = cmd->local_role; dev->role = cmd->remote_role; ba2str(&dev->dst, addr); DBG("connecting to %s %s", addr, dev->iface); dev->io = bt_io_connect(connect_cb, dev, NULL, &gerr, BT_IO_OPT_SOURCE_BDADDR, &adapter_addr, BT_IO_OPT_DEST_BDADDR, &dev->dst, BT_IO_OPT_PSM, BNEP_PSM, BT_IO_OPT_SEC_LEVEL, BT_IO_SEC_MEDIUM, BT_IO_OPT_OMTU, BNEP_MTU, BT_IO_OPT_IMTU, BNEP_MTU, BT_IO_OPT_INVALID); if (!dev->io) { error("%s", gerr->message); g_error_free(gerr); g_free(dev); status = HAL_STATUS_FAILED; goto failed; } devices = g_slist_append(devices, dev); bt_pan_notify_conn_state(dev, HAL_PAN_STATE_CONNECTING); status = HAL_STATUS_SUCCESS; failed: ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_PAN, HAL_OP_PAN_CONNECT, status); } static void bt_pan_disconnect(const void *buf, uint16_t len) { const struct hal_cmd_pan_disconnect *cmd = buf; struct pan_device *dev; uint8_t status; GSList *l; bdaddr_t dst; DBG(""); android2bdaddr(&cmd->bdaddr, &dst); l = g_slist_find_custom(devices, &dst, device_cmp); if (!l) { status = HAL_STATUS_FAILED; goto failed; } dev = l->data; if (dev->conn_state == HAL_PAN_STATE_CONNECTED && dev->session) bnep_disconnect(dev->session); bt_pan_notify_conn_state(dev, HAL_PAN_STATE_DISCONNECTED); status = HAL_STATUS_SUCCESS; failed: ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_PAN, HAL_OP_PAN_DISCONNECT, status); } static gboolean nap_watchdog_cb(GIOChannel *chan, GIOCondition cond, gpointer user_data) { struct pan_device *dev = user_data; DBG("disconnected"); bt_pan_notify_conn_state(dev, HAL_PAN_STATE_DISCONNECTED); return FALSE; } static gboolean nap_setup_cb(GIOChannel *chan, GIOCondition cond, gpointer user_data) { struct pan_device *dev = user_data; uint8_t packet[BNEP_MTU]; int sk, n, err; if (cond & (G_IO_ERR | G_IO_HUP | G_IO_NVAL)) { error("Hangup or error or inval on BNEP socket"); return FALSE; } sk = g_io_channel_unix_get_fd(chan); /* * BNEP_SETUP_CONNECTION_REQUEST_MSG should be read and left in case * of kernel setup connection msg handling. */ n = recv(sk, packet, sizeof(packet), MSG_PEEK); if (n < 0) { error("read(): %s(%d)", strerror(errno), errno); goto failed; } if (n < 3) { error("pan: to few setup connection request data received"); goto failed; } err = nap_create_bridge(); if (err < 0) error("pan: Failed to create bridge: %s (%d)", strerror(-err), -err); if (bnep_server_add(sk, (err < 0) ? NULL : BNEP_BRIDGE, dev->iface, &dev->dst, packet, n) < 0) { error("pan: server_connadd failed"); goto failed; } dev->watch = g_io_add_watch(chan, G_IO_HUP | G_IO_ERR | G_IO_NVAL, nap_watchdog_cb, dev); g_io_channel_unref(dev->io); dev->io = NULL; bt_pan_notify_ctrl_state(dev, HAL_PAN_CTRL_ENABLED, HAL_STATUS_SUCCESS); bt_pan_notify_conn_state(dev, HAL_PAN_STATE_CONNECTED); return FALSE; failed: pan_device_remove(dev); return FALSE; } static void nap_connect_cb(GIOChannel *chan, GError *err, gpointer user_data) { struct pan_device *dev = user_data; DBG(""); if (err) { error("%s", err->message); bt_pan_notify_conn_state(dev, HAL_PAN_STATE_DISCONNECTED); return; } g_io_channel_set_close_on_unref(chan, TRUE); dev->watch = g_io_add_watch(chan, G_IO_IN | G_IO_HUP | G_IO_ERR | G_IO_NVAL, nap_setup_cb, dev); } static void nap_confirm_cb(GIOChannel *chan, gpointer data) { struct pan_device *dev; bdaddr_t dst; char address[18]; GError *err = NULL; DBG(""); bt_io_get(chan, &err, BT_IO_OPT_DEST_BDADDR, &dst, BT_IO_OPT_DEST, address, BT_IO_OPT_INVALID); if (err) { error("%s", err->message); g_error_free(err); return; } DBG("incoming connect request from %s", address); dev = g_new0(struct pan_device, 1); bacpy(&dev->dst, &dst); local_role = HAL_PAN_ROLE_NAP; dev->role = HAL_PAN_ROLE_PANU; strncpy(dev->iface, BNEP_NAP_INTERFACE, 16); dev->iface[15] = '\0'; dev->io = g_io_channel_ref(chan); g_io_channel_set_close_on_unref(dev->io, TRUE); if (!bt_io_accept(dev->io, nap_connect_cb, dev, NULL, &err)) { error("bt_io_accept: %s", err->message); g_error_free(err); goto failed; } devices = g_slist_append(devices, dev); bt_pan_notify_conn_state(dev, HAL_PAN_STATE_CONNECTING); return; failed: bt_pan_notify_conn_state(dev, HAL_PAN_STATE_DISCONNECTED); } static void destroy_nap_device(void) { DBG(""); nap_remove_bridge(); if (nap_io) { g_io_channel_shutdown(nap_io, FALSE, NULL); g_io_channel_unref(nap_io); nap_io = NULL; } } static int register_nap_server(void) { GError *gerr = NULL; DBG(""); nap_io = bt_io_listen(NULL, nap_confirm_cb, NULL, NULL, &gerr, BT_IO_OPT_SOURCE_BDADDR, &adapter_addr, BT_IO_OPT_PSM, BNEP_PSM, BT_IO_OPT_SEC_LEVEL, BT_IO_SEC_MEDIUM, BT_IO_OPT_OMTU, BNEP_MTU, BT_IO_OPT_IMTU, BNEP_MTU, BT_IO_OPT_INVALID); if (!nap_io) { destroy_nap_device(); error("%s", gerr->message); g_error_free(gerr); return -EIO; } return 0; } static void bt_pan_enable(const void *buf, uint16_t len) { const struct hal_cmd_pan_enable *cmd = buf; uint8_t status, state; int err; DBG(""); if (local_role == cmd->local_role) { status = HAL_STATUS_SUCCESS; goto reply; } /* destroy existing server */ destroy_nap_device(); switch (cmd->local_role) { case HAL_PAN_ROLE_NAP: break; case HAL_PAN_ROLE_NONE: local_role = HAL_PAN_ROLE_NONE; status = HAL_STATUS_SUCCESS; state = HAL_PAN_CTRL_DISABLED; goto notify; default: status = HAL_STATUS_UNSUPPORTED; goto reply; } local_role = cmd->local_role; err = register_nap_server(); if (err < 0) { status = HAL_STATUS_FAILED; destroy_nap_device(); goto reply; } status = HAL_STATUS_SUCCESS; state = HAL_PAN_CTRL_ENABLED; notify: bt_pan_notify_ctrl_state(NULL, state, status); reply: ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_PAN, HAL_OP_PAN_ENABLE, status); } static void bt_pan_get_role(const void *buf, uint16_t len) { struct hal_rsp_pan_get_role rsp; DBG(""); rsp.local_role = local_role; ipc_send_rsp_full(hal_ipc, HAL_SERVICE_ID_PAN, HAL_OP_PAN_GET_ROLE, sizeof(rsp), &rsp, -1); } static const struct ipc_handler cmd_handlers[] = { /* HAL_OP_PAN_ENABLE */ { bt_pan_enable, false, sizeof(struct hal_cmd_pan_enable) }, /* HAL_OP_PAN_GET_ROLE */ { bt_pan_get_role, false, 0 }, /* HAL_OP_PAN_CONNECT */ { bt_pan_connect, false, sizeof(struct hal_cmd_pan_connect) }, /* HAL_OP_PAN_DISCONNECT */ { bt_pan_disconnect, false, sizeof(struct hal_cmd_pan_disconnect) }, }; static sdp_record_t *nap_record(void) { sdp_list_t *svclass, *pfseq, *apseq, *root, *aproto; uuid_t root_uuid, nap, l2cap, bnep; sdp_profile_desc_t profile[1]; sdp_list_t *proto[2]; sdp_data_t *v, *p; uint16_t psm = BNEP_PSM, version = 0x0100; uint16_t security = 0x0001, type = 0xfffe; uint32_t rate = 0; const char *desc = "Network Access Point", *name = "Network Service"; sdp_record_t *record; uint16_t ptype[] = { 0x0800, /* IPv4 */ 0x0806, /* ARP */ }; sdp_data_t *head, *pseq, *data; record = sdp_record_alloc(); if (!record) return NULL; record->attrlist = NULL; record->pattern = NULL; sdp_uuid16_create(&nap, NAP_SVCLASS_ID); svclass = sdp_list_append(NULL, &nap); sdp_set_service_classes(record, svclass); sdp_uuid16_create(&profile[0].uuid, NAP_PROFILE_ID); profile[0].version = 0x0100; pfseq = sdp_list_append(NULL, &profile[0]); sdp_set_profile_descs(record, pfseq); sdp_set_info_attr(record, name, NULL, desc); sdp_attr_add_new(record, SDP_ATTR_NET_ACCESS_TYPE, SDP_UINT16, &type); sdp_attr_add_new(record, SDP_ATTR_MAX_NET_ACCESSRATE, SDP_UINT32, &rate); sdp_uuid16_create(&root_uuid, PUBLIC_BROWSE_GROUP); root = sdp_list_append(NULL, &root_uuid); sdp_set_browse_groups(record, root); sdp_uuid16_create(&l2cap, L2CAP_UUID); proto[0] = sdp_list_append(NULL, &l2cap); p = sdp_data_alloc(SDP_UINT16, &psm); proto[0] = sdp_list_append(proto[0], p); apseq = sdp_list_append(NULL, proto[0]); sdp_uuid16_create(&bnep, BNEP_UUID); proto[1] = sdp_list_append(NULL, &bnep); v = sdp_data_alloc(SDP_UINT16, &version); proto[1] = sdp_list_append(proto[1], v); head = sdp_data_alloc(SDP_UINT16, &ptype[0]); data = sdp_data_alloc(SDP_UINT16, &ptype[1]); sdp_seq_append(head, data); pseq = sdp_data_alloc(SDP_SEQ16, head); proto[1] = sdp_list_append(proto[1], pseq); apseq = sdp_list_append(apseq, proto[1]); aproto = sdp_list_append(NULL, apseq); sdp_set_access_protos(record, aproto); sdp_add_lang_attr(record); sdp_attr_add_new(record, SDP_ATTR_SECURITY_DESC, SDP_UINT16, &security); sdp_data_free(p); sdp_data_free(v); sdp_list_free(apseq, NULL); sdp_list_free(root, NULL); sdp_list_free(aproto, NULL); sdp_list_free(proto[0], NULL); sdp_list_free(proto[1], NULL); sdp_list_free(svclass, NULL); sdp_list_free(pfseq, NULL); return record; } static sdp_record_t *panu_record(void) { sdp_list_t *svclass, *pfseq, *apseq, *root, *aproto; uuid_t root_uuid, panu, l2cap, bnep; sdp_profile_desc_t profile[1]; sdp_list_t *proto[2]; sdp_data_t *v, *p; uint16_t psm = BNEP_PSM, version = 0x0100; uint16_t security = 0x0001, type = 0xfffe; uint32_t rate = 0; const char *desc = "PAN User", *name = "Network Service"; sdp_record_t *record; uint16_t ptype[] = { 0x0800, /* IPv4 */ 0x0806, /* ARP */ }; sdp_data_t *head, *pseq, *data; record = sdp_record_alloc(); if (!record) return NULL; record->attrlist = NULL; record->pattern = NULL; sdp_uuid16_create(&panu, PANU_SVCLASS_ID); svclass = sdp_list_append(NULL, &panu); sdp_set_service_classes(record, svclass); sdp_uuid16_create(&profile[0].uuid, PANU_PROFILE_ID); profile[0].version = 0x0100; pfseq = sdp_list_append(NULL, &profile[0]); sdp_set_profile_descs(record, pfseq); sdp_set_info_attr(record, name, NULL, desc); sdp_attr_add_new(record, SDP_ATTR_NET_ACCESS_TYPE, SDP_UINT16, &type); sdp_attr_add_new(record, SDP_ATTR_MAX_NET_ACCESSRATE, SDP_UINT32, &rate); sdp_uuid16_create(&root_uuid, PUBLIC_BROWSE_GROUP); root = sdp_list_append(NULL, &root_uuid); sdp_set_browse_groups(record, root); sdp_uuid16_create(&l2cap, L2CAP_UUID); proto[0] = sdp_list_append(NULL, &l2cap); p = sdp_data_alloc(SDP_UINT16, &psm); proto[0] = sdp_list_append(proto[0], p); apseq = sdp_list_append(NULL, proto[0]); sdp_uuid16_create(&bnep, BNEP_UUID); proto[1] = sdp_list_append(NULL, &bnep); v = sdp_data_alloc(SDP_UINT16, &version); proto[1] = sdp_list_append(proto[1], v); head = sdp_data_alloc(SDP_UINT16, &ptype[0]); data = sdp_data_alloc(SDP_UINT16, &ptype[1]); sdp_seq_append(head, data); pseq = sdp_data_alloc(SDP_SEQ16, head); proto[1] = sdp_list_append(proto[1], pseq); apseq = sdp_list_append(apseq, proto[1]); aproto = sdp_list_append(NULL, apseq); sdp_set_access_protos(record, aproto); sdp_add_lang_attr(record); sdp_attr_add_new(record, SDP_ATTR_SECURITY_DESC, SDP_UINT16, &security); sdp_data_free(p); sdp_data_free(v); sdp_list_free(apseq, NULL); sdp_list_free(root, NULL); sdp_list_free(aproto, NULL); sdp_list_free(proto[0], NULL); sdp_list_free(proto[1], NULL); sdp_list_free(svclass, NULL); sdp_list_free(pfseq, NULL); return record; } bool bt_pan_register(struct ipc *ipc, const bdaddr_t *addr, uint8_t mode) { sdp_record_t *nap_rec, *panu_rec; int err; DBG(""); bacpy(&adapter_addr, addr); nap_rec = nap_record(); if (bt_adapter_add_record(nap_rec, SVC_HINT_NETWORKING) < 0) { sdp_record_free(nap_rec); error("Failed to allocate PAN-NAP sdp record"); return false; } panu_rec = panu_record(); if (bt_adapter_add_record(panu_rec, SVC_HINT_NETWORKING) < 0) { sdp_record_free(nap_rec); sdp_record_free(panu_rec); error("Failed to allocate PAN-PANU sdp record"); return false; } err = bnep_init(); if (err < 0) { error("Failed to init BNEP"); bt_adapter_remove_record(nap_rec->handle); bt_adapter_remove_record(panu_rec->handle); return false; } err = register_nap_server(); if (err < 0) { error("Failed to register NAP server"); bt_adapter_remove_record(nap_rec->handle); bt_adapter_remove_record(panu_rec->handle); bnep_cleanup(); return false; } nap_rec_id = nap_rec->handle; panu_rec_id = panu_rec->handle; hal_ipc = ipc; ipc_register(hal_ipc, HAL_SERVICE_ID_PAN, cmd_handlers, G_N_ELEMENTS(cmd_handlers)); return true; } void bt_pan_unregister(void) { DBG(""); g_slist_free_full(devices, pan_device_free); devices = NULL; local_role = HAL_PAN_ROLE_NONE; bnep_cleanup(); ipc_unregister(hal_ipc, HAL_SERVICE_ID_PAN); hal_ipc = NULL; bt_adapter_remove_record(nap_rec_id); nap_rec_id = 0; bt_adapter_remove_record(panu_rec_id); panu_rec_id = 0; destroy_nap_device(); } bluez-5.82/android/PaxHeaders/pts-sdp.txt0000644000000000000000000000005012537515745015437 xustar0020 atime=1743516876 20 ctime=1743591289 bluez-5.82/android/pts-sdp.txt0000644000000000000000000000412412537515745015121 0ustar00rootrootPTS test results for SDP PTS version: 6.1 Tested: 13-May-2015 Android version: 5.1 Results: PASS test passed FAIL test failed INC test is inconclusive N/A test is disabled due to PICS setup NONE test result is none Note: from haltest: bluetooth set_adapter_property BT_PROPERTY_ADAPTER_SCAN_MODE BT_SCAN_MODE_CONNECTABLE bluetooth enable socket listen BTSOCK_L2CAP BlueZ 0 ------------------------------------------------------------------------------- Test Name Result Notes ------------------------------------------------------------------------------- TC_SERVER_BRW_BV_01_C PASS TC_SERVER_BRW_BV_01_C PASS TC_SERVER_SA_BI_01_C PASS TC_SERVER_SA_BI_02_C PASS TC_SERVER_SA_BI_03_C PASS TC_SERVER_SA_BV_01_C PASS TC_SERVER_SA_BV_03_C PASS TC_SERVER_SA_BV_04_C PASS TC_SERVER_SA_BV_05_C PASS TC_SERVER_SA_BV_06_C PASS TC_SERVER_SA_BV_07_C PASS TC_SERVER_SA_BV_08_C PASS TC_SERVER_SA_BV_09_C PASS TC_SERVER_SA_BV_10_C PASS TC_SERVER_SA_BV_11_C PASS TC_SERVER_SA_BV_12_C PASS TC_SERVER_SA_BV_13_C PASS TC_SERVER_SA_BV_14_C PASS TC_SERVER_SA_BV_15_C PASS TC_SERVER_SA_BV_16_C PASS TC_SERVER_SA_BV_17_C PASS TC_SERVER_SA_BV_18_C PASS TC_SERVER_SA_BV_19_C PASS TC_SERVER_SA_BV_20_C PASS TC_SERVER_SA_BV_21_C PASS TC_SERVER_SS_BI_01_C PASS TC_SERVER_SS_BI_02_C PASS TC_SERVER_SS_BV_01_C PASS TC_SERVER_SS_BV_03_C PASS TC_SERVER_SS_BV_04_C PASS TC_SERVER_SSA_BI_01_C PASS TC_SERVER_SSA_BI_02_C PASS TC_SERVER_SSA_BV_01_C PASS TC_SERVER_SSA_BV_02_C PASS TC_SERVER_SSA_BV_03_C PASS TC_SERVER_SSA_BV_04_C PASS TC_SERVER_SSA_BV_06_C PASS TC_SERVER_SSA_BV_07_C PASS TC_SERVER_SSA_BV_08_C PASS TC_SERVER_SSA_BV_09_C PASS TC_SERVER_SSA_BV_10_C PASS TC_SERVER_SSA_BV_11_C PASS TC_SERVER_SSA_BV_12_C PASS TC_SERVER_SSA_BV_13_C PASS TC_SERVER_SSA_BV_14_C PASS TC_SERVER_SSA_BV_15_C PASS TC_SERVER_SSA_BV_16_C PASS TC_SERVER_SSA_BV_17_C PASS TC_SERVER_SSA_BV_18_C PASS TC_SERVER_SSA_BV_19_C PASS TC_SERVER_SSA_BV_20_C PASS TC_SERVER_SSA_BV_21_C PASS TC_SERVER_SSA_BV_22_C PASS TC_SERVER_SSA_BV_23_C PASS ------------------------------------------------------------------------------- bluez-5.82/android/PaxHeaders/pixit-did.txt0000644000000000000000000000005012537515745015740 xustar0020 atime=1743516876 20 ctime=1743591289 bluez-5.82/android/pixit-did.txt0000644000000000000000000000150212537515745015417 0ustar00rootrootDID PIXIT for the PTS tool. PTS version: 6.1 * - different than PTS defaults & - should be set to IUT Bluetooth address Required PIXIT settings ------------------------------------------------------------------------------- Parameter Name Value ------------------------------------------------------------------------------- TSPX_security_enabled False TSPX_ClientExecutableURL False (*) TSPX_ServiceDescription False (*) TSPX_DocumentationURL False (*) TSPX_bd_addr_iut 112233445566 (*&) TSPX_class_of_device_pts 200404 TSPX_device_search_time 30 TSPX_delete_link_key False TSPX_pin_code 0000 TSPX_time_guard 200000 TSPX_use_implicit_send True TSPX_secure_simple_pairing_pass_key_confirmation False ------------------------------------------------------------------------------- bluez-5.82/android/PaxHeaders/pts-gap.txt0000644000000000000000000000005012537515745015420 xustar0020 atime=1743516876 20 ctime=1743591289 bluez-5.82/android/pts-gap.txt0000644000000000000000000003417212537515745015110 0ustar00rootrootPTS test results for GAP PTS version: 6.1 Tested: 11-May-2015 Android version: 5.1 Kernel version: 4.1 Results: PASS test passed FAIL test failed INC test is inconclusive N/A test is disabled due to PICS setup ------------------------------------------------------------------------------- Test Name Result Notes ------------------------------------------------------------------------------- TC_MOD_NDIS_BV_01_C PASS IUT must be non-discoverable TC_MOD_LDIS_BV_01_C PASS btmgmt discov limited 30 TC_MOD_LDIS_BV_02_C PASS btmgmt discov limited 30 TC_MOD_LDIS_BV_03_C PASS btmgmt discov limited 30 TC_MOD_GDIS_BV_01_C PASS IUT must be discoverable TC_MOD_GDIS_BV_02_C PASS IUT must be discoverable TC_MOD_NCON_BV_01_C PASS btmgmt connectable off TC_MOD_CON_BV_01_C PASS btmgmt connectable on TC_BROB_BCST_BV_01_C N/A TC_BROB_BCST_BV_02_C N/A TC_BROB_BCST_BV_03_C N/A TC_BROB_OBSV_BV_01_C N/A TC_BROB_OBSV_BV_02_C N/A TC_BROB_OBSV_BV_03_C N/A TC_BROB_OBSV_BV_04_C N/A TC_BROB_OBSV_BV_05_C N/A TC_DISC_NONM_BV_01_C PASS btmgmt connectable off btmgmt advertising on TC_DISC_NONM_BV_02_C PASS btmgmt connectable on btmgmt discov off btmgmt advertising on TC_DISC_LIMM_BV_01_C PASS btmgmt connectable on btmgmt discov off btmgmt discov limited 30 TC_DISC_LIMM_BV_02_C PASS btmgmt connectable on btmgmt advertising on btmgmt discov limited 30 TC_DISC_LIMM_BV_03_C PASS btmgmt connectable on btmgmt discov off btmgmt discov limited 30 btmgmt advertising on TC_DISC_LIMM_BV_04_C PASS btmgmt connectable on btmgmt discov off btmgmt power off btmgmt bredr off btmgmt power on btmgmt discov limited 30 btmgmt advertising on TC_DISC_GENM_BV_01_C PASS btmgmt connectable on btmgmt discov on btmgmt advertising on TC_DISC_GENM_BV_02_C PASS btmgmt connectable on btmgmt advertising on btmgmt discov on TC_DISC_GENM_BV_03_C PASS btmgmt connectable on btmgmt discov on btmgmt advertising on TC_DISC_GENM_BV_04_C PASS btmgmt connectable on btmgmt power off btmgmt le on btmgmt bredr off btmgmt power on btmgmt discov on btmgmt advertising on TC_DISC_LIMP_BV_01_C PASS btmgmt find -l PTS AD flags must have bit 1 unset and bit 0 set TC_DISC_LIMP_BV_02_C PASS btmgmt find -l PTS AD flags must have bit 1 set and bit 0 unset TC_DISC_LIMP_BV_03_C PASS btmgmt find -l PTS AD flags must have bit 1 and bit 0 unset TC_DISC_LIMP_BV_04_C PASS btmgmt find -l PTS AD flags must have bit 1 and bit 0 unset TC_DISC_LIMP_BV_05_C PASS btmgmt find -l PTS AD flags must have bit 1 and bit 0 unset TC_DISC_GENP_BV_01_C PASS btmgmt find -l PTS AD flags must have bit 1 set and bit 0 unset TC_DISC_GENP_BV_02_C PASS btmgmt find -l PTS AD flags must have bit 1 unset and bit 0 set TC_DISC_GENP_BV_03_C PASS btmgmt find -l PTS AD flags must have bit 1 and bit 0 unset TC_DISC_GENP_BV_04_C PASS btmgmt find -l PTS AD flags must have bit 1 and bit 0 unset TC_DISC_GENP_BV_05_C PASS btmgmt find -l PTS AD flags must have bit 1 and bit 0 unset TC_IDLE_GIN_BV_01_C PASS Start discovery from IUT TC_IDLE_LIN_BV_01_C PASS hcitool scan --iac=liac TC_IDLE_NAMP_BV_01_C PASS haltest: gattc register_client gattc listen 1 gattc search_service 1 1800 gattc get_characteristic 1 {1800,0,1} gattc read_characteristic 1 {1800,0,1} {2a00,1} TC_IDLE_NAMP_BV_02_C PASS btmgmt advertising on TC_CONN_NCON_BV_01_C PASS btmgmt connectable off btmgmt advertising on TC_CONN_NCON_BV_02_C PASS Note: non-connectable and discoverable ? TC_CONN_NCON_BV_03_C PASS Note: non-connectable and discoverable ? TC_CONN_DCON_BV_01_C PASS btmgmt connectable on btmgmt advertising on TC_CONN_DCON_BV_02_C N/A TC_CONN_DCON_BV_03_C N/A TC_CONN_UCON_BV_01_C PASS btmgmt connectable on btmgmt advertising on TC_CONN_UCON_BV_02_C PASS btmgmt connectable on btmgmt discov on btmgmt advertising on TC_CONN_UCON_BV_03_C PASS btmgmt connectable on btmgmt advertising on btmgmt discov limited 30 TC_CONN_UCON_BV_04_C N/A TC_CONN_UCON_BV_05_C N/A TC_CONN_ACEP_BV_01_C PASS 'gattc connect' prior to pressing OK on PTS TC_CONN_ACEP_BV_02_C N/A TC_CONN_GCEP_BV_01_C PASS 'gattc connect' prior to pressing OK on PTS TC_CONN_GCEP_BV_02_C PASS 'gattc connect' prior to pressing OK on PTS TC_CONN_GCEP_BV_03_C N/A TC_CONN_GCEP_BV_04_C N/A TC_CONN_SCEP_BV_01_C PASS 'gattc connect' prior to pressing OK on PTS TC_CONN_SCEP_BV_02_C N/A TC_CONN_DCEP_BV_01_C PASS 'gattc connect' prior to pressing OK on PTS TC_CONN_DCEP_BV_02_C N/A TC_CONN_DCEP_BV_03_C PASS gattc connect TC_CONN_DCEP_BV_04_C N/A TC_CONN_CPUP_BV_01_C PASS btmgmt advertising on TC_CONN_CPUP_BV_02_C PASS btmgmt advertising on TC_CONN_CPUP_BV_03_C PASS btmgmt advertising on TC_CONN_CPUP_BV_04_C PASS gattc register_client gattc connect gattc disconnect TC_CONN_CPUP_BV_05_C PASS gattc register_client gattc connect gattc disconnect TC_CONN_CPUP_BV_06_C PASS gattc register_client gattc connect 1 hcitool lecup 0x00C8 0x0960 0x0007 0x0960 gattc disconnect TC_CONN_TERM_BV_01_C PASS gattc register_client gattc listen gattc disconnect TC_CONN_PRDA_BV_01_C PASS gattc register_client gattc listen gattc disconnect TC_CONN_PRDA_BV_02_C PASS PTS issue #12950 gattc register_client gattc connect bluetooth create_bond gattc connect gattc test_command 226 0 2 TC_BOND_NBON_BV_01_C PASS haltest: gattc register_client gattc connect gatt disconnect gattc connect gatt disconnect TC_BOND_NBON_BV_02_C PASS haltest: gattc register_client gattc connect
bluetooth create_bond
gattc connect
bluetooth create_bond
TC_BOND_NBON_BV_03_C PASS haltest: gattc listen TC_BOND_BON_BV_01_C PASS PTS issue #12503 haltest: bluetooth set_adapter_property BT_PROPERTY_ADAPTER_SCAN_MODE BT_SCAN_MODE_CONNECTABLE gattc register_client gattc listen 1 bluetooth create_bond TC_BOND_BON_BV_02_C PASS gattc regicter_client gattc scan gattc connect bluetooth create_bond gattc connect gattc test_command 226 1 TC_BOND_BON_BV_03_C PASS gattc register_client gattc listen 1 TC_BOND_BON_BV_04_C PASS haltest: gattc_register_client gattc connect
gattc disconnect gattc connect
gattc test_command 226 0 2 TC_SEC_AUT_BV_11_C PASS haltest: gattc register_client gatts register_server gatts add_service 2 3 gatts add_characteristic 2 1b 10 68 gatts start_service 2 1b 1 gattc listen 1 PTS asks for handle with Insufficient auth gatts send_response 1 1 0 1d 0 0x1234 TC_SEC_AUT_BV_12_C PASS haltest: gatts register_server gatts add_service 1 3 gatts add_characteristic 1 1b 10 68 gatts start_service 1 1b 1 gatts connect 1 PTS asks for handle with Insufficient auth gatts send_response 1 1 0 1d 0 0x1234 TC_SEC_AUT_BV_13_C PASS haltest: gatts register_server gatts add_service 1 3 gatts add_characteristic 1 1b 10 68 gatts start_service 1 1b 1 gatts connect 1 PTS asks for handle with Insufficient auth gatts send_response 1 1 0 1d 0 0x1234 TC_SEC_AUT_BV_14_C PASS haltest: gattc register_client gatts register_server gatts add_service 2 3 gatts add_characteristic 2 1b 10 68 gatts start_service 2 1b 1 gattc listen 1 PTS asks for handle with Insufficient auth gatts send_response 1 1 0 1d 0 0x1234 TC_SEC_AUT_BV_15_C N/A TC_SEC_AUT_BV_16_C N/A TC_SEC_AUT_BV_17_C PASS haltest: gattc register_client gattc connect gattc search_service gattc get_characteristic gattc read_characteristic bluetooth create_bond TC_SEC_AUT_BV_18_C PASS haltest: gattc register_client gattc listen gattc search_service gattc get_characteristic gattc read_characteristic bluetooth create_bond gattc read_characteristic TC_SEC_AUT_BV_19_C PASS TC_SEC_AUT_BV_20_C PASS haltest: gattc register_client gattc listen 1 1 gattc search_service 2 gattc get_characteristic 2 {1801,1,1} gattc read_characteristic 2 {1801,1,1} {2a05,1} gattc read_characteristic 2 {1801,1,1} {2a05,1} 1 TC_SEC_AUT_BV_21_C PASS haltest: gattc register_client gattc connect bluetooth create_bond gattc connect gattc test_command 226 0 1 TC_SEC_AUT_BV_22_C PASS btmgmt io-cap 3 haltest: gattc register_client gattc listen gattc test_command 226 1 TC_SEC_AUT_BV_23_C PASS haltest: gattc register_client gatts register_server gatts add_service 2 3 gatts add_characteristic 2 1b 10 34 gatts start_service 2 1b 1 gattc listen 1 PTS asks for handle with insufficient encryption gatts send_response 3 1 0 1d 0 0x1234 TC_SEC_AUT_BV_24_C PASS haltest: gatts register_server gatts add_service 1 3 gatts add_characteristic 1 1d 10 34 gatts start_service 1 1d 1 gatts connect gatts disconnect gatts connect PTS asks for handle with insufficient encryption gatts send_response 2 1 0 1f 0 0x1234 TC_SEC_CSIGN_BV_01_C PASS haltest: gattc connect bluetooth create_bond gattc connect gattc write_characteristic: 4 gattc disconnect TC_SEC_CSIGN_BV_02_C PASS haltest: gattc register_client gatts register_server gatts add_service 2 3 gatts add_characteristic 2 1d 66 129 gatts start_service 2 1d 1 gattc listen 1 gatts disconnect TC_SEC_CSIGN_BI_01_C PASS gattc register_client gatts register_server gatts add_service 2 3 gatts add_characteristic 2 1d 66 129 gatts start_service 2 1d 1 gattc listen 1 gatts disconnect gattc disconnect TC_SEC_CSIGN_BI_02_C PASS gattc register_client gatts register_server gatts add_service 2 3 gatts add_characteristic 2 1b 66 129 gatts start_service 2 1b 1 gattc listen 1 gatts disconnect gattc disconnect TC_SEC_CSIGN_BI_03_C PASS gattc register_client gatts register_server gatts add_service 2 3 gatts add_characteristic 2 1b 66 129 gatts start_service 2 1b 1 gattc listen 1 gatts disconnect gattc disconnect bluetooth remove_bond TC_SEC_CSIGN_BI_04_C PASS gattc register_client gatts register_server gatts add_service 2 3 gatts add_characteristic 2 1b 64 256 gatts start_service 2 1b 1 gattc listen 1 gatts disconnect gattc disconnect TC_PRIV_CONN_BV_01_C N/A TC_PRIV_CONN_BV_02_C N/A TC_PRIV_CONN_BV_03_C N/A TC_PRIV_CONN_BV_04_C N/A TC_PRIV_CONN_BV_05_C N/A TC_PRIV_CONN_BV_06_C N/A TC_PRIV_CONN_BV_07_C N/A TC_PRIV_CONN_BV_08_C N/A TC_PRIV_CONN_BV_09_C N/A TC_PRIV_CONN_BV_10_C PASS PTS issue #12951 Note: PIXITs required to be changed: TSPX_using_public_device_address: FALSE TSPX_using_random_device_address: TRUE echo 30 > /sys/kernel/debug/bluetooth/hci0/ rpa_timeout btmgmt power off btmgmt privacy on btmgmt power on TC_PRIV_CONN_BV_11_C INC PTS issue #12952 JIRA #BA-186 TC_ADV_BV_01_C N/A TC_ADV_BV_02_C PASS gattc register_client gattc listen 1 1 TC_ADV_BV_03_C PASS gattc register_client gattc listen 1 1 TC_ADV_BV_04_C N/A TC_ADV_BV_05_C PASS gattc register_client gattc listen 1 1 TC_ADV_BV_06_C N/A TC_ADV_BV_07_C N/A TC_ADV_BV_08_C N/A TC_ADV_BV_09_C N/A TC_ADV_BV_10_C N/A TC_ADV_BV_11_C N/A TC_ADV_BV_12_C N/A TC_ADV_BV_13_C N/A TC_ADV_BV_14_C N/A TC_ADV_BV_15_C N/A TC_ADV_BV_16_C N/A TC_GAT_BV_01_C PASS haltest: gattc register_client gattc listen TC_GAT_BV_02_C N/A TC_GAT_BV_03_C N/A TC_GAT_BV_04_C N/A TC_GAT_BV_05_C N/A TC_GAT_BV_06_C N/A TC_GAT_BV_07_C N/A TC_GAT_BV_08_C N/A TC_DM_NCON_BV_01_C PASS bluetooth set_adapter_property BT_PROPERTY_ADAPTER_SCAN_MODE BT_SCAN_MODE_NONE gattc register_client gattc listen 1 TC_DM_CON_BV_01_C PASS bluetooth set_adapter_property BT_PROPERTY_ADAPTER_SCAN_MODE BT_SCAN_MODE_CONNECTABLE_DISCOVERABLE gattc register_client gattc listen 1 TC_DM_NBON_BV_01_C PASS btmgmt pairable off btmgmt pair -c 0x04 -t 0x01 TC_DM_BON_BV_01_C PASS btmgmt pairable on btmgmt pair -c 0x04 -t 0x01 TC_DM_GIN_BV_01_C PASS TC_DM_LIN_BV_01_C PASS TC_DM_NAD_BV_01_C PASS btmgmt find TC_DM_NAD_BV_02_C PASS TC_DM_LEP_BV_01_C PASS bluetooth set_adapter_property BT_PROPERTY_ADAPTER_SCAN_MODE BT_SCAN_MODE_CONNECTABLE_DISCOVERABLE gattc register_client gattc listen 1 1 TC_DM_LEP_BV_02_C PASS Use basic rate PTS dongle haltest: bluetooth set_adapter_property TC_DM_LEP_BV_04_C PASS haltest: gattc connect TC_DM_LEP_BV_05_C PASS Use basic rate PTS dongle btmgmt find -b l2test -n TC_DM_LEP_BV_06_C PASS gattc connect TC_DM_LEP_BV_07_C PASS bluetooth set_adapter_property BT_PROPERTY_ADAPTER_SCAN_MODE BT_SCAN_MODE_CONNECTABLE_DISCOVERABLE gattc register_client gattc listen 1 1 TC_DM_LEP_BV_08_C PASS bluetooth set_adapter_property BT_PROPERTY_ADAPTER_SCAN_MODE BT_SCAN_MODE_CONNECTABLE_DISCOVERABLE gattc register_client gattc listen 1 1 TC_DM_LEP_BV_09_C PASS haltest: bluetooth enable bluetooth set_adapter_property BT_PROPERTY_ADAPTER_SCAN_MODE BT_SCAN_MODE_CONNECTABLE_DISCOVERABLE gattc register_client gattc scan 1 gattc connect l2test -n -P 31 disconnect TC_DM_LEP_BV_10_C PASS btmgmt find l2test -n -P 31 TC_DM_LEP_BV_11_C PASS haltest: bluetooth enable bluetooth set_adapter_property BT_PROPERTY_ADAPTER_SCAN_MODE BT_SCAN_MODE_CONNECTABLE_DISCOVERABLE gattc register_client gattc connect gattc disconnect ------------------------------------------------------------------------------- bluez-5.82/android/PaxHeaders/pics-bnep.txt0000644000000000000000000000005012537515745015725 xustar0020 atime=1743516876 20 ctime=1743591289 bluez-5.82/android/pics-bnep.txt0000644000000000000000000000176712537515745015421 0ustar00rootrootBNEP PICS for the PTS tool. PTS version: 6.1 * - different than PTS defaults # - not yet implemented/supported M - mandatory if such role selected O - optional Profile Version ------------------------------------------------------------------------------- Parameter Name Selected Description ------------------------------------------------------------------------------- TSPC_BNEP_1_1 True BNEP Connection Setup (M) TSPC_BNEP_1_2 True (*) BNEP Data Packet Reception (M) TSPC_BNEP_1_3 True (*) BNEP Data Packet Transmission (M) TSPC_BNEP_1_3a True (*) BNEP Compressed Packet Transmission (O) TSPC_BNEP_1_3b True (*) BNEP Compressed Packet Transmission Source Only (O) TSPC_BNEP_1_4 True BNEP Control Message Processing (M) TSPC_BNEP_1_5 True BNEP Extension Header Processing (M) TSPC_BNEP_1_6 True Network Protocol Filter Message Transmission (O) TSPC_BNEP_1_7 True Multicast Address Filter Message Transmission (O) ------------------------------------------------------------------------------- bluez-5.82/android/PaxHeaders/pixit-bnep.txt0000644000000000000000000000005012537515745016124 xustar0020 atime=1743516876 20 ctime=1743591289 bluez-5.82/android/pixit-bnep.txt0000644000000000000000000000157512537515745015615 0ustar00rootrootBNEP PIXIT for the PTS tool. PTS version: 6.1 * - different than PTS defaults & - should be set to IUT Bluetooth address # - should be set to PTS's bin/audio folder Required PIXIT settings ------------------------------------------------------------------------------- Parameter Name Value ------------------------------------------------------------------------------- TSPX_class_of_device 04041C TSPX_security_control_data TSPX_content_protection_data TSPX_bd_addr_iut 112233445566 (*&) TSPX_delete_link_key FALSE TSPX_pin_code 1234 TSPX_security_enabled FALSE TSPX_time_guard 300000 TSPX_use_implicit_send TRUE TSPX_auth_password 0000 TSPX_auth_user_id PTS TSPX_l2cap_psm 000F TSPX_rfcomm_channel 8 TSPX_no_confirmations FALSE TSPX_UUID_dest_address 1116 TSPX_UUID_source_address 1115 TSPX_MAC_dest_address 000000000000 (*&) TSPX_MAC_source_address 000000000000 (*&) bluez-5.82/android/PaxHeaders/tester-avrcp.c0000644000000000000000000000005014015011623016042 xustar0020 atime=1743516864 20 ctime=1743591278 bluez-5.82/android/tester-avrcp.c0000644000000000000000000004746014015011623015536 0ustar00rootroot// SPDX-License-Identifier: Apache-2.0 /* * Copyright (C) 2014 Intel Corporation * */ #define _GNU_SOURCE #include #include "emulator/bthost.h" #include "src/shared/util.h" #include "src/shared/tester.h" #include "src/shared/queue.h" #include "lib/bluetooth.h" #include "android/utils.h" #include "tester-main.h" static struct queue *list; #define AVRCP_GET_ELEMENT_ATTRIBUTES 0x20 #define AVRCP_GET_PLAY_STATUS 0x30 #define AVRCP_REGISTER_NOTIFICATION 0x31 #define sdp_rsp_pdu 0x07, \ 0x00, 0x00, \ 0x00, 0x7f, \ 0x00, 0x7c, \ 0x36, 0x00, 0x79, 0x36, 0x00, 0x3b, 0x09, 0x00, 0x00, \ 0x0a, 0x00, 0x01, 0x00, 0x04, 0x09, 0x00, 0x01, 0x35, \ 0x06, 0x19, 0x11, 0x0e, 0x19, 0x11, 0x0f, 0x09, 0x00, \ 0x04, 0x35, 0x10, 0x35, 0x06, 0x19, 0x01, 0x00, 0x09, \ 0x00, 0x17, 0x35, 0x06, 0x19, 0x00, 0x17, 0x09, 0x01, \ 0x03, 0x09, 0x00, 0x09, 0x35, 0x08, 0x35, 0x06, 0x19, \ 0x11, 0x0e, 0x09, 0x01, 0x00, 0x09, 0x03, 0x11, 0x09, \ 0x00, 0x01, 0x36, 0x00, 0x38, 0x09, 0x00, 0x00, 0x0a, \ 0x00, 0x01, 0x00, 0x05, 0x09, 0x00, 0x01, 0x35, 0x03, \ 0x19, 0x11, 0x0c, 0x09, 0x00, 0x04, 0x35, 0x10, 0x35, \ 0x06, 0x19, 0x01, 0x00, 0x09, 0x00, 0x17, 0x35, 0x06, \ 0x19, 0x00, 0x17, 0x09, 0x01, 0x03, 0x09, 0x00, 0x09, \ 0x35, 0x08, 0x35, 0x06, 0x19, 0x11, 0x0e, 0x09, 0x01, \ 0x04, 0x09, 0x03, 0x11, 0x09, 0x00, 0x02, \ 0x00 static const struct pdu_set sdp_pdus[] = { { end_pdu, raw_pdu(sdp_rsp_pdu) }, { end_pdu, end_pdu }, }; static struct emu_l2cap_cid_data sdp_data = { .pdu = sdp_pdus, .is_sdp = TRUE, }; #define req_dsc 0x00, 0x01 #define rsp_dsc 0x02, 0x01, 0x04, 0x08 #define req_get 0x10, 0x02, 0x04 #define rsp_get 0x12, 0x02, 0x01, 0x00, 0x07, 0x06, 0x00, \ 0x00, 0xff, 0xff, 0x02, 0x40 #define req_cfg 0x20, 0x03, 0x04, 0x04, 0x01, 0x00, 0x07, \ 0x06, 0x00, 0x00, 0x21, 0x15, 0x02, \ 0x40 #define rsp_cfg 0x22, 0x03 #define req_open 0x30, 0x06, 0x04 #define rsp_open 0x32, 0x06 #define req_close 0x40, 0x08, 0x04 #define rsp_close 0x42, 0x08 #define req_start 0x40, 0x07, 0x04 #define rsp_start 0x42, 0x07 #define req_suspend 0x50, 0x09, 0x04 #define rsp_suspend 0x52, 0x09 #define req_play_status 0x00, 0x11, 0x0e, 0x01, 0x48, 0x00, 0x00, 0x19, 0x58, \ 0x30, 0x00, 0x00, 0x00 #define rsp_play_status 0x02, 0x11, 0x0e, 0x0c, 0x48, 0x00, 0x00, 0x19, 0x58, \ 0x30, 0x00, 0x00, 0x09, 0xbb, 0xbb, 0xbb, 0xbb, 0xaa, \ 0xaa, 0xaa, 0xaa, 0x00 #define req_track_notif 0x00, 0x11, 0x0e, 0x03, 0x48, 0x00, 0x00, 0x19, 0x58, \ 0x31, 0x00, 0x00, 0x05, 0x02, 0x00, 0x00, 0x00, 0x00 #define rsp_track_notif 0x00, 0x11, 0x0e, 0x0F, 0x48, 0x00, 0x00, 0x19, 0x58, \ 0x31, 0x00, 0x00, 0x09, 0x02, 0xFF, 0xFF, 0xFF, 0xFF, \ 0xFF, 0xFF, 0xFF, 0xFF #define req_position_notif 0x00, 0x11, 0x0e, 0x03, 0x48, 0x00, 0x00, 0x19, \ 0x58, 0x31, 0x00, 0x00, 0x05, 0x05, 0x00, \ 0x00, 0x00, 0x00 #define rsp_position_notif 0x00, 0x11, 0x0e, 0x0F, 0x48, 0x00, 0x00, 0x19, \ 0x58, 0x31, 0x00, 0x00, 0x04, 0x05, 0xFF, \ 0xFF, 0xFF, 0xFF #define req_status_notif 0x00, 0x11, 0x0e, 0x03, 0x48, 0x00, 0x00, 0x19, \ 0x58, 0x31, 0x00, 0x00, 0x05, 0x01, 0x00, \ 0x00, 0x00, 0x00 #define rsp_status_notif 0x00, 0x11, 0x0e, 0x0D, 0x48, 0x00, 0x00, 0x19, \ 0x58, 0x31, 0x00, 0x00, 0x01, 0x01, 0x00 #define req_ele_attr 0x00, 0x11, 0x0e, 0x01, 0x48, 0x00, 0x00, 0x19, 0x58, \ 0x20, 0x00, 0x00, 0x0D, 0x00, 0x00, 0x00, 0x00, \ 0x02, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x07 #define rsp_ele_attr 0x02, 0x11, 0x0e, 0x0c, 0x48, 0x00, 0x00, 0x19, 0x58, \ 0x20, 0x00, 0x00, 0x2a, 0x02, 0x00, 0x00, 0x00, 0x01, \ 0x00, 0x6a, 0x00, 0x13, 0x47, 0x69, 0x76, 0x65, 0x20, \ 0x50, 0x65, 0x61, 0x63, 0x65, 0x20, 0x61, 0x20, 0x43, \ 0x68, 0x61, 0x6e, 0x63, 0x65, 0x00, 0x00, 0x00, 0x07, \ 0x00, 0x6a, 0x00, 0x06, 0x31, 0x30, 0x33, 0x30, 0x30, \ 0x30 static const struct pdu_set pdus[] = { { raw_pdu(req_dsc), raw_pdu(rsp_dsc) }, { raw_pdu(req_get), raw_pdu(rsp_get) }, { raw_pdu(req_cfg), raw_pdu(rsp_cfg) }, { raw_pdu(req_open), raw_pdu(rsp_open) }, { raw_pdu(req_close), raw_pdu(rsp_close) }, { raw_pdu(req_start), raw_pdu(rsp_start) }, { raw_pdu(req_suspend), raw_pdu(rsp_suspend) }, { end_pdu, end_pdu }, }; static struct emu_l2cap_cid_data a2dp_data = { .pdu = pdus, }; static struct emu_l2cap_cid_data avrcp_data; static btrc_element_attr_val_t ele_attrs[2] = { { .attr_id = BTRC_MEDIA_ATTR_TITLE, .text = {0x47, 0x69, 0x76, 0x65, 0x20, 0x50, 0x65, 0x61, 0x63, 0x65, 0x20, 0x61, 0x20, 0x43, 0x68, 0x61, 0x6e, 0x63, 0x65} }, { .attr_id = BTRC_MEDIA_ATTR_PLAYING_TIME, .text = {0x31, 0x30, 0x33, 0x30, 0x30, 0x30} } }; static btrc_element_attr_val_t exp_attrs[2]; static void print_avrcp(const char *str, void *user_data) { tester_debug("avrcp: %s", str); } static void avrcp_cid_hook_cb(const void *data, uint16_t len, void *user_data) { struct step *step; uint8_t pdu, event; util_hexdump('>', data, len, print_avrcp, NULL); pdu = ((uint8_t *) data)[9]; switch (pdu) { case AVRCP_GET_PLAY_STATUS: step = g_new0(struct step, 1); step->callback = CB_AVRCP_PLAY_STATUS_RSP; step->callback_result.song_length = get_be32(data + 13); step->callback_result.song_position = get_be32(data + 17); step->callback_result.play_status = ((uint8_t *) data)[21]; schedule_callback_verification(step); break; case AVRCP_REGISTER_NOTIFICATION: event = ((uint8_t *) data)[13]; switch (event) { case 0x01: step = g_new0(struct step, 1); step->callback = CB_AVRCP_REG_NOTIF_RSP; step->callback_result.play_status = ((uint8_t *) data)[14]; schedule_callback_verification(step); break; case 0x02: step = g_new0(struct step, 1); step->callback = CB_AVRCP_REG_NOTIF_RSP; step->callback_result.rc_index = get_be64(data + 14); schedule_callback_verification(step); break; case 0x05: step = g_new0(struct step, 1); step->callback = CB_AVRCP_REG_NOTIF_RSP; step->callback_result.song_position = get_be32(data + 14); schedule_callback_verification(step); break; } break; case AVRCP_GET_ELEMENT_ATTRIBUTES: step = g_new0(struct step, 1); step->callback = CB_AVRCP_GET_ATTR_RSP; step->callback_result.num_of_attrs = ((uint8_t *) data)[13]; memset(exp_attrs, 0, 2 * sizeof(btrc_element_attr_val_t)); exp_attrs[0].attr_id = get_be16(data + 16); memcpy(exp_attrs[0].text, data + 22, 19); exp_attrs[1].attr_id = get_be16(data + 43); memcpy(exp_attrs[1].text, data + 49, 6); step->callback_result.attrs = exp_attrs; schedule_callback_verification(step); break; } } static void avrcp_connect_request_cb(uint16_t handle, uint16_t cid, void *user_data) { struct test_data *data = tester_get_data(); struct bthost *bthost = hciemu_client_get_host(data->hciemu); struct emu_l2cap_cid_data *cid_data = user_data; cid_data->handle = handle; cid_data->cid = cid; bthost_add_cid_hook(bthost, handle, cid, avrcp_cid_hook_cb, cid_data); } static struct emu_set_l2cap_data avrcp_setup_data = { .psm = 23, .func = avrcp_connect_request_cb, .user_data = &avrcp_data, }; static void a2dp_connect_request_cb(uint16_t handle, uint16_t cid, void *user_data) { struct emu_l2cap_cid_data *cid_data = user_data; if (cid_data->handle) return; cid_data->handle = handle; cid_data->cid = cid; avrcp_data.handle = handle; avrcp_data.cid = cid; tester_handle_l2cap_data_exchange(cid_data); } static struct emu_set_l2cap_data a2dp_setup_data = { .psm = 25, .func = a2dp_connect_request_cb, .user_data = &a2dp_data, }; static struct emu_set_l2cap_data sdp_setup_data = { .psm = 1, .func = tester_generic_connect_cb, .user_data = &sdp_data, }; static void avrcp_connect_action(void) { struct test_data *data = tester_get_data(); const uint8_t *addr = hciemu_get_client_bdaddr(data->hciemu); struct step *step = g_new0(struct step, 1); bt_bdaddr_t bdaddr; sdp_data.handle = 0; sdp_data.cid = 0; a2dp_data.handle = 0; a2dp_data.cid = 0; avrcp_data.handle = 0; avrcp_data.cid = 0; bdaddr2android((const bdaddr_t *) addr, &bdaddr); step->action_status = data->if_a2dp->connect(&bdaddr); schedule_action_verification(step); } static void avrcp_disconnect_action(void) { struct test_data *data = tester_get_data(); const uint8_t *addr = hciemu_get_client_bdaddr(data->hciemu); struct step *step = g_new0(struct step, 1); bt_bdaddr_t bdaddr; bdaddr2android((const bdaddr_t *) addr, &bdaddr); step->action_status = data->if_a2dp->disconnect(&bdaddr); schedule_action_verification(step); } static void avrcp_get_play_status_req(void) { struct test_data *data = tester_get_data(); struct bthost *bthost = hciemu_client_get_host(data->hciemu); const struct iovec pdu = raw_pdu(req_play_status); struct step *step = g_new0(struct step, 1); bthost_send_cid_v(bthost, avrcp_data.handle, avrcp_data.cid, &pdu, 1); step->action_status = BT_STATUS_SUCCESS; schedule_action_verification(step); } static void avrcp_get_play_status_rsp(void) { struct test_data *data = tester_get_data(); struct step *step = g_new0(struct step, 1); step->action_status = data->if_avrcp->get_play_status_rsp(0x00, 0xbbbbbbbb, 0xaaaaaaaa); schedule_action_verification(step); } static void avrcp_reg_notif_track_changed_req(void) { struct test_data *data = tester_get_data(); struct bthost *bthost = hciemu_client_get_host(data->hciemu); const struct iovec pdu = raw_pdu(req_track_notif); struct step *step = g_new0(struct step, 1); bthost_send_cid_v(bthost, avrcp_data.handle, avrcp_data.cid, &pdu, 1); step->action_status = BT_STATUS_SUCCESS; schedule_action_verification(step); } static void avrcp_reg_notif_track_changed_rsp(void) { struct test_data *data = tester_get_data(); struct step *step = g_new0(struct step, 1); uint64_t track; btrc_register_notification_t reg; track = 0xffffffffffffffff; memcpy(reg.track, &track, sizeof(btrc_uid_t)); step->action_status = data->if_avrcp->register_notification_rsp( BTRC_EVT_TRACK_CHANGE, BTRC_NOTIFICATION_TYPE_INTERIM, ®); schedule_action_verification(step); } static void avrcp_reg_notif_play_position_changed_req(void) { struct test_data *data = tester_get_data(); struct bthost *bthost = hciemu_client_get_host(data->hciemu); const struct iovec pdu = raw_pdu(req_position_notif); struct step *step = g_new0(struct step, 1); bthost_send_cid_v(bthost, avrcp_data.handle, avrcp_data.cid, &pdu, 1); step->action_status = BT_STATUS_SUCCESS; schedule_action_verification(step); } static void avrcp_reg_notif_play_position_changed_rsp(void) { struct test_data *data = tester_get_data(); struct step *step = g_new0(struct step, 1); btrc_register_notification_t reg; reg.song_pos = 0xffffffff; step->action_status = data->if_avrcp->register_notification_rsp( BTRC_EVT_PLAY_POS_CHANGED, BTRC_NOTIFICATION_TYPE_INTERIM, ®); schedule_action_verification(step); } static void avrcp_reg_notif_play_status_changed_req(void) { struct test_data *data = tester_get_data(); struct bthost *bthost = hciemu_client_get_host(data->hciemu); const struct iovec pdu = raw_pdu(req_status_notif); struct step *step = g_new0(struct step, 1); bthost_send_cid_v(bthost, avrcp_data.handle, avrcp_data.cid, &pdu, 1); step->action_status = BT_STATUS_SUCCESS; schedule_action_verification(step); } static void avrcp_reg_notif_play_status_changed_rsp(void) { struct test_data *data = tester_get_data(); struct step *step = g_new0(struct step, 1); btrc_register_notification_t reg; reg.play_status = BTRC_PLAYSTATE_STOPPED; step->action_status = data->if_avrcp->register_notification_rsp( BTRC_EVT_PLAY_STATUS_CHANGED, BTRC_NOTIFICATION_TYPE_CHANGED, ®); schedule_action_verification(step); } static void avrcp_get_element_attributes_req(void) { struct test_data *data = tester_get_data(); struct bthost *bthost = hciemu_client_get_host(data->hciemu); const struct iovec pdu = raw_pdu(req_ele_attr); struct step *step = g_new0(struct step, 1); bthost_send_cid_v(bthost, avrcp_data.handle, avrcp_data.cid, &pdu, 1); step->action_status = BT_STATUS_SUCCESS; schedule_action_verification(step); } static void avrcp_get_element_attributes_rsp(void) { struct test_data *data = tester_get_data(); struct step *step = g_new0(struct step, 1); step->action_status = data->if_avrcp->get_element_attr_rsp(2, ele_attrs); schedule_action_verification(step); } static struct test_case test_cases[] = { TEST_CASE_BREDRLE("AVRCP Init", ACTION_SUCCESS(dummy_action, NULL), ), TEST_CASE_BREDRLE("AVRCP Connect - Success", ACTION_SUCCESS(bluetooth_enable_action, NULL), CALLBACK_STATE(CB_BT_ADAPTER_STATE_CHANGED, BT_STATE_ON), ACTION_SUCCESS(emu_setup_powered_remote_action, NULL), ACTION_SUCCESS(emu_set_ssp_mode_action, NULL), ACTION_SUCCESS(set_default_ssp_request_handler, NULL), ACTION_SUCCESS(emu_add_l2cap_server_action, &sdp_setup_data), ACTION_SUCCESS(emu_add_l2cap_server_action, &a2dp_setup_data), ACTION_SUCCESS(emu_add_l2cap_server_action, &avrcp_setup_data), ACTION_SUCCESS(avrcp_connect_action, NULL), CALLBACK_AV_CONN_STATE(CB_A2DP_CONN_STATE, BTAV_CONNECTION_STATE_CONNECTING), CALLBACK_AV_CONN_STATE(CB_A2DP_CONN_STATE, BTAV_CONNECTION_STATE_CONNECTED), ACTION_SUCCESS(bluetooth_disable_action, NULL), CALLBACK_STATE(CB_BT_ADAPTER_STATE_CHANGED, BT_STATE_OFF), ), TEST_CASE_BREDRLE("AVRCP Disconnect - Success", ACTION_SUCCESS(bluetooth_enable_action, NULL), CALLBACK_STATE(CB_BT_ADAPTER_STATE_CHANGED, BT_STATE_ON), ACTION_SUCCESS(emu_setup_powered_remote_action, NULL), ACTION_SUCCESS(emu_set_ssp_mode_action, NULL), ACTION_SUCCESS(set_default_ssp_request_handler, NULL), ACTION_SUCCESS(emu_add_l2cap_server_action, &sdp_setup_data), ACTION_SUCCESS(emu_add_l2cap_server_action, &a2dp_setup_data), ACTION_SUCCESS(emu_add_l2cap_server_action, &avrcp_setup_data), ACTION_SUCCESS(avrcp_connect_action, NULL), CALLBACK_AV_CONN_STATE(CB_A2DP_CONN_STATE, BTAV_CONNECTION_STATE_CONNECTING), CALLBACK_AV_CONN_STATE(CB_A2DP_CONN_STATE, BTAV_CONNECTION_STATE_CONNECTED), ACTION_SUCCESS(avrcp_disconnect_action, NULL), CALLBACK_AV_CONN_STATE(CB_A2DP_CONN_STATE, BTAV_CONNECTION_STATE_DISCONNECTING), CALLBACK_AV_CONN_STATE(CB_A2DP_CONN_STATE, BTAV_CONNECTION_STATE_DISCONNECTED), ACTION_SUCCESS(bluetooth_disable_action, NULL), CALLBACK_STATE(CB_BT_ADAPTER_STATE_CHANGED, BT_STATE_OFF), ), TEST_CASE_BREDRLE("AVRCP GetPlayStatus - Success", ACTION_SUCCESS(bluetooth_enable_action, NULL), CALLBACK_STATE(CB_BT_ADAPTER_STATE_CHANGED, BT_STATE_ON), ACTION_SUCCESS(emu_setup_powered_remote_action, NULL), ACTION_SUCCESS(emu_set_ssp_mode_action, NULL), ACTION_SUCCESS(set_default_ssp_request_handler, NULL), ACTION_SUCCESS(emu_add_l2cap_server_action, &sdp_setup_data), ACTION_SUCCESS(emu_add_l2cap_server_action, &a2dp_setup_data), ACTION_SUCCESS(emu_add_l2cap_server_action, &avrcp_setup_data), ACTION_SUCCESS(avrcp_connect_action, NULL), CALLBACK_AV_CONN_STATE(CB_A2DP_CONN_STATE, BTAV_CONNECTION_STATE_CONNECTING), CALLBACK_AV_CONN_STATE(CB_A2DP_CONN_STATE, BTAV_CONNECTION_STATE_CONNECTED), ACTION_SUCCESS(avrcp_get_play_status_req, NULL), CALLBACK(CB_AVRCP_PLAY_STATUS_REQ), ACTION_SUCCESS(avrcp_get_play_status_rsp, NULL), CALLBACK_RC_PLAY_STATUS(CB_AVRCP_PLAY_STATUS_RSP, 0xbbbbbbbb, 0xaaaaaaaa, 0x00), ACTION_SUCCESS(bluetooth_disable_action, NULL), CALLBACK_STATE(CB_BT_ADAPTER_STATE_CHANGED, BT_STATE_OFF), ), TEST_CASE_BREDRLE("AVRCP RegNotifTrackChanged - Success", ACTION_SUCCESS(bluetooth_enable_action, NULL), CALLBACK_STATE(CB_BT_ADAPTER_STATE_CHANGED, BT_STATE_ON), ACTION_SUCCESS(emu_setup_powered_remote_action, NULL), ACTION_SUCCESS(emu_set_ssp_mode_action, NULL), ACTION_SUCCESS(set_default_ssp_request_handler, NULL), ACTION_SUCCESS(emu_add_l2cap_server_action, &sdp_setup_data), ACTION_SUCCESS(emu_add_l2cap_server_action, &a2dp_setup_data), ACTION_SUCCESS(emu_add_l2cap_server_action, &avrcp_setup_data), ACTION_SUCCESS(avrcp_connect_action, NULL), CALLBACK_AV_CONN_STATE(CB_A2DP_CONN_STATE, BTAV_CONNECTION_STATE_CONNECTING), CALLBACK_AV_CONN_STATE(CB_A2DP_CONN_STATE, BTAV_CONNECTION_STATE_CONNECTED), ACTION_SUCCESS(avrcp_reg_notif_track_changed_req, NULL), CALLBACK(CB_AVRCP_REG_NOTIF_REQ), ACTION_SUCCESS(avrcp_reg_notif_track_changed_rsp, NULL), CALLBACK_RC_REG_NOTIF_TRACK_CHANGED(CB_AVRCP_REG_NOTIF_RSP, 0xffffffffffffffff), ACTION_SUCCESS(bluetooth_disable_action, NULL), CALLBACK_STATE(CB_BT_ADAPTER_STATE_CHANGED, BT_STATE_OFF), ), TEST_CASE_BREDRLE("AVRCP RegNotifPlayPositionChanged - Success", ACTION_SUCCESS(bluetooth_enable_action, NULL), CALLBACK_STATE(CB_BT_ADAPTER_STATE_CHANGED, BT_STATE_ON), ACTION_SUCCESS(emu_setup_powered_remote_action, NULL), ACTION_SUCCESS(emu_set_ssp_mode_action, NULL), ACTION_SUCCESS(set_default_ssp_request_handler, NULL), ACTION_SUCCESS(emu_add_l2cap_server_action, &sdp_setup_data), ACTION_SUCCESS(emu_add_l2cap_server_action, &a2dp_setup_data), ACTION_SUCCESS(emu_add_l2cap_server_action, &avrcp_setup_data), ACTION_SUCCESS(avrcp_connect_action, NULL), CALLBACK_AV_CONN_STATE(CB_A2DP_CONN_STATE, BTAV_CONNECTION_STATE_CONNECTING), CALLBACK_AV_CONN_STATE(CB_A2DP_CONN_STATE, BTAV_CONNECTION_STATE_CONNECTED), ACTION_SUCCESS(avrcp_reg_notif_play_position_changed_req, NULL), CALLBACK(CB_AVRCP_REG_NOTIF_REQ), ACTION_SUCCESS(avrcp_reg_notif_play_position_changed_rsp, NULL), CALLBACK_RC_REG_NOTIF_POSITION_CHANGED(CB_AVRCP_REG_NOTIF_RSP, 0xffffffff), ACTION_SUCCESS(bluetooth_disable_action, NULL), CALLBACK_STATE(CB_BT_ADAPTER_STATE_CHANGED, BT_STATE_OFF), ), TEST_CASE_BREDRLE("AVRCP RegNotifPlayStatusChanged - Success", ACTION_SUCCESS(bluetooth_enable_action, NULL), CALLBACK_STATE(CB_BT_ADAPTER_STATE_CHANGED, BT_STATE_ON), ACTION_SUCCESS(emu_setup_powered_remote_action, NULL), ACTION_SUCCESS(emu_set_ssp_mode_action, NULL), ACTION_SUCCESS(set_default_ssp_request_handler, NULL), ACTION_SUCCESS(emu_add_l2cap_server_action, &sdp_setup_data), ACTION_SUCCESS(emu_add_l2cap_server_action, &a2dp_setup_data), ACTION_SUCCESS(emu_add_l2cap_server_action, &avrcp_setup_data), ACTION_SUCCESS(avrcp_connect_action, NULL), CALLBACK_AV_CONN_STATE(CB_A2DP_CONN_STATE, BTAV_CONNECTION_STATE_CONNECTING), CALLBACK_AV_CONN_STATE(CB_A2DP_CONN_STATE, BTAV_CONNECTION_STATE_CONNECTED), ACTION_SUCCESS(avrcp_reg_notif_play_status_changed_req, NULL), CALLBACK(CB_AVRCP_REG_NOTIF_REQ), ACTION_SUCCESS(avrcp_reg_notif_play_status_changed_rsp, NULL), CALLBACK_RC_REG_NOTIF_STATUS_CHANGED(CB_AVRCP_REG_NOTIF_RSP, 0x00), ACTION_SUCCESS(bluetooth_disable_action, NULL), CALLBACK_STATE(CB_BT_ADAPTER_STATE_CHANGED, BT_STATE_OFF), ), TEST_CASE_BREDRLE("AVRCP GetElementAttributes - Success", ACTION_SUCCESS(bluetooth_enable_action, NULL), CALLBACK_STATE(CB_BT_ADAPTER_STATE_CHANGED, BT_STATE_ON), ACTION_SUCCESS(emu_setup_powered_remote_action, NULL), ACTION_SUCCESS(emu_set_ssp_mode_action, NULL), ACTION_SUCCESS(set_default_ssp_request_handler, NULL), ACTION_SUCCESS(emu_add_l2cap_server_action, &sdp_setup_data), ACTION_SUCCESS(emu_add_l2cap_server_action, &a2dp_setup_data), ACTION_SUCCESS(emu_add_l2cap_server_action, &avrcp_setup_data), ACTION_SUCCESS(avrcp_connect_action, NULL), CALLBACK_AV_CONN_STATE(CB_A2DP_CONN_STATE, BTAV_CONNECTION_STATE_CONNECTING), CALLBACK_AV_CONN_STATE(CB_A2DP_CONN_STATE, BTAV_CONNECTION_STATE_CONNECTED), ACTION_SUCCESS(avrcp_get_element_attributes_req, NULL), CALLBACK(CB_AVRCP_GET_ATTR_REQ), ACTION_SUCCESS(avrcp_get_element_attributes_rsp, NULL), CALLBACK_RC_GET_ELEMENT_ATTRIBUTES(CB_AVRCP_GET_ATTR_RSP, 2, ele_attrs), ACTION_SUCCESS(bluetooth_disable_action, NULL), CALLBACK_STATE(CB_BT_ADAPTER_STATE_CHANGED, BT_STATE_OFF), ), }; struct queue *get_avrcp_tests(void) { uint16_t i = 0; list = queue_new(); for (; i < sizeof(test_cases) / sizeof(test_cases[0]); ++i) queue_push_tail(list, &test_cases[i]); return list; } void remove_avrcp_tests(void) { queue_destroy(list, NULL); } bluez-5.82/android/PaxHeaders/hal-ipc.h0000644000000000000000000000005014015011623014745 xustar0020 atime=1743516862 20 ctime=1743591276 bluez-5.82/android/hal-ipc.h0000644000000000000000000000110414015011623014422 0ustar00rootroot/* SPDX-License-Identifier: Apache-2.0 */ /* * Copyright (C) 2013 Intel Corporation * */ struct hal_ipc_handler { void (*handler) (void *buf, uint16_t len, int fd); bool var_len; size_t data_len; }; bool hal_ipc_init(const char *path, size_t size); bool hal_ipc_accept(void); void hal_ipc_cleanup(void); int hal_ipc_cmd(uint8_t service_id, uint8_t opcode, uint16_t len, void *param, size_t *rsp_len, void *rsp, int *fd); void hal_ipc_register(uint8_t service, const struct hal_ipc_handler *handlers, uint8_t size); void hal_ipc_unregister(uint8_t service); bluez-5.82/android/PaxHeaders/pics-pan.txt0000644000000000000000000000005012537515745015557 xustar0020 atime=1743516876 20 ctime=1743591288 bluez-5.82/android/pics-pan.txt0000644000000000000000000001723012537515745015243 0ustar00rootrootPAN PICS for the PTS tool. PTS version: 6.1 * - different than PTS defaults # - not yet implemented/supported M - mandatory O - optional Roles ------------------------------------------------------------------------------- Parameter Name Selected Description ------------------------------------------------------------------------------- TSPC_PAN_1_1 True (*) Role: Network Access Point (O.1) TSPC_PAN_1_2 False Role: Group Ad-hoc Network (O.1) TSPC_PAN_1_3 True (*) Role: PAN User (O.1) TSPC_PAN_1a_1 True BNEP: BNEP Connection Setup (M) TSPC_PAN_1a_2 True BNEP: BNEP Data Packet Reception (M) TSPC_PAN_1a_3 True BNEP: BNEP Data Packet Transmission (M) TSPC_PAN_1a_3a True BNEP: BNEP Compressed Packet Transmission (O) TSPC_PAN_1a_3b True BNEP: BNEP Compressed Packet Transmission Source Only (O) TSPC_PAN_1a_4 True BNEP: BNEP Control Message Processing (M) TSPC_PAN_1a_5 True BNEP: BNEP Extension Header Processing (M) TSPC_PAN_1a_6 False BNEP: Network Protocol Filter Message Transmission (O) TSPC_PAN_1a_7 False BNEP: Multicast Address Filter Message Transmission (O) ------------------------------------------------------------------------------- O.1: It is mandatory to support at least one of the defined roles. ------------------------------------------------------------------------------- Network Access Point Application Features ------------------------------------------------------------------------------- Parameter Name Selected Description ------------------------------------------------------------------------------- TSPC_PAN_2_1 True NAP: Support BNEP (M) TSPC_PAN_2_2 True NAP: Support BNEP Forwarding (M) TSPC_PAN_2_3 False NAP: Support Layer 2-Bridging between PAN and External Network (C.1) TSPC_PAN_2_4 True (*) NAP: Support IP forwarding between PAN and External Network (C.1) TSPC_PAN_2_5 False NAP: Support BNEP Packet Filtering (O) TSPC_PAN_2_6 False NAP: Support IPv4 (C.2) TSPC_PAN_2_6a False NAP: Supports operable routable IPv4 address (O) TSPC_PAN_2_6b False NAP: Support link-local address configuration for IPv4 (C.4) TSPC_PAN_2_7 False NAP: Support ping client for IPv4 (O) TSPC_PAN_2_8 False NAP: Support DHCP Client for IPv4 (O) TSPC_PAN_2_9 False NAP: Support DNS/LLMNR Resolver for IPv4 (O) TSPC_PAN_2_9a False (*) NAP: Support LLMNR Sender for IPv4 (C.5) TSPC_PAN_2_9b False NAP: Support LLMNR Responder for IPv4 (O) TSPC_PAN_2_10 False NAP: Support HTTP Client for IPv4 (O) TSPC_PAN_2_11 False NAP: Support WAP Client for IPv4 (O) TSPC_PAN_2_12 False NAP: Support IPv6 (C.3) TSPC_PAN_2_13 False NAP: Support ping client for IPv6 (O) TSPC_PAN_2_14 False NAP: Support DNS/LLMNR Resolver for IPv6 (O) TSPC_PAN_2_14a False (*) NAP: Support LLMNR Sender for IPv6 (C.6) TSPC_PAN_2_14b False NAP: Support LLMNR Responder for IPv6 (O) TSPC_PAN_2_15 False NAP: Support HTTP Client for IPv6 (O) TSPC_PAN_2_16 False NAP: Support WAP Client for IPv6 (O) TSPC_PAN_2_17 True NAP: Supports Connectable Mode (M) TSPC_PAN_2_18 True NAP: NAP Service Record (M) TSPC_PAN_2_19 False NAP: Support at least three PANUs (O) TSPC_PAN_2_20 False NAP: Support at least two PANUs (O) ------------------------------------------------------------------------------- Note that support for IP-related features only applies to the PAN interface of the NAP (i.e. If the IP stack is accessible by PANUs). C.1: Network Access Point devices MUST support either (TSPC_PAN_2_3) OR (TSPC_PAN_2_4). C.2: Mandatory to support IF any IPv4-based transport protocol OR (TSPC_PAN_2_7-11) is supported, ELSE Optional. C.3: Mandatory to support IF any IPv6-based transport protocol OR (TSPC_PAN_2_13-16) is supported, ELSE Optional. C.4: Mandatory if TSPC_PAN_2_6 is supported and TSPC_PAN_2_6a is not supported, otherwise optional. C.5: Mandatory if item (TSPC_PAN_2_6) supported. C.6: Mandatory if item (TSPC_PAN_2_12) supported ------------------------------------------------------------------------------- Group Ad-hoc Network Application Features (GN Application Features) ------------------------------------------------------------------------------- Parameter Name Selected Description ------------------------------------------------------------------------------- TSPC_PAN_3_1 False (*) GN: Support BNEP (M) TSPC_PAN_3_2 False (*) GN: Support BNEP Forwarding (M) TSPC_PAN_3_3 False GN: Support BNEP Packet Filtering (O) TSPC_PAN_3_4 False GN: Support IPv4 (C.1) TSPC_PAN_3_5 False GN: Support ping client for IPv4 (O) TSPC_PAN_3_6 False GN: Support DHCP Client for IPv4 (O) TSPC_PAN_3_7 False GN: Support DNS/LLMNR Resolver for IPv4 (O) TSPC_PAN_3_7a False (*) GN: Support LLMNR Sender for IPv4 (C.3) TSPC_PAN_3_7b False GN: Support LLMNR Responder for IPv4 (O) TSPC_PAN_3_8 False GN: Support HTTP Client for IPv4 (O) TSPC_PAN_3_9 False GN: Support WAP Client for IPv4 (O) TSPC_PAN_3_10 False GN: Support IPv6 (C.2) TSPC_PAN_3_11 False GN: Support ping client for IPv6 (O) TSPC_PAN_3_12 False GN: Support DNS/LLMNR Resolver for IPv6 (O) TSPC_PAN_3_12a False (*) GN: Support LLMNR Sender for IPv6 (C.4) TSPC_PAN_3_12b False GN: Support LLMNR Responder for IPv6 (O) TSPC_PAN_3_13 False GN: Support HTTP Client for IPv6 (O) TSPC_PAN_3_14 False GN: Support WAP Client for IPv6 (O) TSPC_PAN_3_15 False (*) GN: Supports Connectable Mode (M) TSPC_PAN_3_16 False (*) GN: GN Service Record (M) TSPC_PAN_3_17 False GN: Support at least three PANUs (O) TSPC_PAN_3_18 False GN: Support at least two PANUs (O) ------------------------------------------------------------------------------- C.1: Mandatory to support IF any IPv4-based transport protocol OR (TSPC_PAN_3_5-9) is supported, ELSE Optional. C.2: Mandatory to support IF any IPv6-based transport protocol OR (TSPC_PAN_3_11-14) is supported, ELSE Optional. C.3: Mandatory to support IF (TSPC_PAN_3_4) is supported. C.4: Mandatory to support if (TSPC_PAN_3_10) is supported. ------------------------------------------------------------------------------- PAN User Application Features ------------------------------------------------------------------------------- Parameter Name Selected Description ------------------------------------------------------------------------------- TSPC_PAN_4_1 True PANU: Support BNEP (M) TSPC_PAN_4_2 True (*) PANU: Support IPv4 (C.1) TSPC_PAN_4_3 False PANU: Support ping client for IPv4 (O) TSPC_PAN_4_4 False PANU: Support DHCP client for IPv4 (O) TSPC_PAN_4_5 False PANU: Support DNS/LLMNR Resolver for IPv4 (O) TSPC_PAN_4_5a False (*) PANU: Support LLMNR Sender for IPv4 (C.2) Reference: SE #3558, TSE #4382, TCW #448 TSPC_PAN_4_5b False PANU: Support LLMNR Responder for IPv4 (O) TSPC_PAN_4_6 False PANU: Support HTTP Client for IPv4 (O) TSPC_PAN_4_7 False PANU: Support WAP Client for IPv4 (O) TSPC_PAN_4_8 False PANU: Support IPv6 (C.1) TSPC_PAN_4_9 False PANU: Support ping client for IPv6 (O) TSPC_PAN_4_10 False PANU: Support DNS/LLMNR Resolver for IPv6 (O) TSPC_PAN_4_10a False (*) PANU: Support LLMNR Sender for IPv6 (C.3) TSPC_PAN_4_10b False PANU: Support LLMNR Responder for IPv6 (O) TSPC_PAN_4_11 False PANU: Support HTTP Client for IPv6 (O) TSPC_PAN_4_12 False PANU: Support WAP Client for IPv6 (O) TSPC_PAN_4_13 False PANU: Support connections to multi-user NAPs/GNs (O) TSPC_PAN_4_14 False PANU: Supports Connectable Mode (O) TSPC_PAN_4_15 False PANU: PANU Service Record (O) TSPC_ALL False Turns on all the test cases ------------------------------------------------------------------------------- C.1: PAN User devices must support at least One of items (TSPC_PAN_4_2) or (TSPC_PAN_4_8). C.2: Mandatory to support if (TSPC_PAN_4_2) is supported. C.3: Mandatory to support if (TSPC_PAN_4_8) is supported. ------------------------------------------------------------------------------- bluez-5.82/android/PaxHeaders/pics-map.txt0000644000000000000000000000005012537515745015556 xustar0020 atime=1743516876 20 ctime=1743591288 bluez-5.82/android/pics-map.txt0000644000000000000000000001777312537515745015256 0ustar00rootrootMAP PICS for the PTS tool. PTS version: 6.1 * - different than PTS defaults # - not yet implemented/supported M - mandatory O - optional Profile Version ------------------------------------------------------------------------------- Parameter Name Selected Description ------------------------------------------------------------------------------- TSPC_MAP_0_1 False Role: Map 1.0 (C1) TSPC_MAP_0_2 True (*) Role: Map 1.1 (C1) TSPC_MAP_0_3 False Role: Map 1.2 (C1) ------------------------------------------------------------------------------- C.1: Mandatory to support only one Profile version. ------------------------------------------------------------------------------- Roles ------------------------------------------------------------------------------- Parameter Name Selected Description ------------------------------------------------------------------------------- TSPC_MAP_1_1 True (*) Role: Messaging Server Equipment (C1) TSPC_MAP_1_2 False Role: Messaging Client Equipment (C1) ------------------------------------------------------------------------------- C.1: It is mandatory to support at least one of the defined roles. ------------------------------------------------------------------------------- Supported features MCE ------------------------------------------------------------------------------- Parameter Name Selected Description ------------------------------------------------------------------------------- TSPC_MAP_2_1 False MCE: Message Notification (C1) TSPC_MAP_2_1a False MCE: SendEvent (C4) TSPC_MAP_2_2 False MCE: Message Browsing (C1) TSPC_MAP_2_2a False MCE: SetFolder (C5) TSPC_MAP_2_2b False MCE: GetFoldersListing (C5) TSPC_MAP_2_2c False MCE: GetMessagesListing (C5) TSPC_MAP_2_2d False MCE: GetMessage (O) TSPC_MAP_2_2e False MCE: SetMessageStatus (O) TSPC_MAP_2_2f False MCE: UpdateInbox (O) TSPC_MAP_2_2g False MCE: Filtering (O) TSPC_MAP_2_2h False MCE: Multiple simultaneous MAS instances (O) TSPC_MAP_2_3 False MCE: Message Uploading (O) TSPC_MAP_2_3a False MCE: SetFolder (C6) TSPC_MAP_2_3b False MCE: GetFoldersListing (C6) TSPC_MAP_2_3c False MCE: PushMessage (C6) TSPC_MAP_2_4 False MCE: Message Delete (O) TSPC_MAP_2_4a False MCE: SetMessageStatus (C7) TSPC_MAP_2_5 False MCE: Notification Registration (C2) TSPC_MAP_2_5a False MCE: SetNotificationRegistration off (O) TSPC_MAP_2_5b False MCE: SetNotificationRegistration on (C8) TSPC_MAP_2_6 False MCE: Supported Message Types TSPC_MAP_2_6a False (*) MCE: EMAIL (C3) TSPC_MAP_2_6b False (*) MCE: SMS_GSM (C3) TSPC_MAP_2_6c False (*) MCE: SMS_CDMA (C3) TSPC_MAP_2_6d False (*) MCE: MMS (C3) TSPC_MAP_2_7 False MCE: Instance Information (Not Supported) TSPC_MAP_2_7a False (*) MCE: GetMASInstanceInformation (Not Supported) TSPC_MAP_2_8 False MCE: Extended MAP-Event-Report (Not Supported) TSPC_MAP_2_8a False (*) MCE: MAP-Event-Report: Version 1.1 (Not Supported) ------------------------------------------------------------------------------- C.1: Mandatory to support at least one of the defined features TSPC_MAP_2_1 or TSPC_MAP_2_2. C.2: Mandatory to support TSPC_MAP_2_5 if TSPC_MAP_2_1 is supported. C.3: Mandatory to support at least one of the defined message types TSPC_MAP_2_6a to TSPC_MAP_2_6d IF TSPC_MAP_2_2 or TSPC_MAP_2_3 is supported. C.4: Support of functionality TSPC_MAP_2_1a mandatory IF related feature TSPC_MAP_2_1 supported. C.5: Support of functionality mandatory IF TSPC_MAP_2_2 supported. C.6: Support of functionality mandatory IF TSPC_MAP_2_3 supported. C.7: Support of functionality mandatory IF TSPC_MAP_2_4 supported. C.8: Mandatory to support IF TSPC_MAP_2_5 (Notification Registration) is supported, otherwise excluded. C.9: Optional to support IF TSPC_MAP_0_3 (MAP v1.2) is supported, otherwise excluded. C.10: Mandatory to support IF TSPC_MAP_0_3 (MAP v1.2) and TSPC_MAP_2_1 (Message Notification) is supported, otherwise excluded. ------------------------------------------------------------------------------- Supported features MSE ------------------------------------------------------------------------------- Parameter Name Selected Description ------------------------------------------------------------------------------- TSPC_MAP_3_1 True MSE: Message Notification (M) TSPC_MAP_3_1a True MSE: SendEvent (M) TSPC_MAP_3_2 True MSE: Message Browsing (M) TSPC_MAP_3_2a True MSE: SetFolder (M) TSPC_MAP_3_2b True MSE: GetFoldersListing (M) TSPC_MAP_3_2c True MSE: GetMessagesListing (M) TSPC_MAP_3_2d True MSE: GetMessage (M) TSPC_MAP_3_2e True MSE: SetMessageStatus (M) TSPC_MAP_3_2f True MSE: UpdateInbox (M) TSPC_MAP_3_2g False MSE: Multiple simultaneous MAS instances (O) TSPC_MAP_3_3 True MSE: Message Uploading (M) TSPC_MAP_3_3a True MSE: SetFolder (M) TSPC_MAP_3_3b True MSE: GetFoldersListing (M) TSPC_MAP_3_3c True MSE: PushMessage (M) TSPC_MAP_3_4 True MSE: Message Delete (M) TSPC_MAP_3_4a True MSE: SetMessageStatus (M) TSPC_MAP_3_5 True MSE: Notification Registration (M) TSPC_MAP_3_5a True MSE: SetNotificationRegistration (M) TSPC_MAP_3_6 False MSE: Supported Message Types TSPC_MAP_3_6a False MSE: EMAIL (C1) TSPC_MAP_3_6b True MSE: SMS_GSM (C1) TSPC_MAP_3_6c True (*) MSE: SMS_CDMA (C1) TSPC_MAP_3_6d True MSE: MMS (C1) TSPC_MAP_3_7 False MSE: Instance Information (Not Supported) TSPC_MAP_3_7a False (*) MSE: GetMASInstanceInformation (Not Supported) TSPC_MAP_3_8 False MSE: Extended MAP-Event-Report (Not Supported) TSPC_MAP_3_8a False (*) MSE: MAP-Event-Report: Version 1.1 (Not Supported) ------------------------------------------------------------------------------- C.1: Mandatory to support at least one of the defined message types TSPC_MAP_3_6a to TSPC_MAP_3_6d IF TSPC_MAP_3_2 or TSPC_MAP_3_3 is supported. C.2: Mandatory to support IF TSPC_MAP_0_3 (MAP v1.2) is supported, otherwise excluded. ------------------------------------------------------------------------------- GOEP v2.0 or later Features ------------------------------------------------------------------------------- Parameter Name Selected Description ------------------------------------------------------------------------------- TSPC_MAP_7b_1 False GOEP v2.0 or later (C1) TSPC_MAP_7b_2 False GOEP v2 Backwards Compatibility (C1) TSPC_MAP_7b_3 False OBEX over L2CAP (C1) ------------------------------------------------------------------------------- C.1: Mandatory if TSPC_MAP_0_3 (MAP v1.2) is supported else excluded. ------------------------------------------------------------------------------- MCE OBEX Header Support ------------------------------------------------------------------------------- Parameter Name Selected Description ------------------------------------------------------------------------------- TSPC_MAP_10_1 False (*) Name (M) TSPC_MAP_10_2 False (*) Typr (M) TSPC_MAP_10_3 False (*) Body (M) TSPC_MAP_10_4 False (*) End of Body (M) TSPC_MAP_10_5 False (*) Target (M) TSPC_MAP_10_6 False (*) Who (M) TSPC_MAP_10_7 False (*) Connection ID (M) TSPC_MAP_10_8 False (*) Application Parameters (M) TSPC_MAP_10_9 False SRM (C2) TSPC_MAP_10_10 False Receive SRMP (C2) TSPC_MAP_10_11 False Send SRMP (C2) ------------------------------------------------------------------------------- C.1: Mandatory if TSPC_MAP_0_3 (MAP v1.2) is supported else excluded. C.2: Optional if TSPC_MAP_0_3 (MAP v1.2) is supported else excluded. ------------------------------------------------------------------------------- GetMessagesListing Filtering Parameter Support ------------------------------------------------------------------------------- Parameter Name Selected Description ------------------------------------------------------------------------------- TSPC_MAP_20_1 False (*) MCE: FilterMessageType (O) TSPC_MAP_20_2 False (*) MCE: FilterPeriodBegin (O) TSPC_MAP_20_3 False (*) MCE: FilterPeriodEnd (O) TSPC_MAP_20_4 False (*) MCE: FilterReadStatus (O) TSPC_MAP_20_5 False (*) MCE: FilterRecipient (O) TSPC_MAP_20_6 False (*) MCE: FilterOriginator (O) TSPC_MAP_20_7 False (*) MCE: FilterPriority (O) TSPC_ALL False (*) Turns on all the test cases ------------------------------------------------------------------------------- bluez-5.82/android/PaxHeaders/ipc-tester.c0000644000000000000000000000005014711225433015513 xustar0020 atime=1743516867 20 ctime=1743591279 bluez-5.82/android/ipc-tester.c0000644000000000000000000013137114711225433015202 0ustar00rootroot// SPDX-License-Identifier: LGPL-2.1-or-later /* * * BlueZ - Bluetooth protocol stack for Linux * * Copyright (C) 2013-2014 Intel Corporation. All rights reserved. * * */ #ifdef HAVE_CONFIG_H #include #endif #define _GNU_SOURCE #include #include #include #include #include #include #include #include #include #include #include #include "lib/bluetooth.h" #include "lib/mgmt.h" #include "src/shared/tester.h" #include "src/shared/mgmt.h" #include "emulator/hciemu.h" #include "hal-msg.h" #include "ipc-common.h" #include #define WAIT_FOR_SIGNAL_TIME 2 /* in seconds */ #define EMULATOR_SIGNAL "emulator_started" struct test_data { struct mgmt *mgmt; uint16_t mgmt_index; struct hciemu *hciemu; enum hciemu_type hciemu_type; pid_t bluetoothd_pid; bool setup_done; }; struct ipc_data { void *buffer; size_t len; }; struct generic_data { struct ipc_data ipc_data; unsigned int num_services; int init_services[]; }; struct regmod_msg { struct ipc_hdr header; struct hal_cmd_register_module cmd; } __attribute__((packed)); #define CONNECT_TIMEOUT (5 * 1000) #define SERVICE_NAME "bluetoothd" static char exec_dir[PATH_MAX]; static int cmd_sk = -1; static int notif_sk = -1; static void read_info_callback(uint8_t status, uint16_t length, const void *param, void *user_data) { struct test_data *data = tester_get_data(); const struct mgmt_rp_read_info *rp = param; char addr[18]; uint16_t manufacturer; uint32_t supported_settings, current_settings; tester_print("Read Info callback"); tester_print(" Status: 0x%02x", status); if (status || !param) { tester_pre_setup_failed(); return; } ba2str(&rp->bdaddr, addr); manufacturer = btohs(rp->manufacturer); supported_settings = btohl(rp->supported_settings); current_settings = btohl(rp->current_settings); tester_print(" Address: %s", addr); tester_print(" Version: 0x%02x", rp->version); tester_print(" Manufacturer: 0x%04x", manufacturer); tester_print(" Supported settings: 0x%08x", supported_settings); tester_print(" Current settings: 0x%08x", current_settings); tester_print(" Class: 0x%02x%02x%02x", rp->dev_class[2], rp->dev_class[1], rp->dev_class[0]); tester_print(" Name: %s", rp->name); tester_print(" Short name: %s", rp->short_name); if (strcmp(hciemu_get_address(data->hciemu), addr)) { tester_pre_setup_failed(); return; } tester_pre_setup_complete(); } static void index_added_callback(uint16_t index, uint16_t length, const void *param, void *user_data) { struct test_data *data = tester_get_data(); tester_print("Index Added callback"); tester_print(" Index: 0x%04x", index); data->mgmt_index = index; mgmt_send(data->mgmt, MGMT_OP_READ_INFO, data->mgmt_index, 0, NULL, read_info_callback, NULL, NULL); } static void index_removed_callback(uint16_t index, uint16_t length, const void *param, void *user_data) { struct test_data *data = tester_get_data(); tester_print("Index Removed callback"); tester_print(" Index: 0x%04x", index); if (index != data->mgmt_index) return; mgmt_unregister_index(data->mgmt, data->mgmt_index); mgmt_unref(data->mgmt); data->mgmt = NULL; tester_post_teardown_complete(); } static void read_index_list_callback(uint8_t status, uint16_t length, const void *param, void *user_data) { struct test_data *data = tester_get_data(); tester_print("Read Index List callback"); tester_print(" Status: 0x%02x", status); if (status || !param) { tester_pre_setup_failed(); return; } mgmt_register(data->mgmt, MGMT_EV_INDEX_ADDED, MGMT_INDEX_NONE, index_added_callback, NULL, NULL); mgmt_register(data->mgmt, MGMT_EV_INDEX_REMOVED, MGMT_INDEX_NONE, index_removed_callback, NULL, NULL); data->hciemu = hciemu_new(data->hciemu_type); if (!data->hciemu) { tester_warn("Failed to setup HCI emulation"); tester_pre_setup_failed(); return; } tester_print("New hciemu instance created"); } static void test_pre_setup(const void *data) { struct test_data *test_data = tester_get_data(); if (!tester_use_debug()) fclose(stderr); test_data->mgmt = mgmt_new_default(); if (!test_data->mgmt) { tester_warn("Failed to setup management interface"); tester_pre_setup_failed(); return; } mgmt_send(test_data->mgmt, MGMT_OP_READ_INDEX_LIST, MGMT_INDEX_NONE, 0, NULL, read_index_list_callback, NULL, NULL); } static void test_post_teardown(const void *data) { struct test_data *test_data = tester_get_data(); if (test_data->hciemu) { hciemu_unref(test_data->hciemu); test_data->hciemu = NULL; } } static void bluetoothd_start(int hci_index) { char prg_name[PATH_MAX + 11]; char index[8]; char *prg_argv[4]; snprintf(prg_name, sizeof(prg_name), "%s/%s", exec_dir, "bluetoothd"); snprintf(index, sizeof(index), "%d", hci_index); prg_argv[0] = prg_name; prg_argv[1] = "-i"; prg_argv[2] = index; prg_argv[3] = NULL; if (!tester_use_debug()) fclose(stderr); execve(prg_argv[0], prg_argv, NULL); } static void emulator(int pipe, int hci_index) { static const char SYSTEM_SOCKET_PATH[] = "\0android_system"; char buf[1024]; struct sockaddr_un addr; struct timeval tv; int fd; ssize_t len; fd = socket(PF_LOCAL, SOCK_DGRAM | SOCK_CLOEXEC, 0); if (fd < 0) goto failed; tv.tv_sec = WAIT_FOR_SIGNAL_TIME; tv.tv_usec = 0; setsockopt(fd, SOL_SOCKET, SO_RCVTIMEO, (char *)&tv, sizeof(tv)); memset(&addr, 0, sizeof(addr)); addr.sun_family = AF_UNIX; memcpy(addr.sun_path, SYSTEM_SOCKET_PATH, sizeof(SYSTEM_SOCKET_PATH)); if (bind(fd, (struct sockaddr *) &addr, sizeof(addr)) < 0) { perror("Failed to bind system socket"); goto failed; } len = write(pipe, EMULATOR_SIGNAL, sizeof(EMULATOR_SIGNAL)); if (len != sizeof(EMULATOR_SIGNAL)) goto failed; memset(buf, 0, sizeof(buf)); len = read(fd, buf, sizeof(buf)); if (len <= 0 || strcmp(buf, "ctl.start=bluetoothd")) goto failed; close(pipe); close(fd); return bluetoothd_start(hci_index); failed: close(pipe); if (fd >= 0) close(fd); } static int accept_connection(int sk) { int err; struct pollfd pfd; int new_sk; memset(&pfd, 0 , sizeof(pfd)); pfd.fd = sk; pfd.events = POLLIN; err = poll(&pfd, 1, CONNECT_TIMEOUT); if (err < 0) { err = errno; tester_warn("Failed to poll: %d (%s)", err, strerror(err)); return -errno; } if (err == 0) { tester_warn("bluetoothd connect timeout"); return -errno; } new_sk = accept(sk, NULL, NULL); if (new_sk < 0) { err = errno; tester_warn("Failed to accept socket: %d (%s)", err, strerror(err)); return -errno; } return new_sk; } static bool init_ipc(void) { struct sockaddr_un addr; int sk; int err; sk = socket(AF_LOCAL, SOCK_SEQPACKET, 0); if (sk < 0) { err = errno; tester_warn("Failed to create socket: %d (%s)", err, strerror(err)); return false; } memset(&addr, 0, sizeof(addr)); addr.sun_family = AF_UNIX; memcpy(addr.sun_path, BLUEZ_HAL_SK_PATH, sizeof(BLUEZ_HAL_SK_PATH)); if (bind(sk, (struct sockaddr *) &addr, sizeof(addr)) < 0) { err = errno; tester_warn("Failed to bind socket: %d (%s)", err, strerror(err)); close(sk); return false; } if (listen(sk, 2) < 0) { err = errno; tester_warn("Failed to listen on socket: %d (%s)", err, strerror(err)); close(sk); return false; } /* Start Android Bluetooth daemon service */ if (property_set("ctl.start", SERVICE_NAME) < 0) { tester_warn("Failed to start service %s", SERVICE_NAME); close(sk); return false; } cmd_sk = accept_connection(sk); if (cmd_sk < 0) { close(sk); return false; } notif_sk = accept_connection(sk); if (notif_sk < 0) { close(sk); close(cmd_sk); cmd_sk = -1; return false; } tester_print("bluetoothd connected"); close(sk); return true; } static void cleanup_ipc(void) { if (cmd_sk < 0) return; close(cmd_sk); cmd_sk = -1; } static gboolean check_for_daemon(gpointer user_data) { int status; struct test_data *data = user_data; if ((waitpid(data->bluetoothd_pid, &status, WNOHANG)) != data->bluetoothd_pid) return true; if (data->setup_done) { if (WIFEXITED(status) && (WEXITSTATUS(status) == EXIT_SUCCESS)) { tester_test_passed(); return false; } tester_test_failed(); } else { tester_setup_failed(); test_post_teardown(data); } tester_warn("Unexpected Daemon shutdown with status %d", status); return false; } static bool setup_module(int service_id) { struct ipc_hdr response; struct ipc_hdr expected_response; struct regmod_msg btmodule_msg = { .header = { .service_id = HAL_SERVICE_ID_CORE, .opcode = HAL_OP_REGISTER_MODULE, .len = sizeof(struct hal_cmd_register_module), }, .cmd = { .service_id = service_id, .mode = HAL_MODE_DEFAULT, .max_clients = 1, }, }; if (write(cmd_sk, &btmodule_msg, sizeof(btmodule_msg)) < 0) goto fail; if (read(cmd_sk, &response, sizeof(response)) < 0) goto fail; expected_response = btmodule_msg.header; expected_response.len = 0; if (memcmp(&response, &expected_response, sizeof(response)) == 0) return true; fail: tester_warn("Module registration failed."); return false; } static void setup(const void *data) { const struct generic_data *generic_data = data; struct test_data *test_data = tester_get_data(); int signal_fd[2]; char buf[1024]; pid_t pid; int len; unsigned int i; if (pipe(signal_fd)) goto failed; pid = fork(); if (pid < 0) { close(signal_fd[0]); close(signal_fd[1]); goto failed; } if (pid == 0) { if (!tester_use_debug()) fclose(stderr); close(signal_fd[0]); emulator(signal_fd[1], test_data->mgmt_index); exit(0); } close(signal_fd[1]); test_data->bluetoothd_pid = pid; len = read(signal_fd[0], buf, sizeof(buf)); if (len <= 0 || (strcmp(buf, EMULATOR_SIGNAL))) { close(signal_fd[0]); goto failed; } g_idle_add_full(G_PRIORITY_DEFAULT_IDLE, check_for_daemon, test_data, NULL); if (!init_ipc()) { tester_warn("Cannot initialize IPC mechanism!"); goto failed; } tester_print("Will init %d services.", generic_data->num_services); for (i = 0; i < generic_data->num_services; i++) if (!setup_module(generic_data->init_services[i])) { cleanup_ipc(); goto failed; } test_data->setup_done = true; tester_setup_complete(); return; failed: g_idle_remove_by_data(test_data); tester_setup_failed(); test_post_teardown(data); } static void teardown(const void *data) { struct test_data *test_data = tester_get_data(); g_idle_remove_by_data(test_data); cleanup_ipc(); if (test_data->bluetoothd_pid) waitpid(test_data->bluetoothd_pid, NULL, 0); tester_teardown_complete(); } static void ipc_send_tc(const void *data) { const struct generic_data *generic_data = data; const struct ipc_data *ipc_data = &generic_data->ipc_data; if (ipc_data->len) { if (write(cmd_sk, ipc_data->buffer, ipc_data->len) < 0) tester_test_failed(); } } #define service_data(args...) { args } #define gen_data(writelen, writebuf, servicelist...) \ { \ .ipc_data = { \ .buffer = writebuf, \ .len = writelen, \ }, \ .init_services = service_data(servicelist), \ .num_services = sizeof((const int[]) \ service_data(servicelist)) / \ sizeof(int), \ } #define test_generic(name, test, setup, teardown, buffer, writelen, \ services...) \ do { \ struct test_data *user; \ static const struct generic_data data = \ gen_data(writelen, buffer, services); \ user = g_malloc0(sizeof(struct test_data)); \ if (!user) \ break; \ user->hciemu_type = HCIEMU_TYPE_BREDRLE; \ tester_add_full(name, &data, test_pre_setup, setup, \ test, teardown, test_post_teardown, \ 3, user, g_free); \ } while (0) #define test_opcode_valid(_name, _service, _opcode, _len, _servicelist...) \ do { \ static struct ipc_hdr hdr = { \ .service_id = _service, \ .opcode = _opcode, \ .len = _len, \ }; \ \ test_generic("Opcode out of range: "_name, \ ipc_send_tc, setup, teardown, \ &hdr, \ sizeof(hdr), \ _servicelist); \ } while (0) struct vardata { struct ipc_hdr hdr; uint8_t buf[IPC_MTU]; } __attribute__((packed)); #define test_datasize_valid(_name, _service, _opcode, _hlen, _addatasize, \ _servicelist...) \ do { \ static struct vardata vdata = { \ .hdr.service_id = _service, \ .hdr.opcode = _opcode, \ .hdr.len = (_hlen) + (_addatasize), \ .buf = {}, \ }; \ test_generic("Data size "_name, \ ipc_send_tc, setup, teardown, \ &vdata, \ sizeof(vdata.hdr) + (_hlen) + (_addatasize),\ _servicelist); \ } while (0) static struct regmod_msg register_bt_msg = { .header = { .service_id = HAL_SERVICE_ID_CORE, .opcode = HAL_OP_REGISTER_MODULE, .len = sizeof(struct hal_cmd_register_module), }, .cmd = { .service_id = HAL_SERVICE_ID_BLUETOOTH, }, }; static struct regmod_msg register_bt_malformed_size_msg = { .header = { .service_id = HAL_SERVICE_ID_CORE, .opcode = HAL_OP_REGISTER_MODULE, /* wrong payload size declared */ .len = sizeof(struct hal_cmd_register_module) - 1, }, .cmd = { .service_id = HAL_SERVICE_ID_CORE, }, }; struct malformed_data3_struct { struct regmod_msg valid_msg; int redundant_data; } __attribute__((packed)); static struct malformed_data3_struct malformed_data3_msg = { /* valid register service message */ .valid_msg = { .header = { .service_id = HAL_SERVICE_ID_CORE, .opcode = HAL_OP_REGISTER_MODULE, .len = sizeof(struct hal_cmd_register_module), }, .cmd = { .service_id = HAL_SERVICE_ID_CORE, }, }, /* plus redundant data */ . redundant_data = 666, }; static struct ipc_hdr enable_unknown_service_hdr = { .service_id = HAL_SERVICE_ID_MAX + 1, .opcode = HAL_OP_REGISTER_MODULE, .len = 0, }; static struct ipc_hdr enable_bt_service_hdr = { .service_id = HAL_SERVICE_ID_BLUETOOTH, .opcode = HAL_OP_ENABLE, .len = 0, }; struct bt_set_adapter_prop_data { struct ipc_hdr hdr; struct hal_cmd_set_adapter_prop prop; /* data placeholder for hal_cmd_set_adapter_prop.val[0] */ uint8_t buf[IPC_MTU - sizeof(struct ipc_hdr) - sizeof(struct hal_cmd_set_adapter_prop)]; } __attribute__((packed)); #define set_name "new name" static struct bt_set_adapter_prop_data bt_set_adapter_prop_data_overs = { .hdr.service_id = HAL_SERVICE_ID_BLUETOOTH, .hdr.opcode = HAL_OP_SET_ADAPTER_PROP, .hdr.len = sizeof(struct hal_cmd_set_adapter_prop) + sizeof(set_name), .prop.type = HAL_PROP_ADAPTER_NAME, /* declare wrong descriptor length */ .prop.len = sizeof(set_name) + 1, /* init prop.val[0] */ .buf = set_name, }; static struct bt_set_adapter_prop_data bt_set_adapter_prop_data_unders = { .hdr.service_id = HAL_SERVICE_ID_BLUETOOTH, .hdr.opcode = HAL_OP_SET_ADAPTER_PROP, .hdr.len = sizeof(struct hal_cmd_set_adapter_prop) + sizeof(set_name), .prop.type = HAL_PROP_ADAPTER_NAME, /* declare wrong descriptor length */ .prop.len = sizeof(set_name) - 1, /* init prop.val[0] */ .buf = set_name, }; struct bt_set_remote_prop_data { struct ipc_hdr hdr; struct hal_cmd_set_remote_device_prop prop; /* data placeholder for hal_cmd_set_remote_device_prop.val[0] */ uint8_t buf[IPC_MTU - sizeof(struct ipc_hdr) - sizeof(struct hal_cmd_set_remote_device_prop)]; } __attribute__((packed)); static struct bt_set_remote_prop_data bt_set_remote_prop_data_overs = { .hdr.service_id = HAL_SERVICE_ID_BLUETOOTH, .hdr.opcode = HAL_OP_SET_REMOTE_DEVICE_PROP, .hdr.len = sizeof(struct hal_cmd_set_remote_device_prop) + sizeof(set_name), .prop.bdaddr = {}, .prop.type = HAL_PROP_DEVICE_NAME, /* declare wrong descriptor length */ .prop.len = sizeof(set_name) + 1, .buf = set_name, }; static struct bt_set_remote_prop_data bt_set_remote_prop_data_unders = { .hdr.service_id = HAL_SERVICE_ID_BLUETOOTH, .hdr.opcode = HAL_OP_SET_REMOTE_DEVICE_PROP, .hdr.len = sizeof(struct hal_cmd_set_remote_device_prop) + sizeof(set_name), .prop.bdaddr = {}, .prop.type = HAL_PROP_DEVICE_NAME, /* declare wrong descriptor length */ .prop.len = sizeof(set_name) - 1, .buf = set_name, }; struct hidhost_set_info_data { struct ipc_hdr hdr; struct hal_cmd_hidhost_set_info info; /* data placeholder for hal_cmd_hidhost_set_info.descr[0] field */ uint8_t buf[IPC_MTU - sizeof(struct ipc_hdr) - sizeof(struct hal_cmd_hidhost_set_info)]; } __attribute__((packed)); #define set_info_data "some descriptor" static struct hidhost_set_info_data hidhost_set_info_data_overs = { .hdr.service_id = HAL_SERVICE_ID_HIDHOST, .hdr.opcode = HAL_OP_HIDHOST_SET_INFO, .hdr.len = sizeof(struct hal_cmd_hidhost_set_info) + sizeof(set_info_data), /* declare wrong descriptor length */ .info.descr_len = sizeof(set_info_data) + 1, /* init .info.descr[0] */ .buf = set_info_data, }; static struct hidhost_set_info_data hidhost_set_info_data_unders = { .hdr.service_id = HAL_SERVICE_ID_HIDHOST, .hdr.opcode = HAL_OP_HIDHOST_SET_INFO, .hdr.len = sizeof(struct hal_cmd_hidhost_set_info) + sizeof(set_info_data), /* declare wrong descriptor length */ .info.descr_len = sizeof(set_info_data) - 1, /* init .info.descr[0] */ .buf = set_info_data, }; struct hidhost_set_report_data { struct ipc_hdr hdr; struct hal_cmd_hidhost_set_report report; /* data placeholder for hal_cmd_hidhost_set_report.data[0] field */ uint8_t buf[IPC_MTU - sizeof(struct ipc_hdr) - sizeof(struct hal_cmd_hidhost_set_report)]; } __attribute__((packed)); #define set_rep_data "1234567890" static struct hidhost_set_report_data hidhost_set_report_data_overs = { .hdr.service_id = HAL_SERVICE_ID_HIDHOST, .hdr.opcode = HAL_OP_HIDHOST_SET_REPORT, .hdr.len = sizeof(struct hal_cmd_hidhost_set_report) + sizeof(set_rep_data), /* declare wrong descriptor length */ .report.len = sizeof(set_rep_data) + 1, /* init report.data[0] */ .buf = set_rep_data, }; static struct hidhost_set_report_data hidhost_set_report_data_unders = { .hdr.service_id = HAL_SERVICE_ID_HIDHOST, .hdr.opcode = HAL_OP_HIDHOST_SET_REPORT, .hdr.len = sizeof(struct hal_cmd_hidhost_set_report) + sizeof(set_rep_data), /* declare wrong descriptor length */ .report.len = sizeof(set_rep_data) - 1, /* init report.data[0] */ .buf = set_rep_data, }; struct hidhost_send_data_data { struct ipc_hdr hdr; struct hal_cmd_hidhost_send_data hiddata; /* data placeholder for hal_cmd_hidhost_send_data.data[0] field */ uint8_t buf[IPC_MTU - sizeof(struct ipc_hdr) - sizeof(struct hal_cmd_hidhost_send_data)]; } __attribute__((packed)); #define send_data_data "1234567890" static struct hidhost_send_data_data hidhost_send_data_overs = { .hdr.service_id = HAL_SERVICE_ID_HIDHOST, .hdr.opcode = HAL_OP_HIDHOST_SEND_DATA, .hdr.len = sizeof(struct hal_cmd_hidhost_send_data) + sizeof(send_data_data), /* declare wrong descriptor length */ .hiddata.len = sizeof(send_data_data) + 1, /* init .hiddata.data[0] */ .buf = send_data_data, }; static struct hidhost_send_data_data hidhost_send_data_unders = { .hdr.service_id = HAL_SERVICE_ID_HIDHOST, .hdr.opcode = HAL_OP_HIDHOST_SEND_DATA, .hdr.len = sizeof(struct hal_cmd_hidhost_send_data) + sizeof(send_data_data), /* declare wrong descriptor length */ .hiddata.len = sizeof(send_data_data) - 1, /* init .hiddata.data[0] */ .buf = send_data_data, }; #define hfp_number "#1234567890" struct hfp_dial_data { struct ipc_hdr hdr; struct hal_cmd_hf_client_dial data; uint8_t buf[IPC_MTU - sizeof(struct ipc_hdr) - sizeof(struct hal_cmd_hf_client_dial)]; } __attribute__((packed)); static struct hfp_dial_data hfp_dial_overs = { .hdr.service_id = HAL_SERVICE_ID_HANDSFREE_CLIENT, .hdr.opcode = HAL_OP_HF_CLIENT_DIAL, .hdr.len = sizeof(struct hal_cmd_hf_client_dial) + sizeof(hfp_number), .data.number_len = sizeof(hfp_number) + 1, .buf = hfp_number, }; static struct hfp_dial_data hfp_dial_unders = { .hdr.service_id = HAL_SERVICE_ID_HANDSFREE_CLIENT, .hdr.opcode = HAL_OP_HF_CLIENT_DIAL, .hdr.len = sizeof(struct hal_cmd_hf_client_dial) + sizeof(hfp_number), .data.number_len = sizeof(hfp_number) - 1, .buf = hfp_number, }; int main(int argc, char *argv[]) { snprintf(exec_dir, sizeof(exec_dir), "%s", dirname(argv[0])); tester_init(&argc, &argv); /* check general IPC errors */ test_generic("Too small data", ipc_send_tc, setup, teardown, ®ister_bt_msg, 1); test_generic("Malformed data (wrong payload declared)", ipc_send_tc, setup, teardown, ®ister_bt_malformed_size_msg, sizeof(register_bt_malformed_size_msg), HAL_SERVICE_ID_BLUETOOTH); test_generic("Malformed data2 (undersized msg)", ipc_send_tc, setup, teardown, ®ister_bt_msg, sizeof(register_bt_msg) - 1, HAL_SERVICE_ID_BLUETOOTH); test_generic("Malformed data3 (oversized msg)", ipc_send_tc, setup, teardown, &malformed_data3_msg, sizeof(malformed_data3_msg), HAL_SERVICE_ID_BLUETOOTH); test_generic("Invalid service", ipc_send_tc, setup, teardown, &enable_unknown_service_hdr, sizeof(enable_unknown_service_hdr), HAL_SERVICE_ID_BLUETOOTH); test_generic("Enable unregistered service", ipc_send_tc, setup, teardown, &enable_bt_service_hdr, sizeof(enable_bt_service_hdr)); /* check service handler's max opcode value */ test_opcode_valid("CORE", HAL_SERVICE_ID_CORE, 0x03, 0); test_opcode_valid("BLUETOOTH", HAL_SERVICE_ID_BLUETOOTH, 0x15, 0, HAL_SERVICE_ID_BLUETOOTH); test_opcode_valid("SOCK", HAL_SERVICE_ID_SOCKET, 0x03, 0, HAL_SERVICE_ID_BLUETOOTH, HAL_SERVICE_ID_SOCKET); test_opcode_valid("HIDHOST", HAL_SERVICE_ID_HIDHOST, 0x10, 0, HAL_SERVICE_ID_BLUETOOTH, HAL_SERVICE_ID_HIDHOST); test_opcode_valid("PAN", HAL_SERVICE_ID_PAN, 0x05, 0, HAL_SERVICE_ID_BLUETOOTH, HAL_SERVICE_ID_PAN); test_opcode_valid("HANDSFREE", HAL_SERVICE_ID_HANDSFREE, 0x10, 0, HAL_SERVICE_ID_BLUETOOTH, HAL_SERVICE_ID_HANDSFREE); test_opcode_valid("A2DP", HAL_SERVICE_ID_A2DP, 0x03, 0, HAL_SERVICE_ID_BLUETOOTH, HAL_SERVICE_ID_A2DP); test_opcode_valid("HEALTH", HAL_SERVICE_ID_HEALTH, 0x06, 0, HAL_SERVICE_ID_BLUETOOTH, HAL_SERVICE_ID_HEALTH); test_opcode_valid("AVRCP", HAL_SERVICE_ID_AVRCP, 0x0b, 0, HAL_SERVICE_ID_BLUETOOTH, HAL_SERVICE_ID_AVRCP); test_opcode_valid("GATT", HAL_SERVICE_ID_GATT, 0x24, 0, HAL_SERVICE_ID_BLUETOOTH, HAL_SERVICE_ID_GATT); test_opcode_valid("HF_CLIENT", HAL_SERVICE_ID_HANDSFREE_CLIENT, 0x10, 0, HAL_SERVICE_ID_BLUETOOTH, HAL_SERVICE_ID_HANDSFREE_CLIENT); test_opcode_valid("MAP_CLIENT", HAL_SERVICE_ID_MAP_CLIENT, 0x01, 0, HAL_SERVICE_ID_BLUETOOTH, HAL_SERVICE_ID_MAP_CLIENT); /* check for valid data size */ test_datasize_valid("CORE Register+", HAL_SERVICE_ID_CORE, HAL_OP_REGISTER_MODULE, sizeof(struct hal_cmd_register_module), 1); test_datasize_valid("CORE Register-", HAL_SERVICE_ID_CORE, HAL_OP_REGISTER_MODULE, sizeof(struct hal_cmd_register_module), -1); test_datasize_valid("CORE Unregister+", HAL_SERVICE_ID_CORE, HAL_OP_UNREGISTER_MODULE, sizeof(struct hal_cmd_unregister_module), 1); test_datasize_valid("CORE Unregister-", HAL_SERVICE_ID_CORE, HAL_OP_UNREGISTER_MODULE, sizeof(struct hal_cmd_unregister_module), -1); /* check for valid data size for BLUETOOTH */ test_datasize_valid("BT Enable+", HAL_SERVICE_ID_BLUETOOTH, HAL_OP_ENABLE, 0, 1, HAL_SERVICE_ID_BLUETOOTH); test_datasize_valid("BT Disable+", HAL_SERVICE_ID_BLUETOOTH, HAL_OP_DISABLE, 0, 1, HAL_SERVICE_ID_BLUETOOTH); test_datasize_valid("BT Get Adapter Props+", HAL_SERVICE_ID_BLUETOOTH, HAL_OP_GET_ADAPTER_PROPS, 0, 1, HAL_SERVICE_ID_BLUETOOTH); test_datasize_valid("BT Get Adapter Prop+", HAL_SERVICE_ID_BLUETOOTH, HAL_OP_GET_ADAPTER_PROP, sizeof(struct hal_cmd_get_adapter_prop), 1, HAL_SERVICE_ID_BLUETOOTH); test_datasize_valid("BT Get Adapter Prop-", HAL_SERVICE_ID_BLUETOOTH, HAL_OP_GET_ADAPTER_PROP, sizeof(struct hal_cmd_get_adapter_prop), -1, HAL_SERVICE_ID_BLUETOOTH); test_datasize_valid("BT Set Adapter Prop+", HAL_SERVICE_ID_BLUETOOTH, HAL_OP_SET_ADAPTER_PROP, sizeof(struct hal_cmd_set_adapter_prop), 1, HAL_SERVICE_ID_BLUETOOTH); test_datasize_valid("BT Set Adapter Prop-", HAL_SERVICE_ID_BLUETOOTH, HAL_OP_SET_ADAPTER_PROP, sizeof(struct hal_cmd_set_adapter_prop), -1, HAL_SERVICE_ID_BLUETOOTH); test_generic("Data size BT Set Adapter Prop Vardata+", ipc_send_tc, setup, teardown, &bt_set_adapter_prop_data_overs, (sizeof(struct ipc_hdr) + sizeof(struct hal_cmd_set_adapter_prop) + sizeof(set_name)), HAL_SERVICE_ID_BLUETOOTH); test_generic("Data size BT Set Adapter Prop Vardata+", ipc_send_tc, setup, teardown, &bt_set_adapter_prop_data_unders, (sizeof(struct ipc_hdr) + sizeof(struct hal_cmd_set_adapter_prop) + sizeof(set_name)), HAL_SERVICE_ID_BLUETOOTH); test_datasize_valid("BT Get Remote Props+", HAL_SERVICE_ID_BLUETOOTH, HAL_OP_GET_REMOTE_DEVICE_PROPS, sizeof(struct hal_cmd_get_remote_device_props), 1, HAL_SERVICE_ID_BLUETOOTH); test_datasize_valid("BT Get Remote Props-", HAL_SERVICE_ID_BLUETOOTH, HAL_OP_GET_REMOTE_DEVICE_PROPS, sizeof(struct hal_cmd_get_remote_device_props), -1, HAL_SERVICE_ID_BLUETOOTH); test_datasize_valid("BT Get Remote Prop+", HAL_SERVICE_ID_BLUETOOTH, HAL_OP_GET_REMOTE_DEVICE_PROP, sizeof(struct hal_cmd_get_remote_device_prop), 1, HAL_SERVICE_ID_BLUETOOTH); test_datasize_valid("BT Get Remote Prop-", HAL_SERVICE_ID_BLUETOOTH, HAL_OP_GET_REMOTE_DEVICE_PROP, sizeof(struct hal_cmd_get_remote_device_prop), -1, HAL_SERVICE_ID_BLUETOOTH); test_datasize_valid("BT Set Remote Prop+", HAL_SERVICE_ID_BLUETOOTH, HAL_OP_SET_REMOTE_DEVICE_PROP, sizeof(struct hal_cmd_set_remote_device_prop), 1, HAL_SERVICE_ID_BLUETOOTH); test_datasize_valid("BT Set Remote Prop-", HAL_SERVICE_ID_BLUETOOTH, HAL_OP_SET_REMOTE_DEVICE_PROP, sizeof(struct hal_cmd_set_remote_device_prop), -1, HAL_SERVICE_ID_BLUETOOTH); test_generic("Data size BT Set Remote Prop Vardata+", ipc_send_tc, setup, teardown, &bt_set_remote_prop_data_overs, (sizeof(struct ipc_hdr) + sizeof(struct hal_cmd_set_remote_device_prop) + sizeof(set_name)), HAL_SERVICE_ID_BLUETOOTH); test_generic("Data size BT Set Remote Prop Vardata-", ipc_send_tc, setup, teardown, &bt_set_remote_prop_data_unders, (sizeof(struct ipc_hdr) + sizeof(struct hal_cmd_set_remote_device_prop) + sizeof(set_name)), HAL_SERVICE_ID_BLUETOOTH); test_datasize_valid("BT Get Remote SV Rec+", HAL_SERVICE_ID_BLUETOOTH, HAL_OP_GET_REMOTE_SERVICE_REC, sizeof(struct hal_cmd_get_remote_service_rec), 1, HAL_SERVICE_ID_BLUETOOTH); test_datasize_valid("BT Get Remote SV Rec-", HAL_SERVICE_ID_BLUETOOTH, HAL_OP_GET_REMOTE_SERVICE_REC, sizeof(struct hal_cmd_get_remote_service_rec), -1, HAL_SERVICE_ID_BLUETOOTH); test_datasize_valid("BT Get Remote Services+", HAL_SERVICE_ID_BLUETOOTH, HAL_OP_GET_REMOTE_SERVICES, sizeof(struct hal_cmd_get_remote_services), 1, HAL_SERVICE_ID_BLUETOOTH); test_datasize_valid("BT Get Remote Services-", HAL_SERVICE_ID_BLUETOOTH, HAL_OP_GET_REMOTE_SERVICES, sizeof(struct hal_cmd_get_remote_services), -1, HAL_SERVICE_ID_BLUETOOTH); test_datasize_valid("BT Start Discovery+", HAL_SERVICE_ID_BLUETOOTH, HAL_OP_START_DISCOVERY, 0, 1, HAL_SERVICE_ID_BLUETOOTH); test_datasize_valid("BT Cancel Discovery+", HAL_SERVICE_ID_BLUETOOTH, HAL_OP_CANCEL_DISCOVERY, 0, 1, HAL_SERVICE_ID_BLUETOOTH); test_datasize_valid("BT Create Bond+", HAL_SERVICE_ID_BLUETOOTH, HAL_OP_CREATE_BOND, sizeof(struct hal_cmd_create_bond), 1, HAL_SERVICE_ID_BLUETOOTH); test_datasize_valid("BT Create Bond-", HAL_SERVICE_ID_BLUETOOTH, HAL_OP_CREATE_BOND, sizeof(struct hal_cmd_create_bond), -1, HAL_SERVICE_ID_BLUETOOTH); test_datasize_valid("BT Remove Bond+", HAL_SERVICE_ID_BLUETOOTH, HAL_OP_REMOVE_BOND, sizeof(struct hal_cmd_remove_bond), 1, HAL_SERVICE_ID_BLUETOOTH); test_datasize_valid("BT Remove Bond-", HAL_SERVICE_ID_BLUETOOTH, HAL_OP_REMOVE_BOND, sizeof(struct hal_cmd_remove_bond), -1, HAL_SERVICE_ID_BLUETOOTH); test_datasize_valid("BT Cancel Bond+", HAL_SERVICE_ID_BLUETOOTH, HAL_OP_CANCEL_BOND, sizeof(struct hal_cmd_cancel_bond), 1, HAL_SERVICE_ID_BLUETOOTH); test_datasize_valid("BT Cancel Bond-", HAL_SERVICE_ID_BLUETOOTH, HAL_OP_CANCEL_BOND, sizeof(struct hal_cmd_cancel_bond), -1, HAL_SERVICE_ID_BLUETOOTH); test_datasize_valid("BT Pin Reply+", HAL_SERVICE_ID_BLUETOOTH, HAL_OP_PIN_REPLY, sizeof(struct hal_cmd_pin_reply), 1, HAL_SERVICE_ID_BLUETOOTH); test_datasize_valid("BT Pin Reply-", HAL_SERVICE_ID_BLUETOOTH, HAL_OP_PIN_REPLY, sizeof(struct hal_cmd_pin_reply), -1, HAL_SERVICE_ID_BLUETOOTH); test_datasize_valid("BT SSP Reply+", HAL_SERVICE_ID_BLUETOOTH, HAL_OP_SSP_REPLY, sizeof(struct hal_cmd_ssp_reply), 1, HAL_SERVICE_ID_BLUETOOTH); test_datasize_valid("BT SSP Reply-", HAL_SERVICE_ID_BLUETOOTH, HAL_OP_SSP_REPLY, sizeof(struct hal_cmd_ssp_reply), -1, HAL_SERVICE_ID_BLUETOOTH); test_datasize_valid("BT DUT Mode Conf+", HAL_SERVICE_ID_BLUETOOTH, HAL_OP_DUT_MODE_CONF, sizeof(struct hal_cmd_dut_mode_conf), 1, HAL_SERVICE_ID_BLUETOOTH); test_datasize_valid("BT DUT Mode Conf-", HAL_SERVICE_ID_BLUETOOTH, HAL_OP_DUT_MODE_CONF, sizeof(struct hal_cmd_dut_mode_conf), -1, HAL_SERVICE_ID_BLUETOOTH); test_datasize_valid("BT DUT Mode Send+", HAL_SERVICE_ID_BLUETOOTH, HAL_OP_DUT_MODE_SEND, sizeof(struct hal_cmd_dut_mode_send), 1, HAL_SERVICE_ID_BLUETOOTH); test_datasize_valid("BT DUT Mode Send-", HAL_SERVICE_ID_BLUETOOTH, HAL_OP_DUT_MODE_SEND, sizeof(struct hal_cmd_dut_mode_send), -1, HAL_SERVICE_ID_BLUETOOTH); test_datasize_valid("BT LE Test+", HAL_SERVICE_ID_BLUETOOTH, HAL_OP_LE_TEST_MODE, sizeof(struct hal_cmd_le_test_mode), 1, HAL_SERVICE_ID_BLUETOOTH); test_datasize_valid("BT LE Test-", HAL_SERVICE_ID_BLUETOOTH, HAL_OP_LE_TEST_MODE, sizeof(struct hal_cmd_le_test_mode), -1, HAL_SERVICE_ID_BLUETOOTH); /* check for valid data size for SOCK */ test_datasize_valid("SOCKET Listen+", HAL_SERVICE_ID_SOCKET, HAL_OP_SOCKET_LISTEN, sizeof(struct hal_cmd_socket_listen), 1, HAL_SERVICE_ID_BLUETOOTH, HAL_SERVICE_ID_SOCKET); test_datasize_valid("SOCKET Listen-", HAL_SERVICE_ID_SOCKET, HAL_OP_SOCKET_LISTEN, sizeof(struct hal_cmd_socket_listen), -1, HAL_SERVICE_ID_BLUETOOTH, HAL_SERVICE_ID_SOCKET); test_datasize_valid("SOCKET Connect+", HAL_SERVICE_ID_SOCKET, HAL_OP_SOCKET_CONNECT, sizeof(struct hal_cmd_socket_connect), 1, HAL_SERVICE_ID_BLUETOOTH, HAL_SERVICE_ID_SOCKET); test_datasize_valid("SOCKET Connect-", HAL_SERVICE_ID_SOCKET, HAL_OP_SOCKET_CONNECT, sizeof(struct hal_cmd_socket_connect), -1, HAL_SERVICE_ID_BLUETOOTH, HAL_SERVICE_ID_SOCKET); /* check for valid data size for HID Host */ test_datasize_valid("HIDHOST Connect+", HAL_SERVICE_ID_HIDHOST, HAL_OP_HIDHOST_CONNECT, sizeof(struct hal_cmd_hidhost_connect), 1, HAL_SERVICE_ID_BLUETOOTH, HAL_SERVICE_ID_HIDHOST); test_datasize_valid("HIDHOST Connect-", HAL_SERVICE_ID_HIDHOST, HAL_OP_HIDHOST_CONNECT, sizeof(struct hal_cmd_hidhost_connect), -1, HAL_SERVICE_ID_BLUETOOTH, HAL_SERVICE_ID_HIDHOST); test_datasize_valid("HIDHOST Disconnect+", HAL_SERVICE_ID_HIDHOST, HAL_OP_HIDHOST_DISCONNECT, sizeof(struct hal_cmd_hidhost_disconnect), 1, HAL_SERVICE_ID_BLUETOOTH, HAL_SERVICE_ID_HIDHOST); test_datasize_valid("HIDHOST Disconnect-", HAL_SERVICE_ID_HIDHOST, HAL_OP_HIDHOST_DISCONNECT, sizeof(struct hal_cmd_hidhost_disconnect), -1, HAL_SERVICE_ID_BLUETOOTH, HAL_SERVICE_ID_HIDHOST); test_datasize_valid("HIDHOST Virt. Unplug+", HAL_SERVICE_ID_HIDHOST, HAL_OP_HIDHOST_VIRTUAL_UNPLUG, sizeof(struct hal_cmd_hidhost_virtual_unplug), 1, HAL_SERVICE_ID_BLUETOOTH, HAL_SERVICE_ID_HIDHOST); test_datasize_valid("HIDHOST Virt. Unplug-", HAL_SERVICE_ID_HIDHOST, HAL_OP_HIDHOST_VIRTUAL_UNPLUG, sizeof(struct hal_cmd_hidhost_virtual_unplug), -1, HAL_SERVICE_ID_BLUETOOTH, HAL_SERVICE_ID_HIDHOST); test_datasize_valid("HIDHOST Set Info+", HAL_SERVICE_ID_HIDHOST, HAL_OP_HIDHOST_SET_INFO, sizeof(struct hal_cmd_hidhost_set_info), 1, HAL_SERVICE_ID_BLUETOOTH, HAL_SERVICE_ID_HIDHOST); test_datasize_valid("HIDHOST Set Info-", HAL_SERVICE_ID_HIDHOST, HAL_OP_HIDHOST_SET_INFO, sizeof(struct hal_cmd_hidhost_set_info), -1, HAL_SERVICE_ID_BLUETOOTH, HAL_SERVICE_ID_HIDHOST); test_generic("Data size HIDHOST Set Info Vardata+", ipc_send_tc, setup, teardown, &hidhost_set_info_data_overs, (sizeof(struct ipc_hdr) + sizeof(struct hal_cmd_hidhost_set_info) + sizeof(set_info_data)), HAL_SERVICE_ID_BLUETOOTH, HAL_SERVICE_ID_HIDHOST); test_generic("Data size HIDHOST Set Info Vardata-", ipc_send_tc, setup, teardown, &hidhost_set_info_data_unders, (sizeof(struct ipc_hdr) + sizeof(struct hal_cmd_hidhost_set_info) + sizeof(set_info_data)), HAL_SERVICE_ID_BLUETOOTH, HAL_SERVICE_ID_HIDHOST); test_datasize_valid("HIDHOST Get Protocol+", HAL_SERVICE_ID_HIDHOST, HAL_OP_HIDHOST_GET_PROTOCOL, sizeof(struct hal_cmd_hidhost_get_protocol), 1, HAL_SERVICE_ID_BLUETOOTH, HAL_SERVICE_ID_HIDHOST); test_datasize_valid("HIDHOST Get Protocol-", HAL_SERVICE_ID_HIDHOST, HAL_OP_HIDHOST_GET_PROTOCOL, sizeof(struct hal_cmd_hidhost_get_protocol), -1, HAL_SERVICE_ID_BLUETOOTH, HAL_SERVICE_ID_HIDHOST); test_datasize_valid("HIDHOST Set Protocol+", HAL_SERVICE_ID_HIDHOST, HAL_OP_HIDHOST_SET_PROTOCOL, sizeof(struct hal_cmd_hidhost_set_protocol), 1, HAL_SERVICE_ID_BLUETOOTH, HAL_SERVICE_ID_HIDHOST); test_datasize_valid("HIDHOST Set Protocol-", HAL_SERVICE_ID_HIDHOST, HAL_OP_HIDHOST_SET_PROTOCOL, sizeof(struct hal_cmd_hidhost_set_protocol), -1, HAL_SERVICE_ID_BLUETOOTH, HAL_SERVICE_ID_HIDHOST); test_datasize_valid("HIDHOST Get Report+", HAL_SERVICE_ID_HIDHOST, HAL_OP_HIDHOST_GET_REPORT, sizeof(struct hal_cmd_hidhost_get_report), 1, HAL_SERVICE_ID_BLUETOOTH, HAL_SERVICE_ID_HIDHOST); test_datasize_valid("HIDHOST Get Report-", HAL_SERVICE_ID_HIDHOST, HAL_OP_HIDHOST_GET_REPORT, sizeof(struct hal_cmd_hidhost_get_report), -1, HAL_SERVICE_ID_BLUETOOTH, HAL_SERVICE_ID_HIDHOST); test_datasize_valid("HIDHOST Set Report+", HAL_SERVICE_ID_HIDHOST, HAL_OP_HIDHOST_SET_REPORT, sizeof(struct hal_cmd_hidhost_set_report), 1, HAL_SERVICE_ID_BLUETOOTH, HAL_SERVICE_ID_HIDHOST); test_datasize_valid("HIDHOST Set Report-", HAL_SERVICE_ID_HIDHOST, HAL_OP_HIDHOST_SET_REPORT, sizeof(struct hal_cmd_hidhost_set_report), -1, HAL_SERVICE_ID_BLUETOOTH, HAL_SERVICE_ID_HIDHOST); test_generic("Data size HIDHOST Set Report Vardata+", ipc_send_tc, setup, teardown, &hidhost_set_report_data_overs, (sizeof(struct ipc_hdr) + sizeof(struct hal_cmd_hidhost_set_report) + sizeof(set_rep_data)), HAL_SERVICE_ID_BLUETOOTH, HAL_SERVICE_ID_HIDHOST); test_generic("Data size HIDHOST Set Report Vardata-", ipc_send_tc, setup, teardown, &hidhost_set_report_data_unders, (sizeof(struct ipc_hdr) + sizeof(struct hal_cmd_hidhost_set_report) + sizeof(set_rep_data)), HAL_SERVICE_ID_BLUETOOTH, HAL_SERVICE_ID_HIDHOST); test_datasize_valid("HIDHOST Send Data+", HAL_SERVICE_ID_HIDHOST, HAL_OP_HIDHOST_SEND_DATA, sizeof(struct hal_cmd_hidhost_send_data), 1, HAL_SERVICE_ID_BLUETOOTH, HAL_SERVICE_ID_HIDHOST); test_datasize_valid("HIDHOST Send Data-", HAL_SERVICE_ID_HIDHOST, HAL_OP_HIDHOST_SEND_DATA, sizeof(struct hal_cmd_hidhost_send_data), -1, HAL_SERVICE_ID_BLUETOOTH, HAL_SERVICE_ID_HIDHOST); test_generic("Data size HIDHOST Send Vardata+", ipc_send_tc, setup, teardown, &hidhost_send_data_overs, (sizeof(struct ipc_hdr) + sizeof(struct hal_cmd_hidhost_send_data) + sizeof(send_data_data)), HAL_SERVICE_ID_BLUETOOTH, HAL_SERVICE_ID_HIDHOST); test_generic("Data size HIDHOST Send Vardata-", ipc_send_tc, setup, teardown, &hidhost_send_data_unders, (sizeof(struct ipc_hdr) + sizeof(struct hal_cmd_hidhost_send_data) + sizeof(send_data_data)), HAL_SERVICE_ID_BLUETOOTH, HAL_SERVICE_ID_HIDHOST); /* check for valid data size for PAN */ test_datasize_valid("PAN Enable+", HAL_SERVICE_ID_PAN, HAL_OP_PAN_ENABLE, sizeof(struct hal_cmd_pan_enable), 1, HAL_SERVICE_ID_BLUETOOTH, HAL_SERVICE_ID_PAN); test_datasize_valid("PAN Enable-", HAL_SERVICE_ID_PAN, HAL_OP_PAN_ENABLE, sizeof(struct hal_cmd_pan_enable), -1, HAL_SERVICE_ID_BLUETOOTH, HAL_SERVICE_ID_PAN); test_datasize_valid("PAN Get Role+", HAL_SERVICE_ID_PAN, HAL_OP_PAN_GET_ROLE, 0, 1, HAL_SERVICE_ID_BLUETOOTH, HAL_SERVICE_ID_PAN); test_datasize_valid("PAN Connect+", HAL_SERVICE_ID_PAN, HAL_OP_PAN_CONNECT, sizeof(struct hal_cmd_pan_connect), 1, HAL_SERVICE_ID_BLUETOOTH, HAL_SERVICE_ID_PAN); test_datasize_valid("PAN Connect-", HAL_SERVICE_ID_PAN, HAL_OP_PAN_CONNECT, sizeof(struct hal_cmd_pan_connect), -1, HAL_SERVICE_ID_BLUETOOTH, HAL_SERVICE_ID_PAN); test_datasize_valid("PAN Disconnect+", HAL_SERVICE_ID_PAN, HAL_OP_PAN_DISCONNECT, sizeof(struct hal_cmd_pan_disconnect), 1, HAL_SERVICE_ID_BLUETOOTH, HAL_SERVICE_ID_PAN); test_datasize_valid("PAN Disconnect-", HAL_SERVICE_ID_PAN, HAL_OP_PAN_DISCONNECT, sizeof(struct hal_cmd_pan_disconnect), -1, HAL_SERVICE_ID_BLUETOOTH, HAL_SERVICE_ID_PAN); /* check for valid data size for A2DP */ test_datasize_valid("A2DP Connect+", HAL_SERVICE_ID_A2DP, HAL_OP_A2DP_CONNECT, sizeof(struct hal_cmd_a2dp_connect), 1, HAL_SERVICE_ID_BLUETOOTH, HAL_SERVICE_ID_A2DP); test_datasize_valid("A2DP Connect-", HAL_SERVICE_ID_A2DP, HAL_OP_A2DP_CONNECT, sizeof(struct hal_cmd_a2dp_connect), -1, HAL_SERVICE_ID_BLUETOOTH, HAL_SERVICE_ID_A2DP); test_datasize_valid("A2DP Disconnect+", HAL_SERVICE_ID_A2DP, HAL_OP_A2DP_DISCONNECT, sizeof(struct hal_cmd_a2dp_disconnect), 1, HAL_SERVICE_ID_BLUETOOTH, HAL_SERVICE_ID_A2DP); test_datasize_valid("A2DP Disconnect-", HAL_SERVICE_ID_A2DP, HAL_OP_A2DP_DISCONNECT, sizeof(struct hal_cmd_a2dp_disconnect), -1, HAL_SERVICE_ID_BLUETOOTH, HAL_SERVICE_ID_A2DP); /* Check for valid data size for Handsfree Client */ test_datasize_valid("HF_CLIENT Connect+", HAL_SERVICE_ID_HANDSFREE_CLIENT, HAL_OP_HF_CLIENT_CONNECT, sizeof(struct hal_cmd_hf_client_connect), 1, HAL_SERVICE_ID_BLUETOOTH, HAL_SERVICE_ID_HANDSFREE_CLIENT); test_datasize_valid("HF_CLIENT Connect-", HAL_SERVICE_ID_HANDSFREE_CLIENT, HAL_OP_HF_CLIENT_CONNECT, sizeof(struct hal_cmd_hf_client_connect), -1, HAL_SERVICE_ID_BLUETOOTH, HAL_SERVICE_ID_HANDSFREE_CLIENT); test_datasize_valid("HF_CLIENT Disconnect+", HAL_SERVICE_ID_HANDSFREE_CLIENT, HAL_OP_HF_CLIENT_DISCONNECT, sizeof(struct hal_cmd_hf_client_disconnect), 1, HAL_SERVICE_ID_BLUETOOTH, HAL_SERVICE_ID_HANDSFREE_CLIENT); test_datasize_valid("HF_CLIENT Disconnect-", HAL_SERVICE_ID_HANDSFREE_CLIENT, HAL_OP_HF_CLIENT_DISCONNECT, sizeof(struct hal_cmd_hf_client_disconnect), -1, HAL_SERVICE_ID_BLUETOOTH, HAL_SERVICE_ID_HANDSFREE_CLIENT); test_datasize_valid("HF_CLIENT Connect Audio+", HAL_SERVICE_ID_HANDSFREE_CLIENT, HAL_OP_HF_CLIENT_CONNECT_AUDIO, sizeof(struct hal_cmd_hf_client_connect_audio), 1, HAL_SERVICE_ID_BLUETOOTH, HAL_SERVICE_ID_HANDSFREE_CLIENT); test_datasize_valid("HF_CLIENT Connect Audio-", HAL_SERVICE_ID_HANDSFREE_CLIENT, HAL_OP_HF_CLIENT_CONNECT_AUDIO, sizeof(struct hal_cmd_hf_client_connect_audio), -1, HAL_SERVICE_ID_BLUETOOTH, HAL_SERVICE_ID_HANDSFREE_CLIENT); test_datasize_valid("HF_CLIENT Disconnect Audio+", HAL_SERVICE_ID_HANDSFREE_CLIENT, HAL_OP_HF_CLIENT_DISCONNECT_AUDIO, sizeof(struct hal_cmd_hf_client_disconnect_audio), 1, HAL_SERVICE_ID_BLUETOOTH, HAL_SERVICE_ID_HANDSFREE_CLIENT); test_datasize_valid("HF_CLIENT Disconnect Audio-", HAL_SERVICE_ID_HANDSFREE_CLIENT, HAL_OP_HF_CLIENT_DISCONNECT_AUDIO, sizeof(struct hal_cmd_hf_client_disconnect_audio), -1, HAL_SERVICE_ID_BLUETOOTH, HAL_SERVICE_ID_HANDSFREE_CLIENT); test_datasize_valid("HF_CLIENT Start VR+", HAL_SERVICE_ID_HANDSFREE_CLIENT, HAL_OP_HF_CLIENT_START_VR, 0, 1, HAL_SERVICE_ID_BLUETOOTH, HAL_SERVICE_ID_HANDSFREE_CLIENT); test_datasize_valid("HF_CLIENT Start VR-", HAL_SERVICE_ID_HANDSFREE_CLIENT, HAL_OP_HF_CLIENT_START_VR, 0, -1, HAL_SERVICE_ID_BLUETOOTH, HAL_SERVICE_ID_HANDSFREE_CLIENT); test_datasize_valid("HF_CLIENT Stop VR+", HAL_SERVICE_ID_HANDSFREE_CLIENT, HAL_OP_HF_CLIENT_STOP_VR, 0, 1, HAL_SERVICE_ID_BLUETOOTH, HAL_SERVICE_ID_HANDSFREE_CLIENT); test_datasize_valid("HF_CLIENT Stop VR-", HAL_SERVICE_ID_HANDSFREE_CLIENT, HAL_OP_HF_CLIENT_STOP_VR, 0, -1, HAL_SERVICE_ID_BLUETOOTH, HAL_SERVICE_ID_HANDSFREE_CLIENT); test_datasize_valid("HF_CLIENT Vol Contr.+", HAL_SERVICE_ID_HANDSFREE_CLIENT, HAL_OP_HF_CLIENT_VOLUME_CONTROL, sizeof(struct hal_cmd_hf_client_volume_control), 1, HAL_SERVICE_ID_BLUETOOTH, HAL_SERVICE_ID_HANDSFREE_CLIENT); test_datasize_valid("HF_CLIENT Vol Contr.-", HAL_SERVICE_ID_HANDSFREE_CLIENT, HAL_OP_HF_CLIENT_VOLUME_CONTROL, sizeof(struct hal_cmd_hf_client_volume_control), -1, HAL_SERVICE_ID_BLUETOOTH, HAL_SERVICE_ID_HANDSFREE_CLIENT); test_generic("Data size HF_CLIENT Dial Vardata+", ipc_send_tc, setup, teardown, &hfp_dial_overs, (sizeof(struct ipc_hdr) + sizeof(struct hal_cmd_hf_client_dial) + sizeof(hfp_number)), HAL_SERVICE_ID_BLUETOOTH, HAL_SERVICE_ID_HANDSFREE_CLIENT); test_generic("Data size HF_CLIENT Dial Vardata-", ipc_send_tc, setup, teardown, &hfp_dial_unders, (sizeof(struct ipc_hdr) + sizeof(struct hal_cmd_hf_client_dial) + sizeof(hfp_number)), HAL_SERVICE_ID_BLUETOOTH, HAL_SERVICE_ID_HANDSFREE_CLIENT); test_datasize_valid("HF_CLIENT Dial Memory+", HAL_SERVICE_ID_HANDSFREE_CLIENT, HAL_OP_HF_CLIENT_DIAL_MEMORY, sizeof(struct hal_cmd_hf_client_dial_memory), 1, HAL_SERVICE_ID_BLUETOOTH, HAL_SERVICE_ID_HANDSFREE_CLIENT); test_datasize_valid("HF_CLIENT Dial Memory-", HAL_SERVICE_ID_HANDSFREE_CLIENT, HAL_OP_HF_CLIENT_DIAL_MEMORY, sizeof(struct hal_cmd_hf_client_dial_memory), -1, HAL_SERVICE_ID_BLUETOOTH, HAL_SERVICE_ID_HANDSFREE_CLIENT); test_datasize_valid("HF_CLIENT Call Action+", HAL_SERVICE_ID_HANDSFREE_CLIENT, HAL_OP_HF_CLIENT_CALL_ACTION, sizeof(struct hal_cmd_hf_client_call_action), 1, HAL_SERVICE_ID_BLUETOOTH, HAL_SERVICE_ID_HANDSFREE_CLIENT); test_datasize_valid("HF_CLIENT Call Action-", HAL_SERVICE_ID_HANDSFREE_CLIENT, HAL_OP_HF_CLIENT_CALL_ACTION, sizeof(struct hal_cmd_hf_client_call_action), -1, HAL_SERVICE_ID_BLUETOOTH, HAL_SERVICE_ID_HANDSFREE_CLIENT); test_datasize_valid("HF_CLIENT Query Current Calls+", HAL_SERVICE_ID_BLUETOOTH, HAL_OP_HF_CLIENT_QUERY_CURRENT_CALLS, 0, 1, HAL_SERVICE_ID_BLUETOOTH, HAL_SERVICE_ID_HANDSFREE_CLIENT); test_datasize_valid("HF_CLIENT Query Current Calls-", HAL_SERVICE_ID_HANDSFREE_CLIENT, HAL_OP_HF_CLIENT_QUERY_CURRENT_CALLS, 0, -1, HAL_SERVICE_ID_BLUETOOTH, HAL_SERVICE_ID_HANDSFREE_CLIENT); test_datasize_valid("HF_CLIENT Query Operator Name+", HAL_SERVICE_ID_HANDSFREE_CLIENT, HAL_OP_HF_CLIENT_QUERY_OPERATOR_NAME, 0, 1, HAL_SERVICE_ID_BLUETOOTH, HAL_SERVICE_ID_HANDSFREE_CLIENT); test_datasize_valid("HF_CLIENT Query Operator Name-", HAL_SERVICE_ID_HANDSFREE_CLIENT, HAL_OP_HF_CLIENT_QUERY_OPERATOR_NAME, 0, -1, HAL_SERVICE_ID_BLUETOOTH, HAL_SERVICE_ID_HANDSFREE_CLIENT); test_datasize_valid("HF_CLIENT Retrieve Subscrb. Info+", HAL_SERVICE_ID_HANDSFREE_CLIENT, HAL_OP_HF_CLIENT_RETRIEVE_SUBSCR_INFO, 0, 1, HAL_SERVICE_ID_BLUETOOTH, HAL_SERVICE_ID_HANDSFREE_CLIENT); test_datasize_valid("HF_CLIENT Retrieve Subscrb. Info-", HAL_SERVICE_ID_HANDSFREE_CLIENT, HAL_OP_HF_CLIENT_RETRIEVE_SUBSCR_INFO, 0, -1, HAL_SERVICE_ID_BLUETOOTH, HAL_SERVICE_ID_HANDSFREE_CLIENT); test_datasize_valid("HF_CLIENT Send DTMF+", HAL_SERVICE_ID_HANDSFREE_CLIENT, HAL_OP_HF_CLIENT_SEND_DTMF, sizeof(struct hal_cmd_hf_client_send_dtmf), 1, HAL_SERVICE_ID_BLUETOOTH, HAL_SERVICE_ID_HANDSFREE_CLIENT); test_datasize_valid("HF_CLIENT Send DTMF-", HAL_SERVICE_ID_HANDSFREE_CLIENT, HAL_OP_HF_CLIENT_SEND_DTMF, sizeof(struct hal_cmd_hf_client_send_dtmf), -1, HAL_SERVICE_ID_BLUETOOTH, HAL_SERVICE_ID_HANDSFREE_CLIENT); test_datasize_valid("HF_CLIENT Get Last Voice Tag+", HAL_SERVICE_ID_HANDSFREE_CLIENT, HAL_OP_HF_CLIENT_GET_LAST_VOICE_TAG_NUM, 0, 1, HAL_SERVICE_ID_BLUETOOTH, HAL_SERVICE_ID_HANDSFREE_CLIENT); test_datasize_valid("HF_CLIENT Get Last Voice Tag-", HAL_SERVICE_ID_HANDSFREE_CLIENT, HAL_OP_HF_CLIENT_GET_LAST_VOICE_TAG_NUM, 0, -1, HAL_SERVICE_ID_BLUETOOTH, HAL_SERVICE_ID_HANDSFREE_CLIENT); /* check for valid data size for MAP CLIENT */ test_datasize_valid("MAP CLIENT Get instances+", HAL_SERVICE_ID_MAP_CLIENT, HAL_OP_MAP_CLIENT_GET_INSTANCES, sizeof(struct hal_cmd_map_client_get_instances), 1, HAL_SERVICE_ID_BLUETOOTH, HAL_SERVICE_ID_MAP_CLIENT); test_datasize_valid("MAP CLIENT Get instances-", HAL_SERVICE_ID_MAP_CLIENT, HAL_OP_MAP_CLIENT_GET_INSTANCES, sizeof(struct hal_cmd_map_client_get_instances), -1, HAL_SERVICE_ID_BLUETOOTH, HAL_SERVICE_ID_MAP_CLIENT); return tester_run(); } bluez-5.82/android/PaxHeaders/hal-handsfree-client.c0000644000000000000000000000005014015011623017400 xustar0020 atime=1743516862 20 ctime=1743591276 bluez-5.82/android/hal-handsfree-client.c0000644000000000000000000003621514015011623017070 0ustar00rootroot// SPDX-License-Identifier: Apache-2.0 /* * Copyright (C) 2014 Intel Corporation * */ #include #include #include #include #include #include "hal-log.h" #include "hal.h" #include "hal-msg.h" #include "ipc-common.h" #include "hal-ipc.h" static const bthf_client_callbacks_t *cbs = NULL; static bool interface_ready(void) { return cbs != NULL; } static void handle_conn_state(void *buf, uint16_t len, int fd) { struct hal_ev_hf_client_conn_state *ev = buf; if (cbs->connection_state_cb) cbs->connection_state_cb(ev->state, ev->peer_feat, ev->chld_feat, (bt_bdaddr_t *) ev->bdaddr); } static void handle_audio_state(void *buf, uint16_t len, int fd) { struct hal_ev_hf_client_audio_state *ev = buf; if (cbs->audio_state_cb) cbs->audio_state_cb(ev->state, (bt_bdaddr_t *) (ev->bdaddr)); } static void handle_vr_state(void *buf, uint16_t len, int fd) { struct hal_ev_hf_client_vr_state *ev = buf; if (cbs->vr_cmd_cb) cbs->vr_cmd_cb(ev->state); } static void handle_network_state(void *buf, uint16_t len, int fd) { struct hal_ev_hf_client_net_state *ev = buf; if (cbs->network_state_cb) cbs->network_state_cb(ev->state); } static void handle_network_roaming(void *buf, uint16_t len, int fd) { struct hal_ev_hf_client_net_roaming_type *ev = buf; if (cbs->network_roaming_cb) cbs->network_roaming_cb(ev->state); } static void handle_network_signal(void *buf, uint16_t len, int fd) { struct hal_ev_hf_client_net_signal_strength *ev = buf; if (cbs->network_signal_cb) cbs->network_signal_cb(ev->signal_strength); } static void handle_battery_level(void *buf, uint16_t len, int fd) { struct hal_ev_hf_client_battery_level *ev = buf; if (cbs->battery_level_cb) cbs->battery_level_cb(ev->battery_level); } static void handle_operator_name(void *buf, uint16_t len, int fd) { struct hal_ev_hf_client_operator_name *ev = buf; uint16_t name_len = ev->name_len; char *name = NULL; if (len != sizeof(*ev) + name_len || (name_len != 0 && ev->name[name_len - 1] != '\0')) { error("invalid operator name, aborting"); exit(EXIT_FAILURE); } if (name_len) name = (char *) ev->name; if (cbs->current_operator_cb) cbs->current_operator_cb(name); } static void handle_call(void *buf, uint16_t len, int fd) { struct hal_ev_hf_client_call_indicator *ev = buf; if (cbs->call_cb) cbs->call_cb(ev->call); } static void handle_call_setup(void *buf, uint16_t len, int fd) { struct hal_ev_hf_client_call_setup_indicator *ev = buf; if (cbs->callsetup_cb) cbs->callsetup_cb(ev->call_setup); } static void handle_call_held(void *buf, uint16_t len, int fd) { struct hal_ev_hf_client_call_held_indicator *ev = buf; if (cbs->callheld_cb) cbs->callheld_cb(ev->call_held); } static void handle_response_and_hold(void *buf, uint16_t len, int fd) { struct hal_ev_hf_client_response_and_hold_status *ev = buf; if (cbs->resp_and_hold_cb) cbs->resp_and_hold_cb(ev->status); } static void handle_clip(void *buf, uint16_t len, int fd) { struct hal_ev_hf_client_calling_line_ident *ev = buf; uint16_t num_len = ev->number_len; char *number = NULL; if (len != sizeof(*ev) + num_len || (num_len != 0 && ev->number[num_len - 1] != '\0')) { error("invalid clip, aborting"); exit(EXIT_FAILURE); } if (num_len) number = (char *) ev->number; if (cbs->clip_cb) cbs->clip_cb(number); } static void handle_call_waiting(void *buf, uint16_t len, int fd) { struct hal_ev_hf_client_call_waiting *ev = buf; uint16_t num_len = ev->number_len; char *number = NULL; if (len != sizeof(*ev) + num_len || (num_len != 0 && ev->number[num_len - 1] != '\0')) { error("invalid call waiting, aborting"); exit(EXIT_FAILURE); } if (num_len) number = (char *) ev->number; if (cbs->call_waiting_cb) cbs->call_waiting_cb(number); } static void handle_current_calls(void *buf, uint16_t len, int fd) { struct hal_ev_hf_client_current_call *ev = buf; uint16_t num_len = ev->number_len; char *number = NULL; if (len != sizeof(*ev) + num_len || (num_len != 0 && ev->number[num_len - 1] != '\0')) { error("invalid current calls, aborting"); exit(EXIT_FAILURE); } if (num_len) number = (char *) ev->number; if (cbs->current_calls_cb) cbs->current_calls_cb(ev->index, ev->direction, ev->call_state, ev->multiparty, number); } static void handle_volume_change(void *buf, uint16_t len, int fd) { struct hal_ev_hf_client_volume_changed *ev = buf; if (cbs->volume_change_cb) cbs->volume_change_cb(ev->type, ev->volume); } static void handle_command_cmp(void *buf, uint16_t len, int fd) { struct hal_ev_hf_client_command_complete *ev = buf; if (cbs->cmd_complete_cb) cbs->cmd_complete_cb(ev->type, ev->cme); } static void handle_subscriber_info(void *buf, uint16_t len, int fd) { const struct hal_ev_hf_client_subscriber_service_info *ev = buf; uint16_t name_len = ev->name_len; char *name = NULL; if (len != sizeof(*ev) + name_len || (name_len != 0 && ev->name[name_len - 1] != '\0')) { error("invalid sunscriber info, aborting"); exit(EXIT_FAILURE); } if (name_len) name = (char *) ev->name; if (cbs->subscriber_info_cb) cbs->subscriber_info_cb(name, ev->type); } static void handle_in_band_ringtone(void *buf, uint16_t len, int fd) { struct hal_ev_hf_client_inband_settings *ev = buf; if (cbs->in_band_ring_tone_cb) cbs->in_band_ring_tone_cb(ev->state); } static void handle_last_voice_tag_number(void *buf, uint16_t len, int fd) { const struct hal_ev_hf_client_last_void_call_tag_num *ev = buf; char *number = NULL; uint16_t num_len = ev->number_len; if (len != sizeof(*ev) + num_len || (num_len != 0 && ev->number[num_len - 1] != '\0')) { error("invalid voice tag, aborting"); exit(EXIT_FAILURE); } if (num_len) number = (char *) ev->number; if (cbs->last_voice_tag_number_callback) cbs->last_voice_tag_number_callback(number); } static void handle_ring_indication(void *buf, uint16_t len, int fd) { if (cbs->ring_indication_cb) cbs->ring_indication_cb(); } /* * handlers will be called from notification thread context, * index in table equals to 'opcode - HAL_MINIMUM_EVENT' */ static const struct hal_ipc_handler ev_handlers[] = { /* HAL_EV_HF_CLIENT_CONN_STATE */ { handle_conn_state, false, sizeof(struct hal_ev_hf_client_conn_state) }, /* HAL_EV_HF_CLIENT_AUDIO_STATE */ { handle_audio_state, false, sizeof(struct hal_ev_hf_client_audio_state) }, /* HAL_EV_HF_CLIENT_VR_STATE */ { handle_vr_state, false, sizeof(struct hal_ev_hf_client_vr_state) }, /*HAL_EV_HF_CLIENT_NET_STATE */ { handle_network_state, false, sizeof(struct hal_ev_hf_client_net_state)}, /*HAL_EV_HF_CLIENT_NET_ROAMING_TYPE */ { handle_network_roaming, false, sizeof(struct hal_ev_hf_client_net_roaming_type) }, /* HAL_EV_HF_CLIENT_NET_SIGNAL_STRENGTH */ { handle_network_signal, false, sizeof(struct hal_ev_hf_client_net_signal_strength) }, /* HAL_EV_HF_CLIENT_BATTERY_LEVEL */ { handle_battery_level, false, sizeof(struct hal_ev_hf_client_battery_level) }, /* HAL_EV_HF_CLIENT_OPERATOR_NAME */ { handle_operator_name, true, sizeof(struct hal_ev_hf_client_operator_name) }, /* HAL_EV_HF_CLIENT_CALL_INDICATOR */ { handle_call, false, sizeof(struct hal_ev_hf_client_call_indicator) }, /* HAL_EV_HF_CLIENT_CALL_SETUP_INDICATOR */ { handle_call_setup, false, sizeof(struct hal_ev_hf_client_call_setup_indicator) }, /* HAL_EV_HF_CLIENT_CALL_HELD_INDICATOR */ { handle_call_held, false, sizeof(struct hal_ev_hf_client_call_held_indicator) }, /* HAL_EV_HF_CLIENT_RESPONSE_AND_HOLD_STATUS */ { handle_response_and_hold, false, sizeof(struct hal_ev_hf_client_response_and_hold_status) }, /* HAL_EV_HF_CLIENT_CALLING_LINE_IDENT */ { handle_clip, true, sizeof(struct hal_ev_hf_client_calling_line_ident) }, /* HAL_EV_HF_CLIENT_CALL_WAITING */ { handle_call_waiting, true, sizeof(struct hal_ev_hf_client_call_waiting) }, /* HAL_EV_HF_CLIENT_CURRENT_CALL */ { handle_current_calls, true, sizeof(struct hal_ev_hf_client_current_call) }, /* HAL_EV_CLIENT_VOLUME_CHANGED */ { handle_volume_change, false, sizeof(struct hal_ev_hf_client_volume_changed) }, /* HAL_EV_CLIENT_COMMAND_COMPLETE */ { handle_command_cmp, false, sizeof(struct hal_ev_hf_client_command_complete) }, /* HAL_EV_CLIENT_SUBSCRIBER_SERVICE_INFO */ { handle_subscriber_info, true, sizeof(struct hal_ev_hf_client_subscriber_service_info) }, /* HAL_EV_CLIENT_INBAND_SETTINGS */ { handle_in_band_ringtone, false, sizeof(struct hal_ev_hf_client_inband_settings) }, /* HAL_EV_CLIENT_LAST_VOICE_CALL_TAG_NUM */ { handle_last_voice_tag_number, true, sizeof(struct hal_ev_hf_client_last_void_call_tag_num) }, /* HAL_EV_CLIENT_RING_INDICATION */ { handle_ring_indication, false, 0 }, }; static bt_status_t init(bthf_client_callbacks_t *callbacks) { struct hal_cmd_register_module cmd; int ret; DBG(""); if (interface_ready()) return BT_STATUS_DONE; cbs = callbacks; hal_ipc_register(HAL_SERVICE_ID_HANDSFREE_CLIENT, ev_handlers, sizeof(ev_handlers)/sizeof(ev_handlers[0])); cmd.service_id = HAL_SERVICE_ID_HANDSFREE_CLIENT; cmd.mode = HAL_MODE_DEFAULT; cmd.max_clients = 1; ret = hal_ipc_cmd(HAL_SERVICE_ID_CORE, HAL_OP_REGISTER_MODULE, sizeof(cmd), &cmd, NULL, NULL, NULL); if (ret != BT_STATUS_SUCCESS) { cbs = NULL; hal_ipc_unregister(HAL_SERVICE_ID_HANDSFREE_CLIENT); } return ret; } static bt_status_t hf_client_connect(bt_bdaddr_t *bd_addr) { struct hal_cmd_hf_client_connect cmd; DBG(""); if (!interface_ready()) return BT_STATUS_NOT_READY; if (!bd_addr) return BT_STATUS_PARM_INVALID; memcpy(cmd.bdaddr, bd_addr, sizeof(cmd.bdaddr)); return hal_ipc_cmd(HAL_SERVICE_ID_HANDSFREE_CLIENT, HAL_OP_HF_CLIENT_CONNECT, sizeof(cmd), &cmd, NULL, NULL, NULL); } static bt_status_t disconnect(bt_bdaddr_t *bd_addr) { struct hal_cmd_hf_client_disconnect cmd; DBG(""); if (!interface_ready()) return BT_STATUS_NOT_READY; if (!bd_addr) return BT_STATUS_PARM_INVALID; memcpy(cmd.bdaddr, bd_addr, sizeof(cmd.bdaddr)); return hal_ipc_cmd(HAL_SERVICE_ID_HANDSFREE_CLIENT, HAL_OP_HF_CLIENT_DISCONNECT, sizeof(cmd), &cmd, NULL, NULL, NULL); } static bt_status_t connect_audio(bt_bdaddr_t *bd_addr) { struct hal_cmd_hf_client_connect_audio cmd; DBG(""); if (!interface_ready()) return BT_STATUS_NOT_READY; if (!bd_addr) return BT_STATUS_PARM_INVALID; memcpy(cmd.bdaddr, bd_addr, sizeof(cmd.bdaddr)); return hal_ipc_cmd(HAL_SERVICE_ID_HANDSFREE_CLIENT, HAL_OP_HF_CLIENT_CONNECT_AUDIO, sizeof(cmd), &cmd, NULL, NULL, NULL); } static bt_status_t disconnect_audio(bt_bdaddr_t *bd_addr) { struct hal_cmd_hf_client_disconnect_audio cmd; DBG(""); if (!interface_ready()) return BT_STATUS_NOT_READY; if (!bd_addr) return BT_STATUS_PARM_INVALID; memcpy(cmd.bdaddr, bd_addr, sizeof(cmd.bdaddr)); return hal_ipc_cmd(HAL_SERVICE_ID_HANDSFREE_CLIENT, HAL_OP_HF_CLIENT_DISCONNECT_AUDIO, sizeof(cmd), &cmd, NULL, NULL, NULL); } static bt_status_t start_voice_recognition(void) { DBG(""); if (!interface_ready()) return BT_STATUS_NOT_READY; return hal_ipc_cmd(HAL_SERVICE_ID_HANDSFREE_CLIENT, HAL_OP_HF_CLIENT_START_VR, 0, NULL, NULL, NULL, NULL); } static bt_status_t stop_voice_recognition(void) { DBG(""); if (!interface_ready()) return BT_STATUS_NOT_READY; return hal_ipc_cmd(HAL_SERVICE_ID_HANDSFREE_CLIENT, HAL_OP_HF_CLIENT_STOP_VR, 0, NULL, NULL, NULL, NULL); } static bt_status_t volume_control(bthf_client_volume_type_t type, int volume) { struct hal_cmd_hf_client_volume_control cmd; DBG(""); if (!interface_ready()) return BT_STATUS_NOT_READY; cmd.type = type; cmd.volume = volume; return hal_ipc_cmd(HAL_SERVICE_ID_HANDSFREE_CLIENT, HAL_OP_HF_CLIENT_VOLUME_CONTROL, sizeof(cmd), &cmd, NULL, NULL, NULL); } static bt_status_t dial(const char *number) { char buf[IPC_MTU]; struct hal_cmd_hf_client_dial *cmd = (void *) buf; size_t len; DBG(""); if (!interface_ready()) return BT_STATUS_NOT_READY; if (number) { cmd->number_len = strlen(number) + 1; memcpy(cmd->number, number, cmd->number_len); } else { cmd->number_len = 0; } len = sizeof(*cmd) + cmd->number_len; return hal_ipc_cmd(HAL_SERVICE_ID_HANDSFREE_CLIENT, HAL_OP_HF_CLIENT_DIAL, len, cmd, NULL, NULL, NULL); } static bt_status_t dial_memory(int location) { struct hal_cmd_hf_client_dial_memory cmd; DBG(""); if (!interface_ready()) return BT_STATUS_NOT_READY; cmd.location = location; return hal_ipc_cmd(HAL_SERVICE_ID_HANDSFREE_CLIENT, HAL_OP_HF_CLIENT_DIAL_MEMORY, sizeof(cmd), &cmd, NULL, NULL, NULL); } static bt_status_t call_action(bthf_client_call_action_t action, int index) { struct hal_cmd_hf_client_call_action cmd; DBG(""); if (!interface_ready()) return BT_STATUS_NOT_READY; cmd.action = action; cmd.index = index; return hal_ipc_cmd(HAL_SERVICE_ID_HANDSFREE_CLIENT, HAL_OP_HF_CLIENT_CALL_ACTION, sizeof(cmd), &cmd, NULL, NULL, NULL); } static bt_status_t query_current_calls(void) { DBG(""); if (!interface_ready()) return BT_STATUS_NOT_READY; return hal_ipc_cmd(HAL_SERVICE_ID_HANDSFREE_CLIENT, HAL_OP_HF_CLIENT_QUERY_CURRENT_CALLS, 0, NULL, NULL, NULL, NULL); } static bt_status_t query_operator_name(void) { DBG(""); if (!interface_ready()) return BT_STATUS_NOT_READY; return hal_ipc_cmd(HAL_SERVICE_ID_HANDSFREE_CLIENT, HAL_OP_HF_CLIENT_QUERY_OPERATOR_NAME, 0, NULL, NULL, NULL, NULL); } static bt_status_t retrieve_subsr_info(void) { DBG(""); if (!interface_ready()) return BT_STATUS_NOT_READY; return hal_ipc_cmd(HAL_SERVICE_ID_HANDSFREE_CLIENT, HAL_OP_HF_CLIENT_RETRIEVE_SUBSCR_INFO, 0, NULL, NULL, NULL, NULL); } static bt_status_t send_dtmf(char tone) { struct hal_cmd_hf_client_send_dtmf cmd; DBG(""); if (!interface_ready()) return BT_STATUS_NOT_READY; cmd.tone = tone; return hal_ipc_cmd(HAL_SERVICE_ID_HANDSFREE_CLIENT, HAL_OP_HF_CLIENT_SEND_DTMF, sizeof(cmd), &cmd, NULL, NULL, NULL); } static bt_status_t request_last_voice_tag_number(void) { DBG(""); if (!interface_ready()) return BT_STATUS_NOT_READY; return hal_ipc_cmd(HAL_SERVICE_ID_HANDSFREE_CLIENT, HAL_OP_HF_CLIENT_GET_LAST_VOICE_TAG_NUM, 0, NULL, NULL, NULL, NULL); } static void cleanup(void) { struct hal_cmd_unregister_module cmd; DBG(""); if (!interface_ready()) return; cmd.service_id = HAL_SERVICE_ID_HANDSFREE_CLIENT; hal_ipc_cmd(HAL_SERVICE_ID_CORE, HAL_OP_UNREGISTER_MODULE, sizeof(cmd), &cmd, NULL, NULL, NULL); hal_ipc_unregister(HAL_SERVICE_ID_HANDSFREE_CLIENT); cbs = NULL; } static bthf_client_interface_t iface = { .size = sizeof(iface), .init = init, .connect = hf_client_connect, .disconnect = disconnect, .connect_audio = connect_audio, .disconnect_audio = disconnect_audio, .start_voice_recognition = start_voice_recognition, .stop_voice_recognition = stop_voice_recognition, .volume_control = volume_control, .dial = dial, .dial_memory = dial_memory, .handle_call_action = call_action, .query_current_calls = query_current_calls, .query_current_operator_name = query_operator_name, .retrieve_subscriber_info = retrieve_subsr_info, .send_dtmf = send_dtmf, .request_last_voice_tag_number = request_last_voice_tag_number, .cleanup = cleanup }; bthf_client_interface_t *bt_get_hf_client_interface(void) { return &iface; } bluez-5.82/android/PaxHeaders/tester-a2dp.c0000644000000000000000000000005014015011623015555 xustar0020 atime=1743516864 20 ctime=1743591278 bluez-5.82/android/tester-a2dp.c0000644000000000000000000001636714015011623015253 0ustar00rootroot// SPDX-License-Identifier: Apache-2.0 /* * Copyright (C) 2014 Intel Corporation * */ #define _GNU_SOURCE #include #include "emulator/bthost.h" #include "src/shared/util.h" #include "src/shared/tester.h" #include "src/shared/queue.h" #include "lib/bluetooth.h" #include "android/utils.h" #include "tester-main.h" static struct queue *list; #define req_dsc 0x00, 0x01 #define rsp_dsc 0x02, 0x01, 0x04, 0x08 #define req_get 0x10, 0x02, 0x04 #define rsp_get 0x12, 0x02, 0x01, 0x00, 0x07, 0x06, 0x00, \ 0x00, 0xff, 0xff, 0x02, 0x40 #define req_cfg 0x20, 0x03, 0x04, 0x04, 0x01, 0x00, 0x07, \ 0x06, 0x00, 0x00, 0x21, 0x15, 0x02, \ 0x40 #define rsp_cfg 0x22, 0x03 #define req_open 0x30, 0x06, 0x04 #define rsp_open 0x32, 0x06 #define req_close 0x40, 0x08, 0x04 #define rsp_close 0x42, 0x08 #define req_start 0x40, 0x07, 0x04 #define rsp_start 0x42, 0x07 #define req_suspend 0x50, 0x09, 0x04 #define rsp_suspend 0x52, 0x09 static const struct pdu_set pdus[] = { { raw_pdu(req_dsc), raw_pdu(rsp_dsc) }, { raw_pdu(req_get), raw_pdu(rsp_get) }, { raw_pdu(req_cfg), raw_pdu(rsp_cfg) }, { raw_pdu(req_open), raw_pdu(rsp_open) }, { raw_pdu(req_close), raw_pdu(rsp_close) }, { raw_pdu(req_start), raw_pdu(rsp_start) }, { raw_pdu(req_suspend), raw_pdu(rsp_suspend) }, { end_pdu, end_pdu }, }; static struct emu_l2cap_cid_data cid_data = { .pdu = pdus, }; static void a2dp_connect_request_cb(uint16_t handle, uint16_t cid, void *user_data) { struct emu_l2cap_cid_data *cid_data = user_data; if (cid_data->handle) return; cid_data->handle = handle; cid_data->cid = cid; tester_handle_l2cap_data_exchange(cid_data); } static struct emu_set_l2cap_data l2cap_setup_data = { .psm = 25, .func = a2dp_connect_request_cb, .user_data = &cid_data, }; static void a2dp_connect_action(void) { struct test_data *data = tester_get_data(); const uint8_t *addr = hciemu_get_client_bdaddr(data->hciemu); struct step *step = g_new0(struct step, 1); bt_bdaddr_t bdaddr; cid_data.handle = 0; cid_data.cid = 0; bdaddr2android((const bdaddr_t *) addr, &bdaddr); step->action_status = data->if_a2dp->connect(&bdaddr); schedule_action_verification(step); } static void a2dp_disconnect_action(void) { struct test_data *data = tester_get_data(); const uint8_t *addr = hciemu_get_client_bdaddr(data->hciemu); struct step *step = g_new0(struct step, 1); bt_bdaddr_t bdaddr; bdaddr2android((const bdaddr_t *) addr, &bdaddr); step->action_status = data->if_a2dp->disconnect(&bdaddr); schedule_action_verification(step); } static void audio_resume_action(void) { struct test_data *data = tester_get_data(); struct step *step = g_new0(struct step, 1); int err; err = data->audio->open_output_stream(data->audio, 0, AUDIO_DEVICE_OUT_ALL_A2DP, AUDIO_OUTPUT_FLAG_NONE, NULL, &data->if_stream, NULL); if (err < 0) { step->action_status = BT_STATUS_FAIL; goto done; } /* Write something to force resume */ data->if_stream->write(data->if_stream, &err, sizeof(err)); done: schedule_action_verification(step); } static void audio_suspend_action(void) { struct test_data *data = tester_get_data(); struct step *step = g_new0(struct step, 1); data->if_stream->common.standby(&data->if_stream->common); schedule_action_verification(step); } static struct test_case test_cases[] = { TEST_CASE_BREDRLE("A2DP Init", ACTION_SUCCESS(dummy_action, NULL), ), TEST_CASE_BREDRLE("A2DP Connect - Success", ACTION_SUCCESS(set_default_ssp_request_handler, NULL), ACTION_SUCCESS(bluetooth_enable_action, NULL), CALLBACK_STATE(CB_BT_ADAPTER_STATE_CHANGED, BT_STATE_ON), ACTION_SUCCESS(emu_setup_powered_remote_action, NULL), ACTION_SUCCESS(emu_set_ssp_mode_action, NULL), ACTION_SUCCESS(emu_add_l2cap_server_action, &l2cap_setup_data), ACTION_SUCCESS(a2dp_connect_action, NULL), CALLBACK_AV_CONN_STATE(CB_A2DP_CONN_STATE, BTAV_CONNECTION_STATE_CONNECTING), CALLBACK_AV_CONN_STATE(CB_A2DP_CONN_STATE, BTAV_CONNECTION_STATE_CONNECTED), ACTION_SUCCESS(bluetooth_disable_action, NULL), CALLBACK_AV_CONN_STATE(CB_A2DP_CONN_STATE, BTAV_CONNECTION_STATE_DISCONNECTED), CALLBACK_STATE(CB_BT_ADAPTER_STATE_CHANGED, BT_STATE_OFF), ), TEST_CASE_BREDRLE("A2DP Disconnect - Success", ACTION_SUCCESS(set_default_ssp_request_handler, NULL), ACTION_SUCCESS(bluetooth_enable_action, NULL), CALLBACK_STATE(CB_BT_ADAPTER_STATE_CHANGED, BT_STATE_ON), ACTION_SUCCESS(emu_setup_powered_remote_action, NULL), ACTION_SUCCESS(emu_set_ssp_mode_action, NULL), ACTION_SUCCESS(emu_add_l2cap_server_action, &l2cap_setup_data), ACTION_SUCCESS(a2dp_connect_action, NULL), CALLBACK_AV_CONN_STATE(CB_A2DP_CONN_STATE, BTAV_CONNECTION_STATE_CONNECTING), CALLBACK_AV_CONN_STATE(CB_A2DP_CONN_STATE, BTAV_CONNECTION_STATE_CONNECTED), ACTION_SUCCESS(a2dp_disconnect_action, NULL), CALLBACK_AV_CONN_STATE(CB_A2DP_CONN_STATE, BTAV_CONNECTION_STATE_DISCONNECTING), CALLBACK_AV_CONN_STATE(CB_A2DP_CONN_STATE, BTAV_CONNECTION_STATE_DISCONNECTED), ACTION_SUCCESS(bluetooth_disable_action, NULL), CALLBACK_STATE(CB_BT_ADAPTER_STATE_CHANGED, BT_STATE_OFF), ), TEST_CASE_BREDRLE("A2DP Resume - Success", ACTION_SUCCESS(set_default_ssp_request_handler, NULL), ACTION_SUCCESS(bluetooth_enable_action, NULL), CALLBACK_STATE(CB_BT_ADAPTER_STATE_CHANGED, BT_STATE_ON), ACTION_SUCCESS(emu_setup_powered_remote_action, NULL), ACTION_SUCCESS(emu_set_ssp_mode_action, NULL), ACTION_SUCCESS(emu_add_l2cap_server_action, &l2cap_setup_data), ACTION_SUCCESS(a2dp_connect_action, NULL), CALLBACK_AV_CONN_STATE(CB_A2DP_CONN_STATE, BTAV_CONNECTION_STATE_CONNECTING), CALLBACK_AV_CONN_STATE(CB_A2DP_CONN_STATE, BTAV_CONNECTION_STATE_CONNECTED), ACTION_SUCCESS(audio_resume_action, NULL), CALLBACK_AV_AUDIO_STATE(CB_A2DP_AUDIO_STATE, BTAV_AUDIO_STATE_STARTED), ACTION_SUCCESS(bluetooth_disable_action, NULL), CALLBACK_AV_CONN_STATE(CB_A2DP_CONN_STATE, BTAV_CONNECTION_STATE_DISCONNECTED), CALLBACK_STATE(CB_BT_ADAPTER_STATE_CHANGED, BT_STATE_OFF), ), TEST_CASE_BREDRLE("A2DP Suspend - Success", ACTION_SUCCESS(set_default_ssp_request_handler, NULL), ACTION_SUCCESS(bluetooth_enable_action, NULL), CALLBACK_STATE(CB_BT_ADAPTER_STATE_CHANGED, BT_STATE_ON), ACTION_SUCCESS(emu_setup_powered_remote_action, NULL), ACTION_SUCCESS(emu_set_ssp_mode_action, NULL), ACTION_SUCCESS(emu_add_l2cap_server_action, &l2cap_setup_data), ACTION_SUCCESS(a2dp_connect_action, NULL), CALLBACK_AV_CONN_STATE(CB_A2DP_CONN_STATE, BTAV_CONNECTION_STATE_CONNECTING), CALLBACK_AV_CONN_STATE(CB_A2DP_CONN_STATE, BTAV_CONNECTION_STATE_CONNECTED), ACTION_SUCCESS(audio_resume_action, NULL), CALLBACK_AV_AUDIO_STATE(CB_A2DP_AUDIO_STATE, BTAV_AUDIO_STATE_STARTED), ACTION_SUCCESS(audio_suspend_action, NULL), CALLBACK_AV_AUDIO_STATE(CB_A2DP_AUDIO_STATE, BTAV_AUDIO_STATE_STOPPED), ACTION_SUCCESS(bluetooth_disable_action, NULL), CALLBACK_AV_CONN_STATE(CB_A2DP_CONN_STATE, BTAV_CONNECTION_STATE_DISCONNECTED), CALLBACK_STATE(CB_BT_ADAPTER_STATE_CHANGED, BT_STATE_OFF), ), }; struct queue *get_a2dp_tests(void) { uint16_t i = 0; list = queue_new(); for (; i < sizeof(test_cases) / sizeof(test_cases[0]); ++i) queue_push_tail(list, &test_cases[i]); return list; } void remove_a2dp_tests(void) { queue_destroy(list, NULL); } bluez-5.82/android/PaxHeaders/hal-hidhost.c0000644000000000000000000000005014015011623015627 xustar0020 atime=1743516862 20 ctime=1743591276 bluez-5.82/android/hal-hidhost.c0000644000000000000000000002175114015011623015316 0ustar00rootroot// SPDX-License-Identifier: Apache-2.0 /* * Copyright (C) 2013 Intel Corporation * */ #include #include #include #include #include "hal-log.h" #include "hal.h" #include "hal-msg.h" #include "ipc-common.h" #include "hal-ipc.h" static const bthh_callbacks_t *cbacks; static bool interface_ready(void) { return cbacks != NULL; } static void handle_conn_state(void *buf, uint16_t len, int fd) { struct hal_ev_hidhost_conn_state *ev = buf; if (cbacks->connection_state_cb) cbacks->connection_state_cb((bt_bdaddr_t *) ev->bdaddr, ev->state); } static void handle_info(void *buf, uint16_t len, int fd) { struct hal_ev_hidhost_info *ev = buf; bthh_hid_info_t info; info.attr_mask = ev->attr; info.sub_class = ev->subclass; info.app_id = ev->app_id; info.vendor_id = ev->vendor; info.product_id = ev->product; info.version = ev->version; info.ctry_code = ev->country; info.dl_len = ev->descr_len; memcpy(info.dsc_list, ev->descr, info.dl_len); if (cbacks->hid_info_cb) cbacks->hid_info_cb((bt_bdaddr_t *) ev->bdaddr, info); } static void handle_proto_mode(void *buf, uint16_t len, int fd) { struct hal_ev_hidhost_proto_mode *ev = buf; if (cbacks->protocol_mode_cb) cbacks->protocol_mode_cb((bt_bdaddr_t *) ev->bdaddr, ev->status, ev->mode); } static void handle_idle_time(void *buf, uint16_t len, int fd) { struct hal_ev_hidhost_idle_time *ev = buf; if (cbacks->idle_time_cb) cbacks->idle_time_cb((bt_bdaddr_t *) ev->bdaddr, ev->status, ev->idle_rate); } static void handle_get_report(void *buf, uint16_t len, int fd) { struct hal_ev_hidhost_get_report *ev = buf; if (len != sizeof(*ev) + ev->len) { error("invalid get report event, aborting"); exit(EXIT_FAILURE); } if (cbacks->get_report_cb) cbacks->get_report_cb((bt_bdaddr_t *) ev->bdaddr, ev->status, ev->data, ev->len); } static void handle_virtual_unplug(void *buf, uint16_t len, int fd) { struct hal_ev_hidhost_virtual_unplug *ev = buf; if (cbacks->virtual_unplug_cb) cbacks->virtual_unplug_cb((bt_bdaddr_t *) ev->bdaddr, ev->status); } static void handle_handshake(void *buf, uint16_t len, int fd) { #if ANDROID_VERSION >= PLATFORM_VER(5, 0, 0) struct hal_ev_hidhost_handshake *ev = buf; if (cbacks->handshake_cb) cbacks->handshake_cb((bt_bdaddr_t *) ev->bdaddr, ev->status); #endif } /* * handlers will be called from notification thread context, * index in table equals to 'opcode - HAL_MINIMUM_EVENT' */ static const struct hal_ipc_handler ev_handlers[] = { /* HAL_EV_HIDHOST_CONN_STATE */ { handle_conn_state, false, sizeof(struct hal_ev_hidhost_conn_state) }, /* HAL_EV_HIDHOST_INFO */ { handle_info, false, sizeof(struct hal_ev_hidhost_info) }, /* HAL_EV_HIDHOST_PROTO_MODE */ { handle_proto_mode, false, sizeof(struct hal_ev_hidhost_proto_mode) }, /* HAL_EV_HIDHOST_IDLE_TIME */ { handle_idle_time, false, sizeof(struct hal_ev_hidhost_idle_time) }, /* HAL_EV_HIDHOST_GET_REPORT */ { handle_get_report, true, sizeof(struct hal_ev_hidhost_get_report) }, /* HAL_EV_HIDHOST_VIRTUAL_UNPLUG */ { handle_virtual_unplug, false, sizeof(struct hal_ev_hidhost_virtual_unplug) }, { handle_handshake, false, sizeof(struct hal_ev_hidhost_handshake) }, }; static bt_status_t hidhost_connect(bt_bdaddr_t *bd_addr) { struct hal_cmd_hidhost_connect cmd; DBG(""); if (!interface_ready()) return BT_STATUS_NOT_READY; if (!bd_addr) return BT_STATUS_PARM_INVALID; memcpy(cmd.bdaddr, bd_addr, sizeof(cmd.bdaddr)); return hal_ipc_cmd(HAL_SERVICE_ID_HIDHOST, HAL_OP_HIDHOST_CONNECT, sizeof(cmd), &cmd, NULL, NULL, NULL); } static bt_status_t disconnect(bt_bdaddr_t *bd_addr) { struct hal_cmd_hidhost_disconnect cmd; DBG(""); if (!interface_ready()) return BT_STATUS_NOT_READY; if (!bd_addr) return BT_STATUS_PARM_INVALID; memcpy(cmd.bdaddr, bd_addr, sizeof(cmd.bdaddr)); return hal_ipc_cmd(HAL_SERVICE_ID_HIDHOST, HAL_OP_HIDHOST_DISCONNECT, sizeof(cmd), &cmd, NULL, NULL, NULL); } static bt_status_t virtual_unplug(bt_bdaddr_t *bd_addr) { struct hal_cmd_hidhost_virtual_unplug cmd; DBG(""); if (!interface_ready()) return BT_STATUS_NOT_READY; if (!bd_addr) return BT_STATUS_PARM_INVALID; memcpy(cmd.bdaddr, bd_addr, sizeof(cmd.bdaddr)); return hal_ipc_cmd(HAL_SERVICE_ID_HIDHOST, HAL_OP_HIDHOST_VIRTUAL_UNPLUG, sizeof(cmd), &cmd, NULL, NULL, NULL); } static bt_status_t set_info(bt_bdaddr_t *bd_addr, bthh_hid_info_t hid_info) { struct hal_cmd_hidhost_set_info cmd; DBG(""); if (!interface_ready()) return BT_STATUS_NOT_READY; if (!bd_addr) return BT_STATUS_PARM_INVALID; memcpy(cmd.bdaddr, bd_addr, sizeof(cmd.bdaddr)); cmd.attr = hid_info.attr_mask; cmd.subclass = hid_info.sub_class; cmd.app_id = hid_info.app_id; cmd.vendor = hid_info.vendor_id; cmd.product = hid_info.product_id; cmd.country = hid_info.ctry_code; cmd.descr_len = hid_info.dl_len; memcpy(cmd.descr, hid_info.dsc_list, cmd.descr_len); return hal_ipc_cmd(HAL_SERVICE_ID_HIDHOST, HAL_OP_HIDHOST_SET_INFO, sizeof(cmd), &cmd, NULL, NULL, NULL); } static bt_status_t get_protocol(bt_bdaddr_t *bd_addr, bthh_protocol_mode_t protocol_mode) { struct hal_cmd_hidhost_get_protocol cmd; DBG(""); if (!interface_ready()) return BT_STATUS_NOT_READY; if (!bd_addr) return BT_STATUS_PARM_INVALID; memcpy(cmd.bdaddr, bd_addr, sizeof(cmd.bdaddr)); /* type match IPC type */ cmd.mode = protocol_mode; return hal_ipc_cmd(HAL_SERVICE_ID_HIDHOST, HAL_OP_HIDHOST_GET_PROTOCOL, sizeof(cmd), &cmd, NULL, NULL, NULL); } static bt_status_t set_protocol(bt_bdaddr_t *bd_addr, bthh_protocol_mode_t protocol_mode) { struct hal_cmd_hidhost_set_protocol cmd; DBG(""); if (!interface_ready()) return BT_STATUS_NOT_READY; if (!bd_addr) return BT_STATUS_PARM_INVALID; memcpy(cmd.bdaddr, bd_addr, sizeof(cmd.bdaddr)); /* type match IPC type */ cmd.mode = protocol_mode; return hal_ipc_cmd(HAL_SERVICE_ID_HIDHOST, HAL_OP_HIDHOST_SET_PROTOCOL, sizeof(cmd), &cmd, NULL, NULL, NULL); } static bt_status_t get_report(bt_bdaddr_t *bd_addr, bthh_report_type_t report_type, uint8_t report_id, int buffer_size) { struct hal_cmd_hidhost_get_report cmd; DBG(""); if (!interface_ready()) return BT_STATUS_NOT_READY; if (!bd_addr) return BT_STATUS_PARM_INVALID; memcpy(cmd.bdaddr, bd_addr, sizeof(cmd.bdaddr)); cmd.id = report_id; cmd.buf_size = buffer_size; /* type match IPC type */ cmd.type = report_type; return hal_ipc_cmd(HAL_SERVICE_ID_HIDHOST, HAL_OP_HIDHOST_GET_REPORT, sizeof(cmd), &cmd, NULL, NULL, NULL); } static bt_status_t set_report(bt_bdaddr_t *bd_addr, bthh_report_type_t report_type, char *report) { uint8_t buf[IPC_MTU]; struct hal_cmd_hidhost_set_report *cmd = (void *) buf; DBG(""); if (!interface_ready()) return BT_STATUS_NOT_READY; if (!bd_addr || !report) return BT_STATUS_PARM_INVALID; memcpy(cmd->bdaddr, bd_addr, sizeof(cmd->bdaddr)); cmd->len = strlen(report); memcpy(cmd->data, report, cmd->len); /* type match IPC type */ cmd->type = report_type; return hal_ipc_cmd(HAL_SERVICE_ID_HIDHOST, HAL_OP_HIDHOST_SET_REPORT, sizeof(*cmd) + cmd->len, buf, NULL, NULL, NULL); } static bt_status_t send_data(bt_bdaddr_t *bd_addr, char *data) { uint8_t buf[IPC_MTU]; struct hal_cmd_hidhost_send_data *cmd = (void *) buf; DBG(""); if (!interface_ready()) return BT_STATUS_NOT_READY; if (!bd_addr || !data) return BT_STATUS_PARM_INVALID; memcpy(cmd->bdaddr, bd_addr, sizeof(cmd->bdaddr)); cmd->len = strlen(data); memcpy(cmd->data, data, cmd->len); return hal_ipc_cmd(HAL_SERVICE_ID_HIDHOST, HAL_OP_HIDHOST_SEND_DATA, sizeof(*cmd) + cmd->len, buf, NULL, NULL, NULL); } static bt_status_t init(bthh_callbacks_t *callbacks) { struct hal_cmd_register_module cmd; int ret; DBG(""); if (interface_ready()) return BT_STATUS_DONE; /* store reference to user callbacks */ cbacks = callbacks; hal_ipc_register(HAL_SERVICE_ID_HIDHOST, ev_handlers, sizeof(ev_handlers)/sizeof(ev_handlers[0])); cmd.service_id = HAL_SERVICE_ID_HIDHOST; cmd.mode = HAL_MODE_DEFAULT; cmd.max_clients = 1; ret = hal_ipc_cmd(HAL_SERVICE_ID_CORE, HAL_OP_REGISTER_MODULE, sizeof(cmd), &cmd, NULL, NULL, NULL); if (ret != BT_STATUS_SUCCESS) { cbacks = NULL; hal_ipc_unregister(HAL_SERVICE_ID_HIDHOST); } return ret; } static void cleanup(void) { struct hal_cmd_unregister_module cmd; DBG(""); if (!interface_ready()) return; cmd.service_id = HAL_SERVICE_ID_HIDHOST; hal_ipc_cmd(HAL_SERVICE_ID_CORE, HAL_OP_UNREGISTER_MODULE, sizeof(cmd), &cmd, NULL, NULL, NULL); hal_ipc_unregister(HAL_SERVICE_ID_HIDHOST); cbacks = NULL; } static bthh_interface_t hidhost_if = { .size = sizeof(hidhost_if), .init = init, .connect = hidhost_connect, .disconnect = disconnect, .virtual_unplug = virtual_unplug, .set_info = set_info, .get_protocol = get_protocol, .set_protocol = set_protocol, .get_report = get_report, .set_report = set_report, .send_data = send_data, .cleanup = cleanup }; bthh_interface_t *bt_get_hidhost_interface(void) { return &hidhost_if; } bluez-5.82/android/PaxHeaders/a2dp.c0000644000000000000000000000005014214376353014270 xustar0020 atime=1743516866 20 ctime=1743591278 bluez-5.82/android/a2dp.c0000644000000000000000000011313514214376353013755 0ustar00rootroot// SPDX-License-Identifier: LGPL-2.1-or-later /* * * BlueZ - Bluetooth protocol stack for Linux * * Copyright (C) 2013-2014 Intel Corporation. All rights reserved. * * */ #ifdef HAVE_CONFIG_H #include #endif #include #include #include #include #include #include #include #include "btio/btio.h" #include "lib/bluetooth.h" #include "lib/sdp.h" #include "lib/sdp_lib.h" #include "profiles/audio/a2dp-codecs.h" #include "src/shared/queue.h" #include "src/shared/util.h" #include "src/log.h" #include "hal-msg.h" #include "ipc-common.h" #include "ipc.h" #include "a2dp.h" #include "utils.h" #include "bluetooth.h" #include "avdtp.h" #include "avrcp.h" #include "audio-msg.h" #define SVC_HINT_CAPTURING 0x08 #define IDLE_TIMEOUT 1 #define AUDIO_RETRY_TIMEOUT 2 static GIOChannel *server = NULL; static GSList *devices = NULL; static GSList *endpoints = NULL; static GSList *setups = NULL; static bdaddr_t adapter_addr; static uint32_t record_id = 0; static guint audio_retry_id = 0; static bool audio_retrying = false; static struct ipc *hal_ipc = NULL; static struct ipc *audio_ipc = NULL; static struct queue *lseps = NULL; struct a2dp_preset { void *data; int8_t len; }; struct a2dp_endpoint { uint8_t id; uint8_t codec; struct avdtp_local_sep *sep; struct a2dp_preset *caps; GSList *presets; }; struct a2dp_device { bdaddr_t dst; uint8_t state; GIOChannel *io; struct avdtp *session; guint idle_id; }; struct a2dp_setup { struct a2dp_device *dev; struct a2dp_endpoint *endpoint; struct a2dp_preset *preset; struct avdtp_stream *stream; uint8_t state; }; static int device_cmp(gconstpointer s, gconstpointer user_data) { const struct a2dp_device *dev = s; const bdaddr_t *dst = user_data; return bacmp(&dev->dst, dst); } static void preset_free(void *data) { struct a2dp_preset *preset = data; g_free(preset->data); g_free(preset); } static void unregister_endpoint(void *data) { struct a2dp_endpoint *endpoint = data; if (endpoint->sep) avdtp_unregister_sep(lseps, endpoint->sep); if (endpoint->caps) preset_free(endpoint->caps); g_slist_free_full(endpoint->presets, preset_free); g_free(endpoint); } static void setup_free(void *data) { struct a2dp_setup *setup = data; if (!g_slist_find(setup->endpoint->presets, setup->preset)) preset_free(setup->preset); g_free(setup); } static void setup_remove(struct a2dp_setup *setup) { setups = g_slist_remove(setups, setup); setup_free(setup); } static void setup_remove_all_by_dev(struct a2dp_device *dev) { GSList *l = setups; while (l) { struct a2dp_setup *setup = l->data; GSList *next = g_slist_next(l); if (setup->dev == dev) setup_remove(setup); l = next; } } static void a2dp_device_free(void *data) { struct a2dp_device *dev = data; if (dev->idle_id > 0) g_source_remove(dev->idle_id); if (dev->session) avdtp_unref(dev->session); if (dev->io) { g_io_channel_shutdown(dev->io, FALSE, NULL); g_io_channel_unref(dev->io); } setup_remove_all_by_dev(dev); g_free(dev); } static void a2dp_device_remove(struct a2dp_device *dev) { devices = g_slist_remove(devices, dev); a2dp_device_free(dev); } static struct a2dp_device *a2dp_device_new(const bdaddr_t *dst) { struct a2dp_device *dev; dev = g_new0(struct a2dp_device, 1); bacpy(&dev->dst, dst); devices = g_slist_prepend(devices, dev); return dev; } static bool a2dp_device_connect(struct a2dp_device *dev, BtIOConnect cb) { GError *err = NULL; dev->io = bt_io_connect(cb, dev, NULL, &err, BT_IO_OPT_SOURCE_BDADDR, &adapter_addr, BT_IO_OPT_DEST_BDADDR, &dev->dst, BT_IO_OPT_PSM, AVDTP_PSM, BT_IO_OPT_SEC_LEVEL, BT_IO_SEC_MEDIUM, BT_IO_OPT_INVALID); if (err) { error("%s", err->message); g_error_free(err); return false; } return true; } static void bt_a2dp_notify_state(struct a2dp_device *dev, uint8_t state) { struct hal_ev_a2dp_conn_state ev; char address[18]; if (dev->state == state) return; dev->state = state; ba2str(&dev->dst, address); DBG("device %s state %u", address, state); bdaddr2android(&dev->dst, ev.bdaddr); ev.state = state; ipc_send_notif(hal_ipc, HAL_SERVICE_ID_A2DP, HAL_EV_A2DP_CONN_STATE, sizeof(ev), &ev); if (state != HAL_A2DP_STATE_DISCONNECTED) return; bt_avrcp_disconnect(&dev->dst); a2dp_device_remove(dev); } static void bt_audio_notify_state(struct a2dp_setup *setup, uint8_t state) { struct hal_ev_a2dp_audio_state ev; char address[18]; if (setup->state == state) return; setup->state = state; ba2str(&setup->dev->dst, address); DBG("device %s state %u", address, state); bdaddr2android(&setup->dev->dst, ev.bdaddr); ev.state = state; ipc_send_notif(hal_ipc, HAL_SERVICE_ID_A2DP, HAL_EV_A2DP_AUDIO_STATE, sizeof(ev), &ev); } static void disconnect_cb(void *user_data) { struct a2dp_device *dev = user_data; bt_a2dp_notify_state(dev, HAL_A2DP_STATE_DISCONNECTED); } static int sbc_check_config(void *caps, uint8_t caps_len, void *conf, uint8_t conf_len) { a2dp_sbc_t *cap, *config; if (conf_len != caps_len || conf_len != sizeof(a2dp_sbc_t)) { error("SBC: Invalid configuration size (%u)", conf_len); return -EINVAL; } cap = caps; config = conf; if (!(cap->frequency & config->frequency)) { error("SBC: Unsupported frequency (%u) by endpoint", config->frequency); return -EINVAL; } if (!(cap->channel_mode & config->channel_mode)) { error("SBC: Unsupported channel mode (%u) by endpoint", config->channel_mode); return -EINVAL; } if (!(cap->block_length & config->block_length)) { error("SBC: Unsupported block length (%u) by endpoint", config->block_length); return -EINVAL; } if (!(cap->allocation_method & config->allocation_method)) { error("SBC: Unsupported allocation method (%u) by endpoint", config->block_length); return -EINVAL; } if (config->max_bitpool < cap->min_bitpool) { error("SBC: Invalid maximun bitpool (%u < %u)", config->max_bitpool, cap->min_bitpool); return -EINVAL; } if (config->min_bitpool > cap->max_bitpool) { error("SBC: Invalid minimun bitpool (%u > %u)", config->min_bitpool, cap->min_bitpool); return -EINVAL; } if (config->max_bitpool > cap->max_bitpool) return -ERANGE; if (config->min_bitpool < cap->min_bitpool) return -ERANGE; return 0; } static int aac_check_config(void *caps, uint8_t caps_len, void *conf, uint8_t conf_len) { a2dp_aac_t *cap, *config; if (conf_len != caps_len || conf_len != sizeof(a2dp_aac_t)) { error("AAC: Invalid configuration size (%u)", conf_len); return -EINVAL; } cap = caps; config = conf; if (!(cap->object_type & config->object_type)) { error("AAC: Unsupported object type (%u) by endpoint", config->object_type); return -EINVAL; } if (!(AAC_GET_FREQUENCY(*cap) & AAC_GET_FREQUENCY(*config))) { error("AAC: Unsupported frequency (%u) by endpoint", AAC_GET_FREQUENCY(*config)); return -EINVAL; } if (!(cap->channels & config->channels)) { error("AAC: Unsupported channels (%u) by endpoint", config->channels); return -EINVAL; } /* VBR support in SNK is mandatory but let's make sure we don't try to * have VBR on remote which for some reason does not support it */ if (!cap->vbr && config->vbr) { error("AAC: Unsupported VBR (%u) by endpoint", config->vbr); return -EINVAL; } if (AAC_GET_BITRATE(*cap) < AAC_GET_BITRATE(*config)) return -ERANGE; return 0; } static int aptx_check_config(void *caps, uint8_t caps_len, void *conf, uint8_t conf_len) { a2dp_aptx_t *cap, *config; if (conf_len != caps_len || conf_len != sizeof(a2dp_aptx_t)) { error("APTX: Invalid configuration size (%u)", conf_len); return -EINVAL; } cap = caps; config = conf; if (!(cap->frequency & config->frequency)) { error("APTX: Unsupported frequenct (%u) by endpoint", config->frequency); return -EINVAL; } if (!(cap->channel_mode & config->channel_mode)) { error("APTX: Unsupported channel mode (%u) by endpoint", config->channel_mode); return -EINVAL; } return 0; } static int check_capabilities(struct a2dp_preset *preset, struct avdtp_media_codec_capability *codec, uint8_t codec_len) { a2dp_vendor_codec_t *vndcodec; /* Codec specific */ switch (codec->media_codec_type) { case A2DP_CODEC_SBC: return sbc_check_config(codec->data, codec_len, preset->data, preset->len); case A2DP_CODEC_MPEG24: return aac_check_config(codec->data, codec_len, preset->data, preset->len); case A2DP_CODEC_VENDOR: vndcodec = (void *) codec->data; if (A2DP_GET_VENDOR_ID(*vndcodec) == APTX_VENDOR_ID && A2DP_GET_CODEC_ID(*vndcodec) == APTX_CODEC_ID) return aptx_check_config(codec->data, codec_len, preset->data, preset->len); return -EINVAL; default: return -EINVAL; } } static struct a2dp_preset *sbc_select_range(void *caps, uint8_t caps_len, void *conf, uint8_t conf_len) { struct a2dp_preset *p; a2dp_sbc_t *cap, *config; cap = caps; config = conf; config->min_bitpool = MAX(config->min_bitpool, cap->min_bitpool); config->max_bitpool = MIN(config->max_bitpool, cap->max_bitpool); p = g_new0(struct a2dp_preset, 1); p->len = conf_len; p->data = util_memdup(conf, p->len); return p; } static struct a2dp_preset *aac_select_range(void *caps, uint8_t caps_len, void *conf, uint8_t conf_len) { struct a2dp_preset *p; a2dp_aac_t *cap, *config; uint32_t bitrate; cap = caps; config = conf; bitrate = MIN(AAC_GET_BITRATE(*cap), AAC_GET_BITRATE(*config)); AAC_SET_BITRATE(*config, bitrate); p = g_new0(struct a2dp_preset, 1); p->len = conf_len; p->data = util_memdup(conf, p->len); return p; } static struct a2dp_preset *select_preset_range(struct a2dp_preset *preset, struct avdtp_media_codec_capability *codec, uint8_t codec_len) { /* Codec specific */ switch (codec->media_codec_type) { case A2DP_CODEC_SBC: return sbc_select_range(codec->data, codec_len, preset->data, preset->len); case A2DP_CODEC_MPEG24: return aac_select_range(codec->data, codec_len, preset->data, preset->len); default: return NULL; } } static struct a2dp_preset *select_preset(struct a2dp_endpoint *endpoint, struct avdtp_remote_sep *rsep) { struct avdtp_service_capability *service; struct avdtp_media_codec_capability *codec; GSList *l; uint8_t codec_len; service = avdtp_get_codec(rsep); codec = (struct avdtp_media_codec_capability *) service->data; codec_len = service->length - sizeof(*codec); for (l = endpoint->presets; l; l = g_slist_next(l)) { struct a2dp_preset *preset = l->data; int err; err = check_capabilities(preset, codec, codec_len); if (err == 0) return preset; if (err == -ERANGE) return select_preset_range(preset, codec, codec_len); } return NULL; } static void setup_add(struct a2dp_device *dev, struct a2dp_endpoint *endpoint, struct a2dp_preset *preset, struct avdtp_stream *stream) { struct a2dp_setup *setup; setup = g_new0(struct a2dp_setup, 1); setup->dev = dev; setup->endpoint = endpoint; setup->preset = preset; setup->stream = stream; setups = g_slist_append(setups, setup); if (dev->idle_id > 0) { g_source_remove(dev->idle_id); dev->idle_id = 0; } } static int select_configuration(struct a2dp_device *dev, struct a2dp_endpoint *endpoint, struct avdtp_remote_sep *rsep) { struct a2dp_preset *preset; struct avdtp_stream *stream; struct avdtp_service_capability *service; struct avdtp_media_codec_capability *codec; GSList *caps; int err; preset = select_preset(endpoint, rsep); if (!preset) { error("Unable to select codec preset"); return -EINVAL; } service = avdtp_service_cap_new(AVDTP_MEDIA_TRANSPORT, NULL, 0); caps = g_slist_append(NULL, service); codec = g_malloc0(sizeof(*codec) + preset->len); codec->media_type = AVDTP_MEDIA_TYPE_AUDIO; codec->media_codec_type = endpoint->codec; memcpy(codec->data, preset->data, preset->len); service = avdtp_service_cap_new(AVDTP_MEDIA_CODEC, codec, sizeof(*codec) + preset->len); caps = g_slist_append(caps, service); g_free(codec); err = avdtp_set_configuration(dev->session, rsep, endpoint->sep, caps, &stream); g_slist_free_full(caps, g_free); if (err < 0) { error("avdtp_set_configuration: %s", strerror(-err)); return err; } setup_add(dev, endpoint, preset, stream); return 0; } static void discover_cb(struct avdtp *session, GSList *seps, struct avdtp_error *err, void *user_data) { struct a2dp_device *dev = user_data; struct a2dp_endpoint *endpoint = NULL; struct avdtp_remote_sep *rsep = NULL; GSList *l; for (l = endpoints; l; l = g_slist_next(l)) { endpoint = l->data; rsep = avdtp_find_remote_sep(session, endpoint->sep); if (rsep) break; } if (!rsep) { error("Unable to find matching endpoint"); goto failed; } if (select_configuration(dev, endpoint, rsep) < 0) goto failed; return; failed: avdtp_shutdown(session); } static gboolean idle_timeout(gpointer user_data) { struct a2dp_device *dev = user_data; int err; dev->idle_id = 0; err = avdtp_discover(dev->session, discover_cb, dev); if (err == 0) return FALSE; error("avdtp_discover: %s", strerror(-err)); bt_a2dp_notify_state(dev, HAL_A2DP_STATE_DISCONNECTED); return FALSE; } static void signaling_connect_cb(GIOChannel *chan, GError *err, gpointer user_data) { struct a2dp_device *dev = user_data; struct avdtp *session; uint16_t imtu, omtu; GError *gerr = NULL; int fd; if (err) { bt_a2dp_notify_state(dev, HAL_A2DP_STATE_DISCONNECTED); error("%s", err->message); return; } bt_io_get(chan, &gerr, BT_IO_OPT_IMTU, &imtu, BT_IO_OPT_OMTU, &omtu, BT_IO_OPT_INVALID); if (gerr) { error("%s", gerr->message); g_error_free(gerr); goto failed; } fd = g_io_channel_unix_get_fd(chan); /* FIXME: Add proper version */ session = avdtp_new(fd, imtu, omtu, 0x0100, lseps); if (!session) goto failed; dev->session = session; avdtp_add_disconnect_cb(dev->session, disconnect_cb, dev); /* Proceed to stream setup if initiator */ if (dev->io) { int perr; g_io_channel_unref(dev->io); dev->io = NULL; perr = avdtp_discover(dev->session, discover_cb, dev); if (perr < 0) { error("avdtp_discover: %s", strerror(-perr)); goto failed; } bt_avrcp_connect(&dev->dst); } else /* Init idle timeout to discover */ dev->idle_id = g_timeout_add_seconds(IDLE_TIMEOUT, idle_timeout, dev); return; failed: bt_a2dp_notify_state(dev, HAL_A2DP_STATE_DISCONNECTED); } static void bt_a2dp_connect(const void *buf, uint16_t len) { const struct hal_cmd_a2dp_connect *cmd = buf; struct a2dp_device *dev; uint8_t status; char addr[18]; bdaddr_t dst; GSList *l; DBG(""); android2bdaddr(&cmd->bdaddr, &dst); l = g_slist_find_custom(devices, &dst, device_cmp); if (l) { status = HAL_STATUS_FAILED; goto failed; } dev = a2dp_device_new(&dst); if (!a2dp_device_connect(dev, signaling_connect_cb)) { a2dp_device_remove(dev); status = HAL_STATUS_FAILED; goto failed; } ba2str(&dev->dst, addr); DBG("connecting to %s", addr); bt_a2dp_notify_state(dev, HAL_A2DP_STATE_CONNECTING); status = HAL_STATUS_SUCCESS; failed: ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_A2DP, HAL_OP_A2DP_CONNECT, status); } static void bt_a2dp_disconnect(const void *buf, uint16_t len) { const struct hal_cmd_a2dp_connect *cmd = buf; uint8_t status; struct a2dp_device *dev; GSList *l; bdaddr_t dst; DBG(""); android2bdaddr(&cmd->bdaddr, &dst); l = g_slist_find_custom(devices, &dst, device_cmp); if (!l) { status = HAL_STATUS_FAILED; goto failed; } dev = l->data; status = HAL_STATUS_SUCCESS; if (dev->io) { bt_a2dp_notify_state(dev, HAL_A2DP_STATE_DISCONNECTED); goto failed; } /* Wait AVDTP session to shutdown */ avdtp_shutdown(dev->session); bt_a2dp_notify_state(dev, HAL_A2DP_STATE_DISCONNECTING); failed: ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_A2DP, HAL_OP_A2DP_DISCONNECT, status); } static const struct ipc_handler cmd_handlers[] = { /* HAL_OP_A2DP_CONNECT */ { bt_a2dp_connect, false, sizeof(struct hal_cmd_a2dp_connect) }, /* HAL_OP_A2DP_DISCONNECT */ { bt_a2dp_disconnect, false, sizeof(struct hal_cmd_a2dp_disconnect) }, }; static struct a2dp_setup *find_setup_by_device(struct a2dp_device *dev) { GSList *l; for (l = setups; l; l = g_slist_next(l)) { struct a2dp_setup *setup = l->data; if (setup->dev == dev) return setup; } return NULL; } static void transport_connect_cb(GIOChannel *chan, GError *err, gpointer user_data) { struct a2dp_device *dev = user_data; struct a2dp_setup *setup; uint16_t imtu, omtu; GError *gerr = NULL; int fd; if (err) { error("%s", err->message); return; } setup = find_setup_by_device(dev); if (!setup) { error("Unable to find stream setup"); return; } bt_io_get(chan, &gerr, BT_IO_OPT_IMTU, &imtu, BT_IO_OPT_OMTU, &omtu, BT_IO_OPT_INVALID); if (gerr) { error("%s", gerr->message); g_error_free(gerr); return; } fd = g_io_channel_unix_get_fd(chan); if (!avdtp_stream_set_transport(setup->stream, fd, imtu, omtu)) { error("avdtp_stream_set_transport: failed"); return; } g_io_channel_set_close_on_unref(chan, FALSE); if (dev->io) { g_io_channel_unref(dev->io); dev->io = NULL; } bt_a2dp_notify_state(dev, HAL_A2DP_STATE_CONNECTED); } static void connect_cb(GIOChannel *chan, GError *err, gpointer user_data) { struct a2dp_device *dev; bdaddr_t dst; char address[18]; GError *gerr = NULL; GSList *l; if (err) { error("%s", err->message); return; } bt_io_get(chan, &gerr, BT_IO_OPT_DEST_BDADDR, &dst, BT_IO_OPT_INVALID); if (gerr) { error("%s", gerr->message); g_error_free(gerr); g_io_channel_shutdown(chan, TRUE, NULL); return; } ba2str(&dst, address); DBG("Incoming connection from %s", address); l = g_slist_find_custom(devices, &dst, device_cmp); if (l) { transport_connect_cb(chan, err, l->data); return; } dev = a2dp_device_new(&dst); bt_a2dp_notify_state(dev, HAL_A2DP_STATE_CONNECTING); signaling_connect_cb(chan, err, dev); } static sdp_record_t *a2dp_record(void) { sdp_list_t *svclass_id, *pfseq, *apseq, *root; uuid_t root_uuid, l2cap_uuid, avdtp_uuid, a2dp_uuid; sdp_profile_desc_t profile[1]; sdp_list_t *aproto, *proto[2]; sdp_record_t *record; sdp_data_t *psm, *version, *features; uint16_t lp = AVDTP_UUID; uint16_t a2dp_ver = 0x0103, avdtp_ver = 0x0103, feat = 0x000f; record = sdp_record_alloc(); if (!record) return NULL; sdp_uuid16_create(&root_uuid, PUBLIC_BROWSE_GROUP); root = sdp_list_append(NULL, &root_uuid); sdp_set_browse_groups(record, root); sdp_uuid16_create(&a2dp_uuid, AUDIO_SOURCE_SVCLASS_ID); svclass_id = sdp_list_append(NULL, &a2dp_uuid); sdp_set_service_classes(record, svclass_id); sdp_uuid16_create(&profile[0].uuid, ADVANCED_AUDIO_PROFILE_ID); profile[0].version = a2dp_ver; pfseq = sdp_list_append(NULL, &profile[0]); sdp_set_profile_descs(record, pfseq); sdp_uuid16_create(&l2cap_uuid, L2CAP_UUID); proto[0] = sdp_list_append(NULL, &l2cap_uuid); psm = sdp_data_alloc(SDP_UINT16, &lp); proto[0] = sdp_list_append(proto[0], psm); apseq = sdp_list_append(NULL, proto[0]); sdp_uuid16_create(&avdtp_uuid, AVDTP_UUID); proto[1] = sdp_list_append(NULL, &avdtp_uuid); version = sdp_data_alloc(SDP_UINT16, &avdtp_ver); proto[1] = sdp_list_append(proto[1], version); apseq = sdp_list_append(apseq, proto[1]); aproto = sdp_list_append(NULL, apseq); sdp_set_access_protos(record, aproto); features = sdp_data_alloc(SDP_UINT16, &feat); sdp_attr_add(record, SDP_ATTR_SUPPORTED_FEATURES, features); sdp_set_info_attr(record, "Audio Source", NULL, NULL); sdp_data_free(psm); sdp_data_free(version); sdp_list_free(proto[0], NULL); sdp_list_free(proto[1], NULL); sdp_list_free(apseq, NULL); sdp_list_free(pfseq, NULL); sdp_list_free(aproto, NULL); sdp_list_free(root, NULL); sdp_list_free(svclass_id, NULL); return record; } static gboolean sep_getcap_ind(struct avdtp *session, struct avdtp_local_sep *sep, GSList **caps, uint8_t *err, void *user_data) { struct a2dp_endpoint *endpoint = user_data; struct a2dp_preset *cap = endpoint->caps; struct avdtp_service_capability *service; struct avdtp_media_codec_capability *codec; *caps = NULL; service = avdtp_service_cap_new(AVDTP_MEDIA_TRANSPORT, NULL, 0); *caps = g_slist_append(*caps, service); codec = g_malloc0(sizeof(*codec) + cap->len); codec->media_type = AVDTP_MEDIA_TYPE_AUDIO; codec->media_codec_type = endpoint->codec; memcpy(codec->data, cap->data, cap->len); service = avdtp_service_cap_new(AVDTP_MEDIA_CODEC, codec, sizeof(*codec) + cap->len); *caps = g_slist_append(*caps, service); g_free(codec); return TRUE; } static int check_config(struct a2dp_endpoint *endpoint, struct a2dp_preset *config) { GSList *l; struct a2dp_preset *caps; for (l = endpoint->presets; l; l = g_slist_next(l)) { struct a2dp_preset *preset = l->data; if (preset->len != config->len) continue; if (memcmp(preset->data, config->data, preset->len) == 0) return 0; } caps = endpoint->caps; /* Codec specific */ switch (endpoint->codec) { case A2DP_CODEC_SBC: return sbc_check_config(caps->data, caps->len, config->data, config->len); default: return -EINVAL; } } static struct a2dp_device *find_device_by_session(struct avdtp *session) { GSList *l; for (l = devices; l; l = g_slist_next(l)) { struct a2dp_device *dev = l->data; if (dev->session == session) return dev; } return NULL; } static struct a2dp_setup *find_setup(uint8_t id) { GSList *l; for (l = setups; l; l = g_slist_next(l)) { struct a2dp_setup *setup = l->data; if (setup->endpoint->id == id) return setup; } return NULL; } static void setup_remove_by_id(uint8_t id) { struct a2dp_setup *setup; setup = find_setup(id); if (!setup) { error("Unable to find stream setup for endpoint %u", id); return; } setup_remove(setup); } static gboolean sep_setconf_ind(struct avdtp *session, struct avdtp_local_sep *sep, struct avdtp_stream *stream, GSList *caps, avdtp_set_configuration_cb cb, void *user_data) { struct a2dp_endpoint *endpoint = user_data; struct a2dp_device *dev; struct a2dp_preset *preset = NULL; DBG(""); dev = find_device_by_session(session); if (!dev) { error("Unable to find device for session %p", session); return FALSE; } for (; caps != NULL; caps = g_slist_next(caps)) { struct avdtp_service_capability *cap = caps->data; struct avdtp_media_codec_capability *codec; if (cap->category == AVDTP_DELAY_REPORTING) return FALSE; if (cap->category != AVDTP_MEDIA_CODEC) continue; codec = (struct avdtp_media_codec_capability *) cap->data; if (codec->media_codec_type != endpoint->codec) return FALSE; preset = g_new0(struct a2dp_preset, 1); preset->len = cap->length - sizeof(*codec); preset->data = util_memdup(codec->data, preset->len); if (check_config(endpoint, preset) < 0) { preset_free(preset); return FALSE; } } if (!preset) return FALSE; setup_add(dev, endpoint, preset, stream); cb(session, stream, NULL); return TRUE; } static gboolean sep_open_ind(struct avdtp *session, struct avdtp_local_sep *sep, struct avdtp_stream *stream, uint8_t *err, void *user_data) { struct a2dp_endpoint *endpoint = user_data; struct a2dp_setup *setup; DBG(""); setup = find_setup(endpoint->id); if (!setup) { error("Unable to find stream setup for endpoint %u", endpoint->id); *err = AVDTP_SEP_NOT_IN_USE; return FALSE; } return TRUE; } static gboolean sep_close_ind(struct avdtp *session, struct avdtp_local_sep *sep, struct avdtp_stream *stream, uint8_t *err, void *user_data) { struct a2dp_endpoint *endpoint = user_data; struct a2dp_setup *setup; DBG(""); setup = find_setup(endpoint->id); if (!setup) { error("Unable to find stream setup for endpoint %u", endpoint->id); *err = AVDTP_SEP_NOT_IN_USE; return FALSE; } bt_audio_notify_state(setup, HAL_AUDIO_STOPPED); setup_remove(setup); return TRUE; } static gboolean sep_start_ind(struct avdtp *session, struct avdtp_local_sep *sep, struct avdtp_stream *stream, uint8_t *err, void *user_data) { struct a2dp_endpoint *endpoint = user_data; struct a2dp_setup *setup; DBG(""); setup = find_setup(endpoint->id); if (!setup) { error("Unable to find stream setup for endpoint %u", endpoint->id); *err = AVDTP_SEP_NOT_IN_USE; return FALSE; } bt_audio_notify_state(setup, HAL_AUDIO_STARTED); return TRUE; } static gboolean sep_suspend_ind(struct avdtp *session, struct avdtp_local_sep *sep, struct avdtp_stream *stream, uint8_t *err, void *user_data) { struct a2dp_endpoint *endpoint = user_data; struct a2dp_setup *setup; DBG(""); setup = find_setup(endpoint->id); if (!setup) { error("Unable to find stream setup for endpoint %u", endpoint->id); *err = AVDTP_SEP_NOT_IN_USE; return FALSE; } bt_audio_notify_state(setup, HAL_AUDIO_SUSPEND); return TRUE; } static struct avdtp_sep_ind sep_ind = { .get_capability = sep_getcap_ind, .set_configuration = sep_setconf_ind, .open = sep_open_ind, .close = sep_close_ind, .start = sep_start_ind, .suspend = sep_suspend_ind, }; static void sep_setconf_cfm(struct avdtp *session, struct avdtp_local_sep *sep, struct avdtp_stream *stream, struct avdtp_error *err, void *user_data) { struct a2dp_endpoint *endpoint = user_data; struct a2dp_setup *setup; int ret; DBG(""); setup = find_setup(endpoint->id); if (!setup) { error("Unable to find stream setup for endpoint %u", endpoint->id); return; } if (err) goto failed; ret = avdtp_open(session, stream); if (ret < 0) { error("avdtp_open: %s", strerror(-ret)); goto failed; } return; failed: setup_remove(setup); } static void sep_open_cfm(struct avdtp *session, struct avdtp_local_sep *sep, struct avdtp_stream *stream, struct avdtp_error *err, void *user_data) { struct a2dp_endpoint *endpoint = user_data; struct a2dp_device *dev; DBG(""); if (err) goto failed; dev = find_device_by_session(session); if (!dev) { error("Unable to find device for session"); goto failed; } a2dp_device_connect(dev, transport_connect_cb); return; failed: setup_remove_by_id(endpoint->id); } static void sep_start_cfm(struct avdtp *session, struct avdtp_local_sep *sep, struct avdtp_stream *stream, struct avdtp_error *err, void *user_data) { struct a2dp_endpoint *endpoint = user_data; struct a2dp_setup *setup; DBG(""); if (err) { setup_remove_by_id(endpoint->id); return; } setup = find_setup(endpoint->id); if (!setup) { error("Unable to find stream setup for %u endpoint", endpoint->id); return; } bt_audio_notify_state(setup, HAL_AUDIO_STARTED); } static void sep_suspend_cfm(struct avdtp *session, struct avdtp_local_sep *sep, struct avdtp_stream *stream, struct avdtp_error *err, void *user_data) { struct a2dp_endpoint *endpoint = user_data; struct a2dp_setup *setup; DBG(""); if (err) { setup_remove_by_id(endpoint->id); return; } setup = find_setup(endpoint->id); if (!setup) { error("Unable to find stream setup for %u endpoint", endpoint->id); return; } bt_audio_notify_state(setup, HAL_AUDIO_STOPPED); } static void sep_close_cfm(struct avdtp *session, struct avdtp_local_sep *sep, struct avdtp_stream *stream, struct avdtp_error *err, void *user_data) { struct a2dp_endpoint *endpoint = user_data; struct a2dp_setup *setup; DBG(""); if (err) return; setup = find_setup(endpoint->id); if (!setup) { error("Unable to find stream setup for %u endpoint", endpoint->id); return; } bt_audio_notify_state(setup, HAL_AUDIO_STOPPED); setup_remove(setup); } static void sep_abort_cfm(struct avdtp *session, struct avdtp_local_sep *sep, struct avdtp_stream *stream, struct avdtp_error *err, void *user_data) { struct a2dp_endpoint *endpoint = user_data; DBG(""); if (err) return; setup_remove_by_id(endpoint->id); } static struct avdtp_sep_cfm sep_cfm = { .set_configuration = sep_setconf_cfm, .open = sep_open_cfm, .start = sep_start_cfm, .suspend = sep_suspend_cfm, .close = sep_close_cfm, .abort = sep_abort_cfm, }; static uint8_t register_endpoint(const uint8_t *uuid, uint8_t codec, GSList *presets) { struct a2dp_endpoint *endpoint; /* FIXME: Add proper check for uuid */ endpoint = g_new0(struct a2dp_endpoint, 1); endpoint->id = g_slist_length(endpoints) + 1; endpoint->codec = codec; endpoint->sep = avdtp_register_sep(lseps, AVDTP_SEP_TYPE_SOURCE, AVDTP_MEDIA_TYPE_AUDIO, codec, FALSE, &sep_ind, &sep_cfm, endpoint); endpoint->caps = presets->data; endpoint->presets = g_slist_copy(g_slist_nth(presets, 1)); if (endpoint->codec == A2DP_CODEC_VENDOR) { a2dp_vendor_codec_t *vndcodec = (void *) endpoint->caps->data; avdtp_sep_set_vendor_codec(endpoint->sep, A2DP_GET_VENDOR_ID(*vndcodec), A2DP_GET_CODEC_ID(*vndcodec)); } endpoints = g_slist_append(endpoints, endpoint); return endpoint->id; } static GSList *parse_presets(const struct audio_preset *p, uint8_t count, uint16_t len) { GSList *l = NULL; uint8_t i; for (i = 0; count > i; i++) { const uint8_t *ptr = (const uint8_t *) p; struct a2dp_preset *preset; if (len < sizeof(struct audio_preset)) { DBG("Invalid preset index %u", i); g_slist_free_full(l, preset_free); return NULL; } len -= sizeof(struct audio_preset); if (len == 0 || len < p->len) { DBG("Invalid preset size of %u for index %u", len, i); g_slist_free_full(l, preset_free); return NULL; } preset = g_new0(struct a2dp_preset, 1); preset->len = p->len; preset->data = util_memdup(p->data, preset->len); l = g_slist_append(l, preset); len -= preset->len; ptr += sizeof(*p) + preset->len; p = (const struct audio_preset *) ptr; } return l; } static void bt_audio_open(const void *buf, uint16_t len) { const struct audio_cmd_open *cmd = buf; struct audio_rsp_open rsp; GSList *presets; DBG(""); audio_retrying = false; if (cmd->presets == 0) { error("No audio presets found"); goto failed; } presets = parse_presets(cmd->preset, cmd->presets, len - sizeof(*cmd)); if (!presets) { error("No audio presets found"); goto failed; } rsp.id = register_endpoint(cmd->uuid, cmd->codec, presets); if (rsp.id == 0) { g_slist_free_full(presets, preset_free); error("Unable to register endpoint"); goto failed; } g_slist_free(presets); ipc_send_rsp_full(audio_ipc, AUDIO_SERVICE_ID, AUDIO_OP_OPEN, sizeof(rsp), &rsp, -1); return; failed: ipc_send_rsp(audio_ipc, AUDIO_SERVICE_ID, AUDIO_OP_OPEN, AUDIO_STATUS_FAILED); } static struct a2dp_endpoint *find_endpoint(uint8_t id) { GSList *l; for (l = endpoints; l; l = g_slist_next(l)) { struct a2dp_endpoint *endpoint = l->data; if (endpoint->id == id) return endpoint; } return NULL; } static void bt_audio_close(const void *buf, uint16_t len) { const struct audio_cmd_close *cmd = buf; struct a2dp_endpoint *endpoint; DBG(""); endpoint = find_endpoint(cmd->id); if (!endpoint) { error("Unable to find endpoint %u", cmd->id); ipc_send_rsp(audio_ipc, AUDIO_SERVICE_ID, AUDIO_OP_CLOSE, AUDIO_STATUS_FAILED); return; } endpoints = g_slist_remove(endpoints, endpoint); unregister_endpoint(endpoint); ipc_send_rsp(audio_ipc, AUDIO_SERVICE_ID, AUDIO_OP_CLOSE, AUDIO_STATUS_SUCCESS); } static void bt_stream_open(const void *buf, uint16_t len) { const struct audio_cmd_open_stream *cmd = buf; struct audio_rsp_open_stream *rsp; struct a2dp_setup *setup; int fd; uint16_t omtu; DBG(""); if (cmd->id) setup = find_setup(cmd->id); else setup = setups ? setups->data : NULL; if (!setup) { error("Unable to find stream for endpoint %u", cmd->id); ipc_send_rsp(audio_ipc, AUDIO_SERVICE_ID, AUDIO_OP_OPEN_STREAM, AUDIO_STATUS_FAILED); return; } if (!avdtp_stream_get_transport(setup->stream, &fd, NULL, &omtu, NULL)) { error("avdtp_stream_get_transport: failed"); ipc_send_rsp(audio_ipc, AUDIO_SERVICE_ID, AUDIO_OP_OPEN_STREAM, AUDIO_STATUS_FAILED); return; } len = sizeof(struct audio_rsp_open_stream) + sizeof(struct audio_preset) + setup->preset->len; rsp = g_malloc0(len); rsp->id = setup->endpoint->id; rsp->mtu = omtu; rsp->preset->len = setup->preset->len; memcpy(rsp->preset->data, setup->preset->data, setup->preset->len); ipc_send_rsp_full(audio_ipc, AUDIO_SERVICE_ID, AUDIO_OP_OPEN_STREAM, len, rsp, fd); g_free(rsp); } static void bt_stream_close(const void *buf, uint16_t len) { const struct audio_cmd_close_stream *cmd = buf; struct a2dp_setup *setup; int err; DBG(""); setup = find_setup(cmd->id); if (!setup) { error("Unable to find stream for endpoint %u", cmd->id); goto failed; } err = avdtp_close(setup->dev->session, setup->stream, FALSE); if (err < 0) { error("avdtp_close: %s", strerror(-err)); goto failed; } ipc_send_rsp(audio_ipc, AUDIO_SERVICE_ID, AUDIO_OP_CLOSE_STREAM, AUDIO_STATUS_SUCCESS); return; failed: ipc_send_rsp(audio_ipc, AUDIO_SERVICE_ID, AUDIO_OP_CLOSE_STREAM, AUDIO_STATUS_FAILED); } static void bt_stream_resume(const void *buf, uint16_t len) { const struct audio_cmd_resume_stream *cmd = buf; struct a2dp_setup *setup; int err; DBG(""); setup = find_setup(cmd->id); if (!setup) { error("Unable to find stream for endpoint %u", cmd->id); goto failed; } if (setup->state != HAL_AUDIO_STARTED) { err = avdtp_start(setup->dev->session, setup->stream); if (err < 0) { error("avdtp_start: %s", strerror(-err)); goto failed; } } ipc_send_rsp(audio_ipc, AUDIO_SERVICE_ID, AUDIO_OP_RESUME_STREAM, AUDIO_STATUS_SUCCESS); return; failed: ipc_send_rsp(audio_ipc, AUDIO_SERVICE_ID, AUDIO_OP_RESUME_STREAM, AUDIO_STATUS_FAILED); } static void bt_stream_suspend(const void *buf, uint16_t len) { const struct audio_cmd_suspend_stream *cmd = buf; struct a2dp_setup *setup; int err; DBG(""); setup = find_setup(cmd->id); if (!setup) { error("Unable to find stream for endpoint %u", cmd->id); goto failed; } err = avdtp_suspend(setup->dev->session, setup->stream); if (err < 0) { error("avdtp_suspend: %s", strerror(-err)); goto failed; } ipc_send_rsp(audio_ipc, AUDIO_SERVICE_ID, AUDIO_OP_SUSPEND_STREAM, AUDIO_STATUS_SUCCESS); return; failed: ipc_send_rsp(audio_ipc, AUDIO_SERVICE_ID, AUDIO_OP_SUSPEND_STREAM, AUDIO_STATUS_FAILED); } static const struct ipc_handler audio_handlers[] = { /* AUDIO_OP_OPEN */ { bt_audio_open, true, sizeof(struct audio_cmd_open) }, /* AUDIO_OP_CLOSE */ { bt_audio_close, false, sizeof(struct audio_cmd_close) }, /* AUDIO_OP_OPEN_STREAM */ { bt_stream_open, false, sizeof(struct audio_cmd_open_stream) }, /* AUDIO_OP_CLOSE_STREAM */ { bt_stream_close, false, sizeof(struct audio_cmd_close_stream) }, /* AUDIO_OP_RESUME_STREAM */ { bt_stream_resume, false, sizeof(struct audio_cmd_resume_stream) }, /* AUDIO_OP_SUSPEND_STREAM */ { bt_stream_suspend, false, sizeof(struct audio_cmd_suspend_stream) }, }; static void bt_audio_unregister(void) { DBG(""); if (audio_retry_id > 0) g_source_remove(audio_retry_id); g_slist_free_full(endpoints, unregister_endpoint); endpoints = NULL; g_slist_free_full(setups, setup_free); setups = NULL; ipc_cleanup(audio_ipc); audio_ipc = NULL; queue_destroy(lseps, NULL); } static bool bt_audio_register(ipc_disconnect_cb disconnect) { DBG(""); audio_ipc = ipc_init(BLUEZ_AUDIO_SK_PATH, sizeof(BLUEZ_AUDIO_SK_PATH), AUDIO_SERVICE_ID_MAX, false, disconnect, NULL); if (!audio_ipc) return false; ipc_register(audio_ipc, AUDIO_SERVICE_ID, audio_handlers, G_N_ELEMENTS(audio_handlers)); return true; } static gboolean audio_retry_register(void *data) { ipc_disconnect_cb cb = data; audio_retry_id = 0; audio_retrying = true; bt_audio_register(cb); return FALSE; } static void audio_disconnected(void *data) { GSList *l; bool restart; DBG(""); if (audio_retrying) goto retry; restart = endpoints != NULL ? true : false; bt_audio_unregister(); for (l = devices; l; l = g_slist_next(l)) { struct a2dp_device *dev = l->data; avdtp_shutdown(dev->session); } if (!restart) return; retry: audio_retry_id = g_timeout_add_seconds(AUDIO_RETRY_TIMEOUT, audio_retry_register, audio_disconnected); } bool bt_a2dp_register(struct ipc *ipc, const bdaddr_t *addr, uint8_t mode) { GError *err = NULL; sdp_record_t *rec; DBG(""); bacpy(&adapter_addr, addr); lseps = queue_new(); server = bt_io_listen(connect_cb, NULL, NULL, NULL, &err, BT_IO_OPT_SOURCE_BDADDR, &adapter_addr, BT_IO_OPT_PSM, AVDTP_PSM, BT_IO_OPT_SEC_LEVEL, BT_IO_SEC_MEDIUM, BT_IO_OPT_CENTRAL, true, BT_IO_OPT_INVALID); if (!server) { error("Failed to listen on AVDTP channel: %s", err->message); g_error_free(err); return false; } rec = a2dp_record(); if (!rec) { error("Failed to allocate A2DP record"); goto fail; } if (bt_adapter_add_record(rec, SVC_HINT_CAPTURING) < 0) { error("Failed to register A2DP record"); sdp_record_free(rec); goto fail; } record_id = rec->handle; hal_ipc = ipc; ipc_register(hal_ipc, HAL_SERVICE_ID_A2DP, cmd_handlers, G_N_ELEMENTS(cmd_handlers)); if (bt_audio_register(audio_disconnected)) return true; fail: g_io_channel_shutdown(server, TRUE, NULL); g_io_channel_unref(server); server = NULL; return false; } void bt_a2dp_unregister(void) { DBG(""); g_slist_free_full(setups, setup_free); setups = NULL; g_slist_free_full(endpoints, unregister_endpoint); endpoints = NULL; g_slist_free_full(devices, a2dp_device_free); devices = NULL; ipc_unregister(hal_ipc, HAL_SERVICE_ID_A2DP); hal_ipc = NULL; bt_adapter_remove_record(record_id); record_id = 0; if (server) { g_io_channel_shutdown(server, TRUE, NULL); g_io_channel_unref(server); server = NULL; } if (audio_ipc) { ipc_unregister(audio_ipc, AUDIO_SERVICE_ID); ipc_cleanup(audio_ipc); audio_ipc = NULL; } } bluez-5.82/android/PaxHeaders/pics-gap.txt0000644000000000000000000000005012537515745015550 xustar0020 atime=1743516875 20 ctime=1743591288 bluez-5.82/android/pics-gap.txt0000644000000000000000000011521112537515745015232 0ustar00rootrootGAP PICS for the PTS tool. PTS version: 6.1 * - different than PTS defaults M - mandatory O - optional Device Configuration ------------------------------------------------------------------------------- Parameter Name Selected Description ------------------------------------------------------------------------------- TSPC_GAP_0_1 False (*) BR/EDR (C.1) TSPC_GAP_0_2 False (*) LE (C.2) TSPC_GAP_0_3 True BR/EDR/LE (C.3) ------------------------------------------------------------------------------- C.1: Mandatory if ('End Product' or 'Host Subsystem') and ('BR Host' or 'BR/HS Host') are Supported ('End Product' or 'Host Subsystem' with 'BR' or 'BR/HS Host' CC), otherwise excluded. Optional for 'Component (Tested)' or 'Component (Non-Tested)'. C.2: Mandatory if ('End Product' or 'Host Subsystem') and ('LE Host') are Supported (End Product or Host Subsystem with LE Host CC), otherwise excluded. Optional for 'Component (Tested)' or 'Component (Non-Tested)'. C.3: Mandatory if ('End Product' or 'Host Subsystem') and ('BR/LE Host' or 'BR/HS/LE Host') are Supported (End Product or Host Subsystem with BR/LE or BR/HS/LE Host CC), otherwise excluded. Optional for 'Component (Tested)' or 'Component (Non-tested)'. Note - Only one transport shall be supported. ------------------------------------------------------------------------------- Version Configuration ------------------------------------------------------------------------------- Parameter Name Selected Description ------------------------------------------------------------------------------- TSPC_GAP_0A_1 True Core Specification Addendum 3 (CSA3), GAP Connection Parameters Changes, Authentication and Lost Bond Changes, Private Addressing Changes, Dual Mode Addressing Changes, Adopted 24 July 2012 (C.1) TSPC_GAP_0A_2 True Core Specification Addendum 4 (CSA4) TSPC_GAP_0A_3 True Core Spec version 4.1 (Core v4.1) GAP Connection Parameters Changes, Authentication and Lost Bond Changes, Private Addressing Changes, Dual Mode Addressing Changes, Adopted 03 December 2013 ------------------------------------------------------------------------------- C.1: Mandatory if 'CSA3 Adopted 24 July 2012' is supported, otherwise Excluded. ------------------------------------------------------------------------------- Modes ------------------------------------------------------------------------------- Parameter Name Selected Description ------------------------------------------------------------------------------- TSPC_GAP_1_1 True Non-discoverable mode (C.1) TSPC_GAP_1_2 True Limited-discoverable Mode (O) TSPC_GAP_1_3 True General-discoverable mode (O) TSPC_GAP_1_4 True Non-connectable mode (O) TSPC_GAP_1_5 True Connectable mode (M) TSPC_GAP_1_6 True Non-bondable mode (O) TSPC_GAP_1_7 True Bondable mode (C.2) TSPC_GAP_1_8 False (*) Non-Synchronizable Mode (C.3) TSPC_GAP_1_9 False (*) Synchronizable Mode (C.4) ------------------------------------------------------------------------------- C.1: Mandatory if TSPC_GAP_0_2 is supported, otherwise Optional. C.2: Mandatory if TSPC_GAP_3_5 is supported, otherwise Optional. C.3: Mandatory if TSPC_GAP_0A_2 or TSPC_GAP_0A_3 and is supported, otherwise Excluded. C.4: Optional if TSPC_GAP_0A_2 or later is supported; Mandatory if TSPC_GAP_0A_2 or later and BB 3a/1 (Connectionless Slave Broadcast Transmitter) are supported, otherwise Excluded. ------------------------------------------------------------------------------- Security Aspects ------------------------------------------------------------------------------- Parameter Name Selected Description ------------------------------------------------------------------------------- TSPC_GAP_2_1 True Authentication procedure (C.1) TSPC_GAP_2_2 True Support of LMP-Authentication (M) TSPC_GAP_2_3 True Initiate LMP-Authentication (C.5) TSPC_GAP_2_4 False (*) Security mode 1 (C.2) TSPC_GAP_2_5 True Security mode 2 (O) TSPC_GAP_2_6 False (*) Security mode 3 (C.7) TSPC_GAP_2_7 True Security mode 4 (C.4) TSPC_GAP_2_8 True Support of Authenticated link key (C.6) TSPC_GAP_2_9 True Support of Unauthenticated link key (C.6) TSPC_GAP_2_10 True No security (C.6) TSPC_GAP_2_11 False (*) Secure Connections Only Mode (O) ------------------------------------------------------------------------------- C.1: Mandatory If (TSPC_GAP_2_5 or TSPC_GAP_2_6) is supported, otherwise Optional. Note 1: The Authentication Procedure in item GAP, TSPC_GAP_2_1 is the one described in Fig. 5.1 on page 198 in the GAP Profile Specification and not the LMP-Authenticaion. C.2: Excluded if TSPC_GAP_2_7 is supported, otherwise Optional. C.5: Mandatory If (TSPC_GAP_2_5 or TSPC_GAP_2_6 or TSPC_GAP_2_7) is supported, otherwise Optional. C.4: Mandatory if (Core Spec 2.1 or later) is supported, otherwise Excluded. Note 2. If a Core 2.0 and earlier design claims to support secure communcation it should support either Security mode 2 or 3. Note 3. A Core 2.1 or later device shall always support secure communication in Security Mode 4, and shall use that mode to connect with another Core 2.1 or later device. It shall use Security Mode 2 only for backward compatibility purposes with Core 2.0 and earlier devices. Security Mode 1 is excluded for Core 2.1 or later devices based on condition C.2. C.6: If TSPC_GAP_2_7 is supported then at least one of (TSPC_GAP_2_8 or TSPC_GAP_2_9 or TSPC_GAP_2_10) is Mandatory, otherwise Excluded. C.7: Excluded if TSPC_GAP_2_7 is supported, otherwise Optional. ------------------------------------------------------------------------------- Idle Mode Procedures ------------------------------------------------------------------------------- Parameter Name Selected Description ------------------------------------------------------------------------------- TSPC_GAP_3_1 True Initiation of general inquiry (C.1) TSPC_GAP_3_2 True Initiation of limited inquiry (C.1) TSPC_GAP_3_3 True Initiation of name discover (O) TSPC_GAP_3_4 True Initiation of device discovery (O) TSPC_GAP_3_5 True Initiation of general bonding (O) TSPC_GAP_3_6 True Initiation of dedicated bonding (O) ------------------------------------------------------------------------------- C.1: Mandatory to support at least one of TSPC_GAP_3_1 or TSPC_GAP_3_2 if TSPC_GAP_3_5 is supported, otherwise Optional. ------------------------------------------------------------------------------- Establishment Procedures ------------------------------------------------------------------------------- Parameter Name Selected Description ------------------------------------------------------------------------------- TSPC_GAP_4_1 True Support link establishment as initiator (M) TSPC_GAP_4_2 True Support link establishment as acceptor (M) TSPC_GAP_4_3 True Support channel establishment as initiator (O) TSPC_GAP_4_4 True Support channel establishment as acceptor (M) TSPC_GAP_4_5 True Support connection establishment as initiator (O) TSPC_GAP_4_6 True Support connection establishment as acceptor (O) TSPC_GAP_4_7 True Support synchronization establishment as receiver (O) ------------------------------------------------------------------------------- LE Roles ------------------------------------------------------------------------------- Parameter Name Selected Description ------------------------------------------------------------------------------- TSPC_GAP_5_1 False (*) Broadcaster (C.1) TSPC_GAP_5_2 False (*) Observer (C.1) TSPC_GAP_5_3 True Peripheral (C.1) TSPC_GAP_5_4 True Central (C.1) ------------------------------------------------------------------------------- C.1: It is mandatory to support at least one of the defined roles. Note: 'LE Roles' is applicable for LE-only configurations, but it appears that PTS is checking this precondition also in some BR/EDR/LE tests. ------------------------------------------------------------------------------- Broadcaster Physical Layer ------------------------------------------------------------------------------- Parameter Name Selected Description ------------------------------------------------------------------------------- TSPC_GAP_6_1 False (*) Broadcaster: Transmitter (M) TSPC_GAP_6_2 False (*) Broadcaster: Receiver (O) ------------------------------------------------------------------------------- Broadcaster Link Layer States ------------------------------------------------------------------------------- Parameter Name Selected Description ------------------------------------------------------------------------------- TSPC_GAP_7_1 False (*) Broadcaster: Standby (M) TSPC_GAP_7_2 False (*) Broadcaster: Advertising (M) ------------------------------------------------------------------------------- Broadcaster Link Layer Advertising Event Types ------------------------------------------------------------------------------- Parameter Name Selected Description ------------------------------------------------------------------------------- TSPC_GAP_8_1 False (*) Broadcaster: Non-Connectable Undirected Event (M) TSPC_GAP_8_2 False (*) Broadcaster: Scannable Undirected Event (O) ------------------------------------------------------------------------------- Broadcaster Link Layer Advertising Data Types ------------------------------------------------------------------------------- Parameter Name Selected Description ------------------------------------------------------------------------------- TSPC_GAP_8A_1 False (*) AD Type-Service UUID (O) TSPC_GAP_8A_2 False (*) AD Type-Local Name (O) TSPC_GAP_8A_3 False (*) AD Type-Flags (O) TSPC_GAP_8A_4 False (*) AD Type-Manufacturer Specific Data (O) TSPC_GAP_8A_5 False (*) AD Type-TX Power Level (O) TSPC_GAP_8A_6 False (*) AD Type-Security Manager Out of Band (OOB) (C.1) TSPC_GAP_8A_7 False (*) AD Type-Security manager TK Value (O) TSPC_GAP_8A_8 False (*) AD Type-Slave Connection Interval Range (O) TSPC_GAP_8A_9 False (*) AD Type-Service Solicitation (O) TSPC_GAP_8A_10 False (*) AD Type-Service Data (O) TSPC_GAP_8A_11 False (*) AD Type-Appearance (O) TSPC_GAP_8A_12 False (*) AD Type-Public Target Address (O) TSPC_GAP_8A_13 False (*) AD Type-Random Target Address (O) TSPC_GAP_8A_14 False (*) AD Type-Advertising Interval (O) TSPC_GAP_8A_15 False (*) AD Type-LE Bluetooth Device Address (O) TSPC_GAP_8A_16 False (*) AD Type –LE Role (O) ------------------------------------------------------------------------------- C.1: Optional if TSPC_SM_2_4 (OOB supported) is supported, otherwise Excluded. ------------------------------------------------------------------------------- Broadcaster Connection Modes and Procedures ------------------------------------------------------------------------------- Parameter Name Selected Description ------------------------------------------------------------------------------- TSPC_GAP_9_1 False (*) Broadcaster: Non-Connectable Mode ------------------------------------------------------------------------------- Broadcaster Broadcasting and Observing Features ------------------------------------------------------------------------------- Parameter Name Selected Description ------------------------------------------------------------------------------- TSPC_GAP_10_1 False (*) Broadcaster: Broadcast Mode TSPC_GAP_11_1 False (*) Broadcaster: Privacy Feature v.1.0 TSPC_GAP_11_1A False (*) Broadcaster: Privacy Feature v1.1 (O) TSPC_GAP_11_2 False (*) Broadcaster: Resolvable Private Address Generation Procedure TSPC_GAP_11_3 False (*) Broadcaster: Non-Resolvable Private Address Generation Procedure (O) ------------------------------------------------------------------------------- Observer Physical Layer ------------------------------------------------------------------------------- Parameter Name Selected Description ------------------------------------------------------------------------------- TSPC_GAP_12_1 False (*) Observer: Receiver TSPC_GAP_12_2 False (*) Observer: Transmitter ------------------------------------------------------------------------------- Observer Link Layer States ------------------------------------------------------------------------------- Parameter Name Selected Description ------------------------------------------------------------------------------- TSPC_GAP_13_1 False (*) Observer: Standby TSPC_GAP_13_2 False (*) Observer: Scanning ------------------------------------------------------------------------------- Observer Link Layer Scanning Types ------------------------------------------------------------------------------- Parameter Name Selected Description ------------------------------------------------------------------------------- TSPC_GAP_14_1 False (*) Observer: Passive Scanning TSPC_GAP_14_2 False (*) Observer: Active Scanning ------------------------------------------------------------------------------- Observer Connection Modes and Procedures ------------------------------------------------------------------------------- Parameter Name Selected Description ------------------------------------------------------------------------------- TSPC_GAP_15_1 False (*) Observer: Non-Connectable Mode ------------------------------------------------------------------------------- Observer Broadcasting and Observing Features ------------------------------------------------------------------------------- Parameter Name Selected Description ------------------------------------------------------------------------------- TSPC_GAP_16_1 False (*) Observer: Observation Procedure ------------------------------------------------------------------------------- Observer Privacy Feature ------------------------------------------------------------------------------- Parameter Name Selected Description ------------------------------------------------------------------------------- TSPC_GAP_17_1 False (*) Observer: Privacy Feature v1.0 (O) TSPC_GAP_17_1A False (*) Observer: Privacy Feature v1.1 (O) TSPC_GAP_17_2 False (*) Observer: Non-Resolvable Private Address Generation Procedure (C.1) TSPC_GAP_17_3 False (*) Observer: Resolvable Private Address Resolution Procedure (C.2) TSPC_GAP_17_4 False (*) Observer: Resolvable Private Address Generation Procedure (C.3) ------------------------------------------------------------------------------- C.1: Mandatory if TSPC_GAP_17_1 and TSPC_GAP_14_2 (Active Scanning) are supported and TSPC_GAP_17_4 (Resolvable Private Address Generation Procedure) is Not Supported; Optional if CSA3 or later and TSPC_GAP_17_4 are supported, otherwise Excluded. C.2: Optional if TSPC_GAP_17_1 is supported, otherwise Excluded. C.3: Mandatory if CSA3 or later and TSPC_GAP_17_1 and TSPC_GAP_14_2 (Active Scanning) are supported and TSPC_GAP_17_2 (Non-Resolvable Private Address Generation Procedure) is not supported; Optional if CSA3 or later and TSPC_GAP_17_2 (Non-Resolvable Private Address Generation Procedure) are supported, otherwise Excluded. ------------------------------------------------------------------------------- Peripheral Physical Layer ------------------------------------------------------------------------------- Parameter Name Selected Description ------------------------------------------------------------------------------- TSPC_GAP_18_1 True Peripheral: Transmitter TSPC_GAP_18_2 True Peripheral: Receiver ------------------------------------------------------------------------------- Peripheral Link Layer States ------------------------------------------------------------------------------- Parameter Name Selected Description ------------------------------------------------------------------------------- TSPC_GAP_19_1 True Peripheral: Standby TSPC_GAP_19_2 True Peripheral: Advertising TSPC_GAP_19_3 True Peripheral: Connection, Slave Role ------------------------------------------------------------------------------- Peripheral Link Layer Advertising Event Types ------------------------------------------------------------------------------- Parameter Name Selected Description ------------------------------------------------------------------------------- TSPC_GAP_20_1 True Peripheral: Connectable Undirected Event (C.1) TSPC_GAP_20_2 True Peripheral: Connectable Directed Event (C.2) TSPC_GAP_20_2A True Peripheral: Low Duty Directed Advertising (C.3) TSPC_GAP_20_3 True Peripheral: Non-Connectable Undirected Event TSPC_GAP_20_4 True Peripheral: Scannable Undirected Event ------------------------------------------------------------------------------- Peripheral Link Layer Advertising Data Types ------------------------------------------------------------------------------- Parameter Name Selected Description ------------------------------------------------------------------------------- TSPC_GAP_20A_1 False (*) AD Type-Service UUID (C.1) TSPC_GAP_20A_2 True AD Type-Local Name (C.1) TSPC_GAP_20A_3 True AD Type-Flags (C.2) TSPC_GAP_20A_4 False (*) AD Type-Manufacturer Specific Data (C.1) TSPC_GAP_20A_5 True AD Type-TX Power Level (C.1) TSPC_GAP_20A_6 False (*) AD Type-Security Manager Out of Band (OOB) (C.3) TSPC_GAP_20A_7 False (*) AD Type-Security manager TK Value (C.1) TSPC_GAP_20A_8 False (*) AD Type-Slave Connection Interval Range (C.1) TSPC_GAP_20A_9 False (*) AD Type-Service Solicitation (C.1) TSPC_GAP_20A_10 False (*) AD Type-Service Data (C.1) TSPC_GAP_20A_11 False (*) AD Type –Appearance (C.1) TSPC_GAP_20A_12 False (*) AD Type-Public Target Address (C.1) TSPC_GAP_20A_13 False (*) AD Type-Random Target Address (C.1) TSPC_GAP_20A_14 False (*) AD Type-Advertising Interval (C.1) TSPC_GAP_20A_15 False (*) AD Type-LE Bluetooth Device Address (C.1) TSPC_GAP_20A_16 False (*) AD Type – LE Role (C.1) ------------------------------------------------------------------------------- C.1: Optional if (TSPC_GAP_20_1 or TSPC_GAP_20_3 or TSPC_GAP_20_4) is supported, otherwise Excluded. C.2: Mandatory if TSPC_GAP_22_2 (Limited Discoverable Mode) or TSPC_GAP_22_3 (General Discoverable Mode) is supported, otherwise Optional. C.3: Optional if (TSPC_GAP_20_1 (Connectable Undirected Event) or TSPC_GAP_20_3 (Non-Connectable Undirected Event) or TSPC_GAP_20_4 (Scannable Undirected Event)) and TSPC_SM_2_4 (OOB supported) are supported, otherwise Excluded. ------------------------------------------------------------------------------- Peripheral Link Layer Control Procedures ------------------------------------------------------------------------------- Parameter Name Selected Description ------------------------------------------------------------------------------- TSPC_GAP_21_1 True Peripheral: Connection Update Procedure (M) TSPC_GAP_21_2 True Peripheral: Channel Map Update Procedure (M) TSPC_GAP_21_3 True Peripheral: Encryption Procedure (O) TSPC_GAP_21_4 True Peripheral: Feature Exchange Procedure (M) TSPC_GAP_21_5 True Peripheral: Version Exchange Procedure (M) TSPC_GAP_21_6 True Peripheral: Termination Procedure (M) TSPC_GAP_21_7 True Peripheral: LE Ping Procedure (C.3) TSPC_GAP_21_8 True Peripheral: Slave Initiated Feature Exchange Procedure (C.4) TSPC_GAP_21_9 True Peripheral: Connection Parameter Request Procedure (C.5) ------------------------------------------------------------------------------- Peripheral Discovery Modes and Procedures ------------------------------------------------------------------------------- Parameter Name Selected Description ------------------------------------------------------------------------------- TSPC_GAP_22_1 True Peripheral: Non-Discoverable Mode (C.2) TSPC_GAP_22_2 True Peripheral: Limited Discoverable Mode (C.1) TSPC_GAP_22_3 True Peripheral: General Discoverable Mode (C.1) TSPC_GAP_22_4 True Peripheral: Name Discovery Procedure (C.3) ------------------------------------------------------------------------------- C.1: Optional if (TSPC_GAP_5_3 OR TSPC_GAP_42_2), otherwise Excluded. C.2: Mandatory if (TSPC_GAP_5_3 or TSPC_GAP_42_1) is supported, otherwise Excluded. C.3: Optional if TSPC_GAP_5_3 is supported, otherwise Excluded. ------------------------------------------------------------------------------- Peripheral Connection Modes and Procedures ------------------------------------------------------------------------------- Parameter Name Selected Description ------------------------------------------------------------------------------- TSPC_GAP_23_1 True Peripheral: Non-Connectable Mode (C.1) TSPC_GAP_23_2 True Peripheral: Directed Connectable Mode (O) TSPC_GAP_23_3 True Peripheral: Undirected Connectable Mode (M) TSPC_GAP_23_4 True Peripheral: Connection Parameter Update Procedure (O) TSPC_GAP_23_5 True Peripheral: Terminate Connection Procedure (M) ------------------------------------------------------------------------------- C.1: Mandatory if TSPC_GAP_5_3 (LE Only – Peripheral role) OR TSPC_GAP_42_3 (BR/EDR/LE – Non-Connectable Mode) OR TSPC_GAP_42_4 (BR/EDR/LE – Connectable Mode) is supported, otherwise Excluded. ------------------------------------------------------------------------------- Peripheral Bonding Modes and Procedures ------------------------------------------------------------------------------- Parameter Name Selected Description ------------------------------------------------------------------------------- TSPC_GAP_24_1 True Peripheral: Non-Bondable Mode (M) TSPC_GAP_24_2 True Peripheral: Bondable Mode (C.1) TSPC_GAP_24_3 True Peripheral: Bonding Procedure (C.2) TSPC_GAP_24_4 True Peripheral: Multiple Bonds (C.3) ------------------------------------------------------------------------------- C.1: Optional if TSPC_GAP_5_3 (LE Only – Peripheral role) OR (TSPC_GAP_38_3 (BR/EDR/LE – Peripheral role) AND NOT TSPC_GAP_42_6 (BR.EDR/LE - Bondable Mode)) is supported, Mandatory if TSPC_GAP_42_6 (BR/EDR/LE – Bondable Mode) is supported, otherwise Excluded. C.2: Optional if TSPC_GAP_24_2 (Bondable Mode) is supported, otherwise Excluded ------------------------------------------------------------------------------- Peripheral Security Aspects Features ------------------------------------------------------------------------------- Parameter Name Selected Description ------------------------------------------------------------------------------- TSPC_GAP_25_1 True Peripheral: Security Mode (O) TSPC_GAP_25_2 True Peripheral: Security Mode 2 (O) TSPC_GAP_25_3 True Peripheral: Authentication Procedure (C.2) TSPC_GAP_25_4 True Peripheral: Authorization Procedure (O) TSPC_GAP_25_5 True Peripheral: Connection Data Signing Procedure (O) TSPC_GAP_25_6 True Peripheral: Authenticate Signed Data Procedure (O) TSPC_GAP_25_7 True Peripheral: Authenticated Pairing (LE security mode 1 level 3) (C.1) TSPC_GAP_25_8 True Peripheral: Unauthenticated Pairing (LE security mode 1 level 2) (C.1) ------------------------------------------------------------------------------- C.1: Optional if TSPC_GAP_25_1 is supported, otherwise Excluded. C.2: Mandatory if TSPC_GAP_0A_1 and TSPC_GAP_27_4 are supported, otherwise Optional. ------------------------------------------------------------------------------- Peripheral Privacy Feature ------------------------------------------------------------------------------- Parameter Name Selected Description ------------------------------------------------------------------------------- TSPC_GAP_26_1 False (*) Peripheral: Privacy Feature v1.0 (O) TSPC_GAP_26_1A True Peripheral: Privacy Feature v1.1 (O) TSPC_GAP_26_2 True Peripheral: Non-Resolvable Private Address Generation Procedure (C.1) TSPC_GAP_26_3 True Peripheral: Resolvable Private Address Generation Procedure (C.2) TSPC_GAP_26_4 True Peripheral: Resolvable Private Address Generation Procedure (C.4) ------------------------------------------------------------------------------- C.1: Optional if TSPC_GAP_26_1 is supported, otherwise Excluded. C.2: Mandatory if TSPC_GAP_26_1 is supported, otherwise Excluded. ------------------------------------------------------------------------------- Peripheral GAP Characteristics ------------------------------------------------------------------------------- Parameter Name Selected Description ------------------------------------------------------------------------------- TSPC_GAP_27_1 True Peripheral: Device Name (M) TSPC_GAP_27_2 True Peripheral: Appearance (M) TSPC_GAP_27_3 False (*) Peripheral: Peripheral Privacy Flag (C.1) TSPC_GAP_27_4 False (*) Peripheral: Reconnection Address (C.2) TSPC_GAP_27_5 False (*) Peripheral: Peripheral Preferred Connection Parameters (O) TSPC_GAP_27_6 False (*) Peripheral: Writeable Device Name (O) TSPC_GAP_27_7 False (*) Peripheral: Writeable Appearance (O) TSPC_GAP_27_8 False (*) Peripheral: Writeable Peripheral Privacy Flag (O) ------------------------------------------------------------------------------- C.1: Mandatory if TSPC_GAP_26_1 is supported, otherwise Excluded. C.2: Optional if TSPC_GAP_26_1 and TSPC_GAP_27_3 are supported, otherwise Excluded. ------------------------------------------------------------------------------- Central Physical Layer ------------------------------------------------------------------------------- Parameter Name Selected Description ------------------------------------------------------------------------------- TSPC_GAP_28_1 True Central: Transmitter (M) TSPC_GAP_28_2 True Central: Receiver (M) ------------------------------------------------------------------------------- Central Link Layer States ------------------------------------------------------------------------------- Parameter Name Selected Description ------------------------------------------------------------------------------- TSPC_GAP_29_1 True Central: Standby (M) TSPC_GAP_29_2 True Central: Scanning (M) TSPC_GAP_29_3 True Central: Initiating (M) TSPC_GAP_29_4 True Central: Connection, Master Role (M) ------------------------------------------------------------------------------- Central Link Layer Scanning Types ------------------------------------------------------------------------------- Parameter Name Selected Description ------------------------------------------------------------------------------- TSPC_GAP_30_1 True Central: Passive Scanning (O) TSPC_GAP_30_2 True Central: Active Scanning (C.1) ------------------------------------------------------------------------------- C.1: Mandatory if (TSPC_GAP_5_4 or TSPC_GAP_38_4) is supported. Optional if TSPC_GAP_30_1 and (TSPC_GAP_5_4 OR TSPC_GAP_38_4) is supported, otherwise Excluded. ------------------------------------------------------------------------------- Central Link Layer Control Procedures ------------------------------------------------------------------------------- Parameter Name Selected Description ------------------------------------------------------------------------------- TSPC_GAP_31_1 True Central: Connection Update Procedure (M) TSPC_GAP_31_2 True Central: Channel Map Update Procedure (M) TSPC_GAP_31_3 True Central: Encryption Procedure (O) TSPC_GAP_31_4 True Central: Feature Exchange Procedure (M) TSPC_GAP_31_5 True Central: Version Exchange Procedure (M) TSPC_GAP_31_6 True Central: Termination Procedure (M) TSPC_GAP_31_7 True Central: LE Ping Procedure (C.1) TSPC_GAP_31_8 True Central: Slave Initiated Feature Exchange Procedure (C.2) TSPC_GAP_31_9 True Central: Connection Parameter Request Procedure (C.3) ------------------------------------------------------------------------------- Central Discovery Modes and Procedures ------------------------------------------------------------------------------- Parameter Name Selected Description ------------------------------------------------------------------------------- TSPC_GAP_32_1 True Central: Limited Discovery Procedure (C.2) TSPC_GAP_32_2 True Central: General Discovery Procedure (C.1) TSPC_GAP_32_3 True Central: Name Discovery Procedure (C.3) ------------------------------------------------------------------------------- C.1: Mandatory if (TSPC_GAP_5_4 or TSPC_GAP_40_1) is supported, else Excluded. C.2: Optional if (TSPC_GAP_5_4 or TSPC_GAP_40_2) is supported, otherwise Excluded. C.3: Optional if (TSPC_GAP_5_4 or TSPC_GAP_40_4) is supported, otherwise Excluded. ------------------------------------------------------------------------------- Central Connection Modes and Procedures ------------------------------------------------------------------------------- Parameter Name Selected Description ------------------------------------------------------------------------------- TSPC_GAP_33_1 True Central: Auto Connection Establishment Procedure (C.3) TSPC_GAP_33_2 True Central: General Connection Establishment Procedure (C.1) TSPC_GAP_33_3 True Central: Selective Connection Establishment Procedure (C.3) TSPC_GAP_33_4 True Central: Direct Connection Establishment Procedure (C.2) TSPC_GAP_33_5 True Central: Connection Parameter Update Procedure (C.2) TSPC_GAP_33_6 True Central: Terminate Connection Procedure (C.2) ------------------------------------------------------------------------------- C.1: Mandatory if (TSPC_GAP_5_4 or TSPC_GAP_40_5) and TSPC_GAP_36_1 is supported, otherwise Optional. C.2: Mandatory if (TSPC_GAP_5_4 or TSPC_GAP_40_5) is supported, otherwise Excluded. C.3: Optional if (TSPC_GAP_5_4 or TSPC_GAP_40_5) is supported, otherwise Excluded. ------------------------------------------------------------------------------- Central Bonding Modes and Procedures ------------------------------------------------------------------------------- Parameter Name Selected Description ------------------------------------------------------------------------------- TSPC_GAP_34_1 True Central: Non-Bondable Mode (C.1) TSPC_GAP_34_2 True Central: Bondable Mode (C.2) TSPC_GAP_34_3 True Central: Bonding Procedure (O) ------------------------------------------------------------------------------- C.1: Mandatory if (TSPC_GAP_5_4 or 39/5) is supported, otherwise Excluded. C.2: Optional if (TSPC_GAP_5_4 or 39/6) is supported, otherwise Excluded. ------------------------------------------------------------------------------- Central Security Features ------------------------------------------------------------------------------- Parameter Name Selected Description ------------------------------------------------------------------------------- TSPC_GAP_35_1 True Central: Security Mode 1 (O) TSPC_GAP_35_2 True Central: Security Mode 2 (O) TSPC_GAP_35_3 True Central: Authentication Procedure (O) TSPC_GAP_35_4 True Central: Authorization Procedure (O) TSPC_GAP_35_5 True Central: Connection Data Signing Procedure (O) TSPC_GAP_35_6 True Central: Authenticate Signed Data Procedure (O) TSPC_GAP_35_7 True Central: Authenticated Pairing (LE security mode 1 level 3) (C.1) TSPC_GAP_35_8 True Central: Unauthenticated Pairing (LE security mode 1 level 2) (C.1) ------------------------------------------------------------------------------- C.1: Optional if TSPC_GAP_35_1 is supported, otherwise Excluded. ------------------------------------------------------------------------------- Central Privacy Feature ------------------------------------------------------------------------------- Parameter Name Selected Description ------------------------------------------------------------------------------- TSPC_GAP_36_1 False (*) Central: Privacy Feature v1.0 (C.2) TSPC_GAP_36_1A True Central: Privacy Feature v1.1 (C.4) TSPC_GAP_36_2 True Central: Non-Resolvable Private Address Generation Procedure (C.1) TSPC_GAP_36_3 True Central: Resolvable Private Address Resolution Procedure (C.2) TSPC_GAP_36_4 False (*) Central: Write to Privacy Characteristic (Enable/Disable Privacy) (O) TSPC_GAP_36_5 True Central: Resolvable Private Address Generation Procedure (C.6) ------------------------------------------------------------------------------- C.1: Mandatory if TSPC_GAP_36_1 and TSPC_GAP_30_2 are supported, otherwise Excluded. C.2: Mandatory if TSPC_GAP_36_1 is supported, otherwise Excluded. ------------------------------------------------------------------------------- Central GAP Characteristics ------------------------------------------------------------------------------- Parameter Name Selected Description ------------------------------------------------------------------------------- TSPC_GAP_37_1 True Central: Device Name (M) TSPC_GAP_37_2 True Central: Appearance (M) ------------------------------------------------------------------------------- BR/EDR/LE Roles ------------------------------------------------------------------------------- Parameter Name Selected Description ------------------------------------------------------------------------------- TSPC_GAP_38_1 False (*) BR/EDR/LE: Broadcaster (C.1) TSPC_GAP_38_2 False (*) BR/EDR/LE: Observer (C.1) TSPC_GAP_38_3 True BR/EDR/LE: Peripheral (C.1) TSPC_GAP_38_4 True BR/EDR/LE: Central (C.1) ------------------------------------------------------------------------------- C.1: It is mandatory to support at least one of the defined roles. This table is applicable for BR/EDR/LE configurations. For LE-only configurations, see 'LE Roles' table for role declarations. ------------------------------------------------------------------------------- Central BR/EDR/LE Modes ------------------------------------------------------------------------------- Parameter Name Selected Description ------------------------------------------------------------------------------- TSPC_GAP_39_1 True Central BR/EDR/LE: Non-Discoverable Mode (C.1) TSPC_GAP_39_2 True Central BR/EDR/LE: Discoverable Mode (C.2) TSPC_GAP_39_3 True Central BR/EDR/LE: Non-Connectable Mode (C.3) TSPC_GAP_39_4 True Central BR/EDR/LE: Connectable Mode (M) TSPC_GAP_39_5 True Central BR/EDR/LE: Non-Bondable Mode (C.4) TSPC_GAP_39_6 True Central BR/EDR/LE: Bondable Mode (C.5) ------------------------------------------------------------------------------- C.1: Mandatory if TSPC_GAP_1_1 is supported over BR/EDR, otherwise Excluded. C.2: Mandatory if (TSPC_GAP_1_2 or TSPC_GAP_1_3) is supported over BR/EDR, otherwise Excluded. C.3: Mandatory if TSPC_GAP_1_4 is supported over BR/EDR, otherwise Excluded. C.4: Mandatory if TSPC_GAP_1_6 is supported over BR/EDR, otherwise Excluded. C.5: Mandatory if TSPC_GAP_1_7 is supported over BR/EDR, otherwise Excluded. ------------------------------------------------------------------------------- Central BR/EDR/LE Idle Mode Procedures ------------------------------------------------------------------------------- Parameter Name Selected Description ------------------------------------------------------------------------------- TSPC_GAP_40_1 True Central BR/EDR/LE: General Discovery (C.1) TSPC_GAP_40_2 True Central BR/EDR/LE: Limited Discovery (C.2) TSPC_GAP_40_3 True Central BR/EDR/LE: Device Type Discovery (C.3) TSPC_GAP_40_4 True Central BR/EDR/LE: Name Discovery (C.4) TSPC_GAP_40_5 True Central BR/EDR/LE: Link Establishment (C.5) ------------------------------------------------------------------------------- C.1: Mandatory if TSPC_GAP_3_1 is supported over BR/EDR, otherwise Excluded. C.2: Mandatory if TSPC_GAP_3_2 is supported over BR/EDR, otherwise Excluded. C.3: Mandatory if (TSPC_GAP_3_1 or TSPC_GAP_3_2) is supported over BR/EDR, otherwise Excluded. C.4: Mandatory if TSPC_GAP_3_3 is supported over BR/EDR, otherwise Excluded. C.5: Mandatory if (TSPC_GAP_4_1 or TSPC_GAP_4_2) is supported over BR/EDR, otherwise Excluded. ------------------------------------------------------------------------------- Central BR/EDR/LE Security Aspects ------------------------------------------------------------------------------- Parameter Name Selected Description ------------------------------------------------------------------------------- TSPC_GAP_41_1 True Central BR/EDR/LE: Security Aspects (M) ------------------------------------------------------------------------------- Peripheral BR/EDR/LE Modes ------------------------------------------------------------------------------- Parameter Name Selected Description ------------------------------------------------------------------------------- TSPC_GAP_42_1 True Peripheral BR/EDR/LE: Non-Discoverable Mode (See Spec) TSPC_GAP_42_2 True Peripheral BR/EDR/LE: Discoverable Mode (See Spec) TSPC_GAP_42_3 True Peripheral BR/EDR/LE: Non-Connectable Mode (See Spec) TSPC_GAP_42_4 True Peripheral BR/EDR/LE: Connectable Mode (M) TSPC_GAP_42_5 True Peripheral BR/EDR/LE: Non-Bondable Mode (See Spec) TSPC_GAP_42_6 True Peripheral BR/EDR/LE: Bondable Mode (See Spec) ------------------------------------------------------------------------------- C.1: Mandatory if TSPC_GAP_1_1 is supported over BR/EDR, otherwise Excluded. C.2: Mandatory if (TSPC_GAP_1_2 or TSPC_GAP_1_3) is supported over BR/EDR, otherwise Excluded. C.3: Mandatory if TSPC_GAP_1_4 is supported over BR/EDR, otherwise Excluded. C.4: Mandatory if TSPC_GAP_1_6 is supported over BR/EDR, otherwise Excluded. C.5: Mandatory if TSPC_GAP_1_7 is supported over BR/EDR, otherwise Excluded. ------------------------------------------------------------------------------- Peripheral BR/EDR/LE Security Aspects ------------------------------------------------------------------------------- Parameter Name Selected Description ------------------------------------------------------------------------------- TSPC_GAP_43_1 True Peripheral BR/EDR/LE: Non-Discoverable Mode ------------------------------------------------------------------------------- Central Simultaneous BR/EDR and LE Transports ------------------------------------------------------------------------------- Parameter Name Selected Description ------------------------------------------------------------------------------- TSPC_GAP_44_1 True Central BR/EDR/LE: Simultaneous BR/EDR and LE Transports – BR/EDR Slave to the same device (O) TSPC_GAP_44_2 True Central BR/EDR/LE: Simultaneous BR/EDR and LE Transports – BR/EDR Master to the same device (O) ------------------------------------------------------------------------------- Peripheral Simultaneous BR/EDR and LE Transports ------------------------------------------------------------------------------- Parameter Name Selected Description ------------------------------------------------------------------------------- TSPC_GAP_45_1 True Simultaneous BR/EDR and LE Transports – BR/EDR Slave to the same device (C.1) TSPC_GAP_45_2 True Simultaneous BR/EDR and LE Transports – BR/EDR Master to the same device (C.1) ------------------------------------------------------------------------------- C.1: Optional if ((SUM ICS 31/14 (Core Spec Version 4.1) or SUM ICS 31/15 (Core Spec Version 4.1+HS)) is supported, otherwise Excluded. ------------------------------------------------------------------------------- ------------------------------------------------------------------------------- Parameter Name Selected Description ------------------------------------------------------------------------------- TSPC_GATT_1_1 True GATT Client Role (O) TSPC_GATT_1_2 True GATT Server Role (O) TSPC_SM_1_1 True Master Role (Initiator) TSPC_SM_1_2 True Slave Role (Responder) TSPC_SM_2_4 True OOB supported (O) ------------------------------------------------------------------------------- bluez-5.82/android/PaxHeaders/pics-hfp.txt0000644000000000000000000000005012537515745015556 xustar0020 atime=1743516876 20 ctime=1743591289 bluez-5.82/android/pics-hfp.txt0000644000000000000000000002674312537515745015253 0ustar00rootrootHFP PICS for the PTS tool. PTS version: 6.1 * - different than PTS defaults # - not yet implemented/supported M - mandatory O - optional Version ------------------------------------------------------------------------------- Parameter Name Selected Description ------------------------------------------------------------------------------- TSPC_HFP_0_1 False Version: Hands-Free Profile v1.5 (O.1) TSPC_HFP_0_2 True (*) Version: Hands-Free Profile v1.6 (O.1) TSPC_HFP_0_3 False Version: Hands-Free Profile v1.7 (O.1) ------------------------------------------------------------------------------- O.1: It is mandatory to support only one of the adopted versions. ------------------------------------------------------------------------------- Roles ------------------------------------------------------------------------------- Parameter Name Selected Description ------------------------------------------------------------------------------- TSPC_HFP_1_1 True (*) Role: Audio Gateway (AG) (O.1) TSPC_HFP_1_2 False Role: Hands-Free (HF) (O.1) ------------------------------------------------------------------------------- O.1: It is mandatory to support at least one of the defined roles. ------------------------------------------------------------------------------- Audio Gateway Role ------------------------------------------------------------------------------- Parameter Name Selected Description ------------------------------------------------------------------------------- TSPC_HFP_2_1 True Connection management (M) TSPC_HFP_2_1a True (*) SLC initiation during active ongoing call (O) TSPC_HFP_2_2 True Phone Status Information (M) TSPC_HFP_2_3 True Audio connection handling (M) TSPC_HFP_2_3a False Audio connection establishment independent of a call processing (O) TSPC_HFP_2_3b True (*) eSCO support in Audio Connection (C.10) TSPC_HFP_2_3c True (*) Codec negotiation (C.7) TSPC_HFP_2_4a False Accept an incoming voice call (in-band ring) (C.1) TSPC_HFP_2_4b True (*) Accept an incoming voice call (no in-band ring) (C.1) TSPC_HFP_2_4c False Capability to change the "in-band ring" settings (O) TSPC_HFP_2_5 True (*) Reject an incoming voice call (O) TSPC_HFP_2_6 True Terminate a call (M) TSPC_HFP_2_7 True Audio connection transfer during an ongoing call (M) TSPC_HFP_2_7a True (*) HF-initiated Audio transfer to AG during ongoing call (O) TSPC_HFP_2_8 True Place a call with a phone number supplied by the HF (M) TSPC_HFP_2_9 True Place a call using memory dialing (M) TSPC_HFP_2_10 True Place a call to the last number dialed (M) TSPC_HFP_2_11 True Call waiting notification (M) TSPC_HFP_2_12 True (*) Three Way Calling (O) TSPC_HFP_2_12a True (*) User Busy (AT+CHLD value 0) (C.3) TSPC_HFP_2_12b True (*) Call Hold Handling (AT+CHLD value 1,2) (C.2) TSPC_HFP_2_12c True (*) Three Way Call (AT+CHLD value 3) (C.3) TSPC_HFP_2_12d False Explicit Call Transfer (AT+CHLD value 4) (C.3) TSPC_HFP_2_13 True Calling Line Identification (CLI) (M) TSPC_HFP_2_14 True (*) Echo canceling (EC) and Noise reduction (NR) (O) TSPC_HFP_2_15 True (*) Voice recognition activation (O) TSPC_HFP_2_15a True (*) Initiate voice recognition from AG (C.6) TSPC_HFP_2_15b True (*) Autonomous voice deactivation (C.6) TSPC_HFP_2_16 False Attach a phone number to a voice tag (O) TSPC_HFP_2_17 True Ability to transmit DTMF codes (M) TSPC_HFP_2_18a True (*) Remote audio volume control – speaker (O) TSPC_HFP_2_18b False Remote audio volume control – microphone (O) TSPC_HFP_2_18c True (*) Volume Level Synchronization – speaker and microphone (C.5) TSPC_HFP_2_19 False Response and hold (O) TSPC_HFP_2_20 True Subscriber Number Information (M) TSPC_HFP_2_21a True Enhanced Call Status (C.4) TSPC_HFP_2_21b False Enhanced Call Control (C.3) TSPC_HFP_2_21c True (*) Enhanced Call Status with limited network notification (C.4) TSPC_HFP_2_22 False Support for automatic link loss recovery (O) TSPC_HFP_2_23 True Individual Indicator Activation (C.9) TSPC_HFP_2_24 True (*) Wide Band Speech service (C.8) TSPC_HFP_2_25 False Support roaming function (O) TSPC_HFP_2_26 False HF Indicators (C.11) TSPC_HFP_2_27 False Support CVSD eSCO s4 setting (C.12) ------------------------------------------------------------------------------- C.1: The AG must support one of item TSPC_HFP_2_4a or TSPC_HFP_2_4b C.2: Mandatory if TSPC_HFP_2_12 is TRUE; otherwise excluded C.3: Optional if TSPC_HFP_2_12 is TRUE; otherwise excluded C.4: The AG must support one of item TSPC_HFP_2_21a or TSPC_HFP_2_21c C.5: Mandatory if TSPC_HFP_2_18a or TSPC_HFP_2_18b; otherwise optional C.6: Optional if TSPC_HFP_2_15 is supported, otherwise excluded C.7: Mandatory if TSPC_HFP_2_24 otherwise excluded C.8: Excluded if TSPC_HFP_0_1 otherwise optional C.9: Excluded if TSPC_HFP_0_1 otherwise mandatory C.10: Mandatory if TSPC_HFP_2_27 or TSPC_HFP_2_24 otherwise optional C.11: Optional IF HFP v1.5 (TSPC_HFP_0_1) OR HFP v1.6 (TSPC_HFP_0_2) is NOT supported, otherwise Excluded. C.12: Excluded IF HFP v1.5 (TSPC_HFP_0_1) OR HFP v1.6 (TSPC_HFP_0_2) is supported, otherwise Mandatory. ------------------------------------------------------------------------------- Hands-Free Role ------------------------------------------------------------------------------- Parameter Name Selected Description ------------------------------------------------------------------------------- TSPC_HFP_3_1 False (*) Connection Management (M) TSPC_HFP_3_2a False (*) Phone Status Information ("service" and "call" indicators) (M) TSPC_HFP_3_2b False Phone Status Information ("callsetup" indicators) (O) TSPC_HFP_3_2c False Accept indicator of signal strength (O) TSPC_HFP_3_2d False Accept indicator of roaming state ("roam:") (O) TSPC_HFP_3_2e False Accept indicator of battery level ("battchg") (O) TSPC_HFP_3_2f False Accept indicator of operator selection (O) TSPC_HFP_3_3 False (*) Audio connection handling (M) TSPC_HFP_3_3a False Audio connection establishment independent of call processing (O) TSPC_HFP_3_3b False eSCO support in Audio Connection (C.7) TSPC_HFP_3_3c False Codec negotiation (C.5) TSPC_HFP_3_4a False (*) Accept an incoming voice call (in-band ring) (M) TSPC_HFP_3_4b False (*) Accept an incoming voice call (no in-band ring) (M) TSPC_HFP_3_4c False Accept an incoming voice call (in-band ring muting) (O) TSPC_HFP_3_5 False (*) Reject an incoming voice call (M) TSPC_HFP_3_6 False (*) Terminate a call (M) TSPC_HFP_3_7 False (*) Audio connection transfer during an ongoing call (M) TSPC_HFP_3_7a False HF-initiated Audio transfer to AG during ongoing call (O) TSPC_HFP_3_8 False Place a call with a phone number supplied by the HF (O) TSPC_HFP_3_9 False Place a call using memory dialing (O) TSPC_HFP_3_10 False Place a call to the last number dialed (O) TSPC_HFP_3_11 False Call waiting notification (O) TSPC_HFP_3_12 False Three Way Calling (O) TSPC_HFP_3_12a False Three way calling (AT+CHLD values 0) (C.2) TSPC_HFP_3_12b False Three way calling (AT+CHLD values 1 and 2) (C.1) TSPC_HFP_3_12c False Three way calling (AT+CHLD value 3) (C.2) TSPC_HFP_3_12d False Three way calling (AT+CHLD value 4) (C.2) TSPC_HFP_3_12e False Originate new call with established call in progress (C.2) TSPC_HFP_3_13 False Calling Line Identification (CLI) (O) TSPC_HFP_3_14 False Echo cancelling (EC) and Noise reduction (NR) (O) TSPC_HFP_3_15 False Voice recognition activation/deactivation (O) TSPC_HFP_3_16 False Attach a phone number to a voice tag (O) TSPC_HFP_3_17 False Ability to transmit DTMF codes (O) TSPC_HFP_3_18a False Remote audio volume control – speaker (O) TSPC_HFP_3_18b False Remote audio volume control – microphone (O) TSPC_HFP_3_18c False Volume Level Synchronization – speaker (C.3) TSPC_HFP_3_18d False Volume Level Synchronization – microphone (C.4) TSPC_HFP_3_18e False HF informs AG about local changes of audio volume (O) TSPC_HFP_3_18f False HF informs AG about local changes of microphone gain (O) TSPC_HFP_3_19 False Response and hold (O) TSPC_HFP_3_20 False Subscriber Number Information (O) TSPC_HFP_3_21a False Enhanced Call Status (O) TSPC_HFP_3_21b False Enhanced Call Control (C.2) TSPC_HFP_3_22 False Support for automatic link loss recovery (O) TSPC_HFP_3_23 False (*) Individual Indicator Activation (C.6) TSPC_HFP_3_24 False Wide Band Speech service (C.6) TSPC_HFP_3_25 False HF Indicators (C.8) TSPC_HFP_3_26 False Support CVSD eSCO S4 setting (C.9) ------------------------------------------------------------------------------- C.1: Mandatory if TSPC_HFP_3_12; otherwise excluded C.2: Optional if TSPC_HFP_3_12; otherwise excluded C.3: Mandatory if TSPC_HFP_3_18a or TSPC_HFP_3_18b, otherwise optional C.4: Mandatory if TSPC_HFP_3_18b, otherwise optional C.5: Mandatory if TSPC_HFP_3_24 otherwise excluded C.6: Excluded if TSPC_HFP_0_1 otherwise optional C.7: Mandatory if TSPC_HFP_3_26 or TSPC_HFP_3_24 otherwise optional C.8: Optional IF HFP v1.5 (TSPC_HFP_0_1) OR HFP v1.6 (TSPC_HFP_0_2) is NOT supported, otherwise Excluded. C.9: Excluded IF HFP v1.5 (TSPC_HFP_0_1) OR HFP v1.6 (TSPC_HFP_0_2) is supported, otherwise Mandatory. ------------------------------------------------------------------------------- Audio Coding Requirements ------------------------------------------------------------------------------- Parameter Name Selected Description ------------------------------------------------------------------------------- TSPC_HFP_4_1 True CVSD audio coding over SCO (M) TSPC_HFP_4_2 True (*) mSBC audio coding over eSCO (C.1) TSPC_HFP_4_3 True (*) CVSD audio coding over eSCO (Initiating) (C.2) TSPC_HFP_4_2 True (*) CVSD audio coding over eSCO (Accepting) (C.2) ------------------------------------------------------------------------------- C.1: Mandatory if Wide band speech service is supported TSPC_HFP_2_24 or TSPC_HFP_3_24, otherwise excluded C.2: Mandatory IF TPSC_HFP_2_3b OR TSPC_HFP_3_3b; otherwise Excluded. ------------------------------------------------------------------------------- Supplementary Interoperability Verification ------------------------------------------------------------------------------- Parameter Name Selected Description ------------------------------------------------------------------------------- TSPC_HFP_8_1 True (*) Multiple audio transfers during call – AG and HF initiated (C.1) TSPC_HFP_8_2 True (*) Audio transfer by SLC release during an active call (C.1) TSPC_HFP_8_3 True (*) Audio transfer by powering ON HF (O) TSPC_HFP_8_4 True (*) SLC during SDP response (O) TSPC_HFP_8_5 True (*) Handle dynamic server channel number for HFP service (O) TSPC_HFP_8_6 False HF disallows connections in non-discoverable mode (C.2) TSPC_HFP_8_7 True (*) HF connects to AG during incoming call (O) TSPC_HFP_8_8 True (*) Link loss during incoming call (C.3) TSPC_HFP_8_9 True (*) SLC release during incoming call (C.3) TSPC_HFP_8_10 True (*) Voice Recognition Activation (C.4) TSPC_HFP_8_11 True (*) Place outgoing call by dialing number on the AG (O) TSPC_HFP_8_12 True (*) Active call termination – NO CARRIER signal (C.5) ------------------------------------------------------------------------------- C.1: Optional if TSPC_HFP_2_7a or TSPC_HFP_3_7a is supported, otherwise excluded C.2: Optional if TSPC_HFP_1_2 is supported, otherwise excluded C.3: Optional if TSPC_HFP_1_1 is supported, otherwise excluded C.4: Optional if TSPC_HFP_2_15 or TSPC_HFP_3_15 is supported, otherwise excluded C.5: Optional if TSPC_HFP_2_6 is supported, otherwise excluded ------------------------------------------------------------------------------- bluez-5.82/android/PaxHeaders/sco.c0000644000000000000000000000005014015011623014207 xustar0020 atime=1743516866 20 ctime=1743591279 bluez-5.82/android/sco.c0000644000000000000000000001427414015011623013700 0ustar00rootroot// SPDX-License-Identifier: GPL-2.0-or-later /* * * BlueZ - Bluetooth protocol stack for Linux * * Copyright (C) 2014 Intel Corporation. All rights reserved. * */ #ifdef HAVE_CONFIG_H #include #endif #include #include #include #include #include "lib/bluetooth.h" #include "btio/btio.h" #include "src/log.h" #include "src/shared/util.h" #include "sco.h" struct bt_sco { int ref_count; GIOChannel *server_io; GIOChannel *io; guint watch; bdaddr_t local_addr; bdaddr_t remote_addr; bt_sco_confirm_func_t confirm_cb; bt_sco_conn_func_t connect_cb; bt_sco_disconn_func_t disconnect_cb; }; /* We support only one sco for the moment */ static bool sco_in_use = false; static void clear_remote_address(struct bt_sco *sco) { memset(&sco->remote_addr, 0, sizeof(bdaddr_t)); } static gboolean disconnect_watch(GIOChannel *chan, GIOCondition cond, gpointer user_data) { struct bt_sco *sco = user_data; g_io_channel_shutdown(sco->io, TRUE, NULL); g_io_channel_unref(sco->io); sco->io = NULL; DBG(""); sco->watch = 0; if (sco->disconnect_cb) sco->disconnect_cb(&sco->remote_addr); clear_remote_address(sco); return FALSE; } static void connect_sco_cb(GIOChannel *chan, GError *err, gpointer user_data) { struct bt_sco *sco = user_data; DBG(""); /* Lets unref connecting io */ if (sco->io) { g_io_channel_unref(sco->io); sco->io = NULL; } if (err) { error("sco: Audio connect failed (%s)", err->message); /* * Connect_sco_cb is called only when connect_cb is in place * Therefore it is safe to call it */ sco->connect_cb(SCO_STATUS_ERROR, &sco->remote_addr); clear_remote_address(sco); return; } g_io_channel_set_close_on_unref(chan, TRUE); sco->io = g_io_channel_ref(chan); sco->watch = g_io_add_watch(chan, G_IO_ERR | G_IO_HUP | G_IO_NVAL, disconnect_watch, sco); /* It is safe to call it here */ sco->connect_cb(SCO_STATUS_OK, &sco->remote_addr); } static void confirm_sco_cb(GIOChannel *chan, gpointer user_data) { char address[18]; bdaddr_t bdaddr; GError *err = NULL; struct bt_sco *sco = user_data; uint16_t voice_settings; DBG(""); bt_io_get(chan, &err, BT_IO_OPT_DEST, address, BT_IO_OPT_DEST_BDADDR, &bdaddr, BT_IO_OPT_INVALID); if (err) { error("sco: audio confirm failed (%s)", err->message); g_error_free(err); goto drop; } if (!sco->confirm_cb || !sco->connect_cb) { error("sco: Connect and/or confirm callback not registered "); goto drop; } /* Check if there is SCO */ if (sco->io) { error("sco: SCO is in progress"); goto drop; } if (!sco->confirm_cb(&bdaddr, &voice_settings)) { error("sco: Audio connection from %s rejected", address); goto drop; } bacpy(&sco->remote_addr, &bdaddr); DBG("Incoming SCO connection from %s, voice settings 0x%x", address, voice_settings); err = NULL; bt_io_set(chan, &err, BT_IO_OPT_VOICE, voice_settings, BT_IO_OPT_INVALID); if (err) { error("sco: Could not set voice settings (%s)", err->message); g_error_free(err); goto drop; } if (!bt_io_accept(chan, connect_sco_cb, sco, NULL, NULL)) { error("sco: Failed to accept audio connection"); goto drop; } sco->io = g_io_channel_ref(chan); return; drop: g_io_channel_shutdown(chan, TRUE, NULL); } static bool sco_listen(struct bt_sco *sco) { GError *err = NULL; if (!sco) return false; sco->server_io = bt_io_listen(NULL, confirm_sco_cb, sco, NULL, &err, BT_IO_OPT_SOURCE_BDADDR, &sco->local_addr, BT_IO_OPT_INVALID); if (!sco->server_io) { error("sco: Failed to listen on SCO: %s", err->message); g_error_free(err); return false; } return true; } struct bt_sco *bt_sco_new(const bdaddr_t *local_bdaddr) { struct bt_sco *sco; if (!local_bdaddr) return NULL; /* For now we support only one SCO connection per time */ if (sco_in_use) return NULL; sco = new0(struct bt_sco, 1); if (!sco) return NULL; bacpy(&sco->local_addr, local_bdaddr); if (!sco_listen(sco)) { free(sco); return NULL; } sco_in_use = true; return bt_sco_ref(sco); } struct bt_sco *bt_sco_ref(struct bt_sco *sco) { if (!sco) return NULL; __sync_fetch_and_add(&sco->ref_count, 1); return sco; } static void sco_free(struct bt_sco *sco) { if (sco->server_io) { g_io_channel_shutdown(sco->server_io, TRUE, NULL); g_io_channel_unref(sco->server_io); } if (sco->io) { g_io_channel_shutdown(sco->io, TRUE, NULL); g_io_channel_unref(sco->io); } g_free(sco); sco_in_use = false; } void bt_sco_unref(struct bt_sco *sco) { DBG(""); if (!sco) return; if (__sync_sub_and_fetch(&sco->ref_count, 1)) return; sco_free(sco); } bool bt_sco_connect(struct bt_sco *sco, const bdaddr_t *addr, uint16_t voice_settings) { GIOChannel *io; GError *gerr = NULL; DBG(""); if (!sco || !sco->connect_cb || !addr) { error("sco: Incorrect parameters or missing connect_cb"); return false; } /* Check if we have connection in progress */ if (sco->io) { error("sco: Connection already in progress"); return false; } io = bt_io_connect(connect_sco_cb, sco, NULL, &gerr, BT_IO_OPT_SOURCE_BDADDR, &sco->local_addr, BT_IO_OPT_DEST_BDADDR, addr, BT_IO_OPT_VOICE, voice_settings, BT_IO_OPT_INVALID); if (!io) { error("sco: unable to connect audio: %s", gerr->message); g_error_free(gerr); return false; } sco->io = io; bacpy(&sco->remote_addr, addr); return true; } void bt_sco_disconnect(struct bt_sco *sco) { if (!sco) return; if (sco->io) g_io_channel_shutdown(sco->io, TRUE, NULL); } bool bt_sco_get_fd_and_mtu(struct bt_sco *sco, int *fd, uint16_t *mtu) { GError *err; if (!sco->io || !fd || !mtu) return false; err = NULL; if (!bt_io_get(sco->io, &err, BT_IO_OPT_MTU, mtu, BT_IO_OPT_INVALID)) { error("Unable to get MTU: %s\n", err->message); g_clear_error(&err); return false; } *fd = g_io_channel_unix_get_fd(sco->io); return true; } void bt_sco_set_confirm_cb(struct bt_sco *sco, bt_sco_confirm_func_t func) { sco->confirm_cb = func; } void bt_sco_set_connect_cb(struct bt_sco *sco, bt_sco_conn_func_t func) { sco->connect_cb = func; } void bt_sco_set_disconnect_cb(struct bt_sco *sco, bt_sco_disconn_func_t func) { sco->disconnect_cb = func; } bluez-5.82/android/PaxHeaders/pixit-mps.txt0000644000000000000000000000005012537515745015777 xustar0020 atime=1743516876 20 ctime=1743591289 bluez-5.82/android/pixit-mps.txt0000644000000000000000000000316612537515745015466 0ustar00rootrootMPS PIXIT for the PTS tool. PTS version: 6.1 * - different than PTS defaults & - should be set to IUT Bluetooth address ^ - should be set accordingly # - should be set according to the reported phone number's type Required PIXIT settings ------------------------------------------------------------------------------- Parameter Name Value ------------------------------------------------------------------------------- TSPX_avrcp_revision 1.5 (^) TSPX_class_of_device 20050C TSPX_establish_avdtp_stream TRUE TSPX_iut_establishes_initial_condition FALSE TSPX_tester_device A (*) TSPX_media_directory TSPX_bd_addr_iut 112233445566 (*&) TSPX_delete_link_key FALSE TSPX_pin_code 0000 TSPX_security_enabled FALSE TSPX_time_guard 300000 TSPX_use_implicit_send TRUE TSPX_auth_password 0000 TSPX_auth_user_id PTS TSPX_l2cap_psm 1003 TSPX_rfcomm_channel 8 TSPX_no_confirmations FALSE TSPX_AG_match_tester_BRSF_codec_negotiation_bit FALSE TSPX_network_supports_correct_call_and_callstatus TRUE TSPX_no_fail_verdicts FALSE TSPX_packet_type_sco 00A0 TSPX_phone_number 1234567 (^) TSPX_phone_number_memory 1 TSPX_phone_number_memory_invalid_index 9999 TSPX_phone_number_type 145 (*#) TSPX_scan_all_memory_dial_locations FALSE TSPX_second_phone_number 1234567 (^) TSPX_second_phone_type 129 TSPX_secure_simple_pairing_pass_key_confirmation FALSE TSPX_server_channel_iut 00 TSPX_server_channel_tester 01 TSPX_verbose_implicit_send FALSE TSPX_verify_CLIP_information TRUE ------------------------------------------------------------------------------- bluez-5.82/android/PaxHeaders/pics-avdtp.txt0000644000000000000000000000005012537515745016117 xustar0020 atime=1743516876 20 ctime=1743591289 bluez-5.82/android/pics-avdtp.txt0000644000000000000000000002603012537515745015601 0ustar00rootrootAVDTP PICS for the PTS tool. PTS version: 6.1 * - different than PTS defaults # - not yet implemented/supported M - mandatory if such role selected O - optional Versions ------------------------------------------------------------------------------- Parameter Name Selected Description ------------------------------------------------------------------------------- TSPC_AVDTP_0_1 False AVDTP 1.0 (C.1) TSPC_AVDTP_0_2 False AVDTP 1.2 (C.1) TSPC_AVDTP_0_3 True (*) AVDTP 1.3 (C.1) ------------------------------------------------------------------------------- C.1: It is mandatory to select only one of the protocol versions. ------------------------------------------------------------------------------- Roles ------------------------------------------------------------------------------- Parameter Name Selected Description ------------------------------------------------------------------------------- TSPC_AVDTP_1_1 True (*) Source (C.1) TSPC_AVDTP_1_2 True (*) Sink (C.1) TSPC_AVDTP_1_3 True (*) Initiator (C.2) TSPC_AVDTP_1_4 True (*) Acceptor (C.2) ------------------------------------------------------------------------------- C.1: It is mandatory to support at least one of the defined roles. C.2: It is within the scope of profiles using the AVDTP specification to mandate Initiator/Acceptor capabilities. It is mandatory to support at least one of the defined roles. ------------------------------------------------------------------------------- Signaling Message Format (Initiator) ------------------------------------------------------------------------------- Parameter Name Selected Description ------------------------------------------------------------------------------- TSPC_AVDTP_2_1 True Transaction label (M) TSPC_AVDTP_2_2 True Packet type (M) TSPC_AVDTP_2_3 True Message type (M) TSPC_AVDTP_2_4 True Signal identifier (M) ------------------------------------------------------------------------------- Signaling Channel Establishment/Disconnection (Initiator) ------------------------------------------------------------------------------- Parameter Name Selected Description ------------------------------------------------------------------------------- TSPC_AVDTP_3_1 True (*) Establish signaling channel (O) TSPC_AVDTP_3_2 True (*) Disconnect signaling channel (O) ------------------------------------------------------------------------------- Stream Discovery and Configuration (Initiator) ------------------------------------------------------------------------------- Parameter Name Selected Description ------------------------------------------------------------------------------- TSPC_AVDTP_4_1 True (*) Stream discover command (O) TSPC_AVDTP_4_2 True (*) Stream get capabilities command (C.2) TSPC_AVDTP_4_3 True (*) Set configuration command (O) TSPC_AVDTP_4_4 True (*) Get configuration command (O) TSPC_AVDTP_4_5 False Reconfigure command (O) TSPC_AVDTP_4_6 True (*) Stream get all capabilities command (C.1) ------------------------------------------------------------------------------- C.1: It is optional to support if TSPC_AVDTP_0_3 is supported, otherwise excluded. C.2: Mandatory to support if TSPC_AVDTP_4_6 is supported, otherwise Optional. ------------------------------------------------------------------------------- Stream Establishment, Suspension and Release (Initiator) ------------------------------------------------------------------------------- Parameter Name Selected Description ------------------------------------------------------------------------------- TSPC_AVDTP_5_1 True (*) Open stream command (O) TSPC_AVDTP_5_2 True (*) Start stream command (O) TSPC_AVDTP_5_3 True (*) Close stream command (O) TSPC_AVDTP_5_4 True (*) Suspend command (O) TSPC_AVDTP_5_5 True (*) Abort stream command (O) ------------------------------------------------------------------------------- Security Signaling (Initiator) ------------------------------------------------------------------------------- Parameter Name Selected Description ------------------------------------------------------------------------------- TSPC_AVDTP_6_1 False Content security control command (O) ------------------------------------------------------------------------------- Message Fragmentation (Initiator) ------------------------------------------------------------------------------- Parameter Name Selected Description ------------------------------------------------------------------------------- TSPC_AVDTP_7_1 True Signaling message fragmentation (M) ------------------------------------------------------------------------------- Signaling Message Format (Acceptor) ------------------------------------------------------------------------------- Parameter Name Selected Description ------------------------------------------------------------------------------- TSPC_AVDTP_8_1 True Transaction label (M) TSPC_AVDTP_8_2 True Packet type (M) TSPC_AVDTP_8_3 True Message type (M) TSPC_AVDTP_8_4 True Signal identifier (M) ------------------------------------------------------------------------------- Signaling Channel Establishment/Disconnection (Acceptor) ------------------------------------------------------------------------------- Parameter Name Selected Description ------------------------------------------------------------------------------- TSPC_AVDTP_9_1 True (*) Establish signaling channel (O) TSPC_AVDTP_9_2 True (*) Disconnect signaling channel (O) ------------------------------------------------------------------------------- Stream Discovery and Configuration (Acceptor) ------------------------------------------------------------------------------- Parameter Name Selected Description ------------------------------------------------------------------------------- TSPC_AVDTP_10_1 True (*) Stream discover response (O) TSPC_AVDTP_10_2 True (*) Stream get capabilities response (C.2) TSPC_AVDTP_10_3 True (*) Set configuration response (O) TSPC_AVDTP_10_4 True (*) Get configuration response (O) TSPC_AVDTP_10_5 False Reconfigure response (O) TSPC_AVDTP_10_6 True (*) Stream get all capabilities response (C.1) ------------------------------------------------------------------------------- C.1: It is optional to support if TSPC_AVDTP_0_3 is supported, otherwise excluded. C.2: It is Mandatory to support if TSPC_AVDTP_10_6 is supported, otherwise Optional. ------------------------------------------------------------------------------- Stream Establishment, Suspension and Release (Acceptor) ------------------------------------------------------------------------------- Parameter Name Selected Description ------------------------------------------------------------------------------- TSPC_AVDTP_11_1 True (*) Open stream response (O) TSPC_AVDTP_11_2 True (*) Start stream response (O) TSPC_AVDTP_11_3 True (*) Close stream response (O) TSPC_AVDTP_11_4 True (*) Suspend response (O) TSPC_AVDTP_11_5 True (*) Abort stream response (O) TSPC_AVDTP_11_6 True (*) General reject message (O) ------------------------------------------------------------------------------- Security Signaling (Acceptor) ------------------------------------------------------------------------------- Parameter Name Selected Description ------------------------------------------------------------------------------- TSPC_AVDTP_12_1 False Content security control response (O) ------------------------------------------------------------------------------- Message Fragmentation (Acceptor) ------------------------------------------------------------------------------- Parameter Name Selected Description ------------------------------------------------------------------------------- TSPC_AVDTP_13_1 True Signaling message fragmentation (M) ------------------------------------------------------------------------------- Source Capabilities ------------------------------------------------------------------------------- Parameter Name Selected Description ------------------------------------------------------------------------------- TSPC_AVDTP_14_1 True Basic transport service support (M) TSPC_AVDTP_14_2 False Reporting service support (O) TSPC_AVDTP_14_3 False Recovery service support (O) TSPC_AVDTP_14_4 False Multiplexing service support (O) TSPC_AVDTP_14_5 False Robust header compression service support (O) TSPC_AVDTP_14_6 True (*) Delay Reporting (C.1) ------------------------------------------------------------------------------- C.1: It is optional to support if TSPC_AVDTP_0_3 is supported, else excluded. ------------------------------------------------------------------------------- Sink Capabilities ------------------------------------------------------------------------------- Parameter Name Selected Description ------------------------------------------------------------------------------- TSPC_AVDTP_15_1 True Basic transport service support (M) TSPC_AVDTP_15_2 False Reporting service support (O) TSPC_AVDTP_15_3 False Recovery service support (O) TSPC_AVDTP_15_4 False Multiplexing service support (O) TSPC_AVDTP_15_5 False Robust header compression service support (O) TSPC_AVDTP_15_6 True (*) Delay Reporting (C.1) ------------------------------------------------------------------------------- C.1: It is optional to support if TSPC_AVDTP_0_3 is supported, else excluded. ------------------------------------------------------------------------------- Message Error Handling Capabilities ------------------------------------------------------------------------------- Parameter Name Selected Description ------------------------------------------------------------------------------- TSPC_AVDTP_16_1 False Reporting Capability Error (C.1) TSPC_AVDTP_16_2 False Reject Corrupted Messages (C.2) TSPC_AVDTP_16_3 True (*) General Reject Response Includes Signal ID (C.3) ------------------------------------------------------------------------------- C.1: Optional if TSPC_AVDTP_0_2 or TSPC_AVDTP_0_3 supported, excluded otherwise. C.2: Optional, excluded if TSPC_AVDTP_16_3 (General Reject Response Includes Signal ID) is supported. C.3: Mandatory if TSPC_AVDTP_0_3 supported, otherwise Optional. ------------------------------------------------------------------------------- Upper Test Interface ------------------------------------------------------------------------------- Parameter Name Selected Description ------------------------------------------------------------------------------- TSPC_AVDTP_17_1 False Upper Test Interface provided (O) ------------------------------------------------------------------------------- L2CAP Capabilities ------------------------------------------------------------------------------- Parameter Name Selected Description ------------------------------------------------------------------------------- TSPC_AVDTP_18_1 False Enhanced Retransmission Mode preferred for signaling channel (O) TSPC_AVDTP_18_2 False Streaming Mode preferred for Media Transport channel (O) TSPC_AVDTP_18_3 False FCS Option (C.1) ------------------------------------------------------------------------------- C.1: Mandatory if TSPC_AVDTP_18_1 is supported, otherwise Optional. ------------------------------------------------------------------------------- bluez-5.82/android/PaxHeaders/pts-sm.txt0000644000000000000000000000005012537515745015270 xustar0020 atime=1743516876 20 ctime=1743591289 bluez-5.82/android/pts-sm.txt0000644000000000000000000000716612537515745014763 0ustar00rootrootPTS test results for SM PTS version: 6.1 Tested: 04-May-2015 Android version: 5.1 kernel version: 4.1 Results: PASS test passed FAIL test failed INC test is inconclusive N/A test is disabled due to PICS setup NONE test result is none ------------------------------------------------------------------------------- Test Name Result Notes ------------------------------------------------------------------------------- TC_PROT_BV_01_C PASS btmgmt pair -c 0x03 -t 0x01 TC_PROT_BV_02_C PASS btmgmt advertising on btmgmt pair -c 0x03 -t 0x01 TC_JW_BV_01_C PASS btmgmt pairable off btmgmt pair -c 0x03 -t 0x01 TC_JW_BV_02_C PASS btmgmt advertising on TC_JW_BV_05_C PASS btmgmt pair -c 0x03 -t 0x01 TC_JW_BI_01_C PASS btmgmt pair -c 0x03 -t 0x01 TC_JW_BI_02_C PASS btmgmt pairable on TC_JW_BI_03_C PASS btmgmt pairable on btmgmt advertising on TC_JW_BI_04_C PASS btmgmt pairable off btmgmg pair -c 0x03 -t 0x01 TC_PKE_BV_01_C PASS btmgmt pairable off btmgmt pair -c 0x04 -t 0x01 Note: provide passkey to PTS TC_PKE_BV_02_C PASS btmgmt pairable off btmgmt io-cap 0x04 Note: provide passkey TC_PKE_BV_04_C PASS btmgmt pair -c 0x04 -t 0x01 TC_PKE_BV_05_C PASS btmgmt io-cap 0x04 l2test -r -J4 -AES -V le_public TC_PKE_BI_01_C PASS btmgmt pair -c 0x04 -t 0x01 Note: Enter invalid passkey in PTS TC_PKE_BI_02_C PASS btmgmt pair -c 0x04 -t 0x01 Note: provide passkey TC_PKE_BI_03_C PASS btmgmt io-cap 0x04 btmgmt advertising on Note: Enter invalid passkey in PTS TC_OOB_BV_01_C N/A TC_OOB_BV_02_C N/A TC_OOB_BV_03_C N/A TC_OOB_BV_04_C N/A TC_OOB_BV_05_C PASS btmgmt pair -c 0x04 -t 0x01 Note: Enter valid passkey in PTS TC_OOB_BV_06_C PASS btmgmt advertising on Note: Enter valid passkey in PTS TC_OOB_BV_07_C PASS btmgmt pair -c 0x04 -t 0x01 TC_OOB_BV_08_C PASS btmgmt advertising on Note: Accept pairing in btmgmt TC_OOB_BV_09_C N/A TC_OOB_BV_10_C N/A TC_OOB_BI_01_C N/A TC_OOB_BI_02_C N/A TC_EKS_BV_01_C PASS btmgmt pair -c 0x04 -t 0x01 Note: Enter valid passkey in PTS TC_EKS_BV_02_C PASS btmgmt advertising on Note: Accept pairing in btmgmt TC_EKS_BI_01_C PASS btmgmt pair -c 0x03 -t 0x01 TC_EKS_BI_02_C PASS btmgmt advertising on TC_SIGN_BV_01_C INC PTS issue #12305 TC_SIGN_BV_03_C PASS haltest: gattc register_client 1234 gattc listen 1 1 Note: IUT must be connectable and discoverable TC_SIGN_BI_01_C PASS haltest: gattc register client 1234 gattc listen 1 1 Note: IUT must be connectable and discoverable TC_KDU_BV_01_C PASS btmgmt pairable on btmgmt advertising on btmgmt connectable on TC_KDU_BV_02_C PASS PTS issue #12302 Note: Can pass it with following instructions: btmgmt privacy on btmgmt advertising on Check our random address (valid for 15 min) Set PIXIT TSPX_bd_addr_iut to random address Set PIXIT TSPX_peer_type to 01 TC_KDU_BV_03_C PASS btmgmt pairable on btmgmt advertising on TC_KDU_BV_04_C PASS btmgmt pair -c 0x03 -t 0x01 TC_KDU_BV_05_C PASS PTS issue #12302 Note: Can pass it with following instructions: btmgmt privacy on Check our random address (valid for 15 min) Set PIXIT TSPX_bd_addr_iut to random address Set PIXIT TSPX_peer_type to 01 TC_KDU_BV_06_C PASS btmgmt pair -c 0x03 -t 0x01 TC_KDU_BV_07_C PASS btmgmt pairable on TC_SIP_BV_01_C PASS btmgmt advertising on TC_SIP_BV_02_C PASS btmgmt pair -c 0x03 -t 0x01 TC_SIE_BV_01_C PASS btmgmt io-cap 0x03 btmgmt advertising on ------------------------------------------------------------------------------- bluez-5.82/android/PaxHeaders/map-client.h0000644000000000000000000000005014015011623015461 xustar0020 atime=1743516867 20 ctime=1743591279 bluez-5.82/android/map-client.h0000644000000000000000000000046314015011623015145 0ustar00rootroot/* SPDX-License-Identifier: LGPL-2.1-or-later */ /* * * BlueZ - Bluetooth protocol stack for Linux * * Copyright (C) 2014 Intel Corporation. All rights reserved. * * */ bool bt_map_client_register(struct ipc *ipc, const bdaddr_t *addr, uint8_t mode); void bt_map_client_unregister(void); bluez-5.82/android/PaxHeaders/bluetoothd_snoop.te0000644000000000000000000000005012447320342017210 xustar0020 atime=1743516875 20 ctime=1743591288 bluez-5.82/android/bluetoothd_snoop.te0000644000000000000000000000102112447320342016663 0ustar00rootroottype bluetoothd_snoop, domain; type bluetoothd_snoop_exec, exec_type, file_type; # Start bluetoothd_snoop from init init_daemon_domain(bluetoothd_snoop) # directory search and read caps allow bluetoothd_snoop self:capability dac_read_search; # use raw and packet sockets caps allow bluetoothd_snoop self:capability net_raw; # monitor socket access allow bluetoothd_snoop self:socket { create bind setopt read }; # sdcard access allow bluetoothd_snoop fuse:dir w_dir_perms; allow bluetoothd_snoop fuse:file create_file_perms; bluez-5.82/android/PaxHeaders/bluetoothd-wrapper.c0000644000000000000000000000005014015011623017252 xustar0020 atime=1743516875 20 ctime=1743591288 bluez-5.82/android/bluetoothd-wrapper.c0000644000000000000000000000332114015011623016732 0ustar00rootroot// SPDX-License-Identifier: Apache-2.0 /* * Copyright (C) 2014 Intel Corporation * */ #include #include #include #include #include #include #include "hal-utils.h" #define VALGRIND_BIN "/system/bin/valgrind" #define BLUETOOTHD_BIN "/system/bin/bluetoothd-main" static void run_valgrind(int debug, int mgmt_dbg) { char *prg_argv[7]; char *prg_envp[3]; prg_argv[0] = VALGRIND_BIN; prg_argv[1] = "--leak-check=full"; prg_argv[2] = "--track-origins=yes"; prg_argv[3] = BLUETOOTHD_BIN; prg_argv[4] = debug ? "-d" : NULL; prg_argv[5] = mgmt_dbg ? "--mgmt-debug" : NULL; prg_argv[6] = NULL; prg_envp[0] = "G_SLICE=always-malloc"; prg_envp[1] = "G_DEBUG=gc-friendly"; prg_envp[2] = NULL; execve(prg_argv[0], prg_argv, prg_envp); } static void run_bluetoothd(int debug, int mgmt_dbg) { char *prg_argv[4]; char *prg_envp[1]; prg_argv[0] = BLUETOOTHD_BIN; prg_argv[1] = debug ? "-d" : NULL; prg_argv[2] = mgmt_dbg ? "--mgmt-debug" : NULL; prg_argv[3] = NULL; prg_envp[0] = NULL; execve(prg_argv[0], prg_argv, prg_envp); } int main(int argc, char *argv[]) { char value[PROPERTY_VALUE_MAX]; int debug = 0; int mgmt_dbg = 0; if (get_config("debug", value, NULL) > 0 && (!strcasecmp(value, "true") || atoi(value) > 0)) debug = 1; if (get_config("mgmtdbg", value, NULL) > 0 && (!strcasecmp(value, "true") || atoi(value) > 0)) { debug = 1; mgmt_dbg = 1; } if (get_config("valgrind", value, NULL) > 0 && (!strcasecmp(value, "true") || atoi(value) > 0)) run_valgrind(debug, mgmt_dbg); /* * In case we failed to execute Valgrind, try to run bluetoothd * without it */ run_bluetoothd(debug, mgmt_dbg); return 0; } bluez-5.82/android/PaxHeaders/pts-mcap.txt0000644000000000000000000000005012537515745015571 xustar0020 atime=1743516876 20 ctime=1743591289 bluez-5.82/android/pts-mcap.txt0000644000000000000000000000673212537515745015262 0ustar00rootrootPTS test results for MCAP PTS version: 6.1 Tested: 19-May-2015 Android version: 5.1 Results: PASS test passed FAIL test failed INC test is inconclusive N/A test is disabled due to PICS setup Note: Test were done with ssp enabled and in most of the cases requires pairing confirmation. This can be done easily by using 'btmgmt monitor'. ------------------------------------------------------------------------------- Test Name Result Notes ------------------------------------------------------------------------------- TC_MCAP_CE_BV_01_C PASS mcaptest -C 4099 -D 4101 -f 2 -dc TC_MCAP_CE_BV_02_C PASS mcaptest -C 4099 -D 4101 -f 2 TC_MCAP_CE_BV_03_C PASS mcaptest -C 4099 -D 4101 -f 2 -c TC_MCAP_CE_BV_04_C PASS mcaptest -C 4099 -D 4101 -f 2 -d TC_MCAP_CM_ABT_BV_01_C N/A TC_MCAP_CM_ABT_BV_02_C PASS mcaptest -C 4099 -D 4101 -f 2 TC_MCAP_CM_ABT_BV_03_C N/A TC_MCAP_CM_DEL_BV_01_C N/A TC_MCAP_CM_DEL_BV_02_C PASS mcaptest -C 4099 -D 4101 -f 2 TC_MCAP_CM_DEL_BV_03_C N/A TC_MCAP_CM_DEL_BV_04_C PASS mcaptest -C 4099 -D 4101 -f 2 TC_MCAP_CM_DIS_BV_01_C PASS mcaptest -C 4099 -D 4101 -ab -e 2 -f 2 TC_MCAP_CM_DIS_BV_02_C PASS mcaptest -C 4099 -D 4101 TC_MCAP_CM_DIS_BV_03_C PASS mcaptest -C 4099 -D 4101 -n -f 2 TC_MCAP_CM_DIS_BV_04_C PASS mcaptest -C 4099 -D 4101 -ab -e 2 -f 2 TC_MCAP_CM_DIS_BV_05_C PASS mcaptest -C 4099 -D 4101 TC_MCAP_CM_REC_BV_01_C N/A TC_MCAP_CM_REC_BV_02_C PASS mcaptest -C 4099 -D 4101 -n -f 2 TC_MCAP_CM_REC_BV_03_C N/A TC_MCAP_CM_REC_BV_04_C PASS mcaptest -C 4099 -D 4101 -n -f 2 TC_MCAP_CM_REC_BV_05_C N/A TC_MCAP_CM_REC_BV_06_C PASS mcaptest -C 4099 -D 4101 -f 2 TC_MCAP_CS_ERR_BI_01_C N/A TC_MCAP_CS_ERR_BI_02_C N/A TC_MCAP_CS_ERR_BI_03_C N/A TC_MCAP_CS_ERR_BI_04_C N/A TC_MCAP_CS_I_BV_01_I N/A TC_MCAP_CS_I_BV_02_I N/A TC_MCAP_CS_I_BV_03_C N/A TC_MCAP_CS_I_BV_04_C N/A TC_MCAP_CS_R_BV_01_I N/A TC_MCAP_CS_R_BV_02_I N/A TC_MCAP_CS_R_BV_03_C N/A TC_MCAP_CS_T_BV_04_C N/A TC_MCAP_ERR_BI_01_C PASS mcaptest -C 4099 -D 4101 -f 2 TC_MCAP_ERR_BI_02_C PASS mcaptest -C 4099 -D 4101 -dn -f 2 TC_MCAP_ERR_BI_03_C PASS mcaptest -C 4099 -D 4101 -f 2 TC_MCAP_ERR_BI_04_C PASS mcaptest -C 4099 -D 4101 -dn -f 2 TC_MCAP_ERR_BI_05_C PASS mcaptest -C 4099 -D 4101 -f 2 TC_MCAP_ERR_BI_06_C PASS mcaptest -C 4099 -D 4101 -dn -f 2 TC_MCAP_ERR_BI_07_C PASS mcaptest -C 4099 -D 4101 -f 2 TC_MCAP_ERR_BI_08_C PASS mcaptest -C 4099 -D 4101 -dn -f 2 TC_MCAP_ERR_BI_09_C PASS mcaptest -C 4099 -D 4101 -f 2 TC_MCAP_ERR_BI_10_C PASS mcaptest -C 4099 -D 4101 -f 2 TC_MCAP_ERR_BI_11_C PASS mcaptest -C 4099 -D 4101 -dn -f 2 TC_MCAP_ERR_BI_12_C PASS mcaptest -C 4099 -D 4101 -dn -f 2 TC_MCAP_ERR_BI_13_C PASS mcaptest -C 4099 -D 4101 -f 2 TC_MCAP_ERR_BI_14_C PASS mcaptest -C 4099 -D 4101 -f 2 TC_MCAP_ERR_BI_15_C PASS mcaptest -C 4099 -D 4101 -dn -f 2 TC_MCAP_ERR_BI_16_C PASS mcaptest -C 4099 -D 4101 -u -f 2 TC_MCAP_ERR_BI_17_C PASS mcaptest -C 4099 -D 4101 -dn -f 2 TC_MCAP_ERR_BI_18_C PASS mcaptest -C 4099 -D 4101 -dn -f 2 TC_MCAP_ERR_BI_19_C N/A TC_MCAP_ERR_BI_20_C PASS mcaptest -C 4099 -D 4101 -g -f 2 TC_MCAP_INV_BI_01_C PASS mcaptest -C 4099 -D 4101 -dc TC_MCAP_INV_BI_02_C PASS mcaptest -C 4099 -D 4101 -dn -f 2 TC_MCAP_INV_BI_03_C PASS mcaptest -C 4099 -D 4101 -d -f 2 -c TC_MCAP_INV_BI_04_C PASS mcaptest -C 4099 -D 4101 -f 2 -c TC_MCAP_INV_BI_05_C PASS mcaptest -C 4099 -D 4101 -f 2 TC_MCAP_INV_BI_06_C PASS mcaptest -C 4099 -D 4101 -f 2 TC_MCAP_INV_BI_07_C PASS mcaptest -C 4099 -D 4101 -f 2 ------------------------------------------------------------------------------- bluez-5.82/android/PaxHeaders/hal-utils.c0000644000000000000000000000005014015011623015325 xustar0020 atime=1743516862 20 ctime=1743591276 bluez-5.82/android/hal-utils.c0000644000000000000000000002221714015011623015012 0ustar00rootroot// SPDX-License-Identifier: Apache-2.0 /* * Copyright (C) 2013 Intel Corporation * */ #define _GNU_SOURCE #include #include #include #include #include #include "hal.h" #include "hal-utils.h" /* * converts uuid to string * buf should be at least 39 bytes * * returns string representation of uuid */ const char *bt_uuid_t2str(const uint8_t *uuid, char *buf) { int shift = 0; unsigned int i; int is_bt; if (!uuid) return strcpy(buf, "NULL"); is_bt = !memcmp(&uuid[4], &BT_BASE_UUID[4], HAL_UUID_LEN - 4); for (i = 0; i < HAL_UUID_LEN; i++) { if (i == 4 && is_bt) break; if (i == 4 || i == 6 || i == 8 || i == 10) { buf[i * 2 + shift] = '-'; shift++; } sprintf(buf + i * 2 + shift, "%02x", uuid[i]); } return buf; } const char *btuuid2str(const uint8_t *uuid) { static char buf[MAX_UUID_STR_LEN]; return bt_uuid_t2str(uuid, buf); } INTMAP(bt_status_t, -1, "(unknown)") DELEMENT(BT_STATUS_SUCCESS), DELEMENT(BT_STATUS_FAIL), DELEMENT(BT_STATUS_NOT_READY), DELEMENT(BT_STATUS_NOMEM), DELEMENT(BT_STATUS_BUSY), DELEMENT(BT_STATUS_DONE), DELEMENT(BT_STATUS_UNSUPPORTED), DELEMENT(BT_STATUS_PARM_INVALID), DELEMENT(BT_STATUS_UNHANDLED), DELEMENT(BT_STATUS_AUTH_FAILURE), DELEMENT(BT_STATUS_RMT_DEV_DOWN), ENDMAP INTMAP(bt_state_t, -1, "(unknown)") DELEMENT(BT_STATE_OFF), DELEMENT(BT_STATE_ON), ENDMAP INTMAP(bt_device_type_t, -1, "(unknown)") DELEMENT(BT_DEVICE_DEVTYPE_BREDR), DELEMENT(BT_DEVICE_DEVTYPE_BLE), DELEMENT(BT_DEVICE_DEVTYPE_DUAL), ENDMAP INTMAP(bt_scan_mode_t, -1, "(unknown)") DELEMENT(BT_SCAN_MODE_NONE), DELEMENT(BT_SCAN_MODE_CONNECTABLE), DELEMENT(BT_SCAN_MODE_CONNECTABLE_DISCOVERABLE), ENDMAP INTMAP(bt_discovery_state_t, -1, "(unknown)") DELEMENT(BT_DISCOVERY_STOPPED), DELEMENT(BT_DISCOVERY_STARTED), ENDMAP INTMAP(bt_acl_state_t, -1, "(unknown)") DELEMENT(BT_ACL_STATE_CONNECTED), DELEMENT(BT_ACL_STATE_DISCONNECTED), ENDMAP INTMAP(bt_bond_state_t, -1, "(unknown)") DELEMENT(BT_BOND_STATE_NONE), DELEMENT(BT_BOND_STATE_BONDING), DELEMENT(BT_BOND_STATE_BONDED), ENDMAP INTMAP(bt_ssp_variant_t, -1, "(unknown)") DELEMENT(BT_SSP_VARIANT_PASSKEY_CONFIRMATION), DELEMENT(BT_SSP_VARIANT_PASSKEY_ENTRY), DELEMENT(BT_SSP_VARIANT_CONSENT), DELEMENT(BT_SSP_VARIANT_PASSKEY_NOTIFICATION), ENDMAP #if ANDROID_VERSION >= PLATFORM_VER(5, 0, 0) INTMAP(bt_property_type_t, -1, "(unknown)") DELEMENT(BT_PROPERTY_BDNAME), DELEMENT(BT_PROPERTY_BDADDR), DELEMENT(BT_PROPERTY_UUIDS), DELEMENT(BT_PROPERTY_CLASS_OF_DEVICE), DELEMENT(BT_PROPERTY_TYPE_OF_DEVICE), DELEMENT(BT_PROPERTY_SERVICE_RECORD), DELEMENT(BT_PROPERTY_ADAPTER_SCAN_MODE), DELEMENT(BT_PROPERTY_ADAPTER_BONDED_DEVICES), DELEMENT(BT_PROPERTY_ADAPTER_DISCOVERY_TIMEOUT), DELEMENT(BT_PROPERTY_REMOTE_FRIENDLY_NAME), DELEMENT(BT_PROPERTY_REMOTE_RSSI), DELEMENT(BT_PROPERTY_REMOTE_VERSION_INFO), DELEMENT(BT_PROPERTY_LOCAL_LE_FEATURES), DELEMENT(BT_PROPERTY_REMOTE_DEVICE_TIMESTAMP), ENDMAP #else INTMAP(bt_property_type_t, -1, "(unknown)") DELEMENT(BT_PROPERTY_BDNAME), DELEMENT(BT_PROPERTY_BDADDR), DELEMENT(BT_PROPERTY_UUIDS), DELEMENT(BT_PROPERTY_CLASS_OF_DEVICE), DELEMENT(BT_PROPERTY_TYPE_OF_DEVICE), DELEMENT(BT_PROPERTY_SERVICE_RECORD), DELEMENT(BT_PROPERTY_ADAPTER_SCAN_MODE), DELEMENT(BT_PROPERTY_ADAPTER_BONDED_DEVICES), DELEMENT(BT_PROPERTY_ADAPTER_DISCOVERY_TIMEOUT), DELEMENT(BT_PROPERTY_REMOTE_FRIENDLY_NAME), DELEMENT(BT_PROPERTY_REMOTE_RSSI), DELEMENT(BT_PROPERTY_REMOTE_VERSION_INFO), DELEMENT(BT_PROPERTY_REMOTE_DEVICE_TIMESTAMP), ENDMAP #endif INTMAP(bt_cb_thread_evt, -1, "(unknown)") DELEMENT(ASSOCIATE_JVM), DELEMENT(DISASSOCIATE_JVM), ENDMAP /* Find first index of given value in table m */ int int2str_findint(int v, const struct int2str m[]) { int i; for (i = 0; m[i].str; ++i) { if (m[i].val == v) return i; } return -1; } /* Find first index of given string in table m */ int int2str_findstr(const char *str, const struct int2str m[]) { int i; for (i = 0; m[i].str; ++i) { if (strcmp(m[i].str, str) == 0) return i; } return -1; } /* * convert bd_addr to string * buf must be at least 18 char long * * returns buf */ const char *bt_bdaddr_t2str(const bt_bdaddr_t *bd_addr, char *buf) { const uint8_t *p; if (!bd_addr) return strcpy(buf, "NULL"); p = bd_addr->address; snprintf(buf, MAX_ADDR_STR_LEN, "%02x:%02x:%02x:%02x:%02x:%02x", p[0], p[1], p[2], p[3], p[4], p[5]); return buf; } /* converts string to bt_bdaddr_t */ void str2bt_bdaddr_t(const char *str, bt_bdaddr_t *bd_addr) { uint8_t *p = bd_addr->address; sscanf(str, "%02hhx:%02hhx:%02hhx:%02hhx:%02hhx:%02hhx", &p[0], &p[1], &p[2], &p[3], &p[4], &p[5]); } /* converts string to uuid */ void str2bt_uuid_t(const char *str, bt_uuid_t *uuid) { int i = 0; memcpy(uuid, BT_BASE_UUID, sizeof(bt_uuid_t)); while (*str && i < (int) sizeof(bt_uuid_t)) { while (*str == '-') str++; if (sscanf(str, "%02hhx", &uuid->uu[i]) != 1) break; i++; str += 2; } } const char *enum_defines(void *v, int i) { const struct int2str *m = v; return m[i].str != NULL ? m[i].str : NULL; } const char *enum_strings(void *v, int i) { const char **m = v; return m[i] != NULL ? m[i] : NULL; } const char *enum_one_string(void *v, int i) { const char *m = v; return (i == 0) && (m[0] != 0) ? m : NULL; } const char *bdaddr2str(const bt_bdaddr_t *bd_addr) { static char buf[MAX_ADDR_STR_LEN]; return bt_bdaddr_t2str(bd_addr, buf); } static void bonded_devices2string(char *str, void *prop, int prop_len) { int count = prop_len / sizeof(bt_bdaddr_t); bt_bdaddr_t *addr = prop; strcat(str, "{"); while (count--) { strcat(str, bdaddr2str(addr)); if (count) strcat(str, ", "); addr++; } strcat(str, "}"); } static void uuids2string(char *str, void *prop, int prop_len) { int count = prop_len / sizeof(bt_uuid_t); bt_uuid_t *uuid = prop; strcat(str, "{"); while (count--) { strcat(str, btuuid2str(uuid->uu)); if (count) strcat(str, ", "); uuid++; } strcat(str, "}"); } #if ANDROID_VERSION >= PLATFORM_VER(5, 0, 0) static void local_le_feat2string(char *str, const bt_local_le_features_t *f) { uint16_t scan_num; str += sprintf(str, "{\n"); str += sprintf(str, "Privacy supported: %s,\n", f->local_privacy_enabled ? "TRUE" : "FALSE"); str += sprintf(str, "Num of advertising instances: %u,\n", f->max_adv_instance); str += sprintf(str, "PRA offloading support: %s,\n", f->rpa_offload_supported ? "TRUE" : "FALSE"); str += sprintf(str, "Num of offloaded IRKs: %u,\n", f->max_irk_list_size); str += sprintf(str, "Num of offloaded scan filters: %u,\n", f->max_adv_filter_supported); scan_num = (f->scan_result_storage_size_hibyte << 8) + f->scan_result_storage_size_lobyte; str += sprintf(str, "Num of offloaded scan results: %u,\n", scan_num); str += sprintf(str, "Activity & energy report support: %s\n", f->activity_energy_info_supported ? "TRUE" : "FALSE"); sprintf(str, "}"); } #endif const char *btproperty2str(const bt_property_t *property) { bt_service_record_t *rec; static char buf[4096]; char *p; p = buf + sprintf(buf, "type=%s len=%d val=", bt_property_type_t2str(property->type), property->len); switch (property->type) { case BT_PROPERTY_BDNAME: case BT_PROPERTY_REMOTE_FRIENDLY_NAME: snprintf(p, property->len + 1, "%s", ((bt_bdname_t *) property->val)->name); break; case BT_PROPERTY_BDADDR: sprintf(p, "%s", bdaddr2str((bt_bdaddr_t *) property->val)); break; case BT_PROPERTY_CLASS_OF_DEVICE: sprintf(p, "%06x", *((int *) property->val)); break; case BT_PROPERTY_TYPE_OF_DEVICE: sprintf(p, "%s", bt_device_type_t2str( *((bt_device_type_t *) property->val))); break; case BT_PROPERTY_REMOTE_RSSI: sprintf(p, "%d", *((char *) property->val)); break; case BT_PROPERTY_ADAPTER_SCAN_MODE: sprintf(p, "%s", bt_scan_mode_t2str(*((bt_scan_mode_t *) property->val))); break; case BT_PROPERTY_ADAPTER_DISCOVERY_TIMEOUT: sprintf(p, "%d", *((int *) property->val)); break; case BT_PROPERTY_ADAPTER_BONDED_DEVICES: bonded_devices2string(p, property->val, property->len); break; case BT_PROPERTY_UUIDS: uuids2string(p, property->val, property->len); break; case BT_PROPERTY_SERVICE_RECORD: rec = property->val; sprintf(p, "{%s, %d, %s}", btuuid2str(rec->uuid.uu), rec->channel, rec->name); break; #if ANDROID_VERSION >= PLATFORM_VER(5, 0, 0) case BT_PROPERTY_LOCAL_LE_FEATURES: local_le_feat2string(p, property->val); break; #endif case BT_PROPERTY_REMOTE_VERSION_INFO: case BT_PROPERTY_REMOTE_DEVICE_TIMESTAMP: default: sprintf(p, "%p", property->val); break; } return buf; } #define PROP_PREFIX "persist.sys.bluetooth." #define PROP_PREFIX_RO "ro.bluetooth." int get_config(const char *config_key, char *value, const char *fallback) { char key[PROPERTY_KEY_MAX]; int ret; if (strlen(config_key) + sizeof(PROP_PREFIX) > sizeof(key)) return 0; snprintf(key, sizeof(key), PROP_PREFIX"%s", config_key); ret = property_get(key, value, ""); if (ret > 0) return ret; snprintf(key, sizeof(key), PROP_PREFIX_RO"%s", config_key); ret = property_get(key, value, ""); if (ret > 0) return ret; if (!fallback) return 0; return property_get(fallback, value, ""); } bluez-5.82/android/PaxHeaders/hal-handsfree.c0000644000000000000000000000005014015011623016124 xustar0020 atime=1743516862 20 ctime=1743591276 bluez-5.82/android/hal-handsfree.c0000644000000000000000000005042414015011623015612 0ustar00rootroot// SPDX-License-Identifier: Apache-2.0 /* * Copyright (C) 2014 Intel Corporation * */ #define _GNU_SOURCE #include #include #include #include #include #include "hal-log.h" #include "hal.h" #include "hal-msg.h" #include "ipc-common.h" #include "hal-ipc.h" #include "hal-utils.h" static const bthf_callbacks_t *cbs = NULL; static bool interface_ready(void) { return cbs != NULL; } static void handle_conn_state(void *buf, uint16_t len, int fd) { struct hal_ev_handsfree_conn_state *ev = buf; if (cbs->connection_state_cb) cbs->connection_state_cb(ev->state, (bt_bdaddr_t *) (ev->bdaddr)); } static void handle_audio_state(void *buf, uint16_t len, int fd) { struct hal_ev_handsfree_audio_state *ev = buf; if (cbs->audio_state_cb) cbs->audio_state_cb(ev->state, (bt_bdaddr_t *) (ev->bdaddr)); } static void handle_vr_state(void *buf, uint16_t len, int fd) { struct hal_ev_handsfree_vr_state *ev = buf; if (cbs->vr_cmd_cb) #if ANDROID_VERSION >= PLATFORM_VER(5, 0, 0) cbs->vr_cmd_cb(ev->state, (bt_bdaddr_t *) (ev->bdaddr)); #else cbs->vr_cmd_cb(ev->state); #endif } static void handle_answer(void *buf, uint16_t len, int fd) { if (cbs->answer_call_cmd_cb) { #if ANDROID_VERSION >= PLATFORM_VER(5, 0, 0) struct hal_ev_handsfree_answer *ev = buf; cbs->answer_call_cmd_cb((bt_bdaddr_t *) (ev->bdaddr)); #else cbs->answer_call_cmd_cb(); #endif } } static void handle_hangup(void *buf, uint16_t len, int fd) { if (cbs->hangup_call_cmd_cb) { #if ANDROID_VERSION >= PLATFORM_VER(5, 0, 0) struct hal_ev_handsfree_hangup *ev = buf; cbs->hangup_call_cmd_cb((bt_bdaddr_t *) (ev->bdaddr)); #else cbs->hangup_call_cmd_cb(); #endif } } static void handle_volume(void *buf, uint16_t len, int fd) { struct hal_ev_handsfree_volume *ev = buf; if (cbs->volume_cmd_cb) #if ANDROID_VERSION >= PLATFORM_VER(5, 0, 0) cbs->volume_cmd_cb(ev->type, ev->volume, (bt_bdaddr_t *) (ev->bdaddr)); #else cbs->volume_cmd_cb(ev->type, ev->volume); #endif } static void handle_dial(void *buf, uint16_t len, int fd) { struct hal_ev_handsfree_dial *ev = buf; uint16_t num_len = ev->number_len; char *number = NULL; if (len != sizeof(*ev) + num_len || (num_len != 0 && ev->number[num_len - 1] != '\0')) { error("invalid dial event, aborting"); exit(EXIT_FAILURE); } if (!cbs->dial_call_cmd_cb) return; if (ev->number_len) number = (char *) ev->number; #if ANDROID_VERSION >= PLATFORM_VER(5, 0, 0) cbs->dial_call_cmd_cb(number, (bt_bdaddr_t *) (ev->bdaddr)); #else cbs->dial_call_cmd_cb(number); #endif } static void handle_dtmf(void *buf, uint16_t len, int fd) { struct hal_ev_handsfree_dtmf *ev = buf; if (cbs->dtmf_cmd_cb) #if ANDROID_VERSION >= PLATFORM_VER(5, 0, 0) cbs->dtmf_cmd_cb(ev->tone, (bt_bdaddr_t *) (ev->bdaddr)); #else cbs->dtmf_cmd_cb(ev->tone); #endif } static void handle_nrec(void *buf, uint16_t len, int fd) { struct hal_ev_handsfree_nrec *ev = buf; if (cbs->nrec_cmd_cb) #if ANDROID_VERSION >= PLATFORM_VER(5, 0, 0) cbs->nrec_cmd_cb(ev->nrec, (bt_bdaddr_t *) (ev->bdaddr)); #else cbs->nrec_cmd_cb(ev->nrec); #endif } static void handle_wbs(void *buf, uint16_t len, int fd) { #if ANDROID_VERSION >= PLATFORM_VER(5, 0, 0) struct hal_ev_handsfree_wbs *ev = buf; if (cbs->wbs_cb) cbs->wbs_cb(ev->wbs, (bt_bdaddr_t *) (ev->bdaddr)); #endif } static void handle_chld(void *buf, uint16_t len, int fd) { struct hal_ev_handsfree_chld *ev = buf; if (cbs->chld_cmd_cb) #if ANDROID_VERSION >= PLATFORM_VER(5, 0, 0) cbs->chld_cmd_cb(ev->chld, (bt_bdaddr_t *) (ev->bdaddr)); #else cbs->chld_cmd_cb(ev->chld); #endif } static void handle_cnum(void *buf, uint16_t len, int fd) { if (cbs->cnum_cmd_cb) { #if ANDROID_VERSION >= PLATFORM_VER(5, 0, 0) struct hal_ev_handsfree_cnum *ev = buf; cbs->cnum_cmd_cb((bt_bdaddr_t *) (ev->bdaddr)); #else cbs->cnum_cmd_cb(NULL); #endif } } static void handle_cind(void *buf, uint16_t len, int fd) { if (cbs->cind_cmd_cb) { #if ANDROID_VERSION >= PLATFORM_VER(5, 0, 0) struct hal_ev_handsfree_cind *ev = buf; cbs->cind_cmd_cb((bt_bdaddr_t *) (ev->bdaddr)); #else cbs->cind_cmd_cb(); #endif } } static void handle_cops(void *buf, uint16_t len, int fd) { if (cbs->cops_cmd_cb) { #if ANDROID_VERSION >= PLATFORM_VER(5, 0, 0) struct hal_ev_handsfree_cops *ev = buf; cbs->cops_cmd_cb((bt_bdaddr_t *) (ev->bdaddr)); #else cbs->cops_cmd_cb(); #endif } } static void handle_clcc(void *buf, uint16_t len, int fd) { if (cbs->clcc_cmd_cb) { #if ANDROID_VERSION >= PLATFORM_VER(5, 0, 0) struct hal_ev_handsfree_clcc *ev = buf; cbs->clcc_cmd_cb((bt_bdaddr_t *) (ev->bdaddr)); #else cbs->clcc_cmd_cb(); #endif } } static void handle_unknown_at(void *buf, uint16_t len, int fd) { struct hal_ev_handsfree_unknown_at *ev = buf; if (len != sizeof(*ev) + ev->len || (ev->len != 0 && ev->buf[ev->len - 1] != '\0')) { error("invalid unknown command event, aborting"); exit(EXIT_FAILURE); } if (cbs->unknown_at_cmd_cb) #if ANDROID_VERSION >= PLATFORM_VER(5, 0, 0) cbs->unknown_at_cmd_cb((char *) ev->buf, (bt_bdaddr_t *) (ev->bdaddr)); #else cbs->unknown_at_cmd_cb((char *) ev->buf); #endif } static void handle_hsp_key_press(void *buf, uint16_t len, int fd) { if (cbs->key_pressed_cmd_cb) { #if ANDROID_VERSION >= PLATFORM_VER(5, 0, 0) struct hal_ev_handsfree_hsp_key_press *ev = buf; cbs->key_pressed_cmd_cb((bt_bdaddr_t *) (ev->bdaddr)); #else cbs->key_pressed_cmd_cb(); #endif } } /* * handlers will be called from notification thread context, * index in table equals to 'opcode - HAL_MINIMUM_EVENT' */ static const struct hal_ipc_handler ev_handlers[] = { /* HAL_EV_HANDSFREE_CONN_STATE */ { handle_conn_state, false, sizeof(struct hal_ev_handsfree_conn_state) }, /* HAL_EV_HANDSFREE_AUDIO_STATE */ { handle_audio_state, false, sizeof(struct hal_ev_handsfree_audio_state) }, /* HAL_EV_HANDSFREE_VR */ { handle_vr_state, false, sizeof(struct hal_ev_handsfree_vr_state) }, /* HAL_EV_HANDSFREE_ANSWER */ { handle_answer, false, sizeof(struct hal_ev_handsfree_answer) }, /* HAL_EV_HANDSFREE_HANGUP */ { handle_hangup, false, sizeof(struct hal_ev_handsfree_hangup) }, /* HAL_EV_HANDSFREE_VOLUME */ { handle_volume, false, sizeof(struct hal_ev_handsfree_volume) }, /* HAL_EV_HANDSFREE_DIAL */ { handle_dial, true, sizeof(struct hal_ev_handsfree_dial) }, /* HAL_EV_HANDSFREE_DTMF */ { handle_dtmf, false, sizeof(struct hal_ev_handsfree_dtmf) }, /* HAL_EV_HANDSFREE_NREC */ { handle_nrec, false, sizeof(struct hal_ev_handsfree_nrec) }, /* HAL_EV_HANDSFREE_CHLD */ { handle_chld, false, sizeof(struct hal_ev_handsfree_chld) }, /* HAL_EV_HANDSFREE_CNUM */ { handle_cnum, false, sizeof(struct hal_ev_handsfree_cnum) }, /* HAL_EV_HANDSFREE_CIND */ { handle_cind, false, sizeof(struct hal_ev_handsfree_cind) }, /* HAL_EV_HANDSFREE_COPS */ { handle_cops, false, sizeof(struct hal_ev_handsfree_cops) }, /* HAL_EV_HANDSFREE_CLCC */ { handle_clcc, false, sizeof(struct hal_ev_handsfree_clcc) }, /* HAL_EV_HANDSFREE_UNKNOWN_AT */ { handle_unknown_at, true, sizeof(struct hal_ev_handsfree_unknown_at) }, /* HAL_EV_HANDSFREE_HSP_KEY_PRESS */ { handle_hsp_key_press, false, sizeof(struct hal_ev_handsfree_hsp_key_press) }, /* HAL_EV_HANDSFREE_WBS */ { handle_wbs, false, sizeof(struct hal_ev_handsfree_wbs) }, }; static uint8_t get_mode(void) { char value[PROPERTY_VALUE_MAX]; if (get_config("handsfree", value, NULL) > 0) { if (!strcasecmp(value, "hfp")) return HAL_MODE_HANDSFREE_HFP; if (!strcasecmp(value, "hfp_wbs")) return HAL_MODE_HANDSFREE_HFP_WBS; } return HAL_MODE_HANDSFREE_HSP_ONLY; } static bt_status_t init_real(bthf_callbacks_t *callbacks, int max_hf_clients) { struct hal_cmd_register_module cmd; int ret; DBG(""); if (interface_ready()) return BT_STATUS_DONE; cbs = callbacks; hal_ipc_register(HAL_SERVICE_ID_HANDSFREE, ev_handlers, sizeof(ev_handlers)/sizeof(ev_handlers[0])); cmd.service_id = HAL_SERVICE_ID_HANDSFREE; cmd.mode = get_mode(); cmd.max_clients = max_hf_clients; ret = hal_ipc_cmd(HAL_SERVICE_ID_CORE, HAL_OP_REGISTER_MODULE, sizeof(cmd), &cmd, NULL, NULL, NULL); if (ret != BT_STATUS_SUCCESS) { cbs = NULL; hal_ipc_unregister(HAL_SERVICE_ID_HANDSFREE); } return ret; } #if ANDROID_VERSION >= PLATFORM_VER(5, 0, 0) static bt_status_t init(bthf_callbacks_t *callbacks, int max_hf_clients) { return init_real(callbacks, max_hf_clients); } #else static bt_status_t init(bthf_callbacks_t *callbacks) { return init_real(callbacks, 1); } #endif static bt_status_t handsfree_connect(bt_bdaddr_t *bd_addr) { struct hal_cmd_handsfree_connect cmd; DBG(""); if (!interface_ready()) return BT_STATUS_NOT_READY; if (!bd_addr) return BT_STATUS_PARM_INVALID; memcpy(cmd.bdaddr, bd_addr, sizeof(cmd.bdaddr)); return hal_ipc_cmd(HAL_SERVICE_ID_HANDSFREE, HAL_OP_HANDSFREE_CONNECT, sizeof(cmd), &cmd, NULL, NULL, NULL); } static bt_status_t disconnect(bt_bdaddr_t *bd_addr) { struct hal_cmd_handsfree_disconnect cmd; DBG(""); if (!interface_ready()) return BT_STATUS_NOT_READY; if (!bd_addr) return BT_STATUS_PARM_INVALID; memcpy(cmd.bdaddr, bd_addr, sizeof(cmd.bdaddr)); return hal_ipc_cmd(HAL_SERVICE_ID_HANDSFREE, HAL_OP_HANDSFREE_DISCONNECT, sizeof(cmd), &cmd, NULL, NULL, NULL); } static bt_status_t connect_audio(bt_bdaddr_t *bd_addr) { struct hal_cmd_handsfree_connect_audio cmd; DBG(""); if (!interface_ready()) return BT_STATUS_NOT_READY; if (!bd_addr) return BT_STATUS_PARM_INVALID; memcpy(cmd.bdaddr, bd_addr, sizeof(cmd.bdaddr)); return hal_ipc_cmd(HAL_SERVICE_ID_HANDSFREE, HAL_OP_HANDSFREE_CONNECT_AUDIO, sizeof(cmd), &cmd, NULL, NULL, NULL); } static bt_status_t disconnect_audio(bt_bdaddr_t *bd_addr) { struct hal_cmd_handsfree_disconnect_audio cmd; DBG(""); if (!interface_ready()) return BT_STATUS_NOT_READY; if (!bd_addr) return BT_STATUS_PARM_INVALID; memcpy(cmd.bdaddr, bd_addr, sizeof(cmd.bdaddr)); return hal_ipc_cmd(HAL_SERVICE_ID_HANDSFREE, HAL_OP_HANDSFREE_DISCONNECT_AUDIO, sizeof(cmd), &cmd, NULL, NULL, NULL); } static bt_status_t start_voice_recognition_real(bt_bdaddr_t *bd_addr) { struct hal_cmd_handsfree_start_vr cmd; DBG(""); if (!interface_ready()) return BT_STATUS_NOT_READY; memset(&cmd, 0, sizeof(cmd)); if (bd_addr) memcpy(cmd.bdaddr, bd_addr, sizeof(cmd.bdaddr)); return hal_ipc_cmd(HAL_SERVICE_ID_HANDSFREE, HAL_OP_HANDSFREE_START_VR, sizeof(cmd), &cmd, NULL, NULL, NULL); } #if ANDROID_VERSION >= PLATFORM_VER(5, 0, 0) static bt_status_t start_voice_recognition(bt_bdaddr_t *bd_addr) { return start_voice_recognition_real(bd_addr); } #else static bt_status_t start_voice_recognition(void) { return start_voice_recognition_real(NULL); } #endif static bt_status_t stop_voice_recognition_real(bt_bdaddr_t *bd_addr) { struct hal_cmd_handsfree_stop_vr cmd; DBG(""); if (!interface_ready()) return BT_STATUS_NOT_READY; memset(&cmd, 0, sizeof(cmd)); if (bd_addr) memcpy(cmd.bdaddr, bd_addr, sizeof(cmd.bdaddr)); return hal_ipc_cmd(HAL_SERVICE_ID_HANDSFREE, HAL_OP_HANDSFREE_STOP_VR, sizeof(cmd), &cmd, NULL, NULL, NULL); } #if ANDROID_VERSION >= PLATFORM_VER(5, 0, 0) static bt_status_t stop_voice_recognition(bt_bdaddr_t *bd_addr) { return stop_voice_recognition_real(bd_addr); } #else static bt_status_t stop_voice_recognition(void) { return stop_voice_recognition_real(NULL); } #endif static bt_status_t volume_control_real(bthf_volume_type_t type, int volume, bt_bdaddr_t *bd_addr) { struct hal_cmd_handsfree_volume_control cmd; DBG(""); if (!interface_ready()) return BT_STATUS_NOT_READY; memset(&cmd, 0, sizeof(cmd)); cmd.type = type; cmd.volume = volume; if (bd_addr) memcpy(cmd.bdaddr, bd_addr, sizeof(cmd.bdaddr)); return hal_ipc_cmd(HAL_SERVICE_ID_HANDSFREE, HAL_OP_HANDSFREE_VOLUME_CONTROL, sizeof(cmd), &cmd, NULL, NULL, NULL); } #if ANDROID_VERSION >= PLATFORM_VER(5, 0, 0) static bt_status_t volume_control(bthf_volume_type_t type, int volume, bt_bdaddr_t *bd_addr) { return volume_control_real(type, volume, bd_addr); } #else static bt_status_t volume_control(bthf_volume_type_t type, int volume) { return volume_control_real(type, volume, NULL); } #endif static bt_status_t device_status_notification(bthf_network_state_t state, bthf_service_type_t type, int signal, int battery) { struct hal_cmd_handsfree_device_status_notif cmd; DBG(""); if (!interface_ready()) return BT_STATUS_NOT_READY; cmd.state = state; cmd.type = type; cmd.signal = signal; cmd.battery = battery; return hal_ipc_cmd(HAL_SERVICE_ID_HANDSFREE, HAL_OP_HANDSFREE_DEVICE_STATUS_NOTIF, sizeof(cmd), &cmd, NULL, NULL, NULL); } static bt_status_t cops_response_real(const char *cops, bt_bdaddr_t *bd_addr) { char buf[IPC_MTU]; struct hal_cmd_handsfree_cops_response *cmd = (void *) buf; size_t len; DBG(""); if (!interface_ready()) return BT_STATUS_NOT_READY; if (!cops) return BT_STATUS_PARM_INVALID; memset(cmd, 0, sizeof(*cmd)); if (bd_addr) memcpy(cmd->bdaddr, bd_addr, sizeof(cmd->bdaddr)); /* Size of cmd.buf */ cmd->len = strlen(cops) + 1; memcpy(cmd->buf, cops, cmd->len); len = sizeof(*cmd) + cmd->len; return hal_ipc_cmd(HAL_SERVICE_ID_HANDSFREE, HAL_OP_HANDSFREE_COPS_RESPONSE, len, cmd, NULL, NULL, NULL); } #if ANDROID_VERSION >= PLATFORM_VER(5, 0, 0) static bt_status_t cops_response(const char *cops, bt_bdaddr_t *bd_addr) { return cops_response_real(cops, bd_addr); } #else static bt_status_t cops_response(const char *cops) { return cops_response_real(cops, NULL); } #endif static bt_status_t cind_response_real(int svc, int num_active, int num_held, bthf_call_state_t state, int signal, int roam, int batt_chg, bt_bdaddr_t *bd_addr) { struct hal_cmd_handsfree_cind_response cmd; DBG(""); if (!interface_ready()) return BT_STATUS_NOT_READY; memset(&cmd, 0, sizeof(cmd)); if (bd_addr) memcpy(cmd.bdaddr, bd_addr, sizeof(cmd.bdaddr)); cmd.svc = svc; cmd.num_active = num_active; cmd.num_held = num_held; cmd.state = state; cmd.signal = signal; cmd.roam = roam; cmd.batt_chg = batt_chg; return hal_ipc_cmd(HAL_SERVICE_ID_HANDSFREE, HAL_OP_HANDSFREE_CIND_RESPONSE, sizeof(cmd), &cmd, NULL, NULL, NULL); } #if ANDROID_VERSION >= PLATFORM_VER(5, 0, 0) static bt_status_t cind_response(int svc, int num_active, int num_held, bthf_call_state_t state, int signal, int roam, int batt_chg, bt_bdaddr_t *bd_addr) { return cind_response_real(svc, num_active, num_held, state, signal, roam, batt_chg, bd_addr); } #else static bt_status_t cind_response(int svc, int num_active, int num_held, bthf_call_state_t state, int signal, int roam, int batt_chg) { return cind_response_real(svc, num_active, num_held, state, signal, roam, batt_chg, NULL); } #endif static bt_status_t formatted_at_response_real(const char *rsp, bt_bdaddr_t *bd_addr) { char buf[IPC_MTU]; struct hal_cmd_handsfree_formatted_at_response *cmd = (void *) buf; size_t len; DBG(""); if (!interface_ready()) return BT_STATUS_NOT_READY; if (!rsp) return BT_STATUS_PARM_INVALID; memset(cmd, 0, sizeof(*cmd)); if (bd_addr) memcpy(cmd->bdaddr, bd_addr, sizeof(cmd->bdaddr)); cmd->len = strlen(rsp) + 1; memcpy(cmd->buf, rsp, cmd->len); len = sizeof(*cmd) + cmd->len; return hal_ipc_cmd(HAL_SERVICE_ID_HANDSFREE, HAL_OP_HANDSFREE_FORMATTED_AT_RESPONSE, len, cmd, NULL, NULL, NULL); } #if ANDROID_VERSION >= PLATFORM_VER(5, 0, 0) static bt_status_t formatted_at_response(const char *rsp, bt_bdaddr_t *bd_addr) { return formatted_at_response_real(rsp, bd_addr); } #else static bt_status_t formatted_at_response(const char *rsp) { return formatted_at_response_real(rsp, NULL); } #endif static bt_status_t at_response_real(bthf_at_response_t response, int error, bt_bdaddr_t *bd_addr) { struct hal_cmd_handsfree_at_response cmd; DBG(""); if (!interface_ready()) return BT_STATUS_NOT_READY; if (bd_addr) memcpy(cmd.bdaddr, bd_addr, sizeof(cmd.bdaddr)); memset(&cmd, 0, sizeof(cmd)); cmd.response = response; cmd.error = error; return hal_ipc_cmd(HAL_SERVICE_ID_HANDSFREE, HAL_OP_HANDSFREE_AT_RESPONSE, sizeof(cmd), &cmd, NULL, NULL, NULL); } #if ANDROID_VERSION >= PLATFORM_VER(5, 0, 0) static bt_status_t at_response(bthf_at_response_t response, int error, bt_bdaddr_t *bd_addr) { return at_response_real(response, error, bd_addr); } #else static bt_status_t at_response(bthf_at_response_t response, int error) { return at_response_real(response, error, NULL); } #endif static bt_status_t clcc_response_real(int index, bthf_call_direction_t dir, bthf_call_state_t state, bthf_call_mode_t mode, bthf_call_mpty_type_t mpty, const char *number, bthf_call_addrtype_t type, bt_bdaddr_t *bd_addr) { char buf[IPC_MTU]; struct hal_cmd_handsfree_clcc_response *cmd = (void *) buf; size_t len; DBG(""); if (!interface_ready()) return BT_STATUS_NOT_READY; memset(cmd, 0, sizeof(*cmd)); if (bd_addr) memcpy(cmd->bdaddr, bd_addr, sizeof(cmd->bdaddr)); cmd->index = index; cmd->dir = dir; cmd->state = state; cmd->mode = mode; cmd->mpty = mpty; cmd->type = type; if (number) { cmd->number_len = strlen(number) + 1; memcpy(cmd->number, number, cmd->number_len); } else { cmd->number_len = 0; } len = sizeof(*cmd) + cmd->number_len; return hal_ipc_cmd(HAL_SERVICE_ID_HANDSFREE, HAL_OP_HANDSFREE_CLCC_RESPONSE, len, cmd, NULL, NULL, NULL); } #if ANDROID_VERSION >= PLATFORM_VER(5, 0, 0) static bt_status_t clcc_response(int index, bthf_call_direction_t dir, bthf_call_state_t state, bthf_call_mode_t mode, bthf_call_mpty_type_t mpty, const char *number, bthf_call_addrtype_t type, bt_bdaddr_t *bd_addr) { return clcc_response_real(index, dir, state, mode, mpty, number, type, bd_addr); } #else static bt_status_t clcc_response(int index, bthf_call_direction_t dir, bthf_call_state_t state, bthf_call_mode_t mode, bthf_call_mpty_type_t mpty, const char *number, bthf_call_addrtype_t type) { return clcc_response_real(index, dir, state, mode, mpty, number, type, NULL); } #endif static bt_status_t phone_state_change(int num_active, int num_held, bthf_call_state_t state, const char *number, bthf_call_addrtype_t type) { char buf[IPC_MTU]; struct hal_cmd_handsfree_phone_state_change *cmd = (void *) buf; size_t len; DBG(""); if (!interface_ready()) return BT_STATUS_NOT_READY; cmd->num_active = num_active; cmd->num_held = num_held; cmd->state = state; cmd->type = type; if (number) { cmd->number_len = strlen(number) + 1; memcpy(cmd->number, number, cmd->number_len); } else { cmd->number_len = 0; } len = sizeof(*cmd) + cmd->number_len; return hal_ipc_cmd(HAL_SERVICE_ID_HANDSFREE, HAL_OP_HANDSFREE_PHONE_STATE_CHANGE, len, cmd, NULL, NULL, NULL); } static void cleanup(void) { struct hal_cmd_unregister_module cmd; DBG(""); if (!interface_ready()) return; cmd.service_id = HAL_SERVICE_ID_HANDSFREE; hal_ipc_cmd(HAL_SERVICE_ID_CORE, HAL_OP_UNREGISTER_MODULE, sizeof(cmd), &cmd, NULL, NULL, NULL); hal_ipc_unregister(HAL_SERVICE_ID_HANDSFREE); cbs = NULL; } #if ANDROID_VERSION >= PLATFORM_VER(5, 0, 0) static bt_status_t configure_wbs(bt_bdaddr_t *bd_addr, bthf_wbs_config_t config) { struct hal_cmd_handsfree_configure_wbs cmd; DBG("%u", config); if (!interface_ready()) return BT_STATUS_NOT_READY; if (!bd_addr) return BT_STATUS_PARM_INVALID; memcpy(cmd.bdaddr, bd_addr, sizeof(cmd.bdaddr)); cmd.config = config; return hal_ipc_cmd(HAL_SERVICE_ID_HANDSFREE, HAL_OP_HANDSFREE_CONFIGURE_WBS, sizeof(cmd), &cmd, NULL, NULL, NULL); } #endif static bthf_interface_t iface = { .size = sizeof(iface), .init = init, .connect = handsfree_connect, .disconnect = disconnect, .connect_audio = connect_audio, .disconnect_audio = disconnect_audio, .start_voice_recognition = start_voice_recognition, .stop_voice_recognition = stop_voice_recognition, .volume_control = volume_control, .device_status_notification = device_status_notification, .cops_response = cops_response, .cind_response = cind_response, .formatted_at_response = formatted_at_response, .at_response = at_response, .clcc_response = clcc_response, .phone_state_change = phone_state_change, .cleanup = cleanup, #if ANDROID_VERSION >= PLATFORM_VER(5, 0, 0) .configure_wbs = configure_wbs, #endif }; bthf_interface_t *bt_get_handsfree_interface(void) { return &iface; } bluez-5.82/android/PaxHeaders/ipc.h0000644000000000000000000000005014015011623014203 xustar0020 atime=1743516866 20 ctime=1743591278 bluez-5.82/android/ipc.h0000644000000000000000000000215714015011623013671 0ustar00rootroot/* SPDX-License-Identifier: LGPL-2.1-or-later */ /* * * BlueZ - Bluetooth protocol stack for Linux * * Copyright (C) 2013-2014 Intel Corporation. All rights reserved. * * */ struct ipc_handler { void (*handler) (const void *buf, uint16_t len); bool var_len; size_t data_len; }; struct ipc; typedef void (*ipc_disconnect_cb) (void *data); struct ipc *ipc_init(const char *path, size_t size, int max_service_id, bool notifications, ipc_disconnect_cb cb, void *cb_data); void ipc_cleanup(struct ipc *ipc); void ipc_send_rsp(struct ipc *ipc, uint8_t service_id, uint8_t opcode, uint8_t status); void ipc_send_rsp_full(struct ipc *ipc, uint8_t service_id, uint8_t opcode, uint16_t len, void *param, int fd); void ipc_send_notif(struct ipc *ipc, uint8_t service_id, uint8_t opcode, uint16_t len, void *param); void ipc_send_notif_with_fd(struct ipc *ipc, uint8_t service_id, uint8_t opcode, uint16_t len, void *param, int fd); void ipc_register(struct ipc *ipc, uint8_t service, const struct ipc_handler *handlers, uint8_t size); void ipc_unregister(struct ipc *ipc, uint8_t service); bluez-5.82/android/PaxHeaders/pics-avrcp.txt0000644000000000000000000000005012537515745016114 xustar0020 atime=1743516876 20 ctime=1743591288 bluez-5.82/android/pics-avrcp.txt0000644000000000000000000010664012537515745015604 0ustar00rootrootAVRCP PICS for the PTS tool. PTS version: 6.1 * - different than PTS defaults # - not yet implemented/supported M - mandatory if such role selected O - optional Roles ------------------------------------------------------------------------------- Parameter Name Selected Description ------------------------------------------------------------------------------- TSPC_AVRCP_1_1 True (*) Role: Controller (CT) (C.1) TSPC_AVRCP_1_2 True (*) Role: Target (TG) (C.1) ------------------------------------------------------------------------------- C.1: Mandatory to support at least one of the defined roles. ------------------------------------------------------------------------------- Controller Features ------------------------------------------------------------------------------- Parameter Name Selected Description ------------------------------------------------------------------------------- TSPC_AVRCP_2_1 False (*) CT: Initiating connection establishment (M) TSPC_AVRCP_2_2 False (*) CT: Accepting connection establishment for control initiated by TG (M) TSPC_AVRCP_2_3 False (*) CT: Initiating connection release (M) TSPC_AVRCP_2_4 False (*) CT: Accepting connection release for control initiated by TG (M) TSPC_AVRCP_2_5 False CT: Sending UNIT INFO (O) TSPC_AVRCP_2_6 False CT: Sending SUBUNIT INFO (O) TSPC_AVRCP_2_7 False CT: Sending PASS THROUGH command category 1 (C.1) TSPC_AVRCP_2_8 False CT: Sending PASS THROUGH command category 2 (C.1) TSPC_AVRCP_2_9 False CT: Sending PASS THROUGH command category 3 (C.1) TSPC_AVRCP_2_10 False CT: Sending PASS THROUGH command category 4 (C.1) TSPC_AVRCP_2_11 False CT: Get Capabilities (O) TSPC_AVRCP_2_12 False CT: List Player Application Setting Attributes (C.9) TSPC_AVRCP_2_13 False CT: List Player Application Setting Values (O) TSPC_AVRCP_2_14 False CT: Get Current Player Application Setting (C.10) TSPC_AVRCP_2_15 False CT: Set Player Application Setting Value (C.10) TSPC_AVRCP_2_16 False CT: Get Player Application Setting Attribute Text (O) TSPC_AVRCP_2_17 False CT: Get Player Application Setting Value Text (O) TSPC_AVRCP_2_18 False CT: Inform Displayable Character Set (O) TSPC_AVRCP_2_19 False CT: Inform Battery Status of CT (O) TSPC_AVRCP_2_20 False CT: Get Element Attributes (O) TSPC_AVRCP_2_21 False CT: Get Play Status (O) TSPC_AVRCP_2_22 False CT: Register Notification (C.11) TSPC_AVRCP_2_23 False CT: Request Continuing Response (C.2) TSPC_AVRCP_2_24 False CT: Abort Continuing Response (C.2) TSPC_AVRCP_2_25 False CT: Next Group (C.12) TSPC_AVRCP_2_26 False CT: Previous Group (C.12) TSPC_AVRCP_2_27 False CT: Media Player Selection (O) TSPC_AVRCP_2_28 False CT: SetAddressedPlayer (O) TSPC_AVRCP_2_29 False CT: GetFolderItems(MediaPlayerList) (C.5) TSPC_AVRCP_2_29b False CT: GetTotalNumberOfItems(MediaPlayerList) (C.5) TSPC_AVRCP_2_30 False CT: EVENT_AVAILABLE_PLAYERS_CHANGED (O) TSPC_AVRCP_2_31 False CT: EVENT_ADDRESSED_PLAYER_CHANGED (O) TSPC_AVRCP_2_32 False CT: Browsing (O) TSPC_AVRCP_2_33 False CT: SetBrowsedPlayer (C.4) TSPC_AVRCP_2_34 False CT: ChangePath (C.4) TSPC_AVRCP_2_35 False CT: GetFolderItems(Filesystem) (C.4) TSPC_AVRCP_2_35b False CT: GetTotalNumberOfItems(Filesystem) (C.4) TSPC_AVRCP_2_36 False CT: GetItemAttributes (O) TSPC_AVRCP_2_37 False CT: PlayItem(Filesystem) (C.4) TSPC_AVRCP_2_38 False CT: EVENT_UIDS_CHANGED (O) TSPC_AVRCP_2_39 False CT: Searching (O) TSPC_AVRCP_2_40 False CT: Search (C.7) TSPC_AVRCP_2_41 False CT: GetFolderItems(Search Results) (C.7) TSPC_AVRCP_2_41b False CT: GetTotalNumberOfItems(Search Results) (C.7) TSPC_AVRCP_2_42 False CT: PlayItem(SearchResultList) (C.7) TSPC_AVRCP_2_43 False CT: NowPlaying (C.8) TSPC_AVRCP_2_44 False CT: GetFolderItems(NowPlayingList) (C.8) TSPC_AVRCP_2_44b False CT: GetTotalNumberOfItems(NowPlayingList) (C.8) TSPC_AVRCP_2_45 False CT: PlayItem(NowPlayingList) (C.8) TSPC_AVRCP_2_46 False CT: AddToNowPlaying (O) TSPC_AVRCP_2_47 False CT: EVENT_NOW_PLAYING_CONTENT_CHANGED (O) TSPC_AVRCP_2_48 False CT: Playable Folders (O) TSPC_AVRCP_2_49 True (*) CT: Absolute Volume (C.3) TSPC_AVRCP_2_50 True (*) CT: SetAbsoluteVolume (C.3) TSPC_AVRCP_2_51 True (*) CT: NotifyVolumeChange (C.3) TSPC_AVRCP_2_52 False (*) CT: Discoverable Mode (M) TSPC_AVRCP_2_53 False CT: PASSTHROUGH operation supporting press and hold (O) TSPC_AVRCP_2_54 False CT: Cover Art (O) TSPC_AVRCP_2_55 False CT: GetImageProperties (C.14) TSPC_AVRCP_2_56 False CT: GetImage (C.13) TSPC_AVRCP_2_57 False CT: GetLinkedThumbnail (C.13) ------------------------------------------------------------------------------- C.1: Mandatory to support at least one of the defined categories (TSPC_AVRCP_2_7 through TSPC_AVRCP_2_10). C.2: Mandatory to support at least one of TSPC_AVRCP_2_23 or TSPC_AVRC_2_24 if TSPC_AVRCP_2_20 is supported, otherwise Optional. C.3: Mandatory if TSPC_AVRCP_2_8 is supported, otherwise Excluded. C.4: Mandatory if TSPC_AVRCP_2_32 is supported, otherwise Excluded. C.5: Mandatory if TSPC_AVRCP_2_27 is supported, otherwise Excluded. C.7: Mandatory if item TSPC_AVRCP_2_39 is supported, Excluded otherwise. C.8: Mandatory if TSPC_AVRCP_2_32 is supported, otherwise Excluded. C.9: Mandatory to support if Player Application Settings feature is supported. If any item TSPC_AVRCP_2_13 through TSPC_AVRCP_2_15 is supported it is required to claim support for this feature in accordance with Player Application Settings support requirements, otherwise Optional. C.10: Mandatory to support either Get or Set Player Application Settings (TSPC_AVRCP_2_14 or TSPC_AVRCP_2_15) if List Player Application Setting Attributes (TSPC_AVRCP_2_12) is supported. Either TSPC_AVRCP_2_14 or TSPC_AVRCP_2_15 must be supported if Player Application Settings feature is supported, in accordance with Player Application Settings support requirements. C.11: Mandatory if TSPC_AVRCP_2_20 or TSPC_AVRCP_2_49 is supported, otherwise Optional. C.12: Mandatory if Basic Group Navigation Feature supported. If any item TSPC_AVRCP_2_25 or TSPC_AVRCP_2_26 is supported it is mandatory to support both, in accordance with Basic Group Navigation support requirements, otherwise Excluded. C.13: Mandatory to support at least one of the functions if TSPC_AVRCP_2_54 (Cover Art) is support, otherwise Excluded. C.14: Optional if TSPC_AVRCP_2_54 (Cover Art) is supported, otherwise Excluded. ------------------------------------------------------------------------------- Controller Profile Version ------------------------------------------------------------------------------- Parameter Name Selected Description ------------------------------------------------------------------------------- TSPC_AVRCP_2b_1 False CT: AVRCP v1.0 (C.1) TSPC_AVRCP_2b_2 False CT: AVRCP v1.3 (C.1) TSPC_AVRCP_2b_3 False CT: AVRCP v1.4 (C.1) TSPC_AVRCP_2b_4 False CT: AVRCP v1.5 (C.1) TSPC_AVRCP_2b_5 False CT: AVRCP v1.6 (C.1) ------------------------------------------------------------------------------- C.1: It is mandatory to support at least one of the profile versions if Controller role supported (SPC_AVRCP_1_1). ------------------------------------------------------------------------------- Operation_id of Category 1 for CT ------------------------------------------------------------------------------- Parameter Name Selected Description ------------------------------------------------------------------------------- TSPC_AVRCP_3_1 False CT: category 1 - Operation id: 0 (C.1) TSPC_AVRCP_3_2 False CT: category 1 - Operation id: 1 (C.1) TSPC_AVRCP_3_3 False CT: category 1 - Operation id: 2 (C.1) TSPC_AVRCP_3_4 False CT: category 1 - Operation id: 3 (C.1) TSPC_AVRCP_3_5 False CT: category 1 - Operation id: 4 (C.1) TSPC_AVRCP_3_6 False CT: category 1 - Operation id: 5 (C.1) TSPC_AVRCP_3_7 False CT: category 1 - Operation id: 6 (C.1) TSPC_AVRCP_3_8 False CT: category 1 - Operation id: 7 (C.1) TSPC_AVRCP_3_9 False CT: category 1 - Operation id: 8 (C.1) TSPC_AVRCP_3_10 False CT: category 1 - Operation id: 9 (C.1) TSPC_AVRCP_3_11 False CT: category 1 - Operation id: dot (C.1) TSPC_AVRCP_3_12 False CT: category 1 - Operation id: enter (C.1) TSPC_AVRCP_3_13 False CT: category 1 - Operation id: clear (C.1) TSPC_AVRCP_3_14 False CT: category 1 - Operation id: sound_select (C.1) TSPC_AVRCP_3_15 False CT: category 1 - Operation id: input_select (C.1) TSPC_AVRCP_3_16 False CT: category 1 - Operation id: display_information (C.1) TSPC_AVRCP_3_17 False CT: category 1 - Operation id: help (C.1) TSPC_AVRCP_3_18 False CT: category 1 - Operation id: power (C.1) TSPC_AVRCP_3_19 False CT: category 1 - Operation id: play (C.1) TSPC_AVRCP_3_20 False CT: category 1 - Operation id: stop (C.1) TSPC_AVRCP_3_21 False CT: category 1 - Operation id: pause (C.1) TSPC_AVRCP_3_22 False CT: category 1 - Operation id: record (C.1) TSPC_AVRCP_3_23 False CT: category 1 - Operation id: rewind (C.1) TSPC_AVRCP_3_24 False CT: category 1 - Operation id: fast_forward (C.1) TSPC_AVRCP_3_25 False CT: category 1 - Operation id: eject (C.1) TSPC_AVRCP_3_26 False CT: category 1 - Operation id: forward (C.1) TSPC_AVRCP_3_27 False CT: category 1 - Operation id: backward (C.1) TSPC_AVRCP_3_28 False CT: category 1 - Operation id: angle (C.1) TSPC_AVRCP_3_29 False CT: category 1 - Operation id: subpicture (C.1) TSPC_AVRCP_3_30 False CT: category 1 - Operation id: F1 (C.1) TSPC_AVRCP_3_31 False CT: category 1 - Operation id: F2 (C.1) TSPC_AVRCP_3_32 False CT: category 1 - Operation id: F3 (C.1) TSPC_AVRCP_3_33 False CT: category 1 - Operation id: F4 (C.1) TSPC_AVRCP_3_34 False CT: category 1 - Operation id: vendor_unique (C.1) ------------------------------------------------------------------------------- C.1: Mandatory to support at least one of these operation_ids if the device supports category 1 (TSPC_AVRCP_2_7). ------------------------------------------------------------------------------- Operation_id of category 2 for CT ------------------------------------------------------------------------------- Parameter Name Selected Description ------------------------------------------------------------------------------- TSPC_AVRCP_4_1 False CT: category 2 - Operation id: 0 (C.1) TSPC_AVRCP_4_2 False CT: category 2 - Operation id: 1 (C.1) TSPC_AVRCP_4_3 False CT: category 2 - Operation id: 2 (C.1) TSPC_AVRCP_4_4 False CT: category 2 - Operation id: 3 (C.1) TSPC_AVRCP_4_5 False CT: category 2 - Operation id: 4 (C.1) TSPC_AVRCP_4_6 False CT: category 2 - Operation id: 5 (C.1) TSPC_AVRCP_4_7 False CT: category 2 - Operation id: 6 (C.1) TSPC_AVRCP_4_8 False CT: category 2 - Operation id: 7 (C.1) TSPC_AVRCP_4_9 False CT: category 2 - Operation id: 8 (C.1) TSPC_AVRCP_4_10 False CT: category 2 - Operation id: 9 (C.1) TSPC_AVRCP_4_11 False CT: category 2 - Operation id: dot (C.1) TSPC_AVRCP_4_12 False CT: category 2 - Operation id: enter (C.1) TSPC_AVRCP_4_13 False CT: category 2 - Operation id: clear (C.1) TSPC_AVRCP_4_14 False CT: category 2 - Operation id: sound_select (C.1) TSPC_AVRCP_4_15 False CT: category 2 - Operation id: input_select (C.1) TSPC_AVRCP_4_16 False CT: category 2 - Operation id: display_information (C.1) TSPC_AVRCP_4_17 False CT: category 2 - Operation id: help (C.1) TSPC_AVRCP_4_18 False CT: category 2 - Operation id: power (C.1) TSPC_AVRCP_4_19 False CT: category 2 - Operation id: volume_up (C.1) TSPC_AVRCP_4_20 False CT: category 2 - Operation id: volume_down (C.1) TSPC_AVRCP_4_21 False CT: category 2 - Operation id: mute (C.1) TSPC_AVRCP_4_22 False CT: category 2 - Operation id: F1 (C.1) TSPC_AVRCP_4_23 False CT: category 2 - Operation id: F2 (C.1) TSPC_AVRCP_4_24 False CT: category 2 - Operation id: F3 (C.1) TSPC_AVRCP_4_25 False CT: category 2 - Operation id: F4 (C.1) TSPC_AVRCP_4_26 False CT: category 2 - Operation id: vendor_unique (C.1) ------------------------------------------------------------------------------- C.1: Mandatory to support at least one of these operation_ids if the device supports category 2 (TSPC_AVRCP_2_8). ------------------------------------------------------------------------------- Operation_id of category 3 for CT ------------------------------------------------------------------------------- Parameter Name Selected Description ------------------------------------------------------------------------------- TSPC_AVRCP_5_1 False CT: category 3 - Operation id: 0 (C.1) TSPC_AVRCP_5_2 False CT: category 3 - Operation id: 1 (C.1) TSPC_AVRCP_5_3 False CT: category 3 - Operation id: 2 (C.1) TSPC_AVRCP_5_4 False CT: category 3 - Operation id: 3 (C.1) TSPC_AVRCP_5_5 False CT: category 3 - Operation id: 4 (C.1) TSPC_AVRCP_5_6 False CT: category 3 - Operation id: 5 (C.1) TSPC_AVRCP_5_7 False CT: category 3 - Operation id: 6 (C.1) TSPC_AVRCP_5_8 False CT: category 3 - Operation id: 7 (C.1) TSPC_AVRCP_5_9 False CT: category 3 - Operation id: 8 (C.1) TSPC_AVRCP_5_10 False CT: category 3 - Operation id: 9 (C.1) TSPC_AVRCP_5_11 False CT: category 3 - Operation id: dot (C.1) TSPC_AVRCP_5_12 False CT: category 3 - Operation id: enter (C.1) TSPC_AVRCP_5_13 False CT: category 3 - Operation id: clear (C.1) TSPC_AVRCP_5_14 False CT: category 3 - Operation id: channel up (C.1) TSPC_AVRCP_5_15 False CT: category 3 - Operation id: channel down (C.1) TSPC_AVRCP_5_16 False CT: category 3 - Operation id: previous channel (C.1) TSPC_AVRCP_5_17 False CT: category 3 - Operation id: sound_select (C.1) TSPC_AVRCP_5_18 False CT: category 3 - Operation id: input_select (C.1) TSPC_AVRCP_5_19 False CT: category 3 - Operation id: display_information (C.1) TSPC_AVRCP_5_20 False CT: category 3 - Operation id: help (C.1) TSPC_AVRCP_5_21 False CT: category 3 - Operation id: power (C.1) TSPC_AVRCP_5_22 False CT: category 3 - Operation id: angle (C.1) TSPC_AVRCP_5_23 False CT: category 3 - Operation id: subpicture(C.1) TSPC_AVRCP_5_24 False CT: category 3 - Operation id: F1 (C.1) TSPC_AVRCP_5_25 False CT: category 3 - Operation id: F2 (C.1) TSPC_AVRCP_5_26 False CT: category 3 - Operation id: F3 (C.1) TSPC_AVRCP_5_27 False CT: category 3 - Operation id: F4 (C.1) TSPC_AVRCP_5_28 False CT: category 3 - Operation id: vendor_unique (C.1) ------------------------------------------------------------------------------- C.1: Mandatory to support at least one of these operation_ids if the device supports category 3 (TSPC_AVRCP_2_9). ------------------------------------------------------------------------------- Operation_id of category 4 for CT ------------------------------------------------------------------------------- Parameter Name Selected Description ------------------------------------------------------------------------------- TSPC_AVRCP_6_1 False CT: category 4 - Operation id: select (C.1) TSPC_AVRCP_6_2 False CT: category 4 - Operation id: up (C.1) TSPC_AVRCP_6_3 False CT: category 4 - Operation id: down (C.1) TSPC_AVRCP_6_4 False CT: category 4 - Operation id: left (C.1) TSPC_AVRCP_6_5 False CT: category 4 - Operation id: right (C.1) TSPC_AVRCP_6_6 False CT: category 4 - Operation id: right up (C.1) TSPC_AVRCP_6_7 False CT: category 4 - Operation id: right down (C.1) TSPC_AVRCP_6_8 False CT: category 4 - Operation id: left up (C.1) TSPC_AVRCP_6_9 False CT: category 4 - Operation id: left down (C.1) TSPC_AVRCP_6_10 False CT: category 4 - Operation id: root menu (C.1) TSPC_AVRCP_6_11 False CT: category 4 - Operation id: setup menu (C.1) TSPC_AVRCP_6_12 False CT: category 4 - Operation id: contents menu (C.1) TSPC_AVRCP_6_13 False CT: category 4 - Operation id: favorite menu (C.1) TSPC_AVRCP_6_14 False CT: category 4 - Operation id: exit (C.1) TSPC_AVRCP_6_15 False CT: category 4 - Operation id: 0 (C.1) TSPC_AVRCP_6_16 False CT: category 4 - Operation id: 1 (C.1) TSPC_AVRCP_6_17 False CT: category 4 - Operation id: 2 (C.1) TSPC_AVRCP_6_18 False CT: category 4 - Operation id: 3 (C.1) TSPC_AVRCP_6_19 False CT: category 4 - Operation id: 4 (C.1) TSPC_AVRCP_6_20 False CT: category 4 - Operation id: 5 (C.1) TSPC_AVRCP_6_21 False CT: category 4 - Operation id: 6 (C.1) TSPC_AVRCP_6_22 False CT: category 4 - Operation id: 7 (C.1) TSPC_AVRCP_6_23 False CT: category 4 - Operation id: 8 (C.1) TSPC_AVRCP_6_24 False CT: category 4 - Operation id: 9 (C.1) TSPC_AVRCP_6_25 False CT: category 4 - Operation id: dot (C.1) TSPC_AVRCP_6_26 False CT: category 4 - Operation id: enter (C.1) TSPC_AVRCP_6_27 False CT: category 4 - Operation id: clear (C.1) TSPC_AVRCP_6_28 False CT: category 4 - Operation id: display_information (C.1) TSPC_AVRCP_6_29 False CT: category 4 - Operation id: help (C.1) TSPC_AVRCP_6_30 False CT: category 4 - Operation id: page up (C.1) TSPC_AVRCP_6_31 False CT: category 4 - Operation id: page down (C.1) TSPC_AVRCP_6_32 False CT: category 4 - Operation id: power (C.1) TSPC_AVRCP_6_33 False CT: category 4 - Operation id: F1 (C.1) TSPC_AVRCP_6_34 False CT: category 4 - Operation id: F2 (C.1) TSPC_AVRCP_6_35 False CT: category 4 - Operation id: F3 (C.1) TSPC_AVRCP_6_36 False CT: category 4 - Operation id: F4 (C.1) TSPC_AVRCP_6_37 False CT: category 4 - Operation id: vendor_unique (C.1) ------------------------------------------------------------------------------- C.1: Mandatory to support at least one of these operation_ids if the device supports category 4 (TSPC_AVRCP_2_10). ------------------------------------------------------------------------------- Target Features ------------------------------------------------------------------------------- Parameter Name Selected Description ------------------------------------------------------------------------------- TSPC_AVRCP_7_1 True (*) TG: Initiating connection establishment for Control (O) TSPC_AVRCP_7_2 True TG: Accept connection establishment for Control initiated by CT (M) TSPC_AVRCP_7_3 True (*) TG: Initiating connection release (M) TSPC_AVRCP_7_4 True TG: Accepting connection release (M) TSPC_AVRCP_7_5 True TG: Receiving UNIT INFO (M) TSPC_AVRCP_7_6 True TG: Receiving SUBUNIT INFO (M) TSPC_AVRCP_7_7 True (*) TG: Receiving PASS THROUGH command category 1 (C.1) TSPC_AVRCP_7_8 True (*) TG: Receiving PASS THROUGH command category 2 (C.1) TSPC_AVRCP_7_9 False TG: Receiving PASS THROUGH command category 3 (C.1) TSPC_AVRCP_7_10 False TG: Receiving PASS THROUGH command category 4 (C.1) TSPC_AVRCP_7_11 True (*) TG: Get Capabilities Response (C.3) TSPC_AVRCP_7_12 False TG: List Player Application Settings Attributes Response (C.14) TSPC_AVRCP_7_13 False TG: List Player Application Setting Values Response (C.14) TSPC_AVRCP_7_14 False TG: Get Current Player Application Settings Value Response (C.14) TSPC_AVRCP_7_15 False TG: Set Player Application Setting Value Response (C.14) TSPC_AVRCP_7_16 False TG: Get Player Application Setting Attribute Text Response (O) TSPC_AVRCP_7_17 False TG: Get Player Application Setting Value Text Response (O) TSPC_AVRCP_7_18 False TG: Inform Displayable Character Set Response (O) TSPC_AVRCP_7_19 False TG: Inform Battery Status Of CT Response (O) TSPC_AVRCP_7_20 True (*) TG: Get Element Attributes Response (C.3) TSPC_AVRCP_7_21 True (*) TG: Get Play Status Response (C.2) TSPC_AVRCP_7_22 True (*) TG: Register Notification Response (C.12) TSPC_AVRCP_7_23 True (*) TG: Notify Event Response: PLAYBACK_STATUS_CHANGED (C.4) TSPC_AVRCP_7_24 True (*) TG: Notify Event Response: TRACK_CHANGED (C.4) TSPC_AVRCP_7_25 False TG: Notify Event Response: TRACK_REACHED_END (O) TSPC_AVRCP_7_26 False TG: Notify Event Response: TRACK_REACHED_START (O) TSPC_AVRCP_7_27 False TG: Notify Event Response: PLAYBACK_POS_CHANGED (O) TSPC_AVRCP_7_28 False TG: Notify Event Response: BATT_STATUS_CHANGED (O) TSPC_AVRCP_7_29 False TG: Notify Event Response: SYSTEM_STATUS_CHANGED (O) TSPC_AVRCP_7_30 False TG: Notify Event Response: PLAYER_APPLICATION_SETTING_CHANGED (O) TSPC_AVRCP_7_31 True (*) TG: Request Continuing Response (C.2) TSPC_AVRCP_7_32 True (*) TG: Abort ContinuingResponse Response (C.2) TSPC_AVRCP_7_34 False TG: Next Group (C.15) TSPC_AVRCP_7_35 False TG: Previous Group (C.15) TSPC_AVRCP_7_36 False TG: Media Player Selection (C.8) TSPC_AVRCP_7_37 False TG: SetAddressedPlayer (C.8) TSPC_AVRCP_7_38 False TG: GetFolderItems(MediaPlayerList) (C.8) TSPC_AVRCP_7_38b False TG: GetTotalNumberOfItems(MediaPlayerList) (C.8) TSPC_AVRCP_7_39 False TG: EVENT_AVAILABLE_PLAYERS_CHANGED (C.8) TSPC_AVRCP_7_40 False TG: EVENT_ADDRESSED_PLAYER_CHANGED (C.8) TSPC_AVRCP_7_41 False TG: Supports Multiple Players (O) TSPC_AVRCP_7_42 False TG: Browsing (O) TSPC_AVRCP_7_42a False TG: Initiating connection establishment for browsing channel (O) TSPC_AVRCP_7_43 False TG: SetBrowsedPlayer (C.6) TSPC_AVRCP_7_44 False TG: ChangePath (C.6) TSPC_AVRCP_7_45 False TG: GetFolderItems(Filesystem) (C.6) TSPC_AVRCP_7_45b False TG: GetTotalNumberOfItems(Filesystem) (C.6) TSPC_AVRCP_7_46 False TG: GetItemAttributes (C.6) TSPC_AVRCP_7_47 False TG: PlayItem(Filesystem) (C.6) TSPC_AVRCP_7_48 False TG: EVENT_UIDS_CHANGED (C.9) TSPC_AVRCP_7_49 False TG: Database Aware Players (O) TSPC_AVRCP_7_50 False TG: Searching (O) TSPC_AVRCP_7_51 False TG: Search (C.10) TSPC_AVRCP_7_52 False TG: GetFolderItems(Search Results) (C.10) TSPC_AVRCP_7_52b False TG: GetTotalNumberOfItems(Search Results) (C.10) TSPC_AVRCP_7_53 False TG: PlayItem(SearchResultList) (C.10) TSPC_AVRCP_7_54 False TG: NowPlaying (C.11) TSPC_AVRCP_7_55 False TG: GetFolderItems(NowPlayingList) (C.11) TSPC_AVRCP_7_55b False TG: GetTotalNumberOfItems(NowPlayingList) (C.11) TSPC_AVRCP_7_56 False TG: PlayItem(NowPlayingList) (C.11) TSPC_AVRCP_7_57 False TG: AddToNowPlaying (O) TSPC_AVRCP_7_58 False TG: EVENT_NOW_PLAYING_CONTENT_CHANGED (C.11) TSPC_AVRCP_7_59 False TG: Playable Folders (O) TSPC_AVRCP_7_60 False TG: Absolute Volume (C.5) TSPC_AVRCP_7_61 False TG: SetAbsoluteVolume (C.5) TSPC_AVRCP_7_62 False TG: NotifyVolumeChange (C.5) TSPC_AVRCP_7_63 False TG: Error Response (O) TSPC_AVRCP_7_64 False TG: General Reject (C.13) TSPC_AVRCP_7_65 True TG: Discoverable Mode (M) TSPC_AVRCP_7_66 False TG: PASSTHROUGH operation supporting press and hold (O) TSPC_AVRCP_7_67 False TG: Cover Art (O) TSPC_AVRCP_7_68 False TG: GetImageProperties (C.16) TSPC_AVRCP_7_69 False TG: GetImage (C.16) TSPC_AVRCP_7_70 False TG: GetLinkedThumbnail (C.16) ------------------------------------------------------------------------------- C.1: Mandatory to support at least one of the categories. Supported operation_id's are shown in Table 8 to Table 11. C.2: Mandatory if 7/20 is supported, otherwise Optional. C.3: Mandatory if 7/7 is supported, otherwise Optional. C.4: Mandatory if 7/22 and 7/20 is supported, otherwise Optional. C.5: Mandatory if 7/8 is supported, otherwise Excluded. C.6: Mandatory if 7/42 is supported, otherwise Excluded. C.7: Mandatory if 7/36 is supported, otherwise Excluded. C.8: Mandatory if (7/7 or 7/9) is supported, otherwise Excluded. C.9: Mandatory if 7/49 is supported, otherwise Optional. C.10: Mandatory if 7/50 is supported, otherwise Excluded. C.11: Mandatory if 7/42 is supported, otherwise Optional. C.12: Mandatory if 7/7 or (7/8 AND 7/60) or 7/9 is supported, otherwise Optional C.13: Mandatory if 7/7 or 7/9 or 7/42 is supported, otherwise Optional. C.14: Mandatory if Player Application Settings Feature supported. If any item 7/12 – 7/15 is supported, all items 7/12 – 7/15 shall be supported, in accordance with Player Application Settings Feature support requirements, otherwise Excluded. C.15: Mandatory if Basic Group Navigation Feature supported. If any item 7/34 or 7/35 is supported it is mandatory to support both, in accordance with Basic Group Navigation support requirements, otherwise Excluded. C.16: Mandatory if 7/67 (Cover Art) is supported, otherwise Excluded. ------------------------------------------------------------------------------- Target Profile Version ------------------------------------------------------------------------------- Parameter Name Selected Description ------------------------------------------------------------------------------- TSPC_AVRCP_7b_1 False TG: AVRCP v1.0 (C.1) TSPC_AVRCP_7b_2 False TG: AVRCP v1.3 (C.1) TSPC_AVRCP_7b_3 False TG: AVRCP v1.4 (C.1) TSPC_AVRCP_7b_4 True (*) TG: AVRCP v1.5 (C.1) TSPC_AVRCP_7b_5 False TG: AVRCP v1.6 (C.1) ------------------------------------------------------------------------------- C.1: It is mandatory to support at least one of the profile versions. ------------------------------------------------------------------------------- operation_id of category 1 for TG ------------------------------------------------------------------------------- Parameter Name Selected Description ------------------------------------------------------------------------------- TSPC_AVRCP_8_1 False TG: category 1 - Operation id: 0 (O) TSPC_AVRCP_8_2 False TG: category 1 - Operation id: 1 (O) TSPC_AVRCP_8_3 False TG: category 1 - Operation id: 2 (O) TSPC_AVRCP_8_4 False TG: category 1 - Operation id: 3 (O) TSPC_AVRCP_8_5 False TG: category 1 - Operation id: 4 (O) TSPC_AVRCP_8_6 False TG: category 1 - Operation id: 5 (O) TSPC_AVRCP_8_7 False TG: category 1 - Operation id: 6 (O) TSPC_AVRCP_8_8 False TG: category 1 - Operation id: 7 (O) TSPC_AVRCP_8_9 False TG: category 1 - Operation id: 8 (O) TSPC_AVRCP_8_10 False TG: category 1 - Operation id: 9 (O) TSPC_AVRCP_8_11 False TG: category 1 - Operation id: dot (O) TSPC_AVRCP_8_12 False TG: category 1 - Operation id: enter (O) TSPC_AVRCP_8_13 False TG: category 1 - Operation id: clear (O) TSPC_AVRCP_8_14 False TG: category 1 - Operation id: sound select (O) TSPC_AVRCP_8_15 False TG: category 1 - Operation id: input select (O) TSPC_AVRCP_8_16 False TG: category 1 - Operation id: display information (O) TSPC_AVRCP_8_17 False TG: category 1 - Operation id: help (O) TSPC_AVRCP_8_18 False TG: category 1 - Operation id: power (O) TSPC_AVRCP_8_19 True TG: category 1 - Operation id: play (M) TSPC_AVRCP_8_20 True TG: category 1 - Operation id: stop (M) TSPC_AVRCP_8_21 True (*) TG: category 1 - Operation id: pause (O) TSPC_AVRCP_8_22 False TG: category 1 - Operation id: record (O) TSPC_AVRCP_8_23 True (*) TG: category 1 - Operation id: rewind (O) TSPC_AVRCP_8_24 True (*) TG: category 1 - Operation id: fast forward (O) TSPC_AVRCP_8_25 False TG: category 1 - Operation id: eject (O) TSPC_AVRCP_8_26 True (*) TG: category 1 - Operation id: forward (O) TSPC_AVRCP_8_27 True (*) TG: category 1 - Operation id: backward (O) TSPC_AVRCP_8_28 False TG: category 1 - Operation id: angle (O) TSPC_AVRCP_8_29 False TG: category 1 - Operation id: subpicture (O) TSPC_AVRCP_8_30 False TG: category 1 - Operation id: F1 (O) TSPC_AVRCP_8_31 False TG: category 1 - Operation id: F2 (O) TSPC_AVRCP_8_32 False TG: category 1 - Operation id: F3 (O) TSPC_AVRCP_8_33 False TG: category 1 - Operation id: F4 (O) TSPC_AVRCP_8_33a False TG: category 1 - Operation id: F5 (O) TSPC_AVRCP_8_34 False TG: category 1 - Operation id: vendor unique (O) ------------------------------------------------------------------------------- operation_id of category 2 for TG ------------------------------------------------------------------------------- Parameter Name Selected Description ------------------------------------------------------------------------------- TSPC_AVRCP_9_1 False TG: category 2 - Operation id: 0 (O) TSPC_AVRCP_9_2 False TG: category 2 - Operation id: 1 (O) TSPC_AVRCP_9_3 False TG: category 2 - Operation id: 2 (O) TSPC_AVRCP_9_4 False TG: category 2 - Operation id: 3 (O) TSPC_AVRCP_9_5 False TG: category 2 - Operation id: 4 (O) TSPC_AVRCP_9_6 False TG: category 2 - Operation id: 5 (O) TSPC_AVRCP_9_7 False TG: category 2 - Operation id: 6 (O) TSPC_AVRCP_9_8 False TG: category 2 - Operation id: 7 (O) TSPC_AVRCP_9_9 False TG: category 2 - Operation id: 8 (O) TSPC_AVRCP_9_10 False TG: category 2 - Operation id: 9 (O) TSPC_AVRCP_9_11 False TG: category 2 - Operation id: dot (O) TSPC_AVRCP_9_12 False TG: category 2 - Operation id: enter (O) TSPC_AVRCP_9_13 False TG: category 2 - Operation id: clear (O) TSPC_AVRCP_9_14 False TG: category 2 - Operation id: sound select (O) TSPC_AVRCP_9_15 False TG: category 2 - Operation id: input select (O) TSPC_AVRCP_9_16 False TG: category 2 - Operation id: display information (O) TSPC_AVRCP_9_17 False TG: category 2 - Operation id: help (O) TSPC_AVRCP_9_18 False TG: category 2 - Operation id: power (O) TSPC_AVRCP_9_19 True TG: category 2 - Operation id: volume up (C.2) TSPC_AVRCP_9_20 True TG: category 2 - Operation id: volume down (C.2) TSPC_AVRCP_9_21 False TG: category 2 - Operation id: mute (O) TSPC_AVRCP_9_24 False TG: category 2 - Operation id: F1 (O) TSPC_AVRCP_9_25 False TG: category 2 - Operation id: F2 (O) TSPC_AVRCP_9_26 False TG: category 2 - Operation id: F3 (O) TSPC_AVRCP_9_27 False TG: category 2 - Operation id: F4 (O) TSPC_AVRCP_9_27a False TG: category 2 - Operation id: F5 (O) TSPC_AVRCP_9_28 False TG: category 2 - Operation id: vendor unique (O) ------------------------------------------------------------------------------- C.2: Mandatory to support if the device supports category 2 (TSPC_AVRCP_7_8). ------------------------------------------------------------------------------- operation_id of category 3 for TG ------------------------------------------------------------------------------- Parameter Name Selected Description ------------------------------------------------------------------------------- TSPC_AVRCP_10_1 False TG: category 3 - Operation id: 0 (O) TSPC_AVRCP_10_2 False TG: category 3 - Operation id: 1 (O) TSPC_AVRCP_10_3 False TG: category 3 - Operation id: 2 (O) TSPC_AVRCP_10_4 False TG: category 3 - Operation id: 3 (O) TSPC_AVRCP_10_5 False TG: category 3 - Operation id: 4 (O) TSPC_AVRCP_10_6 False TG: category 3 - Operation id: 5 (O) TSPC_AVRCP_10_7 False TG: category 3 - Operation id: 6 (O) TSPC_AVRCP_10_8 False TG: category 3 - Operation id: 7 (O) TSPC_AVRCP_10_9 False TG: category 3 - Operation id: 8 (O) TSPC_AVRCP_10_10 False TG: category 3 - Operation id: 9 (O) TSPC_AVRCP_10_11 False TG: category 3 - Operation id: dot (O) TSPC_AVRCP_10_12 False TG: category 3 - Operation id: enter (O) TSPC_AVRCP_10_13 False TG: category 3 - Operation id: clear (O) TSPC_AVRCP_10_14 False (*) TG: category 3 - Operation id: channel up (C.3) TSPC_AVRCP_10_15 False (*) TG: category 3 - Operation id: channel down (C.3) TSPC_AVRCP_10_16 False TG: category 3 - Operation id: previous channel (O) TSPC_AVRCP_10_17 False TG: category 3 - Operation id: sound select (O) TSPC_AVRCP_10_18 False TG: category 3 - Operation id: input select (O) TSPC_AVRCP_10_19 False TG: category 3 - Operation id: display information (O) TSPC_AVRCP_10_20 False TG: category 3 - Operation id: help (O) TSPC_AVRCP_10_21 False TG: category 3 - Operation id: power (O) TSPC_AVRCP_10_21a False TG: category 3 - Operation id: angle (O) TSPC_AVRCP_10_21b False TG: category 3 - Operation id: subpicture (O) TSPC_AVRCP_10_22 False TG: category 3 - Operation id: F1 (O) TSPC_AVRCP_10_23 False TG: category 3 - Operation id: F2 (O) TSPC_AVRCP_10_24 False TG: category 3 - Operation id: F3 (O) TSPC_AVRCP_10_25 False TG: category 3 - Operation id: F4 (O) TSPC_AVRCP_10_25a False TG: category 3 - Operation id: F5 (O) TSPC_AVRCP_10_26 False TG: category 3 - Operation id: vendor unique (O) ------------------------------------------------------------------------------- C.3: Mandatory to support if the device supports category 3 (TSPC_AVRCP_7_9). ------------------------------------------------------------------------------- operation_id of category 4 for TG ------------------------------------------------------------------------------- Parameter Name Selected Description ------------------------------------------------------------------------------- TSPC_AVRCP_11_1 False (*) TG: category 4 - Operation id: select (C.4) TSPC_AVRCP_11_2 False (*) TG: category 4 - Operation id: up (C.4) TSPC_AVRCP_11_3 False (*) TG: category 4 - Operation id: down (C.4) TSPC_AVRCP_11_4 False (*) TG: category 4 - Operation id: left (C.4) TSPC_AVRCP_11_5 False (*) TG: category 4 - Operation id: right (C.4) TSPC_AVRCP_11_6 False TG: category 4 - Operation id: right up (O) TSPC_AVRCP_11_7 False TG: category 4 - Operation id: right down (O) TSPC_AVRCP_11_8 False TG: category 4 - Operation id: left up (O) TSPC_AVRCP_11_9 False TG: category 4 - Operation id: left down (O) TSPC_AVRCP_11_10 False (*) TG: category 4 - Operation id: root menu (C.4) TSPC_AVRCP_11_11 False TG: category 4 - Operation id: setup menu (O) TSPC_AVRCP_11_12 False TG: category 4 - Operation id: contents menu (O) TSPC_AVRCP_11_13 False TG: category 4 - Operation id: favorite menu (O) TSPC_AVRCP_11_14 False TG: category 4 - Operation id: exit (O) TSPC_AVRCP_11_15 False TG: category 4 - Operation id: 0 (O) TSPC_AVRCP_11_16 False TG: category 4 - Operation id: 1 (O) TSPC_AVRCP_11_17 False TG: category 4 - Operation id: 2 (O) TSPC_AVRCP_11_18 False TG: category 4 - Operation id: 3 (O) TSPC_AVRCP_11_19 False TG: category 4 - Operation id: 4 (O) TSPC_AVRCP_11_20 False TG: category 4 - Operation id: 5 (O) TSPC_AVRCP_11_21 False TG: category 4 - Operation id: 6 (O) TSPC_AVRCP_11_22 False TG: category 4 - Operation id: 7 (O) TSPC_AVRCP_11_23 False TG: category 4 - Operation id: 8 (O) TSPC_AVRCP_11_24 False TG: category 4 - Operation id: 9 (O) TSPC_AVRCP_11_25 False TG: category 4 - Operation id: dot (O) TSPC_AVRCP_11_26 False TG: category 4 - Operation id: enter (O) TSPC_AVRCP_11_27 False TG: category 4 - Operation id: clear (O) TSPC_AVRCP_11_28 False TG: category 4 - Operation id: disply (O) TSPC_AVRCP_11_29 False TG: category 4 - Operation id: help (O) TSPC_AVRCP_11_30 False TG: category 4 - Operation id: page up (O) TSPC_AVRCP_11_31 False TG: category 4 - Operation id: page down (O) TSPC_AVRCP_11_32 False TG: category 4 - Operation id: power (O) TSPC_AVRCP_11_33 False TG: category 4 - Operation id: F1 (O) TSPC_AVRCP_11_34 False TG: category 4 - Operation id: F2 (O) TSPC_AVRCP_11_35 False TG: category 4 - Operation id: F3 (O) TSPC_AVRCP_11_36 False TG: category 4 - Operation id: F4 (O) TSPC_AVRCP_11_36a False TG: category 4 - Operation id: F5 (O) TSPC_AVRCP_11_37 False TG: category 4 - Operation id: vendor unique (O) TSPC_AVRCP_12_1 True General discoverable mode (M) TSPC_AVRCP_13_1 True General discoverable mode (M) TSPC_AVRCP_14_1 False OBEX Connect operation (C.1) TSPC_AVRCP_14_2 False OBEX Get operation (C.1) TSPC_AVRCP_14_3 False OBEX Disconnect operation (C.1) TSPC_AVRCP_15_1 False OBEX Connect operation (C.1) TSPC_AVRCP_15_2 False OBEX Get operation (C.1) TSPC_AVRCP_15_3 False OBEX Disconnect operation (C.1) TSPC_ALL False Enables all test cases when set to TRUE. ------------------------------------------------------------------------------- C.4: Mandatory to support if the device supports category 4 (TSPC_AVRCP_7_10). ------------------------------------------------------------------------------- bluez-5.82/android/PaxHeaders/pixit-hid.txt0000644000000000000000000000005012537515745015744 xustar0020 atime=1743516876 20 ctime=1743591289 bluez-5.82/android/pixit-hid.txt0000644000000000000000000000205212537515745015424 0ustar00rootrootHID PIXIT for the PTS tool. PTS version: 6.1 * - different than PTS defaults & - should be set to IUT Bluetooth address Required PIXIT settings ------------------------------------------------------------------------------- Parameter Name Value ------------------------------------------------------------------------------- TSPX_security_enabled True TSPX_delete_link_key True TSPX_query_iut_sdp True TSPX_bd_addr_iut 112233445566 (*&) TSPX_pointing_device_class_of_device 002580 TSPX_keyboard_device_class_of_device 002540 TSPX_host_class_of_device 100108 TSPX_pts_device_role MOUSE TSPX_pin_code 0000 TSPX_use_dynamic_pin_code False TSPX_time_guard 6000000 TSPX_hid_data_interval 1000 TSPX_use_implicit_send True TSPX_verbose_implicit_send False TSPX_no_fail_verdicts False TSPX_time_reconnect 30000 TSPX_secure_simple_pairing_pass_key_confirmation False TSPX_hid_report_id 1 TSPX_hid_report_data ff00 (*) ------------------------------------------------------------------------------- bluez-5.82/android/PaxHeaders/hal-audio-aptx.c0000644000000000000000000000005014015011623016240 xustar0020 atime=1743516862 20 ctime=1743591275 bluez-5.82/android/hal-audio-aptx.c0000644000000000000000000001307014015011623015722 0ustar00rootroot// SPDX-License-Identifier: Apache-2.0 /* * Copyright (C) 2014 Tieto Poland * */ #define _GNU_SOURCE #include #include #include #include #include #include "audio-msg.h" #include "hal-audio.h" #include "hal-log.h" #include "profiles/audio/a2dp-codecs.h" #define APTX_SO_NAME "libbt-aptx.so" struct aptx_data { a2dp_aptx_t aptx; void *enc; }; static const a2dp_aptx_t aptx_presets[] = { { .info = A2DP_SET_VENDOR_ID_CODEC_ID(APTX_VENDOR_ID, APTX_CODEC_ID), .frequency = APTX_SAMPLING_FREQ_44100 | APTX_SAMPLING_FREQ_48000, .channel_mode = APTX_CHANNEL_MODE_STEREO, }, { .info = A2DP_SET_VENDOR_ID_CODEC_ID(APTX_VENDOR_ID, APTX_CODEC_ID), .frequency = APTX_SAMPLING_FREQ_48000, .channel_mode = APTX_CHANNEL_MODE_STEREO, }, { .info = A2DP_SET_VENDOR_ID_CODEC_ID(APTX_VENDOR_ID, APTX_CODEC_ID), .frequency = APTX_SAMPLING_FREQ_44100, .channel_mode = APTX_CHANNEL_MODE_STEREO, }, }; static void *aptx_handle; static int aptx_btencsize; static int (*aptx_init)(void *, short); static int (*aptx_encode)(void *, void *, void *, void *); static bool aptx_load(void) { const char * (*aptx_version)(void); const char * (*aptx_build)(void); int (*aptx_sizeofenc)(void); aptx_handle = dlopen(APTX_SO_NAME, RTLD_LAZY); if (!aptx_handle) { error("APTX: failed to open library %s (%s)", APTX_SO_NAME, dlerror()); return false; } aptx_version = dlsym(aptx_handle, "aptxbtenc_version"); aptx_build = dlsym(aptx_handle, "aptxbtenc_build"); if (aptx_version && aptx_build) info("APTX: using library version %s build %s", aptx_version(), aptx_build()); else warn("APTX: cannot retrieve library version"); aptx_sizeofenc = dlsym(aptx_handle, "SizeofAptxbtenc"); aptx_init = dlsym(aptx_handle, "aptxbtenc_init"); aptx_encode = dlsym(aptx_handle, "aptxbtenc_encodestereo"); if (!aptx_sizeofenc || !aptx_init || !aptx_encode) { error("APTX: failed initialize library"); dlclose(aptx_handle); aptx_handle = NULL; return false; } aptx_btencsize = aptx_sizeofenc(); info("APTX: codec library initialized (size=%d)", aptx_btencsize); return true; } static void aptx_unload(void) { if (!aptx_handle) return; dlclose(aptx_handle); aptx_handle = NULL; } static int aptx_get_presets(struct audio_preset *preset, size_t *len) { int i; int count; size_t new_len = 0; uint8_t *ptr = (uint8_t *) preset; size_t preset_size = sizeof(*preset) + sizeof(a2dp_aptx_t); DBG(""); count = sizeof(aptx_presets) / sizeof(aptx_presets[0]); for (i = 0; i < count; i++) { preset = (struct audio_preset *) ptr; if (new_len + preset_size > *len) break; preset->len = sizeof(a2dp_aptx_t); memcpy(preset->data, &aptx_presets[i], preset->len); new_len += preset_size; ptr += preset_size; } *len = new_len; return i; } static bool aptx_codec_init(struct audio_preset *preset, uint16_t payload_len, void **codec_data) { struct aptx_data *aptx_data; DBG(""); if (preset->len != sizeof(a2dp_aptx_t)) { error("APTX: preset size mismatch"); return false; } aptx_data = malloc(sizeof(*aptx_data)); if (!aptx_data) return false; memset(aptx_data, 0, sizeof(*aptx_data)); memcpy(&aptx_data->aptx, preset->data, preset->len); aptx_data->enc = calloc(1, aptx_btencsize); if (!aptx_data->enc) { error("APTX: failed to create encoder"); free(aptx_data); return false; } /* 1 = big-endian, this is what devices are using */ aptx_init(aptx_data->enc, 1); *codec_data = aptx_data; return true; } static bool aptx_cleanup(void *codec_data) { struct aptx_data *aptx_data = (struct aptx_data *) codec_data; free(aptx_data->enc); free(codec_data); return true; } static bool aptx_get_config(void *codec_data, struct audio_input_config *config) { struct aptx_data *aptx_data = (struct aptx_data *) codec_data; config->rate = aptx_data->aptx.frequency & APTX_SAMPLING_FREQ_48000 ? 48000 : 44100; config->channels = AUDIO_CHANNEL_OUT_STEREO; config->format = AUDIO_FORMAT_PCM_16_BIT; return true; } static size_t aptx_get_buffer_size(void *codec_data) { /* TODO: return actual value */ return 0; } static size_t aptx_get_mediapacket_duration(void *codec_data) { /* TODO: return actual value */ return 0; } static ssize_t aptx_encode_mediapacket(void *codec_data, const uint8_t *buffer, size_t len, struct media_packet *mp, size_t mp_data_len, size_t *written) { struct aptx_data *aptx_data = (struct aptx_data *) codec_data; const int16_t *ptr = (const void *) buffer; size_t bytes_in = 0; size_t bytes_out = 0; while ((len - bytes_in) >= 16 && (mp_data_len - bytes_out) >= 4) { int pcm_l[4], pcm_r[4]; int i; for (i = 0; i < 4; i++) { pcm_l[i] = ptr[0]; pcm_r[i] = ptr[1]; ptr += 2; } aptx_encode(aptx_data->enc, pcm_l, pcm_r, &mp->data[bytes_out]); bytes_in += 16; bytes_out += 4; } *written = bytes_out; return bytes_in; } static bool aptx_update_qos(void *codec_data, uint8_t op) { /* * aptX has constant bitrate of 352kbps (with constant 4:1 compression * ratio) thus QoS is not possible here. */ return false; } static const struct audio_codec codec = { .type = A2DP_CODEC_VENDOR, .use_rtp = false, .load = aptx_load, .unload = aptx_unload, .get_presets = aptx_get_presets, .init = aptx_codec_init, .cleanup = aptx_cleanup, .get_config = aptx_get_config, .get_buffer_size = aptx_get_buffer_size, .get_mediapacket_duration = aptx_get_mediapacket_duration, .encode_mediapacket = aptx_encode_mediapacket, .update_qos = aptx_update_qos, }; const struct audio_codec *codec_aptx(void) { return &codec; } bluez-5.82/android/PaxHeaders/pixit-gatt.txt0000644000000000000000000000005012537515745016137 xustar0020 atime=1743516876 20 ctime=1743591289 bluez-5.82/android/pixit-gatt.txt0000644000000000000000000000211412537515745015616 0ustar00rootrootGATT PIXIT for the PTS tool. PTS version: 6.1 * - different than PTS defaults & - should be set to IUT Bluetooth address Required PIXIT settings ------------------------------------------------------------------------------- Parameter Name Value ------------------------------------------------------------------------------- TSPX_bd_addr_iut 112233445566 (*&) TSPX_security_enabled FALSE TSPX_delete_link_key TRUE TSPX_time_guard 180000 TSPX_selected_handle 0012 TSPX_use_implicit_send TRUE TSPX_secure_simple_pairing_pass_key_confirmation FALSE TSPX_iut_use_dynamic_bd_addr FALSE TSPX_iut_setup_att_over_br_edr FALSE TSPX_tester_database_file [Path to GATT Test Database] TSPX_iut_is_client_periphral FALSE TSPX_iut_is_server_central FALSE TSPX_mtu_size 23 TSPX_pin_code 0000 TSPX_use_dynamic_pin FALSE TSPX_delete_ltk FALSE TSPX_characteristic_readable FALSE TSPX_tester_appearance 0000 TSPX_iut_use_resolvable_random_access FALSE ------------------------------------------------------------------------------- bluez-5.82/android/PaxHeaders/pics-scpp.txt0000644000000000000000000000005012537515745015746 xustar0020 atime=1743516876 20 ctime=1743591289 bluez-5.82/android/pics-scpp.txt0000644000000000000000000001461312537515745015434 0ustar00rootrootScPP PICS for the PTS tool. PTS version: 6.1 * - different than PTS defaults M - mandatory O - optional Connection Roles ------------------------------------------------------------------------------- Parameter Name Selected Description ------------------------------------------------------------------------------- TSPC_ScPP_1_1 False (*) Scan Server (C.1) TSPC_ScPP_1_2 True Scan Client (C.1) ------------------------------------------------------------------------------- Note: Mandatory to support at least one of TSPC_ScPP_1_1 or TSPC_ScPP_1_2. ------------------------------------------------------------------------------- Transport Requirements ------------------------------------------------------------------------------- Parameter Name Selected Description ------------------------------------------------------------------------------- TSPC_ScPP_2_1 False (*) Profile supported over BR/EDR (C.1) TSPC_ScPP_2_2 True Profile supported over LE (M) ------------------------------------------------------------------------------- C.1: Excluded for this profile. ------------------------------------------------------------------------------- Services - Scan Server ------------------------------------------------------------------------------- Parameter Name Selected Description ------------------------------------------------------------------------------- TSPC_ScPP_3_1 False (*) Implements Scan Parameters Service (M) ------------------------------------------------------------------------------- GAP Requirements - Scan Server Role ------------------------------------------------------------------------------- Parameter Name Selected Description ------------------------------------------------------------------------------- TSPC_ScPP_4_1 False (*) Peripheral (M) TSPC_ScPP_4_2 False (*) Directed Connectable Mode (O) TSPC_ScPP_4_3 False (*) Undirected Connectable Mode (M) TSPC_ScPP_4_4 False (*) Bondable mode (peripheral) (O) TSPC_ScPP_4_5 False (*) Bonding procedure (peripheral) (O) TSPC_ScPP_4_6 False (*) LE Security Mode 1 (peripheral) (M) ------------------------------------------------------------------------------- Scan Server ------------------------------------------------------------------------------- Parameter Name Selected Description ------------------------------------------------------------------------------- TSPC_ScPP_5_1 False (*) SM 2.3.1- TSPC_ScPP_5_2 False (*) Unauthenticated no MITM protection. (LE Security Level 2, Just Works) (O) TSPC_ScPP_5_3 False (*) Authenticated MITM protection (LE Security Level 3, Passkey) (O) ------------------------------------------------------------------------------- Client Services Support - Scan Client Role ------------------------------------------------------------------------------- Parameter Name Selected Description ------------------------------------------------------------------------------- TSPC_ScPP_6_1 True Scan Parameters Service (M) ------------------------------------------------------------------------------- Discover Services and Characteristics - Scan Client Role ------------------------------------------------------------------------------- Parameter Name Selected Description ------------------------------------------------------------------------------- TSPC_ScPP_7_1 True Discover Scan Parameters Service (M) TSPC_ScPP_7_2 True Discover Scan Parameters characteristic: Scan interval Window (M) TSPC_ScPP_7_3 True Discover Scan Parameters characteristic: Scan Refresh (M) TSPC_ScPP_7_4 True Discover Scan Parameters characteristic: Scan Refresh – Client Characteristic Configuration Descriptor (M) ------------------------------------------------------------------------------- Features - Client ------------------------------------------------------------------------------- Parameter Name Selected Description ------------------------------------------------------------------------------- TSPC_ScPP_8_1 True Write Scan Interval Window characteristic (M) TSPC_ScPP_8_2 True Configure Scan Refresh characteristic: Client Characteristic Configuration characteristic descriptor with 0x0001 (M) TSPC_ScPP_8_3 True Notify Scan Refresh characteristic (M) ------------------------------------------------------------------------------- GATT Requirements - Scan Client ------------------------------------------------------------------------------- Parameter Name Selected Description ------------------------------------------------------------------------------- TSPC_ScPP_9_1 True Attribute Protocol supported over LE Transport (M) TSPC_ScPP_9_2 True Generic Attribute Profile Client (M) TSPC_ScPP_9_3 True Discover All Primary Services (C.1) TSPC_ScPP_9_4 True Discover Primary Services by Service UUID (C.1) TSPC_ScPP_9_5 True Discover All Characteristics of a Service (C.2) TSPC_ScPP_9_6 True Discover Characteristics by UUID (C.2) TSPC_ScPP_9_7 True Discover All Characteristic Descriptors (M) TSPC_ScPP_9_8 True Write without Response (M) TSPC_ScPP_9_9 True Write Characteristic Descriptors (M) TSPC_ScPP_9_10 True Notifications (M) ------------------------------------------------------------------------------- C.1: Mandatory to support one of these sub-procedures for service discovery C.2: Mandatory to support one of these sub-procedures for characteristic discovery ------------------------------------------------------------------------------- GAP Requirements - Scan Client ------------------------------------------------------------------------------- Parameter Name Selected Description ------------------------------------------------------------------------------- TSPC_ScPP_10_1 True Central (M) TSPC_ScPP_10_2 True Bondable mode (central) (O) TSPC_ScPP_10_3 True Bonding procedure (central) (O) TSPC_ScPP_10_4 True Central (M) ------------------------------------------------------------------------------- SM Requirements - Scan Client ------------------------------------------------------------------------------- Parameter Name Selected Description ------------------------------------------------------------------------------- TSPC_ScPP_11_1 True No Security Requirements (LE Security Level 1, No Security) (M) TSPC_ScPP_11_2 True Unauthenticated no MITM protection (LE Security Level 2, Just Works) (O) TSPC_ScPP_11_3 True Authenticated MITM protection (LE Security Level 3, Passkey) (O) ------------------------------------------------------------------------------- bluez-5.82/android/PaxHeaders/avdtptest.c0000644000000000000000000000005014015011623015441 xustar0020 atime=1743516864 20 ctime=1743591278 bluez-5.82/android/avdtptest.c0000644000000000000000000004524514015011623015134 0ustar00rootroot// SPDX-License-Identifier: GPL-2.0-or-later /* * * BlueZ - Bluetooth protocol stack for Linux * * Copyright (C) 2014 Intel Corporation * * */ #ifdef HAVE_CONFIG_H #include #endif #define _GNU_SOURCE #include #include #include #include #include #include #include #include "lib/bluetooth.h" #include "lib/hci.h" #include "lib/hci_lib.h" #include "btio/btio.h" #include "src/shared/util.h" #include "src/shared/queue.h" #include "avdtp.h" static GMainLoop *mainloop = NULL; static int dev_role = AVDTP_SEP_TYPE_SOURCE; static bool preconf = false; static struct avdtp *avdtp = NULL; struct avdtp_stream *avdtp_stream = NULL; struct avdtp_local_sep *local_sep = NULL; struct avdtp_remote_sep *remote_sep = NULL; static GIOChannel *io = NULL; static bool reject = false; static bdaddr_t src; static bdaddr_t dst; static uint16_t version = 0x0103; static guint media_player = 0; static guint media_recorder = 0; static guint idle_id = 0; static struct queue *lseps = NULL; static bool fragment = false; static enum { CMD_GET_CONF, CMD_OPEN, CMD_START, CMD_SUSPEND, CMD_CLOSE, CMD_ABORT, CMD_DELAY, CMD_NONE, } command = CMD_NONE; static const char sbc_codec[] = {0x00, 0x00, 0x11, 0x15, 0x02, 0x40}; static const char sbc_media_frame[] = { 0x00, 0x60, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x9c, 0xfd, 0x40, 0xbd, 0xde, 0xa9, 0x75, 0x43, 0x20, 0x87, 0x64, 0x44, 0x32, 0x7f, 0xbe, 0xf7, 0x76, 0xfe, 0xf7, 0xbb, 0xbb, 0x7f, 0xbe, 0xf7, 0x76, 0xfe, 0xf7, 0xbb, 0xbb, 0x7f, 0xbe, 0xf7, 0x76, 0xfe, 0xf7, 0xbb, 0xbb, 0x80, 0x3e, 0xf7, 0x76, 0xfe, 0xf7, 0xbb, 0xbb, 0x83, 0x41, 0x07, 0x77, 0x09, 0x07, 0x43, 0xb3, 0x81, 0xbc, 0xf8, 0x77, 0x02, 0xe5, 0xa4, 0x3a, 0xa0, 0xcb, 0x38, 0xbb, 0x57, 0x90, 0xd9, 0x08, 0x9c, 0x1d, 0x86, 0x59, 0x01, 0x0c, 0x21, 0x44, 0x68, 0x35, 0xa8, 0x57, 0x97, 0x0e, 0x9b, 0xbb, 0x62, 0xc4, 0xca, 0x57, 0x04, 0xa1, 0xca, 0x3b, 0xa3, 0x48, 0xd2, 0x66, 0x11, 0x33, 0x6a, 0x3b, 0xb4, 0xbb, 0x08, 0x77, 0x17, 0x03, 0xb4, 0x3b, 0x79, 0x3b, 0x46, 0x97, 0x0e, 0xf7, 0x3d, 0xbb, 0x3d, 0x49, 0x25, 0x86, 0x88, 0xb4, 0xad, 0x3b, 0x62, 0xbb, 0xa4, 0x47, 0x29, 0x99, 0x3b, 0x3b, 0xaf, 0xc6, 0xd4, 0x37, 0x68, 0x94, 0x0a, 0xbb }; static void parse_command(const char *cmd) { if (!strncmp(cmd, "getconf", sizeof("getconf"))) { command = CMD_GET_CONF; } else if (!strncmp(cmd, "open", sizeof("open"))) { command = CMD_OPEN; } else if (!strncmp(cmd, "start", sizeof("start"))) { command = CMD_START; } else if (!strncmp(cmd, "suspend", sizeof("suspend"))) { command = CMD_SUSPEND; } else if (!strncmp(cmd, "close", sizeof("close"))) { command = CMD_CLOSE; } else if (!strncmp(cmd, "abort", sizeof("abort"))) { command = CMD_ABORT; } else if (!strncmp(cmd, "delay", sizeof("delay"))) { command = CMD_DELAY; } else { printf("Unknown command '%s'\n", cmd); printf("(getconf open start suspend close abort delay)\n"); exit(1); } } static void send_command(void) { avdtp_state_t state = avdtp_sep_get_state(local_sep); switch (command) { case CMD_GET_CONF: avdtp_get_configuration(avdtp, avdtp_stream); break; case CMD_OPEN: if (state == AVDTP_STATE_CONFIGURED) avdtp_open(avdtp, avdtp_stream); break; case CMD_START: if (state == AVDTP_STATE_OPEN) avdtp_start(avdtp, avdtp_stream); break; case CMD_SUSPEND: if (state == AVDTP_STATE_STREAMING) avdtp_suspend(avdtp , avdtp_stream); break; case CMD_CLOSE: if (state == AVDTP_STATE_STREAMING) avdtp_close(avdtp, avdtp_stream, FALSE); break; case CMD_ABORT: avdtp_abort(avdtp , avdtp_stream); break; case CMD_DELAY: avdtp_delay_report(avdtp , avdtp_stream , 250); break; case CMD_NONE: default: break; } } static gboolean media_writer(gpointer user_data) { uint16_t omtu; int fd; int to_write; if (!avdtp_stream_get_transport(avdtp_stream, &fd, NULL, &omtu, NULL)) return TRUE; if (omtu < sizeof(sbc_media_frame)) to_write = omtu; else to_write = sizeof(sbc_media_frame); if (write(fd, sbc_media_frame, to_write) < 0) return TRUE; send_command(); return TRUE; } static bool start_media_player(void) { int fd; uint16_t omtu; printf("Media streaming started\n"); if (media_player || !avdtp_stream) return false; if (!avdtp_stream_get_transport(avdtp_stream, &fd, NULL, &omtu, NULL)) return false; media_player = g_timeout_add(200, media_writer, NULL); if (!media_player) return false; return true; } static void stop_media_player(void) { if (!media_player) return; printf("Media streaming stopped\n"); g_source_remove(media_player); media_player = 0; } #if __BYTE_ORDER == __LITTLE_ENDIAN struct rtp_header { unsigned cc:4; unsigned x:1; unsigned p:1; unsigned v:2; unsigned pt:7; unsigned m:1; uint16_t sequence_number; uint32_t timestamp; uint32_t ssrc; uint32_t csrc[0]; } __attribute__ ((packed)); #elif __BYTE_ORDER == __BIG_ENDIAN struct rtp_header { unsigned v:2; unsigned p:1; unsigned x:1; unsigned cc:4; unsigned m:1; unsigned pt:7; uint16_t sequence_number; uint32_t timestamp; uint32_t ssrc; uint32_t csrc[0]; } __attribute__ ((packed)); #else #error "Unknown byte order" #endif static gboolean media_reader(GIOChannel *source, GIOCondition condition, gpointer data) { char buf[UINT16_MAX]; struct rtp_header *rtp = (void *) buf; static bool decode = false; uint16_t imtu; int fd, ret; if (!avdtp_stream_get_transport(avdtp_stream, &fd, &imtu, NULL, NULL)) return TRUE; ret = read(fd, buf, imtu); if (ret < 0) { printf("Reading failed (%s)\n", strerror(errno)); return TRUE; } if (ret < (int) sizeof(*rtp)) { printf("Not enough media data received (%u bytes)", ret); return TRUE; } if (!decode) { printf("V=%u P=%u X=%u CC=%u M=%u PT=%u SeqNr=%d\n", rtp->v, rtp->p, rtp->x, rtp->cc, rtp->m, rtp->pt, be16_to_cpu(rtp->sequence_number)); decode = true; } send_command(); return TRUE; } static bool start_media_recorder(void) { int fd; uint16_t omtu; GIOChannel *chan; printf("Media recording started\n"); if (media_recorder || !avdtp_stream) return false; if (!avdtp_stream_get_transport(avdtp_stream, &fd, NULL, &omtu, NULL)) return false; chan = g_io_channel_unix_new(fd); media_recorder = g_io_add_watch(chan, G_IO_IN, media_reader, NULL); g_io_channel_unref(chan); if (!media_recorder) return false; return true; } static void stop_media_recorder(void) { if (!media_recorder) return; printf("Media recording stopped\n"); g_source_remove(media_recorder); media_recorder = 0; } static void set_configuration_cfm(struct avdtp *session, struct avdtp_local_sep *lsep, struct avdtp_stream *stream, struct avdtp_error *err, void *user_data) { printf("%s\n", __func__); if (preconf) avdtp_open(avdtp, avdtp_stream); } static void get_configuration_cfm(struct avdtp *session, struct avdtp_local_sep *lsep, struct avdtp_stream *stream, struct avdtp_error *err, void *user_data) { printf("%s\n", __func__); } static void disconnect_cb(void *user_data) { printf("Disconnected\n"); g_main_loop_quit(mainloop); } static void discover_cb(struct avdtp *session, GSList *seps, struct avdtp_error *err, void *user_data) { struct avdtp_service_capability *service; GSList *caps = NULL; int ret; remote_sep = avdtp_find_remote_sep(avdtp, local_sep); if (!remote_sep) { printf("Unable to find matching endpoint\n"); avdtp_shutdown(session); return; } printf("Matching endpoint found\n"); service = avdtp_service_cap_new(AVDTP_MEDIA_TRANSPORT, NULL, 0); caps = g_slist_append(caps, service); service = avdtp_service_cap_new(AVDTP_MEDIA_CODEC, sbc_codec, sizeof(sbc_codec)); caps = g_slist_append(caps, service); ret = avdtp_set_configuration(avdtp, remote_sep, local_sep, caps, &avdtp_stream); g_slist_free_full(caps, g_free); if (ret < 0) { printf("Failed to set configuration (%s)\n", strerror(-ret)); avdtp_shutdown(session); } } static void connect_cb(GIOChannel *chan, GError *err, gpointer user_data) { uint16_t imtu, omtu; GError *gerr = NULL; int fd; if (err) { printf("%s\n", err->message); g_main_loop_quit(mainloop); return; } bt_io_get(chan, &gerr, BT_IO_OPT_IMTU, &imtu, BT_IO_OPT_OMTU, &omtu, BT_IO_OPT_DEST_BDADDR, &dst, BT_IO_OPT_INVALID); if (gerr) { printf("%s\n", gerr->message); g_main_loop_quit(mainloop); return; } printf("Connected (imtu=%d omtu=%d)\n", imtu, omtu); fd = g_io_channel_unix_get_fd(chan); if (avdtp && avdtp_stream) { if (!avdtp_stream_set_transport(avdtp_stream, fd, imtu, omtu)) { printf("avdtp_stream_set_transport: failed\n"); g_main_loop_quit(mainloop); } g_io_channel_set_close_on_unref(chan, FALSE); send_command(); return; } avdtp = avdtp_new(fd, imtu, omtu, version, lseps); if (!avdtp) { printf("Failed to create avdtp instance\n"); g_main_loop_quit(mainloop); return; } avdtp_add_disconnect_cb(avdtp, disconnect_cb, NULL); if (preconf) { int ret; ret = avdtp_discover(avdtp, discover_cb, NULL); if (ret < 0) { printf("avdtp_discover failed: %s", strerror(-ret)); g_main_loop_quit(mainloop); } } } static GIOChannel *do_connect(GError **err) { if (fragment) return bt_io_connect(connect_cb, NULL, NULL, err, BT_IO_OPT_SOURCE_BDADDR, &src, BT_IO_OPT_DEST_BDADDR, &dst, BT_IO_OPT_SEC_LEVEL, BT_IO_SEC_LOW, BT_IO_OPT_PSM, AVDTP_PSM, BT_IO_OPT_MTU, 48, BT_IO_OPT_INVALID); return bt_io_connect(connect_cb, NULL, NULL, err, BT_IO_OPT_SOURCE_BDADDR, &src, BT_IO_OPT_DEST_BDADDR, &dst, BT_IO_OPT_SEC_LEVEL, BT_IO_SEC_LOW, BT_IO_OPT_PSM, AVDTP_PSM, BT_IO_OPT_INVALID); } static void open_cfm(struct avdtp *session, struct avdtp_local_sep *lsep, struct avdtp_stream *stream, struct avdtp_error *err, void *user_data) { GError *gerr = NULL; printf("%s\n", __func__); do_connect(&gerr); if (gerr) { printf("connect failed: %s\n", gerr->message); g_error_free(gerr); g_main_loop_quit(mainloop); } } static void start_cfm(struct avdtp *session, struct avdtp_local_sep *lsep, struct avdtp_stream *stream, struct avdtp_error *err, void *user_data) { printf("%s\n", __func__); if (dev_role == AVDTP_SEP_TYPE_SOURCE) start_media_player(); else start_media_recorder(); } static void suspend_cfm(struct avdtp *session, struct avdtp_local_sep *lsep, struct avdtp_stream *stream, struct avdtp_error *err, void *user_data) { printf("%s\n", __func__); if (dev_role == AVDTP_SEP_TYPE_SOURCE) stop_media_player(); else stop_media_recorder(); } static void close_cfm(struct avdtp *session, struct avdtp_local_sep *lsep, struct avdtp_stream *stream, struct avdtp_error *err, void *user_data) { printf("%s\n", __func__); if (dev_role == AVDTP_SEP_TYPE_SOURCE) stop_media_player(); else stop_media_recorder(); avdtp_stream = NULL; } static void abort_cfm(struct avdtp *session, struct avdtp_local_sep *lsep, struct avdtp_stream *stream, struct avdtp_error *err, void *user_data) { printf("%s\n", __func__); if (dev_role == AVDTP_SEP_TYPE_SOURCE) stop_media_player(); else stop_media_recorder(); avdtp_stream = NULL; } static void reconfigure_cfm(struct avdtp *session, struct avdtp_local_sep *lsep, struct avdtp_stream *stream, struct avdtp_error *err, void *user_data) { printf("%s\n", __func__); } static void delay_report_cfm(struct avdtp *session, struct avdtp_local_sep *lsep, struct avdtp_stream *stream, struct avdtp_error *err, void *user_data) { printf("%s\n", __func__); } static struct avdtp_sep_cfm sep_cfm = { .set_configuration = set_configuration_cfm, .get_configuration = get_configuration_cfm, .open = open_cfm, .start = start_cfm, .suspend = suspend_cfm, .close = close_cfm, .abort = abort_cfm, .reconfigure = reconfigure_cfm, .delay_report = delay_report_cfm, }; static gboolean get_capability_ind(struct avdtp *session, struct avdtp_local_sep *sep, GSList **caps, uint8_t *err, void *user_data) { struct avdtp_service_capability *service; int i; printf("%s\n", __func__); if (idle_id > 0) { g_source_remove(idle_id); idle_id = 0; } if (reject) return FALSE; *caps = NULL; service = avdtp_service_cap_new(AVDTP_MEDIA_TRANSPORT, NULL, 0); *caps = g_slist_append(*caps, service); service = avdtp_service_cap_new(AVDTP_MEDIA_CODEC, sbc_codec, sizeof(sbc_codec)); *caps = g_slist_append(*caps, service); if (fragment) for (i = 0; i < 10; i++) { service = avdtp_service_cap_new(AVDTP_MEDIA_CODEC, sbc_codec, sizeof(sbc_codec)); *caps = g_slist_append(*caps, service); } return TRUE; } static gboolean set_configuration_ind(struct avdtp *session, struct avdtp_local_sep *lsep, struct avdtp_stream *stream, GSList *caps, avdtp_set_configuration_cb cb, void *user_data) { printf("%s\n", __func__); if (reject) return FALSE; if (idle_id > 0) { g_source_remove(idle_id); idle_id = 0; } avdtp_stream = stream; cb(session, stream, NULL); send_command(); return TRUE; } static gboolean get_configuration_ind(struct avdtp *session, struct avdtp_local_sep *lsep, uint8_t *err, void *user_data) { printf("%s\n", __func__); if (reject) return FALSE; return TRUE; } static gboolean open_ind(struct avdtp *session, struct avdtp_local_sep *lsep, struct avdtp_stream *stream, uint8_t *err, void *user_data) { printf("%s\n", __func__); if (reject) return FALSE; send_command(); return TRUE; } static gboolean start_ind(struct avdtp *session, struct avdtp_local_sep *lsep, struct avdtp_stream *stream, uint8_t *err, void *user_data) { printf("%s\n", __func__); if (reject) return FALSE; if (dev_role == AVDTP_SEP_TYPE_SOURCE) start_media_player(); else start_media_recorder(); send_command(); return TRUE; } static gboolean suspend_ind(struct avdtp *session, struct avdtp_local_sep *sep, struct avdtp_stream *stream, uint8_t *err, void *user_data) { printf("%s\n", __func__); if (reject) return FALSE; if (dev_role == AVDTP_SEP_TYPE_SOURCE) stop_media_player(); else stop_media_recorder(); return TRUE; } static gboolean close_ind(struct avdtp *session, struct avdtp_local_sep *sep, struct avdtp_stream *stream, uint8_t *err, void *user_data) { printf("%s\n", __func__); if (reject) return FALSE; if (dev_role == AVDTP_SEP_TYPE_SOURCE) stop_media_player(); else stop_media_recorder(); avdtp_stream = NULL; return TRUE; } static void abort_ind(struct avdtp *session, struct avdtp_local_sep *sep, struct avdtp_stream *stream, uint8_t *err, void *user_data) { printf("%s\n", __func__); if (dev_role == AVDTP_SEP_TYPE_SOURCE) stop_media_player(); else stop_media_recorder(); avdtp_stream = NULL; } static gboolean reconfigure_ind(struct avdtp *session, struct avdtp_local_sep *lsep, uint8_t *err, void *user_data) { printf("%s\n", __func__); if (reject) return FALSE; return TRUE; } static gboolean delayreport_ind(struct avdtp *session, struct avdtp_local_sep *lsep, uint8_t rseid, uint16_t delay, uint8_t *err, void *user_data) { printf("%s\n", __func__); if (reject) return FALSE; return TRUE; } static struct avdtp_sep_ind sep_ind = { .get_capability = get_capability_ind, .set_configuration = set_configuration_ind, .get_configuration = get_configuration_ind, .open = open_ind, .close = close_ind, .start = start_ind, .suspend = suspend_ind, .abort = abort_ind, .reconfigure = reconfigure_ind, .delayreport = delayreport_ind, }; static void usage(void) { printf("avdtptest - AVDTP testing ver %s\n", VERSION); printf("Usage:\n" "\tavdtptest [options]\n"); printf("options:\n" "\t-d SRC (source) or SINK (sink)\n" "\t-i HCI adapter\n" "\t-c connect\n" "\t-l listen\n" "\t-r reject commands\n" "\t-f fragment\n" "\t-p configure stream\n" "\t-s send command\n" "\t-v set version (0x0100, 0x0102, 0x0103\n"); } static struct option main_options[] = { { "help", 0, 0, 'h' }, { "device_role", 1, 0, 'd' }, { "adapter", 1, 0, 'i' }, { "connect", 1, 0, 'c' }, { "listen", 0, 0, 'l' }, { "reject", 0, 0, 'r' }, { "fragment", 0, 0, 'f' }, { "preconf", 0, 0, 'p' }, { "send", 1, 0, 's' }, { "version", 1, 0, 'v' }, { 0, 0, 0, 0 } }; static GIOChannel *do_listen(GError **err) { if (fragment) return bt_io_listen(connect_cb, NULL, NULL, NULL, err, BT_IO_OPT_SOURCE_BDADDR, &src, BT_IO_OPT_PSM, AVDTP_PSM, BT_IO_OPT_SEC_LEVEL, BT_IO_SEC_LOW, BT_IO_OPT_MTU, 48, BT_IO_OPT_INVALID); return bt_io_listen(connect_cb, NULL, NULL, NULL, err, BT_IO_OPT_SOURCE_BDADDR, &src, BT_IO_OPT_PSM, AVDTP_PSM, BT_IO_OPT_SEC_LEVEL, BT_IO_SEC_LOW, BT_IO_OPT_INVALID); } int main(int argc, char *argv[]) { GError *err = NULL; int opt; bacpy(&src, BDADDR_ANY); bacpy(&dst, BDADDR_ANY); mainloop = g_main_loop_new(NULL, FALSE); if (!mainloop) { printf("Failed to create main loop\n"); exit(1); } while ((opt = getopt_long(argc, argv, "d:hi:s:c:v:lrfp", main_options, NULL)) != EOF) { switch (opt) { case 'i': if (!strncmp(optarg, "hci", 3)) hci_devba(atoi(optarg + 3), &src); else str2ba(optarg, &src); break; case 'd': if (!strncasecmp(optarg, "SRC", sizeof("SRC"))) { dev_role = AVDTP_SEP_TYPE_SOURCE; } else if (!strncasecmp(optarg, "SINK", sizeof("SINK"))) { dev_role = AVDTP_SEP_TYPE_SINK; } else { usage(); exit(1); } break; case 'c': if (str2ba(optarg, &dst) < 0) { usage(); exit(1); } break; case 'l': bacpy(&dst, BDADDR_ANY); break; case 'r': reject = true; break; case 'f': fragment = true; break; case 'p': preconf = true; break; case 's': parse_command(optarg); break; case 'v': version = strtol(optarg, NULL, 0); if (version != 0x0100 && version != 0x0102 && version != 0x0103) { printf("invalid version\n"); exit(1); } break; case 'h': usage(); exit(0); default: usage(); exit(1); } } lseps = queue_new(); local_sep = avdtp_register_sep(lseps, dev_role, AVDTP_MEDIA_TYPE_AUDIO, 0x00, TRUE, &sep_ind, &sep_cfm, NULL); if (!local_sep) { printf("Failed to register sep\n"); exit(1); } queue_push_tail(lseps, local_sep); if (!bacmp(&dst, BDADDR_ANY)) { printf("Listening...\n"); io = do_listen(&err); } else { printf("Connecting...\n"); io = do_connect(&err); } if (!io) { printf("Failed: %s\n", err->message); g_error_free(err); exit(1); } g_main_loop_run(mainloop); printf("Done\n"); queue_destroy(lseps, NULL); avdtp_unref(avdtp); avdtp = NULL; g_main_loop_unref(mainloop); mainloop = NULL; return 0; } bluez-5.82/android/PaxHeaders/system-emulator.c0000644000000000000000000000005014711225433016606 xustar0020 atime=1743516867 20 ctime=1743591279 bluez-5.82/android/system-emulator.c0000644000000000000000000001057514711225433016277 0ustar00rootroot// SPDX-License-Identifier: LGPL-2.1-or-later /* * * BlueZ - Bluetooth protocol stack for Linux * * Copyright (C) 2013-2014 Intel Corporation. All rights reserved. * * */ #ifdef HAVE_CONFIG_H #include #endif #define _GNU_SOURCE #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #ifndef WAIT_ANY #define WAIT_ANY (-1) #endif #include "src/shared/mainloop.h" static char exec_dir[PATH_MAX]; static pid_t daemon_pid = -1; static pid_t snoop_pid = -1; static void run_valgrind(char *prg_name) { char *prg_argv[6]; char *prg_envp[3]; prg_argv[0] = "/usr/bin/valgrind"; prg_argv[1] = "--leak-check=full"; prg_argv[2] = "--track-origins=yes"; prg_argv[3] = prg_name; prg_argv[4] = "-d"; prg_argv[5] = NULL; prg_envp[0] = "G_SLICE=always-malloc"; prg_envp[1] = "G_DEBUG=gc-friendly"; prg_envp[2] = NULL; execve(prg_argv[0], prg_argv, prg_envp); } static void run_bluetoothd(char *prg_name) { char *prg_argv[3]; char *prg_envp[1]; prg_argv[0] = prg_name; prg_argv[1] = "-d"; prg_argv[2] = NULL; prg_envp[0] = NULL; execve(prg_argv[0], prg_argv, prg_envp); } static void ctl_start(void) { char prg_name[PATH_MAX + 11]; pid_t pid; snprintf(prg_name, sizeof(prg_name), "%s/%s", exec_dir, "bluetoothd"); printf("Starting %s\n", prg_name); pid = fork(); if (pid < 0) { perror("Failed to fork new process"); return; } if (pid == 0) { run_valgrind(prg_name); /* Fallback to no valgrind if running with valgind failed */ run_bluetoothd(prg_name); exit(0); } printf("New process %d created\n", pid); daemon_pid = pid; } static void snoop_start(void) { char prg_name[PATH_MAX + 17]; char *prg_argv[3]; char *prg_envp[1]; pid_t pid; snprintf(prg_name, sizeof(prg_name), "%s/%s", exec_dir, "bluetoothd-snoop"); prg_argv[0] = prg_name; prg_argv[1] = "/tmp/btsnoop_hci.log"; prg_argv[2] = NULL; prg_envp[0] = NULL; printf("Starting %s\n", prg_name); pid = fork(); if (pid < 0) { perror("Failed to fork new process"); return; } if (pid == 0) { execve(prg_argv[0], prg_argv, prg_envp); exit(0); } printf("New process %d created\n", pid); snoop_pid = pid; } static void snoop_stop(void) { printf("Stoping %s/%s\n", exec_dir, "bluetoothd-snoop"); kill(snoop_pid, SIGTERM); } static void system_socket_callback(int fd, uint32_t events, void *user_data) { char buf[4096]; ssize_t len; if (events & (EPOLLERR | EPOLLHUP)) { mainloop_remove_fd(fd); return; } len = read(fd, buf, sizeof(buf)); if (len < 0) return; printf("Received %s\n", buf); if (!strcmp(buf, "bluetooth.start=daemon")) { if (daemon_pid > 0) return; ctl_start(); } else if (!strcmp(buf, "bluetooth.start=snoop")) { if (snoop_pid > 0) return; snoop_start(); } else if (!strcmp(buf, "bluetooth.stop=snoop")) { if (snoop_pid > 0) snoop_stop(); } } static void signal_callback(int signum, void *user_data) { switch (signum) { case SIGINT: case SIGTERM: mainloop_quit(); break; case SIGCHLD: while (1) { pid_t pid; int status; pid = waitpid(WAIT_ANY, &status, WNOHANG); if (pid < 0 || pid == 0) break; printf("Process %d terminated with status=%d\n", pid, status); if (pid == daemon_pid) daemon_pid = -1; else if (pid == snoop_pid) snoop_pid = -1; } break; } } int main(int argc, char *argv[]) { const char SYSTEM_SOCKET_PATH[] = "\0android_system"; struct sockaddr_un addr; int fd; mainloop_init(); printf("Android system emulator ver %s\n", VERSION); snprintf(exec_dir, sizeof(exec_dir), "%s", dirname(argv[0])); fd = socket(PF_LOCAL, SOCK_DGRAM | SOCK_CLOEXEC, 0); if (fd < 0) { perror("Failed to create system socket"); return EXIT_FAILURE; } memset(&addr, 0, sizeof(addr)); addr.sun_family = AF_UNIX; memcpy(addr.sun_path, SYSTEM_SOCKET_PATH, sizeof(SYSTEM_SOCKET_PATH)); if (bind(fd, (struct sockaddr *) &addr, sizeof(addr)) < 0) { perror("Failed to bind system socket"); close(fd); return EXIT_FAILURE; } mainloop_add_fd(fd, EPOLLIN, system_socket_callback, NULL, NULL); /* Make sure bluetoothd creates files with proper permissions */ umask(0177); return mainloop_run_with_signal(signal_callback, NULL); } bluez-5.82/android/PaxHeaders/pixit-scpp.txt0000644000000000000000000000005012537515745016145 xustar0020 atime=1743516876 20 ctime=1743591289 bluez-5.82/android/pixit-scpp.txt0000644000000000000000000000154212537515745015630 0ustar00rootrootScPP PIXIT for the PTS tool. PTS version: 6.1 * - different than PTS defaults & - should be set to IUT Bluetooth address Required PIXIT settings ------------------------------------------------------------------------------- Parameter Name Value ------------------------------------------------------------------------------- TSPX_bd_addr_iut 112233445566 (*&) TSPX_time_guard 180000 TSPX_use_implicit_send TRUE TSPX_tester_database_file C:/Program Files/... TSPX_mtu_size 23 TSPX_secure_simple_pairing_pass_key_confirmation FALSE TSPX_delete_link_key FALSE TSPX_pin_code 0000 TSPX_use_dynamic_pin FALSE TSPX_delete_ltk FALSE TSPX_security_enabled FALSE TSPX_tester_appearance 0000 TSPX_iut_use_resolvable_random_address FALSE ------------------------------------------------------------------------------- bluez-5.82/android/PaxHeaders/tester-map-client.c0000644000000000000000000000005014015011623016760 xustar0020 atime=1743516864 20 ctime=1743591278 bluez-5.82/android/tester-map-client.c0000644000000000000000000001030514015011623016440 0ustar00rootroot// SPDX-License-Identifier: Apache-2.0 /* * Copyright (C) 2014 Intel Corporation * */ #define _GNU_SOURCE #include #include "emulator/bthost.h" #include "src/shared/tester.h" #include "src/shared/queue.h" #include "tester-main.h" static struct queue *list = NULL; /* List of map client test cases */ #define INST0_ID 0 #define INST1_ID 1 #define sdp_rsp_pdu 0x07, \ 0x00, 0x00, \ 0x00, 0xb5, \ 0x00, 0xb2, \ 0x35, 0xb0, 0x36, 0x00, 0x56, 0x09, 0x00, 0x00, 0x0a, \ 0x00, 0x01, 0x00, 0x09, 0x09, 0x00, 0x01, 0x35, 0x03, \ 0x19, 0x11, 0x32, 0x09, 0x00, 0x04, 0x35, 0x11, 0x35, \ 0x03, 0x19, 0x01, 0x00, 0x35, 0x05, 0x19, 0x00, 0x03, \ 0x08, 0x04, 0x35, 0x03, 0x19, 0x00, 0x08, 0x09, 0x00, \ 0x05, 0x35, 0x03, 0x19, 0x10, 0x02, 0x09, 0x00, 0x09, \ 0x35, 0x08, 0x35, 0x06, 0x19, 0x11, 0x34, 0x09, 0x01, \ 0x01, 0x09, 0x01, 0x00, 0x25, 0x0c, 0x4d, 0x41, 0x50, \ 0x20, 0x53, 0x4d, 0x53, 0x2f, 0x4d, 0x4d, 0x53, 0x00, \ 0x09, 0x03, 0x15, 0x08, 0x00, 0x09, 0x03, 0x16, 0x08, \ 0x0e, 0x36, 0x00, 0x54, 0x09, 0x00, 0x00, 0x0a, 0x00, \ 0x01, 0x00, 0x0a, 0x09, 0x00, 0x01, 0x35, 0x03, 0x19, \ 0x11, 0x32, 0x09, 0x00, 0x04, 0x35, 0x11, 0x35, 0x03, \ 0x19, 0x01, 0x00, 0x35, 0x05, 0x19, 0x00, 0x03, 0x08, \ 0x05, 0x35, 0x03, 0x19, 0x00, 0x08, 0x09, 0x00, 0x05, \ 0x35, 0x03, 0x19, 0x10, 0x02, 0x09, 0x00, 0x09, 0x35, \ 0x08, 0x35, 0x06, 0x19, 0x11, 0x34, 0x09, 0x01, 0x01, \ 0x09, 0x01, 0x00, 0x25, 0x0a, 0x4d, 0x41, 0x50, 0x20, \ 0x45, 0x4d, 0x41, 0x49, 0x4c, 0x00, 0x09, 0x03, 0x15, \ 0x08, 0x01, 0x09, 0x03, 0x16, 0x08, 0x01, \ 0x00 static const struct pdu_set pdus[] = { { end_pdu, raw_pdu(sdp_rsp_pdu) }, { end_pdu, end_pdu }, }; static struct emu_l2cap_cid_data cid_data = { .pdu = pdus, }; static bt_bdaddr_t emu_remote_bdaddr_val = { .address = { 0x00, 0xaa, 0x01, 0x01, 0x00, 0x00 }, }; static struct emu_set_l2cap_data l2cap_sdp_setup_data = { .psm = 1, .func = tester_generic_connect_cb, .user_data = &cid_data, }; /* TODO define all parameters according to specification document */ static btmce_mas_instance_t remote_map_inst_sms_mms_email_val[] = { { INST0_ID, 4, 14, "MAP SMS/MMS" }, { INST1_ID, 5, 1, "MAP EMAIL" }, }; static void map_client_cid_hook_cb(const void *data, uint16_t len, void *user_data) { /* TODO extend if needed */ } static void map_client_conn_cb(uint16_t handle, void *user_data) { struct test_data *data = tester_get_data(); struct bthost *bthost = hciemu_client_get_host(data->hciemu); tester_print("New connection with handle 0x%04x", handle); if (data->hciemu_type == HCIEMU_TYPE_BREDR) { tester_warn("Not handled device type."); return; } cid_data.cid = 0x0040; cid_data.handle = handle; bthost_add_cid_hook(bthost, handle, cid_data.cid, map_client_cid_hook_cb, &cid_data); } static void map_client_get_instances_action(void) { struct test_data *data = tester_get_data(); struct step *current_data_step = queue_peek_head(data->steps); bt_bdaddr_t *bd_addr = current_data_step->set_data; struct step *step = g_new0(struct step, 1); step->action_status = data->if_map_client->get_remote_mas_instances(bd_addr); schedule_action_verification(step); } static struct test_case test_cases[] = { TEST_CASE_BREDRLE("MAP Client Init", ACTION_SUCCESS(dummy_action, NULL), ), TEST_CASE_BREDRLE("MAP Client - Get mas instances success", ACTION_SUCCESS(bluetooth_enable_action, NULL), CALLBACK_STATE(CB_BT_ADAPTER_STATE_CHANGED, BT_STATE_ON), ACTION_SUCCESS(emu_setup_powered_remote_action, NULL), ACTION_SUCCESS(emu_add_l2cap_server_action, &l2cap_sdp_setup_data), ACTION_SUCCESS(emu_set_connect_cb_action, map_client_conn_cb), ACTION_SUCCESS(map_client_get_instances_action, &emu_remote_bdaddr_val), CALLBACK_MAP_CLIENT_REMOTE_MAS_INSTANCE(BT_STATUS_SUCCESS, NULL, 2, remote_map_inst_sms_mms_email_val), ACTION_SUCCESS(bluetooth_disable_action, NULL), CALLBACK_STATE(CB_BT_ADAPTER_STATE_CHANGED, BT_STATE_OFF), ), }; struct queue *get_map_client_tests(void) { uint16_t i = 0; list = queue_new(); for (; i < sizeof(test_cases) / sizeof(test_cases[0]); ++i) queue_push_tail(list, &test_cases[i]); return list; } void remove_map_client_tests(void) { queue_destroy(list, NULL); } bluez-5.82/android/PaxHeaders/pixit-rfcomm.txt0000644000000000000000000000005012537515745016463 xustar0020 atime=1743516876 20 ctime=1743591289 bluez-5.82/android/pixit-rfcomm.txt0000644000000000000000000000166212537515745016151 0ustar00rootrootRFCOMM PIXIT for the PTS tool. PTS version: 6.1 * - different than PTS defaults & - should be set to IUT Bluetooth address # - should be set to PTS's bin/audio folder Required PIXIT settings ------------------------------------------------------------------------------- Parameter Name Value ------------------------------------------------------------------------------- TSPX_bd_addr_iut 11223344556677 (*&) TSPX_delete_link_key FALSE TSPX_pin_code 1234 TSPX_security_enabled TRUE TSPX_time_guard 300000 TSPX_use_implicit_send TRUE TSPX_service_name_tester COM5 TSPX_class_of_device_tester 200408 TSPX_server_channel_iut 01 (*) TSPX_verification_pattern_length 4 TSPX_T1_Acknowledgement_Timer 20000 TSPX_T1_Acknowledgement_Timer_Dlc 300000 TSPX_T2_Response_Timer 20000 TSPX_max_frame_size_iut 127 TSPX_RPN_parameters_iut 0302001113 ------------------------------------------------------------------------------- bluez-5.82/android/PaxHeaders/pics-hsp.txt0000644000000000000000000000005012537515745015573 xustar0020 atime=1743516876 20 ctime=1743591289 bluez-5.82/android/pics-hsp.txt0000644000000000000000000001203712537515745015257 0ustar00rootrootHSP PICS for the PTS tool. PTS version: 6.1 * - different than PTS defaults # - not yet implemented/supported M - mandatory O - optional Version ------------------------------------------------------------------------------- Parameter Name Selected Description ------------------------------------------------------------------------------- TSPC_HSP_0_1 False Version: Headset Profile v1.1 (C.1) TSPC_HSP_0_2 True (*) Version: Headset Profile v1.2 (C.1) ------------------------------------------------------------------------------- C.1: Mandatory to support one and only one of these versions. ------------------------------------------------------------------------------- Roles ------------------------------------------------------------------------------- Parameter Name Selected Description ------------------------------------------------------------------------------- TSPC_HSP_1_1 True (*) Role: Audio Gateway (AG) (C.1) TSPC_HSP_1_2 False Role: Headset (HS) (C.1) ------------------------------------------------------------------------------- C.1: Mandatory to support at least one of the defined roles. ------------------------------------------------------------------------------- Audio Gateway Role ------------------------------------------------------------------------------- Parameter Name Selected Description ------------------------------------------------------------------------------- TSPC_HSP_2_1 True Incoming audio connection establishment (M) TSPC_HSP_2_2 True (*) Ring (AT command) (C.3) TSPC_HSP_2_3 False Inband ring tone (O) TSPC_HSP_2_4 True (*) Outgoing audio connection establishment (O) TSPC_HSP_2_5 True (*) Audio connection release from HS (C.5) TSPC_HSP_2_6 True Audio connection release from AG (M) TSPC_HSP_2_7 True Audio connection transfer: AG to HS (M) TSPC_HSP_2_8 True Audio connection transfer: HS to AG (M) TSPC_HSP_2_9 True (*) Remote audio volume control (C.1) TSPC_HSP_2_10 True (*) HS informs AG about local changes of audio volume (O) TSPC_HSP_2_11 True (*) Audio volume setting storage by HS (O) TSPC_HSP_2_12 False Remote microphone gain control (C.2) TSPC_HSP_2_13 False HS informs AG about local changes of microphone gain (O) TSPC_HSP_2_14 False Microphone gain setting storage by HS (O) TSPC_HSP_2_15 True Connection handling with Detach/Page (M) TSPC_HSP_2_16 False Connection handling with Park Mode (C.4) ------------------------------------------------------------------------------- C.1: Mandatory if TSPC_HSP_2_10 is supported, otherwise optional C:2: Mandatory if TSPC_HSP_2_13 is supported, otherwise optional C.3: Excluded if TSPC_HSP_2_3 and TSPC_HSP_4_1 ("Show that in-band ringing and RING are mutually exclusive") are supported, otherwise optional C.4: Excluded if TSPC_HSP_0_2 is supported, otherwise optional C.5: Mandatory if TSPC_HSP_0_1 is supported, otherwise optional ------------------------------------------------------------------------------- Headset Application Features ------------------------------------------------------------------------------- Parameter Name Selected Description ------------------------------------------------------------------------------- TSPC_HSP_3_1 False (*) Incoming audio connection establishment (M) TSPC_HSP_3_2 False (*) Ring (AT command) (M) TSPC_HSP_3_3 False (*) Inband ring tone (M) TSPC_HSP_3_4 False (*) Outgoing audio connection establishment (M) TSPC_HSP_3_5 False (*) Audio connection release from HS (M) TSPC_HSP_3_6 False (*) Audio connection release from AG (M) TSPC_HSP_3_7 False (*) Audio connection transfer: AG to HS (M) TSPC_HSP_3_8 False (*) Audio connection transfer: HS to AG (M) TSPC_HSP_3_9 False Remote audio volume control (C.1) TSPC_HSP_3_10 False HS informs AG about local changes of audio volume (O) TSPC_HSP_3_11 False Audio volume setting storage by HS (O) TSPC_HSP_3_12 False Remote microphone gain control (C.2) TSPC_HSP_3_13 False HS informs AG about local changes of microphone gain (O) TSPC_HSP_3_14 False Microphone gain setting storage by HS (O) TSPC_HSP_3_15 False (*) Connection handling with Detach/Page (M) TSPC_HSP_3_16 False Connection handling with Park Mode (C.3) ------------------------------------------------------------------------------- C.1: Mandatory if TSPC_HSP_3_10 is supported, otherwise optional C.2: Mandatory if TSPC_HSP_2_13 is supported, otherwise optional C.3: Excluded if TSPC_HSP_0_2 is supported, otherwise optional ------------------------------------------------------------------------------- Errata Service Releases ------------------------------------------------------------------------------- Parameter Name Selected Description ------------------------------------------------------------------------------- TSPC_HSP_4_1 False Show that in-band ringing and RING are mutually exclusive (C.1) ------------------------------------------------------------------------------- C.1: Excluded if TSPC_HSP_0_2 is supported, otherwise optional ------------------------------------------------------------------------------- bluez-5.82/android/PaxHeaders/pixit-avctp.txt0000644000000000000000000000005012537515745016315 xustar0020 atime=1743516876 20 ctime=1743591289 bluez-5.82/android/pixit-avctp.txt0000644000000000000000000000231412537515745015776 0ustar00rootrootAVCTP PIXIT for the PTS tool. PTS version: 6.1 * - different than PTS defaults & - should be set to IUT Bluetooth address # - should be set to PTS's bin/audio folder Required PIXIT settings ------------------------------------------------------------------------------- Parameter Name Value ------------------------------------------------------------------------------- TSPX_avctp_psm 0017 TSPX_avctp_profile_id 110E TSPX_connect_avdtp TRUE TSPX_avctp_tester_command_data TSPX_avctp_tester_response_data TSPX_avctp_iut_command_data TSPX_avctp_iut_response_data TSPX_bd_addr_iut 11223344556677 (*&) TSPX_pin_code 0000 TSPX_delete_link_key FALSE TSPX_security_enabled FALSE TSPX_class_of_device 20050C TSPX_player_feature_bitmask 0000000000000007FFF00070000000000 TSPX_avrcp_version TSPX_establish_avdtp_stream TRUE TSPX_tester_av_role SNK (*) TSPX_time_guard 300000 TSPX_avrcp_only FALSE TSPX_use_implicit_send TRUE TSPX_media_directory C:\Program Files\Bluetooth SIG\Bluetooth PTS\ bin\audio (#) TSPX_no_confirmations FALSE TSPX_auth_password 0000 TSPX_auth_user_id PTS TSPX_rfcomm_channel 8 TSPX_l2cap_psm 1011 ------------------------------------------------------------------------------- bluez-5.82/android/PaxHeaders/pics-gavdp.txt0000644000000000000000000000005012537515745016102 xustar0020 atime=1743516876 20 ctime=1743591289 bluez-5.82/android/pics-gavdp.txt0000644000000000000000000000307212537515745015565 0ustar00rootrootGAVDP PICS for the PTS tool. PTS version: 6.1 * - different than PTS defaults # - not yet implemented/supported M - mandatory O - optional Role ------------------------------------------------------------------------------- Parameter Name Selected Description ------------------------------------------------------------------------------- TSPC_GAVDP_1_1 True (*) Initiator (C.1) TSPC_GAVDP_1_2 True (*) Initiator (C.1) ------------------------------------------------------------------------------- C.1: Mandatory if Acceptor/Initiator is not supported ------------------------------------------------------------------------------- GAVDP Procedures (Initiator) ------------------------------------------------------------------------------- Parameter Name Selected Description ------------------------------------------------------------------------------- TSPC_GAVDP_2_1 True Connection Establishment (M) TSPC_GAVDP_2_2 True (*) Transfer Control -Suspend (O) TSPC_GAVDP_2_3 False Transfer Control – Change Parameters (O) ------------------------------------------------------------------------------- GAVDP Procedures (Acceptor) ------------------------------------------------------------------------------- Parameter Name Selected Description ------------------------------------------------------------------------------- TSPC_GAVDP_3_1 True Connection Establishment (M) TSPC_GAVDP_3_2 True (*) Transfer Control -Suspend (O) TSPC_GAVDP_3_3 False Transfer Control – Change Parameters (O) ------------------------------------------------------------------------------- bluez-5.82/android/PaxHeaders/system0000644000000000000000000000005014773213554014550 xustar0020 atime=1743591291 20 ctime=1743591276 bluez-5.82/android/system/0000755000000000000000000000000014773213554014306 5ustar00rootrootbluez-5.82/android/system/PaxHeaders/audio.h0000644000000000000000000000005014015011623016055 xustar0020 atime=1743516862 20 ctime=1743591276 bluez-5.82/android/system/audio.h0000644000000000000000000016413314015011623015546 0ustar00rootroot/* SPDX-License-Identifier: Apache-2.0 */ /* * Copyright (C) 2011 The Android Open Source Project * */ #ifndef ANDROID_AUDIO_CORE_H #define ANDROID_AUDIO_CORE_H #include #include #include #include #include __BEGIN_DECLS #define popcount __builtin_popcount /* The enums were moved here mostly from * frameworks/base/include/media/AudioSystem.h */ /* device address used to refer to the standard remote submix */ #define AUDIO_REMOTE_SUBMIX_DEVICE_ADDRESS "0" /* AudioFlinger and AudioPolicy services use I/O handles to identify audio sources and sinks */ typedef int audio_io_handle_t; #define AUDIO_IO_HANDLE_NONE 0 /* Audio stream types */ typedef enum { /* These values must kept in sync with * frameworks/base/media/java/android/media/AudioSystem.java */ AUDIO_STREAM_DEFAULT = -1, AUDIO_STREAM_MIN = 0, AUDIO_STREAM_VOICE_CALL = 0, AUDIO_STREAM_SYSTEM = 1, AUDIO_STREAM_RING = 2, AUDIO_STREAM_MUSIC = 3, AUDIO_STREAM_ALARM = 4, AUDIO_STREAM_NOTIFICATION = 5, AUDIO_STREAM_BLUETOOTH_SCO = 6, AUDIO_STREAM_ENFORCED_AUDIBLE = 7, /* Sounds that cannot be muted by user * and must be routed to speaker */ AUDIO_STREAM_DTMF = 8, AUDIO_STREAM_TTS = 9, AUDIO_STREAM_CNT, AUDIO_STREAM_MAX = AUDIO_STREAM_CNT - 1, } audio_stream_type_t; /* Do not change these values without updating their counterparts * in frameworks/base/media/java/android/media/AudioAttributes.java */ typedef enum { AUDIO_CONTENT_TYPE_UNKNOWN = 0, AUDIO_CONTENT_TYPE_SPEECH = 1, AUDIO_CONTENT_TYPE_MUSIC = 2, AUDIO_CONTENT_TYPE_MOVIE = 3, AUDIO_CONTENT_TYPE_SONIFICATION = 4, AUDIO_CONTENT_TYPE_CNT, AUDIO_CONTENT_TYPE_MAX = AUDIO_CONTENT_TYPE_CNT - 1, } audio_content_type_t; /* Do not change these values without updating their counterparts * in frameworks/base/media/java/android/media/AudioAttributes.java */ typedef enum { AUDIO_USAGE_UNKNOWN = 0, AUDIO_USAGE_MEDIA = 1, AUDIO_USAGE_VOICE_COMMUNICATION = 2, AUDIO_USAGE_VOICE_COMMUNICATION_SIGNALLING = 3, AUDIO_USAGE_ALARM = 4, AUDIO_USAGE_NOTIFICATION = 5, AUDIO_USAGE_NOTIFICATION_TELEPHONY_RINGTONE = 6, AUDIO_USAGE_NOTIFICATION_COMMUNICATION_REQUEST = 7, AUDIO_USAGE_NOTIFICATION_COMMUNICATION_INSTANT = 8, AUDIO_USAGE_NOTIFICATION_COMMUNICATION_DELAYED = 9, AUDIO_USAGE_NOTIFICATION_EVENT = 10, AUDIO_USAGE_ASSISTANCE_ACCESSIBILITY = 11, AUDIO_USAGE_ASSISTANCE_NAVIGATION_GUIDANCE = 12, AUDIO_USAGE_ASSISTANCE_SONIFICATION = 13, AUDIO_USAGE_GAME = 14, AUDIO_USAGE_CNT, AUDIO_USAGE_MAX = AUDIO_USAGE_CNT - 1, } audio_usage_t; typedef uint32_t audio_flags_mask_t; /* Do not change these values without updating their counterparts * in frameworks/base/media/java/android/media/AudioAttributes.java */ enum { AUDIO_FLAG_AUDIBILITY_ENFORCED = 0x1, AUDIO_FLAG_SECURE = 0x2, AUDIO_FLAG_SCO = 0x4, AUDIO_FLAG_BEACON = 0x8, AUDIO_FLAG_HW_AV_SYNC = 0x10, AUDIO_FLAG_HW_HOTWORD = 0x20, }; /* Do not change these values without updating their counterparts * in frameworks/base/media/java/android/media/MediaRecorder.java, * frameworks/av/services/audiopolicy/AudioPolicyService.cpp, * and system/media/audio_effects/include/audio_effects/audio_effects_conf.h! */ typedef enum { AUDIO_SOURCE_DEFAULT = 0, AUDIO_SOURCE_MIC = 1, AUDIO_SOURCE_VOICE_UPLINK = 2, AUDIO_SOURCE_VOICE_DOWNLINK = 3, AUDIO_SOURCE_VOICE_CALL = 4, AUDIO_SOURCE_CAMCORDER = 5, AUDIO_SOURCE_VOICE_RECOGNITION = 6, AUDIO_SOURCE_VOICE_COMMUNICATION = 7, AUDIO_SOURCE_REMOTE_SUBMIX = 8, /* Source for the mix to be presented remotely. */ /* An example of remote presentation is Wifi Display */ /* where a dongle attached to a TV can be used to */ /* play the mix captured by this audio source. */ AUDIO_SOURCE_CNT, AUDIO_SOURCE_MAX = AUDIO_SOURCE_CNT - 1, AUDIO_SOURCE_HOTWORD = 1999, /* A low-priority, preemptible audio source for for background software hotword detection. Same tuning as AUDIO_SOURCE_VOICE_RECOGNITION. Used only internally to the framework. Not exposed at the audio HAL. */ } audio_source_t; /* Audio attributes */ #define AUDIO_ATTRIBUTES_TAGS_MAX_SIZE 256 typedef struct { audio_content_type_t content_type; audio_usage_t usage; audio_source_t source; audio_flags_mask_t flags; char tags[AUDIO_ATTRIBUTES_TAGS_MAX_SIZE]; /* UTF8 */ } audio_attributes_t; /* special audio session values * (XXX: should this be living in the audio effects land?) */ typedef enum { /* session for effects attached to a particular output stream * (value must be less than 0) */ AUDIO_SESSION_OUTPUT_STAGE = -1, /* session for effects applied to output mix. These effects can * be moved by audio policy manager to another output stream * (value must be 0) */ AUDIO_SESSION_OUTPUT_MIX = 0, /* application does not specify an explicit session ID to be used, * and requests a new session ID to be allocated * TODO use unique values for AUDIO_SESSION_OUTPUT_MIX and AUDIO_SESSION_ALLOCATE, * after all uses have been updated from 0 to the appropriate symbol, and have been tested. */ AUDIO_SESSION_ALLOCATE = 0, } audio_session_t; /* a unique ID allocated by AudioFlinger for use as a audio_io_handle_t or audio_session_t */ typedef int audio_unique_id_t; #define AUDIO_UNIQUE_ID_ALLOCATE AUDIO_SESSION_ALLOCATE /* Audio sub formats (see enum audio_format). */ /* PCM sub formats */ typedef enum { /* All of these are in native byte order */ AUDIO_FORMAT_PCM_SUB_16_BIT = 0x1, /* DO NOT CHANGE - PCM signed 16 bits */ AUDIO_FORMAT_PCM_SUB_8_BIT = 0x2, /* DO NOT CHANGE - PCM unsigned 8 bits */ AUDIO_FORMAT_PCM_SUB_32_BIT = 0x3, /* PCM signed .31 fixed point */ AUDIO_FORMAT_PCM_SUB_8_24_BIT = 0x4, /* PCM signed 7.24 fixed point */ AUDIO_FORMAT_PCM_SUB_FLOAT = 0x5, /* PCM single-precision floating point */ AUDIO_FORMAT_PCM_SUB_24_BIT_PACKED = 0x6, /* PCM signed .23 fixed point packed in 3 bytes */ } audio_format_pcm_sub_fmt_t; /* The audio_format_*_sub_fmt_t declarations are not currently used */ /* MP3 sub format field definition : can use 11 LSBs in the same way as MP3 * frame header to specify bit rate, stereo mode, version... */ typedef enum { AUDIO_FORMAT_MP3_SUB_NONE = 0x0, } audio_format_mp3_sub_fmt_t; /* AMR NB/WB sub format field definition: specify frame block interleaving, * bandwidth efficient or octet aligned, encoding mode for recording... */ typedef enum { AUDIO_FORMAT_AMR_SUB_NONE = 0x0, } audio_format_amr_sub_fmt_t; /* AAC sub format field definition: specify profile or bitrate for recording... */ typedef enum { AUDIO_FORMAT_AAC_SUB_MAIN = 0x1, AUDIO_FORMAT_AAC_SUB_LC = 0x2, AUDIO_FORMAT_AAC_SUB_SSR = 0x4, AUDIO_FORMAT_AAC_SUB_LTP = 0x8, AUDIO_FORMAT_AAC_SUB_HE_V1 = 0x10, AUDIO_FORMAT_AAC_SUB_SCALABLE = 0x20, AUDIO_FORMAT_AAC_SUB_ERLC = 0x40, AUDIO_FORMAT_AAC_SUB_LD = 0x80, AUDIO_FORMAT_AAC_SUB_HE_V2 = 0x100, AUDIO_FORMAT_AAC_SUB_ELD = 0x200, } audio_format_aac_sub_fmt_t; /* VORBIS sub format field definition: specify quality for recording... */ typedef enum { AUDIO_FORMAT_VORBIS_SUB_NONE = 0x0, } audio_format_vorbis_sub_fmt_t; /* Audio format consists of a main format field (upper 8 bits) and a sub format * field (lower 24 bits). * * The main format indicates the main codec type. The sub format field * indicates options and parameters for each format. The sub format is mainly * used for record to indicate for instance the requested bitrate or profile. * It can also be used for certain formats to give informations not present in * the encoded audio stream (e.g. octet alignement for AMR). */ typedef enum { AUDIO_FORMAT_INVALID = 0xFFFFFFFFUL, AUDIO_FORMAT_DEFAULT = 0, AUDIO_FORMAT_PCM = 0x00000000UL, /* DO NOT CHANGE */ AUDIO_FORMAT_MP3 = 0x01000000UL, AUDIO_FORMAT_AMR_NB = 0x02000000UL, AUDIO_FORMAT_AMR_WB = 0x03000000UL, AUDIO_FORMAT_AAC = 0x04000000UL, AUDIO_FORMAT_HE_AAC_V1 = 0x05000000UL, /* Deprecated, Use AUDIO_FORMAT_AAC_HE_V1*/ AUDIO_FORMAT_HE_AAC_V2 = 0x06000000UL, /* Deprecated, Use AUDIO_FORMAT_AAC_HE_V2*/ AUDIO_FORMAT_VORBIS = 0x07000000UL, AUDIO_FORMAT_OPUS = 0x08000000UL, AUDIO_FORMAT_AC3 = 0x09000000UL, AUDIO_FORMAT_E_AC3 = 0x0A000000UL, AUDIO_FORMAT_MAIN_MASK = 0xFF000000UL, AUDIO_FORMAT_SUB_MASK = 0x00FFFFFFUL, /* Aliases */ /* note != AudioFormat.ENCODING_PCM_16BIT */ AUDIO_FORMAT_PCM_16_BIT = (AUDIO_FORMAT_PCM | AUDIO_FORMAT_PCM_SUB_16_BIT), /* note != AudioFormat.ENCODING_PCM_8BIT */ AUDIO_FORMAT_PCM_8_BIT = (AUDIO_FORMAT_PCM | AUDIO_FORMAT_PCM_SUB_8_BIT), AUDIO_FORMAT_PCM_32_BIT = (AUDIO_FORMAT_PCM | AUDIO_FORMAT_PCM_SUB_32_BIT), AUDIO_FORMAT_PCM_8_24_BIT = (AUDIO_FORMAT_PCM | AUDIO_FORMAT_PCM_SUB_8_24_BIT), AUDIO_FORMAT_PCM_FLOAT = (AUDIO_FORMAT_PCM | AUDIO_FORMAT_PCM_SUB_FLOAT), AUDIO_FORMAT_PCM_24_BIT_PACKED = (AUDIO_FORMAT_PCM | AUDIO_FORMAT_PCM_SUB_24_BIT_PACKED), AUDIO_FORMAT_AAC_MAIN = (AUDIO_FORMAT_AAC | AUDIO_FORMAT_AAC_SUB_MAIN), AUDIO_FORMAT_AAC_LC = (AUDIO_FORMAT_AAC | AUDIO_FORMAT_AAC_SUB_LC), AUDIO_FORMAT_AAC_SSR = (AUDIO_FORMAT_AAC | AUDIO_FORMAT_AAC_SUB_SSR), AUDIO_FORMAT_AAC_LTP = (AUDIO_FORMAT_AAC | AUDIO_FORMAT_AAC_SUB_LTP), AUDIO_FORMAT_AAC_HE_V1 = (AUDIO_FORMAT_AAC | AUDIO_FORMAT_AAC_SUB_HE_V1), AUDIO_FORMAT_AAC_SCALABLE = (AUDIO_FORMAT_AAC | AUDIO_FORMAT_AAC_SUB_SCALABLE), AUDIO_FORMAT_AAC_ERLC = (AUDIO_FORMAT_AAC | AUDIO_FORMAT_AAC_SUB_ERLC), AUDIO_FORMAT_AAC_LD = (AUDIO_FORMAT_AAC | AUDIO_FORMAT_AAC_SUB_LD), AUDIO_FORMAT_AAC_HE_V2 = (AUDIO_FORMAT_AAC | AUDIO_FORMAT_AAC_SUB_HE_V2), AUDIO_FORMAT_AAC_ELD = (AUDIO_FORMAT_AAC | AUDIO_FORMAT_AAC_SUB_ELD), } audio_format_t; /* For the channel mask for position assignment representation */ enum { /* These can be a complete audio_channel_mask_t. */ AUDIO_CHANNEL_NONE = 0x0, AUDIO_CHANNEL_INVALID = 0xC0000000, /* These can be the bits portion of an audio_channel_mask_t * with representation AUDIO_CHANNEL_REPRESENTATION_POSITION. * Using these bits as a complete audio_channel_mask_t is deprecated. */ /* output channels */ AUDIO_CHANNEL_OUT_FRONT_LEFT = 0x1, AUDIO_CHANNEL_OUT_FRONT_RIGHT = 0x2, AUDIO_CHANNEL_OUT_FRONT_CENTER = 0x4, AUDIO_CHANNEL_OUT_LOW_FREQUENCY = 0x8, AUDIO_CHANNEL_OUT_BACK_LEFT = 0x10, AUDIO_CHANNEL_OUT_BACK_RIGHT = 0x20, AUDIO_CHANNEL_OUT_FRONT_LEFT_OF_CENTER = 0x40, AUDIO_CHANNEL_OUT_FRONT_RIGHT_OF_CENTER = 0x80, AUDIO_CHANNEL_OUT_BACK_CENTER = 0x100, AUDIO_CHANNEL_OUT_SIDE_LEFT = 0x200, AUDIO_CHANNEL_OUT_SIDE_RIGHT = 0x400, AUDIO_CHANNEL_OUT_TOP_CENTER = 0x800, AUDIO_CHANNEL_OUT_TOP_FRONT_LEFT = 0x1000, AUDIO_CHANNEL_OUT_TOP_FRONT_CENTER = 0x2000, AUDIO_CHANNEL_OUT_TOP_FRONT_RIGHT = 0x4000, AUDIO_CHANNEL_OUT_TOP_BACK_LEFT = 0x8000, AUDIO_CHANNEL_OUT_TOP_BACK_CENTER = 0x10000, AUDIO_CHANNEL_OUT_TOP_BACK_RIGHT = 0x20000, /* TODO: should these be considered complete channel masks, or only bits? */ AUDIO_CHANNEL_OUT_MONO = AUDIO_CHANNEL_OUT_FRONT_LEFT, AUDIO_CHANNEL_OUT_STEREO = (AUDIO_CHANNEL_OUT_FRONT_LEFT | AUDIO_CHANNEL_OUT_FRONT_RIGHT), AUDIO_CHANNEL_OUT_QUAD = (AUDIO_CHANNEL_OUT_FRONT_LEFT | AUDIO_CHANNEL_OUT_FRONT_RIGHT | AUDIO_CHANNEL_OUT_BACK_LEFT | AUDIO_CHANNEL_OUT_BACK_RIGHT), AUDIO_CHANNEL_OUT_QUAD_BACK = AUDIO_CHANNEL_OUT_QUAD, /* like AUDIO_CHANNEL_OUT_QUAD_BACK with *_SIDE_* instead of *_BACK_* */ AUDIO_CHANNEL_OUT_QUAD_SIDE = (AUDIO_CHANNEL_OUT_FRONT_LEFT | AUDIO_CHANNEL_OUT_FRONT_RIGHT | AUDIO_CHANNEL_OUT_SIDE_LEFT | AUDIO_CHANNEL_OUT_SIDE_RIGHT), AUDIO_CHANNEL_OUT_5POINT1 = (AUDIO_CHANNEL_OUT_FRONT_LEFT | AUDIO_CHANNEL_OUT_FRONT_RIGHT | AUDIO_CHANNEL_OUT_FRONT_CENTER | AUDIO_CHANNEL_OUT_LOW_FREQUENCY | AUDIO_CHANNEL_OUT_BACK_LEFT | AUDIO_CHANNEL_OUT_BACK_RIGHT), AUDIO_CHANNEL_OUT_5POINT1_BACK = AUDIO_CHANNEL_OUT_5POINT1, /* like AUDIO_CHANNEL_OUT_5POINT1_BACK with *_SIDE_* instead of *_BACK_* */ AUDIO_CHANNEL_OUT_5POINT1_SIDE = (AUDIO_CHANNEL_OUT_FRONT_LEFT | AUDIO_CHANNEL_OUT_FRONT_RIGHT | AUDIO_CHANNEL_OUT_FRONT_CENTER | AUDIO_CHANNEL_OUT_LOW_FREQUENCY | AUDIO_CHANNEL_OUT_SIDE_LEFT | AUDIO_CHANNEL_OUT_SIDE_RIGHT), // matches the correct AudioFormat.CHANNEL_OUT_7POINT1_SURROUND definition for 7.1 AUDIO_CHANNEL_OUT_7POINT1 = (AUDIO_CHANNEL_OUT_FRONT_LEFT | AUDIO_CHANNEL_OUT_FRONT_RIGHT | AUDIO_CHANNEL_OUT_FRONT_CENTER | AUDIO_CHANNEL_OUT_LOW_FREQUENCY | AUDIO_CHANNEL_OUT_BACK_LEFT | AUDIO_CHANNEL_OUT_BACK_RIGHT | AUDIO_CHANNEL_OUT_SIDE_LEFT | AUDIO_CHANNEL_OUT_SIDE_RIGHT), AUDIO_CHANNEL_OUT_ALL = (AUDIO_CHANNEL_OUT_FRONT_LEFT | AUDIO_CHANNEL_OUT_FRONT_RIGHT | AUDIO_CHANNEL_OUT_FRONT_CENTER | AUDIO_CHANNEL_OUT_LOW_FREQUENCY | AUDIO_CHANNEL_OUT_BACK_LEFT | AUDIO_CHANNEL_OUT_BACK_RIGHT | AUDIO_CHANNEL_OUT_FRONT_LEFT_OF_CENTER | AUDIO_CHANNEL_OUT_FRONT_RIGHT_OF_CENTER | AUDIO_CHANNEL_OUT_BACK_CENTER| AUDIO_CHANNEL_OUT_SIDE_LEFT| AUDIO_CHANNEL_OUT_SIDE_RIGHT| AUDIO_CHANNEL_OUT_TOP_CENTER| AUDIO_CHANNEL_OUT_TOP_FRONT_LEFT| AUDIO_CHANNEL_OUT_TOP_FRONT_CENTER| AUDIO_CHANNEL_OUT_TOP_FRONT_RIGHT| AUDIO_CHANNEL_OUT_TOP_BACK_LEFT| AUDIO_CHANNEL_OUT_TOP_BACK_CENTER| AUDIO_CHANNEL_OUT_TOP_BACK_RIGHT), /* These are bits only, not complete values */ /* input channels */ AUDIO_CHANNEL_IN_LEFT = 0x4, AUDIO_CHANNEL_IN_RIGHT = 0x8, AUDIO_CHANNEL_IN_FRONT = 0x10, AUDIO_CHANNEL_IN_BACK = 0x20, AUDIO_CHANNEL_IN_LEFT_PROCESSED = 0x40, AUDIO_CHANNEL_IN_RIGHT_PROCESSED = 0x80, AUDIO_CHANNEL_IN_FRONT_PROCESSED = 0x100, AUDIO_CHANNEL_IN_BACK_PROCESSED = 0x200, AUDIO_CHANNEL_IN_PRESSURE = 0x400, AUDIO_CHANNEL_IN_X_AXIS = 0x800, AUDIO_CHANNEL_IN_Y_AXIS = 0x1000, AUDIO_CHANNEL_IN_Z_AXIS = 0x2000, AUDIO_CHANNEL_IN_VOICE_UPLINK = 0x4000, AUDIO_CHANNEL_IN_VOICE_DNLINK = 0x8000, /* TODO: should these be considered complete channel masks, or only bits, or deprecated? */ AUDIO_CHANNEL_IN_MONO = AUDIO_CHANNEL_IN_FRONT, AUDIO_CHANNEL_IN_STEREO = (AUDIO_CHANNEL_IN_LEFT | AUDIO_CHANNEL_IN_RIGHT), AUDIO_CHANNEL_IN_FRONT_BACK = (AUDIO_CHANNEL_IN_FRONT | AUDIO_CHANNEL_IN_BACK), AUDIO_CHANNEL_IN_ALL = (AUDIO_CHANNEL_IN_LEFT | AUDIO_CHANNEL_IN_RIGHT | AUDIO_CHANNEL_IN_FRONT | AUDIO_CHANNEL_IN_BACK| AUDIO_CHANNEL_IN_LEFT_PROCESSED | AUDIO_CHANNEL_IN_RIGHT_PROCESSED | AUDIO_CHANNEL_IN_FRONT_PROCESSED | AUDIO_CHANNEL_IN_BACK_PROCESSED| AUDIO_CHANNEL_IN_PRESSURE | AUDIO_CHANNEL_IN_X_AXIS | AUDIO_CHANNEL_IN_Y_AXIS | AUDIO_CHANNEL_IN_Z_AXIS | AUDIO_CHANNEL_IN_VOICE_UPLINK | AUDIO_CHANNEL_IN_VOICE_DNLINK), }; /* A channel mask per se only defines the presence or absence of a channel, not the order. * But see AUDIO_INTERLEAVE_* below for the platform convention of order. * * audio_channel_mask_t is an opaque type and its internal layout should not * be assumed as it may change in the future. * Instead, always use the functions declared in this header to examine. * * These are the current representations: * * AUDIO_CHANNEL_REPRESENTATION_POSITION * is a channel mask representation for position assignment. * Each low-order bit corresponds to the spatial position of a transducer (output), * or interpretation of channel (input). * The user of a channel mask needs to know the context of whether it is for output or input. * The constants AUDIO_CHANNEL_OUT_* or AUDIO_CHANNEL_IN_* apply to the bits portion. * It is not permitted for no bits to be set. * * AUDIO_CHANNEL_REPRESENTATION_INDEX * is a channel mask representation for index assignment. * Each low-order bit corresponds to a selected channel. * There is no platform interpretation of the various bits. * There is no concept of output or input. * It is not permitted for no bits to be set. * * All other representations are reserved for future use. * * Warning: current representation distinguishes between input and output, but this will not the be * case in future revisions of the platform. Wherever there is an ambiguity between input and output * that is currently resolved by checking the channel mask, the implementer should look for ways to * fix it with additional information outside of the mask. */ typedef uint32_t audio_channel_mask_t; /* Maximum number of channels for all representations */ #define AUDIO_CHANNEL_COUNT_MAX 30 /* log(2) of maximum number of representations, not part of public API */ #define AUDIO_CHANNEL_REPRESENTATION_LOG2 2 /* Representations */ typedef enum { AUDIO_CHANNEL_REPRESENTATION_POSITION = 0, // must be zero for compatibility // 1 is reserved for future use AUDIO_CHANNEL_REPRESENTATION_INDEX = 2, // 3 is reserved for future use } audio_channel_representation_t; /* The return value is undefined if the channel mask is invalid. */ static inline uint32_t audio_channel_mask_get_bits(audio_channel_mask_t channel) { return channel & ((1 << AUDIO_CHANNEL_COUNT_MAX) - 1); } /* The return value is undefined if the channel mask is invalid. */ static inline audio_channel_representation_t audio_channel_mask_get_representation( audio_channel_mask_t channel) { // The right shift should be sufficient, but also "and" for safety in case mask is not 32 bits return (audio_channel_representation_t) ((channel >> AUDIO_CHANNEL_COUNT_MAX) & ((1 << AUDIO_CHANNEL_REPRESENTATION_LOG2) - 1)); } /* Returns true if the channel mask is valid, * or returns false for AUDIO_CHANNEL_NONE, AUDIO_CHANNEL_INVALID, and other invalid values. * This function is unable to determine whether a channel mask for position assignment * is invalid because an output mask has an invalid output bit set, * or because an input mask has an invalid input bit set. * All other APIs that take a channel mask assume that it is valid. */ static inline bool audio_channel_mask_is_valid(audio_channel_mask_t channel) { uint32_t bits = audio_channel_mask_get_bits(channel); audio_channel_representation_t representation = audio_channel_mask_get_representation(channel); switch (representation) { case AUDIO_CHANNEL_REPRESENTATION_POSITION: case AUDIO_CHANNEL_REPRESENTATION_INDEX: break; default: bits = 0; break; } return bits != 0; } /* Not part of public API */ static inline audio_channel_mask_t audio_channel_mask_from_representation_and_bits( audio_channel_representation_t representation, uint32_t bits) { return (audio_channel_mask_t) ((representation << AUDIO_CHANNEL_COUNT_MAX) | bits); } /* Expresses the convention when stereo audio samples are stored interleaved * in an array. This should improve readability by allowing code to use * symbolic indices instead of hard-coded [0] and [1]. * * For multi-channel beyond stereo, the platform convention is that channels * are interleaved in order from least significant channel mask bit * to most significant channel mask bit, with unused bits skipped. * Any exceptions to this convention will be noted at the appropriate API. */ enum { AUDIO_INTERLEAVE_LEFT = 0, AUDIO_INTERLEAVE_RIGHT = 1, }; typedef enum { AUDIO_MODE_INVALID = -2, AUDIO_MODE_CURRENT = -1, AUDIO_MODE_NORMAL = 0, AUDIO_MODE_RINGTONE = 1, AUDIO_MODE_IN_CALL = 2, AUDIO_MODE_IN_COMMUNICATION = 3, AUDIO_MODE_CNT, AUDIO_MODE_MAX = AUDIO_MODE_CNT - 1, } audio_mode_t; /* This enum is deprecated */ typedef enum { AUDIO_IN_ACOUSTICS_NONE = 0, AUDIO_IN_ACOUSTICS_AGC_ENABLE = 0x0001, AUDIO_IN_ACOUSTICS_AGC_DISABLE = 0, AUDIO_IN_ACOUSTICS_NS_ENABLE = 0x0002, AUDIO_IN_ACOUSTICS_NS_DISABLE = 0, AUDIO_IN_ACOUSTICS_TX_IIR_ENABLE = 0x0004, AUDIO_IN_ACOUSTICS_TX_DISABLE = 0, } audio_in_acoustics_t; enum { AUDIO_DEVICE_NONE = 0x0, /* reserved bits */ AUDIO_DEVICE_BIT_IN = 0x80000000, AUDIO_DEVICE_BIT_DEFAULT = 0x40000000, /* output devices */ AUDIO_DEVICE_OUT_EARPIECE = 0x1, AUDIO_DEVICE_OUT_SPEAKER = 0x2, AUDIO_DEVICE_OUT_WIRED_HEADSET = 0x4, AUDIO_DEVICE_OUT_WIRED_HEADPHONE = 0x8, AUDIO_DEVICE_OUT_BLUETOOTH_SCO = 0x10, AUDIO_DEVICE_OUT_BLUETOOTH_SCO_HEADSET = 0x20, AUDIO_DEVICE_OUT_BLUETOOTH_SCO_CARKIT = 0x40, AUDIO_DEVICE_OUT_BLUETOOTH_A2DP = 0x80, AUDIO_DEVICE_OUT_BLUETOOTH_A2DP_HEADPHONES = 0x100, AUDIO_DEVICE_OUT_BLUETOOTH_A2DP_SPEAKER = 0x200, AUDIO_DEVICE_OUT_AUX_DIGITAL = 0x400, AUDIO_DEVICE_OUT_HDMI = AUDIO_DEVICE_OUT_AUX_DIGITAL, /* uses an analog connection (multiplexed over the USB connector pins for instance) */ AUDIO_DEVICE_OUT_ANLG_DOCK_HEADSET = 0x800, AUDIO_DEVICE_OUT_DGTL_DOCK_HEADSET = 0x1000, /* USB accessory mode: your Android device is a USB device and the dock is a USB host */ AUDIO_DEVICE_OUT_USB_ACCESSORY = 0x2000, /* USB host mode: your Android device is a USB host and the dock is a USB device */ AUDIO_DEVICE_OUT_USB_DEVICE = 0x4000, AUDIO_DEVICE_OUT_REMOTE_SUBMIX = 0x8000, /* Telephony voice TX path */ AUDIO_DEVICE_OUT_TELEPHONY_TX = 0x10000, /* Analog jack with line impedance detected */ AUDIO_DEVICE_OUT_LINE = 0x20000, /* HDMI Audio Return Channel */ AUDIO_DEVICE_OUT_HDMI_ARC = 0x40000, /* S/PDIF out */ AUDIO_DEVICE_OUT_SPDIF = 0x80000, /* FM transmitter out */ AUDIO_DEVICE_OUT_FM = 0x100000, /* Line out for av devices */ AUDIO_DEVICE_OUT_AUX_LINE = 0x200000, /* limited-output speaker device for acoustic safety */ AUDIO_DEVICE_OUT_SPEAKER_SAFE = 0x400000, AUDIO_DEVICE_OUT_DEFAULT = AUDIO_DEVICE_BIT_DEFAULT, AUDIO_DEVICE_OUT_ALL = (AUDIO_DEVICE_OUT_EARPIECE | AUDIO_DEVICE_OUT_SPEAKER | AUDIO_DEVICE_OUT_WIRED_HEADSET | AUDIO_DEVICE_OUT_WIRED_HEADPHONE | AUDIO_DEVICE_OUT_BLUETOOTH_SCO | AUDIO_DEVICE_OUT_BLUETOOTH_SCO_HEADSET | AUDIO_DEVICE_OUT_BLUETOOTH_SCO_CARKIT | AUDIO_DEVICE_OUT_BLUETOOTH_A2DP | AUDIO_DEVICE_OUT_BLUETOOTH_A2DP_HEADPHONES | AUDIO_DEVICE_OUT_BLUETOOTH_A2DP_SPEAKER | AUDIO_DEVICE_OUT_HDMI | AUDIO_DEVICE_OUT_ANLG_DOCK_HEADSET | AUDIO_DEVICE_OUT_DGTL_DOCK_HEADSET | AUDIO_DEVICE_OUT_USB_ACCESSORY | AUDIO_DEVICE_OUT_USB_DEVICE | AUDIO_DEVICE_OUT_REMOTE_SUBMIX | AUDIO_DEVICE_OUT_TELEPHONY_TX | AUDIO_DEVICE_OUT_LINE | AUDIO_DEVICE_OUT_HDMI_ARC | AUDIO_DEVICE_OUT_SPDIF | AUDIO_DEVICE_OUT_FM | AUDIO_DEVICE_OUT_AUX_LINE | AUDIO_DEVICE_OUT_SPEAKER_SAFE | AUDIO_DEVICE_OUT_DEFAULT), AUDIO_DEVICE_OUT_ALL_A2DP = (AUDIO_DEVICE_OUT_BLUETOOTH_A2DP | AUDIO_DEVICE_OUT_BLUETOOTH_A2DP_HEADPHONES | AUDIO_DEVICE_OUT_BLUETOOTH_A2DP_SPEAKER), AUDIO_DEVICE_OUT_ALL_SCO = (AUDIO_DEVICE_OUT_BLUETOOTH_SCO | AUDIO_DEVICE_OUT_BLUETOOTH_SCO_HEADSET | AUDIO_DEVICE_OUT_BLUETOOTH_SCO_CARKIT), AUDIO_DEVICE_OUT_ALL_USB = (AUDIO_DEVICE_OUT_USB_ACCESSORY | AUDIO_DEVICE_OUT_USB_DEVICE), /* input devices */ AUDIO_DEVICE_IN_COMMUNICATION = AUDIO_DEVICE_BIT_IN | 0x1, AUDIO_DEVICE_IN_AMBIENT = AUDIO_DEVICE_BIT_IN | 0x2, AUDIO_DEVICE_IN_BUILTIN_MIC = AUDIO_DEVICE_BIT_IN | 0x4, AUDIO_DEVICE_IN_BLUETOOTH_SCO_HEADSET = AUDIO_DEVICE_BIT_IN | 0x8, AUDIO_DEVICE_IN_WIRED_HEADSET = AUDIO_DEVICE_BIT_IN | 0x10, AUDIO_DEVICE_IN_AUX_DIGITAL = AUDIO_DEVICE_BIT_IN | 0x20, AUDIO_DEVICE_IN_HDMI = AUDIO_DEVICE_IN_AUX_DIGITAL, /* Telephony voice RX path */ AUDIO_DEVICE_IN_VOICE_CALL = AUDIO_DEVICE_BIT_IN | 0x40, AUDIO_DEVICE_IN_TELEPHONY_RX = AUDIO_DEVICE_IN_VOICE_CALL, AUDIO_DEVICE_IN_BACK_MIC = AUDIO_DEVICE_BIT_IN | 0x80, AUDIO_DEVICE_IN_REMOTE_SUBMIX = AUDIO_DEVICE_BIT_IN | 0x100, AUDIO_DEVICE_IN_ANLG_DOCK_HEADSET = AUDIO_DEVICE_BIT_IN | 0x200, AUDIO_DEVICE_IN_DGTL_DOCK_HEADSET = AUDIO_DEVICE_BIT_IN | 0x400, AUDIO_DEVICE_IN_USB_ACCESSORY = AUDIO_DEVICE_BIT_IN | 0x800, AUDIO_DEVICE_IN_USB_DEVICE = AUDIO_DEVICE_BIT_IN | 0x1000, /* FM tuner input */ AUDIO_DEVICE_IN_FM_TUNER = AUDIO_DEVICE_BIT_IN | 0x2000, /* TV tuner input */ AUDIO_DEVICE_IN_TV_TUNER = AUDIO_DEVICE_BIT_IN | 0x4000, /* Analog jack with line impedance detected */ AUDIO_DEVICE_IN_LINE = AUDIO_DEVICE_BIT_IN | 0x8000, /* S/PDIF in */ AUDIO_DEVICE_IN_SPDIF = AUDIO_DEVICE_BIT_IN | 0x10000, AUDIO_DEVICE_IN_BLUETOOTH_A2DP = AUDIO_DEVICE_BIT_IN | 0x20000, AUDIO_DEVICE_IN_LOOPBACK = AUDIO_DEVICE_BIT_IN | 0x40000, AUDIO_DEVICE_IN_DEFAULT = AUDIO_DEVICE_BIT_IN | AUDIO_DEVICE_BIT_DEFAULT, AUDIO_DEVICE_IN_ALL = (AUDIO_DEVICE_IN_COMMUNICATION | AUDIO_DEVICE_IN_AMBIENT | AUDIO_DEVICE_IN_BUILTIN_MIC | AUDIO_DEVICE_IN_BLUETOOTH_SCO_HEADSET | AUDIO_DEVICE_IN_WIRED_HEADSET | AUDIO_DEVICE_IN_HDMI | AUDIO_DEVICE_IN_TELEPHONY_RX | AUDIO_DEVICE_IN_BACK_MIC | AUDIO_DEVICE_IN_REMOTE_SUBMIX | AUDIO_DEVICE_IN_ANLG_DOCK_HEADSET | AUDIO_DEVICE_IN_DGTL_DOCK_HEADSET | AUDIO_DEVICE_IN_USB_ACCESSORY | AUDIO_DEVICE_IN_USB_DEVICE | AUDIO_DEVICE_IN_FM_TUNER | AUDIO_DEVICE_IN_TV_TUNER | AUDIO_DEVICE_IN_LINE | AUDIO_DEVICE_IN_SPDIF | AUDIO_DEVICE_IN_BLUETOOTH_A2DP | AUDIO_DEVICE_IN_LOOPBACK | AUDIO_DEVICE_IN_DEFAULT), AUDIO_DEVICE_IN_ALL_SCO = AUDIO_DEVICE_IN_BLUETOOTH_SCO_HEADSET, AUDIO_DEVICE_IN_ALL_USB = (AUDIO_DEVICE_IN_USB_ACCESSORY | AUDIO_DEVICE_IN_USB_DEVICE), }; typedef uint32_t audio_devices_t; /* the audio output flags serve two purposes: * - when an AudioTrack is created they indicate a "wish" to be connected to an * output stream with attributes corresponding to the specified flags * - when present in an output profile descriptor listed for a particular audio * hardware module, they indicate that an output stream can be opened that * supports the attributes indicated by the flags. * the audio policy manager will try to match the flags in the request * (when getOuput() is called) to an available output stream. */ typedef enum { AUDIO_OUTPUT_FLAG_NONE = 0x0, // no attributes AUDIO_OUTPUT_FLAG_DIRECT = 0x1, // this output directly connects a track // to one output stream: no software mixer AUDIO_OUTPUT_FLAG_PRIMARY = 0x2, // this output is the primary output of // the device. It is unique and must be // present. It is opened by default and // receives routing, audio mode and volume // controls related to voice calls. AUDIO_OUTPUT_FLAG_FAST = 0x4, // output supports "fast tracks", // defined elsewhere AUDIO_OUTPUT_FLAG_DEEP_BUFFER = 0x8, // use deep audio buffers AUDIO_OUTPUT_FLAG_COMPRESS_OFFLOAD = 0x10, // offload playback of compressed // streams to hardware codec AUDIO_OUTPUT_FLAG_NON_BLOCKING = 0x20, // use non-blocking write AUDIO_OUTPUT_FLAG_HW_AV_SYNC = 0x40 // output uses a hardware A/V synchronization source } audio_output_flags_t; /* The audio input flags are analogous to audio output flags. * Currently they are used only when an AudioRecord is created, * to indicate a preference to be connected to an input stream with * attributes corresponding to the specified flags. */ typedef enum { AUDIO_INPUT_FLAG_NONE = 0x0, // no attributes AUDIO_INPUT_FLAG_FAST = 0x1, // prefer an input that supports "fast tracks" AUDIO_INPUT_FLAG_HW_HOTWORD = 0x2, // prefer an input that captures from hw hotword source } audio_input_flags_t; /* Additional information about compressed streams offloaded to * hardware playback * The version and size fields must be initialized by the caller by using * one of the constants defined here. */ typedef struct { uint16_t version; // version of the info structure uint16_t size; // total size of the structure including version and size uint32_t sample_rate; // sample rate in Hz audio_channel_mask_t channel_mask; // channel mask audio_format_t format; // audio format audio_stream_type_t stream_type; // stream type uint32_t bit_rate; // bit rate in bits per second int64_t duration_us; // duration in microseconds, -1 if unknown bool has_video; // true if stream is tied to a video stream bool is_streaming; // true if streaming, false if local playback } audio_offload_info_t; #define AUDIO_MAKE_OFFLOAD_INFO_VERSION(maj,min) \ ((((maj) & 0xff) << 8) | ((min) & 0xff)) #define AUDIO_OFFLOAD_INFO_VERSION_0_1 AUDIO_MAKE_OFFLOAD_INFO_VERSION(0, 1) #define AUDIO_OFFLOAD_INFO_VERSION_CURRENT AUDIO_OFFLOAD_INFO_VERSION_0_1 static const audio_offload_info_t AUDIO_INFO_INITIALIZER = { version: AUDIO_OFFLOAD_INFO_VERSION_CURRENT, size: sizeof(audio_offload_info_t), sample_rate: 0, channel_mask: 0, format: AUDIO_FORMAT_DEFAULT, stream_type: AUDIO_STREAM_VOICE_CALL, bit_rate: 0, duration_us: 0, has_video: false, is_streaming: false }; /* common audio stream configuration parameters * You should memset() the entire structure to zero before use to * ensure forward compatibility */ struct audio_config { uint32_t sample_rate; audio_channel_mask_t channel_mask; audio_format_t format; audio_offload_info_t offload_info; size_t frame_count; }; typedef struct audio_config audio_config_t; static const audio_config_t AUDIO_CONFIG_INITIALIZER = { sample_rate: 0, channel_mask: AUDIO_CHANNEL_NONE, format: AUDIO_FORMAT_DEFAULT, offload_info: { version: AUDIO_OFFLOAD_INFO_VERSION_CURRENT, size: sizeof(audio_offload_info_t), sample_rate: 0, channel_mask: 0, format: AUDIO_FORMAT_DEFAULT, stream_type: AUDIO_STREAM_VOICE_CALL, bit_rate: 0, duration_us: 0, has_video: false, is_streaming: false }, frame_count: 0, }; /* audio hw module handle functions or structures referencing a module */ typedef int audio_module_handle_t; /****************************** * Volume control *****************************/ /* If the audio hardware supports gain control on some audio paths, * the platform can expose them in the audio_policy.conf file. The audio HAL * will then implement gain control functions that will use the following data * structures. */ /* Type of gain control exposed by an audio port */ #define AUDIO_GAIN_MODE_JOINT 0x1 /* supports joint channel gain control */ #define AUDIO_GAIN_MODE_CHANNELS 0x2 /* supports separate channel gain control */ #define AUDIO_GAIN_MODE_RAMP 0x4 /* supports gain ramps */ typedef uint32_t audio_gain_mode_t; /* An audio_gain struct is a representation of a gain stage. * A gain stage is always attached to an audio port. */ struct audio_gain { audio_gain_mode_t mode; /* e.g. AUDIO_GAIN_MODE_JOINT */ audio_channel_mask_t channel_mask; /* channels which gain an be controlled. N/A if AUDIO_GAIN_MODE_CHANNELS is not supported */ int min_value; /* minimum gain value in millibels */ int max_value; /* maximum gain value in millibels */ int default_value; /* default gain value in millibels */ unsigned int step_value; /* gain step in millibels */ unsigned int min_ramp_ms; /* minimum ramp duration in ms */ unsigned int max_ramp_ms; /* maximum ramp duration in ms */ }; /* The gain configuration structure is used to get or set the gain values of a * given port */ struct audio_gain_config { int index; /* index of the corresponding audio_gain in the audio_port gains[] table */ audio_gain_mode_t mode; /* mode requested for this command */ audio_channel_mask_t channel_mask; /* channels which gain value follows. N/A in joint mode */ int values[sizeof(audio_channel_mask_t) * 8]; /* gain values in millibels for each channel ordered from LSb to MSb in channel mask. The number of values is 1 in joint mode or popcount(channel_mask) */ unsigned int ramp_duration_ms; /* ramp duration in ms */ }; /****************************** * Routing control *****************************/ /* Types defined here are used to describe an audio source or sink at internal * framework interfaces (audio policy, patch panel) or at the audio HAL. * Sink and sources are grouped in a concept of “audio port” representing an * audio end point at the edge of the system managed by the module exposing * the interface. */ /* Audio port role: either source or sink */ typedef enum { AUDIO_PORT_ROLE_NONE, AUDIO_PORT_ROLE_SOURCE, AUDIO_PORT_ROLE_SINK, } audio_port_role_t; /* Audio port type indicates if it is a session (e.g AudioTrack), * a mix (e.g PlaybackThread output) or a physical device * (e.g AUDIO_DEVICE_OUT_SPEAKER) */ typedef enum { AUDIO_PORT_TYPE_NONE, AUDIO_PORT_TYPE_DEVICE, AUDIO_PORT_TYPE_MIX, AUDIO_PORT_TYPE_SESSION, } audio_port_type_t; /* Each port has a unique ID or handle allocated by policy manager */ typedef int audio_port_handle_t; #define AUDIO_PORT_HANDLE_NONE 0 /* maximum audio device address length */ #define AUDIO_DEVICE_MAX_ADDRESS_LEN 32 /* extension for audio port configuration structure when the audio port is a * hardware device */ struct audio_port_config_device_ext { audio_module_handle_t hw_module; /* module the device is attached to */ audio_devices_t type; /* device type (e.g AUDIO_DEVICE_OUT_SPEAKER) */ char address[AUDIO_DEVICE_MAX_ADDRESS_LEN]; /* device address. "" if N/A */ }; /* extension for audio port configuration structure when the audio port is a * sub mix */ struct audio_port_config_mix_ext { audio_module_handle_t hw_module; /* module the stream is attached to */ audio_io_handle_t handle; /* I/O handle of the input/output stream */ union { //TODO: change use case for output streams: use strategy and mixer attributes audio_stream_type_t stream; audio_source_t source; } usecase; }; /* extension for audio port configuration structure when the audio port is an * audio session */ struct audio_port_config_session_ext { audio_session_t session; /* audio session */ }; /* Flags indicating which fields are to be considered in struct audio_port_config */ #define AUDIO_PORT_CONFIG_SAMPLE_RATE 0x1 #define AUDIO_PORT_CONFIG_CHANNEL_MASK 0x2 #define AUDIO_PORT_CONFIG_FORMAT 0x4 #define AUDIO_PORT_CONFIG_GAIN 0x8 #define AUDIO_PORT_CONFIG_ALL (AUDIO_PORT_CONFIG_SAMPLE_RATE | \ AUDIO_PORT_CONFIG_CHANNEL_MASK | \ AUDIO_PORT_CONFIG_FORMAT | \ AUDIO_PORT_CONFIG_GAIN) /* audio port configuration structure used to specify a particular configuration of * an audio port */ struct audio_port_config { audio_port_handle_t id; /* port unique ID */ audio_port_role_t role; /* sink or source */ audio_port_type_t type; /* device, mix ... */ unsigned int config_mask; /* e.g AUDIO_PORT_CONFIG_ALL */ unsigned int sample_rate; /* sampling rate in Hz */ audio_channel_mask_t channel_mask; /* channel mask if applicable */ audio_format_t format; /* format if applicable */ struct audio_gain_config gain; /* gain to apply if applicable */ union { struct audio_port_config_device_ext device; /* device specific info */ struct audio_port_config_mix_ext mix; /* mix specific info */ struct audio_port_config_session_ext session; /* session specific info */ } ext; }; /* max number of sampling rates in audio port */ #define AUDIO_PORT_MAX_SAMPLING_RATES 16 /* max number of channel masks in audio port */ #define AUDIO_PORT_MAX_CHANNEL_MASKS 16 /* max number of audio formats in audio port */ #define AUDIO_PORT_MAX_FORMATS 16 /* max number of gain controls in audio port */ #define AUDIO_PORT_MAX_GAINS 16 /* extension for audio port structure when the audio port is a hardware device */ struct audio_port_device_ext { audio_module_handle_t hw_module; /* module the device is attached to */ audio_devices_t type; /* device type (e.g AUDIO_DEVICE_OUT_SPEAKER) */ char address[AUDIO_DEVICE_MAX_ADDRESS_LEN]; }; /* Latency class of the audio mix */ typedef enum { AUDIO_LATENCY_LOW, AUDIO_LATENCY_NORMAL, } audio_mix_latency_class_t; /* extension for audio port structure when the audio port is a sub mix */ struct audio_port_mix_ext { audio_module_handle_t hw_module; /* module the stream is attached to */ audio_io_handle_t handle; /* I/O handle of the input.output stream */ audio_mix_latency_class_t latency_class; /* latency class */ // other attributes: routing strategies }; /* extension for audio port structure when the audio port is an audio session */ struct audio_port_session_ext { audio_session_t session; /* audio session */ }; struct audio_port { audio_port_handle_t id; /* port unique ID */ audio_port_role_t role; /* sink or source */ audio_port_type_t type; /* device, mix ... */ unsigned int num_sample_rates; /* number of sampling rates in following array */ unsigned int sample_rates[AUDIO_PORT_MAX_SAMPLING_RATES]; unsigned int num_channel_masks; /* number of channel masks in following array */ audio_channel_mask_t channel_masks[AUDIO_PORT_MAX_CHANNEL_MASKS]; unsigned int num_formats; /* number of formats in following array */ audio_format_t formats[AUDIO_PORT_MAX_FORMATS]; unsigned int num_gains; /* number of gains in following array */ struct audio_gain gains[AUDIO_PORT_MAX_GAINS]; struct audio_port_config active_config; /* current audio port configuration */ union { struct audio_port_device_ext device; struct audio_port_mix_ext mix; struct audio_port_session_ext session; } ext; }; /* An audio patch represents a connection between one or more source ports and * one or more sink ports. Patches are connected and disconnected by audio policy manager or by * applications via framework APIs. * Each patch is identified by a handle at the interface used to create that patch. For instance, * when a patch is created by the audio HAL, the HAL allocates and returns a handle. * This handle is unique to a given audio HAL hardware module. * But the same patch receives another system wide unique handle allocated by the framework. * This unique handle is used for all transactions inside the framework. */ typedef int audio_patch_handle_t; #define AUDIO_PATCH_HANDLE_NONE 0 #define AUDIO_PATCH_PORTS_MAX 16 struct audio_patch { audio_patch_handle_t id; /* patch unique ID */ unsigned int num_sources; /* number of sources in following array */ struct audio_port_config sources[AUDIO_PATCH_PORTS_MAX]; unsigned int num_sinks; /* number of sinks in following array */ struct audio_port_config sinks[AUDIO_PATCH_PORTS_MAX]; }; /* a HW synchronization source returned by the audio HAL */ typedef uint32_t audio_hw_sync_t; /* an invalid HW synchronization source indicating an error */ #define AUDIO_HW_SYNC_INVALID 0 static inline bool audio_is_output_device(audio_devices_t device) { if (((device & AUDIO_DEVICE_BIT_IN) == 0) && (popcount(device) == 1) && ((device & ~AUDIO_DEVICE_OUT_ALL) == 0)) return true; else return false; } static inline bool audio_is_input_device(audio_devices_t device) { if ((device & AUDIO_DEVICE_BIT_IN) != 0) { device &= ~AUDIO_DEVICE_BIT_IN; if ((popcount(device) == 1) && ((device & ~AUDIO_DEVICE_IN_ALL) == 0)) return true; } return false; } static inline bool audio_is_output_devices(audio_devices_t device) { return (device & AUDIO_DEVICE_BIT_IN) == 0; } static inline bool audio_is_a2dp_in_device(audio_devices_t device) { if ((device & AUDIO_DEVICE_BIT_IN) != 0) { device &= ~AUDIO_DEVICE_BIT_IN; if ((popcount(device) == 1) && (device & AUDIO_DEVICE_IN_BLUETOOTH_A2DP)) return true; } return false; } static inline bool audio_is_a2dp_out_device(audio_devices_t device) { if ((popcount(device) == 1) && (device & AUDIO_DEVICE_OUT_ALL_A2DP)) return true; else return false; } // Deprecated - use audio_is_a2dp_out_device() instead static inline bool audio_is_a2dp_device(audio_devices_t device) { return audio_is_a2dp_out_device(device); } static inline bool audio_is_bluetooth_sco_device(audio_devices_t device) { if ((device & AUDIO_DEVICE_BIT_IN) == 0) { if ((popcount(device) == 1) && ((device & ~AUDIO_DEVICE_OUT_ALL_SCO) == 0)) return true; } else { device &= ~AUDIO_DEVICE_BIT_IN; if ((popcount(device) == 1) && ((device & ~AUDIO_DEVICE_IN_BLUETOOTH_SCO_HEADSET) == 0)) return true; } return false; } static inline bool audio_is_usb_out_device(audio_devices_t device) { return ((popcount(device) == 1) && (device & AUDIO_DEVICE_OUT_ALL_USB)); } static inline bool audio_is_usb_in_device(audio_devices_t device) { if ((device & AUDIO_DEVICE_BIT_IN) != 0) { device &= ~AUDIO_DEVICE_BIT_IN; if (popcount(device) == 1 && (device & AUDIO_DEVICE_IN_ALL_USB) != 0) return true; } return false; } /* OBSOLETE - use audio_is_usb_out_device() instead. */ static inline bool audio_is_usb_device(audio_devices_t device) { return audio_is_usb_out_device(device); } static inline bool audio_is_remote_submix_device(audio_devices_t device) { if ((device & AUDIO_DEVICE_OUT_REMOTE_SUBMIX) == AUDIO_DEVICE_OUT_REMOTE_SUBMIX || (device & AUDIO_DEVICE_IN_REMOTE_SUBMIX) == AUDIO_DEVICE_IN_REMOTE_SUBMIX) return true; else return false; } /* Returns true if: * representation is valid, and * there is at least one channel bit set which _could_ correspond to an input channel, and * there are no channel bits set which could _not_ correspond to an input channel. * Otherwise returns false. */ static inline bool audio_is_input_channel(audio_channel_mask_t channel) { uint32_t bits = audio_channel_mask_get_bits(channel); switch (audio_channel_mask_get_representation(channel)) { case AUDIO_CHANNEL_REPRESENTATION_POSITION: if (bits & ~AUDIO_CHANNEL_IN_ALL) { bits = 0; } // fall through case AUDIO_CHANNEL_REPRESENTATION_INDEX: return bits != 0; default: return false; } } /* Returns true if: * representation is valid, and * there is at least one channel bit set which _could_ correspond to an output channel, and * there are no channel bits set which could _not_ correspond to an output channel. * Otherwise returns false. */ static inline bool audio_is_output_channel(audio_channel_mask_t channel) { uint32_t bits = audio_channel_mask_get_bits(channel); switch (audio_channel_mask_get_representation(channel)) { case AUDIO_CHANNEL_REPRESENTATION_POSITION: if (bits & ~AUDIO_CHANNEL_OUT_ALL) { bits = 0; } // fall through case AUDIO_CHANNEL_REPRESENTATION_INDEX: return bits != 0; default: return false; } } /* Returns the number of channels from an input channel mask, * used in the context of audio input or recording. * If a channel bit is set which could _not_ correspond to an input channel, * it is excluded from the count. * Returns zero if the representation is invalid. */ static inline uint32_t audio_channel_count_from_in_mask(audio_channel_mask_t channel) { uint32_t bits = audio_channel_mask_get_bits(channel); switch (audio_channel_mask_get_representation(channel)) { case AUDIO_CHANNEL_REPRESENTATION_POSITION: // TODO: We can now merge with from_out_mask and remove anding bits &= AUDIO_CHANNEL_IN_ALL; // fall through case AUDIO_CHANNEL_REPRESENTATION_INDEX: return popcount(bits); default: return 0; } } /* Returns the number of channels from an output channel mask, * used in the context of audio output or playback. * If a channel bit is set which could _not_ correspond to an output channel, * it is excluded from the count. * Returns zero if the representation is invalid. */ static inline uint32_t audio_channel_count_from_out_mask(audio_channel_mask_t channel) { uint32_t bits = audio_channel_mask_get_bits(channel); switch (audio_channel_mask_get_representation(channel)) { case AUDIO_CHANNEL_REPRESENTATION_POSITION: // TODO: We can now merge with from_in_mask and remove anding bits &= AUDIO_CHANNEL_OUT_ALL; // fall through case AUDIO_CHANNEL_REPRESENTATION_INDEX: return popcount(bits); default: return 0; } } /* Derive an output channel mask for position assignment from a channel count. * This is to be used when the content channel mask is unknown. The 1, 2, 4, 5, 6, 7 and 8 channel * cases are mapped to the standard game/home-theater layouts, but note that 4 is mapped to quad, * and not stereo + FC + mono surround. A channel count of 3 is arbitrarily mapped to stereo + FC * for continuity with stereo. * Returns the matching channel mask, * or AUDIO_CHANNEL_NONE if the channel count is zero, * or AUDIO_CHANNEL_INVALID if the channel count exceeds that of the * configurations for which a default output channel mask is defined. */ static inline audio_channel_mask_t audio_channel_out_mask_from_count(uint32_t channel_count) { uint32_t bits; switch (channel_count) { case 0: return AUDIO_CHANNEL_NONE; case 1: bits = AUDIO_CHANNEL_OUT_MONO; break; case 2: bits = AUDIO_CHANNEL_OUT_STEREO; break; case 3: bits = AUDIO_CHANNEL_OUT_STEREO | AUDIO_CHANNEL_OUT_FRONT_CENTER; break; case 4: // 4.0 bits = AUDIO_CHANNEL_OUT_QUAD; break; case 5: // 5.0 bits = AUDIO_CHANNEL_OUT_QUAD | AUDIO_CHANNEL_OUT_FRONT_CENTER; break; case 6: // 5.1 bits = AUDIO_CHANNEL_OUT_5POINT1; break; case 7: // 6.1 bits = AUDIO_CHANNEL_OUT_5POINT1 | AUDIO_CHANNEL_OUT_BACK_CENTER; break; case 8: bits = AUDIO_CHANNEL_OUT_7POINT1; break; default: return AUDIO_CHANNEL_INVALID; } return audio_channel_mask_from_representation_and_bits( AUDIO_CHANNEL_REPRESENTATION_POSITION, bits); } /* Derive an input channel mask for position assignment from a channel count. * Currently handles only mono and stereo. * Returns the matching channel mask, * or AUDIO_CHANNEL_NONE if the channel count is zero, * or AUDIO_CHANNEL_INVALID if the channel count exceeds that of the * configurations for which a default input channel mask is defined. */ static inline audio_channel_mask_t audio_channel_in_mask_from_count(uint32_t channel_count) { uint32_t bits; switch (channel_count) { case 0: return AUDIO_CHANNEL_NONE; case 1: bits = AUDIO_CHANNEL_IN_MONO; break; case 2: bits = AUDIO_CHANNEL_IN_STEREO; break; default: return AUDIO_CHANNEL_INVALID; } return audio_channel_mask_from_representation_and_bits( AUDIO_CHANNEL_REPRESENTATION_POSITION, bits); } /* Derive a channel mask for index assignment from a channel count. * Returns the matching channel mask, * or AUDIO_CHANNEL_NONE if the channel count is zero, * or AUDIO_CHANNEL_INVALID if the channel count exceeds AUDIO_CHANNEL_COUNT_MAX. */ static inline audio_channel_mask_t audio_channel_mask_for_index_assignment_from_count( uint32_t channel_count) { uint32_t bits; if (channel_count == 0) { return AUDIO_CHANNEL_NONE; } if (channel_count > AUDIO_CHANNEL_COUNT_MAX) { return AUDIO_CHANNEL_INVALID; } bits = (1 << channel_count) - 1; return audio_channel_mask_from_representation_and_bits( AUDIO_CHANNEL_REPRESENTATION_INDEX, bits); } static inline bool audio_is_valid_format(audio_format_t format) { switch (format & AUDIO_FORMAT_MAIN_MASK) { case AUDIO_FORMAT_PCM: switch (format) { case AUDIO_FORMAT_PCM_16_BIT: case AUDIO_FORMAT_PCM_8_BIT: case AUDIO_FORMAT_PCM_32_BIT: case AUDIO_FORMAT_PCM_8_24_BIT: case AUDIO_FORMAT_PCM_FLOAT: case AUDIO_FORMAT_PCM_24_BIT_PACKED: return true; case AUDIO_FORMAT_INVALID: case AUDIO_FORMAT_DEFAULT: case AUDIO_FORMAT_MP3: case AUDIO_FORMAT_AMR_NB: case AUDIO_FORMAT_AMR_WB: case AUDIO_FORMAT_AAC: case AUDIO_FORMAT_HE_AAC_V1: case AUDIO_FORMAT_HE_AAC_V2: case AUDIO_FORMAT_VORBIS: case AUDIO_FORMAT_OPUS: case AUDIO_FORMAT_AC3: case AUDIO_FORMAT_E_AC3: case AUDIO_FORMAT_MAIN_MASK: case AUDIO_FORMAT_SUB_MASK: case AUDIO_FORMAT_AAC_MAIN: case AUDIO_FORMAT_AAC_LC: case AUDIO_FORMAT_AAC_SSR: case AUDIO_FORMAT_AAC_LTP: case AUDIO_FORMAT_AAC_HE_V1: case AUDIO_FORMAT_AAC_SCALABLE: case AUDIO_FORMAT_AAC_ERLC: case AUDIO_FORMAT_AAC_LD: case AUDIO_FORMAT_AAC_HE_V2: case AUDIO_FORMAT_AAC_ELD: default: return false; } /* not reached */ case AUDIO_FORMAT_MP3: case AUDIO_FORMAT_AMR_NB: case AUDIO_FORMAT_AMR_WB: case AUDIO_FORMAT_AAC: case AUDIO_FORMAT_HE_AAC_V1: case AUDIO_FORMAT_HE_AAC_V2: case AUDIO_FORMAT_VORBIS: case AUDIO_FORMAT_OPUS: case AUDIO_FORMAT_AC3: case AUDIO_FORMAT_E_AC3: return true; default: return false; } } static inline bool audio_is_linear_pcm(audio_format_t format) { return ((format & AUDIO_FORMAT_MAIN_MASK) == AUDIO_FORMAT_PCM); } static inline size_t audio_bytes_per_sample(audio_format_t format) { size_t size = 0; switch (format) { case AUDIO_FORMAT_PCM_32_BIT: case AUDIO_FORMAT_PCM_8_24_BIT: size = sizeof(int32_t); break; case AUDIO_FORMAT_PCM_24_BIT_PACKED: size = sizeof(uint8_t) * 3; break; case AUDIO_FORMAT_PCM_16_BIT: size = sizeof(int16_t); break; case AUDIO_FORMAT_PCM_8_BIT: size = sizeof(uint8_t); break; case AUDIO_FORMAT_PCM_FLOAT: size = sizeof(float); break; case AUDIO_FORMAT_INVALID: case AUDIO_FORMAT_DEFAULT: case AUDIO_FORMAT_MP3: case AUDIO_FORMAT_AMR_NB: case AUDIO_FORMAT_AMR_WB: case AUDIO_FORMAT_AAC: case AUDIO_FORMAT_HE_AAC_V1: case AUDIO_FORMAT_HE_AAC_V2: case AUDIO_FORMAT_VORBIS: case AUDIO_FORMAT_OPUS: case AUDIO_FORMAT_AC3: case AUDIO_FORMAT_E_AC3: case AUDIO_FORMAT_MAIN_MASK: case AUDIO_FORMAT_SUB_MASK: case AUDIO_FORMAT_AAC_MAIN: case AUDIO_FORMAT_AAC_LC: case AUDIO_FORMAT_AAC_SSR: case AUDIO_FORMAT_AAC_LTP: case AUDIO_FORMAT_AAC_HE_V1: case AUDIO_FORMAT_AAC_SCALABLE: case AUDIO_FORMAT_AAC_ERLC: case AUDIO_FORMAT_AAC_LD: case AUDIO_FORMAT_AAC_HE_V2: case AUDIO_FORMAT_AAC_ELD: default: break; } return size; } /* converts device address to string sent to audio HAL via set_parameters */ #if 0 /* never used error */ static char *audio_device_address_to_parameter(audio_devices_t device, const char *address) { const size_t kSize = AUDIO_DEVICE_MAX_ADDRESS_LEN + sizeof("a2dp_sink_address="); char param[kSize]; if (device & AUDIO_DEVICE_OUT_ALL_A2DP) snprintf(param, kSize, "%s=%s", "a2dp_sink_address", address); else if (device & AUDIO_DEVICE_OUT_REMOTE_SUBMIX) snprintf(param, kSize, "%s=%s", "mix", address); else snprintf(param, kSize, "%s", address); return strdup(param); } #endif __END_DECLS #endif // ANDROID_AUDIO_CORE_H bluez-5.82/android/PaxHeaders/hal-gatt.c0000644000000000000000000000005014015011623015124 xustar0020 atime=1743516862 20 ctime=1743591276 bluez-5.82/android/hal-gatt.c0000644000000000000000000016062214015011623014614 0ustar00rootroot// SPDX-License-Identifier: Apache-2.0 /* * Copyright (C) 2014 Intel Corporation * */ #define _GNU_SOURCE #include #include #include #include "hal-log.h" #include "hal.h" #include "hal-msg.h" #include "ipc-common.h" #include "hal-ipc.h" #include "hal-utils.h" static const btgatt_callbacks_t *cbs = NULL; static bool interface_ready(void) { return cbs != NULL; } static void gatt_id_from_hal(btgatt_gatt_id_t *to, struct hal_gatt_gatt_id *from) { memcpy(&to->uuid, from->uuid, sizeof(to->uuid)); to->inst_id = from->inst_id; } static void gatt_id_to_hal(struct hal_gatt_gatt_id *to, btgatt_gatt_id_t *from) { memcpy(to->uuid, &from->uuid, sizeof(from->uuid)); to->inst_id = from->inst_id; } static void srvc_id_from_hal(btgatt_srvc_id_t *to, struct hal_gatt_srvc_id *from) { memcpy(&to->id.uuid, from->uuid, sizeof(to->id.uuid)); to->id.inst_id = from->inst_id; to->is_primary = from->is_primary; } static void srvc_id_to_hal(struct hal_gatt_srvc_id *to, btgatt_srvc_id_t *from) { memcpy(to->uuid, &from->id.uuid, sizeof(from->id.uuid)); to->inst_id = from->id.inst_id; to->is_primary = from->is_primary; } /* Client Event Handlers */ static void handle_register_client(void *buf, uint16_t len, int fd) { struct hal_ev_gatt_client_register_client *ev = buf; if (cbs->client->register_client_cb) cbs->client->register_client_cb(ev->status, ev->client_if, (bt_uuid_t *) ev->app_uuid); } static void handle_scan_result(void *buf, uint16_t len, int fd) { struct hal_ev_gatt_client_scan_result *ev = buf; uint8_t ad[62]; if (len != sizeof(*ev) + ev->len) { error("gatt: invalid scan result event, aborting"); exit(EXIT_FAILURE); } /* Java assumes that passed data has 62 bytes */ memset(ad, 0, sizeof(ad)); memcpy(ad, ev->adv_data, ev->len > sizeof(ad) ? sizeof(ad) : ev->len); if (cbs->client->scan_result_cb) cbs->client->scan_result_cb((bt_bdaddr_t *) ev->bda, ev->rssi, ad); } static void handle_connect(void *buf, uint16_t len, int fd) { struct hal_ev_gatt_client_connect *ev = buf; if (cbs->client->open_cb) cbs->client->open_cb(ev->conn_id, ev->status, ev->client_if, (bt_bdaddr_t *) ev->bda); } static void handle_disconnect(void *buf, uint16_t len, int fd) { struct hal_ev_gatt_client_disconnect *ev = buf; if (cbs->client->close_cb) cbs->client->close_cb(ev->conn_id, ev->status, ev->client_if, (bt_bdaddr_t *) ev->bda); } static void handle_search_complete(void *buf, uint16_t len, int fd) { struct hal_ev_gatt_client_search_complete *ev = buf; if (cbs->client->search_complete_cb) cbs->client->search_complete_cb(ev->conn_id, ev->status); } static void handle_search_result(void *buf, uint16_t len, int fd) { struct hal_ev_gatt_client_search_result *ev = buf; btgatt_srvc_id_t srvc_id; srvc_id_from_hal(&srvc_id, &ev->srvc_id); if (cbs->client->search_result_cb) cbs->client->search_result_cb(ev->conn_id, &srvc_id); } static void handle_get_characteristic(void *buf, uint16_t len, int fd) { struct hal_ev_gatt_client_get_characteristic *ev = buf; btgatt_gatt_id_t char_id; btgatt_srvc_id_t srvc_id; srvc_id_from_hal(&srvc_id, &ev->srvc_id); gatt_id_from_hal(&char_id, &ev->char_id); if (cbs->client->get_characteristic_cb) cbs->client->get_characteristic_cb(ev->conn_id, ev->status, &srvc_id, &char_id, ev->char_prop); } static void handle_get_descriptor(void *buf, uint16_t len, int fd) { struct hal_ev_gatt_client_get_descriptor *ev = buf; btgatt_gatt_id_t descr_id; btgatt_gatt_id_t char_id; btgatt_srvc_id_t srvc_id; srvc_id_from_hal(&srvc_id, &ev->srvc_id); gatt_id_from_hal(&char_id, &ev->char_id); gatt_id_from_hal(&descr_id, &ev->descr_id); if (cbs->client->get_descriptor_cb) cbs->client->get_descriptor_cb(ev->conn_id, ev->status, &srvc_id, &char_id, &descr_id); } static void handle_get_included_service(void *buf, uint16_t len, int fd) { struct hal_ev_gatt_client_get_inc_service *ev = buf; btgatt_srvc_id_t srvc_id; btgatt_srvc_id_t incl_srvc_id; srvc_id_from_hal(&srvc_id, &ev->srvc_id); srvc_id_from_hal(&incl_srvc_id, &ev->incl_srvc_id); if (cbs->client->get_included_service_cb) cbs->client->get_included_service_cb(ev->conn_id, ev->status, &srvc_id, &incl_srvc_id); } static void handle_register_for_notification(void *buf, uint16_t len, int fd) { struct hal_ev_gatt_client_reg_for_notif *ev = buf; btgatt_gatt_id_t char_id; btgatt_srvc_id_t srvc_id; srvc_id_from_hal(&srvc_id, &ev->srvc_id); gatt_id_from_hal(&char_id, &ev->char_id); if (cbs->client->register_for_notification_cb) cbs->client->register_for_notification_cb(ev->conn_id, ev->registered, ev->status, &srvc_id, &char_id); } static void handle_notify(void *buf, uint16_t len, int fd) { struct hal_ev_gatt_client_notify *ev = buf; btgatt_notify_params_t params; if (len != sizeof(*ev) + ev->len) { error("gatt: invalid notify event, aborting"); exit(EXIT_FAILURE); } memset(¶ms, 0, sizeof(params)); memcpy(params.value, ev->value, ev->len); memcpy(¶ms.bda, ev->bda, sizeof(params.bda)); srvc_id_from_hal(¶ms.srvc_id, &ev->srvc_id); gatt_id_from_hal(¶ms.char_id, &ev->char_id); params.len = ev->len; params.is_notify = ev->is_notify; if (cbs->client->notify_cb) cbs->client->notify_cb(ev->conn_id, ¶ms); } static void handle_read_characteristic(void *buf, uint16_t len, int fd) { struct hal_ev_gatt_client_read_characteristic *ev = buf; btgatt_read_params_t params; if (len != sizeof(*ev) + ev->data.len) { error("gatt: invalid read characteristic event, aborting"); exit(EXIT_FAILURE); } memset(¶ms, 0, sizeof(params)); srvc_id_from_hal(¶ms.srvc_id, &ev->data.srvc_id); gatt_id_from_hal(¶ms.char_id, &ev->data.char_id); gatt_id_from_hal(¶ms.descr_id, &ev->data.descr_id); memcpy(¶ms.value.value, ev->data.value, ev->data.len); params.value_type = ev->data.value_type; params.value.len = ev->data.len; params.status = ev->data.status; if (cbs->client->read_characteristic_cb) cbs->client->read_characteristic_cb(ev->conn_id, ev->status, ¶ms); } static void handle_write_characteristic(void *buf, uint16_t len, int fd) { struct hal_ev_gatt_client_write_characteristic *ev = buf; btgatt_write_params_t params; memset(¶ms, 0, sizeof(params)); srvc_id_from_hal(¶ms.srvc_id, &ev->data.srvc_id); gatt_id_from_hal(¶ms.char_id, &ev->data.char_id); gatt_id_from_hal(¶ms.descr_id, &ev->data.descr_id); params.status = ev->data.status; if (cbs->client->write_characteristic_cb) cbs->client->write_characteristic_cb(ev->conn_id, ev->status, ¶ms); } static void handle_read_descriptor(void *buf, uint16_t len, int fd) { struct hal_ev_gatt_client_read_descriptor *ev = buf; btgatt_read_params_t params; if (len != sizeof(*ev) + ev->data.len) { error("gatt: invalid read descriptor event, aborting"); exit(EXIT_FAILURE); } memset(¶ms, 0, sizeof(params)); srvc_id_from_hal(¶ms.srvc_id, &ev->data.srvc_id); gatt_id_from_hal(¶ms.char_id, &ev->data.char_id); gatt_id_from_hal(¶ms.descr_id, &ev->data.descr_id); memcpy(¶ms.value.value, ev->data.value, ev->data.len); params.value_type = ev->data.value_type; params.value.len = ev->data.len; params.status = ev->data.status; if (cbs->client->read_descriptor_cb) cbs->client->read_descriptor_cb(ev->conn_id, ev->status, ¶ms); } static void handle_write_descriptor(void *buf, uint16_t len, int fd) { struct hal_ev_gatt_client_write_descriptor *ev = buf; btgatt_write_params_t params; memset(¶ms, 0, sizeof(params)); srvc_id_from_hal(¶ms.srvc_id, &ev->data.srvc_id); gatt_id_from_hal(¶ms.char_id, &ev->data.char_id); gatt_id_from_hal(¶ms.descr_id, &ev->data.descr_id); params.status = ev->data.status; if (cbs->client->write_descriptor_cb) cbs->client->write_descriptor_cb(ev->conn_id, ev->status, ¶ms); } static void handle_execute_write(void *buf, uint16_t len, int fd) { struct hal_ev_gatt_client_exec_write *ev = buf; if (cbs->client->execute_write_cb) cbs->client->execute_write_cb(ev->conn_id, ev->status); } static void handle_read_remote_rssi(void *buf, uint16_t len, int fd) { struct hal_ev_gatt_client_read_remote_rssi *ev = buf; if (cbs->client->read_remote_rssi_cb) cbs->client->read_remote_rssi_cb(ev->client_if, (bt_bdaddr_t *) ev->address, ev->rssi, ev->status); } static void handle_listen(void *buf, uint16_t len, int fd) { struct hal_ev_gatt_client_listen *ev = buf; if (cbs->client->listen_cb) cbs->client->listen_cb(ev->status, ev->server_if); } /* Server Event Handlers */ static void handle_register_server(void *buf, uint16_t len, int fd) { struct hal_ev_gatt_server_register *ev = buf; if (cbs->server->register_server_cb) cbs->server->register_server_cb(ev->status, ev->server_if, (bt_uuid_t *) &ev->uuid); } static void handle_connection(void *buf, uint16_t len, int fd) { struct hal_ev_gatt_server_connection *ev = buf; if (cbs->server->connection_cb) cbs->server->connection_cb(ev->conn_id, ev->server_if, ev->connected, (bt_bdaddr_t *) &ev->bdaddr); } static void handle_service_added(void *buf, uint16_t len, int fd) { struct hal_ev_gatt_server_service_added *ev = buf; btgatt_srvc_id_t srvc_id; srvc_id_from_hal(&srvc_id, &ev->srvc_id); if (cbs->server->service_added_cb) cbs->server->service_added_cb(ev->status, ev->server_if, &srvc_id, ev->srvc_handle); } static void handle_included_service_added(void *buf, uint16_t len, int fd) { struct hal_ev_gatt_server_inc_srvc_added *ev = buf; if (cbs->server->included_service_added_cb) cbs->server->included_service_added_cb(ev->status, ev->server_if, ev->srvc_handle, ev->incl_srvc_handle); } static void handle_characteristic_added(void *buf, uint16_t len, int fd) { struct hal_ev_gatt_server_characteristic_added *ev = buf; if (cbs->server->characteristic_added_cb) cbs->server->characteristic_added_cb(ev->status, ev->server_if, (bt_uuid_t *) &ev->uuid, ev->srvc_handle, ev->char_handle); } static void handle_descriptor_added(void *buf, uint16_t len, int fd) { struct hal_ev_gatt_server_descriptor_added *ev = buf; if (cbs->server->descriptor_added_cb) cbs->server->descriptor_added_cb(ev->status, ev->server_if, (bt_uuid_t *) &ev->uuid, ev->srvc_handle, ev->descr_handle); } static void handle_service_started(void *buf, uint16_t len, int fd) { struct hal_ev_gatt_server_service_started *ev = buf; if (cbs->server->service_started_cb) cbs->server->service_started_cb(ev->status, ev->server_if, ev->srvc_handle); } static void handle_service_stopped(void *buf, uint16_t len, int fd) { struct hal_ev_gatt_server_service_stopped *ev = buf; if (cbs->server->service_stopped_cb) cbs->server->service_stopped_cb(ev->status, ev->server_if, ev->srvc_handle); } static void handle_service_deleted(void *buf, uint16_t len, int fd) { struct hal_ev_gatt_server_service_deleted *ev = buf; if (cbs->server->service_deleted_cb) cbs->server->service_deleted_cb(ev->status, ev->server_if, ev->srvc_handle); } static void handle_request_read(void *buf, uint16_t len, int fd) { struct hal_ev_gatt_server_request_read *ev = buf; if (cbs->server->request_read_cb) cbs->server->request_read_cb(ev->conn_id, ev->trans_id, (bt_bdaddr_t *) &ev->bdaddr, ev->attr_handle, ev->offset, ev->is_long); } static void handle_request_write(void *buf, uint16_t len, int fd) { struct hal_ev_gatt_server_request_write *ev = buf; if (len != sizeof(*ev) + ev->length) { error("gatt: invalid request write event, aborting"); exit(EXIT_FAILURE); } if (cbs->server->request_write_cb) cbs->server->request_write_cb(ev->conn_id, ev->trans_id, (bt_bdaddr_t *) ev->bdaddr, ev->attr_handle, ev->offset, ev->length, ev->need_rsp, ev->is_prep, ev->value); } static void handle_request_exec_write(void *buf, uint16_t len, int fd) { struct hal_ev_gatt_server_request_exec_write *ev = buf; if (cbs->server->request_exec_write_cb) cbs->server->request_exec_write_cb(ev->conn_id, ev->trans_id, (bt_bdaddr_t *) ev->bdaddr, ev->exec_write); } static void handle_response_confirmation(void *buf, uint16_t len, int fd) { struct hal_ev_gatt_server_rsp_confirmation *ev = buf; if (cbs->server->response_confirmation_cb) cbs->server->response_confirmation_cb(ev->status, ev->handle); } static void handle_configure_mtu(void *buf, uint16_t len, int fd) { #if ANDROID_VERSION >= PLATFORM_VER(5, 0, 0) struct hal_ev_gatt_client_configure_mtu *ev = buf; if (cbs->client->configure_mtu_cb) cbs->client->configure_mtu_cb(ev->conn_id, ev->status, ev->mtu); #endif } static void handle_filter_config(void *buf, uint16_t len, int fd) { #if ANDROID_VERSION >= PLATFORM_VER(5, 0, 0) struct hal_ev_gatt_client_filter_config *ev = buf; if (cbs->client->scan_filter_cfg_cb) cbs->client->scan_filter_cfg_cb(ev->action, ev->client_if, ev->status, ev->type, ev->space); #endif } static void handle_filter_params(void *buf, uint16_t len, int fd) { #if ANDROID_VERSION >= PLATFORM_VER(5, 0, 0) struct hal_ev_gatt_client_filter_params *ev = buf; if (cbs->client->scan_filter_param_cb) cbs->client->scan_filter_param_cb(ev->action, ev->client_if, ev->status, ev->space); #endif } static void handle_filter_status(void *buf, uint16_t len, int fd) { #if ANDROID_VERSION >= PLATFORM_VER(5, 0, 0) struct hal_ev_gatt_client_filter_status *ev = buf; if (cbs->client->scan_filter_status_cb) cbs->client->scan_filter_status_cb(ev->enable, ev->client_if, ev->status); #endif } static void handle__multi_adv_enable(void *buf, uint16_t len, int fd) { #if ANDROID_VERSION >= PLATFORM_VER(5, 0, 0) struct hal_ev_gatt_client_multi_adv_enable *ev = buf; if (cbs->client->multi_adv_enable_cb) cbs->client->multi_adv_enable_cb(ev->client_if, ev->status); #endif } static void handle_multi_adv_update(void *buf, uint16_t len, int fd) { #if ANDROID_VERSION >= PLATFORM_VER(5, 0, 0) struct hal_ev_gatt_client_multi_adv_update *ev = buf; if (cbs->client->multi_adv_update_cb) cbs->client->multi_adv_update_cb(ev->client_if, ev->status); #endif } static void handle_multi_adv_data(void *buf, uint16_t len, int fd) { #if ANDROID_VERSION >= PLATFORM_VER(5, 0, 0) struct hal_ev_gatt_client_multi_adv_data *ev = buf; if (cbs->client->multi_adv_data_cb) cbs->client->multi_adv_data_cb(ev->client_if, ev->status); #endif } static void handle_multi_adv_disable(void *buf, uint16_t len, int fd) { #if ANDROID_VERSION >= PLATFORM_VER(5, 0, 0) struct hal_ev_gatt_client_multi_adv_disable *ev = buf; if (cbs->client->multi_adv_disable_cb) cbs->client->multi_adv_disable_cb(ev->client_if, ev->status); #endif } static void handle_client_congestion(void *buf, uint16_t len, int fd) { #if ANDROID_VERSION >= PLATFORM_VER(5, 0, 0) struct hal_ev_gatt_client_congestion *ev = buf; if (cbs->client->congestion_cb) cbs->client->congestion_cb(ev->conn_id, ev->congested); #endif } static void handle_config_batchscan(void *buf, uint16_t len, int fd) { #if ANDROID_VERSION >= PLATFORM_VER(5, 0, 0) struct hal_ev_gatt_client_config_batchscan *ev = buf; if (cbs->client->batchscan_cfg_storage_cb) cbs->client->batchscan_cfg_storage_cb(ev->client_if, ev->status); #endif } static void handle_enable_batchscan(void *buf, uint16_t len, int fd) { #if ANDROID_VERSION >= PLATFORM_VER(5, 0, 0) struct hal_ev_gatt_client_enable_batchscan *ev = buf; if (cbs->client->batchscan_enb_disable_cb) cbs->client->batchscan_enb_disable_cb(ev->action, ev->client_if, ev->status); #endif } static void handle_client_batchscan_reports(void *buf, uint16_t len, int fd) { #if ANDROID_VERSION >= PLATFORM_VER(5, 0, 0) struct hal_ev_gatt_client_batchscan_reports *ev = buf; if (cbs->client->batchscan_reports_cb) cbs->client->batchscan_reports_cb(ev->client_if, ev->status, ev->format, ev->num, ev->data_len, ev->data); #endif } static void handle_batchscan_threshold(void *buf, uint16_t len, int fd) { #if ANDROID_VERSION >= PLATFORM_VER(5, 0, 0) struct hal_ev_gatt_client_batchscan_threshold *ev = buf; if (cbs->client->batchscan_threshold_cb) cbs->client->batchscan_threshold_cb(ev->client_if); #endif } static void handle_track_adv(void *buf, uint16_t len, int fd) { #if ANDROID_VERSION >= PLATFORM_VER(5, 0, 0) struct hal_ev_gatt_client_track_adv *ev = buf; if (cbs->client->track_adv_event_cb) cbs->client->track_adv_event_cb(ev->client_if, ev->filetr_index, ev->address_type, (bt_bdaddr_t *) ev->address, ev->state); #endif } static void handle_indication_send(void *buf, uint16_t len, int fd) { #if ANDROID_VERSION >= PLATFORM_VER(5, 0, 0) struct hal_ev_gatt_server_indication_sent *ev = buf; if (cbs->server->indication_sent_cb) cbs->server->indication_sent_cb(ev->conn_id, ev->status); #endif } static void handle_server_congestion(void *buf, uint16_t len, int fd) { #if ANDROID_VERSION >= PLATFORM_VER(5, 0, 0) struct hal_ev_gatt_server_congestion *ev = buf; if (cbs->server->congestion_cb) cbs->server->congestion_cb(ev->conn_id, ev->congested); #endif } static void handle_server_mtu_changed(void *buf, uint16_t len, int fd) { #if ANDROID_VERSION >= PLATFORM_VER(5, 1, 0) struct hal_ev_gatt_server_mtu_changed *ev = buf; if (cbs->server->mtu_changed_cb) cbs->server->mtu_changed_cb(ev->conn_id, ev->mtu); #endif } /* * handlers will be called from notification thread context, * index in table equals to 'opcode - HAL_MINIMUM_EVENT' */ static const struct hal_ipc_handler ev_handlers[] = { /* HAL_EV_GATT_CLIENT_REGISTER_CLIENT */ { handle_register_client, false, sizeof(struct hal_ev_gatt_client_register_client) }, /* HAL_EV_GATT_CLIENT_SCAN_RESULT */ { handle_scan_result, true, sizeof(struct hal_ev_gatt_client_scan_result) }, /* HAL_EV_GATT_CLIENT_CONNECT */ { handle_connect, false, sizeof(struct hal_ev_gatt_client_connect) }, /* HAL_EV_GATT_CLIENT_DISCONNECT */ { handle_disconnect, false, sizeof(struct hal_ev_gatt_client_disconnect) }, /* HAL_EV_GATT_CLIENT_SEARCH_COMPLETE */ { handle_search_complete, false, sizeof(struct hal_ev_gatt_client_search_complete) }, /* HAL_EV_GATT_CLIENT_SEARCH_RESULT */ { handle_search_result, false, sizeof(struct hal_ev_gatt_client_search_result) }, /* HAL_EV_GATT_CLIENT_GET_CHARACTERISTIC */ { handle_get_characteristic, false, sizeof(struct hal_ev_gatt_client_get_characteristic) }, /* HAL_EV_GATT_CLIENT_GET_DESCRIPTOR */ { handle_get_descriptor, false, sizeof(struct hal_ev_gatt_client_get_descriptor) }, /* HAL_EV_GATT_CLIENT_GET_INC_SERVICE */ { handle_get_included_service, false, sizeof(struct hal_ev_gatt_client_get_inc_service) }, /* HAL_EV_GATT_CLIENT_REGISTER_FOR_NOTIF */ { handle_register_for_notification, false, sizeof(struct hal_ev_gatt_client_reg_for_notif) }, /* HAL_EV_GATT_CLIENT_NOTIFY */ { handle_notify, true, sizeof(struct hal_ev_gatt_client_notify) }, /* HAL_EV_GATT_CLIENT_READ_CHARACTERISTIC */ { handle_read_characteristic, true, sizeof(struct hal_ev_gatt_client_read_characteristic) }, /* HAL_EV_GATT_CLIENT_WRITE_CHARACTERISTIC */ { handle_write_characteristic, false, sizeof(struct hal_ev_gatt_client_write_characteristic) }, /* HAL_EV_GATT_CLIENT_READ_DESCRIPTOR */ { handle_read_descriptor, true, sizeof(struct hal_ev_gatt_client_read_descriptor) }, /* HAL_EV_GATT_CLIENT_WRITE_DESCRIPTOR */ { handle_write_descriptor, false, sizeof(struct hal_ev_gatt_client_write_descriptor) }, /* HAL_EV_GATT_CLIENT_EXEC_WRITE */ { handle_execute_write, false, sizeof(struct hal_ev_gatt_client_exec_write) }, /* HAL_EV_GATT_CLIENT_READ_REMOTE_RSSI */ { handle_read_remote_rssi, false, sizeof(struct hal_ev_gatt_client_read_remote_rssi) }, /* HAL_EV_GATT_CLIENT_LISTEN */ { handle_listen, false, sizeof(struct hal_ev_gatt_client_listen) }, /* HAL_EV_GATT_SERVER_REGISTER */ { handle_register_server, false, sizeof(struct hal_ev_gatt_server_register) }, /* HAL_EV_GATT_SERVER_CONNECTION */ { handle_connection, false, sizeof(struct hal_ev_gatt_server_connection) }, /* HAL_EV_GATT_SERVER_SERVICE_ADDED */ { handle_service_added, false, sizeof(struct hal_ev_gatt_server_service_added) }, /* HAL_EV_GATT_SERVER_INC_SRVC_ADDED */ { handle_included_service_added, false, sizeof(struct hal_ev_gatt_server_inc_srvc_added) }, /* HAL_EV_GATT_SERVER_CHAR_ADDED */ { handle_characteristic_added, false, sizeof(struct hal_ev_gatt_server_characteristic_added) }, /* HAL_EV_GATT_SERVER_DESCRIPTOR_ADDED */ { handle_descriptor_added, false, sizeof(struct hal_ev_gatt_server_descriptor_added) }, /* HAL_EV_GATT_SERVER_SERVICE_STARTED */ { handle_service_started, false, sizeof(struct hal_ev_gatt_server_service_started) }, /* HAL_EV_GATT_SERVER_SERVICE_STOPPED */ { handle_service_stopped, false, sizeof(struct hal_ev_gatt_server_service_stopped) }, /* HAL_EV_GATT_SERVER_SERVICE_DELETED */ { handle_service_deleted, false, sizeof(struct hal_ev_gatt_server_service_deleted) }, /* HAL_EV_GATT_SERVER_REQUEST_READ */ { handle_request_read, false, sizeof(struct hal_ev_gatt_server_request_read) }, /* HAL_EV_GATT_SERVER_REQUEST_WRITE */ { handle_request_write, true, sizeof(struct hal_ev_gatt_server_request_write) }, /* HAL_EV_GATT_SERVER_REQUEST_EXEC_WRITE */ { handle_request_exec_write, false, sizeof(struct hal_ev_gatt_server_request_exec_write) }, /* HAL_EV_GATT_SERVER_RSP_CONFIRMATION */ { handle_response_confirmation, false, sizeof(struct hal_ev_gatt_server_rsp_confirmation) }, /* HAL_EV_GATT_CLIENT_CONFIGURE_MTU */ { handle_configure_mtu, false, sizeof(struct hal_ev_gatt_client_configure_mtu) }, /* HAL_EV_GATT_CLIENT_FILTER_CONFIG */ { handle_filter_config, false, sizeof(struct hal_ev_gatt_client_filter_config) }, /* HAL_EV_GATT_CLIENT_FILTER_PARAMS */ { handle_filter_params, false, sizeof(struct hal_ev_gatt_client_filter_params) }, /* HAL_EV_GATT_CLIENT_FILTER_STATUS */ { handle_filter_status, false, sizeof(struct hal_ev_gatt_client_filter_status) }, /* HAL_EV_GATT_CLIENT_MULTI_ADV_ENABLE */ { handle__multi_adv_enable, false, sizeof(struct hal_ev_gatt_client_multi_adv_enable) }, /* HAL_EV_GATT_CLIENT_MULTI_ADV_UPDATE */ { handle_multi_adv_update, false, sizeof(struct hal_ev_gatt_client_multi_adv_update) }, /* HAL_EV_GATT_CLIENT_MULTI_ADV_DATA */ { handle_multi_adv_data, false, sizeof(struct hal_ev_gatt_client_multi_adv_data) }, /* HAL_EV_GATT_CLIENT_MULTI_ADV_DISABLE */ { handle_multi_adv_disable, false, sizeof(struct hal_ev_gatt_client_multi_adv_disable) }, /* HAL_EV_GATT_CLIENT_CONGESTION */ { handle_client_congestion, false, sizeof(struct hal_ev_gatt_client_congestion) }, /* HAL_EV_GATT_CLIENT_CONFIG_BATCHSCAN */ { handle_config_batchscan, false, sizeof(struct hal_ev_gatt_client_config_batchscan) }, /* HAL_EV_GATT_CLIENT_ENABLE_BATCHSCAN */ { handle_enable_batchscan, false, sizeof(struct hal_ev_gatt_client_enable_batchscan) }, /* HAL_EV_GATT_CLIENT_BATCHSCAN_REPORTS */ { handle_client_batchscan_reports, true, sizeof(struct hal_ev_gatt_client_batchscan_reports) }, /* HAL_EV_GATT_CLIENT_BATCHSCAN_THRESHOLD */ { handle_batchscan_threshold, false, sizeof(struct hal_ev_gatt_client_batchscan_threshold) }, /* HAL_EV_GATT_CLIENT_TRACK_ADV */ { handle_track_adv, false, sizeof(struct hal_ev_gatt_client_track_adv) }, /* HAL_EV_GATT_SERVER_INDICATION_SENT */ { handle_indication_send, false, sizeof(struct hal_ev_gatt_server_indication_sent) }, /* HAL_EV_GATT_SERVER_CONGESTION */ { handle_server_congestion, false, sizeof(struct hal_ev_gatt_server_congestion) }, /* HAL_EV_GATT_SERVER_MTU_CHANGED */ { handle_server_mtu_changed, false, sizeof(struct hal_ev_gatt_server_mtu_changed) }, }; /* Client API */ static bt_status_t register_client(bt_uuid_t *uuid) { struct hal_cmd_gatt_client_register cmd; if (!interface_ready()) return BT_STATUS_NOT_READY; memcpy(cmd.uuid, uuid, sizeof(*uuid)); return hal_ipc_cmd(HAL_SERVICE_ID_GATT, HAL_OP_GATT_CLIENT_REGISTER, sizeof(cmd), &cmd, NULL, NULL, NULL); } static bt_status_t unregister_client(int client_if) { struct hal_cmd_gatt_client_unregister cmd; if (!interface_ready()) return BT_STATUS_NOT_READY; cmd.client_if = client_if; return hal_ipc_cmd(HAL_SERVICE_ID_GATT, HAL_OP_GATT_CLIENT_UNREGISTER, sizeof(cmd), &cmd, NULL, NULL, NULL); } static bt_status_t scan_real(int client_if, bool start) { struct hal_cmd_gatt_client_scan cmd; if (!interface_ready()) return BT_STATUS_NOT_READY; cmd.client_if = client_if; cmd.start = start; return hal_ipc_cmd(HAL_SERVICE_ID_GATT, HAL_OP_GATT_CLIENT_SCAN, sizeof(cmd), &cmd, NULL, NULL, NULL); } #if ANDROID_VERSION >= PLATFORM_VER(5, 0, 0) static bt_status_t scan(bool start) { return scan_real(0, start); } #else static bt_status_t scan(int client_if, bool start) { return scan_real(client_if, start); } #endif static bt_status_t connect_real(int client_if, const bt_bdaddr_t *bd_addr, bool is_direct, int transport) { struct hal_cmd_gatt_client_connect cmd; if (!interface_ready()) return BT_STATUS_NOT_READY; cmd.client_if = client_if; cmd.is_direct = is_direct; cmd.transport = transport; memcpy(cmd.bdaddr, bd_addr, sizeof(*bd_addr)); return hal_ipc_cmd(HAL_SERVICE_ID_GATT, HAL_OP_GATT_CLIENT_CONNECT, sizeof(cmd), &cmd, NULL, NULL, NULL); } #if ANDROID_VERSION >= PLATFORM_VER(5, 0, 0) static bt_status_t connect(int client_if, const bt_bdaddr_t *bd_addr, bool is_direct, int transport) { return connect_real(client_if, bd_addr, is_direct, transport); } #else static bt_status_t connect(int client_if, const bt_bdaddr_t *bd_addr, bool is_direct) { return connect_real(client_if, bd_addr, is_direct, BT_TRANSPORT_UNKNOWN); } #endif static bt_status_t disconnect(int client_if, const bt_bdaddr_t *bd_addr, int conn_id) { struct hal_cmd_gatt_client_disconnect cmd; if (!interface_ready()) return BT_STATUS_NOT_READY; cmd.client_if = client_if; cmd.conn_id = conn_id; memcpy(cmd.bdaddr, bd_addr, sizeof(*bd_addr)); return hal_ipc_cmd(HAL_SERVICE_ID_GATT, HAL_OP_GATT_CLIENT_DISCONNECT, sizeof(cmd), &cmd, NULL, NULL, NULL); } static bt_status_t listen(int client_if, bool start) { struct hal_cmd_gatt_client_listen cmd; if (!interface_ready()) return BT_STATUS_NOT_READY; cmd.client_if = client_if; cmd.start = start; return hal_ipc_cmd(HAL_SERVICE_ID_GATT, HAL_OP_GATT_CLIENT_LISTEN, sizeof(cmd), &cmd, NULL, NULL, NULL); } static bt_status_t refresh(int client_if, const bt_bdaddr_t *bd_addr) { struct hal_cmd_gatt_client_refresh cmd; if (!interface_ready()) return BT_STATUS_NOT_READY; cmd.client_if = client_if; memcpy(cmd.bdaddr, bd_addr, sizeof(*bd_addr)); return hal_ipc_cmd(HAL_SERVICE_ID_GATT, HAL_OP_GATT_CLIENT_REFRESH, sizeof(cmd), &cmd, NULL, NULL, NULL); } static bt_status_t search_service(int conn_id, bt_uuid_t *filter_uuid) { char buf[IPC_MTU]; struct hal_cmd_gatt_client_search_service *cmd = (void *) buf; size_t len = sizeof(*cmd); if (!interface_ready()) return BT_STATUS_NOT_READY; memset(cmd, 0, sizeof(*cmd)); cmd->conn_id = conn_id; if (filter_uuid) { memcpy(cmd->filter_uuid, filter_uuid, sizeof(*filter_uuid)); len += sizeof(*filter_uuid); cmd->filtered = 1; } return hal_ipc_cmd(HAL_SERVICE_ID_GATT, HAL_OP_GATT_CLIENT_SEARCH_SERVICE, len, cmd, NULL, NULL, NULL); } static bt_status_t get_included_service(int conn_id, btgatt_srvc_id_t *srvc_id, btgatt_srvc_id_t *start_incl_srvc_id) { char buf[IPC_MTU]; struct hal_cmd_gatt_client_get_included_service *cmd = (void *) buf; size_t len = sizeof(*cmd); if (!interface_ready()) return BT_STATUS_NOT_READY; cmd->conn_id = conn_id; srvc_id_to_hal(&cmd->srvc_id, srvc_id); cmd->continuation = 0; if (start_incl_srvc_id) { srvc_id_to_hal(&cmd->incl_srvc_id[0], start_incl_srvc_id); len += sizeof(cmd->incl_srvc_id[0]); cmd->continuation = 1; } return hal_ipc_cmd(HAL_SERVICE_ID_GATT, HAL_OP_GATT_CLIENT_GET_INCLUDED_SERVICE, len, cmd, NULL, NULL, NULL); } static bt_status_t get_characteristic(int conn_id, btgatt_srvc_id_t *srvc_id, btgatt_gatt_id_t *start_char_id) { char buf[IPC_MTU]; struct hal_cmd_gatt_client_get_characteristic *cmd = (void *) buf; size_t len = sizeof(*cmd); if (!interface_ready()) return BT_STATUS_NOT_READY; cmd->conn_id = conn_id; srvc_id_to_hal(&cmd->srvc_id, srvc_id); cmd->continuation = 0; if (start_char_id) { gatt_id_to_hal(&cmd->char_id[0], start_char_id); len += sizeof(cmd->char_id[0]); cmd->continuation = 1; } return hal_ipc_cmd(HAL_SERVICE_ID_GATT, HAL_OP_GATT_CLIENT_GET_CHARACTERISTIC, len, cmd, NULL, NULL, NULL); } static bt_status_t get_descriptor(int conn_id, btgatt_srvc_id_t *srvc_id, btgatt_gatt_id_t *char_id, btgatt_gatt_id_t *start_descr_id) { char buf[IPC_MTU]; struct hal_cmd_gatt_client_get_descriptor *cmd = (void *) buf; size_t len = sizeof(*cmd); if (!interface_ready()) return BT_STATUS_NOT_READY; cmd->conn_id = conn_id; srvc_id_to_hal(&cmd->srvc_id, srvc_id); gatt_id_to_hal(&cmd->char_id, char_id); cmd->continuation = 0; if (start_descr_id) { gatt_id_to_hal(&cmd->descr_id[0], start_descr_id); len += sizeof(cmd->descr_id[0]); cmd->continuation = 1; } return hal_ipc_cmd(HAL_SERVICE_ID_GATT, HAL_OP_GATT_CLIENT_GET_DESCRIPTOR, len, cmd, NULL , NULL, NULL); } static bt_status_t read_characteristic(int conn_id, btgatt_srvc_id_t *srvc_id, btgatt_gatt_id_t *char_id, int auth_req) { struct hal_cmd_gatt_client_read_characteristic cmd; if (!interface_ready()) return BT_STATUS_NOT_READY; cmd.conn_id = conn_id; cmd.auth_req = auth_req; srvc_id_to_hal(&cmd.srvc_id, srvc_id); gatt_id_to_hal(&cmd.char_id, char_id); return hal_ipc_cmd(HAL_SERVICE_ID_GATT, HAL_OP_GATT_CLIENT_READ_CHARACTERISTIC, sizeof(cmd), &cmd, NULL, NULL, NULL); } static bt_status_t write_characteristic(int conn_id, btgatt_srvc_id_t *srvc_id, btgatt_gatt_id_t *char_id, int write_type, int len, int auth_req, char *p_value) { char buf[IPC_MTU]; struct hal_cmd_gatt_client_write_characteristic *cmd = (void *) buf; size_t cmd_len = sizeof(*cmd) + len; if (!interface_ready()) return BT_STATUS_NOT_READY; cmd->conn_id = conn_id; cmd->write_type = write_type; cmd->len = len; cmd->auth_req = auth_req; srvc_id_to_hal(&cmd->srvc_id, srvc_id); gatt_id_to_hal(&cmd->char_id, char_id); memcpy(cmd->value, p_value, len); return hal_ipc_cmd(HAL_SERVICE_ID_GATT, HAL_OP_GATT_CLIENT_WRITE_CHARACTERISTIC, cmd_len, cmd, NULL, NULL, NULL); } static bt_status_t read_descriptor(int conn_id, btgatt_srvc_id_t *srvc_id, btgatt_gatt_id_t *char_id, btgatt_gatt_id_t *descr_id, int auth_req) { struct hal_cmd_gatt_client_read_descriptor cmd; if (!interface_ready()) return BT_STATUS_NOT_READY; cmd.conn_id = conn_id; cmd.auth_req = auth_req; srvc_id_to_hal(&cmd.srvc_id, srvc_id); gatt_id_to_hal(&cmd.char_id, char_id); gatt_id_to_hal(&cmd.descr_id, descr_id); return hal_ipc_cmd(HAL_SERVICE_ID_GATT, HAL_OP_GATT_CLIENT_READ_DESCRIPTOR, sizeof(cmd), &cmd, NULL, NULL, NULL); } static bt_status_t write_descriptor(int conn_id, btgatt_srvc_id_t *srvc_id, btgatt_gatt_id_t *char_id, btgatt_gatt_id_t *descr_id, int write_type, int len, int auth_req, char *p_value) { char buf[IPC_MTU]; struct hal_cmd_gatt_client_write_descriptor *cmd = (void *) buf; size_t cmd_len = sizeof(*cmd) + len; if (!interface_ready()) return BT_STATUS_NOT_READY; cmd->conn_id = conn_id; cmd->write_type = write_type; cmd->len = len; cmd->auth_req = auth_req; srvc_id_to_hal(&cmd->srvc_id, srvc_id); gatt_id_to_hal(&cmd->char_id, char_id); gatt_id_to_hal(&cmd->descr_id, descr_id); memcpy(cmd->value, p_value, len); return hal_ipc_cmd(HAL_SERVICE_ID_GATT, HAL_OP_GATT_CLIENT_WRITE_DESCRIPTOR, cmd_len, cmd, NULL, NULL, NULL); } static bt_status_t execute_write(int conn_id, int execute) { struct hal_cmd_gatt_client_execute_write cmd; if (!interface_ready()) return BT_STATUS_NOT_READY; cmd.conn_id = conn_id; cmd.execute = execute; return hal_ipc_cmd(HAL_SERVICE_ID_GATT, HAL_OP_GATT_CLIENT_EXECUTE_WRITE, sizeof(cmd), &cmd, NULL, NULL, NULL); } static bt_status_t register_for_notification(int client_if, const bt_bdaddr_t *bd_addr, btgatt_srvc_id_t *srvc_id, btgatt_gatt_id_t *char_id) { struct hal_cmd_gatt_client_register_for_notification cmd; if (!interface_ready()) return BT_STATUS_NOT_READY; cmd.client_if = client_if; memcpy(cmd.bdaddr, bd_addr, sizeof(*bd_addr)); srvc_id_to_hal(&cmd.srvc_id, srvc_id); gatt_id_to_hal(&cmd.char_id, char_id); return hal_ipc_cmd(HAL_SERVICE_ID_GATT, HAL_OP_GATT_CLIENT_REGISTER_FOR_NOTIFICATION, sizeof(cmd), &cmd, NULL, NULL, NULL); } static bt_status_t deregister_for_notification(int client_if, const bt_bdaddr_t *bd_addr, btgatt_srvc_id_t *srvc_id, btgatt_gatt_id_t *char_id) { struct hal_cmd_gatt_client_deregister_for_notification cmd; if (!interface_ready()) return BT_STATUS_NOT_READY; cmd.client_if = client_if; memcpy(cmd.bdaddr, bd_addr, sizeof(*bd_addr)); srvc_id_to_hal(&cmd.srvc_id, srvc_id); gatt_id_to_hal(&cmd.char_id, char_id); return hal_ipc_cmd(HAL_SERVICE_ID_GATT, HAL_OP_GATT_CLIENT_DEREGISTER_FOR_NOTIFICATION, sizeof(cmd), &cmd, NULL, NULL, NULL); } static bt_status_t read_remote_rssi(int client_if, const bt_bdaddr_t *bd_addr) { struct hal_cmd_gatt_client_read_remote_rssi cmd; if (!interface_ready()) return BT_STATUS_NOT_READY; cmd.client_if = client_if; memcpy(cmd.bdaddr, bd_addr, sizeof(*bd_addr)); return hal_ipc_cmd(HAL_SERVICE_ID_GATT, HAL_OP_GATT_CLIENT_READ_REMOTE_RSSI, sizeof(cmd), &cmd, NULL, NULL, NULL); } static int get_device_type(const bt_bdaddr_t *bd_addr) { struct hal_cmd_gatt_client_get_device_type cmd; uint8_t dev_type; size_t resp_len = sizeof(dev_type); bt_status_t status; if (!interface_ready()) return BT_STATUS_NOT_READY; memcpy(cmd.bdaddr, bd_addr, sizeof(*bd_addr)); status = hal_ipc_cmd(HAL_SERVICE_ID_GATT, HAL_OP_GATT_CLIENT_GET_DEVICE_TYPE, sizeof(cmd), &cmd, &resp_len, &dev_type, NULL); if (status != BT_STATUS_SUCCESS || resp_len != sizeof(dev_type)) return 0; return dev_type; } static bt_status_t set_adv_data_real(int server_if, bool set_scan_rsp, bool include_name, bool include_txpower, int min_interval, int max_interval, int appearance, uint16_t manufacturer_len, char *manufacturer_data, uint16_t service_data_len, char *service_data, uint16_t service_uuid_len, char *service_uuid) { char buf[IPC_MTU]; struct hal_cmd_gatt_client_set_adv_data *cmd = (void *) buf; size_t cmd_len; uint8_t *data; if (!interface_ready()) return BT_STATUS_NOT_READY; cmd_len = sizeof(*cmd) + manufacturer_len + service_data_len + service_uuid_len; if (cmd_len > IPC_MTU) return BT_STATUS_FAIL; cmd->server_if = server_if; cmd->set_scan_rsp = set_scan_rsp; cmd->include_name = include_name; cmd->include_txpower = include_txpower; cmd->min_interval = min_interval; cmd->max_interval = max_interval; cmd->appearance = appearance; cmd->manufacturer_len = manufacturer_len; cmd->service_data_len = service_data_len; cmd->service_uuid_len = service_uuid_len; data = cmd->data; if (manufacturer_data && manufacturer_len) { memcpy(data, manufacturer_data, manufacturer_len); data += manufacturer_len; } if (service_data && service_data_len) { memcpy(data, service_data, service_data_len); data += service_data_len; } if (service_uuid && service_uuid_len) memcpy(data, service_uuid, service_uuid_len); return hal_ipc_cmd(HAL_SERVICE_ID_GATT, HAL_OP_GATT_CLIENT_SET_ADV_DATA, cmd_len, cmd, NULL, NULL, NULL); } /* * This is temporary solution and support for older Android versions might * be removed at any time. */ #if ANDROID_VERSION < PLATFORM_VER(4, 4, 3) static bt_status_t set_adv_data(int server_if, bool set_scan_rsp, bool include_name, bool include_txpower, int min_interval, int max_interval, int appearance, uint16_t manufacturer_len, char *manufacturer_data) { return set_adv_data_real(server_if, set_scan_rsp, include_name, include_txpower, min_interval, max_interval, appearance, manufacturer_len, manufacturer_data, 0, NULL, 0, NULL); } #else static bt_status_t set_adv_data(int server_if, bool set_scan_rsp, bool include_name, bool include_txpower, int min_interval, int max_interval, int appearance, uint16_t manufacturer_len, char *manufacturer_data, uint16_t service_data_len, char *service_data, uint16_t service_uuid_len, char *service_uuid) { return set_adv_data_real(server_if, set_scan_rsp, include_name, include_txpower, min_interval, max_interval, appearance, manufacturer_len, manufacturer_data, service_data_len, service_data, service_uuid_len, service_uuid); } #endif static bt_status_t test_command(int command, btgatt_test_params_t *params) { struct hal_cmd_gatt_client_test_command cmd; if (!interface_ready()) return BT_STATUS_NOT_READY; cmd.command = command; memcpy(cmd.bda1, params->bda1, sizeof(*params->bda1)); memcpy(cmd.uuid1, params->uuid1, sizeof(*params->uuid1)); cmd.u1 = params->u1; cmd.u2 = params->u2; cmd.u3 = params->u3; cmd.u4 = params->u4; cmd.u5 = params->u5; return hal_ipc_cmd(HAL_SERVICE_ID_GATT, HAL_OP_GATT_CLIENT_TEST_COMMAND, sizeof(cmd), &cmd, NULL, NULL, NULL); } #if ANDROID_VERSION >= PLATFORM_VER(5, 0, 0) static bt_status_t scan_filter_param_setup(int client_if, int action, int filt_index, int feat_seln, int list_logic_type, int filt_logic_type, int rssi_high_thres, int rssi_low_thres, int dely_mode, int found_timeout, int lost_timeout, int found_timeout_cnt) { struct hal_cmd_gatt_client_scan_filter_setup cmd; if (!interface_ready()) return BT_STATUS_NOT_READY; cmd.client_if = client_if; cmd.action = action; cmd.filter_index = filt_index; cmd.features = feat_seln; cmd.list_type = list_logic_type; cmd.filter_type = filt_logic_type; cmd.rssi_hi = rssi_high_thres; cmd.rssi_lo = rssi_low_thres; cmd.delivery_mode = dely_mode; cmd.found_timeout = found_timeout; cmd.lost_timeout = lost_timeout; cmd.found_timeout_cnt = found_timeout_cnt; return hal_ipc_cmd(HAL_SERVICE_ID_GATT, HAL_OP_GATT_CLIENT_SCAN_FILTER_SETUP, sizeof(cmd), &cmd, NULL, NULL, NULL); } static bt_status_t scan_filter_add_remove(int client_if, int action, int filt_type, int filt_index, int company_id, int company_id_mask, const bt_uuid_t *p_uuid, const bt_uuid_t *p_uuid_mask, const bt_bdaddr_t *bd_addr, char addr_type, int data_len, char *p_data, int mask_len, char *p_mask) { char buf[IPC_MTU]; struct hal_cmd_gatt_client_scan_filter_add_remove *cmd = (void *) buf; size_t cmd_len; if (!interface_ready()) return BT_STATUS_NOT_READY; if (!p_uuid || !p_uuid_mask || !bd_addr) return BT_STATUS_PARM_INVALID; cmd_len = sizeof(*cmd) + data_len + mask_len; if (cmd_len > IPC_MTU) return BT_STATUS_FAIL; cmd->client_if = client_if; cmd->action = action; cmd->filter_type = filt_type; cmd->filter_index = filt_index; cmd->company_id = company_id; cmd->company_id_mask = company_id_mask; memcpy(cmd->uuid, p_uuid, sizeof(*p_uuid)); memcpy(cmd->uuid_mask, p_uuid_mask, sizeof(*p_uuid_mask)); memcpy(cmd->address, bd_addr, sizeof(*bd_addr)); cmd->address_type = addr_type; cmd->data_len = data_len; memcpy(cmd->data_mask, p_data, data_len); cmd->mask_len = mask_len; memcpy(cmd->data_mask + data_len, p_mask, mask_len); return hal_ipc_cmd(HAL_SERVICE_ID_GATT, HAL_OP_GATT_CLIENT_SCAN_FILTER_ADD_REMOVE, cmd_len, cmd, NULL, NULL, NULL); } static bt_status_t scan_filter_clear(int client_if, int filt_index) { struct hal_cmd_gatt_client_scan_filter_clear cmd; if (!interface_ready()) return BT_STATUS_NOT_READY; cmd.client_if = client_if; cmd.index = filt_index; return hal_ipc_cmd(HAL_SERVICE_ID_GATT, HAL_OP_GATT_CLIENT_SCAN_FILTER_CLEAR, sizeof(cmd), &cmd, NULL, NULL, NULL); } static bt_status_t scan_filter_enable(int client_if, bool enable) { struct hal_cmd_gatt_client_scan_filter_enable cmd; if (!interface_ready()) return BT_STATUS_NOT_READY; cmd.client_if = client_if; cmd.enable = enable; return hal_ipc_cmd(HAL_SERVICE_ID_GATT, HAL_OP_GATT_CLIENT_SCAN_FILTER_ENABLE, sizeof(cmd), &cmd, NULL, NULL, NULL); } static bt_status_t configure_mtu(int conn_id, int mtu) { struct hal_cmd_gatt_client_configure_mtu cmd; if (!interface_ready()) return BT_STATUS_NOT_READY; cmd.conn_id = conn_id; cmd.mtu = mtu; return hal_ipc_cmd(HAL_SERVICE_ID_GATT, HAL_OP_GATT_CLIENT_CONFIGURE_MTU, sizeof(cmd), &cmd, NULL, NULL, NULL); } static bt_status_t conn_parameter_update(const bt_bdaddr_t *bd_addr, int min_interval, int max_interval, int latency, int timeout) { struct hal_cmd_gatt_client_conn_param_update cmd; if (!interface_ready()) return BT_STATUS_NOT_READY; if (!bd_addr) return BT_STATUS_PARM_INVALID; memcpy(cmd.address, bd_addr, sizeof(*bd_addr)); cmd.min_interval = min_interval; cmd.max_interval = max_interval; cmd.latency = latency; cmd.timeout = timeout; return hal_ipc_cmd(HAL_SERVICE_ID_GATT, HAL_OP_GATT_CLIENT_CONN_PARAM_UPDATE, sizeof(cmd), &cmd, NULL, NULL, NULL); } static bt_status_t set_scan_parameters(int scan_interval, int scan_window) { struct hal_cmd_gatt_client_set_scan_param cmd; if (!interface_ready()) return BT_STATUS_NOT_READY; cmd.interval = scan_interval; cmd.window = scan_window; return hal_ipc_cmd(HAL_SERVICE_ID_GATT, HAL_OP_GATT_CLIENT_SET_SCAN_PARAM, sizeof(cmd), &cmd, NULL, NULL, NULL); } static bt_status_t multi_adv_enable(int client_if, int min_interval, int max_interval, int adv_type, int chnl_map, int tx_power, int timeout_s) { struct hal_cmd_gatt_client_setup_multi_adv cmd; if (!interface_ready()) return BT_STATUS_NOT_READY; cmd.client_if = client_if; cmd.min_interval = min_interval; cmd.max_interval = max_interval; cmd.type = adv_type; cmd.channel_map = chnl_map; cmd.tx_power = tx_power; cmd.timeout = timeout_s; return hal_ipc_cmd(HAL_SERVICE_ID_GATT, HAL_OP_GATT_CLIENT_SETUP_MULTI_ADV, sizeof(cmd), &cmd, NULL, NULL, NULL); } static bt_status_t multi_adv_update(int client_if, int min_interval, int max_interval, int adv_type, int chnl_map, int tx_power, int timeout_s) { struct hal_cmd_gatt_client_update_multi_adv cmd; if (!interface_ready()) return BT_STATUS_NOT_READY; cmd.client_if = client_if; cmd.min_interval = min_interval; cmd.max_interval = max_interval; cmd.type = adv_type; cmd.channel_map = chnl_map; cmd.tx_power = tx_power; cmd.timeout = timeout_s; return hal_ipc_cmd(HAL_SERVICE_ID_GATT, HAL_OP_GATT_CLIENT_UPDATE_MULTI_ADV, sizeof(cmd), &cmd, NULL, NULL, NULL); } static bt_status_t multi_adv_set_inst_data(int client_if, bool set_scan_rsp, bool include_name, bool incl_txpower, int appearance, int manufacturer_len, char *manufacturer_data, int service_data_len, char *service_data, int service_uuid_len, char *service_uuid) { char buf[IPC_MTU]; struct hal_cmd_gatt_client_setup_multi_adv_inst *cmd = (void *) buf; int off = 0; if (!interface_ready()) return BT_STATUS_NOT_READY; if (manufacturer_len > 0 && !manufacturer_data) return BT_STATUS_PARM_INVALID; if (service_data_len > 0 && !service_data) return BT_STATUS_PARM_INVALID; if (service_uuid_len > 0 && !service_uuid) return BT_STATUS_PARM_INVALID; if (sizeof(*cmd) + manufacturer_len + service_data_len + service_uuid_len > IPC_MTU) return BT_STATUS_FAIL; cmd->client_if = client_if; cmd->set_scan_rsp = set_scan_rsp; cmd->include_name = include_name; cmd->include_tx_power = incl_txpower; cmd->appearance = appearance; cmd->manufacturer_data_len = manufacturer_len; cmd->service_data_len = service_data_len; cmd->service_uuid_len = service_uuid_len; if (manufacturer_len > 0) { memcpy(cmd->data_service_uuid, manufacturer_data, manufacturer_len); off += manufacturer_len; } if (service_data_len > 0) { memcpy(cmd->data_service_uuid + off, service_data, service_data_len); off += service_data_len; } if (service_uuid_len > 0) { memcpy(cmd->data_service_uuid + off, service_uuid, service_uuid_len); off += service_uuid_len; } return hal_ipc_cmd(HAL_SERVICE_ID_GATT, HAL_OP_GATT_CLIENT_SETUP_MULTI_ADV_INST, sizeof(*cmd) + off, cmd, NULL, NULL, NULL); } static bt_status_t multi_adv_disable(int client_if) { struct hal_cmd_gatt_client_disable_multi_adv_inst cmd; if (!interface_ready()) return BT_STATUS_NOT_READY; cmd.client_if = client_if; return hal_ipc_cmd(HAL_SERVICE_ID_GATT, HAL_OP_GATT_CLIENT_DISABLE_MULTI_ADV_INST, sizeof(cmd), &cmd, NULL, NULL, NULL); } static bt_status_t batchscan_cfg_storage(int client_if, int batch_scan_full_max, int batch_scan_trunc_max, int batch_scan_notify_threshold) { struct hal_cmd_gatt_client_configure_batchscan cmd; if (!interface_ready()) return BT_STATUS_NOT_READY; cmd.client_if = client_if; cmd.full_max = batch_scan_full_max; cmd.trunc_max = batch_scan_trunc_max; cmd.notify_threshold = batch_scan_notify_threshold; return hal_ipc_cmd(HAL_SERVICE_ID_GATT, HAL_OP_GATT_CLIENT_CONFIGURE_BATCHSCAN, sizeof(cmd), &cmd, NULL, NULL, NULL); } static bt_status_t batchscan_enb_batch_scan(int client_if, int scan_mode, int scan_interval, int scan_window, int addr_type, int discard_rule) { struct hal_cmd_gatt_client_enable_batchscan cmd; if (!interface_ready()) return BT_STATUS_NOT_READY; cmd.client_if = client_if; cmd.mode = scan_mode; cmd.interval = scan_interval; cmd.window = scan_window; cmd.address_type = addr_type; cmd.discard_rule = discard_rule; return hal_ipc_cmd(HAL_SERVICE_ID_GATT, HAL_OP_GATT_CLIENT_ENABLE_BATCHSCAN, sizeof(cmd), &cmd, NULL, NULL, NULL); } static bt_status_t batchscan_dis_batch_scan(int client_if) { struct hal_cmd_gatt_client_disable_batchscan cmd; if (!interface_ready()) return BT_STATUS_NOT_READY; cmd.client_if = client_if; return hal_ipc_cmd(HAL_SERVICE_ID_GATT, HAL_OP_GATT_CLIENT_DISABLE_BATCHSCAN, sizeof(cmd), &cmd, NULL, NULL, NULL); } static bt_status_t batchscan_read_reports(int client_if, int scan_mode) { struct hal_cmd_gatt_client_read_batchscan_reports cmd; if (!interface_ready()) return BT_STATUS_NOT_READY; cmd.client_if = client_if; cmd.scan_mode = scan_mode; return hal_ipc_cmd(HAL_SERVICE_ID_GATT, HAL_OP_GATT_CLIENT_READ_BATCHSCAN_REPORTS, sizeof(cmd), &cmd, NULL, NULL, NULL); } #endif /* Server API */ static bt_status_t register_server(bt_uuid_t *uuid) { struct hal_cmd_gatt_server_register cmd; if (!interface_ready()) return BT_STATUS_NOT_READY; memcpy(cmd.uuid, uuid, sizeof(*uuid)); return hal_ipc_cmd(HAL_SERVICE_ID_GATT, HAL_OP_GATT_SERVER_REGISTER, sizeof(cmd), &cmd, NULL, NULL, NULL); } static bt_status_t unregister_server(int server_if) { struct hal_cmd_gatt_server_unregister cmd; if (!interface_ready()) return BT_STATUS_NOT_READY; cmd.server_if = server_if; return hal_ipc_cmd(HAL_SERVICE_ID_GATT, HAL_OP_GATT_SERVER_UNREGISTER, sizeof(cmd), &cmd, NULL, NULL, NULL); } static bt_status_t server_connect_real(int server_if, const bt_bdaddr_t *bd_addr, bool is_direct, int transport) { struct hal_cmd_gatt_server_connect cmd; if (!interface_ready()) return BT_STATUS_NOT_READY; cmd.server_if = server_if; cmd.is_direct = is_direct; cmd.transport = transport; memcpy(cmd.bdaddr, bd_addr, sizeof(*bd_addr)); return hal_ipc_cmd(HAL_SERVICE_ID_GATT, HAL_OP_GATT_SERVER_CONNECT, sizeof(cmd), &cmd, NULL, NULL, NULL); } #if ANDROID_VERSION >= PLATFORM_VER(5, 0, 0) static bt_status_t server_connect(int server_if, const bt_bdaddr_t *bd_addr, bool is_direct, int transport) { return server_connect_real(server_if, bd_addr, is_direct, transport); } #else static bt_status_t server_connect(int server_if, const bt_bdaddr_t *bd_addr, bool is_direct) { return server_connect_real(server_if, bd_addr, is_direct, BT_TRANSPORT_UNKNOWN); } #endif static bt_status_t server_disconnect(int server_if, const bt_bdaddr_t *bd_addr, int conn_id) { struct hal_cmd_gatt_server_disconnect cmd; if (!interface_ready()) return BT_STATUS_NOT_READY; cmd.server_if = server_if; cmd.conn_id = conn_id; memcpy(cmd.bdaddr, bd_addr, sizeof(*bd_addr)); return hal_ipc_cmd(HAL_SERVICE_ID_GATT, HAL_OP_GATT_SERVER_DISCONNECT, sizeof(cmd), &cmd, NULL, NULL, NULL); } static bt_status_t add_service(int server_if, btgatt_srvc_id_t *srvc_id, int num_handles) { struct hal_cmd_gatt_server_add_service cmd; if (!interface_ready()) return BT_STATUS_NOT_READY; cmd.server_if = server_if; cmd.num_handles = num_handles; srvc_id_to_hal(&cmd.srvc_id, srvc_id); return hal_ipc_cmd(HAL_SERVICE_ID_GATT, HAL_OP_GATT_SERVER_ADD_SERVICE, sizeof(cmd), &cmd, NULL, NULL, NULL); } static bt_status_t add_included_service(int server_if, int service_handle, int included_handle) { struct hal_cmd_gatt_server_add_inc_service cmd; if (!interface_ready()) return BT_STATUS_NOT_READY; cmd.server_if = server_if; cmd.service_handle = service_handle; cmd.included_handle = included_handle; return hal_ipc_cmd(HAL_SERVICE_ID_GATT, HAL_OP_GATT_SERVER_ADD_INC_SERVICE, sizeof(cmd), &cmd, NULL, NULL, NULL); } static bt_status_t add_characteristic(int server_if, int service_handle, bt_uuid_t *uuid, int properties, int permissions) { struct hal_cmd_gatt_server_add_characteristic cmd; if (!interface_ready()) return BT_STATUS_NOT_READY; cmd.server_if = server_if; cmd.service_handle = service_handle; cmd.properties = properties; cmd.permissions = permissions; memcpy(cmd.uuid, uuid, sizeof(*uuid)); return hal_ipc_cmd(HAL_SERVICE_ID_GATT, HAL_OP_GATT_SERVER_ADD_CHARACTERISTIC, sizeof(cmd), &cmd, NULL, NULL, NULL); } static bt_status_t add_descriptor(int server_if, int service_handle, bt_uuid_t *uuid, int permissions) { struct hal_cmd_gatt_server_add_descriptor cmd; if (!interface_ready()) return BT_STATUS_NOT_READY; cmd.server_if = server_if; cmd.service_handle = service_handle; cmd.permissions = permissions; memcpy(cmd.uuid, uuid, sizeof(*uuid)); return hal_ipc_cmd(HAL_SERVICE_ID_GATT, HAL_OP_GATT_SERVER_ADD_DESCRIPTOR, sizeof(cmd), &cmd, NULL, NULL, NULL); } static bt_status_t start_service_real(int server_if, int service_handle, int transport) { struct hal_cmd_gatt_server_start_service cmd; if (!interface_ready()) return BT_STATUS_NOT_READY; cmd.server_if = server_if; cmd.service_handle = service_handle; cmd.transport = transport; return hal_ipc_cmd(HAL_SERVICE_ID_GATT, HAL_OP_GATT_SERVER_START_SERVICE, sizeof(cmd), &cmd, NULL, NULL, NULL); } #if ANDROID_VERSION >= PLATFORM_VER(5, 0, 0) static bt_status_t start_service(int server_if, int service_handle, int transport) { return start_service_real(server_if, service_handle, transport); } #else static bt_status_t start_service(int server_if, int service_handle, int transport) { int transport_mask = 0; /* Android 5 changes transport enum to bit mask. */ switch (transport) { case 0: transport_mask = GATT_SERVER_TRANSPORT_LE_BIT; break; case 1: transport_mask = GATT_SERVER_TRANSPORT_BREDR_BIT; break; case 2: transport_mask = GATT_SERVER_TRANSPORT_LE_BIT | GATT_SERVER_TRANSPORT_BREDR_BIT; break; } return start_service_real(server_if, service_handle, transport_mask); } #endif static bt_status_t stop_service(int server_if, int service_handle) { struct hal_cmd_gatt_server_stop_service cmd; if (!interface_ready()) return BT_STATUS_NOT_READY; cmd.server_if = server_if; cmd.service_handle = service_handle; return hal_ipc_cmd(HAL_SERVICE_ID_GATT, HAL_OP_GATT_SERVER_STOP_SERVICE, sizeof(cmd), &cmd, NULL, NULL, NULL); } static bt_status_t delete_service(int server_if, int service_handle) { struct hal_cmd_gatt_server_delete_service cmd; if (!interface_ready()) return BT_STATUS_NOT_READY; cmd.server_if = server_if; cmd.service_handle = service_handle; return hal_ipc_cmd(HAL_SERVICE_ID_GATT, HAL_OP_GATT_SERVER_DELETE_SERVICE, sizeof(cmd), &cmd, NULL, NULL, NULL); } static bt_status_t send_indication(int server_if, int attribute_handle, int conn_id, int len, int confirm, char *p_value) { char buf[IPC_MTU]; struct hal_cmd_gatt_server_send_indication *cmd = (void *) buf; size_t cmd_len = sizeof(*cmd) + len; if (!interface_ready()) return BT_STATUS_NOT_READY; cmd->server_if = server_if; cmd->attribute_handle = attribute_handle; cmd->conn_id = conn_id; cmd->len = len; cmd->confirm = confirm; memcpy(cmd->value, p_value, len); return hal_ipc_cmd(HAL_SERVICE_ID_GATT, HAL_OP_GATT_SERVER_SEND_INDICATION, cmd_len, cmd, NULL, NULL, NULL); } static bt_status_t send_response(int conn_id, int trans_id, int status, btgatt_response_t *response) { char buf[IPC_MTU]; struct hal_cmd_gatt_server_send_response *cmd = (void *) buf; size_t cmd_len = sizeof(*cmd) + sizeof(*response); memset(buf, 0 , IPC_MTU); if (!interface_ready()) return BT_STATUS_NOT_READY; cmd->conn_id = conn_id; cmd->trans_id = trans_id; cmd->status = status; cmd->handle = response->attr_value.handle; cmd->offset = response->attr_value.offset; cmd->auth_req = response->attr_value.auth_req; cmd->len = response->attr_value.len; memcpy(cmd->data, response->attr_value.value, cmd->len); return hal_ipc_cmd(HAL_SERVICE_ID_GATT, HAL_OP_GATT_SERVER_SEND_RESPONSE, cmd_len, cmd, NULL, NULL, NULL); } static bt_status_t init(const btgatt_callbacks_t *callbacks) { struct hal_cmd_register_module cmd; int ret; DBG(""); if (interface_ready()) return BT_STATUS_DONE; cbs = callbacks; hal_ipc_register(HAL_SERVICE_ID_GATT, ev_handlers, sizeof(ev_handlers)/sizeof(ev_handlers[0])); cmd.service_id = HAL_SERVICE_ID_GATT; cmd.mode = HAL_MODE_DEFAULT; cmd.max_clients = 1; ret = hal_ipc_cmd(HAL_SERVICE_ID_CORE, HAL_OP_REGISTER_MODULE, sizeof(cmd), &cmd, NULL, NULL, NULL); if (ret != BT_STATUS_SUCCESS) { cbs = NULL; hal_ipc_unregister(HAL_SERVICE_ID_GATT); } return ret; } static void cleanup(void) { struct hal_cmd_unregister_module cmd; DBG(""); if (!interface_ready()) return; cmd.service_id = HAL_SERVICE_ID_GATT; hal_ipc_cmd(HAL_SERVICE_ID_CORE, HAL_OP_UNREGISTER_MODULE, sizeof(cmd), &cmd, NULL, NULL, NULL); hal_ipc_unregister(HAL_SERVICE_ID_GATT); cbs = NULL; } static btgatt_client_interface_t client_iface = { .register_client = register_client, .unregister_client = unregister_client, .scan = scan, .connect = connect, .disconnect = disconnect, .listen = listen, .refresh = refresh, .search_service = search_service, .get_included_service = get_included_service, .get_characteristic = get_characteristic, .get_descriptor = get_descriptor, .read_characteristic = read_characteristic, .write_characteristic = write_characteristic, .read_descriptor = read_descriptor, .write_descriptor = write_descriptor, .execute_write = execute_write, .register_for_notification = register_for_notification, .deregister_for_notification = deregister_for_notification, .read_remote_rssi = read_remote_rssi, #if ANDROID_VERSION >= PLATFORM_VER(5, 0, 0) .scan_filter_param_setup = scan_filter_param_setup, .scan_filter_add_remove = scan_filter_add_remove, .scan_filter_clear = scan_filter_clear, .scan_filter_enable = scan_filter_enable, #endif .get_device_type = get_device_type, .set_adv_data = set_adv_data, #if ANDROID_VERSION >= PLATFORM_VER(5, 0, 0) .configure_mtu = configure_mtu, .conn_parameter_update = conn_parameter_update, .set_scan_parameters = set_scan_parameters, .multi_adv_enable = multi_adv_enable, .multi_adv_update = multi_adv_update, .multi_adv_set_inst_data = multi_adv_set_inst_data, .multi_adv_disable = multi_adv_disable, .batchscan_cfg_storage = batchscan_cfg_storage, .batchscan_enb_batch_scan = batchscan_enb_batch_scan, .batchscan_dis_batch_scan = batchscan_dis_batch_scan, .batchscan_read_reports = batchscan_read_reports, #endif .test_command = test_command, }; static btgatt_server_interface_t server_iface = { .register_server = register_server, .unregister_server = unregister_server, .connect = server_connect, .disconnect = server_disconnect, .add_service = add_service, .add_included_service = add_included_service, .add_characteristic = add_characteristic, .add_descriptor = add_descriptor, .start_service = start_service, .stop_service = stop_service, .delete_service = delete_service, .send_indication = send_indication, .send_response = send_response, }; static btgatt_interface_t iface = { .size = sizeof(iface), .init = init, .cleanup = cleanup, .client = &client_iface, .server = &server_iface, }; btgatt_interface_t *bt_get_gatt_interface(void) { return &iface; } bluez-5.82/android/PaxHeaders/init.bluetooth.rc0000644000000000000000000000005012447320342016564 xustar0020 atime=1743516875 20 ctime=1743591288 bluez-5.82/android/init.bluetooth.rc0000644000000000000000000000177612447320342016260 0ustar00rootroot# required permissions on boot chown bluetooth bluetooth /data/misc/bluetooth chown bluetooth bluetooth /dev/uhid chown system bluetooth /dev/uinput # services on property:bluetooth.start=daemon setprop bluetooth.start none start bluetoothd on property:bluetooth.stop=daemon setprop bluetooth.stop none stop bluetoothd on property:bluetooth.start=snoop setprop bluetooth.start none start bluetoothd-snoop on property:bluetooth.stop=snoop setprop bluetooth.stop none stop bluetoothd-snoop service bluetoothd /system/bin/bluetoothd class main # init does not yet support setting capabilities so run as root, # bluetoothd drop uid to bluetooth with the right linux capabilities group bluetooth disabled oneshot service bluetoothd-snoop /system/bin/bluetoothd-snoop class main # init does not yet support setting capabilities so run as root, # bluetoothd-snoop drops unneeded linux capabilities group nobody disabled oneshot bluez-5.82/android/PaxHeaders/a2dp-sink.h0000644000000000000000000000005014015011623015220 xustar0020 atime=1743516866 20 ctime=1743591278 bluez-5.82/android/a2dp-sink.h0000644000000000000000000000045114015011623014701 0ustar00rootroot/* SPDX-License-Identifier: LGPL-2.1-or-later */ /* * * BlueZ - Bluetooth protocol stack for Linux * * Copyright (C) 2014 Intel Corporation. All rights reserved. * * */ bool bt_a2dp_sink_register(struct ipc *ipc, const bdaddr_t *addr, uint8_t mode); void bt_a2dp_sink_unregister(void); bluez-5.82/android/PaxHeaders/pics-iopt.txt0000644000000000000000000000005012537515745015754 xustar0020 atime=1743516876 20 ctime=1743591289 bluez-5.82/android/pics-iopt.txt0000644000000000000000000001502312537515745015436 0ustar00rootrootIOPT PICS for the PTS tool. PTS version: 6.1 * - different than PTS defaults # - not yet implemented/supported M - mandatory O - optional Profiles ------------------------------------------------------------------------------- Parameter Name Selected Description ------------------------------------------------------------------------------- TSPC_support_ Support for: Advanced AdvancedAudioDistributionProfile_Sink False Audio Distribution Profile. Role: Sink TSPC_support_ Support for: Advanced AdvancedAudioDistributionProfile_Source True (*) Audio Distribution Profile. Role: Source TSPC_support_AVRemoteControlProfile_CT True (*) Support for: Audio\Video Remote Control Profile. Role: Controller TSPC_support_AVRemoteControlProfile_TG True (*) Support for: Audio\Video Remote Control Profile. Role: Target TSPC_support_BasicImagingProfile_CLIENT False Support for: Basic Imaging Profile. Role: Client TSPC_support_BasicImagingProfile_ Support for: Basic SERVER_ImagingAutomaticArchive False Imaging Profile. Role: Server Functionality: Imaging autoarchive TSPC_support_BasicImagingProfile_ False Support for: Basic SERVER_ImagingReferencedObjects Imaging Profile. Role: Server Functionality: Imaging ref. objects TSPC_support_BasicImagingProfile_ False Support for: Basic SERVER_ImagingResponder Imaging Profile. Role: Server Functionality: Imaging responder TSPC_support_ False Support for: Basic BasicPrintingProfile_PRINTER Printing Profile. Role: Printer TSPC_support_ Support for: Basic BasicPriProfile_PRINTER_ReflectedUI False Printing Profile. Role: Printer Functionality: Reflected UI TSPC_support_BasicPrintingProfile_ Support for: Basic SENDER_Referenced_objects_Service False Printing Profile. Role: Sender Functionality: Refe. objects service TSPC_support_DialUpNetworkingProfile_DT False Support for: Dial-Up Networking Profile. Role: Data Terminal TSPC_support_DialUpNetworkingProfile_GW False Support for: Dial-Up Networking Profile. Role: Gateway TSPC_support_ False Support for: Extended ExtendedServiceDiscoveryProfile_IP_LAP SDP. Version: IP-LAP TSPC_support_ False Support for: Extended ExtendedServiceDiscoveryProfile_IP_PAN SDP. Version: IP-PAN TSPC_support_ False Support for: Extended ExtendedServiceDiscoveryProfile_L2CAP SDP. Version: L2CAP TSPC_support_FAXProfile_DT False Support for: FAX Profile Role: Data Terminal TSPC_support_FAXProfile_GW False Support for: FAX Profile Role: Gateway TSPC_support_FileTransferProfile_CLIENT False Support for: FTP Role: Client TSPC_support_FileTransferProfile_SERVER False Support for: FTP Role: Server TSPC_support_HealthDeviceProfile_Sink True (*) Support for: HDP Role: Sink TSPC_support_HealthDeviceProfile_Source False Support for: HDP Role: Source TSPC_support_NewHandsFreeProfile_AG True (*) Support for: HFP Role: Audio gateway TSPC_support_NewHandsFreeProfile_HF False Support for: HFP Role: Hands-Free unit TSPC_support_ False Support for: Hard Copy HardCopyReplacementProfile_ cable Repl. Profile CLIENT_CR_RegisterNotofication_support Role: Client Functionality: CR register notification support TSPC_support_ False Support for: Hard Copy HardCopyReplacementProfile_CLIENT_print cable Repl. Profile. Role: Client Functionality: Print TSPC_support_ False Support for: Hard Copy HardCopyReplacementProfile_CLIENT_scan cable Repl. Profile. Role: Client Functionality: Scan TSPC_support_ False Support for: Hard Copy HardCopyReplacementProfile_SERVER_print cable Repl. Profile. Role: Server Functionality: Print TSPC_support_ False Support for: Hard Copy HardCopyReplacementProfile_SERVER_scan cable Repl. Profile. Role: Server Functionality: Scan TSPC_support_HeadsetProfile_AG True (*) Support for: HSP Role: Audio Gateway TSPC_support_HeadsetProfile_HS False Support for: HSP Role: Headset TSPC_support_ False Support for: HID HumanInterfaceDeviceProfile Role: Device TSPC_support_HID_Host True (*) Support for: HID Role: Host TSPC_support_LANAccessProfile_DT False Support for: LAN Access Profile. Role: Data Terminal TSPC_support_LANAccessProfile_LAP False Support for: LAN Access Profile. Role: LAN Access Point TSPC_support_MessaeAccessProfile_MCE False Support for: MAP Role: MCE TSPC_support_MessageAccessProfile_MSE True (*) Support for: MAP Role: MSE TSPC_support_ObjectPushProfile_CLIENT True (*) Support for: OPP Role: Client TSPC_support_ObjectPushProfile_SERVER True (*) Support for: OPP Role: Server TSPC_support_ False Support for: PAN PersonalAreaNetworkProfile_GN Role: GN TSPC_support_ True (*) Support for: PAN PersonalAreaNetworkProfile_NAP Role: NAP TSPC_support_ True (*) Support for: PAN PersonalAreaNetworkProfile_PANU Role: PANU TSPC_support_PhonebookAccessProfile_PCE False Support for: PBAP Role: PCE TSPC_support_PhonebookAccessProfile_PSE True (*) Support for: PBAP Role: PSE TSPC_support_SerialPortProfile_Service False Support for: SPP Role: Dev B TSPC_support_ False Support for: Service ServiceDiscoveryApplicationProfile Discovery Application Profile TSPC_support_SIMAccessProfile_CLIENT False Support for: SIM access Profile. Role: Client TSPC_support_SIMAccessProfile_SERVER False Support for: SIM access Profile. Role: Server TSPC_support_ False Support for: SynchronizationProfile_CLIENT Synchronization Profile Role: Client TSPC_support_ False Support for: SynchronizationProfile_SERVER Synchronization Profile Role: Server TSPC_support_UDIProfile_MT False Support for: UDI Profile Role: MT TSPC_support_UDIProfile_TA False Support for: UDI Profile Role: TA TSPC_support_ False Support for: Video VideoDistributionProfile_Sink distribution Profile Role: Sink TSPC_support_ False Support for: Video VideoDistributionProfile_Source distribution Profile Role: Source TSPC_support_WAPOverBluetooth_CLIENT False Support for: WAP over Bluetooth Profile Role: Client TSPC_support_WAPOverBluetooth_PROXY False Support for: WAP over Bluetooth Profile Role: PROXY TSPC_support_GNSS_SERVER False Support for: GNSS Role: Server bluez-5.82/android/PaxHeaders/hal-audio.c0000644000000000000000000000005014572354773015316 xustar0020 atime=1743516862 20 ctime=1743591275 bluez-5.82/android/hal-audio.c0000644000000000000000000010523514572354773015005 0ustar00rootroot// SPDX-License-Identifier: Apache-2.0 /* * Copyright (C) 2013 Intel Corporation * */ #define _GNU_SOURCE #include #include #include #include #include #include #include #include #include #include #include #include #include #include "audio-msg.h" #include "ipc-common.h" #include "hal-log.h" #include "hal-msg.h" #include "hal-audio.h" #include "hal-utils.h" #include "hal.h" #define FIXED_A2DP_PLAYBACK_LATENCY_MS 25 #define FIXED_BUFFER_SIZE (20 * 512) #define MAX_DELAY 100000 /* 100ms */ static const uint8_t a2dp_src_uuid[] = { 0x00, 0x00, 0x11, 0x0a, 0x00, 0x00, 0x10, 0x00, 0x80, 0x00, 0x00, 0x80, 0x5f, 0x9b, 0x34, 0xfb }; static int listen_sk = -1; static int audio_sk = -1; static pthread_t ipc_th = 0; static pthread_mutex_t sk_mutex = PTHREAD_MUTEX_INITIALIZER; static void timespec_add(struct timespec *base, uint64_t time_us, struct timespec *res) { res->tv_sec = base->tv_sec + time_us / 1000000; res->tv_nsec = base->tv_nsec + (time_us % 1000000) * 1000; if (res->tv_nsec >= 1000000000) { res->tv_sec++; res->tv_nsec -= 1000000000; } } static void timespec_diff(struct timespec *a, struct timespec *b, struct timespec *res) { res->tv_sec = a->tv_sec - b->tv_sec; res->tv_nsec = a->tv_nsec - b->tv_nsec; if (res->tv_nsec < 0) { res->tv_sec--; res->tv_nsec += 1000000000; /* 1sec */ } } static uint64_t timespec_diff_us(struct timespec *a, struct timespec *b) { struct timespec res; timespec_diff(a, b, &res); return res.tv_sec * 1000000ll + res.tv_nsec / 1000ll; } #if defined(ANDROID) /* * Bionic does not have clock_nanosleep() prototype in time.h even though * it provides its implementation. */ extern int clock_nanosleep(clockid_t clock_id, int flags, const struct timespec *request, struct timespec *remain); #endif static struct { const audio_codec_get_t get_codec; bool loaded; } audio_codecs[] = { { .get_codec = codec_aptx, .loaded = false }, { .get_codec = codec_sbc, .loaded = false }, }; #define NUM_CODECS (sizeof(audio_codecs) / sizeof(audio_codecs[0])) #define MAX_AUDIO_ENDPOINTS NUM_CODECS struct audio_endpoint { uint8_t id; const struct audio_codec *codec; void *codec_data; int fd; struct media_packet *mp; size_t mp_data_len; uint16_t seq; uint32_t samples; struct timespec start; bool resync; }; static struct audio_endpoint audio_endpoints[MAX_AUDIO_ENDPOINTS]; enum a2dp_state_t { AUDIO_A2DP_STATE_NONE, AUDIO_A2DP_STATE_STANDBY, AUDIO_A2DP_STATE_SUSPENDED, AUDIO_A2DP_STATE_STARTED }; struct a2dp_stream_out { struct audio_stream_out stream; struct audio_endpoint *ep; enum a2dp_state_t audio_state; struct audio_input_config cfg; uint8_t *downmix_buf; }; struct a2dp_audio_dev { struct audio_hw_device dev; struct a2dp_stream_out *out; }; static int audio_ipc_cmd(uint8_t service_id, uint8_t opcode, uint16_t len, void *param, size_t *rsp_len, void *rsp, int *fd) { ssize_t ret; struct msghdr msg; struct iovec iv[2]; struct ipc_hdr cmd; char cmsgbuf[CMSG_SPACE(sizeof(int))]; struct ipc_status s; size_t s_len = sizeof(s); pthread_mutex_lock(&sk_mutex); if (audio_sk < 0) { error("audio: Invalid cmd socket passed to audio_ipc_cmd"); goto failed; } if (!rsp || !rsp_len) { memset(&s, 0, s_len); rsp_len = &s_len; rsp = &s; } memset(&msg, 0, sizeof(msg)); memset(&cmd, 0, sizeof(cmd)); cmd.service_id = service_id; cmd.opcode = opcode; cmd.len = len; iv[0].iov_base = &cmd; iv[0].iov_len = sizeof(cmd); iv[1].iov_base = param; iv[1].iov_len = len; msg.msg_iov = iv; msg.msg_iovlen = 2; ret = sendmsg(audio_sk, &msg, 0); if (ret < 0) { error("audio: Sending command failed:%s", strerror(errno)); goto failed; } /* socket was shutdown */ if (ret == 0) { error("audio: Command socket closed"); goto failed; } memset(&msg, 0, sizeof(msg)); memset(&cmd, 0, sizeof(cmd)); iv[0].iov_base = &cmd; iv[0].iov_len = sizeof(cmd); iv[1].iov_base = rsp; iv[1].iov_len = *rsp_len; msg.msg_iov = iv; msg.msg_iovlen = 2; if (fd) { memset(cmsgbuf, 0, sizeof(cmsgbuf)); msg.msg_control = cmsgbuf; msg.msg_controllen = sizeof(cmsgbuf); } ret = recvmsg(audio_sk, &msg, 0); if (ret < 0) { error("audio: Receiving command response failed:%s", strerror(errno)); goto failed; } if (ret < (ssize_t) sizeof(cmd)) { error("audio: Too small response received(%zd bytes)", ret); goto failed; } if (cmd.service_id != service_id) { error("audio: Invalid service id (%u vs %u)", cmd.service_id, service_id); goto failed; } if (ret != (ssize_t) (sizeof(cmd) + cmd.len)) { error("audio: Malformed response received(%zd bytes)", ret); goto failed; } if (cmd.opcode != opcode && cmd.opcode != AUDIO_OP_STATUS) { error("audio: Invalid opcode received (%u vs %u)", cmd.opcode, opcode); goto failed; } if (cmd.opcode == AUDIO_OP_STATUS) { struct ipc_status *s = rsp; if (sizeof(*s) != cmd.len) { error("audio: Invalid status length"); goto failed; } if (s->code == AUDIO_STATUS_SUCCESS) { error("audio: Invalid success status response"); goto failed; } pthread_mutex_unlock(&sk_mutex); return s->code; } pthread_mutex_unlock(&sk_mutex); /* Receive auxiliary data in msg */ if (fd) { struct cmsghdr *cmsg; *fd = -1; for (cmsg = CMSG_FIRSTHDR(&msg); cmsg; cmsg = CMSG_NXTHDR(&msg, cmsg)) { if (cmsg->cmsg_level == SOL_SOCKET && cmsg->cmsg_type == SCM_RIGHTS) { memcpy(fd, CMSG_DATA(cmsg), sizeof(int)); break; } } if (*fd < 0) goto failed; } *rsp_len = cmd.len; return AUDIO_STATUS_SUCCESS; failed: /* Some serious issue happen on IPC - recover */ shutdown(audio_sk, SHUT_RDWR); pthread_mutex_unlock(&sk_mutex); return AUDIO_STATUS_FAILED; } static int ipc_open_cmd(const struct audio_codec *codec) { uint8_t buf[BLUEZ_AUDIO_MTU]; struct audio_cmd_open *cmd = (struct audio_cmd_open *) buf; struct audio_rsp_open rsp; size_t cmd_len = sizeof(buf) - sizeof(*cmd); size_t rsp_len = sizeof(rsp); int result; DBG(""); memcpy(cmd->uuid, a2dp_src_uuid, sizeof(a2dp_src_uuid)); cmd->codec = codec->type; cmd->presets = codec->get_presets(cmd->preset, &cmd_len); cmd_len += sizeof(*cmd); result = audio_ipc_cmd(AUDIO_SERVICE_ID, AUDIO_OP_OPEN, cmd_len, cmd, &rsp_len, &rsp, NULL); if (result != AUDIO_STATUS_SUCCESS) return 0; return rsp.id; } static int ipc_close_cmd(uint8_t endpoint_id) { struct audio_cmd_close cmd; int result; DBG(""); cmd.id = endpoint_id; result = audio_ipc_cmd(AUDIO_SERVICE_ID, AUDIO_OP_CLOSE, sizeof(cmd), &cmd, NULL, NULL, NULL); return result; } static int ipc_open_stream_cmd(uint8_t *endpoint_id, uint16_t *mtu, int *fd, struct audio_preset **caps) { char buf[BLUEZ_AUDIO_MTU]; struct audio_cmd_open_stream cmd; struct audio_rsp_open_stream *rsp = (struct audio_rsp_open_stream *) &buf; size_t rsp_len = sizeof(buf); int result; DBG(""); if (!caps) return AUDIO_STATUS_FAILED; cmd.id = *endpoint_id; result = audio_ipc_cmd(AUDIO_SERVICE_ID, AUDIO_OP_OPEN_STREAM, sizeof(cmd), &cmd, &rsp_len, rsp, fd); if (result == AUDIO_STATUS_SUCCESS) { size_t buf_len = sizeof(struct audio_preset) + rsp->preset[0].len; *endpoint_id = rsp->id; *mtu = rsp->mtu; *caps = malloc(buf_len); memcpy(*caps, &rsp->preset, buf_len); } else { *caps = NULL; } return result; } static int ipc_close_stream_cmd(uint8_t endpoint_id) { struct audio_cmd_close_stream cmd; int result; DBG(""); cmd.id = endpoint_id; result = audio_ipc_cmd(AUDIO_SERVICE_ID, AUDIO_OP_CLOSE_STREAM, sizeof(cmd), &cmd, NULL, NULL, NULL); return result; } static int ipc_resume_stream_cmd(uint8_t endpoint_id) { struct audio_cmd_resume_stream cmd; int result; DBG(""); cmd.id = endpoint_id; result = audio_ipc_cmd(AUDIO_SERVICE_ID, AUDIO_OP_RESUME_STREAM, sizeof(cmd), &cmd, NULL, NULL, NULL); return result; } static int ipc_suspend_stream_cmd(uint8_t endpoint_id) { struct audio_cmd_suspend_stream cmd; int result; DBG(""); cmd.id = endpoint_id; result = audio_ipc_cmd(AUDIO_SERVICE_ID, AUDIO_OP_SUSPEND_STREAM, sizeof(cmd), &cmd, NULL, NULL, NULL); return result; } struct register_state { struct audio_endpoint *ep; bool error; }; static void register_endpoint(const struct audio_codec *codec, struct register_state *state) { struct audio_endpoint *ep = state->ep; /* don't even try to register more endpoints if one failed */ if (state->error) return; ep->id = ipc_open_cmd(codec); if (!ep->id) { state->error = true; error("Failed to register endpoint"); return; } ep->codec = codec; ep->codec_data = NULL; ep->fd = -1; state->ep++; } static int register_endpoints(void) { struct register_state state; unsigned int i; state.ep = &audio_endpoints[0]; state.error = false; for (i = 0; i < NUM_CODECS; i++) { const struct audio_codec *codec = audio_codecs[i].get_codec(); if (!audio_codecs[i].loaded) continue; register_endpoint(codec, &state); } return state.error ? AUDIO_STATUS_FAILED : AUDIO_STATUS_SUCCESS; } static void unregister_endpoints(void) { size_t i; for (i = 0; i < MAX_AUDIO_ENDPOINTS; i++) { struct audio_endpoint *ep = &audio_endpoints[i]; if (ep->id) { ipc_close_cmd(ep->id); memset(ep, 0, sizeof(*ep)); } } } static bool open_endpoint(struct audio_endpoint **epp, struct audio_input_config *cfg) { struct audio_preset *preset; struct audio_endpoint *ep = *epp; const struct audio_codec *codec; uint16_t mtu; uint16_t payload_len; int fd; size_t i; uint8_t ep_id = 0; if (ep) ep_id = ep->id; if (ipc_open_stream_cmd(&ep_id, &mtu, &fd, &preset) != AUDIO_STATUS_SUCCESS) return false; DBG("ep_id=%d mtu=%u", ep_id, mtu); for (i = 0; i < MAX_AUDIO_ENDPOINTS; i++) if (audio_endpoints[i].id == ep_id) { ep = &audio_endpoints[i]; break; } if (!ep) { error("Cound not find opened endpoint"); goto failed; } *epp = ep; payload_len = mtu; if (ep->codec->use_rtp) payload_len -= sizeof(struct rtp_header); ep->fd = fd; codec = ep->codec; codec->init(preset, payload_len, &ep->codec_data); codec->get_config(ep->codec_data, cfg); ep->mp = calloc(mtu, 1); if (!ep->mp) goto failed; if (ep->codec->use_rtp) { struct media_packet_rtp *mp_rtp = (struct media_packet_rtp *) ep->mp; mp_rtp->hdr.v = 2; mp_rtp->hdr.pt = 0x60; mp_rtp->hdr.ssrc = htonl(1); } ep->mp_data_len = payload_len; free(preset); return true; failed: close(fd); free(preset); return false; } static void close_endpoint(struct audio_endpoint *ep) { ipc_close_stream_cmd(ep->id); if (ep->fd >= 0) { close(ep->fd); ep->fd = -1; } free(ep->mp); ep->codec->cleanup(ep->codec_data); ep->codec_data = NULL; } static bool resume_endpoint(struct audio_endpoint *ep) { if (ipc_resume_stream_cmd(ep->id) != AUDIO_STATUS_SUCCESS) return false; ep->samples = 0; ep->resync = false; ep->codec->update_qos(ep->codec_data, QOS_POLICY_DEFAULT); return true; } static void downmix_to_mono(struct a2dp_stream_out *out, const uint8_t *buffer, size_t bytes) { const int16_t *input = (const void *) buffer; int16_t *output = (void *) out->downmix_buf; size_t i, frames; /* PCM 16bit stereo */ frames = bytes / (2 * sizeof(int16_t)); for (i = 0; i < frames; i++) { int16_t l = get_le16(&input[i * 2]); int16_t r = get_le16(&input[i * 2 + 1]); put_le16((l + r) / 2, &output[i]); } } static bool wait_for_endpoint(struct audio_endpoint *ep, bool *writable) { int ret; while (true) { struct pollfd pollfd; pollfd.fd = ep->fd; pollfd.events = POLLOUT; pollfd.revents = 0; ret = poll(&pollfd, 1, 500); if (ret >= 0) { *writable = !!(pollfd.revents & POLLOUT); break; } if (errno != EINTR) { ret = errno; error("poll failed (%d)", ret); return false; } } return true; } static bool write_to_endpoint(struct audio_endpoint *ep, size_t bytes) { struct media_packet *mp = (struct media_packet *) ep->mp; int ret; while (true) { ret = write(ep->fd, mp, bytes); if (ret >= 0) break; /* * this should not happen so let's issue warning, but do not * fail, we can try to write next packet */ if (errno == EAGAIN) { ret = errno; warn("write failed (%d)", ret); break; } if (errno != EINTR) { ret = errno; error("write failed (%d)", ret); return false; } } return true; } static bool write_data(struct a2dp_stream_out *out, const void *buffer, size_t bytes) { struct audio_endpoint *ep = out->ep; struct media_packet *mp = (struct media_packet *) ep->mp; struct media_packet_rtp *mp_rtp = (struct media_packet_rtp *) ep->mp; size_t free_space = ep->mp_data_len; size_t consumed = 0; while (consumed < bytes) { size_t written = 0; ssize_t read; uint32_t samples; int ret; struct timespec current; uint64_t audio_sent, audio_passed; bool do_write = false; /* * prepare media packet in advance so we don't waste time after * wakeup */ if (ep->codec->use_rtp) { mp_rtp->hdr.sequence_number = htons(ep->seq++); mp_rtp->hdr.timestamp = htonl(ep->samples); } read = ep->codec->encode_mediapacket(ep->codec_data, buffer + consumed, bytes - consumed, mp, free_space, &written); /* * not much we can do here, let's just ignore remaining * data and continue */ if (read <= 0) return true; /* calculate where are we and where we should be */ clock_gettime(CLOCK_MONOTONIC, ¤t); if (!ep->samples) memcpy(&ep->start, ¤t, sizeof(ep->start)); audio_sent = ep->samples * 1000000ll / out->cfg.rate; audio_passed = timespec_diff_us(¤t, &ep->start); /* * if we're ahead of stream then wait for next write point, * if we're lagging more than 100ms then stop writing and just * skip data until we're back in sync */ if (audio_sent > audio_passed) { struct timespec anchor; ep->resync = false; timespec_add(&ep->start, audio_sent, &anchor); while (true) { ret = clock_nanosleep(CLOCK_MONOTONIC, TIMER_ABSTIME, &anchor, NULL); if (!ret) break; if (ret != EINTR) { error("clock_nanosleep failed (%d)", ret); return false; } } } else if (!ep->resync) { uint64_t diff = audio_passed - audio_sent; if (diff > MAX_DELAY) { warn("lag is %jums, resyncing", diff / 1000); ep->codec->update_qos(ep->codec_data, QOS_POLICY_DECREASE); ep->resync = true; } } /* we send data only in case codec encoded some data, i.e. some * codecs do internal buffering and output data only if full * frame can be encoded * in resync mode we'll just drop mediapackets */ if (written > 0 && !ep->resync) { /* wait some time for socket to be ready for write, * but we'll just skip writing data if timeout occurs */ if (!wait_for_endpoint(ep, &do_write)) return false; if (do_write) { if (ep->codec->use_rtp) written += sizeof(struct rtp_header); if (!write_to_endpoint(ep, written)) return false; } } /* * AudioFlinger provides 16bit PCM, so sample size is 2 bytes * multiplied by number of channels. Number of channels is * simply number of bits set in channels mask. */ samples = read / (2 * popcount(out->cfg.channels)); ep->samples += samples; consumed += read; } return true; } static ssize_t out_write(struct audio_stream_out *stream, const void *buffer, size_t bytes) { struct a2dp_stream_out *out = (struct a2dp_stream_out *) stream; const void *in_buf = buffer; size_t in_len = bytes; /* just return in case we're closing */ if (out->audio_state == AUDIO_A2DP_STATE_NONE) return -1; /* We can auto-start only from standby */ if (out->audio_state == AUDIO_A2DP_STATE_STANDBY) { DBG("stream in standby, auto-start"); if (!resume_endpoint(out->ep)) return -1; out->audio_state = AUDIO_A2DP_STATE_STARTED; } if (out->audio_state != AUDIO_A2DP_STATE_STARTED) { error("audio: stream not started"); return -1; } if (out->ep->fd < 0) { error("audio: no transport socket"); return -1; } /* * currently Android audioflinger is not able to provide mono stream on * A2DP output so down mixing needs to be done in hal-audio plugin. * * for reference see * AudioFlinger::PlaybackThread::readOutputParameters() * frameworks/av/services/audioflinger/Threads.cpp:1631 */ if (out->cfg.channels == AUDIO_CHANNEL_OUT_MONO) { if (!out->downmix_buf) { error("audio: downmix buffer not initialized"); return -1; } downmix_to_mono(out, buffer, bytes); in_buf = out->downmix_buf; in_len = bytes / 2; } if (!write_data(out, in_buf, in_len)) return -1; return bytes; } static uint32_t out_get_sample_rate(const struct audio_stream *stream) { struct a2dp_stream_out *out = (struct a2dp_stream_out *) stream; DBG(""); return out->cfg.rate; } static int out_set_sample_rate(struct audio_stream *stream, uint32_t rate) { struct a2dp_stream_out *out = (struct a2dp_stream_out *) stream; DBG(""); if (rate != out->cfg.rate) { warn("audio: cannot set sample rate to %d", rate); return -1; } return 0; } static size_t out_get_buffer_size(const struct audio_stream *stream) { DBG(""); /* * We should return proper buffer size calculated by codec (so each * input buffer is encoded into single media packed) but this does not * work well with AudioFlinger and causes problems. For this reason we * use magic value here and out_write code takes care of splitting * input buffer into multiple media packets. */ return FIXED_BUFFER_SIZE; } static uint32_t out_get_channels(const struct audio_stream *stream) { DBG(""); /* * AudioFlinger can only provide stereo stream, so we return it here and * later we'll downmix this to mono in case codec requires it */ return AUDIO_CHANNEL_OUT_STEREO; } static audio_format_t out_get_format(const struct audio_stream *stream) { struct a2dp_stream_out *out = (struct a2dp_stream_out *) stream; DBG(""); return out->cfg.format; } static int out_set_format(struct audio_stream *stream, audio_format_t format) { DBG(""); return -ENOSYS; } static int out_standby(struct audio_stream *stream) { struct a2dp_stream_out *out = (struct a2dp_stream_out *) stream; DBG(""); if (out->audio_state == AUDIO_A2DP_STATE_STARTED) { if (ipc_suspend_stream_cmd(out->ep->id) != AUDIO_STATUS_SUCCESS) return -1; out->audio_state = AUDIO_A2DP_STATE_STANDBY; } return 0; } static int out_dump(const struct audio_stream *stream, int fd) { DBG(""); return -ENOSYS; } static int out_set_parameters(struct audio_stream *stream, const char *kvpairs) { struct a2dp_stream_out *out = (struct a2dp_stream_out *) stream; char *kvpair; char *str; char *saveptr; bool enter_suspend = false; bool exit_suspend = false; DBG("%s", kvpairs); str = strdup(kvpairs); if (!str) return -ENOMEM; kvpair = strtok_r(str, ";", &saveptr); for (; kvpair && *kvpair; kvpair = strtok_r(NULL, ";", &saveptr)) { char *keyval; keyval = strchr(kvpair, '='); if (!keyval) continue; *keyval = '\0'; keyval++; if (!strcmp(kvpair, "closing")) { if (!strcmp(keyval, "true")) out->audio_state = AUDIO_A2DP_STATE_NONE; } else if (!strcmp(kvpair, "A2dpSuspended")) { if (!strcmp(keyval, "true")) enter_suspend = true; else exit_suspend = true; } } free(str); if (enter_suspend && out->audio_state == AUDIO_A2DP_STATE_STARTED) { if (ipc_suspend_stream_cmd(out->ep->id) != AUDIO_STATUS_SUCCESS) return -1; out->audio_state = AUDIO_A2DP_STATE_SUSPENDED; } if (exit_suspend && out->audio_state == AUDIO_A2DP_STATE_SUSPENDED) out->audio_state = AUDIO_A2DP_STATE_STANDBY; return 0; } static char *out_get_parameters(const struct audio_stream *stream, const char *keys) { DBG(""); return strdup(""); } static uint32_t out_get_latency(const struct audio_stream_out *stream) { struct a2dp_stream_out *out = (struct a2dp_stream_out *) stream; struct audio_endpoint *ep = out->ep; size_t pkt_duration; DBG(""); pkt_duration = ep->codec->get_mediapacket_duration(ep->codec_data); return FIXED_A2DP_PLAYBACK_LATENCY_MS + pkt_duration / 1000; } static int out_set_volume(struct audio_stream_out *stream, float left, float right) { DBG(""); /* volume controlled in audioflinger mixer (digital) */ return -ENOSYS; } static int out_get_render_position(const struct audio_stream_out *stream, uint32_t *dsp_frames) { DBG(""); return -ENOSYS; } static int out_add_audio_effect(const struct audio_stream *stream, effect_handle_t effect) { DBG(""); return -ENOSYS; } static int out_remove_audio_effect(const struct audio_stream *stream, effect_handle_t effect) { DBG(""); return -ENOSYS; } static uint32_t in_get_sample_rate(const struct audio_stream *stream) { DBG(""); return -ENOSYS; } static int in_set_sample_rate(struct audio_stream *stream, uint32_t rate) { DBG(""); return -ENOSYS; } static size_t in_get_buffer_size(const struct audio_stream *stream) { DBG(""); return -ENOSYS; } static uint32_t in_get_channels(const struct audio_stream *stream) { DBG(""); return -ENOSYS; } static audio_format_t in_get_format(const struct audio_stream *stream) { DBG(""); return -ENOSYS; } static int in_set_format(struct audio_stream *stream, audio_format_t format) { DBG(""); return -ENOSYS; } static int in_standby(struct audio_stream *stream) { DBG(""); return -ENOSYS; } static int in_dump(const struct audio_stream *stream, int fd) { DBG(""); return -ENOSYS; } static int in_set_parameters(struct audio_stream *stream, const char *kvpairs) { DBG(""); return -ENOSYS; } static char *in_get_parameters(const struct audio_stream *stream, const char *keys) { DBG(""); return strdup(""); } static int in_set_gain(struct audio_stream_in *stream, float gain) { DBG(""); return -ENOSYS; } static ssize_t in_read(struct audio_stream_in *stream, void *buffer, size_t bytes) { DBG(""); return -ENOSYS; } static uint32_t in_get_input_frames_lost(struct audio_stream_in *stream) { DBG(""); return -ENOSYS; } static int in_add_audio_effect(const struct audio_stream *stream, effect_handle_t effect) { DBG(""); return -ENOSYS; } static int in_remove_audio_effect(const struct audio_stream *stream, effect_handle_t effect) { DBG(""); return -ENOSYS; } static int audio_open_output_stream_real(struct audio_hw_device *dev, audio_io_handle_t handle, audio_devices_t devices, audio_output_flags_t flags, struct audio_config *config, struct audio_stream_out **stream_out, const char *address) { struct a2dp_audio_dev *a2dp_dev = (struct a2dp_audio_dev *) dev; struct a2dp_stream_out *out; out = calloc(1, sizeof(struct a2dp_stream_out)); if (!out) return -ENOMEM; DBG(""); out->stream.common.get_sample_rate = out_get_sample_rate; out->stream.common.set_sample_rate = out_set_sample_rate; out->stream.common.get_buffer_size = out_get_buffer_size; out->stream.common.get_channels = out_get_channels; out->stream.common.get_format = out_get_format; out->stream.common.set_format = out_set_format; out->stream.common.standby = out_standby; out->stream.common.dump = out_dump; out->stream.common.set_parameters = out_set_parameters; out->stream.common.get_parameters = out_get_parameters; out->stream.common.add_audio_effect = out_add_audio_effect; out->stream.common.remove_audio_effect = out_remove_audio_effect; out->stream.get_latency = out_get_latency; out->stream.set_volume = out_set_volume; out->stream.write = out_write; out->stream.get_render_position = out_get_render_position; /* We want to autoselect opened endpoint */ out->ep = NULL; if (!open_endpoint(&out->ep, &out->cfg)) goto fail; DBG("rate=%d channels=%d format=%d", out->cfg.rate, out->cfg.channels, out->cfg.format); if (out->cfg.channels == AUDIO_CHANNEL_OUT_MONO) { out->downmix_buf = malloc(FIXED_BUFFER_SIZE / 2); if (!out->downmix_buf) goto fail; } *stream_out = &out->stream; a2dp_dev->out = out; out->audio_state = AUDIO_A2DP_STATE_STANDBY; return 0; fail: error("audio: cannot open output stream"); free(out); *stream_out = NULL; return -EIO; } #if ANDROID_VERSION >= PLATFORM_VER(5, 0, 0) static int audio_open_output_stream(struct audio_hw_device *dev, audio_io_handle_t handle, audio_devices_t devices, audio_output_flags_t flags, struct audio_config *config, struct audio_stream_out **stream_out, const char *address) { return audio_open_output_stream_real(dev, handle, devices, flags, config, stream_out, address); } #else static int audio_open_output_stream(struct audio_hw_device *dev, audio_io_handle_t handle, audio_devices_t devices, audio_output_flags_t flags, struct audio_config *config, struct audio_stream_out **stream_out) { return audio_open_output_stream_real(dev, handle, devices, flags, config, stream_out, NULL); } #endif static void audio_close_output_stream(struct audio_hw_device *dev, struct audio_stream_out *stream) { struct a2dp_audio_dev *a2dp_dev = (struct a2dp_audio_dev *) dev; struct a2dp_stream_out *out = (struct a2dp_stream_out *) stream; DBG(""); close_endpoint(a2dp_dev->out->ep); free(out->downmix_buf); free(stream); a2dp_dev->out = NULL; } static int audio_set_parameters(struct audio_hw_device *dev, const char *kvpairs) { struct a2dp_audio_dev *a2dp_dev = (struct a2dp_audio_dev *) dev; struct a2dp_stream_out *out = a2dp_dev->out; DBG(""); if (!out) return 0; return out->stream.common.set_parameters((struct audio_stream *) out, kvpairs); } static char *audio_get_parameters(const struct audio_hw_device *dev, const char *keys) { DBG(""); return strdup(""); } static int audio_init_check(const struct audio_hw_device *dev) { DBG(""); return 0; } static int audio_set_voice_volume(struct audio_hw_device *dev, float volume) { DBG(""); return -ENOSYS; } static int audio_set_master_volume(struct audio_hw_device *dev, float volume) { DBG(""); return -ENOSYS; } static int audio_set_mode(struct audio_hw_device *dev, int mode) { DBG(""); return -ENOSYS; } static int audio_set_mic_mute(struct audio_hw_device *dev, bool state) { DBG(""); return -ENOSYS; } static int audio_get_mic_mute(const struct audio_hw_device *dev, bool *state) { DBG(""); return -ENOSYS; } static size_t audio_get_input_buffer_size(const struct audio_hw_device *dev, const struct audio_config *config) { DBG(""); return -ENOSYS; } static int audio_open_input_stream_real(struct audio_hw_device *dev, audio_io_handle_t handle, audio_devices_t devices, struct audio_config *config, struct audio_stream_in **stream_in, audio_input_flags_t flags, const char *address, audio_source_t source) { struct audio_stream_in *in; DBG(""); in = calloc(1, sizeof(struct audio_stream_in)); if (!in) return -ENOMEM; in->common.get_sample_rate = in_get_sample_rate; in->common.set_sample_rate = in_set_sample_rate; in->common.get_buffer_size = in_get_buffer_size; in->common.get_channels = in_get_channels; in->common.get_format = in_get_format; in->common.set_format = in_set_format; in->common.standby = in_standby; in->common.dump = in_dump; in->common.set_parameters = in_set_parameters; in->common.get_parameters = in_get_parameters; in->common.add_audio_effect = in_add_audio_effect; in->common.remove_audio_effect = in_remove_audio_effect; in->set_gain = in_set_gain; in->read = in_read; in->get_input_frames_lost = in_get_input_frames_lost; *stream_in = in; return 0; } #if ANDROID_VERSION >= PLATFORM_VER(5, 0, 0) static int audio_open_input_stream(struct audio_hw_device *dev, audio_io_handle_t handle, audio_devices_t devices, struct audio_config *config, struct audio_stream_in **stream_in, audio_input_flags_t flags, const char *address, audio_source_t source) { return audio_open_input_stream_real(dev, handle, devices, config, stream_in, flags, address, source); } #else static int audio_open_input_stream(struct audio_hw_device *dev, audio_io_handle_t handle, audio_devices_t devices, struct audio_config *config, struct audio_stream_in **stream_in) { return audio_open_input_stream_real(dev, handle, devices, config, stream_in, 0, NULL, 0); } #endif static void audio_close_input_stream(struct audio_hw_device *dev, struct audio_stream_in *stream_in) { DBG(""); free(stream_in); } static int audio_dump(const audio_hw_device_t *device, int fd) { DBG(""); return -ENOSYS; } #if ANDROID_VERSION >= PLATFORM_VER(5, 0, 0) static int set_master_mute(struct audio_hw_device *dev, bool mute) { DBG(""); return -ENOSYS; } static int get_master_mute(struct audio_hw_device *dev, bool *mute) { DBG(""); return -ENOSYS; } static int create_audio_patch(struct audio_hw_device *dev, unsigned int num_sources, const struct audio_port_config *sources, unsigned int num_sinks, const struct audio_port_config *sinks, audio_patch_handle_t *handle) { DBG(""); return -ENOSYS; } static int release_audio_patch(struct audio_hw_device *dev, audio_patch_handle_t handle) { DBG(""); return -ENOSYS; } static int get_audio_port(struct audio_hw_device *dev, struct audio_port *port) { DBG(""); return -ENOSYS; } static int set_audio_port_config(struct audio_hw_device *dev, const struct audio_port_config *config) { DBG(""); return -ENOSYS; } #endif static int audio_close(hw_device_t *device) { struct a2dp_audio_dev *a2dp_dev = (struct a2dp_audio_dev *)device; unsigned int i; DBG(""); unregister_endpoints(); for (i = 0; i < NUM_CODECS; i++) { const struct audio_codec *codec = audio_codecs[i].get_codec(); if (!audio_codecs[i].loaded) continue; if (codec->unload) codec->unload(); audio_codecs[i].loaded = false; } shutdown(listen_sk, SHUT_RDWR); shutdown(audio_sk, SHUT_RDWR); pthread_join(ipc_th, NULL); close(listen_sk); listen_sk = -1; free(a2dp_dev); return 0; } static void *ipc_handler(void *data) { bool done = false; struct pollfd pfd; int sk; DBG(""); while (!done) { DBG("Waiting for connection ..."); sk = accept(listen_sk, NULL, NULL); if (sk < 0) { int err = errno; if (err == EINTR) continue; if (err != ECONNABORTED && err != EINVAL) error("audio: Failed to accept socket: %d (%s)", err, strerror(err)); break; } pthread_mutex_lock(&sk_mutex); audio_sk = sk; pthread_mutex_unlock(&sk_mutex); DBG("Audio IPC: Connected"); if (register_endpoints() != AUDIO_STATUS_SUCCESS) { error("audio: Failed to register endpoints"); unregister_endpoints(); pthread_mutex_lock(&sk_mutex); shutdown(audio_sk, SHUT_RDWR); close(audio_sk); audio_sk = -1; pthread_mutex_unlock(&sk_mutex); continue; } memset(&pfd, 0, sizeof(pfd)); pfd.fd = audio_sk; pfd.events = POLLHUP | POLLERR | POLLNVAL; /* Check if socket is still alive. Empty while loop.*/ while (poll(&pfd, 1, -1) < 0 && errno == EINTR); info("Audio HAL: Socket closed"); pthread_mutex_lock(&sk_mutex); close(audio_sk); audio_sk = -1; pthread_mutex_unlock(&sk_mutex); } /* audio_sk is closed at this point, just cleanup endpoints states */ memset(audio_endpoints, 0, sizeof(audio_endpoints)); info("Closing Audio IPC thread"); return NULL; } static int audio_ipc_init(void) { struct sockaddr_un addr; int err; int sk; DBG(""); sk = socket(PF_LOCAL, SOCK_SEQPACKET, 0); if (sk < 0) { err = -errno; error("audio: Failed to create socket: %d (%s)", -err, strerror(-err)); return err; } memset(&addr, 0, sizeof(addr)); addr.sun_family = AF_UNIX; memcpy(addr.sun_path, BLUEZ_AUDIO_SK_PATH, sizeof(BLUEZ_AUDIO_SK_PATH)); if (bind(sk, (struct sockaddr *) &addr, sizeof(addr)) < 0) { err = -errno; error("audio: Failed to bind socket: %d (%s)", -err, strerror(-err)); goto failed; } if (listen(sk, 1) < 0) { err = -errno; error("audio: Failed to listen on the socket: %d (%s)", -err, strerror(-err)); goto failed; } listen_sk = sk; err = pthread_create(&ipc_th, NULL, ipc_handler, NULL); if (err) { err = -err; ipc_th = 0; error("audio: Failed to start Audio IPC thread: %d (%s)", -err, strerror(-err)); goto failed; } return 0; failed: close(sk); return err; } static int audio_open(const hw_module_t *module, const char *name, hw_device_t **device) { struct a2dp_audio_dev *a2dp_dev; size_t i; int err; DBG(""); if (strcmp(name, AUDIO_HARDWARE_INTERFACE)) { error("audio: interface %s not matching [%s]", name, AUDIO_HARDWARE_INTERFACE); return -EINVAL; } err = audio_ipc_init(); if (err < 0) return err; a2dp_dev = calloc(1, sizeof(struct a2dp_audio_dev)); if (!a2dp_dev) return -ENOMEM; a2dp_dev->dev.common.tag = HARDWARE_DEVICE_TAG; a2dp_dev->dev.common.version = AUDIO_DEVICE_API_VERSION_CURRENT; a2dp_dev->dev.common.module = (struct hw_module_t *) module; a2dp_dev->dev.common.close = audio_close; a2dp_dev->dev.init_check = audio_init_check; a2dp_dev->dev.set_voice_volume = audio_set_voice_volume; a2dp_dev->dev.set_master_volume = audio_set_master_volume; a2dp_dev->dev.set_mode = audio_set_mode; a2dp_dev->dev.set_mic_mute = audio_set_mic_mute; a2dp_dev->dev.get_mic_mute = audio_get_mic_mute; a2dp_dev->dev.set_parameters = audio_set_parameters; a2dp_dev->dev.get_parameters = audio_get_parameters; a2dp_dev->dev.get_input_buffer_size = audio_get_input_buffer_size; a2dp_dev->dev.open_output_stream = audio_open_output_stream; a2dp_dev->dev.close_output_stream = audio_close_output_stream; a2dp_dev->dev.open_input_stream = audio_open_input_stream; a2dp_dev->dev.close_input_stream = audio_close_input_stream; a2dp_dev->dev.dump = audio_dump; #if ANDROID_VERSION >= PLATFORM_VER(5, 0, 0) a2dp_dev->dev.set_master_mute = set_master_mute; a2dp_dev->dev.get_master_mute = get_master_mute; a2dp_dev->dev.create_audio_patch = create_audio_patch; a2dp_dev->dev.release_audio_patch = release_audio_patch; a2dp_dev->dev.get_audio_port = get_audio_port; a2dp_dev->dev.set_audio_port_config = set_audio_port_config; #endif for (i = 0; i < NUM_CODECS; i++) { const struct audio_codec *codec = audio_codecs[i].get_codec(); if (codec->load && !codec->load()) continue; audio_codecs[i].loaded = true; } /* * Note that &a2dp_dev->dev.common is the same pointer as a2dp_dev. * This results from the structure of following structs:a2dp_audio_dev, * audio_hw_device. We will rely on this later in the code. */ *device = &a2dp_dev->dev.common; return 0; } static struct hw_module_methods_t hal_module_methods = { .open = audio_open, }; __attribute__ ((visibility("default"))) struct audio_module HAL_MODULE_INFO_SYM = { .common = { .tag = HARDWARE_MODULE_TAG, .version_major = 1, .version_minor = 0, .id = AUDIO_HARDWARE_MODULE_ID, .name = "A2DP Bluez HW HAL", .author = "Intel Corporation", .methods = &hal_module_methods, }, }; bluez-5.82/android/PaxHeaders/pics-opp.txt0000644000000000000000000000005012537515745015577 xustar0020 atime=1743516876 20 ctime=1743591288 bluez-5.82/android/pics-opp.txt0000644000000000000000000002231212537515745015260 0ustar00rootrootOPP PICS for the PTS tool. PTS version: 6.1 * - different than PTS defaults # - not yet implemented/supported M - mandatory O - optional Roles ------------------------------------------------------------------------------- Parameter Name Selected Description ------------------------------------------------------------------------------- TSPC_OPP_1_1 True (*) Role: Object Push Client (C.1) TSPC_OPP_1_2 True (*) Role: Object Push Server (C.1) ------------------------------------------------------------------------------- C.1: Mandatory to support at least one of the defined roles. ------------------------------------------------------------------------------- Client Profile Version ------------------------------------------------------------------------------- Parameter Name Selected Description ------------------------------------------------------------------------------- TSPC_OPP_1b_1 True (*) Client supports OPP version 1.1. (C.1) TSPC_OPP_1b_2 False Client supports OPP version 1.2. (C.1) ------------------------------------------------------------------------------- C.1: It is mandatory to support at least one of the profile versions. ------------------------------------------------------------------------------- Client Application Features ------------------------------------------------------------------------------- Parameter Name Selected Description ------------------------------------------------------------------------------- TSPC_OPP_2_1 True Client: Perform Service Discovery request (M) TSPC_OPP_2_2 True Client: Authentication/PIN exchange supported. (M) TSPC_OPP_2_2a True (*) Client: Require Authentication/PIN by default. (O) TSPC_OPP_2_3 True Client: Object Push (M) TSPC_OPP_2_4 True (*) Client: vCard 2.1 (C.3) TSPC_OPP_2_5 False Client: vCalender 1.0 (O) TSPC_OPP_2_6 False Client: vMsg as defined in IrMC 1.1 (O) TSPC_OPP_2_7 False Client: vNote as defined in IrMC 1.1 (O) TSPC_OPP_2_8 True (*) Client: Support content formats other than those declared in TSPC_OPP_2_4 through TSPC_OPP_2_7. (O) TSPC_OPP_2_8a False Client: Support specific set of other content formats. (C.4) TSPC_OPP_2_8b True (*) Client: Support all content formats. (C.4) TSPC_OPP_2_9 True (*) Client: Push multiple vCard objects. (O) TSPC_OPP_2_9a False Client: Push multiple vCard objects using different PUT operations. (C.5) TSPC_OPP_2_9b True (*) Client: Push multiple vCard objects using the same PUT operation. (C.5) TSPC_OPP_2_10 False Client: Push multiple vCalender objects. (O) TSPC_OPP_2_10a False Client: Push multiple vCalendar objects using different PUT operations. (C.6) TSPC_OPP_2_10b False Client: Push multiple vCalendar objects using the same PUT operation. (C.6) TSPC_OPP_2_11 False Client: Push multiple vMsg objects. (O) TSPC_OPP_2_11a False Client: Push multiple vMsg objects using different PUT operations. (C.7) TSPC_OPP_2_11b False Client: Push multiple vMsg objects using the same PUT operation. (C.7) TSPC_OPP_2_12 False Client: Push multiple vNote objects. (O) TSPC_OPP_2_12a False Client: Push multiple vNote objects using different PUT operations. (C.8) TSPC_OPP_2_12b False Client: Push multiple vNote objects using the same PUT operation. (C.8) TSPC_OPP_2_13 False Client: Pull business card (O) TSPC_OPP_2_14 False Client: vCard 2.1 (C.1) TSPC_OPP_2_15 False Client: Exchange business card (O) TSPC_OPP_2_16 False Client: vCard 2.1 (C.2) TSPC_OPP_2_17 False GOEP v2 (C.9) TSPC_OPP_2_18 False GOEP v2 Backward Compability (C.9) TSPC_OPP_2_19 False OBEX over L2CAP (C.9) TSPC_OPP_2_20 False OBEX Reliable Session (C.10) TSPC_OPP_2_21 False OBEX SRM (C.10) TSPC_OPP_2_22 False Send OBEX SRMP header (C.10) TSPC_OPP_2_23 False Receive OBEX SRMP header (C.11) ------------------------------------------------------------------------------- C.1: Mandatory to Support IF (TSPC_OPP_2_13) Business Card Pull is supported. C.2: Mandatory to Support IF (TSPC_OPP_2_15) Business Card Exchange is supported. C.3: vCard 2.1 support is required for devices containing phonebook applications. vCard 2.1 support optional for other devices. C.4: Mandatory to support one of TSPC_OPP_2_8a or TSPC_OPP_2_8b if TSPC_OPP_2_8 is supported. Otherwise, both items are excluded. C.5: Mandatory to support at least one of TSPC_OPP_2_9a and TSPC_OPP_2_9b if TSPC_OPP_2_9 is supported. Otherwise, both items are excluded. C.6: Mandatory to support at least one of TSPC_OPP_2_10a and TSPC_OPP_2_10b if TSPC_OPP_2_10 is supported. Otherwise, both items are excluded. C.7: Mandatory to support at least one of TSPC_OPP_2_11a and TSPC_OPP_2_11b if TSPC_OPP_2_11 is supported. Otherwise, both items are excluded. C.8: Mandatory to support at least one of TSPC_OPP_2_12a and TSPC_OPP_2_12b if TSPC_OPP_2_12 is supported. Otherwise, both items are excluded. C.9: Mandatory if TSPC_OPP_1b_2 supported. C.10: Optional to support if TSPC_OPP_1b_2 supported else excluded. C.11: Mandatory if TSPC_OPP_17 and TSPC_OPP_21 supported else excluded. ------------------------------------------------------------------------------- Server Profile Version ------------------------------------------------------------------------------- Parameter Name Selected Description ------------------------------------------------------------------------------- TSPC_OPP_2b_1 True (*) Server supports OPP version 1.1. TSPC_OPP_2b_2 False Server supports OPP version 1.2. ------------------------------------------------------------------------------- C.1: It is mandatory to support at least one of the profile versions. ------------------------------------------------------------------------------- Server Application Features ------------------------------------------------------------------------------- Parameter Name Selected Description ------------------------------------------------------------------------------- TSPC_OPP_3_1 True Server: Provide information on supported contents type on service discovery request. (M) TSPC_OPP_3_2 True Server: Authentication/PIN exchange supported. (M) TSPC_OPP_3_3 True Server: Object Push (M) TSPC_OPP_3_3a True (*) Server: Receive multiple objects in the same PUT operation. (O) TSPC_OPP_3_4 True (*) Server: vCard 2.1 (C.3) TSPC_OPP_3_5 False Server: vCalender 1.0 format (O) TSPC_OPP_3_6 False Server: vMsg as defined in IrMC 1.1 (O) TSPC_OPP_3_7 False Server: vNote as defined in IrMC 1.1 (O) TSPC_OPP_3_8 True (*) Server: Support content formats other than those declared in TSPC_OPP_3_4 through TSPC_OPP_3_7. (O) TSPC_OPP_3_8a False Server: Support specific set of other content formats. (C.4) TSPC_OPP_3_8b True (*) Server: Support all content formats. (C.4) TSPC_OPP_3_9 True (*) Server: Object Push vCard reject. (O) TSPC_OPP_3_10 False Server: Object Push vCal reject. (O) TSPC_OPP_3_11 False Server: Object Push vMsg reject. (O) TSPC_OPP_3_12 False Server: Object Push vNote reject. (O) TSPC_OPP_3_13 False Server: Business card pull (O.1) TSPC_OPP_3_14 False Server: vCard 2.1 (C.1) TSPC_OPP_3_15 False Server: Business card pull reject. (O) TSPC_OPP_3_16 False Server: Business card exchange (O.2) TSPC_OPP_3_17 False Server: vCard 2.1 (C.2) TSPC_OPP_3_18 False Server: Business card exchange reject. (O) TSPC_OPP_3_19 False GOEP v2 (C.5) TSPC_OPP_3_20 False GOEP v2 Backward Compability (C.5) TSPC_OPP_3_21 False OBEX over L2CAP (C.5) TSPC_OPP_3_22 False OBEX Reliable Session (C.16) TSPC_OPP_3_23 False OBEX SRM (C.6) TSPC_OPP_3_24 False Send OBEX SRMP header (C.6) TSPC_OPP_3_25 False Receive OBEX SRMP header (C.7) ------------------------------------------------------------------------------- O.1: IF NOT Supported, an error message must be sent on request for Business Card Pull. O.2: IF NOT Supported, an error message must be sent on request for Business Card Exchange. C.1: Mandatory to Support IF (TSPC_OPP_3_13) Business Card Pull is supported. C.2: Mandatory to Support IF (TSPC_OPP_3_16) Business Card Exchange is supported. C.3: vCard 2.1 support is required for devices containing phonebook applications. vCard 2.1 support optional for other devices. C.4: Mandatory to support one of TSPC_OPP_3_8a or TSPC_OPP_3_8b if TSPC_OPP_3_8 is supported. Otherwise, both items are excluded. C.5: Mandatory if TSPC_OPP_2b_2 supported. C.6: Optional to support if TSPC_OPP_2b_2 supported, else excluded. C.7: Mandatory if TSPC_OPP_3_19 and TSPC_OPP_3_23 supported else excluded. ------------------------------------------------------------------------------- Additional OPP Capabilities ------------------------------------------------------------------------------- Parameter Name Selected Description ------------------------------------------------------------------------------- TSPC_OPP_4_1 False Abort-Push Operation (O) TSPC_OPP_4_2 False Intentionally Left Blank (N/A) TSPC_OPP_4_3 False Multiple vCards transferred as a single vObject (C.1) TSPC_OPP_4_4 False Multiple vCards transfer (C.1) TSPC_OPP_4_5 False vCards with multiple Phone Number Fields (C.1) TSPC_OPP_4_6 False Push vCal to Different Time Zone Server (C.1) ------------------------------------------------------------------------------- C.1: Optional if TSPC_OPP_1_2 is supported, otherwise excluded. ------------------------------------------------------------------------------- bluez-5.82/android/PaxHeaders/pixit-pbap.txt0000644000000000000000000000005012537515745016122 xustar0020 atime=1743516876 20 ctime=1743591289 bluez-5.82/android/pixit-pbap.txt0000644000000000000000000000236212537515745015606 0ustar00rootrootPBAP PIXIT for the PTS tool. PTS version: 6.1 * - different than PTS defaults & - should be set to IUT Bluetooth address Required PIXIT settings ------------------------------------------------------------------------------- Parameter Name Value ------------------------------------------------------------------------------- TSPX_auth_password 0000 TSPX_auth_user_id PTS TSPX_security_enabled True TSPX_bd_addr_iut 112233445566 (*&) TSPX_pin_code 0000 TSPX_time_guard 100000 TSPX_use_implicit_send True TSPX_client_class_of_device 100204 TSPX_server_class_of_device 100204 TSPX_PSE_vCardSelector 0000000000000001 TSPX_delete_link_key False TSPX_PBAP_profile_version 1 TSPX_PBAP_supported_repositories 1 TSPX_PBAP_rfcomm_channel 1 TSPX_telecom_folder_path telecom TSPX_secure_simple_pairing_pass_key_confirmation False TSPX_check_downloaded_contents_after_disconnect False TSPX_SPP_rfcomm_channel 03 TSPX_l2cap_psm 1005 TSPX_rfcomm_channel 2 TSPX_obex_auth_password 0000 TSPX_no_confirmations False TSPX_PSE_vCardSelector 0000000000000001 TSPX_Automation False TSPX_search_criteria PTS ------------------------------------------------------------------------------- bluez-5.82/android/PaxHeaders/hal-avrcp-ctrl.c0000644000000000000000000000005014015011623016242 xustar0020 atime=1743516862 20 ctime=1743591276 bluez-5.82/android/hal-avrcp-ctrl.c0000644000000000000000000000557614015011623015740 0ustar00rootroot// SPDX-License-Identifier: Apache-2.0 /* * Copyright (C) 2014 Intel Corporation * */ #define _GNU_SOURCE #include #include #include #include #include "hal-utils.h" #include "hal-log.h" #include "hal.h" #include "hal-msg.h" #include "ipc-common.h" #include "hal-ipc.h" static const btrc_ctrl_callbacks_t *cbs = NULL; static bool interface_ready(void) { return cbs != NULL; } static void handle_connection_state(void *buf, uint16_t len, int fd) { struct hal_ev_avrcp_ctrl_conn_state *ev = buf; if (cbs->connection_state_cb) cbs->connection_state_cb(ev->state, (bt_bdaddr_t *) (ev->bdaddr)); } static void handle_passthrough_rsp(void *buf, uint16_t len, int fd) { struct hal_ev_avrcp_ctrl_passthrough_rsp *ev = buf; if (cbs->passthrough_rsp_cb) cbs->passthrough_rsp_cb(ev->id, ev->key_state); } /* * handlers will be called from notification thread context, * index in table equals to 'opcode - HAL_MINIMUM_EVENT' */ static const struct hal_ipc_handler ev_handlers[] = { /* HAL_EV_AVRCP_CTRL_CONN_STATE */ { handle_connection_state, false, sizeof(struct hal_ev_avrcp_ctrl_conn_state) }, /* HAL_EV_AVRCP_CTRL_PASSTHROUGH_RSP */ { handle_passthrough_rsp, false, sizeof(struct hal_ev_avrcp_ctrl_passthrough_rsp) }, }; static bt_status_t init(btrc_ctrl_callbacks_t *callbacks) { struct hal_cmd_register_module cmd; int ret; DBG(""); if (interface_ready()) return BT_STATUS_DONE; cbs = callbacks; hal_ipc_register(HAL_SERVICE_ID_AVRCP_CTRL, ev_handlers, sizeof(ev_handlers) / sizeof(ev_handlers[0])); cmd.service_id = HAL_SERVICE_ID_AVRCP_CTRL; cmd.mode = HAL_MODE_DEFAULT; cmd.max_clients = 1; ret = hal_ipc_cmd(HAL_SERVICE_ID_CORE, HAL_OP_REGISTER_MODULE, sizeof(cmd), &cmd, NULL, NULL, NULL); if (ret != BT_STATUS_SUCCESS) { cbs = NULL; hal_ipc_unregister(HAL_SERVICE_ID_AVRCP_CTRL); } return ret; } static bt_status_t send_pass_through_cmd(bt_bdaddr_t *bd_addr, uint8_t key_code, uint8_t key_state) { struct hal_cmd_avrcp_ctrl_send_passthrough cmd; DBG(""); if (!interface_ready()) return BT_STATUS_NOT_READY; memcpy(cmd.bdaddr, bd_addr, sizeof(cmd.bdaddr)); cmd.key_code = key_code; cmd.key_state = key_state; return hal_ipc_cmd(HAL_SERVICE_ID_AVRCP_CTRL, HAL_OP_AVRCP_CTRL_SEND_PASSTHROUGH, sizeof(cmd), &cmd, NULL, NULL, NULL); } static void cleanup(void) { struct hal_cmd_unregister_module cmd; DBG(""); if (!interface_ready()) return; cmd.service_id = HAL_SERVICE_ID_AVRCP_CTRL; hal_ipc_cmd(HAL_SERVICE_ID_CORE, HAL_OP_UNREGISTER_MODULE, sizeof(cmd), &cmd, NULL, NULL, NULL); hal_ipc_unregister(HAL_SERVICE_ID_AVRCP_CTRL); cbs = NULL; } static btrc_ctrl_interface_t iface = { .size = sizeof(iface), .init = init, .send_pass_through_cmd = send_pass_through_cmd, .cleanup = cleanup }; btrc_ctrl_interface_t *bt_get_avrcp_ctrl_interface(void) { return &iface; } bluez-5.82/android/PaxHeaders/pts-spp.txt0000644000000000000000000000005012537515745015453 xustar0020 atime=1743516876 20 ctime=1743591289 bluez-5.82/android/pts-spp.txt0000644000000000000000000000127012537515745015134 0ustar00rootrootPTS test results for SPP PTS version: 6.1 Tested: 19-May-2015 Android version: 5.1 Results: PASS test passed FAIL test failed INC test is inconclusive N/A test is disabled due to PICS setup ------------------------------------------------------------------------------- Test Name Result Notes ------------------------------------------------------------------------------- TC_DevA_APP_BV_01_C PASS haltest: socket connect BTSOCK_RFCOMM 00001101 0 TC_DevB_APP_BV_02_C PASS haltest: socket listen BTSOCK_RFCOMM SerialPort 00001101 Note: IUT must be in connectable, discoverable mode. ------------------------------------------------------------------------------- bluez-5.82/android/PaxHeaders/handsfree-client.h0000644000000000000000000000005014015011623016643 xustar0020 atime=1743516866 20 ctime=1743591279 bluez-5.82/android/handsfree-client.h0000644000000000000000000000043314015011623016324 0ustar00rootroot/* SPDX-License-Identifier: LGPL-2.1-or-later */ /* * * BlueZ - Bluetooth protocol stack for Linux * * Copyright (C) 2014 Intel Corporation. All rights reserved. * * */ bool bt_hf_client_register(struct ipc *ipc, const bdaddr_t *addr); void bt_hf_client_unregister(void); bluez-5.82/android/PaxHeaders/pics-hdp.txt0000644000000000000000000000005012537515745015554 xustar0020 atime=1743516876 20 ctime=1743591289 bluez-5.82/android/pics-hdp.txt0000644000000000000000000003670712537515745015252 0ustar00rootrootHDP PICS for the PTS tool. PTS version: 6.1 * - different than PTS defaults # - not yet implemented/supported M - mandatory O - optional Profile Version ------------------------------------------------------------------------------- Parameter Name Selected Description ------------------------------------------------------------------------------- TSPC_HDP_0_1 False HDP 1.0 (C.1) TSPC_HDP_0_2 True HDP 1.1 (C.1) ------------------------------------------------------------------------------- C.1: Mandatory to support only one Profile version. ------------------------------------------------------------------------------- Roles ------------------------------------------------------------------------------- Parameter Name Selected Description ------------------------------------------------------------------------------- TSPC_HDP_1_1 True Supports Source Role (C.1, C.2) TSPC_HDP_1_2 True (*) Supports Sink Role (C.1, C.3) ------------------------------------------------------------------------------- C.1: At least one of the defined roles is Mandatory. C.2: Mandatory if TSPC_MCAP_1_1 is supported, otherwise Excluded. C.3: Mandatory if TSPC_MCAP_1_1 is supported, otherwise Excluded. ------------------------------------------------------------------------------- GAP Features - Source ------------------------------------------------------------------------------- Parameter Name Selected Description ------------------------------------------------------------------------------- TSPC_HDP_2_1 True Supports General-discoverable Mode (M) TSPC_HDP_2_2 True Supports bondable Mode (M) (C.1) TSPC_HDP_2_3 True Supports Response to Authentication requests (M) TSPC_HDP_2_4 True Supports Initiation of Authentication (M) (C.2) TSPC_HDP_2_5 True Supports Acceptance of Encryption request (M) TSPC_HDP_2_6 True Supports Initiation of Encryption (M) (C.3) TSPC_HDP_2_7 True (*) Supports General Inquiry (C.5) (C.4) TSPC_HDP_2_8 True Supports Acceptance of Bonding requests (M) TSPC_HDP_2_9 True (*) Supports Initiation of Bonding (O) TSPC_HDP_2_10 True (*) Supports Extended Inquiry Response (C.7) TSPC_HDP_2_11 False Supports use of Health Class of Device (O) ------------------------------------------------------------------------------- C.1: Mandatory if TSPC_HDP_2_9 is supported, otherwise Optional. C.2: Mandatory if Security Mode 2, 3, or 4 is supported, otherwise Optional. C.3: Mandatory if Bluetooth version 2.1 + EDR is claimed, otherwise Optional. C.4: Mandatory if TSPC_HDP_2_9 is supported, otherwise Optional. C.5: Mandatory if TSPC_HDP_2_9 is supported, otherwise Optional. C.6: Mandatory if Bluetooth Core Specification 2.1 + EDR or later (Not SUM ICS 31/4) and Table 2/1 (Supports General-discoverable Mode) is supported, otherwise Optional if Bluetooth Core Specification 2.1 + EDR or later (Not SUM ICS 31/4) is supported, otherwise Excluded. C.7: Mandatory if Bluetooth Core specification 2.1 + EDR or later is supported, otherwise Excluded. ------------------------------------------------------------------------------- L2CAP Features - Source ------------------------------------------------------------------------------- Parameter Name Selected Description ------------------------------------------------------------------------------- TSPC_HDP_3_1 True Supports Reliable Control Channel (C.1) TSPC_HDP_3_2 True Uses FCS for Control Channel (M) TSPC_HDP_3_3 True Supports Reliable Data Channel (C.1) TSPC_HDP_3_4 True Can send data using SAR in ERTM (C.2) TSPC_HDP_3_5 True (*) Uses FCS for Reliable Data Channel (O) TSPC_HDP_3_6 True (*) Supports FCS option of "No FCS" for Reliable Data Channel (C.3) TSPC_HDP_3_7 True Supports Streaming Data Channel (C.4) TSPC_HDP_3_8 True (*) Can send data using SAR in SM (C.5) TSPC_HDP_3_9 True (*) Uses FCS for Steaming Data Channel (C.6) TSPC_HDP_3_10 True (*) Supports FCS option of "No FCS" for Streaming (C.7) TSPC_HDP_3_11 True Maximum number of simultaneous Data Channels supported (DCmax) per MCL (C.8) ------------------------------------------------------------------------------- C.1: Mandatory if TSPC_L2CAP_2_12 is supported, otherwise Excluded. C.2: Mandatory if TSPC_L2CAP_2_22 is supported, otherwise Excluded. C.3: Optional if TSPC_L2CAP_2_14 is supported, otherwise Excluded. C.4: Optional if TSPC_L2CAP_2_13 is supported, otherwise Excluded. C.5: Mandatory if TSPC_HDP_3_7 and TSPC_L2CAP_2_23 are supported, otherwise Excluded. C.6: Optional if TSPC_HDP_3_7 is supported, otherwise Excluded. C.7: Optional if TSPC_HDP_3_7 and TSPC_L2CAP_2_14 are supported, otherwise Excluded. C.8: >=2 if Table TSPC_HDP_3_7 is claimed, otherwise >=1. ------------------------------------------------------------------------------- SDP Attributes - Source ------------------------------------------------------------------------------- Parameter Name Selected Description ------------------------------------------------------------------------------- TSPC_HDP_4_1 True Supports advertisement of HDP Service Record (C.1) (C.4) TSPC_HDP_4_2 True Service Class ID List (C.2) TSPC_HDP_4_3 True Protocol Descriptor List (C.2) TSPC_HDP_4_4 True Bluetooth Profile Descriptor List (C.2) TSPC_HDP_4_5 True (*) Additional Protocol Descriptor Lists (C.2) TSPC_HDP_4_6 True (*) Service Name (O) TSPC_HDP_4_7 True (*) Service Description (O) TSPC_HDP_4_8 True (*) Provider Name (O) TSPC_HDP_4_9 True HDP Supported Features (MDEP List) (C.3) TSPC_HDP_4_10 True MCAP Data Exchange Specification (C.3) TSPC_HDP_4_11 True MCAP Supported Procedures (C.3) TSPC_HDP_4_12 True (*) Service Record State (O) ------------------------------------------------------------------------------- C.1: Mandatory if TSPC_HDP_6_3 is supported, otherwise Excluded. C.2: Mandatory if TSPC_HDP_4_1 is supported, otherwise Optional. C.3: Mandatory if TSPC_HDP_4_1 is supported, otherwise Excluded. C.4: Mandatory to support SDP Server Role (SDP 1b/1) if this item is supported. ------------------------------------------------------------------------------- Device Identification - Source ------------------------------------------------------------------------------- Parameter Name Selected Description ------------------------------------------------------------------------------- TSPC_HDP_5_1 True Device Identification Profile v1.3 or later (C.1) ------------------------------------------------------------------------------- C.1: Mandatory if TSPC_HDP_4_1 is supported, otherwise Optional. ------------------------------------------------------------------------------- HDP Features - Source ------------------------------------------------------------------------------- Parameter Name Selected Description ------------------------------------------------------------------------------- TSPC_HDP_6_1 True Supports Standard Op Codes (M) TSPC_HDP_6_2 True (*) Supports Initiate creation of Control and Data Channels (C.3) (C.7) (C.1 - MCAP Status) TSPC_HDP_6_3 True Supports Accept creation of Control and Data Channels (C.3) (C.8) (C.1 - MCAP Status) TSPC_HDP_6_4 False Supports Initiate Reconnection of MDL (O) (C.2 - MCAP Status) TSPC_HDP_6_5 True Supports Accept Reconnection of MDL (C.4) TSPC_HDP_6_6 False Supports Clock Synchronization Protocol (O) TSPC_HDP_6_7 False (*) Supports Sync-Slave (C.5) TSPC_HDP_6_8 False Supports Sync-Master (C.6) ------------------------------------------------------------------------------- C.1: If TSPC_HDP_6_1 is supported, at least one is Mandatory, otherwise Excluded. C.2: Optional if TSPC_HDP_6_1 is supported, otherwise Excluded. C.3: Mandatory to support at least one. C.4: Mandatory if TSPC_HDP_6_3 is supported, otherwise Excluded. C.5: Mandatory if TSPC_HDP_6_6 is supported, otherwise Excluded. C.6: Optional if TSPC_HDP_6_6 is supported, otherwise Excluded. C.7: Mandatory to support SDP Client Role (SDP 1b/2) if this item is supported. C.8: Mandatory to support SDP Server Role (SDP 1b/1) if this item is supported. ------------------------------------------------------------------------------- Data Exchange Features - Source ------------------------------------------------------------------------------- Parameter Name Selected Description ------------------------------------------------------------------------------- TSPC_HDP_7_1 False Supports Initiation of Echo Test (O) TSPC_HDP_7_2 True Supports Acceptance of Echo Test (M) TSPC_HDP_7_3 True Supports IEEE 11073-20601 (M) TSPC_HDP_7_4 False (*) Supports IEEE 11073-20601 Agent Role (C.1) TSPC_HDP_7_5 True (*) Supports IEEE 11073-20601 Manager Role (C.1) TSPC_HDP_7_6 False Supports Initiation of Association Release (O) ------------------------------------------------------------------------------- C.1: If TSPC_HDP_7_3 is supported, at least one is Mandatory, otherwise Excluded. ------------------------------------------------------------------------------- GAP Features - Sink ------------------------------------------------------------------------------- Parameter Name Selected Description ------------------------------------------------------------------------------- TSPC_HDP_8_1 True Supports General-discoverable Mode (M) TSPC_HDP_8_2 True Supports Bondable Mode (M) (C.1) TSPC_HDP_8_3 True Supports Response to Authentitaction requests (M) TSPC_HDP_8_4 True Supports Initiation of Authentication (M) (C.2) TSPC_HDP_8_5 True Supports Acceptance of Encryption request (M) TSPC_HDP_8_6 True Supports Initiation of Encryption (M) (C.3) TSPC_HDP_8_7 True Supports General Inquiry (M) C.4) TSPC_HDP_8_8 True Supports Acceptance of Bonding requests (M) TSPC_HDP_8_9 True (*) Supports Initiation of Bonding (O) TSPC_HDP_8_10 True (*) Supports Extended Inquiry Response (C.5) (C.6) TSPC_HDP_8_11 False Supports use of Health Class of Device (O) ------------------------------------------------------------------------------- C.1: Mandatory if TSPC_HDP_8_9 is supported, otherwise Optional. C.2: Mandatory if Security Mode 2, 3, or 4 is supported, otherwise Optional. C.3: Mandatory if Bluetooth version 2.1 + EDR is claimed (Not SUM ICS 31/4), otherwise Optional. C.4: Mandatory if TSPC_HDP_8_9 is supported, otherwise Optional. C.5: Mandatory if Bluetooth Core Specification 2.1 + EDR or later (Not SUM ICS 31/4) and TSPC_HDP_8_1 is supported, otherwise Optional if Bluetooth Core Specification 2.1 + EDR or later is supported, otherwise Excluded. C.6: Mandatory if Bluetooth Core specification 2.1 + EDR or later (Not SUM ICS 31/4) is supported, otherwise Excluded. ------------------------------------------------------------------------------- L2CAP Features - Sink ------------------------------------------------------------------------------- Parameter Name Selected Description ------------------------------------------------------------------------------- TSPC_HDP_9_1 True Supports Reliable Control Channel (C.1) TSPC_HDP_9_2 True Uses FCS for Control Channel (M) TSPC_HDP_9_3 True Supports Reliable Data Channel (C.1) TSPC_HDP_9_4 True Can send data using SAR in ERTM (C.2) TSPC_HDP_9_5 True (*) Uses FCS for Reliable Data Channel (O) TSPC_HDP_9_6 True (*) Supports FCS option of "No FCS" for Reliable Data Channel (C.3) TSPC_HDP_9_7 True Supports Streaming Data Channel (C.4) TSPC_HDP_9_8 True Can send data using SAR in SM (C.5) TSPC_HDP_9_9 True (*) Uses FCS for Steaming Data Channel (O) TSPC_HDP_9_10 True (*) Supports FCS option of "No FCS" for Streaming Data Channel (C.3) TSPC_HDP_9_11 True Maximum number of simultaneous Data Channels supported (DCmax) per MCL (M) ------------------------------------------------------------------------------- C.1: Mandatory if TSPC_L2CAP_2_12 is supported, otherwise Excluded. C.2: Mandatory if TSPC_L2CAP_2_22 is supported, otherwise Excluded. C.3: Optional if TSPC_L2CAP_2_14 is supported, otherwise Excluded. C.4: Mandatory if TSPC_L2CAP_2_13 is supported, otherwise Excluded. C.5: Mandatory if TSPC_L2CAP_2_23 is supported, otherwise Excluded. ------------------------------------------------------------------------------- SDP Attributes - Sink ------------------------------------------------------------------------------- Parameter Name Selected Description ------------------------------------------------------------------------------- TSPC_HDP_10_1 True Supports advertisement of HDP Service Record (C.1) TSPC_HDP_10_2 True Service Class ID List (M) TSPC_HDP_10_3 True Protocol Descriptor List (M) TSPC_HDP_10_4 True Bluetooth Profile Descriptor List (M) TSPC_HDP_10_5 True Additional Protocol Descriptor Lists (M) TSPC_HDP_10_6 True (*) Service Name (O) TSPC_HDP_10_7 True (*) Service Description (O) TSPC_HDP_10_8 True (*) Provider Name (O) TSPC_HDP_10_9 True HDP Supported Features (MDEP List) (M) TSPC_HDP_10_10 True MCAP Data Exchange Specification (M) TSPC_HDP_10_11 True MCAP Supported Procedures (M) TSPC_HDP_10_12 True (*) Service Record State (O) ------------------------------------------------------------------------------- C.1: Mandatory to support 10/1 and SDP Server Role (SDP 1b/1). ------------------------------------------------------------------------------- Device Identification - Sink ------------------------------------------------------------------------------- Parameter Name Selected Description ------------------------------------------------------------------------------- TSPC_HDP_11_1 True Device Identification Profile v1.3 or later (M) (C.1) ------------------------------------------------------------------------------- C.1: Mandatory if 1/2 is supported. ------------------------------------------------------------------------------- HDP Features - Sink ------------------------------------------------------------------------------- Parameter Name Selected Description ------------------------------------------------------------------------------- TSPC_HDP_12_1 True Supports Standard Op Codes (M) TSPC_HDP_12_2 True Supports Initiate creation of Control and Data Channels (C.1) (C.5) TSPC_HDP_12_3 True Supports Accept creation of Control and Data Channels (C.1) (C.6) TSPC_HDP_12_4 False Supports Initiate Reconnection of MDL (O) (C.2) TSPC_HDP_12_5 True Supports Accept Reconnection of MDL (M) TSPC_HDP_12_6 False Supports Clock Synchronization Protocol (O) TSPC_HDP_12_7 False Supports Sync-Slave (C.3) TSPC_HDP_12_8 False Supports Sync-Master (C.6) ------------------------------------------------------------------------------- C.1: Mandatory if TSPC_HDP_12_1 is supported, otherwise Excluded. C.2: Optional if TSPC_HDP_12_1 is supported, otherwise Excluded. C.3: Mandatory if TSPC_HDP_12_6 is supported, otherwise Excluded. C.4: Optional if TSPC_HDP_12_6 is supported, otherwise Excluded. C.5: Mandatory to support 12/2 and SDP Client Role (SDP 1b/2). C.6: Mandatory to support 12/3 and SDP Server Role (SDP 1b/1). ------------------------------------------------------------------------------- Data Exchange Features - Sink ------------------------------------------------------------------------------- Parameter Name Selected Description ------------------------------------------------------------------------------- TSPC_HDP_13_1 False Supports Initiation of Echo Test (O) TSPC_HDP_13_2 True Supports Acceptance of Echo Test (M) TSPC_HDP_13_3 True Supports IEEE 11073-20601 (M) TSPC_HDP_13_4 True (*) Supports IEEE 11073-20601 Agent Role (C.1) TSPC_HDP_13_5 False Supports IEEE 11073-20601 Manager Role (C.1) TSPC_HDP_13_6 False Supports Initiation of Association Release (O) ------------------------------------------------------------------------------- C.1: If TSPC_HDP_13_3 is supported, at least one is Mandatory, otherwise Excluded. ------------------------------------------------------------------------------- bluez-5.82/android/PaxHeaders/tester-socket.c0000644000000000000000000000005014015011623016217 xustar0020 atime=1743516864 20 ctime=1743591278 bluez-5.82/android/tester-socket.c0000644000000000000000000003335214015011623015706 0ustar00rootroot// SPDX-License-Identifier: Apache-2.0 /* * Copyright (C) 2014 Intel Corporation * */ #define _GNU_SOURCE #include #include #include #include "emulator/bthost.h" #include "src/shared/tester.h" #include "src/shared/queue.h" #include "tester-main.h" static struct queue *list; /* List of socket test cases */ static bt_bdaddr_t bdaddr_dummy = { .address = { 0x00, 0x11, 0x22, 0x33, 0x44, 0x55} }; static int got_fd_result = -1; static struct bt_action_data btsock_param_socktype_0 = { .addr = &bdaddr_dummy, .sock_type = 0, .channel = 1, .service_uuid = NULL, .service_name = "Test service", .flags = 0, .fd = &got_fd_result, }; static struct bt_action_data btsock_param_socktype_l2cap = { .addr = &bdaddr_dummy, .sock_type = BTSOCK_L2CAP, .channel = 1, .service_uuid = NULL, .service_name = "Test service", .flags = 0, .fd = &got_fd_result, }; static struct bt_action_data btsock_param_channel_0 = { .addr = &bdaddr_dummy, .sock_type = BTSOCK_RFCOMM, .channel = 0, .service_uuid = NULL, .service_name = "Test service", .flags = 0, .fd = &got_fd_result, }; static struct bt_action_data btsock_param = { .addr = &bdaddr_dummy, .sock_type = BTSOCK_RFCOMM, .channel = 1, .service_uuid = NULL, .service_name = "Test service", .flags = 0, .fd = &got_fd_result, }; static struct bt_action_data btsock_param_inv_bdaddr = { .addr = NULL, .sock_type = BTSOCK_RFCOMM, .channel = 1, .service_uuid = NULL, .service_name = "Test service", .flags = 0, .fd = &got_fd_result, }; static bt_bdaddr_t emu_remote_bdaddr_val = { .address = { 0x00, 0xaa, 0x01, 0x01, 0x00, 0x00 }, }; static bt_property_t prop_emu_remote_bdadr = { .type = BT_PROPERTY_BDADDR, .val = &emu_remote_bdaddr_val, .len = sizeof(emu_remote_bdaddr_val), }; static bt_property_t prop_emu_remotes_default_set[] = { { BT_PROPERTY_BDADDR, sizeof(emu_remote_bdaddr_val), &emu_remote_bdaddr_val }, }; static struct bt_action_data btsock_param_emu_bdaddr = { .addr = &emu_remote_bdaddr_val, .sock_type = BTSOCK_RFCOMM, .channel = 1, .service_uuid = NULL, .service_name = "Test service", .flags = 0, .fd = &got_fd_result, }; static struct emu_set_l2cap_data l2cap_setup_data = { .psm = 0x0003, .func = NULL, .user_data = NULL, }; static struct bt_action_data prop_emu_remote_bdaddr_req = { .addr = &emu_remote_bdaddr_val, .prop_type = BT_PROPERTY_BDADDR, .prop = &prop_emu_remote_bdadr, }; static void socket_listen_action(void) { struct test_data *data = tester_get_data(); struct step *current_data_step = queue_peek_head(data->steps); struct bt_action_data *action_data = current_data_step->set_data; struct step *step = g_new0(struct step, 1); *action_data->fd = -1; step->action_status = data->if_sock->listen(action_data->sock_type, action_data->service_name, action_data->service_uuid, action_data->channel, action_data->fd, action_data->flags); schedule_action_verification(step); } static void socket_connect_action(void) { struct test_data *data = tester_get_data(); struct step *current_data_step = queue_peek_head(data->steps); struct bt_action_data *action_data = current_data_step->set_data; struct step *step; int status; *action_data->fd = -1; status = data->if_sock->connect(action_data->addr, action_data->sock_type, action_data->service_uuid, action_data->channel, action_data->fd, action_data->flags); tester_print("status %d sock_fd %d", status, *action_data->fd); if (!status) return; step = g_new0(struct step, 1); step->action_status = status; schedule_action_verification(step); } static gboolean socket_chan_cb(GIOChannel *io, GIOCondition cond, gpointer user_data) { int sock_fd = g_io_channel_unix_get_fd(io); struct step *step = g_new0(struct step, 1); int channel, len; tester_print("%s", __func__); if (cond & G_IO_HUP) { tester_warn("Socket %d hang up", sock_fd); step->action_status = BT_STATUS_FAIL; goto done; } if (cond & (G_IO_ERR | G_IO_NVAL)) { tester_warn("Socket error: sock %d cond %d", sock_fd, cond); step->action_status = BT_STATUS_FAIL; goto done; } len = read(sock_fd, &channel, sizeof(channel)); if (len != sizeof(channel)) { tester_warn("Socket read failed"); step->action_status = BT_STATUS_FAIL; goto done; } tester_print("read correct channel: %d", channel); step->action_status = BT_STATUS_SUCCESS; done: schedule_action_verification(step); return FALSE; } static void socket_read_fd_action(void) { struct test_data *data = tester_get_data(); struct step *current_data_step = queue_peek_head(data->steps); struct bt_action_data *action_data = current_data_step->set_data; GIOChannel *io; io = g_io_channel_unix_new(*action_data->fd); g_io_channel_set_close_on_unref(io, TRUE); g_io_add_watch(io, G_IO_IN | G_IO_HUP | G_IO_ERR | G_IO_NVAL, socket_chan_cb, NULL); g_io_channel_unref(io); } static void socket_verify_fd_action(void) { struct test_data *data = tester_get_data(); struct step *current_data_step = queue_peek_head(data->steps); struct bt_action_data *action_data = current_data_step->set_data; struct step *step = g_new0(struct step, 1); if (!*action_data->fd) { step->action_status = BT_STATUS_FAIL; goto done; } step->action_status = (fcntl(*action_data->fd, F_GETFD) < 0) ? BT_STATUS_FAIL : BT_STATUS_SUCCESS; done: schedule_action_verification(step); } static void socket_verify_channel_action(void) { struct test_data *data = tester_get_data(); struct step *current_data_step = queue_peek_head(data->steps); struct bt_action_data *action_data = current_data_step->set_data; int channel, len; struct step *step = g_new0(struct step, 1); if (!*action_data->fd) { tester_warn("Ups no action_data->fd"); step->action_status = BT_STATUS_FAIL; goto done; } len = read(*action_data->fd, &channel, sizeof(channel)); if (len != sizeof(channel) || channel != action_data->channel) { tester_warn("Ups bad channel"); step->action_status = BT_STATUS_FAIL; goto done; } step->action_status = BT_STATUS_SUCCESS; done: schedule_action_verification(step); } static void socket_close_channel_action(void) { struct test_data *data = tester_get_data(); struct step *current_data_step = queue_peek_head(data->steps); struct bt_action_data *action_data = current_data_step->set_data; struct step *step = g_new0(struct step, 1); if (!*action_data->fd) { tester_warn("Ups no action_data->fd"); step->action_status = BT_STATUS_FAIL; goto done; } close(*action_data->fd); *action_data->fd = -1; step->action_status = BT_STATUS_SUCCESS; done: schedule_action_verification(step); } static struct test_case test_cases[] = { TEST_CASE_BREDRLE("Socket Init", ACTION_SUCCESS(dummy_action, NULL), ), TEST_CASE_BREDRLE("Socket Listen - Invalid: sock_type 0", ACTION_SUCCESS(bluetooth_enable_action, NULL), CALLBACK_STATE(CB_BT_ADAPTER_STATE_CHANGED, BT_STATE_ON), ACTION(BT_STATUS_PARM_INVALID, socket_listen_action, &btsock_param_socktype_0), ACTION_SUCCESS(bluetooth_disable_action, NULL), CALLBACK_STATE(CB_BT_ADAPTER_STATE_CHANGED, BT_STATE_OFF), ), TEST_CASE_BREDRLE("Socket Listen - Invalid: sock_type L2CAP", ACTION_SUCCESS(bluetooth_enable_action, NULL), CALLBACK_STATE(CB_BT_ADAPTER_STATE_CHANGED, BT_STATE_ON), ACTION(BT_STATUS_UNSUPPORTED, socket_listen_action, &btsock_param_socktype_l2cap), ACTION_SUCCESS(bluetooth_disable_action, NULL), CALLBACK_STATE(CB_BT_ADAPTER_STATE_CHANGED, BT_STATE_OFF), ), TEST_CASE_BREDRLE("Socket Listen - Invalid: chan, uuid", ACTION_SUCCESS(bluetooth_enable_action, NULL), CALLBACK_STATE(CB_BT_ADAPTER_STATE_CHANGED, BT_STATE_ON), ACTION(BT_STATUS_PARM_INVALID, socket_listen_action, &btsock_param_channel_0), ACTION_SUCCESS(bluetooth_disable_action, NULL), CALLBACK_STATE(CB_BT_ADAPTER_STATE_CHANGED, BT_STATE_OFF), ), TEST_CASE_BREDRLE("Socket Listen - Check returned fd valid", ACTION_SUCCESS(bluetooth_enable_action, NULL), CALLBACK_STATE(CB_BT_ADAPTER_STATE_CHANGED, BT_STATE_ON), ACTION_SUCCESS(socket_listen_action, &btsock_param), ACTION_SUCCESS(socket_verify_fd_action, &btsock_param), ACTION_SUCCESS(bluetooth_disable_action, NULL), CALLBACK_STATE(CB_BT_ADAPTER_STATE_CHANGED, BT_STATE_OFF), ), TEST_CASE_BREDRLE("Socket Listen - Check returned channel", ACTION_SUCCESS(bluetooth_enable_action, NULL), CALLBACK_STATE(CB_BT_ADAPTER_STATE_CHANGED, BT_STATE_ON), ACTION_SUCCESS(socket_listen_action, &btsock_param), ACTION_SUCCESS(socket_verify_fd_action, &btsock_param), ACTION_SUCCESS(socket_verify_channel_action, &btsock_param), ACTION_SUCCESS(bluetooth_disable_action, NULL), CALLBACK_STATE(CB_BT_ADAPTER_STATE_CHANGED, BT_STATE_OFF), ), TEST_CASE_BREDRLE("Socket Listen - Close and Listen again", ACTION_SUCCESS(bluetooth_enable_action, NULL), CALLBACK_STATE(CB_BT_ADAPTER_STATE_CHANGED, BT_STATE_ON), ACTION_SUCCESS(socket_listen_action, &btsock_param), ACTION_SUCCESS(socket_verify_fd_action, &btsock_param), ACTION_SUCCESS(socket_verify_channel_action, &btsock_param), ACTION_SUCCESS(socket_close_channel_action, &btsock_param), ACTION_SUCCESS(socket_listen_action, &btsock_param), ACTION_SUCCESS(socket_verify_fd_action, &btsock_param), ACTION_SUCCESS(socket_verify_channel_action, &btsock_param), ACTION_SUCCESS(bluetooth_disable_action, NULL), CALLBACK_STATE(CB_BT_ADAPTER_STATE_CHANGED, BT_STATE_OFF), ), TEST_CASE_BREDRLE("Socket Listen - Invalid: double Listen", ACTION_SUCCESS(bluetooth_enable_action, NULL), CALLBACK_STATE(CB_BT_ADAPTER_STATE_CHANGED, BT_STATE_ON), ACTION_SUCCESS(socket_listen_action, &btsock_param), ACTION_SUCCESS(socket_verify_fd_action, &btsock_param), ACTION_SUCCESS(socket_verify_channel_action, &btsock_param), ACTION(BT_STATUS_BUSY, socket_listen_action, &btsock_param), ACTION_SUCCESS(bluetooth_disable_action, NULL), CALLBACK_STATE(CB_BT_ADAPTER_STATE_CHANGED, BT_STATE_OFF), ), TEST_CASE_BREDRLE("Socket Connect - Invalid: sock_type 0", ACTION_SUCCESS(bluetooth_enable_action, NULL), CALLBACK_STATE(CB_BT_ADAPTER_STATE_CHANGED, BT_STATE_ON), ACTION(BT_STATUS_PARM_INVALID, socket_connect_action, &btsock_param_socktype_0), ACTION_SUCCESS(bluetooth_disable_action, NULL), CALLBACK_STATE(CB_BT_ADAPTER_STATE_CHANGED, BT_STATE_OFF), ), TEST_CASE_BREDRLE("Socket Connect - Invalid: sock_type L2CAP", ACTION_SUCCESS(bluetooth_enable_action, NULL), CALLBACK_STATE(CB_BT_ADAPTER_STATE_CHANGED, BT_STATE_ON), ACTION(BT_STATUS_UNSUPPORTED, socket_connect_action, &btsock_param_socktype_l2cap), ACTION_SUCCESS(bluetooth_disable_action, NULL), CALLBACK_STATE(CB_BT_ADAPTER_STATE_CHANGED, BT_STATE_OFF), ), TEST_CASE_BREDRLE("Socket Connect - Invalid: chan, uuid", ACTION_SUCCESS(bluetooth_enable_action, NULL), CALLBACK_STATE(CB_BT_ADAPTER_STATE_CHANGED, BT_STATE_ON), ACTION(BT_STATUS_PARM_INVALID, socket_connect_action, &btsock_param_channel_0), ACTION_SUCCESS(bluetooth_disable_action, NULL), CALLBACK_STATE(CB_BT_ADAPTER_STATE_CHANGED, BT_STATE_OFF), ), TEST_CASE_BREDRLE("Socket Connect - Invalid: bdaddr", ACTION_SUCCESS(bluetooth_enable_action, NULL), CALLBACK_STATE(CB_BT_ADAPTER_STATE_CHANGED, BT_STATE_ON), ACTION(BT_STATUS_PARM_INVALID, socket_connect_action, &btsock_param_inv_bdaddr), ACTION_SUCCESS(bluetooth_disable_action, NULL), CALLBACK_STATE(CB_BT_ADAPTER_STATE_CHANGED, BT_STATE_OFF), ), TEST_CASE_BREDRLE("Socket Connect - Check returned fd valid", ACTION_SUCCESS(set_default_ssp_request_handler, NULL), ACTION_SUCCESS(bluetooth_enable_action, NULL), CALLBACK_STATE(CB_BT_ADAPTER_STATE_CHANGED, BT_STATE_ON), ACTION_SUCCESS(emu_setup_powered_remote_action, NULL), ACTION_SUCCESS(emu_set_ssp_mode_action, NULL), ACTION_SUCCESS(bt_create_bond_action, &prop_emu_remote_bdaddr_req), CALLBACK_BOND_STATE(BT_BOND_STATE_BONDING, &prop_emu_remote_bdadr, 1), CALLBACK_DEVICE_FOUND(prop_emu_remotes_default_set, 1), CALLBACK_BOND_STATE(BT_BOND_STATE_BONDED, &prop_emu_remote_bdadr, 1), CALLBACK_DEVICE_PROPS(NULL, 0), ACTION_SUCCESS(emu_add_l2cap_server_action, &l2cap_setup_data), ACTION_SUCCESS(emu_add_rfcomm_server_action, &btsock_param_emu_bdaddr), ACTION_SUCCESS(socket_connect_action, &btsock_param_emu_bdaddr), ACTION_SUCCESS(socket_verify_fd_action, &btsock_param_emu_bdaddr), ACTION_SUCCESS(bluetooth_disable_action, NULL), CALLBACK_STATE(CB_BT_ADAPTER_STATE_CHANGED, BT_STATE_OFF), ), TEST_CASE_BREDRLE("Socket Connect - Check returned chann", ACTION_SUCCESS(set_default_ssp_request_handler, NULL), ACTION_SUCCESS(bluetooth_enable_action, NULL), CALLBACK_STATE(CB_BT_ADAPTER_STATE_CHANGED, BT_STATE_ON), ACTION_SUCCESS(emu_setup_powered_remote_action, NULL), ACTION_SUCCESS(emu_set_ssp_mode_action, NULL), ACTION_SUCCESS(bt_create_bond_action, &prop_emu_remote_bdaddr_req), CALLBACK_BOND_STATE(BT_BOND_STATE_BONDING, &prop_emu_remote_bdadr, 1), CALLBACK_DEVICE_FOUND(prop_emu_remotes_default_set, 1), CALLBACK_BOND_STATE(BT_BOND_STATE_BONDED, &prop_emu_remote_bdadr, 1), CALLBACK_DEVICE_PROPS(NULL, 0), ACTION_SUCCESS(emu_add_l2cap_server_action, &l2cap_setup_data), ACTION_SUCCESS(emu_add_rfcomm_server_action, &btsock_param_emu_bdaddr), ACTION_SUCCESS(socket_connect_action, &btsock_param_emu_bdaddr), ACTION_SUCCESS(socket_verify_fd_action, &btsock_param_emu_bdaddr), ACTION_SUCCESS(socket_verify_channel_action, &btsock_param_emu_bdaddr), ACTION_SUCCESS(socket_read_fd_action, &btsock_param_emu_bdaddr), ACTION_SUCCESS(bluetooth_disable_action, NULL), CALLBACK_STATE(CB_BT_ADAPTER_STATE_CHANGED, BT_STATE_OFF), ), }; struct queue *get_socket_tests(void) { uint16_t i = 0; list = queue_new(); for (; i < sizeof(test_cases) / sizeof(test_cases[0]); ++i) queue_push_tail(list, &test_cases[i]); return list; } void remove_socket_tests(void) { queue_destroy(list, NULL); } bluez-5.82/android/PaxHeaders/pts-pbap.txt0000644000000000000000000000005012537515745015573 xustar0020 atime=1743516876 20 ctime=1743591289 bluez-5.82/android/pts-pbap.txt0000644000000000000000000000753412537515745015265 0ustar00rootrootPTS test results for PBAP PTS version: 6.1 Tested: 19-May-2015 Android version: 5.1 Results: PASS test passed FAIL test failed INC test is inconclusive N/A test is disabled due to PICS setup ------------------------------------------------------------------------------- Test Name Result Notes ------------------------------------------------------------------------------- TC_PCE_SSM_BV_01_C N/A TC_PCE_SSM_BV_02_C N/A TC_PCE_SSM_BV_06_C N/A TC_PCE_SSM_BV_08_C N/A TC_PCE_SSM_BI_01_C N/A TC_PCE_SSM_BV_09_C N/A TC_PCE_SSM_BV_10_C N/A TC_PCE_PBD_BV_01_C N/A TC_PCE_PBD_BV_04_C N/A TC_PCE_PBD_BV_38_C N/A TC_PCE_PBD_BV_29_C N/A TC_PCE_PBD_BV_40_C N/A TC_PCE_PBD_BV_41_C N/A TC_PCE_PBD_BV_42_C N/A TC_PCE_PBD_BV_43_C N/A TC_PCE_PBD_BV_44_C N/A TC_PCE_PBD_BV_45_C N/A TC_PCE_PBD_BV_46_C N/A TC_PCE_PBD_BV_47_C N/A TC_PCE_PBD_BV_48_C N/A TC_PCE_PBB_BV_01_C N/A TC_PCE_PBB_BV_02_C N/A TC_PCE_PBB_BV_03_C N/A TC_PCE_PBB_BV_05_C N/A TC_PCE_PBB_BV_39_C N/A TC_PCE_PBB_BV_40_C N/A TC_PCE_PBB_BV_41_C N/A TC_PCE_PBB_BV_42_C N/A TC_PCE_PBB_BV_33_C N/A TC_PCE_PBB_BV_34_C N/A TC_PCE_PBB_BV_35_C N/A TC_PCE_PBB_BV_36_C N/A TC_PCE_PBB_BV_43_C N/A TC_PCE_PBB_BV_37_C N/A TC_PCE_PBB_BV_38_C N/A TC_PCE_PBF_BV_01_I N/A TC_PCE_PBF_BV_02_I N/A TC_PCE_PBF_BV_03_I N/A TC_PCE_PDF_BV_01_I N/A TC_PCE_PDF_BV_06_I N/A TC_PSE_SSM_BV_03_C PASS Tester must accept obex request TC_PSE_SSM_BV_05_C PASS TC_PSE_SSM_BV_07_C PASS Tester must accept obex request with TSPX_auth_password set in PIXITs TC_PSE_SSM_BI_02_C PASS TC_PSE_SSM_BI_03_C N/A TC_PSE_SSM_BV_08_I PASS Tester must compare passkey on IUT and PTS TC_PSE_SSM_BV_11_C N/A TC_PSE_PBD_BV_02_C PASS Tester must compare phone book size with the value given by PTS TC_PSE_PBD_BV_03_C PASS Tester must compare phone book size with the value given by PTS TC_PSE_PBD_BV_05_C N/A TC_PSE_PBD_BI_01_C PASS TC_PSE_PBD_BV_06_C N/A TC_PSE_PBD_BV_07_C N/A TC_PSE_PBD_BV_08_C N/A TC_PSE_PBD_BV_09_C N/A TC_PSE_PBD_BV_10_C N/A TC_PSE_PBD_BV_17_C PASS TC_PSE_PBD_BV_18_C N/A TC_PSE_PBD_BV_19_C N/A TC_PSE_PBD_BV_20_C N/A TC_PSE_PBD_BV_21_C N/A TC_PSE_PBD_BV_22_C N/A TC_PSE_PBD_BV_23_C N/A TC_PSE_PBD_BV_24_C N/A TC_PSE_PBD_BV_25_C N/A TC_PSE_PBD_BV_26_C N/A TC_PSE_PBD_BV_27_C N/A TC_PSE_PBD_BV_28_C N/A TC_PSE_PBD_BV_29_C N/A TC_PSE_PBD_BV_30_C N/A TC_PSE_PBD_BV_31_C N/A TC_PSE_PBD_BV_32_C N/A TC_PSE_PBD_BV_33_C N/A TC_PSE_PBD_BV_34_C N/A TC_PSE_PBD_BV_35_C N/A TC_PSE_PBD_BV_36_C PASS TC_PSE_PBD_BV_37_C N/A TC_PSE_PBB_BV_06_C PASS TC_PSE_PBB_BV_07_C PASS TC_PSE_PBB_BV_08_C PASS Tester must compare phone book size with the value given by PTS TC_PSE_PBB_BV_09_C PASS TC_PSE_PBB_BV_10_C PASS Tester must verify vcard content received by PTS TC_PSE_PBB_BV_11_C PASS Tester must verify number of new missed calls with value given by PTS TC_PSE_PBB_BI_01_C PASS TC_PSE_PBB_BI_07_C PASS TC_PSE_PBB_BV_12_C PASS TC_PSE_PBB_BV_13_C N/A TC_PSE_PBB_BV_14_C N/A TC_PSE_PBB_BV_15_C N/A TC_PSE_PBB_BV_16_C N/A TC_PSE_PBB_BV_17_C N/A TC_PSE_PBB_BV_18_C N/A TC_PSE_PBB_BV_19_C N/A TC_PSE_PBB_BV_20_C N/A TC_PSE_PBB_BV_21_C N/A TC_PSE_PBB_BV_22_C N/A TC_PSE_PBB_BV_23_C N/A TC_PSE_PBB_BV_24_C N/A TC_PSE_PBB_BV_25_C N/A TC_PSE_PBB_BV_26_C N/A TC_PSE_PBB_BV_27_C N/A TC_PSE_PBB_BV_44_C N/A TC_PSE_PBB_BV_45_C N/A TC_PSE_PBB_BV_46_C N/A TC_PSE_PBB_BV_28_C N/A TC_PSE_PBB_BV_29_C N/A TC_PSE_PBB_BV_30_C N/A TC_PSE_PBB_BV_31_C PASS Requires entries in IUT's /och and /ich folders TC_PSE_PBB_BV_32_C N/A TC_PSE_PBF_BV_01_I PASS TC_PSE_PBF_BV_02_I PASS Tester must verify vcard content received by PTS TC_PSE_PBF_BV_03_I PASS TC_PSE_PDF_BV_01_I PASS Tester must compare phone book size with the value given by PTS TC_PSE_PDF_BV_06_I PASS TC_PSE_BC_BV_03_I N/A TC_PSE_CON_BV_02_I N/A TC_PSE_ROB_BV_01_I N/A TC_PSE_SRM_BI_03_I N/A TC_PSE_SRM_BI_05_I N/A TC_PSE_SRM_BV_08_I N/A TC_PSE_SRMP_BI_02_I N/A TC_PSE_SRMP_BV_02_I N/A ------------------------------------------------------------------------------- bluez-5.82/android/PaxHeaders/ipc-common.h0000644000000000000000000000005014015011623015471 xustar0020 atime=1743516862 20 ctime=1743591276 bluez-5.82/android/ipc-common.h0000644000000000000000000000067714015011623015164 0ustar00rootroot/* SPDX-License-Identifier: LGPL-2.1-or-later */ /* * * BlueZ - Bluetooth protocol stack for Linux * * Copyright (C) 2014 Intel Corporation. All rights reserved. * * */ #define IPC_MTU 1024 #define IPC_STATUS_SUCCESS 0x00 struct ipc_hdr { uint8_t service_id; uint8_t opcode; uint16_t len; uint8_t payload[0]; } __attribute__((packed)); #define IPC_OP_STATUS 0x00 struct ipc_status { uint8_t code; } __attribute__((packed)); bluez-5.82/android/PaxHeaders/cts.txt0000644000000000000000000000005012442567731014633 xustar0020 atime=1743516875 20 ctime=1743591288 bluez-5.82/android/cts.txt0000644000000000000000000000362112442567731014316 0ustar00rootrootAndroid Compatibility Test Suite results Tested: 27-Nov-2014 Android version: 5.0 Kernel Version: 3.18 CTS version: 5.0 R1 (android-5.0.0_r7) Note: CTS reliable write GATT tests require using CTS from master branch or https://android-review.googlesource.com/99354 applied. (*) Tests are disabled due to CTS quality issues. Link for reference: https://android.googlesource.com/platform/cts/+/0a62e4a0a9910101ccf2ccc43f6%5E!/ ------------------------------------------------------------------------------- android.bluetooth.cts.BasicAdapterTest (automated tests) Test Name Result Notes ------------------------------------------------------------------------------- testAndroidTestCaseSetupProperly PASS test_checkBluetoothAddress PASS test_enableDisable PASS test_getAddress PASS test_getBondedDevices PASS test_getDefaultAdapter PASS test_getName PASS test_getRemoteDevice PASS test_listenUsingRfcommWithServiceRecord PASS ------------------------------------------------------------------------------- ------------------------------------------------------------------------------- com.android.cts.verifier (manual tests) Test Name Result Notes ------------------------------------------------------------------------------- Toggle Bluetooth PASS BLE Client Test: connect N/A (*) discover service N/A (*) read/write characteristic N/A (*) reliable write N/A (*) notify characteristic N/A (*) read/write descriptor N/A (*) read RSSI N/A (*) disconnect N/A (*) BLE Server Test: add service N/A (*) connection N/A (*) read characteristic request N/A (*) write characteristic request N/A (*) read descriptor request N/A (*) write descriptor request N/A (*) reliable write N/A (*) disconnection N/A (*) Insecure Client PASS Insecure Server PASS Secure Client PASS Secure Server PASS ------------------------------------------------------------------------------- bluez-5.82/android/PaxHeaders/hal-socket.c0000644000000000000000000000005014015011623015455 xustar0020 atime=1743516862 20 ctime=1743591276 bluez-5.82/android/hal-socket.c0000644000000000000000000000354514015011623015145 0ustar00rootroot// SPDX-License-Identifier: Apache-2.0 /* * Copyright (C) 2013 Intel Corporation * */ #define _GNU_SOURCE #include #include #include #include #include "hal-ipc.h" #include "hal-log.h" #include "hal-msg.h" #include "hal-utils.h" #include "hal.h" static bt_status_t socket_listen(btsock_type_t type, const char *service_name, const uint8_t *uuid, int chan, int *sock, int flags) { struct hal_cmd_socket_listen cmd; if (!sock) return BT_STATUS_PARM_INVALID; DBG("uuid %s chan %d sock %p type %d service_name %s flags 0x%02x", btuuid2str(uuid), chan, sock, type, service_name, flags); memset(&cmd, 0, sizeof(cmd)); /* type match IPC type */ cmd.type = type; cmd.flags = flags; cmd.channel = chan; if (uuid) memcpy(cmd.uuid, uuid, sizeof(cmd.uuid)); if (service_name) memcpy(cmd.name, service_name, strlen(service_name)); return hal_ipc_cmd(HAL_SERVICE_ID_SOCKET, HAL_OP_SOCKET_LISTEN, sizeof(cmd), &cmd, NULL, NULL, sock); } static bt_status_t socket_connect(const bt_bdaddr_t *bdaddr, btsock_type_t type, const uint8_t *uuid, int chan, int *sock, int flags) { struct hal_cmd_socket_connect cmd; if (!sock) return BT_STATUS_PARM_INVALID; DBG("bdaddr %s uuid %s chan %d sock %p type %d flags 0x%02x", bdaddr2str(bdaddr), btuuid2str(uuid), chan, sock, type, flags); memset(&cmd, 0, sizeof(cmd)); /* type match IPC type */ cmd.type = type; cmd.flags = flags; cmd.channel = chan; if (uuid) memcpy(cmd.uuid, uuid, sizeof(cmd.uuid)); if (bdaddr) memcpy(cmd.bdaddr, bdaddr, sizeof(cmd.bdaddr)); return hal_ipc_cmd(HAL_SERVICE_ID_SOCKET, HAL_OP_SOCKET_CONNECT, sizeof(cmd), &cmd, NULL, NULL, sock); } static btsock_interface_t socket_if = { sizeof(socket_if), socket_listen, socket_connect }; btsock_interface_t *bt_get_socket_interface(void) { return &socket_if; } bluez-5.82/android/PaxHeaders/pts-mps.txt0000644000000000000000000000005012537515745015450 xustar0020 atime=1743516876 20 ctime=1743591289 bluez-5.82/android/pts-mps.txt0000644000000000000000000000404512537515745015134 0ustar00rootrootPTS test results for MPS PTS version: 6.1 Tested: 20-May-2015 Android version: 5.1 Results: PASS test passed FAIL test failed INC test is inconclusive N/A test is disabled due to PICS setup NONE test result is none Note: Do not use AOSP Music player. Use e.g. MortPlayer or Poweramp. For full tests conformance, "Touch sounds" on IUT should be disabled (Settings > Sound¬ification > Other sounds) ------------------------------------------------------------------------------- Test Name Result Notes ------------------------------------------------------------------------------- TC_AG_PSE_HFPB_CTH_SD_BV_01_I PASS TC_AG_SRC_HFAV_ACT_SD_BV_01_I PASS TC_AG_SRC_HFAV_ACT_SD_BV_02_I PASS TC_AG_SRC_HFAV_ACT_SD_BV_03_I PASS TC_AG_SRC_HFAV_CLH_SD_BV_01_I PASS TC_AG_SRC_HFAV_CLH_SD_BV_02_I N/A TC_AG_SRC_HFAV_CLH_SD_BV_03_I PASS TC_AG_SRC_HFAV_CLH_SD_BV_04_I PASS TC_AG_SRC_HFAV_CLH_SD_BV_05_I PASS TC_AG_SRC_HFAV_CLH_SD_BV_06_I PASS PTS issue #13466 TC_AVP_CTH_SD_BI_01_I PASS TC_AVP_CTH_SD_BI_02_I PASS TC_HF_SNK_HFAV_ACT_SD_BV_01_I N/A TC_HF_SNK_HFAV_ACT_SD_BV_02_I N/A TC_HF_SNK_HFAV_ACT_SD_BV_03_I N/A TC_HF_SNK_HFAV_CLH_SD_BV_01_I N/A TC_HF_SNK_HFAV_CLH_SD_BV_02_I N/A TC_HF_SNK_HFAV_CLH_SD_BV_03_I N/A TC_HF_SNK_HFAV_CLH_SD_BV_04_I N/A TC_HF_SNK_HFAV_CLH_SD_BV_05_I N/A TC_HF_SNK_HFAV_CLH_SD_BV_06_I N/A TC_HF_SNK_CT_HFAV_ACT_MD_BV_01_I N/A TC_HF_SNK_CT_HFAV_CLH_MD_BV_01_I N/A TC_HF_SNK_CT_HFAV_CLH_MD_BV_02_I N/A TC_HF_SNK_CT_HFAV_CLH_MD_BV_03_I N/A TC_HF_SNK_CT_HFAV_CLH_MD_BV_04_I N/A TC_HF_SNK_CT_HFAV_CLH_MD_BV_05_I N/A TC_HF_SNK_CT_HFAV_CLH_MD_BV_06_I N/A TC_SDP_CTH_SD_BV_01_I PASS TC_SNK_PCE_AVO_ACT_SD_BV_01_I N/A TC_SRC_PSE_AVO_ACT_SD_BV_01_I PASS TC_SRC_TG_HFAV_ACT_MD_BV_01_I PASS TC_SRC_TG_HFAV_CLH_MD_BV_01_I PASS TC_SRC_TG_HFAV_CLH_MD_BV_02_I PASS TC_SRC_TG_HFAV_CLH_MD_BV_03_I PASS TC_SRC_TG_HFAV_CLH_MD_BV_04_I PASS TC_SRC_TG_HFAV_CLH_MD_BV_05_I PASS TC_SRC_TG_HFAV_CLH_MD_BV_06_I PASS TC_PAIRING_HF_SNK_CT N/A Pairing helper for MD tests ------------------------------------------------------------------------------- bluez-5.82/android/PaxHeaders/tester-pan.c0000644000000000000000000000005014015011623015505 xustar0020 atime=1743516864 20 ctime=1743591278 bluez-5.82/android/tester-pan.c0000644000000000000000000001610414015011623015170 0ustar00rootroot// SPDX-License-Identifier: Apache-2.0 /* * Copyright (C) 2014 Intel Corporation * */ #define _GNU_SOURCE #include #include "emulator/bthost.h" #include "lib/bluetooth.h" #include "android/utils.h" #include "src/shared/tester.h" #include "src/shared/queue.h" #include "tester-main.h" static struct queue *list; /* List of pan test cases */ #define pan_conn_req_pdu 0x01, 0x01, 0x02, 0x11, 0x16, 0x11, 0x15 #define pan_conn_rsp_pdu 0x01, 0x02, 0x00, 0x00 static const struct pdu_set pdus[] = { { raw_pdu(pan_conn_req_pdu), raw_pdu(pan_conn_rsp_pdu) }, { end_pdu, end_pdu }, }; static struct emu_l2cap_cid_data cid_data = { .pdu = pdus, }; static struct emu_set_l2cap_data l2cap_setup_data = { .psm = 15, .func = tester_generic_connect_cb, .user_data = &cid_data, }; static void pan_connect_action(void) { struct test_data *data = tester_get_data(); const uint8_t *pan_addr = hciemu_get_client_bdaddr(data->hciemu); struct step *step = g_new0(struct step, 1); bt_bdaddr_t bdaddr; bdaddr2android((const bdaddr_t *) pan_addr, &bdaddr); step->action_status = data->if_pan->connect(&bdaddr, BTPAN_ROLE_PANU, BTPAN_ROLE_PANNAP); schedule_action_verification(step); } static void pan_disconnect_action(void) { struct test_data *data = tester_get_data(); const uint8_t *pan_addr = hciemu_get_client_bdaddr(data->hciemu); struct step *step = g_new0(struct step, 1); bt_bdaddr_t bdaddr; bdaddr2android((const bdaddr_t *) pan_addr, &bdaddr); step->action_status = data->if_pan->disconnect(&bdaddr); schedule_action_verification(step); } static void pan_get_local_role_action(void) { struct test_data *data = tester_get_data(); const uint8_t *pan_addr = hciemu_get_client_bdaddr(data->hciemu); struct step *step = g_new0(struct step, 1); bt_bdaddr_t bdaddr; int role; bdaddr2android((const bdaddr_t *) pan_addr, &bdaddr); role = data->if_pan->get_local_role(); if (role == BTPAN_ROLE_PANU) step->action_status = BT_STATUS_SUCCESS; else step->action_status = BT_STATUS_FAIL; schedule_action_verification(step); } static void pan_enable_nap_action(void) { struct test_data *data = tester_get_data(); struct step *step = g_new0(struct step, 1); step->action_status = data->if_pan->enable(BTPAN_ROLE_PANNAP); schedule_action_verification(step); } static void pan_enable_panu_action(void) { struct test_data *data = tester_get_data(); struct step *step = g_new0(struct step, 1); step->action_status = data->if_pan->enable(BTPAN_ROLE_PANU); schedule_action_verification(step); } static void pan_enable_none_action(void) { struct test_data *data = tester_get_data(); struct step *step = g_new0(struct step, 1); step->action_status = data->if_pan->enable(BTPAN_ROLE_NONE); schedule_action_verification(step); } static struct test_case test_cases[] = { TEST_CASE_BREDRLE("PAN Init", ACTION_SUCCESS(dummy_action, NULL), ), TEST_CASE_BREDRLE("PAN Connect - Success", ACTION_SUCCESS(set_default_ssp_request_handler, NULL), ACTION_SUCCESS(bluetooth_enable_action, NULL), CALLBACK_STATE(CB_BT_ADAPTER_STATE_CHANGED, BT_STATE_ON), ACTION_SUCCESS(emu_setup_powered_remote_action, NULL), ACTION_SUCCESS(emu_set_ssp_mode_action, NULL), ACTION_SUCCESS(emu_add_l2cap_server_action, &l2cap_setup_data), ACTION_SUCCESS(pan_connect_action, NULL), CALLBACK_PAN_CONN_STATE(CB_PAN_CONNECTION_STATE, BT_STATUS_SUCCESS, BTPAN_STATE_CONNECTING, BTPAN_ROLE_PANU, BTPAN_ROLE_PANNAP), CALLBACK_PAN_CTRL_STATE(CB_PAN_CONTROL_STATE, BT_STATUS_SUCCESS, BTPAN_STATE_ENABLED, BTPAN_ROLE_PANU), CALLBACK_PAN_CONN_STATE(CB_PAN_CONNECTION_STATE, BT_STATUS_SUCCESS, BTPAN_STATE_CONNECTED, BTPAN_ROLE_PANU, BTPAN_ROLE_PANNAP), ACTION_SUCCESS(bluetooth_disable_action, NULL), CALLBACK_PAN_CONN_STATE(CB_PAN_CONNECTION_STATE, BT_STATUS_SUCCESS, BTPAN_STATE_DISCONNECTED, BTPAN_ROLE_PANU, BTPAN_ROLE_PANNAP), CALLBACK_STATE(CB_BT_ADAPTER_STATE_CHANGED, BT_STATE_OFF), ), TEST_CASE_BREDRLE("PAN Disconnect - Success", ACTION_SUCCESS(set_default_ssp_request_handler, NULL), ACTION_SUCCESS(bluetooth_enable_action, NULL), CALLBACK_STATE(CB_BT_ADAPTER_STATE_CHANGED, BT_STATE_ON), ACTION_SUCCESS(emu_setup_powered_remote_action, NULL), ACTION_SUCCESS(emu_set_ssp_mode_action, NULL), ACTION_SUCCESS(emu_add_l2cap_server_action, &l2cap_setup_data), ACTION_SUCCESS(pan_connect_action, NULL), CALLBACK_PAN_CONN_STATE(CB_PAN_CONNECTION_STATE, BT_STATUS_SUCCESS, BTPAN_STATE_CONNECTING, BTPAN_ROLE_PANU, BTPAN_ROLE_PANNAP), CALLBACK_PAN_CTRL_STATE(CB_PAN_CONTROL_STATE, BT_STATUS_SUCCESS, BTPAN_STATE_ENABLED, BTPAN_ROLE_PANU), CALLBACK_PAN_CONN_STATE(CB_PAN_CONNECTION_STATE, BT_STATUS_SUCCESS, BTPAN_STATE_CONNECTED, BTPAN_ROLE_PANU, BTPAN_ROLE_PANNAP), ACTION_SUCCESS(pan_disconnect_action, NULL), CALLBACK_PAN_CONN_STATE(CB_PAN_CONNECTION_STATE, BT_STATUS_SUCCESS, BTPAN_STATE_DISCONNECTED, BTPAN_ROLE_PANU, BTPAN_ROLE_PANNAP), ACTION_SUCCESS(bluetooth_disable_action, NULL), CALLBACK_STATE(CB_BT_ADAPTER_STATE_CHANGED, BT_STATE_OFF), ), TEST_CASE_BREDRLE("PAN GetLocalRole - Success", ACTION_SUCCESS(set_default_ssp_request_handler, NULL), ACTION_SUCCESS(bluetooth_enable_action, NULL), CALLBACK_STATE(CB_BT_ADAPTER_STATE_CHANGED, BT_STATE_ON), ACTION_SUCCESS(emu_setup_powered_remote_action, NULL), ACTION_SUCCESS(emu_set_ssp_mode_action, NULL), ACTION_SUCCESS(emu_add_l2cap_server_action, &l2cap_setup_data), ACTION_SUCCESS(pan_connect_action, NULL), CALLBACK_PAN_CONN_STATE(CB_PAN_CONNECTION_STATE, BT_STATUS_SUCCESS, BTPAN_STATE_CONNECTING, BTPAN_ROLE_PANU, BTPAN_ROLE_PANNAP), CALLBACK_PAN_CTRL_STATE(CB_PAN_CONTROL_STATE, BT_STATUS_SUCCESS, BTPAN_STATE_ENABLED, BTPAN_ROLE_PANU), CALLBACK_PAN_CONN_STATE(CB_PAN_CONNECTION_STATE, BT_STATUS_SUCCESS, BTPAN_STATE_CONNECTED, BTPAN_ROLE_PANU, BTPAN_ROLE_PANNAP), ACTION_SUCCESS(pan_get_local_role_action, NULL), ACTION_SUCCESS(bluetooth_disable_action, NULL), CALLBACK_PAN_CONN_STATE(CB_PAN_CONNECTION_STATE, BT_STATUS_SUCCESS, BTPAN_STATE_DISCONNECTED, BTPAN_ROLE_PANU, BTPAN_ROLE_PANNAP), CALLBACK_STATE(CB_BT_ADAPTER_STATE_CHANGED, BT_STATE_OFF), ), TEST_CASE_BREDRLE("PAN Enable NAP - Success", ACTION_SUCCESS(pan_enable_nap_action, NULL), CALLBACK_PAN_CTRL_STATE(CB_PAN_CONTROL_STATE, BT_STATUS_SUCCESS, BTPAN_STATE_ENABLED, BTPAN_ROLE_PANNAP), ), TEST_CASE_BREDRLE("PAN Enable PANU - Success", ACTION(BT_STATUS_UNSUPPORTED, pan_enable_panu_action, NULL), ), TEST_CASE_BREDRLE("PAN Enable NONE - Success", ACTION_SUCCESS(pan_enable_nap_action, NULL), CALLBACK_PAN_CTRL_STATE(CB_PAN_CONTROL_STATE, BT_STATUS_SUCCESS, BTPAN_STATE_ENABLED, BTPAN_ROLE_PANNAP), ACTION_SUCCESS(pan_enable_none_action, NULL), CALLBACK_PAN_CTRL_STATE(CB_PAN_CONTROL_STATE, BT_STATUS_SUCCESS, BTPAN_STATE_DISABLED, BTPAN_ROLE_NONE), ), }; struct queue *get_pan_tests(void) { uint16_t i = 0; list = queue_new(); for (; i < sizeof(test_cases) / sizeof(test_cases[0]); ++i) queue_push_tail(list, &test_cases[i]); return list; } void remove_pan_tests(void) { queue_destroy(list, NULL); } bluez-5.82/android/PaxHeaders/tester-main.h0000644000000000000000000000005014015011623015660 xustar0020 atime=1743516864 20 ctime=1743591278 bluez-5.82/android/tester-main.h0000644000000000000000000005327714015011623015357 0ustar00rootroot/* SPDX-License-Identifier: LGPL-2.1-or-later */ /* * * BlueZ - Bluetooth protocol stack for Linux * * Copyright (C) 2014 Intel Corporation. All rights reserved. * * */ #include #include #include #include #include #include #include #include #include #include #include "emulator/hciemu.h" #include struct pdu_set { struct iovec req; struct iovec rsp; }; #define raw_data(args...) ((unsigned char[]) { args }) #define raw_pdu(args...) \ { \ .iov_base = raw_data(args), \ .iov_len = sizeof(raw_data(args)), \ } #define end_pdu { .iov_base = NULL } #define TEST_CASE_BREDR(text, ...) { \ HCIEMU_TYPE_BREDR, \ text, \ sizeof((struct step[]) {__VA_ARGS__}) / sizeof(struct step), \ (struct step[]) {__VA_ARGS__}, \ } #define TEST_CASE_BREDRLE(text, ...) { \ HCIEMU_TYPE_BREDRLE, \ text, \ sizeof((struct step[]) {__VA_ARGS__}) / sizeof(struct step), \ (struct step[]) {__VA_ARGS__}, \ } #define MODIFY_DATA(status, modif_fun, from, to, len) { \ .action_status = status, \ .action = modif_fun, \ .set_data = from, \ .set_data_2 = to, \ .set_data_len = len, \ } #define PROCESS_DATA(status, proc_fun, data1, data2, data3) { \ .action_status = status, \ .action = proc_fun, \ .set_data = data1, \ .set_data_2 = data2, \ .set_data_3 = data3, \ } #define ACTION(status, act_fun, data_set) { \ .action_status = status, \ .action = act_fun, \ .set_data = data_set, \ } #define ACTION_FAIL(act_fun, data_set) \ ACTION(BT_STATUS_FAIL, act_fun, data_set) #define ACTION_SUCCESS(act_fun, data_set) \ ACTION(BT_STATUS_SUCCESS, act_fun, data_set) #define CALLBACK(cb) { \ .callback = cb, \ } #define CALLBACK_STATE(cb, cb_res) { \ .callback = cb, \ .callback_result.state = cb_res, \ } #define CALLBACK_STATUS(cb, cb_res) { \ .callback = cb, \ .callback_result.status = cb_res, \ } #define CALLBACK_ERROR(cb, cb_err) { \ .callback = cb, \ .callback_result.error = cb_err, \ } #define CALLBACK_ADAPTER_PROPS(props, prop_cnt) { \ .callback = CB_BT_ADAPTER_PROPERTIES, \ .callback_result.properties = props, \ .callback_result.num_properties = prop_cnt, \ } #define CALLBACK_PROPS(cb, props, prop_cnt) { \ .callback = cb, \ .callback_result.properties = props, \ .callback_result.num_properties = prop_cnt, \ } #define CALLBACK_HH_MODE(cb, cb_res, cb_mode) { \ .callback = cb, \ .callback_result.status = cb_res, \ .callback_result.mode = cb_mode, \ } #define CALLBACK_HHREPORT(cb, cb_res, cb_rep_size) { \ .callback = cb, \ .callback_result.status = cb_res, \ .callback_result.report_size = cb_rep_size, \ } #define CLLBACK_GATTC_SCAN_RES(props, prop_cnt, cb_adv_data) {\ .callback = CB_GATTC_SCAN_RESULT, \ .callback_result.properties = props, \ .callback_result.num_properties = prop_cnt, \ .callback_result.adv_data = cb_adv_data, \ } #define CALLBACK_GATTC_CONNECT(cb_res, cb_prop, cb_conn_id, cb_client_id) { \ .callback = CB_GATTC_OPEN, \ .callback_result.status = cb_res, \ .callback_result.properties = cb_prop, \ .callback_result.num_properties = 1, \ .callback_result.conn_id = cb_conn_id, \ .callback_result.gatt_app_id = cb_client_id, \ } #define CALLBACK_GATTC_SEARCH_RESULT(cb_conn_id, cb_service) { \ .callback = CB_GATTC_SEARCH_RESULT, \ .callback_result.conn_id = cb_conn_id, \ .callback_result.service = cb_service \ } #define CALLBACK_GATTC_SEARCH_COMPLETE(cb_res, cb_conn_id) { \ .callback = CB_GATTC_SEARCH_COMPLETE, \ .callback_result.conn_id = cb_conn_id \ } #define CALLBACK_GATTC_GET_CHARACTERISTIC_CB(cb_res, cb_conn_id, cb_service, \ cb_char, cb_char_prop) { \ .callback = CB_GATTC_GET_CHARACTERISTIC, \ .callback_result.conn_id = cb_conn_id, \ .callback_result.status = cb_res, \ .callback_result.service = cb_service, \ .callback_result.characteristic = cb_char, \ .callback_result.char_prop = cb_char_prop \ } #define CALLBACK_GATTC_GET_DESCRIPTOR(cb_res, cb_conn_id, cb_service, \ cb_char, cb_desc) { \ .callback = CB_GATTC_GET_DESCRIPTOR, \ .callback_result.conn_id = cb_conn_id, \ .callback_result.status = cb_res, \ .callback_result.service = cb_service, \ .callback_result.characteristic = cb_char, \ .callback_result.descriptor = cb_desc \ } #define CALLBACK_GATTC_GET_INCLUDED(cb_res, cb_conn_id, cb_service, \ cb_incl) { \ .callback = CB_GATTC_GET_INCLUDED_SERVICE, \ .callback_result.conn_id = cb_conn_id, \ .callback_result.status = cb_res, \ .callback_result.service = cb_service, \ .callback_result.included = cb_incl, \ } #define CALLBACK_GATTC_READ_CHARACTERISTIC(cb_res, cb_conn_id, cb_read_data) { \ .callback = CB_GATTC_READ_CHARACTERISTIC, \ .callback_result.conn_id = cb_conn_id, \ .callback_result.status = cb_res, \ .callback_result.read_params = cb_read_data, \ } #define CALLBACK_GATTC_READ_DESCRIPTOR(cb_res, cb_conn_id, cb_read_data) { \ .callback = CB_GATTC_READ_DESCRIPTOR, \ .callback_result.conn_id = cb_conn_id, \ .callback_result.status = cb_res, \ .callback_result.read_params = cb_read_data, \ } #define CALLBACK_GATTC_WRITE_DESCRIPTOR(cb_res, cb_conn_id, cb_write_data) { \ .callback = CB_GATTC_WRITE_DESCRIPTOR, \ .callback_result.conn_id = cb_conn_id, \ .callback_result.status = cb_res, \ .callback_result.write_params = cb_write_data, \ } #define CALLBACK_GATTC_WRITE_CHARACTERISTIC(cb_res, cb_conn_id, \ cb_write_data) { \ .callback = CB_GATTC_WRITE_CHARACTERISTIC, \ .callback_result.conn_id = cb_conn_id, \ .callback_result.status = cb_res, \ .callback_result.write_params = cb_write_data, \ } #define CALLBACK_GATTC_REGISTER_FOR_NOTIF(cb_res, cb_conn_id, cb_char,\ cb_service, cb_registered) { \ .callback = CB_GATTC_REGISTER_FOR_NOTIFICATION, \ .callback_result.conn_id = cb_conn_id, \ .callback_result.status = cb_res, \ .callback_result.service = cb_service, \ .callback_result.characteristic = cb_char, \ .callback_result.notification_registered = cb_registered \ } #define CALLBACK_GATTC_NOTIFY(cb_conn_id, cb_notify) { \ .callback = CB_GATTC_NOTIFY, \ .callback_result.conn_id = cb_conn_id, \ .callback_result.notify_params = cb_notify \ } #define CALLBACK_GATTC_DISCONNECT(cb_res, cb_prop, cb_conn_id, cb_client_id) { \ .callback = CB_GATTC_CLOSE, \ .callback_result.status = cb_res, \ .callback_result.properties = cb_prop, \ .callback_result.num_properties = 1, \ .callback_result.conn_id = cb_conn_id, \ .callback_result.gatt_app_id = cb_client_id, \ } #define CALLBACK_GATTS_CONNECTION(cb_res, cb_prop, cb_conn_id, cb_server_id) { \ .callback = CB_GATTS_CONNECTION, \ .callback_result.connected = cb_res, \ .callback_result.properties = cb_prop, \ .callback_result.num_properties = 1, \ .callback_result.conn_id = cb_conn_id, \ .callback_result.gatt_app_id = cb_server_id, \ } #define CALLBACK_GATTS_NOTIF_CONF(cb_conn_id, cb_status) { \ .callback = CB_GATTS_INDICATION_SEND, \ .callback_result.conn_id = cb_conn_id, \ .callback_result.status = cb_status, \ } #define CALLBACK_GATTS_SERVICE_ADDED(cb_res, cb_server_id, cb_service, \ cb_srvc_handle, \ cb_store_srvc_handle) { \ .callback = CB_GATTS_SERVICE_ADDED, \ .callback_result.status = cb_res, \ .callback_result.gatt_app_id = cb_server_id, \ .callback_result.service = cb_service, \ .callback_result.srvc_handle = cb_srvc_handle, \ .store_srvc_handle = cb_store_srvc_handle, \ } #define CALLBACK_GATTS_INC_SERVICE_ADDED(cb_res, cb_server_id, cb_srvc_handle, \ cb_inc_srvc_handle) { \ .callback = CB_GATTS_INCLUDED_SERVICE_ADDED, \ .callback_result.status = cb_res, \ .callback_result.gatt_app_id = cb_server_id, \ .callback_result.srvc_handle = cb_srvc_handle, \ .callback_result.inc_srvc_handle = cb_inc_srvc_handle, \ } #define CALLBACK_GATTS_CHARACTERISTIC_ADDED(cb_res, cb_server_id, cb_uuid, \ cb_srvc_handle, \ cb_char_handle, \ cb_store_char_handle) { \ .callback = CB_GATTS_CHARACTERISTIC_ADDED, \ .callback_result.status = cb_res, \ .callback_result.gatt_app_id = cb_server_id, \ .callback_result.uuid = cb_uuid, \ .callback_result.srvc_handle = cb_srvc_handle, \ .callback_result.char_handle = cb_char_handle, \ .store_char_handle = cb_store_char_handle, \ } #define CALLBACK_GATTS_DESCRIPTOR_ADDED(cb_res, cb_server_id, cb_uuid, \ cb_srvc_handle, cb_desc_handle, \ cb_store_desc_handle) { \ .callback = CB_GATTS_DESCRIPTOR_ADDED, \ .callback_result.status = cb_res, \ .callback_result.gatt_app_id = cb_server_id, \ .callback_result.uuid = cb_uuid, \ .callback_result.srvc_handle = cb_srvc_handle, \ .callback_result.desc_handle = cb_desc_handle, \ .store_desc_handle = cb_store_desc_handle, \ } #define CALLBACK_GATTS_SERVICE_STARTED(cb_res, cb_server_id, cb_srvc_handle) { \ .callback = CB_GATTS_SERVICE_STARTED, \ .callback_result.status = cb_res, \ .callback_result.gatt_app_id = cb_server_id, \ .callback_result.srvc_handle = cb_srvc_handle, \ } #define CALLBACK_GATTS_SERVICE_STOPPED(cb_res, cb_server_id, cb_srvc_handle) { \ .callback = CB_GATTS_SERVICE_STOPPED, \ .callback_result.status = cb_res, \ .callback_result.gatt_app_id = cb_server_id, \ .callback_result.srvc_handle = cb_srvc_handle, \ } #define CALLBACK_GATTS_SERVICE_DELETED(cb_res, cb_server_id, cb_srvc_handle) { \ .callback = CB_GATTS_SERVICE_DELETED, \ .callback_result.status = cb_res, \ .callback_result.gatt_app_id = cb_server_id, \ .callback_result.srvc_handle = cb_srvc_handle, \ } #define CALLBACK_GATTS_REQUEST_READ(cb_conn_id, cb_trans_id, cb_prop, \ cb_attr_handle, cb_offset, \ cb_is_long) { \ .callback = CB_GATTS_REQUEST_READ, \ .callback_result.conn_id = cb_conn_id, \ .callback_result.trans_id = cb_trans_id, \ .callback_result.properties = cb_prop, \ .callback_result.num_properties = 1, \ .callback_result.attr_handle = cb_attr_handle, \ .callback_result.offset = cb_offset, \ .callback_result.is_long = cb_is_long, \ } #define CALLBACK_GATTS_REQUEST_WRITE(cb_conn_id, cb_trans_id, cb_prop, \ cb_attr_handle, cb_offset, \ cb_length, cb_need_rsp, \ cb_is_prep, cb_value) { \ .callback = CB_GATTS_REQUEST_WRITE, \ .callback_result.conn_id = cb_conn_id, \ .callback_result.trans_id = cb_trans_id, \ .callback_result.properties = cb_prop, \ .callback_result.num_properties = 1, \ .callback_result.attr_handle = cb_attr_handle, \ .callback_result.offset = cb_offset, \ .callback_result.length = cb_length, \ .callback_result.need_rsp = cb_need_rsp, \ .callback_result.is_prep = cb_is_prep, \ .callback_result.value = cb_value, \ } #define CALLBACK_MAP_CLIENT_REMOTE_MAS_INSTANCE(cb_status, cb_prop, \ cb_num_inst, cb_instances) { \ .callback = CB_MAP_CLIENT_REMOTE_MAS_INSTANCES, \ .callback_result.properties = cb_prop, \ .callback_result.num_properties = 1, \ .callback_result.status = cb_status, \ .callback_result.num_mas_instances = cb_num_inst, \ .callback_result.mas_instances = cb_instances, \ } #define CALLBACK_PAN_CTRL_STATE(cb, cb_res, cb_state, cb_local_role) { \ .callback = cb, \ .callback_result.status = cb_res, \ .callback_result.ctrl_state = cb_state, \ .callback_result.local_role = cb_local_role, \ } #define CALLBACK_PAN_CONN_STATE(cb, cb_res, cb_state, cb_local_role, \ cb_remote_role) { \ .callback = cb, \ .callback_result.status = cb_res, \ .callback_result.conn_state = cb_state, \ .callback_result.local_role = cb_local_role, \ .callback_result.remote_role = cb_remote_role, \ } #define CALLBACK_HDP_APP_REG_STATE(cb, cb_app_id, cb_state) { \ .callback = cb, \ .callback_result.app_id = cb_app_id, \ .callback_result.app_state = cb_state, \ } #define CALLBACK_HDP_CHANNEL_STATE(cb, cb_app_id, cb_channel_id, \ cb_mdep_cfg_index, cb_state) { \ .callback = cb, \ .callback_result.app_id = cb_app_id, \ .callback_result.channel_id = cb_channel_id, \ .callback_result.mdep_cfg_index = cb_mdep_cfg_index, \ .callback_result.channel_state = cb_state, \ } #define CALLBACK_AV_CONN_STATE(cb, cb_av_conn_state) { \ .callback = cb, \ .callback_result.av_conn_state = cb_av_conn_state, \ } #define CALLBACK_AV_AUDIO_STATE(cb, cb_av_audio_state) { \ .callback = cb, \ .callback_result.av_audio_state = cb_av_audio_state, \ } #define CALLBACK_RC_PLAY_STATUS(cb, cb_length, cb_position, cb_status) { \ .callback = cb, \ .callback_result.song_length = cb_length, \ .callback_result.song_position = cb_position, \ .callback_result.play_status = cb_status, \ } #define CALLBACK_RC_REG_NOTIF_TRACK_CHANGED(cb, cb_index) { \ .callback = cb, \ .callback_result.rc_index = cb_index, \ } #define CALLBACK_RC_REG_NOTIF_POSITION_CHANGED(cb, cb_position) { \ .callback = cb, \ .callback_result.song_position = cb_position, \ } #define CALLBACK_RC_REG_NOTIF_STATUS_CHANGED(cb, cb_status) { \ .callback = cb, \ .callback_result.play_status = cb_status, \ } #define CALLBACK_RC_GET_ELEMENT_ATTRIBUTES(cb, cb_num_of_attrs, cb_attrs) { \ .callback = cb, \ .callback_result.num_of_attrs = cb_num_of_attrs, \ .callback_result.attrs = cb_attrs, \ } #define CALLBACK_DEVICE_PROPS(props, prop_cnt) \ CALLBACK_PROPS(CB_BT_REMOTE_DEVICE_PROPERTIES, props, prop_cnt) #define CALLBACK_DEVICE_FOUND(props, prop_cnt) \ CALLBACK_PROPS(CB_BT_DEVICE_FOUND, props, prop_cnt) #define CALLBACK_BOND_STATE(cb_res, props, prop_cnt) { \ .callback = CB_BT_BOND_STATE_CHANGED, \ .callback_result.state = cb_res, \ .callback_result.properties = props, \ .callback_result.num_properties = prop_cnt, \ } #define CALLBACK_BOND_STATE_FAILED(cb_res, props, prop_cnt, reason) { \ .callback = CB_BT_BOND_STATE_CHANGED, \ .callback_result.state = cb_res, \ .callback_result.status = reason, \ .callback_result.properties = props, \ .callback_result.num_properties = prop_cnt, \ } #define CALLBACK_SSP_REQ(pair_var, props, prop_cnt) { \ .callback = CB_BT_SSP_REQUEST, \ .callback_result.pairing_variant = pair_var, \ .callback_result.properties = props, \ .callback_result.num_properties = prop_cnt, \ } #define DBG_CB(cb) { cb, #cb } /* * NOTICE: * Callback enum sections should be * updated while adding new HAL to tester. */ typedef enum { CB_BT_NONE, CB_BT_ADAPTER_STATE_CHANGED, CB_BT_ADAPTER_PROPERTIES, CB_BT_REMOTE_DEVICE_PROPERTIES, CB_BT_DEVICE_FOUND, CB_BT_DISCOVERY_STATE_CHANGED, CB_BT_PIN_REQUEST, CB_BT_SSP_REQUEST, CB_BT_BOND_STATE_CHANGED, CB_BT_ACL_STATE_CHANGED, CB_BT_THREAD_EVT, CB_BT_DUT_MODE_RECV, CB_BT_LE_TEST_MODE, /* Hidhost cb */ CB_HH_CONNECTION_STATE, CB_HH_HID_INFO, CB_HH_PROTOCOL_MODE, CB_HH_IDLE_TIME, CB_HH_GET_REPORT, CB_HH_VIRTUAL_UNPLUG, /* PAN cb */ CB_PAN_CONTROL_STATE, CB_PAN_CONNECTION_STATE, /* HDP cb */ CB_HDP_APP_REG_STATE, CB_HDP_CHANNEL_STATE, /* A2DP cb */ CB_A2DP_CONN_STATE, CB_A2DP_AUDIO_STATE, /* AVRCP */ CB_AVRCP_PLAY_STATUS_REQ, CB_AVRCP_PLAY_STATUS_RSP, CB_AVRCP_REG_NOTIF_REQ, CB_AVRCP_REG_NOTIF_RSP, CB_AVRCP_GET_ATTR_REQ, CB_AVRCP_GET_ATTR_RSP, /* Gatt client */ CB_GATTC_REGISTER_CLIENT, CB_GATTC_SCAN_RESULT, CB_GATTC_OPEN, CB_GATTC_CLOSE, CB_GATTC_SEARCH_COMPLETE, CB_GATTC_SEARCH_RESULT, CB_GATTC_GET_CHARACTERISTIC, CB_GATTC_GET_DESCRIPTOR, CB_GATTC_GET_INCLUDED_SERVICE, CB_GATTC_REGISTER_FOR_NOTIFICATION, CB_GATTC_NOTIFY, CB_GATTC_READ_CHARACTERISTIC, CB_GATTC_WRITE_CHARACTERISTIC, CB_GATTC_READ_DESCRIPTOR, CB_GATTC_WRITE_DESCRIPTOR, CB_GATTC_EXECUTE_WRITE, CB_GATTC_READ_REMOTE_RSSI, CB_GATTC_LISTEN, /* Gatt server */ CB_GATTS_REGISTER_SERVER, CB_GATTS_CONNECTION, CB_GATTS_SERVICE_ADDED, CB_GATTS_INCLUDED_SERVICE_ADDED, CB_GATTS_CHARACTERISTIC_ADDED, CB_GATTS_DESCRIPTOR_ADDED, CB_GATTS_SERVICE_STARTED, CB_GATTS_SERVICE_STOPPED, CB_GATTS_SERVICE_DELETED, CB_GATTS_REQUEST_READ, CB_GATTS_REQUEST_WRITE, CB_GATTS_REQUEST_EXEC_WRITE, CB_GATTS_RESPONSE_CONFIRMATION, CB_GATTS_INDICATION_SEND, /* Map client */ CB_MAP_CLIENT_REMOTE_MAS_INSTANCES, /* Emulator callbacks */ CB_EMU_CONFIRM_SEND_DATA, CB_EMU_ENCRYPTION_ENABLED, CB_EMU_ENCRYPTION_DISABLED, CB_EMU_CONNECTION_REJECTED, CB_EMU_VALUE_INDICATION, CB_EMU_VALUE_NOTIFICATION, CB_EMU_READ_RESPONSE, CB_EMU_WRITE_RESPONSE, CB_EMU_ATT_ERROR, } expected_bt_callback_t; struct test_data { struct mgmt *mgmt; audio_hw_device_t *audio; struct hw_device_t *device; struct hciemu *hciemu; enum hciemu_type hciemu_type; const bt_interface_t *if_bluetooth; const btsock_interface_t *if_sock; const bthh_interface_t *if_hid; const btpan_interface_t *if_pan; const bthl_interface_t *if_hdp; const btav_interface_t *if_a2dp; struct audio_stream_out *if_stream; const btrc_interface_t *if_avrcp; const btgatt_interface_t *if_gatt; const btmce_interface_t *if_map_client; const void *test_data; struct queue *steps; guint signalfd; uint16_t mgmt_index; pid_t bluetoothd_pid; struct queue *pdus; }; /* * Struct holding bluetooth HAL action parameters */ struct bt_action_data { bt_bdaddr_t *addr; /* Remote props action arguments */ const int prop_type; const bt_property_t *prop; /* Bonding requests parameters */ bt_pin_code_t *pin; const uint8_t pin_len; const uint8_t ssp_variant; const bool accept; const uint16_t io_cap; /* Socket HAL specific params */ const btsock_type_t sock_type; const int channel; const uint8_t *service_uuid; const char *service_name; const int flags; int *fd; /* HidHost params */ const int report_size; /*Connection params*/ const uint8_t bearer_type; const uint8_t transport_type; }; /* bthost's l2cap server setup parameters */ struct emu_set_l2cap_data { const uint16_t psm; const bthost_l2cap_connect_cb func; void *user_data; }; struct emu_l2cap_cid_data { const struct pdu_set *pdu; uint16_t handle; uint16_t cid; bool is_sdp; }; struct map_inst_data { int32_t id; int32_t scn; int32_t msg_types; int32_t name_len; uint8_t *name; }; /* * Callback data structure should be enhanced with data * returned by callbacks. It's used for test case step * matching with expected step data. */ struct bt_callback_data { int state; int status; int num_properties; bt_property_t *properties; bt_uuid_t *uuid; bt_ssp_variant_t pairing_variant; bthh_protocol_mode_t mode; int report_size; bool adv_data; int gatt_app_id; int conn_id; int trans_id; int offset; bool is_long; int connected; uint16_t *attr_handle; uint16_t *srvc_handle; uint16_t *inc_srvc_handle; uint16_t *char_handle; uint16_t *desc_handle; btgatt_srvc_id_t *service; btgatt_gatt_id_t *characteristic; btgatt_gatt_id_t *descriptor; btgatt_srvc_id_t *included; btgatt_read_params_t *read_params; btgatt_write_params_t *write_params; btgatt_notify_params_t *notify_params; int notification_registered; int char_prop; int length; uint8_t *value; bool need_rsp; bool is_prep; uint8_t error; btpan_control_state_t ctrl_state; btpan_connection_state_t conn_state; int local_role; int remote_role; int app_id; int channel_id; int mdep_cfg_index; bthl_app_reg_state_t app_state; bthl_channel_state_t channel_state; btav_connection_state_t av_conn_state; btav_audio_state_t av_audio_state; uint32_t song_length; uint32_t song_position; btrc_play_status_t play_status; uint64_t rc_index; uint8_t num_of_attrs; btrc_element_attr_val_t *attrs; int num_mas_instances; btmce_mas_instance_t *mas_instances; }; /* * Step structure contains expected step data and step * action, which should be performed before step check. */ struct step { void (*action)(void); int action_status; expected_bt_callback_t callback; struct bt_callback_data callback_result; void *set_data; void *set_data_2; void *set_data_3; int set_data_len; uint16_t *store_srvc_handle; uint16_t *store_char_handle; uint16_t *store_desc_handle; }; struct test_case { const uint8_t emu_type; const char *title; const uint16_t step_num; const struct step *step; }; void tester_handle_l2cap_data_exchange(struct emu_l2cap_cid_data *cid_data); void tester_generic_connect_cb(uint16_t handle, uint16_t cid, void *user_data); /* Get, remove test cases API */ struct queue *get_bluetooth_tests(void); void remove_bluetooth_tests(void); struct queue *get_socket_tests(void); void remove_socket_tests(void); struct queue *get_hidhost_tests(void); void remove_hidhost_tests(void); struct queue *get_pan_tests(void); void remove_pan_tests(void); struct queue *get_hdp_tests(void); void remove_hdp_tests(void); struct queue *get_a2dp_tests(void); void remove_a2dp_tests(void); struct queue *get_avrcp_tests(void); void remove_avrcp_tests(void); struct queue *get_gatt_tests(void); void remove_gatt_tests(void); struct queue *get_map_client_tests(void); void remove_map_client_tests(void); /* Generic tester API */ void schedule_action_verification(struct step *step); void schedule_callback_verification(struct step *step); /* Emulator actions */ void emu_setup_powered_remote_action(void); void emu_set_pin_code_action(void); void emu_set_ssp_mode_action(void); void emu_set_connect_cb_action(void); void emu_remote_connect_hci_action(void); void emu_remote_disconnect_hci_action(void); void emu_set_io_cap(void); void emu_add_l2cap_server_action(void); void emu_add_rfcomm_server_action(void); /* Actions */ void dummy_action(void); void bluetooth_enable_action(void); void bluetooth_disable_action(void); void bt_set_property_action(void); void bt_get_property_action(void); void bt_start_discovery_action(void); void bt_cancel_discovery_action(void); void bt_get_device_props_action(void); void bt_get_device_prop_action(void); void bt_set_device_prop_action(void); void bt_create_bond_action(void); void bt_pin_reply_accept_action(void); void bt_ssp_reply_accept_action(void); void bt_cancel_bond_action(void); void bt_remove_bond_action(void); void set_default_ssp_request_handler(void); bluez-5.82/android/PaxHeaders/pts-dis.txt0000644000000000000000000000005012537515745015430 xustar0020 atime=1743516876 20 ctime=1743591289 bluez-5.82/android/pts-dis.txt0000644000000000000000000000200712537515745015110 0ustar00rootrootPTS test results for DIS PTS version: 6.1 Tested: 11-May-2015 Android version: 5.1 Results: PASS test passed FAIL test failed INC test is inconclusive N/A test is disabled due to PICS setup NONE test result is none NOTE: DIS testing might require some extra properties to be set. Please refer to the README file in android folder. Section: Customization. ------------------------------------------------------------------------------- Test Name Result Notes ------------------------------------------------------------------------------- TC_SD_BV_01_C PASS TC_DEC_BV_01_C PASS TC_DEC_BV_02_C PASS TC_DEC_BV_03_C PASS TC_DEC_BV_04_C PASS TC_DEC_BV_05_C PASS TC_DEC_BV_06_C PASS TC_DEC_BV_07_C PASS TC_DEC_BV_08_C N/A TC_DEC_BV_09_C PASS TC_CR_BV_01_C PASS TC_CR_BV_02_C PASS TC_CR_BV_03_C PASS TC_CR_BV_04_C PASS TC_CR_BV_05_C PASS TC_CR_BV_06_C PASS TC_CR_BV_07_C PASS TC_CR_BV_08_C N/A TC_CR_BV_09_C PASS TC_SDP_BV_01_C PASS ------------------------------------------------------------------------------- bluez-5.82/android/PaxHeaders/Android.mk0000644000000000000000000000005013540747026015207 xustar0020 atime=1743516875 20 ctime=1743591288 bluez-5.82/android/Android.mk0000644000000000000000000004226613540747026014702 0ustar00rootrootLOCAL_PATH := external/bluetooth # Retrieve BlueZ version from configure.ac file BLUEZ_VERSION := `grep "^AC_INIT" $(LOCAL_PATH)/bluez/configure.ac | sed -e "s/.*,.\(.*\))/\1/"` ANDROID_VERSION := $(shell echo $(PLATFORM_VERSION) | awk -F. '{ printf "0x%02d%02d%02d",$$1,$$2,$$3 }') ANDROID_GE_5_0_0 := $(shell test `echo $$(($(ANDROID_VERSION)))` -lt `echo $$((0x050000))`; echo $$?) # Specify pathmap for glib and sbc pathmap_INCL += glib:external/bluetooth/glib \ sbc:external/bluetooth/sbc \ # Specify common compiler flags BLUEZ_COMMON_CFLAGS := -DVERSION=\"$(BLUEZ_VERSION)\" \ -DANDROID_VERSION=$(ANDROID_VERSION) \ -DANDROID_STORAGEDIR=\"/data/misc/bluetooth\" \ -DHAVE_LINUX_IF_ALG_H \ -DHAVE_LINUX_TYPES_H \ # Enable warnings enabled in autotools build BLUEZ_COMMON_CFLAGS += -Wall -Wextra \ -Wdeclaration-after-statement \ -Wmissing-declarations \ -Wredundant-decls \ -Wcast-align \ # Disable warnings enabled by Android but not enabled in autotools build BLUEZ_COMMON_CFLAGS += -Wno-pointer-arith \ -Wno-missing-field-initializers \ -Wno-unused-parameter \ # # Android BlueZ daemon (bluetoothd) # include $(CLEAR_VARS) LOCAL_SRC_FILES := \ bluez/android/main.c \ bluez/android/bluetooth.c \ bluez/profiles/scanparam/scpp.c \ bluez/profiles/deviceinfo/dis.c \ bluez/profiles/battery/bas.c \ bluez/profiles/input/hog-lib.c \ bluez/android/hidhost.c \ bluez/android/socket.c \ bluez/android/ipc.c \ bluez/android/avdtp.c \ bluez/android/a2dp.c \ bluez/android/a2dp-sink.c \ bluez/android/avctp.c \ bluez/android/avrcp.c \ bluez/android/avrcp-lib.c \ bluez/android/pan.c \ bluez/android/handsfree.c \ bluez/android/handsfree-client.c \ bluez/android/gatt.c \ bluez/android/health.c \ bluez/android/sco.c \ bluez/profiles/health/mcap.c \ bluez/android/map-client.c \ bluez/android/log.c \ bluez/src/shared/mgmt.c \ bluez/src/shared/util.c \ bluez/src/shared/queue.c \ bluez/src/shared/ringbuf.c \ bluez/src/shared/hfp.c \ bluez/src/shared/gatt-db.c \ bluez/src/shared/io-glib.c \ bluez/src/shared/timeout-glib.c \ bluez/src/shared/crypto.c \ bluez/src/shared/uhid.c \ bluez/src/shared/att.c \ bluez/src/shared/ad.c \ bluez/src/sdpd-database.c \ bluez/src/sdpd-service.c \ bluez/src/sdpd-request.c \ bluez/src/sdpd-server.c \ bluez/src/uuid-helper.c \ bluez/src/eir.c \ bluez/lib/sdp.c \ bluez/lib/bluetooth.c \ bluez/lib/hci.c \ bluez/lib/uuid.c \ bluez/btio/btio.c \ bluez/src/sdp-client.c \ bluez/profiles/network/bnep.c \ bluez/attrib/gattrib.c \ bluez/attrib/gatt.c \ bluez/attrib/att.c LOCAL_C_INCLUDES := \ $(call include-path-for, glib) \ $(call include-path-for, glib)/glib \ LOCAL_C_INCLUDES += \ $(LOCAL_PATH)/bluez \ LOCAL_CFLAGS := $(BLUEZ_COMMON_CFLAGS) LOCAL_SHARED_LIBRARIES := \ libglib \ LOCAL_STATIC_LIBRARIES := \ bluetooth-headers \ LOCAL_MODULE_TAGS := optional # for userdebug/eng this module is bluetoothd-main since bluetoothd is used as # wrapper to launch bluetooth with Valgrind ifneq (,$(filter userdebug eng,$(TARGET_BUILD_VARIANT))) LOCAL_MODULE := bluetoothd-main LOCAL_STRIP_MODULE := false else LOCAL_MODULE := bluetoothd endif LOCAL_ADDITIONAL_DEPENDENCIES := $(LOCAL_PATH)/bluez/configure.ac include $(BUILD_EXECUTABLE) # # bluetooth.default.so HAL # include $(CLEAR_VARS) LOCAL_SRC_FILES := \ bluez/android/hal-ipc.c \ bluez/android/hal-bluetooth.c \ bluez/android/hal-socket.c \ bluez/android/hal-hidhost.c \ bluez/android/hal-pan.c \ bluez/android/hal-a2dp.c \ bluez/android/hal-avrcp.c \ bluez/android/hal-handsfree.c \ bluez/android/hal-gatt.c \ bluez/android/hal-utils.c \ bluez/android/hal-health.c \ ifeq ($(ANDROID_GE_5_0_0), 1) LOCAL_SRC_FILES += \ bluez/android/hal-handsfree-client.c \ bluez/android/hal-map-client.c \ bluez/android/hal-a2dp-sink.c \ bluez/android/hal-avrcp-ctrl.c endif LOCAL_C_INCLUDES += \ $(call include-path-for, system-core) \ $(call include-path-for, libhardware) \ LOCAL_SHARED_LIBRARIES := \ libcutils \ LOCAL_CFLAGS := $(BLUEZ_COMMON_CFLAGS) LOCAL_MODULE := bluetooth.default LOCAL_MODULE_TAGS := optional LOCAL_MODULE_CLASS := SHARED_LIBRARIES LOCAL_REQUIRED_MODULES := bluetoothd bluetoothd-snoop init.bluetooth.rc ifeq ($(ANDROID_GE_5_0_0), 1) LOCAL_MODULE_RELATIVE_PATH := hw else LOCAL_MODULE_PATH := $(TARGET_OUT_SHARED_LIBRARIES)/hw endif include $(BUILD_SHARED_LIBRARY) # # haltest # include $(CLEAR_VARS) LOCAL_SRC_FILES := \ bluez/android/client/haltest.c \ bluez/android/client/pollhandler.c \ bluez/android/client/terminal.c \ bluez/android/client/history.c \ bluez/android/client/tabcompletion.c \ bluez/android/client/if-audio.c \ bluez/android/client/if-sco.c \ bluez/android/client/if-av.c \ bluez/android/client/if-rc.c \ bluez/android/client/if-bt.c \ bluez/android/client/if-hf.c \ bluez/android/client/if-hh.c \ bluez/android/client/if-pan.c \ bluez/android/client/if-hl.c \ bluez/android/client/if-sock.c \ bluez/android/client/if-gatt.c \ bluez/android/hal-utils.c \ ifeq ($(ANDROID_GE_5_0_0), 1) LOCAL_SRC_FILES += \ bluez/android/client/if-hf-client.c \ bluez/android/client/if-mce.c \ bluez/android/client/if-av-sink.c \ bluez/android/client/if-rc-ctrl.c endif LOCAL_C_INCLUDES += \ $(call include-path-for, system-core) \ $(call include-path-for, libhardware) \ LOCAL_C_INCLUDES += \ $(LOCAL_PATH)/bluez/android \ LOCAL_CFLAGS := $(BLUEZ_COMMON_CFLAGS) -Wno-declaration-after-statement LOCAL_SHARED_LIBRARIES := \ libhardware \ libcutils \ LOCAL_MODULE_PATH := $(TARGET_OUT_OPTIONAL_EXECUTABLES) LOCAL_MODULE_TAGS := debug LOCAL_MODULE := haltest include $(BUILD_EXECUTABLE) # # mcaptest # include $(CLEAR_VARS) LOCAL_SRC_FILES := \ bluez/src/shared/log.c \ bluez/src/log.c \ bluez/btio/btio.c \ bluez/lib/bluetooth.c \ bluez/lib/hci.c \ bluez/profiles/health/mcap.c \ bluez/tools/mcaptest.c \ LOCAL_C_INCLUDES := \ $(call include-path-for, glib) \ $(call include-path-for, glib)/glib \ LOCAL_C_INCLUDES += \ $(LOCAL_PATH)/bluez \ LOCAL_CFLAGS := $(BLUEZ_COMMON_CFLAGS) LOCAL_SHARED_LIBRARIES := \ libglib \ LOCAL_STATIC_LIBRARIES := \ bluetooth-headers \ LOCAL_MODULE_PATH := $(TARGET_OUT_OPTIONAL_EXECUTABLES) LOCAL_MODULE_TAGS := debug LOCAL_MODULE := mcaptest include $(BUILD_EXECUTABLE) # # bneptest # include $(CLEAR_VARS) LOCAL_SRC_FILES := \ bluez/src/log.c \ bluez/btio/btio.c \ bluez/lib/bluetooth.c \ bluez/lib/hci.c \ bluez/profiles/network/bnep.c \ bluez/tools/bneptest.c \ LOCAL_C_INCLUDES := \ $(call include-path-for, glib) \ $(call include-path-for, glib)/glib \ LOCAL_C_INCLUDES += \ $(LOCAL_PATH)/bluez \ LOCAL_CFLAGS := $(BLUEZ_COMMON_CFLAGS) LOCAL_SHARED_LIBRARIES := \ libglib \ LOCAL_STATIC_LIBRARIES := \ bluetooth-headers \ LOCAL_MODULE_PATH := $(TARGET_OUT_OPTIONAL_EXECUTABLES) LOCAL_MODULE_TAGS := debug LOCAL_MODULE := bneptest include $(BUILD_EXECUTABLE) # # avdtptest # include $(CLEAR_VARS) LOCAL_SRC_FILES := \ bluez/android/avdtptest.c \ bluez/android/avdtp.c \ bluez/src/log.c \ bluez/btio/btio.c \ bluez/lib/bluetooth.c \ bluez/lib/hci.c \ bluez/src/shared/util.c \ bluez/src/shared/queue.c \ LOCAL_C_INCLUDES += \ $(LOCAL_PATH)/bluez \ $(call include-path-for, glib) \ $(call include-path-for, glib)/glib \ LOCAL_CFLAGS := $(BLUEZ_COMMON_CFLAGS) LOCAL_SHARED_LIBRARIES := \ libglib \ LOCAL_STATIC_LIBRARIES := \ bluetooth-headers \ LOCAL_MODULE_PATH := $(TARGET_OUT_OPTIONAL_EXECUTABLES) LOCAL_MODULE_TAGS := debug LOCAL_MODULE := avdtptest include $(BUILD_EXECUTABLE) # # btmon # include $(CLEAR_VARS) LOCAL_SRC_FILES := \ bluez/monitor/main.c \ bluez/monitor/display.c \ bluez/monitor/hcidump.c \ bluez/monitor/control.c \ bluez/monitor/packet.c \ bluez/monitor/l2cap.c \ bluez/monitor/avctp.c \ bluez/monitor/avdtp.c \ bluez/monitor/a2dp.c \ bluez/monitor/rfcomm.c \ bluez/monitor/bnep.c \ bluez/monitor/uuid.c \ bluez/monitor/sdp.c \ bluez/monitor/vendor.c \ bluez/monitor/lmp.c \ bluez/monitor/crc.c \ bluez/monitor/ll.c \ bluez/monitor/hwdb.c \ bluez/monitor/keys.c \ bluez/monitor/ellisys.c \ bluez/monitor/analyze.c \ bluez/monitor/intel.c \ bluez/monitor/broadcom.c \ bluez/src/shared/util.c \ bluez/src/shared/queue.c \ bluez/src/shared/crypto.c \ bluez/src/shared/btsnoop.c \ bluez/src/shared/mainloop.c \ bluez/lib/hci.c \ bluez/lib/bluetooth.c \ LOCAL_C_INCLUDES := \ $(LOCAL_PATH)/bluez \ LOCAL_CFLAGS := $(BLUEZ_COMMON_CFLAGS) LOCAL_STATIC_LIBRARIES := \ bluetooth-headers \ LOCAL_MODULE_PATH := $(TARGET_OUT_OPTIONAL_EXECUTABLES) LOCAL_MODULE_TAGS := debug LOCAL_MODULE := btmon LOCAL_ADDITIONAL_DEPENDENCIES := $(LOCAL_PATH)/bluez/configure.ac include $(BUILD_EXECUTABLE) # # btproxy # include $(CLEAR_VARS) LOCAL_SRC_FILES := \ bluez/tools/btproxy.c \ bluez/src/shared/mainloop.c \ bluez/src/shared/util.c \ bluez/src/shared/ecc.c \ LOCAL_C_INCLUDES := \ $(LOCAL_PATH)/bluez \ LOCAL_CFLAGS := $(BLUEZ_COMMON_CFLAGS) LOCAL_MODULE_PATH := $(TARGET_OUT_OPTIONAL_EXECUTABLES) LOCAL_MODULE_TAGS := debug LOCAL_MODULE := btproxy LOCAL_ADDITIONAL_DEPENDENCIES := $(LOCAL_PATH)/bluez/configure.ac include $(BUILD_EXECUTABLE) # # A2DP audio # include $(CLEAR_VARS) LOCAL_SRC_FILES := \ bluez/android/hal-audio.c \ bluez/android/hal-audio-sbc.c \ bluez/android/hal-audio-aptx.c \ LOCAL_C_INCLUDES = \ $(LOCAL_PATH)/bluez \ $(call include-path-for, system-core) \ $(call include-path-for, libhardware) \ $(call include-path-for, sbc) \ LOCAL_SHARED_LIBRARIES := \ libcutils \ libsbc \ LOCAL_CFLAGS := $(BLUEZ_COMMON_CFLAGS) -Wno-declaration-after-statement LOCAL_LDFLAGS := -ldl LOCAL_MODULE_TAGS := optional LOCAL_MODULE := audio.a2dp.default ifeq ($(ANDROID_GE_5_0_0), 1) LOCAL_MODULE_RELATIVE_PATH := hw else LOCAL_MODULE_PATH := $(TARGET_OUT_SHARED_LIBRARIES)/hw endif include $(BUILD_SHARED_LIBRARY) # # SCO audio # include $(CLEAR_VARS) LOCAL_SRC_FILES := bluez/android/hal-sco.c \ bluez/android/hal-utils.c LOCAL_C_INCLUDES = \ $(call include-path-for, system-core) \ $(call include-path-for, libhardware) \ $(call include-path-for, audio-utils) \ LOCAL_SHARED_LIBRARIES := \ libcutils \ libaudioutils \ LOCAL_CFLAGS := $(BLUEZ_COMMON_CFLAGS) -Wno-declaration-after-statement LOCAL_MODULE_TAGS := optional LOCAL_MODULE := audio.sco.default ifeq ($(ANDROID_GE_5_0_0), 1) LOCAL_MODULE_RELATIVE_PATH := hw else LOCAL_MODULE_PATH := $(TARGET_OUT_SHARED_LIBRARIES)/hw endif include $(BUILD_SHARED_LIBRARY) # # l2cap-test # include $(CLEAR_VARS) LOCAL_SRC_FILES := \ bluez/tools/l2test.c \ bluez/lib/bluetooth.c \ bluez/lib/hci.c \ LOCAL_C_INCLUDES := \ $(LOCAL_PATH)/bluez \ LOCAL_CFLAGS := $(BLUEZ_COMMON_CFLAGS) LOCAL_STATIC_LIBRARIES := \ bluetooth-headers \ LOCAL_MODULE_PATH := $(TARGET_OUT_OPTIONAL_EXECUTABLES) LOCAL_MODULE_TAGS := debug LOCAL_MODULE := l2test LOCAL_ADDITIONAL_DEPENDENCIES := $(LOCAL_PATH)/bluez/configure.ac include $(BUILD_EXECUTABLE) # # bluetoothd-snoop # include $(CLEAR_VARS) LOCAL_SRC_FILES := \ bluez/android/bluetoothd-snoop.c \ bluez/src/shared/mainloop.c \ bluez/src/shared/btsnoop.c \ bluez/android/log.c \ LOCAL_C_INCLUDES := \ $(LOCAL_PATH)/bluez \ $(LOCAL_PATH)/bluez/lib \ LOCAL_CFLAGS := $(BLUEZ_COMMON_CFLAGS) LOCAL_MODULE_TAGS := optional LOCAL_MODULE := bluetoothd-snoop LOCAL_ADDITIONAL_DEPENDENCIES := $(LOCAL_PATH)/bluez/configure.ac include $(BUILD_EXECUTABLE) # # init.bluetooth.rc # include $(CLEAR_VARS) LOCAL_MODULE := init.bluetooth.rc LOCAL_MODULE_CLASS := ETC LOCAL_SRC_FILES := bluez/android/$(LOCAL_MODULE) LOCAL_MODULE_TAGS := optional LOCAL_MODULE_PATH := $(TARGET_ROOT_OUT) include $(BUILD_PREBUILT) # # btmgmt # include $(CLEAR_VARS) LOCAL_SRC_FILES := \ bluez/tools/btmgmt.c \ bluez/lib/bluetooth.c \ bluez/lib/hci.c \ bluez/lib/sdp.c \ bluez/src/shared/mainloop.c \ bluez/src/shared/io-mainloop.c \ bluez/src/shared/mgmt.c \ bluez/src/shared/queue.c \ bluez/src/shared/util.c \ bluez/src/shared/gap.c \ bluez/src/uuid-helper.c \ bluez/client/display.c \ LOCAL_C_INCLUDES := \ $(LOCAL_PATH)/bluez \ $(LOCAL_PATH)/bluez/android/compat \ LOCAL_CFLAGS := $(BLUEZ_COMMON_CFLAGS) LOCAL_STATIC_LIBRARIES := \ bluetooth-headers \ LOCAL_MODULE_PATH := $(TARGET_OUT_OPTIONAL_EXECUTABLES) LOCAL_MODULE_TAGS := debug LOCAL_MODULE := btmgmt LOCAL_ADDITIONAL_DEPENDENCIES := $(LOCAL_PATH)/bluez/configure.ac include $(BUILD_EXECUTABLE) # # hcitool # include $(CLEAR_VARS) LOCAL_SRC_FILES := \ bluez/tools/hcitool.c \ bluez/src/oui.c \ bluez/lib/bluetooth.c \ bluez/lib/hci.c \ LOCAL_C_INCLUDES := \ $(LOCAL_PATH)/bluez \ LOCAL_CFLAGS := $(BLUEZ_COMMON_CFLAGS) LOCAL_STATIC_LIBRARIES := \ bluetooth-headers \ LOCAL_MODULE_PATH := $(TARGET_OUT_OPTIONAL_EXECUTABLES) LOCAL_MODULE_TAGS := debug LOCAL_MODULE := hcitool LOCAL_ADDITIONAL_DEPENDENCIES := $(LOCAL_PATH)/bluez/configure.ac include $(BUILD_EXECUTABLE) # # hciconfig # include $(CLEAR_VARS) LOCAL_SRC_FILES:= \ bluez/tools/hciconfig.c \ bluez/tools/csr.c \ bluez/lib/bluetooth.c \ bluez/lib/hci.c \ LOCAL_C_INCLUDES := \ $(LOCAL_PATH)/bluez \ LOCAL_CFLAGS := $(BLUEZ_COMMON_CFLAGS) LOCAL_STATIC_LIBRARIES := \ bluetooth-headers \ LOCAL_MODULE_PATH := $(TARGET_OUT_OPTIONAL_EXECUTABLES) LOCAL_MODULE_TAGS := debug LOCAL_MODULE := hciconfig LOCAL_ADDITIONAL_DEPENDENCIES := $(LOCAL_PATH)/bluez/configure.ac include $(BUILD_EXECUTABLE) # # l2ping # include $(CLEAR_VARS) LOCAL_SRC_FILES := \ bluez/tools/l2ping.c \ bluez/lib/bluetooth.c \ bluez/lib/hci.c \ LOCAL_C_INCLUDES := \ $(LOCAL_PATH)/bluez \ LOCAL_CFLAGS := $(BLUEZ_COMMON_CFLAGS) LOCAL_STATIC_LIBRARIES := \ bluetooth-headers \ LOCAL_MODULE_PATH := $(TARGET_OUT_OPTIONAL_EXECUTABLES) LOCAL_MODULE_TAGS := debug LOCAL_MODULE := l2ping LOCAL_ADDITIONAL_DEPENDENCIES := $(LOCAL_PATH)/bluez/configure.ac include $(BUILD_EXECUTABLE) # # avtest # include $(CLEAR_VARS) LOCAL_SRC_FILES := \ bluez/tools/avtest.c \ bluez/lib/bluetooth.c \ bluez/lib/hci.c \ LOCAL_C_INCLUDES := \ $(LOCAL_PATH)/bluez \ LOCAL_CFLAGS := $(BLUEZ_COMMON_CFLAGS) LOCAL_STATIC_LIBRARIES := \ bluetooth-headers \ LOCAL_MODULE_PATH := $(TARGET_OUT_OPTIONAL_EXECUTABLES) LOCAL_MODULE_TAGS := debug LOCAL_MODULE := avtest LOCAL_ADDITIONAL_DEPENDENCIES := $(LOCAL_PATH)/bluez/configure.ac include $(BUILD_EXECUTABLE) # # hciattach # include $(CLEAR_VARS) LOCAL_SRC_FILES := \ bluez/tools/hciattach.c \ bluez/tools/hciattach_st.c \ bluez/tools/hciattach_ti.c \ bluez/tools/hciattach_tialt.c \ bluez/tools/hciattach_ath3k.c \ bluez/tools/hciattach_qualcomm.c \ bluez/tools/hciattach_intel.c \ bluez/tools/hciattach_bcm43xx.c \ bluez/lib/bluetooth.c \ bluez/lib/hci.c \ LOCAL_C_INCLUDES := \ $(LOCAL_PATH)/bluez \ LOCAL_CFLAGS := $(BLUEZ_COMMON_CFLAGS) LOCAL_STATIC_LIBRARIES := \ bluetooth-headers \ LOCAL_MODULE_TAGS := optional LOCAL_MODULE := hciattach LOCAL_ADDITIONAL_DEPENDENCIES := $(LOCAL_PATH)/bluez/configure.ac include $(BUILD_EXECUTABLE) # # libsbc # include $(CLEAR_VARS) LOCAL_SRC_FILES:= \ sbc/sbc/sbc.c \ sbc/sbc/sbc_primitives.c \ sbc/sbc/sbc_primitives_mmx.c \ sbc/sbc/sbc_primitives_neon.c \ sbc/sbc/sbc_primitives_armv6.c \ sbc/sbc/sbc_primitives_iwmmxt.c \ LOCAL_C_INCLUDES:= \ $(LOCAL_PATH)/sbc \ LOCAL_CFLAGS:= \ -Os \ -Wno-sign-compare \ -Wno-missing-field-initializers \ -Wno-unused-parameter \ -Wno-type-limits \ -Wno-empty-body \ LOCAL_MODULE := libsbc include $(BUILD_SHARED_LIBRARY) ifneq (,$(filter userdebug eng,$(TARGET_BUILD_VARIANT))) # # bluetoothd (debug) # this is just a wrapper used in userdebug/eng to launch bluetoothd-main # with/without Valgrind # include $(CLEAR_VARS) LOCAL_SRC_FILES := \ bluez/android/bluetoothd-wrapper.c \ bluez/android/hal-utils.c LOCAL_CFLAGS := $(BLUEZ_COMMON_CFLAGS) LOCAL_SHARED_LIBRARIES := \ libcutils \ LOCAL_MODULE_PATH := $(TARGET_OUT_EXECUTABLES) LOCAL_MODULE_TAGS := optional LOCAL_MODULE := bluetoothd LOCAL_REQUIRED_MODULES := \ bluetoothd-main \ valgrind \ memcheck-$(TARGET_ARCH)-linux \ vgpreload_core-$(TARGET_ARCH)-linux \ vgpreload_memcheck-$(TARGET_ARCH)-linux \ default.supp include $(BUILD_EXECUTABLE) endif # # bluetooth-headers # include $(CLEAR_VARS) LOCAL_MODULE := bluetooth-headers LOCAL_NODULE_TAGS := optional LOCAL_MODULE_CLASS := STATIC_LIBRARIES include_path := $(local-intermediates-dir)/include include_files := $(wildcard $(LOCAL_PATH)/bluez/lib/*.h) $(shell mkdir -p $(include_path)/bluetooth) $(foreach file,$(include_files),$(shell cp -u $(file) $(include_path)/bluetooth)) LOCAL_EXPORT_C_INCLUDE_DIRS := $(include_path) include $(BUILD_STATIC_LIBRARY) # # avtest # include $(CLEAR_VARS) LOCAL_SRC_FILES := \ bluez/tools/avinfo.c \ bluez/lib/bluetooth.c \ bluez/lib/hci.c \ LOCAL_C_INCLUDES := \ $(LOCAL_PATH)/bluez \ LOCAL_CFLAGS := $(BLUEZ_COMMON_CFLAGS) LOCAL_STATIC_LIBRARIES := \ bluetooth-headers \ LOCAL_MODULE_PATH := $(TARGET_OUT_OPTIONAL_EXECUTABLES) LOCAL_MODULE_TAGS := debug LOCAL_MODULE := avinfo LOCAL_ADDITIONAL_DEPENDENCIES := $(LOCAL_PATH)/bluez/configure.ac include $(BUILD_EXECUTABLE) # # rctest # include $(CLEAR_VARS) LOCAL_SRC_FILES := \ bluez/tools/rctest.c \ bluez/lib/bluetooth.c \ bluez/lib/hci.c \ bluez/lib/sdp.c \ LOCAL_C_INCLUDES := \ $(LOCAL_PATH)/bluez \ LOCAL_CFLAGS := $(BLUEZ_COMMON_CFLAGS) LOCAL_STATIC_LIBRARIES := \ bluetooth-headers \ LOCAL_MODULE_PATH := $(TARGET_OUT_OPTIONAL_EXECUTABLES) LOCAL_MODULE_TAGS := debug LOCAL_MODULE := rctest LOCAL_ADDITIONAL_DEPENDENCIES := $(LOCAL_PATH)/bluez/configure.ac include $(BUILD_EXECUTABLE) bluez-5.82/android/PaxHeaders/ipc.c0000644000000000000000000000005014015011623014176 xustar0020 atime=1743516866 20 ctime=1743591278 bluez-5.82/android/ipc.c0000644000000000000000000002131614015011623013662 0ustar00rootroot// SPDX-License-Identifier: LGPL-2.1-or-later /* * * BlueZ - Bluetooth protocol stack for Linux * * Copyright (C) 2013-2014 Intel Corporation. All rights reserved. * * */ #ifdef HAVE_CONFIG_H #include #endif #include #include #include #include #include #include #include #include #include #include #include "ipc-common.h" #include "ipc.h" #include "src/log.h" struct service_handler { const struct ipc_handler *handler; uint8_t size; }; struct ipc { struct service_handler *services; int service_max; const char *path; size_t size; GIOChannel *cmd_io; guint cmd_watch; bool notifications; GIOChannel *notif_io; guint notif_watch; ipc_disconnect_cb disconnect_cb; void *disconnect_cb_data; }; static void ipc_disconnect(struct ipc *ipc, bool in_cleanup) { if (ipc->cmd_watch) { g_source_remove(ipc->cmd_watch); ipc->cmd_watch = 0; } if (ipc->cmd_io) { g_io_channel_shutdown(ipc->cmd_io, TRUE, NULL); g_io_channel_unref(ipc->cmd_io); ipc->cmd_io = NULL; } if (ipc->notif_watch) { g_source_remove(ipc->notif_watch); ipc->notif_watch = 0; } if (ipc->notif_io) { g_io_channel_shutdown(ipc->notif_io, TRUE, NULL); g_io_channel_unref(ipc->notif_io); ipc->notif_io = NULL; } if (in_cleanup) return; if (ipc->disconnect_cb) ipc->disconnect_cb(ipc->disconnect_cb_data); } static int ipc_handle_msg(struct service_handler *handlers, size_t max_index, const void *buf, ssize_t len) { const struct ipc_hdr *msg = buf; const struct ipc_handler *handler; if (len < (ssize_t) sizeof(*msg)) { DBG("message too small (%zd bytes)", len); return -EBADMSG; } if (len != (ssize_t) (sizeof(*msg) + msg->len)) { DBG("message malformed (%zd bytes)", len); return -EBADMSG; } /* if service is valid */ if (msg->service_id > max_index) { DBG("unknown service (0x%x)", msg->service_id); return -EOPNOTSUPP; } /* if service is registered */ if (!handlers[msg->service_id].handler) { DBG("service not registered (0x%x)", msg->service_id); return -EOPNOTSUPP; } /* if opcode is valid */ if (msg->opcode == IPC_OP_STATUS || msg->opcode > handlers[msg->service_id].size) { DBG("invalid opcode 0x%x for service 0x%x", msg->opcode, msg->service_id); return -EOPNOTSUPP; } /* opcode is table offset + 1 */ handler = &handlers[msg->service_id].handler[msg->opcode - 1]; /* if payload size is valid */ if ((handler->var_len && handler->data_len > msg->len) || (!handler->var_len && handler->data_len != msg->len)) { DBG("invalid size for opcode 0x%x service 0x%x", msg->opcode, msg->service_id); return -EMSGSIZE; } handler->handler(msg->payload, msg->len); return 0; } static gboolean cmd_watch_cb(GIOChannel *io, GIOCondition cond, gpointer user_data) { struct ipc *ipc = user_data; char buf[IPC_MTU]; ssize_t ret; int fd, err; if (cond & (G_IO_NVAL | G_IO_ERR | G_IO_HUP)) { info("IPC: command socket closed"); ipc->cmd_watch = 0; goto fail; } fd = g_io_channel_unix_get_fd(io); ret = read(fd, buf, sizeof(buf)); if (ret < 0) { error("IPC: command read failed (%s)", strerror(errno)); goto fail; } err = ipc_handle_msg(ipc->services, ipc->service_max, buf, ret); if (err < 0) { error("IPC: failed to handle message (%s)", strerror(-err)); goto fail; } return TRUE; fail: ipc_disconnect(ipc, false); return FALSE; } static gboolean notif_watch_cb(GIOChannel *io, GIOCondition cond, gpointer user_data) { struct ipc *ipc = user_data; info("IPC: notification socket closed"); ipc->notif_watch = 0; ipc_disconnect(ipc, false); return FALSE; } static GIOChannel *ipc_connect(const char *path, size_t size, GIOFunc connect_cb, void *user_data) { struct sockaddr_un addr; GIOCondition cond; GIOChannel *io; int sk; sk = socket(PF_LOCAL, SOCK_SEQPACKET, 0); if (sk < 0) { error("IPC: failed to create socket: %d (%s)", errno, strerror(errno)); return NULL; } io = g_io_channel_unix_new(sk); g_io_channel_set_close_on_unref(io, TRUE); g_io_channel_set_flags(io, G_IO_FLAG_NONBLOCK, NULL); memset(&addr, 0, sizeof(addr)); addr.sun_family = AF_UNIX; memcpy(addr.sun_path, path, size); connect(sk, (struct sockaddr *) &addr, sizeof(addr)); cond = G_IO_OUT | G_IO_ERR | G_IO_HUP | G_IO_NVAL; g_io_add_watch(io, cond, connect_cb, user_data); return io; } static gboolean notif_connect_cb(GIOChannel *io, GIOCondition cond, gpointer user_data) { struct ipc *ipc = user_data; DBG(""); if (cond & (G_IO_NVAL | G_IO_ERR | G_IO_HUP)) { error("IPC: notification socket connect failed"); ipc_disconnect(ipc, false); return FALSE; } cond = G_IO_ERR | G_IO_HUP | G_IO_NVAL; ipc->notif_watch = g_io_add_watch(io, cond, notif_watch_cb, ipc); cond = G_IO_IN | G_IO_ERR | G_IO_HUP | G_IO_NVAL; ipc->cmd_watch = g_io_add_watch(ipc->cmd_io, cond, cmd_watch_cb, ipc); info("IPC: successfully connected (with notifications)"); return FALSE; } static gboolean cmd_connect_cb(GIOChannel *io, GIOCondition cond, gpointer user_data) { struct ipc *ipc = user_data; DBG(""); if (cond & (G_IO_NVAL | G_IO_ERR | G_IO_HUP)) { error("IPC: command socket connect failed"); ipc_disconnect(ipc, false); return FALSE; } if (ipc->notifications) { ipc->notif_io = ipc_connect(ipc->path, ipc->size, notif_connect_cb, ipc); if (!ipc->notif_io) ipc_disconnect(ipc, false); return FALSE; } cond = G_IO_IN | G_IO_ERR | G_IO_HUP | G_IO_NVAL; ipc->cmd_watch = g_io_add_watch(ipc->cmd_io, cond, cmd_watch_cb, ipc); info("IPC: successfully connected (without notifications)"); return FALSE; } struct ipc *ipc_init(const char *path, size_t size, int max_service_id, bool notifications, ipc_disconnect_cb cb, void *cb_data) { struct ipc *ipc; ipc = g_new0(struct ipc, 1); ipc->services = g_new0(struct service_handler, max_service_id + 1); ipc->service_max = max_service_id; ipc->path = path; ipc->size = size; ipc->notifications = notifications; ipc->cmd_io = ipc_connect(path, size, cmd_connect_cb, ipc); if (!ipc->cmd_io) { g_free(ipc->services); g_free(ipc); return NULL; } ipc->disconnect_cb = cb; ipc->disconnect_cb_data = cb_data; return ipc; } void ipc_cleanup(struct ipc *ipc) { ipc_disconnect(ipc, true); g_free(ipc->services); g_free(ipc); } static void ipc_send(int sk, uint8_t service_id, uint8_t opcode, uint16_t len, void *param, int fd) { struct msghdr msg; struct iovec iv[2]; struct ipc_hdr m; char cmsgbuf[CMSG_SPACE(sizeof(int))]; struct cmsghdr *cmsg; memset(&msg, 0, sizeof(msg)); memset(&m, 0, sizeof(m)); memset(cmsgbuf, 0, sizeof(cmsgbuf)); m.service_id = service_id; m.opcode = opcode; m.len = len; iv[0].iov_base = &m; iv[0].iov_len = sizeof(m); iv[1].iov_base = param; iv[1].iov_len = len; msg.msg_iov = iv; msg.msg_iovlen = 2; if (fd >= 0) { msg.msg_control = cmsgbuf; msg.msg_controllen = sizeof(cmsgbuf); cmsg = CMSG_FIRSTHDR(&msg); cmsg->cmsg_level = SOL_SOCKET; cmsg->cmsg_type = SCM_RIGHTS; cmsg->cmsg_len = CMSG_LEN(sizeof(int)); /* Initialize the payload */ memcpy(CMSG_DATA(cmsg), &fd, sizeof(int)); } if (sendmsg(sk, &msg, 0) < 0) { error("IPC send failed :%s", strerror(errno)); /* TODO disconnect IPC here when this function becomes static */ raise(SIGTERM); } } void ipc_send_rsp(struct ipc *ipc, uint8_t service_id, uint8_t opcode, uint8_t status) { struct ipc_status s; int sk; sk = g_io_channel_unix_get_fd(ipc->cmd_io); if (status == IPC_STATUS_SUCCESS) { ipc_send(sk, service_id, opcode, 0, NULL, -1); return; } s.code = status; ipc_send(sk, service_id, IPC_OP_STATUS, sizeof(s), &s, -1); } void ipc_send_rsp_full(struct ipc *ipc, uint8_t service_id, uint8_t opcode, uint16_t len, void *param, int fd) { ipc_send(g_io_channel_unix_get_fd(ipc->cmd_io), service_id, opcode, len, param, fd); } void ipc_send_notif(struct ipc *ipc, uint8_t service_id, uint8_t opcode, uint16_t len, void *param) { return ipc_send_notif_with_fd(ipc, service_id, opcode, len, param, -1); } void ipc_send_notif_with_fd(struct ipc *ipc, uint8_t service_id, uint8_t opcode, uint16_t len, void *param, int fd) { if (!ipc || !ipc->notif_io) return; ipc_send(g_io_channel_unix_get_fd(ipc->notif_io), service_id, opcode, len, param, fd); } void ipc_register(struct ipc *ipc, uint8_t service, const struct ipc_handler *handlers, uint8_t size) { if (service > ipc->service_max) return; ipc->services[service].handler = handlers; ipc->services[service].size = size; } void ipc_unregister(struct ipc *ipc, uint8_t service) { if (service > ipc->service_max) return; ipc->services[service].handler = NULL; ipc->services[service].size = 0; } bluez-5.82/android/PaxHeaders/hidhost.h0000644000000000000000000000005014015011623015072 xustar0020 atime=1743516865 20 ctime=1743591278 bluez-5.82/android/hidhost.h0000644000000000000000000000044214015011623014553 0ustar00rootroot/* SPDX-License-Identifier: LGPL-2.1-or-later */ /* * * BlueZ - Bluetooth protocol stack for Linux * * Copyright (C) 2013-2014 Intel Corporation. All rights reserved. * * */ bool bt_hid_register(struct ipc *ipc, const bdaddr_t *addr, uint8_t mode); void bt_hid_unregister(void); bluez-5.82/android/PaxHeaders/pixit-gavdp.txt0000644000000000000000000000005012537515745016301 xustar0020 atime=1743516876 20 ctime=1743591289 bluez-5.82/android/pixit-gavdp.txt0000644000000000000000000000174212537515745015766 0ustar00rootrootGAVDP PIXIT for the PTS tool. PTS version: 6.1 * - different than PTS defaults & - should be set to IUT Bluetooth address ^ - should be set accordingly # - should be set to PTS's bin/audio folder Required PIXIT settings ------------------------------------------------------------------------------- Parameter Name Value ------------------------------------------------------------------------------- TSPX_SNK_class_of_device 04041C TSPX_SRC_class_of_device 080418 TSPX_bd_addr_iut 112233445566 (*&) TSPX_delete_link_key FALSE TSPX_media_directory C:\Program Files\Bluetooth SIG\Bluetooth PTS\ bin\audio (#) TSPX_no_avrcp FALSE TSPX_pin_code 0000 TSPX_security_enabled FALSE TSPX_tester_av_role TSPX_time_guard 300000 TSPX_use_implicit_send TRUE TSPX_auth_password 0000 TSPX_auth_user_id PTS TSPX_rfcomm_channel 8 TSPX_l2cap_psm 1011 TSPX_no_confirmations FALSE TSPX_cover_art_uuid ------------------------------------------------------------------------------- bluez-5.82/android/PaxHeaders/pics-spp.txt0000644000000000000000000000005012537515745015603 xustar0020 atime=1743516875 20 ctime=1743591288 bluez-5.82/android/pics-spp.txt0000644000000000000000000001062512537515745015270 0ustar00rootrootSPP PICS for the PTS tool. PTS version: 6.1 * - different than PTS defaults # - not yet implemented/supported M - mandatory O - optional Profile Version ------------------------------------------------------------------------------- Item Selected Description ------------------------------------------------------------------------------- TSPC_SPP_0_1 False SPP v1.1 (C.1) TSPC_SPP_0_2 True (*) SPP v1.2 (C.1) ------------------------------------------------------------------------------- C.1: Mandatory to support only one Profile version. ------------------------------------------------------------------------------- Device Role ------------------------------------------------------------------------------- Item Selected Description ------------------------------------------------------------------------------- TSPC_SPP_1_1 True (*) Device A (DevA) (C.1) TSPC_SPP_1_2 True (*) Device B (DevB) (C.1) ------------------------------------------------------------------------------- C.1: It is mandatory to support at least one of the defined roles. ------------------------------------------------------------------------------- Support of SPP Service ------------------------------------------------------------------------------- Item Selected Description ------------------------------------------------------------------------------- TSPC_SPP_2_1 True (*) Support of Serial Profile Service (C.1) ------------------------------------------------------------------------------- C.1: Mandatory for devices that support Serial Profile for serial cable emulation as a Bluetooth service. Irrelevant of devices that only support Serial Profile for usage by another application profile e.g. Fax Profile, Dun Profile, Hands free Profile, etc. ------------------------------------------------------------------------------- Application Procedures ------------------------------------------------------------------------------- Item Selected Description ------------------------------------------------------------------------------- TSPC_SPP_3_1 True (*) Establish link and set up virtual serial connection (C.1) TSPC_SPP_3_2 True (*) Accept link and virtual serial connection establishment (C.2) TSPC_SPP_3_3 True (*) Register Service record for application in local SDP database (C.2) TSPC_SPP_3_4 True (*) No release in Sniff mode. Sniff mode enabled in the Link Manager (O) TSPC_SPP_3_5 True (*) No release in Hold mode. Hold mode enabled in the Link Manager (O) TSPC_SPP_3_6 True (*) No release in Park mode. Park mode enabled in the Link Manager (O) TSPC_SPP_3_7 True (*) No release after Master/Slave switch. M/S switch enabled in the Link manager (O) ------------------------------------------------------------------------------- C.1: Mandatory for Device A, Irrelevant for Device B. C.2: Mandatory for Device B, Irrelevant for Device A. ------------------------------------------------------------------------------- Service Port Profile Record Content (SerialPort UUID) ------------------------------------------------------------------------------- Item Selected Description ------------------------------------------------------------------------------- TSPC_SPP_4_1 True (*) SerialPort service class (UUID16: 0x1101) (C.1) TSPC_SPP_4_2 True (*) Protocol0, L2CAP (C.1) TSPC_SPP_4_3 True (*) Protocol1, RFCOMM (C.1) TSPC_SPP_4_4 True (*) Server Channel number (C.1) TSPC_SPP_4_5 True (*) Displayable text name (C.2) TSPC_SPP_4_6 True (*) BluetoothProfileDescriptorList (C.3) ------------------------------------------------------------------------------- C.1: Mandatory for role B, if capability 'Support of Serial Profile Service' (TSPC_SPP_2_1) supported. Irrelevant for role A. C.2: Mandatory to support if TSPC_SPP_2_1 AND TSPC_SPP_0_1 are supported, otherwise Optional. C.3: Mandatory to support if TSPC_SPP_2_1 AND TSPC_SPP_0_2 are supported, otherwise Optional. ------------------------------------------------------------------------------- Encryption ------------------------------------------------------------------------------- Item Selected Description ------------------------------------------------------------------------------- TSPC_SPP_5_1 True (*) Initiate encryption (O) TSPC_SPP_5_2 True Accept encryption request (M) TSPC_SPP_5_3 True Point to point encryption (M) TSPC_SPP_5_4 True Stop encryption (M) ------------------------------------------------------------------------------- bluez-5.82/android/PaxHeaders/pts-gavdp.txt0000644000000000000000000000005012537515745015752 xustar0020 atime=1743516876 20 ctime=1743591289 bluez-5.82/android/pts-gavdp.txt0000644000000000000000000000123012537515745015427 0ustar00rootrootPTS test results for GAVDP PTS version: 6.1 Tested: 08-May-2015 Android version: 5.1 Results: PASS test passed FAIL test failed INC test is inconclusive N/A test is disabled due to PICS setup NONE test result is none ------------------------------------------------------------------------------- Test Name Result Notes ------------------------------------------------------------------------------- TC_ACP_APP_CON_BV_01_C PASS TC_ACP_APP_TRC_BV_01_C N/A TC_ACP_APP_TRC_BV_02_C PASS TC_INT_APP_CON_BV_01_C PASS TC_INT_APP_TRC_BV_01_C N/A TC_INT_APP_TRC_BV_02_C PASS ------------------------------------------------------------------------------- bluez-5.82/android/PaxHeaders/pixit-sdp.txt0000644000000000000000000000005012537515745015766 xustar0020 atime=1743516876 20 ctime=1743591289 bluez-5.82/android/pixit-sdp.txt0000644000000000000000000000363112537515745015452 0ustar00rootrootSDP PIXIT for the PTS tool. PTS version: 6.1 * - different than PTS defaults & - should be set to IUT Bluetooth address ^ - should be set accordingly # - should be set according to the reported phone number's type Required PIXIT settings ------------------------------------------------------------------------------- Parameter Name Value ------------------------------------------------------------------------------- TSPX_sdp_service_search_pattern 0100 TSPX_sdp_service_search_pattern_no_results EEEE TSPX_sdp_service_search_additional_protocol_descriptor_list TSPX_sdp_service_search_bluetooth_profile_descriptor_list TSPX_sdp_service_search_pattern_browse_group_list TSPX_sdp_service_search_pattern_client_exe_url TSPX_sdp_service_search_pattern_documentation_url TSPX_sdp_service_search_pattern_icon_url TSPX_sdp_service_search_pattern_language_base_attribute_id_list TSPX_sdp_service_search_pattern_protocol_descriptor_list TSPX_sdp_service_search_pattern_provider_name TSPX_sdp_service_search_pattern_service_availability TSPX_sdp_service_search_pattern_service_data_base_state 1000(*) TSPX_sdp_service_search_pattern_service_description TSPX_sdp_service_search_pattern_service_id TSPX_sdp_service_search_pattern_service_info_time_to_live TSPX_sdp_service_search_pattern_version_number_list 1000(*) TSPX_sdp_service_search_pattern_service_name TSPX_sdp_service_search_pattern_service_record_state TSPX_sdp_unsupported_attribute_id EEEE TSPX_security_enabled FALSE TSPX_delete_link_key FALSE TSPX_bd_addr_iut 112233445566(*&) TSPX_class_of_device_pts 200404 TSPX_class_of_device_test_pts_initiator TRUE TSPX_limited_inquiry_used FALSE TSPX_pin_code 0000 TSPX_time_guard 200000 TSPX_device_search_time 20 TSPX_use_implicit_send TRUE TSPX_secure_simple_pairing_pass_key_confirmation FALSE ------------------------------------------------------------------------------- bluez-5.82/android/PaxHeaders/health.c0000644000000000000000000000005014015011623014670 xustar0020 atime=1743516867 20 ctime=1743591279 bluez-5.82/android/health.c0000644000000000000000000012334414015011623014360 0ustar00rootroot// SPDX-License-Identifier: LGPL-2.1-or-later /* * * BlueZ - Bluetooth protocol stack for Linux * * Copyright (C) 2014 Intel Corporation. All rights reserved. * Copyright (C) 2010 GSyC/LibreSoft, Universidad Rey Juan Carlos. * * */ #ifdef HAVE_CONFIG_H #include #endif #define _GNU_SOURCE #include #include #include #include #include #include "btio/btio.h" #include "lib/bluetooth.h" #include "lib/sdp.h" #include "lib/sdp_lib.h" #include "lib/uuid.h" #include "lib/l2cap.h" #include "src/log.h" #include "src/shared/util.h" #include "src/shared/queue.h" #include "src/uuid-helper.h" #include "src/sdp-client.h" #include "profiles/health/mcap.h" #include "hal-msg.h" #include "ipc-common.h" #include "ipc.h" #include "utils.h" #include "bluetooth.h" #include "health.h" #define SVC_HINT_HEALTH 0x00 #define HDP_VERSION 0x0101 #define DATA_EXCHANGE_SPEC_11073 0x01 #define CHANNEL_TYPE_ANY 0x00 #define CHANNEL_TYPE_RELIABLE 0x01 #define CHANNEL_TYPE_STREAM 0x02 #define MDEP_ECHO 0x00 #define MDEP_INITIAL 0x01 #define MDEP_FINAL 0x7F static bdaddr_t adapter_addr; static struct ipc *hal_ipc = NULL; static struct queue *apps = NULL; static struct mcap_instance *mcap = NULL; static uint32_t record_id = 0; static uint32_t record_state = 0; struct mdep_cfg { uint8_t role; uint16_t data_type; uint8_t channel_type; char *descr; uint8_t id; /* mdep id */ }; struct health_device { bdaddr_t dst; uint16_t app_id; struct mcap_mcl *mcl; struct queue *channels; /* data channels */ uint16_t ccpsm; uint16_t dcpsm; }; struct health_channel { uint8_t mdep_id; uint8_t type; struct health_device *dev; uint8_t remote_mdep; struct mcap_mdl *mdl; bool mdl_conn; uint16_t mdl_id; /* MDL ID */ uint16_t id; /* channel id */ }; struct health_app { char *app_name; char *provider_name; char *service_name; char *service_descr; uint8_t num_of_mdep; struct queue *mdeps; uint16_t id; /* app id */ struct queue *devices; }; static void send_app_reg_notify(struct health_app *app, uint8_t state) { struct hal_ev_health_app_reg_state ev; DBG(""); ev.id = app->id; ev.state = state; ipc_send_notif(hal_ipc, HAL_SERVICE_ID_HEALTH, HAL_EV_HEALTH_APP_REG_STATE, sizeof(ev), &ev); } static void send_channel_state_notify(struct health_channel *channel, uint8_t state, int fd) { struct hal_ev_health_channel_state ev; DBG(""); bdaddr2android(&channel->dev->dst, ev.bdaddr); ev.app_id = channel->dev->app_id; ev.mdep_index = channel->mdep_id - 1; ev.channel_id = channel->id; ev.channel_state = state; ipc_send_notif_with_fd(hal_ipc, HAL_SERVICE_ID_HEALTH, HAL_EV_HEALTH_CHANNEL_STATE, sizeof(ev), &ev, fd); } static void unref_mdl(struct health_channel *channel) { if (!channel || !channel->mdl) return; mcap_mdl_unref(channel->mdl); channel->mdl = NULL; channel->mdl_conn = false; } static void free_health_channel(void *data) { struct health_channel *channel = data; int fd; DBG("channel %p", channel); if (!channel) return; fd = mcap_mdl_get_fd(channel->mdl); if (fd >= 0) shutdown(fd, SHUT_RDWR); unref_mdl(channel); free(channel); } static void destroy_channel(void *data) { struct health_channel *channel = data; if (!channel) return; send_channel_state_notify(channel, HAL_HEALTH_CHANNEL_DESTROYED, -1); queue_remove(channel->dev->channels, channel); free_health_channel(channel); } static void unref_mcl(struct health_device *dev) { if (!dev || !dev->mcl) return; mcap_close_mcl(dev->mcl, FALSE); mcap_mcl_unref(dev->mcl); dev->mcl = NULL; } static void free_health_device(void *data) { struct health_device *dev = data; if (!dev) return; unref_mcl(dev); queue_destroy(dev->channels, free_health_channel); free(dev); } static void free_mdep_cfg(void *data) { struct mdep_cfg *cfg = data; if (!cfg) return; free(cfg->descr); free(cfg); } static void free_health_app(void *data) { struct health_app *app = data; if (!app) return; free(app->app_name); free(app->provider_name); free(app->service_name); free(app->service_descr); queue_destroy(app->mdeps, free_mdep_cfg); queue_destroy(app->devices, free_health_device); free(app); } static bool match_channel_by_mdl(const void *data, const void *user_data) { const struct health_channel *channel = data; const struct mcap_mdl *mdl = user_data; return channel->mdl == mdl; } static bool match_channel_by_id(const void *data, const void *user_data) { const struct health_channel *channel = data; uint16_t channel_id = PTR_TO_INT(user_data); return channel->id == channel_id; } static bool match_dev_by_mcl(const void *data, const void *user_data) { const struct health_device *dev = data; const struct mcap_mcl *mcl = user_data; return dev->mcl == mcl; } static bool match_dev_by_addr(const void *data, const void *user_data) { const struct health_device *dev = data; const bdaddr_t *addr = user_data; return !bacmp(&dev->dst, addr); } static bool match_channel_by_mdep_id(const void *data, const void *user_data) { const struct health_channel *channel = data; uint16_t mdep_id = PTR_TO_INT(user_data); return channel->mdep_id == mdep_id; } static bool match_mdep_by_role(const void *data, const void *user_data) { const struct mdep_cfg *mdep = data; uint16_t role = PTR_TO_INT(user_data); return mdep->role == role; } static bool match_mdep_by_id(const void *data, const void *user_data) { const struct mdep_cfg *mdep = data; uint16_t mdep_id = PTR_TO_INT(user_data); return mdep->id == mdep_id; } static bool match_app_by_id(const void *data, const void *user_data) { const struct health_app *app = data; uint16_t app_id = PTR_TO_INT(user_data); return app->id == app_id; } static struct health_channel *search_channel_by_id(uint16_t id) { const struct queue_entry *apps_entry, *devices_entry; struct health_app *app; struct health_channel *channel; struct health_device *dev; DBG(""); apps_entry = queue_get_entries(apps); while (apps_entry) { app = apps_entry->data; devices_entry = queue_get_entries(app->devices); while (devices_entry) { dev = devices_entry->data; channel = queue_find(dev->channels, match_channel_by_id, INT_TO_PTR(id)); if (channel) return channel; devices_entry = devices_entry->next; } apps_entry = apps_entry->next; } return NULL; } static struct health_channel *search_channel_by_mdl(struct mcap_mdl *mdl) { const struct queue_entry *apps_entry, *devices_entry; struct health_app *app; struct health_channel *channel; struct health_device *dev; DBG(""); apps_entry = queue_get_entries(apps); while (apps_entry) { app = apps_entry->data; devices_entry = queue_get_entries(app->devices); while (devices_entry) { dev = devices_entry->data; channel = queue_find(dev->channels, match_channel_by_mdl, mdl); if (channel) return channel; devices_entry = devices_entry->next; } apps_entry = apps_entry->next; } return NULL; } static struct health_device *search_dev_by_mcl(struct mcap_mcl *mcl) { const struct queue_entry *apps_entry; struct health_app *app; struct health_device *dev; DBG(""); apps_entry = queue_get_entries(apps); while (apps_entry) { app = apps_entry->data; dev = queue_find(app->devices, match_dev_by_mcl, mcl); if (dev) return dev; apps_entry = apps_entry->next; } return NULL; } static struct health_app *search_app_by_mdepid(uint8_t mdepid) { const struct queue_entry *apps_entry; struct health_app *app; DBG(""); apps_entry = queue_get_entries(apps); while (apps_entry) { app = apps_entry->data; if (queue_find(app->mdeps, match_mdep_by_id, INT_TO_PTR(mdepid))) return app; apps_entry = apps_entry->next; } return NULL; } static int register_service_protocols(sdp_record_t *rec, struct health_app *app) { uuid_t l2cap_uuid, mcap_c_uuid; sdp_list_t *l2cap_list, *proto_list = NULL, *mcap_list = NULL; sdp_list_t *access_proto_list = NULL; sdp_data_t *psm = NULL, *mcap_ver = NULL; uint32_t ccpsm; uint16_t version = MCAP_VERSION; GError *err = NULL; int ret = -1; DBG(""); /* set l2cap information */ sdp_uuid16_create(&l2cap_uuid, L2CAP_UUID); l2cap_list = sdp_list_append(NULL, &l2cap_uuid); if (!l2cap_list) goto fail; ccpsm = mcap_get_ctrl_psm(mcap, &err); if (err) goto fail; psm = sdp_data_alloc(SDP_UINT16, &ccpsm); if (!psm) goto fail; if (!sdp_list_append(l2cap_list, psm)) goto fail; proto_list = sdp_list_append(NULL, l2cap_list); if (!proto_list) goto fail; /* set mcap information */ sdp_uuid16_create(&mcap_c_uuid, MCAP_CTRL_UUID); mcap_list = sdp_list_append(NULL, &mcap_c_uuid); if (!mcap_list) goto fail; mcap_ver = sdp_data_alloc(SDP_UINT16, &version); if (!mcap_ver) goto fail; if (!sdp_list_append(mcap_list, mcap_ver)) goto fail; if (!sdp_list_append(proto_list, mcap_list)) goto fail; /* attach protocol information to service record */ access_proto_list = sdp_list_append(NULL, proto_list); if (!access_proto_list) goto fail; sdp_set_access_protos(rec, access_proto_list); ret = 0; fail: sdp_list_free(l2cap_list, NULL); sdp_list_free(mcap_list, NULL); sdp_list_free(proto_list, NULL); sdp_list_free(access_proto_list, NULL); if (psm) sdp_data_free(psm); if (mcap_ver) sdp_data_free(mcap_ver); if (err) g_error_free(err); return ret; } static int register_service_profiles(sdp_record_t *rec) { int ret; sdp_list_t *profile_list; sdp_profile_desc_t hdp_profile; DBG(""); /* set hdp information */ sdp_uuid16_create(&hdp_profile.uuid, HDP_SVCLASS_ID); hdp_profile.version = HDP_VERSION; profile_list = sdp_list_append(NULL, &hdp_profile); if (!profile_list) return -1; /* set profile descriptor list */ ret = sdp_set_profile_descs(rec, profile_list); sdp_list_free(profile_list, NULL); return ret; } static int register_service_additional_protocols(sdp_record_t *rec, struct health_app *app) { int ret = -1; uuid_t l2cap_uuid, mcap_d_uuid; sdp_list_t *l2cap_list, *proto_list = NULL, *mcap_list = NULL; sdp_list_t *access_proto_list = NULL; sdp_data_t *psm = NULL; uint32_t dcpsm; GError *err = NULL; DBG(""); /* set l2cap information */ sdp_uuid16_create(&l2cap_uuid, L2CAP_UUID); l2cap_list = sdp_list_append(NULL, &l2cap_uuid); if (!l2cap_list) goto fail; dcpsm = mcap_get_data_psm(mcap, &err); if (err) goto fail; psm = sdp_data_alloc(SDP_UINT16, &dcpsm); if (!psm) goto fail; if (!sdp_list_append(l2cap_list, psm)) goto fail; proto_list = sdp_list_append(NULL, l2cap_list); if (!proto_list) goto fail; /* set mcap information */ sdp_uuid16_create(&mcap_d_uuid, MCAP_DATA_UUID); mcap_list = sdp_list_append(NULL, &mcap_d_uuid); if (!mcap_list) goto fail; if (!sdp_list_append(proto_list, mcap_list)) goto fail; /* attach protocol information to service record */ access_proto_list = sdp_list_append(NULL, proto_list); if (!access_proto_list) goto fail; sdp_set_add_access_protos(rec, access_proto_list); ret = 0; fail: sdp_list_free(l2cap_list, NULL); sdp_list_free(mcap_list, NULL); sdp_list_free(proto_list, NULL); sdp_list_free(access_proto_list, NULL); if (psm) sdp_data_free(psm); if (err) g_error_free(err); return ret; } static sdp_list_t *mdeps_to_sdp_features(struct mdep_cfg *mdep) { sdp_data_t *mdepid, *dtype = NULL, *role = NULL, *descr = NULL; sdp_list_t *f_list = NULL; DBG(""); mdepid = sdp_data_alloc(SDP_UINT8, &mdep->id); if (!mdepid) return NULL; dtype = sdp_data_alloc(SDP_UINT16, &mdep->data_type); if (!dtype) goto fail; role = sdp_data_alloc(SDP_UINT8, &mdep->role); if (!role) goto fail; if (mdep->descr) { descr = sdp_data_alloc(SDP_TEXT_STR8, mdep->descr); if (!descr) goto fail; } f_list = sdp_list_append(NULL, mdepid); if (!f_list) goto fail; if (!sdp_list_append(f_list, dtype)) goto fail; if (!sdp_list_append(f_list, role)) goto fail; if (descr && !sdp_list_append(f_list, descr)) goto fail; return f_list; fail: sdp_list_free(f_list, NULL); if (mdepid) sdp_data_free(mdepid); if (dtype) sdp_data_free(dtype); if (role) sdp_data_free(role); if (descr) sdp_data_free(descr); return NULL; } static void free_hdp_list(void *list) { sdp_list_t *hdp_list = list; sdp_list_free(hdp_list, (sdp_free_func_t)sdp_data_free); } static void register_features(void *data, void *user_data) { struct mdep_cfg *mdep = data; sdp_list_t **sup_features = user_data; sdp_list_t *hdp_feature; DBG(""); hdp_feature = mdeps_to_sdp_features(mdep); if (!hdp_feature) return; if (!*sup_features) { *sup_features = sdp_list_append(NULL, hdp_feature); if (!*sup_features) sdp_list_free(hdp_feature, (sdp_free_func_t)sdp_data_free); } else if (!sdp_list_append(*sup_features, hdp_feature)) { sdp_list_free(hdp_feature, (sdp_free_func_t)sdp_data_free); } } static int register_service_sup_features(sdp_record_t *rec, struct health_app *app) { sdp_list_t *sup_features = NULL; DBG(""); queue_foreach(app->mdeps, register_features, &sup_features); if (!sup_features) return -1; if (sdp_set_supp_feat(rec, sup_features) < 0) { sdp_list_free(sup_features, free_hdp_list); return -1; } sdp_list_free(sup_features, free_hdp_list); return 0; } static int register_data_exchange_spec(sdp_record_t *rec) { sdp_data_t *spec; uint8_t data_spec = DATA_EXCHANGE_SPEC_11073; /* As of now only 11073 is supported, so we set it as default */ DBG(""); spec = sdp_data_alloc(SDP_UINT8, &data_spec); if (!spec) return -1; if (sdp_attr_add(rec, SDP_ATTR_DATA_EXCHANGE_SPEC, spec) < 0) { sdp_data_free(spec); return -1; } return 0; } static int register_mcap_features(sdp_record_t *rec) { sdp_data_t *mcap_proc; uint8_t mcap_sup_proc = MCAP_SUP_PROC; DBG(""); mcap_proc = sdp_data_alloc(SDP_UINT8, &mcap_sup_proc); if (!mcap_proc) return -1; if (sdp_attr_add(rec, SDP_ATTR_MCAP_SUPPORTED_PROCEDURES, mcap_proc) < 0) { sdp_data_free(mcap_proc); return -1; } return 0; } static int set_sdp_services_uuid(sdp_record_t *rec, uint8_t role) { uuid_t source, sink; sdp_list_t *list = NULL; sdp_uuid16_create(&sink, HDP_SINK_SVCLASS_ID); sdp_uuid16_create(&source, HDP_SOURCE_SVCLASS_ID); sdp_get_service_classes(rec, &list); switch (role) { case HAL_HEALTH_MDEP_ROLE_SOURCE: if (!sdp_list_find(list, &source, sdp_uuid_cmp)) list = sdp_list_append(list, &source); break; case HAL_HEALTH_MDEP_ROLE_SINK: if (!sdp_list_find(list, &sink, sdp_uuid_cmp)) list = sdp_list_append(list, &sink); break; } if (sdp_set_service_classes(rec, list) < 0) { sdp_list_free(list, NULL); return -1; } sdp_list_free(list, NULL); return 0; } static int update_sdp_record(struct health_app *app) { sdp_record_t *rec; uint8_t role; DBG(""); if (record_id > 0) { bt_adapter_remove_record(record_id); record_id = 0; } rec = sdp_record_alloc(); if (!rec) return -1; role = HAL_HEALTH_MDEP_ROLE_SOURCE; if (queue_find(app->mdeps, match_mdep_by_role, INT_TO_PTR(role))) set_sdp_services_uuid(rec, role); role = HAL_HEALTH_MDEP_ROLE_SINK; if (queue_find(app->mdeps, match_mdep_by_role, INT_TO_PTR(role))) set_sdp_services_uuid(rec, role); sdp_set_info_attr(rec, app->service_name, app->provider_name, app->service_descr); if (register_service_protocols(rec, app) < 0) goto fail; if (register_service_profiles(rec) < 0) goto fail; if (register_service_additional_protocols(rec, app) < 0) goto fail; if (register_service_sup_features(rec, app) < 0) goto fail; if (register_data_exchange_spec(rec) < 0) goto fail; if (register_mcap_features(rec) < 0) goto fail; if (sdp_set_record_state(rec, record_state++) < 0) goto fail; if (bt_adapter_add_record(rec, SVC_HINT_HEALTH) < 0) { error("health: Failed to register HEALTH record"); goto fail; } record_id = rec->handle; return 0; fail: sdp_record_free(rec); return -1; } static struct health_app *create_health_app(const char *app_name, const char *provider, const char *srv_name, const char *srv_descr, uint8_t mdeps) { struct health_app *app; static unsigned int app_id = 1; DBG(""); app = new0(struct health_app, 1); app->id = app_id++; app->num_of_mdep = mdeps; app->app_name = strdup(app_name); if (provider) { app->provider_name = strdup(provider); if (!app->provider_name) goto fail; } if (srv_name) { app->service_name = strdup(srv_name); if (!app->service_name) goto fail; } if (srv_descr) { app->service_descr = strdup(srv_descr); if (!app->service_descr) goto fail; } app->mdeps = queue_new(); app->devices = queue_new(); return app; fail: free_health_app(app); return NULL; } static void bt_health_register_app(const void *buf, uint16_t len) { const struct hal_cmd_health_reg_app *cmd = buf; struct hal_rsp_health_reg_app rsp; struct health_app *app; uint16_t off; uint16_t app_name_len, provider_len, srv_name_len, srv_descr_len; char *app_name, *provider = NULL, *srv_name = NULL, *srv_descr = NULL; DBG(""); if (len != sizeof(*cmd) + cmd->len || cmd->app_name_off > cmd->provider_name_off || cmd->provider_name_off > cmd->service_name_off || cmd->service_name_off > cmd->service_descr_off || cmd->service_descr_off > cmd->len) { error("health: Invalid register app command, terminating"); raise(SIGTERM); return; } app_name = (char *) cmd->data; app_name_len = cmd->provider_name_off - cmd->app_name_off; off = app_name_len; provider_len = cmd->service_name_off - off; if (provider_len > 0) provider = (char *) cmd->data + off; off += provider_len; srv_name_len = cmd->service_descr_off - off; if (srv_name_len > 0) srv_name = (char *) cmd->data + off; off += srv_name_len; srv_descr_len = cmd->len - off; if (srv_descr_len > 0) srv_descr = (char *) cmd->data + off; app = create_health_app(app_name, provider, srv_name, srv_descr, cmd->num_of_mdep); if (!app) goto fail; queue_push_tail(apps, app); rsp.app_id = app->id; ipc_send_rsp_full(hal_ipc, HAL_SERVICE_ID_HEALTH, HAL_OP_HEALTH_REG_APP, sizeof(rsp), &rsp, -1); return; fail: free_health_app(app); ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_HEALTH, HAL_OP_HEALTH_MDEP, HAL_STATUS_FAILED); } static uint8_t android2channel_type(uint8_t type) { switch (type) { case HAL_HEALTH_CHANNEL_TYPE_RELIABLE: return CHANNEL_TYPE_RELIABLE; case HAL_HEALTH_CHANNEL_TYPE_STREAMING: return CHANNEL_TYPE_STREAM; default: return CHANNEL_TYPE_ANY; } } static void bt_health_mdep_cfg_data(const void *buf, uint16_t len) { const struct hal_cmd_health_mdep *cmd = buf; struct health_app *app; struct mdep_cfg *mdep = NULL; uint8_t status; DBG(""); app = queue_find(apps, match_app_by_id, INT_TO_PTR(cmd->app_id)); if (!app) { status = HAL_STATUS_INVALID; goto fail; } mdep = new0(struct mdep_cfg, 1); mdep->role = cmd->role; mdep->data_type = cmd->data_type; mdep->channel_type = android2channel_type(cmd->channel_type); mdep->id = queue_length(app->mdeps) + 1; if (cmd->descr_len > 0) { mdep->descr = malloc0(cmd->descr_len); memcpy(mdep->descr, cmd->descr, cmd->descr_len); } queue_push_tail(app->mdeps, mdep); if (app->num_of_mdep != queue_length(app->mdeps)) goto send_rsp; /* add sdp record from app configuration data */ /* * TODO: Check what to be done if mupltple applications are trying to * register with different role and different configurations. * 1) Does device supports SOURCE and SINK at the same time ? * 2) Does it require different SDP records or one record with * multile MDEP configurations ? */ if (update_sdp_record(app) < 0) { error("health: HDP SDP record preparation failed"); status = HAL_STATUS_FAILED; goto fail; } send_app_reg_notify(app, HAL_HEALTH_APP_REG_SUCCESS); send_rsp: ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_HEALTH, HAL_OP_HEALTH_MDEP, HAL_STATUS_SUCCESS); return; fail: if (status != HAL_STATUS_SUCCESS) { free_mdep_cfg(mdep); queue_remove(apps, app); free_health_app(app); } ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_HEALTH, HAL_OP_HEALTH_MDEP, status); } static void bt_health_unregister_app(const void *buf, uint16_t len) { const struct hal_cmd_health_unreg_app *cmd = buf; struct health_app *app; DBG(""); app = queue_remove_if(apps, match_app_by_id, INT_TO_PTR(cmd->app_id)); if (!app) { ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_HEALTH, HAL_OP_HEALTH_UNREG_APP, HAL_STATUS_INVALID); return; } send_app_reg_notify(app, HAL_HEALTH_APP_DEREG_SUCCESS); if (record_id > 0) { bt_adapter_remove_record(record_id); record_id = 0; } free_health_app(app); ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_HEALTH, HAL_OP_HEALTH_UNREG_APP, HAL_STATUS_SUCCESS); } static int get_prot_desc_entry(sdp_data_t *entry, int type, guint16 *val) { sdp_data_t *iter; int proto; if (!entry || !SDP_IS_SEQ(entry->dtd)) return -1; iter = entry->val.dataseq; if (!(iter->dtd & SDP_UUID_UNSPEC)) return -1; proto = sdp_uuid_to_proto(&iter->val.uuid); if (proto != type) return -1; if (!val) return 0; iter = iter->next; if (iter->dtd != SDP_UINT16) return -1; *val = iter->val.uint16; return 0; } static int get_prot_desc_list(const sdp_record_t *rec, uint16_t *psm, uint16_t *version) { sdp_data_t *pdl, *p0, *p1; if (!psm && !version) return -1; pdl = sdp_data_get(rec, SDP_ATTR_PROTO_DESC_LIST); if (!pdl || !SDP_IS_SEQ(pdl->dtd)) return -1; p0 = pdl->val.dataseq; if (get_prot_desc_entry(p0, L2CAP_UUID, psm) < 0) return -1; p1 = p0->next; if (get_prot_desc_entry(p1, MCAP_CTRL_UUID, version) < 0) return -1; return 0; } static int get_ccpsm(sdp_list_t *recs, uint16_t *ccpsm) { sdp_list_t *l; for (l = recs; l; l = l->next) { sdp_record_t *rec = l->data; if (!get_prot_desc_list(rec, ccpsm, NULL)) return 0; } return -1; } static int get_add_prot_desc_list(const sdp_record_t *rec, uint16_t *psm) { sdp_data_t *pdl, *p0, *p1; if (!psm) return -1; pdl = sdp_data_get(rec, SDP_ATTR_ADD_PROTO_DESC_LIST); if (!pdl || pdl->dtd != SDP_SEQ8) return -1; pdl = pdl->val.dataseq; if (pdl->dtd != SDP_SEQ8) return -1; p0 = pdl->val.dataseq; if (get_prot_desc_entry(p0, L2CAP_UUID, psm) < 0) return -1; p1 = p0->next; if (get_prot_desc_entry(p1, MCAP_DATA_UUID, NULL) < 0) return -1; return 0; } static int get_dcpsm(sdp_list_t *recs, uint16_t *dcpsm) { sdp_list_t *l; for (l = recs; l; l = l->next) { sdp_record_t *rec = l->data; if (!get_add_prot_desc_list(rec, dcpsm)) return 0; } return -1; } static int send_echo_data(int sock, const void *buf, uint32_t size) { const uint8_t *buf_b = buf; uint32_t sent = 0; while (sent < size) { int n = write(sock, buf_b + sent, size - sent); if (n < 0) return -1; sent += n; } return 0; } static gboolean serve_echo(GIOChannel *io, GIOCondition cond, gpointer data) { struct health_channel *channel = data; uint8_t buf[MCAP_DC_MTU]; int fd, len, ret; DBG("channel %p", channel); if (cond & (G_IO_ERR | G_IO_HUP | G_IO_NVAL)) { DBG("Error condition on channel"); return FALSE; } fd = g_io_channel_unix_get_fd(io); len = read(fd, buf, sizeof(buf)); if (len < 0) { DBG("Error reading ECHO"); return FALSE; } ret = send_echo_data(fd, buf, len); if (ret != len) DBG("Error sending ECHO back"); return FALSE; } static void mcap_mdl_connected_cb(struct mcap_mdl *mdl, void *data) { struct health_channel *channel = data; int fd; DBG("Data channel connected: mdl %p channel %p", mdl, channel); if (!channel) { channel = search_channel_by_mdl(mdl); if (!channel) { error("health: channel data does not exist"); return; } } if (!channel->mdl) channel->mdl = mcap_mdl_ref(mdl); fd = mcap_mdl_get_fd(channel->mdl); if (fd < 0) { error("health: error retrieving fd"); goto fail; } if (channel->mdep_id == MDEP_ECHO) { GIOChannel *io; io = g_io_channel_unix_new(fd); g_io_add_watch(io, G_IO_ERR | G_IO_HUP | G_IO_NVAL | G_IO_IN, serve_echo, channel); g_io_channel_unref(io); return; } info("health: MDL connected"); send_channel_state_notify(channel, HAL_HEALTH_CHANNEL_CONNECTED, fd); return; fail: /* TODO: mcap_mdl_abort */ destroy_channel(channel); } static void mcap_mdl_closed_cb(struct mcap_mdl *mdl, void *data) { struct health_channel *channel = data; info("health: MDL closed"); if (!channel) return; channel->mdl_conn = false; } static void mcap_mdl_deleted_cb(struct mcap_mdl *mdl, void *data) { struct health_channel *channel; info("health: MDL deleted"); channel = search_channel_by_mdl(mdl); if (!channel) return; DBG("channel %p mdl %p", channel, mdl); destroy_channel(channel); } static void mcap_mdl_aborted_cb(struct mcap_mdl *mdl, void *data) { DBG("Not Implemeneted"); } static struct health_device *create_device(struct health_app *app, const uint8_t *addr) { struct health_device *dev; /* create device and push it to devices queue */ dev = new0(struct health_device, 1); android2bdaddr(addr, &dev->dst); dev->channels = queue_new(); dev->app_id = app->id; queue_push_tail(app->devices, dev); return dev; } static struct health_device *get_device(struct health_app *app, const uint8_t *addr) { struct health_device *dev; bdaddr_t bdaddr; android2bdaddr(addr, &bdaddr); dev = queue_find(app->devices, match_dev_by_addr, &bdaddr); if (dev) return dev; return create_device(app, addr); } static struct health_channel *create_channel(struct health_app *app, uint8_t mdep_index, struct health_device *dev) { struct mdep_cfg *mdep; struct health_channel *channel; static unsigned int channel_id = 1; DBG("mdep %u", mdep_index); if (!dev || !app) return NULL; mdep = queue_find(app->mdeps, match_mdep_by_id, INT_TO_PTR(mdep_index)); if (!mdep) { if (mdep_index == MDEP_ECHO) { mdep = new0(struct mdep_cfg, 1); /* Leave other configuration zeroes */ mdep->id = MDEP_ECHO; queue_push_tail(app->mdeps, mdep); } else { return NULL; } } /* create channel and push it to device */ channel = new0(struct health_channel, 1); channel->mdep_id = mdep->id; channel->type = mdep->channel_type; channel->id = channel_id++; channel->dev = dev; queue_push_tail(dev->channels, channel); return channel; } static struct health_channel *connect_channel(struct health_app *app, struct mcap_mcl *mcl, uint8_t mdepid) { struct health_device *device; bdaddr_t addr; DBG("app %p mdepid %u", app, mdepid); mcap_mcl_get_addr(mcl, &addr); if (!app) { DBG("No app found for mdepid %u", mdepid); return NULL; } device = get_device(app, (uint8_t *) &addr); return create_channel(app, mdepid, device); } static uint8_t conf_to_l2cap(uint8_t conf) { return conf == CHANNEL_TYPE_STREAM ? L2CAP_MODE_STREAMING : L2CAP_MODE_ERTM; } static uint8_t mcap_mdl_conn_req_cb(struct mcap_mcl *mcl, uint8_t mdepid, uint16_t mdlid, uint8_t *conf, void *data) { GError *gerr = NULL; struct health_channel *channel; struct health_app *app; struct mdep_cfg *mdep; DBG("Data channel request: mdepid %u mdlid %u conf %u", mdepid, mdlid, *conf); if (mdepid == MDEP_ECHO) /* For echo service take last app */ app = queue_peek_tail(apps); else app = search_app_by_mdepid(mdepid); if (!app) return MCAP_MDL_BUSY; channel = connect_channel(app, mcl, mdepid); if (!channel) return MCAP_MDL_BUSY; /* Channel is assigned here after creation */ mcl->cb->user_data = channel; if (mdepid == MDEP_ECHO) { switch (*conf) { case CHANNEL_TYPE_ANY: *conf = CHANNEL_TYPE_RELIABLE; break; case CHANNEL_TYPE_RELIABLE: break; case CHANNEL_TYPE_STREAM: return MCAP_CONFIGURATION_REJECTED; default: /* * Special case defined in HDP spec 3.4. * When an invalid configuration is received we shall * close the MCL when we are still processing the * callback. */ /* TODO close device */ return MCAP_CONFIGURATION_REJECTED; /* not processed */ } if (!mcap_set_data_chan_mode(mcap, L2CAP_MODE_ERTM, &gerr)) { error("Error: %s", gerr->message); g_error_free(gerr); return MCAP_MDL_BUSY; } /* TODO: Create channel */ return MCAP_SUCCESS; } mdep = queue_find(app->mdeps, match_mdep_by_id, INT_TO_PTR(mdepid)); if (!mdep) return MCAP_MDL_BUSY; switch (*conf) { case CHANNEL_TYPE_ANY: if (mdep->role == HAL_HEALTH_MDEP_ROLE_SINK) { return MCAP_CONFIGURATION_REJECTED; } else { if (queue_length(channel->dev->channels) <= 1) *conf = CHANNEL_TYPE_RELIABLE; else *conf = CHANNEL_TYPE_STREAM; } break; case CHANNEL_TYPE_STREAM: if (mdep->role == HAL_HEALTH_MDEP_ROLE_SOURCE) return MCAP_CONFIGURATION_REJECTED; break; case CHANNEL_TYPE_RELIABLE: if (mdep->role == HAL_HEALTH_MDEP_ROLE_SOURCE) return MCAP_CONFIGURATION_REJECTED; break; default: /* * Special case defined in HDP spec 3.4. When an invalid * configuration is received we shall close the MCL when * we are still processing the callback. */ /* TODO: close device */ return MCAP_CONFIGURATION_REJECTED; /* not processed */ } if (!mcap_set_data_chan_mode(mcap, conf_to_l2cap(*conf), &gerr)) { error("health: error setting L2CAP mode: %s", gerr->message); g_error_free(gerr); return MCAP_MDL_BUSY; } return MCAP_SUCCESS; } static uint8_t mcap_mdl_reconn_req_cb(struct mcap_mdl *mdl, void *data) { struct health_channel *channel; GError *err = NULL; DBG(""); channel = search_channel_by_mdl(mdl); if (!channel) { error("health: channel data does not exist"); return MCAP_UNSPECIFIED_ERROR; } if (!mcap_set_data_chan_mode(mcap, conf_to_l2cap(channel->type), &err)) { error("health: %s", err->message); g_error_free(err); return MCAP_MDL_BUSY; } return MCAP_SUCCESS; } static void connect_mdl_cb(struct mcap_mdl *mdl, GError *gerr, gpointer data) { struct health_channel *channel = data; int fd; DBG(""); if (gerr) { error("health: error connecting to MDL %s", gerr->message); goto fail; } fd = mcap_mdl_get_fd(channel->mdl); if (fd < 0) { error("health: error retrieving fd"); goto fail; } info("health: MDL connected"); channel->mdl_conn = true; /* first data channel should be reliable data channel */ if (!queue_length(channel->dev->channels)) if (channel->type != CHANNEL_TYPE_RELIABLE) goto fail; send_channel_state_notify(channel, HAL_HEALTH_CHANNEL_CONNECTED, fd); return; fail: /* TODO: mcap_mdl_abort */ destroy_channel(channel); } static void reconnect_mdl_cb(struct mcap_mdl *mdl, GError *gerr, gpointer data) { struct health_channel *channel = data; uint8_t mode; GError *err = NULL; DBG(""); if (gerr) { error("health: error reconnecting to MDL %s", gerr->message); goto fail; } channel->mdl_id = mcap_mdl_get_mdlid(mdl); if (channel->type == CHANNEL_TYPE_RELIABLE) mode = L2CAP_MODE_ERTM; else mode = L2CAP_MODE_STREAMING; if (!mcap_connect_mdl(channel->mdl, mode, channel->dev->dcpsm, connect_mdl_cb, channel, NULL, &err)) { error("health: error connecting to mdl"); g_error_free(err); goto fail; } return; fail: /* TODO: mcap_mdl_abort */ destroy_channel(channel); } static int reconnect_mdl(struct health_channel *channel) { GError *gerr = NULL; DBG(""); if (!channel) return -1; if (!mcap_reconnect_mdl(channel->mdl, reconnect_mdl_cb, channel, NULL, &gerr)){ error("health: reconnect failed %s", gerr->message); destroy_channel(channel); } return 0; } static void create_mdl_cb(struct mcap_mdl *mdl, uint8_t type, GError *gerr, gpointer data) { struct health_channel *channel = data; uint8_t mode; GError *err = NULL; DBG(""); if (gerr) { error("health: error creating MDL %s", gerr->message); goto fail; } if (channel->type == CHANNEL_TYPE_ANY && type != CHANNEL_TYPE_ANY) channel->type = type; /* * if requested channel type is not same as preferred * channel type from remote device, then abort the connection. */ if (channel->type != type) { /* TODO: abort mdl */ error("health: channel type requested %d preferred %d not same", channel->type, type); goto fail; } if (!channel->mdl) channel->mdl = mcap_mdl_ref(mdl); channel->type = type; channel->mdl_id = mcap_mdl_get_mdlid(mdl); if (channel->type == CHANNEL_TYPE_RELIABLE) mode = L2CAP_MODE_ERTM; else mode = L2CAP_MODE_STREAMING; if (!mcap_connect_mdl(channel->mdl, mode, channel->dev->dcpsm, connect_mdl_cb, channel, NULL, &err)) { error("health: error connecting to mdl"); g_error_free(err); goto fail; } return; fail: destroy_channel(channel); } static bool check_role(uint8_t rec_role, uint8_t app_role) { if ((rec_role == HAL_HEALTH_MDEP_ROLE_SINK && app_role == HAL_HEALTH_MDEP_ROLE_SOURCE) || (rec_role == HAL_HEALTH_MDEP_ROLE_SOURCE && app_role == HAL_HEALTH_MDEP_ROLE_SINK)) return true; return false; } static bool get_mdep_from_rec(const sdp_record_t *rec, uint8_t role, uint16_t d_type, uint8_t *mdep) { sdp_data_t *list, *feat; if (!mdep) return false; list = sdp_data_get(rec, SDP_ATTR_SUPPORTED_FEATURES_LIST); if (!list || !SDP_IS_SEQ(list->dtd)) return false; for (feat = list->val.dataseq; feat; feat = feat->next) { sdp_data_t *data_type, *mdepid, *role_t; if (!SDP_IS_SEQ(feat->dtd)) continue; mdepid = feat->val.dataseq; if (!mdepid) continue; data_type = mdepid->next; if (!data_type) continue; role_t = data_type->next; if (!role_t) continue; if (data_type->dtd != SDP_UINT16 || mdepid->dtd != SDP_UINT8 || role_t->dtd != SDP_UINT8) continue; if (data_type->val.uint16 != d_type || !check_role(role_t->val.uint8, role)) continue; *mdep = mdepid->val.uint8; return true; } return false; } static bool get_remote_mdep(sdp_list_t *recs, struct health_channel *channel) { struct health_app *app; struct mdep_cfg *mdep; uint8_t mdep_id; app = queue_find(apps, match_app_by_id, INT_TO_PTR(channel->dev->app_id)); if (!app) return false; mdep = queue_find(app->mdeps, match_mdep_by_id, INT_TO_PTR(channel->mdep_id)); if (!mdep) return false; if (!get_mdep_from_rec(recs->data, mdep->role, mdep->data_type, &mdep_id)) { error("health: no matching MDEP: %u", channel->mdep_id); return false; } channel->remote_mdep = mdep_id; return true; } static bool create_mdl(struct health_channel *channel) { struct health_app *app; struct mdep_cfg *mdep; uint8_t type; GError *gerr = NULL; app = queue_find(apps, match_app_by_id, INT_TO_PTR(channel->dev->app_id)); if (!app) return false; mdep = queue_find(app->mdeps, match_mdep_by_id, INT_TO_PTR(channel->mdep_id)); if (!mdep) return false; if (mdep->role == HAL_HEALTH_MDEP_ROLE_SOURCE) type = channel->type; else type = CHANNEL_TYPE_ANY; if (!mcap_create_mdl(channel->dev->mcl, channel->remote_mdep, type, create_mdl_cb, channel, NULL, &gerr)) { error("health: error creating mdl %s", gerr->message); g_error_free(gerr); return false; } return true; } static bool set_mcl_cb(struct mcap_mcl *mcl, gpointer user_data, GError **err) { return mcap_mcl_set_cb(mcl, user_data, err, MCAP_MDL_CB_CONNECTED, mcap_mdl_connected_cb, MCAP_MDL_CB_CLOSED, mcap_mdl_closed_cb, MCAP_MDL_CB_DELETED, mcap_mdl_deleted_cb, MCAP_MDL_CB_ABORTED, mcap_mdl_aborted_cb, MCAP_MDL_CB_REMOTE_CONN_REQ, mcap_mdl_conn_req_cb, MCAP_MDL_CB_REMOTE_RECONN_REQ, mcap_mdl_reconn_req_cb, MCAP_MDL_CB_INVALID); } static void create_mcl_cb(struct mcap_mcl *mcl, GError *err, gpointer data) { struct health_channel *channel = data; gboolean ret; GError *gerr = NULL; DBG(""); if (err) { error("health: error creating MCL : %s", err->message); goto fail; } if (!channel->dev->mcl) channel->dev->mcl = mcap_mcl_ref(mcl); info("health: MCL connected"); ret = set_mcl_cb(channel->dev->mcl, channel, &gerr); if (!ret) { error("health: error setting mdl callbacks: %s", gerr->message); g_error_free(gerr); goto fail; } if (!create_mdl(channel)) goto fail; return; fail: destroy_channel(channel); } static void search_cb(sdp_list_t *recs, int err, gpointer data) { struct health_channel *channel = data; GError *gerr = NULL; DBG(""); if (err < 0 || !recs) { error("health: Error getting remote SDP records"); goto fail; } if (get_ccpsm(recs, &channel->dev->ccpsm) < 0) { error("health: Can't get remote PSM for control channel"); goto fail; } if (get_dcpsm(recs, &channel->dev->dcpsm) < 0) { error("health: Can't get remote PSM for data channel"); goto fail; } if (!get_remote_mdep(recs, channel)) { error("health: Can't get remote MDEP data"); goto fail; } if (!mcap_create_mcl(mcap, &channel->dev->dst, channel->dev->ccpsm, create_mcl_cb, channel, NULL, &gerr)) { error("health: error creating mcl %s", gerr->message); g_error_free(gerr); goto fail; } return; fail: destroy_channel(channel); } static int connect_mcl(struct health_channel *channel) { uuid_t uuid; int err; DBG(""); bt_string2uuid(&uuid, HDP_UUID); err = bt_search_service(&adapter_addr, &channel->dev->dst, &uuid, search_cb, channel, NULL, 0); if (!err) send_channel_state_notify(channel, HAL_HEALTH_CHANNEL_CONNECTING, -1); return err; } static struct health_app *get_app(uint16_t app_id) { return queue_find(apps, match_app_by_id, INT_TO_PTR(app_id)); } static struct health_channel *get_channel(struct health_app *app, uint8_t mdep_index, struct health_device *dev) { struct health_channel *channel; uint8_t index; if (!dev) return NULL; index = mdep_index + 1; channel = queue_find(dev->channels, match_channel_by_mdep_id, INT_TO_PTR(index)); if (channel) return channel; return create_channel(app, index, dev); } static void bt_health_connect_channel(const void *buf, uint16_t len) { const struct hal_cmd_health_connect_channel *cmd = buf; struct hal_rsp_health_connect_channel rsp; struct health_device *dev = NULL; struct health_channel *channel = NULL; struct health_app *app; DBG(""); app = get_app(cmd->app_id); if (!app) goto send_rsp; dev = get_device(app, cmd->bdaddr); channel = get_channel(app, cmd->mdep_index, dev); if (!channel) goto send_rsp; if (!queue_length(dev->channels)) { if (channel->type != CHANNEL_TYPE_RELIABLE) { error("health: first data shannel should be reliable"); goto fail; } } if (!dev->mcl) { if (connect_mcl(channel) < 0) { error("health: error retrieving HDP SDP record"); goto fail; } } else { /* data channel is already connected */ if (channel->mdl && channel->mdl_conn) goto fail; /* create mdl if it does not exists */ if (!channel->mdl && !create_mdl(channel)) goto fail; /* reconnect mdl if it exists */ if (channel->mdl && !channel->mdl_conn) { if (reconnect_mdl(channel) < 0) goto fail; } } rsp.channel_id = channel->id; ipc_send_rsp_full(hal_ipc, HAL_SERVICE_ID_HEALTH, HAL_OP_HEALTH_CONNECT_CHANNEL, sizeof(rsp), &rsp, -1); return; fail: queue_remove(channel->dev->channels, channel); free_health_channel(channel); send_rsp: ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_HEALTH, HAL_OP_HEALTH_CONNECT_CHANNEL, HAL_STATUS_FAILED); } static void channel_delete_cb(GError *gerr, gpointer data) { struct health_channel *channel = data; DBG(""); if (gerr) { error("health: channel delete failed %s", gerr->message); return; } destroy_channel(channel); } static void bt_health_destroy_channel(const void *buf, uint16_t len) { const struct hal_cmd_health_destroy_channel *cmd = buf; struct health_channel *channel; GError *gerr = NULL; DBG(""); channel = search_channel_by_id(cmd->channel_id); if (!channel) goto fail; if (!mcap_delete_mdl(channel->mdl, channel_delete_cb, channel, NULL, &gerr)) { error("health: channel delete failed %s", gerr->message); goto fail; } ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_HEALTH, HAL_OP_HEALTH_DESTROY_CHANNEL, HAL_STATUS_SUCCESS); return; fail: ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_HEALTH, HAL_OP_HEALTH_DESTROY_CHANNEL, HAL_STATUS_INVALID); } static const struct ipc_handler cmd_handlers[] = { /* HAL_OP_HEALTH_REG_APP */ { bt_health_register_app, true, sizeof(struct hal_cmd_health_reg_app) }, /* HAL_OP_HEALTH_MDEP */ { bt_health_mdep_cfg_data, true, sizeof(struct hal_cmd_health_mdep) }, /* HAL_OP_HEALTH_UNREG_APP */ { bt_health_unregister_app, false, sizeof(struct hal_cmd_health_unreg_app) }, /* HAL_OP_HEALTH_CONNECT_CHANNEL */ { bt_health_connect_channel, false, sizeof(struct hal_cmd_health_connect_channel) }, /* HAL_OP_HEALTH_DESTROY_CHANNEL */ { bt_health_destroy_channel, false, sizeof(struct hal_cmd_health_destroy_channel) }, }; static void mcl_connected(struct mcap_mcl *mcl, gpointer data) { GError *gerr = NULL; bool ret; DBG(""); info("health: MCL connected"); ret = set_mcl_cb(mcl, NULL, &gerr); if (!ret) { error("health: error setting mcl callbacks: %s", gerr->message); g_error_free(gerr); } } static void mcl_reconnected(struct mcap_mcl *mcl, gpointer data) { struct health_device *dev; DBG(""); info("health: MCL reconnected"); dev = search_dev_by_mcl(mcl); if (!dev) { error("device data does not exists"); return; } } static void mcl_disconnected(struct mcap_mcl *mcl, gpointer data) { struct health_device *dev; DBG(""); info("health: MCL disconnected"); dev = search_dev_by_mcl(mcl); unref_mcl(dev); } static void mcl_uncached(struct mcap_mcl *mcl, gpointer data) { /* mcap library maintains cache of mcls, not required here */ } bool bt_health_register(struct ipc *ipc, const bdaddr_t *addr, uint8_t mode) { GError *err = NULL; DBG(""); bacpy(&adapter_addr, addr); mcap = mcap_create_instance(&adapter_addr, BT_IO_SEC_MEDIUM, 0, 0, mcl_connected, mcl_reconnected, mcl_disconnected, mcl_uncached, NULL, /* CSP is not used right now */ NULL, &err); if (!mcap) { error("health: MCAP instance creation failed %s", err->message); g_error_free(err); return false; } hal_ipc = ipc; apps = queue_new(); ipc_register(hal_ipc, HAL_SERVICE_ID_HEALTH, cmd_handlers, G_N_ELEMENTS(cmd_handlers)); return true; } void bt_health_unregister(void) { DBG(""); mcap_instance_unref(mcap); queue_destroy(apps, free_health_app); ipc_unregister(hal_ipc, HAL_SERVICE_ID_HEALTH); hal_ipc = NULL; } bluez-5.82/android/PaxHeaders/Makefile.am0000644000000000000000000000005014572354773015343 xustar0020 atime=1743515718 20 ctime=1743591275 bluez-5.82/android/Makefile.am0000644000000000000000000002514314572354773015031 0ustar00rootrootif ANDROID AM_CPPFLAGS += -DANDROID_VERSION=0x050100 android_plugindir = $(abs_top_srcdir)/android/.libs noinst_PROGRAMS += android/system-emulator android_system_emulator_SOURCES = android/system-emulator.c android_system_emulator_LDADD = src/libshared-mainloop.la noinst_PROGRAMS += android/bluetoothd-snoop android_bluetoothd_snoop_SOURCES = android/bluetoothd-snoop.c src/log.c android_bluetoothd_snoop_LDADD = src/libshared-mainloop.la $(GLIB_LIBS) noinst_PROGRAMS += android/bluetoothd android_bluetoothd_SOURCES = android/main.c \ src/log.c \ android/hal-msg.h \ android/audio-msg.h \ android/sco-msg.h \ android/utils.h \ src/sdpd-database.c src/sdpd-server.c \ src/sdpd-service.c src/sdpd-request.c \ src/uuid-helper.h src/uuid-helper.c \ src/eir.h src/eir.c \ android/bluetooth.h android/bluetooth.c \ android/hidhost.h android/hidhost.c \ profiles/scanparam/scpp.h \ profiles/scanparam/scpp.c \ profiles/deviceinfo/dis.h \ profiles/deviceinfo/dis.c \ profiles/battery/bas.h profiles/battery/bas.c \ profiles/input/hog-lib.h \ profiles/input/hog-lib.c \ android/ipc-common.h \ android/ipc.h android/ipc.c \ android/avdtp.h android/avdtp.c \ android/a2dp.h android/a2dp.c \ android/a2dp-sink.h android/a2dp-sink.c \ android/avctp.h android/avctp.c \ android/avrcp.h android/avrcp.c \ android/avrcp-lib.h android/avrcp-lib.c \ android/socket.h android/socket.c \ android/sco.h android/sco.c \ android/pan.h android/pan.c \ android/handsfree.h android/handsfree.c \ android/handsfree-client.c android/handsfree-client.h \ android/gatt.h android/gatt.c \ android/health.h android/health.c \ profiles/health/mcap.h profiles/health/mcap.c \ android/map-client.h android/map-client.c \ attrib/att.c attrib/att.h \ attrib/gatt.c attrib/gatt.h \ attrib/gattrib.c attrib/gattrib.h \ btio/btio.h btio/btio.c \ src/sdp-client.h src/sdp-client.c \ profiles/network/bnep.h profiles/network/bnep.c android_bluetoothd_LDADD = lib/libbluetooth-internal.la \ src/libshared-glib.la $(GLIB_LIBS) plugin_LTLIBRARIES += android/bluetooth.default.la android_bluetooth_default_la_SOURCES = android/hal.h android/hal-bluetooth.c \ android/hal-socket.c \ android/hal-hidhost.c \ android/hal-health.c \ android/hal-pan.c \ android/hal-a2dp.c \ android/hal-a2dp-sink.c \ android/hal-avrcp.c \ android/hal-avrcp-ctrl.c \ android/hal-handsfree.c \ android/hal-handsfree-client.c \ android/hal-gatt.c \ android/hal-map-client.c \ android/hardware/bluetooth.h \ android/hardware/bt_av.h \ android/hardware/bt_gatt.h \ android/hardware/bt_gatt_client.h \ android/hardware/bt_gatt_server.h \ android/hardware/bt_gatt_types.h \ android/hardware/bt_hf.h \ android/hardware/bt_hh.h \ android/hardware/bt_hl.h \ android/hardware/bt_pan.h \ android/hardware/bt_rc.h \ android/hardware/bt_sock.h \ android/hardware/bt_hf_client.h \ android/hardware/bt_mce.h \ android/hardware/hardware.h \ android/cutils/properties.h \ android/ipc-common.h \ android/hal-log.h \ android/hal-ipc.h android/hal-ipc.c \ android/hal-utils.h android/hal-utils.c android_bluetooth_default_la_CFLAGS = $(AM_CFLAGS) -fvisibility=hidden android_bluetooth_default_la_CPPFLAGS = $(AM_CPPFLAGS) -I$(srcdir)/android android_bluetooth_default_la_LDFLAGS = $(AM_LDFLAGS) -module -avoid-version \ -no-undefined noinst_PROGRAMS += android/avdtptest android_avdtptest_SOURCES = android/avdtptest.c \ src/log.h src/log.c \ btio/btio.h btio/btio.c \ src/shared/util.h src/shared/util.c \ src/shared/queue.h src/shared/queue.c \ src/shared/log.h src/shared/log.c \ android/avdtp.h android/avdtp.c android_avdtptest_CFLAGS = $(AM_CFLAGS) android_avdtptest_LDADD = lib/libbluetooth-internal.la $(GLIB_LIBS) noinst_PROGRAMS += android/haltest android_haltest_SOURCES = android/client/haltest.c \ android/client/pollhandler.h \ android/client/pollhandler.c \ android/client/terminal.h \ android/client/terminal.c \ android/client/history.h \ android/client/history.c \ android/client/tabcompletion.c \ android/client/if-main.h \ android/client/if-av.c \ android/client/if-av-sink.c \ android/client/if-rc.c \ android/client/if-rc-ctrl.c \ android/client/if-bt.c \ android/client/if-gatt.c \ android/client/if-hf.c \ android/client/if-hf-client.c \ android/client/if-hh.c \ android/client/if-pan.c \ android/client/if-hl.c \ android/client/if-sock.c \ android/client/if-audio.c \ android/client/if-sco.c \ android/client/if-mce.c \ android/hardware/hardware.c \ android/hal-utils.h android/hal-utils.c android_haltest_CPPFLAGS = $(AM_CPPFLAGS) -I$(srcdir)/android \ -DPLUGINDIR=\""$(android_plugindir)"\" android_haltest_LDFLAGS = $(AM_LDFLAGS) -pthread android_haltest_LDADD = -ldl -lm noinst_PROGRAMS += android/android-tester android_android_tester_SOURCES = emulator/hciemu.h emulator/hciemu.c \ emulator/vhci.h emulator/vhci.c \ emulator/btdev.h emulator/btdev.c \ emulator/bthost.h emulator/bthost.c \ emulator/smp.c \ monitor/rfcomm.h \ android/hardware/hardware.c \ android/tester-bluetooth.c \ android/tester-socket.c \ android/tester-hidhost.c \ android/tester-pan.c \ android/tester-hdp.c \ android/tester-a2dp.c \ android/tester-avrcp.c \ android/tester-gatt.c \ android/tester-map-client.c \ android/tester-main.h android/tester-main.c android_android_tester_CPPFLAGS = $(AM_CPPFLAGS) -I$(srcdir)/android \ -DPLUGINDIR=\""$(android_plugindir)"\" android_android_tester_LDADD = lib/libbluetooth-internal.la \ src/libshared-glib.la $(GLIB_LIBS) -ldl android_android_tester_LDFLAGS = $(AM_LDFLAGS) -pthread noinst_PROGRAMS += android/ipc-tester android_ipc_tester_SOURCES = emulator/hciemu.h emulator/hciemu.c \ emulator/vhci.h emulator/vhci.c \ emulator/btdev.h emulator/btdev.c \ emulator/bthost.h emulator/bthost.c \ emulator/smp.c \ android/hal-utils.h android/hal-utils.c \ android/ipc-common.h android/ipc-tester.c android_ipc_tester_CPPFLAGS = $(AM_CPPFLAGS) -I$(srcdir)/android android_ipc_tester_LDADD = lib/libbluetooth-internal.la \ src/libshared-glib.la $(GLIB_LIBS) plugin_LTLIBRARIES += android/audio.a2dp.default.la android_audio_a2dp_default_la_SOURCES = android/audio-msg.h \ android/hal-msg.h \ android/hal-audio.h \ android/hal-audio.c \ android/hal-audio-sbc.c \ android/hal-audio-aptx.c \ android/hardware/audio.h \ android/hardware/audio_effect.h \ android/hardware/hardware.h \ android/system/audio.h android_audio_a2dp_default_la_CFLAGS = $(AM_CFLAGS) -fvisibility=hidden android_audio_a2dp_default_la_CPPFLAGS = $(AM_CPPFLAGS) -I$(srcdir)/android \ $(SBC_CFLAGS) android_audio_a2dp_default_la_LIBADD = $(SBC_LIBS) -lrt android_audio_a2dp_default_la_LDFLAGS = $(AM_LDFLAGS) -module -avoid-version \ -no-undefined -pthread plugin_LTLIBRARIES += android/audio.sco.default.la android_audio_sco_default_la_SOURCES = android/hal-log.h \ android/sco-msg.h \ android/hal-sco.c \ android/hardware/audio.h \ android/hardware/audio_effect.h \ android/hardware/hardware.h \ android/audio_utils/resampler.c \ android/audio_utils/resampler.h \ android/system/audio.h android_audio_sco_default_la_CFLAGS = $(AM_CFLAGS) -fvisibility=hidden android_audio_sco_default_la_CPPFLAGS = $(AM_CPPFLAGS) -I$(srcdir)/android android_audio_sco_default_la_LIBADD = $(SPEEXDSP_LIBS) -lrt android_audio_sco_default_la_LDFLAGS = $(AM_LDFLAGS) -module -avoid-version \ -no-undefined unit_tests += android/test-ipc android_test_ipc_SOURCES = android/test-ipc.c \ src/log.h src/log.c \ android/ipc-common.h \ android/ipc.c android/ipc.h android_test_ipc_LDADD = src/libshared-glib.la $(GLIB_LIBS) endif EXTRA_DIST += android/Android.mk android/README \ android/compat/readline/history.h \ android/compat/readline/readline.h \ android/compat/wordexp.h \ android/bluetoothd-wrapper.c \ android/log.c \ android/bluetoothd.te \ android/bluetoothd_snoop.te \ android/init.bluetooth.rc \ android/hal-ipc-api.txt \ android/audio-ipc-api.txt \ android/cts.txt \ android/pics-rfcomm.txt \ android/pics-spp.txt \ android/pics-sdp.txt \ android/pics-l2cap.txt \ android/pics-gap.txt \ android/pics-did.txt \ android/pics-hid.txt \ android/pics-pan.txt \ android/pics-opp.txt \ android/pics-map.txt \ android/pics-pbap.txt \ android/pics-a2dp.txt \ android/pics-avctp.txt \ android/pics-avrcp.txt \ android/pics-hsp.txt \ android/pics-hfp.txt \ android/pics-gatt.txt \ android/pics-mcap.txt \ android/pics-hdp.txt \ android/pics-iopt.txt \ android/pics-sm.txt \ android/pics-mps.txt \ android/pics-hogp.txt \ android/pics-scpp.txt \ android/pics-dis.txt \ android/pics-avdtp.txt \ android/pics-gavdp.txt \ android/pics-bnep.txt \ android/pixit-l2cap.txt \ android/pixit-gap.txt \ android/pixit-did.txt \ android/pixit-hid.txt \ android/pixit-pan.txt \ android/pixit-opp.txt \ android/pixit-map.txt \ android/pixit-pbap.txt \ android/pixit-a2dp.txt \ android/pixit-avctp.txt \ android/pixit-avrcp.txt \ android/pixit-hsp.txt \ android/pixit-hfp.txt \ android/pixit-gatt.txt \ android/pixit-mcap.txt \ android/pixit-hdp.txt \ android/pixit-iopt.txt \ android/pixit-sm.txt \ android/pixit-mps.txt \ android/pixit-hogp.txt \ android/pixit-scpp.txt \ android/pixit-dis.txt \ android/pixit-rfcomm.txt \ android/pixit-spp.txt \ android/pixit-avdtp.txt \ android/pixit-gavdp.txt \ android/pixit-sdp.txt \ android/pixit-bnep.txt \ android/pts-rfcomm.txt \ android/pts-spp.txt \ android/pts-l2cap.txt \ android/pts-gap.txt \ android/pts-did.txt \ android/pts-hid.txt \ android/pts-pan.txt \ android/pts-opp.txt \ android/pts-map.txt \ android/pts-a2dp.txt \ android/pts-avrcp.txt \ android/pts-avctp.txt \ android/pts-pbap.txt \ android/pts-hfp.txt \ android/pts-gatt.txt \ android/pts-hsp.txt \ android/pts-iopt.txt \ android/pts-hdp.txt \ android/pts-mcap.txt \ android/pts-mps.txt \ android/pts-sm.txt \ android/pts-hogp.txt \ android/pts-scpp.txt \ android/pts-dis.txt \ android/pts-avdtp.txt \ android/pts-gavdp.txt \ android/pts-sdp.txt \ android/pts-bnep.txt bluez-5.82/android/PaxHeaders/pts-bnep.txt0000644000000000000000000000005012537515745015575 xustar0020 atime=1743516876 20 ctime=1743591289 bluez-5.82/android/pts-bnep.txt0000644000000000000000000000451012537515745015256 0ustar00rootrootPTS test results for BNEP PTS version: 6.1 Tested: 11-May-2015 Android version: 5.1 Kernel version: 4.1 Results: PASS test passed FAIL test failed INC test is inconclusive N/A test is disabled due to PICS setup -------------------------------------------------------------------------------- Test Name Result Notes -------------------------------------------------------------------------------- TC_CTRL_BV_01_C PASS bneptest -s -b -n TC_CTRL_BV_02_C PASS bneptest -c -b -n TC_CTRL_BV_03_C PASS bneptest -s -b -n TC_CTRL_BV_04_C PASS PTS issue #13169 bneptest -s -b -n TC_CTRL_BV_05_C PASS PTS issue #13169 bneptest -s -b -n TC_CTRL_BV_06_C PASS PTS issue #13169 bneptest -s -b -n TC_CTRL_BV_07_C PASS PTS issue #13169 bneptest -c -b -n -t 3 -d 0 -e 1500 -y 1 TC_CTRL_BV_08_C PASS PTS issue #13169 bneptest -s -b -n TC_CTRL_BV_09_C PASS bneptest -c -b -n -t 5 -g 00:00:00:00:00:00 -j ff:ff:ff:ff:ff:ff -y 1 TC_CTRL_BV_10_C PASS PTS issue #13169 bneptest -s -b -n TC_CTRL_BV_19_C PASS bneptest -s -b -n TC_RX_TYPE_0_BV_11_C PASS PTS issue #13171 bneptest -s -b -n TC_RX_C_BV_12_C PASS PTS issue #13171 bneptest -s -b -n TC_RX_C_S_BV_13_C PASS PTS issue #13171 bneptest -s -b -n TC_RX_C_S_BV_14_C PASS PTS issue #13171 bneptest -s -b -n TC_RX_TYPE_0_BV_15_C PASS PTS issue #13169 bneptest -s -b -n TC_RX_TYPE_0_BV_16_C PASS PTS issue #13171 bneptest -s -b -n TC_RX_TYPE_0_BV_17_C PASS PTS issue #13169 bneptest -s -b -n TC_RX_TYPE_0_BV_18_C PASS PTS issue #13171 bneptest -s -b -n TC_TX_TYPE_0_BV_20_C PASS bneptest -c -b -n -w 0 -k -f TC_TX_C_BV_21_C PASS bneptest -c -b -n -w 2 -k -f TC_TX_C_S_BV_22_C PASS bneptest -c -b -n -w 3 -k -f TC_TX_C_D_BV_23_C PASS bneptest -c -b -n -w 4 -k -f bluez-5.82/android/PaxHeaders/cutils0000644000000000000000000000005014773213554014527 xustar0020 atime=1743591291 20 ctime=1743591276 bluez-5.82/android/cutils/0000755000000000000000000000000014773213554014265 5ustar00rootrootbluez-5.82/android/cutils/PaxHeaders/properties.h0000644000000000000000000000005014015011623017127 xustar0020 atime=1743516862 20 ctime=1743591276 bluez-5.82/android/cutils/properties.h0000644000000000000000000000322314015011623016610 0ustar00rootroot/* SPDX-License-Identifier: LGPL-2.1-or-later */ /* * * BlueZ - Bluetooth protocol stack for Linux * * Copyright (C) 2013 Intel Corporation. All rights reserved. * * */ #include #include #include #include #include #include #define PROPERTY_VALUE_MAX 32 #define PROPERTY_KEY_MAX 32 #define BLUETOOTH_MODE_PROPERTY_NAME "persist.sys.bluetooth.mode" #define BLUETOOTH_MODE_PROPERTY_HANDSFREE "persist.sys.bluetooth.handsfree" static inline int property_get(const char *key, char *value, const char *default_value) { const char *prop = NULL; if (!strcmp(key, BLUETOOTH_MODE_PROPERTY_NAME)) prop = getenv("BLUETOOTH_MODE"); if (!strcmp(key, BLUETOOTH_MODE_PROPERTY_HANDSFREE)) prop = getenv("BLUETOOTH_HANDSFREE_MODE"); if (!prop) prop = default_value; if (prop) { strncpy(value, prop, PROPERTY_VALUE_MAX); value[PROPERTY_VALUE_MAX - 1] = '\0'; return strlen(value); } return 0; } /* property_set: returns 0 on success, < 0 on failure */ static inline int property_set(const char *key, const char *value) { static const char SYSTEM_SOCKET_PATH[] = "\0android_system"; struct sockaddr_un addr; char msg[256]; int fd, len; fd = socket(PF_LOCAL, SOCK_DGRAM, 0); if (fd < 0) return -1; memset(&addr, 0, sizeof(addr)); addr.sun_family = AF_UNIX; memcpy(addr.sun_path, SYSTEM_SOCKET_PATH, sizeof(SYSTEM_SOCKET_PATH)); if (connect(fd, (struct sockaddr *) &addr, sizeof(addr)) < 0) { close(fd); return 0; } len = snprintf(msg, sizeof(msg), "%s=%s", key, value); if (send(fd, msg, len + 1, 0) < 0) { close(fd); return -1; } close(fd); return 0; } bluez-5.82/android/PaxHeaders/pixit-iopt.txt0000644000000000000000000000005012537515745016153 xustar0020 atime=1743516876 20 ctime=1743591289 bluez-5.82/android/pixit-iopt.txt0000644000000000000000000000144512537515745015640 0ustar00rootrootIOPT PIXIT for the PTS tool. PTS version: 6.1 * - different than PTS defaults & - should be set to IUT Bluetooth address Required PIXIT settings ------------------------------------------------------------------------------- Parameter Name Value ------------------------------------------------------------------------------- TSPX_security_enabled FALSE TSPX_delete_link_key FALSE TSPX_bd_addr_iut 112233445566 (*&) TSPX_class_of_device_pts 200404 TSPX_class_of_device_test_pts_initiator TRUE TSPX_limited_inquiry_used FALSE TSPX_pin_code 0000 TSPX_time_guard 200000 TSPX_device_search_time 20 TSPX_use_implicit_send TRUE TSPX_secure_simple_pairing_pass_key_confirmation FALSE ------------------------------------------------------------------------------- bluez-5.82/android/PaxHeaders/pics-did.txt0000644000000000000000000000005012537515745015541 xustar0020 atime=1743516876 20 ctime=1743591288 bluez-5.82/android/pics-did.txt0000644000000000000000000000127112537515745015223 0ustar00rootrootDID PICS for the PTS tool. PTS version: 6.1 * - different than PTS defaults # - not yet implemented/supported M - mandatory O - optional SDP Requirements ------------------------------------------------------------------------------- Parameter Name Selected Description ------------------------------------------------------------------------------- TSPC_DID_1_1 True Specification ID (M) TSPC_DID_1_2 True Vendor ID (M) TSPC_DID_1_3 True Product ID (M) TSPC_DID_1_4 True Version (M) TSPC_DID_1_5 True Primary Record (M) TSPC_DID_1_6 True Vendor ID Source (M) TSPC_ALL False Turns on all the test cases ------------------------------------------------------------------------------- bluez-5.82/android/PaxHeaders/pts-scpp.txt0000644000000000000000000000005012537515745015616 xustar0020 atime=1743516876 20 ctime=1743591289 bluez-5.82/android/pts-scpp.txt0000644000000000000000000000121312537515745015274 0ustar00rootrootPTS test results for ScPP PTS version: 6.1 Tested: 12-May-2015 Android version: 5.1 Results: PASS test passed FAIL test failed INC test is inconclusive N/A test is disabled due to PICS setup NONE test result is none ------------------------------------------------------------------------------- Test Name Result Notes ------------------------------------------------------------------------------- TC_SPDS_SC_BV_01_I PASS TC_SPDC_SC_BV_01_I PASS TC_SPDC_SC_BV_02_I PASS TC_SPDC_SC_BV_03_I PASS TC_SPWF_SC_BV_01_I PASS TC_SPCF_SC_BV_01_I PASS TC_SPNF_SC_BV_01_I PASS ------------------------------------------------------------------------------- bluez-5.82/android/PaxHeaders/pics-mps.txt0000644000000000000000000000005012537515745015600 xustar0020 atime=1743516876 20 ctime=1743591289 bluez-5.82/android/pics-mps.txt0000644000000000000000000003717312537515745015274 0ustar00rootrootMPS PICS for the PTS tool. PTS version: 6.1 * - different than PTS defaults # - not yet implemented/supported M - mandatory O - optional Profile Version ------------------------------------------------------------------------------- Parameter Name Selected Description ------------------------------------------------------------------------------- TSPC_MPS_0_1 True MPS v1.0 (M) ------------------------------------------------------------------------------- Profile Version Requirements ------------------------------------------------------------------------------- Parameter Name Selected Description ------------------------------------------------------------------------------- TSPC_MPS_1_1 True (*) A2DP 1.2 or later (O) TSPC_MPS_1_2 True (*) AVRCP 1.3 or later (O) TSPC_MPS_1_3 False DUN 1.1 or later (O) TSPC_MPS_1_4 True (*) HFP 1.5 or later (O) TSPC_MPS_1_5 True (*) PAN 1.0 or later (O) TSPC_MPS_1_6 True (*) PBAP 1.1 or later (O) ------------------------------------------------------------------------------- Profile Roles ------------------------------------------------------------------------------- Parameter Name Selected Description ------------------------------------------------------------------------------- TSPC_MPS_2_1 True (*) A2DP Source (SRC) (C.1) TSPC_MPS_2_2 False A2DP Sink (SNK) (C.1) TSPC_MPS_2_3 True (*) AVRCP Controller (CT) (C.1) TSPC_MPS_2_4 True (*) AVRCP Target (TG) (C.1) TSPC_MPS_2_5 False DUN Gateway (GW) (C.1) TSPC_MPS_2_6 False DUN Data Terminal (DT) (C.1) TSPC_MPS_2_7 True (*) HFP Audio Gateway (AG) (C.1) TSPC_MPS_2_8 False HFP Hands-Free (HF) (C.1) TSPC_MPS_2_9 True (*) PAN Network Access Point (NAP) (C.1) TSPC_MPS_2_10 False PAN Group Ad-hoc Network (GN) (C.1) TSPC_MPS_2_11 True (*) PAN User (PANU) (C.1) TSPC_MPS_2_12 False PBAP PCE (C.1) TSPC_MPS_2_13 True (*) PBAP PSE (C.1) ------------------------------------------------------------------------------- C.1: Mandatory to declare each role as supported within the represented Profile otherwise Excluded. The roles declared shall match that of the roles supported within the Profile. ------------------------------------------------------------------------------- Profile Features ------------------------------------------------------------------------------- Parameter Name Selected Description ------------------------------------------------------------------------------- TSPC_MPS_3_1 True (*) Receiving PASS THROUGH command in Category 1 (AVRCP - TG) (C.1) TSPC_MPS_3_2 True (*) Receiving PASS THROUGH command in Category 1 (AVRCP - TG) - PAUSE (C.1) TSPC_MPS_3_3 False Sending PASS THROUGH command in Category 1 (AVRCP - CT) - PLAY (C.2) TSPC_MPS_3_4 False Sending PASS THROUGH command in Category 1 (AVRCP - CT) - PAUSE (C.2) TSPC_MPS_3_5 True (*) Transfer Control - Suspend (GAVDP - Initiator) (C.3) TSPC_MPS_3_6 True (*) Transfer Control - Suspend (GAVDP - Acceptor) (C.4) TSPC_MPS_3_7 False Accept an incoming voice call (in-band ring) (C.5) TSPC_MPS_3_8 True (*) Accept an incoming voice call (no in-band ring) (C.5) TSPC_MPS_3_9 False Place a call with a phone number supplied by the HF (C.6) TSPC_MPS_3_10 True (*) Register Notification: PLAYBACK_STATUS_CHANGED (C.7) TSPC_MPS_3_11 True (*) Ability to support parallel data and call operation (O) TSPC_MPS_3_12 True (*) PBAP Phone Book Download (C.8) TSPC_MPS_3_13 True (*) Ability to support multiple concurrent device connections (O) ------------------------------------------------------------------------------- C.1: Mandatory if TSPC_MPS_2_1 (A2DP Source role) and TSPC_MPS_2_4 (AVRCP Target role) are supported, otherwise Excluded. C.2: Mandatory if TSPC_MPS_2_2 (A2DP Sink role) and TSPC_MPS_2_3 (AVRCP Controller role) are supported, otherwise Excluded. C.3: Mandatory if TSPC_MPS_1_4 (HFP 1.5 or later) and TSPC_MPS_2_1 (A2DP Source role) are supported; Optional if TSPC_MPS_1_4 (HFP 1.5 or later) and TSPC_MPS_2_2 (A2DP Sink role) are supported, otherwise Excluded. C.4: Mandatory if TSPC_MPS_1_4 (HFP 1.5 or later) and TSPC_MPS_2_1 (A2DP Source role) or TSPC_MPS_2_2 (A2DP Sink role) are supported, otherwise Excluded. C.5: Mandatory to support at least one if TSPC_MPS_1_4 (HFP 1.5 or later) is supported, otherwise Excluded. C.6: Mandatory if TSPC_MPS_1_4 (HFP 1.5 or later) and HFP 3/8 (Place a call with a phone number supplied by the HF) are supported, otherwise Excluded. C.7: Mandatory if TSPC_MPS_2_3 (AVRCP Controller role) is supported, otherwise Excluded. C.8: Mandatory if TSPC_MPS_1_6 (PBAP 1.1 or later) and PBAP 2/1 (Phone Book Download) are supported, otherwise Excluded. ------------------------------------------------------------------------------- Device Capability Support ------------------------------------------------------------------------------- Parameter Name Selected Description ------------------------------------------------------------------------------- TSPC_MPS_4_1 True Multiple Profiles Single Device (MPSD) (M) TSPC_MPS_4_2 True (*) Multiple Profiles Multiple Devices (MPMD) (C.1) ------------------------------------------------------------------------------- C.1: Mandatory if TSPC_MPS_3_13 (Ability to support multiple concurrent device connections), otherwise Excluded. ------------------------------------------------------------------------------- MPSD scenarios ------------------------------------------------------------------------------- Parameter Name Selected Description ------------------------------------------------------------------------------- TSPC_MPS_6_1 True (*) HFP-AG and A2DP-SRC Implementation Answer Incoming Call during Audio Streaming (C.1) TSPC_MPS_6_2 False HFP-HF and A2DP-SNK Implementation Answer Incoming Call during Audio Streaming (C.2) TSPC_MPS_6_3 True (*) HFP-AG and A2DP-SRC Implementation Outgoing Call during Audio Streaming (C.1) TSPC_MPS_6_4 False HFP-HF and A2DP-SNK Implementation Outgoing Call during Audio Streaming (C.2) TSPC_MPS_6_5 True (*) HFP-AG and A2DP-SRC Implementation Reject/Ignore Incoming Call during Audio Streaming (C.1) TSPC_MPS_6_6 False HFP-HF and A2DP-SNK Implementation Reject/Ignore Incoming Call during Audio Streaming (C.2) TSPC_MPS_6_7 True (*) HFP-AG and A2DP-SRC Implementation HFP Call Termination during AVP Connection (C.1) TSPC_MPS_6_8 False HFP-HF and A2DP-SNK Implementation HFP Call Termination during AVP Connection (C.2) TSPC_MPS_6_9 True (*) HFP-AG and A2DP-SRC Implementation Press Play on Audio Player during Active Call (C.1) TSPC_MPS_6_10 False HFP-HF and A2DP-SNK Implementation Press Play on Audio Player during Active Call (C.2) TSPC_MPS_6_11 True (*) HFP-AG and A2DP-SRC Implementation Start Audio Streaming after AVRCP Play Command (C.1) TSPC_MPS_6_12 False HFP-HF and A2DP-SNK Implementation Start Audio Streaming after AVRCP Play Command (C.2) TSPC_MPS_6_13 True (*) HFP-AG and A2DP-SRC Implementation Suspend Audio Streaming after AVRCP Pause/Stop (C.1) TSPC_MPS_6_14 False HFP-HF and A2DP-SNK Implementation Suspend Audio Streaming after AVRCP Pause/Stop (C.2) TSPC_MPS_6_15 False HFP-AG and DUN-GW Implementation Data Communication under PSDM (DUN) during Active Voice Call (C.3) TSPC_MPS_6_16 False HFP-HF and DUN-DT Implementation Data Communication under PSDM (DUN) during Active Voice call (C.4) TSPC_MPS_6_17 False HFP-AG and DUN-GW Implementation Outgoing Voice Call during Data Communication under PSDM (DUN) (C.3) TSPC_MPS_6_18 False HFP-HF and DUN-DT Implementation Outgoing Voice Call during Data Communication under PSDM (DUN) (C.4) TSPC_MPS_6_19 False HFP-AG and DUN-GW Implementation Incoming Voice Call during Data Communication under PSDM (DUN) (C.3) TSPC_MPS_6_20 False HFP-HF and DUN-DT Implementation Incoming Voice Call during Data Communication under PSDM (DUN) (C.4) TSPC_MPS_6_21 False A2DP-SRC and DUN-GW Implementation Start Audio Streaming during Data Communication under PSDM (DUN) (C.5) TSPC_MPS_6_22 False A2DP-SNK and DUN-DT Implementation Start Audio Streaming during Data Communication under PSDM (DUN) (C.6) TSPC_MPS_6_23 False A2DP-SRC and DUN-GW Implementation Data Communication Establishment under PSDM (DUN) during Audio Streaming (C.5) TSPC_MPS_6_24 False A2DP-SNK and DUN-DT Implementation Data Communication Establishment under PSDM (DUN) during Audio Streaming (C.6) TSPC_MPS_6_25 False HFP-AG and DUN-GW Implementation Terminate Voice Call/Data Call during Data Communication and Voice Call (C.5) TSPC_MPS_6_26 False HFP-HF and DUN-DT Implementation Terminate Voice Call/Data Call during Data Communication and Voice Call (C.6) TSPC_MPS_6_27 True (*) HFP-AG and PAN-NAP Implementation Data Communication in Personal Area Network during Active Voice Call (C.7) TSPC_MPS_6_28 False HFP-HF and PAN-PANU Implementation Data Communication in Personal Area Network during Active Voice Call (C.8) TSPC_MPS_6_29 True (*) HFP-AG and PAN-NAP Implementation Outgoing Voice Call during Data Communication in Personal Area Network (C.7) TSPC_MPS_6_30 False HFP-HF and PAN-PANU Implementation Outgoing Voice Call during Data Communication in Personal Area Network (C.8) TSPC_MPS_6_31 True (*) HFP-AG and PAN-NAP Implementation Incoming Voice Call during Data Communication in Personal Area Network (C.7) TSPC_MPS_6_32 False HFP-HF and PAN-PANU Implementation Incoming Voice Call during Data Communication in Personal Area Network (C.8) TSPC_MPS_6_33 True (*) A2DP-SRC and PAN-NAP Implementation Start Audio Streaming during Data Communication in Personal Area Network (C.9) TSPC_MPS_6_34 False A2DP-SNK and PAN-PANU Implementation Start Audio Streaming during Data Communication in Personal Area Network (C.10) TSPC_MPS_6_35 True (*) A2DP-SRC and PAN-NAP Implementation Data Communication Establishment in Personal Area Network during Audio Streaming (C.9) TSPC_MPS_6_36 False A2DP-SNK and PAN_PANU Implementation Data Communication Establishment in Personal Area Network during Audio Streaming (C.10) TSPC_MPS_6_37 True (*) A2DP-SRC_PBAP-Server Implementation Phonebook Download during Audio Streaming (C.11) TSPC_MPS_6_38 False A2DP-SNK and PBAP-Client Implementation Phonebook Download during Audio Streaming (C.12) TSPC_MPS_6_39 True (*) HFP-AG and PBAP-Server Implementation PBAP and HFP Connection Behaviour (C.13) ------------------------------------------------------------------------------- C.1: Mandatory if TSPC_MPS_2_1, TSPC_MPS_2_4 and TSPC_MPS_2_7 are supported, otherwise Excluded. C.2: Mandatory if TSPC_MPS_2_2, TSPC_MPS_2_3 and TSPC_MPS_2_8 are supported, otherwise Excluded. C.3: Mandatory if TSPC_MPS_2_5 and TSPC_MPS_2_7 are supported and TSPC_MPS_3_9, otherwise Excluded. C.4: Mandatory if TSPC_MPS_2_6 and TSPC_MPS_2_8 are supported, otherwise Excluded. C.5: Mandatory if TSPC_MPS_2_1 and TSPC_MPS_2_5 are supported, otherwise Excluded. C.6: Mandatory if TSPC_MPS_2_2 and TSPC_MPS_2_6 are supported, otherwise Excluded. C.7: Mandatory if TSPC_MPS_2_7 and TSPC_MPS_2_9 and TSPC_MPS_3_11 are supported, otherwise Excluded. C.8: Mandatory if TSPC_MPS_2_8 and TSPC_MPS_2_11 are supported and TSPC_MPS_3_11, otherwise Excluded. C.9: Mandatory if TSPC_MPS_2_1 and TSPC_MPS_2_9 are supported, otherwise Excluded. C.10: Mandatory if TSPC_MPS_2_2 and TSPC_MPS_2_11 are supported, otherwise Excluded. C.11: Mandatory if TSPC_MPS_2_1 and TSPC_MPS_2_13 are supported, otherwise Excluded. C.12: Mandatory if TSPC_MPS_2_2 and TSPC_MPS_2_12 are supported, otherwise Excluded. C.13: Mandatory if TSPC_MPS_2_7 and TSPC_MPS_2_13 are supported, otherwise Excluded. ------------------------------------------------------------------------------- MPMD Features ------------------------------------------------------------------------------- Parameter Name Selected Description ------------------------------------------------------------------------------- TSPC_MPS_7_1 False HFP-HF and A2DP-SNK and AVRCP-CT Implementation Answer Incoming Call during Audio Streaming (C.1) TSPC_MPS_7_2 True (*) A2DP-SRC and AVRCP-TG Implementation Answer Incoming Call during Audio Streaming (C.2) TSPC_MPS_7_3 False HFP-HF and A2DP-SNK and AVRCP-CT Implementation Outgoing Call during Audio Streaming (C.1) TSPC_MPS_7_4 True (*) A2DP-SRC and AVRCP-TG Implementation Outgoing Call during Audio Streaming (C.2) TSPC_MPS_7_5 False HFP-HF and A2DP-SNK and AVRCP-CT Implementation Reject/Ignore Incoming Call during Audio Streaming (C.1) TSPC_MPS_7_6 True (*) A2DP-SRC and AVRCP-TG Implementation Reject/Ignore Incoming Call during Audio Streaming (C.2) TSPC_MPS_7_7 False HFP-HF and A2DP-SNK and AVRCP-CT Implementation HFP Call Termination during AVP Connection (C.1) TSPC_MPS_7_8 True (*) A2DP-SRC and AVRCP-TG Implementation HFP Call Termination during AVP Connection (C.2) TSPC_MPS_7_9 False HFP-HF and A2DP-SNK and AVRCP-CT Implementation Press Play on Audio Player during Active Call (C.1) TSPC_MPS_7_10 True (*) A2DP-SRC and AVRCP-TG Implementation Press Play on Audio Player during Active Call (C.2) TSPC_MPS_7_11 True (*) A2DP-SRC and AVRCP-TG Implementation Start Audio Streaming during Data Communication under PSDM (C.2) TSPC_MPS_7_12 False A2DP-SNK and AVRCP-CT and DUN-DT Implementation Start Audio Streaming during Data Communication under PSDM (C.3) TSPC_MPS_7_13 True (*) A2DP-SRC and AVRCP-TG Implementation Start Packet Data Communication during Audio Streaming (C.2) TSPC_MPS_7_14 False A2DP-SNK and AVRCP-CT and DUN-DT Implementation Start Packet Data Communication during Audio Streaming (C.3) ------------------------------------------------------------------------------- C.1: Mandatory if TSPC_MPS_2_2, TSPC_MPS_2_3 and TSPC_MPS_2_8 are supported, otherwise Excluded. C.2: Mandatory if TSPC_MPS_2_1 and TSPC_MPS_2_4 are supported, otherwise Excluded. C.3: Mandatory if TSPC_MPS_2_2, TSPC_MPS_2_3 and 2/6TSPC_MPS_2_6 supported, otherwise Excluded. ------------------------------------------------------------------------------- MPS Procedures ------------------------------------------------------------------------------- Parameter Name Selected Description ------------------------------------------------------------------------------- TSPC_MPS_8_1 True (*) AVP Suspension (C.1) TSPC_MPS_8_2 True (*) Profile (Dis-)Connection behaviour (C.2) ------------------------------------------------------------------------------- C.1: Mandatory if TSPC_MPS_1_1 and TSPC_MPS_1_2 are supported, otherwise Excluded. C.2: Mandatory if TSPC_MPS_1_1, TSPC_MPS_1_2 and TSPC_MPS_1_4 are supported, otherwise Excluded. ------------------------------------------------------------------------------- MPS Dependencies ------------------------------------------------------------------------------- Parameter Name Selected Description ------------------------------------------------------------------------------- TSPC_MPS_9_1 True Implements Bluetooth Core Specification v2.1 + EDR or later (M) ------------------------------------------------------------------------------- MPS Requirements ------------------------------------------------------------------------------- Parameter Name Selected Description ------------------------------------------------------------------------------- TSPC_MPS_10_1 True SDP Record (M) TSPC_MPS_10_2 True (*) Media Stream Suspension (C.1) TSPC_MPS_10_3 True (*) Sniff Mode during Streaming (C.2) ------------------------------------------------------------------------------- C.1: Mandatory if TSPC_MPS_1_1 and TSPC_MPS_1_4 are supported, otherwise Excluded. C.2: Mandatory if TSPC_MPS_1_1 is supported, otherwise Excluded. ------------------------------------------------------------------------------- bluez-5.82/android/PaxHeaders/pixit-sm.txt0000644000000000000000000000005012537515745015617 xustar0020 atime=1743516876 20 ctime=1743591289 bluez-5.82/android/pixit-sm.txt0000644000000000000000000000442612537515745015306 0ustar00rootrootSM PIXIT for the PTS tool. PTS version: 6.1 * - different than PTS defaults & - should be set to IUT Bluetooth address Required PIXIT settings ------------------------------------------------------------------------------- Parameter Name Value ------------------------------------------------------------------------------- TSPX_bd_addr_iut 112233445566 (*&) TSPX_SMP_pin_code 111111 TSPX_OOB_Data 000000000000FE12036E5A 889F4D TSPX_peer_addr_type 00 TSPX_own_addr_type 00 TSPX_conn_interval_min 0190 TSPX_conn_interval_max 0190 TSPX_con_latency 0000 TSPX_client_class_of_device 100104 TSPX_server_class_of_device 100104 TSPX_security_enabled TRUE TSPX_delete_link_key TRUE TSPX_pin_code 1234 TSPX_ATTR_HANDLE 0000 TSPX_ATTR_VALUE 000000000000000 TSPX_delay_variation_in FFFFFFFF TSPX_delay_variation_out FFFFFFFF TSPX_flushto FFFF TSPX_inmtu 02A0 TSPX_inquiry_length 17 TSPX_latency_in FFFFFFFF TSPX_latency_out FFFFFFFF TSPX_linkto 3000 TSPX_max_nbr_retransmission 10 TSPX_no_fail_verdicts FALSE TSPX_outmtu 02A0 TSPX_tester_role_optional L2CAP_ROLE_INITIATOR TSPX_page_scan_mode 00 TSPX_page_scan_repetition_mode 00 TSPX_peak_bandwidth_in 00000000 TSPX_peak_bandwidth_out 00000000 TSPX_psm 0011 TSPX_service_type_in 01 TSPX_service_type_out 01 TSPX_support_retransmissions TRUE TSPX_time_guard 180000 TSPX_timer_ertx 120000 TSPX_timer_ertx_max 300000 TSPX_timer_ertx_min 60000 TSPX_timer_rtx 10000 TSPX_timer_rtx_max 60000 TSPX_timer_rtx_min 1000 TSPX_token_bucket_size_in 00000000 TSPX_token_bucket_size_out 00000000 TSPX_token_rate_in 00000000 TSPX_token_rate_out 00000000 TSPX_rfc_mode_mode 03 TSPX_rfc_mode_tx_window_size 08 TSPX_rfc_mode_max_transmit 03 TSPX_rfc_mode_retransmission_timeout 07D0 TSPX_rfc_mode_monitor_timeout 2EE0 TSPX_rfc_mode_maximum_pdu_size 02A0 TSPX_extended_window_size 0012 TSPX_use_implicit_send TRUE TSPX_use_dynamic_pin FALSE TSPX_iut_SDU_size_in_bytes 144 TSPX_secure_simple_pairing_pass_key_confirmation FALSE TSPX_Min_Encryption_Key_Length 07 TSPX_Bonding_Flags 00 ------------------------------------------------------------------------------- bluez-5.82/android/PaxHeaders/audio_utils0000644000000000000000000000005014773213554015545 xustar0020 atime=1743591291 20 ctime=1743591276 bluez-5.82/android/audio_utils/0000755000000000000000000000000014773213554015303 5ustar00rootrootbluez-5.82/android/audio_utils/PaxHeaders/resampler.h0000644000000000000000000000005014015011623017743 xustar0020 atime=1743516862 20 ctime=1743591276 bluez-5.82/android/audio_utils/resampler.h0000644000000000000000000000571014015011623017427 0ustar00rootroot/* SPDX-License-Identifier: Apache-2.0 */ /* ** Copyright 2008, The Android Open-Source Project ** */ #ifndef ANDROID_RESAMPLER_H #define ANDROID_RESAMPLER_H #include #include __BEGIN_DECLS #define RESAMPLER_QUALITY_MAX 10 #define RESAMPLER_QUALITY_MIN 0 #define RESAMPLER_QUALITY_DEFAULT 4 #define RESAMPLER_QUALITY_VOIP 3 #define RESAMPLER_QUALITY_DESKTOP 5 struct resampler_buffer { union { void* raw; short* i16; int8_t* i8; }; size_t frame_count; }; /* call back interface used by the resampler to get new data */ struct resampler_buffer_provider { /** * get a new buffer of data: * as input: buffer->frame_count is the number of frames requested * as output: buffer->frame_count is the number of frames returned * buffer->raw points to data returned */ int (*get_next_buffer)(struct resampler_buffer_provider *provider, struct resampler_buffer *buffer); /** * release a consumed buffer of data: * as input: buffer->frame_count is the number of frames released * buffer->raw points to data released */ void (*release_buffer)(struct resampler_buffer_provider *provider, struct resampler_buffer *buffer); }; /* resampler interface */ struct resampler_itfe { /** * reset resampler state */ void (*reset)(struct resampler_itfe *resampler); /** * resample input from buffer provider and output at most *outFrameCount to out buffer. * *outFrameCount is updated with the actual number of frames produced. */ int (*resample_from_provider)(struct resampler_itfe *resampler, int16_t *out, size_t *outFrameCount); /** * resample at most *inFrameCount frames from in buffer and output at most * *outFrameCount to out buffer. *inFrameCount and *outFrameCount are updated respectively * with the number of frames remaining in input and written to output. */ int (*resample_from_input)(struct resampler_itfe *resampler, int16_t *in, size_t *inFrameCount, int16_t *out, size_t *outFrameCount); /** * return the latency introduced by the resampler in ns. */ int32_t (*delay_ns)(struct resampler_itfe *resampler); }; /** * create a resampler according to input parameters passed. * If resampler_buffer_provider is not NULL only resample_from_provider() can be called. * If resampler_buffer_provider is NULL only resample_from_input() can be called. */ int create_resampler(uint32_t inSampleRate, uint32_t outSampleRate, uint32_t channelCount, uint32_t quality, struct resampler_buffer_provider *provider, struct resampler_itfe **); /** * release resampler resources. */ void release_resampler(struct resampler_itfe *); __END_DECLS #endif // ANDROID_RESAMPLER_H bluez-5.82/android/audio_utils/PaxHeaders/resampler.c0000644000000000000000000000005014015011623017736 xustar0020 atime=1743516862 20 ctime=1743591276 bluez-5.82/android/audio_utils/resampler.c0000644000000000000000000002236214015011623017424 0ustar00rootroot// SPDX-License-Identifier: Apache-2.0 /* ** Copyright 2011, The Android Open-Source Project ** */ //#define LOG_NDEBUG 0 #include #include #include #include #include #include #include "hal-log.h" struct resampler { struct resampler_itfe itfe; SpeexResamplerState *speex_resampler; // handle on speex resampler struct resampler_buffer_provider *provider; // buffer provider installed by client uint32_t in_sample_rate; // input sampling rate in Hz uint32_t out_sample_rate; // output sampling rate in Hz uint32_t channel_count; // number of channels (interleaved) int16_t *in_buf; // input buffer size_t in_buf_size; // input buffer size size_t frames_in; // number of frames in input buffer size_t frames_rq; // cached number of output frames size_t frames_needed; // minimum number of input frames to produce // frames_rq output frames int32_t speex_delay_ns; // delay introduced by speex resampler in ns }; //------------------------------------------------------------------------------ // speex based resampler //------------------------------------------------------------------------------ static void resampler_reset(struct resampler_itfe *resampler) { struct resampler *rsmp = (struct resampler *)resampler; rsmp->frames_in = 0; rsmp->frames_rq = 0; if (rsmp != NULL && rsmp->speex_resampler != NULL) { speex_resampler_reset_mem(rsmp->speex_resampler); } } static int32_t resampler_delay_ns(struct resampler_itfe *resampler) { struct resampler *rsmp = (struct resampler *)resampler; int32_t delay = (int32_t)((1000000000 * (int64_t)rsmp->frames_in) / rsmp->in_sample_rate); delay += rsmp->speex_delay_ns; return delay; } // outputs a number of frames less or equal to *outFrameCount and updates *outFrameCount // with the actual number of frames produced. static int resampler_resample_from_provider(struct resampler_itfe *resampler, int16_t *out, size_t *outFrameCount) { struct resampler *rsmp = (struct resampler *)resampler; size_t framesRq; size_t framesWr; size_t inFrames; if (rsmp == NULL || out == NULL || outFrameCount == NULL) { return -EINVAL; } if (rsmp->provider == NULL) { *outFrameCount = 0; return -ENOSYS; } framesRq = *outFrameCount; // update and cache the number of frames needed at the input sampling rate to produce // the number of frames requested at the output sampling rate if (framesRq != rsmp->frames_rq) { rsmp->frames_needed = (framesRq * rsmp->in_sample_rate) / rsmp->out_sample_rate + 1; rsmp->frames_rq = framesRq; } framesWr = 0; inFrames = 0; while (framesWr < framesRq) { size_t outFrames; if (rsmp->frames_in < rsmp->frames_needed) { struct resampler_buffer buf; // make sure that the number of frames present in rsmp->in_buf (rsmp->frames_in) is at // least the number of frames needed to produce the number of frames requested at // the output sampling rate if (rsmp->in_buf_size < rsmp->frames_needed) { rsmp->in_buf_size = rsmp->frames_needed; rsmp->in_buf = (int16_t *)realloc(rsmp->in_buf, rsmp->in_buf_size * rsmp->channel_count * sizeof(int16_t)); } buf.frame_count = rsmp->frames_needed - rsmp->frames_in; rsmp->provider->get_next_buffer(rsmp->provider, &buf); if (buf.raw == NULL) { break; } memcpy(rsmp->in_buf + rsmp->frames_in * rsmp->channel_count, buf.raw, buf.frame_count * rsmp->channel_count * sizeof(int16_t)); rsmp->frames_in += buf.frame_count; rsmp->provider->release_buffer(rsmp->provider, &buf); } outFrames = framesRq - framesWr; inFrames = rsmp->frames_in; if (rsmp->channel_count == 1) { speex_resampler_process_int(rsmp->speex_resampler, 0, rsmp->in_buf, (void *) &inFrames, out + framesWr, (void *) &outFrames); } else { speex_resampler_process_interleaved_int(rsmp->speex_resampler, rsmp->in_buf, (void *) &inFrames, out + framesWr * rsmp->channel_count, (void *) &outFrames); } framesWr += outFrames; rsmp->frames_in -= inFrames; if ((framesWr != framesRq) && (rsmp->frames_in != 0)) warn("ReSampler::resample() remaining %zd frames in and %zd out", rsmp->frames_in, (framesRq - framesWr)); } if (rsmp->frames_in) { memmove(rsmp->in_buf, rsmp->in_buf + inFrames * rsmp->channel_count, rsmp->frames_in * rsmp->channel_count * sizeof(int16_t)); } *outFrameCount = framesWr; return 0; } static int resampler_resample_from_input(struct resampler_itfe *resampler, int16_t *in, size_t *inFrameCount, int16_t *out, size_t *outFrameCount) { struct resampler *rsmp = (struct resampler *)resampler; if (rsmp == NULL || in == NULL || inFrameCount == NULL || out == NULL || outFrameCount == NULL) { return -EINVAL; } if (rsmp->provider != NULL) { *outFrameCount = 0; return -ENOSYS; } if (rsmp->channel_count == 1) { speex_resampler_process_int(rsmp->speex_resampler, 0, in, (void *) inFrameCount, out, (void *) outFrameCount); } else { speex_resampler_process_interleaved_int(rsmp->speex_resampler, in, (void *) inFrameCount, out, (void *) outFrameCount); } DBG("resampler_resample_from_input() DONE in %zd out %zd", *inFrameCount, *outFrameCount); return 0; } int create_resampler(uint32_t inSampleRate, uint32_t outSampleRate, uint32_t channelCount, uint32_t quality, struct resampler_buffer_provider* provider, struct resampler_itfe **resampler) { int error; struct resampler *rsmp; int frames; DBG("create_resampler() In SR %d Out SR %d channels %d", inSampleRate, outSampleRate, channelCount); if (resampler == NULL) { return -EINVAL; } *resampler = NULL; if (quality <= RESAMPLER_QUALITY_MIN || quality >= RESAMPLER_QUALITY_MAX) { return -EINVAL; } rsmp = (struct resampler *)calloc(1, sizeof(struct resampler)); rsmp->speex_resampler = speex_resampler_init(channelCount, inSampleRate, outSampleRate, quality, &error); if (rsmp->speex_resampler == NULL) { error("ReSampler: Cannot create speex resampler: %s", speex_resampler_strerror(error)); free(rsmp); return -ENODEV; } rsmp->itfe.reset = resampler_reset; rsmp->itfe.resample_from_provider = resampler_resample_from_provider; rsmp->itfe.resample_from_input = resampler_resample_from_input; rsmp->itfe.delay_ns = resampler_delay_ns; rsmp->provider = provider; rsmp->in_sample_rate = inSampleRate; rsmp->out_sample_rate = outSampleRate; rsmp->channel_count = channelCount; rsmp->in_buf = NULL; rsmp->in_buf_size = 0; resampler_reset(&rsmp->itfe); frames = speex_resampler_get_input_latency(rsmp->speex_resampler); rsmp->speex_delay_ns = (int32_t)((1000000000 * (int64_t)frames) / rsmp->in_sample_rate); frames = speex_resampler_get_output_latency(rsmp->speex_resampler); rsmp->speex_delay_ns += (int32_t)((1000000000 * (int64_t)frames) / rsmp->out_sample_rate); *resampler = &rsmp->itfe; DBG("create_resampler() DONE rsmp %p &rsmp->itfe %p speex %p", rsmp, &rsmp->itfe, rsmp->speex_resampler); return 0; } void release_resampler(struct resampler_itfe *resampler) { struct resampler *rsmp = (struct resampler *)resampler; if (rsmp == NULL) { return; } free(rsmp->in_buf); if (rsmp->speex_resampler != NULL) { speex_resampler_destroy(rsmp->speex_resampler); } free(rsmp); } bluez-5.82/android/PaxHeaders/bluetoothd.te0000644000000000000000000000005012447320342015772 xustar0020 atime=1743516875 20 ctime=1743591288 bluez-5.82/android/bluetoothd.te0000644000000000000000000000331212447320342015452 0ustar00rootroottype bluetoothd, domain; type bluetoothd_exec, exec_type, file_type; type bluetoothd_main_exec, exec_type, file_type; # Start bluetoothd from init init_daemon_domain(bluetoothd) # Data file accesses allow bluetoothd bluetooth_data_file:dir w_dir_perms; allow bluetoothd bluetooth_data_file:notdevfile_class_set create_file_perms; allow bluetoothd self:capability { setuid net_admin net_bind_service net_raw }; allow bluetoothd kernel:system module_request; # TODO: this may be romoved for userbuild where we don't use bluetoothd_wrapper allow bluetoothd bluetoothd_main_exec:file { execute execute_no_trans read open }; # IPC socket communication allow bluetoothd self:socket { create_socket_perms accept listen setopt getopt }; # Allow clients to use a socket provided by the bluetooth app. allow bluetoothd { bluetooth mediaserver }:unix_stream_socket connectto; # Allow system app to use sockets and fds allow bluetooth bluetoothd:fd use; allow bluetooth bluetoothd:unix_stream_socket rw_socket_perms; # Allow user bluetooth apps to use sockets and fds allow bluetoothdomain bluetoothd:fd use; allow bluetoothdomain bluetoothd:unix_stream_socket { getopt setopt getattr read write ioctl shutdown }; # Other domains that can create and use bluetooth sockets. allow bluetoothdomain self:socket create_socket_perms; #This we might should put to mediaserver.te ? allow mediaserver bluetoothd:fd use; allow mediaserver bluetoothd:socket rw_socket_perms; # needs /system/bin/log access allow bluetoothd devpts:chr_file rw_file_perms; # access to uhid device allow bluetoothd uhid_device:chr_file rw_file_perms; # tethering allow bluetoothd self:udp_socket create_socket_perms; allow bluetoothd self:tcp_socket { create ioctl }; bluez-5.82/android/PaxHeaders/pixit-l2cap.txt0000644000000000000000000000005012537515745016201 xustar0020 atime=1743516876 20 ctime=1743591289 bluez-5.82/android/pixit-l2cap.txt0000644000000000000000000000637712537515745015677 0ustar00rootrootL2CAP PIXIT for the PTS tool. PTS version: 6.1 * - different than PTS defaults & - should be set to IUT Bluetooth address Required PIXIT settings ------------------------------------------------------------------------------- Parameter Name Value ------------------------------------------------------------------------------- TSPX_bd_addr_iut 112233445566 (*&) TSPX_client_class_of_device 100104 TSPX_server_class_of_device 100104 TSPX_security_enabled TRUE (*) TSPX_delete_link_key FALSE TSPX_pin_code 0000 TSPX_flushto FFFF TSPX_inmtu 02A0 TSPX_no_fail_verditcs FALSE TSPX_oumtu 02A0 TSPX_tester_mps 0017 TSPX_tester_mtu 02A0 TSPX_iut_role_initiator TRUE (*) TSPX_le_psm 0080 (*) TSPX_psm 1011 (*) TSPX_psm_unsupported 00F1 TSPX_psm_authentication_required 00F2 TSPX_psm_authorization_required 00F3 TSPX_psm_encryption_key_size_required 00F4 TSPX_time_guard 180000 TSPX_timer_ertx 120000 TSPX_timer_ertx_max 300000 TSPX_timer_ertx_min 60000 TSPX_timer_rtx 10000 TSPX_timer_rtx_max 60000 TSPX_timer_rtx_min 1000 TSPX_rfc_mode_tx_window_size 08 TSPX_rfc_mode_max_transmit 03 TSPX_rfc_mode_retransmission_timeout 07D0 TSPX_rfc_mode_monitor_timeout 2EE0 TSPX_rfc_mode_maximum_pdu_size 02A0 TSPX_extended_window_size 0012 TSPX_use_implicit_send TRUE TSPX_use_dynamic_pin FALSE TSPX_iut_SDU_size_in_bytes 144 TSPX_secure_simple_pairing_pass_key_confirmation FALSE TSPX_iut_address_type_random FALSE TSPX_tester_adv_interval_min 0030 TSPX_tester_adv_interval_max 0050 TSPX_tester_le_scan_interval 0C80 TSPX_tester_le_scan_window 0C80 TSPX_tester_conn_interval_min 0028 TSPX_tester_conn_interval_max 0050 TSPX_tester_conn_latency 0000 TSPX_tester_supervision_timeout 0C80 TSPX_tester_min_CE_length 0080 TSPX_tester_max_CE_length 0C80 ------------------------------------------------------------------------------- bluez-5.82/android/PaxHeaders/audio-ipc-api.txt0000644000000000000000000000005012302000171016432 xustar0020 atime=1743516875 20 ctime=1743591288 bluez-5.82/android/audio-ipc-api.txt0000644000000000000000000000540212302000171016114 0ustar00rootrootBluetooth Audio Plugin ====================== The audio plugin happen to be in a different socket but all the rules for HAL socket apply here as well, the abstract socket name is "\0bluez_audio_socket" (tentative): .---Audio---. .--Android--. | Plugin | | Daemon | | | Command | | | | --------------------------> | | | | | | | | <-------------------------- | | | | Response | | | | | | | | | | | | | | '-----------' '-----------' Audio HAL Daemon ---------------------------------------------------- call dev->open() --> command 0x01 return dev->open() <-- response 0x01 call dev->open_output_stream() --> command 0x03 return dev->open_output_stream() <-- response 0x03 call stream->write() --> command 0x05 return stream->write() <-- response 0x05 call stream->common.standby() --> command 0x06 return stream->common.standby() <-- response 0x06 call dev->close_output_stream() --> command 0x04 return dev->close_output_stream() <-- response 0x04 call dev->close() --> command 0x02 return dev->close() <-- response 0x02 Audio Service (ID 0) ==================== Opcode 0x00 - Error response Response parameters: Status (1 octet) Opcode 0x01 - Open Audio Endpoint commmand Command parameters: Service UUID (16 octets) Codec ID (1 octet) Number of codec presets (1 octet) Codec capabilities length (1 octet) Codec capabilities (variable) Codec preset # length (1 octet) Codec preset # configuration (variable) ... Response parameters: Endpoint ID (1 octet) Opcode 0x02 - Close Audio Endpoint command Command parameters: Endpoint ID (1 octet) Response parameters: Opcode 0x03 - Open Stream command Command parameters: Endpoint ID (1 octet) Response parameters: Outgoing MTU (2 octets) Codec configuration length (1 octet) Codec configuration (1 octet) File descriptor (inline) Opcode 0x04 - Close Stream command Command parameters: Endpoint ID (1 octet) Response parameters: Opcode 0x05 - Resume Stream command Command parameters: Endpoint ID (1 octet) Response parameters: Opcode 0x06 - Suspend Stream command Command parameters: Endpoint ID (1 octet) Response parameters: bluez-5.82/android/PaxHeaders/hal-audio-sbc.c0000644000000000000000000000005014621503015016037 xustar0020 atime=1743516862 20 ctime=1743591275 bluez-5.82/android/hal-audio-sbc.c0000644000000000000000000002217514621503015015527 0ustar00rootroot// SPDX-License-Identifier: Apache-2.0 /* * Copyright (C) 2013 Intel Corporation * */ #define _GNU_SOURCE #include #include #include #include #include #include "audio-msg.h" #include "hal-audio.h" #include "hal-log.h" #include "../profiles/audio/a2dp-codecs.h" #define MAX_FRAMES_IN_PAYLOAD 15 #define SBC_QUALITY_MIN_BITPOOL 33 #define SBC_QUALITY_STEP 5 #if __BYTE_ORDER == __LITTLE_ENDIAN struct rtp_payload { unsigned frame_count:4; unsigned rfa0:1; unsigned is_last_fragment:1; unsigned is_first_fragment:1; unsigned is_fragmented:1; } __attribute__ ((packed)); #elif __BYTE_ORDER == __BIG_ENDIAN struct rtp_payload { unsigned is_fragmented:1; unsigned is_first_fragment:1; unsigned is_last_fragment:1; unsigned rfa0:1; unsigned frame_count:4; } __attribute__ ((packed)); #else #error "Unknown byte order" #endif struct media_packet_sbc { struct media_packet_rtp hdr; struct rtp_payload payload; uint8_t data[0]; }; struct sbc_data { a2dp_sbc_t sbc; sbc_t enc; uint16_t payload_len; size_t in_frame_len; size_t in_buf_size; size_t out_frame_len; unsigned frame_duration; unsigned frames_per_packet; }; static const a2dp_sbc_t sbc_presets[] = { { .frequency = SBC_SAMPLING_FREQ_44100 | SBC_SAMPLING_FREQ_48000, .channel_mode = SBC_CHANNEL_MODE_MONO | SBC_CHANNEL_MODE_DUAL_CHANNEL | SBC_CHANNEL_MODE_STEREO | SBC_CHANNEL_MODE_JOINT_STEREO, .subbands = SBC_SUBBANDS_4 | SBC_SUBBANDS_8, .allocation_method = SBC_ALLOCATION_SNR | SBC_ALLOCATION_LOUDNESS, .block_length = SBC_BLOCK_LENGTH_4 | SBC_BLOCK_LENGTH_8 | SBC_BLOCK_LENGTH_12 | SBC_BLOCK_LENGTH_16, .min_bitpool = SBC_MIN_BITPOOL, .max_bitpool = SBC_BITPOOL_HQ_JOINT_STEREO_44100, }, { .frequency = SBC_SAMPLING_FREQ_44100, .channel_mode = SBC_CHANNEL_MODE_JOINT_STEREO, .subbands = SBC_SUBBANDS_8, .allocation_method = SBC_ALLOCATION_LOUDNESS, .block_length = SBC_BLOCK_LENGTH_16, .min_bitpool = SBC_MIN_BITPOOL, .max_bitpool = SBC_BITPOOL_HQ_JOINT_STEREO_44100, }, { .frequency = SBC_SAMPLING_FREQ_48000, .channel_mode = SBC_CHANNEL_MODE_JOINT_STEREO, .subbands = SBC_SUBBANDS_8, .allocation_method = SBC_ALLOCATION_LOUDNESS, .block_length = SBC_BLOCK_LENGTH_16, .min_bitpool = SBC_MIN_BITPOOL, .max_bitpool = SBC_BITPOOL_HQ_JOINT_STEREO_48000, }, }; static int sbc_get_presets(struct audio_preset *preset, size_t *len) { int i; int count; size_t new_len = 0; uint8_t *ptr = (uint8_t *) preset; size_t preset_size = sizeof(*preset) + sizeof(a2dp_sbc_t); count = sizeof(sbc_presets) / sizeof(sbc_presets[0]); for (i = 0; i < count; i++) { preset = (struct audio_preset *) ptr; if (new_len + preset_size > *len) break; preset->len = sizeof(a2dp_sbc_t); memcpy(preset->data, &sbc_presets[i], preset->len); new_len += preset_size; ptr += preset_size; } *len = new_len; return i; } static int sbc_freq2int(uint8_t freq) { switch (freq) { case SBC_SAMPLING_FREQ_16000: return 16000; case SBC_SAMPLING_FREQ_32000: return 32000; case SBC_SAMPLING_FREQ_44100: return 44100; case SBC_SAMPLING_FREQ_48000: return 48000; default: return 0; } } static const char *sbc_mode2str(uint8_t mode) { switch (mode) { case SBC_CHANNEL_MODE_MONO: return "Mono"; case SBC_CHANNEL_MODE_DUAL_CHANNEL: return "DualChannel"; case SBC_CHANNEL_MODE_STEREO: return "Stereo"; case SBC_CHANNEL_MODE_JOINT_STEREO: return "JointStereo"; default: return "(unknown)"; } } static int sbc_blocks2int(uint8_t blocks) { switch (blocks) { case SBC_BLOCK_LENGTH_4: return 4; case SBC_BLOCK_LENGTH_8: return 8; case SBC_BLOCK_LENGTH_12: return 12; case SBC_BLOCK_LENGTH_16: return 16; default: return 0; } } static int sbc_subbands2int(uint8_t subbands) { switch (subbands) { case SBC_SUBBANDS_4: return 4; case SBC_SUBBANDS_8: return 8; default: return 0; } } static const char *sbc_allocation2str(uint8_t allocation) { switch (allocation) { case SBC_ALLOCATION_SNR: return "SNR"; case SBC_ALLOCATION_LOUDNESS: return "Loudness"; default: return "(unknown)"; } } static void sbc_init_encoder(struct sbc_data *sbc_data) { a2dp_sbc_t *in = &sbc_data->sbc; sbc_t *out = &sbc_data->enc; sbc_init_a2dp(out, 0L, in, sizeof(*in)); out->endian = SBC_LE; out->bitpool = in->max_bitpool; DBG("frequency=%d channel_mode=%s block_length=%d subbands=%d allocation=%s bitpool=%d-%d", sbc_freq2int(in->frequency), sbc_mode2str(in->channel_mode), sbc_blocks2int(in->block_length), sbc_subbands2int(in->subbands), sbc_allocation2str(in->allocation_method), in->min_bitpool, in->max_bitpool); } static void sbc_codec_calculate(struct sbc_data *sbc_data) { size_t in_frame_len; size_t out_frame_len; size_t num_frames; in_frame_len = sbc_get_codesize(&sbc_data->enc); out_frame_len = sbc_get_frame_length(&sbc_data->enc); num_frames = sbc_data->payload_len / out_frame_len; if (num_frames > MAX_FRAMES_IN_PAYLOAD) num_frames = MAX_FRAMES_IN_PAYLOAD; sbc_data->in_frame_len = in_frame_len; sbc_data->in_buf_size = num_frames * in_frame_len; sbc_data->out_frame_len = out_frame_len; sbc_data->frame_duration = sbc_get_frame_duration(&sbc_data->enc); sbc_data->frames_per_packet = num_frames; DBG("in_frame_len=%zu out_frame_len=%zu frames_per_packet=%zu", in_frame_len, out_frame_len, num_frames); } static bool sbc_codec_init(struct audio_preset *preset, uint16_t payload_len, void **codec_data) { struct sbc_data *sbc_data; if (preset->len != sizeof(a2dp_sbc_t)) { error("SBC: preset size mismatch"); return false; } sbc_data = calloc(1, sizeof(struct sbc_data)); if (!sbc_data) return false; memcpy(&sbc_data->sbc, preset->data, preset->len); sbc_init_encoder(sbc_data); sbc_data->payload_len = payload_len; sbc_codec_calculate(sbc_data); *codec_data = sbc_data; return true; } static bool sbc_cleanup(void *codec_data) { struct sbc_data *sbc_data = (struct sbc_data *) codec_data; sbc_finish(&sbc_data->enc); free(codec_data); return true; } static bool sbc_get_config(void *codec_data, struct audio_input_config *config) { struct sbc_data *sbc_data = (struct sbc_data *) codec_data; switch (sbc_data->sbc.frequency) { case SBC_SAMPLING_FREQ_16000: config->rate = 16000; break; case SBC_SAMPLING_FREQ_32000: config->rate = 32000; break; case SBC_SAMPLING_FREQ_44100: config->rate = 44100; break; case SBC_SAMPLING_FREQ_48000: config->rate = 48000; break; default: return false; } config->channels = sbc_data->sbc.channel_mode == SBC_CHANNEL_MODE_MONO ? AUDIO_CHANNEL_OUT_MONO : AUDIO_CHANNEL_OUT_STEREO; config->format = AUDIO_FORMAT_PCM_16_BIT; return true; } static size_t sbc_get_buffer_size(void *codec_data) { struct sbc_data *sbc_data = (struct sbc_data *) codec_data; return sbc_data->in_buf_size; } static size_t sbc_get_mediapacket_duration(void *codec_data) { struct sbc_data *sbc_data = (struct sbc_data *) codec_data; return sbc_data->frame_duration * sbc_data->frames_per_packet; } static ssize_t sbc_encode_mediapacket(void *codec_data, const uint8_t *buffer, size_t len, struct media_packet *mp, size_t mp_data_len, size_t *written) { struct sbc_data *sbc_data = (struct sbc_data *) codec_data; struct media_packet_sbc *mp_sbc = (struct media_packet_sbc *) mp; size_t consumed = 0; size_t encoded = 0; uint8_t frame_count = 0; mp_data_len -= sizeof(mp_sbc->payload); while (len - consumed >= sbc_data->in_frame_len && mp_data_len - encoded >= sbc_data->out_frame_len && frame_count < sbc_data->frames_per_packet) { ssize_t read; ssize_t written = 0; read = sbc_encode(&sbc_data->enc, buffer + consumed, sbc_data->in_frame_len, mp_sbc->data + encoded, mp_data_len - encoded, &written); if (read < 0) { error("SBC: failed to encode block at frame %d (%zd)", frame_count, read); break; } frame_count++; consumed += read; encoded += written; } *written = encoded + sizeof(mp_sbc->payload); mp_sbc->payload.frame_count = frame_count; return consumed; } static bool sbc_update_qos(void *codec_data, uint8_t op) { struct sbc_data *sbc_data = (struct sbc_data *) codec_data; uint8_t curr_bitpool = sbc_data->enc.bitpool; uint8_t new_bitpool = curr_bitpool; switch (op) { case QOS_POLICY_DEFAULT: new_bitpool = sbc_data->sbc.max_bitpool; break; case QOS_POLICY_DECREASE: if (curr_bitpool > SBC_QUALITY_MIN_BITPOOL) { new_bitpool = curr_bitpool - SBC_QUALITY_STEP; if (new_bitpool < SBC_QUALITY_MIN_BITPOOL) new_bitpool = SBC_QUALITY_MIN_BITPOOL; } break; } if (new_bitpool == curr_bitpool) return false; sbc_data->enc.bitpool = new_bitpool; sbc_codec_calculate(sbc_data); info("SBC: bitpool changed: %d -> %d", curr_bitpool, new_bitpool); return true; } static const struct audio_codec codec = { .type = A2DP_CODEC_SBC, .use_rtp = true, .get_presets = sbc_get_presets, .init = sbc_codec_init, .cleanup = sbc_cleanup, .get_config = sbc_get_config, .get_buffer_size = sbc_get_buffer_size, .get_mediapacket_duration = sbc_get_mediapacket_duration, .encode_mediapacket = sbc_encode_mediapacket, .update_qos = sbc_update_qos, }; const struct audio_codec *codec_sbc(void) { return &codec; } bluez-5.82/android/PaxHeaders/pixit-avrcp.txt0000644000000000000000000000005012537515745016313 xustar0020 atime=1743516876 20 ctime=1743591289 bluez-5.82/android/pixit-avrcp.txt0000644000000000000000000000213412537515745015774 0ustar00rootrootAVRCP PIXIT for the PTS tool. PTS version: 6.1 * - different than PTS defaults & - should be set to IUT Bluetooth address # - should be set to PTS's bin/audio folder Required PIXIT settings ------------------------------------------------------------------------------- Parameter Name Value ------------------------------------------------------------------------------- TSPX_security_enabled FALSE TSPX_bd_addr_iut 112233445566 (*&) TSPX_class_of_device 20050C TSPX_player_feature_bitmask 0000000000000007FFF00070000000000 TSPX_pin_code 0000 TSPX_delete_link_key FALSE TSPX_time_guard 300000 TSPX_avrcp_only FALSE TSPX_search_string 3 TSPX_max_avc_fragments 10 TSPX_establish_avdtp_stream TRUE TSPX_use_implicit_send TRUE TSPX_avrcp_version TSPX_tester_av_role TSPX_media_directory C:\Program Files\Bluetooth SIG\Bluetooth PTS\ bin\audio (#) TSPX_auth_password 0000 TSPX_auth_user_id PTS TSPX_rfcomm_channel 8 TSPX_l2cap_psm 1011 TSPX_no_confirmations FALSE TSPX_no_cover_art_folder TSPX_cover_art_folder ------------------------------------------------------------------------------- bluez-5.82/android/PaxHeaders/pixit-pan.txt0000644000000000000000000000005012537515745015756 xustar0020 atime=1743516876 20 ctime=1743591289 bluez-5.82/android/pixit-pan.txt0000644000000000000000000000252312537515745015441 0ustar00rootrootPAN PIXIT for the PTS tool. PTS version: 6.1 * - different than PTS defaults & - should be set to IUT or PTS Bluetooth/MAC address respectively Required PIXIT settings ------------------------------------------------------------------------------- Parameter Name Value ------------------------------------------------------------------------------- TSPX_GN_class_of_device 020104 TSPX_NAP_class_of_device 020300 TSPX_PANU_class_of_device 020104 TSPX_time_guard 300000 TSPX_bd_addr_iut 112233445566 (*&) TSPX_security_enabled False TSPX_pin_code 0000 TSPX_delete_link_key False TSPX_use_implicit_send True TSPX_iut_ip_address 192.168.1.100 (*&) TSPX_iut_port_number 4242 TSPX_PTS_ip_address 192.168.168.100 TSPX_PTS_port_number 4242 TSPX_bd_addr_PTS 112233445566 (*&) TSPX_checksum E851 TSPX_secure_simple_pairing_pass_key_confirmation False TSPX_iut_friendly_bt_name gprs-pc TSPX_PTS_role_when_iut_is_PANU default TSPX_auth_password 0000 TSPX_auth_user_id PTS TSPX_l2cap_psm 000F TSPX_rfcomm_channel 8 TSPX_no_confirmations FALSE TSPX_UUID_dest_address 0000 TSPX_UUID_source_address 0000 TSPX_MAC_dest_address 112233445566 (*&) TSPX_MAC_source_address 112233445566 (*&) ------------------------------------------------------------------------------- bluez-5.82/android/PaxHeaders/pics-hid.txt0000644000000000000000000000005012537515745015545 xustar0020 atime=1743516876 20 ctime=1743591288 bluez-5.82/android/pics-hid.txt0000644000000000000000000003464112537515745015236 0ustar00rootrootHID PICS for the PTS tool. PTS version: 6.1 * - different than PTS defaults # - not yet implemented/supported M - mandatory O - optional Roles ------------------------------------------------------------------------------- Parameter Name Selected Description ------------------------------------------------------------------------------- TSPC_HID_1_1 True (*) Role: Host, Report protocol (O.1) TSPC_HID_1_2 False Role: HID Role (O.1) TSPC_HID_1_3 False Role: Host, Boot protocol (O.1) ------------------------------------------------------------------------------- O.1: It is Mandatory to support One of these roles. ------------------------------------------------------------------------------- Application Procedures ------------------------------------------------------------------------------- Parameter Name Selected Description ------------------------------------------------------------------------------- TSPC_HID_2_1 True (*) Host: Establish HID connection (C.4) TSPC_HID_2_2 True (*) Host: Accept HID connection (C.4) TSPC_HID_2_3 True (*) Host: Terminate HID connection (C.4) TSPC_HID_2_4 True (*) Host: Accept termination of HID connection (C.4) TSPC_HID_2_5 True (*) Host: Support for virtual cables (C.4) TSPC_HID_2_6 True (*) Host: HID initiated connection (C.4) TSPC_HID_2_7 True (*) Host: Host initiated connection (C.4) TSPC_HID_2_8 True (*) Host: Host data transfer to HID (C.1) TSPC_HID_2_9 True (*) Host: HID data transfer to Host (C.1) TSPC_HID_2_10 False Host: Boot mode data transfer to Host (C.2) TSPC_HID_2_11 False Host : Boot mode data transfer to HID (C.2) TSPC_HID_2_12 False Host : Support for Application to send GET_Report (O) TSPC_HID_2_13 False Host : Support for Application to send SET_REPORT (O) TSPC_HID_2_14 False Host : Support for sending HCI_CONTROL with VIRTUAL_CABLE_UNPLUG (C.3) TSPC_HID_2_15 False Host : Support for receiving HCI_CONTROL with VIRTUAL_CABLE_UNPLUG (C.3) ------------------------------------------------------------------------------- C.1: Optional for Boot Mode Only Hosts (TSPC_HID_1_3); Mandatory for Host Role (TSPC_HID_1_1); OTHERWISE Excluded. C.2: Mandatory for Boot Mode Only Hosts (TSPC_HID_1_3); otherwise Optional. C.3: Optional IF (TSPC_HID_2_5) supported, otherwise excluded. C.4: Mandatory IF TSPC_HID_1_1 (Host, Report protocol) is supported, otherwise Optional. ------------------------------------------------------------------------------- Device to Host Transfers ------------------------------------------------------------------------------- Parameter Name Selected Description ------------------------------------------------------------------------------- TSPC_HID_3_1 False Host : Data reports larger than host MTU on Control channel (O) TSPC_HID_3_2 True (*) Host : Data reports larger than host MTU on Interrupt channel (C.1) TSPC_HID_3_3 True (*) Host : Data reports to host (C.1) TSPC_HID_3_4 False Host : Boot mode reports to host (C.2) ------------------------------------------------------------------------------- C.1: Excluded for Boot Mode Only Hosts (TSPC_HID_1_3); Mandatory IF TSPC_HID_2_12 is supported, otherwise Optional. C.2: Mandatory IF TSPC_HID_1_3 is supported, otherwise Optional. ------------------------------------------------------------------------------- Host to Device Transfers ------------------------------------------------------------------------------- Parameter Name Selected Description ------------------------------------------------------------------------------- TSPC_HID_4_1 False Host : Data reports larger than device MTU on Control channel (C.1) TSPC_HID_4_2 False Host : Data reports larger than device MTU on Interrupt channel (C.1) TSPC_HID_4_3 True (*) Host : Data reports to device (C.2) TSPC_HID_4_4 False Host : Boot mode reports to device (O) ------------------------------------------------------------------------------- C.1: Excluded for Boot Mode Only Hosts (TSPC_HID_1_3); otherwise Optional C.2: Excluded for Boot Mode Only Hosts (TSPC_HID_1_3); otherwise Mandatory for Host Role (TSPC_HID_1_1). ------------------------------------------------------------------------------- HID Control Commands ------------------------------------------------------------------------------- Parameter Name Selected Description ------------------------------------------------------------------------------- TSPC_HID_5_1 False Host : Set_Protocol command (C.1, C.4) TSPC_HID_5_2 False Host : Get_Protocol command (C.1, C.4) TSPC_HID_5_3 False Host : Set_Idle command (O) TSPC_HID_5_4 False Host : Get_Idle command (O) TSPC_HID_5_5 False Host : Set_Report command (C.2) TSPC_HID_5_6 False Host : Get_Report command (C.3) ------------------------------------------------------------------------------- C.1: Mandatory for Boot Mode Only Hosts (TSPC_HID_1_3); otherwise Optional. C.2: Mandatory IF (TSPC_HID_1_1) supported AND (TSPC_HID_2_13) supported. C.3: Mandatory IF (TSPC_HID_1_1) Supported AND (TSPC_HID_2_12) Supported C.4: Mandatory to support TSPC_HID_5_1 (Set_Protocol command) AND TSPC_HID_5_2 (Get_Protocol command) IF one of TSPC_HID_5_1 (Set_Protocol command) OR TSPC_HID_5_2 (Get_Protocol command) is supported, otherwise Excluded. ------------------------------------------------------------------------------- Host Link Manager Procedures ------------------------------------------------------------------------------- Parameter Name Selected Description ------------------------------------------------------------------------------- TSPC_HID_6_1 False Host : Initiate Authentication before connection completed (C.1) TSPC_HID_6_2 False Host : Initiate Authentication after connection completed (C.1) TSPC_HID_6_3 False Host : Initiate pairing before connection completed (C.2) TSPC_HID_6_4 False Host : Initiate pairing after connection completed (C.2) TSPC_HID_6_5 False Host : Encryption (O) TSPC_HID_6_6 False Host : Initiate encryption (C.3) TSPC_HID_6_7 False Host : Accept encryption requests (C.3) TSPC_HID_6_8 True (*) Host : Role switch (Master/Slave) (C.4) TSPC_HID_6_9 True (*) Host : Request Master Slave switch (C.4) TSPC_HID_6_10 True (*) Host : Accept Master Slave switch requests (C.4) TSPC_HID_6_11 False Host : Hold mode (O) TSPC_HID_6_12 True (*) Host : Sniff mode (C.4) TSPC_HID_6_13 False Host : Park mode (O) ------------------------------------------------------------------------------- C.1: Mandatory to support TSPC_HID_6_1 AND TSPC_HID_6_2 IF GAP 2/3 (Initiate LMP-Authentication) is supported, otherwise Excluded. C.2: If Pairing supported both (TSPC_HID_6_3) AND (TSPC_HID_6_4) must be supported. C.3: Mandatory IF (TSPC_HID_6_5) encryption supported. C.4: Mandatory IF (TSPC_HID_1_1) supported, otherwise Excluded. ------------------------------------------------------------------------------- Host Link Control Requirements ------------------------------------------------------------------------------- Parameter Name Selected Description ------------------------------------------------------------------------------- TSPC_HID_7_1 True (*) Host : Supports inquiry, 79 channel (C.1) TSPC_HID_7_2 False Host : Supports inquiry scan, 79 channel (C.2) ------------------------------------------------------------------------------- C.1: Mandatory to support IF (TSPC_HID_1_1) supported, otherwise Excluded. C.2: Feature should not be used by a Host, but can be supported in LM. ------------------------------------------------------------------------------- HID Device Roles ------------------------------------------------------------------------------- Parameter Name Selected Description ------------------------------------------------------------------------------- TSPC_HID_8_1 False Hid : Pointing HID (O.1) TSPC_HID_8_2 False Hid : Keyboard HID (O.1) TSPC_HID_8_3 False Hid : Identification HID (O.1) TSPC_HID_8_4 False Hid : Other HID (O.1) ------------------------------------------------------------------------------- O.1: It is Mandatory to support One of these roles IF (TSPC_HID_1_2) is selected ------------------------------------------------------------------------------- HID Application Procedures ------------------------------------------------------------------------------- Parameter Name Selected Description ------------------------------------------------------------------------------- TSPC_HID_9_1 False Hid : Establish HID connection (O) TSPC_HID_9_2 False (*) Hid : Accept HID connection (M) TSPC_HID_9_3 False Hid : Terminate HID connection (O) TSPC_HID_9_4 False (*) Hid : Accept Termination of HID connection (M) TSPC_HID_9_5 False Hid : Support for virtual cables (O) TSPC_HID_9_6 False Hid : HID initiated reconnection (C.1) TSPC_HID_9_7 False Hid : Host initiated reconnection (C.1) TSPC_HID_9_8 False Hid : Host data transfer to HID (C.2) TSPC_HID_9_9 False Hid : HID data transfer to Host (C.2) TSPC_HID_9_10 False Hid : HID Boot mode data transfer to Host (C.3) TSPC_HID_9_11 False Hid : Host Boot mode data transfer to HID (C.4) TSPC_HID_9_12 False Hid : Output reports declared (C.4) TSPC_HID_9_13 False Hid : Input reports declared (C.3) TSPC_HID_9_14 False Hid : Feature reports declared (O) TSPC_HID_9_15 False Hid : Support for sending HCI_CONTROL with VIRTUAL_CABLE_UNPLUG (C.5) TSPC_HID_9_16 False Hid : Support for receiving HCI_CONTROL with VIRTUAL_CABLE_UNPLUG (C.5) ------------------------------------------------------------------------------- C.1: One of these is Mandatory IF (TSPC_HID_9_5) is supported (SDP attribute 0x204=True) C.2: One of these is Mandatory if TSPC_HID_1_2 (HID Role) is supported. C.3: Mandatory IF (TSPC_HID_8_1) OR (TSPC_HID_8_2) is selected C.4: Mandatory IF (TSPC_HID_8_2) is supported (for status indicators) C.5: Optional IF (TSPC_HID_9_5) supported, otherwise excluded. ------------------------------------------------------------------------------- Device to Host Transfers ------------------------------------------------------------------------------- Parameter Name Selected Description ------------------------------------------------------------------------------- TSPC_HID_10_1 False Hid : Data reports larger than host MTU on Control channel (O) TSPC_HID_10_2 False Hid : Data reports larger than host MTU on Interrupt channel (O) TSPC_HID_10_3 False Hid : Data reports to host (O) TSPC_HID_10_4 False Hid : Boot mode reports to host (C.1) ------------------------------------------------------------------------------- C.1: Mandatory IF (TSPC_HID_8_1) OR (TSPC_HID_8_2) is supported. Optional for other HIDs. ------------------------------------------------------------------------------- Host to Device Transfers ------------------------------------------------------------------------------- Parameter Name Selected Description ------------------------------------------------------------------------------- TSPC_HID_11_1 False Hid : Data reports larger than device MTU on Control channel (O) TSPC_HID_11_2 False Hid : Data reports larger than device MTU on Interrupt channel (O) TSPC_HID_11_3 False Hid : Data reports to device (O) TSPC_HID_11_4 False Hid : Boot mode reports to device (C.1) ------------------------------------------------------------------------------- C.1: Mandatory IF (TSPC_HID_8_2) is supported. Optional for other HIDs. ------------------------------------------------------------------------------- HID Control Commands ------------------------------------------------------------------------------- Parameter Name Selected Description ------------------------------------------------------------------------------- TSPC_HID_12_1 False Hid : Set_Protocol command (C.1, C.5) TSPC_HID_12_2 False Hid : Get_Protocol command (C.1, C.5) TSPC_HID_12_3 False Hid : Set_Idle command (C.2) TSPC_HID_12_4 False Hid : Get_Idle command (C.2) TSPC_HID_12_5 False Hid : Set_Report command (C.3) TSPC_HID_12_6 False Hid : Get_Report command (C.4) ------------------------------------------------------------------------------- C.1: Mandatory IF (TSPC_HID_8_1) OR (TSPC_HID_8_2) is supported. Optional for other HIDs. If either Set_Protocol or Get_Protocol supported, both are Mandatory. C.2: Mandatory IF (TSPC_HID_8_2) Keyboard is selected. Optional for other HIDs. C.3: Mandatory IF (TSPC_HID_9_12) or (TSPC_HID_9_14) supported. C.4: Mandatory IF (TSPC_HID_9_13) or (TSPC_HID_9_14) supported C.5: If either TSPC_HID_12_1 (Set_Protocol command) OR TSPC_HID_12_2 (Get_Protocol command) is supported, both TSPC_HID_12_1 (Set_Protocol command) AND TSPC_HID_12_2 (Get_Protocol command) are Mandatory to support ------------------------------------------------------------------------------- HID Link Manager Procedures ------------------------------------------------------------------------------- Parameter Name Selected Description ------------------------------------------------------------------------------- TSPC_HID_13_1 False Hid : Host initiated Authentication before connection completed (C.1) TSPC_HID_13_2 False Hid : Host initiated Authentication after connection completed (C.1) TSPC_HID_13_3 False Hid : Item no longer used (N/A) TSPC_HID_13_4 False Hid : Item no longer used (N/A) TSPC_HID_13_5 False Hid : Encryption (C.1) TSPC_HID_13_6 False Hid : Initiate encryption (O) TSPC_HID_13_7 False Hid : Accept encryption requests (C.2) TSPC_HID_13_8 False Hid : Role switch (Master/Slave) (C.3) TSPC_HID_13_9 False Hid : Request Master Slave switch (O) TSPC_HID_13_10 False Hid : Accept Master Slave switch requests (C.3) TSPC_HID_13_11 False Hid : Hold mode (O) TSPC_HID_13_12 False Hid : Sniff mode (O) TSPC_HID_13_13 False Hid : Park mode (O) ------------------------------------------------------------------------------- C.1: Mandatory IF (TSPC_HID_8_2) OR (TSPC_HID_8_3) is selected. Optional for other HIDs. C.2: Mandatory IF (TSPC_HID_13_5) supported. C.3: Mandatory IF (TSPC_HID_9_6) is supported. ------------------------------------------------------------------------------- HID Link Control Requirements ------------------------------------------------------------------------------- Parameter Name Selected Description ------------------------------------------------------------------------------- TSPC_HID_14_1 False Hid : Supports inquiry, 79 channel (O) TSPC_HID_14_2 False Hid : Supports inquiry scan, 79 channel (M.1) TSPC_ALL False Enables all test cases when set to true. ------------------------------------------------------------------------------- M.1: Mandatory IF (TSPC_HID_1_2) is supported. ------------------------------------------------------------------------------- bluez-5.82/android/PaxHeaders/hal-pan.c0000644000000000000000000000005014015011623014743 xustar0020 atime=1743516862 20 ctime=1743591276 bluez-5.82/android/hal-pan.c0000644000000000000000000001056214015011623014430 0ustar00rootroot// SPDX-License-Identifier: Apache-2.0 /* * Copyright (C) 2013 Intel Corporation * */ #define _GNU_SOURCE #include #include #include #include "hal-utils.h" #include "hal-log.h" #include "hal.h" #include "hal-msg.h" #include "hal-ipc.h" static const btpan_callbacks_t *cbs = NULL; static bool interface_ready(void) { return cbs != NULL; } static void handle_conn_state(void *buf, uint16_t len, int fd) { struct hal_ev_pan_conn_state *ev = buf; if (cbs->connection_state_cb) cbs->connection_state_cb(ev->state, ev->status, (bt_bdaddr_t *) ev->bdaddr, ev->local_role, ev->remote_role); } static void handle_ctrl_state(void *buf, uint16_t len, int fd) { struct hal_ev_pan_ctrl_state *ev = buf; #if ANDROID_VERSION >= PLATFORM_VER(5, 0, 0) if (cbs->control_state_cb) cbs->control_state_cb(ev->state, ev->local_role, ev->status, (char *)ev->name); #else /* * Callback declared in bt_pan.h is 'typedef void * (*btpan_control_state_callback)(btpan_control_state_t state, * bt_status_t error, int local_role, const char* ifname); * But PanService.Java defined it wrong way. * private void onControlStateChanged(int local_role, int state, * int error, String ifname). * First and third parameters are misplaced, so sending data according * to PanService.Java. */ if (cbs->control_state_cb) cbs->control_state_cb(ev->local_role, ev->state, ev->status, (char *)ev->name); #endif } /* * handlers will be called from notification thread context, * index in table equals to 'opcode - HAL_MINIMUM_EVENT' */ static const struct hal_ipc_handler ev_handlers[] = { /* HAL_EV_PAN_CTRL_STATE */ { handle_ctrl_state, false, sizeof(struct hal_ev_pan_ctrl_state) }, /* HAL_EV_PAN_CONN_STATE */ { handle_conn_state, false, sizeof(struct hal_ev_pan_conn_state) }, }; static bt_status_t pan_enable(int local_role) { struct hal_cmd_pan_enable cmd; DBG(""); if (!interface_ready()) return BT_STATUS_NOT_READY; cmd.local_role = local_role; return hal_ipc_cmd(HAL_SERVICE_ID_PAN, HAL_OP_PAN_ENABLE, sizeof(cmd), &cmd, NULL, NULL, NULL); } static int pan_get_local_role(void) { struct hal_rsp_pan_get_role rsp; size_t len = sizeof(rsp); bt_status_t status; DBG(""); if (!interface_ready()) return BTPAN_ROLE_NONE; status = hal_ipc_cmd(HAL_SERVICE_ID_PAN, HAL_OP_PAN_GET_ROLE, 0, NULL, &len, &rsp, NULL); if (status != BT_STATUS_SUCCESS) return BTPAN_ROLE_NONE; return rsp.local_role; } static bt_status_t pan_connect(const bt_bdaddr_t *bd_addr, int local_role, int remote_role) { struct hal_cmd_pan_connect cmd; DBG(""); if (!interface_ready()) return BT_STATUS_NOT_READY; memcpy(cmd.bdaddr, bd_addr, sizeof(cmd.bdaddr)); cmd.local_role = local_role; cmd.remote_role = remote_role; return hal_ipc_cmd(HAL_SERVICE_ID_PAN, HAL_OP_PAN_CONNECT, sizeof(cmd), &cmd, NULL, NULL, NULL); } static bt_status_t pan_disconnect(const bt_bdaddr_t *bd_addr) { struct hal_cmd_pan_disconnect cmd; DBG(""); if (!interface_ready()) return BT_STATUS_NOT_READY; memcpy(cmd.bdaddr, bd_addr, sizeof(cmd.bdaddr)); return hal_ipc_cmd(HAL_SERVICE_ID_PAN, HAL_OP_PAN_DISCONNECT, sizeof(cmd), &cmd, NULL, NULL, NULL); } static bt_status_t pan_init(const btpan_callbacks_t *callbacks) { struct hal_cmd_register_module cmd; int ret; DBG(""); if (interface_ready()) return BT_STATUS_DONE; cbs = callbacks; hal_ipc_register(HAL_SERVICE_ID_PAN, ev_handlers, sizeof(ev_handlers)/sizeof(ev_handlers[0])); cmd.service_id = HAL_SERVICE_ID_PAN; cmd.mode = HAL_MODE_DEFAULT; cmd.max_clients = 1; ret = hal_ipc_cmd(HAL_SERVICE_ID_CORE, HAL_OP_REGISTER_MODULE, sizeof(cmd), &cmd, NULL, NULL, NULL); if (ret != BT_STATUS_SUCCESS) { cbs = NULL; hal_ipc_unregister(HAL_SERVICE_ID_PAN); } return ret; } static void pan_cleanup(void) { struct hal_cmd_unregister_module cmd; DBG(""); if (!interface_ready()) return; cmd.service_id = HAL_SERVICE_ID_PAN; hal_ipc_cmd(HAL_SERVICE_ID_CORE, HAL_OP_UNREGISTER_MODULE, sizeof(cmd), &cmd, NULL, NULL, NULL); hal_ipc_unregister(HAL_SERVICE_ID_PAN); cbs = NULL; } static btpan_interface_t pan_if = { .size = sizeof(pan_if), .init = pan_init, .enable = pan_enable, .get_local_role = pan_get_local_role, .connect = pan_connect, .disconnect = pan_disconnect, .cleanup = pan_cleanup }; btpan_interface_t *bt_get_pan_interface(void) { return &pan_if; } bluez-5.82/android/PaxHeaders/pics-avctp.txt0000644000000000000000000000005012537515745016116 xustar0020 atime=1743516876 20 ctime=1743591288 bluez-5.82/android/pics-avctp.txt0000644000000000000000000000647212537515745015610 0ustar00rootrootAVCTP PICS for the PTS tool. PTS version: 6.1 * - different than PTS defaults # - not yet implemented/supported M - mandatory if such role selected O - optional Protocol Version ------------------------------------------------------------------------------- Parameter Name Selected Description ------------------------------------------------------------------------------- TSPC_AVCTP_0_1 False AVCTP 1.0 (C.1) TSPC_AVCTP_0_2 False AVCTP 1.2 (C.1) TSPC_AVCTP_0_3 False AVCTP 1.3 (C.1) TSPC_AVCTP_0_4 True (*) AVCTP 1.4 (C.1) ------------------------------------------------------------------------------- C.1: Mandatory to support only one Protocol Version. ------------------------------------------------------------------------------- Roles ------------------------------------------------------------------------------- Parameter Name Selected Description ------------------------------------------------------------------------------- TSPC_AVCTP_1_1 True (*) Controller (C.1) TSPC_AVCTP_1_2 True (*) Target (C.1) ------------------------------------------------------------------------------- C.1: Mandatory to support at least one of the defined roles. ------------------------------------------------------------------------------- Controller Features ------------------------------------------------------------------------------- Parameter Name Selected Description ------------------------------------------------------------------------------- TSPC_AVCTP_2_1 False Message fragmentation (O) TSPC_AVCTP_2_2 True Transaction label management (M) TSPC_AVCTP_2_3 True Packet type field management (M) TSPC_AVCTP_2_4 True Message type field management (M) TSPC_AVCTP_2_5 True PID field management (M) TSPC_AVCTP_2_6 True IPID field mangement (M) TSPC_AVCTP_2_7 True Message information management (M) TSPC_AVCTP_2_8 False Event registration for message reception (O) TSPC_AVCTP_2_9 False Event registration for connection request (O) TSPC_AVCTP_2_10 False Event registration for disconnection (O) TSPC_AVCTP_2_11 False Connect request (O) TSPC_AVCTP_2_12 False Disconnect request (O) TSPC_AVCTP_2_13 False Send message (O) TSPC_AVCTP_2_14 False Support for multiple AVCTP channel establishment (O) ------------------------------------------------------------------------------- Target Features ------------------------------------------------------------------------------- Parameter Name Selected Description ------------------------------------------------------------------------------- TSPC_AVCTP_3_1 False Message fragmentation (O) TSPC_AVCTP_3_2 True Transaction label management (M) TSPC_AVCTP_3_3 True Packet type field management (M) TSPC_AVCTP_3_4 True Message type field management (M) TSPC_AVCTP_3_5 True PID field management (M) TSPC_AVCTP_3_6 True IPID field management (M) TSPC_AVCTP_3_7 True Message information management (M) TSPC_AVCTP_3_8 True (*) Event registration for message reception (O) TSPC_AVCTP_3_9 True (*) Event registration for connection request (O) TSPC_AVCTP_3_10 True (*) Event registration for disconnection request (O) TSPC_AVCTP_3_11 True (*) Connect request (O) TSPC_AVCTP_3_12 True (*) Disconnect request (O) TSPC_AVCTP_3_13 True (*) Send message (O) TSPC_AVCTP_ALL False Enables all test cases when set to TRUE ------------------------------------------------------------------------------- bluez-5.82/android/PaxHeaders/pics-sm.txt0000644000000000000000000000005012537515745015420 xustar0020 atime=1743516876 20 ctime=1743591289 bluez-5.82/android/pics-sm.txt0000644000000000000000000001054112537515745015102 0ustar00rootrootSM PICS for the PTS tool. PTS version: 6.1 * - different than PTS defaults ^ - field not available on PTS M - mandatory O - optional Connection Roles ------------------------------------------------------------------------------- Parameter Name Selected Description ------------------------------------------------------------------------------- TSPC_SM_1_1 True Master Role (Initiator) (C.1) TSPC_SM_1_2 True Slave Role (Responder) (C.2) ------------------------------------------------------------------------------- C.1: Mandatory to support if TSPC_SM_1_2 is NOT supported, otherwise Optional C.2: Optional IF ((4.0 OR 4.0+HS) AND TSPC_GAP_5_3) OR ((4.1 OR 4.1+HS OR 4.2 OR 4.2+HS) AND (TSPC_GAP_5_3 OR TSPC_GAP_38_3))) ------------------------------------------------------------------------------- Security Properties ------------------------------------------------------------------------------- Parameter Name Selected Description ------------------------------------------------------------------------------- TSPC_SM_2_1 True Authenticated MITM protection (O) TSPC_SM_2_2 True Unauthenticated no MITM protection (C.1) TSPC_SM_2_3 True No security requirements (M) TSPC_SM_2_4 False (*) OOB supported (O) TSPC_SM_2_5 (^) LE Secure Connections (C.2) ------------------------------------------------------------------------------- C.1: If TSPC_SM_2_1 is supported then Mandatory, else Optional C.2: Optional IF Core 4.2 OR Core 4.2+HS are supported, otherwise Excluded ------------------------------------------------------------------------------- Encryption Key Size ------------------------------------------------------------------------------- Parameter Name Selected Description ------------------------------------------------------------------------------- TSPC_SM_3_1 True Encryption Key Size Negotiation (M) ------------------------------------------------------------------------------- Pairing Method ------------------------------------------------------------------------------- Parameter Name Selected Description ------------------------------------------------------------------------------- TSPC_SM_4_1 True Just Works (O) TSPC_SM_4_2 True Passkey Entry (C.1) TSPC_SM_4_3 False (*) Out of Band (C.1) ------------------------------------------------------------------------------- C.1: Mandatory to support at least one of the defined methods IF TSPC_SM_2_1 is supported, otherwise Excluded. ------------------------------------------------------------------------------- Security Initiation ------------------------------------------------------------------------------- Parameter Name Selected Description ------------------------------------------------------------------------------- TSPC_SM_5_1 True Encryption Setup using STK (C.3) TSPC_SM_5_2 True Encryption Setup using LTK (O) TSPC_SM_5_3 True Slave Initiated Security (C.1) TSPC_SM_5_4 True Slave Initiated Security – Master response(C.2) ------------------------------------------------------------------------------- C.1: Mandatory if TSPC_SM_1_2 is supported, otherwise Excluded C.2: Mandatory if TSPC_SM_1_1 is supported, otherwise Excluded C.3: Mandatory IF TSPC_SM_2_1 OR TSPC_SM_2_2 OR TSPC_SM_2_4 is supported, otherwise Excluded ------------------------------------------------------------------------------- Signing Algorithm ------------------------------------------------------------------------------- Parameter Name Selected Description ------------------------------------------------------------------------------- TSPC_SM_6_1 True Signing Algorithm - Generation (O) TSPC_SM_6_2 True Signing Algorithm - Resolving (O) ------------------------------------------------------------------------------- Key Distribution ------------------------------------------------------------------------------- Parameter Name Selected Description ------------------------------------------------------------------------------- TSPC_SM_7_1 True Encryption Key (C.1) TSPC_SM_7_2 True Identity Key (C.2) TSPC_SM_7_3 True Signing Key (C.3) ------------------------------------------------------------------------------- C.1: Mandatory if TSPC_GAP_24_2 OR TSPC_GAP_42_6 is supported, ELSE Optional C.2: Mandatory if TSPC_GAP_26_3 is supported, ELSE Optional C.3: Mandatory if TSPC_GAP_25_6 OR TSPC_GAP_35_6 is supported, ELSE Optional ------------------------------------------------------------------------------- bluez-5.82/android/PaxHeaders/tester-bluetooth.c0000644000000000000000000000005014015011623016734 xustar0020 atime=1743516864 20 ctime=1743591278 bluez-5.82/android/tester-bluetooth.c0000644000000000000000000014043214015011623016421 0ustar00rootroot// SPDX-License-Identifier: Apache-2.0 /* * Copyright (C) 2014 Intel Corporation * */ #define _GNU_SOURCE #include #include "emulator/bthost.h" #include "src/shared/tester.h" #include "src/shared/queue.h" #include "tester-main.h" static struct queue *list; /* List of bluetooth test cases */ static bt_bdaddr_t emu_bdaddr_val = { .address = { 0x00, 0xaa, 0x01, 0x00, 0x00, 0x00 }, }; static bt_property_t prop_emu_bdaddr = { .type = BT_PROPERTY_BDADDR, .val = &emu_bdaddr_val, .len = sizeof(emu_bdaddr_val), }; static char emu_bdname_val[] = "BlueZ for Android"; static bt_property_t prop_emu_bdname = { .type = BT_PROPERTY_BDNAME, .val = &emu_bdname_val, .len = sizeof(emu_bdname_val) - 1, }; static char emu_uuids_val[] = { /* Multi profile UUID */ 0x00, 0x00, 0x11, 0x3b, 0x00, 0x00, 0x10, 0x00, 0x80, 0x00, 0x00, 0x80, 0x5F, 0x9B, 0x34, 0xFB, /* Device identification profile UUID */ 0x00, 0x00, 0x12, 0x00, 0x00, 0x00, 0x10, 0x00, 0x80, 0x00, 0x00, 0x80, 0x5F, 0x9B, 0x34, 0xFB, }; static bt_property_t prop_emu_uuids = { .type = BT_PROPERTY_UUIDS, .val = &emu_uuids_val, .len = sizeof(emu_uuids_val), }; static uint32_t emu_cod_val = 0x00020c; static bt_property_t prop_emu_cod = { .type = BT_PROPERTY_CLASS_OF_DEVICE, .val = &emu_cod_val, .len = sizeof(emu_cod_val), }; static bt_device_type_t emu_tod_dual_val = BT_DEVICE_DEVTYPE_DUAL; static bt_property_t prop_emu_dual_tod = { .type = BT_PROPERTY_TYPE_OF_DEVICE, .val = &emu_tod_dual_val, .len = sizeof(emu_tod_dual_val), }; static bt_scan_mode_t emu_scan_mode_val = BT_SCAN_MODE_NONE; static bt_property_t prop_emu_scan_mode = { .type = BT_PROPERTY_ADAPTER_SCAN_MODE, .val = &emu_scan_mode_val, .len = sizeof(emu_scan_mode_val), }; static uint32_t emu_disc_timeout_val = 120; static bt_property_t prop_emu_disc_timeout = { .type = BT_PROPERTY_ADAPTER_DISCOVERY_TIMEOUT, .val = &emu_disc_timeout_val, .len = sizeof(emu_disc_timeout_val), }; static bt_property_t prop_emu_bonded_devs = { .type = BT_PROPERTY_ADAPTER_BONDED_DEVICES, .val = NULL, .len = 0, }; static bt_bdaddr_t emu_remote_bdaddr_val = { .address = { 0x00, 0xaa, 0x01, 0x01, 0x00, 0x00 }, }; static bt_property_t prop_emu_remote_bdadr = { .type = BT_PROPERTY_BDADDR, .val = &emu_remote_bdaddr_val, .len = sizeof(emu_remote_bdaddr_val), }; static struct bt_action_data prop_emu_remote_ble_bdaddr_req = { .addr = &emu_remote_bdaddr_val, .prop_type = BT_PROPERTY_BDADDR, }; static uint32_t emu_remote_type_val = BT_DEVICE_DEVTYPE_BREDR; static uint32_t emu_remote_tod_ble_val = BT_DEVICE_DEVTYPE_BLE; static bt_property_t prop_emu_remote_ble_tod_prop = { .type = BT_PROPERTY_TYPE_OF_DEVICE, .val = &emu_remote_tod_ble_val, .len = sizeof(emu_remote_tod_ble_val), }; static struct bt_action_data prop_emu_remote_ble_tod_req = { .addr = &emu_remote_bdaddr_val, .prop_type = BT_PROPERTY_TYPE_OF_DEVICE, }; static int32_t emu_remote_rssi_val = -60; static int32_t emu_remote_ble_rssi_val = 127; static bt_property_t prop_emu_remote_ble_rssi_prop = { .type = BT_PROPERTY_REMOTE_RSSI, .val = &emu_remote_ble_rssi_val, .len = sizeof(emu_remote_ble_rssi_val), }; static struct bt_action_data prop_emu_remote_ble_rssi_req = { .addr = &emu_remote_bdaddr_val, .prop_type = BT_PROPERTY_REMOTE_RSSI, }; static char emu_remote_bdname_val[] = "00:AA:01:01:00:00"; static bt_property_t prop_emu_remote_ble_bdname_prop = { .type = BT_PROPERTY_BDNAME, .val = &emu_remote_bdname_val, .len = sizeof(emu_remote_bdname_val) - 1, }; static struct bt_action_data prop_emu_remote_ble_bdname_req = { .addr = &emu_remote_bdaddr_val, .prop_type = BT_PROPERTY_BDNAME, }; static uint32_t emu_remote_cod_val = 0; static bt_property_t prop_emu_remote_ble_cod_prop = { .type = BT_PROPERTY_CLASS_OF_DEVICE, .val = &emu_remote_cod_val, .len = sizeof(emu_remote_cod_val), }; static struct bt_action_data prop_emu_remote_ble_cod_req = { .addr = &emu_remote_bdaddr_val, .prop_type = BT_PROPERTY_CLASS_OF_DEVICE, }; static bt_property_t prop_emu_remote_ble_uuids_prop = { .type = BT_PROPERTY_UUIDS, .val = NULL, .len = 0, }; static struct bt_action_data prop_emu_remote_ble_uuids_req = { .addr = &emu_remote_bdaddr_val, .prop_type = BT_PROPERTY_UUIDS, }; static bt_property_t prop_emu_remote_ble_timestamp_prop = { .type = BT_PROPERTY_REMOTE_DEVICE_TIMESTAMP, .val = NULL, .len = 4, }; static struct bt_action_data prop_emu_remote_ble_timestamp_req = { .addr = &emu_remote_bdaddr_val, .prop_type = BT_PROPERTY_REMOTE_DEVICE_TIMESTAMP, }; static struct bt_action_data prop_emu_remote_ble_scan_mode_req = { .addr = &emu_remote_bdaddr_val, .prop_type = BT_PROPERTY_ADAPTER_SCAN_MODE, }; static struct bt_action_data prop_emu_remote_ble_bondeddev_req = { .addr = &emu_remote_bdaddr_val, .prop_type = BT_PROPERTY_ADAPTER_BONDED_DEVICES, }; static struct bt_action_data prop_emu_remote_ble_disctimeout_req = { .addr = &emu_remote_bdaddr_val, .prop_type = BT_PROPERTY_ADAPTER_DISCOVERY_TIMEOUT, }; static struct bt_action_data prop_emu_remote_ble_verinfo_req = { .addr = &emu_remote_bdaddr_val, .prop_type = BT_PROPERTY_REMOTE_VERSION_INFO, }; static char prop_test_fname_val[] = "FriendlyTestName"; static bt_property_t prop_emu_remote_ble_fname_prop = { .type = BT_PROPERTY_REMOTE_FRIENDLY_NAME, .val = &prop_test_fname_val, .len = sizeof(prop_test_fname_val) - 1, }; static struct bt_action_data prop_emu_remote_ble_fname_req = { .addr = &emu_remote_bdaddr_val, .prop_type = BT_PROPERTY_REMOTE_FRIENDLY_NAME, .prop = &prop_emu_remote_ble_fname_prop, }; static bt_pin_code_t emu_pin_value = { .pin = { 0x30, 0x30, 0x30, 0x30 }, }; static bt_pin_code_t emu_pin_invalid_value = { .pin = { 0x30, 0x10, 0x30, 0x30 }, }; static struct bt_action_data emu_pin_set_req = { .addr = &emu_remote_bdaddr_val, .pin = &emu_pin_value, .pin_len = 4, }; static struct bt_action_data emu_pin_set_invalid_req = { .addr = &emu_remote_bdaddr_val, .pin = &emu_pin_invalid_value, .pin_len = 4, }; static bt_property_t prop_emu_default_set[] = { { BT_PROPERTY_BDADDR, sizeof(emu_bdaddr_val), NULL }, { BT_PROPERTY_BDNAME, sizeof(emu_bdname_val) - 1, &emu_bdname_val }, { BT_PROPERTY_CLASS_OF_DEVICE, sizeof(uint32_t), NULL }, { BT_PROPERTY_TYPE_OF_DEVICE, sizeof(emu_tod_dual_val), &emu_tod_dual_val }, { BT_PROPERTY_ADAPTER_SCAN_MODE, sizeof(emu_scan_mode_val), &emu_scan_mode_val }, { BT_PROPERTY_ADAPTER_DISCOVERY_TIMEOUT, sizeof(emu_disc_timeout_val), &emu_disc_timeout_val}, { BT_PROPERTY_ADAPTER_BONDED_DEVICES, 0, NULL }, { BT_PROPERTY_UUIDS, sizeof(emu_uuids_val), &emu_uuids_val }, }; static bt_property_t prop_emu_remote_ble_default_set[] = { { BT_PROPERTY_BDADDR, sizeof(emu_remote_bdaddr_val), &emu_remote_bdaddr_val }, { BT_PROPERTY_TYPE_OF_DEVICE, sizeof(emu_remote_tod_ble_val), &emu_remote_tod_ble_val }, { BT_PROPERTY_REMOTE_RSSI, sizeof(emu_remote_ble_rssi_val), &emu_remote_ble_rssi_val }, }; static bt_property_t prop_emu_remote_bredr_default_set[] = { { BT_PROPERTY_BDADDR, sizeof(emu_remote_bdaddr_val), &emu_remote_bdaddr_val }, { BT_PROPERTY_TYPE_OF_DEVICE, sizeof(emu_remote_type_val), &emu_remote_type_val }, { BT_PROPERTY_REMOTE_RSSI, sizeof(emu_remote_rssi_val), &emu_remote_rssi_val }, }; static bt_property_t prop_emu_remote_any_default_set[] = { { BT_PROPERTY_BDADDR, sizeof(emu_remote_bdaddr_val), &emu_remote_bdaddr_val }, }; static bt_property_t prop_emu_remote_bles_query_set[] = { { BT_PROPERTY_TYPE_OF_DEVICE, sizeof(emu_remote_tod_ble_val), &emu_remote_tod_ble_val }, { BT_PROPERTY_CLASS_OF_DEVICE, sizeof(emu_remote_cod_val), &emu_remote_cod_val }, { BT_PROPERTY_REMOTE_RSSI, sizeof(emu_remote_ble_rssi_val), &emu_remote_ble_rssi_val }, { BT_PROPERTY_BDNAME, sizeof(emu_remote_bdname_val) - 1, &emu_remote_bdname_val }, { BT_PROPERTY_UUIDS, 0, NULL }, { BT_PROPERTY_REMOTE_DEVICE_TIMESTAMP, 4, NULL }, }; static bt_property_t prop_emu_remotes_pin_req_set[] = { { BT_PROPERTY_BDADDR, sizeof(emu_remote_bdaddr_val), &emu_remote_bdaddr_val }, { BT_PROPERTY_CLASS_OF_DEVICE, sizeof(emu_remote_cod_val), &emu_remote_cod_val }, { BT_PROPERTY_BDNAME, sizeof(emu_remote_bdname_val) - 1, &emu_remote_bdname_val }, }; static char test_bdname[] = "test_bdname"; static bt_property_t prop_test_bdname = { .type = BT_PROPERTY_BDNAME, .val = test_bdname, .len = sizeof(test_bdname) - 1, }; static struct bt_action_data prop_test_remote_ble_bdname_req = { .addr = &emu_remote_bdaddr_val, .prop_type = BT_PROPERTY_BDNAME, .prop = &prop_test_bdname, }; static bt_scan_mode_t test_scan_mode_connectable_discoverable = BT_SCAN_MODE_CONNECTABLE_DISCOVERABLE; static bt_property_t prop_test_scanmode_conn_discov = { .type = BT_PROPERTY_ADAPTER_SCAN_MODE, .val = &test_scan_mode_connectable_discoverable, .len = sizeof(bt_scan_mode_t), }; static uint32_t test_disctimeout_val = 600; static bt_property_t prop_test_disctimeout = { .type = BT_PROPERTY_ADAPTER_DISCOVERY_TIMEOUT, .val = &test_disctimeout_val, .len = sizeof(test_disctimeout_val), }; static struct bt_action_data prop_test_remote_ble_disc_timeout_req = { .addr = &emu_remote_bdaddr_val, .prop_type = BT_PROPERTY_ADAPTER_DISCOVERY_TIMEOUT, .prop = &prop_test_disctimeout, }; static unsigned char test_uuids_val[] = { 0xfb, 0x34, 0x9b, 0x5f, 0x80, 0x00, 0x00, 0x80, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; static bt_property_t prop_test_uuid = { .type = BT_PROPERTY_UUIDS, .val = &test_uuids_val, .len = sizeof(test_uuids_val), }; static struct bt_action_data prop_test_remote_ble_uuids_req = { .addr = &emu_remote_bdaddr_val, .prop_type = BT_PROPERTY_UUIDS, .prop = &prop_test_uuid, }; static uint32_t test_cod_val = 0; static bt_property_t prop_test_cod = { .type = BT_PROPERTY_CLASS_OF_DEVICE, .val = &test_cod_val, .len = sizeof(test_cod_val), }; static struct bt_action_data prop_test_remote_ble_cod_req = { .addr = &emu_remote_bdaddr_val, .prop_type = BT_PROPERTY_CLASS_OF_DEVICE, .prop = &prop_test_cod, }; static uint32_t test_tod_val = BT_DEVICE_DEVTYPE_BLE; static bt_property_t prop_test_tod = { .type = BT_PROPERTY_TYPE_OF_DEVICE, .val = &test_tod_val, .len = sizeof(test_tod_val), }; static struct bt_action_data prop_test_remote_ble_tod_req = { .addr = &emu_remote_bdaddr_val, .prop_type = BT_PROPERTY_TYPE_OF_DEVICE, .prop = &prop_test_tod, }; static int32_t test_remote_rssi_val = -9; static bt_property_t prop_test_remote_rssi = { .type = BT_PROPERTY_REMOTE_RSSI, .val = &test_remote_rssi_val, .len = sizeof(test_remote_rssi_val), }; static struct bt_action_data prop_test_remote_ble_rssi_req = { .addr = &emu_remote_bdaddr_val, .prop_type = BT_PROPERTY_REMOTE_RSSI, .prop = &prop_test_remote_rssi, }; static bt_service_record_t test_srvc_record_val = { .uuid = { {0x00} }, .channel = 12, .name = "bt_name", }; static bt_property_t prop_test_srvc_record = { .type = BT_PROPERTY_SERVICE_RECORD, .val = &test_srvc_record_val, .len = sizeof(test_srvc_record_val), }; static struct bt_action_data prop_test_remote_ble_srvc_record_req = { .addr = &emu_remote_bdaddr_val, .prop_type = BT_PROPERTY_SERVICE_RECORD, .prop = &prop_test_srvc_record, }; static bt_bdaddr_t test_bdaddr_val = { .address = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, }; static bt_property_t prop_test_bdaddr = { .type = BT_PROPERTY_BDADDR, .val = &test_bdaddr_val, .len = sizeof(test_bdaddr_val), }; static struct bt_action_data prop_test_remote_ble_bdaddr_req = { .addr = &emu_remote_bdaddr_val, .prop_type = BT_PROPERTY_BDADDR, .prop = &prop_test_bdaddr, }; static struct bt_action_data prop_test_bdaddr_req = { .addr = &test_bdaddr_val, .prop_type = BT_PROPERTY_BDADDR, .prop = &prop_test_bdaddr, }; static bt_scan_mode_t setprop_scan_mode_conn_val = BT_SCAN_MODE_CONNECTABLE; static bt_property_t prop_test_scan_mode_conn = { .type = BT_PROPERTY_ADAPTER_SCAN_MODE, .val = &setprop_scan_mode_conn_val, .len = sizeof(setprop_scan_mode_conn_val), }; static bt_scan_mode_t test_scan_mode_none_val = BT_SCAN_MODE_NONE; static bt_property_t prop_test_scan_mode_none = { .type = BT_PROPERTY_ADAPTER_SCAN_MODE, .val = &test_scan_mode_none_val, .len = sizeof(test_scan_mode_none_val), }; static struct bt_action_data prop_test_remote_ble_scanmode_req = { .addr = &emu_remote_bdaddr_val, .prop_type = BT_PROPERTY_ADAPTER_SCAN_MODE, .prop = &prop_test_scan_mode_none, }; static bt_bdaddr_t test_bonded_dev_addr_val = { .address = { 0x00, 0x01, 0x02, 0x03, 0x04, 0x05 }, }; static bt_property_t prop_test_bonded_dev_addr = { .type = BT_PROPERTY_ADAPTER_BONDED_DEVICES, .val = &test_bonded_dev_addr_val, .len = sizeof(test_bonded_dev_addr_val), }; static struct bt_action_data prop_test_ble_bonded_dev_req = { .addr = &emu_remote_bdaddr_val, .prop_type = BT_PROPERTY_ADAPTER_BONDED_DEVICES, .prop = &prop_test_bonded_dev_addr, }; static uint32_t test_remote_timestamp_val = 42; static bt_property_t prop_test_remote_ble_timestamp_prop = { .type = BT_PROPERTY_REMOTE_DEVICE_TIMESTAMP, .val = &test_remote_timestamp_val, .len = sizeof(test_remote_timestamp_val), }; static struct bt_action_data prop_test_remote_ble_timestamp_req = { .addr = &emu_remote_bdaddr_val, .prop_type = BT_PROPERTY_REMOTE_DEVICE_TIMESTAMP, .prop = &prop_test_remote_ble_timestamp_prop, }; static struct bt_action_data ssp_confirm_accept_reply = { .addr = &emu_remote_bdaddr_val, .ssp_variant = BT_SSP_VARIANT_PASSKEY_CONFIRMATION, .accept = TRUE, }; static struct bt_action_data ssp_confirm_reject_reply = { .addr = &emu_remote_bdaddr_val, .ssp_variant = BT_SSP_VARIANT_PASSKEY_CONFIRMATION, .accept = FALSE, }; static struct bt_action_data no_input_no_output_io_cap = { .io_cap = 0x03, }; static struct bt_action_data display_yes_no_io_cap = { .io_cap = 0x01, }; static uint16_t test_conn_handle = 0; static void conn_cb(uint16_t handle, void *user_data) { struct test_data *data = tester_get_data(); struct bthost *bthost = hciemu_client_get_host(data->hciemu); tester_print("New connection with handle 0x%04x", handle); test_conn_handle = handle; bthost_request_auth(bthost, handle); } static struct test_case test_cases[] = { TEST_CASE_BREDRLE("Bluetooth Init", ACTION_SUCCESS(dummy_action, NULL), ), TEST_CASE_BREDRLE("Bluetooth Enable - Success", ACTION_SUCCESS(bluetooth_enable_action, NULL), CALLBACK_ADAPTER_PROPS(prop_emu_default_set, 8), CALLBACK_STATE(CB_BT_ADAPTER_STATE_CHANGED, BT_STATE_ON), ), TEST_CASE_BREDRLE("Bluetooth Enable - Success 2", ACTION_SUCCESS(bluetooth_enable_action, NULL), CALLBACK_ADAPTER_PROPS(prop_emu_default_set, 8), CALLBACK_STATE(CB_BT_ADAPTER_STATE_CHANGED, BT_STATE_ON), ACTION_SUCCESS(bluetooth_enable_action, NULL), ), TEST_CASE_BREDRLE("Bluetooth Disable - Success", ACTION_SUCCESS(bluetooth_enable_action, NULL), CALLBACK_STATE(CB_BT_ADAPTER_STATE_CHANGED, BT_STATE_ON), ACTION_SUCCESS(bluetooth_disable_action, NULL), CALLBACK_STATE(CB_BT_ADAPTER_STATE_CHANGED, BT_STATE_OFF), ), TEST_CASE_BREDRLE("Bluetooth Set BDNAME - Success", ACTION_SUCCESS(bluetooth_enable_action, NULL), CALLBACK_STATE(CB_BT_ADAPTER_STATE_CHANGED, BT_STATE_ON), ACTION_SUCCESS(bt_set_property_action, &prop_test_bdname), CALLBACK_ADAPTER_PROPS(&prop_test_bdname, 1), ), TEST_CASE_BREDRLE("Bluetooth Set SCAN_MODE - Success", ACTION_SUCCESS(bluetooth_enable_action, NULL), CALLBACK_STATE(CB_BT_ADAPTER_STATE_CHANGED, BT_STATE_ON), ACTION_SUCCESS(bt_set_property_action, &prop_test_scanmode_conn_discov), CALLBACK_ADAPTER_PROPS(&prop_test_scanmode_conn_discov, 1), ), TEST_CASE_BREDRLE("Bluetooth Set DISCOVERY_TIMEOUT - Success", ACTION_SUCCESS(bluetooth_enable_action, NULL), CALLBACK_STATE(CB_BT_ADAPTER_STATE_CHANGED, BT_STATE_ON), ACTION_SUCCESS(bt_set_property_action, &prop_test_disctimeout), CALLBACK_ADAPTER_PROPS(&prop_test_disctimeout, 1), ), TEST_CASE_BREDRLE("Bluetooth Get BDADDR - Success", ACTION_SUCCESS(bluetooth_enable_action, NULL), CALLBACK_STATE(CB_BT_ADAPTER_STATE_CHANGED, BT_STATE_ON), ACTION_SUCCESS(bt_get_property_action, &prop_emu_bdaddr), CALLBACK_ADAPTER_PROPS(&prop_emu_bdaddr, 1), ), TEST_CASE_BREDRLE("Bluetooth Get BDNAME - Success", ACTION_SUCCESS(bluetooth_enable_action, NULL), CALLBACK_STATE(CB_BT_ADAPTER_STATE_CHANGED, BT_STATE_ON), ACTION_SUCCESS(bt_get_property_action, &prop_emu_bdname), CALLBACK_ADAPTER_PROPS(&prop_emu_bdname, 1), ), TEST_CASE_BREDRLE("Bluetooth Set UUID - Fail", ACTION_SUCCESS(bluetooth_enable_action, NULL), CALLBACK_STATE(CB_BT_ADAPTER_STATE_CHANGED, BT_STATE_ON), ACTION_FAIL(bt_set_property_action, &prop_test_uuid), ), TEST_CASE_BREDRLE("Bluetooth Set CLASS_OF_DEVICE - Fail", ACTION_SUCCESS(bluetooth_enable_action, NULL), CALLBACK_STATE(CB_BT_ADAPTER_STATE_CHANGED, BT_STATE_ON), ACTION_FAIL(bt_set_property_action, &prop_test_cod), ), TEST_CASE_BREDRLE("Bluetooth Set TYPE_OF_DEVICE - Fail", ACTION_SUCCESS(bluetooth_enable_action, NULL), CALLBACK_STATE(CB_BT_ADAPTER_STATE_CHANGED, BT_STATE_ON), ACTION_FAIL(bt_set_property_action, &prop_test_tod), ), TEST_CASE_BREDRLE("Bluetooth Set REMOTE_RSSI - Fail", ACTION_SUCCESS(bluetooth_enable_action, NULL), CALLBACK_STATE(CB_BT_ADAPTER_STATE_CHANGED, BT_STATE_ON), ACTION_FAIL(bt_set_property_action, &prop_test_remote_rssi), ), TEST_CASE_BREDRLE("Bluetooth Set SERVICE_RECORD - Fail", ACTION_SUCCESS(bluetooth_enable_action, NULL), CALLBACK_STATE(CB_BT_ADAPTER_STATE_CHANGED, BT_STATE_ON), ACTION_FAIL(bt_set_property_action, &prop_test_srvc_record), ), TEST_CASE_BREDRLE("Bluetooth Set BDADDR - Fail", ACTION_SUCCESS(bluetooth_enable_action, NULL), CALLBACK_STATE(CB_BT_ADAPTER_STATE_CHANGED, BT_STATE_ON), ACTION_FAIL(bt_set_property_action, &prop_test_bdaddr), ), TEST_CASE_BREDRLE("Bluetooth Set SCAN_MODE_CONNECTABLE - Success", ACTION_SUCCESS(bluetooth_enable_action, NULL), CALLBACK_STATE(CB_BT_ADAPTER_STATE_CHANGED, BT_STATE_ON), ACTION_SUCCESS(bt_set_property_action, &prop_test_scan_mode_conn), CALLBACK_ADAPTER_PROPS(&prop_test_scan_mode_conn, 1), ), TEST_CASE_BREDRLE("Bluetooth Set BONDED_DEVICES - Fail", ACTION_SUCCESS(bluetooth_enable_action, NULL), CALLBACK_STATE(CB_BT_ADAPTER_STATE_CHANGED, BT_STATE_ON), ACTION_FAIL(bt_set_property_action, &prop_test_bonded_dev_addr), ), TEST_CASE_BREDRLE("Bluetooth Get CLASS_OF_DEVICE - Success", ACTION_SUCCESS(bluetooth_enable_action, NULL), CALLBACK_STATE(CB_BT_ADAPTER_STATE_CHANGED, BT_STATE_ON), ACTION_SUCCESS(bt_get_property_action, &prop_emu_cod), CALLBACK_ADAPTER_PROPS(&prop_emu_cod, 1), ), TEST_CASE_BREDRLE("Bluetooth Get TYPE_OF_DEVICE - Success", ACTION_SUCCESS(bluetooth_enable_action, NULL), CALLBACK_STATE(CB_BT_ADAPTER_STATE_CHANGED, BT_STATE_ON), ACTION_SUCCESS(bt_get_property_action, &prop_emu_dual_tod), CALLBACK_ADAPTER_PROPS(&prop_emu_dual_tod, 1), ), TEST_CASE_BREDRLE("Bluetooth Get SCAN_MODE - Success", ACTION_SUCCESS(bluetooth_enable_action, NULL), CALLBACK_STATE(CB_BT_ADAPTER_STATE_CHANGED, BT_STATE_ON), ACTION_SUCCESS(bt_get_property_action, &prop_emu_scan_mode), CALLBACK_ADAPTER_PROPS(&prop_emu_scan_mode, 1), ), TEST_CASE_BREDRLE("Bluetooth Get DISCOVERY_TIMEOUT - Success", ACTION_SUCCESS(bluetooth_enable_action, NULL), CALLBACK_STATE(CB_BT_ADAPTER_STATE_CHANGED, BT_STATE_ON), ACTION_SUCCESS(bt_get_property_action, &prop_emu_disc_timeout), CALLBACK_ADAPTER_PROPS(&prop_emu_disc_timeout, 1), ), TEST_CASE_BREDRLE("Bluetooth Get UUIDS - Success", ACTION_SUCCESS(bluetooth_enable_action, NULL), CALLBACK_STATE(CB_BT_ADAPTER_STATE_CHANGED, BT_STATE_ON), ACTION_SUCCESS(bt_get_property_action, &prop_emu_uuids), CALLBACK_ADAPTER_PROPS(&prop_emu_uuids, 1), ), TEST_CASE_BREDRLE("Bluetooth Get BONDED_DEVICES - Success", ACTION_SUCCESS(bluetooth_enable_action, NULL), CALLBACK_STATE(CB_BT_ADAPTER_STATE_CHANGED, BT_STATE_ON), ACTION_SUCCESS(bt_get_property_action, &prop_emu_bonded_devs), CALLBACK_ADAPTER_PROPS(&prop_emu_bonded_devs, 1), ), TEST_CASE_BREDRLE("Bluetooth Set SCAN_MODE - Success 2", ACTION_SUCCESS(bluetooth_enable_action, NULL), CALLBACK_STATE(CB_BT_ADAPTER_STATE_CHANGED, BT_STATE_ON), ACTION_SUCCESS(bt_set_property_action, &prop_test_scan_mode_none), CALLBACK_ADAPTER_PROPS(&prop_test_scan_mode_none, 1), ), TEST_CASE_BREDRLE("Bluetooth Discovery Start - Success", ACTION_SUCCESS(bluetooth_enable_action, NULL), CALLBACK_STATE(CB_BT_ADAPTER_STATE_CHANGED, BT_STATE_ON), ACTION_SUCCESS(bt_start_discovery_action, NULL), CALLBACK_STATE(CB_BT_DISCOVERY_STATE_CHANGED, BT_DISCOVERY_STARTED), ), TEST_CASE_BREDRLE("Bluetooth Discovery Start - Done", ACTION_SUCCESS(bluetooth_enable_action, NULL), CALLBACK_STATE(CB_BT_ADAPTER_STATE_CHANGED, BT_STATE_ON), ACTION_SUCCESS(bt_start_discovery_action, NULL), CALLBACK_STATE(CB_BT_DISCOVERY_STATE_CHANGED, BT_DISCOVERY_STARTED), ACTION_SUCCESS(bt_start_discovery_action, NULL), ), TEST_CASE_BREDRLE("Bluetooth Discovery Stop - Success", ACTION_SUCCESS(bluetooth_enable_action, NULL), CALLBACK_STATE(CB_BT_ADAPTER_STATE_CHANGED, BT_STATE_ON), ACTION_SUCCESS(bt_start_discovery_action, NULL), CALLBACK_STATE(CB_BT_DISCOVERY_STATE_CHANGED, BT_DISCOVERY_STARTED), ACTION_SUCCESS(bt_cancel_discovery_action, NULL), CALLBACK_STATE(CB_BT_DISCOVERY_STATE_CHANGED, BT_DISCOVERY_STOPPED), ), TEST_CASE_BREDRLE("Bluetooth Discovery Stop - Done", ACTION_SUCCESS(bluetooth_enable_action, NULL), CALLBACK_STATE(CB_BT_ADAPTER_STATE_CHANGED, BT_STATE_ON), ACTION_SUCCESS(bt_start_discovery_action, NULL), CALLBACK_STATE(CB_BT_DISCOVERY_STATE_CHANGED, BT_DISCOVERY_STARTED), ACTION_SUCCESS(bt_cancel_discovery_action, NULL), CALLBACK_STATE(CB_BT_DISCOVERY_STATE_CHANGED, BT_DISCOVERY_STOPPED), ACTION_SUCCESS(bt_start_discovery_action, NULL), ), TEST_CASE_BREDRLE("Bluetooth Discovery Device Found", ACTION_SUCCESS(bluetooth_enable_action, NULL), CALLBACK_STATE(CB_BT_ADAPTER_STATE_CHANGED, BT_STATE_ON), ACTION_SUCCESS(bt_start_discovery_action, NULL), CALLBACK_STATE(CB_BT_DISCOVERY_STATE_CHANGED, BT_DISCOVERY_STARTED), ACTION_SUCCESS(emu_setup_powered_remote_action, NULL), CALLBACK_DEVICE_FOUND(prop_emu_remote_ble_default_set, 3), ACTION_SUCCESS(bt_cancel_discovery_action, NULL), CALLBACK_STATE(CB_BT_DISCOVERY_STATE_CHANGED, BT_DISCOVERY_STOPPED), ), TEST_CASE_BREDRLE("Bluetooth Device Get Props - Success", ACTION_SUCCESS(bluetooth_enable_action, NULL), CALLBACK_STATE(CB_BT_ADAPTER_STATE_CHANGED, BT_STATE_ON), ACTION_SUCCESS(bt_start_discovery_action, NULL), CALLBACK_STATE(CB_BT_DISCOVERY_STATE_CHANGED, BT_DISCOVERY_STARTED), ACTION_SUCCESS(emu_setup_powered_remote_action, NULL), CALLBACK_DEVICE_FOUND(prop_emu_remote_any_default_set, 1), ACTION_SUCCESS(bt_cancel_discovery_action, NULL), CALLBACK_STATE(CB_BT_DISCOVERY_STATE_CHANGED, BT_DISCOVERY_STOPPED), ACTION_SUCCESS(bt_get_device_props_action, &emu_remote_bdaddr_val), CALLBACK_DEVICE_PROPS(prop_emu_remote_bles_query_set, 6), ), TEST_CASE_BREDRLE("Bluetooth Device Get BDNAME - Success", ACTION_SUCCESS(bluetooth_enable_action, NULL), CALLBACK_STATE(CB_BT_ADAPTER_STATE_CHANGED, BT_STATE_ON), ACTION_SUCCESS(bt_start_discovery_action, NULL), CALLBACK_STATE(CB_BT_DISCOVERY_STATE_CHANGED, BT_DISCOVERY_STARTED), ACTION_SUCCESS(emu_setup_powered_remote_action, NULL), CALLBACK_DEVICE_FOUND(prop_emu_remote_any_default_set, 1), ACTION_SUCCESS(bt_cancel_discovery_action, NULL), CALLBACK_STATE(CB_BT_DISCOVERY_STATE_CHANGED, BT_DISCOVERY_STOPPED), ACTION_SUCCESS(bt_get_device_prop_action, &prop_emu_remote_ble_bdname_req), CALLBACK_DEVICE_PROPS(&prop_emu_remote_ble_bdname_prop, 1), ), TEST_CASE_BREDRLE("Bluetooth Device Get UUIDS - Success", ACTION_SUCCESS(bluetooth_enable_action, NULL), CALLBACK_STATE(CB_BT_ADAPTER_STATE_CHANGED, BT_STATE_ON), ACTION_SUCCESS(bt_start_discovery_action, NULL), CALLBACK_STATE(CB_BT_DISCOVERY_STATE_CHANGED, BT_DISCOVERY_STARTED), ACTION_SUCCESS(emu_setup_powered_remote_action, NULL), CALLBACK_DEVICE_FOUND(prop_emu_remote_any_default_set, 1), ACTION_SUCCESS(bt_cancel_discovery_action, NULL), CALLBACK_STATE(CB_BT_DISCOVERY_STATE_CHANGED, BT_DISCOVERY_STOPPED), ACTION_SUCCESS(bt_get_device_prop_action, &prop_emu_remote_ble_uuids_req), CALLBACK_DEVICE_PROPS(&prop_emu_remote_ble_uuids_prop, 1), ), TEST_CASE_BREDRLE("Bluetooth Device Get COD - Success", ACTION_SUCCESS(bluetooth_enable_action, NULL), CALLBACK_STATE(CB_BT_ADAPTER_STATE_CHANGED, BT_STATE_ON), ACTION_SUCCESS(bt_start_discovery_action, NULL), CALLBACK_STATE(CB_BT_DISCOVERY_STATE_CHANGED, BT_DISCOVERY_STARTED), ACTION_SUCCESS(emu_setup_powered_remote_action, NULL), CALLBACK_DEVICE_FOUND(prop_emu_remote_any_default_set, 1), ACTION_SUCCESS(bt_cancel_discovery_action, NULL), CALLBACK_STATE(CB_BT_DISCOVERY_STATE_CHANGED, BT_DISCOVERY_STOPPED), ACTION_SUCCESS(bt_get_device_prop_action, &prop_emu_remote_ble_cod_req), CALLBACK_DEVICE_PROPS(&prop_emu_remote_ble_cod_prop, 1), ), TEST_CASE_BREDRLE("Bluetooth Device Get TOD - Success", ACTION_SUCCESS(bluetooth_enable_action, NULL), CALLBACK_STATE(CB_BT_ADAPTER_STATE_CHANGED, BT_STATE_ON), ACTION_SUCCESS(bt_start_discovery_action, NULL), CALLBACK_STATE(CB_BT_DISCOVERY_STATE_CHANGED, BT_DISCOVERY_STARTED), ACTION_SUCCESS(emu_setup_powered_remote_action, NULL), CALLBACK_DEVICE_FOUND(prop_emu_remote_any_default_set, 1), ACTION_SUCCESS(bt_cancel_discovery_action, NULL), CALLBACK_STATE(CB_BT_DISCOVERY_STATE_CHANGED, BT_DISCOVERY_STOPPED), ACTION_SUCCESS(bt_get_device_prop_action, &prop_emu_remote_ble_tod_req), CALLBACK_DEVICE_PROPS(&prop_emu_remote_ble_tod_prop, 1), ), TEST_CASE_BREDRLE("Bluetooth Device Get RSSI - Success", ACTION_SUCCESS(bluetooth_enable_action, NULL), CALLBACK_STATE(CB_BT_ADAPTER_STATE_CHANGED, BT_STATE_ON), ACTION_SUCCESS(bt_start_discovery_action, NULL), CALLBACK_STATE(CB_BT_DISCOVERY_STATE_CHANGED, BT_DISCOVERY_STARTED), ACTION_SUCCESS(emu_setup_powered_remote_action, NULL), CALLBACK_DEVICE_FOUND(prop_emu_remote_any_default_set, 1), ACTION_SUCCESS(bt_cancel_discovery_action, NULL), CALLBACK_STATE(CB_BT_DISCOVERY_STATE_CHANGED, BT_DISCOVERY_STOPPED), ACTION_SUCCESS(bt_get_device_prop_action, &prop_emu_remote_ble_rssi_req), CALLBACK_DEVICE_PROPS(&prop_emu_remote_ble_rssi_prop, 1), ), TEST_CASE_BREDRLE("Bluetooth Device Get TIMESTAMP - Success", ACTION_SUCCESS(bluetooth_enable_action, NULL), CALLBACK_STATE(CB_BT_ADAPTER_STATE_CHANGED, BT_STATE_ON), ACTION_SUCCESS(bt_start_discovery_action, NULL), CALLBACK_STATE(CB_BT_DISCOVERY_STATE_CHANGED, BT_DISCOVERY_STARTED), ACTION_SUCCESS(emu_setup_powered_remote_action, NULL), CALLBACK_DEVICE_FOUND(prop_emu_remote_any_default_set, 1), ACTION_SUCCESS(bt_cancel_discovery_action, NULL), CALLBACK_STATE(CB_BT_DISCOVERY_STATE_CHANGED, BT_DISCOVERY_STOPPED), ACTION_SUCCESS(bt_get_device_prop_action, &prop_emu_remote_ble_timestamp_req), CALLBACK_DEVICE_PROPS(&prop_emu_remote_ble_timestamp_prop, 1), ), TEST_CASE_BREDRLE("Bluetooth Device Get BDADDR - Fail", ACTION_SUCCESS(bluetooth_enable_action, NULL), CALLBACK_STATE(CB_BT_ADAPTER_STATE_CHANGED, BT_STATE_ON), ACTION_SUCCESS(bt_start_discovery_action, NULL), CALLBACK_STATE(CB_BT_DISCOVERY_STATE_CHANGED, BT_DISCOVERY_STARTED), ACTION_SUCCESS(emu_setup_powered_remote_action, NULL), CALLBACK_DEVICE_FOUND(prop_emu_remote_any_default_set, 1), ACTION_SUCCESS(bt_cancel_discovery_action, NULL), CALLBACK_STATE(CB_BT_DISCOVERY_STATE_CHANGED, BT_DISCOVERY_STOPPED), ACTION_FAIL(bt_get_device_prop_action, &prop_emu_remote_ble_bdaddr_req), ), TEST_CASE_BREDRLE("Bluetooth Device Get SCAN_MODE - Fail", ACTION_SUCCESS(bluetooth_enable_action, NULL), CALLBACK_STATE(CB_BT_ADAPTER_STATE_CHANGED, BT_STATE_ON), ACTION_SUCCESS(bt_start_discovery_action, NULL), CALLBACK_STATE(CB_BT_DISCOVERY_STATE_CHANGED, BT_DISCOVERY_STARTED), ACTION_SUCCESS(emu_setup_powered_remote_action, NULL), CALLBACK_DEVICE_FOUND(prop_emu_remote_any_default_set, 1), ACTION_SUCCESS(bt_cancel_discovery_action, NULL), CALLBACK_STATE(CB_BT_DISCOVERY_STATE_CHANGED, BT_DISCOVERY_STOPPED), ACTION_FAIL(bt_get_device_prop_action, &prop_emu_remote_ble_scan_mode_req), ), TEST_CASE_BREDRLE("Bluetooth Device Get BONDED_DEVICES - Fail", ACTION_SUCCESS(bluetooth_enable_action, NULL), CALLBACK_STATE(CB_BT_ADAPTER_STATE_CHANGED, BT_STATE_ON), ACTION_SUCCESS(bt_start_discovery_action, NULL), CALLBACK_STATE(CB_BT_DISCOVERY_STATE_CHANGED, BT_DISCOVERY_STARTED), ACTION_SUCCESS(emu_setup_powered_remote_action, NULL), CALLBACK_DEVICE_FOUND(prop_emu_remote_any_default_set, 1), ACTION_SUCCESS(bt_cancel_discovery_action, NULL), CALLBACK_STATE(CB_BT_DISCOVERY_STATE_CHANGED, BT_DISCOVERY_STOPPED), ACTION_FAIL(bt_get_device_prop_action, &prop_emu_remote_ble_bondeddev_req), ), TEST_CASE_BREDRLE("Bluetooth Device Get DISCOVERY_TIMEOUT - Fail", ACTION_SUCCESS(bluetooth_enable_action, NULL), CALLBACK_STATE(CB_BT_ADAPTER_STATE_CHANGED, BT_STATE_ON), ACTION_SUCCESS(bt_start_discovery_action, NULL), CALLBACK_STATE(CB_BT_DISCOVERY_STATE_CHANGED, BT_DISCOVERY_STARTED), ACTION_SUCCESS(emu_setup_powered_remote_action, NULL), CALLBACK_DEVICE_FOUND(prop_emu_remote_any_default_set, 1), ACTION_SUCCESS(bt_cancel_discovery_action, NULL), CALLBACK_STATE(CB_BT_DISCOVERY_STATE_CHANGED, BT_DISCOVERY_STOPPED), ACTION_FAIL(bt_get_device_prop_action, &prop_emu_remote_ble_disctimeout_req), ), TEST_CASE_BREDRLE("Bluetooth Device Get VERSION_INFO - Fail", ACTION_SUCCESS(bluetooth_enable_action, NULL), CALLBACK_STATE(CB_BT_ADAPTER_STATE_CHANGED, BT_STATE_ON), ACTION_SUCCESS(bt_start_discovery_action, NULL), CALLBACK_STATE(CB_BT_DISCOVERY_STATE_CHANGED, BT_DISCOVERY_STARTED), ACTION_SUCCESS(emu_setup_powered_remote_action, NULL), CALLBACK_DEVICE_FOUND(prop_emu_remote_any_default_set, 1), ACTION_SUCCESS(bt_cancel_discovery_action, NULL), CALLBACK_STATE(CB_BT_DISCOVERY_STATE_CHANGED, BT_DISCOVERY_STOPPED), ACTION_FAIL(bt_get_device_prop_action, &prop_emu_remote_ble_verinfo_req), ), TEST_CASE_BREDRLE("Bluetooth Device Get FRIENDLY_NAME - Fail", ACTION_SUCCESS(bluetooth_enable_action, NULL), CALLBACK_STATE(CB_BT_ADAPTER_STATE_CHANGED, BT_STATE_ON), ACTION_SUCCESS(bt_start_discovery_action, NULL), CALLBACK_STATE(CB_BT_DISCOVERY_STATE_CHANGED, BT_DISCOVERY_STARTED), ACTION_SUCCESS(emu_setup_powered_remote_action, NULL), CALLBACK_DEVICE_FOUND(prop_emu_remote_any_default_set, 1), ACTION_SUCCESS(bt_cancel_discovery_action, NULL), CALLBACK_STATE(CB_BT_DISCOVERY_STATE_CHANGED, BT_DISCOVERY_STOPPED), ACTION_FAIL(bt_get_device_prop_action, &prop_emu_remote_ble_fname_req), ), TEST_CASE_BREDRLE("Bluetooth Device Set FRIENDLY_NAME - Success", ACTION_SUCCESS(bluetooth_enable_action, NULL), CALLBACK_STATE(CB_BT_ADAPTER_STATE_CHANGED, BT_STATE_ON), ACTION_SUCCESS(bt_start_discovery_action, NULL), CALLBACK_STATE(CB_BT_DISCOVERY_STATE_CHANGED, BT_DISCOVERY_STARTED), ACTION_SUCCESS(emu_setup_powered_remote_action, NULL), CALLBACK_DEVICE_FOUND(prop_emu_remote_any_default_set, 1), ACTION_SUCCESS(bt_cancel_discovery_action, NULL), CALLBACK_STATE(CB_BT_DISCOVERY_STATE_CHANGED, BT_DISCOVERY_STOPPED), ACTION_SUCCESS(bt_set_device_prop_action, &prop_emu_remote_ble_fname_req), ACTION_SUCCESS(bt_get_device_prop_action, &prop_emu_remote_ble_fname_req), CALLBACK_DEVICE_PROPS(&prop_emu_remote_ble_fname_prop, 1), ), TEST_CASE_BREDRLE("Bluetooth Device Set BDNAME - Fail", ACTION_SUCCESS(bluetooth_enable_action, NULL), CALLBACK_STATE(CB_BT_ADAPTER_STATE_CHANGED, BT_STATE_ON), ACTION_SUCCESS(bt_start_discovery_action, NULL), CALLBACK_STATE(CB_BT_DISCOVERY_STATE_CHANGED, BT_DISCOVERY_STARTED), ACTION_SUCCESS(emu_setup_powered_remote_action, NULL), CALLBACK_DEVICE_FOUND(prop_emu_remote_any_default_set, 1), ACTION_SUCCESS(bt_cancel_discovery_action, NULL), CALLBACK_STATE(CB_BT_DISCOVERY_STATE_CHANGED, BT_DISCOVERY_STOPPED), ACTION_FAIL(bt_set_device_prop_action, &prop_test_remote_ble_bdname_req), ), TEST_CASE_BREDRLE("Bluetooth Device Set UUIDS - Fail", ACTION_SUCCESS(bluetooth_enable_action, NULL), CALLBACK_STATE(CB_BT_ADAPTER_STATE_CHANGED, BT_STATE_ON), ACTION_SUCCESS(bt_start_discovery_action, NULL), CALLBACK_STATE(CB_BT_DISCOVERY_STATE_CHANGED, BT_DISCOVERY_STARTED), ACTION_SUCCESS(emu_setup_powered_remote_action, NULL), CALLBACK_DEVICE_FOUND(prop_emu_remote_any_default_set, 1), ACTION_SUCCESS(bt_cancel_discovery_action, NULL), CALLBACK_STATE(CB_BT_DISCOVERY_STATE_CHANGED, BT_DISCOVERY_STOPPED), ACTION_FAIL(bt_set_device_prop_action, &prop_test_remote_ble_uuids_req), ), TEST_CASE_BREDRLE("Bluetooth Device Set COD - Fail", ACTION_SUCCESS(bluetooth_enable_action, NULL), CALLBACK_STATE(CB_BT_ADAPTER_STATE_CHANGED, BT_STATE_ON), ACTION_SUCCESS(bt_start_discovery_action, NULL), CALLBACK_STATE(CB_BT_DISCOVERY_STATE_CHANGED, BT_DISCOVERY_STARTED), ACTION_SUCCESS(emu_setup_powered_remote_action, NULL), CALLBACK_DEVICE_FOUND(prop_emu_remote_any_default_set, 1), ACTION_SUCCESS(bt_cancel_discovery_action, NULL), CALLBACK_STATE(CB_BT_DISCOVERY_STATE_CHANGED, BT_DISCOVERY_STOPPED), ACTION_FAIL(bt_set_device_prop_action, &prop_test_remote_ble_cod_req), ), TEST_CASE_BREDRLE("Bluetooth Device Set TOD - Fail", ACTION_SUCCESS(bluetooth_enable_action, NULL), CALLBACK_STATE(CB_BT_ADAPTER_STATE_CHANGED, BT_STATE_ON), ACTION_SUCCESS(bt_start_discovery_action, NULL), CALLBACK_STATE(CB_BT_DISCOVERY_STATE_CHANGED, BT_DISCOVERY_STARTED), ACTION_SUCCESS(emu_setup_powered_remote_action, NULL), CALLBACK_DEVICE_FOUND(prop_emu_remote_any_default_set, 1), ACTION_SUCCESS(bt_cancel_discovery_action, NULL), CALLBACK_STATE(CB_BT_DISCOVERY_STATE_CHANGED, BT_DISCOVERY_STOPPED), ACTION_FAIL(bt_set_device_prop_action, &prop_test_remote_ble_tod_req), ), TEST_CASE_BREDRLE("Bluetooth Device Set RSSI - Fail", ACTION_SUCCESS(bluetooth_enable_action, NULL), CALLBACK_STATE(CB_BT_ADAPTER_STATE_CHANGED, BT_STATE_ON), ACTION_SUCCESS(bt_start_discovery_action, NULL), CALLBACK_STATE(CB_BT_DISCOVERY_STATE_CHANGED, BT_DISCOVERY_STARTED), ACTION_SUCCESS(emu_setup_powered_remote_action, NULL), CALLBACK_DEVICE_FOUND(prop_emu_remote_any_default_set, 1), ACTION_SUCCESS(bt_cancel_discovery_action, NULL), CALLBACK_STATE(CB_BT_DISCOVERY_STATE_CHANGED, BT_DISCOVERY_STOPPED), ACTION_FAIL(bt_set_device_prop_action, &prop_test_remote_ble_rssi_req), ), TEST_CASE_BREDRLE("Bluetooth Device Set TIMESTAMP - Fail", ACTION_SUCCESS(bluetooth_enable_action, NULL), CALLBACK_STATE(CB_BT_ADAPTER_STATE_CHANGED, BT_STATE_ON), ACTION_SUCCESS(bt_start_discovery_action, NULL), CALLBACK_STATE(CB_BT_DISCOVERY_STATE_CHANGED, BT_DISCOVERY_STARTED), ACTION_SUCCESS(emu_setup_powered_remote_action, NULL), CALLBACK_DEVICE_FOUND(prop_emu_remote_any_default_set, 1), ACTION_SUCCESS(bt_cancel_discovery_action, NULL), CALLBACK_STATE(CB_BT_DISCOVERY_STATE_CHANGED, BT_DISCOVERY_STOPPED), ACTION_FAIL(bt_set_device_prop_action, &prop_test_remote_ble_timestamp_req), ), TEST_CASE_BREDRLE("Bluetooth Device Set BDADDR - Fail", ACTION_SUCCESS(bluetooth_enable_action, NULL), CALLBACK_STATE(CB_BT_ADAPTER_STATE_CHANGED, BT_STATE_ON), ACTION_SUCCESS(bt_start_discovery_action, NULL), CALLBACK_STATE(CB_BT_DISCOVERY_STATE_CHANGED, BT_DISCOVERY_STARTED), ACTION_SUCCESS(emu_setup_powered_remote_action, NULL), CALLBACK_DEVICE_FOUND(prop_emu_remote_any_default_set, 1), ACTION_SUCCESS(bt_cancel_discovery_action, NULL), CALLBACK_STATE(CB_BT_DISCOVERY_STATE_CHANGED, BT_DISCOVERY_STOPPED), ACTION_FAIL(bt_set_device_prop_action, &prop_test_remote_ble_bdaddr_req), ), TEST_CASE_BREDRLE("Bluetooth Device Set SERVICE_RECORD - Fail", ACTION_SUCCESS(bluetooth_enable_action, NULL), CALLBACK_STATE(CB_BT_ADAPTER_STATE_CHANGED, BT_STATE_ON), ACTION_SUCCESS(bt_start_discovery_action, NULL), CALLBACK_STATE(CB_BT_DISCOVERY_STATE_CHANGED, BT_DISCOVERY_STARTED), ACTION_SUCCESS(emu_setup_powered_remote_action, NULL), CALLBACK_DEVICE_FOUND(prop_emu_remote_any_default_set, 1), ACTION_SUCCESS(bt_cancel_discovery_action, NULL), CALLBACK_STATE(CB_BT_DISCOVERY_STATE_CHANGED, BT_DISCOVERY_STOPPED), ACTION_FAIL(bt_set_device_prop_action, &prop_test_remote_ble_srvc_record_req), ), TEST_CASE_BREDRLE("Bluetooth Device Set SCAN_MODE - Fail", ACTION_SUCCESS(bluetooth_enable_action, NULL), CALLBACK_STATE(CB_BT_ADAPTER_STATE_CHANGED, BT_STATE_ON), ACTION_SUCCESS(bt_start_discovery_action, NULL), CALLBACK_STATE(CB_BT_DISCOVERY_STATE_CHANGED, BT_DISCOVERY_STARTED), ACTION_SUCCESS(emu_setup_powered_remote_action, NULL), CALLBACK_DEVICE_FOUND(prop_emu_remote_any_default_set, 1), ACTION_SUCCESS(bt_cancel_discovery_action, NULL), CALLBACK_STATE(CB_BT_DISCOVERY_STATE_CHANGED, BT_DISCOVERY_STOPPED), ACTION_FAIL(bt_set_device_prop_action, &prop_test_remote_ble_scanmode_req), ), TEST_CASE_BREDRLE("Bluetooth Device Set BONDED_DEVICES - Fail", ACTION_SUCCESS(bluetooth_enable_action, NULL), CALLBACK_STATE(CB_BT_ADAPTER_STATE_CHANGED, BT_STATE_ON), ACTION_SUCCESS(bt_start_discovery_action, NULL), CALLBACK_STATE(CB_BT_DISCOVERY_STATE_CHANGED, BT_DISCOVERY_STARTED), ACTION_SUCCESS(emu_setup_powered_remote_action, NULL), CALLBACK_DEVICE_FOUND(prop_emu_remote_any_default_set, 1), ACTION_SUCCESS(bt_cancel_discovery_action, NULL), CALLBACK_STATE(CB_BT_DISCOVERY_STATE_CHANGED, BT_DISCOVERY_STOPPED), ACTION_FAIL(bt_set_device_prop_action, &prop_test_ble_bonded_dev_req), ), TEST_CASE_BREDRLE("Bluetooth Device Set DISCOVERY_TIMEOUT - Fail", ACTION_SUCCESS(bluetooth_enable_action, NULL), CALLBACK_STATE(CB_BT_ADAPTER_STATE_CHANGED, BT_STATE_ON), ACTION_SUCCESS(bt_start_discovery_action, NULL), CALLBACK_STATE(CB_BT_DISCOVERY_STATE_CHANGED, BT_DISCOVERY_STARTED), ACTION_SUCCESS(emu_setup_powered_remote_action, NULL), CALLBACK_DEVICE_FOUND(prop_emu_remote_any_default_set, 1), ACTION_SUCCESS(bt_cancel_discovery_action, NULL), CALLBACK_STATE(CB_BT_DISCOVERY_STATE_CHANGED, BT_DISCOVERY_STOPPED), ACTION_FAIL(bt_set_device_prop_action, &prop_test_remote_ble_disc_timeout_req), ), TEST_CASE_BREDR("Bluetooth Create Bond PIN - Success", ACTION_SUCCESS(bluetooth_enable_action, NULL), CALLBACK_STATE(CB_BT_ADAPTER_STATE_CHANGED, BT_STATE_ON), ACTION_SUCCESS(emu_set_pin_code_action, &emu_pin_set_req), ACTION_SUCCESS(bt_start_discovery_action, NULL), CALLBACK_STATE(CB_BT_DISCOVERY_STATE_CHANGED, BT_DISCOVERY_STARTED), ACTION_SUCCESS(emu_setup_powered_remote_action, NULL), CALLBACK_DEVICE_FOUND(prop_emu_remote_bredr_default_set, 3), ACTION_SUCCESS(bt_cancel_discovery_action, NULL), CALLBACK_STATE(CB_BT_DISCOVERY_STATE_CHANGED, BT_DISCOVERY_STOPPED), ACTION_SUCCESS(bt_create_bond_action, &prop_test_remote_ble_bdaddr_req), CALLBACK_BOND_STATE(BT_BOND_STATE_BONDING, &prop_emu_remote_bdadr, 1), CALLBACK_PROPS(CB_BT_PIN_REQUEST, prop_emu_remotes_pin_req_set, 2), ACTION_SUCCESS(bt_pin_reply_accept_action, &emu_pin_set_req), CALLBACK_BOND_STATE(BT_BOND_STATE_BONDED, &prop_emu_remote_bdadr, 1), ), TEST_CASE_BREDR("Bluetooth Create Bond PIN - Bad PIN", ACTION_SUCCESS(bluetooth_enable_action, NULL), CALLBACK_STATE(CB_BT_ADAPTER_STATE_CHANGED, BT_STATE_ON), ACTION_SUCCESS(emu_set_pin_code_action, &emu_pin_set_req), ACTION_SUCCESS(bt_start_discovery_action, NULL), CALLBACK_STATE(CB_BT_DISCOVERY_STATE_CHANGED, BT_DISCOVERY_STARTED), ACTION_SUCCESS(emu_setup_powered_remote_action, NULL), CALLBACK_DEVICE_FOUND(prop_emu_remote_bredr_default_set, 3), ACTION_SUCCESS(bt_cancel_discovery_action, NULL), CALLBACK_STATE(CB_BT_DISCOVERY_STATE_CHANGED, BT_DISCOVERY_STOPPED), ACTION_SUCCESS(bt_create_bond_action, &prop_test_remote_ble_bdaddr_req), CALLBACK_BOND_STATE(BT_BOND_STATE_BONDING, &prop_emu_remote_bdadr, 1), CALLBACK_PROPS(CB_BT_PIN_REQUEST, prop_emu_remotes_pin_req_set, 2), ACTION_SUCCESS(bt_pin_reply_accept_action, &emu_pin_set_invalid_req), CALLBACK_BOND_STATE_FAILED(BT_BOND_STATE_NONE, &prop_emu_remote_bdadr, 1, BT_STATUS_AUTH_FAILURE), ), TEST_CASE_BREDR("Bluetooth Create Bond SSP -Success", ACTION_SUCCESS(bluetooth_enable_action, NULL), CALLBACK_STATE(CB_BT_ADAPTER_STATE_CHANGED, BT_STATE_ON), ACTION_SUCCESS(emu_set_ssp_mode_action, NULL), ACTION_SUCCESS(emu_set_io_cap, &display_yes_no_io_cap), ACTION_SUCCESS(bt_start_discovery_action, NULL), CALLBACK_STATE(CB_BT_DISCOVERY_STATE_CHANGED, BT_DISCOVERY_STARTED), ACTION_SUCCESS(emu_setup_powered_remote_action, NULL), CALLBACK_DEVICE_FOUND(prop_emu_remote_bredr_default_set, 3), ACTION_SUCCESS(bt_cancel_discovery_action, NULL), CALLBACK_STATE(CB_BT_DISCOVERY_STATE_CHANGED, BT_DISCOVERY_STOPPED), ACTION_SUCCESS(bt_create_bond_action, &prop_test_remote_ble_bdaddr_req), CALLBACK_BOND_STATE(BT_BOND_STATE_BONDING, &prop_emu_remote_bdadr, 1), CALLBACK_SSP_REQ(BT_SSP_VARIANT_PASSKEY_CONFIRMATION, prop_emu_remotes_pin_req_set, 2), ACTION_SUCCESS(bt_ssp_reply_accept_action, &ssp_confirm_accept_reply), CALLBACK_BOND_STATE(BT_BOND_STATE_BONDED, &prop_emu_remote_bdadr, 1), ), TEST_CASE_BREDR("Bluetooth Create Bond SSP - Negative reply", ACTION_SUCCESS(bluetooth_enable_action, NULL), CALLBACK_STATE(CB_BT_ADAPTER_STATE_CHANGED, BT_STATE_ON), ACTION_SUCCESS(emu_set_ssp_mode_action, NULL), ACTION_SUCCESS(emu_set_io_cap, &display_yes_no_io_cap), ACTION_SUCCESS(bt_start_discovery_action, NULL), CALLBACK_STATE(CB_BT_DISCOVERY_STATE_CHANGED, BT_DISCOVERY_STARTED), ACTION_SUCCESS(emu_setup_powered_remote_action, NULL), CALLBACK_DEVICE_FOUND(prop_emu_remote_bredr_default_set, 3), ACTION_SUCCESS(bt_cancel_discovery_action, NULL), CALLBACK_STATE(CB_BT_DISCOVERY_STATE_CHANGED, BT_DISCOVERY_STOPPED), ACTION_SUCCESS(bt_create_bond_action, &prop_test_remote_ble_bdaddr_req), CALLBACK_BOND_STATE(BT_BOND_STATE_BONDING, &prop_emu_remote_bdadr, 1), CALLBACK_SSP_REQ(BT_SSP_VARIANT_PASSKEY_CONFIRMATION, prop_emu_remotes_pin_req_set, 2), ACTION_SUCCESS(bt_ssp_reply_accept_action, &ssp_confirm_reject_reply), CALLBACK_BOND_STATE_FAILED(BT_BOND_STATE_NONE, &prop_emu_remote_bdadr, 1, BT_STATUS_AUTH_FAILURE), ), TEST_CASE_BREDR("Bluetooth Create Bond - No Discovery", ACTION_SUCCESS(bluetooth_enable_action, NULL), CALLBACK_STATE(CB_BT_ADAPTER_STATE_CHANGED, BT_STATE_ON), ACTION_SUCCESS(emu_setup_powered_remote_action, NULL), ACTION_SUCCESS(emu_set_ssp_mode_action, NULL), ACTION_SUCCESS(emu_set_io_cap, &display_yes_no_io_cap), ACTION_SUCCESS(bt_create_bond_action, &prop_test_remote_ble_bdaddr_req), CALLBACK_BOND_STATE(BT_BOND_STATE_BONDING, &prop_emu_remote_bdadr, 1), CALLBACK_SSP_REQ(BT_SSP_VARIANT_PASSKEY_CONFIRMATION, prop_emu_remotes_pin_req_set, 2), ACTION_SUCCESS(bt_ssp_reply_accept_action, &ssp_confirm_accept_reply), CALLBACK_BOND_STATE(BT_BOND_STATE_BONDED, &prop_emu_remote_bdadr, 1), ACTION_SUCCESS(bluetooth_disable_action, NULL), CALLBACK_STATE(CB_BT_ADAPTER_STATE_CHANGED, BT_STATE_OFF), ), TEST_CASE_BREDR("Bluetooth Create Bond - Bad Address", ACTION_SUCCESS(bluetooth_enable_action, NULL), CALLBACK_STATE(CB_BT_ADAPTER_STATE_CHANGED, BT_STATE_ON), ACTION_SUCCESS(emu_setup_powered_remote_action, NULL), ACTION_SUCCESS(emu_set_ssp_mode_action, NULL), ACTION_SUCCESS(bt_create_bond_action, &prop_test_bdaddr_req), CALLBACK_BOND_STATE(BT_BOND_STATE_BONDING, &prop_test_bdaddr, 1), CALLBACK_BOND_STATE_FAILED(BT_BOND_STATE_NONE, &prop_test_bdaddr, 1, BT_STATUS_FAIL), ACTION_SUCCESS(bluetooth_disable_action, NULL), CALLBACK_STATE(CB_BT_ADAPTER_STATE_CHANGED, BT_STATE_OFF), ), TEST_CASE_BREDR("Bluetooth Cancel Bonding - Success", ACTION_SUCCESS(bluetooth_enable_action, NULL), CALLBACK_STATE(CB_BT_ADAPTER_STATE_CHANGED, BT_STATE_ON), ACTION_SUCCESS(emu_set_ssp_mode_action, NULL), ACTION_SUCCESS(emu_set_io_cap, &display_yes_no_io_cap), ACTION_SUCCESS(bt_start_discovery_action, NULL), CALLBACK_STATE(CB_BT_DISCOVERY_STATE_CHANGED, BT_DISCOVERY_STARTED), ACTION_SUCCESS(emu_setup_powered_remote_action, NULL), CALLBACK_DEVICE_FOUND(prop_emu_remote_bredr_default_set, 3), ACTION_SUCCESS(bt_cancel_discovery_action, NULL), CALLBACK_STATE(CB_BT_DISCOVERY_STATE_CHANGED, BT_DISCOVERY_STOPPED), ACTION_SUCCESS(bt_create_bond_action, &prop_test_remote_ble_bdaddr_req), CALLBACK_BOND_STATE(BT_BOND_STATE_BONDING, &prop_emu_remote_bdadr, 1), CALLBACK_SSP_REQ(BT_SSP_VARIANT_PASSKEY_CONFIRMATION, prop_emu_remotes_pin_req_set, 2), ACTION_SUCCESS(bt_cancel_bond_action, &emu_remote_bdaddr_val), CALLBACK_BOND_STATE_FAILED(BT_BOND_STATE_NONE, &prop_emu_remote_bdadr, 1, BT_STATUS_FAIL), ACTION_SUCCESS(bluetooth_disable_action, NULL), CALLBACK_STATE(CB_BT_ADAPTER_STATE_CHANGED, BT_STATE_OFF), ), TEST_CASE_BREDR("Bluetooth Remove Bond - Success", ACTION_SUCCESS(bluetooth_enable_action, NULL), CALLBACK_STATE(CB_BT_ADAPTER_STATE_CHANGED, BT_STATE_ON), ACTION_SUCCESS(emu_set_ssp_mode_action, NULL), ACTION_SUCCESS(emu_set_io_cap, &display_yes_no_io_cap), ACTION_SUCCESS(bt_start_discovery_action, NULL), CALLBACK_STATE(CB_BT_DISCOVERY_STATE_CHANGED, BT_DISCOVERY_STARTED), ACTION_SUCCESS(emu_setup_powered_remote_action, NULL), CALLBACK_DEVICE_FOUND(prop_emu_remote_bredr_default_set, 3), ACTION_SUCCESS(bt_cancel_discovery_action, NULL), CALLBACK_STATE(CB_BT_DISCOVERY_STATE_CHANGED, BT_DISCOVERY_STOPPED), ACTION_SUCCESS(bt_create_bond_action, &prop_test_remote_ble_bdaddr_req), CALLBACK_BOND_STATE(BT_BOND_STATE_BONDING, &prop_emu_remote_bdadr, 1), CALLBACK_SSP_REQ(BT_SSP_VARIANT_PASSKEY_CONFIRMATION, prop_emu_remotes_pin_req_set, 2), ACTION_SUCCESS(bt_ssp_reply_accept_action, &ssp_confirm_accept_reply), CALLBACK_BOND_STATE(BT_BOND_STATE_BONDED, &prop_emu_remote_bdadr, 1), ACTION_SUCCESS(bt_remove_bond_action, &emu_remote_bdaddr_val), CALLBACK_BOND_STATE(BT_BOND_STATE_NONE, &prop_emu_remote_bdadr, 1), ACTION_SUCCESS(bluetooth_disable_action, NULL), CALLBACK_STATE(CB_BT_ADAPTER_STATE_CHANGED, BT_STATE_OFF), ), TEST_CASE_BREDR("Bluetooth Accept Bond - Just Works - Success", ACTION_SUCCESS(bluetooth_enable_action, NULL), CALLBACK_STATE(CB_BT_ADAPTER_STATE_CHANGED, BT_STATE_ON), ACTION_SUCCESS(bt_set_property_action, &prop_test_scanmode_conn_discov), CALLBACK_ADAPTER_PROPS(&prop_test_scanmode_conn_discov, 1), ACTION_SUCCESS(emu_setup_powered_remote_action, NULL), ACTION_SUCCESS(emu_set_ssp_mode_action, NULL), ACTION_SUCCESS(emu_set_io_cap, &no_input_no_output_io_cap), ACTION_SUCCESS(emu_set_connect_cb_action, conn_cb), ACTION_SUCCESS(emu_remote_connect_hci_action, NULL), CALLBACK_BOND_STATE(BT_BOND_STATE_BONDING, &prop_emu_remote_bdadr, 1), CALLBACK_BOND_STATE(BT_BOND_STATE_BONDED, &prop_emu_remote_bdadr, 1), ACTION_SUCCESS(bt_remove_bond_action, &emu_remote_bdaddr_val), CALLBACK_BOND_STATE(BT_BOND_STATE_NONE, &prop_emu_remote_bdadr, 1), ACTION_SUCCESS(bluetooth_disable_action, NULL), CALLBACK_STATE(CB_BT_ADAPTER_STATE_CHANGED, BT_STATE_OFF), ), TEST_CASE_BREDR("Bluetooth Accept Bond - No Bond - Success", ACTION_SUCCESS(bluetooth_enable_action, NULL), CALLBACK_STATE(CB_BT_ADAPTER_STATE_CHANGED, BT_STATE_ON), ACTION_SUCCESS(bt_set_property_action, &prop_test_scanmode_conn_discov), CALLBACK_ADAPTER_PROPS(&prop_test_scanmode_conn_discov, 1), ACTION_SUCCESS(emu_setup_powered_remote_action, NULL), ACTION_SUCCESS(emu_set_ssp_mode_action, NULL), ACTION_SUCCESS(emu_set_io_cap, &no_input_no_output_io_cap), ACTION_SUCCESS(emu_set_connect_cb_action, conn_cb), ACTION_SUCCESS(emu_remote_connect_hci_action, NULL), CALLBACK_BOND_STATE(BT_BOND_STATE_BONDING, &prop_emu_remote_bdadr, 1), CALLBACK_BOND_STATE(BT_BOND_STATE_BONDED, &prop_emu_remote_bdadr, 1), ACTION_SUCCESS(emu_remote_disconnect_hci_action, &test_conn_handle), ACTION_SUCCESS(bluetooth_disable_action, NULL), CALLBACK_BOND_STATE(BT_BOND_STATE_BONDING, &prop_emu_remote_bdadr, 1), CALLBACK_BOND_STATE(BT_BOND_STATE_NONE, &prop_emu_remote_bdadr, 1), CALLBACK_STATE(CB_BT_ADAPTER_STATE_CHANGED, BT_STATE_OFF), ), }; struct queue *get_bluetooth_tests(void) { uint16_t i = 0; list = queue_new(); for (; i < sizeof(test_cases) / sizeof(test_cases[0]); ++i) queue_push_tail(list, &test_cases[i]); return list; } void remove_bluetooth_tests(void) { queue_destroy(list, NULL); } bluez-5.82/android/PaxHeaders/pixit-hfp.txt0000644000000000000000000000005012537515745015755 xustar0020 atime=1743516876 20 ctime=1743591289 bluez-5.82/android/pixit-hfp.txt0000644000000000000000000000276612537515745015451 0ustar00rootrootHFP PIXIT for the PTS tool. PTS version: 6.1 * - different than PTS defaults & - should be set to IUT Bluetooth address # - should be set to the respective phone numbers ^ - should be set according to the reported phone number's type Required PIXIT settings ------------------------------------------------------------------------------- Parameter Name Value ------------------------------------------------------------------------------- TSPX_security_enabled TRUE TSPX_bd_addr_iut 112233445566 (*&) TSPX_hf_class_of_device 200408 TSPX_ag_class_of_device 400204 TSPX_packet_type_sco 00A0 TSPX_phone_number 1234567 (#) TSPX_second_phone_number 7654321 (#) TSPX_phone_number_type 145 (*^) TSPX_second_phone_number_type 145 (*^) TSPX_phone_number_memory 1 TSPX_phone_number_memory_invalid_index 9999 TSPX_scan_all_memory_dial_locations FALSE TSPX_pin_code 0000 TSPX_time_guard 300000 TSPX_use_implicit_send TRUE TSPX_verbose_implicit_send FALSE TSPX_delete_link_key FALSE TSPX_server_channel_tester 01 TSPX_server_channel_iut 00 TSPX_verify_CLIP_information TRUE TSPX_no_fail_verdict FALSE TSPX_network_supports_correct_call_and_callstatus TRUE TSPX_secure_simple_pairing_pass_key_confirmation FALSE TSPX_AG_match_tester_BRSF_codec_negotiation_bit FALSE TSPX_HFP_Unsupported_HF_Indicator_1 99 TSPX_HFP_Supported_HF_Indiccator_1 1 TSPX_Automation FALSE ------------------------------------------------------------------------------- bluez-5.82/android/PaxHeaders/pics-sdp.txt0000644000000000000000000000005012537515745015567 xustar0020 atime=1743516875 20 ctime=1743591288 bluez-5.82/android/pics-sdp.txt0000644000000000000000000001373012537515745015254 0ustar00rootrootSDP PICS for the PTS tool. PTS version: 6.1 * - different than PTS defaults # - not yet implemented/supported M - mandatory O - optional Support Different Size Capabilities on UUID ------------------------------------------------------------------------------- Item Selected Description ------------------------------------------------------------------------------- TSPC_SDP_1_1 True Support for 128 bit UUID (M) TSPC_SDP_1_2 True Support for 32 bit UUID (M) TSPC_SDP_1_3 True Support for 16 bit UUID (M) ------------------------------------------------------------------------------- Roles ------------------------------------------------------------------------------- Item Selected Description ------------------------------------------------------------------------------- TSPC_SDP_1b_1 True (*) Support for server role (C.1) TSPC_SDP_1b_2 True (*) Support for client role (C.1) ------------------------------------------------------------------------------- C.1 Mandatory to support at least one of the roles ------------------------------------------------------------------------------- Valid Service Search Request ------------------------------------------------------------------------------- Item Selected Description ------------------------------------------------------------------------------- TSPC_SDP_2_1 True (*) Support for respond on search of single Service, using ServiceSearchRequest (C.2) TSPC_SDP_2_2 True (*) Support for respond on search of Service, using continuation state (O) TSPC_SDP_2_3 True (*) Search for services using the continuation state (C.1) ------------------------------------------------------------------------------- C.1 Mandatory to support if the client role is supported TSPC_SDP_1b_2 C.2 Mandatory to support if the server role is supported TSPC_SDP_1b_1 ------------------------------------------------------------------------------- Invalid Service Search Request ------------------------------------------------------------------------------- Item Selected Description ------------------------------------------------------------------------------- TSPC_SDP_3_1 True Support for error response on Service search request (M) ------------------------------------------------------------------------------- Valid Service Attribute Request ------------------------------------------------------------------------------- Item Selected Description ------------------------------------------------------------------------------- TSPC_SDP_4_1 True Support for respond on search of Attribute(s) (M) TSPC_SDP_4_2 True (*) Support for respond on search of Attribute, using continuation state (O) TSPC_SDP_4_3 True (*) Support for respond on search on attribute AdditionalProtocolDescriptorList (O) ------------------------------------------------------------------------------- Invalid Service Attribute Request ------------------------------------------------------------------------------- Item Selected Description ------------------------------------------------------------------------------- TSPC_SDP_5_1 True Support for error response on Attribute search request (M) ------------------------------------------------------------------------------- Valid Service Search Attribute Request ------------------------------------------------------------------------------- Item Selected Description ------------------------------------------------------------------------------- TSPC_SDP_6_1 True Support for respond on search for Service(s) and Attribute(s) (M) TSPC_SDP_6_2 True (*) Support for respond on search of Attribute, using continuation state (O) TSPC_SDP_6_3 True (*) Support for respond on search on attribute AdditionalProtocolDescriptorList on existing service (O) ------------------------------------------------------------------------------- Invalid Service Search Attribute Request ------------------------------------------------------------------------------- Item Selected Description ------------------------------------------------------------------------------- TSPC_SDP_7_1 True Support for error response on Service and Attribute request (M) ------------------------------------------------------------------------------- Service Browsing ------------------------------------------------------------------------------- Item Selected Description ------------------------------------------------------------------------------- TSPC_SDP_8_1 True (*) Support for browsing, using SDP_ServiceSearchRequest and SDP_ServiceAttributeRequest (O) TSPC_SDP_8_2 True (*) Support for browsing, using SDP_ServiceSearchAttributeRequest (O) ------------------------------------------------------------------------------- Attributes Present in IUT ------------------------------------------------------------------------------- Item Selected Description ------------------------------------------------------------------------------- TSPC_SDP_9_1 True (*) ServiceID (O) TSPC_SDP_9_2 True (*) ProtocolDescriptorList (O) TSPC_SDP_9_3 True (*) ServiceRecordState (O) TSPC_SDP_9_4 True (*) ServiceInfoTimeToLive (O) TSPC_SDP_9_5 True (*) BrowseGroupList (O) TSPC_SDP_9_6 True (*) LanguageBaseAttributeIdList (O) TSPC_SDP_9_7 True (*) ServiceAvailability (O) TSPC_SDP_9_8 True (*) IconURL (O) TSPC_SDP_9_9 True (*) ServiceName (O) TSPC_SDP_9_10 True (*) ServiceDescription (O) TSPC_SDP_9_11 True (*) ProviderName (O) TSPC_SDP_9_12 True (*) VersionNumberList (O) TSPC_SDP_9_13 True (*) ServiceDataBaseState (O) TSPC_SDP_9_14 True (*) BluetoothProfileDescriptorList (O) TSPC_SDP_9_15 True (*) DocumentationURL (O) TSPC_SDP_9_16 True (*) ClientExecutableURL (O) TSPC_SDP_9_17 True (*) AdditionalProtocolDescriptorList (C.1) TSPC_SDP_9_18 True ServiceRecordHandle (M) TSPC_SDP_9_19 True ServiceClassIDList (M) ------------------------------------------------------------------------------- C.1: Optional if TSPC_SDP_9_2 is supported, otherwise excluded ------------------------------------------------------------------------------- bluez-5.82/android/PaxHeaders/gatt.c0000644000000000000000000000005014447506753014410 xustar0020 atime=1743516867 20 ctime=1743591279 bluez-5.82/android/gatt.c0000644000000000000000000054014214447506753014077 0ustar00rootroot// SPDX-License-Identifier: LGPL-2.1-or-later /* * * BlueZ - Bluetooth protocol stack for Linux * * Copyright (C) 2014 Intel Corporation. All rights reserved. * * */ #ifdef HAVE_CONFIG_H #include #endif #include #include #include #include #include #include #include "ipc.h" #include "ipc-common.h" #include "lib/sdp.h" #include "lib/sdp_lib.h" #include "lib/uuid.h" #include "bluetooth.h" #include "gatt.h" #include "src/log.h" #include "hal-msg.h" #include "utils.h" #include "src/shared/util.h" #include "src/shared/queue.h" #include "src/shared/att.h" #include "src/shared/gatt-db.h" #include "src/shared/ad.h" #include "attrib/gattrib.h" #include "attrib/att.h" #include "attrib/gatt.h" #include "btio/btio.h" /* set according to Android bt_gatt_client.h */ #define GATT_MAX_ATTR_LEN 600 #define GATT_SUCCESS 0x00000000 #define GATT_FAILURE 0x00000101 #define BASE_UUID16_OFFSET 12 #define GATT_PERM_READ 0x00000001 #define GATT_PERM_READ_ENCRYPTED 0x00000002 #define GATT_PERM_READ_MITM 0x00000004 #define GATT_PERM_READ_AUTHORIZATION 0x00000008 #define GATT_PERM_WRITE 0x00000100 #define GATT_PERM_WRITE_ENCRYPTED 0x00000200 #define GATT_PERM_WRITE_MITM 0x00000400 #define GATT_PERM_WRITE_AUTHORIZATION 0x00000800 #define GATT_PERM_WRITE_SIGNED 0x00010000 #define GATT_PERM_WRITE_SIGNED_MITM 0x00020000 #define GATT_PERM_NONE 0x10000000 #define GATT_PAIR_CONN_TIMEOUT 30 #define GATT_CONN_TIMEOUT 2 static const uint8_t BLUETOOTH_UUID[] = { 0xfb, 0x34, 0x9b, 0x5f, 0x80, 0x00, 0x00, 0x80, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; typedef enum { DEVICE_DISCONNECTED = 0, DEVICE_CONNECT_INIT, /* connection procedure initiated */ DEVICE_CONNECT_READY, /* dev found during LE scan */ DEVICE_CONNECTED, /* connection has been established */ } gatt_device_state_t; static const char *device_state_str[] = { "DISCONNECTED", "CONNECT INIT", "CONNECT READY", "CONNECTED", }; struct pending_trans_data { unsigned int id; uint8_t opcode; struct gatt_db_attribute *attrib; unsigned int serial_id; }; struct gatt_app { int32_t id; uint8_t uuid[16]; gatt_type_t type; /* Valid for client applications */ struct queue *notifications; gatt_conn_cb_t func; struct adv_instance *adv; }; struct element_id { bt_uuid_t uuid; uint8_t instance; }; struct descriptor { struct element_id id; uint16_t handle; }; struct characteristic { struct element_id id; struct gatt_char ch; uint16_t end_handle; struct queue *descriptors; }; struct service { struct element_id id; struct gatt_primary prim; struct gatt_included incl; bool primary; struct queue *chars; struct queue *included; /* Valid only for primary services */ bool incl_search_done; }; struct notification_data { struct hal_gatt_srvc_id service; struct hal_gatt_gatt_id ch; struct app_connection *conn; guint notif_id; guint ind_id; int ref; }; struct gatt_device { bdaddr_t bdaddr; gatt_device_state_t state; GAttrib *attrib; GIOChannel *att_io; struct queue *services; bool partial_srvc_search; guint watch_id; guint server_id; guint ind_id; int ref; struct queue *autoconnect_apps; struct queue *pending_requests; }; struct app_connection { struct gatt_device *device; struct gatt_app *app; struct queue *transactions; int32_t id; guint timeout_id; bool wait_execute_write; }; struct service_sdp { int32_t service_handle; uint32_t sdp_handle; }; static struct ipc *hal_ipc = NULL; static bdaddr_t adapter_addr; static bool scanning = false; static unsigned int advertising_cnt = 0; static uint32_t adv_inst_bits = 0; static struct queue *gatt_apps = NULL; static struct queue *gatt_devices = NULL; static struct queue *app_connections = NULL; static struct queue *services_sdp = NULL; static struct queue *listen_apps = NULL; static struct gatt_db *gatt_db = NULL; static struct gatt_db_attribute *service_changed_attrib = NULL; static GIOChannel *le_io = NULL; static GIOChannel *bredr_io = NULL; static uint32_t gatt_sdp_handle = 0; static uint32_t gap_sdp_handle = 0; static uint32_t dis_sdp_handle = 0; static struct bt_crypto *crypto = NULL; static int test_client_if = 0; static const uint8_t TEST_UUID[] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x02, 0x03, 0x04 }; static bool is_bluetooth_uuid(const uint8_t *uuid) { int i; for (i = 0; i < 16; i++) { /* ignore minimal uuid (16) value */ if (i == 12 || i == 13) continue; if (uuid[i] != BLUETOOTH_UUID[i]) return false; } return true; } static void android2uuid(const uint8_t *uuid, bt_uuid_t *dst) { if (is_bluetooth_uuid(uuid)) { /* copy 16 bit uuid value from full android 128bit uuid */ dst->type = BT_UUID16; dst->value.u16 = (uuid[13] << 8) + uuid[12]; } else { int i; dst->type = BT_UUID128; for (i = 0; i < 16; i++) dst->value.u128.data[i] = uuid[15 - i]; } } static void uuid2android(const bt_uuid_t *src, uint8_t *uuid) { bt_uuid_t uu128; uint8_t i; if (src->type != BT_UUID128) { bt_uuid_to_uuid128(src, &uu128); src = &uu128; } for (i = 0; i < 16; i++) uuid[15 - i] = src->value.u128.data[i]; } static void hal_srvc_id_to_element_id(const struct hal_gatt_srvc_id *from, struct element_id *to) { to->instance = from->inst_id; android2uuid(from->uuid, &to->uuid); } static void element_id_to_hal_srvc_id(const struct element_id *from, uint8_t primary, struct hal_gatt_srvc_id *to) { to->is_primary = primary; to->inst_id = from->instance; uuid2android(&from->uuid, to->uuid); } static void hal_gatt_id_to_element_id(const struct hal_gatt_gatt_id *from, struct element_id *to) { to->instance = from->inst_id; android2uuid(from->uuid, &to->uuid); } static void element_id_to_hal_gatt_id(const struct element_id *from, struct hal_gatt_gatt_id *to) { to->inst_id = from->instance; uuid2android(&from->uuid, to->uuid); } static void destroy_characteristic(void *data) { struct characteristic *chars = data; if (!chars) return; queue_destroy(chars->descriptors, free); free(chars); } static void destroy_service(void *data) { struct service *srvc = data; if (!srvc) return; queue_destroy(srvc->chars, destroy_characteristic); /* * Included services we keep on two queues. * 1. On the same queue with primary services. * 2. On the queue inside primary service. * So we need to free service memory only once but we need to destroy * two queues */ queue_destroy(srvc->included, NULL); free(srvc); } static bool match_app_by_uuid(const void *data, const void *user_data) { const uint8_t *exp_uuid = user_data; const struct gatt_app *client = data; return !memcmp(exp_uuid, client->uuid, sizeof(client->uuid)); } static bool match_app_by_id(const void *data, const void *user_data) { int32_t exp_id = PTR_TO_INT(user_data); const struct gatt_app *client = data; return client->id == exp_id; } static struct gatt_app *find_app_by_id(int32_t id) { return queue_find(gatt_apps, match_app_by_id, INT_TO_PTR(id)); } static bool match_device_by_bdaddr(const void *data, const void *user_data) { const struct gatt_device *dev = data; const bdaddr_t *addr = user_data; return !bacmp(&dev->bdaddr, addr); } static bool match_device_by_state(const void *data, const void *user_data) { const struct gatt_device *dev = data; if (dev->state != PTR_TO_UINT(user_data)) return false; return true; } static bool match_pending_device(const void *data, const void *user_data) { const struct gatt_device *dev = data; if ((dev->state == DEVICE_CONNECT_INIT) || (dev->state == DEVICE_CONNECT_READY)) return true; return false; } static bool match_connection_by_id(const void *data, const void *user_data) { const struct app_connection *conn = data; const int32_t id = PTR_TO_INT(user_data); return conn->id == id; } static bool match_connection_by_device_and_app(const void *data, const void *user_data) { const struct app_connection *conn = data; const struct app_connection *match = user_data; return conn->device == match->device && conn->app == match->app; } static struct app_connection *find_connection_by_id(int32_t conn_id) { struct app_connection *conn; conn = queue_find(app_connections, match_connection_by_id, INT_TO_PTR(conn_id)); if (conn && conn->device->state == DEVICE_CONNECTED) return conn; return NULL; } static bool match_connection_by_device(const void *data, const void *user_data) { const struct app_connection *conn = data; const struct gatt_device *dev = user_data; return conn->device == dev; } static bool match_connection_by_app(const void *data, const void *user_data) { const struct app_connection *conn = data; const struct gatt_app *app = user_data; return conn->app == app; } static struct gatt_device *find_device_by_addr(const bdaddr_t *addr) { return queue_find(gatt_devices, match_device_by_bdaddr, addr); } static struct gatt_device *find_pending_device(void) { return queue_find(gatt_devices, match_pending_device, NULL); } static struct gatt_device *find_device_by_state(uint32_t state) { return queue_find(gatt_devices, match_device_by_state, UINT_TO_PTR(state)); } static bool match_srvc_by_element_id(const void *data, const void *user_data) { const struct element_id *exp_id = user_data; const struct service *service = data; if (service->id.instance == exp_id->instance) return !bt_uuid_cmp(&service->id.uuid, &exp_id->uuid); return false; } static bool match_srvc_by_higher_inst_id(const void *data, const void *user_data) { const struct service *s = data; uint8_t inst_id = PTR_TO_INT(user_data); /* For now we match inst_id as it is unique */ return inst_id < s->id.instance; } static bool match_srvc_by_bt_uuid(const void *data, const void *user_data) { const bt_uuid_t *exp_uuid = user_data; const struct service *service = data; return !bt_uuid_cmp(exp_uuid, &service->id.uuid); } static bool match_srvc_by_range(const void *data, const void *user_data) { const struct service *srvc = data; const struct att_range *range = user_data; return !memcmp(&srvc->prim.range, range, sizeof(srvc->prim.range)); } static bool match_char_by_higher_inst_id(const void *data, const void *user_data) { const struct characteristic *ch = data; uint8_t inst_id = PTR_TO_INT(user_data); /* For now we match inst_id as it is unique, we'll match uuids later */ return inst_id < ch->id.instance; } static bool match_descr_by_element_id(const void *data, const void *user_data) { const struct element_id *exp_id = user_data; const struct descriptor *descr = data; if (exp_id->instance == descr->id.instance) return !bt_uuid_cmp(&descr->id.uuid, &exp_id->uuid); return false; } static bool match_descr_by_higher_inst_id(const void *data, const void *user_data) { const struct descriptor *descr = data; uint8_t instance = PTR_TO_INT(user_data); /* For now we match instance as it is unique */ return instance < descr->id.instance; } static bool match_notification(const void *a, const void *b) { const struct notification_data *a1 = a; const struct notification_data *b1 = b; if (a1->conn != b1->conn) return false; if (memcmp(&a1->ch, &b1->ch, sizeof(a1->ch))) return false; if (memcmp(&a1->service, &b1->service, sizeof(a1->service))) return false; return true; } static bool match_char_by_element_id(const void *data, const void *user_data) { const struct element_id *exp_id = user_data; const struct characteristic *chars = data; if (exp_id->instance == chars->id.instance) return !bt_uuid_cmp(&chars->id.uuid, &exp_id->uuid); return false; } static void destroy_notification(void *data) { struct notification_data *notification = data; struct gatt_app *app; if (!notification) return; if (--notification->ref) return; app = notification->conn->app; queue_remove_if(app->notifications, match_notification, notification); free(notification); } static void unregister_notification(void *data) { struct notification_data *notification = data; struct gatt_device *dev = notification->conn->device; /* * No device means it was already disconnected and client cleanup was * triggered afterwards, but once client unregisters, device stays if * used by others. Then just unregister single handle. */ if (!queue_find(gatt_devices, NULL, dev)) return; if (notification->notif_id && dev) g_attrib_unregister(dev->attrib, notification->notif_id); if (notification->ind_id && dev) g_attrib_unregister(dev->attrib, notification->ind_id); } static void device_set_state(struct gatt_device *dev, uint32_t state) { char bda[18]; if (dev->state == state) return; ba2str(&dev->bdaddr, bda); DBG("gatt: Device %s state changed %s -> %s", bda, device_state_str[dev->state], device_state_str[state]); dev->state = state; } static bool auto_connect_le(struct gatt_device *dev) { /* For LE devices use auto connect feature if possible */ if (bt_kernel_conn_control()) { if (!bt_auto_connect_add(bt_get_id_addr(&dev->bdaddr, NULL))) return false; } else { /* Trigger discovery if not already started */ if (!scanning && !bt_le_discovery_start()) { error("gatt: Could not start scan"); return false; } } device_set_state(dev, DEVICE_CONNECT_INIT); return true; } static void connection_cleanup(struct gatt_device *device) { if (device->watch_id) { g_source_remove(device->watch_id); device->watch_id = 0; } if (device->att_io) { g_io_channel_shutdown(device->att_io, FALSE, NULL); g_io_channel_unref(device->att_io); device->att_io = NULL; } if (device->attrib) { GAttrib *attrib = device->attrib; if (device->server_id > 0) g_attrib_unregister(device->attrib, device->server_id); if (device->ind_id > 0) g_attrib_unregister(device->attrib, device->ind_id); device->attrib = NULL; g_attrib_cancel_all(attrib); g_attrib_unref(attrib); } /* * If device was in connection_pending or connectable state we * search device list if we should stop the scan. */ if (!scanning && (device->state == DEVICE_CONNECT_INIT || device->state == DEVICE_CONNECT_READY)) { if (!find_pending_device()) bt_le_discovery_stop(NULL); } /* If device is not bonded service cache should be refreshed */ if (!bt_device_is_bonded(&device->bdaddr)) queue_remove_all(device->services, NULL, NULL, destroy_service); device_set_state(device, DEVICE_DISCONNECTED); if (!queue_isempty(device->autoconnect_apps)) auto_connect_le(device); else bt_auto_connect_remove(&device->bdaddr); } static void free_adv_instance(struct adv_instance *adv) { if (!adv) return; if (adv->instance) adv_inst_bits &= ~(1 << (adv->instance - 1)); bt_ad_unref(adv->ad); bt_ad_unref(adv->sr); free(adv); } static void destroy_gatt_app(void *data) { struct gatt_app *app = data; if (!app) return; /* * First we want to get all notifications and unregister them. * We don't pass unregister_notification to queue_destroy, * because destroy notification performs operations on queue * too. So remove all elements and then destroy queue. */ if (app->type == GATT_CLIENT) while (queue_peek_head(app->notifications)) { struct notification_data *notification; notification = queue_pop_head(app->notifications); unregister_notification(notification); } queue_destroy(app->notifications, free); free_adv_instance(app->adv); free(app); } struct pending_request { struct gatt_db_attribute *attrib; int length; uint8_t *value; uint16_t offset; uint8_t *filter_value; uint16_t filter_vlen; bool completed; uint8_t error; }; static void destroy_pending_request(void *data) { struct pending_request *entry = data; if (!entry) return; free(entry->value); free(entry->filter_value); free(entry); } static void destroy_device(void *data) { struct gatt_device *dev = data; if (!dev) return; queue_destroy(dev->services, destroy_service); queue_destroy(dev->pending_requests, destroy_pending_request); queue_destroy(dev->autoconnect_apps, NULL); bt_auto_connect_remove(&dev->bdaddr); free(dev); } static struct gatt_device *device_ref(struct gatt_device *device) { if (!device) return NULL; device->ref++; return device; } static void device_unref(struct gatt_device *device) { if (!device) return; if (--device->ref) return; destroy_device(device); } static struct gatt_device *create_device(const bdaddr_t *addr) { struct gatt_device *dev; dev = new0(struct gatt_device, 1); bacpy(&dev->bdaddr, addr); dev->services = queue_new(); dev->autoconnect_apps = queue_new(); dev->pending_requests = queue_new(); queue_push_head(gatt_devices, dev); return device_ref(dev); } static void send_client_connect_status_notify(struct app_connection *conn, int32_t status) { struct hal_ev_gatt_client_connect ev; if (conn->app->func) { conn->app->func(&conn->device->bdaddr, status == GATT_SUCCESS ? 0 : -ENOTCONN, conn->device->attrib); return; } ev.client_if = conn->app->id; ev.conn_id = conn->id; ev.status = status; bdaddr2android(&conn->device->bdaddr, &ev.bda); ipc_send_notif(hal_ipc, HAL_SERVICE_ID_GATT, HAL_EV_GATT_CLIENT_CONNECT, sizeof(ev), &ev); } static void send_server_connection_state_notify(struct app_connection *conn, bool connected) { struct hal_ev_gatt_server_connection ev; if (conn->app->func) { conn->app->func(&conn->device->bdaddr, connected ? 0 : -ENOTCONN, conn->device->attrib); return; } ev.server_if = conn->app->id; ev.conn_id = conn->id; ev.connected = connected; bdaddr2android(&conn->device->bdaddr, &ev.bdaddr); ipc_send_notif(hal_ipc, HAL_SERVICE_ID_GATT, HAL_EV_GATT_SERVER_CONNECTION, sizeof(ev), &ev); } static void send_client_disconnect_status_notify(struct app_connection *conn, int32_t status) { struct hal_ev_gatt_client_disconnect ev; if (conn->app->func) { conn->app->func(&conn->device->bdaddr, -ENOTCONN, conn->device->attrib); return; } ev.client_if = conn->app->id; ev.conn_id = conn->id; ev.status = status; bdaddr2android(&conn->device->bdaddr, &ev.bda); ipc_send_notif(hal_ipc, HAL_SERVICE_ID_GATT, HAL_EV_GATT_CLIENT_DISCONNECT, sizeof(ev), &ev); } static void notify_app_disconnect_status(struct app_connection *conn, int32_t status) { if (!conn->app) return; if (conn->app->type == GATT_CLIENT) send_client_disconnect_status_notify(conn, status); else send_server_connection_state_notify(conn, !!status); } static void notify_app_connect_status(struct app_connection *conn, int32_t status) { if (!conn->app) return; if (conn->app->type == GATT_CLIENT) send_client_connect_status_notify(conn, status); else send_server_connection_state_notify(conn, !status); } static void destroy_connection(void *data) { struct app_connection *conn = data; if (!conn) return; if (conn->timeout_id > 0) g_source_remove(conn->timeout_id); switch (conn->device->state) { case DEVICE_CONNECTED: notify_app_disconnect_status(conn, GATT_SUCCESS); break; case DEVICE_CONNECT_INIT: case DEVICE_CONNECT_READY: notify_app_connect_status(conn, GATT_FAILURE); break; case DEVICE_DISCONNECTED: break; } if (!queue_find(app_connections, match_connection_by_device, conn->device)) connection_cleanup(conn->device); queue_destroy(conn->transactions, free); device_unref(conn->device); free(conn); } static gboolean disconnected_cb(GIOChannel *io, GIOCondition cond, gpointer user_data) { struct gatt_device *dev = user_data; int sock, err = 0; socklen_t len; sock = g_io_channel_unix_get_fd(io); len = sizeof(err); if (!getsockopt(sock, SOL_SOCKET, SO_ERROR, &err, &len)) DBG("%s (%d)", strerror(err), err); queue_remove_all(app_connections, match_connection_by_device, dev, destroy_connection); return FALSE; } static bool get_local_mtu(struct gatt_device *dev, uint16_t *mtu) { GIOChannel *io; uint16_t imtu, omtu; io = g_attrib_get_channel(dev->attrib); if (!bt_io_get(io, NULL, BT_IO_OPT_IMTU, &imtu, BT_IO_OPT_OMTU, &omtu, BT_IO_OPT_INVALID)) { error("gatt: Failed to get local MTU"); return false; } /* * Limit MTU to MIN(IMTU, OMTU). This is to avoid situation where * local OMTU < MIN(remote MTU, IMTU) */ if (mtu) *mtu = MIN(imtu, omtu); return true; } static void notify_client_mtu_change(struct app_connection *conn, bool success) { struct hal_ev_gatt_client_configure_mtu ev; size_t mtu; g_attrib_get_buffer(conn->device->attrib, &mtu); ev.conn_id = conn->id; ev.status = success ? GATT_SUCCESS : GATT_FAILURE; ev.mtu = mtu; ipc_send_notif(hal_ipc, HAL_SERVICE_ID_GATT, HAL_EV_GATT_CLIENT_CONFIGURE_MTU, sizeof(ev), &ev); } static void notify_server_mtu(struct app_connection *conn) { struct hal_ev_gatt_server_mtu_changed ev; size_t mtu; g_attrib_get_buffer(conn->device->attrib, &mtu); ev.conn_id = conn->id; ev.mtu = mtu; ipc_send_notif(hal_ipc, HAL_SERVICE_ID_GATT, HAL_EV_GATT_SERVER_MTU_CHANGED, sizeof(ev), &ev); } static void notify_mtu_change(void *data, void *user_data) { struct gatt_device *device = user_data; struct app_connection *conn = data; if (conn->device != device) return; if (!conn->app) { error("gatt: can't notify mtu - no app registered for conn"); return; } switch (conn->app->type) { case GATT_CLIENT: notify_client_mtu_change(conn, true); break; case GATT_SERVER: notify_server_mtu(conn); break; default: break; } } static bool update_mtu(struct gatt_device *device, uint16_t rmtu) { uint16_t mtu, lmtu; if (!get_local_mtu(device, &lmtu)) return false; DBG("remote_mtu:%d local_mtu:%d", rmtu, lmtu); if (rmtu < ATT_DEFAULT_LE_MTU) { error("gatt: remote MTU invalid (%u bytes)", rmtu); return false; } mtu = MIN(lmtu, rmtu); if (mtu == ATT_DEFAULT_LE_MTU) return true; if (!g_attrib_set_mtu(device->attrib, mtu)) { error("gatt: Failed to set MTU"); return false; } queue_foreach(app_connections, notify_mtu_change, device); return true; } static void att_handler(const uint8_t *ipdu, uint16_t len, gpointer user_data); static void exchange_mtu_cb(guint8 status, const guint8 *pdu, guint16 plen, gpointer user_data) { struct gatt_device *device = user_data; uint16_t rmtu; DBG(""); if (status) { error("gatt: MTU exchange: %s", att_ecode2str(status)); goto failed; } if (!dec_mtu_resp(pdu, plen, &rmtu)) { error("gatt: MTU exchange: protocol error"); goto failed; } update_mtu(device, rmtu); failed: device_unref(device); } static void send_exchange_mtu_request(struct gatt_device *device) { uint16_t mtu; if (!get_local_mtu(device, &mtu)) return; DBG("mtu %u", mtu); if (!gatt_exchange_mtu(device->attrib, mtu, exchange_mtu_cb, device_ref(device))) device_unref(device); } static void ignore_confirmation_cb(guint8 status, const guint8 *pdu, guint16 len, gpointer user_data) { /* Ignored. */ } static void notify_att_range_change(struct gatt_device *dev, struct att_range *range) { uint16_t handle; uint16_t length = 0; uint16_t ccc; uint8_t *pdu; size_t mtu; GAttribResultFunc confirmation_cb = NULL; handle = gatt_db_attribute_get_handle(service_changed_attrib); if (!handle) return; ccc = bt_get_gatt_ccc(&dev->bdaddr); if (!ccc) return; pdu = g_attrib_get_buffer(dev->attrib, &mtu); switch (ccc) { case 0x0001: length = enc_notification(handle, (uint8_t *) range, sizeof(*range), pdu, mtu); break; case 0x0002: length = enc_indication(handle, (uint8_t *) range, sizeof(*range), pdu, mtu); confirmation_cb = ignore_confirmation_cb; break; default: /* 0xfff4 reserved for future use */ break; } g_attrib_send(dev->attrib, 0, pdu, length, confirmation_cb, NULL, NULL); } static struct app_connection *create_connection(struct gatt_device *device, struct gatt_app *app) { struct app_connection *new_conn; static int32_t last_conn_id = 1; /* Check if already connected */ new_conn = new0(struct app_connection, 1); /* Make connection id unique to connection record (app, device) pair */ new_conn->app = app; new_conn->id = last_conn_id++; new_conn->transactions = queue_new(); queue_push_head(app_connections, new_conn); new_conn->device = device_ref(device); return new_conn; } static struct service *create_service(uint8_t id, bool primary, char *uuid, void *data) { struct service *s; s = new0(struct service, 1); if (bt_string_to_uuid(&s->id.uuid, uuid) < 0) { error("gatt: Cannot convert string to uuid"); free(s); return NULL; } s->chars = queue_new(); s->included = queue_new(); s->id.instance = id; /* Put primary service to our local list */ s->primary = primary; if (s->primary) memcpy(&s->prim, data, sizeof(s->prim)); else memcpy(&s->incl, data, sizeof(s->incl)); return s; } static void send_client_primary_notify(void *data, void *user_data) { struct hal_ev_gatt_client_search_result ev; struct service *p = data; int32_t conn_id = PTR_TO_INT(user_data); /* In service queue we will have also included services */ if (!p->primary) return; ev.conn_id = conn_id; element_id_to_hal_srvc_id(&p->id, 1, &ev.srvc_id); uuid2android(&p->id.uuid, ev.srvc_id.uuid); ipc_send_notif(hal_ipc, HAL_SERVICE_ID_GATT, HAL_EV_GATT_CLIENT_SEARCH_RESULT, sizeof(ev), &ev); } static void send_client_search_complete_notify(int32_t status, int32_t conn_id) { struct hal_ev_gatt_client_search_complete ev; ev.status = status; ev.conn_id = conn_id; ipc_send_notif(hal_ipc, HAL_SERVICE_ID_GATT, HAL_EV_GATT_CLIENT_SEARCH_COMPLETE, sizeof(ev), &ev); } struct discover_srvc_data { bt_uuid_t uuid; struct app_connection *conn; }; static void discover_srvc_by_uuid_cb(uint8_t status, GSList *ranges, void *user_data) { struct discover_srvc_data *cb_data = user_data; struct gatt_primary prim; struct service *s; int32_t gatt_status; struct gatt_device *dev = cb_data->conn->device; uint8_t instance_id = queue_length(dev->services); DBG("Status %d", status); if (status) { error("gatt: Discover pri srvc filtered by uuid failed: %s", att_ecode2str(status)); gatt_status = GATT_FAILURE; goto reply; } if (!ranges) { info("gatt: No primary services searched by uuid found"); gatt_status = GATT_SUCCESS; goto reply; } bt_uuid_to_string(&cb_data->uuid, prim.uuid, sizeof(prim.uuid)); for (; ranges; ranges = ranges->next) { memcpy(&prim.range, ranges->data, sizeof(prim.range)); s = create_service(instance_id++, true, prim.uuid, &prim); if (!s) { gatt_status = GATT_FAILURE; goto reply; } queue_push_tail(dev->services, s); send_client_primary_notify(s, INT_TO_PTR(cb_data->conn->id)); DBG("attr handle = 0x%04x, end grp handle = 0x%04x uuid: %s", prim.range.start, prim.range.end, prim.uuid); } /* Partial search service scanning was performed */ dev->partial_srvc_search = true; gatt_status = GATT_SUCCESS; reply: send_client_search_complete_notify(gatt_status, cb_data->conn->id); free(cb_data); } static void discover_srvc_all_cb(uint8_t status, GSList *services, void *user_data) { struct discover_srvc_data *cb_data = user_data; struct gatt_device *dev = cb_data->conn->device; int32_t gatt_status; GSList *l; /* * There might be multiply services with same uuid. Therefore make sure * each primary service one has unique instance_id */ uint8_t instance_id = queue_length(dev->services); DBG("Status %d", status); if (status) { error("gatt: Discover all primary services failed: %s", att_ecode2str(status)); gatt_status = GATT_FAILURE; goto reply; } if (!services) { info("gatt: No primary services found"); gatt_status = GATT_SUCCESS; goto reply; } for (l = services; l; l = l->next) { struct gatt_primary *prim = l->data; struct service *p; if (queue_find(dev->services, match_srvc_by_range, &prim->range)) continue; p = create_service(instance_id++, true, prim->uuid, prim); if (!p) continue; queue_push_tail(dev->services, p); DBG("attr handle = 0x%04x, end grp handle = 0x%04x uuid: %s", prim->range.start, prim->range.end, prim->uuid); } /* * Send all found services notifications - first cache, * then send notifies */ queue_foreach(dev->services, send_client_primary_notify, INT_TO_PTR(cb_data->conn->id)); /* Full search service scanning was performed */ dev->partial_srvc_search = false; gatt_status = GATT_SUCCESS; reply: send_client_search_complete_notify(gatt_status, cb_data->conn->id); free(cb_data); } static gboolean connection_timeout(void *user_data) { struct app_connection *conn = user_data; conn->timeout_id = 0; queue_remove(app_connections, conn); destroy_connection(conn); return FALSE; } static void discover_primary_cb(uint8_t status, GSList *services, void *user_data) { struct discover_srvc_data *cb_data = user_data; struct app_connection *conn = cb_data->conn; struct gatt_device *dev = conn->device; GSList *l, *uuids = NULL; DBG("Status %d", status); if (status) { error("gatt: Discover all primary services failed: %s", att_ecode2str(status)); free(cb_data); return; } if (!services) { info("gatt: No primary services found"); free(cb_data); return; } for (l = services; l; l = l->next) { struct gatt_primary *prim = l->data; uint8_t *new_uuid; bt_uuid_t uuid, u128; DBG("uuid: %s", prim->uuid); if (bt_string_to_uuid(&uuid, prim->uuid) < 0) { error("gatt: Cannot convert string to uuid"); continue; } bt_uuid_to_uuid128(&uuid, &u128); new_uuid = util_memdup(&u128.value.u128, sizeof(u128.value.u128)); uuids = g_slist_prepend(uuids, new_uuid); } bt_device_set_uuids(&dev->bdaddr, uuids); free(cb_data); conn->timeout_id = g_timeout_add_seconds(GATT_CONN_TIMEOUT, connection_timeout, conn); } static guint search_dev_for_srvc(struct app_connection *conn, bt_uuid_t *uuid) { struct discover_srvc_data *cb_data; cb_data = new0(struct discover_srvc_data, 1); cb_data->conn = conn; if (uuid) { memcpy(&cb_data->uuid, uuid, sizeof(cb_data->uuid)); return gatt_discover_primary(conn->device->attrib, uuid, discover_srvc_by_uuid_cb, cb_data); } if (conn->app) return gatt_discover_primary(conn->device->attrib, NULL, discover_srvc_all_cb, cb_data); return gatt_discover_primary(conn->device->attrib, NULL, discover_primary_cb, cb_data); } struct connect_data { struct gatt_device *dev; int32_t status; }; static void notify_app_connect_status_by_device(void *data, void *user_data) { struct app_connection *conn = data; struct connect_data *con_data = user_data; if (conn->device == con_data->dev) notify_app_connect_status(conn, con_data->status); } static struct app_connection *find_conn_without_app(struct gatt_device *dev) { struct app_connection conn_match; conn_match.device = dev; conn_match.app = NULL; return queue_find(app_connections, match_connection_by_device_and_app, &conn_match); } static struct app_connection *find_conn(const bdaddr_t *addr, int32_t app_id) { struct app_connection conn_match; struct gatt_device *dev; struct gatt_app *app; /* Check if app is registered */ app = find_app_by_id(app_id); if (!app) { error("gatt: Client id %d not found", app_id); return NULL; } /* Check if device is known */ dev = find_device_by_addr(addr); if (!dev) { error("gatt: Client id %d not found", app_id); return NULL; } conn_match.device = dev; conn_match.app = app; return queue_find(app_connections, match_connection_by_device_and_app, &conn_match); } static void create_app_connection(void *data, void *user_data) { struct gatt_device *dev = user_data; struct gatt_app *app; app = find_app_by_id(PTR_TO_INT(data)); if (!app) return; DBG("Autoconnect application id=%d", app->id); if (!find_conn(&dev->bdaddr, PTR_TO_INT(data))) create_connection(dev, app); } static void ind_handler(const uint8_t *cmd, uint16_t cmd_len, gpointer user_data) { struct gatt_device *dev = user_data; uint16_t resp_length = 0; size_t length; uint8_t *opdu = g_attrib_get_buffer(dev->attrib, &length); /* * We have to send confirmation here. If some client is * registered for this indication, event will be send in * handle_notification */ resp_length = enc_confirmation(opdu, length); g_attrib_send(dev->attrib, 0, opdu, resp_length, NULL, NULL, NULL); } static void connect_cb(GIOChannel *io, GError *gerr, gpointer user_data) { struct gatt_device *dev = user_data; struct connect_data data; struct att_range range; uint32_t status; GError *err = NULL; GAttrib *attrib; uint16_t mtu, cid; if (dev->state != DEVICE_CONNECT_READY) { error("gatt: Device not in a connecting state!?"); g_io_channel_shutdown(io, TRUE, NULL); return; } if (dev->att_io) { g_io_channel_unref(dev->att_io); dev->att_io = NULL; } if (gerr) { error("gatt: connection failed %s", gerr->message); device_set_state(dev, DEVICE_DISCONNECTED); status = GATT_FAILURE; goto reply; } if (!bt_io_get(io, &err, BT_IO_OPT_IMTU, &mtu, BT_IO_OPT_CID, &cid, BT_IO_OPT_INVALID)) { error("gatt: Could not get imtu or cid: %s", err->message); device_set_state(dev, DEVICE_DISCONNECTED); status = GATT_FAILURE; g_error_free(err); goto reply; } /* on BR/EDR MTU must not be less then minimal allowed MTU */ if (cid != ATT_CID && mtu < ATT_DEFAULT_L2CAP_MTU) { error("gatt: MTU too small (%u bytes)", mtu); device_set_state(dev, DEVICE_DISCONNECTED); status = GATT_FAILURE; goto reply; } DBG("mtu %u cid %u", mtu, cid); /* on LE we always start with default MTU */ if (cid == ATT_CID) mtu = ATT_DEFAULT_LE_MTU; attrib = g_attrib_new(io, mtu, true); if (!attrib) { error("gatt: unable to create new GAttrib instance"); device_set_state(dev, DEVICE_DISCONNECTED); status = GATT_FAILURE; goto reply; } dev->attrib = attrib; dev->watch_id = g_io_add_watch(io, G_IO_HUP | G_IO_ERR | G_IO_NVAL, disconnected_cb, dev); dev->server_id = g_attrib_register(attrib, GATTRIB_ALL_REQS, GATTRIB_ALL_HANDLES, att_handler, dev, NULL); dev->ind_id = g_attrib_register(attrib, ATT_OP_HANDLE_IND, GATTRIB_ALL_HANDLES, ind_handler, dev, NULL); if ((dev->server_id && dev->ind_id) == 0) error("gatt: Could not attach to server"); device_set_state(dev, DEVICE_CONNECTED); /* Send exchange mtu request as we assume being client and server */ /* TODO: Dont exchange mtu if no client apps */ /* MTU exchange shall not be used on BR/EDR - Vol 3. Part G. 4.3.1 */ if (cid == ATT_CID) send_exchange_mtu_request(dev); /* * Service Changed Characteristic and CCC Descriptor handles * should not change if there are bonded devices. We have them * constant all the time, thus they should be excluded from * range indicating changes. */ range.start = gatt_db_attribute_get_handle(service_changed_attrib) + 2; range.end = 0xffff; /* * If there is ccc stored for that device we were acting as server for * it, and as we dont have last connect and last services (de)activation * timestamps we should always assume something has changed. */ notify_att_range_change(dev, &range); status = GATT_SUCCESS; reply: /* * Make sure there are app_connections for all apps interested in auto * connect to that device */ queue_foreach(dev->autoconnect_apps, create_app_connection, dev); if (!queue_find(app_connections, match_connection_by_device, dev)) { struct app_connection *conn; if (!dev->attrib) return; conn = create_connection(dev, NULL); if (!conn) return; if (bt_is_pairing(&dev->bdaddr)) /* * If there is bonding ongoing lets wait for paired * callback. Once we get that we can start search * services */ conn->timeout_id = g_timeout_add_seconds( GATT_PAIR_CONN_TIMEOUT, connection_timeout, conn); else /* * There is no ongoing bonding, lets search for primary * services */ search_dev_for_srvc(conn, NULL); } data.dev = dev; data.status = status; queue_foreach(app_connections, notify_app_connect_status_by_device, &data); /* For BR/EDR notify about MTU since it is not negotiable*/ if (cid != ATT_CID && status == GATT_SUCCESS) queue_foreach(app_connections, notify_mtu_change, dev); device_unref(dev); /* Check if we should restart scan */ if (scanning) bt_le_discovery_start(); /* FIXME: What to do if discovery won't start here. */ } static int connect_le(struct gatt_device *dev) { GIOChannel *io; GError *gerr = NULL; char addr[18]; const bdaddr_t *bdaddr; uint8_t bdaddr_type; ba2str(&dev->bdaddr, addr); /* There is one connection attempt going on */ if (dev->att_io) { info("gatt: connection to dev %s is ongoing", addr); return -EALREADY; } DBG("Connection attempt to: %s", addr); bdaddr = bt_get_id_addr(&dev->bdaddr, &bdaddr_type); /* * This connection will help us catch any PDUs that comes before * pairing finishes */ io = bt_io_connect(connect_cb, device_ref(dev), NULL, &gerr, BT_IO_OPT_SOURCE_BDADDR, &adapter_addr, BT_IO_OPT_SOURCE_TYPE, BDADDR_LE_PUBLIC, BT_IO_OPT_DEST_BDADDR, bdaddr, BT_IO_OPT_DEST_TYPE, bdaddr_type, BT_IO_OPT_CID, ATT_CID, BT_IO_OPT_SEC_LEVEL, BT_IO_SEC_LOW, BT_IO_OPT_INVALID); if (!io) { error("gatt: Failed bt_io_connect(%s): %s", addr, gerr->message); g_error_free(gerr); return -EIO; } /* Keep this, so we can cancel the connection */ dev->att_io = io; device_set_state(dev, DEVICE_CONNECT_READY); return 0; } static int connect_next_dev(void) { struct gatt_device *dev; DBG(""); dev = find_device_by_state(DEVICE_CONNECT_READY); if (!dev) return -ENODEV; return connect_le(dev); } static void bt_le_discovery_stop_cb(void) { DBG(""); /* Check now if there is any device ready to connect */ if (connect_next_dev() < 0) bt_le_discovery_start(); } static void le_device_found_handler(const bdaddr_t *addr, int rssi, uint16_t eir_len, const void *eir, bool connectable, bool bonded) { uint8_t buf[IPC_MTU]; struct hal_ev_gatt_client_scan_result *ev = (void *) buf; struct gatt_device *dev; char bda[18]; if (!scanning) goto done; ba2str(addr, bda); DBG("LE Device found: %s, rssi: %d, adv_data: %d", bda, rssi, !!eir); bdaddr2android(addr, ev->bda); ev->rssi = rssi; ev->len = eir_len; memcpy(ev->adv_data, eir, ev->len); ipc_send_notif(hal_ipc, HAL_SERVICE_ID_GATT, HAL_EV_GATT_CLIENT_SCAN_RESULT, sizeof(*ev) + ev->len, ev); done: if (!connectable) return; /* We use auto connect feature from kernel if possible */ if (bt_kernel_conn_control()) return; dev = find_device_by_addr(addr); if (!dev) { if (!bonded) return; dev = create_device(addr); } if (dev->state != DEVICE_CONNECT_INIT) return; device_set_state(dev, DEVICE_CONNECT_READY); /* * We are ok to perform connect now. Stop discovery * and once it is stopped continue with creating ACL */ bt_le_discovery_stop(bt_le_discovery_stop_cb); } static struct gatt_app *register_app(const uint8_t *uuid, gatt_type_t type) { static int32_t application_id = 1; struct gatt_app *app; if (queue_find(gatt_apps, match_app_by_uuid, uuid)) { error("gatt: app uuid is already on list"); return NULL; } app = new0(struct gatt_app, 1); app->type = type; if (app->type == GATT_CLIENT) app->notifications = queue_new(); memcpy(app->uuid, uuid, sizeof(app->uuid)); app->id = application_id++; queue_push_head(gatt_apps, app); if (app->type == GATT_SERVER) queue_push_tail(listen_apps, INT_TO_PTR(app->id)); return app; } static void handle_client_register(const void *buf, uint16_t len) { const struct hal_cmd_gatt_client_register *cmd = buf; struct hal_ev_gatt_client_register_client ev; struct gatt_app *app; DBG(""); memset(&ev, 0, sizeof(ev)); app = register_app(cmd->uuid, GATT_CLIENT); if (app) { ev.client_if = app->id; ev.status = GATT_SUCCESS; } else { ev.status = GATT_FAILURE; } /* We should send notification with given in cmd UUID */ memcpy(ev.app_uuid, cmd->uuid, sizeof(ev.app_uuid)); ipc_send_notif(hal_ipc, HAL_SERVICE_ID_GATT, HAL_EV_GATT_CLIENT_REGISTER_CLIENT, sizeof(ev), &ev); ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_GATT, HAL_OP_GATT_CLIENT_REGISTER, HAL_STATUS_SUCCESS); } static void handle_client_scan(const void *buf, uint16_t len) { const struct hal_cmd_gatt_client_scan *cmd = buf; uint8_t status; DBG("new state %d", cmd->start); if (cmd->client_if != 0) { void *registered = find_app_by_id(cmd->client_if); if (!registered) { error("gatt: Client not registered"); status = HAL_STATUS_FAILED; goto reply; } } /* Turn off scan */ if (!cmd->start) { DBG("Stopping LE SCAN"); if (scanning) { bt_le_discovery_stop(NULL); scanning = false; } status = HAL_STATUS_SUCCESS; goto reply; } /* Reply success if we already do scan */ if (scanning) { status = HAL_STATUS_SUCCESS; goto reply; } /* Turn on scan */ if (!bt_le_discovery_start()) { error("gatt: LE scan switch failed"); status = HAL_STATUS_FAILED; goto reply; } scanning = true; status = HAL_STATUS_SUCCESS; reply: ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_GATT, HAL_OP_GATT_CLIENT_SCAN, status); } static int connect_bredr(struct gatt_device *dev) { BtIOSecLevel sec_level; GIOChannel *io; GError *gerr = NULL; char addr[18]; ba2str(&dev->bdaddr, addr); /* There is one connection attempt going on */ if (dev->att_io) { info("gatt: connection to dev %s is ongoing", addr); return -EALREADY; } DBG("Connection attempt to: %s", addr); sec_level = bt_device_is_bonded(&dev->bdaddr) ? BT_IO_SEC_MEDIUM : BT_IO_SEC_LOW; io = bt_io_connect(connect_cb, device_ref(dev), NULL, &gerr, BT_IO_OPT_SOURCE_BDADDR, &adapter_addr, BT_IO_OPT_SOURCE_TYPE, BDADDR_BREDR, BT_IO_OPT_DEST_BDADDR, &dev->bdaddr, BT_IO_OPT_DEST_TYPE, BDADDR_BREDR, BT_IO_OPT_PSM, ATT_PSM, BT_IO_OPT_SEC_LEVEL, sec_level, BT_IO_OPT_INVALID); if (!io) { error("gatt: Failed bt_io_connect(%s): %s", addr, gerr->message); g_error_free(gerr); return -EIO; } device_set_state(dev, DEVICE_CONNECT_READY); /* Keep this, so we can cancel the connection */ dev->att_io = io; return 0; } static bool trigger_connection(struct app_connection *conn, bool direct) { switch (conn->device->state) { case DEVICE_DISCONNECTED: /* * If device was last seen over BR/EDR connect over it. * Note: Connection state is handled in connect_bredr() func */ if (bt_device_last_seen_bearer(&conn->device->bdaddr) == BDADDR_BREDR) return connect_bredr(conn->device) == 0; if (direct) return connect_le(conn->device) == 0; bt_gatt_add_autoconnect(conn->app->id, &conn->device->bdaddr); return auto_connect_le(conn->device); case DEVICE_CONNECTED: notify_app_connect_status(conn, GATT_SUCCESS); return true; case DEVICE_CONNECT_READY: case DEVICE_CONNECT_INIT: default: /* In those cases connection is already triggered. */ return true; } } static void remove_autoconnect_device(struct gatt_device *dev) { bt_auto_connect_remove(&dev->bdaddr); if (dev->state == DEVICE_CONNECT_INIT) device_set_state(dev, DEVICE_DISCONNECTED); device_unref(dev); } static void clear_autoconnect_devices(void *data, void *user_data) { struct gatt_device *dev = data; if (queue_remove(dev->autoconnect_apps, user_data)) if (queue_isempty(dev->autoconnect_apps)) remove_autoconnect_device(dev); } static uint8_t unregister_app(int client_if) { struct gatt_app *cl; /* * Make sure that there is no devices in auto connect list for this * application */ queue_foreach(gatt_devices, clear_autoconnect_devices, INT_TO_PTR(client_if)); cl = queue_remove_if(gatt_apps, match_app_by_id, INT_TO_PTR(client_if)); if (!cl) { error("gatt: client_if=%d not found", client_if); return HAL_STATUS_FAILED; } /* Destroy app connections with proper notifications for this app. */ queue_remove_all(app_connections, match_connection_by_app, cl, destroy_connection); destroy_gatt_app(cl); return HAL_STATUS_SUCCESS; } static void send_client_listen_notify(int32_t id, int32_t status) { struct hal_ev_gatt_client_listen ev; /* Server if because of typo in android headers */ ev.server_if = id; ev.status = status; ipc_send_notif(hal_ipc, HAL_SERVICE_ID_GATT, HAL_EV_GATT_CLIENT_LISTEN, sizeof(ev), &ev); } struct listen_data { int32_t client_id; bool start; }; static struct listen_data *create_listen_data(int32_t client_id, bool start) { struct listen_data *d; d = new0(struct listen_data, 1); d->client_id = client_id; d->start = start; return d; } static void set_advertising_cb(uint8_t status, void *user_data) { struct listen_data *l = user_data; send_client_listen_notify(l->client_id, status); /* In case of success update advertising state*/ if (!status) advertising_cnt = l->start ? 1 : 0; /* * Let's remove client from the list in two cases * 1. Start failed * 2. Stop succeed */ if ((l->start && status) || (!l->start && !status)) queue_remove(listen_apps, INT_TO_PTR(l->client_id)); free(l); } static void handle_client_unregister(const void *buf, uint16_t len) { const struct hal_cmd_gatt_client_unregister *cmd = buf; uint8_t status; void *listening_client; struct listen_data *data; DBG(""); listening_client = queue_find(listen_apps, NULL, INT_TO_PTR(cmd->client_if)); if (listening_client) { advertising_cnt--; queue_remove(listen_apps, INT_TO_PTR(cmd->client_if)); } else { status = unregister_app(cmd->client_if); goto reply; } if (!advertising_cnt) { data = create_listen_data(cmd->client_if, false); if (!bt_le_set_advertising(data->start, set_advertising_cb, data)) { error("gatt: Could not set advertising"); status = HAL_STATUS_FAILED; free(data); goto reply; } } status = unregister_app(cmd->client_if); reply: ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_GATT, HAL_OP_GATT_CLIENT_UNREGISTER, status); } static uint8_t handle_connect(int32_t app_id, const bdaddr_t *addr, bool direct) { struct app_connection conn_match; struct app_connection *conn; struct gatt_device *device; struct gatt_app *app; DBG(""); app = find_app_by_id(app_id); if (!app) return HAL_STATUS_FAILED; device = find_device_by_addr(addr); if (!device) device = create_device(addr); conn_match.device = device; conn_match.app = app; conn = queue_find(app_connections, match_connection_by_device_and_app, &conn_match); if (!conn) { conn = create_connection(device, app); if (!conn) return HAL_STATUS_NOMEM; } if (!trigger_connection(conn, direct)) return HAL_STATUS_FAILED; return HAL_STATUS_SUCCESS; } static void handle_client_connect(const void *buf, uint16_t len) { const struct hal_cmd_gatt_client_connect *cmd = buf; uint8_t status; bdaddr_t addr; DBG("is_direct:%u transport:%u", cmd->is_direct, cmd->transport); android2bdaddr(&cmd->bdaddr, &addr); /* TODO handle transport flag */ status = handle_connect(cmd->client_if, &addr, cmd->is_direct); ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_GATT, HAL_OP_GATT_CLIENT_CONNECT, status); } static void handle_client_disconnect(const void *buf, uint16_t len) { const struct hal_cmd_gatt_client_disconnect *cmd = buf; struct app_connection *conn; uint8_t status; DBG(""); /* TODO: should we care to match also bdaddr when conn_id is unique? */ conn = queue_remove_if(app_connections, match_connection_by_id, INT_TO_PTR(cmd->conn_id)); destroy_connection(conn); status = HAL_STATUS_SUCCESS; ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_GATT, HAL_OP_GATT_CLIENT_DISCONNECT, status); } static void handle_client_listen(const void *buf, uint16_t len) { const struct hal_cmd_gatt_client_listen *cmd = buf; uint8_t status; struct listen_data *data; bool req_sent = false; void *listening_client; DBG(""); if (!find_app_by_id(cmd->client_if)) { error("gatt: Client not registered"); status = HAL_STATUS_FAILED; goto reply; } listening_client = queue_find(listen_apps, NULL, INT_TO_PTR(cmd->client_if)); /* Start listening */ if (cmd->start) { if (listening_client) { status = HAL_STATUS_SUCCESS; goto reply; } queue_push_tail(listen_apps, INT_TO_PTR(cmd->client_if)); /* If listen is already on just return success*/ if (advertising_cnt > 0) { advertising_cnt++; status = HAL_STATUS_SUCCESS; goto reply; } } else { /* Stop listening. Check if client was listening */ if (!listening_client) { error("gatt: This client %d does not listen", cmd->client_if); status = HAL_STATUS_FAILED; goto reply; } /* * In case there is more listening clients don't stop * advertising */ if (advertising_cnt > 1) { advertising_cnt--; queue_remove(listen_apps, INT_TO_PTR(cmd->client_if)); status = HAL_STATUS_SUCCESS; goto reply; } } data = create_listen_data(cmd->client_if, cmd->start); if (!bt_le_set_advertising(cmd->start, set_advertising_cb, data)) { error("gatt: Could not set advertising"); status = HAL_STATUS_FAILED; free(data); goto reply; } /* * Use this flag to keep in mind that we are waiting for callback with * result */ req_sent = true; status = HAL_STATUS_SUCCESS; reply: ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_GATT, HAL_OP_GATT_CLIENT_LISTEN, status); /* In case of early success or error, just send notification up */ if (!req_sent) { int32_t gatt_status = status == HAL_STATUS_SUCCESS ? GATT_SUCCESS : GATT_FAILURE; send_client_listen_notify(cmd->client_if, gatt_status); } } static void handle_client_refresh(const void *buf, uint16_t len) { const struct hal_cmd_gatt_client_refresh *cmd = buf; struct gatt_device *dev; uint8_t status; bdaddr_t bda; /* * This is Android's framework hidden API call. It seams that no * notification is expected and Bluedroid silently updates device's * cache under the hood. As we use lazy caching ,we can just clear the * cache and we're done. */ DBG(""); android2bdaddr(&cmd->bdaddr, &bda); dev = find_device_by_addr(&bda); if (!dev) { status = HAL_STATUS_FAILED; goto done; } queue_remove_all(dev->services, NULL, NULL, destroy_service); status = HAL_STATUS_SUCCESS; done: ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_GATT, HAL_OP_GATT_CLIENT_REFRESH, status); } static void handle_client_search_service(const void *buf, uint16_t len) { const struct hal_cmd_gatt_client_search_service *cmd = buf; struct app_connection *conn; uint8_t status; struct service *s; bt_uuid_t uuid; guint srvc_search_success; DBG(""); if (len != sizeof(*cmd) + (cmd->filtered ? 16 : 0)) { error("Invalid search service size (%u bytes), terminating", len); raise(SIGTERM); return; } conn = find_connection_by_id(cmd->conn_id); if (!conn) { error("gatt: dev with conn_id=%d not found", cmd->conn_id); status = HAL_STATUS_FAILED; goto reply; } if (conn->device->state != DEVICE_CONNECTED) { char bda[18]; ba2str(&conn->device->bdaddr, bda); error("gatt: device %s not connected", bda); status = HAL_STATUS_FAILED; goto reply; } if (cmd->filtered) android2uuid(cmd->filter_uuid, &uuid); /* Services not cached yet */ if (queue_isempty(conn->device->services)) { if (cmd->filtered) srvc_search_success = search_dev_for_srvc(conn, &uuid); else srvc_search_success = search_dev_for_srvc(conn, NULL); if (!srvc_search_success) { status = HAL_STATUS_FAILED; goto reply; } status = HAL_STATUS_SUCCESS; goto reply; } /* Search in cached services for given service */ if (cmd->filtered) { /* Search in cache for service by uuid */ s = queue_find(conn->device->services, match_srvc_by_bt_uuid, &uuid); if (s) { send_client_primary_notify(s, INT_TO_PTR(conn->id)); } else { if (!search_dev_for_srvc(conn, &uuid)) { status = HAL_STATUS_FAILED; goto reply; } status = HAL_STATUS_SUCCESS; goto reply; } } else { /* Refresh service cache if only partial search was performed */ if (conn->device->partial_srvc_search) { srvc_search_success = search_dev_for_srvc(conn, NULL); if (!srvc_search_success) { status = HAL_STATUS_FAILED; goto reply; } } else queue_foreach(conn->device->services, send_client_primary_notify, INT_TO_PTR(cmd->conn_id)); } send_client_search_complete_notify(GATT_SUCCESS, conn->id); status = HAL_STATUS_SUCCESS; reply: ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_GATT, HAL_OP_GATT_CLIENT_SEARCH_SERVICE, status); } static void send_client_incl_service_notify(const struct element_id *srvc_id, const struct service *incl, int32_t conn_id) { struct hal_ev_gatt_client_get_inc_service ev; memset(&ev, 0, sizeof(ev)); ev.conn_id = conn_id; element_id_to_hal_srvc_id(srvc_id, 1, &ev.srvc_id); if (incl) { element_id_to_hal_srvc_id(&incl->id, 0, &ev.incl_srvc_id); ev.status = GATT_SUCCESS; } else { ev.status = GATT_FAILURE; } ipc_send_notif(hal_ipc, HAL_SERVICE_ID_GATT , HAL_EV_GATT_CLIENT_GET_INC_SERVICE, sizeof(ev), &ev); } struct get_included_data { struct service *prim; struct app_connection *conn; }; static int get_inst_id_of_prim_services(const struct gatt_device *dev) { struct service *s = queue_peek_tail(dev->services); if (s) return s->id.instance; return -1; } static void get_included_cb(uint8_t status, GSList *included, void *user_data) { struct get_included_data *data = user_data; struct app_connection *conn = data->conn; struct service *service = data->prim; struct service *incl = NULL; int instance_id; DBG(""); free(data); if (status) { error("gatt: no included services found"); goto failed; } /* Remember that we already search included services.*/ service->incl_search_done = true; /* * There might be multiply services with same uuid. Therefore make sure * each service has unique instance id. Let's take the latest instance * id of primary service and start iterate included services from this * point. */ instance_id = get_inst_id_of_prim_services(conn->device); if (instance_id < 0) goto failed; for (; included; included = included->next) { struct gatt_included *included_service = included->data; incl = create_service(++instance_id, false, included_service->uuid, included_service); if (!incl) continue; /* * Lets keep included service on two queues. * 1. on services queue together with primary service * 2. on special queue inside primary service */ queue_push_tail(service->included, incl); queue_push_tail(conn->device->services, incl); } /* * Notify upper layer about first included service. * Android framework will iterate for next one. */ incl = queue_peek_head(service->included); failed: send_client_incl_service_notify(&service->id, incl, conn->id); } static void search_included_services(struct app_connection *conn, struct service *service) { struct get_included_data *data; uint16_t start, end; data = new0(struct get_included_data, 1); data->prim = service; data->conn = conn; if (service->primary) { start = service->prim.range.start; end = service->prim.range.end; } else { start = service->incl.range.start; end = service->incl.range.end; } gatt_find_included(conn->device->attrib, start, end, get_included_cb, data); } static bool find_service(int32_t conn_id, struct element_id *service_id, struct app_connection **connection, struct service **service) { struct service *srvc; struct app_connection *conn; conn = find_connection_by_id(conn_id); if (!conn) { error("gatt: conn_id=%d not found", conn_id); return false; } srvc = queue_find(conn->device->services, match_srvc_by_element_id, service_id); if (!srvc) { error("gatt: Service with inst_id: %d not found", service_id->instance); return false; } *connection = conn; *service = srvc; return true; } static void handle_client_get_included_service(const void *buf, uint16_t len) { const struct hal_cmd_gatt_client_get_included_service *cmd = buf; struct app_connection *conn; struct service *prim_service; struct service *incl_service = NULL; struct element_id match_id; struct element_id srvc_id; uint8_t status; DBG(""); hal_srvc_id_to_element_id(&cmd->srvc_id, &srvc_id); if (len != sizeof(*cmd) + (cmd->continuation ? sizeof(cmd->incl_srvc_id[0]) : 0)) { error("Invalid get incl services size (%u bytes), terminating", len); raise(SIGTERM); return; } hal_srvc_id_to_element_id(&cmd->srvc_id, &match_id); if (!find_service(cmd->conn_id, &match_id, &conn, &prim_service)) { status = HAL_STATUS_FAILED; goto notify; } if (!prim_service->incl_search_done) { search_included_services(conn, prim_service); status = HAL_STATUS_SUCCESS; goto reply; } /* Try to use cache here */ if (!cmd->continuation) { incl_service = queue_peek_head(prim_service->included); } else { uint8_t inst_id = cmd->incl_srvc_id[0].inst_id; incl_service = queue_find(prim_service->included, match_srvc_by_higher_inst_id, INT_TO_PTR(inst_id)); } status = HAL_STATUS_SUCCESS; notify: /* * In case of error in handling request we need to send event with * service id of cmd and gatt failure status. */ send_client_incl_service_notify(&srvc_id, incl_service, cmd->conn_id); reply: ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_GATT, HAL_OP_GATT_CLIENT_GET_INCLUDED_SERVICE, status); } static void send_client_char_notify(const struct hal_gatt_srvc_id *service, const struct hal_gatt_gatt_id *charac, int32_t char_prop, int32_t conn_id) { struct hal_ev_gatt_client_get_characteristic ev; ev.conn_id = conn_id; if (charac) { memcpy(&ev.char_id, charac, sizeof(struct hal_gatt_gatt_id)); ev.char_prop = char_prop; ev.status = GATT_SUCCESS; } else { memset(&ev.char_id, 0, sizeof(struct hal_gatt_gatt_id)); ev.char_prop = 0; ev.status = GATT_FAILURE; } memcpy(&ev.srvc_id, service, sizeof(struct hal_gatt_srvc_id)); ipc_send_notif(hal_ipc, HAL_SERVICE_ID_GATT, HAL_EV_GATT_CLIENT_GET_CHARACTERISTIC, sizeof(ev), &ev); } static void convert_send_client_char_notify(const struct characteristic *ch, int32_t conn_id, const struct service *service) { struct hal_gatt_srvc_id srvc; struct hal_gatt_gatt_id charac; element_id_to_hal_srvc_id(&service->id, service->primary, &srvc); if (ch) { element_id_to_hal_gatt_id(&ch->id, &charac); send_client_char_notify(&srvc, &charac, ch->ch.properties, conn_id); } else { send_client_char_notify(&srvc, NULL, 0, conn_id); } } static void cache_all_srvc_chars(struct service *srvc, GSList *characteristics) { uint16_t inst_id = 0; bt_uuid_t uuid; for (; characteristics; characteristics = characteristics->next) { struct characteristic *ch; ch = new0(struct characteristic, 1); ch->descriptors = queue_new(); memcpy(&ch->ch, characteristics->data, sizeof(ch->ch)); bt_string_to_uuid(&uuid, ch->ch.uuid); bt_uuid_to_uuid128(&uuid, &ch->id.uuid); /* * For now we increment inst_id and use it as characteristic * handle */ ch->id.instance = ++inst_id; /* Store end handle to use later for descriptors discovery */ if (characteristics->next) { struct gatt_char *next = characteristics->next->data; ch->end_handle = next->handle - 1; } else { ch->end_handle = srvc->primary ? srvc->prim.range.end : srvc->incl.range.end; } DBG("attr handle = 0x%04x, end handle = 0x%04x uuid: %s", ch->ch.handle, ch->end_handle, ch->ch.uuid); queue_push_tail(srvc->chars, ch); } } struct discover_char_data { int32_t conn_id; struct service *service; }; static void discover_char_cb(uint8_t status, GSList *characteristics, void *user_data) { struct discover_char_data *data = user_data; struct service *srvc = data->service; if (status) { error("gatt: Failed to get characteristics: %s", att_ecode2str(status)); convert_send_client_char_notify(NULL, data->conn_id, srvc); goto done; } if (queue_isempty(srvc->chars)) cache_all_srvc_chars(srvc, characteristics); convert_send_client_char_notify(queue_peek_head(srvc->chars), data->conn_id, srvc); done: free(data); } static void handle_client_get_characteristic(const void *buf, uint16_t len) { const struct hal_cmd_gatt_client_get_characteristic *cmd = buf; struct characteristic *ch; struct element_id match_id; struct app_connection *conn; struct service *srvc; uint8_t status; DBG(""); if (len != sizeof(*cmd) + (cmd->continuation ? sizeof(cmd->char_id[0]) : 0)) { error("Invalid get characteristic size (%u bytes), terminating", len); raise(SIGTERM); return; } hal_srvc_id_to_element_id(&cmd->srvc_id, &match_id); if (!find_service(cmd->conn_id, &match_id, &conn, &srvc)) { status = HAL_STATUS_FAILED; goto done; } /* Discover all characteristics for services if not cached yet */ if (queue_isempty(srvc->chars)) { struct discover_char_data *cb_data; struct att_range range; cb_data = new0(struct discover_char_data, 1); cb_data->service = srvc; cb_data->conn_id = conn->id; range = srvc->primary ? srvc->prim.range : srvc->incl.range; if (!gatt_discover_char(conn->device->attrib, range.start, range.end, NULL, discover_char_cb, cb_data)) { free(cb_data); status = HAL_STATUS_FAILED; goto done; } status = HAL_STATUS_SUCCESS; goto done; } if (cmd->continuation) ch = queue_find(srvc->chars, match_char_by_higher_inst_id, INT_TO_PTR(cmd->char_id[0].inst_id)); else ch = queue_peek_head(srvc->chars); convert_send_client_char_notify(ch, conn->id, srvc); status = HAL_STATUS_SUCCESS; done: if (status != HAL_STATUS_SUCCESS) send_client_char_notify(&cmd->srvc_id, NULL, 0, cmd->conn_id); ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_GATT, HAL_OP_GATT_CLIENT_GET_CHARACTERISTIC, status); } static void send_client_descr_notify(int32_t status, int32_t conn_id, bool primary, const struct element_id *srvc, const struct element_id *ch, const struct element_id *opt_descr) { struct hal_ev_gatt_client_get_descriptor ev; memset(&ev, 0, sizeof(ev)); ev.status = status; ev.conn_id = conn_id; element_id_to_hal_srvc_id(srvc, primary, &ev.srvc_id); element_id_to_hal_gatt_id(ch, &ev.char_id); if (opt_descr) element_id_to_hal_gatt_id(opt_descr, &ev.descr_id); ipc_send_notif(hal_ipc, HAL_SERVICE_ID_GATT, HAL_EV_GATT_CLIENT_GET_DESCRIPTOR, sizeof(ev), &ev); } struct discover_desc_data { struct app_connection *conn; struct service *srvc; struct characteristic *ch; }; static void gatt_discover_desc_cb(guint8 status, GSList *descs, gpointer user_data) { struct discover_desc_data *data = user_data; struct app_connection *conn = data->conn; struct service *srvc = data->srvc; struct characteristic *ch = data->ch; struct descriptor *descr; int i = 0; if (status != 0) { error("Discover all characteristic descriptors failed [%s]: %s", ch->ch.uuid, att_ecode2str(status)); goto reply; } for ( ; descs; descs = descs->next) { struct gatt_desc *desc = descs->data; bt_uuid_t uuid; descr = new0(struct descriptor, 1); bt_string_to_uuid(&uuid, desc->uuid); bt_uuid_to_uuid128(&uuid, &descr->id.uuid); descr->id.instance = ++i; descr->handle = desc->handle; DBG("attr handle = 0x%04x, uuid: %s", desc->handle, desc->uuid); queue_push_tail(ch->descriptors, descr); } reply: descr = queue_peek_head(ch->descriptors); send_client_descr_notify(status ? GATT_FAILURE : GATT_SUCCESS, conn->id, srvc->primary, &srvc->id, &ch->id, descr ? &descr->id : NULL); free(data); } static bool build_descr_cache(struct app_connection *conn, struct service *srvc, struct characteristic *ch) { struct discover_desc_data *cb_data; uint16_t start, end; /* Clip range to given characteristic */ start = ch->ch.value_handle + 1; end = ch->end_handle; /* If there are no descriptors, notify with fail status. */ if (start > end) return false; cb_data = new0(struct discover_desc_data, 1); cb_data->conn = conn; cb_data->srvc = srvc; cb_data->ch = ch; if (!gatt_discover_desc(conn->device->attrib, start, end, NULL, gatt_discover_desc_cb, cb_data)) { free(cb_data); return false; } return true; } static void handle_client_get_descriptor(const void *buf, uint16_t len) { const struct hal_cmd_gatt_client_get_descriptor *cmd = buf; struct descriptor *descr = NULL; struct characteristic *ch; struct service *srvc; struct element_id srvc_id; struct element_id char_id; struct app_connection *conn; int32_t conn_id; uint8_t primary; uint8_t status; DBG(""); if (len != sizeof(*cmd) + (cmd->continuation ? sizeof(cmd->descr_id[0]) : 0)) { error("gatt: Invalid get descr command (%u bytes), terminating", len); raise(SIGTERM); return; } conn_id = cmd->conn_id; primary = cmd->srvc_id.is_primary; hal_srvc_id_to_element_id(&cmd->srvc_id, &srvc_id); hal_gatt_id_to_element_id(&cmd->char_id, &char_id); if (!find_service(conn_id, &srvc_id, &conn, &srvc)) { error("gatt: Get descr. could not find service"); status = HAL_STATUS_FAILED; goto failed; } ch = queue_find(srvc->chars, match_char_by_element_id, &char_id); if (!ch) { error("gatt: Get descr. could not find characteristic"); status = HAL_STATUS_FAILED; goto failed; } if (queue_isempty(ch->descriptors)) { if (build_descr_cache(conn, srvc, ch)) { ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_GATT, HAL_OP_GATT_CLIENT_GET_DESCRIPTOR, HAL_STATUS_SUCCESS); return; } } status = HAL_STATUS_SUCCESS; /* Send from cache */ if (cmd->continuation) descr = queue_find(ch->descriptors, match_descr_by_higher_inst_id, INT_TO_PTR(cmd->descr_id[0].inst_id)); else descr = queue_peek_head(ch->descriptors); failed: send_client_descr_notify(descr ? GATT_SUCCESS : GATT_FAILURE, conn_id, primary, &srvc_id, &char_id, &descr->id); ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_GATT, HAL_OP_GATT_CLIENT_GET_DESCRIPTOR, status); } struct char_op_data { int32_t conn_id; const struct element_id *srvc_id; const struct element_id *char_id; uint8_t primary; }; static struct char_op_data *create_char_op_data(int32_t conn_id, const struct element_id *s_id, const struct element_id *ch_id, bool primary) { struct char_op_data *d; d = new0(struct char_op_data, 1); d->conn_id = conn_id; d->srvc_id = s_id; d->char_id = ch_id; d->primary = primary; return d; } static void send_client_read_char_notify(int32_t status, const uint8_t *pdu, uint16_t len, int32_t conn_id, const struct element_id *s_id, const struct element_id *ch_id, uint8_t primary) { uint8_t buf[IPC_MTU]; struct hal_ev_gatt_client_read_characteristic *ev = (void *) buf; ssize_t vlen; memset(buf, 0, sizeof(buf)); ev->conn_id = conn_id; ev->status = status; ev->data.status = status; element_id_to_hal_srvc_id(s_id, primary, &ev->data.srvc_id); element_id_to_hal_gatt_id(ch_id, &ev->data.char_id); if (status == 0 && pdu) { vlen = dec_read_resp(pdu, len, ev->data.value, sizeof(buf)); if (vlen < 0) { error("gatt: Protocol error"); ev->status = GATT_FAILURE; } else { ev->data.len = vlen; } } ipc_send_notif(hal_ipc, HAL_SERVICE_ID_GATT, HAL_EV_GATT_CLIENT_READ_CHARACTERISTIC, sizeof(*ev) + ev->data.len, ev); } static void read_char_cb(guint8 status, const guint8 *pdu, guint16 len, gpointer user_data) { struct char_op_data *data = user_data; send_client_read_char_notify(status, pdu, len, data->conn_id, data->srvc_id, data->char_id, data->primary); free(data); } static int get_cid(struct gatt_device *dev) { GIOChannel *io; uint16_t cid; io = g_attrib_get_channel(dev->attrib); if (!bt_io_get(io, NULL, BT_IO_OPT_CID, &cid, BT_IO_OPT_INVALID)) { error("gatt: Failed to get CID"); return -1; } return cid; } static int get_sec_level(struct gatt_device *dev) { GIOChannel *io; int sec_level; io = g_attrib_get_channel(dev->attrib); if (!bt_io_get(io, NULL, BT_IO_OPT_SEC_LEVEL, &sec_level, BT_IO_OPT_INVALID)) { error("gatt: Failed to get sec_level"); return -1; } return sec_level; } static bool set_security(struct gatt_device *device, int req_sec_level) { int sec_level; GError *gerr = NULL; GIOChannel *io; sec_level = get_sec_level(device); if (sec_level < 0) return false; if (req_sec_level <= sec_level) return true; io = g_attrib_get_channel(device->attrib); if (!io) return false; bt_io_set(io, &gerr, BT_IO_OPT_SEC_LEVEL, req_sec_level, BT_IO_OPT_INVALID); if (gerr) { error("gatt: Failed to set security level: %s", gerr->message); g_error_free(gerr); return false; } return true; } bool bt_gatt_set_security(const bdaddr_t *bdaddr, int sec_level) { struct gatt_device *device; device = find_device_by_addr(bdaddr); if (!device) return false; return set_security(device, sec_level); } static bool set_auth_type(struct gatt_device *device, int auth_type) { int sec_level; switch (auth_type) { case HAL_GATT_AUTHENTICATION_MITM: sec_level = BT_SECURITY_HIGH; break; case HAL_GATT_AUTHENTICATION_NO_MITM: sec_level = BT_SECURITY_MEDIUM; break; case HAL_GATT_AUTHENTICATION_NONE: sec_level = BT_SECURITY_LOW; break; default: error("gatt: Invalid auth_type value: %d", auth_type); return false; } return set_security(device, sec_level); } static void handle_client_read_characteristic(const void *buf, uint16_t len) { const struct hal_cmd_gatt_client_read_characteristic *cmd = buf; struct char_op_data *cb_data; struct characteristic *ch; struct app_connection *conn; struct service *srvc; struct element_id srvc_id; struct element_id char_id; uint8_t status; DBG(""); /* TODO authorization needs to be handled */ hal_srvc_id_to_element_id(&cmd->srvc_id, &srvc_id); hal_gatt_id_to_element_id(&cmd->char_id, &char_id); if (!find_service(cmd->conn_id, &srvc_id, &conn, &srvc)) { status = HAL_STATUS_FAILED; goto failed; } /* search characteristics by element id */ ch = queue_find(srvc->chars, match_char_by_element_id, &char_id); if (!ch) { error("gatt: Characteristic with inst_id: %d not found", cmd->char_id.inst_id); status = HAL_STATUS_FAILED; goto failed; } cb_data = create_char_op_data(cmd->conn_id, &srvc->id, &ch->id, cmd->srvc_id.is_primary); if (!set_auth_type(conn->device, cmd->auth_req)) { error("gatt: Failed to set security %d", cmd->auth_req); status = HAL_STATUS_FAILED; free(cb_data); goto failed; } if (!gatt_read_char(conn->device->attrib, ch->ch.value_handle, read_char_cb, cb_data)) { error("gatt: Cannot read characteristic with inst_id: %d", cmd->char_id.inst_id); status = HAL_STATUS_FAILED; free(cb_data); goto failed; } status = HAL_STATUS_SUCCESS; failed: ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_GATT, HAL_OP_GATT_CLIENT_READ_CHARACTERISTIC, status); /* * We should send notification with service, characteristic id in case * of errors. */ if (status != HAL_STATUS_SUCCESS) send_client_read_char_notify(GATT_FAILURE, NULL, 0, cmd->conn_id, &srvc_id, &char_id, cmd->srvc_id.is_primary); } static void send_client_write_char_notify(int32_t status, int32_t conn_id, const struct element_id *srvc_id, const struct element_id *char_id, uint8_t primary) { struct hal_ev_gatt_client_write_characteristic ev; memset(&ev, 0, sizeof(ev)); ev.conn_id = conn_id; ev.status = status; ev.data.status = status; element_id_to_hal_srvc_id(srvc_id, primary, &ev.data.srvc_id); element_id_to_hal_gatt_id(char_id, &ev.data.char_id); ipc_send_notif(hal_ipc, HAL_SERVICE_ID_GATT, HAL_EV_GATT_CLIENT_WRITE_CHARACTERISTIC, sizeof(ev), &ev); } static void write_char_cb(guint8 status, const guint8 *pdu, guint16 len, gpointer user_data) { struct char_op_data *data = user_data; send_client_write_char_notify(status, data->conn_id, data->srvc_id, data->char_id, data->primary); free(data); } static guint signed_write_cmd(struct gatt_device *dev, uint16_t handle, const uint8_t *value, uint16_t vlen) { uint8_t csrk[16]; uint32_t sign_cnt; guint res; memset(csrk, 0, 16); if (!bt_get_csrk(&dev->bdaddr, true, csrk, &sign_cnt, NULL)) { error("gatt: Could not get csrk key"); return 0; } res = gatt_signed_write_cmd(dev->attrib, handle, value, vlen, crypto, csrk, sign_cnt, NULL, NULL); if (!res) { error("gatt: Signed write command failed"); return 0; } bt_update_sign_counter(&dev->bdaddr, true, ++sign_cnt); return res; } static void handle_client_write_characteristic(const void *buf, uint16_t len) { const struct hal_cmd_gatt_client_write_characteristic *cmd = buf; struct char_op_data *cb_data = NULL; struct characteristic *ch; struct app_connection *conn; struct service *srvc; struct element_id srvc_id; struct element_id char_id; uint8_t status; guint res; DBG(""); if (len != sizeof(*cmd) + cmd->len) { error("Invalid write char size (%u bytes), terminating", len); raise(SIGTERM); return; } hal_srvc_id_to_element_id(&cmd->srvc_id, &srvc_id); hal_gatt_id_to_element_id(&cmd->char_id, &char_id); if (!find_service(cmd->conn_id, &srvc_id, &conn, &srvc)) { status = HAL_STATUS_FAILED; goto failed; } /* search characteristics by instance id */ ch = queue_find(srvc->chars, match_char_by_element_id, &char_id); if (!ch) { error("gatt: Characteristic with inst_id: %d not found", cmd->char_id.inst_id); status = HAL_STATUS_FAILED; goto failed; } if (cmd->write_type == GATT_WRITE_TYPE_PREPARE || cmd->write_type == GATT_WRITE_TYPE_DEFAULT) { cb_data = create_char_op_data(cmd->conn_id, &srvc->id, &ch->id, cmd->srvc_id.is_primary); } if (!set_auth_type(conn->device, cmd->auth_req)) { error("gatt: Failed to set security %d", cmd->auth_req); status = HAL_STATUS_FAILED; goto failed; } switch (cmd->write_type) { case GATT_WRITE_TYPE_NO_RESPONSE: res = gatt_write_cmd(conn->device->attrib, ch->ch.value_handle, cmd->value, cmd->len, NULL, NULL); break; case GATT_WRITE_TYPE_PREPARE: res = gatt_reliable_write_char(conn->device->attrib, ch->ch.value_handle, cmd->value, cmd->len, write_char_cb, cb_data); break; case GATT_WRITE_TYPE_DEFAULT: res = gatt_write_char(conn->device->attrib, ch->ch.value_handle, cmd->value, cmd->len, write_char_cb, cb_data); break; case GATT_WRITE_TYPE_SIGNED: if (get_cid(conn->device) != ATT_CID) { error("gatt: Cannot write signed on BR/EDR bearer"); status = HAL_STATUS_FAILED; goto failed; } if (get_sec_level(conn->device) > BT_SECURITY_LOW) res = gatt_write_cmd(conn->device->attrib, ch->ch.value_handle, cmd->value, cmd->len, NULL, NULL); else res = signed_write_cmd(conn->device, ch->ch.value_handle, cmd->value, cmd->len); break; default: error("gatt: Write type %d unsupported", cmd->write_type); status = HAL_STATUS_UNSUPPORTED; goto failed; } if (!res) { error("gatt: Cannot write char. with inst_id: %d", cmd->char_id.inst_id); status = HAL_STATUS_FAILED; goto failed; } status = HAL_STATUS_SUCCESS; failed: ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_GATT, HAL_OP_GATT_CLIENT_WRITE_CHARACTERISTIC, status); /* * We should send notification with service, characteristic id in case * of error and write with no response */ if (status != HAL_STATUS_SUCCESS || cmd->write_type == GATT_WRITE_TYPE_NO_RESPONSE || cmd->write_type == GATT_WRITE_TYPE_SIGNED) { int32_t gatt_status = (status == HAL_STATUS_SUCCESS) ? GATT_SUCCESS : GATT_FAILURE; send_client_write_char_notify(gatt_status, cmd->conn_id, &srvc_id, &char_id, cmd->srvc_id.is_primary); free(cb_data); } } static void send_client_descr_read_notify(int32_t status, const uint8_t *pdu, guint16 len, int32_t conn_id, const struct element_id *srvc, const struct element_id *ch, const struct element_id *descr, uint8_t primary) { uint8_t buf[IPC_MTU]; struct hal_ev_gatt_client_read_descriptor *ev = (void *) buf; memset(buf, 0, sizeof(buf)); ev->status = status; ev->conn_id = conn_id; ev->data.status = ev->status; element_id_to_hal_srvc_id(srvc, primary, &ev->data.srvc_id); element_id_to_hal_gatt_id(ch, &ev->data.char_id); element_id_to_hal_gatt_id(descr, &ev->data.descr_id); if (status == 0 && pdu) { ssize_t ret; ret = dec_read_resp(pdu, len, ev->data.value, GATT_MAX_ATTR_LEN); if (ret < 0) { error("gatt: Protocol error"); ev->status = GATT_FAILURE; } else { ev->data.len = ret; } } ipc_send_notif(hal_ipc, HAL_SERVICE_ID_GATT, HAL_EV_GATT_CLIENT_READ_DESCRIPTOR, sizeof(*ev) + ev->data.len, ev); } struct desc_data { int32_t conn_id; const struct element_id *srvc_id; const struct element_id *char_id; const struct element_id *descr_id; uint8_t primary; }; static void read_desc_cb(guint8 status, const guint8 *pdu, guint16 len, gpointer user_data) { struct desc_data *cb_data = user_data; if (status != 0) error("gatt: Discover all char descriptors failed: %s", att_ecode2str(status)); send_client_descr_read_notify(status, pdu, len, cb_data->conn_id, cb_data->srvc_id, cb_data->char_id, cb_data->descr_id, cb_data->primary); free(cb_data); } static struct desc_data *create_desc_data(int32_t conn_id, const struct element_id *s_id, const struct element_id *ch_id, const struct element_id *d_id, uint8_t primary) { struct desc_data *d; d = new0(struct desc_data, 1); d->conn_id = conn_id; d->srvc_id = s_id; d->char_id = ch_id; d->descr_id = d_id; d->primary = primary; return d; } static void handle_client_read_descriptor(const void *buf, uint16_t len) { const struct hal_cmd_gatt_client_read_descriptor *cmd = buf; struct desc_data *cb_data; struct characteristic *ch; struct descriptor *descr; struct service *srvc; struct element_id char_id; struct element_id descr_id; struct element_id srvc_id; struct app_connection *conn; int32_t conn_id = 0; uint8_t primary; uint8_t status; DBG(""); conn_id = cmd->conn_id; primary = cmd->srvc_id.is_primary; hal_srvc_id_to_element_id(&cmd->srvc_id, &srvc_id); hal_gatt_id_to_element_id(&cmd->char_id, &char_id); hal_gatt_id_to_element_id(&cmd->descr_id, &descr_id); if (!find_service(conn_id, &srvc_id, &conn, &srvc)) { error("gatt: Read descr. could not find service"); status = HAL_STATUS_FAILED; goto failed; } ch = queue_find(srvc->chars, match_char_by_element_id, &char_id); if (!ch) { error("gatt: Read descr. could not find characteristic"); status = HAL_STATUS_FAILED; goto failed; } descr = queue_find(ch->descriptors, match_descr_by_element_id, &descr_id); if (!descr) { error("gatt: Read descr. could not find descriptor"); status = HAL_STATUS_FAILED; goto failed; } cb_data = create_desc_data(conn_id, &srvc->id, &ch->id, &descr->id, primary); if (!set_auth_type(conn->device, cmd->auth_req)) { error("gatt: Failed to set security %d", cmd->auth_req); status = HAL_STATUS_FAILED; free(cb_data); goto failed; } if (!gatt_read_char(conn->device->attrib, descr->handle, read_desc_cb, cb_data)) { free(cb_data); status = HAL_STATUS_FAILED; goto failed; } status = HAL_STATUS_SUCCESS; failed: if (status != HAL_STATUS_SUCCESS) send_client_descr_read_notify(GATT_FAILURE, NULL, 0, conn_id, &srvc_id, &char_id, &descr_id, primary); ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_GATT, HAL_OP_GATT_CLIENT_READ_DESCRIPTOR, status); } static void send_client_descr_write_notify(int32_t status, int32_t conn_id, const struct element_id *srvc, const struct element_id *ch, const struct element_id *descr, uint8_t primary) { uint8_t buf[IPC_MTU]; struct hal_ev_gatt_client_write_descriptor *ev = (void *) buf; memset(buf, 0, sizeof(buf)); ev->status = status; ev->conn_id = conn_id; element_id_to_hal_srvc_id(srvc, primary, &ev->data.srvc_id); element_id_to_hal_gatt_id(ch, &ev->data.char_id); element_id_to_hal_gatt_id(descr, &ev->data.descr_id); ev->data.status = status; ipc_send_notif(hal_ipc, HAL_SERVICE_ID_GATT, HAL_EV_GATT_CLIENT_WRITE_DESCRIPTOR, sizeof(*ev), ev); } static void write_descr_cb(guint8 status, const guint8 *pdu, guint16 len, gpointer user_data) { struct desc_data *cb_data = user_data; if (status) error("gatt: Write descriptors failed: %s", att_ecode2str(status)); send_client_descr_write_notify(status, cb_data->conn_id, cb_data->srvc_id, cb_data->char_id, cb_data->descr_id, cb_data->primary); free(cb_data); } static void handle_client_write_descriptor(const void *buf, uint16_t len) { const struct hal_cmd_gatt_client_write_descriptor *cmd = buf; struct desc_data *cb_data = NULL; struct characteristic *ch; struct descriptor *descr; struct service *srvc; struct element_id srvc_id; struct element_id char_id; struct element_id descr_id; struct app_connection *conn; int32_t conn_id; uint8_t primary; uint8_t status; guint res; DBG(""); if (len != sizeof(*cmd) + cmd->len) { error("Invalid write desriptor command (%u bytes), terminating", len); raise(SIGTERM); return; } primary = cmd->srvc_id.is_primary; conn_id = cmd->conn_id; hal_srvc_id_to_element_id(&cmd->srvc_id, &srvc_id); hal_gatt_id_to_element_id(&cmd->char_id, &char_id); hal_gatt_id_to_element_id(&cmd->descr_id, &descr_id); if (!find_service(cmd->conn_id, &srvc_id, &conn, &srvc)) { error("gatt: Write descr. could not find service"); status = HAL_STATUS_FAILED; goto failed; } ch = queue_find(srvc->chars, match_char_by_element_id, &char_id); if (!ch) { error("gatt: Write descr. could not find characteristic"); status = HAL_STATUS_FAILED; goto failed; } descr = queue_find(ch->descriptors, match_descr_by_element_id, &descr_id); if (!descr) { error("gatt: Write descr. could not find descriptor"); status = HAL_STATUS_FAILED; goto failed; } if (cmd->write_type != GATT_WRITE_TYPE_NO_RESPONSE) cb_data = create_desc_data(conn_id, &srvc->id, &ch->id, &descr->id, primary); if (!set_auth_type(conn->device, cmd->auth_req)) { error("gatt: Failed to set security %d", cmd->auth_req); status = HAL_STATUS_FAILED; goto failed; } switch (cmd->write_type) { case GATT_WRITE_TYPE_NO_RESPONSE: res = gatt_write_cmd(conn->device->attrib, descr->handle, cmd->value, cmd->len, NULL , NULL); break; case GATT_WRITE_TYPE_PREPARE: res = gatt_reliable_write_char(conn->device->attrib, descr->handle, cmd->value, cmd->len, write_descr_cb, cb_data); break; case GATT_WRITE_TYPE_DEFAULT: res = gatt_write_char(conn->device->attrib, descr->handle, cmd->value, cmd->len, write_descr_cb, cb_data); break; default: error("gatt: Write type %d unsupported", cmd->write_type); status = HAL_STATUS_UNSUPPORTED; goto failed; } if (!res) { error("gatt: Write desc, could not write desc"); status = HAL_STATUS_FAILED; goto failed; } status = HAL_STATUS_SUCCESS; failed: if (status != HAL_STATUS_SUCCESS || cmd->write_type == GATT_WRITE_TYPE_NO_RESPONSE) { int32_t gatt_status = (status == HAL_STATUS_SUCCESS) ? GATT_SUCCESS : GATT_FAILURE; send_client_descr_write_notify(gatt_status, conn_id, &srvc_id, &char_id, &descr_id, primary); free(cb_data); } ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_GATT, HAL_OP_GATT_CLIENT_WRITE_DESCRIPTOR, status); } static void send_client_write_execute_notify(int32_t id, int32_t status) { struct hal_ev_gatt_client_exec_write ev; ev.conn_id = id; ev.status = status; ipc_send_notif(hal_ipc, HAL_SERVICE_ID_GATT, HAL_EV_GATT_CLIENT_EXEC_WRITE, sizeof(ev), &ev); } static void write_execute_cb(guint8 status, const guint8 *pdu, guint16 len, gpointer user_data) { send_client_write_execute_notify(PTR_TO_INT(user_data), status); } static void handle_client_execute_write(const void *buf, uint16_t len) { const struct hal_cmd_gatt_client_execute_write *cmd = buf; struct app_connection *conn; uint8_t status; uint8_t flags; DBG(""); conn = find_connection_by_id(cmd->conn_id); if (!conn) { status = HAL_STATUS_FAILED; goto reply; } flags = cmd->execute ? ATT_WRITE_ALL_PREP_WRITES : ATT_CANCEL_ALL_PREP_WRITES; if (!gatt_execute_write(conn->device->attrib, flags, write_execute_cb, INT_TO_PTR(cmd->conn_id))) { error("gatt: Could not send execute write"); status = HAL_STATUS_FAILED; goto reply; } status = HAL_STATUS_SUCCESS; reply: ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_GATT, HAL_OP_GATT_CLIENT_EXECUTE_WRITE, status); /* In case of early error send also notification.*/ if (status != HAL_STATUS_SUCCESS) send_client_write_execute_notify(cmd->conn_id, GATT_FAILURE); } static void handle_notification(const uint8_t *pdu, uint16_t len, gpointer user_data) { uint8_t buf[IPC_MTU]; struct hal_ev_gatt_client_notify *ev = (void *) buf; struct notification_data *notification = user_data; uint8_t data_offset = sizeof(uint8_t) + sizeof(uint16_t); if (len < data_offset) return; memcpy(&ev->char_id, ¬ification->ch, sizeof(ev->char_id)); memcpy(&ev->srvc_id, ¬ification->service, sizeof(ev->srvc_id)); bdaddr2android(¬ification->conn->device->bdaddr, &ev->bda); ev->conn_id = notification->conn->id; ev->is_notify = pdu[0] == ATT_OP_HANDLE_NOTIFY; /* We have to cut opcode and handle from data */ ev->len = len - data_offset; memcpy(ev->value, pdu + data_offset, len - data_offset); ipc_send_notif(hal_ipc, HAL_SERVICE_ID_GATT, HAL_EV_GATT_CLIENT_NOTIFY, sizeof(*ev) + ev->len, ev); } static void send_register_for_notification_ev(int32_t id, int32_t registered, int32_t status, const struct hal_gatt_srvc_id *srvc, const struct hal_gatt_gatt_id *ch) { struct hal_ev_gatt_client_reg_for_notif ev; ev.conn_id = id; ev.status = status; ev.registered = registered; memcpy(&ev.srvc_id, srvc, sizeof(ev.srvc_id)); memcpy(&ev.char_id, ch, sizeof(ev.char_id)); ipc_send_notif(hal_ipc, HAL_SERVICE_ID_GATT, HAL_EV_GATT_CLIENT_REGISTER_FOR_NOTIF, sizeof(ev), &ev); } static void handle_client_register_for_notification(const void *buf, uint16_t len) { const struct hal_cmd_gatt_client_register_for_notification *cmd = buf; struct notification_data *notification; struct characteristic *c; struct element_id match_id; struct app_connection *conn; int32_t conn_id = 0; struct service *service; uint8_t status; int32_t gatt_status; bdaddr_t addr; DBG(""); android2bdaddr(&cmd->bdaddr, &addr); conn = find_conn(&addr, cmd->client_if); if (!conn) { status = HAL_STATUS_FAILED; goto failed; } conn_id = conn->id; hal_srvc_id_to_element_id(&cmd->srvc_id, &match_id); service = queue_find(conn->device->services, match_srvc_by_element_id, &match_id); if (!service) { status = HAL_STATUS_FAILED; goto failed; } hal_gatt_id_to_element_id(&cmd->char_id, &match_id); c = queue_find(service->chars, match_char_by_element_id, &match_id); if (!c) { status = HAL_STATUS_FAILED; goto failed; } notification = new0(struct notification_data, 1); _Pragma("GCC diagnostic push") _Pragma("GCC diagnostic ignored \"-Warray-bounds\"") _Pragma("GCC diagnostic ignored \"-Wstringop-overflow\"") memcpy(¬ification->ch, &cmd->char_id, sizeof(notification->ch)); _Pragma("GCC diagnostic pop") memcpy(¬ification->service, &cmd->srvc_id, sizeof(notification->service)); notification->conn = conn; if (queue_find(conn->app->notifications, match_notification, notification)) { free(notification); status = HAL_STATUS_SUCCESS; goto failed; } notification->notif_id = g_attrib_register(conn->device->attrib, ATT_OP_HANDLE_NOTIFY, c->ch.value_handle, handle_notification, notification, destroy_notification); if (!notification->notif_id) { free(notification); status = HAL_STATUS_FAILED; goto failed; } notification->ind_id = g_attrib_register(conn->device->attrib, ATT_OP_HANDLE_IND, c->ch.value_handle, handle_notification, notification, destroy_notification); if (!notification->ind_id) { g_attrib_unregister(conn->device->attrib, notification->notif_id); free(notification); status = HAL_STATUS_FAILED; goto failed; } /* * Because same data - notification - is shared by two handlers, we * introduce ref counter to be sure that data can be freed with no risk. * Counter is decremented in destroy_notification. */ notification->ref = 2; queue_push_tail(conn->app->notifications, notification); status = HAL_STATUS_SUCCESS; failed: gatt_status = status ? GATT_FAILURE : GATT_SUCCESS; send_register_for_notification_ev(conn_id, 1, gatt_status, &cmd->srvc_id, &cmd->char_id); ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_GATT, HAL_OP_GATT_CLIENT_REGISTER_FOR_NOTIFICATION, status); } static void handle_client_deregister_for_notification(const void *buf, uint16_t len) { const struct hal_cmd_gatt_client_deregister_for_notification *cmd = buf; struct notification_data *notification, notif; struct app_connection *conn; int32_t conn_id = 0; uint8_t status; int32_t gatt_status; bdaddr_t addr; DBG(""); android2bdaddr(&cmd->bdaddr, &addr); conn = find_conn(&addr, cmd->client_if); if (!conn) { status = HAL_STATUS_FAILED; goto failed; } conn_id = conn->id; memcpy(¬if.ch, &cmd->char_id, sizeof(notif.ch)); memcpy(¬if.service, &cmd->srvc_id, sizeof(notif.service)); notif.conn = conn; notification = queue_find(conn->app->notifications, match_notification, ¬if); if (!notification) { status = HAL_STATUS_FAILED; goto failed; } unregister_notification(notification); status = HAL_STATUS_SUCCESS; failed: gatt_status = status ? GATT_FAILURE : GATT_SUCCESS; send_register_for_notification_ev(conn_id, 0, gatt_status, &cmd->srvc_id, &cmd->char_id); ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_GATT, HAL_OP_GATT_CLIENT_DEREGISTER_FOR_NOTIFICATION, status); } static void send_client_remote_rssi_notify(int32_t client_if, const bdaddr_t *addr, int32_t rssi, int32_t status) { struct hal_ev_gatt_client_read_remote_rssi ev; ev.client_if = client_if; bdaddr2android(addr, &ev.address); ev.rssi = rssi; ev.status = status; ipc_send_notif(hal_ipc, HAL_SERVICE_ID_GATT, HAL_EV_GATT_CLIENT_READ_REMOTE_RSSI, sizeof(ev), &ev); } static void read_remote_rssi_cb(uint8_t status, const bdaddr_t *addr, int8_t rssi, void *user_data) { int32_t client_if = PTR_TO_INT(user_data); int32_t gatt_status = status ? GATT_FAILURE : GATT_SUCCESS; send_client_remote_rssi_notify(client_if, addr, rssi, gatt_status); } static void handle_client_read_remote_rssi(const void *buf, uint16_t len) { const struct hal_cmd_gatt_client_read_remote_rssi *cmd = buf; uint8_t status; bdaddr_t bdaddr; DBG(""); if (!find_app_by_id(cmd->client_if)) { status = HAL_STATUS_FAILED; goto failed; } android2bdaddr(cmd->bdaddr, &bdaddr); if (!bt_read_device_rssi(&bdaddr, read_remote_rssi_cb, INT_TO_PTR(cmd->client_if))) { error("gatt: Could not read RSSI"); status = HAL_STATUS_FAILED; goto failed; } status = HAL_STATUS_SUCCESS; failed: ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_GATT, HAL_OP_GATT_CLIENT_READ_REMOTE_RSSI, status); if (status != HAL_STATUS_SUCCESS) send_client_remote_rssi_notify(cmd->client_if, &bdaddr, 0, GATT_FAILURE); } static void handle_client_get_device_type(const void *buf, uint16_t len) { const struct hal_cmd_gatt_client_get_device_type *cmd = buf; struct hal_rsp_gatt_client_get_device_type rsp; bdaddr_t bdaddr; DBG(""); android2bdaddr(cmd->bdaddr, &bdaddr); rsp.type = bt_get_device_android_type(&bdaddr); ipc_send_rsp_full(hal_ipc, HAL_SERVICE_ID_GATT, HAL_OP_GATT_CLIENT_GET_DEVICE_TYPE, sizeof(rsp), &rsp, -1); } static void handle_client_set_adv_data(const void *buf, uint16_t len) { const struct hal_cmd_gatt_client_set_adv_data *cmd = buf; uint8_t status; if (len != sizeof(*cmd) + cmd->manufacturer_len) { error("Invalid set adv data command (%u bytes), terminating", len); raise(SIGTERM); return; } DBG("scan_rsp=%u name=%u tx=%u min=%d max=%d app=%d", cmd->set_scan_rsp, cmd->include_name, cmd->include_txpower, cmd->min_interval, cmd->max_interval, cmd->appearance); DBG("manufacturer=%u service_data=%u service_uuid=%u", cmd->manufacturer_len, cmd->service_data_len, cmd->service_uuid_len); /* TODO This should be implemented when kernel supports it */ if (cmd->manufacturer_len || cmd->service_data_len || cmd->service_uuid_len) { error("gatt: Extra advertising data not supported"); status = HAL_STATUS_FAILED; goto failed; } status = HAL_STATUS_SUCCESS; failed: ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_GATT, HAL_OP_GATT_CLIENT_SET_ADV_DATA, status); } static void test_command_result(guint8 status, const guint8 *pdu, guint16 len, gpointer user_data) { DBG("status: %d", status); } static uint8_t test_read_write(bdaddr_t *bdaddr, bt_uuid_t *uuid, uint16_t op, uint16_t u2, uint16_t u3, uint16_t u4, uint16_t u5) { guint16 length = 0; struct gatt_device *dev; uint8_t *pdu; size_t mtu; dev = find_device_by_addr(bdaddr); if (!dev || dev->state != DEVICE_CONNECTED) return HAL_STATUS_FAILED; pdu = g_attrib_get_buffer(dev->attrib, &mtu); if (!pdu) return HAL_STATUS_FAILED; switch (op) { case ATT_OP_READ_REQ: length = enc_read_req(u2, pdu, mtu); break; case ATT_OP_READ_BY_TYPE_REQ: length = enc_read_by_type_req(u2, u3, uuid, pdu, mtu); break; case ATT_OP_READ_BLOB_REQ: length = enc_read_blob_req(u2, u3, pdu, mtu); break; case ATT_OP_READ_BY_GROUP_REQ: length = enc_read_by_grp_req(u2, u3, uuid, pdu, mtu); break; case ATT_OP_READ_MULTI_REQ: return HAL_STATUS_UNSUPPORTED; case ATT_OP_WRITE_REQ: length = enc_write_req(u2, (uint8_t *) &u3, sizeof(u3), pdu, mtu); break; case ATT_OP_WRITE_CMD: length = enc_write_cmd(u2, (uint8_t *) &u3, sizeof(u3), pdu, mtu); break; case ATT_OP_PREP_WRITE_REQ: length = enc_prep_write_req(u2, u3, (uint8_t *) &u4, sizeof(u4), pdu, mtu); break; case ATT_OP_EXEC_WRITE_REQ: length = enc_exec_write_req(u2, pdu, mtu); break; case ATT_OP_SIGNED_WRITE_CMD: if (signed_write_cmd(dev, u2, (uint8_t *) &u3, sizeof(u3))) return HAL_STATUS_SUCCESS; else return HAL_STATUS_FAILED; default: error("gatt: Unknown operation type"); return HAL_STATUS_UNSUPPORTED; } if (!g_attrib_send(dev->attrib, 0, pdu, length, test_command_result, NULL, NULL)) return HAL_STATUS_FAILED; return HAL_STATUS_SUCCESS; } static uint8_t test_increase_security(bdaddr_t *bdaddr, uint16_t u1) { struct gatt_device *device; device = find_device_by_addr(bdaddr); if (!device) return HAL_STATUS_FAILED; if (!set_auth_type(device, u1)) return HAL_STATUS_FAILED; return HAL_STATUS_SUCCESS; } static void handle_client_test_command(const void *buf, uint16_t len) { const struct hal_cmd_gatt_client_test_command *cmd = buf; struct gatt_app *app; bdaddr_t bdaddr; bt_uuid_t uuid; uint8_t status; DBG(""); android2bdaddr(cmd->bda1, &bdaddr); android2uuid(cmd->uuid1, &uuid); switch (cmd->command) { case GATT_CLIENT_TEST_CMD_ENABLE: if (cmd->u1) { if (!test_client_if) { app = register_app(TEST_UUID, GATT_CLIENT); if (app) test_client_if = app->id; } if (test_client_if) status = HAL_STATUS_SUCCESS; else status = HAL_STATUS_FAILED; } else { status = unregister_app(test_client_if); test_client_if = 0; } break; case GATT_CLIENT_TEST_CMD_CONNECT: /* TODO u1 holds device type, for now assume BLE */ status = handle_connect(test_client_if, &bdaddr, false); break; case GATT_CLIENT_TEST_CMD_DISCONNECT: app = queue_find(gatt_apps, match_app_by_id, INT_TO_PTR(test_client_if)); queue_remove_all(app_connections, match_connection_by_app, app, destroy_connection); status = HAL_STATUS_SUCCESS; break; case GATT_CLIENT_TEST_CMD_DISCOVER: status = HAL_STATUS_FAILED; break; case GATT_CLIENT_TEST_CMD_READ: case GATT_CLIENT_TEST_CMD_WRITE: status = test_read_write(&bdaddr, &uuid, cmd->u1, cmd->u2, cmd->u3, cmd->u4, cmd->u5); break; case GATT_CLIENT_TEST_CMD_INCREASE_SECURITY: status = test_increase_security(&bdaddr, cmd->u1); break; case GATT_CLIENT_TEST_CMD_PAIRING_CONFIG: default: status = HAL_STATUS_FAILED; break; } ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_GATT, HAL_OP_GATT_CLIENT_TEST_COMMAND, status); } static void handle_server_register(const void *buf, uint16_t len) { const struct hal_cmd_gatt_server_register *cmd = buf; struct hal_ev_gatt_server_register ev; struct gatt_app *app; DBG(""); memset(&ev, 0, sizeof(ev)); app = register_app(cmd->uuid, GATT_SERVER); if (app) { ev.server_if = app->id; ev.status = GATT_SUCCESS; } else { ev.status = GATT_FAILURE; } memcpy(ev.uuid, cmd->uuid, sizeof(ev.uuid)); ipc_send_notif(hal_ipc, HAL_SERVICE_ID_GATT, HAL_EV_GATT_SERVER_REGISTER, sizeof(ev), &ev); ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_GATT, HAL_OP_GATT_SERVER_REGISTER, HAL_STATUS_SUCCESS); } static void handle_server_unregister(const void *buf, uint16_t len) { const struct hal_cmd_gatt_server_unregister *cmd = buf; uint8_t status; DBG(""); status = unregister_app(cmd->server_if); ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_GATT, HAL_OP_GATT_SERVER_UNREGISTER, status); } static void handle_server_connect(const void *buf, uint16_t len) { const struct hal_cmd_gatt_server_connect *cmd = buf; uint8_t status; bdaddr_t addr; DBG(""); android2bdaddr(&cmd->bdaddr, &addr); /* TODO: Handle transport flag */ status = handle_connect(cmd->server_if, &addr, cmd->is_direct); ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_GATT, HAL_OP_GATT_SERVER_CONNECT, status); } static void handle_server_disconnect(const void *buf, uint16_t len) { const struct hal_cmd_gatt_server_disconnect *cmd = buf; struct app_connection *conn; uint8_t status; DBG(""); /* TODO: should we care to match also bdaddr when conn_id is unique? */ conn = queue_remove_if(app_connections, match_connection_by_id, INT_TO_PTR(cmd->conn_id)); destroy_connection(conn); status = HAL_STATUS_SUCCESS; ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_GATT, HAL_OP_GATT_SERVER_DISCONNECT, status); } static void handle_server_add_service(const void *buf, uint16_t len) { const struct hal_cmd_gatt_server_add_service *cmd = buf; struct hal_ev_gatt_server_service_added ev; struct gatt_app *server; struct gatt_db_attribute *service; uint8_t status; bt_uuid_t uuid; DBG(""); memset(&ev, 0, sizeof(ev)); server = find_app_by_id(cmd->server_if); if (!server) { status = HAL_STATUS_FAILED; goto failed; } android2uuid(cmd->srvc_id.uuid, &uuid); service = gatt_db_add_service(gatt_db, &uuid, cmd->srvc_id.is_primary, cmd->num_handles); if (!service) { status = HAL_STATUS_FAILED; goto failed; } ev.srvc_handle = gatt_db_attribute_get_handle(service); if (!ev.srvc_handle) { status = HAL_STATUS_FAILED; goto failed; } status = HAL_STATUS_SUCCESS; failed: ev.status = status == HAL_STATUS_SUCCESS ? GATT_SUCCESS : GATT_FAILURE; ev.srvc_id = cmd->srvc_id; ev.server_if = cmd->server_if; ipc_send_notif(hal_ipc, HAL_SERVICE_ID_GATT, HAL_EV_GATT_SERVER_SERVICE_ADDED, sizeof(ev), &ev); ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_GATT, HAL_OP_GATT_SERVER_ADD_SERVICE, status); } static void handle_server_add_included_service(const void *buf, uint16_t len) { const struct hal_cmd_gatt_server_add_inc_service *cmd = buf; struct hal_ev_gatt_server_inc_srvc_added ev; struct gatt_app *server; struct gatt_db_attribute *service, *include; uint8_t status; DBG(""); memset(&ev, 0, sizeof(ev)); server = find_app_by_id(cmd->server_if); if (!server) { status = HAL_STATUS_FAILED; goto failed; } service = gatt_db_get_attribute(gatt_db, cmd->service_handle); if (!service) { status = HAL_STATUS_FAILED; goto failed; } include = gatt_db_get_attribute(gatt_db, cmd->included_handle); if (!include) { status = HAL_STATUS_FAILED; goto failed; } service = gatt_db_service_add_included(service, include); if (!service) { status = HAL_STATUS_FAILED; goto failed; } ev.incl_srvc_handle = gatt_db_attribute_get_handle(service); status = HAL_STATUS_SUCCESS; failed: ev.srvc_handle = cmd->service_handle; ev.status = status; ev.server_if = cmd->server_if; ev.status = status == HAL_STATUS_SUCCESS ? GATT_SUCCESS : GATT_FAILURE; ipc_send_notif(hal_ipc, HAL_SERVICE_ID_GATT, HAL_EV_GATT_SERVER_INC_SRVC_ADDED, sizeof(ev), &ev); ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_GATT, HAL_OP_GATT_SERVER_ADD_INC_SERVICE, status); } static bool is_service(const bt_uuid_t *type) { bt_uuid_t uuid; bt_uuid16_create(&uuid, GATT_PRIM_SVC_UUID); if (!bt_uuid_cmp(&uuid, type)) return true; bt_uuid16_create(&uuid, GATT_SND_SVC_UUID); if (!bt_uuid_cmp(&uuid, type)) return true; return false; } static bool match_pending_dev_request(const void *data, const void *user_data) { const struct pending_request *pending_request = data; return !pending_request->completed; } static void send_dev_complete_response(struct gatt_device *device, uint8_t opcode) { size_t mtu; uint8_t *rsp = g_attrib_get_buffer(device->attrib, &mtu); struct pending_request *val; uint16_t len = 0; uint8_t error = 0; if (queue_isempty(device->pending_requests)) return; if (queue_find(device->pending_requests, match_pending_dev_request, NULL)) { DBG("Still pending requests"); return; } val = queue_peek_head(device->pending_requests); if (!val) { error = ATT_ECODE_ATTR_NOT_FOUND; goto done; } if (val->error) { error = val->error; goto done; } switch (opcode) { case ATT_OP_READ_BY_TYPE_REQ: { struct att_data_list *adl; int iterator = 0; int length; struct queue *temp; temp = queue_new(); val = queue_pop_head(device->pending_requests); if (!val) { queue_destroy(temp, NULL); error = ATT_ECODE_ATTR_NOT_FOUND; goto done; } if (val->error) { queue_destroy(temp, NULL); error = val->error; destroy_pending_request(val); goto done; } length = val->length; while (val && val->length == length && val->error == 0) { queue_push_tail(temp, val); val = queue_pop_head(device->pending_requests); } adl = att_data_list_alloc(queue_length(temp), sizeof(uint16_t) + length); destroy_pending_request(val); val = queue_pop_head(temp); while (val) { uint8_t *value = adl->data[iterator++]; uint16_t handle; handle = gatt_db_attribute_get_handle(val->attrib); put_le16(handle, value); memcpy(&value[2], val->value, val->length); destroy_pending_request(val); val = queue_pop_head(temp); } len = enc_read_by_type_resp(adl, rsp, mtu); att_data_list_free(adl); queue_destroy(temp, destroy_pending_request); break; } case ATT_OP_READ_BLOB_REQ: len = enc_read_blob_resp(val->value, val->length, val->offset, rsp, mtu); break; case ATT_OP_READ_REQ: len = enc_read_resp(val->value, val->length, rsp, mtu); break; case ATT_OP_READ_BY_GROUP_REQ: { struct att_data_list *adl; int iterator = 0; int length; struct queue *temp; temp = queue_new(); val = queue_pop_head(device->pending_requests); if (!val) { queue_destroy(temp, NULL); error = ATT_ECODE_ATTR_NOT_FOUND; goto done; } length = val->length; while (val && val->length == length) { queue_push_tail(temp, val); val = queue_pop_head(device->pending_requests); } adl = att_data_list_alloc(queue_length(temp), 2 * sizeof(uint16_t) + length); val = queue_pop_head(temp); while (val) { uint8_t *value = adl->data[iterator++]; uint16_t start_handle, end_handle; gatt_db_attribute_get_service_handles(val->attrib, &start_handle, &end_handle); put_le16(start_handle, value); put_le16(end_handle, &value[2]); memcpy(&value[4], val->value, val->length); destroy_pending_request(val); val = queue_pop_head(temp); } len = enc_read_by_grp_resp(adl, rsp, mtu); att_data_list_free(adl); queue_destroy(temp, destroy_pending_request); break; } case ATT_OP_FIND_BY_TYPE_REQ: { GSList *list = NULL; val = queue_pop_head(device->pending_requests); while (val) { struct att_range *range; const bt_uuid_t *type; /* Its find by type and value - filter by value here */ if ((val->length != val->filter_vlen) || memcmp(val->value, val->filter_value, val->length)) { destroy_pending_request(val); val = queue_pop_head(device->pending_requests); continue; } range = new0(struct att_range, 1); range->start = gatt_db_attribute_get_handle( val->attrib); type = gatt_db_attribute_get_type(val->attrib); if (is_service(type)) gatt_db_attribute_get_service_handles( val->attrib, NULL, &range->end); else range->end = range->start; list = g_slist_append(list, range); destroy_pending_request(val); val = queue_pop_head(device->pending_requests); } if (list && !error) len = enc_find_by_type_resp(list, rsp, mtu); else error = ATT_ECODE_ATTR_NOT_FOUND; g_slist_free_full(list, free); break; } case ATT_OP_EXEC_WRITE_REQ: len = enc_exec_write_resp(rsp); break; case ATT_OP_WRITE_REQ: len = enc_write_resp(rsp); break; case ATT_OP_PREP_WRITE_REQ: { uint16_t handle; handle = gatt_db_attribute_get_handle(val->attrib); len = enc_prep_write_resp(handle, val->offset, val->value, val->length, rsp, mtu); break; } default: break; } done: if (!len) len = enc_error_resp(opcode, 0x0000, error, rsp, mtu); g_attrib_send(device->attrib, 0, rsp, len, NULL, NULL, NULL); queue_remove_all(device->pending_requests, NULL, NULL, destroy_pending_request); } struct request_processing_data { uint8_t opcode; struct gatt_device *device; }; static uint8_t check_device_permissions(struct gatt_device *device, uint8_t opcode, uint32_t permissions) { GIOChannel *io; int sec_level; io = g_attrib_get_channel(device->attrib); if (!bt_io_get(io, NULL, BT_IO_OPT_SEC_LEVEL, &sec_level, BT_IO_OPT_INVALID)) return ATT_ECODE_UNLIKELY; DBG("opcode 0x%02x permissions %u sec_level %u", opcode, permissions, sec_level); switch (opcode) { case ATT_OP_SIGNED_WRITE_CMD: if (!(permissions & GATT_PERM_WRITE_SIGNED)) return ATT_ECODE_WRITE_NOT_PERM; if (permissions & GATT_PERM_WRITE_SIGNED_MITM) { bool auth; if (bt_get_csrk(&device->bdaddr, true, NULL, NULL, &auth) && auth) break; return ATT_ECODE_AUTHENTICATION; } break; case ATT_OP_READ_BY_TYPE_REQ: case ATT_OP_READ_REQ: case ATT_OP_READ_BLOB_REQ: case ATT_OP_READ_MULTI_REQ: case ATT_OP_READ_BY_GROUP_REQ: case ATT_OP_FIND_BY_TYPE_REQ: case ATT_OP_FIND_INFO_REQ: if (!(permissions & GATT_PERM_READ)) return ATT_ECODE_READ_NOT_PERM; if ((permissions & GATT_PERM_READ_MITM) && sec_level < BT_SECURITY_HIGH) return ATT_ECODE_AUTHENTICATION; if ((permissions & GATT_PERM_READ_ENCRYPTED) && sec_level < BT_SECURITY_MEDIUM) return ATT_ECODE_INSUFF_ENC; if (permissions & GATT_PERM_READ_AUTHORIZATION) return ATT_ECODE_AUTHORIZATION; break; case ATT_OP_WRITE_REQ: case ATT_OP_WRITE_CMD: case ATT_OP_PREP_WRITE_REQ: case ATT_OP_EXEC_WRITE_REQ: if (!(permissions & GATT_PERM_WRITE)) return ATT_ECODE_WRITE_NOT_PERM; if ((permissions & GATT_PERM_WRITE_MITM) && sec_level < BT_SECURITY_HIGH) return ATT_ECODE_AUTHENTICATION; if ((permissions & GATT_PERM_WRITE_ENCRYPTED) && sec_level < BT_SECURITY_MEDIUM) return ATT_ECODE_INSUFF_ENC; if (permissions & GATT_PERM_WRITE_AUTHORIZATION) return ATT_ECODE_AUTHORIZATION; break; default: return ATT_ECODE_UNLIKELY; } return 0; } static uint8_t err_to_att(int err) { if (!err || (err > 0 && err < UINT8_MAX)) return err; switch (err) { case -ENOENT: return ATT_ECODE_INVALID_HANDLE; case -ENOMEM: return ATT_ECODE_INSUFF_RESOURCES; default: return ATT_ECODE_UNLIKELY; } } static void attribute_read_cb(struct gatt_db_attribute *attrib, int err, const uint8_t *value, size_t length, void *user_data) { struct pending_request *resp_data = user_data; uint8_t error = err_to_att(err); resp_data->attrib = attrib; resp_data->length = length; resp_data->error = error; resp_data->completed = true; if (!length) return; resp_data->value = malloc0(length); if (!resp_data->value) { resp_data->error = ATT_ECODE_INSUFF_RESOURCES; return; } memcpy(resp_data->value, value, length); } static void read_requested_attributes(void *data, void *user_data) { struct pending_request *resp_data = data; struct request_processing_data *process_data = user_data; struct bt_att *att = g_attrib_get_att(process_data->device->attrib); struct gatt_db_attribute *attrib; uint32_t permissions; uint8_t error; attrib = resp_data->attrib; if (!attrib) { resp_data->error = ATT_ECODE_ATTR_NOT_FOUND; resp_data->completed = true; return; } permissions = gatt_db_attribute_get_permissions(attrib); /* * Check if it is attribute we didn't declare permissions, like service * declaration or included service. Set permissions to read only */ if (permissions == 0) permissions = GATT_PERM_READ; error = check_device_permissions(process_data->device, process_data->opcode, permissions); if (error != 0) { resp_data->error = error; resp_data->completed = true; return; } gatt_db_attribute_read(attrib, resp_data->offset, process_data->opcode, att, attribute_read_cb, resp_data); } static void process_dev_pending_requests(struct gatt_device *device, uint8_t att_opcode) { struct request_processing_data process_data; if (queue_isempty(device->pending_requests)) return; process_data.device = device; process_data.opcode = att_opcode; /* Process pending requests and prepare response */ queue_foreach(device->pending_requests, read_requested_attributes, &process_data); send_dev_complete_response(device, att_opcode); } static struct pending_trans_data *conn_add_transact(struct app_connection *conn, uint8_t opcode, struct gatt_db_attribute *attrib, unsigned int serial_id) { struct pending_trans_data *transaction; static int32_t trans_id = 1; transaction = new0(struct pending_trans_data, 1); transaction->id = trans_id++; transaction->opcode = opcode; transaction->attrib = attrib; transaction->serial_id = serial_id; queue_push_tail(conn->transactions, transaction); return transaction; } static bool get_dst_addr(struct bt_att *att, bdaddr_t *dst) { GIOChannel *io = NULL; GError *gerr = NULL; io = g_io_channel_unix_new(bt_att_get_fd(att)); if (!io) return false; bt_io_get(io, &gerr, BT_IO_OPT_DEST_BDADDR, dst, BT_IO_OPT_INVALID); if (gerr) { error("gatt: bt_io_get: %s", gerr->message); g_error_free(gerr); g_io_channel_unref(io); return false; } g_io_channel_unref(io); return true; } static void read_cb(struct gatt_db_attribute *attrib, unsigned int id, uint16_t offset, uint8_t opcode, struct bt_att *att, void *user_data) { struct pending_trans_data *transaction; struct hal_ev_gatt_server_request_read ev; struct gatt_app *app; struct app_connection *conn; int32_t app_id = PTR_TO_INT(user_data); bdaddr_t bdaddr; DBG("id %u", id); app = find_app_by_id(app_id); if (!app) { error("gatt: read_cb, cound not found app id"); goto failed; } if (!get_dst_addr(att, &bdaddr)) { error("gatt: read_cb, could not obtain dst BDADDR"); goto failed; } conn = find_conn(&bdaddr, app->id); if (!conn) { error("gatt: read_cb, cound not found connection"); goto failed; } memset(&ev, 0, sizeof(ev)); /* Store the request data, complete callback and transaction id */ transaction = conn_add_transact(conn, opcode, attrib, id); bdaddr2android(&bdaddr, ev.bdaddr); ev.conn_id = conn->id; ev.attr_handle = gatt_db_attribute_get_handle(attrib); ev.offset = offset; ev.is_long = opcode == ATT_OP_READ_BLOB_REQ; ev.trans_id = transaction->id; ipc_send_notif(hal_ipc, HAL_SERVICE_ID_GATT, HAL_EV_GATT_SERVER_REQUEST_READ, sizeof(ev), &ev); return; failed: gatt_db_attribute_read_result(attrib, id, -ENOENT, NULL, 0); } static void write_cb(struct gatt_db_attribute *attrib, unsigned int id, uint16_t offset, const uint8_t *value, size_t len, uint8_t opcode, struct bt_att *att, void *user_data) { uint8_t buf[IPC_MTU]; struct hal_ev_gatt_server_request_write *ev = (void *) buf; struct pending_trans_data *transaction; struct gatt_app *app; int32_t app_id = PTR_TO_INT(user_data); struct app_connection *conn; bdaddr_t bdaddr; DBG("id %u", id); app = find_app_by_id(app_id); if (!app) { error("gatt: write_cb could not found app id"); goto failed; } if (!get_dst_addr(att, &bdaddr)) { error("gatt: write_cb, could not obtain dst BDADDR"); goto failed; } conn = find_conn(&bdaddr, app->id); if (!conn) { error("gatt: write_cb could not found connection"); goto failed; } /* * Remember that this application has ongoing prep write * Need it later to find out where to send execute write */ if (opcode == ATT_OP_PREP_WRITE_REQ) conn->wait_execute_write = true; /* Store the request data, complete callback and transaction id */ transaction = conn_add_transact(conn, opcode, attrib, id); memset(ev, 0, sizeof(*ev)); bdaddr2android(&bdaddr, &ev->bdaddr); ev->attr_handle = gatt_db_attribute_get_handle(attrib); ev->offset = offset; ev->conn_id = conn->id; ev->trans_id = transaction->id; ev->is_prep = opcode == ATT_OP_PREP_WRITE_REQ; if (opcode == ATT_OP_WRITE_REQ || opcode == ATT_OP_PREP_WRITE_REQ) ev->need_rsp = 0x01; else gatt_db_attribute_write_result(attrib, id, 0); ev->length = len; memcpy(ev->value, value, len); ipc_send_notif(hal_ipc, HAL_SERVICE_ID_GATT, HAL_EV_GATT_SERVER_REQUEST_WRITE, sizeof(*ev) + ev->length , ev); return; failed: gatt_db_attribute_write_result(attrib, id, ATT_ECODE_UNLIKELY); } static uint32_t android_to_gatt_permissions(int32_t hal_permissions) { uint32_t permissions = 0; if (hal_permissions & HAL_GATT_PERMISSION_READ) permissions |= GATT_PERM_READ; if (hal_permissions & HAL_GATT_PERMISSION_READ_ENCRYPTED) permissions |= GATT_PERM_READ_ENCRYPTED | GATT_PERM_READ; if (hal_permissions & HAL_GATT_PERMISSION_READ_ENCRYPTED_MITM) permissions |= GATT_PERM_READ_MITM | GATT_PERM_READ_ENCRYPTED | GATT_PERM_READ; if (hal_permissions & HAL_GATT_PERMISSION_WRITE) permissions |= GATT_PERM_WRITE; if (hal_permissions & HAL_GATT_PERMISSION_WRITE_ENCRYPTED) permissions |= GATT_PERM_WRITE_ENCRYPTED | GATT_PERM_WRITE; if (hal_permissions & HAL_GATT_PERMISSION_WRITE_ENCRYPTED_MITM) permissions |= GATT_PERM_WRITE_MITM | GATT_PERM_WRITE_ENCRYPTED | GATT_PERM_WRITE; if (hal_permissions & HAL_GATT_PERMISSION_WRITE_SIGNED) permissions |= GATT_PERM_WRITE_SIGNED; if (hal_permissions & HAL_GATT_PERMISSION_WRITE_SIGNED_MITM) permissions |= GATT_PERM_WRITE_SIGNED_MITM | GATT_PERM_WRITE_SIGNED; return permissions; } static void handle_server_add_characteristic(const void *buf, uint16_t len) { const struct hal_cmd_gatt_server_add_characteristic *cmd = buf; struct hal_ev_gatt_server_characteristic_added ev; struct gatt_app *server; struct gatt_db_attribute *attrib; bt_uuid_t uuid; uint8_t status; uint32_t permissions; int32_t app_id = cmd->server_if; DBG(""); memset(&ev, 0, sizeof(ev)); server = find_app_by_id(app_id); if (!server) { status = HAL_STATUS_FAILED; goto failed; } attrib = gatt_db_get_attribute(gatt_db, cmd->service_handle); if (!attrib) { status = HAL_STATUS_FAILED; goto failed; } android2uuid(cmd->uuid, &uuid); permissions = android_to_gatt_permissions(cmd->permissions); attrib = gatt_db_service_add_characteristic(attrib, &uuid, permissions, cmd->properties, read_cb, write_cb, INT_TO_PTR(app_id)); if (!attrib) { status = HAL_STATUS_FAILED; goto failed; } ev.char_handle = gatt_db_attribute_get_handle(attrib); status = HAL_STATUS_SUCCESS; failed: ev.srvc_handle = cmd->service_handle; ev.status = status; ev.server_if = app_id; ev.status = status == HAL_STATUS_SUCCESS ? GATT_SUCCESS : GATT_FAILURE; memcpy(ev.uuid, cmd->uuid, sizeof(cmd->uuid)); ipc_send_notif(hal_ipc, HAL_SERVICE_ID_GATT, HAL_EV_GATT_SERVER_CHAR_ADDED, sizeof(ev), &ev); ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_GATT, HAL_OP_GATT_SERVER_ADD_CHARACTERISTIC, status); } static void handle_server_add_descriptor(const void *buf, uint16_t len) { const struct hal_cmd_gatt_server_add_descriptor *cmd = buf; struct hal_ev_gatt_server_descriptor_added ev; struct gatt_app *server; struct gatt_db_attribute *attrib; bt_uuid_t uuid; uint8_t status; uint32_t permissions; int32_t app_id = cmd->server_if; DBG(""); memset(&ev, 0, sizeof(ev)); server = find_app_by_id(app_id); if (!server) { status = HAL_STATUS_FAILED; goto failed; } android2uuid(cmd->uuid, &uuid); permissions = android_to_gatt_permissions(cmd->permissions); attrib = gatt_db_get_attribute(gatt_db, cmd->service_handle); if (!attrib) { status = HAL_STATUS_FAILED; goto failed; } attrib = gatt_db_service_add_descriptor(attrib, &uuid, permissions, read_cb, write_cb, INT_TO_PTR(app_id)); if (!attrib) { status = HAL_STATUS_FAILED; goto failed; } ev.descr_handle = gatt_db_attribute_get_handle(attrib); status = HAL_STATUS_SUCCESS; failed: ev.server_if = app_id; ev.srvc_handle = cmd->service_handle; memcpy(ev.uuid, cmd->uuid, sizeof(cmd->uuid)); ev.status = status == HAL_STATUS_SUCCESS ? GATT_SUCCESS : GATT_FAILURE; ipc_send_notif(hal_ipc, HAL_SERVICE_ID_GATT, HAL_EV_GATT_SERVER_DESCRIPTOR_ADDED, sizeof(ev), &ev); ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_GATT, HAL_OP_GATT_SERVER_ADD_DESCRIPTOR, status); } static void notify_service_change(void *data, void *user_data) { struct att_range range; struct gatt_db_attribute *attrib = user_data; gatt_db_attribute_get_service_handles(attrib, &range.start, &range.end); /* In case of db error */ if (!range.end) return; notify_att_range_change(data, &range); } static sdp_record_t *get_sdp_record(uuid_t *uuid, uint16_t start, uint16_t end, const char *name) { sdp_list_t *svclass_id, *apseq, *proto[2], *root, *aproto; uuid_t root_uuid, proto_uuid, l2cap; sdp_record_t *record; sdp_data_t *psm, *sh, *eh; uint16_t lp = ATT_PSM; record = sdp_record_alloc(); if (!record) return NULL; sdp_uuid16_create(&root_uuid, PUBLIC_BROWSE_GROUP); root = sdp_list_append(NULL, &root_uuid); sdp_set_browse_groups(record, root); sdp_list_free(root, NULL); svclass_id = sdp_list_append(NULL, uuid); sdp_set_service_classes(record, svclass_id); sdp_list_free(svclass_id, NULL); sdp_uuid16_create(&l2cap, L2CAP_UUID); proto[0] = sdp_list_append(NULL, &l2cap); psm = sdp_data_alloc(SDP_UINT16, &lp); proto[0] = sdp_list_append(proto[0], psm); apseq = sdp_list_append(NULL, proto[0]); sdp_uuid16_create(&proto_uuid, ATT_UUID); proto[1] = sdp_list_append(NULL, &proto_uuid); sh = sdp_data_alloc(SDP_UINT16, &start); proto[1] = sdp_list_append(proto[1], sh); eh = sdp_data_alloc(SDP_UINT16, &end); proto[1] = sdp_list_append(proto[1], eh); apseq = sdp_list_append(apseq, proto[1]); aproto = sdp_list_append(NULL, apseq); sdp_set_access_protos(record, aproto); if (name) sdp_set_info_attr(record, name, "BlueZ for Android", NULL); sdp_data_free(psm); sdp_data_free(sh); sdp_data_free(eh); sdp_list_free(proto[0], NULL); sdp_list_free(proto[1], NULL); sdp_list_free(apseq, NULL); sdp_list_free(aproto, NULL); return record; } static uint32_t add_sdp_record(const bt_uuid_t *uuid, uint16_t start, uint16_t end, const char *name) { sdp_record_t *rec; uuid_t u, u32; switch (uuid->type) { case BT_UUID16: sdp_uuid16_create(&u, uuid->value.u16); break; case BT_UUID32: sdp_uuid32_create(&u32, uuid->value.u32); sdp_uuid32_to_uuid128(&u, &u32); break; case BT_UUID128: sdp_uuid128_create(&u, &uuid->value.u128); break; case BT_UUID_UNSPEC: default: return 0; } rec = get_sdp_record(&u, start, end, name); if (!rec) return 0; if (bt_adapter_add_record(rec, 0) < 0) { error("gatt: Failed to register SDP record"); sdp_record_free(rec); return 0; } return rec->handle; } static bool match_service_sdp(const void *data, const void *user_data) { const struct service_sdp *s = data; return s->service_handle == PTR_TO_INT(user_data); } static struct service_sdp *new_service_sdp_record(int32_t service_handle) { bt_uuid_t uuid; struct service_sdp *s; struct gatt_db_attribute *attrib; uint16_t end_handle; attrib = gatt_db_get_attribute(gatt_db, service_handle); if (!attrib) return NULL; gatt_db_attribute_get_service_handles(attrib, NULL, &end_handle); if (!end_handle) return NULL; if (!gatt_db_attribute_get_service_uuid(attrib, &uuid)) return NULL; s = new0(struct service_sdp, 1); s->service_handle = service_handle; s->sdp_handle = add_sdp_record(&uuid, service_handle, end_handle, NULL); if (!s->sdp_handle) { free(s); return NULL; } return s; } static void free_service_sdp_record(void *data) { struct service_sdp *s = data; if (!s) return; bt_adapter_remove_record(s->sdp_handle); free(s); } static bool add_service_sdp_record(int32_t service_handle) { struct service_sdp *s; s = queue_find(services_sdp, match_service_sdp, INT_TO_PTR(service_handle)); if (s) return true; s = new_service_sdp_record(service_handle); if (!s) return false; queue_push_tail(services_sdp, s); return true; } static void remove_service_sdp_record(int32_t service_handle) { struct service_sdp *s; s = queue_remove_if(services_sdp, match_service_sdp, INT_TO_PTR(service_handle)); if (!s) return; free_service_sdp_record(s); } static void handle_server_start_service(const void *buf, uint16_t len) { const struct hal_cmd_gatt_server_start_service *cmd = buf; struct hal_ev_gatt_server_service_started ev; struct gatt_app *server; struct gatt_db_attribute *attrib; uint8_t status; DBG("transport 0x%02x", cmd->transport); memset(&ev, 0, sizeof(ev)); if (cmd->transport == 0) { status = HAL_STATUS_FAILED; goto failed; } server = find_app_by_id(cmd->server_if); if (!server) { status = HAL_STATUS_FAILED; goto failed; } if (cmd->transport & GATT_SERVER_TRANSPORT_BREDR_BIT) { if (!add_service_sdp_record(cmd->service_handle)) { status = HAL_STATUS_FAILED; goto failed; } } /* TODO: Handle BREDR only */ attrib = gatt_db_get_attribute(gatt_db, cmd->service_handle); if (!attrib) { status = HAL_STATUS_FAILED; goto failed; } if (!gatt_db_service_set_active(attrib, true)) { /* * no need to clean SDP since this can fail only if service * handle is invalid in which case add_sdp_record() also fails */ status = HAL_STATUS_FAILED; goto failed; } queue_foreach(gatt_devices, notify_service_change, attrib); status = HAL_STATUS_SUCCESS; failed: ev.status = status == HAL_STATUS_SUCCESS ? GATT_SUCCESS : GATT_FAILURE; ev.server_if = cmd->server_if; ev.srvc_handle = cmd->service_handle; ipc_send_notif(hal_ipc, HAL_SERVICE_ID_GATT, HAL_EV_GATT_SERVER_SERVICE_STARTED, sizeof(ev), &ev); ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_GATT, HAL_OP_GATT_SERVER_START_SERVICE, status); } static void handle_server_stop_service(const void *buf, uint16_t len) { const struct hal_cmd_gatt_server_stop_service *cmd = buf; struct hal_ev_gatt_server_service_stopped ev; struct gatt_app *server; struct gatt_db_attribute *attrib; uint8_t status; DBG(""); memset(&ev, 0, sizeof(ev)); server = find_app_by_id(cmd->server_if); if (!server) { status = HAL_STATUS_FAILED; goto failed; } attrib = gatt_db_get_attribute(gatt_db, cmd->service_handle); if (!attrib) { status = HAL_STATUS_FAILED; goto failed; } if (!gatt_db_service_set_active(attrib, false)) { status = HAL_STATUS_FAILED; goto failed; } remove_service_sdp_record(cmd->service_handle); status = HAL_STATUS_SUCCESS; queue_foreach(gatt_devices, notify_service_change, attrib); failed: ev.status = status == HAL_STATUS_SUCCESS ? GATT_SUCCESS : GATT_FAILURE; ev.server_if = cmd->server_if; ev.srvc_handle = cmd->service_handle; ipc_send_notif(hal_ipc, HAL_SERVICE_ID_GATT, HAL_EV_GATT_SERVER_SERVICE_STOPPED, sizeof(ev), &ev); ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_GATT, HAL_OP_GATT_SERVER_STOP_SERVICE, status); } static void handle_server_delete_service(const void *buf, uint16_t len) { const struct hal_cmd_gatt_server_delete_service *cmd = buf; struct hal_ev_gatt_server_service_deleted ev; struct gatt_app *server; struct gatt_db_attribute *attrib; uint8_t status; DBG(""); memset(&ev, 0, sizeof(ev)); server = find_app_by_id(cmd->server_if); if (!server) { status = HAL_STATUS_FAILED; goto failed; } attrib = gatt_db_get_attribute(gatt_db, cmd->service_handle); if (!attrib) { status = HAL_STATUS_FAILED; goto failed; } if (!gatt_db_remove_service(gatt_db, attrib)) { status = HAL_STATUS_FAILED; goto failed; } remove_service_sdp_record(cmd->service_handle); status = HAL_STATUS_SUCCESS; failed: ev.status = status == HAL_STATUS_SUCCESS ? GATT_SUCCESS : GATT_FAILURE; ev.srvc_handle = cmd->service_handle; ev.server_if = cmd->server_if; ipc_send_notif(hal_ipc, HAL_SERVICE_ID_GATT, HAL_EV_GATT_SERVER_SERVICE_DELETED, sizeof(ev), &ev); ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_GATT, HAL_OP_GATT_SERVER_DELETE_SERVICE, status); } static void indication_confirmation_cb(guint8 status, const guint8 *pdu, guint16 len, gpointer user_data) { struct hal_ev_gatt_server_indication_sent ev; ev.status = status; ev.conn_id = PTR_TO_UINT(user_data); ipc_send_notif(hal_ipc, HAL_SERVICE_ID_GATT, HAL_EV_GATT_SERVER_INDICATION_SENT, sizeof(ev), &ev); } static void handle_server_send_indication(const void *buf, uint16_t len) { const struct hal_cmd_gatt_server_send_indication *cmd = buf; struct app_connection *conn; uint8_t status; uint16_t length; uint8_t *pdu; size_t mtu; GAttribResultFunc confirmation_cb = NULL; DBG(""); conn = find_connection_by_id(cmd->conn_id); if (!conn) { error("gatt: Could not find connection"); status = HAL_STATUS_FAILED; goto reply; } pdu = g_attrib_get_buffer(conn->device->attrib, &mtu); if (cmd->confirm) { length = enc_indication(cmd->attribute_handle, (uint8_t *) cmd->value, cmd->len, pdu, mtu); confirmation_cb = indication_confirmation_cb; } else { length = enc_notification(cmd->attribute_handle, (uint8_t *) cmd->value, cmd->len, pdu, mtu); } if (!g_attrib_send(conn->device->attrib, 0, pdu, length, confirmation_cb, UINT_TO_PTR(conn->id), NULL)) { error("gatt: Failed to send indication"); status = HAL_STATUS_FAILED; } else { status = HAL_STATUS_SUCCESS; } /* Here we confirm failed indications and all notifications */ if (status || !confirmation_cb) indication_confirmation_cb(status, NULL, 0, UINT_TO_PTR(conn->id)); reply: ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_GATT, HAL_OP_GATT_SERVER_SEND_INDICATION, status); } static bool match_trans_id(const void *data, const void *user_data) { const struct pending_trans_data *transaction = data; return transaction->id == PTR_TO_UINT(user_data); } static bool find_conn_waiting_exec_write(const void *data, const void *user_data) { const struct app_connection *conn = data; return conn->wait_execute_write; } static bool pending_execute_write(void) { return queue_find(app_connections, find_conn_waiting_exec_write, NULL); } static void handle_server_send_response(const void *buf, uint16_t len) { const struct hal_cmd_gatt_server_send_response *cmd = buf; struct pending_trans_data *transaction; struct app_connection *conn; uint8_t status; DBG(""); conn = find_connection_by_id(cmd->conn_id); if (!conn) { error("gatt: could not found connection"); status = HAL_STATUS_FAILED; goto reply; } transaction = queue_remove_if(conn->transactions, match_trans_id, UINT_TO_PTR(cmd->trans_id)); if (!transaction) { error("gatt: transaction ID = %d not found", cmd->trans_id); status = HAL_STATUS_FAILED; goto reply; } if (transaction->opcode == ATT_OP_EXEC_WRITE_REQ) { struct pending_request *req; conn->wait_execute_write = false; /* Check for execute response from all server applications */ if (pending_execute_write()) goto done; /* * This is usually done through db write callback but for * execute write we dont have the attribute or handle to call * gatt_db_attribute_write(). */ req = queue_peek_head(conn->device->pending_requests); if (!req) goto done; /* Cast status to uint8_t, due to (byte) cast in java layer. */ req->error = err_to_att((uint8_t) cmd->status); req->completed = true; /* * FIXME: Handle situation when not all server applications * respond with a success. */ } /* Cast status to uint8_t, due to (byte) cast in java layer. */ if (transaction->opcode < ATT_OP_WRITE_REQ) gatt_db_attribute_read_result(transaction->attrib, transaction->serial_id, err_to_att((uint8_t) cmd->status), cmd->data, cmd->len); else gatt_db_attribute_write_result(transaction->attrib, transaction->serial_id, err_to_att((uint8_t) cmd->status)); send_dev_complete_response(conn->device, transaction->opcode); done: /* Clean request data */ free(transaction); status = HAL_STATUS_SUCCESS; reply: ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_GATT, HAL_OP_GATT_SERVER_SEND_RESPONSE, status); } static void handle_client_scan_filter_setup(const void *buf, uint16_t len) { const struct hal_cmd_gatt_client_scan_filter_setup *cmd = buf; DBG("client_if %u", cmd->client_if); /* TODO */ ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_GATT, HAL_OP_GATT_CLIENT_SCAN_FILTER_SETUP, HAL_STATUS_UNSUPPORTED); } static void handle_client_scan_filter_add_remove(const void *buf, uint16_t len) { const struct hal_cmd_gatt_client_scan_filter_add_remove *cmd = buf; DBG("client_if %u", cmd->client_if); /* TODO */ ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_GATT, HAL_OP_GATT_CLIENT_SCAN_FILTER_ADD_REMOVE, HAL_STATUS_UNSUPPORTED); } static void handle_client_scan_filter_clear(const void *buf, uint16_t len) { const struct hal_cmd_gatt_client_scan_filter_clear *cmd = buf; DBG("client_if %u", cmd->client_if); /* TODO */ ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_GATT, HAL_OP_GATT_CLIENT_SCAN_FILTER_CLEAR, HAL_STATUS_UNSUPPORTED); } static void handle_client_scan_filter_enable(const void *buf, uint16_t len) { const struct hal_cmd_gatt_client_scan_filter_enable *cmd = buf; DBG("client_if %u", cmd->client_if); /* TODO */ ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_GATT, HAL_OP_GATT_CLIENT_SCAN_FILTER_ENABLE, HAL_STATUS_UNSUPPORTED); } static void handle_client_configure_mtu(const void *buf, uint16_t len) { const struct hal_cmd_gatt_client_configure_mtu *cmd = buf; static struct app_connection *conn; uint8_t status; DBG("conn_id %u mtu %d", cmd->conn_id, cmd->mtu); conn = find_connection_by_id(cmd->conn_id); if (!conn) { status = HAL_STATUS_FAILED; goto failed; } /* * currently MTU is always exchanged on connection, just report current * value * * TODO figure out when send failed status in notification * TODO should we fail for BR/EDR? */ notify_client_mtu_change(conn, false); status = HAL_STATUS_SUCCESS; failed: ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_GATT, HAL_OP_GATT_CLIENT_CONFIGURE_MTU, status); } static void handle_client_conn_param_update(const void *buf, uint16_t len) { const struct hal_cmd_gatt_client_conn_param_update *cmd = buf; char address[18]; bdaddr_t bdaddr; android2bdaddr(cmd->address, &bdaddr); ba2str(&bdaddr, address); DBG("%s", address); /* TODO */ ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_GATT, HAL_OP_GATT_CLIENT_CONN_PARAM_UPDATE, HAL_STATUS_UNSUPPORTED); } static void handle_client_set_scan_param(const void *buf, uint16_t len) { const struct hal_cmd_gatt_client_set_scan_param *cmd = buf; DBG("interval %d window %d", cmd->interval, cmd->window); /* TODO */ ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_GATT, HAL_OP_GATT_CLIENT_SET_SCAN_PARAM, HAL_STATUS_UNSUPPORTED); } static struct adv_instance *find_adv_instance(uint32_t client_if) { struct gatt_app *app; struct adv_instance *adv; uint8_t inst = 0; unsigned int i; app = find_app_by_id(client_if); if (!app) return NULL; if (app->adv) return app->adv; /* Assume that kernel supports <= 32 advertising instances (5 today) * We have already indicated the number to the android framework layers * via the LE features so we don't check again here. * The kernel will detect the error if needed */ for (i = 0; i < sizeof(adv_inst_bits) * 8; i++) { uint32_t mask = 1 << i; if (!(adv_inst_bits & mask)) { inst = i + 1; adv_inst_bits |= mask; break; } } if (!inst) return NULL; adv = new0(__typeof__(*adv), 1); adv->instance = inst; app->adv = adv; DBG("Assigned advertising instance %d for client %d", inst, client_if); return adv; }; /* Build advertising data object from a data buffer containing * manufacturer_data, service_data, service uuids (in that order) * The input data is raw with no TLV structure and the service uuids are 128 bit */ static struct bt_ad *build_adv_data(int32_t manufacturer_data_len, int32_t service_data_len, int32_t service_uuid_len, const uint8_t *data_in) { const int one_svc_uuid_len = 128 / 8; /* Android uses 128bit UUIDs */ uint8_t *src = (uint8_t *)data_in; struct bt_ad *ad; unsigned num_svc_uuids, i; ad = bt_ad_new(); if (manufacturer_data_len >= 2) { /* Includes manufacturer id */ uint16_t manufacturer_id; manufacturer_id = bt_get_le16(src); src += 2; if (!bt_ad_add_manufacturer_data(ad, manufacturer_id, src, manufacturer_data_len - 2)) goto err; src += manufacturer_data_len - 2; } if (service_data_len >= 2) { /* Includes service uuid (always 16 bit) */ bt_uuid_t bt_uuid; uint16_t uuid16; uuid16 = bt_get_le16(src); src += 2; bt_uuid16_create(&bt_uuid, uuid16); if (!bt_ad_add_service_data(ad, &bt_uuid, src, service_data_len - 2)) goto err; src += service_data_len - 2; } if (service_uuid_len % one_svc_uuid_len) { error("Service UUIDs not multiple of %d bytes (%d)", one_svc_uuid_len, service_uuid_len); num_svc_uuids = 0; } else { num_svc_uuids = service_uuid_len / one_svc_uuid_len; } for (i = 0; i < num_svc_uuids; i++) { bt_uuid_t bt_uuid; android2uuid(src, &bt_uuid); src += one_svc_uuid_len; if (!bt_ad_add_service_uuid(ad, &bt_uuid)) goto err; } return ad; err: bt_ad_unref(ad); return NULL; } static void handle_client_setup_multi_adv(const void *buf, uint16_t len) { const struct hal_cmd_gatt_client_setup_multi_adv *cmd = buf; struct hal_ev_gatt_client_multi_adv_enable ev; struct adv_instance *adv; uint8_t status; DBG("client_if %d min_interval=%d max_interval=%d type=%d channel_map=0x%x tx_power=%d timeout=%d", cmd->client_if, cmd->min_interval, cmd->max_interval, cmd->type, cmd->channel_map, cmd->tx_power, cmd->timeout); adv = find_adv_instance(cmd->client_if); if (!adv) { status = HAL_STATUS_FAILED; goto out; } status = HAL_STATUS_SUCCESS; adv->timeout = cmd->timeout; adv->type = cmd->type; if (adv->type != ANDROID_ADVERTISING_EVENT_TYPE_SCANNABLE) { bt_ad_unref(adv->sr); adv->sr = NULL; } out: ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_GATT, HAL_OP_GATT_CLIENT_SETUP_MULTI_ADV, status); ev.client_if = cmd->client_if; ev.status = status; ipc_send_notif(hal_ipc, HAL_SERVICE_ID_GATT, HAL_EV_GATT_CLIENT_MULTI_ADV_ENABLE, sizeof(ev), &ev); } /* This is not currently called by Android 5.1 */ static void handle_client_update_multi_adv(const void *buf, uint16_t len) { const struct hal_cmd_gatt_client_update_multi_adv *cmd = buf; DBG("client_if %d", cmd->client_if); /* TODO */ ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_GATT, HAL_OP_GATT_CLIENT_UPDATE_MULTI_ADV, HAL_STATUS_UNSUPPORTED); } struct addrm_adv_cb_data { int32_t client_if; struct adv_instance *adv; }; static void add_advertising_cb(uint8_t status, void *user_data) { struct addrm_adv_cb_data *cb_data = user_data; struct hal_ev_gatt_client_multi_adv_data ev = { .status = status, .client_if = cb_data->client_if, }; ipc_send_notif(hal_ipc, HAL_SERVICE_ID_GATT, HAL_EV_GATT_CLIENT_MULTI_ADV_DATA, sizeof(ev), &ev); free(cb_data); } static void handle_client_setup_multi_adv_inst(const void *buf, uint16_t len) { const struct hal_cmd_gatt_client_setup_multi_adv_inst *cmd = buf; struct adv_instance *adv; struct bt_ad *adv_data; struct addrm_adv_cb_data *cb_data = NULL; uint8_t status = HAL_STATUS_FAILED; DBG("client_if %d set_scan_rsp=%d include_name=%d include_tx_power=%d appearance=%d manuf_data_len=%d svc_data_len=%d svc_uuid_len=%d", cmd->client_if, cmd->set_scan_rsp, cmd->include_name, cmd->include_tx_power, cmd->appearance, cmd->manufacturer_data_len, cmd->service_data_len, cmd->service_uuid_len ); adv = find_adv_instance(cmd->client_if); if (!adv) goto out; adv->include_tx_power = cmd->include_tx_power ? 1 : 0; adv_data = build_adv_data(cmd->manufacturer_data_len, cmd->service_data_len, cmd->service_uuid_len, cmd->data_service_uuid); if (!adv_data) goto out; if (cmd->set_scan_rsp) { bt_ad_unref(adv->sr); adv->sr = adv_data; } else { bt_ad_unref(adv->ad); adv->ad = adv_data; } cb_data = new0(__typeof__(*cb_data), 1); cb_data->client_if = cmd->client_if; cb_data->adv = adv; if (!bt_le_add_advertising(adv, add_advertising_cb, cb_data)) { error("gatt: Could not add advertising"); free(cb_data); goto out; } status = HAL_STATUS_SUCCESS; out: ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_GATT, HAL_OP_GATT_CLIENT_SETUP_MULTI_ADV_INST, status); if (status != HAL_STATUS_SUCCESS) { struct hal_ev_gatt_client_multi_adv_data ev = { .status = status, .client_if = cmd->client_if, }; ipc_send_notif(hal_ipc, HAL_SERVICE_ID_GATT, HAL_EV_GATT_CLIENT_MULTI_ADV_DATA, sizeof(ev), &ev); } } static void remove_advertising_cb(uint8_t status, void *user_data) { struct addrm_adv_cb_data *cb_data = user_data; struct hal_ev_gatt_client_multi_adv_data ev = { .status = status, .client_if = cb_data->client_if, }; free_adv_instance(cb_data->adv); ipc_send_notif(hal_ipc, HAL_SERVICE_ID_GATT, HAL_EV_GATT_CLIENT_MULTI_ADV_DISABLE, sizeof(ev), &ev); free(cb_data); } static void handle_client_disable_multi_adv_inst(const void *buf, uint16_t len) { const struct hal_cmd_gatt_client_disable_multi_adv_inst *cmd = buf; struct adv_instance *adv; struct gatt_app *app; struct addrm_adv_cb_data *cb_data = NULL; uint8_t status = HAL_STATUS_FAILED; DBG("client_if %d", cmd->client_if); adv = find_adv_instance(cmd->client_if); if (!adv) goto out; cb_data = new0(__typeof__(*cb_data), 1); cb_data->client_if = cmd->client_if; cb_data->adv = adv; if (!bt_le_remove_advertising(adv, remove_advertising_cb, cb_data)) { error("gatt: Could not remove advertising"); free(cb_data); goto out; } app = find_app_by_id(cmd->client_if); if (app) app->adv = NULL; status = HAL_STATUS_SUCCESS; out: ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_GATT, HAL_OP_GATT_CLIENT_DISABLE_MULTI_ADV_INST, status); if (status != HAL_STATUS_SUCCESS) { struct hal_ev_gatt_client_multi_adv_data ev = { .status = status, .client_if = cmd->client_if, }; ipc_send_notif(hal_ipc, HAL_SERVICE_ID_GATT, HAL_EV_GATT_CLIENT_MULTI_ADV_DISABLE, sizeof(ev), &ev); } } static void handle_client_configure_batchscan(const void *buf, uint16_t len) { const struct hal_cmd_gatt_client_configure_batchscan *cmd = buf; DBG("client_if %d", cmd->client_if); /* TODO */ ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_GATT, HAL_OP_GATT_CLIENT_CONFIGURE_BATCHSCAN, HAL_STATUS_UNSUPPORTED); } static void handle_client_enable_batchscan(const void *buf, uint16_t len) { const struct hal_cmd_gatt_client_enable_batchscan *cmd = buf; DBG("client_if %d", cmd->client_if); /* TODO */ ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_GATT, HAL_OP_GATT_CLIENT_ENABLE_BATCHSCAN, HAL_STATUS_UNSUPPORTED); } static void handle_client_disable_batchscan(const void *buf, uint16_t len) { const struct hal_cmd_gatt_client_disable_batchscan *cmd = buf; DBG("client_if %d", cmd->client_if); /* TODO */ ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_GATT, HAL_OP_GATT_CLIENT_DISABLE_BATCHSCAN, HAL_STATUS_UNSUPPORTED); } static void handle_client_read_batchscan_reports(const void *buf, uint16_t len) { const struct hal_cmd_gatt_client_read_batchscan_reports *cmd = buf; DBG("client_if %d", cmd->client_if); /* TODO */ ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_GATT, HAL_OP_GATT_CLIENT_READ_BATCHSCAN_REPORTS, HAL_STATUS_UNSUPPORTED); } static const struct ipc_handler cmd_handlers[] = { /* HAL_OP_GATT_CLIENT_REGISTER */ { handle_client_register, false, sizeof(struct hal_cmd_gatt_client_register) }, /* HAL_OP_GATT_CLIENT_UNREGISTER */ { handle_client_unregister, false, sizeof(struct hal_cmd_gatt_client_unregister) }, /* HAL_OP_GATT_CLIENT_SCAN */ { handle_client_scan, false, sizeof(struct hal_cmd_gatt_client_scan) }, /* HAL_OP_GATT_CLIENT_CONNECT */ { handle_client_connect, false, sizeof(struct hal_cmd_gatt_client_connect) }, /* HAL_OP_GATT_CLIENT_DISCONNECT */ { handle_client_disconnect, false, sizeof(struct hal_cmd_gatt_client_disconnect) }, /* HAL_OP_GATT_CLIENT_LISTEN */ { handle_client_listen, false, sizeof(struct hal_cmd_gatt_client_listen) }, /* HAL_OP_GATT_CLIENT_REFRESH */ { handle_client_refresh, false, sizeof(struct hal_cmd_gatt_client_refresh) }, /* HAL_OP_GATT_CLIENT_SEARCH_SERVICE */ { handle_client_search_service, true, sizeof(struct hal_cmd_gatt_client_search_service) }, /* HAL_OP_GATT_CLIENT_GET_INCLUDED_SERVICE */ { handle_client_get_included_service, true, sizeof(struct hal_cmd_gatt_client_get_included_service) }, /* HAL_OP_GATT_CLIENT_GET_CHARACTERISTIC */ { handle_client_get_characteristic, true, sizeof(struct hal_cmd_gatt_client_get_characteristic) }, /* HAL_OP_GATT_CLIENT_GET_DESCRIPTOR */ { handle_client_get_descriptor, true, sizeof(struct hal_cmd_gatt_client_get_descriptor) }, /* HAL_OP_GATT_CLIENT_READ_CHARACTERISTIC */ { handle_client_read_characteristic, false, sizeof(struct hal_cmd_gatt_client_read_characteristic) }, /* HAL_OP_GATT_CLIENT_WRITE_CHARACTERISTIC */ { handle_client_write_characteristic, true, sizeof(struct hal_cmd_gatt_client_write_characteristic) }, /* HAL_OP_GATT_CLIENT_READ_DESCRIPTOR */ { handle_client_read_descriptor, false, sizeof(struct hal_cmd_gatt_client_read_descriptor) }, /* HAL_OP_GATT_CLIENT_WRITE_DESCRIPTOR */ { handle_client_write_descriptor, true, sizeof(struct hal_cmd_gatt_client_write_descriptor) }, /* HAL_OP_GATT_CLIENT_EXECUTE_WRITE */ { handle_client_execute_write, false, sizeof(struct hal_cmd_gatt_client_execute_write)}, /* HAL_OP_GATT_CLIENT_REGISTER_FOR_NOTIFICATION */ { handle_client_register_for_notification, false, sizeof(struct hal_cmd_gatt_client_register_for_notification) }, /* HAL_OP_GATT_CLIENT_DEREGISTER_FOR_NOTIFICATION */ { handle_client_deregister_for_notification, false, sizeof(struct hal_cmd_gatt_client_deregister_for_notification) }, /* HAL_OP_GATT_CLIENT_READ_REMOTE_RSSI */ { handle_client_read_remote_rssi, false, sizeof(struct hal_cmd_gatt_client_read_remote_rssi) }, /* HAL_OP_GATT_CLIENT_GET_DEVICE_TYPE */ { handle_client_get_device_type, false, sizeof(struct hal_cmd_gatt_client_get_device_type) }, /* HAL_OP_GATT_CLIENT_SET_ADV_DATA */ { handle_client_set_adv_data, true, sizeof(struct hal_cmd_gatt_client_set_adv_data) }, /* HAL_OP_GATT_CLIENT_TEST_COMMAND */ { handle_client_test_command, false, sizeof(struct hal_cmd_gatt_client_test_command) }, /* HAL_OP_GATT_SERVER_REGISTER */ { handle_server_register, false, sizeof(struct hal_cmd_gatt_server_register) }, /* HAL_OP_GATT_SERVER_UNREGISTER */ { handle_server_unregister, false, sizeof(struct hal_cmd_gatt_server_unregister) }, /* HAL_OP_GATT_SERVER_CONNECT */ { handle_server_connect, false, sizeof(struct hal_cmd_gatt_server_connect) }, /* HAL_OP_GATT_SERVER_DISCONNECT */ { handle_server_disconnect, false, sizeof(struct hal_cmd_gatt_server_disconnect) }, /* HAL_OP_GATT_SERVER_ADD_SERVICE */ { handle_server_add_service, false, sizeof(struct hal_cmd_gatt_server_add_service) }, /* HAL_OP_GATT_SERVER_ADD_INC_SERVICE */ { handle_server_add_included_service, false, sizeof(struct hal_cmd_gatt_server_add_inc_service) }, /* HAL_OP_GATT_SERVER_ADD_CHARACTERISTIC */ { handle_server_add_characteristic, false, sizeof(struct hal_cmd_gatt_server_add_characteristic) }, /* HAL_OP_GATT_SERVER_ADD_DESCRIPTOR */ { handle_server_add_descriptor, false, sizeof(struct hal_cmd_gatt_server_add_descriptor) }, /* HAL_OP_GATT_SERVER_START_SERVICE */ { handle_server_start_service, false, sizeof(struct hal_cmd_gatt_server_start_service) }, /* HAL_OP_GATT_SERVER_STOP_SERVICE */ { handle_server_stop_service, false, sizeof(struct hal_cmd_gatt_server_stop_service) }, /* HAL_OP_GATT_SERVER_DELETE_SERVICE */ { handle_server_delete_service, false, sizeof(struct hal_cmd_gatt_server_delete_service) }, /* HAL_OP_GATT_SERVER_SEND_INDICATION */ { handle_server_send_indication, true, sizeof(struct hal_cmd_gatt_server_send_indication) }, /* HAL_OP_GATT_SERVER_SEND_RESPONSE */ { handle_server_send_response, true, sizeof(struct hal_cmd_gatt_server_send_response) }, /* HAL_OP_GATT_CLIENT_SCAN_FILTER_SETUP */ { handle_client_scan_filter_setup, false, sizeof(struct hal_cmd_gatt_client_scan_filter_setup) }, /* HAL_OP_GATT_CLIENT_SCAN_FILTER_ADD_REMOVE */ { handle_client_scan_filter_add_remove, true, sizeof(struct hal_cmd_gatt_client_scan_filter_add_remove) }, /* HAL_OP_GATT_CLIENT_SCAN_FILTER_CLEAR */ { handle_client_scan_filter_clear, false, sizeof(struct hal_cmd_gatt_client_scan_filter_clear) }, /* HAL_OP_GATT_CLIENT_SCAN_FILTER_ENABLE */ { handle_client_scan_filter_enable, false, sizeof(struct hal_cmd_gatt_client_scan_filter_enable) }, /* HAL_OP_GATT_CLIENT_CONFIGURE_MTU */ { handle_client_configure_mtu, false, sizeof(struct hal_cmd_gatt_client_configure_mtu) }, /* HAL_OP_GATT_CLIENT_CONN_PARAM_UPDATE */ { handle_client_conn_param_update, false, sizeof(struct hal_cmd_gatt_client_conn_param_update) }, /* HAL_OP_GATT_CLIENT_SET_SCAN_PARAM */ { handle_client_set_scan_param, false, sizeof(struct hal_cmd_gatt_client_set_scan_param) }, /* HAL_OP_GATT_CLIENT_SETUP_MULTI_ADV */ { handle_client_setup_multi_adv, false, sizeof(struct hal_cmd_gatt_client_setup_multi_adv) }, /* HAL_OP_GATT_CLIENT_UPDATE_MULTI_ADV */ { handle_client_update_multi_adv, false, sizeof(struct hal_cmd_gatt_client_update_multi_adv) }, /* HAL_OP_GATT_CLIENT_SETUP_MULTI_ADV_INST */ { handle_client_setup_multi_adv_inst, true, sizeof(struct hal_cmd_gatt_client_setup_multi_adv_inst) }, /* HAL_OP_GATT_CLIENT_DISABLE_MULTI_ADV_INST */ { handle_client_disable_multi_adv_inst, false, sizeof(struct hal_cmd_gatt_client_disable_multi_adv_inst) }, /* HAL_OP_GATT_CLIENT_CONFIGURE_BATCHSCAN */ { handle_client_configure_batchscan, false, sizeof(struct hal_cmd_gatt_client_configure_batchscan) }, /* HAL_OP_GATT_CLIENT_ENABLE_BATCHSCAN */ { handle_client_enable_batchscan, false, sizeof(struct hal_cmd_gatt_client_enable_batchscan) }, /* HAL_OP_GATT_CLIENT_DISABLE_BATCHSCAN */ { handle_client_disable_batchscan, false, sizeof(struct hal_cmd_gatt_client_disable_batchscan) }, /* HAL_OP_GATT_CLIENT_READ_BATCHSCAN_REPORTS */ { handle_client_read_batchscan_reports, false, sizeof(struct hal_cmd_gatt_client_read_batchscan_reports) }, }; static uint8_t read_by_type(const uint8_t *cmd, uint16_t cmd_len, struct gatt_device *device) { uint16_t start, end; uint16_t len = 0; bt_uuid_t uuid; struct queue *q; DBG(""); switch (cmd[0]) { case ATT_OP_READ_BY_TYPE_REQ: len = dec_read_by_type_req(cmd, cmd_len, &start, &end, &uuid); break; case ATT_OP_READ_BY_GROUP_REQ: len = dec_read_by_grp_req(cmd, cmd_len, &start, &end, &uuid); break; default: break; } if (!len) return ATT_ECODE_INVALID_PDU; if (start > end || start == 0) return ATT_ECODE_INVALID_HANDLE; q = queue_new(); switch (cmd[0]) { case ATT_OP_READ_BY_TYPE_REQ: gatt_db_read_by_type(gatt_db, start, end, uuid, q); break; case ATT_OP_READ_BY_GROUP_REQ: gatt_db_read_by_group_type(gatt_db, start, end, uuid, q); break; default: break; } if (queue_isempty(q)) { queue_destroy(q, NULL); return ATT_ECODE_ATTR_NOT_FOUND; } while (queue_peek_head(q)) { struct pending_request *data; struct gatt_db_attribute *attrib = queue_pop_head(q); data = new0(struct pending_request, 1); data->attrib = attrib; queue_push_tail(device->pending_requests, data); } queue_destroy(q, NULL); process_dev_pending_requests(device, cmd[0]); return 0; } static uint8_t read_request(const uint8_t *cmd, uint16_t cmd_len, struct gatt_device *dev) { struct gatt_db_attribute *attrib; uint16_t handle; uint16_t len; uint16_t offset; struct pending_request *data; DBG(""); switch (cmd[0]) { case ATT_OP_READ_BLOB_REQ: len = dec_read_blob_req(cmd, cmd_len, &handle, &offset); if (!len) return ATT_ECODE_INVALID_PDU; break; case ATT_OP_READ_REQ: len = dec_read_req(cmd, cmd_len, &handle); if (!len) return ATT_ECODE_INVALID_PDU; offset = 0; break; default: error("gatt: Unexpected read type 0x%02x", cmd[0]); return ATT_ECODE_REQ_NOT_SUPP; } attrib = gatt_db_get_attribute(gatt_db, handle); if (attrib == 0) return ATT_ECODE_INVALID_HANDLE; data = new0(struct pending_request, 1); data->offset = offset; data->attrib = attrib; queue_push_tail(dev->pending_requests, data); process_dev_pending_requests(dev, cmd[0]); return 0; } static uint8_t mtu_att_handle(const uint8_t *cmd, uint16_t cmd_len, struct gatt_device *dev) { uint16_t rmtu, mtu, len; size_t length; uint8_t *rsp; DBG(""); len = dec_mtu_req(cmd, cmd_len, &rmtu); if (!len) return ATT_ECODE_INVALID_PDU; /* MTU exchange shall not be used on BR/EDR - Vol 3. Part G. 4.3.1 */ if (get_cid(dev) != ATT_CID) return ATT_ECODE_UNLIKELY; if (!get_local_mtu(dev, &mtu)) return ATT_ECODE_UNLIKELY; if (!update_mtu(dev, rmtu)) return ATT_ECODE_UNLIKELY; rsp = g_attrib_get_buffer(dev->attrib, &length); /* Respond with our MTU */ len = enc_mtu_resp(mtu, rsp, length); if (!g_attrib_send(dev->attrib, 0, rsp, len, NULL, NULL, NULL)) return ATT_ECODE_UNLIKELY; return 0; } static uint8_t find_info_handle(const uint8_t *cmd, uint16_t cmd_len, uint8_t *rsp, size_t rsp_size, uint16_t *length) { struct gatt_db_attribute *attrib; struct queue *q, *temp; struct att_data_list *adl; int iterator = 0; uint16_t start, end; uint16_t len, queue_len; uint8_t format; uint8_t ret = 0; DBG(""); len = dec_find_info_req(cmd, cmd_len, &start, &end); if (!len) return ATT_ECODE_INVALID_PDU; if (start > end || start == 0) return ATT_ECODE_INVALID_HANDLE; q = queue_new(); gatt_db_find_information(gatt_db, start, end, q); if (queue_isempty(q)) { queue_destroy(q, NULL); return ATT_ECODE_ATTR_NOT_FOUND; } temp = queue_new(); attrib = queue_peek_head(q); /* UUIDS can be only 128 bit and 16 bit */ len = bt_uuid_len(gatt_db_attribute_get_type(attrib)); if (len != 2 && len != 16) { queue_destroy(q, NULL); queue_destroy(temp, NULL); return ATT_ECODE_UNLIKELY; } while (attrib) { const bt_uuid_t *type; type = gatt_db_attribute_get_type(attrib); if (bt_uuid_len(type) != len) break; queue_push_tail(temp, queue_pop_head(q)); attrib = queue_peek_head(q); } queue_destroy(q, NULL); queue_len = queue_length(temp); adl = att_data_list_alloc(queue_len, len + sizeof(uint16_t)); if (!adl) { queue_destroy(temp, NULL); return ATT_ECODE_INSUFF_RESOURCES; } while (queue_peek_head(temp)) { uint8_t *value; const bt_uuid_t *type; struct gatt_db_attribute *attrib = queue_pop_head(temp); uint16_t handle; type = gatt_db_attribute_get_type(attrib); if (!type) break; value = adl->data[iterator++]; handle = gatt_db_attribute_get_handle(attrib); put_le16(handle, value); memcpy(&value[2], &type->value, len); } if (len == 2) format = ATT_FIND_INFO_RESP_FMT_16BIT; else format = ATT_FIND_INFO_RESP_FMT_128BIT; len = enc_find_info_resp(format, adl, rsp, rsp_size); if (!len) ret = ATT_ECODE_UNLIKELY; *length = len; att_data_list_free(adl); queue_destroy(temp, NULL); return ret; } struct find_by_type_request_data { struct gatt_device *device; uint8_t *search_value; size_t search_vlen; uint8_t error; }; static void find_by_type_request_cb(struct gatt_db_attribute *attrib, void *user_data) { struct find_by_type_request_data *find_data = user_data; struct pending_request *request_data; if (find_data->error) return; request_data = new0(struct pending_request, 1); request_data->filter_value = malloc0(find_data->search_vlen); if (!request_data->filter_value) { destroy_pending_request(request_data); find_data->error = ATT_ECODE_INSUFF_RESOURCES; return; } request_data->attrib = attrib; request_data->filter_vlen = find_data->search_vlen; memcpy(request_data->filter_value, find_data->search_value, find_data->search_vlen); queue_push_tail(find_data->device->pending_requests, request_data); } static uint8_t find_by_type_request(const uint8_t *cmd, uint16_t cmd_len, struct gatt_device *device) { uint8_t search_value[cmd_len]; size_t search_vlen; uint16_t start, end; bt_uuid_t uuid; uint16_t len; struct find_by_type_request_data data; DBG(""); len = dec_find_by_type_req(cmd, cmd_len, &start, &end, &uuid, search_value, &search_vlen); if (!len) return ATT_ECODE_INVALID_PDU; if (start > end || start == 0) return ATT_ECODE_INVALID_HANDLE; data.error = 0; data.search_vlen = search_vlen; data.search_value = search_value; data.device = device; if (gatt_db_find_by_type(gatt_db, start, end, &uuid, find_by_type_request_cb, &data) == 0) { size_t mtu; uint8_t *rsp = g_attrib_get_buffer(device->attrib, &mtu); len = enc_error_resp(ATT_OP_FIND_BY_TYPE_REQ, start, ATT_ECODE_ATTR_NOT_FOUND, rsp, mtu); g_attrib_send(device->attrib, 0, rsp, len, NULL, NULL, NULL); return 0; } if (!data.error) process_dev_pending_requests(device, ATT_OP_FIND_BY_TYPE_REQ); return data.error; } static void write_confirm(struct gatt_db_attribute *attrib, int err, void *user_data) { if (!err) return; error("Error writting attribute %p", attrib); } static void write_cmd_request(const uint8_t *cmd, uint16_t cmd_len, struct gatt_device *dev) { uint8_t value[cmd_len]; struct gatt_db_attribute *attrib; uint32_t permissions; uint16_t handle; uint16_t len; size_t vlen; len = dec_write_cmd(cmd, cmd_len, &handle, value, &vlen); if (!len) return; if (handle == 0) return; attrib = gatt_db_get_attribute(gatt_db, handle); if (!attrib) return; permissions = gatt_db_attribute_get_permissions(attrib); if (check_device_permissions(dev, cmd[0], permissions)) return; gatt_db_attribute_write(attrib, 0, value, vlen, cmd[0], g_attrib_get_att(dev->attrib), write_confirm, NULL); } static void write_signed_cmd_request(const uint8_t *cmd, uint16_t cmd_len, struct gatt_device *dev) { uint8_t value[cmd_len]; uint8_t s[ATT_SIGNATURE_LEN]; struct gatt_db_attribute *attrib; uint32_t permissions; uint16_t handle; uint16_t len; size_t vlen; uint8_t csrk[16]; uint32_t sign_cnt; if (get_cid(dev) != ATT_CID) { error("gatt: Remote tries write signed on BR/EDR bearer"); connection_cleanup(dev); return; } if (get_sec_level(dev) != BT_SECURITY_LOW) { error("gatt: Remote tries write signed on encrypted link"); connection_cleanup(dev); return; } if (!bt_get_csrk(&dev->bdaddr, false, csrk, &sign_cnt, NULL)) { error("gatt: No valid csrk from remote device"); return; } len = dec_signed_write_cmd(cmd, cmd_len, &handle, value, &vlen, s); if (handle == 0) return; attrib = gatt_db_get_attribute(gatt_db, handle); if (!attrib) return; permissions = gatt_db_attribute_get_permissions(attrib); if (check_device_permissions(dev, cmd[0], permissions)) return; if (len) { uint8_t t[ATT_SIGNATURE_LEN]; uint32_t r_sign_cnt = get_le32(s); if (r_sign_cnt < sign_cnt) { error("gatt: Invalid sign counter (%d<%d)", r_sign_cnt, sign_cnt); return; } /* Generate signature and verify it */ if (!bt_crypto_sign_att(crypto, csrk, cmd, cmd_len - ATT_SIGNATURE_LEN, r_sign_cnt, t)) { error("gatt: Error when generating att signature"); return; } if (memcmp(t, s, ATT_SIGNATURE_LEN)) { error("gatt: signature does not match"); return; } /* Signature OK, proceed with write */ bt_update_sign_counter(&dev->bdaddr, false, r_sign_cnt); gatt_db_attribute_write(attrib, 0, value, vlen, cmd[0], g_attrib_get_att(dev->attrib), write_confirm, NULL); } } static void attribute_write_cb(struct gatt_db_attribute *attrib, int err, void *user_data) { struct pending_request *data = user_data; uint8_t error = err_to_att(err); DBG(""); data->attrib = attrib; data->error = error; data->completed = true; } static uint8_t write_req_request(const uint8_t *cmd, uint16_t cmd_len, struct gatt_device *dev) { uint8_t value[cmd_len]; struct pending_request *data; struct gatt_db_attribute *attrib; uint32_t permissions; uint16_t handle; uint16_t len; uint8_t error; size_t vlen; len = dec_write_req(cmd, cmd_len, &handle, value, &vlen); if (!len) return ATT_ECODE_INVALID_PDU; if (handle == 0) return ATT_ECODE_INVALID_HANDLE; attrib = gatt_db_get_attribute(gatt_db, handle); if (!attrib) return ATT_ECODE_ATTR_NOT_FOUND; permissions = gatt_db_attribute_get_permissions(attrib); error = check_device_permissions(dev, cmd[0], permissions); if (error) return error; data = new0(struct pending_request, 1); data->attrib = attrib; queue_push_tail(dev->pending_requests, data); if (!gatt_db_attribute_write(attrib, 0, value, vlen, cmd[0], g_attrib_get_att(dev->attrib), attribute_write_cb, data)) { queue_remove(dev->pending_requests, data); free(data); return ATT_ECODE_UNLIKELY; } send_dev_complete_response(dev, cmd[0]); return 0; } static uint8_t write_prep_request(const uint8_t *cmd, uint16_t cmd_len, struct gatt_device *dev) { uint8_t value[cmd_len]; struct pending_request *data; struct gatt_db_attribute *attrib; uint32_t permissions; uint16_t handle; uint16_t offset; uint8_t error; uint16_t len; size_t vlen; len = dec_prep_write_req(cmd, cmd_len, &handle, &offset, value, &vlen); if (!len) return ATT_ECODE_INVALID_PDU; if (handle == 0) return ATT_ECODE_INVALID_HANDLE; attrib = gatt_db_get_attribute(gatt_db, handle); if (!attrib) return ATT_ECODE_ATTR_NOT_FOUND; permissions = gatt_db_attribute_get_permissions(attrib); error = check_device_permissions(dev, cmd[0], permissions); if (error) return error; data = new0(struct pending_request, 1); data->attrib = attrib; data->offset = offset; queue_push_tail(dev->pending_requests, data); data->value = util_memdup(value, vlen); data->length = vlen; if (!gatt_db_attribute_write(attrib, offset, value, vlen, cmd[0], g_attrib_get_att(dev->attrib), attribute_write_cb, data)) { queue_remove(dev->pending_requests, data); g_free(data->value); free(data); return ATT_ECODE_UNLIKELY; } send_dev_complete_response(dev, cmd[0]); return 0; } static void send_server_write_execute_notify(void *data, void *user_data) { struct hal_ev_gatt_server_request_exec_write *ev = user_data; struct pending_trans_data *transaction; struct app_connection *conn = data; if (!conn->wait_execute_write) return; ev->conn_id = conn->id; transaction = conn_add_transact(conn, ATT_OP_EXEC_WRITE_REQ, NULL, 0); ev->trans_id = transaction->id; ipc_send_notif(hal_ipc, HAL_SERVICE_ID_GATT, HAL_EV_GATT_SERVER_REQUEST_EXEC_WRITE, sizeof(*ev), ev); } static uint8_t write_execute_request(const uint8_t *cmd, uint16_t cmd_len, struct gatt_device *dev) { struct hal_ev_gatt_server_request_exec_write ev; uint8_t value; struct pending_request *data; /* * Check if there was any write prep before. * TODO: Try to find better error code if possible */ if (!pending_execute_write()) return ATT_ECODE_UNLIKELY; if (!dec_exec_write_req(cmd, cmd_len, &value)) return ATT_ECODE_INVALID_PDU; memset(&ev, 0, sizeof(ev)); bdaddr2android(&dev->bdaddr, &ev.bdaddr); ev.exec_write = value; data = new0(struct pending_request, 1); queue_push_tail(dev->pending_requests, data); queue_foreach(app_connections, send_server_write_execute_notify, &ev); send_dev_complete_response(dev, cmd[0]); return 0; } static void att_handler(const uint8_t *ipdu, uint16_t len, gpointer user_data) { struct gatt_device *dev = user_data; uint8_t status; uint16_t resp_length = 0; size_t length; uint8_t *opdu = g_attrib_get_buffer(dev->attrib, &length); DBG("op 0x%02x", ipdu[0]); if (len > length) { error("gatt: Too much data on ATT socket %p", opdu); status = ATT_ECODE_INVALID_PDU; goto done; } switch (ipdu[0]) { case ATT_OP_READ_BY_GROUP_REQ: case ATT_OP_READ_BY_TYPE_REQ: status = read_by_type(ipdu, len, dev); break; case ATT_OP_READ_REQ: case ATT_OP_READ_BLOB_REQ: status = read_request(ipdu, len, dev); break; case ATT_OP_MTU_REQ: status = mtu_att_handle(ipdu, len, dev); break; case ATT_OP_FIND_INFO_REQ: status = find_info_handle(ipdu, len, opdu, length, &resp_length); break; case ATT_OP_WRITE_REQ: status = write_req_request(ipdu, len, dev); break; case ATT_OP_WRITE_CMD: write_cmd_request(ipdu, len, dev); /* No response on write cmd */ return; case ATT_OP_SIGNED_WRITE_CMD: write_signed_cmd_request(ipdu, len, dev); /* No response on write signed cmd */ return; case ATT_OP_PREP_WRITE_REQ: status = write_prep_request(ipdu, len, dev); break; case ATT_OP_FIND_BY_TYPE_REQ: status = find_by_type_request(ipdu, len, dev); break; case ATT_OP_EXEC_WRITE_REQ: status = write_execute_request(ipdu, len, dev); break; case ATT_OP_READ_MULTI_REQ: default: DBG("Unsupported request 0x%02x", ipdu[0]); status = ATT_ECODE_REQ_NOT_SUPP; break; } done: if (status) resp_length = enc_error_resp(ipdu[0], 0x0000, status, opdu, length); g_attrib_send(dev->attrib, 0, opdu, resp_length, NULL, NULL, NULL); } static void connect_confirm(GIOChannel *io, void *user_data) { struct gatt_device *dev; bdaddr_t dst; GError *gerr = NULL; DBG(""); bt_io_get(io, &gerr, BT_IO_OPT_DEST_BDADDR, &dst, BT_IO_OPT_INVALID); if (gerr) { error("gatt: bt_io_get: %s", gerr->message); g_error_free(gerr); return; } /* TODO Handle collision */ dev = find_device_by_addr(&dst); if (!dev) { dev = create_device(&dst); } else { if ((dev->state != DEVICE_DISCONNECTED) && !(dev->state == DEVICE_CONNECT_INIT && bt_kernel_conn_control())) { char addr[18]; ba2str(&dst, addr); info("gatt: Rejecting incoming connection from %s", addr); goto drop; } } if (!bt_io_accept(io, connect_cb, device_ref(dev), NULL, NULL)) { error("gatt: failed to accept connection"); device_unref(dev); goto drop; } queue_foreach(listen_apps, create_app_connection, dev); device_set_state(dev, DEVICE_CONNECT_READY); return; drop: g_io_channel_shutdown(io, TRUE, NULL); } struct gap_srvc_handles { struct gatt_db_attribute *srvc; /* Characteristics */ struct gatt_db_attribute *dev_name; struct gatt_db_attribute *appear; struct gatt_db_attribute *priv; }; static struct gap_srvc_handles gap_srvc_data; #define APPEARANCE_GENERIC_PHONE 0x0040 #define PERIPHERAL_PRIVACY_DISABLE 0x00 static void device_name_read_cb(struct gatt_db_attribute *attrib, unsigned int id, uint16_t offset, uint8_t opcode, struct bt_att *att, void *user_data) { const char *name = bt_get_adapter_name(); gatt_db_attribute_read_result(attrib, id, 0, (void *) name, strlen(name)); } static void register_gap_service(void) { uint16_t start, end; bt_uuid_t uuid; /* GAP UUID */ bt_uuid16_create(&uuid, 0x1800); gap_srvc_data.srvc = gatt_db_add_service(gatt_db, &uuid, true, 7); /* Device name characteristic */ bt_uuid16_create(&uuid, GATT_CHARAC_DEVICE_NAME); gap_srvc_data.dev_name = gatt_db_service_add_characteristic(gap_srvc_data.srvc, &uuid, GATT_PERM_READ, GATT_CHR_PROP_READ, device_name_read_cb, NULL, NULL); /* Appearance */ bt_uuid16_create(&uuid, GATT_CHARAC_APPEARANCE); gap_srvc_data.appear = gatt_db_service_add_characteristic(gap_srvc_data.srvc, &uuid, GATT_PERM_READ, GATT_CHR_PROP_READ, NULL, NULL, NULL); if (gap_srvc_data.appear) { uint16_t value; /* Store appearance into db */ value = cpu_to_le16(APPEARANCE_GENERIC_PHONE); gatt_db_attribute_write(gap_srvc_data.appear, 0, (void *) &value, sizeof(value), ATT_OP_WRITE_REQ, NULL, write_confirm, NULL); } /* Pripheral privacy flag */ bt_uuid16_create(&uuid, GATT_CHARAC_PERIPHERAL_PRIV_FLAG); gap_srvc_data.priv = gatt_db_service_add_characteristic(gap_srvc_data.srvc, &uuid, GATT_PERM_READ, GATT_CHR_PROP_READ, NULL, NULL, NULL); if (gap_srvc_data.priv) { uint8_t value; /* Store privacy into db */ value = PERIPHERAL_PRIVACY_DISABLE; gatt_db_attribute_write(gap_srvc_data.priv, 0, &value, sizeof(value), ATT_OP_WRITE_REQ, NULL, write_confirm, NULL); } gatt_db_service_set_active(gap_srvc_data.srvc , true); /* SDP */ bt_uuid16_create(&uuid, 0x1800); gatt_db_attribute_get_service_handles(gap_srvc_data.srvc, &start, &end); gap_sdp_handle = add_sdp_record(&uuid, start, end, "Generic Access Profile"); if (!gap_sdp_handle) error("gatt: Failed to register GAP SDP record"); } static void device_info_read_cb(struct gatt_db_attribute *attrib, unsigned int id, uint16_t offset, uint8_t opcode, struct bt_att *att, void *user_data) { char *buf = user_data; gatt_db_attribute_read_result(attrib, id, 0, user_data, strlen(buf)); } static void device_info_read_system_id_cb(struct gatt_db_attribute *attrib, unsigned int id, uint16_t offset, uint8_t opcode, struct bt_att *att, void *user_data) { uint8_t pdu[8]; put_le64(bt_config_get_system_id(), pdu); gatt_db_attribute_read_result(attrib, id, 0, pdu, sizeof(pdu)); } static void device_info_read_pnp_id_cb(struct gatt_db_attribute *attrib, unsigned int id, uint16_t offset, uint8_t opcode, struct bt_att *att, void *user_data) { uint8_t pdu[7]; pdu[0] = bt_config_get_pnp_source(); put_le16(bt_config_get_pnp_vendor(), &pdu[1]); put_le16(bt_config_get_pnp_product(), &pdu[3]); put_le16(bt_config_get_pnp_version(), &pdu[5]); gatt_db_attribute_read_result(attrib, id, 0, pdu, sizeof(pdu)); } static void register_device_info_service(void) { bt_uuid_t uuid; struct gatt_db_attribute *service; uint16_t start_handle, end_handle; const char *data; uint32_t enc_perm = GATT_PERM_READ | GATT_PERM_READ_ENCRYPTED; DBG(""); /* Device Information Service */ bt_uuid16_create(&uuid, 0x180a); service = gatt_db_add_service(gatt_db, &uuid, true, 17); /* User data are not const hence (void *) cast is used */ data = bt_config_get_name(); if (data) { bt_uuid16_create(&uuid, GATT_CHARAC_MODEL_NUMBER_STRING); gatt_db_service_add_characteristic(service, &uuid, GATT_PERM_READ, GATT_CHR_PROP_READ, device_info_read_cb, NULL, (void *) data); } data = bt_config_get_serial(); if (data) { bt_uuid16_create(&uuid, GATT_CHARAC_SERIAL_NUMBER_STRING); gatt_db_service_add_characteristic(service, &uuid, enc_perm, GATT_CHR_PROP_READ, device_info_read_cb, NULL, (void *) data); } if (bt_config_get_system_id()) { bt_uuid16_create(&uuid, GATT_CHARAC_SYSTEM_ID); gatt_db_service_add_characteristic(service, &uuid, enc_perm, GATT_CHR_PROP_READ, device_info_read_system_id_cb, NULL, NULL); } data = bt_config_get_fw_rev(); if (data) { bt_uuid16_create(&uuid, GATT_CHARAC_FIRMWARE_REVISION_STRING); gatt_db_service_add_characteristic(service, &uuid, GATT_PERM_READ, GATT_CHR_PROP_READ, device_info_read_cb, NULL, (void *) data); } data = bt_config_get_hw_rev(); if (data) { bt_uuid16_create(&uuid, GATT_CHARAC_HARDWARE_REVISION_STRING); gatt_db_service_add_characteristic(service, &uuid, GATT_PERM_READ, GATT_CHR_PROP_READ, device_info_read_cb, NULL, (void *) data); } bt_uuid16_create(&uuid, GATT_CHARAC_SOFTWARE_REVISION_STRING); gatt_db_service_add_characteristic(service, &uuid, GATT_PERM_READ, GATT_CHR_PROP_READ, device_info_read_cb, NULL, VERSION); data = bt_config_get_vendor(); if (data) { bt_uuid16_create(&uuid, GATT_CHARAC_MANUFACTURER_NAME_STRING); gatt_db_service_add_characteristic(service, &uuid, GATT_PERM_READ, GATT_CHR_PROP_READ, device_info_read_cb, NULL, (void *) data); } if (bt_config_get_pnp_source()) { bt_uuid16_create(&uuid, GATT_CHARAC_PNP_ID); gatt_db_service_add_characteristic(service, &uuid, GATT_PERM_READ, GATT_CHR_PROP_READ, device_info_read_pnp_id_cb, NULL, NULL); } gatt_db_service_set_active(service, true); /* SDP */ bt_uuid16_create(&uuid, 0x180a); gatt_db_attribute_get_service_handles(service, &start_handle, &end_handle); dis_sdp_handle = add_sdp_record(&uuid, start_handle, end_handle, "Device Information Service"); if (!dis_sdp_handle) error("gatt: Failed to register DIS SDP record"); } static void gatt_srvc_change_write_cb(struct gatt_db_attribute *attrib, unsigned int id, uint16_t offset, const uint8_t *value, size_t len, uint8_t opcode, struct bt_att *att, void *user_data) { struct gatt_device *dev; bdaddr_t bdaddr; if (!get_dst_addr(att, &bdaddr)) { error("gatt: srvc_change_write_cb, could not obtain BDADDR"); return; } dev = find_device_by_addr(&bdaddr); if (!dev) { error("gatt: Could not find device ?!"); return; } if (!bt_device_is_bonded(&bdaddr)) { gatt_db_attribute_write_result(attrib, id, ATT_ECODE_AUTHORIZATION); return; } /* 2 octets are expected as CCC value */ if (len != 2) { gatt_db_attribute_write_result(attrib, id, ATT_ECODE_INVAL_ATTR_VALUE_LEN); return; } /* Set services changed indication value */ bt_store_gatt_ccc(&bdaddr, get_le16(value)); gatt_db_attribute_write_result(attrib, id, 0); } static void gatt_srvc_change_read_cb(struct gatt_db_attribute *attrib, unsigned int id, uint16_t offset, uint8_t opcode, struct bt_att *att, void *user_data) { struct gatt_device *dev; uint8_t pdu[2]; bdaddr_t bdaddr; if (!get_dst_addr(att, &bdaddr)) { error("gatt: srvc_change_read_cb, could not obtain BDADDR"); return; } dev = find_device_by_addr(&bdaddr); if (!dev) { error("gatt: Could not find device ?!"); return; } put_le16(bt_get_gatt_ccc(&dev->bdaddr), pdu); gatt_db_attribute_read_result(attrib, id, 0, pdu, sizeof(pdu)); } static void register_gatt_service(void) { struct gatt_db_attribute *service; uint16_t start_handle, end_handle; bt_uuid_t uuid; DBG(""); bt_uuid16_create(&uuid, 0x1801); service = gatt_db_add_service(gatt_db, &uuid, true, 4); bt_uuid16_create(&uuid, GATT_CHARAC_SERVICE_CHANGED); service_changed_attrib = gatt_db_service_add_characteristic(service, &uuid, GATT_PERM_NONE, GATT_CHR_PROP_INDICATE, NULL, NULL, NULL); bt_uuid16_create(&uuid, GATT_CLIENT_CHARAC_CFG_UUID); gatt_db_service_add_descriptor(service, &uuid, GATT_PERM_READ | GATT_PERM_WRITE, gatt_srvc_change_read_cb, gatt_srvc_change_write_cb, NULL); gatt_db_service_set_active(service, true); /* SDP */ bt_uuid16_create(&uuid, 0x1801); gatt_db_attribute_get_service_handles(service, &start_handle, &end_handle); gatt_sdp_handle = add_sdp_record(&uuid, start_handle, end_handle, "Generic Attribute Profile"); if (!gatt_sdp_handle) error("gatt: Failed to register GATT SDP record"); } static bool start_listening(void) { /* BR/EDR socket */ bredr_io = bt_io_listen(NULL, connect_confirm, NULL, NULL, NULL, BT_IO_OPT_SOURCE_TYPE, BDADDR_BREDR, BT_IO_OPT_PSM, ATT_PSM, BT_IO_OPT_SEC_LEVEL, BT_IO_SEC_LOW, BT_IO_OPT_INVALID); /* LE socket */ le_io = bt_io_listen(NULL, connect_confirm, NULL, NULL, NULL, BT_IO_OPT_SOURCE_TYPE, BDADDR_LE_PUBLIC, BT_IO_OPT_CID, ATT_CID, BT_IO_OPT_SEC_LEVEL, BT_IO_SEC_LOW, BT_IO_OPT_INVALID); if (!le_io && !bredr_io) { error("gatt: Failed to start listening IO"); return false; } return true; } static void gatt_paired_cb(const bdaddr_t *addr) { struct gatt_device *dev; char address[18]; struct app_connection *conn; dev = find_device_by_addr(addr); if (!dev) return; ba2str(addr, address); DBG("Paired device %s", address); /* conn without app is internal one used for search primary services */ conn = find_conn_without_app(dev); if (!conn) return; if (conn->timeout_id > 0) { g_source_remove(conn->timeout_id); conn->timeout_id = 0; } search_dev_for_srvc(conn, NULL); } static void gatt_unpaired_cb(const bdaddr_t *addr) { struct gatt_device *dev; char address[18]; dev = find_device_by_addr(addr); if (!dev) return; ba2str(addr, address); DBG("Unpaired device %s", address); queue_remove(gatt_devices, dev); destroy_device(dev); } bool bt_gatt_register(struct ipc *ipc, const bdaddr_t *addr) { DBG(""); if (!bt_paired_register(gatt_paired_cb)) { error("gatt: Could not register paired callback"); return false; } if (!bt_unpaired_register(gatt_unpaired_cb)) { error("gatt: Could not register unpaired callback"); return false; } if (!start_listening()) return false; crypto = bt_crypto_new(); if (!crypto) { error("gatt: Failed to setup crypto"); goto failed; } gatt_devices = queue_new(); gatt_apps = queue_new(); app_connections = queue_new(); listen_apps = queue_new(); services_sdp = queue_new(); gatt_db = gatt_db_new(); if (!gatt_db) { error("gatt: Failed to allocate memory for database"); goto failed; } if (!bt_le_register(le_device_found_handler)) { error("gatt: bt_le_register failed"); goto failed; } bacpy(&adapter_addr, addr); hal_ipc = ipc; ipc_register(hal_ipc, HAL_SERVICE_ID_GATT, cmd_handlers, G_N_ELEMENTS(cmd_handlers)); register_gap_service(); register_device_info_service(); register_gatt_service(); info("gatt: LE: %s BR/EDR: %s", le_io ? "enabled" : "disabled", bredr_io ? "enabled" : "disabled"); return true; failed: bt_paired_unregister(gatt_paired_cb); bt_unpaired_unregister(gatt_unpaired_cb); queue_destroy(gatt_apps, NULL); gatt_apps = NULL; queue_destroy(gatt_devices, NULL); gatt_devices = NULL; queue_destroy(app_connections, NULL); app_connections = NULL; queue_destroy(listen_apps, NULL); listen_apps = NULL; queue_destroy(services_sdp, NULL); services_sdp = NULL; gatt_db_unref(gatt_db); gatt_db = NULL; bt_crypto_unref(crypto); crypto = NULL; if (le_io) { g_io_channel_unref(le_io); le_io = NULL; } if (bredr_io) { g_io_channel_unref(bredr_io); bredr_io = NULL; } return false; } void bt_gatt_unregister(void) { DBG(""); ipc_unregister(hal_ipc, HAL_SERVICE_ID_GATT); hal_ipc = NULL; queue_destroy(app_connections, destroy_connection); app_connections = NULL; queue_destroy(gatt_apps, destroy_gatt_app); gatt_apps = NULL; queue_destroy(gatt_devices, destroy_device); gatt_devices = NULL; queue_destroy(services_sdp, free_service_sdp_record); services_sdp = NULL; queue_destroy(listen_apps, NULL); listen_apps = NULL; gatt_db_unref(gatt_db); gatt_db = NULL; if (le_io) { g_io_channel_unref(le_io); le_io = NULL; } if (bredr_io) { g_io_channel_unref(bredr_io); bredr_io = NULL; } if (gap_sdp_handle) { bt_adapter_remove_record(gap_sdp_handle); gap_sdp_handle = 0; } if (gatt_sdp_handle) { bt_adapter_remove_record(gatt_sdp_handle); gatt_sdp_handle = 0; } if (dis_sdp_handle) { bt_adapter_remove_record(dis_sdp_handle); dis_sdp_handle = 0; } bt_crypto_unref(crypto); crypto = NULL; bt_le_unregister(); bt_unpaired_unregister(gatt_unpaired_cb); } unsigned int bt_gatt_register_app(const char *uuid, gatt_type_t type, gatt_conn_cb_t func) { struct gatt_app *app; bt_uuid_t u, u128; bt_string_to_uuid(&u, uuid); bt_uuid_to_uuid128(&u, &u128); app = register_app((void *) &u128.value.u128, type); if (!app) return 0; app->func = func; return app->id; } bool bt_gatt_unregister_app(unsigned int id) { uint8_t status; status = unregister_app(id); return status != HAL_STATUS_FAILED; } bool bt_gatt_connect_app(unsigned int id, const bdaddr_t *addr) { uint8_t status; status = handle_connect(id, addr, false); return status != HAL_STATUS_FAILED; } bool bt_gatt_disconnect_app(unsigned int id, const bdaddr_t *addr) { struct app_connection match; struct app_connection *conn; struct gatt_device *device; struct gatt_app *app; app = find_app_by_id(id); if (!app) return false; device = find_device_by_addr(addr); if (!device) return false; match.device = device; match.app = app; conn = queue_remove_if(app_connections, match_connection_by_device_and_app, &match); if (!conn) return false; destroy_connection(conn); return true; } bool bt_gatt_add_autoconnect(unsigned int id, const bdaddr_t *addr) { struct gatt_device *dev; struct gatt_app *app; DBG(""); app = find_app_by_id(id); if (!app) { error("gatt: App ID=%d not found", id); return false; } dev = find_device_by_addr(addr); if (!dev) { error("gatt: Device not found"); return false; } /* Take reference of device for auto connect purpose */ if (queue_isempty(dev->autoconnect_apps)) device_ref(dev); if (!queue_find(dev->autoconnect_apps, NULL, INT_TO_PTR(id))) return queue_push_head(dev->autoconnect_apps, INT_TO_PTR(id)); return true; } void bt_gatt_remove_autoconnect(unsigned int id, const bdaddr_t *addr) { struct gatt_device *dev; DBG(""); dev = find_device_by_addr(addr); if (!dev) { error("gatt: Device not found"); return; } queue_remove(dev->autoconnect_apps, INT_TO_PTR(id)); if (queue_isempty(dev->autoconnect_apps)) remove_autoconnect_device(dev); } bluez-5.82/android/PaxHeaders/pts-l2cap.txt0000644000000000000000000000005012537515745015652 xustar0020 atime=1743516876 20 ctime=1743591289 bluez-5.82/android/pts-l2cap.txt0000644000000000000000000001674712537515745015352 0ustar00rootrootPTS test results for L2CAP PTS version: 6.1 Tested: 28-May-2015 Android version: 5.1 Kernel version: 4.1 Results: PASS test passed FAIL test failed INC test is inconclusive N/A test is disabled due to PICS setup ------------------------------------------------------------------------------- Test Name Result Notes ------------------------------------------------------------------------------- For all tests daemon should be stopped then: setprop ctl.start hciattach TC_COS_CED_BV_01_C PASS l2test -n -P 4113 TC_COS_CED_BV_03_C PASS l2test -y -N 1 -P 4113 TC_COS_CED_BV_04_C PASS l2test -n -P 4113 TC_COS_CED_BV_05_C PASS l2test -r -P 4113 TC_COS_CED_BV_07_C PASS l2test -n -P 4113 TC_COS_CED_BV_08_C PASS l2test -n -P 4113 TC_COS_CED_BV_09_C PASS l2test -n -P 4113 TC_COS_CED_BV_10_C N/A TC_COS_CED_BV_11_C PASS l2test -u -P 4113 TC_COS_CED_BI_01_C PASS TC_COS_CFD_BV_01_C PASS l2test -r -P 4113 TC_COS_CFD_BV_02_C PASS l2test -n -P 4113 TC_COS_CFD_BV_03_C PASS l2test -n -P 4113 TC_COS_CFD_BV_08_C PASS l2test -n -P 4113 TC_COS_CFD_BV_09_C PASS l2test -n -P 4113 TC_COS_CFD_BV_10_C N/A TC_COS_CFD_BV_11_C PASS l2test -n -P 4113 TC_COS_CFD_BV_12_C PASS l2test -n -P 4113 TC_COS_CFD_BV_13_C N/A TC_COS_IEX_BV_01_C PASS l2test -n -P 4113 TC_COS_IEX_BV_02_C PASS TC_COS_ECH_BV_01_C PASS TC_COS_ECH_BV_02_C PASS l2ping -c 1 TC_COS_CFC_BV_01_C PASS l2test -y -N 1 -b 40 -V le_public -P 37 TC_COS_CFC_BV_02_C PASS l2test -y -N 1 -b 1 -V le_public -P 37 TC_COS_CFC_BV_03_C PASS l2test -u -V le_public -P 37 TC_COS_CFC_BV_04_C PASS l2test -u -V le_public -P 37 TC_COS_CFC_BV_05_C PASS l2test -u -V le_public l2test -u -V le_public TC_CLS_CLR_BV_01_C N/A TC_CLS_UCD_BV_01_C PASS TC_CLS_UCD_BV_02_C PASS l2test -s -G -N 1 -P 4113 TC_CLS_UCD_BV_03_C PASS l2test -s -E -G -N 1 -P 4113 TC_EXF_BV_01_C PASS TC_EXF_BV_02_C PASS TC_EXF_BV_03_C PASS TC_EXF_BV_04_C N/A TC_EXF_BV_05_C PASS TC_EXF_BV_06_C N/A TC_CMC_BV_01_C PASS l2test -r -X ertm -P 4113 TC_CMC_BV_02_C PASS l2test -r -X ertm -P 4113 TC_CMC_BV_03_C PASS l2test -r -X ertm -P 4113 TC_CMC_BV_04_C PASS l2test -r -X streaming -P 4113 TC_CMC_BV_05_C PASS l2test -r -X streaming -P 4113 TC_CMC_BV_06_C PASS l2test -r -X streaming -P 4113 TC_CMC_BV_07_C PASS l2test -r -X ertm -P 4113 TC_CMC_BV_08_C PASS l2test -r -X streaming -P 4113 TC_CMC_BV_09_C PASS l2test -r -X basic -P 4113 TC_CMC_BV_10_C PASS l2test -n -P 4113 TC_CMC_BV_11_C PASS l2test -n -P 4113 TC_CMC_BV_12_C PASS l2test -z -X ertm TC_CMC_BV_13_C PASS l2test -z -X streaming TC_CMC_BV_14_C PASS l2test -r -X streaming -P 4113 TC_CMC_BV_15_C PASS l2test -r -X streaming -P 4113 TC_CMC_BI_01_C PASS l2test -r -X ertm -P 4113 TC_CMC_BI_02_C PASS l2test -r -X ertm -P 4113 TC_CMC_BI_03_C PASS l2test -r -X streaming -P 4113 TC_CMC_BI_04_C PASS l2test -r -X streaming -P 4113 TC_CMC_BI_05_C PASS l2test -r -X basic -P 4113 TC_CMC_BI_06_C PASS l2test -r -X basic -P 4113 TC_FOC_BV_01_C PASS l2test -r -X ertm -P 4113 -F 0 TC_FOC_BV_02_C PASS l2test -r -X ertm -P 4113 -F 0 TC_FOC_BV_03_C PASS l2test -r -X ertm -P 4113 -F 0 TC_OFS_BV_01_C PASS l2test -x -X ertm -P 4113 -F 0 -N 1 TC_OFS_BV_02_C PASS l2test -r -X ertm -P 4113 -F 0 TC_OFS_BV_03_C PASS l2test -x -X streaming -P 4113 -F 0 -N 1 TC_OFS_BV_04_C PASS l2test -d -X streaming -P 4113 -F 0 TC_OFS_BV_05_C PASS l2test -x -X ertm -P 4113 -N 1 TC_OFS_BV_06_C PASS l2test -r -X ertm -P 4113 TC_OFS_BV_07_C PASS l2test -x -X streaming -P 4113 -F 0 -N 1 TC_OFS_BV_08_C PASS l2test -d -X streaming -P 4113 TC_ERM_BV_01_C PASS l2test -x -X ertm -P 4113 -N 3 -Y 3 TC_ERM_BV_02_C PASS l2test -r -X ertm -P 4113 TC_ERM_BV_03_C PASS l2test -r -X ertm -P 4113 TC_ERM_BV_05_C PASS l2test -x -X ertm -P 4113 -N 2 -Y 2 TC_ERM_BV_06_C PASS l2test -x -X ertm -P 4113 -N 2 -Y 2 TC_ERM_BV_07_C PASS l2test -r -H 1000 -K 10000 -X ertm -P 4113 TC_ERM_BV_08_C PASS l2test -x -X ertm -P 4113 -N 1 TC_ERM_BV_09_C PASS l2test -X ertm -P 4113 TC_ERM_BV_10_C PASS l2test -x -X ertm -P 4113 -N 1 TC_ERM_BV_11_C PASS l2test -x -X ertm -P 4113 -N 1 -Q 1 TC_ERM_BV_12_C PASS l2test -x -X ertm -P 4113 -R -N 1 -Q 1 TC_ERM_BV_13_C PASS l2test -x -X ertm -P 4113 -N 2 TC_ERM_BV_14_C PASS l2test -x -X ertm -P 4113 -N 4 TC_ERM_BV_15_C PASS l2test -x -X ertm -P 4113 -N 4 TC_ERM_BV_16_C N/A TC_ERM_BV_17_C PASS l2test -X ertm -P 4113 TC_ERM_BV_18_C PASS l2test -x -X ertm -P 4113 -N 1 TC_ERM_BV_19_C PASS l2test -x -X ertm -P 4113 -N 1 TC_ERM_BV_20_C PASS l2test -x -X ertm -P 4113 -N 1 TC_ERM_BV_21_C PASS l2test -x -X ertm -P 4113 -D 2000 -N 2 TC_ERM_BV_22_C PASS l2test -r -H 1000 -K 10000 -X ertm -P 4113 TC_ERM_BV_23_C PASS l2test -x -X ertm -P 4113 -N 2 TC_ERM_BI_01_C N/A TC_ERM_BI_02_C PASS l2test -X ertm -P 4113 TC_ERM_BI_03_C PASS l2test -x -X ertm -P 4113 -N 2 TC_ERM_BI_04_C PASS l2test -x -X ertm -P 4113 -N 2 TC_ERM_BI_05_C PASS l2test -x -X ertm -P 4113 -N 2 TC_STM_BV_01_C PASS l2test -x -X streaming -P 4113 -N 3 -Y 3 TC_STM_BV_02_C PASS l2test -d -X streaming -P 4113 TC_STM_BV_03_C PASS l2test -x -X streaming -P 4113 -N 2 TC_STM_BV_11_C N/A TC_STM_BV_12_C N/A TC_STM_BV_13_C N/A TC_FIX_BV_01_C PASS l2test -z -P 4113 TC_FIX_BV_02_C N/A TC_EWC_BV_01_C N/A TC_EWC_BV_02_C N/A TC_EWC_BV_03_C N/A TC_LSC_BV_01_C N/A TC_LSC_BV_02_C N/A TC_LSC_BV_03_C N/A TC_LSC_BI_04_C N/A TC_LSC_BI_05_C N/A TC_LSC_BV_06_C N/A TC_LSC_BV_07_C N/A TC_LSC_BV_08_C N/A TC_LSC_BV_09_C N/A TC_LSC_BI_10_C N/A TC_LSC_BI_11_C N/A TC_LSC_BV_12_C N/A TC_CCH_BV_01_C N/A TC_CCH_BV_02_C N/A TC_CCH_BV_03_C N/A TC_CCH_BV_04_C N/A TC_ECF_BV_01_C N/A TC_ECF_BV_02_C N/A TC_ECF_BV_03_C N/A TC_ECF_BV_04_C N/A TC_ECF_BV_05_C N/A TC_ECF_BV_06_C N/A TC_ECF_BV_07_C N/A TC_ECF_BV_08_C N/A TC_LE_CPU_BV_01_C PASS btmgmt advertising on l2test -r -V le_public -J 4 TC_LE_CPU_BV_02_C PASS l2test -n -V le_public -J 4 TC_LE_CPU_BI_01_C PASS l2test -n -V le_public -J 4 TC_LE_CPU_BI_02_C PASS btmgmt advertising on l2test -r -V le_public -J 4 TC_LE_REJ_BI_01_C PASS l2test -n -V le_public -J 4 TC_LE_REJ_BI_02_C PASS l2test -n -V le_public -J 4 TC_LE_CFC_BV_01_C PASS l2test -n -V le_public -P 37 TC_LE_CFC_BV_02_C PASS l2test -n -V le_public -P 37 TC_LE_CFC_BV_03_C PASS l2test -x -N 1 -V le_public hcitool lecc hcitool ledc TC_LE_CFC_BV_04_C PASS l2test -n -V le_public -P 241 TC_LE_CFC_BV_05_C PASS l2test -r -V le_public -J 4 hcitool lecc hcitool ledc TC_LE_CFC_BV_06_C PASS l2test -s -N 10 -V le_public TC_LE_CFC_BV_07_C PASS l2test -u -V le_public TC_LE_CFC_BI_01_C PASS l2test -u -V le_public TC_LE_CFC_BV_08_C PASS l2test -n -V le_public -P 37 TC_LE_CFC_BV_09_C PASS l2test -n -V le_public -P 37 TC_LE_CFC_BV_16_C PASS l2test -n -V le_public -P 37 TC_LE_CFC_BV_17_C N/A TC_LE_CID_BV_01_C PASS PTS issue #12730 l2test -r -J 2 l2test -r -J 4 -V le_public hcitool cc hcitool lecc --static l2test -s -N 1 -C 0 -e 5 -D 10000 l2test -s -N 1 -C 0 -D 10000 -g 10000 -V le_public TC_LE_CID_BV_02_I PASS PTS issue #12730 l2test -r -J 2 l2test -r -J 4 -V le_public l2test -w -N 1 -C 0 -D 5000 -g 10000 l2test -w -N 1 -C 0 -D 5000 -e 5 -g 10000 -V le_public hcitool cc hcitool lecc --static bluez-5.82/android/PaxHeaders/avrcp-lib.h0000644000000000000000000000005014015011623015307 xustar0020 atime=1743516866 20 ctime=1743591278 bluez-5.82/android/avrcp-lib.h0000644000000000000000000003334214015011623014775 0ustar00rootroot/* SPDX-License-Identifier: LGPL-2.1-or-later */ /* * * BlueZ - Bluetooth protocol stack for Linux * * Copyright (C) 2014 Intel Corporation. All rights reserved. * * */ /* Control PDU ids */ #define AVRCP_GET_CAPABILITIES 0x10 #define AVRCP_LIST_PLAYER_ATTRIBUTES 0X11 #define AVRCP_LIST_PLAYER_VALUES 0x12 #define AVRCP_GET_CURRENT_PLAYER_VALUE 0x13 #define AVRCP_SET_PLAYER_VALUE 0x14 #define AVRCP_GET_PLAYER_ATTRIBUTE_TEXT 0x15 #define AVRCP_GET_PLAYER_VALUE_TEXT 0x16 #define AVRCP_DISPLAYABLE_CHARSET 0x17 #define AVRCP_CT_BATTERY_STATUS 0x18 #define AVRCP_GET_ELEMENT_ATTRIBUTES 0x20 #define AVRCP_GET_PLAY_STATUS 0x30 #define AVRCP_REGISTER_NOTIFICATION 0x31 #define AVRCP_REQUEST_CONTINUING 0x40 #define AVRCP_ABORT_CONTINUING 0x41 #define AVRCP_SET_ABSOLUTE_VOLUME 0x50 #define AVRCP_SET_ADDRESSED_PLAYER 0x60 #define AVRCP_SET_BROWSED_PLAYER 0x70 #define AVRCP_GET_FOLDER_ITEMS 0x71 #define AVRCP_CHANGE_PATH 0x72 #define AVRCP_GET_ITEM_ATTRIBUTES 0x73 #define AVRCP_PLAY_ITEM 0x74 #define AVRCP_SEARCH 0x80 #define AVRCP_ADD_TO_NOW_PLAYING 0x90 #define AVRCP_GENERAL_REJECT 0xA0 /* Notification events */ #define AVRCP_EVENT_STATUS_CHANGED 0x01 #define AVRCP_EVENT_TRACK_CHANGED 0x02 #define AVRCP_EVENT_TRACK_REACHED_END 0x03 #define AVRCP_EVENT_TRACK_REACHED_START 0x04 #define AVRCP_EVENT_PLAYBACK_POS_CHANGED 0x05 #define AVRCP_EVENT_SETTINGS_CHANGED 0x08 #define AVRCP_EVENT_NOW_PLAYING_CONTENT_CHANGED 0x09 #define AVRCP_EVENT_AVAILABLE_PLAYERS_CHANGED 0x0a #define AVRCP_EVENT_ADDRESSED_PLAYER_CHANGED 0x0b #define AVRCP_EVENT_UIDS_CHANGED 0x0c #define AVRCP_EVENT_VOLUME_CHANGED 0x0d #define AVRCP_EVENT_LAST AVRCP_EVENT_VOLUME_CHANGED /* Status codes */ #define AVRCP_STATUS_INVALID_COMMAND 0x00 #define AVRCP_STATUS_INVALID_PARAM 0x01 #define AVRCP_STATUS_PARAM_NOT_FOUND 0x02 #define AVRCP_STATUS_INTERNAL_ERROR 0x03 #define AVRCP_STATUS_SUCCESS 0x04 #define AVRCP_STATUS_UID_CHANGED 0x05 #define AVRCP_STATUS_NOT_DIRECTORY 0x08 #define AVRCP_STATUS_DOES_NOT_EXIST 0x09 #define AVRCP_STATUS_INVALID_SCOPE 0x0a #define AVRCP_STATUS_OUT_OF_BOUNDS 0x0b #define AVRCP_STATUS_INVALID_PLAYER_ID 0x11 #define AVRCP_STATUS_PLAYER_NOT_BROWSABLE 0x12 #define AVRCP_STATUS_NO_AVAILABLE_PLAYERS 0x15 #define AVRCP_STATUS_ADDRESSED_PLAYER_CHANGED 0x16 /* Capabilities for AVRCP_GET_CAPABILITIES pdu */ #define CAP_COMPANY_ID 0x02 #define CAP_EVENTS_SUPPORTED 0x03 /* Player Attributes */ #define AVRCP_ATTRIBUTE_ILEGAL 0x00 #define AVRCP_ATTRIBUTE_EQUALIZER 0x01 #define AVRCP_ATTRIBUTE_REPEAT_MODE 0x02 #define AVRCP_ATTRIBUTE_SHUFFLE 0x03 #define AVRCP_ATTRIBUTE_SCAN 0x04 #define AVRCP_ATTRIBUTE_LAST AVRCP_ATTRIBUTE_SCAN /* equalizer values */ #define AVRCP_EQUALIZER_OFF 0x01 #define AVRCP_EQUALIZER_ON 0x02 /* repeat mode values */ #define AVRCP_REPEAT_MODE_OFF 0x01 #define AVRCP_REPEAT_MODE_SINGLE 0x02 #define AVRCP_REPEAT_MODE_ALL 0x03 #define AVRCP_REPEAT_MODE_GROUP 0x04 /* shuffle values */ #define AVRCP_SHUFFLE_OFF 0x01 #define AVRCP_SHUFFLE_ALL 0x02 #define AVRCP_SHUFFLE_GROUP 0x03 /* scan values */ #define AVRCP_SCAN_OFF 0x01 #define AVRCP_SCAN_ALL 0x02 #define AVRCP_SCAN_GROUP 0x03 /* media attributes */ #define AVRCP_MEDIA_ATTRIBUTE_ILLEGAL 0x00 #define AVRCP_MEDIA_ATTRIBUTE_TITLE 0x01 #define AVRCP_MEDIA_ATTRIBUTE_ARTIST 0x02 #define AVRCP_MEDIA_ATTRIBUTE_ALBUM 0x03 #define AVRCP_MEDIA_ATTRIBUTE_TRACK 0x04 #define AVRCP_MEDIA_ATTRIBUTE_N_TRACKS 0x05 #define AVRCP_MEDIA_ATTRIBUTE_GENRE 0x06 #define AVRCP_MEDIA_ATTRIBUTE_DURATION 0x07 #define AVRCP_MEDIA_ATTRIBUTE_LAST AVRCP_MEDIA_ATTRIBUTE_DURATION /* Media Scope */ #define AVRCP_MEDIA_PLAYER_LIST 0x00 #define AVRCP_MEDIA_PLAYER_VFS 0x01 #define AVRCP_MEDIA_SEARCH 0x02 #define AVRCP_MEDIA_NOW_PLAYING 0x03 /* SDP features */ #define AVRCP_FEATURE_CATEGORY_1 0x0001 #define AVRCP_FEATURE_CATEGORY_2 0x0002 #define AVRCP_FEATURE_CATEGORY_3 0x0004 #define AVRCP_FEATURE_CATEGORY_4 0x0008 #define AVRCP_FEATURE_PLAYER_SETTINGS 0x0010 #define AVRCP_FEATURE_GROUP_NAVIGATION 0x0020 #define AVRCP_FEATURE_BROWSING 0x0040 #define AVRCP_FEATURE_MULTIPLE_PLAYERS 0x0080 /* Company IDs for vendor dependent commands */ #define IEEEID_BTSIG 0x001958 struct avrcp; struct avrcp_control_ind { int (*get_capabilities) (struct avrcp *session, uint8_t transaction, void *user_data); int (*list_attributes) (struct avrcp *session, uint8_t transaction, void *user_data); int (*get_attribute_text) (struct avrcp *session, uint8_t transaction, uint8_t number, uint8_t *attrs, void *user_data); int (*list_values) (struct avrcp *session, uint8_t transaction, uint8_t attr, void *user_data); int (*get_value_text) (struct avrcp *session, uint8_t transaction, uint8_t attr, uint8_t number, uint8_t *values, void *user_data); int (*get_value) (struct avrcp *session, uint8_t transaction, uint8_t number, uint8_t *attrs, void *user_data); int (*set_value) (struct avrcp *session, uint8_t transaction, uint8_t number, uint8_t *attrs, uint8_t *values, void *user_data); int (*get_play_status) (struct avrcp *session, uint8_t transaction, void *user_data); int (*get_element_attributes) (struct avrcp *session, uint8_t transaction, uint64_t uid, uint8_t number, uint32_t *attrs, void *user_data); int (*register_notification) (struct avrcp *session, uint8_t transaction, uint8_t event, uint32_t interval, void *user_data); int (*set_volume) (struct avrcp *session, uint8_t transaction, uint8_t volume, void *user_data); int (*set_addressed) (struct avrcp *session, uint8_t transaction, uint16_t id, void *user_data); int (*set_browsed) (struct avrcp *session, uint8_t transaction, uint16_t id, void *user_data); int (*get_folder_items) (struct avrcp *session, uint8_t transaction, uint8_t scope, uint32_t start, uint32_t end, uint16_t number, uint32_t *attrs, void *user_data); int (*change_path) (struct avrcp *session, uint8_t transaction, uint16_t counter, uint8_t direction, uint64_t uid, void *user_data); int (*get_item_attributes) (struct avrcp *session, uint8_t transaction, uint8_t scope, uint64_t uid, uint16_t counter, uint8_t number, uint32_t *attrs, void *user_data); int (*play_item) (struct avrcp *session, uint8_t transaction, uint8_t scope, uint64_t uid, uint16_t counter, void *user_data); int (*search) (struct avrcp *session, uint8_t transaction, const char *string, void *user_data); int (*add_to_now_playing) (struct avrcp *session, uint8_t transaction, uint8_t scope, uint64_t uid, uint16_t counter, void *user_data); }; struct avrcp_control_cfm { void (*get_capabilities) (struct avrcp *session, int err, uint8_t number, uint8_t *params, void *user_data); void (*list_attributes) (struct avrcp *session, int err, uint8_t number, uint8_t *attrs, void *user_data); void (*get_attribute_text) (struct avrcp *session, int err, uint8_t number, uint8_t *attrs, char **text, void *user_data); void (*list_values) (struct avrcp *session, int err, uint8_t number, uint8_t *values, void *user_data); void (*get_value_text) (struct avrcp *session, int err, uint8_t number, uint8_t *values, char **text, void *user_data); void (*get_value) (struct avrcp *session, int err, uint8_t number, uint8_t *attrs, uint8_t *values, void *user_data); void (*set_value) (struct avrcp *session, int err, void *user_data); void (*get_play_status) (struct avrcp *session, int err, uint8_t status, uint32_t position, uint32_t duration, void *user_data); void (*get_element_attributes) (struct avrcp *session, int err, uint8_t number, uint32_t *attrs, char **text, void *user_data); bool (*register_notification) (struct avrcp *session, int err, uint8_t code, uint8_t event, void *params, void *user_data); void (*set_volume) (struct avrcp *session, int err, uint8_t volume, void *user_data); void (*set_addressed) (struct avrcp *session, int err, void *user_data); void (*set_browsed) (struct avrcp *session, int err, uint16_t counter, uint32_t items, char *path, void *user_data); void (*get_folder_items) (struct avrcp *session, int err, uint16_t counter, uint16_t number, uint8_t *params, void *user_data); void (*change_path) (struct avrcp *session, int err, uint32_t items, void *user_data); void (*get_item_attributes) (struct avrcp *session, int err, uint8_t number, uint32_t *attrs, char **text, void *user_data); void (*play_item) (struct avrcp *session, int err, void *user_data); void (*search) (struct avrcp *session, int err, uint16_t counter, uint32_t items, void *user_data); void (*add_to_now_playing) (struct avrcp *session, int err, void *user_data); }; struct avrcp_passthrough_handler { uint8_t op; bool (*func) (struct avrcp *session, bool pressed, void *user_data); }; typedef void (*avrcp_destroy_cb_t) (void *user_data); struct avrcp *avrcp_new(int fd, size_t imtu, size_t omtu, uint16_t version); void avrcp_shutdown(struct avrcp *session); void avrcp_set_destroy_cb(struct avrcp *session, avrcp_destroy_cb_t cb, void *user_data); int avrcp_connect_browsing(struct avrcp *session, int fd, size_t imtu, size_t omtu); void avrcp_register_player(struct avrcp *session, const struct avrcp_control_ind *ind, const struct avrcp_control_cfm *cfm, void *user_data); void avrcp_set_passthrough_handlers(struct avrcp *session, const struct avrcp_passthrough_handler *handlers, void *user_data); int avrcp_init_uinput(struct avrcp *session, const char *name, const char *address); int avrcp_send(struct avrcp *session, uint8_t transaction, uint8_t code, uint8_t subunit, uint8_t pdu_id, const struct iovec *iov, int iov_cnt); int avrcp_get_capabilities(struct avrcp *session, uint8_t param); int avrcp_register_notification(struct avrcp *session, uint8_t event, uint32_t interval); int avrcp_list_player_attributes(struct avrcp *session); int avrcp_get_player_attribute_text(struct avrcp *session, uint8_t number, uint8_t *attrs); int avrcp_list_player_values(struct avrcp *session, uint8_t attr); int avrcp_get_player_value_text(struct avrcp *session, uint8_t attr, uint8_t number, uint8_t *values); int avrcp_set_player_value(struct avrcp *session, uint8_t number, uint8_t *attrs, uint8_t *values); int avrcp_get_current_player_value(struct avrcp *session, uint8_t number, uint8_t *attrs); int avrcp_get_play_status(struct avrcp *session); int avrcp_set_volume(struct avrcp *session, uint8_t volume); int avrcp_get_element_attributes(struct avrcp *session); int avrcp_set_addressed_player(struct avrcp *session, uint16_t player_id); int avrcp_set_browsed_player(struct avrcp *session, uint16_t player_id); int avrcp_get_folder_items(struct avrcp *session, uint8_t scope, uint32_t start, uint32_t end, uint8_t number, uint32_t *attrs); int avrcp_change_path(struct avrcp *session, uint8_t direction, uint64_t uid, uint16_t counter); int avrcp_get_item_attributes(struct avrcp *session, uint8_t scope, uint64_t uid, uint16_t counter, uint8_t number, uint32_t *attrs); int avrcp_play_item(struct avrcp *session, uint8_t scope, uint64_t uid, uint16_t counter); int avrcp_search(struct avrcp *session, const char *string); int avrcp_add_to_now_playing(struct avrcp *session, uint8_t scope, uint64_t uid, uint16_t counter); int avrcp_get_capabilities_rsp(struct avrcp *session, uint8_t transaction, uint8_t number, uint8_t *events); int avrcp_list_player_attributes_rsp(struct avrcp *session, uint8_t transaction, uint8_t number, uint8_t *attrs); int avrcp_get_player_attribute_text_rsp(struct avrcp *session, uint8_t transaction, uint8_t number, uint8_t *attrs, const char **text); int avrcp_list_player_values_rsp(struct avrcp *session, uint8_t transaction, uint8_t number, uint8_t *values); int avrcp_get_play_status_rsp(struct avrcp *session, uint8_t transaction, uint32_t position, uint32_t duration, uint8_t status); int avrcp_get_player_values_text_rsp(struct avrcp *session, uint8_t transaction, uint8_t number, uint8_t *values, const char **text); int avrcp_get_current_player_value_rsp(struct avrcp *session, uint8_t transaction, uint8_t number, uint8_t *attrs, uint8_t *values); int avrcp_set_player_value_rsp(struct avrcp *session, uint8_t transaction); int avrcp_get_element_attrs_rsp(struct avrcp *session, uint8_t transaction, uint8_t *params, size_t params_len); int avrcp_register_notification_rsp(struct avrcp *session, uint8_t transaction, uint8_t code, uint8_t event, void *data, size_t len); int avrcp_set_volume_rsp(struct avrcp *session, uint8_t transaction, uint8_t volume); int avrcp_set_addressed_player_rsp(struct avrcp *session, uint8_t transaction, uint8_t status); int avrcp_set_browsed_player_rsp(struct avrcp *session, uint8_t transaction, uint8_t status, uint16_t counter, uint32_t items, uint8_t depth, const char **folders); int avrcp_get_folder_items_rsp(struct avrcp *session, uint8_t transaction, uint8_t status, uint16_t counter, uint8_t number, uint8_t *type, uint16_t *len, uint8_t **params); int avrcp_change_path_rsp(struct avrcp *session, uint8_t transaction, uint8_t status, uint32_t items); int avrcp_get_item_attributes_rsp(struct avrcp *session, uint8_t transaction, uint8_t status, uint8_t number, uint32_t *attrs, const char **text); int avrcp_play_item_rsp(struct avrcp *session, uint8_t transaction, uint8_t status); int avrcp_search_rsp(struct avrcp *session, uint8_t transaction, uint8_t status, uint16_t counter, uint32_t items); int avrcp_add_to_now_playing_rsp(struct avrcp *session, uint8_t transaction, uint8_t status); int avrcp_send_passthrough(struct avrcp *session, uint32_t vendor, uint8_t op); bluez-5.82/android/PaxHeaders/a2dp-sink.c0000644000000000000000000000005014015011623015213 xustar0020 atime=1743516866 20 ctime=1743591278 bluez-5.82/android/a2dp-sink.c0000644000000000000000000000261014015011623014673 0ustar00rootroot// SPDX-License-Identifier: LGPL-2.1-or-later /* * * BlueZ - Bluetooth protocol stack for Linux * * Copyright (C) 2014 Intel Corporation. All rights reserved. * * */ #ifdef HAVE_CONFIG_H #include #endif #include #include #include "lib/bluetooth.h" #include "src/log.h" #include "hal-msg.h" #include "ipc.h" #include "a2dp-sink.h" static struct ipc *hal_ipc = NULL; static void bt_a2dp_sink_connect(const void *buf, uint16_t len) { /* TODO */ DBG(""); ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_A2DP_SINK, HAL_OP_A2DP_CONNECT, HAL_STATUS_UNSUPPORTED); } static void bt_a2dp_sink_disconnect(const void *buf, uint16_t len) { /* TODO */ DBG(""); ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_A2DP_SINK, HAL_OP_A2DP_DISCONNECT, HAL_STATUS_UNSUPPORTED); } static const struct ipc_handler cmd_handlers[] = { /* HAL_OP_A2DP_CONNECT */ { bt_a2dp_sink_connect, false, sizeof(struct hal_cmd_a2dp_connect) }, /* HAL_OP_A2DP_DISCONNECT */ { bt_a2dp_sink_disconnect, false, sizeof(struct hal_cmd_a2dp_disconnect) }, }; bool bt_a2dp_sink_register(struct ipc *ipc, const bdaddr_t *addr, uint8_t mode) { DBG(""); hal_ipc = ipc; ipc_register(hal_ipc, HAL_SERVICE_ID_A2DP_SINK, cmd_handlers, G_N_ELEMENTS(cmd_handlers)); return true; } void bt_a2dp_sink_unregister(void) { DBG(""); ipc_unregister(hal_ipc, HAL_SERVICE_ID_A2DP_SINK); hal_ipc = NULL; } bluez-5.82/android/PaxHeaders/pixit-gap.txt0000644000000000000000000000005012537515745015747 xustar0020 atime=1743516876 20 ctime=1743591289 bluez-5.82/android/pixit-gap.txt0000644000000000000000000000426012537515745015432 0ustar00rootrootGAP PIXIT for the PTS tool. PTS version: 6.1 * - different than PTS defaults & - should be set to IUT Bluetooth address # - should be set to IUT name Required PIXIT settings ------------------------------------------------------------------------------- Parameter Name Value ------------------------------------------------------------------------------- TSPX_bd_addr_iut 112233445566 (*&) TSPX_bd_addr_PTS C000DEADBEEF TSPX_broadcaster_class_of_device 100104 TSPX_observer_class_of_device 100104 TSPX_peripheral_class_of_device 100104 TSPX_central_class_of_device 100104 TSPX_security_enabled True TSPX_delete_link_key True TSPX_pin_code 0000 TSPX_time_guard 300000 TSPX_use_implicit_send True TSPX_use_dynamic_pin False TSPX_secure_simple_pairing_pass_key_confirmation False TSPX_using_public_device_address True TSPX_using_random_device_address False TSPX_lim_adv_timeout 30720 TSPX_gen_disc_adv_min 30720 TSPX_lim_disc_scan_min 10240 TSPX_gen_disc_scan_min 10240 TSPX_database_file Database-GAP.sig TSPX_iut_rx_mtu 23 TSPX_iut_private_address_interval 30000 (*) TSPX_iut_privacy_enabled False TSPX_psm 1001 TSPX_iut_valid_connection_interval_min 00C8 TSPX_iut_valid_conneciton_interval_max 0960 TSPX_iut_valid_connection_latency 0006 TSPX_iut_valid_timeout_multiplier 0962 TSPX_iut_connection_parameter_timeout 30000 TSPX_iut_invalid_connection_interval_min 0000 TSPX_iut_invalid_conneciton_interval_max 0000 TSPX_iut_invalid_connection_latency 0000 TSPX_iut_invalid_timeout_multiplier 0000 TSPX_LE_scan_interval 0010 TSPX_LE_scan_window 0010 TSPX_con_interval_min 0032 TSPX_con_interval_max 0046 TSPX_con_latency 0000 TSPX_supervision_timeout 07D0 TSPX_minimum_ce_length 0000 TSPX_maximum_ce_length 0000 TSPX_pairing_before_service_request False TSPX_iut_mandates_mitm False TSPX_encryption_before_service_request False TSPX_tester_appearance 0000 TSPX_iut_advertising_data_in_broadcasting_mode [set to default value] TSPX_iut_device_name_in_adv_packet_for_random_address PTS-66DE (#) ------------------------------------------------------------------------------- bluez-5.82/android/PaxHeaders/hal-avrcp.c0000644000000000000000000000005014015011623015300 xustar0020 atime=1743516862 20 ctime=1743591276 bluez-5.82/android/hal-avrcp.c0000644000000000000000000004024014015011623014761 0ustar00rootroot// SPDX-License-Identifier: Apache-2.0 /* * Copyright (C) 2014 Intel Corporation * */ #define _GNU_SOURCE #include #include #include #include #include "hal-utils.h" #include "hal-log.h" #include "hal.h" #include "hal-msg.h" #include "ipc-common.h" #include "hal-ipc.h" static const btrc_callbacks_t *cbs = NULL; static bool interface_ready(void) { return cbs != NULL; } static void handle_remote_features(void *buf, uint16_t len, int fd) { struct hal_ev_avrcp_remote_features *ev = buf; if (cbs->remote_features_cb) cbs->remote_features_cb((bt_bdaddr_t *) (ev->bdaddr), ev->features); } static void handle_get_play_status(void *buf, uint16_t len, int fd) { if (cbs->get_play_status_cb) cbs->get_play_status_cb(); } static void handle_list_player_attrs(void *buf, uint16_t len, int fd) { if (cbs->list_player_app_attr_cb) cbs->list_player_app_attr_cb(); } static void handle_list_player_values(void *buf, uint16_t len, int fd) { struct hal_ev_avrcp_list_player_values *ev = buf; if (cbs->list_player_app_values_cb) cbs->list_player_app_values_cb(ev->attr); } static void handle_get_player_values(void *buf, uint16_t len, int fd) { struct hal_ev_avrcp_get_player_values *ev = buf; btrc_player_attr_t attrs[4]; int i; if (!cbs->get_player_app_value_cb) return; /* Convert uint8_t array to btrc_player_attr_t array */ for (i = 0; i < ev->number; i++) attrs[i] = ev->attrs[i]; cbs->get_player_app_value_cb(ev->number, attrs); } static void handle_get_player_attrs_text(void *buf, uint16_t len, int fd) { struct hal_ev_avrcp_get_player_attrs_text *ev = buf; btrc_player_attr_t attrs[4]; int i; if (!cbs->get_player_app_attrs_text_cb) return; /* Convert uint8_t array to btrc_player_attr_t array */ for (i = 0; i < ev->number; i++) attrs[i] = ev->attrs[i]; cbs->get_player_app_attrs_text_cb(ev->number, attrs); } static void handle_get_player_values_text(void *buf, uint16_t len, int fd) { struct hal_ev_avrcp_get_player_values_text *ev = buf; if (cbs->get_player_app_values_text_cb) cbs->get_player_app_values_text_cb(ev->attr, ev->number, ev->values); } static void handle_set_player_value(void *buf, uint16_t len, int fd) { struct hal_ev_avrcp_set_player_values *ev = buf; struct hal_avrcp_player_attr_value *attrs; btrc_player_settings_t values; int i; if (!cbs->set_player_app_value_cb) return; attrs = (struct hal_avrcp_player_attr_value *) ev->attrs; /* Convert to btrc_player_settings_t */ values.num_attr = ev->number; for (i = 0; i < ev->number; i++) { values.attr_ids[i] = attrs[i].attr; values.attr_values[i] = attrs[i].value; } cbs->set_player_app_value_cb(&values); } static void handle_get_element_attrs(void *buf, uint16_t len, int fd) { struct hal_ev_avrcp_get_element_attrs *ev = buf; btrc_media_attr_t attrs[BTRC_MAX_APP_SETTINGS]; int i; if (!cbs->get_element_attr_cb) return; /* Convert uint8_t array to btrc_media_attr_t array */ for (i = 0; i < ev->number; i++) attrs[i] = ev->attrs[i]; cbs->get_element_attr_cb(ev->number, attrs); } static void handle_register_notification(void *buf, uint16_t len, int fd) { struct hal_ev_avrcp_register_notification *ev = buf; if (cbs->register_notification_cb) cbs->register_notification_cb(ev->event, ev->param); } static void handle_volume_changed(void *buf, uint16_t len, int fd) { struct hal_ev_avrcp_volume_changed *ev = buf; if (cbs->volume_change_cb) cbs->volume_change_cb(ev->volume, ev->type); } static void handle_passthrough_cmd(void *buf, uint16_t len, int fd) { struct hal_ev_avrcp_passthrough_cmd *ev = buf; if (cbs->passthrough_cmd_cb) cbs->passthrough_cmd_cb(ev->id, ev->state); } /* * handlers will be called from notification thread context, * index in table equals to 'opcode - HAL_MINIMUM_EVENT' */ static const struct hal_ipc_handler ev_handlers[] = { /* HAL_EV_AVRCP_REMOTE_FEATURES */ { handle_remote_features, false, sizeof(struct hal_ev_avrcp_remote_features) }, /* HAL_EV_AVRCP_GET_PLAY_STATUS */ { handle_get_play_status, false, 0 }, /* HAL_EV_AVRCP_LIST_PLAYER_ATTRS */ { handle_list_player_attrs, false, 0 }, /* HAL_EV_AVRCP_LIST_PLAYER_VALUES */ { handle_list_player_values, false, sizeof(struct hal_ev_avrcp_list_player_values) }, /* HAL_EV_AVRCP_GET_PLAYER_VALUES */ { handle_get_player_values, true, sizeof(struct hal_ev_avrcp_get_player_values) }, /* HAL_EV_AVRCP_GET_PLAYER_ATTRS_TEXT */ { handle_get_player_attrs_text, true, sizeof(struct hal_ev_avrcp_get_player_attrs_text) }, /* HAL_EV_AVRCP_GET_PLAYER_VALUES_TEXT */ { handle_get_player_values_text, true, sizeof(struct hal_ev_avrcp_get_player_values_text) }, /* HAL_EV_AVRCP_SET_PLAYER_VALUES */ { handle_set_player_value, true, sizeof(struct hal_ev_avrcp_set_player_values) }, /* HAL_EV_AVRCP_GET_ELEMENT_ATTRS */ { handle_get_element_attrs, true, sizeof(struct hal_ev_avrcp_get_element_attrs) }, /* HAL_EV_AVRCP_REGISTER_NOTIFICATION */ { handle_register_notification, false, sizeof(struct hal_ev_avrcp_register_notification) }, /* HAL_EV_AVRCP_VOLUME_CHANGED */ { handle_volume_changed, false, sizeof(struct hal_ev_avrcp_volume_changed) }, /* HAL_EV_AVRCP_PASSTHROUGH_CMD */ { handle_passthrough_cmd, false, sizeof(struct hal_ev_avrcp_passthrough_cmd) }, }; static bt_status_t init(btrc_callbacks_t *callbacks) { struct hal_cmd_register_module cmd; int ret; DBG(""); if (interface_ready()) return BT_STATUS_DONE; cbs = callbacks; hal_ipc_register(HAL_SERVICE_ID_AVRCP, ev_handlers, sizeof(ev_handlers) / sizeof(ev_handlers[0])); cmd.service_id = HAL_SERVICE_ID_AVRCP; cmd.mode = HAL_MODE_DEFAULT; cmd.max_clients = 1; ret = hal_ipc_cmd(HAL_SERVICE_ID_CORE, HAL_OP_REGISTER_MODULE, sizeof(cmd), &cmd, NULL, NULL, NULL); if (ret != BT_STATUS_SUCCESS) { cbs = NULL; hal_ipc_unregister(HAL_SERVICE_ID_AVRCP); } return ret; } static bt_status_t get_play_status_rsp(btrc_play_status_t status, uint32_t song_len, uint32_t song_pos) { struct hal_cmd_avrcp_get_play_status cmd; DBG(""); if (!interface_ready()) return BT_STATUS_NOT_READY; cmd.status = status; cmd.duration = song_len; cmd.position = song_pos; return hal_ipc_cmd(HAL_SERVICE_ID_AVRCP, HAL_OP_AVRCP_GET_PLAY_STATUS, sizeof(cmd), &cmd, NULL, NULL, NULL); } static bt_status_t list_player_app_attr_rsp(int num_attr, btrc_player_attr_t *p_attrs) { char buf[IPC_MTU]; struct hal_cmd_avrcp_list_player_attrs *cmd = (void *) buf; size_t len; DBG(""); if (!interface_ready()) return BT_STATUS_NOT_READY; if (num_attr < 0) return BT_STATUS_PARM_INVALID; len = sizeof(*cmd) + num_attr; if (len > IPC_MTU) return BT_STATUS_PARM_INVALID; cmd->number = num_attr; memcpy(cmd->attrs, p_attrs, num_attr); return hal_ipc_cmd(HAL_SERVICE_ID_AVRCP, HAL_OP_AVRCP_LIST_PLAYER_ATTRS, len, cmd, NULL, NULL, NULL); } static bt_status_t list_player_app_value_rsp(int num_val, uint8_t *p_vals) { char buf[IPC_MTU]; struct hal_cmd_avrcp_list_player_values *cmd = (void *) buf; size_t len; DBG(""); if (!interface_ready()) return BT_STATUS_NOT_READY; if (num_val < 0) return BT_STATUS_PARM_INVALID; len = sizeof(*cmd) + num_val; if (len > IPC_MTU) return BT_STATUS_PARM_INVALID; cmd->number = num_val; memcpy(cmd->values, p_vals, num_val); return hal_ipc_cmd(HAL_SERVICE_ID_AVRCP, HAL_OP_AVRCP_LIST_PLAYER_VALUES, len, cmd, NULL, NULL, NULL); } static bt_status_t get_player_app_value_rsp(btrc_player_settings_t *p_vals) { char buf[IPC_MTU]; struct hal_cmd_avrcp_get_player_attrs *cmd = (void *) buf; size_t len, attrs_len; int i; DBG(""); if (!interface_ready()) return BT_STATUS_NOT_READY; if (!p_vals) return BT_STATUS_PARM_INVALID; attrs_len = p_vals->num_attr * sizeof(struct hal_avrcp_player_attr_value); len = sizeof(*cmd) + attrs_len; if (len > IPC_MTU) return BT_STATUS_PARM_INVALID; cmd->number = p_vals->num_attr; for (i = 0; i < p_vals->num_attr; i++) { cmd->attrs[i].attr = p_vals->attr_ids[i]; cmd->attrs[i].value = p_vals->attr_values[i]; } return hal_ipc_cmd(HAL_SERVICE_ID_AVRCP, HAL_OP_AVRCP_GET_PLAYER_ATTRS, len, cmd, NULL, NULL, NULL); } static int write_text(uint8_t *ptr, uint8_t id, uint8_t *text, size_t *len) { struct hal_avrcp_player_setting_text *value = (void *) ptr; size_t attr_len = sizeof(*value); if (attr_len + *len > IPC_MTU) return 0; value->id = id; value->len = strnlen((const char *) text, BTRC_MAX_ATTR_STR_LEN); *len += attr_len; if (value->len + *len > IPC_MTU) value->len = IPC_MTU - *len; memcpy(value->text, text, value->len); *len += value->len; return attr_len + value->len; } static uint8_t write_player_setting_text(uint8_t *ptr, uint8_t num_attr, btrc_player_setting_text_t *p_attrs, size_t *len) { int i; for (i = 0; i < num_attr && *len < IPC_MTU; i++) { int ret; ret = write_text(ptr, p_attrs[i].id, p_attrs[i].text, len); if (ret == 0) break; ptr += ret; } return i; } static bt_status_t get_player_app_attr_text_rsp(int num_attr, btrc_player_setting_text_t *p_attrs) { char buf[IPC_MTU]; struct hal_cmd_avrcp_get_player_attrs_text *cmd = (void *) buf; uint8_t *ptr; size_t len; DBG(""); if (!interface_ready()) return BT_STATUS_NOT_READY; if (num_attr < 0 || num_attr > BTRC_MAX_APP_SETTINGS) return BT_STATUS_PARM_INVALID; len = sizeof(*cmd); ptr = (uint8_t *) &cmd->attrs[0]; cmd->number = write_player_setting_text(ptr, num_attr, p_attrs, &len); return hal_ipc_cmd(HAL_SERVICE_ID_AVRCP, HAL_OP_AVRCP_GET_PLAYER_ATTRS_TEXT, len, cmd, NULL, NULL, NULL); } static bt_status_t get_player_app_value_text_rsp(int num_val, btrc_player_setting_text_t *p_vals) { char buf[IPC_MTU]; struct hal_cmd_avrcp_get_player_values_text *cmd = (void *) buf; uint8_t *ptr; size_t len; DBG(""); if (!interface_ready()) return BT_STATUS_NOT_READY; if (num_val < 0) return BT_STATUS_PARM_INVALID; len = sizeof(*cmd); ptr = (uint8_t *) &cmd->values[0]; cmd->number = write_player_setting_text(ptr, num_val, p_vals, &len); return hal_ipc_cmd(HAL_SERVICE_ID_AVRCP, HAL_OP_AVRCP_GET_PLAYER_VALUES_TEXT, len, cmd, NULL, NULL, NULL); } static uint8_t write_element_attr_text(uint8_t *ptr, uint8_t num_attr, btrc_element_attr_val_t *p_attrs, size_t *len) { int i; for (i = 0; i < num_attr && *len < IPC_MTU; i++) { int ret; ret = write_text(ptr, p_attrs[i].attr_id, p_attrs[i].text, len); if (ret == 0) break; ptr += ret; } return i; } static bt_status_t get_element_attr_rsp(uint8_t num_attr, btrc_element_attr_val_t *p_attrs) { char buf[IPC_MTU]; struct hal_cmd_avrcp_get_element_attrs_text *cmd = (void *) buf; size_t len; uint8_t *ptr; DBG(""); if (!interface_ready()) return BT_STATUS_NOT_READY; len = sizeof(*cmd); ptr = (uint8_t *) &cmd->values[0]; cmd->number = write_element_attr_text(ptr, num_attr, p_attrs, &len); return hal_ipc_cmd(HAL_SERVICE_ID_AVRCP, HAL_OP_AVRCP_GET_ELEMENT_ATTRS_TEXT, len, cmd, NULL, NULL, NULL); } static bt_status_t set_player_app_value_rsp(btrc_status_t rsp_status) { struct hal_cmd_avrcp_set_player_attrs_value cmd; DBG(""); if (!interface_ready()) return BT_STATUS_NOT_READY; cmd.status = rsp_status; return hal_ipc_cmd(HAL_SERVICE_ID_AVRCP, HAL_OP_AVRCP_SET_PLAYER_ATTRS_VALUE, sizeof(cmd), &cmd, NULL, NULL, NULL); } static bt_status_t play_status_changed_rsp(btrc_notification_type_t type, btrc_play_status_t *play_status) { char buf[IPC_MTU]; struct hal_cmd_avrcp_register_notification *cmd = (void *) buf; size_t len; cmd->event = BTRC_EVT_PLAY_STATUS_CHANGED; cmd->type = type; cmd->len = 1; memcpy(cmd->data, play_status, cmd->len); len = sizeof(*cmd) + cmd->len; return hal_ipc_cmd(HAL_SERVICE_ID_AVRCP, HAL_OP_AVRCP_REGISTER_NOTIFICATION, len, cmd, NULL, NULL, NULL); } static bt_status_t track_change_rsp(btrc_notification_type_t type, btrc_uid_t *track) { char buf[IPC_MTU]; struct hal_cmd_avrcp_register_notification *cmd = (void *) buf; size_t len; cmd->event = BTRC_EVT_TRACK_CHANGE; cmd->type = type; cmd->len = sizeof(*track); memcpy(cmd->data, track, cmd->len); len = sizeof(*cmd) + cmd->len; return hal_ipc_cmd(HAL_SERVICE_ID_AVRCP, HAL_OP_AVRCP_REGISTER_NOTIFICATION, len, cmd, NULL, NULL, NULL); } static bt_status_t track_reached_end_rsp(btrc_notification_type_t type) { struct hal_cmd_avrcp_register_notification cmd; cmd.event = BTRC_EVT_TRACK_REACHED_END; cmd.type = type; cmd.len = 0; return hal_ipc_cmd(HAL_SERVICE_ID_AVRCP, HAL_OP_AVRCP_REGISTER_NOTIFICATION, sizeof(cmd), &cmd, NULL, NULL, NULL); } static bt_status_t track_reached_start_rsp(btrc_notification_type_t type) { struct hal_cmd_avrcp_register_notification cmd; cmd.event = BTRC_EVT_TRACK_REACHED_START; cmd.type = type; cmd.len = 0; return hal_ipc_cmd(HAL_SERVICE_ID_AVRCP, HAL_OP_AVRCP_REGISTER_NOTIFICATION, sizeof(cmd), &cmd, NULL, NULL, NULL); } static bt_status_t play_pos_changed_rsp(btrc_notification_type_t type, uint32_t *song_pos) { char buf[IPC_MTU]; struct hal_cmd_avrcp_register_notification *cmd = (void *) buf; size_t len; cmd->event = BTRC_EVT_PLAY_POS_CHANGED; cmd->type = type; cmd->len = sizeof(*song_pos); memcpy(cmd->data, song_pos, cmd->len); len = sizeof(*cmd) + cmd->len; return hal_ipc_cmd(HAL_SERVICE_ID_AVRCP, HAL_OP_AVRCP_REGISTER_NOTIFICATION, len, cmd, NULL, NULL, NULL); } static bt_status_t settings_changed_rsp(btrc_notification_type_t type, btrc_player_settings_t *player_setting) { char buf[IPC_MTU]; struct hal_cmd_avrcp_register_notification *cmd = (void *) buf; struct hal_avrcp_player_attr_value *attrs; size_t len, param_len; int i; param_len = player_setting->num_attr * sizeof(*attrs); len = sizeof(*cmd) + param_len; if (len > IPC_MTU) return BT_STATUS_PARM_INVALID; cmd->event = BTRC_EVT_APP_SETTINGS_CHANGED; cmd->type = type; cmd->len = param_len; attrs = (struct hal_avrcp_player_attr_value *) &cmd->data[0]; for (i = 0; i < player_setting->num_attr; i++) { attrs[i].attr = player_setting->attr_ids[i]; attrs[i].value = player_setting->attr_values[i]; } return hal_ipc_cmd(HAL_SERVICE_ID_AVRCP, HAL_OP_AVRCP_REGISTER_NOTIFICATION, len, cmd, NULL, NULL, NULL); } static bt_status_t register_notification_rsp(btrc_event_id_t event_id, btrc_notification_type_t type, btrc_register_notification_t *p_param) { DBG(""); if (!interface_ready()) return BT_STATUS_NOT_READY; switch (event_id) { case BTRC_EVT_PLAY_STATUS_CHANGED: return play_status_changed_rsp(type, &p_param->play_status); case BTRC_EVT_TRACK_CHANGE: return track_change_rsp(type, &p_param->track); case BTRC_EVT_TRACK_REACHED_END: return track_reached_end_rsp(type); case BTRC_EVT_TRACK_REACHED_START: return track_reached_start_rsp(type); case BTRC_EVT_PLAY_POS_CHANGED: return play_pos_changed_rsp(type, &p_param->song_pos); case BTRC_EVT_APP_SETTINGS_CHANGED: return settings_changed_rsp(type, &p_param->player_setting); default: return BT_STATUS_PARM_INVALID; } } static bt_status_t set_volume(uint8_t volume) { struct hal_cmd_avrcp_set_volume cmd; DBG(""); if (!interface_ready()) return BT_STATUS_NOT_READY; cmd.value = volume; return hal_ipc_cmd(HAL_SERVICE_ID_AVRCP, HAL_OP_AVRCP_SET_VOLUME, sizeof(cmd), &cmd, NULL, NULL, NULL); } static void cleanup(void) { struct hal_cmd_unregister_module cmd; DBG(""); if (!interface_ready()) return; cmd.service_id = HAL_SERVICE_ID_AVRCP; hal_ipc_cmd(HAL_SERVICE_ID_CORE, HAL_OP_UNREGISTER_MODULE, sizeof(cmd), &cmd, NULL, NULL, NULL); hal_ipc_unregister(HAL_SERVICE_ID_AVRCP); cbs = NULL; } static btrc_interface_t iface = { .size = sizeof(iface), .init = init, .get_play_status_rsp = get_play_status_rsp, .list_player_app_attr_rsp = list_player_app_attr_rsp, .list_player_app_value_rsp = list_player_app_value_rsp, .get_player_app_value_rsp = get_player_app_value_rsp, .get_player_app_attr_text_rsp = get_player_app_attr_text_rsp, .get_player_app_value_text_rsp = get_player_app_value_text_rsp, .get_element_attr_rsp = get_element_attr_rsp, .set_player_app_value_rsp = set_player_app_value_rsp, .register_notification_rsp = register_notification_rsp, .set_volume = set_volume, .cleanup = cleanup }; btrc_interface_t *bt_get_avrcp_interface(void) { return &iface; } bluez-5.82/android/PaxHeaders/main.c0000644000000000000000000000005014015011623014347 xustar0020 atime=1743516864 20 ctime=1743591278 bluez-5.82/android/main.c0000644000000000000000000003762414015011623014044 0ustar00rootroot// SPDX-License-Identifier: LGPL-2.1-or-later /* * * BlueZ - Bluetooth protocol stack for Linux * * Copyright (C) 2013-2014 Intel Corporation. All rights reserved. * * */ #ifdef HAVE_CONFIG_H #include #endif #define _GNU_SOURCE #include #include #include #include #include #include #include #include #include #include #if defined(ANDROID) #include #include #endif #include #include "lib/bluetooth.h" #include "lib/sdp.h" #include "src/log.h" #include "src/sdpd.h" #include "src/shared/util.h" #include "ipc-common.h" #include "ipc.h" #include "bluetooth.h" #include "socket.h" #include "hidhost.h" #include "hal-msg.h" #include "a2dp.h" #include "pan.h" #include "avrcp.h" #include "handsfree.h" #include "gatt.h" #include "health.h" #include "handsfree-client.h" #include "map-client.h" #include "utils.h" #define DEFAULT_VENDOR "BlueZ" #define DEFAULT_MODEL "BlueZ for Android" #define DEFAULT_NAME "BlueZ for Android" #define STARTUP_GRACE_SECONDS 5 #define SHUTDOWN_GRACE_SECONDS 5 static char *config_vendor = NULL; static char *config_model = NULL; static char *config_name = NULL; static char *config_serial = NULL; static char *config_fw_rev = NULL; static char *config_hw_rev = NULL; static uint64_t config_system_id = 0; static uint16_t config_pnp_source = 0x0002; /* USB */ static uint16_t config_pnp_vendor = 0x1d6b; /* Linux Foundation */ static uint16_t config_pnp_product = 0x0247; /* BlueZ for Android */ static uint16_t config_pnp_version = 0x0000; static guint quit_timeout = 0; static bdaddr_t adapter_bdaddr; static GMainLoop *event_loop; static struct ipc *hal_ipc = NULL; static bool services[HAL_SERVICE_ID_MAX + 1] = { false }; const char *bt_config_get_vendor(void) { if (config_vendor) return config_vendor; return DEFAULT_VENDOR; } const char *bt_config_get_name(void) { if (config_name) return config_name; return DEFAULT_NAME; } const char *bt_config_get_model(void) { if (config_model) return config_model; return DEFAULT_MODEL; } const char *bt_config_get_serial(void) { return config_serial; } const char *bt_config_get_fw_rev(void) { return config_fw_rev; } const char *bt_config_get_hw_rev(void) { return config_hw_rev; } uint64_t bt_config_get_system_id(void) { return config_system_id; } uint16_t bt_config_get_pnp_source(void) { return config_pnp_source; } uint16_t bt_config_get_pnp_vendor(void) { return config_pnp_vendor; } uint16_t bt_config_get_pnp_product(void) { return config_pnp_product; } uint16_t bt_config_get_pnp_version(void) { return config_pnp_version; } static void service_register(const void *buf, uint16_t len) { const struct hal_cmd_register_module *m = buf; uint8_t status; if (m->service_id > HAL_SERVICE_ID_MAX || services[m->service_id]) { status = HAL_STATUS_FAILED; goto failed; } switch (m->service_id) { case HAL_SERVICE_ID_BLUETOOTH: if (!bt_bluetooth_register(hal_ipc, m->mode)) { status = HAL_STATUS_FAILED; goto failed; } break; case HAL_SERVICE_ID_SOCKET: bt_socket_register(hal_ipc, &adapter_bdaddr, m->mode); break; case HAL_SERVICE_ID_HIDHOST: if (!bt_hid_register(hal_ipc, &adapter_bdaddr, m->mode)) { status = HAL_STATUS_FAILED; goto failed; } break; case HAL_SERVICE_ID_A2DP: if (!bt_a2dp_register(hal_ipc, &adapter_bdaddr, m->mode)) { status = HAL_STATUS_FAILED; goto failed; } break; case HAL_SERVICE_ID_PAN: if (!bt_pan_register(hal_ipc, &adapter_bdaddr, m->mode)) { status = HAL_STATUS_FAILED; goto failed; } break; case HAL_SERVICE_ID_AVRCP: if (!bt_avrcp_register(hal_ipc, &adapter_bdaddr, m->mode)) { status = HAL_STATUS_FAILED; goto failed; } break; case HAL_SERVICE_ID_HANDSFREE: if (!bt_handsfree_register(hal_ipc, &adapter_bdaddr, m->mode, m->max_clients)) { status = HAL_STATUS_FAILED; goto failed; } break; case HAL_SERVICE_ID_GATT: if (!bt_gatt_register(hal_ipc, &adapter_bdaddr)) { status = HAL_STATUS_FAILED; goto failed; } break; case HAL_SERVICE_ID_HEALTH: if (!bt_health_register(hal_ipc, &adapter_bdaddr, m->mode)) { status = HAL_STATUS_FAILED; goto failed; } break; case HAL_SERVICE_ID_HANDSFREE_CLIENT: if (!bt_hf_client_register(hal_ipc, &adapter_bdaddr)) { status = HAL_STATUS_FAILED; goto failed; } break; case HAL_SERVICE_ID_MAP_CLIENT: if (!bt_map_client_register(hal_ipc, &adapter_bdaddr, m->mode)) { status = HAL_STATUS_FAILED; goto failed; } break; default: DBG("service %u not supported", m->service_id); status = HAL_STATUS_FAILED; goto failed; } services[m->service_id] = true; status = HAL_STATUS_SUCCESS; info("Service ID=%u registered", m->service_id); failed: ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_CORE, HAL_OP_REGISTER_MODULE, status); } static bool unregister_service(uint8_t id) { if (id > HAL_SERVICE_ID_MAX || !services[id]) return false; switch (id) { case HAL_SERVICE_ID_BLUETOOTH: bt_bluetooth_unregister(); break; case HAL_SERVICE_ID_SOCKET: bt_socket_unregister(); break; case HAL_SERVICE_ID_HIDHOST: bt_hid_unregister(); break; case HAL_SERVICE_ID_A2DP: bt_a2dp_unregister(); break; case HAL_SERVICE_ID_PAN: bt_pan_unregister(); break; case HAL_SERVICE_ID_AVRCP: bt_avrcp_unregister(); break; case HAL_SERVICE_ID_HANDSFREE: bt_handsfree_unregister(); break; case HAL_SERVICE_ID_GATT: bt_gatt_unregister(); break; case HAL_SERVICE_ID_HEALTH: bt_health_unregister(); break; case HAL_SERVICE_ID_HANDSFREE_CLIENT: bt_hf_client_unregister(); break; case HAL_SERVICE_ID_MAP_CLIENT: bt_map_client_unregister(); break; default: DBG("service %u not supported", id); return false; } services[id] = false; return true; } static void service_unregister(const void *buf, uint16_t len) { const struct hal_cmd_unregister_module *m = buf; uint8_t status; if (!unregister_service(m->service_id)) { status = HAL_STATUS_FAILED; goto failed; } status = HAL_STATUS_SUCCESS; info("Service ID=%u unregistered", m->service_id); failed: ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_CORE, HAL_OP_UNREGISTER_MODULE, status); } static char *get_prop(char *prop, uint16_t len, const uint8_t *val) { /* TODO should fail if set more than once ? */ free(prop); prop = malloc0(len); if (!prop) return NULL; memcpy(prop, val, len); prop[len - 1] = '\0'; return prop; } static void parse_pnp_id(uint16_t len, const uint8_t *val) { int result; uint16_t vendor, product, version , source; char *pnp; /* version is optional */ version = config_pnp_version; pnp = get_prop(NULL, len, val); if (!pnp) return; DBG("pnp_id %s", pnp); result = sscanf(pnp, "bluetooth:%4hx:%4hx:%4hx", &vendor, &product, &version); if (result != EOF && result >= 2) { source = 0x0001; goto done; } result = sscanf(pnp, "usb:%4hx:%4hx:%4hx", &vendor, &product, &version); if (result != EOF && result >= 2) { source = 0x0002; goto done; } free(pnp); return; done: free(pnp); config_pnp_source = source; config_pnp_vendor = vendor; config_pnp_product = product; config_pnp_version = version; } static void parse_system_id(uint16_t len, const uint8_t *val) { uint64_t res; char *id; id = get_prop(NULL, len, val); if (!id) return; res = strtoull(id, NULL, 16); if (res == ULLONG_MAX && errno == ERANGE) goto done; config_system_id = res; done: free(id); } static void configuration(const void *buf, uint16_t len) { const struct hal_cmd_configuration *cmd = buf; const struct hal_config_prop *prop; unsigned int i; buf += sizeof(*cmd); len -= sizeof(*cmd); for (i = 0; i < cmd->num; i++) { prop = buf; if (len < sizeof(*prop) || len < sizeof(*prop) + prop->len) { error("Invalid configuration command, terminating"); raise(SIGTERM); return; } switch (prop->type) { case HAL_CONFIG_VENDOR: config_vendor = get_prop(config_vendor, prop->len, prop->val); DBG("vendor %s", config_vendor); break; case HAL_CONFIG_NAME: config_name = get_prop(config_name, prop->len, prop->val); DBG("name %s", config_name); break; case HAL_CONFIG_MODEL: config_model = get_prop(config_model, prop->len, prop->val); DBG("model %s", config_model); break; case HAL_CONFIG_SERIAL_NUMBER: config_serial = get_prop(config_serial, prop->len, prop->val); DBG("serial %s", config_serial); break; case HAL_CONFIG_SYSTEM_ID: parse_system_id(prop->len, prop->val); break; case HAL_CONFIG_PNP_ID: parse_pnp_id(prop->len, prop->val); break; case HAL_CONFIG_FW_REV: config_fw_rev = get_prop(config_fw_rev, prop->len, prop->val); DBG("fw_rev %s", config_fw_rev); break; case HAL_CONFIG_HW_REV: config_hw_rev = get_prop(config_hw_rev, prop->len, prop->val); DBG("hw_rev %s", config_hw_rev); break; default: error("Invalid configuration option (%u), terminating", prop->type); raise(SIGTERM); return; } buf += sizeof(*prop) + prop->len; len -= sizeof(*prop) + prop->len; } ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_CORE, HAL_OP_CONFIGURATION, HAL_STATUS_SUCCESS); } static const struct ipc_handler cmd_handlers[] = { /* HAL_OP_REGISTER_MODULE */ { service_register, false, sizeof(struct hal_cmd_register_module) }, /* HAL_OP_UNREGISTER_MODULE */ { service_unregister, false, sizeof(struct hal_cmd_unregister_module) }, /* HAL_OP_CONFIGURATION */ { configuration, true, sizeof(struct hal_cmd_configuration) }, }; static void bluetooth_stopped(void) { g_main_loop_quit(event_loop); } static gboolean quit_eventloop(gpointer user_data) { g_main_loop_quit(event_loop); quit_timeout = 0; return FALSE; } static void stop_bluetooth(void) { static bool __stop = false; if (__stop) return; __stop = true; if (!bt_bluetooth_stop(bluetooth_stopped)) { g_main_loop_quit(event_loop); return; } quit_timeout = g_timeout_add_seconds(SHUTDOWN_GRACE_SECONDS, quit_eventloop, NULL); } static void ipc_disconnected(void *data) { stop_bluetooth(); } static void adapter_ready(int err, const bdaddr_t *addr) { if (err < 0) { error("Adapter initialization failed: %s", strerror(-err)); exit(EXIT_FAILURE); } bacpy(&adapter_bdaddr, addr); if (quit_timeout > 0) { g_source_remove(quit_timeout); quit_timeout = 0; } info("Adapter initialized"); hal_ipc = ipc_init(BLUEZ_HAL_SK_PATH, sizeof(BLUEZ_HAL_SK_PATH), HAL_SERVICE_ID_MAX, true, ipc_disconnected, NULL); if (!hal_ipc) { error("Failed to initialize IPC"); exit(EXIT_FAILURE); } ipc_register(hal_ipc, HAL_SERVICE_ID_CORE, cmd_handlers, G_N_ELEMENTS(cmd_handlers)); } static gboolean signal_handler(GIOChannel *channel, GIOCondition cond, gpointer user_data) { static bool __terminated = false; struct signalfd_siginfo si; ssize_t result; int fd; if (cond & (G_IO_NVAL | G_IO_ERR | G_IO_HUP)) return FALSE; fd = g_io_channel_unix_get_fd(channel); result = read(fd, &si, sizeof(si)); if (result != sizeof(si)) return FALSE; switch (si.ssi_signo) { case SIGINT: case SIGTERM: if (!__terminated) { info("Terminating"); stop_bluetooth(); } __terminated = true; break; } return TRUE; } static guint setup_signalfd(void) { GIOChannel *channel; guint source; sigset_t mask; int fd; sigemptyset(&mask); sigaddset(&mask, SIGINT); sigaddset(&mask, SIGTERM); if (sigprocmask(SIG_BLOCK, &mask, NULL) < 0) { perror("Failed to set signal mask"); return 0; } fd = signalfd(-1, &mask, 0); if (fd < 0) { perror("Failed to create signal descriptor"); return 0; } channel = g_io_channel_unix_new(fd); g_io_channel_set_close_on_unref(channel, TRUE); g_io_channel_set_encoding(channel, NULL, NULL); g_io_channel_set_buffered(channel, FALSE); source = g_io_add_watch(channel, G_IO_IN | G_IO_HUP | G_IO_ERR | G_IO_NVAL, signal_handler, NULL); g_io_channel_unref(channel); return source; } static gboolean option_version = FALSE; static gint option_index = -1; static gboolean option_dbg = FALSE; static gboolean option_mgmt_dbg = FALSE; static GOptionEntry options[] = { { "version", 'v', 0, G_OPTION_ARG_NONE, &option_version, "Show version information and exit", NULL }, { "index", 'i', 0, G_OPTION_ARG_INT, &option_index, "Use specified controller", "INDEX"}, { "debug", 'd', 0, G_OPTION_ARG_NONE, &option_dbg, "Enable debug logs", NULL}, { "mgmt-debug", 0, 0, G_OPTION_ARG_NONE, &option_mgmt_dbg, "Enable mgmt debug logs", NULL}, { NULL } }; static void cleanup_services(void) { int i; DBG(""); for (i = HAL_SERVICE_ID_MAX; i > HAL_SERVICE_ID_CORE; i--) unregister_service(i); } static bool set_capabilities(void) { #if defined(ANDROID) struct __user_cap_header_struct header; struct __user_cap_data_struct cap; header.version = _LINUX_CAPABILITY_VERSION; header.pid = 0; /* * CAP_NET_ADMIN: Allow use of MGMT interface * CAP_NET_BIND_SERVICE: Allow use of privileged PSM * CAP_NET_RAW: Allow use of bnep ioctl calls */ cap.effective = cap.permitted = CAP_TO_MASK(CAP_NET_RAW) | CAP_TO_MASK(CAP_NET_ADMIN) | CAP_TO_MASK(CAP_NET_BIND_SERVICE); cap.inheritable = 0; /* don't clear capabilities when dropping root */ if (prctl(PR_SET_KEEPCAPS, 1) < 0) { error("%s: prctl(): %s", __func__, strerror(errno)); return false; } /* Android bluetooth user UID=1002 */ if (setuid(1002) < 0) { error("%s: setuid(): %s", __func__, strerror(errno)); return false; } /* TODO: Move to cap_set_proc once bionic support it */ if (capset(&header, &cap) < 0) { error("%s: capset(): %s", __func__, strerror(errno)); return false; } /* TODO: Move to cap_get_proc once bionic support it */ if (capget(&header, &cap) < 0) { error("%s: capget(): %s", __func__, strerror(errno)); return false; } DBG("Caps: eff: 0x%x, perm: 0x%x, inh: 0x%x", cap.effective, cap.permitted, cap.inheritable); #endif return true; } static void set_version(void) { uint8_t major, minor; if (sscanf(VERSION, "%hhu.%hhu", &major, &minor) != 2) return; config_pnp_version = major << 8 | minor; } int main(int argc, char *argv[]) { GOptionContext *context; GError *err = NULL; guint signal; set_version(); context = g_option_context_new(NULL); g_option_context_add_main_entries(context, options, NULL); if (g_option_context_parse(context, &argc, &argv, &err) == FALSE) { if (err != NULL) { g_printerr("%s\n", err->message); g_error_free(err); } else g_printerr("An unknown error occurred\n"); exit(EXIT_FAILURE); } g_option_context_free(context); if (option_version == TRUE) { printf("%s\n", VERSION); exit(EXIT_SUCCESS); } signal = setup_signalfd(); if (!signal) return EXIT_FAILURE; if (option_dbg || option_mgmt_dbg) __btd_log_init("*", 0); else __btd_log_init(NULL, 0); if (!set_capabilities()) { __btd_log_cleanup(); g_source_remove(signal); return EXIT_FAILURE; } quit_timeout = g_timeout_add_seconds(STARTUP_GRACE_SECONDS, quit_eventloop, NULL); if (quit_timeout == 0) { error("Failed to init startup timeout"); __btd_log_cleanup(); g_source_remove(signal); return EXIT_FAILURE; } if (!bt_bluetooth_start(option_index, option_mgmt_dbg, adapter_ready)) { __btd_log_cleanup(); g_source_remove(quit_timeout); g_source_remove(signal); return EXIT_FAILURE; } /* Use params: mtu = 0, flags = 0 */ start_sdp_server(0, 0); DBG("Entering main loop"); event_loop = g_main_loop_new(NULL, FALSE); g_main_loop_run(event_loop); g_source_remove(signal); if (quit_timeout > 0) g_source_remove(quit_timeout); cleanup_services(); stop_sdp_server(); bt_bluetooth_cleanup(); g_main_loop_unref(event_loop); /* If no adapter was initialized, hal_ipc is NULL */ if (hal_ipc) { ipc_unregister(hal_ipc, HAL_SERVICE_ID_CORE); ipc_cleanup(hal_ipc); } info("Exit"); __btd_log_cleanup(); free(config_vendor); free(config_model); free(config_name); free(config_serial); free(config_fw_rev); free(config_hw_rev); return EXIT_SUCCESS; } bluez-5.82/android/PaxHeaders/pts-hid.txt0000644000000000000000000000005012537515745015415 xustar0020 atime=1743516876 20 ctime=1743591289 bluez-5.82/android/pts-hid.txt0000644000000000000000000000362112537515745015100 0ustar00rootrootPTS test results for HID PTS version: 6.1 Tested: 19-May-2015 Android version: 5.1 Results: PASS test passed FAIL test failed INC test is inconclusive N/A test is disabled due to PICS setup ------------------------------------------------------------------------------- Test Name Result Notes ------------------------------------------------------------------------------- TC_HOS_HCE_BV_01_I PASS TC_HOS_HCE_BV_03_I PASS TC_HOS_HCE_BV_04_I PASS TC_HOS_HCR_BV_01_I PASS TC_HOS_HCR_BV_02_I PASS TC_HOS_HCR_BV_03_I N/A TC_HOS_HCR_BV_04_I N/A TC_HOS_HDT_BV_01_I PASS TC_HOS_HDT_BV_02_I PASS haltest: hidhost connect hidhost send_data ff00 NOTE: PTS displays wrong report data on popup PTS issue #13021 TC_HOS_HDT_BV_03_I N/A TC_HOS_HDT_BV_04_I N/A TC_HOS_HID_BV_01_C N/A TC_HOS_HID_BV_02_C N/A TC_HOS_HID_BV_03_C N/A TC_HOS_HID_BV_04_C N/A TC_HOS_HID_BV_05_C N/A TC_HOS_HID_BV_06_C N/A TC_HOS_HID_BV_08_C N/A TC_HOS_HID_BV_09_C N/A TC_HOS_HID_BV_10_C N/A TC_HOS_DAT_BV_01_C PASS haltest: hidhost connect hidhost send_data ff00 NOTE: PTS displays wrong report data on popup PTS issue #13021 TC_HOS_DAT_BV_02_C N/A TC_HOS_DAT_BI_01_C N/A TC_HOS_DAT_BI_02_C N/A TC_DEV_HCE_BV_01_I N/A TC_DEV_HCE_BV_02_I N/A TC_DEV_HCE_BV_03_I N/A TC_DEV_HCE_BV_04_I N/A TC_DEV_HCE_BV_05_I N/A TC_DEV_HCR_BV_01_I N/A TC_DEV_HCR_BV_02_I N/A TC_DEV_HCR_BV_03_I N/A TC_DEV_HCR_BV_04_I N/A TC_DEV_HDT_BV_01_I N/A TC_DEV_HDT_BV_02_I N/A TC_DEV_HDT_BV_03_I N/A TC_DEV_HDT_BV_04_I N/A TC_DEV_HID_BV_01_C N/A TC_DEV_HID_BV_03_C N/A TC_DEV_HID_BV_04_C N/A TC_DEV_HID_BV_05_C N/A TC_DEV_HID_BV_06_C N/A TC_DEV_HID_BV_08_C N/A TC_DEV_HID_BV_09_C N/A TC_DEV_HID_BV_10_C N/A TC_DEV_HID_BI_01_C N/A TC_DEV_HID_BI_02_C N/A TC_DEV_DAT_BV_01_C N/A TC_DEV_SDD_BV_01_C N/A TC_DEV_SDD_BV_02_C N/A TC_DEV_SDD_BV_03_C N/A TC_DEV_SDD_BV_04_I N/A ------------------------------------------------------------------------------- bluez-5.82/android/PaxHeaders/compat0000644000000000000000000000005014773213570014505 xustar0020 atime=1743591291 20 ctime=1743591288 bluez-5.82/android/compat/0000755000000000000000000000000014773213570014243 5ustar00rootrootbluez-5.82/android/compat/PaxHeaders/wordexp.h0000644000000000000000000000005014015011623016403 xustar0020 atime=1743516875 20 ctime=1743591288 bluez-5.82/android/compat/wordexp.h0000644000000000000000000000072014015011623016063 0ustar00rootroot/* SPDX-License-Identifier: LGPL-2.1-or-later */ /* * * BlueZ - Bluetooth protocol stack for Linux * * Copyright (C) 1991-2013 Free Software Foundation, Inc. * * */ #ifndef _WORDEXP_H_ #define _WORDEXP_H_ #define WRDE_NOCMD 0 typedef struct { size_t we_wordc; char **we_wordv; size_t we_offs; } wordexp_t; static inline int wordexp(const char *c, wordexp_t *w, int _i) { return -1; } static inline void wordfree(wordexp_t *__wordexp) { } #endif bluez-5.82/android/compat/PaxHeaders/readline0000644000000000000000000000005014773213570016270 xustar0020 atime=1743591291 20 ctime=1743591288 bluez-5.82/android/compat/readline/0000755000000000000000000000000014773213570016026 5ustar00rootrootbluez-5.82/android/compat/readline/PaxHeaders/history.h0000644000000000000000000000005014015011623020177 xustar0020 atime=1743516875 20 ctime=1743591288 bluez-5.82/android/compat/readline/history.h0000644000000000000000000000042114015011623017655 0ustar00rootroot/* SPDX-License-Identifier: GPL-2.0-or-later */ /* * * BlueZ - Bluetooth protocol stack for Linux * * Copyright (C) 1987-2011 Free Software Foundation, Inc. * * */ #ifndef _HISTORY_H_ #define _HISTORY_H_ static inline void add_history(const char *c) { } #endif bluez-5.82/android/compat/readline/PaxHeaders/readline.h0000644000000000000000000000005014015011623020261 xustar0020 atime=1743516875 20 ctime=1743591288 bluez-5.82/android/compat/readline/readline.h0000644000000000000000000000335014015011623017743 0ustar00rootroot/* SPDX-License-Identifier: GPL-2.0-or-later */ /* * * BlueZ - Bluetooth protocol stack for Linux * * Copyright (C) 1987-2011 Free Software Foundation, Inc. * * */ #ifndef _READLINE_H_ #define _READLINE_H_ typedef void (*rl_vcpfunc_t)(char *c); typedef void (*rl_compdisp_func_t)(char **c, int i, int j); typedef char *(*rl_compentry_func_t)(const char *c, int i); typedef char **(*rl_completion_func_t)(const char *c, int i, int j); #define RL_STATE_DONE 0x1000000 #define RL_ISSTATE(x) (rl_readline_state & (x)) static int rl_end; static int rl_point; static int rl_readline_state; static int rl_erase_empty_line; static int rl_attempted_completion_over; static char *rl_prompt; static char *rl_line_buffer; static rl_compdisp_func_t rl_completion_display_matches_hook; static rl_completion_func_t rl_attempted_completion_function; static inline void rl_callback_handler_install(const char *c, rl_vcpfunc_t f) { printf("readline not available\n"); exit(1); } static inline int rl_set_prompt(const char *c) { return -1; } static inline void rl_replace_line(const char *c, int i) { } static inline void rl_redisplay(void) { } static inline char **rl_completion_matches(const char *c, rl_compentry_func_t f) { return NULL; } static inline int rl_insert_text(const char *c) { return -1; } static inline int rl_crlf(void) { return -1; } static inline void rl_callback_read_char(void) { } static inline int rl_message(const char *c, ...) { return -1; } static inline void rl_callback_handler_remove(void) { } static inline char *rl_copy_text(int i, int j) { return NULL; } static inline void rl_save_prompt(void) { } static inline void rl_restore_prompt(void) { } static inline int rl_forced_update_display(void) { return -1; } #endif bluez-5.82/android/PaxHeaders/avrcp.h0000644000000000000000000000005014015011623014543 xustar0020 atime=1743516866 20 ctime=1743591278 bluez-5.82/android/avrcp.h0000644000000000000000000000060214015011623014222 0ustar00rootroot/* SPDX-License-Identifier: LGPL-2.1-or-later */ /* * * BlueZ - Bluetooth protocol stack for Linux * * Copyright (C) 2013-2014 Intel Corporation. All rights reserved. * * */ bool bt_avrcp_register(struct ipc *ipc, const bdaddr_t *addr, uint8_t mode); void bt_avrcp_unregister(void); void bt_avrcp_connect(const bdaddr_t *dst); void bt_avrcp_disconnect(const bdaddr_t *dst); bluez-5.82/android/PaxHeaders/handsfree.c0000644000000000000000000000005014621503015015366 xustar0020 atime=1743516866 20 ctime=1743591279 bluez-5.82/android/handsfree.c0000644000000000000000000020736114621503015015060 0ustar00rootroot// SPDX-License-Identifier: LGPL-2.1-or-later /* * * BlueZ - Bluetooth protocol stack for Linux * * Copyright (C) 2013-2014 Intel Corporation. All rights reserved. * * */ #ifdef HAVE_CONFIG_H #include #endif #include #include #include #include #include #include "lib/bluetooth.h" #include "lib/sdp.h" #include "lib/sdp_lib.h" #include "src/sdp-client.h" #include "src/uuid-helper.h" #include "src/shared/hfp.h" #include "src/shared/queue.h" #include "src/shared/util.h" #include "btio/btio.h" #include "hal-msg.h" #include "ipc-common.h" #include "ipc.h" #include "handsfree.h" #include "bluetooth.h" #include "src/log.h" #include "utils.h" #include "sco-msg.h" #include "sco.h" #define HSP_AG_CHANNEL 12 #define HFP_AG_CHANNEL 13 #define HFP_AG_FEAT_3WAY 0x00000001 #define HFP_AG_FEAT_ECNR 0x00000002 #define HFP_AG_FEAT_VR 0x00000004 #define HFP_AG_FEAT_INBAND 0x00000008 #define HFP_AG_FEAT_VTAG 0x00000010 #define HFP_AG_FEAT_REJ_CALL 0x00000020 #define HFP_AG_FEAT_ECS 0x00000040 #define HFP_AG_FEAT_ECC 0x00000080 #define HFP_AG_FEAT_EXT_ERR 0x00000100 #define HFP_AG_FEAT_CODEC 0x00000200 #define HFP_HF_FEAT_ECNR 0x00000001 #define HFP_HF_FEAT_3WAY 0x00000002 #define HFP_HF_FEAT_CLI 0x00000004 #define HFP_HF_FEAT_VR 0x00000008 #define HFP_HF_FEAT_RVC 0x00000010 #define HFP_HF_FEAT_ECS 0x00000020 #define HFP_HF_FEAT_ECC 0x00000040 #define HFP_HF_FEAT_CODEC 0x00000080 #define HFP_AG_FEATURES (HFP_AG_FEAT_3WAY | HFP_AG_FEAT_ECNR |\ HFP_AG_FEAT_VR | HFP_AG_FEAT_REJ_CALL |\ HFP_AG_FEAT_ECS | HFP_AG_FEAT_EXT_ERR) #define HFP_AG_CHLD "0,1,2,3" /* offsets in indicators table, should be incremented when sending CIEV */ #define IND_SERVICE 0 #define IND_CALL 1 #define IND_CALLSETUP 2 #define IND_CALLHELD 3 #define IND_SIGNAL 4 #define IND_ROAM 5 #define IND_BATTCHG 6 #define IND_COUNT (IND_BATTCHG + 1) #define RING_TIMEOUT 2 #define CVSD_OFFSET 0 #define MSBC_OFFSET 1 #define CODECS_COUNT (MSBC_OFFSET + 1) #define CODEC_ID_CVSD 0x01 #define CODEC_ID_MSBC 0x02 struct indicator { const char *name; int min; int max; int val; bool always_active; bool active; }; struct hfp_codec { uint8_t type; bool local_supported; bool remote_supported; }; struct hf_device { bdaddr_t bdaddr; uint8_t state; uint8_t audio_state; uint32_t features; bool clip_enabled; bool cmee_enabled; bool ccwa_enabled; bool indicators_enabled; struct indicator inds[IND_COUNT]; int num_active; int num_held; int setup_state; guint call_hanging_up; uint8_t negotiated_codec; uint8_t proposed_codec; struct hfp_codec codecs[CODECS_COUNT]; guint ring; char *clip; bool hsp; struct hfp_gw *gw; guint delay_sco; }; static const struct indicator inds_defaults[] = { { "service", 0, 1, 0, false, true }, { "call", 0, 1, 0, true, true }, { "callsetup", 0, 3, 0, true, true }, { "callheld", 0, 2, 0, true, true }, { "signal", 0, 5, 0, false, true }, { "roam", 0, 1, 0, false, true }, { "battchg", 0, 5, 0, false, true }, }; static const struct hfp_codec codecs_defaults[] = { { CODEC_ID_CVSD, true, false}, { CODEC_ID_MSBC, false, false}, }; static struct queue *devices = NULL; static uint32_t hfp_ag_features = 0; static bdaddr_t adapter_addr; static struct ipc *hal_ipc = NULL; static struct ipc *sco_ipc = NULL; static uint32_t hfp_record_id = 0; static GIOChannel *hfp_server = NULL; static uint32_t hsp_record_id = 0; static GIOChannel *hsp_server = NULL; static struct bt_sco *sco = NULL; static unsigned int max_hfp_clients = 0; static void set_state(struct hf_device *dev, uint8_t state) { struct hal_ev_handsfree_conn_state ev; char address[18]; if (dev->state == state) return; dev->state = state; ba2str(&dev->bdaddr, address); DBG("device %s state %u", address, state); bdaddr2android(&dev->bdaddr, ev.bdaddr); ev.state = state; ipc_send_notif(hal_ipc, HAL_SERVICE_ID_HANDSFREE, HAL_EV_HANDSFREE_CONN_STATE, sizeof(ev), &ev); } static void set_audio_state(struct hf_device *dev, uint8_t state) { struct hal_ev_handsfree_audio_state ev; char address[18]; if (dev->audio_state == state) return; dev->audio_state = state; ba2str(&dev->bdaddr, address); DBG("device %s audio state %u", address, state); bdaddr2android(&dev->bdaddr, ev.bdaddr); ev.state = state; ipc_send_notif(hal_ipc, HAL_SERVICE_ID_HANDSFREE, HAL_EV_HANDSFREE_AUDIO_STATE, sizeof(ev), &ev); } static void init_codecs(struct hf_device *dev) { memcpy(dev->codecs, codecs_defaults, sizeof(dev->codecs)); if (hfp_ag_features & HFP_AG_FEAT_CODEC) dev->codecs[MSBC_OFFSET].local_supported = true; } static struct hf_device *device_create(const bdaddr_t *bdaddr) { struct hf_device *dev; dev = new0(struct hf_device, 1); bacpy(&dev->bdaddr, bdaddr); dev->setup_state = HAL_HANDSFREE_CALL_STATE_IDLE; dev->state = HAL_EV_HANDSFREE_CONN_STATE_DISCONNECTED; dev->audio_state = HAL_EV_HANDSFREE_AUDIO_STATE_DISCONNECTED; memcpy(dev->inds, inds_defaults, sizeof(dev->inds)); init_codecs(dev); queue_push_head(devices, dev); return dev; } static void device_destroy(struct hf_device *dev) { hfp_gw_unref(dev->gw); if (dev->delay_sco) g_source_remove(dev->delay_sco); if (dev->audio_state == HAL_EV_HANDSFREE_AUDIO_STATE_CONNECTED) bt_sco_disconnect(sco); if (dev->ring) g_source_remove(dev->ring); g_free(dev->clip); if (dev->call_hanging_up) g_source_remove(dev->call_hanging_up); set_audio_state(dev, HAL_EV_HANDSFREE_AUDIO_STATE_DISCONNECTED); set_state(dev, HAL_EV_HANDSFREE_CONN_STATE_DISCONNECTED); queue_remove(devices, dev); free(dev); } static bool match_by_bdaddr(const void *data, const void *match_data) { const struct hf_device *dev = data; const bdaddr_t *addr = match_data; return !bacmp(&dev->bdaddr, addr); } static struct hf_device *find_device(const bdaddr_t *bdaddr) { if (!bacmp(bdaddr, BDADDR_ANY)) return queue_peek_head(devices); return queue_find(devices, match_by_bdaddr, bdaddr); } static struct hf_device *get_device(const bdaddr_t *bdaddr) { struct hf_device *dev; dev = find_device(bdaddr); if (dev) return dev; if (queue_length(devices) == max_hfp_clients) return NULL; return device_create(bdaddr); } static void disconnect_watch(void *user_data) { struct hf_device *dev = user_data; DBG(""); device_destroy(dev); } static void at_cmd_unknown(const char *command, void *user_data) { struct hf_device *dev = user_data; uint8_t buf[IPC_MTU]; struct hal_ev_handsfree_unknown_at *ev = (void *) buf; bdaddr2android(&dev->bdaddr, ev->bdaddr); /* copy while string including terminating NULL */ ev->len = strlen(command) + 1; if (ev->len > IPC_MTU - sizeof(*ev)) { hfp_gw_send_result(dev->gw, HFP_RESULT_ERROR); return; } memcpy(ev->buf, command, ev->len); ipc_send_notif(hal_ipc, HAL_SERVICE_ID_HANDSFREE, HAL_EV_HANDSFREE_UNKNOWN_AT, sizeof(*ev) + ev->len, ev); } static void at_cmd_vgm(struct hfp_context *context, enum hfp_gw_cmd_type type, void *user_data) { struct hf_device *dev = user_data; struct hal_ev_handsfree_volume ev; unsigned int val; DBG(""); switch (type) { case HFP_GW_CMD_TYPE_SET: if (!hfp_context_get_number(context, &val) || val > 15) break; if (hfp_context_has_next(context)) break; ev.type = HAL_HANDSFREE_VOLUME_TYPE_MIC; ev.volume = val; bdaddr2android(&dev->bdaddr, ev.bdaddr); ipc_send_notif(hal_ipc, HAL_SERVICE_ID_HANDSFREE, HAL_EV_HANDSFREE_VOLUME, sizeof(ev), &ev); /* Framework is not replying with result for AT+VGM */ hfp_gw_send_result(dev->gw, HFP_RESULT_OK); return; case HFP_GW_CMD_TYPE_READ: case HFP_GW_CMD_TYPE_TEST: case HFP_GW_CMD_TYPE_COMMAND: break; } hfp_gw_send_result(dev->gw, HFP_RESULT_ERROR); } static void at_cmd_vgs(struct hfp_context *context, enum hfp_gw_cmd_type type, void *user_data) { struct hf_device *dev = user_data; struct hal_ev_handsfree_volume ev; unsigned int val; DBG(""); switch (type) { case HFP_GW_CMD_TYPE_SET: if (!hfp_context_get_number(context, &val) || val > 15) break; if (hfp_context_has_next(context)) break; ev.type = HAL_HANDSFREE_VOLUME_TYPE_SPEAKER; ev.volume = val; bdaddr2android(&dev->bdaddr, ev.bdaddr); ipc_send_notif(hal_ipc, HAL_SERVICE_ID_HANDSFREE, HAL_EV_HANDSFREE_VOLUME, sizeof(ev), &ev); /* Framework is not replying with result for AT+VGS */ hfp_gw_send_result(dev->gw, HFP_RESULT_OK); return; case HFP_GW_CMD_TYPE_READ: case HFP_GW_CMD_TYPE_TEST: case HFP_GW_CMD_TYPE_COMMAND: break; } hfp_gw_send_result(dev->gw, HFP_RESULT_ERROR); } static void at_cmd_cops(struct hfp_context *context, enum hfp_gw_cmd_type type, void *user_data) { struct hf_device *dev = user_data; struct hal_ev_handsfree_cops ev; unsigned int val; switch (type) { case HFP_GW_CMD_TYPE_SET: if (!hfp_context_get_number(context, &val) || val != 3) break; if (!hfp_context_get_number(context, &val) || val != 0) break; if (hfp_context_has_next(context)) break; hfp_gw_send_result(dev->gw, HFP_RESULT_OK); return; case HFP_GW_CMD_TYPE_READ: bdaddr2android(&dev->bdaddr, ev.bdaddr); ipc_send_notif(hal_ipc, HAL_SERVICE_ID_HANDSFREE, HAL_EV_HANDSFREE_COPS, sizeof(ev), &ev); return; case HFP_GW_CMD_TYPE_TEST: case HFP_GW_CMD_TYPE_COMMAND: break; } hfp_gw_send_result(dev->gw, HFP_RESULT_ERROR); } static void at_cmd_bia(struct hfp_context *context, enum hfp_gw_cmd_type type, void *user_data) { struct hf_device *dev = user_data; unsigned int val, i, def; bool tmp[IND_COUNT]; DBG(""); switch (type) { case HFP_GW_CMD_TYPE_SET: for (i = 0; i < IND_COUNT; i++) tmp[i] = dev->inds[i].active; i = 0; do { def = (i < IND_COUNT) ? dev->inds[i].active : 0; if (!hfp_context_get_number_default(context, &val, def)) goto failed; if (val > 1) goto failed; if (i < IND_COUNT) { tmp[i] = val || dev->inds[i].always_active; i++; } } while (hfp_context_has_next(context)); for (i = 0; i < IND_COUNT; i++) dev->inds[i].active = tmp[i]; hfp_gw_send_result(dev->gw, HFP_RESULT_OK); return; case HFP_GW_CMD_TYPE_TEST: case HFP_GW_CMD_TYPE_READ: case HFP_GW_CMD_TYPE_COMMAND: break; } failed: hfp_gw_send_result(dev->gw, HFP_RESULT_ERROR); } static void at_cmd_a(struct hfp_context *context, enum hfp_gw_cmd_type type, void *user_data) { struct hf_device *dev = user_data; struct hal_ev_handsfree_answer ev; DBG(""); switch (type) { case HFP_GW_CMD_TYPE_COMMAND: if (hfp_context_has_next(context)) break; bdaddr2android(&dev->bdaddr, ev.bdaddr); ipc_send_notif(hal_ipc, HAL_SERVICE_ID_HANDSFREE, HAL_EV_HANDSFREE_ANSWER, sizeof(ev), &ev); /* Framework is not replying with result for ATA */ hfp_gw_send_result(dev->gw, HFP_RESULT_OK); return; case HFP_GW_CMD_TYPE_SET: case HFP_GW_CMD_TYPE_READ: case HFP_GW_CMD_TYPE_TEST: break; } hfp_gw_send_result(dev->gw, HFP_RESULT_ERROR); } static void at_cmd_d(struct hfp_context *context, enum hfp_gw_cmd_type type, void *user_data) { struct hf_device *dev = user_data; char buf[IPC_MTU]; struct hal_ev_handsfree_dial *ev = (void *) buf; int cnt; DBG(""); switch (type) { case HFP_GW_CMD_TYPE_SET: if (!hfp_context_get_unquoted_string(context, (char *) ev->number, 255)) break; bdaddr2android(&dev->bdaddr, ev->bdaddr); ev->number_len = strlen((char *) ev->number); if (ev->number[ev->number_len - 1] != ';') break; if (ev->number[0] == '>') cnt = strspn((char *) ev->number + 1, "0123456789") + 1; else cnt = strspn((char *) ev->number, "0123456789ABC*#+"); if (cnt != ev->number_len - 1) break; ev->number_len++; ipc_send_notif(hal_ipc, HAL_SERVICE_ID_HANDSFREE, HAL_EV_HANDSFREE_DIAL, sizeof(*ev) + ev->number_len, ev); return; case HFP_GW_CMD_TYPE_READ: case HFP_GW_CMD_TYPE_TEST: case HFP_GW_CMD_TYPE_COMMAND: break; } hfp_gw_send_result(dev->gw, HFP_RESULT_ERROR); } static void at_cmd_ccwa(struct hfp_context *context, enum hfp_gw_cmd_type type, void *user_data) { struct hf_device *dev = user_data; unsigned int val; DBG(""); switch (type) { case HFP_GW_CMD_TYPE_SET: if (!hfp_context_get_number(context, &val) || val > 1) break; if (hfp_context_has_next(context)) break; dev->ccwa_enabled = val; hfp_gw_send_result(dev->gw, HFP_RESULT_OK); return; case HFP_GW_CMD_TYPE_READ: case HFP_GW_CMD_TYPE_TEST: case HFP_GW_CMD_TYPE_COMMAND: break; } hfp_gw_send_result(dev->gw, HFP_RESULT_ERROR); } static void at_cmd_chup(struct hfp_context *context, enum hfp_gw_cmd_type type, void *user_data) { struct hf_device *dev = user_data; struct hal_ev_handsfree_hangup ev; DBG(""); switch (type) { case HFP_GW_CMD_TYPE_COMMAND: if (hfp_context_has_next(context)) break; bdaddr2android(&dev->bdaddr, ev.bdaddr); ipc_send_notif(hal_ipc, HAL_SERVICE_ID_HANDSFREE, HAL_EV_HANDSFREE_HANGUP, sizeof(ev), &ev); /* Framework is not replying with result for AT+CHUP */ hfp_gw_send_result(dev->gw, HFP_RESULT_OK); return; case HFP_GW_CMD_TYPE_READ: case HFP_GW_CMD_TYPE_TEST: case HFP_GW_CMD_TYPE_SET: break; } hfp_gw_send_result(dev->gw, HFP_RESULT_ERROR); } static void at_cmd_clcc(struct hfp_context *context, enum hfp_gw_cmd_type type, void *user_data) { struct hf_device *dev = user_data; struct hal_ev_handsfree_clcc ev; DBG(""); switch (type) { case HFP_GW_CMD_TYPE_COMMAND: if (hfp_context_has_next(context)) break; bdaddr2android(&dev->bdaddr, ev.bdaddr); ipc_send_notif(hal_ipc, HAL_SERVICE_ID_HANDSFREE, HAL_EV_HANDSFREE_CLCC, sizeof(ev), &ev); return; case HFP_GW_CMD_TYPE_READ: case HFP_GW_CMD_TYPE_TEST: case HFP_GW_CMD_TYPE_SET: break; } hfp_gw_send_result(dev->gw, HFP_RESULT_ERROR); } static void at_cmd_cmee(struct hfp_context *context, enum hfp_gw_cmd_type type, void *user_data) { struct hf_device *dev = user_data; unsigned int val; DBG(""); switch (type) { case HFP_GW_CMD_TYPE_SET: if (!hfp_context_get_number(context, &val) || val > 1) break; if (hfp_context_has_next(context)) break; dev->cmee_enabled = val; hfp_gw_send_result(dev->gw, HFP_RESULT_OK); return; case HFP_GW_CMD_TYPE_READ: case HFP_GW_CMD_TYPE_TEST: case HFP_GW_CMD_TYPE_COMMAND: break; } hfp_gw_send_result(dev->gw, HFP_RESULT_ERROR); } static void at_cmd_clip(struct hfp_context *context, enum hfp_gw_cmd_type type, void *user_data) { struct hf_device *dev = user_data; unsigned int val; DBG(""); switch (type) { case HFP_GW_CMD_TYPE_SET: if (!hfp_context_get_number(context, &val) || val > 1) break; if (hfp_context_has_next(context)) break; dev->clip_enabled = val; hfp_gw_send_result(dev->gw, HFP_RESULT_OK); return; case HFP_GW_CMD_TYPE_READ: case HFP_GW_CMD_TYPE_TEST: case HFP_GW_CMD_TYPE_COMMAND: break; } hfp_gw_send_result(dev->gw, HFP_RESULT_ERROR); } static void at_cmd_vts(struct hfp_context *context, enum hfp_gw_cmd_type type, void *user_data) { struct hf_device *dev = user_data; struct hal_ev_handsfree_dtmf ev; char str[2]; DBG(""); switch (type) { case HFP_GW_CMD_TYPE_SET: if (!hfp_context_get_unquoted_string(context, str, 2)) break; if (!((str[0] >= '0' && str[0] <= '9') || (str[0] >= 'A' && str[0] <= 'D') || str[0] == '*' || str[0] == '#')) break; if (hfp_context_has_next(context)) break; bdaddr2android(&dev->bdaddr, ev.bdaddr); ev.tone = str[0]; ipc_send_notif(hal_ipc, HAL_SERVICE_ID_HANDSFREE, HAL_EV_HANDSFREE_DTMF, sizeof(ev), &ev); /* Framework is not replying with result for AT+VTS */ hfp_gw_send_result(dev->gw, HFP_RESULT_OK); return; case HFP_GW_CMD_TYPE_READ: case HFP_GW_CMD_TYPE_TEST: case HFP_GW_CMD_TYPE_COMMAND: break; } hfp_gw_send_result(dev->gw, HFP_RESULT_ERROR); } static void at_cmd_cnum(struct hfp_context *context, enum hfp_gw_cmd_type type, void *user_data) { struct hf_device *dev = user_data; struct hal_ev_handsfree_cnum ev; DBG(""); switch (type) { case HFP_GW_CMD_TYPE_COMMAND: if (hfp_context_has_next(context)) break; bdaddr2android(&dev->bdaddr, ev.bdaddr); ipc_send_notif(hal_ipc, HAL_SERVICE_ID_HANDSFREE, HAL_EV_HANDSFREE_CNUM, sizeof(ev), &ev); return; case HFP_GW_CMD_TYPE_SET: case HFP_GW_CMD_TYPE_READ: case HFP_GW_CMD_TYPE_TEST: break; } hfp_gw_send_result(dev->gw, HFP_RESULT_ERROR); } static void at_cmd_binp(struct hfp_context *context, enum hfp_gw_cmd_type type, void *user_data) { struct hf_device *dev = user_data; DBG(""); /* TODO */ hfp_gw_send_result(dev->gw, HFP_RESULT_ERROR); } static void at_cmd_bldn(struct hfp_context *context, enum hfp_gw_cmd_type type, void *user_data) { struct hf_device *dev = user_data; struct hal_ev_handsfree_dial ev; DBG(""); switch (type) { case HFP_GW_CMD_TYPE_COMMAND: if (hfp_context_has_next(context)) break; bdaddr2android(&dev->bdaddr, ev.bdaddr); ev.number_len = 0; ipc_send_notif(hal_ipc, HAL_SERVICE_ID_HANDSFREE, HAL_EV_HANDSFREE_DIAL, sizeof(ev), &ev); return; case HFP_GW_CMD_TYPE_READ: case HFP_GW_CMD_TYPE_TEST: case HFP_GW_CMD_TYPE_SET: break; } hfp_gw_send_result(dev->gw, HFP_RESULT_ERROR); } static void at_cmd_bvra(struct hfp_context *context, enum hfp_gw_cmd_type type, void *user_data) { struct hf_device *dev = user_data; struct hal_ev_handsfree_vr_state ev; unsigned int val; DBG(""); switch (type) { case HFP_GW_CMD_TYPE_SET: if (!hfp_context_get_number(context, &val) || val > 1) break; if (hfp_context_has_next(context)) break; if (val) ev.state = HAL_HANDSFREE_VR_STARTED; else ev.state = HAL_HANDSFREE_VR_STOPPED; bdaddr2android(&dev->bdaddr, ev.bdaddr); ipc_send_notif(hal_ipc, HAL_SERVICE_ID_HANDSFREE, HAL_EV_HANDSFREE_VR, sizeof(ev), &ev); return; case HFP_GW_CMD_TYPE_READ: case HFP_GW_CMD_TYPE_TEST: case HFP_GW_CMD_TYPE_COMMAND: break; } hfp_gw_send_result(dev->gw, HFP_RESULT_ERROR); } static void at_cmd_nrec(struct hfp_context *context, enum hfp_gw_cmd_type type, void *user_data) { struct hf_device *dev = user_data; struct hal_ev_handsfree_nrec ev; unsigned int val; DBG(""); switch (type) { case HFP_GW_CMD_TYPE_SET: /* * Android HAL defines start and stop parameter for NREC * callback, but spec allows HF to only disable AG's NREC * feature for SLC duration. Follow spec here. */ if (!hfp_context_get_number(context, &val) || val != 0) break; if (hfp_context_has_next(context)) break; ev.nrec = HAL_HANDSFREE_NREC_STOP; bdaddr2android(&dev->bdaddr, ev.bdaddr); ipc_send_notif(hal_ipc, HAL_SERVICE_ID_HANDSFREE, HAL_EV_HANDSFREE_NREC, sizeof(ev), &ev); /* Framework is not replying with context for AT+NREC */ hfp_gw_send_result(dev->gw, HFP_RESULT_OK); return; case HFP_GW_CMD_TYPE_READ: case HFP_GW_CMD_TYPE_TEST: case HFP_GW_CMD_TYPE_COMMAND: break; } hfp_gw_send_result(dev->gw, HFP_RESULT_ERROR); } static void at_cmd_bsir(struct hfp_context *context, enum hfp_gw_cmd_type type, void *user_data) { struct hf_device *dev = user_data; DBG(""); /* TODO */ hfp_gw_send_result(dev->gw, HFP_RESULT_ERROR); } static void at_cmd_btrh(struct hfp_context *context, enum hfp_gw_cmd_type type, void *user_data) { struct hf_device *dev = user_data; DBG(""); /* TODO */ hfp_gw_send_result(dev->gw, HFP_RESULT_ERROR); } static void disconnect_sco_cb(const bdaddr_t *addr) { struct hf_device *dev; DBG(""); dev = find_device(addr); if (!dev) { error("handsfree: Could not find device"); return; } set_audio_state(dev, HAL_EV_HANDSFREE_AUDIO_STATE_DISCONNECTED); } static void select_codec(struct hf_device *dev, uint8_t codec_type) { uint8_t type = CODEC_ID_CVSD; int i; if (codec_type > 0) { type = codec_type; goto done; } for (i = CODECS_COUNT - 1; i >= CVSD_OFFSET; i--) { if (!dev->codecs[i].local_supported) continue; if (!dev->codecs[i].remote_supported) continue; type = dev->codecs[i].type; break; } done: dev->proposed_codec = type; hfp_gw_send_info(dev->gw, "+BCS: %u", type); } static bool codec_negotiation_supported(struct hf_device *dev) { return (dev->features & HFP_HF_FEAT_CODEC) && (hfp_ag_features & HFP_AG_FEAT_CODEC); } static void connect_sco_cb(enum sco_status status, const bdaddr_t *addr) { struct hf_device *dev; dev = find_device(addr); if (!dev) { error("handsfree: Connect sco failed, no device?"); return; } if (status == SCO_STATUS_OK) { set_audio_state(dev, HAL_EV_HANDSFREE_AUDIO_STATE_CONNECTED); return; } /* Try fallback to CVSD first */ if (codec_negotiation_supported(dev) && dev->negotiated_codec != CODEC_ID_CVSD) { info("handsfree: trying fallback with CVSD"); select_codec(dev, CODEC_ID_CVSD); return; } error("handsfree: audio connect failed"); set_audio_state(dev, HAL_EV_HANDSFREE_AUDIO_STATE_DISCONNECTED); } static bool connect_sco(struct hf_device *dev) { uint16_t voice_settings; if (codec_negotiation_supported(dev) && dev->negotiated_codec != CODEC_ID_CVSD) voice_settings = BT_VOICE_TRANSPARENT; else voice_settings = BT_VOICE_CVSD_16BIT; if (!bt_sco_connect(sco, &dev->bdaddr, voice_settings)) return false; set_audio_state(dev, HAL_EV_HANDSFREE_AUDIO_STATE_CONNECTING); return true; } static gboolean connect_sco_delayed(void *data) { struct hf_device *dev = data; DBG(""); dev->delay_sco = 0; if (connect_sco(dev)) return FALSE; /* * we try connect to negotiated codec. If it fails, and it isn't * CVSD codec, try connect CVSD */ if (dev->negotiated_codec != CODEC_ID_CVSD) select_codec(dev, CODEC_ID_CVSD); return FALSE; } static void at_cmd_bcc(struct hfp_context *result, enum hfp_gw_cmd_type type, void *user_data) { struct hf_device *dev = user_data; DBG(""); switch (type) { case HFP_GW_CMD_TYPE_COMMAND: if (!codec_negotiation_supported(dev)) break; if (hfp_context_has_next(result)) break; hfp_gw_send_result(dev->gw, HFP_RESULT_OK); /* we haven't negotiated codec, start selection */ if (!dev->negotiated_codec) { select_codec(dev, 0); return; } /* Delay SCO connection so that OK response is send first */ if (dev->delay_sco == 0) dev->delay_sco = g_idle_add(connect_sco_delayed, dev); return; case HFP_GW_CMD_TYPE_READ: case HFP_GW_CMD_TYPE_TEST: case HFP_GW_CMD_TYPE_SET: break; } hfp_gw_send_result(dev->gw, HFP_RESULT_ERROR); } static void at_cmd_bcs(struct hfp_context *result, enum hfp_gw_cmd_type type, void *user_data) { struct hf_device *dev = user_data; struct hal_ev_handsfree_wbs ev; unsigned int val; DBG(""); switch (type) { case HFP_GW_CMD_TYPE_SET: if (!hfp_context_get_number(result, &val)) break; if (hfp_context_has_next(result)) break; /* Remote replied with other codec. Reply with error */ if (dev->proposed_codec != val) { dev->proposed_codec = 0; break; } ev.wbs = val; bdaddr2android(&dev->bdaddr, ev.bdaddr); ipc_send_notif(hal_ipc, HAL_SERVICE_ID_HANDSFREE, HAL_EV_HANDSFREE_WBS, sizeof(ev), &ev); dev->proposed_codec = 0; dev->negotiated_codec = val; hfp_gw_send_result(dev->gw, HFP_RESULT_OK); /* * Delay SCO connection so that OK response is send first, * then connect with negotiated parameters. */ if (dev->delay_sco == 0) dev->delay_sco = g_idle_add(connect_sco_delayed, dev); return; case HFP_GW_CMD_TYPE_READ: case HFP_GW_CMD_TYPE_TEST: case HFP_GW_CMD_TYPE_COMMAND: break; } hfp_gw_send_result(dev->gw, HFP_RESULT_ERROR); } static void at_cmd_ckpd(struct hfp_context *result, enum hfp_gw_cmd_type type, void *user_data) { struct hf_device *dev = user_data; struct hal_ev_handsfree_hsp_key_press ev; unsigned int val; DBG(""); switch (type) { case HFP_GW_CMD_TYPE_SET: if (!hfp_context_get_number(result, &val) || val != 200) break; if (hfp_context_has_next(result)) break; bdaddr2android(&dev->bdaddr, ev.bdaddr); ipc_send_notif(hal_ipc, HAL_SERVICE_ID_HANDSFREE, HAL_EV_HANDSFREE_HSP_KEY_PRESS, sizeof(ev), &ev); hfp_gw_send_result(dev->gw, HFP_RESULT_OK); return; case HFP_GW_CMD_TYPE_READ: case HFP_GW_CMD_TYPE_TEST: case HFP_GW_CMD_TYPE_COMMAND: break; } hfp_gw_send_result(dev->gw, HFP_RESULT_ERROR); } static void register_post_slc_at(struct hf_device *dev) { hfp_gw_set_command_handler(dev->gw, at_cmd_unknown, dev, NULL); if (dev->hsp) { hfp_gw_register(dev->gw, at_cmd_ckpd, "+CKPD", dev, NULL); hfp_gw_register(dev->gw, at_cmd_vgs, "+VGS", dev, NULL); hfp_gw_register(dev->gw, at_cmd_vgm, "+VGM", dev, NULL); return; } hfp_gw_register(dev->gw, at_cmd_a, "A", dev, NULL); hfp_gw_register(dev->gw, at_cmd_d, "D", dev, NULL); hfp_gw_register(dev->gw, at_cmd_ccwa, "+CCWA", dev, NULL); hfp_gw_register(dev->gw, at_cmd_chup, "+CHUP", dev, NULL); hfp_gw_register(dev->gw, at_cmd_clcc, "+CLCC", dev, NULL); hfp_gw_register(dev->gw, at_cmd_cops, "+COPS", dev, NULL); hfp_gw_register(dev->gw, at_cmd_cmee, "+CMEE", dev, NULL); hfp_gw_register(dev->gw, at_cmd_clip, "+CLIP", dev, NULL); hfp_gw_register(dev->gw, at_cmd_vts, "+VTS", dev, NULL); hfp_gw_register(dev->gw, at_cmd_cnum, "+CNUM", dev, NULL); hfp_gw_register(dev->gw, at_cmd_bia, "+BIA", dev, NULL); hfp_gw_register(dev->gw, at_cmd_binp, "+BINP", dev, NULL); hfp_gw_register(dev->gw, at_cmd_bldn, "+BLDN", dev, NULL); hfp_gw_register(dev->gw, at_cmd_bvra, "+BVRA", dev, NULL); hfp_gw_register(dev->gw, at_cmd_nrec, "+NREC", dev, NULL); hfp_gw_register(dev->gw, at_cmd_vgs, "+VGS", dev, NULL); hfp_gw_register(dev->gw, at_cmd_vgm, "+VGM", dev, NULL); hfp_gw_register(dev->gw, at_cmd_bsir, "+BSIR", dev, NULL); hfp_gw_register(dev->gw, at_cmd_btrh, "+BTRH", dev, NULL); hfp_gw_register(dev->gw, at_cmd_bcc, "+BCC", dev, NULL); hfp_gw_register(dev->gw, at_cmd_bcs, "+BCS", dev, NULL); } static void at_cmd_cmer(struct hfp_context *result, enum hfp_gw_cmd_type type, void *user_data) { struct hf_device *dev = user_data; unsigned int val; switch (type) { case HFP_GW_CMD_TYPE_SET: /* mode must be =3 */ if (!hfp_context_get_number(result, &val) || val != 3) break; /* keyp is don't care */ if (!hfp_context_get_number(result, &val)) break; /* disp is don't care */ if (!hfp_context_get_number(result, &val)) break; /* ind must be 0 or 1 */ if (!hfp_context_get_number(result, &val) || val > 1) break; dev->indicators_enabled = val; /* skip bfr if present */ hfp_context_get_number(result, &val); if (hfp_context_has_next(result)) break; hfp_gw_send_result(dev->gw, HFP_RESULT_OK); if (dev->features & HFP_HF_FEAT_3WAY) return; register_post_slc_at(dev); set_state(dev, HAL_EV_HANDSFREE_CONN_STATE_SLC_CONNECTED); return; case HFP_GW_CMD_TYPE_TEST: case HFP_GW_CMD_TYPE_READ: case HFP_GW_CMD_TYPE_COMMAND: break; } hfp_gw_send_result(dev->gw, HFP_RESULT_ERROR); if (dev->state != HAL_EV_HANDSFREE_CONN_STATE_SLC_CONNECTED) hfp_gw_disconnect(dev->gw); } static void at_cmd_cind(struct hfp_context *result, enum hfp_gw_cmd_type type, void *user_data) { struct hf_device *dev = user_data; struct hal_ev_handsfree_cind ev; char *buf, *ptr; int len; unsigned int i; switch (type) { case HFP_GW_CMD_TYPE_TEST: /* * If device supports Codec Negotiation, AT+BAC should be * received first */ if (codec_negotiation_supported(dev) && !dev->codecs[CVSD_OFFSET].remote_supported) break; len = strlen("+CIND:") + 1; for (i = 0; i < IND_COUNT; i++) { len += strlen("(\"\",(X,X)),"); len += strlen(dev->inds[i].name); } buf = g_malloc(len); if (sprintf(buf, "+CIND:") != strlen("+CIND:")) { g_free(buf); break; } ptr = buf + strlen("+CIND:"); for (i = 0; i < IND_COUNT; i++) { int printed; printed = sprintf(ptr, "(\"%s\",(%d%c%d)),", dev->inds[i].name, dev->inds[i].min, dev->inds[i].max == 1 ? ',' : '-', dev->inds[i].max); if (printed < 0) goto fail; ptr += printed; } ptr--; *ptr = '\0'; hfp_gw_send_info(dev->gw, "%s", buf); hfp_gw_send_result(dev->gw, HFP_RESULT_OK); g_free(buf); return; case HFP_GW_CMD_TYPE_READ: bdaddr2android(&dev->bdaddr, ev.bdaddr); ipc_send_notif(hal_ipc, HAL_SERVICE_ID_HANDSFREE, HAL_EV_HANDSFREE_CIND, sizeof(ev), &ev); return; case HFP_GW_CMD_TYPE_SET: case HFP_GW_CMD_TYPE_COMMAND: break; } fail: hfp_gw_send_result(dev->gw, HFP_RESULT_ERROR); if (dev->state != HAL_EV_HANDSFREE_CONN_STATE_SLC_CONNECTED) hfp_gw_disconnect(dev->gw); } static void at_cmd_brsf(struct hfp_context *result, enum hfp_gw_cmd_type type, void *user_data) { struct hf_device *dev = user_data; unsigned int feat; switch (type) { case HFP_GW_CMD_TYPE_SET: if (!hfp_context_get_number(result, &feat)) break; if (hfp_context_has_next(result)) break; /* TODO verify features */ dev->features = feat; hfp_gw_send_info(dev->gw, "+BRSF: %u", hfp_ag_features); hfp_gw_send_result(dev->gw, HFP_RESULT_OK); return; case HFP_GW_CMD_TYPE_READ: case HFP_GW_CMD_TYPE_TEST: case HFP_GW_CMD_TYPE_COMMAND: break; } hfp_gw_send_result(dev->gw, HFP_RESULT_ERROR); if (dev->state != HAL_EV_HANDSFREE_CONN_STATE_SLC_CONNECTED) hfp_gw_disconnect(dev->gw); } static void at_cmd_chld(struct hfp_context *result, enum hfp_gw_cmd_type type, void *user_data) { struct hf_device *dev = user_data; struct hal_ev_handsfree_chld ev; unsigned int val; DBG(""); switch (type) { case HFP_GW_CMD_TYPE_SET: if (!hfp_context_get_number(result, &val) || val > 3) break; /* No ECC support */ if (hfp_context_has_next(result)) break; /* value match HAL type */ ev.chld = val; bdaddr2android(&dev->bdaddr, ev.bdaddr); ipc_send_notif(hal_ipc, HAL_SERVICE_ID_HANDSFREE, HAL_EV_HANDSFREE_CHLD, sizeof(ev), &ev); return; case HFP_GW_CMD_TYPE_TEST: hfp_gw_send_info(dev->gw, "+CHLD: (%s)", HFP_AG_CHLD); hfp_gw_send_result(dev->gw, HFP_RESULT_OK); register_post_slc_at(dev); set_state(dev, HAL_EV_HANDSFREE_CONN_STATE_SLC_CONNECTED); return; case HFP_GW_CMD_TYPE_READ: case HFP_GW_CMD_TYPE_COMMAND: break; } hfp_gw_send_result(dev->gw, HFP_RESULT_ERROR); if (dev->state != HAL_EV_HANDSFREE_CONN_STATE_SLC_CONNECTED) hfp_gw_disconnect(dev->gw); } static struct hfp_codec *find_codec_by_type(struct hf_device *dev, uint8_t type) { int i; for (i = 0; i < CODECS_COUNT; i++) if (type == dev->codecs[i].type) return &dev->codecs[i]; return NULL; } static void at_cmd_bac(struct hfp_context *result, enum hfp_gw_cmd_type type, void *user_data) { struct hf_device *dev = user_data; unsigned int val; DBG(""); switch (type) { case HFP_GW_CMD_TYPE_SET: if (!codec_negotiation_supported(dev)) goto failed; /* set codecs to defaults */ init_codecs(dev); dev->negotiated_codec = 0; /* * At least CVSD mandatory codec must exist * HFP V1.6 4.34.1 */ if (!hfp_context_get_number(result, &val) || val != CODEC_ID_CVSD) goto failed; dev->codecs[CVSD_OFFSET].remote_supported = true; if (hfp_context_get_number(result, &val)) { if (val != CODEC_ID_MSBC) goto failed; dev->codecs[MSBC_OFFSET].remote_supported = true; } while (hfp_context_has_next(result)) { struct hfp_codec *codec; if (!hfp_context_get_number(result, &val)) goto failed; codec = find_codec_by_type(dev, val); if (!codec) continue; codec->remote_supported = true; } hfp_gw_send_result(dev->gw, HFP_RESULT_OK); if (dev->proposed_codec) select_codec(dev, 0); return; case HFP_GW_CMD_TYPE_TEST: case HFP_GW_CMD_TYPE_READ: case HFP_GW_CMD_TYPE_COMMAND: break; } failed: hfp_gw_send_result(dev->gw, HFP_RESULT_ERROR); if (dev->state != HAL_EV_HANDSFREE_CONN_STATE_SLC_CONNECTED) hfp_gw_disconnect(dev->gw); } static void register_slc_at(struct hf_device *dev) { hfp_gw_register(dev->gw, at_cmd_brsf, "+BRSF", dev, NULL); hfp_gw_register(dev->gw, at_cmd_cind, "+CIND", dev, NULL); hfp_gw_register(dev->gw, at_cmd_cmer, "+CMER", dev, NULL); hfp_gw_register(dev->gw, at_cmd_chld, "+CHLD", dev, NULL); hfp_gw_register(dev->gw, at_cmd_bac, "+BAC", dev, NULL); } static void connect_cb(GIOChannel *chan, GError *err, gpointer user_data) { struct hf_device *dev = user_data; DBG(""); if (err) { error("handsfree: connect failed (%s)", err->message); goto failed; } dev->gw = hfp_gw_new(g_io_channel_unix_get_fd(chan)); if (!dev->gw) goto failed; g_io_channel_set_close_on_unref(chan, FALSE); hfp_gw_set_close_on_unref(dev->gw, true); hfp_gw_set_disconnect_handler(dev->gw, disconnect_watch, dev, NULL); if (dev->hsp) { register_post_slc_at(dev); set_state(dev, HAL_EV_HANDSFREE_CONN_STATE_CONNECTED); set_state(dev, HAL_EV_HANDSFREE_CONN_STATE_SLC_CONNECTED); return; } register_slc_at(dev); set_state(dev, HAL_EV_HANDSFREE_CONN_STATE_CONNECTED); return; failed: g_io_channel_shutdown(chan, TRUE, NULL); device_destroy(dev); } static void confirm_cb(GIOChannel *chan, gpointer data) { char address[18]; bdaddr_t bdaddr; GError *err = NULL; struct hf_device *dev; bt_io_get(chan, &err, BT_IO_OPT_DEST, address, BT_IO_OPT_DEST_BDADDR, &bdaddr, BT_IO_OPT_INVALID); if (err) { error("handsfree: confirm failed (%s)", err->message); g_error_free(err); goto drop; } DBG("incoming connect from %s", address); dev = get_device(&bdaddr); if (!dev) { error("handsfree: Failed to get device object for %s", address); goto drop; } if (dev->state != HAL_EV_HANDSFREE_CONN_STATE_DISCONNECTED) { info("handsfree: refusing connection from %s", address); goto drop; } if (!bt_io_accept(chan, connect_cb, dev, NULL, NULL)) { error("handsfree: failed to accept connection"); device_destroy(dev); goto drop; } dev->hsp = GPOINTER_TO_INT(data); set_state(dev, HAL_EV_HANDSFREE_CONN_STATE_CONNECTING); return; drop: g_io_channel_shutdown(chan, TRUE, NULL); } static void sdp_hsp_search_cb(sdp_list_t *recs, int err, gpointer data) { struct hf_device *dev = data; sdp_list_t *protos; GError *gerr = NULL; GIOChannel *io; uuid_t class; int channel; DBG(""); if (err < 0) { error("handsfree: unable to get SDP record: %s", strerror(-err)); goto fail; } sdp_uuid16_create(&class, HEADSET_SVCLASS_ID); /* Find record with proper service class */ for (; recs; recs = recs->next) { sdp_record_t *rec = recs->data; if (rec && !sdp_uuid_cmp(&rec->svclass, &class)) break; } if (!recs || !recs->data) { info("handsfree: no valid HSP SDP records found"); goto fail; } if (sdp_get_access_protos(recs->data, &protos) < 0) { error("handsfree: unable to get access protocols from record"); goto fail; } /* TODO read remote version? */ /* TODO read volume control support */ channel = sdp_get_proto_port(protos, RFCOMM_UUID); sdp_list_foreach(protos, (sdp_list_func_t) sdp_list_free, NULL); sdp_list_free(protos, NULL); if (channel <= 0) { error("handsfree: unable to get RFCOMM channel from record"); goto fail; } io = bt_io_connect(connect_cb, dev, NULL, &gerr, BT_IO_OPT_SOURCE_BDADDR, &adapter_addr, BT_IO_OPT_DEST_BDADDR, &dev->bdaddr, BT_IO_OPT_SEC_LEVEL, BT_IO_SEC_MEDIUM, BT_IO_OPT_CHANNEL, channel, BT_IO_OPT_INVALID); if (!io) { error("handsfree: unable to connect: %s", gerr->message); g_error_free(gerr); goto fail; } dev->hsp = true; g_io_channel_unref(io); return; fail: device_destroy(dev); } static int sdp_search_hsp(struct hf_device *dev) { uuid_t uuid; sdp_uuid16_create(&uuid, HEADSET_SVCLASS_ID); return bt_search_service(&adapter_addr, &dev->bdaddr, &uuid, sdp_hsp_search_cb, dev, NULL, 0); } static void sdp_hfp_search_cb(sdp_list_t *recs, int err, gpointer data) { struct hf_device *dev = data; sdp_list_t *protos; GError *gerr = NULL; GIOChannel *io; uuid_t class; int channel; DBG(""); if (err < 0) { error("handsfree: unable to get SDP record: %s", strerror(-err)); goto fail; } sdp_uuid16_create(&class, HANDSFREE_SVCLASS_ID); /* Find record with proper service class */ for (; recs; recs = recs->next) { sdp_record_t *rec = recs->data; if (rec && !sdp_uuid_cmp(&rec->svclass, &class)) break; } if (!recs || !recs->data) { info("handsfree: no HFP SDP records found, trying HSP"); if (sdp_search_hsp(dev) < 0) { error("handsfree: HSP SDP search failed"); goto fail; } return; } if (sdp_get_access_protos(recs->data, &protos) < 0) { error("handsfree: unable to get access protocols from record"); goto fail; } channel = sdp_get_proto_port(protos, RFCOMM_UUID); sdp_list_foreach(protos, (sdp_list_func_t) sdp_list_free, NULL); sdp_list_free(protos, NULL); if (channel <= 0) { error("handsfree: unable to get RFCOMM channel from record"); goto fail; } /* TODO read remote version? */ io = bt_io_connect(connect_cb, dev, NULL, &gerr, BT_IO_OPT_SOURCE_BDADDR, &adapter_addr, BT_IO_OPT_DEST_BDADDR, &dev->bdaddr, BT_IO_OPT_SEC_LEVEL, BT_IO_SEC_MEDIUM, BT_IO_OPT_CHANNEL, channel, BT_IO_OPT_INVALID); if (!io) { error("handsfree: unable to connect: %s", gerr->message); g_error_free(gerr); goto fail; } g_io_channel_unref(io); return; fail: device_destroy(dev); } static int sdp_search_hfp(struct hf_device *dev) { uuid_t uuid; sdp_uuid16_create(&uuid, HANDSFREE_SVCLASS_ID); return bt_search_service(&adapter_addr, &dev->bdaddr, &uuid, sdp_hfp_search_cb, dev, NULL, 0); } static void handle_connect(const void *buf, uint16_t len) { const struct hal_cmd_handsfree_connect *cmd = buf; struct hf_device *dev; char addr[18]; uint8_t status; bdaddr_t bdaddr; int ret; DBG(""); android2bdaddr(&cmd->bdaddr, &bdaddr); dev = get_device(&bdaddr); if (!dev) { status = HAL_STATUS_FAILED; goto failed; } if (dev->state != HAL_EV_HANDSFREE_CONN_STATE_DISCONNECTED) { status = HAL_STATUS_FAILED; goto failed; } ba2str(&bdaddr, addr); DBG("connecting to %s", addr); /* prefer HFP over HSP */ ret = hfp_server ? sdp_search_hfp(dev) : sdp_search_hsp(dev); if (ret < 0) { error("handsfree: SDP search failed"); device_destroy(dev); status = HAL_STATUS_FAILED; goto failed; } set_state(dev, HAL_EV_HANDSFREE_CONN_STATE_CONNECTING); status = HAL_STATUS_SUCCESS; failed: ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_HANDSFREE, HAL_OP_HANDSFREE_CONNECT, status); } static void handle_disconnect(const void *buf, uint16_t len) { const struct hal_cmd_handsfree_disconnect *cmd = buf; struct hf_device *dev; bdaddr_t bdaddr; uint8_t status; DBG(""); android2bdaddr(cmd->bdaddr, &bdaddr); dev = find_device(&bdaddr); if (!dev) { status = HAL_STATUS_FAILED; goto failed; } if (dev->state == HAL_EV_HANDSFREE_CONN_STATE_DISCONNECTED) { status = HAL_STATUS_FAILED; goto failed; } if (dev->state == HAL_EV_HANDSFREE_CONN_STATE_DISCONNECTING) { status = HAL_STATUS_SUCCESS; goto failed; } if (dev->state == HAL_EV_HANDSFREE_CONN_STATE_CONNECTING) { device_destroy(dev); } else { set_state(dev, HAL_EV_HANDSFREE_CONN_STATE_DISCONNECTING); hfp_gw_disconnect(dev->gw); } status = HAL_STATUS_SUCCESS; failed: ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_HANDSFREE, HAL_OP_HANDSFREE_DISCONNECT, status); } static bool disconnect_sco(struct hf_device *dev) { if (dev->audio_state == HAL_EV_HANDSFREE_AUDIO_STATE_DISCONNECTED || dev->audio_state == HAL_EV_HANDSFREE_AUDIO_STATE_DISCONNECTING) return false; bt_sco_disconnect(sco); set_audio_state(dev, HAL_EV_HANDSFREE_AUDIO_STATE_DISCONNECTING); return true; } static bool connect_audio(struct hf_device *dev) { if (dev->audio_state != HAL_EV_HANDSFREE_AUDIO_STATE_DISCONNECTED) return false; /* we haven't negotiated codec, start selection */ if (codec_negotiation_supported(dev) && !dev->negotiated_codec) { select_codec(dev, 0); return true; } return connect_sco(dev); } static void handle_connect_audio(const void *buf, uint16_t len) { const struct hal_cmd_handsfree_connect_audio *cmd = buf; struct hf_device *dev; bdaddr_t bdaddr; uint8_t status; DBG(""); android2bdaddr(cmd->bdaddr, &bdaddr); dev = find_device(&bdaddr); if (!dev) { status = HAL_STATUS_FAILED; goto done; } if (dev->audio_state != HAL_EV_HANDSFREE_AUDIO_STATE_DISCONNECTED) { status = HAL_STATUS_FAILED; goto done; } status = connect_audio(dev) ? HAL_STATUS_SUCCESS : HAL_STATUS_FAILED; done: ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_HANDSFREE, HAL_OP_HANDSFREE_CONNECT_AUDIO, status); } static void handle_disconnect_audio(const void *buf, uint16_t len) { const struct hal_cmd_handsfree_disconnect_audio *cmd = buf; struct hf_device *dev; bdaddr_t bdaddr; uint8_t status; DBG(""); android2bdaddr(cmd->bdaddr, &bdaddr); dev = find_device(&bdaddr); if (!dev) { status = HAL_STATUS_FAILED; goto done; } status = disconnect_sco(dev) ? HAL_STATUS_SUCCESS : HAL_STATUS_FAILED; done: ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_HANDSFREE, HAL_OP_HANDSFREE_DISCONNECT_AUDIO, status); } static void handle_start_vr(const void *buf, uint16_t len) { const struct hal_cmd_handsfree_start_vr *cmd = buf; struct hf_device *dev; bdaddr_t bdaddr; uint8_t status; DBG(""); android2bdaddr(cmd->bdaddr, &bdaddr); dev = find_device(&bdaddr); if (!dev) { status = HAL_STATUS_FAILED; goto done; } if (dev->features & HFP_HF_FEAT_VR) { hfp_gw_send_info(dev->gw, "+BVRA: 1"); status = HAL_STATUS_SUCCESS; } else { status = HAL_STATUS_FAILED; } done: ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_HANDSFREE, HAL_OP_HANDSFREE_START_VR, status); } static void handle_stop_vr(const void *buf, uint16_t len) { const struct hal_cmd_handsfree_stop_vr *cmd = buf; struct hf_device *dev; bdaddr_t bdaddr; uint8_t status; DBG(""); android2bdaddr(cmd->bdaddr, &bdaddr); dev = find_device(&bdaddr); if (!dev) { status = HAL_STATUS_FAILED; goto done; } if (dev->features & HFP_HF_FEAT_VR) { hfp_gw_send_info(dev->gw, "+BVRA: 0"); status = HAL_STATUS_SUCCESS; } else { status = HAL_STATUS_FAILED; } done: ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_HANDSFREE, HAL_OP_HANDSFREE_STOP_VR, status); } static void handle_volume_control(const void *buf, uint16_t len) { const struct hal_cmd_handsfree_volume_control *cmd = buf; struct hf_device *dev; uint8_t status, volume; bdaddr_t bdaddr; DBG("type=%u volume=%u", cmd->type, cmd->volume); android2bdaddr(cmd->bdaddr, &bdaddr); dev = find_device(&bdaddr); if (!dev) { status = HAL_STATUS_FAILED; goto done; } volume = cmd->volume > 15 ? 15 : cmd->volume; switch (cmd->type) { case HAL_HANDSFREE_VOLUME_TYPE_MIC: hfp_gw_send_info(dev->gw, "+VGM: %u", volume); status = HAL_STATUS_SUCCESS; break; case HAL_HANDSFREE_VOLUME_TYPE_SPEAKER: hfp_gw_send_info(dev->gw, "+VGS: %u", volume); status = HAL_STATUS_SUCCESS; break; default: status = HAL_STATUS_FAILED; break; } done: ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_HANDSFREE, HAL_OP_HANDSFREE_VOLUME_CONTROL, status); } static void update_indicator(struct hf_device *dev, int ind, uint8_t val) { DBG("ind=%u new=%u old=%u", ind, val, dev->inds[ind].val); if (dev->inds[ind].val == val) return; dev->inds[ind].val = val; if (!dev->indicators_enabled) return; if (!dev->inds[ind].active) return; /* indicator numbers in CIEV start from 1 */ hfp_gw_send_info(dev->gw, "+CIEV: %u,%u", ind + 1, val); } static void device_status_notif(void *data, void *user_data) { struct hf_device *dev = data; struct hal_cmd_handsfree_device_status_notif *cmd = user_data; update_indicator(dev, IND_SERVICE, cmd->state); update_indicator(dev, IND_ROAM, cmd->type); update_indicator(dev, IND_SIGNAL, cmd->signal); update_indicator(dev, IND_BATTCHG, cmd->battery); } static void handle_device_status_notif(const void *buf, uint16_t len) { const struct hal_cmd_handsfree_device_status_notif *cmd = buf; uint8_t status; DBG(""); if (queue_isempty(devices)) { status = HAL_STATUS_FAILED; goto done; } /* Cast cmd to void as queue api needs that */ queue_foreach(devices, device_status_notif, (void *) cmd); status = HAL_STATUS_SUCCESS; done: ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_HANDSFREE, HAL_OP_HANDSFREE_DEVICE_STATUS_NOTIF, status); } static void handle_cops(const void *buf, uint16_t len) { const struct hal_cmd_handsfree_cops_response *cmd = buf; struct hf_device *dev; bdaddr_t bdaddr; uint8_t status; if (len != sizeof(*cmd) + cmd->len || (cmd->len != 0 && cmd->buf[cmd->len - 1] != '\0')) { error("Invalid cops response command, terminating"); raise(SIGTERM); return; } DBG(""); android2bdaddr(cmd->bdaddr, &bdaddr); dev = find_device(&bdaddr); if (!dev) { status = HAL_STATUS_FAILED; goto done; } hfp_gw_send_info(dev->gw, "+COPS: 0,0,\"%.16s\"", cmd->len ? (char *) cmd->buf : ""); hfp_gw_send_result(dev->gw, HFP_RESULT_OK); status = HAL_STATUS_SUCCESS; done: ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_HANDSFREE, HAL_OP_HANDSFREE_COPS_RESPONSE, status); } static unsigned int get_callsetup(uint8_t state) { switch (state) { case HAL_HANDSFREE_CALL_STATE_INCOMING: return 1; case HAL_HANDSFREE_CALL_STATE_DIALING: return 2; case HAL_HANDSFREE_CALL_STATE_ALERTING: return 3; default: return 0; } } static void handle_cind(const void *buf, uint16_t len) { const struct hal_cmd_handsfree_cind_response *cmd = buf; struct hf_device *dev; bdaddr_t bdaddr; uint8_t status; DBG(""); android2bdaddr(cmd->bdaddr, &bdaddr); dev = find_device(&bdaddr); if (!dev) { status = HAL_STATUS_FAILED; goto done; } /* HAL doesn't provide indicators values so need to convert here */ dev->inds[IND_SERVICE].val = cmd->svc; dev->inds[IND_CALL].val = !!(cmd->num_active + cmd->num_held); dev->inds[IND_CALLSETUP].val = get_callsetup(cmd->state); dev->inds[IND_CALLHELD].val = cmd->num_held ? (cmd->num_active ? 1 : 2) : 0; dev->inds[IND_SIGNAL].val = cmd->signal; dev->inds[IND_ROAM].val = cmd->roam; dev->inds[IND_BATTCHG].val = cmd->batt_chg; /* Order must match indicators_defaults table */ hfp_gw_send_info(dev->gw, "+CIND: %u,%u,%u,%u,%u,%u,%u", dev->inds[IND_SERVICE].val, dev->inds[IND_CALL].val, dev->inds[IND_CALLSETUP].val, dev->inds[IND_CALLHELD].val, dev->inds[IND_SIGNAL].val, dev->inds[IND_ROAM].val, dev->inds[IND_BATTCHG].val); hfp_gw_send_result(dev->gw, HFP_RESULT_OK); status = HAL_STATUS_SUCCESS; done: ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_HANDSFREE, HAL_OP_HANDSFREE_CIND_RESPONSE, status); } static void handle_formatted_at_resp(const void *buf, uint16_t len) { const struct hal_cmd_handsfree_formatted_at_response *cmd = buf; struct hf_device *dev; bdaddr_t bdaddr; uint8_t status; DBG(""); if (len != sizeof(*cmd) + cmd->len || (cmd->len != 0 && cmd->buf[cmd->len - 1] != '\0')) { error("Invalid formatted AT response command, terminating"); raise(SIGTERM); return; } android2bdaddr(cmd->bdaddr, &bdaddr); dev = find_device(&bdaddr); if (!dev) { status = HAL_STATUS_FAILED; goto done; } hfp_gw_send_info(dev->gw, "%s", cmd->len ? (char *) cmd->buf : ""); status = HAL_STATUS_SUCCESS; done: ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_HANDSFREE, HAL_OP_HANDSFREE_FORMATTED_AT_RESPONSE, status); } static void handle_at_resp(const void *buf, uint16_t len) { const struct hal_cmd_handsfree_at_response *cmd = buf; struct hf_device *dev; bdaddr_t bdaddr; uint8_t status; DBG(""); android2bdaddr(cmd->bdaddr, &bdaddr); dev = find_device(&bdaddr); if (!dev) { status = HAL_STATUS_FAILED; goto done; } if (cmd->response == HAL_HANDSFREE_AT_RESPONSE_OK) hfp_gw_send_result(dev->gw, HFP_RESULT_OK); else if (dev->cmee_enabled) hfp_gw_send_error(dev->gw, cmd->error); else hfp_gw_send_result(dev->gw, HFP_RESULT_ERROR); status = HAL_STATUS_SUCCESS; done: ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_HANDSFREE, HAL_OP_HANDSFREE_AT_RESPONSE, status); } static void handle_clcc_resp(const void *buf, uint16_t len) { const struct hal_cmd_handsfree_clcc_response *cmd = buf; struct hf_device *dev; uint8_t status; bdaddr_t bdaddr; char *number; if (len != sizeof(*cmd) + cmd->number_len || (cmd->number_len != 0 && cmd->number[cmd->number_len - 1] != '\0')) { error("Invalid CLCC response command, terminating"); raise(SIGTERM); return; } DBG(""); android2bdaddr(cmd->bdaddr, &bdaddr); dev = find_device(&bdaddr); if (!dev) { status = HAL_STATUS_FAILED; goto done; } if (!cmd->index) { hfp_gw_send_result(dev->gw, HFP_RESULT_OK); status = HAL_STATUS_SUCCESS; goto done; } number = cmd->number_len ? (char *) cmd->number : ""; switch (cmd->state) { case HAL_HANDSFREE_CALL_STATE_INCOMING: case HAL_HANDSFREE_CALL_STATE_WAITING: case HAL_HANDSFREE_CALL_STATE_ACTIVE: case HAL_HANDSFREE_CALL_STATE_HELD: case HAL_HANDSFREE_CALL_STATE_DIALING: case HAL_HANDSFREE_CALL_STATE_ALERTING: if (cmd->type == HAL_HANDSFREE_CALL_ADDRTYPE_INTERNATIONAL && number[0] != '+') hfp_gw_send_info(dev->gw, "+CLCC: %u,%u,%u,%u,%u,\"+%s\",%u", cmd->index, cmd->dir, cmd->state, cmd->mode, cmd->mpty, number, cmd->type); else hfp_gw_send_info(dev->gw, "+CLCC: %u,%u,%u,%u,%u,\"%s\",%u", cmd->index, cmd->dir, cmd->state, cmd->mode, cmd->mpty, number, cmd->type); status = HAL_STATUS_SUCCESS; break; case HAL_HANDSFREE_CALL_STATE_IDLE: default: status = HAL_STATUS_FAILED; break; } done: ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_HANDSFREE, HAL_OP_HANDSFREE_CLCC_RESPONSE, status); } static gboolean ring_cb(gpointer user_data) { struct hf_device *dev = user_data; hfp_gw_send_info(dev->gw, "RING"); if (dev->clip_enabled && dev->clip) hfp_gw_send_info(dev->gw, "%s", dev->clip); return TRUE; } static void phone_state_dialing(struct hf_device *dev, int num_active, int num_held) { if (dev->call_hanging_up) { g_source_remove(dev->call_hanging_up); dev->call_hanging_up = 0; } update_indicator(dev, IND_CALLSETUP, 2); if (num_active == 0 && num_held > 0) update_indicator(dev, IND_CALLHELD, 2); if (dev->num_active == 0 && dev->num_held == 0) connect_audio(dev); } static void phone_state_alerting(struct hf_device *dev, int num_active, int num_held) { if (dev->call_hanging_up) { g_source_remove(dev->call_hanging_up); dev->call_hanging_up = 0; } update_indicator(dev, IND_CALLSETUP, 3); } static void phone_state_waiting(struct hf_device *dev, int num_active, int num_held, uint8_t type, const uint8_t *number, int number_len) { char *num; if (!dev->ccwa_enabled) return; num = number_len ? (char *) number : ""; if (type == HAL_HANDSFREE_CALL_ADDRTYPE_INTERNATIONAL && num[0] != '+') hfp_gw_send_info(dev->gw, "+CCWA: \"+%s\",%u", num, type); else hfp_gw_send_info(dev->gw, "+CCWA: \"%s\",%u", num, type); update_indicator(dev, IND_CALLSETUP, 1); } static void phone_state_incoming(struct hf_device *dev, int num_active, int num_held, uint8_t type, const uint8_t *number, int number_len) { char *num; if (dev->setup_state == HAL_HANDSFREE_CALL_STATE_INCOMING) { if (dev->num_active != num_active || dev->num_held != num_held) { if (dev->num_active == num_held && dev->num_held == num_active) return; /* * calls changed while waiting call ie. due to * termination of active call */ update_indicator(dev, IND_CALLHELD, num_held ? (num_active ? 1 : 2) : 0); update_indicator(dev, IND_CALL, !!(num_active + num_held)); } return; } if (dev->call_hanging_up) return; if (num_active > 0 || num_held > 0) { phone_state_waiting(dev, num_active, num_held, type, number, number_len); return; } update_indicator(dev, IND_CALLSETUP, 1); num = number_len ? (char *) number : ""; if (type == HAL_HANDSFREE_CALL_ADDRTYPE_INTERNATIONAL && num[0] != '+') dev->clip = g_strdup_printf("+CLIP: \"+%s\",%u", num, type); else dev->clip = g_strdup_printf("+CLIP: \"%s\",%u", num, type); /* send first RING */ ring_cb(dev); dev->ring = g_timeout_add_seconds_full(G_PRIORITY_DEFAULT, RING_TIMEOUT, ring_cb, dev, NULL); if (!dev->ring) { g_free(dev->clip); dev->clip = NULL; } } static gboolean hang_up_cb(gpointer user_data) { struct hf_device *dev = user_data; DBG(""); dev->call_hanging_up = 0; return FALSE; } static void phone_state_idle(struct hf_device *dev, int num_active, int num_held) { if (dev->ring) { g_source_remove(dev->ring); dev->ring = 0; if (dev->clip) { g_free(dev->clip); dev->clip = NULL; } } switch (dev->setup_state) { case HAL_HANDSFREE_CALL_STATE_INCOMING: if (num_active > dev->num_active) { update_indicator(dev, IND_CALL, 1); if (dev->num_active == 0 && dev->num_held == 0) connect_audio(dev); } if (num_held >= dev->num_held && num_held != 0) update_indicator(dev, IND_CALLHELD, 1); update_indicator(dev, IND_CALLSETUP, 0); if (num_active == 0 && num_held == 0 && num_active == dev->num_active && num_held == dev->num_held) dev->call_hanging_up = g_timeout_add(800, hang_up_cb, dev); break; case HAL_HANDSFREE_CALL_STATE_DIALING: case HAL_HANDSFREE_CALL_STATE_ALERTING: if (num_active > dev->num_active) update_indicator(dev, IND_CALL, 1); update_indicator(dev, IND_CALLHELD, num_held ? (num_active ? 1 : 2) : 0); update_indicator(dev, IND_CALLSETUP, 0); /* disconnect SCO if we hang up while dialing or alerting */ if (num_active == 0 && num_held == 0) disconnect_sco(dev); break; case HAL_HANDSFREE_CALL_STATE_IDLE: if (dev->call_hanging_up) { g_source_remove(dev->call_hanging_up); dev->call_hanging_up = 0; return; } /* check if calls swapped */ if (num_held != 0 && num_active != 0 && dev->num_active == num_held && dev->num_held == num_active) { /* TODO better way for forcing indicator */ dev->inds[IND_CALLHELD].val = 0; } else if ((num_active > 0 || num_held > 0) && dev->num_active == 0 && dev->num_held == 0) { /* * If number of active or held calls change but there * was no call setup change this means that there were * calls present when headset was connected. */ connect_audio(dev); } else if (num_active == 0 && num_held == 0) { disconnect_sco(dev); } update_indicator(dev, IND_CALLHELD, num_held ? (num_active ? 1 : 2) : 0); update_indicator(dev, IND_CALL, !!(num_active + num_held)); update_indicator(dev, IND_CALLSETUP, 0); /* If call was terminated due to carrier lost send NO CARRIER */ if (num_active == 0 && num_held == 0 && dev->inds[IND_SERVICE].val == 0 && (dev->num_active > 0 || dev->num_held > 0)) hfp_gw_send_info(dev->gw, "NO CARRIER"); break; default: DBG("unhandled state %u", dev->setup_state); break; } } static void phone_state_change(void *data, void *user_data) { struct hf_device *dev = data; struct hal_cmd_handsfree_phone_state_change *cmd = user_data; switch (cmd->state) { case HAL_HANDSFREE_CALL_STATE_DIALING: phone_state_dialing(dev, cmd->num_active, cmd->num_held); break; case HAL_HANDSFREE_CALL_STATE_ALERTING: phone_state_alerting(dev, cmd->num_active, cmd->num_held); break; case HAL_HANDSFREE_CALL_STATE_INCOMING: phone_state_incoming(dev, cmd->num_active, cmd->num_held, cmd->type, cmd->number, cmd->number_len); break; case HAL_HANDSFREE_CALL_STATE_IDLE: phone_state_idle(dev, cmd->num_active, cmd->num_held); break; default: DBG("unhandled new state %u (current state %u)", cmd->state, dev->setup_state); return; } dev->num_active = cmd->num_active; dev->num_held = cmd->num_held; dev->setup_state = cmd->state; } static void handle_phone_state_change(const void *buf, uint16_t len) { const struct hal_cmd_handsfree_phone_state_change *cmd = buf; uint8_t status; if (len != sizeof(*cmd) + cmd->number_len || (cmd->number_len != 0 && cmd->number[cmd->number_len - 1] != '\0')) { error("Invalid phone state change command, terminating"); raise(SIGTERM); return; } DBG("active=%u hold=%u state=%u", cmd->num_active, cmd->num_held, cmd->state); if (queue_isempty(devices)) { status = HAL_STATUS_FAILED; goto failed; } /* Cast cmd to void as queue api needs that */ queue_foreach(devices, phone_state_change, (void *) cmd); status = HAL_STATUS_SUCCESS; failed: ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_HANDSFREE, HAL_OP_HANDSFREE_PHONE_STATE_CHANGE, status); } static void handle_configure_wbs(const void *buf, uint16_t len) { const struct hal_cmd_handsfree_configure_wbs *cmd = buf; struct hf_device *dev; bdaddr_t bdaddr; uint8_t status; if (!(hfp_ag_features & HFP_AG_FEAT_CODEC)) { status = HAL_STATUS_FAILED; goto done; } android2bdaddr(cmd->bdaddr, &bdaddr); dev = find_device(&bdaddr); if (!dev) { status = HAL_STATUS_FAILED; goto done; } if (dev->audio_state != HAL_EV_HANDSFREE_AUDIO_STATE_DISCONNECTED) { status = HAL_STATUS_FAILED; goto done; } switch (cmd->config) { case HAL_HANDSFREE_WBS_NO: dev->codecs[MSBC_OFFSET].local_supported = false; break; case HAL_HANDSFREE_WBS_YES: dev->codecs[MSBC_OFFSET].local_supported = true; break; case HAL_HANDSFREE_WBS_NONE: /* TODO */ default: status = HAL_STATUS_FAILED; goto done; } /* * cleanup negotiated codec if WBS support was changed, it will be * renegotiated on next audio connection based on currently supported * codecs */ dev->negotiated_codec = 0; status = HAL_STATUS_SUCCESS; done: ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_HANDSFREE, HAL_OP_HANDSFREE_CONFIGURE_WBS, status); } static const struct ipc_handler cmd_handlers[] = { /* HAL_OP_HANDSFREE_CONNECT */ { handle_connect, false, sizeof(struct hal_cmd_handsfree_connect) }, /* HAL_OP_HANDSFREE_DISCONNECT */ { handle_disconnect, false, sizeof(struct hal_cmd_handsfree_disconnect) }, /* HAL_OP_HANDSFREE_CONNECT_AUDIO */ { handle_connect_audio, false, sizeof(struct hal_cmd_handsfree_connect_audio) }, /* HAL_OP_HANDSFREE_DISCONNECT_AUDIO */ { handle_disconnect_audio, false, sizeof(struct hal_cmd_handsfree_disconnect_audio) }, /* define HAL_OP_HANDSFREE_START_VR */ { handle_start_vr, false, sizeof(struct hal_cmd_handsfree_start_vr) }, /* define HAL_OP_HANDSFREE_STOP_VR */ { handle_stop_vr, false, sizeof(struct hal_cmd_handsfree_stop_vr) }, /* HAL_OP_HANDSFREE_VOLUME_CONTROL */ { handle_volume_control, false, sizeof(struct hal_cmd_handsfree_volume_control) }, /* HAL_OP_HANDSFREE_DEVICE_STATUS_NOTIF */ { handle_device_status_notif, false, sizeof(struct hal_cmd_handsfree_device_status_notif) }, /* HAL_OP_HANDSFREE_COPS_RESPONSE */ { handle_cops, true, sizeof(struct hal_cmd_handsfree_cops_response) }, /* HAL_OP_HANDSFREE_CIND_RESPONSE */ { handle_cind, false, sizeof(struct hal_cmd_handsfree_cind_response) }, /* HAL_OP_HANDSFREE_FORMATTED_AT_RESPONSE */ { handle_formatted_at_resp, true, sizeof(struct hal_cmd_handsfree_formatted_at_response) }, /* HAL_OP_HANDSFREE_AT_RESPONSE */ { handle_at_resp, false, sizeof(struct hal_cmd_handsfree_at_response) }, /* HAL_OP_HANDSFREE_CLCC_RESPONSE */ { handle_clcc_resp, true, sizeof(struct hal_cmd_handsfree_clcc_response) }, /* HAL_OP_HANDSFREE_PHONE_STATE_CHANGE */ { handle_phone_state_change, true, sizeof(struct hal_cmd_handsfree_phone_state_change) }, /* HAL_OP_HANDSFREE_CONFIGURE_WBS */ { handle_configure_wbs, false, sizeof(struct hal_cmd_handsfree_configure_wbs) }, }; static sdp_record_t *headset_ag_record(void) { sdp_list_t *svclass_id, *pfseq, *apseq, *root; uuid_t root_uuid, svclass_uuid, ga_svclass_uuid; uuid_t l2cap_uuid, rfcomm_uuid; sdp_profile_desc_t profile; sdp_list_t *aproto, *proto[2]; sdp_record_t *record; sdp_data_t *channel; uint8_t netid = 0x01; sdp_data_t *network; uint8_t ch = HSP_AG_CHANNEL; record = sdp_record_alloc(); if (!record) return NULL; network = sdp_data_alloc(SDP_UINT8, &netid); if (!network) { sdp_record_free(record); return NULL; } sdp_uuid16_create(&root_uuid, PUBLIC_BROWSE_GROUP); root = sdp_list_append(NULL, &root_uuid); sdp_set_browse_groups(record, root); sdp_uuid16_create(&svclass_uuid, HEADSET_AGW_SVCLASS_ID); svclass_id = sdp_list_append(NULL, &svclass_uuid); sdp_uuid16_create(&ga_svclass_uuid, GENERIC_AUDIO_SVCLASS_ID); svclass_id = sdp_list_append(svclass_id, &ga_svclass_uuid); sdp_set_service_classes(record, svclass_id); sdp_uuid16_create(&profile.uuid, HEADSET_PROFILE_ID); profile.version = 0x0102; pfseq = sdp_list_append(NULL, &profile); sdp_set_profile_descs(record, pfseq); sdp_uuid16_create(&l2cap_uuid, L2CAP_UUID); proto[0] = sdp_list_append(NULL, &l2cap_uuid); apseq = sdp_list_append(NULL, proto[0]); sdp_uuid16_create(&rfcomm_uuid, RFCOMM_UUID); proto[1] = sdp_list_append(NULL, &rfcomm_uuid); channel = sdp_data_alloc(SDP_UINT8, &ch); proto[1] = sdp_list_append(proto[1], channel); apseq = sdp_list_append(apseq, proto[1]); aproto = sdp_list_append(NULL, apseq); sdp_set_access_protos(record, aproto); sdp_set_info_attr(record, "Voice Gateway", NULL, NULL); sdp_attr_add(record, SDP_ATTR_EXTERNAL_NETWORK, network); sdp_data_free(channel); sdp_list_free(proto[0], NULL); sdp_list_free(proto[1], NULL); sdp_list_free(apseq, NULL); sdp_list_free(pfseq, NULL); sdp_list_free(aproto, NULL); sdp_list_free(root, NULL); sdp_list_free(svclass_id, NULL); return record; } static bool confirm_sco_cb(const bdaddr_t *addr, uint16_t *voice_settings) { char address[18]; struct hf_device *dev; ba2str(addr, address); DBG("incoming SCO connection from %s", address); dev = find_device(addr); if (!dev || dev->state != HAL_EV_HANDSFREE_CONN_STATE_SLC_CONNECTED) { error("handsfree: audio connection from %s rejected", address); return false; } /* If HF initiate SCO there must be no WBS used */ *voice_settings = 0; set_audio_state(dev, HAL_EV_HANDSFREE_AUDIO_STATE_CONNECTING); return true; } static bool enable_hsp_ag(void) { sdp_record_t *rec; GError *err = NULL; DBG(""); hsp_server = bt_io_listen(NULL, confirm_cb, GINT_TO_POINTER(true), NULL, &err, BT_IO_OPT_SOURCE_BDADDR, &adapter_addr, BT_IO_OPT_CHANNEL, HSP_AG_CHANNEL, BT_IO_OPT_SEC_LEVEL, BT_IO_SEC_MEDIUM, BT_IO_OPT_INVALID); if (!hsp_server) { error("Failed to listen on Headset rfcomm: %s", err->message); g_error_free(err); return false; } rec = headset_ag_record(); if (!rec) { error("Failed to allocate Headset record"); goto failed; } if (bt_adapter_add_record(rec, 0) < 0) { error("Failed to register Headset record"); sdp_record_free(rec); goto failed; } hsp_record_id = rec->handle; return true; failed: g_io_channel_shutdown(hsp_server, TRUE, NULL); g_io_channel_unref(hsp_server); hsp_server = NULL; return false; } static void cleanup_hsp_ag(void) { if (hsp_server) { g_io_channel_shutdown(hsp_server, TRUE, NULL); g_io_channel_unref(hsp_server); hsp_server = NULL; } if (hsp_record_id > 0) { bt_adapter_remove_record(hsp_record_id); hsp_record_id = 0; } } static sdp_record_t *hfp_ag_record(void) { sdp_list_t *svclass_id, *pfseq, *apseq, *root; uuid_t root_uuid, svclass_uuid, ga_svclass_uuid; uuid_t l2cap_uuid, rfcomm_uuid; sdp_profile_desc_t profile; sdp_list_t *aproto, *proto[2]; sdp_record_t *record; sdp_data_t *channel, *features; uint8_t netid = 0x01; uint16_t sdpfeat; sdp_data_t *network; uint8_t ch = HFP_AG_CHANNEL; record = sdp_record_alloc(); if (!record) return NULL; network = sdp_data_alloc(SDP_UINT8, &netid); if (!network) { sdp_record_free(record); return NULL; } sdp_uuid16_create(&root_uuid, PUBLIC_BROWSE_GROUP); root = sdp_list_append(NULL, &root_uuid); sdp_set_browse_groups(record, root); sdp_uuid16_create(&svclass_uuid, HANDSFREE_AGW_SVCLASS_ID); svclass_id = sdp_list_append(NULL, &svclass_uuid); sdp_uuid16_create(&ga_svclass_uuid, GENERIC_AUDIO_SVCLASS_ID); svclass_id = sdp_list_append(svclass_id, &ga_svclass_uuid); sdp_set_service_classes(record, svclass_id); sdp_uuid16_create(&profile.uuid, HANDSFREE_PROFILE_ID); profile.version = 0x0106; pfseq = sdp_list_append(NULL, &profile); sdp_set_profile_descs(record, pfseq); sdp_uuid16_create(&l2cap_uuid, L2CAP_UUID); proto[0] = sdp_list_append(NULL, &l2cap_uuid); apseq = sdp_list_append(NULL, proto[0]); sdp_uuid16_create(&rfcomm_uuid, RFCOMM_UUID); proto[1] = sdp_list_append(NULL, &rfcomm_uuid); channel = sdp_data_alloc(SDP_UINT8, &ch); proto[1] = sdp_list_append(proto[1], channel); apseq = sdp_list_append(apseq, proto[1]); /* Codec Negotiation bit in SDP feature is different then in BRSF */ sdpfeat = hfp_ag_features & 0x0000003F; if (hfp_ag_features & HFP_AG_FEAT_CODEC) sdpfeat |= 0x00000020; else sdpfeat &= ~0x00000020; features = sdp_data_alloc(SDP_UINT16, &sdpfeat); sdp_attr_add(record, SDP_ATTR_SUPPORTED_FEATURES, features); aproto = sdp_list_append(NULL, apseq); sdp_set_access_protos(record, aproto); sdp_set_info_attr(record, "Hands-Free Audio Gateway", NULL, NULL); sdp_attr_add(record, SDP_ATTR_EXTERNAL_NETWORK, network); sdp_data_free(channel); sdp_list_free(proto[0], NULL); sdp_list_free(proto[1], NULL); sdp_list_free(apseq, NULL); sdp_list_free(pfseq, NULL); sdp_list_free(aproto, NULL); sdp_list_free(root, NULL); sdp_list_free(svclass_id, NULL); return record; } static bool enable_hfp_ag(void) { sdp_record_t *rec; GError *err = NULL; DBG(""); if (hfp_server) return false; hfp_server = bt_io_listen(NULL, confirm_cb, GINT_TO_POINTER(false), NULL, &err, BT_IO_OPT_SOURCE_BDADDR, &adapter_addr, BT_IO_OPT_CHANNEL, HFP_AG_CHANNEL, BT_IO_OPT_SEC_LEVEL, BT_IO_SEC_MEDIUM, BT_IO_OPT_INVALID); if (!hfp_server) { error("Failed to listen on Handsfree rfcomm: %s", err->message); g_error_free(err); return false; } rec = hfp_ag_record(); if (!rec) { error("Failed to allocate Handsfree record"); goto failed; } if (bt_adapter_add_record(rec, 0) < 0) { error("Failed to register Handsfree record"); sdp_record_free(rec); goto failed; } hfp_record_id = rec->handle; return true; failed: g_io_channel_shutdown(hfp_server, TRUE, NULL); g_io_channel_unref(hfp_server); hfp_server = NULL; return false; } static void cleanup_hfp_ag(void) { if (hfp_server) { g_io_channel_shutdown(hfp_server, TRUE, NULL); g_io_channel_unref(hfp_server); hfp_server = NULL; } if (hfp_record_id > 0) { bt_adapter_remove_record(hfp_record_id); hfp_record_id = 0; } } static void bt_sco_get_fd(const void *buf, uint16_t len) { const struct sco_cmd_get_fd *cmd = buf; struct sco_rsp_get_fd rsp; struct hf_device *dev; bdaddr_t bdaddr; uint16_t mtu; int fd; DBG(""); android2bdaddr(cmd->bdaddr, &bdaddr); dev = find_device(&bdaddr); if (!dev || !bt_sco_get_fd_and_mtu(sco, &fd, &mtu)) goto failed; rsp.mtu = mtu; DBG("fd %d mtu %u", fd, rsp.mtu); ipc_send_rsp_full(sco_ipc, SCO_SERVICE_ID, SCO_OP_GET_FD, sizeof(rsp), &rsp, fd); return; failed: ipc_send_rsp(sco_ipc, SCO_SERVICE_ID, SCO_OP_STATUS, SCO_STATUS_FAILED); } static const struct ipc_handler sco_handlers[] = { /* SCO_OP_GET_FD */ { bt_sco_get_fd, false, sizeof(struct sco_cmd_get_fd) } }; static void bt_sco_unregister(void) { DBG(""); ipc_cleanup(sco_ipc); sco_ipc = NULL; } static bool bt_sco_register(ipc_disconnect_cb disconnect) { DBG(""); sco_ipc = ipc_init(BLUEZ_SCO_SK_PATH, sizeof(BLUEZ_SCO_SK_PATH), SCO_SERVICE_ID, false, disconnect, NULL); if (!sco_ipc) return false; ipc_register(sco_ipc, SCO_SERVICE_ID, sco_handlers, G_N_ELEMENTS(sco_handlers)); return true; } bool bt_handsfree_register(struct ipc *ipc, const bdaddr_t *addr, uint8_t mode, int max_clients) { DBG("mode 0x%x max_clients %d", mode, max_clients); bacpy(&adapter_addr, addr); if (max_clients < 1) return false; devices = queue_new(); if (!enable_hsp_ag()) goto failed_queue; sco = bt_sco_new(addr); if (!sco) goto failed_hsp; bt_sco_set_confirm_cb(sco, confirm_sco_cb); bt_sco_set_connect_cb(sco, connect_sco_cb); bt_sco_set_disconnect_cb(sco, disconnect_sco_cb); if (mode == HAL_MODE_HANDSFREE_HSP_ONLY) goto done; hfp_ag_features = HFP_AG_FEATURES; if (mode == HAL_MODE_HANDSFREE_HFP_WBS) hfp_ag_features |= HFP_AG_FEAT_CODEC; if (enable_hfp_ag()) goto done; bt_sco_unref(sco); sco = NULL; hfp_ag_features = 0; failed_hsp: cleanup_hsp_ag(); failed_queue: queue_destroy(devices, NULL); devices = NULL; return false; done: hal_ipc = ipc; ipc_register(hal_ipc, HAL_SERVICE_ID_HANDSFREE, cmd_handlers, G_N_ELEMENTS(cmd_handlers)); bt_sco_register(NULL); max_hfp_clients = max_clients; return true; } void bt_handsfree_unregister(void) { DBG(""); bt_sco_unregister(); ipc_unregister(hal_ipc, HAL_SERVICE_ID_HANDSFREE); hal_ipc = NULL; cleanup_hfp_ag(); cleanup_hsp_ag(); bt_sco_unref(sco); sco = NULL; hfp_ag_features = 0; queue_destroy(devices, (queue_destroy_func_t) device_destroy); devices = NULL; max_hfp_clients = 0; } bluez-5.82/android/PaxHeaders/pts-gatt.txt0000644000000000000000000000005012537515745015610 xustar0020 atime=1743516876 20 ctime=1743591289 bluez-5.82/android/pts-gatt.txt0000644000000000000000000013344112537515745015277 0ustar00rootrootPTS test results for GATT PTS version: 6.1 Tested: 24-April-2015 Android version: 5.1 Results: PASS test passed FAIL test failed INC test is inconclusive N/A test is disabled due to PICS setup ------------------------------------------------------------------------------- Test Name Result Notes ------------------------------------------------------------------------------- TC_GAC_CL_BV_01_C PASS haltest: gattc scan gattc search_service gattc get_characteristic gattc write_characteristic: type 3 TC_GAC_SR_BV_01_C PASS PTS issue #13073 TSE #6271 haltest: gatts add_service gatts add_chaaracteristic: 10 17 gatts start_service gatts send_response: value greater than MTU repeat with correct offset gatts send_response: value greater than MTU repeat with correct offset TC_GAD_CL_BV_01_C PASS haltest: NOTE: Repeat following steps if asked gattc connect gattc search_service gattc disconnect TC_GAD_CL_BV_02_C PASS haltest: NOTE: Repeat following steps if asked gattc connect gattc search_service gattc disconnect TC_GAD_CL_BV_03_C PASS haltest: NOTE: Repeat following steps if asked gattc connect gattc test_command 0xe0 0x2802 0x08 0x0001 0xffff NOTE: Keep on mind MTU size (some att rsp could not fit) gattc_disconnect TC_GAD_CL_BV_04_C PASS haltest: NOTE: Repeat following steps if asked gattc connect gattc search_service gattc get_characteristic TC_GAD_CL_BV_05_C PASS haltest: NOTE: Repeat following steps if asked gattc connect gattc test_command 0xe0 0x2803 0x08 gattc disconnect TC_GAD_CL_BV_06_C PASS haltest: NOTE: Repeat following steps if asked gattc connect gattc search_service gattc get_characteristic gattc get_descriptor gattc disconnect TC_GAD_CL_BV_07_C PASS haltest: NOTE: Repeat following step if asked bluetooth get_remote_services TC_GAD_CL_BV_08_C PASS haltest: NOTE: Repear following step if asked bluetooth get_remote_services TC_GAD_SR_BV_01_C PASS haltest: gattc register_client gattc listen TC_GAD_SR_BV_02_C PASS haltest: gattc register_client gattc listen TC_GAD_SR_BV_03_C PASS haltest: gattc register_client gattc listen gatts register_server gatts add_service gatts start_service gatts add_service gatts add_included_service gatts start_service TC_GAD_SR_BV_04_C PASS haltest: gattc register_client gattc listen TC_GAD_SR_BV_05_C PASS haltest: gattc register_client gattc listen TC_GAD_SR_BV_06_C PASS haltest: gattc register_client gattc listen TC_GAD_SR_BV_07_C PASS haltest: when requested: bluetooth get_remote_services NOTE: check if found requested service TC_GAD_SR_BV_08_C PASS haltest: when requested: bluetooth get_remote_services NOTE: check if found requested service TC_GAR_CL_BV_01_C PASS haltest: gattc connect gattc search_service gattc get_characteristic gattc read_characteristic gattc disconnect TC_GAR_CL_BI_01_C PASS haltest: gattc connect gattc test_command 0xe0 0x0000 0x0a gattc disconnect TC_GAR_CL_BI_02_C PASS haltest: gattc connect gattc search_service gattc get_characteristic gattc read_characteristic gattc disconnect TC_GAR_CL_BI_03_C PASS haltest: gattc connect gattc test_command 0xe0 0x0000 0x0a gattc disconnect TC_GAR_CL_BI_04_C PASS haltest: gattc connect gattc test_command 0xe0 0x0000 0x0a gattc disconnect TC_GAR_CL_BI_05_C PASS haltest: gattc connect gattc search_service gattc get_characteristic gattc read_characteristic gattc disconnect TC_GAR_CL_BV_03_C PASS haltest: gattc connect gattc test_command 0xe0 0x08 0x0001 0xffff gattc disconnect TC_GAR_CL_BI_06_C PASS haltest: gattc connect gattc test_command 0xe0 0x08 gattc disconnect TC_GAR_CL_BI_07_C PASS haltest: gattc connect gattc test_command 0xe0 0x08 gattc disconnect TC_GAR_CL_BI_09_C PASS haltest: gattc connect gattc test_command 0xe0 0x08 gattc disconnect TC_GAR_CL_BI_10_C PASS haltest: gattc connect gattc test_command 0xe0 0x08 gattc disconnect TC_GAR_CL_BI_11_C PASS haltest: gattc connect gattc test_command 0xe0 0x08 gattc disconnect TC_GAR_CL_BV_04_C PASS haltest: gattc connect gattc search_service NOTE: Repeat following steps if asked gattc get_characteristic gattc read_characteristic NOTE: After reading all characteristics gattc disconnect TC_GAR_CL_BI_12_C PASS haltest: gattc connect gattc search_service gattc get_characteristic gattc read_characteristic gattc disconnect TC_GAR_CL_BI_13_C PASS haltest: gattc connect gattc test_command 0xe0 0x0000 0x0c gattc disconnect TC_GAR_CL_BI_14_C PASS haltest: gattc connect gattc test_command 0xe0 0x0000 0x0a gattc disconnect TC_GAR_CL_BI_15_C PASS haltest: gattc connect gattc search_service gattc get_characteristic gattc read_characteristic gattc disconnect TC_GAR_CL_BI_16_C PASS haltest: gattc connect gattc search_service gattc get_characteristic gattc read_characteristic gattc disconnect TC_GAR_CL_BI_17_C PASS haltest: gattc connect gattc search_service gattc get_characteristic gattc read_characteristic gattc disconnect TC_GAR_CL_BV_05_C N/A TC_GAR_CL_BI_18_C N/A TC_GAR_CL_BI_19_C N/A TC_GAR_CL_BI_20_C N/A TC_GAR_CL_BI_21_C N/A TC_GAR_CL_BI_22_C N/A TC_GAR_CL_BV_06_C PASS haltest: gattc connect gattc search_service gattc get_characteristic gattc get_descriptor gattc read_descriptor gattc disconnect TC_GAR_CL_BI_23_C PASS haltest: gattc connect gattc search_service gattc get_characteristic gattc get_descriptor gattc read_descriptor gattc disconnect TC_GAR_CL_BI_24_C PASS haltest: gattc connect gattc test_command 0xe0 0x0000 0x0a gattc disconnect TC_GAR_CL_BI_25_C PASS haltest: gattc connect gattc search_service gattc get_characteristic gattc get_descriptor gattc read_descriptor gattc disconnect TC_GAR_CL_BI_26_C PASS haltest: gattc connect gattc search_service gattc get_characteristic gattc get_descriptor gattc read_descriptor gattc disconnect TC_GAR_CL_BI_27_C PASS haltest: gattc connect gattc search_service gattc get_characteristic gattc get_descriptor gattc read_descriptor gattc disconnect TC_GAR_CL_BV_07_C PASS haltest: gattc connect gattc search_service NOTE: Repeat following step if asked gattc get_characteristic gattc get_descriptor gattc read_descriptor NOTE: After reading all characteristics gattc disconnect TC_GAR_CL_BI_28_C PASS haltest: gattc connect gattc search_service gattc get_characteristic gattc get_descriptor gattc read_descriptor gattc disconnect TC_GAR_CL_BI_29_C PASS haltest: gattc connect gattc test_command 0xe0 0x0000 0x0c gattc disconnect TC_GAR_CL_BI_30_C PASS haltest: gattc connect gattc test_command 0xe0 0x0000 0x0a gattc disconnect TC_GAR_CL_BI_31_C PASS haltest: gattc connect gattc search_service gattc get_characteristic gattc get_descriptor gattc read_descriptor gattc disconnect TC_GAR_CL_BI_32_C PASS haltest: gattc connect gattc search_service gattc get_characteristic gattc get_descriptor gattc read_descriptor gattc disconnect TC_GAR_CL_BI_33_C PASS haltest: gattc connect gattc search_service gattc get_characteristic gattc get_descriptor gattc read_descriptor gattc disconnect TC_GAR_CL_BI_34_C PASS haltest: gattc connect gattc test_command 224 0 0x0a gattc disconnect TC_GAR_CL_BI_35_C PASS haltest: gattc connect gattc search_service gattc get_characteristic gattc read_characteristic gattc disconnect TC_GAR_SR_BV_01_C PASS TC_GAR_SR_BI_01_C PASS TC_GAR_SR_BI_02_C PASS TC_GAR_SR_BI_03_C PASS haltest: gatts add_service gatts add_characteristic: 2 1 gatts start_service gatts send_response: 8 TC_GAR_SR_BI_04_C PASS haltest: gatts add_service gatts add_chaaracteristic: 2 3 gatts start_service gatts send_response TC_GAR_SR_BI_05_C PASS haltest: gatts add_service gatts add_characteristic: 2 1 gatts start_service gatts send_response: 12 TC_GAR_SR_BV_03_C PASS TC_GAR_SR_BI_06_C PASS haltest: gatts add_service gatts add_chaaracteristic: 2 16 gatts start_service TC_GAR_SR_BI_07_C PASS TC_GAR_SR_BI_08_C PASS TC_GAR_SR_BI_09_C PASS haltest: gatts add_service gatts add_chaaracteristic: 2 1 gatts start_service gatts send_response: 8 TC_GAR_SR_BI_10_C PASS haltest: gatts add_service gatts add_chaaracteristic: 2 1 gatts start_service gatts send_response: 5 TC_GAR_SR_BI_11_C PASS haltest: gatts add_service gatts add_chaaracteristic: 2 1 gatts start_service gatts send_response: 12 TC_GAR_SR_BV_04_C PASS haltest: gatts add_service gatts add_chaaracteristic: 2 1 gatts start_service gatts send_response: value greater than MTU repeat with correct offset TC_GAR_SR_BI_12_C PASS haltest: gatts add_service gatts add_chaaracteristic: 8 16 gatts start_service gatts send_response TC_GAR_SR_BI_13_C PASS haltest: gatts add_service gatts add_chaaracteristic: 2 1 gatts start_service gatts send_response: value greater than MTU repeat with correct offset gatts send_response: 7 TC_GAR_SR_BI_14_C PASS haltest: gatts add_service gatts add_characteristic: 2 1 gatts start_service gatts send_response: 1 TC_GAR_SR_BI_15_C PASS haltest: gatts add_service gatts add_characteristic: 2 1 gatts start_service gatts send_response: 8 TC_GAR_SR_BI_16_C PASS haltest: gatts add_service gatts add_characteristic: 2 1 gatts start_service gatts send_response: 5 TC_GAR_SR_BI_17_C PASS haltest: gatts add_service gatts add_characteristic: 2 1 gatts start_service gatts send_response: 12 TC_GAR_SR_BV_05_C N/A TC_GAR_SR_BI_18_C N/A TC_GAR_SR_BI_19_C N/A TC_GAR_SR_BI_20_C N/A TC_GAR_SR_BI_21_C N/A TC_GAR_SR_BI_22_C N/A TC_GAR_SR_BV_06_C PASS haltest: gatts add_service gatts add_characteristic: 2 1 gatts add_descriptor gatts start_service gatts send_response TC_GAR_SR_BI_23_C PASS haltest: gatts add_service gatts add_chaaracteristic: 2 1 gatts add_descriptor: 16 gatts start_service TC_GAR_SR_BI_24_C PASS haltest: gatts add_service gatts add_characteristic: 2 1 gatts add_descriptor gatts start_service gatts send_response: 1 TC_GAR_SR_BI_25_C PASS haltest: gatts add_service gatts add_chaaracteristic: 2 1 gatts add_descriptor: 1 gatts start_service gatts send_response: 8 TC_GAR_SR_BI_26_C PASS haltest: gatts add_service gatts add_chaaracteristic: 2 1 gatts add_descriptor: 1 gatts start_service gatts send_response: 5 TC_GAR_SR_BI_27_C PASS haltest: gatts add_service gatts add_chaaracteristic: 2 1 gatts add_descriptor: 1 gatts start_service gatts send_response: 12 TC_GAR_SR_BV_07_C PASS haltest: gatts add_service gatts add_chaaracteristic: 2 1 gatts add_descriptor: 1 gatts start_service gatts send_response: value greater than MTU repeat with correct offset TC_GAR_SR_BV_08_C PASS haltest: gatts add_service gatts add_chaaracteristic: 2 1 gatts add_descriptor: 1 gatts start_service gatts send_response: value greater than MTU repeat with correct offset TC_GAR_SR_BI_28_C PASS haltest: gatts add_service gatts add_chaaracteristic: 2 1 gatts add_descriptor: 16 gatts start_service TC_GAR_SR_BI_29_C PASS haltest: gatts add_service gatts add_chaaracteristic: 2 1 gatts add_descriptor: 1 gatts start_service gatts send_response: value greater than MTU repeat with correct offset gatts send_response: 7 TC_GAR_SR_BI_30_C PASS haltest: gatts add_service gatts add_chaaracteristic: 2 1 gatts add_descriptor: 1 gatts start_service gatts send_response: 1 TC_GAR_SR_BI_31_C PASS haltest: gatts add_service gatts add_chaaracteristic: 2 1 gatts add_descriptor: 1 gatts start_service gatts send_response: 8 TC_GAR_SR_BI_32_C PASS haltest: gatts add_service gatts add_chaaracteristic: 2 1 gatts add_descriptor: 1 gatts start_service gatts send_response: 5 TC_GAR_SR_BI_33_C PASS haltest: gatts add_service gatts add_chaaracteristic: 2 1 gatts add_descriptor: 1 gatts start_service gatts send_response: 12 TC_GAR_SR_BI_34_C PASS haltest: gatts add_service gatts add_characteristic gatts start_service gatts send_response 0x80-0x9F TC_GAR_SR_BI_35_C PASS haltest: gatts add_service gatts add_characteristic gatts start_service gatts send_response 0x80-0x9F TC_GAW_CL_BV_01_C PASS haltest: gattc connect gattc search_service gattc get_characteristic gattc write_characteristic 1 gattc disconnect TC_GAW_CL_BV_02_C PASS haltest: gattc connect gattc search_service gattc get_characteristic gattc write_characteristic 4 gattc disconnect TC_GAW_CL_BV_03_C PASS haltest: gattc connect gattc search_service gattc get_characteristic gattc write_characteristic 2 gattc disconnect TC_GAW_CL_BI_02_C PASS haltest: gattc connect gattc test_command 0xe1 0x0000 0x12 gattc disconnect TC_GAW_CL_BI_03_C PASS haltest: gattc connect gattc search_service gattc get_characteristic gattc write_characteristic 2 gattc disconnect TC_GAW_CL_BI_04_C PASS haltest: gattc connect gattc search_service gattc get_characteristic gattc write_characteristic 2 gattc disconnect TC_GAW_CL_BI_05_C PASS haltest: gattc connect gattc search_service gattc get_characteristic gattc write_characteristic 2 gattc disconnect TC_GAW_CL_BI_06_C PASS haltest: gattc connect gattc search_service gattc get_characteristic gattc write_characteristic 2 gattc disconnect TC_GAW_CL_BV_05_C PASS haltest: gattc connect gattc search_service gattc get_characteristic gattc write_characteristic 3 gattc execute_write 1 gattc disconnect TC_GAW_CL_BI_07_C PASS haltest: gattc connect gattc test_command 0xe1 0x0000 0x12 gattc disconnect TC_GAW_CL_BI_08_C PASS haltest: gattc connect gattc search_service gattc get_characteristic gattc write_characteristic 3 gattc execute_write 1 gattc disconnect TC_GAW_CL_BI_09_C PASS haltest: gattc connect gattc test_command 0xe1 0x0000 0x16 gattc test_command 0xe1 0x0000 0x18 1 gattc disconnect TC_GAW_CL_BI_11_C PASS haltest: gattc connect gattc search_service gattc get_characteristic gattc write_characteristic 3 gattc disconnect TC_GAW_CL_BI_12_C PASS haltest: gattc connect gattc search_service gattc get_characteristic gattc write_characteristic 3 gattc disconnect TC_GAW_CL_BI_13_C PASS haltest: gattc connect gattc search_service gattc get_characteristic gattc write_characteristic 3 gattc disconnect TC_GAW_CL_BV_06_C PASS haltest: gattc connect gattc search_service gattc get_characteristic gattc write_characteristic 3 gattc execute_write 1 gattc disconnect TC_GAW_CL_BI_14_C PASS haltest: gattc connect gattc test_command 0xe1 0x0000 0x16 gattc disconnect TC_GAW_CL_BI_15_C PASS haltest: gattc connect gattc search_service gattc get_characteristic gattc write_characteristic 3 gattc disconnect TC_GAW_CL_BI_17_C PASS haltest: gattc connect gattc search_service gattc get_characteristic gattc write_characteristic 3 gattc disconnect TC_GAW_CL_BI_18_C PASS haltest: gattc connect gattc search_service gattc get_characteristic gattc write_characteristic 3 gattc disconnect TC_GAW_CL_BI_19_C PASS haltest: gattc connect gattc search_service gattc get_characteristic gattc write_characteristic 3 gattc disconnect TC_GAW_CL_BV_08_C PASS haltest: gattc connect gattc search_service gattc get_characteristic gattc get_descriptor gattc write_descriptor 2 gattc disconnect TC_GAW_CL_BI_20_C PASS haltest: gattc connect gattc test_command 0xe1 0x0000 0x12 gattc disconnect TC_GAW_CL_BI_21_C PASS haltest: gattc connect gattc search_service gattc get_characteristic gattc get_descriptor gattc write_descriptor 2 gattc disconnect TC_GAW_CL_BI_22_C PASS haltest: gattc connect gattc search_service gattc get_characteristic gattc get_descriptor gattc write_descriptor 2 gattc disconnect TC_GAW_CL_BI_23_C PASS haltest: gattc connect gattc search_service gattc get_characteristic gattc get_descriptor gattc write_descriptor 2 gattc disconnect TC_GAW_CL_BI_24_C PASS haltest: gattc connect gattc search_service gattc get_characteristic gattc get_descriptor gattc write_descriptor 2 gattc disconnect TC_GAW_CL_BV_09_C PASS haltest: gattc connect gattc search_service gattc get_characteristic gattc get_descriptor gattc write_descriptor 3 gattc execute_write 1 gattc disconnect TC_GAW_CL_BI_25_C PASS haltest: gattc connect gattc test_command 0xe1 0x0000 0x16 gattc disconnect TC_GAW_CL_BI_26_C PASS haltest: gattc connect gattc search_service gattc get_characteristic gattc get_descriptor gattc write_descriptor 3 gattc disconnect TC_GAW_CL_BI_27_C PASS haltest: gattc connect gattc test_command 0xe1 0x0000 0x16 gattc test_command 0xe1 0x0000 0x18 1 gattc disconnect TC_GAW_CL_BI_29_C PASS haltest: gattc connect gattc search_service gattc get_characteristic gattc get_descriptor gattc write_descriptor 3 gattc disconnect TC_GAW_CL_BI_30_C PASS haltest: gattc connect gattc search_service gattc get_characteristic gattc get_descriptor gattc write_descriptor 3 gattc disconnect TC_GAW_CL_BI_31_C PASS haltest: gattc connect gattc test_command 0xe1 0x0000 0x16 0x0000 gattc disconnect TC_GAW_CL_BI_32_C PASS haltest: gattc connect gattc test_command 0xe1 0x0000 0x16 gattc test_command 0xe1 0x0000 0x18 0 gattc disconnect TC_GAW_CL_BI_33_C PASS haltest: gattc connect gattc search_service gattc get_characteristic gattc write_characteristic 2 gattc disconnect TC_GAW_CL_BI_34_C PASS haltest: gattc connect gattc search_service gattc get_characteristic gattc write_characteristic 2 gattc disconnect TC_GAW_CL_BI_35_C PASS haltest: gattc connect gattc search_service gattc get_characteristic gattc get_descriptor gattc write_descriptor 2 gattc disconnect TC_GAW_CL_BI_36_C PASS haltest: gattc connect gattc search_service gattc get_characteristic gattc get_descriptor gattc write_descriptor 2 gattc disconnect TC_GAW_SR_BV_01_C PASS haltest: gatts add_service gatts add_characteristic: 4 17 gatts start_service TC_GAW_SR_BV_02_C PASS haltest: gatts add service gatts add_characteristics: 66 145 gatts start_service gattc listen gatts send_response: (twice) NOTE: gatts_request_write_cb shall be called (verify it) TC_GAW_SR_BI_01_C PASS haltest: gatts add_service gatts add_characteristic: 68 129 gatts start_service gatts send_response: repeat with 1 TC_GAW_SR_BV_03_C PASS haltest: gatts add_service gatts add_characteristic: 10 17 TC_GAW_SR_BI_02_C PASS haltest: gatts add_service gatts add_characteristic: 10 17 gatts start_service gatts send_response: 1 TC_GAW_SR_BI_03_C PASS haltest: gatts add_service gatts add_characteristic: 10 1 gatts start_service TC_GAW_SR_BI_04_C PASS haltest: gatts add_service gatts add_characteristic: 10 17 gatts start_service gatts send_response: 8 TC_GAW_SR_BI_05_C PASS haltest: gatts add_service gatts add_characteristic: 10 17 gatts start_service gatts send_response: 5 TC_GAW_SR_BI_06_C PASS haltest: gatts add_service gatts add_characteristic: 10 17 gatts start_service gatts send_response: 12 TC_GAW_SR_BV_05_C PASS haltest: gatts add_service gatts add_characteristic: 10 17 gatts start_service gatts send_response: value greater than MTU repeat with correct offset gatts send_response: repeat with correct value TC_GAW_SR_BI_07_C PASS haltest: gatts add_service gatts add_characteristic: 10 17 gatts start_service gatts send_response TC_GAW_SR_BI_08_C PASS haltest: gatts add_service gatts add_characteristic: 2 1 gatts start_service TC_GAW_SR_BI_09_C PASS haltest: gatts add_service gatts add_characteristic: 10 17 gatts start_service gatts send_response: value greater than MTU repeat with correct offset gatts send_response: 7 TC_GAW_SR_BI_11_C PASS haltest: gatts add_service gatts add_characteristic: 10 17 gatts start_service gatts send_response: value greater than MTU repeat with correct offset gatts send_response: 8 TC_GAW_SR_BI_12_C PASS haltest: gatts add_service gatts add_characteristic: 10 17 gatts start_service gatts send_response: value greater than MTU repeat with correct offset gatts send_response: 5 TC_GAW_SR_BI_13_C PASS haltest: gatts add_service gatts add_characteristic: 10 17 gatts start_service gatts send_response: value greater than MTU repeat with correct offset gatts send_response: 12 TC_GAW_SR_BV_06_C PASS haltest: gatts add_service gatts add_characteristic: 10 17 gatts start_service gatts send_response: repeat with correct value TC_GAW_SR_BV_10_C PASS haltest: gatts add_service gatts add_characteristic: 10 17 gatts start_service gatts send_response: value greater than MTU repeat with correct offset gatts send_response: repeat with correct value TC_GAW_SR_BI_14_C PASS haltest: gatts add_service gatts add_characteristic: 10 17 gatts start_service gatts send_response: 1 TC_GAW_SR_BI_15_C PASS haltest: gatts add_service gatts add_characteristic: 10 17 gatts start_service gatts send_response: 3 TC_GAW_SR_BI_17_C PASS haltest: gatts add_service gatts add_characteristic: 10 17 gatts start_service gatts send_response: 8 TC_GAW_SR_BI_18_C PASS haltest: gatts add_service gatts add_characteristic: 10 17 gatts start_service gatts send_response: 5 TC_GAW_SR_BI_19_C PASS haltest: gatts add_service gatts add_characteristic: 10 17 gatts start_service gatts send_response: 12 TC_GAW_SR_BV_07_C PASS haltest: gatts add_service gatts add_characteristic: 10 17 gatts start_service gatts send_response: repeat with correct value TC_GAW_CL_BV_08_C PASS haltest: gatts add_service gatts add_characteristic: 10 17 gatts add_descriptor: 17 gatts start_service gatts send_response TC_GAW_SR_BI_20_C PASS haltest: gatts add_service gatts add_characteristic: 10 17 gatts add_descriptor: 17 gatts start_service gatts send_response: 1 TC_GAW_SR_BI_21_C PASS haltest: gatts add_service gatts add_characteristic: 2 1 gatts add_descriptor: 1 gatts start_service TC_GAW_SR_BI_22_C PASS haltest: gatts add_service gatts add_characteristic: 10 17 gatts add_descriptor: 17 gatts start_service gatts send_response: 8 TC_GAW_SR_BI_23_C PASS haltest: gatts add_service gatts add_characteristic: 10 17 gatts add_descriptor: 17 gatts start_service gatts send_response: 5 TC_GAW_SR_BI_24_C PASS haltest: gatts add_service gatts add_characteristic: 10 17 gatts add_descriptor: 17 gatts start_service gatts send_response: 12 TC_GAW_SR_BV_09_C PASS haltest: gatts add_service gatts add_characteristic: 10 17 gatts add_descriptor: 17 gatts start_service gatts send_response: value greater than MTU repeat with correct offset gatts send_response: repeat with correct value TC_GAW_SR_BI_25_C PASS haltest: gatts add_service gatts add_characteristic: 10 17 gatts add_descriptor: 17 gatts start_service gatts send_response: 1 TC_GAW_SR_BI_26_C PASS haltest: gatts add_service gatts add_characteristic: 10 17 gatts add_descriptor: 1 gatts start_service TC_GAW_SR_BI_27_C PASS haltest: gatts add_service gatts add_characteristic: 10 17 gatts add_descriptor: 1 gatts start_service gatts send_response: value greater than MTU repeat with correct offset gatts send_response: 7 TC_GAW_SR_BI_29_C PASS haltest: gatts add_service gatts add_characteristic: 10 17 gatts add_descriptor: 17 gatts start_service gatts send_response: 8 TC_GAW_SR_BI_30_C PASS haltest: gatts add_service gatts add_characteristic: 10 17 gatts add_descriptor: 17 gatts start_service gatts send_response: 5 TC_GAW_SR_BI_31_C PASS haltest: gatts add_service gatts add_characteristic: 10 17 gatts add_descriptor: 17 gatts start_service gatts send_response: 12 TC_GAW_SR_BI_32_C PASS PTS issue #12823 haltest: gatts add_service gatts add_characteristic: 10 17 gatts start_service gatts send_response gatts send_response: 13 TC_GAW_SR_BI_33_C PASS haltest: gatts add_service gatts add_characteristic: 10 17 gatts start_service gatts send_response: value greater than MTU repeat with correct offset gatts send_response: 13 TC_GAW_SR_BI_34_C PASS haltest: gatts add_service gatts add_characteristic: 10 17 gatts add_descriptor: 17 gatts start_service gatts send_response gatts send_response: 13 TC_GAW_SR_BI_35_C PASS haltest: gatts add_service gatts add_characteristic: 10 17 gatts add_descriptor: 17 gatts start_service gatts send_response: value greater than MTU repeat with correct offset gatts send_response: 13 TC_GAN_CL_BV_01_C PASS haltest: gattc connect gattc search_service gattc get_characteristic gattc get_descriptor gattc write_descriptor 2 0x0100 gattc disconnect TC_GAN_SR_BV_01_C PASS haltest: gatts add_service gatts add_chaaracteristic: 26 17 gatts add_descriptor: 2902 11 gatts start_service gatts send_response gatts send_response gatts send_indication: char value handle 0 TC_GAI_CL_BV_01_C PASS haltest: gattc connect gattc search_service gattc get_characteristic gattc get_descriptor gattc write_descriptor 2 0x0200 gattc disconnect TC_GAI_SR_BV_01_C PASS haltest: gatts add_service gatts add_chaaracteristic: 42 17 gatts add_descriptor: 17 gatts start_service gatts add_service gatts start_service TC_GAS_CL_BV_01_C PASS haltest: gattc connect gattc disconnect TC_GAS_SR_BV_01_C PASS haltest: gatts add_service gatts add_chaaracteristic: 42 17 gatts add_descriptor: 17 gatts start_service gatts add_service gatts start_service TC_GAT_CL_BV_01_C PASS haltest: gattc connect gattc search_service gattc get_characteristic gattc read_characteristic wait for 30 sec timeout TC_GAT_CL_BV_02_C PASS haltest: gattc connect gattc search_service gattc get_characteristic gattc write_characteristic 2 wait for 30 sec timeout TC_GAT_SR_BV_01_C PASS haltest: gatts add_service gatts add_characteristic: 42 17 gatts add_descriptor: 17 gatts start_service gatts add_service gatts start_service TC_GPA_CL_BV_01_C PASS haltest: gattc connect gattc test_command 0xe0 0x08 gattc disconnect TC_GPA_CL_BV_02_C PASS haltest: gattc connect gattc test_command 0xe0 0x08 gattc disconnect TC_GPA_CL_BV_03_C PASS haltest: gattc connect gattc test_command 0xe0 0x08 gattc disconnect TC_GPA_CL_BV_04_C PASS haltest: gattc connect gattc test_command 0xe0 0x08 gattc disconnect TC_GPA_CL_BV_05_C PASS haltest: gattc connect gattc test_command 0xe0 0x08 gattc disconnect TC_GPA_CL_BV_06_C PASS haltest: gattc connect gattc test_command 0xe0 0x08 gattc connect gattc search_service gattc get_characteristic gattc read_descriptor gattc disconnect TC_GPA_CL_BV_07_C PASS haltest: gattc connect gattc test_command 0xe0 0x08 gattc disconnect TC_GPA_CL_BV_08_C PASS haltest: gattc connect gattc test_command 0xe0 0x08 gattc disconnect TC_GPA_CL_BV_11_C PASS haltest: gattc connect Repeat following steps 5 times: 1.Find Characteristic Aggregate Format gattc test_command 224 [u1] 8 2.Read aggregate descriptor gattc test_command 224 [u1] 10 3.Read 3 handles from aggregate descriptor value gattc test_command 224 [u1] 10 4.Compare descriptors values gattc disconnect TC_GPA_CL_BV_12_C PASS haltest: gattc connect Repeat following steps 5 times: 1.Find Characteristic Presentation Format gattc test_command 224 [u1] 8 2.Find characteristic in this range gattc test_command 224 2803 [u1] 8 3.Read characteristic declaration gattc test_command 224 [u1] 10 4.Read characteristic value gattc test_command 224 [u1] 10 5.Compare characteristic value and presentation format gattc disconnect TC_GPA_SR_BV_01_C PASS TC_GPA_SR_BV_02_C PASS haltest: gatts add_service gatts start_service TC_GPA_SR_BV_03_C PASS haltest: gatts add_service gatts add_service add_included_service gatts start_service gatts start_service TC_GPA_SR_BV_04_C PASS haltest: gatts add_service gatts add_chaaracteristic: 10 17 gatts start_service TC_GPA_SR_BV_05_C PASS haltest: gatts add_service gatts add_chaaracteristic: 138 17 gatts add_descriptor 2900 gatts start_service TC_GPA_SR_BV_06_C PASS haltest: gatts add_service gatts add_chaaracteristic: 138 17 gatts add_descriptor 2901 gatts start_service TC_GPA_SR_BV_07_C PASS TC_GPA_SR_BV_08_C PASS haltest: gatts add_service gatts add_chaaracteristic: 138 17 gatts add_descriptor 2903 gatts start_service gatts send_response TC_GPA_SR_BV_11_C INC PTS issue #13392 haltest: gatts add_service gatts add_chaaracteristic: 138 17 gatts add_descriptor 2905 gatts start_service gatts send_response: repeat with correct offset and data TC_GPA_SR_BV_12_C PASS haltest: gatts add_service gatts add_chaaracteristic: 10 17 gatts add_descriptor 2904 gatts start_service gatts send_response: repeat with correct data ------------------------------------------------------------------------------- bluez-5.82/android/PaxHeaders/pts-avctp.txt0000644000000000000000000000005012537515745015766 xustar0020 atime=1743516876 20 ctime=1743591289 bluez-5.82/android/pts-avctp.txt0000644000000000000000000000241512537515745015451 0ustar00rootrootPTS test results for AVCTP PTS version: 6.1 Tested: 21-May-2015 Android version: 5.1 Results: PASS test passed FAIL test failed INC test is inconclusive N/A test is disabled due to PICS setup Controller (CT) ------------------------------------------------------------------------------- Test Name Result Notes ------------------------------------------------------------------------------- TC_CT_CCM_BV_01_C N/A TC_CT_CCM_BV_02_C N/A TC_CT_CCM_BV_03_C N/A TC_CT_CCM_BV_04_C N/A TC_CT_CCM_BI_01_C N/A TC_CT_NFR_BV_01_C N/A TC_CT_NFR_BV_04_C PASS haltest: rc set_volume 5 Note: IUT must be connectable and discoverable TC_CT_FRA_BV_01_C N/A TC_CT_FRA_BV_04_C N/A ------------------------------------------------------------------------------- Target (TG) ------------------------------------------------------------------------------- Test Name Result Notes ------------------------------------------------------------------------------- TC_TG_CCM_BV_01_C PASS TC_TG_CCM_BV_02_C PASS TC_TG_CCM_BV_03_C PASS TC_TG_CCM_BV_04_C PASS TC_TG_NFR_BV_02_C PASS TC_TG_NFR_BV_03_C PASS TC_TG_NFR_BI_01_C PASS TC_TG_FRA_BV_02_C N/A Fragmentation not supported TC_TG_FRA_BV_03_C N/A Fragmentation not supported ------------------------------------------------------------------------------- bluez-5.82/android/PaxHeaders/pics-pbap.txt0000644000000000000000000000005012537515745015723 xustar0020 atime=1743516876 20 ctime=1743591288 bluez-5.82/android/pics-pbap.txt0000644000000000000000000005504612537515745015416 0ustar00rootrootPBAP PICS for the PTS tool. PTS version: 6.1 * - different than PTS defaults # - not yet implemented/supported M - mandatory O - optional Roles ------------------------------------------------------------------------------- Parameter Name Selected Description ------------------------------------------------------------------------------- TSPC_PBAP_1_1 False Role: PCE (C.1) TSPC_PBAP_1_2 True (*) Role: PSE (C.1) ------------------------------------------------------------------------------- C1: It is mandatory to support at least one of the defined roles. ------------------------------------------------------------------------------- Client Major Profile Version (X.Y) ------------------------------------------------------------------------------- Parameter Name Selected Description ------------------------------------------------------------------------------- TSPC_PBAP_2a_1 False PBAP 1.0 (C.1) TSPC_PBAP_2a_2 False PBAP 1.1 (C.1) TSPC_PBAP_2a_3 False PBAP 1.2 (C.1) ------------------------------------------------------------------------------- C.1: Mandatory to support one and only one major profile version. ------------------------------------------------------------------------------- Client Minor Profile Version (X.Y) ------------------------------------------------------------------------------- Parameter Name Selected Description ------------------------------------------------------------------------------- TSPC_PBAP_2b_1 False PBAP 1.1.1 (C.1) ------------------------------------------------------------------------------- C.1: Optional if 2a/2 (PBAP 1.1) is supported, otherwise Excluded. ------------------------------------------------------------------------------- Supported features (PCE) ------------------------------------------------------------------------------- Parameter Name Selected Description ------------------------------------------------------------------------------- TSPC_PBAP_2_1 False (*) PCE: Phone Book Download (C.1) TSPC_PBAP_2_2 False (*) PCE: Phone Book Browsing (C.1) TSPC_PBAP_2_3 False (*) PCE: Session Management (M) TSPC_PBAP_2_4 False PCE: Able to Request Size of the Phonebook (O) TSPC_PBAP_2_5 False PCE: Database Identifier (C.2) TSPC_PBAP_2_6 False PCE: Folder Version Counters (C.2) TSPC_PBAP_2_7 False PCE: vCard Selecting (C.2) TSPC_PBAP_2_7a False PCE: Able to send vCardSelector (C.2) TSPC_PBAP_2_7b False PCE: Able to send vCardSelectorOperator (C.2) TSPC_PBAP_2_8 False (*) PCE: Enhanced Missed Calls (C.4) TSPC_PBAP_2_8a False (*) PCE: Able to reset the missed Calls (C.2) TSPC_PBAP_2_9 False PCE: X-BT-UCI vCard Field (C.2) TSPC_PBAP_2_9a False PCE: Able to request X-BT-UCI Field (C.2) TSPC_PBAP_2_10 False PCE: X-BT-UID vCard Field (C.2) TSPC_PBAP_2_10a False PCE: Referencing Contacts (C.2) TSPC_PBAP_2_12 False PCE: Contact Image Default Format (C.2) TSPC_PBAP_2_12a False PCE: Able to request Contact Images (C.2) TSPC_PBAP_2_13 False PCE: Supported Phonebook Objects (C.3) TSPC_PBAP_2_13a False (*) PCE: Telecom/pb (C.3) TSPC_PBAP_2_13b False PCE: Telecom/ich (C.3) TSPC_PBAP_2_13c False PCE: Telecom/och (C.3) TSPC_PBAP_2_13d False (*) PCE: Telecom/mch (C.3) TSPC_PBAP_2_13e False (*) PCE: Telecom/cch (C.3) TSPC_PBAP_2_13f False PCE: Telecom/spd (C.3) TSPC_PBAP_2_13g False PCE: Telecom/fav (C.3) TSPC_PBAP_2_13h False PCE: SIM1/Telecom/pb (C.3) TSPC_PBAP_2_13i False PCE: SIM1/Telecom/ich (C.3) TSPC_PBAP_2_13j False PCE: SIM1/Telecom/och (C.3) TSPC_PBAP_2_13k False PCE: SIM1/Telecom/mch (C.3) TSPC_PBAP_2_13l False PCE: SIM1/Telecom/cch (C.3) ------------------------------------------------------------------------------- C.1: It is mandatory to support at least one of the defined features. C.2: Optional if TSPC_PBAP_0_3 (PBAP 1.2) is supported, otherwise Excluded. C.3: Mandatory to support at least one of the listed phonebook objects . C.4: Optional if TSPC_PBAP_0_3 (PBAP 1.2) and any of the mch or cch folders (13d,13e,13k,13l) are supported, otherwise Excluded. ------------------------------------------------------------------------------- Supported Phone Book Download functions (PCE) ------------------------------------------------------------------------------- Parameter Name Selected Description ------------------------------------------------------------------------------- TSPC_PBAP_3_1 False (*) PCE: Pull Phone Book (C.1) ------------------------------------------------------------------------------- C1: Mandatory for PCE if Phone Book Download (TSPC_PBAP_2_1) is supported, otherwise excluded. ------------------------------------------------------------------------------- Supported Phone Book Browsing functions (PCE) ------------------------------------------------------------------------------- Parameter Name Selected Description ------------------------------------------------------------------------------- TSPC_PBAP_4_1 False (*) PCE: Set Phone Book (C.1) TSPC_PBAP_4_2 False (*) PCE: Pull vCard Listing (C.1) TSPC_PBAP_4_3 False (*) PCE: Pull vCard Entry (C.1) ------------------------------------------------------------------------------- C1: Mandatory for PCE if Phone Book Browsing TSPC_PBAP_2_2 is supported, otherwise excluded. ------------------------------------------------------------------------------- Used vCard formats (PCE) ------------------------------------------------------------------------------- Parameter Name Selected Description ------------------------------------------------------------------------------- TSPC_PBAP_5_1 False (*) PCE: vCard 2.1 (C.1) TSPC_PBAP_5_2 False (*) PCE: vCard 3.0 (C.1) ------------------------------------------------------------------------------- C1: It is mandatory to support at least one of the defined versions if PCE supported. ------------------------------------------------------------------------------- OBEX Functions for PCE ------------------------------------------------------------------------------- Parameter Name Selected Description ------------------------------------------------------------------------------- TSPC_PBAP_6_1 False (*) PCE: Connect (M) TSPC_PBAP_6_2 False (*) PCE: Disconnect (M) TSPC_PBAP_6_3 False (*) PCE: Get (M) TSPC_PBAP_6_4 False PCE: Abort (O) TSPC_PBAP_6_5 False (*) PCE: SetPath (C.1) TSPC_PBAP_6_6 False PCE: Support for OBEX authentication initiation (C.2) ------------------------------------------------------------------------------- C.1: Mandatory if TSPC_PBAP_2_2 (Phone Book Browsing) is supported, otherwise Excluded. C.2: Optional to support initiation if TSPC_PBAP_0_1 (PBAP 1.0) or TSPC_PBAP_0_2 (PBAP 1.1) is supported, otherwise Excluded. ------------------------------------------------------------------------------- PCE OBEX Header Support ------------------------------------------------------------------------------- Parameter Name Selected Description ------------------------------------------------------------------------------- TSPC_PBAP_7_1 False (*) PCE: Name (M) TSPC_PBAP_7_2 False (*) PCE: Type (M) TSPC_PBAP_7_3 False (*) PCE: Body (M) TSPC_PBAP_7_4 False (*) PCE: End of Body (M) TSPC_PBAP_7_5 False (*) PCE: Target (M) TSPC_PBAP_7_6 False (*) PCE: Who (M) TSPC_PBAP_7_7 False (*) PCE: Connection ID (M) TSPC_PBAP_7_8 False (*) PCE: Authentication Challenge (M) TSPC_PBAP_7_9 False (*) PCE: Authentication Response (M) TSPC_PBAP_7_10 False (*) PCE: Application Parameters (M) TSPC_PBAP_7_11 False PCE: Single Response Mode (C.1) TSPC_PBAP_7_12 False PCE: Single Response Mode Parameter (ability to parse) (C.1) TSPC_PBAP_7_13 False PCE: Single Response Mode Parameter (ability to send) (C.2) ------------------------------------------------------------------------------- C.1: Mandatory if TSPC_PBAP_0_3 (PBAP 1.2) is supported, otherwise Excluded. C.2: Optional if TSPC_PBAP_2a_3 (PBAP 1.2) is supported, otherwise Excluded. ------------------------------------------------------------------------------- OBEX Error Codes for PCE ------------------------------------------------------------------------------- Parameter Name Selected Description ------------------------------------------------------------------------------- TSPC_PBAP_8_1 False (*) PCE: Bad Request (M) TSPC_PBAP_8_2 False (*) PCE: Not Implemented (M) TSPC_PBAP_8_3 False (*) PCE: Unauthorized (M) TSPC_PBAP_8_4 False (*) PCE: Precondition Failed (M) TSPC_PBAP_8_5 False (*) PCE: Not Found (M) TSPC_PBAP_8_6 False (*) PCE: Not Acceptable (M) TSPC_PBAP_8_7 False (*) PCE: Service Unavailable (M) TSPC_PBAP_8_8 False (*) PCE: Forbidden (M) ------------------------------------------------------------------------------- Server Major Profile Version (X.Y) ------------------------------------------------------------------------------- Parameter Name Selected Description ------------------------------------------------------------------------------- TSPC_PBAP_9a_1 False PBAP 1.0 (C.1) TSPC_PBAP_9a_2 True (*) PBAP 1.1 (C.1) TSPC_PBAP_9a_3 False PBAP 1.2 (C.1) ------------------------------------------------------------------------------- C.1: Mandatory to support one and only one major profile version. ------------------------------------------------------------------------------- Server Minor Profile Version (X.Y.Z) ------------------------------------------------------------------------------- Parameter Name Selected Description ------------------------------------------------------------------------------- TSPC_PBAP_9b_1 False PBAP 1.1.1 (C.1) ------------------------------------------------------------------------------- C.1: Optional if 9a/2 (PBAP 1.1) is supported, otherwise Excluded. ------------------------------------------------------------------------------- Supported features (PSE) ------------------------------------------------------------------------------- Parameter Name Selected Description ------------------------------------------------------------------------------- TSPC_PBAP_9_1 True PSE: Phone Book Download (M) TSPC_PBAP_9_2 True PSE: Phone Book Browsing (M) TSPC_PBAP_9_3 True PSE: Session Management (M) TSPC_PBAP_9_4 True PSE: Able to request the size of the Phonebook (M) TSPC_PBAP_9_5 False PSE: Database Identifier (C.1) TSPC_PBAP_9_5a False PSE: Able to keep a persistent Database Identifier (C.2) TSPC_PBAP_9_5b False PSE: Able to regenerate a Database Identifier (C.2) TSPC_PBAP_9_6 False PSE: Folder Version Counters (C.1) TSPC_PBAP_9_6a False PSE: Able to Insert or Remove Entries (C.2) TSPC_PBAP_9_6b False PSE: Able to Modify contact primary Fields (C.2) TSPC_PBAP_9_6c False PSE: Able to Modify contact secondary Fields (C.2) TSPC_PBAP_9_7 False (*) PSE: vCard Selecting (C.1) TSPC_PBAP_9_8 False (*) PSE: Enhanced Missed Calls (C.4) TSPC_PBAP_9_9 False PSE: X-BT-UCI vCard Field (C.2) TSPC_PBAP_9_10 False PSE: X-BT-UID vCard Field (C.2) TSPC_PBAP_9_10a False PSE: Referencing Contacts (C.3) TSPC_PBAP_9_12 False PSE: Contact Image Default Format (C.1) TSPC_PBAP_9_12a False PSE: Able to request Contact Images (C.2) TSPC_PBAP_9_13 False PSE: Supported Phonebook Objects TSPC_PBAP_9_13a True PSE: Telecom/pb (M) TSPC_PBAP_9_13b True (*) PSE: Telecom/ich (O) TSPC_PBAP_9_13c True (*) PSE: Telecom/och (O) TSPC_PBAP_9_13d True (*) PSE: Telecom/mch (O) TSPC_PBAP_9_13e True PSE: Telecom/cch (O) TSPC_PBAP_9_13f False PSE: Telecom/spd (C.2) TSPC_PBAP_9_13g False PSE: Telecom/fav (C.2) TSPC_PBAP_9_13h False (*) PSE: SIM1/Telecom/pb (O) TSPC_PBAP_9_13i False PSE: SIM1/Telecom/ich (O) TSPC_PBAP_9_13j False PSE: SIM1/Telecom/och (O) TSPC_PBAP_9_13k False (*) PSE: SIM1/Telecom/mch (O) TSPC_PBAP_9_13l False PSE: SIM1/Telecom/cch (O) TSPC_PBAP_9_14 False PSE: Deleted Handles Behavior TSPC_PBAP_9_14a True PSE: Error reporting (C.5) TSPC_PBAP_9_14b False PSE: Change tracking (C.5) ------------------------------------------------------------------------------- C.1: Mandatory if TSPC_PBAP_0_3 (PBAP 1.2) is supported, otherwise Excluded. C.2: Optional if TSPC_PBAP_0_3 (PBAP 1.2) is supported, otherwise Excluded. C.3: Optional if TSPC_PBAP_9_10 (X-BT-UID vCard Property) is supported, otherwise Excluded. C.4: Optional if TSPC_PBAP_0_3 (PBAP 1.2) and any of the mch or cch folders (13d,13e,13k,13l) are supported, otherwise Excluded. C.5: It is mandatory to support at least one of the defined deleted handles behaviors. ------------------------------------------------------------------------------- Supported Phone Book Download functions ( PSE ) ------------------------------------------------------------------------------- Parameter Name Selected Description ------------------------------------------------------------------------------- TSPC_PBAP_10_1 True PSE: Pull Phone Book (M) TSPC_PBAP_10_2 False PSE: Call History Function (O) ------------------------------------------------------------------------------- Supported Phone Book Browsing functions ( PSE ) ------------------------------------------------------------------------------- Parameter Name Selected Description ------------------------------------------------------------------------------- TSPC_PBAP_11_1 True PSE: Set Phone Book (M) TSPC_PBAP_11_2 True PSE: Pull vCard Listing (M) TSPC_PBAP_11_3 True PSE: Pull vCard Entry (M) ------------------------------------------------------------------------------- Used vCard formats (PSE) ------------------------------------------------------------------------------- Parameter Name Selected Description ------------------------------------------------------------------------------- TSPC_PBAP_12_1 True PSE: vCard 2.1 (M) TSPC_PBAP_12_2 True PSE: vCard 3.0 (M) ------------------------------------------------------------------------------- OBEX Functions for PSE ------------------------------------------------------------------------------- Parameter Name Selected Description ------------------------------------------------------------------------------- TSPC_PBAP_13_1 True PSE: Connect (M) TSPC_PBAP_13_2 True PSE: Disconnect (M) TSPC_PBAP_13_3 True PSE: Get (M) TSPC_PBAP_13_4 True PSE: Abort (M) TSPC_PBAP_13_5 True PSE: SetPath (M) TSPC_PBAP_13_6 False PSE: Support for OBEX authentication initiation (C.1) ------------------------------------------------------------------------------- C.1: Optional to support initiation if TSPC_PBAP_0_1 (PBAP 1.0) or TSPC_PBAP_0_2 (PBAP 1.1) is supported, otherwise Excluded. ------------------------------------------------------------------------------- PSE OBEX Header Support ------------------------------------------------------------------------------- Parameter Name Selected Description ------------------------------------------------------------------------------- TSPC_PBAP_14_1 True PSE: Name (M) TSPC_PBAP_14_2 True PSE: Type (M) TSPC_PBAP_14_3 True PSE: Body (M) TSPC_PBAP_14_4 True PSE: End of Body (M) TSPC_PBAP_14_5 True PSE: Target (M) TSPC_PBAP_14_6 True PSE: Who (M) TSPC_PBAP_14_7 True PSE: Connection ID (M) TSPC_PBAP_14_8 True PSE: Authentication Challenge (M) TSPC_PBAP_14_9 True PSE: Authentication Response (M) TSPC_PBAP_14_10 True PSE: Application Parameters (M) TSPC_PBAP_14_11 False PSE: Single Response Mode (C.1) TSPC_PBAP_14_12 False PSE: Single Response Mode Parameter (ability to parse) (C.1) TSPC_PBAP_14_13 False PSE: Single Response Mode Parameter (ability to send) (C.2) ------------------------------------------------------------------------------- C.1: Mandatory if TSPC_PBAP_0_3 (PBAP 1.2) is supported, otherwise Excluded. C.2: Optional if TSPC_PBAP_9a_3 (PBAP 1.2) is supported, otherwise Excluded. ------------------------------------------------------------------------------- OBEX Error Codes for PSE ------------------------------------------------------------------------------- Parameter Name Selected Description ------------------------------------------------------------------------------- TSPC_PBAP_15_1 True PSE: Bad Request (M) TSPC_PBAP_15_2 True PSE: Not Implemented (M) TSPC_PBAP_15_3 True (*) PSE: Unauthorized (O) TSPC_PBAP_15_4 True (*) PSE: Precondition Failed (C.1) TSPC_PBAP_15_5 True PSE: Not Found (M) TSPC_PBAP_15_6 True (*) PSE: Not Acceptable (O) TSPC_PBAP_15_7 True PSE: Service Unavailable (M) TSPC_PBAP_15_8 True (*) PSE: Forbidden (O) ------------------------------------------------------------------------------- C.1: Mandatory if TSPC_PBAP_9_14a (Error reporting) is supported, otherwise Optional. ------------------------------------------------------------------------------- GAP Modes for PCE ------------------------------------------------------------------------------- Parameter Name Selected Description ------------------------------------------------------------------------------- TSPC_PBAP_16_1 False (*) PCE: General discoverable mode (M) TSPC_PBAP_16_2 False (*) PCE: Pairable mode (M) ------------------------------------------------------------------------------- GAP Modes for PSE ------------------------------------------------------------------------------- Parameter Name Selected Description ------------------------------------------------------------------------------- TSPC_PBAP_17_1 True PSE: General discoverable mode (M) TSPC_PBAP_17_2 True PSE: Pairable mode (M) ------------------------------------------------------------------------------- GAP Security Modes for PCE ------------------------------------------------------------------------------- Parameter Name Selected Description ------------------------------------------------------------------------------- TSPC_PBAP_18_1 False (*) PCE: Authentication Procedure (M) TSPC_PBAP_18_2 False (*) PCE: Initiate LMP-Authentication (M) TSPC_PBAP_18_3 False PCE: Security mode 1 (C.1) TSPC_PBAP_18_4 False PCE: Security mode 2 (C.1) TSPC_PBAP_18_5 False PCE: Security mode 3 (C.1) TSPC_PBAP_18_6 False PCE: Security mode 4 (C.1) ------------------------------------------------------------------------------- C.1: At least one of TSPC_PBAP_18_4, TSPC_PBAP_18_5 or TSPC_PBAP_18_6 (security mode 2, 3, or 4) shall be supported. ------------------------------------------------------------------------------- GAP Security Modes for PSE ------------------------------------------------------------------------------- Parameter Name Selected Description ------------------------------------------------------------------------------- TSPC_PBAP_19_1 True PSE: Authentication Procedure (M) TSPC_PBAP_19_2 True PSE: Initiate LMP-Authentication (M) TSPC_PBAP_19_3 False PSE: Security mode 1 (C.2) TSPC_PBAP_19_4 False PSE: Security mode 2 (C.1) TSPC_PBAP_19_5 False PSE: Security mode 3 (C.1) TSPC_PBAP_19_6 True (*) PSE: Security mode 4 (C.1) ------------------------------------------------------------------------------- C.1: At least one of TSPC_PBAP_19_3, TSPC_PBAP_19_4, TSPC_PBAP_19_5 or TSPC_PBAP_19_6 (security mode 2, 3, or 4) shall be supported. C.2: Excluded in PSE. ------------------------------------------------------------------------------- GAP Idle Modes for PSE ------------------------------------------------------------------------------- Parameter Name Selected Description ------------------------------------------------------------------------------- TSPC_PBAP_21_1 True PSE: Initiation of General Inquiry (M) TSPC_PBAP_21_2 False PSE: Initiation of Limited Inquiry (O) ------------------------------------------------------------------------------- SDP Attributes (PCE) ------------------------------------------------------------------------------- Parameter Name Selected Description ------------------------------------------------------------------------------- TSPC_PBAP_22_1 False (*) PCE: BluetoothProfileDescriptorList (M) ------------------------------------------------------------------------------- SDP Attributes (PSE) ------------------------------------------------------------------------------- Parameter Name Selected Description ------------------------------------------------------------------------------- TSPC_PBAP_23_1 True PSE: ProtocolDescriptorList (M) TSPC_PBAP_23_2 True PSE: BluetoothProfileDescriptorList (M) ------------------------------------------------------------------------------- Additional PBAP Capabilities ------------------------------------------------------------------------------- Parameter Name Selected Description ------------------------------------------------------------------------------- TSPC_PBAP_24_1 False PCE: Retrieve Large Phone Book (C.1) TSPC_PBAP_24_2 False PSE: Transfer Large Phone Book (C.2) TSPC_PBAP_24_3 False PCE: Retrieve Empty Phone Book (C.1) TSPC_PBAP_24_4 False PSE: Transfer Empty Phone Book (C.2) TSPC_PBAP_24_5 False PSE: Return Phonebook – Limit number of entries (C.2) TSPC_PBAP_24_6 False PSE: Return vCard listing – Limit number of entries (C.2) TSPC_PBAP_24_7 False PSE: Phone Book Order (C.2) TSPC_PBAP_24_8 False PSE: Call stack timestamps (C.3) TSPC_PBAP_24_9 False PSE: No User Interaction (C.2) TSPC_PBAP_24_10 False PSE: Special Character Handling (C.2) ------------------------------------------------------------------------------- C.1: Optional if TSPC_PBAP_2_1 is supported, otherwise excluded. C.2: Optional if TSPC_PBAP_1_2 is supported, otherwise excluded. C.3: Optional if TSPC_PBAP_10_2 is supported, otherwise excluded. ------------------------------------------------------------------------------- GOEP 2.0 or later Features (PCE) ------------------------------------------------------------------------------- Parameter Name Selected Description ------------------------------------------------------------------------------- TSPC_PBAP_25_1 False PCE: GOEP v2.0 or later (M) TSPC_PBAP_25_2 False (*) PCE: GOEP v2 Backwards Compatibility (M) TSPC_PBAP_25_3 False PCE: OBEX over L2CAP (M) TSPC_PBAP_25_4 False PCE: OBEX SRM (M) TSPC_PBAP_25_5 False (*) PCE: Send OBEX SRMP header (C.1) TSPC_PBAP_25_6 False PCE: Receive OBEX SRMP header (M) ------------------------------------------------------------------------------- C.1: Optional to support if TSPC_PBAP_25_4 (OBEX SRM) is supported, otherwise Excluded. ------------------------------------------------------------------------------- GOEP 2.0 or later Features (PSE) ------------------------------------------------------------------------------- Parameter Name Selected Description ------------------------------------------------------------------------------- TSPC_PBAP_26_1 False PSE: GOEP v2.0 or later (M) TSPC_PBAP_26_2 False (*) PSE: GOEP v2 Backwards Compatibility (M) TSPC_PBAP_26_3 False PSE: OBEX over L2CAP (M) TSPC_PBAP_26_4 False PSE: OBEX SRM (M) TSPC_PBAP_26_5 False (*) PSE: Send OBEX SRMP header (C.1) TSPC_PBAP_26_6 False PSE: Receive OBEX SRMP header (M) TSPC_ALL False Turns on all the test cases ------------------------------------------------------------------------------- C.1: Optional if TSPC_PBAP_26_4 (OBEX SRM) is supported, otherwise Excluded. ------------------------------------------------------------------------------- bluez-5.82/android/PaxHeaders/pixit-hdp.txt0000644000000000000000000000005012537515745015753 xustar0020 atime=1743516876 20 ctime=1743591289 bluez-5.82/android/pixit-hdp.txt0000644000000000000000000000222212537515745015432 0ustar00rootrootHDP PIXIT for the PTS tool. PTS version: 6.1 * - different than PTS defaults & - should be set to IUT Bluetooth address Required PIXIT settings ------------------------------------------------------------------------------- Parameter Name Value ------------------------------------------------------------------------------- TSPX_security_enabled TRUE TSPX_delete_link_key FALSE TSPX_bd_addr_iut 112233445566 (*&) TSPX_sink_device_class_of_device 000900 TSPX_source_device_class_of_device 000900 TSPX_pin_code 0000 TSPX_use_dynamic_pin FALSE TSPX_use_implicit_send TRUE TSPX_MCAP_l2cap_psm_control 1001 TSPX_MCAP_l2cap_psm_data 1003 TSPX_MCAP_sync_lead_time 0BB8 TSPX_MCAP_timestamp_native_resolution 2233 TSPX_MCAP_timestamp_native_accuracy 1400 TSPX_MCAP_timestamp_required_accuracy 6400 TSPX_DC_max 1 TSPX_secure_simple_pairing_pass_key_confirmation FALSE TSPX_time_guard 60000000 TSPX_ieee_device_specialization 10417 TSPX_ieee_standard_configuration TRUE TSPX_MCAP_bluetooth_clock_access_resolution 55 ------------------------------------------------------------------------------- bluez-5.82/android/PaxHeaders/bluetoothd-snoop.c0000644000000000000000000000005014015011623016730 xustar0020 atime=1743516867 20 ctime=1743591279 bluez-5.82/android/bluetoothd-snoop.c0000644000000000000000000001111114015011623016404 0ustar00rootroot// SPDX-License-Identifier: LGPL-2.1-or-later /* * * BlueZ - Bluetooth protocol stack for Linux * * Copyright (C) 2013-2014 Intel Corporation. All rights reserved. * * */ #ifdef HAVE_CONFIG_H #include #endif #include #include #include #if defined(ANDROID) #include #endif #include "lib/bluetooth.h" #include "lib/hci.h" #include "lib/mgmt.h" #include "src/shared/mainloop.h" #include "src/shared/btsnoop.h" #include "src/log.h" #define DEFAULT_SNOOP_FILE "/sdcard/btsnoop_hci.log" static struct btsnoop *snoop = NULL; static uint8_t monitor_buf[BTSNOOP_MAX_PACKET_SIZE]; static int monitor_fd = -1; static void signal_callback(int signum, void *user_data) { switch (signum) { case SIGINT: case SIGTERM: mainloop_quit(); break; } } static uint32_t get_flags_from_opcode(uint16_t opcode) { switch (opcode) { case BTSNOOP_OPCODE_NEW_INDEX: case BTSNOOP_OPCODE_DEL_INDEX: break; case BTSNOOP_OPCODE_COMMAND_PKT: return 0x02; case BTSNOOP_OPCODE_EVENT_PKT: return 0x03; case BTSNOOP_OPCODE_ACL_TX_PKT: return 0x00; case BTSNOOP_OPCODE_ACL_RX_PKT: return 0x01; case BTSNOOP_OPCODE_SCO_TX_PKT: case BTSNOOP_OPCODE_SCO_RX_PKT: break; case BTSNOOP_OPCODE_OPEN_INDEX: case BTSNOOP_OPCODE_CLOSE_INDEX: break; } return 0xff; } static void data_callback(int fd, uint32_t events, void *user_data) { unsigned char control[32]; struct mgmt_hdr hdr; struct msghdr msg; struct iovec iov[2]; if (events & (EPOLLERR | EPOLLHUP)) { mainloop_remove_fd(monitor_fd); return; } iov[0].iov_base = &hdr; iov[0].iov_len = MGMT_HDR_SIZE; iov[1].iov_base = monitor_buf; iov[1].iov_len = sizeof(monitor_buf); memset(&msg, 0, sizeof(msg)); msg.msg_iov = iov; msg.msg_iovlen = 2; msg.msg_control = control; msg.msg_controllen = sizeof(control); while (true) { struct cmsghdr *cmsg; struct timeval *tv = NULL; struct timeval ctv; uint16_t opcode, index, pktlen; uint32_t flags; ssize_t len; len = recvmsg(monitor_fd, &msg, MSG_DONTWAIT); if (len < 0) break; if (len < MGMT_HDR_SIZE) break; for (cmsg = CMSG_FIRSTHDR(&msg); cmsg != NULL; cmsg = CMSG_NXTHDR(&msg, cmsg)) { if (cmsg->cmsg_level != SOL_SOCKET) continue; if (cmsg->cmsg_type == SCM_TIMESTAMP) { memcpy(&ctv, CMSG_DATA(cmsg), sizeof(ctv)); tv = &ctv; } } opcode = btohs(hdr.opcode); index = btohs(hdr.index); pktlen = btohs(hdr.len); if (index) continue; flags = get_flags_from_opcode(opcode); if (flags != 0xff) btsnoop_write(snoop, tv, flags, 0, monitor_buf, pktlen); } } static int open_monitor(const char *path) { struct sockaddr_hci addr; int opt = 1; snoop = btsnoop_create(path, 0, 0, BTSNOOP_FORMAT_HCI); if (!snoop) return -1; monitor_fd = socket(AF_BLUETOOTH, SOCK_RAW | SOCK_CLOEXEC, BTPROTO_HCI); if (monitor_fd < 0) goto failed; memset(&addr, 0, sizeof(addr)); addr.hci_family = AF_BLUETOOTH; addr.hci_dev = HCI_DEV_NONE; addr.hci_channel = HCI_CHANNEL_MONITOR; if (bind(monitor_fd, (struct sockaddr *) &addr, sizeof(addr)) < 0) goto failed_close; if (setsockopt(monitor_fd, SOL_SOCKET, SO_TIMESTAMP, &opt, sizeof(opt)) < 0) goto failed_close; mainloop_add_fd(monitor_fd, EPOLLIN, data_callback, NULL, NULL); return 0; failed_close: close(monitor_fd); monitor_fd = -1; failed: btsnoop_unref(snoop); snoop = NULL; return -1; } static void close_monitor(void) { btsnoop_unref(snoop); snoop = NULL; close(monitor_fd); monitor_fd = -1; } static void set_capabilities(void) { #if defined(ANDROID) struct __user_cap_header_struct header; struct __user_cap_data_struct cap; header.version = _LINUX_CAPABILITY_VERSION; header.pid = 0; /* * CAP_NET_RAW: for snooping * CAP_DAC_READ_SEARCH: override path search permissions */ cap.effective = cap.permitted = CAP_TO_MASK(CAP_NET_RAW) | CAP_TO_MASK(CAP_DAC_READ_SEARCH); cap.inheritable = 0; /* TODO: Move to cap_set_proc once bionic support it */ if (capset(&header, &cap) < 0) exit(EXIT_FAILURE); #endif } int main(int argc, char *argv[]) { const char *path; __btd_log_init(NULL, 0); DBG(""); set_capabilities(); if (argc > 1) path = argv[1]; else path = DEFAULT_SNOOP_FILE; mainloop_init(); if (!strcmp(DEFAULT_SNOOP_FILE, path)) rename(DEFAULT_SNOOP_FILE, DEFAULT_SNOOP_FILE ".old"); if (open_monitor(path) < 0) { error("bluetoothd_snoop: start failed"); return EXIT_FAILURE; } info("bluetoothd_snoop: started"); mainloop_run_with_signal(signal_callback, NULL); close_monitor(); info("bluetoothd_snoop: stopped"); __btd_log_cleanup(); return EXIT_SUCCESS; } bluez-5.82/android/PaxHeaders/tester-gatt.c0000644000000000000000000000005014015011623015666 xustar0020 atime=1743516864 20 ctime=1743591278 bluez-5.82/android/tester-gatt.c0000644000000000000000000040766614015011623015372 0ustar00rootroot// SPDX-License-Identifier: Apache-2.0 /* * Copyright (C) 2014 Intel Corporation * */ #define _GNU_SOURCE #include #include "emulator/bthost.h" #include "lib/bluetooth.h" #include "src/shared/util.h" #include "src/shared/tester.h" #include "src/shared/queue.h" #include "tester-main.h" #define ATT_HANDLE_SIZE 2 #define L2CAP_ATT_ERROR 0x01 #define L2CAP_ATT_EXCHANGE_MTU_REQ 0x02 #define L2CAP_ATT_EXCHANGE_MTU_RSP 0x03 #define L2CAP_ATT_FIND_BY_TYPE_REQ 0x06 #define L2CAP_ATT_READ_REQ 0x0a #define L2CAP_ATT_READ_RSP 0x0b #define L2CAP_ATT_WRITE_REQ 0x12 #define L2CAP_ATT_WRITE_RSP 0x13 #define L2CAP_ATT_HANDLE_VALUE_NOTIFY 0x1b #define L2CAP_ATT_HANDLE_VALUE_IND 0x1d #define GATT_STATUS_SUCCESS 0x00000000 #define GATT_STATUS_FAILURE 0x00000101 #define GATT_STATUS_INS_AUTH 0x08 #define GATT_ERR_INVAL_ATTR_VALUE_LEN 0x0D #define GATT_SERVER_DISCONNECTED 0 #define GATT_SERVER_CONNECTED 1 #define APP1_ID 1 #define APP2_ID 2 #define CONN1_ID 1 #define CONN2_ID 2 #define TRANS1_ID 1 #define BT_TRANSPORT_UNKNOWN 0x00 #define GATT_SERVER_TRANSPORT_LE 0x01 #define GATT_SERVER_TRANSPORT_BREDR 0x02 #define GATT_SERVER_TRANSPORT_LE_BREDR (0x01 | 0x02) #define GATT_WRITE_TYPE_NO_RESPONSE 0x01 #define GATT_WRITE_TYPE_DEFAULT 0x02 #define GATT_WRITE_TYPE_PREPARE 0x03 #define GATT_WRITE_TYPE_SIGNED 0x04 #define CHAR_PROP_BROADCAST 0x01 #define CHAR_PROP_READ 0x02 #define CHAR_PROP_WRITE_WITHOUT_RESPONSE 0x04 #define CHAR_PROP_WRITE 0x08 #define CHAR_PROP_NOTIFY 0x10 #define CHAR_PROP_INDICATE 0x20 #define CHAR_PROP_AUTHENTICATED_SIGNED_WRITES 0x40 #define CHAR_PROP_EXTENDED_PROPERTIES 0x80 #define CHAR_PERM_READ 0x0001 #define CHAR_PERM_READ_ENCRYPTED 0x0002 #define CHAR_PERM_READ_ENCRYPTED_MITM 0x0004 #define CHAR_PERM_WRITE 0x0010 #define CHAR_PERM_WRITE_ENCRYPTED 0x0020 #define CHAR_PERM_WRITE_ENCRYPTED_MITM 0x0040 #define CHAR_PERM_WRITE_SIGNED 0x0080 #define CHAR_PERM_WRITE_SIGNED_MITM 0x0100 static struct queue *list; /* List of gatt test cases */ static uint16_t srvc1_handle; static uint16_t inc_srvc1_handle; static uint16_t char1_handle; static struct iovec char1_handle_v = { .iov_base = &char1_handle, .iov_len = sizeof(char1_handle), }; struct set_att_data { char *to; char *from; int len; }; struct att_write_req_data { uint16_t *attr_handle; uint8_t *value; }; static bt_uuid_t app1_uuid = { .uu = { 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01 }, }; static bt_uuid_t app2_uuid = { .uu = { 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02 }, }; static uint8_t value_1[] = {0x01}; static uint8_t att_write_req_value_1[] = {0x00, 0x01, 0x02, 0x03}; static struct iovec att_write_req_value_1_v = { .iov_base = att_write_req_value_1, .iov_len = sizeof(att_write_req_value_1), }; struct gatt_connect_data { const int app_id; const int conn_id; }; struct gatt_search_service_data { const int conn_id; bt_uuid_t *filter_uuid; }; struct get_char_data { const int conn_id; btgatt_srvc_id_t *service; }; struct get_desc_data { const int conn_id; btgatt_srvc_id_t *service; btgatt_gatt_id_t *characteristic; btgatt_gatt_id_t *desc; }; struct get_incl_data { const int conn_id; btgatt_srvc_id_t *service; btgatt_srvc_id_t *start_service; }; struct read_char_data { const int conn_id; btgatt_srvc_id_t *service; btgatt_gatt_id_t *characteristic; int auth_req; }; struct read_desc_data { const int conn_id; btgatt_srvc_id_t *service; btgatt_gatt_id_t *characteristic; btgatt_gatt_id_t *descriptor; int auth_req; }; struct write_char_data { int conn_id; btgatt_srvc_id_t *service; btgatt_gatt_id_t *characteristic; int write_type; int len; int auth_req; char *p_value; }; struct write_desc_data { int conn_id; btgatt_srvc_id_t *service; btgatt_gatt_id_t *characteristic; btgatt_gatt_id_t *descriptor; int write_type; int len; int auth_req; char *p_value; }; struct notif_data { int conn_id; const bt_bdaddr_t *bdaddr; btgatt_srvc_id_t *service; btgatt_gatt_id_t *charac; }; struct add_service_data { int app_id; btgatt_srvc_id_t *service; int num_handles; }; struct add_included_service_data { int app_id; uint16_t *inc_srvc_handle; uint16_t *srvc_handle; }; struct add_char_data { int app_id; uint16_t *srvc_handle; bt_uuid_t *uuid; int properties; int permissions; }; struct add_desc_data { int app_id; uint16_t *srvc_handle; bt_uuid_t *uuid; int permissions; }; struct start_srvc_data { int app_id; uint16_t *srvc_handle; int transport; }; struct stop_srvc_data { int app_id; uint16_t *srvc_handle; }; struct delete_srvc_data { int app_id; uint16_t *srvc_handle; }; struct send_indication_data { int app_id; uint16_t *attr_handle; int conn_id; int len; int confirm; char *p_value; }; struct send_resp_data { int conn_id; int trans_id; int status; btgatt_response_t *response; }; static bt_bdaddr_t emu_remote_bdaddr_val = { .address = { 0x00, 0xaa, 0x01, 0x01, 0x00, 0x00 }, }; static bt_device_type_t emu_remote_ble_device_type = BT_DEVICE_DEVTYPE_BLE; static bt_property_t prop_emu_remotes_default_set[] = { { BT_PROPERTY_BDADDR, sizeof(emu_remote_bdaddr_val), &emu_remote_bdaddr_val }, }; static bt_property_t prop_emu_remotes_default_le_set[] = { { BT_PROPERTY_BDADDR, sizeof(emu_remote_bdaddr_val), &emu_remote_bdaddr_val }, { BT_PROPERTY_TYPE_OF_DEVICE, sizeof(bt_device_type_t), &emu_remote_ble_device_type }, }; static struct bt_action_data prop_test_remote_ble_bdaddr_req = { .addr = &emu_remote_bdaddr_val, .prop_type = BT_PROPERTY_BDADDR, .prop = &prop_emu_remotes_default_set[0], }; static bt_scan_mode_t setprop_scan_mode_conn_val = BT_SCAN_MODE_CONNECTABLE_DISCOVERABLE; static bt_property_t prop_test_scan_mode_conn = { .type = BT_PROPERTY_ADAPTER_SCAN_MODE, .val = &setprop_scan_mode_conn_val, .len = sizeof(setprop_scan_mode_conn_val), }; static struct emu_l2cap_cid_data cid_data; static struct gatt_connect_data app1_conn_req = { .app_id = APP1_ID, .conn_id = CONN1_ID, }; static struct gatt_connect_data app1_conn2_req = { .app_id = APP1_ID, .conn_id = CONN2_ID, }; static struct gatt_connect_data app2_conn_req = { .app_id = APP2_ID, .conn_id = CONN2_ID, }; static struct gatt_search_service_data search_services_1 = { .conn_id = CONN1_ID, .filter_uuid = NULL, }; static const struct iovec exchange_mtu_req_pdu = raw_pdu(0x02, 0xa0, 0x02); static const struct iovec exchange_mtu_resp_pdu = raw_pdu(0x03, 0xa0, 0x02); static struct bt_action_data bearer_type = { .bearer_type = BDADDR_LE_PUBLIC, }; static btgatt_srvc_id_t service_1 = { .is_primary = true, .id = { .inst_id = 0, .uuid.uu = {0xfb, 0x34, 0x9b, 0x5f, 0x80, 0x00, 0x00, 0x80, 0x00, 0x10, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00} } }; static btgatt_srvc_id_t service_2 = { .is_primary = true, .id = { .inst_id = 1, .uuid.uu = {0xfb, 0x34, 0x9b, 0x5f, 0x80, 0x00, 0x00, 0x80, 0x00, 0x10, 0x00, 0x00, 0x01, 0x18, 0x00, 0x00}, } }; static btgatt_srvc_id_t service_add_1 = { .is_primary = true, .id = { .inst_id = 0, .uuid.uu = {0xfb, 0x34, 0x9b, 0x5f, 0x80, 0x00, 0x00, 0x80, 0x00, 0x10, 0x00, 0x00, 0xFF, 0xEF, 0x00, 0x00}, } }; static btgatt_srvc_id_t service_add_2 = { .is_primary = true, .id = { .inst_id = 1, .uuid.uu = {0xfb, 0x34, 0x9b, 0x5f, 0x80, 0x00, 0x00, 0x80, 0x00, 0x10, 0x00, 0x00, 0xFF, 0xDF, 0x00, 0x00}, } }; static btgatt_srvc_id_t service_add_3 = { .is_primary = true, .id = { .inst_id = 2, .uuid.uu = {0xfb, 0x34, 0x9b, 0x5f, 0x80, 0x00, 0x00, 0x80, 0x00, 0x10, 0x00, 0x00, 0xFF, 0xCF, 0x00, 0x00}, } }; static btgatt_srvc_id_t included_1 = { .is_primary = false, .id = { .inst_id = 1, .uuid.uu = {0xfb, 0x34, 0x9b, 0x5f, 0x80, 0x00, 0x00, 0x80, 0x00, 0x10, 0x00, 0x00, 0xff, 0xfe, 0x00, 0x00}, } }; static btgatt_srvc_id_t included_2 = { .is_primary = false, .id = { .inst_id = 1, .uuid.uu = {0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10}, } }; static btgatt_gatt_id_t characteristic_1 = { .inst_id = 1, .uuid.uu = {0xfb, 0x34, 0x9b, 0x5f, 0x80, 0x00, 0x00, 0x80, 0x00, 0x10, 0x00, 0x00, 0x19, 0x00, 0x00, 0x00} }; static btgatt_gatt_id_t desc_1 = { .inst_id = 1, .uuid.uu = {0xfb, 0x34, 0x9b, 0x5f, 0x80, 0x00, 0x00, 0x80, 0x00, 0x10, 0x00, 0x00, 0x00, 0x29, 0x00, 0x00} }; static btgatt_gatt_id_t desc_2 = { .inst_id = 2, .uuid.uu = {0xfb, 0x34, 0x9b, 0x5f, 0x80, 0x00, 0x00, 0x80, 0x00, 0x10, 0x00, 0x00, 0x01, 0x29, 0x00, 0x00} }; static btgatt_read_params_t read_params_1; static btgatt_write_params_t write_params_1; static btgatt_notify_params_t notify_params_1; static struct get_char_data get_char_data_1 = { .conn_id = CONN1_ID, .service = &service_1 }; static struct get_char_data get_char_data_2 = { .conn_id = CONN1_ID, .service = &service_2 }; static struct get_desc_data get_desc_data_1 = { .conn_id = CONN1_ID, .service = &service_1, .characteristic = &characteristic_1, }; static struct get_desc_data get_desc_data_2 = { .conn_id = CONN1_ID, .service = &service_1, .characteristic = &characteristic_1, .desc = &desc_1, }; static struct read_char_data read_char_data_1 = { .conn_id = CONN1_ID, .service = &service_1, .characteristic = &characteristic_1, }; static struct read_char_data read_char_data_2 = { .conn_id = CONN1_ID, .service = &service_2, .characteristic = &characteristic_1, }; static struct read_desc_data read_desc_data_1 = { .conn_id = CONN1_ID, .service = &service_1, .characteristic = &characteristic_1, .descriptor = &desc_1, }; static struct read_desc_data read_desc_data_2 = { .conn_id = CONN1_ID, .service = &service_1, .characteristic = &characteristic_1, .descriptor = &desc_2, }; static struct get_incl_data get_incl_data_1 = { .conn_id = CONN1_ID, .service = &service_1 }; static char value_2[] = {0x00, 0x01, 0x02, 0x03}; static struct write_char_data write_char_data_1 = { .conn_id = CONN1_ID, .service = &service_1, .characteristic = &characteristic_1, .write_type = GATT_WRITE_TYPE_NO_RESPONSE, .len = sizeof(value_2), .p_value = value_2, .auth_req = 0 }; static struct write_char_data write_char_data_2 = { .conn_id = CONN1_ID, .service = &service_1, .characteristic = &characteristic_1, .write_type = GATT_WRITE_TYPE_DEFAULT, .len = sizeof(value_2), .p_value = value_2, .auth_req = 0 }; static struct write_desc_data write_desc_data_1 = { .conn_id = CONN1_ID, .service = &service_1, .characteristic = &characteristic_1, .descriptor = &desc_1, .write_type = 2, .len = sizeof(value_2), .auth_req = 0, .p_value = value_2, }; static struct write_desc_data write_desc_data_2 = { .conn_id = CONN1_ID, .service = &service_1, .characteristic = &characteristic_1, .descriptor = &desc_2, .write_type = 2, .len = sizeof(value_2), .auth_req = 0, .p_value = value_2, }; static struct notif_data notif_data_1 = { .conn_id = CONN1_ID, .service = &service_1, .charac = &characteristic_1, .bdaddr = &emu_remote_bdaddr_val, }; static struct add_service_data add_service_data_1 = { .app_id = APP1_ID, .service = &service_add_1, .num_handles = 1 }; static struct add_service_data add_service_data_2 = { .app_id = APP1_ID, .service = &service_add_2, .num_handles = 1 }; static struct add_service_data add_service_data_3 = { .app_id = APP1_ID, .service = &service_add_3, .num_handles = 1 }; static struct add_service_data add_service_data_4 = { .app_id = APP1_ID, .service = &service_add_1, .num_handles = 2 }; static struct add_service_data add_service_data_5 = { .app_id = APP1_ID, .service = &service_add_1, .num_handles = 3 }; static struct add_service_data add_service_data_6 = { .app_id = APP1_ID, .service = &service_add_1, .num_handles = 4 }; static struct add_service_data add_bad_service_data_1 = { .app_id = APP1_ID, .service = &service_add_1, .num_handles = 0 }; static struct add_service_data add_sec_service_data_1 = { .app_id = APP1_ID, .service = &included_1, .num_handles = 1 }; static uint16_t srvc_bad_handle = 0xffff; static struct add_included_service_data add_inc_service_data_1 = { .app_id = APP1_ID, .inc_srvc_handle = &inc_srvc1_handle, .srvc_handle = &srvc1_handle }; static struct add_included_service_data add_bad_inc_service_data_1 = { .app_id = APP1_ID, .inc_srvc_handle = &srvc_bad_handle, .srvc_handle = &srvc1_handle }; static struct add_char_data add_char_data_1 = { .app_id = APP1_ID, .srvc_handle = &srvc1_handle, .uuid = &app1_uuid, .properties = 0, .permissions = 0 }; static struct add_char_data add_char_data_2 = { .app_id = APP1_ID, .srvc_handle = &srvc1_handle, .uuid = &app1_uuid, .properties = CHAR_PROP_WRITE, .permissions = CHAR_PERM_WRITE }; static struct add_char_data add_bad_char_data_1 = { .app_id = APP1_ID, .srvc_handle = &srvc_bad_handle, .uuid = &app1_uuid, .properties = 0, .permissions = 0 }; static struct add_desc_data add_bad_desc_data_1 = { .app_id = APP1_ID, .srvc_handle = &srvc_bad_handle, .uuid = &app2_uuid, .permissions = 0 }; static struct add_desc_data add_bad_desc_data_2 = { .app_id = APP2_ID, .srvc_handle = &srvc1_handle, .uuid = &app2_uuid, .permissions = 0 }; static struct add_desc_data add_desc_data_1 = { .app_id = APP1_ID, .srvc_handle = &srvc1_handle, .uuid = &app2_uuid, .permissions = 0 }; static struct start_srvc_data start_srvc_data_1 = { .app_id = APP1_ID, .srvc_handle = &srvc1_handle, .transport = GATT_SERVER_TRANSPORT_LE_BREDR }; static struct start_srvc_data start_srvc_data_2 = { .app_id = APP1_ID, .srvc_handle = &srvc1_handle, .transport = GATT_SERVER_TRANSPORT_LE }; static struct start_srvc_data start_bad_srvc_data_1 = { .app_id = APP1_ID, .srvc_handle = &srvc_bad_handle, .transport = GATT_SERVER_TRANSPORT_LE }; static struct start_srvc_data start_bad_srvc_data_2 = { .app_id = APP1_ID, .srvc_handle = &srvc1_handle, .transport = 0 }; static struct stop_srvc_data stop_srvc_data_1 = { .app_id = APP1_ID, .srvc_handle = &srvc1_handle }; static struct stop_srvc_data stop_bad_srvc_data_1 = { .app_id = APP1_ID, .srvc_handle = &srvc_bad_handle }; static struct delete_srvc_data delete_srvc_data_1 = { .app_id = APP1_ID, .srvc_handle = &srvc1_handle }; static struct delete_srvc_data delete_bad_srvc_data_1 = { .app_id = APP1_ID, .srvc_handle = &srvc_bad_handle }; static uint16_t srvc_indication_handle_1 = 0x01; static struct send_indication_data send_indication_data_1 = { .app_id = APP1_ID, .attr_handle = &srvc_indication_handle_1, .conn_id = CONN1_ID, .len = sizeof(value_2), .p_value = value_2, .confirm = 1 }; static struct send_indication_data send_indication_data_2 = { .app_id = APP1_ID, .attr_handle = &srvc_indication_handle_1, .conn_id = CONN1_ID, .len = sizeof(value_2), .p_value = value_2, .confirm = 0 }; static struct send_indication_data send_bad_indication_data_1 = { .app_id = APP1_ID, .attr_handle = &srvc_indication_handle_1, .conn_id = CONN2_ID, .len = sizeof(value_2), .p_value = value_2, .confirm = 0 }; struct set_read_params { btgatt_read_params_t *params; btgatt_srvc_id_t *srvc_id; btgatt_gatt_id_t *char_id; btgatt_gatt_id_t *descr_id; uint8_t *value; uint16_t len; uint16_t value_type; uint8_t status; }; struct set_write_params { btgatt_write_params_t *params; btgatt_srvc_id_t *srvc_id; btgatt_gatt_id_t *char_id; btgatt_gatt_id_t *descr_id; uint8_t status; }; struct set_notify_params { btgatt_notify_params_t *params; uint8_t *value; uint16_t len; uint8_t is_notify; btgatt_srvc_id_t *srvc_id; btgatt_gatt_id_t *char_id; bt_bdaddr_t *bdaddr; }; static struct set_read_params set_read_param_1 = { .params = &read_params_1, .srvc_id = &service_1, .char_id = &characteristic_1, .value = value_1, .len = sizeof(value_1), .status = BT_STATUS_SUCCESS }; static struct set_read_params set_read_param_2 = { .params = &read_params_1, .srvc_id = &service_1, .char_id = &characteristic_1, .status = GATT_STATUS_INS_AUTH }; static struct set_read_params set_read_param_3 = { .params = &read_params_1, .srvc_id = &service_2, .char_id = &characteristic_1, .status = BT_STATUS_FAIL }; static struct set_read_params set_read_param_4 = { .params = &read_params_1, .srvc_id = &service_1, .char_id = &characteristic_1, .descr_id = &desc_1, .value = value_1, .len = sizeof(value_1), .status = BT_STATUS_SUCCESS }; static struct set_read_params set_read_param_5 = { .params = &read_params_1, .srvc_id = &service_1, .char_id = &characteristic_1, .descr_id = &desc_1, .status = GATT_STATUS_INS_AUTH }; static struct set_read_params set_read_param_6 = { .params = &read_params_1, .srvc_id = &service_1, .char_id = &characteristic_1, .descr_id = &desc_2, .status = BT_STATUS_FAIL }; static struct set_write_params set_write_param_1 = { .params = &write_params_1, .srvc_id = &service_1, .char_id = &characteristic_1, .status = BT_STATUS_SUCCESS }; static struct set_write_params set_write_param_2 = { .params = &write_params_1, .srvc_id = &service_1, .char_id = &characteristic_1, .status = GATT_STATUS_INS_AUTH }; static struct set_write_params set_write_param_3 = { .params = &write_params_1, .srvc_id = &service_1, .char_id = &characteristic_1, .status = BT_STATUS_FAIL }; static struct set_write_params set_write_param_4 = { .params = &write_params_1, .srvc_id = &service_1, .char_id = &characteristic_1, .descr_id = &desc_1, .status = BT_STATUS_SUCCESS }; static struct set_write_params set_write_param_5 = { .params = &write_params_1, .srvc_id = &service_1, .char_id = &characteristic_1, .descr_id = &desc_2, .status = BT_STATUS_FAIL }; static struct set_write_params set_write_param_6 = { .params = &write_params_1, .srvc_id = &service_1, .char_id = &characteristic_1, .descr_id = &desc_1, .status = GATT_STATUS_INS_AUTH }; static struct set_notify_params set_notify_param_1 = { .params = ¬ify_params_1, .value = value_1, .len = sizeof(value_1), .is_notify = 0, .srvc_id = &service_1, .char_id = &characteristic_1, .bdaddr = &emu_remote_bdaddr_val }; static struct set_notify_params set_notify_param_2 = { .params = ¬ify_params_1, .value = value_1, .len = sizeof(value_1), .is_notify = 1, .srvc_id = &service_1, .char_id = &characteristic_1, .bdaddr = &emu_remote_bdaddr_val }; static btgatt_response_t response_1 = { .handle = 0x1c, .attr_value.auth_req = 0, .attr_value.handle = 0x1d, .attr_value.len = 0, .attr_value.offset = 0, }; static btgatt_response_t response_2 = { .handle = 0x1c, .attr_value.auth_req = 0, .attr_value.handle = 0x1d, .attr_value.len = sizeof(att_write_req_value_1), .attr_value.offset = 0, }; static struct send_resp_data send_resp_data_1 = { .conn_id = CONN1_ID, .trans_id = TRANS1_ID, .status = BT_STATUS_SUCCESS, .response = &response_1, }; static struct send_resp_data send_resp_data_2 = { .conn_id = CONN1_ID, .trans_id = TRANS1_ID, .status = BT_STATUS_SUCCESS, .response = &response_2, }; static struct send_resp_data send_resp_data_2_error = { .conn_id = CONN1_ID, .trans_id = TRANS1_ID, .status = GATT_ERR_INVAL_ATTR_VALUE_LEN, .response = &response_2, }; #define SEARCH_SERVICE_SINGLE_SUCCESS_PDUS \ raw_pdu(0x10, 0x01, 0x00, 0xff, 0xff, 0x00, 0x28), \ raw_pdu(0x11, 0x06, 0x01, 0x00, 0x10, 0x00, 0x00, 0x18), \ raw_pdu(0x10, 0x11, 0x00, 0xff, 0xff, 0x00, 0x28), \ raw_pdu(0x01, 0x10, 0x11, 0x00, 0x0a) #define READ_BY_TYPE_SINGLE_CHARACTERISTIC_PDUS \ raw_pdu(0x08, 0x01, 0x00, 0x10, 0x00, 0x03, 0x28), \ raw_pdu(0x09, 0x07, 0x02, 0x00, 0x04, 0x00, 0x00, 0x19, 0x00), \ raw_pdu(0x08, 0x03, 0x00, 0x10, 0x00, 0x03, 0x28), \ raw_pdu(0x01, 0x08, 0x03, 0x00, 0x0a) static struct iovec search_service[] = { SEARCH_SERVICE_SINGLE_SUCCESS_PDUS, end_pdu }; static struct iovec search_service_2[] = { raw_pdu(0x10, 0x01, 0x00, 0xff, 0xff, 0x00, 0x28), raw_pdu(0x11, 0x06, 0x01, 0x00, 0x10, 0x00, 0x00, 0x18), raw_pdu(0x10, 0x11, 0x00, 0xff, 0xff, 0x00, 0x28), raw_pdu(0x11, 0x06, 0x11, 0x00, 0x20, 0x00, 0x01, 0x18), raw_pdu(0x10, 0x21, 0x00, 0xff, 0xff, 0x00, 0x28), raw_pdu(0x01, 0x10, 0x21, 0x00, 0x0a), end_pdu }; static struct iovec search_service_3[] = { raw_pdu(0x10, 0x01, 0x00, 0xff, 0xff, 0x00, 0x28), raw_pdu(0x01, 0x08, 0x01, 0x00, 0x0a), end_pdu }; static struct iovec search_service_4[] = { raw_pdu(0x10, 0x01, 0x00, 0xff, 0xff, 0x00, 0x28), raw_pdu(0x11, 0x06, 0x01, 0x00, 0x00, 0x00, 0x00, 0x18), end_pdu }; static struct iovec get_characteristic_1[] = { SEARCH_SERVICE_SINGLE_SUCCESS_PDUS, READ_BY_TYPE_SINGLE_CHARACTERISTIC_PDUS, end_pdu }; static struct iovec get_characteristic_2[] = { SEARCH_SERVICE_SINGLE_SUCCESS_PDUS, raw_pdu(0x08, 0x01, 0x00, 0x10, 0x00, 0x03, 0x28), raw_pdu(0x09, 0x07, 0x00, 0x00, 0x04, 0x00, 0x00, 0x19, 0x00), end_pdu }; static struct iovec get_descriptor_0[] = { SEARCH_SERVICE_SINGLE_SUCCESS_PDUS, READ_BY_TYPE_SINGLE_CHARACTERISTIC_PDUS, raw_pdu(0x04, 0x01, 0x00, 0x10, 0x00), raw_pdu(0x05, 0x01, 0x00, 0x00, 0x00, 0x29), end_pdu }; static struct iovec get_descriptor_1[] = { SEARCH_SERVICE_SINGLE_SUCCESS_PDUS, READ_BY_TYPE_SINGLE_CHARACTERISTIC_PDUS, raw_pdu(0x04, 0x01, 0x00, 0x10, 0x00), raw_pdu(0x05, 0x01, 0x04, 0x00, 0x00, 0x29), raw_pdu(0x04, 0x05, 0x00, 0x10, 0x00), raw_pdu(0x01, 0x04, 0x05, 0x00, 0x0a), end_pdu }; static struct iovec get_descriptor_2[] = { SEARCH_SERVICE_SINGLE_SUCCESS_PDUS, READ_BY_TYPE_SINGLE_CHARACTERISTIC_PDUS, raw_pdu(0x04, 0x01, 0x00, 0x10, 0x00), raw_pdu(0x05, 0x01, 0x04, 0x00, 0x00, 0x29, 0x05, 0x00, 0x01, 0x29), raw_pdu(0x04, 0x06, 0x00, 0x10, 0x00), raw_pdu(0x01, 0x04, 0x06, 0x00, 0x0a), end_pdu }; static struct iovec get_descriptor_3[] = { SEARCH_SERVICE_SINGLE_SUCCESS_PDUS, READ_BY_TYPE_SINGLE_CHARACTERISTIC_PDUS, raw_pdu(0x04, 0x01, 0x00, 0x10, 0x00), raw_pdu(0x01, 0x04, 0x01, 0x00, 0x0a), end_pdu }; static struct iovec get_included_0[] = { SEARCH_SERVICE_SINGLE_SUCCESS_PDUS, raw_pdu(0x08, 0x01, 0x00, 0x10, 0x00, 0x02, 0x28), raw_pdu(0x09, 0x08, 0x00, 0x00, 0x15, 0x00, 0x19, 0x00, 0xff, 0xfe), end_pdu }; static struct iovec get_included_1[] = { SEARCH_SERVICE_SINGLE_SUCCESS_PDUS, raw_pdu(0x08, 0x01, 0x00, 0x10, 0x00, 0x02, 0x28), raw_pdu(0x09, 0x08, 0x02, 0x00, 0x15, 0x00, 0x19, 0x00, 0xff, 0xfe), raw_pdu(0x08, 0x03, 0x00, 0x10, 0x00, 0x02, 0x28), raw_pdu(0x01, 0x08, 0x03, 0x00, 0x0a), end_pdu }; static struct iovec get_included_2[] = { SEARCH_SERVICE_SINGLE_SUCCESS_PDUS, raw_pdu(0x08, 0x01, 0x00, 0x10, 0x00, 0x02, 0x28), raw_pdu(0x09, 0x06, 0x02, 0x00, 0x15, 0x00, 0x19, 0x00), raw_pdu(0x0a, 0x15, 0x00), raw_pdu(0x0b, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10), raw_pdu(0x08, 0x03, 0x00, 0x10, 0x00, 0x02, 0x28), raw_pdu(0x01, 0x08, 0x03, 0x00, 0x0a), end_pdu }; static struct iovec get_included_3[] = { SEARCH_SERVICE_SINGLE_SUCCESS_PDUS, raw_pdu(0x08, 0x01, 0x00, 0x10, 0x00, 0x02, 0x28), raw_pdu(0x01, 0x08, 0x01, 0x00, 0x0a), end_pdu }; static struct iovec read_characteristic_1[] = { SEARCH_SERVICE_SINGLE_SUCCESS_PDUS, raw_pdu(0x08, 0x01, 0x00, 0x10, 0x00, 0x03, 0x28), raw_pdu(0x09, 0x07, 0x02, 0x00, 0x04, 0x03, 0x00, 0x19, 0x00), raw_pdu(0x08, 0x03, 0x00, 0x10, 0x00, 0x03, 0x28), raw_pdu(0x01, 0x08, 0x03, 0x00, 0x0a), raw_pdu(0x0a, 0x03, 0x00), raw_pdu(0x0b, 0x01), end_pdu }; static struct iovec read_characteristic_2[] = { SEARCH_SERVICE_SINGLE_SUCCESS_PDUS, raw_pdu(0x08, 0x01, 0x00, 0x10, 0x00, 0x03, 0x28), raw_pdu(0x09, 0x07, 0x02, 0x00, 0x04, 0x03, 0x00, 0x19, 0x00), raw_pdu(0x08, 0x03, 0x00, 0x10, 0x00, 0x03, 0x28), raw_pdu(0x01, 0x08, 0x03, 0x00, 0x0a), raw_pdu(0x0a, 0x03, 0x00), raw_pdu(0x01, 0x0a, 0x03, 0x00, 0x08), end_pdu }; static struct iovec read_descriptor_1[] = { SEARCH_SERVICE_SINGLE_SUCCESS_PDUS, READ_BY_TYPE_SINGLE_CHARACTERISTIC_PDUS, raw_pdu(0x04, 0x01, 0x00, 0x10, 0x00), raw_pdu(0x05, 0x01, 0x04, 0x00, 0x00, 0x29), raw_pdu(0x04, 0x05, 0x00, 0x10, 0x00), raw_pdu(0x01, 0x04, 0x05, 0x00, 0x0a), raw_pdu(0x0a, 0x04, 0x00), raw_pdu(0x0b, 0x01), end_pdu }; static struct iovec read_descriptor_2[] = { SEARCH_SERVICE_SINGLE_SUCCESS_PDUS, READ_BY_TYPE_SINGLE_CHARACTERISTIC_PDUS, raw_pdu(0x04, 0x01, 0x00, 0x10, 0x00), raw_pdu(0x05, 0x01, 0x04, 0x00, 0x00, 0x29), raw_pdu(0x04, 0x05, 0x00, 0x10, 0x00), raw_pdu(0x01, 0x04, 0x05, 0x00, 0x0a), raw_pdu(0x0a, 0x04, 0x00), raw_pdu(0x01, 0x0a, 0x04, 0x00, 0x08), end_pdu }; static struct iovec write_characteristic_1[] = { SEARCH_SERVICE_SINGLE_SUCCESS_PDUS, raw_pdu(0x08, 0x01, 0x00, 0x10, 0x00, 0x03, 0x28), raw_pdu(0x09, 0x07, 0x02, 0x00, 0x04, 0x03, 0x00, 0x19, 0x00), raw_pdu(0x08, 0x03, 0x00, 0x10, 0x00, 0x03, 0x28), raw_pdu(0x01, 0x08, 0x03, 0x00, 0x0a), raw_pdu(0x52, 0x03, 0x00, 0x00, 0x01, 0x02, 0x03), end_pdu }; static struct iovec write_characteristic_2[] = { SEARCH_SERVICE_SINGLE_SUCCESS_PDUS, raw_pdu(0x08, 0x01, 0x00, 0x10, 0x00, 0x03, 0x28), raw_pdu(0x09, 0x07, 0x02, 0x00, 0x04, 0x03, 0x00, 0x19, 0x00), raw_pdu(0x08, 0x03, 0x00, 0x10, 0x00, 0x03, 0x28), raw_pdu(0x01, 0x08, 0x03, 0x00, 0x0a), raw_pdu(0x12, 0x03, 0x00, 0x00, 0x01, 0x02, 0x03), raw_pdu(0x13), end_pdu }; static struct iovec write_characteristic_3[] = { SEARCH_SERVICE_SINGLE_SUCCESS_PDUS, raw_pdu(0x08, 0x01, 0x00, 0x10, 0x00, 0x03, 0x28), raw_pdu(0x09, 0x07, 0x02, 0x00, 0x04, 0x03, 0x00, 0x19, 0x00), raw_pdu(0x08, 0x03, 0x00, 0x10, 0x00, 0x03, 0x28), raw_pdu(0x01, 0x08, 0x03, 0x00, 0x0a), raw_pdu(0x12, 0x03, 0x00, 0x00, 0x01, 0x02, 0x03), raw_pdu(0x01, 0x12, 0x03, 0x00, 0x08), end_pdu }; static struct iovec write_descriptor_1[] = { SEARCH_SERVICE_SINGLE_SUCCESS_PDUS, READ_BY_TYPE_SINGLE_CHARACTERISTIC_PDUS, raw_pdu(0x04, 0x01, 0x00, 0x10, 0x00), raw_pdu(0x05, 0x01, 0x04, 0x00, 0x00, 0x29), raw_pdu(0x04, 0x05, 0x00, 0x10, 0x00), raw_pdu(0x01, 0x04, 0x05, 0x00, 0x0a), raw_pdu(0x12, 0x04, 0x00, 0x00, 0x01, 0x02, 0x03), raw_pdu(0x13), end_pdu }; static struct iovec write_descriptor_2[] = { SEARCH_SERVICE_SINGLE_SUCCESS_PDUS, READ_BY_TYPE_SINGLE_CHARACTERISTIC_PDUS, raw_pdu(0x04, 0x01, 0x00, 0x10, 0x00), raw_pdu(0x05, 0x01, 0x04, 0x00, 0x00, 0x29), raw_pdu(0x04, 0x05, 0x00, 0x10, 0x00), raw_pdu(0x01, 0x04, 0x05, 0x00, 0x0a), raw_pdu(0x12, 0x04, 0x00, 0x00, 0x01, 0x02, 0x03), raw_pdu(0x01, 0x12, 0x04, 0x00, 0x08), end_pdu }; static struct iovec notification_1[] = { SEARCH_SERVICE_SINGLE_SUCCESS_PDUS, READ_BY_TYPE_SINGLE_CHARACTERISTIC_PDUS, end_pdu }; static struct iovec notification_2[] = { SEARCH_SERVICE_SINGLE_SUCCESS_PDUS, READ_BY_TYPE_SINGLE_CHARACTERISTIC_PDUS, raw_pdu(0x1d, 0x03, 0x00, 0x01), raw_pdu(0x1e), end_pdu }; static struct iovec notification_3[] = { SEARCH_SERVICE_SINGLE_SUCCESS_PDUS, READ_BY_TYPE_SINGLE_CHARACTERISTIC_PDUS, raw_pdu(0x1b, 0x03, 0x00, 0x01), end_pdu }; static struct iovec send_indication_1[] = { raw_pdu(0x1d, 0x01, 0x00, 0x00, 0x01, 0x02, 0x03), raw_pdu(0x1e), end_pdu }; static struct iovec send_notification_1[] = { raw_pdu(0x1b, 0x01, 0x00, 0x00, 0x01, 0x02, 0x03), end_pdu }; static struct iovec search_range_1[] = { raw_pdu(0x01, 0xff, 0xff, 0xff), end_pdu }; static struct iovec primary_type = raw_pdu(0x00, 0x28); /* att commands define raw pdus */ static struct iovec att_read_req_op_v = raw_pdu(L2CAP_ATT_READ_REQ); static struct iovec att_write_req_op_v = raw_pdu(L2CAP_ATT_WRITE_REQ); static struct iovec att_find_by_type_req_op_v = raw_pdu(L2CAP_ATT_FIND_BY_TYPE_REQ); static struct iovec svc_change_ccc_handle_v = raw_pdu(0x1c, 0x00); static struct iovec svc_change_ccc_value_v = raw_pdu(0x00, 0x01); static void gatt_client_register_action(void) { struct test_data *data = tester_get_data(); struct step *current_data_step = queue_peek_head(data->steps); bt_uuid_t *app_uuid = current_data_step->set_data; struct step *step = g_new0(struct step, 1); if (!app_uuid) { tester_warn("No app uuid provided for register action."); return; } step->action_status = data->if_gatt->client->register_client(app_uuid); schedule_action_verification(step); } static void gatt_client_unregister_action(void) { struct test_data *data = tester_get_data(); struct step *current_data_step = queue_peek_head(data->steps); int32_t cl_id = PTR_TO_INT(current_data_step->set_data); struct step *step = g_new0(struct step, 1); step->action_status = data->if_gatt->client->unregister_client(cl_id); schedule_action_verification(step); } static void gatt_client_start_scan_action(void) { struct test_data *data = tester_get_data(); struct step *step = g_new0(struct step, 1); step->action_status = data->if_gatt->client->scan(TRUE); schedule_action_verification(step); } static void gatt_client_stop_scan_action(void) { struct test_data *data = tester_get_data(); struct step *step = g_new0(struct step, 1); step->action_status = data->if_gatt->client->scan(FALSE); schedule_action_verification(step); } static void gatt_client_connect_action(void) { struct test_data *data = tester_get_data(); struct step *current_data_step = queue_peek_head(data->steps); struct gatt_connect_data *conn_data = current_data_step->set_data; struct step *step = g_new0(struct step, 1); step->action_status = data->if_gatt->client->connect( conn_data->app_id, &emu_remote_bdaddr_val, 0, BT_TRANSPORT_UNKNOWN); schedule_action_verification(step); } static void gatt_client_disconnect_action(void) { struct test_data *data = tester_get_data(); struct step *current_data_step = queue_peek_head(data->steps); struct gatt_connect_data *conn_data = current_data_step->set_data; struct step *step = g_new0(struct step, 1); step->action_status = data->if_gatt->client->disconnect( conn_data->app_id, &emu_remote_bdaddr_val, conn_data->conn_id); schedule_action_verification(step); } static void gatt_client_do_listen_action(void) { struct test_data *data = tester_get_data(); struct step *current_data_step = queue_peek_head(data->steps); struct gatt_connect_data *conn_data = current_data_step->set_data; struct step *step = g_new0(struct step, 1); step->action_status = data->if_gatt->client->listen( conn_data->app_id, 1); schedule_action_verification(step); } static void gatt_client_stop_listen_action(void) { struct test_data *data = tester_get_data(); struct step *current_data_step = queue_peek_head(data->steps); struct gatt_connect_data *conn_data = current_data_step->set_data; struct step *step = g_new0(struct step, 1); step->action_status = data->if_gatt->client->listen( conn_data->app_id, 0); schedule_action_verification(step); } static void gatt_client_get_characteristic_action(void) { struct test_data *data = tester_get_data(); struct step *current_data_step = queue_peek_head(data->steps); struct get_char_data *get_char = current_data_step->set_data; const btgatt_client_interface_t *client = data->if_gatt->client; struct step *step = g_new0(struct step, 1); int status; status = client->get_characteristic(get_char->conn_id, get_char->service, NULL); step->action_status = status; schedule_action_verification(step); } static void gatt_client_get_descriptor_action(void) { struct test_data *data = tester_get_data(); struct step *current_data_step = queue_peek_head(data->steps); struct get_desc_data *get_desc = current_data_step->set_data; const btgatt_client_interface_t *client = data->if_gatt->client; struct step *step = g_new0(struct step, 1); int status; status = client->get_descriptor(get_desc->conn_id, get_desc->service, get_desc->characteristic, get_desc->desc); step->action_status = status; schedule_action_verification(step); } static void gatt_client_get_included_action(void) { struct test_data *data = tester_get_data(); struct step *current_data_step = queue_peek_head(data->steps); struct get_incl_data *get_incl = current_data_step->set_data; const btgatt_client_interface_t *client = data->if_gatt->client; struct step *step = g_new0(struct step, 1); int status; status = client->get_included_service(get_incl->conn_id, get_incl->service, get_incl->start_service); step->action_status = status; schedule_action_verification(step); } static void gatt_client_read_characteristic_action(void) { struct test_data *data = tester_get_data(); struct step *current_data_step = queue_peek_head(data->steps); struct read_char_data *read_char_data = current_data_step->set_data; const btgatt_client_interface_t *client = data->if_gatt->client; struct step *step = g_new0(struct step, 1); int status; status = client->read_characteristic(read_char_data->conn_id, read_char_data->service, read_char_data->characteristic, read_char_data->auth_req); step->action_status = status; schedule_action_verification(step); } static void gatt_client_read_descriptor_action(void) { struct test_data *data = tester_get_data(); struct step *current_data_step = queue_peek_head(data->steps); struct read_desc_data *read_desc_data = current_data_step->set_data; const btgatt_client_interface_t *client = data->if_gatt->client; struct step *step = g_new0(struct step, 1); int status; status = client->read_descriptor(read_desc_data->conn_id, read_desc_data->service, read_desc_data->characteristic, read_desc_data->descriptor, read_desc_data->auth_req); step->action_status = status; schedule_action_verification(step); } static void gatt_client_write_characteristic_action(void) { struct test_data *data = tester_get_data(); struct step *current_data_step = queue_peek_head(data->steps); struct write_char_data *write_char_data = current_data_step->set_data; const btgatt_client_interface_t *client = data->if_gatt->client; struct step *step = g_new0(struct step, 1); int status; status = client->write_characteristic(write_char_data->conn_id, write_char_data->service, write_char_data->characteristic, write_char_data->write_type, write_char_data->len, write_char_data->auth_req, write_char_data->p_value); step->action_status = status; schedule_action_verification(step); } static void gatt_client_register_for_notification_action(void) { struct test_data *data = tester_get_data(); struct step *current_data_step = queue_peek_head(data->steps); struct notif_data *notif_data = current_data_step->set_data; const btgatt_client_interface_t *client = data->if_gatt->client; struct step *step = g_new0(struct step, 1); int status; status = client->register_for_notification(notif_data->conn_id, notif_data->bdaddr, notif_data->service, notif_data->charac); step->action_status = status; schedule_action_verification(step); } static void gatt_client_deregister_for_notification_action(void) { struct test_data *data = tester_get_data(); struct step *current_data_step = queue_peek_head(data->steps); struct notif_data *notif_data = current_data_step->set_data; const btgatt_client_interface_t *client = data->if_gatt->client; struct step *step = g_new0(struct step, 1); int status; status = client->deregister_for_notification(notif_data->conn_id, notif_data->bdaddr, notif_data->service, notif_data->charac); step->action_status = status; schedule_action_verification(step); } static void gatt_server_register_action(void) { struct test_data *data = tester_get_data(); struct step *current_data_step = queue_peek_head(data->steps); bt_uuid_t *app_uuid = current_data_step->set_data; struct step *step = g_new0(struct step, 1); if (!app_uuid) { tester_warn("No app uuid provided for register action."); return; } step->action_status = data->if_gatt->server->register_server(app_uuid); schedule_action_verification(step); } static void gatt_server_unregister_action(void) { struct test_data *data = tester_get_data(); struct step *current_data_step = queue_peek_head(data->steps); int32_t sr_id = PTR_TO_INT(current_data_step->set_data); struct step *step = g_new0(struct step, 1); step->action_status = data->if_gatt->server->unregister_server(sr_id); schedule_action_verification(step); } static void gatt_server_connect_action(void) { struct test_data *data = tester_get_data(); struct step *current_data_step = queue_peek_head(data->steps); struct gatt_connect_data *conn_data = current_data_step->set_data; struct step *step = g_new0(struct step, 1); step->action_status = data->if_gatt->server->connect( conn_data->app_id, &emu_remote_bdaddr_val, 0, BT_TRANSPORT_UNKNOWN); schedule_action_verification(step); } static void gatt_server_disconnect_action(void) { struct test_data *data = tester_get_data(); struct step *current_data_step = queue_peek_head(data->steps); struct gatt_connect_data *conn_data = current_data_step->set_data; struct step *step = g_new0(struct step, 1); step->action_status = data->if_gatt->server->disconnect( conn_data->app_id, &emu_remote_bdaddr_val, conn_data->conn_id); schedule_action_verification(step); } static void gatt_server_add_service_action(void) { struct test_data *data = tester_get_data(); struct step *current_data_step = queue_peek_head(data->steps); struct add_service_data *add_srvc_data = current_data_step->set_data; struct step *step = g_new0(struct step, 1); step->action_status = data->if_gatt->server->add_service( add_srvc_data->app_id, add_srvc_data->service, add_srvc_data->num_handles); schedule_action_verification(step); } static void gatt_server_add_inc_service_action(void) { struct test_data *data = tester_get_data(); struct step *current_data_step = queue_peek_head(data->steps); struct add_included_service_data *add_inc_srvc_data = current_data_step->set_data; struct step *step = g_new0(struct step, 1); step->action_status = data->if_gatt->server->add_included_service( add_inc_srvc_data->app_id, *add_inc_srvc_data->srvc_handle, *add_inc_srvc_data->inc_srvc_handle); schedule_action_verification(step); } static void gatt_server_add_char_action(void) { struct test_data *data = tester_get_data(); struct step *current_data_step = queue_peek_head(data->steps); struct add_char_data *add_char_data = current_data_step->set_data; struct step *step = g_new0(struct step, 1); step->action_status = data->if_gatt->server->add_characteristic( add_char_data->app_id, *add_char_data->srvc_handle, add_char_data->uuid, add_char_data->properties, add_char_data->permissions); schedule_action_verification(step); } static void gatt_server_add_desc_action(void) { struct test_data *data = tester_get_data(); struct step *current_data_step = queue_peek_head(data->steps); struct add_desc_data *add_desc_data = current_data_step->set_data; struct step *step = g_new0(struct step, 1); step->action_status = data->if_gatt->server->add_descriptor( add_desc_data->app_id, *add_desc_data->srvc_handle, add_desc_data->uuid, add_desc_data->permissions); schedule_action_verification(step); } static void gatt_client_write_descriptor_action(void) { struct test_data *data = tester_get_data(); struct step *current_data_step = queue_peek_head(data->steps); struct write_desc_data *write_desc_data = current_data_step->set_data; const btgatt_client_interface_t *client = data->if_gatt->client; struct step *step = g_new0(struct step, 1); int status; status = client->write_descriptor(write_desc_data->conn_id, write_desc_data->service, write_desc_data->characteristic, write_desc_data->descriptor, write_desc_data->write_type, write_desc_data->len, write_desc_data->auth_req, write_desc_data->p_value); step->action_status = status; schedule_action_verification(step); } static void gatt_server_start_srvc_action(void) { struct test_data *data = tester_get_data(); struct step *current_data_step = queue_peek_head(data->steps); struct start_srvc_data *start_srvc_data = current_data_step->set_data; struct step *step = g_new0(struct step, 1); step->action_status = data->if_gatt->server->start_service( start_srvc_data->app_id, *start_srvc_data->srvc_handle, start_srvc_data->transport); schedule_action_verification(step); } static void gatt_server_stop_srvc_action(void) { struct test_data *data = tester_get_data(); struct step *current_data_step = queue_peek_head(data->steps); struct stop_srvc_data *stop_srvc_data = current_data_step->set_data; struct step *step = g_new0(struct step, 1); step->action_status = data->if_gatt->server->stop_service( stop_srvc_data->app_id, *stop_srvc_data->srvc_handle); schedule_action_verification(step); } static void gatt_server_delete_srvc_action(void) { struct test_data *data = tester_get_data(); struct step *current_data_step = queue_peek_head(data->steps); struct delete_srvc_data *delete_srvc_data = current_data_step->set_data; struct step *step = g_new0(struct step, 1); step->action_status = data->if_gatt->server->delete_service( delete_srvc_data->app_id, *delete_srvc_data->srvc_handle); schedule_action_verification(step); } static void gatt_server_send_indication_action(void) { struct test_data *data = tester_get_data(); struct step *current_data_step = queue_peek_head(data->steps); struct send_indication_data *send_indication_data = current_data_step->set_data; struct step *step = g_new0(struct step, 1); step->action_status = data->if_gatt->server->send_indication( send_indication_data->app_id, *send_indication_data->attr_handle, send_indication_data->conn_id, send_indication_data->len, send_indication_data->confirm, send_indication_data->p_value); schedule_action_verification(step); } static void gatt_server_send_response_action(void) { struct test_data *data = tester_get_data(); struct step *current_data_step = queue_peek_head(data->steps); struct send_resp_data *send_resp_data = current_data_step->set_data; struct step *step = g_new0(struct step, 1); step->action_status = data->if_gatt->server->send_response( send_resp_data->conn_id, send_resp_data->trans_id, send_resp_data->status, send_resp_data->response); schedule_action_verification(step); } static void gatt_cid_hook_cb(const void *data, uint16_t len, void *user_data) { struct test_data *t_data = tester_get_data(); struct bthost *bthost = hciemu_client_get_host(t_data->hciemu); struct emu_l2cap_cid_data *cid_data = user_data; const uint8_t *pdu = data; struct iovec *gatt_pdu = queue_peek_head(t_data->pdus); struct step *step; tester_debug("Received att pdu with opcode 0x%02x", pdu[0]); switch (pdu[0]) { case L2CAP_ATT_ERROR: step = g_new0(struct step, 1); step->callback = CB_EMU_ATT_ERROR; step->callback_result.error = pdu[4]; schedule_callback_verification(step); break; case L2CAP_ATT_EXCHANGE_MTU_REQ: tester_print("Exchange MTU request received."); if (!memcmp(exchange_mtu_req_pdu.iov_base, pdu, len)) bthost_send_cid_v(bthost, cid_data->handle, cid_data->cid, &exchange_mtu_resp_pdu, 1); break; case L2CAP_ATT_EXCHANGE_MTU_RSP: tester_print("Exchange MTU response received."); break; case L2CAP_ATT_HANDLE_VALUE_IND: step = g_new0(struct step, 1); step->callback = CB_EMU_VALUE_INDICATION; schedule_callback_verification(step); goto respond; case L2CAP_ATT_HANDLE_VALUE_NOTIFY: step = g_new0(struct step, 1); step->callback = CB_EMU_VALUE_NOTIFICATION; schedule_callback_verification(step); break; case L2CAP_ATT_READ_RSP: /* TODO - More complicated cases should also verify pdu data */ step = g_new0(struct step, 1); step->callback = CB_EMU_READ_RESPONSE; schedule_callback_verification(step); break; case L2CAP_ATT_WRITE_RSP: /* TODO - More complicated cases should also verify pdu data */ step = g_new0(struct step, 1); step->callback = CB_EMU_WRITE_RESPONSE; schedule_callback_verification(step); break; default: if (!gatt_pdu || !gatt_pdu->iov_base) { tester_print("Unknown ATT packet."); break; } if (gatt_pdu->iov_len != len) { tester_print("Size of incoming frame is not valid"); tester_print("Expected size = %zd incoming size = %d", gatt_pdu->iov_len, len); break; } respond: if (memcmp(gatt_pdu->iov_base, data, len)) { tester_print("Incoming data mismatch"); break; } queue_pop_head(t_data->pdus); gatt_pdu = queue_pop_head(t_data->pdus); if (!gatt_pdu || !gatt_pdu->iov_base) break; bthost_send_cid_v(bthost, cid_data->handle, cid_data->cid, gatt_pdu, 1); break; } } static void gatt_remote_send_frame_action(void) { struct test_data *t_data = tester_get_data(); struct bthost *bthost = hciemu_client_get_host(t_data->hciemu); struct iovec *gatt_pdu = queue_pop_head(t_data->pdus); struct step *step = g_new0(struct step, 1); if (!gatt_pdu) { tester_print("No frame to send"); step->action_status = BT_STATUS_FAIL; } else { bthost_send_cid_v(bthost, cid_data.handle, cid_data.cid, gatt_pdu, 1); step->action_status = BT_STATUS_SUCCESS; } schedule_action_verification(step); } static void gatt_remote_send_raw_pdu_action(void) { struct test_data *data = tester_get_data(); struct bthost *bthost = hciemu_client_get_host(data->hciemu); struct step *current_data_step = queue_peek_head(data->steps); struct iovec *pdu = current_data_step->set_data; struct iovec *pdu2 = current_data_step->set_data_2; struct iovec *pdu3 = current_data_step->set_data_3; struct step *step = g_new0(struct step, 1); if (cid_data.handle && cid_data.cid) { struct iovec rsp[3]; size_t len = 0; if (!pdu) { step->action_status = BT_STATUS_FAIL; goto done; } rsp[0].iov_base = pdu->iov_base; rsp[0].iov_len = pdu->iov_len; len++; if (pdu2) { rsp[1].iov_base = pdu2->iov_base; rsp[1].iov_len = pdu2->iov_len; len++; } if (pdu3) { rsp[2].iov_base = pdu3->iov_base; rsp[2].iov_len = pdu3->iov_len; len++; } bthost_send_cid_v(bthost, cid_data.handle, cid_data.cid, rsp, len); step->action_status = BT_STATUS_SUCCESS; } else { tester_debug("No connection set up"); step->action_status = BT_STATUS_FAIL; } done: schedule_action_verification(step); } static void gatt_conn_cb(uint16_t handle, void *user_data) { struct test_data *data = tester_get_data(); struct bthost *bthost = hciemu_client_get_host(data->hciemu); tester_print("New connection with handle 0x%04x", handle); if (data->hciemu_type == HCIEMU_TYPE_BREDR) { tester_warn("Not handled device type."); return; } cid_data.cid = 0x0004; cid_data.handle = handle; bthost_add_cid_hook(bthost, handle, cid_data.cid, gatt_cid_hook_cb, &cid_data); } static void gatt_client_search_services(void) { struct test_data *data = tester_get_data(); struct step *current_data_step = queue_peek_head(data->steps); struct step *step = g_new0(struct step, 1); struct gatt_search_service_data *search_data; int status; search_data = current_data_step->set_data; status = data->if_gatt->client->search_service(search_data->conn_id, search_data->filter_uuid); step->action_status = status; schedule_action_verification(step); } static void init_pdus(void) { struct test_data *data = tester_get_data(); struct step *current_data_step = queue_peek_head(data->steps); struct step *step = g_new0(struct step, 1); struct iovec *pdu = current_data_step->set_data; while (pdu->iov_base) { queue_push_tail(data->pdus, pdu); pdu++; } step->action_status = BT_STATUS_SUCCESS; schedule_action_verification(step); } static void init_read_params_action(void) { struct test_data *data = tester_get_data(); struct step *current_data_step = queue_peek_head(data->steps); struct step *step = g_new0(struct step, 1); struct set_read_params *set_param_data = current_data_step->set_data; btgatt_read_params_t *param = set_param_data->params; memset(param, 0, sizeof(*param)); if (set_param_data->srvc_id) memcpy(¶m->srvc_id, set_param_data->srvc_id, sizeof(btgatt_srvc_id_t)); if (set_param_data->char_id) memcpy(¶m->char_id, set_param_data->char_id, sizeof(btgatt_gatt_id_t)); if (set_param_data->descr_id) memcpy(¶m->descr_id, set_param_data->descr_id, sizeof(btgatt_gatt_id_t)); param->value_type = set_param_data->value_type; param->status = set_param_data->status; param->value.len = set_param_data->len; if (param->value.len != 0) memcpy(¶m->value.value, set_param_data->value, param->value.len); step->action_status = BT_STATUS_SUCCESS; schedule_action_verification(step); } static void init_write_params_action(void) { struct test_data *data = tester_get_data(); struct step *current_data_step = queue_peek_head(data->steps); struct step *step = g_new0(struct step, 1); struct set_write_params *set_param_data = current_data_step->set_data; btgatt_write_params_t *param = set_param_data->params; memset(param, 0, sizeof(*param)); if (set_param_data->srvc_id) memcpy(¶m->srvc_id, set_param_data->srvc_id, sizeof(btgatt_srvc_id_t)); if (set_param_data->char_id) memcpy(¶m->char_id, set_param_data->char_id, sizeof(btgatt_gatt_id_t)); if (set_param_data->descr_id) memcpy(¶m->descr_id, set_param_data->descr_id, sizeof(btgatt_gatt_id_t)); param->status = set_param_data->status; step->action_status = BT_STATUS_SUCCESS; schedule_action_verification(step); } static void init_notify_params_action(void) { struct test_data *data = tester_get_data(); struct step *current_data_step = queue_peek_head(data->steps); struct step *step = g_new0(struct step, 1); struct set_notify_params *set_param_data = current_data_step->set_data; btgatt_notify_params_t *param = set_param_data->params; memset(param, 0, sizeof(*param)); if (set_param_data->srvc_id) memcpy(¶m->srvc_id, set_param_data->srvc_id, sizeof(btgatt_srvc_id_t)); if (set_param_data->char_id) memcpy(¶m->char_id, set_param_data->char_id, sizeof(btgatt_gatt_id_t)); param->len = set_param_data->len; param->is_notify = set_param_data->is_notify; memcpy(¶m->bda, set_param_data->bdaddr, sizeof(bt_bdaddr_t)); if (param->len != 0) memcpy(¶m->value, set_param_data->value, param->len); step->action_status = BT_STATUS_SUCCESS; schedule_action_verification(step); } static void trigger_device_found(void *user_data) { emu_setup_powered_remote_action(); } static void delayemu_setup_powered_remote_action(void) { /* Make sure discovery is enabled before enabling advertising. * Unfortunately GATT HAL doesn't have discovering callback like * Bluetooth HAL so we need to delay */ tester_wait(1, trigger_device_found, NULL); } static struct test_case test_cases[] = { TEST_CASE_BREDRLE("Gatt Init", ACTION_SUCCESS(dummy_action, NULL), ), TEST_CASE_BREDRLE("Gatt Client - Register", ACTION_SUCCESS(gatt_client_register_action, &app1_uuid), CALLBACK_STATUS(CB_GATTC_REGISTER_CLIENT, BT_STATUS_SUCCESS), ), TEST_CASE_BREDRLE("Gatt Client - Unregister", ACTION_SUCCESS(gatt_client_register_action, &app1_uuid), CALLBACK_STATUS(CB_GATTC_REGISTER_CLIENT, BT_STATUS_SUCCESS), ACTION_SUCCESS(gatt_client_unregister_action, INT_TO_PTR(APP1_ID)), ACTION_SUCCESS(gatt_client_register_action, &app1_uuid), CALLBACK_STATUS(CB_GATTC_REGISTER_CLIENT, BT_STATUS_SUCCESS), ), TEST_CASE_BREDRLE("Gatt Client - Scan", ACTION_SUCCESS(bluetooth_enable_action, NULL), CALLBACK_STATE(CB_BT_ADAPTER_STATE_CHANGED, BT_STATE_ON), ACTION_SUCCESS(emu_set_ssp_mode_action, NULL), ACTION_SUCCESS(gatt_client_register_action, &app1_uuid), CALLBACK_STATUS(CB_GATTC_REGISTER_CLIENT, BT_STATUS_SUCCESS), ACTION_SUCCESS(gatt_client_start_scan_action, NULL), ACTION_SUCCESS(delayemu_setup_powered_remote_action, NULL), CALLBACK_DEVICE_FOUND(prop_emu_remotes_default_le_set, 2), CLLBACK_GATTC_SCAN_RES(prop_emu_remotes_default_set, 1, TRUE), ACTION_SUCCESS(gatt_client_stop_scan_action, NULL), ACTION_SUCCESS(bluetooth_disable_action, NULL), CALLBACK_STATE(CB_BT_ADAPTER_STATE_CHANGED, BT_STATE_OFF), ), TEST_CASE_BREDRLE("Gatt Client - LE Connect", ACTION_SUCCESS(bluetooth_enable_action, NULL), CALLBACK_STATE(CB_BT_ADAPTER_STATE_CHANGED, BT_STATE_ON), ACTION_SUCCESS(emu_set_ssp_mode_action, NULL), ACTION_SUCCESS(emu_set_connect_cb_action, gatt_conn_cb), ACTION_SUCCESS(gatt_client_register_action, &app1_uuid), CALLBACK_STATUS(CB_GATTC_REGISTER_CLIENT, BT_STATUS_SUCCESS), ACTION_SUCCESS(gatt_client_start_scan_action, NULL), ACTION_SUCCESS(delayemu_setup_powered_remote_action, NULL), CALLBACK_DEVICE_FOUND(prop_emu_remotes_default_le_set, 2), CLLBACK_GATTC_SCAN_RES(prop_emu_remotes_default_set, 1, TRUE), ACTION_SUCCESS(gatt_client_stop_scan_action, NULL), ACTION_SUCCESS(gatt_client_connect_action, &app1_conn_req), CALLBACK_GATTC_CONNECT(GATT_STATUS_SUCCESS, prop_emu_remotes_default_set, CONN1_ID, APP1_ID), ACTION_SUCCESS(bluetooth_disable_action, NULL), CALLBACK_STATE(CB_BT_ADAPTER_STATE_CHANGED, BT_STATE_OFF), ), TEST_CASE_BREDRLE("Gatt Client - LE Disconnect", ACTION_SUCCESS(bluetooth_enable_action, NULL), CALLBACK_STATE(CB_BT_ADAPTER_STATE_CHANGED, BT_STATE_ON), ACTION_SUCCESS(emu_set_ssp_mode_action, NULL), ACTION_SUCCESS(emu_set_connect_cb_action, gatt_conn_cb), ACTION_SUCCESS(gatt_client_register_action, &app1_uuid), CALLBACK_STATUS(CB_GATTC_REGISTER_CLIENT, BT_STATUS_SUCCESS), ACTION_SUCCESS(gatt_client_start_scan_action, NULL), ACTION_SUCCESS(delayemu_setup_powered_remote_action, NULL), CALLBACK_DEVICE_FOUND(prop_emu_remotes_default_le_set, 2), CLLBACK_GATTC_SCAN_RES(prop_emu_remotes_default_set, 1, TRUE), ACTION_SUCCESS(gatt_client_stop_scan_action, NULL), ACTION_SUCCESS(gatt_client_connect_action, &app1_conn_req), CALLBACK_GATTC_CONNECT(GATT_STATUS_SUCCESS, prop_emu_remotes_default_set, CONN1_ID, APP1_ID), ACTION_SUCCESS(gatt_client_disconnect_action, &app1_conn_req), CALLBACK_GATTC_DISCONNECT(GATT_STATUS_SUCCESS, prop_emu_remotes_default_set, CONN1_ID, APP1_ID), ACTION_SUCCESS(bluetooth_disable_action, NULL), CALLBACK_STATE(CB_BT_ADAPTER_STATE_CHANGED, BT_STATE_OFF), ), TEST_CASE_BREDRLE("Gatt Client - LE Multiple Client Conn./Disc.", ACTION_SUCCESS(bluetooth_enable_action, NULL), CALLBACK_STATE(CB_BT_ADAPTER_STATE_CHANGED, BT_STATE_ON), ACTION_SUCCESS(emu_set_ssp_mode_action, NULL), ACTION_SUCCESS(emu_set_connect_cb_action, gatt_conn_cb), ACTION_SUCCESS(gatt_client_register_action, &app1_uuid), CALLBACK_STATUS(CB_GATTC_REGISTER_CLIENT, BT_STATUS_SUCCESS), ACTION_SUCCESS(gatt_client_register_action, &app2_uuid), CALLBACK_STATUS(CB_GATTC_REGISTER_CLIENT, BT_STATUS_SUCCESS), ACTION_SUCCESS(gatt_client_start_scan_action, NULL), ACTION_SUCCESS(delayemu_setup_powered_remote_action, NULL), CLLBACK_GATTC_SCAN_RES(prop_emu_remotes_default_set, 1, TRUE), ACTION_SUCCESS(gatt_client_stop_scan_action, NULL), ACTION_SUCCESS(gatt_client_connect_action, &app1_conn_req), CALLBACK_GATTC_CONNECT(GATT_STATUS_SUCCESS, prop_emu_remotes_default_set, CONN1_ID, APP1_ID), ACTION_SUCCESS(gatt_client_connect_action, &app2_conn_req), CALLBACK_GATTC_CONNECT(GATT_STATUS_SUCCESS, prop_emu_remotes_default_set, CONN2_ID, APP2_ID), ACTION_SUCCESS(gatt_client_disconnect_action, &app2_conn_req), CALLBACK_GATTC_DISCONNECT(GATT_STATUS_SUCCESS, prop_emu_remotes_default_set, CONN2_ID, APP2_ID), ACTION_SUCCESS(gatt_client_disconnect_action, &app1_conn_req), CALLBACK_GATTC_DISCONNECT(GATT_STATUS_SUCCESS, prop_emu_remotes_default_set, CONN1_ID, APP1_ID), ACTION_SUCCESS(bluetooth_disable_action, NULL), CALLBACK_STATE(CB_BT_ADAPTER_STATE_CHANGED, BT_STATE_OFF), ), TEST_CASE_BREDRLE("Gatt Client - Listen and Disconnect", ACTION_SUCCESS(bluetooth_enable_action, NULL), CALLBACK_STATE(CB_BT_ADAPTER_STATE_CHANGED, BT_STATE_ON), ACTION_SUCCESS(delayemu_setup_powered_remote_action, NULL), ACTION_SUCCESS(emu_set_ssp_mode_action, NULL), ACTION_SUCCESS(emu_set_connect_cb_action, gatt_conn_cb), ACTION_SUCCESS(bt_set_property_action, &prop_test_scan_mode_conn), CALLBACK_ADAPTER_PROPS(&prop_test_scan_mode_conn, 1), ACTION_SUCCESS(gatt_client_register_action, &app1_uuid), CALLBACK_STATUS(CB_GATTC_REGISTER_CLIENT, BT_STATUS_SUCCESS), ACTION_SUCCESS(gatt_client_do_listen_action, &app1_conn_req), CALLBACK_STATUS(CB_GATTC_LISTEN, GATT_STATUS_SUCCESS), ACTION_SUCCESS(emu_remote_connect_hci_action, &bearer_type), CALLBACK_GATTC_CONNECT(GATT_STATUS_SUCCESS, prop_emu_remotes_default_set, CONN1_ID, APP1_ID), ACTION_SUCCESS(gatt_client_stop_listen_action, &app1_conn_req), CALLBACK_STATUS(CB_GATTC_LISTEN, GATT_STATUS_SUCCESS), ACTION_SUCCESS(gatt_client_disconnect_action, &app1_conn_req), CALLBACK_GATTC_DISCONNECT(GATT_STATUS_SUCCESS, prop_emu_remotes_default_set, CONN1_ID, APP1_ID), ACTION_SUCCESS(bluetooth_disable_action, NULL), CALLBACK_STATE(CB_BT_ADAPTER_STATE_CHANGED, BT_STATE_OFF), ), TEST_CASE_BREDRLE("Gatt Client - Double Listen", ACTION_SUCCESS(bluetooth_enable_action, NULL), CALLBACK_STATE(CB_BT_ADAPTER_STATE_CHANGED, BT_STATE_ON), ACTION_SUCCESS(delayemu_setup_powered_remote_action, NULL), ACTION_SUCCESS(emu_set_ssp_mode_action, NULL), ACTION_SUCCESS(emu_set_connect_cb_action, gatt_conn_cb), ACTION_SUCCESS(bt_set_property_action, &prop_test_scan_mode_conn), CALLBACK_ADAPTER_PROPS(&prop_test_scan_mode_conn, 1), ACTION_SUCCESS(gatt_client_register_action, &app1_uuid), CALLBACK_STATUS(CB_GATTC_REGISTER_CLIENT, BT_STATUS_SUCCESS), ACTION_SUCCESS(gatt_client_do_listen_action, &app1_conn_req), CALLBACK_STATUS(CB_GATTC_LISTEN, GATT_STATUS_SUCCESS), ACTION_SUCCESS(emu_remote_connect_hci_action, &bearer_type), CALLBACK_GATTC_CONNECT(GATT_STATUS_SUCCESS, prop_emu_remotes_default_set, CONN1_ID, APP1_ID), ACTION_SUCCESS(gatt_client_stop_listen_action, &app1_conn_req), CALLBACK_STATUS(CB_GATTC_LISTEN, GATT_STATUS_SUCCESS), ACTION_SUCCESS(gatt_client_disconnect_action, &app1_conn_req), CALLBACK_GATTC_DISCONNECT(GATT_STATUS_SUCCESS, prop_emu_remotes_default_set, CONN1_ID, APP1_ID), /* Close ACL on emulated remotes side so it can reconnect */ ACTION_SUCCESS(emu_remote_disconnect_hci_action, &cid_data.handle), CALLBACK_STATE(CB_BT_ACL_STATE_CHANGED, BT_ACL_STATE_DISCONNECTED), ACTION_SUCCESS(gatt_client_do_listen_action, &app1_conn_req), CALLBACK_STATUS(CB_GATTC_LISTEN, GATT_STATUS_SUCCESS), ACTION_SUCCESS(emu_remote_connect_hci_action, &bearer_type), CALLBACK_GATTC_CONNECT(GATT_STATUS_SUCCESS, prop_emu_remotes_default_set, CONN2_ID, APP1_ID), ACTION_SUCCESS(gatt_client_disconnect_action, &app1_conn2_req), CALLBACK_GATTC_DISCONNECT(GATT_STATUS_SUCCESS, prop_emu_remotes_default_set, CONN2_ID, APP1_ID), ACTION_SUCCESS(gatt_client_stop_listen_action, &app1_conn_req), CALLBACK_STATUS(CB_GATTC_LISTEN, GATT_STATUS_SUCCESS), ACTION_SUCCESS(bluetooth_disable_action, NULL), CALLBACK_STATE(CB_BT_ADAPTER_STATE_CHANGED, BT_STATE_OFF), ), TEST_CASE_BREDRLE("Gatt Client - Search Service - Single", ACTION_SUCCESS(init_pdus, search_service), ACTION_SUCCESS(bluetooth_enable_action, NULL), CALLBACK_STATE(CB_BT_ADAPTER_STATE_CHANGED, BT_STATE_ON), ACTION_SUCCESS(emu_set_ssp_mode_action, NULL), ACTION_SUCCESS(emu_set_connect_cb_action, gatt_conn_cb), ACTION_SUCCESS(gatt_client_register_action, &app1_uuid), CALLBACK_STATUS(CB_GATTC_REGISTER_CLIENT, BT_STATUS_SUCCESS), ACTION_SUCCESS(gatt_client_start_scan_action, NULL), ACTION_SUCCESS(delayemu_setup_powered_remote_action, NULL), CLLBACK_GATTC_SCAN_RES(prop_emu_remotes_default_set, 1, TRUE), ACTION_SUCCESS(gatt_client_stop_scan_action, NULL), ACTION_SUCCESS(gatt_client_connect_action, &app1_conn_req), CALLBACK_GATTC_CONNECT(GATT_STATUS_SUCCESS, prop_emu_remotes_default_set, CONN1_ID, APP1_ID), ACTION_SUCCESS(gatt_client_search_services, &search_services_1), CALLBACK_GATTC_SEARCH_RESULT(CONN1_ID, &service_1), CALLBACK_GATTC_SEARCH_COMPLETE(GATT_STATUS_SUCCESS, CONN1_ID), ACTION_SUCCESS(bluetooth_disable_action, NULL), CALLBACK_STATE(CB_BT_ADAPTER_STATE_CHANGED, BT_STATE_OFF), ), TEST_CASE_BREDRLE("Gatt Client - Search Service - Multiple", ACTION_SUCCESS(init_pdus, search_service_2), ACTION_SUCCESS(bluetooth_enable_action, NULL), CALLBACK_STATE(CB_BT_ADAPTER_STATE_CHANGED, BT_STATE_ON), ACTION_SUCCESS(emu_set_ssp_mode_action, NULL), ACTION_SUCCESS(emu_set_connect_cb_action, gatt_conn_cb), ACTION_SUCCESS(gatt_client_register_action, &app1_uuid), CALLBACK_STATUS(CB_GATTC_REGISTER_CLIENT, BT_STATUS_SUCCESS), ACTION_SUCCESS(gatt_client_start_scan_action, NULL), ACTION_SUCCESS(delayemu_setup_powered_remote_action, NULL), CLLBACK_GATTC_SCAN_RES(prop_emu_remotes_default_set, 1, TRUE), ACTION_SUCCESS(gatt_client_stop_scan_action, NULL), ACTION_SUCCESS(gatt_client_connect_action, &app1_conn_req), CALLBACK_GATTC_CONNECT(GATT_STATUS_SUCCESS, prop_emu_remotes_default_set, CONN1_ID, APP1_ID), ACTION_SUCCESS(gatt_client_search_services, &search_services_1), CALLBACK_GATTC_SEARCH_RESULT(CONN1_ID, &service_1), CALLBACK_GATTC_SEARCH_RESULT(CONN1_ID, &service_2), CALLBACK_GATTC_SEARCH_COMPLETE(GATT_STATUS_SUCCESS, CONN1_ID), ACTION_SUCCESS(bluetooth_disable_action, NULL), CALLBACK_STATE(CB_BT_ADAPTER_STATE_CHANGED, BT_STATE_OFF), ), TEST_CASE_BREDRLE("Gatt Client - Search Service - None", ACTION_SUCCESS(init_pdus, search_service_3), ACTION_SUCCESS(bluetooth_enable_action, NULL), CALLBACK_STATE(CB_BT_ADAPTER_STATE_CHANGED, BT_STATE_ON), ACTION_SUCCESS(emu_set_ssp_mode_action, NULL), ACTION_SUCCESS(emu_set_connect_cb_action, gatt_conn_cb), ACTION_SUCCESS(gatt_client_register_action, &app1_uuid), CALLBACK_STATUS(CB_GATTC_REGISTER_CLIENT, BT_STATUS_SUCCESS), ACTION_SUCCESS(gatt_client_start_scan_action, NULL), ACTION_SUCCESS(delayemu_setup_powered_remote_action, NULL), CLLBACK_GATTC_SCAN_RES(prop_emu_remotes_default_set, 1, TRUE), ACTION_SUCCESS(gatt_client_stop_scan_action, NULL), ACTION_SUCCESS(gatt_client_connect_action, &app1_conn_req), CALLBACK_GATTC_CONNECT(GATT_STATUS_SUCCESS, prop_emu_remotes_default_set, CONN1_ID, APP1_ID), ACTION_SUCCESS(gatt_client_search_services, &search_services_1), CALLBACK_GATTC_SEARCH_COMPLETE(GATT_STATUS_SUCCESS, CONN1_ID), ACTION_SUCCESS(bluetooth_disable_action, NULL), CALLBACK_STATE(CB_BT_ADAPTER_STATE_CHANGED, BT_STATE_OFF), ), TEST_CASE_BREDRLE("Gatt Client - Search Service - Incorrect rsp", ACTION_SUCCESS(init_pdus, search_service_4), ACTION_SUCCESS(bluetooth_enable_action, NULL), CALLBACK_STATE(CB_BT_ADAPTER_STATE_CHANGED, BT_STATE_ON), ACTION_SUCCESS(emu_set_ssp_mode_action, NULL), ACTION_SUCCESS(emu_set_connect_cb_action, gatt_conn_cb), ACTION_SUCCESS(gatt_client_register_action, &app1_uuid), CALLBACK_STATUS(CB_GATTC_REGISTER_CLIENT, BT_STATUS_SUCCESS), ACTION_SUCCESS(gatt_client_start_scan_action, NULL), ACTION_SUCCESS(delayemu_setup_powered_remote_action, NULL), CLLBACK_GATTC_SCAN_RES(prop_emu_remotes_default_set, 1, TRUE), ACTION_SUCCESS(gatt_client_stop_scan_action, NULL), ACTION_SUCCESS(gatt_client_connect_action, &app1_conn_req), CALLBACK_GATTC_CONNECT(GATT_STATUS_SUCCESS, prop_emu_remotes_default_set, CONN1_ID, APP1_ID), ACTION_SUCCESS(gatt_client_search_services, &search_services_1), CALLBACK_GATTC_SEARCH_COMPLETE(GATT_STATUS_SUCCESS, CONN1_ID), ACTION_SUCCESS(bluetooth_disable_action, NULL), CALLBACK_STATE(CB_BT_ADAPTER_STATE_CHANGED, BT_STATE_OFF), ), TEST_CASE_BREDRLE("Gatt Client - Get Characteristic - Single", ACTION_SUCCESS(init_pdus, get_characteristic_1), ACTION_SUCCESS(bluetooth_enable_action, NULL), CALLBACK_STATE(CB_BT_ADAPTER_STATE_CHANGED, BT_STATE_ON), ACTION_SUCCESS(emu_set_ssp_mode_action, NULL), ACTION_SUCCESS(emu_set_connect_cb_action, gatt_conn_cb), ACTION_SUCCESS(gatt_client_register_action, &app1_uuid), CALLBACK_STATUS(CB_GATTC_REGISTER_CLIENT, BT_STATUS_SUCCESS), ACTION_SUCCESS(gatt_client_start_scan_action, NULL), ACTION_SUCCESS(delayemu_setup_powered_remote_action, NULL), CLLBACK_GATTC_SCAN_RES(prop_emu_remotes_default_set, 1, TRUE), ACTION_SUCCESS(gatt_client_stop_scan_action, NULL), ACTION_SUCCESS(gatt_client_connect_action, &app1_conn_req), CALLBACK_GATTC_CONNECT(GATT_STATUS_SUCCESS, prop_emu_remotes_default_set, CONN1_ID, APP1_ID), ACTION_SUCCESS(gatt_client_search_services, &search_services_1), CALLBACK_GATTC_SEARCH_COMPLETE(GATT_STATUS_SUCCESS, CONN1_ID), ACTION_SUCCESS(gatt_client_get_characteristic_action, &get_char_data_1), CALLBACK_GATTC_GET_CHARACTERISTIC_CB(GATT_STATUS_SUCCESS, CONN1_ID, &service_1, &characteristic_1, 4), ACTION_SUCCESS(bluetooth_disable_action, NULL), CALLBACK_STATE(CB_BT_ADAPTER_STATE_CHANGED, BT_STATE_OFF), ), TEST_CASE_BREDRLE("Gatt Client - Get Characteristic - Incorrect rsp", ACTION_SUCCESS(init_pdus, get_characteristic_2), ACTION_SUCCESS(bluetooth_enable_action, NULL), CALLBACK_STATE(CB_BT_ADAPTER_STATE_CHANGED, BT_STATE_ON), ACTION_SUCCESS(emu_set_ssp_mode_action, NULL), ACTION_SUCCESS(emu_set_connect_cb_action, gatt_conn_cb), ACTION_SUCCESS(gatt_client_register_action, &app1_uuid), CALLBACK_STATUS(CB_GATTC_REGISTER_CLIENT, BT_STATUS_SUCCESS), ACTION_SUCCESS(gatt_client_start_scan_action, NULL), ACTION_SUCCESS(delayemu_setup_powered_remote_action, NULL), CLLBACK_GATTC_SCAN_RES(prop_emu_remotes_default_set, 1, TRUE), ACTION_SUCCESS(gatt_client_stop_scan_action, NULL), ACTION_SUCCESS(gatt_client_connect_action, &app1_conn_req), CALLBACK_GATTC_CONNECT(GATT_STATUS_SUCCESS, prop_emu_remotes_default_set, CONN1_ID, APP1_ID), ACTION_SUCCESS(gatt_client_search_services, &search_services_1), CALLBACK_GATTC_SEARCH_COMPLETE(GATT_STATUS_SUCCESS, CONN1_ID), ACTION_SUCCESS(gatt_client_get_characteristic_action, &get_char_data_1), CALLBACK_GATTC_GET_CHARACTERISTIC_CB(GATT_STATUS_FAILURE, CONN1_ID, &service_1, NULL, 0), ACTION_SUCCESS(bluetooth_disable_action, NULL), CALLBACK_STATE(CB_BT_ADAPTER_STATE_CHANGED, BT_STATE_OFF), ), TEST_CASE_BREDRLE("Gatt Client - Get Characteristic - None", ACTION_SUCCESS(init_pdus, get_characteristic_1), ACTION_SUCCESS(bluetooth_enable_action, NULL), CALLBACK_STATE(CB_BT_ADAPTER_STATE_CHANGED, BT_STATE_ON), ACTION_SUCCESS(emu_set_ssp_mode_action, NULL), ACTION_SUCCESS(emu_set_connect_cb_action, gatt_conn_cb), ACTION_SUCCESS(gatt_client_register_action, &app1_uuid), CALLBACK_STATUS(CB_GATTC_REGISTER_CLIENT, BT_STATUS_SUCCESS), ACTION_SUCCESS(gatt_client_start_scan_action, NULL), ACTION_SUCCESS(delayemu_setup_powered_remote_action, NULL), CLLBACK_GATTC_SCAN_RES(prop_emu_remotes_default_set, 1, TRUE), ACTION_SUCCESS(gatt_client_stop_scan_action, NULL), ACTION_SUCCESS(gatt_client_connect_action, &app1_conn_req), CALLBACK_GATTC_CONNECT(GATT_STATUS_SUCCESS, prop_emu_remotes_default_set, CONN1_ID, APP1_ID), ACTION_SUCCESS(gatt_client_search_services, &search_services_1), CALLBACK_GATTC_SEARCH_COMPLETE(GATT_STATUS_SUCCESS, CONN1_ID), ACTION_FAIL(gatt_client_get_characteristic_action, &get_char_data_2), CALLBACK_GATTC_GET_CHARACTERISTIC_CB(GATT_STATUS_FAILURE, CONN1_ID, &service_2, NULL, 0), ACTION_SUCCESS(bluetooth_disable_action, NULL), CALLBACK_STATE(CB_BT_ADAPTER_STATE_CHANGED, BT_STATE_OFF), ), TEST_CASE_BREDRLE("Gatt Client - Get Descriptor - Incorrect rsp", ACTION_SUCCESS(init_pdus, get_descriptor_0), ACTION_SUCCESS(bluetooth_enable_action, NULL), CALLBACK_STATE(CB_BT_ADAPTER_STATE_CHANGED, BT_STATE_ON), ACTION_SUCCESS(emu_set_ssp_mode_action, NULL), ACTION_SUCCESS(emu_set_connect_cb_action, gatt_conn_cb), ACTION_SUCCESS(gatt_client_register_action, &app1_uuid), CALLBACK_STATUS(CB_GATTC_REGISTER_CLIENT, BT_STATUS_SUCCESS), ACTION_SUCCESS(gatt_client_start_scan_action, NULL), ACTION_SUCCESS(delayemu_setup_powered_remote_action, NULL), CLLBACK_GATTC_SCAN_RES(prop_emu_remotes_default_set, 1, TRUE), ACTION_SUCCESS(gatt_client_stop_scan_action, NULL), ACTION_SUCCESS(gatt_client_connect_action, &app1_conn_req), CALLBACK_GATTC_CONNECT(GATT_STATUS_SUCCESS, prop_emu_remotes_default_set, CONN1_ID, APP1_ID), ACTION_SUCCESS(gatt_client_search_services, &search_services_1), CALLBACK_GATTC_SEARCH_COMPLETE(GATT_STATUS_SUCCESS, CONN1_ID), ACTION_SUCCESS(gatt_client_get_characteristic_action, &get_char_data_1), CALLBACK_GATTC_GET_CHARACTERISTIC_CB(GATT_STATUS_SUCCESS, CONN1_ID, &service_1, &characteristic_1, 4), ACTION_SUCCESS(gatt_client_get_descriptor_action, &get_desc_data_1), CALLBACK_GATTC_GET_DESCRIPTOR(GATT_STATUS_FAILURE, CONN1_ID, &service_1, &characteristic_1, NULL), ACTION_SUCCESS(bluetooth_disable_action, NULL), CALLBACK_STATE(CB_BT_ADAPTER_STATE_CHANGED, BT_STATE_OFF), ), TEST_CASE_BREDRLE("Gatt Client - Get Descriptor - Single", ACTION_SUCCESS(init_pdus, get_descriptor_1), ACTION_SUCCESS(bluetooth_enable_action, NULL), CALLBACK_STATE(CB_BT_ADAPTER_STATE_CHANGED, BT_STATE_ON), ACTION_SUCCESS(emu_set_ssp_mode_action, NULL), ACTION_SUCCESS(emu_set_connect_cb_action, gatt_conn_cb), ACTION_SUCCESS(gatt_client_register_action, &app1_uuid), CALLBACK_STATUS(CB_GATTC_REGISTER_CLIENT, BT_STATUS_SUCCESS), ACTION_SUCCESS(gatt_client_start_scan_action, NULL), ACTION_SUCCESS(delayemu_setup_powered_remote_action, NULL), CLLBACK_GATTC_SCAN_RES(prop_emu_remotes_default_set, 1, TRUE), ACTION_SUCCESS(gatt_client_stop_scan_action, NULL), ACTION_SUCCESS(gatt_client_connect_action, &app1_conn_req), CALLBACK_GATTC_CONNECT(GATT_STATUS_SUCCESS, prop_emu_remotes_default_set, CONN1_ID, APP1_ID), ACTION_SUCCESS(gatt_client_search_services, &search_services_1), CALLBACK_GATTC_SEARCH_COMPLETE(GATT_STATUS_SUCCESS, CONN1_ID), ACTION_SUCCESS(gatt_client_get_characteristic_action, &get_char_data_1), CALLBACK_GATTC_GET_CHARACTERISTIC_CB(GATT_STATUS_SUCCESS, CONN1_ID, &service_1, &characteristic_1, 4), ACTION_SUCCESS(gatt_client_get_descriptor_action, &get_desc_data_1), CALLBACK_GATTC_GET_DESCRIPTOR(GATT_STATUS_SUCCESS, CONN1_ID, &service_1, &characteristic_1, &desc_1), ACTION_SUCCESS(bluetooth_disable_action, NULL), CALLBACK_STATE(CB_BT_ADAPTER_STATE_CHANGED, BT_STATE_OFF), ), TEST_CASE_BREDRLE("Gatt Client - Get Descriptor - Multiple", ACTION_SUCCESS(init_pdus, get_descriptor_2), ACTION_SUCCESS(bluetooth_enable_action, NULL), CALLBACK_STATE(CB_BT_ADAPTER_STATE_CHANGED, BT_STATE_ON), ACTION_SUCCESS(emu_set_ssp_mode_action, NULL), ACTION_SUCCESS(emu_set_connect_cb_action, gatt_conn_cb), ACTION_SUCCESS(gatt_client_register_action, &app1_uuid), CALLBACK_STATUS(CB_GATTC_REGISTER_CLIENT, BT_STATUS_SUCCESS), ACTION_SUCCESS(gatt_client_start_scan_action, NULL), ACTION_SUCCESS(delayemu_setup_powered_remote_action, NULL), CLLBACK_GATTC_SCAN_RES(prop_emu_remotes_default_set, 1, TRUE), ACTION_SUCCESS(gatt_client_stop_scan_action, NULL), ACTION_SUCCESS(gatt_client_connect_action, &app1_conn_req), CALLBACK_GATTC_CONNECT(GATT_STATUS_SUCCESS, prop_emu_remotes_default_set, CONN1_ID, APP1_ID), ACTION_SUCCESS(gatt_client_search_services, &search_services_1), CALLBACK_GATTC_SEARCH_COMPLETE(GATT_STATUS_SUCCESS, CONN1_ID), ACTION_SUCCESS(gatt_client_get_characteristic_action, &get_char_data_1), CALLBACK_GATTC_GET_CHARACTERISTIC_CB(GATT_STATUS_SUCCESS, CONN1_ID, &service_1, &characteristic_1, 4), ACTION_SUCCESS(gatt_client_get_descriptor_action, &get_desc_data_1), CALLBACK_GATTC_GET_DESCRIPTOR(GATT_STATUS_SUCCESS, CONN1_ID, &service_1, &characteristic_1, &desc_1), ACTION_SUCCESS(gatt_client_get_descriptor_action, &get_desc_data_2), CALLBACK_GATTC_GET_DESCRIPTOR(GATT_STATUS_SUCCESS, CONN1_ID, &service_1, &characteristic_1, &desc_2), ACTION_SUCCESS(bluetooth_disable_action, NULL), CALLBACK_STATE(CB_BT_ADAPTER_STATE_CHANGED, BT_STATE_OFF), ), TEST_CASE_BREDRLE("Gatt Client - Get Descriptor - None", ACTION_SUCCESS(init_pdus, get_descriptor_3), ACTION_SUCCESS(bluetooth_enable_action, NULL), CALLBACK_STATE(CB_BT_ADAPTER_STATE_CHANGED, BT_STATE_ON), ACTION_SUCCESS(emu_set_ssp_mode_action, NULL), ACTION_SUCCESS(emu_set_connect_cb_action, gatt_conn_cb), ACTION_SUCCESS(gatt_client_register_action, &app1_uuid), CALLBACK_STATUS(CB_GATTC_REGISTER_CLIENT, BT_STATUS_SUCCESS), ACTION_SUCCESS(gatt_client_start_scan_action, NULL), ACTION_SUCCESS(delayemu_setup_powered_remote_action, NULL), CLLBACK_GATTC_SCAN_RES(prop_emu_remotes_default_set, 1, TRUE), ACTION_SUCCESS(gatt_client_stop_scan_action, NULL), ACTION_SUCCESS(gatt_client_connect_action, &app1_conn_req), CALLBACK_GATTC_CONNECT(GATT_STATUS_SUCCESS, prop_emu_remotes_default_set, CONN1_ID, APP1_ID), ACTION_SUCCESS(gatt_client_search_services, &search_services_1), CALLBACK_GATTC_SEARCH_COMPLETE(GATT_STATUS_SUCCESS, CONN1_ID), ACTION_SUCCESS(gatt_client_get_characteristic_action, &get_char_data_1), CALLBACK_GATTC_GET_CHARACTERISTIC_CB(GATT_STATUS_SUCCESS, CONN1_ID, &service_1, &characteristic_1, 4), ACTION_SUCCESS(gatt_client_get_descriptor_action, &get_desc_data_1), CALLBACK_GATTC_GET_DESCRIPTOR(GATT_STATUS_FAILURE, CONN1_ID, &service_1, &characteristic_1, NULL), ACTION_SUCCESS(bluetooth_disable_action, NULL), CALLBACK_STATE(CB_BT_ADAPTER_STATE_CHANGED, BT_STATE_OFF), ), TEST_CASE_BREDRLE("Gatt Client - Get Included Services - Incorrect rsp", ACTION_SUCCESS(init_pdus, get_included_0), ACTION_SUCCESS(bluetooth_enable_action, NULL), CALLBACK_STATE(CB_BT_ADAPTER_STATE_CHANGED, BT_STATE_ON), ACTION_SUCCESS(emu_set_ssp_mode_action, NULL), ACTION_SUCCESS(emu_set_connect_cb_action, gatt_conn_cb), ACTION_SUCCESS(gatt_client_register_action, &app1_uuid), CALLBACK_STATUS(CB_GATTC_REGISTER_CLIENT, BT_STATUS_SUCCESS), ACTION_SUCCESS(gatt_client_start_scan_action, NULL), ACTION_SUCCESS(delayemu_setup_powered_remote_action, NULL), CLLBACK_GATTC_SCAN_RES(prop_emu_remotes_default_set, 1, TRUE), ACTION_SUCCESS(gatt_client_stop_scan_action, NULL), ACTION_SUCCESS(gatt_client_connect_action, &app1_conn_req), CALLBACK_GATTC_CONNECT(GATT_STATUS_SUCCESS, prop_emu_remotes_default_set, CONN1_ID, APP1_ID), ACTION_SUCCESS(gatt_client_search_services, &search_services_1), CALLBACK_GATTC_SEARCH_COMPLETE(GATT_STATUS_SUCCESS, CONN1_ID), ACTION_SUCCESS(gatt_client_get_included_action, &get_incl_data_1), CALLBACK_GATTC_GET_INCLUDED(GATT_STATUS_FAILURE, CONN1_ID, &service_1, NULL), ACTION_SUCCESS(bluetooth_disable_action, NULL), CALLBACK_STATE(CB_BT_ADAPTER_STATE_CHANGED, BT_STATE_OFF), ), TEST_CASE_BREDRLE("Gatt Client - Get Included Service - 16 UUID", ACTION_SUCCESS(init_pdus, get_included_1), ACTION_SUCCESS(bluetooth_enable_action, NULL), CALLBACK_STATE(CB_BT_ADAPTER_STATE_CHANGED, BT_STATE_ON), ACTION_SUCCESS(emu_set_ssp_mode_action, NULL), ACTION_SUCCESS(emu_set_connect_cb_action, gatt_conn_cb), ACTION_SUCCESS(gatt_client_register_action, &app1_uuid), CALLBACK_STATUS(CB_GATTC_REGISTER_CLIENT, BT_STATUS_SUCCESS), ACTION_SUCCESS(gatt_client_start_scan_action, NULL), ACTION_SUCCESS(delayemu_setup_powered_remote_action, NULL), CLLBACK_GATTC_SCAN_RES(prop_emu_remotes_default_set, 1, TRUE), ACTION_SUCCESS(gatt_client_stop_scan_action, NULL), ACTION_SUCCESS(gatt_client_connect_action, &app1_conn_req), CALLBACK_GATTC_CONNECT(GATT_STATUS_SUCCESS, prop_emu_remotes_default_set, CONN1_ID, APP1_ID), ACTION_SUCCESS(gatt_client_search_services, &search_services_1), CALLBACK_GATTC_SEARCH_COMPLETE(GATT_STATUS_SUCCESS, CONN1_ID), ACTION_SUCCESS(gatt_client_get_included_action, &get_incl_data_1), CALLBACK_GATTC_GET_INCLUDED(GATT_STATUS_SUCCESS, CONN1_ID, &service_1, &included_1), ACTION_SUCCESS(bluetooth_disable_action, NULL), CALLBACK_STATE(CB_BT_ADAPTER_STATE_CHANGED, BT_STATE_OFF), ), TEST_CASE_BREDRLE("Gatt Client - Get Included Service - 128 UUID", ACTION_SUCCESS(init_pdus, get_included_2), ACTION_SUCCESS(bluetooth_enable_action, NULL), CALLBACK_STATE(CB_BT_ADAPTER_STATE_CHANGED, BT_STATE_ON), ACTION_SUCCESS(emu_set_ssp_mode_action, NULL), ACTION_SUCCESS(emu_set_connect_cb_action, gatt_conn_cb), ACTION_SUCCESS(gatt_client_register_action, &app1_uuid), CALLBACK_STATUS(CB_GATTC_REGISTER_CLIENT, BT_STATUS_SUCCESS), ACTION_SUCCESS(gatt_client_start_scan_action, NULL), ACTION_SUCCESS(delayemu_setup_powered_remote_action, NULL), CLLBACK_GATTC_SCAN_RES(prop_emu_remotes_default_set, 1, TRUE), ACTION_SUCCESS(gatt_client_stop_scan_action, NULL), ACTION_SUCCESS(gatt_client_connect_action, &app1_conn_req), CALLBACK_GATTC_CONNECT(GATT_STATUS_SUCCESS, prop_emu_remotes_default_set, CONN1_ID, APP1_ID), ACTION_SUCCESS(gatt_client_search_services, &search_services_1), CALLBACK_GATTC_SEARCH_COMPLETE(GATT_STATUS_SUCCESS, CONN1_ID), ACTION_SUCCESS(gatt_client_get_included_action, &get_incl_data_1), CALLBACK_GATTC_GET_INCLUDED(GATT_STATUS_SUCCESS, CONN1_ID, &service_1, &included_2), ACTION_SUCCESS(bluetooth_disable_action, NULL), CALLBACK_STATE(CB_BT_ADAPTER_STATE_CHANGED, BT_STATE_OFF), ), TEST_CASE_BREDRLE("Gatt Client - Get Included Service - None", ACTION_SUCCESS(init_pdus, get_included_3), ACTION_SUCCESS(bluetooth_enable_action, NULL), CALLBACK_STATE(CB_BT_ADAPTER_STATE_CHANGED, BT_STATE_ON), ACTION_SUCCESS(emu_set_ssp_mode_action, NULL), ACTION_SUCCESS(emu_set_connect_cb_action, gatt_conn_cb), ACTION_SUCCESS(gatt_client_register_action, &app1_uuid), CALLBACK_STATUS(CB_GATTC_REGISTER_CLIENT, BT_STATUS_SUCCESS), ACTION_SUCCESS(gatt_client_start_scan_action, NULL), ACTION_SUCCESS(delayemu_setup_powered_remote_action, NULL), CLLBACK_GATTC_SCAN_RES(prop_emu_remotes_default_set, 1, TRUE), ACTION_SUCCESS(gatt_client_stop_scan_action, NULL), ACTION_SUCCESS(gatt_client_connect_action, &app1_conn_req), CALLBACK_GATTC_CONNECT(GATT_STATUS_SUCCESS, prop_emu_remotes_default_set, CONN1_ID, APP1_ID), ACTION_SUCCESS(gatt_client_search_services, &search_services_1), CALLBACK_GATTC_SEARCH_COMPLETE(GATT_STATUS_SUCCESS, CONN1_ID), ACTION_SUCCESS(gatt_client_get_included_action, &get_incl_data_1), CALLBACK_GATTC_GET_INCLUDED(GATT_STATUS_FAILURE, CONN1_ID, &service_1, NULL), ACTION_SUCCESS(bluetooth_disable_action, NULL), CALLBACK_STATE(CB_BT_ADAPTER_STATE_CHANGED, BT_STATE_OFF), ), TEST_CASE_BREDRLE("Gatt Client - Read Characteristic - Success", ACTION_SUCCESS(init_pdus, read_characteristic_1), ACTION_SUCCESS(bluetooth_enable_action, NULL), CALLBACK_STATE(CB_BT_ADAPTER_STATE_CHANGED, BT_STATE_ON), ACTION_SUCCESS(init_read_params_action, &set_read_param_1), ACTION_SUCCESS(emu_set_ssp_mode_action, NULL), ACTION_SUCCESS(emu_set_connect_cb_action, gatt_conn_cb), ACTION_SUCCESS(gatt_client_register_action, &app1_uuid), CALLBACK_STATUS(CB_GATTC_REGISTER_CLIENT, BT_STATUS_SUCCESS), ACTION_SUCCESS(gatt_client_start_scan_action, NULL), ACTION_SUCCESS(delayemu_setup_powered_remote_action, NULL), CLLBACK_GATTC_SCAN_RES(prop_emu_remotes_default_set, 1, TRUE), ACTION_SUCCESS(gatt_client_stop_scan_action, NULL), ACTION_SUCCESS(gatt_client_connect_action, &app1_conn_req), CALLBACK_GATTC_CONNECT(GATT_STATUS_SUCCESS, prop_emu_remotes_default_set, CONN1_ID, APP1_ID), ACTION_SUCCESS(gatt_client_search_services, &search_services_1), CALLBACK_GATTC_SEARCH_COMPLETE(GATT_STATUS_SUCCESS, CONN1_ID), ACTION_SUCCESS(gatt_client_get_characteristic_action, &get_char_data_1), CALLBACK_GATTC_GET_CHARACTERISTIC_CB(GATT_STATUS_SUCCESS, CONN1_ID, &service_1, &characteristic_1, 4), ACTION_SUCCESS(gatt_client_read_characteristic_action, &read_char_data_1), CALLBACK_GATTC_READ_CHARACTERISTIC(GATT_STATUS_SUCCESS, CONN1_ID, &read_params_1), ACTION_SUCCESS(bluetooth_disable_action, NULL), CALLBACK_STATE(CB_BT_ADAPTER_STATE_CHANGED, BT_STATE_OFF), ), TEST_CASE_BREDRLE("Gatt Client - Read Characteristic - Insuf. Auth.", ACTION_SUCCESS(init_pdus, read_characteristic_2), ACTION_SUCCESS(bluetooth_enable_action, NULL), CALLBACK_STATE(CB_BT_ADAPTER_STATE_CHANGED, BT_STATE_ON), ACTION_SUCCESS(init_read_params_action, &set_read_param_2), ACTION_SUCCESS(emu_set_ssp_mode_action, NULL), ACTION_SUCCESS(emu_set_connect_cb_action, gatt_conn_cb), ACTION_SUCCESS(gatt_client_register_action, &app1_uuid), CALLBACK_STATUS(CB_GATTC_REGISTER_CLIENT, BT_STATUS_SUCCESS), ACTION_SUCCESS(gatt_client_start_scan_action, NULL), ACTION_SUCCESS(delayemu_setup_powered_remote_action, NULL), CLLBACK_GATTC_SCAN_RES(prop_emu_remotes_default_set, 1, TRUE), ACTION_SUCCESS(gatt_client_stop_scan_action, NULL), ACTION_SUCCESS(gatt_client_connect_action, &app1_conn_req), CALLBACK_GATTC_CONNECT(GATT_STATUS_SUCCESS, prop_emu_remotes_default_set, CONN1_ID, APP1_ID), ACTION_SUCCESS(gatt_client_search_services, &search_services_1), CALLBACK_GATTC_SEARCH_COMPLETE(GATT_STATUS_SUCCESS, CONN1_ID), ACTION_SUCCESS(gatt_client_get_characteristic_action, &get_char_data_1), CALLBACK_GATTC_GET_CHARACTERISTIC_CB(GATT_STATUS_SUCCESS, CONN1_ID, &service_1, &characteristic_1, 4), ACTION_SUCCESS(gatt_client_read_characteristic_action, &read_char_data_1), CALLBACK_GATTC_READ_CHARACTERISTIC(GATT_STATUS_INS_AUTH, CONN1_ID, &read_params_1), ACTION_SUCCESS(bluetooth_disable_action, NULL), CALLBACK_STATE(CB_BT_ADAPTER_STATE_CHANGED, BT_STATE_OFF), ), TEST_CASE_BREDRLE("Gatt Client - Read Characteristic - Wrong params", ACTION_SUCCESS(init_pdus, read_characteristic_2), ACTION_SUCCESS(bluetooth_enable_action, NULL), CALLBACK_STATE(CB_BT_ADAPTER_STATE_CHANGED, BT_STATE_ON), ACTION_SUCCESS(init_read_params_action, &set_read_param_3), ACTION_SUCCESS(emu_set_ssp_mode_action, NULL), ACTION_SUCCESS(emu_set_connect_cb_action, gatt_conn_cb), ACTION_SUCCESS(gatt_client_register_action, &app1_uuid), CALLBACK_STATUS(CB_GATTC_REGISTER_CLIENT, BT_STATUS_SUCCESS), ACTION_SUCCESS(gatt_client_start_scan_action, NULL), ACTION_SUCCESS(delayemu_setup_powered_remote_action, NULL), CLLBACK_GATTC_SCAN_RES(prop_emu_remotes_default_set, 1, TRUE), ACTION_SUCCESS(gatt_client_stop_scan_action, NULL), ACTION_SUCCESS(gatt_client_connect_action, &app1_conn_req), CALLBACK_GATTC_CONNECT(GATT_STATUS_SUCCESS, prop_emu_remotes_default_set, CONN1_ID, APP1_ID), ACTION_SUCCESS(gatt_client_search_services, &search_services_1), CALLBACK_GATTC_SEARCH_COMPLETE(GATT_STATUS_SUCCESS, CONN1_ID), ACTION_SUCCESS(gatt_client_get_characteristic_action, &get_char_data_1), CALLBACK_GATTC_GET_CHARACTERISTIC_CB(GATT_STATUS_SUCCESS, CONN1_ID, &service_1, &characteristic_1, 4), ACTION_FAIL(gatt_client_read_characteristic_action, &read_char_data_2), CALLBACK_GATTC_READ_CHARACTERISTIC(GATT_STATUS_FAILURE, CONN1_ID, &read_params_1), ACTION_SUCCESS(bluetooth_disable_action, NULL), CALLBACK_STATE(CB_BT_ADAPTER_STATE_CHANGED, BT_STATE_OFF), ), TEST_CASE_BREDRLE("Gatt Client - Read Descriptor - Success", ACTION_SUCCESS(init_pdus, read_descriptor_1), ACTION_SUCCESS(init_read_params_action, &set_read_param_4), ACTION_SUCCESS(bluetooth_enable_action, NULL), CALLBACK_STATE(CB_BT_ADAPTER_STATE_CHANGED, BT_STATE_ON), ACTION_SUCCESS(emu_set_ssp_mode_action, NULL), ACTION_SUCCESS(emu_set_connect_cb_action, gatt_conn_cb), ACTION_SUCCESS(gatt_client_register_action, &app1_uuid), CALLBACK_STATUS(CB_GATTC_REGISTER_CLIENT, BT_STATUS_SUCCESS), ACTION_SUCCESS(gatt_client_start_scan_action, NULL), ACTION_SUCCESS(delayemu_setup_powered_remote_action, NULL), CLLBACK_GATTC_SCAN_RES(prop_emu_remotes_default_set, 1, TRUE), ACTION_SUCCESS(gatt_client_stop_scan_action, NULL), ACTION_SUCCESS(gatt_client_connect_action, &app1_conn_req), CALLBACK_GATTC_CONNECT(GATT_STATUS_SUCCESS, prop_emu_remotes_default_set, CONN1_ID, APP1_ID), ACTION_SUCCESS(gatt_client_search_services, &search_services_1), CALLBACK_GATTC_SEARCH_COMPLETE(GATT_STATUS_SUCCESS, CONN1_ID), ACTION_SUCCESS(gatt_client_get_characteristic_action, &get_char_data_1), CALLBACK_GATTC_GET_CHARACTERISTIC_CB(GATT_STATUS_SUCCESS, CONN1_ID, &service_1, &characteristic_1, 4), ACTION_SUCCESS(gatt_client_get_descriptor_action, &get_desc_data_1), CALLBACK_GATTC_GET_DESCRIPTOR(GATT_STATUS_SUCCESS, CONN1_ID, &service_1, &characteristic_1, &desc_1), ACTION_SUCCESS(gatt_client_read_descriptor_action, &read_desc_data_1), CALLBACK_GATTC_READ_DESCRIPTOR(GATT_STATUS_SUCCESS, CONN1_ID, &read_params_1), ACTION_SUCCESS(bluetooth_disable_action, NULL), CALLBACK_STATE(CB_BT_ADAPTER_STATE_CHANGED, BT_STATE_OFF), ), TEST_CASE_BREDRLE("Gatt Client - Read Descriptor - Insuf. Auth.", ACTION_SUCCESS(init_pdus, read_descriptor_2), ACTION_SUCCESS(init_read_params_action, &set_read_param_5), ACTION_SUCCESS(bluetooth_enable_action, NULL), CALLBACK_STATE(CB_BT_ADAPTER_STATE_CHANGED, BT_STATE_ON), ACTION_SUCCESS(emu_set_ssp_mode_action, NULL), ACTION_SUCCESS(emu_set_connect_cb_action, gatt_conn_cb), ACTION_SUCCESS(gatt_client_register_action, &app1_uuid), CALLBACK_STATUS(CB_GATTC_REGISTER_CLIENT, BT_STATUS_SUCCESS), ACTION_SUCCESS(gatt_client_start_scan_action, NULL), ACTION_SUCCESS(delayemu_setup_powered_remote_action, NULL), CLLBACK_GATTC_SCAN_RES(prop_emu_remotes_default_set, 1, TRUE), ACTION_SUCCESS(gatt_client_stop_scan_action, NULL), ACTION_SUCCESS(gatt_client_connect_action, &app1_conn_req), CALLBACK_GATTC_CONNECT(GATT_STATUS_SUCCESS, prop_emu_remotes_default_set, CONN1_ID, APP1_ID), ACTION_SUCCESS(gatt_client_search_services, &search_services_1), CALLBACK_GATTC_SEARCH_COMPLETE(GATT_STATUS_SUCCESS, CONN1_ID), ACTION_SUCCESS(gatt_client_get_characteristic_action, &get_char_data_1), CALLBACK_GATTC_GET_CHARACTERISTIC_CB(GATT_STATUS_SUCCESS, CONN1_ID, &service_1, &characteristic_1, 4), ACTION_SUCCESS(gatt_client_get_descriptor_action, &get_desc_data_1), CALLBACK_GATTC_GET_DESCRIPTOR(GATT_STATUS_SUCCESS, CONN1_ID, &service_1, &characteristic_1, &desc_1), ACTION_SUCCESS(gatt_client_read_descriptor_action, &read_desc_data_1), CALLBACK_GATTC_READ_DESCRIPTOR(GATT_STATUS_INS_AUTH, CONN1_ID, &read_params_1), ACTION_SUCCESS(bluetooth_disable_action, NULL), CALLBACK_STATE(CB_BT_ADAPTER_STATE_CHANGED, BT_STATE_OFF), ), TEST_CASE_BREDRLE("Gatt Client - Read Descriptor - Wrong params", ACTION_SUCCESS(init_pdus, read_descriptor_2), ACTION_SUCCESS(init_read_params_action, &set_read_param_6), ACTION_SUCCESS(bluetooth_enable_action, NULL), CALLBACK_STATE(CB_BT_ADAPTER_STATE_CHANGED, BT_STATE_ON), ACTION_SUCCESS(emu_set_ssp_mode_action, NULL), ACTION_SUCCESS(emu_set_connect_cb_action, gatt_conn_cb), ACTION_SUCCESS(gatt_client_register_action, &app1_uuid), CALLBACK_STATUS(CB_GATTC_REGISTER_CLIENT, BT_STATUS_SUCCESS), ACTION_SUCCESS(gatt_client_start_scan_action, NULL), ACTION_SUCCESS(delayemu_setup_powered_remote_action, NULL), CLLBACK_GATTC_SCAN_RES(prop_emu_remotes_default_set, 1, TRUE), ACTION_SUCCESS(gatt_client_stop_scan_action, NULL), ACTION_SUCCESS(gatt_client_connect_action, &app1_conn_req), CALLBACK_GATTC_CONNECT(GATT_STATUS_SUCCESS, prop_emu_remotes_default_set, CONN1_ID, APP1_ID), ACTION_SUCCESS(gatt_client_search_services, &search_services_1), CALLBACK_GATTC_SEARCH_COMPLETE(GATT_STATUS_SUCCESS, CONN1_ID), ACTION_SUCCESS(gatt_client_get_characteristic_action, &get_char_data_1), CALLBACK_GATTC_GET_CHARACTERISTIC_CB(GATT_STATUS_SUCCESS, CONN1_ID, &service_1, &characteristic_1, 4), ACTION_SUCCESS(gatt_client_get_descriptor_action, &get_desc_data_1), CALLBACK_GATTC_GET_DESCRIPTOR(GATT_STATUS_SUCCESS, CONN1_ID, &service_1, &characteristic_1, &desc_1), ACTION_FAIL(gatt_client_read_descriptor_action, &read_desc_data_2), CALLBACK_GATTC_READ_DESCRIPTOR(GATT_STATUS_FAILURE, CONN1_ID, &read_params_1), ACTION_SUCCESS(bluetooth_disable_action, NULL), CALLBACK_STATE(CB_BT_ADAPTER_STATE_CHANGED, BT_STATE_OFF), ), TEST_CASE_BREDRLE("Gatt Client - Write Characteristic Cmd - Success", ACTION_SUCCESS(init_pdus, write_characteristic_1), ACTION_SUCCESS(bluetooth_enable_action, NULL), CALLBACK_STATE(CB_BT_ADAPTER_STATE_CHANGED, BT_STATE_ON), ACTION_SUCCESS(init_write_params_action, &set_write_param_1), ACTION_SUCCESS(emu_set_ssp_mode_action, NULL), ACTION_SUCCESS(emu_set_connect_cb_action, gatt_conn_cb), ACTION_SUCCESS(gatt_client_register_action, &app1_uuid), CALLBACK_STATUS(CB_GATTC_REGISTER_CLIENT, BT_STATUS_SUCCESS), ACTION_SUCCESS(gatt_client_start_scan_action, NULL), ACTION_SUCCESS(delayemu_setup_powered_remote_action, NULL), CLLBACK_GATTC_SCAN_RES(prop_emu_remotes_default_set, 1, TRUE), ACTION_SUCCESS(gatt_client_stop_scan_action, NULL), ACTION_SUCCESS(gatt_client_connect_action, &app1_conn_req), CALLBACK_GATTC_CONNECT(GATT_STATUS_SUCCESS, prop_emu_remotes_default_set, CONN1_ID, APP1_ID), ACTION_SUCCESS(gatt_client_search_services, &search_services_1), CALLBACK_GATTC_SEARCH_COMPLETE(GATT_STATUS_SUCCESS, CONN1_ID), ACTION_SUCCESS(gatt_client_get_characteristic_action, &get_char_data_1), CALLBACK_GATTC_GET_CHARACTERISTIC_CB(GATT_STATUS_SUCCESS, CONN1_ID, &service_1, &characteristic_1, 4), ACTION_SUCCESS(gatt_client_write_characteristic_action, &write_char_data_1), CALLBACK_GATTC_WRITE_CHARACTERISTIC(GATT_STATUS_SUCCESS, CONN1_ID, &write_params_1), ACTION_SUCCESS(bluetooth_disable_action, NULL), CALLBACK_STATE(CB_BT_ADAPTER_STATE_CHANGED, BT_STATE_OFF), ), TEST_CASE_BREDRLE("Gatt Client - Write Characteristic Req - Success", ACTION_SUCCESS(init_pdus, write_characteristic_2), ACTION_SUCCESS(bluetooth_enable_action, NULL), CALLBACK_STATE(CB_BT_ADAPTER_STATE_CHANGED, BT_STATE_ON), ACTION_SUCCESS(init_write_params_action, &set_write_param_1), ACTION_SUCCESS(emu_set_ssp_mode_action, NULL), ACTION_SUCCESS(emu_set_connect_cb_action, gatt_conn_cb), ACTION_SUCCESS(gatt_client_register_action, &app1_uuid), CALLBACK_STATUS(CB_GATTC_REGISTER_CLIENT, BT_STATUS_SUCCESS), ACTION_SUCCESS(gatt_client_start_scan_action, NULL), ACTION_SUCCESS(delayemu_setup_powered_remote_action, NULL), CLLBACK_GATTC_SCAN_RES(prop_emu_remotes_default_set, 1, TRUE), ACTION_SUCCESS(gatt_client_stop_scan_action, NULL), ACTION_SUCCESS(gatt_client_connect_action, &app1_conn_req), CALLBACK_GATTC_CONNECT(GATT_STATUS_SUCCESS, prop_emu_remotes_default_set, CONN1_ID, APP1_ID), ACTION_SUCCESS(gatt_client_search_services, &search_services_1), CALLBACK_GATTC_SEARCH_COMPLETE(GATT_STATUS_SUCCESS, CONN1_ID), ACTION_SUCCESS(gatt_client_get_characteristic_action, &get_char_data_1), CALLBACK_GATTC_GET_CHARACTERISTIC_CB(GATT_STATUS_SUCCESS, CONN1_ID, &service_1, &characteristic_1, 4), ACTION_SUCCESS(gatt_client_write_characteristic_action, &write_char_data_2), CALLBACK_GATTC_WRITE_CHARACTERISTIC(GATT_STATUS_SUCCESS, CONN1_ID, &write_params_1), ACTION_SUCCESS(bluetooth_disable_action, NULL), CALLBACK_STATE(CB_BT_ADAPTER_STATE_CHANGED, BT_STATE_OFF), ), TEST_CASE_BREDRLE("Gatt Client - Write Characteristic - Insuf. Auth.", ACTION_SUCCESS(init_pdus, write_characteristic_3), ACTION_SUCCESS(bluetooth_enable_action, NULL), CALLBACK_STATE(CB_BT_ADAPTER_STATE_CHANGED, BT_STATE_ON), ACTION_SUCCESS(init_write_params_action, &set_write_param_2), ACTION_SUCCESS(emu_set_ssp_mode_action, NULL), ACTION_SUCCESS(emu_set_connect_cb_action, gatt_conn_cb), ACTION_SUCCESS(gatt_client_register_action, &app1_uuid), CALLBACK_STATUS(CB_GATTC_REGISTER_CLIENT, BT_STATUS_SUCCESS), ACTION_SUCCESS(gatt_client_start_scan_action, NULL), ACTION_SUCCESS(delayemu_setup_powered_remote_action, NULL), CLLBACK_GATTC_SCAN_RES(prop_emu_remotes_default_set, 1, TRUE), ACTION_SUCCESS(gatt_client_stop_scan_action, NULL), ACTION_SUCCESS(gatt_client_connect_action, &app1_conn_req), CALLBACK_GATTC_CONNECT(GATT_STATUS_SUCCESS, prop_emu_remotes_default_set, CONN1_ID, APP1_ID), ACTION_SUCCESS(gatt_client_search_services, &search_services_1), CALLBACK_GATTC_SEARCH_COMPLETE(GATT_STATUS_SUCCESS, CONN1_ID), ACTION_SUCCESS(gatt_client_get_characteristic_action, &get_char_data_1), CALLBACK_GATTC_GET_CHARACTERISTIC_CB(GATT_STATUS_SUCCESS, CONN1_ID, &service_1, &characteristic_1, 4), ACTION_SUCCESS(gatt_client_write_characteristic_action, &write_char_data_2), CALLBACK_GATTC_WRITE_CHARACTERISTIC(GATT_STATUS_INS_AUTH, CONN1_ID, &write_params_1), ACTION_SUCCESS(bluetooth_disable_action, NULL), CALLBACK_STATE(CB_BT_ADAPTER_STATE_CHANGED, BT_STATE_OFF), ), TEST_CASE_BREDRLE("Gatt Client - Write Characteristic - Wrong Params", ACTION_SUCCESS(init_pdus, write_characteristic_3), ACTION_SUCCESS(bluetooth_enable_action, NULL), CALLBACK_STATE(CB_BT_ADAPTER_STATE_CHANGED, BT_STATE_ON), ACTION_SUCCESS(init_write_params_action, &set_write_param_3), ACTION_SUCCESS(emu_set_ssp_mode_action, NULL), ACTION_SUCCESS(emu_set_connect_cb_action, gatt_conn_cb), ACTION_SUCCESS(gatt_client_register_action, &app1_uuid), CALLBACK_STATUS(CB_GATTC_REGISTER_CLIENT, BT_STATUS_SUCCESS), ACTION_SUCCESS(gatt_client_start_scan_action, NULL), ACTION_SUCCESS(delayemu_setup_powered_remote_action, NULL), CLLBACK_GATTC_SCAN_RES(prop_emu_remotes_default_set, 1, TRUE), ACTION_SUCCESS(gatt_client_stop_scan_action, NULL), ACTION_FAIL(gatt_client_write_characteristic_action, &write_char_data_2), CALLBACK_GATTC_WRITE_CHARACTERISTIC(GATT_STATUS_FAILURE, CONN1_ID, &write_params_1), ACTION_SUCCESS(bluetooth_disable_action, NULL), CALLBACK_STATE(CB_BT_ADAPTER_STATE_CHANGED, BT_STATE_OFF), ), TEST_CASE_BREDRLE("Gatt Client - Register For Notification - Success", ACTION_SUCCESS(init_pdus, notification_1), ACTION_SUCCESS(bluetooth_enable_action, NULL), CALLBACK_STATE(CB_BT_ADAPTER_STATE_CHANGED, BT_STATE_ON), ACTION_SUCCESS(emu_set_ssp_mode_action, NULL), ACTION_SUCCESS(emu_set_connect_cb_action, gatt_conn_cb), ACTION_SUCCESS(gatt_client_register_action, &app1_uuid), CALLBACK_STATUS(CB_GATTC_REGISTER_CLIENT, BT_STATUS_SUCCESS), ACTION_SUCCESS(gatt_client_start_scan_action, NULL), ACTION_SUCCESS(delayemu_setup_powered_remote_action, NULL), CLLBACK_GATTC_SCAN_RES(prop_emu_remotes_default_set, 1, TRUE), ACTION_SUCCESS(gatt_client_stop_scan_action, NULL), ACTION_SUCCESS(gatt_client_connect_action, &app1_conn_req), CALLBACK_GATTC_CONNECT(GATT_STATUS_SUCCESS, prop_emu_remotes_default_set, CONN1_ID, APP1_ID), ACTION_SUCCESS(gatt_client_search_services, &search_services_1), CALLBACK_GATTC_SEARCH_COMPLETE(GATT_STATUS_SUCCESS, CONN1_ID), ACTION_SUCCESS(gatt_client_get_characteristic_action, &get_char_data_1), CALLBACK_GATTC_GET_CHARACTERISTIC_CB(GATT_STATUS_SUCCESS, CONN1_ID, &service_1, &characteristic_1, 4), ACTION_SUCCESS(gatt_client_register_for_notification_action, ¬if_data_1), CALLBACK_GATTC_REGISTER_FOR_NOTIF(GATT_STATUS_SUCCESS, CONN1_ID, &characteristic_1, &service_1, 1), ACTION_SUCCESS(bluetooth_disable_action, NULL), CALLBACK_STATE(CB_BT_ADAPTER_STATE_CHANGED, BT_STATE_OFF), ), TEST_CASE_BREDRLE("Gatt Client - Deregister For Notification - Success", ACTION_SUCCESS(init_pdus, notification_1), ACTION_SUCCESS(bluetooth_enable_action, NULL), CALLBACK_STATE(CB_BT_ADAPTER_STATE_CHANGED, BT_STATE_ON), ACTION_SUCCESS(emu_set_ssp_mode_action, NULL), ACTION_SUCCESS(emu_set_connect_cb_action, gatt_conn_cb), ACTION_SUCCESS(gatt_client_register_action, &app1_uuid), CALLBACK_STATUS(CB_GATTC_REGISTER_CLIENT, BT_STATUS_SUCCESS), ACTION_SUCCESS(gatt_client_start_scan_action, NULL), ACTION_SUCCESS(delayemu_setup_powered_remote_action, NULL), CLLBACK_GATTC_SCAN_RES(prop_emu_remotes_default_set, 1, TRUE), ACTION_SUCCESS(gatt_client_stop_scan_action, NULL), ACTION_SUCCESS(gatt_client_connect_action, &app1_conn_req), CALLBACK_GATTC_CONNECT(GATT_STATUS_SUCCESS, prop_emu_remotes_default_set, CONN1_ID, APP1_ID), ACTION_SUCCESS(gatt_client_search_services, &search_services_1), CALLBACK_GATTC_SEARCH_COMPLETE(GATT_STATUS_SUCCESS, CONN1_ID), ACTION_SUCCESS(gatt_client_get_characteristic_action, &get_char_data_1), CALLBACK_GATTC_GET_CHARACTERISTIC_CB(GATT_STATUS_SUCCESS, CONN1_ID, &service_1, &characteristic_1, 4), ACTION_SUCCESS(gatt_client_register_for_notification_action, ¬if_data_1), CALLBACK_GATTC_REGISTER_FOR_NOTIF(GATT_STATUS_SUCCESS, CONN1_ID, &characteristic_1, &service_1, 1), ACTION_SUCCESS(gatt_client_deregister_for_notification_action, ¬if_data_1), CALLBACK_GATTC_REGISTER_FOR_NOTIF(GATT_STATUS_SUCCESS, CONN1_ID, &characteristic_1, &service_1, 0), ACTION_SUCCESS(bluetooth_disable_action, NULL), CALLBACK_STATE(CB_BT_ADAPTER_STATE_CHANGED, BT_STATE_OFF), ), TEST_CASE_BREDRLE("Gatt Client - Register For Notification - Indicate", ACTION_SUCCESS(init_pdus, notification_2), ACTION_SUCCESS(init_notify_params_action, &set_notify_param_1), ACTION_SUCCESS(bluetooth_enable_action, NULL), CALLBACK_STATE(CB_BT_ADAPTER_STATE_CHANGED, BT_STATE_ON), ACTION_SUCCESS(emu_set_ssp_mode_action, NULL), ACTION_SUCCESS(emu_set_connect_cb_action, gatt_conn_cb), ACTION_SUCCESS(gatt_client_register_action, &app1_uuid), CALLBACK_STATUS(CB_GATTC_REGISTER_CLIENT, BT_STATUS_SUCCESS), ACTION_SUCCESS(gatt_client_start_scan_action, NULL), ACTION_SUCCESS(delayemu_setup_powered_remote_action, NULL), CLLBACK_GATTC_SCAN_RES(prop_emu_remotes_default_set, 1, TRUE), ACTION_SUCCESS(gatt_client_stop_scan_action, NULL), ACTION_SUCCESS(gatt_client_connect_action, &app1_conn_req), CALLBACK_GATTC_CONNECT(GATT_STATUS_SUCCESS, prop_emu_remotes_default_set, CONN1_ID, APP1_ID), ACTION_SUCCESS(gatt_client_search_services, &search_services_1), CALLBACK_GATTC_SEARCH_COMPLETE(GATT_STATUS_SUCCESS, CONN1_ID), ACTION_SUCCESS(gatt_client_get_characteristic_action, &get_char_data_1), CALLBACK_GATTC_GET_CHARACTERISTIC_CB(GATT_STATUS_SUCCESS, CONN1_ID, &service_1, &characteristic_1, 4), ACTION_SUCCESS(gatt_client_register_for_notification_action, ¬if_data_1), CALLBACK_GATTC_REGISTER_FOR_NOTIF(GATT_STATUS_SUCCESS, CONN1_ID, &characteristic_1, &service_1, 1), ACTION_SUCCESS(gatt_remote_send_frame_action, NULL), CALLBACK_GATTC_NOTIFY(CONN1_ID, ¬ify_params_1), ACTION_SUCCESS(bluetooth_disable_action, NULL), CALLBACK_STATE(CB_BT_ADAPTER_STATE_CHANGED, BT_STATE_OFF), ), TEST_CASE_BREDRLE("Gatt Client - Register For Notification - Notify", ACTION_SUCCESS(init_pdus, notification_3), ACTION_SUCCESS(init_notify_params_action, &set_notify_param_2), ACTION_SUCCESS(bluetooth_enable_action, NULL), CALLBACK_STATE(CB_BT_ADAPTER_STATE_CHANGED, BT_STATE_ON), ACTION_SUCCESS(emu_set_ssp_mode_action, NULL), ACTION_SUCCESS(emu_set_connect_cb_action, gatt_conn_cb), ACTION_SUCCESS(gatt_client_register_action, &app1_uuid), CALLBACK_STATUS(CB_GATTC_REGISTER_CLIENT, BT_STATUS_SUCCESS), ACTION_SUCCESS(gatt_client_start_scan_action, NULL), ACTION_SUCCESS(delayemu_setup_powered_remote_action, NULL), CLLBACK_GATTC_SCAN_RES(prop_emu_remotes_default_set, 1, TRUE), ACTION_SUCCESS(gatt_client_stop_scan_action, NULL), ACTION_SUCCESS(gatt_client_connect_action, &app1_conn_req), CALLBACK_GATTC_CONNECT(GATT_STATUS_SUCCESS, prop_emu_remotes_default_set, CONN1_ID, APP1_ID), ACTION_SUCCESS(gatt_client_search_services, &search_services_1), CALLBACK_GATTC_SEARCH_COMPLETE(GATT_STATUS_SUCCESS, CONN1_ID), ACTION_SUCCESS(gatt_client_get_characteristic_action, &get_char_data_1), CALLBACK_GATTC_GET_CHARACTERISTIC_CB(GATT_STATUS_SUCCESS, CONN1_ID, &service_1, &characteristic_1, 4), ACTION_SUCCESS(gatt_client_register_for_notification_action, ¬if_data_1), CALLBACK_GATTC_REGISTER_FOR_NOTIF(GATT_STATUS_SUCCESS, CONN1_ID, &characteristic_1, &service_1, 1), ACTION_SUCCESS(gatt_remote_send_frame_action, NULL), CALLBACK_GATTC_NOTIFY(CONN1_ID, ¬ify_params_1), ACTION_SUCCESS(bluetooth_disable_action, NULL), CALLBACK_STATE(CB_BT_ADAPTER_STATE_CHANGED, BT_STATE_OFF), ), TEST_CASE_BREDRLE("Gatt Client - Write Descriptor - Success", ACTION_SUCCESS(init_pdus, write_descriptor_1), ACTION_SUCCESS(bluetooth_enable_action, NULL), CALLBACK_STATE(CB_BT_ADAPTER_STATE_CHANGED, BT_STATE_ON), ACTION_SUCCESS(init_write_params_action, &set_write_param_4), ACTION_SUCCESS(emu_set_ssp_mode_action, NULL), ACTION_SUCCESS(emu_set_connect_cb_action, gatt_conn_cb), ACTION_SUCCESS(gatt_client_register_action, &app1_uuid), CALLBACK_STATUS(CB_GATTC_REGISTER_CLIENT, BT_STATUS_SUCCESS), ACTION_SUCCESS(gatt_client_start_scan_action, NULL), ACTION_SUCCESS(delayemu_setup_powered_remote_action, NULL), CLLBACK_GATTC_SCAN_RES(prop_emu_remotes_default_set, 1, TRUE), ACTION_SUCCESS(gatt_client_stop_scan_action, NULL), ACTION_SUCCESS(gatt_client_connect_action, &app1_conn_req), CALLBACK_GATTC_CONNECT(GATT_STATUS_SUCCESS, prop_emu_remotes_default_set, CONN1_ID, APP1_ID), ACTION_SUCCESS(gatt_client_search_services, &search_services_1), CALLBACK_GATTC_SEARCH_COMPLETE(GATT_STATUS_SUCCESS, CONN1_ID), ACTION_SUCCESS(gatt_client_get_characteristic_action, &get_char_data_1), CALLBACK_GATTC_GET_CHARACTERISTIC_CB(GATT_STATUS_SUCCESS, CONN1_ID, &service_1, &characteristic_1, 4), ACTION_SUCCESS(gatt_client_get_descriptor_action, &get_desc_data_1), CALLBACK_GATTC_GET_DESCRIPTOR(GATT_STATUS_SUCCESS, CONN1_ID, &service_1, &characteristic_1, &desc_1), ACTION_SUCCESS(gatt_client_write_descriptor_action, &write_desc_data_1), CALLBACK_GATTC_WRITE_DESCRIPTOR(GATT_STATUS_SUCCESS, CONN1_ID, &write_params_1), ACTION_SUCCESS(bluetooth_disable_action, NULL), CALLBACK_STATE(CB_BT_ADAPTER_STATE_CHANGED, BT_STATE_OFF), ), TEST_CASE_BREDRLE("Gatt Client - Write Descriptor - Insuf. Auth.", ACTION_SUCCESS(init_pdus, write_descriptor_2), ACTION_SUCCESS(bluetooth_enable_action, NULL), CALLBACK_STATE(CB_BT_ADAPTER_STATE_CHANGED, BT_STATE_ON), ACTION_SUCCESS(init_write_params_action, &set_write_param_6), ACTION_SUCCESS(emu_set_ssp_mode_action, NULL), ACTION_SUCCESS(emu_set_connect_cb_action, gatt_conn_cb), ACTION_SUCCESS(gatt_client_register_action, &app1_uuid), CALLBACK_STATUS(CB_GATTC_REGISTER_CLIENT, BT_STATUS_SUCCESS), ACTION_SUCCESS(gatt_client_start_scan_action, NULL), ACTION_SUCCESS(delayemu_setup_powered_remote_action, NULL), CLLBACK_GATTC_SCAN_RES(prop_emu_remotes_default_set, 1, TRUE), ACTION_SUCCESS(gatt_client_stop_scan_action, NULL), ACTION_SUCCESS(gatt_client_connect_action, &app1_conn_req), CALLBACK_GATTC_CONNECT(GATT_STATUS_SUCCESS, prop_emu_remotes_default_set, CONN1_ID, APP1_ID), ACTION_SUCCESS(gatt_client_search_services, &search_services_1), CALLBACK_GATTC_SEARCH_COMPLETE(GATT_STATUS_SUCCESS, CONN1_ID), ACTION_SUCCESS(gatt_client_get_characteristic_action, &get_char_data_1), CALLBACK_GATTC_GET_CHARACTERISTIC_CB(GATT_STATUS_SUCCESS, CONN1_ID, &service_1, &characteristic_1, 4), ACTION_SUCCESS(gatt_client_get_descriptor_action, &get_desc_data_1), CALLBACK_GATTC_GET_DESCRIPTOR(GATT_STATUS_SUCCESS, CONN1_ID, &service_1, &characteristic_1, &desc_1), ACTION_SUCCESS(gatt_client_write_descriptor_action, &write_desc_data_1), CALLBACK_GATTC_WRITE_DESCRIPTOR(GATT_STATUS_INS_AUTH, CONN1_ID, &write_params_1), ACTION_SUCCESS(bluetooth_disable_action, NULL), CALLBACK_STATE(CB_BT_ADAPTER_STATE_CHANGED, BT_STATE_OFF), ), TEST_CASE_BREDRLE("Gatt Client - Write Descriptor - Wrong Param", ACTION_SUCCESS(init_pdus, write_descriptor_1), ACTION_SUCCESS(bluetooth_enable_action, NULL), CALLBACK_STATE(CB_BT_ADAPTER_STATE_CHANGED, BT_STATE_ON), ACTION_SUCCESS(init_write_params_action, &set_write_param_5), ACTION_SUCCESS(emu_set_ssp_mode_action, NULL), ACTION_SUCCESS(emu_set_connect_cb_action, gatt_conn_cb), ACTION_SUCCESS(gatt_client_register_action, &app1_uuid), CALLBACK_STATUS(CB_GATTC_REGISTER_CLIENT, BT_STATUS_SUCCESS), ACTION_SUCCESS(gatt_client_start_scan_action, NULL), ACTION_SUCCESS(delayemu_setup_powered_remote_action, NULL), CLLBACK_GATTC_SCAN_RES(prop_emu_remotes_default_set, 1, TRUE), ACTION_SUCCESS(gatt_client_stop_scan_action, NULL), ACTION_SUCCESS(gatt_client_connect_action, &app1_conn_req), CALLBACK_GATTC_CONNECT(GATT_STATUS_SUCCESS, prop_emu_remotes_default_set, CONN1_ID, APP1_ID), ACTION_SUCCESS(gatt_client_search_services, &search_services_1), CALLBACK_GATTC_SEARCH_COMPLETE(GATT_STATUS_SUCCESS, CONN1_ID), ACTION_SUCCESS(gatt_client_get_characteristic_action, &get_char_data_1), CALLBACK_GATTC_GET_CHARACTERISTIC_CB(GATT_STATUS_SUCCESS, CONN1_ID, &service_1, &characteristic_1, 4), ACTION_SUCCESS(gatt_client_get_descriptor_action, &get_desc_data_1), CALLBACK_GATTC_GET_DESCRIPTOR(GATT_STATUS_SUCCESS, CONN1_ID, &service_1, &characteristic_1, &desc_1), ACTION_FAIL(gatt_client_write_descriptor_action, &write_desc_data_2), CALLBACK_GATTC_WRITE_DESCRIPTOR(GATT_STATUS_FAILURE, CONN1_ID, &write_params_1), ACTION_SUCCESS(bluetooth_disable_action, NULL), CALLBACK_STATE(CB_BT_ADAPTER_STATE_CHANGED, BT_STATE_OFF), ), TEST_CASE_BREDRLE("Gatt Server - Register", ACTION_SUCCESS(gatt_server_register_action, &app1_uuid), CALLBACK_STATUS(CB_GATTS_REGISTER_SERVER, BT_STATUS_SUCCESS), ), TEST_CASE_BREDRLE("Gatt Server - Unregister", ACTION_SUCCESS(gatt_server_register_action, &app1_uuid), CALLBACK_STATUS(CB_GATTS_REGISTER_SERVER, BT_STATUS_SUCCESS), ACTION_SUCCESS(gatt_server_unregister_action, INT_TO_PTR(APP1_ID)), ACTION_SUCCESS(gatt_server_register_action, &app1_uuid), CALLBACK_STATUS(CB_GATTS_REGISTER_SERVER, BT_STATUS_SUCCESS), ), TEST_CASE_BREDRLE("Gatt Server - LE Connect", ACTION_SUCCESS(bluetooth_enable_action, NULL), CALLBACK_STATE(CB_BT_ADAPTER_STATE_CHANGED, BT_STATE_ON), ACTION_SUCCESS(emu_set_ssp_mode_action, NULL), ACTION_SUCCESS(emu_set_connect_cb_action, gatt_conn_cb), ACTION_SUCCESS(gatt_server_register_action, &app1_uuid), CALLBACK_STATUS(CB_GATTS_REGISTER_SERVER, BT_STATUS_SUCCESS), ACTION_SUCCESS(bt_start_discovery_action, NULL), CALLBACK_STATE(CB_BT_DISCOVERY_STATE_CHANGED, BT_DISCOVERY_STARTED), ACTION_SUCCESS(emu_setup_powered_remote_action, NULL), CALLBACK_DEVICE_FOUND(prop_emu_remotes_default_le_set, 2), ACTION_SUCCESS(bt_cancel_discovery_action, NULL), ACTION_SUCCESS(gatt_server_connect_action, &app1_conn_req), CALLBACK_GATTS_CONNECTION(GATT_SERVER_CONNECTED, prop_emu_remotes_default_set, CONN1_ID, APP1_ID), ACTION_SUCCESS(bluetooth_disable_action, NULL), CALLBACK_STATE(CB_BT_ADAPTER_STATE_CHANGED, BT_STATE_OFF), ), TEST_CASE_BREDRLE("Gatt Server - LE Disconnect", ACTION_SUCCESS(bluetooth_enable_action, NULL), CALLBACK_STATE(CB_BT_ADAPTER_STATE_CHANGED, BT_STATE_ON), ACTION_SUCCESS(emu_set_ssp_mode_action, NULL), ACTION_SUCCESS(emu_set_connect_cb_action, gatt_conn_cb), ACTION_SUCCESS(gatt_server_register_action, &app1_uuid), CALLBACK_STATUS(CB_GATTS_REGISTER_SERVER, BT_STATUS_SUCCESS), ACTION_SUCCESS(bt_start_discovery_action, NULL), CALLBACK_STATE(CB_BT_DISCOVERY_STATE_CHANGED, BT_DISCOVERY_STARTED), ACTION_SUCCESS(emu_setup_powered_remote_action, NULL), CALLBACK_DEVICE_FOUND(prop_emu_remotes_default_le_set, 2), ACTION_SUCCESS(bt_cancel_discovery_action, NULL), ACTION_SUCCESS(gatt_server_connect_action, &app1_conn_req), CALLBACK_GATTS_CONNECTION(GATT_SERVER_CONNECTED, prop_emu_remotes_default_set, CONN1_ID, APP1_ID), ACTION_SUCCESS(gatt_server_disconnect_action, &app1_conn_req), CALLBACK_GATTS_CONNECTION(GATT_SERVER_DISCONNECTED, prop_emu_remotes_default_set, CONN1_ID, APP1_ID), ACTION_SUCCESS(bluetooth_disable_action, NULL), CALLBACK_STATE(CB_BT_ADAPTER_STATE_CHANGED, BT_STATE_OFF), ), TEST_CASE_BREDRLE("Gatt Server - LE Multiple Server Conn./Disc", ACTION_SUCCESS(bluetooth_enable_action, NULL), CALLBACK_STATE(CB_BT_ADAPTER_STATE_CHANGED, BT_STATE_ON), ACTION_SUCCESS(emu_set_ssp_mode_action, NULL), ACTION_SUCCESS(emu_set_connect_cb_action, gatt_conn_cb), ACTION_SUCCESS(gatt_server_register_action, &app1_uuid), CALLBACK_STATUS(CB_GATTS_REGISTER_SERVER, BT_STATUS_SUCCESS), ACTION_SUCCESS(gatt_server_register_action, &app2_uuid), CALLBACK_STATUS(CB_GATTS_REGISTER_SERVER, BT_STATUS_SUCCESS), ACTION_SUCCESS(bt_start_discovery_action, NULL), CALLBACK_STATE(CB_BT_DISCOVERY_STATE_CHANGED, BT_DISCOVERY_STARTED), ACTION_SUCCESS(emu_setup_powered_remote_action, NULL), CALLBACK_DEVICE_FOUND(prop_emu_remotes_default_le_set, 2), ACTION_SUCCESS(bt_cancel_discovery_action, NULL), ACTION_SUCCESS(gatt_server_connect_action, &app1_conn_req), CALLBACK_GATTS_CONNECTION(GATT_SERVER_CONNECTED, prop_emu_remotes_default_set, CONN1_ID, APP1_ID), ACTION_SUCCESS(gatt_server_connect_action, &app2_conn_req), CALLBACK_GATTS_CONNECTION(GATT_SERVER_CONNECTED, prop_emu_remotes_default_set, CONN2_ID, APP2_ID), ACTION_SUCCESS(gatt_server_disconnect_action, &app2_conn_req), CALLBACK_GATTS_CONNECTION(GATT_SERVER_DISCONNECTED, prop_emu_remotes_default_set, CONN2_ID, APP2_ID), ACTION_SUCCESS(gatt_server_disconnect_action, &app1_conn_req), CALLBACK_GATTS_CONNECTION(GATT_SERVER_DISCONNECTED, prop_emu_remotes_default_set, CONN1_ID, APP1_ID), ACTION_SUCCESS(bluetooth_disable_action, NULL), CALLBACK_STATE(CB_BT_ADAPTER_STATE_CHANGED, BT_STATE_OFF), ), TEST_CASE_BREDRLE("Gatt Server - Add Single Service Successful", ACTION_SUCCESS(gatt_server_register_action, &app1_uuid), CALLBACK_STATUS(CB_GATTS_REGISTER_SERVER, BT_STATUS_SUCCESS), ACTION_SUCCESS(gatt_server_add_service_action, &add_service_data_1), CALLBACK_GATTS_SERVICE_ADDED(GATT_STATUS_SUCCESS, APP1_ID, &service_add_1, NULL, NULL), ), TEST_CASE_BREDRLE("Gatt Server - Add Multiple Services Successful", ACTION_SUCCESS(gatt_server_register_action, &app1_uuid), CALLBACK_STATUS(CB_GATTS_REGISTER_SERVER, BT_STATUS_SUCCESS), ACTION_SUCCESS(gatt_server_add_service_action, &add_service_data_1), CALLBACK_GATTS_SERVICE_ADDED(GATT_STATUS_SUCCESS, APP1_ID, &service_add_1, NULL, NULL), ACTION_SUCCESS(gatt_server_add_service_action, &add_service_data_2), CALLBACK_GATTS_SERVICE_ADDED(GATT_STATUS_SUCCESS, APP1_ID, &service_add_2, NULL, NULL), ACTION_SUCCESS(gatt_server_add_service_action, &add_service_data_3), CALLBACK_GATTS_SERVICE_ADDED(GATT_STATUS_SUCCESS, APP1_ID, &service_add_3, NULL, NULL), ), TEST_CASE_BREDRLE("Gatt Server - Add Service with 0 handles", ACTION_SUCCESS(gatt_server_register_action, &app1_uuid), CALLBACK_STATUS(CB_GATTS_REGISTER_SERVER, BT_STATUS_SUCCESS), ACTION_FAIL(gatt_server_add_service_action, &add_bad_service_data_1), CALLBACK_GATTS_SERVICE_ADDED(GATT_STATUS_FAILURE, APP1_ID, &service_add_1, NULL, NULL), ), TEST_CASE_BREDRLE("Gatt Server - Add Secondary Service", ACTION_SUCCESS(gatt_server_register_action, &app1_uuid), CALLBACK_STATUS(CB_GATTS_REGISTER_SERVER, BT_STATUS_SUCCESS), ACTION_SUCCESS(gatt_server_add_service_action, &add_sec_service_data_1), CALLBACK_GATTS_SERVICE_ADDED(GATT_STATUS_SUCCESS, APP1_ID, &included_1, NULL, NULL), ), TEST_CASE_BREDRLE("Gatt Server - Add Included Service Successful", ACTION_SUCCESS(gatt_server_register_action, &app1_uuid), CALLBACK_STATUS(CB_GATTS_REGISTER_SERVER, BT_STATUS_SUCCESS), ACTION_SUCCESS(gatt_server_add_service_action, &add_service_data_4), CALLBACK_GATTS_SERVICE_ADDED(GATT_STATUS_SUCCESS, APP1_ID, &service_add_1, NULL, &srvc1_handle), ACTION_SUCCESS(gatt_server_add_service_action, &add_service_data_4), CALLBACK_GATTS_SERVICE_ADDED(GATT_STATUS_SUCCESS, APP1_ID, &service_add_1, NULL, &inc_srvc1_handle), ACTION_SUCCESS(gatt_server_add_inc_service_action, &add_inc_service_data_1), CALLBACK_GATTS_INC_SERVICE_ADDED(GATT_STATUS_SUCCESS, APP1_ID, &srvc1_handle, NULL), ), TEST_CASE_BREDRLE("Gatt Server - Add Inc. Service with wrong handle", ACTION_SUCCESS(gatt_server_register_action, &app1_uuid), CALLBACK_STATUS(CB_GATTS_REGISTER_SERVER, BT_STATUS_SUCCESS), ACTION_SUCCESS(gatt_server_add_service_action, &add_service_data_4), CALLBACK_GATTS_SERVICE_ADDED(GATT_STATUS_SUCCESS, APP1_ID, &service_add_1, NULL, &srvc1_handle), ACTION_SUCCESS(gatt_server_add_service_action, &add_service_data_4), CALLBACK_GATTS_SERVICE_ADDED(GATT_STATUS_SUCCESS, APP1_ID, &service_add_1, NULL, NULL), ACTION_FAIL(gatt_server_add_inc_service_action, &add_bad_inc_service_data_1), CALLBACK_GATTS_INC_SERVICE_ADDED(GATT_STATUS_FAILURE, APP1_ID, &srvc1_handle, NULL), ), TEST_CASE_BREDRLE("Gatt Server - Add Single Characteristic Successful", ACTION_SUCCESS(gatt_server_register_action, &app1_uuid), CALLBACK_STATUS(CB_GATTS_REGISTER_SERVER, BT_STATUS_SUCCESS), ACTION_SUCCESS(gatt_server_add_service_action, &add_service_data_5), CALLBACK_GATTS_SERVICE_ADDED(GATT_STATUS_SUCCESS, APP1_ID, &service_add_1, NULL, &srvc1_handle), ACTION_SUCCESS(gatt_server_add_char_action, &add_char_data_1), CALLBACK_GATTS_CHARACTERISTIC_ADDED(GATT_STATUS_SUCCESS, APP1_ID, &app1_uuid, &srvc1_handle, NULL, NULL), ), TEST_CASE_BREDRLE("Gatt Server - Add Char. wrong service handle", ACTION_SUCCESS(gatt_server_register_action, &app1_uuid), CALLBACK_STATUS(CB_GATTS_REGISTER_SERVER, BT_STATUS_SUCCESS), ACTION_SUCCESS(gatt_server_add_service_action, &add_service_data_5), CALLBACK_GATTS_SERVICE_ADDED(GATT_STATUS_SUCCESS, APP1_ID, &service_add_1, NULL, &srvc1_handle), ACTION_FAIL(gatt_server_add_char_action, &add_bad_char_data_1), CALLBACK_GATTS_CHARACTERISTIC_ADDED(GATT_STATUS_FAILURE, APP1_ID, &app1_uuid, NULL, NULL, NULL), ), TEST_CASE_BREDRLE("Gatt Server - Add Single Descriptor Successful", ACTION_SUCCESS(gatt_server_register_action, &app1_uuid), CALLBACK_STATUS(CB_GATTS_REGISTER_SERVER, BT_STATUS_SUCCESS), ACTION_SUCCESS(gatt_server_add_service_action, &add_service_data_6), CALLBACK_GATTS_SERVICE_ADDED(GATT_STATUS_SUCCESS, APP1_ID, &service_add_1, NULL, &srvc1_handle), ACTION_SUCCESS(gatt_server_add_char_action, &add_char_data_1), CALLBACK_GATTS_CHARACTERISTIC_ADDED(GATT_STATUS_SUCCESS, APP1_ID, &app1_uuid, &srvc1_handle, NULL, NULL), ACTION_SUCCESS(gatt_server_add_desc_action, &add_desc_data_1), CALLBACK_GATTS_DESCRIPTOR_ADDED(GATT_STATUS_SUCCESS, APP1_ID, &app2_uuid, &srvc1_handle, NULL, NULL), ), TEST_CASE_BREDRLE("Gatt Server - Add Desc. wrong service handle", ACTION_SUCCESS(gatt_server_register_action, &app1_uuid), CALLBACK_STATUS(CB_GATTS_REGISTER_SERVER, BT_STATUS_SUCCESS), ACTION_SUCCESS(gatt_server_add_service_action, &add_service_data_6), CALLBACK_GATTS_SERVICE_ADDED(GATT_STATUS_SUCCESS, APP1_ID, &service_add_1, NULL, &srvc1_handle), ACTION_SUCCESS(gatt_server_add_char_action, &add_char_data_1), CALLBACK_GATTS_CHARACTERISTIC_ADDED(GATT_STATUS_SUCCESS, APP1_ID, &app1_uuid, &srvc1_handle, NULL, NULL), ACTION_FAIL(gatt_server_add_desc_action, &add_bad_desc_data_1), CALLBACK_GATTS_DESCRIPTOR_ADDED(GATT_STATUS_FAILURE, APP1_ID, &app2_uuid, NULL, NULL, NULL), ), TEST_CASE_BREDRLE("Gatt Server - Add Desc. wrong app ID", ACTION_SUCCESS(gatt_server_register_action, &app1_uuid), CALLBACK_STATUS(CB_GATTS_REGISTER_SERVER, BT_STATUS_SUCCESS), ACTION_SUCCESS(gatt_server_add_service_action, &add_service_data_6), CALLBACK_GATTS_SERVICE_ADDED(GATT_STATUS_SUCCESS, APP1_ID, &service_add_1, NULL, &srvc1_handle), ACTION_SUCCESS(gatt_server_add_char_action, &add_char_data_1), CALLBACK_GATTS_CHARACTERISTIC_ADDED(GATT_STATUS_SUCCESS, APP1_ID, &app1_uuid, &srvc1_handle, NULL, NULL), ACTION_FAIL(gatt_server_add_desc_action, &add_bad_desc_data_2), CALLBACK_GATTS_DESCRIPTOR_ADDED(GATT_STATUS_FAILURE, APP2_ID, &app2_uuid, NULL, NULL, NULL), ), TEST_CASE_BREDRLE("Gatt Server - Start Service Successful BREDRLE", ACTION_SUCCESS(gatt_server_register_action, &app1_uuid), CALLBACK_STATUS(CB_GATTS_REGISTER_SERVER, BT_STATUS_SUCCESS), ACTION_SUCCESS(gatt_server_add_service_action, &add_service_data_1), CALLBACK_GATTS_SERVICE_ADDED(GATT_STATUS_SUCCESS, APP1_ID, &service_add_1, NULL, &srvc1_handle), ACTION_SUCCESS(gatt_server_start_srvc_action, &start_srvc_data_1), CALLBACK_GATTS_SERVICE_STARTED(GATT_STATUS_SUCCESS, APP1_ID, &srvc1_handle), ), TEST_CASE_BREDRLE("Gatt Server - Start Service Successful LE", ACTION_SUCCESS(gatt_server_register_action, &app1_uuid), CALLBACK_STATUS(CB_GATTS_REGISTER_SERVER, BT_STATUS_SUCCESS), ACTION_SUCCESS(gatt_server_add_service_action, &add_service_data_1), CALLBACK_GATTS_SERVICE_ADDED(GATT_STATUS_SUCCESS, APP1_ID, &service_add_1, NULL, &srvc1_handle), ACTION_SUCCESS(gatt_server_start_srvc_action, &start_srvc_data_2), CALLBACK_GATTS_SERVICE_STARTED(GATT_STATUS_SUCCESS, APP1_ID, &srvc1_handle), ), TEST_CASE_BREDRLE("Gatt Server - Start Service wrong service handle", ACTION_SUCCESS(gatt_server_register_action, &app1_uuid), CALLBACK_STATUS(CB_GATTS_REGISTER_SERVER, BT_STATUS_SUCCESS), ACTION_SUCCESS(gatt_server_add_service_action, &add_service_data_1), CALLBACK_GATTS_SERVICE_ADDED(GATT_STATUS_SUCCESS, APP1_ID, &service_add_1, NULL, NULL), ACTION_FAIL(gatt_server_start_srvc_action, &start_bad_srvc_data_1), CALLBACK_GATTS_SERVICE_STARTED(GATT_STATUS_FAILURE, APP1_ID, NULL), ), TEST_CASE_BREDRLE("Gatt Server - Start Service wrong server transport", ACTION_SUCCESS(gatt_server_register_action, &app1_uuid), CALLBACK_STATUS(CB_GATTS_REGISTER_SERVER, BT_STATUS_SUCCESS), ACTION_SUCCESS(gatt_server_add_service_action, &add_service_data_1), CALLBACK_GATTS_SERVICE_ADDED(GATT_STATUS_SUCCESS, APP1_ID, &service_add_1, NULL, &srvc1_handle), ACTION_FAIL(gatt_server_start_srvc_action, &start_bad_srvc_data_2), CALLBACK_GATTS_SERVICE_STARTED(GATT_STATUS_FAILURE, APP1_ID, &srvc1_handle), ), TEST_CASE_BREDRLE("Gatt Server - Stop Service Successful", ACTION_SUCCESS(gatt_server_register_action, &app1_uuid), CALLBACK_STATUS(CB_GATTS_REGISTER_SERVER, BT_STATUS_SUCCESS), ACTION_SUCCESS(gatt_server_add_service_action, &add_service_data_1), CALLBACK_GATTS_SERVICE_ADDED(GATT_STATUS_SUCCESS, APP1_ID, &service_add_1, NULL, &srvc1_handle), ACTION_SUCCESS(gatt_server_start_srvc_action, &start_srvc_data_1), CALLBACK_GATTS_SERVICE_STARTED(GATT_STATUS_SUCCESS, APP1_ID, &srvc1_handle), ACTION_SUCCESS(gatt_server_stop_srvc_action, &stop_srvc_data_1), CALLBACK_GATTS_SERVICE_STOPPED(GATT_STATUS_SUCCESS, APP1_ID, &srvc1_handle), ), TEST_CASE_BREDRLE("Gatt Server - Stop Service wrong service handle", ACTION_SUCCESS(gatt_server_register_action, &app1_uuid), CALLBACK_STATUS(CB_GATTS_REGISTER_SERVER, BT_STATUS_SUCCESS), ACTION_SUCCESS(gatt_server_add_service_action, &add_service_data_1), CALLBACK_GATTS_SERVICE_ADDED(GATT_STATUS_SUCCESS, APP1_ID, &service_add_1, NULL, &srvc1_handle), ACTION_SUCCESS(gatt_server_start_srvc_action, &start_srvc_data_1), CALLBACK_GATTS_SERVICE_STARTED(GATT_STATUS_SUCCESS, APP1_ID, &srvc1_handle), ACTION_FAIL(gatt_server_stop_srvc_action, &stop_bad_srvc_data_1), CALLBACK_GATTS_SERVICE_STOPPED(GATT_STATUS_FAILURE, APP1_ID, NULL), ), TEST_CASE_BREDRLE("Gatt Server - Delete Service Successful", ACTION_SUCCESS(gatt_server_register_action, &app1_uuid), CALLBACK_STATUS(CB_GATTS_REGISTER_SERVER, BT_STATUS_SUCCESS), ACTION_SUCCESS(gatt_server_add_service_action, &add_service_data_1), CALLBACK_GATTS_SERVICE_ADDED(GATT_STATUS_SUCCESS, APP1_ID, &service_add_1, NULL, &srvc1_handle), ACTION_SUCCESS(gatt_server_delete_srvc_action, &delete_srvc_data_1), CALLBACK_GATTS_SERVICE_DELETED(GATT_STATUS_SUCCESS, APP1_ID, &srvc1_handle), ), TEST_CASE_BREDRLE("Gatt Server - Delete Service wrong handle", ACTION_SUCCESS(gatt_server_register_action, &app1_uuid), CALLBACK_STATUS(CB_GATTS_REGISTER_SERVER, BT_STATUS_SUCCESS), ACTION_SUCCESS(gatt_server_add_service_action, &add_service_data_1), CALLBACK_GATTS_SERVICE_ADDED(GATT_STATUS_SUCCESS, APP1_ID, &service_add_1, NULL, &srvc1_handle), ACTION_FAIL(gatt_server_delete_srvc_action, &delete_bad_srvc_data_1), CALLBACK_GATTS_SERVICE_DELETED(GATT_STATUS_FAILURE, APP1_ID, NULL), ), TEST_CASE_BREDRLE("Gatt Server - Send Indication", ACTION_SUCCESS(init_pdus, send_indication_1), ACTION_SUCCESS(bluetooth_enable_action, NULL), CALLBACK_STATE(CB_BT_ADAPTER_STATE_CHANGED, BT_STATE_ON), ACTION_SUCCESS(emu_set_ssp_mode_action, NULL), ACTION_SUCCESS(emu_set_connect_cb_action, gatt_conn_cb), ACTION_SUCCESS(gatt_server_register_action, &app1_uuid), CALLBACK_STATUS(CB_GATTS_REGISTER_SERVER, BT_STATUS_SUCCESS), ACTION_SUCCESS(bt_start_discovery_action, NULL), CALLBACK_STATE(CB_BT_DISCOVERY_STATE_CHANGED, BT_DISCOVERY_STARTED), ACTION_SUCCESS(emu_setup_powered_remote_action, NULL), CALLBACK_DEVICE_FOUND(prop_emu_remotes_default_le_set, 2), ACTION_SUCCESS(bt_cancel_discovery_action, NULL), ACTION_SUCCESS(gatt_server_connect_action, &app1_conn_req), CALLBACK_GATTS_CONNECTION(GATT_SERVER_CONNECTED, prop_emu_remotes_default_set, CONN1_ID, APP1_ID), ACTION_SUCCESS(gatt_server_send_indication_action, &send_indication_data_1), CALLBACK(CB_EMU_VALUE_INDICATION), CALLBACK_GATTS_NOTIF_CONF(CONN1_ID, GATT_STATUS_SUCCESS), ACTION_SUCCESS(bluetooth_disable_action, NULL), CALLBACK_STATE(CB_BT_ADAPTER_STATE_CHANGED, BT_STATE_OFF), ), TEST_CASE_BREDRLE("Gatt Server - Send Notification", ACTION_SUCCESS(init_pdus, send_notification_1), ACTION_SUCCESS(bluetooth_enable_action, NULL), CALLBACK_STATE(CB_BT_ADAPTER_STATE_CHANGED, BT_STATE_ON), ACTION_SUCCESS(emu_set_ssp_mode_action, NULL), ACTION_SUCCESS(emu_set_connect_cb_action, gatt_conn_cb), ACTION_SUCCESS(gatt_server_register_action, &app1_uuid), CALLBACK_STATUS(CB_GATTS_REGISTER_SERVER, BT_STATUS_SUCCESS), ACTION_SUCCESS(bt_start_discovery_action, NULL), CALLBACK_STATE(CB_BT_DISCOVERY_STATE_CHANGED, BT_DISCOVERY_STARTED), ACTION_SUCCESS(emu_setup_powered_remote_action, NULL), CALLBACK_DEVICE_FOUND(prop_emu_remotes_default_le_set, 2), ACTION_SUCCESS(bt_cancel_discovery_action, NULL), ACTION_SUCCESS(gatt_server_connect_action, &app1_conn_req), CALLBACK_GATTS_CONNECTION(GATT_SERVER_CONNECTED, prop_emu_remotes_default_set, CONN1_ID, APP1_ID), ACTION_SUCCESS(gatt_server_send_indication_action, &send_indication_data_2), CALLBACK_GATTS_NOTIF_CONF(CONN1_ID, GATT_STATUS_SUCCESS), CALLBACK(CB_EMU_VALUE_NOTIFICATION), ACTION_SUCCESS(bluetooth_disable_action, NULL), CALLBACK_STATE(CB_BT_ADAPTER_STATE_CHANGED, BT_STATE_OFF), ), TEST_CASE_BREDRLE("Gatt Server - Send Notification, wrong conn id", ACTION_SUCCESS(bluetooth_enable_action, NULL), CALLBACK_STATE(CB_BT_ADAPTER_STATE_CHANGED, BT_STATE_ON), ACTION_SUCCESS(emu_set_ssp_mode_action, NULL), ACTION_SUCCESS(emu_set_connect_cb_action, gatt_conn_cb), ACTION_SUCCESS(gatt_server_register_action, &app1_uuid), CALLBACK_STATUS(CB_GATTS_REGISTER_SERVER, BT_STATUS_SUCCESS), ACTION_SUCCESS(bt_start_discovery_action, NULL), CALLBACK_STATE(CB_BT_DISCOVERY_STATE_CHANGED, BT_DISCOVERY_STARTED), ACTION_SUCCESS(emu_setup_powered_remote_action, NULL), CALLBACK_DEVICE_FOUND(prop_emu_remotes_default_le_set, 2), ACTION_SUCCESS(bt_cancel_discovery_action, NULL), ACTION_SUCCESS(gatt_server_connect_action, &app1_conn_req), CALLBACK_GATTS_CONNECTION(GATT_SERVER_CONNECTED, prop_emu_remotes_default_set, CONN1_ID, APP1_ID), ACTION_FAIL(gatt_server_send_indication_action, &send_bad_indication_data_1), ACTION_SUCCESS(bluetooth_disable_action, NULL), CALLBACK_STATE(CB_BT_ADAPTER_STATE_CHANGED, BT_STATE_OFF), ), TEST_CASE_BREDRLE("Gatt Server - Send response to read char request", ACTION_SUCCESS(bluetooth_enable_action, NULL), CALLBACK_STATE(CB_BT_ADAPTER_STATE_CHANGED, BT_STATE_ON), ACTION_SUCCESS(emu_set_ssp_mode_action, NULL), ACTION_SUCCESS(emu_set_connect_cb_action, gatt_conn_cb), ACTION_SUCCESS(gatt_server_register_action, &app1_uuid), CALLBACK_STATUS(CB_GATTS_REGISTER_SERVER, BT_STATUS_SUCCESS), ACTION_SUCCESS(gatt_server_add_service_action, &add_service_data_5), CALLBACK_GATTS_SERVICE_ADDED(GATT_STATUS_SUCCESS, APP1_ID, &service_add_1, NULL, &srvc1_handle), ACTION_SUCCESS(gatt_server_add_char_action, &add_char_data_1), CALLBACK_GATTS_CHARACTERISTIC_ADDED(GATT_STATUS_SUCCESS, APP1_ID, &app1_uuid, &srvc1_handle, NULL, &char1_handle), ACTION_SUCCESS(gatt_server_start_srvc_action, &start_srvc_data_2), CALLBACK_GATTS_SERVICE_STARTED(GATT_STATUS_SUCCESS, APP1_ID, &srvc1_handle), ACTION_SUCCESS(bt_start_discovery_action, NULL), CALLBACK_STATE(CB_BT_DISCOVERY_STATE_CHANGED, BT_DISCOVERY_STARTED), ACTION_SUCCESS(emu_setup_powered_remote_action, NULL), CALLBACK_DEVICE_FOUND(prop_emu_remotes_default_le_set, 2), ACTION_SUCCESS(bt_cancel_discovery_action, NULL), ACTION_SUCCESS(gatt_server_connect_action, &app1_conn_req), CALLBACK_GATTS_CONNECTION(GATT_SERVER_CONNECTED, prop_emu_remotes_default_set, CONN1_ID, APP1_ID), PROCESS_DATA(GATT_STATUS_SUCCESS, gatt_remote_send_raw_pdu_action, &att_read_req_op_v, &char1_handle_v, NULL), CALLBACK_GATTS_REQUEST_READ(CONN1_ID, TRANS1_ID, prop_emu_remotes_default_set, &char1_handle, 0, false), ACTION_SUCCESS(gatt_server_send_response_action, &send_resp_data_1), CALLBACK(CB_EMU_READ_RESPONSE), ACTION_SUCCESS(bluetooth_disable_action, NULL), CALLBACK_STATE(CB_BT_ADAPTER_STATE_CHANGED, BT_STATE_OFF), ), TEST_CASE_BREDRLE("Gatt Server - Send response to write char request", ACTION_SUCCESS(bluetooth_enable_action, NULL), CALLBACK_STATE(CB_BT_ADAPTER_STATE_CHANGED, BT_STATE_ON), ACTION_SUCCESS(emu_set_ssp_mode_action, NULL), ACTION_SUCCESS(emu_set_connect_cb_action, gatt_conn_cb), ACTION_SUCCESS(gatt_server_register_action, &app1_uuid), CALLBACK_STATUS(CB_GATTS_REGISTER_SERVER, BT_STATUS_SUCCESS), ACTION_SUCCESS(gatt_server_add_service_action, &add_service_data_5), CALLBACK_GATTS_SERVICE_ADDED(GATT_STATUS_SUCCESS, APP1_ID, &service_add_1, NULL, &srvc1_handle), ACTION_SUCCESS(gatt_server_add_char_action, &add_char_data_2), CALLBACK_GATTS_CHARACTERISTIC_ADDED(GATT_STATUS_SUCCESS, APP1_ID, &app1_uuid, &srvc1_handle, NULL, &char1_handle), ACTION_SUCCESS(gatt_server_start_srvc_action, &start_srvc_data_2), CALLBACK_GATTS_SERVICE_STARTED(GATT_STATUS_SUCCESS, APP1_ID, &srvc1_handle), ACTION_SUCCESS(bt_start_discovery_action, NULL), CALLBACK_STATE(CB_BT_DISCOVERY_STATE_CHANGED, BT_DISCOVERY_STARTED), ACTION_SUCCESS(emu_setup_powered_remote_action, NULL), CALLBACK_DEVICE_FOUND(prop_emu_remotes_default_le_set, 2), ACTION_SUCCESS(bt_cancel_discovery_action, NULL), ACTION_SUCCESS(gatt_server_connect_action, &app1_conn_req), CALLBACK_GATTS_CONNECTION(GATT_SERVER_CONNECTED, prop_emu_remotes_default_set, CONN1_ID, APP1_ID), PROCESS_DATA(GATT_STATUS_SUCCESS, gatt_remote_send_raw_pdu_action, &att_write_req_op_v, &char1_handle_v, &att_write_req_value_1_v), CALLBACK_GATTS_REQUEST_WRITE(CONN1_ID, TRANS1_ID, prop_emu_remotes_default_set, &char1_handle, 0, sizeof(att_write_req_value_1), true, false, att_write_req_value_1), ACTION_SUCCESS(gatt_server_send_response_action, &send_resp_data_2), CALLBACK(CB_EMU_WRITE_RESPONSE), ACTION_SUCCESS(bluetooth_disable_action, NULL), CALLBACK_STATE(CB_BT_ADAPTER_STATE_CHANGED, BT_STATE_OFF), ), TEST_CASE_BREDRLE("Gatt Server - Find By Type - Attribute not found", ACTION_SUCCESS(bluetooth_enable_action, NULL), CALLBACK_STATE(CB_BT_ADAPTER_STATE_CHANGED, BT_STATE_ON), ACTION_SUCCESS(emu_set_ssp_mode_action, NULL), ACTION_SUCCESS(emu_set_connect_cb_action, gatt_conn_cb), ACTION_SUCCESS(gatt_server_register_action, &app1_uuid), CALLBACK_STATUS(CB_GATTS_REGISTER_SERVER, BT_STATUS_SUCCESS), ACTION_SUCCESS(gatt_server_add_service_action, &add_service_data_5), CALLBACK_GATTS_SERVICE_ADDED(GATT_STATUS_SUCCESS, APP1_ID, &service_add_1, NULL, &srvc1_handle), ACTION_SUCCESS(gatt_server_add_char_action, &add_char_data_2), CALLBACK_GATTS_CHARACTERISTIC_ADDED(GATT_STATUS_SUCCESS, APP1_ID, &app1_uuid, &srvc1_handle, NULL, &char1_handle), ACTION_SUCCESS(gatt_server_start_srvc_action, &start_srvc_data_2), CALLBACK_GATTS_SERVICE_STARTED(GATT_STATUS_SUCCESS, APP1_ID, &srvc1_handle), ACTION_SUCCESS(bt_start_discovery_action, NULL), CALLBACK_STATE(CB_BT_DISCOVERY_STATE_CHANGED, BT_DISCOVERY_STARTED), ACTION_SUCCESS(emu_setup_powered_remote_action, NULL), CALLBACK_DEVICE_FOUND(prop_emu_remotes_default_le_set, 2), ACTION_SUCCESS(bt_cancel_discovery_action, NULL), ACTION_SUCCESS(gatt_server_connect_action, &app1_conn_req), CALLBACK_GATTS_CONNECTION(GATT_SERVER_CONNECTED, prop_emu_remotes_default_set, CONN1_ID, APP1_ID), PROCESS_DATA(GATT_STATUS_SUCCESS, gatt_remote_send_raw_pdu_action, &att_find_by_type_req_op_v, &search_range_1, &primary_type), CALLBACK_ERROR(CB_EMU_ATT_ERROR, 0x0a), ACTION_SUCCESS(bluetooth_disable_action, NULL), CALLBACK_STATE(CB_BT_ADAPTER_STATE_CHANGED, BT_STATE_OFF), ), /* This tests embeded ccc */ TEST_CASE_BREDRLE("Gatt Server - Srvc change write req. success", ACTION_SUCCESS(bluetooth_enable_action, NULL), CALLBACK_STATE(CB_BT_ADAPTER_STATE_CHANGED, BT_STATE_ON), ACTION_SUCCESS(emu_set_ssp_mode_action, NULL), ACTION_SUCCESS(emu_set_connect_cb_action, gatt_conn_cb), ACTION_SUCCESS(gatt_server_register_action, &app1_uuid), CALLBACK_STATUS(CB_GATTS_REGISTER_SERVER, BT_STATUS_SUCCESS), ACTION_SUCCESS(bt_start_discovery_action, NULL), CALLBACK_STATE(CB_BT_DISCOVERY_STATE_CHANGED, BT_DISCOVERY_STARTED), ACTION_SUCCESS(emu_setup_powered_remote_action, NULL), CALLBACK_DEVICE_FOUND(prop_emu_remotes_default_le_set, 2), ACTION_SUCCESS(bt_cancel_discovery_action, NULL), ACTION_SUCCESS(gatt_server_connect_action, &app1_conn_req), CALLBACK_GATTS_CONNECTION(GATT_SERVER_CONNECTED, prop_emu_remotes_default_set, CONN1_ID, APP1_ID), /* For CCC we need to be bonded */ ACTION_SUCCESS(bt_create_bond_action, &prop_test_remote_ble_bdaddr_req), CALLBACK_BOND_STATE(BT_BOND_STATE_BONDED, &prop_emu_remotes_default_set[0], 1), /* Write and receive confirmation */ PROCESS_DATA(GATT_STATUS_SUCCESS, gatt_remote_send_raw_pdu_action, &att_write_req_op_v, &svc_change_ccc_handle_v, &svc_change_ccc_value_v), CALLBACK(CB_EMU_WRITE_RESPONSE), /* Shutdown */ ACTION_SUCCESS(bluetooth_disable_action, NULL), CALLBACK_STATE(CB_BT_ADAPTER_STATE_CHANGED, BT_STATE_OFF), ), TEST_CASE_BREDRLE("Gatt Server - Send error resp to write char request", ACTION_SUCCESS(bluetooth_enable_action, NULL), CALLBACK_STATE(CB_BT_ADAPTER_STATE_CHANGED, BT_STATE_ON), ACTION_SUCCESS(emu_set_ssp_mode_action, NULL), ACTION_SUCCESS(emu_set_connect_cb_action, gatt_conn_cb), ACTION_SUCCESS(gatt_server_register_action, &app1_uuid), CALLBACK_STATUS(CB_GATTS_REGISTER_SERVER, BT_STATUS_SUCCESS), ACTION_SUCCESS(gatt_server_add_service_action, &add_service_data_5), CALLBACK_GATTS_SERVICE_ADDED(GATT_STATUS_SUCCESS, APP1_ID, &service_add_1, NULL, &srvc1_handle), ACTION_SUCCESS(gatt_server_add_char_action, &add_char_data_2), CALLBACK_GATTS_CHARACTERISTIC_ADDED(GATT_STATUS_SUCCESS, APP1_ID, &app1_uuid, &srvc1_handle, NULL, &char1_handle), ACTION_SUCCESS(gatt_server_start_srvc_action, &start_srvc_data_2), CALLBACK_GATTS_SERVICE_STARTED(GATT_STATUS_SUCCESS, APP1_ID, &srvc1_handle), ACTION_SUCCESS(bt_start_discovery_action, NULL), CALLBACK_STATE(CB_BT_DISCOVERY_STATE_CHANGED, BT_DISCOVERY_STARTED), ACTION_SUCCESS(emu_setup_powered_remote_action, NULL), CALLBACK_DEVICE_FOUND(prop_emu_remotes_default_le_set, 2), ACTION_SUCCESS(bt_cancel_discovery_action, NULL), ACTION_SUCCESS(gatt_server_connect_action, &app1_conn_req), CALLBACK_GATTS_CONNECTION(GATT_SERVER_CONNECTED, prop_emu_remotes_default_set, CONN1_ID, APP1_ID), PROCESS_DATA(GATT_STATUS_SUCCESS, gatt_remote_send_raw_pdu_action, &att_write_req_op_v, &char1_handle_v, &att_write_req_value_1_v), CALLBACK_GATTS_REQUEST_WRITE(CONN1_ID, TRANS1_ID, prop_emu_remotes_default_set, &char1_handle, 0, sizeof(att_write_req_value_1), true, false, att_write_req_value_1), ACTION_SUCCESS(gatt_server_send_response_action, &send_resp_data_2_error), CALLBACK_ERROR(CB_EMU_ATT_ERROR, GATT_ERR_INVAL_ATTR_VALUE_LEN), ACTION_SUCCESS(bluetooth_disable_action, NULL), CALLBACK_STATE(CB_BT_ADAPTER_STATE_CHANGED, BT_STATE_OFF), ), }; struct queue *get_gatt_tests(void) { uint16_t i = 0; list = queue_new(); for (; i < sizeof(test_cases) / sizeof(test_cases[0]); ++i) queue_push_tail(list, &test_cases[i]); return list; } void remove_gatt_tests(void) { queue_destroy(list, NULL); } bluez-5.82/android/PaxHeaders/hal-a2dp.c0000644000000000000000000000005014015011623015013 xustar0020 atime=1743516862 20 ctime=1743591276 bluez-5.82/android/hal-a2dp.c0000644000000000000000000000634714015011623014506 0ustar00rootroot// SPDX-License-Identifier: Apache-2.0 /* * Copyright (C) 2013 Intel Corporation * */ #include #include #include #include "hal-log.h" #include "hal.h" #include "hal-msg.h" #include "hal-ipc.h" static const btav_callbacks_t *cbs = NULL; static bool interface_ready(void) { return cbs != NULL; } static void handle_conn_state(void *buf, uint16_t len, int fd) { struct hal_ev_a2dp_conn_state *ev = buf; if (cbs->connection_state_cb) cbs->connection_state_cb(ev->state, (bt_bdaddr_t *) (ev->bdaddr)); } static void handle_audio_state(void *buf, uint16_t len, int fd) { struct hal_ev_a2dp_audio_state *ev = buf; if (cbs->audio_state_cb) cbs->audio_state_cb(ev->state, (bt_bdaddr_t *)(ev->bdaddr)); } static void handle_audio_config(void *buf, uint16_t len, int fd) { #if ANDROID_VERSION >= PLATFORM_VER(5, 0, 0) struct hal_ev_a2dp_audio_config *ev = buf; if (cbs->audio_config_cb) cbs->audio_config_cb((bt_bdaddr_t *)(ev->bdaddr), ev->sample_rate, ev->channel_count); #endif } /* * handlers will be called from notification thread context, * index in table equals to 'opcode - HAL_MINIMUM_EVENT' */ static const struct hal_ipc_handler ev_handlers[] = { /* HAL_EV_A2DP_CONN_STATE */ { handle_conn_state, false, sizeof(struct hal_ev_a2dp_conn_state) }, /* HAL_EV_A2DP_AUDIO_STATE */ { handle_audio_state, false, sizeof(struct hal_ev_a2dp_audio_state) }, /* HAL_EV_A2DP_AUDIO_CONFIG */ { handle_audio_config, false, sizeof(struct hal_ev_a2dp_audio_config) }, }; static bt_status_t a2dp_connect(bt_bdaddr_t *bd_addr) { struct hal_cmd_a2dp_connect cmd; DBG(""); if (!interface_ready()) return BT_STATUS_NOT_READY; memcpy(cmd.bdaddr, bd_addr, sizeof(cmd.bdaddr)); return hal_ipc_cmd(HAL_SERVICE_ID_A2DP, HAL_OP_A2DP_CONNECT, sizeof(cmd), &cmd, NULL, NULL, NULL); } static bt_status_t disconnect(bt_bdaddr_t *bd_addr) { struct hal_cmd_a2dp_disconnect cmd; DBG(""); if (!interface_ready()) return BT_STATUS_NOT_READY; memcpy(cmd.bdaddr, bd_addr, sizeof(cmd.bdaddr)); return hal_ipc_cmd(HAL_SERVICE_ID_A2DP, HAL_OP_A2DP_DISCONNECT, sizeof(cmd), &cmd, NULL, NULL, NULL); } static bt_status_t init(btav_callbacks_t *callbacks) { struct hal_cmd_register_module cmd; int ret; DBG(""); if (interface_ready()) return BT_STATUS_DONE; cbs = callbacks; hal_ipc_register(HAL_SERVICE_ID_A2DP, ev_handlers, sizeof(ev_handlers)/sizeof(ev_handlers[0])); cmd.service_id = HAL_SERVICE_ID_A2DP; cmd.mode = HAL_MODE_DEFAULT; cmd.max_clients = 1; ret = hal_ipc_cmd(HAL_SERVICE_ID_CORE, HAL_OP_REGISTER_MODULE, sizeof(cmd), &cmd, NULL, NULL, NULL); if (ret != BT_STATUS_SUCCESS) { cbs = NULL; hal_ipc_unregister(HAL_SERVICE_ID_A2DP); } return ret; } static void cleanup(void) { struct hal_cmd_unregister_module cmd; DBG(""); if (!interface_ready()) return; cmd.service_id = HAL_SERVICE_ID_A2DP; hal_ipc_cmd(HAL_SERVICE_ID_CORE, HAL_OP_UNREGISTER_MODULE, sizeof(cmd), &cmd, NULL, NULL, NULL); hal_ipc_unregister(HAL_SERVICE_ID_A2DP); cbs = NULL; } static btav_interface_t iface = { .size = sizeof(iface), .init = init, .connect = a2dp_connect, .disconnect = disconnect, .cleanup = cleanup }; btav_interface_t *bt_get_a2dp_interface(void) { return &iface; } bluez-5.82/android/PaxHeaders/avctp.c0000644000000000000000000000005014667536076014573 xustar0020 atime=1743516866 20 ctime=1743591278 bluez-5.82/android/avctp.c0000644000000000000000000010621314667536076014257 0ustar00rootroot// SPDX-License-Identifier: GPL-2.0-or-later /* * * BlueZ - Bluetooth protocol stack for Linux * * Copyright (C) 2006-2010 Nokia Corporation * Copyright (C) 2004-2010 Marcel Holtmann * Copyright (C) 2011 Texas Instruments, Inc. * * */ #ifdef HAVE_CONFIG_H #include #endif #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "lib/sdp.h" #include "src/shared/util.h" #include "src/log.h" #include "avctp.h" /* * AV/C Panel 1.23, page 76: * command with the pressed value is valid for two seconds */ #define AVC_PRESS_TIMEOUT 2 #define QUIRK_NO_RELEASE 1 << 0 /* Message types */ #define AVCTP_COMMAND 0 #define AVCTP_RESPONSE 1 /* Packet types */ #define AVCTP_PACKET_SINGLE 0 #define AVCTP_PACKET_START 1 #define AVCTP_PACKET_CONTINUE 2 #define AVCTP_PACKET_END 3 #if __BYTE_ORDER == __LITTLE_ENDIAN struct avctp_header { uint8_t ipid:1; uint8_t cr:1; uint8_t packet_type:2; uint8_t transaction:4; uint16_t pid; } __attribute__ ((packed)); struct avc_header { uint8_t code:4; uint8_t _hdr0:4; uint8_t subunit_id:3; uint8_t subunit_type:5; uint8_t opcode; } __attribute__ ((packed)); #elif __BYTE_ORDER == __BIG_ENDIAN struct avctp_header { uint8_t transaction:4; uint8_t packet_type:2; uint8_t cr:1; uint8_t ipid:1; uint16_t pid; } __attribute__ ((packed)); struct avc_header { uint8_t _hdr0:4; uint8_t code:4; uint8_t subunit_type:5; uint8_t subunit_id:3; uint8_t opcode; } __attribute__ ((packed)); #else #error "Unknown byte order" #endif struct avctp_control_req { struct avctp_pending_req *p; uint8_t code; uint8_t subunit; uint8_t op; struct iovec *iov; int iov_cnt; avctp_rsp_cb func; void *user_data; }; struct avctp_browsing_req { struct avctp_pending_req *p; struct iovec *iov; int iov_cnt; avctp_browsing_rsp_cb func; void *user_data; }; typedef int (*avctp_process_cb) (void *data); struct avctp_pending_req { struct avctp_channel *chan; uint8_t transaction; guint timeout; int err; avctp_process_cb process; void *data; avctp_destroy_cb_t destroy; }; struct avctp_channel { struct avctp *session; GIOChannel *io; uint8_t transaction; guint watch; uint16_t imtu; uint16_t omtu; uint8_t *buffer; GSList *handlers; struct avctp_pending_req *p; GQueue *queue; GSList *processed; guint process_id; avctp_destroy_cb_t destroy; }; struct key_pressed { uint8_t op; uint8_t *params; size_t params_len; guint timer; }; struct avctp { unsigned int ref; int uinput; unsigned int passthrough_id; unsigned int unit_id; unsigned int subunit_id; struct avctp_channel *control; struct avctp_channel *browsing; struct avctp_passthrough_handler *handler; uint8_t key_quirks[256]; struct key_pressed key; uint16_t version; avctp_destroy_cb_t destroy; void *data; }; struct avctp_passthrough_handler { avctp_passthrough_cb cb; void *user_data; unsigned int id; }; struct avctp_pdu_handler { uint8_t opcode; avctp_control_pdu_cb cb; void *user_data; unsigned int id; }; struct avctp_browsing_pdu_handler { avctp_browsing_pdu_cb cb; void *user_data; unsigned int id; avctp_destroy_cb_t destroy; }; static struct { const char *name; uint8_t avc; uint16_t uinput; } key_map[] = { { "SELECT", AVC_SELECT, KEY_SELECT }, { "UP", AVC_UP, KEY_UP }, { "DOWN", AVC_DOWN, KEY_DOWN }, { "LEFT", AVC_LEFT, KEY_LEFT }, { "RIGHT", AVC_RIGHT, KEY_RIGHT }, { "ROOT MENU", AVC_ROOT_MENU, KEY_MENU }, { "CONTENTS MENU", AVC_CONTENTS_MENU, KEY_PROGRAM }, { "FAVORITE MENU", AVC_FAVORITE_MENU, KEY_FAVORITES }, { "EXIT", AVC_EXIT, KEY_EXIT }, { "ON DEMAND MENU", AVC_ON_DEMAND_MENU, KEY_MENU }, { "APPS MENU", AVC_APPS_MENU, KEY_MENU }, { "0", AVC_0, KEY_0 }, { "1", AVC_1, KEY_1 }, { "2", AVC_2, KEY_2 }, { "3", AVC_3, KEY_3 }, { "4", AVC_4, KEY_4 }, { "5", AVC_5, KEY_5 }, { "6", AVC_6, KEY_6 }, { "7", AVC_7, KEY_7 }, { "8", AVC_8, KEY_8 }, { "9", AVC_9, KEY_9 }, { "DOT", AVC_DOT, KEY_DOT }, { "ENTER", AVC_ENTER, KEY_ENTER }, { "CHANNEL UP", AVC_CHANNEL_UP, KEY_CHANNELUP }, { "CHANNEL DOWN", AVC_CHANNEL_DOWN, KEY_CHANNELDOWN }, { "CHANNEL PREVIOUS", AVC_CHANNEL_PREVIOUS, KEY_LAST }, { "INPUT SELECT", AVC_INPUT_SELECT, KEY_CONFIG }, { "INFO", AVC_INFO, KEY_INFO }, { "HELP", AVC_HELP, KEY_HELP }, { "POWER", AVC_POWER, KEY_POWER2 }, { "VOLUME UP", AVC_VOLUME_UP, KEY_VOLUMEUP }, { "VOLUME DOWN", AVC_VOLUME_DOWN, KEY_VOLUMEDOWN }, { "MUTE", AVC_MUTE, KEY_MUTE }, { "PLAY", AVC_PLAY, KEY_PLAYCD }, { "STOP", AVC_STOP, KEY_STOPCD }, { "PAUSE", AVC_PAUSE, KEY_PAUSECD }, { "FORWARD", AVC_FORWARD, KEY_NEXTSONG }, { "BACKWARD", AVC_BACKWARD, KEY_PREVIOUSSONG }, { "RECORD", AVC_RECORD, KEY_RECORD }, { "REWIND", AVC_REWIND, KEY_REWIND }, { "FAST FORWARD", AVC_FAST_FORWARD, KEY_FASTFORWARD }, { "LIST", AVC_LIST, KEY_LIST }, { "F1", AVC_F1, KEY_F1 }, { "F2", AVC_F2, KEY_F2 }, { "F3", AVC_F3, KEY_F3 }, { "F4", AVC_F4, KEY_F4 }, { "F5", AVC_F5, KEY_F5 }, { "F6", AVC_F6, KEY_F6 }, { "F7", AVC_F7, KEY_F7 }, { "F8", AVC_F8, KEY_F8 }, { "F9", AVC_F9, KEY_F9 }, { "RED", AVC_RED, KEY_RED }, { "GREEN", AVC_GREEN, KEY_GREEN }, { "BLUE", AVC_BLUE, KEY_BLUE }, { "YELLOW", AVC_YELLOW, KEY_YELLOW }, { NULL } }; static gboolean process_queue(gpointer user_data); static gboolean avctp_passthrough_rsp(struct avctp *session, uint8_t code, uint8_t subunit, uint8_t *operands, size_t operand_count, void *user_data); static int send_event(int fd, uint16_t type, uint16_t code, int32_t value) { struct input_event event; int err; memset(&event, 0, sizeof(event)); event.type = type; event.code = code; event.value = value; do { err = write(fd, &event, sizeof(event)); } while (err < 0 && errno == EINTR); if (err < 0) { err = -errno; error("send_event: %s (%d)", strerror(-err), -err); } return err; } static void send_key(int fd, uint16_t key, int pressed) { send_event(fd, EV_KEY, key, pressed); send_event(fd, EV_SYN, SYN_REPORT, 0); } static gboolean auto_release(gpointer user_data) { struct avctp *session = user_data; session->key.timer = 0; DBG("AV/C: key press timeout"); send_key(session->uinput, session->key.op, 0); return FALSE; } static ssize_t handle_panel_passthrough(struct avctp *session, uint8_t transaction, uint8_t *code, uint8_t *subunit, uint8_t *operands, size_t operand_count, void *user_data) { struct avctp_passthrough_handler *handler = session->handler; const char *status; int pressed, i; if (*code != AVC_CTYPE_CONTROL || *subunit != AVC_SUBUNIT_PANEL) { *code = AVC_CTYPE_REJECTED; return operand_count; } if (operand_count == 0) goto done; if (operands[0] & 0x80) { status = "released"; pressed = 0; } else { status = "pressed"; pressed = 1; } if (session->key.timer == 0 && handler != NULL) { if (handler->cb(session, operands[0] & 0x7F, pressed, handler->user_data)) goto done; } if (session->uinput < 0) { DBG("AV/C: uinput not initialized"); *code = AVC_CTYPE_NOT_IMPLEMENTED; return 0; } for (i = 0; key_map[i].name != NULL; i++) { uint8_t key_quirks; if ((operands[0] & 0x7F) != key_map[i].avc) continue; DBG("AV/C: %s %s", key_map[i].name, status); key_quirks = session->key_quirks[key_map[i].avc]; if (key_quirks & QUIRK_NO_RELEASE) { if (!pressed) { DBG("AV/C: Ignoring release"); break; } DBG("AV/C: treating key press as press + release"); send_key(session->uinput, key_map[i].uinput, 1); send_key(session->uinput, key_map[i].uinput, 0); break; } if (pressed) { if (session->key.timer > 0) { g_source_remove(session->key.timer); send_key(session->uinput, session->key.op, 0); } session->key.op = key_map[i].uinput; session->key.timer = g_timeout_add_seconds( AVC_PRESS_TIMEOUT, auto_release, session); } else if (session->key.timer > 0) { g_source_remove(session->key.timer); session->key.timer = 0; } send_key(session->uinput, key_map[i].uinput, pressed); break; } if (key_map[i].name == NULL) { DBG("AV/C: unknown button 0x%02X %s", operands[0] & 0x7F, status); *code = AVC_CTYPE_NOT_IMPLEMENTED; return operand_count; } done: *code = AVC_CTYPE_ACCEPTED; return operand_count; } static ssize_t handle_unit_info(struct avctp *session, uint8_t transaction, uint8_t *code, uint8_t *subunit, uint8_t *operands, size_t operand_count, void *user_data) { if (*code != AVC_CTYPE_STATUS) { *code = AVC_CTYPE_REJECTED; return 0; } *code = AVC_CTYPE_STABLE; /* * The first operand should be 0x07 for the UNITINFO response. * Neither AVRCP (section 22.1, page 117) nor AVC Digital * Interface Command Set (section 9.2.1, page 45) specs * explain this value but both use it */ if (operand_count >= 1) operands[0] = 0x07; if (operand_count >= 2) operands[1] = AVC_SUBUNIT_PANEL << 3; DBG("reply to AVC_OP_UNITINFO"); return operand_count; } static ssize_t handle_subunit_info(struct avctp *session, uint8_t transaction, uint8_t *code, uint8_t *subunit, uint8_t *operands, size_t operand_count, void *user_data) { if (*code != AVC_CTYPE_STATUS) { *code = AVC_CTYPE_REJECTED; return 0; } *code = AVC_CTYPE_STABLE; /* * The first operand should be 0x07 for the UNITINFO response. * Neither AVRCP (section 22.1, page 117) nor AVC Digital * Interface Command Set (section 9.2.1, page 45) specs * explain this value but both use it */ if (operand_count >= 2) operands[1] = AVC_SUBUNIT_PANEL << 3; DBG("reply to AVC_OP_SUBUNITINFO"); return operand_count; } static struct avctp_pdu_handler *find_handler(GSList *list, uint8_t opcode) { for (; list; list = list->next) { struct avctp_pdu_handler *handler = list->data; if (handler->opcode == opcode) return handler; } return NULL; } static void pending_destroy(gpointer data, gpointer user_data) { struct avctp_pending_req *req = data; if (req->destroy) req->destroy(req->data); if (req->timeout > 0) g_source_remove(req->timeout); g_free(req); } static void avctp_channel_destroy(struct avctp_channel *chan) { g_io_channel_shutdown(chan->io, TRUE, NULL); g_io_channel_unref(chan->io); if (chan->watch) g_source_remove(chan->watch); if (chan->p) pending_destroy(chan->p, NULL); if (chan->process_id > 0) g_source_remove(chan->process_id); if (chan->destroy) chan->destroy(chan); g_free(chan->buffer); g_queue_foreach(chan->queue, pending_destroy, NULL); g_queue_free(chan->queue); g_slist_foreach(chan->processed, pending_destroy, NULL); g_slist_free(chan->processed); g_slist_free_full(chan->handlers, g_free); g_free(chan); } static int avctp_send(struct avctp_channel *control, uint8_t transaction, uint8_t cr, uint8_t code, uint8_t subunit, uint8_t opcode, const struct iovec *iov, int iov_cnt) { struct avctp_header avctp; struct avc_header avc; struct msghdr msg; int sk, err = 0; struct iovec pdu[iov_cnt + 2]; int i; size_t len = sizeof(avctp) + sizeof(avc); DBG(""); pdu[0].iov_base = &avctp; pdu[0].iov_len = sizeof(avctp); pdu[1].iov_base = &avc; pdu[1].iov_len = sizeof(avc); for (i = 0; i < iov_cnt; i++) { pdu[i + 2].iov_base = iov[i].iov_base; pdu[i + 2].iov_len = iov[i].iov_len; len += iov[i].iov_len; } if (control->omtu < len) return -EOVERFLOW; sk = g_io_channel_unix_get_fd(control->io); memset(&avctp, 0, sizeof(avctp)); avctp.transaction = transaction; avctp.packet_type = AVCTP_PACKET_SINGLE; avctp.cr = cr; avctp.pid = htons(AV_REMOTE_SVCLASS_ID); memset(&avc, 0, sizeof(avc)); avc.code = code; avc.subunit_type = subunit; avc.opcode = opcode; memset(&msg, 0, sizeof(msg)); msg.msg_iov = pdu; msg.msg_iovlen = iov_cnt + 2; if (sendmsg(sk, &msg, 0) < 0) err = -errno; return err; } static int avctp_browsing_send(struct avctp_channel *browsing, uint8_t transaction, uint8_t cr, const struct iovec *iov, int iov_cnt) { struct avctp_header avctp; struct msghdr msg; struct iovec pdu[iov_cnt + 1]; int sk, err = 0; int i; size_t len = sizeof(avctp); for (i = 0; i < iov_cnt; i++) { pdu[i + 1].iov_base = iov[i].iov_base; pdu[i + 1].iov_len = iov[i].iov_len; len += iov[i].iov_len; } pdu[0].iov_base = &avctp; pdu[0].iov_len = sizeof(avctp); if (browsing->omtu < len) return -EOVERFLOW; sk = g_io_channel_unix_get_fd(browsing->io); memset(&avctp, 0, sizeof(avctp)); avctp.transaction = transaction; avctp.packet_type = AVCTP_PACKET_SINGLE; avctp.cr = cr; avctp.pid = htons(AV_REMOTE_SVCLASS_ID); memset(&msg, 0, sizeof(msg)); msg.msg_iov = pdu; msg.msg_iovlen = iov_cnt + 1; if (sendmsg(sk, &msg, 0) < 0) err = -errno; return err; } static void control_req_destroy(void *data) { struct avctp_control_req *req = data; struct avctp_pending_req *p = req->p; struct avctp *session = p->chan->session; int i; if (p->err == 0 || req->func == NULL) goto done; req->func(session, AVC_CTYPE_REJECTED, req->subunit, NULL, 0, req->user_data); done: for (i = 0; i < req->iov_cnt; i++) g_free(req->iov[i].iov_base); g_free(req->iov); g_free(req); } static void browsing_req_destroy(void *data) { struct avctp_browsing_req *req = data; struct avctp_pending_req *p = req->p; struct avctp *session = p->chan->session; int i; if (p->err == 0 || req->func == NULL) goto done; req->func(session, NULL, 0, req->user_data); done: for (i = 0; i < req->iov_cnt; i++) g_free(req->iov[i].iov_base); g_free(req->iov); g_free(req); } static gboolean req_timeout(gpointer user_data) { struct avctp_channel *chan = user_data; struct avctp_pending_req *p = chan->p; DBG("transaction %u", p->transaction); p->timeout = 0; p->err = -ETIMEDOUT; pending_destroy(p, NULL); chan->p = NULL; if (chan->process_id == 0) chan->process_id = g_idle_add(process_queue, chan); return FALSE; } static int process_control(void *data) { struct avctp_control_req *req = data; struct avctp_pending_req *p = req->p; return avctp_send(p->chan, p->transaction, AVCTP_COMMAND, req->code, req->subunit, req->op, req->iov, req->iov_cnt); } static int process_browsing(void *data) { struct avctp_browsing_req *req = data; struct avctp_pending_req *p = req->p; return avctp_browsing_send(p->chan, p->transaction, AVCTP_COMMAND, req->iov, req->iov_cnt); } static gboolean process_queue(void *user_data) { struct avctp_channel *chan = user_data; struct avctp_pending_req *p = chan->p; chan->process_id = 0; if (p != NULL) return FALSE; while ((p = g_queue_pop_head(chan->queue))) { if (p->process(p->data) == 0) break; pending_destroy(p, NULL); } if (p == NULL) return FALSE; chan->p = p; p->timeout = g_timeout_add_seconds(2, req_timeout, chan); return FALSE; } static struct avctp *avctp_ref(struct avctp *session) { __sync_fetch_and_add(&session->ref, 1); DBG("%p: ref=%d", session, session->ref); return session; } static void avctp_unref(struct avctp *session) { DBG("%p: ref=%d", session, session->ref); if (__sync_sub_and_fetch(&session->ref, 1)) return; if (session->browsing) avctp_channel_destroy(session->browsing); if (session->control) avctp_channel_destroy(session->control); if (session->destroy) session->destroy(session->data); g_free(session->handler); if (session->key.timer > 0) g_source_remove(session->key.timer); if (session->uinput >= 0) { DBG("AVCTP: closing uinput"); ioctl(session->uinput, UI_DEV_DESTROY); close(session->uinput); session->uinput = -1; } g_free(session); } static void control_response(struct avctp_channel *control, struct avctp_header *avctp, struct avc_header *avc, uint8_t *operands, size_t operand_count) { struct avctp_pending_req *p = control->p; struct avctp_control_req *req; GSList *l; if (p && p->transaction == avctp->transaction) { control->processed = g_slist_prepend(control->processed, p); if (p->timeout > 0) { g_source_remove(p->timeout); p->timeout = 0; } control->p = NULL; if (control->process_id == 0) control->process_id = g_idle_add(process_queue, control); } avctp_ref(control->session); for (l = control->processed; l; l = l->next) { p = l->data; req = p->data; if (p->transaction != avctp->transaction) continue; if (req->func && req->func(control->session, avc->code, avc->subunit_type, operands, operand_count, req->user_data)) break; control->processed = g_slist_remove(control->processed, p); pending_destroy(p, NULL); break; } avctp_unref(control->session); } static void browsing_response(struct avctp_channel *browsing, struct avctp_header *avctp, uint8_t *operands, size_t operand_count) { struct avctp_pending_req *p = browsing->p; struct avctp_browsing_req *req; GSList *l; if (p && p->transaction == avctp->transaction) { browsing->processed = g_slist_prepend(browsing->processed, p); if (p->timeout > 0) { g_source_remove(p->timeout); p->timeout = 0; } browsing->p = NULL; if (browsing->process_id == 0) browsing->process_id = g_idle_add(process_queue, browsing); } avctp_ref(browsing->session); for (l = browsing->processed; l; l = l->next) { p = l->data; req = p->data; if (p->transaction != avctp->transaction) continue; if (req->func && req->func(browsing->session, operands, operand_count, req->user_data)) break; browsing->processed = g_slist_remove(browsing->processed, p); pending_destroy(p, NULL); break; } avctp_unref(browsing->session); } static gboolean session_browsing_cb(GIOChannel *chan, GIOCondition cond, gpointer data) { struct avctp *session = data; struct avctp_channel *browsing = session->browsing; uint8_t *buf = browsing->buffer; uint8_t *operands; struct avctp_header *avctp; int sock, ret, packet_size, operand_count; struct avctp_browsing_pdu_handler *handler; if (cond & (G_IO_ERR | G_IO_HUP | G_IO_NVAL)) goto failed; sock = g_io_channel_unix_get_fd(chan); ret = read(sock, buf, browsing->imtu); if (ret <= 0) goto failed; if (ret < AVCTP_HEADER_LENGTH) { error("Too small AVCTP packet"); goto failed; } avctp = (struct avctp_header *) buf; if (avctp->packet_type != AVCTP_PACKET_SINGLE) { error("Invalid packet type"); goto failed; } operands = buf + AVCTP_HEADER_LENGTH; ret -= AVCTP_HEADER_LENGTH; operand_count = ret; if (avctp->cr == AVCTP_RESPONSE) { browsing_response(browsing, avctp, operands, operand_count); return TRUE; } packet_size = AVCTP_HEADER_LENGTH; avctp->cr = AVCTP_RESPONSE; handler = g_slist_nth_data(browsing->handlers, 0); if (handler == NULL) { DBG("handler not found"); /* FIXME: Add general reject */ /* packet_size += avrcp_browsing_general_reject(operands); */ goto send; } ret = handler->cb(session, avctp->transaction, operands, operand_count, handler->user_data); if (ret < 0) { if (ret == -EAGAIN) return TRUE; goto failed; } packet_size += ret; send: if (packet_size != 0) { ret = write(sock, buf, packet_size); if (ret != packet_size) goto failed; } return TRUE; failed: DBG("AVCTP Browsing: disconnected"); avctp_channel_destroy(session->browsing); session->browsing = NULL; return FALSE; } static gboolean session_cb(GIOChannel *chan, GIOCondition cond, gpointer data) { struct avctp *session = data; struct avctp_channel *control = session->control; uint8_t *buf = control->buffer; uint8_t *operands, code, subunit; struct avctp_header *avctp; struct avc_header *avc; int packet_size, operand_count, sock; struct avctp_pdu_handler *handler; ssize_t ret; if (cond & (G_IO_ERR | G_IO_HUP | G_IO_NVAL)) goto failed; sock = g_io_channel_unix_get_fd(chan); ret = read(sock, buf, control->imtu); if (ret <= 0) goto failed; if (ret < AVCTP_HEADER_LENGTH) { error("Too small AVCTP packet"); goto failed; } avctp = (struct avctp_header *) buf; ret -= AVCTP_HEADER_LENGTH; if (ret < AVC_HEADER_LENGTH) { error("Too small AVC packet"); goto failed; } avc = (struct avc_header *) (buf + AVCTP_HEADER_LENGTH); ret -= AVC_HEADER_LENGTH; operands = (uint8_t *) avc + AVC_HEADER_LENGTH; operand_count = ret; if (avctp->cr == AVCTP_RESPONSE) { control_response(control, avctp, avc, operands, operand_count); return TRUE; } packet_size = AVCTP_HEADER_LENGTH + AVC_HEADER_LENGTH; avctp->cr = AVCTP_RESPONSE; if (avctp->packet_type != AVCTP_PACKET_SINGLE) { avc->code = AVC_CTYPE_NOT_IMPLEMENTED; goto done; } if (avctp->pid != htons(AV_REMOTE_SVCLASS_ID)) { avctp->ipid = 1; packet_size = AVCTP_HEADER_LENGTH; goto done; } handler = find_handler(control->handlers, avc->opcode); if (!handler) { DBG("handler not found for 0x%02x", avc->opcode); avc->code = AVC_CTYPE_REJECTED; goto done; } code = avc->code; subunit = avc->subunit_type; ret = handler->cb(session, avctp->transaction, &code, &subunit, operands, operand_count, handler->user_data); if (ret < 0) { if (ret == -EAGAIN) return TRUE; goto failed; } packet_size += ret; avc->code = code; avc->subunit_type = subunit; done: ret = write(sock, buf, packet_size); if (ret != packet_size) goto failed; return TRUE; failed: DBG("AVCTP session %p got disconnected", session); avctp_shutdown(session); return FALSE; } static int uinput_create(const char *name) { struct uinput_user_dev dev; int fd, err, i; fd = open("/dev/uinput", O_RDWR); if (fd < 0) { fd = open("/dev/input/uinput", O_RDWR); if (fd < 0) { fd = open("/dev/misc/uinput", O_RDWR); if (fd < 0) { err = -errno; error("Can't open input device: %s (%d)", strerror(-err), -err); return err; } } } memset(&dev, 0, sizeof(dev)); if (name) strncpy(dev.name, name, UINPUT_MAX_NAME_SIZE - 1); dev.id.bustype = BUS_BLUETOOTH; dev.id.vendor = 0x0000; dev.id.product = 0x0000; dev.id.version = 0x0000; if (write(fd, &dev, sizeof(dev)) < 0) { err = -errno; error("Can't write device information: %s (%d)", strerror(-err), -err); close(fd); return err; } ioctl(fd, UI_SET_EVBIT, EV_KEY); ioctl(fd, UI_SET_EVBIT, EV_REL); ioctl(fd, UI_SET_EVBIT, EV_REP); ioctl(fd, UI_SET_EVBIT, EV_SYN); for (i = 0; key_map[i].name != NULL; i++) ioctl(fd, UI_SET_KEYBIT, key_map[i].uinput); if (ioctl(fd, UI_DEV_CREATE, NULL) < 0) { err = -errno; error("Can't create uinput device: %s (%d)", strerror(-err), -err); close(fd); return err; } return fd; } int avctp_init_uinput(struct avctp *session, const char *name, const char *address) { if (g_str_equal(name, "Nokia CK-20W")) { session->key_quirks[AVC_FORWARD] |= QUIRK_NO_RELEASE; session->key_quirks[AVC_BACKWARD] |= QUIRK_NO_RELEASE; session->key_quirks[AVC_PLAY] |= QUIRK_NO_RELEASE; session->key_quirks[AVC_PAUSE] |= QUIRK_NO_RELEASE; } session->uinput = uinput_create(address); if (session->uinput < 0) { error("AVCTP: failed to init uinput for %s", address); return session->uinput; } return 0; } static struct avctp_channel *avctp_channel_create(struct avctp *session, int fd, size_t imtu, size_t omtu, avctp_destroy_cb_t destroy) { struct avctp_channel *chan; chan = g_new0(struct avctp_channel, 1); chan->session = session; chan->io = g_io_channel_unix_new(fd); chan->queue = g_queue_new(); chan->imtu = imtu; chan->omtu = omtu; chan->buffer = g_malloc0(MAX(imtu, omtu)); chan->destroy = destroy; return chan; } static void handler_free(void *data) { struct avctp_browsing_pdu_handler *handler = data; if (handler->destroy) handler->destroy(handler->user_data); g_free(data); } static void avctp_destroy_browsing(void *data) { struct avctp_channel *chan = data; g_slist_free_full(chan->handlers, handler_free); chan->handlers = NULL; } static struct avctp_pending_req *pending_create(struct avctp_channel *chan, avctp_process_cb process, void *data, avctp_destroy_cb_t destroy) { struct avctp_pending_req *p; GSList *l, *tmp; if (!chan->processed) goto done; tmp = g_slist_copy(chan->processed); /* Find first unused transaction id */ for (l = tmp; l; l = g_slist_next(l)) { struct avctp_pending_req *req = l->data; if (req->transaction == chan->transaction) { chan->transaction++; chan->transaction %= 16; tmp = g_slist_delete_link(tmp, l); l = tmp; } } g_slist_free(tmp); done: p = g_new0(struct avctp_pending_req, 1); p->chan = chan; p->transaction = chan->transaction; p->process = process; p->data = data; p->destroy = destroy; chan->transaction++; chan->transaction %= 16; return p; } static int avctp_send_req(struct avctp *session, uint8_t code, uint8_t subunit, uint8_t opcode, const struct iovec *iov, int iov_cnt, avctp_rsp_cb func, void *user_data) { struct avctp_channel *control = session->control; struct avctp_pending_req *p; struct avctp_control_req *req; struct iovec *pdu; int i; if (control == NULL) return -ENOTCONN; pdu = g_new0(struct iovec, iov_cnt); for (i = 0; i < iov_cnt; i++) { pdu[i].iov_len = iov[i].iov_len; pdu[i].iov_base = util_memdup(iov[i].iov_base, iov[i].iov_len); } req = g_new0(struct avctp_control_req, 1); req->code = code; req->subunit = subunit; req->op = opcode; req->func = func; req->iov = pdu; req->iov_cnt = iov_cnt; req->user_data = user_data; p = pending_create(control, process_control, req, control_req_destroy); req->p = p; g_queue_push_tail(control->queue, p); if (control->process_id == 0) control->process_id = g_idle_add(process_queue, control); return 0; } int avctp_send_browsing_req(struct avctp *session, const struct iovec *iov, int iov_cnt, avctp_browsing_rsp_cb func, void *user_data) { struct avctp_channel *browsing = session->browsing; struct avctp_pending_req *p; struct avctp_browsing_req *req; struct iovec *pdu; int i; if (browsing == NULL) return -ENOTCONN; pdu = g_new0(struct iovec, iov_cnt); for (i = 0; i < iov_cnt; i++) { pdu[i].iov_len = iov[i].iov_len; pdu[i].iov_base = util_memdup(iov[i].iov_base, iov[i].iov_len); } req = g_new0(struct avctp_browsing_req, 1); req->func = func; req->iov = pdu; req->iov_cnt = iov_cnt; req->user_data = user_data; p = pending_create(browsing, process_browsing, req, browsing_req_destroy); req->p = p; g_queue_push_tail(browsing->queue, p); /* Connection did not complete, delay process of the request */ if (browsing->watch == 0) return 0; if (browsing->process_id == 0) browsing->process_id = g_idle_add(process_queue, browsing); return 0; } int avctp_send_browsing(struct avctp *session, uint8_t transaction, const struct iovec *iov, int iov_cnt) { struct avctp_channel *browsing = session->browsing; if (browsing == NULL) return -ENOTCONN; return avctp_browsing_send(browsing, transaction, AVCTP_RESPONSE, iov, iov_cnt); } static const char *op2str(uint8_t op) { int i; for (i = 0; key_map[i].name != NULL; i++) { if ((op & 0x7F) == key_map[i].avc) return key_map[i].name; } return "UNKNOWN"; } static int avctp_passthrough_press(struct avctp *session, uint8_t op, uint8_t *params, size_t params_len) { struct iovec iov[2]; int iov_cnt; uint8_t operands[2]; DBG("%s", op2str(op)); iov[0].iov_base = operands; iov[0].iov_len = sizeof(operands); /* Button pressed */ operands[0] = op & 0x7f; if (params_len > 0) { iov[1].iov_base = params; iov[1].iov_len = params_len; iov_cnt = 2; operands[1] = params_len; } else { iov_cnt = 1; operands[1] = 0; } return avctp_send_req(session, AVC_CTYPE_CONTROL, AVC_SUBUNIT_PANEL, AVC_OP_PASSTHROUGH, iov, iov_cnt, avctp_passthrough_rsp, NULL); } static int avctp_passthrough_release(struct avctp *session, uint8_t op, uint8_t *params, size_t params_len) { struct iovec iov[2]; int iov_cnt; uint8_t operands[2]; DBG("%s", op2str(op)); iov[0].iov_base = operands; iov[0].iov_len = sizeof(operands); /* Button released */ operands[0] = op | 0x80; if (params_len > 0) { iov[1].iov_base = params; iov[1].iov_len = params_len; iov_cnt = 2; operands[1] = params_len; } else { iov_cnt = 1; operands[1] = 0; } return avctp_send_req(session, AVC_CTYPE_CONTROL, AVC_SUBUNIT_PANEL, AVC_OP_PASSTHROUGH, iov, iov_cnt, NULL, NULL); } static gboolean repeat_timeout(gpointer user_data) { struct avctp *session = user_data; avctp_passthrough_release(session, session->key.op, session->key.params, session->key.params_len); avctp_passthrough_press(session, session->key.op, session->key.params, session->key.params_len); return TRUE; } static void release_pressed(struct avctp *session) { avctp_passthrough_release(session, session->key.op, session->key.params, session->key.params_len); if (session->key.timer > 0) g_source_remove(session->key.timer); session->key.timer = 0; } static bool set_pressed(struct avctp *session, uint8_t op, uint8_t *params, size_t params_len) { if (session->key.timer > 0) { if (session->key.op == op) return TRUE; release_pressed(session); } if (op != AVC_FAST_FORWARD && op != AVC_REWIND) return FALSE; session->key.op = op; session->key.params = params; session->key.params_len = params_len; session->key.timer = g_timeout_add_seconds(AVC_PRESS_TIMEOUT, repeat_timeout, session); return TRUE; } static gboolean avctp_passthrough_rsp(struct avctp *session, uint8_t code, uint8_t subunit, uint8_t *operands, size_t operand_count, void *user_data) { uint8_t *params; size_t params_len; DBG("code 0x%02x operand_count %zd", code, operand_count); if (code != AVC_CTYPE_ACCEPTED) return FALSE; if (operands[0] == AVC_VENDOR_UNIQUE) { params = &operands[2]; params_len = operand_count - 2; } else { params = NULL; params_len = 0; } if (set_pressed(session, operands[0], params, params_len)) return FALSE; avctp_passthrough_release(session, operands[0], params, params_len); return FALSE; } int avctp_send_passthrough(struct avctp *session, uint8_t op, uint8_t *params, size_t params_len) { /* Auto release if key pressed */ if (session->key.timer > 0) release_pressed(session); return avctp_passthrough_press(session, op, params, params_len); } int avctp_send_vendor(struct avctp *session, uint8_t transaction, uint8_t code, uint8_t subunit, const struct iovec *iov, int iov_cnt) { struct avctp_channel *control = session->control; if (control == NULL) return -ENOTCONN; return avctp_send(control, transaction, AVCTP_RESPONSE, code, subunit, AVC_OP_VENDORDEP, iov, iov_cnt); } int avctp_send_vendor_req(struct avctp *session, uint8_t code, uint8_t subunit, const struct iovec *iov, int iov_cnt, avctp_rsp_cb func, void *user_data) { return avctp_send_req(session, code, subunit, AVC_OP_VENDORDEP, iov, iov_cnt, func, user_data); } unsigned int avctp_register_passthrough_handler(struct avctp *session, avctp_passthrough_cb cb, void *user_data) { struct avctp_channel *control = session->control; struct avctp_passthrough_handler *handler; static unsigned int id = 0; if (control == NULL || session->handler != NULL) return 0; handler = g_new(struct avctp_passthrough_handler, 1); handler->cb = cb; handler->user_data = user_data; handler->id = ++id; session->handler = handler; return handler->id; } bool avctp_unregister_passthrough_handler(struct avctp *session, unsigned int id) { if (session->handler == NULL) return false; if (session->handler->id != id) return false; g_free(session->handler); session->handler = NULL; return true; } unsigned int avctp_register_pdu_handler(struct avctp *session, uint8_t opcode, avctp_control_pdu_cb cb, void *user_data) { struct avctp_channel *control = session->control; struct avctp_pdu_handler *handler; static unsigned int id = 0; if (control == NULL) return 0; handler = find_handler(control->handlers, opcode); if (handler) return 0; handler = g_new(struct avctp_pdu_handler, 1); handler->opcode = opcode; handler->cb = cb; handler->user_data = user_data; handler->id = ++id; control->handlers = g_slist_append(control->handlers, handler); return handler->id; } unsigned int avctp_register_browsing_pdu_handler(struct avctp *session, avctp_browsing_pdu_cb cb, void *user_data, avctp_destroy_cb_t destroy) { struct avctp_channel *browsing = session->browsing; struct avctp_browsing_pdu_handler *handler; static unsigned int id = 0; if (browsing == NULL) return 0; if (browsing->handlers != NULL) return 0; handler = g_new(struct avctp_browsing_pdu_handler, 1); handler->cb = cb; handler->user_data = user_data; handler->id = ++id; handler->destroy = destroy; browsing->handlers = g_slist_append(browsing->handlers, handler); return handler->id; } bool avctp_unregister_pdu_handler(struct avctp *session, unsigned int id) { struct avctp_channel *control = session->control; GSList *l; if (!control) return false; for (l = control->handlers; l; l = g_slist_next(l)) { struct avctp_pdu_handler *handler = l->data; if (handler->id != id) continue; control->handlers = g_slist_remove(control->handlers, handler); g_free(handler); return true; } return false; } bool avctp_unregister_browsing_pdu_handler(struct avctp *session, unsigned int id) { struct avctp_channel *browsing = session->browsing; GSList *l; if (browsing == NULL) return false; for (l = browsing->handlers; l; l = g_slist_next(l)) { struct avctp_browsing_pdu_handler *handler = l->data; if (handler->id != id) continue; browsing->handlers = g_slist_remove(browsing->handlers, handler); g_free(handler); return true; } return false; } struct avctp *avctp_new(int fd, size_t imtu, size_t omtu, uint16_t version) { struct avctp *session; struct avctp_channel *control; GIOCondition cond = G_IO_IN | G_IO_ERR | G_IO_HUP | G_IO_NVAL; session = g_new0(struct avctp, 1); session->version = version; control = avctp_channel_create(session, fd, imtu, omtu, NULL); if (!control) { g_free(session); return NULL; } session->uinput = -1; session->control = control; session->passthrough_id = avctp_register_pdu_handler(session, AVC_OP_PASSTHROUGH, handle_panel_passthrough, NULL); session->unit_id = avctp_register_pdu_handler(session, AVC_OP_UNITINFO, handle_unit_info, NULL); session->subunit_id = avctp_register_pdu_handler(session, AVC_OP_SUBUNITINFO, handle_subunit_info, NULL); control->watch = g_io_add_watch(session->control->io, cond, (GIOFunc) session_cb, session); return avctp_ref(session); } int avctp_connect_browsing(struct avctp *session, int fd, size_t imtu, size_t omtu) { struct avctp_channel *browsing; GIOCondition cond = G_IO_IN | G_IO_ERR | G_IO_HUP | G_IO_NVAL; if (session->browsing) return -EISCONN; browsing = avctp_channel_create(session, fd, imtu, omtu, avctp_destroy_browsing); if (!browsing) return -EINVAL; session->browsing = browsing; browsing->watch = g_io_add_watch(session->browsing->io, cond, (GIOFunc) session_browsing_cb, session); return 0; } void avctp_set_destroy_cb(struct avctp *session, avctp_destroy_cb_t cb, void *user_data) { session->destroy = cb; session->data = user_data; } void avctp_shutdown(struct avctp *session) { if (!session) return; avctp_unref(session); } bluez-5.82/android/PaxHeaders/bluetooth.h0000644000000000000000000000005014015011623015435 xustar0020 atime=1743516864 20 ctime=1743591278 bluez-5.82/android/bluetooth.h0000644000000000000000000000642714015011623015127 0ustar00rootroot/* SPDX-License-Identifier: LGPL-2.1-or-later */ /* * * BlueZ - Bluetooth protocol stack for Linux * * Copyright (C) 2013-2014 Intel Corporation. All rights reserved. * * */ typedef void (*bt_bluetooth_ready)(int err, const bdaddr_t *addr); bool bt_bluetooth_start(int index, bool mgmt_dbg, bt_bluetooth_ready cb); typedef void (*bt_bluetooth_stopped)(void); bool bt_bluetooth_stop(bt_bluetooth_stopped cb); void bt_bluetooth_cleanup(void); bool bt_bluetooth_register(struct ipc *ipc, uint8_t mode); void bt_bluetooth_unregister(void); int bt_adapter_add_record(sdp_record_t *rec, uint8_t svc_hint); void bt_adapter_remove_record(uint32_t handle); typedef void (*bt_le_device_found)(const bdaddr_t *addr, int rssi, uint16_t eir_len, const void *eir, bool connectable, bool bonded); bool bt_le_register(bt_le_device_found cb); void bt_le_unregister(void); bool bt_le_discovery_start(void); typedef void (*bt_le_discovery_stopped)(void); bool bt_le_discovery_stop(bt_le_discovery_stopped cb); typedef void (*bt_le_set_advertising_done)(uint8_t status, void *user_data); bool bt_le_set_advertising(bool advertising, bt_le_set_advertising_done cb, void *user_data); uint8_t bt_get_device_android_type(const bdaddr_t *addr); bool bt_is_device_le(const bdaddr_t *addr); uint8_t bt_device_last_seen_bearer(const bdaddr_t *bdaddr); const char *bt_get_adapter_name(void); bool bt_device_is_bonded(const bdaddr_t *bdaddr); bool bt_device_set_uuids(const bdaddr_t *bdaddr, GSList *uuids); typedef void (*bt_read_device_rssi_done)(uint8_t status, const bdaddr_t *addr, int8_t rssi, void *user_data); bool bt_read_device_rssi(const bdaddr_t *addr, bt_read_device_rssi_done cb, void *user_data); bool bt_get_csrk(const bdaddr_t *addr, bool local, uint8_t key[16], uint32_t *sign_cnt, bool *authenticated); void bt_update_sign_counter(const bdaddr_t *addr, bool local, uint32_t val); void bt_store_gatt_ccc(const bdaddr_t *addr, uint16_t value); uint16_t bt_get_gatt_ccc(const bdaddr_t *addr); const bdaddr_t *bt_get_id_addr(const bdaddr_t *addr, uint8_t *type); bool bt_kernel_conn_control(void); bool bt_auto_connect_add(const bdaddr_t *addr); void bt_auto_connect_remove(const bdaddr_t *addr); typedef void (*bt_unpaired_device_cb)(const bdaddr_t *addr); bool bt_unpaired_register(bt_unpaired_device_cb cb); void bt_unpaired_unregister(bt_unpaired_device_cb cb); typedef void (*bt_paired_device_cb)(const bdaddr_t *addr); bool bt_paired_register(bt_paired_device_cb cb); void bt_paired_unregister(bt_paired_device_cb cb); bool bt_is_pairing(const bdaddr_t *addr); struct bt_ad; struct adv_instance { uint8_t instance; int32_t timeout; int32_t type; struct bt_ad *ad; struct bt_ad *sr; unsigned include_tx_power:1; }; /* Values below have no C API definition - only in Java (AdvertiseManager.java) * and bluedroid */ enum android_adv_type { ANDROID_ADVERTISING_EVENT_TYPE_CONNECTABLE = 0, ANDROID_ADVERTISING_EVENT_TYPE_SCANNABLE = 2, ANDROID_ADVERTISING_EVENT_TYPE_NON_CONNECTABLE = 3, }; typedef void (*bt_le_addrm_advertising_done)(uint8_t status, void *user_data); bool bt_le_add_advertising(struct adv_instance *adv, bt_le_addrm_advertising_done cb, void *user_data); bool bt_le_remove_advertising(struct adv_instance *adv, bt_le_addrm_advertising_done cb, void *user_data); bluez-5.82/android/PaxHeaders/client0000644000000000000000000000005014773213557014505 xustar0020 atime=1743591291 20 ctime=1743591279 bluez-5.82/android/client/0000755000000000000000000000000014773213557014243 5ustar00rootrootbluez-5.82/android/client/PaxHeaders/if-main.h0000644000000000000000000000005014015011623016226 xustar0020 atime=1743516867 20 ctime=1743591279 bluez-5.82/android/client/if-main.h0000644000000000000000000001347214015011623015716 0ustar00rootroot/* SPDX-License-Identifier: Apache-2.0 */ /* * Copyright (C) 2013 Intel Corporation * */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "hal.h" #if ANDROID_VERSION >= PLATFORM_VER(5, 0, 0) #include #include #endif #include #include #include #include #include extern audio_hw_device_t *if_audio; /* Interfaces from hal that can be populated during application lifetime */ extern const bt_interface_t *if_bluetooth; extern const btav_interface_t *if_av; extern const btrc_interface_t *if_rc; extern const bthf_interface_t *if_hf; extern const bthh_interface_t *if_hh; extern const btpan_interface_t *if_pan; extern const bthl_interface_t *if_hl; extern const btsock_interface_t *if_sock; extern const btgatt_interface_t *if_gatt; extern const btgatt_server_interface_t *if_gatt_server; extern const btgatt_client_interface_t *if_gatt_client; #if ANDROID_VERSION >= PLATFORM_VER(5, 0, 0) extern const btrc_ctrl_interface_t *if_rc_ctrl; extern const bthf_client_interface_t *if_hf_client; extern const btmce_interface_t *if_mce; extern const btav_interface_t *if_av_sink; #endif /* * Structure defines top level interfaces that can be used in test tool * this will contain values as: bluetooth, av, gatt, socket, pan... */ struct interface { const char *name; /* interface name */ struct method *methods; /* methods available for this interface */ }; extern const struct interface audio_if; extern const struct interface sco_if; extern const struct interface bluetooth_if; extern const struct interface av_if; extern const struct interface rc_if; extern const struct interface gatt_if; extern const struct interface gatt_client_if; extern const struct interface gatt_server_if; extern const struct interface pan_if; extern const struct interface sock_if; extern const struct interface hf_if; extern const struct interface hh_if; extern const struct interface hl_if; #if ANDROID_VERSION >= PLATFORM_VER(5, 0, 0) extern const struct interface ctrl_rc_if; extern const struct interface hf_client_if; extern const struct interface mce_if; extern const struct interface av_sink_if; #endif /* Interfaces that will show up in tool (first part of command line) */ extern const struct interface *interfaces[]; #define METHOD(name, func, comp, help) {name, func, comp, help} #define STD_METHOD(m) {#m, m##_p, NULL, NULL} #define STD_METHODC(m) {#m, m##_p, m##_c, NULL} #define STD_METHODH(m, h) {#m, m##_p, NULL, h} #define STD_METHODCH(m, h) {#m, m##_p, m##_c, h} #define END_METHOD {"", NULL, NULL, NULL} /* * Function to parse argument for function, argv[0] and argv[1] are already * parsed before this function is called and contain interface and method name * up to argc - 1 arguments are finished and should be used to decide which * function enumeration function to return */ typedef void (*parse_and_call)(int argc, const char **argv); /* * This is prototype of function that will return string for given number. * Purpose is to enumerate string for auto completion. * Function of this type will always be called in loop. * First time function is called i = 0, then if function returns non-NULL * it will be called again till for some value of i it will return NULL */ typedef const char *(*enum_func)(void *user, int i); /* * This is prototype of function that when given argc, argv will * fill enum_func with pointer to function that will enumerate * parameters for argc argument, user will be passed to enum_func. */ typedef void (*tab_complete)(int argc, const char **argv, enum_func *enum_func, void **user); /* * For each method there is name and two functions to parse command line * and call proper hal function on. */ struct method { const char *name; parse_and_call func; tab_complete complete; const char *help; }; int haltest_error(const char *format, ...) __attribute__((format(printf, 1, 2))); int haltest_info(const char *format, ...)__attribute__((format(printf, 1, 2))); int haltest_warn(const char *format, ...)__attribute__((format(printf, 1, 2))); /* Enumerator for discovered devices, to be used as tab completion enum_func */ const char *enum_devices(void *v, int i); const char *interface_name(void *v, int i); const char *command_name(void *v, int i); void add_remote_device(const bt_bdaddr_t *addr); bool close_hw_bt_dev(void); const struct interface *get_interface(const char *name); struct method *get_method(struct method *methods, const char *name); struct method *get_command(const char *name); const struct method *get_interface_method(const char *iname, const char *mname); #define NELEM(x) ((int) (sizeof(x) / sizeof((x)[0]))) /* Helper macro for executing function on interface and printing BT_STATUS */ #define EXEC(f, ...) \ { \ if (f) { \ int err = f(__VA_ARGS__); \ haltest_info("%s: %s\n", #f, bt_status_t2str(err)); \ } else { \ haltest_info("%s is NULL\n", #f); \ } \ } /* Helper macro for executing void function on interface */ #define EXECV(f, ...) \ { \ (void) f(__VA_ARGS__); \ haltest_info("%s: void\n", #f); \ } #define RETURN_IF_NULL(x) \ do { if (!x) { haltest_error("%s is NULL\n", #x); return; } } while (0) #define VERIFY_ADDR_ARG(n, adr) \ do { \ if (n < argc) {\ str2bt_bdaddr_t(argv[n], adr); \ } else { \ haltest_error("No address specified\n");\ return;\ } \ } while (0) bluez-5.82/android/client/PaxHeaders/if-pan.c0000644000000000000000000000005014015011623016053 xustar0020 atime=1743516867 20 ctime=1743591279 bluez-5.82/android/client/if-pan.c0000644000000000000000000001043614015011623015540 0ustar00rootroot// SPDX-License-Identifier: Apache-2.0 /* * Copyright (C) 2013 Intel Corporation * */ #define _GNU_SOURCE #include "if-main.h" #include "../hal-utils.h" const btpan_interface_t *if_pan = NULL; typedef int btpan_role_t; SINTMAP(btpan_role_t, -1, "(unknown)") DELEMENT(BTPAN_ROLE_NONE), DELEMENT(BTPAN_ROLE_PANNAP), DELEMENT(BTPAN_ROLE_PANU), ENDMAP SINTMAP(btpan_connection_state_t, -1, "(unknown)") DELEMENT(BTPAN_STATE_CONNECTED), DELEMENT(BTPAN_STATE_CONNECTING), DELEMENT(BTPAN_STATE_DISCONNECTED), DELEMENT(BTPAN_STATE_DISCONNECTING), ENDMAP SINTMAP(btpan_control_state_t, -1, "(unknown)") DELEMENT(BTPAN_STATE_ENABLED), DELEMENT(BTPAN_STATE_DISABLED), ENDMAP #if ANDROID_VERSION >= PLATFORM_VER(5, 0, 0) static void control_state_cb(btpan_control_state_t state, int local_role, bt_status_t error, const char *ifname) #else static void control_state_cb(btpan_control_state_t state, bt_status_t error, int local_role, const char *ifname) #endif { haltest_info("%s: state=%s error=%s local_role=%s ifname=%s\n", __func__, btpan_control_state_t2str(state), bt_status_t2str(error), btpan_role_t2str(local_role), ifname); } static char last_used_addr[MAX_ADDR_STR_LEN]; static void connection_state_cb(btpan_connection_state_t state, bt_status_t error, const bt_bdaddr_t *bd_addr, int local_role, int remote_role) { haltest_info("%s: state=%s error=%s bd_addr=%s local_role=%s remote_role=%s\n", __func__, btpan_connection_state_t2str(state), bt_status_t2str(error), bt_bdaddr_t2str(bd_addr, last_used_addr), btpan_role_t2str(local_role), btpan_role_t2str(remote_role)); } static btpan_callbacks_t pan_cbacks = { .size = sizeof(pan_cbacks), .control_state_cb = control_state_cb, .connection_state_cb = connection_state_cb }; static void init_p(int argc, const char **argv) { RETURN_IF_NULL(if_pan); EXEC(if_pan->init, &pan_cbacks); } /* enable */ static void enable_c(int argc, const char **argv, enum_func *enum_func, void **user) { if (argc == 3) { *user = TYPE_ENUM(btpan_role_t); *enum_func = enum_defines; } } static void enable_p(int argc, const char **argv) { int local_role; RETURN_IF_NULL(if_pan); /* local role */ if (argc < 3) { haltest_error("No local mode specified\n"); return; } local_role = str2btpan_role_t(argv[2]); if (local_role == -1) local_role = atoi(argv[2]); EXEC(if_pan->enable, local_role); } /* get_local_role */ static void get_local_role_p(int argc, const char **argv) { int local_role; RETURN_IF_NULL(if_pan); local_role = if_pan->get_local_role(); haltest_info("local_role: %s\n", btpan_role_t2str(local_role)); } /* connect */ static void connect_c(int argc, const char **argv, enum_func *enum_func, void **user) { if (argc == 3) { *user = NULL; *enum_func = enum_devices; } else if (argc == 4 || argc == 5) { *user = TYPE_ENUM(btpan_role_t); *enum_func = enum_defines; } } static void connect_p(int argc, const char **argv) { bt_bdaddr_t addr; int local_role; int remote_role; RETURN_IF_NULL(if_pan); VERIFY_ADDR_ARG(2, &addr); /* local role */ if (argc < 4) { haltest_error("No local mode specified\n"); return; } local_role = str2btpan_role_t(argv[3]); if (local_role == -1) local_role = atoi(argv[3]); /* remote role */ if (argc < 5) { haltest_error("No remote mode specified\n"); return; } remote_role = str2btpan_role_t(argv[4]); if (remote_role == -1) remote_role = atoi(argv[4]); EXEC(if_pan->connect, &addr, local_role, remote_role); } /* disconnect */ static void disconnect_c(int argc, const char **argv, enum_func *enum_func, void **user) { if (argc == 3) { *user = last_used_addr; *enum_func = enum_one_string; } } static void disconnect_p(int argc, const char **argv) { bt_bdaddr_t addr; RETURN_IF_NULL(if_pan); VERIFY_ADDR_ARG(2, &addr); EXEC(if_pan->disconnect, &addr); } /* cleanup */ static void cleanup_p(int argc, const char **argv) { RETURN_IF_NULL(if_pan); EXECV(if_pan->cleanup); if_pan = NULL; } static struct method methods[] = { STD_METHOD(init), STD_METHODCH(connect, " "), STD_METHODCH(enable, ""), STD_METHOD(get_local_role), STD_METHODCH(disconnect, ""), STD_METHOD(cleanup), END_METHOD }; const struct interface pan_if = { .name = "pan", .methods = methods }; bluez-5.82/android/client/PaxHeaders/if-rc-ctrl.c0000644000000000000000000000005014015011623016643 xustar0020 atime=1743516867 20 ctime=1743591279 bluez-5.82/android/client/if-rc-ctrl.c0000644000000000000000000000406414015011623016330 0ustar00rootroot// SPDX-License-Identifier: Apache-2.0 /* * Copyright (C) 2014 Intel Corporation * */ #define _GNU_SOURCE #include #include #include #include #include #include "if-main.h" #include "pollhandler.h" #include "../hal-utils.h" const btrc_ctrl_interface_t *if_rc_ctrl = NULL; static char last_addr[MAX_ADDR_STR_LEN]; static void passthrough_rsp_cb(int id, int key_state) { haltest_info("%s: id=%d key_state=%d\n", __func__, id, key_state); } static void connection_state_cb(bool state, bt_bdaddr_t *bd_addr) { haltest_info("%s: state=%s bd_addr=%s\n", __func__, state ? "true" : "false", bt_bdaddr_t2str(bd_addr, last_addr)); } static btrc_ctrl_callbacks_t rc_ctrl_cbacks = { .size = sizeof(rc_ctrl_cbacks), .passthrough_rsp_cb = passthrough_rsp_cb, .connection_state_cb = connection_state_cb, }; /* init */ static void init_p(int argc, const char **argv) { RETURN_IF_NULL(if_rc_ctrl); EXEC(if_rc_ctrl->init, &rc_ctrl_cbacks); } /* cleanup */ static void cleanup_p(int argc, const char **argv) { RETURN_IF_NULL(if_rc_ctrl); EXECV(if_rc_ctrl->cleanup); if_rc_ctrl = NULL; } /* send_pass_through_cmd */ static void send_pass_through_cmd_c(int argc, const char **argv, enum_func *enum_func, void **user) { if (argc == 3) { *user = NULL; *enum_func = enum_devices; } } static void send_pass_through_cmd_p(int argc, const char **argv) { bt_bdaddr_t addr; uint8_t key_code, key_state; RETURN_IF_NULL(if_rc); VERIFY_ADDR_ARG(2, &addr); if (argc < 4) { haltest_error("No key code specified\n"); return; } key_code = (uint8_t) atoi(argv[3]); if (argc < 5) { haltest_error("No key state specified\n"); return; } key_state = (uint8_t) atoi(argv[4]); EXEC(if_rc_ctrl->send_pass_through_cmd, &addr, key_code, key_state); } static struct method methods[] = { STD_METHOD(init), STD_METHODCH(send_pass_through_cmd, " "), STD_METHOD(cleanup), END_METHOD }; const struct interface ctrl_rc_if = { .name = "rc-ctrl", .methods = methods }; bluez-5.82/android/client/PaxHeaders/if-sco.c0000644000000000000000000000005014015011623016061 xustar0020 atime=1743516867 20 ctime=1743591279 bluez-5.82/android/client/if-sco.c0000644000000000000000000004452214015011623015551 0ustar00rootroot// SPDX-License-Identifier: Apache-2.0 /* * Copyright (C) 2014 Intel Corporation * */ #define _GNU_SOURCE #include #include #include #include "if-main.h" #include "../hal-utils.h" audio_hw_device_t *if_audio_sco = NULL; static struct audio_stream_out *stream_out = NULL; static struct audio_stream_in *stream_in = NULL; static size_t buffer_size = 0; static size_t buffer_size_in = 0; static pthread_t play_thread = 0; static pthread_mutex_t outstream_mutex = PTHREAD_MUTEX_INITIALIZER; static pthread_mutex_t state_mutex = PTHREAD_MUTEX_INITIALIZER; enum state { STATE_STOPPED, STATE_STOPPING, STATE_PLAYING, STATE_SUSPENDED, STATE_MAX }; SINTMAP(audio_channel_mask_t, -1, "(AUDIO_CHANNEL_INVALID)") DELEMENT(AUDIO_CHANNEL_OUT_FRONT_LEFT), DELEMENT(AUDIO_CHANNEL_OUT_FRONT_RIGHT), DELEMENT(AUDIO_CHANNEL_OUT_FRONT_CENTER), DELEMENT(AUDIO_CHANNEL_OUT_LOW_FREQUENCY), DELEMENT(AUDIO_CHANNEL_OUT_BACK_LEFT), DELEMENT(AUDIO_CHANNEL_OUT_BACK_RIGHT), DELEMENT(AUDIO_CHANNEL_OUT_FRONT_LEFT_OF_CENTER), DELEMENT(AUDIO_CHANNEL_OUT_FRONT_RIGHT_OF_CENTER), DELEMENT(AUDIO_CHANNEL_OUT_BACK_CENTER), DELEMENT(AUDIO_CHANNEL_OUT_SIDE_LEFT), DELEMENT(AUDIO_CHANNEL_OUT_SIDE_RIGHT), DELEMENT(AUDIO_CHANNEL_OUT_TOP_CENTER), DELEMENT(AUDIO_CHANNEL_OUT_TOP_FRONT_LEFT), DELEMENT(AUDIO_CHANNEL_OUT_TOP_FRONT_CENTER), DELEMENT(AUDIO_CHANNEL_OUT_TOP_FRONT_RIGHT), DELEMENT(AUDIO_CHANNEL_OUT_TOP_BACK_LEFT), DELEMENT(AUDIO_CHANNEL_OUT_TOP_BACK_CENTER), DELEMENT(AUDIO_CHANNEL_OUT_TOP_BACK_RIGHT), DELEMENT(AUDIO_CHANNEL_OUT_MONO), DELEMENT(AUDIO_CHANNEL_OUT_STEREO), DELEMENT(AUDIO_CHANNEL_OUT_QUAD), #if ANDROID_VERSION < PLATFORM_VER(5, 0, 0) DELEMENT(AUDIO_CHANNEL_OUT_SURROUND), #else DELEMENT(AUDIO_CHANNEL_OUT_QUAD_BACK), DELEMENT(AUDIO_CHANNEL_OUT_QUAD_SIDE), DELEMENT(AUDIO_CHANNEL_OUT_5POINT1_BACK), DELEMENT(AUDIO_CHANNEL_OUT_5POINT1_SIDE), #endif DELEMENT(AUDIO_CHANNEL_OUT_5POINT1), DELEMENT(AUDIO_CHANNEL_OUT_7POINT1), DELEMENT(AUDIO_CHANNEL_OUT_ALL), DELEMENT(AUDIO_CHANNEL_OUT_FRONT_LEFT), DELEMENT(AUDIO_CHANNEL_OUT_FRONT_LEFT), DELEMENT(AUDIO_CHANNEL_OUT_FRONT_LEFT), DELEMENT(AUDIO_CHANNEL_OUT_FRONT_LEFT), DELEMENT(AUDIO_CHANNEL_OUT_FRONT_LEFT), ENDMAP SINTMAP(audio_format_t, -1, "(AUDIO_FORMAT_INVALID)") DELEMENT(AUDIO_FORMAT_DEFAULT), DELEMENT(AUDIO_FORMAT_PCM), DELEMENT(AUDIO_FORMAT_MP3), DELEMENT(AUDIO_FORMAT_AMR_NB), DELEMENT(AUDIO_FORMAT_AMR_WB), DELEMENT(AUDIO_FORMAT_AAC), DELEMENT(AUDIO_FORMAT_HE_AAC_V1), DELEMENT(AUDIO_FORMAT_HE_AAC_V2), DELEMENT(AUDIO_FORMAT_VORBIS), DELEMENT(AUDIO_FORMAT_MAIN_MASK), DELEMENT(AUDIO_FORMAT_SUB_MASK), DELEMENT(AUDIO_FORMAT_PCM_16_BIT), DELEMENT(AUDIO_FORMAT_PCM_8_BIT), DELEMENT(AUDIO_FORMAT_PCM_32_BIT), DELEMENT(AUDIO_FORMAT_PCM_8_24_BIT), ENDMAP static int current_state = STATE_STOPPED; #define SAMPLERATE 44100 static short sample[SAMPLERATE]; static uint16_t sample_pos; static void init_p(int argc, const char **argv) { int err; const hw_module_t *module; audio_hw_device_t *device; err = hw_get_module_by_class(AUDIO_HARDWARE_MODULE_ID, "sco", &module); if (err) { haltest_error("hw_get_module_by_class returned %d\n", err); return; } err = audio_hw_device_open(module, &device); if (err) { haltest_error("audio_hw_device_open returned %d\n", err); return; } if_audio_sco = device; } static int feed_from_file(short *buffer, void *data) { FILE *in = data; return fread(buffer, buffer_size, 1, in); } static int feed_from_generator(short *buffer, void *data) { size_t i = 0; float volume = 0.5; float *freq = data; float f = 1; if (freq) f = *freq; /* buffer_size is in bytes but we are using buffer of shorts (2 bytes)*/ for (i = 0; i < buffer_size / sizeof(*buffer) - 1;) { if (sample_pos >= SAMPLERATE) sample_pos = sample_pos % SAMPLERATE; /* Use the same sample for both channels */ buffer[i++] = sample[sample_pos] * volume; buffer[i++] = sample[sample_pos] * volume; sample_pos += f; } return buffer_size; } static int feed_from_in(short *buffer, void *data) { return stream_in->read(stream_in, buffer, buffer_size_in); } static void prepare_sample(void) { int x; double s; haltest_info("Preparing audio sample...\n"); for (x = 0; x < SAMPLERATE; x++) { /* prepare sinusoidal 1Hz sample */ s = (2.0 * 3.14159) * ((double)x / SAMPLERATE); s = sin(s); /* remap <-1, 1> to signed 16bit PCM range */ sample[x] = s * 32767; } sample_pos = 0; } static void mono_to_stereo_pcm16(const int16_t *in, int16_t *out, size_t samples) { size_t i; for (i = 0; i < samples; i++) { out[2 * i] = in[i]; out[2 * i + 1] = in[i]; } } static void *playback_thread(void *data) { int (*filbuff_cb) (short*, void*); short buffer[buffer_size / sizeof(short)]; short buffer_in[buffer_size_in / sizeof(short)]; size_t len = 0; ssize_t w_len = 0; FILE *in = data; void *cb_data = NULL; float freq = 440.0; /* Use file or fall back to generator */ if (in) { if (data == stream_in) filbuff_cb = feed_from_in; else { filbuff_cb = feed_from_file; cb_data = in; } } else { prepare_sample(); filbuff_cb = feed_from_generator; cb_data = &freq; } pthread_mutex_lock(&state_mutex); current_state = STATE_PLAYING; pthread_mutex_unlock(&state_mutex); do { pthread_mutex_lock(&state_mutex); if (current_state == STATE_STOPPING) { haltest_info("Detected stopping\n"); pthread_mutex_unlock(&state_mutex); break; } else if (current_state == STATE_SUSPENDED) { pthread_mutex_unlock(&state_mutex); usleep(500); continue; } pthread_mutex_unlock(&state_mutex); if (data && data == stream_in) { int chan_in = popcount(stream_in->common.get_channels(&stream_in->common)); int chan_out = popcount(stream_out->common.get_channels(&stream_out->common)); len = filbuff_cb(buffer_in, cb_data); if (chan_in == 1 && chan_out == 2) { mono_to_stereo_pcm16(buffer_in, buffer, buffer_size_in / 2); } } else len = filbuff_cb(buffer, cb_data); pthread_mutex_lock(&outstream_mutex); if (!stream_out) { pthread_mutex_unlock(&outstream_mutex); break; } w_len = stream_out->write(stream_out, buffer, buffer_size); pthread_mutex_unlock(&outstream_mutex); } while (len && w_len > 0); if (in && data != stream_in) fclose(in); pthread_mutex_lock(&state_mutex); current_state = STATE_STOPPED; pthread_mutex_unlock(&state_mutex); haltest_info("Done playing.\n"); return NULL; } static void write_stereo_pcm16(const short *input, size_t len, FILE *out) { short sample[2]; size_t i; for (i = 0; i < len / 2; i++) { sample[0] = input[i]; sample[1] = input[i]; fwrite(sample, sizeof(sample), 1, out); } } static void *read_thread(void *data) { int (*filbuff_cb) (short*, void*) = feed_from_in; short buffer[buffer_size_in / sizeof(short)]; ssize_t len = 0; void *cb_data = NULL; FILE *out = data; pthread_mutex_lock(&state_mutex); current_state = STATE_PLAYING; pthread_mutex_unlock(&state_mutex); do { pthread_mutex_lock(&state_mutex); if (current_state == STATE_STOPPING) { haltest_info("Detected stopping\n"); pthread_mutex_unlock(&state_mutex); break; } else if (current_state == STATE_SUSPENDED) { pthread_mutex_unlock(&state_mutex); usleep(500); continue; } pthread_mutex_unlock(&state_mutex); len = filbuff_cb(buffer, cb_data); if (len < 0) { haltest_error("Error receiving SCO data"); break; } haltest_info("Read %zd bytes\n", len); if (out) { write_stereo_pcm16(buffer, len, out); haltest_info("Written %zd bytes\n", len * 2); } } while (len); if (out) fclose(out); pthread_mutex_lock(&state_mutex); current_state = STATE_STOPPED; pthread_mutex_unlock(&state_mutex); haltest_info("Done reading.\n"); return NULL; } static void play_p(int argc, const char **argv) { const char *fname = NULL; FILE *in = NULL; RETURN_IF_NULL(if_audio_sco); RETURN_IF_NULL(stream_out); if (argc < 3) { haltest_error("Invalid audio file path.\n"); haltest_info("Using sound generator.\n"); } else { fname = argv[2]; in = fopen(fname, "r"); if (in == NULL) { haltest_error("Cannot open file: %s\n", fname); return; } haltest_info("Playing file: %s\n", fname); } if (buffer_size == 0) { haltest_error("Invalid buffer size. Was stream_out opened?\n"); goto fail; } pthread_mutex_lock(&state_mutex); if (current_state != STATE_STOPPED) { haltest_error("Already playing or stream suspended!\n"); pthread_mutex_unlock(&state_mutex); goto fail; } pthread_mutex_unlock(&state_mutex); if (pthread_create(&play_thread, NULL, playback_thread, in) != 0) { haltest_error("Cannot create playback thread!\n"); goto fail; } return; fail: if (in) fclose(in); } static void loop_p(int argc, const char **argv) { int chan_out, chan_in; RETURN_IF_NULL(if_audio_sco); RETURN_IF_NULL(stream_out); RETURN_IF_NULL(stream_in); chan_out = popcount(stream_out->common.get_channels(&stream_out->common)); chan_in = popcount(stream_in->common.get_channels(&stream_in->common)); if (!buffer_size || !buffer_size_in) { haltest_error("Invalid buffer sizes. Streams opened\n"); return; } if (buffer_size / chan_out != buffer_size_in / chan_in) { haltest_error("read/write buffers differ, not supported\n"); return; } pthread_mutex_lock(&state_mutex); if (current_state != STATE_STOPPED) { haltest_error("Already playing or stream suspended!\n"); pthread_mutex_unlock(&state_mutex); return; } pthread_mutex_unlock(&state_mutex); if (pthread_create(&play_thread, NULL, playback_thread, stream_in) != 0) haltest_error("Cannot create playback thread!\n"); } static void read_p(int argc, const char **argv) { const char *fname = NULL; FILE *out = NULL; RETURN_IF_NULL(if_audio_sco); RETURN_IF_NULL(stream_in); pthread_mutex_lock(&state_mutex); if (current_state != STATE_STOPPED) { haltest_error("Already playing or stream suspended!\n"); pthread_mutex_unlock(&state_mutex); return; } pthread_mutex_unlock(&state_mutex); if (argc < 3) { haltest_error("Invalid audio file path.\n"); haltest_info("Using read and through away\n"); } else { fname = argv[2]; out = fopen(fname, "w"); if (!out) { haltest_error("Cannot open file: %s\n", fname); return; } haltest_info("Reading to file: %s\n", fname); } if (!buffer_size_in) { haltest_error("Invalid buffer size.\n"); goto failed; } if (pthread_create(&play_thread, NULL, read_thread, out) != 0) { haltest_error("Cannot create playback thread!\n"); goto failed; } return; failed: if (out) fclose(out); } static void stop_p(int argc, const char **argv) { RETURN_IF_NULL(if_audio_sco); RETURN_IF_NULL(play_thread); pthread_mutex_lock(&state_mutex); if (current_state == STATE_STOPPED || current_state == STATE_STOPPING) { pthread_mutex_unlock(&state_mutex); return; } if (stream_out) { pthread_mutex_lock(&outstream_mutex); stream_out->common.standby(&stream_out->common); pthread_mutex_unlock(&outstream_mutex); } current_state = STATE_STOPPING; pthread_mutex_unlock(&state_mutex); pthread_join(play_thread, NULL); play_thread = 0; haltest_info("Ended %s\n", __func__); } static void open_output_stream_p(int argc, const char **argv) { struct audio_config *config; int err; RETURN_IF_NULL(if_audio_sco); pthread_mutex_lock(&state_mutex); if (current_state == STATE_PLAYING) { haltest_error("Already playing!\n"); pthread_mutex_unlock(&state_mutex); return; } pthread_mutex_unlock(&state_mutex); if (argc < 3) { haltest_info("No sampling rate specified. Use default conf\n"); config = NULL; } else { config = calloc(1, sizeof(struct audio_config)); if (!config) return; config->sample_rate = atoi(argv[2]); config->channel_mask = AUDIO_CHANNEL_OUT_STEREO; config->format = AUDIO_FORMAT_PCM_16_BIT; } #if ANDROID_VERSION >= PLATFORM_VER(5, 0, 0) err = if_audio_sco->open_output_stream(if_audio_sco, 0, AUDIO_DEVICE_OUT_ALL_SCO, AUDIO_OUTPUT_FLAG_NONE, config, &stream_out, NULL); #else err = if_audio_sco->open_output_stream(if_audio_sco, 0, AUDIO_DEVICE_OUT_ALL_SCO, AUDIO_OUTPUT_FLAG_NONE, config, &stream_out); #endif if (err < 0) { haltest_error("open output stream returned %d\n", err); goto failed; } buffer_size = stream_out->common.get_buffer_size(&stream_out->common); if (buffer_size == 0) haltest_error("Invalid buffer size received!\n"); else haltest_info("Using buffer size: %zu\n", buffer_size); failed: if (config) free(config); } static void close_output_stream_p(int argc, const char **argv) { RETURN_IF_NULL(if_audio_sco); RETURN_IF_NULL(stream_out); if (play_thread) stop_p(argc, argv); if_audio_sco->close_output_stream(if_audio_sco, stream_out); stream_out = NULL; buffer_size = 0; } static void open_input_stream_p(int argc, const char **argv) { struct audio_config *config; int err; RETURN_IF_NULL(if_audio_sco); pthread_mutex_lock(&state_mutex); if (current_state == STATE_PLAYING) { haltest_error("Already playing!\n"); pthread_mutex_unlock(&state_mutex); return; } pthread_mutex_unlock(&state_mutex); if (argc < 3) { haltest_info("No sampling rate specified. Use default conf\n"); config = NULL; } else { config = calloc(1, sizeof(struct audio_config)); if (!config) return; config->sample_rate = atoi(argv[2]); config->channel_mask = AUDIO_CHANNEL_OUT_MONO; config->format = AUDIO_FORMAT_PCM_16_BIT; } #if ANDROID_VERSION >= PLATFORM_VER(5, 0, 0) err = if_audio_sco->open_input_stream(if_audio_sco, 0, AUDIO_DEVICE_IN_BLUETOOTH_SCO_HEADSET, config, &stream_in, 0, NULL, 0); #else err = if_audio_sco->open_input_stream(if_audio_sco, 0, AUDIO_DEVICE_IN_BLUETOOTH_SCO_HEADSET, config, &stream_in); #endif if (err < 0) { haltest_error("open output stream returned %d\n", err); goto failed; } buffer_size_in = stream_in->common.get_buffer_size(&stream_in->common); if (buffer_size_in == 0) haltest_error("Invalid buffer size received!\n"); else haltest_info("Using buffer size: %zu\n", buffer_size_in); failed: if (config) free(config); } static void close_input_stream_p(int argc, const char **argv) { RETURN_IF_NULL(if_audio_sco); RETURN_IF_NULL(stream_in); if (play_thread) stop_p(argc, argv); if_audio_sco->close_input_stream(if_audio_sco, stream_in); stream_in = NULL; buffer_size_in = 0; } static void cleanup_p(int argc, const char **argv) { int err; RETURN_IF_NULL(if_audio_sco); pthread_mutex_lock(&state_mutex); if (current_state != STATE_STOPPED) { pthread_mutex_unlock(&state_mutex); close_output_stream_p(0, NULL); } else { pthread_mutex_unlock(&state_mutex); } err = audio_hw_device_close(if_audio_sco); if (err < 0) { haltest_error("audio_hw_device_close returned %d\n", err); return; } if_audio_sco = NULL; } static void suspend_p(int argc, const char **argv) { RETURN_IF_NULL(if_audio_sco); RETURN_IF_NULL(stream_out); pthread_mutex_lock(&state_mutex); if (current_state != STATE_PLAYING) { pthread_mutex_unlock(&state_mutex); return; } current_state = STATE_SUSPENDED; pthread_mutex_unlock(&state_mutex); pthread_mutex_lock(&outstream_mutex); stream_out->common.standby(&stream_out->common); pthread_mutex_unlock(&outstream_mutex); } static void resume_p(int argc, const char **argv) { RETURN_IF_NULL(if_audio_sco); RETURN_IF_NULL(stream_out); pthread_mutex_lock(&state_mutex); if (current_state == STATE_SUSPENDED) current_state = STATE_PLAYING; pthread_mutex_unlock(&state_mutex); } static void get_latency_p(int argc, const char **argv) { RETURN_IF_NULL(if_audio_sco); RETURN_IF_NULL(stream_out); haltest_info("Output audio stream latency: %d\n", stream_out->get_latency(stream_out)); } static void get_buffer_size_p(int argc, const char **argv) { RETURN_IF_NULL(if_audio_sco); RETURN_IF_NULL(stream_out); haltest_info("Current output buffer size: %zu\n", stream_out->common.get_buffer_size(&stream_out->common)); } static void get_channels_p(int argc, const char **argv) { audio_channel_mask_t channels; RETURN_IF_NULL(if_audio_sco); RETURN_IF_NULL(stream_out); channels = stream_out->common.get_channels(&stream_out->common); haltest_info("Channels: %s\n", audio_channel_mask_t2str(channels)); } static void get_format_p(int argc, const char **argv) { audio_format_t format; RETURN_IF_NULL(if_audio_sco); RETURN_IF_NULL(stream_out); format = stream_out->common.get_format(&stream_out->common); haltest_info("Format: %s\n", audio_format_t2str(format)); } static void get_sample_rate_p(int argc, const char **argv) { RETURN_IF_NULL(if_audio_sco); RETURN_IF_NULL(stream_out); haltest_info("Current sample rate: %d\n", stream_out->common.get_sample_rate(&stream_out->common)); } static void get_parameters_p(int argc, const char **argv) { const char *keystr; RETURN_IF_NULL(if_audio_sco); RETURN_IF_NULL(stream_out); if (argc < 3) { haltest_info("No keys given.\n"); keystr = ""; } else { keystr = argv[2]; } haltest_info("Current parameters: %s\n", stream_out->common.get_parameters(&stream_out->common, keystr)); } static void set_parameters_p(int argc, const char **argv) { RETURN_IF_NULL(if_audio_sco); RETURN_IF_NULL(stream_out); if (argc < 3) { haltest_error("No key=value; pairs given.\n"); return; } stream_out->common.set_parameters(&stream_out->common, argv[2]); } static void set_sample_rate_p(int argc, const char **argv) { RETURN_IF_NULL(if_audio_sco); RETURN_IF_NULL(stream_out); if (argc < 3) return; stream_out->common.set_sample_rate(&stream_out->common, atoi(argv[2])); } static void init_check_p(int argc, const char **argv) { RETURN_IF_NULL(if_audio_sco); haltest_info("Init check result: %d\n", if_audio_sco->init_check(if_audio_sco)); } static struct method methods[] = { STD_METHOD(init), STD_METHOD(cleanup), STD_METHODH(open_output_stream, "sample_rate"), STD_METHOD(close_output_stream), STD_METHODH(open_input_stream, "sampling rate"), STD_METHOD(close_input_stream), STD_METHODH(play, ""), STD_METHOD(read), STD_METHOD(loop), STD_METHOD(stop), STD_METHOD(suspend), STD_METHOD(resume), STD_METHOD(get_latency), STD_METHOD(get_buffer_size), STD_METHOD(get_channels), STD_METHOD(get_format), STD_METHOD(get_sample_rate), STD_METHODH(get_parameters, ""), STD_METHODH(set_parameters, ""), STD_METHODH(set_sample_rate, ""), STD_METHOD(init_check), END_METHOD }; const struct interface sco_if = { .name = "sco", .methods = methods }; bluez-5.82/android/client/PaxHeaders/if-mce.c0000644000000000000000000000005014015011623016041 xustar0020 atime=1743516867 20 ctime=1743591279 bluez-5.82/android/client/if-mce.c0000644000000000000000000000311314015011623015520 0ustar00rootroot// SPDX-License-Identifier: Apache-2.0 /* * Copyright (C) 2014 Intel Corporation * */ #define _GNU_SOURCE #include "if-main.h" #include "../hal-utils.h" const btmce_interface_t *if_mce = NULL; /* * Callback for get_remote_mas_instances */ static void btmce_remote_mas_instances_cb(bt_status_t status, bt_bdaddr_t *bd_addr, int num_instances, btmce_mas_instance_t *instances) { int i; haltest_info("%s: status=%s bd_addr=%s num_instance=%d\n", __func__, bt_status_t2str(status), bdaddr2str(bd_addr), num_instances); for (i = 0; i < num_instances; i++) haltest_info("id=%d scn=%d msg_types=%d name=%s\n", instances[i].id, instances[i].scn, instances[i].msg_types, instances[i].p_name); } static btmce_callbacks_t mce_cbacks = { .size = sizeof(mce_cbacks), .remote_mas_instances_cb = btmce_remote_mas_instances_cb, }; /* init */ static void init_p(int argc, const char **argv) { RETURN_IF_NULL(if_mce); EXEC(if_mce->init, &mce_cbacks); } static void get_remote_mas_instances_c(int argc, const char **argv, enum_func *enum_func, void **user) { if (argc == 3) { *user = NULL; *enum_func = enum_devices; } } /* search for MAS instances on remote device */ static void get_remote_mas_instances_p(int argc, const char **argv) { bt_bdaddr_t addr; RETURN_IF_NULL(if_mce); VERIFY_ADDR_ARG(2, &addr); EXEC(if_mce->get_remote_mas_instances, &addr); } static struct method methods[] = { STD_METHOD(init), STD_METHODCH(get_remote_mas_instances, ""), END_METHOD }; const struct interface mce_if = { .name = "mce", .methods = methods }; bluez-5.82/android/client/PaxHeaders/if-hl.c0000644000000000000000000000005014015011623015700 xustar0020 atime=1743516867 20 ctime=1743591279 bluez-5.82/android/client/if-hl.c0000644000000000000000000001735514015011623015374 0ustar00rootroot// SPDX-License-Identifier: Apache-2.0 /* * Copyright (C) 2014 Intel Corporation * */ #define _GNU_SOURCE #include #include #include #include #include #include #include "if-main.h" #include "pollhandler.h" #include "../hal-utils.h" SINTMAP(bthl_mdep_role_t, -1, "(unknown)") DELEMENT(BTHL_MDEP_ROLE_SOURCE), DELEMENT(BTHL_MDEP_ROLE_SINK), ENDMAP SINTMAP(bthl_channel_type_t, -1, "(unknown)") DELEMENT(BTHL_CHANNEL_TYPE_RELIABLE), DELEMENT(BTHL_CHANNEL_TYPE_STREAMING), DELEMENT(BTHL_CHANNEL_TYPE_ANY), ENDMAP SINTMAP(bthl_app_reg_state_t, -1, "(unknown)") DELEMENT(BTHL_APP_REG_STATE_REG_SUCCESS), DELEMENT(BTHL_APP_REG_STATE_REG_FAILED), DELEMENT(BTHL_APP_REG_STATE_DEREG_SUCCESS), DELEMENT(BTHL_APP_REG_STATE_DEREG_FAILED), ENDMAP SINTMAP(bthl_channel_state_t, -1, "(unknown)") DELEMENT(BTHL_CONN_STATE_CONNECTING), DELEMENT(BTHL_CONN_STATE_CONNECTED), DELEMENT(BTHL_CONN_STATE_DISCONNECTING), DELEMENT(BTHL_CONN_STATE_DISCONNECTED), DELEMENT(BTHL_CONN_STATE_DESTROYED), ENDMAP #define APP_ID_SIZE 20 #define MDEP_CFG_SIZE 10 #define CHANNEL_ID_SIZE 50 struct channel_info { int fd; }; struct mdep_cfg { uint8_t role; struct channel_info channel[CHANNEL_ID_SIZE]; }; struct { struct mdep_cfg mdep[MDEP_CFG_SIZE]; } app[APP_ID_SIZE]; const bthl_interface_t *if_hl = NULL; static void app_reg_state_cb(int app_id, bthl_app_reg_state_t state) { haltest_info("%s: app_id=%d app_reg_state=%s\n", __func__, app_id, bthl_app_reg_state_t2str(state)); } static void channel_state_cb(int app_id, bt_bdaddr_t *bd_addr, int index, int channel_id, bthl_channel_state_t state, int fd) { char addr[MAX_ADDR_STR_LEN]; haltest_info("%s: app_id=%d bd_addr=%s mdep_cfg_index=%d\n" "channel_id=%d channel_state=%s fd=%d\n", __func__, app_id, bt_bdaddr_t2str(bd_addr, addr), index, channel_id, bthl_channel_state_t2str(state), fd); if (app_id >= APP_ID_SIZE || index >= MDEP_CFG_SIZE || channel_id >= CHANNEL_ID_SIZE) { haltest_error("exceeds maximum limit"); return; } if (state == BTHL_CONN_STATE_CONNECTED) { app[app_id].mdep[index].channel[channel_id].fd = fd; /* * PTS expects dummy data on fd when it * connects in source role. */ if (app[app_id].mdep[index].role == BTHL_MDEP_ROLE_SOURCE) if (write(fd, "0", sizeof("0")) < 0) haltest_error("writing data on fd failed\n"); return; } if (state == BTHL_CONN_STATE_DISCONNECTED || state == BTHL_CONN_STATE_DESTROYED) { if (app[app_id].mdep[index].channel[channel_id].fd >= 0) { close(app[app_id].mdep[index].channel[channel_id].fd); app[app_id].mdep[index].channel[channel_id].fd = -1; } } } static bthl_callbacks_t hl_cbacks = { .size = sizeof(hl_cbacks), .app_reg_state_cb = app_reg_state_cb, .channel_state_cb = channel_state_cb, }; /* init */ static void init_p(int argc, const char **argv) { int i, j, k; for (i = 0; i < APP_ID_SIZE; i++) { for (j = 0; j < MDEP_CFG_SIZE; j++) { app[i].mdep[j].role = 0; for (k = 0; k < CHANNEL_ID_SIZE; k++) app[i].mdep[j].channel[k].fd = -1; } } RETURN_IF_NULL(if_hl); EXEC(if_hl->init, &hl_cbacks); } /* register_application */ static void register_application_p(int argc, const char **argv) { bthl_reg_param_t reg; uint16_t mdep_argc_init, mdep_argc_off; int app_id = -1; int i; RETURN_IF_NULL(if_hl); if (argc <= 2) { haltest_error("No app name is specified\n"); return; } if (argc <= 3) { haltest_error("No provider is specified\n"); return; } if (argc <= 4) { haltest_error("No service name is specified\n"); return; } if (argc <= 5) { haltest_error("No service description is specified\n"); return; } if (argc <= 6) { haltest_error("No num of mdeps is specified\n"); return; } memset(®, 0, sizeof(reg)); if (argc != ((atoi(argv[6]) * 4) + 7)) { haltest_error("mdep cfg argumetns are not proper\n"); return; } reg.application_name = argv[2]; if (strcmp("-", argv[3])) reg.provider_name = argv[3]; if (strcmp("-", argv[4])) reg.srv_name = argv[4]; if (strcmp("-", argv[5])) reg.srv_desp = argv[5]; reg.number_of_mdeps = atoi(argv[6]); reg.mdep_cfg = malloc(reg.number_of_mdeps * sizeof(bthl_mdep_cfg_t)); if (!reg.mdep_cfg) { haltest_error("malloc failed\n"); return; } mdep_argc_init = 7; for (i = 0; i < reg.number_of_mdeps; i++) { mdep_argc_off = mdep_argc_init + (4 * i); reg.mdep_cfg[i].mdep_role = str2bthl_mdep_role_t(argv[mdep_argc_off]); reg.mdep_cfg[i].data_type = atoi(argv[mdep_argc_off + 1]); reg.mdep_cfg[i].channel_type = str2bthl_channel_type_t(argv[mdep_argc_off + 2]); if (!strcmp("-", argv[mdep_argc_off + 3])) { reg.mdep_cfg[i].mdep_description = NULL; continue; } reg.mdep_cfg[i].mdep_description = argv[mdep_argc_off + 3]; } EXEC(if_hl->register_application, ®, &app_id); for (i = 0; i < reg.number_of_mdeps; i++) app[app_id].mdep[i].role = reg.mdep_cfg[i].mdep_role; free(reg.mdep_cfg); } /* unregister_application */ static void unregister_application_p(int argc, const char **argv) { uint32_t app_id; RETURN_IF_NULL(if_hl); if (argc <= 2) { haltest_error("No app id is specified"); return; } app_id = (uint32_t) atoi(argv[2]); EXEC(if_hl->unregister_application, app_id); } /* connect_channel */ static void connect_channel_p(int argc, const char **argv) { uint32_t app_id, mdep_cfg_index; int channel_id = -1; bt_bdaddr_t bd_addr; RETURN_IF_NULL(if_hl); if (argc <= 2) { haltest_error("No app id is specified"); return; } VERIFY_ADDR_ARG(3, &bd_addr); if (argc <= 4) { haltest_error("No mdep cfg index is specified"); return; } app_id = (uint32_t) atoi(argv[2]); mdep_cfg_index = (uint32_t) atoi(argv[4]); EXEC(if_hl->connect_channel, app_id, &bd_addr, mdep_cfg_index, &channel_id); } /* destroy_channel */ static void destroy_channel_p(int argc, const char **argv) { uint32_t channel_id; RETURN_IF_NULL(if_hl); if (argc <= 2) { haltest_error("No channel id is specified"); return; } channel_id = (uint32_t) atoi(argv[2]); EXEC(if_hl->destroy_channel, channel_id); } /* close_channel */ static void close_channel_p(int argc, const char **argv) { uint32_t app_id; uint8_t index; int channel_id; RETURN_IF_NULL(if_hl); if (argc <= 2) { haltest_error("No app id is specified"); return; } if (argc <= 3) { haltest_error("No mdep_cfg_index is specified"); return; } if (argc <= 4) { haltest_error("No channel_id is specified"); return; } app_id = (uint32_t) atoi(argv[2]); if (app_id >= APP_ID_SIZE) { haltest_error("Wrong app_id specified: %u\n", app_id); return; } index = (uint8_t) atoi(argv[3]); if (index >= MDEP_CFG_SIZE) { haltest_error("Wrong mdep cfg index: %u\n", index); return; } channel_id = atoi(argv[4]); if (channel_id >= CHANNEL_ID_SIZE) { haltest_error("Wrong channel id: %u\n", channel_id); return; } if (app[app_id].mdep[index].channel[channel_id].fd >= 0) { shutdown(app[app_id].mdep[index].channel[channel_id].fd, SHUT_RDWR); app[app_id].mdep[index].channel[channel_id].fd = -1; } } /* cleanup */ static void cleanup_p(int argc, const char **argv) { RETURN_IF_NULL(if_hl); EXECV(if_hl->cleanup); if_hl = NULL; } static struct method methods[] = { STD_METHOD(init), STD_METHODH(register_application, " \n" "\n" "[[] [] [] []]" "..."), STD_METHODH(unregister_application, ""), STD_METHODH(connect_channel, " "), STD_METHODH(destroy_channel, ""), STD_METHODH(close_channel, " "), STD_METHOD(cleanup), END_METHOD }; const struct interface hl_if = { .name = "hl", .methods = methods }; bluez-5.82/android/client/PaxHeaders/pollhandler.h0000644000000000000000000000005014015011623017212 xustar0020 atime=1743516867 20 ctime=1743591279 bluez-5.82/android/client/pollhandler.h0000644000000000000000000000057114015011623016676 0ustar00rootroot/* SPDX-License-Identifier: Apache-2.0 */ /* * Copyright (C) 2013 Intel Corporation * */ #include /* Function to be called when there are event for some descriptor */ typedef void (*poll_handler)(struct pollfd *pollfd); int poll_register_fd(int fd, short events, poll_handler ph); int poll_unregister_fd(int fd, poll_handler ph); void poll_dispatch_loop(void); bluez-5.82/android/client/PaxHeaders/pollhandler.c0000644000000000000000000000005014015011623017205 xustar0020 atime=1743516867 20 ctime=1743591279 bluez-5.82/android/client/pollhandler.c0000644000000000000000000000467514015011623016702 0ustar00rootroot// SPDX-License-Identifier: Apache-2.0 /* * Copyright (C) 2013 Intel Corporation * */ #include #include #include #include "pollhandler.h" /* * Code that allows to poll multiply file descriptors for events * File descriptors can be added and removed at runtime * * Call poll_register_fd function first to add file descriptors to monitor * Then call poll_dispatch_loop that will poll all registered file descriptors * as long as they are not unregistered. * * When event happen on given fd appropriate user supplied handler is called */ /* Maximum number of files to monitor */ #define MAX_OPEN_FD 10 /* Storage for pollfd structures for monitored file descriptors */ static struct pollfd fds[MAX_OPEN_FD]; static poll_handler fds_handler[MAX_OPEN_FD]; /* Number of registered file descriptors */ static int fds_count = 0; /* * Function polls file descriptor in loop and calls appropriate handler * on event. Function returns when there is no more file descriptor to * monitor */ void poll_dispatch_loop(void) { while (fds_count > 0) { int i; int cur_fds_count = fds_count; int ready = poll(fds, fds_count, 1000); for (i = 0; i < fds_count && ready > 0; ++i) { if (fds[i].revents == 0) continue; fds_handler[i](fds + i); ready--; /* * If handler was remove from table * just skip the rest and poll again * This is due to reordering of tables in * register/unregister functions */ if (cur_fds_count != fds_count) break; } } } /* * Registers file descriptor to be monitored for events (see man poll(2)) * for events. * * return non negative value on success * -EMFILE when there are to much descriptors */ int poll_register_fd(int fd, short events, poll_handler ph) { if (fds_count >= MAX_OPEN_FD) return -EMFILE; fds_handler[fds_count] = ph; fds[fds_count].fd = fd; fds[fds_count].events = events; fds_count++; return fds_count; } /* * Unregisters file descriptor * Both fd and ph must match previously registered data * * return 0 if unregister succeeded * -EBADF if arguments do not match any register handler */ int poll_unregister_fd(int fd, poll_handler ph) { int i; for (i = 0; i < fds_count; ++i) { if (fds_handler[i] == ph && fds[i].fd == fd) { fds_count--; if (i < fds_count) { fds[i].fd = fds[fds_count].fd; fds[i].events = fds[fds_count].events; fds_handler[i] = fds_handler[fds_count]; } return 0; } } return -EBADF; } bluez-5.82/android/client/PaxHeaders/history.c0000644000000000000000000000005014015011623016402 xustar0020 atime=1743516867 20 ctime=1743591279 bluez-5.82/android/client/history.c0000644000000000000000000000310614015011623016063 0ustar00rootroot// SPDX-License-Identifier: Apache-2.0 /* * Copyright (C) 2013 Intel Corporation * */ #include #include #include #include #include "history.h" /* Very simple history storage for easy usage of tool */ #define HISTORY_DEPTH 40 #define LINE_SIZE 200 static char lines[HISTORY_DEPTH][LINE_SIZE]; static int last_line = 0; static int history_size = 0; /* TODO: Storing history not implemented yet */ void history_store(const char *filename) { } /* Restoring history from file */ void history_restore(const char *filename) { char line[1000]; FILE *f = fopen(filename, "rt"); if (f == NULL) return; for (;;) { if (fgets(line, 1000, f) != NULL) { int l = strlen(line); while (l > 0 && isspace(line[--l])) line[l] = 0; if (l > 0) history_add_line(line); } else break; } fclose(f); } /* Add new line to history buffer */ void history_add_line(const char *line) { if (line == NULL || strlen(line) == 0) return; if (strcmp(line, lines[last_line]) == 0) return; last_line = (last_line + 1) % HISTORY_DEPTH; strncpy(&lines[last_line][0], line, LINE_SIZE - 1); if (history_size < HISTORY_DEPTH) history_size++; } /* * Get n-th line from history * 0 - means latest * -1 - means oldest * return -1 if there is no such line */ int history_get_line(int n, char *buf, int buf_size) { if (n == -1) n = history_size - 1; if (n >= history_size || buf_size == 0 || n < 0) return -1; strncpy(buf, &lines[(HISTORY_DEPTH + last_line - n) % HISTORY_DEPTH][0], buf_size - 1); buf[buf_size - 1] = 0; return n; } bluez-5.82/android/client/PaxHeaders/history.h0000644000000000000000000000005014015011623016407 xustar0020 atime=1743516867 20 ctime=1743591279 bluez-5.82/android/client/history.h0000644000000000000000000000042214015011623016066 0ustar00rootroot/* SPDX-License-Identifier: Apache-2.0 */ /* * Copyright (C) 2013 Intel Corporation * */ void history_store(const char *filename); void history_restore(const char *filename); void history_add_line(const char *line); int history_get_line(int n, char *buf, int buf_size); bluez-5.82/android/client/PaxHeaders/if-av-sink.c0000644000000000000000000000005014015011623016645 xustar0020 atime=1743516867 20 ctime=1743591279 bluez-5.82/android/client/if-av-sink.c0000644000000000000000000000541014015011623016326 0ustar00rootroot// SPDX-License-Identifier: Apache-2.0 /* * Copyright (C) 2014 Intel Corporation * */ #define _GNU_SOURCE #include "if-main.h" #include "../hal-utils.h" const btav_interface_t *if_av_sink = NULL; SINTMAP(btav_connection_state_t, -1, "(unknown)") DELEMENT(BTAV_CONNECTION_STATE_DISCONNECTED), DELEMENT(BTAV_CONNECTION_STATE_CONNECTING), DELEMENT(BTAV_CONNECTION_STATE_CONNECTED), DELEMENT(BTAV_CONNECTION_STATE_DISCONNECTING), ENDMAP SINTMAP(btav_audio_state_t, -1, "(unknown)") DELEMENT(BTAV_AUDIO_STATE_REMOTE_SUSPEND), DELEMENT(BTAV_AUDIO_STATE_STOPPED), DELEMENT(BTAV_AUDIO_STATE_STARTED), ENDMAP static char last_addr[MAX_ADDR_STR_LEN]; static void connection_state(btav_connection_state_t state, bt_bdaddr_t *bd_addr) { haltest_info("(sink) %s: connection_state=%s remote_bd_addr=%s\n", __func__, btav_connection_state_t2str(state), bt_bdaddr_t2str(bd_addr, last_addr)); } static void audio_state(btav_audio_state_t state, bt_bdaddr_t *bd_addr) { haltest_info("(sink) %s: audio_state=%s remote_bd_addr=%s\n", __func__, btav_audio_state_t2str(state), bt_bdaddr_t2str(bd_addr, last_addr)); } static void audio_config(bt_bdaddr_t *bd_addr, uint32_t sample_rate, uint8_t channel_count) { haltest_info("(sink) %s: addr=%s\n sample_rate=%d\n channel_count=%d\n", __func__, bt_bdaddr_t2str(bd_addr, last_addr), sample_rate, channel_count); } static btav_callbacks_t av_cbacks = { .size = sizeof(av_cbacks), .connection_state_cb = connection_state, .audio_state_cb = audio_state, .audio_config_cb = audio_config, }; /* init */ static void init_p(int argc, const char **argv) { RETURN_IF_NULL(if_av_sink); EXEC(if_av_sink->init, &av_cbacks); } /* connect */ static void connect_c(int argc, const char **argv, enum_func *enum_func, void **user) { if (argc == 3) { *user = NULL; *enum_func = enum_devices; } } static void connect_p(int argc, const char **argv) { bt_bdaddr_t addr; RETURN_IF_NULL(if_av_sink); VERIFY_ADDR_ARG(2, &addr); EXEC(if_av_sink->connect, &addr); } /* disconnect */ static void disconnect_c(int argc, const char **argv, enum_func *enum_func, void **user) { if (argc == 3) { *user = last_addr; *enum_func = enum_one_string; } } static void disconnect_p(int argc, const char **argv) { bt_bdaddr_t addr; RETURN_IF_NULL(if_av_sink); VERIFY_ADDR_ARG(2, &addr); EXEC(if_av_sink->disconnect, &addr); } /* cleanup */ static void cleanup_p(int argc, const char **argv) { RETURN_IF_NULL(if_av_sink); EXECV(if_av_sink->cleanup); if_av_sink = NULL; } static struct method methods[] = { STD_METHOD(init), STD_METHODCH(connect, ""), STD_METHODCH(disconnect, ""), STD_METHOD(cleanup), END_METHOD }; const struct interface av_sink_if = { .name = "av-sink", .methods = methods }; bluez-5.82/android/client/PaxHeaders/if-rc.c0000644000000000000000000000005014015011623015701 xustar0020 atime=1743516867 20 ctime=1743591279 bluez-5.82/android/client/if-rc.c0000644000000000000000000002162614015011623015371 0ustar00rootroot// SPDX-License-Identifier: Apache-2.0 /* * Copyright (C) 2014 Intel Corporation * */ #define _GNU_SOURCE #include #include #include #include #include #include "if-main.h" #include "pollhandler.h" #include "../hal-utils.h" const btrc_interface_t *if_rc = NULL; SINTMAP(btrc_play_status_t, -1, "(unknown)") DELEMENT(BTRC_PLAYSTATE_STOPPED), DELEMENT(BTRC_PLAYSTATE_PLAYING), DELEMENT(BTRC_PLAYSTATE_PAUSED), DELEMENT(BTRC_PLAYSTATE_FWD_SEEK), DELEMENT(BTRC_PLAYSTATE_REV_SEEK), DELEMENT(BTRC_PLAYSTATE_ERROR), ENDMAP SINTMAP(btrc_media_attr_t, -1, "(unknown)") DELEMENT(BTRC_MEDIA_ATTR_TITLE), DELEMENT(BTRC_MEDIA_ATTR_ARTIST), DELEMENT(BTRC_MEDIA_ATTR_ALBUM), DELEMENT(BTRC_MEDIA_ATTR_TRACK_NUM), DELEMENT(BTRC_MEDIA_ATTR_NUM_TRACKS), DELEMENT(BTRC_MEDIA_ATTR_GENRE), DELEMENT(BTRC_MEDIA_ATTR_PLAYING_TIME), ENDMAP SINTMAP(btrc_status_t, -1, "(unknown)") DELEMENT(BTRC_STS_BAD_CMD), DELEMENT(BTRC_STS_BAD_PARAM), DELEMENT(BTRC_STS_NOT_FOUND), DELEMENT(BTRC_STS_INTERNAL_ERR), DELEMENT(BTRC_STS_NO_ERROR), ENDMAP SINTMAP(btrc_event_id_t, -1, "(unknown)") DELEMENT(BTRC_EVT_PLAY_STATUS_CHANGED), DELEMENT(BTRC_EVT_TRACK_CHANGE), DELEMENT(BTRC_EVT_TRACK_REACHED_END), DELEMENT(BTRC_EVT_TRACK_REACHED_START), DELEMENT(BTRC_EVT_PLAY_POS_CHANGED), DELEMENT(BTRC_EVT_APP_SETTINGS_CHANGED), ENDMAP SINTMAP(btrc_notification_type_t, -1, "(unknown)") DELEMENT(BTRC_NOTIFICATION_TYPE_INTERIM), DELEMENT(BTRC_NOTIFICATION_TYPE_CHANGED), ENDMAP static char last_addr[MAX_ADDR_STR_LEN]; static void remote_features_cb(bt_bdaddr_t *bd_addr, btrc_remote_features_t features) { haltest_info("%s: remote_bd_addr=%s features=%u\n", __func__, bt_bdaddr_t2str(bd_addr, last_addr), features); } static void get_play_status_cb(void) { haltest_info("%s\n", __func__); } static void list_player_app_attr_cb(void) { haltest_info("%s\n", __func__); } static void list_player_app_values_cb(btrc_player_attr_t attr_id) { haltest_info("%s, attr_id=%d\n", __func__, attr_id); } static void get_player_app_value_cb(uint8_t num_attr, btrc_player_attr_t *p_attrs) { int i; haltest_info("%s, num_attr=%d\n", __func__, num_attr); for (i = 0; i < num_attr; i++) haltest_info("attribute=%u\n", p_attrs[i]); } static void get_player_app_attrs_text_cb(uint8_t num_attr, btrc_player_attr_t *p_attrs) { int i; haltest_info("%s, num_attr=%d\n", __func__, num_attr); for (i = 0; i < num_attr; i++) haltest_info("attribute=%u\n", p_attrs[i]); } static void get_player_app_values_text_cb(uint8_t attr_id, uint8_t num_val, uint8_t *p_vals) { haltest_info("%s, attr_id=%d num_val=%d values=%p\n", __func__, attr_id, num_val, p_vals); } static void set_player_app_value_cb(btrc_player_settings_t *p_vals) { int i; haltest_info("%s, num_attr=%u\n", __func__, p_vals->num_attr); for (i = 0; i < p_vals->num_attr; i++) haltest_info("attr id=%u, values=%u\n", p_vals->attr_ids[i], p_vals->attr_values[i]); } static void get_element_attr_cb(uint8_t num_attr, btrc_media_attr_t *attrs) { uint8_t i; haltest_info("%s, num_of_attributes=%d\n", __func__, num_attr); for (i = 0; i < num_attr; i++) haltest_info("attr id=%s\n", btrc_media_attr_t2str(attrs[i])); } static void register_notification_cb(btrc_event_id_t event_id, uint32_t param) { haltest_info("%s, event=%u param=%u\n", __func__, event_id, param); } static void volume_change_cb(uint8_t volume, uint8_t ctype) { haltest_info("%s, volume=%d ctype=%d\n", __func__, volume, ctype); } static void passthrough_cmd_cb(int id, int key_state) { haltest_info("%s, id=%d key_state=%d\n", __func__, id, key_state); } static btrc_callbacks_t rc_cbacks = { .size = sizeof(rc_cbacks), .remote_features_cb = remote_features_cb, .get_play_status_cb = get_play_status_cb, .list_player_app_attr_cb = list_player_app_attr_cb, .list_player_app_values_cb = list_player_app_values_cb, .get_player_app_value_cb = get_player_app_value_cb, .get_player_app_attrs_text_cb = get_player_app_attrs_text_cb, .get_player_app_values_text_cb = get_player_app_values_text_cb, .set_player_app_value_cb = set_player_app_value_cb, .get_element_attr_cb = get_element_attr_cb, .register_notification_cb = register_notification_cb, .volume_change_cb = volume_change_cb, .passthrough_cmd_cb = passthrough_cmd_cb, }; /* init */ static void init_p(int argc, const char **argv) { RETURN_IF_NULL(if_rc); EXEC(if_rc->init, &rc_cbacks); } /* get_play_status_rsp */ static void get_play_status_rsp_c(int argc, const char **argv, enum_func *enum_func, void **user) { if (argc == 3) { *user = TYPE_ENUM(btrc_play_status_t); *enum_func = enum_defines; } } static void get_play_status_rsp_p(int argc, const char **argv) { btrc_play_status_t play_status; uint32_t song_len, song_pos; RETURN_IF_NULL(if_rc); if (argc <= 2) { haltest_error("No play status specified"); return; } if (argc <= 3) { haltest_error("No song length specified"); return; } if (argc <= 4) { haltest_error("No song position specified"); return; } play_status = str2btrc_play_status_t(argv[2]); song_len = (uint32_t) atoi(argv[3]); song_pos = (uint32_t) atoi(argv[4]); EXEC(if_rc->get_play_status_rsp, play_status, song_len, song_pos); } /* get_element_attr_rsp */ static void get_element_attr_rsp_c(int argc, const char **argv, enum_func *enum_func, void **user) { if (argc == 4) { *user = TYPE_ENUM(btrc_media_attr_t); *enum_func = enum_defines; } } static void get_element_attr_rsp_p(int argc, const char **argv) { uint8_t num_attr; btrc_element_attr_val_t attrs; RETURN_IF_NULL(if_rc); if (argc <= 2) { haltest_error("No number of attributes specified"); return; } if (argc <= 4) { haltest_error("No attr id and value specified"); return; } num_attr = (uint8_t) atoi(argv[2]); attrs.attr_id = str2btrc_media_attr_t(argv[3]); strcpy((char *)attrs.text, argv[4]); EXEC(if_rc->get_element_attr_rsp, num_attr, &attrs); } /* set_volume */ static void set_volume_c(int argc, const char **argv, enum_func *enum_func, void **user) { } static void set_volume_p(int argc, const char **argv) { uint8_t volume; RETURN_IF_NULL(if_rc); if (argc <= 2) { haltest_error("No volume specified"); return; } volume = (uint8_t) atoi(argv[2]); EXEC(if_rc->set_volume, volume); } /* set_player_app_value_rsp */ static void set_player_app_value_rsp_c(int argc, const char **argv, enum_func *enum_func, void **user) { if (argc == 3) { *user = TYPE_ENUM(btrc_status_t); *enum_func = enum_defines; } } static void set_player_app_value_rsp_p(int argc, const char **argv) { btrc_status_t rsp_status; RETURN_IF_NULL(if_rc); if (argc <= 2) { haltest_error("No response status specified"); return; } rsp_status = str2btrc_status_t(argv[2]); EXEC(if_rc->set_player_app_value_rsp, rsp_status); } /* register_notification_rsp */ static void register_notification_rsp_c(int argc, const char **argv, enum_func *enum_func, void **user) { if (argc == 3) { *user = TYPE_ENUM(btrc_event_id_t); *enum_func = enum_defines; } if (argc == 4) { *user = TYPE_ENUM(btrc_notification_type_t); *enum_func = enum_defines; } } static void register_notification_rsp_p(int argc, const char **argv) { btrc_event_id_t event_id; btrc_notification_type_t type; btrc_register_notification_t reg; uint32_t song_pos; uint64_t track; RETURN_IF_NULL(if_rc); memset(®, 0, sizeof(reg)); event_id = str2btrc_event_id_t(argv[2]); type = str2btrc_notification_type_t(argv[3]); switch (event_id) { case BTRC_EVT_PLAY_STATUS_CHANGED: reg.play_status = str2btrc_play_status_t(argv[4]); break; case BTRC_EVT_TRACK_CHANGE: track = strtoull(argv[5], NULL, 10); memcpy(reg.track, &track, sizeof(btrc_uid_t)); break; case BTRC_EVT_TRACK_REACHED_END: case BTRC_EVT_TRACK_REACHED_START: break; case BTRC_EVT_PLAY_POS_CHANGED: song_pos = strtoul(argv[4], NULL, 10); memcpy(®.song_pos, &song_pos, sizeof(uint32_t)); break; case BTRC_EVT_APP_SETTINGS_CHANGED: haltest_error("not supported"); return; } EXEC(if_rc->register_notification_rsp, event_id, type, ®); } /* cleanup */ static void cleanup_p(int argc, const char **argv) { RETURN_IF_NULL(if_rc); EXECV(if_rc->cleanup); if_rc = NULL; } static struct method methods[] = { STD_METHOD(init), STD_METHODCH(get_play_status_rsp, " "), STD_METHODCH(get_element_attr_rsp, " "), STD_METHODCH(set_player_app_value_rsp, ""), STD_METHODCH(set_volume, ""), STD_METHODCH(register_notification_rsp, " \n" "BTRC_EVT_PLAY_STATUS_CHANGED \n" "BTRC_EVT_TRACK_CHANGE \n" "BTRC_EVT_TRACK_REACHED_END \n" "BTRC_EVT_TRACK_REACHED_START \n" "BTRC_EVT_PLAY_POS_CHANGED \n"), STD_METHOD(cleanup), END_METHOD }; const struct interface rc_if = { .name = "rc", .methods = methods }; bluez-5.82/android/client/PaxHeaders/haltest.c0000644000000000000000000000005014015011623016345 xustar0020 atime=1743516867 20 ctime=1743591279 bluez-5.82/android/client/haltest.c0000644000000000000000000002244214015011623016032 0ustar00rootroot// SPDX-License-Identifier: Apache-2.0 /* * Copyright (C) 2013 Intel Corporation * */ #ifdef HAVE_CONFIG_H #include #endif #define _GNU_SOURCE #include #include #include #include #include #include #include #include #include #include "if-main.h" #include "terminal.h" #include "pollhandler.h" #include "history.h" static void process_line(char *line_buffer); const struct interface *interfaces[] = { &audio_if, &sco_if, &bluetooth_if, &av_if, &rc_if, &gatt_if, &gatt_client_if, &gatt_server_if, &hf_if, &hh_if, &pan_if, &hl_if, &sock_if, #if ANDROID_VERSION >= PLATFORM_VER(5, 0, 0) &hf_client_if, &mce_if, &ctrl_rc_if, &av_sink_if, #endif NULL }; static struct method commands[]; struct method *get_method(struct method *methods, const char *name) { while (strcmp(methods->name, "") != 0) { if (strcmp(methods->name, name) == 0) return methods; methods++; } return NULL; } /* function returns interface of given name or NULL if not found */ const struct interface *get_interface(const char *name) { int i; for (i = 0; interfaces[i] != NULL; ++i) { if (strcmp(interfaces[i]->name, name) == 0) break; } return interfaces[i]; } int haltest_error(const char *format, ...) { va_list args; int ret; va_start(args, format); ret = terminal_vprint(format, args); va_end(args); return ret; } int haltest_info(const char *format, ...) { va_list args; int ret; va_start(args, format); ret = terminal_vprint(format, args); va_end(args); return ret; } int haltest_warn(const char *format, ...) { va_list args; int ret; va_start(args, format); ret = terminal_vprint(format, args); va_end(args); return ret; } static void help_print_interface(const struct interface *i) { struct method *m; for (m = i->methods; strcmp(m->name, "") != 0; m++) haltest_info("%s %s %s\n", i->name, m->name, (m->help ? m->help : "")); } /* Help completion */ static void help_c(int argc, const char **argv, enum_func *enum_func, void **user) { if (argc == 2) *enum_func = interface_name; } /* Help execution */ static void help_p(int argc, const char **argv) { const struct method *m = commands; const struct interface **ip = interfaces; const struct interface *i; if (argc == 1) { terminal_print("haltest allows to call Android HAL methods.\n"); terminal_print("\nAvailable commands:\n"); while (0 != strcmp(m->name, "")) { terminal_print("\t%s %s\n", m->name, (m->help ? m->help : "")); m++; } terminal_print("\nAvailable interfaces to use:\n"); while (NULL != *ip) { terminal_print("\t%s\n", (*ip)->name); ip++; } terminal_print("\nTo get help on methods for each interface type:\n"); terminal_print("\n\thelp \n"); terminal_print("\nBasic scenario:\n\tbluetooth init\n"); terminal_print("\tbluetooth enable\n\tbluetooth start_discovery\n"); terminal_print("\tbluetooth get_profile_interface handsfree\n"); terminal_print("\thandsfree init\n\n"); return; } i = get_interface(argv[1]); if (i == NULL) { haltest_error("No such interface\n"); return; } help_print_interface(i); } /* quit/exit execution */ static void quit_p(int argc, const char **argv) { char cleanup_audio[] = "audio cleanup"; close_hw_bt_dev(); process_line(cleanup_audio); exit(0); } static int fd_stack[10]; static int fd_stack_pointer = 0; static void stdin_handler(struct pollfd *pollfd); static void process_file(const char *name) { int fd = open(name, O_RDONLY); if (fd < 0) { haltest_error("Can't open file: %s for reading\n", name); return; } if (fd_stack_pointer >= 10) { haltest_error("To many open files\n"); close(fd); return; } fd_stack[fd_stack_pointer++] = fd; poll_unregister_fd(fd_stack[fd_stack_pointer - 2], stdin_handler); poll_register_fd(fd_stack[fd_stack_pointer - 1], POLLIN, stdin_handler); } static void source_p(int argc, const char **argv) { if (argc < 2) { haltest_error("No file specified"); return; } process_file(argv[1]); } /* Commands available without interface */ static struct method commands[] = { STD_METHODCH(help, "[]"), STD_METHOD(quit), METHOD("exit", quit_p, NULL, NULL), STD_METHODH(source, ""), END_METHOD }; /* Gets comman by name */ struct method *get_command(const char *name) { return get_method(commands, name); } /* Function to enumerate interface names */ const char *interface_name(void *v, int i) { return interfaces[i] ? interfaces[i]->name : NULL; } /* Function to enumerate command and interface names */ const char *command_name(void *v, int i) { int cmd_cnt = NELEM(commands); if (i >= cmd_cnt) return interface_name(v, i - cmd_cnt); else return commands[i].name; } /* * This function changes input parameter line_buffer so it has * null termination after each token (due to strtok) * Output argv is filled with pointers to arguments * returns number of tokens parsed - argc */ static int command_line_to_argv(char *line_buffer, char *argv[], int argv_size) { static const char *token_breaks = "\r\n\t "; char *token; int argc = 0; token = strtok(line_buffer, token_breaks); while (token != NULL && argc < (int) argv_size) { argv[argc++] = token; token = strtok(NULL, token_breaks); } return argc; } static void process_line(char *line_buffer) { char *argv[50]; int argc; int i = 0; struct method *m; argc = command_line_to_argv(line_buffer, argv, 50); if (argc < 1) return; while (interfaces[i] != NULL) { if (strcmp(interfaces[i]->name, argv[0])) { i++; continue; } if (argc < 2 || strcmp(argv[1], "?") == 0) { help_print_interface(interfaces[i]); return; } m = get_method(interfaces[i]->methods, argv[1]); if (m != NULL) { m->func(argc, (const char **) argv); return; } haltest_error("No function %s found\n", argv[1]); return; } /* No interface, try commands */ m = get_command(argv[0]); if (m == NULL) haltest_error("No such command %s\n", argv[0]); else m->func(argc, (const char **) argv); } /* called when there is something on stdin */ static void stdin_handler(struct pollfd *pollfd) { char buf[10]; if (pollfd->revents & POLLIN) { int count = read(fd_stack[fd_stack_pointer - 1], buf, 10); if (count > 0) { int i; for (i = 0; i < count; ++i) terminal_process_char(buf[i], process_line); return; } } if (fd_stack_pointer > 1) poll_register_fd(fd_stack[fd_stack_pointer - 2], POLLIN, stdin_handler); if (fd_stack_pointer > 0) { poll_unregister_fd(fd_stack[--fd_stack_pointer], stdin_handler); if (fd_stack[fd_stack_pointer]) close(fd_stack[fd_stack_pointer]); } } static void usage(void) { printf("haltest Android Bluetooth HAL testing tool\n" "Usage:\n"); printf("\thaltest [options]\n"); printf("options:\n" "\t-i --ivi Initialize only IVI interfaces\n" "\t-n, --no-init Don't initialize any interfaces\n" "\t-v --version Print version\n" "\t-h, --help Show help options\n"); } static void print_version(void) { printf("haltest version %s\n", VERSION); } static const struct option main_options[] = { { "no-init", no_argument, NULL, 'n' }, { "ivi", no_argument, NULL, 'i' }, { "help", no_argument, NULL, 'h' }, { "version", no_argument, NULL, 'v' }, { NULL } }; static bool no_init = false; static bool ivi_only = false; static void parse_command_line(int argc, char *argv[]) { for (;;) { int opt; opt = getopt_long(argc, argv, "inhv", main_options, NULL); if (opt < 0) break; switch (opt) { case 'n': no_init = true; break; case 'i': ivi_only = true; break; case 'h': usage(); exit(0); case 'v': print_version(); exit(0); default: putchar('\n'); exit(-1); break; } } } static const char * const interface_names[] = { BT_PROFILE_HANDSFREE_ID, BT_PROFILE_ADVANCED_AUDIO_ID, BT_PROFILE_AV_RC_ID, BT_PROFILE_HEALTH_ID, BT_PROFILE_HIDHOST_ID, BT_PROFILE_PAN_ID, BT_PROFILE_GATT_ID, BT_PROFILE_SOCKETS_ID, NULL }; static const char * const ivi_interface_inames[] = { #if ANDROID_VERSION >= PLATFORM_VER(5, 0, 0) BT_PROFILE_HANDSFREE_CLIENT_ID, BT_PROFILE_MAP_CLIENT_ID, BT_PROFILE_AV_RC_CTRL_ID, BT_PROFILE_ADVANCED_AUDIO_SINK_ID, #endif NULL }; static void init(const char * const *inames) { const struct method *m; const char *argv[4]; char init_audio[] = "audio init"; char init_sco[] = "sco init"; char init_bt[] = "bluetooth init"; uint32_t i; process_line(init_audio); process_line(init_sco); process_line(init_bt); m = get_interface_method("bluetooth", "get_profile_interface"); while (*inames) { argv[2] = *inames; m->func(3, argv); inames++; } /* Init what is available to init */ for (i = 3; i < NELEM(interfaces) - 1; ++i) { m = get_interface_method(interfaces[i]->name, "init"); if (m != NULL) m->func(2, argv); } } int main(int argc, char **argv) { struct stat rcstat; parse_command_line(argc, argv); terminal_setup(); if (!no_init) { if (ivi_only) init(ivi_interface_inames); else init(interface_names); } history_restore(".haltest_history"); fd_stack[fd_stack_pointer++] = 0; /* Register command line handler */ poll_register_fd(0, POLLIN, stdin_handler); if (stat(".haltestrc", &rcstat) == 0 && (rcstat.st_mode & S_IFREG) != 0) process_file(".haltestrc"); poll_dispatch_loop(); return 0; } bluez-5.82/android/client/PaxHeaders/if-gatt.c0000644000000000000000000000005014015011623016234 xustar0020 atime=1743516867 20 ctime=1743591279 bluez-5.82/android/client/if-gatt.c0000644000000000000000000021246114015011623015723 0ustar00rootroot// SPDX-License-Identifier: Apache-2.0 /* * Copyright (C) 2013 Intel Corporation * */ #define _GNU_SOURCE #include #include #include #include "../hal-utils.h" #include "if-main.h" const btgatt_interface_t *if_gatt = NULL; /* * In version 19 some callback were changed. * btgatt_char_id_t -> btgatt_gatt_id_t * bt_uuid_t -> btgatt_gatt_id_t */ #define str2btgatt_descr_id_t str2btgatt_gatt_id_t #define btgatt_descr_id_t2str btgatt_gatt_id_t2str #define btgatt_descr_id_t btgatt_gatt_id_t #define MAX_CHAR_ID_STR_LEN (MAX_UUID_STR_LEN + 3 + 11) #define MAX_SRVC_ID_STR_LEN (MAX_UUID_STR_LEN + 3 + 11 + 1 + 11) /* How man characters print from binary objects (arbitrary) */ #define MAX_HEX_VAL_STR_LEN 100 #define MAX_NOTIFY_PARAMS_STR_LEN (MAX_SRVC_ID_STR_LEN + MAX_CHAR_ID_STR_LEN \ + MAX_ADDR_STR_LEN + MAX_HEX_VAL_STR_LEN + 60) #define MAX_READ_PARAMS_STR_LEN (MAX_SRVC_ID_STR_LEN + MAX_CHAR_ID_STR_LEN \ + MAX_UUID_STR_LEN + MAX_HEX_VAL_STR_LEN + 80) /* Hex arguments must have "0x" or "0X" prefix */ #define VERIFY_INT_ARG(n, v, err) \ do { \ if (n < argc) \ v = strtol(argv[n], NULL, 0); \ else { \ haltest_error(err); \ return;\ } \ } while (0) #define VERIFY_HEX_ARG(n, v, err) \ do { \ if (n < argc) \ v = strtol(argv[n], NULL, 16); \ else { \ haltest_error(err); \ return;\ } \ } while (0) /* Helper macros to verify arguments of methods */ #define VERIFY_CLIENT_IF(n, v) VERIFY_INT_ARG(n, v, "No client_if specified\n") #define VERIFY_SERVER_IF(n, v) VERIFY_INT_ARG(n, v, "No server_if specified\n") #define VERIFY_CONN_ID(n, v) VERIFY_INT_ARG(n, v, "No conn_if specified\n") #define VERIFY_TRANS_ID(n, v) VERIFY_INT_ARG(n, v, "No trans_id specified\n") #define VERIFY_STATUS(n, v) VERIFY_INT_ARG(n, v, "No status specified\n") #define VERIFY_OFFSET(n, v) VERIFY_INT_ARG(n, v, "No offset specified\n") #define VERIFY_TEST_ARG(n, v) VERIFY_INT_ARG(n, v, "No test arg specified\n") #define VERIFY_ACTION(n, v) VERIFY_INT_ARG(n, v, "No action specified\n") #define VERIFY_FILT_TYPE(n, v) VERIFY_INT_ARG(n, v, "No filter specified\n") #define VERIFY_FILT_INDEX(n, v) VERIFY_INT_ARG(n, v, \ "No filter index specified\n") #define VERIFY_FEAT_SELN(n, v) VERIFY_INT_ARG(n, v, "No feat seln specified\n") #define VERIFY_LIST_LOGIC_TYPE(n, v) VERIFY_INT_ARG(n, v, \ "No list logic type specified\n") #define VERIFY_FILT_LOGIC_TYPE(n, v) VERIFY_INT_ARG(n, v, \ "No filt logic type specified\n") #define VERIFY_RSSI_HI_THR(n, v) VERIFY_INT_ARG(n, v, \ "No rssi hi threshold specified\n") #define VERIFY_RSSI_LOW_THR(n, v) VERIFY_INT_ARG(n, v, \ "No low threshold specified\n") #define VERIFY_DELY_MODE(n, v) VERIFY_INT_ARG(n, v, "No delay mode specified\n") #define VERIFY_FND_TIME(n, v) VERIFY_INT_ARG(n, v, "No found time specified\n") #define VERIFY_LOST_TIME(n, v) VERIFY_INT_ARG(n, v, "No lost time specified\n") #define VERIFY_FND_TIME_CNT(n, v) VERIFY_INT_ARG(n, v, \ "No found timer counter specified\n") #define VERIFY_COMP_ID(n, v) VERIFY_INT_ARG(n, v, "No company id specified\n") #define VERIFY_COMP_ID_MASK(n, v) VERIFY_INT_ARG(n, v, \ "No comp. id mask specified\n") #define VERIFY_DATA_LEN(n, v) VERIFY_INT_ARG(n, v, "No data len specified\n") #define VERIFY_MASK_LEN(n, v) VERIFY_INT_ARG(n, v, "No mask len specified\n") #define VERIFY_MIN_INTERVAL(n, v) VERIFY_INT_ARG(n, v, \ "No min interval specified\n") #define VERIFY_MAX_INTERVAL(n, v) VERIFY_INT_ARG(n, v, \ "No max interval specified\n") #define VERIFY_APPEARANCE(n, v) VERIFY_INT_ARG(n, v, "No apperance specified\n") #define VERIFY_MANUFACTURER_LEN(n, v) VERIFY_INT_ARG(n, v, \ "No manufacturer len specified\n") #define VERIFY_SERVICE_DATA_LEN(n, v) VERIFY_INT_ARG(n, v, \ "No service data len specified\n") #define VERIFY_SERVICE_UUID_LEN(n, v) VERIFY_INT_ARG(n, v, \ "No service uuid len specified\n") #define VERIFY_MTU(n, v) VERIFY_INT_ARG(n, v, "No mtu specified\n") #define VERIFY_LATENCY(n, v) VERIFY_INT_ARG(n, v, \ "No latency specified\n") #define VERIFY_TIMEOUT(n, v) VERIFY_INT_ARG(n, v, "No timeout specified\n") #define VERIFY_SCAN_INTERVAL(n, v) VERIFY_INT_ARG(n, v, \ "No scan interval specified\n") #define VERIFY_SCAN_WINDOW(n, v) VERIFY_INT_ARG(n, v, \ "No scan window specified\n") #define VERIFY_ADV_TYPE(n, v) VERIFY_INT_ARG(n, v, \ "No advertising type specified\n") #define VERIFY_CHNL_MAP(n, v) VERIFY_INT_ARG(n, v, \ "No channel map specified\n") #define VERIFY_TX_POWER(n, v) VERIFY_INT_ARG(n, v, \ "No tx power specified\n") #define VERIFY_TIMEOUT_S(n, v) VERIFY_INT_ARG(n, v, \ "No timeout in sec specified\n") #define VERIFY_BATCH_SCAN_FULL_MAX(n, v) VERIFY_INT_ARG(n, v, \ "No batch scan full max specified\n") #define VERIFY_BATCH_SCAN_TRUNC_MAX(n, v) VERIFY_INT_ARG(n, v, \ "No batch scan trunc max specified\n") #define VERIFY_BATCH_SCAN_NOTIFY_THR(n, v) VERIFY_INT_ARG(n, v, \ "No batch scan notify threshold specified\n") #define VERIFY_SCAN_MODE(n, v) VERIFY_INT_ARG(n, v, \ "No scan mode specified\n") #define VERIFY_ADDR_TYPE(n, v) VERIFY_INT_ARG(n, v, \ "No address type specified\n") #define VERIFY_DISCARD_RULE(n, v) VERIFY_INT_ARG(n, v, \ "No discard rule specified\n") #define VERIFY_HANDLE(n, v) VERIFY_HEX_ARG(n, v, "No "#v" specified\n") #define VERIFY_SERVICE_HANDLE(n, v) VERIFY_HANDLE(n, v) #define VERIFY_UUID(n, v) \ do { \ if (n < argc) \ gatt_str2bt_uuid_t(argv[n], -1, v); \ else { \ haltest_error("No uuid specified\n"); \ return;\ } \ } while (0) #define VERIFY_SRVC_ID(n, v) \ do { \ if (n < argc) \ str2btgatt_srvc_id_t(argv[n], v); \ else { \ haltest_error("No srvc_id specified\n"); \ return;\ } \ } while (0) #define VERIFY_CHAR_ID(n, v) \ do { \ if (n < argc) \ str2btgatt_gatt_id_t(argv[n], v); \ else { \ haltest_error("No char_id specified\n"); \ return;\ } \ } while (0) #define VERIFY_DESCR_ID(n, v) \ do { \ if (n < argc) \ str2btgatt_descr_id_t(argv[n], v); \ else { \ haltest_error("No descr_id specified\n"); \ return;\ } \ } while (0) #define GET_VERIFY_HEX_STRING(i, v, l) \ do { \ int ll;\ const char *n = argv[i]; \ if (argc <= i) { \ haltest_error("No value specified\n"); \ return; \ } \ if (n[0] != '0' || (n[1] != 'X' && n[1] != 'x')) { \ haltest_error("Value must be hex string\n"); \ return; \ } \ ll = fill_buffer(n + 2, (uint8_t *) v, sizeof(v)); \ if (ll < 0) { \ haltest_error("Value must be byte hex string\n"); \ return; \ } \ l = ll; \ } while (0) /* Gatt uses little endian uuid */ static const char GATT_BASE_UUID[] = { 0xfb, 0x34, 0x9b, 0x5f, 0x80, 0x00, 0x00, 0x80, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; /* * converts gatt uuid to string * buf should be at least 39 bytes * * This function formats 16, 32 and 128 bits uuid * * returns string representation of uuid */ static char *gatt_uuid_t2str(const bt_uuid_t *uuid, char *buf) { int shift = 0; int i = 16; int limit = 0; int j = 0; /* for bluetooth uuid only 32 bits */ if (0 == memcmp(&uuid->uu, &GATT_BASE_UUID, sizeof(bt_uuid_t) - 4)) { limit = 12; /* make it 16 bits */ if (uuid->uu[15] == 0 && uuid->uu[14] == 0) i = 14; } while (i-- > limit) { if (i == 11 || i == 9 || i == 7 || i == 5) { buf[j * 2 + shift] = '-'; shift++; } sprintf(buf + j * 2 + shift, "%02x", uuid->uu[i]); ++j; } return buf; } /* * Tries to convert hex string of given size into out buffer. * Output buffer is little endian. */ static void scan_field(const char *str, int len, uint8_t *out, int out_size) { int i; memset(out, 0, out_size); if (out_size * 2 > len + 1) out_size = (len + 1) / 2; for (i = 0; i < out_size && len > 0; ++i) { len -= 2; if (len >= 0) sscanf(str + len, "%02hhx", &out[i]); else sscanf(str, "%1hhx", &out[i]); } } /* Like strchr but with upper limit instead of 0 terminated string */ static const char *strchrlimit(const char *p, const char *e, int c) { while (p < e && *p != (char) c) ++p; return p < e ? p : NULL; } /* * converts string to uuid * it accepts uuid in following forms: * 123 * 0000123 * 0000123-0014-1234-0000-000056789abc * 0000123001412340000000056789abc * 123-14-1234-0-56789abc */ static void gatt_str2bt_uuid_t(const char *str, int len, bt_uuid_t *uuid) { int dash_cnt = 0; int dashes[6] = {-1}; /* indexes of '-' or \0 */ static uint8_t filed_offset[] = { 16, 12, 10, 8, 6, 0 }; const char *p = str; const char *e; int i; e = str + ((len >= 0) ? len : (int) strlen(str)); while (p != NULL && dash_cnt < 5) { const char *f = strchrlimit(p, e, '-'); if (f != NULL) dashes[++dash_cnt] = f++ - str; p = f; } /* get index of \0 to dashes table */ if (dash_cnt < 5) dashes[++dash_cnt] = e - str; memcpy(uuid, GATT_BASE_UUID, sizeof(bt_uuid_t)); /* whole uuid in one string without dashes */ if (dash_cnt == 1 && dashes[1] > 8) { if (dashes[1] > 32) dashes[1] = 32; scan_field(str, dashes[1], &uuid->uu[16 - (dashes[1] + 1) / 2], (dashes[1] + 1) / 2); } else { for (i = 0; i < dash_cnt; ++i) { scan_field(str + dashes[i] + 1, dashes[i + 1] - dashes[i] - 1, &uuid->uu[filed_offset[i + 1]], filed_offset[i] - filed_offset[i + 1]); } } } /* char_id formating function */ static char *btgatt_gatt_id_t2str(const btgatt_gatt_id_t *char_id, char *buf) { char uuid_buf[MAX_UUID_STR_LEN]; sprintf(buf, "{%s,%d}", gatt_uuid_t2str(&char_id->uuid, uuid_buf), char_id->inst_id); return buf; } /* Parse btgatt_gatt_id_t */ static void str2btgatt_gatt_id_t(const char *buf, btgatt_gatt_id_t *char_id) { const char *e; memcpy(&char_id->uuid, &GATT_BASE_UUID, sizeof(bt_uuid_t)); char_id->inst_id = 0; if (*buf == '{') buf++; e = strpbrk(buf, " ,}"); if (e == NULL) e = buf + strlen(buf); gatt_str2bt_uuid_t(buf, e - buf, &char_id->uuid); if (*e == ',') { buf = e + 1; e = strpbrk(buf, " ,}"); if (e == NULL) e = buf + strlen(buf); if (buf < e) char_id->inst_id = atoi(buf); } } /* service_id formating function */ static char *btgatt_srvc_id_t2str(const btgatt_srvc_id_t *srvc_id, char *buf) { char uuid_buf[MAX_UUID_STR_LEN]; sprintf(buf, "{%s,%d,%d}", gatt_uuid_t2str(&srvc_id->id.uuid, uuid_buf), srvc_id->id.inst_id, srvc_id->is_primary); return buf; } /* Parse btgatt_srvc_id_t */ static void str2btgatt_srvc_id_t(const char *buf, btgatt_srvc_id_t *srvc_id) { const char *e; memcpy(&srvc_id->id.uuid, &GATT_BASE_UUID, sizeof(bt_uuid_t)); srvc_id->id.inst_id = 0; srvc_id->is_primary = 1; if (*buf == '{') buf++; e = strpbrk(buf, " ,}"); if (e == NULL) e = buf + strlen(buf); gatt_str2bt_uuid_t(buf, e - buf, &srvc_id->id.uuid); if (*e == ',') { buf = e + 1; e = strpbrk(buf, " ,}"); if (e == NULL) e = buf + strlen(buf); if (buf < e) srvc_id->id.inst_id = atoi(buf); } if (*e == ',') { buf = e + 1; e = strpbrk(buf, " ,}"); if (e == NULL) e = buf + strlen(buf); if (buf < e) srvc_id->is_primary = atoi(buf); } } /* Converts array of uint8_t to string representation */ static char *array2str(const uint8_t *v, int size, char *buf, int out_size) { int limit = size; int i; if (out_size > 0) { *buf = '\0'; if (size >= 2 * out_size) limit = (out_size - 2) / 2; for (i = 0; i < limit; ++i) sprintf(buf + 2 * i, "%02x", v[i]); /* output buffer not enough to hold whole field fill with ...*/ if (limit < size) sprintf(buf + 2 * i, "..."); } return buf; } /* Converts btgatt_notify_params_t to string */ static char *btgatt_notify_params_t2str(const btgatt_notify_params_t *data, char *buf) { char addr[MAX_ADDR_STR_LEN]; char srvc_id[MAX_SRVC_ID_STR_LEN]; char char_id[MAX_CHAR_ID_STR_LEN]; char value[MAX_HEX_VAL_STR_LEN]; sprintf(buf, "{bda=%s, srvc_id=%s, char_id=%s, val=%s, is_notify=%u}", bt_bdaddr_t2str(&data->bda, addr), btgatt_srvc_id_t2str(&data->srvc_id, srvc_id), btgatt_gatt_id_t2str(&data->char_id, char_id), array2str(data->value, data->len, value, sizeof(value)), data->is_notify); return buf; } static char *btgatt_unformatted_value_t2str(const btgatt_unformatted_value_t *v, char *buf, int size) { return array2str(v->value, v->len, buf, size); } static char *btgatt_read_params_t2str(const btgatt_read_params_t *data, char *buf) { char srvc_id[MAX_SRVC_ID_STR_LEN]; char char_id[MAX_CHAR_ID_STR_LEN]; char descr_id[MAX_UUID_STR_LEN]; char value[MAX_HEX_VAL_STR_LEN]; sprintf(buf, "{srvc_id=%s, char_id=%s, descr_id=%s, val=%s value_type=%d, status=%d}", btgatt_srvc_id_t2str(&data->srvc_id, srvc_id), btgatt_gatt_id_t2str(&data->char_id, char_id), btgatt_descr_id_t2str(&data->descr_id, descr_id), btgatt_unformatted_value_t2str(&data->value, value, 100), data->value_type, data->status); return buf; } /* BT-GATT Client callbacks. */ /* Cache client_if and conn_id for tab completion */ static char client_if_str[20]; static char conn_id_str[20]; /* Cache address for tab completion */ static char last_addr[MAX_ADDR_STR_LEN]; /* Callback invoked in response to register_client */ static void gattc_register_client_cb(int status, int client_if, bt_uuid_t *app_uuid) { char buf[MAX_UUID_STR_LEN]; snprintf(client_if_str, sizeof(client_if_str), "%d", client_if); haltest_info("%s: status=%d client_if=%d app_uuid=%s\n", __func__, status, client_if, gatt_uuid_t2str(app_uuid, buf)); } /* Callback for scan results */ static void gattc_scan_result_cb(bt_bdaddr_t *bda, int rssi, uint8_t *adv_data) { char buf[MAX_ADDR_STR_LEN]; haltest_info("%s: bda=%s rssi=%d adv_data=%p\n", __func__, bt_bdaddr_t2str(bda, buf), rssi, adv_data); } /* GATT open callback invoked in response to open */ static void gattc_connect_cb(int conn_id, int status, int client_if, bt_bdaddr_t *bda) { haltest_info("%s: conn_id=%d status=%d, client_if=%d bda=%s\n", __func__, conn_id, status, client_if, bt_bdaddr_t2str(bda, last_addr)); } /* Callback invoked in response to close */ static void gattc_disconnect_cb(int conn_id, int status, int client_if, bt_bdaddr_t *bda) { char buf[MAX_ADDR_STR_LEN]; haltest_info("%s: conn_id=%d status=%d, client_if=%d bda=%s\n", __func__, conn_id, status, client_if, bt_bdaddr_t2str(bda, buf)); } /* * Invoked in response to search_service when the GATT service search * has been completed. */ static void gattc_search_complete_cb(int conn_id, int status) { haltest_info("%s: conn_id=%d status=%d\n", __func__, conn_id, status); } /* Reports GATT services on a remote device */ static void gattc_search_result_cb(int conn_id, btgatt_srvc_id_t *srvc_id) { char srvc_id_buf[MAX_SRVC_ID_STR_LEN]; haltest_info("%s: conn_id=%d srvc_id=%s\n", __func__, conn_id, btgatt_srvc_id_t2str(srvc_id, srvc_id_buf)); } /* GATT characteristic enumeration result callback */ static void gattc_get_characteristic_cb(int conn_id, int status, btgatt_srvc_id_t *srvc_id, btgatt_gatt_id_t *char_id, int char_prop) { char srvc_id_buf[MAX_SRVC_ID_STR_LEN]; char char_id_buf[MAX_CHAR_ID_STR_LEN]; haltest_info("%s: conn_id=%d status=%d srvc_id=%s char_id=%s, char_prop=0x%x\n", __func__, conn_id, status, btgatt_srvc_id_t2str(srvc_id, srvc_id_buf), btgatt_gatt_id_t2str(char_id, char_id_buf), char_prop); /* enumerate next characteristic */ if (status == 0) EXEC(if_gatt->client->get_characteristic, conn_id, srvc_id, char_id); } /* GATT descriptor enumeration result callback */ static void gattc_get_descriptor_cb(int conn_id, int status, btgatt_srvc_id_t *srvc_id, btgatt_gatt_id_t *char_id, btgatt_descr_id_t *descr_id) { char buf[MAX_UUID_STR_LEN]; char srvc_id_buf[MAX_SRVC_ID_STR_LEN]; char char_id_buf[MAX_CHAR_ID_STR_LEN]; haltest_info("%s: conn_id=%d status=%d srvc_id=%s char_id=%s, descr_id=%s\n", __func__, conn_id, status, btgatt_srvc_id_t2str(srvc_id, srvc_id_buf), btgatt_gatt_id_t2str(char_id, char_id_buf), btgatt_descr_id_t2str(descr_id, buf)); if (status == 0) EXEC(if_gatt->client->get_descriptor, conn_id, srvc_id, char_id, descr_id); } /* GATT included service enumeration result callback */ static void gattc_get_included_service_cb(int conn_id, int status, btgatt_srvc_id_t *srvc_id, btgatt_srvc_id_t *incl_srvc_id) { char srvc_id_buf[MAX_SRVC_ID_STR_LEN]; char incl_srvc_id_buf[MAX_SRVC_ID_STR_LEN]; haltest_info("%s: conn_id=%d status=%d srvc_id=%s incl_srvc_id=%s)\n", __func__, conn_id, status, btgatt_srvc_id_t2str(srvc_id, srvc_id_buf), btgatt_srvc_id_t2str(incl_srvc_id, incl_srvc_id_buf)); if (status == 0) EXEC(if_gatt->client->get_included_service, conn_id, srvc_id, incl_srvc_id); } /* Callback invoked in response to [de]register_for_notification */ static void gattc_register_for_notification_cb(int conn_id, int registered, int status, btgatt_srvc_id_t *srvc_id, btgatt_gatt_id_t *char_id) { char srvc_id_buf[MAX_SRVC_ID_STR_LEN]; char char_id_buf[MAX_CHAR_ID_STR_LEN]; haltest_info("%s: conn_id=%d registered=%d status=%d srvc_id=%s char_id=%s\n", __func__, conn_id, registered, status, btgatt_srvc_id_t2str(srvc_id, srvc_id_buf), btgatt_gatt_id_t2str(char_id, char_id_buf)); } /* * Remote device notification callback, invoked when a remote device sends * a notification or indication that a client has registered for. */ static void gattc_notify_cb(int conn_id, btgatt_notify_params_t *p_data) { char buf[MAX_NOTIFY_PARAMS_STR_LEN]; haltest_info("%s: conn_id=%d data=%s\n", __func__, conn_id, btgatt_notify_params_t2str(p_data, buf)); } /* Reports result of a GATT read operation */ static void gattc_read_characteristic_cb(int conn_id, int status, btgatt_read_params_t *p_data) { char buf[MAX_READ_PARAMS_STR_LEN]; haltest_info("%s: conn_id=%d status=%d data=%s\n", __func__, conn_id, status, btgatt_read_params_t2str(p_data, buf)); } /* GATT write characteristic operation callback */ static void gattc_write_characteristic_cb(int conn_id, int status, btgatt_write_params_t *p_data) { haltest_info("%s: conn_id=%d status=%d\n", __func__, conn_id, status); } /* GATT execute prepared write callback */ static void gattc_execute_write_cb(int conn_id, int status) { haltest_info("%s: conn_id=%d status=%d\n", __func__, conn_id, status); } /* Callback invoked in response to read_descriptor */ static void gattc_read_descriptor_cb(int conn_id, int status, btgatt_read_params_t *p_data) { char buf[MAX_READ_PARAMS_STR_LEN]; haltest_info("%s: conn_id=%d status=%d data=%s\n", __func__, conn_id, status, btgatt_read_params_t2str(p_data, buf)); } /* Callback invoked in response to write_descriptor */ static void gattc_write_descriptor_cb(int conn_id, int status, btgatt_write_params_t *p_data) { haltest_info("%s: conn_id=%d status=%d\n", __func__, conn_id, status); } /* Callback triggered in response to read_remote_rssi */ static void gattc_read_remote_rssi_cb(int client_if, bt_bdaddr_t *bda, int rssi, int status) { char buf[MAX_ADDR_STR_LEN]; haltest_info("%s: client_if=%d bda=%s rssi=%d satus=%d\n", __func__, client_if, bt_bdaddr_t2str(bda, buf), rssi, status); } /* Callback invoked in response to listen */ static void gattc_listen_cb(int status, int client_if) { haltest_info("%s: client_if=%d status=%d\n", __func__, client_if, status); } #if ANDROID_VERSION >= PLATFORM_VER(5, 0, 0) /* Callback invoked when the MTU for a given connection changes */ static void gattc_configure_mtu_cb(int conn_id, int status, int mtu) { haltest_info("%s: conn_id=%d, status=%d, mtu=%d\n", __func__, conn_id, status, mtu); } /* Callback invoked when a scan filter configuration command has completed */ static void gattc_scan_filter_cfg_cb(int action, int client_if, int status, int filt_type, int avbl_space) { haltest_info("%s: action=%d, client_if=%d, status=%d, filt_type=%d" ", avbl_space=%d", __func__, action, client_if, status, filt_type, avbl_space); } /* Callback invoked when scan param has been added, cleared, or deleted */ static void gattc_scan_filter_param_cb(int action, int client_if, int status, int avbl_space) { haltest_info("%s: action=%d, client_if=%d, status=%d, avbl_space=%d", __func__, action, client_if, status, avbl_space); } /* Callback invoked when a scan filter configuration command has completed */ static void gattc_scan_filter_status_cb(int enable, int client_if, int status) { haltest_info("%s: enable=%d, client_if=%d, status=%d", __func__, enable, client_if, status); } /* Callback invoked when multi-adv enable operation has completed */ static void gattc_multi_adv_enable_cb(int client_if, int status) { haltest_info("%s: client_if=%d, status=%d", __func__, client_if, status); } /* Callback invoked when multi-adv param update operation has completed */ static void gattc_multi_adv_update_cb(int client_if, int status) { haltest_info("%s: client_if=%d, status=%d", __func__, client_if, status); } /* Callback invoked when multi-adv instance data set operation has completed */ static void gattc_multi_adv_data_cb(int client_if, int status) { haltest_info("%s: client_if=%d, status=%d", __func__, client_if, status); } /* Callback invoked when multi-adv disable operation has completed */ static void gattc_multi_adv_disable_cb(int client_if, int status) { haltest_info("%s: client_if=%d, status=%d", __func__, client_if, status); } /* * Callback notifying an application that a remote device connection is * currently congested and cannot receive any more data. An application should * avoid sending more data until a further callback is received indicating the * congestion status has been cleared. */ static void gattc_congestion_cb(int conn_id, bool congested) { haltest_info("%s: conn_id=%d, congested=%d", __func__, conn_id, congested); } /* Callback invoked when batchscan storage config operation has completed */ static void gattc_batchscan_cfg_storage_cb(int client_if, int status) { haltest_info("%s: client_if=%d, status=%d", __func__, client_if, status); } /* Callback invoked when batchscan enable / disable operation has completed */ static void gattc_batchscan_enable_disable_cb(int action, int client_if, int status) { haltest_info("%s: action=%d, client_if=%d, status=%d", __func__, action, client_if, status); } /* Callback invoked when batchscan reports are obtained */ static void gattc_batchscan_reports_cb(int client_if, int status, int report_format, int num_records, int data_len, uint8_t* rep_data) { /* BTGATT_MAX_ATTR_LEN = 600 */ char valbuf[600]; haltest_info("%s: client_if=%d, status=%d, report_format=%d" ", num_records=%d, data_len=%d, rep_data=%s", __func__, client_if, status, report_format, num_records, data_len, array2str(rep_data, data_len, valbuf, sizeof(valbuf))); } /* Callback invoked when batchscan storage threshold limit is crossed */ static void gattc_batchscan_threshold_cb(int client_if) { haltest_info("%s: client_if=%d", __func__, client_if); } /* Track ADV VSE callback invoked when tracked device is found or lost */ static void gattc_track_adv_event_cb(int client_if, int filt_index, int addr_type, bt_bdaddr_t* bda, int adv_state) { char buf[MAX_ADDR_STR_LEN]; haltest_info("%s, client_if=%d, filt_index=%d, addr_type=%d, bda=%s" ", adv_state=%d", __func__, client_if, filt_index, addr_type, bt_bdaddr_t2str(bda, buf), adv_state); } #endif static const btgatt_client_callbacks_t btgatt_client_callbacks = { .register_client_cb = gattc_register_client_cb, .scan_result_cb = gattc_scan_result_cb, .open_cb = gattc_connect_cb, .close_cb = gattc_disconnect_cb, .search_complete_cb = gattc_search_complete_cb, .search_result_cb = gattc_search_result_cb, .get_characteristic_cb = gattc_get_characteristic_cb, .get_descriptor_cb = gattc_get_descriptor_cb, .get_included_service_cb = gattc_get_included_service_cb, .register_for_notification_cb = gattc_register_for_notification_cb, .notify_cb = gattc_notify_cb, .read_characteristic_cb = gattc_read_characteristic_cb, .write_characteristic_cb = gattc_write_characteristic_cb, .read_descriptor_cb = gattc_read_descriptor_cb, .write_descriptor_cb = gattc_write_descriptor_cb, .execute_write_cb = gattc_execute_write_cb, .read_remote_rssi_cb = gattc_read_remote_rssi_cb, .listen_cb = gattc_listen_cb, #if ANDROID_VERSION >= PLATFORM_VER(5, 0, 0) .configure_mtu_cb = gattc_configure_mtu_cb, .scan_filter_cfg_cb = gattc_scan_filter_cfg_cb, .scan_filter_param_cb = gattc_scan_filter_param_cb, .scan_filter_status_cb = gattc_scan_filter_status_cb, .multi_adv_enable_cb = gattc_multi_adv_enable_cb, .multi_adv_update_cb = gattc_multi_adv_update_cb, .multi_adv_data_cb = gattc_multi_adv_data_cb, .multi_adv_disable_cb = gattc_multi_adv_disable_cb, .congestion_cb = gattc_congestion_cb, .batchscan_cfg_storage_cb = gattc_batchscan_cfg_storage_cb, .batchscan_enb_disable_cb = gattc_batchscan_enable_disable_cb, .batchscan_reports_cb = gattc_batchscan_reports_cb, .batchscan_threshold_cb = gattc_batchscan_threshold_cb, .track_adv_event_cb = gattc_track_adv_event_cb, #endif }; /* BT-GATT Server callbacks */ /* Cache server_if and conn_id for tab completion */ static char server_if_str[20]; /* Callback invoked in response to register_server */ static void gatts_register_server_cb(int status, int server_if, bt_uuid_t *app_uuid) { char buf[MAX_UUID_STR_LEN]; haltest_info("%s: status=%d server_if=%d app_uuid=%s\n", __func__, status, server_if, gatt_uuid_t2str(app_uuid, buf)); } /* * Callback indicating that a remote device has connected * or been disconnected */ static void gatts_connection_cb(int conn_id, int server_if, int connected, bt_bdaddr_t *bda) { haltest_info("%s: conn_id=%d server_if=%d connected=%d bda=%s\n", __func__, conn_id, server_if, connected, bt_bdaddr_t2str(bda, last_addr)); snprintf(conn_id_str, sizeof(conn_id_str), "%d", conn_id); } /* Callback invoked in response to create_service */ static void gatts_service_added_cb(int status, int server_if, btgatt_srvc_id_t *srvc_id, int srvc_handle) { char buf[MAX_SRVC_ID_STR_LEN]; snprintf(server_if_str, sizeof(server_if_str), "%d", server_if); haltest_info("%s: status=%d server_if=%d srvc_id=%s handle=0x%x\n", __func__, status, server_if, btgatt_srvc_id_t2str(srvc_id, buf), srvc_handle); } /* Callback indicating that an included service has been added to a service */ static void gatts_included_service_added_cb(int status, int server_if, int srvc_handle, int incl_srvc_handle) { haltest_info("%s: status=%d server_if=%d srvc_handle=0x%x inc_srvc_handle=0x%x\n", __func__, status, server_if, srvc_handle, incl_srvc_handle); } /* Callback invoked when a characteristic has been added to a service */ static void gatts_characteristic_added_cb(int status, int server_if, bt_uuid_t *uuid, int srvc_handle, int char_handle) { char buf[MAX_SRVC_ID_STR_LEN]; haltest_info("%s: status=%d server_if=%d uuid=%s srvc_handle=0x%x char_handle=0x%x\n", __func__, status, server_if, gatt_uuid_t2str(uuid, buf), srvc_handle, char_handle); } /* Callback invoked when a descriptor has been added to a characteristic */ static void gatts_descriptor_added_cb(int status, int server_if, bt_uuid_t *uuid, int srvc_handle, int descr_handle) { char buf[MAX_SRVC_ID_STR_LEN]; haltest_info("%s: status=%d server_if=%d uuid=%s srvc_handle=0x%x descr_handle=0x%x\n", __func__, status, server_if, gatt_uuid_t2str(uuid, buf), srvc_handle, descr_handle); } /* Callback invoked in response to start_service */ static void gatts_service_started_cb(int status, int server_if, int srvc_handle) { haltest_info("%s: status=%d server_if=%d srvc_handle=0x%x\n", __func__, status, server_if, srvc_handle); } /* Callback invoked in response to stop_service */ static void gatts_service_stopped_cb(int status, int server_if, int srvc_handle) { haltest_info("%s: status=%d server_if=%d srvc_handle=0x%x\n", __func__, status, server_if, srvc_handle); } /* Callback triggered when a service has been deleted */ static void gatts_service_deleted_cb(int status, int server_if, int srvc_handle) { haltest_info("%s: status=%d server_if=%d srvc_handle=0x%x\n", __func__, status, server_if, srvc_handle); } /* * Callback invoked when a remote device has requested to read a characteristic * or descriptor. The application must respond by calling send_response */ static void gatts_request_read_cb(int conn_id, int trans_id, bt_bdaddr_t *bda, int attr_handle, int offset, bool is_long) { char buf[MAX_ADDR_STR_LEN]; haltest_info("%s: conn_id=%d trans_id=%d bda=%s attr_handle=0x%x offset=%d is_long=%d\n", __func__, conn_id, trans_id, bt_bdaddr_t2str(bda, buf), attr_handle, offset, is_long); } /* * Callback invoked when a remote device has requested to write to a * characteristic or descriptor. */ static void gatts_request_write_cb(int conn_id, int trans_id, bt_bdaddr_t *bda, int attr_handle, int offset, int length, bool need_rsp, bool is_prep, uint8_t *value) { char buf[MAX_ADDR_STR_LEN]; char valbuf[100]; haltest_info("%s: conn_id=%d trans_id=%d bda=%s attr_handle=0x%x offset=%d length=%d need_rsp=%d is_prep=%d value=%s\n", __func__, conn_id, trans_id, bt_bdaddr_t2str(bda, buf), attr_handle, offset, length, need_rsp, is_prep, array2str(value, length, valbuf, sizeof(valbuf))); } /* Callback invoked when a previously prepared write is to be executed */ static void gatts_request_exec_write_cb(int conn_id, int trans_id, bt_bdaddr_t *bda, int exec_write) { char buf[MAX_ADDR_STR_LEN]; haltest_info("%s: conn_id=%d trans_id=%d bda=%s exec_write=%d\n", __func__, conn_id, trans_id, bt_bdaddr_t2str(bda, buf), exec_write); } /* * Callback triggered in response to send_response if the remote device * sends a confirmation. */ static void gatts_response_confirmation_cb(int status, int handle) { haltest_info("%s: status=%d handle=0x%x\n", __func__, status, handle); } #if ANDROID_VERSION >= PLATFORM_VER(5, 0, 0) /** * Callback confirming that a notification or indication has been sent * to a remote device. */ static void gatts_indication_sent_cb(int conn_id, int status) { haltest_info("%s: status=%d conn_id=%d", __func__, status, conn_id); } /** * Callback notifying an application that a remote device connection is * currently congested and cannot receive any more data. An application * should avoid sending more data until a further callback is received * indicating the congestion status has been cleared. */ static void gatts_congestion_cb(int conn_id, bool congested) { haltest_info("%s: conn_id=%d congested=%d", __func__, conn_id, congested); } #endif static const btgatt_server_callbacks_t btgatt_server_callbacks = { .register_server_cb = gatts_register_server_cb, .connection_cb = gatts_connection_cb, .service_added_cb = gatts_service_added_cb, .included_service_added_cb = gatts_included_service_added_cb, .characteristic_added_cb = gatts_characteristic_added_cb, .descriptor_added_cb = gatts_descriptor_added_cb, .service_started_cb = gatts_service_started_cb, .service_stopped_cb = gatts_service_stopped_cb, .service_deleted_cb = gatts_service_deleted_cb, .request_read_cb = gatts_request_read_cb, .request_write_cb = gatts_request_write_cb, .request_exec_write_cb = gatts_request_exec_write_cb, .response_confirmation_cb = gatts_response_confirmation_cb, #if ANDROID_VERSION >= PLATFORM_VER(5, 0, 0) .indication_sent_cb = gatts_indication_sent_cb, .congestion_cb = gatts_congestion_cb, #endif }; static const btgatt_callbacks_t gatt_cbacks = { .size = sizeof(gatt_cbacks), .client = &btgatt_client_callbacks, .server = &btgatt_server_callbacks }; /* * convert hex string to uint8_t array */ static int fill_buffer(const char *str, uint8_t *out, int out_size) { int str_len; int i, j; char c; uint8_t b; str_len = strlen(str); /* Hex string must be byte format */ if (str_len % 2) return -1; for (i = 0, j = 0; i < out_size && j < str_len; i++, j++) { c = str[j]; if (c >= 'a' && c <= 'f') c += 'A' - 'a'; if (c >= '0' && c <= '9') b = c - '0'; else if (c >= 'A' && c <= 'F') b = 10 + c - 'A'; else return 0; j++; c = str[j]; if (c >= 'a' && c <= 'f') c += 'A' - 'a'; if (c >= '0' && c <= '9') b = b * 16 + c - '0'; else if (c >= 'A' && c <= 'F') b = b * 16 + 10 + c - 'A'; else return 0; out[i] = b; } return i; } /* gatt client methods */ /* init */ static void init_p(int argc, const char **argv) { RETURN_IF_NULL(if_gatt); EXEC(if_gatt->init, &gatt_cbacks); } /* cleanup */ static void cleanup_p(int argc, const char **argv) { RETURN_IF_NULL(if_gatt); EXECV(if_gatt->cleanup); if_gatt = NULL; } static struct method methods[] = { STD_METHOD(init), STD_METHOD(cleanup), END_METHOD }; const struct interface gatt_if = { .name = "gatt", .methods = methods }; /* register_client */ static void register_client_p(int argc, const char **argv) { bt_uuid_t uuid; RETURN_IF_NULL(if_gatt); /* uuid */ if (argc <= 2) gatt_str2bt_uuid_t("babe4bed", -1, &uuid); else gatt_str2bt_uuid_t(argv[2], -1, &uuid); EXEC(if_gatt->client->register_client, &uuid); } /* unregister_client */ static void unregister_client_c(int argc, const char **argv, enum_func *enum_func, void **user) { if (argc == 3) { *user = client_if_str; *enum_func = enum_one_string; } } static void unregister_client_p(int argc, const char **argv) { int client_if; RETURN_IF_NULL(if_gatt); VERIFY_CLIENT_IF(2, client_if); EXEC(if_gatt->client->unregister_client, client_if); } /* scan */ /* Same completion as unregister for now, start stop is not auto completed */ #define scan_c unregister_client_c static void scan_p(int argc, const char **argv) { #if ANDROID_VERSION < PLATFORM_VER(5, 0, 0) int client_if; #endif int start = 1; RETURN_IF_NULL(if_gatt); #if ANDROID_VERSION < PLATFORM_VER(5, 0, 0) VERIFY_CLIENT_IF(2, client_if); /* start */ if (argc >= 4) start = strtol(argv[3], NULL, 0); EXEC(if_gatt->client->scan, client_if, start); #else /* start */ if (argc >= 3) start = strtol(argv[2], NULL, 0); EXEC(if_gatt->client->scan, start); #endif } /* connect */ static void connect_c(int argc, const char **argv, enum_func *enum_func, void **user) { if (argc == 3) { *user = client_if_str; *enum_func = enum_one_string; } else if (argc == 4) { *user = NULL; *enum_func = enum_devices; } } static void connect_p(int argc, const char **argv) { int client_if; bt_bdaddr_t bd_addr; int is_direct = 1; #if ANDROID_VERSION >= PLATFORM_VER(5, 0, 0) int transport = 1; #endif RETURN_IF_NULL(if_gatt); VERIFY_CLIENT_IF(2, client_if); VERIFY_ADDR_ARG(3, &bd_addr); /* is_direct */ if (argc > 4) is_direct = strtol(argv[4], NULL, 0); #if ANDROID_VERSION < PLATFORM_VER(5, 0, 0) EXEC(if_gatt->client->connect, client_if, &bd_addr, is_direct); #else /* transport */ if (argc > 5) transport = strtol(argv[5], NULL, 0); EXEC(if_gatt->client->connect, client_if, &bd_addr, is_direct, transport); #endif } /* disconnect */ static void disconnect_c(int argc, const char **argv, enum_func *enum_func, void **user) { if (argc == 3) { *user = client_if_str; *enum_func = enum_one_string; } else if (argc == 4) { *user = last_addr; *enum_func = enum_one_string; } else if (argc == 5) { *user = conn_id_str; *enum_func = enum_one_string; } } static void disconnect_p(int argc, const char **argv) { int client_if; bt_bdaddr_t bd_addr; int conn_id; RETURN_IF_NULL(if_gatt); VERIFY_CLIENT_IF(2, client_if); VERIFY_ADDR_ARG(3, &bd_addr); VERIFY_CONN_ID(4, conn_id); EXEC(if_gatt->client->disconnect, client_if, &bd_addr, conn_id); } /* listen */ /* Same completion as unregister for now, start stop is not auto completed */ #define listen_c unregister_client_c static void listen_p(int argc, const char **argv) { int client_if; int start = 1; RETURN_IF_NULL(if_gatt); VERIFY_CLIENT_IF(2, client_if); /* start */ if (argc >= 4) start = strtol(argv[3], NULL, 0); EXEC(if_gatt->client->listen, client_if, start); } /* refresh */ static void refresh_c(int argc, const char **argv, enum_func *enum_func, void **user) { if (argc == 3) { *user = client_if_str; *enum_func = enum_one_string; } else if (argc == 4) { *enum_func = enum_devices; } } static void refresh_p(int argc, const char **argv) { int client_if; bt_bdaddr_t bd_addr; RETURN_IF_NULL(if_gatt); VERIFY_CLIENT_IF(2, client_if); VERIFY_ADDR_ARG(3, &bd_addr); EXEC(if_gatt->client->refresh, client_if, &bd_addr); } /* search_service */ static void search_service_c(int argc, const char **argv, enum_func *enum_func, void **user) { if (argc == 3) { *user = conn_id_str; *enum_func = enum_one_string; } } static void search_service_p(int argc, const char **argv) { int conn_id; RETURN_IF_NULL(if_gatt); VERIFY_CONN_ID(2, conn_id); /* uuid */ if (argc <= 3) { EXEC(if_gatt->client->search_service, conn_id, NULL); } else { bt_uuid_t filter_uuid; gatt_str2bt_uuid_t(argv[3], -1, &filter_uuid); EXEC(if_gatt->client->search_service, conn_id, &filter_uuid); } } /* get_included_service */ static void get_included_service_c(int argc, const char **argv, enum_func *enum_func, void **user) { if (argc == 3) { *user = conn_id_str; *enum_func = enum_one_string; } } static void get_included_service_p(int argc, const char **argv) { int conn_id; btgatt_srvc_id_t srvc_id; RETURN_IF_NULL(if_gatt); VERIFY_CONN_ID(2, conn_id); VERIFY_SRVC_ID(3, &srvc_id); EXEC(if_gatt->client->get_included_service, conn_id, &srvc_id, NULL); } /* get_characteristic */ /* Same completion as get_included_service_c */ #define get_characteristic_c get_included_service_c static void get_characteristic_p(int argc, const char **argv) { int conn_id; btgatt_srvc_id_t srvc_id; RETURN_IF_NULL(if_gatt); VERIFY_CONN_ID(2, conn_id); VERIFY_SRVC_ID(3, &srvc_id); EXEC(if_gatt->client->get_characteristic, conn_id, &srvc_id, NULL); } /* get_descriptor */ /* Same completion as get_included_service_c */ #define get_descriptor_c get_included_service_c static void get_descriptor_p(int argc, const char **argv) { int conn_id; btgatt_srvc_id_t srvc_id; btgatt_gatt_id_t char_id; RETURN_IF_NULL(if_gatt); VERIFY_CONN_ID(2, conn_id); VERIFY_SRVC_ID(3, &srvc_id); VERIFY_CHAR_ID(4, &char_id); EXEC(if_gatt->client->get_descriptor, conn_id, &srvc_id, &char_id, NULL); } /* read_characteristic */ /* Same completion as get_included_service_c */ #define read_characteristic_c get_included_service_c static void read_characteristic_p(int argc, const char **argv) { int conn_id; btgatt_srvc_id_t srvc_id; btgatt_gatt_id_t char_id; int auth_req = 0; RETURN_IF_NULL(if_gatt); VERIFY_CONN_ID(2, conn_id); VERIFY_SRVC_ID(3, &srvc_id); VERIFY_CHAR_ID(4, &char_id); /* auth_req */ if (argc > 5) auth_req = strtol(argv[5], NULL, 0); EXEC(if_gatt->client->read_characteristic, conn_id, &srvc_id, &char_id, auth_req); } /* write_characteristic */ static void write_characteristic_c(int argc, const char **argv, enum_func *enum_func, void **user) { /* * This should be from tGATT_WRITE_TYPE but it's burried * inside bluedroid guts */ static const char *wrtypes[] = { "1", "2", "3", NULL }; if (argc == 3) { *user = conn_id_str; *enum_func = enum_one_string; } else if (argc == 6) { *user = wrtypes; *enum_func = enum_strings; } } static void write_characteristic_p(int argc, const char **argv) { int conn_id; btgatt_srvc_id_t srvc_id; btgatt_gatt_id_t char_id; int write_type; int len; int auth_req = 0; uint8_t value[100]; RETURN_IF_NULL(if_gatt); VERIFY_CONN_ID(2, conn_id); VERIFY_SRVC_ID(3, &srvc_id); VERIFY_CHAR_ID(4, &char_id); /* write type */ if (argc <= 5) { haltest_error("No write type specified\n"); return; } write_type = strtol(argv[5], NULL, 0); GET_VERIFY_HEX_STRING(6, value, len); /* auth_req */ if (argc > 7) auth_req = strtol(argv[7], NULL, 0); EXEC(if_gatt->client->write_characteristic, conn_id, &srvc_id, &char_id, write_type, len, auth_req, (char *) value); } /* read_descriptor */ /* Same completion as get_included_service_c */ #define read_descriptor_c get_included_service_c static void read_descriptor_p(int argc, const char **argv) { int conn_id; btgatt_srvc_id_t srvc_id; btgatt_gatt_id_t char_id; btgatt_descr_id_t descr_id; int auth_req = 0; RETURN_IF_NULL(if_gatt); VERIFY_CONN_ID(2, conn_id); VERIFY_SRVC_ID(3, &srvc_id); VERIFY_CHAR_ID(4, &char_id); VERIFY_DESCR_ID(5, &descr_id); /* auth_req */ if (argc > 6) auth_req = strtol(argv[6], NULL, 0); EXEC(if_gatt->client->read_descriptor, conn_id, &srvc_id, &char_id, &descr_id, auth_req); } /* write_descriptor */ static void write_descriptor_c(int argc, const char **argv, enum_func *enum_func, void **user) { /* * This should be from tGATT_WRITE_TYPE but it's burried * inside bluedroid guts */ static const char *wrtypes[] = { "1", "2", "3", NULL }; if (argc == 3) { *user = conn_id_str; *enum_func = enum_one_string; } else if (argc == 7) { *user = wrtypes; *enum_func = enum_strings; } } static void write_descriptor_p(int argc, const char **argv) { int conn_id; btgatt_srvc_id_t srvc_id; btgatt_gatt_id_t char_id; btgatt_descr_id_t descr_id; int write_type; int len; int auth_req = 0; uint8_t value[200] = {0}; RETURN_IF_NULL(if_gatt); VERIFY_CONN_ID(2, conn_id); VERIFY_SRVC_ID(3, &srvc_id); VERIFY_CHAR_ID(4, &char_id); VERIFY_DESCR_ID(5, &descr_id); /* write type */ if (argc <= 6) { haltest_error("No write type specified\n"); return; } write_type = strtol(argv[6], NULL, 0); /* value */ if (argc <= 7) { haltest_error("No value specified\n"); return; } /* len in chars */ if (strncmp(argv[7], "0X", 2) && strncmp(argv[7], "0x", 2)) { haltest_error("Value must be hex string"); return; } len = fill_buffer(argv[7] + 2, value, sizeof(value)); /* auth_req */ if (argc > 8) auth_req = strtol(argv[8], NULL, 0); EXEC(if_gatt->client->write_descriptor, conn_id, &srvc_id, &char_id, &descr_id, write_type, len, auth_req, (char *) value); } /* execute_write */ /* Same completion as search_service */ #define execute_write_c search_service_c static void execute_write_p(int argc, const char **argv) { int conn_id; int execute; RETURN_IF_NULL(if_gatt); VERIFY_CONN_ID(2, conn_id); /* execute */ if (argc <= 3) { haltest_error("No execute specified\n"); return; } execute = strtol(argv[3], NULL, 0); EXEC(if_gatt->client->execute_write, conn_id, execute); } /* register_for_notification */ static void register_for_notification_c(int argc, const char **argv, enum_func *enum_func, void **user) { if (argc == 3) { *user = client_if_str; *enum_func = enum_one_string; } else if (argc == 4) { *user = last_addr; *enum_func = enum_one_string; } } static void register_for_notification_p(int argc, const char **argv) { int client_if; bt_bdaddr_t bd_addr; btgatt_srvc_id_t srvc_id; btgatt_gatt_id_t char_id; RETURN_IF_NULL(if_gatt); VERIFY_CLIENT_IF(2, client_if); VERIFY_ADDR_ARG(3, &bd_addr); VERIFY_SRVC_ID(4, &srvc_id); VERIFY_CHAR_ID(5, &char_id); EXEC(if_gatt->client->register_for_notification, client_if, &bd_addr, &srvc_id, &char_id); } /* deregister_for_notification */ /* Same completion as search_service */ #define deregister_for_notification_c register_for_notification_c static void deregister_for_notification_p(int argc, const char **argv) { int client_if; bt_bdaddr_t bd_addr; btgatt_srvc_id_t srvc_id; btgatt_gatt_id_t char_id; RETURN_IF_NULL(if_gatt); VERIFY_CLIENT_IF(2, client_if); VERIFY_ADDR_ARG(3, &bd_addr); VERIFY_SRVC_ID(4, &srvc_id); VERIFY_CHAR_ID(5, &char_id); EXEC(if_gatt->client->deregister_for_notification, client_if, &bd_addr, &srvc_id, &char_id); } /* read_remote_rssi */ static void read_remote_rssi_c(int argc, const char **argv, enum_func *enum_func, void **user) { if (argc == 3) { *user = client_if_str; *enum_func = enum_one_string; } else if (argc == 4) { *enum_func = enum_devices; } } static void read_remote_rssi_p(int argc, const char **argv) { int client_if; bt_bdaddr_t bd_addr; RETURN_IF_NULL(if_gatt); VERIFY_CLIENT_IF(2, client_if); VERIFY_ADDR_ARG(3, &bd_addr); EXEC(if_gatt->client->read_remote_rssi, client_if, &bd_addr); } #if ANDROID_VERSION >= PLATFORM_VER(5, 0, 0) /* scan filter parameter setup */ static void scan_filter_param_setup_c(int argc, const char **argv, enum_func *enum_func, void **user) { if (argc == 2) { *user = client_if_str; *enum_func = enum_one_string; } } static void scan_filter_param_setup_p(int argc, const char **argv) { int client_if; int action; int filt_index; int feat_seln; int list_logic_type, filt_logic_type; int rssi_high_thres, rssi_low_thres; int dely_mode; int found_timeout, lost_timeout, found_timeout_cnt; RETURN_IF_NULL(if_gatt); VERIFY_CLIENT_IF(2, client_if); VERIFY_ACTION(3, action); VERIFY_FILT_INDEX(4, filt_index); VERIFY_FEAT_SELN(5, feat_seln); VERIFY_LIST_LOGIC_TYPE(6, list_logic_type); VERIFY_FILT_LOGIC_TYPE(7, filt_logic_type); VERIFY_RSSI_HI_THR(8, rssi_high_thres); VERIFY_RSSI_LOW_THR(9, rssi_low_thres); VERIFY_DELY_MODE(10, dely_mode); VERIFY_FND_TIME(11, found_timeout); VERIFY_LOST_TIME(12, lost_timeout); VERIFY_FND_TIME_CNT(13, found_timeout_cnt); EXEC(if_gatt->client->scan_filter_param_setup, client_if, action, filt_index, feat_seln, list_logic_type, filt_logic_type, rssi_high_thres, rssi_low_thres, dely_mode, found_timeout, lost_timeout, found_timeout_cnt); } /* scan filter add remove */ static void scan_filter_add_remove_c(int argc, const char **argv, enum_func *enum_func, void **user) { if (argc == 2) { *user = client_if_str; *enum_func = enum_one_string; } else if (argc == 10) { *user = last_addr; *enum_func = enum_one_string; } } static void scan_filter_add_remove_p(int argc, const char **argv) { int client_if; int action; int filt_type; int filt_index; int company_id; int company_id_mask; bt_uuid_t p_uuid; bt_uuid_t p_uuid_mask; bt_bdaddr_t bd_addr; char addr_type; int data_len; uint8_t p_data[100]; int mask_len; uint8_t p_mask[100]; RETURN_IF_NULL(if_gatt); VERIFY_CLIENT_IF(2, client_if); VERIFY_ACTION(3, action); VERIFY_FILT_TYPE(4, filt_type); VERIFY_FILT_INDEX(5, filt_index); VERIFY_COMP_ID(6, company_id); VERIFY_COMP_ID_MASK(7, company_id_mask); if (argc <= 9) { haltest_error("No p_uuid, p_uuid_mask specified\n"); return; } gatt_str2bt_uuid_t(argv[8], -1, &p_uuid); gatt_str2bt_uuid_t(argv[9], -1, &p_uuid_mask); VERIFY_UUID(8, &p_uuid); VERIFY_UUID(9, &p_uuid_mask); VERIFY_ADDR_ARG(10, &bd_addr); VERIFY_ADDR_TYPE(11, addr_type); GET_VERIFY_HEX_STRING(12, p_data, data_len); GET_VERIFY_HEX_STRING(13, p_mask, mask_len); EXEC(if_gatt->client->scan_filter_add_remove, client_if, action, filt_type, filt_index, company_id, company_id_mask, &p_uuid, &p_uuid_mask, &bd_addr, addr_type, data_len, (char *) p_data, mask_len, (char *) p_mask); } /* scan filter clean */ static void scan_filter_clear_c(int argc, const char **argv, enum_func *enum_func, void **user) { if (argc == 2) { *user = client_if_str; *enum_func = enum_one_string; } } static void scan_filter_clear_p(int argc, const char **argv) { int client_if; int filt_index; RETURN_IF_NULL(if_gatt); VERIFY_CLIENT_IF(2, client_if); VERIFY_FILT_INDEX(3, filt_index); EXEC(if_gatt->client->scan_filter_clear, client_if, filt_index); } /* scan filter enable */ static void scan_filter_enable_c(int argc, const char **argv, enum_func *enum_func, void **user) { if (argc == 2) { *user = client_if_str; *enum_func = enum_one_string; } } static void scan_filter_enable_p(int argc, const char **argv) { int client_if; int enable = 0; RETURN_IF_NULL(if_gatt); VERIFY_CLIENT_IF(2, client_if); /* enable */ if (argc >= 4) enable = strtol(argv[3], NULL, 0); EXEC(if_gatt->client->scan_filter_clear, client_if, enable); } /* set advertising data */ static void set_adv_data_c(int argc, const char **argv, enum_func *enum_func, void **user) { if (argc == 2) { *user = client_if_str; *enum_func = enum_one_string; } } static void set_adv_data_p(int argc, const char **argv) { int client_if; bool set_scan_rsp = false; bool include_name = false; bool include_txpower = false; int min_interval, max_interval; int appearance; uint16_t manufacturer_len; uint8_t manufacturer_data[100]; uint16_t service_data_len; uint8_t service_data[100]; uint16_t service_uuid_len; uint8_t service_uuid[100]; RETURN_IF_NULL(if_gatt); VERIFY_CLIENT_IF(2, client_if); /* set scan response */ if (argc >= 4) set_scan_rsp = strtol(argv[3], NULL, 0); /* include name */ if (argc >= 5) include_name = strtol(argv[4], NULL, 0); /* include txpower */ if (argc >= 6) include_txpower = strtol(argv[5], NULL, 0); VERIFY_MIN_INTERVAL(6, min_interval); VERIFY_MAX_INTERVAL(7, max_interval); VERIFY_APPEARANCE(8, appearance); GET_VERIFY_HEX_STRING(9, manufacturer_data, manufacturer_len); GET_VERIFY_HEX_STRING(10, service_data, service_data_len); GET_VERIFY_HEX_STRING(11, service_uuid, service_uuid_len); EXEC(if_gatt->client->set_adv_data, client_if, set_scan_rsp, include_name, include_txpower, min_interval, max_interval, appearance, manufacturer_len, (char *) manufacturer_data, service_data_len, (char *) service_data, service_uuid_len, (char *) service_uuid); } /* configure mtu */ static void configure_mtu_c(int argc, const char **argv, enum_func *enum_func, void **user) { if (argc == 2) { *user = conn_id_str; *enum_func = enum_one_string; } } static void configure_mtu_p(int argc, const char **argv) { int conn_id; int mtu; RETURN_IF_NULL(if_gatt); VERIFY_CONN_ID(2, conn_id); VERIFY_MTU(3, mtu); EXEC(if_gatt->client->configure_mtu, conn_id, mtu); } /* con parameter update */ static void conn_parameter_update_c(int argc, const char **argv, enum_func *enum_func, void **user) { if (argc == 2) { *user = last_addr; *enum_func = enum_one_string; } } static void conn_parameter_update_p(int argc, const char **argv) { bt_bdaddr_t bd_addr; int min_interval, max_interval; int latency; int timeout; RETURN_IF_NULL(if_gatt); VERIFY_ADDR_ARG(2, &bd_addr); VERIFY_MIN_INTERVAL(3, min_interval); VERIFY_MAX_INTERVAL(4, max_interval); VERIFY_LATENCY(5, latency); VERIFY_TIMEOUT(6, timeout); EXEC(if_gatt->client->conn_parameter_update, &bd_addr, min_interval, max_interval, latency, timeout); } /* set scan parameters */ static void set_scan_parameters_p(int argc, const char **argv) { int scan_interval; int scan_window; RETURN_IF_NULL(if_gatt); VERIFY_SCAN_INTERVAL(2, scan_interval); VERIFY_SCAN_WINDOW(3, scan_window); EXEC(if_gatt->client->set_scan_parameters, scan_interval, scan_window); } /* enable multi advertising */ static void multi_adv_enable_c(int argc, const char **argv, enum_func *enum_func, void **user) { if (argc == 2) { *user = client_if_str; *enum_func = enum_one_string; } } static void multi_adv_enable_p(int argc, const char **argv) { int client_if; int min_interval, max_interval; int adv_type; int chnl_map; int tx_power; int timeout_s; RETURN_IF_NULL(if_gatt); VERIFY_CLIENT_IF(2, client_if); VERIFY_MIN_INTERVAL(3, min_interval); VERIFY_MAX_INTERVAL(4, max_interval); VERIFY_ADV_TYPE(5, adv_type); VERIFY_CHNL_MAP(6, chnl_map); VERIFY_TX_POWER(7, tx_power); VERIFY_TIMEOUT_S(8, timeout_s); EXEC(if_gatt->client->multi_adv_enable, client_if, min_interval, max_interval, adv_type, chnl_map, tx_power, timeout_s); } /* update multi advertiser */ static void multi_adv_update_c(int argc, const char **argv, enum_func *enum_func, void **user) { if (argc == 2) { *user = client_if_str; *enum_func = enum_one_string; } } static void multi_adv_update_p(int argc, const char **argv) { int client_if; int min_interval, max_interval; int adv_type; int chnl_map; int tx_power; int timeout_s; RETURN_IF_NULL(if_gatt); VERIFY_CLIENT_IF(2, client_if); VERIFY_MIN_INTERVAL(3, min_interval); VERIFY_MAX_INTERVAL(4, max_interval); VERIFY_ADV_TYPE(5, adv_type); VERIFY_CHNL_MAP(6, chnl_map); VERIFY_TX_POWER(7, tx_power); VERIFY_TIMEOUT_S(8, timeout_s); EXEC(if_gatt->client->multi_adv_update, client_if, min_interval, max_interval, adv_type, chnl_map, tx_power, timeout_s); } /* set advertising data */ static void multi_adv_set_inst_data_c(int argc, const char **argv, enum_func *enum_func, void **user) { if (argc == 2) { *user = client_if_str; *enum_func = enum_one_string; } } static void multi_adv_set_inst_data_p(int argc, const char **argv) { int client_if; bool set_scan_rsp = false; bool include_name = false; bool include_txpower = false; int appearance; uint16_t manufacturer_len; uint8_t manufacturer_data[100]; uint16_t service_data_len; uint8_t service_data[100]; uint16_t service_uuid_len; uint8_t service_uuid[100]; RETURN_IF_NULL(if_gatt); VERIFY_CLIENT_IF(2, client_if); /* set scan response */ if (argc >= 4) set_scan_rsp = strtol(argv[3], NULL, 0); /* include name */ if (argc >= 5) include_name = strtol(argv[4], NULL, 0); /* include txpower */ if (argc >= 6) include_txpower = strtol(argv[5], NULL, 0); VERIFY_APPEARANCE(6, appearance); GET_VERIFY_HEX_STRING(7, manufacturer_data, manufacturer_len); GET_VERIFY_HEX_STRING(8, service_data, service_data_len); GET_VERIFY_HEX_STRING(9, service_uuid, service_uuid_len); EXEC(if_gatt->client->multi_adv_set_inst_data, client_if, set_scan_rsp, include_name, include_txpower, appearance, manufacturer_len, (char *) manufacturer_data, service_data_len, (char *) service_data, service_uuid_len, (char *) service_uuid); } /* multi advertising disable */ static void multi_adv_disable_c(int argc, const char **argv, enum_func *enum_func, void **user) { if (argc == 2) { *user = client_if_str; *enum_func = enum_one_string; } } static void multi_adv_disable_p(int argc, const char **argv) { int client_if; RETURN_IF_NULL(if_gatt); VERIFY_CLIENT_IF(2, client_if); EXEC(if_gatt->client->multi_adv_disable, client_if); } /* batch scanconfigure storage */ static void batchscan_cfg_storage_c(int argc, const char **argv, enum_func *enum_func, void **user) { if (argc == 2) { *user = client_if_str; *enum_func = enum_one_string; } } static void batchscan_cfg_storage_p(int argc, const char **argv) { int client_if; int batch_scan_full_max; int batch_scan_trunc_max; int batch_scan_notify_threshold; RETURN_IF_NULL(if_gatt); VERIFY_CLIENT_IF(2, client_if); VERIFY_BATCH_SCAN_FULL_MAX(3, batch_scan_full_max); VERIFY_BATCH_SCAN_TRUNC_MAX(4, batch_scan_trunc_max); VERIFY_BATCH_SCAN_NOTIFY_THR(5, batch_scan_notify_threshold); EXEC(if_gatt->client->batchscan_cfg_storage, client_if, batch_scan_full_max, batch_scan_trunc_max, batch_scan_notify_threshold); } /* enable batch scan */ static void batchscan_enb_batch_scan_c(int argc, const char **argv, enum_func *enum_func, void **user) { if (argc == 2) { *user = client_if_str; *enum_func = enum_one_string; } } static void batchscan_enb_batch_scan_p(int argc, const char **argv) { int client_if; int scan_mode, scan_interval, scan_window; int addr_type; int discard_rule; RETURN_IF_NULL(if_gatt); VERIFY_CLIENT_IF(2, client_if); VERIFY_SCAN_MODE(3, scan_mode); VERIFY_SCAN_INTERVAL(4, scan_interval); VERIFY_SCAN_WINDOW(5, scan_window); VERIFY_ADDR_TYPE(6, addr_type); VERIFY_DISCARD_RULE(7, discard_rule); EXEC(if_gatt->client->batchscan_enb_batch_scan, client_if, scan_mode, scan_interval, scan_window, addr_type, discard_rule); } /* batchscan disable */ static void batchscan_dis_batch_scan_c(int argc, const char **argv, enum_func *enum_func, void **user) { if (argc == 2) { *user = client_if_str; *enum_func = enum_one_string; } } static void batchscan_dis_batch_scan_p(int argc, const char **argv) { int client_if; RETURN_IF_NULL(if_gatt); VERIFY_CLIENT_IF(2, client_if); EXEC(if_gatt->client->batchscan_dis_batch_scan, client_if); } /* batchscan read reports */ static void batchscan_read_reports_c(int argc, const char **argv, enum_func *enum_func, void **user) { if (argc == 2) { *user = client_if_str; *enum_func = enum_one_string; } } static void batchscan_read_reports_p(int argc, const char **argv) { int client_if; int scan_mode; RETURN_IF_NULL(if_gatt); VERIFY_CLIENT_IF(2, client_if); VERIFY_SCAN_MODE(3, scan_mode); EXEC(if_gatt->client->batchscan_read_reports, client_if, scan_mode); } #endif /* get_device_type */ static void get_device_type_c(int argc, const char **argv, enum_func *enum_func, void **user) { if (argc == 3) *enum_func = enum_devices; } static void get_device_type_p(int argc, const char **argv) { bt_bdaddr_t bd_addr; int dev_type; RETURN_IF_NULL(if_gatt); VERIFY_ADDR_ARG(2, &bd_addr); dev_type = if_gatt->client->get_device_type(&bd_addr); haltest_info("%s: %d\n", "get_device_type", dev_type); } /* test_command */ static void test_command_c(int argc, const char **argv, enum_func *enum_func, void **user) { if (argc == 4) *enum_func = enum_devices; } static void test_command_p(int argc, const char **argv) { int command; int i; bt_bdaddr_t bd_addr; bt_uuid_t uuid; btgatt_test_params_t params = { .bda1 = &bd_addr, .uuid1 = &uuid }; uint16_t *u = ¶ms.u1; RETURN_IF_NULL(if_gatt); /* command */ if (argc <= 2) { haltest_error("No command specified\n"); return; } command = strtol(argv[2], NULL, 0); VERIFY_ADDR_ARG(3, &bd_addr); VERIFY_UUID(4, &uuid); for (i = 5; i < argc; i++) VERIFY_TEST_ARG(i, *u++); EXEC(if_gatt->client->test_command, command, ¶ms); } static struct method client_methods[] = { STD_METHODH(register_client, "[]"), STD_METHODCH(unregister_client, ""), #if ANDROID_VERSION >= PLATFORM_VER(5, 0, 0) STD_METHODCH(scan, "[1|0]"), STD_METHODCH(connect, " [] [ " " " " " " " " "), STD_METHODCH(scan_filter_add_remove, " " " " " [] " " [] []"), STD_METHODCH(scan_filter_clear, " "), STD_METHODCH(scan_filter_enable, " []"), STD_METHODCH(set_adv_data, " [] " " [] " " [] []" " []"), STD_METHODCH(configure_mtu, " "), STD_METHODCH(conn_parameter_update, " " " "), STD_METHODH(set_scan_parameters, " "), STD_METHODCH(multi_adv_enable, " " " " " "), STD_METHODCH(multi_adv_update, " " " " " "), STD_METHODCH(multi_adv_set_inst_data, " []" " [] " " [] []" " []"), STD_METHODCH(multi_adv_disable, ""), STD_METHODCH(batchscan_cfg_storage, " " " " " "), STD_METHODCH(batchscan_enb_batch_scan, " " " " " "), STD_METHODCH(batchscan_dis_batch_scan, ""), STD_METHODCH(batchscan_read_reports, " "), #else STD_METHODCH(scan, " [1|0]"), STD_METHODCH(connect, " []"), #endif STD_METHODCH(disconnect, " "), STD_METHODCH(refresh, " "), STD_METHODCH(search_service, " []"), STD_METHODCH(get_included_service, " "), STD_METHODCH(get_characteristic, " "), STD_METHODCH(get_descriptor, " "), STD_METHODCH(read_characteristic, " []"), STD_METHODCH(write_characteristic, " []"), STD_METHODCH(read_descriptor, " []"), STD_METHODCH(write_descriptor, " []"), STD_METHODCH(execute_write, " "), STD_METHODCH(register_for_notification, " "), STD_METHODCH(deregister_for_notification, " "), STD_METHODCH(read_remote_rssi, " "), STD_METHODCH(get_device_type, ""), STD_METHODCH(test_command, " [u1] [u2] [u3] [u4] [u5]"), STD_METHODCH(listen, " [1|0]"), END_METHOD }; const struct interface gatt_client_if = { .name = "gattc", .methods = client_methods }; /* gatt server methods */ /* register_server */ static void gatts_register_server_p(int argc, const char *argv[]) { bt_uuid_t uuid; RETURN_IF_NULL(if_gatt); /* uuid */ if (argc <= 2) gatt_str2bt_uuid_t("bed4babe", -1, &uuid); else gatt_str2bt_uuid_t(argv[2], -1, &uuid); EXEC(if_gatt->server->register_server, &uuid); } /* unregister_server */ static void gatts_unregister_server_c(int argc, const char **argv, enum_func *enum_func, void **user) { if (argc == 3) { *user = server_if_str; *enum_func = enum_one_string; } } static void gatts_unregister_server_p(int argc, const char *argv[]) { int server_if; RETURN_IF_NULL(if_gatt); VERIFY_SERVER_IF(2, server_if); EXEC(if_gatt->server->unregister_server, server_if); } /* connect */ static void gatts_connect_c(int argc, const char **argv, enum_func *enum_func, void **user) { if (argc == 3) { *user = server_if_str; *enum_func = enum_one_string; } else if (argc == 4) { *user = NULL; *enum_func = enum_devices; } } static void gatts_connect_p(int argc, const char *argv[]) { int server_if; bt_bdaddr_t bd_addr; int is_direct = 1; #if ANDROID_VERSION >= PLATFORM_VER(5, 0, 0) int transport = 1; #endif RETURN_IF_NULL(if_gatt); VERIFY_SERVER_IF(2, server_if); VERIFY_ADDR_ARG(3, &bd_addr); /* is_direct */ if (argc > 4) is_direct = strtol(argv[4], NULL, 0); #if ANDROID_VERSION < PLATFORM_VER(5, 0, 0) EXEC(if_gatt->server->connect, server_if, &bd_addr, is_direct); #else /* transport */ if (argc > 5) transport = strtol(argv[5], NULL, 0); EXEC(if_gatt->server->connect, server_if, &bd_addr, is_direct, transport); #endif } /* disconnect */ static void gatts_disconnect_c(int argc, const char **argv, enum_func *enum_func, void **user) { if (argc == 3) { *user = server_if_str; *enum_func = enum_one_string; } else if (argc == 4) { *user = last_addr; *enum_func = enum_one_string; } else if (argc == 5) { *user = conn_id_str; *enum_func = enum_one_string; } } static void gatts_disconnect_p(int argc, const char *argv[]) { int server_if; bt_bdaddr_t bd_addr; int conn_id; RETURN_IF_NULL(if_gatt); VERIFY_SERVER_IF(2, server_if); VERIFY_ADDR_ARG(3, &bd_addr); VERIFY_CONN_ID(4, conn_id); EXEC(if_gatt->server->disconnect, server_if, &bd_addr, conn_id); } /* add_service */ /* Same completion as gatts_unregister_server_c */ #define gatts_add_service_c gatts_unregister_server_c static void gatts_add_service_p(int argc, const char *argv[]) { int server_if; btgatt_srvc_id_t srvc_id; int num_handles; RETURN_IF_NULL(if_gatt); VERIFY_SERVER_IF(2, server_if); VERIFY_SRVC_ID(3, &srvc_id); /* num handles */ if (argc <= 4) { haltest_error("No num_handles specified\n"); return; } num_handles = strtol(argv[4], NULL, 0); EXEC(if_gatt->server->add_service, server_if, &srvc_id, num_handles); } /* add_included_service */ /* Same completion as gatts_unregister_server_c */ #define gatts_add_included_service_c gatts_unregister_server_c static void gatts_add_included_service_p(int argc, const char *argv[]) { int server_if; int service_handle; int included_handle; RETURN_IF_NULL(if_gatt); VERIFY_SERVER_IF(2, server_if); VERIFY_SERVICE_HANDLE(3, service_handle); VERIFY_HANDLE(4, included_handle); EXEC(if_gatt->server->add_included_service, server_if, service_handle, included_handle); } /* add_characteristic */ /* Same completion as gatts_unregister_server_c */ #define gatts_add_characteristic_c gatts_unregister_server_c static void gatts_add_characteristic_p(int argc, const char *argv[]) { int server_if; int service_handle; int properties; int permissions; bt_uuid_t uuid; RETURN_IF_NULL(if_gatt); VERIFY_SERVER_IF(2, server_if); VERIFY_SERVICE_HANDLE(3, service_handle); VERIFY_UUID(4, &uuid); /* properties */ if (argc <= 5) { haltest_error("No properties specified\n"); return; } properties = strtol(argv[5], NULL, 0); /* permissions */ if (argc <= 6) { haltest_error("No permissions specified\n"); return; } permissions = strtol(argv[6], NULL, 0); EXEC(if_gatt->server->add_characteristic, server_if, service_handle, &uuid, properties, permissions); } /* add_descriptor */ /* Same completion as gatts_unregister_server_c */ #define gatts_add_descriptor_c gatts_unregister_server_c static void gatts_add_descriptor_p(int argc, const char *argv[]) { int server_if; int service_handle; int permissions; bt_uuid_t uuid; RETURN_IF_NULL(if_gatt); VERIFY_SERVER_IF(2, server_if); VERIFY_SERVICE_HANDLE(3, service_handle); VERIFY_UUID(4, &uuid); /* permissions */ if (argc <= 5) { haltest_error("No permissions specified\n"); return; } permissions = strtol(argv[5], NULL, 0); EXEC(if_gatt->server->add_descriptor, server_if, service_handle, &uuid, permissions); } /* start_service */ /* Same completion as gatts_unregister_server_c */ #define gatts_start_service_c gatts_unregister_server_c static void gatts_start_service_p(int argc, const char *argv[]) { int server_if; int service_handle; int transport; RETURN_IF_NULL(if_gatt); VERIFY_SERVER_IF(2, server_if); VERIFY_SERVICE_HANDLE(3, service_handle); /* transport */ if (argc <= 4) { haltest_error("No transport specified\n"); return; } transport = strtol(argv[4], NULL, 0); EXEC(if_gatt->server->start_service, server_if, service_handle, transport); } /* stop_service */ /* Same completion as gatts_unregister_server_c */ #define gatts_stop_service_c gatts_unregister_server_c static void gatts_stop_service_p(int argc, const char *argv[]) { int server_if; int service_handle; RETURN_IF_NULL(if_gatt); VERIFY_SERVER_IF(2, server_if); VERIFY_SERVICE_HANDLE(3, service_handle); EXEC(if_gatt->server->stop_service, server_if, service_handle); } /* delete_service */ /* Same completion as gatts_unregister_server_c */ #define gatts_delete_service_c gatts_unregister_server_c static void gatts_delete_service_p(int argc, const char *argv[]) { int server_if; int service_handle; RETURN_IF_NULL(if_gatt); VERIFY_SERVER_IF(2, server_if); VERIFY_SERVICE_HANDLE(3, service_handle); EXEC(if_gatt->server->delete_service, server_if, service_handle); } /* send_indication */ static void gatts_send_indication_p(int argc, const char *argv[]) { int server_if; int attr_handle; int conn_id; int confirm; char data[200]; int len = 0; RETURN_IF_NULL(if_gatt); VERIFY_SERVER_IF(2, server_if); VERIFY_HANDLE(3, attr_handle); VERIFY_CONN_ID(4, conn_id); /* confirm */ if (argc <= 5) { haltest_error("No transport specified\n"); return; } confirm = strtol(argv[5], NULL, 0); GET_VERIFY_HEX_STRING(6, data, len); EXEC(if_gatt->server->send_indication, server_if, attr_handle, conn_id, len, confirm, data); } /* send_response */ static void gatts_send_response_p(int argc, const char *argv[]) { int conn_id; int trans_id; int status; btgatt_response_t data; memset(&data, 0, sizeof(data)); RETURN_IF_NULL(if_gatt); VERIFY_CONN_ID(2, conn_id); VERIFY_TRANS_ID(3, trans_id); VERIFY_STATUS(4, status); VERIFY_HANDLE(5, data.attr_value.handle); VERIFY_OFFSET(6, data.attr_value.offset); data.attr_value.auth_req = 0; data.attr_value.len = 0; GET_VERIFY_HEX_STRING(7, data.attr_value.value, data.attr_value.len); haltest_info("conn_id %d, trans_id %d, status %d", conn_id, trans_id, status); EXEC(if_gatt->server->send_response, conn_id, trans_id, status, &data); } #define GATTS_METHODH(n, h) METHOD(#n, gatts_##n##_p, NULL, h) #define GATTS_METHODCH(n, h) METHOD(#n, gatts_##n##_p, gatts_##n##_c, h) static struct method server_methods[] = { GATTS_METHODH(register_server, "[]"), GATTS_METHODCH(unregister_server, ""), #if ANDROID_VERSION >= PLATFORM_VER(5, 0, 0) GATTS_METHODCH(connect, " [] []"), #else GATTS_METHODCH(connect, " []"), #endif GATTS_METHODCH(disconnect, " "), GATTS_METHODCH(add_service, " "), GATTS_METHODCH(add_included_service, " "), GATTS_METHODCH(add_characteristic, " "), GATTS_METHODCH(add_descriptor, " "), GATTS_METHODCH(start_service, " "), GATTS_METHODCH(stop_service, " "), GATTS_METHODCH(delete_service, " "), GATTS_METHODH(send_indication, " []"), GATTS_METHODH(send_response, " []"), END_METHOD }; const struct interface gatt_server_if = { .name = "gatts", .methods = server_methods }; bluez-5.82/android/client/PaxHeaders/terminal.h0000644000000000000000000000005014015011623016521 xustar0020 atime=1743516867 20 ctime=1743591279 bluez-5.82/android/client/terminal.h0000644000000000000000000000170714015011623016207 0ustar00rootroot/* SPDX-License-Identifier: Apache-2.0 */ /* * Copyright (C) 2013 Intel Corporation * */ #include /* size of supported line */ #define LINE_BUF_MAX 1024 enum key_codes { KEY_BACKSPACE = 0x7F, KEY_INSERT = 1000, /* arbitrary value */ KEY_DELETE, KEY_HOME, KEY_END, KEY_PGUP, KEY_PGDOWN, KEY_LEFT, KEY_RIGHT, KEY_UP, KEY_DOWN, KEY_CLEFT, KEY_CRIGHT, KEY_CUP, KEY_CDOWN, KEY_SLEFT, KEY_SRIGHT, KEY_SUP, KEY_SDOWN, KEY_MLEFT, KEY_MRIGHT, KEY_MUP, KEY_MDOWN, KEY_STAB, KEY_M_p, KEY_M_n }; typedef void (*line_callback)(char *); void terminal_setup(void); int terminal_print(const char *format, ...); int terminal_vprint(const char *format, va_list args); void terminal_process_char(int c, line_callback process_line); void terminal_insert_into_command_line(const char *p); void terminal_draw_command_line(void); void terminal_prompt_for(const char *s, line_callback process_line); void process_tab(const char *line, int len); bluez-5.82/android/client/PaxHeaders/if-hf.c0000644000000000000000000000005014015011623015672 xustar0020 atime=1743516867 20 ctime=1743591279 bluez-5.82/android/client/if-hf.c0000644000000000000000000005633714015011623015371 0ustar00rootroot// SPDX-License-Identifier: Apache-2.0 /* * Copyright (C) 2013 Intel Corporation * */ #define _GNU_SOURCE #include "if-main.h" #include "../hal-utils.h" const bthf_interface_t *if_hf = NULL; SINTMAP(bthf_at_response_t, -1, "(unknown)") DELEMENT(BTHF_AT_RESPONSE_ERROR), DELEMENT(BTHF_AT_RESPONSE_OK), ENDMAP SINTMAP(bthf_connection_state_t, -1, "(unknown)") DELEMENT(BTHF_CONNECTION_STATE_DISCONNECTED), DELEMENT(BTHF_CONNECTION_STATE_CONNECTING), DELEMENT(BTHF_CONNECTION_STATE_CONNECTED), DELEMENT(BTHF_CONNECTION_STATE_SLC_CONNECTED), DELEMENT(BTHF_CONNECTION_STATE_DISCONNECTING), ENDMAP SINTMAP(bthf_audio_state_t, -1, "(unknown)") DELEMENT(BTHF_AUDIO_STATE_DISCONNECTED), DELEMENT(BTHF_AUDIO_STATE_CONNECTING), DELEMENT(BTHF_AUDIO_STATE_CONNECTED), DELEMENT(BTHF_AUDIO_STATE_DISCONNECTING), ENDMAP SINTMAP(bthf_vr_state_t, -1, "(unknown)") DELEMENT(BTHF_VR_STATE_STOPPED), DELEMENT(BTHF_VR_STATE_STARTED), ENDMAP SINTMAP(bthf_volume_type_t, -1, "(unknown)") DELEMENT(BTHF_VOLUME_TYPE_SPK), DELEMENT(BTHF_VOLUME_TYPE_MIC), ENDMAP SINTMAP(bthf_nrec_t, -1, "(unknown)") DELEMENT(BTHF_NREC_STOP), DELEMENT(BTHF_NREC_START), ENDMAP SINTMAP(bthf_chld_type_t, -1, "(unknown)") DELEMENT(BTHF_CHLD_TYPE_RELEASEHELD), DELEMENT(BTHF_CHLD_TYPE_RELEASEACTIVE_ACCEPTHELD), DELEMENT(BTHF_CHLD_TYPE_HOLDACTIVE_ACCEPTHELD), DELEMENT(BTHF_CHLD_TYPE_ADDHELDTOCONF), ENDMAP /* Network Status */ SINTMAP(bthf_network_state_t, -1, "(unknown)") DELEMENT(BTHF_NETWORK_STATE_NOT_AVAILABLE), DELEMENT(BTHF_NETWORK_STATE_AVAILABLE), ENDMAP /* Service type */ SINTMAP(bthf_service_type_t, -1, "(unknown)") DELEMENT(BTHF_SERVICE_TYPE_HOME), DELEMENT(BTHF_SERVICE_TYPE_ROAMING), ENDMAP SINTMAP(bthf_call_state_t, -1, "(unknown)") DELEMENT(BTHF_CALL_STATE_ACTIVE), DELEMENT(BTHF_CALL_STATE_HELD), DELEMENT(BTHF_CALL_STATE_DIALING), DELEMENT(BTHF_CALL_STATE_ALERTING), DELEMENT(BTHF_CALL_STATE_INCOMING), DELEMENT(BTHF_CALL_STATE_WAITING), DELEMENT(BTHF_CALL_STATE_IDLE), ENDMAP SINTMAP(bthf_call_direction_t, -1, "(unknown)") DELEMENT(BTHF_CALL_DIRECTION_OUTGOING), DELEMENT(BTHF_CALL_DIRECTION_INCOMING), ENDMAP SINTMAP(bthf_call_mode_t, -1, "(unknown)") DELEMENT(BTHF_CALL_TYPE_VOICE), DELEMENT(BTHF_CALL_TYPE_DATA), DELEMENT(BTHF_CALL_TYPE_FAX), ENDMAP SINTMAP(bthf_call_mpty_type_t, -1, "(unknown)") DELEMENT(BTHF_CALL_MPTY_TYPE_SINGLE), DELEMENT(BTHF_CALL_MPTY_TYPE_MULTI), ENDMAP SINTMAP(bthf_call_addrtype_t, -1, "(unknown)") DELEMENT(BTHF_CALL_ADDRTYPE_UNKNOWN), DELEMENT(BTHF_CALL_ADDRTYPE_INTERNATIONAL), ENDMAP #if ANDROID_VERSION >= PLATFORM_VER(5, 0, 0) SINTMAP(bthf_wbs_config_t, -1, "(unknown)") DELEMENT(BTHF_WBS_NONE), DELEMENT(BTHF_WBS_NO), DELEMENT(BTHF_WBS_YES), ENDMAP #endif /* Callbacks */ static char last_addr[MAX_ADDR_STR_LEN]; /* * Callback for connection state change. * state will have one of the values from BtHfConnectionState */ static void connection_state_cb(bthf_connection_state_t state, bt_bdaddr_t *bd_addr) { haltest_info("%s: state=%s bd_addr=%s\n", __func__, bthf_connection_state_t2str(state), bt_bdaddr_t2str(bd_addr, last_addr)); } /* * Callback for audio connection state change. * state will have one of the values from BtHfAudioState */ static void audio_state_cb(bthf_audio_state_t state, bt_bdaddr_t *bd_addr) { haltest_info("%s: state=%s bd_addr=%s\n", __func__, bthf_audio_state_t2str(state), bt_bdaddr_t2str(bd_addr, last_addr)); } /* * Callback for VR connection state change. * state will have one of the values from BtHfVRState */ #if ANDROID_VERSION >= PLATFORM_VER(5, 0, 0) static void vr_cmd_cb(bthf_vr_state_t state, bt_bdaddr_t *bd_addr) { haltest_info("%s: state=%s bd_addr=%s\n", __func__, bthf_vr_state_t2str(state), bt_bdaddr_t2str(bd_addr, last_addr)); } #else static void vr_cmd_cb(bthf_vr_state_t state) { haltest_info("%s: state=%s\n", __func__, bthf_vr_state_t2str(state)); } #endif /* Callback for answer incoming call (ATA) */ #if ANDROID_VERSION >= PLATFORM_VER(5, 0, 0) static void answer_call_cmd_cb(bt_bdaddr_t *bd_addr) { haltest_info("%s: bd_addr=%s\n", __func__, bt_bdaddr_t2str(bd_addr, last_addr)); } #else static void answer_call_cmd_cb(void) { haltest_info("%s\n", __func__); } #endif /* Callback for disconnect call (AT+CHUP) */ #if ANDROID_VERSION >= PLATFORM_VER(5, 0, 0) static void hangup_call_cmd_cb(bt_bdaddr_t *bd_addr) { haltest_info("%s: bd_addr=%s\n", __func__, bt_bdaddr_t2str(bd_addr, last_addr)); } #else static void hangup_call_cmd_cb(void) { haltest_info("%s\n", __func__); } #endif /* * Callback for disconnect call (AT+CHUP) * type will denote Speaker/Mic gain (BtHfVolumeControl). */ #if ANDROID_VERSION >= PLATFORM_VER(5, 0, 0) static void volume_cmd_cb(bthf_volume_type_t type, int volume, bt_bdaddr_t *bd_addr) { haltest_info("%s: type=%s volume=%d bd_addr=%s\n", __func__, bthf_volume_type_t2str(type), volume, bt_bdaddr_t2str(bd_addr, last_addr)); } #else static void volume_cmd_cb(bthf_volume_type_t type, int volume) { haltest_info("%s: type=%s volume=%d\n", __func__, bthf_volume_type_t2str(type), volume); } #endif /* * Callback for dialing an outgoing call * If number is NULL, redial */ #if ANDROID_VERSION >= PLATFORM_VER(5, 0, 0) static void dial_call_cmd_cb(char *number, bt_bdaddr_t *bd_addr) { haltest_info("%s: number=%s bd_addr=%s\n", __func__, number, bt_bdaddr_t2str(bd_addr, last_addr)); } #else static void dial_call_cmd_cb(char *number) { haltest_info("%s: number=%s\n", __func__, number); } #endif /* * Callback for sending DTMF tones * tone contains the dtmf character to be sent */ #if ANDROID_VERSION >= PLATFORM_VER(5, 0, 0) static void dtmf_cmd_cb(char tone, bt_bdaddr_t *bd_addr) { haltest_info("%s: tone=%d bd_addr=%s\n", __func__, tone, bt_bdaddr_t2str(bd_addr, last_addr)); } #else static void dtmf_cmd_cb(char tone) { haltest_info("%s: tone=%d\n", __func__, tone); } #endif /* * Callback for enabling/disabling noise reduction/echo cancellation * value will be 1 to enable, 0 to disable */ #if ANDROID_VERSION >= PLATFORM_VER(5, 0, 0) static void nrec_cmd_cb(bthf_nrec_t nrec, bt_bdaddr_t *bd_addr) { haltest_info("%s: nrec=%s bd_addr=%s\n", __func__, bthf_nrec_t2str(nrec), bt_bdaddr_t2str(bd_addr, last_addr)); } #else static void nrec_cmd_cb(bthf_nrec_t nrec) { haltest_info("%s: nrec=%s\n", __func__, bthf_nrec_t2str(nrec)); } #endif /* * Callback for call hold handling (AT+CHLD) * value will contain the call hold command (0, 1, 2, 3) */ #if ANDROID_VERSION >= PLATFORM_VER(5, 0, 0) static void chld_cmd_cb(bthf_chld_type_t chld, bt_bdaddr_t *bd_addr) { haltest_info("%s: chld=%s bd_addr=%s\n", __func__, bthf_chld_type_t2str(chld), bt_bdaddr_t2str(bd_addr, last_addr)); } #else static void chld_cmd_cb(bthf_chld_type_t chld) { haltest_info("%s: chld=%s\n", __func__, bthf_chld_type_t2str(chld)); } #endif /* Callback for CNUM (subscriber number) */ #if ANDROID_VERSION >= PLATFORM_VER(5, 0, 0) static void cnum_cmd_cb(bt_bdaddr_t *bd_addr) { haltest_info("%s: bd_addr=%s\n", __func__, bt_bdaddr_t2str(bd_addr, last_addr)); } #else static void cnum_cmd_cb(void) { haltest_info("%s\n", __func__); } #endif /* Callback for indicators (CIND) */ #if ANDROID_VERSION >= PLATFORM_VER(5, 0, 0) static void cind_cmd_cb(bt_bdaddr_t *bd_addr) { haltest_info("%s: bd_addr=%s\n", __func__, bt_bdaddr_t2str(bd_addr, last_addr)); } #else static void cind_cmd_cb(void) { haltest_info("%s\n", __func__); } #endif /* Callback for operator selection (COPS) */ #if ANDROID_VERSION >= PLATFORM_VER(5, 0, 0) static void cops_cmd_cb(bt_bdaddr_t *bd_addr) { haltest_info("%s: bd_addr=%s\n", __func__, bt_bdaddr_t2str(bd_addr, last_addr)); } #else static void cops_cmd_cb(void) { haltest_info("%s\n", __func__); } #endif /* Callback for call list (AT+CLCC) */ #if ANDROID_VERSION >= PLATFORM_VER(5, 0, 0) static void clcc_cmd_cb(bt_bdaddr_t *bd_addr) { haltest_info("%s: bd_addr=%s\n", __func__, bt_bdaddr_t2str(bd_addr, last_addr)); } #else static void clcc_cmd_cb(void) { haltest_info("%s\n", __func__); } #endif /* * Callback for unknown AT command recd from HF * at_string will contain the unparsed AT string */ #if ANDROID_VERSION >= PLATFORM_VER(5, 0, 0) static void unknown_at_cmd_cb(char *at_string, bt_bdaddr_t *bd_addr) { haltest_info("%s: at_string=%s bd_addr=%s\n", __func__, at_string, bt_bdaddr_t2str(bd_addr, last_addr)); } #else static void unknown_at_cmd_cb(char *at_string) { haltest_info("%s: at_string=%s\n", __func__, at_string); } #endif /* Callback for keypressed (HSP) event. */ #if ANDROID_VERSION >= PLATFORM_VER(5, 0, 0) static void key_pressed_cmd_cb(bt_bdaddr_t *bd_addr) { haltest_info("%s: bd_addr=%s\n", __func__, bt_bdaddr_t2str(bd_addr, last_addr)); } #else static void key_pressed_cmd_cb(void) { haltest_info("%s\n", __func__); } #endif #if ANDROID_VERSION >= PLATFORM_VER(5, 0, 0) static void wbs_cb(bthf_wbs_config_t wbs, bt_bdaddr_t *bd_addr) { haltest_info("%s: bd_addr=%s\n", __func__, bt_bdaddr_t2str(bd_addr, last_addr)); } #endif static bthf_callbacks_t hf_cbacks = { .size = sizeof(hf_cbacks), .connection_state_cb = connection_state_cb, .audio_state_cb = audio_state_cb, .vr_cmd_cb = vr_cmd_cb, .answer_call_cmd_cb = answer_call_cmd_cb, .hangup_call_cmd_cb = hangup_call_cmd_cb, .volume_cmd_cb = volume_cmd_cb, .dial_call_cmd_cb = dial_call_cmd_cb, .dtmf_cmd_cb = dtmf_cmd_cb, .nrec_cmd_cb = nrec_cmd_cb, #if ANDROID_VERSION >= PLATFORM_VER(5, 0, 0) .wbs_cb = wbs_cb, #endif .chld_cmd_cb = chld_cmd_cb, .cnum_cmd_cb = cnum_cmd_cb, .cind_cmd_cb = cind_cmd_cb, .cops_cmd_cb = cops_cmd_cb, .clcc_cmd_cb = clcc_cmd_cb, .unknown_at_cmd_cb = unknown_at_cmd_cb, .key_pressed_cmd_cb = key_pressed_cmd_cb, }; /* init */ static void init_p(int argc, const char **argv) { #if ANDROID_VERSION >= PLATFORM_VER(5, 0, 0) int max_hf_clients; #endif RETURN_IF_NULL(if_hf); #if ANDROID_VERSION >= PLATFORM_VER(5, 0, 0) if (argc <= 2) max_hf_clients = 1; else max_hf_clients = atoi(argv[2]); EXEC(if_hf->init, &hf_cbacks, max_hf_clients); #else EXEC(if_hf->init, &hf_cbacks); #endif } /* connect */ static void connect_c(int argc, const char **argv, enum_func *enum_func, void **user) { if (argc == 3) { *user = NULL; *enum_func = enum_devices; } } static void connect_p(int argc, const char **argv) { bt_bdaddr_t addr; RETURN_IF_NULL(if_hf); VERIFY_ADDR_ARG(2, &addr); EXEC(if_hf->connect, &addr); } /* disconnect */ /* * This completion function will be used for several methods * returning recently connected address */ static void connected_addr_c(int argc, const char **argv, enum_func *enum_func, void **user) { if (argc == 3) { *user = last_addr; *enum_func = enum_one_string; } } /* Map completion to connected_addr_c */ #define disconnect_c connected_addr_c static void disconnect_p(int argc, const char **argv) { bt_bdaddr_t addr; RETURN_IF_NULL(if_hf); VERIFY_ADDR_ARG(2, &addr); EXEC(if_hf->disconnect, &addr); } /* create an audio connection */ /* Map completion to connected_addr_c */ #define connect_audio_c connected_addr_c static void connect_audio_p(int argc, const char **argv) { bt_bdaddr_t addr; RETURN_IF_NULL(if_hf); VERIFY_ADDR_ARG(2, &addr); EXEC(if_hf->connect_audio, &addr); } /* close the audio connection */ /* Map completion to connected_addr_c */ #define disconnect_audio_c connected_addr_c static void disconnect_audio_p(int argc, const char **argv) { bt_bdaddr_t addr; RETURN_IF_NULL(if_hf); VERIFY_ADDR_ARG(2, &addr); EXEC(if_hf->disconnect_audio, &addr); } /* start voice recognition */ static void start_voice_recognition_p(int argc, const char **argv) { #if ANDROID_VERSION >= PLATFORM_VER(5, 0, 0) bt_bdaddr_t addr; #endif RETURN_IF_NULL(if_hf); #if ANDROID_VERSION >= PLATFORM_VER(5, 0, 0) VERIFY_ADDR_ARG(2, &addr); EXEC(if_hf->start_voice_recognition, &addr); #else EXEC(if_hf->start_voice_recognition); #endif } /* stop voice recognition */ static void stop_voice_recognition_p(int argc, const char **argv) { #if ANDROID_VERSION >= PLATFORM_VER(5, 0, 0) bt_bdaddr_t addr; #endif RETURN_IF_NULL(if_hf); #if ANDROID_VERSION >= PLATFORM_VER(5, 0, 0) VERIFY_ADDR_ARG(2, &addr); EXEC(if_hf->stop_voice_recognition, &addr); #else EXEC(if_hf->stop_voice_recognition); #endif } /* volume control */ static void volume_control_c(int argc, const char **argv, enum_func *enum_func, void **user) { if (argc == 3) { *user = TYPE_ENUM(bthf_volume_type_t); *enum_func = enum_defines; } } static void volume_control_p(int argc, const char **argv) { bthf_volume_type_t type; int volume; #if ANDROID_VERSION >= PLATFORM_VER(5, 0, 0) bt_bdaddr_t addr; #endif RETURN_IF_NULL(if_hf); /* volume type */ if (argc <= 2) { haltest_error("No volume type specified\n"); return; } type = str2bthf_volume_type_t(argv[2]); /* volume */ if (argc <= 3) { haltest_error("No volume specified\n"); return; } volume = atoi(argv[3]); #if ANDROID_VERSION >= PLATFORM_VER(5, 0, 0) VERIFY_ADDR_ARG(4, &addr); EXEC(if_hf->volume_control, type, volume, &addr); #else EXEC(if_hf->volume_control, type, volume); #endif } /* Combined device status change notification */ static void device_status_notification_c(int argc, const char **argv, enum_func *enum_func, void **user) { if (argc == 3) { *user = TYPE_ENUM(bthf_network_state_t); *enum_func = enum_defines; } else if (argc == 4) { *user = TYPE_ENUM(bthf_service_type_t); *enum_func = enum_defines; } } static void device_status_notification_p(int argc, const char **argv) { bthf_network_state_t ntk_state; bthf_service_type_t svc_type; int signal; int batt_chg; RETURN_IF_NULL(if_hf); /* network state */ if (argc <= 2) { haltest_error("No network state specified\n"); return; } ntk_state = str2bthf_network_state_t(argv[2]); /* service type */ if (argc <= 3) { haltest_error("No service type specified\n"); return; } svc_type = str2bthf_service_type_t(argv[3]); /* signal */ if (argc <= 4) { haltest_error("No signal specified\n"); return; } signal = atoi(argv[4]); /* batt_chg */ if (argc <= 5) { haltest_error("No batt_chg specified\n"); return; } batt_chg = atoi(argv[5]); EXEC(if_hf->device_status_notification, ntk_state, svc_type, signal, batt_chg); } /* Response for COPS command */ static void cops_response_p(int argc, const char **argv) { #if ANDROID_VERSION >= PLATFORM_VER(5, 0, 0) bt_bdaddr_t addr; #endif RETURN_IF_NULL(if_hf); /* response */ if (argc <= 2) { haltest_error("No cops specified\n"); return; } #if ANDROID_VERSION >= PLATFORM_VER(5, 0, 0) VERIFY_ADDR_ARG(3, &addr); EXEC(if_hf->cops_response, argv[2], &addr); #else EXEC(if_hf->cops_response, argv[2]); #endif } /* Response for CIND command */ static void cind_response_c(int argc, const char **argv, enum_func *enum_func, void **user) { if (argc == 6) { *user = TYPE_ENUM(bthf_call_state_t); *enum_func = enum_defines; } } static void cind_response_p(int argc, const char **argv) { int svc; int num_active; int num_held; bthf_call_state_t call_setup_state; int signal; int roam; int batt_chg; #if ANDROID_VERSION >= PLATFORM_VER(5, 0, 0) bt_bdaddr_t addr; #endif RETURN_IF_NULL(if_hf); /* svc */ if (argc <= 2) { haltest_error("No service specified\n"); return; } svc = atoi(argv[2]); /* num active */ if (argc <= 3) { haltest_error("No num active specified\n"); return; } num_active = atoi(argv[3]); /* num held */ if (argc <= 4) { haltest_error("No num held specified\n"); return; } num_held = atoi(argv[4]); /* call setup state */ if (argc <= 5) { haltest_error("No call setup state specified\n"); return; } call_setup_state = str2bthf_call_state_t(argv[5]); /* signal */ if (argc <= 6) { haltest_error("No signal specified\n"); return; } signal = atoi(argv[6]); /* roam */ if (argc <= 7) { haltest_error("No roam specified\n"); return; } roam = atoi(argv[7]); /* batt_chg */ if (argc <= 8) { haltest_error("No batt_chg specified\n"); return; } batt_chg = atoi(argv[8]); #if ANDROID_VERSION >= PLATFORM_VER(5, 0, 0) VERIFY_ADDR_ARG(9, &addr); EXEC(if_hf->cind_response, svc, num_active, num_held, call_setup_state, signal, roam, batt_chg, &addr); #else EXEC(if_hf->cind_response, svc, num_active, num_held, call_setup_state, signal, roam, batt_chg); #endif } /* Pre-formatted AT response, typically in response to unknown AT cmd */ static void formatted_at_response_p(int argc, const char **argv) { #if ANDROID_VERSION >= PLATFORM_VER(5, 0, 0) bt_bdaddr_t addr; #endif RETURN_IF_NULL(if_hf); /* response */ if (argc <= 2) { haltest_error("No response specified\n"); return; } #if ANDROID_VERSION >= PLATFORM_VER(5, 0, 0) VERIFY_ADDR_ARG(3, &addr); EXEC(if_hf->formatted_at_response, argv[2], &addr); #else EXEC(if_hf->formatted_at_response, argv[2]); #endif } /* at_response */ static void at_response_c(int argc, const char **argv, enum_func *enum_func, void **user) { if (argc == 3) { *user = TYPE_ENUM(bthf_at_response_t); *enum_func = enum_defines; } } static void at_response_p(int argc, const char **argv) { bthf_at_response_t response_code; int error_code; #if ANDROID_VERSION >= PLATFORM_VER(5, 0, 0) bt_bdaddr_t addr; #endif RETURN_IF_NULL(if_hf); /* response type */ if (argc <= 2) { haltest_error("No response specified\n"); return; } response_code = str2bthf_at_response_t(argv[2]); /* error code */ if (argc <= 3) error_code = 0; else error_code = atoi(argv[3]); #if ANDROID_VERSION >= PLATFORM_VER(5, 0, 0) VERIFY_ADDR_ARG(4, &addr); EXEC(if_hf->at_response, response_code, error_code, &addr); #else EXEC(if_hf->at_response, response_code, error_code); #endif } /* response for CLCC command */ static void clcc_response_c(int argc, const char **argv, enum_func *enum_func, void **user) { if (argc == 4) { *user = TYPE_ENUM(bthf_call_direction_t); *enum_func = enum_defines; } else if (argc == 5) { *user = TYPE_ENUM(bthf_call_state_t); *enum_func = enum_defines; } else if (argc == 6) { *user = TYPE_ENUM(bthf_call_mode_t); *enum_func = enum_defines; } else if (argc == 7) { *user = TYPE_ENUM(bthf_call_mpty_type_t); *enum_func = enum_defines; } else if (argc == 9) { *user = TYPE_ENUM(bthf_call_addrtype_t); *enum_func = enum_defines; } } static void clcc_response_p(int argc, const char **argv) { int index; bthf_call_direction_t dir; bthf_call_state_t state; bthf_call_mode_t mode; bthf_call_mpty_type_t mpty; const char *number; bthf_call_addrtype_t type; #if ANDROID_VERSION >= PLATFORM_VER(5, 0, 0) bt_bdaddr_t addr; #endif RETURN_IF_NULL(if_hf); /* index */ if (argc <= 2) { haltest_error("No index specified\n"); return; } index = atoi(argv[2]); /* direction */ if (argc <= 3) { haltest_error("No direction specified\n"); return; } dir = str2bthf_call_direction_t(argv[3]); /* call state */ if (argc <= 4) { haltest_error("No call state specified\n"); return; } state = str2bthf_call_state_t(argv[4]); /* call mode */ if (argc <= 5) { haltest_error("No mode specified\n"); return; } mode = str2bthf_call_mode_t(argv[5]); /* call mpty type */ if (argc <= 6) { haltest_error("No mpty type specified\n"); return; } mpty = str2bthf_call_mpty_type_t(argv[6]); /* number */ if (argc <= 7) { haltest_error("No number specified\n"); return; } number = argv[7]; /* call mpty type */ if (argc <= 8) { haltest_error("No address type specified\n"); return; } type = str2bthf_call_addrtype_t(argv[8]); #if ANDROID_VERSION >= PLATFORM_VER(5, 0, 0) VERIFY_ADDR_ARG(9, &addr); EXEC(if_hf->clcc_response, index, dir, state, mode, mpty, number, type, &addr); #else EXEC(if_hf->clcc_response, index, dir, state, mode, mpty, number, type); #endif } #if ANDROID_VERSION >= PLATFORM_VER(5, 0, 0) static void configure_wbs_c(int argc, const char **argv, enum_func *enum_func, void **user) { if (argc == 4) { *user = TYPE_ENUM(bthf_wbs_config_t); *enum_func = enum_defines; } } static void configure_wbs_p(int argc, const char **argv) { bthf_wbs_config_t wbs; bt_bdaddr_t addr; RETURN_IF_NULL(if_hf); if (argc <= 3) { haltest_error("Too few parameters specified\n"); return; } VERIFY_ADDR_ARG(2, &addr); wbs = str2bthf_wbs_config_t(argv[3]); EXEC(if_hf->configure_wbs, &addr, wbs); } #endif /* phone state change */ static void phone_state_change_c(int argc, const char **argv, enum_func *enum_func, void **user) { if (argc == 5) { *user = TYPE_ENUM(bthf_call_state_t); *enum_func = enum_defines; } else if (argc == 7) { *user = TYPE_ENUM(bthf_call_addrtype_t); *enum_func = enum_defines; } } static void phone_state_change_p(int argc, const char **argv) { int num_active; int num_held; bthf_call_state_t call_setup_state; const char *number; bthf_call_addrtype_t type; RETURN_IF_NULL(if_hf); /* num_active */ if (argc <= 2) { haltest_error("No num_active specified\n"); return; } num_active = atoi(argv[2]); /* num_held */ if (argc <= 3) { haltest_error("No num_held specified\n"); return; } num_held = atoi(argv[3]); /* setup state */ if (argc <= 4) { haltest_error("No call setup state specified\n"); return; } call_setup_state = str2bthf_call_state_t(argv[4]); /* number */ if (argc <= 5) { haltest_error("No number specified\n"); return; } number = argv[5]; /* call mpty type */ if (argc <= 6) { haltest_error("No address type specified\n"); return; } type = str2bthf_call_addrtype_t(argv[6]); EXEC(if_hf->phone_state_change, num_active, num_held, call_setup_state, number, type); } /* cleanup */ static void cleanup_p(int argc, const char **argv) { RETURN_IF_NULL(if_hf); EXECV(if_hf->cleanup); if_hf = NULL; } static struct method methods[] = { #if ANDROID_VERSION >= PLATFORM_VER(5, 0, 0) STD_METHODH(init, "[]"), STD_METHODH(start_voice_recognition, ""), STD_METHODH(stop_voice_recognition, ""), STD_METHODCH(volume_control, " "), STD_METHODH(cops_response, " "), STD_METHODCH(cind_response, " " " "), STD_METHODH(formatted_at_response, " "), STD_METHODCH(at_response, " [ ]"), STD_METHODCH(clcc_response, " " " "), STD_METHODCH(configure_wbs, " "), #else STD_METHOD(init), STD_METHOD(start_voice_recognition), STD_METHOD(stop_voice_recognition), STD_METHODCH(volume_control, " "), STD_METHODH(cops_response, ""), STD_METHODCH(cind_response, " " " "), STD_METHODH(formatted_at_response, ""), STD_METHODCH(at_response, " []"), STD_METHODCH(clcc_response, " " ""), #endif STD_METHODCH(connect, ""), STD_METHODCH(disconnect, ""), STD_METHODCH(connect_audio, ""), STD_METHODCH(disconnect_audio, ""), STD_METHODCH(device_status_notification, " "), STD_METHODCH(phone_state_change, " " ""), STD_METHOD(cleanup), END_METHOD }; const struct interface hf_if = { .name = "handsfree", .methods = methods }; bluez-5.82/android/client/PaxHeaders/tabcompletion.c0000644000000000000000000000005014015011623017541 xustar0020 atime=1743516867 20 ctime=1743591279 bluez-5.82/android/client/tabcompletion.c0000644000000000000000000002022014015011623017216 0ustar00rootroot// SPDX-License-Identifier: Apache-2.0 /* * Copyright (C) 2013 Intel Corporation * */ #define _GNU_SOURCE #include #include #include #include "if-main.h" #include "terminal.h" /* how many times tab was hit */ static int tab_hit_count; typedef struct split_arg { struct split_arg *next; /* next argument in buffer */ const char *origin; /* pointer to original argument */ char ntcopy[1]; /* null terminated copy of argument */ } split_arg_t; /* function returns method of given name or NULL if not found */ const struct method *get_interface_method(const char *iname, const char *mname) { const struct interface *iface = get_interface(iname); if (iface == NULL) return NULL; return get_method(iface->methods, mname); } /* prints matching elements */ static void print_matches(enum_func f, void *user, const char *prefix, int len) { int i; const char *enum_name; putchar('\n'); for (i = 0; NULL != (enum_name = f(user, i)); ++i) { if (strncmp(enum_name, prefix, len) == 0) printf("%s\t", enum_name); } putchar('\n'); terminal_draw_command_line(); } /* * This function splits command line into linked list of arguments. * line_buffer - pointer to input command line * size - size of command line to parse * buf - output buffer to keep split arguments list * buf_size_in_bytes - size of buf */ static int split_command(const char *line_buffer, int size, split_arg_t *buf, int buf_size_in_bytes) { split_arg_t *prev = NULL; split_arg_t *arg = buf; int argc = 0; const char *p = line_buffer; const char *e = p + (size > 0 ? size : (int) strlen(p)); int len; do { while (p < e && isspace(*p)) p++; arg->origin = p; arg->next = NULL; while (p < e && !isspace(*p)) p++; len = p - arg->origin; if (&arg->ntcopy[0] + len + 1 > (const char *) buf + buf_size_in_bytes) break; strncpy(arg->ntcopy, arg->origin, len); arg->ntcopy[len] = 0; if (prev != NULL) prev->next = arg; prev = arg; arg += (2 * sizeof(*arg) + len) / sizeof(*arg); argc++; } while (p < e); return argc; } /* Function to enumerate method names */ static const char *methods_name(void *v, int i) { const struct interface *iface = v; return iface->methods[i].name[0] ? iface->methods[i].name : NULL; } struct command_completion_args; typedef void (*short_help)(struct command_completion_args *args); struct command_completion_args { const split_arg_t *arg; /* list of arguments */ const char *typed; /* last typed element */ enum_func func; /* enumerating function */ void *user; /* argument to enumerating function */ short_help help; /* help function */ const char *user_help; /* additional data (used by short_help) */ }; /* complete command line */ static void tab_completion(struct command_completion_args *args) { const char *name = args->typed; const int len = strlen(name); int i; int j; char prefix[128] = {0}; int prefix_len = 0; int count = 0; const char *enum_name; for (i = 0; NULL != (enum_name = args->func(args->user, i)); ++i) { /* prefix does not match */ if (strncmp(enum_name, name, len) != 0) continue; /* prefix matches first time */ if (count++ == 0) { strcpy(prefix, enum_name); prefix_len = strlen(prefix); continue; } /* Prefix matches next time reduce prefix to common part */ for (j = 0; prefix[j] != 0 && prefix[j] == enum_name[j];) ++j; prefix_len = j; prefix[j] = 0; } if (count == 0) { /* no matches */ if (args->help != NULL) args->help(args); tab_hit_count = 0; return; } /* len == prefix_len => nothing new was added */ if (len == prefix_len) { if (count != 1) { if (tab_hit_count == 1) { putchar('\a'); } else if (tab_hit_count == 2 || args->help == NULL) { print_matches(args->func, args->user, name, len); } else { args->help(args); tab_hit_count = 1; } } else if (count == 1) { /* nothing to add, exact match add space */ terminal_insert_into_command_line(" "); } } else { /* new chars can be added from some interface name(s) */ if (count == 1) { /* exact match, add space */ prefix[prefix_len++] = ' '; prefix[prefix_len] = '\0'; } terminal_insert_into_command_line(prefix + len); tab_hit_count = 0; } } /* interface completion */ static void command_completion(split_arg_t *arg) { struct command_completion_args args = { .arg = arg, .typed = arg->ntcopy, .func = command_name }; tab_completion(&args); } /* method completion */ static void method_completion(const struct interface *iface, split_arg_t *arg) { struct command_completion_args args = { .arg = arg, .typed = arg->next->ntcopy, .func = methods_name, .user = (void *) iface }; if (iface == NULL) return; tab_completion(&args); } static const char *bold = "\x1b[1m"; static const char *normal = "\x1b[0m"; static bool find_nth_argument(const char *str, int n, const char **s, const char **e) { const char *p = str; int argc = 0; *e = NULL; while (p != NULL && *p != 0) { while (isspace(*p)) ++p; if (n == argc) *s = p; if (*p == '[') { p = strchr(p, ']'); if (p != NULL) *e = ++p; } else if (*p == '<') { p = strchr(p, '>'); if (p != NULL) *e = ++p; } else { *e = strchr(p, ' '); if (*e == NULL) *e = p + strlen(p); p = *e; } if (n == argc) break; argc++; *e = NULL; } return *e != NULL; } /* prints short help on method for interface */ static void method_help(struct command_completion_args *args) { int argc; const split_arg_t *arg = args->arg; const char *sb = NULL; const char *eb = NULL; const char *arg1 = ""; int arg1_size = 0; /* size of method field (for methods > 0) */ if (args->user_help == NULL) return; for (argc = 0; arg != NULL; argc++) arg = arg->next; /* Check if this is method from interface */ if (get_command(args->arg->ntcopy) == NULL) { /* if so help is missing interface and method name */ arg1 = args->arg->next->ntcopy; arg1_size = strlen(arg1) + 1; } find_nth_argument(args->user_help, argc - (arg1_size ? 3 : 2), &sb, &eb); if (eb != NULL) haltest_info("%s %-*s%.*s%s%.*s%s%s\n", args->arg->ntcopy, arg1_size, arg1, (int) (sb - args->user_help), args->user_help, bold, (int) (eb - sb), sb, normal, eb); else haltest_info("%s %-*s%s\n", args->arg->ntcopy, arg1_size, arg1, args->user_help); } /* So we have empty enumeration */ static const char *return_null(void *user, int i) { return NULL; } /* * parameter completion function * argc - number of elements in arg list * arg - list of arguments * method - method to get completion from (can be NULL) */ static void param_completion(int argc, const split_arg_t *arg, const struct method *method, int hlpix) { int i; const char *argv[argc]; const split_arg_t *tmp = arg; struct command_completion_args args = { .arg = arg, .func = return_null }; /* prepare standard argv from arg */ for (i = 0; i < argc; ++i) { argv[i] = tmp->ntcopy; tmp = tmp->next; } if (method != NULL && method->complete != NULL) { /* ask method for completion function */ method->complete(argc, argv, &args.func, &args.user); } /* If method provided enumeration function call try to complete */ if (args.func != NULL) { args.typed = argv[argc - 1]; args.help = method_help; args.user_help = method ? method->help : NULL; tab_completion(&args); } } /* * This method gets called when user tapped tab key. * line - points to command line * len - size of line that should be used for completions. This should be * cursor position during tab hit. */ void process_tab(const char *line, int len) { int argc; static split_arg_t buf[(LINE_BUF_MAX * 2) / sizeof(split_arg_t)]; const struct method *method; argc = split_command(line, len, buf, sizeof(buf)); tab_hit_count++; if (argc == 0) return; if (argc == 1) { command_completion(buf); return; } method = get_command(buf[0].ntcopy); if (method != NULL) { param_completion(argc, buf, method, 1); } else if (argc == 2) { method_completion(get_interface(buf[0].ntcopy), buf); } else { /* Find method for pair */ method = get_interface_method(buf[0].ntcopy, buf[0].next->ntcopy); param_completion(argc, buf, method, 2); } } bluez-5.82/android/client/PaxHeaders/terminal.c0000644000000000000000000000005014015011623016514 xustar0020 atime=1743516867 20 ctime=1743591279 bluez-5.82/android/client/terminal.c0000644000000000000000000004243714015011623016207 0ustar00rootroot// SPDX-License-Identifier: Apache-2.0 /* * Copyright (C) 2013 Intel Corporation * */ #include #include #include #include #include #include #include "terminal.h" #include "history.h" /* * Character sequences recognized by code in this file * Leading ESC 0x1B is not included */ #define SEQ_INSERT "[2~" #define SEQ_DELETE "[3~" #define SEQ_HOME "OH" #define SEQ_END "OF" #define SEQ_PGUP "[5~" #define SEQ_PGDOWN "[6~" #define SEQ_LEFT "[D" #define SEQ_RIGHT "[C" #define SEQ_UP "[A" #define SEQ_DOWN "[B" #define SEQ_STAB "[Z" #define SEQ_M_n "n" #define SEQ_M_p "p" #define SEQ_CLEFT "[1;5D" #define SEQ_CRIGHT "[1;5C" #define SEQ_CUP "[1;5A" #define SEQ_CDOWN "[1;5B" #define SEQ_SLEFT "[1;2D" #define SEQ_SRIGHT "[1;2C" #define SEQ_SUP "[1;2A" #define SEQ_SDOWN "[1;2B" #define SEQ_MLEFT "[1;3D" #define SEQ_MRIGHT "[1;3C" #define SEQ_MUP "[1;3A" #define SEQ_MDOWN "[1;3B" #define KEY_SEQUENCE(k) { KEY_##k, SEQ_##k } struct ansii_sequence { int code; const char *sequence; }; /* Table connects single int key codes with character sequences */ static const struct ansii_sequence ansii_sequnces[] = { KEY_SEQUENCE(INSERT), KEY_SEQUENCE(DELETE), KEY_SEQUENCE(HOME), KEY_SEQUENCE(END), KEY_SEQUENCE(PGUP), KEY_SEQUENCE(PGDOWN), KEY_SEQUENCE(LEFT), KEY_SEQUENCE(RIGHT), KEY_SEQUENCE(UP), KEY_SEQUENCE(DOWN), KEY_SEQUENCE(CLEFT), KEY_SEQUENCE(CRIGHT), KEY_SEQUENCE(CUP), KEY_SEQUENCE(CDOWN), KEY_SEQUENCE(SLEFT), KEY_SEQUENCE(SRIGHT), KEY_SEQUENCE(SUP), KEY_SEQUENCE(SDOWN), KEY_SEQUENCE(MLEFT), KEY_SEQUENCE(MRIGHT), KEY_SEQUENCE(MUP), KEY_SEQUENCE(MDOWN), KEY_SEQUENCE(STAB), KEY_SEQUENCE(M_p), KEY_SEQUENCE(M_n), { 0, NULL } }; #define KEY_SEQUNCE_NOT_FINISHED -1 #define KEY_C_C 3 #define KEY_C_D 4 #define KEY_C_L 12 #define isseqence(c) ((c) == 0x1B) /* * Number of characters that consist of ANSI sequence * Should not be less then longest string in ansi_sequences */ #define MAX_ASCII_SEQUENCE 10 static char current_sequence[MAX_ASCII_SEQUENCE]; static int current_sequence_len = -1; /* single line typed by user goes here */ static char line_buf[LINE_BUF_MAX]; /* index of cursor in input line */ static int line_buf_ix = 0; /* current length of input line */ static int line_len = 0; /* line index used for fetching lines from history */ static int line_index = 0; static char prompt_buf[10] = "> "; static const char *const noprompt = ""; static const char *current_prompt = prompt_buf; static const char *prompt = prompt_buf; /* * Moves cursor to right or left * * n - positive - moves cursor right * n - negative - moves cursor left */ static void terminal_move_cursor(int n) { if (n < 0) { for (; n < 0; n++) putchar('\b'); } else if (n > 0) { printf("%*s", n, line_buf + line_buf_ix); } } /* Draw command line */ void terminal_draw_command_line(void) { /* * this needs to be checked here since line_buf is not cleared * before parsing event though line_len and line_buf_ix are */ if (line_len > 0) printf("%s%s", prompt, line_buf); else printf("%s", prompt); /* move cursor to it's place */ terminal_move_cursor(line_buf_ix - line_len); } /* inserts string into command line at cursor position */ void terminal_insert_into_command_line(const char *p) { int len = strlen(p); if (line_len == line_buf_ix) { strcat(line_buf, p); printf("%s", p); line_len = line_len + len; line_buf_ix = line_len; } else { memmove(line_buf + line_buf_ix + len, line_buf + line_buf_ix, line_len - line_buf_ix + 1); memmove(line_buf + line_buf_ix, p, len); printf("%s", line_buf + line_buf_ix); line_buf_ix += len; line_len += len; terminal_move_cursor(line_buf_ix - line_len); } } /* Prints string and redraws command line */ int terminal_print(const char *format, ...) { va_list args; int ret; va_start(args, format); ret = terminal_vprint(format, args); va_end(args); return ret; } /* Prints string and redraws command line */ int terminal_vprint(const char *format, va_list args) { int ret; printf("\r%*s\r", (int) line_len + 1, " "); ret = vprintf(format, args); terminal_draw_command_line(); fflush(stdout); return ret; } /* * Call this when text in line_buf was changed * and line needs to be redrawn */ static void terminal_line_replaced(void) { int len = strlen(line_buf); /* line is shorter that previous */ if (len < line_len) { /* if new line is shorter move cursor to end of new end */ while (line_buf_ix > len) { putchar('\b'); line_buf_ix--; } /* If cursor was not at the end, move it to the end */ if (line_buf_ix < line_len) printf("%.*s", line_len - line_buf_ix, line_buf + line_buf_ix); /* over write end of previous line */ while (line_len >= len++) putchar(' '); } /* draw new line */ printf("\r%s%s", prompt, line_buf); /* set up indexes to new line */ line_len = strlen(line_buf); line_buf_ix = line_len; fflush(stdout); } static void terminal_clear_line(void) { line_buf[0] = '\0'; terminal_line_replaced(); } static void terminal_clear_screen(void) { line_buf[0] = '\0'; line_buf_ix = 0; line_len = 0; printf("\x1b[2J\x1b[1;1H%s", prompt); } static void terminal_delete_char(void) { /* delete character under cursor if not at the very end */ if (line_buf_ix >= line_len) return; /* * Prepare buffer with one character missing * trailing 0 is moved */ line_len--; memmove(line_buf + line_buf_ix, line_buf + line_buf_ix + 1, line_len - line_buf_ix + 1); /* print rest of line from current cursor position */ printf("%s \b", line_buf + line_buf_ix); /* move back cursor */ terminal_move_cursor(line_buf_ix - line_len); } /* * Function tries to replace current line with specified line in history * new_line_index - new line to show, -1 to show oldest */ static void terminal_get_line_from_history(int new_line_index) { new_line_index = history_get_line(new_line_index, line_buf, LINE_BUF_MAX); if (new_line_index >= 0) { terminal_line_replaced(); line_index = new_line_index; } } /* * Function searches history back or forward for command line that starts * with characters up to cursor position * * back - true - searches backward * back - false - searches forward (more recent commands) */ static void terminal_match_hitory(bool back) { char buf[line_buf_ix + 1]; int line; int matching_line = -1; int dir = back ? 1 : -1; line = line_index + dir; while (matching_line == -1 && line >= 0) { int new_line_index; new_line_index = history_get_line(line, buf, line_buf_ix + 1); if (new_line_index < 0) break; if (0 == strncmp(line_buf, buf, line_buf_ix)) matching_line = line; line += dir; } if (matching_line >= 0) { int pos = line_buf_ix; terminal_get_line_from_history(matching_line); /* move back to cursor position to original place */ line_buf_ix = pos; terminal_move_cursor(pos - line_len); } } /* * Converts terminal character sequences to single value representing * keyboard keys */ static int terminal_convert_sequence(int c) { int i; /* Not in sequence yet? */ if (current_sequence_len == -1) { /* Is ansi sequence detected by 0x1B ? */ if (isseqence(c)) { current_sequence_len++; return KEY_SEQUNCE_NOT_FINISHED; } return c; } /* Inside sequence */ current_sequence[current_sequence_len++] = c; current_sequence[current_sequence_len] = '\0'; for (i = 0; ansii_sequnces[i].code; ++i) { /* Matches so far? */ if (0 != strncmp(current_sequence, ansii_sequnces[i].sequence, current_sequence_len)) continue; /* Matches as a whole? */ if (ansii_sequnces[i].sequence[current_sequence_len] == 0) { current_sequence_len = -1; return ansii_sequnces[i].code; } /* partial match (not whole sequence yet) */ return KEY_SEQUNCE_NOT_FINISHED; } terminal_print("ansi char 0x%X %c\n", c); /* * Sequence does not match * mark that no in sequence any more, return char */ current_sequence_len = -1; return c; } typedef void (*terminal_action)(int c, line_callback process_line); #define TERMINAL_ACTION(n) \ static void n(int c, void (*process_line)(char *line)) TERMINAL_ACTION(terminal_action_null) { } /* Mapping between keys and function */ typedef struct { int key; terminal_action func; } KeyAction; int action_keys[] = { KEY_SEQUNCE_NOT_FINISHED, KEY_LEFT, KEY_RIGHT, KEY_HOME, KEY_END, KEY_DELETE, KEY_CLEFT, KEY_CRIGHT, KEY_SUP, KEY_SDOWN, KEY_UP, KEY_DOWN, KEY_BACKSPACE, KEY_INSERT, KEY_PGUP, KEY_PGDOWN, KEY_CUP, KEY_CDOWN, KEY_SLEFT, KEY_SRIGHT, KEY_MLEFT, KEY_MRIGHT, KEY_MUP, KEY_MDOWN, KEY_STAB, KEY_M_n, KEY_M_p, KEY_C_C, KEY_C_D, KEY_C_L, '\t', '\r', '\n', }; #define NELEM(x) ((int) (sizeof(x) / sizeof((x)[0]))) /* * current_actions holds all recognizable kes and actions for them * additional element (index 0) is used for default action */ static KeyAction current_actions[NELEM(action_keys) + 1]; /* KeyAction comparator by key, for qsort and bsearch */ static int KeyActionKeyCompare(const void *a, const void *b) { return ((const KeyAction *) a)->key - ((const KeyAction *) b)->key; } /* Find action by key, NULL if no action for this key */ static KeyAction *terminal_get_action(int key) { KeyAction a = { .key = key }; return bsearch(&a, current_actions + 1, NELEM(action_keys), sizeof(a), KeyActionKeyCompare); } /* Sets new set of actions to use */ static void terminal_set_actions(const KeyAction *actions) { int i; /* Make map with empty function for every key */ for (i = 0; i < NELEM(action_keys); ++i) { /* * + 1 due to 0 index reserved for default action that is * called for non mapped key */ current_actions[i + 1].key = action_keys[i]; current_actions[i + 1].func = terminal_action_null; } /* Sort action from 1 (index 0 - default action) */ qsort(current_actions + 1, NELEM(action_keys), sizeof(KeyAction), KeyActionKeyCompare); /* Set default action (first in array) */ current_actions[0] = *actions++; /* Copy rest of actions into their places */ for (; actions->key; ++actions) { KeyAction *place = terminal_get_action(actions->key); if (place) place->func = actions->func; } } TERMINAL_ACTION(terminal_action_left) { /* if not at the beginning move to previous character */ if (line_buf_ix <= 0) return; line_buf_ix--; terminal_move_cursor(-1); } TERMINAL_ACTION(terminal_action_right) { /* * If not at the end, just print current character * and modify position */ if (line_buf_ix < line_len) putchar(line_buf[line_buf_ix++]); } TERMINAL_ACTION(terminal_action_home) { /* move to beginning of line and update position */ printf("\r%s", prompt); line_buf_ix = 0; } TERMINAL_ACTION(terminal_action_end) { /* if not at the end of line */ if (line_buf_ix < line_len) { /* print everything from cursor */ printf("%s", line_buf + line_buf_ix); /* just modify current position */ line_buf_ix = line_len; } } TERMINAL_ACTION(terminal_action_del) { terminal_delete_char(); } TERMINAL_ACTION(terminal_action_word_left) { int old_pos; /* * Move by word left * * Are we at the beginning of line? */ if (line_buf_ix <= 0) return; old_pos = line_buf_ix; line_buf_ix--; /* skip spaces left */ while (line_buf_ix && isspace(line_buf[line_buf_ix])) line_buf_ix--; /* skip all non spaces to the left */ while (line_buf_ix > 0 && !isspace(line_buf[line_buf_ix - 1])) line_buf_ix--; /* move cursor to new position */ terminal_move_cursor(line_buf_ix - old_pos); } TERMINAL_ACTION(terminal_action_word_right) { int old_pos; /* * Move by word right * * are we at the end of line? */ if (line_buf_ix >= line_len) return; old_pos = line_buf_ix; /* skip all spaces */ while (line_buf_ix < line_len && isspace(line_buf[line_buf_ix])) line_buf_ix++; /* skip all non spaces */ while (line_buf_ix < line_len && !isspace(line_buf[line_buf_ix])) line_buf_ix++; /* * Move cursor to right by printing text * between old cursor and new */ if (line_buf_ix > old_pos) printf("%.*s", (int) (line_buf_ix - old_pos), line_buf + old_pos); } TERMINAL_ACTION(terminal_action_history_begin) { terminal_get_line_from_history(-1); } TERMINAL_ACTION(terminal_action_history_end) { if (line_index > 0) terminal_get_line_from_history(0); } TERMINAL_ACTION(terminal_action_history_up) { terminal_get_line_from_history(line_index + 1); } TERMINAL_ACTION(terminal_action_history_down) { if (line_index > 0) terminal_get_line_from_history(line_index - 1); } TERMINAL_ACTION(terminal_action_tab) { /* tab processing */ process_tab(line_buf, line_buf_ix); } TERMINAL_ACTION(terminal_action_backspace) { if (line_buf_ix <= 0) return; if (line_buf_ix == line_len) { printf("\b \b"); line_len = --line_buf_ix; line_buf[line_len] = 0; } else { putchar('\b'); line_buf_ix--; line_len--; memmove(line_buf + line_buf_ix, line_buf + line_buf_ix + 1, line_len - line_buf_ix + 1); printf("%s \b", line_buf + line_buf_ix); terminal_move_cursor(line_buf_ix - line_len); } } TERMINAL_ACTION(terminal_action_find_history_forward) { /* Search history forward */ terminal_match_hitory(false); } TERMINAL_ACTION(terminal_action_find_history_backward) { /* Search history forward */ terminal_match_hitory(true); } TERMINAL_ACTION(terminal_action_ctrl_c) { terminal_clear_line(); } TERMINAL_ACTION(terminal_action_ctrl_d) { if (line_len > 0) { terminal_delete_char(); } else { puts(""); exit(0); } } TERMINAL_ACTION(terminal_action_clear_screen) { terminal_clear_screen(); } TERMINAL_ACTION(terminal_action_enter) { /* * On new line add line to history * forget history position */ history_add_line(line_buf); line_len = 0; line_buf_ix = 0; line_index = -1; /* print new line */ putchar(c); prompt = noprompt; process_line(line_buf); /* clear current line */ line_buf[0] = '\0'; prompt = current_prompt; printf("%s", prompt); } TERMINAL_ACTION(terminal_action_default) { char str[2] = { c, 0 }; if (!isprint(c)) /* * TODO: remove this print once all meaningful sequences * are identified */ printf("char-0x%02x\n", c); else if (line_buf_ix < LINE_BUF_MAX - 1) terminal_insert_into_command_line(str); } /* Callback to call when user hit enter during prompt for */ static line_callback prompt_callback; static KeyAction normal_actions[] = { { 0, terminal_action_default }, { KEY_LEFT, terminal_action_left }, { KEY_RIGHT, terminal_action_right }, { KEY_HOME, terminal_action_home }, { KEY_END, terminal_action_end }, { KEY_DELETE, terminal_action_del }, { KEY_CLEFT, terminal_action_word_left }, { KEY_CRIGHT, terminal_action_word_right }, { KEY_SUP, terminal_action_history_begin }, { KEY_SDOWN, terminal_action_history_end }, { KEY_UP, terminal_action_history_up }, { KEY_DOWN, terminal_action_history_down }, { '\t', terminal_action_tab }, { KEY_BACKSPACE, terminal_action_backspace }, { KEY_M_n, terminal_action_find_history_forward }, { KEY_M_p, terminal_action_find_history_backward }, { KEY_C_C, terminal_action_ctrl_c }, { KEY_C_D, terminal_action_ctrl_d }, { KEY_C_L, terminal_action_clear_screen }, { '\r', terminal_action_enter }, { '\n', terminal_action_enter }, { 0, NULL }, }; TERMINAL_ACTION(terminal_action_answer) { putchar(c); terminal_set_actions(normal_actions); /* Restore default prompt */ current_prompt = prompt_buf; /* No prompt for prints */ prompt = noprompt; line_buf_ix = 0; line_len = 0; /* Call user function with what was typed */ prompt_callback(line_buf); line_buf[0] = 0; /* promot_callback could change current_prompt */ prompt = current_prompt; printf("%s", prompt); } TERMINAL_ACTION(terminal_action_prompt_ctrl_c) { printf("^C\n"); line_buf_ix = 0; line_len = 0; line_buf[0] = 0; current_prompt = prompt_buf; prompt = current_prompt; terminal_set_actions(normal_actions); printf("%s", prompt); } static KeyAction prompt_actions[] = { { 0, terminal_action_default }, { KEY_LEFT, terminal_action_left }, { KEY_RIGHT, terminal_action_right }, { KEY_HOME, terminal_action_home }, { KEY_END, terminal_action_end }, { KEY_DELETE, terminal_action_del }, { KEY_CLEFT, terminal_action_word_left }, { KEY_CRIGHT, terminal_action_word_right }, { KEY_BACKSPACE, terminal_action_backspace }, { KEY_C_C, terminal_action_prompt_ctrl_c }, { KEY_C_D, terminal_action_ctrl_d }, { '\r', terminal_action_answer }, { '\n', terminal_action_answer }, { 0, NULL }, }; void terminal_process_char(int c, line_callback process_line) { KeyAction *a; c = terminal_convert_sequence(c); /* Get action for this key */ a = terminal_get_action(c); /* No action found, get default one */ if (a == NULL) a = ¤t_actions[0]; a->func(c, process_line); fflush(stdout); } void terminal_prompt_for(const char *s, line_callback process_line) { current_prompt = s; if (prompt != noprompt) { prompt = s; terminal_clear_line(); } prompt_callback = process_line; terminal_set_actions(prompt_actions); } static struct termios origianl_tios; static void terminal_cleanup(void) { tcsetattr(0, TCSANOW, &origianl_tios); } void terminal_setup(void) { struct termios tios; terminal_set_actions(normal_actions); tcgetattr(0, &origianl_tios); tios = origianl_tios; /* * Turn off echo since all editing is done by hand, * Ctrl-c handled internally */ tios.c_lflag &= ~(ICANON | ECHO | BRKINT | IGNBRK); tcsetattr(0, TCSANOW, &tios); /* Restore terminal at exit */ atexit(terminal_cleanup); printf("%s", prompt); fflush(stdout); } bluez-5.82/android/client/PaxHeaders/if-audio.c0000644000000000000000000000005014015011623016376 xustar0020 atime=1743516867 20 ctime=1743591279 bluez-5.82/android/client/if-audio.c0000644000000000000000000003001214015011623016053 0ustar00rootroot// SPDX-License-Identifier: Apache-2.0 /* * Copyright (C) 2014 Intel Corporation * */ #define _GNU_SOURCE #include #include #include #include "if-main.h" #include "../hal-utils.h" audio_hw_device_t *if_audio = NULL; static struct audio_stream_out *stream_out = NULL; static size_t buffer_size = 0; static pthread_t play_thread = 0; static pthread_mutex_t outstream_mutex = PTHREAD_MUTEX_INITIALIZER; static pthread_mutex_t state_mutex = PTHREAD_MUTEX_INITIALIZER; enum state { STATE_STOPPED, STATE_STOPPING, STATE_PLAYING, STATE_SUSPENDED, STATE_MAX }; SINTMAP(audio_channel_mask_t, -1, "(AUDIO_CHANNEL_INVALID)") DELEMENT(AUDIO_CHANNEL_OUT_FRONT_LEFT), DELEMENT(AUDIO_CHANNEL_OUT_FRONT_RIGHT), DELEMENT(AUDIO_CHANNEL_OUT_FRONT_CENTER), DELEMENT(AUDIO_CHANNEL_OUT_LOW_FREQUENCY), DELEMENT(AUDIO_CHANNEL_OUT_BACK_LEFT), DELEMENT(AUDIO_CHANNEL_OUT_BACK_RIGHT), DELEMENT(AUDIO_CHANNEL_OUT_FRONT_LEFT_OF_CENTER), DELEMENT(AUDIO_CHANNEL_OUT_FRONT_RIGHT_OF_CENTER), DELEMENT(AUDIO_CHANNEL_OUT_BACK_CENTER), DELEMENT(AUDIO_CHANNEL_OUT_SIDE_LEFT), DELEMENT(AUDIO_CHANNEL_OUT_SIDE_RIGHT), DELEMENT(AUDIO_CHANNEL_OUT_TOP_CENTER), DELEMENT(AUDIO_CHANNEL_OUT_TOP_FRONT_LEFT), DELEMENT(AUDIO_CHANNEL_OUT_TOP_FRONT_CENTER), DELEMENT(AUDIO_CHANNEL_OUT_TOP_FRONT_RIGHT), DELEMENT(AUDIO_CHANNEL_OUT_TOP_BACK_LEFT), DELEMENT(AUDIO_CHANNEL_OUT_TOP_BACK_CENTER), DELEMENT(AUDIO_CHANNEL_OUT_TOP_BACK_RIGHT), DELEMENT(AUDIO_CHANNEL_OUT_MONO), DELEMENT(AUDIO_CHANNEL_OUT_STEREO), DELEMENT(AUDIO_CHANNEL_OUT_QUAD), #if ANDROID_VERSION < PLATFORM_VER(5, 0, 0) DELEMENT(AUDIO_CHANNEL_OUT_SURROUND), #else DELEMENT(AUDIO_CHANNEL_OUT_QUAD_BACK), DELEMENT(AUDIO_CHANNEL_OUT_QUAD_SIDE), DELEMENT(AUDIO_CHANNEL_OUT_5POINT1_BACK), DELEMENT(AUDIO_CHANNEL_OUT_5POINT1_SIDE), #endif DELEMENT(AUDIO_CHANNEL_OUT_5POINT1), DELEMENT(AUDIO_CHANNEL_OUT_7POINT1), DELEMENT(AUDIO_CHANNEL_OUT_ALL), DELEMENT(AUDIO_CHANNEL_OUT_FRONT_LEFT), DELEMENT(AUDIO_CHANNEL_OUT_FRONT_LEFT), DELEMENT(AUDIO_CHANNEL_OUT_FRONT_LEFT), DELEMENT(AUDIO_CHANNEL_OUT_FRONT_LEFT), DELEMENT(AUDIO_CHANNEL_OUT_FRONT_LEFT), ENDMAP SINTMAP(audio_format_t, -1, "(AUDIO_FORMAT_INVALID)") DELEMENT(AUDIO_FORMAT_DEFAULT), DELEMENT(AUDIO_FORMAT_PCM), DELEMENT(AUDIO_FORMAT_MP3), DELEMENT(AUDIO_FORMAT_AMR_NB), DELEMENT(AUDIO_FORMAT_AMR_WB), DELEMENT(AUDIO_FORMAT_AAC), DELEMENT(AUDIO_FORMAT_HE_AAC_V1), DELEMENT(AUDIO_FORMAT_HE_AAC_V2), DELEMENT(AUDIO_FORMAT_VORBIS), DELEMENT(AUDIO_FORMAT_MAIN_MASK), DELEMENT(AUDIO_FORMAT_SUB_MASK), DELEMENT(AUDIO_FORMAT_PCM_16_BIT), DELEMENT(AUDIO_FORMAT_PCM_8_BIT), DELEMENT(AUDIO_FORMAT_PCM_32_BIT), DELEMENT(AUDIO_FORMAT_PCM_8_24_BIT), ENDMAP static int current_state = STATE_STOPPED; #define SAMPLERATE 44100 static short sample[SAMPLERATE]; static uint16_t sample_pos; static void init_p(int argc, const char **argv) { int err; const hw_module_t *module; audio_hw_device_t *device; err = hw_get_module_by_class(AUDIO_HARDWARE_MODULE_ID, AUDIO_HARDWARE_MODULE_ID_A2DP, &module); if (err) { haltest_error("hw_get_module_by_class returned %d\n", err); return; } err = audio_hw_device_open(module, &device); if (err) { haltest_error("audio_hw_device_open returned %d\n", err); return; } if_audio = device; } static int feed_from_file(short *buffer, void *data) { FILE *in = data; return fread(buffer, buffer_size, 1, in); } static int feed_from_generator(short *buffer, void *data) { size_t i = 0; float volume = 0.5; float *freq = data; float f = 1; if (freq) f = *freq; /* buffer_size is in bytes but we are using buffer of shorts (2 bytes)*/ for (i = 0; i < buffer_size / sizeof(*buffer) - 1;) { if (sample_pos >= SAMPLERATE) sample_pos = sample_pos % SAMPLERATE; /* Use the same sample for both channels */ buffer[i++] = sample[sample_pos] * volume; buffer[i++] = sample[sample_pos] * volume; sample_pos += f; } return buffer_size; } static void prepare_sample(void) { int x; double s; haltest_info("Preparing audio sample...\n"); for (x = 0; x < SAMPLERATE; x++) { /* prepare sinusoidal 1Hz sample */ s = (2.0 * 3.14159) * ((double)x / SAMPLERATE); s = sin(s); /* remap <-1, 1> to signed 16bit PCM range */ sample[x] = s * 32767; } sample_pos = 0; } static void *playback_thread(void *data) { int (*filbuff_cb) (short*, void*); short buffer[buffer_size / sizeof(short)]; size_t len = 0; ssize_t w_len = 0; FILE *in = data; void *cb_data = NULL; float freq = 440.0; /* Use file or fall back to generator */ if (in) { filbuff_cb = feed_from_file; cb_data = in; } else { prepare_sample(); filbuff_cb = feed_from_generator; cb_data = &freq; } pthread_mutex_lock(&state_mutex); current_state = STATE_PLAYING; pthread_mutex_unlock(&state_mutex); do { pthread_mutex_lock(&state_mutex); if (current_state == STATE_STOPPING) { pthread_mutex_unlock(&state_mutex); break; } else if (current_state == STATE_SUSPENDED) { pthread_mutex_unlock(&state_mutex); usleep(500); continue; } pthread_mutex_unlock(&state_mutex); len = filbuff_cb(buffer, cb_data); pthread_mutex_lock(&outstream_mutex); if (!stream_out) { pthread_mutex_unlock(&outstream_mutex); break; } w_len = stream_out->write(stream_out, buffer, buffer_size); pthread_mutex_unlock(&outstream_mutex); } while (len && w_len > 0); if (in) fclose(in); pthread_mutex_lock(&state_mutex); current_state = STATE_STOPPED; pthread_mutex_unlock(&state_mutex); haltest_info("Done playing.\n"); return NULL; } static void play_p(int argc, const char **argv) { const char *fname = NULL; FILE *in = NULL; RETURN_IF_NULL(if_audio); RETURN_IF_NULL(stream_out); if (argc < 3) { haltest_error("Invalid audio file path.\n"); haltest_info("Using sound generator.\n"); } else { fname = argv[2]; in = fopen(fname, "r"); if (in == NULL) { haltest_error("Cannot open file: %s\n", fname); return; } haltest_info("Playing file: %s\n", fname); } if (buffer_size == 0) { haltest_error("Invalid buffer size. Was stream_out opened?\n"); goto fail; } pthread_mutex_lock(&state_mutex); if (current_state != STATE_STOPPED) { haltest_error("Already playing or stream suspended!\n"); pthread_mutex_unlock(&state_mutex); goto fail; } pthread_mutex_unlock(&state_mutex); if (pthread_create(&play_thread, NULL, playback_thread, in) != 0) { haltest_error("Cannot create playback thread!\n"); goto fail; } return; fail: if (in) fclose(in); } static void stop_p(int argc, const char **argv) { pthread_mutex_lock(&state_mutex); if (current_state == STATE_STOPPED || current_state == STATE_STOPPING) { pthread_mutex_unlock(&state_mutex); return; } current_state = STATE_STOPPING; pthread_mutex_unlock(&state_mutex); pthread_mutex_lock(&outstream_mutex); stream_out->common.standby(&stream_out->common); pthread_mutex_unlock(&outstream_mutex); } static void open_output_stream_p(int argc, const char **argv) { int err; RETURN_IF_NULL(if_audio); pthread_mutex_lock(&state_mutex); if (current_state == STATE_PLAYING) { haltest_error("Already playing!\n"); pthread_mutex_unlock(&state_mutex); return; } pthread_mutex_unlock(&state_mutex); #if ANDROID_VERSION >= PLATFORM_VER(5, 0, 0) err = if_audio->open_output_stream(if_audio, 0, AUDIO_DEVICE_OUT_ALL_A2DP, AUDIO_OUTPUT_FLAG_NONE, NULL, &stream_out, NULL); #else err = if_audio->open_output_stream(if_audio, 0, AUDIO_DEVICE_OUT_ALL_A2DP, AUDIO_OUTPUT_FLAG_NONE, NULL, &stream_out); #endif if (err < 0) { haltest_error("open output stream returned %d\n", err); return; } buffer_size = stream_out->common.get_buffer_size(&stream_out->common); if (buffer_size == 0) haltest_error("Invalid buffer size received!\n"); else haltest_info("Using buffer size: %zu\n", buffer_size); } static void close_output_stream_p(int argc, const char **argv) { RETURN_IF_NULL(if_audio); RETURN_IF_NULL(stream_out); stop_p(argc, argv); haltest_info("Waiting for playback thread...\n"); pthread_join(play_thread, NULL); if_audio->close_output_stream(if_audio, stream_out); stream_out = NULL; buffer_size = 0; } static void cleanup_p(int argc, const char **argv) { int err; RETURN_IF_NULL(if_audio); pthread_mutex_lock(&state_mutex); if (current_state != STATE_STOPPED) { pthread_mutex_unlock(&state_mutex); close_output_stream_p(0, NULL); } else { pthread_mutex_unlock(&state_mutex); } err = audio_hw_device_close(if_audio); if (err < 0) { haltest_error("audio_hw_device_close returned %d\n", err); return; } if_audio = NULL; } static void suspend_p(int argc, const char **argv) { RETURN_IF_NULL(if_audio); RETURN_IF_NULL(stream_out); pthread_mutex_lock(&state_mutex); if (current_state != STATE_PLAYING) { pthread_mutex_unlock(&state_mutex); return; } current_state = STATE_SUSPENDED; pthread_mutex_unlock(&state_mutex); pthread_mutex_lock(&outstream_mutex); stream_out->common.standby(&stream_out->common); pthread_mutex_unlock(&outstream_mutex); } static void resume_p(int argc, const char **argv) { RETURN_IF_NULL(if_audio); RETURN_IF_NULL(stream_out); pthread_mutex_lock(&state_mutex); if (current_state == STATE_SUSPENDED) current_state = STATE_PLAYING; pthread_mutex_unlock(&state_mutex); } static void get_latency_p(int argc, const char **argv) { RETURN_IF_NULL(if_audio); RETURN_IF_NULL(stream_out); haltest_info("Output audio stream latency: %d\n", stream_out->get_latency(stream_out)); } static void get_buffer_size_p(int argc, const char **argv) { RETURN_IF_NULL(if_audio); RETURN_IF_NULL(stream_out); haltest_info("Current output buffer size: %zu\n", stream_out->common.get_buffer_size(&stream_out->common)); } static void get_channels_p(int argc, const char **argv) { audio_channel_mask_t channels; RETURN_IF_NULL(if_audio); RETURN_IF_NULL(stream_out); channels = stream_out->common.get_channels(&stream_out->common); haltest_info("Channels: %s\n", audio_channel_mask_t2str(channels)); } static void get_format_p(int argc, const char **argv) { audio_format_t format; RETURN_IF_NULL(if_audio); RETURN_IF_NULL(stream_out); format = stream_out->common.get_format(&stream_out->common); haltest_info("Format: %s\n", audio_format_t2str(format)); } static void get_sample_rate_p(int argc, const char **argv) { RETURN_IF_NULL(if_audio); RETURN_IF_NULL(stream_out); haltest_info("Current sample rate: %d\n", stream_out->common.get_sample_rate(&stream_out->common)); } static void get_parameters_p(int argc, const char **argv) { const char *keystr; RETURN_IF_NULL(if_audio); RETURN_IF_NULL(stream_out); if (argc < 3) { haltest_info("No keys given.\n"); keystr = ""; } else { keystr = argv[2]; } haltest_info("Current parameters: %s\n", stream_out->common.get_parameters(&stream_out->common, keystr)); } static void set_parameters_p(int argc, const char **argv) { RETURN_IF_NULL(if_audio); RETURN_IF_NULL(stream_out); if (argc < 3) { haltest_error("No key=value; pairs given.\n"); return; } stream_out->common.set_parameters(&stream_out->common, argv[2]); } static void set_sample_rate_p(int argc, const char **argv) { RETURN_IF_NULL(if_audio); RETURN_IF_NULL(stream_out); if (argc < 3) return; stream_out->common.set_sample_rate(&stream_out->common, atoi(argv[2])); } static void init_check_p(int argc, const char **argv) { RETURN_IF_NULL(if_audio); haltest_info("Init check result: %d\n", if_audio->init_check(if_audio)); } static struct method methods[] = { STD_METHOD(init), STD_METHOD(cleanup), STD_METHOD(open_output_stream), STD_METHOD(close_output_stream), STD_METHODH(play, ""), STD_METHOD(stop), STD_METHOD(suspend), STD_METHOD(resume), STD_METHOD(get_latency), STD_METHOD(get_buffer_size), STD_METHOD(get_channels), STD_METHOD(get_format), STD_METHOD(get_sample_rate), STD_METHODH(get_parameters, ""), STD_METHODH(set_parameters, ""), STD_METHODH(set_sample_rate, ""), STD_METHOD(init_check), END_METHOD }; const struct interface audio_if = { .name = "audio", .methods = methods }; bluez-5.82/android/client/PaxHeaders/if-hf-client.c0000644000000000000000000000005014015011623017146 xustar0020 atime=1743516867 20 ctime=1743591279 bluez-5.82/android/client/if-hf-client.c0000644000000000000000000004454714015011623016645 0ustar00rootroot// SPDX-License-Identifier: Apache-2.0 /* * Copyright (C) 2014 Intel Corporation * */ #define _GNU_SOURCE #include "if-main.h" #include "../hal-utils.h" const bthf_client_interface_t *if_hf_client = NULL; static char last_addr[MAX_ADDR_STR_LEN]; SINTMAP(bthf_client_connection_state_t, -1, "(unknown)") DELEMENT(BTHF_CLIENT_CONNECTION_STATE_DISCONNECTED), DELEMENT(BTHF_CLIENT_CONNECTION_STATE_CONNECTING), DELEMENT(BTHF_CLIENT_CONNECTION_STATE_CONNECTED), DELEMENT(BTHF_CLIENT_CONNECTION_STATE_SLC_CONNECTED), DELEMENT(BTHF_CLIENT_CONNECTION_STATE_DISCONNECTING), ENDMAP SINTMAP(bthf_client_audio_state_t, -1, "(unknown)") DELEMENT(BTHF_CLIENT_AUDIO_STATE_DISCONNECTED), DELEMENT(BTHF_CLIENT_AUDIO_STATE_CONNECTING), DELEMENT(BTHF_CLIENT_AUDIO_STATE_CONNECTED), DELEMENT(BTHF_CLIENT_AUDIO_STATE_CONNECTED_MSBC), ENDMAP SINTMAP(bthf_client_vr_state_t, -1, "(unknown)") DELEMENT(BTHF_CLIENT_VR_STATE_STOPPED), DELEMENT(BTHF_CLIENT_VR_STATE_STARTED), ENDMAP SINTMAP(bthf_client_network_state_t, -1, "(unknown)") DELEMENT(BTHF_CLIENT_NETWORK_STATE_NOT_AVAILABLE), DELEMENT(BTHF_CLIENT_NETWORK_STATE_AVAILABLE), ENDMAP SINTMAP(bthf_client_service_type_t, -1, "(unknown)") DELEMENT(BTHF_CLIENT_SERVICE_TYPE_HOME), DELEMENT(BTHF_CLIENT_SERVICE_TYPE_ROAMING), ENDMAP SINTMAP(bthf_client_call_t, -1, "(unknown)") DELEMENT(BTHF_CLIENT_CALL_NO_CALLS_IN_PROGRESS), DELEMENT(BTHF_CLIENT_CALL_CALLS_IN_PROGRESS), ENDMAP SINTMAP(bthf_client_callsetup_t, -1, "(unknown)") DELEMENT(BTHF_CLIENT_CALLSETUP_NONE), DELEMENT(BTHF_CLIENT_CALLSETUP_INCOMING), DELEMENT(BTHF_CLIENT_CALLSETUP_OUTGOING), DELEMENT(BTHF_CLIENT_CALLSETUP_ALERTING), ENDMAP SINTMAP(bthf_client_callheld_t, -1, "(unknown)") DELEMENT(BTHF_CLIENT_CALLHELD_NONE), DELEMENT(BTHF_CLIENT_CALLHELD_HOLD_AND_ACTIVE), DELEMENT(BTHF_CLIENT_CALLHELD_HOLD), ENDMAP SINTMAP(bthf_client_resp_and_hold_t, -1, "(unknown)") DELEMENT(BTHF_CLIENT_RESP_AND_HOLD_HELD), DELEMENT(BTRH_CLIENT_RESP_AND_HOLD_ACCEPT), DELEMENT(BTRH_CLIENT_RESP_AND_HOLD_REJECT), ENDMAP SINTMAP(bthf_client_call_direction_t, -1, "(unknown)") DELEMENT(BTHF_CLIENT_CALL_DIRECTION_OUTGOING), DELEMENT(BTHF_CLIENT_CALL_DIRECTION_INCOMING), ENDMAP SINTMAP(bthf_client_call_state_t, -1, "(unknown)") DELEMENT(BTHF_CLIENT_CALL_STATE_ACTIVE), DELEMENT(BTHF_CLIENT_CALL_STATE_HELD), DELEMENT(BTHF_CLIENT_CALL_STATE_DIALING), DELEMENT(BTHF_CLIENT_CALL_STATE_ALERTING), DELEMENT(BTHF_CLIENT_CALL_STATE_INCOMING), DELEMENT(BTHF_CLIENT_CALL_STATE_WAITING), DELEMENT(BTHF_CLIENT_CALL_STATE_HELD_BY_RESP_HOLD), ENDMAP SINTMAP(bthf_client_call_mpty_type_t, -1, "(unknown)") DELEMENT(BTHF_CLIENT_CALL_MPTY_TYPE_SINGLE), DELEMENT(BTHF_CLIENT_CALL_MPTY_TYPE_MULTI), ENDMAP SINTMAP(bthf_client_volume_type_t, -1, "(unknown)") DELEMENT(BTHF_CLIENT_VOLUME_TYPE_SPK), DELEMENT(BTHF_CLIENT_VOLUME_TYPE_MIC), ENDMAP SINTMAP(bthf_client_cmd_complete_t, -1, "(unknown)") DELEMENT(BTHF_CLIENT_CMD_COMPLETE_OK), DELEMENT(BTHF_CLIENT_CMD_COMPLETE_ERROR), DELEMENT(BTHF_CLIENT_CMD_COMPLETE_ERROR_NO_CARRIER), DELEMENT(BTHF_CLIENT_CMD_COMPLETE_ERROR_BUSY), DELEMENT(BTHF_CLIENT_CMD_COMPLETE_ERROR_NO_ANSWER), DELEMENT(BTHF_CLIENT_CMD_COMPLETE_ERROR_DELAYED), DELEMENT(BTHF_CLIENT_CMD_COMPLETE_ERROR_BLACKLISTED), DELEMENT(BTHF_CLIENT_CMD_COMPLETE_ERROR_CME), ENDMAP SINTMAP(bthf_client_subscriber_service_type_t, -1, "(unknown)") DELEMENT(BTHF_CLIENT_SERVICE_UNKNOWN), DELEMENT(BTHF_CLIENT_SERVICE_VOICE), DELEMENT(BTHF_CLIENT_SERVICE_FAX), ENDMAP SINTMAP(bthf_client_in_band_ring_state_t, -1, "(unknown)") DELEMENT(BTHF_CLIENT_IN_BAND_RINGTONE_NOT_PROVIDED), DELEMENT(BTHF_CLIENT_IN_BAND_RINGTONE_PROVIDED), ENDMAP SINTMAP(bthf_client_call_action_t, -1, "(unknown)") DELEMENT(BTHF_CLIENT_CALL_ACTION_CHLD_0), DELEMENT(BTHF_CLIENT_CALL_ACTION_CHLD_1), DELEMENT(BTHF_CLIENT_CALL_ACTION_CHLD_2), DELEMENT(BTHF_CLIENT_CALL_ACTION_CHLD_3), DELEMENT(BTHF_CLIENT_CALL_ACTION_CHLD_4), DELEMENT(BTHF_CLIENT_CALL_ACTION_CHLD_1x), DELEMENT(BTHF_CLIENT_CALL_ACTION_CHLD_2x), DELEMENT(BTHF_CLIENT_CALL_ACTION_ATA), DELEMENT(BTHF_CLIENT_CALL_ACTION_CHUP), DELEMENT(BTHF_CLIENT_CALL_ACTION_BTRH_0), DELEMENT(BTHF_CLIENT_CALL_ACTION_BTRH_1), DELEMENT(BTHF_CLIENT_CALL_ACTION_BTRH_2), ENDMAP /* Callbacks */ static char features_str[512]; static const char *pear_features_t2str(int feat) { memset(features_str, 0, sizeof(features_str)); sprintf(features_str, "BTHF_CLIENT_PEER_FEAT_3WAY: %s,\n" "BTHF_CLIENT_PEER_FEAT_ECNR: %s,\n" "BTHF_CLIENT_PEER_FEAT_VREC: %s,\n" "BTHF_CLIENT_PEER_FEAT_INBAND: %s,\n" "BTHF_CLIENT_PEER_FEAT_VTAG: %s,\n" "BTHF_CLIENT_PEER_FEAT_REJECT: %s,\n" "BTHF_CLIENT_PEER_FEAT_ECS: %s,\n" "BTHF_CLIENT_PEER_FEAT_ECC: %s,\n" "BTHF_CLIENT_PEER_FEAT_EXTERR: %s,\n" "BTHF_CLIENT_PEER_FEAT_CODEC: %s,\n", feat & BTHF_CLIENT_PEER_FEAT_3WAY ? "True" : "False", feat & BTHF_CLIENT_PEER_FEAT_ECNR ? "True" : "False", feat & BTHF_CLIENT_PEER_FEAT_VREC ? "True" : "False", feat & BTHF_CLIENT_PEER_FEAT_INBAND ? "True" : "False", feat & BTHF_CLIENT_PEER_FEAT_VTAG ? "True" : "False", feat & BTHF_CLIENT_PEER_FEAT_REJECT ? "True" : "False", feat & BTHF_CLIENT_PEER_FEAT_ECS ? "True" : "False", feat & BTHF_CLIENT_PEER_FEAT_ECC ? "True" : "False", feat & BTHF_CLIENT_PEER_FEAT_EXTERR ? "True" : "False", feat & BTHF_CLIENT_PEER_FEAT_CODEC ? "True" : "False"); return features_str; } static const char *chld_features_t2str(int feat) { memset(features_str, 0, sizeof(features_str)); sprintf(features_str, "BTHF_CLIENT_CHLD_FEAT_REL: %s,\n" "BTHF_CLIENT_CHLD_FEAT_REL_ACC: %s,\n" "BTHF_CLIENT_CHLD_FEAT_REL_X: %s,\n" "BTHF_CLIENT_CHLD_FEAT_HOLD_ACC: %s,\n" "BTHF_CLIENT_CHLD_FEAT_PRIV_X: %s,\n" "BTHF_CLIENT_CHLD_FEAT_MERGE: %s,\n" "BTHF_CLIENT_CHLD_FEAT_MERGE_DETACH: %s,\n", feat & BTHF_CLIENT_CHLD_FEAT_REL ? "True" : "False", feat & BTHF_CLIENT_CHLD_FEAT_REL_ACC ? "True" : "False", feat & BTHF_CLIENT_CHLD_FEAT_REL_X ? "True" : "False", feat & BTHF_CLIENT_CHLD_FEAT_HOLD_ACC ? "True" : "False", feat & BTHF_CLIENT_CHLD_FEAT_PRIV_X ? "True" : "False", feat & BTHF_CLIENT_CHLD_FEAT_MERGE ? "True" : "False", feat & BTHF_CLIENT_CHLD_FEAT_MERGE_DETACH ? "True" : "False"); return features_str; } /* Callback for connection state change. */ static void hf_client_connection_state_callback( bthf_client_connection_state_t state, unsigned int peer_feat, unsigned int chld_feat, bt_bdaddr_t *bd_addr) { haltest_info("%s: state=%s bd_addr=%s\n", __func__, bthf_client_connection_state_t2str(state), bt_bdaddr_t2str(bd_addr, last_addr)); if (state != BTHF_CLIENT_CONNECTION_STATE_CONNECTED) return; haltest_info("\tpeer_features%s\n", pear_features_t2str(peer_feat)); haltest_info("\tchld_feat=%s\n", chld_features_t2str(chld_feat)); } /* Callback for audio connection state change. */ static void hf_client_audio_state_callback(bthf_client_audio_state_t state, bt_bdaddr_t *bd_addr) { haltest_info("%s: state=%s bd_addr=%s\n", __func__, bthf_client_audio_state_t2str(state), bt_bdaddr_t2str(bd_addr, last_addr)); } /* Callback for VR connection state change. */ static void hf_client_vr_cmd_callback(bthf_client_vr_state_t state) { haltest_info("%s: vr_state=%s\n", __func__, bthf_client_vr_state_t2str(state)); } /* Callback for network state change */ static void hf_client_network_state_callback(bthf_client_network_state_t state) { haltest_info("%s: network_state=%s\n", __func__, bthf_client_network_state_t2str(state)); } /* Callback for network roaming status change */ static void hf_client_network_roaming_callback(bthf_client_service_type_t type) { haltest_info("%s: service_type=%s\n", __func__, bthf_client_service_type_t2str(type)); } /* Callback for signal strength indication */ static void hf_client_network_signal_callback(int signal_strength) { haltest_info("%s: signal strength=%d\n", __func__, signal_strength); } /* Callback for battery level indication */ static void hf_client_battery_level_callback(int battery_level) { haltest_info("%s: battery_lvl=%d\n", __func__, battery_level); } /* Callback for current operator name */ static void hf_client_current_operator_callback(const char *name) { haltest_info("%s: operator_name=%s\n", __func__, name); } /* Callback for call indicator */ static void hf_client_call_callback(bthf_client_call_t call) { haltest_info("%s: call_state=%s\n", __func__, bthf_client_call_t2str(call)); } /* Callback for callsetup indicator */ static void hf_client_callsetup_callback(bthf_client_callsetup_t callsetup) { haltest_info("%s: callsetup=%s\n", __func__, bthf_client_callsetup_t2str(callsetup)); } /* Callback for callheld indicator */ static void hf_client_callheld_callback(bthf_client_callheld_t callheld) { haltest_info("%s: callheld=%s\n", __func__, bthf_client_callheld_t2str(callheld)); } /* Callback for response and hold */ static void hf_client_resp_and_hold_callback( bthf_client_resp_and_hold_t resp_and_hold) { haltest_info("%s: resp_and_hold=%s\n", __func__, bthf_client_resp_and_hold_t2str(resp_and_hold)); } /* Callback for Calling Line Identification notification */ static void hf_client_clip_callback(const char *number) { haltest_info("%s: number=%s\n", __func__, number); } /* Callback for Call Waiting notification */ static void hf_client_call_waiting_callback(const char *number) { haltest_info("%s: number=%s\n", __func__, number); } /* Callback for listing current calls. Can be called multiple time. */ static void hf_client_current_calls_callback(int index, bthf_client_call_direction_t dir, bthf_client_call_state_t state, bthf_client_call_mpty_type_t mpty, const char *number) { haltest_info("%s: index=%d, direction=%s, state=%s, m_party=%s\n", __func__, index, bthf_client_call_direction_t2str(dir), bthf_client_call_state_t2str(state), bthf_client_call_mpty_type_t2str(mpty)); if (number) haltest_info("%s: number=%s\n", __func__, number); } /* Callback for audio volume change */ static void hf_client_volume_change_callback(bthf_client_volume_type_t type, int volume) { haltest_info("%s: vol_type=%s, value=%d\n", __func__, bthf_client_volume_type_t2str(type), volume); } /* Callback for command complete event */ static void hf_client_cmd_complete_callback(bthf_client_cmd_complete_t type, int cme) { haltest_info("%s: type=%s, cme=%d\n", __func__, bthf_client_cmd_complete_t2str(type), cme); } /* Callback for subscriber information */ static void hf_client_subscriber_info_callback(const char *name, bthf_client_subscriber_service_type_t type) { haltest_info("%s: name=%s, type=%s\n", __func__, name, bthf_client_subscriber_service_type_t2str(type)); } /* Callback for in-band ring tone settings */ static void hf_client_in_band_ring_tone_callback( bthf_client_in_band_ring_state_t state) { haltest_info("%s: state=%s\n", __func__, bthf_client_in_band_ring_state_t2str(state)); } /* Callback for requested number from AG */ static void hf_client_last_voice_tag_number_callback(const char *number) { haltest_info("%s: number=%s\n", __func__, number); } /* Callback for sending ring indication to app */ static void hf_client_ring_indication_callback(void) { haltest_info("%s\n", __func__); } static bthf_client_callbacks_t hf_client_cbacks = { .size = sizeof(hf_client_cbacks), .connection_state_cb = hf_client_connection_state_callback, .audio_state_cb = hf_client_audio_state_callback, .vr_cmd_cb = hf_client_vr_cmd_callback, .network_state_cb = hf_client_network_state_callback, .network_roaming_cb = hf_client_network_roaming_callback, .network_signal_cb = hf_client_network_signal_callback, .battery_level_cb = hf_client_battery_level_callback, .current_operator_cb = hf_client_current_operator_callback, .call_cb = hf_client_call_callback, .callsetup_cb = hf_client_callsetup_callback, .callheld_cb = hf_client_callheld_callback, .resp_and_hold_cb = hf_client_resp_and_hold_callback, .clip_cb = hf_client_clip_callback, .call_waiting_cb = hf_client_call_waiting_callback, .current_calls_cb = hf_client_current_calls_callback, .volume_change_cb = hf_client_volume_change_callback, .cmd_complete_cb = hf_client_cmd_complete_callback, .subscriber_info_cb = hf_client_subscriber_info_callback, .in_band_ring_tone_cb = hf_client_in_band_ring_tone_callback, .last_voice_tag_number_callback = hf_client_last_voice_tag_number_callback, .ring_indication_cb = hf_client_ring_indication_callback, }; /* init */ static void init_p(int argc, const char **argv) { RETURN_IF_NULL(if_hf_client); EXEC(if_hf_client->init, &hf_client_cbacks); } static void connect_c(int argc, const char **argv, enum_func *enum_func, void **user) { if (argc == 3) { *user = NULL; *enum_func = enum_devices; } } /* connect to audio gateway */ static void connect_p(int argc, const char **argv) { bt_bdaddr_t addr; RETURN_IF_NULL(if_hf_client); VERIFY_ADDR_ARG(2, &addr); EXEC(if_hf_client->connect, &addr); } /* * This completion function will be used for several methods * returning recently connected address */ static void connected_addr_c(int argc, const char **argv, enum_func *enum_func, void **user) { if (argc == 3) { *user = last_addr; *enum_func = enum_one_string; } } /* Map completion to connected_addr_c */ #define disconnect_c connected_addr_c /* disconnect from audio gateway */ static void disconnect_p(int argc, const char **argv) { bt_bdaddr_t addr; RETURN_IF_NULL(if_hf_client); VERIFY_ADDR_ARG(2, &addr); EXEC(if_hf_client->disconnect, &addr); } static void connect_audio_c(int argc, const char **argv, enum_func *enum_func, void **user) { if (argc == 3) { *user = NULL; *enum_func = enum_devices; } } /* create an audio connection */ static void connect_audio_p(int argc, const char **argv) { bt_bdaddr_t addr; RETURN_IF_NULL(if_hf_client); VERIFY_ADDR_ARG(2, &addr); EXEC(if_hf_client->connect_audio, &addr); } /* Map completion to connected_addr_c */ #define disconnect_audio_c connected_addr_c /* close the audio connection */ static void disconnect_audio_p(int argc, const char **argv) { bt_bdaddr_t addr; RETURN_IF_NULL(if_hf_client); VERIFY_ADDR_ARG(2, &addr); EXEC(if_hf_client->disconnect_audio, &addr); } /* start voice recognition */ static void start_voice_recognition_p(int argc, const char **argv) { RETURN_IF_NULL(if_hf_client); EXEC(if_hf_client->start_voice_recognition); } /* stop voice recognition */ static void stop_voice_recognition_p(int argc, const char **argv) { RETURN_IF_NULL(if_hf_client); EXEC(if_hf_client->stop_voice_recognition); } static void volume_control_c(int argc, const char **argv, enum_func *enum_func, void **user) { if (argc == 3) { *user = TYPE_ENUM(bthf_client_volume_type_t); *enum_func = enum_defines; } } /* volume control */ static void volume_control_p(int argc, const char **argv) { bthf_client_volume_type_t type; int volume; RETURN_IF_NULL(if_hf_client); /* volume type */ if (argc <= 2) { haltest_error("No volume type specified\n"); return; } type = str2bthf_client_volume_type_t(argv[2]); /* volume */ if (argc <= 3) { haltest_error("No volume specified\n"); return; } volume = atoi(argv[3]); EXEC(if_hf_client->volume_control, type, volume); } /* place a call with number a number */ static void dial_p(int argc, const char **argv) { RETURN_IF_NULL(if_hf_client); /* number string */ if (argc <= 2) { haltest_info("Number not specified. Redial\n"); EXEC(if_hf_client->dial, NULL); return; } EXEC(if_hf_client->dial, argv[2]); } /* place a call with number specified by location (speed dial) */ static void dial_memory_p(int argc, const char **argv) { RETURN_IF_NULL(if_hf_client); /* memory index */ if (argc <= 2) { haltest_error("No memory index specified\n"); return; } EXEC(if_hf_client->dial_memory, atoi(argv[2])); } static void handle_call_action_c(int argc, const char **argv, enum_func *enum_func, void **user) { if (argc == 3) { *user = TYPE_ENUM(bthf_client_call_action_t); *enum_func = enum_defines; } } /* perform specified call related action */ static void handle_call_action_p(int argc, const char **argv) { bthf_client_call_action_t action; int index = 0; RETURN_IF_NULL(if_hf_client); /* action */ if (argc <= 2) { haltest_error("No action specified\n"); return; } action = str2bthf_client_call_action_t(argv[2]); /* call index */ if (action == BTHF_CLIENT_CALL_ACTION_CHLD_1x || action == BTHF_CLIENT_CALL_ACTION_CHLD_2x) { if (argc <= 3) { haltest_error("No call index specified\n"); return; } index = atoi(argv[3]); } EXEC(if_hf_client->handle_call_action, action, index); } /* query list of current calls */ static void query_current_calls_p(int argc, const char **argv) { RETURN_IF_NULL(if_hf_client); EXEC(if_hf_client->query_current_calls); } /* query name of current selected operator */ static void query_current_operator_name_p(int argc, const char **argv) { RETURN_IF_NULL(if_hf_client); EXEC(if_hf_client->query_current_operator_name); } /* Retrieve subscriber information */ static void retrieve_subscriber_info_p(int argc, const char **argv) { RETURN_IF_NULL(if_hf_client); EXEC(if_hf_client->retrieve_subscriber_info); } /* Send DTMF code*/ static void send_dtmf_p(int argc, const char **argv) { RETURN_IF_NULL(if_hf_client); EXEC(if_hf_client->send_dtmf, *argv[2]); } /* Request a phone number from AG corresponding to last voice tag recorded */ static void request_last_voice_tag_number_p(int argc, const char **argv) { RETURN_IF_NULL(if_hf_client); EXEC(if_hf_client->request_last_voice_tag_number); } /* Closes the interface. */ static void cleanup_p(int argc, const char **argv) { RETURN_IF_NULL(if_hf_client); EXECV(if_hf_client->cleanup); if_hf_client = NULL; } static struct method methods[] = { STD_METHOD(init), STD_METHODCH(connect, ""), STD_METHODCH(disconnect, ""), STD_METHODCH(connect_audio, ""), STD_METHODCH(disconnect_audio, ""), STD_METHOD(start_voice_recognition), STD_METHOD(stop_voice_recognition), STD_METHODCH(volume_control, " "), STD_METHODH(dial, ""), STD_METHODH(dial_memory, ""), STD_METHODCH(handle_call_action, " "), STD_METHOD(query_current_calls), STD_METHOD(query_current_operator_name), STD_METHOD(retrieve_subscriber_info), STD_METHODH(send_dtmf, ""), STD_METHOD(request_last_voice_tag_number), STD_METHOD(cleanup), END_METHOD }; const struct interface hf_client_if = { .name = "handsfree_client", .methods = methods }; bluez-5.82/android/client/PaxHeaders/if-hh.c0000644000000000000000000000005014015011623015674 xustar0020 atime=1743516867 20 ctime=1743591279 bluez-5.82/android/client/if-hh.c0000644000000000000000000002362314015011623015363 0ustar00rootroot// SPDX-License-Identifier: Apache-2.0 /* * Copyright (C) 2013 Intel Corporation * */ #define _GNU_SOURCE #include #include #include #include #include #include "if-main.h" #include "pollhandler.h" #include "../hal-utils.h" const bthh_interface_t *if_hh = NULL; SINTMAP(bthh_protocol_mode_t, -1, "(unknown)") DELEMENT(BTHH_REPORT_MODE), DELEMENT(BTHH_BOOT_MODE), DELEMENT(BTHH_UNSUPPORTED_MODE), ENDMAP SINTMAP(bthh_report_type_t, -1, "(unknown)") DELEMENT(BTHH_INPUT_REPORT), DELEMENT(BTHH_OUTPUT_REPORT), DELEMENT(BTHH_FEATURE_REPORT), ENDMAP SINTMAP(bthh_connection_state_t, -1, "(unknown)") DELEMENT(BTHH_CONN_STATE_CONNECTED), DELEMENT(BTHH_CONN_STATE_CONNECTING), DELEMENT(BTHH_CONN_STATE_DISCONNECTED), DELEMENT(BTHH_CONN_STATE_DISCONNECTING), DELEMENT(BTHH_CONN_STATE_FAILED_MOUSE_FROM_HOST), DELEMENT(BTHH_CONN_STATE_FAILED_KBD_FROM_HOST), DELEMENT(BTHH_CONN_STATE_FAILED_TOO_MANY_DEVICES), DELEMENT(BTHH_CONN_STATE_FAILED_NO_BTHID_DRIVER), DELEMENT(BTHH_CONN_STATE_FAILED_GENERIC), DELEMENT(BTHH_CONN_STATE_UNKNOWN), ENDMAP SINTMAP(bthh_status_t, -1, "(unknown)") DELEMENT(BTHH_OK), DELEMENT(BTHH_HS_HID_NOT_READY), DELEMENT(BTHH_HS_INVALID_RPT_ID), DELEMENT(BTHH_HS_TRANS_NOT_SPT), DELEMENT(BTHH_HS_INVALID_PARAM), DELEMENT(BTHH_HS_ERROR), DELEMENT(BTHH_ERR), DELEMENT(BTHH_ERR_SDP), DELEMENT(BTHH_ERR_PROTO), DELEMENT(BTHH_ERR_DB_FULL), DELEMENT(BTHH_ERR_TOD_UNSPT), DELEMENT(BTHH_ERR_NO_RES), DELEMENT(BTHH_ERR_AUTH_FAILED), DELEMENT(BTHH_ERR_HDL), ENDMAP static char connected_device_addr[MAX_ADDR_STR_LEN]; /* * Callback for connection state change. * state will have one of the values from bthh_connection_state_t */ static void connection_state_cb(bt_bdaddr_t *bd_addr, bthh_connection_state_t state) { char addr[MAX_ADDR_STR_LEN]; haltest_info("%s: bd_addr=%s connection_state=%s\n", __func__, bt_bdaddr_t2str(bd_addr, addr), bthh_connection_state_t2str(state)); if (state == BTHH_CONN_STATE_CONNECTED) strcpy(connected_device_addr, addr); } /* * Callback for virtual unplug api. * the status of the virtual unplug */ static void virtual_unplug_cb(bt_bdaddr_t *bd_addr, bthh_status_t hh_status) { char addr[MAX_ADDR_STR_LEN]; haltest_info("%s: bd_addr=%s hh_status=%s\n", __func__, bt_bdaddr_t2str(bd_addr, addr), bthh_status_t2str(hh_status)); } /* Callback for Android 5.0 handshake api. */ #if ANDROID_VERSION >= PLATFORM_VER(5, 0, 0) static void handshake_cb(bt_bdaddr_t *bd_addr, bthh_status_t hh_status) { char addr[MAX_ADDR_STR_LEN]; haltest_info("%s: bd_addr=%s hh_status=%s\n", __func__, bt_bdaddr_t2str(bd_addr, addr), bthh_status_t2str(hh_status)); } #endif /* * Callback for get hid info * hid_info will contain attr_mask, sub_class, app_id, vendor_id, product_id, * version, ctry_code, len */ static void hid_info_cb(bt_bdaddr_t *bd_addr, bthh_hid_info_t hid_info) { char addr[MAX_ADDR_STR_LEN]; /* TODO: bluedroid does not seem to ever call this callback */ haltest_info("%s: bd_addr=%s\n", __func__, bt_bdaddr_t2str(bd_addr, addr)); } /* * Callback for get/set protocol api. * the protocol mode is one of the value from bthh_protocol_mode_t */ static void protocol_mode_cb(bt_bdaddr_t *bd_addr, bthh_status_t hh_status, bthh_protocol_mode_t mode) { char addr[MAX_ADDR_STR_LEN]; haltest_info("%s: bd_addr=%s hh_status=%s mode=%s\n", __func__, bt_bdaddr_t2str(bd_addr, addr), bthh_status_t2str(hh_status), bthh_protocol_mode_t2str(mode)); } /* Callback for get/set_idle_time api. */ static void idle_time_cb(bt_bdaddr_t *bd_addr, bthh_status_t hh_status, int idle_rate) { char addr[MAX_ADDR_STR_LEN]; haltest_info("%s: bd_addr=%s hh_status=%s idle_rate=%d\n", __func__, bt_bdaddr_t2str(bd_addr, addr), bthh_status_t2str(hh_status), idle_rate); } /* * Callback for get report api. * if status is ok rpt_data contains the report data */ static void get_report_cb(bt_bdaddr_t *bd_addr, bthh_status_t hh_status, uint8_t *rpt_data, int rpt_size) { char addr[MAX_ADDR_STR_LEN]; /* TODO: print actual report */ haltest_info("%s: bd_addr=%s hh_status=%s rpt_size=%d\n", __func__, bt_bdaddr_t2str(bd_addr, addr), bthh_status_t2str(hh_status), rpt_size); } static bthh_callbacks_t bthh_callbacks = { .size = sizeof(bthh_callbacks), .connection_state_cb = connection_state_cb, .hid_info_cb = hid_info_cb, .protocol_mode_cb = protocol_mode_cb, .idle_time_cb = idle_time_cb, .get_report_cb = get_report_cb, .virtual_unplug_cb = virtual_unplug_cb, #if ANDROID_VERSION >= PLATFORM_VER(5, 0, 0) .handshake_cb = handshake_cb #endif }; /* init */ static void init_p(int argc, const char **argv) { RETURN_IF_NULL(if_hh); EXEC(if_hh->init, &bthh_callbacks); } /* connect */ static void connect_c(int argc, const char **argv, enum_func *enum_func, void **user) { if (argc == 3) { *user = (void *) connected_device_addr; *enum_func = enum_one_string; } } static void connect_p(int argc, const char **argv) { bt_bdaddr_t addr; RETURN_IF_NULL(if_hh); VERIFY_ADDR_ARG(2, &addr); EXEC(if_hh->connect, &addr); } /* disconnect */ /* Same completion as connect_c */ #define disconnect_c connect_c static void disconnect_p(int argc, const char **argv) { bt_bdaddr_t addr; RETURN_IF_NULL(if_hh); VERIFY_ADDR_ARG(2, &addr); EXEC(if_hh->disconnect, &addr); } /* virtual_unplug */ /* Same completion as connect_c */ #define virtual_unplug_c connect_c static void virtual_unplug_p(int argc, const char **argv) { bt_bdaddr_t addr; RETURN_IF_NULL(if_hh); VERIFY_ADDR_ARG(2, &addr); EXEC(if_hh->virtual_unplug, &addr); } /* set_info */ /* Same completion as connect_c */ #define set_info_c connect_c static void set_info_p(int argc, const char **argv) { bt_bdaddr_t addr; bthh_hid_info_t hid_info; RETURN_IF_NULL(if_hh); VERIFY_ADDR_ARG(2, &addr); memset(&hid_info, 0, sizeof(hid_info)); /* * This command is intentionally not supported. See comment from * bt_hid_info() in android/hidhost.c */ EXEC(if_hh->set_info, &addr, hid_info); } /* get_protocol */ static void get_protocol_c(int argc, const char **argv, enum_func *enum_func, void **user) { if (argc == 3) { *user = connected_device_addr; *enum_func = enum_one_string; } else if (argc == 4) { *user = TYPE_ENUM(bthh_protocol_mode_t); *enum_func = enum_defines; } } static void get_protocol_p(int argc, const char **argv) { bt_bdaddr_t addr; bthh_protocol_mode_t protocolMode; RETURN_IF_NULL(if_hh); VERIFY_ADDR_ARG(2, &addr); if (argc < 4) { haltest_error("No protocol mode specified\n"); return; } protocolMode = str2bthh_protocol_mode_t(argv[3]); EXEC(if_hh->get_protocol, &addr, protocolMode); } /* set_protocol */ /* Same completion as get_protocol_c */ #define set_protocol_c get_protocol_c static void set_protocol_p(int argc, const char **argv) { bt_bdaddr_t addr; bthh_protocol_mode_t protocolMode; RETURN_IF_NULL(if_hh); VERIFY_ADDR_ARG(2, &addr); if (argc < 4) { haltest_error("No protocol mode specified\n"); return; } protocolMode = str2bthh_protocol_mode_t(argv[3]); EXEC(if_hh->set_protocol, &addr, protocolMode); } /* get_report */ static void get_report_c(int argc, const char **argv, enum_func *enum_func, void **user) { if (argc == 3) { *user = connected_device_addr; *enum_func = enum_one_string; } else if (argc == 4) { *user = TYPE_ENUM(bthh_report_type_t); *enum_func = enum_defines; } } static void get_report_p(int argc, const char **argv) { bt_bdaddr_t addr; bthh_report_type_t reportType; uint8_t reportId; int bufferSize; RETURN_IF_NULL(if_hh); VERIFY_ADDR_ARG(2, &addr); if (argc < 4) { haltest_error("No report type specified\n"); return; } reportType = str2bthh_report_type_t(argv[3]); if (argc < 5) { haltest_error("No reportId specified\n"); return; } reportId = (uint8_t) atoi(argv[4]); if (argc < 6) { haltest_error("No bufferSize specified\n"); return; } bufferSize = atoi(argv[5]); EXEC(if_hh->get_report, &addr, reportType, reportId, bufferSize); } /* set_report */ static void set_report_c(int argc, const char **argv, enum_func *enum_func, void **user) { if (argc == 3) { *user = connected_device_addr; *enum_func = enum_one_string; } else if (argc == 4) { *user = TYPE_ENUM(bthh_report_type_t); *enum_func = enum_defines; } } static void set_report_p(int argc, const char **argv) { bt_bdaddr_t addr; bthh_report_type_t reportType; RETURN_IF_NULL(if_hh); VERIFY_ADDR_ARG(2, &addr); if (argc <= 3) { haltest_error("No report type specified\n"); return; } reportType = str2bthh_report_type_t(argv[3]); if (argc <= 4) { haltest_error("No report specified\n"); return; } EXEC(if_hh->set_report, &addr, reportType, (char *) argv[4]); } /* send_data */ static void send_data_c(int argc, const char **argv, enum_func *enum_func, void **user) { if (argc == 3) { *user = connected_device_addr; *enum_func = enum_one_string; } } static void send_data_p(int argc, const char **argv) { bt_bdaddr_t addr; RETURN_IF_NULL(if_hh); VERIFY_ADDR_ARG(2, &addr); if (argc <= 3) { haltest_error("No data to send specified\n"); return; } EXEC(if_hh->send_data, &addr, (char *) argv[3]); } /* cleanup */ static void cleanup_p(int argc, const char **argv) { RETURN_IF_NULL(if_hh); EXECV(if_hh->cleanup); } /* Methods available in bthh_interface_t */ static struct method methods[] = { STD_METHOD(init), STD_METHODCH(connect, ""), STD_METHODCH(disconnect, ""), STD_METHODCH(virtual_unplug, ""), STD_METHODCH(set_info, ""), STD_METHODCH(get_protocol, " "), STD_METHODCH(set_protocol, " "), STD_METHODCH(get_report, " "), STD_METHODCH(set_report, " "), STD_METHODCH(send_data, " "), STD_METHOD(cleanup), END_METHOD }; const struct interface hh_if = { .name = "hidhost", .methods = methods }; bluez-5.82/android/client/PaxHeaders/if-sock.c0000644000000000000000000000005014015011623016234 xustar0020 atime=1743516867 20 ctime=1743591279 bluez-5.82/android/client/if-sock.c0000644000000000000000000001725714015011623015731 0ustar00rootroot// SPDX-License-Identifier: Apache-2.0 /* * Copyright (C) 2013 Intel Corporation * */ #define _GNU_SOURCE #include #include #include #include #include "if-main.h" #include "pollhandler.h" #include "../hal-utils.h" const btsock_interface_t *if_sock = NULL; SINTMAP(btsock_type_t, -1, "(unknown)") DELEMENT(BTSOCK_RFCOMM), DELEMENT(BTSOCK_SCO), DELEMENT(BTSOCK_L2CAP), ENDMAP #define MAX_LISTEN_FD 15 static int listen_fd[MAX_LISTEN_FD]; static int listen_fd_count; static const char * const uuids[] = { "00001101", "00001105", "0000112f", NULL }; /* * This function reads data from file descriptor and * prints it to the user */ static void receive_from_client(struct pollfd *pollfd) { char buf[16]; /* * Buffer for lines: * 41 42 43 20 20 00 31 32 00 07 04 00 00 00 00 00 ABC .12..... */ char outbuf[sizeof(buf) * 4 + 2]; int i; int ret; if (pollfd->revents & POLLHUP) { haltest_error("Disconnected fd=%d\n", pollfd->fd); poll_unregister_fd(pollfd->fd, receive_from_client); } else if (pollfd->revents & POLLIN) { haltest_info("receiving from client fd=%d\n", pollfd->fd); do { memset(outbuf, ' ', sizeof(outbuf)); outbuf[sizeof(outbuf) - 1] = 0; ret = recv(pollfd->fd, buf, sizeof(buf), MSG_DONTWAIT); for (i = 0; i < ret; ++i) sprintf(outbuf + i * 3, "%02X ", (unsigned) buf[i]); outbuf[i * 3] = ' '; for (i = 0; i < ret; ++i) sprintf(outbuf + 48 + i, "%c", (isprint(buf[i]) ? buf[i] : '.')); if (ret > 0) haltest_info("%s\n", outbuf); } while (ret > 0); } else { /* For now disconnect on all other events */ haltest_error("Poll event %x\n", pollfd->revents); poll_unregister_fd(pollfd->fd, receive_from_client); } } /* * This function read from fd socket information about * connected socket */ static void receive_sock_connect_signal(struct pollfd *pollfd) { sock_connect_signal_t cs; char addr_str[MAX_ADDR_STR_LEN]; if (pollfd->revents & POLLIN) { int ret; poll_unregister_fd(pollfd->fd, receive_sock_connect_signal); ret = read(pollfd->fd, &cs, sizeof(cs)); if (ret != sizeof(cs)) { haltest_info("Read on connect return %d\n", ret); return; } haltest_info("Connection to %s channel %d status=%d\n", bt_bdaddr_t2str(&cs.bd_addr, addr_str), cs.channel, cs.status); if (cs.status == 0) poll_register_fd(pollfd->fd, POLLIN, receive_from_client); } if (pollfd->revents & POLLHUP) { haltest_error("Disconnected fd=%d revents=0x%X\n", pollfd->fd, pollfd->revents); poll_unregister_fd(pollfd->fd, receive_sock_connect_signal); } } /* * This function read from fd socket information about * incoming connection and starts monitoring new connection * on file descriptor read from fd. */ static void read_accepted(int fd) { int ret; struct msghdr msg; struct iovec iv; char cmsgbuf[CMSG_SPACE(1)]; struct cmsghdr *cmsgptr; sock_connect_signal_t cs; int accepted_fd = -1; char addr_str[MAX_ADDR_STR_LEN]; memset(&msg, 0, sizeof(msg)); memset(&iv, 0, sizeof(iv)); memset(cmsgbuf, 0, sizeof(cmsgbuf)); iv.iov_base = &cs; iv.iov_len = sizeof(cs); msg.msg_iov = &iv; msg.msg_iovlen = 1; msg.msg_control = cmsgbuf; msg.msg_controllen = sizeof(cmsgbuf); do { ret = recvmsg(fd, &msg, MSG_NOSIGNAL); } while (ret < 0 && errno == EINTR); if (ret < 16 || (msg.msg_flags & (MSG_CTRUNC | MSG_OOB | MSG_ERRQUEUE)) != 0) haltest_error("Failed to accept connection\n"); for (cmsgptr = CMSG_FIRSTHDR(&msg); cmsgptr != NULL; cmsgptr = CMSG_NXTHDR(&msg, cmsgptr)) { int count; if (cmsgptr->cmsg_level != SOL_SOCKET || cmsgptr->cmsg_type != SCM_RIGHTS) continue; memcpy(&accepted_fd, CMSG_DATA(cmsgptr), sizeof(accepted_fd)); count = ((cmsgptr->cmsg_len - CMSG_LEN(0)) / sizeof(int)); if (count != 1) haltest_error("Failed to accept descriptors count=%d\n", count); break; } haltest_info("Incoming connection from %s channel %d status=%d fd=%d\n", bt_bdaddr_t2str(&cs.bd_addr, addr_str), cs.channel, cs.status, accepted_fd); poll_register_fd(accepted_fd, POLLIN, receive_from_client); } /* handles incoming connections on socket */ static void client_connected(struct pollfd *pollfd) { haltest_info("client connected %x\n", pollfd->revents); if (pollfd->revents & POLLHUP) poll_unregister_fd(pollfd->fd, client_connected); else if (pollfd->revents & POLLIN) read_accepted(pollfd->fd); } /* listen */ static void listen_c(int argc, const char **argv, enum_func *enum_func, void **user) { if (argc == 3) { *user = TYPE_ENUM(btsock_type_t); *enum_func = enum_defines; } else if (argc == 5) { *user = (void *) uuids; *enum_func = enum_strings; } } static void listen_p(int argc, const char **argv) { btsock_type_t type; const char *service_name; bt_uuid_t service_uuid; int channel; int sock_fd = -1; int flags; RETURN_IF_NULL(if_sock); /* Socket type */ if (argc < 3) { haltest_error("No socket type specified\n"); return; } type = str2btsock_type_t(argv[2]); if ((int) type == -1) type = atoi(argv[2]); /* service name */ if (argc < 4) { haltest_error("No service name specified\n"); return; } service_name = argv[3]; /* uuid */ if (argc < 5) { haltest_error("No uuid specified\n"); return; } str2bt_uuid_t(argv[4], &service_uuid); /* channel */ channel = argc > 5 ? atoi(argv[5]) : 0; /* flags */ flags = argc > 6 ? atoi(argv[6]) : 0; if (listen_fd_count >= MAX_LISTEN_FD) { haltest_error("Max (%d) listening sockets exceeded\n", listen_fd_count); return; } EXEC(if_sock->listen, type, service_name, &service_uuid.uu[0], channel, &sock_fd, flags); if (sock_fd > 0) { int channel = 0; int ret = read(sock_fd, &channel, 4); if (ret != 4) haltest_info("Read channel failed\n"); haltest_info("Channel returned from first read %d\n", channel); listen_fd[listen_fd_count++] = sock_fd; poll_register_fd(sock_fd, POLLIN, client_connected); } } /* connect */ static void connect_c(int argc, const char **argv, enum_func *enum_func, void **user) { if (argc == 3) { *enum_func = enum_devices; } else if (argc == 4) { *user = TYPE_ENUM(btsock_type_t); *enum_func = enum_defines; } else if (argc == 5) { *user = (void *) uuids; *enum_func = enum_strings; } } static void connect_p(int argc, const char **argv) { bt_bdaddr_t addr; btsock_type_t type; bt_uuid_t uuid; int channel; int sock_fd = -1; int flags; /* Address */ if (argc <= 2) { haltest_error("No address specified\n"); return; } str2bt_bdaddr_t(argv[2], &addr); /* Socket type */ if (argc <= 3) { haltest_error("No socket type specified\n"); return; } type = str2btsock_type_t(argv[3]); if ((int) type == -1) type = atoi(argv[3]); /* uuid */ if (argc <= 4) { haltest_error("No uuid specified\n"); return; } str2bt_uuid_t(argv[4], &uuid); /* channel */ if (argc <= 5) { haltest_error("No channel specified\n"); return; } channel = atoi(argv[5]); /* flags */ flags = argc <= 6 ? 0 : atoi(argv[6]); RETURN_IF_NULL(if_sock); EXEC(if_sock->connect, &addr, type, &uuid.uu[0], channel, &sock_fd, flags); if (sock_fd > 0) { int channel = 0; int ret = read(sock_fd, &channel, 4); if (ret != 4) haltest_info("Read channel failed\n"); haltest_info("Channel returned from first read %d\n", channel); listen_fd[listen_fd_count++] = sock_fd; poll_register_fd(sock_fd, POLLIN, receive_sock_connect_signal); } } /* Methods available in btsock_interface_t */ static struct method methods[] = { STD_METHODCH(listen, " [] []"), STD_METHODCH(connect, " []"), END_METHOD }; const struct interface sock_if = { .name = "socket", .methods = methods }; bluez-5.82/android/client/PaxHeaders/if-av.c0000644000000000000000000000005014015011623015703 xustar0020 atime=1743516867 20 ctime=1743591279 bluez-5.82/android/client/if-av.c0000644000000000000000000000545014015011623015370 0ustar00rootroot// SPDX-License-Identifier: Apache-2.0 /* * Copyright (C) 2013 Intel Corporation * */ #define _GNU_SOURCE #include "if-main.h" #include "../hal-utils.h" const btav_interface_t *if_av = NULL; SINTMAP(btav_connection_state_t, -1, "(unknown)") DELEMENT(BTAV_CONNECTION_STATE_DISCONNECTED), DELEMENT(BTAV_CONNECTION_STATE_CONNECTING), DELEMENT(BTAV_CONNECTION_STATE_CONNECTED), DELEMENT(BTAV_CONNECTION_STATE_DISCONNECTING), ENDMAP SINTMAP(btav_audio_state_t, -1, "(unknown)") DELEMENT(BTAV_AUDIO_STATE_REMOTE_SUSPEND), DELEMENT(BTAV_AUDIO_STATE_STOPPED), DELEMENT(BTAV_AUDIO_STATE_STARTED), ENDMAP static char last_addr[MAX_ADDR_STR_LEN]; static void connection_state(btav_connection_state_t state, bt_bdaddr_t *bd_addr) { haltest_info("%s: connection_state=%s remote_bd_addr=%s\n", __func__, btav_connection_state_t2str(state), bt_bdaddr_t2str(bd_addr, last_addr)); } static void audio_state(btav_audio_state_t state, bt_bdaddr_t *bd_addr) { haltest_info("%s: audio_state=%s remote_bd_addr=%s\n", __func__, btav_audio_state_t2str(state), bt_bdaddr_t2str(bd_addr, last_addr)); } #if ANDROID_VERSION >= PLATFORM_VER(5, 0, 0) static void audio_config(bt_bdaddr_t *bd_addr, uint32_t sample_rate, uint8_t channel_count) { haltest_info("%s: remote_addr=%s\n sample_rate=%d\n channel_count=%d\n", __func__, bt_bdaddr_t2str(bd_addr, last_addr), sample_rate, channel_count); } #endif static btav_callbacks_t av_cbacks = { .size = sizeof(av_cbacks), .connection_state_cb = connection_state, .audio_state_cb = audio_state, #if ANDROID_VERSION >= PLATFORM_VER(5, 0, 0) .audio_config_cb = audio_config, #endif }; /* init */ static void init_p(int argc, const char **argv) { RETURN_IF_NULL(if_av); EXEC(if_av->init, &av_cbacks); } /* connect */ static void connect_c(int argc, const char **argv, enum_func *enum_func, void **user) { if (argc == 3) { *user = NULL; *enum_func = enum_devices; } } static void connect_p(int argc, const char **argv) { bt_bdaddr_t addr; RETURN_IF_NULL(if_av); VERIFY_ADDR_ARG(2, &addr); EXEC(if_av->connect, &addr); } /* disconnect */ static void disconnect_c(int argc, const char **argv, enum_func *enum_func, void **user) { if (argc == 3) { *user = last_addr; *enum_func = enum_one_string; } } static void disconnect_p(int argc, const char **argv) { bt_bdaddr_t addr; RETURN_IF_NULL(if_av); VERIFY_ADDR_ARG(2, &addr); EXEC(if_av->disconnect, &addr); } /* cleanup */ static void cleanup_p(int argc, const char **argv) { RETURN_IF_NULL(if_av); EXECV(if_av->cleanup); if_av = NULL; } static struct method methods[] = { STD_METHOD(init), STD_METHODCH(connect, ""), STD_METHODCH(disconnect, ""), STD_METHOD(cleanup), END_METHOD }; const struct interface av_if = { .name = "av", .methods = methods }; bluez-5.82/android/client/PaxHeaders/if-bt.c0000644000000000000000000000005014015011623015702 xustar0020 atime=1743516867 20 ctime=1743591279 bluez-5.82/android/client/if-bt.c0000644000000000000000000006045014015011623015370 0ustar00rootroot// SPDX-License-Identifier: Apache-2.0 /* * Copyright (C) 2013 Intel Corporation * */ #define _GNU_SOURCE #include #include #include "if-main.h" #include "terminal.h" #include "../hal-msg.h" #include "../hal-utils.h" static hw_device_t *bt_device; const bt_interface_t *if_bluetooth; #define VERIFY_PROP_TYPE_ARG(n, typ) \ do { \ if (n < argc) \ typ = str2btpropertytype(argv[n]); \ else { \ haltest_error("No property type specified\n"); \ return;\ } \ } while (0) static bt_scan_mode_t str2btscanmode(const char *str) { bt_scan_mode_t v = str2bt_scan_mode_t(str); if ((int) v != -1) return v; haltest_warn("WARN: %s cannot convert %s\n", __func__, str); return (bt_scan_mode_t) atoi(str); } static bt_ssp_variant_t str2btsspvariant(const char *str) { bt_ssp_variant_t v = str2bt_ssp_variant_t(str); if ((int) v != -1) return v; haltest_warn("WARN: %s cannot convert %s\n", __func__, str); return (bt_ssp_variant_t) atoi(str); } static bt_property_type_t str2btpropertytype(const char *str) { bt_property_type_t v = str2bt_property_type_t(str); if ((int) v != -1) return v; haltest_warn("WARN: %s cannot convert %s\n", __func__, str); return (bt_property_type_t) atoi(str); } static void dump_properties(int num_properties, bt_property_t *properties) { int i; for (i = 0; i < num_properties; i++) { /* * properities sometimes come unaligned hence memcp to * aligned buffer */ bt_property_t prop; memcpy(&prop, properties + i, sizeof(prop)); haltest_info("prop: %s\n", btproperty2str(&prop)); } } /* Cache for remote devices, stored in sorted array */ static bt_bdaddr_t *remote_devices = NULL; static int remote_devices_cnt = 0; static int remote_devices_capacity = 0; /* Adds address to remote device set so it can be used in tab completion */ void add_remote_device(const bt_bdaddr_t *addr) { int i; if (remote_devices == NULL) { remote_devices = malloc(4 * sizeof(bt_bdaddr_t)); remote_devices_cnt = 0; if (remote_devices == NULL) { remote_devices_capacity = 0; return; } remote_devices_capacity = 4; } /* Array is sorted, search for right place */ for (i = 0; i < remote_devices_cnt; ++i) { int res = memcmp(&remote_devices[i], addr, sizeof(*addr)); if (res == 0) return; /* Already added */ else if (res > 0) break; } /* Realloc space if needed */ if (remote_devices_cnt >= remote_devices_capacity) { bt_bdaddr_t *tmp; remote_devices_capacity *= 2; /* * Save reference to previously allocated memory block so that * it can be freed in case realloc fails. */ tmp = remote_devices; remote_devices = realloc(remote_devices, sizeof(bt_bdaddr_t) * remote_devices_capacity); if (remote_devices == NULL) { free(tmp); remote_devices_capacity = 0; remote_devices_cnt = 0; return; } } if (i < remote_devices_cnt) memmove(remote_devices + i + 1, remote_devices + i, (remote_devices_cnt - i) * sizeof(bt_bdaddr_t)); remote_devices[i] = *addr; remote_devices_cnt++; } const char *enum_devices(void *v, int i) { static char buf[MAX_ADDR_STR_LEN]; if (i >= remote_devices_cnt) return NULL; bt_bdaddr_t2str(&remote_devices[i], buf); return buf; } static void add_remote_device_from_props(int num_properties, const bt_property_t *properties) { int i; for (i = 0; i < num_properties; i++) { /* * properities sometimes come unaligned hence memcp to * aligned buffer */ bt_property_t property; memcpy(&property, properties + i, sizeof(property)); if (property.type == BT_PROPERTY_BDADDR) add_remote_device((bt_bdaddr_t *) property.val); } } bool close_hw_bt_dev(void) { if (!bt_device) return false; bt_device->close(bt_device); return true; } static void adapter_state_changed_cb(bt_state_t state) { haltest_info("%s: state=%s\n", __func__, bt_state_t2str(state)); } static void adapter_properties_cb(bt_status_t status, int num_properties, bt_property_t *properties) { haltest_info("%s: status=%s num_properties=%d\n", __func__, bt_status_t2str(status), num_properties); dump_properties(num_properties, properties); } static void remote_device_properties_cb(bt_status_t status, bt_bdaddr_t *bd_addr, int num_properties, bt_property_t *properties) { haltest_info("%s: status=%s bd_addr=%s num_properties=%d\n", __func__, bt_status_t2str(status), bdaddr2str(bd_addr), num_properties); add_remote_device(bd_addr); dump_properties(num_properties, properties); } static void device_found_cb(int num_properties, bt_property_t *properties) { haltest_info("%s: num_properties=%d\n", __func__, num_properties); add_remote_device_from_props(num_properties, properties); dump_properties(num_properties, properties); } static void discovery_state_changed_cb(bt_discovery_state_t state) { haltest_info("%s: state=%s\n", __func__, bt_discovery_state_t2str(state)); } /* * Buffer for remote addres that came from one of bind request. * It's stored for command completion. */ static char last_remote_addr[MAX_ADDR_STR_LEN]; static bt_ssp_variant_t last_ssp_variant = (bt_ssp_variant_t) -1; static bt_bdaddr_t pin_request_addr; static void pin_request_answer(char *reply) { bt_pin_code_t pin; int accept = 0; int pin_len = strlen(reply); if (pin_len > 0) { accept = 1; if (pin_len > 16) pin_len = 16; memcpy(&pin.pin, reply, pin_len); } EXEC(if_bluetooth->pin_reply, &pin_request_addr, accept, pin_len, &pin); } static void pin_request_cb(bt_bdaddr_t *remote_bd_addr, bt_bdname_t *bd_name, uint32_t cod) { /* Store for command completion */ bt_bdaddr_t2str(remote_bd_addr, last_remote_addr); pin_request_addr = *remote_bd_addr; haltest_info("%s: remote_bd_addr=%s bd_name=%s cod=%06x\n", __func__, last_remote_addr, bd_name->name, cod); terminal_prompt_for("Enter pin: ", pin_request_answer); } /* Variables to store information from ssp_request_cb used for ssp_reply */ static bt_bdaddr_t ssp_request_addr; static bt_ssp_variant_t ssp_request_variant; static uint32_t ssp_request_pask_key; /* Called when user hit enter on prompt for confirmation */ static void ssp_request_yes_no_answer(char *reply) { int accept = *reply == 0 || *reply == 'y' || *reply == 'Y'; if_bluetooth->ssp_reply(&ssp_request_addr, ssp_request_variant, accept, ssp_request_pask_key); } static void ssp_request_cb(bt_bdaddr_t *remote_bd_addr, bt_bdname_t *bd_name, uint32_t cod, bt_ssp_variant_t pairing_variant, uint32_t pass_key) { static char prompt[50]; /* Store for command completion */ bt_bdaddr_t2str(remote_bd_addr, last_remote_addr); last_ssp_variant = pairing_variant; haltest_info("%s: remote_bd_addr=%s bd_name=%s cod=%06x pairing_variant=%s pass_key=%d\n", __func__, last_remote_addr, bd_name->name, cod, bt_ssp_variant_t2str(pairing_variant), pass_key); switch (pairing_variant) { case BT_SSP_VARIANT_PASSKEY_CONFIRMATION: sprintf(prompt, "Does other device show %d [Y/n] ?", pass_key); ssp_request_addr = *remote_bd_addr; ssp_request_variant = pairing_variant; ssp_request_pask_key = pass_key; terminal_prompt_for(prompt, ssp_request_yes_no_answer); break; case BT_SSP_VARIANT_CONSENT: sprintf(prompt, "Consent pairing [Y/n] ?"); ssp_request_addr = *remote_bd_addr; ssp_request_variant = pairing_variant; ssp_request_pask_key = 0; terminal_prompt_for(prompt, ssp_request_yes_no_answer); break; case BT_SSP_VARIANT_PASSKEY_ENTRY: case BT_SSP_VARIANT_PASSKEY_NOTIFICATION: default: haltest_info("Not automatically handled\n"); break; } } static void bond_state_changed_cb(bt_status_t status, bt_bdaddr_t *remote_bd_addr, bt_bond_state_t state) { haltest_info("%s: status=%s remote_bd_addr=%s state=%s\n", __func__, bt_status_t2str(status), bdaddr2str(remote_bd_addr), bt_bond_state_t2str(state)); } static void acl_state_changed_cb(bt_status_t status, bt_bdaddr_t *remote_bd_addr, bt_acl_state_t state) { haltest_info("%s: status=%s remote_bd_addr=%s state=%s\n", __func__, bt_status_t2str(status), bdaddr2str(remote_bd_addr), bt_acl_state_t2str(state)); } static void thread_evt_cb(bt_cb_thread_evt evt) { haltest_info("%s: evt=%s\n", __func__, bt_cb_thread_evt2str(evt)); } static void dut_mode_recv_cb(uint16_t opcode, uint8_t *buf, uint8_t len) { haltest_info("%s\n", __func__); } static void le_test_mode_cb(bt_status_t status, uint16_t num_packets) { haltest_info("%s %s %d\n", __func__, bt_status_t2str(status), num_packets); } #if ANDROID_VERSION >= PLATFORM_VER(5, 0, 0) static void energy_info_cb(bt_activity_energy_info *energy_info) { haltest_info("%s status=%s, ctrl_state=0x%02X, tx_time=0x%jx," "rx_time=0x%jx, idle_time=0x%jx, energu_used=0x%jx\n", __func__, bt_status_t2str(energy_info->status), energy_info->ctrl_state, energy_info->tx_time, energy_info->rx_time, energy_info->idle_time, energy_info->energy_used); } #endif static bt_callbacks_t bt_callbacks = { .size = sizeof(bt_callbacks), .adapter_state_changed_cb = adapter_state_changed_cb, .adapter_properties_cb = adapter_properties_cb, .remote_device_properties_cb = remote_device_properties_cb, .device_found_cb = device_found_cb, .discovery_state_changed_cb = discovery_state_changed_cb, .pin_request_cb = pin_request_cb, .ssp_request_cb = ssp_request_cb, .bond_state_changed_cb = bond_state_changed_cb, .acl_state_changed_cb = acl_state_changed_cb, .thread_evt_cb = thread_evt_cb, .dut_mode_recv_cb = dut_mode_recv_cb, .le_test_mode_cb = le_test_mode_cb, #if ANDROID_VERSION >= PLATFORM_VER(5, 0, 0) .energy_info_cb = energy_info_cb, #endif }; #if ANDROID_VERSION >= PLATFORM_VER(5, 0, 0) static alarm_cb alarm_cb_p = NULL; static void *alarm_cb_p_data = NULL; static bool set_wake_alarm(uint64_t delay_millis, bool should_wake, alarm_cb cb, void *data) { haltest_info("%s: delay %"PRIu64" should_wake %u cb %p data %p\n", __func__, delay_millis, should_wake, cb, data); /* TODO call alarm callback after specified delay */ alarm_cb_p = cb; alarm_cb_p_data = data; return true; } static int acquire_wake_lock(const char *lock_name) { haltest_info("%s: %s\n", __func__, lock_name); return BT_STATUS_SUCCESS; } static int release_wake_lock(const char *lock_name) { haltest_info("%s: %s\n", __func__, lock_name); return BT_STATUS_SUCCESS; } static bt_os_callouts_t bt_os_callouts = { .size = sizeof(bt_os_callouts), .set_wake_alarm = set_wake_alarm, .acquire_wake_lock = acquire_wake_lock, .release_wake_lock = release_wake_lock, }; #endif static void init_p(int argc, const char **argv) { int err; const hw_module_t *module; err = hw_get_module(BT_HARDWARE_MODULE_ID, &module); if (err) { haltest_error("he_get_module returned %d\n", err); return; } err = module->methods->open(module, BT_HARDWARE_MODULE_ID, &bt_device); if (err) { haltest_error("module->methods->open returned %d\n", err); return; } if_bluetooth = ((bluetooth_device_t *) bt_device)->get_bluetooth_interface(); if (!if_bluetooth) { haltest_error("get_bluetooth_interface returned NULL\n"); return; } EXEC(if_bluetooth->init, &bt_callbacks); #if ANDROID_VERSION >= PLATFORM_VER(5, 0, 0) EXEC(if_bluetooth->set_os_callouts, &bt_os_callouts); #endif } static void cleanup_p(int argc, const char **argv) { RETURN_IF_NULL(if_bluetooth); EXECV(if_bluetooth->cleanup); if_bluetooth = NULL; } static void enable_p(int argc, const char **argv) { RETURN_IF_NULL(if_bluetooth); EXEC(if_bluetooth->enable); } #if ANDROID_VERSION >= PLATFORM_VER(5, 0, 0) static void read_energy_info_p(int argc, const char **argv) { RETURN_IF_NULL(if_bluetooth); EXEC(if_bluetooth->read_energy_info); } #define get_connection_state_c complete_addr_c static void get_connection_state_p(int argc, const char **argv) { bt_bdaddr_t addr; RETURN_IF_NULL(if_bluetooth); VERIFY_ADDR_ARG(2, &addr); haltest_info("if_bluetooth->get_connection_state : %d\n", if_bluetooth->get_connection_state(&addr)); } #endif static void disable_p(int argc, const char **argv) { RETURN_IF_NULL(if_bluetooth); EXEC(if_bluetooth->disable); } static void get_adapter_properties_p(int argc, const char **argv) { RETURN_IF_NULL(if_bluetooth); EXEC(if_bluetooth->get_adapter_properties); } static void get_adapter_property_c(int argc, const char **argv, enum_func *enum_func, void **user) { if (argc == 3) { *user = TYPE_ENUM(bt_property_type_t); *enum_func = enum_defines; } } static void get_adapter_property_p(int argc, const char **argv) { int type; RETURN_IF_NULL(if_bluetooth); VERIFY_PROP_TYPE_ARG(2, type); EXEC(if_bluetooth->get_adapter_property, type); } static const char * const names[] = { "BT_PROPERTY_BDNAME", "BT_PROPERTY_ADAPTER_SCAN_MODE", "BT_PROPERTY_ADAPTER_DISCOVERY_TIMEOUT", NULL }; static void set_adapter_property_c(int argc, const char **argv, enum_func *enum_func, void **user) { if (argc == 3) { *user = (void *) names; *enum_func = enum_strings; } else if (argc == 4) { if (0 == strcmp(argv[2], "BT_PROPERTY_ADAPTER_SCAN_MODE")) { *user = TYPE_ENUM(bt_scan_mode_t); *enum_func = enum_defines; } } } static void set_adapter_property_p(int argc, const char **argv) { bt_property_t property; bt_scan_mode_t mode; int timeout; RETURN_IF_NULL(if_bluetooth); VERIFY_PROP_TYPE_ARG(2, property.type); if (argc <= 3) { haltest_error("No property value specified\n"); return; } switch (property.type) { case BT_PROPERTY_BDNAME: property.len = strlen(argv[3]) + 1; property.val = (char *) argv[3]; break; case BT_PROPERTY_ADAPTER_SCAN_MODE: mode = str2btscanmode(argv[3]); property.len = sizeof(bt_scan_mode_t); property.val = &mode; break; case BT_PROPERTY_ADAPTER_DISCOVERY_TIMEOUT: timeout = atoi(argv[3]); property.val = &timeout; property.len = sizeof(timeout); break; case BT_PROPERTY_BDADDR: case BT_PROPERTY_UUIDS: case BT_PROPERTY_CLASS_OF_DEVICE: case BT_PROPERTY_TYPE_OF_DEVICE: case BT_PROPERTY_SERVICE_RECORD: case BT_PROPERTY_ADAPTER_BONDED_DEVICES: case BT_PROPERTY_REMOTE_FRIENDLY_NAME: case BT_PROPERTY_REMOTE_RSSI: case BT_PROPERTY_REMOTE_VERSION_INFO: case BT_PROPERTY_REMOTE_DEVICE_TIMESTAMP: #if ANDROID_VERSION >= PLATFORM_VER(5, 0, 0) case BT_PROPERTY_LOCAL_LE_FEATURES: #endif default: haltest_error("Invalid property %s\n", argv[3]); return; } EXEC(if_bluetooth->set_adapter_property, &property); } /* This function is to be used for completion methods that need only address */ static void complete_addr_c(int argc, const char **argv, enum_func *enum_func, void **user) { if (argc == 3) { *user = NULL; *enum_func = enum_devices; } } /* Just addres to complete, use complete_addr_c */ #define get_remote_device_properties_c complete_addr_c static void get_remote_device_properties_p(int argc, const char **argv) { bt_bdaddr_t addr; RETURN_IF_NULL(if_bluetooth); VERIFY_ADDR_ARG(2, &addr); EXEC(if_bluetooth->get_remote_device_properties, &addr); } static void get_remote_device_property_c(int argc, const char **argv, enum_func *enum_func, void **user) { if (argc == 3) { *user = NULL; *enum_func = enum_devices; } else if (argc == 4) { *user = TYPE_ENUM(bt_property_type_t); *enum_func = enum_defines; } } static void get_remote_device_property_p(int argc, const char **argv) { bt_property_type_t type; bt_bdaddr_t addr; RETURN_IF_NULL(if_bluetooth); VERIFY_ADDR_ARG(2, &addr); VERIFY_PROP_TYPE_ARG(3, type); EXEC(if_bluetooth->get_remote_device_property, &addr, type); } /* * Same completion as for get_remote_device_property_c can be used for * set_remote_device_property_c. No need to create separate function. */ #define set_remote_device_property_c get_remote_device_property_c static void set_remote_device_property_p(int argc, const char **argv) { bt_property_t property; bt_bdaddr_t addr; RETURN_IF_NULL(if_bluetooth); VERIFY_ADDR_ARG(2, &addr); VERIFY_PROP_TYPE_ARG(3, property.type); switch (property.type) { case BT_PROPERTY_REMOTE_FRIENDLY_NAME: property.len = strlen(argv[4]); property.val = (char *) argv[4]; break; case BT_PROPERTY_BDNAME: case BT_PROPERTY_BDADDR: case BT_PROPERTY_UUIDS: case BT_PROPERTY_CLASS_OF_DEVICE: case BT_PROPERTY_TYPE_OF_DEVICE: case BT_PROPERTY_SERVICE_RECORD: case BT_PROPERTY_ADAPTER_SCAN_MODE: case BT_PROPERTY_ADAPTER_BONDED_DEVICES: case BT_PROPERTY_ADAPTER_DISCOVERY_TIMEOUT: case BT_PROPERTY_REMOTE_RSSI: case BT_PROPERTY_REMOTE_VERSION_INFO: case BT_PROPERTY_REMOTE_DEVICE_TIMESTAMP: #if ANDROID_VERSION >= PLATFORM_VER(5, 0, 0) case BT_PROPERTY_LOCAL_LE_FEATURES: #endif default: return; } EXEC(if_bluetooth->set_remote_device_property, &addr, &property); } /* For now uuid is not autocompleted. Use routine for complete_addr_c */ #define get_remote_service_record_c complete_addr_c static void get_remote_service_record_p(int argc, const char **argv) { bt_bdaddr_t addr; bt_uuid_t uuid; RETURN_IF_NULL(if_bluetooth); VERIFY_ADDR_ARG(2, &addr); if (argc <= 3) { haltest_error("No uuid specified\n"); return; } str2bt_uuid_t(argv[3], &uuid); EXEC(if_bluetooth->get_remote_service_record, &addr, &uuid); } /* Just addres to complete, use complete_addr_c */ #define get_remote_services_c complete_addr_c static void get_remote_services_p(int argc, const char **argv) { bt_bdaddr_t addr; RETURN_IF_NULL(if_bluetooth); VERIFY_ADDR_ARG(2, &addr); EXEC(if_bluetooth->get_remote_services, &addr); } static void start_discovery_p(int argc, const char **argv) { RETURN_IF_NULL(if_bluetooth); EXEC(if_bluetooth->start_discovery); } static void cancel_discovery_p(int argc, const char **argv) { RETURN_IF_NULL(if_bluetooth); EXEC(if_bluetooth->cancel_discovery); } /* Just addres to complete, use complete_addr_c */ #define create_bond_c complete_addr_c static void create_bond_p(int argc, const char **argv) { bt_bdaddr_t addr; #if ANDROID_VERSION >= PLATFORM_VER(5, 0, 0) int transport; #endif RETURN_IF_NULL(if_bluetooth); VERIFY_ADDR_ARG(2, &addr); #if ANDROID_VERSION >= PLATFORM_VER(5, 0, 0) if (argc < 4) transport = BT_TRANSPORT_UNKNOWN; else transport = atoi(argv[3]); EXEC(if_bluetooth->create_bond, &addr, transport); #else EXEC(if_bluetooth->create_bond, &addr); #endif } /* Just addres to complete, use complete_addr_c */ #define remove_bond_c complete_addr_c static void remove_bond_p(int argc, const char **argv) { bt_bdaddr_t addr; RETURN_IF_NULL(if_bluetooth); VERIFY_ADDR_ARG(2, &addr); EXEC(if_bluetooth->remove_bond, &addr); } /* Just addres to complete, use complete_addr_c */ #define cancel_bond_c complete_addr_c static void cancel_bond_p(int argc, const char **argv) { bt_bdaddr_t addr; RETURN_IF_NULL(if_bluetooth); VERIFY_ADDR_ARG(2, &addr); EXEC(if_bluetooth->cancel_bond, &addr); } static void pin_reply_c(int argc, const char **argv, enum_func *enum_func, void **user) { static const char *const completions[] = { last_remote_addr, NULL }; if (argc == 3) { *user = (void *) completions; *enum_func = enum_strings; } } static void pin_reply_p(int argc, const char **argv) { bt_bdaddr_t addr; bt_pin_code_t pin; int pin_len = 0; int accept = 0; RETURN_IF_NULL(if_bluetooth); VERIFY_ADDR_ARG(2, &addr); if (argc > 3) { accept = 1; pin_len = strlen(argv[3]); memcpy(pin.pin, argv[3], pin_len); } EXEC(if_bluetooth->pin_reply, &addr, accept, pin_len, &pin); } static void ssp_reply_c(int argc, const char **argv, enum_func *enum_func, void **user) { if (argc == 3) { *user = last_remote_addr; *enum_func = enum_one_string; } else if (argc == 5) { *user = "1"; *enum_func = enum_one_string; } else if (argc == 4) { if (-1 != (int) last_ssp_variant) { *user = (void *) bt_ssp_variant_t2str(last_ssp_variant); *enum_func = enum_one_string; } else { *user = TYPE_ENUM(bt_ssp_variant_t); *enum_func = enum_defines; } } } static void ssp_reply_p(int argc, const char **argv) { bt_bdaddr_t addr; bt_ssp_variant_t var; int accept; int passkey; RETURN_IF_NULL(if_bluetooth); VERIFY_ADDR_ARG(2, &addr); if (argc < 4) { haltest_error("No ssp variant specified\n"); return; } var = str2btsspvariant(argv[3]); if (argc < 5) { haltest_error("No accept value specified\n"); return; } accept = atoi(argv[4]); passkey = 0; if (accept && var == BT_SSP_VARIANT_PASSKEY_ENTRY && argc >= 5) passkey = atoi(argv[4]); EXEC(if_bluetooth->ssp_reply, &addr, var, accept, passkey); } static void get_profile_interface_c(int argc, const char **argv, enum_func *enum_func, void **user) { static const char *const profile_ids[] = { BT_PROFILE_HANDSFREE_ID, BT_PROFILE_ADVANCED_AUDIO_ID, BT_PROFILE_HEALTH_ID, BT_PROFILE_SOCKETS_ID, BT_PROFILE_HIDHOST_ID, BT_PROFILE_PAN_ID, BT_PROFILE_GATT_ID, BT_PROFILE_AV_RC_ID, #if ANDROID_VERSION >= PLATFORM_VER(5, 0, 0) BT_PROFILE_HANDSFREE_CLIENT_ID, BT_PROFILE_MAP_CLIENT_ID, BT_PROFILE_AV_RC_CTRL_ID, BT_PROFILE_ADVANCED_AUDIO_SINK_ID, #endif NULL }; if (argc == 3) { *user = (void *) profile_ids; *enum_func = enum_strings; } } static void get_profile_interface_p(int argc, const char **argv) { const char *id; const void **pif = NULL; RETURN_IF_NULL(if_bluetooth); if (argc <= 2) { haltest_error("No interface specified\n"); return; } id = argv[2]; if (strcmp(BT_PROFILE_HANDSFREE_ID, id) == 0) pif = (const void **) &if_hf; else if (strcmp(BT_PROFILE_ADVANCED_AUDIO_ID, id) == 0) pif = (const void **) &if_av; else if (strcmp(BT_PROFILE_HEALTH_ID, id) == 0) pif = (const void **) &if_hl; else if (strcmp(BT_PROFILE_SOCKETS_ID, id) == 0) pif = (const void **) &if_sock; else if (strcmp(BT_PROFILE_HIDHOST_ID, id) == 0) pif = (const void **) &if_hh; else if (strcmp(BT_PROFILE_PAN_ID, id) == 0) pif = (const void **) &if_pan; else if (strcmp(BT_PROFILE_AV_RC_ID, id) == 0) pif = (const void **) &if_rc; else if (strcmp(BT_PROFILE_GATT_ID, id) == 0) pif = (const void **) &if_gatt; #if ANDROID_VERSION >= PLATFORM_VER(5, 0, 0) else if (strcmp(BT_PROFILE_AV_RC_CTRL_ID, id) == 0) pif = (const void **) &if_rc_ctrl; else if (strcmp(BT_PROFILE_HANDSFREE_CLIENT_ID, id) == 0) pif = (const void **) &if_hf_client; else if (strcmp(BT_PROFILE_MAP_CLIENT_ID, id) == 0) pif = (const void **) &if_mce; else if (strcmp(BT_PROFILE_ADVANCED_AUDIO_SINK_ID, id) == 0) pif = (const void **) &if_av_sink; #endif else haltest_error("%s is not correct for get_profile_interface\n", id); if (pif != NULL) { *pif = if_bluetooth->get_profile_interface(id); haltest_info("get_profile_interface(%s) : %p\n", id, *pif); } } static void dut_mode_configure_p(int argc, const char **argv) { uint8_t mode; RETURN_IF_NULL(if_bluetooth); if (argc <= 2) { haltest_error("No dut mode specified\n"); return; } mode = strtol(argv[2], NULL, 0); EXEC(if_bluetooth->dut_mode_configure, mode); } static void dut_mode_send_p(int argc, const char **argv) { haltest_error("not implemented\n"); } static void le_test_mode_p(int argc, const char **argv) { haltest_error("not implemented\n"); } static void config_hci_snoop_log_p(int argc, const char **argv) { uint8_t mode; RETURN_IF_NULL(if_bluetooth); if (argc <= 2) { haltest_error("No mode specified\n"); return; } mode = strtol(argv[2], NULL, 0); EXEC(if_bluetooth->config_hci_snoop_log, mode); } static struct method methods[] = { STD_METHOD(init), STD_METHOD(cleanup), STD_METHOD(enable), STD_METHOD(disable), STD_METHOD(get_adapter_properties), STD_METHODCH(get_adapter_property, ""), STD_METHODCH(set_adapter_property, " "), STD_METHODCH(get_remote_device_properties, ""), STD_METHODCH(get_remote_device_property, " "), STD_METHODCH(set_remote_device_property, " "), STD_METHODCH(get_remote_service_record, " "), STD_METHODCH(get_remote_services, ""), STD_METHOD(start_discovery), STD_METHOD(cancel_discovery), #if ANDROID_VERSION >= PLATFORM_VER(5, 0, 0) STD_METHODCH(create_bond, " []"), STD_METHOD(read_energy_info), STD_METHODCH(get_connection_state, ""), #else STD_METHODCH(create_bond, ""), #endif STD_METHODCH(remove_bond, ""), STD_METHODCH(cancel_bond, ""), STD_METHODCH(pin_reply, "
[]"), STD_METHODCH(ssp_reply, "
1|0 []"), STD_METHODCH(get_profile_interface, ""), STD_METHODH(dut_mode_configure, ""), STD_METHOD(dut_mode_send), STD_METHOD(le_test_mode), STD_METHODH(config_hci_snoop_log, ""), END_METHOD }; const struct interface bluetooth_if = { .name = "bluetooth", .methods = methods }; bluez-5.82/android/PaxHeaders/hal-sco.c0000644000000000000000000000005014572354773015001 xustar0020 atime=1743516862 20 ctime=1743591276 bluez-5.82/android/hal-sco.c0000644000000000000000000010145714572354773014472 0ustar00rootroot// SPDX-License-Identifier: Apache-2.0 /* * Copyright (C) 2013 Intel Corporation * */ #define _GNU_SOURCE #include #include #include #include #include #include #include #include #include #include #include #include #include "hal-utils.h" #include "sco-msg.h" #include "ipc-common.h" #include "hal-log.h" #include "hal.h" #define AUDIO_STREAM_DEFAULT_RATE 44100 #define AUDIO_STREAM_SCO_RATE 8000 #define AUDIO_STREAM_DEFAULT_FORMAT AUDIO_FORMAT_PCM_16_BIT #define OUT_BUFFER_SIZE 2560 #define OUT_STREAM_FRAMES 2560 #define IN_STREAM_FRAMES 5292 #define SOCKET_POLL_TIMEOUT_MS 500 static int listen_sk = -1; static int ipc_sk = -1; static int sco_fd = -1; static uint16_t sco_mtu = 0; static pthread_mutex_t sco_mutex = PTHREAD_MUTEX_INITIALIZER; static pthread_t ipc_th = 0; static pthread_mutex_t sk_mutex = PTHREAD_MUTEX_INITIALIZER; static struct sco_stream_in *sco_stream_in = NULL; static struct sco_stream_out *sco_stream_out = NULL; struct sco_audio_config { uint32_t rate; uint32_t channels; uint32_t frame_num; audio_format_t format; }; struct sco_stream_out { struct audio_stream_out stream; struct sco_audio_config cfg; uint8_t *downmix_buf; uint8_t *cache; size_t cache_len; size_t samples; struct timespec start; struct resampler_itfe *resampler; int16_t *resample_buf; uint32_t resample_frame_num; bt_bdaddr_t bd_addr; }; static void sco_close_socket(void) { DBG("sco fd %d", sco_fd); if (sco_fd < 0) return; shutdown(sco_fd, SHUT_RDWR); close(sco_fd); sco_fd = -1; } struct sco_stream_in { struct audio_stream_in stream; struct sco_audio_config cfg; struct resampler_itfe *resampler; int16_t *resample_buf; uint32_t resample_frame_num; bt_bdaddr_t bd_addr; }; struct sco_dev { struct audio_hw_device dev; struct sco_stream_out *out; struct sco_stream_in *in; }; /* * return the minimum frame numbers from resampling between BT stack's rate * and audio flinger's. For output stream, 'output' shall be true, otherwise * false for input streams at audio flinger side. */ static size_t get_resample_frame_num(uint32_t sco_rate, uint32_t rate, size_t frame_num, bool output) { size_t resample_frames_num = frame_num * sco_rate / rate + output; DBG("resampler: sco_rate %d frame_num %zd rate %d resample frames %zd", sco_rate, frame_num, rate, resample_frames_num); return resample_frames_num; } /* SCO IPC functions */ static int sco_ipc_cmd(uint8_t service_id, uint8_t opcode, uint16_t len, void *param, size_t *rsp_len, void *rsp, int *fd) { ssize_t ret; struct msghdr msg; struct iovec iv[2]; struct ipc_hdr cmd; char cmsgbuf[CMSG_SPACE(sizeof(int))]; struct ipc_status s; size_t s_len = sizeof(s); pthread_mutex_lock(&sk_mutex); if (ipc_sk < 0) { error("sco: Invalid cmd socket passed to sco_ipc_cmd"); goto failed; } if (!rsp || !rsp_len) { memset(&s, 0, s_len); rsp_len = &s_len; rsp = &s; } memset(&msg, 0, sizeof(msg)); memset(&cmd, 0, sizeof(cmd)); cmd.service_id = service_id; cmd.opcode = opcode; cmd.len = len; iv[0].iov_base = &cmd; iv[0].iov_len = sizeof(cmd); iv[1].iov_base = param; iv[1].iov_len = len; msg.msg_iov = iv; msg.msg_iovlen = 2; ret = sendmsg(ipc_sk, &msg, 0); if (ret < 0) { error("sco: Sending command failed:%s", strerror(errno)); goto failed; } /* socket was shutdown */ if (ret == 0) { error("sco: Command socket closed"); goto failed; } memset(&msg, 0, sizeof(msg)); memset(&cmd, 0, sizeof(cmd)); iv[0].iov_base = &cmd; iv[0].iov_len = sizeof(cmd); iv[1].iov_base = rsp; iv[1].iov_len = *rsp_len; msg.msg_iov = iv; msg.msg_iovlen = 2; if (fd) { memset(cmsgbuf, 0, sizeof(cmsgbuf)); msg.msg_control = cmsgbuf; msg.msg_controllen = sizeof(cmsgbuf); } ret = recvmsg(ipc_sk, &msg, 0); if (ret < 0) { error("sco: Receiving command response failed:%s", strerror(errno)); goto failed; } if (ret < (ssize_t) sizeof(cmd)) { error("sco: Too small response received(%zd bytes)", ret); goto failed; } if (cmd.service_id != service_id) { error("sco: Invalid service id (%u vs %u)", cmd.service_id, service_id); goto failed; } if (ret != (ssize_t) (sizeof(cmd) + cmd.len)) { error("sco: Malformed response received(%zd bytes)", ret); goto failed; } if (cmd.opcode != opcode && cmd.opcode != SCO_OP_STATUS) { error("sco: Invalid opcode received (%u vs %u)", cmd.opcode, opcode); goto failed; } if (cmd.opcode == SCO_OP_STATUS) { struct ipc_status *s = rsp; if (sizeof(*s) != cmd.len) { error("sco: Invalid status length"); goto failed; } if (s->code == SCO_STATUS_SUCCESS) { error("sco: Invalid success status response"); goto failed; } pthread_mutex_unlock(&sk_mutex); return s->code; } pthread_mutex_unlock(&sk_mutex); /* Receive auxiliary data in msg */ if (fd) { struct cmsghdr *cmsg; *fd = -1; for (cmsg = CMSG_FIRSTHDR(&msg); cmsg; cmsg = CMSG_NXTHDR(&msg, cmsg)) { if (cmsg->cmsg_level == SOL_SOCKET && cmsg->cmsg_type == SCM_RIGHTS) { memcpy(fd, CMSG_DATA(cmsg), sizeof(int)); break; } } if (*fd < 0) goto failed; } *rsp_len = cmd.len; return SCO_STATUS_SUCCESS; failed: /* Some serious issue happen on IPC - recover */ shutdown(ipc_sk, SHUT_RDWR); pthread_mutex_unlock(&sk_mutex); return SCO_STATUS_FAILED; } static int ipc_get_sco_fd(bt_bdaddr_t *bd_addr) { int ret = SCO_STATUS_SUCCESS; pthread_mutex_lock(&sco_mutex); if (sco_fd < 0) { struct sco_cmd_get_fd cmd; struct sco_rsp_get_fd rsp; size_t rsp_len = sizeof(rsp); DBG("Getting SCO fd"); memcpy(cmd.bdaddr, bd_addr, sizeof(cmd.bdaddr)); ret = sco_ipc_cmd(SCO_SERVICE_ID, SCO_OP_GET_FD, sizeof(cmd), &cmd, &rsp_len, &rsp, &sco_fd); /* Sometimes mtu returned is wrong */ sco_mtu = /* rsp.mtu */ 48; } pthread_mutex_unlock(&sco_mutex); return ret; } /* Audio stream functions */ static void downmix_to_mono(struct sco_stream_out *out, const uint8_t *buffer, size_t frame_num) { const int16_t *input = (const void *) buffer; int16_t *output = (void *) out->downmix_buf; size_t i; for (i = 0; i < frame_num; i++) { int16_t l = get_le16(&input[i * 2]); int16_t r = get_le16(&input[i * 2 + 1]); put_le16((l + r) / 2, &output[i]); } } static uint64_t timespec_diff_us(struct timespec *a, struct timespec *b) { struct timespec res; res.tv_sec = a->tv_sec - b->tv_sec; res.tv_nsec = a->tv_nsec - b->tv_nsec; if (res.tv_nsec < 0) { res.tv_sec--; res.tv_nsec += 1000000000ll; /* 1sec */ } return res.tv_sec * 1000000ll + res.tv_nsec / 1000ll; } static bool write_data(struct sco_stream_out *out, const uint8_t *buffer, size_t bytes) { struct pollfd pfd; size_t len, written = 0; int ret; uint8_t *p; uint64_t audio_sent_us, audio_passed_us; pfd.fd = sco_fd; pfd.events = POLLOUT | POLLHUP | POLLNVAL; while (bytes > written) { struct timespec now; /* poll for sending */ if (poll(&pfd, 1, SOCKET_POLL_TIMEOUT_MS) == 0) { DBG("timeout fd %d", sco_fd); return false; } if (pfd.revents & (POLLHUP | POLLNVAL)) { error("error fd %d, events 0x%x", sco_fd, pfd.revents); return false; } len = bytes - written > sco_mtu ? sco_mtu : bytes - written; clock_gettime(CLOCK_REALTIME, &now); /* Mark start of the stream */ if (!out->samples) memcpy(&out->start, &now, sizeof(out->start)); audio_sent_us = out->samples * 1000000ll / AUDIO_STREAM_SCO_RATE; audio_passed_us = timespec_diff_us(&now, &out->start); if ((int) (audio_sent_us - audio_passed_us) > 1500) { struct timespec timeout = {0, (audio_sent_us - audio_passed_us) * 1000}; DBG("Sleeping for %d ms", (int) (audio_sent_us - audio_passed_us)); nanosleep(&timeout, NULL); } else if ((int)(audio_passed_us - audio_sent_us) > 50000) { DBG("\n\nResync\n\n"); out->samples = 0; memcpy(&out->start, &now, sizeof(out->start)); } if (out->cache_len) { DBG("First packet cache_len %zd", out->cache_len); memcpy(out->cache + out->cache_len, buffer, sco_mtu - out->cache_len); p = out->cache; } else { if (bytes - written >= sco_mtu) p = (void *) buffer + written; else { memcpy(out->cache, buffer + written, bytes - written); out->cache_len = bytes - written; DBG("Last packet, cache %zd bytes", bytes - written); written += bytes - written; continue; } } ret = write(sco_fd, p, len); if (ret > 0) { if (out->cache_len) { written = sco_mtu - out->cache_len; out->cache_len = 0; } else written += ret; out->samples += ret / 2; DBG("written %d samples %zd total %zd bytes", ret, out->samples, written); continue; } if (errno == EAGAIN) { ret = errno; warn("write failed (%d)", ret); continue; } if (errno != EINTR) { ret = errno; error("write failed (%d) fd %d bytes %zd", ret, sco_fd, bytes); return false; } } DBG("written %zd bytes", bytes); return true; } static ssize_t out_write(struct audio_stream_out *stream, const void *buffer, size_t bytes) { struct sco_stream_out *out = (struct sco_stream_out *) stream; #if ANDROID_VERSION >= PLATFORM_VER(5, 0, 0) size_t frame_num = bytes / audio_stream_out_frame_size(stream); #else size_t frame_num = bytes / audio_stream_frame_size(&out->stream.common); #endif size_t output_frame_num = frame_num; void *send_buf = out->downmix_buf; size_t total; DBG("write to fd %d bytes %zu", sco_fd, bytes); if (ipc_get_sco_fd(&out->bd_addr) != SCO_STATUS_SUCCESS) return -1; if (!out->downmix_buf) { error("sco: downmix buffer not initialized"); return -1; } downmix_to_mono(out, buffer, frame_num); if (out->resampler) { int ret; /* limit resampler's output within what resample buf can hold */ output_frame_num = out->resample_frame_num; ret = out->resampler->resample_from_input(out->resampler, send_buf, &frame_num, out->resample_buf, &output_frame_num); if (ret) { error("Failed to resample frames: %zd input %zd (%s)", frame_num, output_frame_num, strerror(ret)); return -1; } send_buf = out->resample_buf; DBG("Resampled: frame_num %zd, output_frame_num %zd", frame_num, output_frame_num); } total = output_frame_num * sizeof(int16_t) * 1; DBG("total %zd", total); if (!write_data(out, send_buf, total)) return -1; return bytes; } static uint32_t out_get_sample_rate(const struct audio_stream *stream) { struct sco_stream_out *out = (struct sco_stream_out *) stream; DBG("rate %u", out->cfg.rate); return out->cfg.rate; } static int out_set_sample_rate(struct audio_stream *stream, uint32_t rate) { DBG("rate %u", rate); return 0; } static size_t out_get_buffer_size(const struct audio_stream *stream) { struct sco_stream_out *out = (struct sco_stream_out *) stream; #if ANDROID_VERSION >= PLATFORM_VER(5, 0, 0) size_t size = audio_stream_out_frame_size(&out->stream) * out->cfg.frame_num; #else size_t size = audio_stream_frame_size(&out->stream.common) * out->cfg.frame_num; #endif /* buffer size without resampling */ if (out->cfg.rate == AUDIO_STREAM_SCO_RATE) size = 576 * 2; DBG("buf size %zd", size); return size; } static uint32_t out_get_channels(const struct audio_stream *stream) { struct sco_stream_out *out = (struct sco_stream_out *) stream; DBG("channels num: %u", popcount(out->cfg.channels)); return out->cfg.channels; } static audio_format_t out_get_format(const struct audio_stream *stream) { struct sco_stream_out *out = (struct sco_stream_out *) stream; DBG("format: %u", out->cfg.format); return out->cfg.format; } static int out_set_format(struct audio_stream *stream, audio_format_t format) { DBG(""); return -ENOSYS; } static int out_standby(struct audio_stream *stream) { DBG(""); return 0; } static int out_dump(const struct audio_stream *stream, int fd) { DBG(""); return -ENOSYS; } static int out_set_parameters(struct audio_stream *stream, const char *kvpairs) { DBG("%s", kvpairs); return 0; } static char *out_get_parameters(const struct audio_stream *stream, const char *keys) { DBG(""); return strdup(""); } static uint32_t out_get_latency(const struct audio_stream_out *stream) { DBG(""); return 0; } static int out_set_volume(struct audio_stream_out *stream, float left, float right) { DBG(""); return -ENOSYS; } static int out_get_render_position(const struct audio_stream_out *stream, uint32_t *dsp_frames) { DBG(""); return -ENOSYS; } static int out_add_audio_effect(const struct audio_stream *stream, effect_handle_t effect) { DBG(""); return -ENOSYS; } static int out_remove_audio_effect(const struct audio_stream *stream, effect_handle_t effect) { DBG(""); return -ENOSYS; } static int sco_open_output_stream_real(struct audio_hw_device *dev, audio_io_handle_t handle, audio_devices_t devices, audio_output_flags_t flags, struct audio_config *config, struct audio_stream_out **stream_out, const char *address) { struct sco_dev *adev = (struct sco_dev *) dev; struct sco_stream_out *out; int chan_num, ret; size_t resample_size; DBG("config %p device flags 0x%02x", config, devices); if (sco_stream_out) { DBG("stream_out already open"); return -EIO; } out = calloc(1, sizeof(struct sco_stream_out)); if (!out) return -ENOMEM; DBG("stream %p sco fd %d mtu %u", out, sco_fd, sco_mtu); out->stream.common.get_sample_rate = out_get_sample_rate; out->stream.common.set_sample_rate = out_set_sample_rate; out->stream.common.get_buffer_size = out_get_buffer_size; out->stream.common.get_channels = out_get_channels; out->stream.common.get_format = out_get_format; out->stream.common.set_format = out_set_format; out->stream.common.standby = out_standby; out->stream.common.dump = out_dump; out->stream.common.set_parameters = out_set_parameters; out->stream.common.get_parameters = out_get_parameters; out->stream.common.add_audio_effect = out_add_audio_effect; out->stream.common.remove_audio_effect = out_remove_audio_effect; out->stream.get_latency = out_get_latency; out->stream.set_volume = out_set_volume; out->stream.write = out_write; out->stream.get_render_position = out_get_render_position; #if ANDROID_VERSION >= PLATFORM_VER(5, 0, 0) if (address) { DBG("address %s", address); str2bt_bdaddr_t(address, &out->bd_addr); } #endif if (ipc_get_sco_fd(&out->bd_addr) != SCO_STATUS_SUCCESS) DBG("SCO is not connected yet; get fd on write()"); if (config) { DBG("config: rate %u chan mask %x format %d offload %p", config->sample_rate, config->channel_mask, config->format, &config->offload_info); out->cfg.format = config->format; out->cfg.channels = config->channel_mask; out->cfg.rate = config->sample_rate; } else { out->cfg.format = AUDIO_STREAM_DEFAULT_FORMAT; out->cfg.channels = AUDIO_CHANNEL_OUT_STEREO; out->cfg.rate = AUDIO_STREAM_DEFAULT_RATE; } out->cfg.frame_num = OUT_STREAM_FRAMES; out->downmix_buf = malloc(out_get_buffer_size(&out->stream.common)); if (!out->downmix_buf) { free(out); return -ENOMEM; } out->cache = malloc(sco_mtu); if (!out->cache) { free(out->downmix_buf); free(out); return -ENOMEM; } if (out->cfg.rate == AUDIO_STREAM_SCO_RATE) goto skip_resampler; /* Channel numbers for resampler */ chan_num = 1; ret = create_resampler(out->cfg.rate, AUDIO_STREAM_SCO_RATE, chan_num, RESAMPLER_QUALITY_DEFAULT, NULL, &out->resampler); if (ret) { error("Failed to create resampler (%s)", strerror(-ret)); goto failed; } out->resample_frame_num = get_resample_frame_num(AUDIO_STREAM_SCO_RATE, out->cfg.rate, out->cfg.frame_num, 1); if (!out->resample_frame_num) { error("frame num is too small to resample, discard it"); goto failed; } resample_size = sizeof(int16_t) * chan_num * out->resample_frame_num; out->resample_buf = malloc(resample_size); if (!out->resample_buf) { error("failed to allocate resample buffer for %u frames", out->resample_frame_num); goto failed; } DBG("Resampler: input %d output %d chan %d frames %u size %zd", out->cfg.rate, AUDIO_STREAM_SCO_RATE, chan_num, out->resample_frame_num, resample_size); skip_resampler: *stream_out = &out->stream; adev->out = out; sco_stream_out = out; return 0; failed: if (out->resampler) release_resampler(out->resampler); free(out->cache); free(out->downmix_buf); free(out); *stream_out = NULL; adev->out = NULL; sco_stream_out = NULL; return ret; } #if ANDROID_VERSION >= PLATFORM_VER(5, 0, 0) static int sco_open_output_stream(struct audio_hw_device *dev, audio_io_handle_t handle, audio_devices_t devices, audio_output_flags_t flags, struct audio_config *config, struct audio_stream_out **stream_out, const char *address) { return sco_open_output_stream_real(dev, handle, devices, flags, config, stream_out, address); } #else static int sco_open_output_stream(struct audio_hw_device *dev, audio_io_handle_t handle, audio_devices_t devices, audio_output_flags_t flags, struct audio_config *config, struct audio_stream_out **stream_out) { return sco_open_output_stream_real(dev, handle, devices, flags, config, stream_out, NULL); } #endif static void sco_close_output_stream(struct audio_hw_device *dev, struct audio_stream_out *stream_out) { struct sco_dev *sco_dev = (struct sco_dev *) dev; struct sco_stream_out *out = (struct sco_stream_out *) stream_out; DBG("dev %p stream %p fd %d", dev, out, sco_fd); if (out->resampler) { release_resampler(out->resampler); free(out->resample_buf); } free(out->cache); free(out->downmix_buf); free(out); sco_dev->out = NULL; pthread_mutex_lock(&sco_mutex); sco_stream_out = NULL; if (!sco_stream_in) sco_close_socket(); pthread_mutex_unlock(&sco_mutex); } static int sco_set_parameters(struct audio_hw_device *dev, const char *kvpairs) { DBG("%s", kvpairs); return 0; } static char *sco_get_parameters(const struct audio_hw_device *dev, const char *keys) { DBG(""); return strdup(""); } static int sco_init_check(const struct audio_hw_device *dev) { DBG(""); return 0; } static int sco_set_voice_volume(struct audio_hw_device *dev, float volume) { DBG("%f", volume); return 0; } static int sco_set_master_volume(struct audio_hw_device *dev, float volume) { DBG("%f", volume); return 0; } static int sco_set_mode(struct audio_hw_device *dev, int mode) { DBG(""); return -ENOSYS; } static int sco_set_mic_mute(struct audio_hw_device *dev, bool state) { DBG(""); return -ENOSYS; } static int sco_get_mic_mute(const struct audio_hw_device *dev, bool *state) { DBG(""); return -ENOSYS; } static size_t sco_get_input_buffer_size(const struct audio_hw_device *dev, const struct audio_config *config) { DBG(""); return -ENOSYS; } static uint32_t in_get_sample_rate(const struct audio_stream *stream) { struct sco_stream_in *in = (struct sco_stream_in *) stream; DBG("rate %u", in->cfg.rate); return in->cfg.rate; } static int in_set_sample_rate(struct audio_stream *stream, uint32_t rate) { DBG("rate %u", rate); return 0; } static size_t in_get_buffer_size(const struct audio_stream *stream) { struct sco_stream_in *in = (struct sco_stream_in *) stream; #if ANDROID_VERSION >= PLATFORM_VER(5, 0, 0) size_t size = audio_stream_in_frame_size(&in->stream) * in->cfg.frame_num; #else size_t size = audio_stream_frame_size(&in->stream.common) * in->cfg.frame_num; #endif /* buffer size without resampling */ if (in->cfg.rate == AUDIO_STREAM_SCO_RATE) size = 576; DBG("buf size %zd", size); return size; } static uint32_t in_get_channels(const struct audio_stream *stream) { struct sco_stream_in *in = (struct sco_stream_in *) stream; DBG("channels num: %u", popcount(in->cfg.channels)); return in->cfg.channels; } static audio_format_t in_get_format(const struct audio_stream *stream) { struct sco_stream_in *in = (struct sco_stream_in *) stream; DBG("format: %u", in->cfg.format); return in->cfg.format; } static int in_set_format(struct audio_stream *stream, audio_format_t format) { DBG(""); return -ENOSYS; } static int in_standby(struct audio_stream *stream) { DBG(""); return 0; } static int in_dump(const struct audio_stream *stream, int fd) { DBG(""); return -ENOSYS; } static int in_set_parameters(struct audio_stream *stream, const char *kvpairs) { DBG("%s", kvpairs); return 0; } static char *in_get_parameters(const struct audio_stream *stream, const char *keys) { DBG(""); return strdup(""); } static int in_add_audio_effect(const struct audio_stream *stream, effect_handle_t effect) { DBG(""); return -ENOSYS; } static int in_remove_audio_effect(const struct audio_stream *stream, effect_handle_t effect) { DBG(""); return -ENOSYS; } static int in_set_gain(struct audio_stream_in *stream, float gain) { DBG(""); return -ENOSYS; } static bool read_data(struct sco_stream_in *in, char *buffer, size_t bytes) { struct pollfd pfd; size_t len, read_bytes = 0; pfd.fd = sco_fd; pfd.events = POLLIN | POLLHUP | POLLNVAL; while (bytes > read_bytes) { int ret; /* poll for reading */ if (poll(&pfd, 1, SOCKET_POLL_TIMEOUT_MS) == 0) { DBG("timeout fd %d", sco_fd); return false; } if (pfd.revents & (POLLHUP | POLLNVAL)) { error("error fd %d, events 0x%x", sco_fd, pfd.revents); return false; } len = bytes - read_bytes > sco_mtu ? sco_mtu : bytes - read_bytes; ret = read(sco_fd, buffer + read_bytes, len); if (ret > 0) { read_bytes += ret; DBG("read %d total %zd", ret, read_bytes); continue; } if (errno == EAGAIN) { ret = errno; warn("read failed (%d)", ret); continue; } if (errno != EINTR) { ret = errno; error("read failed (%d) fd %d bytes %zd", ret, sco_fd, bytes); return false; } } DBG("read %zd bytes", read_bytes); return true; } static ssize_t in_read(struct audio_stream_in *stream, void *buffer, size_t bytes) { struct sco_stream_in *in = (struct sco_stream_in *) stream; size_t frame_size, frame_num, input_frame_num; void *read_buf = buffer; size_t total = bytes; int ret; #if ANDROID_VERSION >= PLATFORM_VER(5, 0, 0) frame_size = audio_stream_in_frame_size(&in->stream); #else frame_size = audio_stream_frame_size(&stream->common); #endif if (!frame_size) return -1; frame_num = bytes / frame_size; input_frame_num = frame_num; DBG("Read from fd %d bytes %zu", sco_fd, bytes); if (ipc_get_sco_fd(&in->bd_addr) != SCO_STATUS_SUCCESS) return -1; if (!in->resampler && in->cfg.rate != AUDIO_STREAM_SCO_RATE) { error("Cannot find resampler"); return -1; } if (in->resampler) { input_frame_num = get_resample_frame_num(AUDIO_STREAM_SCO_RATE, in->cfg.rate, frame_num, 0); if (input_frame_num > in->resample_frame_num) { DBG("resize input frames from %zd to %d", input_frame_num, in->resample_frame_num); input_frame_num = in->resample_frame_num; } read_buf = in->resample_buf; total = input_frame_num * sizeof(int16_t) * 1; } if(!read_data(in, read_buf, total)) return -1; if (in->resampler) { ret = in->resampler->resample_from_input(in->resampler, in->resample_buf, &input_frame_num, (int16_t *) buffer, &frame_num); if (ret) { error("Failed to resample frames: %zd input %zd (%s)", frame_num, input_frame_num, strerror(ret)); return -1; } DBG("resampler: remain %zd output %zd frames", input_frame_num, frame_num); } return bytes; } static uint32_t in_get_input_frames_lost(struct audio_stream_in *stream) { DBG(""); return -ENOSYS; } static int sco_open_input_stream_real(struct audio_hw_device *dev, audio_io_handle_t handle, audio_devices_t devices, struct audio_config *config, struct audio_stream_in **stream_in, audio_input_flags_t flags, const char *address, audio_source_t source) { struct sco_dev *sco_dev = (struct sco_dev *) dev; struct sco_stream_in *in; int chan_num, ret; size_t resample_size; DBG("config %p device flags 0x%02x", config, devices); if (sco_stream_in) { DBG("stream_in already open"); ret = -EIO; goto failed2; } in = calloc(1, sizeof(struct sco_stream_in)); if (!in) return -ENOMEM; DBG("stream %p sco fd %d mtu %u", in, sco_fd, sco_mtu); in->stream.common.get_sample_rate = in_get_sample_rate; in->stream.common.set_sample_rate = in_set_sample_rate; in->stream.common.get_buffer_size = in_get_buffer_size; in->stream.common.get_channels = in_get_channels; in->stream.common.get_format = in_get_format; in->stream.common.set_format = in_set_format; in->stream.common.standby = in_standby; in->stream.common.dump = in_dump; in->stream.common.set_parameters = in_set_parameters; in->stream.common.get_parameters = in_get_parameters; in->stream.common.add_audio_effect = in_add_audio_effect; in->stream.common.remove_audio_effect = in_remove_audio_effect; in->stream.set_gain = in_set_gain; in->stream.read = in_read; in->stream.get_input_frames_lost = in_get_input_frames_lost; #if ANDROID_VERSION >= PLATFORM_VER(5, 0, 0) if (address) { DBG("address %s", address); str2bt_bdaddr_t(address, &in->bd_addr); } #endif if (config) { DBG("config: rate %u chan mask %x format %d offload %p", config->sample_rate, config->channel_mask, config->format, &config->offload_info); in->cfg.format = config->format; in->cfg.channels = config->channel_mask; in->cfg.rate = config->sample_rate; } else { in->cfg.format = AUDIO_STREAM_DEFAULT_FORMAT; in->cfg.channels = AUDIO_CHANNEL_OUT_MONO; in->cfg.rate = AUDIO_STREAM_DEFAULT_RATE; } in->cfg.frame_num = IN_STREAM_FRAMES; if (in->cfg.rate == AUDIO_STREAM_SCO_RATE) goto skip_resampler; /* Channel numbers for resampler */ chan_num = 1; ret = create_resampler(AUDIO_STREAM_SCO_RATE, in->cfg.rate, chan_num, RESAMPLER_QUALITY_DEFAULT, NULL, &in->resampler); if (ret) { error("Failed to create resampler (%s)", strerror(-ret)); goto failed; } in->resample_frame_num = get_resample_frame_num(AUDIO_STREAM_SCO_RATE, in->cfg.rate, in->cfg.frame_num, 0); resample_size = sizeof(int16_t) * chan_num * in->resample_frame_num; in->resample_buf = malloc(resample_size); if (!in->resample_buf) { error("failed to allocate resample buffer for %d frames", in->resample_frame_num); goto failed; } DBG("Resampler: input %d output %d chan %d frames %u size %zd", AUDIO_STREAM_SCO_RATE, in->cfg.rate, chan_num, in->resample_frame_num, resample_size); skip_resampler: *stream_in = &in->stream; sco_dev->in = in; sco_stream_in = in; return 0; failed: if (in->resampler) release_resampler(in->resampler); free(in); failed2: *stream_in = NULL; sco_dev->in = NULL; sco_stream_in = NULL; return ret; } #if ANDROID_VERSION >= PLATFORM_VER(5, 0, 0) static int sco_open_input_stream(struct audio_hw_device *dev, audio_io_handle_t handle, audio_devices_t devices, struct audio_config *config, struct audio_stream_in **stream_in, audio_input_flags_t flags, const char *address, audio_source_t source) { return sco_open_input_stream_real(dev, handle, devices, config, stream_in, flags, address, source); } #else static int sco_open_input_stream(struct audio_hw_device *dev, audio_io_handle_t handle, audio_devices_t devices, struct audio_config *config, struct audio_stream_in **stream_in) { return sco_open_input_stream_real(dev, handle, devices, config, stream_in, 0, NULL, 0); } #endif static void sco_close_input_stream(struct audio_hw_device *dev, struct audio_stream_in *stream_in) { struct sco_dev *sco_dev = (struct sco_dev *) dev; struct sco_stream_in *in = (struct sco_stream_in *) stream_in; DBG("dev %p stream %p fd %d", dev, in, sco_fd); if (in->resampler) { release_resampler(in->resampler); free(in->resample_buf); } free(in); sco_dev->in = NULL; pthread_mutex_lock(&sco_mutex); sco_stream_in = NULL; if (!sco_stream_out) sco_close_socket(); pthread_mutex_unlock(&sco_mutex); } static int sco_dump(const audio_hw_device_t *device, int fd) { DBG(""); return 0; } #if ANDROID_VERSION >= PLATFORM_VER(5, 0, 0) static int set_master_mute(struct audio_hw_device *dev, bool mute) { DBG(""); return -ENOSYS; } static int get_master_mute(struct audio_hw_device *dev, bool *mute) { DBG(""); return -ENOSYS; } static int create_audio_patch(struct audio_hw_device *dev, unsigned int num_sources, const struct audio_port_config *sources, unsigned int num_sinks, const struct audio_port_config *sinks, audio_patch_handle_t *handle) { DBG(""); return -ENOSYS; } static int release_audio_patch(struct audio_hw_device *dev, audio_patch_handle_t handle) { DBG(""); return -ENOSYS; } static int get_audio_port(struct audio_hw_device *dev, struct audio_port *port) { DBG(""); return -ENOSYS; } static int set_audio_port_config(struct audio_hw_device *dev, const struct audio_port_config *config) { DBG(""); return -ENOSYS; } #endif static int sco_close(hw_device_t *device) { DBG(""); free(device); return 0; } static void *ipc_handler(void *data) { bool done = false; struct pollfd pfd; int sk; DBG(""); while (!done) { DBG("Waiting for connection ..."); sk = accept(listen_sk, NULL, NULL); if (sk < 0) { int err = errno; if (err == EINTR) continue; if (err != ECONNABORTED && err != EINVAL) error("sco: Failed to accept socket: %d (%s)", err, strerror(err)); break; } pthread_mutex_lock(&sk_mutex); ipc_sk = sk; pthread_mutex_unlock(&sk_mutex); DBG("SCO IPC: Connected"); memset(&pfd, 0, sizeof(pfd)); pfd.fd = ipc_sk; pfd.events = POLLHUP | POLLERR | POLLNVAL; /* Check if socket is still alive. Empty while loop.*/ while (poll(&pfd, 1, -1) < 0 && errno == EINTR); info("SCO HAL: Socket closed"); pthread_mutex_lock(&sk_mutex); close(ipc_sk); ipc_sk = -1; pthread_mutex_unlock(&sk_mutex); } info("Closing SCO IPC thread"); return NULL; } static int sco_ipc_init(void) { struct sockaddr_un addr; int err; int sk; DBG(""); sk = socket(PF_LOCAL, SOCK_SEQPACKET, 0); if (sk < 0) { err = -errno; error("sco: Failed to create socket: %d (%s)", -err, strerror(-err)); return err; } memset(&addr, 0, sizeof(addr)); addr.sun_family = AF_UNIX; memcpy(addr.sun_path, BLUEZ_SCO_SK_PATH, sizeof(BLUEZ_SCO_SK_PATH)); if (bind(sk, (struct sockaddr *) &addr, sizeof(addr)) < 0) { err = -errno; error("sco: Failed to bind socket: %d (%s)", -err, strerror(-err)); goto failed; } if (listen(sk, 1) < 0) { err = -errno; error("sco: Failed to listen on the socket: %d (%s)", -err, strerror(-err)); goto failed; } listen_sk = sk; err = pthread_create(&ipc_th, NULL, ipc_handler, NULL); if (err) { err = -err; ipc_th = 0; error("sco: Failed to start IPC thread: %d (%s)", -err, strerror(-err)); goto failed; } return 0; failed: close(sk); return err; } static int sco_open(const hw_module_t *module, const char *name, hw_device_t **device) { struct sco_dev *dev; int err; DBG(""); if (strcmp(name, AUDIO_HARDWARE_INTERFACE)) { error("SCO: interface %s not matching [%s]", name, AUDIO_HARDWARE_INTERFACE); return -EINVAL; } err = sco_ipc_init(); if (err < 0) return err; dev = calloc(1, sizeof(struct sco_dev)); if (!dev) return -ENOMEM; dev->dev.common.tag = HARDWARE_DEVICE_TAG; dev->dev.common.version = AUDIO_DEVICE_API_VERSION_CURRENT; dev->dev.common.module = (struct hw_module_t *) module; dev->dev.common.close = sco_close; dev->dev.init_check = sco_init_check; dev->dev.set_voice_volume = sco_set_voice_volume; dev->dev.set_master_volume = sco_set_master_volume; dev->dev.set_mode = sco_set_mode; dev->dev.set_mic_mute = sco_set_mic_mute; dev->dev.get_mic_mute = sco_get_mic_mute; dev->dev.set_parameters = sco_set_parameters; dev->dev.get_parameters = sco_get_parameters; dev->dev.get_input_buffer_size = sco_get_input_buffer_size; dev->dev.open_output_stream = sco_open_output_stream; dev->dev.close_output_stream = sco_close_output_stream; dev->dev.open_input_stream = sco_open_input_stream; dev->dev.close_input_stream = sco_close_input_stream; dev->dev.dump = sco_dump; #if ANDROID_VERSION >= PLATFORM_VER(5, 0, 0) dev->dev.set_master_mute = set_master_mute; dev->dev.get_master_mute = get_master_mute; dev->dev.create_audio_patch = create_audio_patch; dev->dev.release_audio_patch = release_audio_patch; dev->dev.get_audio_port = get_audio_port; dev->dev.set_audio_port_config = set_audio_port_config; #endif *device = &dev->dev.common; return 0; } static struct hw_module_methods_t hal_module_methods = { .open = sco_open, }; __attribute__ ((visibility("default"))) struct audio_module HAL_MODULE_INFO_SYM = { .common = { .tag = HARDWARE_MODULE_TAG, .version_major = 1, .version_minor = 0, .id = AUDIO_HARDWARE_MODULE_ID, .name = "SCO Audio HW HAL", .author = "Intel Corporation", .methods = &hal_module_methods, }, }; bluez-5.82/android/PaxHeaders/pts-hdp.txt0000644000000000000000000000005012537515745015424 xustar0020 atime=1743516876 20 ctime=1743591289 bluez-5.82/android/pts-hdp.txt0000644000000000000000000002376112537515745015116 0ustar00rootrootPTS test results for HDP PTS version: 6.1 Tested: 08-May-2015 Android version: 5.1 Results: PASS test passed FAIL test failed INC test is inconclusive N/A test is disabled due to PICS setup ------------------------------------------------------------------------------- Test Name Result Notes ------------------------------------------------------------------------------- TC_SRC_CON_BV_01_I PASS haltest: hl register_application for instance: hl register_application health intel heartrate heartrate-monitor 1 BTHL_MDEP_ROLE_SOURCE 4100 BTHL_CHANNEL_TYPE_RELIABLE testing when prompted: bluetooth ssp_reply for instance: bluetooth ssp_reply BT_SSP_VARIANT_CONSENT 1 Note: IUT must be discoverable, connectable TC_SRC_CON_BV_02_I PASS Note: IUT must be in discoverable mode TC_SRC_CON_BV_03_I PASS when prompted: bluetooth ssp_reply TC_SRC_CON_BV_04_I PASS haltest: hl connect_channel when prompted: bluetooth ssp_reply TC_SRC_CON_BV_05_I PASS when prompted: bluetooth ssp_reply Note: IUT must be in connectable mode TC_SRC_CON_BV_06_I PASS haltest: hl connect_channel when prompted: bluetooth ssp_reply TC_SRC_CON_BV_07_I PASS bluetooth start_discovery Note: PTS HDP device must be discovered TC_SRC_CON_BV_08_I PASS bluetooth remove_bond when prompted: bluetooth ssp_reply TC_SRC_CON_BV_09_I PASS haltest: hl connect_channel when prompted: bluetooth ssp_reply TC_SRC_CON_BV_10_I N/A TC_SRC_CC_BV_01_C PASS haltest: hl connect_channel when prompted: bluetooth ssp_reply TC_SRC_CC_BV_02_C PASS when prompted: bluetooth ssp_reply Note: IUT must be discoverable, connectable TC_SRC_CC_BV_03_C PASS haltest: hl register_application bluez-android Bluez bluez-hdp health-device-profile 1 BTHL_MDEP_ROLE_SOURCE 4100 BTHL_CHANNEL_TYPE_RELIABLE pulse-oximeter hl connect_channel when prompted: bluetooth ssp_reply TC_SRC_CC_BV_05_C PASS haltest: hl register_application bluez-android Bluez bluez-hdp health-device-profile 1 BTHL_MDEP_ROLE_SOURCE 4100 BTHL_CHANNEL_TYPE_RELIABLE pulse-oximeter Note: IUT must be discoverable, connectable TC_SRC_CC_BV_07_C PASS haltest: hl register_application bluez-android Bluez bluez-hdp health-device-profile 2 BTHL_MDEP_ROLE_SOURCE 4100 BTHL_CHANNEL_TYPE_RELIABLE pulse-oximeter BTHL_MDEP_ROLE_SOURCE 4100 BTHL_CHANNEL_TYPE_STREAMING pulse-oximeter hl connect_channel when prompted: bluetooth ssp_reply when prompted: hl connect_channel TC_SRC_CC_BV_09_C PASS haltest: hl register_application bluez-android Bluez bluez-hdp health-device-profile 2 BTHL_MDEP_ROLE_SOURCE 4100 BTHL_CHANNEL_TYPE_RELIABLE pulse-oximeter BTHL_MDEP_ROLE_SOURCE 4100 BTHL_CHANNEL_TYPE_STREAMING pulse-oximeter when prompted: bluetooth ssp_reply Note: IUT must be discoverable, connectable TC_SRC_CC_BI_12_C PASS haltest: hl register_application bluez-android Bluez bluez-hdp health-device-profile 1 BTHL_MDEP_ROLE_SOURCE 4100 BTHL_CHANNEL_TYPE_RELIABLE pulse-oximeter Note: IUT must be discoverable, connectable TC_SRC_HCT_BV_01_I PASS haltest: hl register_application bluez-android Bluez bluez-hdp health-device-profile 1 BTHL_MDEP_ROLE_SOURCE 4100 BTHL_CHANNEL_TYPE_RELIABLE pulse-oximeter hl connect_channel when prompted: bluetooth ssp_reply TC_SRC_HCT_BV_02_I PASS haltest: hl register_application bluez-android Bluez bluez-hdp health-device-profile 1 BTHL_MDEP_ROLE_SOURCE 4100 BTHL_CHANNEL_TYPE_RELIABLE pulse-oximeter Note: IUT must be discoverable, connectable TC_SRC_HCT_BV_03_I N/A TC_SRC_HCT_BV_04_I PASS haltest: hl register_application bluez-android Bluez bluez-hdp health-device-profile 1 BTHL_MDEP_ROLE_SOURCE 4100 BTHL_CHANNEL_TYPE_RELIABLE pulse-oximeter when prompted: bluetooth ssp_reply Note: IUT must be discoverable, connectable TC_SRC_HCT_BV_05_C N/A TC_SRC_HCT_BV_06_C PASS haltest: hl register_application bluez-android Bluez bluez-hdp health-device-profile 1 BTHL_MDEP_ROLE_SOURCE 4100 BTHL_CHANNEL_TYPE_RELIABLE pulse-oximeter when prompted: bluetooth ssp_reply Note: IUT must be discoverable, connectable TC_SRC_HCT_BV_07_C PASS haltest: hl register_application bluez-android Bluez bluez-hdp health-device-profile 1 BTHL_MDEP_ROLE_SOURCE 4100 BTHL_CHANNEL_TYPE_RELIABLE pulse-oximeter TC_SRC_DE_BV_01_I N/A TC_SRC_DE_BV_02_I PASS haltest: hl register_application bluez-android Bluez bluez-hdp health-device-profile 1 BTHL_MDEP_ROLE_SOURCE 4100 BTHL_CHANNEL_TYPE_RELIABLE pulse-oximeter Note: IUT must be discoverable, connectable TC_SRC_DEP_BV_01_I N/A TC_SRC_DEP_BV_02_I N/A TC_SNK_CON_BV_01_I PASS haltest: hl register_application for instance: hl register_application health intel heartrate heartrate-monitor 1 BTHL_MDEP_ROLE_SINK 4100 BTHL_CHANNEL_TYPE_RELIABLE testing when prompted: bluetooth ssp_reply for instance: bluetooth ssp_reply BT_SSP_VARIANT_CONSENT 1 Note: IUT must be discoverable, connectable TC_SNK_CON_BV_02_I PASS Note: IUT must be discoverable, connectable TC_SNK_CON_BV_03_I PASS when prompted: bluetooth ssp_reply Note: IUT must be discoverable, connectable TC_SNK_CON_BV_04_I PASS haltest: hl connect_channel when prompted: bluetooth ssp_reply Note: IUT must be discoverable, connectable TC_SNK_CON_BV_05_I PASS when prompted: bluetooth ssp_reply TC_SNK_CON_BV_06_I PASS haltest: hl connect_channel when prompted: bluetooth ssp_reply TC_SNK_CON_BV_07_I PASS bluetooth start_discovery TC_SNK_CON_BV_08_I PASS bluetooth remove_bond when prompted: bluetooth ssp_reply Note: IUT must be discoverable, connectable TC_SNK_CON_BV_09_I PASS haltest: hl connect_channel when prompted: bluetooth ssp_reply TC_SNK_CON_BV_10_I N/A TC_SNK_CC_BV_01_C PASS haltest: hl connect_channel when prompted: bluetooth ssp_reply TC_SNK_CC_BV_02_C PASS when prompted: bluetooth ssp_reply Note: IUT must be discoverable, connectable TC_SNK_CC_BV_04_C PASS haltest: hl register_application bluez-android Bluez bluez-hdp health-device-profile 1 BTHL_MDEP_ROLE_SINK 4100 BTHL_CHANNEL_TYPE_RELIABLE pulse-oximeter hl connect_channel when prompted: bluetooth ssp_reply TC_SNK_CC_BV_06_C PASS haltest: hl register_application bluez-android Bluez bluez-hdp health-device-profile 2 BTHL_MDEP_ROLE_SINK 4100 BTHL_CHANNEL_TYPE_RELIABLE pulse-oximeter BTHL_MDEP_ROLE_SINK 4100 BTHL_CHANNEL_TYPE_STREAMING pulse-oximeter TC_SNK_CC_BV_08_C PASS haltest: hl register_application bluez-android Bluez bluez-hdp health-device-profile 2 BTHL_MDEP_ROLE_SINK 4100 BTHL_CHANNEL_TYPE_RELIABLE pulse-oximeter BTHL_MDEP_ROLE_SINK 4100 BTHL_CHANNEL_TYPE_STREAMING pulse-oximeter hl connect_channel when prompted: bluetooth ssp_reply when prompted: hl connect_channel TC_SNK_CC_BV_10_C PASS haltest: hl register_application bluez-android Bluez bluez-hdp health-device-profile 2 BTHL_MDEP_ROLE_SINK 4100 BTHL_CHANNEL_TYPE_RELIABLE pulse-oximeter BTHL_MDEP_ROLE_SINK 4100 BTHL_CHANNEL_TYPE_STREAMING pulse-oximeter Note: IUT must be discoverable, connectable TC_SNK_CC_BI_11_C PASS haltest: hl register_application bluez-android Bluez bluez-hdp health-device-profile 1 BTHL_MDEP_ROLE_SINK 4100 BTHL_CHANNEL_TYPE_RELIABLE pulse-oximeter Note: IUT must be discoverable, connectable TC_SNK_HCT_BV_01_I PASS haltest: hl register_application bluez-android Bluez bluez-hdp health-device-profile 1 BTHL_MDEP_ROLE_SINK 4100 BTHL_CHANNEL_TYPE_RELIABLE pulse-oximeter hl connect_channel when prompted: bluetooth ssp_reply TC_SNK_HCT_BV_02_I PASS haltest: hl register_application bluez-android Bluez bluez-hdp health-device-profile 1 BTHL_MDEP_ROLE_SINK 4100 BTHL_CHANNEL_TYPE_RELIABLE pulse-oximeter Note: IUT must be discoverable, connectable TC_SNK_HCT_BV_03_I N/A TC_SNK_HCT_BV_04_I PASS haltest: hl register_application bluez-android Bluez bluez-hdp health-device-profile 1 BTHL_MDEP_ROLE_SINK 4100 BTHL_CHANNEL_TYPE_RELIABLE pulse-oximeter when prompted: bluetooth ssp_reply Note: IUT must be discoverable, connectable TC_SNK_HCT_BV_05_C N/A TC_SNK_HCT_BV_06_C PASS haltest: hl register_application bluez-android Bluez bluez-hdp health-device-profile 1 BTHL_MDEP_ROLE_SINK 4100 BTHL_CHANNEL_TYPE_RELIABLE pulse-oximeter Note: IUT must be discoverable, connectable TC_SNK_HCT_BV_07_C PASS haltest: hl register_application bluez-android Bluez bluez-hdp health-device-profile 1 BTHL_MDEP_ROLE_SINK 4100 BTHL_CHANNEL_TYPE_RELIABLE pulse-oximeter Note: IUT must be discoverable, connectable TC_SNK_DE_BV_01_I N/A TC_SNK_DE_BV_02_I PASS haltest: hl register_application bluez-android Bluez bluez-hdp health-device-profile 1 BTHL_MDEP_ROLE_SINK 4100 BTHL_CHANNEL_TYPE_RELIABLE pulse-oximeter Note: IUT must be discoverable, connectable TC_SNK_DEP_BV_03_I N/A TC_SNK_DEP_BV_04_I N/A ------------------------------------------------------------------------------- bluez-5.82/android/PaxHeaders/hal-bluetooth.c0000644000000000000000000000005014572354773016222 xustar0020 atime=1743516862 20 ctime=1743591276 bluez-5.82/android/hal-bluetooth.c0000644000000000000000000006517714572354773015723 0ustar00rootroot// SPDX-License-Identifier: Apache-2.0 /* * Copyright (C) 2013 Intel Corporation * */ #define _GNU_SOURCE #include #include #include #include #include #include #include "hal-log.h" #include "hal.h" #include "hal-msg.h" #include "ipc-common.h" #include "hal-ipc.h" #include "hal-utils.h" static const bt_callbacks_t *bt_hal_cbacks = NULL; #define enum_prop_to_hal(prop, hal_prop, type) do { \ static type e; \ prop.val = &e; \ prop.len = sizeof(e); \ e = *((uint8_t *) (hal_prop->val)); \ } while (0) #define enum_prop_from_hal(prop, hal_len, hal_val, enum_type) do { \ enum_type e; \ if (prop->len != sizeof(e)) { \ error("invalid HAL property %u (%u vs %zu), aborting ", \ prop->type, prop->len, sizeof(e)); \ exit(EXIT_FAILURE); \ } \ memcpy(&e, prop->val, sizeof(e)); \ *((uint8_t *) hal_val) = e; /* enums are mapped to 1 byte */ \ *hal_len = 1; \ } while (0) static void handle_adapter_state_changed(void *buf, uint16_t len, int fd) { struct hal_ev_adapter_state_changed *ev = buf; DBG("state: %s", bt_state_t2str(ev->state)); if (bt_hal_cbacks->adapter_state_changed_cb) bt_hal_cbacks->adapter_state_changed_cb(ev->state); } static void adapter_props_to_hal(bt_property_t *send_props, struct hal_property *prop, uint8_t num_props, uint16_t len) { void *buf = prop; uint8_t i; for (i = 0; i < num_props; i++) { if (sizeof(*prop) + prop->len > len) { error("invalid adapter properties(%zu > %u), aborting", sizeof(*prop) + prop->len, len); exit(EXIT_FAILURE); } send_props[i].type = prop->type; switch (prop->type) { case HAL_PROP_ADAPTER_TYPE: enum_prop_to_hal(send_props[i], prop, bt_device_type_t); break; case HAL_PROP_ADAPTER_SCAN_MODE: enum_prop_to_hal(send_props[i], prop, bt_scan_mode_t); break; case HAL_PROP_ADAPTER_SERVICE_REC: default: send_props[i].len = prop->len; send_props[i].val = prop->val; break; } DBG("prop[%d]: %s", i, btproperty2str(&send_props[i])); len -= sizeof(*prop) + prop->len; buf += sizeof(*prop) + prop->len; prop = buf; } if (!len) return; error("invalid adapter properties (%u bytes left), aborting", len); exit(EXIT_FAILURE); } static void adapter_prop_from_hal(const bt_property_t *property, uint8_t *type, uint16_t *len, void *val) { /* type match IPC type */ *type = property->type; switch (property->type) { case HAL_PROP_ADAPTER_SCAN_MODE: enum_prop_from_hal(property, len, val, bt_scan_mode_t); break; case BT_PROPERTY_BDNAME: case BT_PROPERTY_BDADDR: case BT_PROPERTY_UUIDS: case BT_PROPERTY_CLASS_OF_DEVICE: case BT_PROPERTY_TYPE_OF_DEVICE: case BT_PROPERTY_SERVICE_RECORD: case BT_PROPERTY_ADAPTER_BONDED_DEVICES: case BT_PROPERTY_ADAPTER_DISCOVERY_TIMEOUT: case BT_PROPERTY_REMOTE_FRIENDLY_NAME: case BT_PROPERTY_REMOTE_RSSI: case BT_PROPERTY_REMOTE_VERSION_INFO: case BT_PROPERTY_REMOTE_DEVICE_TIMESTAMP: #if ANDROID_VERSION >= PLATFORM_VER(5, 0, 0) case BT_PROPERTY_LOCAL_LE_FEATURES: #endif default: *len = property->len; memcpy(val, property->val, property->len); break; } } static void device_props_to_hal(bt_property_t *send_props, struct hal_property *prop, uint8_t num_props, uint16_t len) { void *buf = prop; uint8_t i; for (i = 0; i < num_props; i++) { if (sizeof(*prop) + prop->len > len) { error("invalid device properties (%zu > %u), aborting", sizeof(*prop) + prop->len, len); exit(EXIT_FAILURE); } send_props[i].type = prop->type; switch (prop->type) { case HAL_PROP_DEVICE_TYPE: enum_prop_to_hal(send_props[i], prop, bt_device_type_t); break; case HAL_PROP_DEVICE_VERSION_INFO: { static bt_remote_version_t e; const struct hal_prop_device_info *p; send_props[i].val = &e; send_props[i].len = sizeof(e); p = (struct hal_prop_device_info *) prop->val; e.manufacturer = p->manufacturer; e.sub_ver = p->sub_version; e.version = p->version; } break; case HAL_PROP_DEVICE_SERVICE_REC: { static bt_service_record_t e; const struct hal_prop_device_service_rec *p; send_props[i].val = &e; send_props[i].len = sizeof(e); p = (struct hal_prop_device_service_rec *) prop->val; memset(&e, 0, sizeof(e)); memcpy(&e.channel, &p->channel, sizeof(e.channel)); memcpy(e.uuid.uu, p->uuid, sizeof(e.uuid.uu)); memcpy(e.name, p->name, p->name_len); } break; default: send_props[i].len = prop->len; send_props[i].val = prop->val; break; } len -= sizeof(*prop) + prop->len; buf += sizeof(*prop) + prop->len; prop = buf; DBG("prop[%d]: %s", i, btproperty2str(&send_props[i])); } if (!len) return; error("invalid device properties (%u bytes left), aborting", len); exit(EXIT_FAILURE); } static void handle_adapter_props_changed(void *buf, uint16_t len, int fd) { struct hal_ev_adapter_props_changed *ev = buf; bt_property_t props[ev->num_props]; DBG(""); if (!bt_hal_cbacks->adapter_properties_cb) return; len -= sizeof(*ev); adapter_props_to_hal(props, ev->props, ev->num_props, len); bt_hal_cbacks->adapter_properties_cb(ev->status, ev->num_props, props); } static void handle_bond_state_change(void *buf, uint16_t len, int fd) { struct hal_ev_bond_state_changed *ev = buf; bt_bdaddr_t *addr = (bt_bdaddr_t *) ev->bdaddr; DBG("state %u", ev->state); if (bt_hal_cbacks->bond_state_changed_cb) bt_hal_cbacks->bond_state_changed_cb(ev->status, addr, ev->state); } static void handle_pin_request(void *buf, uint16_t len, int fd) { struct hal_ev_pin_request *ev = buf; /* Those are declared as packed, so it's safe to assign pointers */ bt_bdaddr_t *addr = (bt_bdaddr_t *) ev->bdaddr; bt_bdname_t *name = (bt_bdname_t *) ev->name; DBG(""); if (bt_hal_cbacks->pin_request_cb) bt_hal_cbacks->pin_request_cb(addr, name, ev->class_of_dev); } static void handle_ssp_request(void *buf, uint16_t len, int fd) { struct hal_ev_ssp_request *ev = buf; /* Those are declared as packed, so it's safe to assign pointers */ bt_bdaddr_t *addr = (bt_bdaddr_t *) ev->bdaddr; bt_bdname_t *name = (bt_bdname_t *) ev->name; DBG(""); if (bt_hal_cbacks->ssp_request_cb) bt_hal_cbacks->ssp_request_cb(addr, name, ev->class_of_dev, ev->pairing_variant, ev->passkey); } void bt_thread_associate(void) { if (bt_hal_cbacks->thread_evt_cb) bt_hal_cbacks->thread_evt_cb(ASSOCIATE_JVM); } void bt_thread_disassociate(void) { if (bt_hal_cbacks->thread_evt_cb) bt_hal_cbacks->thread_evt_cb(DISASSOCIATE_JVM); } static bool interface_ready(void) { return bt_hal_cbacks != NULL; } static void handle_discovery_state_changed(void *buf, uint16_t len, int fd) { struct hal_ev_discovery_state_changed *ev = buf; DBG(""); if (bt_hal_cbacks->discovery_state_changed_cb) bt_hal_cbacks->discovery_state_changed_cb(ev->state); } static void handle_device_found(void *buf, uint16_t len, int fd) { struct hal_ev_device_found *ev = buf; bt_property_t props[ev->num_props]; DBG(""); if (!bt_hal_cbacks->device_found_cb) return; len -= sizeof(*ev); device_props_to_hal(props, ev->props, ev->num_props, len); bt_hal_cbacks->device_found_cb(ev->num_props, props); } static void handle_device_state_changed(void *buf, uint16_t len, int fd) { struct hal_ev_remote_device_props *ev = buf; bt_property_t props[ev->num_props]; DBG(""); if (!bt_hal_cbacks->remote_device_properties_cb) return; len -= sizeof(*ev); device_props_to_hal(props, ev->props, ev->num_props, len); bt_hal_cbacks->remote_device_properties_cb(ev->status, (bt_bdaddr_t *)ev->bdaddr, ev->num_props, props); } static void handle_acl_state_changed(void *buf, uint16_t len, int fd) { struct hal_ev_acl_state_changed *ev = buf; bt_bdaddr_t *addr = (bt_bdaddr_t *) ev->bdaddr; DBG("state %u", ev->state); if (bt_hal_cbacks->acl_state_changed_cb) bt_hal_cbacks->acl_state_changed_cb(ev->status, addr, ev->state); } static void handle_dut_mode_receive(void *buf, uint16_t len, int fd) { struct hal_ev_dut_mode_receive *ev = buf; DBG(""); if (len != sizeof(*ev) + ev->len) { error("invalid dut mode receive event (%u), aborting", len); exit(EXIT_FAILURE); } if (bt_hal_cbacks->dut_mode_recv_cb) bt_hal_cbacks->dut_mode_recv_cb(ev->opcode, ev->data, ev->len); } static void handle_le_test_mode(void *buf, uint16_t len, int fd) { struct hal_ev_le_test_mode *ev = buf; DBG(""); if (bt_hal_cbacks->le_test_mode_cb) bt_hal_cbacks->le_test_mode_cb(ev->status, ev->num_packets); } static void handle_energy_info(void *buf, uint16_t len, int fd) { #if ANDROID_VERSION >= PLATFORM_VER(5, 0, 0) struct hal_ev_energy_info *ev = buf; bt_activity_energy_info info; DBG(""); info.ctrl_state = ev->ctrl_state; info.energy_used = ev->energy_used; info.idle_time = ev->idle_time; info.rx_time = ev->rx_time; info.status = ev->status; info.tx_time = ev->status; if (bt_hal_cbacks->energy_info_cb) bt_hal_cbacks->energy_info_cb(&info); #endif } /* * handlers will be called from notification thread context, * index in table equals to 'opcode - HAL_MINIMUM_EVENT' */ static const struct hal_ipc_handler ev_handlers[] = { /* HAL_EV_ADAPTER_STATE_CHANGED */ { handle_adapter_state_changed, false, sizeof(struct hal_ev_adapter_state_changed) }, /* HAL_EV_ADAPTER_PROPS_CHANGED */ { handle_adapter_props_changed, true, sizeof(struct hal_ev_adapter_props_changed) + sizeof(struct hal_property) }, /* HAL_EV_REMOTE_DEVICE_PROPS */ { handle_device_state_changed, true, sizeof(struct hal_ev_remote_device_props) + sizeof(struct hal_property) }, /* HAL_EV_DEVICE_FOUND */ { handle_device_found, true, sizeof(struct hal_ev_device_found) + sizeof(struct hal_property) }, /* HAL_EV_DISCOVERY_STATE_CHANGED */ { handle_discovery_state_changed, false, sizeof(struct hal_ev_discovery_state_changed) }, /* HAL_EV_PIN_REQUEST */ { handle_pin_request, false, sizeof(struct hal_ev_pin_request) }, /* HAL_EV_SSP_REQUEST */ { handle_ssp_request, false, sizeof(struct hal_ev_ssp_request) }, /* HAL_EV_BOND_STATE_CHANGED */ { handle_bond_state_change, false, sizeof(struct hal_ev_bond_state_changed) }, /* HAL_EV_ACL_STATE_CHANGED */ { handle_acl_state_changed, false, sizeof(struct hal_ev_acl_state_changed) }, /* HAL_EV_DUT_MODE_RECEIVE */ { handle_dut_mode_receive, true, sizeof(struct hal_ev_dut_mode_receive) }, /* HAL_EV_LE_TEST_MODE */ { handle_le_test_mode, false, sizeof(struct hal_ev_le_test_mode) }, /* HAL_EV_ENERGY_INFO */ { handle_energy_info, false, sizeof(struct hal_ev_energy_info) }, }; static uint8_t get_mode(void) { char value[PROPERTY_VALUE_MAX]; if (get_config("mode", value, NULL) > 0) { if (!strcasecmp(value, "bredr")) return HAL_MODE_BREDR; if (!strcasecmp(value, "le")) return HAL_MODE_LE; } return HAL_MODE_DEFAULT; } static uint16_t add_prop(const char *prop, uint8_t type, void *buf) { struct hal_config_prop *hal_prop = buf; hal_prop->type = type; hal_prop->len = strlen(prop) + 1; memcpy(hal_prop->val, prop, hal_prop->len); return sizeof(*hal_prop) + hal_prop->len; } static int send_configuration(void) { char buf[IPC_MTU]; struct hal_cmd_configuration *cmd = (void *) buf; char prop[PROPERTY_VALUE_MAX]; uint16_t len = sizeof(*cmd); cmd->num = 0; if (get_config("vendor", prop, "ro.product.manufacturer") > 0) { len += add_prop(prop, HAL_CONFIG_VENDOR, buf + len); cmd->num++; } if (get_config("name", prop, "ro.product.name") > 0) { len += add_prop(prop, HAL_CONFIG_NAME, buf + len); cmd->num++; } if (get_config("model", prop, "ro.product.model") > 0) { len += add_prop(prop, HAL_CONFIG_MODEL, buf + len); cmd->num++; } if (get_config("serialno", prop, "ro.serialno") > 0) { len += add_prop(prop, HAL_CONFIG_SERIAL_NUMBER, buf + len); cmd->num++; } if (get_config("systemid", prop, NULL) > 0) { len += add_prop(prop, HAL_CONFIG_SYSTEM_ID, buf + len); cmd->num++; } if (get_config("pnpid", prop, NULL) > 0) { len += add_prop(prop, HAL_CONFIG_PNP_ID, buf + len); cmd->num++; } if (get_config("fwrev", prop, "ro.build.version.release") > 0) { len += add_prop(prop, HAL_CONFIG_FW_REV, buf + len); cmd->num++; } if (get_config("hwrev", prop, "ro.board.platform") > 0) { len += add_prop(prop, HAL_CONFIG_HW_REV, buf + len); cmd->num++; } return hal_ipc_cmd(HAL_SERVICE_ID_CORE, HAL_OP_CONFIGURATION, len, cmd, NULL, NULL, NULL); } static int init(bt_callbacks_t *callbacks) { struct hal_cmd_register_module cmd; int status; DBG(""); if (interface_ready()) return BT_STATUS_DONE; hal_ipc_register(HAL_SERVICE_ID_BLUETOOTH, ev_handlers, sizeof(ev_handlers)/sizeof(ev_handlers[0])); if (!hal_ipc_init(BLUEZ_HAL_SK_PATH, sizeof(BLUEZ_HAL_SK_PATH))) return BT_STATUS_FAIL; bt_hal_cbacks = callbacks; /* Start Android Bluetooth daemon service */ if (property_set("bluetooth.start", "daemon") < 0) { error("Failed to set bluetooth.start=daemon"); hal_ipc_cleanup(); bt_hal_cbacks = NULL; return BT_STATUS_FAIL; } if (!hal_ipc_accept()) { hal_ipc_cleanup(); bt_hal_cbacks = NULL; return BT_STATUS_FAIL; } status = send_configuration(); if (status != BT_STATUS_SUCCESS) { error("Failed to send configuration"); goto fail; } cmd.service_id = HAL_SERVICE_ID_BLUETOOTH; cmd.mode = get_mode(); cmd.max_clients = 1; status = hal_ipc_cmd(HAL_SERVICE_ID_CORE, HAL_OP_REGISTER_MODULE, sizeof(cmd), &cmd, NULL, NULL, NULL); if (status != BT_STATUS_SUCCESS) { error("Failed to register 'bluetooth' service"); goto fail; } cmd.service_id = HAL_SERVICE_ID_SOCKET; cmd.max_clients = 1; #if ANDROID_VERSION >= PLATFORM_VER(5, 0, 0) cmd.mode = HAL_MODE_SOCKET_DYNAMIC_MAP; #else cmd.mode = HAL_MODE_DEFAULT; #endif status = hal_ipc_cmd(HAL_SERVICE_ID_CORE, HAL_OP_REGISTER_MODULE, sizeof(cmd), &cmd, NULL, NULL, NULL); if (status != BT_STATUS_SUCCESS) { error("Failed to register 'socket' service"); goto fail; } return status; fail: hal_ipc_cleanup(); bt_hal_cbacks = NULL; hal_ipc_unregister(HAL_SERVICE_ID_BLUETOOTH); return status; } static int enable(void) { DBG(""); if (!interface_ready()) return BT_STATUS_NOT_READY; return hal_ipc_cmd(HAL_SERVICE_ID_BLUETOOTH, HAL_OP_ENABLE, 0, NULL, NULL, NULL, NULL); } static int disable(void) { DBG(""); if (!interface_ready()) return BT_STATUS_NOT_READY; return hal_ipc_cmd(HAL_SERVICE_ID_BLUETOOTH, HAL_OP_DISABLE, 0, NULL, NULL, NULL, NULL); } static void cleanup(void) { DBG(""); if (!interface_ready()) return; hal_ipc_cleanup(); hal_ipc_unregister(HAL_SERVICE_ID_BLUETOOTH); bt_hal_cbacks = NULL; } static int get_adapter_properties(void) { DBG(""); if (!interface_ready()) return BT_STATUS_NOT_READY; return hal_ipc_cmd(HAL_SERVICE_ID_BLUETOOTH, HAL_OP_GET_ADAPTER_PROPS, 0, NULL, NULL, NULL, NULL); } static int get_adapter_property(bt_property_type_t type) { struct hal_cmd_get_adapter_prop cmd; DBG("prop: %s (%d)", bt_property_type_t2str(type), type); if (!interface_ready()) return BT_STATUS_NOT_READY; /* type match IPC type */ cmd.type = type; return hal_ipc_cmd(HAL_SERVICE_ID_BLUETOOTH, HAL_OP_GET_ADAPTER_PROP, sizeof(cmd), &cmd, NULL, NULL, NULL); } static int set_adapter_property(const bt_property_t *property) { char buf[IPC_MTU]; struct hal_cmd_set_adapter_prop *cmd = (void *) buf; uint16_t len_ret; size_t len; DBG("prop: %s", btproperty2str(property)); if (!interface_ready()) return BT_STATUS_NOT_READY; adapter_prop_from_hal(property, &cmd->type, &len_ret, cmd->val); cmd->len = len_ret; len = sizeof(*cmd) + cmd->len; return hal_ipc_cmd(HAL_SERVICE_ID_BLUETOOTH, HAL_OP_SET_ADAPTER_PROP, len, cmd, NULL, NULL, NULL); } static int get_remote_device_properties(bt_bdaddr_t *remote_addr) { struct hal_cmd_get_remote_device_props cmd; DBG("bdaddr: %s", bdaddr2str(remote_addr)); if (!interface_ready()) return BT_STATUS_NOT_READY; memcpy(cmd.bdaddr, remote_addr, sizeof(cmd.bdaddr)); return hal_ipc_cmd(HAL_SERVICE_ID_BLUETOOTH, HAL_OP_GET_REMOTE_DEVICE_PROPS, sizeof(cmd), &cmd, NULL, NULL, NULL); } static int get_remote_device_property(bt_bdaddr_t *remote_addr, bt_property_type_t type) { struct hal_cmd_get_remote_device_prop cmd; DBG("bdaddr: %s prop: %s", bdaddr2str(remote_addr), bt_property_type_t2str(type)); if (!interface_ready()) return BT_STATUS_NOT_READY; memcpy(cmd.bdaddr, remote_addr, sizeof(cmd.bdaddr)); /* type match IPC type */ cmd.type = type; return hal_ipc_cmd(HAL_SERVICE_ID_BLUETOOTH, HAL_OP_GET_REMOTE_DEVICE_PROP, sizeof(cmd), &cmd, NULL, NULL, NULL); } static int set_remote_device_property(bt_bdaddr_t *remote_addr, const bt_property_t *property) { char buf[IPC_MTU]; struct hal_cmd_set_remote_device_prop *cmd = (void *) buf; size_t len; DBG("bdaddr: %s prop: %s", bdaddr2str(remote_addr), bt_property_type_t2str(property->type)); if (!interface_ready()) return BT_STATUS_NOT_READY; memcpy(cmd->bdaddr, remote_addr, sizeof(cmd->bdaddr)); /* type match IPC type */ cmd->type = property->type; cmd->len = property->len; memcpy(cmd->val, property->val, property->len); len = sizeof(*cmd) + cmd->len; return hal_ipc_cmd(HAL_SERVICE_ID_BLUETOOTH, HAL_OP_SET_REMOTE_DEVICE_PROP, len, cmd, NULL, NULL, NULL); } static int get_remote_service_record(bt_bdaddr_t *remote_addr, bt_uuid_t *uuid) { struct hal_cmd_get_remote_service_rec cmd; DBG("bdaddr: %s", bdaddr2str(remote_addr)); if (!interface_ready()) return BT_STATUS_NOT_READY; memcpy(cmd.bdaddr, remote_addr, sizeof(cmd.bdaddr)); memcpy(cmd.uuid, uuid, sizeof(cmd.uuid)); return hal_ipc_cmd(HAL_SERVICE_ID_BLUETOOTH, HAL_OP_GET_REMOTE_SERVICE_REC, sizeof(cmd), &cmd, NULL, NULL, NULL); } static int get_remote_services(bt_bdaddr_t *remote_addr) { struct hal_cmd_get_remote_services cmd; DBG("bdaddr: %s", bdaddr2str(remote_addr)); if (!interface_ready()) return BT_STATUS_NOT_READY; memcpy(cmd.bdaddr, remote_addr, sizeof(cmd.bdaddr)); return hal_ipc_cmd(HAL_SERVICE_ID_BLUETOOTH, HAL_OP_GET_REMOTE_SERVICES, sizeof(cmd), &cmd, NULL, NULL, NULL); } static int start_discovery(void) { DBG(""); if (!interface_ready()) return BT_STATUS_NOT_READY; return hal_ipc_cmd(HAL_SERVICE_ID_BLUETOOTH, HAL_OP_START_DISCOVERY, 0, NULL, NULL, NULL, NULL); } static int cancel_discovery(void) { DBG(""); if (!interface_ready()) return BT_STATUS_NOT_READY; return hal_ipc_cmd(HAL_SERVICE_ID_BLUETOOTH, HAL_OP_CANCEL_DISCOVERY, 0, NULL, NULL, NULL, NULL); } static int create_bond_real(const bt_bdaddr_t *bd_addr, int transport) { struct hal_cmd_create_bond cmd; DBG("bdaddr: %s", bdaddr2str(bd_addr)); if (!interface_ready()) return BT_STATUS_NOT_READY; cmd.transport = transport; memcpy(cmd.bdaddr, bd_addr, sizeof(cmd.bdaddr)); return hal_ipc_cmd(HAL_SERVICE_ID_BLUETOOTH, HAL_OP_CREATE_BOND, sizeof(cmd), &cmd, NULL, NULL, NULL); } #if ANDROID_VERSION >= PLATFORM_VER(5, 0, 0) static int create_bond(const bt_bdaddr_t *bd_addr, int transport) { return create_bond_real(bd_addr, transport); } #else static int create_bond(const bt_bdaddr_t *bd_addr) { return create_bond_real(bd_addr, BT_TRANSPORT_UNKNOWN); } #endif static int cancel_bond(const bt_bdaddr_t *bd_addr) { struct hal_cmd_cancel_bond cmd; DBG("bdaddr: %s", bdaddr2str(bd_addr)); if (!interface_ready()) return BT_STATUS_NOT_READY; memcpy(cmd.bdaddr, bd_addr, sizeof(cmd.bdaddr)); return hal_ipc_cmd(HAL_SERVICE_ID_BLUETOOTH, HAL_OP_CANCEL_BOND, sizeof(cmd), &cmd, NULL, NULL, NULL); } static int remove_bond(const bt_bdaddr_t *bd_addr) { struct hal_cmd_remove_bond cmd; DBG("bdaddr: %s", bdaddr2str(bd_addr)); if (!interface_ready()) return BT_STATUS_NOT_READY; memcpy(cmd.bdaddr, bd_addr, sizeof(cmd.bdaddr)); return hal_ipc_cmd(HAL_SERVICE_ID_BLUETOOTH, HAL_OP_REMOVE_BOND, sizeof(cmd), &cmd, NULL, NULL, NULL); } static int pin_reply(const bt_bdaddr_t *bd_addr, uint8_t accept, uint8_t pin_len, bt_pin_code_t *pin_code) { struct hal_cmd_pin_reply cmd; DBG("bdaddr: %s", bdaddr2str(bd_addr)); if (!interface_ready()) return BT_STATUS_NOT_READY; memcpy(cmd.bdaddr, bd_addr, sizeof(cmd.bdaddr)); cmd.accept = accept; cmd.pin_len = pin_len; memcpy(cmd.pin_code, pin_code, sizeof(cmd.pin_code)); return hal_ipc_cmd(HAL_SERVICE_ID_BLUETOOTH, HAL_OP_PIN_REPLY, sizeof(cmd), &cmd, NULL, NULL, NULL); } static int ssp_reply(const bt_bdaddr_t *bd_addr, bt_ssp_variant_t variant, uint8_t accept, uint32_t passkey) { struct hal_cmd_ssp_reply cmd; DBG("bdaddr: %s", bdaddr2str(bd_addr)); if (!interface_ready()) return BT_STATUS_NOT_READY; memcpy(cmd.bdaddr, bd_addr, sizeof(cmd.bdaddr)); /* type match IPC type */ cmd.ssp_variant = variant; cmd.accept = accept; cmd.passkey = passkey; return hal_ipc_cmd(HAL_SERVICE_ID_BLUETOOTH, HAL_OP_SSP_REPLY, sizeof(cmd), &cmd, NULL, NULL, NULL); } static const void *get_profile_interface(const char *profile_id) { DBG("%s", profile_id); if (!interface_ready()) return NULL; if (!strcmp(profile_id, BT_PROFILE_SOCKETS_ID)) return bt_get_socket_interface(); if (!strcmp(profile_id, BT_PROFILE_HIDHOST_ID)) return bt_get_hidhost_interface(); if (!strcmp(profile_id, BT_PROFILE_PAN_ID)) return bt_get_pan_interface(); if (!strcmp(profile_id, BT_PROFILE_ADVANCED_AUDIO_ID)) return bt_get_a2dp_interface(); if (!strcmp(profile_id, BT_PROFILE_AV_RC_ID)) return bt_get_avrcp_interface(); if (!strcmp(profile_id, BT_PROFILE_HANDSFREE_ID)) return bt_get_handsfree_interface(); if (!strcmp(profile_id, BT_PROFILE_GATT_ID)) return bt_get_gatt_interface(); if (!strcmp(profile_id, BT_PROFILE_HEALTH_ID)) return bt_get_health_interface(); #if ANDROID_VERSION >= PLATFORM_VER(5, 0, 0) if (!strcmp(profile_id, BT_PROFILE_AV_RC_CTRL_ID)) return bt_get_avrcp_ctrl_interface(); if (!strcmp(profile_id, BT_PROFILE_HANDSFREE_CLIENT_ID)) return bt_get_hf_client_interface(); if (!strcmp(profile_id, BT_PROFILE_MAP_CLIENT_ID)) return bt_get_map_client_interface(); if (!strcmp(profile_id, BT_PROFILE_ADVANCED_AUDIO_SINK_ID)) return bt_get_a2dp_sink_interface(); #endif return NULL; } static int dut_mode_configure(uint8_t enable) { struct hal_cmd_dut_mode_conf cmd; DBG("enable %u", enable); if (!interface_ready()) return BT_STATUS_NOT_READY; cmd.enable = enable; return hal_ipc_cmd(HAL_SERVICE_ID_BLUETOOTH, HAL_OP_DUT_MODE_CONF, sizeof(cmd), &cmd, NULL, NULL, NULL); } static int dut_mode_send(uint16_t opcode, uint8_t *buf, uint8_t buf_len) { char cmd_buf[IPC_MTU]; struct hal_cmd_dut_mode_send *cmd = (void *) cmd_buf; size_t len; DBG("opcode %u len %u", opcode, buf_len); if (!interface_ready()) return BT_STATUS_NOT_READY; cmd->opcode = opcode; cmd->len = buf_len; memcpy(cmd->data, buf, cmd->len); len = sizeof(*cmd) + cmd->len; return hal_ipc_cmd(HAL_SERVICE_ID_BLUETOOTH, HAL_OP_DUT_MODE_SEND, len, cmd, NULL, NULL, NULL); } static int le_test_mode(uint16_t opcode, uint8_t *buf, uint8_t buf_len) { char cmd_buf[IPC_MTU]; struct hal_cmd_le_test_mode *cmd = (void *) cmd_buf; size_t len; DBG("opcode %u len %u", opcode, buf_len); if (!interface_ready()) return BT_STATUS_NOT_READY; cmd->opcode = opcode; cmd->len = buf_len; memcpy(cmd->data, buf, cmd->len); len = sizeof(*cmd) + cmd->len; return hal_ipc_cmd(HAL_SERVICE_ID_BLUETOOTH, HAL_OP_LE_TEST_MODE, len, cmd, NULL, NULL, NULL); } static int config_hci_snoop_log(uint8_t enable) { const char *property; DBG("enable %u", enable); property = enable ? "bluetooth.start" : "bluetooth.stop"; if (property_set(property, "snoop") < 0) { error("Failed to set %s=snoop", property); return BT_STATUS_FAIL; } return BT_STATUS_SUCCESS; } #if ANDROID_VERSION >= PLATFORM_VER(5, 0, 0) static int get_connection_state(const bt_bdaddr_t *bd_addr) { struct hal_cmd_get_connection_state cmd; struct hal_rsp_get_connection_state rsp; size_t rsp_len = sizeof(rsp); bt_status_t status; DBG("bdaddr: %s", bdaddr2str(bd_addr)); if (!interface_ready()) return 0; memcpy(cmd.bdaddr, bd_addr, sizeof(cmd.bdaddr)); status = hal_ipc_cmd(HAL_SERVICE_ID_BLUETOOTH, HAL_OP_GET_CONNECTION_STATE, sizeof(cmd), &cmd, &rsp_len, &rsp, NULL); if (status != BT_STATUS_SUCCESS) return 0; return rsp.connection_state; } static int set_os_callouts(bt_os_callouts_t *callouts) { DBG("callouts: %p", callouts); /* TODO: implement */ return BT_STATUS_SUCCESS; } static int read_energy_info(void) { DBG(""); if (!interface_ready()) return BT_STATUS_NOT_READY; return hal_ipc_cmd(HAL_SERVICE_ID_BLUETOOTH, HAL_OP_READ_ENERGY_INFO, 0, NULL, NULL, NULL, NULL); } #endif static const bt_interface_t bluetooth_if = { .size = sizeof(bt_interface_t), .init = init, .enable = enable, .disable = disable, .cleanup = cleanup, .get_adapter_properties = get_adapter_properties, .get_adapter_property = get_adapter_property, .set_adapter_property = set_adapter_property, .get_remote_device_properties = get_remote_device_properties, .get_remote_device_property = get_remote_device_property, .set_remote_device_property = set_remote_device_property, .get_remote_service_record = get_remote_service_record, .get_remote_services = get_remote_services, .start_discovery = start_discovery, .cancel_discovery = cancel_discovery, .create_bond = create_bond, .remove_bond = remove_bond, .cancel_bond = cancel_bond, .pin_reply = pin_reply, .ssp_reply = ssp_reply, .get_profile_interface = get_profile_interface, .dut_mode_configure = dut_mode_configure, .dut_mode_send = dut_mode_send, .le_test_mode = le_test_mode, .config_hci_snoop_log = config_hci_snoop_log, #if ANDROID_VERSION >= PLATFORM_VER(5, 0, 0) .get_connection_state = get_connection_state, .set_os_callouts = set_os_callouts, .read_energy_info = read_energy_info, #endif }; static const bt_interface_t *get_bluetooth_interface(void) { DBG(""); return &bluetooth_if; } static int close_bluetooth(struct hw_device_t *device) { DBG(""); cleanup(); free(device); return 0; } static int open_bluetooth(const struct hw_module_t *module, char const *name, struct hw_device_t **device) { bluetooth_device_t *dev = malloc(sizeof(bluetooth_device_t)); DBG(""); if (!dev) { error("Failed to allocate memory for device"); return -ENOMEM; } memset(dev, 0, sizeof(bluetooth_device_t)); dev->common.tag = HARDWARE_DEVICE_TAG; dev->common.version = 0; dev->common.module = (struct hw_module_t *) module; dev->common.close = close_bluetooth; dev->get_bluetooth_interface = get_bluetooth_interface; *device = (struct hw_device_t *) dev; return 0; } static struct hw_module_methods_t bluetooth_module_methods = { .open = open_bluetooth, }; __attribute__ ((visibility("default"))) struct hw_module_t HAL_MODULE_INFO_SYM = { .tag = HARDWARE_MODULE_TAG, .version_major = 1, .version_minor = 0, .id = BT_HARDWARE_MODULE_ID, .name = "BlueZ Bluetooth stack", .author = "Intel Corporation", .methods = &bluetooth_module_methods }; bluez-5.82/android/PaxHeaders/pixit-avdtp.txt0000644000000000000000000000005012537515745016316 xustar0020 atime=1743516876 20 ctime=1743591289 bluez-5.82/android/pixit-avdtp.txt0000644000000000000000000000166012537515745016002 0ustar00rootrootAVDTP PIXIT for the PTS tool. PTS version: 6.1 * - different than PTS defaults & - should be set to IUT Bluetooth address # - should be set to PTS's bin/audio folder Required PIXIT settings ------------------------------------------------------------------------------- Parameter Name Value ------------------------------------------------------------------------------- TSPX_SNK_class_of_device 04041C TSPX_SRC_class_of_device 080418 TSPX_security_control_data TSPX_content_protection_data TSPX_content_protection_type TSPX_no_avrcp TRUE TSPX_media_directory TSPX_bd_addr_iut 11223344556677 (*&) TSPX_delete_link_key FALSE TSPX_pin_code 1234 TSPX_security_enabled TRUE (*) TSPX_time_guard 300000 TSPX_use_implicit_send TRUE TSPX_auth_password 0000 TSPX_auth_user_id PTS TSPX_l2cap_psm 1003 TSPX_rfcomm_channel 8 TSPX_no_confirmations FALSE ------------------------------------------------------------------------------- bluez-5.82/android/PaxHeaders/hal-audio.h0000644000000000000000000000005014015011623015273 xustar0020 atime=1743516862 20 ctime=1743591275 bluez-5.82/android/hal-audio.h0000644000000000000000000000342514015011623014760 0ustar00rootroot/* SPDX-License-Identifier: Apache-2.0 */ /* * Copyright (C) 2013 Intel Corporation * */ #include #include #if __BYTE_ORDER == __LITTLE_ENDIAN struct rtp_header { unsigned cc:4; unsigned x:1; unsigned p:1; unsigned v:2; unsigned pt:7; unsigned m:1; uint16_t sequence_number; uint32_t timestamp; uint32_t ssrc; uint32_t csrc[0]; } __attribute__ ((packed)); #elif __BYTE_ORDER == __BIG_ENDIAN struct rtp_header { unsigned v:2; unsigned p:1; unsigned x:1; unsigned cc:4; unsigned m:1; unsigned pt:7; uint16_t sequence_number; uint32_t timestamp; uint32_t ssrc; uint32_t csrc[0]; } __attribute__ ((packed)); #else #error "Unknown byte order" #endif struct media_packet { uint8_t data[0]; }; struct media_packet_rtp { struct rtp_header hdr; uint8_t data[0]; }; struct audio_input_config { uint32_t rate; uint32_t channels; audio_format_t format; }; struct audio_codec { uint8_t type; bool use_rtp; bool (*load) (void); void (*unload) (void); int (*get_presets) (struct audio_preset *preset, size_t *len); bool (*init) (struct audio_preset *preset, uint16_t mtu, void **codec_data); bool (*cleanup) (void *codec_data); bool (*get_config) (void *codec_data, struct audio_input_config *config); size_t (*get_buffer_size) (void *codec_data); size_t (*get_mediapacket_duration) (void *codec_data); ssize_t (*encode_mediapacket) (void *codec_data, const uint8_t *buffer, size_t len, struct media_packet *mp, size_t mp_data_len, size_t *written); bool (*update_qos) (void *codec_data, uint8_t op); }; #define QOS_POLICY_DEFAULT 0x00 #define QOS_POLICY_DECREASE 0x01 typedef const struct audio_codec * (*audio_codec_get_t) (void); const struct audio_codec *codec_sbc(void); const struct audio_codec *codec_aptx(void); bluez-5.82/android/PaxHeaders/hal-log.h0000644000000000000000000000005014015011623014753 xustar0020 atime=1743516862 20 ctime=1743591276 bluez-5.82/android/hal-log.h0000644000000000000000000000121414015011623014432 0ustar00rootroot/* SPDX-License-Identifier: Apache-2.0 */ /* * Copyright (C) 2013 Intel Corporation * */ #define LOG_TAG "BlueZ" #ifdef __BIONIC__ #include #else #include #define LOG_INFO " I" #define LOG_WARN " W" #define LOG_ERROR " E" #define LOG_DEBUG " D" #define ALOG(pri, tag, fmt, arg...) fprintf(stderr, tag pri": " fmt"\n", ##arg) #endif #define info(fmt, arg...) ALOG(LOG_INFO, LOG_TAG, fmt, ##arg) #define warn(fmt, arg...) ALOG(LOG_WARN, LOG_TAG, fmt, ##arg) #define error(fmt, arg...) ALOG(LOG_ERROR, LOG_TAG, fmt, ##arg) #define DBG(fmt, arg...) ALOG(LOG_DEBUG, LOG_TAG, "%s:%s() "fmt, __FILE__, \ __func__, ##arg) bluez-5.82/android/PaxHeaders/pics-l2cap.txt0000644000000000000000000000005012537515745016002 xustar0020 atime=1743516875 20 ctime=1743591288 bluez-5.82/android/pics-l2cap.txt0000644000000000000000000002234712537515745015473 0ustar00rootrootL2CAP PICS for the PTS tool. PTS version: 6.1 * - different than PTS defaults # - not yet implemented/supported M - mandatory O - optional Roles ------------------------------------------------------------------------------- Parameter Name Selected Description ------------------------------------------------------------------------------- TSPC_L2CAP_1_1 True Data Channel Initiator (C.3) TSPC_L2CAP_1_2 True Data Channel Acceptor (C.1) TSPC_L2CAP_1_3 True LE Master (C.2) TSPC_L2CAP_1_4 True LE Slave (C.2) TSPC_L2CAP_1_5 True LE Data Channel Initiator (C.4) TSPC_L2CAP_1_6 True LE Data Channel Acceptor (C.5) ------------------------------------------------------------------------------- C.1: Mandatory IF BR/EDR or BR/EDR/LE is supported, otherwise Excluded. C.2: Mandatory to support (at least one of TSPC_L2CAP_1_3 or TSPC_L2CAP_1_4) IF LE or BR/EDR/LE is supported, otherwise Excluded. C.3: Optional IF BR/EDR or BR/EDR/LE is supported, otherwise Excluded. C.4: Optional IF LE or BR/EDR/LE and Core Spec v4.1 or Core Spec v4.1+HS and TSPC_L2CAP_2_46 is supported, otherwise Excluded. C.5: Mandatory IF LE or BR/EDR/LE and Core Spec v4.1 or Core Spec v4.1+HS and TSPC_L2CAP_2_46 is supported, otherwise Excluded. ------------------------------------------------------------------------------- General Operation ------------------------------------------------------------------------------- Parameter Name Selected Description ------------------------------------------------------------------------------- TSPC_L2CAP_2_1 True Support of L2CAP signaling channel (C.20) TSPC_L2CAP_2_2 True Support of configuration process (C.20) TSPC_L2CAP_2_3 True Support of connection oriented data channel (C.20) TSPC_L2CAP_2_4 True Support of command echo request (C.21) TSPC_L2CAP_2_5 True Support of command echo response (C.20) TSPC_L2CAP_2_6 True Support of command information request (C.21) TSPC_L2CAP_2_7 True Support of command information response (C.20) TSPC_L2CAP_2_8 False (*) Support of a channel group (C.21) TSPC_L2CAP_2_9 False (*) Support of packet for connectionless channel (C.21) TSPC_L2CAP_2_10 False (*) Support retransmission mode (C.21) TSPC_L2CAP_2_11 False (*) Support flow control mode(C.21) TSPC_L2CAP_2_12 True Enhanced Retransmission Mode (C.1, C.13) TSPC_L2CAP_2_13 True Streaming Mode (C.1, C.14) TSPC_L2CAP_2_14 True FCS Option (C.2) TSPC_L2CAP_2_15 True Generate Local Busy Condition (C.3) TSPC_L2CAP_2_16 False (*) Send Reject (C.3) TSPC_L2CAP_2_17 True Send Selective Reject (C.3) TSPC_L2CAP_2_18 True Mandatory use of ERTM (C.4) TSPC_L2CAP_2_19 True Mandatory use of Streaming Mode (C.5) TSPC_L2CAP_2_20 True Optional use of ERTM (C.4) TSPC_L2CAP_2_21 True Optional use of Streaming Mode (C.5) TSPC_L2CAP_2_22 True Send data using SAR in ERTM (C.6) TSPC_L2CAP_2_23 True Send data using SAR in Streaming Mode (C.7) TSPC_L2CAP_2_24 True Actively request Basic Mode for a PSM that supports the use of ERTM or Streaming Mode (C.8) TSPC_L2CAP_2_25 True Supports performing L2CAP channel mode configuration fallback from SM to ERTM (C.9) TSPC_L2CAP_2_26 True Supports sending more than one unacknowledged I-Frame when operating in ERTM (C.10) TSPC_L2CAP_2_27 True Supports sending more than three unacknowledged I-Frame when operating in ERTM (C.10) TSPC_L2CAP_2_28 True Supports configuring the peer TxWindow greater than 1 (C.11) TSPC_L2CAP_2_29 False (*) AMP Support (C.12) TSPC_L2CAP_2_30 True Fixed Channel Support (C.12) TSPC_L2CAP_2_31 False (*) AMP Manager Support (C.12) TSPC_L2CAP_2_32 False (*) ERTM over AMP (C.12) TSPC_L2CAP_2_33 False (*) Streaming Mode Source over AMP Support (C.15) TSPC_L2CAP_2_34 False (*) Streaming Mode Sink over AMP Support (C.15) TSPC_L2CAP_2_35 True Unicast Connectionless Data, Reception (C.1, C.16) TSPC_L2CAP_2_36 True Ability to transmit an unencrypted packet over a Unicast connectionless L2CAP channel (C.16) TSPC_L2CAP_2_37 True Ability to transmit an encrypted packet over a Unicast connectionless L2CAP channel (C.16) TSPC_L2CAP_2_38 False (*) Extended Flow Specification for BR/EDR (C.8) TSPC_L2CAP_2_39 False (*) Extended Window Size (C.8) TSPC_L2CAP_2_40 True Support of Low Energy signaling channel (C.17) TSPC_L2CAP_2_41 True Support of command reject (C.17) TSPC_L2CAP_2_42 True Send Connection Parameter Update Request (C.18) TSPC_L2CAP_2_43 True Send Connection Parameter Update Response (C.19) TSPC_L2CAP_2_44 False (*) Extended Flow Specification for AMP (C.22) TSPC_L2CAP_2_45 True Send disconnect request command (C.21) TSCP_L2CAP_2_46 True Support LE Credit Based Flow Control Mode (C.23) TSCP_L2CAP_2_47 True Support for LE Data Channel (C.24) ------------------------------------------------------------------------------- C.1: Mandatory to support at least one of TSPC_L2CAP_2_12 OR TSPC_L2CAP_2_13 OR TSPC_L2CAP_2_35 IF BR/EDR OR BR/EDR/LE AND SUM_ICS 31/7 (CSA1) OR Core Spec v3.0 or later is supported, ELSE Excluded C.2: Optional IF TSPC_L2CAP_2_12 OR TSPC_L2CAP_2_13 is claimed, ELSE Excluded. C.3: Optional IF TSPC_L2CAP_2_12 AND TSPC_L2CAP_2_28 is claimed, ELSE Excluded. C.4: IF TSPC_L2CAP_2_12 is claimed THEN either TSPC_L2CAP_2_18 OR TSPC_L2CAP_2_20 is Mandatory, ELSE Excluded. C.5: IF TSPC_L2CAP_2_13 is claimed THEN either TSPC_L2CAP_2_19 OR TSPC_L2CAP_2_21 are Mandatory, ELSE Excluded. C.6: Optional IF TSPC_L2CAP_2_12 is claimed, ELSE Excluded. C.7: Optional IF TSPC_L2CAP_2_13 is claimed, ELSE Excluded. C.8: Optional IF TSPC_L2CAP_2_12 OR TSPC_L2CAP_2_13 is claimed, ELSE Excluded. C.9: Mandatory IF TSPC_L2CAP_2_12 AND TSPC_L2CAP_2_13 AND TSPC_L2CAP_2_21 is claimed, ELSE Excluded. C.10: Optional IF TSPC_L2CAP_2_12 is claimed, ELSE Excluded. C.11: Optional IF TSPC_L2CAP_2_12 is claimed, ELSE Excluded. C.12: Mandatory IF Core Spec v3.0+HS OR Core Spec v4.0+HS OR Core Spec v4.1+HS OR Core Spec v4.2+HS is claimed, ELSE Optional. C.13: Mandatory IF Core Spec v3.0+HS OR Core Spec v4.0+HS OR Core Spec v4.1+HS OR Core Spec v4.2+HS is claimed, ELSE Optional. C.14: Optional IF Core Spec v3.0 OR or later is claimed, ELSE Excluded. C.15: Optional IF TSPC_L2CAP_2_29 is claimed, ELSE Excluded. C.16: Optional IF Core Spec v3.0 or later is claimed, ELSE Excluded. C.17: Mandatory IF LE OR BR/EDR/LE is claimed, ELSE Excluded. C.18: Optional IF Core Spec 4.0 OR TSPC_L2CAP_1_4 is claimed, ELSE Excluded. C.19: Mandatory IF Core Spec 4.0 AND TSPC_L2CAP_1_3 is claimed, ELSE Excluded. C.20: Mandatory IF BR/EDR OR BR/EDR/LE, is claimed, ELSE Excluded C.21: Optional IF BR/EDR OR BR/EDR/LE, is claimed, ELSE Excluded. C.22: Mandatory IF TSPC_L2CAP_2_29 is claimed, ELSE Excluded. C.23: Optional LE OR BR/EDR/LE AND Core Spec v4.1 OR Core Spec v4.1+HS OR Core Spec v4.2 OR Core Spec v4.2+HS is supported, otherwise Excluded. C.24: Mandatory IF TSPC_L2CAP_2_46 is supported, otherwise Excluded. ------------------------------------------------------------------------------- Configurable Parameters ------------------------------------------------------------------------------- Parameter Name Selected Description ------------------------------------------------------------------------------- TSPC_L2CAP_3_1 True Support of RTX timer (M) TSPC_L2CAP_3_2 True Support of ERTX timer (C.4) TSPC_L2CAP_3_3 True Support minimum MTU size 48 octets (C.4) TSPC_L2CAP_3_4 True Support MTU size larger than 48 octets (C.5) TSPC_L2CAP_3_5 True Support of flush timeout value for reliable channel (C.4) TSPC_L2CAP_3_6 False (*) Support of flush timeout value for unreliable channel (C.5) TSPC_L2CAP_3_7 False (*) Support of bi-directional quality of service (QoS) option field (C.1) TSPC_L2CAP_3_8 False (*) Negotiate QoS service type (C.5) TSPC_L2CAP_3_9 False (*) Negotiate and support service type ‘No traffic’ (C.2) TSPC_L2CAP_3_10 False (*) Negotiate and support service type ‘Best effort’ (C.3) TSPC_L2CAP_3_11 False (*) Negotiate and support service type ‘Guaranteed’ (C.2) TSPC_L2CAP_3_12 True Support minimum MTU size 23 octets (C.6) TSPC_L2CAP_3_13 False (*) Negotiate and support service type ‘No traffic’ for Extended Flow Specification (C.7) TSPC_L2CAP_3_14 False (*) Negotiate and support service type ‘Best Effort' for Extended Flow Specification (C.8) TSPC_L2CAP_3_15 False (*) Negotiate and support service type ‘Guaranteed’ for Extended Flow Specification (C.9) TSPC_L2CAP_3_16 True Support Multiple Simultaneous LE Data Channels (C.10) ------------------------------------------------------------------------------- C.1: Mandatory if TSPC_L2CAP_3_8 is supported, ELSE Optional. C.2: Optional if TSPC_L2CAP_3_8 is supported, ELSE Excluded. C.3: Mandatory if TSPC_L2CAP_3_8 is supported, ELSE Excluded. C.4: Mandatory IF BR/EDR OR BR/EDR/LE is claimed, ELSE Excluded. C.5: Optional IF BR/EDR OR BR/EDR/LE is claimed, ELSE Excluded. C.6: Mandatory IF LE OR BR/EDR/LE is claimed, ELSE Excluded. C.7: Optional if TSPC_L2CAP_2_44 OR TSPC_L2CAP_2_38 is supported, ELSE Excluded. C.8: Mandatory if TSPC_L2CAP_2_44 OR TSPC_L2CAP_2_38 is supported, ELSE Excluded. C.9: Optional if TSPC_L2CAP_2_44 OR TSPC_L2CAP_2_38 is supported, ELSE Excluded. C.10: Optional IF TSPC_L2CAP_2_47 AND Core Spec 4.1 is supported, otherwise Excluded. ------------------------------------------------------------------------------- bluez-5.82/android/PaxHeaders/handsfree.h0000644000000000000000000000005014015011623015367 xustar0020 atime=1743516866 20 ctime=1743591279 bluez-5.82/android/handsfree.h0000644000000000000000000000050614015011623015051 0ustar00rootroot/* SPDX-License-Identifier: LGPL-2.1-or-later */ /* * * BlueZ - Bluetooth protocol stack for Linux * * Copyright (C) 2013-2014 Intel Corporation. All rights reserved. * * */ bool bt_handsfree_register(struct ipc *ipc, const bdaddr_t *addr, uint8_t mode, int max_clients); void bt_handsfree_unregister(void); bluez-5.82/android/PaxHeaders/socket.h0000644000000000000000000000005014015011623014720 xustar0020 atime=1743516866 20 ctime=1743591278 bluez-5.82/android/socket.h0000644000000000000000000000065314015011623014405 0ustar00rootroot/* SPDX-License-Identifier: LGPL-2.1-or-later */ /* * * BlueZ - Bluetooth protocol stack for Linux * * Copyright (C) 2013-2014 Intel Corporation. All rights reserved. * * */ struct hal_sock_connect_signal { short size; uint8_t bdaddr[6]; int channel; int status; } __attribute__((packed)); void bt_socket_register(struct ipc *ipc, const bdaddr_t *addr, uint8_t mode); void bt_socket_unregister(void); bluez-5.82/android/PaxHeaders/hal-msg.h0000644000000000000000000000005014015011623014760 xustar0020 atime=1743516862 20 ctime=1743591275 bluez-5.82/android/hal-msg.h0000644000000000000000000016635314015011623014457 0ustar00rootroot/* SPDX-License-Identifier: LGPL-2.1-or-later */ /* * * BlueZ - Bluetooth protocol stack for Linux * * Copyright (C) 2013 Intel Corporation. All rights reserved. * * */ static const char BLUEZ_HAL_SK_PATH[] = "\0bluez_hal_socket"; #define HAL_MINIMUM_EVENT 0x81 #define HAL_SERVICE_ID_CORE 0 #define HAL_SERVICE_ID_BLUETOOTH 1 #define HAL_SERVICE_ID_SOCKET 2 #define HAL_SERVICE_ID_HIDHOST 3 #define HAL_SERVICE_ID_PAN 4 #define HAL_SERVICE_ID_HANDSFREE 5 #define HAL_SERVICE_ID_A2DP 6 #define HAL_SERVICE_ID_HEALTH 7 #define HAL_SERVICE_ID_AVRCP 8 #define HAL_SERVICE_ID_GATT 9 #define HAL_SERVICE_ID_HANDSFREE_CLIENT 10 #define HAL_SERVICE_ID_MAP_CLIENT 11 #define HAL_SERVICE_ID_AVRCP_CTRL 12 #define HAL_SERVICE_ID_A2DP_SINK 13 #define HAL_SERVICE_ID_MAX HAL_SERVICE_ID_A2DP_SINK /* Core Service */ #define HAL_STATUS_SUCCESS IPC_STATUS_SUCCESS #define HAL_STATUS_FAILED 0x01 #define HAL_STATUS_NOT_READY 0x02 #define HAL_STATUS_NOMEM 0x03 #define HAL_STATUS_BUSY 0x04 #define HAL_STATUS_DONE 0x05 #define HAL_STATUS_UNSUPPORTED 0x06 #define HAL_STATUS_INVALID 0x07 #define HAL_STATUS_UNHANDLED 0x08 #define HAL_STATUS_AUTH_FAILURE 0x09 #define HAL_STATUS_REMOTE_DEVICE_DOWN 0x0a #define HAL_OP_STATUS IPC_OP_STATUS #define HAL_MODE_DEFAULT 0x00 #define HAL_MODE_BREDR 0x01 #define HAL_MODE_LE 0x02 #define HAL_OP_REGISTER_MODULE 0x01 struct hal_cmd_register_module { uint8_t service_id; uint8_t mode; int32_t max_clients; } __attribute__((packed)); #define HAL_OP_UNREGISTER_MODULE 0x02 struct hal_cmd_unregister_module { uint8_t service_id; } __attribute__((packed)); #define HAL_CONFIG_VENDOR 0x00 #define HAL_CONFIG_MODEL 0x01 #define HAL_CONFIG_NAME 0x02 #define HAL_CONFIG_SERIAL_NUMBER 0x03 #define HAL_CONFIG_SYSTEM_ID 0x04 #define HAL_CONFIG_PNP_ID 0x05 #define HAL_CONFIG_FW_REV 0x06 #define HAL_CONFIG_HW_REV 0x07 struct hal_config_prop { uint8_t type; uint16_t len; uint8_t val[0]; } __attribute__((packed)); #define HAL_OP_CONFIGURATION 0x03 struct hal_cmd_configuration { uint8_t num; struct hal_config_prop props[0]; } __attribute__((packed)); /* Bluetooth Core HAL API */ #define HAL_OP_ENABLE 0x01 #define HAL_OP_DISABLE 0x02 #define HAL_OP_GET_ADAPTER_PROPS 0x03 #define HAL_OP_GET_ADAPTER_PROP 0x04 struct hal_cmd_get_adapter_prop { uint8_t type; } __attribute__((packed)); #define HAL_MAX_NAME_LENGTH 249 #define HAL_PROP_ADAPTER_NAME 0x01 #define HAL_PROP_ADAPTER_ADDR 0x02 #define HAL_PROP_ADAPTER_UUIDS 0x03 #define HAL_PROP_ADAPTER_CLASS 0x04 #define HAL_PROP_ADAPTER_TYPE 0x05 #define HAL_PROP_ADAPTER_SERVICE_REC 0x06 #define HAL_PROP_ADAPTER_SCAN_MODE 0x07 #define HAL_PROP_ADAPTER_BONDED_DEVICES 0x08 #define HAL_PROP_ADAPTER_DISC_TIMEOUT 0x09 #define HAL_PROP_DEVICE_NAME 0x01 #define HAL_PROP_DEVICE_ADDR 0x02 #define HAL_PROP_DEVICE_UUIDS 0x03 #define HAL_PROP_DEVICE_CLASS 0x04 #define HAL_PROP_DEVICE_TYPE 0x05 #define HAL_PROP_DEVICE_SERVICE_REC 0x06 struct hal_prop_device_service_rec { uint8_t uuid[16]; uint16_t channel; uint8_t name_len; uint8_t name[]; } __attribute__((packed)); #define HAL_PROP_DEVICE_FRIENDLY_NAME 0x0a #define HAL_PROP_DEVICE_RSSI 0x0b #define HAL_PROP_DEVICE_VERSION_INFO 0x0c struct hal_prop_device_info { uint8_t version; uint16_t sub_version; uint16_t manufacturer; } __attribute__((packed)); #define HAL_PROP_ADAPTER_LOCAL_LE_FEAT 0x0d #define HAL_PROP_DEVICE_TIMESTAMP 0xFF #define HAL_ADAPTER_SCAN_MODE_NONE 0x00 #define HAL_ADAPTER_SCAN_MODE_CONN 0x01 #define HAL_ADAPTER_SCAN_MODE_CONN_DISC 0x02 #define HAL_TYPE_BREDR 0x01 #define HAL_TYPE_LE 0x02 #define HAL_TYPE_DUAL 0x03 #define HAL_OP_SET_ADAPTER_PROP 0x05 struct hal_cmd_set_adapter_prop { uint8_t type; uint16_t len; uint8_t val[0]; } __attribute__((packed)); #define HAL_OP_GET_REMOTE_DEVICE_PROPS 0x06 struct hal_cmd_get_remote_device_props { uint8_t bdaddr[6]; } __attribute__((packed)); #define HAL_OP_GET_REMOTE_DEVICE_PROP 0x07 struct hal_cmd_get_remote_device_prop { uint8_t bdaddr[6]; uint8_t type; } __attribute__((packed)); #define HAL_OP_SET_REMOTE_DEVICE_PROP 0x08 struct hal_cmd_set_remote_device_prop { uint8_t bdaddr[6]; uint8_t type; uint16_t len; uint8_t val[0]; } __attribute__((packed)); #define HAL_OP_GET_REMOTE_SERVICE_REC 0x09 struct hal_cmd_get_remote_service_rec { uint8_t bdaddr[6]; uint8_t uuid[16]; } __attribute__((packed)); #define HAL_OP_GET_REMOTE_SERVICES 0x0a struct hal_cmd_get_remote_services { uint8_t bdaddr[6]; } __attribute__((packed)); #define HAL_OP_START_DISCOVERY 0x0b #define HAL_OP_CANCEL_DISCOVERY 0x0c #define BT_TRANSPORT_UNKNOWN 0x00 #define BT_TRANSPORT_BR_EDR 0x01 #define BT_TRANSPORT_LE 0x02 #define HAL_OP_CREATE_BOND 0x0d struct hal_cmd_create_bond { uint8_t bdaddr[6]; uint8_t transport; } __attribute__((packed)); #define HAL_OP_REMOVE_BOND 0x0e struct hal_cmd_remove_bond { uint8_t bdaddr[6]; } __attribute__((packed)); #define HAL_OP_CANCEL_BOND 0x0f struct hal_cmd_cancel_bond { uint8_t bdaddr[6]; } __attribute__((packed)); #define HAL_OP_PIN_REPLY 0x10 struct hal_cmd_pin_reply { uint8_t bdaddr[6]; uint8_t accept; uint8_t pin_len; uint8_t pin_code[16]; } __attribute__((packed)); #define HAL_SSP_VARIANT_CONFIRM 0x00 #define HAL_SSP_VARIANT_ENTRY 0x01 #define HAL_SSP_VARIANT_CONSENT 0x02 #define HAL_SSP_VARIANT_NOTIF 0x03 #define HAL_OP_SSP_REPLY 0x11 struct hal_cmd_ssp_reply { uint8_t bdaddr[6]; uint8_t ssp_variant; uint8_t accept; uint32_t passkey; } __attribute__((packed)); #define HAL_OP_DUT_MODE_CONF 0x12 struct hal_cmd_dut_mode_conf { uint8_t enable; } __attribute__((packed)); #define HAL_OP_DUT_MODE_SEND 0x13 struct hal_cmd_dut_mode_send { uint16_t opcode; uint8_t len; uint8_t data[0]; } __attribute__((packed)); #define HAL_OP_LE_TEST_MODE 0x14 struct hal_cmd_le_test_mode { uint16_t opcode; uint8_t len; uint8_t data[0]; } __attribute__((packed)); #define HAL_OP_GET_CONNECTION_STATE 0x15 struct hal_cmd_get_connection_state { uint8_t bdaddr[6]; } __attribute__((packed)); struct hal_rsp_get_connection_state { int32_t connection_state; } __attribute__((packed)); #define HAL_OP_READ_ENERGY_INFO 0x16 /* Bluetooth Socket HAL api */ #define HAL_MODE_SOCKET_DEFAULT HAL_MODE_DEFAULT #define HAL_MODE_SOCKET_DYNAMIC_MAP 0x01 #define HAL_SOCK_RFCOMM 0x01 #define HAL_SOCK_SCO 0x02 #define HAL_SOCK_L2CAP 0x03 #define HAL_SOCK_FLAG_ENCRYPT 0x01 #define HAL_SOCK_FLAG_AUTH 0x02 #define HAL_OP_SOCKET_LISTEN 0x01 struct hal_cmd_socket_listen { uint8_t type; uint8_t name[256]; uint8_t uuid[16]; int32_t channel; uint8_t flags; } __attribute__((packed)); #define HAL_OP_SOCKET_CONNECT 0x02 struct hal_cmd_socket_connect { uint8_t bdaddr[6]; uint8_t type; uint8_t uuid[16]; int32_t channel; uint8_t flags; } __attribute__((packed)); /* Bluetooth HID Host HAL API */ #define HAL_OP_HIDHOST_CONNECT 0x01 struct hal_cmd_hidhost_connect { uint8_t bdaddr[6]; } __attribute__((packed)); #define HAL_OP_HIDHOST_DISCONNECT 0x02 struct hal_cmd_hidhost_disconnect { uint8_t bdaddr[6]; } __attribute__((packed)); #define HAL_OP_HIDHOST_VIRTUAL_UNPLUG 0x03 struct hal_cmd_hidhost_virtual_unplug { uint8_t bdaddr[6]; } __attribute__((packed)); #define HAL_OP_HIDHOST_SET_INFO 0x04 struct hal_cmd_hidhost_set_info { uint8_t bdaddr[6]; uint8_t attr; uint8_t subclass; uint8_t app_id; uint16_t vendor; uint16_t product; uint16_t country; uint16_t descr_len; uint8_t descr[0]; } __attribute__((packed)); #define HAL_HIDHOST_REPORT_PROTOCOL 0x00 #define HAL_HIDHOST_BOOT_PROTOCOL 0x01 #define HAL_HIDHOST_UNSUPPORTED_PROTOCOL 0xff #define HAL_OP_HIDHOST_GET_PROTOCOL 0x05 struct hal_cmd_hidhost_get_protocol { uint8_t bdaddr[6]; uint8_t mode; } __attribute__((packed)); #define HAL_OP_HIDHOST_SET_PROTOCOL 0x06 struct hal_cmd_hidhost_set_protocol { uint8_t bdaddr[6]; uint8_t mode; } __attribute__((packed)); #define HAL_HIDHOST_INPUT_REPORT 0x01 #define HAL_HIDHOST_OUTPUT_REPORT 0x02 #define HAL_HIDHOST_FEATURE_REPORT 0x03 #define HAL_OP_HIDHOST_GET_REPORT 0x07 struct hal_cmd_hidhost_get_report { uint8_t bdaddr[6]; uint8_t type; uint8_t id; uint16_t buf_size; } __attribute__((packed)); #define HAL_OP_HIDHOST_SET_REPORT 0x08 struct hal_cmd_hidhost_set_report { uint8_t bdaddr[6]; uint8_t type; uint16_t len; uint8_t data[0]; } __attribute__((packed)); #define HAL_OP_HIDHOST_SEND_DATA 0x09 struct hal_cmd_hidhost_send_data { uint8_t bdaddr[6]; uint16_t len; uint8_t data[0]; } __attribute__((packed)); /* a2dp source and sink HAL API */ #define HAL_OP_A2DP_CONNECT 0x01 struct hal_cmd_a2dp_connect { uint8_t bdaddr[6]; } __attribute__((packed)); #define HAL_OP_A2DP_DISCONNECT 0x02 struct hal_cmd_a2dp_disconnect { uint8_t bdaddr[6]; } __attribute__((packed)); /* PAN HAL API */ /* PAN Roles */ #define HAL_PAN_ROLE_NONE 0x00 #define HAL_PAN_ROLE_NAP 0x01 #define HAL_PAN_ROLE_PANU 0x02 /* PAN Control states */ #define HAL_PAN_CTRL_ENABLED 0x00 #define HAL_PAN_CTRL_DISABLED 0x01 /* PAN Connection states */ #define HAL_PAN_STATE_CONNECTED 0x00 #define HAL_PAN_STATE_CONNECTING 0x01 #define HAL_PAN_STATE_DISCONNECTED 0x02 #define HAL_PAN_STATE_DISCONNECTING 0x03 /* PAN status values */ #define HAL_PAN_STATUS_FAIL 0x01 #define HAL_PAN_STATUS_NOT_READY 0x02 #define HAL_PAN_STATUS_NO_MEMORY 0x03 #define HAL_PAN_STATUS_BUSY 0x04 #define HAL_PAN_STATUS_DONE 0x05 #define HAL_PAN_STATUS_UNSUPORTED 0x06 #define HAL_PAN_STATUS_INVAL 0x07 #define HAL_PAN_STATUS_UNHANDLED 0x08 #define HAL_PAN_STATUS_AUTH_FAILED 0x09 #define HAL_PAN_STATUS_DEVICE_DOWN 0x0A #define HAL_OP_PAN_ENABLE 0x01 struct hal_cmd_pan_enable { uint8_t local_role; } __attribute__((packed)); #define HAL_OP_PAN_GET_ROLE 0x02 struct hal_rsp_pan_get_role { uint8_t local_role; } __attribute__((packed)); #define HAL_OP_PAN_CONNECT 0x03 struct hal_cmd_pan_connect { uint8_t bdaddr[6]; uint8_t local_role; uint8_t remote_role; } __attribute__((packed)); #define HAL_OP_PAN_DISCONNECT 0x04 struct hal_cmd_pan_disconnect { uint8_t bdaddr[6]; } __attribute__((packed)); #define HAL_HEALTH_MDEP_ROLE_SOURCE 0x00 #define HAL_HEALTH_MDEP_ROLE_SINK 0x01 #define HAL_HEALTH_CHANNEL_TYPE_RELIABLE 0x00 #define HAL_HEALTH_CHANNEL_TYPE_STREAMING 0x01 #define HAL_HEALTH_CHANNEL_TYPE_ANY 0x02 #define HAL_OP_HEALTH_REG_APP 0x01 struct hal_cmd_health_reg_app { uint8_t num_of_mdep; uint16_t app_name_off; uint16_t provider_name_off; uint16_t service_name_off; uint16_t service_descr_off; uint16_t len; uint8_t data[0]; } __attribute__((packed)); struct hal_rsp_health_reg_app { uint16_t app_id; } __attribute__((packed)); #define HAL_OP_HEALTH_MDEP 0x02 struct hal_cmd_health_mdep { uint16_t app_id; uint8_t role; uint16_t data_type; uint8_t channel_type; uint16_t descr_len; uint8_t descr[0]; } __attribute__((packed)); #define HAL_OP_HEALTH_UNREG_APP 0x03 struct hal_cmd_health_unreg_app { uint16_t app_id; } __attribute__((packed)); #define HAL_OP_HEALTH_CONNECT_CHANNEL 0x04 struct hal_cmd_health_connect_channel { uint16_t app_id; uint8_t bdaddr[6]; uint8_t mdep_index; } __attribute__((packed)); struct hal_rsp_health_connect_channel { uint16_t channel_id; } __attribute__((packed)); #define HAL_OP_HEALTH_DESTROY_CHANNEL 0x05 struct hal_cmd_health_destroy_channel { uint16_t channel_id; } __attribute__((packed)); /* Handsfree HAL API */ #define HAL_MODE_HANDSFREE_HSP_ONLY HAL_MODE_DEFAULT #define HAL_MODE_HANDSFREE_HFP 0x01 #define HAL_MODE_HANDSFREE_HFP_WBS 0x02 #define HAL_OP_HANDSFREE_CONNECT 0x01 struct hal_cmd_handsfree_connect { uint8_t bdaddr[6]; } __attribute__((packed)); #define HAL_OP_HANDSFREE_DISCONNECT 0x02 struct hal_cmd_handsfree_disconnect { uint8_t bdaddr[6]; } __attribute__((packed)); #define HAL_OP_HANDSFREE_CONNECT_AUDIO 0x03 struct hal_cmd_handsfree_connect_audio { uint8_t bdaddr[6]; } __attribute__((packed)); #define HAL_OP_HANDSFREE_DISCONNECT_AUDIO 0x04 struct hal_cmd_handsfree_disconnect_audio { uint8_t bdaddr[6]; } __attribute__((packed)); #define HAL_OP_HANDSFREE_START_VR 0x05 struct hal_cmd_handsfree_start_vr { uint8_t bdaddr[6]; } __attribute__((packed)); #define HAL_OP_HANDSFREE_STOP_VR 0x06 struct hal_cmd_handsfree_stop_vr { uint8_t bdaddr[6]; } __attribute__((packed)); #define HAL_HANDSFREE_VOLUME_TYPE_SPEAKER 0x00 #define HAL_HANDSFREE_VOLUME_TYPE_MIC 0x01 #define HAL_OP_HANDSFREE_VOLUME_CONTROL 0x07 struct hal_cmd_handsfree_volume_control { uint8_t type; uint8_t volume; uint8_t bdaddr[6]; } __attribute__((packed)); #define HAL_HANDSFREE_NETWORK_STATE_NOT_AVAILABLE 0x00 #define HAL_HANDSFREE_NETWORK_STATE_AVAILABLE 0x01 #define HAL_HANDSFREE_SERVICE_TYPE_HOME 0x00 #define HAL_HANDSFREE_SERVICE_TYPE_ROAMING 0x01 #define HAL_OP_HANDSFREE_DEVICE_STATUS_NOTIF 0x08 struct hal_cmd_handsfree_device_status_notif { uint8_t state; uint8_t type; uint8_t signal; uint8_t battery; } __attribute__((packed)); #define HAL_OP_HANDSFREE_COPS_RESPONSE 0x09 struct hal_cmd_handsfree_cops_response { uint16_t len; uint8_t bdaddr[6]; uint8_t buf[0]; } __attribute__((packed)); #define HAL_HANDSFREE_CALL_STATE_ACTIVE 0x00 #define HAL_HANDSFREE_CALL_STATE_HELD 0x01 #define HAL_HANDSFREE_CALL_STATE_DIALING 0x02 #define HAL_HANDSFREE_CALL_STATE_ALERTING 0x03 #define HAL_HANDSFREE_CALL_STATE_INCOMING 0x04 #define HAL_HANDSFREE_CALL_STATE_WAITING 0x05 #define HAL_HANDSFREE_CALL_STATE_IDLE 0x06 #define HAL_OP_HANDSFREE_CIND_RESPONSE 0x0A struct hal_cmd_handsfree_cind_response { uint8_t svc; uint8_t num_active; uint8_t num_held; uint8_t state; uint8_t signal; uint8_t roam; uint8_t batt_chg; uint8_t bdaddr[6]; } __attribute__((packed)); #define HAL_OP_HANDSFREE_FORMATTED_AT_RESPONSE 0x0B struct hal_cmd_handsfree_formatted_at_response { uint16_t len; uint8_t bdaddr[6]; uint8_t buf[0]; } __attribute__((packed)); #define HAL_HANDSFREE_AT_RESPONSE_ERROR 0x00 #define HAL_HANDSFREE_AT_RESPONSE_OK 0x01 #define HAL_OP_HANDSFREE_AT_RESPONSE 0x0C struct hal_cmd_handsfree_at_response { uint8_t response; uint8_t error; uint8_t bdaddr[6]; } __attribute__((packed)); #define HAL_HANDSFREE_CALL_DIRECTION_OUTGOING 0x00 #define HAL_HANDSFREE_CALL_DIRECTION_INCOMING 0x01 #define HAL_HANDSFREE_CALL_TYPE_VOICE 0x00 #define HAL_HANDSFREE_CALL_TYPE_DATA 0x01 #define HAL_HANDSFREE_CALL_TYPE_FAX 0x02 #define HAL_HANDSFREE_CALL_MPTY_TYPE_SINGLE 0x00 #define HAL_HANDSFREE_CALL_MPTY_TYPE_MULTI 0x01 #define HAL_HANDSFREE_CALL_ADDRTYPE_UNKNOWN 0x81 #define HAL_HANDSFREE_CALL_ADDRTYPE_INTERNATIONAL 0x91 #define HAL_OP_HANDSFREE_CLCC_RESPONSE 0x0D struct hal_cmd_handsfree_clcc_response { uint8_t index; uint8_t dir; uint8_t state; uint8_t mode; uint8_t mpty; uint8_t type; uint8_t bdaddr[6]; uint16_t number_len; uint8_t number[0]; } __attribute__((packed)); #define HAL_OP_HANDSFREE_PHONE_STATE_CHANGE 0x0E struct hal_cmd_handsfree_phone_state_change { uint8_t num_active; uint8_t num_held; uint8_t state; uint8_t type; uint16_t number_len; uint8_t number[0]; } __attribute__((packed)); #define HAL_HANDSFREE_WBS_NONE 0x00 #define HAL_HANDSFREE_WBS_NO 0x01 #define HAL_HANDSFREE_WBS_YES 0x02 #define HAL_OP_HANDSFREE_CONFIGURE_WBS 0x0F struct hal_cmd_handsfree_configure_wbs { uint8_t bdaddr[6]; uint8_t config; } __attribute__((packed)); /* AVRCP TARGET HAL API */ #define HAL_AVRCP_PLAY_STATUS_STOPPED 0x00 #define HAL_AVRCP_PLAY_STATUS_PLAYING 0x01 #define HAL_AVRCP_PLAY_STATUS_PAUSED 0x02 #define HAL_AVRCP_PLAY_STATUS_FWD_SEEK 0x03 #define HAL_AVRCP_PLAY_STATUS_REV_SEEK 0x04 #define HAL_AVRCP_PLAY_STATUS_ERROR 0xff #define HAL_OP_AVRCP_GET_PLAY_STATUS 0x01 struct hal_cmd_avrcp_get_play_status { uint8_t status; uint32_t duration; uint32_t position; } __attribute__((packed)); #define HAL_AVRCP_PLAYER_ATTR_EQUALIZER 0x01 #define HAL_AVRCP_PLAYER_ATTR_REPEAT 0x02 #define HAL_AVRCP_PLAYER_ATTR_SHUFFLE 0x03 #define HAL_AVRCP_PLAYER_ATTR_SCAN 0x04 #define HAL_OP_AVRCP_LIST_PLAYER_ATTRS 0x02 struct hal_cmd_avrcp_list_player_attrs { uint8_t number; uint8_t attrs[0]; } __attribute__((packed)); #define HAL_OP_AVRCP_LIST_PLAYER_VALUES 0x03 struct hal_cmd_avrcp_list_player_values { uint8_t number; uint8_t values[0]; } __attribute__((packed)); struct hal_avrcp_player_attr_value { uint8_t attr; uint8_t value; } __attribute__((packed)); #define HAL_OP_AVRCP_GET_PLAYER_ATTRS 0x04 struct hal_cmd_avrcp_get_player_attrs { uint8_t number; struct hal_avrcp_player_attr_value attrs[0]; } __attribute__((packed)); struct hal_avrcp_player_setting_text { uint8_t id; uint8_t len; uint8_t text[0]; } __attribute__((packed)); #define HAL_OP_AVRCP_GET_PLAYER_ATTRS_TEXT 0x05 struct hal_cmd_avrcp_get_player_attrs_text { uint8_t number; struct hal_avrcp_player_setting_text attrs[0]; } __attribute__((packed)); #define HAL_OP_AVRCP_GET_PLAYER_VALUES_TEXT 0x06 struct hal_cmd_avrcp_get_player_values_text { uint8_t number; struct hal_avrcp_player_setting_text values[0]; } __attribute__((packed)); #define HAL_AVRCP_MEDIA_ATTR_TITLE 0x01 #define HAL_AVRCP_MEDIA_ATTR_ARTIST 0x02 #define HAL_AVRCP_MEDIA_ATTR_ALBUM 0x03 #define HAL_AVRCP_MEDIA_ATTR_TRACK_NUM 0x04 #define HAL_AVRCP_MEDIA_ATTR_NUM_TRACKS 0x05 #define HAL_AVRCP_MEDIA_ATTR_GENRE 0x06 #define HAL_AVRCP_MEDIA_ATTR_DURATION 0x07 #define HAL_OP_AVRCP_GET_ELEMENT_ATTRS_TEXT 0x07 struct hal_cmd_avrcp_get_element_attrs_text { uint8_t number; struct hal_avrcp_player_setting_text values[0]; } __attribute__((packed)); #define HAL_OP_AVRCP_SET_PLAYER_ATTRS_VALUE 0x08 struct hal_cmd_avrcp_set_player_attrs_value { uint8_t status; } __attribute__((packed)); #define HAL_AVRCP_EVENT_STATUS_CHANGED 0x01 #define HAL_AVRCP_EVENT_TRACK_CHANGED 0x02 #define HAL_AVRCP_EVENT_TRACK_REACHED_END 0x03 #define HAL_AVRCP_EVENT_TRACK_REACHED_START 0x04 #define HAL_AVRCP_EVENT_POSITION_CHANGED 0x05 #define HAL_AVRCP_EVENT_SETTING_CHANGED 0x08 #define HAL_AVRCP_EVENT_TYPE_INTERIM 0x00 #define HAL_AVRCP_EVENT_TYPE_CHANGED 0x01 #define HAL_OP_AVRCP_REGISTER_NOTIFICATION 0x09 struct hal_cmd_avrcp_register_notification { uint8_t event; uint8_t type; uint8_t len; uint8_t data[0]; } __attribute__((packed)); #define HAL_OP_AVRCP_SET_VOLUME 0x0a struct hal_cmd_avrcp_set_volume { uint8_t value; } __attribute__((packed)); /* AVRCP CTRL HAL API */ #define HAL_OP_AVRCP_CTRL_SEND_PASSTHROUGH 0x01 struct hal_cmd_avrcp_ctrl_send_passthrough { uint8_t bdaddr[6]; uint8_t key_code; uint8_t key_state; } __attribute__((packed)); /* GATT HAL API */ #define HAL_OP_GATT_CLIENT_REGISTER 0x01 struct hal_cmd_gatt_client_register { uint8_t uuid[16]; } __attribute__((packed)); #define HAL_OP_GATT_CLIENT_UNREGISTER 0x02 struct hal_cmd_gatt_client_unregister { int32_t client_if; } __attribute__((packed)); #define HAL_OP_GATT_CLIENT_SCAN 0x03 struct hal_cmd_gatt_client_scan { int32_t client_if; uint8_t start; } __attribute__((packed)); #define HAL_OP_GATT_CLIENT_CONNECT 0x04 struct hal_cmd_gatt_client_connect { int32_t client_if; uint8_t bdaddr[6]; uint8_t is_direct; int32_t transport; } __attribute__((packed)); #define HAL_OP_GATT_CLIENT_DISCONNECT 0x05 struct hal_cmd_gatt_client_disconnect { int32_t client_if; uint8_t bdaddr[6]; int32_t conn_id; } __attribute__((packed)); #define HAL_OP_GATT_CLIENT_LISTEN 0x06 struct hal_cmd_gatt_client_listen { int32_t client_if; uint8_t start; } __attribute__((packed)); #define HAL_OP_GATT_CLIENT_REFRESH 0x07 struct hal_cmd_gatt_client_refresh { int32_t client_if; uint8_t bdaddr[6]; } __attribute__((packed)); #define HAL_OP_GATT_CLIENT_SEARCH_SERVICE 0x08 struct hal_cmd_gatt_client_search_service { int32_t conn_id; uint8_t filtered; uint8_t filter_uuid[0]; } __attribute__((packed)); #define HAL_OP_GATT_CLIENT_GET_INCLUDED_SERVICE 0x09 struct hal_gatt_srvc_id { uint8_t uuid[16]; uint8_t inst_id; uint8_t is_primary; } __attribute__((packed)); struct hal_cmd_gatt_client_get_included_service { int32_t conn_id; struct hal_gatt_srvc_id srvc_id; uint8_t continuation; struct hal_gatt_srvc_id incl_srvc_id[0]; } __attribute__((packed)); #define HAL_OP_GATT_CLIENT_GET_CHARACTERISTIC 0x0a struct hal_gatt_gatt_id { uint8_t uuid[16]; uint8_t inst_id; } __attribute__((packed)); struct hal_cmd_gatt_client_get_characteristic { int32_t conn_id; struct hal_gatt_srvc_id srvc_id; uint8_t continuation; struct hal_gatt_gatt_id char_id[0]; } __attribute__((packed)); #define HAL_OP_GATT_CLIENT_GET_DESCRIPTOR 0x0b struct hal_cmd_gatt_client_get_descriptor { int32_t conn_id; struct hal_gatt_srvc_id srvc_id; struct hal_gatt_gatt_id char_id; uint8_t continuation; struct hal_gatt_gatt_id descr_id[0]; } __attribute__((packed)); #define HAL_OP_GATT_CLIENT_READ_CHARACTERISTIC 0x0c struct hal_cmd_gatt_client_read_characteristic { int32_t conn_id; struct hal_gatt_srvc_id srvc_id; struct hal_gatt_gatt_id char_id; int32_t auth_req; } __attribute__((packed)); #define GATT_WRITE_TYPE_NO_RESPONSE 0x01 #define GATT_WRITE_TYPE_DEFAULT 0x02 #define GATT_WRITE_TYPE_PREPARE 0x03 #define GATT_WRITE_TYPE_SIGNED 0x04 #define HAL_OP_GATT_CLIENT_WRITE_CHARACTERISTIC 0x0d struct hal_cmd_gatt_client_write_characteristic { int32_t conn_id; struct hal_gatt_srvc_id srvc_id; struct hal_gatt_gatt_id char_id; int32_t write_type; int32_t len; int32_t auth_req; uint8_t value[0]; } __attribute__((packed)); #define HAL_OP_GATT_CLIENT_READ_DESCRIPTOR 0x0e struct hal_cmd_gatt_client_read_descriptor { int32_t conn_id; struct hal_gatt_srvc_id srvc_id; struct hal_gatt_gatt_id char_id; struct hal_gatt_gatt_id descr_id; int32_t auth_req; } __attribute__((packed)); #define HAL_OP_GATT_CLIENT_WRITE_DESCRIPTOR 0x0f struct hal_cmd_gatt_client_write_descriptor { int32_t conn_id; struct hal_gatt_srvc_id srvc_id; struct hal_gatt_gatt_id char_id; struct hal_gatt_gatt_id descr_id; int32_t write_type; int32_t len; int32_t auth_req; uint8_t value[0]; } __attribute__((packed)); #define HAL_OP_GATT_CLIENT_EXECUTE_WRITE 0x10 struct hal_cmd_gatt_client_execute_write { int32_t conn_id; int32_t execute; } __attribute__((packed)); #define HAL_OP_GATT_CLIENT_REGISTER_FOR_NOTIFICATION 0x11 struct hal_cmd_gatt_client_register_for_notification { int32_t client_if; uint8_t bdaddr[6]; struct hal_gatt_srvc_id srvc_id; struct hal_gatt_gatt_id char_id; } __attribute__((packed)); #define HAL_OP_GATT_CLIENT_DEREGISTER_FOR_NOTIFICATION 0x12 struct hal_cmd_gatt_client_deregister_for_notification { int32_t client_if; uint8_t bdaddr[6]; struct hal_gatt_srvc_id srvc_id; struct hal_gatt_gatt_id char_id; } __attribute__((packed)); #define HAL_OP_GATT_CLIENT_READ_REMOTE_RSSI 0x13 struct hal_cmd_gatt_client_read_remote_rssi { int32_t client_if; uint8_t bdaddr[6]; } __attribute__((packed)); #define HAL_OP_GATT_CLIENT_GET_DEVICE_TYPE 0x14 struct hal_cmd_gatt_client_get_device_type { uint8_t bdaddr[6]; } __attribute__((packed)); struct hal_rsp_gatt_client_get_device_type { uint8_t type; } __attribute__((packed)); #define HAL_OP_GATT_CLIENT_SET_ADV_DATA 0x015 struct hal_cmd_gatt_client_set_adv_data { int32_t server_if; uint8_t set_scan_rsp; uint8_t include_name; uint8_t include_txpower; int32_t min_interval; int32_t max_interval; int32_t appearance; uint16_t manufacturer_len; uint16_t service_data_len; uint16_t service_uuid_len; uint8_t data[0]; } __attribute__((packed)); #define GATT_CLIENT_TEST_CMD_ENABLE 0x01 #define GATT_CLIENT_TEST_CMD_CONNECT 0x02 #define GATT_CLIENT_TEST_CMD_DISCONNECT 0x03 #define GATT_CLIENT_TEST_CMD_DISCOVER 0x04 #define GATT_CLIENT_TEST_CMD_READ 0xe0 #define GATT_CLIENT_TEST_CMD_WRITE 0xe1 #define GATT_CLIENT_TEST_CMD_INCREASE_SECURITY 0xe2 #define GATT_CLIENT_TEST_CMD_PAIRING_CONFIG 0xf0 #define HAL_OP_GATT_CLIENT_TEST_COMMAND 0x16 struct hal_cmd_gatt_client_test_command { int32_t command; uint8_t bda1[6]; uint8_t uuid1[16]; uint16_t u1; uint16_t u2; uint16_t u3; uint16_t u4; uint16_t u5; } __attribute__((packed)); #define HAL_OP_GATT_SERVER_REGISTER 0x17 struct hal_cmd_gatt_server_register { uint8_t uuid[16]; } __attribute__((packed)); #define HAL_OP_GATT_SERVER_UNREGISTER 0x18 struct hal_cmd_gatt_server_unregister { int32_t server_if; } __attribute__((packed)); #define HAL_OP_GATT_SERVER_CONNECT 0x19 struct hal_cmd_gatt_server_connect { int32_t server_if; uint8_t bdaddr[6]; uint8_t is_direct; int32_t transport; } __attribute__((packed)); #define HAL_OP_GATT_SERVER_DISCONNECT 0x1a struct hal_cmd_gatt_server_disconnect { int32_t server_if; uint8_t bdaddr[6]; int32_t conn_id; } __attribute__((packed)); #define HAL_OP_GATT_SERVER_ADD_SERVICE 0x1b struct hal_cmd_gatt_server_add_service { int32_t server_if; struct hal_gatt_srvc_id srvc_id; int32_t num_handles; } __attribute__((packed)); #define HAL_OP_GATT_SERVER_ADD_INC_SERVICE 0x1c struct hal_cmd_gatt_server_add_inc_service { int32_t server_if; int32_t service_handle; int32_t included_handle; } __attribute__((packed)); #define HAL_OP_GATT_SERVER_ADD_CHARACTERISTIC 0x1d struct hal_cmd_gatt_server_add_characteristic { int32_t server_if; int32_t service_handle; uint8_t uuid[16]; int32_t properties; int32_t permissions; } __attribute__((packed)); #define HAL_OP_GATT_SERVER_ADD_DESCRIPTOR 0x1e struct hal_cmd_gatt_server_add_descriptor { int32_t server_if; int32_t service_handle; uint8_t uuid[16]; int32_t permissions; } __attribute__((packed)); #define GATT_SERVER_TRANSPORT_LE_BIT 0x01 #define GATT_SERVER_TRANSPORT_BREDR_BIT 0x02 #define HAL_OP_GATT_SERVER_START_SERVICE 0x1f struct hal_cmd_gatt_server_start_service { int32_t server_if; int32_t service_handle; int32_t transport; } __attribute__((packed)); #define HAL_OP_GATT_SERVER_STOP_SERVICE 0x20 struct hal_cmd_gatt_server_stop_service { int32_t server_if; int32_t service_handle; } __attribute__((packed)); #define HAL_OP_GATT_SERVER_DELETE_SERVICE 0x21 struct hal_cmd_gatt_server_delete_service { int32_t server_if; int32_t service_handle; } __attribute__((packed)); #define HAL_OP_GATT_SERVER_SEND_INDICATION 0x22 struct hal_cmd_gatt_server_send_indication { int32_t server_if; int32_t attribute_handle; int32_t conn_id; int32_t len; int32_t confirm; uint8_t value[0]; } __attribute__((packed)); #define HAL_OP_GATT_SERVER_SEND_RESPONSE 0x23 struct hal_cmd_gatt_server_send_response { int32_t conn_id; int32_t trans_id; uint16_t handle; uint16_t offset; uint8_t auth_req; int32_t status; uint16_t len; uint8_t data[0]; } __attribute__((packed)); #define HAL_OP_GATT_CLIENT_SCAN_FILTER_SETUP 0x024 struct hal_cmd_gatt_client_scan_filter_setup { int32_t client_if; int32_t action; int32_t filter_index; int32_t features; int32_t list_type; int32_t filter_type; int32_t rssi_hi; int32_t rssi_lo; int32_t delivery_mode; int32_t found_timeout; int32_t lost_timeout; int32_t found_timeout_cnt; } __attribute__((packed)); #define HAL_OP_GATT_CLIENT_SCAN_FILTER_ADD_REMOVE 0x025 struct hal_cmd_gatt_client_scan_filter_add_remove { int32_t client_if; int32_t action; int32_t filter_type; int32_t filter_index; int32_t company_id; int32_t company_id_mask; uint8_t uuid[16]; uint8_t uuid_mask[16]; uint8_t address[6]; uint8_t address_type; int32_t data_len; int32_t mask_len; uint8_t data_mask[0]; /* common buffer for data and mask */ } __attribute__((packed)); #define HAL_OP_GATT_CLIENT_SCAN_FILTER_CLEAR 0x26 struct hal_cmd_gatt_client_scan_filter_clear { int32_t client_if; int32_t index; } __attribute__((packed)); #define HAL_OP_GATT_CLIENT_SCAN_FILTER_ENABLE 0x27 struct hal_cmd_gatt_client_scan_filter_enable { int32_t client_if; uint8_t enable; } __attribute__((packed)); #define HAL_OP_GATT_CLIENT_CONFIGURE_MTU 0x28 struct hal_cmd_gatt_client_configure_mtu { int32_t conn_id; int32_t mtu; } __attribute__((packed)); #define HAL_OP_GATT_CLIENT_CONN_PARAM_UPDATE 0x29 struct hal_cmd_gatt_client_conn_param_update { uint8_t address[6]; int32_t min_interval; int32_t max_interval; int32_t latency; int32_t timeout; } __attribute__((packed)); #define HAL_OP_GATT_CLIENT_SET_SCAN_PARAM 0x2a struct hal_cmd_gatt_client_set_scan_param { int32_t interval; int32_t window; } __attribute__((packed)); #define HAL_OP_GATT_CLIENT_SETUP_MULTI_ADV 0x2b struct hal_cmd_gatt_client_setup_multi_adv { int32_t client_if; int32_t min_interval; int32_t max_interval; int32_t type; int32_t channel_map; int32_t tx_power; int32_t timeout; } __attribute__((packed)); #define HAL_OP_GATT_CLIENT_UPDATE_MULTI_ADV 0x2c struct hal_cmd_gatt_client_update_multi_adv { int32_t client_if; int32_t min_interval; int32_t max_interval; int32_t type; int32_t channel_map; int32_t tx_power; int32_t timeout; } __attribute__((packed)); #define HAL_OP_GATT_CLIENT_SETUP_MULTI_ADV_INST 0x2d struct hal_cmd_gatt_client_setup_multi_adv_inst { int32_t client_if; uint8_t set_scan_rsp; uint8_t include_name; uint8_t include_tx_power; int32_t appearance; int32_t manufacturer_data_len; int32_t service_data_len; int32_t service_uuid_len; uint8_t data_service_uuid[0]; } __attribute__((packed)); #define HAL_OP_GATT_CLIENT_DISABLE_MULTI_ADV_INST 0x2e struct hal_cmd_gatt_client_disable_multi_adv_inst { int32_t client_if; } __attribute__((packed)); #define HAL_OP_GATT_CLIENT_CONFIGURE_BATCHSCAN 0x2f struct hal_cmd_gatt_client_configure_batchscan { int32_t client_if; int32_t full_max; int32_t trunc_max; int32_t notify_threshold; } __attribute__((packed)); #define HAL_OP_GATT_CLIENT_ENABLE_BATCHSCAN 0x30 struct hal_cmd_gatt_client_enable_batchscan { int32_t client_if; int32_t mode; int32_t interval; int32_t window; int32_t address_type; int32_t discard_rule; } __attribute__((packed)); #define HAL_OP_GATT_CLIENT_DISABLE_BATCHSCAN 0x31 struct hal_cmd_gatt_client_disable_batchscan { int32_t client_if; } __attribute__((packed)); #define HAL_OP_GATT_CLIENT_READ_BATCHSCAN_REPORTS 0x32 struct hal_cmd_gatt_client_read_batchscan_reports { int32_t client_if; int32_t scan_mode; } __attribute__((packed)); /* Handsfree client HAL API */ #define HAL_OP_HF_CLIENT_CONNECT 0x01 struct hal_cmd_hf_client_connect { uint8_t bdaddr[6]; } __attribute__((packed)); #define HAL_OP_HF_CLIENT_DISCONNECT 0x02 struct hal_cmd_hf_client_disconnect { uint8_t bdaddr[6]; } __attribute__((packed)); #define HAL_OP_HF_CLIENT_CONNECT_AUDIO 0x03 struct hal_cmd_hf_client_connect_audio { uint8_t bdaddr[6]; } __attribute__((packed)); #define HAL_OP_HF_CLIENT_DISCONNECT_AUDIO 0x04 struct hal_cmd_hf_client_disconnect_audio { uint8_t bdaddr[6]; } __attribute__((packed)); #define HAL_OP_HF_CLIENT_START_VR 0x05 #define HAL_OP_HF_CLIENT_STOP_VR 0x06 #define HF_CLIENT_VOLUME_TYPE_SPEAKER 0x00 #define HF_CLIENT_VOLUME_TYPE_MIC 0x01 #define HAL_OP_HF_CLIENT_VOLUME_CONTROL 0x07 struct hal_cmd_hf_client_volume_control { uint8_t type; uint8_t volume; } __attribute__((packed)); #define HAL_OP_HF_CLIENT_DIAL 0x08 struct hal_cmd_hf_client_dial { uint16_t number_len; uint8_t number[0]; } __attribute__((packed)); #define HAL_OP_HF_CLIENT_DIAL_MEMORY 0x09 struct hal_cmd_hf_client_dial_memory { int32_t location; } __attribute__((packed)); #define HAL_HF_CLIENT_ACTION_CHLD_0 0x00 #define HAL_HF_CLIENT_ACTION_CHLD_1 0x01 #define HAL_HF_CLIENT_ACTION_CHLD_2 0x02 #define HAL_HF_CLIENT_ACTION_CHLD_3 0x03 #define HAL_HF_CLIENT_ACTION_CHLD_4 0x04 #define HAL_HF_CLIENT_ACTION_CHLD_1x 0x05 #define HAL_HF_CLIENT_ACTION_CHLD_2x 0x06 #define HAL_HF_CLIENT_ACTION_ATA 0x07 #define HAL_HF_CLIENT_ACTION_CHUP 0x08 #define HAL_HF_CLIENT_ACTION_BRTH_0 0x09 #define HAL_HF_CLIENT_ACTION_BRTH_1 0x0a #define HAL_HF_CLIENT_ACTION_BRTH_2 0x0b #define HAL_OP_HF_CLIENT_CALL_ACTION 0x0a struct hal_cmd_hf_client_call_action { uint8_t action; int32_t index; } __attribute__((packed)); #define HAL_OP_HF_CLIENT_QUERY_CURRENT_CALLS 0x0b #define HAL_OP_HF_CLIENT_QUERY_OPERATOR_NAME 0x0c #define HAL_OP_HF_CLIENT_RETRIEVE_SUBSCR_INFO 0x0d #define HAL_OP_HF_CLIENT_SEND_DTMF 0x0e struct hal_cmd_hf_client_send_dtmf { uint8_t tone; } __attribute__((packed)); #define HAL_OP_HF_CLIENT_GET_LAST_VOICE_TAG_NUM 0x0f /* MAP CLIENT HAL API */ #define HAL_OP_MAP_CLIENT_GET_INSTANCES 0x01 struct hal_cmd_map_client_get_instances { uint8_t bdaddr[6]; } __attribute__((packed)); /* Notifications and confirmations */ #define HAL_POWER_OFF 0x00 #define HAL_POWER_ON 0x01 #define HAL_EV_ADAPTER_STATE_CHANGED 0x81 struct hal_ev_adapter_state_changed { uint8_t state; } __attribute__((packed)); #define HAL_EV_ADAPTER_PROPS_CHANGED 0x82 struct hal_property { uint8_t type; uint16_t len; uint8_t val[0]; } __attribute__((packed)); struct hal_ev_adapter_props_changed { uint8_t status; uint8_t num_props; struct hal_property props[0]; } __attribute__((packed)); #define HAL_EV_REMOTE_DEVICE_PROPS 0x83 struct hal_ev_remote_device_props { uint8_t status; uint8_t bdaddr[6]; uint8_t num_props; struct hal_property props[0]; } __attribute__((packed)); #define HAL_EV_DEVICE_FOUND 0x84 struct hal_ev_device_found { uint8_t num_props; struct hal_property props[0]; } __attribute__((packed)); #define HAL_DISCOVERY_STATE_STOPPED 0x00 #define HAL_DISCOVERY_STATE_STARTED 0x01 #define HAL_EV_DISCOVERY_STATE_CHANGED 0x85 struct hal_ev_discovery_state_changed { uint8_t state; } __attribute__((packed)); #define HAL_EV_PIN_REQUEST 0x86 struct hal_ev_pin_request { uint8_t bdaddr[6]; uint8_t name[249]; uint32_t class_of_dev; } __attribute__((packed)); #define HAL_EV_SSP_REQUEST 0x87 struct hal_ev_ssp_request { uint8_t bdaddr[6]; uint8_t name[249]; uint32_t class_of_dev; uint8_t pairing_variant; uint32_t passkey; } __attribute__((packed)); #define HAL_BOND_STATE_NONE 0 #define HAL_BOND_STATE_BONDING 1 #define HAL_BOND_STATE_BONDED 2 #define HAL_EV_BOND_STATE_CHANGED 0x88 struct hal_ev_bond_state_changed { uint8_t status; uint8_t bdaddr[6]; uint8_t state; } __attribute__((packed)); #define HAL_ACL_STATE_CONNECTED 0x00 #define HAL_ACL_STATE_DISCONNECTED 0x01 #define HAL_EV_ACL_STATE_CHANGED 0x89 struct hal_ev_acl_state_changed { uint8_t status; uint8_t bdaddr[6]; uint8_t state; } __attribute__((packed)); #define HAL_EV_DUT_MODE_RECEIVE 0x8a struct hal_ev_dut_mode_receive { uint16_t opcode; uint8_t len; uint8_t data[0]; } __attribute__((packed)); #define HAL_EV_LE_TEST_MODE 0x8b struct hal_ev_le_test_mode { uint8_t status; uint16_t num_packets; } __attribute__((packed)); #define HAL_EV_ENERGY_INFO 0x8c struct hal_ev_energy_info { uint8_t status; uint8_t ctrl_state; uint64_t tx_time; uint64_t rx_time; uint64_t idle_time; uint64_t energy_used; } __attribute__((packed)); #define HAL_HIDHOST_STATE_CONNECTED 0x00 #define HAL_HIDHOST_STATE_CONNECTING 0x01 #define HAL_HIDHOST_STATE_DISCONNECTED 0x02 #define HAL_HIDHOST_STATE_DISCONNECTING 0x03 #define HAL_HIDHOST_STATE_NO_HID 0x07 #define HAL_HIDHOST_STATE_FAILED 0x08 #define HAL_HIDHOST_STATE_UNKNOWN 0x09 #define HAL_EV_HIDHOST_CONN_STATE 0x81 struct hal_ev_hidhost_conn_state { uint8_t bdaddr[6]; uint8_t state; } __attribute__((packed)); #define HAL_HIDHOST_STATUS_OK 0x00 #define HAL_HIDHOST_HS_NOT_READY 0x01 #define HAL_HIDHOST_HS_INVALID_RAPORT_ID 0x02 #define HAL_HIDHOST_HS_TRANS_NOT_SUPPORTED 0x03 #define HAL_HIDHOST_HS_INVALID_PARAM 0x04 #define HAL_HIDHOST_HS_ERROR 0x05 #define HAL_HIDHOST_GENERAL_ERROR 0x06 #define HAL_HIDHOST_SDP_ERROR 0x07 #define HAL_HIDHOST_PROTOCOL_ERROR 0x08 #define HAL_HIDHOST_DB_ERROR 0x09 #define HAL_HIDHOST_TOD_UNSUPPORTED_ERROR 0x0a #define HAL_HIDHOST_NO_RESOURCES_ERROR 0x0b #define HAL_HIDHOST_AUTH_FAILED_ERROR 0x0c #define HAL_HIDHOST_HDL_ERROR 0x0d #define HAL_EV_HIDHOST_INFO 0x82 struct hal_ev_hidhost_info { uint8_t bdaddr[6]; uint8_t attr; uint8_t subclass; uint8_t app_id; uint16_t vendor; uint16_t product; uint16_t version; uint8_t country; uint16_t descr_len; uint8_t descr[884]; } __attribute__((packed)); #define HAL_EV_HIDHOST_PROTO_MODE 0x83 struct hal_ev_hidhost_proto_mode { uint8_t bdaddr[6]; uint8_t status; uint8_t mode; } __attribute__((packed)); #define HAL_EV_HIDHOST_IDLE_TIME 0x84 struct hal_ev_hidhost_idle_time { uint8_t bdaddr[6]; uint8_t status; uint32_t idle_rate; } __attribute__((packed)); #define HAL_EV_HIDHOST_GET_REPORT 0x85 struct hal_ev_hidhost_get_report { uint8_t bdaddr[6]; uint8_t status; uint16_t len; uint8_t data[0]; } __attribute__((packed)); #define HAL_EV_HIDHOST_VIRTUAL_UNPLUG 0x86 struct hal_ev_hidhost_virtual_unplug { uint8_t bdaddr[6]; uint8_t status; } __attribute__((packed)); #define HAL_EV_HIDHOST_HANDSHAKE 0x87 struct hal_ev_hidhost_handshake { uint8_t bdaddr[6]; uint8_t status; } __attribute__((packed)); #define HAL_EV_PAN_CTRL_STATE 0x81 struct hal_ev_pan_ctrl_state { uint8_t state; uint8_t status; uint8_t local_role; uint8_t name[17]; } __attribute__((packed)); #define HAL_EV_PAN_CONN_STATE 0x82 struct hal_ev_pan_conn_state { uint8_t state; uint8_t status; uint8_t bdaddr[6]; uint8_t local_role; uint8_t remote_role; } __attribute__((packed)); #define HAL_HEALTH_APP_REG_SUCCESS 0x00 #define HAL_HEALTH_APP_REG_FAILED 0x01 #define HAL_HEALTH_APP_DEREG_SUCCESS 0x02 #define HAL_HEALTH_APP_DEREG_FAILED 0x03 #define HAL_HEALTH_CHANNEL_CONNECTING 0x00 #define HAL_HEALTH_CHANNEL_CONNECTED 0x01 #define HAL_HEALTH_CHANNEL_DISCONNECTING 0x02 #define HAL_HEALTH_CHANNEL_DISCONNECTED 0x03 #define HAL_HEALTH_CHANNEL_DESTROYED 0x04 #define HAL_EV_HEALTH_APP_REG_STATE 0x81 struct hal_ev_health_app_reg_state { uint16_t id; uint8_t state; } __attribute__((packed)); #define HAL_EV_HEALTH_CHANNEL_STATE 0x82 struct hal_ev_health_channel_state { uint16_t app_id; uint8_t bdaddr[6]; uint8_t mdep_index; uint16_t channel_id; uint8_t channel_state; } __attribute__((packed)); #define HAL_A2DP_STATE_DISCONNECTED 0x00 #define HAL_A2DP_STATE_CONNECTING 0x01 #define HAL_A2DP_STATE_CONNECTED 0x02 #define HAL_A2DP_STATE_DISCONNECTING 0x03 #define HAL_EV_A2DP_CONN_STATE 0x81 struct hal_ev_a2dp_conn_state { uint8_t state; uint8_t bdaddr[6]; } __attribute__((packed)); #define HAL_AUDIO_SUSPEND 0x00 #define HAL_AUDIO_STOPPED 0x01 #define HAL_AUDIO_STARTED 0x02 #define HAL_EV_A2DP_AUDIO_STATE 0x82 struct hal_ev_a2dp_audio_state { uint8_t state; uint8_t bdaddr[6]; } __attribute__((packed)); #define HAL_EV_A2DP_AUDIO_CONFIG 0x83 struct hal_ev_a2dp_audio_config { uint8_t bdaddr[6]; uint32_t sample_rate; uint8_t channel_count; } __attribute__((packed)); #define HAL_EV_HANDSFREE_CONN_STATE_DISCONNECTED 0x00 #define HAL_EV_HANDSFREE_CONN_STATE_CONNECTING 0x01 #define HAL_EV_HANDSFREE_CONN_STATE_CONNECTED 0x02 #define HAL_EV_HANDSFREE_CONN_STATE_SLC_CONNECTED 0x03 #define HAL_EV_HANDSFREE_CONN_STATE_DISCONNECTING 0x04 #define HAL_EV_HANDSFREE_CONN_STATE 0x81 struct hal_ev_handsfree_conn_state { uint8_t state; uint8_t bdaddr[6]; } __attribute__((packed)); #define HAL_EV_HANDSFREE_AUDIO_STATE_DISCONNECTED 0x00 #define HAL_EV_HANDSFREE_AUDIO_STATE_CONNECTING 0x01 #define HAL_EV_HANDSFREE_AUDIO_STATE_CONNECTED 0x02 #define HAL_EV_HANDSFREE_AUDIO_STATE_DISCONNECTING 0x03 #define HAL_EV_HANDSFREE_AUDIO_STATE 0x82 struct hal_ev_handsfree_audio_state { uint8_t state; uint8_t bdaddr[6]; } __attribute__((packed)); #define HAL_HANDSFREE_VR_STOPPED 0x00 #define HAL_HANDSFREE_VR_STARTED 0x01 #define HAL_EV_HANDSFREE_VR 0x83 struct hal_ev_handsfree_vr_state { uint8_t state; uint8_t bdaddr[6]; } __attribute__((packed)); #define HAL_EV_HANDSFREE_ANSWER 0x84 struct hal_ev_handsfree_answer { uint8_t bdaddr[6]; } __attribute__((packed)); #define HAL_EV_HANDSFREE_HANGUP 0x85 struct hal_ev_handsfree_hangup { uint8_t bdaddr[6]; } __attribute__((packed)); #define HAL_EV_HANDSFREE_VOLUME 0x86 struct hal_ev_handsfree_volume { uint8_t type; uint8_t volume; uint8_t bdaddr[6]; } __attribute__((packed)); #define HAL_EV_HANDSFREE_DIAL 0x87 struct hal_ev_handsfree_dial { uint8_t bdaddr[6]; uint16_t number_len; uint8_t number[0]; } __attribute__((packed)); #define HAL_EV_HANDSFREE_DTMF 0x88 struct hal_ev_handsfree_dtmf { uint8_t tone; uint8_t bdaddr[6]; } __attribute__((packed)); #define HAL_HANDSFREE_NREC_STOP 0x00 #define HAL_HANDSFREE_NREC_START 0x01 #define HAL_EV_HANDSFREE_NREC 0x89 struct hal_ev_handsfree_nrec { uint8_t nrec; uint8_t bdaddr[6]; } __attribute__((packed)); #define HAL_HANDSFREE_CHLD_TYPE_RELEASEHELD 0x00 #define HAL_HANDSFREE_CHLD_TYPE_RELEASEACTIVE_ACCEPTHELD 0x01 #define HAL_HANDSFREE_CHLD_TYPE_HOLDACTIVE_ACCEPTHELD 0x02 #define HAL_HANDSFREE_CHLD_TYPE_ADDHELDTOCONF 0x03 #define HAL_EV_HANDSFREE_CHLD 0x8A struct hal_ev_handsfree_chld { uint8_t chld; uint8_t bdaddr[6]; } __attribute__((packed)); #define HAL_EV_HANDSFREE_CNUM 0x8B struct hal_ev_handsfree_cnum { uint8_t bdaddr[6]; } __attribute__((packed)); #define HAL_EV_HANDSFREE_CIND 0x8C struct hal_ev_handsfree_cind { uint8_t bdaddr[6]; } __attribute__((packed)); #define HAL_EV_HANDSFREE_COPS 0x8D struct hal_ev_handsfree_cops { uint8_t bdaddr[6]; } __attribute__((packed)); #define HAL_EV_HANDSFREE_CLCC 0x8E struct hal_ev_handsfree_clcc { uint8_t bdaddr[6]; } __attribute__((packed)); #define HAL_EV_HANDSFREE_UNKNOWN_AT 0x8F struct hal_ev_handsfree_unknown_at { uint8_t bdaddr[6]; uint16_t len; uint8_t buf[0]; } __attribute__((packed)); #define HAL_EV_HANDSFREE_HSP_KEY_PRESS 0x90 struct hal_ev_handsfree_hsp_key_press { uint8_t bdaddr[6]; } __attribute__((packed)); #define HAL_EV_HANDSFREE_WBS 0x91 struct hal_ev_handsfree_wbs { uint8_t wbs; uint8_t bdaddr[6]; } __attribute__((packed)); #define HAL_AVRCP_FEATURE_NONE 0x00 #define HAL_AVRCP_FEATURE_METADATA 0x01 #define HAL_AVRCP_FEATURE_ABSOLUTE_VOLUME 0x02 #define HAL_AVRCP_FEATURE_BROWSE 0x04 #define HAL_EV_AVRCP_REMOTE_FEATURES 0x81 struct hal_ev_avrcp_remote_features { uint8_t bdaddr[6]; uint8_t features; } __attribute__((packed)); #define HAL_EV_AVRCP_GET_PLAY_STATUS 0x82 #define HAL_EV_AVRCP_LIST_PLAYER_ATTRS 0x83 #define HAL_EV_AVRCP_LIST_PLAYER_VALUES 0x84 struct hal_ev_avrcp_list_player_values { uint8_t attr; } __attribute__((packed)); #define HAL_EV_AVRCP_GET_PLAYER_VALUES 0x85 struct hal_ev_avrcp_get_player_values { uint8_t number; uint8_t attrs[0]; } __attribute__((packed)); #define HAL_EV_AVRCP_GET_PLAYER_ATTRS_TEXT 0x86 struct hal_ev_avrcp_get_player_attrs_text { uint8_t number; uint8_t attrs[0]; } __attribute__((packed)); #define HAL_EV_AVRCP_GET_PLAYER_VALUES_TEXT 0x87 struct hal_ev_avrcp_get_player_values_text { uint8_t attr; uint8_t number; uint8_t values[0]; } __attribute__((packed)); #define HAL_EV_AVRCP_SET_PLAYER_VALUES 0x88 struct hal_ev_avrcp_set_player_values { uint8_t number; struct hal_avrcp_player_attr_value attrs[0]; } __attribute__((packed)); #define HAL_EV_AVRCP_GET_ELEMENT_ATTRS 0x89 struct hal_ev_avrcp_get_element_attrs { uint8_t number; uint8_t attrs[0]; } __attribute__((packed)); #define HAL_EV_AVRCP_REGISTER_NOTIFICATION 0x8a struct hal_ev_avrcp_register_notification { uint8_t event; uint32_t param; } __attribute__((packed)); #define HAL_EV_AVRCP_VOLUME_CHANGED 0x8b struct hal_ev_avrcp_volume_changed { uint8_t volume; uint8_t type; } __attribute__((packed)); #define HAL_EV_AVRCP_PASSTHROUGH_CMD 0x8c struct hal_ev_avrcp_passthrough_cmd { uint8_t id; uint8_t state; } __attribute__((packed)); #define HAL_EV_AVRCP_CTRL_CONN_STATE 0x81 struct hal_ev_avrcp_ctrl_conn_state { uint8_t state; uint8_t bdaddr[6]; } __attribute__((packed)); #define HAL_EV_AVRCP_CTRL_PASSTHROUGH_RSP 0x82 struct hal_ev_avrcp_ctrl_passthrough_rsp { uint8_t id; uint8_t key_state; } __attribute__((packed)); #define HAL_EV_GATT_CLIENT_REGISTER_CLIENT 0x81 struct hal_ev_gatt_client_register_client { int32_t status; int32_t client_if; uint8_t app_uuid[16]; } __attribute__((packed)); #define HAL_EV_GATT_CLIENT_SCAN_RESULT 0x82 struct hal_ev_gatt_client_scan_result { uint8_t bda[6]; int32_t rssi; uint16_t len; uint8_t adv_data[0]; } __attribute__((packed)); #define HAL_EV_GATT_CLIENT_CONNECT 0x83 struct hal_ev_gatt_client_connect { int32_t conn_id; int32_t status; int32_t client_if; uint8_t bda[6]; } __attribute__((packed)); #define HAL_EV_GATT_CLIENT_DISCONNECT 0x84 struct hal_ev_gatt_client_disconnect { int32_t conn_id; int32_t status; int32_t client_if; uint8_t bda[6]; } __attribute__((packed)); #define HAL_EV_GATT_CLIENT_SEARCH_COMPLETE 0x85 struct hal_ev_gatt_client_search_complete { int32_t conn_id; int32_t status; } __attribute__((packed)); #define HAL_EV_GATT_CLIENT_SEARCH_RESULT 0x86 struct hal_ev_gatt_client_search_result { int32_t conn_id; struct hal_gatt_srvc_id srvc_id; } __attribute__((packed)); #define HAL_EV_GATT_CLIENT_GET_CHARACTERISTIC 0x87 struct hal_ev_gatt_client_get_characteristic { int32_t conn_id; int32_t status; struct hal_gatt_srvc_id srvc_id; struct hal_gatt_gatt_id char_id; int32_t char_prop; } __attribute__((packed)); #define HAL_EV_GATT_CLIENT_GET_DESCRIPTOR 0x88 struct hal_ev_gatt_client_get_descriptor { int32_t conn_id; int32_t status; struct hal_gatt_srvc_id srvc_id; struct hal_gatt_gatt_id char_id; struct hal_gatt_gatt_id descr_id; } __attribute__((packed)); #define HAL_EV_GATT_CLIENT_GET_INC_SERVICE 0X89 struct hal_ev_gatt_client_get_inc_service { int32_t conn_id; int32_t status; struct hal_gatt_srvc_id srvc_id; struct hal_gatt_srvc_id incl_srvc_id; } __attribute__((packed)); #define HAL_EV_GATT_CLIENT_REGISTER_FOR_NOTIF 0x8a struct hal_ev_gatt_client_reg_for_notif { int32_t conn_id; int32_t registered; int32_t status; struct hal_gatt_srvc_id srvc_id; struct hal_gatt_gatt_id char_id; } __attribute__((packed)); #define HAL_EV_GATT_CLIENT_NOTIFY 0x8b struct hal_ev_gatt_client_notify { int32_t conn_id; uint8_t bda[6]; struct hal_gatt_srvc_id srvc_id; struct hal_gatt_gatt_id char_id; uint8_t is_notify; uint16_t len; uint8_t value[0]; } __attribute__((packed)); #define HAL_EV_GATT_CLIENT_READ_CHARACTERISTIC 0x8c struct hal_gatt_read_params { struct hal_gatt_srvc_id srvc_id; struct hal_gatt_gatt_id char_id; struct hal_gatt_gatt_id descr_id; uint8_t status; uint16_t value_type; uint16_t len; uint8_t value[0]; } __attribute__((packed)); struct hal_ev_gatt_client_read_characteristic { int32_t conn_id; int32_t status; struct hal_gatt_read_params data; } __attribute__((packed)); #define HAL_EV_GATT_CLIENT_WRITE_CHARACTERISTIC 0x8d struct hal_gatt_write_params { struct hal_gatt_srvc_id srvc_id; struct hal_gatt_gatt_id char_id; struct hal_gatt_gatt_id descr_id; uint8_t status; } __attribute__((packed)); struct hal_ev_gatt_client_write_characteristic { int32_t conn_id; int32_t status; struct hal_gatt_write_params data; } __attribute__((packed)); #define HAL_EV_GATT_CLIENT_READ_DESCRIPTOR 0x8e struct hal_ev_gatt_client_read_descriptor { int32_t conn_id; int32_t status; struct hal_gatt_read_params data; } __attribute__((packed)); #define HAL_EV_GATT_CLIENT_WRITE_DESCRIPTOR 0x8f struct hal_ev_gatt_client_write_descriptor { int32_t conn_id; int32_t status; struct hal_gatt_write_params data; } __attribute__((packed)); #define HAL_EV_GATT_CLIENT_EXEC_WRITE 0x90 struct hal_ev_gatt_client_exec_write { int32_t conn_id; int32_t status; } __attribute__((packed)); #define HAL_EV_GATT_CLIENT_READ_REMOTE_RSSI 0x91 struct hal_ev_gatt_client_read_remote_rssi { int32_t client_if; uint8_t address[6]; int32_t rssi; int32_t status; } __attribute__((packed)); #define HAL_EV_GATT_CLIENT_LISTEN 0x92 struct hal_ev_gatt_client_listen { int32_t status; int32_t server_if; } __attribute__((packed)); #define HAL_EV_GATT_SERVER_REGISTER 0x93 struct hal_ev_gatt_server_register { int32_t status; int32_t server_if; uint8_t uuid[16]; } __attribute__((packed)); #define HAL_EV_GATT_SERVER_CONNECTION 0x94 struct hal_ev_gatt_server_connection { int32_t conn_id; int32_t server_if; int32_t connected; uint8_t bdaddr[6]; } __attribute__((packed)); #define HAL_EV_GATT_SERVER_SERVICE_ADDED 0x95 struct hal_ev_gatt_server_service_added { int32_t status; int32_t server_if; struct hal_gatt_srvc_id srvc_id; int32_t srvc_handle; } __attribute__((packed)); #define HAL_EV_GATT_SERVER_INC_SRVC_ADDED 0x96 struct hal_ev_gatt_server_inc_srvc_added { int32_t status; int32_t server_if; int32_t srvc_handle; int32_t incl_srvc_handle; } __attribute__((packed)); #define HAL_EV_GATT_SERVER_CHAR_ADDED 0x97 struct hal_ev_gatt_server_characteristic_added { int32_t status; int32_t server_if; uint8_t uuid[16]; int32_t srvc_handle; int32_t char_handle; } __attribute__((packed)); #define HAL_EV_GATT_SERVER_DESCRIPTOR_ADDED 0x98 struct hal_ev_gatt_server_descriptor_added { int32_t status; int32_t server_if; uint8_t uuid[16]; int32_t srvc_handle; int32_t descr_handle; } __attribute__((packed)); #define HAL_EV_GATT_SERVER_SERVICE_STARTED 0x99 struct hal_ev_gatt_server_service_started { int32_t status; int32_t server_if; int32_t srvc_handle; } __attribute__((packed)); #define HAL_EV_GATT_SERVER_SERVICE_STOPPED 0x9a struct hal_ev_gatt_server_service_stopped { int32_t status; int32_t server_if; int32_t srvc_handle; } __attribute__((packed)); #define HAL_EV_GATT_SERVER_SERVICE_DELETED 0x9b struct hal_ev_gatt_server_service_deleted { int32_t status; int32_t server_if; int32_t srvc_handle; } __attribute__((packed)); #define HAL_EV_GATT_SERVER_REQUEST_READ 0x9c struct hal_ev_gatt_server_request_read { int32_t conn_id; int32_t trans_id; uint8_t bdaddr[6]; int32_t attr_handle; int32_t offset; uint8_t is_long; } __attribute__((packed)); #define HAL_EV_GATT_SERVER_REQUEST_WRITE 0x9d struct hal_ev_gatt_server_request_write { int32_t conn_id; int32_t trans_id; uint8_t bdaddr[6]; int32_t attr_handle; int32_t offset; int32_t length; uint8_t need_rsp; uint8_t is_prep; uint8_t value[0]; } __attribute__((packed)); #define HAL_EV_GATT_SERVER_REQUEST_EXEC_WRITE 0x9e struct hal_ev_gatt_server_request_exec_write { int32_t conn_id; int32_t trans_id; uint8_t bdaddr[6]; int32_t exec_write; } __attribute__((packed)); #define HAL_EV_GATT_SERVER_RSP_CONFIRMATION 0x9f struct hal_ev_gatt_server_rsp_confirmation { int32_t status; int32_t handle; } __attribute__((packed)); #define HAL_EV_GATT_CLIENT_CONFIGURE_MTU 0xa0 struct hal_ev_gatt_client_configure_mtu { int32_t conn_id; int32_t status; int32_t mtu; } __attribute__((packed)); #define HAL_EV_GATT_CLIENT_FILTER_CONFIG 0xa1 struct hal_ev_gatt_client_filter_config { int32_t action; int32_t client_if; int32_t status; int32_t type; int32_t space; } __attribute__((packed)); #define HAL_EV_GATT_CLIENT_FILTER_PARAMS 0xa2 struct hal_ev_gatt_client_filter_params { int32_t action; int32_t client_if; int32_t status; int32_t space; } __attribute__((packed)); #define HAL_EV_GATT_CLIENT_FILTER_STATUS 0xa3 struct hal_ev_gatt_client_filter_status { int32_t enable; int32_t client_if; int32_t status; } __attribute__((packed)); #define HAL_EV_GATT_CLIENT_MULTI_ADV_ENABLE 0xa4 struct hal_ev_gatt_client_multi_adv_enable { int32_t client_if; int32_t status; } __attribute__((packed)); #define HAL_EV_GATT_CLIENT_MULTI_ADV_UPDATE 0xa5 struct hal_ev_gatt_client_multi_adv_update { int32_t client_if; int32_t status; } __attribute__((packed)); #define HAL_EV_GATT_CLIENT_MULTI_ADV_DATA 0xa6 struct hal_ev_gatt_client_multi_adv_data { int32_t client_if; int32_t status; } __attribute__((packed)); #define HAL_EV_GATT_CLIENT_MULTI_ADV_DISABLE 0xa7 struct hal_ev_gatt_client_multi_adv_disable { int32_t client_if; int32_t status; } __attribute__((packed)); #define HAL_EV_GATT_CLIENT_CONGESTION 0xa8 struct hal_ev_gatt_client_congestion { int32_t conn_id; uint8_t congested; } __attribute__((packed)); #define HAL_EV_GATT_CLIENT_CONFIG_BATCHSCAN 0xa9 struct hal_ev_gatt_client_config_batchscan { int32_t client_if; int32_t status; } __attribute__((packed)); #define HAL_EV_GATT_CLIENT_ENABLE_BATCHSCAN 0xaa struct hal_ev_gatt_client_enable_batchscan { int32_t action; int32_t client_if; int32_t status; } __attribute__((packed)); #define HAL_EV_GATT_CLIENT_BATCHSCAN_REPORTS 0xab struct hal_ev_gatt_client_batchscan_reports { int32_t client_if; int32_t status; int32_t format; int32_t num; int32_t data_len; uint8_t data[0]; } __attribute__((packed)); #define HAL_EV_GATT_CLIENT_BATCHSCAN_THRESHOLD 0xac struct hal_ev_gatt_client_batchscan_threshold { int32_t client_if; } __attribute__((packed)); #define HAL_EV_GATT_CLIENT_TRACK_ADV 0xad struct hal_ev_gatt_client_track_adv { int32_t client_if; int32_t filetr_index; int32_t address_type; uint8_t address[6]; int32_t state; } __attribute__((packed)); #define HAL_EV_GATT_SERVER_INDICATION_SENT 0xae struct hal_ev_gatt_server_indication_sent { int32_t conn_id; int32_t status; } __attribute__((packed)); #define HAL_EV_GATT_SERVER_CONGESTION 0xaf struct hal_ev_gatt_server_congestion { int32_t conn_id; uint8_t congested; } __attribute__((packed)); #define HAL_EV_GATT_SERVER_MTU_CHANGED 0xb0 struct hal_ev_gatt_server_mtu_changed { int32_t conn_id; int32_t mtu; } __attribute__((packed)); #define HAL_GATT_PERMISSION_READ 0x0001 #define HAL_GATT_PERMISSION_READ_ENCRYPTED 0x0002 #define HAL_GATT_PERMISSION_READ_ENCRYPTED_MITM 0x0004 #define HAL_GATT_PERMISSION_WRITE 0x0010 #define HAL_GATT_PERMISSION_WRITE_ENCRYPTED 0x0020 #define HAL_GATT_PERMISSION_WRITE_ENCRYPTED_MITM 0x0040 #define HAL_GATT_PERMISSION_WRITE_SIGNED 0x0080 #define HAL_GATT_PERMISSION_WRITE_SIGNED_MITM 0x0100 #define HAL_GATT_AUTHENTICATION_NONE 0 #define HAL_GATT_AUTHENTICATION_NO_MITM 1 #define HAL_GATT_AUTHENTICATION_MITM 2 #define HAL_HF_CLIENT_CONN_STATE_DISCONNECTED 0x00 #define HAL_HF_CLIENT_CONN_STATE_CONNECTING 0x01 #define HAL_HF_CLIENT_CONN_STATE_CONNECTED 0x02 #define HAL_HF_CLIENT_CONN_STATE_SLC_CONNECTED 0x03 #define HAL_HF_CLIENT_CONN_STATE_DISCONNECTING 0x04 #define HAL_HF_CLIENT_PEER_FEAT_3WAY 0x00000001 #define HAL_HF_CLIENT_PEER_FEAT_ECNR 0x00000002 #define HAL_HF_CLIENT_PEER_FEAT_VREC 0x00000004 #define HAL_HF_CLIENT_PEER_FEAT_INBAND 0x00000008 #define HAL_HF_CLIENT_PEER_FEAT_VTAG 0x00000010 #define HAL_HF_CLIENT_PEER_FEAT_REJECT 0x00000020 #define HAL_HF_CLIENT_PEER_FEAT_ECS 0x00000040 #define HAL_HF_CLIENT_PEER_FEAT_ECC 0x00000080 #define HAL_HF_CLIENT_PEER_FEAT_EXTERR 0x00000100 #define HAL_HF_CLIENT_PEER_FEAT_CODEC 0x00000200 #define HAL_HF_CLIENT_CHLD_FEAT_REL 0x00000001 #define HAL_HF_CLIENT_CHLD_FEAT_REL_ACC 0x00000002 #define HAL_HF_CLIENT_CHLD_FEAT_REL_X 0x00000004 #define HAL_HF_CLIENT_CHLD_FEAT_HOLD_ACC 0x00000008 #define HAL_HF_CLIENT_CHLD_FEAT_PRIV_X 0x00000010 #define HAL_HF_CLIENT_CHLD_FEAT_MERGE 0x00000020 #define HAL_HF_CLIENT_CHLD_FEAT_MERGE_DETACH 0x00000040 #define HAL_EV_HF_CLIENT_CONN_STATE 0x81 struct hal_ev_hf_client_conn_state { uint8_t state; uint32_t peer_feat; uint32_t chld_feat; uint8_t bdaddr[6]; } __attribute__((packed)); #define HAL_HF_CLIENT_AUDIO_STATE_DISCONNECTED 0x00 #define HAL_HF_CLIENT_AUDIO_STATE_CONNECTING 0x01 #define HAL_HF_CLIENT_AUDIO_STATE_CONNECTED 0x02 #define HAL_HF_CLIENT_AUDIO_STATE_CONNECTED_MSBC 0x03 #define HAL_EV_HF_CLIENT_AUDIO_STATE 0x82 struct hal_ev_hf_client_audio_state { uint8_t state; uint8_t bdaddr[6]; } __attribute__((packed)); #define HAL_HF_CLIENT_VR_STOPPED 0x00 #define HAL_HF_CLIENT_VR_STARTED 0x01 #define HAL_EV_HF_CLIENT_VR_STATE 0x83 struct hal_ev_hf_client_vr_state { uint8_t state; } __attribute__((packed)); #define HAL_HF_CLIENT_NET_NOT_AVAILABLE 0x00 #define HAL_HF_CLIENT_NET_AVAILABLE 0x01 #define HAL_EV_HF_CLIENT_NET_STATE 0x84 struct hal_ev_hf_client_net_state { uint8_t state; } __attribute__((packed)); #define HAL_HF_CLIENT_NET_ROAMING_TYPE_HOME 0x00 #define HAL_HF_CLIENT_NET_ROAMING_TYPE_ROAMING 0x01 #define HAL_EV_HF_CLIENT_NET_ROAMING_TYPE 0x85 struct hal_ev_hf_client_net_roaming_type { uint8_t state; } __attribute__((packed)); #define HAL_EV_HF_CLIENT_NET_SIGNAL_STRENGTH 0x86 struct hal_ev_hf_client_net_signal_strength { uint8_t signal_strength; } __attribute__((packed)); #define HAL_EV_HF_CLIENT_BATTERY_LEVEL 0x87 struct hal_ev_hf_client_battery_level { uint8_t battery_level; } __attribute__((packed)); #define HAL_EV_HF_CLIENT_OPERATOR_NAME 0x88 struct hal_ev_hf_client_operator_name { uint16_t name_len; uint8_t name[0]; } __attribute__((packed)); #define HAL_HF_CLIENT_CALL_IND_NO_CALL_IN_PROGERSS 0x00 #define HAL_HF_CLIENT_CALL_IND_CALL_IN_PROGERSS 0x01 #define HAL_EV_HF_CLIENT_CALL_INDICATOR 0x89 struct hal_ev_hf_client_call_indicator { uint8_t call; } __attribute__((packed)); #define HAL_HF_CLIENT_CALL_SETUP_NONE 0x00 #define HAL_HF_CLIENT_CALL_SETUP_INCOMING 0x01 #define HAL_HF_CLIENT_CALL_SETUP_OUTGOING 0x02 #define HAL_HF_CLIENT_CALL_SETUP_ALERTING 0x03 #define HAL_EV_HF_CLIENT_CALL_SETUP_INDICATOR 0x8a struct hal_ev_hf_client_call_setup_indicator { uint8_t call_setup; } __attribute__((packed)); #define HAL_HF_CLIENT_CALL_HELD_IND_NONE 0x00 #define HAL_HF_CLIENT_CALL_HELD_IND_HOLD_AND_ACTIVE 0x01 #define HAL_HF_CLIENT_CALL_SETUP_IND_HOLD 0x02 #define HAL_EV_HF_CLIENT_CALL_HELD_INDICATOR 0x8b struct hal_ev_hf_client_call_held_indicator { uint8_t call_held; } __attribute__((packed)); #define HAL_HF_CLIENT_RESP_AND_HOLD_STATUS_HELD 0x00 #define HAL_HF_CLIENT_RESP_AND_HOLD_STATUS_ACCEPT 0x01 #define HAL_HF_CLIENT_RESP_AND_HOLD_STATUS_REJECT 0x02 #define HAL_EV_HF_CLIENT_RESPONSE_AND_HOLD_STATUS 0x8c struct hal_ev_hf_client_response_and_hold_status { uint8_t status; } __attribute__((packed)); #define HAL_EV_HF_CLIENT_CALLING_LINE_IDENT 0x8d struct hal_ev_hf_client_calling_line_ident { uint16_t number_len; uint8_t number[0]; } __attribute__((packed)); #define HAL_EV_HF_CLIENT_CALL_WAITING 0x8e struct hal_ev_hf_client_call_waiting { uint16_t number_len; uint8_t number[0]; } __attribute__((packed)); #define HAL_HF_CLIENT_DIRECTION_OUTGOING 0x00 #define HAL_HF_CLIENT_DIRECTION_INCOMING 0x01 #define HAL_HF_CLIENT_CALL_STATE_ACTIVE 0x00 #define HAL_HF_CLIENT_CALL_STATE_HELD 0x01 #define HAL_HF_CLIENT_CALL_STATE_DIALING 0x02 #define HAL_HF_CLIENT_CALL_STATE_ALERTING 0x03 #define HAL_HF_CLIENT_CALL_STATE_INCOMING 0x04 #define HAL_HF_CLIENT_CALL_STATE_WAITING 0x05 #define HAL_HF_CLIENT_CALL_STATE_HELD_BY_RESP_AND_HOLD 0x06 #define HAL_EV_HF_CLIENT_CURRENT_CALL 0x8f struct hal_ev_hf_client_current_call { uint8_t index; uint8_t direction; uint8_t call_state; uint8_t multiparty; uint16_t number_len; uint8_t number[0]; } __attribute__((packed)); #define HAL_EV_CLIENT_VOLUME_CHANGED 0x90 struct hal_ev_hf_client_volume_changed { uint8_t type; uint8_t volume; } __attribute__((packed)); #define HAL_HF_CLIENT_CMD_COMP_OK 0x00 #define HAL_HF_CLIENT_CMD_COMP_ERR 0x01 #define HAL_HF_CLIENT_CMD_COMP_ERR_NO_CARRIER 0x02 #define HAL_HF_CLIENT_CMD_COMP_ERR_BUSY 0x03 #define HAL_HF_CLIENT_CMD_COMP_ERR_NO_ANSWER 0x04 #define HAL_HF_CLIENT_CMD_COMP_ERR_DELAYED 0x05 #define HAL_HF_CLIENT_CMD_COMP_ERR_BACKLISTED 0x06 #define HAL_HF_CLIENT_CMD_COMP_ERR_CME 0x07 #define HAL_EV_CLIENT_COMMAND_COMPLETE 0x91 struct hal_ev_hf_client_command_complete { uint8_t type; uint8_t cme; } __attribute__((packed)); #define HAL_HF_CLIENT_SUBSCR_TYPE_UNKNOWN 0x00 #define HAL_HF_CLIENT_SUBSCR_TYPE_VOICE 0x01 #define HAL_HF_CLIENT_SUBSCR_TYPE_FAX 0x02 #define HAL_EV_CLIENT_SUBSCRIBER_SERVICE_INFO 0x92 struct hal_ev_hf_client_subscriber_service_info { uint8_t type; uint16_t name_len; uint8_t name[0]; } __attribute__((packed)); #define HAL_HF_CLIENT_INBAND_RINGTONE_NOT_PROVIDED 0x00 #define HAL_HF_CLIENT_INBAND_RINGTONE_PROVIDED 0x01 #define HAL_EV_CLIENT_INBAND_SETTINGS 0x93 struct hal_ev_hf_client_inband_settings { uint8_t state; } __attribute__((packed)); #define HAL_EV_CLIENT_LAST_VOICE_CALL_TAG_NUM 0x94 struct hal_ev_hf_client_last_void_call_tag_num { uint16_t number_len; uint8_t number[0]; } __attribute__((packed)); #define HAL_EV_CLIENT_RING_INDICATION 0x95 #define HAL_EV_MAP_CLIENT_REMOTE_MAS_INSTANCES 0x81 struct hal_map_client_mas_instance { int32_t id; int32_t scn; int32_t msg_types; int32_t name_len; uint8_t name[0]; } __attribute__((packed)); struct hal_ev_map_client_remote_mas_instances { int8_t status; uint8_t bdaddr[6]; int32_t num_instances; struct hal_map_client_mas_instance instances[0]; } __attribute__((packed)); bluez-5.82/android/PaxHeaders/pts-rfcomm.txt0000644000000000000000000000005012537515745016134 xustar0020 atime=1743516876 20 ctime=1743591289 bluez-5.82/android/pts-rfcomm.txt0000644000000000000000000000231712537515745015620 0ustar00rootrootPTS test results for RFCOMM PTS version: 6.1 Tested: 12-May-2015 Android version: 5.1 Kernel version: 4.1 Results: PASS test passed FAIL test failed INC test is inconclusive N/A test is disabled due to PICS setup NONE test result is none ------------------------------------------------------------------------------- Test Name Result Notes ------------------------------------------------------------------------------- TC_RFC_BV_01_C PASS rctest -n -P 1 TC_RFC_BV_02_C PASS rctest -r -P 1 TC_RFC_BV_03_C PASS rctest -r -P 1 TC_RFC_BV_04_C PASS rctest -r -P 1 TC_RFC_BV_05_C PASS rctest -n -P 4 Note: test requires IUT to connect on the given channel. sdptool browse to check the channel. TC_RFC_BV_06_C PASS rctest -r -P 1 TC_RFC_BV_07_C PASS rctest -r -P 1 TC_RFC_BV_08_C PASS rctest -r -P 1 TC_RFC_BV_11_C PASS rctest -r -P 1 TC_RFC_BV_13_C PASS rctest -r -P 1 TC_RFC_BV_14_C N/A TC_RFC_BV_15_C PASS rctest -r -P 1 TC_RFC_BV_17_C PASS rctest -d -P 1 TC_RFC_BV_19_C PASS TC_RFC_BV_21_C PASS rctest -w -N 10 -P 1 TC_RFC_BV_22_C PASS rctest -w -N 10 -P 1 TC_RFC_BV_25_C PASS rctest -r -P 1 ------------------------------------------------------------------------------- bluez-5.82/android/PaxHeaders/pixit-hsp.txt0000644000000000000000000000005012537515745015772 xustar0020 atime=1743516876 20 ctime=1743591289 bluez-5.82/android/pixit-hsp.txt0000644000000000000000000000202612537515745015453 0ustar00rootrootHSP PIXIT for the PTS tool. PTS version: 6.1 * - different than PTS defaults & - should be set to IUT Bluetooth address Required PIXIT settings ------------------------------------------------------------------------------- Parameter Name Value ------------------------------------------------------------------------------- TSPX_security_enabled TRUE TSPX_bd_addr_iut 112233445566 (*&) TSPX_hs_class_of_device 200404 TSPX_ag_class_of_device 400204 TSPX_packet_type_sco 00A0 TSPX_pin_code 0000 TSPX_time_guard 20000000 TSPX_use_implicit_send TRUE TSPX_verbose_implicit_send FALSE TSPX_delete_link_key FALSE TSPX_server_channel_tester 01 TSPX_server_channel_iut 00 TSPX_no_fail_verdict FALSE TSPX_remote_audio_volume_control TRUE TSPX_secure_simple_pairing_pass_key_confirmation FALSE TSPX_inband_ring_only FALSE TSPX_no_ring_or_inband_ring_tone FALSE TSPX_iut_establish_audio_before_RING FALSE ------------------------------------------------------------------------------- bluez-5.82/android/PaxHeaders/tester-hidhost.c0000644000000000000000000000005014015011623016371 xustar0020 atime=1743516864 20 ctime=1743591278 bluez-5.82/android/tester-hidhost.c0000644000000000000000000006074614015011623016067 0ustar00rootroot// SPDX-License-Identifier: Apache-2.0 /* * Copyright (C) 2014 Intel Corporation * */ #define _GNU_SOURCE #include #include "emulator/bthost.h" #include "src/shared/tester.h" #include "src/shared/queue.h" #include "lib/bluetooth.h" #include "android/utils.h" #include "tester-main.h" #define HID_GET_REPORT_PROTOCOL 0x60 #define HID_GET_BOOT_PROTOCOL 0x61 #define HID_SET_REPORT_PROTOCOL 0x70 #define HID_SET_BOOT_PROTOCOL 0x71 #define HID_SET_INPUT_REPORT 0x51 #define HID_SET_OUTPUT_REPORT 0x52 #define HID_SET_FEATURE_REPORT 0x53 #define HID_SEND_DATA 0xa2 #define HID_GET_INPUT_REPORT 0x49 #define HID_GET_OUTPUT_REPORT 0x4a #define HID_GET_FEATURE_REPORT 0x4b #define HID_MODE_DEFAULT 0x00 #define HID_MODE_BREDR 0x01 #define HID_MODE_LE 0x02 #define HID_EXPECTED_REPORT_SIZE 0x02 #define HID_VIRTUAL_CABLE_UNPLUG 0x15 static struct queue *list; /* List of hidhost test cases */ #define did_req_pdu 0x06, \ 0x00, 0x00, \ 0x00, 0x0f, \ 0x35, 0x03, \ 0x19, 0x12, 0x00, 0xff, 0xff, 0x35, 0x05, 0x0a, 0x00, \ 0x00, 0xff, 0xff, 0x00 #define did_rsp_pdu 0x07, \ 0x00, 0x00, \ 0x00, 0x4f, \ 0x00, 0x4c, \ 0x35, 0x4a, 0x35, 0x48, 0x09, 0x00, 0x00, 0x0a, 0x00, \ 0x01, 0x00, 0x00, 0x09, 0x00, 0x01, 0x35, 0x03, 0x19, \ 0x12, 0x00, 0x09, 0x00, 0x05, 0x35, 0x03, 0x19, 0x10, \ 0x02, 0x09, 0x00, 0x09, 0x35, 0x08, 0x35, 0x06, 0x19, \ 0x12, 0x00, 0x09, 0x01, 0x03, 0x09, 0x02, 0x00, 0x09, \ 0x01, 0x03, 0x09, 0x02, 0x01, 0x09, 0x1d, 0x6b, 0x09, \ 0x02, 0x02, 0x09, 0x02, 0x46, 0x09, 0x02, 0x03, 0x09, \ 0x05, 0x0e, 0x09, 0x02, 0x04, 0x28, 0x01, 0x09, 0x02, \ 0x05, 0x09, 0x00, 0x02, \ 0x00 #define hid_req_pdu 0x06, \ 0x00, 0x01, \ 0x00, 0x0f, \ 0x35, 0x03, \ 0x19, 0x11, 0x24, 0xff, 0xff, 0x35, 0x05, 0x0a, 0x00, \ 0x00, 0xff, 0xff, 0x00 #define hid_rsp_pdu 0x07, \ 0x00, 0x01, \ 0x01, 0x71, \ 0x01, 0x6E, \ 0x36, 0x01, 0x6b, 0x36, 0x01, 0x68, 0x09, 0x00, 0x00, \ 0x0a, 0x00, 0x01, 0x00, 0x00, 0x09, 0x00, 0x01, 0x35, \ 0x03, 0x19, 0x11, 0x24, 0x09, 0x00, 0x04, 0x35, 0x0d, \ 0x35, 0x06, 0x19, 0x01, 0x00, 0x09, 0x00, 0x11, 0x35, \ 0x03, 0x19, 0x00, 0x11, 0x09, 0x00, 0x05, 0x35, 0x03, \ 0x19, 0x10, 0x02, 0x09, 0x00, 0x06, 0x35, 0x09, 0x09, \ 0x65, 0x6e, 0x09, 0x00, 0x6a, 0x09, 0x01, 0x00, 0x09, \ 0x00, 0x09, 0x35, 0x08, 0x35, 0x06, 0x19, 0x11, 0x24, \ 0x09, 0x01, 0x00, 0x09, 0x00, 0x0d, 0x35, 0x0f, 0x35, \ 0x0d, 0x35, 0x06, 0x19, 0x01, 0x00, 0x09, 0x00, 0x13, \ 0x35, 0x03, 0x19, 0x00, 0x11, 0x09, 0x01, 0x00, 0x25, \ 0x1e, 0x4c, 0x6f, 0x67, 0x69, 0x74, 0x65, 0x63, 0x68, \ 0x20, 0x42, 0x6c, 0x75, 0x65, 0x74, 0x6f, 0x6f, 0x74, \ 0x68, 0x20, 0x4d, 0x6f, 0x75, 0x73, 0x65, 0x20, 0x4d, \ 0x35, 0x35, 0x35, 0x62, 0x09, 0x01, 0x01, 0x25, 0x0f, \ 0x42, 0x6c, 0x75, 0x65, 0x74, 0x6f, 0x6f, 0x74, 0x68, \ 0x20, 0x4d, 0x6f, 0x75, 0x73, 0x65, 0x09, 0x01, 0x02, \ 0x25, 0x08, 0x4c, 0x6f, 0x67, 0x69, 0x74, 0x65, 0x63, \ 0x68, 0x09, 0x02, 0x00, 0x09, 0x01, 0x00, 0x09, 0x02, \ 0x01, 0x09, 0x01, 0x11, 0x09, 0x02, 0x02, 0x08, 0x80, \ 0x09, 0x02, 0x03, 0x08, 0x21, 0x09, 0x02, 0x04, 0x28, \ 0x01, 0x09, 0x02, 0x05, 0x28, 0x01, 0x09, 0x02, 0x06, \ 0x35, 0x74, 0x35, 0x72, 0x08, 0x22, 0x25, 0x6e, 0x05, \ 0x01, 0x09, 0x02, 0xa1, 0x01, 0x85, 0x02, 0x09, 0x01, \ 0xa1, 0x00, 0x05, 0x09, 0x19, 0x01, 0x29, 0x08, 0x15, \ 0x00, 0x25, 0x01, 0x75, 0x01, 0x95, 0x08, 0x81, 0x02, \ 0x05, 0x01, 0x09, 0x30, 0x09, 0x31, 0x16, 0x01, 0xf8, \ 0x26, 0xff, 0x07, 0x75, 0x0c, 0x95, 0x02, 0x81, 0x06, \ 0x09, 0x38, 0x15, 0x81, 0x25, 0x7f, 0x75, 0x08, 0x95, \ 0x01, 0x81, 0x06, 0x05, 0x0c, 0x0a, 0x38, 0x02, 0x81, \ 0x06, 0x05, 0x09, 0x19, 0x09, 0x29, 0x10, 0x15, 0x00, \ 0x25, 0x01, 0x95, 0x08, 0x75, 0x01, 0x81, 0x02, 0xc0, \ 0xc0, 0x06, 0x00, 0xff, 0x09, 0x01, 0xa1, 0x01, 0x85, \ 0x10, 0x75, 0x08, 0x95, 0x06, 0x15, 0x00, 0x26, 0xff, \ 0x00, 0x09, 0x01, 0x81, 0x00, 0x09, 0x01, 0x91, 0x00, \ 0xc0, 0x09, 0x02, 0x07, 0x35, 0x08, 0x35, 0x06, 0x09, \ 0x04, 0x09, 0x09, 0x01, 0x00, 0x09, 0x02, 0x08, 0x28, \ 0x00, 0x09, 0x02, 0x09, 0x28, 0x01, 0x09, 0x02, 0x0a, \ 0x28, 0x01, 0x09, 0x02, 0x0b, 0x09, 0x01, 0x00, 0x09, \ 0x02, 0x0c, 0x09, 0x0c, 0x80, 0x09, 0x02, 0x0d, 0x28, \ 0x00, 0x09, 0x02, 0x0e, 0x28, 0x01, \ 0x00 static const struct pdu_set sdp_pdus[] = { { raw_pdu(did_req_pdu), raw_pdu(did_rsp_pdu) }, { raw_pdu(hid_req_pdu), raw_pdu(hid_rsp_pdu) }, { end_pdu, end_pdu }, }; static struct emu_l2cap_cid_data sdp_cid_data = { .pdu = sdp_pdus, .is_sdp = TRUE, }; #define hid_keyboard_rsp_pdu 0x07, \ 0x00, 0x01, \ 0x02, 0x04, \ 0x02, 0x01, \ 0x36, 0x01, 0xfe, 0x36, 0x01, 0x93, 0x09, 0x00, 0x00, \ 0x0a, 0x00, 0x01, 0x00, 0x00, 0x09, 0x00, 0x01, 0x35, \ 0x03, 0x19, 0x11, 0x24, 0x09, 0x00, 0x04, 0x35, 0x0d, \ 0x35, 0x06, 0x19, 0x01, 0x00, 0x09, 0x00, 0x11, 0x35, \ 0x03, 0x19, 0x00, 0x11, 0x09, 0x00, 0x06, 0x35, 0x09, \ 0x09, 0x65, 0x6e, 0x09, 0x00, 0x6a, 0x09, 0x01, 0x00, \ 0x09, 0x00, 0x09, 0x35, 0x08, 0x35, 0x06, 0x19, 0x11, \ 0x24, 0x09, 0x01, 0x00, 0x09, 0x00, 0x0d, 0x35, 0x0f, \ 0x35, 0x0d, 0x35, 0x06, 0x19, 0x01, 0x00, 0x09, 0x00, \ 0x13, 0x35, 0x03, 0x19, 0x00, 0x11, 0x09, 0x01, 0x00, \ 0x25, 0x10, 0x53, 0x41, 0x4d, 0x53, 0x55, 0x4e, 0x47, \ 0x20, 0x4b, 0x65, 0x79, 0x62, 0x6f, 0x61, 0x72, 0x64, \ 0x09, 0x01, 0x01, 0x25, 0x08, 0x4b, 0x65, 0x79, 0x62, \ 0x6f, 0x61, 0x72, 0x64, 0x09, 0x01, 0x02, 0x25, 0x0d, \ 0x43, 0x53, 0x52, 0x20, 0x48, 0x49, 0x44, 0x45, 0x6e, \ 0x67, 0x69, 0x6e, 0x65, 0x09, 0x02, 0x00, 0x09, 0x01, \ 0x00, 0x09, 0x02, 0x01, 0x09, 0x01, 0x11, 0x09, 0x02, \ 0x02, 0x08, 0x40, 0x09, 0x02, 0x03, 0x08, 0x23, 0x09, \ 0x02, 0x04, 0x28, 0x01, 0x09, 0x02, 0x05, 0x28, 0x01, \ 0x09, 0x02, 0x06, 0x35, 0xb7, 0x35, 0xb5, 0x08, 0x22, \ 0x25, 0xb1, 0x05, 0x01, 0x09, 0x06, 0xa1, 0x01, 0x05, \ 0x07, 0x85, 0x01, 0x19, 0xe0, 0x29, 0xe7, 0x15, 0x00, \ 0x25, 0x01, 0x75, 0x01, 0x95, 0x08, 0x81, 0x02, 0x95, \ 0x01, 0x75, 0x08, 0x81, 0x01, 0x95, 0x05, 0x75, 0x01, \ 0x05, 0x08, 0x85, 0x01, 0x19, 0x01, 0x29, 0x05, 0x91, \ 0x02, 0x95, 0x01, 0x75, 0x03, 0x91, 0x03, 0x95, 0x06, \ 0x75, 0x08, 0x15, 0x00, 0x25, 0x65, 0x05, 0x07, 0x19, \ 0x00, 0x29, 0x6f, 0x81, 0x00, 0xc0, 0x05, 0x0c, 0x09, \ 0x01, 0xa1, 0x01, 0x85, 0x02, 0x05, 0x0c, 0x15, 0x00, \ 0x25, 0x01, 0x75, 0x01, 0x95, 0x18, 0x09, 0xe2, 0x09, \ 0xea, 0x09, 0xe9, 0x09, 0xb7, 0x09, 0xcd, 0x0a, 0x23, \ 0x02, 0x0a, 0x8a, 0x01, 0x0a, 0x21, 0x02, 0x75, 0x01, \ 0x95, 0x03, 0x81, 0x02, 0x75, 0x01, 0x95, 0x05, 0x81, \ 0x01, 0x05, 0x08, 0x85, 0xff, 0x95, 0x01, 0x75, 0x02, \ 0x09, 0x24, 0x09, 0x26, 0x81, 0x02, 0x75, 0x06, 0x81, \ 0x01, 0xc0, 0x06, 0x7f, 0xff, 0x09, 0x01, 0xa1, 0x01, \ 0x85, 0x03, 0x15, 0x00, 0x25, 0x01, 0x09, 0xb9, 0x09, \ 0xb5, 0x09, 0xba, 0x09, 0xbb, 0x09, 0xbc, 0x09, 0xbd, \ 0x09, 0xb6, 0x09, 0xb7, 0x75, 0x01, 0x95, 0x06, 0x81, \ 0x02, 0x75, 0x01, 0x95, 0x02, 0x81, 0x01, 0xc0, 0x09, \ 0x02, 0x07, 0x35, 0x08, 0x35, 0x06, 0x09, 0x04, 0x09, \ 0x09, 0x01, 0x00, 0x09, 0x02, 0x08, 0x28, 0x00, 0x09, \ 0x02, 0x09, 0x28, 0x01, 0x09, 0x02, 0x0a, 0x28, 0x01, \ 0x09, 0x02, 0x0b, 0x09, 0x01, 0x00, 0x09, 0x02, 0x0c, \ 0x09, 0x1f, 0x40, 0x09, 0x02, 0x0d, 0x28, 0x00, 0x09, \ 0x02, 0x0e, 0x28, 0x01, 0x36, 0x00, 0x65, 0x09, 0x00, \ 0x00, 0x0a, 0x00, 0x01, 0x00, 0x01, 0x09, 0x00, 0x01, \ 0x35, 0x03, 0x19, 0x12, 0x00, 0x09, 0x00, 0x04, 0x35, \ 0x0d, 0x35, 0x06, 0x19, 0x01, 0x00, 0x09, 0x00, 0x01, \ 0x35, 0x03, 0x19, 0x00, 0x01, 0x09, 0x00, 0x06, 0x35, \ 0x09, 0x09, 0x65, 0x6e, 0x09, 0x00, 0x6a, 0x09, 0x01, \ 0x00, 0x09, 0x00, 0x09, 0x35, 0x08, 0x35, 0x06, 0x19, \ 0x12, 0x00, 0x09, 0x01, 0x00, 0x09, 0x01, 0x01, 0x25, \ 0x00, 0x09, 0x02, 0x00, 0x09, 0x01, 0x00, 0x09, 0x02, \ 0x01, 0x09, 0x23, 0x3d, 0x09, 0x02, 0x02, 0x09, 0x01, \ 0x3d, 0x09, 0x02, 0x03, 0x09, 0x00, 0x00, 0x09, 0x02, \ 0x04, 0x28, 0x01, 0x09, 0x02, 0x05, 0x09, 0x00, 0x02, \ 0x00 static const struct pdu_set sdp_kb_pdus[] = { { raw_pdu(did_req_pdu), raw_pdu(did_rsp_pdu) }, { raw_pdu(hid_req_pdu), raw_pdu(hid_keyboard_rsp_pdu) }, { end_pdu, end_pdu }, }; static struct emu_l2cap_cid_data sdp_kb_cid_data = { .pdu = sdp_kb_pdus, .is_sdp = TRUE, }; static struct emu_l2cap_cid_data ctrl_cid_data; static struct emu_l2cap_cid_data intr_cid_data; static void hid_prepare_reply_protocol_mode(struct emu_l2cap_cid_data *cid_data) { struct test_data *t_data = tester_get_data(); struct bthost *bthost = hciemu_client_get_host(t_data->hciemu); const struct iovec pdu = raw_pdu(0xa0, 0x00); bthost_send_cid_v(bthost, cid_data->handle, cid_data->cid, &pdu, 1); } static void hid_prepare_reply_report(struct emu_l2cap_cid_data *cid_data) { struct test_data *t_data = tester_get_data(); struct bthost *bthost = hciemu_client_get_host(t_data->hciemu); const struct iovec pdu = raw_pdu(0xa2, 0x01, 0x00); bthost_send_cid_v(bthost, cid_data->handle, cid_data->cid, &pdu, 1); } static void hid_ctrl_cid_hook_cb(const void *data, uint16_t len, void *user_data) { struct emu_l2cap_cid_data *cid_data = user_data; uint8_t header = ((uint8_t *) data)[0]; struct step *step; switch (header) { case HID_GET_REPORT_PROTOCOL: case HID_GET_BOOT_PROTOCOL: case HID_SET_REPORT_PROTOCOL: case HID_SET_BOOT_PROTOCOL: hid_prepare_reply_protocol_mode(cid_data); break; case HID_GET_INPUT_REPORT: case HID_GET_OUTPUT_REPORT: case HID_GET_FEATURE_REPORT: hid_prepare_reply_report(cid_data); break; /* * HID device doesnot reply for this commads, so reaching pdu's * to hid device means assuming test passed */ case HID_SET_INPUT_REPORT: case HID_SET_OUTPUT_REPORT: case HID_SET_FEATURE_REPORT: case HID_SEND_DATA: /* Successfully verify sending data step */ step = g_new0(struct step, 1); step->callback = CB_EMU_CONFIRM_SEND_DATA; schedule_callback_verification(step); break; case HID_VIRTUAL_CABLE_UNPLUG: step = g_new0(struct step, 1); step->callback = CB_EMU_CONNECTION_REJECTED; schedule_callback_verification(step); break; } } static void hid_ctrl_connect_cb(uint16_t handle, uint16_t cid, void *user_data) { struct test_data *data = tester_get_data(); struct bthost *bthost = hciemu_client_get_host(data->hciemu); struct emu_l2cap_cid_data *cid_data = user_data; cid_data->handle = handle; cid_data->cid = cid; bthost_add_cid_hook(bthost, handle, cid, hid_ctrl_cid_hook_cb, cid_data); } static void hid_intr_cid_hook_cb(const void *data, uint16_t len, void *user_data) { uint8_t header = ((uint8_t *) data)[0]; struct step *step; switch (header) { case HID_SEND_DATA: /* Successfully verify sending data step */ step = g_new0(struct step, 1); step->callback = CB_EMU_CONFIRM_SEND_DATA; schedule_callback_verification(step); break; } } static void hid_intr_connect_cb(uint16_t handle, uint16_t cid, void *user_data) { struct test_data *data = tester_get_data(); struct bthost *bthost = hciemu_client_get_host(data->hciemu); struct emu_l2cap_cid_data *cid_data = user_data; cid_data->handle = handle; cid_data->cid = cid; bthost_add_cid_hook(bthost, handle, cid, hid_intr_cid_hook_cb, cid_data); } static bt_scan_mode_t setprop_scan_mode_conn_val = BT_SCAN_MODE_CONNECTABLE; static bt_property_t prop_test_scan_mode_conn = { .type = BT_PROPERTY_ADAPTER_SCAN_MODE, .val = &setprop_scan_mode_conn_val, .len = sizeof(setprop_scan_mode_conn_val), }; /* Emulate SDP (PSM = 1) */ static struct emu_set_l2cap_data l2cap_setup_sdp_data = { .psm = 1, .func = tester_generic_connect_cb, .user_data = &sdp_cid_data, }; static struct emu_set_l2cap_data l2cap_setup_kb_sdp_data = { .psm = 1, .func = tester_generic_connect_cb, .user_data = &sdp_kb_cid_data, }; /* Emulate Control Channel (PSM = 17) */ static struct emu_set_l2cap_data l2cap_setup_cc_data = { .psm = 17, .func = hid_ctrl_connect_cb, .user_data = &ctrl_cid_data, }; /* Emulate Interrupt Channel (PSM = 19) */ static struct emu_set_l2cap_data l2cap_setup_ic_data = { .psm = 19, .func = hid_intr_connect_cb, .user_data = &intr_cid_data, }; static void hidhost_connect_action(void) { struct test_data *data = tester_get_data(); const uint8_t *hid_addr = hciemu_get_client_bdaddr(data->hciemu); struct step *step = g_new0(struct step, 1); bt_bdaddr_t bdaddr; bdaddr2android((const bdaddr_t *) hid_addr, &bdaddr); step->action_status = data->if_hid->connect(&bdaddr); schedule_action_verification(step); } static void hidhost_disconnect_action(void) { struct test_data *data = tester_get_data(); const uint8_t *hid_addr = hciemu_get_client_bdaddr(data->hciemu); struct step *step = g_new0(struct step, 1); bt_bdaddr_t bdaddr; bdaddr2android((const bdaddr_t *) hid_addr, &bdaddr); step->action_status = data->if_hid->disconnect(&bdaddr); schedule_action_verification(step); } static void hidhost_virtual_unplug_action(void) { struct test_data *data = tester_get_data(); const uint8_t *hid_addr = hciemu_get_client_bdaddr(data->hciemu); struct step *step = g_new0(struct step, 1); bt_bdaddr_t bdaddr; bdaddr2android((const bdaddr_t *) hid_addr, &bdaddr); step->action_status = data->if_hid->virtual_unplug(&bdaddr); schedule_action_verification(step); } static void hidhost_get_protocol_action(void) { struct test_data *data = tester_get_data(); const uint8_t *hid_addr = hciemu_get_client_bdaddr(data->hciemu); struct step *step = g_new0(struct step, 1); bt_bdaddr_t bdaddr; bdaddr2android((const bdaddr_t *) hid_addr, &bdaddr); step->action_status = data->if_hid->get_protocol(&bdaddr, BTHH_REPORT_MODE); schedule_action_verification(step); } static void hidhost_set_protocol_action(void) { struct test_data *data = tester_get_data(); const uint8_t *hid_addr = hciemu_get_client_bdaddr(data->hciemu); struct step *step = g_new0(struct step, 1); bt_bdaddr_t bdaddr; bdaddr2android((const bdaddr_t *) hid_addr, &bdaddr); step->action_status = data->if_hid->set_protocol(&bdaddr, BTHH_REPORT_MODE); schedule_action_verification(step); } static void hidhost_get_report_action(void) { struct test_data *data = tester_get_data(); const uint8_t *hid_addr = hciemu_get_client_bdaddr(data->hciemu); struct step *step = g_new0(struct step, 1); bt_bdaddr_t bdaddr; bdaddr2android((const bdaddr_t *) hid_addr, &bdaddr); step->action_status = data->if_hid->get_report(&bdaddr, BTHH_INPUT_REPORT, 1, 20); schedule_action_verification(step); } static void hidhost_set_report_action(void) { struct test_data *data = tester_get_data(); const uint8_t *hid_addr = hciemu_get_client_bdaddr(data->hciemu); struct step *step = g_new0(struct step, 1); char *buf = "fe0201"; bt_bdaddr_t bdaddr; bdaddr2android((const bdaddr_t *) hid_addr, &bdaddr); step->action_status = data->if_hid->send_data(&bdaddr, buf); schedule_action_verification(step); } static void hidhost_send_data_action(void) { struct test_data *data = tester_get_data(); const uint8_t *hid_addr = hciemu_get_client_bdaddr(data->hciemu); struct step *step = g_new0(struct step, 1); char *buf = "010101"; bt_bdaddr_t bdaddr; bdaddr2android((const bdaddr_t *) hid_addr, &bdaddr); step->action_status = data->if_hid->set_report(&bdaddr, BTHH_INPUT_REPORT, buf); schedule_action_verification(step); } static void client_l2cap_rsp(uint8_t code, const void *data, uint16_t len, void *user_data) { struct test_data *t_data = tester_get_data(); struct bthost *bthost = hciemu_client_get_host(t_data->hciemu); struct emu_l2cap_cid_data *cid_data = user_data; const uint16_t *psm = data; const struct iovec con_req = raw_pdu(0x13, 0x00, /* PSM */ 0x41, 0x00); /* Source CID */ if (len < sizeof(*psm)) { tester_warn("Invalid l2cap response."); return; } switch (*psm) { case 0x40: bthost_add_cid_hook(bthost, cid_data->handle, 0x40, hid_ctrl_cid_hook_cb, cid_data); bthost_l2cap_req(bthost, cid_data->handle, 0x02, con_req.iov_base, con_req.iov_len, client_l2cap_rsp, cid_data); break; case 0x41: bthost_add_cid_hook(bthost, cid_data->handle, 0x41, hid_intr_cid_hook_cb, cid_data); break; default: break; } } static void hidhost_conn_cb(uint16_t handle, void *user_data) { const struct iovec con_req = raw_pdu(0x11, 0x00, /* PSM */ 0x40, 0x00); /* Source CID */ struct test_data *data = tester_get_data(); struct bthost *bthost = hciemu_client_get_host(data->hciemu); if (data->hciemu_type == HCIEMU_TYPE_BREDR) { tester_warn("Not handled device type."); return; } ctrl_cid_data.cid = 0x40; ctrl_cid_data.handle = handle; tester_print("Sending L2CAP Request from remote"); bthost_l2cap_req(bthost, handle, 0x02, con_req.iov_base, con_req.iov_len, client_l2cap_rsp, &ctrl_cid_data); } static struct test_case test_cases[] = { TEST_CASE_BREDRLE("HidHost Init", ACTION_SUCCESS(dummy_action, NULL), ), TEST_CASE_BREDRLE("HidHost Connect Success", ACTION_SUCCESS(bluetooth_enable_action, NULL), CALLBACK_STATE(CB_BT_ADAPTER_STATE_CHANGED, BT_STATE_ON), ACTION_SUCCESS(emu_setup_powered_remote_action, NULL), ACTION_SUCCESS(emu_set_ssp_mode_action, NULL), ACTION_SUCCESS(emu_add_l2cap_server_action, &l2cap_setup_sdp_data), ACTION_SUCCESS(emu_add_l2cap_server_action, &l2cap_setup_cc_data), ACTION_SUCCESS(emu_add_l2cap_server_action, &l2cap_setup_ic_data), ACTION_SUCCESS(hidhost_connect_action, NULL), CALLBACK_STATE(CB_HH_CONNECTION_STATE, BTHH_CONN_STATE_CONNECTED), ACTION_SUCCESS(bluetooth_disable_action, NULL), CALLBACK_STATE(CB_HH_CONNECTION_STATE, BTHH_CONN_STATE_DISCONNECTED), CALLBACK_STATE(CB_BT_ADAPTER_STATE_CHANGED, BT_STATE_OFF), ), TEST_CASE_BREDRLE("HidHost Disconnect Success", ACTION_SUCCESS(bluetooth_enable_action, NULL), CALLBACK_STATE(CB_BT_ADAPTER_STATE_CHANGED, BT_STATE_ON), ACTION_SUCCESS(emu_setup_powered_remote_action, NULL), ACTION_SUCCESS(emu_set_ssp_mode_action, NULL), ACTION_SUCCESS(emu_add_l2cap_server_action, &l2cap_setup_sdp_data), ACTION_SUCCESS(emu_add_l2cap_server_action, &l2cap_setup_cc_data), ACTION_SUCCESS(emu_add_l2cap_server_action, &l2cap_setup_ic_data), ACTION_SUCCESS(hidhost_connect_action, NULL), CALLBACK_STATE(CB_HH_CONNECTION_STATE, BTHH_CONN_STATE_CONNECTED), ACTION_SUCCESS(hidhost_disconnect_action, NULL), CALLBACK_STATE(CB_HH_CONNECTION_STATE, BTHH_CONN_STATE_DISCONNECTED), ), TEST_CASE_BREDRLE("HidHost VirtualUnplug Success", ACTION_SUCCESS(bluetooth_enable_action, NULL), CALLBACK_STATE(CB_BT_ADAPTER_STATE_CHANGED, BT_STATE_ON), ACTION_SUCCESS(emu_setup_powered_remote_action, NULL), ACTION_SUCCESS(emu_set_ssp_mode_action, NULL), ACTION_SUCCESS(emu_add_l2cap_server_action, &l2cap_setup_sdp_data), ACTION_SUCCESS(emu_add_l2cap_server_action, &l2cap_setup_cc_data), ACTION_SUCCESS(emu_add_l2cap_server_action, &l2cap_setup_ic_data), ACTION_SUCCESS(hidhost_connect_action, NULL), CALLBACK_STATE(CB_HH_CONNECTION_STATE, BTHH_CONN_STATE_CONNECTED), ACTION_SUCCESS(hidhost_virtual_unplug_action, NULL), CALLBACK_STATE(CB_HH_CONNECTION_STATE, BTHH_CONN_STATE_DISCONNECTED), ), TEST_CASE_BREDRLE("HidHost GetProtocol Success", ACTION_SUCCESS(bluetooth_enable_action, NULL), CALLBACK_STATE(CB_BT_ADAPTER_STATE_CHANGED, BT_STATE_ON), ACTION_SUCCESS(emu_setup_powered_remote_action, NULL), ACTION_SUCCESS(emu_set_ssp_mode_action, NULL), ACTION_SUCCESS(emu_add_l2cap_server_action, &l2cap_setup_sdp_data), ACTION_SUCCESS(emu_add_l2cap_server_action, &l2cap_setup_cc_data), ACTION_SUCCESS(emu_add_l2cap_server_action, &l2cap_setup_ic_data), ACTION_SUCCESS(hidhost_connect_action, NULL), CALLBACK_STATE(CB_HH_CONNECTION_STATE, BTHH_CONN_STATE_CONNECTED), ACTION_SUCCESS(hidhost_get_protocol_action, NULL), CALLBACK_HH_MODE(CB_HH_PROTOCOL_MODE, BTHH_OK, HID_MODE_BREDR), ), TEST_CASE_BREDRLE("HidHost SetProtocol Success", ACTION_SUCCESS(bluetooth_enable_action, NULL), CALLBACK_STATE(CB_BT_ADAPTER_STATE_CHANGED, BT_STATE_ON), ACTION_SUCCESS(emu_setup_powered_remote_action, NULL), ACTION_SUCCESS(emu_set_ssp_mode_action, NULL), ACTION_SUCCESS(emu_add_l2cap_server_action, &l2cap_setup_sdp_data), ACTION_SUCCESS(emu_add_l2cap_server_action, &l2cap_setup_cc_data), ACTION_SUCCESS(emu_add_l2cap_server_action, &l2cap_setup_ic_data), ACTION_SUCCESS(hidhost_connect_action, NULL), CALLBACK_STATE(CB_HH_CONNECTION_STATE, BTHH_CONN_STATE_CONNECTED), ACTION_SUCCESS(hidhost_set_protocol_action, NULL), CALLBACK_HH_MODE(CB_HH_PROTOCOL_MODE, BTHH_OK, HID_MODE_BREDR), ), TEST_CASE_BREDRLE("HidHost GetReport Success", ACTION_SUCCESS(bluetooth_enable_action, NULL), CALLBACK_STATE(CB_BT_ADAPTER_STATE_CHANGED, BT_STATE_ON), ACTION_SUCCESS(emu_setup_powered_remote_action, NULL), ACTION_SUCCESS(emu_set_ssp_mode_action, NULL), ACTION_SUCCESS(emu_add_l2cap_server_action, &l2cap_setup_sdp_data), ACTION_SUCCESS(emu_add_l2cap_server_action, &l2cap_setup_cc_data), ACTION_SUCCESS(emu_add_l2cap_server_action, &l2cap_setup_ic_data), ACTION_SUCCESS(hidhost_connect_action, NULL), CALLBACK_STATE(CB_HH_CONNECTION_STATE, BTHH_CONN_STATE_CONNECTED), ACTION_SUCCESS(hidhost_get_report_action, NULL), CALLBACK_HHREPORT(CB_HH_GET_REPORT, BTHH_OK, HID_EXPECTED_REPORT_SIZE), ), TEST_CASE_BREDRLE("HidHost SetReport Success", ACTION_SUCCESS(bluetooth_enable_action, NULL), CALLBACK_STATE(CB_BT_ADAPTER_STATE_CHANGED, BT_STATE_ON), ACTION_SUCCESS(emu_setup_powered_remote_action, NULL), ACTION_SUCCESS(emu_set_ssp_mode_action, NULL), ACTION_SUCCESS(emu_add_l2cap_server_action, &l2cap_setup_sdp_data), ACTION_SUCCESS(emu_add_l2cap_server_action, &l2cap_setup_cc_data), ACTION_SUCCESS(emu_add_l2cap_server_action, &l2cap_setup_ic_data), ACTION_SUCCESS(hidhost_connect_action, NULL), CALLBACK_STATE(CB_HH_CONNECTION_STATE, BTHH_CONN_STATE_CONNECTING), CALLBACK_STATE(CB_HH_CONNECTION_STATE, BTHH_CONN_STATE_CONNECTED), ACTION_SUCCESS(hidhost_set_report_action, NULL), CALLBACK(CB_EMU_CONFIRM_SEND_DATA), ), TEST_CASE_BREDRLE("HidHost SendData Success", ACTION_SUCCESS(bluetooth_enable_action, NULL), CALLBACK_STATE(CB_BT_ADAPTER_STATE_CHANGED, BT_STATE_ON), ACTION_SUCCESS(emu_setup_powered_remote_action, NULL), ACTION_SUCCESS(emu_set_ssp_mode_action, NULL), ACTION_SUCCESS(emu_add_l2cap_server_action, &l2cap_setup_sdp_data), ACTION_SUCCESS(emu_add_l2cap_server_action, &l2cap_setup_cc_data), ACTION_SUCCESS(emu_add_l2cap_server_action, &l2cap_setup_ic_data), ACTION_SUCCESS(hidhost_connect_action, NULL), CALLBACK_STATE(CB_HH_CONNECTION_STATE, BTHH_CONN_STATE_CONNECTED), ACTION_SUCCESS(hidhost_send_data_action, NULL), CALLBACK(CB_EMU_CONFIRM_SEND_DATA), ), TEST_CASE_BREDRLE("HidHost Connect Encrypted Success", ACTION_SUCCESS(bluetooth_enable_action, NULL), CALLBACK_STATE(CB_BT_ADAPTER_STATE_CHANGED, BT_STATE_ON), ACTION_SUCCESS(emu_setup_powered_remote_action, NULL), ACTION_SUCCESS(emu_set_ssp_mode_action, NULL), ACTION_SUCCESS(set_default_ssp_request_handler, NULL), ACTION_SUCCESS(emu_add_l2cap_server_action, &l2cap_setup_kb_sdp_data), ACTION_SUCCESS(emu_add_l2cap_server_action, &l2cap_setup_cc_data), ACTION_SUCCESS(emu_add_l2cap_server_action, &l2cap_setup_ic_data), ACTION_SUCCESS(hidhost_connect_action, NULL), CALLBACK(CB_EMU_ENCRYPTION_ENABLED), CALLBACK_STATE(CB_HH_CONNECTION_STATE, BTHH_CONN_STATE_CONNECTED), ACTION_SUCCESS(hidhost_send_data_action, NULL), ACTION_SUCCESS(bluetooth_disable_action, NULL), CALLBACK_STATE(CB_HH_CONNECTION_STATE, BTHH_CONN_STATE_DISCONNECTED), CALLBACK_STATE(CB_BT_ADAPTER_STATE_CHANGED, BT_STATE_OFF), ), TEST_CASE_BREDRLE("HidHost Reject Unknown Remote Connection", ACTION_SUCCESS(bluetooth_enable_action, NULL), CALLBACK_STATE(CB_BT_ADAPTER_STATE_CHANGED, BT_STATE_ON), ACTION_SUCCESS(bt_set_property_action, &prop_test_scan_mode_conn), CALLBACK_ADAPTER_PROPS(&prop_test_scan_mode_conn, 1), ACTION_SUCCESS(emu_setup_powered_remote_action, NULL), ACTION_SUCCESS(emu_add_l2cap_server_action, &l2cap_setup_kb_sdp_data), ACTION_SUCCESS(emu_add_l2cap_server_action, &l2cap_setup_cc_data), ACTION_SUCCESS(emu_add_l2cap_server_action, &l2cap_setup_ic_data), /* Trigger incoming connection */ ACTION_SUCCESS(emu_set_connect_cb_action, hidhost_conn_cb), ACTION_SUCCESS(emu_remote_connect_hci_action, NULL), CALLBACK(CB_EMU_CONNECTION_REJECTED), ), }; struct queue *get_hidhost_tests(void) { uint16_t i = 0; list = queue_new(); for (; i < sizeof(test_cases) / sizeof(test_cases[0]); ++i) queue_push_tail(list, &test_cases[i]); return list; } void remove_hidhost_tests(void) { queue_destroy(list, NULL); } bluez-5.82/android/PaxHeaders/test-ipc.c0000644000000000000000000000005014015011623015153 xustar0020 atime=1743516867 20 ctime=1743591279 bluez-5.82/android/test-ipc.c0000644000000000000000000003134114015011623014636 0ustar00rootroot// SPDX-License-Identifier: LGPL-2.1-or-later /* * * BlueZ - Bluetooth protocol stack for Linux * * Copyright (C) 2013-2014 Intel Corporation. All rights reserved. * * */ #ifdef HAVE_CONFIG_H #include #endif #include #include #include #include #include #include #include #include #include #include #include #include "src/shared/util.h" #include "src/log.h" #include "android/ipc-common.h" #include "android/ipc.h" static const char HAL_SK_PATH[] = "\0test_hal_socket"; #define SERVICE_ID_MAX 10 struct test_data { bool disconnect; const void *cmd; uint16_t cmd_size; uint8_t service; const struct ipc_handler *handlers; uint8_t handlers_size; }; struct context { GMainLoop *main_loop; int sk; guint source; guint cmd_source; guint notif_source; GIOChannel *cmd_io; GIOChannel *notif_io; const struct test_data *data; }; static struct ipc *ipc = NULL; static void context_quit(struct context *context) { g_main_loop_quit(context->main_loop); } static gboolean cmd_watch(GIOChannel *io, GIOCondition cond, gpointer user_data) { struct context *context = user_data; const struct test_data *test_data = context->data; const struct ipc_hdr *sent_msg = test_data->cmd; uint8_t buf[128]; int sk; struct ipc_hdr success_resp = { .service_id = sent_msg->service_id, .opcode = sent_msg->opcode, .len = 0, }; if (cond & (G_IO_HUP | G_IO_ERR | G_IO_NVAL)) { g_assert(test_data->disconnect); return FALSE; } g_assert(!test_data->disconnect); sk = g_io_channel_unix_get_fd(io); g_assert(read(sk, buf, sizeof(buf)) == sizeof(struct ipc_hdr)); g_assert(!memcmp(&success_resp, buf, sizeof(struct ipc_hdr))); context_quit(context); return TRUE; } static gboolean notif_watch(GIOChannel *io, GIOCondition cond, gpointer user_data) { struct context *context = user_data; const struct test_data *test_data = context->data; if (cond & (G_IO_HUP | G_IO_ERR | G_IO_NVAL)) { g_assert(test_data->disconnect); return FALSE; } g_assert(!test_data->disconnect); return TRUE; } static gboolean connect_handler(GIOChannel *io, GIOCondition cond, gpointer user_data) { struct context *context = user_data; const struct test_data *test_data = context->data; GIOChannel *new_io; GIOCondition watch_cond; int sk; if (cond & (G_IO_HUP | G_IO_ERR | G_IO_NVAL)) { g_assert(FALSE); return FALSE; } g_assert(!context->cmd_source || !context->notif_source); sk = accept(context->sk, NULL, NULL); g_assert(sk >= 0); new_io = g_io_channel_unix_new(sk); watch_cond = G_IO_IN | G_IO_HUP | G_IO_ERR | G_IO_NVAL; if (context->cmd_source && !context->notif_source) { context->notif_source = g_io_add_watch(new_io, watch_cond, notif_watch, context); g_assert(context->notif_source > 0); context->notif_io = new_io; } if (!context->cmd_source) { context->cmd_source = g_io_add_watch(new_io, watch_cond, cmd_watch, context); context->cmd_io = new_io; } if (context->cmd_source && context->notif_source && !test_data->cmd) context_quit(context); return TRUE; } static struct context *create_context(gconstpointer data) { struct context *context = g_new0(struct context, 1); struct sockaddr_un addr; GIOChannel *io; int ret, sk; context->main_loop = g_main_loop_new(NULL, FALSE); g_assert(context->main_loop); sk = socket(AF_LOCAL, SOCK_SEQPACKET, 0); g_assert(sk >= 0); memset(&addr, 0, sizeof(addr)); addr.sun_family = AF_UNIX; memcpy(addr.sun_path, HAL_SK_PATH, sizeof(HAL_SK_PATH)); ret = bind(sk, (struct sockaddr *) &addr, sizeof(addr)); g_assert(ret == 0); ret = listen(sk, 5); g_assert(ret == 0); io = g_io_channel_unix_new(sk); g_io_channel_set_close_on_unref(io, TRUE); context->source = g_io_add_watch(io, G_IO_IN | G_IO_HUP | G_IO_ERR | G_IO_NVAL, connect_handler, context); g_assert(context->source > 0); g_io_channel_unref(io); context->sk = sk; context->data = data; return context; } static void execute_context(struct context *context) { g_main_loop_run(context->main_loop); g_io_channel_shutdown(context->notif_io, TRUE, NULL); g_io_channel_shutdown(context->cmd_io, TRUE, NULL); g_io_channel_unref(context->cmd_io); g_io_channel_unref(context->notif_io); g_source_remove(context->notif_source); g_source_remove(context->cmd_source); g_source_remove(context->source); g_main_loop_unref(context->main_loop); g_free(context); } static void disconnected(void *data) { struct context *context = data; g_assert(context->data->disconnect); context_quit(context); } static void test_init(gconstpointer data) { struct context *context = create_context(data); ipc = ipc_init(HAL_SK_PATH, sizeof(HAL_SK_PATH), SERVICE_ID_MAX, true, NULL, NULL); g_assert(ipc); execute_context(context); ipc_cleanup(ipc); ipc = NULL; } static gboolean send_cmd(gpointer user_data) { struct context *context = user_data; const struct test_data *test_data = context->data; int sk; sk = g_io_channel_unix_get_fd(context->cmd_io); g_assert(sk >= 0); g_assert(write(sk, test_data->cmd, test_data->cmd_size) == test_data->cmd_size); return FALSE; } static gboolean register_service(gpointer user_data) { struct context *context = user_data; const struct test_data *test_data = context->data; ipc_register(ipc, test_data->service, test_data->handlers, test_data->handlers_size); return FALSE; } static gboolean unregister_service(gpointer user_data) { struct context *context = user_data; const struct test_data *test_data = context->data; ipc_unregister(ipc, test_data->service); return FALSE; } static void test_cmd(gconstpointer data) { struct context *context = create_context(data); ipc = ipc_init(HAL_SK_PATH, sizeof(HAL_SK_PATH), SERVICE_ID_MAX, true, disconnected, context); g_assert(ipc); g_idle_add(send_cmd, context); execute_context(context); ipc_cleanup(ipc); ipc = NULL; } static void test_cmd_reg(gconstpointer data) { struct context *context = create_context(data); const struct test_data *test_data = context->data; ipc = ipc_init(HAL_SK_PATH, sizeof(HAL_SK_PATH), SERVICE_ID_MAX, true, disconnected, context); g_assert(ipc); g_idle_add(register_service, context); g_idle_add(send_cmd, context); execute_context(context); ipc_unregister(ipc, test_data->service); ipc_cleanup(ipc); ipc = NULL; } static void test_cmd_reg_1(gconstpointer data) { struct context *context = create_context(data); ipc = ipc_init(HAL_SK_PATH, sizeof(HAL_SK_PATH), SERVICE_ID_MAX, true, disconnected, context); g_assert(ipc); g_idle_add(register_service, context); g_idle_add(unregister_service, context); g_idle_add(send_cmd, context); execute_context(context); ipc_cleanup(ipc); ipc = NULL; } static void test_cmd_handler_1(const void *buf, uint16_t len) { ipc_send_rsp(ipc, 0, 1, 0); } static void test_cmd_handler_2(const void *buf, uint16_t len) { ipc_send_rsp(ipc, 0, 2, 0); } static void test_cmd_handler_invalid(const void *buf, uint16_t len) { g_assert(false); } static const struct test_data test_init_1 = {}; static const struct ipc_hdr test_cmd_1_hdr = { .service_id = 0, .opcode = 1, .len = 0 }; static const struct ipc_hdr test_cmd_2_hdr = { .service_id = 0, .opcode = 2, .len = 0 }; static const struct test_data test_cmd_service_invalid_1 = { .cmd = &test_cmd_1_hdr, .cmd_size = sizeof(test_cmd_1_hdr), .disconnect = true, }; static const struct ipc_handler cmd_handlers[] = { { test_cmd_handler_1, false, 0 } }; static const struct test_data test_cmd_service_valid_1 = { .cmd = &test_cmd_1_hdr, .cmd_size = sizeof(test_cmd_1_hdr), .service = 0, .handlers = cmd_handlers, .handlers_size = 1 }; static const struct test_data test_cmd_service_invalid_2 = { .cmd = &test_cmd_1_hdr, .cmd_size = sizeof(test_cmd_1_hdr), .service = 0, .handlers = cmd_handlers, .handlers_size = 1, .disconnect = true, }; static const struct ipc_handler cmd_handlers_invalid_2[] = { { test_cmd_handler_1, false, 0 }, { test_cmd_handler_invalid, false, 0 } }; static const struct ipc_handler cmd_handlers_invalid_1[] = { { test_cmd_handler_invalid, false, 0 }, { test_cmd_handler_2, false, 0 }, }; static const struct test_data test_cmd_opcode_valid_1 = { .cmd = &test_cmd_1_hdr, .cmd_size = sizeof(test_cmd_1_hdr), .service = 0, .handlers = cmd_handlers_invalid_2, .handlers_size = 2, }; static const struct test_data test_cmd_opcode_valid_2 = { .cmd = &test_cmd_2_hdr, .cmd_size = sizeof(test_cmd_2_hdr), .service = 0, .handlers = cmd_handlers_invalid_1, .handlers_size = 2, }; static const struct test_data test_cmd_opcode_invalid_1 = { .cmd = &test_cmd_2_hdr, .cmd_size = sizeof(test_cmd_2_hdr), .service = 0, .handlers = cmd_handlers, .handlers_size = 1, .disconnect = true, }; static const struct test_data test_cmd_hdr_invalid = { .cmd = &test_cmd_1_hdr, .cmd_size = sizeof(test_cmd_1_hdr) - 1, .service = 0, .handlers = cmd_handlers, .handlers_size = 1, .disconnect = true, }; #define VARDATA_EX1 "some data example" struct vardata { struct ipc_hdr hdr; uint8_t data[IPC_MTU - sizeof(struct ipc_hdr)]; } __attribute__((packed)); static const struct vardata test_cmd_vardata = { .hdr.service_id = 0, .hdr.opcode = 1, .hdr.len = sizeof(VARDATA_EX1), .data = VARDATA_EX1, }; static const struct ipc_handler cmd_vardata_handlers[] = { { test_cmd_handler_1, true, sizeof(VARDATA_EX1) } }; static const struct test_data test_cmd_vardata_valid = { .cmd = &test_cmd_vardata, .cmd_size = sizeof(struct ipc_hdr) + sizeof(VARDATA_EX1), .service = 0, .handlers = cmd_vardata_handlers, .handlers_size = 1, }; static const struct ipc_handler cmd_vardata_handlers_valid2[] = { { test_cmd_handler_1, true, sizeof(VARDATA_EX1) - 1 } }; static const struct test_data test_cmd_vardata_valid_2 = { .cmd = &test_cmd_vardata, .cmd_size = sizeof(struct ipc_hdr) + sizeof(VARDATA_EX1), .service = 0, .handlers = cmd_vardata_handlers_valid2, .handlers_size = 1, }; static const struct test_data test_cmd_vardata_invalid_1 = { .cmd = &test_cmd_vardata, .cmd_size = sizeof(struct ipc_hdr) + sizeof(VARDATA_EX1) - 1, .service = 0, .handlers = cmd_vardata_handlers, .handlers_size = 1, .disconnect = true, }; static const struct ipc_hdr test_cmd_service_offrange_hdr = { .service_id = SERVICE_ID_MAX + 1, .opcode = 1, .len = 0 }; static const struct test_data test_cmd_service_offrange = { .cmd = &test_cmd_service_offrange_hdr, .cmd_size = sizeof(struct ipc_hdr), .service = 0, .handlers = cmd_handlers, .handlers_size = 1, .disconnect = true, }; static const struct vardata test_cmd_invalid_data_1 = { .hdr.service_id = 0, .hdr.opcode = 1, .hdr.len = sizeof(VARDATA_EX1), .data = VARDATA_EX1, }; static const struct test_data test_cmd_msg_invalid_1 = { .cmd = &test_cmd_invalid_data_1, .cmd_size = sizeof(struct ipc_hdr) + sizeof(VARDATA_EX1) - 1, .service = 0, .handlers = cmd_handlers, .handlers_size = 1, .disconnect = true, }; static const struct vardata test_cmd_invalid_data_2 = { .hdr.service_id = 0, .hdr.opcode = 1, .hdr.len = sizeof(VARDATA_EX1) - 1, .data = VARDATA_EX1, }; static const struct test_data test_cmd_msg_invalid_2 = { .cmd = &test_cmd_invalid_data_2, .cmd_size = sizeof(struct ipc_hdr) + sizeof(VARDATA_EX1), .service = 0, .handlers = cmd_handlers, .handlers_size = 1, .disconnect = true, }; int main(int argc, char *argv[]) { g_test_init(&argc, &argv, NULL); if (g_test_verbose()) __btd_log_init("*", 0); g_test_add_data_func("/android_ipc/init", &test_init_1, test_init); g_test_add_data_func("/android_ipc/service_invalid_1", &test_cmd_service_invalid_1, test_cmd); g_test_add_data_func("/android_ipc/service_valid_1", &test_cmd_service_valid_1, test_cmd_reg); g_test_add_data_func("/android_ipc/service_invalid_2", &test_cmd_service_invalid_2, test_cmd_reg_1); g_test_add_data_func("/android_ipc/opcode_valid_1", &test_cmd_opcode_valid_1, test_cmd_reg); g_test_add_data_func("/android_ipc/opcode_valid_2", &test_cmd_opcode_valid_2, test_cmd_reg); g_test_add_data_func("/android_ipc/opcode_invalid_1", &test_cmd_opcode_invalid_1, test_cmd_reg); g_test_add_data_func("/android_ipc/vardata_valid", &test_cmd_vardata_valid, test_cmd_reg); g_test_add_data_func("/android_ipc/vardata_valid_2", &test_cmd_vardata_valid_2, test_cmd_reg); g_test_add_data_func("/android_ipc/vardata_invalid_1", &test_cmd_vardata_invalid_1, test_cmd_reg); g_test_add_data_func("/android_ipc/service_offrange", &test_cmd_service_offrange, test_cmd_reg); g_test_add_data_func("/android_ipc/hdr_invalid", &test_cmd_hdr_invalid, test_cmd_reg); g_test_add_data_func("/android_ipc/msg_invalid_1", &test_cmd_msg_invalid_1, test_cmd_reg); g_test_add_data_func("/android_ipc/msg_invalid_2", &test_cmd_msg_invalid_2, test_cmd_reg); return g_test_run(); } bluez-5.82/android/PaxHeaders/avrcp.c0000644000000000000000000000005014015011623014536 xustar0020 atime=1743516866 20 ctime=1743591278 bluez-5.82/android/avrcp.c0000644000000000000000000006270514015011623014231 0ustar00rootroot// SPDX-License-Identifier: LGPL-2.1-or-later /* * * BlueZ - Bluetooth protocol stack for Linux * * Copyright (C) 2013-2014 Intel Corporation. All rights reserved. * * */ #ifdef HAVE_CONFIG_H #include #endif #include #include #include #include #include "btio/btio.h" #include "lib/bluetooth.h" #include "lib/sdp.h" #include "lib/sdp_lib.h" #include "src/sdp-client.h" #include "src/shared/util.h" #include "src/log.h" #include "avctp.h" #include "avrcp-lib.h" #include "hal-msg.h" #include "ipc-common.h" #include "ipc.h" #include "bluetooth.h" #include "avrcp.h" #include "utils.h" #define L2CAP_PSM_AVCTP 0x17 static bdaddr_t adapter_addr; static uint32_t record_tg_id = 0; static uint32_t record_ct_id = 0; static GSList *devices = NULL; static GIOChannel *server = NULL; static struct ipc *hal_ipc = NULL; struct avrcp_request { struct avrcp_device *dev; uint8_t pdu_id; uint8_t event_id; uint8_t transaction; }; struct avrcp_device { bdaddr_t dst; uint16_t version; uint16_t features; struct avrcp *session; GIOChannel *io; GQueue *queue; }; static struct avrcp_request *pop_request(uint8_t pdu_id, uint8_t event_id, bool peek) { GSList *l; for (l = devices; l; l = g_slist_next(l)) { struct avrcp_device *dev = l->data; GList *reqs = g_queue_peek_head_link(dev->queue); int i; for (i = 0; reqs; reqs = g_list_next(reqs), i++) { struct avrcp_request *req = reqs->data; if (req->pdu_id != pdu_id || req->event_id != event_id) continue; if (!peek) g_queue_pop_nth(dev->queue, i); return req; } } return NULL; } static void handle_get_play_status(const void *buf, uint16_t len) { const struct hal_cmd_avrcp_get_play_status *cmd = buf; uint8_t status; struct avrcp_request *req; int ret; DBG(""); req = pop_request(AVRCP_GET_PLAY_STATUS, 0, false); if (!req) { status = HAL_STATUS_FAILED; goto done; } ret = avrcp_get_play_status_rsp(req->dev->session, req->transaction, cmd->position, cmd->duration, cmd->status); if (ret < 0) { status = HAL_STATUS_FAILED; g_free(req); goto done; } status = HAL_STATUS_SUCCESS; g_free(req); done: ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_AVRCP, HAL_OP_AVRCP_GET_PLAY_STATUS, status); } static void handle_list_player_attrs(const void *buf, uint16_t len) { DBG(""); ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_AVRCP, HAL_OP_AVRCP_LIST_PLAYER_ATTRS, HAL_STATUS_FAILED); } static void handle_list_player_values(const void *buf, uint16_t len) { DBG(""); ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_AVRCP, HAL_OP_AVRCP_LIST_PLAYER_VALUES, HAL_STATUS_FAILED); } static void handle_get_player_attrs(const void *buf, uint16_t len) { DBG(""); ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_AVRCP, HAL_OP_AVRCP_GET_PLAYER_ATTRS, HAL_STATUS_FAILED); } static void handle_get_player_attrs_text(const void *buf, uint16_t len) { DBG(""); ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_AVRCP, HAL_OP_AVRCP_GET_PLAYER_ATTRS_TEXT, HAL_STATUS_FAILED); } static void handle_get_player_values_text(const void *buf, uint16_t len) { DBG(""); ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_AVRCP, HAL_OP_AVRCP_GET_PLAYER_VALUES_TEXT, HAL_STATUS_FAILED); } static size_t write_element_text(uint8_t id, uint8_t text_len, uint8_t *text, uint8_t *pdu) { uint16_t charset = 106; size_t len = 0; put_be32(id, pdu); pdu += 4; len += 4; put_be16(charset, pdu); pdu += 2; len += 2; put_be16(text_len, pdu); pdu += 2; len += 2; memcpy(pdu, text, text_len); len += text_len; return len; } static void write_element_attrs(uint8_t *ptr, uint8_t number, uint8_t *pdu, size_t *len) { int i; *pdu = number; pdu++; *len += 1; for (i = 0; i < number; i++) { struct hal_avrcp_player_setting_text *text = (void *) ptr; size_t ret; ret = write_element_text(text->id, text->len, text->text, pdu); ptr += sizeof(*text) + text->len; pdu += ret; *len += ret; } } static void handle_get_element_attrs_text(const void *buf, uint16_t len) { struct hal_cmd_avrcp_get_element_attrs_text *cmd = (void *) buf; uint8_t status; struct avrcp_request *req; uint8_t pdu[IPC_MTU]; uint8_t *ptr; size_t pdu_len; int ret; DBG(""); req = pop_request(AVRCP_GET_ELEMENT_ATTRIBUTES, 0, false); if (!req) { status = HAL_STATUS_FAILED; goto done; } ptr = (uint8_t *) &cmd->values[0]; pdu_len = 0; write_element_attrs(ptr, cmd->number, pdu, &pdu_len); ret = avrcp_get_element_attrs_rsp(req->dev->session, req->transaction, pdu, pdu_len); if (ret < 0) { status = HAL_STATUS_FAILED; g_free(req); goto done; } status = HAL_STATUS_SUCCESS; g_free(req); done: ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_AVRCP, HAL_OP_AVRCP_GET_ELEMENT_ATTRS_TEXT, status); } static void handle_set_player_attrs_value(const void *buf, uint16_t len) { DBG(""); ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_AVRCP, HAL_OP_AVRCP_SET_PLAYER_ATTRS_VALUE, HAL_STATUS_FAILED); } static void handle_register_notification(const void *buf, uint16_t len) { struct hal_cmd_avrcp_register_notification *cmd = (void *) buf; uint8_t status; struct avrcp_request *req; uint8_t code; bool peek = false; int ret; DBG(""); switch (cmd->type) { case HAL_AVRCP_EVENT_TYPE_INTERIM: code = AVC_CTYPE_INTERIM; peek = true; break; case HAL_AVRCP_EVENT_TYPE_CHANGED: code = AVC_CTYPE_CHANGED; break; default: status = HAL_STATUS_FAILED; goto done; } req = pop_request(AVRCP_REGISTER_NOTIFICATION, cmd->event, peek); if (!req) { status = HAL_STATUS_FAILED; goto done; } ret = avrcp_register_notification_rsp(req->dev->session, req->transaction, code, cmd->event, cmd->data, cmd->len); if (ret < 0) { status = HAL_STATUS_FAILED; if (!peek) g_free(req); goto done; } status = HAL_STATUS_SUCCESS; if (!peek) g_free(req); done: ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_AVRCP, HAL_OP_AVRCP_REGISTER_NOTIFICATION, status); } static void handle_set_volume(const void *buf, uint16_t len) { struct hal_cmd_avrcp_set_volume *cmd = (void *) buf; struct avrcp_device *dev; uint8_t status; int ret; DBG(""); if (!devices) { error("AVRCP: No device found to set volume"); status = HAL_STATUS_FAILED; goto done; } /* * Peek the first device since the HAL cannot really address a specific * device it might mean there could only be one connected. */ dev = devices->data; ret = avrcp_set_volume(dev->session, cmd->value & 0x7f); if (ret < 0) { status = HAL_STATUS_FAILED; goto done; } status = HAL_STATUS_SUCCESS; done: ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_AVRCP, HAL_OP_AVRCP_SET_VOLUME, status); } static const struct ipc_handler cmd_handlers[] = { /* HAL_OP_AVRCP_GET_PLAY_STATUS */ { handle_get_play_status, false, sizeof(struct hal_cmd_avrcp_get_play_status) }, /* HAL_OP_AVRCP_LIST_PLAYER_ATTRS */ { handle_list_player_attrs, true, sizeof(struct hal_cmd_avrcp_list_player_attrs) }, /* HAL_OP_AVRCP_LIST_PLAYER_VALUES */ { handle_list_player_values, true, sizeof(struct hal_cmd_avrcp_list_player_values) }, /* HAL_OP_AVRCP_GET_PLAYER_ATTRS */ { handle_get_player_attrs, true, sizeof(struct hal_cmd_avrcp_get_player_attrs) }, /* HAL_OP_AVRCP_GET_PLAYER_ATTRS_TEXT */ { handle_get_player_attrs_text, true, sizeof(struct hal_cmd_avrcp_get_player_attrs_text) }, /* HAL_OP_AVRCP_GET_PLAYER_VALUES_TEXT */ { handle_get_player_values_text, true, sizeof(struct hal_cmd_avrcp_get_player_values_text) }, /* HAL_OP_AVRCP_GET_ELEMENT_ATTRS_TEXT */ { handle_get_element_attrs_text, true, sizeof(struct hal_cmd_avrcp_get_element_attrs_text) }, /* HAL_OP_AVRCP_SET_PLAYER_ATTRS_VALUE */ { handle_set_player_attrs_value, true, sizeof(struct hal_cmd_avrcp_set_player_attrs_value) }, /* HAL_OP_AVRCP_REGISTER_NOTIFICATION */ { handle_register_notification, true, sizeof(struct hal_cmd_avrcp_register_notification) }, /* HAL_OP_AVRCP_SET_VOLUME */ { handle_set_volume, false, sizeof(struct hal_cmd_avrcp_set_volume) }, }; static sdp_record_t *avrcp_tg_record(void) { sdp_list_t *svclass_id, *pfseq, *apseq, *root; uuid_t root_uuid, l2cap, avctp, avrtg; sdp_profile_desc_t profile[1]; sdp_list_t *aproto_control, *proto_control[2]; sdp_record_t *record; sdp_data_t *psm, *version, *features; uint16_t lp = L2CAP_PSM_AVCTP; uint16_t avrcp_ver = 0x0105, avctp_ver = 0x0104; uint16_t feat = (AVRCP_FEATURE_CATEGORY_1 | AVRCP_FEATURE_CATEGORY_2 | AVRCP_FEATURE_CATEGORY_3 | AVRCP_FEATURE_CATEGORY_4); record = sdp_record_alloc(); if (!record) return NULL; sdp_uuid16_create(&root_uuid, PUBLIC_BROWSE_GROUP); root = sdp_list_append(NULL, &root_uuid); sdp_set_browse_groups(record, root); /* Service Class ID List */ sdp_uuid16_create(&avrtg, AV_REMOTE_TARGET_SVCLASS_ID); svclass_id = sdp_list_append(NULL, &avrtg); sdp_set_service_classes(record, svclass_id); /* Protocol Descriptor List */ sdp_uuid16_create(&l2cap, L2CAP_UUID); proto_control[0] = sdp_list_append(NULL, &l2cap); psm = sdp_data_alloc(SDP_UINT16, &lp); proto_control[0] = sdp_list_append(proto_control[0], psm); apseq = sdp_list_append(NULL, proto_control[0]); sdp_uuid16_create(&avctp, AVCTP_UUID); proto_control[1] = sdp_list_append(NULL, &avctp); version = sdp_data_alloc(SDP_UINT16, &avctp_ver); proto_control[1] = sdp_list_append(proto_control[1], version); apseq = sdp_list_append(apseq, proto_control[1]); aproto_control = sdp_list_append(NULL, apseq); sdp_set_access_protos(record, aproto_control); /* Bluetooth Profile Descriptor List */ sdp_uuid16_create(&profile[0].uuid, AV_REMOTE_PROFILE_ID); profile[0].version = avrcp_ver; pfseq = sdp_list_append(NULL, &profile[0]); sdp_set_profile_descs(record, pfseq); features = sdp_data_alloc(SDP_UINT16, &feat); sdp_attr_add(record, SDP_ATTR_SUPPORTED_FEATURES, features); sdp_set_info_attr(record, "AVRCP TG", NULL, NULL); sdp_data_free(psm); sdp_data_free(version); sdp_list_free(proto_control[0], NULL); sdp_list_free(proto_control[1], NULL); sdp_list_free(apseq, NULL); sdp_list_free(aproto_control, NULL); sdp_list_free(pfseq, NULL); sdp_list_free(root, NULL); sdp_list_free(svclass_id, NULL); return record; } static sdp_record_t *avrcp_ct_record(void) { sdp_list_t *svclass_id, *pfseq, *apseq, *root; uuid_t root_uuid, l2cap, avctp, avrct, avrctr; sdp_profile_desc_t profile[1]; sdp_list_t *aproto, *proto[2]; sdp_record_t *record; sdp_data_t *psm, *version, *features; uint16_t lp = AVCTP_CONTROL_PSM; uint16_t avrcp_ver = 0x0105, avctp_ver = 0x0104; uint16_t feat = ( AVRCP_FEATURE_CATEGORY_1 | AVRCP_FEATURE_CATEGORY_2 | AVRCP_FEATURE_CATEGORY_3 | AVRCP_FEATURE_CATEGORY_4); record = sdp_record_alloc(); if (!record) return NULL; sdp_uuid16_create(&root_uuid, PUBLIC_BROWSE_GROUP); root = sdp_list_append(NULL, &root_uuid); sdp_set_browse_groups(record, root); /* Service Class ID List */ sdp_uuid16_create(&avrct, AV_REMOTE_SVCLASS_ID); svclass_id = sdp_list_append(NULL, &avrct); sdp_uuid16_create(&avrctr, AV_REMOTE_CONTROLLER_SVCLASS_ID); svclass_id = sdp_list_append(svclass_id, &avrctr); sdp_set_service_classes(record, svclass_id); /* Protocol Descriptor List */ sdp_uuid16_create(&l2cap, L2CAP_UUID); proto[0] = sdp_list_append(NULL, &l2cap); psm = sdp_data_alloc(SDP_UINT16, &lp); proto[0] = sdp_list_append(proto[0], psm); apseq = sdp_list_append(NULL, proto[0]); sdp_uuid16_create(&avctp, AVCTP_UUID); proto[1] = sdp_list_append(NULL, &avctp); version = sdp_data_alloc(SDP_UINT16, &avctp_ver); proto[1] = sdp_list_append(proto[1], version); apseq = sdp_list_append(apseq, proto[1]); aproto = sdp_list_append(NULL, apseq); sdp_set_access_protos(record, aproto); /* Bluetooth Profile Descriptor List */ sdp_uuid16_create(&profile[0].uuid, AV_REMOTE_PROFILE_ID); profile[0].version = avrcp_ver; pfseq = sdp_list_append(NULL, &profile[0]); sdp_set_profile_descs(record, pfseq); features = sdp_data_alloc(SDP_UINT16, &feat); sdp_attr_add(record, SDP_ATTR_SUPPORTED_FEATURES, features); sdp_set_info_attr(record, "AVRCP CT", NULL, NULL); free(psm); free(version); sdp_list_free(proto[0], NULL); sdp_list_free(proto[1], NULL); sdp_list_free(apseq, NULL); sdp_list_free(pfseq, NULL); sdp_list_free(aproto, NULL); sdp_list_free(root, NULL); sdp_list_free(svclass_id, NULL); return record; } static void queue_free(void *data, void *user_data) { g_free(data); } static void avrcp_device_free(void *data) { struct avrcp_device *dev = data; if (dev->queue) { g_queue_foreach(dev->queue, queue_free, NULL); g_queue_free(dev->queue); } if (dev->session) avrcp_shutdown(dev->session); if (dev->io) { g_io_channel_shutdown(dev->io, FALSE, NULL); g_io_channel_unref(dev->io); } g_free(dev); } static void avrcp_device_remove(struct avrcp_device *dev) { devices = g_slist_remove(devices, dev); avrcp_device_free(dev); } static struct avrcp_device *avrcp_device_new(const bdaddr_t *dst) { struct avrcp_device *dev; dev = g_new0(struct avrcp_device, 1); bacpy(&dev->dst, dst); devices = g_slist_prepend(devices, dev); return dev; } static int device_cmp(gconstpointer s, gconstpointer user_data) { const struct avrcp_device *dev = s; const bdaddr_t *dst = user_data; return bacmp(&dev->dst, dst); } static struct avrcp_device *avrcp_device_find(const bdaddr_t *dst) { GSList *l; l = g_slist_find_custom(devices, dst, device_cmp); if (!l) return NULL; return l->data; } static void disconnect_cb(void *data) { struct avrcp_device *dev = data; DBG(""); dev->session = NULL; avrcp_device_remove(dev); } static bool handle_fast_forward(struct avrcp *session, bool pressed, void *user_data) { struct hal_ev_avrcp_passthrough_cmd ev; DBG("pressed %s", pressed ? "true" : "false"); ev.id = AVC_FAST_FORWARD; ev.state = pressed; ipc_send_notif(hal_ipc, HAL_SERVICE_ID_AVRCP, HAL_EV_AVRCP_PASSTHROUGH_CMD, sizeof(ev), &ev); return true; } static bool handle_rewind(struct avrcp *session, bool pressed, void *user_data) { struct hal_ev_avrcp_passthrough_cmd ev; DBG("pressed %s", pressed ? "true" : "false"); ev.id = AVC_REWIND; ev.state = pressed; ipc_send_notif(hal_ipc, HAL_SERVICE_ID_AVRCP, HAL_EV_AVRCP_PASSTHROUGH_CMD, sizeof(ev), &ev); return true; } static const struct avrcp_passthrough_handler passthrough_handlers[] = { { AVC_FAST_FORWARD, handle_fast_forward }, { AVC_REWIND, handle_rewind }, { }, }; static int handle_get_capabilities_cmd(struct avrcp *session, uint8_t transaction, void *user_data) { uint8_t events[] = { AVRCP_EVENT_STATUS_CHANGED, AVRCP_EVENT_TRACK_CHANGED, AVRCP_EVENT_PLAYBACK_POS_CHANGED }; DBG(""); /* * Android do not provide this info via HAL so the list most * be hardcoded according to what RegisterNotification can * actually handle */ avrcp_get_capabilities_rsp(session, transaction, sizeof(events), events); return 0; } static void push_request(struct avrcp_device *dev, uint8_t pdu_id, uint8_t event_id, uint8_t transaction) { struct avrcp_request *req; req = g_new0(struct avrcp_request, 1); req->dev = dev; req->pdu_id = pdu_id; req->event_id = event_id; req->transaction = transaction; g_queue_push_tail(dev->queue, req); } static int handle_get_play_status_cmd(struct avrcp *session, uint8_t transaction, void *user_data) { struct avrcp_device *dev = user_data; DBG(""); ipc_send_notif(hal_ipc, HAL_SERVICE_ID_AVRCP, HAL_EV_AVRCP_GET_PLAY_STATUS, 0, NULL); push_request(dev, AVRCP_GET_PLAY_STATUS, 0, transaction); return 0; } static int handle_get_element_attrs_cmd(struct avrcp *session, uint8_t transaction, uint64_t uid, uint8_t number, uint32_t *attrs, void *user_data) { struct avrcp_device *dev = user_data; uint8_t buf[IPC_MTU]; struct hal_ev_avrcp_get_element_attrs *ev = (void *) buf; int i; DBG(""); ev->number = number; /* Set everything in case of empty list */ if (ev->number == 0) { for (i = 0; i < HAL_AVRCP_MEDIA_ATTR_DURATION; i++) { /* Skip 0x00 as the attributes start with 0x01 */ ev->attrs[i] = i + 1; } ev->number = i; goto done; } for (i = 0; i < number; i++) ev->attrs[i] = attrs[i]; done: ipc_send_notif(hal_ipc, HAL_SERVICE_ID_AVRCP, HAL_EV_AVRCP_GET_ELEMENT_ATTRS, sizeof(*ev) + ev->number, ev); push_request(dev, AVRCP_GET_ELEMENT_ATTRIBUTES, 0, transaction); return 0; } static int handle_register_notification_cmd(struct avrcp *session, uint8_t transaction, uint8_t event, uint32_t interval, void *user_data) { struct avrcp_device *dev = user_data; struct hal_ev_avrcp_register_notification ev; DBG(""); /* TODO: Add any missing events supported by Android */ switch (event) { case AVRCP_EVENT_STATUS_CHANGED: case AVRCP_EVENT_TRACK_CHANGED: case AVRCP_EVENT_PLAYBACK_POS_CHANGED: break; default: return -EINVAL; } ev.event = event; ev.param = interval; ipc_send_notif(hal_ipc, HAL_SERVICE_ID_AVRCP, HAL_EV_AVRCP_REGISTER_NOTIFICATION, sizeof(ev), &ev); push_request(dev, AVRCP_REGISTER_NOTIFICATION, event, transaction); return 0; } static const struct avrcp_control_ind control_ind = { .get_capabilities = handle_get_capabilities_cmd, .get_play_status = handle_get_play_status_cmd, .get_element_attributes = handle_get_element_attrs_cmd, .register_notification = handle_register_notification_cmd, }; static bool handle_register_notification_rsp(struct avrcp *session, int err, uint8_t code, uint8_t event, void *params, void *user_data) { struct avrcp_device *dev = user_data; struct hal_ev_avrcp_volume_changed ev; uint8_t *volume = params; if (err < 0) { error("AVRCP: %s", strerror(-err)); return false; } if (code != AVC_CTYPE_INTERIM && code != AVC_CTYPE_CHANGED) return false; if (event != AVRCP_EVENT_VOLUME_CHANGED) return false; ev.type = code; ev.volume = volume[0]; ipc_send_notif(hal_ipc, HAL_SERVICE_ID_AVRCP, HAL_EV_AVRCP_VOLUME_CHANGED, sizeof(ev), &ev); if (code == AVC_CTYPE_INTERIM) return true; avrcp_register_notification(dev->session, event, 0); return false; } static void handle_get_capabilities_rsp(struct avrcp *session, int err, uint8_t number, uint8_t *events, void *user_data) { struct avrcp_device *dev = user_data; int i; if (err < 0) { error("AVRCP: %s", strerror(-err)); return; } for (i = 0; i < number; i++) { if (events[i] != AVRCP_EVENT_VOLUME_CHANGED) continue; avrcp_register_notification(dev->session, events[i], 0); break; } return; } static void handle_set_volume_rsp(struct avrcp *session, int err, uint8_t value, void *user_data) { struct hal_ev_avrcp_volume_changed ev; if (err < 0) { ev.volume = 0; ev.type = AVC_CTYPE_REJECTED; goto done; } ev.volume = value; ev.type = AVC_CTYPE_ACCEPTED; done: ipc_send_notif(hal_ipc, HAL_SERVICE_ID_AVRCP, HAL_EV_AVRCP_VOLUME_CHANGED, sizeof(ev), &ev); } static const struct avrcp_control_cfm control_cfm = { .get_capabilities = handle_get_capabilities_rsp, .register_notification = handle_register_notification_rsp, .set_volume = handle_set_volume_rsp, }; static int avrcp_device_add_session(struct avrcp_device *dev, int fd, uint16_t imtu, uint16_t omtu) { struct hal_ev_avrcp_remote_features ev; char address[18]; dev->session = avrcp_new(fd, imtu, omtu, dev->version); if (!dev->session) return -EINVAL; avrcp_set_destroy_cb(dev->session, disconnect_cb, dev); avrcp_set_passthrough_handlers(dev->session, passthrough_handlers, dev); avrcp_register_player(dev->session, &control_ind, &control_cfm, dev); dev->queue = g_queue_new(); ba2str(&dev->dst, address); /* FIXME: get the real name of the device */ avrcp_init_uinput(dev->session, "bluetooth", address); bdaddr2android(&dev->dst, ev.bdaddr); ev.features = HAL_AVRCP_FEATURE_NONE; DBG("version 0x%02x", dev->version); if (dev->version < 0x0103) goto done; ev.features |= HAL_AVRCP_FEATURE_METADATA; if (dev->version < 0x0104) goto done; ev.features |= HAL_AVRCP_FEATURE_ABSOLUTE_VOLUME; avrcp_get_capabilities(dev->session, CAP_EVENTS_SUPPORTED); done: ipc_send_notif(hal_ipc, HAL_SERVICE_ID_AVRCP, HAL_EV_AVRCP_REMOTE_FEATURES, sizeof(ev), &ev); return 0; } static void connect_cb(GIOChannel *chan, GError *err, gpointer user_data) { struct avrcp_device *dev = user_data; uint16_t imtu, omtu; char address[18]; GError *gerr = NULL; int fd; if (err) { error("%s", err->message); return; } bt_io_get(chan, &gerr, BT_IO_OPT_DEST, address, BT_IO_OPT_IMTU, &imtu, BT_IO_OPT_OMTU, &omtu, BT_IO_OPT_INVALID); if (gerr) { error("%s", gerr->message); g_error_free(gerr); g_io_channel_shutdown(chan, TRUE, NULL); return; } fd = g_io_channel_unix_get_fd(chan); if (avrcp_device_add_session(dev, fd, imtu, omtu) < 0) { avrcp_device_free(dev); return; } g_io_channel_set_close_on_unref(chan, FALSE); if (dev->io) { g_io_channel_unref(dev->io); dev->io = NULL; } DBG("%s connected", address); } static bool avrcp_device_connect(struct avrcp_device *dev, BtIOConnect cb) { GError *err = NULL; dev->io = bt_io_connect(cb, dev, NULL, &err, BT_IO_OPT_SOURCE_BDADDR, &adapter_addr, BT_IO_OPT_DEST_BDADDR, &dev->dst, BT_IO_OPT_PSM, L2CAP_PSM_AVCTP, BT_IO_OPT_SEC_LEVEL, BT_IO_SEC_MEDIUM, BT_IO_OPT_INVALID); if (err) { error("%s", err->message); g_error_free(err); return false; } return true; } static void search_cb(sdp_list_t *recs, int err, gpointer data) { struct avrcp_device *dev = data; sdp_list_t *list; DBG(""); if (!g_slist_find(devices, dev)) return; if (err < 0) { error("Unable to get AV_REMOTE_SVCLASS_ID SDP record: %s", strerror(-err)); goto fail; } if (!recs || !recs->data) { error("No AVRCP records found"); goto fail; } for (list = recs; list; list = list->next) { sdp_record_t *rec = list->data; sdp_list_t *l; sdp_profile_desc_t *desc; int features; if (sdp_get_profile_descs(rec, &l) < 0) continue; desc = l->data; dev->version = desc->version; if (sdp_get_int_attr(rec, SDP_ATTR_SUPPORTED_FEATURES, &features) == 0) dev->features = features; sdp_list_free(l, free); break; } if (dev->io) { GError *gerr = NULL; if (!bt_io_accept(dev->io, connect_cb, dev, NULL, &gerr)) { error("bt_io_accept: %s", gerr->message); g_error_free(gerr); goto fail; } return; } if (!avrcp_device_connect(dev, connect_cb)) { error("Unable to connect to AVRCP"); goto fail; } return; fail: avrcp_device_remove(dev); } static int avrcp_device_search(struct avrcp_device *dev) { uuid_t uuid; sdp_uuid16_create(&uuid, AV_REMOTE_SVCLASS_ID); return bt_search_service(&adapter_addr, &dev->dst, &uuid, search_cb, dev, NULL, 0); } static void confirm_cb(GIOChannel *chan, gpointer data) { struct avrcp_device *dev; char address[18]; bdaddr_t dst; GError *err = NULL; bt_io_get(chan, &err, BT_IO_OPT_DEST_BDADDR, &dst, BT_IO_OPT_DEST, address, BT_IO_OPT_INVALID); if (err) { error("%s", err->message); g_error_free(err); g_io_channel_shutdown(chan, TRUE, NULL); return; } DBG("incoming connect from %s", address); dev = avrcp_device_find(&dst); if (dev && dev->session) { error("AVRCP: Refusing unexpected connect"); g_io_channel_shutdown(chan, TRUE, NULL); return; } dev = avrcp_device_new(&dst); if (avrcp_device_search(dev) < 0) { error("AVRCP: Failed to search SDP details"); avrcp_device_free(dev); g_io_channel_shutdown(chan, TRUE, NULL); } dev->io = g_io_channel_ref(chan); } bool bt_avrcp_register(struct ipc *ipc, const bdaddr_t *addr, uint8_t mode) { GError *err = NULL; sdp_record_t *rec; DBG(""); bacpy(&adapter_addr, addr); server = bt_io_listen(NULL, confirm_cb, NULL, NULL, &err, BT_IO_OPT_SOURCE_BDADDR, &adapter_addr, BT_IO_OPT_PSM, L2CAP_PSM_AVCTP, BT_IO_OPT_SEC_LEVEL, BT_IO_SEC_MEDIUM, BT_IO_OPT_INVALID); if (!server) { error("Failed to listen on AVDTP channel: %s", err->message); g_error_free(err); return false; } rec = avrcp_tg_record(); if (!rec) { error("Failed to allocate AVRCP TG record"); goto fail; } if (bt_adapter_add_record(rec, 0) < 0) { error("Failed to register AVRCP TG record"); sdp_record_free(rec); goto fail; } record_tg_id = rec->handle; rec = avrcp_ct_record(); if (!rec) { error("Failed to allocate AVRCP CT record"); bt_adapter_remove_record(record_tg_id); goto fail; } if (bt_adapter_add_record(rec, 0) < 0) { error("Failed to register AVRCP CT record"); bt_adapter_remove_record(record_tg_id); sdp_record_free(rec); goto fail; } record_ct_id = rec->handle; hal_ipc = ipc; ipc_register(hal_ipc, HAL_SERVICE_ID_AVRCP, cmd_handlers, G_N_ELEMENTS(cmd_handlers)); return true; fail: g_io_channel_shutdown(server, TRUE, NULL); g_io_channel_unref(server); server = NULL; return false; } void bt_avrcp_unregister(void) { DBG(""); g_slist_free_full(devices, avrcp_device_free); devices = NULL; ipc_unregister(hal_ipc, HAL_SERVICE_ID_AVRCP); hal_ipc = NULL; bt_adapter_remove_record(record_tg_id); record_tg_id = 0; bt_adapter_remove_record(record_ct_id); record_ct_id = 0; if (server) { g_io_channel_shutdown(server, TRUE, NULL); g_io_channel_unref(server); server = NULL; } } void bt_avrcp_connect(const bdaddr_t *dst) { struct avrcp_device *dev; char addr[18]; DBG(""); if (avrcp_device_find(dst)) return; dev = avrcp_device_new(dst); if (avrcp_device_search(dev) < 0) { error("AVRCP: Failed to search SDP details"); avrcp_device_free(dev); } ba2str(&dev->dst, addr); DBG("connecting to %s", addr); } void bt_avrcp_disconnect(const bdaddr_t *dst) { struct avrcp_device *dev; DBG(""); dev = avrcp_device_find(dst); if (!dev) return; if (dev->session) { avrcp_shutdown(dev->session); return; } avrcp_device_remove(dev); } bluez-5.82/android/PaxHeaders/sco-msg.h0000644000000000000000000000005014015011623015000 xustar0020 atime=1743516862 20 ctime=1743591276 bluez-5.82/android/sco-msg.h0000644000000000000000000000105514015011623014462 0ustar00rootroot/* SPDX-License-Identifier: LGPL-2.1-or-later */ /* * * BlueZ - Bluetooth protocol stack for Linux * * Copyright (C) 2014 Intel Corporation. All rights reserved. * * */ static const char BLUEZ_SCO_SK_PATH[] = "\0bluez_sco_socket"; #define SCO_SERVICE_ID 0 #define SCO_STATUS_SUCCESS IPC_STATUS_SUCCESS #define SCO_STATUS_FAILED 0x01 #define SCO_OP_STATUS IPC_OP_STATUS #define SCO_OP_GET_FD 0x01 struct sco_cmd_get_fd { uint8_t bdaddr[6]; } __attribute__((packed)); struct sco_rsp_get_fd { uint16_t mtu; } __attribute__((packed)); bluez-5.82/android/PaxHeaders/avrcp-lib.c0000644000000000000000000000005014214376353015321 xustar0020 atime=1743516866 20 ctime=1743591278 bluez-5.82/android/avrcp-lib.c0000644000000000000000000023341114214376353015006 0ustar00rootroot// SPDX-License-Identifier: LGPL-2.1-or-later /* * * BlueZ - Bluetooth protocol stack for Linux * * Copyright (C) 2014 Intel Corporation. All rights reserved. * * */ #ifdef HAVE_CONFIG_H #include #endif #define _GNU_SOURCE #include #include #include #include #include "lib/bluetooth.h" #include "src/shared/util.h" #include "src/log.h" #include "avctp.h" #include "avrcp-lib.h" /* Packet types */ #define AVRCP_PACKET_TYPE_SINGLE 0x00 #define AVRCP_PACKET_TYPE_START 0x01 #define AVRCP_PACKET_TYPE_CONTINUING 0x02 #define AVRCP_PACKET_TYPE_END 0x03 #define AVRCP_CHARSET_UTF8 0x006a #if __BYTE_ORDER == __LITTLE_ENDIAN struct avrcp_header { uint8_t company_id[3]; uint8_t pdu_id; uint8_t packet_type:2; uint8_t rsvd:6; uint16_t params_len; uint8_t params[0]; } __attribute__ ((packed)); #define AVRCP_HEADER_LENGTH 7 #elif __BYTE_ORDER == __BIG_ENDIAN struct avrcp_header { uint8_t company_id[3]; uint8_t pdu_id; uint8_t rsvd:6; uint8_t packet_type:2; uint16_t params_len; uint8_t params[0]; } __attribute__ ((packed)); #define AVRCP_HEADER_LENGTH 7 #else #error "Unknown byte order" #endif struct avrcp_browsing_header { uint8_t pdu_id; uint16_t params_len; uint8_t params[0]; } __attribute__ ((packed)); #define AVRCP_BROWSING_HEADER_LENGTH 3 struct get_capabilities_req { uint8_t cap; uint8_t params[0]; } __attribute__ ((packed)); struct get_capabilities_rsp { uint8_t cap; uint8_t number; uint8_t params[0]; } __attribute__ ((packed)); struct list_attributes_rsp { uint8_t number; uint8_t params[0]; } __attribute__ ((packed)); struct list_values_req { uint8_t attr; } __attribute__ ((packed)); struct list_values_rsp { uint8_t number; uint8_t params[0]; } __attribute__ ((packed)); struct get_value_req { uint8_t number; uint8_t attrs[0]; } __attribute__ ((packed)); struct attr_value { uint8_t attr; uint8_t value; } __attribute__ ((packed)); struct value_rsp { uint8_t number; struct attr_value values[0]; } __attribute__ ((packed)); struct set_value_req { uint8_t number; struct attr_value values[0]; } __attribute__ ((packed)); struct get_attribute_text_req { uint8_t number; uint8_t attrs[0]; } __attribute__ ((packed)); struct text_value { uint8_t attr; uint16_t charset; uint8_t len; char data[0]; } __attribute__ ((packed)); struct get_attribute_text_rsp { uint8_t number; struct text_value values[0]; } __attribute__ ((packed)); struct get_value_text_req { uint8_t attr; uint8_t number; uint8_t values[0]; } __attribute__ ((packed)); struct get_value_text_rsp { uint8_t number; struct text_value values[0]; } __attribute__ ((packed)); struct media_item { uint32_t attr; uint16_t charset; uint16_t len; char data[0]; } __attribute__ ((packed)); struct get_element_attributes_req { uint64_t id; uint8_t number; uint32_t attrs[0]; } __attribute__ ((packed)); struct get_element_attributes_rsp { uint8_t number; struct media_item items[0]; } __attribute__ ((packed)); struct get_play_status_rsp { uint32_t duration; uint32_t position; uint8_t status; } __attribute__ ((packed)); struct register_notification_req { uint8_t event; uint32_t interval; } __attribute__ ((packed)); struct register_notification_rsp { uint8_t event; uint8_t data[0]; } __attribute__ ((packed)); struct set_volume_req { uint8_t value; } __attribute__ ((packed)); struct set_volume_rsp { uint8_t value; } __attribute__ ((packed)); struct set_addressed_req { uint16_t id; } __attribute__ ((packed)); struct set_addressed_rsp { uint8_t status; } __attribute__ ((packed)); struct set_browsed_req { uint16_t id; } __attribute__ ((packed)); struct set_browsed_rsp { uint8_t status; uint16_t counter; uint32_t items; uint16_t charset; uint8_t depth; uint8_t data[0]; } __attribute__ ((packed)); struct get_folder_items_req { uint8_t scope; uint32_t start; uint32_t end; uint8_t number; uint32_t attrs[0]; } __attribute__ ((packed)); struct get_folder_items_rsp { uint8_t status; uint16_t counter; uint16_t number; uint8_t data[0]; } __attribute__ ((packed)); struct change_path_req { uint16_t counter; uint8_t direction; uint64_t uid; } __attribute__ ((packed)); struct change_path_rsp { uint8_t status; uint32_t items; } __attribute__ ((packed)); struct get_item_attributes_req { uint8_t scope; uint64_t uid; uint16_t counter; uint8_t number; uint32_t attrs[0]; } __attribute__ ((packed)); struct get_item_attributes_rsp { uint8_t status; uint8_t number; struct media_item items[0]; } __attribute__ ((packed)); struct play_item_req { uint8_t scope; uint64_t uid; uint16_t counter; } __attribute__ ((packed)); struct play_item_rsp { uint8_t status; } __attribute__ ((packed)); struct search_req { uint16_t charset; uint16_t len; char string[0]; } __attribute__ ((packed)); struct search_rsp { uint8_t status; uint16_t counter; uint32_t items; } __attribute__ ((packed)); struct add_to_now_playing_req { uint8_t scope; uint64_t uid; uint16_t counter; } __attribute__ ((packed)); struct add_to_now_playing_rsp { uint8_t status; } __attribute__ ((packed)); struct avrcp_control_handler { uint8_t id; uint8_t code; uint8_t rsp; ssize_t (*func) (struct avrcp *session, uint8_t transaction, uint16_t params_len, uint8_t *params, void *user_data); }; struct avrcp_browsing_handler { uint8_t id; ssize_t (*func) (struct avrcp *session, uint8_t transaction, uint16_t params_len, uint8_t *params, void *user_data); }; struct avrcp_continuing { uint8_t pdu_id; struct iovec pdu; }; struct avrcp { struct avctp *conn; struct avrcp_player *player; const struct avrcp_control_handler *control_handlers; void *control_data; unsigned int control_id; uint16_t control_mtu; struct avrcp_continuing *continuing; const struct avrcp_passthrough_handler *passthrough_handlers; void *passthrough_data; unsigned int passthrough_id; const struct avrcp_browsing_handler *browsing_handlers; void *browsing_data; unsigned int browsing_id; avrcp_destroy_cb_t destroy; void *destroy_data; }; struct avrcp_player { const struct avrcp_control_ind *ind; const struct avrcp_control_cfm *cfm; void *user_data; }; static inline uint32_t ntoh24(const uint8_t src[3]) { return src[0] << 16 | src[1] << 8 | src[2]; } static inline void hton24(uint8_t dst[3], uint32_t src) { dst[0] = (src & 0xff0000) >> 16; dst[1] = (src & 0x00ff00) >> 8; dst[2] = (src & 0x0000ff); } static void continuing_free(struct avrcp_continuing *continuing) { g_free(continuing->pdu.iov_base); g_free(continuing); } void avrcp_shutdown(struct avrcp *session) { if (session->conn) { if (session->control_id > 0) avctp_unregister_pdu_handler(session->conn, session->control_id); if (session->passthrough_id > 0) avctp_unregister_passthrough_handler(session->conn, session->passthrough_id); if (session->browsing_id > 0) avctp_unregister_browsing_pdu_handler(session->conn, session->browsing_id); /* clear destroy callback that would call shutdown again */ avctp_set_destroy_cb(session->conn, NULL, NULL); avctp_shutdown(session->conn); } if (session->destroy) session->destroy(session->destroy_data); if (session->continuing) continuing_free(session->continuing); g_free(session->player); g_free(session); } static struct avrcp_header *parse_pdu(uint8_t *operands, size_t operand_count) { struct avrcp_header *pdu; if (!operands || operand_count < sizeof(*pdu)) { error("AVRCP: packet too small (%zu bytes)", operand_count); return NULL; } pdu = (void *) operands; pdu->params_len = ntohs(pdu->params_len); if (operand_count != pdu->params_len + sizeof(*pdu)) { error("AVRCP: invalid parameter length (%u bytes)", pdu->params_len); return NULL; } return pdu; } static struct avrcp_browsing_header *parse_browsing_pdu(uint8_t *operands, size_t operand_count) { struct avrcp_browsing_header *pdu; if (!operands || operand_count < sizeof(*pdu)) { error("AVRCP: packet too small (%zu bytes)", operand_count); return NULL; } pdu = (void *) operands; pdu->params_len = ntohs(pdu->params_len); if (operand_count != pdu->params_len + sizeof(*pdu)) { error("AVRCP: invalid parameter length (%u bytes)", pdu->params_len); return NULL; } return pdu; } static uint8_t errno2status(int err) { switch (err) { case -ENOSYS: return AVRCP_STATUS_INVALID_COMMAND; case -EINVAL: return AVRCP_STATUS_INVALID_PARAM; case 0: return AVRCP_STATUS_SUCCESS; case -ENOTDIR: return AVRCP_STATUS_NOT_DIRECTORY; case -EBADRQC: return AVRCP_STATUS_INVALID_SCOPE; case -ERANGE: return AVRCP_STATUS_OUT_OF_BOUNDS; case -ENOENT: return AVRCP_STATUS_DOES_NOT_EXIST; default: return AVRCP_STATUS_INTERNAL_ERROR; } } static ssize_t handle_vendordep_pdu(struct avctp *conn, uint8_t transaction, uint8_t *code, uint8_t *subunit, uint8_t *operands, size_t operand_count, void *user_data) { struct avrcp *session = user_data; const struct avrcp_control_handler *handler; struct avrcp_header *pdu; uint32_t company_id; ssize_t ret; pdu = parse_pdu(operands, operand_count); if (!pdu) { pdu = (void *) operands; pdu->params[0] = AVRCP_STATUS_INVALID_COMMAND; goto reject; } company_id = ntoh24(pdu->company_id); if (company_id != IEEEID_BTSIG) { *code = AVC_CTYPE_NOT_IMPLEMENTED; return 0; } DBG("AVRCP PDU 0x%02X, len 0x%04X", pdu->pdu_id, pdu->params_len); pdu->packet_type = 0; pdu->rsvd = 0; if (!session->control_handlers) goto reject; for (handler = session->control_handlers; handler->id; handler++) { if (handler->id == pdu->pdu_id) break; } if (handler->id != pdu->pdu_id || handler->code != *code) { pdu->params[0] = AVRCP_STATUS_INVALID_COMMAND; goto reject; } if (!handler->func) { pdu->params[0] = AVRCP_STATUS_INVALID_PARAM; goto reject; } ret = handler->func(session, transaction, pdu->params_len, pdu->params, session->control_data); if (ret == 0) return -EAGAIN; if (ret < 0) { if (ret == -EAGAIN) return ret; pdu->params[0] = errno2status(ret); goto reject; } *code = handler->rsp; pdu->params_len = htons(ret); return AVRCP_HEADER_LENGTH + ret; reject: pdu->params_len = htons(1); *code = AVC_CTYPE_REJECTED; return AVRCP_HEADER_LENGTH + 1; } static bool handle_passthrough_pdu(struct avctp *conn, uint8_t op, bool pressed, void *user_data) { struct avrcp *session = user_data; const struct avrcp_passthrough_handler *handler; if (!session->passthrough_handlers) return false; for (handler = session->passthrough_handlers; handler->func; handler++) { if (handler->op == op) break; } if (handler->func == NULL) return false; return handler->func(session, pressed, session->passthrough_data); } static void disconnect_cb(void *data) { struct avrcp *session = data; session->conn = NULL; avrcp_shutdown(session); } struct avrcp *avrcp_new(int fd, size_t imtu, size_t omtu, uint16_t version) { struct avrcp *session; session = g_new0(struct avrcp, 1); session->conn = avctp_new(fd, imtu, omtu, version); if (!session->conn) { g_free(session); return NULL; } session->passthrough_id = avctp_register_passthrough_handler( session->conn, handle_passthrough_pdu, session); session->control_id = avctp_register_pdu_handler(session->conn, AVC_OP_VENDORDEP, handle_vendordep_pdu, session); session->control_mtu = omtu - AVC_DATA_OFFSET; /* * 27.1.2 AV/C Command Frame * An AV/C command frame contains up to 512 octets of data */ if (session->control_mtu > AVC_DATA_MTU) session->control_mtu = AVC_DATA_MTU; avctp_set_destroy_cb(session->conn, disconnect_cb, session); return session; } static ssize_t handle_browsing_pdu(struct avctp *conn, uint8_t transaction, uint8_t *operands, size_t operand_count, void *user_data) { struct avrcp *session = user_data; const struct avrcp_browsing_handler *handler; struct avrcp_browsing_header *pdu; int ret; pdu = parse_browsing_pdu(operands, operand_count); if (!pdu) { pdu = (void *) operands; pdu->params[0] = AVRCP_STATUS_INVALID_COMMAND; goto reject; } DBG("AVRCP Browsing PDU 0x%02X, len 0x%04X", pdu->pdu_id, pdu->params_len); if (!session->browsing_handlers) { pdu->pdu_id = AVRCP_GENERAL_REJECT; pdu->params[0] = AVRCP_STATUS_INTERNAL_ERROR; goto reject; } for (handler = session->browsing_handlers; handler->id; handler++) { if (handler->id == pdu->pdu_id) break; } if (handler->id != pdu->pdu_id) { pdu->pdu_id = AVRCP_GENERAL_REJECT; pdu->params[0] = AVRCP_STATUS_INVALID_COMMAND; goto reject; } if (!handler->func) { pdu->params[0] = AVRCP_STATUS_INVALID_PARAM; goto reject; } ret = handler->func(session, transaction, pdu->params_len, pdu->params, session->control_data); if (ret == 0) return -EAGAIN; if (ret < 0) { if (ret == -EAGAIN) return ret; pdu->params[0] = errno2status(ret); goto reject; } pdu->params_len = htons(ret); return AVRCP_BROWSING_HEADER_LENGTH + ret; reject: pdu->params_len = htons(1); return AVRCP_BROWSING_HEADER_LENGTH + 1; } static void browsing_disconnect_cb(void *data) { struct avrcp *session = data; session->browsing_id = 0; } int avrcp_connect_browsing(struct avrcp *session, int fd, size_t imtu, size_t omtu) { int err; err = avctp_connect_browsing(session->conn, fd, imtu, omtu); if (err < 0) return err; session->browsing_id = avctp_register_browsing_pdu_handler( session->conn, handle_browsing_pdu, session, browsing_disconnect_cb); return 0; } void avrcp_set_destroy_cb(struct avrcp *session, avrcp_destroy_cb_t cb, void *user_data) { session->destroy = cb; session->destroy_data = user_data; } static ssize_t get_capabilities(struct avrcp *session, uint8_t transaction, uint16_t params_len, uint8_t *params, void *user_data) { struct avrcp_player *player = user_data; struct get_capabilities_req *req; if (!params || params_len != sizeof(*req)) return -EINVAL; req = (void *) params; switch (req->cap) { case CAP_COMPANY_ID: req->params[0] = 1; hton24(&req->params[1], IEEEID_BTSIG); return 5; case CAP_EVENTS_SUPPORTED: if (!player->ind || !player->ind->get_capabilities) return -ENOSYS; return player->ind->get_capabilities(session, transaction, player->user_data); } return -EINVAL; } static ssize_t list_attributes(struct avrcp *session, uint8_t transaction, uint16_t params_len, uint8_t *params, void *user_data) { struct avrcp_player *player = user_data; DBG(""); if (!player->ind || !player->ind->list_attributes) return -ENOSYS; return player->ind->list_attributes(session, transaction, player->user_data); } static bool check_attributes(uint8_t number, const uint8_t *attrs) { int i; for (i = 0; i < number; i++) { if (attrs[i] > AVRCP_ATTRIBUTE_LAST || attrs[i] == AVRCP_ATTRIBUTE_ILEGAL) return false; } return true; } static ssize_t get_attribute_text(struct avrcp *session, uint8_t transaction, uint16_t params_len, uint8_t *params, void *user_data) { struct avrcp_player *player = user_data; struct get_attribute_text_req *req; DBG(""); if (!player->ind || !player->ind->get_attribute_text) return -ENOSYS; if (!params || params_len < sizeof(*req)) return -EINVAL; req = (void *) params; if (params_len != sizeof(*req) + req->number) return -EINVAL; if (!check_attributes(req->number, req->attrs)) return -EINVAL; return player->ind->get_attribute_text(session, transaction, req->number, req->attrs, player->user_data); } static ssize_t list_values(struct avrcp *session, uint8_t transaction, uint16_t params_len, uint8_t *params, void *user_data) { struct avrcp_player *player = user_data; struct list_values_req *req; DBG(""); if (!params || params_len != sizeof(*req)) return -EINVAL; req = (void *) params; if (req->attr > AVRCP_ATTRIBUTE_LAST || req->attr == AVRCP_ATTRIBUTE_ILEGAL) return -EINVAL; if (!player->ind || !player->ind->list_values) return -ENOSYS; return player->ind->list_values(session, transaction, req->attr, player->user_data); } static bool check_value(uint8_t attr, uint8_t number, const uint8_t *values) { int i; for (i = 0; i < number; i++) { /* Check for invalid value */ switch (attr) { case AVRCP_ATTRIBUTE_EQUALIZER: if (values[i] < AVRCP_EQUALIZER_OFF || values[i] > AVRCP_EQUALIZER_ON) return false; break; case AVRCP_ATTRIBUTE_REPEAT_MODE: if (values[i] < AVRCP_REPEAT_MODE_OFF || values[i] > AVRCP_REPEAT_MODE_GROUP) return false; break; case AVRCP_ATTRIBUTE_SHUFFLE: if (values[i] < AVRCP_SHUFFLE_OFF || values[i] > AVRCP_SHUFFLE_GROUP) return false; break; case AVRCP_ATTRIBUTE_SCAN: if (values[i] < AVRCP_SCAN_OFF || values[i] > AVRCP_SCAN_GROUP) return false; break; } } return true; } static ssize_t get_value_text(struct avrcp *session, uint8_t transaction, uint16_t params_len, uint8_t *params, void *user_data) { struct avrcp_player *player = user_data; struct get_value_text_req *req; DBG(""); if (!player->ind || !player->ind->get_value_text) return -ENOSYS; if (!params || params_len < sizeof(*req)) return -EINVAL; req = (void *) params; if (params_len != sizeof(*req) + req->number) return -EINVAL; if (req->number > AVRCP_ATTRIBUTE_LAST || req->number == AVRCP_ATTRIBUTE_ILEGAL) return -EINVAL; if (!check_value(req->attr, req->number, req->values)) return -EINVAL; return player->ind->get_value_text(session, transaction, params[0], params[1], ¶ms[2], player->user_data); } static ssize_t get_value(struct avrcp *session, uint8_t transaction, uint16_t params_len, uint8_t *params, void *user_data) { struct avrcp_player *player = user_data; struct get_value_req *req; DBG(""); if (!player->ind || !player->ind->get_value) return -ENOSYS; if (!params || params_len < sizeof(*req)) return -EINVAL; req = (void *) params; if (params_len < sizeof(*req) + req->number) return -EINVAL; if (!check_attributes(req->number, req->attrs)) return -EINVAL; return player->ind->get_value(session, transaction, params[0], ¶ms[1], player->user_data); } static ssize_t set_value(struct avrcp *session, uint8_t transaction, uint16_t params_len, uint8_t *params, void *user_data) { struct avrcp_player *player = user_data; struct set_value_req *req; uint8_t attrs[AVRCP_ATTRIBUTE_LAST]; uint8_t values[AVRCP_ATTRIBUTE_LAST]; int i; DBG(""); if (!player->ind || !player->ind->set_value) return -ENOSYS; if (!params || params_len < sizeof(*req)) return -EINVAL; req = (void *) params; if (params_len < sizeof(*req) + req->number * sizeof(*req->values)) return -EINVAL; for (i = 0; i < req->number; i++) { attrs[i] = req->values[i].attr; values[i] = req->values[i].value; if (!check_value(attrs[i], 1, &values[i])) return -EINVAL; } return player->ind->set_value(session, transaction, req->number, attrs, values, player->user_data); } static ssize_t get_play_status(struct avrcp *session, uint8_t transaction, uint16_t params_len, uint8_t *params, void *user_data) { struct avrcp_player *player = user_data; DBG(""); if (!player->ind || !player->ind->get_play_status) return -ENOSYS; return player->ind->get_play_status(session, transaction, player->user_data); } static bool parse_attributes(struct get_element_attributes_req *req, uint16_t params_len, uint8_t number, uint32_t *attrs) { int i; for (i = 0; i < number && params_len >= sizeof(*attrs); i++, params_len -= sizeof(*attrs)) { attrs[i] = be32_to_cpu(req->attrs[i]); if (attrs[i] == AVRCP_MEDIA_ATTRIBUTE_ILLEGAL || attrs[i] > AVRCP_MEDIA_ATTRIBUTE_LAST) return false; } return true; } static ssize_t get_element_attributes(struct avrcp *session, uint8_t transaction, uint16_t params_len, uint8_t *params, void *user_data) { struct avrcp_player *player = user_data; struct get_element_attributes_req *req; uint64_t uid; uint32_t attrs[AVRCP_MEDIA_ATTRIBUTE_LAST]; DBG(""); if (!player->ind || !player->ind->get_element_attributes) return -ENOSYS; req = (void *) params; if (!params || params_len < sizeof(*req)) return -EINVAL; if (!parse_attributes(req, params_len - sizeof(*req), req->number, attrs)) return -EINVAL; uid = get_be64(params); return player->ind->get_element_attributes(session, transaction, uid, req->number, attrs, player->user_data); } static ssize_t register_notification(struct avrcp *session, uint8_t transaction, uint16_t params_len, uint8_t *params, void *user_data) { struct avrcp_player *player = user_data; struct register_notification_req *req; uint32_t interval; DBG(""); if (!player->ind || !player->ind->register_notification) return -ENOSYS; if (!params || params_len != sizeof(*req)) return -EINVAL; req = (void *) params; interval = be32_to_cpu(req->interval); return player->ind->register_notification(session, transaction, req->event, interval, player->user_data); } static ssize_t set_volume(struct avrcp *session, uint8_t transaction, uint16_t params_len, uint8_t *params, void *user_data) { struct avrcp_player *player = user_data; struct set_volume_req *req; uint8_t volume; DBG(""); if (!player->ind || !player->ind->set_volume) return -ENOSYS; if (!params || params_len != sizeof(volume)) return -EINVAL; req = (void *) params; volume = req->value & 0x7f; return player->ind->set_volume(session, transaction, volume, player->user_data); } static ssize_t set_addressed(struct avrcp *session, uint8_t transaction, uint16_t params_len, uint8_t *params, void *user_data) { struct avrcp_player *player = user_data; struct set_addressed_req *req; uint16_t id; DBG(""); if (!player->ind || !player->ind->set_addressed) return -ENOSYS; if (!params || params_len != sizeof(*req)) return -EINVAL; req = (void *) params; id = be16_to_cpu(req->id); return player->ind->set_addressed(session, transaction, id, player->user_data); } static void continuing_new(struct avrcp *session, uint8_t pdu_id, const struct iovec *iov, int iov_cnt, size_t offset) { struct avrcp_continuing *continuing; int i; size_t len = 0; continuing = g_new0(struct avrcp_continuing, 1); continuing->pdu_id = pdu_id; for (i = 0; i < iov_cnt; i++) { if (i == 0 && offset) { len += iov[i].iov_len - offset; continue; } len += iov[i].iov_len; } continuing->pdu.iov_base = g_malloc0(len); DBG("len %zu", len); for (i = 0; i < iov_cnt; i++) { if (i == 0 && offset) { memcpy(continuing->pdu.iov_base, iov[i].iov_base + offset, iov[i].iov_len - offset); continuing->pdu.iov_len += iov[i].iov_len - offset; continue; } memcpy(continuing->pdu.iov_base + continuing->pdu.iov_len, iov[i].iov_base, iov[i].iov_len); continuing->pdu.iov_len += iov[i].iov_len; } session->continuing = continuing; } static int avrcp_send_internal(struct avrcp *session, uint8_t transaction, uint8_t code, uint8_t subunit, uint8_t pdu_id, uint8_t type, const struct iovec *iov, int iov_cnt) { struct iovec pdu[iov_cnt + 1]; struct avrcp_header hdr; int i; /* * If a receiver receives a start fragment or non-fragmented AVRCP * Specific AV/C message when it already has an incomplete fragment * from that sender then the receiver shall consider the first PDU * aborted. */ if (session->continuing) { continuing_free(session->continuing); session->continuing = NULL; } memset(&hdr, 0, sizeof(hdr)); pdu[0].iov_base = &hdr; pdu[0].iov_len = sizeof(hdr); hdr.packet_type = type; for (i = 0; i < iov_cnt; i++) { pdu[i + 1].iov_base = iov[i].iov_base; if (pdu[0].iov_len + hdr.params_len + iov[i].iov_len <= session->control_mtu) { pdu[i + 1].iov_len = iov[i].iov_len; hdr.params_len += iov[i].iov_len; if (hdr.packet_type != AVRCP_PACKET_TYPE_SINGLE) hdr.packet_type = AVRCP_PACKET_TYPE_END; continue; } /* * Only send what can fit and store the remaining in the * continuing iovec */ pdu[i + 1].iov_len = session->control_mtu - (pdu[0].iov_len + hdr.params_len); hdr.params_len += pdu[i + 1].iov_len; continuing_new(session, pdu_id, &iov[i], iov_cnt - i, pdu[i + 1].iov_len); hdr.packet_type = hdr.packet_type != AVRCP_PACKET_TYPE_SINGLE ? AVRCP_PACKET_TYPE_CONTINUING : AVRCP_PACKET_TYPE_START; break; } hton24(hdr.company_id, IEEEID_BTSIG); hdr.pdu_id = pdu_id; hdr.params_len = htons(hdr.params_len); return avctp_send_vendor(session->conn, transaction, code, subunit, pdu, iov_cnt + 1); } static ssize_t request_continuing(struct avrcp *session, uint8_t transaction, uint16_t params_len, uint8_t *params, void *user_data) { struct iovec iov; int err; DBG(""); if (!params || params_len != 1 || !session->continuing || session->continuing->pdu_id != params[0]) return -EINVAL; iov.iov_base = session->continuing->pdu.iov_base; iov.iov_len = session->continuing->pdu.iov_len; DBG("len %zu", iov.iov_len); session->continuing->pdu.iov_base = NULL; err = avrcp_send_internal(session, transaction, AVC_CTYPE_STABLE, AVC_SUBUNIT_PANEL, params[0], AVRCP_PACKET_TYPE_CONTINUING, &iov, 1); g_free(iov.iov_base); if (err < 0) return -EINVAL; return 0; } static ssize_t abort_continuing(struct avrcp *session, uint8_t transaction, uint16_t params_len, uint8_t *params, void *user_data) { DBG(""); if (!params || params_len != 1 || !session->continuing) return -EINVAL; continuing_free(session->continuing); session->continuing = NULL; avrcp_send_internal(session, transaction, AVC_CTYPE_ACCEPTED, AVC_SUBUNIT_PANEL, AVRCP_ABORT_CONTINUING, AVRCP_PACKET_TYPE_SINGLE, NULL, 0); return 0; } static const struct avrcp_control_handler player_handlers[] = { { AVRCP_GET_CAPABILITIES, AVC_CTYPE_STATUS, AVC_CTYPE_STABLE, get_capabilities }, { AVRCP_LIST_PLAYER_ATTRIBUTES, AVC_CTYPE_STATUS, AVC_CTYPE_STABLE, list_attributes }, { AVRCP_GET_PLAYER_ATTRIBUTE_TEXT, AVC_CTYPE_STATUS, AVC_CTYPE_STABLE, get_attribute_text }, { AVRCP_LIST_PLAYER_VALUES, AVC_CTYPE_STATUS, AVC_CTYPE_STABLE, list_values }, { AVRCP_GET_PLAYER_VALUE_TEXT, AVC_CTYPE_STATUS, AVC_CTYPE_STABLE, get_value_text }, { AVRCP_GET_CURRENT_PLAYER_VALUE, AVC_CTYPE_STATUS, AVC_CTYPE_STABLE, get_value }, { AVRCP_SET_PLAYER_VALUE, AVC_CTYPE_CONTROL, AVC_CTYPE_STABLE, set_value }, { AVRCP_GET_PLAY_STATUS, AVC_CTYPE_STATUS, AVC_CTYPE_STABLE, get_play_status }, { AVRCP_GET_ELEMENT_ATTRIBUTES, AVC_CTYPE_STATUS, AVC_CTYPE_STABLE, get_element_attributes }, { AVRCP_REGISTER_NOTIFICATION, AVC_CTYPE_NOTIFY, AVC_CTYPE_INTERIM, register_notification }, { AVRCP_SET_ABSOLUTE_VOLUME, AVC_CTYPE_CONTROL, AVC_CTYPE_STABLE, set_volume }, { AVRCP_SET_ADDRESSED_PLAYER, AVC_CTYPE_CONTROL, AVC_CTYPE_STABLE, set_addressed }, { AVRCP_REQUEST_CONTINUING, AVC_CTYPE_CONTROL, AVC_CTYPE_STABLE, request_continuing }, { AVRCP_ABORT_CONTINUING, AVC_CTYPE_CONTROL, AVC_CTYPE_ACCEPTED, abort_continuing }, { }, }; static void avrcp_set_control_handlers(struct avrcp *session, const struct avrcp_control_handler *handlers, void *user_data) { session->control_handlers = handlers; session->control_data = user_data; } static ssize_t set_browsed(struct avrcp *session, uint8_t transaction, uint16_t params_len, uint8_t *params, void *user_data) { struct avrcp_player *player = user_data; struct set_browsed_req *req; uint16_t id; DBG(""); if (!player->ind || !player->ind->set_browsed) return -ENOSYS; if (!params || params_len != sizeof(*req)) return -EINVAL; req = (void *) params; id = be16_to_cpu(req->id); return player->ind->set_browsed(session, transaction, id, player->user_data); } static ssize_t get_folder_items(struct avrcp *session, uint8_t transaction, uint16_t params_len, uint8_t *params, void *user_data) { struct avrcp_player *player = user_data; struct get_folder_items_req *req; uint32_t start, end; uint16_t number; uint32_t attrs[AVRCP_MEDIA_ATTRIBUTE_LAST]; int i; DBG(""); if (!player->ind || !player->ind->get_folder_items) return -ENOSYS; if (!params || params_len < sizeof(*req)) return -EINVAL; req = (void *) params; if (req->scope > AVRCP_MEDIA_NOW_PLAYING) return -EBADRQC; start = be32_to_cpu(req->start); end = be32_to_cpu(req->end); if (start > end) return -ERANGE; number = be16_to_cpu(req->number); for (i = 0; i < number; i++) { attrs[i] = be32_to_cpu(req->attrs[i]); if (attrs[i] == AVRCP_MEDIA_ATTRIBUTE_ILLEGAL || attrs[i] > AVRCP_MEDIA_ATTRIBUTE_LAST) return -EINVAL; } return player->ind->get_folder_items(session, transaction, req->scope, start, end, number, attrs, player->user_data); } static ssize_t change_path(struct avrcp *session, uint8_t transaction, uint16_t params_len, uint8_t *params, void *user_data) { struct avrcp_player *player = user_data; struct change_path_req *req; uint16_t counter; uint64_t uid; DBG(""); if (!player->ind || !player->ind->change_path) return -ENOSYS; if (!params || params_len < sizeof(*req)) return -EINVAL; req = (void *) params; counter = be16_to_cpu(req->counter); uid = be64_to_cpu(req->uid); return player->ind->change_path(session, transaction, counter, req->direction, uid, player->user_data); } static ssize_t get_item_attributes(struct avrcp *session, uint8_t transaction, uint16_t params_len, uint8_t *params, void *user_data) { struct avrcp_player *player = user_data; struct get_item_attributes_req *req; uint64_t uid; uint16_t counter; uint32_t attrs[AVRCP_MEDIA_ATTRIBUTE_LAST]; int i; DBG(""); if (!player->ind || !player->ind->get_item_attributes) return -ENOSYS; if (!params || params_len < sizeof(*req)) return -EINVAL; req = (void *) params; if (req->scope > AVRCP_MEDIA_NOW_PLAYING) return -EBADRQC; uid = be64_to_cpu(req->uid); counter = be16_to_cpu(req->counter); for (i = 0; i < req->number; i++) { attrs[i] = be32_to_cpu(req->attrs[i]); if (attrs[i] == AVRCP_MEDIA_ATTRIBUTE_ILLEGAL || attrs[i] > AVRCP_MEDIA_ATTRIBUTE_LAST) return -EINVAL; } return player->ind->get_item_attributes(session, transaction, req->scope, uid, counter, req->number, attrs, player->user_data); } static ssize_t play_item(struct avrcp *session, uint8_t transaction, uint16_t params_len, uint8_t *params, void *user_data) { struct avrcp_player *player = user_data; struct play_item_req *req; uint64_t uid; uint16_t counter; DBG(""); if (!player->ind || !player->ind->play_item) return -ENOSYS; if (!params || params_len < sizeof(*req)) return -EINVAL; req = (void *) params; if (req->scope > AVRCP_MEDIA_NOW_PLAYING) return -EBADRQC; uid = be64_to_cpu(req->uid); counter = be16_to_cpu(req->counter); return player->ind->play_item(session, transaction, req->scope, uid, counter, player->user_data); } static ssize_t search(struct avrcp *session, uint8_t transaction, uint16_t params_len, uint8_t *params, void *user_data) { struct avrcp_player *player = user_data; struct search_req *req; char *string; uint16_t len; int ret; DBG(""); if (!player->ind || !player->ind->search) return -ENOSYS; if (!params || params_len < sizeof(*req)) return -EINVAL; req = (void *) params; len = be16_to_cpu(req->len); if (!len) return -EINVAL; string = strndup(req->string, len); ret = player->ind->search(session, transaction, string, player->user_data); free(string); return ret; } static ssize_t add_to_now_playing(struct avrcp *session, uint8_t transaction, uint16_t params_len, uint8_t *params, void *user_data) { struct avrcp_player *player = user_data; struct add_to_now_playing_req *req; uint64_t uid; uint16_t counter; DBG(""); if (!player->ind || !player->ind->add_to_now_playing) return -ENOSYS; if (!params || params_len < sizeof(*req)) return -EINVAL; req = (void *) params; if (req->scope > AVRCP_MEDIA_NOW_PLAYING) return -EBADRQC; uid = be64_to_cpu(req->uid); counter = be16_to_cpu(req->counter); return player->ind->add_to_now_playing(session, transaction, req->scope, uid, counter, player->user_data); } static const struct avrcp_browsing_handler browsing_handlers[] = { { AVRCP_SET_BROWSED_PLAYER, set_browsed }, { AVRCP_GET_FOLDER_ITEMS, get_folder_items }, { AVRCP_CHANGE_PATH, change_path }, { AVRCP_GET_ITEM_ATTRIBUTES, get_item_attributes }, { AVRCP_PLAY_ITEM, play_item }, { AVRCP_SEARCH, search }, { AVRCP_ADD_TO_NOW_PLAYING, add_to_now_playing }, { }, }; static void avrcp_set_browsing_handlers(struct avrcp *session, const struct avrcp_browsing_handler *handlers, void *user_data) { session->browsing_handlers = handlers; session->browsing_data = user_data; } void avrcp_register_player(struct avrcp *session, const struct avrcp_control_ind *ind, const struct avrcp_control_cfm *cfm, void *user_data) { struct avrcp_player *player; player = g_new0(struct avrcp_player, 1); player->ind = ind; player->cfm = cfm; player->user_data = user_data; avrcp_set_control_handlers(session, player_handlers, player); avrcp_set_browsing_handlers(session, browsing_handlers, player); session->player = player; } void avrcp_set_passthrough_handlers(struct avrcp *session, const struct avrcp_passthrough_handler *handlers, void *user_data) { session->passthrough_handlers = handlers; session->passthrough_data = user_data; } int avrcp_init_uinput(struct avrcp *session, const char *name, const char *address) { return avctp_init_uinput(session->conn, name, address); } int avrcp_send(struct avrcp *session, uint8_t transaction, uint8_t code, uint8_t subunit, uint8_t pdu_id, const struct iovec *iov, int iov_cnt) { return avrcp_send_internal(session, transaction, code, subunit, pdu_id, AVRCP_PACKET_TYPE_SINGLE, iov, iov_cnt); } static int status2errno(uint8_t status) { switch (status) { case AVRCP_STATUS_INVALID_COMMAND: return -ENOSYS; case AVRCP_STATUS_INVALID_PARAM: return -EINVAL; case AVRCP_STATUS_SUCCESS: return 0; case AVRCP_STATUS_NOT_DIRECTORY: return -ENOTDIR; case AVRCP_STATUS_INVALID_SCOPE: return -EBADRQC; case AVRCP_STATUS_OUT_OF_BOUNDS: return -ERANGE; case AVRCP_STATUS_INTERNAL_ERROR: case AVRCP_STATUS_INVALID_PLAYER_ID: case AVRCP_STATUS_PLAYER_NOT_BROWSABLE: case AVRCP_STATUS_NO_AVAILABLE_PLAYERS: case AVRCP_STATUS_ADDRESSED_PLAYER_CHANGED: return -EPERM; default: return -EPROTO; } } static int parse_status(struct avrcp_header *pdu) { if (pdu->params_len < 1) return -EPROTO; return status2errno(pdu->params[0]); } static int parse_browsing_status(struct avrcp_browsing_header *pdu) { if (pdu->params_len < 1) return -EPROTO; return status2errno(pdu->params[0]); } static int avrcp_send_req(struct avrcp *session, uint8_t code, uint8_t subunit, uint8_t pdu_id, const struct iovec *iov, int iov_cnt, avctp_rsp_cb func, void *user_data) { struct iovec pdu[iov_cnt + 1]; struct avrcp_header hdr; int i; memset(&hdr, 0, sizeof(hdr)); pdu[0].iov_base = &hdr; pdu[0].iov_len = sizeof(hdr); for (i = 0; i < iov_cnt; i++) { pdu[i + 1].iov_base = iov[i].iov_base; pdu[i + 1].iov_len = iov[i].iov_len; hdr.params_len += iov[i].iov_len; } hton24(hdr.company_id, IEEEID_BTSIG); hdr.pdu_id = pdu_id; hdr.packet_type = AVRCP_PACKET_TYPE_SINGLE; hdr.params_len = htons(hdr.params_len); return avctp_send_vendor_req(session->conn, code, subunit, pdu, iov_cnt + 1, func, user_data); } static int avrcp_send_browsing_req(struct avrcp *session, uint8_t pdu_id, const struct iovec *iov, int iov_cnt, avctp_browsing_rsp_cb func, void *user_data) { struct iovec pdu[iov_cnt + 1]; struct avrcp_browsing_header hdr; int i; memset(&hdr, 0, sizeof(hdr)); for (i = 0; i < iov_cnt; i++) { pdu[i + 1].iov_base = iov[i].iov_base; pdu[i + 1].iov_len = iov[i].iov_len; hdr.params_len += iov[i].iov_len; } hdr.pdu_id = pdu_id; hdr.params_len = htons(hdr.params_len); pdu[0].iov_base = &hdr; pdu[0].iov_len = sizeof(hdr); return avctp_send_browsing_req(session->conn, pdu, iov_cnt + 1, func, user_data); } static int avrcp_send_browsing(struct avrcp *session, uint8_t transaction, uint8_t pdu_id, const struct iovec *iov, int iov_cnt) { struct iovec pdu[iov_cnt + 1]; struct avrcp_browsing_header hdr; int i; memset(&hdr, 0, sizeof(hdr)); for (i = 0; i < iov_cnt; i++) { pdu[i + 1].iov_base = iov[i].iov_base; pdu[i + 1].iov_len = iov[i].iov_len; hdr.params_len += iov[i].iov_len; } hdr.pdu_id = pdu_id; hdr.params_len = htons(hdr.params_len); pdu[0].iov_base = &hdr; pdu[0].iov_len = sizeof(hdr); return avctp_send_browsing(session->conn, transaction, pdu, iov_cnt + 1); } static gboolean get_capabilities_rsp(struct avctp *conn, uint8_t code, uint8_t subunit, uint8_t *operands, size_t operand_count, void *user_data) { struct avrcp *session = user_data; struct avrcp_player *player = session->player; struct avrcp_header *pdu; struct get_capabilities_rsp *rsp; uint8_t number = 0; uint8_t *params = NULL; int err; DBG(""); if (!player || !player->cfm || !player->cfm->get_capabilities) return FALSE; pdu = parse_pdu(operands, operand_count); if (!pdu) { err = -EPROTO; goto done; } if (code == AVC_CTYPE_REJECTED) { err = parse_status(pdu); goto done; } if (pdu->params_len < sizeof(*rsp)) { err = -EPROTO; goto done; } rsp = (void *) pdu->params; switch (rsp->cap) { case CAP_COMPANY_ID: case CAP_EVENTS_SUPPORTED: break; default: err = -EPROTO; goto done; } if (rsp->number > 0) { number = rsp->number; params = rsp->params; } err = 0; done: player->cfm->get_capabilities(session, err, number, params, player->user_data); return FALSE; } int avrcp_get_capabilities(struct avrcp *session, uint8_t param) { struct iovec iov; struct get_capabilities_req req; req.cap = param; iov.iov_base = &req; iov.iov_len = sizeof(req); return avrcp_send_req(session, AVC_CTYPE_STATUS, AVC_SUBUNIT_PANEL, AVRCP_GET_CAPABILITIES, &iov, 1, get_capabilities_rsp, session); } static gboolean register_notification_rsp(struct avctp *conn, uint8_t code, uint8_t subunit, uint8_t *operands, size_t operand_count, void *user_data) { struct avrcp *session = user_data; struct avrcp_player *player = session->player; struct avrcp_header *pdu; struct register_notification_rsp *rsp; uint8_t event = 0; uint16_t value16, value16_2[2]; uint32_t value32; uint64_t value64; uint8_t *params = NULL; int err; DBG(""); if (!player || !player->cfm || !player->cfm->register_notification) return FALSE; pdu = parse_pdu(operands, operand_count); if (!pdu) { err = -EPROTO; goto done; } if (code == AVC_CTYPE_REJECTED) { err = parse_status(pdu); goto done; } if (pdu->params_len < sizeof(*rsp)) { err = -EPROTO; goto done; } rsp = (void *) pdu->params; event = rsp->event; if (event > AVRCP_EVENT_LAST) { err = -EPROTO; goto done; } switch (event) { case AVRCP_EVENT_STATUS_CHANGED: if (pdu->params_len != sizeof(*rsp) + sizeof(uint8_t)) { err = -EPROTO; goto done; } params = rsp->data; break; case AVRCP_EVENT_VOLUME_CHANGED: if (pdu->params_len != sizeof(*rsp) + sizeof(uint8_t)) { err = -EPROTO; goto done; } params = rsp->data; params[0] &= 0x7f; break; case AVRCP_EVENT_TRACK_CHANGED: if (pdu->params_len != sizeof(*rsp) + sizeof(value64)) { err = -EPROTO; goto done; } value64 = get_be64(rsp->data); params = (uint8_t *) &value64; break; case AVRCP_EVENT_PLAYBACK_POS_CHANGED: if (pdu->params_len != sizeof(*rsp) + sizeof(value32)) { err = -EPROTO; goto done; } value32 = get_be32(rsp->data); params = (uint8_t *) &value32; break; case AVRCP_EVENT_ADDRESSED_PLAYER_CHANGED: if (pdu->params_len < sizeof(*rsp) + sizeof(value16_2)) { err = -EPROTO; goto done; } value16_2[0] = get_be16(rsp->data); value16_2[1] = get_be16(rsp->data + 2); params = (uint8_t *) value16_2; break; case AVRCP_EVENT_SETTINGS_CHANGED: if (pdu->params_len < sizeof(*rsp) + sizeof(uint8_t)) { err = -EPROTO; goto done; } params = rsp->data; break; case AVRCP_EVENT_UIDS_CHANGED: if (pdu->params_len < sizeof(*rsp) + sizeof(value16)) { err = -EPROTO; goto done; } value16 = get_be16(rsp->data); params = (uint8_t *) &value16; break; } err = 0; done: return player->cfm->register_notification(session, err, code, event, params, player->user_data); } int avrcp_register_notification(struct avrcp *session, uint8_t event, uint32_t interval) { struct iovec iov; struct register_notification_req req; if (event > AVRCP_EVENT_LAST) return -EINVAL; req.event = event; req.interval = cpu_to_be32(interval); iov.iov_base = &req; iov.iov_len = sizeof(req); return avrcp_send_req(session, AVC_CTYPE_NOTIFY, AVC_SUBUNIT_PANEL, AVRCP_REGISTER_NOTIFICATION, &iov, 1, register_notification_rsp, session); } static gboolean list_attributes_rsp(struct avctp *conn, uint8_t code, uint8_t subunit, uint8_t *operands, size_t operand_count, void *user_data) { struct avrcp *session = user_data; struct avrcp_player *player = session->player; struct avrcp_header *pdu = (void *) operands; struct list_attributes_rsp *rsp; uint8_t number = 0; uint8_t *attrs = NULL; int err; DBG(""); if (!player || !player->cfm || !player->cfm->list_attributes) return FALSE; pdu = parse_pdu(operands, operand_count); if (!pdu) { err = -EPROTO; goto done; } if (code == AVC_CTYPE_REJECTED) { err = parse_status(pdu); goto done; } rsp = (void *) pdu->params; if (pdu->params_len < sizeof(*rsp)) { err = -EPROTO; goto done; } number = rsp->number; if (number > 0) attrs = rsp->params; err = 0; done: player->cfm->list_attributes(session, err, number, attrs, player->user_data); return FALSE; } int avrcp_list_player_attributes(struct avrcp *session) { return avrcp_send_req(session, AVC_CTYPE_STATUS, AVC_SUBUNIT_PANEL, AVRCP_LIST_PLAYER_ATTRIBUTES, NULL, 0, list_attributes_rsp, session); } static int parse_text_rsp(struct avrcp_header *pdu, uint8_t *number, uint8_t *attrs, char **text) { uint8_t *ptr; uint16_t params_len; int i; if (pdu->params_len < 1) return -EPROTO; *number = pdu->params[0]; if (*number > AVRCP_ATTRIBUTE_LAST) { *number = 0; return -EPROTO; } params_len = pdu->params_len - 1; for (i = 0, ptr = &pdu->params[1]; i < *number && params_len > 0; i++) { struct text_value *val; if (params_len < sizeof(*val)) goto fail; val = (void *) ptr; attrs[i] = val->attr; params_len -= sizeof(*val); ptr += sizeof(*val); if (val->len > params_len) goto fail; if (val->len > 0) { text[i] = g_strndup(val->data, val->len); params_len -= val->len; ptr += val->len; } } if (i != *number) goto fail; return 0; fail: for (i -= 1; i >= 0; i--) g_free(text[i]); *number = 0; return -EPROTO; } static gboolean get_attribute_text_rsp(struct avctp *conn, uint8_t code, uint8_t subunit, uint8_t *operands, size_t operand_count, void *user_data) { struct avrcp *session = user_data; struct avrcp_player *player = session->player; struct avrcp_header *pdu; uint8_t number = 0; uint8_t attrs[AVRCP_ATTRIBUTE_LAST]; char *text[AVRCP_ATTRIBUTE_LAST]; int err; DBG(""); if (!player || !player->cfm || !player->cfm->get_attribute_text) return FALSE; pdu = parse_pdu(operands, operand_count); if (!pdu) { err = -EPROTO; goto done; } if (code == AVC_CTYPE_REJECTED) { err = parse_status(pdu); goto done; } err = parse_text_rsp(pdu, &number, attrs, text); done: player->cfm->get_attribute_text(session, err, number, attrs, text, player->user_data); return FALSE; } int avrcp_get_player_attribute_text(struct avrcp *session, uint8_t number, uint8_t *attrs) { struct iovec iov[2]; if (!number || number > AVRCP_ATTRIBUTE_LAST) return -EINVAL; iov[0].iov_base = &number; iov[0].iov_len = sizeof(number); iov[1].iov_base = attrs; iov[1].iov_len = number; return avrcp_send_req(session, AVC_CTYPE_STATUS, AVC_SUBUNIT_PANEL, AVRCP_GET_PLAYER_ATTRIBUTE_TEXT, iov, 2, get_attribute_text_rsp, session); } static gboolean list_values_rsp(struct avctp *conn, uint8_t code, uint8_t subunit, uint8_t *operands, size_t operand_count, void *user_data) { struct avrcp *session = user_data; struct avrcp_player *player = session->player; struct avrcp_header *pdu; struct list_values_rsp *rsp; uint8_t number = 0; uint8_t *values = NULL; int err; DBG(""); if (!player || !player->cfm || !player->cfm->list_values) return FALSE; pdu = parse_pdu(operands, operand_count); if (!pdu) { err = -EPROTO; goto done; } if (code == AVC_CTYPE_REJECTED) { err = parse_status(pdu); goto done; } if (pdu->params_len < sizeof(*rsp)) { err = -EPROTO; goto done; } rsp = (void *) pdu->params; if (rsp->number > 0) { number = rsp->number; values = rsp->params; } err = 0; done: player->cfm->list_values(session, err, number, values, player->user_data); return FALSE; } int avrcp_list_player_values(struct avrcp *session, uint8_t attr) { struct iovec iov; iov.iov_base = &attr; iov.iov_len = sizeof(attr); return avrcp_send_req(session, AVC_CTYPE_STATUS, AVC_SUBUNIT_PANEL, AVRCP_LIST_PLAYER_VALUES, &iov, 1, list_values_rsp, session); } static gboolean get_value_text_rsp(struct avctp *conn, uint8_t code, uint8_t subunit, uint8_t *operands, size_t operand_count, void *user_data) { struct avrcp *session = user_data; struct avrcp_player *player = session->player; struct avrcp_header *pdu; uint8_t number = 0; uint8_t values[AVRCP_ATTRIBUTE_LAST]; char *text[AVRCP_ATTRIBUTE_LAST]; int err; DBG(""); if (!player || !player->cfm || !player->cfm->get_value_text) return FALSE; pdu = parse_pdu(operands, operand_count); if (!pdu) { err = -EPROTO; goto done; } if (code == AVC_CTYPE_REJECTED) { err = parse_status(pdu); goto done; } err = parse_text_rsp(pdu, &number, values, text); done: player->cfm->get_value_text(session, err, number, values, text, player->user_data); return FALSE; } int avrcp_get_player_value_text(struct avrcp *session, uint8_t attr, uint8_t number, uint8_t *values) { struct iovec iov[2]; struct get_value_text_req req; if (!number) return -EINVAL; req.attr = attr; req.number = number; iov[0].iov_base = &req; iov[0].iov_len = sizeof(req); iov[1].iov_base = values; iov[1].iov_len = number; return avrcp_send_req(session, AVC_CTYPE_STATUS, AVC_SUBUNIT_PANEL, AVRCP_GET_PLAYER_VALUE_TEXT, iov, 2, get_value_text_rsp, session); } static int parse_value(struct avrcp_header *pdu, uint8_t *number, uint8_t *attrs, uint8_t *values) { int i; struct value_rsp *rsp; if (pdu->params_len < sizeof(*rsp)) return -EPROTO; rsp = (void *) pdu->params; /* * Check if PDU is big enough to hold the number of (attribute, value) * tuples. */ if (rsp->number > AVRCP_ATTRIBUTE_LAST || sizeof(*rsp) + rsp->number * 2 != pdu->params_len) { *number = 0; return -EPROTO; } for (i = 0; i < rsp->number; i++) { attrs[i] = rsp->values[i].attr; values[i] = rsp->values[i].value; } *number = rsp->number; return 0; } static gboolean get_value_rsp(struct avctp *conn, uint8_t code, uint8_t subunit, uint8_t *operands, size_t operand_count, void *user_data) { struct avrcp *session = user_data; struct avrcp_player *player = session->player; struct avrcp_header *pdu; uint8_t number = 0; uint8_t attrs[AVRCP_ATTRIBUTE_LAST]; uint8_t values[AVRCP_ATTRIBUTE_LAST]; int err; DBG(""); if (!player || !player->cfm || !player->cfm->get_value) return FALSE; pdu = parse_pdu(operands, operand_count); if (!pdu) { err = -EPROTO; goto done; } if (code == AVC_CTYPE_REJECTED) { err = parse_status(pdu); goto done; } err = parse_value(pdu, &number, attrs, values); done: player->cfm->get_value(session, err, number, attrs, values, player->user_data); return FALSE; } int avrcp_get_current_player_value(struct avrcp *session, uint8_t number, uint8_t *attrs) { struct iovec iov[2]; if (number > AVRCP_ATTRIBUTE_LAST) return -EINVAL; iov[0].iov_base = &number; iov[0].iov_len = sizeof(number); iov[1].iov_base = attrs; iov[1].iov_len = number; return avrcp_send_req(session, AVC_CTYPE_STATUS, AVC_SUBUNIT_PANEL, AVRCP_GET_CURRENT_PLAYER_VALUE, iov, 2, get_value_rsp, session); } static gboolean set_value_rsp(struct avctp *conn, uint8_t code, uint8_t subunit, uint8_t *operands, size_t operand_count, void *user_data) { struct avrcp *session = user_data; struct avrcp_player *player = session->player; struct avrcp_header *pdu; int err; DBG(""); if (!player || !player->cfm || !player->cfm->set_value) return FALSE; pdu = parse_pdu(operands, operand_count); if (!pdu) { err = -EPROTO; goto done; } if (code == AVC_CTYPE_REJECTED) { err = parse_status(pdu); goto done; } err = 0; done: player->cfm->set_value(session, err, player->user_data); return FALSE; } int avrcp_set_player_value(struct avrcp *session, uint8_t number, uint8_t *attrs, uint8_t *values) { struct iovec iov[2]; struct attr_value val[AVRCP_ATTRIBUTE_LAST]; int i; if (number > AVRCP_ATTRIBUTE_LAST) return -EINVAL; iov[0].iov_base = &number; iov[0].iov_len = sizeof(number); for (i = 0; i < number; i++) { val[i].attr = attrs[i]; val[i].value = values[i]; } iov[1].iov_base = val; iov[1].iov_len = sizeof(*val) * number; return avrcp_send_req(session, AVC_CTYPE_CONTROL, AVC_SUBUNIT_PANEL, AVRCP_SET_PLAYER_VALUE, iov, 2, set_value_rsp, session); } static gboolean get_play_status_rsp(struct avctp *conn, uint8_t code, uint8_t subunit, uint8_t *operands, size_t operand_count, void *user_data) { struct avrcp *session = user_data; struct avrcp_player *player = session->player; struct avrcp_header *pdu; struct get_play_status_rsp *rsp; uint8_t status = 0; uint32_t position = 0; uint32_t duration = 0; int err; DBG(""); if (!player || !player->cfm || !player->cfm->get_play_status) return FALSE; pdu = parse_pdu(operands, operand_count); if (!pdu) { err = -EPROTO; goto done; } if (code == AVC_CTYPE_REJECTED) { err = parse_status(pdu); goto done; } if (pdu->params_len < sizeof(*rsp)) { err = -EPROTO; goto done; } rsp = (void *) pdu->params; duration = be32_to_cpu(rsp->duration); position = be32_to_cpu(rsp->position); status = rsp->status; err = 0; done: player->cfm->get_play_status(session, err, status, position, duration, player->user_data); return FALSE; } int avrcp_get_play_status(struct avrcp *session) { return avrcp_send_req(session, AVC_CTYPE_STATUS, AVC_SUBUNIT_PANEL, AVRCP_GET_PLAY_STATUS, NULL, 0, get_play_status_rsp, session); } static gboolean set_volume_rsp(struct avctp *conn, uint8_t code, uint8_t subunit, uint8_t *operands, size_t operand_count, void *user_data) { struct avrcp *session = user_data; struct avrcp_player *player = session->player; struct avrcp_header *pdu; struct set_volume_rsp *rsp; uint8_t value = 0; int err; DBG(""); if (!player || !player->cfm || !player->cfm->set_volume) return FALSE; pdu = parse_pdu(operands, operand_count); if (!pdu) { err = -EPROTO; goto done; } if (code == AVC_CTYPE_REJECTED) { err = parse_status(pdu); goto done; } if (pdu->params_len < sizeof(*rsp)) { err = -EPROTO; goto done; } rsp = (void *) pdu->params; value = rsp->value & 0x7f; err = 0; done: player->cfm->set_volume(session, err, value, player->user_data); return FALSE; } int avrcp_set_volume(struct avrcp *session, uint8_t volume) { struct iovec iov; iov.iov_base = &volume; iov.iov_len = sizeof(volume); return avrcp_send_req(session, AVC_CTYPE_CONTROL, AVC_SUBUNIT_PANEL, AVRCP_SET_ABSOLUTE_VOLUME, &iov, 1, set_volume_rsp, session); } static int parse_attribute_list(uint8_t *params, uint16_t params_len, uint8_t number, uint32_t *attrs, char **text) { struct media_item *item; int i; if (number > AVRCP_MEDIA_ATTRIBUTE_LAST) return -EPROTO; for (i = 0; i < number && params_len >= sizeof(*item); i++) { item = (void *) params; item->attr = be32_to_cpu(item->attr); item->charset = be16_to_cpu(item->charset); item->len = be16_to_cpu(item->len); params_len -= sizeof(*item); params += sizeof(*item); if (item->len > params_len) goto fail; if (item->len > 0) { text[i] = g_strndup(item->data, item->len); attrs[i] = item->attr; params_len -= item->len; params += item->len; } else { text[i] = NULL; attrs[i] = 0; } } return 0; fail: for (i -= 1; i >= 0; i--) g_free(text[i]); return -EPROTO; } static void free_attribute_list(uint8_t number, char **text) { while(number--) g_free(text[number]); } static int parse_elements(struct avrcp_header *pdu, uint8_t *number, uint32_t *attrs, char **text) { struct get_element_attributes_rsp *rsp; if (pdu->params_len < sizeof(*rsp)) return -EPROTO; rsp = (void *) pdu->params; if (rsp->number > AVRCP_MEDIA_ATTRIBUTE_LAST) return -EPROTO; *number = rsp->number; return parse_attribute_list(pdu->params + sizeof(*rsp), pdu->params_len - sizeof(*rsp), *number, attrs, text); } static int parse_items(struct avrcp_browsing_header *pdu, uint8_t *number, uint32_t *attrs, char **text) { struct get_item_attributes_rsp *rsp; if (pdu->params_len < sizeof(*rsp)) return -EPROTO; rsp = (void *) pdu->params; if (rsp->number > AVRCP_MEDIA_ATTRIBUTE_LAST) return -EPROTO; *number = rsp->number; return parse_attribute_list(pdu->params + sizeof(*rsp), pdu->params_len - sizeof(*rsp), *number, attrs, text); } static gboolean get_element_attributes_rsp(struct avctp *conn, uint8_t code, uint8_t subunit, uint8_t *operands, size_t operand_count, void *user_data) { struct avrcp *session = user_data; struct avrcp_player *player = session->player; struct avrcp_header *pdu; uint8_t number = 0; uint32_t attrs[AVRCP_MEDIA_ATTRIBUTE_LAST]; char *text[AVRCP_MEDIA_ATTRIBUTE_LAST]; int err; DBG(""); if (!player || !player->cfm || !player->cfm->get_element_attributes) return FALSE; pdu = parse_pdu(operands, operand_count); if (!pdu) { err = -EPROTO; goto done; } if (code == AVC_CTYPE_REJECTED) { err = parse_status(pdu); goto done; } err = parse_elements(pdu, &number, attrs, text); done: player->cfm->get_element_attributes(session, err, number, attrs, text, player->user_data); if (err == 0) free_attribute_list(number, text); return FALSE; } int avrcp_get_element_attributes(struct avrcp *session) { struct iovec iov; struct get_element_attributes_req req; /* This returns all attributes */ memset(&req, 0, sizeof(req)); iov.iov_base = &req; iov.iov_len = sizeof(req); return avrcp_send_req(session, AVC_CTYPE_STATUS, AVC_SUBUNIT_PANEL, AVRCP_GET_ELEMENT_ATTRIBUTES, &iov, 1, get_element_attributes_rsp, session); } static gboolean set_addressed_rsp(struct avctp *conn, uint8_t code, uint8_t subunit, uint8_t *operands, size_t operand_count, void *user_data) { struct avrcp *session = user_data; struct avrcp_player *player = session->player; struct avrcp_header *pdu; int err; DBG(""); if (!player || !player->cfm || !player->cfm->set_addressed) return FALSE; pdu = parse_pdu(operands, operand_count); if (!pdu) { err = -EPROTO; goto done; } err = parse_status(pdu); done: player->cfm->set_addressed(session, err, player->user_data); return FALSE; } int avrcp_set_addressed_player(struct avrcp *session, uint16_t player_id) { struct iovec iov; struct set_addressed_req req; req.id = cpu_to_be16(player_id); iov.iov_base = &req; iov.iov_len = sizeof(req); return avrcp_send_req(session, AVC_CTYPE_CONTROL, AVC_SUBUNIT_PANEL, AVRCP_SET_ADDRESSED_PLAYER, &iov, 1, set_addressed_rsp, session); } static char *parse_folder_list(uint8_t *params, uint16_t params_len, uint8_t depth) { char **folders, *path; uint8_t count; size_t i; folders = g_new0(char *, depth + 2); folders[0] = g_strdup("/Filesystem"); for (i = 0, count = 1; count <= depth && i < params_len; count++) { uint8_t len; len = params[i++]; if (i + len > params_len || len == 0) { g_strfreev(folders); return NULL; } folders[count] = util_memdup(¶ms[i], len); i += len; } path = g_build_pathv("/", folders); g_strfreev(folders); return path; } static gboolean set_browsed_rsp(struct avctp *conn, uint8_t *operands, size_t operand_count, void *user_data) { struct avrcp *session = user_data; struct avrcp_player *player = session->player; struct avrcp_browsing_header *pdu; struct set_browsed_rsp *rsp; uint16_t counter = 0; uint32_t items = 0; char *path = NULL; int err; DBG(""); if (!player || !player->cfm || !player->cfm->set_browsed) return FALSE; pdu = parse_browsing_pdu(operands, operand_count); if (!pdu) { err = -EPROTO; goto done; } err = parse_browsing_status(pdu); if (err < 0) goto done; if (pdu->params_len < sizeof(*rsp)) { err = -EPROTO; goto done; } rsp = (void *) pdu->params; counter = be16_to_cpu(rsp->counter); items = be32_to_cpu(rsp->items); path = parse_folder_list(rsp->data, pdu->params_len - sizeof(*rsp), rsp->depth); if (!path) err = -EPROTO; done: player->cfm->set_browsed(session, err, counter, items, path, player->user_data); return FALSE; } int avrcp_set_browsed_player(struct avrcp *session, uint16_t player_id) { struct iovec iov; struct set_browsed_req req; req.id = cpu_to_be16(player_id); iov.iov_base = &req; iov.iov_len = sizeof(req); return avrcp_send_browsing_req(session, AVRCP_SET_BROWSED_PLAYER, &iov, 1, set_browsed_rsp, session); } static gboolean get_folder_items_rsp(struct avctp *conn, uint8_t *operands, size_t operand_count, void *user_data) { struct avrcp *session = user_data; struct avrcp_player *player = session->player; struct avrcp_browsing_header *pdu; struct get_folder_items_rsp *rsp; uint16_t counter = 0, number = 0; uint8_t *params = NULL; int err; DBG(""); if (!player || !player->cfm || !player->cfm->get_folder_items) return FALSE; pdu = parse_browsing_pdu(operands, operand_count); if (!pdu) { err = -EPROTO; goto done; } err = parse_browsing_status(pdu); if (err < 0) goto done; if (pdu->params_len < sizeof(*rsp)) { err = -EPROTO; goto done; } rsp = (void *) pdu->params; counter = be16_to_cpu(rsp->counter); number = be16_to_cpu(rsp->number); params = rsp->data; /* FIXME: Add proper parsing for each item type */ done: player->cfm->get_folder_items(session, err, counter, number, params, player->user_data); return FALSE; } int avrcp_get_folder_items(struct avrcp *session, uint8_t scope, uint32_t start, uint32_t end, uint8_t number, uint32_t *attrs) { struct iovec iov[2]; struct get_folder_items_req req; int i; req.scope = scope; req.start = cpu_to_be32(start); req.end = cpu_to_be32(end); req.number = number; iov[0].iov_base = &req; iov[0].iov_len = sizeof(req); if (!number) return avrcp_send_browsing_req(session, AVRCP_GET_FOLDER_ITEMS, iov, 1, get_folder_items_rsp, session); for (i = 0; i < number; i++) attrs[i] = cpu_to_be32(attrs[i]); iov[1].iov_base = attrs; iov[1].iov_len = number * sizeof(*attrs); return avrcp_send_browsing_req(session, AVRCP_GET_FOLDER_ITEMS, iov, 2, get_folder_items_rsp, session); } static gboolean change_path_rsp(struct avctp *conn, uint8_t *operands, size_t operand_count, void *user_data) { struct avrcp *session = user_data; struct avrcp_player *player = session->player; struct avrcp_browsing_header *pdu; struct change_path_rsp *rsp; uint32_t items = 0; int err; DBG(""); if (!player || !player->cfm || !player->cfm->change_path) return FALSE; pdu = parse_browsing_pdu(operands, operand_count); if (!pdu) { err = -EPROTO; goto done; } err = parse_browsing_status(pdu); if (err < 0) goto done; if (pdu->params_len < sizeof(*rsp)) { err = -EPROTO; goto done; } rsp = (void *) pdu->params; items = be32_to_cpu(rsp->items); done: player->cfm->change_path(session, err, items, player->user_data); return FALSE; } int avrcp_change_path(struct avrcp *session, uint8_t direction, uint64_t uid, uint16_t counter) { struct iovec iov; struct change_path_req req; req.counter = cpu_to_be16(counter); req.direction = direction; req.uid = cpu_to_be64(uid); iov.iov_base = &req; iov.iov_len = sizeof(req); return avrcp_send_browsing_req(session, AVRCP_CHANGE_PATH, &iov, 1, change_path_rsp, session); } static gboolean get_item_attributes_rsp(struct avctp *conn, uint8_t *operands, size_t operand_count, void *user_data) { struct avrcp *session = user_data; struct avrcp_player *player = session->player; struct avrcp_browsing_header *pdu; uint8_t number = 0; uint32_t attrs[AVRCP_MEDIA_ATTRIBUTE_LAST]; char *text[AVRCP_MEDIA_ATTRIBUTE_LAST]; int err; DBG(""); if (!player || !player->cfm || !player->cfm->get_item_attributes) return FALSE; pdu = parse_browsing_pdu(operands, operand_count); if (!pdu) { err = -EPROTO; goto done; } err = parse_browsing_status(pdu); if (err < 0) goto done; err = parse_items(pdu, &number, attrs, text); done: player->cfm->get_item_attributes(session, err, number, attrs, text, player->user_data); if (err == 0) free_attribute_list(number, text); return FALSE; } int avrcp_get_item_attributes(struct avrcp *session, uint8_t scope, uint64_t uid, uint16_t counter, uint8_t number, uint32_t *attrs) { struct iovec iov[2]; struct get_item_attributes_req req; int i; req.scope = scope; req.uid = cpu_to_be64(uid); req.counter = cpu_to_be16(counter); req.number = number; iov[0].iov_base = &req; iov[0].iov_len = sizeof(req); if (!number) return avrcp_send_browsing_req(session, AVRCP_GET_ITEM_ATTRIBUTES, iov, 1, get_item_attributes_rsp, session); if (number > AVRCP_MEDIA_ATTRIBUTE_LAST) return -EINVAL; for (i = 0; i < number; i++) { if (attrs[i] > AVRCP_MEDIA_ATTRIBUTE_LAST || attrs[i] == AVRCP_MEDIA_ATTRIBUTE_ILLEGAL) return -EINVAL; attrs[i] = cpu_to_be32(attrs[i]); } iov[1].iov_base = attrs; iov[1].iov_len = number * sizeof(*attrs); return avrcp_send_browsing_req(session, AVRCP_GET_ITEM_ATTRIBUTES, iov, 2, get_item_attributes_rsp, session); } static gboolean play_item_rsp(struct avctp *conn, uint8_t *operands, size_t operand_count, void *user_data) { struct avrcp *session = user_data; struct avrcp_player *player = session->player; struct avrcp_browsing_header *pdu; int err; DBG(""); if (!player || !player->cfm || !player->cfm->play_item) return FALSE; pdu = parse_browsing_pdu(operands, operand_count); if (!pdu) { err = -EPROTO; goto done; } err = parse_browsing_status(pdu); done: player->cfm->play_item(session, err, player->user_data); return FALSE; } int avrcp_play_item(struct avrcp *session, uint8_t scope, uint64_t uid, uint16_t counter) { struct iovec iov; struct play_item_req req; if (scope > AVRCP_MEDIA_NOW_PLAYING) return -EINVAL; req.scope = scope; req.uid = cpu_to_be64(uid); req.counter = cpu_to_be16(counter); iov.iov_base = &req; iov.iov_len = sizeof(req); return avrcp_send_browsing_req(session, AVRCP_PLAY_ITEM, &iov, 1, play_item_rsp, session); } static gboolean search_rsp(struct avctp *conn, uint8_t *operands, size_t operand_count, void *user_data) { struct avrcp *session = user_data; struct avrcp_player *player = session->player; struct avrcp_browsing_header *pdu; struct search_rsp *rsp; uint16_t counter = 0; uint32_t items = 0; int err; DBG(""); if (!player || !player->cfm || !player->cfm->search) return FALSE; pdu = parse_browsing_pdu(operands, operand_count); if (!pdu) { err = -EPROTO; goto done; } err = parse_browsing_status(pdu); if (err < 0) goto done; if (pdu->params_len < sizeof(*rsp)) { err = -EPROTO; goto done; } rsp = (void *) pdu->params; counter = be16_to_cpu(rsp->counter); items = be32_to_cpu(rsp->items); err = 0; done: player->cfm->search(session, err, counter, items, player->user_data); return FALSE; } int avrcp_search(struct avrcp *session, const char *string) { struct iovec iov[2]; struct search_req req; size_t len; if (!string) return -EINVAL; len = strnlen(string, UINT8_MAX); req.charset = cpu_to_be16(AVRCP_CHARSET_UTF8); req.len = cpu_to_be16(len); iov[0].iov_base = &req; iov[0].iov_len = sizeof(req); iov[1].iov_base = (void *) string; iov[1].iov_len = len; return avrcp_send_browsing_req(session, AVRCP_SEARCH, iov, 2, search_rsp, session); } static gboolean add_to_now_playing_rsp(struct avctp *conn, uint8_t *operands, size_t operand_count, void *user_data) { struct avrcp *session = user_data; struct avrcp_player *player = session->player; struct avrcp_browsing_header *pdu; int err; DBG(""); if (!player || !player->cfm || !player->cfm->add_to_now_playing) return FALSE; pdu = parse_browsing_pdu(operands, operand_count); if (!pdu) { err = -EPROTO; goto done; } err = parse_browsing_status(pdu); done: player->cfm->add_to_now_playing(session, err, player->user_data); return FALSE; } int avrcp_add_to_now_playing(struct avrcp *session, uint8_t scope, uint64_t uid, uint16_t counter) { struct iovec iov; struct add_to_now_playing_req req; if (scope > AVRCP_MEDIA_NOW_PLAYING) return -EINVAL; req.scope = scope; req.uid = cpu_to_be64(uid); req.counter = cpu_to_be16(counter); iov.iov_base = &req; iov.iov_len = sizeof(req); return avrcp_send_browsing_req(session, AVRCP_ADD_TO_NOW_PLAYING, &iov, 1, add_to_now_playing_rsp, session); } int avrcp_get_capabilities_rsp(struct avrcp *session, uint8_t transaction, uint8_t number, uint8_t *events) { struct iovec iov[2]; struct get_capabilities_rsp rsp; if (number > AVRCP_EVENT_LAST) return -EINVAL; rsp.cap = CAP_EVENTS_SUPPORTED; rsp.number = number; iov[0].iov_base = &rsp; iov[0].iov_len = sizeof(rsp); iov[1].iov_base = events; iov[1].iov_len = number; return avrcp_send(session, transaction, AVC_CTYPE_STABLE, AVC_SUBUNIT_PANEL, AVRCP_GET_CAPABILITIES, iov, 2); } int avrcp_list_player_attributes_rsp(struct avrcp *session, uint8_t transaction, uint8_t number, uint8_t *attrs) { struct iovec iov[2]; struct list_attributes_rsp rsp; if (number > AVRCP_ATTRIBUTE_LAST) return -EINVAL; rsp.number = number; iov[0].iov_base = &rsp; iov[0].iov_len = sizeof(rsp); if (!number) return avrcp_send(session, transaction, AVC_CTYPE_STABLE, AVC_SUBUNIT_PANEL, AVRCP_LIST_PLAYER_ATTRIBUTES, iov, 1); iov[1].iov_base = attrs; iov[1].iov_len = number; return avrcp_send(session, transaction, AVC_CTYPE_STABLE, AVC_SUBUNIT_PANEL, AVRCP_LIST_PLAYER_ATTRIBUTES, iov, 2); } int avrcp_get_player_attribute_text_rsp(struct avrcp *session, uint8_t transaction, uint8_t number, uint8_t *attrs, const char **text) { struct iovec iov[1 + AVRCP_ATTRIBUTE_LAST * 2]; struct text_value val[AVRCP_ATTRIBUTE_LAST]; int i; if (number > AVRCP_ATTRIBUTE_LAST) return -EINVAL; iov[0].iov_base = &number; iov[0].iov_len = sizeof(number); for (i = 0; i < number; i++) { uint8_t len = 0; if (attrs[i] > AVRCP_ATTRIBUTE_LAST || attrs[i] == AVRCP_ATTRIBUTE_ILEGAL) return -EINVAL; if (text[i]) len = strlen(text[i]); val[i].attr = attrs[i]; val[i].charset = cpu_to_be16(AVRCP_CHARSET_UTF8); val[i].len = len; iov[i + 1].iov_base = &val[i]; iov[i + 1].iov_len = sizeof(val[i]); iov[i + 2].iov_base = (void *) text[i]; iov[i + 2].iov_len = len; } return avrcp_send(session, transaction, AVC_CTYPE_STABLE, AVC_SUBUNIT_PANEL, AVRCP_GET_PLAYER_ATTRIBUTE_TEXT, iov, 1 + i * 2); } int avrcp_list_player_values_rsp(struct avrcp *session, uint8_t transaction, uint8_t number, uint8_t *values) { struct iovec iov[2]; if (number > AVRCP_ATTRIBUTE_LAST) return -EINVAL; iov[0].iov_base = &number; iov[0].iov_len = sizeof(number); iov[1].iov_base = values; iov[1].iov_len = number; return avrcp_send(session, transaction, AVC_CTYPE_STABLE, AVC_SUBUNIT_PANEL, AVRCP_LIST_PLAYER_VALUES, iov, 2); } int avrcp_get_play_status_rsp(struct avrcp *session, uint8_t transaction, uint32_t position, uint32_t duration, uint8_t status) { struct iovec iov; struct get_play_status_rsp rsp; rsp.duration = cpu_to_be32(duration); rsp.position = cpu_to_be32(position); rsp.status = status; iov.iov_base = &rsp; iov.iov_len = sizeof(rsp); return avrcp_send(session, transaction, AVC_CTYPE_STABLE, AVC_SUBUNIT_PANEL, AVRCP_GET_PLAY_STATUS, &iov, 1); } int avrcp_get_player_values_text_rsp(struct avrcp *session, uint8_t transaction, uint8_t number, uint8_t *values, const char **text) { struct iovec iov[1 + AVRCP_ATTRIBUTE_LAST * 2]; struct text_value val[AVRCP_ATTRIBUTE_LAST]; int i; if (number > AVRCP_ATTRIBUTE_LAST) return -EINVAL; iov[0].iov_base = &number; iov[0].iov_len = sizeof(number); for (i = 0; i < number; i++) { uint8_t len = 0; if (text[i]) len = strlen(text[i]); val[i].attr = values[i]; val[i].charset = cpu_to_be16(AVRCP_CHARSET_UTF8); val[i].len = len; iov[i + 1].iov_base = &val[i]; iov[i + 1].iov_len = sizeof(val[i]); iov[i + 2].iov_base = (void *) text[i]; iov[i + 2].iov_len = len; } return avrcp_send(session, transaction, AVC_CTYPE_STABLE, AVC_SUBUNIT_PANEL, AVRCP_GET_PLAYER_VALUE_TEXT, iov, 1 + i * 2); } int avrcp_get_current_player_value_rsp(struct avrcp *session, uint8_t transaction, uint8_t number, uint8_t *attrs, uint8_t *values) { struct iovec iov[1 + AVRCP_ATTRIBUTE_LAST]; struct attr_value val[AVRCP_ATTRIBUTE_LAST]; int i; if (number > AVRCP_ATTRIBUTE_LAST) return -EINVAL; iov[0].iov_base = &number; iov[0].iov_len = sizeof(number); for (i = 0; i < number; i++) { val[i].attr = attrs[i]; val[i].value = values[i]; iov[i + 1].iov_base = &val[i]; iov[i + 1].iov_len = sizeof(val[i]); } return avrcp_send(session, transaction, AVC_CTYPE_STABLE, AVC_SUBUNIT_PANEL, AVRCP_GET_CURRENT_PLAYER_VALUE, iov, 1 + i); } int avrcp_set_player_value_rsp(struct avrcp *session, uint8_t transaction) { return avrcp_send(session, transaction, AVC_CTYPE_STABLE, AVC_SUBUNIT_PANEL, AVRCP_SET_PLAYER_VALUE, NULL, 0); } int avrcp_get_element_attrs_rsp(struct avrcp *session, uint8_t transaction, uint8_t *params, size_t params_len) { struct iovec iov; iov.iov_base = params; iov.iov_len = params_len; return avrcp_send(session, transaction, AVC_CTYPE_STABLE, AVC_SUBUNIT_PANEL, AVRCP_GET_ELEMENT_ATTRIBUTES, &iov, 1); } int avrcp_register_notification_rsp(struct avrcp *session, uint8_t transaction, uint8_t code, uint8_t event, void *data, size_t len) { struct iovec iov[2]; uint16_t *player; uint8_t *volume; if (event > AVRCP_EVENT_LAST) return -EINVAL; iov[0].iov_base = &event; iov[0].iov_len = sizeof(event); switch (event) { case AVRCP_EVENT_STATUS_CHANGED: if (len != sizeof(uint8_t)) return -EINVAL; break; case AVRCP_EVENT_VOLUME_CHANGED: if (len != sizeof(uint8_t)) return -EINVAL; volume = data; if (volume[0] > 127) return -EINVAL; break; case AVRCP_EVENT_TRACK_CHANGED: if (len != sizeof(uint64_t)) return -EINVAL; put_be64(*(uint64_t *) data, data); break; case AVRCP_EVENT_PLAYBACK_POS_CHANGED: if (len != sizeof(uint32_t)) return -EINVAL; put_be32(*(uint32_t *) data, data); break; case AVRCP_EVENT_ADDRESSED_PLAYER_CHANGED: if (len != 4) return -EINVAL; player = data; player[0] = cpu_to_be16(player[0]); player[1] = cpu_to_be16(player[1]); break; case AVRCP_EVENT_SETTINGS_CHANGED: if (len < sizeof(uint8_t)) return -EINVAL; break; case AVRCP_EVENT_UIDS_CHANGED: if (len != sizeof(uint16_t)) return -EINVAL; put_be16(*(uint16_t *) data, data); break; default: return avrcp_send(session, transaction, code, AVC_SUBUNIT_PANEL, AVRCP_REGISTER_NOTIFICATION, iov, 1); } iov[1].iov_base = data; iov[1].iov_len = len; return avrcp_send(session, transaction, code, AVC_SUBUNIT_PANEL, AVRCP_REGISTER_NOTIFICATION, iov, 2); } int avrcp_set_volume_rsp(struct avrcp *session, uint8_t transaction, uint8_t volume) { struct iovec iov; if (volume > 127) return -EINVAL; iov.iov_base = &volume; iov.iov_len = sizeof(volume); return avrcp_send(session, transaction, AVC_CTYPE_STABLE, AVC_SUBUNIT_PANEL, AVRCP_SET_ABSOLUTE_VOLUME, &iov, 1); } int avrcp_set_addressed_player_rsp(struct avrcp *session, uint8_t transaction, uint8_t status) { struct iovec iov; iov.iov_base = &status; iov.iov_len = sizeof(status); return avrcp_send(session, transaction, AVC_CTYPE_STABLE, AVC_SUBUNIT_PANEL, AVRCP_SET_ADDRESSED_PLAYER, &iov, 1); } static int avrcp_status_rsp(struct avrcp *session, uint8_t transaction, uint8_t pdu_id, uint8_t status) { struct iovec iov; if (status > AVRCP_STATUS_ADDRESSED_PLAYER_CHANGED) return -EINVAL; iov.iov_base = &status; iov.iov_len = sizeof(status); return avrcp_send_browsing(session, transaction, pdu_id, &iov, 1); } int avrcp_set_browsed_player_rsp(struct avrcp *session, uint8_t transaction, uint8_t status, uint16_t counter, uint32_t items, uint8_t depth, const char **folders) { struct iovec iov[UINT8_MAX * 2 + 1]; struct set_browsed_rsp rsp; uint16_t len[UINT8_MAX]; int i; if (status != AVRCP_STATUS_SUCCESS) return avrcp_status_rsp(session, transaction, AVRCP_SET_BROWSED_PLAYER, status); rsp.status = status; rsp.counter = cpu_to_be16(counter); rsp.items = cpu_to_be32(items); rsp.charset = cpu_to_be16(AVRCP_CHARSET_UTF8); rsp.depth = depth; iov[0].iov_base = &rsp; iov[0].iov_len = sizeof(rsp); if (!depth) return avrcp_send_browsing(session, transaction, AVRCP_SET_BROWSED_PLAYER, iov, 1); for (i = 0; i < depth; i++) { if (!folders[i]) return -EINVAL; len[i] = strlen(folders[i]); iov[i * 2 + 2].iov_base = (void *) folders[i]; iov[i * 2 + 2].iov_len = len[i]; len[i] = cpu_to_be16(len[i]); iov[i * 2 + 1].iov_base = &len[i]; iov[i * 2 + 1].iov_len = sizeof(len[i]); } return avrcp_send_browsing(session, transaction, AVRCP_SET_BROWSED_PLAYER, iov, depth * 2 + 1); } int avrcp_get_folder_items_rsp(struct avrcp *session, uint8_t transaction, uint8_t status, uint16_t counter, uint8_t number, uint8_t *type, uint16_t *len, uint8_t **params) { struct iovec iov[UINT8_MAX * 2 + 1]; struct get_folder_items_rsp rsp; uint8_t item[UINT8_MAX][3]; int i; if (status != AVRCP_STATUS_SUCCESS) return avrcp_status_rsp(session, transaction, AVRCP_GET_FOLDER_ITEMS, status); rsp.status = status; rsp.counter = cpu_to_be16(counter); rsp.number = cpu_to_be16(number); iov[0].iov_base = &rsp; iov[0].iov_len = sizeof(rsp); for (i = 0; i < number; i++) { item[i][0] = type[i]; put_be16(len[i], &item[i][1]); iov[i * 2 + 1].iov_base = item[i]; iov[i * 2 + 1].iov_len = sizeof(item[i]); iov[i * 2 + 2].iov_base = params[i]; iov[i * 2 + 2].iov_len = len[i]; } return avrcp_send_browsing(session, transaction, AVRCP_GET_FOLDER_ITEMS, iov, number * 2 + 1); } int avrcp_change_path_rsp(struct avrcp *session, uint8_t transaction, uint8_t status, uint32_t items) { struct iovec iov; struct change_path_rsp rsp; if (status != AVRCP_STATUS_SUCCESS) return avrcp_status_rsp(session, transaction, AVRCP_CHANGE_PATH, status); rsp.status = status; rsp.items = cpu_to_be32(items); iov.iov_base = &rsp; iov.iov_len = sizeof(rsp); return avrcp_send_browsing(session, transaction, AVRCP_CHANGE_PATH, &iov, 1); } static bool pack_attribute_list(struct iovec *iov, uint8_t number, uint32_t *attrs, const char **text) { int i; struct media_item val[AVRCP_MEDIA_ATTRIBUTE_LAST]; for (i = 0; i < number; i++) { uint16_t len = 0; if (attrs[i] > AVRCP_MEDIA_ATTRIBUTE_LAST || attrs[i] == AVRCP_MEDIA_ATTRIBUTE_ILLEGAL) return false; if (text[i]) len = strlen(text[i]); val[i].attr = cpu_to_be32(attrs[i]); val[i].charset = cpu_to_be16(AVRCP_CHARSET_UTF8); val[i].len = cpu_to_be16(len); iov[i].iov_base = &val[i]; iov[i].iov_len = sizeof(val[i]); iov[i + 1].iov_base = (void *) text[i]; iov[i + 1].iov_len = len; } return true; } int avrcp_get_item_attributes_rsp(struct avrcp *session, uint8_t transaction, uint8_t status, uint8_t number, uint32_t *attrs, const char **text) { struct iovec iov[AVRCP_MEDIA_ATTRIBUTE_LAST * 2 + 1]; struct get_item_attributes_rsp rsp; if (number > AVRCP_MEDIA_ATTRIBUTE_LAST) return -EINVAL; if (status != AVRCP_STATUS_SUCCESS) return avrcp_status_rsp(session, transaction, AVRCP_GET_ITEM_ATTRIBUTES, status); rsp.status = status; rsp.number = number; iov[0].iov_base = &rsp; iov[0].iov_len = sizeof(rsp); if (!pack_attribute_list(&iov[1], number, attrs, text)) return -EINVAL; return avrcp_send_browsing(session, transaction, AVRCP_GET_ITEM_ATTRIBUTES, iov, number * 2 + 1); } int avrcp_play_item_rsp(struct avrcp *session, uint8_t transaction, uint8_t status) { return avrcp_status_rsp(session, transaction, AVRCP_PLAY_ITEM, status); } int avrcp_search_rsp(struct avrcp *session, uint8_t transaction, uint8_t status, uint16_t counter, uint32_t items) { struct iovec iov; struct search_rsp rsp; if (status != AVRCP_STATUS_SUCCESS) return avrcp_status_rsp(session, transaction, AVRCP_SEARCH, status); rsp.status = status; rsp.counter = cpu_to_be16(counter); rsp.items = cpu_to_be32(items); iov.iov_base = &rsp; iov.iov_len = sizeof(rsp); return avrcp_send_browsing(session, transaction, AVRCP_SEARCH, &iov, 1); } int avrcp_add_to_now_playing_rsp(struct avrcp *session, uint8_t transaction, uint8_t status) { return avrcp_status_rsp(session, transaction, AVRCP_ADD_TO_NOW_PLAYING, status); } int avrcp_send_passthrough(struct avrcp *session, uint32_t vendor, uint8_t op) { uint8_t params[5]; if (!vendor) return avctp_send_passthrough(session->conn, op, NULL, 0); hton24(params, vendor); put_be16(op, ¶ms[3]); return avctp_send_passthrough(session->conn, AVC_VENDOR_UNIQUE, params, sizeof(params)); } bluez-5.82/android/PaxHeaders/hal-ipc.c0000644000000000000000000000005014015011623014740 xustar0020 atime=1743516862 20 ctime=1743591276 bluez-5.82/android/hal-ipc.c0000644000000000000000000002136514015011623014430 0ustar00rootroot// SPDX-License-Identifier: Apache-2.0 /* * Copyright (C) 2013 Intel Corporation * */ #include #include #include #include #include #include #include #include #include #include #include "hal.h" #include "hal-msg.h" #include "hal-log.h" #include "ipc-common.h" #include "hal-ipc.h" #define CONNECT_TIMEOUT (10 * 1000) static int listen_sk = -1; static int cmd_sk = -1; static int notif_sk = -1; static pthread_mutex_t cmd_sk_mutex = PTHREAD_MUTEX_INITIALIZER; static pthread_t notif_th = 0; struct service_handler { const struct hal_ipc_handler *handler; uint8_t size; }; static struct service_handler services[HAL_SERVICE_ID_MAX + 1]; void hal_ipc_register(uint8_t service, const struct hal_ipc_handler *handlers, uint8_t size) { services[service].handler = handlers; services[service].size = size; } void hal_ipc_unregister(uint8_t service) { services[service].handler = NULL; services[service].size = 0; } static bool handle_msg(void *buf, ssize_t len, int fd) { struct ipc_hdr *msg = buf; const struct hal_ipc_handler *handler; uint8_t opcode; if (len < (ssize_t) sizeof(*msg)) { error("IPC: message too small (%zd bytes)", len); return false; } if (len != (ssize_t) (sizeof(*msg) + msg->len)) { error("IPC: message malformed (%zd bytes)", len); return false; } /* if service is valid */ if (msg->service_id > HAL_SERVICE_ID_MAX) { error("IPC: unknown service (0x%x)", msg->service_id); return false; } /* if service is registered */ if (!services[msg->service_id].handler) { error("IPC: unregistered service (0x%x)", msg->service_id); return false; } /* if opcode fit valid range */ if (msg->opcode < HAL_MINIMUM_EVENT) { error("IPC: invalid opcode for service 0x%x (0x%x)", msg->service_id, msg->opcode); return false; } /* * opcode is used as table offset and must be adjusted as events start * with HAL_MINIMUM_EVENT offset */ opcode = msg->opcode - HAL_MINIMUM_EVENT; /* if opcode is valid */ if (opcode >= services[msg->service_id].size) { error("IPC: invalid opcode for service 0x%x (0x%x)", msg->service_id, msg->opcode); return false; } handler = &services[msg->service_id].handler[opcode]; /* if payload size is valid */ if ((handler->var_len && handler->data_len > msg->len) || (!handler->var_len && handler->data_len != msg->len)) { error("IPC: message size invalid for service 0x%x opcode 0x%x " "(%u bytes)", msg->service_id, msg->opcode, msg->len); return false; } handler->handler(msg->payload, msg->len, fd); return true; } static void *notification_handler(void *data) { struct msghdr msg; struct iovec iv; struct cmsghdr *cmsg; char cmsgbuf[CMSG_SPACE(sizeof(int))]; char buf[IPC_MTU]; ssize_t ret; int fd; bt_thread_associate(); while (true) { memset(&msg, 0, sizeof(msg)); memset(buf, 0, sizeof(buf)); memset(cmsgbuf, 0, sizeof(cmsgbuf)); iv.iov_base = buf; iv.iov_len = sizeof(buf); msg.msg_iov = &iv; msg.msg_iovlen = 1; msg.msg_control = cmsgbuf; msg.msg_controllen = sizeof(cmsgbuf); ret = recvmsg(notif_sk, &msg, 0); if (ret < 0) { error("Receiving notifications failed: %s", strerror(errno)); goto failed; } /* socket was shutdown */ if (ret == 0) { pthread_mutex_lock(&cmd_sk_mutex); if (cmd_sk == -1) { pthread_mutex_unlock(&cmd_sk_mutex); break; } pthread_mutex_unlock(&cmd_sk_mutex); error("Notification socket closed"); goto failed; } fd = -1; /* Receive auxiliary data in msg */ for (cmsg = CMSG_FIRSTHDR(&msg); cmsg; cmsg = CMSG_NXTHDR(&msg, cmsg)) { if (cmsg->cmsg_level == SOL_SOCKET && cmsg->cmsg_type == SCM_RIGHTS) { memcpy(&fd, CMSG_DATA(cmsg), sizeof(int)); break; } } if (!handle_msg(buf, ret, fd)) goto failed; } close(notif_sk); notif_sk = -1; bt_thread_disassociate(); DBG("exit"); return NULL; failed: exit(EXIT_FAILURE); } static int accept_connection(int sk) { int err; struct pollfd pfd; int new_sk; memset(&pfd, 0 , sizeof(pfd)); pfd.fd = sk; pfd.events = POLLIN; err = poll(&pfd, 1, CONNECT_TIMEOUT); if (err < 0) { err = errno; error("Failed to poll: %d (%s)", err, strerror(err)); return -1; } if (err == 0) { error("bluetoothd connect timeout"); return -1; } new_sk = accept(sk, NULL, NULL); if (new_sk < 0) { err = errno; error("Failed to accept socket: %d (%s)", err, strerror(err)); return -1; } return new_sk; } bool hal_ipc_accept(void) { int err; cmd_sk = accept_connection(listen_sk); if (cmd_sk < 0) return false; notif_sk = accept_connection(listen_sk); if (notif_sk < 0) { close(cmd_sk); cmd_sk = -1; return false; } err = pthread_create(¬if_th, NULL, notification_handler, NULL); if (err) { notif_th = 0; error("Failed to start notification thread: %d (%s)", err, strerror(err)); close(cmd_sk); cmd_sk = -1; close(notif_sk); notif_sk = -1; return false; } info("IPC connected"); return true; } bool hal_ipc_init(const char *path, size_t size) { struct sockaddr_un addr; int sk; int err; sk = socket(AF_LOCAL, SOCK_SEQPACKET, 0); if (sk < 0) { err = errno; error("Failed to create socket: %d (%s)", err, strerror(err)); return false; } memset(&addr, 0, sizeof(addr)); addr.sun_family = AF_UNIX; memcpy(addr.sun_path, path, size); if (bind(sk, (struct sockaddr *) &addr, sizeof(addr)) < 0) { err = errno; error("Failed to bind socket: %d (%s)", err, strerror(err)); close(sk); return false; } if (listen(sk, 2) < 0) { err = errno; error("Failed to listen on socket: %d (%s)", err, strerror(err)); close(sk); return false; } listen_sk = sk; return true; } void hal_ipc_cleanup(void) { close(listen_sk); listen_sk = -1; pthread_mutex_lock(&cmd_sk_mutex); if (cmd_sk >= 0) { close(cmd_sk); cmd_sk = -1; } pthread_mutex_unlock(&cmd_sk_mutex); if (notif_sk < 0) return; shutdown(notif_sk, SHUT_RD); pthread_join(notif_th, NULL); notif_th = 0; } int hal_ipc_cmd(uint8_t service_id, uint8_t opcode, uint16_t len, void *param, size_t *rsp_len, void *rsp, int *fd) { ssize_t ret; struct msghdr msg; struct iovec iv[2]; struct ipc_hdr cmd; char cmsgbuf[CMSG_SPACE(sizeof(int))]; struct ipc_status s; size_t s_len = sizeof(s); if (cmd_sk < 0) { error("Invalid cmd socket passed to hal_ipc_cmd"); goto failed; } if (!rsp || !rsp_len) { memset(&s, 0, s_len); rsp_len = &s_len; rsp = &s; } memset(&msg, 0, sizeof(msg)); memset(&cmd, 0, sizeof(cmd)); cmd.service_id = service_id; cmd.opcode = opcode; cmd.len = len; iv[0].iov_base = &cmd; iv[0].iov_len = sizeof(cmd); iv[1].iov_base = param; iv[1].iov_len = len; msg.msg_iov = iv; msg.msg_iovlen = 2; pthread_mutex_lock(&cmd_sk_mutex); ret = sendmsg(cmd_sk, &msg, 0); if (ret < 0) { error("Sending command failed:%s", strerror(errno)); pthread_mutex_unlock(&cmd_sk_mutex); goto failed; } /* socket was shutdown */ if (ret == 0) { error("Command socket closed"); pthread_mutex_unlock(&cmd_sk_mutex); goto failed; } memset(&msg, 0, sizeof(msg)); memset(&cmd, 0, sizeof(cmd)); iv[0].iov_base = &cmd; iv[0].iov_len = sizeof(cmd); iv[1].iov_base = rsp; iv[1].iov_len = *rsp_len; msg.msg_iov = iv; msg.msg_iovlen = 2; if (fd) { memset(cmsgbuf, 0, sizeof(cmsgbuf)); msg.msg_control = cmsgbuf; msg.msg_controllen = sizeof(cmsgbuf); } ret = recvmsg(cmd_sk, &msg, 0); pthread_mutex_unlock(&cmd_sk_mutex); if (ret < 0) { error("Receiving command response failed: %s", strerror(errno)); goto failed; } if (ret < (ssize_t) sizeof(cmd)) { error("Too small response received(%zd bytes)", ret); goto failed; } if (cmd.service_id != service_id) { error("Invalid service id (0x%x vs 0x%x)", cmd.service_id, service_id); goto failed; } if (ret != (ssize_t) (sizeof(cmd) + cmd.len)) { error("Malformed response received(%zd bytes)", ret); goto failed; } if (cmd.opcode != opcode && cmd.opcode != HAL_OP_STATUS) { error("Invalid opcode received (0x%x vs 0x%x)", cmd.opcode, opcode); goto failed; } if (cmd.opcode == HAL_OP_STATUS) { struct ipc_status *s = rsp; if (sizeof(*s) != cmd.len) { error("Invalid status length"); goto failed; } if (s->code == HAL_STATUS_SUCCESS) { error("Invalid success status response"); goto failed; } return s->code; } /* Receive auxiliary data in msg */ if (fd) { struct cmsghdr *cmsg; *fd = -1; for (cmsg = CMSG_FIRSTHDR(&msg); cmsg; cmsg = CMSG_NXTHDR(&msg, cmsg)) { if (cmsg->cmsg_level == SOL_SOCKET && cmsg->cmsg_type == SCM_RIGHTS) { memcpy(fd, CMSG_DATA(cmsg), sizeof(int)); break; } } } *rsp_len = cmd.len; return BT_STATUS_SUCCESS; failed: exit(EXIT_FAILURE); } bluez-5.82/android/PaxHeaders/pixit-a2dp.txt0000644000000000000000000000005012537515745016026 xustar0020 atime=1743516876 20 ctime=1743591289 bluez-5.82/android/pixit-a2dp.txt0000644000000000000000000000166712537515745015521 0ustar00rootrootA2DP PIXIT for the PTS tool. PTS version: 6.1 * - different than PTS defaults & - should be set to IUT Bluetooth address # - should be set to PTS's bin/audio folder Required PIXIT settings ------------------------------------------------------------------------------- Parameter Name Value ------------------------------------------------------------------------------- TSPX_security_enabled TRUE (*) TSPX_bd_addr_iut 112233445566 (*&) TSPX_SRC_class_of_device 080418 TSPX_SNK_class_of_device 04041C TSPX_pin_code 0000 TSPX_delete_link_key FALSE TSPX_time_guard 300000 TSPX_use_implicit_send TRUE TSPX_media_directory C:\Program Files\Bluetooth SIG\Bluetooth PTS\ bin\audio (#) TSPX_no_avrcp FALSE (*) TSPX_auth_password 0000 TSPX_auth_user_id PTS TSPX_rfcomm_channel 8 TSPX_l2cap_psm 1011 TSPX_no_confirmations FALSE TSPX_cover_art_uuid 3EEE ------------------------------------------------------------------------------- bluez-5.82/android/PaxHeaders/hal-health.c0000644000000000000000000000005014015011623015432 xustar0020 atime=1743516862 20 ctime=1743591276 bluez-5.82/android/hal-health.c0000644000000000000000000001443414015011623015121 0ustar00rootroot// SPDX-License-Identifier: Apache-2.0 /* * Copyright (C) 2014 Intel Corporation * */ #include #include #include #include #include #include #include #include "hal-log.h" #include "hal.h" #include "hal-msg.h" #include "ipc-common.h" #include "hal-ipc.h" static const bthl_callbacks_t *cbacks = NULL; static bool interface_ready(void) { return cbacks != NULL; } static void handle_app_registration_state(void *buf, uint16_t len, int fd) { struct hal_ev_health_app_reg_state *ev = buf; if (cbacks->app_reg_state_cb) cbacks->app_reg_state_cb(ev->id, ev->state); } static void handle_channel_state(void *buf, uint16_t len, int fd) { struct hal_ev_health_channel_state *ev = buf; int flags; if (fd < 0) goto end; flags = fcntl(fd, F_GETFL, 0); if (flags < 0) { error("health: fcntl GETFL error: %s", strerror(errno)); return; } /* Clean O_NONBLOCK fd flag as Android Java layer expects */ if (fcntl(fd, F_SETFL, flags & ~O_NONBLOCK) < 0) { error("health: fcntl SETFL error: %s", strerror(errno)); return; } end: if (cbacks->channel_state_cb) cbacks->channel_state_cb(ev->app_id, (bt_bdaddr_t *) ev->bdaddr, ev->mdep_index, ev->channel_id, ev->channel_state, fd); } /* * handlers will be called from notification thread context, * index in table equals to 'opcode - HAL_MINIMUM_EVENT' */ static const struct hal_ipc_handler ev_handlers[] = { /* HAL_EV_HEALTH_APP_REG_STATE */ { handle_app_registration_state, false, sizeof(struct hal_ev_health_app_reg_state) }, /* HAL_EV_HEALTH_CHANNEL_STATE */ { handle_channel_state, false, sizeof(struct hal_ev_health_channel_state) }, }; static bt_status_t register_application(bthl_reg_param_t *reg, int *app_id) { uint8_t buf[IPC_MTU]; struct hal_cmd_health_reg_app *cmd = (void *) buf; struct hal_rsp_health_reg_app rsp; size_t rsp_len = sizeof(rsp); bt_status_t status; uint16_t off, len; int i; DBG(""); if (!interface_ready()) return BT_STATUS_NOT_READY; if (!reg || !app_id || !reg->application_name) return BT_STATUS_PARM_INVALID; *app_id = -1; memset(buf, 0, IPC_MTU); cmd->num_of_mdep = reg->number_of_mdeps; off = 0; cmd->app_name_off = off; len = strlen(reg->application_name) + 1; memcpy(cmd->data, reg->application_name, len); off += len; cmd->provider_name_off = off; if (reg->provider_name) { len = strlen(reg->provider_name) + 1; memcpy(cmd->data + off, reg->provider_name, len); off += len; } cmd->service_name_off = off; if (reg->srv_name) { len = strlen(reg->srv_name) + 1; memcpy(cmd->data + off, reg->srv_name, len); off += len; } cmd->service_descr_off = off; if (reg->srv_desp) { len = strlen(reg->srv_desp) + 1; memcpy(cmd->data + off, reg->srv_desp, len); off += len; } cmd->len = off; status = hal_ipc_cmd(HAL_SERVICE_ID_HEALTH, HAL_OP_HEALTH_REG_APP, sizeof(*cmd) + cmd->len, buf, &rsp_len, &rsp, NULL); if (status != BT_STATUS_SUCCESS) return status; for (i = 0; i < reg->number_of_mdeps; i++) { struct hal_cmd_health_mdep *mdep = (void *) buf; memset(buf, 0, IPC_MTU); mdep->app_id = rsp.app_id; mdep->role = reg->mdep_cfg[i].mdep_role; mdep->data_type = reg->mdep_cfg[i].data_type; mdep->channel_type = reg->mdep_cfg[i].channel_type; if (reg->mdep_cfg[i].mdep_description) { mdep->descr_len = strlen(reg->mdep_cfg[i].mdep_description) + 1; memcpy(mdep->descr, reg->mdep_cfg[i].mdep_description, mdep->descr_len); } status = hal_ipc_cmd(HAL_SERVICE_ID_HEALTH, HAL_OP_HEALTH_MDEP, sizeof(*mdep) + mdep->descr_len, buf, NULL, NULL, NULL); if (status != BT_STATUS_SUCCESS) return status; } *app_id = rsp.app_id; return status; } static bt_status_t unregister_application(int app_id) { struct hal_cmd_health_unreg_app cmd; DBG(""); if (!interface_ready()) return BT_STATUS_NOT_READY; cmd.app_id = app_id; return hal_ipc_cmd(HAL_SERVICE_ID_HEALTH, HAL_OP_HEALTH_UNREG_APP, sizeof(cmd), &cmd, NULL, NULL, NULL); } static bt_status_t connect_channel(int app_id, bt_bdaddr_t *bd_addr, int mdep_cfg_index, int *channel_id) { struct hal_cmd_health_connect_channel cmd; struct hal_rsp_health_connect_channel rsp; size_t len = sizeof(rsp); bt_status_t status; DBG(""); if (!interface_ready()) return BT_STATUS_NOT_READY; if (!bd_addr || !channel_id) return BT_STATUS_PARM_INVALID; *channel_id = -1; cmd.app_id = app_id; cmd.mdep_index = mdep_cfg_index; memcpy(cmd.bdaddr, bd_addr, sizeof(cmd.bdaddr)); status = hal_ipc_cmd(HAL_SERVICE_ID_HEALTH, HAL_OP_HEALTH_CONNECT_CHANNEL, sizeof(cmd), &cmd, &len, &rsp, NULL); if (status == BT_STATUS_SUCCESS) *channel_id = rsp.channel_id; return status; } static bt_status_t destroy_channel(int channel_id) { struct hal_cmd_health_destroy_channel cmd; DBG(""); if (!interface_ready()) return BT_STATUS_NOT_READY; cmd.channel_id = channel_id; return hal_ipc_cmd(HAL_SERVICE_ID_HEALTH, HAL_OP_HEALTH_DESTROY_CHANNEL, sizeof(cmd), &cmd, NULL, NULL, NULL); } static bt_status_t init(bthl_callbacks_t *callbacks) { struct hal_cmd_register_module cmd; int ret; DBG(""); if (interface_ready()) return BT_STATUS_DONE; /* store reference to user callbacks */ cbacks = callbacks; hal_ipc_register(HAL_SERVICE_ID_HEALTH, ev_handlers, sizeof(ev_handlers)/sizeof(ev_handlers[0])); cmd.service_id = HAL_SERVICE_ID_HEALTH; cmd.mode = HAL_MODE_DEFAULT; cmd.max_clients = 1; ret = hal_ipc_cmd(HAL_SERVICE_ID_CORE, HAL_OP_REGISTER_MODULE, sizeof(cmd), &cmd, NULL, NULL, NULL); if (ret != BT_STATUS_SUCCESS) { cbacks = NULL; hal_ipc_unregister(HAL_SERVICE_ID_HEALTH); } return ret; } static void cleanup(void) { struct hal_cmd_unregister_module cmd; DBG(""); if (!interface_ready()) return; cmd.service_id = HAL_SERVICE_ID_HEALTH; hal_ipc_cmd(HAL_SERVICE_ID_CORE, HAL_OP_UNREGISTER_MODULE, sizeof(cmd), &cmd, NULL, NULL, NULL); hal_ipc_unregister(HAL_SERVICE_ID_HEALTH); cbacks = NULL; } static bthl_interface_t health_if = { .size = sizeof(health_if), .init = init, .register_application = register_application, .unregister_application = unregister_application, .connect_channel = connect_channel, .destroy_channel = destroy_channel, .cleanup = cleanup }; bthl_interface_t *bt_get_health_interface(void) { return &health_if; } bluez-5.82/android/PaxHeaders/pts-avrcp.txt0000644000000000000000000000005012537515745015764 xustar0020 atime=1743516876 20 ctime=1743591289 bluez-5.82/android/pts-avrcp.txt0000644000000000000000000001420712537515745015451 0ustar00rootrootPTS test results for AVRCP PTS version: 6.1 Tested: 21-May-2015 Android version: 5.1 Results: PASS test passed FAIL test failed INC test is inconclusive N/A test is disabled due to PICS setup Controller (CT) ------------------------------------------------------------------------------- Test Name Result Notes ------------------------------------------------------------------------------- TC_CT_BGN_BV_01_I N/A TC_CT_BGN_BV_02_I N/A TC_CT_CA_BV_01_C N/A TC_CT_CA_BV_03_C N/A TC_CT_CA_BV_05_C N/A TC_CT_CA_BV_07_C N/A TC_CT_CA_BV_09_C N/A TC_CT_CA_BV_11_C N/A TC_CT_CA_BV_13_C N/A TC_CT_CA_BV_15_C N/A TC_CT_CA_BV_17_C N/A TC_CT_CA_BV_18_C N/A TC_CT_CA_BV_01_I N/A TC_CT_CA_BV_02_I N/A TC_CT_CA_BV_03_I N/A TC_CT_CEC_BV_01_I N/A TC_CT_CEC_BV_02_I N/A TC_CT_CFG_BV_01_C N/A TC_CT_CON_BV_01_C N/A TC_CT_CON_BV_03_C N/A TC_CT_CRC_BV_01_I N/A TC_CT_CRC_BV_02_I N/A TC_CT_ICC_BV_01_I N/A TC_CT_ICC_BV_02_I N/A TC_CT_MCN_CB_BV_01_C N/A TC_CT_MCN_CB_BV_04_C N/A TC_CT_MCN_CB_BV_07_C N/A TC_CT_MCN_CB_BV_12_C N/A TC_CT_MCN_CB_BV_01_I N/A TC_CT_MCN_CB_BV_02_I N/A TC_CT_MCN_CB_BV_03_I N/A TC_CT_MCN_CB_BV_04_I N/A TC_CT_MCN_CB_BV_05_I N/A TC_CT_MCN_CB_BV_06_I N/A TC_CT_MCN_NP_BV_01_C N/A TC_CT_MCN_NP_BV_03_C N/A TC_CT_MCN_NP_BV_05_C N/A TC_CT_MCN_NP_BV_08_C N/A TC_CT_MCN_NP_BV_10_C N/A TC_CT_MCN_NP_BV_01_I N/A TC_CT_MCN_NP_BV_02_I N/A TC_CT_MCN_NP_BV_03_I N/A TC_CT_MCN_NP_BV_04_I N/A TC_CT_MCN_NP_BV_05_I N/A TC_CT_MCN_NP_BV_06_I N/A TC_CT_MCN_NP_BV_07_I N/A TC_CT_MCN_SRC_BV_01_C N/A TC_CT_MCN_SRC_BV_03_C N/A TC_CT_MCN_SRC_BV_05_C N/A TC_CT_MCN_SRC_BV_07_C N/A TC_CT_MCN_SRC_BV_01_I N/A TC_CT_MCN_SRC_BV_02_I N/A TC_CT_MCN_SRC_BV_03_I N/A TC_CT_MCN_SRC_BV_04_I N/A TC_CT_MDI_BV_01_C N/A TC_CT_MDI_BV_03_C N/A TC_CT_MPS_BV_01_C N/A TC_CT_MPS_BV_03_C N/A TC_CT_MPS_BV_08_C N/A TC_CT_MPS_BV_11_C N/A TC_CT_MPS_BV_01_I N/A TC_CT_MPS_BV_02_I N/A TC_CT_MPS_BV_03_I N/A TC_CT_NFY_BV_01_C N/A TC_CT_PAS_BV_01_C N/A TC_CT_PAS_BV_03_C N/A TC_CT_PAS_BV_05_C N/A TC_CT_PAS_BV_07_C N/A TC_CT_PAS_BV_09_C N/A TC_CT_PAS_BV_11_C N/A TC_CT_PTH_BV_01_C N/A TC_CT_PTH_BV_02_C N/A TC_CT_PTT_BV_01_I N/A TC_CT_PTT_BV_02_I N/A TC_CT_PTT_BV_03_I N/A TC_CT_PTT_BV_04_I N/A TC_CT_PTT_BV_05_I N/A TC_CT_RCR_BV_01_C N/A TC_CT_RCR_BV_03_C N/A TC_CT_VLH_BI_03_C PASS Send SetAbsolute Volume command by pressing volume up or down buttons then adb logcat and check VOLUME_CHANGED value TC_CT_VLH_BI_04_C PASS adb logcat: check VOLUME_CHANGED value TC_CT_VLH_BV_01_C PASS Send SetAbsolute Volume command by pressing volume up or down buttons TC_CT_VLH_BV_03_C PASS adb logcat: check VOLUME_CHANGED value TC_CT_VLH_BV_01_I PASS Send SetAbsolute Volume command by pressing volume up or down buttons TC_CT_VLH_BV_02_I PASS Send SetAbsolute Volume command by pressing volume up or down buttons ------------------------------------------------------------------------------- Target (TG) ------------------------------------------------------------------------------- Test Name Result Notes ------------------------------------------------------------------------------- TC_TG_BGN_BV_01_I N/A TC_TG_BGN_BV_02_I N/A TC_TG_CA_BI_01_C N/A TC_TG_CA_BI_02_C N/A TC_TG_CA_BI_03_C N/A TC_TG_CA_BI_04_C N/A TC_TG_CA_BI_05_C N/A TC_TG_CA_BI_06_C N/A TC_TG_CA_BI_07_C N/A TC_TG_CA_BI_08_C N/A TC_TG_CA_BI_09_C N/A TC_TG_CA_BI_10_C N/A TC_TG_CA_BV_02_C N/A TC_TG_CA_BV_04_C N/A TC_TG_CA_BV_06_C N/A TC_TG_CA_BV_08_C N/A TC_TG_CA_BV_10_C N/A TC_TG_CA_BV_12_C N/A TC_TG_CA_BV_14_C N/A TC_TG_CA_BV_16_C N/A TC_TG_CA_BV_01_I N/A TC_TG_CA_BV_02_I N/A TC_TG_CA_BV_03_I N/A TC_TG_CEC_BV_01_I PASS TC_TG_CEC_BV_02_I PASS TC_TG_CFG_BI_01_C PASS TC_TG_CFG_BV_02_C PASS TC_TG_CON_BV_02_C N/A TC_TG_CON_BV_04_C N/A TC_TG_CON_BV_05_C N/A TC_TG_CRC_BV_01_I PASS TC_TG_CRC_BV_02_I PASS Disconnect from PTS TC_TG_ICC_BV_01_I PASS TC_TG_ICC_BV_02_I PASS TC_TG_INV_BI_01_C PASS TC_TG_INV_BI_02_C N/A TC_TG_MCN_CB_BI_01_C N/A TC_TG_MCN_CB_BI_02_C N/A TC_TG_MCN_CB_BI_03_C N/A TC_TG_MCN_CB_BI_04_C N/A TC_TG_MCN_CB_BI_05_C N/A TC_TG_MCN_CB_BV_02_C N/A TC_TG_MCN_CB_BV_02_I N/A TC_TG_MCN_CB_BV_03_C N/A TC_TG_MCN_CB_BV_03_I N/A TC_TG_MCN_CB_BV_04_I N/A TC_TG_MCN_CB_BV_05_C N/A TC_TG_MCN_CB_BV_05_I N/A TC_TG_MCN_CB_BV_06_C N/A TC_TG_MCN_CB_BV_06_I N/A TC_TG_MCN_CB_BV_07_I N/A TC_TG_MCN_CB_BV_08_C N/A TC_TG_MCN_CB_BV_09_C N/A TC_TG_MCN_CB_BV_10_C N/A TC_TG_MCN_CB_BV_11_C N/A TC_TG_MCN_CB_BV_13_C N/A TC_TG_MCN_NP_BI_01_C N/A TC_TG_MCN_NP_BI_02_C N/A TC_TG_MCN_NP_BV_01_I N/A TC_TG_MCN_NP_BV_02_C N/A TC_TG_MCN_NP_BV_02_I N/A TC_TG_MCN_NP_BV_03_I N/A TC_TG_MCN_NP_BV_04_C N/A TC_TG_MCN_NP_BV_04_I N/A TC_TG_MCN_NP_BV_05_I N/A TC_TG_MCN_NP_BV_06_C N/A TC_TG_MCN_NP_BV_06_I N/A TC_TG_MCN_NP_BV_07_C N/A TC_TG_MCN_NP_BV_07_I N/A TC_TG_MCN_NP_BV_09_C N/A TC_TG_MCN_NP_BV_11_C N/A TC_TG_MCN_SRC_BV_01_I N/A TC_TG_MCN_SRC_BV_02_C N/A TC_TG_MCN_SRC_BV_02_I N/A TC_TG_MCN_SRC_BV_03_I N/A TC_TG_MCN_SRC_BV_04_C N/A TC_TG_MCN_SRC_BV_04_I N/A TC_TG_MCN_SRC_BV_06_C N/A TC_TG_MCN_SRC_BV_08_C N/A TC_TG_MDI_BV_02_C PASS TC_TG_MDI_BV_04_C PASS TC_TG_MDI_BV_05_C PASS TC_TG_MPS_BI_01_C N/A TC_TG_MPS_BI_02_C N/A TC_TG_MPS_BV_01_I N/A TC_TG_MPS_BV_02_C N/A TC_TG_MPS_BV_02_I N/A TC_TG_MPS_BV_03_I N/A TC_TG_MPS_BV_04_C N/A TC_TG_MPS_BV_05_C N/A TC_TG_MPS_BV_06_C N/A TC_TG_MPS_BV_07_C N/A TC_TG_MPS_BV_09_C N/A TC_TG_MPS_BV_10_C N/A TC_TG_MPS_BV_12_C N/A TC_TG_NFY_BI_01_C PASS TC_TG_NFY_BV_02_C PASS Change track when requested TC_TG_NFY_BV_03_C N/A TC_TG_NFY_BV_04_C PASS TC_TG_NFY_BV_05_C PASS TC_TG_NFY_BV_06_C N/A TC_TG_NFY_BV_07_C N/A TC_TG_NFY_BV_08_C PASS TC_TG_PAS_BI_01_C N/A TC_TG_PAS_BI_02_C N/A TC_TG_PAS_BI_03_C N/A TC_TG_PAS_BI_04_C N/A TC_TG_PAS_BI_05_C N/A TC_TG_PAS_BV_02_C N/A TC_TG_PAS_BV_04_C N/A TC_TG_PAS_BV_06_C N/A TC_TG_PAS_BV_08_C N/A TC_TG_PAS_BV_10_C N/A TC_TG_PTT_BV_01_I PASS TC_TG_PTT_BV_02_I PASS TC_TG_PTT_BV_03_I N/A TC_TG_PTT_BV_04_I N/A TC_TG_PTT_BV_05_I N/A TC_TG_RCR_BV_02_C PASS Use modified media metadata (artist, title, album etc.) to be larger than 512 byte. TC_TG_RCR_BV_04_C PASS Use modified media metadata (artist, title, album etc.) to be larger than 512 byte. TC_TG_VLH_BI_01_C N/A TC_TG_VLH_BI_02_C N/A TC_TG_VLH_BV_01_I N/A TC_TG_VLH_BV_02_C N/A TC_TG_VLH_BV_02_I N/A TC_TG_VLH_BV_04_C N/A ------------------------------------------------------------------------------- bluez-5.82/android/PaxHeaders/avdtp.h0000644000000000000000000000005014015011623014546 xustar0020 atime=1743516864 20 ctime=1743591278 bluez-5.82/android/avdtp.h0000644000000000000000000002210414015011623014226 0ustar00rootroot/* SPDX-License-Identifier: GPL-2.0-or-later */ /* * * BlueZ - Bluetooth protocol stack for Linux * * Copyright (C) 2006-2010 Nokia Corporation * Copyright (C) 2004-2010 Marcel Holtmann * * */ struct avdtp; struct avdtp_stream; struct avdtp_local_sep; struct avdtp_remote_sep; struct avdtp_error { uint8_t category; union { uint8_t error_code; int posix_errno; } err; }; #define AVDTP_PSM 25 /* SEP capability categories */ #define AVDTP_MEDIA_TRANSPORT 0x01 #define AVDTP_REPORTING 0x02 #define AVDTP_RECOVERY 0x03 #define AVDTP_CONTENT_PROTECTION 0x04 #define AVDTP_HEADER_COMPRESSION 0x05 #define AVDTP_MULTIPLEXING 0x06 #define AVDTP_MEDIA_CODEC 0x07 #define AVDTP_DELAY_REPORTING 0x08 #define AVDTP_ERRNO 0xff /* AVDTP error definitions */ #define AVDTP_BAD_HEADER_FORMAT 0x01 #define AVDTP_BAD_LENGTH 0x11 #define AVDTP_BAD_ACP_SEID 0x12 #define AVDTP_SEP_IN_USE 0x13 #define AVDTP_SEP_NOT_IN_USE 0x14 #define AVDTP_BAD_SERV_CATEGORY 0x17 #define AVDTP_BAD_PAYLOAD_FORMAT 0x18 #define AVDTP_NOT_SUPPORTED_COMMAND 0x19 #define AVDTP_INVALID_CAPABILITIES 0x1A #define AVDTP_BAD_RECOVERY_TYPE 0x22 #define AVDTP_BAD_MEDIA_TRANSPORT_FORMAT 0x23 #define AVDTP_BAD_RECOVERY_FORMAT 0x25 #define AVDTP_BAD_ROHC_FORMAT 0x26 #define AVDTP_BAD_CP_FORMAT 0x27 #define AVDTP_BAD_MULTIPLEXING_FORMAT 0x28 #define AVDTP_UNSUPPORTED_CONFIGURATION 0x29 #define AVDTP_BAD_STATE 0x31 /* SEP types definitions */ #define AVDTP_SEP_TYPE_SOURCE 0x00 #define AVDTP_SEP_TYPE_SINK 0x01 /* Media types definitions */ #define AVDTP_MEDIA_TYPE_AUDIO 0x00 #define AVDTP_MEDIA_TYPE_VIDEO 0x01 #define AVDTP_MEDIA_TYPE_MULTIMEDIA 0x02 typedef enum { AVDTP_STATE_IDLE, AVDTP_STATE_CONFIGURED, AVDTP_STATE_OPEN, AVDTP_STATE_STREAMING, AVDTP_STATE_CLOSING, AVDTP_STATE_ABORTING, } avdtp_state_t; struct avdtp_service_capability { uint8_t category; uint8_t length; uint8_t data[0]; } __attribute__ ((packed)); #if __BYTE_ORDER == __LITTLE_ENDIAN struct avdtp_media_codec_capability { uint8_t rfa0:4; uint8_t media_type:4; uint8_t media_codec_type; uint8_t data[0]; } __attribute__ ((packed)); #elif __BYTE_ORDER == __BIG_ENDIAN struct avdtp_media_codec_capability { uint8_t media_type:4; uint8_t rfa0:4; uint8_t media_codec_type; uint8_t data[0]; } __attribute__ ((packed)); #else #error "Unknown byte order" #endif typedef void (*avdtp_stream_state_cb) (struct avdtp_stream *stream, avdtp_state_t old_state, avdtp_state_t new_state, struct avdtp_error *err, void *user_data); typedef void (*avdtp_set_configuration_cb) (struct avdtp *session, struct avdtp_stream *stream, struct avdtp_error *err); /* Callbacks for when a reply is received to a command that we sent */ struct avdtp_sep_cfm { void (*set_configuration) (struct avdtp *session, struct avdtp_local_sep *lsep, struct avdtp_stream *stream, struct avdtp_error *err, void *user_data); void (*get_configuration) (struct avdtp *session, struct avdtp_local_sep *lsep, struct avdtp_stream *stream, struct avdtp_error *err, void *user_data); void (*open) (struct avdtp *session, struct avdtp_local_sep *lsep, struct avdtp_stream *stream, struct avdtp_error *err, void *user_data); void (*start) (struct avdtp *session, struct avdtp_local_sep *lsep, struct avdtp_stream *stream, struct avdtp_error *err, void *user_data); void (*suspend) (struct avdtp *session, struct avdtp_local_sep *lsep, struct avdtp_stream *stream, struct avdtp_error *err, void *user_data); void (*close) (struct avdtp *session, struct avdtp_local_sep *lsep, struct avdtp_stream *stream, struct avdtp_error *err, void *user_data); void (*abort) (struct avdtp *session, struct avdtp_local_sep *lsep, struct avdtp_stream *stream, struct avdtp_error *err, void *user_data); void (*reconfigure) (struct avdtp *session, struct avdtp_local_sep *lsep, struct avdtp_stream *stream, struct avdtp_error *err, void *user_data); void (*delay_report) (struct avdtp *session, struct avdtp_local_sep *lsep, struct avdtp_stream *stream, struct avdtp_error *err, void *user_data); }; /* * Callbacks for indicating when we received a new command. The return value * indicates whether the command should be rejected or accepted */ struct avdtp_sep_ind { gboolean (*get_capability) (struct avdtp *session, struct avdtp_local_sep *sep, GSList **caps, uint8_t *err, void *user_data); gboolean (*set_configuration) (struct avdtp *session, struct avdtp_local_sep *lsep, struct avdtp_stream *stream, GSList *caps, avdtp_set_configuration_cb cb, void *user_data); gboolean (*get_configuration) (struct avdtp *session, struct avdtp_local_sep *lsep, uint8_t *err, void *user_data); gboolean (*open) (struct avdtp *session, struct avdtp_local_sep *lsep, struct avdtp_stream *stream, uint8_t *err, void *user_data); gboolean (*start) (struct avdtp *session, struct avdtp_local_sep *lsep, struct avdtp_stream *stream, uint8_t *err, void *user_data); gboolean (*suspend) (struct avdtp *session, struct avdtp_local_sep *sep, struct avdtp_stream *stream, uint8_t *err, void *user_data); gboolean (*close) (struct avdtp *session, struct avdtp_local_sep *sep, struct avdtp_stream *stream, uint8_t *err, void *user_data); void (*abort) (struct avdtp *session, struct avdtp_local_sep *sep, struct avdtp_stream *stream, uint8_t *err, void *user_data); gboolean (*reconfigure) (struct avdtp *session, struct avdtp_local_sep *lsep, uint8_t *err, void *user_data); gboolean (*delayreport) (struct avdtp *session, struct avdtp_local_sep *lsep, uint8_t rseid, uint16_t delay, uint8_t *err, void *user_data); }; typedef void (*avdtp_discover_cb_t) (struct avdtp *session, GSList *seps, struct avdtp_error *err, void *user_data); typedef void (*avdtp_disconnect_cb_t) (void *user_data); struct avdtp *avdtp_new(int fd, size_t imtu, size_t omtu, uint16_t version, struct queue *lseps); unsigned int avdtp_add_disconnect_cb(struct avdtp *session, avdtp_disconnect_cb_t cb, void *user_data); gboolean avdtp_remove_disconnect_cb(struct avdtp *session, unsigned int id); void avdtp_shutdown(struct avdtp *session); void avdtp_unref(struct avdtp *session); struct avdtp *avdtp_ref(struct avdtp *session); struct avdtp_service_capability *avdtp_service_cap_new(uint8_t category, const void *data, int size); struct avdtp_service_capability *avdtp_get_codec(struct avdtp_remote_sep *sep); int avdtp_discover(struct avdtp *session, avdtp_discover_cb_t cb, void *user_data); gboolean avdtp_has_stream(struct avdtp *session, struct avdtp_stream *stream); unsigned int avdtp_stream_add_cb(struct avdtp *session, struct avdtp_stream *stream, avdtp_stream_state_cb cb, void *data); gboolean avdtp_stream_remove_cb(struct avdtp *session, struct avdtp_stream *stream, unsigned int id); gboolean avdtp_stream_set_transport(struct avdtp_stream *stream, int fd, size_t imtu, size_t omtu); gboolean avdtp_stream_get_transport(struct avdtp_stream *stream, int *sock, uint16_t *imtu, uint16_t *omtu, GSList **caps); struct avdtp_service_capability *avdtp_stream_get_codec( struct avdtp_stream *stream); gboolean avdtp_stream_has_capabilities(struct avdtp_stream *stream, GSList *caps); struct avdtp_remote_sep *avdtp_stream_get_remote_sep( struct avdtp_stream *stream); int avdtp_set_configuration(struct avdtp *session, struct avdtp_remote_sep *rsep, struct avdtp_local_sep *lsep, GSList *caps, struct avdtp_stream **stream); int avdtp_get_configuration(struct avdtp *session, struct avdtp_stream *stream); int avdtp_open(struct avdtp *session, struct avdtp_stream *stream); int avdtp_start(struct avdtp *session, struct avdtp_stream *stream); int avdtp_suspend(struct avdtp *session, struct avdtp_stream *stream); int avdtp_close(struct avdtp *session, struct avdtp_stream *stream, gboolean immediate); int avdtp_abort(struct avdtp *session, struct avdtp_stream *stream); int avdtp_delay_report(struct avdtp *session, struct avdtp_stream *stream, uint16_t delay); struct avdtp_local_sep *avdtp_register_sep(struct queue *lseps, uint8_t type, uint8_t media_type, uint8_t codec_type, gboolean delay_reporting, struct avdtp_sep_ind *ind, struct avdtp_sep_cfm *cfm, void *user_data); void avdtp_sep_set_vendor_codec(struct avdtp_local_sep *sep, uint32_t vendor_id, uint16_t codec_id); /* Find a matching pair of local and remote SEP ID's */ struct avdtp_remote_sep *avdtp_find_remote_sep(struct avdtp *session, struct avdtp_local_sep *lsep); int avdtp_unregister_sep(struct queue *lseps, struct avdtp_local_sep *sep); avdtp_state_t avdtp_sep_get_state(struct avdtp_local_sep *sep); void avdtp_error_init(struct avdtp_error *err, uint8_t type, int id); const char *avdtp_strerror(struct avdtp_error *err); uint8_t avdtp_error_category(struct avdtp_error *err); int avdtp_error_error_code(struct avdtp_error *err); int avdtp_error_posix_errno(struct avdtp_error *err); bluez-5.82/android/PaxHeaders/utils.h0000644000000000000000000000005014015011623014570 xustar0020 atime=1743516864 20 ctime=1743591278 bluez-5.82/android/utils.h0000644000000000000000000000145014015011623014251 0ustar00rootroot/* SPDX-License-Identifier: LGPL-2.1-or-later */ /* * * BlueZ - Bluetooth protocol stack for Linux * * Copyright (C) 2013-2014 Intel Corporation. All rights reserved. * * */ static inline void android2bdaddr(const void *buf, bdaddr_t *dst) { baswap(dst, buf); } static inline void bdaddr2android(const bdaddr_t *src, void *buf) { baswap(buf, src); } const char *bt_config_get_vendor(void); const char *bt_config_get_model(void); const char *bt_config_get_name(void); const char *bt_config_get_serial(void); const char *bt_config_get_fw_rev(void); const char *bt_config_get_hw_rev(void); uint64_t bt_config_get_system_id(void); uint16_t bt_config_get_pnp_source(void); uint16_t bt_config_get_pnp_vendor(void); uint16_t bt_config_get_pnp_product(void); uint16_t bt_config_get_pnp_version(void); bluez-5.82/android/PaxHeaders/pics-hogp.txt0000644000000000000000000000005012537515745015736 xustar0020 atime=1743516876 20 ctime=1743591289 bluez-5.82/android/pics-hogp.txt0000644000000000000000000004624612537515745015433 0ustar00rootrootHOGP PICS for the PTS tool. PTS version: 6.1 * - different than PTS defaults # - not yet implemented/supported M - mandatory O - optional Profile Roles ------------------------------------------------------------------------------- Parameter Name Selected Description ------------------------------------------------------------------------------- TSPC_HOGP_1_1 False (*) HID Device (Server) (C.1) TSPC_HOGP_1_2 True Report Host (Client) (C.1) TSPC_HOGP_1_3 False (*) Boot Host (Client) (C.1) ------------------------------------------------------------------------------- C.1: Mandatory to support at least one of TSPC_HOGP_1_1 or TSPC_HOGP_1_2 or TSPC_HOGP_1_3. ------------------------------------------------------------------------------- Transport ------------------------------------------------------------------------------- Parameter Name Selected Description ------------------------------------------------------------------------------- TSPC_HOGP_2_1 False (*) Profile supported over BR/EDR (C.1) TSPC_HOGP_2_2 True Profile supported over LE (M) ------------------------------------------------------------------------------- C.1: Excluded for this profile. ------------------------------------------------------------------------------- Services - HID Device ------------------------------------------------------------------------------- Parameter Name Selected Description ------------------------------------------------------------------------------- TSPC_HOGP_3_1 False (*) Implements HID Service (M.1) TSPC_HOGP_3_2 False (*) Multiple Service instances - HID Service (O) TSPC_HOGP_3_3 False (*) Implements Battery Service (M.1) TSPC_HOGP_3_4 False (*) Implements Device Information Service (M.1) TSPC_HOGP_3_5 False (*) Implements Scan Parameters Service (O) ------------------------------------------------------------------------------- M.1: Mandatory if TSPC_HOGP_1_1 selected ------------------------------------------------------------------------------- Features - HID Device ------------------------------------------------------------------------------- Parameter Name Selected Description ------------------------------------------------------------------------------- TSPC_HOGP_4_1 False (*) Include HID Service UUID in AD in GAP Discoverable Mode (O) TSPC_HOGP_4_2 False (*) Include Local Name in AD or Scan Response Data (O) TSPC_HOGP_4_3 False (*) Include Appearance in AD or Scan Response Data (O) TSPC_HOGP_4_4 False (*) Support Device Information Service characteristic: PnP ID (M) TSPC_HOGP_4_5 False (*) Report characteristic (C.1) TSPC_HOGP_4_6 False (*) Non-HID Service characteristic described within Report Map characteristic (C.1) TSPC_HOGP_4_7 False (*) External Report Reference characteristic descriptor for Report Map characteristic (C.2) ------------------------------------------------------------------------------- C.1: Mandatory to support at least one of these features. C.2: Mandatory if TSPC_HOGP_4_6 is supported, else excluded. ------------------------------------------------------------------------------- GAP Requirements - HID Device ------------------------------------------------------------------------------- Parameter Name Selected Description ------------------------------------------------------------------------------- TSPC_HOGP_5_1 False (*) Peripheral (M.1) TSPC_HOGP_5_2 False (*) Directed Connectable Mode (O) TSPC_HOGP_5_3 False (*) Undirected Connectable Mode (M.1) TSPC_HOGP_5_4 False (*) Bondable mode (peripheral) (M.1) TSPC_HOGP_5_5 False (*) Bonding procedure (peripheral) (M.1) TSPC_HOGP_5_6 False (*) LE Security Mode 1 (peripheral) (M.1) ------------------------------------------------------------------------------- M.1: Mandatory if TSPC_HOGP_1_1 selected ------------------------------------------------------------------------------- SM Requirements - HID Device ------------------------------------------------------------------------------- Parameter Name Selected Description ------------------------------------------------------------------------------- TSPC_HOGP_6_1 False (*) No security (LE Security Level 1) (M.1) TSPC_HOGP_6_2 False (*) Unauthenticated no MITM protection (LE Security Level 2, Just Works) (M.1) TSPC_HOGP_6_3 False (*) Authenticated MITM protection (LE Security Level 3, Passkey) (O) ------------------------------------------------------------------------------- M.1: Mandatory if TSPC_HOGP_1_1 selected ------------------------------------------------------------------------------- Client Services Support - Report Host ------------------------------------------------------------------------------- Parameter Name Selected Description ------------------------------------------------------------------------------- TSPC_HOGP_7_1 True HID Service (M.1) TSPC_HOGP_7_2 True Battery Service (M.1) TSPC_HOGP_7_3 True Device Information Service (M.1) TSPC_HOGP_7_4 True Scan Parameters Service (M.1) ------------------------------------------------------------------------------- M.1: Mandatory if TSPC_HOGP_1_2 selected ------------------------------------------------------------------------------- GATT based Profile Support - Report Host ------------------------------------------------------------------------------- Parameter Name Selected Description ------------------------------------------------------------------------------- TSPC_HOGP_7a_1 True Scan Parameters Profile (M.1) ------------------------------------------------------------------------------- M.1: Mandatory if TSPC_HOGP_1_2 selected ------------------------------------------------------------------------------- Client Service Support - Boot Host ------------------------------------------------------------------------------- Parameter Name Selected Description ------------------------------------------------------------------------------- TSPC_HOGP_8_1 False (*) HID Service (M.1) TSPC_HOGP_8_2 False (*) Battery Service (O) TSPC_HOGP_8_3 False (*) Device Information Service (O) ------------------------------------------------------------------------------- M.1: Mandatory if TSPC_HOGP_1_3 selected ------------------------------------------------------------------------------- Discover Services & Characteristics - Report Host ------------------------------------------------------------------------------- Parameter Name Selected Description ------------------------------------------------------------------------------- TSPC_HOGP_9_1 True Discover HID Service (M.1) TSPC_HOGP_9_2 True Discover Battery Service (M.1) TSPC_HOGP_9_3 True Discover Device Information Service (M.1) TSPC_HOGP_9_4 True Discover Scan Parameters Service (M.1) TSPC_HOGP_9_5 True Discover HID Service characteristic: Report Map (M.1) TSPC_HOGP_9_6 True Discover HID Service characteristic: Report Map - External Report Reference characteristic descriptor (M.1) TSPC_HOGP_9_7 True Discover HID Service characteristic: Report (M.1) TSPC_HOGP_9_8 True Discover HID Service characteristic: Report - Client Characteristic Configuration characteristic descriptor (M.1) TSPC_HOGP_9_9 True Discover HID Service characteristic: Report - Report Reference characteristic descriptor (M.1) TSPC_HOGP_9_10 True Discover HID Service characteristic: HID Information (M.1) TSPC_HOGP_9_11 True Discover HID Service characteristic: HID Control Point (M.1) TSPC_HOGP_9_12 True Discover HID Service characteristic: Protocol Mode (O) TSPC_HOGP_9_13 True Discover Battery Service characteristic: Battery Level (M.1) TSPC_HOGP_9_14 True Discover Battery Service characteristic: Battery Level - Client Characteristic Configuration characteristic descriptor (M.1) TSPC_HOGP_9_15 True Discover Device Information Service characteristic: PnP ID (M.1) TSPC_HOGP_9_16 True Discover non-HID Service characteristic: Report Reference characteristic descriptor (M.1) ------------------------------------------------------------------------------- M.1: Mandatory if TSPC_HOGP_1_2 selected ------------------------------------------------------------------------------- Discover Services & Characteristics - Boot Host ------------------------------------------------------------------------------- Parameter Name Selected Description ------------------------------------------------------------------------------- TSPC_HOGP_10_1 False (*) Discover HID Service (M.1) TSPC_HOGP_10_2 False (*) Discover Battery Service (O) TSPC_HOGP_10_3 False (*) Discover Device Information Service (O) TSPC_HOGP_10_4 False (*) Discover HID Service characteristic: Protocol Mode (M.1) TSPC_HOGP_10_5 False (*) Discover HID Service characteristic: Boot Keyboard Input Report (C.1, C.2) TSPC_HOGP_10_6 False (*) Discover HID Service characteristic: Boot Keyboard Input Report - Client Characteristic Configuration characteristic descriptor (C.3) TSPC_HOGP_10_7 False (*) Discover HID Service characteristic: Boot Keyboard Output Report (C.1, C.2) TSPC_HOGP_10_8 False (*) Discover HID Service characteristic: Boot Mouse Input Report (C.1) TSPC_HOGP_10_9 False (*) Discover HID Service characteristic: Boot Mouse Input Report - Client Characteristic Configuration characteristic descriptor (C.4) TSPC_HOGP_10_10 False (*) Discover Battery Service characteristic: Battery Level (O) TSPC_HOGP_10_11 False (*) Discover Battery Service characteristic: Battery Level - Client Characteristic Configuration characteristic descriptor (O) TSPC_HOGP_10_12 False (*) Discover Device Information Service characteristic: PnP ID (O) ------------------------------------------------------------------------------- M.1: Mandatory if TSPC_HOGP_1_3 selected C.1: Mandatory to support at least one of TSPC_HOGP_10_5, TSPC_HOGP_10_7, or TSPC_HOGP_10_8. C.2: If one of TSPC_HOGP_10_5 or TSPC_HOGP_10_7 is supported, both shall be supported. C.3: Mandatory to support if TSPC_HOGP_10_5 is supported, else excluded. C.4: Mandatory to support if TSPC_HOGP_10_8 is supported, else excluded. ------------------------------------------------------------------------------- Features - Report Host ------------------------------------------------------------------------------- Parameter Name Selected Description ------------------------------------------------------------------------------- TSPC_HOGP_11_1 True Read Report Map characteristic (M.1) TSPC_HOGP_11_2 True Read Report Map characteristic: External Report Reference characteristic descriptor (M.1) TSPC_HOGP_11_3 True Read Report characteristic: Report Type: Input Report (M.1) TSPC_HOGP_11_4 True Write Report characteristic: Report Type: Input Report (M.1) TSPC_HOGP_11_5 True Read Report characteristic: Report Type: Output Report (M.1) TSPC_HOGP_11_6 True Write HID Report characteristic: Report Type: Output Report (M.1) TSPC_HOGP_11_7 True Read HID Report characteristic: Report Type: Feature Report (M.1) TSPC_HOGP_11_8 True Write HID Report characteristic: Report Type: Feature Report (M.1) TSPC_HOGP_11_9 True Read Report characteristic: Report Reference characteristic descriptor (M.1) TSPC_HOGP_11_10 True Read Report characteristic: Input Report: Client Characteristic Configuration characteristic descriptor (M.1) TSPC_HOGP_11_11 True Report characteristic configuration with 0x0001 (M.1) TSPC_HOGP_11_11a True Report characteristic configuration with 0x0000 (O) TSPC_HOGP_11_12 True Read HID Information characteristic (M.1) TSPC_HOGP_11_13 False (*) Suspend State (O) TSPC_HOGP_11_14 False (*) Exit Suspend State (C.1) TSPC_HOGP_11_15 False (*) Write HID Control Point characteristic: Suspend command (C.1) TSPC_HOGP_11_16 False (*) Write HID Control Point characteristic: Exit Suspend command (C.1) TSPC_HOGP_11_17 False (*) Read Protocol Mode characteristic: Get Protocol command (O) TSPC_HOGP_11_18 False (*) Write Protocol Mode characteristic: Set Report Protocol Mode command (O) TSPC_HOGP_11_19 True Read Battery Level characteristic (M.1) TSPC_HOGP_11_20 True Read Battery Level characteristic: Client Characteristic Configuration characteristic descriptor (M.1) TSPC_HOGP_11_21 True Battery Level characteristic configuration with 0x0000 0r 0x0001 (M.1) TSPC_HOGP_11_22 True Read non-HID Service characteristic: Report Reference characteristic descriptor (M.1) TSPC_HOGP_11_23 True Read PnP ID characteristic (M.1) TSPC_HOGP_11_24 True Notify Report characteristic (M.1) TSPC_HOGP_11_25 True Notify Battery Level characteristic (M.1) ------------------------------------------------------------------------------- M.1: Mandatory if TSPC_HOGP_1_2 selected C.1: Mandatory to support if TSPC_HOGP_11_13 is supported, else excluded. ------------------------------------------------------------------------------- Features - Boot Host ------------------------------------------------------------------------------- Parameter Name Selected Description ------------------------------------------------------------------------------- TSPC_HOGP_12_1 False (*) Read Protocol Mode characteristic: Get Protocol Mode command (M.1) TSPC_HOGP_12_2 False (*) Write Protocol Mode characteristic: Set Boot Protocol Mode command (M.1) TSPC_HOGP_12_3 False (*) Read HID Service characteristic: Boot Keyboard Input Report (C.1) TSPC_HOGP_12_4 False (*) Write HID Service characteristic: Boot Keyboard Input Report (C.1) TSPC_HOGP_12_5 False (*) Read Client Characteristic Configuration characteristic descriptor for Boot Keyboard Input Report (C.1) TSPC_HOGP_12_6 False (*) Boot Keyboard Input Report characteristic: configuration with 0x0000 or 0x0001 (C.1) TSPC_HOGP_12_7 False (*) Read HID Service characteristic: Boot Keyboard Output Report (C.1) TSPC_HOGP_12_8 False (*) Write HID Service characteristic: Boot Keyboard Output Report (C.1) TSPC_HOGP_12_9 False (*) Read HID Service characteristic: Boot Mouse Input Report (C.2) TSPC_HOGP_12_10 False (*) Write HID Service characteristic: Boot Mouse Input Report (C.2) TSPC_HOGP_12_11 False (*) Read Client Characteristic Configuration characteristic descriptor for Boot Mouse Input Report (C.2) TSPC_HOGP_12_12 False (*) Boot Mouse Input Report characteristic: configuration with 0x0000 or 0x0001 (C.2) TSPC_HOGP_12_13 False (*) Notify Boot Keyboard Input Report characteristic (C.1) TSPC_HOGP_12_14 False (*) Notify Boot Mouse Input Report characteristic (C.2) TSPC_HOGP_12_15 False (*) Read Battery Level characteristic (O) TSPC_HOGP_12_16 False (*) Read Battery Level characteristic: Client Characteristic Configuration characteristic descriptor (O) TSPC_HOGP_12_17 False (*) Battery Level characteristic: configuration with 0x0000 or 0x0001 (O) TSPC_HOGP_12_18 False (*) Notify Battery Level characteristic (O) TSPC_HOGP_12_19 False (*) Read PnP ID characteristic (O) ------------------------------------------------------------------------------- M.1: Mandatory if TSPC_HOGP_1_3 selected C.1: Mandatory to support if TSPC_HOGP_10_5 or TSPC_HOGP_10_7 is supported, else excluded. C.2: Mandatory to support if TSPC_HOGP_10_8 is supported, else excluded. ------------------------------------------------------------------------------- GATT Requirements - Report Host ------------------------------------------------------------------------------- Parameter Name Selected Description ------------------------------------------------------------------------------- TSPC_HOGP_13_1 True Attribute Protocol supported over LE Transport (M.1) TSPC_HOGP_13_2 True Generic Attribute Profile Client (M.1) TSPC_HOGP_13_3 True Discover All Primary Services (C.1) TSPC_HOGP_13_4 False (*) Discover Primary Services by Service UUID (C.1) TSPC_HOGP_13_5 True Find Included Services (M.1) TSPC_HOGP_13_6 True Discover All Characteristics of a Service (C.2) TSPC_HOGP_13_7 False (*) Discover Characteristics by UUID (C.2) TSPC_HOGP_13_8 True Discover All Characteristic Descriptors (M.1) TSPC_HOGP_13_9 True Read Characteristic Value (M.1) TSPC_HOGP_13_10 True Read using Characteristic UUID (O) TSPC_HOGP_13_11 True Read Long Characteristic Value (M.1) TSPC_HOGP_13_12 True Read Characteristic Descriptors (M.1) TSPC_HOGP_13_13 True Write without Response (M.1) TSPC_HOGP_13_14 True Write Characteristic Value (M.1) TSPC_HOGP_13_15 True Write Characteristic Descriptors (M.1) TSPC_HOGP_13_16 True Notifications (M.1) TSPC_HOGP_13_17 True Exchange MTU (M.1) ------------------------------------------------------------------------------- M.1: Mandatory if TSPC_HOGP_1_2 selected C.1: Mandatory to support at least one of these features. C.2: Mandatory to support at least one of these features. ------------------------------------------------------------------------------- GATT Requirements - Boot Host ------------------------------------------------------------------------------- Parameter Name Selected Description ------------------------------------------------------------------------------- TSPC_HOGP_14_1 False (*) Attribute Protocol supported over LE Transport (M.1) TSPC_HOGP_14_2 False (*) Generic Attribute Profile Client (M.1) TSPC_HOGP_14_3 False (*) Discover All Primary Services (C.1) TSPC_HOGP_14_4 False (*) Discover Primary Services by Service UUID (C.1) TSPC_HOGP_14_5 False (*) Discover All Characteristics of a Service (O) TSPC_HOGP_14_6 False (*) Discover Characteristics by UUID (O) TSPC_HOGP_14_7 False (*) Discover All Characteristic Descriptors (M.1) TSPC_HOGP_14_8 False (*) Read Characteristic Value (M.1) TSPC_HOGP_14_9 False (*) Read using Characteristic UUID (M.1) TSPC_HOGP_14_10 False (*) Read Characteristic Descriptors (M.1) TSPC_HOGP_14_11 False (*) Write without Response (M.1) TSPC_HOGP_14_12 False (*) Write Characteristic Value (M.1) TSPC_HOGP_14_13 False (*) Write Characteristic Descriptors (M.1) TSPC_HOGP_14_14 False (*) Notifications (M.1) ------------------------------------------------------------------------------- M.1: Mandatory if TSPC_HOGP_1_3 selected ------------------------------------------------------------------------------- GAP Requirements - HID Host ------------------------------------------------------------------------------- Parameter Name Selected Description ------------------------------------------------------------------------------- TSPC_HOGP_15_1 True Central (M.1) TSPC_HOGP_15_2 True LE Security Mode 1 (central) (M.1) ------------------------------------------------------------------------------- M.1: Mandatory if TSPC_HOGP_1_2 or TSPC_HOGP_1_3 is selected ------------------------------------------------------------------------------- SM Requirements - HID Host ------------------------------------------------------------------------------- Parameter Name Selected Description ------------------------------------------------------------------------------- TSPC_HOGP_16_1 True No Security Requirements (LE Security Level 1, No Security) (M.1) TSPC_HOGP_16_2 True Unauthenticated no MITM protection (LE Security Level 2, Just Works) (M.1) TSPC_HOGP_16_3 True Authenticated MITM protection (LE Security Level 3, Passkey) (O) ------------------------------------------------------------------------------- M.1: Mandatory if TSPC_HOGP_1_2 or TSPC_HOGP_1_3 is selected ------------------------------------------------------------------------------- bluez-5.82/android/PaxHeaders/pixit-map.txt0000644000000000000000000000005012537515745015755 xustar0020 atime=1743516876 20 ctime=1743591289 bluez-5.82/android/pixit-map.txt0000644000000000000000000000302712537515745015440 0ustar00rootrootMAP PIXIT for the PTS tool. PTS version: 6.1 * - different than PTS defaults & - should be set to IUT Bluetooth address # - should be set to tester's phone number $ - should be set to IUT e-mail address Required PIXIT settings ------------------------------------------------------------------------------- Parameter Name Value ------------------------------------------------------------------------------- TSPX_auth_password 0000 TSPX_auth_user_id PTS TSPX_bd_addr_iut 112233445566 (*&) TSPX_client_class_of_device 100204 TSPX_delete_link_key FALSE TSPX_get_object_name put.gif TSPX_initial_path TSPX_l2cap_psm 1001 TSPX_no_confirmations FALSE TSPX_pin_code 0000 TSPX_rfcomm_channel 8 TSPX_secure_simple_pairing_pass_key_confirmation FALSE TSPX_security_enabled TRUE TSPX_server_class_of_device 100204 TSPX_time_guard 300000 TSPX_use_implicit_send TRUE TSPX_Message_Access_rfcomm_channel 1 TSPX_Message_Notification_rfcomm_channel 2 TSPX_SPP_rfcomm_channel 03 TSPX_filter_period_begin 20100101T000000 TSPX_filter_period_end 20111231T125959 TSPX_filter_recipient PTS TSPX_filter_originator PTS TSPX_default_message_upload_folder_in_msg draft TSPX_default_test_folder_in_msg inbox TSPX_message_notification_l2cap_psm 1003 TSPX_message_notification_rfcomm_channel 9 TSPX_upload_msg_phonenumber 123456789 (#) TSPX_upload_msg_emailaddress IUT-email ($) TSPX_Automation FALSE ------------------------------------------------------------------------------- bluez-5.82/android/PaxHeaders/pixit-dis.txt0000644000000000000000000000005012537515745015757 xustar0020 atime=1743516876 20 ctime=1743591289 bluez-5.82/android/pixit-dis.txt0000644000000000000000000000161412537515745015442 0ustar00rootrootDIS PIXIT for the PTS tool. PTS version: 6.1 * - different than PTS defaults & - should be set to IUT Bluetooth address Required PIXIT settings ------------------------------------------------------------------------------- Parameter Name Value ------------------------------------------------------------------------------- TSPX_bd_addr_iut 112233445566 (*&) TSPX_time_guard 180000 TSPX_use_implicit_send TRUE TSPX_tester_database_file C:/Program Files/... TSPX_mtu_size 23 TSPX_secure_simple_pairing_pass_key_confirmation FALSE TSPX_delete_link_key FALSE TSPX_pin_code 0000 TSPX_use_dynamic_pin FALSE TSPX_delete_ltk FALSE TSPX_security_enabled TRUE (*) TSPX_iut_setup_att_over_br_edr FALSE TSPX_tester_appearance 0000 TSPX_iut_use_resolvable_random_address FALSE ------------------------------------------------------------------------------- bluez-5.82/android/PaxHeaders/pts-hfp.txt0000644000000000000000000000005012537515745015426 xustar0020 atime=1743516876 20 ctime=1743591289 bluez-5.82/android/pts-hfp.txt0000644000000000000000000001301312537515745015105 0ustar00rootrootPTS test results for HFP PTS version: 6.1 Tested: 14-May-2015 Android version: 5.1 Results: PASS test passed FAIL test failed INC test is inconclusive N/A test is disabled due to PICS setup ------------------------------------------------------------------------------- Test Name Result Notes ------------------------------------------------------------------------------- TC_AG_OOR_BV_01_I PASS TC_AG_OOR_BV_02_I PASS TC_AG_TRS_BV_01_I PASS TC_AG_PSI_BV_01_I PASS TC_AG_PSI_BV_02_I N/A TC_AG_PSI_BV_03_I PASS TC_AG_PSI_BV_04_I PASS TC_AG_PSI_BV_05_I PASS TC_AG_ACS_BV_02_I N/A TC_AG_ACS_BV_04_I PASS TC_AG_ACS_BV_06_I N/A TC_AG_ACS_BV_08_I PASS TC_AG_ACS_BV_10_I N/A TC_AG_ACS_BV_11_I PASS TC_AG_ACS_BI_14_I PASS TC_AG_ACS_BI_16_I N/A TC_AG_ACR_BV_01_I PASS TC_AG_ACR_BV_02_I PASS TC_AG_CLI_BV_01_I PASS TC_AG_ICA_BV_01_I N/A TC_AG_ICA_BV_02_I N/A TC_AG_ICA_BV_04_I PASS TC_AG_ICA_BV_05_I N/A TC_AG_ICA_BV_06_I PASS TC_AG_ICR_BV_01_I PASS TC_AG_ICR_BV_02_I PASS TC_AG_TCA_BV_01_I PASS TC_AG_TCA_BV_02_I PASS TC_AG_TCA_BV_03_I PASS TC_AG_TCA_BV_04_I PASS TC_AG_TCA_BV_05_I PASS TC_AG_ATH_BV_03_I PASS TC_AG_ATH_BV_04_I PASS TC_AG_ATH_BV_05_I PASS TC_AG_ATH_BV_06_I PASS TC_AG_ATA_BV_01_I PASS TC_AG_ATA_BV_02_I PASS TC_AG_OCN_BV_01_I PASS TC_AG_OCM_BV_01_I PASS TC_AG_OCM_BV_02_I PASS TC_AG_OCL_BV_01_I PASS TC_AG_OCL_BV_02_I PASS TC_AG_TWC_BV_01_I PASS TC_AG_TWC_BV_02_I PASS TC_AG_TWC_BV_03_I PASS TC_AG_TWC_BV_04_I PASS TC_AG_TWC_BV_05_I PASS TC_AG_TWC_BV_06_I N/A TC_AG_CIT_BV_01_I PASS TC_AG_ENO_BV_01_I PASS TC_AG_ENO_BV_02_I N/A TC_AG_VRA_BV_01_I PASS TC_AG_VRA_BV_02_I PASS TC_AG_VRA_BI_01_I PASS TC_AG_VRD_BV_01_I N/A TC_AG_VTG_BV_01_I N/A TC_AG_TDC_BV_01_I PASS TC_AG_RSV_BV_01_I PASS TC_AG_RSV_BV_02_I PASS TC_AG_RSV_BV_03_I PASS TC_AG_RMV_BV_01_I N/A TC_AG_RMV_BV_02_I N/A TC_AG_RMV_BV_03_I N/A TC_AG_ECS_BV_01_I PASS TC_AG_ECS_BV_02_I PASS TC_AG_ECS_BV_03_I PASS TC_AG_ECC_BV_01_I N/A TC_AG_ECC_BV_02_I N/A TC_AG_ECC_BI_03_I PASS TC_AG_ECC_BI_04_I PASS TC_AG_RHH_BV_01_I N/A TC_AG_RHH_BV_02_I N/A TC_AG_RHH_BV_03_I N/A TC_AG_RHH_BV_04_I N/A TC_AG_RHH_BV_05_I N/A TC_AG_RHH_BV_06_I N/A TC_AG_RHH_BV_07_I N/A TC_AG_RHH_BV_08_I N/A TC_AG_NUM_BV_01_I PASS TC_AG_SLC_BV_01_C PASS TC_AG_SLC_BV_02_C PASS TC_AG_SLC_BV_03_C PASS TC_AG_SLC_BV_04_C PASS TC_AG_SLC_BV_05_I PASS TC_AG_SLC_BV_06_I PASS TC_AG_SLC_BV_07_I PASS TC_AG_SLC_BV_09_I N/A TC_AG_SLC_BV_10_I N/A TC_AG_ACC_BV_08_I PASS TC_AG_ACC_BV_09_I PASS TC_AG_ACC_BV_10_I PASS TC_AG_ACC_BV_11_I PASS TC_AG_ACC_BI_12_I PASS TC_AG_ACC_BI_13_I PASS TC_AG_ACC_BI_14_I PASS TC_AG_ACC_BV_15_I PASS TC_AG_WBS_BV_01_I PASS TC_AG_DIS_BV_01_I PASS TC_AG_SDP_BV_01_I PASS TC_AG_IIA_BV_01_I PASS TC_AG_IIA_BV_02_I PASS TC_AG_IIA_BV_03_I N/A TC_AG_IIA_BV_05_I PASS TC_AG_IID_BV_01_I PASS TC_AG_IID_BV_02_I N/A TC_AG_IID_BV_03_I PASS TC_AG_IID_BV_04_I PASS TC_AG_IIC_BV_01_I PASS TC_AG_IIC_BV_02_I PASS TC_AG_IIC_BV_03_I PASS TC_AG_HFI_BV_02_I N/A TC_AG_HFI_BV_03_I N/A TC_HF_OOR_BV_01_I N/A TC_HF_OOR_BV_02_I N/A TC_HF_TRS_BV_01_I N/A TC_HF_PSI_BV_01_I N/A TC_HF_PSI_BV_02_I N/A TC_HF_PSI_BV_03_I N/A TC_HF_PSI_BV_04_I N/A TC_HF_ACS_BV_01_I N/A TC_HF_ACS_BV_03_I N/A TC_HF_ACS_BV_05_I N/A TC_HF_ACS_BV_07_I N/A TC_HF_ACS_BV_09_I N/A TC_HF_ACS_BV_12_I N/A TC_HF_ACS_BI_13_I N/A TC_HF_ACR_BV_01_I N/A TC_HF_ACR_BV_02_I N/A TC_HF_CLI_BV_01_I N/A TC_HF_ICA_BV_01_I N/A TC_HF_ICA_BV_02_I N/A TC_HF_ICA_BV_03_I N/A TC_HF_ICA_BV_04_I N/A TC_HF_ICA_BV_05_I N/A TC_HF_ICA_BV_06_I N/A TC_HF_ICA_BV_07_I N/A TC_HF_ICR_BV_01_I N/A TC_HF_ICR_BV_02_I N/A TC_HF_TCA_BV_01_I N/A TC_HF_TCA_BV_02_I N/A TC_HF_TCA_BV_03_I N/A TC_HF_TCA_BV_04_I N/A TC_HF_ATH_BV_03_I N/A TC_HF_ATH_BV_04_I N/A TC_HF_ATH_BV_05_I N/A TC_HF_ATH_BV_06_I N/A TC_HF_ATH_BV_09_I N/A TC_HF_ATA_BV_01_I N/A TC_HF_ATA_BV_02_I N/A TC_HF_ATA_BV_03_I N/A TC_HF_OCN_BV_01_I N/A TC_HF_OCM_BV_01_I N/A TC_HF_OCM_BV_02_I N/A TC_HF_OCL_BV_01_I N/A TC_HF_OCL_BV_02_I N/A TC_HF_TWC_BV_01_I N/A TC_HF_TWC_BV_02_I N/A TC_HF_TWC_BV_03_I N/A TC_HF_TWC_BV_04_I N/A TC_HF_TWC_BV_05_I N/A TC_HF_TWC_BV_06_I N/A TC_HF_CIT_BV_01_I N/A TC_HF_ENO_BV_01_I N/A TC_HF_VRA_BV_01_I N/A TC_HF_VRA_BV_02_I N/A TC_HF_VRA_BV_03_I N/A TC_HF_VRD_BV_01_I N/A TC_HF_VTG_BV_01_I N/A TC_HF_TDC_BV_01_I N/A TC_HF_RSV_BV_01_I N/A TC_HF_RSV_BV_02_I N/A TC_HF_RSV_BV_03_I N/A TC_HF_RMV_BV_01_I N/A TC_HF_RMV_BV_02_I N/A TC_HF_RMV_BV_03_I N/A TC_HF_ECS_BV_01_I N/A TC_HF_ECS_BV_02_I N/A TC_HF_ECS_BV_03_I N/A TC_HF_ECC_BV_01_I N/A TC_HF_ECC_BV_02_I N/A TC_HF_RHH_BV_01_I N/A TC_HF_RHH_BV_02_I N/A TC_HF_RHH_BV_03_I N/A TC_HF_RHH_BV_04_I N/A TC_HF_RHH_BV_05_I N/A TC_HF_RHH_BV_06_I N/A TC_HF_RHH_BV_07_I N/A TC_HF_RHH_BV_08_I N/A TC_HF_NUM_BV_01_I N/A TC_HF_NUM_BI_01_I N/A TC_HF_SLC_BV_01_C N/A TC_HF_SLC_BV_02_C N/A TC_HF_SLC_BV_03_C N/A TC_HF_SLC_BV_04_C N/A TC_HF_SLC_BV_05_I N/A TC_HF_SLC_BV_06_I N/A TC_HF_SLC_BV_08_I N/A TC_HF_ACC_BV_01_I N/A TC_HF_ACC_BV_02_I N/A TC_HF_ACC_BV_03_I N/A TC_HF_ACC_BV_04_I N/A TC_HF_ACC_BV_05_I N/A TC_HF_ACC_BV_06_I N/A TC_HF_ACC_BV_07_I N/A TC_HF_WBS_BV_02_I N/A TC_HF_WBS_BV_03_I N/A TC_HF_DIS_BV_01_I N/A TC_HF_DIS_BV_02_I N/A TC_HF_SDP_BV_01_I N/A TC_HF_SDP_BV_02_C N/A TC_HF_SDP_BV_03_C N/A TC_HF_ATAH_BV_01_I N/A TC_HF_OCA_BV_01_I N/A TC_HF_IIA_BV_04_I N/A TC_AG_COD_BV_02_I PASS TC_AG_ATAH_BV_01_I PASS TC_AG_ATA_BV_03_I PASS TC_AG_ATH_BV_09_I PASS TC_AG_SDP_BV_02_C PASS TC_AG_SDP_BV_03_C PASS TC_AG_ICA_BV_07_I PASS TC_AG_ICA_BV_08_I PASS TC_AG_ICA_BV_09_I PASS TC_AG_VRA_BV_03_I PASS TC_AG_OCA_BV_01_I PASS TC_AG_TCA_BV_06_I PASS TC_HF_ATAH_BV_03_I N/A TC_HF_ATA_BV_03_I N/A TC_HF_ATH_BV_09_I N/A TC_HF_SDP_BV_02_C N/A TC_HF_SDP_BV_03_C N/A TC_HF_DIS_BV_02_I N/A TC_HF_ICA_BV_07_I N/A TC_HF_VRA_BV_03_I N/A TC_HF_OCA_BV_01_I N/A bluez-5.82/android/PaxHeaders/README0000644000000000000000000000005012640115300014136 xustar0020 atime=1743516875 20 ctime=1743591288 bluez-5.82/android/README0000644000000000000000000004121112640115300013616 0ustar00rootrootBlueZ for Android ***************** Since Android 4.2 there exists a well standardized HAL interface that the Bluetooth stack is expected to provide and which enables the easy replacement of the stack of choice on Android. Android BlueZ is intended as a drop-in replacement to Android provided Bluetooth stack. More details about BlueZ for Android architecture and components can be found in android/hal-ipc-api.txt file. Supported Android version: 4.4 KitKat and 5.0, 5.1 Lollipop Building and running on Android =============================== Steps needed to build and run Android Open Source Project with integrated BlueZ. Build requirements ------------------ - GLib - Android 4.2 or later don't provide GLib and one must provide it in 'external/bluetooth/glib' folder of Android tree. Sample Android GLib port is available at https://github.com/bluez-android/glib - SBC - A2DP code requires SBC library (version 1.2 or higher) present in 'external/bluetooth/sbc' directory. Library is build from Android.mk provided by BlueZ. SBC code is available at git://git.kernel.org/pub/scm/bluetooth/sbc - Bionic support - Android 5.0 provides all required functionality. Running BlueZ on Android 4.4 requires backporting missing features (epoll_create1 and ppoll calls). Sample Bionic for Android 4.4 with all required features backported is available at https://github.com/bluez-android/aosp_platform_bionic Runtime requirements -------------------- BlueZ HAL library requires 'bluetoothd' and 'bluetoothd-snoop' services to be available on Android system. Some permissions settings are also required. This can be done by importing init.bluetooth.rc file in init.rc file of targeted board: import init.bluetooth.rc For convenience examples are provided at: https://github.com/bluez-android/aosp_device_lge_mako (Nexus 4) https://github.com/bluez-android/aosp_device_lge_hammerhead (Nexus 5) https://github.com/bluez-android/aosp_device_asus_flo (Nexus 7 2013) Security-Enhanced Linux in Android ---------------------------------- Since 5.0 release Android moved to full enforcement of SELinux. This requires proper policy to be provided for all BlueZ for Android services (and services interacting with BlueZ). Policies should be placed in external/selinux/ path. Required policy files are provided at: bluetoothd.te bluetoothd_snoop.te For convenience sepolicy.git with all required policies is available at: https://github.com/bluez-android/aosp_platform_external_sepolicy Downloading and building ------------------------ Building for Android requires full Android AOSP source tree. Sample Android tree with all required components present is available at https://github.com/bluez-android This tree provides support for Nexus4 (mako), Nexus 5 (hammerhead) and Nexus 7 2013 (flo, deb). Tree does not provide binary blobs needed to run Android on supported devices. Those can be obtained from https://developers.google.com/android/nexus/drivers. Binary blobs needs to be unpacked (EULA acceptance required) into 'vendor' directory of Android tree. Downloading: Android 5.0 - 'lollipop' branch Android 4.4 - 'kitkat' branch repo init -u https://github.com/bluez-android/aosp_platform_manifest \ -b lollipop repo sync Building: source build/envsetup.sh lunch aosp_-userdebug make -j8 Flashing: adb reboot bootloader fastboot flashall -w After full build is done it is possible to rebuild only BlueZ: 'cd external/bluetooth/bluez/android/' 'mm' (or 'mm -B' to force rebuilding of all files) 'adb sync' to update target device. Downloading and building for Intel devices ------------------------------------------ Sample Android tree with all required components for Intel devices based on Intel reference image (https://01.org/android-ia) can be reconstructed following instructions below. This tree provides support for Dell XPS12, Minnowboard MAX, Intel NUC, Acer Iconia W700 and other devices mentioned in: https://01.org/android-ia/guides/devices Downloading: repo init -u https://github.com/01org/android-bluez-manifest.git -b android-ia \ -m topic/bluez repo sync Building: source build/envsetup.sh lunch haswell_generic-eng make -j8 Installing: Live and Install image is $OUT/live.img Flash live.img to USB flash and boot from it. More instructions here: https://01.org/android-ia/guides/developers/build-and-install Linux Kernel requirements ------------------------- BlueZ for Android uses Linux Bluetooth subsystem and it must be enabled in kernel. Minimal required version of management interface is 1.3. This corresponds to Linux 3.9 but latest available version is recommended. Other requirements include UHID and network bridge support. Following kernel options should be enabled: CONFIG_BT CONFIG_BT_RFCOMM CONFIG_BT_RFCOMM_TTY CONFIG_BT_BNEP CONFIG_BT_BNEP_MC_FILTER CONFIG_BT_BNEP_PROTO_FILTER CONFIG_BRIDGE CONFIG_UHID CONFIG_CRYPTO_CMAC CONFIG_CRYPTO_USER_API CONFIG_CRYPTO_USER_API_HASH CONFIG_CRYPTO_USER_API_SKCIPHER Also BT chip driver needs to be enabled e.g: CONFIG_BT_HCIBTUSB If it is not possible to use new enough Linux kernel one can use updated bluetooth subsystem from Backports project. More information about Backports can be found at https://backports.wiki.kernel.org. Sample kernels using backports for running BlueZ on Android are available at https://github.com/bluez-android. Running with Valgrind --------------------- BlueZ for Android is preconfigured to be easily run under Valgrind memcheck. Appropriate configuration and required modules are automatically included when building either userdebug or eng variant of Android platform. Valgrind can be enabled in runtime by setting "persist.sys.bluetooth.valgrind" property to either literal "true" or any numeric value >0. For example: adb root adb shell setprop persist.sys.bluetooth.valgrind true After changing property value Bluetooth need to be restarted to apply changes (this can be done using UI, just disable and enable it again). Property is persistent, i.e. there's no need to enable Valgrind again after reboot. It's recommended to have unstripped libglib.so installed which will enable complete backtraces in Valgrind output. Otherwise, in many cases backtrace will break at e.g. g_free() function without prior callers. It's possible to have proper library installed automatically by appropriate entry in Android.mk, see https://github.com/bluez-android/glib for an example. When running with valgrind SElinux needs to be set into permissive mode. This can be done by executing 'setenforce 0' from root shell. Enabling BlueZ debugs --------------------- BlueZ debug logs can be enabled in runtime by setting "persist.sys.bluetooth.debug" property to either literal "true" or any numeric value >0. For example: adb root adb shell setprop persist.sys.bluetooth.debug 1 After changing property value Bluetooth needs to be restarted to apply changes. There is also a possibility to enable mgmt debug logs which also enables debugs as above. To enable it proceed in the same way as described above but use system properties called: persist.sys.bluetooth.mgmtdbg Note: Debugs are only available on NON USER build variants Customization ------------- It is possible to customize BlueZ for Android through Android system properties. This may include enabling extra profiles or features inside HALs implementation These properties are read on Bluetooth stack startup only and require stack restart if changed. All customization properties names start with "persist.sys.bluetooth." or "ro.bluetooth." followed by specific HAL name e.g. "persist.sys.bluetooth.handsfree". If both are present "persist.sys.bluetooth." takes precedence. This allows for read only properties to be set during build leaving enough flexibility for developing or debugging purposes. This section list available customization options. Property Value Description ------------------------------------------- mode bredr Enable BlueZ in BR/EDR mode le Enable BlueZ in LE mode Enable BlueZ in default mode - enable BR/EDR/LE if available. handsfree hfp Enable Handsfree Profile (HFP) with narrowband speech only hfp_wbs Enable Handsfree Profile (HFP) with narrowband and wideband speech support Don't enable Handsfree Profile (HFP) vendor Set vendor name in DIS. If not set fallback to "ro.product.manufacturer". model Set model name used as default adapter name. If not set fallback to "ro.product.model". name Set model number in DIS. If not set fallback to "ro.product.name". serialno Set serial number in DIS. If not set fallback to "ro.serialno". systemid Set system ID in DIS. Hex string encoded uint64. pnpid PnP information used in DIS and DID profiles. Required format: "Source:VID:PID:Version". Source must be either "bluetooth" or "usb". VID, PID and Version are uint16. Version is optional. fwrev Firmware revision in DIS. If not set fallback to "ro.build.version.release". hwrew Hardware revision in DIS. If not set fallback to "ro.board.platform". Building and running on Linux ----------------------------- It is possible to build and test BlueZ for Android daemon on Linux (eg. PC). Simply follow instructions available at README file in BlueZ top directory. Android daemon binary is located at android/bluetoothd. See next section on how to test Android daemon on Linux. Testing tool ------------ BT HAL test tools located in android/haltest is provided for HAL level testing of both Android daemon and HAL library. Start it with '-n' parameter and type 'bluetooth init' in prompt to initialize HAL library. Running without parameter will make haltest try to initialize all services after start. On Android required bluetoothd service will be started automatically. On Linux it is required to start android/bluetoothd manually before init command timeout or use provided android/system-emulator, which takes care of launching daemon automatically on HAL library initialization. To deinitialize HAL library and stop daemon type 'bluetooth cleanup'. Type 'help' for more information. Tab completion is also supported. Implementation status ===================== Summary of HALs implementation status. complete - implementation is feature complete and Android Framework is able to use it normally partial - implementation is in progress and not all required features are present, Android Framework is able to use some of features initial - only initial implementations is present, Android Framework is able to initialize but most likely not able to use it not started - no implementation, Android Framework is not able to initialize it Profile ID HAL header 4.4 Status 5.0 status ------------------------------------------------------------- core bluetooth.h complete partial a2dp bt_av.h complete complete gatt bt_gatt.h complete partial bt_gatt_client.h complete partial bt_gatt_server.h complete partial handsfree bt_hf.h complete complete hidhost bt_hh.h complete complete health bt_hl.h complete complete pan bt_pan.h complete complete avrcp bt_rc.h complete complete socket bt_sock.h complete partial handsfree_client bt_hf_client.h N/A complete map_client bt_mce.h N/A complete a2dp_sink bt_av.h N/A partial avrcp_ctrl bt_rc.h N/A partial Implementation shortcomings =========================== It is possible that some of HAL functionality (although being marked as complete) is missing implementation due to reasons like feature feasibility or necessity for latest Android Framework. This sections provides list of such deficiencies. Note that HAL library is always expected to fully implement HAL API so missing implementation might happen only in daemon. HAL Bluetooth ------------- methods: dut_mode_send never called from Android Framework le_test_mode never called from Android Framework callbacks: dut_mode_recv_cb empty JNI implementation le_test_mode_cb empty JNI implementation properties: BT_PROPERTY_SERVICE_RECORD not supported for adapter, for device this property is returned as a response to get_remote_service_record call BT_PROPERTY_REMOTE_VERSION_INFO information required by this property (LMP information) are not accessible from mgmt interface, also marking this property as settable is probably a typo in HAL header HAL Socket ---------- Support only for BTSOCK_RFCOMM socket type. HAL AVRCP --------- methods: list_player_app_attr_rsp never called from Android Framework list_player_app_value_rsp never called from Android Framework get_player_app_value_rsp never called from Android Framework get_player_app_attr_text_rsp never called from Android Framework get_player_app_value_text_rsp never called from Android Framework set_player_app_value_rsp never called from Android Framework callbacks: list_player_app_attr_cb NULL JNI implementation list_player_app_values_cb NULL JNI implementation get_player_app_value_cb NULL JNI implementation get_player_app_attrs_text_cb NULL JNI implementation get_player_app_values_text_cb NULL JNI implementation set_player_app_value_cb NULL JNI implementation HAL GATT -------- methods: client->set_adv_data missing kernel support for vendor data client->connect is_direct parameter is ignored Audio SCO HAL ============= When Bluetooth chip's audio is not wired directly to device audio, Audio SCO HAL is used to enable SCO support. It needs to be loaded by AudioFlinger following audio_policy.conf configuration. Example of configuration is shown below: ... sco { outputs { sco { sampling_rates 8000|44100 channel_masks AUDIO_CHANNEL_OUT_STEREO formats AUDIO_FORMAT_PCM_16_BIT devices AUDIO_DEVICE_OUT_ALL_SCO } } inputs { sco { sampling_rates 8000|44100 channel_masks AUDIO_CHANNEL_IN_MONO formats AUDIO_FORMAT_PCM_16_BIT devices AUDIO_DEVICE_IN_BLUETOOTH_SCO_HEADSET } } } ... Known Android issues ==================== It is possible that BlueZ is triggering bugs on Android Framework that could affect qualification or user experience. This section provides list of recommended Android fixes that are not part of latest AOSP release supported by BlueZ. For Android 5.1 Lollipop: https://android-review.googlesource.com/177314 For Android 5.0 Lollipop: https://android-review.googlesource.com/99761 https://android-review.googlesource.com/100297 https://android-review.googlesource.com/102882 https://android-review.googlesource.com/132733 https://android-review.googlesource.com/132763 https://android-review.googlesource.com/177314 For Android 4.4 KitKat: https://android-review.googlesource.com/82757 https://android-review.googlesource.com/87670 https://android-review.googlesource.com/88384 https://android-review.googlesource.com/99761 https://android-review.googlesource.com/99850 https://android-review.googlesource.com/100297 https://android-review.googlesource.com/102882 https://android-review.googlesource.com/177314 Unimplemented Bluetooth features ================================ Some Bluetooth functionality require support from outside of BT stack eg. telephony stack. This sections describes profiles optional features not implemented due to lack of support in other Android subsystems or missing API in respective BT HALs. Profile Feature Comments -------------------------------------------------------- HFP Attach a phone number to AT+BINP=1 a voice tag HFP Enhanced Call Control AT+CHLD={1x,2x} HFP Explicit Call Transfer AT+CHLD=4 HFP Response and Hold AT+BTRH, +BTRH HFP In-band Ring Tone +BSIR AVRCP Player Settings HAL API present but not used AVRCP Browsing No HAL API GATT Read multiple characteristics No HAL API Reporting Bugs ============== Bugs should be reported at https://01.org/jira/browse/BA. When reporting a bug please attach logs from logcat (logcat -v time) and HCI trace. Daemon debug logs should be enabled. When reporting daemon crash please run it under valgrind if possible. For details on how to enabled debug logs and valgrind see "Enabling BlueZ debugs" section. bluez-5.82/android/PaxHeaders/handsfree-client.c0000644000000000000000000000005014131623652016650 xustar0020 atime=1743516866 20 ctime=1743591279 bluez-5.82/android/handsfree-client.c0000644000000000000000000014030414131623652016333 0ustar00rootroot// SPDX-License-Identifier: LGPL-2.1-or-later /* * * BlueZ - Bluetooth protocol stack for Linux * * Copyright (C) 2014 Intel Corporation. All rights reserved. * * */ #ifdef HAVE_CONFIG_H #include #endif #include #include #include #include #include #include "lib/bluetooth.h" #include "lib/sdp.h" #include "lib/sdp_lib.h" #include "src/sdp-client.h" #include "src/shared/hfp.h" #include "src/shared/queue.h" #include "src/shared/util.h" #include "btio/btio.h" #include "ipc.h" #include "ipc-common.h" #include "src/log.h" #include "utils.h" #include "bluetooth.h" #include "hal-msg.h" #include "handsfree-client.h" #include "sco.h" #define HFP_HF_CHANNEL 7 #define HFP_HF_FEAT_ECNR 0x00000001 #define HFP_HF_FEAT_3WAY 0x00000002 #define HFP_HF_FEAT_CLI 0x00000004 #define HFP_HF_FEAT_VR 0x00000008 #define HFP_HF_FEAT_RVC 0x00000010 #define HFP_HF_FEAT_ECS 0x00000020 #define HFP_HF_FEAT_ECC 0x00000040 #define HFP_HF_FEAT_CODEC 0x00000080 #define HFP_HF_FEAT_HF_IND 0x00000100 #define HFP_HF_FEAT_ESCO_S4_T2 0x00000200 #define HFP_AG_FEAT_3WAY 0x00000001 #define HFP_AG_FEAT_ECNR 0x00000002 #define HFP_AG_FEAT_VR 0x00000004 #define HFP_AG_FEAT_INBAND 0x00000008 #define HFP_AG_FEAT_VTAG 0x00000010 #define HFP_AG_FEAT_REJ_CALL 0x00000020 #define HFP_AG_FEAT_ECS 0x00000040 #define HFP_AG_FEAT_ECC 0x00000080 #define HFP_AG_FEAT_EXT_ERR 0x00000100 #define HFP_AG_FEAT_CODEC 0x00000200 #define HFP_HF_FEATURES (HFP_HF_FEAT_ECNR | HFP_HF_FEAT_3WAY |\ HFP_HF_FEAT_CLI | HFP_HF_FEAT_VR |\ HFP_HF_FEAT_RVC | HFP_HF_FEAT_ECS |\ HFP_HF_FEAT_ECC) #define CVSD_OFFSET 0 #define MSBC_OFFSET 1 #define CODECS_COUNT (MSBC_OFFSET + 1) #define CODEC_ID_CVSD 0x01 #define CODEC_ID_MSBC 0x02 #define MAX_NUMBER_LEN 33 #define MAX_OPERATOR_NAME_LEN 17 enum hfp_indicator { HFP_INDICATOR_SERVICE = 0, HFP_INDICATOR_CALL, HFP_INDICATOR_CALLSETUP, HFP_INDICATOR_CALLHELD, HFP_INDICATOR_SIGNAL, HFP_INDICATOR_ROAM, HFP_INDICATOR_BATTCHG, HFP_INDICATOR_LAST }; typedef void (*ciev_func_t)(uint8_t val); struct indicator { uint8_t index; uint32_t min; uint32_t max; uint32_t val; ciev_func_t cb; }; struct hfp_codec { uint8_t type; bool local_supported; bool remote_supported; }; struct device { bdaddr_t bdaddr; struct hfp_hf *hf; uint8_t state; uint8_t audio_state; uint8_t negotiated_codec; uint32_t features; struct hfp_codec codecs[2]; struct indicator ag_ind[HFP_INDICATOR_LAST]; uint32_t chld_features; }; static const struct hfp_codec codecs_defaults[] = { { CODEC_ID_CVSD, true, false}, { CODEC_ID_MSBC, false, false}, }; static bdaddr_t adapter_addr; static struct ipc *hal_ipc = NULL; static uint32_t hfp_hf_features = 0; static uint32_t hfp_hf_record_id = 0; static struct queue *devices = NULL; static GIOChannel *hfp_hf_server = NULL; static struct bt_sco *sco = NULL; static struct device *find_default_device(void) { return queue_peek_head(devices); } static bool match_by_bdaddr(const void *data, const void *user_data) { const bdaddr_t *addr1 = data; const bdaddr_t *addr2 = user_data; return !bacmp(addr1, addr2); } static struct device *find_device(const bdaddr_t *addr) { return queue_find(devices, match_by_bdaddr, addr); } static void init_codecs(struct device *dev) { memcpy(&dev->codecs, codecs_defaults, sizeof(dev->codecs)); if (hfp_hf_features & HFP_HF_FEAT_CODEC) dev->codecs[MSBC_OFFSET].local_supported = true; } static struct device *device_create(const bdaddr_t *bdaddr) { struct device *dev; dev = new0(struct device, 1); bacpy(&dev->bdaddr, bdaddr); dev->state = HAL_HF_CLIENT_CONN_STATE_DISCONNECTED; dev->audio_state = HAL_HF_CLIENT_AUDIO_STATE_DISCONNECTED; init_codecs(dev); queue_push_tail(devices, dev); return dev; } static struct device *get_device(const bdaddr_t *addr) { struct device *dev; dev = find_device(addr); if (dev) return dev; /* We do support only one device as for now */ if (queue_isempty(devices)) return device_create(addr); return NULL; } static void device_set_state(struct device *dev, uint8_t state) { struct hal_ev_hf_client_conn_state ev; char address[18]; if (dev->state == state) return; memset(&ev, 0, sizeof(ev)); dev->state = state; ba2str(&dev->bdaddr, address); DBG("device %s state %u", address, state); bdaddr2android(&dev->bdaddr, ev.bdaddr); ev.state = state; ev.chld_feat = dev->chld_features; ev.peer_feat = dev->features; ipc_send_notif(hal_ipc, HAL_SERVICE_ID_HANDSFREE_CLIENT, HAL_EV_HF_CLIENT_CONN_STATE, sizeof(ev), &ev); } static void device_destroy(struct device *dev) { device_set_state(dev, HAL_HF_CLIENT_CONN_STATE_DISCONNECTED); queue_remove(devices, dev); if (dev->hf) hfp_hf_unref(dev->hf); free(dev); } static void handle_disconnect(const void *buf, uint16_t len) { const struct hal_cmd_hf_client_disconnect *cmd = buf; struct device *dev; uint32_t status; bdaddr_t bdaddr; char addr[18]; DBG(""); android2bdaddr(&cmd->bdaddr, &bdaddr); ba2str(&bdaddr, addr); DBG("Disconnect %s", addr); dev = get_device(&bdaddr); if (!dev) { status = HAL_STATUS_FAILED; goto done; } if (dev->state == HAL_HF_CLIENT_CONN_STATE_DISCONNECTED) { status = HAL_STATUS_FAILED; goto done; } if (dev->state == HAL_HF_CLIENT_CONN_STATE_DISCONNECTING) { status = HAL_STATUS_SUCCESS; goto done; } if (dev->state == HAL_HF_CLIENT_CONN_STATE_CONNECTING) { device_destroy(dev); status = HAL_STATUS_SUCCESS; goto done; } status = hfp_hf_disconnect(dev->hf) ? HAL_STATUS_SUCCESS : HAL_STATUS_FAILED; if (status) device_set_state(dev, HAL_HF_CLIENT_CONN_STATE_DISCONNECTING); done: ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_HANDSFREE_CLIENT, HAL_OP_HF_CLIENT_DISCONNECT, status); } static void set_audio_state(struct device *dev, uint8_t state) { struct hal_ev_hf_client_audio_state ev; char address[18]; if (dev->audio_state == state) return; dev->audio_state = state; ba2str(&dev->bdaddr, address); DBG("device %s audio state %u", address, state); bdaddr2android(&dev->bdaddr, ev.bdaddr); ev.state = state; ipc_send_notif(hal_ipc, HAL_SERVICE_ID_HANDSFREE_CLIENT, HAL_EV_HF_CLIENT_AUDIO_STATE, sizeof(ev), &ev); } static void bcc_cb(enum hfp_result result, enum hfp_error cme_err, void *user_data) { struct device *dev = user_data; if (result != HFP_RESULT_OK) set_audio_state(dev, HAL_HF_CLIENT_AUDIO_STATE_DISCONNECTED); } static bool codec_negotiation_supported(struct device *dev) { return (dev->features & HFP_AG_FEAT_CODEC) && (hfp_hf_features & HFP_HF_FEAT_CODEC); } static bool connect_sco(struct device *dev) { if (codec_negotiation_supported(dev)) return hfp_hf_send_command(dev->hf, bcc_cb, dev, "AT+BCC"); return bt_sco_connect(sco, &dev->bdaddr, BT_VOICE_CVSD_16BIT); } static void handle_connect_audio(const void *buf, uint16_t len) { const struct hal_cmd_hf_client_connect_audio *cmd = (void *) buf; struct device *dev; uint8_t status; bdaddr_t bdaddr; DBG(""); android2bdaddr(&cmd->bdaddr, &bdaddr); dev = find_device(&bdaddr); if (!dev || dev->state != HAL_HF_CLIENT_CONN_STATE_SLC_CONNECTED || dev->audio_state != HAL_HF_CLIENT_AUDIO_STATE_DISCONNECTED) { error("hf-client: Cannot create SCO, check SLC or audio state"); status = HAL_STATUS_FAILED; goto done; } if (connect_sco(dev)) { status = HAL_STATUS_SUCCESS; set_audio_state(dev, HAL_HF_CLIENT_AUDIO_STATE_CONNECTING); } else { status = HAL_STATUS_FAILED; } done: ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_HANDSFREE_CLIENT, HAL_OP_HF_CLIENT_CONNECT_AUDIO, status); } static void handle_disconnect_audio(const void *buf, uint16_t len) { const struct hal_cmd_hf_client_disconnect_audio *cmd = (void *) buf; struct device *dev; uint8_t status; bdaddr_t bdaddr; DBG(""); android2bdaddr(&cmd->bdaddr, &bdaddr); dev = find_device(&bdaddr); if (!dev || dev->audio_state == HAL_HF_CLIENT_AUDIO_STATE_DISCONNECTED) { error("hf-client: Device not found or audio not connected"); status = HAL_STATUS_FAILED; goto done; } bt_sco_disconnect(sco); status = HAL_STATUS_SUCCESS; done: ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_HANDSFREE_CLIENT, HAL_OP_HF_CLIENT_DISCONNECT_AUDIO, status); } static void cmd_complete_cb(enum hfp_result result, enum hfp_error cme_err, void *user_data) { struct hal_ev_hf_client_command_complete ev; DBG(""); memset(&ev, 0, sizeof(ev)); switch (result) { case HFP_RESULT_OK: ev.type = HAL_HF_CLIENT_CMD_COMP_OK; break; case HFP_RESULT_NO_CARRIER: ev.type = HAL_HF_CLIENT_CMD_COMP_ERR_NO_CARRIER; break; case HFP_RESULT_ERROR: ev.type = HAL_HF_CLIENT_CMD_COMP_ERR; break; case HFP_RESULT_BUSY: ev.type = HAL_HF_CLIENT_CMD_COMP_ERR_BUSY; break; case HFP_RESULT_NO_ANSWER: ev.type = HAL_HF_CLIENT_CMD_COMP_ERR_NO_ANSWER; break; case HFP_RESULT_DELAYED: ev.type = HAL_HF_CLIENT_CMD_COMP_ERR_DELAYED; break; case HFP_RESULT_REJECTED: ev.type = HAL_HF_CLIENT_CMD_COMP_ERR_BACKLISTED; break; case HFP_RESULT_CME_ERROR: ev.type = HAL_HF_CLIENT_CMD_COMP_ERR_CME; ev.cme = cme_err; break; case HFP_RESULT_CONNECT: case HFP_RESULT_RING: case HFP_RESULT_NO_DIALTONE: default: error("hf-client: Unknown error code %d", result); ev.type = HAL_HF_CLIENT_CMD_COMP_ERR; break; } ipc_send_notif(hal_ipc, HAL_SERVICE_ID_HANDSFREE_CLIENT, HAL_EV_CLIENT_COMMAND_COMPLETE, sizeof(ev), &ev); } static void handle_start_vr(const void *buf, uint16_t len) { struct device *dev; uint8_t status; DBG(""); dev = find_default_device(); if (!dev) { status = HAL_STATUS_FAILED; goto done; } if (hfp_hf_send_command(dev->hf, cmd_complete_cb, NULL, "AT+BVRA=1")) status = HAL_STATUS_SUCCESS; else status = HAL_STATUS_FAILED; done: ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_HANDSFREE_CLIENT, HAL_OP_HF_CLIENT_START_VR, status); } static void handle_stop_vr(const void *buf, uint16_t len) { struct device *dev; uint8_t status; DBG(""); dev = find_default_device(); if (!dev) { status = HAL_STATUS_FAILED; goto done; } if (hfp_hf_send_command(dev->hf, cmd_complete_cb, NULL, "AT+BVRA=0")) status = HAL_STATUS_SUCCESS; else status = HAL_STATUS_FAILED; done: ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_HANDSFREE_CLIENT, HAL_OP_HF_CLIENT_STOP_VR, status); } static void handle_volume_control(const void *buf, uint16_t len) { const struct hal_cmd_hf_client_volume_control *cmd = buf; struct device *dev; uint8_t status; uint8_t vol; bool ret; DBG(""); dev = find_default_device(); if (!dev) { status = HAL_STATUS_FAILED; goto done; } /* * Volume is in the range 0-15. Make sure we send correct value * to remote device */ vol = cmd->volume > 15 ? 15 : cmd->volume; switch (cmd->type) { case HF_CLIENT_VOLUME_TYPE_SPEAKER: ret = hfp_hf_send_command(dev->hf, cmd_complete_cb, NULL, "AT+VGS=%u", vol); break; case HF_CLIENT_VOLUME_TYPE_MIC: ret = hfp_hf_send_command(dev->hf, cmd_complete_cb, NULL, "AT+VGM=%u", vol); break; default: ret = false; break; } status = ret ? HAL_STATUS_SUCCESS : HAL_STATUS_FAILED; done: ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_HANDSFREE_CLIENT, HAL_OP_HF_CLIENT_VOLUME_CONTROL, status); } static void handle_dial(const void *buf, uint16_t len) { const struct hal_cmd_hf_client_dial *cmd = buf; struct device *dev; uint8_t status; bool ret; DBG(""); if (len != sizeof(*cmd) + cmd->number_len) goto failed; dev = find_default_device(); if (!dev) { status = HAL_STATUS_FAILED; goto done; } if (cmd->number_len > 0) { if (cmd->number[cmd->number_len - 1] != '\0') goto failed; DBG("Dialing %s", cmd->number); ret = hfp_hf_send_command(dev->hf, cmd_complete_cb, NULL, "ATD%s;", cmd->number); } else { DBG("Redialing"); ret = hfp_hf_send_command(dev->hf, cmd_complete_cb, NULL, "AT+BLDN"); } status = ret ? HAL_STATUS_SUCCESS : HAL_STATUS_FAILED; done: ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_HANDSFREE_CLIENT, HAL_OP_HF_CLIENT_DIAL, status); return; failed: error("Malformed number data, size (%u bytes), terminating", len); raise(SIGTERM); } static void handle_dial_memory(const void *buf, uint16_t len) { const struct hal_cmd_hf_client_dial_memory *cmd = buf; struct device *dev; uint8_t status; DBG(""); dev = find_default_device(); if (!dev) { status = HAL_STATUS_FAILED; goto done; } /* For some reason location in BT HAL is int. Therefore that check */ if (cmd->location < 0) { status = HAL_STATUS_FAILED; goto done; } if (hfp_hf_send_command(dev->hf, cmd_complete_cb, NULL , "ATD>%d;", cmd->location)) status = HAL_STATUS_SUCCESS; else status = HAL_STATUS_FAILED; done: ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_HANDSFREE_CLIENT, HAL_OP_HF_CLIENT_DIAL_MEMORY, status); } static void handle_call_action(const void *buf, uint16_t len) { const struct hal_cmd_hf_client_call_action *cmd = buf; struct device *dev; uint8_t status; bool ret; DBG(""); dev = find_default_device(); if (!dev) { status = HAL_STATUS_FAILED; goto done; } switch (cmd->action) { case HAL_HF_CLIENT_ACTION_CHLD_0: ret = hfp_hf_send_command(dev->hf, cmd_complete_cb, NULL, "AT+CHLD=0"); break; case HAL_HF_CLIENT_ACTION_CHLD_1: ret = hfp_hf_send_command(dev->hf, cmd_complete_cb, NULL, "AT+CHLD=1"); break; case HAL_HF_CLIENT_ACTION_CHLD_2: ret = hfp_hf_send_command(dev->hf, cmd_complete_cb, NULL, "AT+CHLD=2"); break; case HAL_HF_CLIENT_ACTION_CHLD_3: ret = hfp_hf_send_command(dev->hf, cmd_complete_cb, NULL, "AT+CHLD=3"); break; case HAL_HF_CLIENT_ACTION_CHLD_4: ret = hfp_hf_send_command(dev->hf, cmd_complete_cb, NULL, "AT+CHLD=4"); break; case HAL_HF_CLIENT_ACTION_CHLD_1x: /* Index is int in BT HAL. Let's be paranoid here */ if (cmd->index <= 0) ret = false; else ret = hfp_hf_send_command(dev->hf, cmd_complete_cb, NULL, "AT+CHLD=1%d", cmd->index); break; case HAL_HF_CLIENT_ACTION_CHLD_2x: /* Index is int in BT HAL. Let's be paranoid here */ if (cmd->index <= 0) ret = false; else ret = hfp_hf_send_command(dev->hf, cmd_complete_cb, NULL, "AT+CHLD=2%d", cmd->index); break; case HAL_HF_CLIENT_ACTION_ATA: ret = hfp_hf_send_command(dev->hf, cmd_complete_cb, NULL, "ATA"); break; case HAL_HF_CLIENT_ACTION_CHUP: ret = hfp_hf_send_command(dev->hf, cmd_complete_cb, NULL, "AT+CHUP"); break; case HAL_HF_CLIENT_ACTION_BRTH_0: ret = hfp_hf_send_command(dev->hf, cmd_complete_cb, NULL, "AT+BTRH=0"); break; case HAL_HF_CLIENT_ACTION_BRTH_1: ret = hfp_hf_send_command(dev->hf, cmd_complete_cb, NULL, "AT+BTRH=1"); break; case HAL_HF_CLIENT_ACTION_BRTH_2: ret = hfp_hf_send_command(dev->hf, cmd_complete_cb, NULL, "AT+BTRH=2"); break; default: error("hf-client: Unknown action %d", cmd->action); ret = false; break; } status = ret ? HAL_STATUS_SUCCESS : HAL_STATUS_FAILED; done: ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_HANDSFREE_CLIENT, HAL_OP_HF_CLIENT_CALL_ACTION, status); } static void handle_query_current_calls(const void *buf, uint16_t len) { struct device *dev; uint8_t status; DBG(""); dev = find_default_device(); if (!dev) { status = HAL_STATUS_FAILED; goto done; } if (hfp_hf_send_command(dev->hf, cmd_complete_cb, NULL, "AT+CLCC")) status = HAL_STATUS_SUCCESS; else status = HAL_STATUS_FAILED; done: ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_HANDSFREE_CLIENT, HAL_OP_HF_CLIENT_QUERY_CURRENT_CALLS, status); } static void handle_query_operator_name(const void *buf, uint16_t len) { struct device *dev; uint8_t status; DBG(""); dev = find_default_device(); if (!dev) { status = HAL_STATUS_FAILED; goto done; } if (hfp_hf_send_command(dev->hf, cmd_complete_cb, NULL, "AT+COPS?")) status = HAL_STATUS_SUCCESS; else status = HAL_STATUS_FAILED; done: ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_HANDSFREE_CLIENT, HAL_OP_HF_CLIENT_QUERY_OPERATOR_NAME, status); } static void handle_retrieve_subscr_info(const void *buf, uint16_t len) { struct device *dev; uint8_t status; DBG(""); dev = find_default_device(); if (!dev) { status = HAL_STATUS_FAILED; goto done; } if (hfp_hf_send_command(dev->hf, cmd_complete_cb, NULL, "AT+CNUM")) status = HAL_STATUS_SUCCESS; else status = HAL_STATUS_FAILED; done: ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_HANDSFREE_CLIENT, HAL_OP_HF_CLIENT_RETRIEVE_SUBSCR_INFO, status); } static void handle_send_dtmf(const void *buf, uint16_t len) { const struct hal_cmd_hf_client_send_dtmf *cmd = buf; struct device *dev; uint8_t status; dev = find_default_device(); if (!dev) { status = HAL_STATUS_FAILED; goto done; } if (hfp_hf_send_command(dev->hf, cmd_complete_cb, NULL, "AT+VTS=%c", (char) cmd->tone)) status = HAL_STATUS_SUCCESS; else status = HAL_STATUS_FAILED; done: ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_HANDSFREE_CLIENT, HAL_OP_HF_CLIENT_SEND_DTMF, status); } static void handle_get_last_vc_tag_num(const void *buf, uint16_t len) { struct device *dev; uint8_t status; dev = find_default_device(); if (!dev) { status = HAL_STATUS_FAILED; goto done; } if (hfp_hf_send_command(dev->hf, cmd_complete_cb, NULL, "AT+BINP=1")) status = HAL_STATUS_SUCCESS; else status = HAL_STATUS_FAILED; done: ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_HANDSFREE_CLIENT, HAL_OP_HF_CLIENT_GET_LAST_VOICE_TAG_NUM, status); } static void disconnect_watch(void *user_data) { DBG(""); device_destroy(user_data); } static void slc_error(struct device *dev) { error("hf-client: Could not create SLC - dropping connection"); hfp_hf_disconnect(dev->hf); } static void set_chld_feat(struct device *dev, char *feat) { DBG(" %s", feat); if (strcmp(feat, "0") == 0) dev->chld_features |= HAL_HF_CLIENT_CHLD_FEAT_REL; else if (strcmp(feat, "1") == 0) dev->chld_features |= HAL_HF_CLIENT_CHLD_FEAT_REL_ACC; else if (strcmp(feat, "1x") == 0) dev->chld_features |= HAL_HF_CLIENT_CHLD_FEAT_REL_X; else if (strcmp(feat, "2") == 0) dev->chld_features |= HAL_HF_CLIENT_CHLD_FEAT_HOLD_ACC; else if (strcmp(feat, "2x") == 0) dev->chld_features |= HAL_HF_CLIENT_CHLD_FEAT_PRIV_X; else if (strcmp(feat, "3") == 0) dev->chld_features |= HAL_HF_CLIENT_CHLD_FEAT_MERGE; else if (strcmp(feat, "4") == 0) dev->chld_features |= HAL_HF_CLIENT_CHLD_FEAT_MERGE_DETACH; } static void get_local_codecs_string(struct device *dev, char *buf, uint8_t len) { int i; uint8_t offset; memset(buf, 0, len); offset = 0; for (i = 0; i < CODECS_COUNT; i++) { char c[8]; int l; if (!dev->codecs[i].local_supported) continue; memset(c, 0, sizeof(c)); l = sprintf(c, "%d,", dev->codecs[i].type); if (l > (len - offset - 1)) { error("hf-client: Codecs cannot fit into buffer"); return; } strcat(&buf[offset], c); offset += l; } } static void bvra_cb(struct hfp_context *context, void *user_data) { struct hal_ev_hf_client_vr_state ev; unsigned int val; if (!hfp_context_get_number(context, &val) || val > 1) return; ev.state = val ? HAL_HF_CLIENT_VR_STARTED : HAL_HF_CLIENT_VR_STOPPED; ipc_send_notif(hal_ipc, HAL_SERVICE_ID_HANDSFREE_CLIENT, HAL_EV_HF_CLIENT_VR_STATE, sizeof(ev), &ev); } static void vgm_cb(struct hfp_context *context, void *user_data) { struct hal_ev_hf_client_volume_changed ev; unsigned int val; if (!hfp_context_get_number(context, &val) || val > 15) return; ev.type = HF_CLIENT_VOLUME_TYPE_MIC; ev.volume = val; ipc_send_notif(hal_ipc, HAL_SERVICE_ID_HANDSFREE_CLIENT, HAL_EV_HF_CLIENT_VR_STATE, sizeof(ev), &ev); } static void vgs_cb(struct hfp_context *context, void *user_data) { struct hal_ev_hf_client_volume_changed ev; unsigned int val; if (!hfp_context_get_number(context, &val) || val > 15) return; ev.type = HF_CLIENT_VOLUME_TYPE_SPEAKER; ev.volume = val; ipc_send_notif(hal_ipc, HAL_SERVICE_ID_HANDSFREE_CLIENT, HAL_EV_CLIENT_VOLUME_CHANGED, sizeof(ev), &ev); } static void brth_cb(struct hfp_context *context, void *user_data) { struct hal_ev_hf_client_response_and_hold_status ev; unsigned int val; DBG(""); if (!hfp_context_get_number(context, &val) || val > HAL_HF_CLIENT_RESP_AND_HOLD_STATUS_REJECT) { error("hf-client: incorrect BTRH response "); return; } ev.status = val; ipc_send_notif(hal_ipc, HAL_SERVICE_ID_HANDSFREE_CLIENT, HAL_EV_HF_CLIENT_RESPONSE_AND_HOLD_STATUS, sizeof(ev), &ev); } static void clcc_cb(struct hfp_context *context, void *user_data) { uint8_t buf[IPC_MTU]; struct hal_ev_hf_client_current_call *ev = (void *) buf; unsigned int val; DBG(""); memset(buf, 0, sizeof(buf)); if (!hfp_context_get_number(context, &val)) { error("hf-client: Could not get index"); return; } ev->index = val; if (!hfp_context_get_number(context, &val) || val > HAL_HF_CLIENT_DIRECTION_INCOMING) { error("hf-client: Could not get direction"); return; } ev->direction = val; if (!hfp_context_get_number(context, &val) || val > HAL_HF_CLIENT_CALL_STATE_HELD_BY_RESP_AND_HOLD) { error("hf-client: Could not get callstate"); return; } ev->call_state = val; /* Next field is MODE but Android is not interested in this. Skip it */ if (!hfp_context_get_number(context, &val)) { error("hf-client: Could not get mode"); return; } if (!hfp_context_get_number(context, &val) || val > 1) { error("hf-client: Could not get multiparty"); return; } ev->multiparty = val; if (hfp_context_get_string(context, (char *) &ev->number[0], MAX_NUMBER_LEN)) ev->number_len = strlen((char *) ev->number) + 1; ipc_send_notif(hal_ipc, HAL_SERVICE_ID_HANDSFREE_CLIENT, HAL_EV_HF_CLIENT_CURRENT_CALL, sizeof(*ev) + ev->number_len, ev); } static void ciev_cb(struct hfp_context *context, void *user_data) { struct device *dev = user_data; unsigned int index, val; int i; DBG(""); if (!hfp_context_get_number(context, &index)) return; if (!hfp_context_get_number(context, &val)) return; for (i = 0; i < HFP_INDICATOR_LAST; i++) { if (dev->ag_ind[i].index != index) continue; if (dev->ag_ind[i].cb) { dev->ag_ind[i].val = val; dev->ag_ind[i].cb(val); return; } } } static void cnum_cb(struct hfp_context *context, void *user_data) { uint8_t buf[IPC_MTU]; struct hal_ev_hf_client_subscriber_service_info *ev = (void *) buf; unsigned int service; DBG(""); /* Alpha field is empty string, just skip it */ hfp_context_skip_field(context); if (!hfp_context_get_string(context, (char *) &ev->name[0], MAX_NUMBER_LEN)) { error("hf-client: Could not get number"); return; } ev->name_len = strlen((char *) &ev->name[0]) + 1; /* Type is not used in Android */ hfp_context_skip_field(context); /* Speed field is empty string, just skip it */ hfp_context_skip_field(context); if (!hfp_context_get_number(context, &service)) return; switch (service) { case 4: ev->type = HAL_HF_CLIENT_SUBSCR_TYPE_VOICE; break; case 5: ev->type = HAL_HF_CLIENT_SUBSCR_TYPE_FAX; break; default: ev->type = HAL_HF_CLIENT_SUBSCR_TYPE_UNKNOWN; break; } ipc_send_notif(hal_ipc, HAL_SERVICE_ID_HANDSFREE_CLIENT, HAL_EV_CLIENT_SUBSCRIBER_SERVICE_INFO, sizeof(*ev) + ev->name_len, ev); } static void cops_cb(struct hfp_context *context, void *user_data) { uint8_t buf[IPC_MTU]; struct hal_ev_hf_client_operator_name *ev = (void *) buf; unsigned int format; DBG(""); /* Not interested in mode */ hfp_context_skip_field(context); if (!hfp_context_get_number(context, &format)) return; if (format != 0) info("hf-client: Not correct string format in +COSP"); if (!hfp_context_get_string(context, (char *) &ev->name[0], MAX_OPERATOR_NAME_LEN)) { error("hf-client: incorrect COPS response"); return; } ev->name_len = strlen((char *) &ev->name[0]) + 1; ipc_send_notif(hal_ipc, HAL_SERVICE_ID_HANDSFREE_CLIENT, HAL_EV_HF_CLIENT_OPERATOR_NAME, sizeof(*ev) + ev->name_len, ev); } static void binp_cb(struct hfp_context *context, void *user_data) { uint8_t buf[IPC_MTU]; struct hal_ev_hf_client_last_void_call_tag_num *ev = (void *) buf; char number[33]; DBG(""); if (!hfp_context_get_string(context, number, sizeof(number))) { error("hf-client: incorrect COPS response"); return; } ev->number_len = strlen(number) + 1; memcpy(ev->number, number, ev->number_len); ipc_send_notif(hal_ipc, HAL_SERVICE_ID_HANDSFREE_CLIENT, HAL_EV_CLIENT_LAST_VOICE_CALL_TAG_NUM, sizeof(*ev) + ev->number_len, ev); } static bool is_codec_supported_localy(struct device *dev, uint8_t codec) { int i; for (i = 0; i < CODECS_COUNT; i++) { if (dev->codecs[i].type != codec) continue; return dev->codecs[i].local_supported; } return false; } static void bcs_resp(enum hfp_result result, enum hfp_error cme_err, void *user_data) { if (result != HFP_RESULT_OK) error("hf-client: Error on AT+BCS (err=%u)", result); } static void bcs_cb(struct hfp_context *context, void *user_data) { struct device *dev = user_data; unsigned int codec; char codecs_string[8]; DBG(""); if (!hfp_context_get_number(context, &codec)) goto failed; if (!is_codec_supported_localy(dev, codec)) goto failed; dev->negotiated_codec = codec; hfp_hf_send_command(dev->hf, bcs_resp, dev, "AT+BCS=%u", codec); return; failed: error("hf-client: Could not get codec"); get_local_codecs_string(dev, codecs_string, sizeof(codecs_string)); hfp_hf_send_command(dev->hf, bcs_resp, dev, "AT+BCS=%s", codecs_string); } static void slc_completed(struct device *dev) { int i; struct indicator *ag_ind; DBG(""); ag_ind = dev->ag_ind; device_set_state(dev, HAL_HF_CLIENT_CONN_STATE_SLC_CONNECTED); /* Notify Android with indicators */ for (i = 0; i < HFP_INDICATOR_LAST; i++) { if (!ag_ind[i].cb) continue; ag_ind[i].cb(ag_ind[i].val); } /* TODO: register unsolicited results handlers */ hfp_hf_register(dev->hf, bvra_cb, "+BRVA", dev, NULL); hfp_hf_register(dev->hf, vgm_cb, "+VGM", dev, NULL); hfp_hf_register(dev->hf, vgs_cb, "+VGS", dev, NULL); hfp_hf_register(dev->hf, brth_cb, "+BTRH", dev, NULL); hfp_hf_register(dev->hf, clcc_cb, "+CLCC", dev, NULL); hfp_hf_register(dev->hf, ciev_cb, "+CIEV", dev, NULL); hfp_hf_register(dev->hf, cops_cb, "+COPS", dev, NULL); hfp_hf_register(dev->hf, cnum_cb, "+CNUM", dev, NULL); hfp_hf_register(dev->hf, binp_cb, "+BINP", dev, NULL); hfp_hf_register(dev->hf, bcs_cb, "+BCS", dev, NULL); if (!hfp_hf_send_command(dev->hf, cmd_complete_cb, NULL, "AT+COPS=3,0")) info("hf-client: Could not send AT+COPS=3,0"); } static void slc_chld_cb(struct hfp_context *context, void *user_data) { struct device *dev = user_data; char feat[3]; if (!hfp_context_open_container(context)) goto failed; while (hfp_context_get_unquoted_string(context, feat, sizeof(feat))) set_chld_feat(dev, feat); if (!hfp_context_close_container(context)) goto failed; return; failed: error("hf-client: Error on CHLD response"); slc_error(dev); } static void slc_chld_resp(enum hfp_result result, enum hfp_error cme_err, void *user_data) { struct device *dev = user_data; DBG(""); hfp_hf_unregister(dev->hf, "+CHLD"); if (result != HFP_RESULT_OK) { error("hf-client: CHLD error: %d", result); slc_error(dev); return; } slc_completed(dev); } static void slc_cmer_resp(enum hfp_result result, enum hfp_error cme_err, void *user_data) { struct device *dev = user_data; DBG(""); if (result != HFP_RESULT_OK) { error("hf-client: CMER error: %d", result); goto failed; } /* Continue with SLC creation */ if (!(dev->features & HFP_AG_FEAT_3WAY)) { slc_completed(dev); return; } if (!hfp_hf_register(dev->hf, slc_chld_cb, "+CHLD", dev, NULL)) { error("hf-client: Could not register +CHLD"); goto failed; } if (!hfp_hf_send_command(dev->hf, slc_chld_resp, dev, "AT+CHLD=?")) { error("hf-client: Could not send AT+CHLD"); goto failed; } return; failed: slc_error(dev); } static void set_indicator_value(uint8_t index, unsigned int val, struct indicator *ag_ind) { int i; for (i = 0; i < HFP_INDICATOR_LAST; i++) { if (index != ag_ind[i].index) continue; ag_ind[i].val = val; ag_ind[i].cb(val); return; } } static void slc_cind_status_cb(struct hfp_context *context, void *user_data) { struct device *dev = user_data; uint8_t index = 1; DBG(""); while (hfp_context_has_next(context)) { uint32_t val; if (!hfp_context_get_number(context, &val)) { error("hf-client: Error on CIND status response"); return; } set_indicator_value(index++, val, dev->ag_ind); } } static void slc_cind_status_resp(enum hfp_result result, enum hfp_error cme_err, void *user_data) { struct device *dev = user_data; DBG(""); hfp_hf_unregister(dev->hf, "+CIND"); if (result != HFP_RESULT_OK) { error("hf-client: CIND error: %d", result); goto failed; } /* Continue with SLC creation */ if (!hfp_hf_send_command(dev->hf, slc_cmer_resp, dev, "AT+CMER=3,0,0,1")) { error("hf-client: Counld not send AT+CMER"); goto failed; } return; failed: slc_error(dev); } static void slc_cind_resp(enum hfp_result result, enum hfp_error cme_err, void *user_data) { struct device *dev = user_data; DBG(""); hfp_hf_unregister(dev->hf, "+CIND"); if (result != HFP_RESULT_OK) { error("hf-client: CIND error: %d", result); goto failed; } /* Continue with SLC creation */ if (!hfp_hf_register(dev->hf, slc_cind_status_cb, "+CIND", dev, NULL)) { error("hf-client: Counld not register +CIND"); goto failed; } if (!hfp_hf_send_command(dev->hf, slc_cind_status_resp, dev, "AT+CIND?")) { error("hf-client: Counld not send AT+CIND?"); goto failed; } return; failed: slc_error(dev); } static void ciev_service_cb(uint8_t val) { struct hal_ev_hf_client_net_state ev; DBG(""); if (val > HAL_HF_CLIENT_NET_ROAMING_TYPE_ROAMING) { error("hf-client: Incorrect state %u:", val); return; } ev.state = val; ipc_send_notif(hal_ipc, HAL_SERVICE_ID_HANDSFREE_CLIENT, HAL_EV_HF_CLIENT_NET_STATE, sizeof(ev), &ev); } static void ciev_call_cb(uint8_t val) { struct hal_ev_hf_client_call_indicator ev; DBG(""); if (val > HAL_HF_CLIENT_CALL_IND_CALL_IN_PROGERSS) { error("hf-client: Incorrect call state %u:", val); return; } ev.call = val; ipc_send_notif(hal_ipc, HAL_SERVICE_ID_HANDSFREE_CLIENT, HAL_EV_HF_CLIENT_CALL_INDICATOR, sizeof(ev), &ev); } static void ciev_callsetup_cb(uint8_t val) { struct hal_ev_hf_client_call_setup_indicator ev; DBG(""); if (val > HAL_HF_CLIENT_CALL_SETUP_ALERTING) { error("hf-client: Incorrect call setup state %u:", val); return; } ev.call_setup = val; ipc_send_notif(hal_ipc, HAL_SERVICE_ID_HANDSFREE_CLIENT, HAL_EV_HF_CLIENT_CALL_SETUP_INDICATOR, sizeof(ev), &ev); } static void ciev_callheld_cb(uint8_t val) { struct hal_ev_hf_client_call_held_indicator ev; DBG(""); if (val > HAL_HF_CLIENT_CALL_SETUP_IND_HOLD) { error("hf-client: Incorrect call held state %u:", val); return; } ev.call_held = val; ipc_send_notif(hal_ipc, HAL_SERVICE_ID_HANDSFREE_CLIENT, HAL_EV_HF_CLIENT_CALL_HELD_INDICATOR, sizeof(ev), &ev); } static void ciev_signal_cb(uint8_t val) { struct hal_ev_hf_client_net_signal_strength ev; DBG(""); if (val > 5) { error("hf-client: Incorrect signal value %u:", val); return; } ev.signal_strength = val; ipc_send_notif(hal_ipc, HAL_SERVICE_ID_HANDSFREE_CLIENT, HAL_EV_HF_CLIENT_NET_SIGNAL_STRENGTH, sizeof(ev), &ev); } static void ciev_roam_cb(uint8_t val) { struct hal_ev_hf_client_net_roaming_type ev; DBG(""); if (val > HAL_HF_CLIENT_NET_ROAMING_TYPE_ROAMING) { error("hf-client: Incorrect roaming state %u:", val); return; } ev.state = val; ipc_send_notif(hal_ipc, HAL_SERVICE_ID_HANDSFREE_CLIENT, HAL_EV_HF_CLIENT_NET_ROAMING_TYPE, sizeof(ev), &ev); } static void ciev_battchg_cb(uint8_t val) { struct hal_ev_hf_client_battery_level ev; DBG(""); if (val > 5) { error("hf-client: Incorrect battery charge value %u:", val); return; } ev.battery_level = val; ipc_send_notif(hal_ipc, HAL_SERVICE_ID_HANDSFREE_CLIENT, HAL_EV_HF_CLIENT_BATTERY_LEVEL, sizeof(ev), &ev); } static void set_indicator_parameters(uint8_t index, const char *indicator, unsigned int min, unsigned int max, struct indicator *ag_ind) { DBG("%s, %i", indicator, index); /* TODO: Verify min/max values ? */ if (strcmp("service", indicator) == 0) { ag_ind[HFP_INDICATOR_SERVICE].index = index; ag_ind[HFP_INDICATOR_SERVICE].min = min; ag_ind[HFP_INDICATOR_SERVICE].max = max; ag_ind[HFP_INDICATOR_SERVICE].cb = ciev_service_cb; return; } if (strcmp("call", indicator) == 0) { ag_ind[HFP_INDICATOR_CALL].index = index; ag_ind[HFP_INDICATOR_CALL].min = min; ag_ind[HFP_INDICATOR_CALL].max = max; ag_ind[HFP_INDICATOR_CALL].cb = ciev_call_cb; return; } if (strcmp("callsetup", indicator) == 0) { ag_ind[HFP_INDICATOR_CALLSETUP].index = index; ag_ind[HFP_INDICATOR_CALLSETUP].min = min; ag_ind[HFP_INDICATOR_CALLSETUP].max = max; ag_ind[HFP_INDICATOR_CALLSETUP].cb = ciev_callsetup_cb; return; } if (strcmp("callheld", indicator) == 0) { ag_ind[HFP_INDICATOR_CALLHELD].index = index; ag_ind[HFP_INDICATOR_CALLHELD].min = min; ag_ind[HFP_INDICATOR_CALLHELD].max = max; ag_ind[HFP_INDICATOR_CALLHELD].cb = ciev_callheld_cb; return; } if (strcmp("signal", indicator) == 0) { ag_ind[HFP_INDICATOR_SIGNAL].index = index; ag_ind[HFP_INDICATOR_SIGNAL].min = min; ag_ind[HFP_INDICATOR_SIGNAL].max = max; ag_ind[HFP_INDICATOR_SIGNAL].cb = ciev_signal_cb; return; } if (strcmp("roam", indicator) == 0) { ag_ind[HFP_INDICATOR_ROAM].index = index; ag_ind[HFP_INDICATOR_ROAM].min = min; ag_ind[HFP_INDICATOR_ROAM].max = max; ag_ind[HFP_INDICATOR_ROAM].cb = ciev_roam_cb; return; } if (strcmp("battchg", indicator) == 0) { ag_ind[HFP_INDICATOR_BATTCHG].index = index; ag_ind[HFP_INDICATOR_BATTCHG].min = min; ag_ind[HFP_INDICATOR_BATTCHG].max = max; ag_ind[HFP_INDICATOR_BATTCHG].cb = ciev_battchg_cb; return; } error("hf-client: Unknown indicator: %s", indicator); } static void slc_cind_cb(struct hfp_context *context, void *user_data) { struct device *dev = user_data; int index = 1; DBG(""); while (hfp_context_has_next(context)) { char name[255]; unsigned int min, max; /* e.g ("callsetup",(0-3)) */ if (!hfp_context_open_container(context)) break; if (!hfp_context_get_string(context, name, sizeof(name))) { error("hf-client: Could not get string"); goto failed; } if (!hfp_context_open_container(context)) { error("hf-client: Could not open container"); goto failed; } if (!hfp_context_get_range(context, &min, &max)) { if (!hfp_context_get_number(context, &min)) { error("hf-client: Could not get number"); goto failed; } if (!hfp_context_get_number(context, &max)) { error("hf-client: Could not get number"); goto failed; } } if (!hfp_context_close_container(context)) { error("hf-client: Could not close container"); goto failed; } if (!hfp_context_close_container(context)) { error("hf-client: Could not close container"); goto failed; } set_indicator_parameters(index, name, min, max, dev->ag_ind); index++; } return; failed: error("hf-client: Error on CIND response"); slc_error(dev); } static void slc_bac_resp(enum hfp_result result, enum hfp_error cme_err, void *user_data) { struct device *dev = user_data; DBG(""); if (result != HFP_RESULT_OK) goto failed; /* Continue with SLC creation */ if (!hfp_hf_register(dev->hf, slc_cind_cb, "+CIND", dev, NULL)) { error("hf-client: Could not register for +CIND"); goto failed; } if (!hfp_hf_send_command(dev->hf, slc_cind_resp, dev, "AT+CIND=?")) goto failed; return; failed: error("hf-client: Error on BAC response"); slc_error(dev); } static bool send_supported_codecs(struct device *dev) { char codecs_string[8]; char bac[16]; memset(bac, 0, sizeof(bac)); strcpy(bac, "AT+BAC="); get_local_codecs_string(dev, codecs_string, sizeof(codecs_string)); strcat(bac, codecs_string); return hfp_hf_send_command(dev->hf, slc_bac_resp, dev, bac); } static void slc_brsf_cb(struct hfp_context *context, void *user_data) { unsigned int feat; struct device *dev = user_data; DBG(""); if (hfp_context_get_number(context, &feat)) dev->features = feat; } static void slc_brsf_resp(enum hfp_result result, enum hfp_error cme_err, void *user_data) { struct device *dev = user_data; hfp_hf_unregister(dev->hf, "+BRSF"); if (result != HFP_RESULT_OK) { error("hf-client: BRSF error: %d", result); goto failed; } /* Continue with SLC creation */ if (codec_negotiation_supported(dev)) { if (send_supported_codecs(dev)) return; error("hf-client: Could not send BAC command"); goto failed; } /* No WBS on remote side. Continue with indicators */ if (!hfp_hf_register(dev->hf, slc_cind_cb, "+CIND", dev, NULL)) { error("hf-client: Could not register for +CIND"); goto failed; } if (!hfp_hf_send_command(dev->hf, slc_cind_resp, dev, "AT+CIND=?")) { error("hf-client: Could not send AT+CIND command"); goto failed; } return; failed: slc_error(dev); } static bool create_slc(struct device *dev) { DBG(""); if (!hfp_hf_register(dev->hf, slc_brsf_cb, "+BRSF", dev, NULL)) return false; return hfp_hf_send_command(dev->hf, slc_brsf_resp, dev, "AT+BRSF=%u", hfp_hf_features); } static void connect_cb(GIOChannel *chan, GError *err, gpointer user_data) { struct device *dev = user_data; DBG(""); if (err) { error("hf-client: connect failed (%s)", err->message); goto failed; } dev->hf = hfp_hf_new(g_io_channel_unix_get_fd(chan)); if (!dev->hf) { error("hf-client: Could not create hfp io"); goto failed; } g_io_channel_set_close_on_unref(chan, FALSE); hfp_hf_set_close_on_unref(dev->hf, true); hfp_hf_set_disconnect_handler(dev->hf, disconnect_watch, dev, NULL); if (!create_slc(dev)) { error("hf-client: Could not start SLC creation"); hfp_hf_disconnect(dev->hf); goto failed; } device_set_state(dev, HAL_HF_CLIENT_CONN_STATE_CONNECTED); return; failed: g_io_channel_shutdown(chan, TRUE, NULL); device_destroy(dev); } static void sdp_hfp_search_cb(sdp_list_t *recs, int err, gpointer data) { sdp_list_t *protos, *classes; struct device *dev = data; GError *gerr = NULL; GIOChannel *io; uuid_t uuid; int channel; DBG(""); if (err < 0) { error("hf-client: unable to get SDP record: %s", strerror(-err)); goto failed; } if (!recs || !recs->data) { info("hf-client: no HFP SDP records found"); goto failed; } if (sdp_get_service_classes(recs->data, &classes) < 0 || !classes) { error("hf-client: unable to get service classes from record"); goto failed; } /* TODO read remote version? */ memcpy(&uuid, classes->data, sizeof(uuid)); sdp_list_free(classes, free); if (!sdp_uuid128_to_uuid(&uuid) || uuid.type != SDP_UUID16 || uuid.value.uuid16 != HANDSFREE_AGW_SVCLASS_ID) { error("hf-client: invalid service record or not HFP"); goto failed; } if (sdp_get_access_protos(recs->data, &protos) < 0) { error("hf-client: unable to get access protocols from record"); sdp_list_free(classes, free); goto failed; } channel = sdp_get_proto_port(protos, RFCOMM_UUID); sdp_list_foreach(protos, (sdp_list_func_t) sdp_list_free, NULL); sdp_list_free(protos, NULL); if (channel <= 0) { error("hf-client: unable to get RFCOMM channel from record"); goto failed; } io = bt_io_connect(connect_cb, dev, NULL, &gerr, BT_IO_OPT_SOURCE_BDADDR, &adapter_addr, BT_IO_OPT_DEST_BDADDR, &dev->bdaddr, BT_IO_OPT_SEC_LEVEL, BT_IO_SEC_MEDIUM, BT_IO_OPT_CHANNEL, channel, BT_IO_OPT_INVALID); if (!io) { error("hf-client: unable to connect: %s", gerr->message); g_error_free(gerr); goto failed; } g_io_channel_unref(io); return; failed: device_destroy(dev); } static int sdp_search_hfp(struct device *dev) { uuid_t uuid; sdp_uuid16_create(&uuid, HANDSFREE_AGW_SVCLASS_ID); return bt_search_service(&adapter_addr, &dev->bdaddr, &uuid, sdp_hfp_search_cb, dev, NULL, 0); } static void handle_connect(const void *buf, uint16_t len) { struct device *dev; const struct hal_cmd_hf_client_connect *cmd = buf; uint32_t status; bdaddr_t bdaddr; char addr[18]; DBG(""); android2bdaddr(&cmd->bdaddr, &bdaddr); ba2str(&bdaddr, addr); DBG("connecting to %s", addr); dev = get_device(&bdaddr); if (!dev) { status = HAL_STATUS_FAILED; goto done; } if (dev->state != HAL_HF_CLIENT_CONN_STATE_DISCONNECTED) { status = HAL_STATUS_FAILED; goto done; } if (sdp_search_hfp(dev) < 0) { status = HAL_STATUS_FAILED; device_destroy(dev); goto done; } device_set_state(dev, HAL_HF_CLIENT_CONN_STATE_CONNECTING); status = HAL_STATUS_SUCCESS; done: ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_HANDSFREE_CLIENT, HAL_OP_HF_CLIENT_CONNECT, status); } static void confirm_cb(GIOChannel *chan, gpointer data) { struct device *dev; char address[18]; bdaddr_t bdaddr; GError *err = NULL; bt_io_get(chan, &err, BT_IO_OPT_DEST, address, BT_IO_OPT_DEST_BDADDR, &bdaddr, BT_IO_OPT_INVALID); if (err) { error("hf-client: confirm failed (%s)", err->message); g_error_free(err); goto drop; } DBG("Incoming connection from %s", address); dev = get_device(&bdaddr); if (!dev) { error("hf-client: There is other AG connected"); goto drop; } if (dev->state != HAL_HF_CLIENT_CONN_STATE_DISCONNECTED) { /* TODO: Handle colision */ error("hf-client: Connections is up or ongoing ?"); goto drop; } device_set_state(dev, HAL_HF_CLIENT_CONN_STATE_CONNECTING); if (!bt_io_accept(chan, connect_cb, dev, NULL, NULL)) { error("hf-client: failed to accept connection"); device_destroy(dev); goto drop; } return; drop: g_io_channel_shutdown(chan, TRUE, NULL); } static const struct ipc_handler cmd_handlers[] = { /* HAL_OP_HF_CLIENT_CONNECT */ { handle_connect, false, sizeof(struct hal_cmd_hf_client_connect) }, /* HAL_OP_HF_CLIENT_DISCONNECT */ { handle_disconnect, false, sizeof(struct hal_cmd_hf_client_disconnect) }, /* HAL_OP_HF_CLIENT_CONNECT_AUDIO */ { handle_connect_audio, false, sizeof(struct hal_cmd_hf_client_connect_audio) }, /* HAL_OP_HF_CLIENT_DISCONNECT_AUDIO */ { handle_disconnect_audio, false, sizeof(struct hal_cmd_hf_client_disconnect_audio) }, /* define HAL_OP_HF_CLIENT_START_VR */ { handle_start_vr, false, 0 }, /* define HAL_OP_HF_CLIENT_STOP_VR */ { handle_stop_vr, false, 0 }, /* HAL_OP_HF_CLIENT_VOLUME_CONTROL */ { handle_volume_control, false, sizeof(struct hal_cmd_hf_client_volume_control) }, /* HAL_OP_HF_CLIENT_DIAL */ { handle_dial, true, sizeof(struct hal_cmd_hf_client_dial) }, /* HAL_OP_HF_CLIENT_DIAL_MEMORY */ { handle_dial_memory, false, sizeof(struct hal_cmd_hf_client_dial_memory) }, /* HAL_OP_HF_CLIENT_CALL_ACTION */ { handle_call_action, false, sizeof(struct hal_cmd_hf_client_call_action) }, /* HAL_OP_HF_CLIENT_QUERY_CURRENT_CALLS */ { handle_query_current_calls, false, 0 }, /* HAL_OP_HF_CLIENT_QUERY_OPERATOR_NAME */ { handle_query_operator_name, false, 0 }, /* HAL_OP_HF_CLIENT_RETRIEVE_SUBSCR_INFO */ { handle_retrieve_subscr_info, false, 0 }, /* HAL_OP_HF_CLIENT_SEND_DTMF */ { handle_send_dtmf, false, sizeof(struct hal_cmd_hf_client_send_dtmf) }, /* HAL_OP_HF_CLIENT_GET_LAST_VOICE_TAG_NUM */ { handle_get_last_vc_tag_num, false, 0 }, }; static sdp_record_t *hfp_hf_record(void) { sdp_list_t *svclass_id, *pfseq, *apseq, *root; uuid_t root_uuid, svclass_uuid, ga_svclass_uuid; uuid_t l2cap_uuid, rfcomm_uuid; sdp_profile_desc_t profile; sdp_list_t *aproto, *proto[2]; sdp_record_t *record; sdp_data_t *channel, *features; uint16_t sdpfeat; uint8_t ch = HFP_HF_CHANNEL; record = sdp_record_alloc(); if (!record) return NULL; sdp_uuid16_create(&root_uuid, PUBLIC_BROWSE_GROUP); root = sdp_list_append(NULL, &root_uuid); sdp_set_browse_groups(record, root); sdp_uuid16_create(&svclass_uuid, HANDSFREE_SVCLASS_ID); svclass_id = sdp_list_append(NULL, &svclass_uuid); sdp_uuid16_create(&ga_svclass_uuid, GENERIC_AUDIO_SVCLASS_ID); svclass_id = sdp_list_append(svclass_id, &ga_svclass_uuid); sdp_set_service_classes(record, svclass_id); sdp_uuid16_create(&profile.uuid, HANDSFREE_PROFILE_ID); profile.version = 0x0106; pfseq = sdp_list_append(NULL, &profile); sdp_set_profile_descs(record, pfseq); sdp_uuid16_create(&l2cap_uuid, L2CAP_UUID); proto[0] = sdp_list_append(NULL, &l2cap_uuid); apseq = sdp_list_append(NULL, proto[0]); sdp_uuid16_create(&rfcomm_uuid, RFCOMM_UUID); proto[1] = sdp_list_append(NULL, &rfcomm_uuid); channel = sdp_data_alloc(SDP_UINT8, &ch); proto[1] = sdp_list_append(proto[1], channel); apseq = sdp_list_append(apseq, proto[1]); /* Codec Negotiation bit in SDP feature is different then in BRSF */ sdpfeat = hfp_hf_features & 0x0000003F; if (hfp_hf_features & HFP_HF_FEAT_CODEC) sdpfeat |= 0x00000020; else sdpfeat &= ~0x00000020; features = sdp_data_alloc(SDP_UINT16, &sdpfeat); sdp_attr_add(record, SDP_ATTR_SUPPORTED_FEATURES, features); aproto = sdp_list_append(NULL, apseq); sdp_set_access_protos(record, aproto); sdp_set_info_attr(record, "Hands-Free unit", NULL, NULL); sdp_data_free(channel); sdp_list_free(proto[0], NULL); sdp_list_free(proto[1], NULL); sdp_list_free(apseq, NULL); sdp_list_free(pfseq, NULL); sdp_list_free(aproto, NULL); sdp_list_free(root, NULL); sdp_list_free(svclass_id, NULL); return record; } static bool enable_hf_client(void) { sdp_record_t *rec; GError *err = NULL; hfp_hf_server = bt_io_listen(NULL, confirm_cb, NULL, NULL, &err, BT_IO_OPT_SOURCE_BDADDR, &adapter_addr, BT_IO_OPT_CHANNEL, HFP_HF_CHANNEL, BT_IO_OPT_SEC_LEVEL, BT_IO_SEC_MEDIUM, BT_IO_OPT_INVALID); if (!hfp_hf_server) { error("hf-client: Failed to listen on Handsfree rfcomm: %s", err->message); g_error_free(err); return false; } hfp_hf_features = HFP_HF_FEATURES; rec = hfp_hf_record(); if (!rec) { error("hf-client: Could not create service record"); goto failed; } if (bt_adapter_add_record(rec, 0) < 0) { error("hf-client: Failed to register service record"); sdp_record_free(rec); goto failed; } hfp_hf_record_id = rec->handle; return true; failed: g_io_channel_shutdown(hfp_hf_server, TRUE, NULL); g_io_channel_unref(hfp_hf_server); hfp_hf_server = NULL; return false; } static void cleanup_hfp_hf(void) { if (hfp_hf_server) { g_io_channel_shutdown(hfp_hf_server, TRUE, NULL); g_io_channel_unref(hfp_hf_server); hfp_hf_server = NULL; } if (hfp_hf_record_id > 0) { bt_adapter_remove_record(hfp_hf_record_id); hfp_hf_record_id = 0; } if (sco) { bt_sco_unref(sco); sco = NULL; } } static bool confirm_sco_cb(const bdaddr_t *addr, uint16_t *voice_settings) { struct device *dev; DBG(""); dev = find_device(addr); if (!dev || dev->state != HAL_HF_CLIENT_CONN_STATE_SLC_CONNECTED) { error("hf-client: No device or SLC not ready"); return false; } set_audio_state(dev, HAL_HF_CLIENT_AUDIO_STATE_CONNECTING); if (codec_negotiation_supported(dev) && dev->negotiated_codec != CODEC_ID_CVSD) *voice_settings = BT_VOICE_TRANSPARENT; else *voice_settings = BT_VOICE_CVSD_16BIT; return true; } static void connect_sco_cb(enum sco_status status, const bdaddr_t *addr) { struct device *dev; uint8_t audio_state; DBG("SCO Status %u", status); /* Device shall be there, just sanity check */ dev = find_device(addr); if (!dev) { error("hf-client: There is no device?"); return; } if (status != SCO_STATUS_OK) { audio_state = HAL_HF_CLIENT_AUDIO_STATE_DISCONNECTED; goto done; } if (dev->negotiated_codec == CODEC_ID_MSBC) audio_state = HAL_HF_CLIENT_AUDIO_STATE_CONNECTED_MSBC; else audio_state = HAL_HF_CLIENT_AUDIO_STATE_CONNECTED; done: set_audio_state(dev, audio_state); } static void disconnect_sco_cb(const bdaddr_t *addr) { struct device *dev; DBG(""); dev = find_device(addr); if (!dev) { error("hf-client: No device"); return; } set_audio_state(dev, HAL_HF_CLIENT_AUDIO_STATE_DISCONNECTED); } bool bt_hf_client_register(struct ipc *ipc, const bdaddr_t *addr) { DBG(""); devices = queue_new(); bacpy(&adapter_addr, addr); if (!enable_hf_client()) goto failed; sco = bt_sco_new(addr); if (!sco) { error("hf-client: Cannot create SCO. HFP AG is in use ?"); goto failed; } bt_sco_set_confirm_cb(sco, confirm_sco_cb); bt_sco_set_connect_cb(sco, connect_sco_cb); bt_sco_set_disconnect_cb(sco, disconnect_sco_cb); hal_ipc = ipc; ipc_register(hal_ipc, HAL_SERVICE_ID_HANDSFREE_CLIENT, cmd_handlers, G_N_ELEMENTS(cmd_handlers)); return true; failed: cleanup_hfp_hf(); queue_destroy(devices, free); devices = NULL; return false; } void bt_hf_client_unregister(void) { DBG(""); cleanup_hfp_hf(); queue_destroy(devices, (void *) device_destroy); devices = NULL; ipc_unregister(hal_ipc, HAL_SERVICE_ID_HANDSFREE); hal_ipc = NULL; } bluez-5.82/android/PaxHeaders/map-client.c0000644000000000000000000000005014015011623015454 xustar0020 atime=1743516867 20 ctime=1743591279 bluez-5.82/android/map-client.c0000644000000000000000000000776314015011623015152 0ustar00rootroot// SPDX-License-Identifier: LGPL-2.1-or-later /* * * BlueZ - Bluetooth protocol stack for Linux * * Copyright (C) 2014 Intel Corporation. All rights reserved. * * */ #ifdef HAVE_CONFIG_H #include #endif #include #include #include #include #include "lib/sdp.h" #include "lib/sdp_lib.h" #include "src/sdp-client.h" #include "ipc.h" #include "lib/bluetooth.h" #include "map-client.h" #include "src/log.h" #include "hal-msg.h" #include "ipc-common.h" #include "utils.h" #include "src/shared/util.h" static struct ipc *hal_ipc = NULL; static bdaddr_t adapter_addr; static int fill_mce_inst(void *buf, int32_t id, int32_t scn, int32_t msg_type, const void *name, uint8_t name_len) { struct hal_map_client_mas_instance *inst = buf; inst->id = id; inst->scn = scn; inst->msg_types = msg_type; inst->name_len = name_len; if (name_len) memcpy(inst->name, name, name_len); return sizeof(*inst) + name_len; } static void map_client_sdp_search_cb(sdp_list_t *recs, int err, gpointer data) { uint8_t buf[IPC_MTU]; struct hal_ev_map_client_remote_mas_instances *ev = (void *) buf; bdaddr_t *dst = data; sdp_list_t *list, *protos; uint8_t status; int32_t id, scn, msg_type, name_len, num_instances = 0; char *name; size_t size; size = sizeof(*ev); bdaddr2android(dst, &ev->bdaddr); if (err < 0) { error("mce: Unable to get SDP record: %s", strerror(-err)); status = HAL_STATUS_FAILED; goto fail; } for (list = recs; list != NULL; list = list->next) { sdp_record_t *rec = list->data; sdp_data_t *data; data = sdp_data_get(rec, SDP_ATTR_MAS_INSTANCE_ID); if (!data) { error("mce: cannot get mas instance id"); continue; } id = data->val.uint8; data = sdp_data_get(rec, SDP_ATTR_SVCNAME_PRIMARY); if (!data) { error("mce: cannot get mas instance name"); continue; } name = data->val.str; name_len = data->unitSize; data = sdp_data_get(rec, SDP_ATTR_SUPPORTED_MESSAGE_TYPES); if (!data) { error("mce: cannot get mas instance msg type"); continue; } msg_type = data->val.uint8; if (sdp_get_access_protos(rec, &protos) < 0) { error("mce: cannot get mas instance sdp protocol list"); continue; } scn = sdp_get_proto_port(protos, RFCOMM_UUID); sdp_list_foreach(protos, (sdp_list_func_t) sdp_list_free, NULL); sdp_list_free(protos, NULL); if (!scn) { error("mce: cannot get mas instance rfcomm channel"); continue; } size += fill_mce_inst(buf + size, id, scn, msg_type, name, name_len); num_instances++; } status = HAL_STATUS_SUCCESS; fail: ev->num_instances = num_instances; ev->status = status; ipc_send_notif(hal_ipc, HAL_SERVICE_ID_MAP_CLIENT, HAL_EV_MAP_CLIENT_REMOTE_MAS_INSTANCES, size, buf); } static void handle_get_instances(const void *buf, uint16_t len) { const struct hal_cmd_map_client_get_instances *cmd = buf; uint8_t status; bdaddr_t *dst; uuid_t uuid; DBG(""); dst = new0(bdaddr_t, 1); if (!dst) { error("mce: Fail to allocate cb data"); status = HAL_STATUS_FAILED; goto failed; } android2bdaddr(&cmd->bdaddr, dst); sdp_uuid16_create(&uuid, MAP_MSE_SVCLASS_ID); if (bt_search_service(&adapter_addr, dst, &uuid, map_client_sdp_search_cb, dst, free, 0)) { error("mce: Failed to search SDP details"); status = HAL_STATUS_FAILED; goto failed; } status = HAL_STATUS_SUCCESS; failed: ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_MAP_CLIENT, HAL_OP_MAP_CLIENT_GET_INSTANCES, status); } static const struct ipc_handler cmd_handlers[] = { /* HAL_OP_MAP_CLIENT_GET_INSTANCES */ { handle_get_instances, false, sizeof(struct hal_cmd_map_client_get_instances) }, }; bool bt_map_client_register(struct ipc *ipc, const bdaddr_t *addr, uint8_t mode) { DBG(""); bacpy(&adapter_addr, addr); hal_ipc = ipc; ipc_register(hal_ipc, HAL_SERVICE_ID_MAP_CLIENT, cmd_handlers, G_N_ELEMENTS(cmd_handlers)); return true; } void bt_map_client_unregister(void) { DBG(""); ipc_unregister(hal_ipc, HAL_SERVICE_ID_MAP_CLIENT); hal_ipc = NULL; } bluez-5.82/android/PaxHeaders/avctp.h0000644000000000000000000000005014015011623014545 xustar0020 atime=1743516866 20 ctime=1743591278 bluez-5.82/android/avctp.h0000644000000000000000000001210414015011623014224 0ustar00rootroot/* SPDX-License-Identifier: GPL-2.0-or-later */ /* * * BlueZ - Bluetooth protocol stack for Linux * * Copyright (C) 2006-2010 Nokia Corporation * Copyright (C) 2004-2010 Marcel Holtmann * * */ #define AVCTP_CONTROL_PSM 23 #define AVCTP_BROWSING_PSM 27 #define AVCTP_HEADER_LENGTH 3 #define AVC_HEADER_LENGTH 3 #define AVC_DATA_OFFSET AVCTP_HEADER_LENGTH + AVC_HEADER_LENGTH #define AVC_DATA_MTU 512 /* ctype entries */ #define AVC_CTYPE_CONTROL 0x0 #define AVC_CTYPE_STATUS 0x1 #define AVC_CTYPE_NOTIFY 0x3 #define AVC_CTYPE_NOT_IMPLEMENTED 0x8 #define AVC_CTYPE_ACCEPTED 0x9 #define AVC_CTYPE_REJECTED 0xA #define AVC_CTYPE_STABLE 0xC #define AVC_CTYPE_CHANGED 0xD #define AVC_CTYPE_INTERIM 0xF /* opcodes */ #define AVC_OP_VENDORDEP 0x00 #define AVC_OP_UNITINFO 0x30 #define AVC_OP_SUBUNITINFO 0x31 #define AVC_OP_PASSTHROUGH 0x7c /* subunits of interest */ #define AVC_SUBUNIT_PANEL 0x09 /* operands in passthrough commands */ #define AVC_SELECT 0x00 #define AVC_UP 0x01 #define AVC_DOWN 0x02 #define AVC_LEFT 0x03 #define AVC_RIGHT 0x04 #define AVC_ROOT_MENU 0x09 #define AVC_CONTENTS_MENU 0x0b #define AVC_FAVORITE_MENU 0x0c #define AVC_EXIT 0x0d #define AVC_ON_DEMAND_MENU 0x0e #define AVC_APPS_MENU 0x0f #define AVC_0 0x20 #define AVC_1 0x21 #define AVC_2 0x22 #define AVC_3 0x23 #define AVC_4 0x24 #define AVC_5 0x25 #define AVC_6 0x26 #define AVC_7 0x27 #define AVC_8 0x28 #define AVC_9 0x29 #define AVC_DOT 0x2a #define AVC_ENTER 0x2b #define AVC_CHANNEL_UP 0x30 #define AVC_CHANNEL_DOWN 0x31 #define AVC_CHANNEL_PREVIOUS 0x32 #define AVC_INPUT_SELECT 0x34 #define AVC_INFO 0x35 #define AVC_HELP 0x36 #define AVC_PAGE_UP 0x37 #define AVC_PAGE_DOWN 0x38 #define AVC_LOCK 0x3a #define AVC_POWER 0x40 #define AVC_VOLUME_UP 0x41 #define AVC_VOLUME_DOWN 0x42 #define AVC_MUTE 0x43 #define AVC_PLAY 0x44 #define AVC_STOP 0x45 #define AVC_PAUSE 0x46 #define AVC_RECORD 0x47 #define AVC_REWIND 0x48 #define AVC_FAST_FORWARD 0x49 #define AVC_EJECT 0x4a #define AVC_FORWARD 0x4b #define AVC_BACKWARD 0x4c #define AVC_LIST 0x4d #define AVC_F1 0x71 #define AVC_F2 0x72 #define AVC_F3 0x73 #define AVC_F4 0x74 #define AVC_F5 0x75 #define AVC_F6 0x76 #define AVC_F7 0x77 #define AVC_F8 0x78 #define AVC_F9 0x79 #define AVC_RED 0x7a #define AVC_GREEN 0x7b #define AVC_BLUE 0x7c #define AVC_YELLOW 0x7c #define AVC_VENDOR_UNIQUE 0x7e #define AVC_VENDOR_NEXT_GROUP 0x00 #define AVC_VENDOR_PREV_GROUP 0x01 struct avctp; typedef bool (*avctp_passthrough_cb) (struct avctp *session, uint8_t op, bool pressed, void *user_data); typedef ssize_t (*avctp_control_pdu_cb) (struct avctp *session, uint8_t transaction, uint8_t *code, uint8_t *subunit, uint8_t *operands, size_t operand_count, void *user_data); typedef gboolean (*avctp_rsp_cb) (struct avctp *session, uint8_t code, uint8_t subunit, uint8_t *operands, size_t operand_count, void *user_data); typedef gboolean (*avctp_browsing_rsp_cb) (struct avctp *session, uint8_t *operands, size_t operand_count, void *user_data); typedef ssize_t (*avctp_browsing_pdu_cb) (struct avctp *session, uint8_t transaction, uint8_t *operands, size_t operand_count, void *user_data); typedef void (*avctp_destroy_cb_t) (void *user_data); struct avctp *avctp_new(int fd, size_t imtu, size_t omtu, uint16_t version); void avctp_set_destroy_cb(struct avctp *session, avctp_destroy_cb_t cb, void *user_data); int avctp_init_uinput(struct avctp *session, const char *name, const char *address); int avctp_connect_browsing(struct avctp *session, int fd, size_t imtu, size_t omtu); void avctp_shutdown(struct avctp *session); unsigned int avctp_register_passthrough_handler(struct avctp *session, avctp_passthrough_cb cb, void *user_data); bool avctp_unregister_passthrough_handler(struct avctp *session, unsigned int id); unsigned int avctp_register_pdu_handler(struct avctp *session, uint8_t opcode, avctp_control_pdu_cb cb, void *user_data); bool avctp_unregister_pdu_handler(struct avctp *session, unsigned int id); unsigned int avctp_register_browsing_pdu_handler(struct avctp *session, avctp_browsing_pdu_cb cb, void *user_data, avctp_destroy_cb_t destroy); bool avctp_unregister_browsing_pdu_handler(struct avctp *session, unsigned int id); int avctp_send_passthrough(struct avctp *session, uint8_t op, uint8_t *params, size_t params_len); int avctp_send_vendor(struct avctp *session, uint8_t transaction, uint8_t code, uint8_t subunit, const struct iovec *iov, int iov_cnt); int avctp_send_vendor_req(struct avctp *session, uint8_t code, uint8_t subunit, const struct iovec *iov, int iov_cnt, avctp_rsp_cb func, void *user_data); int avctp_send_browsing(struct avctp *session, uint8_t transaction, const struct iovec *iov, int iov_cnt); int avctp_send_browsing_req(struct avctp *session, const struct iovec *iov, int iov_cnt, avctp_browsing_rsp_cb func, void *user_data); bluez-5.82/android/PaxHeaders/socket.c0000644000000000000000000000005014015011623014713 xustar0020 atime=1743516866 20 ctime=1743591278 bluez-5.82/android/socket.c0000644000000000000000000007213314015011623014402 0ustar00rootroot// SPDX-License-Identifier: LGPL-2.1-or-later /* * * BlueZ - Bluetooth protocol stack for Linux * * Copyright (C) 2013-2014 Intel Corporation. All rights reserved. * * */ #ifdef HAVE_CONFIG_H #include #endif #include #include #include #include #include "lib/bluetooth.h" #include "btio/btio.h" #include "lib/sdp.h" #include "lib/sdp_lib.h" #include "src/sdp-client.h" #include "src/sdpd.h" #include "src/log.h" #include "hal-msg.h" #include "ipc-common.h" #include "ipc.h" #include "utils.h" #include "bluetooth.h" #include "socket.h" #define RFCOMM_CHANNEL_MAX 30 #define OPP_DEFAULT_CHANNEL 9 #define HSP_AG_DEFAULT_CHANNEL 12 #define HFP_AG_DEFAULT_CHANNEL 13 #define PBAP_DEFAULT_CHANNEL 15 #define MAP_MAS_DEFAULT_CHANNEL 16 #define SVC_HINT_OBEX 0x10 /* Hardcoded MAP stuff needed for MAS SMS Instance.*/ #define DEFAULT_MAS_INSTANCE 0x00 #define MAP_MSG_TYPE_SMS_GSM 0x02 #define MAP_MSG_TYPE_SMS_CDMA 0x04 #define DEFAULT_MAS_MSG_TYPE (MAP_MSG_TYPE_SMS_GSM | MAP_MSG_TYPE_SMS_CDMA) static struct ipc *hal_ipc = NULL; struct rfcomm_sock { int channel; /* RFCOMM channel */ BtIOSecLevel sec_level; /* for socket to BT */ int bt_sock; guint bt_watch; /* for socket to HAL */ int jv_sock; guint jv_watch; bdaddr_t dst; uint32_t service_handle; uint8_t *buf; int buf_size; }; struct rfcomm_channel { bool reserved; struct rfcomm_sock *rfsock; }; static bdaddr_t adapter_addr; static uint8_t hal_mode = HAL_MODE_SOCKET_DEFAULT; static const uint8_t zero_uuid[16] = { 0 }; /* Simple list of RFCOMM connected sockets */ static GList *connections = NULL; static struct rfcomm_channel servers[RFCOMM_CHANNEL_MAX + 1]; static uint32_t test_sdp_record_uuid16 = 0; static uint32_t test_sdp_record_uuid32 = 0; static uint32_t test_sdp_record_uuid128 = 0; static int rfsock_set_buffer(struct rfcomm_sock *rfsock) { socklen_t len = sizeof(int); int rcv, snd, size, err; err = getsockopt(rfsock->bt_sock, SOL_SOCKET, SO_RCVBUF, &rcv, &len); if (err < 0) { int err = -errno; error("getsockopt(SO_RCVBUF): %s", strerror(-err)); return err; } err = getsockopt(rfsock->bt_sock, SOL_SOCKET, SO_SNDBUF, &snd, &len); if (err < 0) { int err = -errno; error("getsockopt(SO_SNDBUF): %s", strerror(-err)); return err; } size = MAX(rcv, snd); DBG("Set buffer size %d", size); rfsock->buf = g_malloc(size); rfsock->buf_size = size; return 0; } static void cleanup_rfsock(gpointer data) { struct rfcomm_sock *rfsock = data; DBG("rfsock %p bt_sock %d jv_sock %d", rfsock, rfsock->bt_sock, rfsock->jv_sock); if (rfsock->jv_sock >= 0) if (close(rfsock->jv_sock) < 0) error("close() fd %d failed: %s", rfsock->jv_sock, strerror(errno)); if (rfsock->bt_sock >= 0) if (close(rfsock->bt_sock) < 0) error("close() fd %d: failed: %s", rfsock->bt_sock, strerror(errno)); if (rfsock->bt_watch > 0) if (!g_source_remove(rfsock->bt_watch)) error("bt_watch source was not found"); if (rfsock->jv_watch > 0) if (!g_source_remove(rfsock->jv_watch)) error("stack_watch source was not found"); if (rfsock->service_handle) bt_adapter_remove_record(rfsock->service_handle); if (rfsock->buf) g_free(rfsock->buf); g_free(rfsock); } static struct rfcomm_sock *create_rfsock(int bt_sock, int *hal_sock) { int fds[2] = {-1, -1}; struct rfcomm_sock *rfsock; if (socketpair(AF_LOCAL, SOCK_STREAM, 0, fds) < 0) { error("socketpair(): %s", strerror(errno)); *hal_sock = -1; return NULL; } rfsock = g_new0(struct rfcomm_sock, 1); rfsock->jv_sock = fds[0]; *hal_sock = fds[1]; rfsock->bt_sock = bt_sock; DBG("rfsock %p", rfsock); if (bt_sock < 0) return rfsock; if (rfsock_set_buffer(rfsock) < 0) { cleanup_rfsock(rfsock); return NULL; } return rfsock; } static sdp_record_t *create_rfcomm_record(uint8_t chan, uuid_t *uuid, const char *svc_name, bool has_obex) { sdp_list_t *svclass_id; sdp_list_t *seq, *proto_seq, *pbg_seq; sdp_list_t *proto[3]; uuid_t l2cap_uuid, rfcomm_uuid, obex_uuid, pbg_uuid; sdp_data_t *channel; sdp_record_t *record; record = sdp_record_alloc(); if (!record) return NULL; record->handle = sdp_next_handle(); svclass_id = sdp_list_append(NULL, uuid); sdp_set_service_classes(record, svclass_id); sdp_uuid16_create(&l2cap_uuid, L2CAP_UUID); proto[0] = sdp_list_append(NULL, &l2cap_uuid); seq = sdp_list_append(NULL, proto[0]); sdp_uuid16_create(&rfcomm_uuid, RFCOMM_UUID); proto[1] = sdp_list_append(NULL, &rfcomm_uuid); channel = sdp_data_alloc(SDP_UINT8, &chan); proto[1] = sdp_list_append(proto[1], channel); seq = sdp_list_append(seq, proto[1]); if (has_obex) { sdp_uuid16_create(&obex_uuid, OBEX_UUID); proto[2] = sdp_list_append(NULL, &obex_uuid); seq = sdp_list_append(seq, proto[2]); } proto_seq = sdp_list_append(NULL, seq); sdp_set_access_protos(record, proto_seq); sdp_uuid16_create(&pbg_uuid, PUBLIC_BROWSE_GROUP); pbg_seq = sdp_list_append(NULL, &pbg_uuid); sdp_set_browse_groups(record, pbg_seq); if (svc_name) sdp_set_info_attr(record, svc_name, NULL, NULL); sdp_data_free(channel); sdp_list_free(proto[0], NULL); sdp_list_free(proto[1], NULL); if (has_obex) sdp_list_free(proto[2], NULL); sdp_list_free(seq, NULL); sdp_list_free(proto_seq, NULL); sdp_list_free(pbg_seq, NULL); sdp_list_free(svclass_id, NULL); return record; } static sdp_record_t *create_opp_record(uint8_t chan, const char *svc_name) { uint8_t formats[] = { 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0xff }; uint8_t dtd = SDP_UINT8; uuid_t uuid; sdp_list_t *seq; sdp_profile_desc_t profile[1]; void *dtds[sizeof(formats)], *values[sizeof(formats)]; sdp_data_t *formats_list; sdp_record_t *record; size_t i; sdp_uuid16_create(&uuid, OBEX_OBJPUSH_SVCLASS_ID); record = create_rfcomm_record(chan, &uuid, svc_name, true); if (!record) return NULL; sdp_uuid16_create(&profile[0].uuid, OBEX_OBJPUSH_PROFILE_ID); profile[0].version = 0x0100; seq = sdp_list_append(NULL, profile); sdp_set_profile_descs(record, seq); for (i = 0; i < sizeof(formats); i++) { dtds[i] = &dtd; values[i] = &formats[i]; } formats_list = sdp_seq_alloc(dtds, values, sizeof(formats)); sdp_attr_add(record, SDP_ATTR_SUPPORTED_FORMATS_LIST, formats_list); sdp_list_free(seq, NULL); return record; } static sdp_record_t *create_pbap_record(uint8_t chan, const char *svc_name) { sdp_list_t *seq; sdp_profile_desc_t profile[1]; uint8_t formats = 0x01; sdp_record_t *record; uuid_t uuid; sdp_uuid16_create(&uuid, PBAP_PSE_SVCLASS_ID); record = create_rfcomm_record(chan, &uuid, svc_name, true); if (!record) return NULL; sdp_uuid16_create(&profile[0].uuid, PBAP_PROFILE_ID); profile[0].version = 0x0101; seq = sdp_list_append(NULL, profile); sdp_set_profile_descs(record, seq); sdp_attr_add_new(record, SDP_ATTR_SUPPORTED_REPOSITORIES, SDP_UINT8, &formats); sdp_list_free(seq, NULL); return record; } static sdp_record_t *create_mas_record(uint8_t chan, const char *svc_name) { sdp_list_t *seq; sdp_profile_desc_t profile[1]; uint8_t minst, mtype; sdp_record_t *record; uuid_t uuid; int cnt, ret; switch (hal_mode) { case HAL_MODE_SOCKET_DYNAMIC_MAP: /* * Service name for MAP is passed as XXYYname * XX - instance * YY - message type */ ret = sscanf(svc_name, "%02hhx%02hhx%n", &minst, &mtype, &cnt); if (ret != 2 || cnt != 4) return NULL; svc_name += 4; break; case HAL_MODE_SOCKET_DEFAULT: minst = DEFAULT_MAS_INSTANCE; mtype = DEFAULT_MAS_MSG_TYPE; break; default: return NULL; } sdp_uuid16_create(&uuid, MAP_MSE_SVCLASS_ID); record = create_rfcomm_record(chan, &uuid, svc_name, true); if (!record) return NULL; sdp_uuid16_create(&profile[0].uuid, MAP_PROFILE_ID); profile[0].version = 0x0101; seq = sdp_list_append(NULL, profile); sdp_set_profile_descs(record, seq); sdp_attr_add_new(record, SDP_ATTR_MAS_INSTANCE_ID, SDP_UINT8, &minst); sdp_attr_add_new(record, SDP_ATTR_SUPPORTED_MESSAGE_TYPES, SDP_UINT8, &mtype); sdp_list_free(seq, NULL); return record; } static sdp_record_t *create_spp_record(uint8_t chan, const char *svc_name) { sdp_record_t *record; uuid_t uuid; sdp_uuid16_create(&uuid, SERIAL_PORT_SVCLASS_ID); record = create_rfcomm_record(chan, &uuid, svc_name, false); if (!record) return NULL; return record; } static sdp_record_t *create_app_record(uint8_t chan, const uint8_t *app_uuid, const char *svc_name) { sdp_record_t *record; uuid_t uuid; sdp_uuid128_create(&uuid, app_uuid); sdp_uuid128_to_uuid(&uuid); record = create_rfcomm_record(chan, &uuid, svc_name, false); if (!record) return NULL; return record; } static const struct profile_info { uint8_t uuid[16]; uint8_t channel; uint8_t svc_hint; BtIOSecLevel sec_level; sdp_record_t * (*create_record)(uint8_t chan, const char *svc_name); } profiles[] = { { .uuid = { 0x00, 0x00, 0x11, 0x08, 0x00, 0x00, 0x10, 0x00, 0x80, 0x00, 0x00, 0x80, 0x5F, 0x9B, 0x34, 0xFB }, .channel = HSP_AG_DEFAULT_CHANNEL, .svc_hint = 0, .sec_level = BT_IO_SEC_MEDIUM, .create_record = NULL }, { .uuid = { 0x00, 0x00, 0x11, 0x1F, 0x00, 0x00, 0x10, 0x00, 0x80, 0x00, 0x00, 0x80, 0x5F, 0x9B, 0x34, 0xFB }, .channel = HFP_AG_DEFAULT_CHANNEL, .svc_hint = 0, .sec_level = BT_IO_SEC_MEDIUM, .create_record = NULL }, { .uuid = { 0x00, 0x00, 0x11, 0x2F, 0x00, 0x00, 0x10, 0x00, 0x80, 0x00, 0x00, 0x80, 0x5F, 0x9B, 0x34, 0xFB }, .channel = PBAP_DEFAULT_CHANNEL, .svc_hint = SVC_HINT_OBEX, .sec_level = BT_IO_SEC_MEDIUM, .create_record = create_pbap_record }, { .uuid = { 0x00, 0x00, 0x11, 0x05, 0x00, 0x00, 0x10, 0x00, 0x80, 0x00, 0x00, 0x80, 0x5F, 0x9B, 0x34, 0xFB }, .channel = OPP_DEFAULT_CHANNEL, .svc_hint = SVC_HINT_OBEX, .sec_level = BT_IO_SEC_LOW, .create_record = create_opp_record }, { .uuid = { 0x00, 0x00, 0x11, 0x32, 0x00, 0x00, 0x10, 0x00, 0x80, 0x00, 0x00, 0x80, 0x5F, 0x9B, 0x34, 0xFB }, .channel = MAP_MAS_DEFAULT_CHANNEL, .svc_hint = SVC_HINT_OBEX, .sec_level = BT_IO_SEC_MEDIUM, .create_record = create_mas_record }, { .uuid = { 0x00, 0x00, 0x11, 0x01, 0x00, 0x00, 0x10, 0x00, 0x80, 0x00, 0x00, 0x80, 0x5F, 0x9B, 0x34, 0xFB }, .channel = 0, .svc_hint = 0, .sec_level = BT_IO_SEC_MEDIUM, .create_record = create_spp_record }, }; static uint32_t sdp_service_register(uint8_t channel, const uint8_t *uuid, const struct profile_info *profile, const void *svc_name) { sdp_record_t *record = NULL; uint8_t svc_hint = 0; if (profile && profile->create_record) { record = profile->create_record(channel, svc_name); svc_hint = profile->svc_hint; } else if (uuid) { record = create_app_record(channel, uuid, svc_name); } if (!record) return 0; if (bt_adapter_add_record(record, svc_hint) < 0) { error("Failed to register on SDP record"); sdp_record_free(record); return 0; } return record->handle; } static int bt_sock_send_fd(int sock_fd, const void *buf, int len, int send_fd) { ssize_t ret; struct msghdr msg; struct cmsghdr *cmsg; struct iovec iv; char cmsgbuf[CMSG_SPACE(sizeof(int))]; DBG("len %d sock_fd %d send_fd %d", len, sock_fd, send_fd); if (sock_fd == -1 || send_fd == -1) return -1; memset(&msg, 0, sizeof(msg)); memset(cmsgbuf, 0, sizeof(cmsgbuf)); msg.msg_control = cmsgbuf; msg.msg_controllen = sizeof(cmsgbuf); cmsg = CMSG_FIRSTHDR(&msg); cmsg->cmsg_level = SOL_SOCKET; cmsg->cmsg_type = SCM_RIGHTS; cmsg->cmsg_len = CMSG_LEN(sizeof(send_fd)); memcpy(CMSG_DATA(cmsg), &send_fd, sizeof(send_fd)); iv.iov_base = (unsigned char *) buf; iv.iov_len = len; msg.msg_iov = &iv; msg.msg_iovlen = 1; ret = sendmsg(sock_fd, &msg, MSG_NOSIGNAL); if (ret < 0) { error("sendmsg(): sock_fd %d send_fd %d: %s", sock_fd, send_fd, strerror(errno)); return ret; } return ret; } static const struct profile_info *get_profile_by_uuid(const uint8_t *uuid) { unsigned int i; for (i = 0; i < G_N_ELEMENTS(profiles); i++) { if (!memcmp(profiles[i].uuid, uuid, 16)) return &profiles[i]; } return NULL; } static int try_write_all(int fd, unsigned char *buf, int len) { int sent = 0; while (len > 0) { int written; written = write(fd, buf, len); if (written < 0) { if (errno == EINTR || errno == EAGAIN) continue; return -1; } if (!written) return 0; len -= written; buf += written; sent += written; } return sent; } static gboolean jv_sock_client_event_cb(GIOChannel *io, GIOCondition cond, gpointer data) { struct rfcomm_sock *rfsock = data; int len, sent; if (cond & G_IO_HUP) { DBG("Socket %d hang up", g_io_channel_unix_get_fd(io)); goto fail; } if (cond & (G_IO_ERR | G_IO_NVAL)) { error("Socket %d error", g_io_channel_unix_get_fd(io)); goto fail; } len = read(rfsock->jv_sock, rfsock->buf, rfsock->buf_size); if (len <= 0) { error("read(): %s", strerror(errno)); /* Read again */ return TRUE; } sent = try_write_all(rfsock->bt_sock, rfsock->buf, len); if (sent < 0) { error("write(): %s", strerror(errno)); goto fail; } return TRUE; fail: DBG("rfsock %p jv_sock %d cond %d", rfsock, rfsock->jv_sock, cond); connections = g_list_remove(connections, rfsock); cleanup_rfsock(rfsock); return FALSE; } static gboolean bt_sock_event_cb(GIOChannel *io, GIOCondition cond, gpointer data) { struct rfcomm_sock *rfsock = data; int len, sent; if (cond & G_IO_HUP) { DBG("Socket %d hang up", g_io_channel_unix_get_fd(io)); goto fail; } if (cond & (G_IO_ERR | G_IO_NVAL)) { error("Socket %d error", g_io_channel_unix_get_fd(io)); goto fail; } len = read(rfsock->bt_sock, rfsock->buf, rfsock->buf_size); if (len <= 0) { error("read(): %s", strerror(errno)); /* Read again */ return TRUE; } sent = try_write_all(rfsock->jv_sock, rfsock->buf, len); if (sent < 0) { error("write(): %s", strerror(errno)); goto fail; } return TRUE; fail: DBG("rfsock %p bt_sock %d cond %d", rfsock, rfsock->bt_sock, cond); connections = g_list_remove(connections, rfsock); cleanup_rfsock(rfsock); return FALSE; } static bool sock_send_accept(struct rfcomm_sock *rfsock, bdaddr_t *bdaddr, int fd_accepted) { struct hal_sock_connect_signal cmd; int len; DBG(""); cmd.size = sizeof(cmd); bdaddr2android(bdaddr, cmd.bdaddr); cmd.channel = rfsock->channel; cmd.status = 0; len = bt_sock_send_fd(rfsock->jv_sock, &cmd, sizeof(cmd), fd_accepted); if (len != sizeof(cmd)) { error("Error sending accept signal"); return false; } return true; } static gboolean jv_sock_server_event_cb(GIOChannel *io, GIOCondition cond, gpointer data) { struct rfcomm_sock *rfsock = data; DBG("rfsock %p jv_sock %d cond %d", rfsock, rfsock->jv_sock, cond); if (cond & G_IO_NVAL) return FALSE; if (cond & (G_IO_ERR | G_IO_HUP)) { servers[rfsock->channel].rfsock = NULL; cleanup_rfsock(rfsock); } return FALSE; } static void accept_cb(GIOChannel *io, GError *err, gpointer user_data) { struct rfcomm_sock *rfsock = user_data; struct rfcomm_sock *new_rfsock; GIOChannel *jv_io; GError *gerr = NULL; bdaddr_t dst; char address[18]; int new_sock; int hal_sock; guint id; GIOCondition cond; if (err) { error("%s", err->message); return; } bt_io_get(io, &gerr, BT_IO_OPT_DEST_BDADDR, &dst, BT_IO_OPT_INVALID); if (gerr) { error("%s", gerr->message); g_error_free(gerr); g_io_channel_shutdown(io, TRUE, NULL); return; } ba2str(&dst, address); DBG("Incoming connection from %s on channel %d (rfsock %p)", address, rfsock->channel, rfsock); new_sock = g_io_channel_unix_get_fd(io); new_rfsock = create_rfsock(new_sock, &hal_sock); if (!new_rfsock) { g_io_channel_shutdown(io, TRUE, NULL); return; } DBG("new rfsock %p bt_sock %d jv_sock %d hal_sock %d", new_rfsock, new_rfsock->bt_sock, new_rfsock->jv_sock, hal_sock); if (!sock_send_accept(rfsock, &dst, hal_sock)) { cleanup_rfsock(new_rfsock); return; } connections = g_list_append(connections, new_rfsock); /* Handle events from Android */ cond = G_IO_IN | G_IO_HUP | G_IO_ERR | G_IO_NVAL; jv_io = g_io_channel_unix_new(new_rfsock->jv_sock); id = g_io_add_watch(jv_io, cond, jv_sock_client_event_cb, new_rfsock); g_io_channel_unref(jv_io); new_rfsock->jv_watch = id; /* Handle rfcomm events */ cond = G_IO_IN | G_IO_HUP | G_IO_ERR | G_IO_NVAL; id = g_io_add_watch(io, cond, bt_sock_event_cb, new_rfsock); g_io_channel_set_close_on_unref(io, FALSE); new_rfsock->bt_watch = id; } static int find_free_channel(void) { int ch; /* channel 0 is reserver so we don't use it */ for (ch = 1; ch <= RFCOMM_CHANNEL_MAX; ch++) { struct rfcomm_channel *srv = &servers[ch]; if (!srv->reserved && srv->rfsock == NULL) return ch; } return 0; } static BtIOSecLevel get_sec_level(uint8_t flags) { /* * HAL_SOCK_FLAG_AUTH should require MITM but in our case setting * security to BT_IO_SEC_HIGH would also require 16-digits PIN code * for pre-2.1 devices which is not what Android expects. For this * reason we ignore this flag to not break apps which use "secure" * sockets (have both auth and encrypt flags set, there is no public * API in Android which should provide proper high security socket). */ return flags & HAL_SOCK_FLAG_ENCRYPT ? BT_IO_SEC_MEDIUM : BT_IO_SEC_LOW; } static uint8_t rfcomm_listen(int chan, const uint8_t *name, const uint8_t *uuid, uint8_t flags, int *hal_sock) { const struct profile_info *profile; struct rfcomm_sock *rfsock = NULL; BtIOSecLevel sec_level; GIOChannel *io, *jv_io; GIOCondition cond; GError *err = NULL; guint id; uuid_t uu; char uuid_str[32]; sdp_uuid128_create(&uu, uuid); sdp_uuid2strn(&uu, uuid_str, sizeof(uuid_str)); DBG("chan %d flags 0x%02x uuid %s name %s", chan, flags, uuid_str, name); if ((!memcmp(uuid, zero_uuid, sizeof(zero_uuid)) && chan <= 0) || (chan > RFCOMM_CHANNEL_MAX)) { error("Invalid rfcomm listen params"); return HAL_STATUS_INVALID; } profile = get_profile_by_uuid(uuid); if (!profile) { sec_level = get_sec_level(flags); } else { if (!profile->create_record) return HAL_STATUS_INVALID; chan = profile->channel; sec_level = profile->sec_level; } if (chan <= 0) chan = find_free_channel(); if (!chan) { error("No free channels"); return HAL_STATUS_BUSY; } if (servers[chan].rfsock != NULL) { error("Channel already registered (%d)", chan); return HAL_STATUS_BUSY; } DBG("chan %d sec_level %d", chan, sec_level); rfsock = create_rfsock(-1, hal_sock); if (!rfsock) return HAL_STATUS_FAILED; rfsock->channel = chan; io = bt_io_listen(accept_cb, NULL, rfsock, NULL, &err, BT_IO_OPT_SOURCE_BDADDR, &adapter_addr, BT_IO_OPT_CHANNEL, chan, BT_IO_OPT_SEC_LEVEL, sec_level, BT_IO_OPT_INVALID); if (!io) { error("Failed listen: %s", err->message); g_error_free(err); goto failed; } rfsock->bt_sock = g_io_channel_unix_get_fd(io); g_io_channel_set_close_on_unref(io, FALSE); g_io_channel_unref(io); /* Handle events from Android */ cond = G_IO_HUP | G_IO_ERR | G_IO_NVAL; jv_io = g_io_channel_unix_new(rfsock->jv_sock); id = g_io_add_watch_full(jv_io, G_PRIORITY_HIGH, cond, jv_sock_server_event_cb, rfsock, NULL); g_io_channel_unref(jv_io); rfsock->jv_watch = id; DBG("rfsock %p bt_sock %d jv_sock %d hal_sock %d", rfsock, rfsock->bt_sock, rfsock->jv_sock, *hal_sock); if (write(rfsock->jv_sock, &chan, sizeof(chan)) != sizeof(chan)) { error("Error sending RFCOMM channel"); goto failed; } rfsock->service_handle = sdp_service_register(chan, uuid, profile, name); servers[chan].rfsock = rfsock; return HAL_STATUS_SUCCESS; failed: cleanup_rfsock(rfsock); close(*hal_sock); return HAL_STATUS_FAILED; } static uint32_t add_test_record(uuid_t *uuid) { sdp_record_t *record; sdp_list_t *svclass_id; sdp_list_t *seq, *pbg_seq, *proto_seq, *ap_seq; sdp_list_t *proto, *proto1, *aproto; uuid_t l2cap_uuid, pbg_uuid, ap_uuid; record = sdp_record_alloc(); if (!record) return 0; record->handle = sdp_next_handle(); svclass_id = sdp_list_append(NULL, uuid); sdp_set_service_classes(record, svclass_id); sdp_uuid16_create(&l2cap_uuid, L2CAP_UUID); proto = sdp_list_append(NULL, &l2cap_uuid); seq = sdp_list_append(NULL, proto); proto_seq = sdp_list_append(NULL, seq); sdp_set_access_protos(record, proto_seq); sdp_uuid16_create(&pbg_uuid, PUBLIC_BROWSE_GROUP); pbg_seq = sdp_list_append(NULL, &pbg_uuid); sdp_set_browse_groups(record, pbg_seq); /* Additional Protocol Descriptor List */ sdp_uuid16_create(&ap_uuid, L2CAP_UUID); proto1 = sdp_list_append(NULL, &ap_uuid); ap_seq = sdp_list_append(NULL, proto1); aproto = sdp_list_append(NULL, ap_seq); sdp_set_add_access_protos(record, aproto); sdp_set_service_id(record, *uuid); sdp_set_record_state(record, 0); sdp_set_service_ttl(record, 0); sdp_set_service_avail(record, 0); sdp_set_url_attr(record, "http://www.bluez.org", "http://www.bluez.org", "http://www.bluez.org"); sdp_list_free(proto, NULL); sdp_list_free(seq, NULL); sdp_list_free(proto_seq, NULL); sdp_list_free(pbg_seq, NULL); sdp_list_free(svclass_id, NULL); if (bt_adapter_add_record(record, 0) < 0) { sdp_record_free(record); return 0; } return record->handle; } static void test_sdp_cleanup(void) { if (test_sdp_record_uuid16) { bt_adapter_remove_record(test_sdp_record_uuid16); test_sdp_record_uuid16 = 0; } if (test_sdp_record_uuid32) { bt_adapter_remove_record(test_sdp_record_uuid32); test_sdp_record_uuid32 = 0; } if (test_sdp_record_uuid128) { bt_adapter_remove_record(test_sdp_record_uuid128); test_sdp_record_uuid128 = 0; } } static void test_sdp_init(void) { char uuid128[] = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff}; uuid_t u; sdp_uuid16_create(&u, 0xffff); test_sdp_record_uuid16 = add_test_record(&u); sdp_uuid32_create(&u, 0xffffffff); test_sdp_record_uuid32 = add_test_record(&u); sdp_uuid128_create(&u, uuid128); test_sdp_record_uuid128 = add_test_record(&u); } static uint8_t l2cap_listen(int chan, const uint8_t *name, const uint8_t *uuid, uint8_t flags, int *hal_sock) { /* TODO be more strict here? */ if (strcmp("BlueZ", (const char *) name)) { error("socket: Only SDP test supported on L2CAP"); return HAL_STATUS_UNSUPPORTED; } test_sdp_cleanup(); test_sdp_init(); *hal_sock = -1; return HAL_STATUS_SUCCESS; } static void handle_listen(const void *buf, uint16_t len) { const struct hal_cmd_socket_listen *cmd = buf; uint8_t status; int hal_sock; switch (cmd->type) { case HAL_SOCK_RFCOMM: status = rfcomm_listen(cmd->channel, cmd->name, cmd->uuid, cmd->flags, &hal_sock); break; case HAL_SOCK_L2CAP: status = l2cap_listen(cmd->channel, cmd->name, cmd->uuid, cmd->flags, &hal_sock); break; case HAL_SOCK_SCO: status = HAL_STATUS_UNSUPPORTED; break; default: status = HAL_STATUS_INVALID; break; } if (status != HAL_STATUS_SUCCESS) goto failed; ipc_send_rsp_full(hal_ipc, HAL_SERVICE_ID_SOCKET, HAL_OP_SOCKET_LISTEN, 0, NULL, hal_sock); close(hal_sock); return; failed: ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_SOCKET, HAL_OP_SOCKET_LISTEN, status); } static bool sock_send_connect(struct rfcomm_sock *rfsock, bdaddr_t *bdaddr) { struct hal_sock_connect_signal cmd; int len; DBG(""); memset(&cmd, 0, sizeof(cmd)); cmd.size = sizeof(cmd); bdaddr2android(bdaddr, cmd.bdaddr); cmd.channel = rfsock->channel; cmd.status = 0; len = write(rfsock->jv_sock, &cmd, sizeof(cmd)); if (len < 0) { error("%s", strerror(errno)); return false; } if (len != sizeof(cmd)) { error("Error sending connect signal"); return false; } return true; } static void connect_cb(GIOChannel *io, GError *err, gpointer user_data) { struct rfcomm_sock *rfsock = user_data; bdaddr_t *dst = &rfsock->dst; GIOChannel *jv_io; char address[18]; guint id; GIOCondition cond; if (err) { error("%s", err->message); goto fail; } ba2str(dst, address); DBG("Connected to %s on channel %d (rfsock %p)", address, rfsock->channel, rfsock); if (!sock_send_connect(rfsock, dst)) goto fail; /* Handle events from Android */ cond = G_IO_IN | G_IO_HUP | G_IO_ERR | G_IO_NVAL; jv_io = g_io_channel_unix_new(rfsock->jv_sock); id = g_io_add_watch(jv_io, cond, jv_sock_client_event_cb, rfsock); g_io_channel_unref(jv_io); rfsock->jv_watch = id; /* Handle rfcomm events */ cond = G_IO_IN | G_IO_ERR | G_IO_HUP | G_IO_NVAL; id = g_io_add_watch(io, cond, bt_sock_event_cb, rfsock); g_io_channel_set_close_on_unref(io, FALSE); rfsock->bt_watch = id; return; fail: connections = g_list_remove(connections, rfsock); cleanup_rfsock(rfsock); } static bool do_rfcomm_connect(struct rfcomm_sock *rfsock, int chan) { GIOChannel *io; GError *gerr = NULL; DBG("rfsock %p sec_level %d chan %d", rfsock, rfsock->sec_level, chan); io = bt_io_connect(connect_cb, rfsock, NULL, &gerr, BT_IO_OPT_SOURCE_BDADDR, &adapter_addr, BT_IO_OPT_DEST_BDADDR, &rfsock->dst, BT_IO_OPT_CHANNEL, chan, BT_IO_OPT_SEC_LEVEL, rfsock->sec_level, BT_IO_OPT_INVALID); if (!io) { error("Failed connect: %s", gerr->message); g_error_free(gerr); return false; } g_io_channel_set_close_on_unref(io, FALSE); g_io_channel_unref(io); if (write(rfsock->jv_sock, &chan, sizeof(chan)) != sizeof(chan)) { error("Error sending RFCOMM channel"); return false; } rfsock->bt_sock = g_io_channel_unix_get_fd(io); rfsock_set_buffer(rfsock); rfsock->channel = chan; connections = g_list_append(connections, rfsock); return true; } static void sdp_search_cb(sdp_list_t *recs, int err, gpointer data) { struct rfcomm_sock *rfsock = data; sdp_list_t *list; int chan; DBG(""); if (err < 0) { error("Unable to get SDP record: %s", strerror(-err)); goto fail; } if (!recs || !recs->data) { error("No SDP records found"); goto fail; } for (list = recs; list != NULL; list = list->next) { sdp_record_t *rec = list->data; sdp_list_t *protos; if (sdp_get_access_protos(rec, &protos) < 0) { error("Unable to get proto list"); goto fail; } chan = sdp_get_proto_port(protos, RFCOMM_UUID); sdp_list_foreach(protos, (sdp_list_func_t) sdp_list_free, NULL); sdp_list_free(protos, NULL); if (chan) break; } if (chan <= 0) { error("Could not get RFCOMM channel %d", chan); goto fail; } DBG("Got RFCOMM channel %d", chan); if (do_rfcomm_connect(rfsock, chan)) return; fail: cleanup_rfsock(rfsock); } static uint8_t connect_rfcomm(const bdaddr_t *addr, int chan, const uint8_t *uuid, uint8_t flags, int *hal_sock) { struct rfcomm_sock *rfsock; char address[18]; uuid_t uu; char uuid_str[32]; sdp_uuid128_create(&uu, uuid); sdp_uuid2strn(&uu, uuid_str, sizeof(uuid_str)); ba2str(addr, address); DBG("addr %s chan %d flags 0x%02x uuid %s", address, chan, flags, uuid_str); if ((!memcmp(uuid, zero_uuid, sizeof(zero_uuid)) && chan <= 0) || !bacmp(addr, BDADDR_ANY)) { error("Invalid rfcomm connect params"); return HAL_STATUS_INVALID; } rfsock = create_rfsock(-1, hal_sock); if (!rfsock) return HAL_STATUS_FAILED; DBG("rfsock %p jv_sock %d hal_sock %d", rfsock, rfsock->jv_sock, *hal_sock); rfsock->sec_level = get_sec_level(flags); bacpy(&rfsock->dst, addr); if (!memcmp(uuid, zero_uuid, sizeof(zero_uuid))) { if (!do_rfcomm_connect(rfsock, chan)) goto failed; } else { if (bt_search_service(&adapter_addr, &rfsock->dst, &uu, sdp_search_cb, rfsock, NULL, 0) < 0) { error("Failed to search SDP records"); goto failed; } } return HAL_STATUS_SUCCESS; failed: cleanup_rfsock(rfsock); close(*hal_sock); return HAL_STATUS_FAILED; } static void handle_connect(const void *buf, uint16_t len) { const struct hal_cmd_socket_connect *cmd = buf; bdaddr_t bdaddr; uint8_t status; int hal_sock; DBG(""); android2bdaddr(cmd->bdaddr, &bdaddr); switch (cmd->type) { case HAL_SOCK_RFCOMM: status = connect_rfcomm(&bdaddr, cmd->channel, cmd->uuid, cmd->flags, &hal_sock); break; case HAL_SOCK_SCO: case HAL_SOCK_L2CAP: status = HAL_STATUS_UNSUPPORTED; break; default: status = HAL_STATUS_INVALID; break; } if (status != HAL_STATUS_SUCCESS) goto failed; ipc_send_rsp_full(hal_ipc, HAL_SERVICE_ID_SOCKET, HAL_OP_SOCKET_CONNECT, 0, NULL, hal_sock); close(hal_sock); return; failed: ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_SOCKET, HAL_OP_SOCKET_CONNECT, status); } static const struct ipc_handler cmd_handlers[] = { /* HAL_OP_SOCKET_LISTEN */ { handle_listen, false, sizeof(struct hal_cmd_socket_listen) }, /* HAL_OP_SOCKET_CONNECT */ { handle_connect, false, sizeof(struct hal_cmd_socket_connect) }, }; void bt_socket_register(struct ipc *ipc, const bdaddr_t *addr, uint8_t mode) { size_t i; DBG(""); hal_mode = mode; /* * make sure channels assigned for profiles are reserved and not used * for app services */ for (i = 0; i < G_N_ELEMENTS(profiles); i++) if (profiles[i].channel) servers[profiles[i].channel].reserved = true; bacpy(&adapter_addr, addr); hal_ipc = ipc; ipc_register(hal_ipc, HAL_SERVICE_ID_SOCKET, cmd_handlers, G_N_ELEMENTS(cmd_handlers)); } void bt_socket_unregister(void) { int ch; DBG(""); test_sdp_cleanup(); g_list_free_full(connections, cleanup_rfsock); for (ch = 0; ch <= RFCOMM_CHANNEL_MAX; ch++) if (servers[ch].rfsock) cleanup_rfsock(servers[ch].rfsock); memset(servers, 0, sizeof(servers)); ipc_unregister(hal_ipc, HAL_SERVICE_ID_SOCKET); hal_ipc = NULL; } bluez-5.82/android/PaxHeaders/pts-hsp.txt0000644000000000000000000000005012537515745015443 xustar0020 atime=1743516876 20 ctime=1743591289 bluez-5.82/android/pts-hsp.txt0000644000000000000000000000173212537515745015127 0ustar00rootrootPTS test results for HSP PTS version: 6.1 Tested: 13-May-2015 Android version: 5.1 Results: PASS test passed FAIL test failed INC test is inconclusive N/A test is disabled due to PICS setup ------------------------------------------------------------------------------- Test Name Result Notes ------------------------------------------------------------------------------- TC_AG_IAC_BV_01_I PASS TC_AG_IAC_BV_02_I N/A TC_AG_OAC_BV_01_I PASS TC_AG_ACR_BV_01_I PASS TC_AG_ACR_BV_02_I PASS TC_AG_ACT_BV_01_I PASS TC_AG_ACT_BV_02_I PASS TC_AG_RAV_BV_01_I PASS TC_AG_RAV_BV_02_I PASS TC_AG_RAV_BV_03_I PASS TC_AG_RAV_BV_04_I N/A TC_AG_RAV_BV_05_I N/A TC_AG_RAV_BV_06_I N/A TC_HS_IAC_BV_01_I N/A TC_HS_IAC_BV_02_I N/A TC_HS_OAC_BV_01_I N/A TC_HS_ACR_BV_01_I N/A TC_HS_ACR_BV_02_I N/A TC_HS_ACT_BV_01_I N/A TC_HS_ACT_BV_02_I N/A TC_HS_RAV_BV_01_I N/A TC_HS_RAV_BV_02_I N/A TC_HS_RAV_BV_03_I N/A TC_HS_RAV_BV_04_I N/A TC_HS_RAV_BV_05_I N/A TC_HS_RAV_BV_06_I N/A bluez-5.82/android/PaxHeaders/avdtp.c0000644000000000000000000000005014667536076014574 xustar0020 atime=1743516864 20 ctime=1743591278 bluez-5.82/android/avdtp.c0000644000000000000000000023710714667536076014267 0ustar00rootroot// SPDX-License-Identifier: GPL-2.0-or-later /* * * BlueZ - Bluetooth protocol stack for Linux * * Copyright (C) 2006-2010 Nokia Corporation * Copyright (C) 2004-2010 Marcel Holtmann * * */ #ifdef HAVE_CONFIG_H #include #endif #include #include #include #include #include #include #include #include #include #include #include #include "lib/bluetooth.h" #include "src/log.h" #include "src/shared/util.h" #include "src/shared/queue.h" #include "avdtp.h" #include "../profiles/audio/a2dp-codecs.h" #define MAX_SEID 0x3E static uint64_t seids; #ifndef MAX # define MAX(x, y) ((x) > (y) ? (x) : (y)) #endif #define AVDTP_DISCOVER 0x01 #define AVDTP_GET_CAPABILITIES 0x02 #define AVDTP_SET_CONFIGURATION 0x03 #define AVDTP_GET_CONFIGURATION 0x04 #define AVDTP_RECONFIGURE 0x05 #define AVDTP_OPEN 0x06 #define AVDTP_START 0x07 #define AVDTP_CLOSE 0x08 #define AVDTP_SUSPEND 0x09 #define AVDTP_ABORT 0x0A #define AVDTP_SECURITY_CONTROL 0x0B #define AVDTP_GET_ALL_CAPABILITIES 0x0C #define AVDTP_DELAY_REPORT 0x0D #define AVDTP_PKT_TYPE_SINGLE 0x00 #define AVDTP_PKT_TYPE_START 0x01 #define AVDTP_PKT_TYPE_CONTINUE 0x02 #define AVDTP_PKT_TYPE_END 0x03 #define AVDTP_MSG_TYPE_COMMAND 0x00 #define AVDTP_MSG_TYPE_GEN_REJECT 0x01 #define AVDTP_MSG_TYPE_ACCEPT 0x02 #define AVDTP_MSG_TYPE_REJECT 0x03 #define REQ_TIMEOUT 6 #define ABORT_TIMEOUT 2 #define DISCONNECT_TIMEOUT 1 #define START_TIMEOUT 1 #if __BYTE_ORDER == __LITTLE_ENDIAN struct avdtp_common_header { uint8_t message_type:2; uint8_t packet_type:2; uint8_t transaction:4; } __attribute__ ((packed)); struct avdtp_single_header { uint8_t message_type:2; uint8_t packet_type:2; uint8_t transaction:4; uint8_t signal_id:6; uint8_t rfa0:2; } __attribute__ ((packed)); struct avdtp_start_header { uint8_t message_type:2; uint8_t packet_type:2; uint8_t transaction:4; uint8_t no_of_packets; uint8_t signal_id:6; uint8_t rfa0:2; } __attribute__ ((packed)); struct avdtp_continue_header { uint8_t message_type:2; uint8_t packet_type:2; uint8_t transaction:4; } __attribute__ ((packed)); struct seid_info { uint8_t rfa0:1; uint8_t inuse:1; uint8_t seid:6; uint8_t rfa2:3; uint8_t type:1; uint8_t media_type:4; } __attribute__ ((packed)); struct seid { uint8_t rfa0:2; uint8_t seid:6; } __attribute__ ((packed)); #elif __BYTE_ORDER == __BIG_ENDIAN struct avdtp_common_header { uint8_t transaction:4; uint8_t packet_type:2; uint8_t message_type:2; } __attribute__ ((packed)); struct avdtp_single_header { uint8_t transaction:4; uint8_t packet_type:2; uint8_t message_type:2; uint8_t rfa0:2; uint8_t signal_id:6; } __attribute__ ((packed)); struct avdtp_start_header { uint8_t transaction:4; uint8_t packet_type:2; uint8_t message_type:2; uint8_t no_of_packets; uint8_t rfa0:2; uint8_t signal_id:6; } __attribute__ ((packed)); struct avdtp_continue_header { uint8_t transaction:4; uint8_t packet_type:2; uint8_t message_type:2; } __attribute__ ((packed)); struct seid_info { uint8_t seid:6; uint8_t inuse:1; uint8_t rfa0:1; uint8_t media_type:4; uint8_t type:1; uint8_t rfa2:3; } __attribute__ ((packed)); struct seid { uint8_t seid:6; uint8_t rfa0:2; } __attribute__ ((packed)); #else #error "Unknown byte order" #endif /* packets */ struct discover_resp { struct seid_info seps[0]; } __attribute__ ((packed)); struct getcap_resp { uint8_t caps[0]; } __attribute__ ((packed)); struct start_req { struct seid first_seid; struct seid other_seids[0]; } __attribute__ ((packed)); struct suspend_req { struct seid first_seid; struct seid other_seids[0]; } __attribute__ ((packed)); struct seid_rej { uint8_t error; } __attribute__ ((packed)); struct conf_rej { uint8_t category; uint8_t error; } __attribute__ ((packed)); #if __BYTE_ORDER == __LITTLE_ENDIAN struct seid_req { uint8_t rfa0:2; uint8_t acp_seid:6; } __attribute__ ((packed)); struct setconf_req { uint8_t rfa0:2; uint8_t acp_seid:6; uint8_t rfa1:2; uint8_t int_seid:6; uint8_t caps[0]; } __attribute__ ((packed)); struct stream_rej { uint8_t rfa0:2; uint8_t acp_seid:6; uint8_t error; } __attribute__ ((packed)); struct reconf_req { uint8_t rfa0:2; uint8_t acp_seid:6; uint8_t serv_cap; uint8_t serv_cap_len; uint8_t caps[0]; } __attribute__ ((packed)); struct delay_req { uint8_t rfa0:2; uint8_t acp_seid:6; uint16_t delay; } __attribute__ ((packed)); #elif __BYTE_ORDER == __BIG_ENDIAN struct seid_req { uint8_t acp_seid:6; uint8_t rfa0:2; } __attribute__ ((packed)); struct setconf_req { uint8_t acp_seid:6; uint8_t rfa0:2; uint8_t int_seid:6; uint8_t rfa1:2; uint8_t caps[0]; } __attribute__ ((packed)); struct stream_rej { uint8_t acp_seid:6; uint8_t rfa0:2; uint8_t error; } __attribute__ ((packed)); struct reconf_req { uint8_t acp_seid:6; uint8_t rfa0:2; uint8_t serv_cap; uint8_t serv_cap_len; uint8_t caps[0]; } __attribute__ ((packed)); struct delay_req { uint8_t acp_seid:6; uint8_t rfa0:2; uint16_t delay; } __attribute__ ((packed)); #else #error "Unknown byte order" #endif struct in_buf { gboolean active; int no_of_packets; uint8_t transaction; uint8_t message_type; uint8_t signal_id; uint8_t buf[1024]; uint8_t data_size; }; struct pending_req { uint8_t transaction; uint8_t signal_id; void *data; size_t data_size; struct avdtp_stream *stream; /* Set if the request targeted a stream */ guint timeout; gboolean collided; }; struct avdtp_remote_sep { uint8_t seid; uint8_t type; uint8_t media_type; struct avdtp_service_capability *codec; gboolean delay_reporting; GSList *caps; /* of type struct avdtp_service_capability */ struct avdtp_stream *stream; }; struct avdtp_local_sep { avdtp_state_t state; struct avdtp_stream *stream; struct seid_info info; uint8_t codec; uint32_t vndcodec_vendor; uint16_t vndcodec_codec; gboolean delay_reporting; GSList *caps; struct avdtp_sep_ind *ind; struct avdtp_sep_cfm *cfm; void *user_data; }; struct stream_callback { avdtp_stream_state_cb cb; void *user_data; unsigned int id; }; struct discover_callback { unsigned int id; avdtp_discover_cb_t cb; void *user_data; }; struct disconnect_callback { unsigned int id; avdtp_disconnect_cb_t cb; void *user_data; }; struct avdtp_stream { GIOChannel *io; uint16_t imtu; uint16_t omtu; struct avdtp *session; struct avdtp_local_sep *lsep; uint8_t rseid; GSList *caps; GSList *callbacks; struct avdtp_service_capability *codec; guint io_id; /* Transport GSource ID */ guint timer; /* Waiting for other side to close or open * the transport channel */ gboolean open_acp; /* If we are in ACT role for Open */ gboolean close_int; /* If we are in INT role for Close */ gboolean abort_int; /* If we are in INT role for Abort */ guint start_timer; /* Wait START command timer */ gboolean delay_reporting; uint16_t delay; /* AVDTP 1.3 Delay Reporting feature */ gboolean starting; /* only valid while sep state == OPEN */ }; /* Structure describing an AVDTP connection between two devices */ struct avdtp { unsigned int ref; uint16_t version; guint auth_id; GIOChannel *io; guint io_id; GSList *seps; /* Elements of type struct avdtp_remote_sep * */ struct queue *lseps; /* Elements of type struct avdtp_local_sep * */ GSList *streams; /* Elements of type struct avdtp_stream * */ GSList *req_queue; /* Elements of type struct pending_req * */ GSList *prio_queue; /* Same as req_queue but is processed before it */ struct avdtp_stream *pending_open; uint16_t imtu; uint16_t omtu; struct in_buf in; char *buf; struct discover_callback *discover; struct pending_req *req; GSList *disconnect; bool shutdown; }; static int send_request(struct avdtp *session, gboolean priority, struct avdtp_stream *stream, uint8_t signal_id, void *buffer, size_t size); static gboolean avdtp_parse_resp(struct avdtp *session, struct avdtp_stream *stream, uint8_t transaction, uint8_t signal_id, void *buf, int size); static gboolean avdtp_parse_rej(struct avdtp *session, struct avdtp_stream *stream, uint8_t transaction, uint8_t signal_id, void *buf, int size); static int process_queue(struct avdtp *session); static void avdtp_sep_set_state(struct avdtp *session, struct avdtp_local_sep *sep, avdtp_state_t state); static const char *avdtp_statestr(avdtp_state_t state) { switch (state) { case AVDTP_STATE_IDLE: return "IDLE"; case AVDTP_STATE_CONFIGURED: return "CONFIGURED"; case AVDTP_STATE_OPEN: return "OPEN"; case AVDTP_STATE_STREAMING: return "STREAMING"; case AVDTP_STATE_CLOSING: return "CLOSING"; case AVDTP_STATE_ABORTING: return "ABORTING"; default: return ""; } } static gboolean try_send(int sk, void *data, size_t len) { int err; do { err = send(sk, data, len, 0); } while (err < 0 && errno == EINTR); if (err < 0) { error("send: %s (%d)", strerror(errno), errno); return FALSE; } else if ((size_t) err != len) { error("try_send: complete buffer not sent (%d/%zu bytes)", err, len); return FALSE; } return TRUE; } static gboolean avdtp_send(struct avdtp *session, uint8_t transaction, uint8_t message_type, uint8_t signal_id, void *data, size_t len) { unsigned int cont_fragments, sent; struct avdtp_start_header start; struct avdtp_continue_header cont; int sock; if (session->io == NULL) { error("avdtp_send: session is closed"); return FALSE; } sock = g_io_channel_unix_get_fd(session->io); /* Single packet - no fragmentation */ if (sizeof(struct avdtp_single_header) + len <= session->omtu) { struct avdtp_single_header single; memset(&single, 0, sizeof(single)); single.transaction = transaction; single.packet_type = AVDTP_PKT_TYPE_SINGLE; single.message_type = message_type; single.signal_id = signal_id; memcpy(session->buf, &single, sizeof(single)); memcpy(session->buf + sizeof(single), data, len); return try_send(sock, session->buf, sizeof(single) + len); } /* Check if there is enough space to start packet */ if (session->omtu < sizeof(start)) { error("No enough space to fragment packet"); return FALSE; } /* Count the number of needed fragments */ cont_fragments = (len - (session->omtu - sizeof(start))) / (session->omtu - sizeof(cont)) + 1; DBG("%zu bytes split into %d fragments", len, cont_fragments + 1); /* Send the start packet */ memset(&start, 0, sizeof(start)); start.transaction = transaction; start.packet_type = AVDTP_PKT_TYPE_START; start.message_type = message_type; start.no_of_packets = cont_fragments + 1; start.signal_id = signal_id; memcpy(session->buf, &start, sizeof(start)); memcpy(session->buf + sizeof(start), data, session->omtu - sizeof(start)); if (!try_send(sock, session->buf, session->omtu)) return FALSE; DBG("first packet with %zu bytes sent", session->omtu - sizeof(start)); sent = session->omtu - sizeof(start); /* Send the continue fragments and the end packet */ while (sent < len) { int left, to_copy; left = len - sent; if (left + sizeof(cont) > session->omtu) { cont.packet_type = AVDTP_PKT_TYPE_CONTINUE; to_copy = session->omtu - sizeof(cont); DBG("sending continue with %d bytes", to_copy); } else { cont.packet_type = AVDTP_PKT_TYPE_END; to_copy = left; DBG("sending end with %d bytes", to_copy); } cont.transaction = transaction; cont.message_type = message_type; memcpy(session->buf, &cont, sizeof(cont)); memcpy(session->buf + sizeof(cont), data + sent, to_copy); if (!try_send(sock, session->buf, to_copy + sizeof(cont))) return FALSE; sent += to_copy; } return TRUE; } static void pending_req_free(void *data) { struct pending_req *req = data; if (req->timeout) g_source_remove(req->timeout); g_free(req->data); g_free(req); } static void close_stream(struct avdtp_stream *stream) { int sock; if (stream->io == NULL) return; sock = g_io_channel_unix_get_fd(stream->io); shutdown(sock, SHUT_RDWR); g_io_channel_shutdown(stream->io, FALSE, NULL); g_io_channel_unref(stream->io); stream->io = NULL; } static gboolean stream_close_timeout(gpointer user_data) { struct avdtp_stream *stream = user_data; DBG("Timed out waiting for peer to close the transport channel"); stream->timer = 0; close_stream(stream); return FALSE; } static gboolean stream_open_timeout(gpointer user_data) { struct avdtp_stream *stream = user_data; DBG("Timed out waiting for peer to open the transport channel"); stream->timer = 0; stream->session->pending_open = NULL; avdtp_abort(stream->session, stream); return FALSE; } void avdtp_error_init(struct avdtp_error *err, uint8_t category, int id) { err->category = category; if (category == AVDTP_ERRNO) err->err.posix_errno = id; else err->err.error_code = id; } uint8_t avdtp_error_category(struct avdtp_error *err) { return err->category; } int avdtp_error_error_code(struct avdtp_error *err) { assert(err->category != AVDTP_ERRNO); return err->err.error_code; } int avdtp_error_posix_errno(struct avdtp_error *err) { assert(err->category == AVDTP_ERRNO); return err->err.posix_errno; } static struct avdtp_stream *find_stream_by_rseid(struct avdtp *session, uint8_t rseid) { GSList *l; for (l = session->streams; l != NULL; l = g_slist_next(l)) { struct avdtp_stream *stream = l->data; if (stream->rseid == rseid) return stream; } return NULL; } static struct avdtp_remote_sep *find_remote_sep(GSList *seps, uint8_t seid) { GSList *l; for (l = seps; l != NULL; l = g_slist_next(l)) { struct avdtp_remote_sep *sep = l->data; if (sep->seid == seid) return sep; } return NULL; } static void stream_free(void *data) { struct avdtp_stream *stream = data; struct avdtp_remote_sep *rsep; stream->lsep->info.inuse = 0; stream->lsep->stream = NULL; rsep = find_remote_sep(stream->session->seps, stream->rseid); if (rsep) rsep->stream = NULL; if (stream->timer) g_source_remove(stream->timer); if (stream->start_timer > 0) g_source_remove(stream->start_timer); if (stream->io) close_stream(stream); if (stream->io_id) g_source_remove(stream->io_id); g_slist_free_full(stream->callbacks, g_free); g_slist_free_full(stream->caps, g_free); g_free(stream); } static gboolean transport_cb(GIOChannel *chan, GIOCondition cond, gpointer data) { struct avdtp_stream *stream = data; struct avdtp_local_sep *sep = stream->lsep; if (stream->close_int && sep->cfm && sep->cfm->close) sep->cfm->close(stream->session, sep, stream, NULL, sep->user_data); if (!(cond & G_IO_NVAL)) close_stream(stream); stream->io_id = 0; if (!stream->abort_int) avdtp_sep_set_state(stream->session, sep, AVDTP_STATE_IDLE); return FALSE; } static void handle_transport_connect(struct avdtp *session, GIOChannel *io, uint16_t imtu, uint16_t omtu) { struct avdtp_stream *stream = session->pending_open; struct avdtp_local_sep *sep = stream->lsep; session->pending_open = NULL; if (stream->timer) { g_source_remove(stream->timer); stream->timer = 0; } if (io == NULL) return; if (stream->io == NULL) stream->io = g_io_channel_ref(io); stream->omtu = omtu; stream->imtu = imtu; avdtp_sep_set_state(session, sep, AVDTP_STATE_OPEN); stream->io_id = g_io_add_watch(io, G_IO_ERR | G_IO_HUP | G_IO_NVAL, (GIOFunc) transport_cb, stream); } static int pending_req_cmp(gconstpointer a, gconstpointer b) { const struct pending_req *req = a; const struct avdtp_stream *stream = b; if (req->stream == stream) return 0; return -1; } static void cleanup_queue(struct avdtp *session, struct avdtp_stream *stream) { GSList *l; struct pending_req *req; while ((l = g_slist_find_custom(session->prio_queue, stream, pending_req_cmp))) { req = l->data; pending_req_free(req); session->prio_queue = g_slist_remove(session->prio_queue, req); } while ((l = g_slist_find_custom(session->req_queue, stream, pending_req_cmp))) { req = l->data; pending_req_free(req); session->req_queue = g_slist_remove(session->req_queue, req); } } static void handle_unanswered_req(struct avdtp *session, struct avdtp_stream *stream) { struct pending_req *req; struct avdtp_local_sep *lsep; struct avdtp_error err; if (!session->req->timeout) /* Request is in process */ return; if (session->req->signal_id == AVDTP_ABORT) { /* Avoid freeing the Abort request here */ DBG("handle_unanswered_req: Abort req, returning"); session->req->stream = NULL; return; } req = session->req; session->req = NULL; avdtp_error_init(&err, AVDTP_ERRNO, EIO); lsep = stream->lsep; switch (req->signal_id) { case AVDTP_RECONFIGURE: error("No reply to Reconfigure request"); if (lsep && lsep->cfm && lsep->cfm->reconfigure) lsep->cfm->reconfigure(session, lsep, stream, &err, lsep->user_data); break; case AVDTP_OPEN: error("No reply to Open request"); if (lsep && lsep->cfm && lsep->cfm->open) lsep->cfm->open(session, lsep, stream, &err, lsep->user_data); break; case AVDTP_START: error("No reply to Start request"); if (lsep && lsep->cfm && lsep->cfm->start) lsep->cfm->start(session, lsep, stream, &err, lsep->user_data); break; case AVDTP_SUSPEND: error("No reply to Suspend request"); if (lsep && lsep->cfm && lsep->cfm->suspend) lsep->cfm->suspend(session, lsep, stream, &err, lsep->user_data); break; case AVDTP_CLOSE: error("No reply to Close request"); if (lsep && lsep->cfm && lsep->cfm->close) lsep->cfm->close(session, lsep, stream, &err, lsep->user_data); break; case AVDTP_SET_CONFIGURATION: error("No reply to SetConfiguration request"); if (lsep && lsep->cfm && lsep->cfm->set_configuration) lsep->cfm->set_configuration(session, lsep, stream, &err, lsep->user_data); } pending_req_free(req); } static void avdtp_sep_set_state(struct avdtp *session, struct avdtp_local_sep *sep, avdtp_state_t state) { struct avdtp_stream *stream = sep->stream; avdtp_state_t old_state; struct avdtp_error err, *err_ptr = NULL; GSList *l; if (!stream) { error("Error changing sep state: stream not available"); return; } if (sep->state == state) { avdtp_error_init(&err, AVDTP_ERRNO, EIO); DBG("stream state change failed: %s", avdtp_strerror(&err)); err_ptr = &err; } else { err_ptr = NULL; DBG("stream state changed: %s -> %s", avdtp_statestr(sep->state), avdtp_statestr(state)); } old_state = sep->state; sep->state = state; switch (state) { case AVDTP_STATE_CONFIGURED: if (sep->info.type == AVDTP_SEP_TYPE_SINK) avdtp_delay_report(session, stream, stream->delay); break; case AVDTP_STATE_OPEN: stream->starting = FALSE; break; case AVDTP_STATE_STREAMING: if (stream->start_timer) { g_source_remove(stream->start_timer); stream->start_timer = 0; } stream->open_acp = FALSE; break; case AVDTP_STATE_CLOSING: case AVDTP_STATE_ABORTING: if (stream->start_timer) { g_source_remove(stream->start_timer); stream->start_timer = 0; } break; case AVDTP_STATE_IDLE: if (stream->start_timer) { g_source_remove(stream->start_timer); stream->start_timer = 0; } if (session->pending_open == stream) handle_transport_connect(session, NULL, 0, 0); if (session->req && session->req->stream == stream) handle_unanswered_req(session, stream); /* Remove pending commands for this stream from the queue */ cleanup_queue(session, stream); break; default: break; } l = stream->callbacks; while (l != NULL) { struct stream_callback *cb = l->data; l = g_slist_next(l); cb->cb(stream, old_state, state, err_ptr, cb->user_data); } if (state == AVDTP_STATE_IDLE && g_slist_find(session->streams, stream)) { session->streams = g_slist_remove(session->streams, stream); stream_free(stream); } if (session->io && session->shutdown && session->streams == NULL) { int sock = g_io_channel_unix_get_fd(session->io); shutdown(sock, SHUT_RDWR); } } static void finalize_discovery(struct avdtp *session, int err) { struct discover_callback *discover = session->discover; struct avdtp_error avdtp_err; if (!discover) return; session->discover = NULL; avdtp_error_init(&avdtp_err, AVDTP_ERRNO, err); if (discover->id > 0) g_source_remove(discover->id); if (discover->cb) discover->cb(session, session->seps, err ? &avdtp_err : NULL, discover->user_data); g_free(discover); } static void release_stream(struct avdtp_stream *stream, struct avdtp *session) { struct avdtp_local_sep *sep = stream->lsep; if (sep->cfm && sep->cfm->abort && (sep->state != AVDTP_STATE_ABORTING || stream->abort_int)) sep->cfm->abort(session, sep, stream, NULL, sep->user_data); avdtp_sep_set_state(session, sep, AVDTP_STATE_IDLE); } static void sep_free(gpointer data) { struct avdtp_remote_sep *sep = data; g_slist_free_full(sep->caps, g_free); g_free(sep); } static void avdtp_free(void *data) { struct avdtp *session = data; DBG("%p", session); g_slist_free_full(session->streams, stream_free); if (session->io) { g_io_channel_shutdown(session->io, FALSE, NULL); g_io_channel_unref(session->io); } if (session->io_id) { g_source_remove(session->io_id); session->io_id = 0; } if (session->req) pending_req_free(session->req); g_slist_free_full(session->req_queue, pending_req_free); g_slist_free_full(session->prio_queue, pending_req_free); g_slist_free_full(session->seps, sep_free); g_slist_free_full(session->disconnect, g_free); /* Free copy of the SEP list */ session->lseps = NULL; g_free(session->buf); g_free(session); } static void process_disconnect(void *data) { struct disconnect_callback *callback = data; callback->cb(callback->user_data); g_free(callback); } static void connection_lost(struct avdtp *session, int err) { DBG("Disconnected: %s (%d)", strerror(err), err); g_slist_foreach(session->streams, (GFunc) release_stream, session); session->streams = NULL; avdtp_ref(session); finalize_discovery(session, err); g_slist_free_full(session->disconnect, process_disconnect); session->disconnect = NULL; avdtp_unref(session); } void avdtp_unref(struct avdtp *session) { if (!session) return; session->ref--; DBG("%p: ref=%d", session, session->ref); if (session->ref > 0) return; finalize_discovery(session, ECONNABORTED); avdtp_free(session); } struct avdtp *avdtp_ref(struct avdtp *session) { session->ref++; DBG("%p: ref=%d", session, session->ref); return session; } static bool match_by_seid(const void *data, const void *user_data) { const struct avdtp_local_sep *sep = data; uint8_t seid = PTR_TO_UINT(user_data); return sep->info.seid == seid; } static struct avdtp_local_sep *find_local_sep_by_seid(struct avdtp *session, uint8_t seid) { return queue_find(session->lseps, match_by_seid, INT_TO_PTR(seid)); } struct avdtp_remote_sep *avdtp_find_remote_sep(struct avdtp *session, struct avdtp_local_sep *lsep) { GSList *l; if (lsep->info.inuse) return NULL; for (l = session->seps; l != NULL; l = g_slist_next(l)) { struct avdtp_remote_sep *sep = l->data; struct avdtp_service_capability *cap; struct avdtp_media_codec_capability *codec_data; /* Type must be different: source <-> sink */ if (sep->type == lsep->info.type) continue; if (sep->media_type != lsep->info.media_type) continue; if (!sep->codec) continue; cap = sep->codec; codec_data = (void *) cap->data; if (codec_data->media_codec_type != lsep->codec) continue; /* FIXME: Add Vendor Specific Codec match to SEP callback */ if (lsep->codec == A2DP_CODEC_VENDOR) { a2dp_vendor_codec_t *vndcodec = (void *) codec_data->data; if (A2DP_GET_VENDOR_ID(*vndcodec) != lsep->vndcodec_vendor) continue; if (A2DP_GET_CODEC_ID(*vndcodec) != lsep->vndcodec_codec) continue; } if (sep->stream == NULL) return sep; } return NULL; } static GSList *caps_to_list(uint8_t *data, int size, struct avdtp_service_capability **codec, gboolean *delay_reporting) { GSList *caps; int processed; if (delay_reporting) *delay_reporting = FALSE; for (processed = 0, caps = NULL; processed + 2 <= size;) { struct avdtp_service_capability *cap; uint8_t length, category; category = data[0]; length = data[1]; if (processed + 2 + length > size) { error("Invalid capability data in getcap resp"); break; } cap = g_malloc(sizeof(struct avdtp_service_capability) + length); memcpy(cap, data, 2 + length); processed += 2 + length; data += 2 + length; caps = g_slist_append(caps, cap); if (category == AVDTP_MEDIA_CODEC && length >= sizeof(struct avdtp_media_codec_capability)) *codec = cap; else if (category == AVDTP_DELAY_REPORTING && delay_reporting) *delay_reporting = TRUE; } return caps; } static gboolean avdtp_unknown_cmd(struct avdtp *session, uint8_t transaction, uint8_t signal_id) { return avdtp_send(session, transaction, AVDTP_MSG_TYPE_GEN_REJECT, signal_id, NULL, 0); } static void copy_seps(void *data, void *user_data) { struct avdtp_local_sep *sep = data; struct seid_info **p = user_data; memcpy(*p, &sep->info, sizeof(struct seid_info)); *p = *p + 1; } static gboolean avdtp_discover_cmd(struct avdtp *session, uint8_t transaction, void *buf, int size) { unsigned int rsp_size, sep_count; struct seid_info *seps, *p; gboolean ret; sep_count = queue_length(session->lseps); if (sep_count == 0) { uint8_t err = AVDTP_NOT_SUPPORTED_COMMAND; return avdtp_send(session, transaction, AVDTP_MSG_TYPE_REJECT, AVDTP_DISCOVER, &err, sizeof(err)); } rsp_size = sep_count * sizeof(struct seid_info); seps = g_new0(struct seid_info, sep_count); p = seps; queue_foreach(session->lseps, copy_seps, &p); ret = avdtp_send(session, transaction, AVDTP_MSG_TYPE_ACCEPT, AVDTP_DISCOVER, seps, rsp_size); g_free(seps); return ret; } static gboolean avdtp_getcap_cmd(struct avdtp *session, uint8_t transaction, struct seid_req *req, unsigned int size, gboolean get_all) { GSList *l, *caps; struct avdtp_local_sep *sep = NULL; unsigned int rsp_size; uint8_t err, buf[1024], *ptr = buf; uint8_t cmd; cmd = get_all ? AVDTP_GET_ALL_CAPABILITIES : AVDTP_GET_CAPABILITIES; if (size < sizeof(struct seid_req)) { err = AVDTP_BAD_LENGTH; goto failed; } sep = find_local_sep_by_seid(session, req->acp_seid); if (!sep) { err = AVDTP_BAD_ACP_SEID; goto failed; } if (!sep->ind->get_capability(session, sep, &caps, &err, sep->user_data)) goto failed; for (l = caps, rsp_size = 0; l != NULL; l = g_slist_next(l)) { struct avdtp_service_capability *cap = l->data; if (rsp_size + cap->length + 2 > sizeof(buf)) break; memcpy(ptr, cap, cap->length + 2); rsp_size += cap->length + 2; ptr += cap->length + 2; g_free(cap); } if (get_all && sep->delay_reporting) { ptr[0] = AVDTP_DELAY_REPORTING; ptr[1] = 0x00; rsp_size += 2; } g_slist_free(caps); return avdtp_send(session, transaction, AVDTP_MSG_TYPE_ACCEPT, cmd, buf, rsp_size); failed: return avdtp_send(session, transaction, AVDTP_MSG_TYPE_REJECT, cmd, &err, sizeof(err)); } static void setconf_cb(struct avdtp *session, struct avdtp_stream *stream, struct avdtp_error *err) { struct conf_rej rej; struct avdtp_local_sep *sep; if (err != NULL) { rej.error = AVDTP_UNSUPPORTED_CONFIGURATION; rej.category = err->err.error_code; avdtp_send(session, session->in.transaction, AVDTP_MSG_TYPE_REJECT, AVDTP_SET_CONFIGURATION, &rej, sizeof(rej)); return; } if (!avdtp_send(session, session->in.transaction, AVDTP_MSG_TYPE_ACCEPT, AVDTP_SET_CONFIGURATION, NULL, 0)) { stream_free(stream); return; } sep = stream->lsep; sep->stream = stream; sep->info.inuse = 1; session->streams = g_slist_append(session->streams, stream); avdtp_sep_set_state(session, sep, AVDTP_STATE_CONFIGURED); } static gboolean avdtp_setconf_cmd(struct avdtp *session, uint8_t transaction, struct setconf_req *req, unsigned int size) { struct conf_rej rej; struct avdtp_local_sep *sep; struct avdtp_stream *stream; uint8_t err, category = 0x00; GSList *l; if (size < sizeof(struct setconf_req)) { error("Too short getcap request"); return FALSE; } sep = find_local_sep_by_seid(session, req->acp_seid); if (!sep) { err = AVDTP_BAD_ACP_SEID; goto failed; } if (sep->stream) { err = AVDTP_SEP_IN_USE; goto failed; } stream = g_new0(struct avdtp_stream, 1); stream->session = session; stream->lsep = sep; stream->rseid = req->int_seid; stream->caps = caps_to_list(req->caps, size - sizeof(struct setconf_req), &stream->codec, &stream->delay_reporting); /* * Verify that the Media Transport capability's length = 0. * Reject otherwise */ for (l = stream->caps; l != NULL; l = g_slist_next(l)) { struct avdtp_service_capability *cap = l->data; if (cap->category == AVDTP_MEDIA_TRANSPORT && cap->length != 0) { err = AVDTP_BAD_MEDIA_TRANSPORT_FORMAT; goto failed_stream; } } if (stream->delay_reporting && session->version < 0x0103) session->version = 0x0103; if (sep->ind && sep->ind->set_configuration) { if (!sep->ind->set_configuration(session, sep, stream, stream->caps, setconf_cb, sep->user_data)) { err = AVDTP_UNSUPPORTED_CONFIGURATION; category = 0x00; goto failed_stream; } } else { if (!avdtp_send(session, transaction, AVDTP_MSG_TYPE_ACCEPT, AVDTP_SET_CONFIGURATION, NULL, 0)) { stream_free(stream); return FALSE; } sep->stream = stream; sep->info.inuse = 1; session->streams = g_slist_append(session->streams, stream); avdtp_sep_set_state(session, sep, AVDTP_STATE_CONFIGURED); } return TRUE; failed_stream: stream_free(stream); failed: rej.error = err; rej.category = category; return avdtp_send(session, transaction, AVDTP_MSG_TYPE_REJECT, AVDTP_SET_CONFIGURATION, &rej, sizeof(rej)); } static gboolean avdtp_getconf_cmd(struct avdtp *session, uint8_t transaction, struct seid_req *req, int size) { GSList *l; struct avdtp_local_sep *sep = NULL; int rsp_size; uint8_t err; uint8_t buf[1024]; uint8_t *ptr = buf; if (size < (int) sizeof(struct seid_req)) { error("Too short getconf request"); return FALSE; } memset(buf, 0, sizeof(buf)); sep = find_local_sep_by_seid(session, req->acp_seid); if (!sep) { err = AVDTP_BAD_ACP_SEID; goto failed; } if (!sep->stream || !sep->stream->caps) { err = AVDTP_UNSUPPORTED_CONFIGURATION; goto failed; } for (l = sep->stream->caps, rsp_size = 0; l; l = g_slist_next(l)) { struct avdtp_service_capability *cap = l->data; if (rsp_size + cap->length + 2 > (int) sizeof(buf)) break; memcpy(ptr, cap, cap->length + 2); rsp_size += cap->length + 2; ptr += cap->length + 2; } return avdtp_send(session, transaction, AVDTP_MSG_TYPE_ACCEPT, AVDTP_GET_CONFIGURATION, buf, rsp_size); failed: return avdtp_send(session, transaction, AVDTP_MSG_TYPE_REJECT, AVDTP_GET_CONFIGURATION, &err, sizeof(err)); } static gboolean avdtp_reconf_cmd(struct avdtp *session, uint8_t transaction, struct seid_req *req, int size) { struct conf_rej rej; rej.error = AVDTP_NOT_SUPPORTED_COMMAND; rej.category = 0x00; return avdtp_send(session, transaction, AVDTP_MSG_TYPE_REJECT, AVDTP_RECONFIGURE, &rej, sizeof(rej)); } static void check_seid_collision(struct pending_req *req, uint8_t id) { struct seid_req *seid = req->data; if (seid->acp_seid == id) req->collided = TRUE; } static void check_start_collision(struct pending_req *req, uint8_t id) { struct start_req *start = req->data; struct seid *seid = &start->first_seid; int count = 1 + req->data_size - sizeof(struct start_req); int i; for (i = 0; i < count; i++, seid++) { if (seid->seid == id) { req->collided = TRUE; return; } } } static void check_suspend_collision(struct pending_req *req, uint8_t id) { struct suspend_req *suspend = req->data; struct seid *seid = &suspend->first_seid; int count = 1 + req->data_size - sizeof(struct suspend_req); int i; for (i = 0; i < count; i++, seid++) { if (seid->seid == id) { req->collided = TRUE; return; } } } static void avdtp_check_collision(struct avdtp *session, uint8_t cmd, struct avdtp_stream *stream) { struct pending_req *req = session->req; if (req == NULL || (req->signal_id != cmd && cmd != AVDTP_ABORT)) return; if (cmd == AVDTP_ABORT) cmd = req->signal_id; switch (cmd) { case AVDTP_OPEN: case AVDTP_CLOSE: check_seid_collision(req, stream->rseid); break; case AVDTP_START: check_start_collision(req, stream->rseid); break; case AVDTP_SUSPEND: check_suspend_collision(req, stream->rseid); break; } } static gboolean avdtp_open_cmd(struct avdtp *session, uint8_t transaction, struct seid_req *req, unsigned int size) { struct avdtp_local_sep *sep; struct avdtp_stream *stream; uint8_t err; if (size < sizeof(struct seid_req)) { error("Too short abort request"); return FALSE; } sep = find_local_sep_by_seid(session, req->acp_seid); if (!sep) { err = AVDTP_BAD_ACP_SEID; goto failed; } if (sep->state != AVDTP_STATE_CONFIGURED) { err = AVDTP_BAD_STATE; goto failed; } stream = sep->stream; if (sep->ind && sep->ind->open) { if (!sep->ind->open(session, sep, stream, &err, sep->user_data)) goto failed; } avdtp_check_collision(session, AVDTP_OPEN, stream); if (!avdtp_send(session, transaction, AVDTP_MSG_TYPE_ACCEPT, AVDTP_OPEN, NULL, 0)) return FALSE; stream->open_acp = TRUE; session->pending_open = stream; stream->timer = g_timeout_add_seconds(REQ_TIMEOUT, stream_open_timeout, stream); return TRUE; failed: return avdtp_send(session, transaction, AVDTP_MSG_TYPE_REJECT, AVDTP_OPEN, &err, sizeof(err)); } static gboolean avdtp_start_cmd(struct avdtp *session, uint8_t transaction, struct start_req *req, unsigned int size) { struct avdtp_local_sep *sep; struct avdtp_stream *stream; struct stream_rej rej; struct seid *seid; uint8_t err, failed_seid; int seid_count, i; if (size < sizeof(struct start_req)) { error("Too short start request"); return FALSE; } seid_count = 1 + size - sizeof(struct start_req); seid = &req->first_seid; for (i = 0; i < seid_count; i++, seid++) { failed_seid = seid->seid; sep = find_local_sep_by_seid(session, seid->seid); if (!sep || !sep->stream) { err = AVDTP_BAD_ACP_SEID; goto failed; } stream = sep->stream; /* Also reject start cmd if state is not open */ if (sep->state != AVDTP_STATE_OPEN) { err = AVDTP_BAD_STATE; goto failed; } stream->starting = TRUE; if (sep->ind && sep->ind->start) { if (!sep->ind->start(session, sep, stream, &err, sep->user_data)) goto failed; } avdtp_check_collision(session, AVDTP_START, stream); avdtp_sep_set_state(session, sep, AVDTP_STATE_STREAMING); } return avdtp_send(session, transaction, AVDTP_MSG_TYPE_ACCEPT, AVDTP_START, NULL, 0); failed: DBG("Rejecting (%d)", err); memset(&rej, 0, sizeof(rej)); rej.acp_seid = failed_seid; rej.error = err; return avdtp_send(session, transaction, AVDTP_MSG_TYPE_REJECT, AVDTP_START, &rej, sizeof(rej)); } static gboolean avdtp_close_cmd(struct avdtp *session, uint8_t transaction, struct seid_req *req, unsigned int size) { struct avdtp_local_sep *sep; struct avdtp_stream *stream; uint8_t err; if (size < sizeof(struct seid_req)) { error("Too short close request"); return FALSE; } sep = find_local_sep_by_seid(session, req->acp_seid); if (!sep || !sep->stream) { err = AVDTP_BAD_ACP_SEID; goto failed; } if (sep->state != AVDTP_STATE_OPEN && sep->state != AVDTP_STATE_STREAMING) { err = AVDTP_BAD_STATE; goto failed; } stream = sep->stream; if (sep->ind && sep->ind->close) { if (!sep->ind->close(session, sep, stream, &err, sep->user_data)) goto failed; } avdtp_check_collision(session, AVDTP_CLOSE, stream); avdtp_sep_set_state(session, sep, AVDTP_STATE_CLOSING); if (!avdtp_send(session, transaction, AVDTP_MSG_TYPE_ACCEPT, AVDTP_CLOSE, NULL, 0)) return FALSE; stream->timer = g_timeout_add_seconds(REQ_TIMEOUT, stream_close_timeout, stream); return TRUE; failed: return avdtp_send(session, transaction, AVDTP_MSG_TYPE_REJECT, AVDTP_CLOSE, &err, sizeof(err)); } static gboolean avdtp_suspend_cmd(struct avdtp *session, uint8_t transaction, struct suspend_req *req, unsigned int size) { struct avdtp_local_sep *sep; struct avdtp_stream *stream; struct stream_rej rej; struct seid *seid; uint8_t err, failed_seid; int seid_count, i; if (size < sizeof(struct suspend_req)) { error("Too short suspend request"); return FALSE; } seid_count = 1 + size - sizeof(struct suspend_req); seid = &req->first_seid; for (i = 0; i < seid_count; i++, seid++) { failed_seid = seid->seid; sep = find_local_sep_by_seid(session, seid->seid); if (!sep || !sep->stream) { err = AVDTP_BAD_ACP_SEID; goto failed; } stream = sep->stream; if (sep->state != AVDTP_STATE_STREAMING) { err = AVDTP_BAD_STATE; goto failed; } if (sep->ind && sep->ind->suspend) { if (!sep->ind->suspend(session, sep, stream, &err, sep->user_data)) goto failed; } avdtp_check_collision(session, AVDTP_SUSPEND, stream); avdtp_sep_set_state(session, sep, AVDTP_STATE_OPEN); } return avdtp_send(session, transaction, AVDTP_MSG_TYPE_ACCEPT, AVDTP_SUSPEND, NULL, 0); failed: memset(&rej, 0, sizeof(rej)); rej.acp_seid = failed_seid; rej.error = err; return avdtp_send(session, transaction, AVDTP_MSG_TYPE_REJECT, AVDTP_SUSPEND, &rej, sizeof(rej)); } static gboolean avdtp_abort_cmd(struct avdtp *session, uint8_t transaction, struct seid_req *req, unsigned int size) { struct avdtp_local_sep *sep; uint8_t err; gboolean ret; if (size < sizeof(struct seid_req)) { error("Too short abort request"); return FALSE; } sep = find_local_sep_by_seid(session, req->acp_seid); if (!sep || !sep->stream) return TRUE; if (sep->ind && sep->ind->abort) sep->ind->abort(session, sep, sep->stream, &err, sep->user_data); avdtp_check_collision(session, AVDTP_ABORT, sep->stream); ret = avdtp_send(session, transaction, AVDTP_MSG_TYPE_ACCEPT, AVDTP_ABORT, NULL, 0); if (ret) avdtp_sep_set_state(session, sep, AVDTP_STATE_ABORTING); return ret; } static gboolean avdtp_secctl_cmd(struct avdtp *session, uint8_t transaction, struct seid_req *req, int size) { return avdtp_unknown_cmd(session, transaction, AVDTP_SECURITY_CONTROL); } static gboolean avdtp_delayreport_cmd(struct avdtp *session, uint8_t transaction, struct delay_req *req, unsigned int size) { struct avdtp_local_sep *sep; struct avdtp_stream *stream; uint8_t err; if (size < sizeof(struct delay_req)) { error("Too short delay report request"); return FALSE; } sep = find_local_sep_by_seid(session, req->acp_seid); if (!sep || !sep->stream) { err = AVDTP_BAD_ACP_SEID; goto failed; } stream = sep->stream; switch (sep->state) { case AVDTP_STATE_IDLE: case AVDTP_STATE_ABORTING: case AVDTP_STATE_CLOSING: err = AVDTP_BAD_STATE; goto failed; case AVDTP_STATE_CONFIGURED: case AVDTP_STATE_OPEN: case AVDTP_STATE_STREAMING: default: break; } stream->delay = ntohs(req->delay); if (sep->ind && sep->ind->delayreport) { if (!sep->ind->delayreport(session, sep, stream->rseid, stream->delay, &err, sep->user_data)) goto failed; } return avdtp_send(session, transaction, AVDTP_MSG_TYPE_ACCEPT, AVDTP_DELAY_REPORT, NULL, 0); failed: return avdtp_send(session, transaction, AVDTP_MSG_TYPE_REJECT, AVDTP_DELAY_REPORT, &err, sizeof(err)); } static gboolean avdtp_parse_cmd(struct avdtp *session, uint8_t transaction, uint8_t signal_id, void *buf, int size) { switch (signal_id) { case AVDTP_DISCOVER: DBG("Received DISCOVER_CMD"); return avdtp_discover_cmd(session, transaction, buf, size); case AVDTP_GET_CAPABILITIES: DBG("Received GET_CAPABILITIES_CMD"); return avdtp_getcap_cmd(session, transaction, buf, size, FALSE); case AVDTP_GET_ALL_CAPABILITIES: DBG("Received GET_ALL_CAPABILITIES_CMD"); return avdtp_getcap_cmd(session, transaction, buf, size, TRUE); case AVDTP_SET_CONFIGURATION: DBG("Received SET_CONFIGURATION_CMD"); return avdtp_setconf_cmd(session, transaction, buf, size); case AVDTP_GET_CONFIGURATION: DBG("Received GET_CONFIGURATION_CMD"); return avdtp_getconf_cmd(session, transaction, buf, size); case AVDTP_RECONFIGURE: DBG("Received RECONFIGURE_CMD"); return avdtp_reconf_cmd(session, transaction, buf, size); case AVDTP_OPEN: DBG("Received OPEN_CMD"); return avdtp_open_cmd(session, transaction, buf, size); case AVDTP_START: DBG("Received START_CMD"); return avdtp_start_cmd(session, transaction, buf, size); case AVDTP_CLOSE: DBG("Received CLOSE_CMD"); return avdtp_close_cmd(session, transaction, buf, size); case AVDTP_SUSPEND: DBG("Received SUSPEND_CMD"); return avdtp_suspend_cmd(session, transaction, buf, size); case AVDTP_ABORT: DBG("Received ABORT_CMD"); return avdtp_abort_cmd(session, transaction, buf, size); case AVDTP_SECURITY_CONTROL: DBG("Received SECURITY_CONTROL_CMD"); return avdtp_secctl_cmd(session, transaction, buf, size); case AVDTP_DELAY_REPORT: DBG("Received DELAY_REPORT_CMD"); return avdtp_delayreport_cmd(session, transaction, buf, size); default: DBG("Received unknown request id %u", signal_id); return avdtp_unknown_cmd(session, transaction, signal_id); } } enum avdtp_parse_result { PARSE_ERROR, PARSE_FRAGMENT, PARSE_SUCCESS }; static enum avdtp_parse_result avdtp_parse_data(struct avdtp *session, void *buf, size_t size) { struct avdtp_common_header *header = buf; struct avdtp_single_header *single = (void *) session->buf; struct avdtp_start_header *start = (void *) session->buf; void *payload; gsize payload_size; switch (header->packet_type) { case AVDTP_PKT_TYPE_SINGLE: if (size < sizeof(*single)) { error("Received too small single packet (%zu bytes)", size); return PARSE_ERROR; } if (session->in.active) { error("SINGLE: Invalid AVDTP packet fragmentation"); return PARSE_ERROR; } payload = session->buf + sizeof(*single); payload_size = size - sizeof(*single); session->in.active = TRUE; session->in.data_size = 0; session->in.no_of_packets = 1; session->in.transaction = header->transaction; session->in.message_type = header->message_type; session->in.signal_id = single->signal_id; break; case AVDTP_PKT_TYPE_START: if (size < sizeof(*start)) { error("Received too small start packet (%zu bytes)", size); return PARSE_ERROR; } if (session->in.active) { error("START: Invalid AVDTP packet fragmentation"); return PARSE_ERROR; } session->in.active = TRUE; session->in.data_size = 0; session->in.transaction = header->transaction; session->in.message_type = header->message_type; session->in.no_of_packets = start->no_of_packets; session->in.signal_id = start->signal_id; payload = session->buf + sizeof(*start); payload_size = size - sizeof(*start); break; case AVDTP_PKT_TYPE_CONTINUE: if (size < sizeof(struct avdtp_continue_header)) { error("Received too small continue packet (%zu bytes)", size); return PARSE_ERROR; } if (!session->in.active) { error("CONTINUE: Invalid AVDTP packet fragmentation"); return PARSE_ERROR; } if (session->in.transaction != header->transaction) { error("Continue transaction id doesn't match"); return PARSE_ERROR; } if (session->in.no_of_packets <= 1) { error("Too few continue packets"); return PARSE_ERROR; } payload = session->buf + sizeof(struct avdtp_continue_header); payload_size = size - sizeof(struct avdtp_continue_header); break; case AVDTP_PKT_TYPE_END: if (size < sizeof(struct avdtp_continue_header)) { error("Received too small end packet (%zu bytes)", size); return PARSE_ERROR; } if (!session->in.active) { error("END: Invalid AVDTP packet fragmentation"); return PARSE_ERROR; } if (session->in.transaction != header->transaction) { error("End transaction id doesn't match"); return PARSE_ERROR; } if (session->in.no_of_packets > 1) { error("Got an end packet too early"); return PARSE_ERROR; } payload = session->buf + sizeof(struct avdtp_continue_header); payload_size = size - sizeof(struct avdtp_continue_header); break; default: error("Invalid AVDTP packet type 0x%02X", header->packet_type); return PARSE_ERROR; } if (session->in.data_size + payload_size > sizeof(session->in.buf)) { error("Not enough incoming buffer space!"); return PARSE_ERROR; } memcpy(session->in.buf + session->in.data_size, payload, payload_size); session->in.data_size += payload_size; if (session->in.no_of_packets > 1) { session->in.no_of_packets--; DBG("Received AVDTP fragment. %d to go", session->in.no_of_packets); return PARSE_FRAGMENT; } session->in.active = FALSE; return PARSE_SUCCESS; } static gboolean session_cb(GIOChannel *chan, GIOCondition cond, gpointer data) { struct avdtp *session = data; struct avdtp_common_header *header; ssize_t size; int fd; DBG(""); if (cond & G_IO_NVAL) { session->io_id = 0; return FALSE; } header = (void *) session->buf; if (cond & (G_IO_HUP | G_IO_ERR)) goto failed; fd = g_io_channel_unix_get_fd(chan); size = read(fd, session->buf, session->imtu); if (size < 0) { error("IO Channel read error"); goto failed; } if ((size_t) size < sizeof(struct avdtp_common_header)) { error("Received too small packet (%zu bytes)", size); goto failed; } switch (avdtp_parse_data(session, session->buf, size)) { case PARSE_ERROR: goto failed; case PARSE_FRAGMENT: return TRUE; case PARSE_SUCCESS: break; } /* Take a reference to protect against callback destroying session */ avdtp_ref(session); if (session->in.message_type == AVDTP_MSG_TYPE_COMMAND) { if (!avdtp_parse_cmd(session, session->in.transaction, session->in.signal_id, session->in.buf, session->in.data_size)) { error("Unable to handle command. Disconnecting"); goto failed; } if (session->req && session->req->collided) { DBG("Collision detected"); goto next; } avdtp_unref(session); return TRUE; } if (session->req == NULL) { error("No pending request, ignoring message"); avdtp_unref(session); return TRUE; } if (header->transaction != session->req->transaction) { error("Transaction label doesn't match"); avdtp_unref(session); return TRUE; } if (session->in.signal_id != session->req->signal_id) { error("Response signal doesn't match"); avdtp_unref(session); return TRUE; } g_source_remove(session->req->timeout); session->req->timeout = 0; switch (header->message_type) { case AVDTP_MSG_TYPE_ACCEPT: if (!avdtp_parse_resp(session, session->req->stream, session->in.transaction, session->in.signal_id, session->in.buf, session->in.data_size)) { error("Unable to parse accept response"); goto failed; } break; case AVDTP_MSG_TYPE_REJECT: if (!avdtp_parse_rej(session, session->req->stream, session->in.transaction, session->in.signal_id, session->in.buf, session->in.data_size)) { error("Unable to parse reject response"); goto failed; } break; case AVDTP_MSG_TYPE_GEN_REJECT: error("Received a General Reject message"); break; default: error("Unknown message type 0x%02X", header->message_type); break; } next: pending_req_free(session->req); session->req = NULL; if (session->ref > 1) process_queue(session); avdtp_unref(session); return TRUE; failed: session->io_id = 0; connection_lost(session, EIO); return FALSE; } static int set_priority(int fd, int priority) { int err; err = setsockopt(fd, SOL_SOCKET, SO_PRIORITY, &priority, sizeof(priority)); if (err == 0 || errno == ENOTSOCK) return 0; err = -errno; error("setsockopt(SO_PRIORITY): %s (%d)", strerror(-err), -err); return err; } struct avdtp *avdtp_new(int fd, size_t imtu, size_t omtu, uint16_t version, struct queue *lseps) { struct avdtp *session; GIOCondition cond = G_IO_IN | G_IO_ERR | G_IO_HUP | G_IO_NVAL; int new_fd; if (!lseps) return NULL; new_fd = dup(fd); if (new_fd < 0) { error("dup(): %s (%d)", strerror(errno), errno); return NULL; } if (set_priority(new_fd, 6) < 0) { close(new_fd); return NULL; } session = g_new0(struct avdtp, 1); session->io = g_io_channel_unix_new(new_fd); session->version = version; session->imtu = imtu; session->omtu = omtu; session->buf = g_malloc0(MAX(session->imtu, session->omtu)); /* This watch should be low priority since otherwise the * connect callback might be dispatched before the session * callback if the kernel wakes us up at the same time for * them. This could happen if a headset is very quick in * sending the Start command after connecting the stream * transport channel. */ session->io_id = g_io_add_watch_full(session->io, G_PRIORITY_LOW, cond, (GIOFunc) session_cb, session, NULL); session->lseps = lseps; return avdtp_ref(session); } unsigned int avdtp_add_disconnect_cb(struct avdtp *session, avdtp_disconnect_cb_t cb, void *user_data) { struct disconnect_callback *callback; static unsigned int id = 0; callback = g_new0(struct disconnect_callback, 1); callback->id = ++id; callback->cb = cb; callback->user_data = user_data; session->disconnect = g_slist_append(session->disconnect, callback); return id; } gboolean avdtp_remove_disconnect_cb(struct avdtp *session, unsigned int id) { GSList *l; for (l = session->disconnect; l; l = g_slist_next(l)) { struct disconnect_callback *callback = l->data; if (callback->id != id) continue; session->disconnect = g_slist_remove(session->disconnect, callback); g_free(callback); return TRUE; } return FALSE; } void avdtp_shutdown(struct avdtp *session) { GSList *l; bool aborting = false; if (!session->io) return; for (l = session->streams; l; l = g_slist_next(l)) { struct avdtp_stream *stream = l->data; if (stream->abort_int || avdtp_close(session, stream, TRUE) == 0) aborting = true; } if (aborting) { /* defer shutdown until all streams are aborted properly */ session->shutdown = true; } else { int sock = g_io_channel_unix_get_fd(session->io); shutdown(sock, SHUT_RDWR); } } static void queue_request(struct avdtp *session, struct pending_req *req, gboolean priority) { if (priority) session->prio_queue = g_slist_append(session->prio_queue, req); else session->req_queue = g_slist_append(session->req_queue, req); } static uint8_t req_get_seid(struct pending_req *req) { if (req->signal_id == AVDTP_DISCOVER) return 0; return ((struct seid_req *) (req->data))->acp_seid; } static int cancel_request(struct avdtp *session, int err) { struct pending_req *req; struct seid_req sreq; struct avdtp_local_sep *lsep; struct avdtp_stream *stream; uint8_t seid; struct avdtp_error averr; req = session->req; session->req = NULL; avdtp_error_init(&averr, AVDTP_ERRNO, err); seid = req_get_seid(req); if (seid) stream = find_stream_by_rseid(session, seid); else stream = NULL; if (stream) { stream->abort_int = TRUE; lsep = stream->lsep; } else lsep = NULL; switch (req->signal_id) { case AVDTP_RECONFIGURE: error("Reconfigure: %s (%d)", strerror(err), err); if (lsep && lsep->cfm && lsep->cfm->reconfigure) lsep->cfm->reconfigure(session, lsep, stream, &averr, lsep->user_data); break; case AVDTP_OPEN: error("Open: %s (%d)", strerror(err), err); if (lsep && lsep->cfm && lsep->cfm->open) lsep->cfm->open(session, lsep, stream, &averr, lsep->user_data); break; case AVDTP_START: error("Start: %s (%d)", strerror(err), err); if (lsep && lsep->cfm && lsep->cfm->start) { lsep->cfm->start(session, lsep, stream, &averr, lsep->user_data); if (stream) stream->starting = FALSE; } break; case AVDTP_SUSPEND: error("Suspend: %s (%d)", strerror(err), err); if (lsep && lsep->cfm && lsep->cfm->suspend) lsep->cfm->suspend(session, lsep, stream, &averr, lsep->user_data); break; case AVDTP_CLOSE: error("Close: %s (%d)", strerror(err), err); if (lsep && lsep->cfm && lsep->cfm->close) { lsep->cfm->close(session, lsep, stream, &averr, lsep->user_data); if (stream) stream->close_int = FALSE; } break; case AVDTP_SET_CONFIGURATION: error("SetConfiguration: %s (%d)", strerror(err), err); if (lsep && lsep->cfm && lsep->cfm->set_configuration) lsep->cfm->set_configuration(session, lsep, stream, &averr, lsep->user_data); goto failed; case AVDTP_DISCOVER: error("Discover: %s (%d)", strerror(err), err); goto failed; case AVDTP_GET_CAPABILITIES: error("GetCapabilities: %s (%d)", strerror(err), err); goto failed; case AVDTP_ABORT: error("Abort: %s (%d)", strerror(err), err); goto failed; } if (!stream) goto failed; memset(&sreq, 0, sizeof(sreq)); sreq.acp_seid = seid; err = send_request(session, TRUE, stream, AVDTP_ABORT, &sreq, sizeof(sreq)); if (err < 0) { error("Unable to send abort request"); goto failed; } goto done; failed: connection_lost(session, err); done: pending_req_free(req); return err; } static gboolean request_timeout(gpointer user_data) { struct avdtp *session = user_data; cancel_request(session, ETIMEDOUT); return FALSE; } static int send_req(struct avdtp *session, gboolean priority, struct pending_req *req) { static int transaction = 0; int err; if (session->req != NULL) { queue_request(session, req, priority); return 0; } req->transaction = transaction++; transaction %= 16; /* FIXME: Should we retry to send if the buffer was not totally sent or in case of EINTR? */ if (!avdtp_send(session, req->transaction, AVDTP_MSG_TYPE_COMMAND, req->signal_id, req->data, req->data_size)) { err = -EIO; goto failed; } session->req = req; req->timeout = g_timeout_add_seconds(req->signal_id == AVDTP_ABORT ? ABORT_TIMEOUT : REQ_TIMEOUT, request_timeout, session); return 0; failed: g_free(req->data); g_free(req); return err; } static int send_request(struct avdtp *session, gboolean priority, struct avdtp_stream *stream, uint8_t signal_id, void *buffer, size_t size) { struct pending_req *req; if (size > 0 && !buffer) { DBG("Invalid buffer %p", buffer); return -EINVAL; } if (stream && stream->abort_int && signal_id != AVDTP_ABORT) { DBG("Unable to send requests while aborting"); return -EINVAL; } req = g_new0(struct pending_req, 1); req->signal_id = signal_id; req->data_size = size; req->stream = stream; if (size > 0) { req->data = g_malloc(size); memcpy(req->data, buffer, size); } return send_req(session, priority, req); } static gboolean avdtp_discover_resp(struct avdtp *session, struct discover_resp *resp, int size) { int sep_count, i; uint8_t getcap_cmd; int ret = 0; gboolean getcap_pending = FALSE; if (session->version >= 0x0103) getcap_cmd = AVDTP_GET_ALL_CAPABILITIES; else getcap_cmd = AVDTP_GET_CAPABILITIES; sep_count = size / sizeof(struct seid_info); for (i = 0; i < sep_count; i++) { struct avdtp_remote_sep *sep; struct avdtp_stream *stream; struct seid_req req; DBG("seid %d type %d media %d in use %d", resp->seps[i].seid, resp->seps[i].type, resp->seps[i].media_type, resp->seps[i].inuse); stream = find_stream_by_rseid(session, resp->seps[i].seid); sep = find_remote_sep(session->seps, resp->seps[i].seid); if (!sep) { if (resp->seps[i].inuse && !stream) continue; sep = g_new0(struct avdtp_remote_sep, 1); session->seps = g_slist_append(session->seps, sep); } sep->stream = stream; sep->seid = resp->seps[i].seid; sep->type = resp->seps[i].type; sep->media_type = resp->seps[i].media_type; memset(&req, 0, sizeof(req)); req.acp_seid = sep->seid; ret = send_request(session, TRUE, NULL, getcap_cmd, &req, sizeof(req)); if (ret < 0) break; getcap_pending = TRUE; } if (!getcap_pending) finalize_discovery(session, -ret); return TRUE; } static gboolean avdtp_get_capabilities_resp(struct avdtp *session, struct getcap_resp *resp, unsigned int size) { struct avdtp_remote_sep *sep; uint8_t seid; /* Check for minimum required packet size includes: * 1. getcap resp header * 2. media transport capability (2 bytes) * 3. media codec capability type + length (2 bytes) * 4. the actual media codec elements * */ if (size < (sizeof(struct getcap_resp) + 4 + sizeof(struct avdtp_media_codec_capability))) { error("Too short getcap resp packet"); return FALSE; } seid = ((struct seid_req *) session->req->data)->acp_seid; sep = find_remote_sep(session->seps, seid); DBG("seid %d type %d media %d", sep->seid, sep->type, sep->media_type); if (sep->caps) { g_slist_free_full(sep->caps, g_free); sep->caps = NULL; sep->codec = NULL; sep->delay_reporting = FALSE; } sep->caps = caps_to_list(resp->caps, size - sizeof(struct getcap_resp), &sep->codec, &sep->delay_reporting); return TRUE; } static gboolean avdtp_set_configuration_resp(struct avdtp *session, struct avdtp_stream *stream, struct avdtp_single_header *resp, int size) { struct avdtp_local_sep *sep = stream->lsep; avdtp_sep_set_state(session, sep, AVDTP_STATE_CONFIGURED); if (sep->cfm && sep->cfm->set_configuration) sep->cfm->set_configuration(session, sep, stream, NULL, sep->user_data); return TRUE; } static gboolean avdtp_reconfigure_resp(struct avdtp *session, struct avdtp_stream *stream, struct avdtp_single_header *resp, int size) { return TRUE; } static gboolean avdtp_open_resp(struct avdtp *session, struct avdtp_stream *stream, struct seid_rej *resp, int size) { struct avdtp_local_sep *sep = stream->lsep; session->pending_open = stream; if (!stream->open_acp && sep->cfm && sep->cfm->open) sep->cfm->open(session, sep, stream, NULL, sep->user_data); return TRUE; } static gboolean avdtp_start_resp(struct avdtp *session, struct avdtp_stream *stream, struct seid_rej *resp, int size) { struct avdtp_local_sep *sep = stream->lsep; /* We might be in STREAMING already if both sides send START_CMD at the * same time and the one in SNK role doesn't reject it as it should */ if (sep->state != AVDTP_STATE_STREAMING) avdtp_sep_set_state(session, sep, AVDTP_STATE_STREAMING); if (sep->cfm && sep->cfm->start) sep->cfm->start(session, sep, stream, NULL, sep->user_data); return TRUE; } static gboolean avdtp_close_resp(struct avdtp *session, struct avdtp_stream *stream, struct seid_rej *resp, int size) { struct avdtp_local_sep *sep = stream->lsep; avdtp_sep_set_state(session, sep, AVDTP_STATE_CLOSING); close_stream(stream); return TRUE; } static gboolean avdtp_suspend_resp(struct avdtp *session, struct avdtp_stream *stream, void *data, int size) { struct avdtp_local_sep *sep = stream->lsep; avdtp_sep_set_state(session, sep, AVDTP_STATE_OPEN); if (sep->cfm && sep->cfm->suspend) sep->cfm->suspend(session, sep, stream, NULL, sep->user_data); return TRUE; } static gboolean avdtp_abort_resp(struct avdtp *session, struct avdtp_stream *stream, struct seid_rej *resp, int size) { struct avdtp_local_sep *sep = stream->lsep; avdtp_sep_set_state(session, sep, AVDTP_STATE_ABORTING); if (sep->cfm && sep->cfm->abort) sep->cfm->abort(session, sep, stream, NULL, sep->user_data); avdtp_sep_set_state(session, sep, AVDTP_STATE_IDLE); return TRUE; } static gboolean avdtp_delay_report_resp(struct avdtp *session, struct avdtp_stream *stream, void *data, int size) { struct avdtp_local_sep *sep = stream->lsep; if (sep->cfm && sep->cfm->delay_report) sep->cfm->delay_report(session, sep, stream, NULL, sep->user_data); return TRUE; } static gboolean avdtp_parse_resp(struct avdtp *session, struct avdtp_stream *stream, uint8_t transaction, uint8_t signal_id, void *buf, int size) { struct pending_req *next; const char *get_all = ""; if (session->prio_queue) next = session->prio_queue->data; else if (session->req_queue) next = session->req_queue->data; else next = NULL; switch (signal_id) { case AVDTP_DISCOVER: DBG("DISCOVER request succeeded"); return avdtp_discover_resp(session, buf, size); case AVDTP_GET_ALL_CAPABILITIES: get_all = "ALL_"; /* fall through */ case AVDTP_GET_CAPABILITIES: DBG("GET_%sCAPABILITIES request succeeded", get_all); if (!avdtp_get_capabilities_resp(session, buf, size)) return FALSE; if (!(next && (next->signal_id == AVDTP_GET_CAPABILITIES || next->signal_id == AVDTP_GET_ALL_CAPABILITIES))) finalize_discovery(session, 0); return TRUE; } /* The remaining commands require an existing stream so bail out * here if the stream got unexpectedly disconnected */ if (!stream) { DBG("AVDTP: stream was closed while waiting for reply"); return TRUE; } switch (signal_id) { case AVDTP_SET_CONFIGURATION: DBG("SET_CONFIGURATION request succeeded"); return avdtp_set_configuration_resp(session, stream, buf, size); case AVDTP_RECONFIGURE: DBG("RECONFIGURE request succeeded"); return avdtp_reconfigure_resp(session, stream, buf, size); case AVDTP_OPEN: DBG("OPEN request succeeded"); return avdtp_open_resp(session, stream, buf, size); case AVDTP_SUSPEND: DBG("SUSPEND request succeeded"); return avdtp_suspend_resp(session, stream, buf, size); case AVDTP_START: DBG("START request succeeded"); return avdtp_start_resp(session, stream, buf, size); case AVDTP_CLOSE: DBG("CLOSE request succeeded"); return avdtp_close_resp(session, stream, buf, size); case AVDTP_ABORT: DBG("ABORT request succeeded"); return avdtp_abort_resp(session, stream, buf, size); case AVDTP_DELAY_REPORT: DBG("DELAY_REPORT request succeeded"); return avdtp_delay_report_resp(session, stream, buf, size); } error("Unknown signal id in accept response: %u", signal_id); return TRUE; } static gboolean seid_rej_to_err(struct seid_rej *rej, unsigned int size, struct avdtp_error *err) { if (size < sizeof(struct seid_rej)) { error("Too small packet for seid_rej"); return FALSE; } avdtp_error_init(err, 0x00, rej->error); return TRUE; } static gboolean conf_rej_to_err(struct conf_rej *rej, unsigned int size, struct avdtp_error *err) { if (size < sizeof(struct conf_rej)) { error("Too small packet for conf_rej"); return FALSE; } avdtp_error_init(err, rej->category, rej->error); return TRUE; } static gboolean stream_rej_to_err(struct stream_rej *rej, unsigned int size, struct avdtp_error *err, uint8_t *acp_seid) { if (size < sizeof(struct stream_rej)) { error("Too small packet for stream_rej"); return FALSE; } avdtp_error_init(err, 0x00, rej->error); if (acp_seid) *acp_seid = rej->acp_seid; return TRUE; } static gboolean avdtp_parse_rej(struct avdtp *session, struct avdtp_stream *stream, uint8_t transaction, uint8_t signal_id, void *buf, int size) { struct avdtp_error err; uint8_t acp_seid; struct avdtp_local_sep *sep = stream ? stream->lsep : NULL; switch (signal_id) { case AVDTP_DISCOVER: case AVDTP_GET_CAPABILITIES: case AVDTP_GET_ALL_CAPABILITIES: if (!seid_rej_to_err(buf, size, &err)) return FALSE; error("%s request rejected: %s (%d)", signal_id == AVDTP_DISCOVER ? "DISCOVER" : signal_id == AVDTP_GET_CAPABILITIES ? "GET_CAPABILITIES" : "GET_ALL_CAPABILITIES", avdtp_strerror(&err), err.err.error_code); if (session->discover) { session->discover->cb(session, session->seps, &err, session->discover->user_data); g_free(session->discover); session->discover = NULL; } return TRUE; case AVDTP_OPEN: if (!seid_rej_to_err(buf, size, &err)) return FALSE; error("OPEN request rejected: %s (%d)", avdtp_strerror(&err), err.err.error_code); if (sep && sep->cfm && sep->cfm->open) sep->cfm->open(session, sep, stream, &err, sep->user_data); return TRUE; case AVDTP_SET_CONFIGURATION: if (!conf_rej_to_err(buf, size, &err)) return FALSE; error("SET_CONFIGURATION request rejected: %s (%d)", avdtp_strerror(&err), err.err.error_code); if (sep && sep->cfm && sep->cfm->set_configuration) sep->cfm->set_configuration(session, sep, stream, &err, sep->user_data); return TRUE; case AVDTP_GET_CONFIGURATION: if (!seid_rej_to_err(buf, size, &err)) return FALSE; error("GET_CONFIGURATION request rejected: %s (%d)", avdtp_strerror(&err), err.err.error_code); if (sep && sep->cfm && sep->cfm->get_configuration) sep->cfm->get_configuration(session, sep, stream, &err, sep->user_data); return TRUE; case AVDTP_RECONFIGURE: if (!conf_rej_to_err(buf, size, &err)) return FALSE; error("RECONFIGURE request rejected: %s (%d)", avdtp_strerror(&err), err.err.error_code); if (sep && sep->cfm && sep->cfm->reconfigure) sep->cfm->reconfigure(session, sep, stream, &err, sep->user_data); return TRUE; case AVDTP_START: if (!stream_rej_to_err(buf, size, &err, &acp_seid)) return FALSE; error("START request rejected: %s (%d)", avdtp_strerror(&err), err.err.error_code); if (sep && sep->cfm && sep->cfm->start) { stream->starting = FALSE; sep->cfm->start(session, sep, stream, &err, sep->user_data); } return TRUE; case AVDTP_SUSPEND: if (!stream_rej_to_err(buf, size, &err, &acp_seid)) return FALSE; error("SUSPEND request rejected: %s (%d)", avdtp_strerror(&err), err.err.error_code); if (sep && sep->cfm && sep->cfm->suspend) sep->cfm->suspend(session, sep, stream, &err, sep->user_data); return TRUE; case AVDTP_CLOSE: if (!stream_rej_to_err(buf, size, &err, &acp_seid)) return FALSE; error("CLOSE request rejected: %s (%d)", avdtp_strerror(&err), err.err.error_code); if (sep && sep->cfm && sep->cfm->close) { sep->cfm->close(session, sep, stream, &err, sep->user_data); stream->close_int = FALSE; } return TRUE; case AVDTP_ABORT: if (!stream_rej_to_err(buf, size, &err, &acp_seid)) return FALSE; error("ABORT request rejected: %s (%d)", avdtp_strerror(&err), err.err.error_code); if (sep && sep->cfm && sep->cfm->abort) sep->cfm->abort(session, sep, stream, &err, sep->user_data); return FALSE; case AVDTP_DELAY_REPORT: if (!stream_rej_to_err(buf, size, &err, &acp_seid)) return FALSE; error("DELAY_REPORT request rejected: %s (%d)", avdtp_strerror(&err), err.err.error_code); if (sep && sep->cfm && sep->cfm->delay_report) sep->cfm->delay_report(session, sep, stream, &err, sep->user_data); return TRUE; default: error("Unknown reject response signal id: %u", signal_id); return TRUE; } } struct avdtp_service_capability *avdtp_stream_get_codec( struct avdtp_stream *stream) { GSList *l; for (l = stream->caps; l; l = l->next) { struct avdtp_service_capability *cap = l->data; if (cap->category == AVDTP_MEDIA_CODEC) return cap; } return NULL; } static gboolean avdtp_stream_has_capability(struct avdtp_stream *stream, struct avdtp_service_capability *cap) { GSList *l; struct avdtp_service_capability *stream_cap; for (l = stream->caps; l; l = g_slist_next(l)) { stream_cap = l->data; if (stream_cap->category != cap->category || stream_cap->length != cap->length) continue; if (memcmp(stream_cap->data, cap->data, cap->length) == 0) return TRUE; } return FALSE; } gboolean avdtp_stream_has_capabilities(struct avdtp_stream *stream, GSList *caps) { for (; caps; caps = g_slist_next(caps)) { struct avdtp_service_capability *cap = caps->data; if (!avdtp_stream_has_capability(stream, cap)) return FALSE; } return TRUE; } struct avdtp_remote_sep *avdtp_stream_get_remote_sep( struct avdtp_stream *stream) { GSList *l; for (l = stream->session->seps; l; l = l->next) { struct avdtp_remote_sep *sep = l->data; if (sep->seid == stream->rseid) return sep; } return NULL; } gboolean avdtp_stream_set_transport(struct avdtp_stream *stream, int fd, size_t imtu, size_t omtu) { GIOChannel *io; if (stream != stream->session->pending_open) return FALSE; if (set_priority(fd, 5) < 0) return FALSE; io = g_io_channel_unix_new(fd); handle_transport_connect(stream->session, io, imtu, omtu); g_io_channel_unref(io); return TRUE; } gboolean avdtp_stream_get_transport(struct avdtp_stream *stream, int *sock, uint16_t *imtu, uint16_t *omtu, GSList **caps) { if (stream->io == NULL) return FALSE; if (sock) *sock = g_io_channel_unix_get_fd(stream->io); if (omtu) *omtu = stream->omtu; if (imtu) *imtu = stream->imtu; if (caps) *caps = stream->caps; return TRUE; } static int process_queue(struct avdtp *session) { GSList **queue, *l; struct pending_req *req; if (session->req) return 0; if (session->prio_queue) queue = &session->prio_queue; else queue = &session->req_queue; if (!*queue) return 0; l = *queue; req = l->data; *queue = g_slist_remove(*queue, req); return send_req(session, FALSE, req); } struct avdtp_service_capability *avdtp_get_codec(struct avdtp_remote_sep *sep) { return sep->codec; } struct avdtp_service_capability *avdtp_service_cap_new(uint8_t category, const void *data, int length) { struct avdtp_service_capability *cap; if (category < AVDTP_MEDIA_TRANSPORT || category > AVDTP_DELAY_REPORTING) return NULL; if (length > 0 && !data) return NULL; cap = g_malloc(sizeof(struct avdtp_service_capability) + length); cap->category = category; cap->length = length; if (length > 0) memcpy(cap->data, data, length); return cap; } static gboolean process_discover(gpointer data) { struct avdtp *session = data; session->discover->id = 0; finalize_discovery(session, 0); return FALSE; } int avdtp_discover(struct avdtp *session, avdtp_discover_cb_t cb, void *user_data) { int err; if (session->discover) return -EBUSY; session->discover = g_new0(struct discover_callback, 1); if (session->seps) { session->discover->cb = cb; session->discover->user_data = user_data; session->discover->id = g_idle_add(process_discover, session); return 0; } err = send_request(session, FALSE, NULL, AVDTP_DISCOVER, NULL, 0); if (err == 0) { session->discover->cb = cb; session->discover->user_data = user_data; } return err; } gboolean avdtp_stream_remove_cb(struct avdtp *session, struct avdtp_stream *stream, unsigned int id) { GSList *l; struct stream_callback *cb; if (!stream) return FALSE; for (cb = NULL, l = stream->callbacks; l != NULL; l = l->next) { struct stream_callback *tmp = l->data; if (tmp && tmp->id == id) { cb = tmp; break; } } if (!cb) return FALSE; stream->callbacks = g_slist_remove(stream->callbacks, cb); g_free(cb); return TRUE; } unsigned int avdtp_stream_add_cb(struct avdtp *session, struct avdtp_stream *stream, avdtp_stream_state_cb cb, void *data) { struct stream_callback *stream_cb; static unsigned int id = 0; stream_cb = g_new(struct stream_callback, 1); stream_cb->cb = cb; stream_cb->user_data = data; stream_cb->id = ++id; stream->callbacks = g_slist_append(stream->callbacks, stream_cb); return stream_cb->id; } int avdtp_get_configuration(struct avdtp *session, struct avdtp_stream *stream) { struct seid_req req; memset(&req, 0, sizeof(req)); req.acp_seid = stream->rseid; return send_request(session, FALSE, stream, AVDTP_GET_CONFIGURATION, &req, sizeof(req)); } static void copy_capabilities(gpointer data, gpointer user_data) { struct avdtp_service_capability *src_cap = data; struct avdtp_service_capability *dst_cap; GSList **l = user_data; dst_cap = avdtp_service_cap_new(src_cap->category, src_cap->data, src_cap->length); *l = g_slist_append(*l, dst_cap); } int avdtp_set_configuration(struct avdtp *session, struct avdtp_remote_sep *rsep, struct avdtp_local_sep *lsep, GSList *caps, struct avdtp_stream **stream) { struct setconf_req *req; struct avdtp_stream *new_stream; unsigned char *ptr; int err, caps_len; struct avdtp_service_capability *cap; GSList *l; if (!(lsep && rsep)) return -EINVAL; DBG("%p: int_seid=%u, acp_seid=%u", session, lsep->info.seid, rsep->seid); new_stream = g_new0(struct avdtp_stream, 1); new_stream->session = session; new_stream->lsep = lsep; new_stream->rseid = rsep->seid; if (rsep->delay_reporting && lsep->delay_reporting) { struct avdtp_service_capability *delay_reporting; delay_reporting = avdtp_service_cap_new(AVDTP_DELAY_REPORTING, NULL, 0); caps = g_slist_append(caps, delay_reporting); new_stream->delay_reporting = TRUE; } g_slist_foreach(caps, copy_capabilities, &new_stream->caps); /* Calculate total size of request */ for (l = caps, caps_len = 0; l != NULL; l = g_slist_next(l)) { cap = l->data; caps_len += cap->length + 2; } req = g_malloc0(sizeof(struct setconf_req) + caps_len); req->int_seid = lsep->info.seid; req->acp_seid = rsep->seid; /* Copy the capabilities into the request */ for (l = caps, ptr = req->caps; l != NULL; l = g_slist_next(l)) { cap = l->data; memcpy(ptr, cap, cap->length + 2); ptr += cap->length + 2; } err = send_request(session, FALSE, new_stream, AVDTP_SET_CONFIGURATION, req, sizeof(struct setconf_req) + caps_len); if (err < 0) stream_free(new_stream); else { lsep->info.inuse = 1; lsep->stream = new_stream; rsep->stream = new_stream; session->streams = g_slist_append(session->streams, new_stream); if (stream) *stream = new_stream; } g_free(req); return err; } int avdtp_open(struct avdtp *session, struct avdtp_stream *stream) { struct seid_req req; if (!g_slist_find(session->streams, stream)) return -EINVAL; if (stream->lsep->state > AVDTP_STATE_CONFIGURED) return -EINVAL; memset(&req, 0, sizeof(req)); req.acp_seid = stream->rseid; return send_request(session, FALSE, stream, AVDTP_OPEN, &req, sizeof(req)); } static gboolean start_timeout(gpointer user_data) { struct avdtp_stream *stream = user_data; struct avdtp *session = stream->session; stream->open_acp = FALSE; if (avdtp_start(session, stream) < 0) error("wait_timeout: avdtp_start failed"); stream->start_timer = 0; return FALSE; } int avdtp_start(struct avdtp *session, struct avdtp_stream *stream) { struct start_req req; int ret; if (!g_slist_find(session->streams, stream)) return -EINVAL; if (stream->lsep->state != AVDTP_STATE_OPEN) return -EINVAL; /* Recommendation 12: * If the RD has configured and opened a stream it is also responsible * to start the streaming via GAVDP_START. */ if (stream->open_acp) { /* If timer already active wait it */ if (stream->start_timer) return 0; stream->start_timer = g_timeout_add_seconds(START_TIMEOUT, start_timeout, stream); return 0; } if (stream->close_int == TRUE) { error("avdtp_start: rejecting start since close is initiated"); return -EINVAL; } if (stream->starting == TRUE) { DBG("stream already started"); return -EINPROGRESS; } memset(&req, 0, sizeof(req)); req.first_seid.seid = stream->rseid; ret = send_request(session, FALSE, stream, AVDTP_START, &req, sizeof(req)); if (ret == 0) stream->starting = TRUE; return ret; } int avdtp_close(struct avdtp *session, struct avdtp_stream *stream, gboolean immediate) { struct seid_req req; int ret; if (!g_slist_find(session->streams, stream)) return -EINVAL; if (stream->close_int == TRUE) { error("avdtp_close: rejecting since close is already initiated"); return -EINVAL; } /* If stream is not yet in the OPEN state, let's use ABORT_CMD */ if (stream->lsep->state < AVDTP_STATE_OPEN) return avdtp_abort(session, stream); if (immediate && session->req && stream == session->req->stream) return avdtp_abort(session, stream); memset(&req, 0, sizeof(req)); req.acp_seid = stream->rseid; ret = send_request(session, FALSE, stream, AVDTP_CLOSE, &req, sizeof(req)); if (ret == 0) stream->close_int = TRUE; return ret; } int avdtp_suspend(struct avdtp *session, struct avdtp_stream *stream) { struct seid_req req; if (!g_slist_find(session->streams, stream)) return -EINVAL; if (stream->lsep->state <= AVDTP_STATE_OPEN || stream->close_int) return -EINVAL; memset(&req, 0, sizeof(req)); req.acp_seid = stream->rseid; return send_request(session, FALSE, stream, AVDTP_SUSPEND, &req, sizeof(req)); } int avdtp_abort(struct avdtp *session, struct avdtp_stream *stream) { struct seid_req req; int ret; if (!g_slist_find(session->streams, stream)) return -EINVAL; if (stream->lsep->state == AVDTP_STATE_ABORTING) return -EINVAL; if (session->req && session->req->timeout > 0 && stream == session->req->stream) return cancel_request(session, ECANCELED); memset(&req, 0, sizeof(req)); req.acp_seid = stream->rseid; ret = send_request(session, TRUE, stream, AVDTP_ABORT, &req, sizeof(req)); if (ret == 0) stream->abort_int = TRUE; return ret; } int avdtp_delay_report(struct avdtp *session, struct avdtp_stream *stream, uint16_t delay) { struct delay_req req; if (!g_slist_find(session->streams, stream)) return -EINVAL; if (stream->lsep->state != AVDTP_STATE_CONFIGURED && stream->lsep->state != AVDTP_STATE_STREAMING) return -EINVAL; if (!stream->delay_reporting || session->version < 0x0103) return -EINVAL; stream->delay = delay; memset(&req, 0, sizeof(req)); req.acp_seid = stream->rseid; req.delay = htons(delay); return send_request(session, TRUE, stream, AVDTP_DELAY_REPORT, &req, sizeof(req)); } struct avdtp_local_sep *avdtp_register_sep(struct queue *lseps, uint8_t type, uint8_t media_type, uint8_t codec_type, gboolean delay_reporting, struct avdtp_sep_ind *ind, struct avdtp_sep_cfm *cfm, void *user_data) { struct avdtp_local_sep *sep; uint8_t seid = util_get_uid(&seids, MAX_SEID); if (!seid) return NULL; sep = g_new0(struct avdtp_local_sep, 1); sep->state = AVDTP_STATE_IDLE; sep->info.seid = seid; sep->info.type = type; sep->info.media_type = media_type; sep->codec = codec_type; sep->ind = ind; sep->cfm = cfm; sep->user_data = user_data; sep->delay_reporting = delay_reporting; DBG("SEP %p registered: type:%d codec:%d seid:%d", sep, sep->info.type, sep->codec, sep->info.seid); queue_push_tail(lseps, sep); return sep; } void avdtp_sep_set_vendor_codec(struct avdtp_local_sep *sep, uint32_t vendor_id, uint16_t codec_id) { sep->vndcodec_vendor = vendor_id; sep->vndcodec_codec = codec_id; } int avdtp_unregister_sep(struct queue *lseps, struct avdtp_local_sep *sep) { if (!sep) return -EINVAL; if (sep->stream) release_stream(sep->stream, sep->stream->session); DBG("SEP %p unregistered: type:%d codec:%d seid:%d", sep, sep->info.type, sep->codec, sep->info.seid); util_clear_uid(&seids, sep->info.seid); queue_remove(lseps, sep); g_free(sep); return 0; } const char *avdtp_strerror(struct avdtp_error *err) { if (err->category == AVDTP_ERRNO) return strerror(err->err.posix_errno); switch (err->err.error_code) { case AVDTP_BAD_HEADER_FORMAT: return "Bad Header Format"; case AVDTP_BAD_LENGTH: return "Bad Packet Length"; case AVDTP_BAD_ACP_SEID: return "Bad Acceptor SEID"; case AVDTP_SEP_IN_USE: return "Stream End Point in Use"; case AVDTP_SEP_NOT_IN_USE: return "Stream End Point Not in Use"; case AVDTP_BAD_SERV_CATEGORY: return "Bad Service Category"; case AVDTP_BAD_PAYLOAD_FORMAT: return "Bad Payload format"; case AVDTP_NOT_SUPPORTED_COMMAND: return "Command Not Supported"; case AVDTP_INVALID_CAPABILITIES: return "Invalid Capabilities"; case AVDTP_BAD_RECOVERY_TYPE: return "Bad Recovery Type"; case AVDTP_BAD_MEDIA_TRANSPORT_FORMAT: return "Bad Media Transport Format"; case AVDTP_BAD_RECOVERY_FORMAT: return "Bad Recovery Format"; case AVDTP_BAD_ROHC_FORMAT: return "Bad Header Compression Format"; case AVDTP_BAD_CP_FORMAT: return "Bad Content Protection Format"; case AVDTP_BAD_MULTIPLEXING_FORMAT: return "Bad Multiplexing Format"; case AVDTP_UNSUPPORTED_CONFIGURATION: return "Configuration not supported"; case AVDTP_BAD_STATE: return "Bad State"; default: return "Unknown error"; } } avdtp_state_t avdtp_sep_get_state(struct avdtp_local_sep *sep) { return sep->state; } gboolean avdtp_has_stream(struct avdtp *session, struct avdtp_stream *stream) { return g_slist_find(session->streams, stream) ? TRUE : FALSE; } bluez-5.82/android/PaxHeaders/hal.h0000644000000000000000000000005014015011623014174 xustar0020 atime=1743516862 20 ctime=1743591276 bluez-5.82/android/hal.h0000644000000000000000000000250014015011623013652 0ustar00rootroot/* SPDX-License-Identifier: Apache-2.0 */ /* * Copyright (C) 2013 Intel Corporation * */ #include #include #include #include #include #include #include #include #include #include #include #define PLATFORM_VER(a, b, c) ((a << 16) | ( b << 8) | (c)) #if ANDROID_VERSION >= PLATFORM_VER(5, 0, 0) #include #include #endif btsock_interface_t *bt_get_socket_interface(void); bthh_interface_t *bt_get_hidhost_interface(void); btpan_interface_t *bt_get_pan_interface(void); btav_interface_t *bt_get_a2dp_interface(void); btrc_interface_t *bt_get_avrcp_interface(void); bthf_interface_t *bt_get_handsfree_interface(void); btgatt_interface_t *bt_get_gatt_interface(void); bthl_interface_t *bt_get_health_interface(void); #if ANDROID_VERSION >= PLATFORM_VER(5, 0, 0) btrc_ctrl_interface_t *bt_get_avrcp_ctrl_interface(void); bthf_client_interface_t *bt_get_hf_client_interface(void); btmce_interface_t *bt_get_map_client_interface(void); btav_interface_t *bt_get_a2dp_sink_interface(void); #endif void bt_thread_associate(void); void bt_thread_disassociate(void); bluez-5.82/android/PaxHeaders/hal-utils.h0000644000000000000000000000005014015011623015332 xustar0020 atime=1743516862 20 ctime=1743591276 bluez-5.82/android/hal-utils.h0000644000000000000000000001007514015011623015016 0ustar00rootroot/* SPDX-License-Identifier: Apache-2.0 */ /* * Copyright (C) 2013 Intel Corporation * */ #include #include #define MAX_UUID_STR_LEN 37 #define HAL_UUID_LEN 16 #define MAX_ADDR_STR_LEN 18 static const char BT_BASE_UUID[] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x80, 0x00, 0x00, 0x80, 0x5f, 0x9b, 0x34, 0xfb }; const char *bt_uuid_t2str(const uint8_t *uuid, char *buf); const char *btuuid2str(const uint8_t *uuid); const char *bt_bdaddr_t2str(const bt_bdaddr_t *bd_addr, char *buf); void str2bt_bdaddr_t(const char *str, bt_bdaddr_t *bd_addr); void str2bt_uuid_t(const char *str, bt_uuid_t *uuid); const char *btproperty2str(const bt_property_t *property); const char *bdaddr2str(const bt_bdaddr_t *bd_addr); int get_config(const char *config_key, char *value, const char *fallback); /* * Begin mapping section * * There are some mappings between integer values (enums) and strings * to be presented to user. To make it easier to convert between those two * set of macros is given. It is specially useful when we want to have * strings that match constants from header files like: * BT_STATUS_SUCCESS (0) and corresponding "BT_STATUS_SUCCESS" * Example of usage: * * INTMAP(int, -1, "invalid") * DELEMENT(BT_STATUS_SUCCESS) * DELEMENT(BT_STATUS_FAIL) * MELEMENT(123, "Some strange value") * ENDMAP * * Just by doing this we have mapping table plus two functions: * int str2int(const char *str); * const char *int2str(int v); * * second argument to INTMAP specifies value to be returned from * str2int function when there is not mapping for such number * third argument specifies default value to be returned from int2str * * If same mapping is to be used in several source files put * INTMAP in c file and DECINTMAP in h file. * * For mappings that are to be used in single file only * use SINTMAP which will create the same but everything will be marked * as static. */ struct int2str { int val; /* int value */ const char *str; /* corresponding string */ }; int int2str_findint(int v, const struct int2str m[]); int int2str_findstr(const char *str, const struct int2str m[]); const char *enum_defines(void *v, int i); const char *enum_strings(void *v, int i); const char *enum_one_string(void *v, int i); #define TYPE_ENUM(type) ((void *) &__##type##2str[0]) #define DECINTMAP(type) \ extern struct int2str __##type##2str[]; \ const char *type##2##str(type v); \ type str##2##type(const char *str); \ #define INTMAP(type, deft, defs) \ const char *type##2##str(type v) \ { \ int i = int2str_findint((int) v, __##type##2str); \ return (i < 0) ? defs : __##type##2str[i].str; \ } \ type str##2##type(const char *str) \ { \ int i = int2str_findstr(str, __##type##2str); \ return (i < 0) ? (type) deft : (type) (__##type##2str[i].val); \ } \ struct int2str __##type##2str[] = { #define SINTMAP(type, deft, defs) \ static struct int2str __##type##2str[]; \ static inline const char *type##2##str(type v) \ { \ int i = int2str_findint((int) v, __##type##2str); \ return (i < 0) ? defs : __##type##2str[i].str; \ } \ static inline type str##2##type(const char *str) \ { \ int i = int2str_findstr(str, __##type##2str); \ return (i < 0) ? (type) deft : (type) (__##type##2str[i].val); \ } \ static struct int2str __##type##2str[] = { #define ENDMAP {0, NULL} }; /* use this to generate string from header file constant */ #define MELEMENT(v, s) {v, s} /* use this to have arbitrary mapping from int to string */ #define DELEMENT(s) {s, #s} /* End of mapping section */ DECINTMAP(bt_status_t); DECINTMAP(bt_state_t); DECINTMAP(bt_device_type_t); DECINTMAP(bt_scan_mode_t); DECINTMAP(bt_discovery_state_t); DECINTMAP(bt_acl_state_t); DECINTMAP(bt_bond_state_t); DECINTMAP(bt_ssp_variant_t); DECINTMAP(bt_property_type_t); DECINTMAP(bt_cb_thread_evt); static inline uint16_t get_le16(const void *src) { const struct __attribute__((packed)) { uint16_t le16; } *p = src; return le16toh(p->le16); } static inline void put_le16(uint16_t val, void *dst) { struct __attribute__((packed)) { uint16_t le16; } *p = dst; p->le16 = htole16(val); } bluez-5.82/android/PaxHeaders/hal-a2dp-sink.c0000644000000000000000000000005014015011623015755 xustar0020 atime=1743516862 20 ctime=1743591276 bluez-5.82/android/hal-a2dp-sink.c0000644000000000000000000000633314015011623015443 0ustar00rootroot// SPDX-License-Identifier: Apache-2.0 /* * Copyright (C) 2014 Intel Corporation * */ #include #include #include #include "hal-log.h" #include "hal.h" #include "hal-msg.h" #include "hal-ipc.h" static const btav_callbacks_t *cbs = NULL; static bool interface_ready(void) { return cbs != NULL; } static void handle_conn_state(void *buf, uint16_t len, int fd) { struct hal_ev_a2dp_conn_state *ev = buf; if (cbs->connection_state_cb) cbs->connection_state_cb(ev->state, (bt_bdaddr_t *) (ev->bdaddr)); } static void handle_audio_state(void *buf, uint16_t len, int fd) { struct hal_ev_a2dp_audio_state *ev = buf; if (cbs->audio_state_cb) cbs->audio_state_cb(ev->state, (bt_bdaddr_t *)(ev->bdaddr)); } static void handle_audio_config(void *buf, uint16_t len, int fd) { struct hal_ev_a2dp_audio_config *ev = buf; if (cbs->audio_config_cb) cbs->audio_config_cb((bt_bdaddr_t *)(ev->bdaddr), ev->sample_rate, ev->channel_count); } /* * handlers will be called from notification thread context, * index in table equals to 'opcode - HAL_MINIMUM_EVENT' */ static const struct hal_ipc_handler ev_handlers[] = { /* HAL_EV_A2DP_CONN_STATE */ { handle_conn_state, false, sizeof(struct hal_ev_a2dp_conn_state) }, /* HAL_EV_A2DP_AUDIO_STATE */ { handle_audio_state, false, sizeof(struct hal_ev_a2dp_audio_state) }, /* HAL_EV_A2DP_AUDIO_CONFIG */ { handle_audio_config, false, sizeof(struct hal_ev_a2dp_audio_config) }, }; static bt_status_t a2dp_connect(bt_bdaddr_t *bd_addr) { struct hal_cmd_a2dp_connect cmd; DBG(""); if (!interface_ready()) return BT_STATUS_NOT_READY; memcpy(cmd.bdaddr, bd_addr, sizeof(cmd.bdaddr)); return hal_ipc_cmd(HAL_SERVICE_ID_A2DP_SINK, HAL_OP_A2DP_CONNECT, sizeof(cmd), &cmd, NULL, NULL, NULL); } static bt_status_t disconnect(bt_bdaddr_t *bd_addr) { struct hal_cmd_a2dp_disconnect cmd; DBG(""); if (!interface_ready()) return BT_STATUS_NOT_READY; memcpy(cmd.bdaddr, bd_addr, sizeof(cmd.bdaddr)); return hal_ipc_cmd(HAL_SERVICE_ID_A2DP_SINK, HAL_OP_A2DP_DISCONNECT, sizeof(cmd), &cmd, NULL, NULL, NULL); } static bt_status_t init(btav_callbacks_t *callbacks) { struct hal_cmd_register_module cmd; int ret; DBG(""); if (interface_ready()) return BT_STATUS_DONE; cbs = callbacks; hal_ipc_register(HAL_SERVICE_ID_A2DP_SINK, ev_handlers, sizeof(ev_handlers)/sizeof(ev_handlers[0])); cmd.service_id = HAL_SERVICE_ID_A2DP_SINK; cmd.mode = HAL_MODE_DEFAULT; cmd.max_clients = 1; ret = hal_ipc_cmd(HAL_SERVICE_ID_CORE, HAL_OP_REGISTER_MODULE, sizeof(cmd), &cmd, NULL, NULL, NULL); if (ret != BT_STATUS_SUCCESS) { cbs = NULL; hal_ipc_unregister(HAL_SERVICE_ID_A2DP_SINK); } return ret; } static void cleanup(void) { struct hal_cmd_unregister_module cmd; DBG(""); if (!interface_ready()) return; cmd.service_id = HAL_SERVICE_ID_A2DP_SINK; hal_ipc_cmd(HAL_SERVICE_ID_CORE, HAL_OP_UNREGISTER_MODULE, sizeof(cmd), &cmd, NULL, NULL, NULL); hal_ipc_unregister(HAL_SERVICE_ID_A2DP_SINK); cbs = NULL; } static btav_interface_t iface = { .size = sizeof(iface), .init = init, .connect = a2dp_connect, .disconnect = disconnect, .cleanup = cleanup }; btav_interface_t *bt_get_a2dp_sink_interface(void) { return &iface; } bluez-5.82/android/PaxHeaders/pics-a2dp.txt0000644000000000000000000000005012537515745015627 xustar0020 atime=1743516876 20 ctime=1743591288 bluez-5.82/android/pics-a2dp.txt0000644000000000000000000001752112537515745015316 0ustar00rootrootA2DP PICS for the PTS tool. PTS version: 6.1 * - different than PTS defaults # - not yet implemented/supported M - mandatory if such role selected O - optional Profile Version ------------------------------------------------------------------------------- Parameter Name Selected Description ------------------------------------------------------------------------------- TSPC_A2DP_0_1 False A2DP 1.0 (C.1) TSPC_A2DP_0_2 False A2DP 1.2 (C.1) TSPC_A2DP_0_3 True (*) A2DP 1.3 (C.1) ------------------------------------------------------------------------------- C.1: It is mandatory to select one of the profile versions. ------------------------------------------------------------------------------- Roles ------------------------------------------------------------------------------- Parameter Name Selected Description ------------------------------------------------------------------------------- TSPC_A2DP_1_1 True (*) Role: Source (C.1) TSPC_A2DP_1_2 False Role: Sink (C.1) ------------------------------------------------------------------------------- C.1: It is mandatory to support at least one of the defined roles. ------------------------------------------------------------------------------- A2DP SRC Features ------------------------------------------------------------------------------- Parameter Name Selected Description ------------------------------------------------------------------------------- TSPC_A2DP_2_1 True SRC: Initiate connection establishment (M) TSPC_A2DP_2_2 True SRC: Accept connection establishment (M) TSPC_A2DP_2_3 True SRC: Initiate start streaming (M) TSPC_A2DP_2_4 True SRC: Accept start streaming (M) TSPC_A2DP_2_5 True SRC: Send audio stream (M) TSPC_A2DP_2_6 True SRC: Initiate connection release (M) TSPC_A2DP_2_7 True SRC: Accept connection release (M) TSPC_A2DP_2_8 True (*) SRC: Initiate suspend (O) TSPC_A2DP_2_9 True (*) SRC: Accept suspend (O) TSPC_A2DP_2_10 True SRC: SBC encoder (M) TSPC_A2DP_2_10a False SRC: Encode and Forward Audio Stream (O) TSPC_A2DP_2_11 False SRC: SBC Configurations in 16 KHz sampling (O) TSPC_A2DP_2_12 False SRC: SBC Configurations in 32 KHz sampling (O) TSPC_A2DP_2_13 True (*) SRC: SBC Configurations in 44.1 KHz sampling (C.1) TSPC_A2DP_2_14 True (*) SRC: SBC Configurations in 48 KHz sampling (C.1) TSPC_A2DP_2_15 False SRC: Delay Reporting (C.2) TSPC_A2DP_2_16 False SRC: SRC video playback via Bluetooth VDP (C.3) TSPC_A2DP_2_17 False SRC: SRC video playback on a local video display (C.3) ------------------------------------------------------------------------------- C.1: At least one of the values shall be supported. C.2: Mandatory if A2DP 0/3 AND (2/16 OR 2/17) is supported, otherwise excluded. C.3: Optional to support if A2DP 0/3 is supported, otherwise excluded. ------------------------------------------------------------------------------- Supported Codecs in SRC ------------------------------------------------------------------------------- Parameter Name Selected Description ------------------------------------------------------------------------------- TSPC_A2DP_3_1 True SRC: SBC encoder (M) TSPC_A2DP_3_1a False SRC: Encode and Forward SBC Audio Stream (O) TSPC_A2DP_3_2 False SRC: Optional codec (O) TSPC_A2DP_3_3 False SRC: MPEG-1,2 Audio decoder (C.1) TSPC_A2DP_3_4 False SRC: MPEG-1,2 Audio encoder (C.1) TSPC_A2DP_3_5 False SRC: MPEG-2,4 AAC decoder (C.1) TSPC_A2DP_3_6 False SRC: MPEG-2,4 AAC encoder (C.1) TSPC_A2DP_3_7 False SRC: ATRAC family decoder (C.1) TSPC_A2DP_3_8 False SRC: ATRAC family encoder (C.1) ------------------------------------------------------------------------------- C.1: At least one of the implementations shall be supported if 3/2 is supported, else excluded. ------------------------------------------------------------------------------- Supported Codec Features in SRC ------------------------------------------------------------------------------- Parameter Name Selected Description ------------------------------------------------------------------------------- TSPC_A2DP_3a_1 True SRC: Channel Mode - Mono (M) TSPC_A2DP_3a_2 True (*) SRC: Channel Mode - Dual Channel (C.1) TSPC_A2DP_3a_3 True (*) SRC: Channel Mode - Stereo (C.1) TSPC_A2DP_3a_4 True (*) SRC: Channel Mode - Joint Stereo (C.1) TSPC_A2DP_3a_5 True SRC: Block Length 4 (M) TSPC_A2DP_3a_6 True SRC: Block Length 8 (M) TSPC_A2DP_3a_7 True SRC: Block Length 12 (M) TSPC_A2DP_3a_8 True SRC: Block Length 16 (M) TSPC_A2DP_3a_9 True (*) SRC: Subbands - 4 (O) TSPC_A2DP_3a_10 True SRC: Subbands - 8 (M) TSPC_A2DP_3a_11 True (*) SRC: Allocation - SNR (O) TSPC_A2DP_3a_12 True SRC: Allocation - Loudness (M) ------------------------------------------------------------------------------- C.1: At least one of the values shall be supported. ------------------------------------------------------------------------------- A2DP Sink Features ------------------------------------------------------------------------------- Parameter Name Selected Description ------------------------------------------------------------------------------- TSPC_A2DP_4_1 False SNK: Initiate connection establishment (O) TSPC_A2DP_4_2 False (*) SNK: Accept connection establishment (M) TSPC_A2DP_4_3 False SNK: Initiate start streaming (O) TSPC_A2DP_4_4 False (*) SNK: Accept start streaming (M) TSPC_A2DP_4_5 False (*) SNK: Receive audio stream (M) TSPC_A2DP_4_6 False SNK: Initiate connection release (O) TSPC_A2DP_4_7 False (*) SNK: Accept connection release (M) TSPC_A2DP_4_8 False SNK: Initiate suspend (O) TSPC_A2DP_4_9 False SNK: Accept suspend (O) TSPC_A2DP_4_10 False (*) SNK: SBC decoder (M) TSPC_A2DP_4_10a False SNK: Decode and Forward Audio Stream (O) TSPC_A2DP_4_11 False SNK: SBC Configurations in 16 KHz sampling (O) TSPC_A2DP_4_12 False SNK: SBC Configurations in 32 KHz sampling (O) TSPC_A2DP_4_13 False (*) SNK: SBC Configurations in 44.1 KHz sampling (M) TSPC_A2DP_4_14 False (*) SNK: SBC Configurations in 48 KHz sampling (M) TSPC_A2DP_4_15 False SNK: Delay Reporting (C.1) ------------------------------------------------------------------------------- C.1: Mandatory to support if A2DP 0/3 is supported, otherwise excluded. ------------------------------------------------------------------------------- Supported codecs in SNK ------------------------------------------------------------------------------- Parameter Name Selected Description ------------------------------------------------------------------------------- TSPC_A2DP_5_1 False (*) SNK: SBC decoder (M) TSPC_A2DP_5_1a False SNK: Decode and Forward SBC Audio Stream (O) TSPC_A2DP_5_2 False SNK: Optional codec decoder (O) TSPC_A2DP_5_3 False SNK: MPEG-1,2 Audio (C.1) TSPC_A2DP_5_4 False SNK: MPEG-2,4 AAC (C.1) TSPC_A2DP_5_5 False SNK: ATRAC family (C.1) ------------------------------------------------------------------------------- C.1: At least one codec shall be supported if Table 5/2 is supported, otherwise excluded. ------------------------------------------------------------------------------- Supported Codec Features in SNK ------------------------------------------------------------------------------- Parameter Name Selected Description ------------------------------------------------------------------------------- TSPC_A2DP_5a_1 False (*) SNK: Channel Mode - Mono (M) TSPC_A2DP_5a_2 False (*) SNK: Channel Mode - Dual Channel (M) TSPC_A2DP_5a_3 False (*) SNK: Channel Mode - Stereo (M) TSPC_A2DP_5a_4 False (*) SNK: Channel Mode - Joint Stereo (M) TSPC_A2DP_5a_5 False (*) SNK: Block Length 4 (M) TSPC_A2DP_5a_6 False (*) SNK: Block Length 8 (M) TSPC_A2DP_5a_7 False (*) SNK: Block Length 12 (M) TSPC_A2DP_5a_8 False (*) SNK: Block Length 16 (M) TSPC_A2DP_5a_9 False (*) SNK: Subbands - 4 (M) TSPC_A2DP_5a_10 False (*) SNK: Subbands - 8 (M) TSPC_A2DP_5a_11 False (*) SNK: Allocation - SNR (M) TSPC_A2DP_5a_12 False (*) SNK: Allocation - Loudness (M) ------------------------------------------------------------------------------- bluez-5.82/android/PaxHeaders/log.c0000644000000000000000000000005014015011623014204 xustar0020 atime=1743516875 20 ctime=1743591288 bluez-5.82/android/log.c0000644000000000000000000000644714015011623013700 0ustar00rootroot// SPDX-License-Identifier: GPL-2.0-or-later /* * * BlueZ - Bluetooth protocol stack for Linux * * Copyright (C) 2014 Intel Corporation. All rights reserved. * * */ #ifdef HAVE_CONFIG_H #include #endif #include #include #include #include #include #include #include #include #include #include #include #include #include "src/log.h" #define LOG_TAG "bluetoothd" #define LOG_DEBUG 3 #define LOG_INFO 4 #define LOG_WARN 5 #define LOG_ERR 6 #define LOG_ID_SYSTEM 3 struct logd_header { uint8_t id; uint16_t pid; /* Android logd expects only 2 bytes for PID */ uint32_t sec; uint32_t nsec; } __attribute__ ((packed)); static int log_fd = -1; static bool legacy_log = false; static void android_log(unsigned char level, const char *fmt, va_list ap) { struct logd_header header; struct iovec vec[4]; int cnt = 0; char *msg; static pid_t pid = 0; if (log_fd < 0) return; /* no need to call getpid all the time since we don't fork */ if (!pid) pid = getpid(); if (vasprintf(&msg, fmt, ap) < 0) return; if (!legacy_log) { struct timespec ts; clock_gettime(CLOCK_REALTIME, &ts); header.id = LOG_ID_SYSTEM; header.pid = pid; header.sec = ts.tv_sec; header.nsec = ts.tv_nsec; vec[0].iov_base = &header; vec[0].iov_len = sizeof(header); cnt += 1; } vec[cnt + 0].iov_base = &level; vec[cnt + 0].iov_len = sizeof(level); vec[cnt + 1].iov_base = LOG_TAG; vec[cnt + 1].iov_len = sizeof(LOG_TAG); vec[cnt + 2].iov_base = msg; vec[cnt + 2].iov_len = strlen(msg) + 1; cnt += 3; writev(log_fd, vec, cnt); free(msg); } void info(const char *format, ...) { va_list ap; va_start(ap, format); android_log(LOG_INFO, format, ap); va_end(ap); } void warn(const char *format, ...) { va_list ap; va_start(ap, format); android_log(LOG_WARN, format, ap); va_end(ap); } void error(const char *format, ...) { va_list ap; va_start(ap, format); android_log(LOG_ERR, format, ap); va_end(ap); } void btd_debug(uint16_t index, const char *format, ...) { va_list ap; va_start(ap, format); android_log(LOG_DEBUG, format, ap); va_end(ap); } static bool init_legacy_log(void) { log_fd = open("/dev/log/system", O_WRONLY); if (log_fd < 0) return false; legacy_log = true; return true; } static bool init_logd(void) { struct sockaddr_un addr; log_fd = socket(PF_UNIX, SOCK_DGRAM | SOCK_CLOEXEC, 0); if (log_fd < 0) return false; if (fcntl(log_fd, F_SETFL, O_NONBLOCK) < 0) goto failed; memset(&addr, 0, sizeof(addr)); addr.sun_family = AF_UNIX; strcpy(addr.sun_path, "/dev/socket/logdw"); if (connect(log_fd, (struct sockaddr *)&addr, sizeof(addr)) < 0) goto failed; return true; failed: close(log_fd); log_fd = -1; return false; } extern struct btd_debug_desc __start___debug[]; extern struct btd_debug_desc __stop___debug[]; void __btd_log_init(const char *debug, int detach) { if (!init_logd() && !init_legacy_log()) return; if (debug) { struct btd_debug_desc *desc; for (desc = __start___debug; desc < __stop___debug; desc++) desc->flags |= BTD_DEBUG_FLAG_PRINT; } info("Bluetooth daemon %s", VERSION); } void __btd_log_cleanup(void) { if (log_fd < 0) return; close(log_fd); log_fd = -1; } bluez-5.82/android/PaxHeaders/audio-msg.h0000644000000000000000000000005014015011623015315 xustar0020 atime=1743516862 20 ctime=1743591275 bluez-5.82/android/audio-msg.h0000644000000000000000000000264614015011623015006 0ustar00rootroot/* SPDX-License-Identifier: LGPL-2.1-or-later */ /* * * BlueZ - Bluetooth protocol stack for Linux * * Copyright (C) 2014 Intel Corporation. All rights reserved. * * */ #define BLUEZ_AUDIO_MTU 1024 static const char BLUEZ_AUDIO_SK_PATH[] = "\0bluez_audio_socket"; #define AUDIO_SERVICE_ID 0 #define AUDIO_SERVICE_ID_MAX AUDIO_SERVICE_ID #define AUDIO_STATUS_SUCCESS IPC_STATUS_SUCCESS #define AUDIO_STATUS_FAILED 0x01 #define AUDIO_OP_STATUS IPC_OP_STATUS #define AUDIO_OP_OPEN 0x01 struct audio_preset { uint8_t len; uint8_t data[0]; } __attribute__((packed)); struct audio_cmd_open { uint8_t uuid[16]; uint8_t codec; uint8_t presets; struct audio_preset preset[0]; } __attribute__((packed)); struct audio_rsp_open { uint8_t id; } __attribute__((packed)); #define AUDIO_OP_CLOSE 0x02 struct audio_cmd_close { uint8_t id; } __attribute__((packed)); #define AUDIO_OP_OPEN_STREAM 0x03 struct audio_cmd_open_stream { uint8_t id; } __attribute__((packed)); struct audio_rsp_open_stream { uint16_t id; uint16_t mtu; struct audio_preset preset[0]; } __attribute__((packed)); #define AUDIO_OP_CLOSE_STREAM 0x04 struct audio_cmd_close_stream { uint8_t id; } __attribute__((packed)); #define AUDIO_OP_RESUME_STREAM 0x05 struct audio_cmd_resume_stream { uint8_t id; } __attribute__((packed)); #define AUDIO_OP_SUSPEND_STREAM 0x06 struct audio_cmd_suspend_stream { uint8_t id; } __attribute__((packed)); bluez-5.82/android/PaxHeaders/pics-dis.txt0000644000000000000000000000005012537515745015560 xustar0020 atime=1743516876 20 ctime=1743591289 bluez-5.82/android/pics-dis.txt0000644000000000000000000000517112537515745015245 0ustar00rootrootDIS PICS for the PTS tool. PTS version: 6.1 * - different than PTS defaults M - mandatory O - optional Transport Requirements ------------------------------------------------------------------------------- Parameter Name Selected Description ------------------------------------------------------------------------------- TSPC_DIS_1_1 True Service supported over BR/EDR (C.1) TSPC_DIS_1_2 True Service supported over LE (C.1) ------------------------------------------------------------------------------- C.1: Mandatory to support at least one of TSPC_DIS_1_1 or TSPC_DIS_1_2. ------------------------------------------------------------------------------- Service Requirements ------------------------------------------------------------------------------- Parameter Name Selected Description ------------------------------------------------------------------------------- TSPC_DIS_2_1 True Device Information Service (M) TSPC_DIS_2_2 True Manufacturer Name String Characteristic (O) TSPC_DIS_2_3 True Model Number String Characteristic (O) TSPC_DIS_2_4 True Serial Number String Characteristic (O) TSPC_DIS_2_5 True Hardware Revision String Characteristic (O) TSPC_DIS_2_6 True Firmware Revision String Characteristic (O) TSPC_DIS_2_7 True Software Revision String Characteristic (O) TSPC_DIS_2_8 True System ID Characteristic (O) TSPC_DIS_2_9 False (*) IEEE 11073-20601 Regulatory Certification Data List Characteristic (O) TSPC_DIS_2_10 True SDP Interoperability (C.1) TSPC_DIS_2_11 True PnP ID (O) ------------------------------------------------------------------------------- C.1: Mandatory if TSPC_DIS_1_1 (BR/EDR) is supported, otherwise excluded. ------------------------------------------------------------------------------- GATT Requirements ------------------------------------------------------------------------------- Parameter Name Selected Description ------------------------------------------------------------------------------- TSPC_DIS_3_1 True Generic Attribute Profile Server (M) ------------------------------------------------------------------------------- SDP Requirements ------------------------------------------------------------------------------- Parameter Name Selected Description ------------------------------------------------------------------------------- TSPC_DIS_4_1 True Support for server role (M) TSPC_DIS_4_2 True ProtocolDescriptorList (M) TSPC_DIS_4_3 True BrowseGroupList (M) ------------------------------------------------------------------------------- Note: Marked as False as TSPC_DIS_1_1 is set to False ------------------------------------------------------------------------------- bluez-5.82/android/PaxHeaders/sco.h0000644000000000000000000000005014015011623014214 xustar0020 atime=1743516866 20 ctime=1743591279 bluez-5.82/android/sco.h0000644000000000000000000000212514015011623013675 0ustar00rootroot/* SPDX-License-Identifier: LGPL-2.1-or-later */ /* * * BlueZ - Bluetooth protocol stack for Linux * * Copyright (C) 2014 Intel Corporation. All rights reserved. * * */ enum sco_status { SCO_STATUS_OK, SCO_STATUS_ERROR, }; struct bt_sco; struct bt_sco *bt_sco_new(const bdaddr_t *local_bdaddr); struct bt_sco *bt_sco_ref(struct bt_sco *sco); void bt_sco_unref(struct bt_sco *sco); bool bt_sco_connect(struct bt_sco *sco, const bdaddr_t *remote_addr, uint16_t voice_settings); void bt_sco_disconnect(struct bt_sco *sco); bool bt_sco_get_fd_and_mtu(struct bt_sco *sco, int *fd, uint16_t *mtu); typedef bool (*bt_sco_confirm_func_t) (const bdaddr_t *remote_addr, uint16_t *voice_settings); typedef void (*bt_sco_conn_func_t) (enum sco_status status, const bdaddr_t *addr); typedef void (*bt_sco_disconn_func_t) (const bdaddr_t *addr); void bt_sco_set_confirm_cb(struct bt_sco *sco, bt_sco_confirm_func_t func); void bt_sco_set_connect_cb(struct bt_sco *sco, bt_sco_conn_func_t func); void bt_sco_set_disconnect_cb(struct bt_sco *sco, bt_sco_disconn_func_t func); bluez-5.82/PaxHeaders/config.guess0000644000000000000000000000005014773211371014171 xustar0020 atime=1743590150 20 ctime=1743591275 bluez-5.82/config.guess0000755000000000000000000014051214773211371013660 0ustar00rootroot#! /bin/sh # Attempt to guess a canonical system name. # Copyright 1992-2022 Free Software Foundation, Inc. # shellcheck disable=SC2006,SC2268 # see below for rationale timestamp='2022-01-09' # This file is free software; you can redistribute it and/or modify it # under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, but # WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, see . # # As a special exception to the GNU General Public License, if you # distribute this file as part of a program that contains a # configuration script generated by Autoconf, you may include it under # the same distribution terms that you use for the rest of that # program. This Exception is an additional permission under section 7 # of the GNU General Public License, version 3 ("GPLv3"). # # Originally written by Per Bothner; maintained since 2000 by Ben Elliston. # # You can get the latest version of this script from: # https://git.savannah.gnu.org/cgit/config.git/plain/config.guess # # Please send patches to . # The "shellcheck disable" line above the timestamp inhibits complaints # about features and limitations of the classic Bourne shell that were # superseded or lifted in POSIX. However, this script identifies a wide # variety of pre-POSIX systems that do not have POSIX shells at all, and # even some reasonably current systems (Solaris 10 as case-in-point) still # have a pre-POSIX /bin/sh. me=`echo "$0" | sed -e 's,.*/,,'` usage="\ Usage: $0 [OPTION] Output the configuration name of the system \`$me' is run on. Options: -h, --help print this help, then exit -t, --time-stamp print date of last modification, then exit -v, --version print version number, then exit Report bugs and patches to ." version="\ GNU config.guess ($timestamp) Originally written by Per Bothner. Copyright 1992-2022 Free Software Foundation, Inc. This is free software; see the source for copying conditions. There is NO warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE." help=" Try \`$me --help' for more information." # Parse command line while test $# -gt 0 ; do case $1 in --time-stamp | --time* | -t ) echo "$timestamp" ; exit ;; --version | -v ) echo "$version" ; exit ;; --help | --h* | -h ) echo "$usage"; exit ;; -- ) # Stop option processing shift; break ;; - ) # Use stdin as input. break ;; -* ) echo "$me: invalid option $1$help" >&2 exit 1 ;; * ) break ;; esac done if test $# != 0; then echo "$me: too many arguments$help" >&2 exit 1 fi # Just in case it came from the environment. GUESS= # CC_FOR_BUILD -- compiler used by this script. Note that the use of a # compiler to aid in system detection is discouraged as it requires # temporary files to be created and, as you can see below, it is a # headache to deal with in a portable fashion. # Historically, `CC_FOR_BUILD' used to be named `HOST_CC'. We still # use `HOST_CC' if defined, but it is deprecated. # Portable tmp directory creation inspired by the Autoconf team. tmp= # shellcheck disable=SC2172 trap 'test -z "$tmp" || rm -fr "$tmp"' 0 1 2 13 15 set_cc_for_build() { # prevent multiple calls if $tmp is already set test "$tmp" && return 0 : "${TMPDIR=/tmp}" # shellcheck disable=SC2039,SC3028 { tmp=`(umask 077 && mktemp -d "$TMPDIR/cgXXXXXX") 2>/dev/null` && test -n "$tmp" && test -d "$tmp" ; } || { test -n "$RANDOM" && tmp=$TMPDIR/cg$$-$RANDOM && (umask 077 && mkdir "$tmp" 2>/dev/null) ; } || { tmp=$TMPDIR/cg-$$ && (umask 077 && mkdir "$tmp" 2>/dev/null) && echo "Warning: creating insecure temp directory" >&2 ; } || { echo "$me: cannot create a temporary directory in $TMPDIR" >&2 ; exit 1 ; } dummy=$tmp/dummy case ${CC_FOR_BUILD-},${HOST_CC-},${CC-} in ,,) echo "int x;" > "$dummy.c" for driver in cc gcc c89 c99 ; do if ($driver -c -o "$dummy.o" "$dummy.c") >/dev/null 2>&1 ; then CC_FOR_BUILD=$driver break fi done if test x"$CC_FOR_BUILD" = x ; then CC_FOR_BUILD=no_compiler_found fi ;; ,,*) CC_FOR_BUILD=$CC ;; ,*,*) CC_FOR_BUILD=$HOST_CC ;; esac } # This is needed to find uname on a Pyramid OSx when run in the BSD universe. # (ghazi@noc.rutgers.edu 1994-08-24) if test -f /.attbin/uname ; then PATH=$PATH:/.attbin ; export PATH fi UNAME_MACHINE=`(uname -m) 2>/dev/null` || UNAME_MACHINE=unknown UNAME_RELEASE=`(uname -r) 2>/dev/null` || UNAME_RELEASE=unknown UNAME_SYSTEM=`(uname -s) 2>/dev/null` || UNAME_SYSTEM=unknown UNAME_VERSION=`(uname -v) 2>/dev/null` || UNAME_VERSION=unknown case $UNAME_SYSTEM in Linux|GNU|GNU/*) LIBC=unknown set_cc_for_build cat <<-EOF > "$dummy.c" #include #if defined(__UCLIBC__) LIBC=uclibc #elif defined(__dietlibc__) LIBC=dietlibc #elif defined(__GLIBC__) LIBC=gnu #else #include /* First heuristic to detect musl libc. */ #ifdef __DEFINED_va_list LIBC=musl #endif #endif EOF cc_set_libc=`$CC_FOR_BUILD -E "$dummy.c" 2>/dev/null | grep '^LIBC' | sed 's, ,,g'` eval "$cc_set_libc" # Second heuristic to detect musl libc. if [ "$LIBC" = unknown ] && command -v ldd >/dev/null && ldd --version 2>&1 | grep -q ^musl; then LIBC=musl fi # If the system lacks a compiler, then just pick glibc. # We could probably try harder. if [ "$LIBC" = unknown ]; then LIBC=gnu fi ;; esac # Note: order is significant - the case branches are not exclusive. case $UNAME_MACHINE:$UNAME_SYSTEM:$UNAME_RELEASE:$UNAME_VERSION in *:NetBSD:*:*) # NetBSD (nbsd) targets should (where applicable) match one or # more of the tuples: *-*-netbsdelf*, *-*-netbsdaout*, # *-*-netbsdecoff* and *-*-netbsd*. For targets that recently # switched to ELF, *-*-netbsd* would select the old # object file format. This provides both forward # compatibility and a consistent mechanism for selecting the # object file format. # # Note: NetBSD doesn't particularly care about the vendor # portion of the name. We always set it to "unknown". UNAME_MACHINE_ARCH=`(uname -p 2>/dev/null || \ /sbin/sysctl -n hw.machine_arch 2>/dev/null || \ /usr/sbin/sysctl -n hw.machine_arch 2>/dev/null || \ echo unknown)` case $UNAME_MACHINE_ARCH in aarch64eb) machine=aarch64_be-unknown ;; armeb) machine=armeb-unknown ;; arm*) machine=arm-unknown ;; sh3el) machine=shl-unknown ;; sh3eb) machine=sh-unknown ;; sh5el) machine=sh5le-unknown ;; earmv*) arch=`echo "$UNAME_MACHINE_ARCH" | sed -e 's,^e\(armv[0-9]\).*$,\1,'` endian=`echo "$UNAME_MACHINE_ARCH" | sed -ne 's,^.*\(eb\)$,\1,p'` machine=${arch}${endian}-unknown ;; *) machine=$UNAME_MACHINE_ARCH-unknown ;; esac # The Operating System including object format, if it has switched # to ELF recently (or will in the future) and ABI. case $UNAME_MACHINE_ARCH in earm*) os=netbsdelf ;; arm*|i386|m68k|ns32k|sh3*|sparc|vax) set_cc_for_build if echo __ELF__ | $CC_FOR_BUILD -E - 2>/dev/null \ | grep -q __ELF__ then # Once all utilities can be ECOFF (netbsdecoff) or a.out (netbsdaout). # Return netbsd for either. FIX? os=netbsd else os=netbsdelf fi ;; *) os=netbsd ;; esac # Determine ABI tags. case $UNAME_MACHINE_ARCH in earm*) expr='s/^earmv[0-9]/-eabi/;s/eb$//' abi=`echo "$UNAME_MACHINE_ARCH" | sed -e "$expr"` ;; esac # The OS release # Debian GNU/NetBSD machines have a different userland, and # thus, need a distinct triplet. However, they do not need # kernel version information, so it can be replaced with a # suitable tag, in the style of linux-gnu. case $UNAME_VERSION in Debian*) release='-gnu' ;; *) release=`echo "$UNAME_RELEASE" | sed -e 's/[-_].*//' | cut -d. -f1,2` ;; esac # Since CPU_TYPE-MANUFACTURER-KERNEL-OPERATING_SYSTEM: # contains redundant information, the shorter form: # CPU_TYPE-MANUFACTURER-OPERATING_SYSTEM is used. GUESS=$machine-${os}${release}${abi-} ;; *:Bitrig:*:*) UNAME_MACHINE_ARCH=`arch | sed 's/Bitrig.//'` GUESS=$UNAME_MACHINE_ARCH-unknown-bitrig$UNAME_RELEASE ;; *:OpenBSD:*:*) UNAME_MACHINE_ARCH=`arch | sed 's/OpenBSD.//'` GUESS=$UNAME_MACHINE_ARCH-unknown-openbsd$UNAME_RELEASE ;; *:SecBSD:*:*) UNAME_MACHINE_ARCH=`arch | sed 's/SecBSD.//'` GUESS=$UNAME_MACHINE_ARCH-unknown-secbsd$UNAME_RELEASE ;; *:LibertyBSD:*:*) UNAME_MACHINE_ARCH=`arch | sed 's/^.*BSD\.//'` GUESS=$UNAME_MACHINE_ARCH-unknown-libertybsd$UNAME_RELEASE ;; *:MidnightBSD:*:*) GUESS=$UNAME_MACHINE-unknown-midnightbsd$UNAME_RELEASE ;; *:ekkoBSD:*:*) GUESS=$UNAME_MACHINE-unknown-ekkobsd$UNAME_RELEASE ;; *:SolidBSD:*:*) GUESS=$UNAME_MACHINE-unknown-solidbsd$UNAME_RELEASE ;; *:OS108:*:*) GUESS=$UNAME_MACHINE-unknown-os108_$UNAME_RELEASE ;; macppc:MirBSD:*:*) GUESS=powerpc-unknown-mirbsd$UNAME_RELEASE ;; *:MirBSD:*:*) GUESS=$UNAME_MACHINE-unknown-mirbsd$UNAME_RELEASE ;; *:Sortix:*:*) GUESS=$UNAME_MACHINE-unknown-sortix ;; *:Twizzler:*:*) GUESS=$UNAME_MACHINE-unknown-twizzler ;; *:Redox:*:*) GUESS=$UNAME_MACHINE-unknown-redox ;; mips:OSF1:*.*) GUESS=mips-dec-osf1 ;; alpha:OSF1:*:*) # Reset EXIT trap before exiting to avoid spurious non-zero exit code. trap '' 0 case $UNAME_RELEASE in *4.0) UNAME_RELEASE=`/usr/sbin/sizer -v | awk '{print $3}'` ;; *5.*) UNAME_RELEASE=`/usr/sbin/sizer -v | awk '{print $4}'` ;; esac # According to Compaq, /usr/sbin/psrinfo has been available on # OSF/1 and Tru64 systems produced since 1995. I hope that # covers most systems running today. This code pipes the CPU # types through head -n 1, so we only detect the type of CPU 0. ALPHA_CPU_TYPE=`/usr/sbin/psrinfo -v | sed -n -e 's/^ The alpha \(.*\) processor.*$/\1/p' | head -n 1` case $ALPHA_CPU_TYPE in "EV4 (21064)") UNAME_MACHINE=alpha ;; "EV4.5 (21064)") UNAME_MACHINE=alpha ;; "LCA4 (21066/21068)") UNAME_MACHINE=alpha ;; "EV5 (21164)") UNAME_MACHINE=alphaev5 ;; "EV5.6 (21164A)") UNAME_MACHINE=alphaev56 ;; "EV5.6 (21164PC)") UNAME_MACHINE=alphapca56 ;; "EV5.7 (21164PC)") UNAME_MACHINE=alphapca57 ;; "EV6 (21264)") UNAME_MACHINE=alphaev6 ;; "EV6.7 (21264A)") UNAME_MACHINE=alphaev67 ;; "EV6.8CB (21264C)") UNAME_MACHINE=alphaev68 ;; "EV6.8AL (21264B)") UNAME_MACHINE=alphaev68 ;; "EV6.8CX (21264D)") UNAME_MACHINE=alphaev68 ;; "EV6.9A (21264/EV69A)") UNAME_MACHINE=alphaev69 ;; "EV7 (21364)") UNAME_MACHINE=alphaev7 ;; "EV7.9 (21364A)") UNAME_MACHINE=alphaev79 ;; esac # A Pn.n version is a patched version. # A Vn.n version is a released version. # A Tn.n version is a released field test version. # A Xn.n version is an unreleased experimental baselevel. # 1.2 uses "1.2" for uname -r. OSF_REL=`echo "$UNAME_RELEASE" | sed -e 's/^[PVTX]//' | tr ABCDEFGHIJKLMNOPQRSTUVWXYZ abcdefghijklmnopqrstuvwxyz` GUESS=$UNAME_MACHINE-dec-osf$OSF_REL ;; Amiga*:UNIX_System_V:4.0:*) GUESS=m68k-unknown-sysv4 ;; *:[Aa]miga[Oo][Ss]:*:*) GUESS=$UNAME_MACHINE-unknown-amigaos ;; *:[Mm]orph[Oo][Ss]:*:*) GUESS=$UNAME_MACHINE-unknown-morphos ;; *:OS/390:*:*) GUESS=i370-ibm-openedition ;; *:z/VM:*:*) GUESS=s390-ibm-zvmoe ;; *:OS400:*:*) GUESS=powerpc-ibm-os400 ;; arm:RISC*:1.[012]*:*|arm:riscix:1.[012]*:*) GUESS=arm-acorn-riscix$UNAME_RELEASE ;; arm*:riscos:*:*|arm*:RISCOS:*:*) GUESS=arm-unknown-riscos ;; SR2?01:HI-UX/MPP:*:* | SR8000:HI-UX/MPP:*:*) GUESS=hppa1.1-hitachi-hiuxmpp ;; Pyramid*:OSx*:*:* | MIS*:OSx*:*:* | MIS*:SMP_DC-OSx*:*:*) # akee@wpdis03.wpafb.af.mil (Earle F. Ake) contributed MIS and NILE. case `(/bin/universe) 2>/dev/null` in att) GUESS=pyramid-pyramid-sysv3 ;; *) GUESS=pyramid-pyramid-bsd ;; esac ;; NILE*:*:*:dcosx) GUESS=pyramid-pyramid-svr4 ;; DRS?6000:unix:4.0:6*) GUESS=sparc-icl-nx6 ;; DRS?6000:UNIX_SV:4.2*:7* | DRS?6000:isis:4.2*:7*) case `/usr/bin/uname -p` in sparc) GUESS=sparc-icl-nx7 ;; esac ;; s390x:SunOS:*:*) SUN_REL=`echo "$UNAME_RELEASE" | sed -e 's/[^.]*//'` GUESS=$UNAME_MACHINE-ibm-solaris2$SUN_REL ;; sun4H:SunOS:5.*:*) SUN_REL=`echo "$UNAME_RELEASE" | sed -e 's/[^.]*//'` GUESS=sparc-hal-solaris2$SUN_REL ;; sun4*:SunOS:5.*:* | tadpole*:SunOS:5.*:*) SUN_REL=`echo "$UNAME_RELEASE" | sed -e 's/[^.]*//'` GUESS=sparc-sun-solaris2$SUN_REL ;; i86pc:AuroraUX:5.*:* | i86xen:AuroraUX:5.*:*) GUESS=i386-pc-auroraux$UNAME_RELEASE ;; i86pc:SunOS:5.*:* | i86xen:SunOS:5.*:*) set_cc_for_build SUN_ARCH=i386 # If there is a compiler, see if it is configured for 64-bit objects. # Note that the Sun cc does not turn __LP64__ into 1 like gcc does. # This test works for both compilers. if test "$CC_FOR_BUILD" != no_compiler_found; then if (echo '#ifdef __amd64'; echo IS_64BIT_ARCH; echo '#endif') | \ (CCOPTS="" $CC_FOR_BUILD -m64 -E - 2>/dev/null) | \ grep IS_64BIT_ARCH >/dev/null then SUN_ARCH=x86_64 fi fi SUN_REL=`echo "$UNAME_RELEASE" | sed -e 's/[^.]*//'` GUESS=$SUN_ARCH-pc-solaris2$SUN_REL ;; sun4*:SunOS:6*:*) # According to config.sub, this is the proper way to canonicalize # SunOS6. Hard to guess exactly what SunOS6 will be like, but # it's likely to be more like Solaris than SunOS4. SUN_REL=`echo "$UNAME_RELEASE" | sed -e 's/[^.]*//'` GUESS=sparc-sun-solaris3$SUN_REL ;; sun4*:SunOS:*:*) case `/usr/bin/arch -k` in Series*|S4*) UNAME_RELEASE=`uname -v` ;; esac # Japanese Language versions have a version number like `4.1.3-JL'. SUN_REL=`echo "$UNAME_RELEASE" | sed -e 's/-/_/'` GUESS=sparc-sun-sunos$SUN_REL ;; sun3*:SunOS:*:*) GUESS=m68k-sun-sunos$UNAME_RELEASE ;; sun*:*:4.2BSD:*) UNAME_RELEASE=`(sed 1q /etc/motd | awk '{print substr($5,1,3)}') 2>/dev/null` test "x$UNAME_RELEASE" = x && UNAME_RELEASE=3 case `/bin/arch` in sun3) GUESS=m68k-sun-sunos$UNAME_RELEASE ;; sun4) GUESS=sparc-sun-sunos$UNAME_RELEASE ;; esac ;; aushp:SunOS:*:*) GUESS=sparc-auspex-sunos$UNAME_RELEASE ;; # The situation for MiNT is a little confusing. The machine name # can be virtually everything (everything which is not # "atarist" or "atariste" at least should have a processor # > m68000). The system name ranges from "MiNT" over "FreeMiNT" # to the lowercase version "mint" (or "freemint"). Finally # the system name "TOS" denotes a system which is actually not # MiNT. But MiNT is downward compatible to TOS, so this should # be no problem. atarist[e]:*MiNT:*:* | atarist[e]:*mint:*:* | atarist[e]:*TOS:*:*) GUESS=m68k-atari-mint$UNAME_RELEASE ;; atari*:*MiNT:*:* | atari*:*mint:*:* | atarist[e]:*TOS:*:*) GUESS=m68k-atari-mint$UNAME_RELEASE ;; *falcon*:*MiNT:*:* | *falcon*:*mint:*:* | *falcon*:*TOS:*:*) GUESS=m68k-atari-mint$UNAME_RELEASE ;; milan*:*MiNT:*:* | milan*:*mint:*:* | *milan*:*TOS:*:*) GUESS=m68k-milan-mint$UNAME_RELEASE ;; hades*:*MiNT:*:* | hades*:*mint:*:* | *hades*:*TOS:*:*) GUESS=m68k-hades-mint$UNAME_RELEASE ;; *:*MiNT:*:* | *:*mint:*:* | *:*TOS:*:*) GUESS=m68k-unknown-mint$UNAME_RELEASE ;; m68k:machten:*:*) GUESS=m68k-apple-machten$UNAME_RELEASE ;; powerpc:machten:*:*) GUESS=powerpc-apple-machten$UNAME_RELEASE ;; RISC*:Mach:*:*) GUESS=mips-dec-mach_bsd4.3 ;; RISC*:ULTRIX:*:*) GUESS=mips-dec-ultrix$UNAME_RELEASE ;; VAX*:ULTRIX*:*:*) GUESS=vax-dec-ultrix$UNAME_RELEASE ;; 2020:CLIX:*:* | 2430:CLIX:*:*) GUESS=clipper-intergraph-clix$UNAME_RELEASE ;; mips:*:*:UMIPS | mips:*:*:RISCos) set_cc_for_build sed 's/^ //' << EOF > "$dummy.c" #ifdef __cplusplus #include /* for printf() prototype */ int main (int argc, char *argv[]) { #else int main (argc, argv) int argc; char *argv[]; { #endif #if defined (host_mips) && defined (MIPSEB) #if defined (SYSTYPE_SYSV) printf ("mips-mips-riscos%ssysv\\n", argv[1]); exit (0); #endif #if defined (SYSTYPE_SVR4) printf ("mips-mips-riscos%ssvr4\\n", argv[1]); exit (0); #endif #if defined (SYSTYPE_BSD43) || defined(SYSTYPE_BSD) printf ("mips-mips-riscos%sbsd\\n", argv[1]); exit (0); #endif #endif exit (-1); } EOF $CC_FOR_BUILD -o "$dummy" "$dummy.c" && dummyarg=`echo "$UNAME_RELEASE" | sed -n 's/\([0-9]*\).*/\1/p'` && SYSTEM_NAME=`"$dummy" "$dummyarg"` && { echo "$SYSTEM_NAME"; exit; } GUESS=mips-mips-riscos$UNAME_RELEASE ;; Motorola:PowerMAX_OS:*:*) GUESS=powerpc-motorola-powermax ;; Motorola:*:4.3:PL8-*) GUESS=powerpc-harris-powermax ;; Night_Hawk:*:*:PowerMAX_OS | Synergy:PowerMAX_OS:*:*) GUESS=powerpc-harris-powermax ;; Night_Hawk:Power_UNIX:*:*) GUESS=powerpc-harris-powerunix ;; m88k:CX/UX:7*:*) GUESS=m88k-harris-cxux7 ;; m88k:*:4*:R4*) GUESS=m88k-motorola-sysv4 ;; m88k:*:3*:R3*) GUESS=m88k-motorola-sysv3 ;; AViiON:dgux:*:*) # DG/UX returns AViiON for all architectures UNAME_PROCESSOR=`/usr/bin/uname -p` if test "$UNAME_PROCESSOR" = mc88100 || test "$UNAME_PROCESSOR" = mc88110 then if test "$TARGET_BINARY_INTERFACE"x = m88kdguxelfx || \ test "$TARGET_BINARY_INTERFACE"x = x then GUESS=m88k-dg-dgux$UNAME_RELEASE else GUESS=m88k-dg-dguxbcs$UNAME_RELEASE fi else GUESS=i586-dg-dgux$UNAME_RELEASE fi ;; M88*:DolphinOS:*:*) # DolphinOS (SVR3) GUESS=m88k-dolphin-sysv3 ;; M88*:*:R3*:*) # Delta 88k system running SVR3 GUESS=m88k-motorola-sysv3 ;; XD88*:*:*:*) # Tektronix XD88 system running UTekV (SVR3) GUESS=m88k-tektronix-sysv3 ;; Tek43[0-9][0-9]:UTek:*:*) # Tektronix 4300 system running UTek (BSD) GUESS=m68k-tektronix-bsd ;; *:IRIX*:*:*) IRIX_REL=`echo "$UNAME_RELEASE" | sed -e 's/-/_/g'` GUESS=mips-sgi-irix$IRIX_REL ;; ????????:AIX?:[12].1:2) # AIX 2.2.1 or AIX 2.1.1 is RT/PC AIX. GUESS=romp-ibm-aix # uname -m gives an 8 hex-code CPU id ;; # Note that: echo "'`uname -s`'" gives 'AIX ' i*86:AIX:*:*) GUESS=i386-ibm-aix ;; ia64:AIX:*:*) if test -x /usr/bin/oslevel ; then IBM_REV=`/usr/bin/oslevel` else IBM_REV=$UNAME_VERSION.$UNAME_RELEASE fi GUESS=$UNAME_MACHINE-ibm-aix$IBM_REV ;; *:AIX:2:3) if grep bos325 /usr/include/stdio.h >/dev/null 2>&1; then set_cc_for_build sed 's/^ //' << EOF > "$dummy.c" #include main() { if (!__power_pc()) exit(1); puts("powerpc-ibm-aix3.2.5"); exit(0); } EOF if $CC_FOR_BUILD -o "$dummy" "$dummy.c" && SYSTEM_NAME=`"$dummy"` then GUESS=$SYSTEM_NAME else GUESS=rs6000-ibm-aix3.2.5 fi elif grep bos324 /usr/include/stdio.h >/dev/null 2>&1; then GUESS=rs6000-ibm-aix3.2.4 else GUESS=rs6000-ibm-aix3.2 fi ;; *:AIX:*:[4567]) IBM_CPU_ID=`/usr/sbin/lsdev -C -c processor -S available | sed 1q | awk '{ print $1 }'` if /usr/sbin/lsattr -El "$IBM_CPU_ID" | grep ' POWER' >/dev/null 2>&1; then IBM_ARCH=rs6000 else IBM_ARCH=powerpc fi if test -x /usr/bin/lslpp ; then IBM_REV=`/usr/bin/lslpp -Lqc bos.rte.libc | \ awk -F: '{ print $3 }' | sed s/[0-9]*$/0/` else IBM_REV=$UNAME_VERSION.$UNAME_RELEASE fi GUESS=$IBM_ARCH-ibm-aix$IBM_REV ;; *:AIX:*:*) GUESS=rs6000-ibm-aix ;; ibmrt:4.4BSD:*|romp-ibm:4.4BSD:*) GUESS=romp-ibm-bsd4.4 ;; ibmrt:*BSD:*|romp-ibm:BSD:*) # covers RT/PC BSD and GUESS=romp-ibm-bsd$UNAME_RELEASE # 4.3 with uname added to ;; # report: romp-ibm BSD 4.3 *:BOSX:*:*) GUESS=rs6000-bull-bosx ;; DPX/2?00:B.O.S.:*:*) GUESS=m68k-bull-sysv3 ;; 9000/[34]??:4.3bsd:1.*:*) GUESS=m68k-hp-bsd ;; hp300:4.4BSD:*:* | 9000/[34]??:4.3bsd:2.*:*) GUESS=m68k-hp-bsd4.4 ;; 9000/[34678]??:HP-UX:*:*) HPUX_REV=`echo "$UNAME_RELEASE" | sed -e 's/[^.]*.[0B]*//'` case $UNAME_MACHINE in 9000/31?) HP_ARCH=m68000 ;; 9000/[34]??) HP_ARCH=m68k ;; 9000/[678][0-9][0-9]) if test -x /usr/bin/getconf; then sc_cpu_version=`/usr/bin/getconf SC_CPU_VERSION 2>/dev/null` sc_kernel_bits=`/usr/bin/getconf SC_KERNEL_BITS 2>/dev/null` case $sc_cpu_version in 523) HP_ARCH=hppa1.0 ;; # CPU_PA_RISC1_0 528) HP_ARCH=hppa1.1 ;; # CPU_PA_RISC1_1 532) # CPU_PA_RISC2_0 case $sc_kernel_bits in 32) HP_ARCH=hppa2.0n ;; 64) HP_ARCH=hppa2.0w ;; '') HP_ARCH=hppa2.0 ;; # HP-UX 10.20 esac ;; esac fi if test "$HP_ARCH" = ""; then set_cc_for_build sed 's/^ //' << EOF > "$dummy.c" #define _HPUX_SOURCE #include #include int main () { #if defined(_SC_KERNEL_BITS) long bits = sysconf(_SC_KERNEL_BITS); #endif long cpu = sysconf (_SC_CPU_VERSION); switch (cpu) { case CPU_PA_RISC1_0: puts ("hppa1.0"); break; case CPU_PA_RISC1_1: puts ("hppa1.1"); break; case CPU_PA_RISC2_0: #if defined(_SC_KERNEL_BITS) switch (bits) { case 64: puts ("hppa2.0w"); break; case 32: puts ("hppa2.0n"); break; default: puts ("hppa2.0"); break; } break; #else /* !defined(_SC_KERNEL_BITS) */ puts ("hppa2.0"); break; #endif default: puts ("hppa1.0"); break; } exit (0); } EOF (CCOPTS="" $CC_FOR_BUILD -o "$dummy" "$dummy.c" 2>/dev/null) && HP_ARCH=`"$dummy"` test -z "$HP_ARCH" && HP_ARCH=hppa fi ;; esac if test "$HP_ARCH" = hppa2.0w then set_cc_for_build # hppa2.0w-hp-hpux* has a 64-bit kernel and a compiler generating # 32-bit code. hppa64-hp-hpux* has the same kernel and a compiler # generating 64-bit code. GNU and HP use different nomenclature: # # $ CC_FOR_BUILD=cc ./config.guess # => hppa2.0w-hp-hpux11.23 # $ CC_FOR_BUILD="cc +DA2.0w" ./config.guess # => hppa64-hp-hpux11.23 if echo __LP64__ | (CCOPTS="" $CC_FOR_BUILD -E - 2>/dev/null) | grep -q __LP64__ then HP_ARCH=hppa2.0w else HP_ARCH=hppa64 fi fi GUESS=$HP_ARCH-hp-hpux$HPUX_REV ;; ia64:HP-UX:*:*) HPUX_REV=`echo "$UNAME_RELEASE" | sed -e 's/[^.]*.[0B]*//'` GUESS=ia64-hp-hpux$HPUX_REV ;; 3050*:HI-UX:*:*) set_cc_for_build sed 's/^ //' << EOF > "$dummy.c" #include int main () { long cpu = sysconf (_SC_CPU_VERSION); /* The order matters, because CPU_IS_HP_MC68K erroneously returns true for CPU_PA_RISC1_0. CPU_IS_PA_RISC returns correct results, however. */ if (CPU_IS_PA_RISC (cpu)) { switch (cpu) { case CPU_PA_RISC1_0: puts ("hppa1.0-hitachi-hiuxwe2"); break; case CPU_PA_RISC1_1: puts ("hppa1.1-hitachi-hiuxwe2"); break; case CPU_PA_RISC2_0: puts ("hppa2.0-hitachi-hiuxwe2"); break; default: puts ("hppa-hitachi-hiuxwe2"); break; } } else if (CPU_IS_HP_MC68K (cpu)) puts ("m68k-hitachi-hiuxwe2"); else puts ("unknown-hitachi-hiuxwe2"); exit (0); } EOF $CC_FOR_BUILD -o "$dummy" "$dummy.c" && SYSTEM_NAME=`"$dummy"` && { echo "$SYSTEM_NAME"; exit; } GUESS=unknown-hitachi-hiuxwe2 ;; 9000/7??:4.3bsd:*:* | 9000/8?[79]:4.3bsd:*:*) GUESS=hppa1.1-hp-bsd ;; 9000/8??:4.3bsd:*:*) GUESS=hppa1.0-hp-bsd ;; *9??*:MPE/iX:*:* | *3000*:MPE/iX:*:*) GUESS=hppa1.0-hp-mpeix ;; hp7??:OSF1:*:* | hp8?[79]:OSF1:*:*) GUESS=hppa1.1-hp-osf ;; hp8??:OSF1:*:*) GUESS=hppa1.0-hp-osf ;; i*86:OSF1:*:*) if test -x /usr/sbin/sysversion ; then GUESS=$UNAME_MACHINE-unknown-osf1mk else GUESS=$UNAME_MACHINE-unknown-osf1 fi ;; parisc*:Lites*:*:*) GUESS=hppa1.1-hp-lites ;; C1*:ConvexOS:*:* | convex:ConvexOS:C1*:*) GUESS=c1-convex-bsd ;; C2*:ConvexOS:*:* | convex:ConvexOS:C2*:*) if getsysinfo -f scalar_acc then echo c32-convex-bsd else echo c2-convex-bsd fi exit ;; C34*:ConvexOS:*:* | convex:ConvexOS:C34*:*) GUESS=c34-convex-bsd ;; C38*:ConvexOS:*:* | convex:ConvexOS:C38*:*) GUESS=c38-convex-bsd ;; C4*:ConvexOS:*:* | convex:ConvexOS:C4*:*) GUESS=c4-convex-bsd ;; CRAY*Y-MP:*:*:*) CRAY_REL=`echo "$UNAME_RELEASE" | sed -e 's/\.[^.]*$/.X/'` GUESS=ymp-cray-unicos$CRAY_REL ;; CRAY*[A-Z]90:*:*:*) echo "$UNAME_MACHINE"-cray-unicos"$UNAME_RELEASE" \ | sed -e 's/CRAY.*\([A-Z]90\)/\1/' \ -e y/ABCDEFGHIJKLMNOPQRSTUVWXYZ/abcdefghijklmnopqrstuvwxyz/ \ -e 's/\.[^.]*$/.X/' exit ;; CRAY*TS:*:*:*) CRAY_REL=`echo "$UNAME_RELEASE" | sed -e 's/\.[^.]*$/.X/'` GUESS=t90-cray-unicos$CRAY_REL ;; CRAY*T3E:*:*:*) CRAY_REL=`echo "$UNAME_RELEASE" | sed -e 's/\.[^.]*$/.X/'` GUESS=alphaev5-cray-unicosmk$CRAY_REL ;; CRAY*SV1:*:*:*) CRAY_REL=`echo "$UNAME_RELEASE" | sed -e 's/\.[^.]*$/.X/'` GUESS=sv1-cray-unicos$CRAY_REL ;; *:UNICOS/mp:*:*) CRAY_REL=`echo "$UNAME_RELEASE" | sed -e 's/\.[^.]*$/.X/'` GUESS=craynv-cray-unicosmp$CRAY_REL ;; F30[01]:UNIX_System_V:*:* | F700:UNIX_System_V:*:*) FUJITSU_PROC=`uname -m | tr ABCDEFGHIJKLMNOPQRSTUVWXYZ abcdefghijklmnopqrstuvwxyz` FUJITSU_SYS=`uname -p | tr ABCDEFGHIJKLMNOPQRSTUVWXYZ abcdefghijklmnopqrstuvwxyz | sed -e 's/\///'` FUJITSU_REL=`echo "$UNAME_RELEASE" | sed -e 's/ /_/'` GUESS=${FUJITSU_PROC}-fujitsu-${FUJITSU_SYS}${FUJITSU_REL} ;; 5000:UNIX_System_V:4.*:*) FUJITSU_SYS=`uname -p | tr ABCDEFGHIJKLMNOPQRSTUVWXYZ abcdefghijklmnopqrstuvwxyz | sed -e 's/\///'` FUJITSU_REL=`echo "$UNAME_RELEASE" | tr ABCDEFGHIJKLMNOPQRSTUVWXYZ abcdefghijklmnopqrstuvwxyz | sed -e 's/ /_/'` GUESS=sparc-fujitsu-${FUJITSU_SYS}${FUJITSU_REL} ;; i*86:BSD/386:*:* | i*86:BSD/OS:*:* | *:Ascend\ Embedded/OS:*:*) GUESS=$UNAME_MACHINE-pc-bsdi$UNAME_RELEASE ;; sparc*:BSD/OS:*:*) GUESS=sparc-unknown-bsdi$UNAME_RELEASE ;; *:BSD/OS:*:*) GUESS=$UNAME_MACHINE-unknown-bsdi$UNAME_RELEASE ;; arm:FreeBSD:*:*) UNAME_PROCESSOR=`uname -p` set_cc_for_build if echo __ARM_PCS_VFP | $CC_FOR_BUILD -E - 2>/dev/null \ | grep -q __ARM_PCS_VFP then FREEBSD_REL=`echo "$UNAME_RELEASE" | sed -e 's/[-(].*//'` GUESS=$UNAME_PROCESSOR-unknown-freebsd$FREEBSD_REL-gnueabi else FREEBSD_REL=`echo "$UNAME_RELEASE" | sed -e 's/[-(].*//'` GUESS=$UNAME_PROCESSOR-unknown-freebsd$FREEBSD_REL-gnueabihf fi ;; *:FreeBSD:*:*) UNAME_PROCESSOR=`/usr/bin/uname -p` case $UNAME_PROCESSOR in amd64) UNAME_PROCESSOR=x86_64 ;; i386) UNAME_PROCESSOR=i586 ;; esac FREEBSD_REL=`echo "$UNAME_RELEASE" | sed -e 's/[-(].*//'` GUESS=$UNAME_PROCESSOR-unknown-freebsd$FREEBSD_REL ;; i*:CYGWIN*:*) GUESS=$UNAME_MACHINE-pc-cygwin ;; *:MINGW64*:*) GUESS=$UNAME_MACHINE-pc-mingw64 ;; *:MINGW*:*) GUESS=$UNAME_MACHINE-pc-mingw32 ;; *:MSYS*:*) GUESS=$UNAME_MACHINE-pc-msys ;; i*:PW*:*) GUESS=$UNAME_MACHINE-pc-pw32 ;; *:SerenityOS:*:*) GUESS=$UNAME_MACHINE-pc-serenity ;; *:Interix*:*) case $UNAME_MACHINE in x86) GUESS=i586-pc-interix$UNAME_RELEASE ;; authenticamd | genuineintel | EM64T) GUESS=x86_64-unknown-interix$UNAME_RELEASE ;; IA64) GUESS=ia64-unknown-interix$UNAME_RELEASE ;; esac ;; i*:UWIN*:*) GUESS=$UNAME_MACHINE-pc-uwin ;; amd64:CYGWIN*:*:* | x86_64:CYGWIN*:*:*) GUESS=x86_64-pc-cygwin ;; prep*:SunOS:5.*:*) SUN_REL=`echo "$UNAME_RELEASE" | sed -e 's/[^.]*//'` GUESS=powerpcle-unknown-solaris2$SUN_REL ;; *:GNU:*:*) # the GNU system GNU_ARCH=`echo "$UNAME_MACHINE" | sed -e 's,[-/].*$,,'` GNU_REL=`echo "$UNAME_RELEASE" | sed -e 's,/.*$,,'` GUESS=$GNU_ARCH-unknown-$LIBC$GNU_REL ;; *:GNU/*:*:*) # other systems with GNU libc and userland GNU_SYS=`echo "$UNAME_SYSTEM" | sed 's,^[^/]*/,,' | tr "[:upper:]" "[:lower:]"` GNU_REL=`echo "$UNAME_RELEASE" | sed -e 's/[-(].*//'` GUESS=$UNAME_MACHINE-unknown-$GNU_SYS$GNU_REL-$LIBC ;; *:Minix:*:*) GUESS=$UNAME_MACHINE-unknown-minix ;; aarch64:Linux:*:*) GUESS=$UNAME_MACHINE-unknown-linux-$LIBC ;; aarch64_be:Linux:*:*) UNAME_MACHINE=aarch64_be GUESS=$UNAME_MACHINE-unknown-linux-$LIBC ;; alpha:Linux:*:*) case `sed -n '/^cpu model/s/^.*: \(.*\)/\1/p' /proc/cpuinfo 2>/dev/null` in EV5) UNAME_MACHINE=alphaev5 ;; EV56) UNAME_MACHINE=alphaev56 ;; PCA56) UNAME_MACHINE=alphapca56 ;; PCA57) UNAME_MACHINE=alphapca56 ;; EV6) UNAME_MACHINE=alphaev6 ;; EV67) UNAME_MACHINE=alphaev67 ;; EV68*) UNAME_MACHINE=alphaev68 ;; esac objdump --private-headers /bin/sh | grep -q ld.so.1 if test "$?" = 0 ; then LIBC=gnulibc1 ; fi GUESS=$UNAME_MACHINE-unknown-linux-$LIBC ;; arc:Linux:*:* | arceb:Linux:*:* | arc32:Linux:*:* | arc64:Linux:*:*) GUESS=$UNAME_MACHINE-unknown-linux-$LIBC ;; arm*:Linux:*:*) set_cc_for_build if echo __ARM_EABI__ | $CC_FOR_BUILD -E - 2>/dev/null \ | grep -q __ARM_EABI__ then GUESS=$UNAME_MACHINE-unknown-linux-$LIBC else if echo __ARM_PCS_VFP | $CC_FOR_BUILD -E - 2>/dev/null \ | grep -q __ARM_PCS_VFP then GUESS=$UNAME_MACHINE-unknown-linux-${LIBC}eabi else GUESS=$UNAME_MACHINE-unknown-linux-${LIBC}eabihf fi fi ;; avr32*:Linux:*:*) GUESS=$UNAME_MACHINE-unknown-linux-$LIBC ;; cris:Linux:*:*) GUESS=$UNAME_MACHINE-axis-linux-$LIBC ;; crisv32:Linux:*:*) GUESS=$UNAME_MACHINE-axis-linux-$LIBC ;; e2k:Linux:*:*) GUESS=$UNAME_MACHINE-unknown-linux-$LIBC ;; frv:Linux:*:*) GUESS=$UNAME_MACHINE-unknown-linux-$LIBC ;; hexagon:Linux:*:*) GUESS=$UNAME_MACHINE-unknown-linux-$LIBC ;; i*86:Linux:*:*) GUESS=$UNAME_MACHINE-pc-linux-$LIBC ;; ia64:Linux:*:*) GUESS=$UNAME_MACHINE-unknown-linux-$LIBC ;; k1om:Linux:*:*) GUESS=$UNAME_MACHINE-unknown-linux-$LIBC ;; loongarch32:Linux:*:* | loongarch64:Linux:*:* | loongarchx32:Linux:*:*) GUESS=$UNAME_MACHINE-unknown-linux-$LIBC ;; m32r*:Linux:*:*) GUESS=$UNAME_MACHINE-unknown-linux-$LIBC ;; m68*:Linux:*:*) GUESS=$UNAME_MACHINE-unknown-linux-$LIBC ;; mips:Linux:*:* | mips64:Linux:*:*) set_cc_for_build IS_GLIBC=0 test x"${LIBC}" = xgnu && IS_GLIBC=1 sed 's/^ //' << EOF > "$dummy.c" #undef CPU #undef mips #undef mipsel #undef mips64 #undef mips64el #if ${IS_GLIBC} && defined(_ABI64) LIBCABI=gnuabi64 #else #if ${IS_GLIBC} && defined(_ABIN32) LIBCABI=gnuabin32 #else LIBCABI=${LIBC} #endif #endif #if ${IS_GLIBC} && defined(__mips64) && defined(__mips_isa_rev) && __mips_isa_rev>=6 CPU=mipsisa64r6 #else #if ${IS_GLIBC} && !defined(__mips64) && defined(__mips_isa_rev) && __mips_isa_rev>=6 CPU=mipsisa32r6 #else #if defined(__mips64) CPU=mips64 #else CPU=mips #endif #endif #endif #if defined(__MIPSEL__) || defined(__MIPSEL) || defined(_MIPSEL) || defined(MIPSEL) MIPS_ENDIAN=el #else #if defined(__MIPSEB__) || defined(__MIPSEB) || defined(_MIPSEB) || defined(MIPSEB) MIPS_ENDIAN= #else MIPS_ENDIAN= #endif #endif EOF cc_set_vars=`$CC_FOR_BUILD -E "$dummy.c" 2>/dev/null | grep '^CPU\|^MIPS_ENDIAN\|^LIBCABI'` eval "$cc_set_vars" test "x$CPU" != x && { echo "$CPU${MIPS_ENDIAN}-unknown-linux-$LIBCABI"; exit; } ;; mips64el:Linux:*:*) GUESS=$UNAME_MACHINE-unknown-linux-$LIBC ;; openrisc*:Linux:*:*) GUESS=or1k-unknown-linux-$LIBC ;; or32:Linux:*:* | or1k*:Linux:*:*) GUESS=$UNAME_MACHINE-unknown-linux-$LIBC ;; padre:Linux:*:*) GUESS=sparc-unknown-linux-$LIBC ;; parisc64:Linux:*:* | hppa64:Linux:*:*) GUESS=hppa64-unknown-linux-$LIBC ;; parisc:Linux:*:* | hppa:Linux:*:*) # Look for CPU level case `grep '^cpu[^a-z]*:' /proc/cpuinfo 2>/dev/null | cut -d' ' -f2` in PA7*) GUESS=hppa1.1-unknown-linux-$LIBC ;; PA8*) GUESS=hppa2.0-unknown-linux-$LIBC ;; *) GUESS=hppa-unknown-linux-$LIBC ;; esac ;; ppc64:Linux:*:*) GUESS=powerpc64-unknown-linux-$LIBC ;; ppc:Linux:*:*) GUESS=powerpc-unknown-linux-$LIBC ;; ppc64le:Linux:*:*) GUESS=powerpc64le-unknown-linux-$LIBC ;; ppcle:Linux:*:*) GUESS=powerpcle-unknown-linux-$LIBC ;; riscv32:Linux:*:* | riscv32be:Linux:*:* | riscv64:Linux:*:* | riscv64be:Linux:*:*) GUESS=$UNAME_MACHINE-unknown-linux-$LIBC ;; s390:Linux:*:* | s390x:Linux:*:*) GUESS=$UNAME_MACHINE-ibm-linux-$LIBC ;; sh64*:Linux:*:*) GUESS=$UNAME_MACHINE-unknown-linux-$LIBC ;; sh*:Linux:*:*) GUESS=$UNAME_MACHINE-unknown-linux-$LIBC ;; sparc:Linux:*:* | sparc64:Linux:*:*) GUESS=$UNAME_MACHINE-unknown-linux-$LIBC ;; tile*:Linux:*:*) GUESS=$UNAME_MACHINE-unknown-linux-$LIBC ;; vax:Linux:*:*) GUESS=$UNAME_MACHINE-dec-linux-$LIBC ;; x86_64:Linux:*:*) set_cc_for_build LIBCABI=$LIBC if test "$CC_FOR_BUILD" != no_compiler_found; then if (echo '#ifdef __ILP32__'; echo IS_X32; echo '#endif') | \ (CCOPTS="" $CC_FOR_BUILD -E - 2>/dev/null) | \ grep IS_X32 >/dev/null then LIBCABI=${LIBC}x32 fi fi GUESS=$UNAME_MACHINE-pc-linux-$LIBCABI ;; xtensa*:Linux:*:*) GUESS=$UNAME_MACHINE-unknown-linux-$LIBC ;; i*86:DYNIX/ptx:4*:*) # ptx 4.0 does uname -s correctly, with DYNIX/ptx in there. # earlier versions are messed up and put the nodename in both # sysname and nodename. GUESS=i386-sequent-sysv4 ;; i*86:UNIX_SV:4.2MP:2.*) # Unixware is an offshoot of SVR4, but it has its own version # number series starting with 2... # I am not positive that other SVR4 systems won't match this, # I just have to hope. -- rms. # Use sysv4.2uw... so that sysv4* matches it. GUESS=$UNAME_MACHINE-pc-sysv4.2uw$UNAME_VERSION ;; i*86:OS/2:*:*) # If we were able to find `uname', then EMX Unix compatibility # is probably installed. GUESS=$UNAME_MACHINE-pc-os2-emx ;; i*86:XTS-300:*:STOP) GUESS=$UNAME_MACHINE-unknown-stop ;; i*86:atheos:*:*) GUESS=$UNAME_MACHINE-unknown-atheos ;; i*86:syllable:*:*) GUESS=$UNAME_MACHINE-pc-syllable ;; i*86:LynxOS:2.*:* | i*86:LynxOS:3.[01]*:* | i*86:LynxOS:4.[02]*:*) GUESS=i386-unknown-lynxos$UNAME_RELEASE ;; i*86:*DOS:*:*) GUESS=$UNAME_MACHINE-pc-msdosdjgpp ;; i*86:*:4.*:*) UNAME_REL=`echo "$UNAME_RELEASE" | sed 's/\/MP$//'` if grep Novell /usr/include/link.h >/dev/null 2>/dev/null; then GUESS=$UNAME_MACHINE-univel-sysv$UNAME_REL else GUESS=$UNAME_MACHINE-pc-sysv$UNAME_REL fi ;; i*86:*:5:[678]*) # UnixWare 7.x, OpenUNIX and OpenServer 6. case `/bin/uname -X | grep "^Machine"` in *486*) UNAME_MACHINE=i486 ;; *Pentium) UNAME_MACHINE=i586 ;; *Pent*|*Celeron) UNAME_MACHINE=i686 ;; esac GUESS=$UNAME_MACHINE-unknown-sysv${UNAME_RELEASE}${UNAME_SYSTEM}${UNAME_VERSION} ;; i*86:*:3.2:*) if test -f /usr/options/cb.name; then UNAME_REL=`sed -n 's/.*Version //p' /dev/null >/dev/null ; then UNAME_REL=`(/bin/uname -X|grep Release|sed -e 's/.*= //')` (/bin/uname -X|grep i80486 >/dev/null) && UNAME_MACHINE=i486 (/bin/uname -X|grep '^Machine.*Pentium' >/dev/null) \ && UNAME_MACHINE=i586 (/bin/uname -X|grep '^Machine.*Pent *II' >/dev/null) \ && UNAME_MACHINE=i686 (/bin/uname -X|grep '^Machine.*Pentium Pro' >/dev/null) \ && UNAME_MACHINE=i686 GUESS=$UNAME_MACHINE-pc-sco$UNAME_REL else GUESS=$UNAME_MACHINE-pc-sysv32 fi ;; pc:*:*:*) # Left here for compatibility: # uname -m prints for DJGPP always 'pc', but it prints nothing about # the processor, so we play safe by assuming i586. # Note: whatever this is, it MUST be the same as what config.sub # prints for the "djgpp" host, or else GDB configure will decide that # this is a cross-build. GUESS=i586-pc-msdosdjgpp ;; Intel:Mach:3*:*) GUESS=i386-pc-mach3 ;; paragon:*:*:*) GUESS=i860-intel-osf1 ;; i860:*:4.*:*) # i860-SVR4 if grep Stardent /usr/include/sys/uadmin.h >/dev/null 2>&1 ; then GUESS=i860-stardent-sysv$UNAME_RELEASE # Stardent Vistra i860-SVR4 else # Add other i860-SVR4 vendors below as they are discovered. GUESS=i860-unknown-sysv$UNAME_RELEASE # Unknown i860-SVR4 fi ;; mini*:CTIX:SYS*5:*) # "miniframe" GUESS=m68010-convergent-sysv ;; mc68k:UNIX:SYSTEM5:3.51m) GUESS=m68k-convergent-sysv ;; M680?0:D-NIX:5.3:*) GUESS=m68k-diab-dnix ;; M68*:*:R3V[5678]*:*) test -r /sysV68 && { echo 'm68k-motorola-sysv'; exit; } ;; 3[345]??:*:4.0:3.0 | 3[34]??A:*:4.0:3.0 | 3[34]??,*:*:4.0:3.0 | 3[34]??/*:*:4.0:3.0 | 4400:*:4.0:3.0 | 4850:*:4.0:3.0 | SKA40:*:4.0:3.0 | SDS2:*:4.0:3.0 | SHG2:*:4.0:3.0 | S7501*:*:4.0:3.0) OS_REL='' test -r /etc/.relid \ && OS_REL=.`sed -n 's/[^ ]* [^ ]* \([0-9][0-9]\).*/\1/p' < /etc/.relid` /bin/uname -p 2>/dev/null | grep 86 >/dev/null \ && { echo i486-ncr-sysv4.3"$OS_REL"; exit; } /bin/uname -p 2>/dev/null | /bin/grep entium >/dev/null \ && { echo i586-ncr-sysv4.3"$OS_REL"; exit; } ;; 3[34]??:*:4.0:* | 3[34]??,*:*:4.0:*) /bin/uname -p 2>/dev/null | grep 86 >/dev/null \ && { echo i486-ncr-sysv4; exit; } ;; NCR*:*:4.2:* | MPRAS*:*:4.2:*) OS_REL='.3' test -r /etc/.relid \ && OS_REL=.`sed -n 's/[^ ]* [^ ]* \([0-9][0-9]\).*/\1/p' < /etc/.relid` /bin/uname -p 2>/dev/null | grep 86 >/dev/null \ && { echo i486-ncr-sysv4.3"$OS_REL"; exit; } /bin/uname -p 2>/dev/null | /bin/grep entium >/dev/null \ && { echo i586-ncr-sysv4.3"$OS_REL"; exit; } /bin/uname -p 2>/dev/null | /bin/grep pteron >/dev/null \ && { echo i586-ncr-sysv4.3"$OS_REL"; exit; } ;; m68*:LynxOS:2.*:* | m68*:LynxOS:3.0*:*) GUESS=m68k-unknown-lynxos$UNAME_RELEASE ;; mc68030:UNIX_System_V:4.*:*) GUESS=m68k-atari-sysv4 ;; TSUNAMI:LynxOS:2.*:*) GUESS=sparc-unknown-lynxos$UNAME_RELEASE ;; rs6000:LynxOS:2.*:*) GUESS=rs6000-unknown-lynxos$UNAME_RELEASE ;; PowerPC:LynxOS:2.*:* | PowerPC:LynxOS:3.[01]*:* | PowerPC:LynxOS:4.[02]*:*) GUESS=powerpc-unknown-lynxos$UNAME_RELEASE ;; SM[BE]S:UNIX_SV:*:*) GUESS=mips-dde-sysv$UNAME_RELEASE ;; RM*:ReliantUNIX-*:*:*) GUESS=mips-sni-sysv4 ;; RM*:SINIX-*:*:*) GUESS=mips-sni-sysv4 ;; *:SINIX-*:*:*) if uname -p 2>/dev/null >/dev/null ; then UNAME_MACHINE=`(uname -p) 2>/dev/null` GUESS=$UNAME_MACHINE-sni-sysv4 else GUESS=ns32k-sni-sysv fi ;; PENTIUM:*:4.0*:*) # Unisys `ClearPath HMP IX 4000' SVR4/MP effort # says GUESS=i586-unisys-sysv4 ;; *:UNIX_System_V:4*:FTX*) # From Gerald Hewes . # How about differentiating between stratus architectures? -djm GUESS=hppa1.1-stratus-sysv4 ;; *:*:*:FTX*) # From seanf@swdc.stratus.com. GUESS=i860-stratus-sysv4 ;; i*86:VOS:*:*) # From Paul.Green@stratus.com. GUESS=$UNAME_MACHINE-stratus-vos ;; *:VOS:*:*) # From Paul.Green@stratus.com. GUESS=hppa1.1-stratus-vos ;; mc68*:A/UX:*:*) GUESS=m68k-apple-aux$UNAME_RELEASE ;; news*:NEWS-OS:6*:*) GUESS=mips-sony-newsos6 ;; R[34]000:*System_V*:*:* | R4000:UNIX_SYSV:*:* | R*000:UNIX_SV:*:*) if test -d /usr/nec; then GUESS=mips-nec-sysv$UNAME_RELEASE else GUESS=mips-unknown-sysv$UNAME_RELEASE fi ;; BeBox:BeOS:*:*) # BeOS running on hardware made by Be, PPC only. GUESS=powerpc-be-beos ;; BeMac:BeOS:*:*) # BeOS running on Mac or Mac clone, PPC only. GUESS=powerpc-apple-beos ;; BePC:BeOS:*:*) # BeOS running on Intel PC compatible. GUESS=i586-pc-beos ;; BePC:Haiku:*:*) # Haiku running on Intel PC compatible. GUESS=i586-pc-haiku ;; x86_64:Haiku:*:*) GUESS=x86_64-unknown-haiku ;; SX-4:SUPER-UX:*:*) GUESS=sx4-nec-superux$UNAME_RELEASE ;; SX-5:SUPER-UX:*:*) GUESS=sx5-nec-superux$UNAME_RELEASE ;; SX-6:SUPER-UX:*:*) GUESS=sx6-nec-superux$UNAME_RELEASE ;; SX-7:SUPER-UX:*:*) GUESS=sx7-nec-superux$UNAME_RELEASE ;; SX-8:SUPER-UX:*:*) GUESS=sx8-nec-superux$UNAME_RELEASE ;; SX-8R:SUPER-UX:*:*) GUESS=sx8r-nec-superux$UNAME_RELEASE ;; SX-ACE:SUPER-UX:*:*) GUESS=sxace-nec-superux$UNAME_RELEASE ;; Power*:Rhapsody:*:*) GUESS=powerpc-apple-rhapsody$UNAME_RELEASE ;; *:Rhapsody:*:*) GUESS=$UNAME_MACHINE-apple-rhapsody$UNAME_RELEASE ;; arm64:Darwin:*:*) GUESS=aarch64-apple-darwin$UNAME_RELEASE ;; *:Darwin:*:*) UNAME_PROCESSOR=`uname -p` case $UNAME_PROCESSOR in unknown) UNAME_PROCESSOR=powerpc ;; esac if command -v xcode-select > /dev/null 2> /dev/null && \ ! xcode-select --print-path > /dev/null 2> /dev/null ; then # Avoid executing cc if there is no toolchain installed as # cc will be a stub that puts up a graphical alert # prompting the user to install developer tools. CC_FOR_BUILD=no_compiler_found else set_cc_for_build fi if test "$CC_FOR_BUILD" != no_compiler_found; then if (echo '#ifdef __LP64__'; echo IS_64BIT_ARCH; echo '#endif') | \ (CCOPTS="" $CC_FOR_BUILD -E - 2>/dev/null) | \ grep IS_64BIT_ARCH >/dev/null then case $UNAME_PROCESSOR in i386) UNAME_PROCESSOR=x86_64 ;; powerpc) UNAME_PROCESSOR=powerpc64 ;; esac fi # On 10.4-10.6 one might compile for PowerPC via gcc -arch ppc if (echo '#ifdef __POWERPC__'; echo IS_PPC; echo '#endif') | \ (CCOPTS="" $CC_FOR_BUILD -E - 2>/dev/null) | \ grep IS_PPC >/dev/null then UNAME_PROCESSOR=powerpc fi elif test "$UNAME_PROCESSOR" = i386 ; then # uname -m returns i386 or x86_64 UNAME_PROCESSOR=$UNAME_MACHINE fi GUESS=$UNAME_PROCESSOR-apple-darwin$UNAME_RELEASE ;; *:procnto*:*:* | *:QNX:[0123456789]*:*) UNAME_PROCESSOR=`uname -p` if test "$UNAME_PROCESSOR" = x86; then UNAME_PROCESSOR=i386 UNAME_MACHINE=pc fi GUESS=$UNAME_PROCESSOR-$UNAME_MACHINE-nto-qnx$UNAME_RELEASE ;; *:QNX:*:4*) GUESS=i386-pc-qnx ;; NEO-*:NONSTOP_KERNEL:*:*) GUESS=neo-tandem-nsk$UNAME_RELEASE ;; NSE-*:NONSTOP_KERNEL:*:*) GUESS=nse-tandem-nsk$UNAME_RELEASE ;; NSR-*:NONSTOP_KERNEL:*:*) GUESS=nsr-tandem-nsk$UNAME_RELEASE ;; NSV-*:NONSTOP_KERNEL:*:*) GUESS=nsv-tandem-nsk$UNAME_RELEASE ;; NSX-*:NONSTOP_KERNEL:*:*) GUESS=nsx-tandem-nsk$UNAME_RELEASE ;; *:NonStop-UX:*:*) GUESS=mips-compaq-nonstopux ;; BS2000:POSIX*:*:*) GUESS=bs2000-siemens-sysv ;; DS/*:UNIX_System_V:*:*) GUESS=$UNAME_MACHINE-$UNAME_SYSTEM-$UNAME_RELEASE ;; *:Plan9:*:*) # "uname -m" is not consistent, so use $cputype instead. 386 # is converted to i386 for consistency with other x86 # operating systems. if test "${cputype-}" = 386; then UNAME_MACHINE=i386 elif test "x${cputype-}" != x; then UNAME_MACHINE=$cputype fi GUESS=$UNAME_MACHINE-unknown-plan9 ;; *:TOPS-10:*:*) GUESS=pdp10-unknown-tops10 ;; *:TENEX:*:*) GUESS=pdp10-unknown-tenex ;; KS10:TOPS-20:*:* | KL10:TOPS-20:*:* | TYPE4:TOPS-20:*:*) GUESS=pdp10-dec-tops20 ;; XKL-1:TOPS-20:*:* | TYPE5:TOPS-20:*:*) GUESS=pdp10-xkl-tops20 ;; *:TOPS-20:*:*) GUESS=pdp10-unknown-tops20 ;; *:ITS:*:*) GUESS=pdp10-unknown-its ;; SEI:*:*:SEIUX) GUESS=mips-sei-seiux$UNAME_RELEASE ;; *:DragonFly:*:*) DRAGONFLY_REL=`echo "$UNAME_RELEASE" | sed -e 's/[-(].*//'` GUESS=$UNAME_MACHINE-unknown-dragonfly$DRAGONFLY_REL ;; *:*VMS:*:*) UNAME_MACHINE=`(uname -p) 2>/dev/null` case $UNAME_MACHINE in A*) GUESS=alpha-dec-vms ;; I*) GUESS=ia64-dec-vms ;; V*) GUESS=vax-dec-vms ;; esac ;; *:XENIX:*:SysV) GUESS=i386-pc-xenix ;; i*86:skyos:*:*) SKYOS_REL=`echo "$UNAME_RELEASE" | sed -e 's/ .*$//'` GUESS=$UNAME_MACHINE-pc-skyos$SKYOS_REL ;; i*86:rdos:*:*) GUESS=$UNAME_MACHINE-pc-rdos ;; i*86:Fiwix:*:*) GUESS=$UNAME_MACHINE-pc-fiwix ;; *:AROS:*:*) GUESS=$UNAME_MACHINE-unknown-aros ;; x86_64:VMkernel:*:*) GUESS=$UNAME_MACHINE-unknown-esx ;; amd64:Isilon\ OneFS:*:*) GUESS=x86_64-unknown-onefs ;; *:Unleashed:*:*) GUESS=$UNAME_MACHINE-unknown-unleashed$UNAME_RELEASE ;; esac # Do we have a guess based on uname results? if test "x$GUESS" != x; then echo "$GUESS" exit fi # No uname command or uname output not recognized. set_cc_for_build cat > "$dummy.c" < #include #endif #if defined(ultrix) || defined(_ultrix) || defined(__ultrix) || defined(__ultrix__) #if defined (vax) || defined (__vax) || defined (__vax__) || defined(mips) || defined(__mips) || defined(__mips__) || defined(MIPS) || defined(__MIPS__) #include #if defined(_SIZE_T_) || defined(SIGLOST) #include #endif #endif #endif main () { #if defined (sony) #if defined (MIPSEB) /* BFD wants "bsd" instead of "newsos". Perhaps BFD should be changed, I don't know.... */ printf ("mips-sony-bsd\n"); exit (0); #else #include printf ("m68k-sony-newsos%s\n", #ifdef NEWSOS4 "4" #else "" #endif ); exit (0); #endif #endif #if defined (NeXT) #if !defined (__ARCHITECTURE__) #define __ARCHITECTURE__ "m68k" #endif int version; version=`(hostinfo | sed -n 's/.*NeXT Mach \([0-9]*\).*/\1/p') 2>/dev/null`; if (version < 4) printf ("%s-next-nextstep%d\n", __ARCHITECTURE__, version); else printf ("%s-next-openstep%d\n", __ARCHITECTURE__, version); exit (0); #endif #if defined (MULTIMAX) || defined (n16) #if defined (UMAXV) printf ("ns32k-encore-sysv\n"); exit (0); #else #if defined (CMU) printf ("ns32k-encore-mach\n"); exit (0); #else printf ("ns32k-encore-bsd\n"); exit (0); #endif #endif #endif #if defined (__386BSD__) printf ("i386-pc-bsd\n"); exit (0); #endif #if defined (sequent) #if defined (i386) printf ("i386-sequent-dynix\n"); exit (0); #endif #if defined (ns32000) printf ("ns32k-sequent-dynix\n"); exit (0); #endif #endif #if defined (_SEQUENT_) struct utsname un; uname(&un); if (strncmp(un.version, "V2", 2) == 0) { printf ("i386-sequent-ptx2\n"); exit (0); } if (strncmp(un.version, "V1", 2) == 0) { /* XXX is V1 correct? */ printf ("i386-sequent-ptx1\n"); exit (0); } printf ("i386-sequent-ptx\n"); exit (0); #endif #if defined (vax) #if !defined (ultrix) #include #if defined (BSD) #if BSD == 43 printf ("vax-dec-bsd4.3\n"); exit (0); #else #if BSD == 199006 printf ("vax-dec-bsd4.3reno\n"); exit (0); #else printf ("vax-dec-bsd\n"); exit (0); #endif #endif #else printf ("vax-dec-bsd\n"); exit (0); #endif #else #if defined(_SIZE_T_) || defined(SIGLOST) struct utsname un; uname (&un); printf ("vax-dec-ultrix%s\n", un.release); exit (0); #else printf ("vax-dec-ultrix\n"); exit (0); #endif #endif #endif #if defined(ultrix) || defined(_ultrix) || defined(__ultrix) || defined(__ultrix__) #if defined(mips) || defined(__mips) || defined(__mips__) || defined(MIPS) || defined(__MIPS__) #if defined(_SIZE_T_) || defined(SIGLOST) struct utsname *un; uname (&un); printf ("mips-dec-ultrix%s\n", un.release); exit (0); #else printf ("mips-dec-ultrix\n"); exit (0); #endif #endif #endif #if defined (alliant) && defined (i860) printf ("i860-alliant-bsd\n"); exit (0); #endif exit (1); } EOF $CC_FOR_BUILD -o "$dummy" "$dummy.c" 2>/dev/null && SYSTEM_NAME=`"$dummy"` && { echo "$SYSTEM_NAME"; exit; } # Apollos put the system type in the environment. test -d /usr/apollo && { echo "$ISP-apollo-$SYSTYPE"; exit; } echo "$0: unable to guess system type" >&2 case $UNAME_MACHINE:$UNAME_SYSTEM in mips:Linux | mips64:Linux) # If we got here on MIPS GNU/Linux, output extra information. cat >&2 <&2 <&2 </dev/null || echo unknown` uname -r = `(uname -r) 2>/dev/null || echo unknown` uname -s = `(uname -s) 2>/dev/null || echo unknown` uname -v = `(uname -v) 2>/dev/null || echo unknown` /usr/bin/uname -p = `(/usr/bin/uname -p) 2>/dev/null` /bin/uname -X = `(/bin/uname -X) 2>/dev/null` hostinfo = `(hostinfo) 2>/dev/null` /bin/universe = `(/bin/universe) 2>/dev/null` /usr/bin/arch -k = `(/usr/bin/arch -k) 2>/dev/null` /bin/arch = `(/bin/arch) 2>/dev/null` /usr/bin/oslevel = `(/usr/bin/oslevel) 2>/dev/null` /usr/convex/getsysinfo = `(/usr/convex/getsysinfo) 2>/dev/null` UNAME_MACHINE = "$UNAME_MACHINE" UNAME_RELEASE = "$UNAME_RELEASE" UNAME_SYSTEM = "$UNAME_SYSTEM" UNAME_VERSION = "$UNAME_VERSION" EOF fi exit 1 # Local variables: # eval: (add-hook 'before-save-hook 'time-stamp) # time-stamp-start: "timestamp='" # time-stamp-format: "%:y-%02m-%02d" # time-stamp-end: "'" # End: bluez-5.82/PaxHeaders/ChangeLog0000644000000000000000000000005014773211264013427 xustar0020 atime=1743590089 20 ctime=1743591275 bluez-5.82/ChangeLog0000644000000000000000000030336514773211264013122 0ustar00rootrootver 5.82: Fix issue with handling BAP state transitions. Fix issue with handling D-Bus interface removal. ver 5.81: Fix issue with handling MAP and supported features. Fix issue with handling SDP record for Phonebook Access Client. Fix issue with handling AVRCP PDU parameters length mismatch. Fix issue with handling AVRCP PDU for SetAbsoluteVolume. Fix issue with handling AVDTP bad media transport format. Fix issue with handling support for LL Privacy setting. ver 5.80: Fix issue with handling address type for all types of keys. Fix issue with handling maximum number of GATT channels. Fix issue with handling MTU auto-tuning feature. Fix issue with handling AVRCP volume in reconfigured transports. Fix issue with handling VCP volume setting requests. Fix issue with handling VCP connection management. Fix issue with handling MAP qualification. Fix issue with handling PBAP qualification. Fix issue with handling BNEP qualification. Add support for PreferredBearer device property. Add support for SupportedTypes Message Access property. Add support for HFP, A2DP, AVRCP, AVCTP and MAP latest versions. ver 5.79: Fix issue with handling address type while pairing. Add support for allowing to set A2DP transport delay. Add support for persistent userspace HID operation. Add support for handling syncing to multiple BISes. ver 5.78: Fix issue with handling notification of scanned BISes to BASS Fix issue with handling checking BIS caps against peer caps. Fix issue with handling MGMT Set Device Flags overwrites. Fix issue with handling ASE notification order. Fix issue with handling BIG Info report events. Fix issue with handling PACS Server role. Fix issue with registering UHID_START multiple times. Fix issue with pairing method not setting auto-connect. ver 5.77: Fix issue with storing and handling connection parameters. Fix issue with handling device that are marked as temporary. Fix issue with HID and special handling for non-keyboards. Fix issue with BR/EDR not support when discoverable is off. Add support for initial implementation of ASHA profile. ver 5.76: Fix issue with broadcast channel location and stream capabilities. Fix issue with handling BIS management and synchronization. Fix issue with handling Extended Advertising. Fix issue with UserspaceHID and replay structures. Add support for providing PPCP characteristic. ver 5.75: Fix issue with build system and header inclusion. ver 5.74: Fix issue with not enabling Wideband Speech when available. Fix issue with UserspaceHID and Bluetooth Classic devices. Fix issue with checking for services being connected. Fix issue with GATT client connection creation. Fix issue with OBEX and small file transfers. Fix issue with handling pairing with Apple AirPods. ver 5.73: Fix issue with BAP and setting up broadcast source. Fix issue with BAP and register all endpoints. Fix issue with BAP and missing metadata property. Fix issue with BAP and not handling out of order responses. Fix issue with BAP and attempting to set device as connectable. Add support for CCP plugin for call control profile. ver 5.72: Fix issue with BAP and handling stream IO linking. Fix issue with BAP and setup of multiple streams per endpoint. Fix issue with AVDTP and potential incorrect transaction label. Fix issue with A2DP and handling crash on suspend. Fix issue with GATT database and an invalid pointer. Add support for AICS service. ver 5.71: Fix issue with not registering CSIS service. Fix issue with registering pairing callbacks. Fix issue with corruption during discovery filter parsing. ver 5.70: Fix issue with not sending GATT confirmations. Fix issue with not handling initiator properly. Fix issue with not checking PBAP counter length. Add support for MICP profile and MICS service. ver 5.69: Fix issue with BAP enabling state correctly when resuming. Fix issue with detaching source ASEs only after Stop Ready. Fix issue with handling VCP audio location and descriptor. Fix issue with generating IRK for adapter with privacy enabled. Add support for BAP broadcast sink. ver 5.68: Fix issue with A2DP and handling of Transport.Acquire. ver 5.67: Fix issue with BAP and initiating QoS and Enable procedures. Fix issue with BAP and detaching streams when PAC is removed. Fix issue with BAP and reading all instances of PAC. Fix issue with BAP and not being able to reconfigure. Fix issue with BAP and transport configuration changes. Fix issue with BAP and handling unexpected disconnect. Fix issue with GATT and not removing pending services. Fix issue with GATT and client ready handling. Fix issue with handling fallback to transient hostname. Add support for SecureConnections configuration option. Add support for Mesh Remove Provisioning. Add support for Mesh Private Beacons. ver 5.66: Fix issue with A2DP and transport connection collisions. Fix issue with allowing application specific error codes. Fix issue with not setting initiator flag correctly. Fix issue with HoG Report MAP size handling. Add initial support for Basic Audio Profile. Add initial support for Volume Control Profile. ver 5.65: Fix issue with A2DP cache invalidation handling. Fix issue with A2DP and not initialized SEP codec. Fix issue with A2DP and multiple SetConfiguration to same SEP Fix issue with AVRCP and not properly initialized volume. Fix issue with SDP records when operating in LE only mode. Fix issue with HoG and not reading report map of instances. Fix issue with GATT server crashing while disconnecting. Fix issue with not removing connected devices. Fix issue with enabling wake support without RPA Resolution. Fix issue with pairing failed due to the error of Already Paired. Add support for CONFIGURATION_DIRECTORY environment variable. Add support for STATE_DIRECTORY environment variable. Add support for "Bonded" property with Device API. Add experimental support for ISO socket. ver 5.64: Fix issue with handling A2DP discover procedure. Fix issue with media endpoint replies and SetConfiguration. Fix issue with HoG queuing events before report map is read. Fix issue with HoG and read order of GATT attributes. Fix issue with HoG and not using UHID_CREATE2 interface. Fix issue with failed scanning for 5 minutes after reboot. ver 5.63: Fix issue with storing IRK causing invalid read access. Fix issue with disconnecting due to GattCharacteristic1.MTU. Add support for Device{Found,Lost} of advertising monitoring. ver 5.62: Fix issue with handling truncation when loading LTKs. Fix issue with accepting Exchange MTU on EATT bearer. Fix issue with clearing DeviceLost timers on power down. Fix issue with AVCTP browsing channel and missing ERTM. Fix issue with AVDTP and local SEID pool for each adapter. Add support for BR/EDR and LE connection failure reasons. ver 5.61: Fix issue with A2DP while waiting for command response. Fix issue with A2DP when SetConfiguration fails. Fix issue with device removal handling. Fix issue with storing discoverable setting. Add support for Central Address Resolution characteristic. Add support for admin policy plugin. ver 5.60: Fix issue with reading from RFKILL device node. Fix issue with AVDTP and parsing capabilities. Fix issue with UnregisterApplication handling. Fix issue with RegisterProfile if UUID already exists. Fix issue with GATT client attribute read with offset. Fix issue with non-discoverable device and advertising monitor. ver 5.59: Fix issue with string to UUID-32 conversion. Fix issue with connect request if SDP search failed. Fix issue with accepting invalid AVDTP capabilities. Fix issue with unregister handling of AVRCP player. ver 5.58: Fix issue with usage of deprecated GLib functions. ver 5.57: Fix issue with handling GATT notification PDU parsing. Fix issue with registering DIS without a valid source. Fix issue with removing remote SEPs when loading from cache. ver 5.56: Fix issue with setting AVDTP disconnect timer. Fix issue with AVDTP not sending GetCapabilities. Fix issue with AVDTP connecting using streaming mode. Fix issue with handling A2DP and remote SEP disappearing. Fix issue with handling session of A2DP channels. Fix issue with GATT and handling device removal. Fix issue with GATT not accepting multiple requests. Fix issue with HID report value callback registration. Add support for new advertising management command. Add support for battery D-Bus interface. ver 5.55: Fix issue with handling security level for HoG. Fix issue with handling HIDSDPDisable attribute. Fix issue with handling HID virtual cable unplug. Fix issue with handling HID channel disconnect order. Fix issue with handling AVDTP delay reporting states. Fix issue with handling AVRCP notification events. Fix issue with handling AVRCP list player attributes. Fix issue with handling AVRCP category 1 player settings. Fix issue with handling AVRCP media player passthrough bitmask. Fix issue with handling HFP 1.7 default features. Fix issue with handling GATT disconnecting handling. Fix issue with handling GATT database hash. Fix issue with handling service changed characteristic. Fix issue with handling read of multiple characteristic values. Fix issue with handling Just-Works auto-accept pairing. Fix issue with handling authentication of bonded devices. Fix issue with handling L2CAP streaming mode for AVDTP. Fix issue with handling SysEx parser for MIDI support. Fix issue with handling configured scan parameter values. Fix issue with handling temporary devices removal. Fix issue with handling advertising flags. ver 5.54: Fix issue with HOGP to accept data only from bonded devices. Fix issue with A2DP sessions being connected at the same time. Fix issue with class UUID matches before connecting profile. Add support for handling MTU auto-tuning option for AVDTP. Add support for new policy for Just-Works repairing. Add support for Enhanced ATT bearer (EATT). ver 5.53: Fix issue with handling unregistration for advertisment. Fix issue with A2DP and handling recovering process. Fix issue with udpating input device information. Add support for loading blocked keys. ver 5.52: Fix issue with AVDTP session disconnect timeout handling. Mark media endpoint APIs as stable interfaces. ver 5.51: Fix issue with first agent not being registered as default. Fix issue with loading devices without Service Changed CCC. Fix issue with GATT client and extended property reading. Fix issue with handling GATT client invalid read behavior. Fix issue with handling GATT disconnect handler removal. Fix issue with missing GATT/GAP service records for SDP. Fix issue with checking SDP continuation state length. Fix issue with HID device removal on HoG disconnect. Fix issue with AVDTP and session destroy handling. Fix issue with AVCTP and output MTU accounting. Fix issue with AVRCP and creating media items. Add support for GATT database caching feature. Add experimental support for Bluetooth Mesh Profile. ver 5.50: Fix issue with GATT and reading long values. Fix issue with GATT and reading multiple includes. Fix issue with GATT and service changes when offline. Fix issue with handling secondary service discovery. Fix issue with handling persistency of CCC values. Fix issue with handling Mesh session on disconnection. Fix issue with handling Mesh proxy PDU SAR message length. Fix issue with handling Mesh default heartbeat TTL value. Add support for Mesh node-reset operation handling. Add support for GATT authorization request handling. Add support for GATT minimum key size requirements. Add support for GATT server and included services. Add support for handling additional advertising data. Add support for handling separate discoverable state. Add support for enabling HFP version 1.7 features. Add support for dedicated Bluetooth logging daemon. ver 5.49: Fix issue with configuring discoverable advertising flag. Fix issue with bearer selection and single mode controllers. Fix issue with Connect and ConnectProfile returning in progress. Fix issue with missing Paired property change when not bonded. Fix issue with storage for controllers without public address. Fix issue with handling AVCTP disconnecting the channel queue. Fix issue with not clearing connectable setting on power off. Fix issue with creating multiple mgmt socket instances. Fix issue with GATT server and BR/EDR only devices. Fix issue with InterfaceAdded event ordering. Add support for generic ConnectDevice method call. Add support for Mesh heartbeat client functionality. ver 5.48: Fix issue with subscriptions for unpaired devices. Fix issue with handling A2DP and no available SEP. Fix issue with handling AVCTP change path support. Fix issue with handling AVCTP browsing channel. Fix issue with handling AVCTP passthrough PDUs. Fix issue with handling detaching of controller. Fix issue with handling start discovery results. Fix issue with handling non-connectable devices. Fix issue with handling unused parameter in WriteValue. Add support for service side AcquireWrite and AcquireNotify. Add support for providing address type information. Add support for cable based authentication and pairing. Add support for Bluetooth Low-Energy battery service. Add support for BTP client for qualification testing. Add support for additional Mesh control functionality. Mark advertising manager APIs as stable interfaces. ver 5.47: Fix issue with handling AcquireNotify registration. Fix issue with handling support for reconnection interval. Fix issue with handling A2DP transport and accepting streams. Fix issue with fallback from BR/EDR to LE bearer handling. Add support for appearance and local name advertising data. Add support for retrieving the supported discovery filters. Add support for decoding Bluetooth 5.0 commands and events. Add support for decoding Bluetooth Mesh advertising bearer. Add support for Bluetooth Mesh control application. ver 5.46: Fix issue with handling ATT over BR/EDR connections. Fix issue with SDP browsing cleanup after connection. Fix issue with pointer dereference and OPP Put request. Fix issue with identity address updates during pairing. Fix issue with not removing services that had disappeared. Add support for improved discovery of included services. Add support for simplified characteristics discovery. Add support for GATT caching configuration option. Add experimental support for AcquireWrite and AcquireNotify. ver 5.45: Fix issue with agent support in Bluetooth client tool. Fix issue with handling re-connection policy. Fix issue with handling unknown ATT commands. Fix issue with handling GATT Service Includes property. Fix issue with handling PullAll for OBEX transfers. Fix issue with handling delay in AVDTP Suspend responses. Fix issue with handling decoding of management frames. Add support for frame counters in Bluetooth monitor tool. ver 5.44: Fix issue with GAP and GATT service registration. Fix issue with wrong address type for ATT sockets. Fix issue with dictionary entries for advertising. Fix issue with device information and HID over GATT. Fix issue with handling secondary service discovery. Fix issue with handling Attribute Read Long procedure. Fix issue with handling Attribute Write Long procedure. Fix issue with handling abort of AVDTP SetConfiguration. Add support for single-mode static address configuration. Add support for MIDI over Bluetooth Low Energy. ver 5.43: Fix issue with HID over GATT support. Fix issue with ATT Find By Type response handling. Fix issue with handling insufficient authentication. Fix issue with bonding while pairing is in progress. Fix issue with BR/EDR pairing for dual-mode devices. Fix issue with handling profile policy resets. Fix issue with connecting state of services. Fix issue with handling PAN GN Master role. Add support for enabling LE Privacy feature. ver 5.42: Fix issue with PBAP call logs from different folders. Fix issue with OBEX over L2CAP and PowerPC architecture. Fix issue with BR/EDR over LE selection during discovery. Fix issue with selection of bearer after bonding. Fix issue with handling socket recv() return values. Fix issue with setting connecting service state. Fix issue with setting correct ATT default MTU value. Fix issue with not setting AVRCP player identifier. Fix issue with handling AVRCP browsable player. Fix issue with addressing AVRCP player changes. Add support for new management tracing capability. Mark GATT D-Bus APIs as stable interfaces. ver 5.41: Fix issue with service state changes handling. Fix issue with AVRCP and no available player. Fix issue with handling discovery filters. Fix issue with handling temporary addresses. Fix issue with GATT MTU size and BR/EDR links. Fix issue with OBEX and creating directories. ver 5.40: Fix issue with not storing GATT attributes. Fix issue with optional GATT notifications. Fix issue with reading GATT extended properties. Fix issue with GATT device name properties. Fix issue with previously paired devices. Fix issue with handling device removal. Fix issue with profile connection handling. Add support for TTY monitor protocol. ver 5.39: Fix issue with missing uHID kernel support. Fix issue with GATT reliable write handling. Fix issue with GATT service changed handling. Fix issue with GATT execute write handling. Fix issue with AVRCP player event handling. Fix issue with AVRCP controller handling. Fix issue with AVDTP connection handling. Fix issue with AVDTP error handling. ver 5.38: Fix issue with stack overflow and UUID handling. Fix issue with ObjectManager interface and GATT. Fix issue with GATT database and error handling. Fix issue with GATT client notifications. Fix issue with GATT object ordering. Fix issue with GATT default MTU exchange. Fix issue with device attribute clearing. Fix issue with AVRCP capabilities request. ver 5.37: Fix issue with registering external profiles. Fix issue with connecting external profiles. Fix issue with GATT service changed handling. Fix issue with not emitting GattServices update. Convert to unified HID over GATT profile support. Convert to KeyboardDisplay as default IO capability. Install btattach utility by default. ver 5.36: Fix issue with PBAP headers for size query. Fix issue with AVRCP current player handling. Fix issue with device information handling. Fix issue with device disconnect handling. Fix issue with duplicate connect handling. Fix issue with attribute claiming for drivers. ver 5.35: Fix issue with connected devices after discovery. Fix issue with profile support and LTK loading. Fix issue with AVRCP events for volume control. Fix issue with OBEX session owner handling. Fix issue with HID over GATT setup failures. Fix issue with GATT notification registration. Fix issue with GATT cache validation feature. Add support for persistent GATT database. Add support for controller enabling option. ver 5.34: Fix issue with GATT profiles and auto-connect. Fix issue with missing GoepL2CapPsm SDP data. Fix issue with suspending AVDTP endpoints. Fix issue with audio service state on disconnect. Add support for AVRCP Set Addressed Player feature. Add support for AVRCP Get Folder Items feature. Add support for Android 5.1 HFP WBS callbacks. ver 5.33: Fix issue with memory leak in GATT database. Fix issue with AVDTP set configuration handling. Fix issue with AVDTP discover procedure. Fix issue with not emitting Paired property. ver 5.32: Fix issue with OPP GET request path handling. Fix issue with ATT information request errors. Fix issue with advertising instance numbers. Fix issue with overwriting SDP record cache. Fix issue with new connections during disconnect. Add support for GATT security auto-elevation. ver 5.31: Fix issue with crash in networking interface. Fix issue with crash when creating endless GATT loops. Fix issue with memory leak when connecting services. Fix issue with memory leak creating new D-Bus proxy. Fix issue with profile connections from remote devices. Fix issue with GATT over BR/EDR and MTU notification. Fix issue with HID and dual mode remote devices. Fix issue with handling A2DP vendor codec setup. Fix issue with AVRCP and syncing player state. Fix issue with GATT secondary discovery handling. Fix issue with wrong characteristic allocation. Add support for handling BNEP setup response. Add support for setting GATT database security flags. Add support for setting discovery filters interface. Add support for user controlled advertising interface. Update Android qualification documentation to PTS 6.1 release. ver 5.30: Fix compilation error in C++ due to inline function. Fix issue with missing storage of device information. Fix issue with GATT client and gaps in service handles. Fix issue with AVDTP discovery callback crashing. Fix issue with AVCTP channel handling in case of conflicts. Fix issue with AVRCP target and get capabilities command. Add experimental support for LE advertising manager API. Add support for Android 5.1 GATT MTU exchange API. ver 5.29: Fix issue with AVCTP initial key repeat timeout. Fix issue with Android application disconnect handling. Fix issue with Android support and service notifications. Fix issue with Android support and Exchange MTU Request. Fix issue with Android HFP support and AT+CMER handling. Fix issue with Android HFP support and SLC setup. Fix issue with Android HFP support and call hold status. Fix issue with Android HFP support and indicator handling. Fix issue with Android HFP support and SCO/eSCO disconnection. Fix issue with Android HID over GATT support and battery service. Fix issue with GATT sending Exchange MTU Request for BR/EDR. Fix issue with GATT notification support without CCC. Fix issue with GATT object life-time after disconnects. Fix issue with GATT notification handling API. Add experimental support for GATT client D-Bus API. Add experimental support for GATT server D-Bus API. Add support for Multi Profile Specification. Update Android qualification documentation to PTS 6.0 release. ver 5.28: Fix issue with GATT device discovery and probing. Fix issue with bearer selection for dual-mode devices. Fix issue with device removal while connected. Fix issue with device name setting from inquiry response. Fix issue with missing termination of name characteristic. Fix issue with UTF-8 length handling for device name. Fix issue with AVCTP key auto release handling. Fix issue with AVCTP key press repetition handling. Fix issue with payload sizes and GATT notifications. Fix issue with memory corruption and GATT notifications. Add support for HID proxy switching and CSR 8510 A10 devices. Add support for Broadcom hex2hcd conversion utility. ver 5.27: Fix issue with endian handling and management interface. Fix issue with pending GATT operations when disconnecting. Fix issue with 128-bit UUID conversions for HID over GATT. Add support for Android 5.0 SELinux policies. ver 5.26: Fix issue with handling A2DP XCASE connection state. Fix issue with crash and A2DP configuration failures. Fix issue with crash during OBEX session shutdown. Add support for version 1.2 of Phonebook Access Profile. Add support for HID over GATT get and set report handling. Add support for Low Energy Secure Connections feature. Add support for Bluetooth 4.2 commands and events. Add support for Android 5.0 Bluetooth features. ver 5.25: Fix issue with SCO connection after codec negotiation. Fix issue with GATT and secondary service discovery. Fix issue with GATT write descriptor callback. Fix issue with MAP supported features bits. Add support for MAP local time and timezone offset. Add support for PBAP speed-dial and favorites folders. Add support for PBAP speed-dial and identifier filters. Add support for controller mode configuration option. Add initial support for Android Lollipop features. ver 5.24: Fix issue with storing of connection parameters. Add support for Phonebook Access Profile 1.2 features. Add support for Message Access Profile 1.2 event reports. Add support for Android Bluetooth configuration options. ver 5.23: Fix issue with concurrent authorization requests. Fix issue with HID report identifier mismatch. Fix issue with crash when receiving uHID events. Fix issue with crash and OBEX disconnect handling. Fix issue with OBEX client transfers and suspend. Fix issue with parsing of MAP application parameters. Fix issue with devices rejecting AVRCP GetCapabilities. Add support for kernel whitelist and Android Bluetooth. ver 5.22: Fix issue with UHID_OUTPUT events mapping. Fix issue with UHID_FEATURE events handling. Fix issue with UINT32_MAX overflow and AVRCP. Fix issue when dirent type DT_UNKNOWN is returned. Add support for kernel whitelist filtering feature. Add support for Android Bluetooth GATT over BR/EDR. ver 5.21: Fix issue with SDP requests and wrong PDU size. Fix issue with handling passive scanning triggers. Add support for storing and loading connection parameters. Add support for kernel background auto-connection feature. Add support for Android Bluetooth Scan Parameters feature. Add support for Android Bluetooth Device Information feature. Add support for Android Bluetooth Health Device interface. ver 5.20: Fix issue with LED handling of PS3 controllers. Add support for Android Bluetooth GATT server interface. Add support for Android Bluetooth HID over GATT feature. Add support for Android Bluetooth multi-profile feature. Add support for Android Bluetooth aptX audio integration. Note: aptX codec not included ver 5.19: Fix issue with OBEX Put-Delete and Create-Empty methods. Fix issue with AVRCP browsable/searchable player properties. Fix issue with handling multiple default agents. Fix issue with handling unpair event per bearer. Fix issue with HID over GATT report ID presence. Add support for HID protocol handling in userspace. Add support for Bluetooth reconnection policy framework. Add support for Android Bluetooth SCO over HCI transport. Add support for Android Bluetooth audio quality control. Add support for Android Bluetooth Low Energy only mode. ver 5.18: Fix issue with identifying LE single mode devices. Fix issue with L2CAP and RFCOMM peer address lookup. Add support for handling OBEX authentication procedure. Add support for Android Bluetooth GATT client interface. ver 5.17: Fix issue with not resetting OBEX SRM setup. Fix issue with BR/EDR devices and auto-connect list. Fix issue with bonding complete detection as peripheral. Fix issue with not updating bearer timestamp of connections. Fix issue with paired property for multiple bearers. Add support for Android Bluetooth Handsfree interface. Add support for Android Bluetooth Wideband speech. ver 5.16: Fix issue with HID over GATT physical location. Fix issue with HID over GATT unique identifier. Fix issue with missing paired property notification. Fix issue with endianess of long term key storage. Add support for storing signature resolving keys. Add support for Android Bluetooth AVRCP interface. ver 5.15: Fix issue with LE enabling and background scanning. Fix issue with HID over GATT input device name. Fix issue with storage of slave long term keys. Add support for handling identity resolving keys. Add support for Android Bluetooth A2DP interface. Add support for Android Bluetooth audio interface. ver 5.14: Fix issue with marking PS3 controllers as trusted. Fix issue with authorization of PS3 controllers. Add support for DualShock 4 controller detection. Add support for legacy pairing emulation. Add support for secure simple pairing emulation. Add support for automated pairing testing. Add support for RFCOMM protocol testing. Add support for HCI controller testing. ver 5.13: Fix issue with PS3 controller detection. Add support for data transfers to L2CAP testing tool. Add support for delay reporting to AVDTP testing tool. Add support for Android Bluetooth Core interface. Add support for Android Bluetooth Socket interface. Add support for Android Bluetooth HID Host interface. Add support for Android Bluetooth PAN interface. ver 5.12: Fix issue with missing reply to DisconnectProfile. Fix issue with icon property and class of device changes. Fix issue with HID devices when SDP record is not available. Fix issue with handling auto-pairing of printers. Fix issue with agent authorization handling. Add support for PS3 controller setup and pairing. Add support for LE L2CAP CoC test capabilities. Add support for AVDTP qualification test cases. Add support for SMP cryptographic test cases. ver 5.11: Fix issue with connection attempt when not powered. Fix issue with assigning player to AVRCP target role. Fix issue with OBEX default cache directory. Fix issue with SDP search error handling. Fix issue with processing of SDP records. Fix issue with HID to HCI switching utility. Fix issue with mgmt end-to-end testing tool. Fix issue with L2CAP end-to-end testing tool. Add support for SMP end-to-end testing tool. Add support for more Wii controllers. ver 5.10: Fix issue with discoverable timeout handling. Fix issue with MAP messages and record version. Fix issue with MAP messages and status events. Fix issue with MAP messages and relative folders. Fix issue with MAP messages and type property signals. Fix issue with transfer size for OBEX GET operations. Fix issue with AVRCP service class identifier. Fix issue with AVRCP tracking seeked signal. Add support for OBEX command line client. ver 5.9: Fix issue with network service and adapter removal. Fix issue with misleading OBEX error messages. Fix issue with OBEX transport reference handling. Fix issue with memory leak with MAP event handler. Fix issue with missing MAP property changed signal. Fix issue with message type property values. Fix issue with empty UUID list for devices. Fix issue with profile agent cancel method. Remove dependency on USB library. ver 5.8: Fix issue with missing OBEX session properties. Fix issue with missing SDP service refresh. Fix issue with SDP attribute range check. Fix issue with priority for SDP transactions. Fix issue with service discovery after pairing. Fix issue with race condition in service list. Fix issue with input service state transition. Fix issue with default authorization for profiles. Fix issue with AVRCP browsing channel connections. Add support for AVRCP role agnostic sessions. ver 5.7: Fix issue with missing UUID discovery during pairing. Fix issue with broken patch for SDP range check handling. Fix issue with AVRCP usage of UID=0 for paused/stopped. Add support MAP notification dispatching. ver 5.6: Fix issue with incoming connections without SDP record. Fix issue with canceling ongoing device connections. Fix issue with handling failed connection attempts. Fix issue with pending resume during A2DP open failures. Fix issue with registering AVRCP unsupported notification. Fix issue with listing available AVRCP target settings. Fix issue with missing error for OBEX SetPath commands. Fix issue with missing OBEX session command queue. Fix issue with retrieving multiple MAP event reports. Add support for command line player utility. ver 5.5: Fix issue with race condition between SDP and properties. Fix issue with handling storage of private device addresses. Fix issue with NFC out-of-band pairing and power states. Fix issue with short name during device update handling. Fix issue with handling AVRCP without A2DP being present. Add support for handling AVRCP pass-through operations. Add support for automatically reconnecting HID devices. Add support for automatically pairing of devices. ver 5.4: Fix issue with invalid memory access and SDP service search. Add support for available player changed event for controller. Add support for UIDs changed event for AVRCP controller. Add support for mandatory AVRCP pass-through operations. Add support for Message Notification Service (MNS) server. Add support for agent methods within command line client. ver 5.3: Fix issue with registering invalid profiles. Fix issue with inconsistent A2DP transport state. Fix issue with A2DP resume while in configured state. Fix issue with buffer overflow when processing SDP response. Fix issue with missing range check for SDP attribute response. Fix issue with missing validation of SDP data elements. Fix issue with missing fallback to static hostname. Fix issue with default adapter assignment. ver 5.2: Fix issue with connection handling for Low Energy. Fix issue with broken device discovery handling. Fix issue with invalid memory access within A2DP. Fix issue with handling empty path name of SetPath. Fix issue with handling Message Access Profile filters. Fix issue with handling network service unregistration. Fix issue with not handling bogus device pairing results. Fix issue with initial service discovery and profile manager. Add support for AVRCP volume notifications. Add support for AVRCP browsing commands. ver 5.1: Fix issue with crash when removing OBEX session. Fix issue with HID device disconnected from kernel. Fix issue with buffer overflow when parsing HID SDP record. Fix issue with SDP_TEXT_STR16 and SDP_URL_STR16 parsing. Add support for integration with systemd's hostname daemon. Add support for separate adapter alias property. Add support for adapter and device modalias properties. Add support for official BlueZ device information. Add support for asynchronous management interface handling. Add tool for testing management interface compliance. Add tool for testing SDP qualification requirements. Add tool for testing various EIR and AD data records. ver 5.0: Introduce D-Bus Properties and ObjectManager interfaces. Add support for generic profile interface. Add support for global agent interface. Add support for integrated OBEX daemon. Add support for integrated hcidump utility. Add support for Bluetooth tracing and monitor utility. Add support for Bluetooth command line client utility. Remove support for Handsfree gateway handling. Remove support for GStreamer A2DP and SBC elements. Disable default installation of Bluetooth library. ver 4.101: Fix issue with missing BlueZ service file. Fix issue with aborting A2DP setup during AVDTP start. Fix issue with handling of multiple A2DP indication. Fix issue with handling AVDTP abort with invalid SEID. Fix issue with rejecting AVDTP abort commands. Add support for handling AVDTP command collision. ver 4.100: Fix issue with crashing when SCO connection fails. Fix issue with HFP gateway failing on first GSM connection. Fix issue with AVRCP and handling of vendor commands. Fix issue with handling AVRCP subunit info command. Fix issue with missing capability for AVRCP track reached end. Fix issue with AVDTP signaling and GStreamer SBC NULL check. Fix issue with AVDTP Reconfigure Reject message. Fix issue with incorrect EIR length parsing. Fix issue with SDP disconnect for HIDSDPDisable. Fix issue with SDP interoperability with Mac OS X Lion. Fix issue with reverse SDP discovery with some devices. Fix issue with discovering state during power off operation. Add support for AVRCP Volume Changed notifications. Add support for AVRCP Set Absolute Volume handling. Add support for display legacy PIN code agent method. Add support for multiple media transports per endpoint. Add support for discovering device information characteristics. Add support for vendor source for Device ID setting. Add support for immediate alert server. Add support for link loss server. Notes: This version requires D-Bus 1.4 or later. This version requires GLib 2.28 or later. ver 4.99: Fix issue with missing retries for BNEP connection setup. Fix issue with not showing name if first EIR has no details. Fix issue with running SDP discovery for LE devices. Add support for GATT using 128-bit Bluetooth UUIDs. Add support for retrieving key size information. Add support for storing Long Term Keys. Add support for Proximity Reporter API. Add support for KeyboardDisplay IO capability. Add support for version 1.0 of management API. Add support for monitoring interface. ver 4.98: Fix issue with adapter list upon initialization failure. Fix issue with missing legacy property for Low Energy. Fix issue with missing EIR information handling. Fix issue with device address type tracking. Fix issue with alert level characteristic. Fix issue with headset shutdown handling. Fix issue with Wiimote address handling. Add support for advanced l2test options. Add support for attribute protocol and multiple adapters. ver 4.97: Update support for proximity profile. Fix issue with SBC audio decoding quality. Fix multiple issues with HFP support. Fix multiple issues with A2DP support. Fix multiple issues with AVDTP support. Fix multiple issues with AVRCP support. Add support for AVRCP meta-data transfer. Add support for Bluetooth based thermometers. ver 4.96: Fix issue with race condition in AVDTP stream start. Fix issue with global adapter offline switching. Fix issue with pairing and No Bonding devices. Add support for Nintendo Wii Remote pairing. ver 4.95: Fix issue with AVCTP replies with invalid PID. Fix issue with AVRCP and unknown packet types. Fix issue with AVRCP not using NOT_IMPLEMENTED correctly. Fix issue with AVDTP discovery if all endpoints are in use. Fix issue with invalid memory writes and media support. Fix issue with not removing device alias and unbonding. Fix issue with device disconnects and offline mode handling. Add support for setting adapter name based on machine-info. Add support for systemd service configuration. ver 4.94: Fix issue with invalid read of memory in various modules. Fix issue with buffer overflow when sending AVDTP commands. Fix issue with response to vendor dependent AVRCP commands. Fix issue with headset when not able to reply with ERROR. Fix issue with crash when creating a device from storage. Fix issue with handling non UTF-8 devices names. Add support for improved discovery procedure. ver 4.93: Fix issue with property type and Health Main channel. Fix issue with crash when removing devices. Add support for hid2hci and udev integration. ver 4.92: Fix issue with handling of A2DP suspend response. Fix issue with crashing when acquiring A2DP stream. Fix issue with missing check for valid SCO before shutdown. Fix issue with waiting for POLLERR when disconnecting SCO. Fix issue with disconnect after primary service discovery. Fix issue with attribute interface registration. Add support for primary services over BR/EDR. Add support for flushable packets of A2DP media. ver 4.91: Fix issue with LMP version string and hciconfig. Fix issue with missing discovery signal when scanning. Fix issue with wrong state and canceling name resolving. Fix issue with missing check during adapter initialization. Fix issue with missing protocol not supported error and A2DP. Fix issue with crash during driver unregistering and A2DP. Fix issue with crash when receiving AVDTP close command. Fix issue with remote SEP handling when A2DP codec changes. Fix issue with SCO hangup handling and state changes. Fix issue with security level and MCAP instances. Fix issue with memory leak and HDP data channels. Add support for discover characteristics by UUID to gatttool. Add initial support for Out-of-Band association model. Add initial support for SIM Access Profile. ver 4.90: Fix issue with setting of global mode property. Fix issue with handling of RequestSession responses. Fix issue with TP_BNEP_CTRL_BV_01_C qualification test. Fix issue with too short AVDTP request timeout. Add support for SIM Access Profile manager. Add support for new UUID utility functions. Add support for attribute server notifications. Add support for client characteristic configuration. Update support for interactive GATT utility. ver 4.89: Fix issue with name resolving when discovery is suspended. Fix issue with parsing flags of advertising report. Fix issue with SEP handling if interface is disabled. Fix issue with device object creation on disconnect event. Fix issue with indicators whenever the driver is initialized. Fix issue with call indicator when parsing call info reply. Fix issue with crash and allowed GATT MTU was too large. Add support for SDP record of Primary GATT services. Add support for interactive mode for GATT utility. ver 4.88: Fix issue with HID channel reference count handling. Fix issue with daemon exit on badly formatted AT+VTS. Fix issue with crash while parsing of endpoint properties. Fix issue with possible crash on AVDTP Suspend request timeout. Fix issue with stopping inquiry before adapter is initialized. Fix issue with creating device object when connection fails. Fix issue with sending HCIDEVUP when adapter is already up. Fix issue with handling bonding IO channel closing. Fix agent cancellation in security mode 3 situations. Update pairing code to support management interface. ver 4.87: Fix issue with initialization when adapter is already up. Fix issue with attribute server MTU and incoming connections. Fix issue with duplicate characteristics after discovery. ver 4.86: Revert wrong fix for SDP PDU size error response. Fix various memory leaks in A2DP and AVDTP support. Add Routing property to MediaTransport interface Add proper tracking mechanism to NREC status. Add READ_BLOB_REQUEST support to attribute server. ver 4.85: Fix issue with event mask setting for older adapters. Fix issue with device creation and pairing failures. Add support for telephony support via oFono. Add support for characteristic security level. Update support for service registration. ver 4.84: Fix issue with wrong parameters and device found signals. Fix issue with leaking EIR data if RSSI does not change. Fix issue with adapter initialization state. Fix issue with closing of SDP server sockets. ver 4.83: Fix issue with already connected HFP/HSP endpoints. Fix missing reply when create device is canceled. Fix memory leak within the attribute server. Fix memory leak with unused extended inquiry name. Fix setting paired state when device->authr is false. Fix clearing authentication request for renewed keys. Add support for storing link keys in runtime memory. Update support for primary service discovery. ver 4.82: Fix crash with mmap of files with multiples of page size. Fix HFP response and hold (AT+BTRH) command response. Fix device creation error response when powered off. Fix device removal when connecting/browsing fails. Add initial attribute permission implementation. Add AVDTP SRC stream send buffer size verification. Add support for setting link policy based on features. ver 4.81: Fix issue with telephony driver initialization. Fix issue with adapter services list initialization. Fix crash after simultaneous authentication requests. Add support for primary service search on device creation. ver 4.80: Fix legacy link key storing for some buggy adapters. Fix invalid memory access when EIR field length is zero. Fix adapter initialization to wait for kernel HCI commands. Fix initialization of adapters which are already up. Fix possible race condition when initializing adapters. Fix possible crashes when attempting to connect AVDTP. Fix not aborting sink stream configuration on disconnect. Fix not indicating disconnected state when connecting to AVDTP. Fix not dropping AVDTP session when canceling stream setup. Fix AVDTP abort not being send when the state is idle. Fix regression with Low Energy and interleave discovery. Add a new configuration option to disable Low Energy support. Add iwmmxt optimization for SBC for ARM PXA series CPUs. Update support for GATT Primary Service Discovery. Update MCAP and HDP support. ver 4.79: Fix issue with adapter initialization race condition. Update new Bluetooth Management interface support. ver 4.78: Fix various issues with AVDTP timer handling. Fix various issues with handling of mode changes. Fix issue with audio disconnect watch in connecting state. Fix issue with handling call waiting indicators in telephony. Fix issue with handling UUID parameter and RegisterEndpoint. Add initial support for Bluetooth Management interface. Add support for Application property to HealthChannel. ver 4.77: Fix issue with device name and accessing already freed memory. Fix issue with handling CHLD=0 command for handsfree. Fix issue with manager properties and no adapters. Fix issue with properties and broken service records. Fix issue with A2DP playback and sample rate changes. Update MCAP and HDP support. ver 4.76: Fix issue in telephony driver with hanging up held call. Fix issue in telephony driver with notifications when on hold. Fix issue with blocking on setconf confirmation callback. Fix issue with not always signaling new streams as sinks. Fix issue with errors in case of endpoint request timeout. Fix issue with HFP/HSP microphone and speaker gain values. Add source if the device attempt to configure local sink stream. Add PSM option for GATT/ATT over BR/EDR on gatttool. Add support for GATT/ATT Attribute Write Request. Update MCAP and HDP support. ver 4.75: Fix use of uninitialized variable on legacy pairing. Fix mismatch of attribute protocol opcode. ver 4.74: Fix regression for Legacy Pairing. Fix wrong PSM value for attribute protocol. Fix issue with RSSI field in advertising reports. Add support for Add BR/EDR and LE interleaved discovery. Add support for GATT write characteristic value option. Add support for specifying download address for AR300x. ver 4.73: Fix problem with EIR data when setting the name. Fix reading local name from command complete event. Fix registering local endpoints with disabled socket interface. Add support for more HCI operations using ops infrastructure. Add support for GATT characteristic hierarchy. Add support for GATT indications. ver 4.72: Fix memory leak while connecting BTIO channels. Fix crash with GStreamer plugin if SBC is not supported. Fix issue with GATT server stop sending notifications. Fix issue with GATT and dealing with the minimum MTU size. Fix issue with file descriptor leak in GATT client. Add support for UUID 128-bit handling in attribute client. Add support for encoders/decoders for MTU Exchange. Add support for the MTU Exchange procedure to the server. Add support for a per channel MTU to the ATT server. Add support for Characteristic interface. Add support for new Media API and framework. Add initial support for HDP plugin. ver 4.71: Fix compilation when SBC support in not enabled. Fix crash with RequestSession and application disconnects. Fix memory leak and possible crash when removing audio device. Fix issue with closing stream of locked sep when reconfiguring. Fix issue where discovery could interfere with bonding. Fix issue with Connected status when PS3 BD remote connects. Fix issue with lifetime of fake input devices. Add support for compile time option of oui.txt path. Add support for printing IEEE1284 device ID for CUPS. Add plugin for setting adapter class via DMI. Add more features for attribute protocol and profile. Add initial support for MCAP. ver 4.70: Fix incoming call indication handling when in WAITING state. Fix various SDP related qualification test case issues. Fix logic to write EIR when SDP records are changed. Fix UTF-8 validity check for remote names in EIR. Add support for UUID-128 extended inquiry response. Add service UUIDs from EIR to the DeviceFound signal. Add fast connectable feature for Handsfree profile. Add HCI command and event definitions for AMP support. Add firmware download support for Qualcommh devices. Add host level support for Atheros AR300x device. Add initial support of ATT and GATT for basic rate. ver 4.69: Fix issue with calling g_option_context_free() twice. Fix inconsistencies with initial LE commands and events. Add support for telephony ClearLastNumber method. Add support for network server interface. ver 4.68: Fix initialization of adapters in RAW mode. Fix signal strength for HFP in Maemo's telephony support. Add support for following the radio state via Maemo's MCE. Add initial set of LE commands and events definitions. Add mode option for L2CAP sockets to the BtIO API. ver 4.67: Fix issue with authentication reply when bonding already completed. Fix issue with not canceling authentication when bonding fails. Fix issue with changed combination keys and temporary storage. Fix issue with sdp_get_supp_feat library function. Fix issue with missing unblock on device removal. Fix issue with not waiting for mode change completion. Add ARMv6 optimized version of analysis filter for SBC encoder. ver 4.66: Fix regression with full debug enabling via SIGUSR2. Fix redundant speaker/microphone gains being sent. Fix not emitting PropertyChanged for SpeakerGain/MicrophoneGain. Fix issue with storage usage when a record is not found in memory. Fix issue with DiscoverServices not retrieving any records. Fix audio profile disconnection order to match whitepaper. Fix auto-accept confirmation when local agent has NoInputNoOutput. Fix remote just-works SSP when MITM protection is required. Fix performing dedicated bonding without MITM requirement. Add support for storing debug link keys in runtime memory. ver 4.65: Fix issues with general bonding being default setting now. Fix driver removal upon device removal. Add new "Blocked" property to device objects. Add hciconfig support for blacklisting. Add support for dynamic debug feature. ver 4.64: Fix invalid memory access in headset_get_nrec function. Fix issue with disconnect event on higher protocol layers. Fix issue with list parsing in sdp_set_supp_features function. Fix device object reference counting for SDP browse requests. Add missing memory checks whenever memory is allocated for SDP. Add support for exporting local services via D-Bus. Add more L2CAP Enhanced Retransmission test options. ver 4.63: Fix avdtp_abort not canceling pending requests. Fix stale connection when abort gets rejected. ver 4.62: Fix accidental symbol breakage with inquiry transmit power. Fix using invalid data from previous headset connection. Fix double free on AVDTP Abort response. Fix possible crash while verifying AVDTP version. Fix missing inuse flag when AVDTP stream is configured. Add support for Bluetooth controller types. ver 4.61: Fix issues with Read Inquiry Response Transmit Power Level. Fix possible invalid read when removing a temporary device. Fix mode restoration when remember_powered is false. Fix conference call releasing in telephony-maemo. Fix segmentation fault with authorization during headset disconnects. Add support for handling unanswered AVDTP request on disconnect. Add support for handling Inquiry Response Transmit Power Level. Add support for caching of remote host features. Add preliminary voice dialing support for HSP. ver 4.60: Fix voice mailbox number reading from SIM. Fix some races with D-Bus mainloop integration. Add helpers for D-Bus signal watches. ver 4.59: Add values for Bluetooth 4.0 specification. Add SDP functions for HDP support. Add test scripts for input and audio. Fix missing close on BtIO create_io function. Fix sending incorrect AVDTP commands after timeout occurs. Fix timer removal when device disconnects unexpectedly. Fix Extended Inquiry Response record for Device ID. ver 4.58: Fix crash when adapter agent exists during authentication. Fix CK-20W quirks for play and pause events. ver 4.57: Fix unloading of drivers for uninitialized adapters. Fix debug message to use requested and not opened SEID. Fix codec selection for GStreamer plugin. Fix deleting of SDP records during service updates. Fix deleting of SDP records when a device is removed. Fix handling when the SDP record is modified on remote device. Fix potential buffer overflow by using snprintf instead of sprintf. Fix const declarations for some storage function parameters. ver 4.56: Add missing values from Bluetooth 3.0 specification. Add proper tracking of device paired status. Fix tracking of devices without permanently stored link key. Fix issue with link key removal after connection failures. Fix legacy pairing information based on remote host features. Fix off-by-one issue with AVDTP capability parsing. Fix AVRCP, AVCTP, AVDTP, A2DP and HFP version numbers. Fix agent canceling before calling agent_destroy. Fix service record parsing with an empty UUID list. Fix various SDP related memory leaks. ver 4.55: Add support for POSIX capabilities dropping. Add special quirk for the Nokia CK-20W car kit. Fix error code handling for AVDTP SetConfiguration response. Fix updating out of range list when RSSI hasn't changed. Fix various memory leaks and unnecessary error checks. ver 4.54: Add introspection interface to output of introspection calls. Fix stream handling when media transport disconnects prematurely. Fix command timeout handling when there's no stream. Fix headset_suspend_stream behavior for invalid states Fix issue with AVDTP ABORTING state transition. Fix issue with AVDTP suspend while closing. ver 4.53: Fix issue with telephony connection state notifications. Fix AVDTP stream leak for invalid media transport config. Fix audio connection authorization handling with timeouts. Fix race condition in authorizing audio connections. Fix device authorized setting for AVRCP-only connections. Fix duplicate attempts from device to connect signal channel. ver 4.52: Add AVCTP support to test utility. Fix AVDTP Abort when transport closes before response. Fix authorization when the audio profiles are slow to connect. Fix potential AVDTP reference leaks. ver 4.51: Add utility for basic AVDTP testing. Add support for configuring L2CAP FCS option. Fix discovery mode for CUPS 1.4.x and later. Fix global state tracking of audio service. Fix last issues with the new build system. ver 4.50: Fix issue with missing manual pages in distribution. Fix issue with the configuration and state directories. Fix issue with creating include directory. Fix dependencies of include file generation. ver 4.49: Add simple test program for basic GAP testing. Add support for confirmation requests to agent example. Add support for full non-recursive build. Add five millisecond delay for Simple Pairing auto-accept. Fix Class of Device setting when InitiallyPowered=false. ver 4.48: Add library function for comparing UUID values. Add support for creating all plugins as builtins. Add support for async handling of service class changes. Add support for source interface to audio IPC. Fix device name settings when device is off or down. Fix issue with enabled SCO server when not necessary. Fix missing D-Bus access policy for CUPS backend. Fix discovery results of CUPS backend. Fix initialization handling of Maemo telephony. ver 4.47: Add support for RFKILL unblock handling. Add support for serial proxy configurations. Add support for caching service class updates. Fix issues with updating SDP service records. Fix usage of limited discoverable mode. Remove deprecated methods and signals for AudioSource. ver 4.46: Add support for A2DP sink role. Fix clearing svc_cache before the adapter is up. Fix various pointer after free usages. Fix various memory leaks. ver 4.45: Fix UDEV_DATADIR fallback if pkg-config fails. Fix adapter cleanup and setup prototypes. Fix double-free with out-of-range devices. Fix inband ring setting to be per-headset. Fix handling of Maemo CSD startup. ver 4.44: Add some missing manual pages. Fix missing number prefix when installing udev rules. Fix program prefix used in Bluetooth udev rules. Fix three-way calling indicator order. Fix downgrade/upgrade of callheld indicator. Fix +CIEV sending when indicator value changes. Fix signal handling for Maemo telephony driver. Fix parsing issues with messages from Maemo CSD. Fix issue with duplicate active calls. ver 4.43: Add support for udev based on-demand startup. Fix verbose error reporting of CUPS backend. Fix various string length issues. Fix issues with Maemo telephony driver. Fix another device setup and temporary flag issue. Fix and update example agent implementation. ver 4.42: Add TI WL1271 to Texas Instruments chip list. Add special udev mode to bluetoothd. Fix regression when there is no agent registered. Fix error return when bonding socket hang up. Fix SCO server socket for HFP handsfree role. Fix shutdown on SCO socket before closing. Fix shutdown on A2DP audio stream channel before closing. Fix issue with asserting on AVDTP reference count bugs. Fix authorization denied issue with certain headsets. Fix AVRCP UNITINFO and SUBUNIT INFO responses. Fix discovery cancel issues in case SDP discovery fails. ver 4.41: Fix pairing even if the ACL gets dropped before successful SDP. Fix regression which caused device to be removed after pairing. Fix HSP record fetching when remote device doesn't support it. Fix SDP discovery canceling when clearing hs->pending. Fix headset never connecting on the first attempt. Fix headset state tracking if bt_search_service() fails. Fix maximum headset connection count check. Fix AVDTP Discover timeout handling. Fix also UI_SET_KEYBIT for the new pause and play key codes. ver 4.40: Add telephony driver for oFono telephony stack. Add support for Dell specific HID proxy switching. Add support for running hid2hci from udev. Add mapping for AVRCP Play and Pause to dedicated key codes. Fix AVRCP keycodes to better match existing X keymap support. Fix various quoting issues within telephony support. Fix memory allocation issue when generating PDUs for SDP. Fix race condition on device removal. Fix non-cancelable issue with CreateDevice method. Fix non-working CancelDiscovery method call. ver 4.39: Add workaround for dealing with unknown inquiry complete. Fix discovering when using software scheduler. Fix wrong NoInputNoOutput IO capability string. Fix race condition with agent during pairing. Fix agent cancellation for security mode 3 acceptor failure. Fix temporary flag removal when device creation fails. Fix hciattach to use ppoll instead of poll. Fix service class update when adapter is down. Fix service classes race condition during startup. Fix release of audio client before freeing the device. ver 4.38: Add support for builtin plugins. Add framework for adapter operations. Add constants for Enhanced Retransmission modes. Fix HCI socket leak in device_remove_bonding. Fix various format string issues. Fix crashes with various free functions. Fix issues with Headset and A2DP drivers to load again. Fix sending AVRCP button released passthrough messages Fix bug which prevent input devices to work after restart. Fix issue with interpretation of UUID-128 as channel. ver 4.37: Add version value for Bluetooth 3.0 devices. Add additional L2CAP extended feature mask bits. Add support for loading plugins in priority order. Add support for more detailed usage of disconnect watches. Add support for AVRCP volume control. Add saturated clipping of SBC decoder output to 16-bit. Fix potentially infinite recursion of adapter_up. Fix SCO handling in the case of an incoming call. Fix input service to use confirm callback. Fix cleanup of temporary device entries from storage. ver 4.36: Add proper tracking of AVCTP connect attempts. Add support to channel pattern in Serial interface. Fix A2DP sink crash if removing device while connecting. Fix error handling if HFP indicators aren't initialized. Fix segfault while handling an incoming SCO connection. Fix Serial.Disconnect to abort connection attempt. ver 4.35: Add support for Handsfree profile headset role. Add additional checks for open SEIDs from clients. Fix device removal while audio IPC client is connected. Fix device removal when an authorization request is pending. Fix incoming AVDTP connect while authorization in progress. Fix disconnection timers for audio support. Fix various potential NULL pointer deferences. Fix callheld indicator value for multiple calls. Fix voice number type usage. Fix GDBus watch handling. ver 4.34: Add support for version checks of plugins. Add support for class property on adapter interface. Add support for second SDP attempt after connection reset. Add support for more detailed audio states. Add support for HFP+A2DP auto connection feature. Add support for new and improved audio IPC. Add program for testing audio IPC interface. Fix various AVDTP qualification related issues. Fix broken SDP AttributeIdList parsing. Fix invalid memory access of SDP URL handling. Fix local class of device race conditions. Fix issue with periodic inquiry on startup. Fix missing temporary devices in some situations. Fix SBC alignment issue for encoding with four subbands. ver 4.33: Add Paired property to the DeviceFound signals. Add support for Headset profile 1.2 version. Fix broken network configuration when IPv6 is disabled. Fix network regression that caused disconnection. Fix SDP truncation of strings with NULL values. Fix service discovery handling of CUPS helper. ver 4.32: Fix broken SDP record handling. Fix SDP data buffer parsing. Fix more SDP memory leaks. Fix read scan enable calls. Fix A2DP stream handling. ver 4.31: Add support for new BtIO helper library. Fix AVDTP session close issue. Fix SDP memory leaks. Fix various uninitialized memory issues. Fix duplicate signal emissions. Fix property changes request handling. Fix class of device storage handling. ver 4.30: Add CID field to L2CAP socket address structure. Fix reset of authentication requirements after bonding. Fix storing of link keys when using dedicated bonding. Fix storing of pre-Bluetooth 2.1 link keys. Fix resetting trust settings on every reboot. Fix handling of local name changes. Fix memory leaks in hciconfig and hcitool ver 4.29: Use AVRCP version 1.0 for now. Decrease AVDTP idle timeout to one second. Delay AVRCP connection when remote device connects A2DP. Add workaround for AVDTP stream setup with broken headsets. Add missing three-way calling feature bit for Handsfree. Fix handsfree callheld indicator updating. Fix parsing of all AT commands within the buffer. Fix authentication replies when disconnected. Fix handling of debug combination keys. Fix handling of changed combination keys. Fix handling of link keys when using no bonding. Fix handling of invalid/unknown authentication requirements. Fix closing of L2CAP raw socket used for dedicated bonding. ver 4.28: Add AVDTP signal fragmentation support. Add more SBC performance optimizations. Add more SBC audio quality improvements. Use native byte order for audio plugins. Set the adapter alias only after checking the EIR data. Fix auto-disconnect issue with explicit A2DP connections. Fix invalid memory access of ALSA plugin. Fix compilation with -Wsign-compare. ver 4.27: Add more SBC optimization (MMX and ARM NEON). Add BT_SECURITY and BT_DEFER_SETUP definitions. Add support for deferred connection setup. Add support for fragmentation of data packets. Add option to trigger dedicated bonding. Follow MITM requirements from remote device. Require MITM for dedicated bonding if capabilities allow it. Fix IO capabilities for non-pairing and pairing cases. Fix no-bonding connections in non-bondable mode. Fix new pairing detection with SSP. Fix bonding with pre-2.1 devices and newer kernels. Fix LIAC setting while toggling Pairable property. Fix device creation for incoming security mode 3 connects. Fix crash within A2DP with bogus pointer. Fix issue with sdp_copy_record() function. Fix crash with extract_des() if sdp_uuid_extract() fails. ver 4.26: Use of constant shift in SBC quantization code. Add possibility to analyze 4 blocks at once in encoder. Fix correct handling of frame sizes in the encoder. Fix for big endian problems in SBC codec. Fix audio client socket to always be non-blocking. Update telephony support for Maemo. ver 4.25: Fix receiving data over the audio control socket. Fix subbands selection for joint-stereo in SBC encoder. Add new SBC analysis filter function. ver 4.24: Fix signal emissions when removing adapters. Fix missing adapter signals on exit. Add support for bringing adapters down on exit. Add support for RememberPowered option. Add support for verbose compiler warnings. Add more options to SBC encoder. ver 4.23: Update audio IPC for better codec handling. Fix bitstream optimization for SBC encoder. Fix length header values of IPC messages. Fix multiple coding style violations. Fix FindDevice to handle temporary devices. Add configuration option for DeviceID. Add support for InitiallyPowered option. Add missing signals for manager properties. Add telephony support for Maemo. ver 4.22: Add deny statements to D-Bus access policy. Add support for LegacyPairing property. Add support for global properties. Add more commands to telephony testing script. Add sender checks for serial and network interfaces. Remove deprecated methods and signals from input interface. Remove deprecated methods and signals from network interface. Remove OffMode option and always use device down. ver 4.21: Fix adapter initialization logic. Fix adapter setup and start security manager early. Fix usage issue with first_init variable. ver 4.20: Cleanup session handling. Cleanup mode setting handling. Fix issue with concurrent audio clients. Fix issue with HFP/HSP suspending. Fix AT result code syntax handling. Add Handsfree support for AT+NREC. Add PairableTimeout adapter property. ver 4.19: Fix installation of manual pages for old daemons. Fix D-Bus signal emmissions for CreateDevice. Fix issues with UUID probing. Fix +BSRF syntax issue. Add Pairable adapter property. Add sdp_copy_record() library function. ver 4.18: Fix release before close issue with RFCOMM TTYs. Fix Connected property on input interface. Fix DeviceFound signals during initial name resolving. Fix service discovery handling. Fix duplicate UUID detection. Fix SBC gain mismatch and decoding handling. Add more options to SBC encoder and decoder. Add special any adapter object for service interface. Add variable prefix to adapter and device object paths. ver 4.17: Fix SBC encoder not writing last frame. Fix missing timer for A2DP suspend. Add more supported devices to hid2hci utility. Add additional functionality to Handsfree support. ver 4.16: Fix wrong parameter usage of watch callbacks. Fix parameters for callback upon path removal. Fix unloading of adapter drivers. ver 4.15: Fix various A2DP state machine issues. Fix some issues with the Handsfree error reporting. Fix format string warnings with recent GCC versions. Remove dependency on GModule. ver 4.14: Fix types of property arrays. Fix potential crash with input devices. Fix PS3 BD remote input event generation. Allow dynamic adapter driver registration. Update udev rules. ver 4.13: Fix service discovery and UUID handling. Fix bonding issues with Simple Pairing. Fix file descriptor misuse of SCO connections. Fix various memory leaks in the device handling. Fix AVCTP disconnect handling. Fix GStreamer modes for MP3 encoding. Add operator selection to Handsfree support. ver 4.12: Fix crash with missing icon value. Fix error checks of HAL plugin. Fix SCO server socket cleanup on exit. Fix memory leaks from DBusPendingCall. Fix handling of pending authorization requests. Fix missing protocol UUIDs in record pattern. ver 4.11: Change SCO server socket into a generic one. Add test script for dummy telephony plugin. Fix uninitialized reply of multiple GetProperties methods. ver 4.10: Fix memory leaks with HAL messages. Add more advanced handsfree features. Add properties to audio, input and network interfaces. Stop device discovery timer on device removal. ver 4.9: Fix signals for Powered and Discoverable properties. Fix handling of Alias and Icon properties. Fix duplicate entries for service UUIDs. ver 4.8: Fix retrieving of formfactor value. Fix retrieving of local and remote extended features. Fix potential NULL pointer dereference during pairing. Fix crash with browsing due to a remotely initated pairing. ver 4.7: Fix pairing and service discovery logic. Fix crashes during suspend and resume. Fix race condition within devdown mode. Add RequestSession and ReleaseSession methods. Add Powered and Discoverable properties. Add Devices property and deprecate ListDevices. Add workaround for a broken carkit from Nokia. ver 4.6: Fix Device ID record handling. Fix service browsing and storage. Fix authentication and encryption for input devices. Fix adapter name initialization. ver 4.5: Fix initialization issue with new adapters. Send HID authentication request without blocking. Hide the verbose SDP debug behind SDP_DEBUG. Add extra UUIDs for service discovery. Add SCO server socket listener. Add authorization support to service plugin. ver 4.4: Add temporary fix for the CUPS compile issue. Add service-api.txt to distribution. Mention the variable prefix of an object path ver 4.3: Add dummy driver for telephony support. Add support for discovery sessions. Add service plugin for external services. Various cleanups. ver 4.2: Avoid memory copies in A2DP write routine. Fix broken logic with Simple Pairing check and old kernels. Allow non-bondable and outgoing SDP without agent. Only remove the bonding for non-temporary devices. Cleanup various unnecessary includes. Make more unexported functions static. Add basic infrastructure for gtk-doc support. ver 4.1: Add 30 seconds timeout to BNEP connection setup phase. Avoid memory copies in A2DP write routine for ALSA. Make sure to include compat/sdp.h in the distribution. ver 4.0: Initial public release. ver 3.36: Add init routines for TI BRF chips. Add extra attributes to the serial port record. Add example record for headset audio gateway record. Use Handsfree version 0x0105 for the gateway role. Fix SDP record registration with specific record handles. Fix BCSP sent/receive handling. Fix various includes for cross-compilation. Allow link mode settings for outgoing connections. Allow bonding during periodic inquiry. ver 3.35: Add two additional company identifiers. Add UUID-128 support for service discovery. Fix usage of friendly names for service discovery. Fix authorization when experiemental is disabled. Fix uninitialized variable in passkey request handling. Enable output of timestamps for l2test and rctest. ver 3.34: Replace various SDP functions with safe versions. Add additional length validation for incoming SDP packets. Use safe function versions for SDP client handling. Fix issue with RemoveDevice during discovery procedure. Fix collect for non-persistent service records. ver 3.33: Add functions for reading and writing the link policy settings. Add definition for authentication requirements. Add support for handling Simple Pairing. Add Simple Pairing support to Agent interface. Add ReleaseMode method to Adapter interface. Add DiscoverServices method to Device interface. Remove obsolete code and cleanup the repository. Move over to use the libgdbus API. Enable PIE by default if supported. ver 3.32: Add OCF constants for synchronous flow control enabling. Add support for switching HID proxy devices from Dell. Add more Bluetooth client/server helper functions. Add support for input service idle timeout option. Fix BNEP reconnection handling. Fix return value for snd_pcm_hw_params() calls. Use upper-case addresses for object paths. Remove HAL support helpers. Remove inotify support. Remove service daemon activation handling. Remove uneeded D-Bus API extension. ver 3.31: Create device object for all pairing cases. Convert authorization to internal function calls. Add initial support for Headset Audio Gateway role. Add generic Bluetooth helper functions for GLib. Fix endiannes handling of connection handles. Don't optimize when debug is enabled. ver 3.30: Convert audio service into a plugin. Convert input service into a plugin. Convert serial service into a plugin. Convert network service into a plugin. Emit old device signals when a property is changed. Fix missing DiscoverDevices and CancelDiscovery methods. Add another company identifier. Add basic support for Bluetooth sessions. Add avinfo utility for AVDTP/A2DP classification. Remove build option for deprecated sdpd binary. ver 3.29: Introduce new D-Bus based API. Add more SBC optimizations. Add support for PS3 remote devices. Fix alignment trap in SDP server. Fix memory leak in sdp_get_uuidseq_attr function. ver 3.28: Add support for MCAP UUIDs. Add support for role switch for audio service. Add disconnect timer for audio service. Add disconnect detection to ALSA plugin. Add more SBC optimizations. Fix alignment issue of SDP server. Remove support for SDP parsing via expat. ver 3.27: Update uinput.h with extra key definitions. Add support for input connect/disconnect callbacks. Add ifdefs around some baud rate definitions. Add another company identifier. Add proper HFP service level connection handling. Add basic headset automatic disconnect support. Add support for new SBC API. Fix SBC decoder noise at high bitpools. Use 32-bit multipliers for further SBC optimization. Check for RFCOMM connection state in SCO connect callback. Make use of parameters selected in ALSA plugin. ver 3.26: Fix compilation issues with UCHAR_MAX, USHRT_MAX and UINT_MAX. Improve handling of different audio transports. Enable services by default and keep old daemons disabled. ver 3.25: Add limited support for Handsfree profile. Add limited support for MPEG12/MP3 codec. Add basic support for UNITINFO and SUBUNITINFO. Add more SBC optimizations. Fix external service (un)registration. Allow GetInfo and GetAddress to fail. ver 3.24: Add definitions for MDP. Add TCP connection support for serial proxy. Add fix for Logitech HID proxy switching. Add missing macros, MIN, MAX, ABS and CLAMP. Add more SBC encoder optimizations. Add initial mechanism to handle headset commands. Fix connecting to handsfree profile headsets. Use proper function for checking signal name. ver 3.23: Fix remote name request handling bug. Fix key search function to honor the mmap area size. Fix Avahi integration of network service. Add new plugin communication for audio service. Enable basic AVRCP support by default. More optimizations to the SBC library. Create common error definitions. ver 3.22: Add missing include file from audio service. Add SBC conformance test utility. Add basic uinput support for AVRCP. Fix L2CAP socket leak in audio service. Fix buffer usage in GStreamer plugin. Fix remote name request event handling. ver 3.21: Add constant for Bluetooth socket options level. Add initial AVRCP support. Add A2DP sink support to GStreamer plugin. Fix interoperability with A2DP suspend. Fix sign error in 8-subband encoder. Fix handling of service classes length size. Store Extended Inquiry Response data information. Publish device id information through EIR. Support higher baud rates for Ericcson based chips. ver 3.20: Fix GStreamer plugin file type detection. Fix potential infinite loop in inotify support. Fix D-Bus signatures for dict handling. Fix issues with service activation. Fix SDP failure handling of audio service. Fix various memory leaks in input service. Add secure device creation method to input service. Add service information methods to serial service. Add config file support to network service. Add scripting capability to network service. Add special on-mode handling. Add optimization for SBC encoder. Add tweaks for D-Bus 1.1.x libraries. Add support for inquiry transmit power level. ver 3.19: Limit range of bitpool announced while in ACP side. Use poll instead of usleep to wait for worker thread. Use default event mask from the specification. Add L2CAP mode constants. Add HID proxy support for Logitech diNovo Edge dongle. Add refresh option to re-request device names. Show correct connection link type. ver 3.18: Don't allocate memory for the Bluetooth base UUID. Implement proper locking for headsets. Fix various A2DP SEP locking issues. Fix and cleanup audio stream handling. Fix stream starting if suspend request is pending. Fix A2DP and AVDTP endianess problems. Add network timeout and retransmission support. Add more detailed decoding of EIR elements. ver 3.17: Fix supported commands bit calculation. Fix crashes in audio and network services. Check PAN source and destination roles. Only export the needed symbols for the plugins. ver 3.16: Update company identifier list. Add support for headsets with SCO audio over HCI. Add support for auto-create through ALSA plugin. Add support for ALSA plugin parameters. Add GStreamer plugin with SBC decoder and encoder. Fix network service NAP, GN and PANU servers. Set EIR information from SDP database. ver 3.15: Add A2DP support to the audio service. Add proxy support to the serial service. Extract main service class for later use. Set service classes value from SDP database. ver 3.14: Add missing signals for the adapter interface. Add definitions and functions for Simple Pairing. Add basic commands for Simple Pairing. Add correct Simple Pairing and EIR interaction. Add missing properties for remote information. Add EPoX endian quirk to the input service. Fix HID descriptor import and storage functions. Fix handling of adapters in raw mode. Fix remote device listing methods. ver 3.13: Fix some issues with the headset support. Fix concurrent pending connection attempts. Fix usage of devname instead of netdev. Add identifier for Nokia SyncML records. Add command for reading the CSR chip revision. Add generic CSR radio test support. Update HCI command table. ver 3.12: Add missing HCI command text descriptions Add missing HCI commands structures. Add missing HCI event structures. Add common bachk() function. Add support for limited discovery mode. Add support for setting of event mask. Add GetRemoteServiceIdentifiers method. Add skeleton for local D-Bus server. Add headset gain control methods. Fix various headset implementation issues. Fix various serial port service issues. Fix various input service issues. Let CUPS plugin discover printers in range. Improve the BCM2035 UART init routine. Ignore connection events for non-ACL links. ver 3.11: Update API documentation. Minimize SDP root records and browse groups. Use same decoder for text and URL strings. Fix URL data size handling. Fix SDP pattern extraction for XML. Fix network connection persistent state. Add network connection helper methods. Add initial version of serial port support. Add class of device tracking. ver 3.10.1: Add option to disable installation of manual pages. Fix input service encryption setup. Fix serial service methods. Fix network service connection handling. Provide a simple init script. ver 3.10: Add initial version of network service. Add initial version of serial service. Add initial version of input service. Add initial version of audio service. Add authorization framework. Add integer based SBC library. Add version code for Bluetooth 2.1 specification. Add ESCO_LINK connection type constant. Export sdp_uuid32_to_uuid128() function. ver 3.9: Add RemoteDeviceDisconnectRequested signal. Add updated service framework. Add embedded GLib library. Add support for using system GLib library. Create internal SDP server library. ver 3.8: Sort discovered devices list based on their RSSI. Send DiscoverableTimeoutChanged signal. Fix local and remote name validity checking. Add ListRemoteDevices and ListRecentRemoteDevices methods. Add basic integration of confirmation concept. Add support for service record description via XML. Add support for external commands to the RFCOMM utility. Add experimental service and authorization API. Add functions for registering binary records. ver 3.7: Fix class of device handling. Fix error replies with pairing and security mode 3. Fix disconnect method for RFCOMM connections. Add match pattern for service searches. Add support for prioritized watches. Add additional PDU length checks. Fix CSRC value for partial responses. ver 3.6.1: Fix IO channel race conditions. Fix pairing issues on big endian systems. Fix pairing issues with page timeout errors. Fix pairing state for security mode 3 requests. Switch to user as default security manager mode. ver 3.6: Update D-Bus based RFCOMM interface support. Use L2CAP raw sockets for HCI connection creation. Add periodic discovery support to the D-Bus interface. Add initial support for device names via EIR. Add proper UTF-8 validation of device names. Add support for the J-Three keyboard. Fix issues with the asynchronous API for SDP. ver 3.5: Fix and cleanup watch functionality. Add support for periodic inquiry mode. Add support for asynchronous SDP requests. Add more request owner tracking. Add asynchronous API for SDP. Document pageto and discovto options. ver 3.4: Improve error reporting for failed HCI commands. Improve handling of CancelBonding. Fixed bonding reply message when disconnected. Fix UUID128 string lookup handling. Fix malloc() versus bt_malloc() usage. ver 3.3: Don't change inquiry mode for Bluetooth 1.1 adapters. Add udev rules for Bluetooth serial PCMCIA cards. Add Cancel and Release methods for passkey agents. Add GetRemoteClass method. Convert to using ppoll() and pselect(). Initialize allocated memory to zero. Remove bcm203x firmware loader. Remove kernel specific timeouts. Add additional private data field for SDP sessions. Add host controller to host flow control defines. Add host number of completed packets defines. Initialize various memory to zero before usage. ver 3.2: Only check for the low-level D-Bus library. Update possible device minor classes. Fix timeout for pending reply. Add more Inquiry with RSSI quirks. Sleep only 100 msecs for device detection. Don't send BondingCreated on link key renewal. Allow storing of all UTF-8 remote device names. Create storage filenames with a generic function. Fix handling of SDP strings. Add adapter type for SDIO cards. Add features bit for link supervision timeout. ver 3.1: Add missing placeholders for feature bits. Fix handling of raw mode devices. Fix busy loop in UUID extraction routine. Remove inquiry mode setting. Remove auth and encrypt settings. ver 3.0: Implement the new BlueZ D-Bus API. Fix broken behavior with EVT_CMD_STATUS. Add features bit for pause encryption. Add additional EIR error code. Add more company identifiers. Add another Phonebook Access identifier. Update sniff subrating data structures. ver 2.25: Use %jx instead of %llx for uint64_t and int64_t. Allow null-terminated text strings. Add UUID for N-Gage games. Add UUID for Apple Macintosh Attributes. Add Apple attributes and iSync records. Add definitions for Apple Agent. Add support for the Handsfree Audio Gateway service. Add support for choosing a specific record handle. Add support for dialup/telephone connections. Add definitions for Apple Agent. Add support for record handle on service registration. ver 2.24: Fix display of SDP text and data strings. Add support for device scan property. Add support for additional access protocols. Update the D-Bus policy configuration file. ver 2.23: Update the new D-Bus interface. Make dfutool ready for big endian architectures. Add support for AVRCP specific service records. Add support for writing complex BCCMD commands. Add the new BCCMD interface utility. Add MicroBCSP implementation from CSR. Add constants and definitions for sniff subrating. Add support for allocation of binary text elements. Add HCI emulation tool. Add fake HID support for old EPoX presenters. Reject connections from unknown HID devices. Fix service discovery deadlocks with Samsung D600 phones. ver 2.22: Remove D-Bus 0.23 support. Add initial version of the new D-Bus interface. Add support for extended inquiry response commands. Add support for the Logitech diNovo Media Desktop Laser. Add compile time buffer checks (FORTIFY SOURCE). Decode reserved LMP feature bits. Fix errno overwrite problems. Fix profile descriptor problem with Samsung phones. ver 2.21: Move create_dirs() and create_file() into the textfile library. Let textfile_put() also replace the last key value pair. Fix memory leaks with textfile_get() usage. Fix infinite loops and false positive matches. Don't retrieve stored link keys for RAW devices. Document the putkey and delkey commands. Show supported commands also in clear text. Support volatile changes of the BD_ADDR for CSR chips. Add support for identification of supported commands. Add missing OCF declarations for the security filter. Add two new company identifiers. ver 2.20: Add UUIDs for video distribution profile. Add UUIDs for phonebook access profile. Add attribute identifier for supported repositories. Add definitions for extended inquiry response. Add functions for extended inquiry response. Add support for extended inquiry response. Add support for HotSync service record. Add support for ActiveSync service record. Add ActiveSync networking support. Fix D-Bus crashes with new API versions. ver 2.19: Fix the GCC 4.0 warnings. Fix the routing for dealing with raw devices. Fix off by one memory allocation error. Fix security problem with escape characters in device name. Add per device service record functions. Send D-Bus signals for inquiry results and remote name resolves. Add support for device specific SDP records. ver 2.18: Support D-Bus 0.23 and 0.33 API versions. Support reading of complex BCCMD values. Support minimum and maximum encryption key length. Add support for reading and writing the inquiry scan type. Add definitions for connection accept timeout and scan enable. Add support for inquiry scan type. Add tool for the CSR BCCMD interface. Add first draft of the Audio/Video control utility. Add disconnect timer support for the A2DP ALSA plugin. Make SBC parameters configurable. Replace non-printable characters in device names. Remove hci_vhci.h header file. Remove hci_uart.h header file. ver 2.17: Set the storage directory through ${localstatedir}. Add the textfile library for ASCII based file access. Add support for return link keys event. Add support for voice setting configuration. Add support for page scan timeout configuration. Add support for storing and deleting of stored link keys. Add support for searching for services with UUID-128. Add support for retrieving all possible service records. Add support for a raw mode view of service records. Add support for HID information caching in hidd. Add support for authentication in pand and dund. Add support for changing BD_ADDR of CSR chips. Add pskey utility for changing CSR persistent storage values. Add the firmware upgrade utility. Add connection caching for the A2DP ALSA plugin. Add functions for stored link keys. Add definitions for PIN type and unit key. Add SDP_WAIT_ON_CLOSE flag for sdp_connect(). Include stdio.h in bluetooth.h header file. Include sys/socket.h in the header files. ver 2.16: Store link keys in ASCII based file format. Support device name caching. Support zero length data sizes in l2test. Change default l2ping data size to 44 bytes. Hide the server record and the public browse group root. Read BD_ADDR if not set and if it is a raw device. Add SDP language attributes. Add support for browsing the L2CAP group. Add support for stored pin codes for outgoing connections. Add support for local commands and extended features. Add support for reading CSR panic and fault codes. Add config option for setting the inquiry mode. Add OUI decoding support. Use unlimited inquiry responses as default. Use cached device names for PIN request. Use the clock offset when getting the remote names. Add function for reading local supported commands. Add function for reading local extended features. Add function for reading remote extended features. Add function for getting the remote name with a clock offset. Add function for extracting the OUI from a BD_ADDR. Add inquiry info structure with RSSI and page scan mode. Fix buffer allocation for features to string conversion. Support inquiry with unlimited number of responses. ver 2.15: Enable the RFCOMM service level security. Add deprecated functions for reading the name. Add command for reading the clock offset. Add command for reading the clock. Add function for reading the clock. Add function for reading the local Bluetooth address. Add function for reading the local supported features. Don't configure raw devices. Don't set inquiry scan or page scan on raw devices. Don't show extended information for raw devices. Support L2CAP signal sizes bigger than 2048 bytes. Cleanup of the socket handling code of the test programs. Use better way for unaligned access. Remove sdp_internal.h and its usage. ver 2.14: Make use of additional connection information. Use library function for reading the RSSI. Use library function for reading the link quality. Use library function for reading the transmit power level. Use library functions for the link supervision timeout. Add tool for changing the device address. Add function for reading the RSSI. Add function for reading the link quality. Add function for reading the transmit power level. Add functions for the link supervision timeout. Remove deprecated functions. Update AM_PATH_BLUEZ macro. ver 2.13: Use file permission 0600 for the link key file. Add support for HID attribute descriptions. Add support for Device ID attributes. Add Device ID and HID attribute definitions. Update the UUID constants and its translations. Update L2CAP socket option definitions. Update connection information definitions. Various whitespace cleanups. ver 2.12: Inherit the device specific options from the default. Use --device for selecting the source device. Add --nosdp option for devices with resource limitation. Add support and parameter option for secure mode. Add a lot of build ids and hardware revisions. Add service classes and profile ids for WAP. Add simple AM_PATH_BLUEZ macro. Update UUID translation tables. Correct kernel interface for CMTP and HIDP support. ver 2.11: Initial support for the kernel security manager. Various cleanups to avoid inclusion of kernel headers. Fix output when the CUPS backend is called without arguments. Fix problems with a 64 bit userland. Use Bluetooth library functions if available. Use standard numbering scheme of SDP record handles. Use bit zero for vendor packets in the filter type bitmask. Add SIM Access types for service discovery. Add more audio/video profile translations. Add another company identifier. Add the missing HCI error codes. Add RFCOMM socket options. Add definition for the SECURE link mode. Add functions for reading and writing the inquiry mode. Add functions for AFH related settings and information. Add version identifier for the Bluetooth 2.0 specification. Add a master option to the hidd. Add support for changing the link key of a connection. Add support for requesting encryption on keyboards. Add support for revision information of Digianswer devices. Add support for the Zoom, IBM and TDK PCMCIA cards. Add checks for the OpenOBEX and the ALSA libraries. Add experimental mRouter support. ver 2.10: Use a define for the configuration directory. Fix string initialization for flags translation. Fix and extend the unaligned access macros. Make compiling with debug information optional. Don't override CFLAGS from configure. Check for usb_get_busses() and usb_interrupt_read(). Add optional support for compiling with PIE. Make installation of the init scripts optional. Make compiling with debug information optional. Don't override CFLAGS from configure. ver 2.9: Retry SDP connect if busy in the CUPS backend. Use packet type and allow role switch in hcitool. Use the functions from the USB library for hid2hci. Add Broadcom firmware loader. Add EPoX endian quirk for buggy keyboards. Add L2CAP info type and info result definitions. Add value for L2CAP_CONF_RFC_MODE. Change RSSI value to signed instead of unsigned. Allow UUID32 values as protocol identifiers. Update the autoconf/automake scripts. ver 2.8: Use LIBS and LDADD instead of LDFLAGS. Use HIDP subclass field for HID boot protocol. Set olen before calling getsockopt() in pand. Restore signals for dev-up script. Add PID file support for pand. Add size parameter to expand_name() in hcid. Add support for audio source and audio sink SDP records. Add support for HID virtual cable unplug. Add support for AmbiCom BT2000C card. Add defines and UUID's for audio/video profiles. Add AVDTP protocol identifier. Add HIDP subclass field. Add PKGConfig support. Fix the event code of inquiry with RSSI. Remove dummy SDP library. ver 2.7: Fix display of decoded LMP features. Update company identifiers. Add AFH related types. Add first bits from EDR prototyping specification. Add support for inquiry with RSSI. Add HCRP related SDP functions. Add HIDP header file. Add support for getting the AFH channel map. Add support for AFH mode. Add support for inquiry mode. Add Bluetooth backend for CUPS. Add the hid2hci utility. Add the hidd utility. Add the pand utility. Add the dund utility. More endian bug fixes. Give udev some time to create the RFCOMM device nodes. Release the TTY if no device node is found. New startup script for the Bluetooth subsystem. Update to the autoconf stuff. ver 2.6: Change default prefix to /usr. Add manpages for hcid and hcid.conf. Add the sdpd server daemon. Add the sdptool utility. Add the ciptool utility. Add new company identifiers. Add BNEP and CMTP header files. Add the SDP library. Use R2 for default value of pscan_rep_mode. ver 2.5: Add decoding of Bluetooth 1.2 features. Add link manager version parameter for Bluetooth 1.2. Add new company identifiers. Add D-Bus support for PIN request. Support for transmit power level. Support for park, sniff and hold mode. Support for role switch. Support for reading the clock offset. Support for requesting authentication. Support for setting connection encryption. Show revision information for Broadcom devices. Replace unprintable characters in device name. Use R1 for default value of pscan_rep_mode. Fix some 64-bit problems. Fix some endian problems. Report an error on PIN helper failure. Update bluepin script for GTK2. ver 2.4: Increase number of inquiry responses. Support for transmit power level. Display all 8 bytes of the features. Add support for reading and writing of IAC. Correct decoding class of device. Use Ericsson revision command for ST Microelectronics devices. Display AVM firmware version with 'revision' command. New code for CSR specific revision information. Support for ST Microelectronics specific initialization. Support for 3Com card version 3.0. Support for TDK, IBM and Socket cards. Support for initial baud rate. Update man pages. Fixes for some memory leaks. ver 2.3: Added const qualifiers to appropriate function arguments. Minor fixes. CSR firmware version is now displayed by 'revision' command. Voice command is working properly on big endian machines. Added support for Texas Bluetooth modules. Added support for high UART baud rates on Ericsson modules. BCSP initialization fixes. Support for role switch command (hcitool). RFCOMM config file parser fixes. Update man pages. Removed GLib dependency. ver 2.2: Updated RFCOMM header file. Additional HCI command and event defines. Support for voice settings (hciconfig). Minor hcitool fixes. Improved configure script. Added Headset testing tool. Updated man pages. RPM package. ver 2.1.1: Resurrect hci_remote_name. ver 2.1: Added hci_{read, write}_class_of_dev(). Added hci_{read, write}_current_iac_lap(). Added hci_write_local_name(). Added RFCOMM header file. Minor fixes. Improved BCSP initialization (hciattach). Support for displaying link quality (hcitool). Support for changing link supervision timeout (hcitool). New RFCOMM TTY configuration tool (rfcomm). Minor fixes and updates. ver 2.0: Additional company IDs. BCSP initialization (hciattach). Minor hciconfig fixes. ver 2.0-pr13: Support for multiple pairing modes. Link key database handling fixes. ver 2.0-pre12: Removed max link key limit. Keys never expire. Link key database is always updated. Reread PIN on SIGHUP (hcid). Bluetooth script starts SDPd, if installed. Other minor fixes. ver 2.0-pre11: Improved link key management and more verbose logging (hcid). Fixed scan command (hcitool). ver 2.0-pre10: Fix hci_inquiry function to return errors and accept user buffers. New functions hci_devba, hci_devid, hci_for_each_dev and hci_get_route. Additional company IDs. Makefile and other minor fixes. Support for reading RSSI, remote name and changing connection type (hcitool). Device initialization fixes (hcid). Other minor fixes and improvements. Build environment cleanup and fixes. ver 2.0-pre9: Improved bluepin. Working X authentication. Improved hcitool. New flexible cmd syntax, additional commands. Human readable display of the device features. LMP features to string translation support. Additional HCI command and event defines. Extended hci_filter API. ver 2.0-pre8: Additional HCI ioctls and defines. All strings and buffers are allocated dynamically. ba2str, str2ba automatically swap bdaddress. Additional hciconfig commands. Support for ACL and SCO MTU ioctls. Support for Inventel and COM1 UART based devices. Minor hcitool fixes. Improved l2test. New L2CAP test modes. Minor fixes and cleanup. ver 2.0-pre7: Bluetooth libraries and header files is now a separate package. New build environment uses automake and libtool. Massive header files cleanup. Bluetooth utilities is now a separate package. New build environment uses automake. Moved all config files and security data to /etc/bluetooth. Various cleanups. ver 2.0-pre6: API cleanup and additions. Improved hcitool. l2test minor output fixes. hciattach opt to display list of supported devices. ver 2.0-pre4: HCI filter enhancements. ver 2.0-pre3: Cleanup. ver 2.0-pre2: Additional HCI library functions. Improved CSR baud rate initialization. PCMCIA scripts fixes and enhancements. Documentation update. ver 2.0-pre1: New UART initialization utility. Hot plugging support for UART based PCMCIA devices. SCO testing utility. New authentication utility (bluepin). Minor fixes and improvements. bluez-5.82/PaxHeaders/gdbus0000644000000000000000000000005014773213555012711 xustar0020 atime=1743591291 20 ctime=1743591277 bluez-5.82/gdbus/0000755000000000000000000000000014773213555012447 5ustar00rootrootbluez-5.82/gdbus/PaxHeaders/polkit.c0000644000000000000000000000005014015011623014411 xustar0020 atime=1743515854 20 ctime=1743591277 bluez-5.82/gdbus/polkit.c0000644000000000000000000001134514015011623014076 0ustar00rootroot// SPDX-License-Identifier: GPL-2.0-or-later /* * * D-Bus helper library * * Copyright (C) 2004-2011 Marcel Holtmann * * */ #ifdef HAVE_CONFIG_H #include #endif #include #include #include int polkit_check_authorization(DBusConnection *conn, const char *action, gboolean interaction, void (*function) (dbus_bool_t authorized, void *user_data), void *user_data, int timeout); static void add_dict_with_string_value(DBusMessageIter *iter, const char *key, const char *str) { DBusMessageIter dict, entry, value; dbus_message_iter_open_container(iter, DBUS_TYPE_ARRAY, DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING DBUS_TYPE_STRING_AS_STRING DBUS_TYPE_VARIANT_AS_STRING DBUS_DICT_ENTRY_END_CHAR_AS_STRING, &dict); dbus_message_iter_open_container(&dict, DBUS_TYPE_DICT_ENTRY, NULL, &entry); dbus_message_iter_append_basic(&entry, DBUS_TYPE_STRING, &key); dbus_message_iter_open_container(&entry, DBUS_TYPE_VARIANT, DBUS_TYPE_STRING_AS_STRING, &value); dbus_message_iter_append_basic(&value, DBUS_TYPE_STRING, &str); dbus_message_iter_close_container(&entry, &value); dbus_message_iter_close_container(&dict, &entry); dbus_message_iter_close_container(iter, &dict); } static void add_empty_string_dict(DBusMessageIter *iter) { DBusMessageIter dict; dbus_message_iter_open_container(iter, DBUS_TYPE_ARRAY, DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING DBUS_TYPE_STRING_AS_STRING DBUS_TYPE_STRING_AS_STRING DBUS_DICT_ENTRY_END_CHAR_AS_STRING, &dict); dbus_message_iter_close_container(iter, &dict); } static void add_arguments(DBusConnection *conn, DBusMessageIter *iter, const char *action, dbus_uint32_t flags) { const char *busname = dbus_bus_get_unique_name(conn); const char *kind = "system-bus-name"; const char *cancel = ""; DBusMessageIter subject; dbus_message_iter_open_container(iter, DBUS_TYPE_STRUCT, NULL, &subject); dbus_message_iter_append_basic(&subject, DBUS_TYPE_STRING, &kind); add_dict_with_string_value(&subject, "name", busname); dbus_message_iter_close_container(iter, &subject); dbus_message_iter_append_basic(iter, DBUS_TYPE_STRING, &action); add_empty_string_dict(iter); dbus_message_iter_append_basic(iter, DBUS_TYPE_UINT32, &flags); dbus_message_iter_append_basic(iter, DBUS_TYPE_STRING, &cancel); } static dbus_bool_t parse_result(DBusMessageIter *iter) { DBusMessageIter result; dbus_bool_t authorized, challenge; dbus_message_iter_recurse(iter, &result); dbus_message_iter_get_basic(&result, &authorized); dbus_message_iter_get_basic(&result, &challenge); return authorized; } struct authorization_data { void (*function) (dbus_bool_t authorized, void *user_data); void *user_data; }; static void authorization_reply(DBusPendingCall *call, void *user_data) { struct authorization_data *data = user_data; DBusMessage *reply; DBusMessageIter iter; dbus_bool_t authorized = FALSE; reply = dbus_pending_call_steal_reply(call); if (dbus_message_get_type(reply) == DBUS_MESSAGE_TYPE_ERROR) goto done; if (dbus_message_has_signature(reply, "(bba{ss})") == FALSE) goto done; dbus_message_iter_init(reply, &iter); authorized = parse_result(&iter); done: if (data->function != NULL) data->function(authorized, data->user_data); dbus_message_unref(reply); dbus_pending_call_unref(call); } #define AUTHORITY_DBUS "org.freedesktop.PolicyKit1" #define AUTHORITY_INTF "org.freedesktop.PolicyKit1.Authority" #define AUTHORITY_PATH "/org/freedesktop/PolicyKit1/Authority" int polkit_check_authorization(DBusConnection *conn, const char *action, gboolean interaction, void (*function) (dbus_bool_t authorized, void *user_data), void *user_data, int timeout) { struct authorization_data *data; DBusMessage *msg; DBusMessageIter iter; DBusPendingCall *call; dbus_uint32_t flags = 0x00000000; if (conn == NULL) return -EINVAL; data = dbus_malloc0(sizeof(*data)); if (data == NULL) return -ENOMEM; msg = dbus_message_new_method_call(AUTHORITY_DBUS, AUTHORITY_PATH, AUTHORITY_INTF, "CheckAuthorization"); if (msg == NULL) { dbus_free(data); return -ENOMEM; } if (interaction == TRUE) flags |= 0x00000001; if (action == NULL) action = "org.freedesktop.policykit.exec"; dbus_message_iter_init_append(msg, &iter); add_arguments(conn, &iter, action, flags); if (dbus_connection_send_with_reply(conn, msg, &call, timeout) == FALSE) { dbus_message_unref(msg); dbus_free(data); return -EIO; } if (call == NULL) { dbus_message_unref(msg); dbus_free(data); return -EIO; } data->function = function; data->user_data = user_data; dbus_pending_call_set_notify(call, authorization_reply, data, dbus_free); dbus_message_unref(msg); return 0; } bluez-5.82/gdbus/PaxHeaders/gdbus.h0000644000000000000000000000005014711225433014231 xustar0020 atime=1743515768 20 ctime=1743591277 bluez-5.82/gdbus/gdbus.h0000644000000000000000000003532014711225433013715 0ustar00rootroot/* SPDX-License-Identifier: GPL-2.0-or-later */ /* * * D-Bus helper library * * Copyright (C) 2004-2011 Marcel Holtmann * Copyright 2024 NXP * * */ #ifndef __GDBUS_H #define __GDBUS_H #ifdef __cplusplus extern "C" { #endif #include #include typedef struct GDBusArgInfo GDBusArgInfo; typedef struct GDBusMethodTable GDBusMethodTable; typedef struct GDBusSignalTable GDBusSignalTable; typedef struct GDBusPropertyTable GDBusPropertyTable; typedef struct GDBusSecurityTable GDBusSecurityTable; typedef void (* GDBusWatchFunction) (DBusConnection *connection, void *user_data); typedef void (* GDBusMessageFunction) (DBusConnection *connection, DBusMessage *message, void *user_data); typedef gboolean (* GDBusSignalFunction) (DBusConnection *connection, DBusMessage *message, void *user_data); DBusConnection *g_dbus_setup_bus(DBusBusType type, const char *name, DBusError *error); DBusConnection *g_dbus_setup_private(DBusBusType type, const char *name, DBusError *error); gboolean g_dbus_request_name(DBusConnection *connection, const char *name, DBusError *error); gboolean g_dbus_set_disconnect_function(DBusConnection *connection, GDBusWatchFunction function, void *user_data, DBusFreeFunction destroy); typedef void (* GDBusDestroyFunction) (void *user_data); typedef DBusMessage * (* GDBusMethodFunction) (DBusConnection *connection, DBusMessage *message, void *user_data); typedef gboolean (*GDBusPropertyGetter)(const GDBusPropertyTable *property, DBusMessageIter *iter, void *data); typedef guint32 GDBusPendingPropertySet; typedef void (*GDBusPropertySetter)(const GDBusPropertyTable *property, DBusMessageIter *value, GDBusPendingPropertySet id, void *data); typedef gboolean (*GDBusPropertyExists)(const GDBusPropertyTable *property, void *data); typedef guint32 GDBusPendingReply; typedef void (* GDBusSecurityFunction) (DBusConnection *connection, const char *action, gboolean interaction, GDBusPendingReply pending); enum GDBusFlags { G_DBUS_FLAG_ENABLE_EXPERIMENTAL = (1 << 0), G_DBUS_FLAG_ENABLE_TESTING = (1 << 1), }; enum GDBusMethodFlags { G_DBUS_METHOD_FLAG_DEPRECATED = (1 << 0), G_DBUS_METHOD_FLAG_NOREPLY = (1 << 1), G_DBUS_METHOD_FLAG_ASYNC = (1 << 2), G_DBUS_METHOD_FLAG_EXPERIMENTAL = (1 << 3), G_DBUS_METHOD_FLAG_TESTING = (1 << 4), }; enum GDBusSignalFlags { G_DBUS_SIGNAL_FLAG_DEPRECATED = (1 << 0), G_DBUS_SIGNAL_FLAG_EXPERIMENTAL = (1 << 1), G_DBUS_SIGNAL_FLAG_TESTING = (1 << 2), }; enum GDBusPropertyFlags { G_DBUS_PROPERTY_FLAG_DEPRECATED = (1 << 0), G_DBUS_PROPERTY_FLAG_EXPERIMENTAL = (1 << 1), G_DBUS_PROPERTY_FLAG_TESTING = (1 << 2), }; enum GDBusSecurityFlags { G_DBUS_SECURITY_FLAG_DEPRECATED = (1 << 0), G_DBUS_SECURITY_FLAG_BUILTIN = (1 << 1), G_DBUS_SECURITY_FLAG_ALLOW_INTERACTION = (1 << 2), }; enum GDbusPropertyChangedFlags { G_DBUS_PROPERTY_CHANGED_FLAG_FLUSH = (1 << 0), }; typedef enum GDBusMethodFlags GDBusMethodFlags; typedef enum GDBusSignalFlags GDBusSignalFlags; typedef enum GDBusPropertyFlags GDBusPropertyFlags; typedef enum GDBusSecurityFlags GDBusSecurityFlags; typedef enum GDbusPropertyChangedFlags GDbusPropertyChangedFlags; struct GDBusArgInfo { const char *name; const char *signature; }; struct GDBusMethodTable { const char *name; GDBusMethodFunction function; GDBusMethodFlags flags; unsigned int privilege; const GDBusArgInfo *in_args; const GDBusArgInfo *out_args; }; struct GDBusSignalTable { const char *name; GDBusSignalFlags flags; const GDBusArgInfo *args; }; struct GDBusPropertyTable { const char *name; const char *type; GDBusPropertyGetter get; GDBusPropertySetter set; GDBusPropertyExists exists; GDBusPropertyFlags flags; }; struct GDBusSecurityTable { unsigned int privilege; const char *action; GDBusSecurityFlags flags; GDBusSecurityFunction function; }; #define GDBUS_ARGS(args...) (const GDBusArgInfo[]) { args, { } } #define GDBUS_METHOD(_name, _in_args, _out_args, _function) \ .name = _name, \ .in_args = _in_args, \ .out_args = _out_args, \ .function = _function #define GDBUS_ASYNC_METHOD(_name, _in_args, _out_args, _function) \ .name = _name, \ .in_args = _in_args, \ .out_args = _out_args, \ .function = _function, \ .flags = G_DBUS_METHOD_FLAG_ASYNC #define GDBUS_DEPRECATED_METHOD(_name, _in_args, _out_args, _function) \ .name = _name, \ .in_args = _in_args, \ .out_args = _out_args, \ .function = _function, \ .flags = G_DBUS_METHOD_FLAG_DEPRECATED #define GDBUS_DEPRECATED_ASYNC_METHOD(_name, _in_args, _out_args, _function) \ .name = _name, \ .in_args = _in_args, \ .out_args = _out_args, \ .function = _function, \ .flags = G_DBUS_METHOD_FLAG_ASYNC | G_DBUS_METHOD_FLAG_DEPRECATED #define GDBUS_EXPERIMENTAL_METHOD(_name, _in_args, _out_args, _function) \ .name = _name, \ .in_args = _in_args, \ .out_args = _out_args, \ .function = _function, \ .flags = G_DBUS_METHOD_FLAG_EXPERIMENTAL #define GDBUS_EXPERIMENTAL_ASYNC_METHOD(_name, _in_args, _out_args, _function) \ .name = _name, \ .in_args = _in_args, \ .out_args = _out_args, \ .function = _function, \ .flags = G_DBUS_METHOD_FLAG_ASYNC | G_DBUS_METHOD_FLAG_EXPERIMENTAL #define GDBUS_TESTING_METHOD(_name, _in_args, _out_args, _function) \ .name = _name, \ .in_args = _in_args, \ .out_args = _out_args, \ .function = _function, \ .flags = G_DBUS_METHOD_FLAG_TESTING #define GDBUS_TESTING_ASYNC_METHOD(_name, _in_args, _out_args, _function) \ .name = _name, \ .in_args = _in_args, \ .out_args = _out_args, \ .function = _function, \ .flags = G_DBUS_METHOD_FLAG_ASYNC | G_DBUS_METHOD_FLAG_TESTING #define GDBUS_NOREPLY_METHOD(_name, _in_args, _out_args, _function) \ .name = _name, \ .in_args = _in_args, \ .out_args = _out_args, \ .function = _function, \ .flags = G_DBUS_METHOD_FLAG_NOREPLY #define GDBUS_SIGNAL(_name, _args) \ .name = _name, \ .args = _args #define GDBUS_DEPRECATED_SIGNAL(_name, _args) \ .name = _name, \ .args = _args, \ .flags = G_DBUS_SIGNAL_FLAG_DEPRECATED #define GDBUS_EXPERIMENTAL_SIGNAL(_name, _args) \ .name = _name, \ .args = _args, \ .flags = G_DBUS_SIGNAL_FLAG_EXPERIMENTAL #define GDBUS_TESTING_SIGNAL(_name, _args) \ .name = _name, \ .args = _args, \ .flags = G_DBUS_SIGNAL_FLAG_EXPERIMENTAL void g_dbus_set_flags(int flags); int g_dbus_get_flags(void); typedef void (*g_dbus_destroy_func_t)(void *user_data); typedef void (*g_dbus_debug_func_t)(const char *str, void *user_data); void g_dbus_set_debug(g_dbus_debug_func_t cb, void *user_data, g_dbus_destroy_func_t destroy); gboolean g_dbus_register_interface(DBusConnection *connection, const char *path, const char *name, const GDBusMethodTable *methods, const GDBusSignalTable *signals, const GDBusPropertyTable *properties, void *user_data, GDBusDestroyFunction destroy); gboolean g_dbus_unregister_interface(DBusConnection *connection, const char *path, const char *name); gboolean g_dbus_register_security(const GDBusSecurityTable *security); gboolean g_dbus_unregister_security(const GDBusSecurityTable *security); void g_dbus_pending_success(DBusConnection *connection, GDBusPendingReply pending); void g_dbus_pending_error(DBusConnection *connection, GDBusPendingReply pending, const char *name, const char *format, ...) __attribute__((format(printf, 4, 5))); void g_dbus_pending_error_valist(DBusConnection *connection, GDBusPendingReply pending, const char *name, const char *format, va_list args); DBusMessage *g_dbus_create_error(DBusMessage *message, const char *name, const char *format, ...) __attribute__((format(printf, 3, 4))); DBusMessage *g_dbus_create_error_valist(DBusMessage *message, const char *name, const char *format, va_list args); DBusMessage *g_dbus_create_reply(DBusMessage *message, int type, ...); DBusMessage *g_dbus_create_reply_valist(DBusMessage *message, int type, va_list args); gboolean g_dbus_send_message(DBusConnection *connection, DBusMessage *message); gboolean g_dbus_send_message_with_reply(DBusConnection *connection, DBusMessage *message, DBusPendingCall **call, int timeout); gboolean g_dbus_send_error(DBusConnection *connection, DBusMessage *message, const char *name, const char *format, ...) __attribute__((format(printf, 4, 5))); gboolean g_dbus_send_error_valist(DBusConnection *connection, DBusMessage *message, const char *name, const char *format, va_list args); gboolean g_dbus_send_reply(DBusConnection *connection, DBusMessage *message, int type, ...); gboolean g_dbus_send_reply_valist(DBusConnection *connection, DBusMessage *message, int type, va_list args); gboolean g_dbus_emit_signal(DBusConnection *connection, const char *path, const char *interface, const char *name, int type, ...); gboolean g_dbus_emit_signal_valist(DBusConnection *connection, const char *path, const char *interface, const char *name, int type, va_list args); guint g_dbus_add_service_watch(DBusConnection *connection, const char *name, GDBusWatchFunction connect, GDBusWatchFunction disconnect, void *user_data, GDBusDestroyFunction destroy); guint g_dbus_add_disconnect_watch(DBusConnection *connection, const char *name, GDBusWatchFunction function, void *user_data, GDBusDestroyFunction destroy); guint g_dbus_add_signal_watch(DBusConnection *connection, const char *sender, const char *path, const char *interface, const char *member, GDBusSignalFunction function, void *user_data, GDBusDestroyFunction destroy); guint g_dbus_add_properties_watch(DBusConnection *connection, const char *sender, const char *path, const char *interface, GDBusSignalFunction function, void *user_data, GDBusDestroyFunction destroy); gboolean g_dbus_remove_watch(DBusConnection *connection, guint tag); void g_dbus_remove_all_watches(DBusConnection *connection); const char *g_dbus_pending_property_get_sender(GDBusPendingPropertySet id); void g_dbus_pending_property_success(GDBusPendingPropertySet id); void g_dbus_pending_property_error_valist(GDBusPendingReply id, const char *name, const char *format, va_list args); void g_dbus_pending_property_error(GDBusPendingReply id, const char *name, const char *format, ...); /* * Note that when multiple properties for a given object path are changed * in the same mainloop iteration, they will be grouped with the last * property changed. If this behaviour is undesired, use * g_dbus_emit_property_changed_full() with the * G_DBUS_PROPERTY_CHANGED_FLAG_FLUSH flag, causing the signal to ignore * any grouping. */ void g_dbus_emit_property_changed(DBusConnection *connection, const char *path, const char *interface, const char *name); void g_dbus_emit_property_changed_full(DBusConnection *connection, const char *path, const char *interface, const char *name, GDbusPropertyChangedFlags flags); gboolean g_dbus_get_properties(DBusConnection *connection, const char *path, const char *interface, DBusMessageIter *iter); gboolean g_dbus_attach_object_manager(DBusConnection *connection); gboolean g_dbus_detach_object_manager(DBusConnection *connection); typedef struct GDBusClient GDBusClient; typedef struct GDBusProxy GDBusProxy; GDBusProxy *g_dbus_proxy_new(GDBusClient *client, const char *path, const char *interface); GDBusProxy *g_dbus_proxy_ref(GDBusProxy *proxy); void g_dbus_proxy_unref(GDBusProxy *proxy); const char *g_dbus_proxy_get_path(const GDBusProxy *proxy); const char *g_dbus_proxy_get_interface(GDBusProxy *proxy); gboolean g_dbus_proxy_get_property(GDBusProxy *proxy, const char *name, DBusMessageIter *iter); GDBusProxy *g_dbus_proxy_lookup(GList *list, int *index, const char *path, const char *interface); char *g_dbus_proxy_path_lookup(GList *list, int *index, const char *path); gboolean g_dbus_proxy_refresh_property(GDBusProxy *proxy, const char *name); typedef void (* GDBusResultFunction) (const DBusError *error, void *user_data); gboolean g_dbus_proxy_set_property_basic(GDBusProxy *proxy, const char *name, int type, const void *value, GDBusResultFunction function, void *user_data, GDBusDestroyFunction destroy); gboolean g_dbus_proxy_set_property_dict(GDBusProxy *proxy, const char *name, GDBusResultFunction function, void *user_data, GDBusDestroyFunction destroy, char *entry, ...); gboolean g_dbus_proxy_set_property_array(GDBusProxy *proxy, const char *name, int type, const void *value, size_t size, GDBusResultFunction function, void *user_data, GDBusDestroyFunction destroy); void g_dbus_dict_append_entry(DBusMessageIter *dict, const char *key, int type, void *val); void g_dbus_dict_append_basic_array(DBusMessageIter *dict, int key_type, const void *key, int type, void *val, int n_elements); void g_dbus_dict_append_array(DBusMessageIter *dict, const char *key, int type, void *val, int n_elements); typedef void (* GDBusSetupFunction) (DBusMessageIter *iter, void *user_data); typedef void (* GDBusReturnFunction) (DBusMessage *message, void *user_data); gboolean g_dbus_proxy_method_call(GDBusProxy *proxy, const char *method, GDBusSetupFunction setup, GDBusReturnFunction function, void *user_data, GDBusDestroyFunction destroy); typedef void (* GDBusClientFunction) (GDBusClient *client, void *user_data); typedef void (* GDBusProxyFunction) (GDBusProxy *proxy, void *user_data); typedef void (* GDBusPropertyFunction) (GDBusProxy *proxy, const char *name, DBusMessageIter *iter, void *user_data); gboolean g_dbus_proxy_set_property_watch(GDBusProxy *proxy, GDBusPropertyFunction function, void *user_data); gboolean g_dbus_proxy_set_removed_watch(GDBusProxy *proxy, GDBusProxyFunction destroy, void *user_data); GDBusClient *g_dbus_client_new(DBusConnection *connection, const char *service, const char *path); GDBusClient *g_dbus_client_new_full(DBusConnection *connection, const char *service, const char *path, const char *root_path); GDBusClient *g_dbus_client_ref(GDBusClient *client); void g_dbus_client_unref(GDBusClient *client); gboolean g_dbus_client_set_connect_watch(GDBusClient *client, GDBusWatchFunction function, void *user_data); gboolean g_dbus_client_set_disconnect_watch(GDBusClient *client, GDBusWatchFunction function, void *user_data); gboolean g_dbus_client_set_signal_watch(GDBusClient *client, GDBusMessageFunction function, void *user_data); gboolean g_dbus_client_set_ready_watch(GDBusClient *client, GDBusClientFunction ready, void *user_data); gboolean g_dbus_client_set_proxy_handlers(GDBusClient *client, GDBusProxyFunction proxy_added, GDBusProxyFunction proxy_removed, GDBusPropertyFunction property_changed, void *user_data); #ifdef __cplusplus } #endif #endif /* __GDBUS_H */ bluez-5.82/gdbus/PaxHeaders/object.c0000644000000000000000000000005014773211264014373 xustar0020 atime=1743590089 20 ctime=1743591277 bluez-5.82/gdbus/object.c0000644000000000000000000013733714773211264014072 0ustar00rootroot// SPDX-License-Identifier: GPL-2.0-or-later /* * * D-Bus helper library * * Copyright (C) 2004-2011 Marcel Holtmann * * */ #ifdef HAVE_CONFIG_H #include #endif #include #include #include #include #include #include "gdbus.h" /* define MAX_INPUT for musl */ #ifndef MAX_INPUT #define MAX_INPUT _POSIX_MAX_INPUT #endif #include "src/shared/util.h" #define info(fmt...) #define error(fmt...) #define debug(fmt...) #define DBUS_INTERFACE_OBJECT_MANAGER "org.freedesktop.DBus.ObjectManager" #ifndef DBUS_ERROR_UNKNOWN_PROPERTY #define DBUS_ERROR_UNKNOWN_PROPERTY "org.freedesktop.DBus.Error.UnknownProperty" #endif #ifndef DBUS_ERROR_PROPERTY_READ_ONLY #define DBUS_ERROR_PROPERTY_READ_ONLY "org.freedesktop.DBus.Error.PropertyReadOnly" #endif struct generic_data { unsigned int refcount; DBusConnection *conn; char *path; GSList *interfaces; GSList *objects; GSList *added; GSList *removed; guint process_id; gboolean pending_prop; char *introspect; struct generic_data *parent; }; struct interface_data { char *name; const GDBusMethodTable *methods; const GDBusSignalTable *signals; const GDBusPropertyTable *properties; GSList *pending_prop; void *user_data; GDBusDestroyFunction destroy; }; struct security_data { GDBusPendingReply pending; DBusMessage *message; const GDBusMethodTable *method; void *iface_user_data; }; struct property_data { DBusConnection *conn; GDBusPendingPropertySet id; DBusMessage *message; }; struct debug_data { g_dbus_debug_func_t func; g_dbus_destroy_func_t destroy; void *data; }; static int global_flags = 0; static struct generic_data *root; static GSList *pending = NULL; static struct debug_data debug = { NULL, NULL, NULL }; static gboolean process_changes(gpointer user_data); static void process_properties_from_interface(struct generic_data *data, struct interface_data *iface); static void process_property_changes(struct generic_data *data); static void print_arguments(GString *gstr, const GDBusArgInfo *args, const char *direction) { for (; args && args->name; args++) { g_string_append_printf(gstr, "name, args->signature); if (direction) g_string_append_printf(gstr, " direction=\"%s\"/>\n", direction); else g_string_append_printf(gstr, "/>\n"); } } #define G_DBUS_ANNOTATE(name_, value_) \ "" #define G_DBUS_ANNOTATE_DEPRECATED \ G_DBUS_ANNOTATE("Deprecated", "true") #define G_DBUS_ANNOTATE_NOREPLY \ G_DBUS_ANNOTATE("Method.NoReply", "true") static gboolean check_experimental(int flags, int flag) { if (!(flags & flag)) return FALSE; return !(global_flags & G_DBUS_FLAG_ENABLE_EXPERIMENTAL); } static bool check_testing(int flags, int flag) { if (!(flags & flag)) return false; return !(global_flags & G_DBUS_FLAG_ENABLE_TESTING); } static void g_dbus_debug(const char *format, ...) { va_list va; char str[MAX_INPUT]; if (!format || !debug.func) return; va_start(va, format); vsnprintf(str, sizeof(str), format, va); debug.func(str, debug.data); va_end(va); } static void generate_interface_xml(GString *gstr, struct interface_data *iface) { const GDBusMethodTable *method; const GDBusSignalTable *signal; const GDBusPropertyTable *property; for (method = iface->methods; method && method->name; method++) { if (check_experimental(method->flags, G_DBUS_METHOD_FLAG_EXPERIMENTAL)) continue; if (check_testing(method->flags, G_DBUS_METHOD_FLAG_TESTING)) continue; g_string_append_printf(gstr, "", method->name); print_arguments(gstr, method->in_args, "in"); print_arguments(gstr, method->out_args, "out"); if (method->flags & G_DBUS_METHOD_FLAG_DEPRECATED) g_string_append_printf(gstr, G_DBUS_ANNOTATE_DEPRECATED); if (method->flags & G_DBUS_METHOD_FLAG_NOREPLY) g_string_append_printf(gstr, G_DBUS_ANNOTATE_NOREPLY); g_string_append_printf(gstr, ""); } for (signal = iface->signals; signal && signal->name; signal++) { if (check_experimental(signal->flags, G_DBUS_SIGNAL_FLAG_EXPERIMENTAL)) continue; if (check_testing(signal->flags, G_DBUS_SIGNAL_FLAG_TESTING)) continue; g_string_append_printf(gstr, "", signal->name); print_arguments(gstr, signal->args, NULL); if (signal->flags & G_DBUS_SIGNAL_FLAG_DEPRECATED) g_string_append_printf(gstr, G_DBUS_ANNOTATE_DEPRECATED); g_string_append_printf(gstr, "\n"); } for (property = iface->properties; property && property->name; property++) { if (check_experimental(property->flags, G_DBUS_PROPERTY_FLAG_EXPERIMENTAL)) continue; if (check_testing(property->flags, G_DBUS_PROPERTY_FLAG_TESTING)) continue; g_string_append_printf(gstr, "", property->name, property->type, property->get ? "read" : "", property->set ? "write" : ""); if (property->flags & G_DBUS_PROPERTY_FLAG_DEPRECATED) g_string_append_printf(gstr, G_DBUS_ANNOTATE_DEPRECATED); g_string_append_printf(gstr, ""); } } static void generate_introspection_xml(DBusConnection *conn, struct generic_data *data, const char *path) { GSList *list; GString *gstr; char **children; int i; g_free(data->introspect); gstr = g_string_new(DBUS_INTROSPECT_1_0_XML_DOCTYPE_DECL_NODE); g_string_append_printf(gstr, ""); for (list = data->interfaces; list; list = list->next) { struct interface_data *iface = list->data; g_string_append_printf(gstr, "", iface->name); generate_interface_xml(gstr, iface); g_string_append_printf(gstr, ""); } if (!dbus_connection_list_registered(conn, path, &children)) goto done; for (i = 0; children[i]; i++) g_string_append_printf(gstr, "", children[i]); dbus_free_string_array(children); done: g_string_append_printf(gstr, ""); data->introspect = g_string_free(gstr, FALSE); } static DBusMessage *introspect(DBusConnection *connection, DBusMessage *message, void *user_data) { struct generic_data *data = user_data; DBusMessage *reply; if (data->introspect == NULL) generate_introspection_xml(connection, data, dbus_message_get_path(message)); reply = dbus_message_new_method_return(message); if (reply == NULL) return NULL; dbus_message_append_args(reply, DBUS_TYPE_STRING, &data->introspect, DBUS_TYPE_INVALID); return reply; } static DBusHandlerResult process_message(DBusConnection *connection, DBusMessage *message, const GDBusMethodTable *method, void *iface_user_data) { DBusMessage *reply; reply = method->function(connection, message, iface_user_data); if (method->flags & G_DBUS_METHOD_FLAG_NOREPLY || dbus_message_get_no_reply(message)) { if (reply != NULL) dbus_message_unref(reply); return DBUS_HANDLER_RESULT_HANDLED; } if (method->flags & G_DBUS_METHOD_FLAG_ASYNC) { if (reply == NULL) return DBUS_HANDLER_RESULT_HANDLED; } if (reply == NULL) return DBUS_HANDLER_RESULT_NEED_MEMORY; g_dbus_send_message(connection, reply); return DBUS_HANDLER_RESULT_HANDLED; } static GDBusPendingReply next_pending = 1; static GSList *pending_security = NULL; static const GDBusSecurityTable *security_table = NULL; void g_dbus_pending_success(DBusConnection *connection, GDBusPendingReply pending) { GSList *list; for (list = pending_security; list; list = list->next) { struct security_data *secdata = list->data; if (secdata->pending != pending) continue; pending_security = g_slist_remove(pending_security, secdata); process_message(connection, secdata->message, secdata->method, secdata->iface_user_data); dbus_message_unref(secdata->message); g_free(secdata); return; } } void g_dbus_pending_error_valist(DBusConnection *connection, GDBusPendingReply pending, const char *name, const char *format, va_list args) { GSList *list; for (list = pending_security; list; list = list->next) { struct security_data *secdata = list->data; if (secdata->pending != pending) continue; pending_security = g_slist_remove(pending_security, secdata); g_dbus_send_error_valist(connection, secdata->message, name, format, args); dbus_message_unref(secdata->message); g_free(secdata); return; } } void g_dbus_pending_error(DBusConnection *connection, GDBusPendingReply pending, const char *name, const char *format, ...) { va_list args; va_start(args, format); g_dbus_pending_error_valist(connection, pending, name, format, args); va_end(args); } int polkit_check_authorization(DBusConnection *conn, const char *action, gboolean interaction, void (*function) (dbus_bool_t authorized, void *user_data), void *user_data, int timeout); struct builtin_security_data { DBusConnection *conn; GDBusPendingReply pending; }; static void builtin_security_result(dbus_bool_t authorized, void *user_data) { struct builtin_security_data *data = user_data; if (authorized == TRUE) g_dbus_pending_success(data->conn, data->pending); else g_dbus_pending_error(data->conn, data->pending, DBUS_ERROR_AUTH_FAILED, NULL); g_free(data); } static void builtin_security_function(DBusConnection *conn, const char *action, gboolean interaction, GDBusPendingReply pending) { struct builtin_security_data *data; data = g_new0(struct builtin_security_data, 1); data->conn = conn; data->pending = pending; if (polkit_check_authorization(conn, action, interaction, builtin_security_result, data, 30000) < 0) g_dbus_pending_error(conn, pending, NULL, NULL); } static gboolean check_privilege(DBusConnection *conn, DBusMessage *msg, const GDBusMethodTable *method, void *iface_user_data) { const GDBusSecurityTable *security; for (security = security_table; security && security->privilege; security++) { struct security_data *secdata; gboolean interaction; if (security->privilege != method->privilege) continue; secdata = g_new(struct security_data, 1); secdata->pending = next_pending++; secdata->message = dbus_message_ref(msg); secdata->method = method; secdata->iface_user_data = iface_user_data; pending_security = g_slist_prepend(pending_security, secdata); if (security->flags & G_DBUS_SECURITY_FLAG_ALLOW_INTERACTION) interaction = TRUE; else interaction = FALSE; if (!(security->flags & G_DBUS_SECURITY_FLAG_BUILTIN) && security->function) security->function(conn, security->action, interaction, secdata->pending); else builtin_security_function(conn, security->action, interaction, secdata->pending); return TRUE; } return FALSE; } static GDBusPendingPropertySet next_pending_property = 1; static GSList *pending_property_set; static int pending_property_data_compare_id(const void *data, const void *user_data) { const struct property_data *propdata = data; const GDBusPendingPropertySet *id = user_data; return propdata->id - *id; } static struct property_data *remove_pending_property_data( GDBusPendingPropertySet id) { struct property_data *propdata; GSList *l; l = g_slist_find_custom(pending_property_set, &id, pending_property_data_compare_id); if (l == NULL) return NULL; propdata = l->data; pending_property_set = g_slist_delete_link(pending_property_set, l); return propdata; } const char *g_dbus_pending_property_get_sender(GDBusPendingPropertySet id) { struct property_data *propdata; GSList *l; l = g_slist_find_custom(pending_property_set, &id, pending_property_data_compare_id); if (l == NULL) return NULL; propdata = l->data; return dbus_message_get_sender(propdata->message); } void g_dbus_pending_property_success(GDBusPendingPropertySet id) { struct property_data *propdata; propdata = remove_pending_property_data(id); if (propdata == NULL) return; g_dbus_send_reply(propdata->conn, propdata->message, DBUS_TYPE_INVALID); dbus_message_unref(propdata->message); g_free(propdata); } void g_dbus_pending_property_error_valist(GDBusPendingReply id, const char *name, const char *format, va_list args) { struct property_data *propdata; propdata = remove_pending_property_data(id); if (propdata == NULL) return; g_dbus_send_error_valist(propdata->conn, propdata->message, name, format, args); dbus_message_unref(propdata->message); g_free(propdata); } void g_dbus_pending_property_error(GDBusPendingReply id, const char *name, const char *format, ...) { va_list args; va_start(args, format); g_dbus_pending_property_error_valist(id, name, format, args); va_end(args); } static void reset_parent(gpointer data, gpointer user_data) { struct generic_data *child = data; struct generic_data *parent = user_data; child->parent = parent; } static void append_property(struct interface_data *iface, const GDBusPropertyTable *p, DBusMessageIter *dict) { DBusMessageIter entry, value; dbus_message_iter_open_container(dict, DBUS_TYPE_DICT_ENTRY, NULL, &entry); dbus_message_iter_append_basic(&entry, DBUS_TYPE_STRING, &p->name); dbus_message_iter_open_container(&entry, DBUS_TYPE_VARIANT, p->type, &value); p->get(p, &value, iface->user_data); dbus_message_iter_close_container(&entry, &value); dbus_message_iter_close_container(dict, &entry); } static void append_properties(struct interface_data *data, DBusMessageIter *iter) { DBusMessageIter dict; const GDBusPropertyTable *p; dbus_message_iter_open_container(iter, DBUS_TYPE_ARRAY, DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING DBUS_TYPE_STRING_AS_STRING DBUS_TYPE_VARIANT_AS_STRING DBUS_DICT_ENTRY_END_CHAR_AS_STRING, &dict); for (p = data->properties; p && p->name; p++) { if (check_experimental(p->flags, G_DBUS_PROPERTY_FLAG_EXPERIMENTAL)) continue; if (check_testing(p->flags, G_DBUS_PROPERTY_FLAG_TESTING)) continue; if (p->get == NULL) continue; if (p->exists != NULL && !p->exists(p, data->user_data)) continue; append_property(data, p, &dict); } dbus_message_iter_close_container(iter, &dict); } static void append_interface(gpointer data, gpointer user_data) { struct interface_data *iface = data; DBusMessageIter *array = user_data; DBusMessageIter entry; dbus_message_iter_open_container(array, DBUS_TYPE_DICT_ENTRY, NULL, &entry); dbus_message_iter_append_basic(&entry, DBUS_TYPE_STRING, &iface->name); append_properties(data, &entry); dbus_message_iter_close_container(array, &entry); } static const char *dbus_message_type_string(DBusMessage *msg) { return dbus_message_type_to_string(dbus_message_get_type(msg)); } static void g_dbus_send_unref(DBusConnection *conn, DBusMessage *msg) { g_dbus_debug("[%s] %s.%s", dbus_message_type_string(msg), dbus_message_get_interface(msg), dbus_message_get_member(msg)); dbus_connection_send(conn, msg, NULL); dbus_message_unref(msg); } static void emit_interfaces_added(struct generic_data *data) { DBusMessage *signal; DBusMessageIter iter, array; if (root == NULL || data == root) return; /* Emit InterfacesAdded on the parent first so it appears first on the * bus as child objects may point to it. */ if (data->parent && data->parent->added) emit_interfaces_added(data->parent); signal = dbus_message_new_signal(root->path, DBUS_INTERFACE_OBJECT_MANAGER, "InterfacesAdded"); if (signal == NULL) return; dbus_message_iter_init_append(signal, &iter); dbus_message_iter_append_basic(&iter, DBUS_TYPE_OBJECT_PATH, &data->path); dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY, DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING DBUS_TYPE_STRING_AS_STRING DBUS_TYPE_ARRAY_AS_STRING DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING DBUS_TYPE_STRING_AS_STRING DBUS_TYPE_VARIANT_AS_STRING DBUS_DICT_ENTRY_END_CHAR_AS_STRING DBUS_DICT_ENTRY_END_CHAR_AS_STRING, &array); g_slist_foreach(data->added, append_interface, &array); g_slist_free(data->added); data->added = NULL; dbus_message_iter_close_container(&iter, &array); /* Use g_dbus_send_unref to avoid recursive calls to g_dbus_flush */ g_dbus_send_unref(data->conn, signal); } static struct interface_data *find_interface(GSList *interfaces, const char *name) { GSList *list; if (name == NULL) return NULL; for (list = interfaces; list; list = list->next) { struct interface_data *iface = list->data; if (!strcmp(name, iface->name)) return iface; } return NULL; } static gboolean g_dbus_args_have_signature(const GDBusArgInfo *args, DBusMessage *message) { const char *sig = dbus_message_get_signature(message); const char *p = NULL; for (; args && args->signature && *sig; args++) { p = args->signature; for (; *sig && *p; sig++, p++) { if (*p != *sig) return FALSE; } } if (*sig || (p && *p) || (args && args->signature)) return FALSE; return TRUE; } static void add_pending(struct generic_data *data) { guint old_id = data->process_id; data->process_id = g_idle_add(process_changes, data); if (old_id > 0) { /* * If the element already had an old idler, remove the old one, * no need to re-add it to the pending list. */ g_source_remove(old_id); return; } pending = g_slist_append(pending, data); } static gboolean remove_interface(struct generic_data *data, const char *name) { struct interface_data *iface; iface = find_interface(data->interfaces, name); if (iface == NULL) return FALSE; process_properties_from_interface(data, iface); data->interfaces = g_slist_remove(data->interfaces, iface); if (iface->destroy) { iface->destroy(iface->user_data); iface->user_data = NULL; } /* * Interface being removed was just added, on the same mainloop * iteration? Don't send any signal */ if (g_slist_find(data->added, iface)) { data->added = g_slist_remove(data->added, iface); g_free(iface->name); g_free(iface); return TRUE; } if (data->parent == NULL) { g_free(iface->name); g_free(iface); return TRUE; } data->removed = g_slist_prepend(data->removed, iface->name); g_free(iface); add_pending(data); return TRUE; } static struct generic_data *invalidate_parent_data(DBusConnection *conn, const char *child_path) { struct generic_data *data = NULL, *child = NULL, *parent = NULL; char *parent_path, *slash; parent_path = g_strdup(child_path); slash = strrchr(parent_path, '/'); if (slash == NULL) goto done; if (slash == parent_path && parent_path[1] != '\0') parent_path[1] = '\0'; else *slash = '\0'; if (!strlen(parent_path)) goto done; if (dbus_connection_get_object_path_data(conn, parent_path, (void *) &data) == FALSE) { goto done; } parent = invalidate_parent_data(conn, parent_path); if (data == NULL) { data = parent; if (data == NULL) goto done; } g_free(data->introspect); data->introspect = NULL; if (!dbus_connection_get_object_path_data(conn, child_path, (void *) &child)) goto done; if (child == NULL || g_slist_find(data->objects, child) != NULL) goto done; if (g_slist_find(parent->objects, child)) goto done; data->objects = g_slist_prepend(data->objects, child); child->parent = data; done: g_free(parent_path); return data; } static inline const GDBusPropertyTable *find_property(const GDBusPropertyTable *properties, const char *name) { const GDBusPropertyTable *p; for (p = properties; p && p->name; p++) { if (strcmp(name, p->name) != 0) continue; if (check_experimental(p->flags, G_DBUS_PROPERTY_FLAG_EXPERIMENTAL)) break; if (check_testing(p->flags, G_DBUS_PROPERTY_FLAG_TESTING)) break; return p; } return NULL; } static DBusMessage *properties_get(DBusConnection *connection, DBusMessage *message, void *user_data) { struct generic_data *data = user_data; struct interface_data *iface; const GDBusPropertyTable *property; const char *interface, *name; DBusMessageIter iter, value; DBusMessage *reply; if (!dbus_message_get_args(message, NULL, DBUS_TYPE_STRING, &interface, DBUS_TYPE_STRING, &name, DBUS_TYPE_INVALID)) return NULL; iface = find_interface(data->interfaces, interface); if (iface == NULL) return g_dbus_create_error(message, DBUS_ERROR_INVALID_ARGS, "No such interface '%s'", interface); property = find_property(iface->properties, name); if (property == NULL) return g_dbus_create_error(message, DBUS_ERROR_INVALID_ARGS, "No such property '%s'", name); if (property->exists != NULL && !property->exists(property, iface->user_data)) return g_dbus_create_error(message, DBUS_ERROR_INVALID_ARGS, "No such property '%s'", name); if (property->get == NULL) return g_dbus_create_error(message, DBUS_ERROR_INVALID_ARGS, "Property '%s' is not readable", name); reply = dbus_message_new_method_return(message); if (reply == NULL) return NULL; dbus_message_iter_init_append(reply, &iter); dbus_message_iter_open_container(&iter, DBUS_TYPE_VARIANT, property->type, &value); if (!property->get(property, &value, iface->user_data)) { dbus_message_unref(reply); return NULL; } dbus_message_iter_close_container(&iter, &value); return reply; } static DBusMessage *properties_get_all(DBusConnection *connection, DBusMessage *message, void *user_data) { struct generic_data *data = user_data; struct interface_data *iface; const char *interface; DBusMessageIter iter; DBusMessage *reply; if (!dbus_message_get_args(message, NULL, DBUS_TYPE_STRING, &interface, DBUS_TYPE_INVALID)) return NULL; iface = find_interface(data->interfaces, interface); if (iface == NULL) return g_dbus_create_error(message, DBUS_ERROR_INVALID_ARGS, "No such interface '%s'", interface); reply = dbus_message_new_method_return(message); if (reply == NULL) return NULL; dbus_message_iter_init_append(reply, &iter); append_properties(iface, &iter); return reply; } static DBusMessage *properties_set(DBusConnection *connection, DBusMessage *message, void *user_data) { struct generic_data *data = user_data; DBusMessageIter iter, sub; struct interface_data *iface; const GDBusPropertyTable *property; const char *name, *interface; struct property_data *propdata; gboolean valid_signature; char *signature; if (!dbus_message_iter_init(message, &iter)) return g_dbus_create_error(message, DBUS_ERROR_INVALID_ARGS, "No arguments given"); if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_STRING) return g_dbus_create_error(message, DBUS_ERROR_INVALID_ARGS, "Invalid argument type: '%c'", dbus_message_iter_get_arg_type(&iter)); dbus_message_iter_get_basic(&iter, &interface); dbus_message_iter_next(&iter); if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_STRING) return g_dbus_create_error(message, DBUS_ERROR_INVALID_ARGS, "Invalid argument type: '%c'", dbus_message_iter_get_arg_type(&iter)); dbus_message_iter_get_basic(&iter, &name); dbus_message_iter_next(&iter); if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_VARIANT) return g_dbus_create_error(message, DBUS_ERROR_INVALID_ARGS, "Invalid argument type: '%c'", dbus_message_iter_get_arg_type(&iter)); dbus_message_iter_recurse(&iter, &sub); iface = find_interface(data->interfaces, interface); if (iface == NULL) return g_dbus_create_error(message, DBUS_ERROR_INVALID_ARGS, "No such interface '%s'", interface); property = find_property(iface->properties, name); if (property == NULL) return g_dbus_create_error(message, DBUS_ERROR_UNKNOWN_PROPERTY, "No such property '%s'", name); if (property->set == NULL) return g_dbus_create_error(message, DBUS_ERROR_PROPERTY_READ_ONLY, "Property '%s' is not writable", name); if (property->exists != NULL && !property->exists(property, iface->user_data)) return g_dbus_create_error(message, DBUS_ERROR_UNKNOWN_PROPERTY, "No such property '%s'", name); signature = dbus_message_iter_get_signature(&sub); valid_signature = strcmp(signature, property->type) ? FALSE : TRUE; dbus_free(signature); if (!valid_signature) return g_dbus_create_error(message, DBUS_ERROR_INVALID_SIGNATURE, "Invalid signature for '%s'", name); propdata = g_new(struct property_data, 1); propdata->id = next_pending_property++; propdata->message = dbus_message_ref(message); propdata->conn = connection; pending_property_set = g_slist_prepend(pending_property_set, propdata); property->set(property, &sub, propdata->id, iface->user_data); return NULL; } static const GDBusMethodTable properties_methods[] = { { GDBUS_METHOD("Get", GDBUS_ARGS({ "interface", "s" }, { "name", "s" }), GDBUS_ARGS({ "value", "v" }), properties_get) }, { GDBUS_ASYNC_METHOD("Set", GDBUS_ARGS({ "interface", "s" }, { "name", "s" }, { "value", "v" }), NULL, properties_set) }, { GDBUS_METHOD("GetAll", GDBUS_ARGS({ "interface", "s" }), GDBUS_ARGS({ "properties", "a{sv}" }), properties_get_all) }, { } }; static const GDBusSignalTable properties_signals[] = { { GDBUS_SIGNAL("PropertiesChanged", GDBUS_ARGS({ "interface", "s" }, { "changed_properties", "a{sv}" }, { "invalidated_properties", "as"})) }, { } }; static void append_name(gpointer data, gpointer user_data) { char *name = data; DBusMessageIter *iter = user_data; dbus_message_iter_append_basic(iter, DBUS_TYPE_STRING, &name); } static void emit_interfaces_removed(struct generic_data *data) { DBusMessage *signal; DBusMessageIter iter, array; if (root == NULL || data == root) return; signal = dbus_message_new_signal(root->path, DBUS_INTERFACE_OBJECT_MANAGER, "InterfacesRemoved"); if (signal == NULL) return; dbus_message_iter_init_append(signal, &iter); dbus_message_iter_append_basic(&iter, DBUS_TYPE_OBJECT_PATH, &data->path); dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY, DBUS_TYPE_STRING_AS_STRING, &array); g_slist_foreach(data->removed, append_name, &array); g_slist_free_full(data->removed, g_free); data->removed = NULL; dbus_message_iter_close_container(&iter, &array); /* Use g_dbus_send_unref to avoid recursive calls to g_dbus_flush */ g_dbus_send_unref(data->conn, signal); } static void remove_pending(struct generic_data *data) { if (data->process_id > 0) { g_source_remove(data->process_id); data->process_id = 0; } pending = g_slist_remove(pending, data); } static gboolean process_changes(gpointer user_data) { struct generic_data *data = user_data; remove_pending(data); if (data->added != NULL) emit_interfaces_added(data); /* Flush pending properties */ if (data->pending_prop == TRUE) process_property_changes(data); if (data->removed != NULL) emit_interfaces_removed(data); data->process_id = 0; return FALSE; } static void generic_unregister(DBusConnection *connection, void *user_data) { struct generic_data *data = user_data; struct generic_data *parent = data->parent; if (parent != NULL) parent->objects = g_slist_remove(parent->objects, data); if (data->process_id > 0) { g_source_remove(data->process_id); data->process_id = 0; process_changes(data); } g_slist_foreach(data->objects, reset_parent, data->parent); g_slist_free(data->objects); dbus_connection_unref(data->conn); g_free(data->introspect); g_free(data->path); g_free(data); } static DBusHandlerResult generic_message(DBusConnection *connection, DBusMessage *message, void *user_data) { struct generic_data *data = user_data; struct interface_data *iface; const GDBusMethodTable *method; const char *interface; g_dbus_debug("[%s:%s] > %s.%s [#%d]", dbus_message_get_sender(message), dbus_message_type_string(message), dbus_message_get_interface(message), dbus_message_get_member(message), dbus_message_get_serial(message)); interface = dbus_message_get_interface(message); iface = find_interface(data->interfaces, interface); if (iface == NULL) return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; for (method = iface->methods; method && method->name && method->function; method++) { if (dbus_message_is_method_call(message, iface->name, method->name) == FALSE) continue; if (check_experimental(method->flags, G_DBUS_METHOD_FLAG_EXPERIMENTAL)) return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; if (check_testing(method->flags, G_DBUS_METHOD_FLAG_TESTING)) return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; if (g_dbus_args_have_signature(method->in_args, message) == FALSE) continue; if (check_privilege(connection, message, method, iface->user_data) == TRUE) return DBUS_HANDLER_RESULT_HANDLED; return process_message(connection, message, method, iface->user_data); } return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; } static DBusObjectPathVTable generic_table = { .unregister_function = generic_unregister, .message_function = generic_message, }; static const GDBusMethodTable introspect_methods[] = { { GDBUS_METHOD("Introspect", NULL, GDBUS_ARGS({ "xml", "s" }), introspect) }, { } }; static void append_interfaces(struct generic_data *data, DBusMessageIter *iter) { DBusMessageIter array; dbus_message_iter_open_container(iter, DBUS_TYPE_ARRAY, DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING DBUS_TYPE_STRING_AS_STRING DBUS_TYPE_ARRAY_AS_STRING DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING DBUS_TYPE_STRING_AS_STRING DBUS_TYPE_VARIANT_AS_STRING DBUS_DICT_ENTRY_END_CHAR_AS_STRING DBUS_DICT_ENTRY_END_CHAR_AS_STRING, &array); g_slist_foreach(data->interfaces, append_interface, &array); dbus_message_iter_close_container(iter, &array); } static void append_object(gpointer data, gpointer user_data) { struct generic_data *child = data; DBusMessageIter *array = user_data; DBusMessageIter entry; dbus_message_iter_open_container(array, DBUS_TYPE_DICT_ENTRY, NULL, &entry); dbus_message_iter_append_basic(&entry, DBUS_TYPE_OBJECT_PATH, &child->path); append_interfaces(child, &entry); dbus_message_iter_close_container(array, &entry); g_slist_foreach(child->objects, append_object, user_data); } static DBusMessage *get_objects(DBusConnection *connection, DBusMessage *message, void *user_data) { struct generic_data *data = user_data; DBusMessage *reply; DBusMessageIter iter; DBusMessageIter array; reply = dbus_message_new_method_return(message); if (reply == NULL) return NULL; dbus_message_iter_init_append(reply, &iter); dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY, DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING DBUS_TYPE_OBJECT_PATH_AS_STRING DBUS_TYPE_ARRAY_AS_STRING DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING DBUS_TYPE_STRING_AS_STRING DBUS_TYPE_ARRAY_AS_STRING DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING DBUS_TYPE_STRING_AS_STRING DBUS_TYPE_VARIANT_AS_STRING DBUS_DICT_ENTRY_END_CHAR_AS_STRING DBUS_DICT_ENTRY_END_CHAR_AS_STRING DBUS_DICT_ENTRY_END_CHAR_AS_STRING, &array); g_slist_foreach(data->objects, append_object, &array); dbus_message_iter_close_container(&iter, &array); return reply; } static const GDBusMethodTable manager_methods[] = { { GDBUS_METHOD("GetManagedObjects", NULL, GDBUS_ARGS({ "objects", "a{oa{sa{sv}}}" }), get_objects) }, { } }; static const GDBusSignalTable manager_signals[] = { { GDBUS_SIGNAL("InterfacesAdded", GDBUS_ARGS({ "object", "o" }, { "interfaces", "a{sa{sv}}" })) }, { GDBUS_SIGNAL("InterfacesRemoved", GDBUS_ARGS({ "object", "o" }, { "interfaces", "as" })) }, { } }; static gboolean add_interface(struct generic_data *data, const char *name, const GDBusMethodTable *methods, const GDBusSignalTable *signals, const GDBusPropertyTable *properties, void *user_data, GDBusDestroyFunction destroy) { struct interface_data *iface; const GDBusMethodTable *method; const GDBusSignalTable *signal; const GDBusPropertyTable *property; for (method = methods; method && method->name; method++) { if (!check_experimental(method->flags, G_DBUS_METHOD_FLAG_EXPERIMENTAL)) goto done; if (!check_testing(method->flags, G_DBUS_METHOD_FLAG_TESTING)) goto done; } for (signal = signals; signal && signal->name; signal++) { if (!check_experimental(signal->flags, G_DBUS_SIGNAL_FLAG_EXPERIMENTAL)) goto done; if (!check_testing(signal->flags, G_DBUS_SIGNAL_FLAG_TESTING)) goto done; } for (property = properties; property && property->name; property++) { if (!check_experimental(property->flags, G_DBUS_PROPERTY_FLAG_EXPERIMENTAL)) goto done; if (!check_testing(property->flags, G_DBUS_PROPERTY_FLAG_TESTING)) goto done; } /* Nothing to register */ return FALSE; done: iface = g_new0(struct interface_data, 1); iface->name = g_strdup(name); iface->methods = methods; iface->signals = signals; iface->properties = properties; iface->user_data = user_data; iface->destroy = destroy; data->interfaces = g_slist_append(data->interfaces, iface); if (data->parent == NULL) return TRUE; data->added = g_slist_append(data->added, iface); add_pending(data); return TRUE; } static struct generic_data *object_path_ref(DBusConnection *connection, const char *path) { struct generic_data *data; if (dbus_connection_get_object_path_data(connection, path, (void *) &data) == TRUE) { if (data != NULL) { data->refcount++; return data; } } data = g_new0(struct generic_data, 1); data->conn = dbus_connection_ref(connection); data->path = g_strdup(path); data->refcount = 1; data->introspect = g_strdup(DBUS_INTROSPECT_1_0_XML_DOCTYPE_DECL_NODE ""); if (!dbus_connection_register_object_path(connection, path, &generic_table, data)) { dbus_connection_unref(data->conn); g_free(data->path); g_free(data->introspect); g_free(data); return NULL; } invalidate_parent_data(connection, path); add_interface(data, DBUS_INTERFACE_INTROSPECTABLE, introspect_methods, NULL, NULL, data, NULL); return data; } static void object_path_unref(DBusConnection *connection, const char *path) { struct generic_data *data = NULL; if (dbus_connection_get_object_path_data(connection, path, (void *) &data) == FALSE) return; if (data == NULL) return; data->refcount--; if (data->refcount > 0) return; remove_interface(data, DBUS_INTERFACE_INTROSPECTABLE); remove_interface(data, DBUS_INTERFACE_PROPERTIES); invalidate_parent_data(data->conn, data->path); dbus_connection_unregister_object_path(data->conn, data->path); } static gboolean check_signal(DBusConnection *conn, const char *path, const char *interface, const char *name, const GDBusArgInfo **args) { struct generic_data *data = NULL; struct interface_data *iface; const GDBusSignalTable *signal; *args = NULL; if (!dbus_connection_get_object_path_data(conn, path, (void *) &data) || data == NULL) { error("dbus_connection_emit_signal: path %s isn't registered", path); return FALSE; } iface = find_interface(data->interfaces, interface); if (iface == NULL) { error("dbus_connection_emit_signal: %s does not implement %s", path, interface); return FALSE; } for (signal = iface->signals; signal && signal->name; signal++) { if (strcmp(signal->name, name) != 0) continue; if (signal->flags & G_DBUS_SIGNAL_FLAG_EXPERIMENTAL) { const char *env = g_getenv("GDBUS_EXPERIMENTAL"); if (g_strcmp0(env, "1") != 0) break; } *args = signal->args; return TRUE; } error("No signal named %s on interface %s", name, interface); return FALSE; } gboolean g_dbus_register_interface(DBusConnection *connection, const char *path, const char *name, const GDBusMethodTable *methods, const GDBusSignalTable *signals, const GDBusPropertyTable *properties, void *user_data, GDBusDestroyFunction destroy) { struct generic_data *data; if (!dbus_validate_path(path, NULL)) { error("Invalid object path: %s", path); return FALSE; } if (!dbus_validate_interface(name, NULL)) { error("Invalid interface: %s", name); return FALSE; } data = object_path_ref(connection, path); if (data == NULL) return FALSE; if (find_interface(data->interfaces, name)) { object_path_unref(connection, path); return FALSE; } if (!add_interface(data, name, methods, signals, properties, user_data, destroy)) { object_path_unref(connection, path); return FALSE; } if (properties != NULL && !find_interface(data->interfaces, DBUS_INTERFACE_PROPERTIES)) add_interface(data, DBUS_INTERFACE_PROPERTIES, properties_methods, properties_signals, NULL, data, NULL); g_free(data->introspect); data->introspect = NULL; return TRUE; } gboolean g_dbus_unregister_interface(DBusConnection *connection, const char *path, const char *name) { struct generic_data *data = NULL; if (path == NULL) return FALSE; if (dbus_connection_get_object_path_data(connection, path, (void *) &data) == FALSE) return FALSE; if (data == NULL) return FALSE; if (remove_interface(data, name) == FALSE) return FALSE; g_free(data->introspect); data->introspect = NULL; object_path_unref(connection, data->path); return TRUE; } gboolean g_dbus_register_security(const GDBusSecurityTable *security) { if (security_table != NULL) return FALSE; security_table = security; return TRUE; } gboolean g_dbus_unregister_security(const GDBusSecurityTable *security) { security_table = NULL; return TRUE; } DBusMessage *g_dbus_create_error_valist(DBusMessage *message, const char *name, const char *format, va_list args) { char str[1024]; /* Check if the message can be replied */ if (dbus_message_get_no_reply(message)) return NULL; if (format) vsnprintf(str, sizeof(str), format, args); else str[0] = '\0'; return dbus_message_new_error(message, name, str); } DBusMessage *g_dbus_create_error(DBusMessage *message, const char *name, const char *format, ...) { va_list args; DBusMessage *reply; va_start(args, format); reply = g_dbus_create_error_valist(message, name, format, args); va_end(args); return reply; } DBusMessage *g_dbus_create_reply_valist(DBusMessage *message, int type, va_list args) { DBusMessage *reply; /* Check if the message can be replied */ if (dbus_message_get_no_reply(message)) return NULL; reply = dbus_message_new_method_return(message); if (reply == NULL) return NULL; if (dbus_message_append_args_valist(reply, type, args) == FALSE) { dbus_message_unref(reply); return NULL; } return reply; } DBusMessage *g_dbus_create_reply(DBusMessage *message, int type, ...) { va_list args; DBusMessage *reply; va_start(args, type); reply = g_dbus_create_reply_valist(message, type, args); va_end(args); return reply; } static void g_dbus_flush(DBusConnection *connection) { GSList *l; for (l = pending; l;) { struct generic_data *data = l->data; l = l->next; if (data->conn != connection) continue; process_changes(data); } } gboolean g_dbus_send_message(DBusConnection *connection, DBusMessage *message) { dbus_bool_t result = FALSE; if (!message) return FALSE; if (dbus_message_get_type(message) == DBUS_MESSAGE_TYPE_METHOD_CALL) dbus_message_set_no_reply(message, TRUE); else if (dbus_message_get_type(message) == DBUS_MESSAGE_TYPE_SIGNAL) { const char *path = dbus_message_get_path(message); const char *interface = dbus_message_get_interface(message); const char *name = dbus_message_get_member(message); const GDBusArgInfo *args; if (!check_signal(connection, path, interface, name, &args)) goto out; } /* Flush pending signal to guarantee message order */ g_dbus_flush(connection); switch (dbus_message_get_type(message)) { case DBUS_MESSAGE_TYPE_METHOD_RETURN: g_dbus_debug("[%s:%s] < [#%d]", dbus_message_get_destination(message), dbus_message_type_string(message), dbus_message_get_reply_serial(message)); break; case DBUS_MESSAGE_TYPE_ERROR: g_dbus_debug("[%s:%s] < %s [#%d]", dbus_message_get_destination(message), dbus_message_type_string(message), dbus_message_get_error_name(message), dbus_message_get_reply_serial(message)); break; case DBUS_MESSAGE_TYPE_SIGNAL: g_dbus_debug("[%s] %s.%s", dbus_message_type_string(message), dbus_message_get_interface(message), dbus_message_get_member(message)); break; default: g_dbus_debug("[%s:%s] < %s.%s", dbus_message_get_destination(message), dbus_message_type_string(message), dbus_message_get_interface(message), dbus_message_get_member(message)); break; } result = dbus_connection_send(connection, message, NULL); out: dbus_message_unref(message); return result; } gboolean g_dbus_send_message_with_reply(DBusConnection *connection, DBusMessage *message, DBusPendingCall **call, int timeout) { dbus_bool_t ret; /* Flush pending signal to guarantee message order */ g_dbus_flush(connection); ret = dbus_connection_send_with_reply(connection, message, call, timeout); if (ret == TRUE && call != NULL && *call == NULL) { error("Unable to send message (passing fd blocked?)"); return FALSE; } g_dbus_debug("[%s:%s] < %s.%s", dbus_message_get_destination(message), dbus_message_type_string(message), dbus_message_get_interface(message), dbus_message_get_member(message)); return ret; } gboolean g_dbus_send_error_valist(DBusConnection *connection, DBusMessage *message, const char *name, const char *format, va_list args) { DBusMessage *error; error = g_dbus_create_error_valist(message, name, format, args); if (error == NULL) return FALSE; return g_dbus_send_message(connection, error); } gboolean g_dbus_send_error(DBusConnection *connection, DBusMessage *message, const char *name, const char *format, ...) { va_list args; gboolean result; va_start(args, format); result = g_dbus_send_error_valist(connection, message, name, format, args); va_end(args); return result; } gboolean g_dbus_send_reply_valist(DBusConnection *connection, DBusMessage *message, int type, va_list args) { DBusMessage *reply; reply = g_dbus_create_reply_valist(message, type, args); if (!reply) return FALSE; return g_dbus_send_message(connection, reply); } gboolean g_dbus_send_reply(DBusConnection *connection, DBusMessage *message, int type, ...) { va_list args; gboolean result; va_start(args, type); result = g_dbus_send_reply_valist(connection, message, type, args); va_end(args); return result; } gboolean g_dbus_emit_signal(DBusConnection *connection, const char *path, const char *interface, const char *name, int type, ...) { va_list args; gboolean result; va_start(args, type); result = g_dbus_emit_signal_valist(connection, path, interface, name, type, args); va_end(args); return result; } gboolean g_dbus_emit_signal_valist(DBusConnection *connection, const char *path, const char *interface, const char *name, int type, va_list args) { DBusMessage *signal; dbus_bool_t ret; const GDBusArgInfo *args_info; if (!check_signal(connection, path, interface, name, &args_info)) return FALSE; signal = dbus_message_new_signal(path, interface, name); if (signal == NULL) { error("Unable to allocate new %s.%s signal", interface, name); return FALSE; } ret = dbus_message_append_args_valist(signal, type, args); if (!ret) goto fail; if (g_dbus_args_have_signature(args_info, signal) == FALSE) { error("%s.%s: got unexpected signature '%s'", interface, name, dbus_message_get_signature(signal)); ret = FALSE; goto fail; } return g_dbus_send_message(connection, signal); fail: dbus_message_unref(signal); return ret; } static void process_properties_from_interface(struct generic_data *data, struct interface_data *iface) { GSList *l; DBusMessage *signal; DBusMessageIter iter, dict, array; GSList *invalidated; if (iface->pending_prop == NULL) return; signal = dbus_message_new_signal(data->path, DBUS_INTERFACE_PROPERTIES, "PropertiesChanged"); if (signal == NULL) { error("Unable to allocate new " DBUS_INTERFACE_PROPERTIES ".PropertiesChanged signal"); return; } iface->pending_prop = g_slist_reverse(iface->pending_prop); dbus_message_iter_init_append(signal, &iter); dbus_message_iter_append_basic(&iter, DBUS_TYPE_STRING, &iface->name); dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY, DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING DBUS_TYPE_STRING_AS_STRING DBUS_TYPE_VARIANT_AS_STRING DBUS_DICT_ENTRY_END_CHAR_AS_STRING, &dict); invalidated = NULL; for (l = iface->pending_prop; l != NULL; l = l->next) { GDBusPropertyTable *p = l->data; if (p->get == NULL) continue; if (p->exists != NULL && !p->exists(p, iface->user_data)) { invalidated = g_slist_prepend(invalidated, p); continue; } append_property(iface, p, &dict); } dbus_message_iter_close_container(&iter, &dict); dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY, DBUS_TYPE_STRING_AS_STRING, &array); for (l = invalidated; l != NULL; l = g_slist_next(l)) { GDBusPropertyTable *p = l->data; dbus_message_iter_append_basic(&array, DBUS_TYPE_STRING, &p->name); } g_slist_free(invalidated); dbus_message_iter_close_container(&iter, &array); g_slist_free(iface->pending_prop); iface->pending_prop = NULL; /* Use g_dbus_send_unref to avoid recursive calls to g_dbus_flush */ g_dbus_send_unref(data->conn, signal); } static void process_property_changes(struct generic_data *data) { GSList *l; data->pending_prop = FALSE; for (l = data->interfaces; l != NULL; l = l->next) { struct interface_data *iface = l->data; process_properties_from_interface(data, iface); } } void g_dbus_emit_property_changed_full(DBusConnection *connection, const char *path, const char *interface, const char *name, GDbusPropertyChangedFlags flags) { const GDBusPropertyTable *property; struct generic_data *data; struct interface_data *iface; if (path == NULL) return; if (!dbus_connection_get_object_path_data(connection, path, (void **) &data) || data == NULL) return; iface = find_interface(data->interfaces, interface); if (iface == NULL) return; /* * If ObjectManager is attached, don't emit property changed if * interface is not yet published */ if (root && g_slist_find(data->added, iface)) return; property = find_property(iface->properties, name); if (property == NULL) { error("Could not find property %s in %p", name, iface->properties); return; } if (g_slist_find(iface->pending_prop, (void *) property) != NULL) return; data->pending_prop = TRUE; iface->pending_prop = g_slist_prepend(iface->pending_prop, (void *) property); if (flags & G_DBUS_PROPERTY_CHANGED_FLAG_FLUSH) process_property_changes(data); else add_pending(data); } void g_dbus_emit_property_changed(DBusConnection *connection, const char *path, const char *interface, const char *name) { g_dbus_emit_property_changed_full(connection, path, interface, name, 0); } gboolean g_dbus_get_properties(DBusConnection *connection, const char *path, const char *interface, DBusMessageIter *iter) { struct generic_data *data; struct interface_data *iface; if (path == NULL) return FALSE; if (!dbus_connection_get_object_path_data(connection, path, (void **) &data) || data == NULL) return FALSE; iface = find_interface(data->interfaces, interface); if (iface == NULL) return FALSE; append_properties(iface, iter); return TRUE; } gboolean g_dbus_attach_object_manager(DBusConnection *connection) { struct generic_data *data; data = object_path_ref(connection, "/"); if (data == NULL) return FALSE; add_interface(data, DBUS_INTERFACE_OBJECT_MANAGER, manager_methods, manager_signals, NULL, data, NULL); root = data; return TRUE; } gboolean g_dbus_detach_object_manager(DBusConnection *connection) { if (!g_dbus_unregister_interface(connection, "/", DBUS_INTERFACE_OBJECT_MANAGER)) return FALSE; root = NULL; return TRUE; } void g_dbus_set_flags(int flags) { global_flags = flags; } int g_dbus_get_flags(void) { return global_flags; } void g_dbus_set_debug(g_dbus_debug_func_t cb, void *user_data, g_dbus_destroy_func_t destroy) { if (debug.destroy) debug.destroy(debug.data); debug.func = cb; debug.destroy = destroy; debug.data = user_data; } bluez-5.82/gdbus/PaxHeaders/mainloop.c0000644000000000000000000000005014015011623014725 xustar0020 atime=1743515843 20 ctime=1743591277 bluez-5.82/gdbus/mainloop.c0000644000000000000000000001655014015011623014415 0ustar00rootroot// SPDX-License-Identifier: GPL-2.0-or-later /* * * D-Bus helper library * * Copyright (C) 2004-2011 Marcel Holtmann * * */ #ifdef HAVE_CONFIG_H #include #endif #include #include #include "gdbus.h" #define info(fmt...) #define error(fmt...) #define debug(fmt...) struct timeout_handler { guint id; DBusTimeout *timeout; }; struct watch_info { guint id; DBusWatch *watch; DBusConnection *conn; }; struct disconnect_data { GDBusWatchFunction function; void *user_data; }; static gboolean disconnected_signal(DBusConnection *conn, DBusMessage *msg, void *data) { struct disconnect_data *dc_data = data; error("Got disconnected from the system message bus"); dc_data->function(conn, dc_data->user_data); dbus_connection_unref(conn); return TRUE; } static gboolean message_dispatch(void *data) { DBusConnection *conn = data; /* Dispatch messages */ while (dbus_connection_dispatch(conn) == DBUS_DISPATCH_DATA_REMAINS); dbus_connection_unref(conn); return FALSE; } static inline void queue_dispatch(DBusConnection *conn, DBusDispatchStatus status) { if (status == DBUS_DISPATCH_DATA_REMAINS) g_idle_add(message_dispatch, dbus_connection_ref(conn)); } static gboolean watch_func(GIOChannel *chan, GIOCondition cond, gpointer data) { struct watch_info *info = data; unsigned int flags = 0; DBusDispatchStatus status; DBusConnection *conn; if (cond & G_IO_IN) flags |= DBUS_WATCH_READABLE; if (cond & G_IO_OUT) flags |= DBUS_WATCH_WRITABLE; if (cond & G_IO_HUP) flags |= DBUS_WATCH_HANGUP; if (cond & G_IO_ERR) flags |= DBUS_WATCH_ERROR; /* Protect connection from being destroyed by dbus_watch_handle */ conn = dbus_connection_ref(info->conn); dbus_watch_handle(info->watch, flags); status = dbus_connection_get_dispatch_status(conn); queue_dispatch(conn, status); dbus_connection_unref(conn); return TRUE; } static void watch_info_free(void *data) { struct watch_info *info = data; if (info->id > 0) { g_source_remove(info->id); info->id = 0; } dbus_connection_unref(info->conn); g_free(info); } static dbus_bool_t add_watch(DBusWatch *watch, void *data) { DBusConnection *conn = data; GIOCondition cond = G_IO_HUP | G_IO_ERR; GIOChannel *chan; struct watch_info *info; unsigned int flags; int fd; if (!dbus_watch_get_enabled(watch)) return TRUE; info = g_new0(struct watch_info, 1); fd = dbus_watch_get_unix_fd(watch); chan = g_io_channel_unix_new(fd); info->watch = watch; info->conn = dbus_connection_ref(conn); dbus_watch_set_data(watch, info, watch_info_free); flags = dbus_watch_get_flags(watch); if (flags & DBUS_WATCH_READABLE) cond |= G_IO_IN; if (flags & DBUS_WATCH_WRITABLE) cond |= G_IO_OUT; info->id = g_io_add_watch(chan, cond, watch_func, info); g_io_channel_unref(chan); return TRUE; } static void remove_watch(DBusWatch *watch, void *data) { if (dbus_watch_get_enabled(watch)) return; /* will trigger watch_info_free() */ dbus_watch_set_data(watch, NULL, NULL); } static void watch_toggled(DBusWatch *watch, void *data) { /* Because we just exit on OOM, enable/disable is * no different from add/remove */ if (dbus_watch_get_enabled(watch)) add_watch(watch, data); else remove_watch(watch, data); } static gboolean timeout_handler_dispatch(gpointer data) { struct timeout_handler *handler = data; handler->id = 0; /* if not enabled should not be polled by the main loop */ if (!dbus_timeout_get_enabled(handler->timeout)) return FALSE; dbus_timeout_handle(handler->timeout); return FALSE; } static void timeout_handler_free(void *data) { struct timeout_handler *handler = data; if (handler->id > 0) { g_source_remove(handler->id); handler->id = 0; } g_free(handler); } static dbus_bool_t add_timeout(DBusTimeout *timeout, void *data) { int interval = dbus_timeout_get_interval(timeout); struct timeout_handler *handler; if (!dbus_timeout_get_enabled(timeout)) return TRUE; handler = g_new0(struct timeout_handler, 1); handler->timeout = timeout; dbus_timeout_set_data(timeout, handler, timeout_handler_free); handler->id = g_timeout_add(interval, timeout_handler_dispatch, handler); return TRUE; } static void remove_timeout(DBusTimeout *timeout, void *data) { /* will trigger timeout_handler_free() */ dbus_timeout_set_data(timeout, NULL, NULL); } static void timeout_toggled(DBusTimeout *timeout, void *data) { if (dbus_timeout_get_enabled(timeout)) add_timeout(timeout, data); else remove_timeout(timeout, data); } static void dispatch_status(DBusConnection *conn, DBusDispatchStatus status, void *data) { if (!dbus_connection_get_is_connected(conn)) return; queue_dispatch(conn, status); } static inline void setup_dbus_with_main_loop(DBusConnection *conn) { dbus_connection_set_watch_functions(conn, add_watch, remove_watch, watch_toggled, conn, NULL); dbus_connection_set_timeout_functions(conn, add_timeout, remove_timeout, timeout_toggled, NULL, NULL); dbus_connection_set_dispatch_status_function(conn, dispatch_status, NULL, NULL); } static gboolean setup_bus(DBusConnection *conn, const char *name, DBusError *error) { gboolean result; DBusDispatchStatus status; if (name != NULL) { result = g_dbus_request_name(conn, name, error); if (error != NULL) { if (dbus_error_is_set(error) == TRUE) return FALSE; } if (result == FALSE) return FALSE; } setup_dbus_with_main_loop(conn); status = dbus_connection_get_dispatch_status(conn); queue_dispatch(conn, status); return TRUE; } DBusConnection *g_dbus_setup_bus(DBusBusType type, const char *name, DBusError *error) { DBusConnection *conn; conn = dbus_bus_get(type, error); if (error != NULL) { if (dbus_error_is_set(error) == TRUE) return NULL; } if (conn == NULL) return NULL; if (setup_bus(conn, name, error) == FALSE) { dbus_connection_unref(conn); return NULL; } return conn; } DBusConnection *g_dbus_setup_private(DBusBusType type, const char *name, DBusError *error) { DBusConnection *conn; conn = dbus_bus_get_private(type, error); if (error != NULL) { if (dbus_error_is_set(error) == TRUE) return NULL; } if (conn == NULL) return NULL; if (setup_bus(conn, name, error) == FALSE) { dbus_connection_close(conn); dbus_connection_unref(conn); return NULL; } return conn; } gboolean g_dbus_request_name(DBusConnection *connection, const char *name, DBusError *error) { int result; result = dbus_bus_request_name(connection, name, DBUS_NAME_FLAG_DO_NOT_QUEUE, error); if (error != NULL) { if (dbus_error_is_set(error) == TRUE) return FALSE; } if (result != DBUS_REQUEST_NAME_REPLY_PRIMARY_OWNER) { if (error != NULL) dbus_set_error(error, name, "Name already in use"); return FALSE; } return TRUE; } gboolean g_dbus_set_disconnect_function(DBusConnection *connection, GDBusWatchFunction function, void *user_data, DBusFreeFunction destroy) { struct disconnect_data *dc_data; dc_data = g_new0(struct disconnect_data, 1); dc_data->function = function; dc_data->user_data = user_data; dbus_connection_set_exit_on_disconnect(connection, FALSE); if (g_dbus_add_signal_watch(connection, NULL, NULL, DBUS_INTERFACE_LOCAL, "Disconnected", disconnected_signal, dc_data, g_free) == 0) { error("Failed to add watch for D-Bus Disconnected signal"); g_free(dc_data); return FALSE; } return TRUE; } bluez-5.82/gdbus/PaxHeaders/watch.c0000644000000000000000000000005014621503015014221 xustar0020 atime=1743515845 20 ctime=1743591277 bluez-5.82/gdbus/watch.c0000644000000000000000000004347314621503015013715 0ustar00rootroot// SPDX-License-Identifier: GPL-2.0-or-later /* * * D-Bus helper library * * Copyright (C) 2004-2011 Marcel Holtmann * * */ #ifdef HAVE_CONFIG_H #include #endif #include #include #include #include #include "gdbus.h" #define info(fmt...) #define error(fmt...) #define debug(fmt...) static DBusHandlerResult message_filter(DBusConnection *connection, DBusMessage *message, void *user_data); static guint listener_id = 0; static GSList *listeners = NULL; struct service_data { DBusConnection *conn; DBusPendingCall *call; char *name; const char *owner; guint id; struct filter_callback *callback; }; struct filter_callback { GDBusWatchFunction conn_func; GDBusWatchFunction disc_func; GDBusSignalFunction signal_func; GDBusDestroyFunction destroy_func; struct service_data *data; void *user_data; guint id; }; struct filter_data { DBusConnection *connection; DBusHandleMessageFunction handle_func; char *name; char *owner; char *path; char *interface; char *member; char *argument; GSList *callbacks; GSList *processed; guint name_watch; gboolean lock; gboolean registered; }; static struct filter_data *filter_data_find_match(DBusConnection *connection, const char *name, const char *owner, const char *path, const char *interface, const char *member, const char *argument) { GSList *current; for (current = listeners; current != NULL; current = current->next) { struct filter_data *data = current->data; if (connection != data->connection) continue; if (g_strcmp0(name, data->name) != 0) continue; if (g_strcmp0(owner, data->owner) != 0) continue; if (g_strcmp0(path, data->path) != 0) continue; if (g_strcmp0(interface, data->interface) != 0) continue; if (g_strcmp0(member, data->member) != 0) continue; if (g_strcmp0(argument, data->argument) != 0) continue; return data; } return NULL; } static struct filter_data *filter_data_find(DBusConnection *connection) { GSList *current; for (current = listeners; current != NULL; current = current->next) { struct filter_data *data = current->data; if (connection != data->connection) continue; return data; } return NULL; } static gboolean format_rule(struct filter_data *data, char *rule, size_t size) { const char *sender; int offset, ret; offset = snprintf(rule, size, "type='signal'"); if (offset < 0) return FALSE; sender = data->name ? : data->owner; if (sender) { ret = snprintf(rule + offset, size - offset, ",sender='%s'", sender); if (ret < 0) return FALSE; offset += ret; } if (data->path) { ret = snprintf(rule + offset, size - offset, ",path='%s'", data->path); if (ret < 0) return FALSE; offset += ret; } if (data->interface) { ret = snprintf(rule + offset, size - offset, ",interface='%s'", data->interface); if (ret < 0) return FALSE; offset += ret; } if (data->member) { ret = snprintf(rule + offset, size - offset, ",member='%s'", data->member); if (ret < 0) return FALSE; offset += ret; } if (data->argument) { ret = snprintf(rule + offset, size - offset, ",arg0='%s'", data->argument); if (ret < 0) return FALSE; } return TRUE; } static gboolean add_match(struct filter_data *data, DBusHandleMessageFunction filter) { DBusError err; char rule[DBUS_MAXIMUM_MATCH_RULE_LENGTH]; format_rule(data, rule, sizeof(rule)); dbus_error_init(&err); dbus_bus_add_match(data->connection, rule, &err); if (dbus_error_is_set(&err)) { error("Adding match rule \"%s\" failed: %s", rule, err.message); dbus_error_free(&err); return FALSE; } data->handle_func = filter; data->registered = TRUE; return TRUE; } static gboolean remove_match(struct filter_data *data) { DBusError err; char rule[DBUS_MAXIMUM_MATCH_RULE_LENGTH]; format_rule(data, rule, sizeof(rule)); dbus_error_init(&err); dbus_bus_remove_match(data->connection, rule, &err); if (dbus_error_is_set(&err)) { error("Removing owner match rule for %s failed: %s", rule, err.message); dbus_error_free(&err); return FALSE; } return TRUE; } static void filter_data_free(struct filter_data *data) { GSList *l; /* Remove filter if there are no listeners left for the connection */ if (filter_data_find(data->connection) == NULL) dbus_connection_remove_filter(data->connection, message_filter, NULL); for (l = data->callbacks; l != NULL; l = l->next) g_free(l->data); g_slist_free(data->callbacks); g_dbus_remove_watch(data->connection, data->name_watch); g_free(data->name); g_free(data->owner); g_free(data->path); g_free(data->interface); g_free(data->member); g_free(data->argument); dbus_connection_unref(data->connection); g_free(data); } static struct filter_data *filter_data_get(DBusConnection *connection, DBusHandleMessageFunction filter, const char *sender, const char *path, const char *interface, const char *member, const char *argument) { struct filter_data *data; const char *name = NULL, *owner = NULL; if (filter_data_find(connection) == NULL) { if (!dbus_connection_add_filter(connection, message_filter, NULL, NULL)) { error("dbus_connection_add_filter() failed"); return NULL; } } if (sender == NULL) goto proceed; if (sender[0] == ':') owner = sender; else name = sender; proceed: data = filter_data_find_match(connection, name, owner, path, interface, member, argument); if (data) return data; data = g_new0(struct filter_data, 1); data->connection = dbus_connection_ref(connection); data->name = g_strdup(name); data->owner = g_strdup(owner); data->path = g_strdup(path); data->interface = g_strdup(interface); data->member = g_strdup(member); data->argument = g_strdup(argument); if (!add_match(data, filter)) { filter_data_free(data); return NULL; } listeners = g_slist_append(listeners, data); return data; } static struct filter_callback *filter_data_find_callback( struct filter_data *data, guint id) { GSList *l; for (l = data->callbacks; l; l = l->next) { struct filter_callback *cb = l->data; if (cb->id == id) return cb; } for (l = data->processed; l; l = l->next) { struct filter_callback *cb = l->data; if (cb->id == id) return cb; } return NULL; } static void filter_data_call_and_free(struct filter_data *data) { GSList *l; for (l = data->callbacks; l != NULL; l = l->next) { struct filter_callback *cb = l->data; if (cb->disc_func) cb->disc_func(data->connection, cb->user_data); if (cb->destroy_func) cb->destroy_func(cb->user_data); g_free(cb); } filter_data_free(data); } static struct filter_callback *filter_data_add_callback( struct filter_data *data, GDBusWatchFunction connect, GDBusWatchFunction disconnect, GDBusSignalFunction signal, GDBusDestroyFunction destroy, void *user_data) { struct filter_callback *cb = NULL; cb = g_new0(struct filter_callback, 1); cb->conn_func = connect; cb->disc_func = disconnect; cb->signal_func = signal; cb->destroy_func = destroy; cb->user_data = user_data; cb->id = ++listener_id; if (data->lock) data->processed = g_slist_append(data->processed, cb); else data->callbacks = g_slist_append(data->callbacks, cb); return cb; } static void service_data_free(struct service_data *data) { struct filter_callback *callback = data->callback; dbus_connection_unref(data->conn); if (data->call) dbus_pending_call_unref(data->call); if (data->id) g_source_remove(data->id); g_free(data->name); g_free(data); callback->data = NULL; } /* Returns TRUE if data is freed */ static gboolean filter_data_remove_callback(struct filter_data *data, struct filter_callback *cb) { data->callbacks = g_slist_remove(data->callbacks, cb); data->processed = g_slist_remove(data->processed, cb); /* Cancel pending operations */ if (cb->data) { if (cb->data->call) dbus_pending_call_cancel(cb->data->call); service_data_free(cb->data); } if (cb->destroy_func) cb->destroy_func(cb->user_data); g_free(cb); /* Don't remove the filter if other callbacks exist or data is lock * processing callbacks */ if (data->callbacks || data->lock) return FALSE; if (data->registered && !remove_match(data)) return FALSE; listeners = g_slist_remove(listeners, data); filter_data_free(data); return TRUE; } static DBusHandlerResult signal_filter(DBusConnection *connection, DBusMessage *message, void *user_data) { struct filter_data *data = user_data; struct filter_callback *cb; while (data->callbacks) { cb = data->callbacks->data; if (cb->signal_func && !cb->signal_func(connection, message, cb->user_data)) { if (filter_data_remove_callback(data, cb)) break; continue; } /* Check if the watch was removed/freed by the callback * function */ if (!g_slist_find(data->callbacks, cb)) continue; data->callbacks = g_slist_remove(data->callbacks, cb); data->processed = g_slist_append(data->processed, cb); } return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; } static void update_name_cache(const char *name, const char *owner) { GSList *l; for (l = listeners; l != NULL; l = l->next) { struct filter_data *data = l->data; if (g_strcmp0(data->name, name) != 0) continue; g_free(data->owner); data->owner = g_strdup(owner); } } static const char *check_name_cache(const char *name) { GSList *l; for (l = listeners; l != NULL; l = l->next) { struct filter_data *data = l->data; if (g_strcmp0(data->name, name) != 0) continue; return data->owner; } return NULL; } static DBusHandlerResult service_filter(DBusConnection *connection, DBusMessage *message, void *user_data) { struct filter_data *data = user_data; struct filter_callback *cb; char *name, *old, *new; if (!dbus_message_get_args(message, NULL, DBUS_TYPE_STRING, &name, DBUS_TYPE_STRING, &old, DBUS_TYPE_STRING, &new, DBUS_TYPE_INVALID)) { error("Invalid arguments for NameOwnerChanged signal"); return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; } update_name_cache(name, new); while (data->callbacks) { cb = data->callbacks->data; if (*new == '\0') { if (cb->disc_func) cb->disc_func(connection, cb->user_data); } else { if (cb->conn_func) cb->conn_func(connection, cb->user_data); } /* Check if the watch was removed/freed by the callback * function */ if (!g_slist_find(data->callbacks, cb)) continue; /* Only auto remove if it is a bus name watch */ if (data->argument[0] == ':' && (cb->conn_func == NULL || cb->disc_func == NULL)) { if (filter_data_remove_callback(data, cb)) break; continue; } data->callbacks = g_slist_remove(data->callbacks, cb); data->processed = g_slist_append(data->processed, cb); } return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; } static DBusHandlerResult message_filter(DBusConnection *connection, DBusMessage *message, void *user_data) { struct filter_data *data; const char *sender, *path, *iface, *member, *arg = NULL; GSList *current, *delete_listener = NULL; /* Only filter signals */ if (dbus_message_get_type(message) != DBUS_MESSAGE_TYPE_SIGNAL) return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; sender = dbus_message_get_sender(message); path = dbus_message_get_path(message); iface = dbus_message_get_interface(message); member = dbus_message_get_member(message); dbus_message_get_args(message, NULL, DBUS_TYPE_STRING, &arg, DBUS_TYPE_INVALID); /* If sender != NULL it is always the owner */ for (current = listeners; current != NULL; current = current->next) { data = current->data; if (connection != data->connection) continue; if (!sender && data->owner) continue; if (data->owner && g_str_equal(sender, data->owner) == FALSE) continue; if (data->path && g_str_equal(path, data->path) == FALSE) continue; if (data->interface && g_str_equal(iface, data->interface) == FALSE) continue; if (data->member && g_str_equal(member, data->member) == FALSE) continue; if (data->argument && g_str_equal(arg, data->argument) == FALSE) continue; if (data->handle_func) { data->lock = TRUE; data->handle_func(connection, message, data); data->callbacks = data->processed; data->processed = NULL; data->lock = FALSE; } if (!data->callbacks) delete_listener = g_slist_prepend(delete_listener, current); } if (delete_listener == NULL) return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; for (current = delete_listener; current != NULL; current = delete_listener->next) { GSList *l = current->data; data = l->data; /* Has any other callback added callbacks back to this data? */ if (data->callbacks != NULL) continue; remove_match(data); listeners = g_slist_delete_link(listeners, l); filter_data_free(data); } g_slist_free(delete_listener); return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; } static gboolean update_service(void *user_data) { struct service_data *data = user_data; struct filter_callback *cb = data->callback; DBusConnection *conn; conn = dbus_connection_ref(data->conn); service_data_free(data); if (cb->conn_func) cb->conn_func(conn, cb->user_data); dbus_connection_unref(conn); return FALSE; } static void service_reply(DBusPendingCall *call, void *user_data) { struct service_data *data = user_data; DBusMessage *reply; DBusError err; reply = dbus_pending_call_steal_reply(call); if (reply == NULL) return; dbus_error_init(&err); if (dbus_set_error_from_message(&err, reply)) goto fail; if (dbus_message_get_args(reply, &err, DBUS_TYPE_STRING, &data->owner, DBUS_TYPE_INVALID) == FALSE) goto fail; update_service(data); goto done; fail: error("%s", err.message); dbus_error_free(&err); service_data_free(data); done: dbus_message_unref(reply); } static void check_service(DBusConnection *connection, const char *name, struct filter_callback *callback) { DBusMessage *message; struct service_data *data; data = g_try_malloc0(sizeof(*data)); if (data == NULL) { error("Can't allocate data structure"); return; } data->conn = dbus_connection_ref(connection); data->name = g_strdup(name); data->callback = callback; callback->data = data; data->owner = check_name_cache(name); if (data->owner != NULL) { data->id = g_idle_add(update_service, data); return; } message = dbus_message_new_method_call(DBUS_SERVICE_DBUS, DBUS_PATH_DBUS, DBUS_INTERFACE_DBUS, "GetNameOwner"); if (message == NULL) { error("Can't allocate new message"); g_free(data); return; } dbus_message_append_args(message, DBUS_TYPE_STRING, &name, DBUS_TYPE_INVALID); if (dbus_connection_send_with_reply(connection, message, &data->call, -1) == FALSE) { error("Failed to execute method call"); g_free(data); goto done; } if (data->call == NULL) { error("D-Bus connection not available"); g_free(data); goto done; } dbus_pending_call_set_notify(data->call, service_reply, data, NULL); done: dbus_message_unref(message); } guint g_dbus_add_service_watch(DBusConnection *connection, const char *name, GDBusWatchFunction connect, GDBusWatchFunction disconnect, void *user_data, GDBusDestroyFunction destroy) { struct filter_data *data; struct filter_callback *cb; if (name == NULL) return 0; data = filter_data_get(connection, service_filter, DBUS_SERVICE_DBUS, DBUS_PATH_DBUS, DBUS_INTERFACE_DBUS, "NameOwnerChanged", name); if (data == NULL) return 0; cb = filter_data_add_callback(data, connect, disconnect, NULL, destroy, user_data); if (cb == NULL) return 0; if (connect) check_service(connection, name, cb); return cb->id; } guint g_dbus_add_disconnect_watch(DBusConnection *connection, const char *name, GDBusWatchFunction func, void *user_data, GDBusDestroyFunction destroy) { return g_dbus_add_service_watch(connection, name, NULL, func, user_data, destroy); } guint g_dbus_add_signal_watch(DBusConnection *connection, const char *sender, const char *path, const char *interface, const char *member, GDBusSignalFunction function, void *user_data, GDBusDestroyFunction destroy) { struct filter_data *data; struct filter_callback *cb; data = filter_data_get(connection, signal_filter, sender, path, interface, member, NULL); if (data == NULL) return 0; cb = filter_data_add_callback(data, NULL, NULL, function, destroy, user_data); if (cb == NULL) return 0; if (data->name != NULL && data->name_watch == 0) data->name_watch = g_dbus_add_service_watch(connection, data->name, NULL, NULL, NULL, NULL); return cb->id; } guint g_dbus_add_properties_watch(DBusConnection *connection, const char *sender, const char *path, const char *interface, GDBusSignalFunction function, void *user_data, GDBusDestroyFunction destroy) { struct filter_data *data; struct filter_callback *cb; data = filter_data_get(connection, signal_filter, sender, path, DBUS_INTERFACE_PROPERTIES, "PropertiesChanged", interface); if (data == NULL) return 0; cb = filter_data_add_callback(data, NULL, NULL, function, destroy, user_data); if (cb == NULL) return 0; if (data->name != NULL && data->name_watch == 0) data->name_watch = g_dbus_add_service_watch(connection, data->name, NULL, NULL, NULL, NULL); return cb->id; } gboolean g_dbus_remove_watch(DBusConnection *connection, guint id) { struct filter_data *data; struct filter_callback *cb; GSList *ldata; if (id == 0) return FALSE; for (ldata = listeners; ldata; ldata = ldata->next) { data = ldata->data; cb = filter_data_find_callback(data, id); if (cb) { filter_data_remove_callback(data, cb); return TRUE; } } return FALSE; } void g_dbus_remove_all_watches(DBusConnection *connection) { struct filter_data *data; while ((data = filter_data_find(connection))) { listeners = g_slist_remove(listeners, data); filter_data_call_and_free(data); } } bluez-5.82/gdbus/PaxHeaders/client.c0000644000000000000000000000005014711225433014376 xustar0020 atime=1743515851 20 ctime=1743591277 bluez-5.82/gdbus/client.c0000644000000000000000000011074714711225433014071 0ustar00rootroot// SPDX-License-Identifier: GPL-2.0-or-later /* * * D-Bus helper library * * Copyright (C) 2004-2011 Marcel Holtmann * Copyright 2024 NXP * * */ #ifdef HAVE_CONFIG_H #include #endif #define _GNU_SOURCE #include #include #include #include #include #include "gdbus.h" #define METHOD_CALL_TIMEOUT (300 * 1000) #ifndef DBUS_INTERFACE_OBJECT_MANAGER #define DBUS_INTERFACE_OBJECT_MANAGER DBUS_INTERFACE_DBUS ".ObjectManager" #endif struct GDBusClient { int ref_count; DBusConnection *dbus_conn; char *service_name; char *base_path; char *root_path; guint watch; guint added_watch; guint removed_watch; GPtrArray *match_rules; DBusPendingCall *pending_call; DBusPendingCall *get_objects_call; GDBusWatchFunction connect_func; void *connect_data; GDBusWatchFunction disconn_func; gboolean connected; void *disconn_data; GDBusMessageFunction signal_func; void *signal_data; GDBusProxyFunction proxy_added; GDBusProxyFunction proxy_removed; GDBusClientFunction ready; void *ready_data; GDBusPropertyFunction property_changed; void *user_data; GList *proxy_list; }; struct GDBusProxy { int ref_count; GDBusClient *client; char *obj_path; char *interface; GHashTable *prop_list; guint watch; GDBusPropertyFunction prop_func; void *prop_data; GDBusProxyFunction removed_func; void *removed_data; DBusPendingCall *get_all_call; gboolean pending; }; struct prop_entry { char *name; int type; DBusMessage *msg; }; static void modify_match_reply(DBusPendingCall *call, void *user_data) { DBusMessage *reply = dbus_pending_call_steal_reply(call); DBusError error; dbus_error_init(&error); if (dbus_set_error_from_message(&error, reply) == TRUE) dbus_error_free(&error); dbus_message_unref(reply); } static gboolean modify_match(DBusConnection *conn, const char *member, const char *rule) { DBusMessage *msg; DBusPendingCall *call; msg = dbus_message_new_method_call(DBUS_SERVICE_DBUS, DBUS_PATH_DBUS, DBUS_INTERFACE_DBUS, member); if (msg == NULL) return FALSE; dbus_message_append_args(msg, DBUS_TYPE_STRING, &rule, DBUS_TYPE_INVALID); if (g_dbus_send_message_with_reply(conn, msg, &call, -1) == FALSE) { dbus_message_unref(msg); return FALSE; } dbus_pending_call_set_notify(call, modify_match_reply, NULL, NULL); dbus_pending_call_unref(call); dbus_message_unref(msg); return TRUE; } static void append_variant(DBusMessageIter *iter, int type, const void *val) { DBusMessageIter value; char sig[2] = { type, '\0' }; dbus_message_iter_open_container(iter, DBUS_TYPE_VARIANT, sig, &value); dbus_message_iter_append_basic(&value, type, val); dbus_message_iter_close_container(iter, &value); } static void append_array_variant(DBusMessageIter *iter, int type, void *val, int n_elements) { DBusMessageIter variant, array; char type_sig[2] = { type, '\0' }; char array_sig[3] = { DBUS_TYPE_ARRAY, type, '\0' }; dbus_message_iter_open_container(iter, DBUS_TYPE_VARIANT, array_sig, &variant); dbus_message_iter_open_container(&variant, DBUS_TYPE_ARRAY, type_sig, &array); if (dbus_type_is_fixed(type) == TRUE) { dbus_message_iter_append_fixed_array(&array, type, val, n_elements); } else if (type == DBUS_TYPE_STRING || type == DBUS_TYPE_OBJECT_PATH) { const char ***str_array = val; int i; for (i = 0; i < n_elements; i++) dbus_message_iter_append_basic(&array, type, &((*str_array)[i])); } dbus_message_iter_close_container(&variant, &array); dbus_message_iter_close_container(iter, &variant); } static void dict_append_basic(DBusMessageIter *dict, int key_type, const void *key, int type, void *val) { DBusMessageIter entry; if (type == DBUS_TYPE_STRING) { const char *str = *((const char **) val); if (str == NULL) return; } dbus_message_iter_open_container(dict, DBUS_TYPE_DICT_ENTRY, NULL, &entry); dbus_message_iter_append_basic(&entry, key_type, key); append_variant(&entry, type, val); dbus_message_iter_close_container(dict, &entry); } void g_dbus_dict_append_entry(DBusMessageIter *dict, const char *key, int type, void *val) { dict_append_basic(dict, DBUS_TYPE_STRING, &key, type, val); } void g_dbus_dict_append_basic_array(DBusMessageIter *dict, int key_type, const void *key, int type, void *val, int n_elements) { DBusMessageIter entry; dbus_message_iter_open_container(dict, DBUS_TYPE_DICT_ENTRY, NULL, &entry); dbus_message_iter_append_basic(&entry, key_type, key); append_array_variant(&entry, type, val, n_elements); dbus_message_iter_close_container(dict, &entry); } void g_dbus_dict_append_array(DBusMessageIter *dict, const char *key, int type, void *val, int n_elements) { g_dbus_dict_append_basic_array(dict, DBUS_TYPE_STRING, &key, type, val, n_elements); } static void append_dict_variant(DBusMessageIter *iter, char *entry, int type, void *val, ...) { DBusMessageIter variant, dict; va_list args; int array_type, size; dbus_message_iter_open_container(iter, DBUS_TYPE_VARIANT, DBUS_TYPE_ARRAY_AS_STRING DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING DBUS_TYPE_STRING_AS_STRING DBUS_TYPE_VARIANT_AS_STRING DBUS_DICT_ENTRY_END_CHAR_AS_STRING, &variant); dbus_message_iter_open_container(&variant, DBUS_TYPE_ARRAY, DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING DBUS_TYPE_STRING_AS_STRING DBUS_TYPE_VARIANT_AS_STRING DBUS_DICT_ENTRY_END_CHAR_AS_STRING, &dict); va_start(args, val); if (type == DBUS_TYPE_ARRAY) { array_type = va_arg(args, int); size = va_arg(args, int); g_dbus_dict_append_array(&dict, entry, array_type, val, size); } else g_dbus_dict_append_entry(&dict, entry, type, val); va_end(args); dbus_message_iter_close_container(&variant, &dict); dbus_message_iter_close_container(iter, &variant); } static void iter_append_iter(DBusMessageIter *base, DBusMessageIter *iter) { int type; type = dbus_message_iter_get_arg_type(iter); if (dbus_type_is_basic(type)) { const void *value; dbus_message_iter_get_basic(iter, &value); dbus_message_iter_append_basic(base, type, &value); } else if (dbus_type_is_container(type)) { DBusMessageIter iter_sub, base_sub; char *sig; dbus_message_iter_recurse(iter, &iter_sub); switch (type) { case DBUS_TYPE_ARRAY: case DBUS_TYPE_VARIANT: sig = dbus_message_iter_get_signature(&iter_sub); break; default: sig = NULL; break; } dbus_message_iter_open_container(base, type, sig, &base_sub); if (sig != NULL) dbus_free(sig); while (dbus_message_iter_get_arg_type(&iter_sub) != DBUS_TYPE_INVALID) { iter_append_iter(&base_sub, &iter_sub); dbus_message_iter_next(&iter_sub); } dbus_message_iter_close_container(base, &base_sub); } } static void prop_entry_update(struct prop_entry *prop, DBusMessageIter *iter) { DBusMessage *msg; DBusMessageIter base; msg = dbus_message_new(DBUS_MESSAGE_TYPE_METHOD_RETURN); if (msg == NULL) return; dbus_message_iter_init_append(msg, &base); iter_append_iter(&base, iter); if (prop->msg != NULL) dbus_message_unref(prop->msg); prop->msg = dbus_message_copy(msg); dbus_message_unref(msg); } static struct prop_entry *prop_entry_new(const char *name, DBusMessageIter *iter) { struct prop_entry *prop; prop = g_try_new0(struct prop_entry, 1); if (prop == NULL) return NULL; prop->name = g_strdup(name); prop->type = dbus_message_iter_get_arg_type(iter); prop_entry_update(prop, iter); return prop; } static void prop_entry_free(gpointer data) { struct prop_entry *prop = data; if (prop->msg != NULL) dbus_message_unref(prop->msg); g_free(prop->name); g_free(prop); } static void add_property(GDBusProxy *proxy, const char *name, DBusMessageIter *iter, gboolean send_changed) { GDBusClient *client = proxy->client; DBusMessageIter value; struct prop_entry *prop; if (dbus_message_iter_get_arg_type(iter) != DBUS_TYPE_VARIANT) return; dbus_message_iter_recurse(iter, &value); prop = g_hash_table_lookup(proxy->prop_list, name); if (prop != NULL) { prop_entry_update(prop, &value); goto done; } prop = prop_entry_new(name, &value); if (prop == NULL) return; g_hash_table_replace(proxy->prop_list, prop->name, prop); done: if (proxy->prop_func) proxy->prop_func(proxy, name, &value, proxy->prop_data); if (client == NULL || send_changed == FALSE) return; if (client->property_changed) client->property_changed(proxy, name, &value, client->user_data); } static void update_properties(GDBusProxy *proxy, DBusMessageIter *iter, gboolean send_changed) { DBusMessageIter dict; if (dbus_message_iter_get_arg_type(iter) != DBUS_TYPE_ARRAY) return; dbus_message_iter_recurse(iter, &dict); while (dbus_message_iter_get_arg_type(&dict) == DBUS_TYPE_DICT_ENTRY) { DBusMessageIter entry; const char *name; dbus_message_iter_recurse(&dict, &entry); if (dbus_message_iter_get_arg_type(&entry) != DBUS_TYPE_STRING) break; dbus_message_iter_get_basic(&entry, &name); dbus_message_iter_next(&entry); add_property(proxy, name, &entry, send_changed); dbus_message_iter_next(&dict); } } static void proxy_added(GDBusClient *client, GDBusProxy *proxy) { if (!proxy->pending) return; if (client->proxy_added) client->proxy_added(proxy, client->user_data); proxy->pending = FALSE; } static void get_all_properties_reply(DBusPendingCall *call, void *user_data) { GDBusProxy *proxy = user_data; GDBusClient *client = proxy->client; DBusMessage *reply = dbus_pending_call_steal_reply(call); DBusMessageIter iter; DBusError error; g_dbus_client_ref(client); dbus_error_init(&error); if (dbus_set_error_from_message(&error, reply) == TRUE) { dbus_error_free(&error); goto done; } dbus_message_iter_init(reply, &iter); update_properties(proxy, &iter, FALSE); done: proxy_added(client, proxy); dbus_message_unref(reply); dbus_pending_call_unref(proxy->get_all_call); proxy->get_all_call = NULL; g_dbus_client_unref(client); } static void get_all_properties(GDBusProxy *proxy) { GDBusClient *client = proxy->client; const char *service_name = client->service_name; DBusMessage *msg; if (proxy->get_all_call) return; msg = dbus_message_new_method_call(service_name, proxy->obj_path, DBUS_INTERFACE_PROPERTIES, "GetAll"); if (msg == NULL) return; dbus_message_append_args(msg, DBUS_TYPE_STRING, &proxy->interface, DBUS_TYPE_INVALID); if (g_dbus_send_message_with_reply(client->dbus_conn, msg, &proxy->get_all_call, -1) == FALSE) { dbus_message_unref(msg); return; } dbus_pending_call_set_notify(proxy->get_all_call, get_all_properties_reply, proxy, NULL); dbus_message_unref(msg); } GDBusProxy *g_dbus_proxy_lookup(GList *list, int *index, const char *path, const char *interface) { GList *l; if (!interface) return NULL; for (l = g_list_nth(list, index ? *index : 0); l; l = g_list_next(l)) { GDBusProxy *proxy = l->data; const char *proxy_iface = g_dbus_proxy_get_interface(proxy); const char *proxy_path = g_dbus_proxy_get_path(proxy); if (index) (*index)++; if (g_str_equal(proxy_iface, interface) == TRUE && g_str_equal(proxy_path, path) == TRUE) return proxy; } return NULL; } char *g_dbus_proxy_path_lookup(GList *list, int *index, const char *path) { int len = strlen(path); GList *l; for (l = g_list_nth(list, index ? *index : 0); l; l = g_list_next(l)) { GDBusProxy *proxy = l->data; const char *proxy_path = g_dbus_proxy_get_path(proxy); if (index) (*index)++; if (!strncasecmp(proxy_path, path, len)) return strdup(proxy_path); } return NULL; } static gboolean properties_changed(DBusConnection *conn, DBusMessage *msg, void *user_data) { GDBusProxy *proxy = user_data; GDBusClient *client = proxy->client; DBusMessageIter iter, entry; const char *interface; if (dbus_message_iter_init(msg, &iter) == FALSE) return TRUE; if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_STRING) return TRUE; dbus_message_iter_get_basic(&iter, &interface); dbus_message_iter_next(&iter); update_properties(proxy, &iter, TRUE); dbus_message_iter_next(&iter); if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_ARRAY) return TRUE; dbus_message_iter_recurse(&iter, &entry); while (dbus_message_iter_get_arg_type(&entry) == DBUS_TYPE_STRING) { const char *name; dbus_message_iter_get_basic(&entry, &name); g_hash_table_remove(proxy->prop_list, name); if (proxy->prop_func) proxy->prop_func(proxy, name, NULL, proxy->prop_data); if (client->property_changed) client->property_changed(proxy, name, NULL, client->user_data); dbus_message_iter_next(&entry); } return TRUE; } static GDBusProxy *proxy_new(GDBusClient *client, const char *path, const char *interface) { GDBusProxy *proxy; proxy = g_try_new0(GDBusProxy, 1); if (proxy == NULL) return NULL; proxy->client = client; proxy->obj_path = g_strdup(path); proxy->interface = g_strdup(interface); proxy->prop_list = g_hash_table_new_full(g_str_hash, g_str_equal, NULL, prop_entry_free); proxy->watch = g_dbus_add_properties_watch(client->dbus_conn, client->service_name, proxy->obj_path, proxy->interface, properties_changed, proxy, NULL); proxy->pending = TRUE; client->proxy_list = g_list_append(client->proxy_list, proxy); return g_dbus_proxy_ref(proxy); } static void proxy_free(gpointer data) { GDBusProxy *proxy = data; if (proxy->client) { GDBusClient *client = proxy->client; if (proxy->get_all_call != NULL) { dbus_pending_call_cancel(proxy->get_all_call); dbus_pending_call_unref(proxy->get_all_call); proxy->get_all_call = NULL; } if (client->proxy_removed) client->proxy_removed(proxy, client->user_data); g_dbus_remove_watch(client->dbus_conn, proxy->watch); g_hash_table_remove_all(proxy->prop_list); proxy->client = NULL; } if (proxy->removed_func) proxy->removed_func(proxy, proxy->removed_data); g_dbus_proxy_unref(proxy); } static void proxy_remove(GDBusClient *client, const char *path, const char *interface) { GList *list; for (list = g_list_first(client->proxy_list); list; list = g_list_next(list)) { GDBusProxy *proxy = list->data; if (g_str_equal(proxy->interface, interface) == TRUE && g_str_equal(proxy->obj_path, path) == TRUE) { client->proxy_list = g_list_delete_link(client->proxy_list, list); proxy_free(proxy); break; } } } static void start_service(GDBusProxy *proxy) { GDBusClient *client = proxy->client; const char *service_name = client->service_name; dbus_uint32_t flags = 0; DBusMessage *msg; msg = dbus_message_new_method_call(DBUS_SERVICE_DBUS, DBUS_PATH_DBUS, DBUS_INTERFACE_DBUS, "StartServiceByName"); if (msg == NULL) return; dbus_message_append_args(msg, DBUS_TYPE_STRING, &service_name, DBUS_TYPE_UINT32, &flags, DBUS_TYPE_INVALID); g_dbus_send_message(client->dbus_conn, msg); return; } GDBusProxy *g_dbus_proxy_new(GDBusClient *client, const char *path, const char *interface) { GDBusProxy *proxy; if (client == NULL) return NULL; proxy = g_dbus_proxy_lookup(client->proxy_list, NULL, path, interface); if (proxy) return g_dbus_proxy_ref(proxy); proxy = proxy_new(client, path, interface); if (proxy == NULL) return NULL; if (!client->connected) { /* Force service to start */ start_service(proxy); return g_dbus_proxy_ref(proxy); } if (!client->get_objects_call) get_all_properties(proxy); return g_dbus_proxy_ref(proxy); } GDBusProxy *g_dbus_proxy_ref(GDBusProxy *proxy) { if (proxy == NULL) return NULL; __sync_fetch_and_add(&proxy->ref_count, 1); return proxy; } void g_dbus_proxy_unref(GDBusProxy *proxy) { if (proxy == NULL) return; if (__sync_sub_and_fetch(&proxy->ref_count, 1) > 0) return; if (proxy->get_all_call != NULL) { dbus_pending_call_cancel(proxy->get_all_call); dbus_pending_call_unref(proxy->get_all_call); } g_hash_table_destroy(proxy->prop_list); g_free(proxy->obj_path); g_free(proxy->interface); g_free(proxy); } const char *g_dbus_proxy_get_path(const GDBusProxy *proxy) { if (proxy == NULL) return NULL; return proxy->obj_path; } const char *g_dbus_proxy_get_interface(GDBusProxy *proxy) { if (proxy == NULL) return NULL; return proxy->interface; } gboolean g_dbus_proxy_get_property(GDBusProxy *proxy, const char *name, DBusMessageIter *iter) { struct prop_entry *prop; if (proxy == NULL || name == NULL) return FALSE; prop = g_hash_table_lookup(proxy->prop_list, name); if (prop == NULL) return FALSE; if (prop->msg == NULL) return FALSE; if (dbus_message_iter_init(prop->msg, iter) == FALSE) return FALSE; return TRUE; } struct refresh_property_data { GDBusProxy *proxy; char *name; }; static void refresh_property_free(gpointer user_data) { struct refresh_property_data *data = user_data; g_free(data->name); g_free(data); } static void refresh_property_reply(DBusPendingCall *call, void *user_data) { struct refresh_property_data *data = user_data; DBusMessage *reply = dbus_pending_call_steal_reply(call); DBusError error; dbus_error_init(&error); if (dbus_set_error_from_message(&error, reply) == FALSE) { DBusMessageIter iter; dbus_message_iter_init(reply, &iter); add_property(data->proxy, data->name, &iter, TRUE); } else dbus_error_free(&error); dbus_message_unref(reply); } gboolean g_dbus_proxy_refresh_property(GDBusProxy *proxy, const char *name) { struct refresh_property_data *data; GDBusClient *client; DBusMessage *msg; DBusMessageIter iter; DBusPendingCall *call; if (proxy == NULL || name == NULL) return FALSE; client = proxy->client; if (client == NULL) return FALSE; data = g_try_new0(struct refresh_property_data, 1); if (data == NULL) return FALSE; data->proxy = proxy; data->name = g_strdup(name); msg = dbus_message_new_method_call(client->service_name, proxy->obj_path, DBUS_INTERFACE_PROPERTIES, "Get"); if (msg == NULL) { refresh_property_free(data); return FALSE; } dbus_message_iter_init_append(msg, &iter); dbus_message_iter_append_basic(&iter, DBUS_TYPE_STRING, &proxy->interface); dbus_message_iter_append_basic(&iter, DBUS_TYPE_STRING, &name); if (g_dbus_send_message_with_reply(client->dbus_conn, msg, &call, -1) == FALSE) { dbus_message_unref(msg); refresh_property_free(data); return FALSE; } dbus_pending_call_set_notify(call, refresh_property_reply, data, refresh_property_free); dbus_pending_call_unref(call); dbus_message_unref(msg); return TRUE; } struct set_property_data { GDBusResultFunction function; void *user_data; GDBusDestroyFunction destroy; }; static void set_property_reply(DBusPendingCall *call, void *user_data) { struct set_property_data *data = user_data; DBusMessage *reply = dbus_pending_call_steal_reply(call); DBusError error; dbus_error_init(&error); dbus_set_error_from_message(&error, reply); if (data->function) data->function(&error, data->user_data); if (data->destroy) data->destroy(data->user_data); dbus_error_free(&error); dbus_message_unref(reply); } gboolean g_dbus_proxy_set_property_basic(GDBusProxy *proxy, const char *name, int type, const void *value, GDBusResultFunction function, void *user_data, GDBusDestroyFunction destroy) { struct set_property_data *data; GDBusClient *client; DBusMessage *msg; DBusMessageIter iter; DBusPendingCall *call; if (proxy == NULL || name == NULL || value == NULL) return FALSE; if (dbus_type_is_basic(type) == FALSE) return FALSE; client = proxy->client; if (client == NULL) return FALSE; data = g_try_new0(struct set_property_data, 1); if (data == NULL) return FALSE; data->function = function; data->user_data = user_data; data->destroy = destroy; msg = dbus_message_new_method_call(client->service_name, proxy->obj_path, DBUS_INTERFACE_PROPERTIES, "Set"); if (msg == NULL) { g_free(data); return FALSE; } dbus_message_iter_init_append(msg, &iter); dbus_message_iter_append_basic(&iter, DBUS_TYPE_STRING, &proxy->interface); dbus_message_iter_append_basic(&iter, DBUS_TYPE_STRING, &name); append_variant(&iter, type, value); if (g_dbus_send_message_with_reply(client->dbus_conn, msg, &call, -1) == FALSE) { dbus_message_unref(msg); g_free(data); return FALSE; } dbus_pending_call_set_notify(call, set_property_reply, data, g_free); dbus_pending_call_unref(call); dbus_message_unref(msg); return TRUE; } gboolean g_dbus_proxy_set_property_dict(GDBusProxy *proxy, const char *name, GDBusResultFunction function, void *user_data, GDBusDestroyFunction destroy, char *entry, ...) { struct set_property_data *data; GDBusClient *client; DBusMessage *msg; DBusMessageIter iter; DBusPendingCall *call; va_list args; int type, array_type, size; void *value; if (proxy == NULL || name == NULL) return FALSE; client = proxy->client; if (client == NULL) return FALSE; data = g_try_new0(struct set_property_data, 1); if (data == NULL) return FALSE; data->function = function; data->user_data = user_data; data->destroy = destroy; msg = dbus_message_new_method_call(client->service_name, proxy->obj_path, DBUS_INTERFACE_PROPERTIES, "Set"); if (msg == NULL) { g_free(data); return FALSE; } dbus_message_iter_init_append(msg, &iter); dbus_message_iter_append_basic(&iter, DBUS_TYPE_STRING, &proxy->interface); dbus_message_iter_append_basic(&iter, DBUS_TYPE_STRING, &name); va_start(args, entry); while (entry != NULL) { type = va_arg(args, int); if (type == DBUS_TYPE_ARRAY) { array_type = va_arg(args, int); size = va_arg(args, int); value = va_arg(args, void *); append_dict_variant(&iter, entry, type, &value, array_type, size); } else { value = va_arg(args, void *); append_dict_variant(&iter, entry, type, &value); } entry = va_arg(args, char *); } va_end(args); if (g_dbus_send_message_with_reply(client->dbus_conn, msg, &call, -1) == FALSE) { dbus_message_unref(msg); g_free(data); return FALSE; } dbus_pending_call_set_notify(call, set_property_reply, data, g_free); dbus_pending_call_unref(call); dbus_message_unref(msg); return TRUE; } gboolean g_dbus_proxy_set_property_array(GDBusProxy *proxy, const char *name, int type, const void *value, size_t size, GDBusResultFunction function, void *user_data, GDBusDestroyFunction destroy) { struct set_property_data *data; GDBusClient *client; DBusMessage *msg; DBusMessageIter iter; DBusPendingCall *call; if (!proxy || !name || !value) return FALSE; if (!dbus_type_is_basic(type)) return FALSE; client = proxy->client; if (!client) return FALSE; data = g_try_new0(struct set_property_data, 1); if (!data) return FALSE; data->function = function; data->user_data = user_data; data->destroy = destroy; msg = dbus_message_new_method_call(client->service_name, proxy->obj_path, DBUS_INTERFACE_PROPERTIES, "Set"); if (!msg) { g_free(data); return FALSE; } dbus_message_iter_init_append(msg, &iter); dbus_message_iter_append_basic(&iter, DBUS_TYPE_STRING, &proxy->interface); dbus_message_iter_append_basic(&iter, DBUS_TYPE_STRING, &name); append_array_variant(&iter, type, &value, size); if (g_dbus_send_message_with_reply(client->dbus_conn, msg, &call, -1) == FALSE) { dbus_message_unref(msg); g_free(data); return FALSE; } dbus_pending_call_set_notify(call, set_property_reply, data, g_free); dbus_pending_call_unref(call); dbus_message_unref(msg); return TRUE; } struct method_call_data { GDBusReturnFunction function; void *user_data; GDBusDestroyFunction destroy; }; static void method_call_reply(DBusPendingCall *call, void *user_data) { struct method_call_data *data = user_data; DBusMessage *reply = dbus_pending_call_steal_reply(call); if (data->function) data->function(reply, data->user_data); if (data->destroy) data->destroy(data->user_data); dbus_message_unref(reply); } gboolean g_dbus_proxy_method_call(GDBusProxy *proxy, const char *method, GDBusSetupFunction setup, GDBusReturnFunction function, void *user_data, GDBusDestroyFunction destroy) { struct method_call_data *data; GDBusClient *client; DBusMessage *msg; DBusPendingCall *call; if (proxy == NULL || method == NULL) return FALSE; client = proxy->client; if (client == NULL) return FALSE; msg = dbus_message_new_method_call(client->service_name, proxy->obj_path, proxy->interface, method); if (msg == NULL) return FALSE; if (setup) { DBusMessageIter iter; dbus_message_iter_init_append(msg, &iter); setup(&iter, user_data); } if (!function) return g_dbus_send_message(client->dbus_conn, msg); data = g_try_new0(struct method_call_data, 1); if (data == NULL) return FALSE; data->function = function; data->user_data = user_data; data->destroy = destroy; if (g_dbus_send_message_with_reply(client->dbus_conn, msg, &call, METHOD_CALL_TIMEOUT) == FALSE) { dbus_message_unref(msg); g_free(data); return FALSE; } dbus_pending_call_set_notify(call, method_call_reply, data, g_free); dbus_pending_call_unref(call); dbus_message_unref(msg); return TRUE; } gboolean g_dbus_proxy_set_property_watch(GDBusProxy *proxy, GDBusPropertyFunction function, void *user_data) { if (proxy == NULL) return FALSE; proxy->prop_func = function; proxy->prop_data = user_data; return TRUE; } gboolean g_dbus_proxy_set_removed_watch(GDBusProxy *proxy, GDBusProxyFunction function, void *user_data) { if (proxy == NULL) return FALSE; proxy->removed_func = function; proxy->removed_data = user_data; return TRUE; } static void refresh_properties(GList *list) { GList *l; for (l = g_list_first(list); l; l = g_list_next(l)) { GDBusProxy *proxy = list->data; if (proxy->pending) get_all_properties(proxy); } } static void parse_properties(GDBusClient *client, const char *path, const char *interface, DBusMessageIter *iter) { GDBusProxy *proxy; if (g_str_equal(interface, DBUS_INTERFACE_INTROSPECTABLE) == TRUE) return; if (g_str_equal(interface, DBUS_INTERFACE_PROPERTIES) == TRUE) return; proxy = g_dbus_proxy_lookup(client->proxy_list, NULL, path, interface); if (proxy && !proxy->pending) { update_properties(proxy, iter, FALSE); return; } if (!proxy) { proxy = proxy_new(client, path, interface); if (proxy == NULL) return; } update_properties(proxy, iter, FALSE); proxy_added(client, proxy); } static void parse_interfaces(GDBusClient *client, const char *path, DBusMessageIter *iter) { DBusMessageIter dict; if (dbus_message_iter_get_arg_type(iter) != DBUS_TYPE_ARRAY) return; dbus_message_iter_recurse(iter, &dict); while (dbus_message_iter_get_arg_type(&dict) == DBUS_TYPE_DICT_ENTRY) { DBusMessageIter entry; const char *interface; dbus_message_iter_recurse(&dict, &entry); if (dbus_message_iter_get_arg_type(&entry) != DBUS_TYPE_STRING) break; dbus_message_iter_get_basic(&entry, &interface); dbus_message_iter_next(&entry); parse_properties(client, path, interface, &entry); dbus_message_iter_next(&dict); } } static gboolean interfaces_added(DBusConnection *conn, DBusMessage *msg, void *user_data) { GDBusClient *client = user_data; DBusMessageIter iter; const char *path; if (dbus_message_iter_init(msg, &iter) == FALSE) return TRUE; if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_OBJECT_PATH) return TRUE; dbus_message_iter_get_basic(&iter, &path); dbus_message_iter_next(&iter); g_dbus_client_ref(client); parse_interfaces(client, path, &iter); g_dbus_client_unref(client); return TRUE; } static gboolean interfaces_removed(DBusConnection *conn, DBusMessage *msg, void *user_data) { GDBusClient *client = user_data; DBusMessageIter iter, entry; const char *path; if (dbus_message_iter_init(msg, &iter) == FALSE) return TRUE; if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_OBJECT_PATH) return TRUE; dbus_message_iter_get_basic(&iter, &path); dbus_message_iter_next(&iter); if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_ARRAY) return TRUE; dbus_message_iter_recurse(&iter, &entry); g_dbus_client_ref(client); while (dbus_message_iter_get_arg_type(&entry) == DBUS_TYPE_STRING) { const char *interface; dbus_message_iter_get_basic(&entry, &interface); proxy_remove(client, path, interface); dbus_message_iter_next(&entry); } g_dbus_client_unref(client); return TRUE; } static void parse_managed_objects(GDBusClient *client, DBusMessage *msg) { DBusMessageIter iter, dict; if (dbus_message_iter_init(msg, &iter) == FALSE) return; if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_ARRAY) return; dbus_message_iter_recurse(&iter, &dict); while (dbus_message_iter_get_arg_type(&dict) == DBUS_TYPE_DICT_ENTRY) { DBusMessageIter entry; const char *path; dbus_message_iter_recurse(&dict, &entry); if (dbus_message_iter_get_arg_type(&entry) != DBUS_TYPE_OBJECT_PATH) break; dbus_message_iter_get_basic(&entry, &path); dbus_message_iter_next(&entry); parse_interfaces(client, path, &entry); dbus_message_iter_next(&dict); } } static void get_managed_objects_reply(DBusPendingCall *call, void *user_data) { GDBusClient *client = user_data; DBusMessage *reply = dbus_pending_call_steal_reply(call); DBusError error; g_dbus_client_ref(client); dbus_error_init(&error); if (dbus_set_error_from_message(&error, reply) == TRUE) { dbus_error_free(&error); goto done; } parse_managed_objects(client, reply); done: if (client->ready) client->ready(client, client->ready_data); dbus_message_unref(reply); dbus_pending_call_unref(client->get_objects_call); client->get_objects_call = NULL; refresh_properties(client->proxy_list); g_dbus_client_unref(client); } static void get_managed_objects(GDBusClient *client) { DBusMessage *msg; if (!client->connected) return; if ((!client->proxy_added && !client->proxy_removed) || !client->root_path) { refresh_properties(client->proxy_list); return; } if (client->get_objects_call != NULL) return; msg = dbus_message_new_method_call(client->service_name, client->root_path, DBUS_INTERFACE_OBJECT_MANAGER, "GetManagedObjects"); if (msg == NULL) return; dbus_message_append_args(msg, DBUS_TYPE_INVALID); if (g_dbus_send_message_with_reply(client->dbus_conn, msg, &client->get_objects_call, -1) == FALSE) { dbus_message_unref(msg); return; } dbus_pending_call_set_notify(client->get_objects_call, get_managed_objects_reply, client, NULL); dbus_message_unref(msg); } static void service_connect(DBusConnection *conn, void *user_data) { GDBusClient *client = user_data; g_dbus_client_ref(client); client->connected = TRUE; get_managed_objects(client); if (client->connect_func) client->connect_func(conn, client->connect_data); g_dbus_client_unref(client); } static void service_disconnect(DBusConnection *conn, void *user_data) { GDBusClient *client = user_data; client->connected = FALSE; g_list_free_full(client->proxy_list, proxy_free); client->proxy_list = NULL; if (client->disconn_func) client->disconn_func(conn, client->disconn_data); } static DBusHandlerResult message_filter(DBusConnection *connection, DBusMessage *message, void *user_data) { GDBusClient *client = user_data; const char *sender, *path, *interface; if (dbus_message_get_type(message) != DBUS_MESSAGE_TYPE_SIGNAL) return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; sender = dbus_message_get_sender(message); if (sender == NULL) return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; path = dbus_message_get_path(message); interface = dbus_message_get_interface(message); if (g_str_has_prefix(path, client->base_path) == FALSE) return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; if (g_str_equal(interface, DBUS_INTERFACE_PROPERTIES) == TRUE) return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; if (client->signal_func) client->signal_func(connection, message, client->signal_data); return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; } GDBusClient *g_dbus_client_new(DBusConnection *connection, const char *service, const char *path) { return g_dbus_client_new_full(connection, service, path, "/"); } GDBusClient *g_dbus_client_new_full(DBusConnection *connection, const char *service, const char *path, const char *root_path) { GDBusClient *client; unsigned int i; if (!connection || !service) return NULL; client = g_try_new0(GDBusClient, 1); if (client == NULL) return NULL; if (dbus_connection_add_filter(connection, message_filter, client, NULL) == FALSE) { g_free(client); return NULL; } client->dbus_conn = dbus_connection_ref(connection); client->service_name = g_strdup(service); client->base_path = g_strdup(path); client->root_path = g_strdup(root_path); client->connected = FALSE; client->match_rules = g_ptr_array_sized_new(1); g_ptr_array_set_free_func(client->match_rules, g_free); client->watch = g_dbus_add_service_watch(connection, service, service_connect, service_disconnect, client, NULL); if (!root_path) return g_dbus_client_ref(client); client->added_watch = g_dbus_add_signal_watch(connection, service, client->root_path, DBUS_INTERFACE_OBJECT_MANAGER, "InterfacesAdded", interfaces_added, client, NULL); client->removed_watch = g_dbus_add_signal_watch(connection, service, client->root_path, DBUS_INTERFACE_OBJECT_MANAGER, "InterfacesRemoved", interfaces_removed, client, NULL); g_ptr_array_add(client->match_rules, g_strdup_printf("type='signal'," "sender='%s',path_namespace='%s'", client->service_name, client->base_path)); for (i = 0; i < client->match_rules->len; i++) { modify_match(client->dbus_conn, "AddMatch", g_ptr_array_index(client->match_rules, i)); } return g_dbus_client_ref(client); } GDBusClient *g_dbus_client_ref(GDBusClient *client) { if (client == NULL) return NULL; __sync_fetch_and_add(&client->ref_count, 1); return client; } void g_dbus_client_unref(GDBusClient *client) { unsigned int i; if (client == NULL) return; if (__sync_sub_and_fetch(&client->ref_count, 1) > 0) return; if (client->pending_call != NULL) { dbus_pending_call_cancel(client->pending_call); dbus_pending_call_unref(client->pending_call); } if (client->get_objects_call != NULL) { dbus_pending_call_cancel(client->get_objects_call); dbus_pending_call_unref(client->get_objects_call); } for (i = 0; i < client->match_rules->len; i++) { modify_match(client->dbus_conn, "RemoveMatch", g_ptr_array_index(client->match_rules, i)); } g_ptr_array_free(client->match_rules, TRUE); dbus_connection_remove_filter(client->dbus_conn, message_filter, client); g_list_free_full(client->proxy_list, proxy_free); /* * Don't call disconn_func twice if disconnection * was previously reported. */ if (client->disconn_func && client->connected) client->disconn_func(client->dbus_conn, client->disconn_data); g_dbus_remove_watch(client->dbus_conn, client->watch); g_dbus_remove_watch(client->dbus_conn, client->added_watch); g_dbus_remove_watch(client->dbus_conn, client->removed_watch); dbus_connection_unref(client->dbus_conn); g_free(client->service_name); g_free(client->base_path); g_free(client->root_path); g_free(client); } gboolean g_dbus_client_set_connect_watch(GDBusClient *client, GDBusWatchFunction function, void *user_data) { if (client == NULL) return FALSE; client->connect_func = function; client->connect_data = user_data; return TRUE; } gboolean g_dbus_client_set_disconnect_watch(GDBusClient *client, GDBusWatchFunction function, void *user_data) { if (client == NULL) return FALSE; client->disconn_func = function; client->disconn_data = user_data; return TRUE; } gboolean g_dbus_client_set_signal_watch(GDBusClient *client, GDBusMessageFunction function, void *user_data) { if (client == NULL) return FALSE; client->signal_func = function; client->signal_data = user_data; return TRUE; } gboolean g_dbus_client_set_ready_watch(GDBusClient *client, GDBusClientFunction ready, void *user_data) { if (client == NULL) return FALSE; client->ready = ready; client->ready_data = user_data; return TRUE; } gboolean g_dbus_client_set_proxy_handlers(GDBusClient *client, GDBusProxyFunction proxy_added, GDBusProxyFunction proxy_removed, GDBusPropertyFunction property_changed, void *user_data) { if (client == NULL) return FALSE; client->proxy_added = proxy_added; client->proxy_removed = proxy_removed; client->property_changed = property_changed; client->user_data = user_data; if (proxy_added || proxy_removed || property_changed) get_managed_objects(client); return TRUE; } bluez-5.82/PaxHeaders/Makefile.in0000644000000000000000000000005014773211377013727 xustar0020 atime=1743590166 20 ctime=1743591275 bluez-5.82/Makefile.in0000644000000000000000000410327614773211377013425 0ustar00rootroot# Makefile.in generated by automake 1.16.5 from Makefile.am. # @configure_input@ # Copyright (C) 1994-2021 Free Software Foundation, Inc. # This Makefile.in is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, # with or without modifications, as long as this notice is preserved. # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY, to the extent permitted by law; without # even the implied warranty of MERCHANTABILITY or FITNESS FOR A # PARTICULAR PURPOSE. @SET_MAKE@ VPATH = @srcdir@ am__is_gnu_make = { \ if test -z '$(MAKELEVEL)'; then \ false; \ elif test -n '$(MAKE_HOST)'; then \ true; \ elif test -n '$(MAKE_VERSION)' && test -n '$(CURDIR)'; then \ true; \ else \ false; \ fi; \ } am__make_running_with_option = \ case $${target_option-} in \ ?) ;; \ *) echo "am__make_running_with_option: internal error: invalid" \ "target option '$${target_option-}' specified" >&2; \ exit 1;; \ esac; \ has_opt=no; \ sane_makeflags=$$MAKEFLAGS; \ if $(am__is_gnu_make); then \ sane_makeflags=$$MFLAGS; \ else \ case $$MAKEFLAGS in \ *\\[\ \ ]*) \ bs=\\; \ sane_makeflags=`printf '%s\n' "$$MAKEFLAGS" \ | sed "s/$$bs$$bs[$$bs $$bs ]*//g"`;; \ esac; \ fi; \ skip_next=no; \ strip_trailopt () \ { \ flg=`printf '%s\n' "$$flg" | sed "s/$$1.*$$//"`; \ }; \ for flg in $$sane_makeflags; do \ test $$skip_next = yes && { skip_next=no; continue; }; \ case $$flg in \ *=*|--*) continue;; \ -*I) strip_trailopt 'I'; skip_next=yes;; \ -*I?*) strip_trailopt 'I';; \ -*O) strip_trailopt 'O'; skip_next=yes;; \ -*O?*) strip_trailopt 'O';; \ -*l) strip_trailopt 'l'; skip_next=yes;; \ -*l?*) strip_trailopt 'l';; \ -[dEDm]) skip_next=yes;; \ -[JT]) skip_next=yes;; \ esac; \ case $$flg in \ *$$target_option*) has_opt=yes; break;; \ esac; \ done; \ test $$has_opt = yes am__make_dryrun = (target_option=n; $(am__make_running_with_option)) am__make_keepgoing = (target_option=k; $(am__make_running_with_option)) pkgdatadir = $(datadir)/@PACKAGE@ pkglibdir = $(libdir)/@PACKAGE@ am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd install_sh_DATA = $(install_sh) -c -m 644 install_sh_PROGRAM = $(install_sh) -c install_sh_SCRIPT = $(install_sh) -c INSTALL_HEADER = $(INSTALL_DATA) transform = $(program_transform_name) NORMAL_INSTALL = : PRE_INSTALL = : POST_INSTALL = : NORMAL_UNINSTALL = : PRE_UNINSTALL = : POST_UNINSTALL = : build_triplet = @build@ host_triplet = @host@ bin_PROGRAMS = $(am__EXEEXT_1) $(am__EXEEXT_2) $(am__EXEEXT_3) \ $(am__EXEEXT_4) $(am__EXEEXT_5) $(am__EXEEXT_6) noinst_PROGRAMS = $(am__EXEEXT_7) $(am__EXEEXT_8) $(am__EXEEXT_9) \ $(am__EXEEXT_10) $(am__EXEEXT_11) $(am__EXEEXT_12) \ $(am__EXEEXT_18) pkglibexec_PROGRAMS = src/bluetoothd$(EXEEXT) $(am__EXEEXT_19) \ $(am__EXEEXT_20) $(am__EXEEXT_21) @DATAFILES_TRUE@@OBEX_TRUE@am__append_1 = obexd/src/obex.conf @LIBRARY_TRUE@am__append_2 = $(lib_headers) @LIBRARY_TRUE@am__append_3 = lib/libbluetooth.la @EXTERNAL_ELL_FALSE@am__append_4 = ell/libell-internal.la @LIBSHARED_ELL_TRUE@am__append_5 = src/libshared-ell.la @READLINE_TRUE@am__append_6 = src/shared/shell.c src/shared/shell.h @ADMIN_TRUE@am__append_7 = admin @ADMIN_TRUE@am__append_8 = plugins/admin.c @NFC_TRUE@am__append_9 = neard @NFC_TRUE@am__append_10 = plugins/neard.c @SAP_TRUE@am__append_11 = sap @SAP_TRUE@am__append_12 = profiles/sap/main.c profiles/sap/manager.h \ @SAP_TRUE@ profiles/sap/manager.c profiles/sap/server.h \ @SAP_TRUE@ profiles/sap/server.c profiles/sap/sap.h \ @SAP_TRUE@ profiles/sap/sap-dummy.c @A2DP_TRUE@am__append_13 = a2dp @A2DP_TRUE@am__append_14 = profiles/audio/source.h profiles/audio/source.c \ @A2DP_TRUE@ profiles/audio/sink.h profiles/audio/sink.c \ @A2DP_TRUE@ profiles/audio/a2dp.h profiles/audio/a2dp.c \ @A2DP_TRUE@ profiles/audio/avdtp.h profiles/audio/avdtp.c \ @A2DP_TRUE@ profiles/audio/a2dp-codecs.h @AVRCP_TRUE@am__append_15 = avrcp @AVRCP_TRUE@am__append_16 = profiles/audio/control.h profiles/audio/control.c \ @AVRCP_TRUE@ profiles/audio/avctp.h profiles/audio/avctp.c \ @AVRCP_TRUE@ profiles/audio/avrcp.h profiles/audio/avrcp.c @NETWORK_TRUE@am__append_17 = network @NETWORK_TRUE@am__append_18 = profiles/network/manager.c \ @NETWORK_TRUE@ profiles/network/bnep.h profiles/network/bnep.c \ @NETWORK_TRUE@ profiles/network/server.h profiles/network/server.c \ @NETWORK_TRUE@ profiles/network/connection.h \ @NETWORK_TRUE@ profiles/network/connection.c @HID_TRUE@am__append_19 = input @HID_TRUE@am__append_20 = profiles/input/manager.c \ @HID_TRUE@ profiles/input/server.h profiles/input/server.c \ @HID_TRUE@ profiles/input/device.h profiles/input/device.c \ @HID_TRUE@ profiles/input/hidp_defs.h profiles/input/sixaxis.h @HOG_TRUE@am__append_21 = hog @HOG_TRUE@am__append_22 = profiles/input/hog.c \ @HOG_TRUE@ profiles/input/hog-lib.c profiles/input/hog-lib.h \ @HOG_TRUE@ profiles/deviceinfo/dis.c profiles/deviceinfo/dis.h \ @HOG_TRUE@ profiles/battery/bas.c profiles/battery/bas.h \ @HOG_TRUE@ profiles/scanparam/scpp.c profiles/scanparam/scpp.h \ @HOG_TRUE@ profiles/input/suspend.h profiles/input/suspend-none.c @HEALTH_TRUE@am__append_23 = health @HEALTH_TRUE@am__append_24 = profiles/health/mcap.h profiles/health/mcap.c \ @HEALTH_TRUE@ profiles/health/hdp_main.c profiles/health/hdp_types.h \ @HEALTH_TRUE@ profiles/health/hdp_manager.h \ @HEALTH_TRUE@ profiles/health/hdp_manager.c \ @HEALTH_TRUE@ profiles/health/hdp.h profiles/health/hdp.c \ @HEALTH_TRUE@ profiles/health/hdp_util.h profiles/health/hdp_util.c @MIDI_TRUE@am__append_25 = midi @MIDI_TRUE@am__append_26 = profiles/midi/midi.c \ @MIDI_TRUE@ profiles/midi/libmidi.h \ @MIDI_TRUE@ profiles/midi/libmidi.c @MIDI_TRUE@am__append_27 = $(ALSA_CFLAGS) @MIDI_TRUE@am__append_28 = $(ALSA_LIBS) @SIXAXIS_TRUE@am__append_29 = sixaxis @SIXAXIS_TRUE@am__append_30 = plugins/sixaxis.c @SIXAXIS_TRUE@am__append_31 = $(UDEV_LIBS) @BAP_TRUE@am__append_32 = bap @BAP_TRUE@am__append_33 = profiles/audio/bap.c @BASS_TRUE@am__append_34 = bass @BASS_TRUE@am__append_35 = profiles/audio/bass.c @MCP_TRUE@am__append_36 = mcp @MCP_TRUE@am__append_37 = profiles/audio/mcp.c @VCP_TRUE@am__append_38 = vcp @VCP_TRUE@am__append_39 = profiles/audio/vcp.h profiles/audio/vcp.c @MICP_TRUE@am__append_40 = micp @MICP_TRUE@am__append_41 = profiles/audio/micp.c @CCP_TRUE@am__append_42 = ccp @CCP_TRUE@am__append_43 = profiles/audio/ccp.c @CSIP_TRUE@am__append_44 = csip @CSIP_TRUE@am__append_45 = profiles/audio/csip.c @ASHA_TRUE@am__append_46 = asha @ASHA_TRUE@am__append_47 = profiles/audio/asha.h profiles/audio/asha.c @EXTERNAL_PLUGINS_TRUE@am__append_48 = src/bluetooth.ver # SPDX-License-Identifier: GPL-2.0 @CLIENT_TRUE@am__append_49 = client/bluetoothctl @MONITOR_TRUE@am__append_50 = monitor/btmon @MANPAGES_TRUE@@MONITOR_TRUE@am__append_51 = monitor/btmon.1 @LOGGER_TRUE@am__append_52 = tools/btmon-logger @LOGGER_TRUE@@SYSTEMD_TRUE@am__append_53 = tools/bluetooth-logger.service @TESTING_TRUE@am__append_54 = emulator/btvirt emulator/b1ee emulator/hfp \ @TESTING_TRUE@ peripheral/btsensor tools/3dsp \ @TESTING_TRUE@ tools/mgmt-tester tools/gap-tester \ @TESTING_TRUE@ tools/l2cap-tester tools/sco-tester \ @TESTING_TRUE@ tools/smp-tester tools/hci-tester \ @TESTING_TRUE@ tools/rfcomm-tester tools/bnep-tester \ @TESTING_TRUE@ tools/userchan-tester tools/iso-tester \ @TESTING_TRUE@ tools/mesh-tester tools/ioctl-tester @TOOLS_TRUE@am__append_55 = tools/rctest tools/l2test tools/l2ping tools/bluemoon \ @TOOLS_TRUE@ tools/hex2hcd tools/mpris-proxy tools/btattach tools/isotest @TOOLS_TRUE@am__append_56 = tools/bdaddr tools/avinfo tools/avtest \ @TOOLS_TRUE@ tools/scotest tools/hwdb \ @TOOLS_TRUE@ tools/hcieventmask tools/hcisecfilter \ @TOOLS_TRUE@ tools/btinfo tools/btconfig \ @TOOLS_TRUE@ tools/btsnoop tools/btproxy \ @TOOLS_TRUE@ tools/btiotest tools/bneptest tools/mcaptest \ @TOOLS_TRUE@ tools/cltest tools/oobtest tools/advtest \ @TOOLS_TRUE@ tools/seq2bseq tools/nokfw tools/rtlfw \ @TOOLS_TRUE@ tools/bcmfw tools/create-image \ @TOOLS_TRUE@ tools/eddystone tools/ibeacon \ @TOOLS_TRUE@ tools/btgatt-client tools/btgatt-server \ @TOOLS_TRUE@ tools/test-runner tools/check-selftest \ @TOOLS_TRUE@ tools/gatt-service profiles/iap/iapd @SYSTEMD_TRUE@@TOOLS_TRUE@am__append_57 = tools/mpris-proxy.service @MANPAGES_TRUE@@TOOLS_TRUE@am__append_58 = tools/rctest.1 tools/l2ping.1 tools/btattach.1 tools/isotest.1 \ @MANPAGES_TRUE@@TOOLS_TRUE@ tools/btmgmt.1 client/bluetoothctl.1 \ @MANPAGES_TRUE@@TOOLS_TRUE@ client/bluetoothctl-mgmt.1 \ @MANPAGES_TRUE@@TOOLS_TRUE@ client/bluetoothctl-monitor.1 client/bluetoothctl-admin.1 \ @MANPAGES_TRUE@@TOOLS_TRUE@ client/bluetoothctl-advertise.1 client/bluetoothctl-endpoint.1 \ @MANPAGES_TRUE@@TOOLS_TRUE@ client/bluetoothctl-gatt.1 client/bluetoothctl-player.1 \ @MANPAGES_TRUE@@TOOLS_TRUE@ client/bluetoothctl-scan.1 client/bluetoothctl-transport.1 \ @MANPAGES_TRUE@@TOOLS_TRUE@ client/bluetoothctl-assistant.1 client/bluetoothctl-hci.1 @DEPRECATED_TRUE@@MESH_TRUE@@TOOLS_TRUE@am__append_59 = tools/meshctl @DEPRECATED_TRUE@@MESH_TRUE@@TOOLS_TRUE@am__append_60 = tools/mesh-gatt/local_node.json tools/mesh-gatt/prov_db.json @MESH_TRUE@@TOOLS_TRUE@am__append_61 = tools/mesh-cfgclient \ @MESH_TRUE@@TOOLS_TRUE@ tools/mesh-cfgtest @DEPRECATED_TRUE@@TOOLS_TRUE@am__append_62 = tools/hciattach tools/hciconfig tools/hcitool tools/hcidump \ @DEPRECATED_TRUE@@TOOLS_TRUE@ tools/rfcomm tools/sdptool tools/ciptool @DEPRECATED_TRUE@@MANPAGES_TRUE@@TOOLS_TRUE@am__append_63 = tools/hciattach.1 tools/hciconfig.1 \ @DEPRECATED_TRUE@@MANPAGES_TRUE@@TOOLS_TRUE@ tools/hcitool.1 tools/hcidump.1 \ @DEPRECATED_TRUE@@MANPAGES_TRUE@@TOOLS_TRUE@ tools/rfcomm.1 tools/sdptool.1 tools/ciptool.1 @HID2HCI_TRUE@udev_PROGRAMS = tools/hid2hci$(EXEEXT) @HID2HCI_TRUE@@MANPAGES_TRUE@am__append_64 = tools/hid2hci.1 @READLINE_TRUE@am__append_65 = tools/btmgmt tools/obex-client-tool tools/obex-server-tool \ @READLINE_TRUE@ tools/bluetooth-player tools/obexctl @DEPRECATED_TRUE@@READLINE_TRUE@am__append_66 = attrib/gatttool @CUPS_TRUE@cups_PROGRAMS = profiles/cups/bluetooth$(EXEEXT) @BTPCLIENT_TRUE@am__append_67 = tools/btpclient tools/btpclientctl # SPDX-License-Identifier: GPL-2.0 @OBEX_TRUE@@SYSTEMD_TRUE@am__append_68 = obexd/src/obex.service @EXPERIMENTAL_TRUE@@OBEX_TRUE@am__append_69 = pcsuite @EXPERIMENTAL_TRUE@@OBEX_TRUE@am__append_70 = obexd/plugins/pcsuite.c @OBEX_TRUE@am__append_71 = obexd/plugins/phonebook-dummy.c obexd/plugins/phonebook-ebook.c \ @OBEX_TRUE@ obexd/plugins/phonebook-tracker.c @OBEX_TRUE@am__append_72 = obexd/src/obexd @ANDROID_TRUE@am__append_73 = -DANDROID_VERSION=0x050100 @ANDROID_TRUE@am__append_74 = android/system-emulator \ @ANDROID_TRUE@ android/bluetoothd-snoop android/bluetoothd \ @ANDROID_TRUE@ android/avdtptest android/haltest \ @ANDROID_TRUE@ android/android-tester android/ipc-tester @ANDROID_TRUE@am__append_75 = android/bluetooth.default.la \ @ANDROID_TRUE@ android/audio.a2dp.default.la \ @ANDROID_TRUE@ android/audio.sco.default.la @ANDROID_TRUE@am__append_76 = android/test-ipc # SPDX-License-Identifier: GPL-2.0 @DATAFILES_TRUE@@MESH_TRUE@am__append_77 = mesh/bluetooth-mesh.conf @DATAFILES_TRUE@@MESH_TRUE@am__append_78 = mesh/mesh-main.conf @MESH_TRUE@@SYSTEMD_TRUE@am__append_79 = mesh/bluetooth-mesh.service @MESH_TRUE@@SYSTEMD_TRUE@am__append_80 = mesh/org.bluez.mesh.service @MESH_TRUE@am__append_81 = mesh/bluetooth-meshd @MANPAGES_TRUE@@MESH_TRUE@am__append_82 = mesh/bluetooth-meshd.8 @MESH_TRUE@am__append_83 = mesh/bluetooth-meshd.8 @HID2HCI_TRUE@am__append_84 = $(rules_DATA) @OBEX_TRUE@am__append_85 = unit/test-gobex-header unit/test-gobex-packet unit/test-gobex \ @OBEX_TRUE@ unit/test-gobex-transfer unit/test-gobex-apparam @MIDI_TRUE@am__append_86 = unit/test-midi @MESH_TRUE@am__append_87 = unit/test-mesh-crypto @MAINTAINER_MODE_TRUE@am__append_88 = $(unit_tests) TESTS = $(am__EXEEXT_17) @DBUS_RUN_SESSION_TRUE@am__append_89 = dbus-run-session -- subdir = . ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 am__aclocal_m4_deps = $(top_srcdir)/acinclude.m4 \ $(top_srcdir)/configure.ac am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \ $(ACLOCAL_M4) DIST_COMMON = $(srcdir)/Makefile.am $(top_srcdir)/configure \ $(am__configure_deps) $(am__dist_zshcompletion_DATA_DIST) \ $(am__pkginclude_HEADERS_DIST) $(am__DIST_COMMON) am__CONFIG_DISTCLEAN_FILES = config.status config.cache config.log \ configure.lineno config.status.lineno mkinstalldirs = $(install_sh) -d CONFIG_HEADER = config.h CONFIG_CLEAN_FILES = lib/bluez.pc mesh/bluetooth-meshd.rst \ mesh/bluetooth-mesh.service obexd/src/obex.service \ obexd/src/org.bluez.obex.service src/bluetoothd.rst \ src/bluetooth.service tools/bluetooth-logger.service \ tools/mpris-proxy.service CONFIG_CLEAN_VPATH_FILES = @CLIENT_TRUE@am__EXEEXT_1 = client/bluetoothctl$(EXEEXT) @MONITOR_TRUE@am__EXEEXT_2 = monitor/btmon$(EXEEXT) @TOOLS_TRUE@am__EXEEXT_3 = tools/rctest$(EXEEXT) tools/l2test$(EXEEXT) \ @TOOLS_TRUE@ tools/l2ping$(EXEEXT) tools/bluemoon$(EXEEXT) \ @TOOLS_TRUE@ tools/hex2hcd$(EXEEXT) tools/mpris-proxy$(EXEEXT) \ @TOOLS_TRUE@ tools/btattach$(EXEEXT) tools/isotest$(EXEEXT) @DEPRECATED_TRUE@@MESH_TRUE@@TOOLS_TRUE@am__EXEEXT_4 = tools/meshctl$(EXEEXT) @MESH_TRUE@@TOOLS_TRUE@am__EXEEXT_5 = tools/mesh-cfgclient$(EXEEXT) \ @MESH_TRUE@@TOOLS_TRUE@ tools/mesh-cfgtest$(EXEEXT) @DEPRECATED_TRUE@@TOOLS_TRUE@am__EXEEXT_6 = tools/hciattach$(EXEEXT) \ @DEPRECATED_TRUE@@TOOLS_TRUE@ tools/hciconfig$(EXEEXT) \ @DEPRECATED_TRUE@@TOOLS_TRUE@ tools/hcitool$(EXEEXT) \ @DEPRECATED_TRUE@@TOOLS_TRUE@ tools/hcidump$(EXEEXT) \ @DEPRECATED_TRUE@@TOOLS_TRUE@ tools/rfcomm$(EXEEXT) \ @DEPRECATED_TRUE@@TOOLS_TRUE@ tools/sdptool$(EXEEXT) \ @DEPRECATED_TRUE@@TOOLS_TRUE@ tools/ciptool$(EXEEXT) am__installdirs = "$(DESTDIR)$(bindir)" "$(DESTDIR)$(cupsdir)" \ "$(DESTDIR)$(pkglibexecdir)" "$(DESTDIR)$(udevdir)" \ "$(DESTDIR)$(libdir)" "$(DESTDIR)$(plugindir)" \ "$(DESTDIR)$(testdir)" "$(DESTDIR)$(man1dir)" \ "$(DESTDIR)$(man5dir)" "$(DESTDIR)$(man7dir)" \ "$(DESTDIR)$(man8dir)" "$(DESTDIR)$(confdir)" \ "$(DESTDIR)$(dbusdir)" "$(DESTDIR)$(dbussessionbusdir)" \ "$(DESTDIR)$(dbussystembusdir)" \ "$(DESTDIR)$(zshcompletiondir)" "$(DESTDIR)$(pkgconfigdir)" \ "$(DESTDIR)$(rulesdir)" "$(DESTDIR)$(statedir)" \ "$(DESTDIR)$(systemdsystemunitdir)" \ "$(DESTDIR)$(systemduserunitdir)" "$(DESTDIR)$(pkgincludedir)" @TESTING_TRUE@am__EXEEXT_7 = emulator/btvirt$(EXEEXT) \ @TESTING_TRUE@ emulator/b1ee$(EXEEXT) emulator/hfp$(EXEEXT) \ @TESTING_TRUE@ peripheral/btsensor$(EXEEXT) tools/3dsp$(EXEEXT) \ @TESTING_TRUE@ tools/mgmt-tester$(EXEEXT) \ @TESTING_TRUE@ tools/gap-tester$(EXEEXT) \ @TESTING_TRUE@ tools/l2cap-tester$(EXEEXT) \ @TESTING_TRUE@ tools/sco-tester$(EXEEXT) \ @TESTING_TRUE@ tools/smp-tester$(EXEEXT) \ @TESTING_TRUE@ tools/hci-tester$(EXEEXT) \ @TESTING_TRUE@ tools/rfcomm-tester$(EXEEXT) \ @TESTING_TRUE@ tools/bnep-tester$(EXEEXT) \ @TESTING_TRUE@ tools/userchan-tester$(EXEEXT) \ @TESTING_TRUE@ tools/iso-tester$(EXEEXT) \ @TESTING_TRUE@ tools/mesh-tester$(EXEEXT) \ @TESTING_TRUE@ tools/ioctl-tester$(EXEEXT) @TOOLS_TRUE@am__EXEEXT_8 = tools/bdaddr$(EXEEXT) tools/avinfo$(EXEEXT) \ @TOOLS_TRUE@ tools/avtest$(EXEEXT) tools/scotest$(EXEEXT) \ @TOOLS_TRUE@ tools/hwdb$(EXEEXT) tools/hcieventmask$(EXEEXT) \ @TOOLS_TRUE@ tools/hcisecfilter$(EXEEXT) tools/btinfo$(EXEEXT) \ @TOOLS_TRUE@ tools/btconfig$(EXEEXT) tools/btsnoop$(EXEEXT) \ @TOOLS_TRUE@ tools/btproxy$(EXEEXT) tools/btiotest$(EXEEXT) \ @TOOLS_TRUE@ tools/bneptest$(EXEEXT) tools/mcaptest$(EXEEXT) \ @TOOLS_TRUE@ tools/cltest$(EXEEXT) tools/oobtest$(EXEEXT) \ @TOOLS_TRUE@ tools/advtest$(EXEEXT) tools/seq2bseq$(EXEEXT) \ @TOOLS_TRUE@ tools/nokfw$(EXEEXT) tools/rtlfw$(EXEEXT) \ @TOOLS_TRUE@ tools/bcmfw$(EXEEXT) tools/create-image$(EXEEXT) \ @TOOLS_TRUE@ tools/eddystone$(EXEEXT) tools/ibeacon$(EXEEXT) \ @TOOLS_TRUE@ tools/btgatt-client$(EXEEXT) \ @TOOLS_TRUE@ tools/btgatt-server$(EXEEXT) \ @TOOLS_TRUE@ tools/test-runner$(EXEEXT) \ @TOOLS_TRUE@ tools/check-selftest$(EXEEXT) \ @TOOLS_TRUE@ tools/gatt-service$(EXEEXT) \ @TOOLS_TRUE@ profiles/iap/iapd$(EXEEXT) @READLINE_TRUE@am__EXEEXT_9 = tools/btmgmt$(EXEEXT) \ @READLINE_TRUE@ tools/obex-client-tool$(EXEEXT) \ @READLINE_TRUE@ tools/obex-server-tool$(EXEEXT) \ @READLINE_TRUE@ tools/bluetooth-player$(EXEEXT) \ @READLINE_TRUE@ tools/obexctl$(EXEEXT) @DEPRECATED_TRUE@@READLINE_TRUE@am__EXEEXT_10 = \ @DEPRECATED_TRUE@@READLINE_TRUE@ attrib/gatttool$(EXEEXT) @BTPCLIENT_TRUE@am__EXEEXT_11 = tools/btpclient$(EXEEXT) \ @BTPCLIENT_TRUE@ tools/btpclientctl$(EXEEXT) @ANDROID_TRUE@am__EXEEXT_12 = android/system-emulator$(EXEEXT) \ @ANDROID_TRUE@ android/bluetoothd-snoop$(EXEEXT) \ @ANDROID_TRUE@ android/bluetoothd$(EXEEXT) \ @ANDROID_TRUE@ android/avdtptest$(EXEEXT) \ @ANDROID_TRUE@ android/haltest$(EXEEXT) \ @ANDROID_TRUE@ android/android-tester$(EXEEXT) \ @ANDROID_TRUE@ android/ipc-tester$(EXEEXT) @ANDROID_TRUE@am__EXEEXT_13 = android/test-ipc$(EXEEXT) @OBEX_TRUE@am__EXEEXT_14 = unit/test-gobex-header$(EXEEXT) \ @OBEX_TRUE@ unit/test-gobex-packet$(EXEEXT) \ @OBEX_TRUE@ unit/test-gobex$(EXEEXT) \ @OBEX_TRUE@ unit/test-gobex-transfer$(EXEEXT) \ @OBEX_TRUE@ unit/test-gobex-apparam$(EXEEXT) @MIDI_TRUE@am__EXEEXT_15 = unit/test-midi$(EXEEXT) @MESH_TRUE@am__EXEEXT_16 = unit/test-mesh-crypto$(EXEEXT) am__EXEEXT_17 = $(am__EXEEXT_13) unit/test-tester$(EXEEXT) \ unit/test-eir$(EXEEXT) unit/test-uuid$(EXEEXT) \ unit/test-textfile$(EXEEXT) unit/test-crc$(EXEEXT) \ unit/test-crypto$(EXEEXT) unit/test-ecc$(EXEEXT) \ unit/test-ringbuf$(EXEEXT) unit/test-queue$(EXEEXT) \ unit/test-mgmt$(EXEEXT) unit/test-uhid$(EXEEXT) \ unit/test-sdp$(EXEEXT) unit/test-avdtp$(EXEEXT) \ unit/test-avctp$(EXEEXT) unit/test-avrcp$(EXEEXT) \ unit/test-hfp$(EXEEXT) unit/test-gdbus-client$(EXEEXT) \ $(am__EXEEXT_14) unit/test-lib$(EXEEXT) \ unit/test-gatt$(EXEEXT) unit/test-hog$(EXEEXT) \ unit/test-gattrib$(EXEEXT) unit/test-bap$(EXEEXT) \ unit/test-micp$(EXEEXT) unit/test-bass$(EXEEXT) \ unit/test-vcp$(EXEEXT) $(am__EXEEXT_15) $(am__EXEEXT_16) @MAINTAINER_MODE_TRUE@am__EXEEXT_18 = $(am__EXEEXT_17) @LOGGER_TRUE@am__EXEEXT_19 = tools/btmon-logger$(EXEEXT) @OBEX_TRUE@am__EXEEXT_20 = obexd/src/obexd$(EXEEXT) @MESH_TRUE@am__EXEEXT_21 = mesh/bluetooth-meshd$(EXEEXT) PROGRAMS = $(bin_PROGRAMS) $(cups_PROGRAMS) $(noinst_PROGRAMS) \ $(pkglibexec_PROGRAMS) $(udev_PROGRAMS) LIBRARIES = $(noinst_LIBRARIES) am__vpath_adj_setup = srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`; am__vpath_adj = case $$p in \ $(srcdir)/*) f=`echo "$$p" | sed "s|^$$srcdirstrip/||"`;; \ *) f=$$p;; \ esac; am__strip_dir = f=`echo $$p | sed -e 's|^.*/||'`; am__install_max = 40 am__nobase_strip_setup = \ srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*|]/\\\\&/g'` am__nobase_strip = \ for p in $$list; do echo "$$p"; done | sed -e "s|$$srcdirstrip/||" am__nobase_list = $(am__nobase_strip_setup); \ for p in $$list; do echo "$$p $$p"; done | \ sed "s| $$srcdirstrip/| |;"' / .*\//!s/ .*/ ./; s,\( .*\)/[^/]*$$,\1,' | \ $(AWK) 'BEGIN { files["."] = "" } { files[$$2] = files[$$2] " " $$1; \ if (++n[$$2] == $(am__install_max)) \ { print $$2, files[$$2]; n[$$2] = 0; files[$$2] = "" } } \ END { for (dir in files) print dir, files[dir] }' am__base_list = \ sed '$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;s/\n/ /g' | \ sed '$$!N;$$!N;$$!N;$$!N;s/\n/ /g' am__uninstall_files_from_dir = { \ test -z "$$files" \ || { test ! -d "$$dir" && test ! -f "$$dir" && test ! -r "$$dir"; } \ || { echo " ( cd '$$dir' && rm -f" $$files ")"; \ $(am__cd) "$$dir" && rm -f $$files; }; \ } LTLIBRARIES = $(lib_LTLIBRARIES) $(noinst_LTLIBRARIES) \ $(plugin_LTLIBRARIES) am__DEPENDENCIES_1 = @ANDROID_TRUE@android_audio_a2dp_default_la_DEPENDENCIES = \ @ANDROID_TRUE@ $(am__DEPENDENCIES_1) am__android_audio_a2dp_default_la_SOURCES_DIST = android/audio-msg.h \ android/hal-msg.h android/hal-audio.h android/hal-audio.c \ android/hal-audio-sbc.c android/hal-audio-aptx.c \ android/hardware/audio.h android/hardware/audio_effect.h \ android/hardware/hardware.h android/system/audio.h am__dirstamp = $(am__leading_dot)dirstamp @ANDROID_TRUE@am_android_audio_a2dp_default_la_OBJECTS = \ @ANDROID_TRUE@ android/audio_a2dp_default_la-hal-audio.lo \ @ANDROID_TRUE@ android/audio_a2dp_default_la-hal-audio-sbc.lo \ @ANDROID_TRUE@ android/audio_a2dp_default_la-hal-audio-aptx.lo android_audio_a2dp_default_la_OBJECTS = \ $(am_android_audio_a2dp_default_la_OBJECTS) AM_V_lt = $(am__v_lt_@AM_V@) am__v_lt_ = $(am__v_lt_@AM_DEFAULT_V@) am__v_lt_0 = --silent am__v_lt_1 = android_audio_a2dp_default_la_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC \ $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=link $(CCLD) \ $(android_audio_a2dp_default_la_CFLAGS) $(CFLAGS) \ $(android_audio_a2dp_default_la_LDFLAGS) $(LDFLAGS) -o $@ @ANDROID_TRUE@am_android_audio_a2dp_default_la_rpath = -rpath \ @ANDROID_TRUE@ $(plugindir) @ANDROID_TRUE@android_audio_sco_default_la_DEPENDENCIES = \ @ANDROID_TRUE@ $(am__DEPENDENCIES_1) am__android_audio_sco_default_la_SOURCES_DIST = android/hal-log.h \ android/sco-msg.h android/hal-sco.c android/hardware/audio.h \ android/hardware/audio_effect.h android/hardware/hardware.h \ android/audio_utils/resampler.c \ android/audio_utils/resampler.h android/system/audio.h @ANDROID_TRUE@am_android_audio_sco_default_la_OBJECTS = \ @ANDROID_TRUE@ android/audio_sco_default_la-hal-sco.lo \ @ANDROID_TRUE@ android/audio_utils/audio_sco_default_la-resampler.lo android_audio_sco_default_la_OBJECTS = \ $(am_android_audio_sco_default_la_OBJECTS) android_audio_sco_default_la_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC \ $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=link $(CCLD) \ $(android_audio_sco_default_la_CFLAGS) $(CFLAGS) \ $(android_audio_sco_default_la_LDFLAGS) $(LDFLAGS) -o $@ @ANDROID_TRUE@am_android_audio_sco_default_la_rpath = -rpath \ @ANDROID_TRUE@ $(plugindir) android_bluetooth_default_la_LIBADD = am__android_bluetooth_default_la_SOURCES_DIST = android/hal.h \ android/hal-bluetooth.c android/hal-socket.c \ android/hal-hidhost.c android/hal-health.c android/hal-pan.c \ android/hal-a2dp.c android/hal-a2dp-sink.c android/hal-avrcp.c \ android/hal-avrcp-ctrl.c android/hal-handsfree.c \ android/hal-handsfree-client.c android/hal-gatt.c \ android/hal-map-client.c android/hardware/bluetooth.h \ android/hardware/bt_av.h android/hardware/bt_gatt.h \ android/hardware/bt_gatt_client.h \ android/hardware/bt_gatt_server.h \ android/hardware/bt_gatt_types.h android/hardware/bt_hf.h \ android/hardware/bt_hh.h android/hardware/bt_hl.h \ android/hardware/bt_pan.h android/hardware/bt_rc.h \ android/hardware/bt_sock.h android/hardware/bt_hf_client.h \ android/hardware/bt_mce.h android/hardware/hardware.h \ android/cutils/properties.h android/ipc-common.h \ android/hal-log.h android/hal-ipc.h android/hal-ipc.c \ android/hal-utils.h android/hal-utils.c @ANDROID_TRUE@am_android_bluetooth_default_la_OBJECTS = \ @ANDROID_TRUE@ android/bluetooth_default_la-hal-bluetooth.lo \ @ANDROID_TRUE@ android/bluetooth_default_la-hal-socket.lo \ @ANDROID_TRUE@ android/bluetooth_default_la-hal-hidhost.lo \ @ANDROID_TRUE@ android/bluetooth_default_la-hal-health.lo \ @ANDROID_TRUE@ android/bluetooth_default_la-hal-pan.lo \ @ANDROID_TRUE@ android/bluetooth_default_la-hal-a2dp.lo \ @ANDROID_TRUE@ android/bluetooth_default_la-hal-a2dp-sink.lo \ @ANDROID_TRUE@ android/bluetooth_default_la-hal-avrcp.lo \ @ANDROID_TRUE@ android/bluetooth_default_la-hal-avrcp-ctrl.lo \ @ANDROID_TRUE@ android/bluetooth_default_la-hal-handsfree.lo \ @ANDROID_TRUE@ android/bluetooth_default_la-hal-handsfree-client.lo \ @ANDROID_TRUE@ android/bluetooth_default_la-hal-gatt.lo \ @ANDROID_TRUE@ android/bluetooth_default_la-hal-map-client.lo \ @ANDROID_TRUE@ android/bluetooth_default_la-hal-ipc.lo \ @ANDROID_TRUE@ android/bluetooth_default_la-hal-utils.lo android_bluetooth_default_la_OBJECTS = \ $(am_android_bluetooth_default_la_OBJECTS) android_bluetooth_default_la_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC \ $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=link $(CCLD) \ $(android_bluetooth_default_la_CFLAGS) $(CFLAGS) \ $(android_bluetooth_default_la_LDFLAGS) $(LDFLAGS) -o $@ @ANDROID_TRUE@am_android_bluetooth_default_la_rpath = -rpath \ @ANDROID_TRUE@ $(plugindir) ell_libell_internal_la_LIBADD = am__ell_libell_internal_la_SOURCES_DIST = ell/util.h ell/log.h \ ell/queue.h ell/hashmap.h ell/random.h ell/signal.h ell/time.h \ ell/time-private.h ell/timeout.h ell/cipher.h ell/checksum.h \ ell/io.h ell/idle.h ell/main.h ell/settings.h ell/strv.h \ ell/string.h ell/utf8.h ell/dbus.h ell/dbus-service.h \ ell/dbus-client.h ell/key.h ell/cert.h ell/pem.h ell/base64.h \ ell/asn1-private.h ell/cert-private.h ell/pem-private.h \ ell/uuid.h ell/useful.h ell/main-private.h ell/tester.h \ ell/tls.h ell/tls-private.h ell/ecc.h ell/ecc-private.h \ ell/cleanup.h ell/ecdh.h ell/private.h ell/missing.h \ ell/util.c ell/log.c ell/queue.c ell/hashmap.c ell/random.c \ ell/signal.c ell/time.c ell/timeout.c ell/io.c ell/idle.c \ ell/main.c ell/settings.c ell/strv.c ell/string.c ell/cipher.c \ ell/checksum.c ell/pem.c ell/cert.c ell/cert-crypto.c \ ell/key.c ell/base64.c ell/utf8.c ell/dbus-private.h \ ell/dbus.c ell/dbus-message.c ell/dbus-util.c \ ell/dbus-service.c ell/dbus-client.c ell/dbus-name-cache.c \ ell/dbus-filter.c ell/gvariant-private.h ell/gvariant-util.c \ ell/siphash-private.h ell/siphash.c ell/uuid.c ell/tester.c \ ell/tls.c ell/tls-extensions.c ell/tls-suites.c \ ell/tls-record.c ell/ecc.c ell/ecc-external.c ell/ecdh.c am__objects_1 = @EXTERNAL_ELL_FALSE@am__objects_2 = ell/util.lo ell/log.lo \ @EXTERNAL_ELL_FALSE@ ell/queue.lo ell/hashmap.lo ell/random.lo \ @EXTERNAL_ELL_FALSE@ ell/signal.lo ell/time.lo ell/timeout.lo \ @EXTERNAL_ELL_FALSE@ ell/io.lo ell/idle.lo ell/main.lo \ @EXTERNAL_ELL_FALSE@ ell/settings.lo ell/strv.lo ell/string.lo \ @EXTERNAL_ELL_FALSE@ ell/cipher.lo ell/checksum.lo ell/pem.lo \ @EXTERNAL_ELL_FALSE@ ell/cert.lo ell/cert-crypto.lo ell/key.lo \ @EXTERNAL_ELL_FALSE@ ell/base64.lo ell/utf8.lo ell/dbus.lo \ @EXTERNAL_ELL_FALSE@ ell/dbus-message.lo ell/dbus-util.lo \ @EXTERNAL_ELL_FALSE@ ell/dbus-service.lo ell/dbus-client.lo \ @EXTERNAL_ELL_FALSE@ ell/dbus-name-cache.lo ell/dbus-filter.lo \ @EXTERNAL_ELL_FALSE@ ell/gvariant-util.lo ell/siphash.lo \ @EXTERNAL_ELL_FALSE@ ell/uuid.lo ell/tester.lo ell/tls.lo \ @EXTERNAL_ELL_FALSE@ ell/tls-extensions.lo ell/tls-suites.lo \ @EXTERNAL_ELL_FALSE@ ell/tls-record.lo ell/ecc.lo \ @EXTERNAL_ELL_FALSE@ ell/ecc-external.lo ell/ecdh.lo @EXTERNAL_ELL_FALSE@am_ell_libell_internal_la_OBJECTS = \ @EXTERNAL_ELL_FALSE@ $(am__objects_1) $(am__objects_2) \ @EXTERNAL_ELL_FALSE@ $(am__objects_1) ell_libell_internal_la_OBJECTS = $(am_ell_libell_internal_la_OBJECTS) @EXTERNAL_ELL_FALSE@am_ell_libell_internal_la_rpath = gdbus_libgdbus_internal_la_LIBADD = am_gdbus_libgdbus_internal_la_OBJECTS = gdbus/mainloop.lo \ gdbus/watch.lo gdbus/object.lo gdbus/client.lo gdbus/polkit.lo gdbus_libgdbus_internal_la_OBJECTS = \ $(am_gdbus_libgdbus_internal_la_OBJECTS) lib_libbluetooth_internal_la_LIBADD = am__objects_3 = lib/bluetooth.lo lib/hci.lo lib/sdp.lo am__objects_4 = lib/uuid.lo am_lib_libbluetooth_internal_la_OBJECTS = $(am__objects_1) \ $(am__objects_3) $(am__objects_1) $(am__objects_4) lib_libbluetooth_internal_la_OBJECTS = \ $(am_lib_libbluetooth_internal_la_OBJECTS) lib_libbluetooth_la_LIBADD = am__lib_libbluetooth_la_SOURCES_DIST = lib/bluetooth.h lib/hci.h \ lib/hci_lib.h lib/sco.h lib/l2cap.h lib/sdp.h lib/sdp_lib.h \ lib/rfcomm.h lib/bnep.h lib/cmtp.h lib/hidp.h lib/bluetooth.c \ lib/hci.c lib/sdp.c @LIBRARY_TRUE@am_lib_libbluetooth_la_OBJECTS = $(am__objects_1) \ @LIBRARY_TRUE@ $(am__objects_3) lib_libbluetooth_la_OBJECTS = $(am_lib_libbluetooth_la_OBJECTS) lib_libbluetooth_la_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC \ $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=link $(CCLD) \ $(AM_CFLAGS) $(CFLAGS) $(lib_libbluetooth_la_LDFLAGS) \ $(LDFLAGS) -o $@ @LIBRARY_TRUE@am_lib_libbluetooth_la_rpath = -rpath $(libdir) src_libshared_ell_la_LIBADD = am__src_libshared_ell_la_SOURCES_DIST = src/shared/io.h \ src/shared/timeout.h src/shared/queue.h src/shared/queue.c \ src/shared/util.h src/shared/util.c src/shared/mgmt.h \ src/shared/mgmt.c src/shared/crypto.h src/shared/crypto.c \ src/shared/ecc.h src/shared/ecc.c src/shared/ringbuf.h \ src/shared/ringbuf.c src/shared/tester.h src/shared/hci.h \ src/shared/hci.c src/shared/hci-crypto.h \ src/shared/hci-crypto.c src/shared/hfp.h src/shared/hfp.c \ src/shared/uhid.h src/shared/uhid.c src/shared/pcap.h \ src/shared/pcap.c src/shared/btsnoop.h src/shared/btsnoop.c \ src/shared/ad.h src/shared/ad.c src/shared/att-types.h \ src/shared/att.h src/shared/att.c src/shared/gatt-helpers.h \ src/shared/gatt-helpers.c src/shared/gatt-client.h \ src/shared/gatt-client.c src/shared/gatt-server.h \ src/shared/gatt-server.c src/shared/gatt-db.h \ src/shared/gatt-db.c src/shared/gap.h src/shared/gap.c \ src/shared/log.h src/shared/log.c src/shared/bap.h \ src/shared/bap.c src/shared/ascs.h src/shared/bap-debug.h \ src/shared/bap-debug.c src/shared/mcs.h src/shared/mcp.h \ src/shared/mcp.c src/shared/vcp.c src/shared/vcp.h \ src/shared/micp.c src/shared/micp.h src/shared/csip.c \ src/shared/csip.h src/shared/bass.h src/shared/bass.c \ src/shared/ccp.h src/shared/ccp.c src/shared/lc3.h \ src/shared/tty.h src/shared/bap-defs.h src/shared/asha.h \ src/shared/asha.c src/shared/shell.c src/shared/shell.h \ src/shared/io-ell.c src/shared/timeout-ell.c \ src/shared/mainloop.h src/shared/mainloop-ell.c @READLINE_TRUE@am__objects_5 = src/shared/libshared_ell_la-shell.lo am__objects_6 = src/shared/libshared_ell_la-queue.lo \ src/shared/libshared_ell_la-util.lo \ src/shared/libshared_ell_la-mgmt.lo \ src/shared/libshared_ell_la-crypto.lo \ src/shared/libshared_ell_la-ecc.lo \ src/shared/libshared_ell_la-ringbuf.lo \ src/shared/libshared_ell_la-hci.lo \ src/shared/libshared_ell_la-hci-crypto.lo \ src/shared/libshared_ell_la-hfp.lo \ src/shared/libshared_ell_la-uhid.lo \ src/shared/libshared_ell_la-pcap.lo \ src/shared/libshared_ell_la-btsnoop.lo \ src/shared/libshared_ell_la-ad.lo \ src/shared/libshared_ell_la-att.lo \ src/shared/libshared_ell_la-gatt-helpers.lo \ src/shared/libshared_ell_la-gatt-client.lo \ src/shared/libshared_ell_la-gatt-server.lo \ src/shared/libshared_ell_la-gatt-db.lo \ src/shared/libshared_ell_la-gap.lo \ src/shared/libshared_ell_la-log.lo \ src/shared/libshared_ell_la-bap.lo \ src/shared/libshared_ell_la-bap-debug.lo \ src/shared/libshared_ell_la-mcp.lo \ src/shared/libshared_ell_la-vcp.lo \ src/shared/libshared_ell_la-micp.lo \ src/shared/libshared_ell_la-csip.lo \ src/shared/libshared_ell_la-bass.lo \ src/shared/libshared_ell_la-ccp.lo \ src/shared/libshared_ell_la-asha.lo $(am__objects_5) @LIBSHARED_ELL_TRUE@am_src_libshared_ell_la_OBJECTS = \ @LIBSHARED_ELL_TRUE@ $(am__objects_6) \ @LIBSHARED_ELL_TRUE@ src/shared/libshared_ell_la-io-ell.lo \ @LIBSHARED_ELL_TRUE@ src/shared/libshared_ell_la-timeout-ell.lo \ @LIBSHARED_ELL_TRUE@ src/shared/libshared_ell_la-mainloop-ell.lo src_libshared_ell_la_OBJECTS = $(am_src_libshared_ell_la_OBJECTS) src_libshared_ell_la_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC \ $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=link $(CCLD) \ $(src_libshared_ell_la_CFLAGS) $(CFLAGS) \ $(src_libshared_ell_la_LDFLAGS) $(LDFLAGS) -o $@ @LIBSHARED_ELL_TRUE@am_src_libshared_ell_la_rpath = src_libshared_glib_la_LIBADD = am__src_libshared_glib_la_SOURCES_DIST = src/shared/io.h \ src/shared/timeout.h src/shared/queue.h src/shared/queue.c \ src/shared/util.h src/shared/util.c src/shared/mgmt.h \ src/shared/mgmt.c src/shared/crypto.h src/shared/crypto.c \ src/shared/ecc.h src/shared/ecc.c src/shared/ringbuf.h \ src/shared/ringbuf.c src/shared/tester.h src/shared/hci.h \ src/shared/hci.c src/shared/hci-crypto.h \ src/shared/hci-crypto.c src/shared/hfp.h src/shared/hfp.c \ src/shared/uhid.h src/shared/uhid.c src/shared/pcap.h \ src/shared/pcap.c src/shared/btsnoop.h src/shared/btsnoop.c \ src/shared/ad.h src/shared/ad.c src/shared/att-types.h \ src/shared/att.h src/shared/att.c src/shared/gatt-helpers.h \ src/shared/gatt-helpers.c src/shared/gatt-client.h \ src/shared/gatt-client.c src/shared/gatt-server.h \ src/shared/gatt-server.c src/shared/gatt-db.h \ src/shared/gatt-db.c src/shared/gap.h src/shared/gap.c \ src/shared/log.h src/shared/log.c src/shared/bap.h \ src/shared/bap.c src/shared/ascs.h src/shared/bap-debug.h \ src/shared/bap-debug.c src/shared/mcs.h src/shared/mcp.h \ src/shared/mcp.c src/shared/vcp.c src/shared/vcp.h \ src/shared/micp.c src/shared/micp.h src/shared/csip.c \ src/shared/csip.h src/shared/bass.h src/shared/bass.c \ src/shared/ccp.h src/shared/ccp.c src/shared/lc3.h \ src/shared/tty.h src/shared/bap-defs.h src/shared/asha.h \ src/shared/asha.c src/shared/shell.c src/shared/shell.h \ src/shared/io-glib.c src/shared/timeout-glib.c \ src/shared/mainloop-glib.c src/shared/mainloop-notify.h \ src/shared/mainloop-notify.c src/shared/tester.c @READLINE_TRUE@am__objects_7 = src/shared/libshared_glib_la-shell.lo am__objects_8 = src/shared/libshared_glib_la-queue.lo \ src/shared/libshared_glib_la-util.lo \ src/shared/libshared_glib_la-mgmt.lo \ src/shared/libshared_glib_la-crypto.lo \ src/shared/libshared_glib_la-ecc.lo \ src/shared/libshared_glib_la-ringbuf.lo \ src/shared/libshared_glib_la-hci.lo \ src/shared/libshared_glib_la-hci-crypto.lo \ src/shared/libshared_glib_la-hfp.lo \ src/shared/libshared_glib_la-uhid.lo \ src/shared/libshared_glib_la-pcap.lo \ src/shared/libshared_glib_la-btsnoop.lo \ src/shared/libshared_glib_la-ad.lo \ src/shared/libshared_glib_la-att.lo \ src/shared/libshared_glib_la-gatt-helpers.lo \ src/shared/libshared_glib_la-gatt-client.lo \ src/shared/libshared_glib_la-gatt-server.lo \ src/shared/libshared_glib_la-gatt-db.lo \ src/shared/libshared_glib_la-gap.lo \ src/shared/libshared_glib_la-log.lo \ src/shared/libshared_glib_la-bap.lo \ src/shared/libshared_glib_la-bap-debug.lo \ src/shared/libshared_glib_la-mcp.lo \ src/shared/libshared_glib_la-vcp.lo \ src/shared/libshared_glib_la-micp.lo \ src/shared/libshared_glib_la-csip.lo \ src/shared/libshared_glib_la-bass.lo \ src/shared/libshared_glib_la-ccp.lo \ src/shared/libshared_glib_la-asha.lo $(am__objects_7) am_src_libshared_glib_la_OBJECTS = $(am__objects_8) \ src/shared/libshared_glib_la-io-glib.lo \ src/shared/libshared_glib_la-timeout-glib.lo \ src/shared/libshared_glib_la-mainloop-glib.lo \ src/shared/libshared_glib_la-mainloop-notify.lo \ src/shared/libshared_glib_la-tester.lo src_libshared_glib_la_OBJECTS = $(am_src_libshared_glib_la_OBJECTS) src_libshared_glib_la_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC \ $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=link $(CCLD) \ $(src_libshared_glib_la_CFLAGS) $(CFLAGS) \ $(src_libshared_glib_la_LDFLAGS) $(LDFLAGS) -o $@ src_libshared_mainloop_la_LIBADD = am__src_libshared_mainloop_la_SOURCES_DIST = src/shared/io.h \ src/shared/timeout.h src/shared/queue.h src/shared/queue.c \ src/shared/util.h src/shared/util.c src/shared/mgmt.h \ src/shared/mgmt.c src/shared/crypto.h src/shared/crypto.c \ src/shared/ecc.h src/shared/ecc.c src/shared/ringbuf.h \ src/shared/ringbuf.c src/shared/tester.h src/shared/hci.h \ src/shared/hci.c src/shared/hci-crypto.h \ src/shared/hci-crypto.c src/shared/hfp.h src/shared/hfp.c \ src/shared/uhid.h src/shared/uhid.c src/shared/pcap.h \ src/shared/pcap.c src/shared/btsnoop.h src/shared/btsnoop.c \ src/shared/ad.h src/shared/ad.c src/shared/att-types.h \ src/shared/att.h src/shared/att.c src/shared/gatt-helpers.h \ src/shared/gatt-helpers.c src/shared/gatt-client.h \ src/shared/gatt-client.c src/shared/gatt-server.h \ src/shared/gatt-server.c src/shared/gatt-db.h \ src/shared/gatt-db.c src/shared/gap.h src/shared/gap.c \ src/shared/log.h src/shared/log.c src/shared/bap.h \ src/shared/bap.c src/shared/ascs.h src/shared/bap-debug.h \ src/shared/bap-debug.c src/shared/mcs.h src/shared/mcp.h \ src/shared/mcp.c src/shared/vcp.c src/shared/vcp.h \ src/shared/micp.c src/shared/micp.h src/shared/csip.c \ src/shared/csip.h src/shared/bass.h src/shared/bass.c \ src/shared/ccp.h src/shared/ccp.c src/shared/lc3.h \ src/shared/tty.h src/shared/bap-defs.h src/shared/asha.h \ src/shared/asha.c src/shared/shell.c src/shared/shell.h \ src/shared/io-mainloop.c src/shared/timeout-mainloop.c \ src/shared/mainloop.h src/shared/mainloop.c \ src/shared/mainloop-notify.h src/shared/mainloop-notify.c @READLINE_TRUE@am__objects_9 = \ @READLINE_TRUE@ src/shared/libshared_mainloop_la-shell.lo am__objects_10 = src/shared/libshared_mainloop_la-queue.lo \ src/shared/libshared_mainloop_la-util.lo \ src/shared/libshared_mainloop_la-mgmt.lo \ src/shared/libshared_mainloop_la-crypto.lo \ src/shared/libshared_mainloop_la-ecc.lo \ src/shared/libshared_mainloop_la-ringbuf.lo \ src/shared/libshared_mainloop_la-hci.lo \ src/shared/libshared_mainloop_la-hci-crypto.lo \ src/shared/libshared_mainloop_la-hfp.lo \ src/shared/libshared_mainloop_la-uhid.lo \ src/shared/libshared_mainloop_la-pcap.lo \ src/shared/libshared_mainloop_la-btsnoop.lo \ src/shared/libshared_mainloop_la-ad.lo \ src/shared/libshared_mainloop_la-att.lo \ src/shared/libshared_mainloop_la-gatt-helpers.lo \ src/shared/libshared_mainloop_la-gatt-client.lo \ src/shared/libshared_mainloop_la-gatt-server.lo \ src/shared/libshared_mainloop_la-gatt-db.lo \ src/shared/libshared_mainloop_la-gap.lo \ src/shared/libshared_mainloop_la-log.lo \ src/shared/libshared_mainloop_la-bap.lo \ src/shared/libshared_mainloop_la-bap-debug.lo \ src/shared/libshared_mainloop_la-mcp.lo \ src/shared/libshared_mainloop_la-vcp.lo \ src/shared/libshared_mainloop_la-micp.lo \ src/shared/libshared_mainloop_la-csip.lo \ src/shared/libshared_mainloop_la-bass.lo \ src/shared/libshared_mainloop_la-ccp.lo \ src/shared/libshared_mainloop_la-asha.lo $(am__objects_9) am_src_libshared_mainloop_la_OBJECTS = $(am__objects_10) \ src/shared/libshared_mainloop_la-io-mainloop.lo \ src/shared/libshared_mainloop_la-timeout-mainloop.lo \ src/shared/libshared_mainloop_la-mainloop.lo \ src/shared/libshared_mainloop_la-mainloop-notify.lo src_libshared_mainloop_la_OBJECTS = \ $(am_src_libshared_mainloop_la_OBJECTS) src_libshared_mainloop_la_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC \ $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=link $(CCLD) \ $(src_libshared_mainloop_la_CFLAGS) $(CFLAGS) \ $(src_libshared_mainloop_la_LDFLAGS) $(LDFLAGS) -o $@ am__android_android_tester_SOURCES_DIST = emulator/hciemu.h \ emulator/hciemu.c emulator/vhci.h emulator/vhci.c \ emulator/btdev.h emulator/btdev.c emulator/bthost.h \ emulator/bthost.c emulator/smp.c monitor/rfcomm.h \ android/hardware/hardware.c android/tester-bluetooth.c \ android/tester-socket.c android/tester-hidhost.c \ android/tester-pan.c android/tester-hdp.c \ android/tester-a2dp.c android/tester-avrcp.c \ android/tester-gatt.c android/tester-map-client.c \ android/tester-main.h android/tester-main.c @ANDROID_TRUE@am_android_android_tester_OBJECTS = emulator/android_android_tester-hciemu.$(OBJEXT) \ @ANDROID_TRUE@ emulator/android_android_tester-vhci.$(OBJEXT) \ @ANDROID_TRUE@ emulator/android_android_tester-btdev.$(OBJEXT) \ @ANDROID_TRUE@ emulator/android_android_tester-bthost.$(OBJEXT) \ @ANDROID_TRUE@ emulator/android_android_tester-smp.$(OBJEXT) \ @ANDROID_TRUE@ android/hardware/android_tester-hardware.$(OBJEXT) \ @ANDROID_TRUE@ android/android_tester-tester-bluetooth.$(OBJEXT) \ @ANDROID_TRUE@ android/android_tester-tester-socket.$(OBJEXT) \ @ANDROID_TRUE@ android/android_tester-tester-hidhost.$(OBJEXT) \ @ANDROID_TRUE@ android/android_tester-tester-pan.$(OBJEXT) \ @ANDROID_TRUE@ android/android_tester-tester-hdp.$(OBJEXT) \ @ANDROID_TRUE@ android/android_tester-tester-a2dp.$(OBJEXT) \ @ANDROID_TRUE@ android/android_tester-tester-avrcp.$(OBJEXT) \ @ANDROID_TRUE@ android/android_tester-tester-gatt.$(OBJEXT) \ @ANDROID_TRUE@ android/android_tester-tester-map-client.$(OBJEXT) \ @ANDROID_TRUE@ android/android_tester-tester-main.$(OBJEXT) android_android_tester_OBJECTS = $(am_android_android_tester_OBJECTS) @ANDROID_TRUE@android_android_tester_DEPENDENCIES = \ @ANDROID_TRUE@ lib/libbluetooth-internal.la \ @ANDROID_TRUE@ src/libshared-glib.la $(am__DEPENDENCIES_1) android_android_tester_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC \ $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=link $(CCLD) \ $(AM_CFLAGS) $(CFLAGS) $(android_android_tester_LDFLAGS) \ $(LDFLAGS) -o $@ am__android_avdtptest_SOURCES_DIST = android/avdtptest.c src/log.h \ src/log.c btio/btio.h btio/btio.c src/shared/util.h \ src/shared/util.c src/shared/queue.h src/shared/queue.c \ src/shared/log.h src/shared/log.c android/avdtp.h \ android/avdtp.c @ANDROID_TRUE@am_android_avdtptest_OBJECTS = \ @ANDROID_TRUE@ android/avdtptest-avdtptest.$(OBJEXT) \ @ANDROID_TRUE@ src/android_avdtptest-log.$(OBJEXT) \ @ANDROID_TRUE@ btio/android_avdtptest-btio.$(OBJEXT) \ @ANDROID_TRUE@ src/shared/android_avdtptest-util.$(OBJEXT) \ @ANDROID_TRUE@ src/shared/android_avdtptest-queue.$(OBJEXT) \ @ANDROID_TRUE@ src/shared/android_avdtptest-log.$(OBJEXT) \ @ANDROID_TRUE@ android/avdtptest-avdtp.$(OBJEXT) android_avdtptest_OBJECTS = $(am_android_avdtptest_OBJECTS) @ANDROID_TRUE@android_avdtptest_DEPENDENCIES = \ @ANDROID_TRUE@ lib/libbluetooth-internal.la \ @ANDROID_TRUE@ $(am__DEPENDENCIES_1) android_avdtptest_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC \ $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=link $(CCLD) \ $(android_avdtptest_CFLAGS) $(CFLAGS) $(AM_LDFLAGS) $(LDFLAGS) \ -o $@ am__android_bluetoothd_SOURCES_DIST = android/main.c src/log.c \ android/hal-msg.h android/audio-msg.h android/sco-msg.h \ android/utils.h src/sdpd-database.c src/sdpd-server.c \ src/sdpd-service.c src/sdpd-request.c src/uuid-helper.h \ src/uuid-helper.c src/eir.h src/eir.c android/bluetooth.h \ android/bluetooth.c android/hidhost.h android/hidhost.c \ profiles/scanparam/scpp.h profiles/scanparam/scpp.c \ profiles/deviceinfo/dis.h profiles/deviceinfo/dis.c \ profiles/battery/bas.h profiles/battery/bas.c \ profiles/input/hog-lib.h profiles/input/hog-lib.c \ android/ipc-common.h android/ipc.h android/ipc.c \ android/avdtp.h android/avdtp.c android/a2dp.h android/a2dp.c \ android/a2dp-sink.h android/a2dp-sink.c android/avctp.h \ android/avctp.c android/avrcp.h android/avrcp.c \ android/avrcp-lib.h android/avrcp-lib.c android/socket.h \ android/socket.c android/sco.h android/sco.c android/pan.h \ android/pan.c android/handsfree.h android/handsfree.c \ android/handsfree-client.c android/handsfree-client.h \ android/gatt.h android/gatt.c android/health.h \ android/health.c profiles/health/mcap.h profiles/health/mcap.c \ android/map-client.h android/map-client.c attrib/att.c \ attrib/att.h attrib/gatt.c attrib/gatt.h attrib/gattrib.c \ attrib/gattrib.h btio/btio.h btio/btio.c src/sdp-client.h \ src/sdp-client.c profiles/network/bnep.h \ profiles/network/bnep.c @ANDROID_TRUE@am_android_bluetoothd_OBJECTS = android/main.$(OBJEXT) \ @ANDROID_TRUE@ src/log.$(OBJEXT) src/sdpd-database.$(OBJEXT) \ @ANDROID_TRUE@ src/sdpd-server.$(OBJEXT) \ @ANDROID_TRUE@ src/sdpd-service.$(OBJEXT) \ @ANDROID_TRUE@ src/sdpd-request.$(OBJEXT) \ @ANDROID_TRUE@ src/uuid-helper.$(OBJEXT) src/eir.$(OBJEXT) \ @ANDROID_TRUE@ android/bluetooth.$(OBJEXT) \ @ANDROID_TRUE@ android/hidhost.$(OBJEXT) \ @ANDROID_TRUE@ profiles/scanparam/scpp.$(OBJEXT) \ @ANDROID_TRUE@ profiles/deviceinfo/dis.$(OBJEXT) \ @ANDROID_TRUE@ profiles/battery/bas.$(OBJEXT) \ @ANDROID_TRUE@ profiles/input/hog-lib.$(OBJEXT) \ @ANDROID_TRUE@ android/ipc.$(OBJEXT) android/avdtp.$(OBJEXT) \ @ANDROID_TRUE@ android/a2dp.$(OBJEXT) \ @ANDROID_TRUE@ android/a2dp-sink.$(OBJEXT) \ @ANDROID_TRUE@ android/avctp.$(OBJEXT) android/avrcp.$(OBJEXT) \ @ANDROID_TRUE@ android/avrcp-lib.$(OBJEXT) \ @ANDROID_TRUE@ android/socket.$(OBJEXT) android/sco.$(OBJEXT) \ @ANDROID_TRUE@ android/pan.$(OBJEXT) \ @ANDROID_TRUE@ android/handsfree.$(OBJEXT) \ @ANDROID_TRUE@ android/handsfree-client.$(OBJEXT) \ @ANDROID_TRUE@ android/gatt.$(OBJEXT) android/health.$(OBJEXT) \ @ANDROID_TRUE@ profiles/health/mcap.$(OBJEXT) \ @ANDROID_TRUE@ android/map-client.$(OBJEXT) \ @ANDROID_TRUE@ attrib/att.$(OBJEXT) attrib/gatt.$(OBJEXT) \ @ANDROID_TRUE@ attrib/gattrib.$(OBJEXT) btio/btio.$(OBJEXT) \ @ANDROID_TRUE@ src/sdp-client.$(OBJEXT) \ @ANDROID_TRUE@ profiles/network/bnep.$(OBJEXT) android_bluetoothd_OBJECTS = $(am_android_bluetoothd_OBJECTS) @ANDROID_TRUE@android_bluetoothd_DEPENDENCIES = \ @ANDROID_TRUE@ lib/libbluetooth-internal.la \ @ANDROID_TRUE@ src/libshared-glib.la $(am__DEPENDENCIES_1) am__android_bluetoothd_snoop_SOURCES_DIST = \ android/bluetoothd-snoop.c src/log.c @ANDROID_TRUE@am_android_bluetoothd_snoop_OBJECTS = \ @ANDROID_TRUE@ android/bluetoothd-snoop.$(OBJEXT) \ @ANDROID_TRUE@ src/log.$(OBJEXT) android_bluetoothd_snoop_OBJECTS = \ $(am_android_bluetoothd_snoop_OBJECTS) @ANDROID_TRUE@android_bluetoothd_snoop_DEPENDENCIES = \ @ANDROID_TRUE@ src/libshared-mainloop.la $(am__DEPENDENCIES_1) am__android_haltest_SOURCES_DIST = android/client/haltest.c \ android/client/pollhandler.h android/client/pollhandler.c \ android/client/terminal.h android/client/terminal.c \ android/client/history.h android/client/history.c \ android/client/tabcompletion.c android/client/if-main.h \ android/client/if-av.c android/client/if-av-sink.c \ android/client/if-rc.c android/client/if-rc-ctrl.c \ android/client/if-bt.c android/client/if-gatt.c \ android/client/if-hf.c android/client/if-hf-client.c \ android/client/if-hh.c android/client/if-pan.c \ android/client/if-hl.c android/client/if-sock.c \ android/client/if-audio.c android/client/if-sco.c \ android/client/if-mce.c android/hardware/hardware.c \ android/hal-utils.h android/hal-utils.c @ANDROID_TRUE@am_android_haltest_OBJECTS = \ @ANDROID_TRUE@ android/client/haltest-haltest.$(OBJEXT) \ @ANDROID_TRUE@ android/client/haltest-pollhandler.$(OBJEXT) \ @ANDROID_TRUE@ android/client/haltest-terminal.$(OBJEXT) \ @ANDROID_TRUE@ android/client/haltest-history.$(OBJEXT) \ @ANDROID_TRUE@ android/client/haltest-tabcompletion.$(OBJEXT) \ @ANDROID_TRUE@ android/client/haltest-if-av.$(OBJEXT) \ @ANDROID_TRUE@ android/client/haltest-if-av-sink.$(OBJEXT) \ @ANDROID_TRUE@ android/client/haltest-if-rc.$(OBJEXT) \ @ANDROID_TRUE@ android/client/haltest-if-rc-ctrl.$(OBJEXT) \ @ANDROID_TRUE@ android/client/haltest-if-bt.$(OBJEXT) \ @ANDROID_TRUE@ android/client/haltest-if-gatt.$(OBJEXT) \ @ANDROID_TRUE@ android/client/haltest-if-hf.$(OBJEXT) \ @ANDROID_TRUE@ android/client/haltest-if-hf-client.$(OBJEXT) \ @ANDROID_TRUE@ android/client/haltest-if-hh.$(OBJEXT) \ @ANDROID_TRUE@ android/client/haltest-if-pan.$(OBJEXT) \ @ANDROID_TRUE@ android/client/haltest-if-hl.$(OBJEXT) \ @ANDROID_TRUE@ android/client/haltest-if-sock.$(OBJEXT) \ @ANDROID_TRUE@ android/client/haltest-if-audio.$(OBJEXT) \ @ANDROID_TRUE@ android/client/haltest-if-sco.$(OBJEXT) \ @ANDROID_TRUE@ android/client/haltest-if-mce.$(OBJEXT) \ @ANDROID_TRUE@ android/hardware/haltest-hardware.$(OBJEXT) \ @ANDROID_TRUE@ android/haltest-hal-utils.$(OBJEXT) android_haltest_OBJECTS = $(am_android_haltest_OBJECTS) android_haltest_DEPENDENCIES = android_haltest_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC \ $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=link $(CCLD) \ $(AM_CFLAGS) $(CFLAGS) $(android_haltest_LDFLAGS) $(LDFLAGS) \ -o $@ am__android_ipc_tester_SOURCES_DIST = emulator/hciemu.h \ emulator/hciemu.c emulator/vhci.h emulator/vhci.c \ emulator/btdev.h emulator/btdev.c emulator/bthost.h \ emulator/bthost.c emulator/smp.c android/hal-utils.h \ android/hal-utils.c android/ipc-common.h android/ipc-tester.c @ANDROID_TRUE@am_android_ipc_tester_OBJECTS = \ @ANDROID_TRUE@ emulator/android_ipc_tester-hciemu.$(OBJEXT) \ @ANDROID_TRUE@ emulator/android_ipc_tester-vhci.$(OBJEXT) \ @ANDROID_TRUE@ emulator/android_ipc_tester-btdev.$(OBJEXT) \ @ANDROID_TRUE@ emulator/android_ipc_tester-bthost.$(OBJEXT) \ @ANDROID_TRUE@ emulator/android_ipc_tester-smp.$(OBJEXT) \ @ANDROID_TRUE@ android/ipc_tester-hal-utils.$(OBJEXT) \ @ANDROID_TRUE@ android/ipc_tester-ipc-tester.$(OBJEXT) android_ipc_tester_OBJECTS = $(am_android_ipc_tester_OBJECTS) @ANDROID_TRUE@android_ipc_tester_DEPENDENCIES = \ @ANDROID_TRUE@ lib/libbluetooth-internal.la \ @ANDROID_TRUE@ src/libshared-glib.la $(am__DEPENDENCIES_1) am__android_system_emulator_SOURCES_DIST = android/system-emulator.c @ANDROID_TRUE@am_android_system_emulator_OBJECTS = \ @ANDROID_TRUE@ android/system-emulator.$(OBJEXT) android_system_emulator_OBJECTS = \ $(am_android_system_emulator_OBJECTS) @ANDROID_TRUE@android_system_emulator_DEPENDENCIES = \ @ANDROID_TRUE@ src/libshared-mainloop.la am__android_test_ipc_SOURCES_DIST = android/test-ipc.c src/log.h \ src/log.c android/ipc-common.h android/ipc.c android/ipc.h @ANDROID_TRUE@am_android_test_ipc_OBJECTS = \ @ANDROID_TRUE@ android/test-ipc.$(OBJEXT) src/log.$(OBJEXT) \ @ANDROID_TRUE@ android/ipc.$(OBJEXT) android_test_ipc_OBJECTS = $(am_android_test_ipc_OBJECTS) @ANDROID_TRUE@android_test_ipc_DEPENDENCIES = src/libshared-glib.la \ @ANDROID_TRUE@ $(am__DEPENDENCIES_1) am__attrib_gatttool_SOURCES_DIST = attrib/gatttool.c attrib/att.c \ attrib/gatt.c attrib/gattrib.c btio/btio.c attrib/gatttool.h \ attrib/interactive.c attrib/utils.c src/log.c client/display.c \ client/display.h @DEPRECATED_TRUE@@READLINE_TRUE@am_attrib_gatttool_OBJECTS = \ @DEPRECATED_TRUE@@READLINE_TRUE@ attrib/gatttool.$(OBJEXT) \ @DEPRECATED_TRUE@@READLINE_TRUE@ attrib/att.$(OBJEXT) \ @DEPRECATED_TRUE@@READLINE_TRUE@ attrib/gatt.$(OBJEXT) \ @DEPRECATED_TRUE@@READLINE_TRUE@ attrib/gattrib.$(OBJEXT) \ @DEPRECATED_TRUE@@READLINE_TRUE@ btio/btio.$(OBJEXT) \ @DEPRECATED_TRUE@@READLINE_TRUE@ attrib/interactive.$(OBJEXT) \ @DEPRECATED_TRUE@@READLINE_TRUE@ attrib/utils.$(OBJEXT) \ @DEPRECATED_TRUE@@READLINE_TRUE@ src/log.$(OBJEXT) \ @DEPRECATED_TRUE@@READLINE_TRUE@ client/display.$(OBJEXT) attrib_gatttool_OBJECTS = $(am_attrib_gatttool_OBJECTS) @DEPRECATED_TRUE@@READLINE_TRUE@attrib_gatttool_DEPENDENCIES = \ @DEPRECATED_TRUE@@READLINE_TRUE@ lib/libbluetooth-internal.la \ @DEPRECATED_TRUE@@READLINE_TRUE@ src/libshared-glib.la \ @DEPRECATED_TRUE@@READLINE_TRUE@ $(am__DEPENDENCIES_1) am__client_bluetoothctl_SOURCES_DIST = client/main.c client/print.h \ client/print.c client/display.h client/display.c \ client/agent.h client/agent.c client/advertising.h \ client/advertising.c client/adv_monitor.h client/adv_monitor.c \ client/gatt.h client/gatt.c client/admin.h client/admin.c \ client/player.h client/player.c client/mgmt.h client/mgmt.c \ client/assistant.h client/assistant.c client/hci.h \ client/hci.c @CLIENT_TRUE@am_client_bluetoothctl_OBJECTS = client/main.$(OBJEXT) \ @CLIENT_TRUE@ client/print.$(OBJEXT) client/display.$(OBJEXT) \ @CLIENT_TRUE@ client/agent.$(OBJEXT) \ @CLIENT_TRUE@ client/advertising.$(OBJEXT) \ @CLIENT_TRUE@ client/adv_monitor.$(OBJEXT) \ @CLIENT_TRUE@ client/gatt.$(OBJEXT) client/admin.$(OBJEXT) \ @CLIENT_TRUE@ client/player.$(OBJEXT) client/mgmt.$(OBJEXT) \ @CLIENT_TRUE@ client/assistant.$(OBJEXT) client/hci.$(OBJEXT) client_bluetoothctl_OBJECTS = $(am_client_bluetoothctl_OBJECTS) @CLIENT_TRUE@client_bluetoothctl_DEPENDENCIES = \ @CLIENT_TRUE@ lib/libbluetooth-internal.la \ @CLIENT_TRUE@ gdbus/libgdbus-internal.la src/libshared-glib.la \ @CLIENT_TRUE@ $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1) am__emulator_b1ee_SOURCES_DIST = emulator/b1ee.c @TESTING_TRUE@am_emulator_b1ee_OBJECTS = emulator/b1ee.$(OBJEXT) emulator_b1ee_OBJECTS = $(am_emulator_b1ee_OBJECTS) @TESTING_TRUE@emulator_b1ee_DEPENDENCIES = src/libshared-mainloop.la am__emulator_btvirt_SOURCES_DIST = emulator/main.c monitor/bt.h \ emulator/serial.h emulator/serial.c emulator/server.h \ emulator/server.c emulator/vhci.h emulator/vhci.c \ emulator/btdev.h emulator/btdev.c emulator/bthost.h \ emulator/bthost.c emulator/smp.c emulator/phy.h emulator/phy.c \ emulator/le.h emulator/le.c @TESTING_TRUE@am_emulator_btvirt_OBJECTS = emulator/main.$(OBJEXT) \ @TESTING_TRUE@ emulator/serial.$(OBJEXT) \ @TESTING_TRUE@ emulator/server.$(OBJEXT) \ @TESTING_TRUE@ emulator/vhci.$(OBJEXT) emulator/btdev.$(OBJEXT) \ @TESTING_TRUE@ emulator/bthost.$(OBJEXT) emulator/smp.$(OBJEXT) \ @TESTING_TRUE@ emulator/phy.$(OBJEXT) emulator/le.$(OBJEXT) emulator_btvirt_OBJECTS = $(am_emulator_btvirt_OBJECTS) @TESTING_TRUE@emulator_btvirt_DEPENDENCIES = \ @TESTING_TRUE@ lib/libbluetooth-internal.la \ @TESTING_TRUE@ src/libshared-mainloop.la am__emulator_hfp_SOURCES_DIST = emulator/hfp.c @TESTING_TRUE@am_emulator_hfp_OBJECTS = emulator/hfp.$(OBJEXT) emulator_hfp_OBJECTS = $(am_emulator_hfp_OBJECTS) @TESTING_TRUE@emulator_hfp_DEPENDENCIES = src/libshared-mainloop.la am__mesh_bluetooth_meshd_SOURCES_DIST = mesh/mesh.h mesh/mesh.c \ mesh/net-keys.h mesh/net-keys.c mesh/mesh-io.h mesh/mesh-io.c \ mesh/mesh-mgmt.h mesh/mesh-mgmt.c mesh/error.h \ mesh/mesh-io-api.h mesh/mesh-io-unit.h mesh/mesh-io-unit.c \ mesh/mesh-io-mgmt.h mesh/mesh-io-mgmt.c mesh/mesh-io-generic.h \ mesh/mesh-io-generic.c mesh/net.h mesh/net.c mesh/crypto.h \ mesh/crypto.c mesh/friend.h mesh/friend.c mesh/appkey.h \ mesh/appkey.c mesh/node.h mesh/node.c mesh/provision.h \ mesh/prov.h mesh/model.h mesh/model.c mesh/cfgmod.h \ mesh/cfgmod-server.c mesh/remprv.h mesh/remprv-server.c \ mesh/mesh-config.h mesh/mesh-config-json.c mesh/util.h \ mesh/util.c mesh/dbus.h mesh/dbus.c mesh/agent.h mesh/agent.c \ mesh/prov-acceptor.c mesh/prov-initiator.c mesh/manager.h \ mesh/manager.c mesh/pb-adv.h mesh/pb-adv.c mesh/keyring.h \ mesh/keyring.c mesh/rpl.h mesh/rpl.c mesh/prv-beacon.h \ mesh/prvbeac-server.c mesh/mesh-defs.h mesh/main.c @MESH_TRUE@am__objects_11 = mesh/mesh.$(OBJEXT) \ @MESH_TRUE@ mesh/net-keys.$(OBJEXT) mesh/mesh-io.$(OBJEXT) \ @MESH_TRUE@ mesh/mesh-mgmt.$(OBJEXT) \ @MESH_TRUE@ mesh/mesh-io-unit.$(OBJEXT) \ @MESH_TRUE@ mesh/mesh-io-mgmt.$(OBJEXT) \ @MESH_TRUE@ mesh/mesh-io-generic.$(OBJEXT) mesh/net.$(OBJEXT) \ @MESH_TRUE@ mesh/crypto.$(OBJEXT) mesh/friend.$(OBJEXT) \ @MESH_TRUE@ mesh/appkey.$(OBJEXT) mesh/node.$(OBJEXT) \ @MESH_TRUE@ mesh/model.$(OBJEXT) mesh/cfgmod-server.$(OBJEXT) \ @MESH_TRUE@ mesh/remprv-server.$(OBJEXT) \ @MESH_TRUE@ mesh/mesh-config-json.$(OBJEXT) mesh/util.$(OBJEXT) \ @MESH_TRUE@ mesh/dbus.$(OBJEXT) mesh/agent.$(OBJEXT) \ @MESH_TRUE@ mesh/prov-acceptor.$(OBJEXT) \ @MESH_TRUE@ mesh/prov-initiator.$(OBJEXT) \ @MESH_TRUE@ mesh/manager.$(OBJEXT) mesh/pb-adv.$(OBJEXT) \ @MESH_TRUE@ mesh/keyring.$(OBJEXT) mesh/rpl.$(OBJEXT) \ @MESH_TRUE@ mesh/prvbeac-server.$(OBJEXT) @MESH_TRUE@am_mesh_bluetooth_meshd_OBJECTS = $(am__objects_11) \ @MESH_TRUE@ mesh/main.$(OBJEXT) mesh_bluetooth_meshd_OBJECTS = $(am_mesh_bluetooth_meshd_OBJECTS) @EXTERNAL_ELL_FALSE@am__DEPENDENCIES_2 = ell/libell-internal.la @MESH_TRUE@mesh_bluetooth_meshd_DEPENDENCIES = src/libshared-ell.la \ @MESH_TRUE@ $(am__DEPENDENCIES_2) am__monitor_btmon_SOURCES_DIST = monitor/main.c monitor/bt.h \ monitor/display.h monitor/display.c monitor/hcidump.h \ monitor/hcidump.c monitor/ellisys.h monitor/ellisys.c \ monitor/control.h monitor/control.c monitor/packet.h \ monitor/packet.c monitor/vendor.h monitor/vendor.c \ monitor/lmp.h monitor/lmp.c monitor/crc.h monitor/crc.c \ monitor/ll.h monitor/ll.c monitor/l2cap.h monitor/l2cap.c \ monitor/sdp.h monitor/sdp.c monitor/avctp.h monitor/avctp.c \ monitor/avdtp.h monitor/avdtp.c monitor/a2dp.h monitor/a2dp.c \ monitor/rfcomm.h monitor/rfcomm.c monitor/bnep.h \ monitor/bnep.c monitor/hwdb.h monitor/hwdb.c monitor/keys.h \ monitor/keys.c monitor/analyze.h monitor/analyze.c \ monitor/intel.h monitor/intel.c monitor/broadcom.h \ monitor/broadcom.c monitor/msft.h monitor/msft.c \ monitor/jlink.h monitor/jlink.c monitor/tty.h \ monitor/emulator.h monitor/att.h monitor/att.c src/log.h \ src/log.c src/textfile.h src/textfile.c src/settings.h \ src/settings.c @MONITOR_TRUE@am_monitor_btmon_OBJECTS = monitor/main.$(OBJEXT) \ @MONITOR_TRUE@ monitor/display.$(OBJEXT) \ @MONITOR_TRUE@ monitor/hcidump.$(OBJEXT) \ @MONITOR_TRUE@ monitor/ellisys.$(OBJEXT) \ @MONITOR_TRUE@ monitor/control.$(OBJEXT) \ @MONITOR_TRUE@ monitor/packet.$(OBJEXT) \ @MONITOR_TRUE@ monitor/vendor.$(OBJEXT) monitor/lmp.$(OBJEXT) \ @MONITOR_TRUE@ monitor/crc.$(OBJEXT) monitor/ll.$(OBJEXT) \ @MONITOR_TRUE@ monitor/l2cap.$(OBJEXT) monitor/sdp.$(OBJEXT) \ @MONITOR_TRUE@ monitor/avctp.$(OBJEXT) monitor/avdtp.$(OBJEXT) \ @MONITOR_TRUE@ monitor/a2dp.$(OBJEXT) monitor/rfcomm.$(OBJEXT) \ @MONITOR_TRUE@ monitor/bnep.$(OBJEXT) monitor/hwdb.$(OBJEXT) \ @MONITOR_TRUE@ monitor/keys.$(OBJEXT) monitor/analyze.$(OBJEXT) \ @MONITOR_TRUE@ monitor/intel.$(OBJEXT) \ @MONITOR_TRUE@ monitor/broadcom.$(OBJEXT) \ @MONITOR_TRUE@ monitor/msft.$(OBJEXT) monitor/jlink.$(OBJEXT) \ @MONITOR_TRUE@ monitor/att.$(OBJEXT) src/log.$(OBJEXT) \ @MONITOR_TRUE@ src/textfile.$(OBJEXT) src/settings.$(OBJEXT) monitor_btmon_OBJECTS = $(am_monitor_btmon_OBJECTS) @MONITOR_TRUE@monitor_btmon_DEPENDENCIES = \ @MONITOR_TRUE@ lib/libbluetooth-internal.la \ @MONITOR_TRUE@ src/libshared-mainloop.la $(am__DEPENDENCIES_1) \ @MONITOR_TRUE@ $(am__DEPENDENCIES_1) am__obexd_src_obexd_SOURCES_DIST = btio/btio.h btio/btio.c \ gobex/gobex.h gobex/gobex.c gobex/gobex-defs.h \ gobex/gobex-defs.c gobex/gobex-packet.c gobex/gobex-packet.h \ gobex/gobex-header.c gobex/gobex-header.h \ gobex/gobex-transfer.c gobex/gobex-debug.h \ gobex/gobex-apparam.c gobex/gobex-apparam.h \ obexd/plugins/filesystem.c obexd/plugins/filesystem.h \ obexd/plugins/bluetooth.c obexd/plugins/pcsuite.c \ obexd/plugins/opp.c obexd/plugins/ftp.c obexd/plugins/ftp.h \ obexd/plugins/irmc.c obexd/plugins/pbap.c \ obexd/plugins/vcard.h obexd/plugins/vcard.c \ obexd/plugins/phonebook.h \ obexd/plugins/phonebook-@PLUGIN_PHONEBOOK@.c \ obexd/plugins/mas.c obexd/src/map_ap.h \ obexd/plugins/messages.h obexd/plugins/messages-dummy.c \ obexd/client/mns.c obexd/client/map-event.h obexd/src/main.c \ obexd/src/obexd.h obexd/src/plugin.h obexd/src/plugin.c \ obexd/src/log.h obexd/src/log.c obexd/src/manager.h \ obexd/src/manager.c obexd/src/obex.h obexd/src/obex.c \ obexd/src/obex-priv.h obexd/src/mimetype.h \ obexd/src/mimetype.c obexd/src/service.h obexd/src/service.c \ obexd/src/transport.h obexd/src/transport.c obexd/src/server.h \ obexd/src/server.c obexd/client/manager.h \ obexd/client/manager.c obexd/client/session.h \ obexd/client/session.c obexd/client/bluetooth.h \ obexd/client/bluetooth.c obexd/client/sync.h \ obexd/client/sync.c obexd/client/pbap.h obexd/client/pbap.c \ obexd/client/ftp.h obexd/client/ftp.c obexd/client/opp.h \ obexd/client/opp.c obexd/client/map.h obexd/client/map.c \ obexd/client/bip.h obexd/client/bip.c \ obexd/client/bip-common.h obexd/client/bip-common.c \ obexd/client/map-event.c obexd/client/transfer.h \ obexd/client/transfer.c obexd/client/transport.h \ obexd/client/transport.c obexd/client/driver.h \ obexd/client/driver.c am__objects_12 = btio/obexd-btio.$(OBJEXT) am__objects_13 = gobex/obexd-gobex.$(OBJEXT) \ gobex/obexd-gobex-defs.$(OBJEXT) \ gobex/obexd-gobex-packet.$(OBJEXT) \ gobex/obexd-gobex-header.$(OBJEXT) \ gobex/obexd-gobex-transfer.$(OBJEXT) \ gobex/obexd-gobex-apparam.$(OBJEXT) @EXPERIMENTAL_TRUE@@OBEX_TRUE@am__objects_14 = obexd/plugins/obexd-pcsuite.$(OBJEXT) @OBEX_TRUE@am__objects_15 = obexd/plugins/obexd-filesystem.$(OBJEXT) \ @OBEX_TRUE@ obexd/plugins/obexd-bluetooth.$(OBJEXT) \ @OBEX_TRUE@ $(am__objects_14) obexd/plugins/obexd-opp.$(OBJEXT) \ @OBEX_TRUE@ obexd/plugins/obexd-ftp.$(OBJEXT) \ @OBEX_TRUE@ obexd/plugins/obexd-irmc.$(OBJEXT) \ @OBEX_TRUE@ obexd/plugins/obexd-pbap.$(OBJEXT) \ @OBEX_TRUE@ obexd/plugins/obexd-vcard.$(OBJEXT) \ @OBEX_TRUE@ obexd/plugins/obexd-phonebook-@PLUGIN_PHONEBOOK@.$(OBJEXT) \ @OBEX_TRUE@ obexd/plugins/obexd-mas.$(OBJEXT) \ @OBEX_TRUE@ obexd/plugins/obexd-messages-dummy.$(OBJEXT) \ @OBEX_TRUE@ obexd/client/obexd-mns.$(OBJEXT) @OBEX_TRUE@am_obexd_src_obexd_OBJECTS = $(am__objects_12) \ @OBEX_TRUE@ $(am__objects_13) $(am__objects_15) \ @OBEX_TRUE@ obexd/src/obexd-main.$(OBJEXT) \ @OBEX_TRUE@ obexd/src/obexd-plugin.$(OBJEXT) \ @OBEX_TRUE@ obexd/src/obexd-log.$(OBJEXT) \ @OBEX_TRUE@ obexd/src/obexd-manager.$(OBJEXT) \ @OBEX_TRUE@ obexd/src/obexd-obex.$(OBJEXT) \ @OBEX_TRUE@ obexd/src/obexd-mimetype.$(OBJEXT) \ @OBEX_TRUE@ obexd/src/obexd-service.$(OBJEXT) \ @OBEX_TRUE@ obexd/src/obexd-transport.$(OBJEXT) \ @OBEX_TRUE@ obexd/src/obexd-server.$(OBJEXT) \ @OBEX_TRUE@ obexd/client/obexd-manager.$(OBJEXT) \ @OBEX_TRUE@ obexd/client/obexd-session.$(OBJEXT) \ @OBEX_TRUE@ obexd/client/obexd-bluetooth.$(OBJEXT) \ @OBEX_TRUE@ obexd/client/obexd-sync.$(OBJEXT) \ @OBEX_TRUE@ obexd/client/obexd-pbap.$(OBJEXT) \ @OBEX_TRUE@ obexd/client/obexd-ftp.$(OBJEXT) \ @OBEX_TRUE@ obexd/client/obexd-opp.$(OBJEXT) \ @OBEX_TRUE@ obexd/client/obexd-map.$(OBJEXT) \ @OBEX_TRUE@ obexd/client/obexd-bip.$(OBJEXT) \ @OBEX_TRUE@ obexd/client/obexd-bip-common.$(OBJEXT) \ @OBEX_TRUE@ obexd/client/obexd-map-event.$(OBJEXT) \ @OBEX_TRUE@ obexd/client/obexd-transfer.$(OBJEXT) \ @OBEX_TRUE@ obexd/client/obexd-transport.$(OBJEXT) \ @OBEX_TRUE@ obexd/client/obexd-driver.$(OBJEXT) am__objects_16 = $(am__objects_1) nodist_obexd_src_obexd_OBJECTS = $(am__objects_16) obexd_src_obexd_OBJECTS = $(am_obexd_src_obexd_OBJECTS) \ $(nodist_obexd_src_obexd_OBJECTS) @OBEX_TRUE@obexd_src_obexd_DEPENDENCIES = \ @OBEX_TRUE@ lib/libbluetooth-internal.la \ @OBEX_TRUE@ gdbus/libgdbus-internal.la src/libshared-glib.la \ @OBEX_TRUE@ $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1) \ @OBEX_TRUE@ $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1) obexd_src_obexd_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC \ $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=link $(CCLD) \ $(AM_CFLAGS) $(CFLAGS) $(obexd_src_obexd_LDFLAGS) $(LDFLAGS) \ -o $@ am__peripheral_btsensor_SOURCES_DIST = peripheral/main.c \ peripheral/efivars.h peripheral/efivars.c peripheral/attach.h \ peripheral/attach.c peripheral/log.h peripheral/log.c \ peripheral/gap.h peripheral/gap.c peripheral/gatt.h \ peripheral/gatt.c @TESTING_TRUE@am_peripheral_btsensor_OBJECTS = \ @TESTING_TRUE@ peripheral/main.$(OBJEXT) \ @TESTING_TRUE@ peripheral/efivars.$(OBJEXT) \ @TESTING_TRUE@ peripheral/attach.$(OBJEXT) \ @TESTING_TRUE@ peripheral/log.$(OBJEXT) \ @TESTING_TRUE@ peripheral/gap.$(OBJEXT) \ @TESTING_TRUE@ peripheral/gatt.$(OBJEXT) peripheral_btsensor_OBJECTS = $(am_peripheral_btsensor_OBJECTS) @TESTING_TRUE@peripheral_btsensor_DEPENDENCIES = \ @TESTING_TRUE@ src/libshared-mainloop.la \ @TESTING_TRUE@ lib/libbluetooth-internal.la am__profiles_cups_bluetooth_SOURCES_DIST = profiles/cups/main.c \ profiles/cups/cups.h profiles/cups/sdp.c profiles/cups/spp.c \ profiles/cups/hcrp.c @CUPS_TRUE@am_profiles_cups_bluetooth_OBJECTS = \ @CUPS_TRUE@ profiles/cups/main.$(OBJEXT) \ @CUPS_TRUE@ profiles/cups/sdp.$(OBJEXT) \ @CUPS_TRUE@ profiles/cups/spp.$(OBJEXT) \ @CUPS_TRUE@ profiles/cups/hcrp.$(OBJEXT) profiles_cups_bluetooth_OBJECTS = \ $(am_profiles_cups_bluetooth_OBJECTS) @CUPS_TRUE@profiles_cups_bluetooth_DEPENDENCIES = \ @CUPS_TRUE@ $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1) \ @CUPS_TRUE@ lib/libbluetooth-internal.la \ @CUPS_TRUE@ gdbus/libgdbus-internal.la am__profiles_iap_iapd_SOURCES_DIST = profiles/iap/main.c @TOOLS_TRUE@am_profiles_iap_iapd_OBJECTS = \ @TOOLS_TRUE@ profiles/iap/main.$(OBJEXT) profiles_iap_iapd_OBJECTS = $(am_profiles_iap_iapd_OBJECTS) @TOOLS_TRUE@profiles_iap_iapd_DEPENDENCIES = \ @TOOLS_TRUE@ gdbus/libgdbus-internal.la $(am__DEPENDENCIES_1) \ @TOOLS_TRUE@ $(am__DEPENDENCIES_1) am__src_bluetoothd_SOURCES_DIST = plugins/hostname.c \ plugins/autopair.c plugins/policy.c profiles/audio/media.h \ profiles/audio/media.c profiles/audio/transport.h \ profiles/audio/transport.c profiles/audio/player.h \ profiles/audio/player.c plugins/admin.c plugins/neard.c \ profiles/sap/main.c profiles/sap/manager.h \ profiles/sap/manager.c profiles/sap/server.h \ profiles/sap/server.c profiles/sap/sap.h \ profiles/sap/sap-dummy.c profiles/audio/source.h \ profiles/audio/source.c profiles/audio/sink.h \ profiles/audio/sink.c profiles/audio/a2dp.h \ profiles/audio/a2dp.c profiles/audio/avdtp.h \ profiles/audio/avdtp.c profiles/audio/a2dp-codecs.h \ profiles/audio/control.h profiles/audio/control.c \ profiles/audio/avctp.h profiles/audio/avctp.c \ profiles/audio/avrcp.h profiles/audio/avrcp.c \ profiles/network/manager.c profiles/network/bnep.h \ profiles/network/bnep.c profiles/network/server.h \ profiles/network/server.c profiles/network/connection.h \ profiles/network/connection.c profiles/input/manager.c \ profiles/input/server.h profiles/input/server.c \ profiles/input/device.h profiles/input/device.c \ profiles/input/hidp_defs.h profiles/input/sixaxis.h \ profiles/input/hog.c profiles/input/hog-lib.c \ profiles/input/hog-lib.h profiles/deviceinfo/dis.c \ profiles/deviceinfo/dis.h profiles/battery/bas.c \ profiles/battery/bas.h profiles/scanparam/scpp.c \ profiles/scanparam/scpp.h profiles/input/suspend.h \ profiles/input/suspend-none.c profiles/health/mcap.h \ profiles/health/mcap.c profiles/health/hdp_main.c \ profiles/health/hdp_types.h profiles/health/hdp_manager.h \ profiles/health/hdp_manager.c profiles/health/hdp.h \ profiles/health/hdp.c profiles/health/hdp_util.h \ profiles/health/hdp_util.c profiles/gap/gas.c \ profiles/scanparam/scan.c profiles/deviceinfo/deviceinfo.c \ profiles/midi/midi.c profiles/midi/libmidi.h \ profiles/midi/libmidi.c profiles/battery/battery.c \ plugins/sixaxis.c profiles/audio/bap.c profiles/audio/bass.c \ profiles/audio/mcp.c profiles/audio/vcp.h profiles/audio/vcp.c \ profiles/audio/micp.c profiles/audio/ccp.c \ profiles/audio/csip.c profiles/audio/asha.h \ profiles/audio/asha.c attrib/att.h attrib/att-database.h \ attrib/att.c attrib/gatt.h attrib/gatt.c attrib/gattrib.h \ attrib/gattrib.c btio/btio.h btio/btio.c src/main.c src/log.h \ src/log.c src/backtrace.h src/backtrace.c src/rfkill.c \ src/btd.h src/sdpd.h src/sdpd-server.c src/sdpd-request.c \ src/sdpd-service.c src/sdpd-database.c src/gatt-database.h \ src/gatt-database.c src/sdp-xml.h src/sdp-xml.c \ src/sdp-client.h src/sdp-client.c src/textfile.h \ src/textfile.c src/uuid-helper.h src/uuid-helper.c \ src/plugin.h src/plugin.c src/storage.h src/storage.c \ src/advertising.h src/advertising.c src/agent.h src/agent.c \ src/error.h src/error.c src/adapter.h src/adapter.c \ src/profile.h src/profile.c src/service.h src/service.c \ src/gatt-client.h src/gatt-client.c src/device.h src/device.c \ src/dbus-common.c src/dbus-common.h src/eir.h src/eir.c \ src/adv_monitor.h src/adv_monitor.c src/battery.h \ src/battery.c src/settings.h src/settings.c src/set.h \ src/set.c src/bluetooth.ver @ADMIN_TRUE@am__objects_17 = plugins/bluetoothd-admin.$(OBJEXT) @NFC_TRUE@am__objects_18 = plugins/bluetoothd-neard.$(OBJEXT) @SAP_TRUE@am__objects_19 = profiles/sap/bluetoothd-main.$(OBJEXT) \ @SAP_TRUE@ profiles/sap/bluetoothd-manager.$(OBJEXT) \ @SAP_TRUE@ profiles/sap/bluetoothd-server.$(OBJEXT) \ @SAP_TRUE@ profiles/sap/bluetoothd-sap-dummy.$(OBJEXT) @A2DP_TRUE@am__objects_20 = \ @A2DP_TRUE@ profiles/audio/bluetoothd-source.$(OBJEXT) \ @A2DP_TRUE@ profiles/audio/bluetoothd-sink.$(OBJEXT) \ @A2DP_TRUE@ profiles/audio/bluetoothd-a2dp.$(OBJEXT) \ @A2DP_TRUE@ profiles/audio/bluetoothd-avdtp.$(OBJEXT) @AVRCP_TRUE@am__objects_21 = \ @AVRCP_TRUE@ profiles/audio/bluetoothd-control.$(OBJEXT) \ @AVRCP_TRUE@ profiles/audio/bluetoothd-avctp.$(OBJEXT) \ @AVRCP_TRUE@ profiles/audio/bluetoothd-avrcp.$(OBJEXT) @NETWORK_TRUE@am__objects_22 = \ @NETWORK_TRUE@ profiles/network/bluetoothd-manager.$(OBJEXT) \ @NETWORK_TRUE@ profiles/network/bluetoothd-bnep.$(OBJEXT) \ @NETWORK_TRUE@ profiles/network/bluetoothd-server.$(OBJEXT) \ @NETWORK_TRUE@ profiles/network/bluetoothd-connection.$(OBJEXT) @HID_TRUE@am__objects_23 = \ @HID_TRUE@ profiles/input/bluetoothd-manager.$(OBJEXT) \ @HID_TRUE@ profiles/input/bluetoothd-server.$(OBJEXT) \ @HID_TRUE@ profiles/input/bluetoothd-device.$(OBJEXT) @HOG_TRUE@am__objects_24 = profiles/input/bluetoothd-hog.$(OBJEXT) \ @HOG_TRUE@ profiles/input/bluetoothd-hog-lib.$(OBJEXT) \ @HOG_TRUE@ profiles/deviceinfo/bluetoothd-dis.$(OBJEXT) \ @HOG_TRUE@ profiles/battery/bluetoothd-bas.$(OBJEXT) \ @HOG_TRUE@ profiles/scanparam/bluetoothd-scpp.$(OBJEXT) \ @HOG_TRUE@ profiles/input/bluetoothd-suspend-none.$(OBJEXT) @HEALTH_TRUE@am__objects_25 = \ @HEALTH_TRUE@ profiles/health/bluetoothd-mcap.$(OBJEXT) \ @HEALTH_TRUE@ profiles/health/bluetoothd-hdp_main.$(OBJEXT) \ @HEALTH_TRUE@ profiles/health/bluetoothd-hdp_manager.$(OBJEXT) \ @HEALTH_TRUE@ profiles/health/bluetoothd-hdp.$(OBJEXT) \ @HEALTH_TRUE@ profiles/health/bluetoothd-hdp_util.$(OBJEXT) @MIDI_TRUE@am__objects_26 = profiles/midi/bluetoothd-midi.$(OBJEXT) \ @MIDI_TRUE@ profiles/midi/bluetoothd-libmidi.$(OBJEXT) @SIXAXIS_TRUE@am__objects_27 = plugins/bluetoothd-sixaxis.$(OBJEXT) @BAP_TRUE@am__objects_28 = profiles/audio/bluetoothd-bap.$(OBJEXT) @BASS_TRUE@am__objects_29 = profiles/audio/bluetoothd-bass.$(OBJEXT) @MCP_TRUE@am__objects_30 = profiles/audio/bluetoothd-mcp.$(OBJEXT) @VCP_TRUE@am__objects_31 = profiles/audio/bluetoothd-vcp.$(OBJEXT) @MICP_TRUE@am__objects_32 = profiles/audio/bluetoothd-micp.$(OBJEXT) @CCP_TRUE@am__objects_33 = profiles/audio/bluetoothd-ccp.$(OBJEXT) @CSIP_TRUE@am__objects_34 = profiles/audio/bluetoothd-csip.$(OBJEXT) @ASHA_TRUE@am__objects_35 = profiles/audio/bluetoothd-asha.$(OBJEXT) am__objects_36 = plugins/bluetoothd-hostname.$(OBJEXT) \ plugins/bluetoothd-autopair.$(OBJEXT) \ plugins/bluetoothd-policy.$(OBJEXT) \ profiles/audio/bluetoothd-media.$(OBJEXT) \ profiles/audio/bluetoothd-transport.$(OBJEXT) \ profiles/audio/bluetoothd-player.$(OBJEXT) $(am__objects_17) \ $(am__objects_18) $(am__objects_19) $(am__objects_20) \ $(am__objects_21) $(am__objects_22) $(am__objects_23) \ $(am__objects_24) $(am__objects_25) \ profiles/gap/bluetoothd-gas.$(OBJEXT) \ profiles/scanparam/bluetoothd-scan.$(OBJEXT) \ profiles/deviceinfo/bluetoothd-deviceinfo.$(OBJEXT) \ $(am__objects_26) \ profiles/battery/bluetoothd-battery.$(OBJEXT) \ $(am__objects_27) $(am__objects_28) $(am__objects_29) \ $(am__objects_30) $(am__objects_31) $(am__objects_32) \ $(am__objects_33) $(am__objects_34) $(am__objects_35) am__objects_37 = attrib/bluetoothd-att.$(OBJEXT) \ attrib/bluetoothd-gatt.$(OBJEXT) \ attrib/bluetoothd-gattrib.$(OBJEXT) am__objects_38 = btio/bluetoothd-btio.$(OBJEXT) am_src_bluetoothd_OBJECTS = $(am__objects_36) $(am__objects_37) \ $(am__objects_38) src/bluetoothd-main.$(OBJEXT) \ src/bluetoothd-log.$(OBJEXT) \ src/bluetoothd-backtrace.$(OBJEXT) \ src/bluetoothd-rfkill.$(OBJEXT) \ src/bluetoothd-sdpd-server.$(OBJEXT) \ src/bluetoothd-sdpd-request.$(OBJEXT) \ src/bluetoothd-sdpd-service.$(OBJEXT) \ src/bluetoothd-sdpd-database.$(OBJEXT) \ src/bluetoothd-gatt-database.$(OBJEXT) \ src/bluetoothd-sdp-xml.$(OBJEXT) \ src/bluetoothd-sdp-client.$(OBJEXT) \ src/bluetoothd-textfile.$(OBJEXT) \ src/bluetoothd-uuid-helper.$(OBJEXT) \ src/bluetoothd-plugin.$(OBJEXT) \ src/bluetoothd-storage.$(OBJEXT) \ src/bluetoothd-advertising.$(OBJEXT) \ src/bluetoothd-agent.$(OBJEXT) src/bluetoothd-error.$(OBJEXT) \ src/bluetoothd-adapter.$(OBJEXT) \ src/bluetoothd-profile.$(OBJEXT) \ src/bluetoothd-service.$(OBJEXT) \ src/bluetoothd-gatt-client.$(OBJEXT) \ src/bluetoothd-device.$(OBJEXT) \ src/bluetoothd-dbus-common.$(OBJEXT) \ src/bluetoothd-eir.$(OBJEXT) \ src/bluetoothd-adv_monitor.$(OBJEXT) \ src/bluetoothd-battery.$(OBJEXT) \ src/bluetoothd-settings.$(OBJEXT) src/bluetoothd-set.$(OBJEXT) \ $(am__objects_1) nodist_src_bluetoothd_OBJECTS = $(am__objects_1) src_bluetoothd_OBJECTS = $(am_src_bluetoothd_OBJECTS) \ $(nodist_src_bluetoothd_OBJECTS) @MIDI_TRUE@am__DEPENDENCIES_3 = $(am__DEPENDENCIES_1) @SIXAXIS_TRUE@am__DEPENDENCIES_4 = $(am__DEPENDENCIES_1) am__DEPENDENCIES_5 = $(am__DEPENDENCIES_3) $(am__DEPENDENCIES_4) src_bluetoothd_DEPENDENCIES = lib/libbluetooth-internal.la \ gdbus/libgdbus-internal.la src/libshared-glib.la \ $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1) \ $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_5) src_bluetoothd_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC \ $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=link $(CCLD) \ $(AM_CFLAGS) $(CFLAGS) $(src_bluetoothd_LDFLAGS) $(LDFLAGS) -o \ $@ am__tools_3dsp_SOURCES_DIST = tools/3dsp.c monitor/bt.h @TESTING_TRUE@am_tools_3dsp_OBJECTS = tools/3dsp.$(OBJEXT) tools_3dsp_OBJECTS = $(am_tools_3dsp_OBJECTS) @TESTING_TRUE@tools_3dsp_DEPENDENCIES = src/libshared-mainloop.la am__tools_advtest_SOURCES_DIST = tools/advtest.c @TOOLS_TRUE@am_tools_advtest_OBJECTS = tools/advtest.$(OBJEXT) tools_advtest_OBJECTS = $(am_tools_advtest_OBJECTS) @TOOLS_TRUE@tools_advtest_DEPENDENCIES = lib/libbluetooth-internal.la \ @TOOLS_TRUE@ src/libshared-mainloop.la tools_avinfo_SOURCES = tools/avinfo.c tools_avinfo_OBJECTS = tools/avinfo.$(OBJEXT) @TOOLS_TRUE@tools_avinfo_DEPENDENCIES = lib/libbluetooth-internal.la tools_avtest_SOURCES = tools/avtest.c tools_avtest_OBJECTS = tools/avtest.$(OBJEXT) @TOOLS_TRUE@tools_avtest_DEPENDENCIES = lib/libbluetooth-internal.la tools_bcmfw_SOURCES = tools/bcmfw.c tools_bcmfw_OBJECTS = tools/bcmfw.$(OBJEXT) tools_bcmfw_LDADD = $(LDADD) am__tools_bdaddr_SOURCES_DIST = tools/bdaddr.c src/oui.h src/oui.c @TOOLS_TRUE@am_tools_bdaddr_OBJECTS = tools/bdaddr.$(OBJEXT) \ @TOOLS_TRUE@ src/oui.$(OBJEXT) tools_bdaddr_OBJECTS = $(am_tools_bdaddr_OBJECTS) @TOOLS_TRUE@tools_bdaddr_DEPENDENCIES = lib/libbluetooth-internal.la \ @TOOLS_TRUE@ $(am__DEPENDENCIES_1) am__tools_bluemoon_SOURCES_DIST = tools/bluemoon.c monitor/bt.h @TOOLS_TRUE@am_tools_bluemoon_OBJECTS = tools/bluemoon.$(OBJEXT) tools_bluemoon_OBJECTS = $(am_tools_bluemoon_OBJECTS) @TOOLS_TRUE@tools_bluemoon_DEPENDENCIES = src/libshared-mainloop.la am__tools_bluetooth_player_SOURCES_DIST = tools/bluetooth-player.c \ client/print.c client/player.c @READLINE_TRUE@am_tools_bluetooth_player_OBJECTS = \ @READLINE_TRUE@ tools/bluetooth-player.$(OBJEXT) \ @READLINE_TRUE@ client/print.$(OBJEXT) client/player.$(OBJEXT) tools_bluetooth_player_OBJECTS = $(am_tools_bluetooth_player_OBJECTS) @READLINE_TRUE@tools_bluetooth_player_DEPENDENCIES = \ @READLINE_TRUE@ gdbus/libgdbus-internal.la \ @READLINE_TRUE@ src/libshared-glib.la $(am__DEPENDENCIES_1) \ @READLINE_TRUE@ $(am__DEPENDENCIES_1) am__tools_bnep_tester_SOURCES_DIST = tools/bnep-tester.c monitor/bt.h \ emulator/hciemu.h emulator/hciemu.c emulator/vhci.h \ emulator/vhci.c emulator/btdev.h emulator/btdev.c \ emulator/bthost.h emulator/bthost.c emulator/smp.c @TESTING_TRUE@am_tools_bnep_tester_OBJECTS = \ @TESTING_TRUE@ tools/bnep-tester.$(OBJEXT) \ @TESTING_TRUE@ emulator/hciemu.$(OBJEXT) \ @TESTING_TRUE@ emulator/vhci.$(OBJEXT) emulator/btdev.$(OBJEXT) \ @TESTING_TRUE@ emulator/bthost.$(OBJEXT) emulator/smp.$(OBJEXT) tools_bnep_tester_OBJECTS = $(am_tools_bnep_tester_OBJECTS) @TESTING_TRUE@tools_bnep_tester_DEPENDENCIES = \ @TESTING_TRUE@ lib/libbluetooth-internal.la \ @TESTING_TRUE@ src/libshared-glib.la $(am__DEPENDENCIES_1) am__tools_bneptest_SOURCES_DIST = tools/bneptest.c btio/btio.h \ btio/btio.c src/log.h src/log.c profiles/network/bnep.h \ profiles/network/bnep.c @TOOLS_TRUE@am_tools_bneptest_OBJECTS = tools/bneptest.$(OBJEXT) \ @TOOLS_TRUE@ btio/btio.$(OBJEXT) src/log.$(OBJEXT) \ @TOOLS_TRUE@ profiles/network/bnep.$(OBJEXT) tools_bneptest_OBJECTS = $(am_tools_bneptest_OBJECTS) @TOOLS_TRUE@tools_bneptest_DEPENDENCIES = \ @TOOLS_TRUE@ lib/libbluetooth-internal.la $(am__DEPENDENCIES_1) \ @TOOLS_TRUE@ src/libshared-mainloop.la am__tools_btattach_SOURCES_DIST = tools/btattach.c monitor/bt.h @TOOLS_TRUE@am_tools_btattach_OBJECTS = tools/btattach.$(OBJEXT) tools_btattach_OBJECTS = $(am_tools_btattach_OBJECTS) @TOOLS_TRUE@tools_btattach_DEPENDENCIES = src/libshared-mainloop.la am__tools_btconfig_SOURCES_DIST = tools/btconfig.c @TOOLS_TRUE@am_tools_btconfig_OBJECTS = tools/btconfig.$(OBJEXT) tools_btconfig_OBJECTS = $(am_tools_btconfig_OBJECTS) @TOOLS_TRUE@tools_btconfig_DEPENDENCIES = src/libshared-mainloop.la am__tools_btgatt_client_SOURCES_DIST = tools/btgatt-client.c \ src/uuid-helper.c @TOOLS_TRUE@am_tools_btgatt_client_OBJECTS = \ @TOOLS_TRUE@ tools/btgatt-client.$(OBJEXT) \ @TOOLS_TRUE@ src/uuid-helper.$(OBJEXT) tools_btgatt_client_OBJECTS = $(am_tools_btgatt_client_OBJECTS) @TOOLS_TRUE@tools_btgatt_client_DEPENDENCIES = \ @TOOLS_TRUE@ src/libshared-mainloop.la \ @TOOLS_TRUE@ lib/libbluetooth-internal.la am__tools_btgatt_server_SOURCES_DIST = tools/btgatt-server.c \ src/uuid-helper.c @TOOLS_TRUE@am_tools_btgatt_server_OBJECTS = \ @TOOLS_TRUE@ tools/btgatt-server.$(OBJEXT) \ @TOOLS_TRUE@ src/uuid-helper.$(OBJEXT) tools_btgatt_server_OBJECTS = $(am_tools_btgatt_server_OBJECTS) @TOOLS_TRUE@tools_btgatt_server_DEPENDENCIES = \ @TOOLS_TRUE@ src/libshared-mainloop.la \ @TOOLS_TRUE@ lib/libbluetooth-internal.la am__tools_btinfo_SOURCES_DIST = tools/btinfo.c monitor/bt.h @TOOLS_TRUE@am_tools_btinfo_OBJECTS = tools/btinfo.$(OBJEXT) tools_btinfo_OBJECTS = $(am_tools_btinfo_OBJECTS) @TOOLS_TRUE@tools_btinfo_DEPENDENCIES = src/libshared-mainloop.la am__tools_btiotest_SOURCES_DIST = tools/btiotest.c btio/btio.h \ btio/btio.c @TOOLS_TRUE@am_tools_btiotest_OBJECTS = tools/btiotest.$(OBJEXT) \ @TOOLS_TRUE@ btio/btio.$(OBJEXT) tools_btiotest_OBJECTS = $(am_tools_btiotest_OBJECTS) @TOOLS_TRUE@tools_btiotest_DEPENDENCIES = \ @TOOLS_TRUE@ lib/libbluetooth-internal.la $(am__DEPENDENCIES_1) am__tools_btmgmt_SOURCES_DIST = tools/btmgmt.c src/uuid-helper.c \ client/display.c client/mgmt.c @READLINE_TRUE@am_tools_btmgmt_OBJECTS = tools/btmgmt.$(OBJEXT) \ @READLINE_TRUE@ src/uuid-helper.$(OBJEXT) \ @READLINE_TRUE@ client/display.$(OBJEXT) client/mgmt.$(OBJEXT) tools_btmgmt_OBJECTS = $(am_tools_btmgmt_OBJECTS) @READLINE_TRUE@tools_btmgmt_DEPENDENCIES = \ @READLINE_TRUE@ lib/libbluetooth-internal.la \ @READLINE_TRUE@ src/libshared-mainloop.la am__tools_btmon_logger_SOURCES_DIST = tools/btmon-logger.c @LOGGER_TRUE@am_tools_btmon_logger_OBJECTS = \ @LOGGER_TRUE@ tools/btmon-logger.$(OBJEXT) tools_btmon_logger_OBJECTS = $(am_tools_btmon_logger_OBJECTS) @LOGGER_TRUE@tools_btmon_logger_DEPENDENCIES = \ @LOGGER_TRUE@ src/libshared-mainloop.la am__tools_btpclient_SOURCES_DIST = tools/btpclient.c src/shared/btp.c \ src/shared/btp.h @BTPCLIENT_TRUE@am_tools_btpclient_OBJECTS = \ @BTPCLIENT_TRUE@ tools/btpclient.$(OBJEXT) \ @BTPCLIENT_TRUE@ src/shared/btp.$(OBJEXT) tools_btpclient_OBJECTS = $(am_tools_btpclient_OBJECTS) @BTPCLIENT_TRUE@tools_btpclient_DEPENDENCIES = \ @BTPCLIENT_TRUE@ lib/libbluetooth-internal.la \ @BTPCLIENT_TRUE@ src/libshared-ell.la $(am__DEPENDENCIES_2) am__tools_btpclientctl_SOURCES_DIST = tools/btpclientctl.c \ client/display.c @BTPCLIENT_TRUE@am_tools_btpclientctl_OBJECTS = \ @BTPCLIENT_TRUE@ tools/btpclientctl.$(OBJEXT) \ @BTPCLIENT_TRUE@ client/display.$(OBJEXT) tools_btpclientctl_OBJECTS = $(am_tools_btpclientctl_OBJECTS) @BTPCLIENT_TRUE@tools_btpclientctl_DEPENDENCIES = \ @BTPCLIENT_TRUE@ src/libshared-mainloop.la \ @BTPCLIENT_TRUE@ src/libshared-glib.la \ @BTPCLIENT_TRUE@ lib/libbluetooth-internal.la am__tools_btproxy_SOURCES_DIST = tools/btproxy.c monitor/bt.h @TOOLS_TRUE@am_tools_btproxy_OBJECTS = tools/btproxy.$(OBJEXT) tools_btproxy_OBJECTS = $(am_tools_btproxy_OBJECTS) @TOOLS_TRUE@tools_btproxy_DEPENDENCIES = src/libshared-mainloop.la am__tools_btsnoop_SOURCES_DIST = tools/btsnoop.c @TOOLS_TRUE@am_tools_btsnoop_OBJECTS = tools/btsnoop.$(OBJEXT) tools_btsnoop_OBJECTS = $(am_tools_btsnoop_OBJECTS) @TOOLS_TRUE@tools_btsnoop_DEPENDENCIES = src/libshared-mainloop.la tools_check_selftest_SOURCES = tools/check-selftest.c tools_check_selftest_OBJECTS = tools/check-selftest.$(OBJEXT) tools_check_selftest_LDADD = $(LDADD) tools_ciptool_SOURCES = tools/ciptool.c tools_ciptool_OBJECTS = tools/ciptool.$(OBJEXT) @DEPRECATED_TRUE@@TOOLS_TRUE@tools_ciptool_DEPENDENCIES = \ @DEPRECATED_TRUE@@TOOLS_TRUE@ lib/libbluetooth-internal.la am__tools_cltest_SOURCES_DIST = tools/cltest.c @TOOLS_TRUE@am_tools_cltest_OBJECTS = tools/cltest.$(OBJEXT) tools_cltest_OBJECTS = $(am_tools_cltest_OBJECTS) @TOOLS_TRUE@tools_cltest_DEPENDENCIES = lib/libbluetooth-internal.la \ @TOOLS_TRUE@ src/libshared-mainloop.la am__tools_create_image_SOURCES_DIST = tools/create-image.c @TOOLS_TRUE@am_tools_create_image_OBJECTS = \ @TOOLS_TRUE@ tools/create-image.$(OBJEXT) tools_create_image_OBJECTS = $(am_tools_create_image_OBJECTS) tools_create_image_LDADD = $(LDADD) am__tools_eddystone_SOURCES_DIST = tools/eddystone.c monitor/bt.h @TOOLS_TRUE@am_tools_eddystone_OBJECTS = tools/eddystone.$(OBJEXT) tools_eddystone_OBJECTS = $(am_tools_eddystone_OBJECTS) @TOOLS_TRUE@tools_eddystone_DEPENDENCIES = src/libshared-mainloop.la am__tools_gap_tester_SOURCES_DIST = tools/gap-tester.c monitor/bt.h \ emulator/hciemu.h emulator/hciemu.c emulator/vhci.h \ emulator/vhci.c emulator/btdev.h emulator/btdev.c \ emulator/bthost.h emulator/bthost.c emulator/smp.c @TESTING_TRUE@am_tools_gap_tester_OBJECTS = \ @TESTING_TRUE@ tools/gap-tester.$(OBJEXT) \ @TESTING_TRUE@ emulator/hciemu.$(OBJEXT) \ @TESTING_TRUE@ emulator/vhci.$(OBJEXT) emulator/btdev.$(OBJEXT) \ @TESTING_TRUE@ emulator/bthost.$(OBJEXT) emulator/smp.$(OBJEXT) tools_gap_tester_OBJECTS = $(am_tools_gap_tester_OBJECTS) @TESTING_TRUE@tools_gap_tester_DEPENDENCIES = \ @TESTING_TRUE@ lib/libbluetooth-internal.la \ @TESTING_TRUE@ gdbus/libgdbus-internal.la src/libshared-glib.la \ @TESTING_TRUE@ $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1) am__tools_gatt_service_SOURCES_DIST = tools/gatt-service.c @TOOLS_TRUE@am_tools_gatt_service_OBJECTS = \ @TOOLS_TRUE@ tools/gatt-service.$(OBJEXT) tools_gatt_service_OBJECTS = $(am_tools_gatt_service_OBJECTS) @TOOLS_TRUE@tools_gatt_service_DEPENDENCIES = \ @TOOLS_TRUE@ gdbus/libgdbus-internal.la \ @TOOLS_TRUE@ src/libshared-mainloop.la $(am__DEPENDENCIES_1) \ @TOOLS_TRUE@ $(am__DEPENDENCIES_1) am__tools_hci_tester_SOURCES_DIST = tools/hci-tester.c monitor/bt.h @TESTING_TRUE@am_tools_hci_tester_OBJECTS = \ @TESTING_TRUE@ tools/hci-tester.$(OBJEXT) tools_hci_tester_OBJECTS = $(am_tools_hci_tester_OBJECTS) @TESTING_TRUE@tools_hci_tester_DEPENDENCIES = src/libshared-glib.la \ @TESTING_TRUE@ $(am__DEPENDENCIES_1) am__tools_hciattach_SOURCES_DIST = tools/hciattach.c tools/hciattach.h \ tools/hciattach_st.c tools/hciattach_ti.c \ tools/hciattach_tialt.c tools/hciattach_ath3k.c \ tools/hciattach_qualcomm.c tools/hciattach_intel.c \ tools/hciattach_bcm43xx.c @DEPRECATED_TRUE@@TOOLS_TRUE@am_tools_hciattach_OBJECTS = \ @DEPRECATED_TRUE@@TOOLS_TRUE@ tools/hciattach.$(OBJEXT) \ @DEPRECATED_TRUE@@TOOLS_TRUE@ tools/hciattach_st.$(OBJEXT) \ @DEPRECATED_TRUE@@TOOLS_TRUE@ tools/hciattach_ti.$(OBJEXT) \ @DEPRECATED_TRUE@@TOOLS_TRUE@ tools/hciattach_tialt.$(OBJEXT) \ @DEPRECATED_TRUE@@TOOLS_TRUE@ tools/hciattach_ath3k.$(OBJEXT) \ @DEPRECATED_TRUE@@TOOLS_TRUE@ tools/hciattach_qualcomm.$(OBJEXT) \ @DEPRECATED_TRUE@@TOOLS_TRUE@ tools/hciattach_intel.$(OBJEXT) \ @DEPRECATED_TRUE@@TOOLS_TRUE@ tools/hciattach_bcm43xx.$(OBJEXT) tools_hciattach_OBJECTS = $(am_tools_hciattach_OBJECTS) @DEPRECATED_TRUE@@TOOLS_TRUE@tools_hciattach_DEPENDENCIES = \ @DEPRECATED_TRUE@@TOOLS_TRUE@ lib/libbluetooth-internal.la am__tools_hciconfig_SOURCES_DIST = tools/hciconfig.c @DEPRECATED_TRUE@@TOOLS_TRUE@am_tools_hciconfig_OBJECTS = \ @DEPRECATED_TRUE@@TOOLS_TRUE@ tools/hciconfig.$(OBJEXT) tools_hciconfig_OBJECTS = $(am_tools_hciconfig_OBJECTS) @DEPRECATED_TRUE@@TOOLS_TRUE@tools_hciconfig_DEPENDENCIES = \ @DEPRECATED_TRUE@@TOOLS_TRUE@ lib/libbluetooth-internal.la am__tools_hcidump_SOURCES_DIST = tools/hcidump.c tools/parser/parser.h \ tools/parser/parser.c tools/parser/lmp.c tools/parser/hci.c \ tools/parser/l2cap.h tools/parser/l2cap.c tools/parser/smp.c \ tools/parser/att.c tools/parser/sdp.h tools/parser/sdp.c \ tools/parser/rfcomm.h tools/parser/rfcomm.c \ tools/parser/bnep.c tools/parser/cmtp.c tools/parser/hidp.c \ tools/parser/hcrp.c tools/parser/avdtp.c tools/parser/avctp.c \ tools/parser/avrcp.c tools/parser/sap.c tools/parser/obex.c \ tools/parser/capi.c tools/parser/ppp.c tools/parser/tcpip.c \ tools/parser/ericsson.c tools/parser/csr.c tools/parser/bpa.c @DEPRECATED_TRUE@@TOOLS_TRUE@am_tools_hcidump_OBJECTS = \ @DEPRECATED_TRUE@@TOOLS_TRUE@ tools/hcidump.$(OBJEXT) \ @DEPRECATED_TRUE@@TOOLS_TRUE@ tools/parser/parser.$(OBJEXT) \ @DEPRECATED_TRUE@@TOOLS_TRUE@ tools/parser/lmp.$(OBJEXT) \ @DEPRECATED_TRUE@@TOOLS_TRUE@ tools/parser/hci.$(OBJEXT) \ @DEPRECATED_TRUE@@TOOLS_TRUE@ tools/parser/l2cap.$(OBJEXT) \ @DEPRECATED_TRUE@@TOOLS_TRUE@ tools/parser/smp.$(OBJEXT) \ @DEPRECATED_TRUE@@TOOLS_TRUE@ tools/parser/att.$(OBJEXT) \ @DEPRECATED_TRUE@@TOOLS_TRUE@ tools/parser/sdp.$(OBJEXT) \ @DEPRECATED_TRUE@@TOOLS_TRUE@ tools/parser/rfcomm.$(OBJEXT) \ @DEPRECATED_TRUE@@TOOLS_TRUE@ tools/parser/bnep.$(OBJEXT) \ @DEPRECATED_TRUE@@TOOLS_TRUE@ tools/parser/cmtp.$(OBJEXT) \ @DEPRECATED_TRUE@@TOOLS_TRUE@ tools/parser/hidp.$(OBJEXT) \ @DEPRECATED_TRUE@@TOOLS_TRUE@ tools/parser/hcrp.$(OBJEXT) \ @DEPRECATED_TRUE@@TOOLS_TRUE@ tools/parser/avdtp.$(OBJEXT) \ @DEPRECATED_TRUE@@TOOLS_TRUE@ tools/parser/avctp.$(OBJEXT) \ @DEPRECATED_TRUE@@TOOLS_TRUE@ tools/parser/avrcp.$(OBJEXT) \ @DEPRECATED_TRUE@@TOOLS_TRUE@ tools/parser/sap.$(OBJEXT) \ @DEPRECATED_TRUE@@TOOLS_TRUE@ tools/parser/obex.$(OBJEXT) \ @DEPRECATED_TRUE@@TOOLS_TRUE@ tools/parser/capi.$(OBJEXT) \ @DEPRECATED_TRUE@@TOOLS_TRUE@ tools/parser/ppp.$(OBJEXT) \ @DEPRECATED_TRUE@@TOOLS_TRUE@ tools/parser/tcpip.$(OBJEXT) \ @DEPRECATED_TRUE@@TOOLS_TRUE@ tools/parser/ericsson.$(OBJEXT) \ @DEPRECATED_TRUE@@TOOLS_TRUE@ tools/parser/csr.$(OBJEXT) \ @DEPRECATED_TRUE@@TOOLS_TRUE@ tools/parser/bpa.$(OBJEXT) tools_hcidump_OBJECTS = $(am_tools_hcidump_OBJECTS) @DEPRECATED_TRUE@@TOOLS_TRUE@tools_hcidump_DEPENDENCIES = \ @DEPRECATED_TRUE@@TOOLS_TRUE@ lib/libbluetooth-internal.la tools_hcieventmask_SOURCES = tools/hcieventmask.c tools_hcieventmask_OBJECTS = tools/hcieventmask.$(OBJEXT) @TOOLS_TRUE@tools_hcieventmask_DEPENDENCIES = \ @TOOLS_TRUE@ lib/libbluetooth-internal.la tools_hcisecfilter_SOURCES = tools/hcisecfilter.c tools_hcisecfilter_OBJECTS = tools/hcisecfilter.$(OBJEXT) tools_hcisecfilter_LDADD = $(LDADD) am__tools_hcitool_SOURCES_DIST = tools/hcitool.c src/oui.h src/oui.c @DEPRECATED_TRUE@@TOOLS_TRUE@am_tools_hcitool_OBJECTS = \ @DEPRECATED_TRUE@@TOOLS_TRUE@ tools/hcitool.$(OBJEXT) \ @DEPRECATED_TRUE@@TOOLS_TRUE@ src/oui.$(OBJEXT) tools_hcitool_OBJECTS = $(am_tools_hcitool_OBJECTS) @DEPRECATED_TRUE@@TOOLS_TRUE@tools_hcitool_DEPENDENCIES = \ @DEPRECATED_TRUE@@TOOLS_TRUE@ lib/libbluetooth-internal.la \ @DEPRECATED_TRUE@@TOOLS_TRUE@ $(am__DEPENDENCIES_1) am__tools_hex2hcd_SOURCES_DIST = tools/hex2hcd.c tools/missing.h @TOOLS_TRUE@am_tools_hex2hcd_OBJECTS = tools/hex2hcd.$(OBJEXT) tools_hex2hcd_OBJECTS = $(am_tools_hex2hcd_OBJECTS) tools_hex2hcd_LDADD = $(LDADD) tools_hid2hci_SOURCES = tools/hid2hci.c tools_hid2hci_OBJECTS = tools/hid2hci.$(OBJEXT) @HID2HCI_TRUE@tools_hid2hci_DEPENDENCIES = $(am__DEPENDENCIES_1) tools_hwdb_SOURCES = tools/hwdb.c tools_hwdb_OBJECTS = tools/hwdb.$(OBJEXT) @TOOLS_TRUE@tools_hwdb_DEPENDENCIES = lib/libbluetooth-internal.la am__tools_ibeacon_SOURCES_DIST = tools/ibeacon.c monitor/bt.h @TOOLS_TRUE@am_tools_ibeacon_OBJECTS = tools/ibeacon.$(OBJEXT) tools_ibeacon_OBJECTS = $(am_tools_ibeacon_OBJECTS) @TOOLS_TRUE@tools_ibeacon_DEPENDENCIES = src/libshared-mainloop.la am__tools_ioctl_tester_SOURCES_DIST = tools/ioctl-tester.c \ monitor/bt.h emulator/hciemu.h emulator/hciemu.c \ emulator/vhci.h emulator/vhci.c emulator/btdev.h \ emulator/btdev.c emulator/bthost.h emulator/bthost.c \ emulator/smp.c @TESTING_TRUE@am_tools_ioctl_tester_OBJECTS = \ @TESTING_TRUE@ tools/ioctl-tester.$(OBJEXT) \ @TESTING_TRUE@ emulator/hciemu.$(OBJEXT) \ @TESTING_TRUE@ emulator/vhci.$(OBJEXT) emulator/btdev.$(OBJEXT) \ @TESTING_TRUE@ emulator/bthost.$(OBJEXT) emulator/smp.$(OBJEXT) tools_ioctl_tester_OBJECTS = $(am_tools_ioctl_tester_OBJECTS) @TESTING_TRUE@tools_ioctl_tester_DEPENDENCIES = \ @TESTING_TRUE@ lib/libbluetooth-internal.la \ @TESTING_TRUE@ src/libshared-glib.la $(am__DEPENDENCIES_1) am__tools_iso_tester_SOURCES_DIST = tools/iso-tester.c tools/tester.h \ monitor/bt.h emulator/hciemu.h emulator/hciemu.c \ emulator/vhci.h emulator/vhci.c emulator/btdev.h \ emulator/btdev.c emulator/bthost.h emulator/bthost.c \ emulator/smp.c @TESTING_TRUE@am_tools_iso_tester_OBJECTS = \ @TESTING_TRUE@ tools/iso-tester.$(OBJEXT) \ @TESTING_TRUE@ emulator/hciemu.$(OBJEXT) \ @TESTING_TRUE@ emulator/vhci.$(OBJEXT) emulator/btdev.$(OBJEXT) \ @TESTING_TRUE@ emulator/bthost.$(OBJEXT) emulator/smp.$(OBJEXT) tools_iso_tester_OBJECTS = $(am_tools_iso_tester_OBJECTS) @TESTING_TRUE@tools_iso_tester_DEPENDENCIES = \ @TESTING_TRUE@ lib/libbluetooth-internal.la \ @TESTING_TRUE@ src/libshared-glib.la $(am__DEPENDENCIES_1) tools_isotest_SOURCES = tools/isotest.c tools_isotest_OBJECTS = tools/isotest.$(OBJEXT) @TOOLS_TRUE@tools_isotest_DEPENDENCIES = lib/libbluetooth-internal.la am__tools_l2cap_tester_SOURCES_DIST = tools/l2cap-tester.c \ tools/tester.h monitor/bt.h emulator/hciemu.h \ emulator/hciemu.c emulator/vhci.h emulator/vhci.c \ emulator/btdev.h emulator/btdev.c emulator/bthost.h \ emulator/bthost.c emulator/smp.c @TESTING_TRUE@am_tools_l2cap_tester_OBJECTS = \ @TESTING_TRUE@ tools/l2cap-tester.$(OBJEXT) \ @TESTING_TRUE@ emulator/hciemu.$(OBJEXT) \ @TESTING_TRUE@ emulator/vhci.$(OBJEXT) emulator/btdev.$(OBJEXT) \ @TESTING_TRUE@ emulator/bthost.$(OBJEXT) emulator/smp.$(OBJEXT) tools_l2cap_tester_OBJECTS = $(am_tools_l2cap_tester_OBJECTS) @TESTING_TRUE@tools_l2cap_tester_DEPENDENCIES = \ @TESTING_TRUE@ lib/libbluetooth-internal.la \ @TESTING_TRUE@ src/libshared-glib.la $(am__DEPENDENCIES_1) tools_l2ping_SOURCES = tools/l2ping.c tools_l2ping_OBJECTS = tools/l2ping.$(OBJEXT) @TOOLS_TRUE@tools_l2ping_DEPENDENCIES = lib/libbluetooth-internal.la tools_l2test_SOURCES = tools/l2test.c tools_l2test_OBJECTS = tools/l2test.$(OBJEXT) @TOOLS_TRUE@tools_l2test_DEPENDENCIES = lib/libbluetooth-internal.la am__tools_mcaptest_SOURCES_DIST = tools/mcaptest.c btio/btio.h \ btio/btio.c src/log.c src/log.h profiles/health/mcap.h \ profiles/health/mcap.c @TOOLS_TRUE@am_tools_mcaptest_OBJECTS = tools/mcaptest.$(OBJEXT) \ @TOOLS_TRUE@ btio/btio.$(OBJEXT) src/log.$(OBJEXT) \ @TOOLS_TRUE@ profiles/health/mcap.$(OBJEXT) tools_mcaptest_OBJECTS = $(am_tools_mcaptest_OBJECTS) @TOOLS_TRUE@tools_mcaptest_DEPENDENCIES = \ @TOOLS_TRUE@ lib/libbluetooth-internal.la $(am__DEPENDENCIES_1) \ @TOOLS_TRUE@ src/libshared-mainloop.la am__tools_mesh_cfgclient_SOURCES_DIST = tools/mesh-cfgclient.c \ tools/mesh/model.h tools/mesh/config-model.h \ tools/mesh/cfgcli.h tools/mesh/cfgcli.c tools/mesh/keys.h \ tools/mesh/keys.c tools/mesh/util.h tools/mesh/util.c \ tools/mesh/remote.h tools/mesh/remote.c tools/mesh/agent.h \ tools/mesh/agent.c tools/mesh/mesh-db.h tools/mesh/mesh-db.c \ mesh/util.h mesh/util.c mesh/crypto.h mesh/crypto.c @MESH_TRUE@@TOOLS_TRUE@am_tools_mesh_cfgclient_OBJECTS = \ @MESH_TRUE@@TOOLS_TRUE@ tools/mesh-cfgclient.$(OBJEXT) \ @MESH_TRUE@@TOOLS_TRUE@ tools/mesh/cfgcli.$(OBJEXT) \ @MESH_TRUE@@TOOLS_TRUE@ tools/mesh/keys.$(OBJEXT) \ @MESH_TRUE@@TOOLS_TRUE@ tools/mesh/util.$(OBJEXT) \ @MESH_TRUE@@TOOLS_TRUE@ tools/mesh/remote.$(OBJEXT) \ @MESH_TRUE@@TOOLS_TRUE@ tools/mesh/agent.$(OBJEXT) \ @MESH_TRUE@@TOOLS_TRUE@ tools/mesh/mesh-db.$(OBJEXT) \ @MESH_TRUE@@TOOLS_TRUE@ mesh/util.$(OBJEXT) \ @MESH_TRUE@@TOOLS_TRUE@ mesh/crypto.$(OBJEXT) tools_mesh_cfgclient_OBJECTS = $(am_tools_mesh_cfgclient_OBJECTS) @MESH_TRUE@@TOOLS_TRUE@tools_mesh_cfgclient_DEPENDENCIES = \ @MESH_TRUE@@TOOLS_TRUE@ lib/libbluetooth-internal.la \ @MESH_TRUE@@TOOLS_TRUE@ src/libshared-ell.la \ @MESH_TRUE@@TOOLS_TRUE@ $(am__DEPENDENCIES_2) am__tools_mesh_cfgtest_SOURCES_DIST = tools/mesh-cfgtest.c @MESH_TRUE@@TOOLS_TRUE@am_tools_mesh_cfgtest_OBJECTS = \ @MESH_TRUE@@TOOLS_TRUE@ tools/mesh-cfgtest.$(OBJEXT) tools_mesh_cfgtest_OBJECTS = $(am_tools_mesh_cfgtest_OBJECTS) @MESH_TRUE@@TOOLS_TRUE@tools_mesh_cfgtest_DEPENDENCIES = \ @MESH_TRUE@@TOOLS_TRUE@ lib/libbluetooth-internal.la \ @MESH_TRUE@@TOOLS_TRUE@ src/libshared-ell.la \ @MESH_TRUE@@TOOLS_TRUE@ $(am__DEPENDENCIES_2) am__tools_mesh_tester_SOURCES_DIST = tools/mesh-tester.c monitor/bt.h \ emulator/hciemu.h emulator/hciemu.c emulator/vhci.h \ emulator/vhci.c emulator/btdev.h emulator/btdev.c \ emulator/bthost.h emulator/bthost.c emulator/smp.c @TESTING_TRUE@am_tools_mesh_tester_OBJECTS = \ @TESTING_TRUE@ tools/mesh-tester.$(OBJEXT) \ @TESTING_TRUE@ emulator/hciemu.$(OBJEXT) \ @TESTING_TRUE@ emulator/vhci.$(OBJEXT) emulator/btdev.$(OBJEXT) \ @TESTING_TRUE@ emulator/bthost.$(OBJEXT) emulator/smp.$(OBJEXT) tools_mesh_tester_OBJECTS = $(am_tools_mesh_tester_OBJECTS) @TESTING_TRUE@tools_mesh_tester_DEPENDENCIES = \ @TESTING_TRUE@ lib/libbluetooth-internal.la \ @TESTING_TRUE@ src/libshared-glib.la $(am__DEPENDENCIES_1) am__tools_meshctl_SOURCES_DIST = tools/meshctl.c tools/mesh/agent.h \ tools/mesh/agent.c tools/mesh/config-model.h \ tools/mesh-gatt/mesh-net.h tools/mesh-gatt/node.h \ tools/mesh-gatt/node.c tools/mesh-gatt/gatt.h \ tools/mesh-gatt/gatt.c tools/mesh-gatt/crypto.h \ tools/mesh-gatt/crypto.c tools/mesh-gatt/keys.h \ tools/mesh-gatt/net.h tools/mesh-gatt/net.c \ tools/mesh-gatt/prov.h tools/mesh-gatt/prov.c \ tools/mesh-gatt/util.h tools/mesh-gatt/util.c \ tools/mesh-gatt/prov-db.h tools/mesh-gatt/prov-db.c \ tools/mesh-gatt/config-client.c \ tools/mesh-gatt/config-server.c tools/mesh-gatt/onoff-model.h \ tools/mesh-gatt/onoff-model.c @DEPRECATED_TRUE@@MESH_TRUE@@TOOLS_TRUE@am_tools_meshctl_OBJECTS = tools/meshctl.$(OBJEXT) \ @DEPRECATED_TRUE@@MESH_TRUE@@TOOLS_TRUE@ tools/mesh/agent.$(OBJEXT) \ @DEPRECATED_TRUE@@MESH_TRUE@@TOOLS_TRUE@ tools/mesh-gatt/node.$(OBJEXT) \ @DEPRECATED_TRUE@@MESH_TRUE@@TOOLS_TRUE@ tools/mesh-gatt/gatt.$(OBJEXT) \ @DEPRECATED_TRUE@@MESH_TRUE@@TOOLS_TRUE@ tools/mesh-gatt/crypto.$(OBJEXT) \ @DEPRECATED_TRUE@@MESH_TRUE@@TOOLS_TRUE@ tools/mesh-gatt/net.$(OBJEXT) \ @DEPRECATED_TRUE@@MESH_TRUE@@TOOLS_TRUE@ tools/mesh-gatt/prov.$(OBJEXT) \ @DEPRECATED_TRUE@@MESH_TRUE@@TOOLS_TRUE@ tools/mesh-gatt/util.$(OBJEXT) \ @DEPRECATED_TRUE@@MESH_TRUE@@TOOLS_TRUE@ tools/mesh-gatt/prov-db.$(OBJEXT) \ @DEPRECATED_TRUE@@MESH_TRUE@@TOOLS_TRUE@ tools/mesh-gatt/config-client.$(OBJEXT) \ @DEPRECATED_TRUE@@MESH_TRUE@@TOOLS_TRUE@ tools/mesh-gatt/config-server.$(OBJEXT) \ @DEPRECATED_TRUE@@MESH_TRUE@@TOOLS_TRUE@ tools/mesh-gatt/onoff-model.$(OBJEXT) tools_meshctl_OBJECTS = $(am_tools_meshctl_OBJECTS) @DEPRECATED_TRUE@@MESH_TRUE@@TOOLS_TRUE@tools_meshctl_DEPENDENCIES = gdbus/libgdbus-internal.la \ @DEPRECATED_TRUE@@MESH_TRUE@@TOOLS_TRUE@ src/libshared-glib.la \ @DEPRECATED_TRUE@@MESH_TRUE@@TOOLS_TRUE@ lib/libbluetooth-internal.la \ @DEPRECATED_TRUE@@MESH_TRUE@@TOOLS_TRUE@ $(am__DEPENDENCIES_1) \ @DEPRECATED_TRUE@@MESH_TRUE@@TOOLS_TRUE@ $(am__DEPENDENCIES_1) am__tools_mgmt_tester_SOURCES_DIST = tools/mgmt-tester.c monitor/bt.h \ emulator/hciemu.h emulator/hciemu.c emulator/vhci.h \ emulator/vhci.c emulator/btdev.h emulator/btdev.c \ emulator/bthost.h emulator/bthost.c emulator/smp.c @TESTING_TRUE@am_tools_mgmt_tester_OBJECTS = \ @TESTING_TRUE@ tools/mgmt-tester.$(OBJEXT) \ @TESTING_TRUE@ emulator/hciemu.$(OBJEXT) \ @TESTING_TRUE@ emulator/vhci.$(OBJEXT) emulator/btdev.$(OBJEXT) \ @TESTING_TRUE@ emulator/bthost.$(OBJEXT) emulator/smp.$(OBJEXT) tools_mgmt_tester_OBJECTS = $(am_tools_mgmt_tester_OBJECTS) @TESTING_TRUE@tools_mgmt_tester_DEPENDENCIES = \ @TESTING_TRUE@ lib/libbluetooth-internal.la \ @TESTING_TRUE@ src/libshared-glib.la $(am__DEPENDENCIES_1) am__tools_mpris_proxy_SOURCES_DIST = tools/mpris-proxy.c @TOOLS_TRUE@am_tools_mpris_proxy_OBJECTS = \ @TOOLS_TRUE@ tools/mpris-proxy.$(OBJEXT) tools_mpris_proxy_OBJECTS = $(am_tools_mpris_proxy_OBJECTS) @TOOLS_TRUE@tools_mpris_proxy_DEPENDENCIES = \ @TOOLS_TRUE@ gdbus/libgdbus-internal.la $(am__DEPENDENCIES_1) \ @TOOLS_TRUE@ $(am__DEPENDENCIES_1) am__tools_nokfw_SOURCES_DIST = tools/nokfw.c @TOOLS_TRUE@am_tools_nokfw_OBJECTS = tools/nokfw.$(OBJEXT) tools_nokfw_OBJECTS = $(am_tools_nokfw_OBJECTS) tools_nokfw_LDADD = $(LDADD) am__tools_obex_client_tool_SOURCES_DIST = gobex/gobex.h gobex/gobex.c \ gobex/gobex-defs.h gobex/gobex-defs.c gobex/gobex-packet.c \ gobex/gobex-packet.h gobex/gobex-header.c gobex/gobex-header.h \ gobex/gobex-transfer.c gobex/gobex-debug.h \ gobex/gobex-apparam.c gobex/gobex-apparam.h btio/btio.h \ btio/btio.c tools/obex-client-tool.c am__objects_39 = gobex/gobex.$(OBJEXT) gobex/gobex-defs.$(OBJEXT) \ gobex/gobex-packet.$(OBJEXT) gobex/gobex-header.$(OBJEXT) \ gobex/gobex-transfer.$(OBJEXT) gobex/gobex-apparam.$(OBJEXT) am__objects_40 = btio/btio.$(OBJEXT) @READLINE_TRUE@am_tools_obex_client_tool_OBJECTS = $(am__objects_39) \ @READLINE_TRUE@ $(am__objects_40) \ @READLINE_TRUE@ tools/obex-client-tool.$(OBJEXT) tools_obex_client_tool_OBJECTS = $(am_tools_obex_client_tool_OBJECTS) @READLINE_TRUE@tools_obex_client_tool_DEPENDENCIES = \ @READLINE_TRUE@ lib/libbluetooth-internal.la \ @READLINE_TRUE@ src/libshared-glib.la $(am__DEPENDENCIES_1) am__tools_obex_server_tool_SOURCES_DIST = gobex/gobex.h gobex/gobex.c \ gobex/gobex-defs.h gobex/gobex-defs.c gobex/gobex-packet.c \ gobex/gobex-packet.h gobex/gobex-header.c gobex/gobex-header.h \ gobex/gobex-transfer.c gobex/gobex-debug.h \ gobex/gobex-apparam.c gobex/gobex-apparam.h btio/btio.h \ btio/btio.c tools/obex-server-tool.c @READLINE_TRUE@am_tools_obex_server_tool_OBJECTS = $(am__objects_39) \ @READLINE_TRUE@ $(am__objects_40) \ @READLINE_TRUE@ tools/obex-server-tool.$(OBJEXT) tools_obex_server_tool_OBJECTS = $(am_tools_obex_server_tool_OBJECTS) @READLINE_TRUE@tools_obex_server_tool_DEPENDENCIES = \ @READLINE_TRUE@ lib/libbluetooth-internal.la \ @READLINE_TRUE@ src/libshared-glib.la $(am__DEPENDENCIES_1) am__tools_obexctl_SOURCES_DIST = tools/obexctl.c @READLINE_TRUE@am_tools_obexctl_OBJECTS = tools/obexctl.$(OBJEXT) tools_obexctl_OBJECTS = $(am_tools_obexctl_OBJECTS) @READLINE_TRUE@tools_obexctl_DEPENDENCIES = \ @READLINE_TRUE@ gdbus/libgdbus-internal.la \ @READLINE_TRUE@ src/libshared-glib.la $(am__DEPENDENCIES_1) \ @READLINE_TRUE@ $(am__DEPENDENCIES_1) am__tools_oobtest_SOURCES_DIST = tools/oobtest.c @TOOLS_TRUE@am_tools_oobtest_OBJECTS = tools/oobtest.$(OBJEXT) tools_oobtest_OBJECTS = $(am_tools_oobtest_OBJECTS) @TOOLS_TRUE@tools_oobtest_DEPENDENCIES = lib/libbluetooth-internal.la \ @TOOLS_TRUE@ src/libshared-mainloop.la tools_rctest_SOURCES = tools/rctest.c tools_rctest_OBJECTS = tools/rctest.$(OBJEXT) @TOOLS_TRUE@tools_rctest_DEPENDENCIES = lib/libbluetooth-internal.la tools_rfcomm_SOURCES = tools/rfcomm.c tools_rfcomm_OBJECTS = tools/rfcomm.$(OBJEXT) @DEPRECATED_TRUE@@TOOLS_TRUE@tools_rfcomm_DEPENDENCIES = \ @DEPRECATED_TRUE@@TOOLS_TRUE@ lib/libbluetooth-internal.la am__tools_rfcomm_tester_SOURCES_DIST = tools/rfcomm-tester.c \ monitor/bt.h emulator/hciemu.h emulator/hciemu.c \ emulator/vhci.h emulator/vhci.c emulator/btdev.h \ emulator/btdev.c emulator/bthost.h emulator/bthost.c \ emulator/smp.c @TESTING_TRUE@am_tools_rfcomm_tester_OBJECTS = \ @TESTING_TRUE@ tools/rfcomm-tester.$(OBJEXT) \ @TESTING_TRUE@ emulator/hciemu.$(OBJEXT) \ @TESTING_TRUE@ emulator/vhci.$(OBJEXT) emulator/btdev.$(OBJEXT) \ @TESTING_TRUE@ emulator/bthost.$(OBJEXT) emulator/smp.$(OBJEXT) tools_rfcomm_tester_OBJECTS = $(am_tools_rfcomm_tester_OBJECTS) @TESTING_TRUE@tools_rfcomm_tester_DEPENDENCIES = \ @TESTING_TRUE@ lib/libbluetooth-internal.la \ @TESTING_TRUE@ src/libshared-glib.la $(am__DEPENDENCIES_1) am__tools_rtlfw_SOURCES_DIST = tools/rtlfw.c @TOOLS_TRUE@am_tools_rtlfw_OBJECTS = tools/rtlfw.$(OBJEXT) tools_rtlfw_OBJECTS = $(am_tools_rtlfw_OBJECTS) tools_rtlfw_LDADD = $(LDADD) am__tools_sco_tester_SOURCES_DIST = tools/sco-tester.c tools/tester.h \ monitor/bt.h emulator/hciemu.h emulator/hciemu.c \ emulator/vhci.h emulator/vhci.c emulator/btdev.h \ emulator/btdev.c emulator/bthost.h emulator/bthost.c \ emulator/smp.c @TESTING_TRUE@am_tools_sco_tester_OBJECTS = \ @TESTING_TRUE@ tools/sco-tester.$(OBJEXT) \ @TESTING_TRUE@ emulator/hciemu.$(OBJEXT) \ @TESTING_TRUE@ emulator/vhci.$(OBJEXT) emulator/btdev.$(OBJEXT) \ @TESTING_TRUE@ emulator/bthost.$(OBJEXT) emulator/smp.$(OBJEXT) tools_sco_tester_OBJECTS = $(am_tools_sco_tester_OBJECTS) @TESTING_TRUE@tools_sco_tester_DEPENDENCIES = \ @TESTING_TRUE@ lib/libbluetooth-internal.la \ @TESTING_TRUE@ src/libshared-glib.la $(am__DEPENDENCIES_1) tools_scotest_SOURCES = tools/scotest.c tools_scotest_OBJECTS = tools/scotest.$(OBJEXT) @TOOLS_TRUE@tools_scotest_DEPENDENCIES = lib/libbluetooth-internal.la am__tools_sdptool_SOURCES_DIST = tools/sdptool.c src/sdp-xml.h \ src/sdp-xml.c @DEPRECATED_TRUE@@TOOLS_TRUE@am_tools_sdptool_OBJECTS = \ @DEPRECATED_TRUE@@TOOLS_TRUE@ tools/sdptool.$(OBJEXT) \ @DEPRECATED_TRUE@@TOOLS_TRUE@ src/sdp-xml.$(OBJEXT) tools_sdptool_OBJECTS = $(am_tools_sdptool_OBJECTS) @DEPRECATED_TRUE@@TOOLS_TRUE@tools_sdptool_DEPENDENCIES = \ @DEPRECATED_TRUE@@TOOLS_TRUE@ lib/libbluetooth-internal.la \ @DEPRECATED_TRUE@@TOOLS_TRUE@ $(am__DEPENDENCIES_1) am__tools_seq2bseq_SOURCES_DIST = tools/seq2bseq.c @TOOLS_TRUE@am_tools_seq2bseq_OBJECTS = tools/seq2bseq.$(OBJEXT) tools_seq2bseq_OBJECTS = $(am_tools_seq2bseq_OBJECTS) tools_seq2bseq_LDADD = $(LDADD) am__tools_smp_tester_SOURCES_DIST = tools/smp-tester.c monitor/bt.h \ emulator/hciemu.h emulator/hciemu.c emulator/vhci.h \ emulator/vhci.c emulator/btdev.h emulator/btdev.c \ emulator/bthost.h emulator/bthost.c emulator/smp.c @TESTING_TRUE@am_tools_smp_tester_OBJECTS = \ @TESTING_TRUE@ tools/smp-tester.$(OBJEXT) \ @TESTING_TRUE@ emulator/hciemu.$(OBJEXT) \ @TESTING_TRUE@ emulator/vhci.$(OBJEXT) emulator/btdev.$(OBJEXT) \ @TESTING_TRUE@ emulator/bthost.$(OBJEXT) emulator/smp.$(OBJEXT) tools_smp_tester_OBJECTS = $(am_tools_smp_tester_OBJECTS) @TESTING_TRUE@tools_smp_tester_DEPENDENCIES = \ @TESTING_TRUE@ lib/libbluetooth-internal.la \ @TESTING_TRUE@ src/libshared-glib.la $(am__DEPENDENCIES_1) tools_test_runner_SOURCES = tools/test-runner.c tools_test_runner_OBJECTS = tools/test-runner.$(OBJEXT) tools_test_runner_LDADD = $(LDADD) am__tools_userchan_tester_SOURCES_DIST = tools/userchan-tester.c \ monitor/bt.h emulator/hciemu.h emulator/hciemu.c \ emulator/vhci.h emulator/vhci.c emulator/btdev.h \ emulator/btdev.c emulator/bthost.h emulator/bthost.c \ emulator/smp.c @TESTING_TRUE@am_tools_userchan_tester_OBJECTS = \ @TESTING_TRUE@ tools/userchan-tester.$(OBJEXT) \ @TESTING_TRUE@ emulator/hciemu.$(OBJEXT) \ @TESTING_TRUE@ emulator/vhci.$(OBJEXT) emulator/btdev.$(OBJEXT) \ @TESTING_TRUE@ emulator/bthost.$(OBJEXT) emulator/smp.$(OBJEXT) tools_userchan_tester_OBJECTS = $(am_tools_userchan_tester_OBJECTS) @TESTING_TRUE@tools_userchan_tester_DEPENDENCIES = \ @TESTING_TRUE@ lib/libbluetooth-internal.la \ @TESTING_TRUE@ src/libshared-glib.la $(am__DEPENDENCIES_1) am_unit_test_avctp_OBJECTS = unit/test-avctp.$(OBJEXT) \ src/log.$(OBJEXT) android/avctp.$(OBJEXT) unit_test_avctp_OBJECTS = $(am_unit_test_avctp_OBJECTS) unit_test_avctp_DEPENDENCIES = src/libshared-glib.la \ $(am__DEPENDENCIES_1) am_unit_test_avdtp_OBJECTS = unit/test-avdtp.$(OBJEXT) \ src/log.$(OBJEXT) android/avdtp.$(OBJEXT) unit_test_avdtp_OBJECTS = $(am_unit_test_avdtp_OBJECTS) unit_test_avdtp_DEPENDENCIES = src/libshared-glib.la \ $(am__DEPENDENCIES_1) am_unit_test_avrcp_OBJECTS = unit/test-avrcp.$(OBJEXT) \ src/log.$(OBJEXT) android/avctp.$(OBJEXT) \ android/avrcp-lib.$(OBJEXT) unit_test_avrcp_OBJECTS = $(am_unit_test_avrcp_OBJECTS) unit_test_avrcp_DEPENDENCIES = lib/libbluetooth-internal.la \ src/libshared-glib.la $(am__DEPENDENCIES_1) am_unit_test_bap_OBJECTS = unit/test-bap.$(OBJEXT) unit_test_bap_OBJECTS = $(am_unit_test_bap_OBJECTS) unit_test_bap_DEPENDENCIES = src/libshared-glib.la \ lib/libbluetooth-internal.la $(am__DEPENDENCIES_1) am_unit_test_bass_OBJECTS = unit/test-bass.$(OBJEXT) $(am__objects_40) unit_test_bass_OBJECTS = $(am_unit_test_bass_OBJECTS) unit_test_bass_DEPENDENCIES = src/libshared-glib.la \ lib/libbluetooth-internal.la $(am__DEPENDENCIES_1) am_unit_test_crc_OBJECTS = unit/test-crc.$(OBJEXT) \ monitor/crc.$(OBJEXT) unit_test_crc_OBJECTS = $(am_unit_test_crc_OBJECTS) unit_test_crc_DEPENDENCIES = src/libshared-glib.la \ $(am__DEPENDENCIES_1) am_unit_test_crypto_OBJECTS = unit/test-crypto.$(OBJEXT) unit_test_crypto_OBJECTS = $(am_unit_test_crypto_OBJECTS) unit_test_crypto_DEPENDENCIES = src/libshared-glib.la \ $(am__DEPENDENCIES_1) am_unit_test_ecc_OBJECTS = unit/test-ecc.$(OBJEXT) unit_test_ecc_OBJECTS = $(am_unit_test_ecc_OBJECTS) unit_test_ecc_DEPENDENCIES = src/libshared-glib.la \ $(am__DEPENDENCIES_1) am_unit_test_eir_OBJECTS = unit/test-eir.$(OBJEXT) src/eir.$(OBJEXT) \ src/uuid-helper.$(OBJEXT) unit_test_eir_OBJECTS = $(am_unit_test_eir_OBJECTS) unit_test_eir_DEPENDENCIES = src/libshared-glib.la \ lib/libbluetooth-internal.la $(am__DEPENDENCIES_1) am_unit_test_gatt_OBJECTS = unit/test-gatt.$(OBJEXT) unit_test_gatt_OBJECTS = $(am_unit_test_gatt_OBJECTS) unit_test_gatt_DEPENDENCIES = src/libshared-glib.la \ lib/libbluetooth-internal.la $(am__DEPENDENCIES_1) am_unit_test_gattrib_OBJECTS = unit/test-gattrib.$(OBJEXT) \ attrib/gattrib.$(OBJEXT) $(am__objects_40) src/log.$(OBJEXT) unit_test_gattrib_OBJECTS = $(am_unit_test_gattrib_OBJECTS) unit_test_gattrib_DEPENDENCIES = src/libshared-glib.la \ lib/libbluetooth-internal.la $(am__DEPENDENCIES_1) \ $(am__DEPENDENCIES_1) am_unit_test_gdbus_client_OBJECTS = unit/test-gdbus-client.$(OBJEXT) unit_test_gdbus_client_OBJECTS = $(am_unit_test_gdbus_client_OBJECTS) unit_test_gdbus_client_DEPENDENCIES = gdbus/libgdbus-internal.la \ src/libshared-glib.la $(am__DEPENDENCIES_1) \ $(am__DEPENDENCIES_1) am__unit_test_gobex_SOURCES_DIST = gobex/gobex.h gobex/gobex.c \ gobex/gobex-defs.h gobex/gobex-defs.c gobex/gobex-packet.c \ gobex/gobex-packet.h gobex/gobex-header.c gobex/gobex-header.h \ gobex/gobex-transfer.c gobex/gobex-debug.h \ gobex/gobex-apparam.c gobex/gobex-apparam.h unit/util.c \ unit/util.h unit/test-gobex.c @OBEX_TRUE@am_unit_test_gobex_OBJECTS = $(am__objects_39) \ @OBEX_TRUE@ unit/util.$(OBJEXT) unit/test-gobex.$(OBJEXT) unit_test_gobex_OBJECTS = $(am_unit_test_gobex_OBJECTS) @OBEX_TRUE@unit_test_gobex_DEPENDENCIES = src/libshared-glib.la \ @OBEX_TRUE@ $(am__DEPENDENCIES_1) am__unit_test_gobex_apparam_SOURCES_DIST = gobex/gobex.h gobex/gobex.c \ gobex/gobex-defs.h gobex/gobex-defs.c gobex/gobex-packet.c \ gobex/gobex-packet.h gobex/gobex-header.c gobex/gobex-header.h \ gobex/gobex-transfer.c gobex/gobex-debug.h \ gobex/gobex-apparam.c gobex/gobex-apparam.h unit/util.c \ unit/util.h unit/test-gobex-apparam.c @OBEX_TRUE@am_unit_test_gobex_apparam_OBJECTS = $(am__objects_39) \ @OBEX_TRUE@ unit/util.$(OBJEXT) \ @OBEX_TRUE@ unit/test-gobex-apparam.$(OBJEXT) unit_test_gobex_apparam_OBJECTS = \ $(am_unit_test_gobex_apparam_OBJECTS) @OBEX_TRUE@unit_test_gobex_apparam_DEPENDENCIES = \ @OBEX_TRUE@ src/libshared-glib.la $(am__DEPENDENCIES_1) am__unit_test_gobex_header_SOURCES_DIST = gobex/gobex.h gobex/gobex.c \ gobex/gobex-defs.h gobex/gobex-defs.c gobex/gobex-packet.c \ gobex/gobex-packet.h gobex/gobex-header.c gobex/gobex-header.h \ gobex/gobex-transfer.c gobex/gobex-debug.h \ gobex/gobex-apparam.c gobex/gobex-apparam.h unit/util.c \ unit/util.h unit/test-gobex-header.c @OBEX_TRUE@am_unit_test_gobex_header_OBJECTS = $(am__objects_39) \ @OBEX_TRUE@ unit/util.$(OBJEXT) \ @OBEX_TRUE@ unit/test-gobex-header.$(OBJEXT) unit_test_gobex_header_OBJECTS = $(am_unit_test_gobex_header_OBJECTS) @OBEX_TRUE@unit_test_gobex_header_DEPENDENCIES = \ @OBEX_TRUE@ src/libshared-glib.la $(am__DEPENDENCIES_1) am__unit_test_gobex_packet_SOURCES_DIST = gobex/gobex.h gobex/gobex.c \ gobex/gobex-defs.h gobex/gobex-defs.c gobex/gobex-packet.c \ gobex/gobex-packet.h gobex/gobex-header.c gobex/gobex-header.h \ gobex/gobex-transfer.c gobex/gobex-debug.h \ gobex/gobex-apparam.c gobex/gobex-apparam.h unit/util.c \ unit/util.h unit/test-gobex-packet.c @OBEX_TRUE@am_unit_test_gobex_packet_OBJECTS = $(am__objects_39) \ @OBEX_TRUE@ unit/util.$(OBJEXT) \ @OBEX_TRUE@ unit/test-gobex-packet.$(OBJEXT) unit_test_gobex_packet_OBJECTS = $(am_unit_test_gobex_packet_OBJECTS) @OBEX_TRUE@unit_test_gobex_packet_DEPENDENCIES = \ @OBEX_TRUE@ src/libshared-glib.la $(am__DEPENDENCIES_1) am__unit_test_gobex_transfer_SOURCES_DIST = gobex/gobex.h \ gobex/gobex.c gobex/gobex-defs.h gobex/gobex-defs.c \ gobex/gobex-packet.c gobex/gobex-packet.h gobex/gobex-header.c \ gobex/gobex-header.h gobex/gobex-transfer.c \ gobex/gobex-debug.h gobex/gobex-apparam.c \ gobex/gobex-apparam.h unit/util.c unit/util.h \ unit/test-gobex-transfer.c @OBEX_TRUE@am_unit_test_gobex_transfer_OBJECTS = $(am__objects_39) \ @OBEX_TRUE@ unit/util.$(OBJEXT) \ @OBEX_TRUE@ unit/test-gobex-transfer.$(OBJEXT) unit_test_gobex_transfer_OBJECTS = \ $(am_unit_test_gobex_transfer_OBJECTS) @OBEX_TRUE@unit_test_gobex_transfer_DEPENDENCIES = \ @OBEX_TRUE@ src/libshared-glib.la $(am__DEPENDENCIES_1) am_unit_test_hfp_OBJECTS = unit/test-hfp.$(OBJEXT) unit_test_hfp_OBJECTS = $(am_unit_test_hfp_OBJECTS) unit_test_hfp_DEPENDENCIES = src/libshared-glib.la \ $(am__DEPENDENCIES_1) am_unit_test_hog_OBJECTS = unit/test-hog.$(OBJEXT) $(am__objects_40) \ profiles/input/hog-lib.$(OBJEXT) \ profiles/scanparam/scpp.$(OBJEXT) \ profiles/battery/bas.$(OBJEXT) \ profiles/deviceinfo/dis.$(OBJEXT) src/log.$(OBJEXT) \ attrib/att.$(OBJEXT) attrib/gatt.$(OBJEXT) \ attrib/gattrib.$(OBJEXT) unit_test_hog_OBJECTS = $(am_unit_test_hog_OBJECTS) unit_test_hog_DEPENDENCIES = src/libshared-glib.la \ lib/libbluetooth-internal.la $(am__DEPENDENCIES_1) am_unit_test_lib_OBJECTS = unit/test-lib.$(OBJEXT) unit_test_lib_OBJECTS = $(am_unit_test_lib_OBJECTS) unit_test_lib_DEPENDENCIES = src/libshared-glib.la \ lib/libbluetooth-internal.la $(am__DEPENDENCIES_1) am__unit_test_mesh_crypto_SOURCES_DIST = unit/test-mesh-crypto.c \ mesh/crypto.h ell/internal ell/ell.h @MESH_TRUE@am_unit_test_mesh_crypto_OBJECTS = \ @MESH_TRUE@ unit/test_mesh_crypto-test-mesh-crypto.$(OBJEXT) unit_test_mesh_crypto_OBJECTS = $(am_unit_test_mesh_crypto_OBJECTS) @MESH_TRUE@unit_test_mesh_crypto_DEPENDENCIES = $(am__DEPENDENCIES_2) am_unit_test_mgmt_OBJECTS = unit/test-mgmt.$(OBJEXT) unit_test_mgmt_OBJECTS = $(am_unit_test_mgmt_OBJECTS) unit_test_mgmt_DEPENDENCIES = src/libshared-glib.la \ $(am__DEPENDENCIES_1) am_unit_test_micp_OBJECTS = unit/test-micp.$(OBJEXT) unit_test_micp_OBJECTS = $(am_unit_test_micp_OBJECTS) unit_test_micp_DEPENDENCIES = src/libshared-glib.la \ lib/libbluetooth-internal.la $(am__DEPENDENCIES_1) am__unit_test_midi_SOURCES_DIST = unit/test-midi.c \ profiles/midi/libmidi.h profiles/midi/libmidi.c @MIDI_TRUE@am_unit_test_midi_OBJECTS = \ @MIDI_TRUE@ unit/test_midi-test-midi.$(OBJEXT) \ @MIDI_TRUE@ profiles/midi/unit_test_midi-libmidi.$(OBJEXT) unit_test_midi_OBJECTS = $(am_unit_test_midi_OBJECTS) @MIDI_TRUE@unit_test_midi_DEPENDENCIES = src/libshared-glib.la \ @MIDI_TRUE@ $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1) am_unit_test_queue_OBJECTS = unit/test-queue.$(OBJEXT) unit_test_queue_OBJECTS = $(am_unit_test_queue_OBJECTS) unit_test_queue_DEPENDENCIES = src/libshared-glib.la \ $(am__DEPENDENCIES_1) am_unit_test_ringbuf_OBJECTS = unit/test-ringbuf.$(OBJEXT) unit_test_ringbuf_OBJECTS = $(am_unit_test_ringbuf_OBJECTS) unit_test_ringbuf_DEPENDENCIES = src/libshared-glib.la \ $(am__DEPENDENCIES_1) am_unit_test_sdp_OBJECTS = unit/test-sdp.$(OBJEXT) \ src/sdpd-database.$(OBJEXT) src/log.$(OBJEXT) \ src/sdpd-service.$(OBJEXT) src/sdpd-request.$(OBJEXT) unit_test_sdp_OBJECTS = $(am_unit_test_sdp_OBJECTS) unit_test_sdp_DEPENDENCIES = lib/libbluetooth-internal.la \ src/libshared-glib.la $(am__DEPENDENCIES_1) am_unit_test_tester_OBJECTS = unit/test-tester.$(OBJEXT) unit_test_tester_OBJECTS = $(am_unit_test_tester_OBJECTS) unit_test_tester_DEPENDENCIES = src/libshared-glib.la \ lib/libbluetooth-internal.la $(am__DEPENDENCIES_1) am_unit_test_textfile_OBJECTS = unit/test-textfile.$(OBJEXT) \ src/textfile.$(OBJEXT) unit_test_textfile_OBJECTS = $(am_unit_test_textfile_OBJECTS) unit_test_textfile_DEPENDENCIES = src/libshared-glib.la \ $(am__DEPENDENCIES_1) am_unit_test_uhid_OBJECTS = unit/test-uhid.$(OBJEXT) unit_test_uhid_OBJECTS = $(am_unit_test_uhid_OBJECTS) unit_test_uhid_DEPENDENCIES = src/libshared-glib.la \ $(am__DEPENDENCIES_1) am_unit_test_uuid_OBJECTS = unit/test-uuid.$(OBJEXT) unit_test_uuid_OBJECTS = $(am_unit_test_uuid_OBJECTS) unit_test_uuid_DEPENDENCIES = src/libshared-glib.la \ lib/libbluetooth-internal.la $(am__DEPENDENCIES_1) am_unit_test_vcp_OBJECTS = unit/test-vcp.$(OBJEXT) $(am__objects_40) unit_test_vcp_OBJECTS = $(am_unit_test_vcp_OBJECTS) unit_test_vcp_DEPENDENCIES = src/libshared-glib.la \ lib/libbluetooth-internal.la $(am__DEPENDENCIES_1) SCRIPTS = $(test_SCRIPTS) AM_V_P = $(am__v_P_@AM_V@) am__v_P_ = $(am__v_P_@AM_DEFAULT_V@) am__v_P_0 = false am__v_P_1 = : AM_V_GEN = $(am__v_GEN_@AM_V@) am__v_GEN_ = $(am__v_GEN_@AM_DEFAULT_V@) am__v_GEN_0 = @echo " GEN " $@; am__v_GEN_1 = AM_V_at = $(am__v_at_@AM_V@) am__v_at_ = $(am__v_at_@AM_DEFAULT_V@) am__v_at_0 = @ am__v_at_1 = DEFAULT_INCLUDES = -I.@am__isrc@ depcomp = $(SHELL) $(top_srcdir)/depcomp am__maybe_remake_depfiles = depfiles am__depfiles_remade = android/$(DEPDIR)/a2dp-sink.Po \ android/$(DEPDIR)/a2dp.Po \ android/$(DEPDIR)/android_tester-tester-a2dp.Po \ android/$(DEPDIR)/android_tester-tester-avrcp.Po \ android/$(DEPDIR)/android_tester-tester-bluetooth.Po \ android/$(DEPDIR)/android_tester-tester-gatt.Po \ android/$(DEPDIR)/android_tester-tester-hdp.Po \ android/$(DEPDIR)/android_tester-tester-hidhost.Po \ android/$(DEPDIR)/android_tester-tester-main.Po \ android/$(DEPDIR)/android_tester-tester-map-client.Po \ android/$(DEPDIR)/android_tester-tester-pan.Po \ android/$(DEPDIR)/android_tester-tester-socket.Po \ android/$(DEPDIR)/audio_a2dp_default_la-hal-audio-aptx.Plo \ android/$(DEPDIR)/audio_a2dp_default_la-hal-audio-sbc.Plo \ android/$(DEPDIR)/audio_a2dp_default_la-hal-audio.Plo \ android/$(DEPDIR)/audio_sco_default_la-hal-sco.Plo \ android/$(DEPDIR)/avctp.Po android/$(DEPDIR)/avdtp.Po \ android/$(DEPDIR)/avdtptest-avdtp.Po \ android/$(DEPDIR)/avdtptest-avdtptest.Po \ android/$(DEPDIR)/avrcp-lib.Po android/$(DEPDIR)/avrcp.Po \ android/$(DEPDIR)/bluetooth.Po \ android/$(DEPDIR)/bluetooth_default_la-hal-a2dp-sink.Plo \ android/$(DEPDIR)/bluetooth_default_la-hal-a2dp.Plo \ android/$(DEPDIR)/bluetooth_default_la-hal-avrcp-ctrl.Plo \ android/$(DEPDIR)/bluetooth_default_la-hal-avrcp.Plo \ android/$(DEPDIR)/bluetooth_default_la-hal-bluetooth.Plo \ android/$(DEPDIR)/bluetooth_default_la-hal-gatt.Plo \ android/$(DEPDIR)/bluetooth_default_la-hal-handsfree-client.Plo \ android/$(DEPDIR)/bluetooth_default_la-hal-handsfree.Plo \ android/$(DEPDIR)/bluetooth_default_la-hal-health.Plo \ android/$(DEPDIR)/bluetooth_default_la-hal-hidhost.Plo \ android/$(DEPDIR)/bluetooth_default_la-hal-ipc.Plo \ android/$(DEPDIR)/bluetooth_default_la-hal-map-client.Plo \ android/$(DEPDIR)/bluetooth_default_la-hal-pan.Plo \ android/$(DEPDIR)/bluetooth_default_la-hal-socket.Plo \ android/$(DEPDIR)/bluetooth_default_la-hal-utils.Plo \ android/$(DEPDIR)/bluetoothd-snoop.Po \ android/$(DEPDIR)/gatt.Po \ android/$(DEPDIR)/haltest-hal-utils.Po \ android/$(DEPDIR)/handsfree-client.Po \ android/$(DEPDIR)/handsfree.Po android/$(DEPDIR)/health.Po \ android/$(DEPDIR)/hidhost.Po android/$(DEPDIR)/ipc.Po \ android/$(DEPDIR)/ipc_tester-hal-utils.Po \ android/$(DEPDIR)/ipc_tester-ipc-tester.Po \ android/$(DEPDIR)/main.Po android/$(DEPDIR)/map-client.Po \ android/$(DEPDIR)/pan.Po android/$(DEPDIR)/sco.Po \ android/$(DEPDIR)/socket.Po \ android/$(DEPDIR)/system-emulator.Po \ android/$(DEPDIR)/test-ipc.Po \ android/audio_utils/$(DEPDIR)/audio_sco_default_la-resampler.Plo \ android/client/$(DEPDIR)/haltest-haltest.Po \ android/client/$(DEPDIR)/haltest-history.Po \ android/client/$(DEPDIR)/haltest-if-audio.Po \ android/client/$(DEPDIR)/haltest-if-av-sink.Po \ android/client/$(DEPDIR)/haltest-if-av.Po \ android/client/$(DEPDIR)/haltest-if-bt.Po \ android/client/$(DEPDIR)/haltest-if-gatt.Po \ android/client/$(DEPDIR)/haltest-if-hf-client.Po \ android/client/$(DEPDIR)/haltest-if-hf.Po \ android/client/$(DEPDIR)/haltest-if-hh.Po \ android/client/$(DEPDIR)/haltest-if-hl.Po \ android/client/$(DEPDIR)/haltest-if-mce.Po \ android/client/$(DEPDIR)/haltest-if-pan.Po \ android/client/$(DEPDIR)/haltest-if-rc-ctrl.Po \ android/client/$(DEPDIR)/haltest-if-rc.Po \ android/client/$(DEPDIR)/haltest-if-sco.Po \ android/client/$(DEPDIR)/haltest-if-sock.Po \ android/client/$(DEPDIR)/haltest-pollhandler.Po \ android/client/$(DEPDIR)/haltest-tabcompletion.Po \ android/client/$(DEPDIR)/haltest-terminal.Po \ android/hardware/$(DEPDIR)/android_tester-hardware.Po \ android/hardware/$(DEPDIR)/haltest-hardware.Po \ attrib/$(DEPDIR)/att.Po attrib/$(DEPDIR)/bluetoothd-att.Po \ attrib/$(DEPDIR)/bluetoothd-gatt.Po \ attrib/$(DEPDIR)/bluetoothd-gattrib.Po \ attrib/$(DEPDIR)/gatt.Po attrib/$(DEPDIR)/gattrib.Po \ attrib/$(DEPDIR)/gatttool.Po attrib/$(DEPDIR)/interactive.Po \ attrib/$(DEPDIR)/utils.Po \ btio/$(DEPDIR)/android_avdtptest-btio.Po \ btio/$(DEPDIR)/bluetoothd-btio.Po btio/$(DEPDIR)/btio.Po \ btio/$(DEPDIR)/obexd-btio.Po client/$(DEPDIR)/admin.Po \ client/$(DEPDIR)/adv_monitor.Po \ client/$(DEPDIR)/advertising.Po client/$(DEPDIR)/agent.Po \ client/$(DEPDIR)/assistant.Po client/$(DEPDIR)/display.Po \ client/$(DEPDIR)/gatt.Po client/$(DEPDIR)/hci.Po \ client/$(DEPDIR)/main.Po client/$(DEPDIR)/mgmt.Po \ client/$(DEPDIR)/player.Po client/$(DEPDIR)/print.Po \ ell/$(DEPDIR)/base64.Plo ell/$(DEPDIR)/cert-crypto.Plo \ ell/$(DEPDIR)/cert.Plo ell/$(DEPDIR)/checksum.Plo \ ell/$(DEPDIR)/cipher.Plo ell/$(DEPDIR)/dbus-client.Plo \ ell/$(DEPDIR)/dbus-filter.Plo ell/$(DEPDIR)/dbus-message.Plo \ ell/$(DEPDIR)/dbus-name-cache.Plo \ ell/$(DEPDIR)/dbus-service.Plo ell/$(DEPDIR)/dbus-util.Plo \ ell/$(DEPDIR)/dbus.Plo ell/$(DEPDIR)/ecc-external.Plo \ ell/$(DEPDIR)/ecc.Plo ell/$(DEPDIR)/ecdh.Plo \ ell/$(DEPDIR)/gvariant-util.Plo ell/$(DEPDIR)/hashmap.Plo \ ell/$(DEPDIR)/idle.Plo ell/$(DEPDIR)/io.Plo \ ell/$(DEPDIR)/key.Plo ell/$(DEPDIR)/log.Plo \ ell/$(DEPDIR)/main.Plo ell/$(DEPDIR)/pem.Plo \ ell/$(DEPDIR)/queue.Plo ell/$(DEPDIR)/random.Plo \ ell/$(DEPDIR)/settings.Plo ell/$(DEPDIR)/signal.Plo \ ell/$(DEPDIR)/siphash.Plo ell/$(DEPDIR)/string.Plo \ ell/$(DEPDIR)/strv.Plo ell/$(DEPDIR)/tester.Plo \ ell/$(DEPDIR)/time.Plo ell/$(DEPDIR)/timeout.Plo \ ell/$(DEPDIR)/tls-extensions.Plo ell/$(DEPDIR)/tls-record.Plo \ ell/$(DEPDIR)/tls-suites.Plo ell/$(DEPDIR)/tls.Plo \ ell/$(DEPDIR)/utf8.Plo ell/$(DEPDIR)/util.Plo \ ell/$(DEPDIR)/uuid.Plo \ emulator/$(DEPDIR)/android_android_tester-btdev.Po \ emulator/$(DEPDIR)/android_android_tester-bthost.Po \ emulator/$(DEPDIR)/android_android_tester-hciemu.Po \ emulator/$(DEPDIR)/android_android_tester-smp.Po \ emulator/$(DEPDIR)/android_android_tester-vhci.Po \ emulator/$(DEPDIR)/android_ipc_tester-btdev.Po \ emulator/$(DEPDIR)/android_ipc_tester-bthost.Po \ emulator/$(DEPDIR)/android_ipc_tester-hciemu.Po \ emulator/$(DEPDIR)/android_ipc_tester-smp.Po \ emulator/$(DEPDIR)/android_ipc_tester-vhci.Po \ emulator/$(DEPDIR)/b1ee.Po emulator/$(DEPDIR)/btdev.Po \ emulator/$(DEPDIR)/bthost.Po emulator/$(DEPDIR)/hciemu.Po \ emulator/$(DEPDIR)/hfp.Po emulator/$(DEPDIR)/le.Po \ emulator/$(DEPDIR)/main.Po emulator/$(DEPDIR)/phy.Po \ emulator/$(DEPDIR)/serial.Po emulator/$(DEPDIR)/server.Po \ emulator/$(DEPDIR)/smp.Po emulator/$(DEPDIR)/vhci.Po \ gdbus/$(DEPDIR)/client.Plo gdbus/$(DEPDIR)/mainloop.Plo \ gdbus/$(DEPDIR)/object.Plo gdbus/$(DEPDIR)/polkit.Plo \ gdbus/$(DEPDIR)/watch.Plo gobex/$(DEPDIR)/gobex-apparam.Po \ gobex/$(DEPDIR)/gobex-defs.Po gobex/$(DEPDIR)/gobex-header.Po \ gobex/$(DEPDIR)/gobex-packet.Po \ gobex/$(DEPDIR)/gobex-transfer.Po gobex/$(DEPDIR)/gobex.Po \ gobex/$(DEPDIR)/obexd-gobex-apparam.Po \ gobex/$(DEPDIR)/obexd-gobex-defs.Po \ gobex/$(DEPDIR)/obexd-gobex-header.Po \ gobex/$(DEPDIR)/obexd-gobex-packet.Po \ gobex/$(DEPDIR)/obexd-gobex-transfer.Po \ gobex/$(DEPDIR)/obexd-gobex.Po lib/$(DEPDIR)/bluetooth.Plo \ lib/$(DEPDIR)/hci.Plo lib/$(DEPDIR)/sdp.Plo \ lib/$(DEPDIR)/uuid.Plo mesh/$(DEPDIR)/agent.Po \ mesh/$(DEPDIR)/appkey.Po mesh/$(DEPDIR)/cfgmod-server.Po \ mesh/$(DEPDIR)/crypto.Po mesh/$(DEPDIR)/dbus.Po \ mesh/$(DEPDIR)/friend.Po mesh/$(DEPDIR)/keyring.Po \ mesh/$(DEPDIR)/main.Po mesh/$(DEPDIR)/manager.Po \ mesh/$(DEPDIR)/mesh-config-json.Po \ mesh/$(DEPDIR)/mesh-io-generic.Po \ mesh/$(DEPDIR)/mesh-io-mgmt.Po mesh/$(DEPDIR)/mesh-io-unit.Po \ mesh/$(DEPDIR)/mesh-io.Po mesh/$(DEPDIR)/mesh-mgmt.Po \ mesh/$(DEPDIR)/mesh.Po mesh/$(DEPDIR)/model.Po \ mesh/$(DEPDIR)/net-keys.Po mesh/$(DEPDIR)/net.Po \ mesh/$(DEPDIR)/node.Po mesh/$(DEPDIR)/pb-adv.Po \ mesh/$(DEPDIR)/prov-acceptor.Po \ mesh/$(DEPDIR)/prov-initiator.Po \ mesh/$(DEPDIR)/prvbeac-server.Po \ mesh/$(DEPDIR)/remprv-server.Po mesh/$(DEPDIR)/rpl.Po \ mesh/$(DEPDIR)/util.Po monitor/$(DEPDIR)/a2dp.Po \ monitor/$(DEPDIR)/analyze.Po monitor/$(DEPDIR)/att.Po \ monitor/$(DEPDIR)/avctp.Po monitor/$(DEPDIR)/avdtp.Po \ monitor/$(DEPDIR)/bnep.Po monitor/$(DEPDIR)/broadcom.Po \ monitor/$(DEPDIR)/control.Po monitor/$(DEPDIR)/crc.Po \ monitor/$(DEPDIR)/display.Po monitor/$(DEPDIR)/ellisys.Po \ monitor/$(DEPDIR)/hcidump.Po monitor/$(DEPDIR)/hwdb.Po \ monitor/$(DEPDIR)/intel.Po monitor/$(DEPDIR)/jlink.Po \ monitor/$(DEPDIR)/keys.Po monitor/$(DEPDIR)/l2cap.Po \ monitor/$(DEPDIR)/ll.Po monitor/$(DEPDIR)/lmp.Po \ monitor/$(DEPDIR)/main.Po monitor/$(DEPDIR)/msft.Po \ monitor/$(DEPDIR)/packet.Po monitor/$(DEPDIR)/rfcomm.Po \ monitor/$(DEPDIR)/sdp.Po monitor/$(DEPDIR)/vendor.Po \ obexd/client/$(DEPDIR)/obexd-bip-common.Po \ obexd/client/$(DEPDIR)/obexd-bip.Po \ obexd/client/$(DEPDIR)/obexd-bluetooth.Po \ obexd/client/$(DEPDIR)/obexd-driver.Po \ obexd/client/$(DEPDIR)/obexd-ftp.Po \ obexd/client/$(DEPDIR)/obexd-manager.Po \ obexd/client/$(DEPDIR)/obexd-map-event.Po \ obexd/client/$(DEPDIR)/obexd-map.Po \ obexd/client/$(DEPDIR)/obexd-mns.Po \ obexd/client/$(DEPDIR)/obexd-opp.Po \ obexd/client/$(DEPDIR)/obexd-pbap.Po \ obexd/client/$(DEPDIR)/obexd-session.Po \ obexd/client/$(DEPDIR)/obexd-sync.Po \ obexd/client/$(DEPDIR)/obexd-transfer.Po \ obexd/client/$(DEPDIR)/obexd-transport.Po \ obexd/plugins/$(DEPDIR)/obexd-bluetooth.Po \ obexd/plugins/$(DEPDIR)/obexd-filesystem.Po \ obexd/plugins/$(DEPDIR)/obexd-ftp.Po \ obexd/plugins/$(DEPDIR)/obexd-irmc.Po \ obexd/plugins/$(DEPDIR)/obexd-mas.Po \ obexd/plugins/$(DEPDIR)/obexd-messages-dummy.Po \ obexd/plugins/$(DEPDIR)/obexd-opp.Po \ obexd/plugins/$(DEPDIR)/obexd-pbap.Po \ obexd/plugins/$(DEPDIR)/obexd-pcsuite.Po \ obexd/plugins/$(DEPDIR)/obexd-phonebook-@PLUGIN_PHONEBOOK@.Po \ obexd/plugins/$(DEPDIR)/obexd-vcard.Po \ obexd/src/$(DEPDIR)/obexd-log.Po \ obexd/src/$(DEPDIR)/obexd-main.Po \ obexd/src/$(DEPDIR)/obexd-manager.Po \ obexd/src/$(DEPDIR)/obexd-mimetype.Po \ obexd/src/$(DEPDIR)/obexd-obex.Po \ obexd/src/$(DEPDIR)/obexd-plugin.Po \ obexd/src/$(DEPDIR)/obexd-server.Po \ obexd/src/$(DEPDIR)/obexd-service.Po \ obexd/src/$(DEPDIR)/obexd-transport.Po \ peripheral/$(DEPDIR)/attach.Po peripheral/$(DEPDIR)/efivars.Po \ peripheral/$(DEPDIR)/gap.Po peripheral/$(DEPDIR)/gatt.Po \ peripheral/$(DEPDIR)/log.Po peripheral/$(DEPDIR)/main.Po \ plugins/$(DEPDIR)/bluetoothd-admin.Po \ plugins/$(DEPDIR)/bluetoothd-autopair.Po \ plugins/$(DEPDIR)/bluetoothd-hostname.Po \ plugins/$(DEPDIR)/bluetoothd-neard.Po \ plugins/$(DEPDIR)/bluetoothd-policy.Po \ plugins/$(DEPDIR)/bluetoothd-sixaxis.Po \ profiles/audio/$(DEPDIR)/bluetoothd-a2dp.Po \ profiles/audio/$(DEPDIR)/bluetoothd-asha.Po \ profiles/audio/$(DEPDIR)/bluetoothd-avctp.Po \ profiles/audio/$(DEPDIR)/bluetoothd-avdtp.Po \ profiles/audio/$(DEPDIR)/bluetoothd-avrcp.Po \ profiles/audio/$(DEPDIR)/bluetoothd-bap.Po \ profiles/audio/$(DEPDIR)/bluetoothd-bass.Po \ profiles/audio/$(DEPDIR)/bluetoothd-ccp.Po \ profiles/audio/$(DEPDIR)/bluetoothd-control.Po \ profiles/audio/$(DEPDIR)/bluetoothd-csip.Po \ profiles/audio/$(DEPDIR)/bluetoothd-mcp.Po \ profiles/audio/$(DEPDIR)/bluetoothd-media.Po \ profiles/audio/$(DEPDIR)/bluetoothd-micp.Po \ profiles/audio/$(DEPDIR)/bluetoothd-player.Po \ profiles/audio/$(DEPDIR)/bluetoothd-sink.Po \ profiles/audio/$(DEPDIR)/bluetoothd-source.Po \ profiles/audio/$(DEPDIR)/bluetoothd-transport.Po \ profiles/audio/$(DEPDIR)/bluetoothd-vcp.Po \ profiles/battery/$(DEPDIR)/bas.Po \ profiles/battery/$(DEPDIR)/bluetoothd-bas.Po \ profiles/battery/$(DEPDIR)/bluetoothd-battery.Po \ profiles/cups/$(DEPDIR)/hcrp.Po \ profiles/cups/$(DEPDIR)/main.Po profiles/cups/$(DEPDIR)/sdp.Po \ profiles/cups/$(DEPDIR)/spp.Po \ profiles/deviceinfo/$(DEPDIR)/bluetoothd-deviceinfo.Po \ profiles/deviceinfo/$(DEPDIR)/bluetoothd-dis.Po \ profiles/deviceinfo/$(DEPDIR)/dis.Po \ profiles/gap/$(DEPDIR)/bluetoothd-gas.Po \ profiles/health/$(DEPDIR)/bluetoothd-hdp.Po \ profiles/health/$(DEPDIR)/bluetoothd-hdp_main.Po \ profiles/health/$(DEPDIR)/bluetoothd-hdp_manager.Po \ profiles/health/$(DEPDIR)/bluetoothd-hdp_util.Po \ profiles/health/$(DEPDIR)/bluetoothd-mcap.Po \ profiles/health/$(DEPDIR)/mcap.Po \ profiles/iap/$(DEPDIR)/main.Po \ profiles/input/$(DEPDIR)/bluetoothd-device.Po \ profiles/input/$(DEPDIR)/bluetoothd-hog-lib.Po \ profiles/input/$(DEPDIR)/bluetoothd-hog.Po \ profiles/input/$(DEPDIR)/bluetoothd-manager.Po \ profiles/input/$(DEPDIR)/bluetoothd-server.Po \ profiles/input/$(DEPDIR)/bluetoothd-suspend-none.Po \ profiles/input/$(DEPDIR)/hog-lib.Po \ profiles/midi/$(DEPDIR)/bluetoothd-libmidi.Po \ profiles/midi/$(DEPDIR)/bluetoothd-midi.Po \ profiles/midi/$(DEPDIR)/unit_test_midi-libmidi.Po \ profiles/network/$(DEPDIR)/bluetoothd-bnep.Po \ profiles/network/$(DEPDIR)/bluetoothd-connection.Po \ profiles/network/$(DEPDIR)/bluetoothd-manager.Po \ profiles/network/$(DEPDIR)/bluetoothd-server.Po \ profiles/network/$(DEPDIR)/bnep.Po \ profiles/sap/$(DEPDIR)/bluetoothd-main.Po \ profiles/sap/$(DEPDIR)/bluetoothd-manager.Po \ profiles/sap/$(DEPDIR)/bluetoothd-sap-dummy.Po \ profiles/sap/$(DEPDIR)/bluetoothd-server.Po \ profiles/scanparam/$(DEPDIR)/bluetoothd-scan.Po \ profiles/scanparam/$(DEPDIR)/bluetoothd-scpp.Po \ profiles/scanparam/$(DEPDIR)/scpp.Po \ src/$(DEPDIR)/android_avdtptest-log.Po \ src/$(DEPDIR)/bluetoothd-adapter.Po \ src/$(DEPDIR)/bluetoothd-adv_monitor.Po \ src/$(DEPDIR)/bluetoothd-advertising.Po \ src/$(DEPDIR)/bluetoothd-agent.Po \ src/$(DEPDIR)/bluetoothd-backtrace.Po \ src/$(DEPDIR)/bluetoothd-battery.Po \ src/$(DEPDIR)/bluetoothd-dbus-common.Po \ src/$(DEPDIR)/bluetoothd-device.Po \ src/$(DEPDIR)/bluetoothd-eir.Po \ src/$(DEPDIR)/bluetoothd-error.Po \ src/$(DEPDIR)/bluetoothd-gatt-client.Po \ src/$(DEPDIR)/bluetoothd-gatt-database.Po \ src/$(DEPDIR)/bluetoothd-log.Po \ src/$(DEPDIR)/bluetoothd-main.Po \ src/$(DEPDIR)/bluetoothd-plugin.Po \ src/$(DEPDIR)/bluetoothd-profile.Po \ src/$(DEPDIR)/bluetoothd-rfkill.Po \ src/$(DEPDIR)/bluetoothd-sdp-client.Po \ src/$(DEPDIR)/bluetoothd-sdp-xml.Po \ src/$(DEPDIR)/bluetoothd-sdpd-database.Po \ src/$(DEPDIR)/bluetoothd-sdpd-request.Po \ src/$(DEPDIR)/bluetoothd-sdpd-server.Po \ src/$(DEPDIR)/bluetoothd-sdpd-service.Po \ src/$(DEPDIR)/bluetoothd-service.Po \ src/$(DEPDIR)/bluetoothd-set.Po \ src/$(DEPDIR)/bluetoothd-settings.Po \ src/$(DEPDIR)/bluetoothd-storage.Po \ src/$(DEPDIR)/bluetoothd-textfile.Po \ src/$(DEPDIR)/bluetoothd-uuid-helper.Po src/$(DEPDIR)/eir.Po \ src/$(DEPDIR)/log.Po src/$(DEPDIR)/oui.Po \ src/$(DEPDIR)/sdp-client.Po src/$(DEPDIR)/sdp-xml.Po \ src/$(DEPDIR)/sdpd-database.Po src/$(DEPDIR)/sdpd-request.Po \ src/$(DEPDIR)/sdpd-server.Po src/$(DEPDIR)/sdpd-service.Po \ src/$(DEPDIR)/settings.Po src/$(DEPDIR)/textfile.Po \ src/$(DEPDIR)/uuid-helper.Po \ src/shared/$(DEPDIR)/android_avdtptest-log.Po \ src/shared/$(DEPDIR)/android_avdtptest-queue.Po \ src/shared/$(DEPDIR)/android_avdtptest-util.Po \ src/shared/$(DEPDIR)/btp.Po \ src/shared/$(DEPDIR)/libshared_ell_la-ad.Plo \ src/shared/$(DEPDIR)/libshared_ell_la-asha.Plo \ src/shared/$(DEPDIR)/libshared_ell_la-att.Plo \ src/shared/$(DEPDIR)/libshared_ell_la-bap-debug.Plo \ src/shared/$(DEPDIR)/libshared_ell_la-bap.Plo \ src/shared/$(DEPDIR)/libshared_ell_la-bass.Plo \ src/shared/$(DEPDIR)/libshared_ell_la-btsnoop.Plo \ src/shared/$(DEPDIR)/libshared_ell_la-ccp.Plo \ src/shared/$(DEPDIR)/libshared_ell_la-crypto.Plo \ src/shared/$(DEPDIR)/libshared_ell_la-csip.Plo \ src/shared/$(DEPDIR)/libshared_ell_la-ecc.Plo \ src/shared/$(DEPDIR)/libshared_ell_la-gap.Plo \ src/shared/$(DEPDIR)/libshared_ell_la-gatt-client.Plo \ src/shared/$(DEPDIR)/libshared_ell_la-gatt-db.Plo \ src/shared/$(DEPDIR)/libshared_ell_la-gatt-helpers.Plo \ src/shared/$(DEPDIR)/libshared_ell_la-gatt-server.Plo \ src/shared/$(DEPDIR)/libshared_ell_la-hci-crypto.Plo \ src/shared/$(DEPDIR)/libshared_ell_la-hci.Plo \ src/shared/$(DEPDIR)/libshared_ell_la-hfp.Plo \ src/shared/$(DEPDIR)/libshared_ell_la-io-ell.Plo \ src/shared/$(DEPDIR)/libshared_ell_la-log.Plo \ src/shared/$(DEPDIR)/libshared_ell_la-mainloop-ell.Plo \ src/shared/$(DEPDIR)/libshared_ell_la-mcp.Plo \ src/shared/$(DEPDIR)/libshared_ell_la-mgmt.Plo \ src/shared/$(DEPDIR)/libshared_ell_la-micp.Plo \ src/shared/$(DEPDIR)/libshared_ell_la-pcap.Plo \ src/shared/$(DEPDIR)/libshared_ell_la-queue.Plo \ src/shared/$(DEPDIR)/libshared_ell_la-ringbuf.Plo \ src/shared/$(DEPDIR)/libshared_ell_la-shell.Plo \ src/shared/$(DEPDIR)/libshared_ell_la-timeout-ell.Plo \ src/shared/$(DEPDIR)/libshared_ell_la-uhid.Plo \ src/shared/$(DEPDIR)/libshared_ell_la-util.Plo \ src/shared/$(DEPDIR)/libshared_ell_la-vcp.Plo \ src/shared/$(DEPDIR)/libshared_glib_la-ad.Plo \ src/shared/$(DEPDIR)/libshared_glib_la-asha.Plo \ src/shared/$(DEPDIR)/libshared_glib_la-att.Plo \ src/shared/$(DEPDIR)/libshared_glib_la-bap-debug.Plo \ src/shared/$(DEPDIR)/libshared_glib_la-bap.Plo \ src/shared/$(DEPDIR)/libshared_glib_la-bass.Plo \ src/shared/$(DEPDIR)/libshared_glib_la-btsnoop.Plo \ src/shared/$(DEPDIR)/libshared_glib_la-ccp.Plo \ src/shared/$(DEPDIR)/libshared_glib_la-crypto.Plo \ src/shared/$(DEPDIR)/libshared_glib_la-csip.Plo \ src/shared/$(DEPDIR)/libshared_glib_la-ecc.Plo \ src/shared/$(DEPDIR)/libshared_glib_la-gap.Plo \ src/shared/$(DEPDIR)/libshared_glib_la-gatt-client.Plo \ src/shared/$(DEPDIR)/libshared_glib_la-gatt-db.Plo \ src/shared/$(DEPDIR)/libshared_glib_la-gatt-helpers.Plo \ src/shared/$(DEPDIR)/libshared_glib_la-gatt-server.Plo \ src/shared/$(DEPDIR)/libshared_glib_la-hci-crypto.Plo \ src/shared/$(DEPDIR)/libshared_glib_la-hci.Plo \ src/shared/$(DEPDIR)/libshared_glib_la-hfp.Plo \ src/shared/$(DEPDIR)/libshared_glib_la-io-glib.Plo \ src/shared/$(DEPDIR)/libshared_glib_la-log.Plo \ src/shared/$(DEPDIR)/libshared_glib_la-mainloop-glib.Plo \ src/shared/$(DEPDIR)/libshared_glib_la-mainloop-notify.Plo \ src/shared/$(DEPDIR)/libshared_glib_la-mcp.Plo \ src/shared/$(DEPDIR)/libshared_glib_la-mgmt.Plo \ src/shared/$(DEPDIR)/libshared_glib_la-micp.Plo \ src/shared/$(DEPDIR)/libshared_glib_la-pcap.Plo \ src/shared/$(DEPDIR)/libshared_glib_la-queue.Plo \ src/shared/$(DEPDIR)/libshared_glib_la-ringbuf.Plo \ src/shared/$(DEPDIR)/libshared_glib_la-shell.Plo \ src/shared/$(DEPDIR)/libshared_glib_la-tester.Plo \ src/shared/$(DEPDIR)/libshared_glib_la-timeout-glib.Plo \ src/shared/$(DEPDIR)/libshared_glib_la-uhid.Plo \ src/shared/$(DEPDIR)/libshared_glib_la-util.Plo \ src/shared/$(DEPDIR)/libshared_glib_la-vcp.Plo \ src/shared/$(DEPDIR)/libshared_mainloop_la-ad.Plo \ src/shared/$(DEPDIR)/libshared_mainloop_la-asha.Plo \ src/shared/$(DEPDIR)/libshared_mainloop_la-att.Plo \ src/shared/$(DEPDIR)/libshared_mainloop_la-bap-debug.Plo \ src/shared/$(DEPDIR)/libshared_mainloop_la-bap.Plo \ src/shared/$(DEPDIR)/libshared_mainloop_la-bass.Plo \ src/shared/$(DEPDIR)/libshared_mainloop_la-btsnoop.Plo \ src/shared/$(DEPDIR)/libshared_mainloop_la-ccp.Plo \ src/shared/$(DEPDIR)/libshared_mainloop_la-crypto.Plo \ src/shared/$(DEPDIR)/libshared_mainloop_la-csip.Plo \ src/shared/$(DEPDIR)/libshared_mainloop_la-ecc.Plo \ src/shared/$(DEPDIR)/libshared_mainloop_la-gap.Plo \ src/shared/$(DEPDIR)/libshared_mainloop_la-gatt-client.Plo \ src/shared/$(DEPDIR)/libshared_mainloop_la-gatt-db.Plo \ src/shared/$(DEPDIR)/libshared_mainloop_la-gatt-helpers.Plo \ src/shared/$(DEPDIR)/libshared_mainloop_la-gatt-server.Plo \ src/shared/$(DEPDIR)/libshared_mainloop_la-hci-crypto.Plo \ src/shared/$(DEPDIR)/libshared_mainloop_la-hci.Plo \ src/shared/$(DEPDIR)/libshared_mainloop_la-hfp.Plo \ src/shared/$(DEPDIR)/libshared_mainloop_la-io-mainloop.Plo \ src/shared/$(DEPDIR)/libshared_mainloop_la-log.Plo \ src/shared/$(DEPDIR)/libshared_mainloop_la-mainloop-notify.Plo \ src/shared/$(DEPDIR)/libshared_mainloop_la-mainloop.Plo \ src/shared/$(DEPDIR)/libshared_mainloop_la-mcp.Plo \ src/shared/$(DEPDIR)/libshared_mainloop_la-mgmt.Plo \ src/shared/$(DEPDIR)/libshared_mainloop_la-micp.Plo \ src/shared/$(DEPDIR)/libshared_mainloop_la-pcap.Plo \ src/shared/$(DEPDIR)/libshared_mainloop_la-queue.Plo \ src/shared/$(DEPDIR)/libshared_mainloop_la-ringbuf.Plo \ src/shared/$(DEPDIR)/libshared_mainloop_la-shell.Plo \ src/shared/$(DEPDIR)/libshared_mainloop_la-timeout-mainloop.Plo \ src/shared/$(DEPDIR)/libshared_mainloop_la-uhid.Plo \ src/shared/$(DEPDIR)/libshared_mainloop_la-util.Plo \ src/shared/$(DEPDIR)/libshared_mainloop_la-vcp.Plo \ tools/$(DEPDIR)/3dsp.Po tools/$(DEPDIR)/advtest.Po \ tools/$(DEPDIR)/avinfo.Po tools/$(DEPDIR)/avtest.Po \ tools/$(DEPDIR)/bcmfw.Po tools/$(DEPDIR)/bdaddr.Po \ tools/$(DEPDIR)/bluemoon.Po \ tools/$(DEPDIR)/bluetooth-player.Po \ tools/$(DEPDIR)/bnep-tester.Po tools/$(DEPDIR)/bneptest.Po \ tools/$(DEPDIR)/btattach.Po tools/$(DEPDIR)/btconfig.Po \ tools/$(DEPDIR)/btgatt-client.Po \ tools/$(DEPDIR)/btgatt-server.Po tools/$(DEPDIR)/btinfo.Po \ tools/$(DEPDIR)/btiotest.Po tools/$(DEPDIR)/btmgmt.Po \ tools/$(DEPDIR)/btmon-logger.Po tools/$(DEPDIR)/btpclient.Po \ tools/$(DEPDIR)/btpclientctl.Po tools/$(DEPDIR)/btproxy.Po \ tools/$(DEPDIR)/btsnoop.Po tools/$(DEPDIR)/check-selftest.Po \ tools/$(DEPDIR)/ciptool.Po tools/$(DEPDIR)/cltest.Po \ tools/$(DEPDIR)/create-image.Po tools/$(DEPDIR)/eddystone.Po \ tools/$(DEPDIR)/gap-tester.Po tools/$(DEPDIR)/gatt-service.Po \ tools/$(DEPDIR)/hci-tester.Po tools/$(DEPDIR)/hciattach.Po \ tools/$(DEPDIR)/hciattach_ath3k.Po \ tools/$(DEPDIR)/hciattach_bcm43xx.Po \ tools/$(DEPDIR)/hciattach_intel.Po \ tools/$(DEPDIR)/hciattach_qualcomm.Po \ tools/$(DEPDIR)/hciattach_st.Po \ tools/$(DEPDIR)/hciattach_ti.Po \ tools/$(DEPDIR)/hciattach_tialt.Po \ tools/$(DEPDIR)/hciconfig.Po tools/$(DEPDIR)/hcidump.Po \ tools/$(DEPDIR)/hcieventmask.Po \ tools/$(DEPDIR)/hcisecfilter.Po tools/$(DEPDIR)/hcitool.Po \ tools/$(DEPDIR)/hex2hcd.Po tools/$(DEPDIR)/hid2hci.Po \ tools/$(DEPDIR)/hwdb.Po tools/$(DEPDIR)/ibeacon.Po \ tools/$(DEPDIR)/ioctl-tester.Po tools/$(DEPDIR)/iso-tester.Po \ tools/$(DEPDIR)/isotest.Po tools/$(DEPDIR)/l2cap-tester.Po \ tools/$(DEPDIR)/l2ping.Po tools/$(DEPDIR)/l2test.Po \ tools/$(DEPDIR)/mcaptest.Po tools/$(DEPDIR)/mesh-cfgclient.Po \ tools/$(DEPDIR)/mesh-cfgtest.Po tools/$(DEPDIR)/mesh-tester.Po \ tools/$(DEPDIR)/meshctl.Po tools/$(DEPDIR)/mgmt-tester.Po \ tools/$(DEPDIR)/mpris-proxy.Po tools/$(DEPDIR)/nokfw.Po \ tools/$(DEPDIR)/obex-client-tool.Po \ tools/$(DEPDIR)/obex-server-tool.Po tools/$(DEPDIR)/obexctl.Po \ tools/$(DEPDIR)/oobtest.Po tools/$(DEPDIR)/rctest.Po \ tools/$(DEPDIR)/rfcomm-tester.Po tools/$(DEPDIR)/rfcomm.Po \ tools/$(DEPDIR)/rtlfw.Po tools/$(DEPDIR)/sco-tester.Po \ tools/$(DEPDIR)/scotest.Po tools/$(DEPDIR)/sdptool.Po \ tools/$(DEPDIR)/seq2bseq.Po tools/$(DEPDIR)/smp-tester.Po \ tools/$(DEPDIR)/test-runner.Po \ tools/$(DEPDIR)/userchan-tester.Po \ tools/mesh-gatt/$(DEPDIR)/config-client.Po \ tools/mesh-gatt/$(DEPDIR)/config-server.Po \ tools/mesh-gatt/$(DEPDIR)/crypto.Po \ tools/mesh-gatt/$(DEPDIR)/gatt.Po \ tools/mesh-gatt/$(DEPDIR)/net.Po \ tools/mesh-gatt/$(DEPDIR)/node.Po \ tools/mesh-gatt/$(DEPDIR)/onoff-model.Po \ tools/mesh-gatt/$(DEPDIR)/prov-db.Po \ tools/mesh-gatt/$(DEPDIR)/prov.Po \ tools/mesh-gatt/$(DEPDIR)/util.Po \ tools/mesh/$(DEPDIR)/agent.Po tools/mesh/$(DEPDIR)/cfgcli.Po \ tools/mesh/$(DEPDIR)/keys.Po tools/mesh/$(DEPDIR)/mesh-db.Po \ tools/mesh/$(DEPDIR)/remote.Po tools/mesh/$(DEPDIR)/util.Po \ tools/parser/$(DEPDIR)/att.Po tools/parser/$(DEPDIR)/avctp.Po \ tools/parser/$(DEPDIR)/avdtp.Po \ tools/parser/$(DEPDIR)/avrcp.Po tools/parser/$(DEPDIR)/bnep.Po \ tools/parser/$(DEPDIR)/bpa.Po tools/parser/$(DEPDIR)/capi.Po \ tools/parser/$(DEPDIR)/cmtp.Po tools/parser/$(DEPDIR)/csr.Po \ tools/parser/$(DEPDIR)/ericsson.Po \ tools/parser/$(DEPDIR)/hci.Po tools/parser/$(DEPDIR)/hcrp.Po \ tools/parser/$(DEPDIR)/hidp.Po tools/parser/$(DEPDIR)/l2cap.Po \ tools/parser/$(DEPDIR)/lmp.Po tools/parser/$(DEPDIR)/obex.Po \ tools/parser/$(DEPDIR)/parser.Po tools/parser/$(DEPDIR)/ppp.Po \ tools/parser/$(DEPDIR)/rfcomm.Po tools/parser/$(DEPDIR)/sap.Po \ tools/parser/$(DEPDIR)/sdp.Po tools/parser/$(DEPDIR)/smp.Po \ tools/parser/$(DEPDIR)/tcpip.Po unit/$(DEPDIR)/test-avctp.Po \ unit/$(DEPDIR)/test-avdtp.Po unit/$(DEPDIR)/test-avrcp.Po \ unit/$(DEPDIR)/test-bap.Po unit/$(DEPDIR)/test-bass.Po \ unit/$(DEPDIR)/test-crc.Po unit/$(DEPDIR)/test-crypto.Po \ unit/$(DEPDIR)/test-ecc.Po unit/$(DEPDIR)/test-eir.Po \ unit/$(DEPDIR)/test-gatt.Po unit/$(DEPDIR)/test-gattrib.Po \ unit/$(DEPDIR)/test-gdbus-client.Po \ unit/$(DEPDIR)/test-gobex-apparam.Po \ unit/$(DEPDIR)/test-gobex-header.Po \ unit/$(DEPDIR)/test-gobex-packet.Po \ unit/$(DEPDIR)/test-gobex-transfer.Po \ unit/$(DEPDIR)/test-gobex.Po unit/$(DEPDIR)/test-hfp.Po \ unit/$(DEPDIR)/test-hog.Po unit/$(DEPDIR)/test-lib.Po \ unit/$(DEPDIR)/test-mgmt.Po unit/$(DEPDIR)/test-micp.Po \ unit/$(DEPDIR)/test-queue.Po unit/$(DEPDIR)/test-ringbuf.Po \ unit/$(DEPDIR)/test-sdp.Po unit/$(DEPDIR)/test-tester.Po \ unit/$(DEPDIR)/test-textfile.Po unit/$(DEPDIR)/test-uhid.Po \ unit/$(DEPDIR)/test-uuid.Po unit/$(DEPDIR)/test-vcp.Po \ unit/$(DEPDIR)/test_mesh_crypto-test-mesh-crypto.Po \ unit/$(DEPDIR)/test_midi-test-midi.Po unit/$(DEPDIR)/util.Po am__mv = mv -f COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \ $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) LTCOMPILE = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) \ $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) \ $(AM_CFLAGS) $(CFLAGS) AM_V_CC = $(am__v_CC_@AM_V@) am__v_CC_ = $(am__v_CC_@AM_DEFAULT_V@) am__v_CC_0 = @echo " CC " $@; am__v_CC_1 = CCLD = $(CC) LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ $(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \ $(AM_LDFLAGS) $(LDFLAGS) -o $@ AM_V_CCLD = $(am__v_CCLD_@AM_V@) am__v_CCLD_ = $(am__v_CCLD_@AM_DEFAULT_V@) am__v_CCLD_0 = @echo " CCLD " $@; am__v_CCLD_1 = SOURCES = $(android_audio_a2dp_default_la_SOURCES) \ $(android_audio_sco_default_la_SOURCES) \ $(android_bluetooth_default_la_SOURCES) \ $(ell_libell_internal_la_SOURCES) \ $(gdbus_libgdbus_internal_la_SOURCES) \ $(lib_libbluetooth_internal_la_SOURCES) \ $(lib_libbluetooth_la_SOURCES) $(src_libshared_ell_la_SOURCES) \ $(src_libshared_glib_la_SOURCES) \ $(src_libshared_mainloop_la_SOURCES) \ $(android_android_tester_SOURCES) $(android_avdtptest_SOURCES) \ $(android_bluetoothd_SOURCES) \ $(android_bluetoothd_snoop_SOURCES) $(android_haltest_SOURCES) \ $(android_ipc_tester_SOURCES) \ $(android_system_emulator_SOURCES) $(android_test_ipc_SOURCES) \ $(attrib_gatttool_SOURCES) $(client_bluetoothctl_SOURCES) \ $(emulator_b1ee_SOURCES) $(emulator_btvirt_SOURCES) \ $(emulator_hfp_SOURCES) $(mesh_bluetooth_meshd_SOURCES) \ $(monitor_btmon_SOURCES) $(obexd_src_obexd_SOURCES) \ $(nodist_obexd_src_obexd_SOURCES) \ $(peripheral_btsensor_SOURCES) \ $(profiles_cups_bluetooth_SOURCES) \ $(profiles_iap_iapd_SOURCES) $(src_bluetoothd_SOURCES) \ $(nodist_src_bluetoothd_SOURCES) $(tools_3dsp_SOURCES) \ $(tools_advtest_SOURCES) tools/avinfo.c tools/avtest.c \ tools/bcmfw.c $(tools_bdaddr_SOURCES) \ $(tools_bluemoon_SOURCES) $(tools_bluetooth_player_SOURCES) \ $(tools_bnep_tester_SOURCES) $(tools_bneptest_SOURCES) \ $(tools_btattach_SOURCES) $(tools_btconfig_SOURCES) \ $(tools_btgatt_client_SOURCES) $(tools_btgatt_server_SOURCES) \ $(tools_btinfo_SOURCES) $(tools_btiotest_SOURCES) \ $(tools_btmgmt_SOURCES) $(tools_btmon_logger_SOURCES) \ $(tools_btpclient_SOURCES) $(tools_btpclientctl_SOURCES) \ $(tools_btproxy_SOURCES) $(tools_btsnoop_SOURCES) \ tools/check-selftest.c tools/ciptool.c $(tools_cltest_SOURCES) \ $(tools_create_image_SOURCES) $(tools_eddystone_SOURCES) \ $(tools_gap_tester_SOURCES) $(tools_gatt_service_SOURCES) \ $(tools_hci_tester_SOURCES) $(tools_hciattach_SOURCES) \ $(tools_hciconfig_SOURCES) $(tools_hcidump_SOURCES) \ tools/hcieventmask.c tools/hcisecfilter.c \ $(tools_hcitool_SOURCES) $(tools_hex2hcd_SOURCES) \ tools/hid2hci.c tools/hwdb.c $(tools_ibeacon_SOURCES) \ $(tools_ioctl_tester_SOURCES) $(tools_iso_tester_SOURCES) \ tools/isotest.c $(tools_l2cap_tester_SOURCES) tools/l2ping.c \ tools/l2test.c $(tools_mcaptest_SOURCES) \ $(tools_mesh_cfgclient_SOURCES) $(tools_mesh_cfgtest_SOURCES) \ $(tools_mesh_tester_SOURCES) $(tools_meshctl_SOURCES) \ $(tools_mgmt_tester_SOURCES) $(tools_mpris_proxy_SOURCES) \ $(tools_nokfw_SOURCES) $(tools_obex_client_tool_SOURCES) \ $(tools_obex_server_tool_SOURCES) $(tools_obexctl_SOURCES) \ $(tools_oobtest_SOURCES) tools/rctest.c tools/rfcomm.c \ $(tools_rfcomm_tester_SOURCES) $(tools_rtlfw_SOURCES) \ $(tools_sco_tester_SOURCES) tools/scotest.c \ $(tools_sdptool_SOURCES) $(tools_seq2bseq_SOURCES) \ $(tools_smp_tester_SOURCES) tools/test-runner.c \ $(tools_userchan_tester_SOURCES) $(unit_test_avctp_SOURCES) \ $(unit_test_avdtp_SOURCES) $(unit_test_avrcp_SOURCES) \ $(unit_test_bap_SOURCES) $(unit_test_bass_SOURCES) \ $(unit_test_crc_SOURCES) $(unit_test_crypto_SOURCES) \ $(unit_test_ecc_SOURCES) $(unit_test_eir_SOURCES) \ $(unit_test_gatt_SOURCES) $(unit_test_gattrib_SOURCES) \ $(unit_test_gdbus_client_SOURCES) $(unit_test_gobex_SOURCES) \ $(unit_test_gobex_apparam_SOURCES) \ $(unit_test_gobex_header_SOURCES) \ $(unit_test_gobex_packet_SOURCES) \ $(unit_test_gobex_transfer_SOURCES) $(unit_test_hfp_SOURCES) \ $(unit_test_hog_SOURCES) $(unit_test_lib_SOURCES) \ $(unit_test_mesh_crypto_SOURCES) $(unit_test_mgmt_SOURCES) \ $(unit_test_micp_SOURCES) $(unit_test_midi_SOURCES) \ $(unit_test_queue_SOURCES) $(unit_test_ringbuf_SOURCES) \ $(unit_test_sdp_SOURCES) $(unit_test_tester_SOURCES) \ $(unit_test_textfile_SOURCES) $(unit_test_uhid_SOURCES) \ $(unit_test_uuid_SOURCES) $(unit_test_vcp_SOURCES) DIST_SOURCES = $(am__android_audio_a2dp_default_la_SOURCES_DIST) \ $(am__android_audio_sco_default_la_SOURCES_DIST) \ $(am__android_bluetooth_default_la_SOURCES_DIST) \ $(am__ell_libell_internal_la_SOURCES_DIST) \ $(gdbus_libgdbus_internal_la_SOURCES) \ $(lib_libbluetooth_internal_la_SOURCES) \ $(am__lib_libbluetooth_la_SOURCES_DIST) \ $(am__src_libshared_ell_la_SOURCES_DIST) \ $(am__src_libshared_glib_la_SOURCES_DIST) \ $(am__src_libshared_mainloop_la_SOURCES_DIST) \ $(am__android_android_tester_SOURCES_DIST) \ $(am__android_avdtptest_SOURCES_DIST) \ $(am__android_bluetoothd_SOURCES_DIST) \ $(am__android_bluetoothd_snoop_SOURCES_DIST) \ $(am__android_haltest_SOURCES_DIST) \ $(am__android_ipc_tester_SOURCES_DIST) \ $(am__android_system_emulator_SOURCES_DIST) \ $(am__android_test_ipc_SOURCES_DIST) \ $(am__attrib_gatttool_SOURCES_DIST) \ $(am__client_bluetoothctl_SOURCES_DIST) \ $(am__emulator_b1ee_SOURCES_DIST) \ $(am__emulator_btvirt_SOURCES_DIST) \ $(am__emulator_hfp_SOURCES_DIST) \ $(am__mesh_bluetooth_meshd_SOURCES_DIST) \ $(am__monitor_btmon_SOURCES_DIST) \ $(am__obexd_src_obexd_SOURCES_DIST) \ $(am__peripheral_btsensor_SOURCES_DIST) \ $(am__profiles_cups_bluetooth_SOURCES_DIST) \ $(am__profiles_iap_iapd_SOURCES_DIST) \ $(am__src_bluetoothd_SOURCES_DIST) \ $(am__tools_3dsp_SOURCES_DIST) \ $(am__tools_advtest_SOURCES_DIST) tools/avinfo.c \ tools/avtest.c tools/bcmfw.c $(am__tools_bdaddr_SOURCES_DIST) \ $(am__tools_bluemoon_SOURCES_DIST) \ $(am__tools_bluetooth_player_SOURCES_DIST) \ $(am__tools_bnep_tester_SOURCES_DIST) \ $(am__tools_bneptest_SOURCES_DIST) \ $(am__tools_btattach_SOURCES_DIST) \ $(am__tools_btconfig_SOURCES_DIST) \ $(am__tools_btgatt_client_SOURCES_DIST) \ $(am__tools_btgatt_server_SOURCES_DIST) \ $(am__tools_btinfo_SOURCES_DIST) \ $(am__tools_btiotest_SOURCES_DIST) \ $(am__tools_btmgmt_SOURCES_DIST) \ $(am__tools_btmon_logger_SOURCES_DIST) \ $(am__tools_btpclient_SOURCES_DIST) \ $(am__tools_btpclientctl_SOURCES_DIST) \ $(am__tools_btproxy_SOURCES_DIST) \ $(am__tools_btsnoop_SOURCES_DIST) tools/check-selftest.c \ tools/ciptool.c $(am__tools_cltest_SOURCES_DIST) \ $(am__tools_create_image_SOURCES_DIST) \ $(am__tools_eddystone_SOURCES_DIST) \ $(am__tools_gap_tester_SOURCES_DIST) \ $(am__tools_gatt_service_SOURCES_DIST) \ $(am__tools_hci_tester_SOURCES_DIST) \ $(am__tools_hciattach_SOURCES_DIST) \ $(am__tools_hciconfig_SOURCES_DIST) \ $(am__tools_hcidump_SOURCES_DIST) tools/hcieventmask.c \ tools/hcisecfilter.c $(am__tools_hcitool_SOURCES_DIST) \ $(am__tools_hex2hcd_SOURCES_DIST) tools/hid2hci.c tools/hwdb.c \ $(am__tools_ibeacon_SOURCES_DIST) \ $(am__tools_ioctl_tester_SOURCES_DIST) \ $(am__tools_iso_tester_SOURCES_DIST) tools/isotest.c \ $(am__tools_l2cap_tester_SOURCES_DIST) tools/l2ping.c \ tools/l2test.c $(am__tools_mcaptest_SOURCES_DIST) \ $(am__tools_mesh_cfgclient_SOURCES_DIST) \ $(am__tools_mesh_cfgtest_SOURCES_DIST) \ $(am__tools_mesh_tester_SOURCES_DIST) \ $(am__tools_meshctl_SOURCES_DIST) \ $(am__tools_mgmt_tester_SOURCES_DIST) \ $(am__tools_mpris_proxy_SOURCES_DIST) \ $(am__tools_nokfw_SOURCES_DIST) \ $(am__tools_obex_client_tool_SOURCES_DIST) \ $(am__tools_obex_server_tool_SOURCES_DIST) \ $(am__tools_obexctl_SOURCES_DIST) \ $(am__tools_oobtest_SOURCES_DIST) tools/rctest.c \ tools/rfcomm.c $(am__tools_rfcomm_tester_SOURCES_DIST) \ $(am__tools_rtlfw_SOURCES_DIST) \ $(am__tools_sco_tester_SOURCES_DIST) tools/scotest.c \ $(am__tools_sdptool_SOURCES_DIST) \ $(am__tools_seq2bseq_SOURCES_DIST) \ $(am__tools_smp_tester_SOURCES_DIST) tools/test-runner.c \ $(am__tools_userchan_tester_SOURCES_DIST) \ $(unit_test_avctp_SOURCES) $(unit_test_avdtp_SOURCES) \ $(unit_test_avrcp_SOURCES) $(unit_test_bap_SOURCES) \ $(unit_test_bass_SOURCES) $(unit_test_crc_SOURCES) \ $(unit_test_crypto_SOURCES) $(unit_test_ecc_SOURCES) \ $(unit_test_eir_SOURCES) $(unit_test_gatt_SOURCES) \ $(unit_test_gattrib_SOURCES) $(unit_test_gdbus_client_SOURCES) \ $(am__unit_test_gobex_SOURCES_DIST) \ $(am__unit_test_gobex_apparam_SOURCES_DIST) \ $(am__unit_test_gobex_header_SOURCES_DIST) \ $(am__unit_test_gobex_packet_SOURCES_DIST) \ $(am__unit_test_gobex_transfer_SOURCES_DIST) \ $(unit_test_hfp_SOURCES) $(unit_test_hog_SOURCES) \ $(unit_test_lib_SOURCES) \ $(am__unit_test_mesh_crypto_SOURCES_DIST) \ $(unit_test_mgmt_SOURCES) $(unit_test_micp_SOURCES) \ $(am__unit_test_midi_SOURCES_DIST) $(unit_test_queue_SOURCES) \ $(unit_test_ringbuf_SOURCES) $(unit_test_sdp_SOURCES) \ $(unit_test_tester_SOURCES) $(unit_test_textfile_SOURCES) \ $(unit_test_uhid_SOURCES) $(unit_test_uuid_SOURCES) \ $(unit_test_vcp_SOURCES) am__can_run_installinfo = \ case $$AM_UPDATE_INFO_DIR in \ n|no|NO) false;; \ *) (install-info --version) >/dev/null 2>&1;; \ esac man1dir = $(mandir)/man1 man5dir = $(mandir)/man5 man7dir = $(mandir)/man7 man8dir = $(mandir)/man8 NROFF = nroff MANS = $(man_MANS) am__dist_zshcompletion_DATA_DIST = completion/zsh/_bluetoothctl DATA = $(conf_DATA) $(dbus_DATA) $(dbussessionbus_DATA) \ $(dbussystembus_DATA) $(dist_zshcompletion_DATA) \ $(pkgconfig_DATA) $(rules_DATA) $(state_DATA) \ $(systemdsystemunit_DATA) $(systemduserunit_DATA) am__pkginclude_HEADERS_DIST = lib/bluetooth.h lib/hci.h lib/hci_lib.h \ lib/sco.h lib/l2cap.h lib/sdp.h lib/sdp_lib.h lib/rfcomm.h \ lib/bnep.h lib/cmtp.h lib/hidp.h HEADERS = $(pkginclude_HEADERS) am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP) \ config.h.in # Read a list of newline-separated strings from the standard input, # and print each of them once, without duplicates. Input order is # *not* preserved. am__uniquify_input = $(AWK) '\ BEGIN { nonempty = 0; } \ { items[$$0] = 1; nonempty = 1; } \ END { if (nonempty) { for (i in items) print i; }; } \ ' # Make sure the list of sources is unique. This is necessary because, # e.g., the same source file might be shared among _SOURCES variables # for different programs/libraries. am__define_uniq_tagged_files = \ list='$(am__tagged_files)'; \ unique=`for i in $$list; do \ if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ done | $(am__uniquify_input)` AM_RECURSIVE_TARGETS = cscope check recheck am__tty_colors_dummy = \ mgn= red= grn= lgn= blu= brg= std=; \ am__color_tests=no am__tty_colors = { \ $(am__tty_colors_dummy); \ if test "X$(AM_COLOR_TESTS)" = Xno; then \ am__color_tests=no; \ elif test "X$(AM_COLOR_TESTS)" = Xalways; then \ am__color_tests=yes; \ elif test "X$$TERM" != Xdumb && { test -t 1; } 2>/dev/null; then \ am__color_tests=yes; \ fi; \ if test $$am__color_tests = yes; then \ red=''; \ grn=''; \ lgn=''; \ blu=''; \ mgn=''; \ brg=''; \ std=''; \ fi; \ } am__recheck_rx = ^[ ]*:recheck:[ ]* am__global_test_result_rx = ^[ ]*:global-test-result:[ ]* am__copy_in_global_log_rx = ^[ ]*:copy-in-global-log:[ ]* # A command that, given a newline-separated list of test names on the # standard input, print the name of the tests that are to be re-run # upon "make recheck". am__list_recheck_tests = $(AWK) '{ \ recheck = 1; \ while ((rc = (getline line < ($$0 ".trs"))) != 0) \ { \ if (rc < 0) \ { \ if ((getline line2 < ($$0 ".log")) < 0) \ recheck = 0; \ break; \ } \ else if (line ~ /$(am__recheck_rx)[nN][Oo]/) \ { \ recheck = 0; \ break; \ } \ else if (line ~ /$(am__recheck_rx)[yY][eE][sS]/) \ { \ break; \ } \ }; \ if (recheck) \ print $$0; \ close ($$0 ".trs"); \ close ($$0 ".log"); \ }' # A command that, given a newline-separated list of test names on the # standard input, create the global log from their .trs and .log files. am__create_global_log = $(AWK) ' \ function fatal(msg) \ { \ print "fatal: making $@: " msg | "cat >&2"; \ exit 1; \ } \ function rst_section(header) \ { \ print header; \ len = length(header); \ for (i = 1; i <= len; i = i + 1) \ printf "="; \ printf "\n\n"; \ } \ { \ copy_in_global_log = 1; \ global_test_result = "RUN"; \ while ((rc = (getline line < ($$0 ".trs"))) != 0) \ { \ if (rc < 0) \ fatal("failed to read from " $$0 ".trs"); \ if (line ~ /$(am__global_test_result_rx)/) \ { \ sub("$(am__global_test_result_rx)", "", line); \ sub("[ ]*$$", "", line); \ global_test_result = line; \ } \ else if (line ~ /$(am__copy_in_global_log_rx)[nN][oO]/) \ copy_in_global_log = 0; \ }; \ if (copy_in_global_log) \ { \ rst_section(global_test_result ": " $$0); \ while ((rc = (getline line < ($$0 ".log"))) != 0) \ { \ if (rc < 0) \ fatal("failed to read from " $$0 ".log"); \ print line; \ }; \ printf "\n"; \ }; \ close ($$0 ".trs"); \ close ($$0 ".log"); \ }' # Restructured Text title. am__rst_title = { sed 's/.*/ & /;h;s/./=/g;p;x;s/ *$$//;p;g' && echo; } # Solaris 10 'make', and several other traditional 'make' implementations, # pass "-e" to $(SHELL), and POSIX 2008 even requires this. Work around it # by disabling -e (using the XSI extension "set +e") if it's set. am__sh_e_setup = case $$- in *e*) set +e;; esac # Default flags passed to test drivers. am__common_driver_flags = \ --color-tests "$$am__color_tests" \ --enable-hard-errors "$$am__enable_hard_errors" \ --expect-failure "$$am__expect_failure" # To be inserted before the command running the test. Creates the # directory for the log if needed. Stores in $dir the directory # containing $f, in $tst the test, in $log the log. Executes the # developer- defined test setup AM_TESTS_ENVIRONMENT (if any), and # passes TESTS_ENVIRONMENT. Set up options for the wrapper that # will run the test scripts (or their associated LOG_COMPILER, if # thy have one). am__check_pre = \ $(am__sh_e_setup); \ $(am__vpath_adj_setup) $(am__vpath_adj) \ $(am__tty_colors); \ srcdir=$(srcdir); export srcdir; \ case "$@" in \ */*) am__odir=`echo "./$@" | sed 's|/[^/]*$$||'`;; \ *) am__odir=.;; \ esac; \ test "x$$am__odir" = x"." || test -d "$$am__odir" \ || $(MKDIR_P) "$$am__odir" || exit $$?; \ if test -f "./$$f"; then dir=./; \ elif test -f "$$f"; then dir=; \ else dir="$(srcdir)/"; fi; \ tst=$$dir$$f; log='$@'; \ if test -n '$(DISABLE_HARD_ERRORS)'; then \ am__enable_hard_errors=no; \ else \ am__enable_hard_errors=yes; \ fi; \ case " $(XFAIL_TESTS) " in \ *[\ \ ]$$f[\ \ ]* | *[\ \ ]$$dir$$f[\ \ ]*) \ am__expect_failure=yes;; \ *) \ am__expect_failure=no;; \ esac; \ $(AM_TESTS_ENVIRONMENT) $(TESTS_ENVIRONMENT) # A shell command to get the names of the tests scripts with any registered # extension removed (i.e., equivalently, the names of the test logs, with # the '.log' extension removed). The result is saved in the shell variable # '$bases'. This honors runtime overriding of TESTS and TEST_LOGS. Sadly, # we cannot use something simpler, involving e.g., "$(TEST_LOGS:.log=)", # since that might cause problem with VPATH rewrites for suffix-less tests. # See also 'test-harness-vpath-rewrite.sh' and 'test-trs-basic.sh'. am__set_TESTS_bases = \ bases='$(TEST_LOGS)'; \ bases=`for i in $$bases; do echo $$i; done | sed 's/\.log$$//'`; \ bases=`echo $$bases` AM_TESTSUITE_SUMMARY_HEADER = ' for $(PACKAGE_STRING)' RECHECK_LOGS = $(TEST_LOGS) TEST_SUITE_LOG = test-suite.log TEST_EXTENSIONS = @EXEEXT@ .test LOG_DRIVER = $(SHELL) $(top_srcdir)/test-driver LOG_COMPILE = $(LOG_COMPILER) $(AM_LOG_FLAGS) $(LOG_FLAGS) am__set_b = \ case '$@' in \ */*) \ case '$*' in \ */*) b='$*';; \ *) b=`echo '$@' | sed 's/\.log$$//'`; \ esac;; \ *) \ b='$*';; \ esac am__test_logs1 = $(TESTS:=.log) am__test_logs2 = $(am__test_logs1:@EXEEXT@.log=.log) TEST_LOGS = $(am__test_logs2:.test.log=.log) TEST_LOG_DRIVER = $(SHELL) $(top_srcdir)/test-driver TEST_LOG_COMPILE = $(TEST_LOG_COMPILER) $(AM_TEST_LOG_FLAGS) \ $(TEST_LOG_FLAGS) am__DIST_COMMON = $(srcdir)/Makefile.in $(srcdir)/Makefile.mesh \ $(srcdir)/Makefile.obexd $(srcdir)/Makefile.plugins \ $(srcdir)/Makefile.tools $(srcdir)/android/Makefile.am \ $(srcdir)/config.h.in $(top_srcdir)/lib/bluez.pc.in \ $(top_srcdir)/mesh/bluetooth-mesh.service.in \ $(top_srcdir)/mesh/bluetooth-meshd.rst.in \ $(top_srcdir)/obexd/src/obex.service.in \ $(top_srcdir)/obexd/src/org.bluez.obex.service.in \ $(top_srcdir)/src/bluetooth.service.in \ $(top_srcdir)/src/bluetoothd.rst.in \ $(top_srcdir)/tools/bluetooth-logger.service.in \ $(top_srcdir)/tools/mpris-proxy.service.in AUTHORS COPYING \ COPYING.LIB ChangeLog INSTALL NEWS README TODO compile \ config.guess config.sub depcomp install-sh ltmain.sh missing \ test-driver DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) distdir = $(PACKAGE)-$(VERSION) top_distdir = $(distdir) am__remove_distdir = \ if test -d "$(distdir)"; then \ find "$(distdir)" -type d ! -perm -200 -exec chmod u+w {} ';' \ && rm -rf "$(distdir)" \ || { sleep 5 && rm -rf "$(distdir)"; }; \ else :; fi am__post_remove_distdir = $(am__remove_distdir) GZIP_ENV = --best DIST_ARCHIVES = $(distdir).tar.xz DIST_TARGETS = dist-xz # Exists only to be overridden by the user if desired. AM_DISTCHECK_DVI_TARGET = dvi distuninstallcheck_listfiles = find . -type f -print am__distuninstallcheck_listfiles = $(distuninstallcheck_listfiles) \ | sed 's|^\./|$(prefix)/|' | grep -v '$(infodir)/dir$$' distcleancheck_listfiles = find . -type f -print pkgincludedir = $(includedir)/bluetooth pkglibexecdir = @PKGLIBEXECDIR@ ACLOCAL = @ACLOCAL@ ALSA_CFLAGS = @ALSA_CFLAGS@ ALSA_LIBS = @ALSA_LIBS@ AMTAR = @AMTAR@ AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@ AR = @AR@ ASAN_LIB = @ASAN_LIB@ AUTOCONF = @AUTOCONF@ AUTOHEADER = @AUTOHEADER@ AUTOMAKE = @AUTOMAKE@ AWK = @AWK@ BACKTRACE_CFLAGS = @BACKTRACE_CFLAGS@ BACKTRACE_LIBS = @BACKTRACE_LIBS@ CC = @CC@ CCDEPMODE = @CCDEPMODE@ CFLAGS = @CFLAGS@ CONFIGDIR = @CONFIGDIR@ CPPFLAGS = @CPPFLAGS@ CSCOPE = @CSCOPE@ CTAGS = @CTAGS@ CUPS_SERVERBIN = @CUPS_SERVERBIN@ CYGPATH_W = @CYGPATH_W@ DBUS_CFLAGS = @DBUS_CFLAGS@ DBUS_CONFDIR = @DBUS_CONFDIR@ DBUS_LIBS = @DBUS_LIBS@ DBUS_SESSIONBUSDIR = @DBUS_SESSIONBUSDIR@ DBUS_SYSTEMBUSDIR = @DBUS_SYSTEMBUSDIR@ DEFS = @DEFS@ DEPDIR = @DEPDIR@ DLLTOOL = @DLLTOOL@ DSYMUTIL = @DSYMUTIL@ DUMPBIN = @DUMPBIN@ ECHO_C = @ECHO_C@ ECHO_N = @ECHO_N@ ECHO_T = @ECHO_T@ EGREP = @EGREP@ ELL_CFLAGS = @ELL_CFLAGS@ ELL_LIBS = @ELL_LIBS@ ETAGS = @ETAGS@ EXEEXT = @EXEEXT@ FGREP = @FGREP@ FILECMD = @FILECMD@ GLIB_CFLAGS = @GLIB_CFLAGS@ GLIB_LIBS = @GLIB_LIBS@ GREP = @GREP@ GTHREAD_CFLAGS = @GTHREAD_CFLAGS@ GTHREAD_LIBS = @GTHREAD_LIBS@ ICAL_CFLAGS = @ICAL_CFLAGS@ ICAL_LIBS = @ICAL_LIBS@ INSTALL = @INSTALL@ INSTALL_DATA = @INSTALL_DATA@ INSTALL_PROGRAM = @INSTALL_PROGRAM@ INSTALL_SCRIPT = @INSTALL_SCRIPT@ INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@ JSONC_CFLAGS = @JSONC_CFLAGS@ JSONC_LIBS = @JSONC_LIBS@ LD = @LD@ LDFLAGS = @LDFLAGS@ LIBEBOOK_CFLAGS = @LIBEBOOK_CFLAGS@ LIBEBOOK_LIBS = @LIBEBOOK_LIBS@ LIBEDATESERVER_CFLAGS = @LIBEDATESERVER_CFLAGS@ LIBEDATESERVER_LIBS = @LIBEDATESERVER_LIBS@ LIBOBJS = @LIBOBJS@ LIBS = @LIBS@ LIBTOOL = @LIBTOOL@ LIPO = @LIPO@ LN_S = @LN_S@ LTLIBOBJS = @LTLIBOBJS@ LT_SYS_LIBRARY_PATH = @LT_SYS_LIBRARY_PATH@ MAINT = @MAINT@ MAKEINFO = @MAKEINFO@ MANIFEST_TOOL = @MANIFEST_TOOL@ MESH_STORAGEDIR = @MESH_STORAGEDIR@ MISC_CFLAGS = @MISC_CFLAGS@ MISC_LDFLAGS = @MISC_LDFLAGS@ MKDIR_P = @MKDIR_P@ NM = @NM@ NMEDIT = @NMEDIT@ OBJDUMP = @OBJDUMP@ OBJEXT = @OBJEXT@ OTOOL = @OTOOL@ OTOOL64 = @OTOOL64@ PACKAGE = @PACKAGE@ PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@ PACKAGE_NAME = @PACKAGE_NAME@ PACKAGE_STRING = @PACKAGE_STRING@ PACKAGE_TARNAME = @PACKAGE_TARNAME@ PACKAGE_URL = @PACKAGE_URL@ PACKAGE_VERSION = @PACKAGE_VERSION@ PATH_SEPARATOR = @PATH_SEPARATOR@ PKGBINDIR = @PKGBINDIR@ PKGLIBEXECDIR = @PKGLIBEXECDIR@ PKG_CONFIG = @PKG_CONFIG@ PKG_CONFIG_LIBDIR = @PKG_CONFIG_LIBDIR@ PKG_CONFIG_PATH = @PKG_CONFIG_PATH@ PLUGIN_PHONEBOOK = @PLUGIN_PHONEBOOK@ RANLIB = @RANLIB@ RST2MAN = @RST2MAN@ SBC_CFLAGS = @SBC_CFLAGS@ SBC_LIBS = @SBC_LIBS@ SED = @SED@ SET_MAKE = @SET_MAKE@ SHELL = @SHELL@ SPEEXDSP_CFLAGS = @SPEEXDSP_CFLAGS@ SPEEXDSP_LIBS = @SPEEXDSP_LIBS@ STRIP = @STRIP@ SYSTEMD_SYSTEMUNITDIR = @SYSTEMD_SYSTEMUNITDIR@ SYSTEMD_USERUNITDIR = @SYSTEMD_USERUNITDIR@ UDEV_CFLAGS = @UDEV_CFLAGS@ UDEV_DIR = @UDEV_DIR@ UDEV_LIBS = @UDEV_LIBS@ VERSION = @VERSION@ WARNING_CFLAGS = @WARNING_CFLAGS@ ZSH_COMPLETIONDIR = @ZSH_COMPLETIONDIR@ abs_builddir = @abs_builddir@ abs_srcdir = @abs_srcdir@ abs_top_builddir = @abs_top_builddir@ abs_top_srcdir = @abs_top_srcdir@ ac_ct_AR = @ac_ct_AR@ ac_ct_CC = @ac_ct_CC@ ac_ct_DUMPBIN = @ac_ct_DUMPBIN@ am__include = @am__include@ am__leading_dot = @am__leading_dot@ am__quote = @am__quote@ am__tar = @am__tar@ am__untar = @am__untar@ bindir = @bindir@ build = @build@ build_alias = @build_alias@ build_cpu = @build_cpu@ build_os = @build_os@ build_vendor = @build_vendor@ builddir = @builddir@ datadir = @datadir@ datarootdir = @datarootdir@ docdir = @docdir@ dvidir = @dvidir@ enable_coverage = @enable_coverage@ enable_dbus_run_session = @enable_dbus_run_session@ enable_valgrind = @enable_valgrind@ exec_prefix = @exec_prefix@ host = @host@ host_alias = @host_alias@ host_cpu = @host_cpu@ host_os = @host_os@ host_vendor = @host_vendor@ htmldir = @htmldir@ includedir = @includedir@ infodir = @infodir@ install_sh = @install_sh@ libdir = @libdir@ libexecdir = @libexecdir@ localedir = @localedir@ localstatedir = @localstatedir@ mandir = @mandir@ mkdir_p = @mkdir_p@ oldincludedir = @oldincludedir@ pdfdir = @pdfdir@ prefix = @prefix@ program_transform_name = @program_transform_name@ psdir = @psdir@ runstatedir = @runstatedir@ sbindir = @sbindir@ sharedstatedir = @sharedstatedir@ srcdir = @srcdir@ sysconfdir = @sysconfdir@ target_alias = @target_alias@ top_build_prefix = @top_build_prefix@ top_builddir = @top_builddir@ top_srcdir = @top_srcdir@ # SPDX-License-Identifier: GPL-2.0 AM_MAKEFLAGS = --no-print-directory AM_CPPFLAGS = $(am__append_73) $(DBUS_CFLAGS) $(GLIB_CFLAGS) \ -I$(builddir)/lib lib_LTLIBRARIES = $(am__append_3) noinst_LIBRARIES = noinst_LTLIBRARIES = lib/libbluetooth-internal.la \ gdbus/libgdbus-internal.la $(am__append_4) \ src/libshared-glib.la src/libshared-mainloop.la \ $(am__append_5) CLEANFILES = $(ell_built_sources) $(builtin_files) obexd/src/builtin.h \ $(am__append_84) EXTRA_DIST = src/org.bluez.service src/genbuiltin src/bluetooth.conf \ src/main.conf profiles/network/network.conf \ profiles/input/input.conf obexd/src/obex.conf $(am__append_60) \ $(am__append_71) obexd/src/genbuiltin android/Android.mk \ android/README android/compat/readline/history.h \ android/compat/readline/readline.h android/compat/wordexp.h \ android/bluetoothd-wrapper.c android/log.c \ android/bluetoothd.te android/bluetoothd_snoop.te \ android/init.bluetooth.rc android/hal-ipc-api.txt \ android/audio-ipc-api.txt android/cts.txt \ android/pics-rfcomm.txt android/pics-spp.txt \ android/pics-sdp.txt android/pics-l2cap.txt \ android/pics-gap.txt android/pics-did.txt android/pics-hid.txt \ android/pics-pan.txt android/pics-opp.txt android/pics-map.txt \ android/pics-pbap.txt android/pics-a2dp.txt \ android/pics-avctp.txt android/pics-avrcp.txt \ android/pics-hsp.txt android/pics-hfp.txt \ android/pics-gatt.txt android/pics-mcap.txt \ android/pics-hdp.txt android/pics-iopt.txt android/pics-sm.txt \ android/pics-mps.txt android/pics-hogp.txt \ android/pics-scpp.txt android/pics-dis.txt \ android/pics-avdtp.txt android/pics-gavdp.txt \ android/pics-bnep.txt android/pixit-l2cap.txt \ android/pixit-gap.txt android/pixit-did.txt \ android/pixit-hid.txt android/pixit-pan.txt \ android/pixit-opp.txt android/pixit-map.txt \ android/pixit-pbap.txt android/pixit-a2dp.txt \ android/pixit-avctp.txt android/pixit-avrcp.txt \ android/pixit-hsp.txt android/pixit-hfp.txt \ android/pixit-gatt.txt android/pixit-mcap.txt \ android/pixit-hdp.txt android/pixit-iopt.txt \ android/pixit-sm.txt android/pixit-mps.txt \ android/pixit-hogp.txt android/pixit-scpp.txt \ android/pixit-dis.txt android/pixit-rfcomm.txt \ android/pixit-spp.txt android/pixit-avdtp.txt \ android/pixit-gavdp.txt android/pixit-sdp.txt \ android/pixit-bnep.txt android/pts-rfcomm.txt \ android/pts-spp.txt android/pts-l2cap.txt android/pts-gap.txt \ android/pts-did.txt android/pts-hid.txt android/pts-pan.txt \ android/pts-opp.txt android/pts-map.txt android/pts-a2dp.txt \ android/pts-avrcp.txt android/pts-avctp.txt \ android/pts-pbap.txt android/pts-hfp.txt android/pts-gatt.txt \ android/pts-hsp.txt android/pts-iopt.txt android/pts-hdp.txt \ android/pts-mcap.txt android/pts-mps.txt android/pts-sm.txt \ android/pts-hogp.txt android/pts-scpp.txt android/pts-dis.txt \ android/pts-avdtp.txt android/pts-gavdp.txt \ android/pts-sdp.txt android/pts-bnep.txt \ mesh/bluetooth-mesh.conf mesh/org.bluez.mesh.service \ mesh/mesh-main.conf tools/hid2hci.rules $(test_scripts) \ doc/assigned-numbers.txt doc/supported-features.txt \ doc/test-coverage.txt doc/test-runner.rst \ doc/settings-storage.txt doc/mgmt-api.txt doc/health-api.txt \ doc/sap-api.txt doc/hci.rst doc/l2cap.rst doc/rfcomm.rst \ doc/sco.rst doc/org.bluez.Adapter.rst doc/org.bluez.Device.rst \ doc/org.bluez.DeviceSet.rst doc/org.bluez.AgentManager.rst \ doc/org.bluez.Agent.rst doc/org.bluez.ProfileManager.rst \ doc/org.bluez.Profile.rst doc/org.bluez.NetworkServer.rst \ doc/org.bluez.Network.rst doc/org.bluez.Input.rst \ doc/org.bluez.BatteryProviderManager.rst \ doc/org.bluez.BatteryProvider.rst doc/org.bluez.Battery.rst \ doc/org.bluez.AdminPolicySet.rst \ doc/org.bluez.AdminPolicyStatus.rst doc/org.bluez.Media.rst \ doc/org.bluez.MediaControl.rst doc/org.bluez.MediaPlayer.rst \ doc/org.bluez.MediaFolder.rst doc/org.bluez.MediaItem.rst \ doc/org.bluez.MediaEndpoint.rst \ doc/org.bluez.MediaTransport.rst \ doc/org.bluez.MediaAssistant.rst doc/org.bluez.GattManager.rst \ doc/org.bluez.GattProfile.rst doc/org.bluez.GattService.rst \ doc/org.bluez.GattCharacteristic.rst \ doc/org.bluez.GattDescriptor.rst \ doc/org.bluez.LEAdvertisingManager.rst \ doc/org.bluez.LEAdvertisement.rst \ doc/org.bluez.AdvertisementMonitorManager.rst \ doc/org.bluez.AdvertisementMonitor.rst \ doc/org.bluez.obex.Client.rst doc/org.bluez.obex.Session.rst \ doc/org.bluez.obex.Transfer.rst \ doc/org.bluez.obex.ObjectPush.rst \ doc/org.bluez.obex.FileTransfer.rst \ doc/org.bluez.obex.Synchronization.rst \ doc/org.bluez.obex.PhonebookAccess.rst \ doc/org.bluez.obex.MessageAccess.rst \ doc/org.bluez.obex.Message.rst \ doc/org.bluez.obex.AgentManager.rst \ doc/org.bluez.obex.Agent.rst doc/org.bluez.obex.Image.rst \ doc/pics-opp.txt doc/pixit-opp.txt doc/pts-opp.txt \ doc/btsnoop.txt tools/magic.btsnoop $(manual_pages) $(patsubst \ %.1,%.rst, $(patsubst %.8,%.rst,$(manual_pages))) pkginclude_HEADERS = $(am__append_2) AM_CFLAGS = $(MISC_CFLAGS) $(WARNING_CFLAGS) $(UDEV_CFLAGS) $(LIBEBOOK_CFLAGS) \ $(LIBEDATASERVER_CFLAGS) $(ell_cflags) AM_LDFLAGS = $(MISC_LDFLAGS) confdir = $(sysconfdir)/bluetooth statedir = $(localstatedir)/lib/bluetooth @DATAFILES_TRUE@dbusdir = $(DBUS_CONFDIR)/dbus-1/system.d @DATAFILES_TRUE@dbus_DATA = src/bluetooth.conf $(am__append_1) \ @DATAFILES_TRUE@ $(am__append_77) @DATAFILES_TRUE@conf_DATA = src/main.conf profiles/input/input.conf \ @DATAFILES_TRUE@ profiles/network/network.conf $(am__append_78) @DATAFILES_TRUE@state_DATA = @SYSTEMD_TRUE@systemdsystemunitdir = $(SYSTEMD_SYSTEMUNITDIR) @SYSTEMD_TRUE@systemdsystemunit_DATA = src/bluetooth.service \ @SYSTEMD_TRUE@ $(am__append_53) $(am__append_79) @SYSTEMD_TRUE@systemduserunitdir = $(SYSTEMD_USERUNITDIR) @SYSTEMD_TRUE@systemduserunit_DATA = $(am__append_57) $(am__append_68) @SYSTEMD_TRUE@dbussystembusdir = $(DBUS_SYSTEMBUSDIR) @SYSTEMD_TRUE@dbussystembus_DATA = src/org.bluez.service \ @SYSTEMD_TRUE@ $(am__append_80) plugindir = $(libdir)/bluetooth/plugins build_plugindir = $(plugindir) @MANPAGES_TRUE@man_MANS = src/bluetoothd.8 doc/hci.7 doc/l2cap.7 \ @MANPAGES_TRUE@ doc/rfcomm.7 doc/sco.7 doc/org.bluez.Adapter.5 \ @MANPAGES_TRUE@ doc/org.bluez.Device.5 \ @MANPAGES_TRUE@ doc/org.bluez.DeviceSet.5 \ @MANPAGES_TRUE@ doc/org.bluez.AgentManager.5 \ @MANPAGES_TRUE@ doc/org.bluez.Agent.5 \ @MANPAGES_TRUE@ doc/org.bluez.ProfileManager.5 \ @MANPAGES_TRUE@ doc/org.bluez.Profile.5 \ @MANPAGES_TRUE@ doc/org.bluez.NetworkServer.5 \ @MANPAGES_TRUE@ doc/org.bluez.Network.5 doc/org.bluez.Input.5 \ @MANPAGES_TRUE@ doc/org.bluez.BatteryProviderManager.5 \ @MANPAGES_TRUE@ doc/org.bluez.BatteryProvider.5 \ @MANPAGES_TRUE@ doc/org.bluez.Battery.5 \ @MANPAGES_TRUE@ doc/org.bluez.AdminPolicySet.5 \ @MANPAGES_TRUE@ doc/org.bluez.AdminPolicyStatus.5 \ @MANPAGES_TRUE@ doc/org.bluez.Media.5 \ @MANPAGES_TRUE@ doc/org.bluez.MediaControl.5 \ @MANPAGES_TRUE@ doc/org.bluez.MediaPlayer.5 \ @MANPAGES_TRUE@ doc/org.bluez.MediaFolder.5 \ @MANPAGES_TRUE@ doc/org.bluez.MediaItem.5 \ @MANPAGES_TRUE@ doc/org.bluez.MediaEndpoint.5 \ @MANPAGES_TRUE@ doc/org.bluez.MediaTransport.5 \ @MANPAGES_TRUE@ doc/org.bluez.MediaAssistant.5 \ @MANPAGES_TRUE@ doc/org.bluez.GattManager.5 \ @MANPAGES_TRUE@ doc/org.bluez.GattProfile.5 \ @MANPAGES_TRUE@ doc/org.bluez.GattService.5 \ @MANPAGES_TRUE@ doc/org.bluez.GattCharacteristic.5 \ @MANPAGES_TRUE@ doc/org.bluez.GattDescriptor.5 \ @MANPAGES_TRUE@ doc/org.bluez.LEAdvertisingManager.5 \ @MANPAGES_TRUE@ doc/org.bluez.LEAdvertisement.5 \ @MANPAGES_TRUE@ doc/org.bluez.AdvertisementMonitorManager.5 \ @MANPAGES_TRUE@ doc/org.bluez.AdvertisementMonitor.5 \ @MANPAGES_TRUE@ doc/org.bluez.obex.Client.5 \ @MANPAGES_TRUE@ doc/org.bluez.obex.Session.5 \ @MANPAGES_TRUE@ doc/org.bluez.obex.Transfer.5 \ @MANPAGES_TRUE@ doc/org.bluez.obex.ObjectPush.5 \ @MANPAGES_TRUE@ doc/org.bluez.obex.FileTransfer.5 \ @MANPAGES_TRUE@ doc/org.bluez.obex.Synchronization.5 \ @MANPAGES_TRUE@ doc/org.bluez.obex.PhonebookAccess.5 \ @MANPAGES_TRUE@ doc/org.bluez.obex.MessageAccess.5 \ @MANPAGES_TRUE@ doc/org.bluez.obex.Message.5 \ @MANPAGES_TRUE@ doc/org.bluez.obex.AgentManager.5 \ @MANPAGES_TRUE@ doc/org.bluez.obex.Agent.5 \ @MANPAGES_TRUE@ doc/org.bluez.obex.Image.5 $(am__append_51) \ @MANPAGES_TRUE@ $(am__append_58) $(am__append_63) \ @MANPAGES_TRUE@ $(am__append_64) $(am__append_82) manual_pages = src/bluetoothd.8 doc/hci.7 doc/l2cap.7 doc/rfcomm.7 \ doc/sco.7 doc/org.bluez.Adapter.5 doc/org.bluez.Device.5 \ doc/org.bluez.DeviceSet.5 doc/org.bluez.AgentManager.5 \ doc/org.bluez.Agent.5 doc/org.bluez.ProfileManager.5 \ doc/org.bluez.Profile.5 doc/org.bluez.NetworkServer.5 \ doc/org.bluez.Network.5 doc/org.bluez.Input.5 \ doc/org.bluez.BatteryProviderManager.5 \ doc/org.bluez.BatteryProvider.5 doc/org.bluez.Battery.5 \ doc/org.bluez.AdminPolicySet.5 \ doc/org.bluez.AdminPolicyStatus.5 doc/org.bluez.Media.5 \ doc/org.bluez.MediaControl.5 doc/org.bluez.MediaPlayer.5 \ doc/org.bluez.MediaFolder.5 doc/org.bluez.MediaItem.5 \ doc/org.bluez.MediaEndpoint.5 doc/org.bluez.MediaTransport.5 \ doc/org.bluez.MediaAssistant.5 doc/org.bluez.GattManager.5 \ doc/org.bluez.GattProfile.5 doc/org.bluez.GattService.5 \ doc/org.bluez.GattCharacteristic.5 \ doc/org.bluez.GattDescriptor.5 \ doc/org.bluez.LEAdvertisingManager.5 \ doc/org.bluez.LEAdvertisement.5 \ doc/org.bluez.AdvertisementMonitorManager.5 \ doc/org.bluez.AdvertisementMonitor.5 \ doc/org.bluez.obex.Client.5 doc/org.bluez.obex.Session.5 \ doc/org.bluez.obex.Transfer.5 doc/org.bluez.obex.ObjectPush.5 \ doc/org.bluez.obex.FileTransfer.5 \ doc/org.bluez.obex.Synchronization.5 \ doc/org.bluez.obex.PhonebookAccess.5 \ doc/org.bluez.obex.MessageAccess.5 \ doc/org.bluez.obex.Message.5 doc/org.bluez.obex.AgentManager.5 \ doc/org.bluez.obex.Agent.5 doc/org.bluez.obex.Image.5 \ monitor/btmon.1 tools/hciattach.1 tools/hciconfig.1 \ tools/hcitool.1 tools/hcidump.1 tools/rfcomm.1 tools/sdptool.1 \ tools/ciptool.1 tools/rctest.1 tools/l2ping.1 tools/btattach.1 \ tools/bdaddr.1 tools/isotest.1 tools/btmgmt.1 \ client/bluetoothctl.1 client/bluetoothctl-mgmt.1 \ client/bluetoothctl-monitor.1 client/bluetoothctl-admin.1 \ client/bluetoothctl-advertise.1 client/bluetoothctl-endpoint.1 \ client/bluetoothctl-gatt.1 client/bluetoothctl-player.1 \ client/bluetoothctl-scan.1 client/bluetoothctl-transport.1 \ client/bluetoothctl-assistant.1 client/bluetoothctl-hci.1 \ tools/hid2hci.1 $(am__append_83) plugin_LTLIBRARIES = $(am__append_75) lib_sources = lib/bluetooth.c lib/hci.c lib/sdp.c lib_headers = lib/bluetooth.h lib/hci.h lib/hci_lib.h \ lib/sco.h lib/l2cap.h lib/sdp.h lib/sdp_lib.h \ lib/rfcomm.h lib/bnep.h lib/cmtp.h lib/hidp.h extra_headers = lib/mgmt.h lib/uuid.h lib/iso.h extra_sources = lib/uuid.c local_headers = $(foreach file,$(lib_headers), lib/bluetooth/$(notdir $(file))) BUILT_SOURCES = $(local_headers) $(ell_built_sources) src/builtin.h \ obexd/src/builtin.h @LIBRARY_TRUE@lib_libbluetooth_la_SOURCES = $(lib_headers) $(lib_sources) @LIBRARY_TRUE@lib_libbluetooth_la_LDFLAGS = $(AM_LDFLAGS) -version-info 22:15:19 lib_libbluetooth_internal_la_SOURCES = $(lib_headers) $(lib_sources) \ $(extra_headers) $(extra_sources) gdbus_libgdbus_internal_la_SOURCES = gdbus/gdbus.h \ gdbus/mainloop.c gdbus/watch.c \ gdbus/object.c gdbus/client.c gdbus/polkit.c @EXTERNAL_ELL_FALSE@ell_cflags = @EXTERNAL_ELL_TRUE@ell_cflags = @ELL_CFLAGS@ @EXTERNAL_ELL_FALSE@ell_ldadd = ell/libell-internal.la @EXTERNAL_ELL_TRUE@ell_ldadd = @ELL_LIBS@ @EXTERNAL_ELL_FALSE@ell_built_sources = ell/shared ell/internal ell/ell.h @EXTERNAL_ELL_TRUE@ell_built_sources = ell/shared @EXTERNAL_ELL_FALSE@ell_headers = ell/util.h \ @EXTERNAL_ELL_FALSE@ ell/log.h \ @EXTERNAL_ELL_FALSE@ ell/queue.h \ @EXTERNAL_ELL_FALSE@ ell/hashmap.h \ @EXTERNAL_ELL_FALSE@ ell/random.h \ @EXTERNAL_ELL_FALSE@ ell/signal.h \ @EXTERNAL_ELL_FALSE@ ell/time.h \ @EXTERNAL_ELL_FALSE@ ell/time-private.h \ @EXTERNAL_ELL_FALSE@ ell/timeout.h \ @EXTERNAL_ELL_FALSE@ ell/cipher.h \ @EXTERNAL_ELL_FALSE@ ell/checksum.h \ @EXTERNAL_ELL_FALSE@ ell/io.h \ @EXTERNAL_ELL_FALSE@ ell/idle.h \ @EXTERNAL_ELL_FALSE@ ell/main.h \ @EXTERNAL_ELL_FALSE@ ell/settings.h \ @EXTERNAL_ELL_FALSE@ ell/strv.h \ @EXTERNAL_ELL_FALSE@ ell/string.h \ @EXTERNAL_ELL_FALSE@ ell/utf8.h \ @EXTERNAL_ELL_FALSE@ ell/dbus.h \ @EXTERNAL_ELL_FALSE@ ell/dbus-service.h \ @EXTERNAL_ELL_FALSE@ ell/dbus-client.h \ @EXTERNAL_ELL_FALSE@ ell/key.h \ @EXTERNAL_ELL_FALSE@ ell/cert.h \ @EXTERNAL_ELL_FALSE@ ell/pem.h \ @EXTERNAL_ELL_FALSE@ ell/base64.h \ @EXTERNAL_ELL_FALSE@ ell/asn1-private.h \ @EXTERNAL_ELL_FALSE@ ell/cert-private.h \ @EXTERNAL_ELL_FALSE@ ell/pem-private.h \ @EXTERNAL_ELL_FALSE@ ell/uuid.h \ @EXTERNAL_ELL_FALSE@ ell/useful.h \ @EXTERNAL_ELL_FALSE@ ell/main-private.h \ @EXTERNAL_ELL_FALSE@ ell/tester.h \ @EXTERNAL_ELL_FALSE@ ell/tls.h \ @EXTERNAL_ELL_FALSE@ ell/tls-private.h \ @EXTERNAL_ELL_FALSE@ ell/ecc.h \ @EXTERNAL_ELL_FALSE@ ell/ecc-private.h \ @EXTERNAL_ELL_FALSE@ ell/cleanup.h \ @EXTERNAL_ELL_FALSE@ ell/ecdh.h @EXTERNAL_ELL_FALSE@ell_sources = ell/private.h ell/missing.h \ @EXTERNAL_ELL_FALSE@ ell/util.c \ @EXTERNAL_ELL_FALSE@ ell/log.c \ @EXTERNAL_ELL_FALSE@ ell/queue.c \ @EXTERNAL_ELL_FALSE@ ell/hashmap.c \ @EXTERNAL_ELL_FALSE@ ell/random.c \ @EXTERNAL_ELL_FALSE@ ell/signal.c \ @EXTERNAL_ELL_FALSE@ ell/time.c \ @EXTERNAL_ELL_FALSE@ ell/timeout.c \ @EXTERNAL_ELL_FALSE@ ell/io.c \ @EXTERNAL_ELL_FALSE@ ell/idle.c \ @EXTERNAL_ELL_FALSE@ ell/main.c \ @EXTERNAL_ELL_FALSE@ ell/settings.c \ @EXTERNAL_ELL_FALSE@ ell/strv.c \ @EXTERNAL_ELL_FALSE@ ell/string.c \ @EXTERNAL_ELL_FALSE@ ell/cipher.c \ @EXTERNAL_ELL_FALSE@ ell/checksum.c \ @EXTERNAL_ELL_FALSE@ ell/pem.c \ @EXTERNAL_ELL_FALSE@ ell/cert.c \ @EXTERNAL_ELL_FALSE@ ell/cert-crypto.c \ @EXTERNAL_ELL_FALSE@ ell/key.c \ @EXTERNAL_ELL_FALSE@ ell/base64.c \ @EXTERNAL_ELL_FALSE@ ell/utf8.c \ @EXTERNAL_ELL_FALSE@ ell/dbus-private.h \ @EXTERNAL_ELL_FALSE@ ell/dbus.c \ @EXTERNAL_ELL_FALSE@ ell/dbus-message.c \ @EXTERNAL_ELL_FALSE@ ell/dbus-util.c \ @EXTERNAL_ELL_FALSE@ ell/dbus-service.c \ @EXTERNAL_ELL_FALSE@ ell/dbus-client.c \ @EXTERNAL_ELL_FALSE@ ell/dbus-name-cache.c \ @EXTERNAL_ELL_FALSE@ ell/dbus-filter.c \ @EXTERNAL_ELL_FALSE@ ell/gvariant-private.h \ @EXTERNAL_ELL_FALSE@ ell/gvariant-util.c \ @EXTERNAL_ELL_FALSE@ ell/siphash-private.h \ @EXTERNAL_ELL_FALSE@ ell/siphash.c \ @EXTERNAL_ELL_FALSE@ ell/uuid.c \ @EXTERNAL_ELL_FALSE@ ell/tester.c \ @EXTERNAL_ELL_FALSE@ ell/tls.c \ @EXTERNAL_ELL_FALSE@ ell/tls-extensions.c \ @EXTERNAL_ELL_FALSE@ ell/tls-suites.c \ @EXTERNAL_ELL_FALSE@ ell/tls-record.c \ @EXTERNAL_ELL_FALSE@ ell/ecc.c \ @EXTERNAL_ELL_FALSE@ ell/ecc-external.c \ @EXTERNAL_ELL_FALSE@ ell/ecdh.c @EXTERNAL_ELL_FALSE@ell_shared = ell/useful.h @EXTERNAL_ELL_FALSE@ell_libell_internal_la_SOURCES = $(ell_headers) $(ell_sources) $(ell_shared) shared_sources = src/shared/io.h src/shared/timeout.h \ src/shared/queue.h src/shared/queue.c src/shared/util.h \ src/shared/util.c src/shared/mgmt.h src/shared/mgmt.c \ src/shared/crypto.h src/shared/crypto.c src/shared/ecc.h \ src/shared/ecc.c src/shared/ringbuf.h src/shared/ringbuf.c \ src/shared/tester.h src/shared/hci.h src/shared/hci.c \ src/shared/hci-crypto.h src/shared/hci-crypto.c \ src/shared/hfp.h src/shared/hfp.c src/shared/uhid.h \ src/shared/uhid.c src/shared/pcap.h src/shared/pcap.c \ src/shared/btsnoop.h src/shared/btsnoop.c src/shared/ad.h \ src/shared/ad.c src/shared/att-types.h src/shared/att.h \ src/shared/att.c src/shared/gatt-helpers.h \ src/shared/gatt-helpers.c src/shared/gatt-client.h \ src/shared/gatt-client.c src/shared/gatt-server.h \ src/shared/gatt-server.c src/shared/gatt-db.h \ src/shared/gatt-db.c src/shared/gap.h src/shared/gap.c \ src/shared/log.h src/shared/log.c src/shared/bap.h \ src/shared/bap.c src/shared/ascs.h src/shared/bap-debug.h \ src/shared/bap-debug.c src/shared/mcs.h src/shared/mcp.h \ src/shared/mcp.c src/shared/vcp.c src/shared/vcp.h \ src/shared/micp.c src/shared/micp.h src/shared/csip.c \ src/shared/csip.h src/shared/bass.h src/shared/bass.c \ src/shared/ccp.h src/shared/ccp.c src/shared/lc3.h \ src/shared/tty.h src/shared/bap-defs.h src/shared/asha.h \ src/shared/asha.c $(am__append_6) src_libshared_glib_la_SOURCES = $(shared_sources) \ src/shared/io-glib.c \ src/shared/timeout-glib.c \ src/shared/mainloop-glib.c \ src/shared/mainloop-notify.h \ src/shared/mainloop-notify.c \ src/shared/tester.c src_libshared_glib_la_LDFLAGS = $(AM_LDFLAGS) src_libshared_glib_la_CFLAGS = $(AM_CFLAGS) src_libshared_mainloop_la_SOURCES = $(shared_sources) \ src/shared/io-mainloop.c \ src/shared/timeout-mainloop.c \ src/shared/mainloop.h src/shared/mainloop.c \ src/shared/mainloop-notify.h \ src/shared/mainloop-notify.c src_libshared_mainloop_la_LDFLAGS = $(AM_LDFLAGS) src_libshared_mainloop_la_CFLAGS = $(AM_CFLAGS) @LIBSHARED_ELL_TRUE@src_libshared_ell_la_SOURCES = $(shared_sources) \ @LIBSHARED_ELL_TRUE@ src/shared/io-ell.c \ @LIBSHARED_ELL_TRUE@ src/shared/timeout-ell.c \ @LIBSHARED_ELL_TRUE@ src/shared/mainloop.h \ @LIBSHARED_ELL_TRUE@ src/shared/mainloop-ell.c @LIBSHARED_ELL_TRUE@src_libshared_ell_la_LDFLAGS = $(AM_LDFLAGS) @LIBSHARED_ELL_TRUE@src_libshared_ell_la_CFLAGS = $(AM_CFLAGS) attrib_sources = attrib/att.h attrib/att-database.h attrib/att.c \ attrib/gatt.h attrib/gatt.c \ attrib/gattrib.h attrib/gattrib.c btio_sources = btio/btio.h btio/btio.c gobex_sources = gobex/gobex.h gobex/gobex.c \ gobex/gobex-defs.h gobex/gobex-defs.c \ gobex/gobex-packet.c gobex/gobex-packet.h \ gobex/gobex-header.c gobex/gobex-header.h \ gobex/gobex-transfer.c gobex/gobex-debug.h \ gobex/gobex-apparam.c gobex/gobex-apparam.h # SPDX-License-Identifier: GPL-2.0 builtin_modules = hostname autopair policy $(am__append_7) \ $(am__append_9) $(am__append_11) $(am__append_13) \ $(am__append_15) $(am__append_17) $(am__append_19) \ $(am__append_21) $(am__append_23) gap scanparam deviceinfo \ $(am__append_25) battery $(am__append_29) $(am__append_32) \ $(am__append_34) $(am__append_36) $(am__append_38) \ $(am__append_40) $(am__append_42) $(am__append_44) \ $(am__append_46) builtin_sources = plugins/hostname.c plugins/autopair.c \ plugins/policy.c profiles/audio/media.h profiles/audio/media.c \ profiles/audio/transport.h profiles/audio/transport.c \ profiles/audio/player.h profiles/audio/player.c \ $(am__append_8) $(am__append_10) $(am__append_12) \ $(am__append_14) $(am__append_16) $(am__append_18) \ $(am__append_20) $(am__append_22) $(am__append_24) \ profiles/gap/gas.c profiles/scanparam/scan.c \ profiles/deviceinfo/deviceinfo.c $(am__append_26) \ profiles/battery/battery.c $(am__append_30) $(am__append_33) \ $(am__append_35) $(am__append_37) $(am__append_39) \ $(am__append_41) $(am__append_43) $(am__append_45) \ $(am__append_47) builtin_cppflags = $(am__append_27) builtin_ldadd = $(am__append_28) $(am__append_31) src_bluetoothd_SOURCES = $(builtin_sources) $(attrib_sources) \ $(btio_sources) src/main.c src/log.h src/log.c src/backtrace.h \ src/backtrace.c src/rfkill.c src/btd.h src/sdpd.h \ src/sdpd-server.c src/sdpd-request.c src/sdpd-service.c \ src/sdpd-database.c src/gatt-database.h src/gatt-database.c \ src/sdp-xml.h src/sdp-xml.c src/sdp-client.h src/sdp-client.c \ src/textfile.h src/textfile.c src/uuid-helper.h \ src/uuid-helper.c src/plugin.h src/plugin.c src/storage.h \ src/storage.c src/advertising.h src/advertising.c src/agent.h \ src/agent.c src/error.h src/error.c src/adapter.h \ src/adapter.c src/profile.h src/profile.c src/service.h \ src/service.c src/gatt-client.h src/gatt-client.c src/device.h \ src/device.c src/dbus-common.c src/dbus-common.h src/eir.h \ src/eir.c src/adv_monitor.h src/adv_monitor.c src/battery.h \ src/battery.c src/settings.h src/settings.c src/set.h \ src/set.c $(am__append_48) src_bluetoothd_LDADD = lib/libbluetooth-internal.la \ gdbus/libgdbus-internal.la \ src/libshared-glib.la \ $(BACKTRACE_LIBS) $(GLIB_LIBS) $(DBUS_LIBS) -ldl -lrt \ $(builtin_ldadd) @EXTERNAL_PLUGINS_TRUE@src_bluetoothd_LDFLAGS = $(AM_LDFLAGS) -Wl,--export-dynamic \ @EXTERNAL_PLUGINS_TRUE@ -Wl,--version-script=$(srcdir)/src/bluetooth.ver src_bluetoothd_CPPFLAGS = $(AM_CPPFLAGS) -DBLUETOOTH_PLUGIN_BUILTIN \ -DPLUGINDIR=\""$(build_plugindir)"\" \ $(BACKTRACE_CFLAGS) $(builtin_cppflags) src_bluetoothd_SHORTNAME = bluetoothd builtin_files = src/builtin.h nodist_src_bluetoothd_SOURCES = $(builtin_files) test_scripts = test/sap_client.py test/bluezutils.py test/dbusdef.py \ test/monitor-bluetooth test/list-devices test/test-discovery \ test/test-manager test/test-adapter test/test-device \ test/simple-agent test/simple-endpoint test/test-sap-server \ test/test-network test/test-profile test/test-health \ test/test-health-sink test/service-record.dtd \ test/service-did.xml test/service-spp.xml test/service-opp.xml \ test/service-ftp.xml test/simple-player test/test-nap \ test/test-hfp test/opp-client test/ftp-client test/pbap-client \ test/map-client test/example-advertisement \ test/example-gatt-server test/example-gatt-client \ test/test-gatt-profile test/test-mesh test/agent.py unit_tests = $(am__append_76) unit/test-tester unit/test-eir \ unit/test-uuid unit/test-textfile unit/test-crc \ unit/test-crypto unit/test-ecc unit/test-ringbuf \ unit/test-queue unit/test-mgmt unit/test-uhid unit/test-sdp \ unit/test-avdtp unit/test-avctp unit/test-avrcp unit/test-hfp \ unit/test-gdbus-client $(am__append_85) unit/test-lib \ unit/test-gatt unit/test-hog unit/test-gattrib unit/test-bap \ unit/test-micp unit/test-bass unit/test-vcp $(am__append_86) \ $(am__append_87) @CLIENT_TRUE@client_bluetoothctl_SOURCES = client/main.c \ @CLIENT_TRUE@ client/print.h client/print.c \ @CLIENT_TRUE@ client/display.h client/display.c \ @CLIENT_TRUE@ client/agent.h client/agent.c \ @CLIENT_TRUE@ client/advertising.h \ @CLIENT_TRUE@ client/advertising.c \ @CLIENT_TRUE@ client/adv_monitor.h \ @CLIENT_TRUE@ client/adv_monitor.c \ @CLIENT_TRUE@ client/gatt.h client/gatt.c \ @CLIENT_TRUE@ client/admin.h client/admin.c \ @CLIENT_TRUE@ client/player.h client/player.c \ @CLIENT_TRUE@ client/mgmt.h client/mgmt.c \ @CLIENT_TRUE@ client/assistant.h client/assistant.c \ @CLIENT_TRUE@ client/hci.h client/hci.c @CLIENT_TRUE@client_bluetoothctl_LDADD = lib/libbluetooth-internal.la \ @CLIENT_TRUE@ gdbus/libgdbus-internal.la src/libshared-glib.la \ @CLIENT_TRUE@ $(GLIB_LIBS) $(DBUS_LIBS) -lreadline @ZSH_COMPLETIONS_TRUE@zshcompletiondir = $(ZSH_COMPLETIONDIR) @ZSH_COMPLETIONS_TRUE@dist_zshcompletion_DATA = completion/zsh/_bluetoothctl @MONITOR_TRUE@monitor_btmon_SOURCES = monitor/main.c monitor/bt.h \ @MONITOR_TRUE@ monitor/display.h monitor/display.c \ @MONITOR_TRUE@ monitor/hcidump.h monitor/hcidump.c \ @MONITOR_TRUE@ monitor/ellisys.h monitor/ellisys.c \ @MONITOR_TRUE@ monitor/control.h monitor/control.c \ @MONITOR_TRUE@ monitor/packet.h monitor/packet.c \ @MONITOR_TRUE@ monitor/vendor.h monitor/vendor.c \ @MONITOR_TRUE@ monitor/lmp.h monitor/lmp.c \ @MONITOR_TRUE@ monitor/crc.h monitor/crc.c \ @MONITOR_TRUE@ monitor/ll.h monitor/ll.c \ @MONITOR_TRUE@ monitor/l2cap.h monitor/l2cap.c \ @MONITOR_TRUE@ monitor/sdp.h monitor/sdp.c \ @MONITOR_TRUE@ monitor/avctp.h monitor/avctp.c \ @MONITOR_TRUE@ monitor/avdtp.h monitor/avdtp.c \ @MONITOR_TRUE@ monitor/a2dp.h monitor/a2dp.c \ @MONITOR_TRUE@ monitor/rfcomm.h monitor/rfcomm.c \ @MONITOR_TRUE@ monitor/bnep.h monitor/bnep.c \ @MONITOR_TRUE@ monitor/hwdb.h monitor/hwdb.c \ @MONITOR_TRUE@ monitor/keys.h monitor/keys.c \ @MONITOR_TRUE@ monitor/analyze.h monitor/analyze.c \ @MONITOR_TRUE@ monitor/intel.h monitor/intel.c \ @MONITOR_TRUE@ monitor/broadcom.h monitor/broadcom.c \ @MONITOR_TRUE@ monitor/msft.h monitor/msft.c \ @MONITOR_TRUE@ monitor/jlink.h monitor/jlink.c \ @MONITOR_TRUE@ monitor/tty.h monitor/emulator.h \ @MONITOR_TRUE@ monitor/att.h monitor/att.c \ @MONITOR_TRUE@ src/log.h src/log.c \ @MONITOR_TRUE@ src/textfile.h src/textfile.c \ @MONITOR_TRUE@ src/settings.h src/settings.c @MONITOR_TRUE@monitor_btmon_LDADD = lib/libbluetooth-internal.la \ @MONITOR_TRUE@ src/libshared-mainloop.la \ @MONITOR_TRUE@ $(GLIB_LIBS) $(UDEV_LIBS) -ldl @LOGGER_TRUE@tools_btmon_logger_SOURCES = tools/btmon-logger.c @LOGGER_TRUE@tools_btmon_logger_LDADD = src/libshared-mainloop.la @TESTING_TRUE@emulator_btvirt_SOURCES = emulator/main.c monitor/bt.h \ @TESTING_TRUE@ emulator/serial.h emulator/serial.c \ @TESTING_TRUE@ emulator/server.h emulator/server.c \ @TESTING_TRUE@ emulator/vhci.h emulator/vhci.c \ @TESTING_TRUE@ emulator/btdev.h emulator/btdev.c \ @TESTING_TRUE@ emulator/bthost.h emulator/bthost.c \ @TESTING_TRUE@ emulator/smp.c \ @TESTING_TRUE@ emulator/phy.h emulator/phy.c \ @TESTING_TRUE@ emulator/le.h emulator/le.c @TESTING_TRUE@emulator_btvirt_LDADD = lib/libbluetooth-internal.la src/libshared-mainloop.la @TESTING_TRUE@emulator_b1ee_SOURCES = emulator/b1ee.c @TESTING_TRUE@emulator_b1ee_LDADD = src/libshared-mainloop.la @TESTING_TRUE@emulator_hfp_SOURCES = emulator/hfp.c @TESTING_TRUE@emulator_hfp_LDADD = src/libshared-mainloop.la @TESTING_TRUE@peripheral_btsensor_SOURCES = peripheral/main.c \ @TESTING_TRUE@ peripheral/efivars.h peripheral/efivars.c \ @TESTING_TRUE@ peripheral/attach.h peripheral/attach.c \ @TESTING_TRUE@ peripheral/log.h peripheral/log.c \ @TESTING_TRUE@ peripheral/gap.h peripheral/gap.c \ @TESTING_TRUE@ peripheral/gatt.h peripheral/gatt.c @TESTING_TRUE@peripheral_btsensor_LDADD = src/libshared-mainloop.la \ @TESTING_TRUE@ lib/libbluetooth-internal.la @TESTING_TRUE@tools_3dsp_SOURCES = tools/3dsp.c monitor/bt.h @TESTING_TRUE@tools_3dsp_LDADD = src/libshared-mainloop.la @TESTING_TRUE@tools_mgmt_tester_SOURCES = tools/mgmt-tester.c monitor/bt.h \ @TESTING_TRUE@ emulator/hciemu.h emulator/hciemu.c \ @TESTING_TRUE@ emulator/vhci.h emulator/vhci.c \ @TESTING_TRUE@ emulator/btdev.h emulator/btdev.c \ @TESTING_TRUE@ emulator/bthost.h emulator/bthost.c \ @TESTING_TRUE@ emulator/smp.c @TESTING_TRUE@tools_mgmt_tester_LDADD = lib/libbluetooth-internal.la \ @TESTING_TRUE@ src/libshared-glib.la $(GLIB_LIBS) @TESTING_TRUE@tools_mesh_tester_SOURCES = tools/mesh-tester.c monitor/bt.h \ @TESTING_TRUE@ emulator/hciemu.h emulator/hciemu.c \ @TESTING_TRUE@ emulator/vhci.h emulator/vhci.c \ @TESTING_TRUE@ emulator/btdev.h emulator/btdev.c \ @TESTING_TRUE@ emulator/bthost.h emulator/bthost.c \ @TESTING_TRUE@ emulator/smp.c @TESTING_TRUE@tools_mesh_tester_LDADD = lib/libbluetooth-internal.la \ @TESTING_TRUE@ src/libshared-glib.la $(GLIB_LIBS) @TESTING_TRUE@tools_l2cap_tester_SOURCES = tools/l2cap-tester.c tools/tester.h monitor/bt.h \ @TESTING_TRUE@ emulator/hciemu.h emulator/hciemu.c \ @TESTING_TRUE@ emulator/vhci.h emulator/vhci.c \ @TESTING_TRUE@ emulator/btdev.h emulator/btdev.c \ @TESTING_TRUE@ emulator/bthost.h emulator/bthost.c \ @TESTING_TRUE@ emulator/smp.c @TESTING_TRUE@tools_l2cap_tester_LDADD = lib/libbluetooth-internal.la \ @TESTING_TRUE@ src/libshared-glib.la $(GLIB_LIBS) @TESTING_TRUE@tools_rfcomm_tester_SOURCES = tools/rfcomm-tester.c monitor/bt.h \ @TESTING_TRUE@ emulator/hciemu.h emulator/hciemu.c \ @TESTING_TRUE@ emulator/vhci.h emulator/vhci.c \ @TESTING_TRUE@ emulator/btdev.h emulator/btdev.c \ @TESTING_TRUE@ emulator/bthost.h emulator/bthost.c \ @TESTING_TRUE@ emulator/smp.c @TESTING_TRUE@tools_rfcomm_tester_LDADD = lib/libbluetooth-internal.la \ @TESTING_TRUE@ src/libshared-glib.la $(GLIB_LIBS) @TESTING_TRUE@tools_bnep_tester_SOURCES = tools/bnep-tester.c monitor/bt.h \ @TESTING_TRUE@ emulator/hciemu.h emulator/hciemu.c \ @TESTING_TRUE@ emulator/vhci.h emulator/vhci.c \ @TESTING_TRUE@ emulator/btdev.h emulator/btdev.c \ @TESTING_TRUE@ emulator/bthost.h emulator/bthost.c \ @TESTING_TRUE@ emulator/smp.c @TESTING_TRUE@tools_bnep_tester_LDADD = lib/libbluetooth-internal.la \ @TESTING_TRUE@ src/libshared-glib.la $(GLIB_LIBS) @TESTING_TRUE@tools_smp_tester_SOURCES = tools/smp-tester.c monitor/bt.h \ @TESTING_TRUE@ emulator/hciemu.h emulator/hciemu.c \ @TESTING_TRUE@ emulator/vhci.h emulator/vhci.c \ @TESTING_TRUE@ emulator/btdev.h emulator/btdev.c \ @TESTING_TRUE@ emulator/bthost.h emulator/bthost.c \ @TESTING_TRUE@ emulator/smp.c @TESTING_TRUE@tools_smp_tester_LDADD = lib/libbluetooth-internal.la \ @TESTING_TRUE@ src/libshared-glib.la $(GLIB_LIBS) @TESTING_TRUE@tools_gap_tester_SOURCES = tools/gap-tester.c monitor/bt.h \ @TESTING_TRUE@ emulator/hciemu.h emulator/hciemu.c \ @TESTING_TRUE@ emulator/vhci.h emulator/vhci.c \ @TESTING_TRUE@ emulator/btdev.h emulator/btdev.c \ @TESTING_TRUE@ emulator/bthost.h emulator/bthost.c \ @TESTING_TRUE@ emulator/smp.c @TESTING_TRUE@tools_gap_tester_LDADD = lib/libbluetooth-internal.la \ @TESTING_TRUE@ gdbus/libgdbus-internal.la \ @TESTING_TRUE@ src/libshared-glib.la \ @TESTING_TRUE@ $(GLIB_LIBS) $(DBUS_LIBS) @TESTING_TRUE@tools_sco_tester_SOURCES = tools/sco-tester.c tools/tester.h monitor/bt.h \ @TESTING_TRUE@ emulator/hciemu.h emulator/hciemu.c \ @TESTING_TRUE@ emulator/vhci.h emulator/vhci.c \ @TESTING_TRUE@ emulator/btdev.h emulator/btdev.c \ @TESTING_TRUE@ emulator/bthost.h emulator/bthost.c \ @TESTING_TRUE@ emulator/smp.c @TESTING_TRUE@tools_sco_tester_LDADD = lib/libbluetooth-internal.la \ @TESTING_TRUE@ src/libshared-glib.la $(GLIB_LIBS) @TESTING_TRUE@tools_hci_tester_SOURCES = tools/hci-tester.c monitor/bt.h @TESTING_TRUE@tools_hci_tester_LDADD = src/libshared-glib.la $(GLIB_LIBS) @TESTING_TRUE@tools_userchan_tester_SOURCES = tools/userchan-tester.c monitor/bt.h \ @TESTING_TRUE@ emulator/hciemu.h emulator/hciemu.c \ @TESTING_TRUE@ emulator/vhci.h emulator/vhci.c \ @TESTING_TRUE@ emulator/btdev.h emulator/btdev.c \ @TESTING_TRUE@ emulator/bthost.h emulator/bthost.c \ @TESTING_TRUE@ emulator/smp.c @TESTING_TRUE@tools_userchan_tester_LDADD = lib/libbluetooth-internal.la \ @TESTING_TRUE@ src/libshared-glib.la $(GLIB_LIBS) @TESTING_TRUE@tools_iso_tester_SOURCES = tools/iso-tester.c tools/tester.h monitor/bt.h \ @TESTING_TRUE@ emulator/hciemu.h emulator/hciemu.c \ @TESTING_TRUE@ emulator/vhci.h emulator/vhci.c \ @TESTING_TRUE@ emulator/btdev.h emulator/btdev.c \ @TESTING_TRUE@ emulator/bthost.h emulator/bthost.c \ @TESTING_TRUE@ emulator/smp.c @TESTING_TRUE@tools_iso_tester_LDADD = lib/libbluetooth-internal.la \ @TESTING_TRUE@ src/libshared-glib.la $(GLIB_LIBS) @TESTING_TRUE@tools_ioctl_tester_SOURCES = tools/ioctl-tester.c monitor/bt.h \ @TESTING_TRUE@ emulator/hciemu.h emulator/hciemu.c \ @TESTING_TRUE@ emulator/vhci.h emulator/vhci.c \ @TESTING_TRUE@ emulator/btdev.h emulator/btdev.c \ @TESTING_TRUE@ emulator/bthost.h emulator/bthost.c \ @TESTING_TRUE@ emulator/smp.c @TESTING_TRUE@tools_ioctl_tester_LDADD = lib/libbluetooth-internal.la \ @TESTING_TRUE@ src/libshared-glib.la $(GLIB_LIBS) @TOOLS_TRUE@tools_bdaddr_SOURCES = tools/bdaddr.c src/oui.h src/oui.c @TOOLS_TRUE@tools_bdaddr_LDADD = lib/libbluetooth-internal.la $(UDEV_LIBS) @TOOLS_TRUE@tools_avinfo_LDADD = lib/libbluetooth-internal.la @TOOLS_TRUE@tools_avtest_LDADD = lib/libbluetooth-internal.la @TOOLS_TRUE@tools_scotest_LDADD = lib/libbluetooth-internal.la @TOOLS_TRUE@tools_hwdb_LDADD = lib/libbluetooth-internal.la @TOOLS_TRUE@tools_hcieventmask_LDADD = lib/libbluetooth-internal.la @TOOLS_TRUE@tools_btinfo_SOURCES = tools/btinfo.c monitor/bt.h @TOOLS_TRUE@tools_btinfo_LDADD = src/libshared-mainloop.la @TOOLS_TRUE@tools_btattach_SOURCES = tools/btattach.c monitor/bt.h @TOOLS_TRUE@tools_btattach_LDADD = src/libshared-mainloop.la @TOOLS_TRUE@tools_btconfig_SOURCES = tools/btconfig.c @TOOLS_TRUE@tools_btconfig_LDADD = src/libshared-mainloop.la @TOOLS_TRUE@tools_btsnoop_SOURCES = tools/btsnoop.c @TOOLS_TRUE@tools_btsnoop_LDADD = src/libshared-mainloop.la @TOOLS_TRUE@tools_btproxy_SOURCES = tools/btproxy.c monitor/bt.h @TOOLS_TRUE@tools_btproxy_LDADD = src/libshared-mainloop.la @TOOLS_TRUE@tools_btiotest_SOURCES = tools/btiotest.c btio/btio.h btio/btio.c @TOOLS_TRUE@tools_btiotest_LDADD = lib/libbluetooth-internal.la $(GLIB_LIBS) @TOOLS_TRUE@tools_mcaptest_SOURCES = tools/mcaptest.c \ @TOOLS_TRUE@ btio/btio.h btio/btio.c \ @TOOLS_TRUE@ src/log.c src/log.h \ @TOOLS_TRUE@ profiles/health/mcap.h profiles/health/mcap.c @TOOLS_TRUE@tools_mcaptest_LDADD = lib/libbluetooth-internal.la $(GLIB_LIBS) \ @TOOLS_TRUE@ src/libshared-mainloop.la -lrt @TOOLS_TRUE@tools_bneptest_SOURCES = tools/bneptest.c \ @TOOLS_TRUE@ btio/btio.h btio/btio.c \ @TOOLS_TRUE@ src/log.h src/log.c \ @TOOLS_TRUE@ profiles/network/bnep.h profiles/network/bnep.c @TOOLS_TRUE@tools_bneptest_LDADD = lib/libbluetooth-internal.la $(GLIB_LIBS) \ @TOOLS_TRUE@ src/libshared-mainloop.la @TOOLS_TRUE@tools_cltest_SOURCES = tools/cltest.c @TOOLS_TRUE@tools_cltest_LDADD = lib/libbluetooth-internal.la src/libshared-mainloop.la @TOOLS_TRUE@tools_oobtest_SOURCES = tools/oobtest.c @TOOLS_TRUE@tools_oobtest_LDADD = lib/libbluetooth-internal.la src/libshared-mainloop.la @TOOLS_TRUE@tools_advtest_SOURCES = tools/advtest.c @TOOLS_TRUE@tools_advtest_LDADD = lib/libbluetooth-internal.la src/libshared-mainloop.la @TOOLS_TRUE@tools_seq2bseq_SOURCES = tools/seq2bseq.c @TOOLS_TRUE@tools_nokfw_SOURCES = tools/nokfw.c @TOOLS_TRUE@tools_rtlfw_SOURCES = tools/rtlfw.c @TOOLS_TRUE@tools_create_image_SOURCES = tools/create-image.c @TOOLS_TRUE@tools_eddystone_SOURCES = tools/eddystone.c monitor/bt.h @TOOLS_TRUE@tools_eddystone_LDADD = src/libshared-mainloop.la @TOOLS_TRUE@tools_ibeacon_SOURCES = tools/ibeacon.c monitor/bt.h @TOOLS_TRUE@tools_ibeacon_LDADD = src/libshared-mainloop.la @TOOLS_TRUE@tools_btgatt_client_SOURCES = tools/btgatt-client.c src/uuid-helper.c @TOOLS_TRUE@tools_btgatt_client_LDADD = src/libshared-mainloop.la \ @TOOLS_TRUE@ lib/libbluetooth-internal.la @TOOLS_TRUE@tools_btgatt_server_SOURCES = tools/btgatt-server.c src/uuid-helper.c @TOOLS_TRUE@tools_btgatt_server_LDADD = src/libshared-mainloop.la \ @TOOLS_TRUE@ lib/libbluetooth-internal.la @TOOLS_TRUE@tools_rctest_LDADD = lib/libbluetooth-internal.la @TOOLS_TRUE@tools_l2test_LDADD = lib/libbluetooth-internal.la @TOOLS_TRUE@tools_l2ping_LDADD = lib/libbluetooth-internal.la @TOOLS_TRUE@tools_bluemoon_SOURCES = tools/bluemoon.c monitor/bt.h @TOOLS_TRUE@tools_bluemoon_LDADD = src/libshared-mainloop.la @TOOLS_TRUE@tools_hex2hcd_SOURCES = tools/hex2hcd.c tools/missing.h @TOOLS_TRUE@tools_mpris_proxy_SOURCES = tools/mpris-proxy.c @TOOLS_TRUE@tools_mpris_proxy_LDADD = gdbus/libgdbus-internal.la $(GLIB_LIBS) $(DBUS_LIBS) @TOOLS_TRUE@tools_gatt_service_SOURCES = tools/gatt-service.c @TOOLS_TRUE@tools_gatt_service_LDADD = gdbus/libgdbus-internal.la \ @TOOLS_TRUE@ src/libshared-mainloop.la $(GLIB_LIBS) $(DBUS_LIBS) @TOOLS_TRUE@tools_isotest_LDADD = lib/libbluetooth-internal.la @TOOLS_TRUE@profiles_iap_iapd_SOURCES = profiles/iap/main.c @TOOLS_TRUE@profiles_iap_iapd_LDADD = gdbus/libgdbus-internal.la $(GLIB_LIBS) $(DBUS_LIBS) @DEPRECATED_TRUE@@MESH_TRUE@@TOOLS_TRUE@tools_meshctl_SOURCES = tools/meshctl.c \ @DEPRECATED_TRUE@@MESH_TRUE@@TOOLS_TRUE@ tools/mesh/agent.h tools/mesh/agent.c \ @DEPRECATED_TRUE@@MESH_TRUE@@TOOLS_TRUE@ tools/mesh/config-model.h\ @DEPRECATED_TRUE@@MESH_TRUE@@TOOLS_TRUE@ tools/mesh-gatt/mesh-net.h \ @DEPRECATED_TRUE@@MESH_TRUE@@TOOLS_TRUE@ tools/mesh-gatt/node.h tools/mesh-gatt/node.c \ @DEPRECATED_TRUE@@MESH_TRUE@@TOOLS_TRUE@ tools/mesh-gatt/gatt.h tools/mesh-gatt/gatt.c \ @DEPRECATED_TRUE@@MESH_TRUE@@TOOLS_TRUE@ tools/mesh-gatt/crypto.h\ @DEPRECATED_TRUE@@MESH_TRUE@@TOOLS_TRUE@ tools/mesh-gatt/crypto.c \ @DEPRECATED_TRUE@@MESH_TRUE@@TOOLS_TRUE@ tools/mesh-gatt/keys.h \ @DEPRECATED_TRUE@@MESH_TRUE@@TOOLS_TRUE@ tools/mesh-gatt/net.h tools/mesh-gatt/net.c \ @DEPRECATED_TRUE@@MESH_TRUE@@TOOLS_TRUE@ tools/mesh-gatt/prov.h tools/mesh-gatt/prov.c \ @DEPRECATED_TRUE@@MESH_TRUE@@TOOLS_TRUE@ tools/mesh-gatt/util.h tools/mesh-gatt/util.c \ @DEPRECATED_TRUE@@MESH_TRUE@@TOOLS_TRUE@ tools/mesh-gatt/prov-db.h \ @DEPRECATED_TRUE@@MESH_TRUE@@TOOLS_TRUE@ tools/mesh-gatt/prov-db.c \ @DEPRECATED_TRUE@@MESH_TRUE@@TOOLS_TRUE@ tools/mesh-gatt/config-client.c \ @DEPRECATED_TRUE@@MESH_TRUE@@TOOLS_TRUE@ tools/mesh-gatt/config-server.c \ @DEPRECATED_TRUE@@MESH_TRUE@@TOOLS_TRUE@ tools/mesh-gatt/onoff-model.h \ @DEPRECATED_TRUE@@MESH_TRUE@@TOOLS_TRUE@ tools/mesh-gatt/onoff-model.c @DEPRECATED_TRUE@@MESH_TRUE@@TOOLS_TRUE@tools_meshctl_LDADD = gdbus/libgdbus-internal.la src/libshared-glib.la \ @DEPRECATED_TRUE@@MESH_TRUE@@TOOLS_TRUE@ lib/libbluetooth-internal.la \ @DEPRECATED_TRUE@@MESH_TRUE@@TOOLS_TRUE@ $(GLIB_LIBS) $(DBUS_LIBS) -ljson-c -lreadline @MESH_TRUE@@TOOLS_TRUE@tools_mesh_cfgclient_SOURCES = tools/mesh-cfgclient.c \ @MESH_TRUE@@TOOLS_TRUE@ tools/mesh/model.h tools/mesh/config-model.h \ @MESH_TRUE@@TOOLS_TRUE@ tools/mesh/cfgcli.h tools/mesh/cfgcli.c \ @MESH_TRUE@@TOOLS_TRUE@ tools/mesh/keys.h tools/mesh/keys.c \ @MESH_TRUE@@TOOLS_TRUE@ tools/mesh/util.h tools/mesh/util.c \ @MESH_TRUE@@TOOLS_TRUE@ tools/mesh/remote.h tools/mesh/remote.c \ @MESH_TRUE@@TOOLS_TRUE@ tools/mesh/agent.h tools/mesh/agent.c \ @MESH_TRUE@@TOOLS_TRUE@ tools/mesh/mesh-db.h tools/mesh/mesh-db.c \ @MESH_TRUE@@TOOLS_TRUE@ mesh/util.h mesh/util.c \ @MESH_TRUE@@TOOLS_TRUE@ mesh/crypto.h mesh/crypto.c @MESH_TRUE@@TOOLS_TRUE@tools_mesh_cfgclient_LDADD = lib/libbluetooth-internal.la src/libshared-ell.la \ @MESH_TRUE@@TOOLS_TRUE@ $(ell_ldadd) -ljson-c -lreadline @MESH_TRUE@@TOOLS_TRUE@tools_mesh_cfgtest_SOURCES = tools/mesh-cfgtest.c @MESH_TRUE@@TOOLS_TRUE@tools_mesh_cfgtest_LDADD = lib/libbluetooth-internal.la src/libshared-ell.la \ @MESH_TRUE@@TOOLS_TRUE@ $(ell_ldadd) @DEPRECATED_TRUE@@TOOLS_TRUE@tools_hciattach_SOURCES = tools/hciattach.c tools/hciattach.h \ @DEPRECATED_TRUE@@TOOLS_TRUE@ tools/hciattach_st.c \ @DEPRECATED_TRUE@@TOOLS_TRUE@ tools/hciattach_ti.c \ @DEPRECATED_TRUE@@TOOLS_TRUE@ tools/hciattach_tialt.c \ @DEPRECATED_TRUE@@TOOLS_TRUE@ tools/hciattach_ath3k.c \ @DEPRECATED_TRUE@@TOOLS_TRUE@ tools/hciattach_qualcomm.c \ @DEPRECATED_TRUE@@TOOLS_TRUE@ tools/hciattach_intel.c \ @DEPRECATED_TRUE@@TOOLS_TRUE@ tools/hciattach_bcm43xx.c @DEPRECATED_TRUE@@TOOLS_TRUE@tools_hciattach_LDADD = lib/libbluetooth-internal.la @DEPRECATED_TRUE@@TOOLS_TRUE@tools_hciconfig_SOURCES = tools/hciconfig.c @DEPRECATED_TRUE@@TOOLS_TRUE@tools_hciconfig_LDADD = lib/libbluetooth-internal.la @DEPRECATED_TRUE@@TOOLS_TRUE@tools_hcitool_SOURCES = tools/hcitool.c src/oui.h src/oui.c @DEPRECATED_TRUE@@TOOLS_TRUE@tools_hcitool_LDADD = lib/libbluetooth-internal.la $(UDEV_LIBS) @DEPRECATED_TRUE@@TOOLS_TRUE@tools_hcidump_SOURCES = tools/hcidump.c \ @DEPRECATED_TRUE@@TOOLS_TRUE@ tools/parser/parser.h tools/parser/parser.c \ @DEPRECATED_TRUE@@TOOLS_TRUE@ tools/parser/lmp.c \ @DEPRECATED_TRUE@@TOOLS_TRUE@ tools/parser/hci.c \ @DEPRECATED_TRUE@@TOOLS_TRUE@ tools/parser/l2cap.h tools/parser/l2cap.c \ @DEPRECATED_TRUE@@TOOLS_TRUE@ tools/parser/smp.c \ @DEPRECATED_TRUE@@TOOLS_TRUE@ tools/parser/att.c \ @DEPRECATED_TRUE@@TOOLS_TRUE@ tools/parser/sdp.h tools/parser/sdp.c \ @DEPRECATED_TRUE@@TOOLS_TRUE@ tools/parser/rfcomm.h tools/parser/rfcomm.c \ @DEPRECATED_TRUE@@TOOLS_TRUE@ tools/parser/bnep.c \ @DEPRECATED_TRUE@@TOOLS_TRUE@ tools/parser/cmtp.c \ @DEPRECATED_TRUE@@TOOLS_TRUE@ tools/parser/hidp.c \ @DEPRECATED_TRUE@@TOOLS_TRUE@ tools/parser/hcrp.c \ @DEPRECATED_TRUE@@TOOLS_TRUE@ tools/parser/avdtp.c \ @DEPRECATED_TRUE@@TOOLS_TRUE@ tools/parser/avctp.c \ @DEPRECATED_TRUE@@TOOLS_TRUE@ tools/parser/avrcp.c \ @DEPRECATED_TRUE@@TOOLS_TRUE@ tools/parser/sap.c \ @DEPRECATED_TRUE@@TOOLS_TRUE@ tools/parser/obex.c \ @DEPRECATED_TRUE@@TOOLS_TRUE@ tools/parser/capi.c \ @DEPRECATED_TRUE@@TOOLS_TRUE@ tools/parser/ppp.c \ @DEPRECATED_TRUE@@TOOLS_TRUE@ tools/parser/tcpip.c \ @DEPRECATED_TRUE@@TOOLS_TRUE@ tools/parser/ericsson.c \ @DEPRECATED_TRUE@@TOOLS_TRUE@ tools/parser/csr.c \ @DEPRECATED_TRUE@@TOOLS_TRUE@ tools/parser/bpa.c @DEPRECATED_TRUE@@TOOLS_TRUE@tools_sdptool_SOURCES = tools/sdptool.c src/sdp-xml.h src/sdp-xml.c @DEPRECATED_TRUE@@TOOLS_TRUE@tools_sdptool_LDADD = lib/libbluetooth-internal.la $(GLIB_LIBS) @DEPRECATED_TRUE@@TOOLS_TRUE@tools_ciptool_LDADD = lib/libbluetooth-internal.la @DEPRECATED_TRUE@@TOOLS_TRUE@tools_hcidump_LDADD = lib/libbluetooth-internal.la @DEPRECATED_TRUE@@TOOLS_TRUE@tools_rfcomm_LDADD = lib/libbluetooth-internal.la @HID2HCI_TRUE@udevdir = $(UDEV_DIR) @HID2HCI_TRUE@tools_hid2hci_LDADD = $(UDEV_LIBS) @READLINE_TRUE@tools_obex_client_tool_SOURCES = $(gobex_sources) $(btio_sources) \ @READLINE_TRUE@ tools/obex-client-tool.c @READLINE_TRUE@tools_obex_client_tool_LDADD = lib/libbluetooth-internal.la \ @READLINE_TRUE@ src/libshared-glib.la $(GLIB_LIBS) -lreadline @READLINE_TRUE@tools_obex_server_tool_SOURCES = $(gobex_sources) $(btio_sources) \ @READLINE_TRUE@ tools/obex-server-tool.c @READLINE_TRUE@tools_obex_server_tool_LDADD = lib/libbluetooth-internal.la \ @READLINE_TRUE@ src/libshared-glib.la $(GLIB_LIBS) @READLINE_TRUE@tools_bluetooth_player_SOURCES = tools/bluetooth-player.c client/print.c \ @READLINE_TRUE@ client/player.c @READLINE_TRUE@tools_bluetooth_player_LDADD = gdbus/libgdbus-internal.la \ @READLINE_TRUE@ src/libshared-glib.la \ @READLINE_TRUE@ $(GLIB_LIBS) $(DBUS_LIBS) -lreadline @READLINE_TRUE@tools_obexctl_SOURCES = tools/obexctl.c @READLINE_TRUE@tools_obexctl_LDADD = gdbus/libgdbus-internal.la src/libshared-glib.la \ @READLINE_TRUE@ $(GLIB_LIBS) $(DBUS_LIBS) -lreadline @READLINE_TRUE@tools_btmgmt_SOURCES = tools/btmgmt.c src/uuid-helper.c client/display.c \ @READLINE_TRUE@ client/mgmt.c @READLINE_TRUE@tools_btmgmt_LDADD = lib/libbluetooth-internal.la src/libshared-mainloop.la \ @READLINE_TRUE@ -lreadline @DEPRECATED_TRUE@@READLINE_TRUE@attrib_gatttool_SOURCES = attrib/gatttool.c attrib/att.c attrib/gatt.c \ @DEPRECATED_TRUE@@READLINE_TRUE@ attrib/gattrib.c btio/btio.c \ @DEPRECATED_TRUE@@READLINE_TRUE@ attrib/gatttool.h attrib/interactive.c \ @DEPRECATED_TRUE@@READLINE_TRUE@ attrib/utils.c src/log.c client/display.c \ @DEPRECATED_TRUE@@READLINE_TRUE@ client/display.h @DEPRECATED_TRUE@@READLINE_TRUE@attrib_gatttool_LDADD = lib/libbluetooth-internal.la \ @DEPRECATED_TRUE@@READLINE_TRUE@ src/libshared-glib.la $(GLIB_LIBS) -lreadline @CUPS_SERVERBIN_FALSE@@CUPS_TRUE@cupsdir = $(libdir)/cups/backend @CUPS_SERVERBIN_TRUE@@CUPS_TRUE@cupsdir = $(CUPS_SERVERBIN)/backend @CUPS_TRUE@profiles_cups_bluetooth_SOURCES = profiles/cups/main.c \ @CUPS_TRUE@ profiles/cups/cups.h \ @CUPS_TRUE@ profiles/cups/sdp.c \ @CUPS_TRUE@ profiles/cups/spp.c \ @CUPS_TRUE@ profiles/cups/hcrp.c @CUPS_TRUE@profiles_cups_bluetooth_LDADD = $(GLIB_LIBS) $(DBUS_LIBS) \ @CUPS_TRUE@ lib/libbluetooth-internal.la \ @CUPS_TRUE@ gdbus/libgdbus-internal.la @BTPCLIENT_TRUE@tools_btpclient_SOURCES = tools/btpclient.c src/shared/btp.c src/shared/btp.h @BTPCLIENT_TRUE@tools_btpclient_LDADD = lib/libbluetooth-internal.la \ @BTPCLIENT_TRUE@ src/libshared-ell.la $(ell_ldadd) @BTPCLIENT_TRUE@tools_btpclientctl_SOURCES = tools/btpclientctl.c client/display.c @BTPCLIENT_TRUE@tools_btpclientctl_LDADD = src/libshared-mainloop.la src/libshared-glib.la \ @BTPCLIENT_TRUE@ lib/libbluetooth-internal.la -lreadline @OBEX_TRUE@@SYSTEMD_TRUE@dbussessionbusdir = $(DBUS_SESSIONBUSDIR) @OBEX_TRUE@@SYSTEMD_TRUE@dbussessionbus_DATA = obexd/src/org.bluez.obex.service @OBEX_TRUE@obex_plugindir = $(libdir)/obex/plugins @OBEX_TRUE@obexd_builtin_modules = filesystem bluetooth \ @OBEX_TRUE@ $(am__append_69) opp ftp irmc pbap mas mns @OBEX_TRUE@obexd_builtin_sources = obexd/plugins/filesystem.c \ @OBEX_TRUE@ obexd/plugins/filesystem.h \ @OBEX_TRUE@ obexd/plugins/bluetooth.c $(am__append_70) \ @OBEX_TRUE@ obexd/plugins/opp.c obexd/plugins/ftp.c \ @OBEX_TRUE@ obexd/plugins/ftp.h obexd/plugins/irmc.c \ @OBEX_TRUE@ obexd/plugins/pbap.c obexd/plugins/vcard.h \ @OBEX_TRUE@ obexd/plugins/vcard.c obexd/plugins/phonebook.h \ @OBEX_TRUE@ obexd/plugins/phonebook-@PLUGIN_PHONEBOOK@.c \ @OBEX_TRUE@ obexd/plugins/mas.c obexd/src/map_ap.h \ @OBEX_TRUE@ obexd/plugins/messages.h \ @OBEX_TRUE@ obexd/plugins/messages-dummy.c obexd/client/mns.c \ @OBEX_TRUE@ obexd/src/map_ap.h obexd/client/map-event.h @OBEX_TRUE@obexd_builtin_nodist = @OBEX_TRUE@obexd_src_obexd_SOURCES = $(btio_sources) $(gobex_sources) \ @OBEX_TRUE@ $(obexd_builtin_sources) \ @OBEX_TRUE@ obexd/src/main.c obexd/src/obexd.h \ @OBEX_TRUE@ obexd/src/plugin.h obexd/src/plugin.c \ @OBEX_TRUE@ obexd/src/log.h obexd/src/log.c \ @OBEX_TRUE@ obexd/src/manager.h obexd/src/manager.c \ @OBEX_TRUE@ obexd/src/obex.h obexd/src/obex.c obexd/src/obex-priv.h \ @OBEX_TRUE@ obexd/src/mimetype.h obexd/src/mimetype.c \ @OBEX_TRUE@ obexd/src/service.h obexd/src/service.c \ @OBEX_TRUE@ obexd/src/transport.h obexd/src/transport.c \ @OBEX_TRUE@ obexd/src/server.h obexd/src/server.c \ @OBEX_TRUE@ obexd/client/manager.h obexd/client/manager.c \ @OBEX_TRUE@ obexd/client/session.h obexd/client/session.c \ @OBEX_TRUE@ obexd/client/bluetooth.h obexd/client/bluetooth.c \ @OBEX_TRUE@ obexd/client/sync.h obexd/client/sync.c \ @OBEX_TRUE@ obexd/client/pbap.h obexd/client/pbap.c \ @OBEX_TRUE@ obexd/client/ftp.h obexd/client/ftp.c \ @OBEX_TRUE@ obexd/client/opp.h obexd/client/opp.c \ @OBEX_TRUE@ obexd/client/map.h obexd/client/map.c \ @OBEX_TRUE@ obexd/client/bip.h obexd/client/bip.c \ @OBEX_TRUE@ obexd/client/bip-common.h obexd/client/bip-common.c \ @OBEX_TRUE@ obexd/client/map-event.h obexd/client/map-event.c \ @OBEX_TRUE@ obexd/client/transfer.h obexd/client/transfer.c \ @OBEX_TRUE@ obexd/client/transport.h obexd/client/transport.c \ @OBEX_TRUE@ obexd/client/driver.h obexd/client/driver.c \ @OBEX_TRUE@ obexd/src/map_ap.h @OBEX_TRUE@obexd_src_obexd_LDADD = lib/libbluetooth-internal.la \ @OBEX_TRUE@ gdbus/libgdbus-internal.la \ @OBEX_TRUE@ src/libshared-glib.la \ @OBEX_TRUE@ $(ICAL_LIBS) $(DBUS_LIBS) $(LIBEBOOK_LIBS) \ @OBEX_TRUE@ $(LIBEDATASERVER_LIBS) $(GLIB_LIBS) -ldl @EXTERNAL_PLUGINS_TRUE@@OBEX_TRUE@obexd_src_obexd_LDFLAGS = $(AM_LDFLAGS) -Wl,--export-dynamic @OBEX_TRUE@obexd_src_obexd_CPPFLAGS = $(AM_CPPFLAGS) $(GLIB_CFLAGS) $(DBUS_CFLAGS) \ @OBEX_TRUE@ $(ICAL_CFLAGS) -DOBEX_PLUGIN_BUILTIN \ @OBEX_TRUE@ -DPLUGINDIR=\""$(obex_plugindir)"\" \ @OBEX_TRUE@ -D_FILE_OFFSET_BITS=64 \ @OBEX_TRUE@ -I$(builddir)/obexd/src obexd_src_obexd_SHORTNAME = obexd obexd_builtin_files = obexd/src/builtin.h $(obexd_builtin_nodist) nodist_obexd_src_obexd_SOURCES = $(obexd_builtin_files) @ANDROID_TRUE@android_plugindir = $(abs_top_srcdir)/android/.libs @ANDROID_TRUE@android_system_emulator_SOURCES = android/system-emulator.c @ANDROID_TRUE@android_system_emulator_LDADD = src/libshared-mainloop.la @ANDROID_TRUE@android_bluetoothd_snoop_SOURCES = android/bluetoothd-snoop.c src/log.c @ANDROID_TRUE@android_bluetoothd_snoop_LDADD = src/libshared-mainloop.la $(GLIB_LIBS) @ANDROID_TRUE@android_bluetoothd_SOURCES = android/main.c \ @ANDROID_TRUE@ src/log.c \ @ANDROID_TRUE@ android/hal-msg.h \ @ANDROID_TRUE@ android/audio-msg.h \ @ANDROID_TRUE@ android/sco-msg.h \ @ANDROID_TRUE@ android/utils.h \ @ANDROID_TRUE@ src/sdpd-database.c src/sdpd-server.c \ @ANDROID_TRUE@ src/sdpd-service.c src/sdpd-request.c \ @ANDROID_TRUE@ src/uuid-helper.h src/uuid-helper.c \ @ANDROID_TRUE@ src/eir.h src/eir.c \ @ANDROID_TRUE@ android/bluetooth.h android/bluetooth.c \ @ANDROID_TRUE@ android/hidhost.h android/hidhost.c \ @ANDROID_TRUE@ profiles/scanparam/scpp.h \ @ANDROID_TRUE@ profiles/scanparam/scpp.c \ @ANDROID_TRUE@ profiles/deviceinfo/dis.h \ @ANDROID_TRUE@ profiles/deviceinfo/dis.c \ @ANDROID_TRUE@ profiles/battery/bas.h profiles/battery/bas.c \ @ANDROID_TRUE@ profiles/input/hog-lib.h \ @ANDROID_TRUE@ profiles/input/hog-lib.c \ @ANDROID_TRUE@ android/ipc-common.h \ @ANDROID_TRUE@ android/ipc.h android/ipc.c \ @ANDROID_TRUE@ android/avdtp.h android/avdtp.c \ @ANDROID_TRUE@ android/a2dp.h android/a2dp.c \ @ANDROID_TRUE@ android/a2dp-sink.h android/a2dp-sink.c \ @ANDROID_TRUE@ android/avctp.h android/avctp.c \ @ANDROID_TRUE@ android/avrcp.h android/avrcp.c \ @ANDROID_TRUE@ android/avrcp-lib.h android/avrcp-lib.c \ @ANDROID_TRUE@ android/socket.h android/socket.c \ @ANDROID_TRUE@ android/sco.h android/sco.c \ @ANDROID_TRUE@ android/pan.h android/pan.c \ @ANDROID_TRUE@ android/handsfree.h android/handsfree.c \ @ANDROID_TRUE@ android/handsfree-client.c android/handsfree-client.h \ @ANDROID_TRUE@ android/gatt.h android/gatt.c \ @ANDROID_TRUE@ android/health.h android/health.c \ @ANDROID_TRUE@ profiles/health/mcap.h profiles/health/mcap.c \ @ANDROID_TRUE@ android/map-client.h android/map-client.c \ @ANDROID_TRUE@ attrib/att.c attrib/att.h \ @ANDROID_TRUE@ attrib/gatt.c attrib/gatt.h \ @ANDROID_TRUE@ attrib/gattrib.c attrib/gattrib.h \ @ANDROID_TRUE@ btio/btio.h btio/btio.c \ @ANDROID_TRUE@ src/sdp-client.h src/sdp-client.c \ @ANDROID_TRUE@ profiles/network/bnep.h profiles/network/bnep.c @ANDROID_TRUE@android_bluetoothd_LDADD = lib/libbluetooth-internal.la \ @ANDROID_TRUE@ src/libshared-glib.la $(GLIB_LIBS) @ANDROID_TRUE@android_bluetooth_default_la_SOURCES = android/hal.h android/hal-bluetooth.c \ @ANDROID_TRUE@ android/hal-socket.c \ @ANDROID_TRUE@ android/hal-hidhost.c \ @ANDROID_TRUE@ android/hal-health.c \ @ANDROID_TRUE@ android/hal-pan.c \ @ANDROID_TRUE@ android/hal-a2dp.c \ @ANDROID_TRUE@ android/hal-a2dp-sink.c \ @ANDROID_TRUE@ android/hal-avrcp.c \ @ANDROID_TRUE@ android/hal-avrcp-ctrl.c \ @ANDROID_TRUE@ android/hal-handsfree.c \ @ANDROID_TRUE@ android/hal-handsfree-client.c \ @ANDROID_TRUE@ android/hal-gatt.c \ @ANDROID_TRUE@ android/hal-map-client.c \ @ANDROID_TRUE@ android/hardware/bluetooth.h \ @ANDROID_TRUE@ android/hardware/bt_av.h \ @ANDROID_TRUE@ android/hardware/bt_gatt.h \ @ANDROID_TRUE@ android/hardware/bt_gatt_client.h \ @ANDROID_TRUE@ android/hardware/bt_gatt_server.h \ @ANDROID_TRUE@ android/hardware/bt_gatt_types.h \ @ANDROID_TRUE@ android/hardware/bt_hf.h \ @ANDROID_TRUE@ android/hardware/bt_hh.h \ @ANDROID_TRUE@ android/hardware/bt_hl.h \ @ANDROID_TRUE@ android/hardware/bt_pan.h \ @ANDROID_TRUE@ android/hardware/bt_rc.h \ @ANDROID_TRUE@ android/hardware/bt_sock.h \ @ANDROID_TRUE@ android/hardware/bt_hf_client.h \ @ANDROID_TRUE@ android/hardware/bt_mce.h \ @ANDROID_TRUE@ android/hardware/hardware.h \ @ANDROID_TRUE@ android/cutils/properties.h \ @ANDROID_TRUE@ android/ipc-common.h \ @ANDROID_TRUE@ android/hal-log.h \ @ANDROID_TRUE@ android/hal-ipc.h android/hal-ipc.c \ @ANDROID_TRUE@ android/hal-utils.h android/hal-utils.c @ANDROID_TRUE@android_bluetooth_default_la_CFLAGS = $(AM_CFLAGS) -fvisibility=hidden @ANDROID_TRUE@android_bluetooth_default_la_CPPFLAGS = $(AM_CPPFLAGS) -I$(srcdir)/android @ANDROID_TRUE@android_bluetooth_default_la_LDFLAGS = $(AM_LDFLAGS) -module -avoid-version \ @ANDROID_TRUE@ -no-undefined @ANDROID_TRUE@android_avdtptest_SOURCES = android/avdtptest.c \ @ANDROID_TRUE@ src/log.h src/log.c \ @ANDROID_TRUE@ btio/btio.h btio/btio.c \ @ANDROID_TRUE@ src/shared/util.h src/shared/util.c \ @ANDROID_TRUE@ src/shared/queue.h src/shared/queue.c \ @ANDROID_TRUE@ src/shared/log.h src/shared/log.c \ @ANDROID_TRUE@ android/avdtp.h android/avdtp.c @ANDROID_TRUE@android_avdtptest_CFLAGS = $(AM_CFLAGS) @ANDROID_TRUE@android_avdtptest_LDADD = lib/libbluetooth-internal.la $(GLIB_LIBS) @ANDROID_TRUE@android_haltest_SOURCES = android/client/haltest.c \ @ANDROID_TRUE@ android/client/pollhandler.h \ @ANDROID_TRUE@ android/client/pollhandler.c \ @ANDROID_TRUE@ android/client/terminal.h \ @ANDROID_TRUE@ android/client/terminal.c \ @ANDROID_TRUE@ android/client/history.h \ @ANDROID_TRUE@ android/client/history.c \ @ANDROID_TRUE@ android/client/tabcompletion.c \ @ANDROID_TRUE@ android/client/if-main.h \ @ANDROID_TRUE@ android/client/if-av.c \ @ANDROID_TRUE@ android/client/if-av-sink.c \ @ANDROID_TRUE@ android/client/if-rc.c \ @ANDROID_TRUE@ android/client/if-rc-ctrl.c \ @ANDROID_TRUE@ android/client/if-bt.c \ @ANDROID_TRUE@ android/client/if-gatt.c \ @ANDROID_TRUE@ android/client/if-hf.c \ @ANDROID_TRUE@ android/client/if-hf-client.c \ @ANDROID_TRUE@ android/client/if-hh.c \ @ANDROID_TRUE@ android/client/if-pan.c \ @ANDROID_TRUE@ android/client/if-hl.c \ @ANDROID_TRUE@ android/client/if-sock.c \ @ANDROID_TRUE@ android/client/if-audio.c \ @ANDROID_TRUE@ android/client/if-sco.c \ @ANDROID_TRUE@ android/client/if-mce.c \ @ANDROID_TRUE@ android/hardware/hardware.c \ @ANDROID_TRUE@ android/hal-utils.h android/hal-utils.c @ANDROID_TRUE@android_haltest_CPPFLAGS = $(AM_CPPFLAGS) -I$(srcdir)/android \ @ANDROID_TRUE@ -DPLUGINDIR=\""$(android_plugindir)"\" @ANDROID_TRUE@android_haltest_LDFLAGS = $(AM_LDFLAGS) -pthread @ANDROID_TRUE@android_haltest_LDADD = -ldl -lm @ANDROID_TRUE@android_android_tester_SOURCES = emulator/hciemu.h emulator/hciemu.c \ @ANDROID_TRUE@ emulator/vhci.h emulator/vhci.c \ @ANDROID_TRUE@ emulator/btdev.h emulator/btdev.c \ @ANDROID_TRUE@ emulator/bthost.h emulator/bthost.c \ @ANDROID_TRUE@ emulator/smp.c \ @ANDROID_TRUE@ monitor/rfcomm.h \ @ANDROID_TRUE@ android/hardware/hardware.c \ @ANDROID_TRUE@ android/tester-bluetooth.c \ @ANDROID_TRUE@ android/tester-socket.c \ @ANDROID_TRUE@ android/tester-hidhost.c \ @ANDROID_TRUE@ android/tester-pan.c \ @ANDROID_TRUE@ android/tester-hdp.c \ @ANDROID_TRUE@ android/tester-a2dp.c \ @ANDROID_TRUE@ android/tester-avrcp.c \ @ANDROID_TRUE@ android/tester-gatt.c \ @ANDROID_TRUE@ android/tester-map-client.c \ @ANDROID_TRUE@ android/tester-main.h android/tester-main.c @ANDROID_TRUE@android_android_tester_CPPFLAGS = $(AM_CPPFLAGS) -I$(srcdir)/android \ @ANDROID_TRUE@ -DPLUGINDIR=\""$(android_plugindir)"\" @ANDROID_TRUE@android_android_tester_LDADD = lib/libbluetooth-internal.la \ @ANDROID_TRUE@ src/libshared-glib.la $(GLIB_LIBS) -ldl @ANDROID_TRUE@android_android_tester_LDFLAGS = $(AM_LDFLAGS) -pthread @ANDROID_TRUE@android_ipc_tester_SOURCES = emulator/hciemu.h emulator/hciemu.c \ @ANDROID_TRUE@ emulator/vhci.h emulator/vhci.c \ @ANDROID_TRUE@ emulator/btdev.h emulator/btdev.c \ @ANDROID_TRUE@ emulator/bthost.h emulator/bthost.c \ @ANDROID_TRUE@ emulator/smp.c \ @ANDROID_TRUE@ android/hal-utils.h android/hal-utils.c \ @ANDROID_TRUE@ android/ipc-common.h android/ipc-tester.c @ANDROID_TRUE@android_ipc_tester_CPPFLAGS = $(AM_CPPFLAGS) -I$(srcdir)/android @ANDROID_TRUE@android_ipc_tester_LDADD = lib/libbluetooth-internal.la \ @ANDROID_TRUE@ src/libshared-glib.la $(GLIB_LIBS) @ANDROID_TRUE@android_audio_a2dp_default_la_SOURCES = android/audio-msg.h \ @ANDROID_TRUE@ android/hal-msg.h \ @ANDROID_TRUE@ android/hal-audio.h \ @ANDROID_TRUE@ android/hal-audio.c \ @ANDROID_TRUE@ android/hal-audio-sbc.c \ @ANDROID_TRUE@ android/hal-audio-aptx.c \ @ANDROID_TRUE@ android/hardware/audio.h \ @ANDROID_TRUE@ android/hardware/audio_effect.h \ @ANDROID_TRUE@ android/hardware/hardware.h \ @ANDROID_TRUE@ android/system/audio.h @ANDROID_TRUE@android_audio_a2dp_default_la_CFLAGS = $(AM_CFLAGS) -fvisibility=hidden @ANDROID_TRUE@android_audio_a2dp_default_la_CPPFLAGS = $(AM_CPPFLAGS) -I$(srcdir)/android \ @ANDROID_TRUE@ $(SBC_CFLAGS) @ANDROID_TRUE@android_audio_a2dp_default_la_LIBADD = $(SBC_LIBS) -lrt @ANDROID_TRUE@android_audio_a2dp_default_la_LDFLAGS = $(AM_LDFLAGS) -module -avoid-version \ @ANDROID_TRUE@ -no-undefined -pthread @ANDROID_TRUE@android_audio_sco_default_la_SOURCES = android/hal-log.h \ @ANDROID_TRUE@ android/sco-msg.h \ @ANDROID_TRUE@ android/hal-sco.c \ @ANDROID_TRUE@ android/hardware/audio.h \ @ANDROID_TRUE@ android/hardware/audio_effect.h \ @ANDROID_TRUE@ android/hardware/hardware.h \ @ANDROID_TRUE@ android/audio_utils/resampler.c \ @ANDROID_TRUE@ android/audio_utils/resampler.h \ @ANDROID_TRUE@ android/system/audio.h @ANDROID_TRUE@android_audio_sco_default_la_CFLAGS = $(AM_CFLAGS) -fvisibility=hidden @ANDROID_TRUE@android_audio_sco_default_la_CPPFLAGS = $(AM_CPPFLAGS) -I$(srcdir)/android @ANDROID_TRUE@android_audio_sco_default_la_LIBADD = $(SPEEXDSP_LIBS) -lrt @ANDROID_TRUE@android_audio_sco_default_la_LDFLAGS = $(AM_LDFLAGS) -module -avoid-version \ @ANDROID_TRUE@ -no-undefined @ANDROID_TRUE@android_test_ipc_SOURCES = android/test-ipc.c \ @ANDROID_TRUE@ src/log.h src/log.c \ @ANDROID_TRUE@ android/ipc-common.h \ @ANDROID_TRUE@ android/ipc.c android/ipc.h @ANDROID_TRUE@android_test_ipc_LDADD = src/libshared-glib.la $(GLIB_LIBS) @MESH_TRUE@mesh_sources = mesh/mesh.h mesh/mesh.c \ @MESH_TRUE@ mesh/net-keys.h mesh/net-keys.c \ @MESH_TRUE@ mesh/mesh-io.h mesh/mesh-io.c \ @MESH_TRUE@ mesh/mesh-mgmt.h mesh/mesh-mgmt.c \ @MESH_TRUE@ mesh/error.h mesh/mesh-io-api.h \ @MESH_TRUE@ mesh/mesh-io-unit.h mesh/mesh-io-unit.c \ @MESH_TRUE@ mesh/mesh-io-mgmt.h mesh/mesh-io-mgmt.c \ @MESH_TRUE@ mesh/mesh-io-generic.h mesh/mesh-io-generic.c \ @MESH_TRUE@ mesh/net.h mesh/net.c \ @MESH_TRUE@ mesh/crypto.h mesh/crypto.c \ @MESH_TRUE@ mesh/friend.h mesh/friend.c \ @MESH_TRUE@ mesh/appkey.h mesh/appkey.c \ @MESH_TRUE@ mesh/node.h mesh/node.c \ @MESH_TRUE@ mesh/provision.h mesh/prov.h \ @MESH_TRUE@ mesh/model.h mesh/model.c \ @MESH_TRUE@ mesh/cfgmod.h mesh/cfgmod-server.c \ @MESH_TRUE@ mesh/remprv.h mesh/remprv-server.c \ @MESH_TRUE@ mesh/mesh-config.h mesh/mesh-config-json.c \ @MESH_TRUE@ mesh/util.h mesh/util.c \ @MESH_TRUE@ mesh/dbus.h mesh/dbus.c \ @MESH_TRUE@ mesh/agent.h mesh/agent.c \ @MESH_TRUE@ mesh/prov-acceptor.c mesh/prov-initiator.c \ @MESH_TRUE@ mesh/manager.h mesh/manager.c \ @MESH_TRUE@ mesh/pb-adv.h mesh/pb-adv.c \ @MESH_TRUE@ mesh/keyring.h mesh/keyring.c \ @MESH_TRUE@ mesh/rpl.h mesh/rpl.c \ @MESH_TRUE@ mesh/prv-beacon.h mesh/prvbeac-server.c \ @MESH_TRUE@ mesh/mesh-defs.h @MESH_TRUE@mesh_bluetooth_meshd_SOURCES = $(mesh_sources) mesh/main.c @MESH_TRUE@mesh_bluetooth_meshd_LDADD = src/libshared-ell.la $(ell_ldadd) -ljson-c @HID2HCI_TRUE@rulesdir = $(UDEV_DIR)/rules.d @HID2HCI_TRUE@rules_DATA = tools/97-hid2hci.rules @TEST_TRUE@testdir = $(pkglibdir)/test @TEST_TRUE@test_SCRIPTS = $(test_scripts) unit_test_tester_SOURCES = unit/test-tester.c unit_test_tester_LDADD = src/libshared-glib.la lib/libbluetooth-internal.la \ $(GLIB_LIBS) unit_test_eir_SOURCES = unit/test-eir.c src/eir.c src/uuid-helper.c unit_test_eir_LDADD = src/libshared-glib.la lib/libbluetooth-internal.la \ $(GLIB_LIBS) unit_test_uuid_SOURCES = unit/test-uuid.c unit_test_uuid_LDADD = src/libshared-glib.la lib/libbluetooth-internal.la \ $(GLIB_LIBS) unit_test_textfile_SOURCES = unit/test-textfile.c src/textfile.h src/textfile.c unit_test_textfile_LDADD = src/libshared-glib.la $(GLIB_LIBS) unit_test_crc_SOURCES = unit/test-crc.c monitor/crc.h monitor/crc.c unit_test_crc_LDADD = src/libshared-glib.la $(GLIB_LIBS) unit_test_crypto_SOURCES = unit/test-crypto.c unit_test_crypto_LDADD = src/libshared-glib.la $(GLIB_LIBS) unit_test_ecc_SOURCES = unit/test-ecc.c unit_test_ecc_LDADD = src/libshared-glib.la $(GLIB_LIBS) unit_test_ringbuf_SOURCES = unit/test-ringbuf.c unit_test_ringbuf_LDADD = src/libshared-glib.la $(GLIB_LIBS) unit_test_queue_SOURCES = unit/test-queue.c unit_test_queue_LDADD = src/libshared-glib.la $(GLIB_LIBS) unit_test_mgmt_SOURCES = unit/test-mgmt.c unit_test_mgmt_LDADD = src/libshared-glib.la $(GLIB_LIBS) unit_test_uhid_SOURCES = unit/test-uhid.c unit_test_uhid_LDADD = src/libshared-glib.la $(GLIB_LIBS) unit_test_sdp_SOURCES = unit/test-sdp.c \ src/sdpd.h src/sdpd-database.c \ src/log.h src/log.c \ src/sdpd-service.c src/sdpd-request.c unit_test_sdp_LDADD = lib/libbluetooth-internal.la \ src/libshared-glib.la $(GLIB_LIBS) unit_test_avdtp_SOURCES = unit/test-avdtp.c \ src/log.h src/log.c \ android/avdtp.c android/avdtp.h unit_test_avdtp_LDADD = src/libshared-glib.la $(GLIB_LIBS) unit_test_avctp_SOURCES = unit/test-avctp.c \ src/log.h src/log.c \ android/avctp.c android/avctp.h unit_test_avctp_LDADD = src/libshared-glib.la $(GLIB_LIBS) unit_test_avrcp_SOURCES = unit/test-avrcp.c \ src/log.h src/log.c \ android/avctp.c android/avctp.h \ android/avrcp-lib.c android/avrcp-lib.h unit_test_avrcp_LDADD = lib/libbluetooth-internal.la \ src/libshared-glib.la $(GLIB_LIBS) unit_test_hfp_SOURCES = unit/test-hfp.c unit_test_hfp_LDADD = src/libshared-glib.la $(GLIB_LIBS) unit_test_gdbus_client_SOURCES = unit/test-gdbus-client.c unit_test_gdbus_client_LDADD = gdbus/libgdbus-internal.la \ src/libshared-glib.la $(GLIB_LIBS) $(DBUS_LIBS) @OBEX_TRUE@unit_test_gobex_SOURCES = $(gobex_sources) unit/util.c unit/util.h \ @OBEX_TRUE@ unit/test-gobex.c @OBEX_TRUE@unit_test_gobex_LDADD = src/libshared-glib.la $(GLIB_LIBS) @OBEX_TRUE@unit_test_gobex_packet_SOURCES = $(gobex_sources) unit/util.c unit/util.h \ @OBEX_TRUE@ unit/test-gobex-packet.c @OBEX_TRUE@unit_test_gobex_packet_LDADD = src/libshared-glib.la $(GLIB_LIBS) @OBEX_TRUE@unit_test_gobex_header_SOURCES = $(gobex_sources) unit/util.c unit/util.h \ @OBEX_TRUE@ unit/test-gobex-header.c @OBEX_TRUE@unit_test_gobex_header_LDADD = src/libshared-glib.la $(GLIB_LIBS) @OBEX_TRUE@unit_test_gobex_transfer_SOURCES = $(gobex_sources) unit/util.c unit/util.h \ @OBEX_TRUE@ unit/test-gobex-transfer.c @OBEX_TRUE@unit_test_gobex_transfer_LDADD = src/libshared-glib.la $(GLIB_LIBS) @OBEX_TRUE@unit_test_gobex_apparam_SOURCES = $(gobex_sources) unit/util.c unit/util.h \ @OBEX_TRUE@ unit/test-gobex-apparam.c @OBEX_TRUE@unit_test_gobex_apparam_LDADD = src/libshared-glib.la $(GLIB_LIBS) unit_test_lib_SOURCES = unit/test-lib.c unit_test_lib_LDADD = src/libshared-glib.la \ lib/libbluetooth-internal.la $(GLIB_LIBS) unit_test_gatt_SOURCES = unit/test-gatt.c unit_test_gatt_LDADD = src/libshared-glib.la \ lib/libbluetooth-internal.la $(GLIB_LIBS) unit_test_hog_SOURCES = unit/test-hog.c \ $(btio_sources) \ profiles/input/hog-lib.h profiles/input/hog-lib.c \ profiles/scanparam/scpp.h profiles/scanparam/scpp.c \ profiles/battery/bas.h profiles/battery/bas.c \ profiles/deviceinfo/dis.h profiles/deviceinfo/dis.c \ src/log.h src/log.c \ attrib/att.h attrib/att.c \ attrib/gatt.h attrib/gatt.c \ attrib/gattrib.h attrib/gattrib.c unit_test_hog_LDADD = src/libshared-glib.la \ lib/libbluetooth-internal.la $(GLIB_LIBS) unit_test_gattrib_SOURCES = unit/test-gattrib.c attrib/gattrib.c \ $(btio_sources) src/log.h src/log.c unit_test_gattrib_LDADD = src/libshared-glib.la \ lib/libbluetooth-internal.la \ $(GLIB_LIBS) $(DBUS_LIBS) -ldl -lrt unit_test_bap_SOURCES = unit/test-bap.c unit_test_bap_LDADD = src/libshared-glib.la \ lib/libbluetooth-internal.la $(GLIB_LIBS) unit_test_micp_SOURCES = unit/test-micp.c unit_test_micp_LDADD = src/libshared-glib.la \ lib/libbluetooth-internal.la $(GLIB_LIBS) unit_test_bass_SOURCES = unit/test-bass.c $(btio_sources) unit_test_bass_LDADD = src/libshared-glib.la \ lib/libbluetooth-internal.la $(GLIB_LIBS) unit_test_vcp_SOURCES = unit/test-vcp.c $(btio_sources) unit_test_vcp_LDADD = src/libshared-glib.la \ lib/libbluetooth-internal.la $(GLIB_LIBS) @MIDI_TRUE@unit_test_midi_CPPFLAGS = $(AM_CPPFLAGS) $(ALSA_CFLAGS) -DMIDI_TEST @MIDI_TRUE@unit_test_midi_SOURCES = unit/test-midi.c \ @MIDI_TRUE@ profiles/midi/libmidi.h \ @MIDI_TRUE@ profiles/midi/libmidi.c @MIDI_TRUE@unit_test_midi_LDADD = src/libshared-glib.la \ @MIDI_TRUE@ $(GLIB_LIBS) $(ALSA_LIBS) @MESH_TRUE@unit_test_mesh_crypto_CPPFLAGS = $(ell_cflags) @MESH_TRUE@unit_test_mesh_crypto_SOURCES = unit/test-mesh-crypto.c \ @MESH_TRUE@ mesh/crypto.h ell/internal ell/ell.h @MESH_TRUE@unit_test_mesh_crypto_LDADD = $(ell_ldadd) AM_TESTS_ENVIRONMENT = MALLOC_CHECK_=3 MALLOC_PERTURB_=69 \ $(am__append_89) @VALGRIND_TRUE@LOG_COMPILER = valgrind --error-exitcode=1 --num-callers=30 @VALGRIND_TRUE@LOG_FLAGS = --trace-children=yes --leak-check=full --show-reachable=no \ @VALGRIND_TRUE@ --suppressions=$(srcdir)/tools/valgrind.supp --quiet pkgconfigdir = $(libdir)/pkgconfig @LIBRARY_TRUE@pkgconfig_DATA = lib/bluez.pc DISTCHECK_CONFIGURE_FLAGS = --disable-datafiles --enable-library \ --enable-health \ --enable-midi \ --enable-manpages \ --enable-android \ --enable-mesh \ --enable-btpclient \ --disable-systemd \ --disable-udev DISTCLEANFILES = $(pkgconfig_DATA) $(unit_tests) $(manual_pages) MAINTAINERCLEANFILES = Makefile.in \ aclocal.m4 configure config.h.in config.sub config.guess \ ltmain.sh depcomp compile missing install-sh mkinstalldirs test-driver @RUN_RST2MAN_FALSE@RST2MAN_PROCESS = $(AM_V_GEN)test -f $@ || \ @RUN_RST2MAN_FALSE@ { echo "Generated manual page $@ does not exist"; false; } @RUN_RST2MAN_TRUE@RST2MAN_PROCESS = $(AM_V_GEN)$(MKDIR_P) $(dir $@) && \ @RUN_RST2MAN_TRUE@ $(RST2MAN) --strict --no-raw \ @RUN_RST2MAN_TRUE@ --no-generator --no-datestamp $< $@ all: $(BUILT_SOURCES) config.h $(MAKE) $(AM_MAKEFLAGS) all-am .SUFFIXES: .SUFFIXES: .c .lo .log .o .obj .test .test$(EXEEXT) .trs am--refresh: Makefile @: $(srcdir)/Makefile.in: @MAINTAINER_MODE_TRUE@ $(srcdir)/Makefile.am $(srcdir)/Makefile.plugins $(srcdir)/Makefile.tools $(srcdir)/Makefile.obexd $(srcdir)/android/Makefile.am $(srcdir)/Makefile.mesh $(am__configure_deps) @for dep in $?; do \ case '$(am__configure_deps)' in \ *$$dep*) \ echo ' cd $(srcdir) && $(AUTOMAKE) --foreign'; \ $(am__cd) $(srcdir) && $(AUTOMAKE) --foreign \ && exit 0; \ exit 1;; \ esac; \ done; \ echo ' cd $(top_srcdir) && $(AUTOMAKE) --foreign Makefile'; \ $(am__cd) $(top_srcdir) && \ $(AUTOMAKE) --foreign Makefile Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status @case '$?' in \ *config.status*) \ echo ' $(SHELL) ./config.status'; \ $(SHELL) ./config.status;; \ *) \ echo ' cd $(top_builddir) && $(SHELL) ./config.status $@ $(am__maybe_remake_depfiles)'; \ cd $(top_builddir) && $(SHELL) ./config.status $@ $(am__maybe_remake_depfiles);; \ esac; $(srcdir)/Makefile.plugins $(srcdir)/Makefile.tools $(srcdir)/Makefile.obexd $(srcdir)/android/Makefile.am $(srcdir)/Makefile.mesh $(am__empty): $(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES) $(SHELL) ./config.status --recheck $(top_srcdir)/configure: @MAINTAINER_MODE_TRUE@ $(am__configure_deps) $(am__cd) $(srcdir) && $(AUTOCONF) $(ACLOCAL_M4): @MAINTAINER_MODE_TRUE@ $(am__aclocal_m4_deps) $(am__cd) $(srcdir) && $(ACLOCAL) $(ACLOCAL_AMFLAGS) $(am__aclocal_m4_deps): config.h: stamp-h1 @test -f $@ || rm -f stamp-h1 @test -f $@ || $(MAKE) $(AM_MAKEFLAGS) stamp-h1 stamp-h1: $(srcdir)/config.h.in $(top_builddir)/config.status @rm -f stamp-h1 cd $(top_builddir) && $(SHELL) ./config.status config.h $(srcdir)/config.h.in: @MAINTAINER_MODE_TRUE@ $(am__configure_deps) ($(am__cd) $(top_srcdir) && $(AUTOHEADER)) rm -f stamp-h1 touch $@ distclean-hdr: -rm -f config.h stamp-h1 lib/bluez.pc: $(top_builddir)/config.status $(top_srcdir)/lib/bluez.pc.in cd $(top_builddir) && $(SHELL) ./config.status $@ mesh/bluetooth-meshd.rst: $(top_builddir)/config.status $(top_srcdir)/mesh/bluetooth-meshd.rst.in cd $(top_builddir) && $(SHELL) ./config.status $@ mesh/bluetooth-mesh.service: $(top_builddir)/config.status $(top_srcdir)/mesh/bluetooth-mesh.service.in cd $(top_builddir) && $(SHELL) ./config.status $@ obexd/src/obex.service: $(top_builddir)/config.status $(top_srcdir)/obexd/src/obex.service.in cd $(top_builddir) && $(SHELL) ./config.status $@ obexd/src/org.bluez.obex.service: $(top_builddir)/config.status $(top_srcdir)/obexd/src/org.bluez.obex.service.in cd $(top_builddir) && $(SHELL) ./config.status $@ src/bluetoothd.rst: $(top_builddir)/config.status $(top_srcdir)/src/bluetoothd.rst.in cd $(top_builddir) && $(SHELL) ./config.status $@ src/bluetooth.service: $(top_builddir)/config.status $(top_srcdir)/src/bluetooth.service.in cd $(top_builddir) && $(SHELL) ./config.status $@ tools/bluetooth-logger.service: $(top_builddir)/config.status $(top_srcdir)/tools/bluetooth-logger.service.in cd $(top_builddir) && $(SHELL) ./config.status $@ tools/mpris-proxy.service: $(top_builddir)/config.status $(top_srcdir)/tools/mpris-proxy.service.in cd $(top_builddir) && $(SHELL) ./config.status $@ install-binPROGRAMS: $(bin_PROGRAMS) @$(NORMAL_INSTALL) @list='$(bin_PROGRAMS)'; test -n "$(bindir)" || list=; \ if test -n "$$list"; then \ echo " $(MKDIR_P) '$(DESTDIR)$(bindir)'"; \ $(MKDIR_P) "$(DESTDIR)$(bindir)" || exit 1; \ fi; \ for p in $$list; do echo "$$p $$p"; done | \ sed 's/$(EXEEXT)$$//' | \ while read p p1; do if test -f $$p \ || test -f $$p1 \ ; then echo "$$p"; echo "$$p"; else :; fi; \ done | \ sed -e 'p;s,.*/,,;n;h' \ -e 's|.*|.|' \ -e 'p;x;s,.*/,,;s/$(EXEEXT)$$//;$(transform);s/$$/$(EXEEXT)/' | \ sed 'N;N;N;s,\n, ,g' | \ $(AWK) 'BEGIN { files["."] = ""; dirs["."] = 1 } \ { d=$$3; if (dirs[d] != 1) { print "d", d; dirs[d] = 1 } \ if ($$2 == $$4) files[d] = files[d] " " $$1; \ else { print "f", $$3 "/" $$4, $$1; } } \ END { for (d in files) print "f", d, files[d] }' | \ while read type dir files; do \ if test "$$dir" = .; then dir=; else dir=/$$dir; fi; \ test -z "$$files" || { \ echo " $(INSTALL_PROGRAM_ENV) $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL_PROGRAM) $$files '$(DESTDIR)$(bindir)$$dir'"; \ $(INSTALL_PROGRAM_ENV) $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL_PROGRAM) $$files "$(DESTDIR)$(bindir)$$dir" || exit $$?; \ } \ ; done uninstall-binPROGRAMS: @$(NORMAL_UNINSTALL) @list='$(bin_PROGRAMS)'; test -n "$(bindir)" || list=; \ files=`for p in $$list; do echo "$$p"; done | \ sed -e 'h;s,^.*/,,;s/$(EXEEXT)$$//;$(transform)' \ -e 's/$$/$(EXEEXT)/' \ `; \ test -n "$$list" || exit 0; \ echo " ( cd '$(DESTDIR)$(bindir)' && rm -f" $$files ")"; \ cd "$(DESTDIR)$(bindir)" && rm -f $$files clean-binPROGRAMS: @list='$(bin_PROGRAMS)'; test -n "$$list" || exit 0; \ echo " rm -f" $$list; \ rm -f $$list || exit $$?; \ test -n "$(EXEEXT)" || exit 0; \ list=`for p in $$list; do echo "$$p"; done | sed 's/$(EXEEXT)$$//'`; \ echo " rm -f" $$list; \ rm -f $$list install-cupsPROGRAMS: $(cups_PROGRAMS) @$(NORMAL_INSTALL) @list='$(cups_PROGRAMS)'; test -n "$(cupsdir)" || list=; \ if test -n "$$list"; then \ echo " $(MKDIR_P) '$(DESTDIR)$(cupsdir)'"; \ $(MKDIR_P) "$(DESTDIR)$(cupsdir)" || exit 1; \ fi; \ for p in $$list; do echo "$$p $$p"; done | \ sed 's/$(EXEEXT)$$//' | \ while read p p1; do if test -f $$p \ || test -f $$p1 \ ; then echo "$$p"; echo "$$p"; else :; fi; \ done | \ sed -e 'p;s,.*/,,;n;h' \ -e 's|.*|.|' \ -e 'p;x;s,.*/,,;s/$(EXEEXT)$$//;$(transform);s/$$/$(EXEEXT)/' | \ sed 'N;N;N;s,\n, ,g' | \ $(AWK) 'BEGIN { files["."] = ""; dirs["."] = 1 } \ { d=$$3; if (dirs[d] != 1) { print "d", d; dirs[d] = 1 } \ if ($$2 == $$4) files[d] = files[d] " " $$1; \ else { print "f", $$3 "/" $$4, $$1; } } \ END { for (d in files) print "f", d, files[d] }' | \ while read type dir files; do \ if test "$$dir" = .; then dir=; else dir=/$$dir; fi; \ test -z "$$files" || { \ echo " $(INSTALL_PROGRAM_ENV) $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL_PROGRAM) $$files '$(DESTDIR)$(cupsdir)$$dir'"; \ $(INSTALL_PROGRAM_ENV) $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL_PROGRAM) $$files "$(DESTDIR)$(cupsdir)$$dir" || exit $$?; \ } \ ; done uninstall-cupsPROGRAMS: @$(NORMAL_UNINSTALL) @list='$(cups_PROGRAMS)'; test -n "$(cupsdir)" || list=; \ files=`for p in $$list; do echo "$$p"; done | \ sed -e 'h;s,^.*/,,;s/$(EXEEXT)$$//;$(transform)' \ -e 's/$$/$(EXEEXT)/' \ `; \ test -n "$$list" || exit 0; \ echo " ( cd '$(DESTDIR)$(cupsdir)' && rm -f" $$files ")"; \ cd "$(DESTDIR)$(cupsdir)" && rm -f $$files clean-cupsPROGRAMS: @list='$(cups_PROGRAMS)'; test -n "$$list" || exit 0; \ echo " rm -f" $$list; \ rm -f $$list || exit $$?; \ test -n "$(EXEEXT)" || exit 0; \ list=`for p in $$list; do echo "$$p"; done | sed 's/$(EXEEXT)$$//'`; \ echo " rm -f" $$list; \ rm -f $$list clean-noinstPROGRAMS: @list='$(noinst_PROGRAMS)'; test -n "$$list" || exit 0; \ echo " rm -f" $$list; \ rm -f $$list || exit $$?; \ test -n "$(EXEEXT)" || exit 0; \ list=`for p in $$list; do echo "$$p"; done | sed 's/$(EXEEXT)$$//'`; \ echo " rm -f" $$list; \ rm -f $$list install-pkglibexecPROGRAMS: $(pkglibexec_PROGRAMS) @$(NORMAL_INSTALL) @list='$(pkglibexec_PROGRAMS)'; test -n "$(pkglibexecdir)" || list=; \ if test -n "$$list"; then \ echo " $(MKDIR_P) '$(DESTDIR)$(pkglibexecdir)'"; \ $(MKDIR_P) "$(DESTDIR)$(pkglibexecdir)" || exit 1; \ fi; \ for p in $$list; do echo "$$p $$p"; done | \ sed 's/$(EXEEXT)$$//' | \ while read p p1; do if test -f $$p \ || test -f $$p1 \ ; then echo "$$p"; echo "$$p"; else :; fi; \ done | \ sed -e 'p;s,.*/,,;n;h' \ -e 's|.*|.|' \ -e 'p;x;s,.*/,,;s/$(EXEEXT)$$//;$(transform);s/$$/$(EXEEXT)/' | \ sed 'N;N;N;s,\n, ,g' | \ $(AWK) 'BEGIN { files["."] = ""; dirs["."] = 1 } \ { d=$$3; if (dirs[d] != 1) { print "d", d; dirs[d] = 1 } \ if ($$2 == $$4) files[d] = files[d] " " $$1; \ else { print "f", $$3 "/" $$4, $$1; } } \ END { for (d in files) print "f", d, files[d] }' | \ while read type dir files; do \ if test "$$dir" = .; then dir=; else dir=/$$dir; fi; \ test -z "$$files" || { \ echo " $(INSTALL_PROGRAM_ENV) $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL_PROGRAM) $$files '$(DESTDIR)$(pkglibexecdir)$$dir'"; \ $(INSTALL_PROGRAM_ENV) $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL_PROGRAM) $$files "$(DESTDIR)$(pkglibexecdir)$$dir" || exit $$?; \ } \ ; done uninstall-pkglibexecPROGRAMS: @$(NORMAL_UNINSTALL) @list='$(pkglibexec_PROGRAMS)'; test -n "$(pkglibexecdir)" || list=; \ files=`for p in $$list; do echo "$$p"; done | \ sed -e 'h;s,^.*/,,;s/$(EXEEXT)$$//;$(transform)' \ -e 's/$$/$(EXEEXT)/' \ `; \ test -n "$$list" || exit 0; \ echo " ( cd '$(DESTDIR)$(pkglibexecdir)' && rm -f" $$files ")"; \ cd "$(DESTDIR)$(pkglibexecdir)" && rm -f $$files clean-pkglibexecPROGRAMS: @list='$(pkglibexec_PROGRAMS)'; test -n "$$list" || exit 0; \ echo " rm -f" $$list; \ rm -f $$list || exit $$?; \ test -n "$(EXEEXT)" || exit 0; \ list=`for p in $$list; do echo "$$p"; done | sed 's/$(EXEEXT)$$//'`; \ echo " rm -f" $$list; \ rm -f $$list install-udevPROGRAMS: $(udev_PROGRAMS) @$(NORMAL_INSTALL) @list='$(udev_PROGRAMS)'; test -n "$(udevdir)" || list=; \ if test -n "$$list"; then \ echo " $(MKDIR_P) '$(DESTDIR)$(udevdir)'"; \ $(MKDIR_P) "$(DESTDIR)$(udevdir)" || exit 1; \ fi; \ for p in $$list; do echo "$$p $$p"; done | \ sed 's/$(EXEEXT)$$//' | \ while read p p1; do if test -f $$p \ || test -f $$p1 \ ; then echo "$$p"; echo "$$p"; else :; fi; \ done | \ sed -e 'p;s,.*/,,;n;h' \ -e 's|.*|.|' \ -e 'p;x;s,.*/,,;s/$(EXEEXT)$$//;$(transform);s/$$/$(EXEEXT)/' | \ sed 'N;N;N;s,\n, ,g' | \ $(AWK) 'BEGIN { files["."] = ""; dirs["."] = 1 } \ { d=$$3; if (dirs[d] != 1) { print "d", d; dirs[d] = 1 } \ if ($$2 == $$4) files[d] = files[d] " " $$1; \ else { print "f", $$3 "/" $$4, $$1; } } \ END { for (d in files) print "f", d, files[d] }' | \ while read type dir files; do \ if test "$$dir" = .; then dir=; else dir=/$$dir; fi; \ test -z "$$files" || { \ echo " $(INSTALL_PROGRAM_ENV) $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL_PROGRAM) $$files '$(DESTDIR)$(udevdir)$$dir'"; \ $(INSTALL_PROGRAM_ENV) $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL_PROGRAM) $$files "$(DESTDIR)$(udevdir)$$dir" || exit $$?; \ } \ ; done uninstall-udevPROGRAMS: @$(NORMAL_UNINSTALL) @list='$(udev_PROGRAMS)'; test -n "$(udevdir)" || list=; \ files=`for p in $$list; do echo "$$p"; done | \ sed -e 'h;s,^.*/,,;s/$(EXEEXT)$$//;$(transform)' \ -e 's/$$/$(EXEEXT)/' \ `; \ test -n "$$list" || exit 0; \ echo " ( cd '$(DESTDIR)$(udevdir)' && rm -f" $$files ")"; \ cd "$(DESTDIR)$(udevdir)" && rm -f $$files clean-udevPROGRAMS: @list='$(udev_PROGRAMS)'; test -n "$$list" || exit 0; \ echo " rm -f" $$list; \ rm -f $$list || exit $$?; \ test -n "$(EXEEXT)" || exit 0; \ list=`for p in $$list; do echo "$$p"; done | sed 's/$(EXEEXT)$$//'`; \ echo " rm -f" $$list; \ rm -f $$list clean-noinstLIBRARIES: -test -z "$(noinst_LIBRARIES)" || rm -f $(noinst_LIBRARIES) install-libLTLIBRARIES: $(lib_LTLIBRARIES) @$(NORMAL_INSTALL) @list='$(lib_LTLIBRARIES)'; test -n "$(libdir)" || list=; \ list2=; for p in $$list; do \ if test -f $$p; then \ list2="$$list2 $$p"; \ else :; fi; \ done; \ test -z "$$list2" || { \ echo " $(MKDIR_P) '$(DESTDIR)$(libdir)'"; \ $(MKDIR_P) "$(DESTDIR)$(libdir)" || exit 1; \ echo " $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL) $(INSTALL_STRIP_FLAG) $$list2 '$(DESTDIR)$(libdir)'"; \ $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL) $(INSTALL_STRIP_FLAG) $$list2 "$(DESTDIR)$(libdir)"; \ } uninstall-libLTLIBRARIES: @$(NORMAL_UNINSTALL) @list='$(lib_LTLIBRARIES)'; test -n "$(libdir)" || list=; \ for p in $$list; do \ $(am__strip_dir) \ echo " $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=uninstall rm -f '$(DESTDIR)$(libdir)/$$f'"; \ $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=uninstall rm -f "$(DESTDIR)$(libdir)/$$f"; \ done clean-libLTLIBRARIES: -test -z "$(lib_LTLIBRARIES)" || rm -f $(lib_LTLIBRARIES) @list='$(lib_LTLIBRARIES)'; \ locs=`for p in $$list; do echo $$p; done | \ sed 's|^[^/]*$$|.|; s|/[^/]*$$||; s|$$|/so_locations|' | \ sort -u`; \ test -z "$$locs" || { \ echo rm -f $${locs}; \ rm -f $${locs}; \ } clean-noinstLTLIBRARIES: -test -z "$(noinst_LTLIBRARIES)" || rm -f $(noinst_LTLIBRARIES) @list='$(noinst_LTLIBRARIES)'; \ locs=`for p in $$list; do echo $$p; done | \ sed 's|^[^/]*$$|.|; s|/[^/]*$$||; s|$$|/so_locations|' | \ sort -u`; \ test -z "$$locs" || { \ echo rm -f $${locs}; \ rm -f $${locs}; \ } install-pluginLTLIBRARIES: $(plugin_LTLIBRARIES) @$(NORMAL_INSTALL) @list='$(plugin_LTLIBRARIES)'; test -n "$(plugindir)" || list=; \ list2=; for p in $$list; do \ if test -f $$p; then \ list2="$$list2 $$p"; \ else :; fi; \ done; \ test -z "$$list2" || { \ echo " $(MKDIR_P) '$(DESTDIR)$(plugindir)'"; \ $(MKDIR_P) "$(DESTDIR)$(plugindir)" || exit 1; \ echo " $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL) $(INSTALL_STRIP_FLAG) $$list2 '$(DESTDIR)$(plugindir)'"; \ $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL) $(INSTALL_STRIP_FLAG) $$list2 "$(DESTDIR)$(plugindir)"; \ } uninstall-pluginLTLIBRARIES: @$(NORMAL_UNINSTALL) @list='$(plugin_LTLIBRARIES)'; test -n "$(plugindir)" || list=; \ for p in $$list; do \ $(am__strip_dir) \ echo " $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=uninstall rm -f '$(DESTDIR)$(plugindir)/$$f'"; \ $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=uninstall rm -f "$(DESTDIR)$(plugindir)/$$f"; \ done clean-pluginLTLIBRARIES: -test -z "$(plugin_LTLIBRARIES)" || rm -f $(plugin_LTLIBRARIES) @list='$(plugin_LTLIBRARIES)'; \ locs=`for p in $$list; do echo $$p; done | \ sed 's|^[^/]*$$|.|; s|/[^/]*$$||; s|$$|/so_locations|' | \ sort -u`; \ test -z "$$locs" || { \ echo rm -f $${locs}; \ rm -f $${locs}; \ } android/$(am__dirstamp): @$(MKDIR_P) android @: > android/$(am__dirstamp) android/$(DEPDIR)/$(am__dirstamp): @$(MKDIR_P) android/$(DEPDIR) @: > android/$(DEPDIR)/$(am__dirstamp) android/audio_a2dp_default_la-hal-audio.lo: android/$(am__dirstamp) \ android/$(DEPDIR)/$(am__dirstamp) android/audio_a2dp_default_la-hal-audio-sbc.lo: \ android/$(am__dirstamp) android/$(DEPDIR)/$(am__dirstamp) android/audio_a2dp_default_la-hal-audio-aptx.lo: \ android/$(am__dirstamp) android/$(DEPDIR)/$(am__dirstamp) android/audio.a2dp.default.la: $(android_audio_a2dp_default_la_OBJECTS) $(android_audio_a2dp_default_la_DEPENDENCIES) $(EXTRA_android_audio_a2dp_default_la_DEPENDENCIES) android/$(am__dirstamp) $(AM_V_CCLD)$(android_audio_a2dp_default_la_LINK) $(am_android_audio_a2dp_default_la_rpath) $(android_audio_a2dp_default_la_OBJECTS) $(android_audio_a2dp_default_la_LIBADD) $(LIBS) android/audio_sco_default_la-hal-sco.lo: android/$(am__dirstamp) \ android/$(DEPDIR)/$(am__dirstamp) android/audio_utils/$(am__dirstamp): @$(MKDIR_P) android/audio_utils @: > android/audio_utils/$(am__dirstamp) android/audio_utils/$(DEPDIR)/$(am__dirstamp): @$(MKDIR_P) android/audio_utils/$(DEPDIR) @: > android/audio_utils/$(DEPDIR)/$(am__dirstamp) android/audio_utils/audio_sco_default_la-resampler.lo: \ android/audio_utils/$(am__dirstamp) \ android/audio_utils/$(DEPDIR)/$(am__dirstamp) android/audio.sco.default.la: $(android_audio_sco_default_la_OBJECTS) $(android_audio_sco_default_la_DEPENDENCIES) $(EXTRA_android_audio_sco_default_la_DEPENDENCIES) android/$(am__dirstamp) $(AM_V_CCLD)$(android_audio_sco_default_la_LINK) $(am_android_audio_sco_default_la_rpath) $(android_audio_sco_default_la_OBJECTS) $(android_audio_sco_default_la_LIBADD) $(LIBS) android/bluetooth_default_la-hal-bluetooth.lo: \ android/$(am__dirstamp) android/$(DEPDIR)/$(am__dirstamp) android/bluetooth_default_la-hal-socket.lo: android/$(am__dirstamp) \ android/$(DEPDIR)/$(am__dirstamp) android/bluetooth_default_la-hal-hidhost.lo: android/$(am__dirstamp) \ android/$(DEPDIR)/$(am__dirstamp) android/bluetooth_default_la-hal-health.lo: android/$(am__dirstamp) \ android/$(DEPDIR)/$(am__dirstamp) android/bluetooth_default_la-hal-pan.lo: android/$(am__dirstamp) \ android/$(DEPDIR)/$(am__dirstamp) android/bluetooth_default_la-hal-a2dp.lo: android/$(am__dirstamp) \ android/$(DEPDIR)/$(am__dirstamp) android/bluetooth_default_la-hal-a2dp-sink.lo: \ android/$(am__dirstamp) android/$(DEPDIR)/$(am__dirstamp) android/bluetooth_default_la-hal-avrcp.lo: android/$(am__dirstamp) \ android/$(DEPDIR)/$(am__dirstamp) android/bluetooth_default_la-hal-avrcp-ctrl.lo: \ android/$(am__dirstamp) android/$(DEPDIR)/$(am__dirstamp) android/bluetooth_default_la-hal-handsfree.lo: \ android/$(am__dirstamp) android/$(DEPDIR)/$(am__dirstamp) android/bluetooth_default_la-hal-handsfree-client.lo: \ android/$(am__dirstamp) android/$(DEPDIR)/$(am__dirstamp) android/bluetooth_default_la-hal-gatt.lo: android/$(am__dirstamp) \ android/$(DEPDIR)/$(am__dirstamp) android/bluetooth_default_la-hal-map-client.lo: \ android/$(am__dirstamp) android/$(DEPDIR)/$(am__dirstamp) android/bluetooth_default_la-hal-ipc.lo: android/$(am__dirstamp) \ android/$(DEPDIR)/$(am__dirstamp) android/bluetooth_default_la-hal-utils.lo: android/$(am__dirstamp) \ android/$(DEPDIR)/$(am__dirstamp) android/bluetooth.default.la: $(android_bluetooth_default_la_OBJECTS) $(android_bluetooth_default_la_DEPENDENCIES) $(EXTRA_android_bluetooth_default_la_DEPENDENCIES) android/$(am__dirstamp) $(AM_V_CCLD)$(android_bluetooth_default_la_LINK) $(am_android_bluetooth_default_la_rpath) $(android_bluetooth_default_la_OBJECTS) $(android_bluetooth_default_la_LIBADD) $(LIBS) ell/$(am__dirstamp): @$(MKDIR_P) ell @: > ell/$(am__dirstamp) ell/$(DEPDIR)/$(am__dirstamp): @$(MKDIR_P) ell/$(DEPDIR) @: > ell/$(DEPDIR)/$(am__dirstamp) ell/util.lo: ell/$(am__dirstamp) ell/$(DEPDIR)/$(am__dirstamp) ell/log.lo: ell/$(am__dirstamp) ell/$(DEPDIR)/$(am__dirstamp) ell/queue.lo: ell/$(am__dirstamp) ell/$(DEPDIR)/$(am__dirstamp) ell/hashmap.lo: ell/$(am__dirstamp) ell/$(DEPDIR)/$(am__dirstamp) ell/random.lo: ell/$(am__dirstamp) ell/$(DEPDIR)/$(am__dirstamp) ell/signal.lo: ell/$(am__dirstamp) ell/$(DEPDIR)/$(am__dirstamp) ell/time.lo: ell/$(am__dirstamp) ell/$(DEPDIR)/$(am__dirstamp) ell/timeout.lo: ell/$(am__dirstamp) ell/$(DEPDIR)/$(am__dirstamp) ell/io.lo: ell/$(am__dirstamp) ell/$(DEPDIR)/$(am__dirstamp) ell/idle.lo: ell/$(am__dirstamp) ell/$(DEPDIR)/$(am__dirstamp) ell/main.lo: ell/$(am__dirstamp) ell/$(DEPDIR)/$(am__dirstamp) ell/settings.lo: ell/$(am__dirstamp) ell/$(DEPDIR)/$(am__dirstamp) ell/strv.lo: ell/$(am__dirstamp) ell/$(DEPDIR)/$(am__dirstamp) ell/string.lo: ell/$(am__dirstamp) ell/$(DEPDIR)/$(am__dirstamp) ell/cipher.lo: ell/$(am__dirstamp) ell/$(DEPDIR)/$(am__dirstamp) ell/checksum.lo: ell/$(am__dirstamp) ell/$(DEPDIR)/$(am__dirstamp) ell/pem.lo: ell/$(am__dirstamp) ell/$(DEPDIR)/$(am__dirstamp) ell/cert.lo: ell/$(am__dirstamp) ell/$(DEPDIR)/$(am__dirstamp) ell/cert-crypto.lo: ell/$(am__dirstamp) ell/$(DEPDIR)/$(am__dirstamp) ell/key.lo: ell/$(am__dirstamp) ell/$(DEPDIR)/$(am__dirstamp) ell/base64.lo: ell/$(am__dirstamp) ell/$(DEPDIR)/$(am__dirstamp) ell/utf8.lo: ell/$(am__dirstamp) ell/$(DEPDIR)/$(am__dirstamp) ell/dbus.lo: ell/$(am__dirstamp) ell/$(DEPDIR)/$(am__dirstamp) ell/dbus-message.lo: ell/$(am__dirstamp) ell/$(DEPDIR)/$(am__dirstamp) ell/dbus-util.lo: ell/$(am__dirstamp) ell/$(DEPDIR)/$(am__dirstamp) ell/dbus-service.lo: ell/$(am__dirstamp) ell/$(DEPDIR)/$(am__dirstamp) ell/dbus-client.lo: ell/$(am__dirstamp) ell/$(DEPDIR)/$(am__dirstamp) ell/dbus-name-cache.lo: ell/$(am__dirstamp) \ ell/$(DEPDIR)/$(am__dirstamp) ell/dbus-filter.lo: ell/$(am__dirstamp) ell/$(DEPDIR)/$(am__dirstamp) ell/gvariant-util.lo: ell/$(am__dirstamp) \ ell/$(DEPDIR)/$(am__dirstamp) ell/siphash.lo: ell/$(am__dirstamp) ell/$(DEPDIR)/$(am__dirstamp) ell/uuid.lo: ell/$(am__dirstamp) ell/$(DEPDIR)/$(am__dirstamp) ell/tester.lo: ell/$(am__dirstamp) ell/$(DEPDIR)/$(am__dirstamp) ell/tls.lo: ell/$(am__dirstamp) ell/$(DEPDIR)/$(am__dirstamp) ell/tls-extensions.lo: ell/$(am__dirstamp) \ ell/$(DEPDIR)/$(am__dirstamp) ell/tls-suites.lo: ell/$(am__dirstamp) ell/$(DEPDIR)/$(am__dirstamp) ell/tls-record.lo: ell/$(am__dirstamp) ell/$(DEPDIR)/$(am__dirstamp) ell/ecc.lo: ell/$(am__dirstamp) ell/$(DEPDIR)/$(am__dirstamp) ell/ecc-external.lo: ell/$(am__dirstamp) ell/$(DEPDIR)/$(am__dirstamp) ell/ecdh.lo: ell/$(am__dirstamp) ell/$(DEPDIR)/$(am__dirstamp) ell/libell-internal.la: $(ell_libell_internal_la_OBJECTS) $(ell_libell_internal_la_DEPENDENCIES) $(EXTRA_ell_libell_internal_la_DEPENDENCIES) ell/$(am__dirstamp) $(AM_V_CCLD)$(LINK) $(am_ell_libell_internal_la_rpath) $(ell_libell_internal_la_OBJECTS) $(ell_libell_internal_la_LIBADD) $(LIBS) gdbus/$(am__dirstamp): @$(MKDIR_P) gdbus @: > gdbus/$(am__dirstamp) gdbus/$(DEPDIR)/$(am__dirstamp): @$(MKDIR_P) gdbus/$(DEPDIR) @: > gdbus/$(DEPDIR)/$(am__dirstamp) gdbus/mainloop.lo: gdbus/$(am__dirstamp) \ gdbus/$(DEPDIR)/$(am__dirstamp) gdbus/watch.lo: gdbus/$(am__dirstamp) gdbus/$(DEPDIR)/$(am__dirstamp) gdbus/object.lo: gdbus/$(am__dirstamp) gdbus/$(DEPDIR)/$(am__dirstamp) gdbus/client.lo: gdbus/$(am__dirstamp) gdbus/$(DEPDIR)/$(am__dirstamp) gdbus/polkit.lo: gdbus/$(am__dirstamp) gdbus/$(DEPDIR)/$(am__dirstamp) gdbus/libgdbus-internal.la: $(gdbus_libgdbus_internal_la_OBJECTS) $(gdbus_libgdbus_internal_la_DEPENDENCIES) $(EXTRA_gdbus_libgdbus_internal_la_DEPENDENCIES) gdbus/$(am__dirstamp) $(AM_V_CCLD)$(LINK) $(gdbus_libgdbus_internal_la_OBJECTS) $(gdbus_libgdbus_internal_la_LIBADD) $(LIBS) lib/$(am__dirstamp): @$(MKDIR_P) lib @: > lib/$(am__dirstamp) lib/$(DEPDIR)/$(am__dirstamp): @$(MKDIR_P) lib/$(DEPDIR) @: > lib/$(DEPDIR)/$(am__dirstamp) lib/bluetooth.lo: lib/$(am__dirstamp) lib/$(DEPDIR)/$(am__dirstamp) lib/hci.lo: lib/$(am__dirstamp) lib/$(DEPDIR)/$(am__dirstamp) lib/sdp.lo: lib/$(am__dirstamp) lib/$(DEPDIR)/$(am__dirstamp) lib/uuid.lo: lib/$(am__dirstamp) lib/$(DEPDIR)/$(am__dirstamp) lib/libbluetooth-internal.la: $(lib_libbluetooth_internal_la_OBJECTS) $(lib_libbluetooth_internal_la_DEPENDENCIES) $(EXTRA_lib_libbluetooth_internal_la_DEPENDENCIES) lib/$(am__dirstamp) $(AM_V_CCLD)$(LINK) $(lib_libbluetooth_internal_la_OBJECTS) $(lib_libbluetooth_internal_la_LIBADD) $(LIBS) lib/libbluetooth.la: $(lib_libbluetooth_la_OBJECTS) $(lib_libbluetooth_la_DEPENDENCIES) $(EXTRA_lib_libbluetooth_la_DEPENDENCIES) lib/$(am__dirstamp) $(AM_V_CCLD)$(lib_libbluetooth_la_LINK) $(am_lib_libbluetooth_la_rpath) $(lib_libbluetooth_la_OBJECTS) $(lib_libbluetooth_la_LIBADD) $(LIBS) src/shared/$(am__dirstamp): @$(MKDIR_P) src/shared @: > src/shared/$(am__dirstamp) src/shared/$(DEPDIR)/$(am__dirstamp): @$(MKDIR_P) src/shared/$(DEPDIR) @: > src/shared/$(DEPDIR)/$(am__dirstamp) src/shared/libshared_ell_la-queue.lo: src/shared/$(am__dirstamp) \ src/shared/$(DEPDIR)/$(am__dirstamp) src/shared/libshared_ell_la-util.lo: src/shared/$(am__dirstamp) \ src/shared/$(DEPDIR)/$(am__dirstamp) src/shared/libshared_ell_la-mgmt.lo: src/shared/$(am__dirstamp) \ src/shared/$(DEPDIR)/$(am__dirstamp) src/shared/libshared_ell_la-crypto.lo: src/shared/$(am__dirstamp) \ src/shared/$(DEPDIR)/$(am__dirstamp) src/shared/libshared_ell_la-ecc.lo: src/shared/$(am__dirstamp) \ src/shared/$(DEPDIR)/$(am__dirstamp) src/shared/libshared_ell_la-ringbuf.lo: src/shared/$(am__dirstamp) \ src/shared/$(DEPDIR)/$(am__dirstamp) src/shared/libshared_ell_la-hci.lo: src/shared/$(am__dirstamp) \ src/shared/$(DEPDIR)/$(am__dirstamp) src/shared/libshared_ell_la-hci-crypto.lo: src/shared/$(am__dirstamp) \ src/shared/$(DEPDIR)/$(am__dirstamp) src/shared/libshared_ell_la-hfp.lo: src/shared/$(am__dirstamp) \ src/shared/$(DEPDIR)/$(am__dirstamp) src/shared/libshared_ell_la-uhid.lo: src/shared/$(am__dirstamp) \ src/shared/$(DEPDIR)/$(am__dirstamp) src/shared/libshared_ell_la-pcap.lo: src/shared/$(am__dirstamp) \ src/shared/$(DEPDIR)/$(am__dirstamp) src/shared/libshared_ell_la-btsnoop.lo: src/shared/$(am__dirstamp) \ src/shared/$(DEPDIR)/$(am__dirstamp) src/shared/libshared_ell_la-ad.lo: src/shared/$(am__dirstamp) \ src/shared/$(DEPDIR)/$(am__dirstamp) src/shared/libshared_ell_la-att.lo: src/shared/$(am__dirstamp) \ src/shared/$(DEPDIR)/$(am__dirstamp) src/shared/libshared_ell_la-gatt-helpers.lo: \ src/shared/$(am__dirstamp) \ src/shared/$(DEPDIR)/$(am__dirstamp) src/shared/libshared_ell_la-gatt-client.lo: \ src/shared/$(am__dirstamp) \ src/shared/$(DEPDIR)/$(am__dirstamp) src/shared/libshared_ell_la-gatt-server.lo: \ src/shared/$(am__dirstamp) \ src/shared/$(DEPDIR)/$(am__dirstamp) src/shared/libshared_ell_la-gatt-db.lo: src/shared/$(am__dirstamp) \ src/shared/$(DEPDIR)/$(am__dirstamp) src/shared/libshared_ell_la-gap.lo: src/shared/$(am__dirstamp) \ src/shared/$(DEPDIR)/$(am__dirstamp) src/shared/libshared_ell_la-log.lo: src/shared/$(am__dirstamp) \ src/shared/$(DEPDIR)/$(am__dirstamp) src/shared/libshared_ell_la-bap.lo: src/shared/$(am__dirstamp) \ src/shared/$(DEPDIR)/$(am__dirstamp) src/shared/libshared_ell_la-bap-debug.lo: src/shared/$(am__dirstamp) \ src/shared/$(DEPDIR)/$(am__dirstamp) src/shared/libshared_ell_la-mcp.lo: src/shared/$(am__dirstamp) \ src/shared/$(DEPDIR)/$(am__dirstamp) src/shared/libshared_ell_la-vcp.lo: src/shared/$(am__dirstamp) \ src/shared/$(DEPDIR)/$(am__dirstamp) src/shared/libshared_ell_la-micp.lo: src/shared/$(am__dirstamp) \ src/shared/$(DEPDIR)/$(am__dirstamp) src/shared/libshared_ell_la-csip.lo: src/shared/$(am__dirstamp) \ src/shared/$(DEPDIR)/$(am__dirstamp) src/shared/libshared_ell_la-bass.lo: src/shared/$(am__dirstamp) \ src/shared/$(DEPDIR)/$(am__dirstamp) src/shared/libshared_ell_la-ccp.lo: src/shared/$(am__dirstamp) \ src/shared/$(DEPDIR)/$(am__dirstamp) src/shared/libshared_ell_la-asha.lo: src/shared/$(am__dirstamp) \ src/shared/$(DEPDIR)/$(am__dirstamp) src/shared/libshared_ell_la-shell.lo: src/shared/$(am__dirstamp) \ src/shared/$(DEPDIR)/$(am__dirstamp) src/shared/libshared_ell_la-io-ell.lo: src/shared/$(am__dirstamp) \ src/shared/$(DEPDIR)/$(am__dirstamp) src/shared/libshared_ell_la-timeout-ell.lo: \ src/shared/$(am__dirstamp) \ src/shared/$(DEPDIR)/$(am__dirstamp) src/shared/libshared_ell_la-mainloop-ell.lo: \ src/shared/$(am__dirstamp) \ src/shared/$(DEPDIR)/$(am__dirstamp) src/$(am__dirstamp): @$(MKDIR_P) src @: > src/$(am__dirstamp) src/libshared-ell.la: $(src_libshared_ell_la_OBJECTS) $(src_libshared_ell_la_DEPENDENCIES) $(EXTRA_src_libshared_ell_la_DEPENDENCIES) src/$(am__dirstamp) $(AM_V_CCLD)$(src_libshared_ell_la_LINK) $(am_src_libshared_ell_la_rpath) $(src_libshared_ell_la_OBJECTS) $(src_libshared_ell_la_LIBADD) $(LIBS) src/shared/libshared_glib_la-queue.lo: src/shared/$(am__dirstamp) \ src/shared/$(DEPDIR)/$(am__dirstamp) src/shared/libshared_glib_la-util.lo: src/shared/$(am__dirstamp) \ src/shared/$(DEPDIR)/$(am__dirstamp) src/shared/libshared_glib_la-mgmt.lo: src/shared/$(am__dirstamp) \ src/shared/$(DEPDIR)/$(am__dirstamp) src/shared/libshared_glib_la-crypto.lo: src/shared/$(am__dirstamp) \ src/shared/$(DEPDIR)/$(am__dirstamp) src/shared/libshared_glib_la-ecc.lo: src/shared/$(am__dirstamp) \ src/shared/$(DEPDIR)/$(am__dirstamp) src/shared/libshared_glib_la-ringbuf.lo: src/shared/$(am__dirstamp) \ src/shared/$(DEPDIR)/$(am__dirstamp) src/shared/libshared_glib_la-hci.lo: src/shared/$(am__dirstamp) \ src/shared/$(DEPDIR)/$(am__dirstamp) src/shared/libshared_glib_la-hci-crypto.lo: \ src/shared/$(am__dirstamp) \ src/shared/$(DEPDIR)/$(am__dirstamp) src/shared/libshared_glib_la-hfp.lo: src/shared/$(am__dirstamp) \ src/shared/$(DEPDIR)/$(am__dirstamp) src/shared/libshared_glib_la-uhid.lo: src/shared/$(am__dirstamp) \ src/shared/$(DEPDIR)/$(am__dirstamp) src/shared/libshared_glib_la-pcap.lo: src/shared/$(am__dirstamp) \ src/shared/$(DEPDIR)/$(am__dirstamp) src/shared/libshared_glib_la-btsnoop.lo: src/shared/$(am__dirstamp) \ src/shared/$(DEPDIR)/$(am__dirstamp) src/shared/libshared_glib_la-ad.lo: src/shared/$(am__dirstamp) \ src/shared/$(DEPDIR)/$(am__dirstamp) src/shared/libshared_glib_la-att.lo: src/shared/$(am__dirstamp) \ src/shared/$(DEPDIR)/$(am__dirstamp) src/shared/libshared_glib_la-gatt-helpers.lo: \ src/shared/$(am__dirstamp) \ src/shared/$(DEPDIR)/$(am__dirstamp) src/shared/libshared_glib_la-gatt-client.lo: \ src/shared/$(am__dirstamp) \ src/shared/$(DEPDIR)/$(am__dirstamp) src/shared/libshared_glib_la-gatt-server.lo: \ src/shared/$(am__dirstamp) \ src/shared/$(DEPDIR)/$(am__dirstamp) src/shared/libshared_glib_la-gatt-db.lo: src/shared/$(am__dirstamp) \ src/shared/$(DEPDIR)/$(am__dirstamp) src/shared/libshared_glib_la-gap.lo: src/shared/$(am__dirstamp) \ src/shared/$(DEPDIR)/$(am__dirstamp) src/shared/libshared_glib_la-log.lo: src/shared/$(am__dirstamp) \ src/shared/$(DEPDIR)/$(am__dirstamp) src/shared/libshared_glib_la-bap.lo: src/shared/$(am__dirstamp) \ src/shared/$(DEPDIR)/$(am__dirstamp) src/shared/libshared_glib_la-bap-debug.lo: src/shared/$(am__dirstamp) \ src/shared/$(DEPDIR)/$(am__dirstamp) src/shared/libshared_glib_la-mcp.lo: src/shared/$(am__dirstamp) \ src/shared/$(DEPDIR)/$(am__dirstamp) src/shared/libshared_glib_la-vcp.lo: src/shared/$(am__dirstamp) \ src/shared/$(DEPDIR)/$(am__dirstamp) src/shared/libshared_glib_la-micp.lo: src/shared/$(am__dirstamp) \ src/shared/$(DEPDIR)/$(am__dirstamp) src/shared/libshared_glib_la-csip.lo: src/shared/$(am__dirstamp) \ src/shared/$(DEPDIR)/$(am__dirstamp) src/shared/libshared_glib_la-bass.lo: src/shared/$(am__dirstamp) \ src/shared/$(DEPDIR)/$(am__dirstamp) src/shared/libshared_glib_la-ccp.lo: src/shared/$(am__dirstamp) \ src/shared/$(DEPDIR)/$(am__dirstamp) src/shared/libshared_glib_la-asha.lo: src/shared/$(am__dirstamp) \ src/shared/$(DEPDIR)/$(am__dirstamp) src/shared/libshared_glib_la-shell.lo: src/shared/$(am__dirstamp) \ src/shared/$(DEPDIR)/$(am__dirstamp) src/shared/libshared_glib_la-io-glib.lo: src/shared/$(am__dirstamp) \ src/shared/$(DEPDIR)/$(am__dirstamp) src/shared/libshared_glib_la-timeout-glib.lo: \ src/shared/$(am__dirstamp) \ src/shared/$(DEPDIR)/$(am__dirstamp) src/shared/libshared_glib_la-mainloop-glib.lo: \ src/shared/$(am__dirstamp) \ src/shared/$(DEPDIR)/$(am__dirstamp) src/shared/libshared_glib_la-mainloop-notify.lo: \ src/shared/$(am__dirstamp) \ src/shared/$(DEPDIR)/$(am__dirstamp) src/shared/libshared_glib_la-tester.lo: src/shared/$(am__dirstamp) \ src/shared/$(DEPDIR)/$(am__dirstamp) src/libshared-glib.la: $(src_libshared_glib_la_OBJECTS) $(src_libshared_glib_la_DEPENDENCIES) $(EXTRA_src_libshared_glib_la_DEPENDENCIES) src/$(am__dirstamp) $(AM_V_CCLD)$(src_libshared_glib_la_LINK) $(src_libshared_glib_la_OBJECTS) $(src_libshared_glib_la_LIBADD) $(LIBS) src/shared/libshared_mainloop_la-queue.lo: src/shared/$(am__dirstamp) \ src/shared/$(DEPDIR)/$(am__dirstamp) src/shared/libshared_mainloop_la-util.lo: src/shared/$(am__dirstamp) \ src/shared/$(DEPDIR)/$(am__dirstamp) src/shared/libshared_mainloop_la-mgmt.lo: src/shared/$(am__dirstamp) \ src/shared/$(DEPDIR)/$(am__dirstamp) src/shared/libshared_mainloop_la-crypto.lo: \ src/shared/$(am__dirstamp) \ src/shared/$(DEPDIR)/$(am__dirstamp) src/shared/libshared_mainloop_la-ecc.lo: src/shared/$(am__dirstamp) \ src/shared/$(DEPDIR)/$(am__dirstamp) src/shared/libshared_mainloop_la-ringbuf.lo: \ src/shared/$(am__dirstamp) \ src/shared/$(DEPDIR)/$(am__dirstamp) src/shared/libshared_mainloop_la-hci.lo: src/shared/$(am__dirstamp) \ src/shared/$(DEPDIR)/$(am__dirstamp) src/shared/libshared_mainloop_la-hci-crypto.lo: \ src/shared/$(am__dirstamp) \ src/shared/$(DEPDIR)/$(am__dirstamp) src/shared/libshared_mainloop_la-hfp.lo: src/shared/$(am__dirstamp) \ src/shared/$(DEPDIR)/$(am__dirstamp) src/shared/libshared_mainloop_la-uhid.lo: src/shared/$(am__dirstamp) \ src/shared/$(DEPDIR)/$(am__dirstamp) src/shared/libshared_mainloop_la-pcap.lo: src/shared/$(am__dirstamp) \ src/shared/$(DEPDIR)/$(am__dirstamp) src/shared/libshared_mainloop_la-btsnoop.lo: \ src/shared/$(am__dirstamp) \ src/shared/$(DEPDIR)/$(am__dirstamp) src/shared/libshared_mainloop_la-ad.lo: src/shared/$(am__dirstamp) \ src/shared/$(DEPDIR)/$(am__dirstamp) src/shared/libshared_mainloop_la-att.lo: src/shared/$(am__dirstamp) \ src/shared/$(DEPDIR)/$(am__dirstamp) src/shared/libshared_mainloop_la-gatt-helpers.lo: \ src/shared/$(am__dirstamp) \ src/shared/$(DEPDIR)/$(am__dirstamp) src/shared/libshared_mainloop_la-gatt-client.lo: \ src/shared/$(am__dirstamp) \ src/shared/$(DEPDIR)/$(am__dirstamp) src/shared/libshared_mainloop_la-gatt-server.lo: \ src/shared/$(am__dirstamp) \ src/shared/$(DEPDIR)/$(am__dirstamp) src/shared/libshared_mainloop_la-gatt-db.lo: \ src/shared/$(am__dirstamp) \ src/shared/$(DEPDIR)/$(am__dirstamp) src/shared/libshared_mainloop_la-gap.lo: src/shared/$(am__dirstamp) \ src/shared/$(DEPDIR)/$(am__dirstamp) src/shared/libshared_mainloop_la-log.lo: src/shared/$(am__dirstamp) \ src/shared/$(DEPDIR)/$(am__dirstamp) src/shared/libshared_mainloop_la-bap.lo: src/shared/$(am__dirstamp) \ src/shared/$(DEPDIR)/$(am__dirstamp) src/shared/libshared_mainloop_la-bap-debug.lo: \ src/shared/$(am__dirstamp) \ src/shared/$(DEPDIR)/$(am__dirstamp) src/shared/libshared_mainloop_la-mcp.lo: src/shared/$(am__dirstamp) \ src/shared/$(DEPDIR)/$(am__dirstamp) src/shared/libshared_mainloop_la-vcp.lo: src/shared/$(am__dirstamp) \ src/shared/$(DEPDIR)/$(am__dirstamp) src/shared/libshared_mainloop_la-micp.lo: src/shared/$(am__dirstamp) \ src/shared/$(DEPDIR)/$(am__dirstamp) src/shared/libshared_mainloop_la-csip.lo: src/shared/$(am__dirstamp) \ src/shared/$(DEPDIR)/$(am__dirstamp) src/shared/libshared_mainloop_la-bass.lo: src/shared/$(am__dirstamp) \ src/shared/$(DEPDIR)/$(am__dirstamp) src/shared/libshared_mainloop_la-ccp.lo: src/shared/$(am__dirstamp) \ src/shared/$(DEPDIR)/$(am__dirstamp) src/shared/libshared_mainloop_la-asha.lo: src/shared/$(am__dirstamp) \ src/shared/$(DEPDIR)/$(am__dirstamp) src/shared/libshared_mainloop_la-shell.lo: src/shared/$(am__dirstamp) \ src/shared/$(DEPDIR)/$(am__dirstamp) src/shared/libshared_mainloop_la-io-mainloop.lo: \ src/shared/$(am__dirstamp) \ src/shared/$(DEPDIR)/$(am__dirstamp) src/shared/libshared_mainloop_la-timeout-mainloop.lo: \ src/shared/$(am__dirstamp) \ src/shared/$(DEPDIR)/$(am__dirstamp) src/shared/libshared_mainloop_la-mainloop.lo: \ src/shared/$(am__dirstamp) \ src/shared/$(DEPDIR)/$(am__dirstamp) src/shared/libshared_mainloop_la-mainloop-notify.lo: \ src/shared/$(am__dirstamp) \ src/shared/$(DEPDIR)/$(am__dirstamp) src/libshared-mainloop.la: $(src_libshared_mainloop_la_OBJECTS) $(src_libshared_mainloop_la_DEPENDENCIES) $(EXTRA_src_libshared_mainloop_la_DEPENDENCIES) src/$(am__dirstamp) $(AM_V_CCLD)$(src_libshared_mainloop_la_LINK) $(src_libshared_mainloop_la_OBJECTS) $(src_libshared_mainloop_la_LIBADD) $(LIBS) emulator/$(am__dirstamp): @$(MKDIR_P) emulator @: > emulator/$(am__dirstamp) emulator/$(DEPDIR)/$(am__dirstamp): @$(MKDIR_P) emulator/$(DEPDIR) @: > emulator/$(DEPDIR)/$(am__dirstamp) emulator/android_android_tester-hciemu.$(OBJEXT): \ emulator/$(am__dirstamp) emulator/$(DEPDIR)/$(am__dirstamp) emulator/android_android_tester-vhci.$(OBJEXT): \ emulator/$(am__dirstamp) emulator/$(DEPDIR)/$(am__dirstamp) emulator/android_android_tester-btdev.$(OBJEXT): \ emulator/$(am__dirstamp) emulator/$(DEPDIR)/$(am__dirstamp) emulator/android_android_tester-bthost.$(OBJEXT): \ emulator/$(am__dirstamp) emulator/$(DEPDIR)/$(am__dirstamp) emulator/android_android_tester-smp.$(OBJEXT): \ emulator/$(am__dirstamp) emulator/$(DEPDIR)/$(am__dirstamp) android/hardware/$(am__dirstamp): @$(MKDIR_P) android/hardware @: > android/hardware/$(am__dirstamp) android/hardware/$(DEPDIR)/$(am__dirstamp): @$(MKDIR_P) android/hardware/$(DEPDIR) @: > android/hardware/$(DEPDIR)/$(am__dirstamp) android/hardware/android_tester-hardware.$(OBJEXT): \ android/hardware/$(am__dirstamp) \ android/hardware/$(DEPDIR)/$(am__dirstamp) android/android_tester-tester-bluetooth.$(OBJEXT): \ android/$(am__dirstamp) android/$(DEPDIR)/$(am__dirstamp) android/android_tester-tester-socket.$(OBJEXT): \ android/$(am__dirstamp) android/$(DEPDIR)/$(am__dirstamp) android/android_tester-tester-hidhost.$(OBJEXT): \ android/$(am__dirstamp) android/$(DEPDIR)/$(am__dirstamp) android/android_tester-tester-pan.$(OBJEXT): android/$(am__dirstamp) \ android/$(DEPDIR)/$(am__dirstamp) android/android_tester-tester-hdp.$(OBJEXT): android/$(am__dirstamp) \ android/$(DEPDIR)/$(am__dirstamp) android/android_tester-tester-a2dp.$(OBJEXT): android/$(am__dirstamp) \ android/$(DEPDIR)/$(am__dirstamp) android/android_tester-tester-avrcp.$(OBJEXT): \ android/$(am__dirstamp) android/$(DEPDIR)/$(am__dirstamp) android/android_tester-tester-gatt.$(OBJEXT): android/$(am__dirstamp) \ android/$(DEPDIR)/$(am__dirstamp) android/android_tester-tester-map-client.$(OBJEXT): \ android/$(am__dirstamp) android/$(DEPDIR)/$(am__dirstamp) android/android_tester-tester-main.$(OBJEXT): android/$(am__dirstamp) \ android/$(DEPDIR)/$(am__dirstamp) android/android-tester$(EXEEXT): $(android_android_tester_OBJECTS) $(android_android_tester_DEPENDENCIES) $(EXTRA_android_android_tester_DEPENDENCIES) android/$(am__dirstamp) @rm -f android/android-tester$(EXEEXT) $(AM_V_CCLD)$(android_android_tester_LINK) $(android_android_tester_OBJECTS) $(android_android_tester_LDADD) $(LIBS) android/avdtptest-avdtptest.$(OBJEXT): android/$(am__dirstamp) \ android/$(DEPDIR)/$(am__dirstamp) src/$(DEPDIR)/$(am__dirstamp): @$(MKDIR_P) src/$(DEPDIR) @: > src/$(DEPDIR)/$(am__dirstamp) src/android_avdtptest-log.$(OBJEXT): src/$(am__dirstamp) \ src/$(DEPDIR)/$(am__dirstamp) btio/$(am__dirstamp): @$(MKDIR_P) btio @: > btio/$(am__dirstamp) btio/$(DEPDIR)/$(am__dirstamp): @$(MKDIR_P) btio/$(DEPDIR) @: > btio/$(DEPDIR)/$(am__dirstamp) btio/android_avdtptest-btio.$(OBJEXT): btio/$(am__dirstamp) \ btio/$(DEPDIR)/$(am__dirstamp) src/shared/android_avdtptest-util.$(OBJEXT): \ src/shared/$(am__dirstamp) \ src/shared/$(DEPDIR)/$(am__dirstamp) src/shared/android_avdtptest-queue.$(OBJEXT): \ src/shared/$(am__dirstamp) \ src/shared/$(DEPDIR)/$(am__dirstamp) src/shared/android_avdtptest-log.$(OBJEXT): \ src/shared/$(am__dirstamp) \ src/shared/$(DEPDIR)/$(am__dirstamp) android/avdtptest-avdtp.$(OBJEXT): android/$(am__dirstamp) \ android/$(DEPDIR)/$(am__dirstamp) android/avdtptest$(EXEEXT): $(android_avdtptest_OBJECTS) $(android_avdtptest_DEPENDENCIES) $(EXTRA_android_avdtptest_DEPENDENCIES) android/$(am__dirstamp) @rm -f android/avdtptest$(EXEEXT) $(AM_V_CCLD)$(android_avdtptest_LINK) $(android_avdtptest_OBJECTS) $(android_avdtptest_LDADD) $(LIBS) android/main.$(OBJEXT): android/$(am__dirstamp) \ android/$(DEPDIR)/$(am__dirstamp) src/log.$(OBJEXT): src/$(am__dirstamp) src/$(DEPDIR)/$(am__dirstamp) src/sdpd-database.$(OBJEXT): src/$(am__dirstamp) \ src/$(DEPDIR)/$(am__dirstamp) src/sdpd-server.$(OBJEXT): src/$(am__dirstamp) \ src/$(DEPDIR)/$(am__dirstamp) src/sdpd-service.$(OBJEXT): src/$(am__dirstamp) \ src/$(DEPDIR)/$(am__dirstamp) src/sdpd-request.$(OBJEXT): src/$(am__dirstamp) \ src/$(DEPDIR)/$(am__dirstamp) src/uuid-helper.$(OBJEXT): src/$(am__dirstamp) \ src/$(DEPDIR)/$(am__dirstamp) src/eir.$(OBJEXT): src/$(am__dirstamp) src/$(DEPDIR)/$(am__dirstamp) android/bluetooth.$(OBJEXT): android/$(am__dirstamp) \ android/$(DEPDIR)/$(am__dirstamp) android/hidhost.$(OBJEXT): android/$(am__dirstamp) \ android/$(DEPDIR)/$(am__dirstamp) profiles/scanparam/$(am__dirstamp): @$(MKDIR_P) profiles/scanparam @: > profiles/scanparam/$(am__dirstamp) profiles/scanparam/$(DEPDIR)/$(am__dirstamp): @$(MKDIR_P) profiles/scanparam/$(DEPDIR) @: > profiles/scanparam/$(DEPDIR)/$(am__dirstamp) profiles/scanparam/scpp.$(OBJEXT): profiles/scanparam/$(am__dirstamp) \ profiles/scanparam/$(DEPDIR)/$(am__dirstamp) profiles/deviceinfo/$(am__dirstamp): @$(MKDIR_P) profiles/deviceinfo @: > profiles/deviceinfo/$(am__dirstamp) profiles/deviceinfo/$(DEPDIR)/$(am__dirstamp): @$(MKDIR_P) profiles/deviceinfo/$(DEPDIR) @: > profiles/deviceinfo/$(DEPDIR)/$(am__dirstamp) profiles/deviceinfo/dis.$(OBJEXT): \ profiles/deviceinfo/$(am__dirstamp) \ profiles/deviceinfo/$(DEPDIR)/$(am__dirstamp) profiles/battery/$(am__dirstamp): @$(MKDIR_P) profiles/battery @: > profiles/battery/$(am__dirstamp) profiles/battery/$(DEPDIR)/$(am__dirstamp): @$(MKDIR_P) profiles/battery/$(DEPDIR) @: > profiles/battery/$(DEPDIR)/$(am__dirstamp) profiles/battery/bas.$(OBJEXT): profiles/battery/$(am__dirstamp) \ profiles/battery/$(DEPDIR)/$(am__dirstamp) profiles/input/$(am__dirstamp): @$(MKDIR_P) profiles/input @: > profiles/input/$(am__dirstamp) profiles/input/$(DEPDIR)/$(am__dirstamp): @$(MKDIR_P) profiles/input/$(DEPDIR) @: > profiles/input/$(DEPDIR)/$(am__dirstamp) profiles/input/hog-lib.$(OBJEXT): profiles/input/$(am__dirstamp) \ profiles/input/$(DEPDIR)/$(am__dirstamp) android/ipc.$(OBJEXT): android/$(am__dirstamp) \ android/$(DEPDIR)/$(am__dirstamp) android/avdtp.$(OBJEXT): android/$(am__dirstamp) \ android/$(DEPDIR)/$(am__dirstamp) android/a2dp.$(OBJEXT): android/$(am__dirstamp) \ android/$(DEPDIR)/$(am__dirstamp) android/a2dp-sink.$(OBJEXT): android/$(am__dirstamp) \ android/$(DEPDIR)/$(am__dirstamp) android/avctp.$(OBJEXT): android/$(am__dirstamp) \ android/$(DEPDIR)/$(am__dirstamp) android/avrcp.$(OBJEXT): android/$(am__dirstamp) \ android/$(DEPDIR)/$(am__dirstamp) android/avrcp-lib.$(OBJEXT): android/$(am__dirstamp) \ android/$(DEPDIR)/$(am__dirstamp) android/socket.$(OBJEXT): android/$(am__dirstamp) \ android/$(DEPDIR)/$(am__dirstamp) android/sco.$(OBJEXT): android/$(am__dirstamp) \ android/$(DEPDIR)/$(am__dirstamp) android/pan.$(OBJEXT): android/$(am__dirstamp) \ android/$(DEPDIR)/$(am__dirstamp) android/handsfree.$(OBJEXT): android/$(am__dirstamp) \ android/$(DEPDIR)/$(am__dirstamp) android/handsfree-client.$(OBJEXT): android/$(am__dirstamp) \ android/$(DEPDIR)/$(am__dirstamp) android/gatt.$(OBJEXT): android/$(am__dirstamp) \ android/$(DEPDIR)/$(am__dirstamp) android/health.$(OBJEXT): android/$(am__dirstamp) \ android/$(DEPDIR)/$(am__dirstamp) profiles/health/$(am__dirstamp): @$(MKDIR_P) profiles/health @: > profiles/health/$(am__dirstamp) profiles/health/$(DEPDIR)/$(am__dirstamp): @$(MKDIR_P) profiles/health/$(DEPDIR) @: > profiles/health/$(DEPDIR)/$(am__dirstamp) profiles/health/mcap.$(OBJEXT): profiles/health/$(am__dirstamp) \ profiles/health/$(DEPDIR)/$(am__dirstamp) android/map-client.$(OBJEXT): android/$(am__dirstamp) \ android/$(DEPDIR)/$(am__dirstamp) attrib/$(am__dirstamp): @$(MKDIR_P) attrib @: > attrib/$(am__dirstamp) attrib/$(DEPDIR)/$(am__dirstamp): @$(MKDIR_P) attrib/$(DEPDIR) @: > attrib/$(DEPDIR)/$(am__dirstamp) attrib/att.$(OBJEXT): attrib/$(am__dirstamp) \ attrib/$(DEPDIR)/$(am__dirstamp) attrib/gatt.$(OBJEXT): attrib/$(am__dirstamp) \ attrib/$(DEPDIR)/$(am__dirstamp) attrib/gattrib.$(OBJEXT): attrib/$(am__dirstamp) \ attrib/$(DEPDIR)/$(am__dirstamp) btio/btio.$(OBJEXT): btio/$(am__dirstamp) \ btio/$(DEPDIR)/$(am__dirstamp) src/sdp-client.$(OBJEXT): src/$(am__dirstamp) \ src/$(DEPDIR)/$(am__dirstamp) profiles/network/$(am__dirstamp): @$(MKDIR_P) profiles/network @: > profiles/network/$(am__dirstamp) profiles/network/$(DEPDIR)/$(am__dirstamp): @$(MKDIR_P) profiles/network/$(DEPDIR) @: > profiles/network/$(DEPDIR)/$(am__dirstamp) profiles/network/bnep.$(OBJEXT): profiles/network/$(am__dirstamp) \ profiles/network/$(DEPDIR)/$(am__dirstamp) android/bluetoothd$(EXEEXT): $(android_bluetoothd_OBJECTS) $(android_bluetoothd_DEPENDENCIES) $(EXTRA_android_bluetoothd_DEPENDENCIES) android/$(am__dirstamp) @rm -f android/bluetoothd$(EXEEXT) $(AM_V_CCLD)$(LINK) $(android_bluetoothd_OBJECTS) $(android_bluetoothd_LDADD) $(LIBS) android/bluetoothd-snoop.$(OBJEXT): android/$(am__dirstamp) \ android/$(DEPDIR)/$(am__dirstamp) android/bluetoothd-snoop$(EXEEXT): $(android_bluetoothd_snoop_OBJECTS) $(android_bluetoothd_snoop_DEPENDENCIES) $(EXTRA_android_bluetoothd_snoop_DEPENDENCIES) android/$(am__dirstamp) @rm -f android/bluetoothd-snoop$(EXEEXT) $(AM_V_CCLD)$(LINK) $(android_bluetoothd_snoop_OBJECTS) $(android_bluetoothd_snoop_LDADD) $(LIBS) android/client/$(am__dirstamp): @$(MKDIR_P) android/client @: > android/client/$(am__dirstamp) android/client/$(DEPDIR)/$(am__dirstamp): @$(MKDIR_P) android/client/$(DEPDIR) @: > android/client/$(DEPDIR)/$(am__dirstamp) android/client/haltest-haltest.$(OBJEXT): \ android/client/$(am__dirstamp) \ android/client/$(DEPDIR)/$(am__dirstamp) android/client/haltest-pollhandler.$(OBJEXT): \ android/client/$(am__dirstamp) \ android/client/$(DEPDIR)/$(am__dirstamp) android/client/haltest-terminal.$(OBJEXT): \ android/client/$(am__dirstamp) \ android/client/$(DEPDIR)/$(am__dirstamp) android/client/haltest-history.$(OBJEXT): \ android/client/$(am__dirstamp) \ android/client/$(DEPDIR)/$(am__dirstamp) android/client/haltest-tabcompletion.$(OBJEXT): \ android/client/$(am__dirstamp) \ android/client/$(DEPDIR)/$(am__dirstamp) android/client/haltest-if-av.$(OBJEXT): \ android/client/$(am__dirstamp) \ android/client/$(DEPDIR)/$(am__dirstamp) android/client/haltest-if-av-sink.$(OBJEXT): \ android/client/$(am__dirstamp) \ android/client/$(DEPDIR)/$(am__dirstamp) android/client/haltest-if-rc.$(OBJEXT): \ android/client/$(am__dirstamp) \ android/client/$(DEPDIR)/$(am__dirstamp) android/client/haltest-if-rc-ctrl.$(OBJEXT): \ android/client/$(am__dirstamp) \ android/client/$(DEPDIR)/$(am__dirstamp) android/client/haltest-if-bt.$(OBJEXT): \ android/client/$(am__dirstamp) \ android/client/$(DEPDIR)/$(am__dirstamp) android/client/haltest-if-gatt.$(OBJEXT): \ android/client/$(am__dirstamp) \ android/client/$(DEPDIR)/$(am__dirstamp) android/client/haltest-if-hf.$(OBJEXT): \ android/client/$(am__dirstamp) \ android/client/$(DEPDIR)/$(am__dirstamp) android/client/haltest-if-hf-client.$(OBJEXT): \ android/client/$(am__dirstamp) \ android/client/$(DEPDIR)/$(am__dirstamp) android/client/haltest-if-hh.$(OBJEXT): \ android/client/$(am__dirstamp) \ android/client/$(DEPDIR)/$(am__dirstamp) android/client/haltest-if-pan.$(OBJEXT): \ android/client/$(am__dirstamp) \ android/client/$(DEPDIR)/$(am__dirstamp) android/client/haltest-if-hl.$(OBJEXT): \ android/client/$(am__dirstamp) \ android/client/$(DEPDIR)/$(am__dirstamp) android/client/haltest-if-sock.$(OBJEXT): \ android/client/$(am__dirstamp) \ android/client/$(DEPDIR)/$(am__dirstamp) android/client/haltest-if-audio.$(OBJEXT): \ android/client/$(am__dirstamp) \ android/client/$(DEPDIR)/$(am__dirstamp) android/client/haltest-if-sco.$(OBJEXT): \ android/client/$(am__dirstamp) \ android/client/$(DEPDIR)/$(am__dirstamp) android/client/haltest-if-mce.$(OBJEXT): \ android/client/$(am__dirstamp) \ android/client/$(DEPDIR)/$(am__dirstamp) android/hardware/haltest-hardware.$(OBJEXT): \ android/hardware/$(am__dirstamp) \ android/hardware/$(DEPDIR)/$(am__dirstamp) android/haltest-hal-utils.$(OBJEXT): android/$(am__dirstamp) \ android/$(DEPDIR)/$(am__dirstamp) android/haltest$(EXEEXT): $(android_haltest_OBJECTS) $(android_haltest_DEPENDENCIES) $(EXTRA_android_haltest_DEPENDENCIES) android/$(am__dirstamp) @rm -f android/haltest$(EXEEXT) $(AM_V_CCLD)$(android_haltest_LINK) $(android_haltest_OBJECTS) $(android_haltest_LDADD) $(LIBS) emulator/android_ipc_tester-hciemu.$(OBJEXT): \ emulator/$(am__dirstamp) emulator/$(DEPDIR)/$(am__dirstamp) emulator/android_ipc_tester-vhci.$(OBJEXT): emulator/$(am__dirstamp) \ emulator/$(DEPDIR)/$(am__dirstamp) emulator/android_ipc_tester-btdev.$(OBJEXT): emulator/$(am__dirstamp) \ emulator/$(DEPDIR)/$(am__dirstamp) emulator/android_ipc_tester-bthost.$(OBJEXT): \ emulator/$(am__dirstamp) emulator/$(DEPDIR)/$(am__dirstamp) emulator/android_ipc_tester-smp.$(OBJEXT): emulator/$(am__dirstamp) \ emulator/$(DEPDIR)/$(am__dirstamp) android/ipc_tester-hal-utils.$(OBJEXT): android/$(am__dirstamp) \ android/$(DEPDIR)/$(am__dirstamp) android/ipc_tester-ipc-tester.$(OBJEXT): android/$(am__dirstamp) \ android/$(DEPDIR)/$(am__dirstamp) android/ipc-tester$(EXEEXT): $(android_ipc_tester_OBJECTS) $(android_ipc_tester_DEPENDENCIES) $(EXTRA_android_ipc_tester_DEPENDENCIES) android/$(am__dirstamp) @rm -f android/ipc-tester$(EXEEXT) $(AM_V_CCLD)$(LINK) $(android_ipc_tester_OBJECTS) $(android_ipc_tester_LDADD) $(LIBS) android/system-emulator.$(OBJEXT): android/$(am__dirstamp) \ android/$(DEPDIR)/$(am__dirstamp) android/system-emulator$(EXEEXT): $(android_system_emulator_OBJECTS) $(android_system_emulator_DEPENDENCIES) $(EXTRA_android_system_emulator_DEPENDENCIES) android/$(am__dirstamp) @rm -f android/system-emulator$(EXEEXT) $(AM_V_CCLD)$(LINK) $(android_system_emulator_OBJECTS) $(android_system_emulator_LDADD) $(LIBS) android/test-ipc.$(OBJEXT): android/$(am__dirstamp) \ android/$(DEPDIR)/$(am__dirstamp) android/test-ipc$(EXEEXT): $(android_test_ipc_OBJECTS) $(android_test_ipc_DEPENDENCIES) $(EXTRA_android_test_ipc_DEPENDENCIES) android/$(am__dirstamp) @rm -f android/test-ipc$(EXEEXT) $(AM_V_CCLD)$(LINK) $(android_test_ipc_OBJECTS) $(android_test_ipc_LDADD) $(LIBS) attrib/gatttool.$(OBJEXT): attrib/$(am__dirstamp) \ attrib/$(DEPDIR)/$(am__dirstamp) attrib/interactive.$(OBJEXT): attrib/$(am__dirstamp) \ attrib/$(DEPDIR)/$(am__dirstamp) attrib/utils.$(OBJEXT): attrib/$(am__dirstamp) \ attrib/$(DEPDIR)/$(am__dirstamp) client/$(am__dirstamp): @$(MKDIR_P) client @: > client/$(am__dirstamp) client/$(DEPDIR)/$(am__dirstamp): @$(MKDIR_P) client/$(DEPDIR) @: > client/$(DEPDIR)/$(am__dirstamp) client/display.$(OBJEXT): client/$(am__dirstamp) \ client/$(DEPDIR)/$(am__dirstamp) attrib/gatttool$(EXEEXT): $(attrib_gatttool_OBJECTS) $(attrib_gatttool_DEPENDENCIES) $(EXTRA_attrib_gatttool_DEPENDENCIES) attrib/$(am__dirstamp) @rm -f attrib/gatttool$(EXEEXT) $(AM_V_CCLD)$(LINK) $(attrib_gatttool_OBJECTS) $(attrib_gatttool_LDADD) $(LIBS) client/main.$(OBJEXT): client/$(am__dirstamp) \ client/$(DEPDIR)/$(am__dirstamp) client/print.$(OBJEXT): client/$(am__dirstamp) \ client/$(DEPDIR)/$(am__dirstamp) client/agent.$(OBJEXT): client/$(am__dirstamp) \ client/$(DEPDIR)/$(am__dirstamp) client/advertising.$(OBJEXT): client/$(am__dirstamp) \ client/$(DEPDIR)/$(am__dirstamp) client/adv_monitor.$(OBJEXT): client/$(am__dirstamp) \ client/$(DEPDIR)/$(am__dirstamp) client/gatt.$(OBJEXT): client/$(am__dirstamp) \ client/$(DEPDIR)/$(am__dirstamp) client/admin.$(OBJEXT): client/$(am__dirstamp) \ client/$(DEPDIR)/$(am__dirstamp) client/player.$(OBJEXT): client/$(am__dirstamp) \ client/$(DEPDIR)/$(am__dirstamp) client/mgmt.$(OBJEXT): client/$(am__dirstamp) \ client/$(DEPDIR)/$(am__dirstamp) client/assistant.$(OBJEXT): client/$(am__dirstamp) \ client/$(DEPDIR)/$(am__dirstamp) client/hci.$(OBJEXT): client/$(am__dirstamp) \ client/$(DEPDIR)/$(am__dirstamp) client/bluetoothctl$(EXEEXT): $(client_bluetoothctl_OBJECTS) $(client_bluetoothctl_DEPENDENCIES) $(EXTRA_client_bluetoothctl_DEPENDENCIES) client/$(am__dirstamp) @rm -f client/bluetoothctl$(EXEEXT) $(AM_V_CCLD)$(LINK) $(client_bluetoothctl_OBJECTS) $(client_bluetoothctl_LDADD) $(LIBS) emulator/b1ee.$(OBJEXT): emulator/$(am__dirstamp) \ emulator/$(DEPDIR)/$(am__dirstamp) emulator/b1ee$(EXEEXT): $(emulator_b1ee_OBJECTS) $(emulator_b1ee_DEPENDENCIES) $(EXTRA_emulator_b1ee_DEPENDENCIES) emulator/$(am__dirstamp) @rm -f emulator/b1ee$(EXEEXT) $(AM_V_CCLD)$(LINK) $(emulator_b1ee_OBJECTS) $(emulator_b1ee_LDADD) $(LIBS) emulator/main.$(OBJEXT): emulator/$(am__dirstamp) \ emulator/$(DEPDIR)/$(am__dirstamp) emulator/serial.$(OBJEXT): emulator/$(am__dirstamp) \ emulator/$(DEPDIR)/$(am__dirstamp) emulator/server.$(OBJEXT): emulator/$(am__dirstamp) \ emulator/$(DEPDIR)/$(am__dirstamp) emulator/vhci.$(OBJEXT): emulator/$(am__dirstamp) \ emulator/$(DEPDIR)/$(am__dirstamp) emulator/btdev.$(OBJEXT): emulator/$(am__dirstamp) \ emulator/$(DEPDIR)/$(am__dirstamp) emulator/bthost.$(OBJEXT): emulator/$(am__dirstamp) \ emulator/$(DEPDIR)/$(am__dirstamp) emulator/smp.$(OBJEXT): emulator/$(am__dirstamp) \ emulator/$(DEPDIR)/$(am__dirstamp) emulator/phy.$(OBJEXT): emulator/$(am__dirstamp) \ emulator/$(DEPDIR)/$(am__dirstamp) emulator/le.$(OBJEXT): emulator/$(am__dirstamp) \ emulator/$(DEPDIR)/$(am__dirstamp) emulator/btvirt$(EXEEXT): $(emulator_btvirt_OBJECTS) $(emulator_btvirt_DEPENDENCIES) $(EXTRA_emulator_btvirt_DEPENDENCIES) emulator/$(am__dirstamp) @rm -f emulator/btvirt$(EXEEXT) $(AM_V_CCLD)$(LINK) $(emulator_btvirt_OBJECTS) $(emulator_btvirt_LDADD) $(LIBS) emulator/hfp.$(OBJEXT): emulator/$(am__dirstamp) \ emulator/$(DEPDIR)/$(am__dirstamp) emulator/hfp$(EXEEXT): $(emulator_hfp_OBJECTS) $(emulator_hfp_DEPENDENCIES) $(EXTRA_emulator_hfp_DEPENDENCIES) emulator/$(am__dirstamp) @rm -f emulator/hfp$(EXEEXT) $(AM_V_CCLD)$(LINK) $(emulator_hfp_OBJECTS) $(emulator_hfp_LDADD) $(LIBS) mesh/$(am__dirstamp): @$(MKDIR_P) mesh @: > mesh/$(am__dirstamp) mesh/$(DEPDIR)/$(am__dirstamp): @$(MKDIR_P) mesh/$(DEPDIR) @: > mesh/$(DEPDIR)/$(am__dirstamp) mesh/mesh.$(OBJEXT): mesh/$(am__dirstamp) \ mesh/$(DEPDIR)/$(am__dirstamp) mesh/net-keys.$(OBJEXT): mesh/$(am__dirstamp) \ mesh/$(DEPDIR)/$(am__dirstamp) mesh/mesh-io.$(OBJEXT): mesh/$(am__dirstamp) \ mesh/$(DEPDIR)/$(am__dirstamp) mesh/mesh-mgmt.$(OBJEXT): mesh/$(am__dirstamp) \ mesh/$(DEPDIR)/$(am__dirstamp) mesh/mesh-io-unit.$(OBJEXT): mesh/$(am__dirstamp) \ mesh/$(DEPDIR)/$(am__dirstamp) mesh/mesh-io-mgmt.$(OBJEXT): mesh/$(am__dirstamp) \ mesh/$(DEPDIR)/$(am__dirstamp) mesh/mesh-io-generic.$(OBJEXT): mesh/$(am__dirstamp) \ mesh/$(DEPDIR)/$(am__dirstamp) mesh/net.$(OBJEXT): mesh/$(am__dirstamp) \ mesh/$(DEPDIR)/$(am__dirstamp) mesh/crypto.$(OBJEXT): mesh/$(am__dirstamp) \ mesh/$(DEPDIR)/$(am__dirstamp) mesh/friend.$(OBJEXT): mesh/$(am__dirstamp) \ mesh/$(DEPDIR)/$(am__dirstamp) mesh/appkey.$(OBJEXT): mesh/$(am__dirstamp) \ mesh/$(DEPDIR)/$(am__dirstamp) mesh/node.$(OBJEXT): mesh/$(am__dirstamp) \ mesh/$(DEPDIR)/$(am__dirstamp) mesh/model.$(OBJEXT): mesh/$(am__dirstamp) \ mesh/$(DEPDIR)/$(am__dirstamp) mesh/cfgmod-server.$(OBJEXT): mesh/$(am__dirstamp) \ mesh/$(DEPDIR)/$(am__dirstamp) mesh/remprv-server.$(OBJEXT): mesh/$(am__dirstamp) \ mesh/$(DEPDIR)/$(am__dirstamp) mesh/mesh-config-json.$(OBJEXT): mesh/$(am__dirstamp) \ mesh/$(DEPDIR)/$(am__dirstamp) mesh/util.$(OBJEXT): mesh/$(am__dirstamp) \ mesh/$(DEPDIR)/$(am__dirstamp) mesh/dbus.$(OBJEXT): mesh/$(am__dirstamp) \ mesh/$(DEPDIR)/$(am__dirstamp) mesh/agent.$(OBJEXT): mesh/$(am__dirstamp) \ mesh/$(DEPDIR)/$(am__dirstamp) mesh/prov-acceptor.$(OBJEXT): mesh/$(am__dirstamp) \ mesh/$(DEPDIR)/$(am__dirstamp) mesh/prov-initiator.$(OBJEXT): mesh/$(am__dirstamp) \ mesh/$(DEPDIR)/$(am__dirstamp) mesh/manager.$(OBJEXT): mesh/$(am__dirstamp) \ mesh/$(DEPDIR)/$(am__dirstamp) mesh/pb-adv.$(OBJEXT): mesh/$(am__dirstamp) \ mesh/$(DEPDIR)/$(am__dirstamp) mesh/keyring.$(OBJEXT): mesh/$(am__dirstamp) \ mesh/$(DEPDIR)/$(am__dirstamp) mesh/rpl.$(OBJEXT): mesh/$(am__dirstamp) \ mesh/$(DEPDIR)/$(am__dirstamp) mesh/prvbeac-server.$(OBJEXT): mesh/$(am__dirstamp) \ mesh/$(DEPDIR)/$(am__dirstamp) mesh/main.$(OBJEXT): mesh/$(am__dirstamp) \ mesh/$(DEPDIR)/$(am__dirstamp) mesh/bluetooth-meshd$(EXEEXT): $(mesh_bluetooth_meshd_OBJECTS) $(mesh_bluetooth_meshd_DEPENDENCIES) $(EXTRA_mesh_bluetooth_meshd_DEPENDENCIES) mesh/$(am__dirstamp) @rm -f mesh/bluetooth-meshd$(EXEEXT) $(AM_V_CCLD)$(LINK) $(mesh_bluetooth_meshd_OBJECTS) $(mesh_bluetooth_meshd_LDADD) $(LIBS) monitor/$(am__dirstamp): @$(MKDIR_P) monitor @: > monitor/$(am__dirstamp) monitor/$(DEPDIR)/$(am__dirstamp): @$(MKDIR_P) monitor/$(DEPDIR) @: > monitor/$(DEPDIR)/$(am__dirstamp) monitor/main.$(OBJEXT): monitor/$(am__dirstamp) \ monitor/$(DEPDIR)/$(am__dirstamp) monitor/display.$(OBJEXT): monitor/$(am__dirstamp) \ monitor/$(DEPDIR)/$(am__dirstamp) monitor/hcidump.$(OBJEXT): monitor/$(am__dirstamp) \ monitor/$(DEPDIR)/$(am__dirstamp) monitor/ellisys.$(OBJEXT): monitor/$(am__dirstamp) \ monitor/$(DEPDIR)/$(am__dirstamp) monitor/control.$(OBJEXT): monitor/$(am__dirstamp) \ monitor/$(DEPDIR)/$(am__dirstamp) monitor/packet.$(OBJEXT): monitor/$(am__dirstamp) \ monitor/$(DEPDIR)/$(am__dirstamp) monitor/vendor.$(OBJEXT): monitor/$(am__dirstamp) \ monitor/$(DEPDIR)/$(am__dirstamp) monitor/lmp.$(OBJEXT): monitor/$(am__dirstamp) \ monitor/$(DEPDIR)/$(am__dirstamp) monitor/crc.$(OBJEXT): monitor/$(am__dirstamp) \ monitor/$(DEPDIR)/$(am__dirstamp) monitor/ll.$(OBJEXT): monitor/$(am__dirstamp) \ monitor/$(DEPDIR)/$(am__dirstamp) monitor/l2cap.$(OBJEXT): monitor/$(am__dirstamp) \ monitor/$(DEPDIR)/$(am__dirstamp) monitor/sdp.$(OBJEXT): monitor/$(am__dirstamp) \ monitor/$(DEPDIR)/$(am__dirstamp) monitor/avctp.$(OBJEXT): monitor/$(am__dirstamp) \ monitor/$(DEPDIR)/$(am__dirstamp) monitor/avdtp.$(OBJEXT): monitor/$(am__dirstamp) \ monitor/$(DEPDIR)/$(am__dirstamp) monitor/a2dp.$(OBJEXT): monitor/$(am__dirstamp) \ monitor/$(DEPDIR)/$(am__dirstamp) monitor/rfcomm.$(OBJEXT): monitor/$(am__dirstamp) \ monitor/$(DEPDIR)/$(am__dirstamp) monitor/bnep.$(OBJEXT): monitor/$(am__dirstamp) \ monitor/$(DEPDIR)/$(am__dirstamp) monitor/hwdb.$(OBJEXT): monitor/$(am__dirstamp) \ monitor/$(DEPDIR)/$(am__dirstamp) monitor/keys.$(OBJEXT): monitor/$(am__dirstamp) \ monitor/$(DEPDIR)/$(am__dirstamp) monitor/analyze.$(OBJEXT): monitor/$(am__dirstamp) \ monitor/$(DEPDIR)/$(am__dirstamp) monitor/intel.$(OBJEXT): monitor/$(am__dirstamp) \ monitor/$(DEPDIR)/$(am__dirstamp) monitor/broadcom.$(OBJEXT): monitor/$(am__dirstamp) \ monitor/$(DEPDIR)/$(am__dirstamp) monitor/msft.$(OBJEXT): monitor/$(am__dirstamp) \ monitor/$(DEPDIR)/$(am__dirstamp) monitor/jlink.$(OBJEXT): monitor/$(am__dirstamp) \ monitor/$(DEPDIR)/$(am__dirstamp) monitor/att.$(OBJEXT): monitor/$(am__dirstamp) \ monitor/$(DEPDIR)/$(am__dirstamp) src/textfile.$(OBJEXT): src/$(am__dirstamp) \ src/$(DEPDIR)/$(am__dirstamp) src/settings.$(OBJEXT): src/$(am__dirstamp) \ src/$(DEPDIR)/$(am__dirstamp) monitor/btmon$(EXEEXT): $(monitor_btmon_OBJECTS) $(monitor_btmon_DEPENDENCIES) $(EXTRA_monitor_btmon_DEPENDENCIES) monitor/$(am__dirstamp) @rm -f monitor/btmon$(EXEEXT) $(AM_V_CCLD)$(LINK) $(monitor_btmon_OBJECTS) $(monitor_btmon_LDADD) $(LIBS) btio/obexd-btio.$(OBJEXT): btio/$(am__dirstamp) \ btio/$(DEPDIR)/$(am__dirstamp) gobex/$(am__dirstamp): @$(MKDIR_P) gobex @: > gobex/$(am__dirstamp) gobex/$(DEPDIR)/$(am__dirstamp): @$(MKDIR_P) gobex/$(DEPDIR) @: > gobex/$(DEPDIR)/$(am__dirstamp) gobex/obexd-gobex.$(OBJEXT): gobex/$(am__dirstamp) \ gobex/$(DEPDIR)/$(am__dirstamp) gobex/obexd-gobex-defs.$(OBJEXT): gobex/$(am__dirstamp) \ gobex/$(DEPDIR)/$(am__dirstamp) gobex/obexd-gobex-packet.$(OBJEXT): gobex/$(am__dirstamp) \ gobex/$(DEPDIR)/$(am__dirstamp) gobex/obexd-gobex-header.$(OBJEXT): gobex/$(am__dirstamp) \ gobex/$(DEPDIR)/$(am__dirstamp) gobex/obexd-gobex-transfer.$(OBJEXT): gobex/$(am__dirstamp) \ gobex/$(DEPDIR)/$(am__dirstamp) gobex/obexd-gobex-apparam.$(OBJEXT): gobex/$(am__dirstamp) \ gobex/$(DEPDIR)/$(am__dirstamp) obexd/plugins/$(am__dirstamp): @$(MKDIR_P) obexd/plugins @: > obexd/plugins/$(am__dirstamp) obexd/plugins/$(DEPDIR)/$(am__dirstamp): @$(MKDIR_P) obexd/plugins/$(DEPDIR) @: > obexd/plugins/$(DEPDIR)/$(am__dirstamp) obexd/plugins/obexd-filesystem.$(OBJEXT): \ obexd/plugins/$(am__dirstamp) \ obexd/plugins/$(DEPDIR)/$(am__dirstamp) obexd/plugins/obexd-bluetooth.$(OBJEXT): \ obexd/plugins/$(am__dirstamp) \ obexd/plugins/$(DEPDIR)/$(am__dirstamp) obexd/plugins/obexd-pcsuite.$(OBJEXT): obexd/plugins/$(am__dirstamp) \ obexd/plugins/$(DEPDIR)/$(am__dirstamp) obexd/plugins/obexd-opp.$(OBJEXT): obexd/plugins/$(am__dirstamp) \ obexd/plugins/$(DEPDIR)/$(am__dirstamp) obexd/plugins/obexd-ftp.$(OBJEXT): obexd/plugins/$(am__dirstamp) \ obexd/plugins/$(DEPDIR)/$(am__dirstamp) obexd/plugins/obexd-irmc.$(OBJEXT): obexd/plugins/$(am__dirstamp) \ obexd/plugins/$(DEPDIR)/$(am__dirstamp) obexd/plugins/obexd-pbap.$(OBJEXT): obexd/plugins/$(am__dirstamp) \ obexd/plugins/$(DEPDIR)/$(am__dirstamp) obexd/plugins/obexd-vcard.$(OBJEXT): obexd/plugins/$(am__dirstamp) \ obexd/plugins/$(DEPDIR)/$(am__dirstamp) obexd/plugins/obexd-phonebook-@PLUGIN_PHONEBOOK@.$(OBJEXT): \ obexd/plugins/$(am__dirstamp) \ obexd/plugins/$(DEPDIR)/$(am__dirstamp) obexd/plugins/obexd-mas.$(OBJEXT): obexd/plugins/$(am__dirstamp) \ obexd/plugins/$(DEPDIR)/$(am__dirstamp) obexd/plugins/obexd-messages-dummy.$(OBJEXT): \ obexd/plugins/$(am__dirstamp) \ obexd/plugins/$(DEPDIR)/$(am__dirstamp) obexd/client/$(am__dirstamp): @$(MKDIR_P) obexd/client @: > obexd/client/$(am__dirstamp) obexd/client/$(DEPDIR)/$(am__dirstamp): @$(MKDIR_P) obexd/client/$(DEPDIR) @: > obexd/client/$(DEPDIR)/$(am__dirstamp) obexd/client/obexd-mns.$(OBJEXT): obexd/client/$(am__dirstamp) \ obexd/client/$(DEPDIR)/$(am__dirstamp) obexd/src/$(am__dirstamp): @$(MKDIR_P) obexd/src @: > obexd/src/$(am__dirstamp) obexd/src/$(DEPDIR)/$(am__dirstamp): @$(MKDIR_P) obexd/src/$(DEPDIR) @: > obexd/src/$(DEPDIR)/$(am__dirstamp) obexd/src/obexd-main.$(OBJEXT): obexd/src/$(am__dirstamp) \ obexd/src/$(DEPDIR)/$(am__dirstamp) obexd/src/obexd-plugin.$(OBJEXT): obexd/src/$(am__dirstamp) \ obexd/src/$(DEPDIR)/$(am__dirstamp) obexd/src/obexd-log.$(OBJEXT): obexd/src/$(am__dirstamp) \ obexd/src/$(DEPDIR)/$(am__dirstamp) obexd/src/obexd-manager.$(OBJEXT): obexd/src/$(am__dirstamp) \ obexd/src/$(DEPDIR)/$(am__dirstamp) obexd/src/obexd-obex.$(OBJEXT): obexd/src/$(am__dirstamp) \ obexd/src/$(DEPDIR)/$(am__dirstamp) obexd/src/obexd-mimetype.$(OBJEXT): obexd/src/$(am__dirstamp) \ obexd/src/$(DEPDIR)/$(am__dirstamp) obexd/src/obexd-service.$(OBJEXT): obexd/src/$(am__dirstamp) \ obexd/src/$(DEPDIR)/$(am__dirstamp) obexd/src/obexd-transport.$(OBJEXT): obexd/src/$(am__dirstamp) \ obexd/src/$(DEPDIR)/$(am__dirstamp) obexd/src/obexd-server.$(OBJEXT): obexd/src/$(am__dirstamp) \ obexd/src/$(DEPDIR)/$(am__dirstamp) obexd/client/obexd-manager.$(OBJEXT): obexd/client/$(am__dirstamp) \ obexd/client/$(DEPDIR)/$(am__dirstamp) obexd/client/obexd-session.$(OBJEXT): obexd/client/$(am__dirstamp) \ obexd/client/$(DEPDIR)/$(am__dirstamp) obexd/client/obexd-bluetooth.$(OBJEXT): obexd/client/$(am__dirstamp) \ obexd/client/$(DEPDIR)/$(am__dirstamp) obexd/client/obexd-sync.$(OBJEXT): obexd/client/$(am__dirstamp) \ obexd/client/$(DEPDIR)/$(am__dirstamp) obexd/client/obexd-pbap.$(OBJEXT): obexd/client/$(am__dirstamp) \ obexd/client/$(DEPDIR)/$(am__dirstamp) obexd/client/obexd-ftp.$(OBJEXT): obexd/client/$(am__dirstamp) \ obexd/client/$(DEPDIR)/$(am__dirstamp) obexd/client/obexd-opp.$(OBJEXT): obexd/client/$(am__dirstamp) \ obexd/client/$(DEPDIR)/$(am__dirstamp) obexd/client/obexd-map.$(OBJEXT): obexd/client/$(am__dirstamp) \ obexd/client/$(DEPDIR)/$(am__dirstamp) obexd/client/obexd-bip.$(OBJEXT): obexd/client/$(am__dirstamp) \ obexd/client/$(DEPDIR)/$(am__dirstamp) obexd/client/obexd-bip-common.$(OBJEXT): obexd/client/$(am__dirstamp) \ obexd/client/$(DEPDIR)/$(am__dirstamp) obexd/client/obexd-map-event.$(OBJEXT): obexd/client/$(am__dirstamp) \ obexd/client/$(DEPDIR)/$(am__dirstamp) obexd/client/obexd-transfer.$(OBJEXT): obexd/client/$(am__dirstamp) \ obexd/client/$(DEPDIR)/$(am__dirstamp) obexd/client/obexd-transport.$(OBJEXT): obexd/client/$(am__dirstamp) \ obexd/client/$(DEPDIR)/$(am__dirstamp) obexd/client/obexd-driver.$(OBJEXT): obexd/client/$(am__dirstamp) \ obexd/client/$(DEPDIR)/$(am__dirstamp) obexd/src/obexd$(EXEEXT): $(obexd_src_obexd_OBJECTS) $(obexd_src_obexd_DEPENDENCIES) $(EXTRA_obexd_src_obexd_DEPENDENCIES) obexd/src/$(am__dirstamp) @rm -f obexd/src/obexd$(EXEEXT) $(AM_V_CCLD)$(obexd_src_obexd_LINK) $(obexd_src_obexd_OBJECTS) $(obexd_src_obexd_LDADD) $(LIBS) peripheral/$(am__dirstamp): @$(MKDIR_P) peripheral @: > peripheral/$(am__dirstamp) peripheral/$(DEPDIR)/$(am__dirstamp): @$(MKDIR_P) peripheral/$(DEPDIR) @: > peripheral/$(DEPDIR)/$(am__dirstamp) peripheral/main.$(OBJEXT): peripheral/$(am__dirstamp) \ peripheral/$(DEPDIR)/$(am__dirstamp) peripheral/efivars.$(OBJEXT): peripheral/$(am__dirstamp) \ peripheral/$(DEPDIR)/$(am__dirstamp) peripheral/attach.$(OBJEXT): peripheral/$(am__dirstamp) \ peripheral/$(DEPDIR)/$(am__dirstamp) peripheral/log.$(OBJEXT): peripheral/$(am__dirstamp) \ peripheral/$(DEPDIR)/$(am__dirstamp) peripheral/gap.$(OBJEXT): peripheral/$(am__dirstamp) \ peripheral/$(DEPDIR)/$(am__dirstamp) peripheral/gatt.$(OBJEXT): peripheral/$(am__dirstamp) \ peripheral/$(DEPDIR)/$(am__dirstamp) peripheral/btsensor$(EXEEXT): $(peripheral_btsensor_OBJECTS) $(peripheral_btsensor_DEPENDENCIES) $(EXTRA_peripheral_btsensor_DEPENDENCIES) peripheral/$(am__dirstamp) @rm -f peripheral/btsensor$(EXEEXT) $(AM_V_CCLD)$(LINK) $(peripheral_btsensor_OBJECTS) $(peripheral_btsensor_LDADD) $(LIBS) profiles/cups/$(am__dirstamp): @$(MKDIR_P) profiles/cups @: > profiles/cups/$(am__dirstamp) profiles/cups/$(DEPDIR)/$(am__dirstamp): @$(MKDIR_P) profiles/cups/$(DEPDIR) @: > profiles/cups/$(DEPDIR)/$(am__dirstamp) profiles/cups/main.$(OBJEXT): profiles/cups/$(am__dirstamp) \ profiles/cups/$(DEPDIR)/$(am__dirstamp) profiles/cups/sdp.$(OBJEXT): profiles/cups/$(am__dirstamp) \ profiles/cups/$(DEPDIR)/$(am__dirstamp) profiles/cups/spp.$(OBJEXT): profiles/cups/$(am__dirstamp) \ profiles/cups/$(DEPDIR)/$(am__dirstamp) profiles/cups/hcrp.$(OBJEXT): profiles/cups/$(am__dirstamp) \ profiles/cups/$(DEPDIR)/$(am__dirstamp) profiles/cups/bluetooth$(EXEEXT): $(profiles_cups_bluetooth_OBJECTS) $(profiles_cups_bluetooth_DEPENDENCIES) $(EXTRA_profiles_cups_bluetooth_DEPENDENCIES) profiles/cups/$(am__dirstamp) @rm -f profiles/cups/bluetooth$(EXEEXT) $(AM_V_CCLD)$(LINK) $(profiles_cups_bluetooth_OBJECTS) $(profiles_cups_bluetooth_LDADD) $(LIBS) profiles/iap/$(am__dirstamp): @$(MKDIR_P) profiles/iap @: > profiles/iap/$(am__dirstamp) profiles/iap/$(DEPDIR)/$(am__dirstamp): @$(MKDIR_P) profiles/iap/$(DEPDIR) @: > profiles/iap/$(DEPDIR)/$(am__dirstamp) profiles/iap/main.$(OBJEXT): profiles/iap/$(am__dirstamp) \ profiles/iap/$(DEPDIR)/$(am__dirstamp) profiles/iap/iapd$(EXEEXT): $(profiles_iap_iapd_OBJECTS) $(profiles_iap_iapd_DEPENDENCIES) $(EXTRA_profiles_iap_iapd_DEPENDENCIES) profiles/iap/$(am__dirstamp) @rm -f profiles/iap/iapd$(EXEEXT) $(AM_V_CCLD)$(LINK) $(profiles_iap_iapd_OBJECTS) $(profiles_iap_iapd_LDADD) $(LIBS) plugins/$(am__dirstamp): @$(MKDIR_P) plugins @: > plugins/$(am__dirstamp) plugins/$(DEPDIR)/$(am__dirstamp): @$(MKDIR_P) plugins/$(DEPDIR) @: > plugins/$(DEPDIR)/$(am__dirstamp) plugins/bluetoothd-hostname.$(OBJEXT): plugins/$(am__dirstamp) \ plugins/$(DEPDIR)/$(am__dirstamp) plugins/bluetoothd-autopair.$(OBJEXT): plugins/$(am__dirstamp) \ plugins/$(DEPDIR)/$(am__dirstamp) plugins/bluetoothd-policy.$(OBJEXT): plugins/$(am__dirstamp) \ plugins/$(DEPDIR)/$(am__dirstamp) profiles/audio/$(am__dirstamp): @$(MKDIR_P) profiles/audio @: > profiles/audio/$(am__dirstamp) profiles/audio/$(DEPDIR)/$(am__dirstamp): @$(MKDIR_P) profiles/audio/$(DEPDIR) @: > profiles/audio/$(DEPDIR)/$(am__dirstamp) profiles/audio/bluetoothd-media.$(OBJEXT): \ profiles/audio/$(am__dirstamp) \ profiles/audio/$(DEPDIR)/$(am__dirstamp) profiles/audio/bluetoothd-transport.$(OBJEXT): \ profiles/audio/$(am__dirstamp) \ profiles/audio/$(DEPDIR)/$(am__dirstamp) profiles/audio/bluetoothd-player.$(OBJEXT): \ profiles/audio/$(am__dirstamp) \ profiles/audio/$(DEPDIR)/$(am__dirstamp) plugins/bluetoothd-admin.$(OBJEXT): plugins/$(am__dirstamp) \ plugins/$(DEPDIR)/$(am__dirstamp) plugins/bluetoothd-neard.$(OBJEXT): plugins/$(am__dirstamp) \ plugins/$(DEPDIR)/$(am__dirstamp) profiles/sap/$(am__dirstamp): @$(MKDIR_P) profiles/sap @: > profiles/sap/$(am__dirstamp) profiles/sap/$(DEPDIR)/$(am__dirstamp): @$(MKDIR_P) profiles/sap/$(DEPDIR) @: > profiles/sap/$(DEPDIR)/$(am__dirstamp) profiles/sap/bluetoothd-main.$(OBJEXT): profiles/sap/$(am__dirstamp) \ profiles/sap/$(DEPDIR)/$(am__dirstamp) profiles/sap/bluetoothd-manager.$(OBJEXT): \ profiles/sap/$(am__dirstamp) \ profiles/sap/$(DEPDIR)/$(am__dirstamp) profiles/sap/bluetoothd-server.$(OBJEXT): \ profiles/sap/$(am__dirstamp) \ profiles/sap/$(DEPDIR)/$(am__dirstamp) profiles/sap/bluetoothd-sap-dummy.$(OBJEXT): \ profiles/sap/$(am__dirstamp) \ profiles/sap/$(DEPDIR)/$(am__dirstamp) profiles/audio/bluetoothd-source.$(OBJEXT): \ profiles/audio/$(am__dirstamp) \ profiles/audio/$(DEPDIR)/$(am__dirstamp) profiles/audio/bluetoothd-sink.$(OBJEXT): \ profiles/audio/$(am__dirstamp) \ profiles/audio/$(DEPDIR)/$(am__dirstamp) profiles/audio/bluetoothd-a2dp.$(OBJEXT): \ profiles/audio/$(am__dirstamp) \ profiles/audio/$(DEPDIR)/$(am__dirstamp) profiles/audio/bluetoothd-avdtp.$(OBJEXT): \ profiles/audio/$(am__dirstamp) \ profiles/audio/$(DEPDIR)/$(am__dirstamp) profiles/audio/bluetoothd-control.$(OBJEXT): \ profiles/audio/$(am__dirstamp) \ profiles/audio/$(DEPDIR)/$(am__dirstamp) profiles/audio/bluetoothd-avctp.$(OBJEXT): \ profiles/audio/$(am__dirstamp) \ profiles/audio/$(DEPDIR)/$(am__dirstamp) profiles/audio/bluetoothd-avrcp.$(OBJEXT): \ profiles/audio/$(am__dirstamp) \ profiles/audio/$(DEPDIR)/$(am__dirstamp) profiles/network/bluetoothd-manager.$(OBJEXT): \ profiles/network/$(am__dirstamp) \ profiles/network/$(DEPDIR)/$(am__dirstamp) profiles/network/bluetoothd-bnep.$(OBJEXT): \ profiles/network/$(am__dirstamp) \ profiles/network/$(DEPDIR)/$(am__dirstamp) profiles/network/bluetoothd-server.$(OBJEXT): \ profiles/network/$(am__dirstamp) \ profiles/network/$(DEPDIR)/$(am__dirstamp) profiles/network/bluetoothd-connection.$(OBJEXT): \ profiles/network/$(am__dirstamp) \ profiles/network/$(DEPDIR)/$(am__dirstamp) profiles/input/bluetoothd-manager.$(OBJEXT): \ profiles/input/$(am__dirstamp) \ profiles/input/$(DEPDIR)/$(am__dirstamp) profiles/input/bluetoothd-server.$(OBJEXT): \ profiles/input/$(am__dirstamp) \ profiles/input/$(DEPDIR)/$(am__dirstamp) profiles/input/bluetoothd-device.$(OBJEXT): \ profiles/input/$(am__dirstamp) \ profiles/input/$(DEPDIR)/$(am__dirstamp) profiles/input/bluetoothd-hog.$(OBJEXT): \ profiles/input/$(am__dirstamp) \ profiles/input/$(DEPDIR)/$(am__dirstamp) profiles/input/bluetoothd-hog-lib.$(OBJEXT): \ profiles/input/$(am__dirstamp) \ profiles/input/$(DEPDIR)/$(am__dirstamp) profiles/deviceinfo/bluetoothd-dis.$(OBJEXT): \ profiles/deviceinfo/$(am__dirstamp) \ profiles/deviceinfo/$(DEPDIR)/$(am__dirstamp) profiles/battery/bluetoothd-bas.$(OBJEXT): \ profiles/battery/$(am__dirstamp) \ profiles/battery/$(DEPDIR)/$(am__dirstamp) profiles/scanparam/bluetoothd-scpp.$(OBJEXT): \ profiles/scanparam/$(am__dirstamp) \ profiles/scanparam/$(DEPDIR)/$(am__dirstamp) profiles/input/bluetoothd-suspend-none.$(OBJEXT): \ profiles/input/$(am__dirstamp) \ profiles/input/$(DEPDIR)/$(am__dirstamp) profiles/health/bluetoothd-mcap.$(OBJEXT): \ profiles/health/$(am__dirstamp) \ profiles/health/$(DEPDIR)/$(am__dirstamp) profiles/health/bluetoothd-hdp_main.$(OBJEXT): \ profiles/health/$(am__dirstamp) \ profiles/health/$(DEPDIR)/$(am__dirstamp) profiles/health/bluetoothd-hdp_manager.$(OBJEXT): \ profiles/health/$(am__dirstamp) \ profiles/health/$(DEPDIR)/$(am__dirstamp) profiles/health/bluetoothd-hdp.$(OBJEXT): \ profiles/health/$(am__dirstamp) \ profiles/health/$(DEPDIR)/$(am__dirstamp) profiles/health/bluetoothd-hdp_util.$(OBJEXT): \ profiles/health/$(am__dirstamp) \ profiles/health/$(DEPDIR)/$(am__dirstamp) profiles/gap/$(am__dirstamp): @$(MKDIR_P) profiles/gap @: > profiles/gap/$(am__dirstamp) profiles/gap/$(DEPDIR)/$(am__dirstamp): @$(MKDIR_P) profiles/gap/$(DEPDIR) @: > profiles/gap/$(DEPDIR)/$(am__dirstamp) profiles/gap/bluetoothd-gas.$(OBJEXT): profiles/gap/$(am__dirstamp) \ profiles/gap/$(DEPDIR)/$(am__dirstamp) profiles/scanparam/bluetoothd-scan.$(OBJEXT): \ profiles/scanparam/$(am__dirstamp) \ profiles/scanparam/$(DEPDIR)/$(am__dirstamp) profiles/deviceinfo/bluetoothd-deviceinfo.$(OBJEXT): \ profiles/deviceinfo/$(am__dirstamp) \ profiles/deviceinfo/$(DEPDIR)/$(am__dirstamp) profiles/midi/$(am__dirstamp): @$(MKDIR_P) profiles/midi @: > profiles/midi/$(am__dirstamp) profiles/midi/$(DEPDIR)/$(am__dirstamp): @$(MKDIR_P) profiles/midi/$(DEPDIR) @: > profiles/midi/$(DEPDIR)/$(am__dirstamp) profiles/midi/bluetoothd-midi.$(OBJEXT): \ profiles/midi/$(am__dirstamp) \ profiles/midi/$(DEPDIR)/$(am__dirstamp) profiles/midi/bluetoothd-libmidi.$(OBJEXT): \ profiles/midi/$(am__dirstamp) \ profiles/midi/$(DEPDIR)/$(am__dirstamp) profiles/battery/bluetoothd-battery.$(OBJEXT): \ profiles/battery/$(am__dirstamp) \ profiles/battery/$(DEPDIR)/$(am__dirstamp) plugins/bluetoothd-sixaxis.$(OBJEXT): plugins/$(am__dirstamp) \ plugins/$(DEPDIR)/$(am__dirstamp) profiles/audio/bluetoothd-bap.$(OBJEXT): \ profiles/audio/$(am__dirstamp) \ profiles/audio/$(DEPDIR)/$(am__dirstamp) profiles/audio/bluetoothd-bass.$(OBJEXT): \ profiles/audio/$(am__dirstamp) \ profiles/audio/$(DEPDIR)/$(am__dirstamp) profiles/audio/bluetoothd-mcp.$(OBJEXT): \ profiles/audio/$(am__dirstamp) \ profiles/audio/$(DEPDIR)/$(am__dirstamp) profiles/audio/bluetoothd-vcp.$(OBJEXT): \ profiles/audio/$(am__dirstamp) \ profiles/audio/$(DEPDIR)/$(am__dirstamp) profiles/audio/bluetoothd-micp.$(OBJEXT): \ profiles/audio/$(am__dirstamp) \ profiles/audio/$(DEPDIR)/$(am__dirstamp) profiles/audio/bluetoothd-ccp.$(OBJEXT): \ profiles/audio/$(am__dirstamp) \ profiles/audio/$(DEPDIR)/$(am__dirstamp) profiles/audio/bluetoothd-csip.$(OBJEXT): \ profiles/audio/$(am__dirstamp) \ profiles/audio/$(DEPDIR)/$(am__dirstamp) profiles/audio/bluetoothd-asha.$(OBJEXT): \ profiles/audio/$(am__dirstamp) \ profiles/audio/$(DEPDIR)/$(am__dirstamp) attrib/bluetoothd-att.$(OBJEXT): attrib/$(am__dirstamp) \ attrib/$(DEPDIR)/$(am__dirstamp) attrib/bluetoothd-gatt.$(OBJEXT): attrib/$(am__dirstamp) \ attrib/$(DEPDIR)/$(am__dirstamp) attrib/bluetoothd-gattrib.$(OBJEXT): attrib/$(am__dirstamp) \ attrib/$(DEPDIR)/$(am__dirstamp) btio/bluetoothd-btio.$(OBJEXT): btio/$(am__dirstamp) \ btio/$(DEPDIR)/$(am__dirstamp) src/bluetoothd-main.$(OBJEXT): src/$(am__dirstamp) \ src/$(DEPDIR)/$(am__dirstamp) src/bluetoothd-log.$(OBJEXT): src/$(am__dirstamp) \ src/$(DEPDIR)/$(am__dirstamp) src/bluetoothd-backtrace.$(OBJEXT): src/$(am__dirstamp) \ src/$(DEPDIR)/$(am__dirstamp) src/bluetoothd-rfkill.$(OBJEXT): src/$(am__dirstamp) \ src/$(DEPDIR)/$(am__dirstamp) src/bluetoothd-sdpd-server.$(OBJEXT): src/$(am__dirstamp) \ src/$(DEPDIR)/$(am__dirstamp) src/bluetoothd-sdpd-request.$(OBJEXT): src/$(am__dirstamp) \ src/$(DEPDIR)/$(am__dirstamp) src/bluetoothd-sdpd-service.$(OBJEXT): src/$(am__dirstamp) \ src/$(DEPDIR)/$(am__dirstamp) src/bluetoothd-sdpd-database.$(OBJEXT): src/$(am__dirstamp) \ src/$(DEPDIR)/$(am__dirstamp) src/bluetoothd-gatt-database.$(OBJEXT): src/$(am__dirstamp) \ src/$(DEPDIR)/$(am__dirstamp) src/bluetoothd-sdp-xml.$(OBJEXT): src/$(am__dirstamp) \ src/$(DEPDIR)/$(am__dirstamp) src/bluetoothd-sdp-client.$(OBJEXT): src/$(am__dirstamp) \ src/$(DEPDIR)/$(am__dirstamp) src/bluetoothd-textfile.$(OBJEXT): src/$(am__dirstamp) \ src/$(DEPDIR)/$(am__dirstamp) src/bluetoothd-uuid-helper.$(OBJEXT): src/$(am__dirstamp) \ src/$(DEPDIR)/$(am__dirstamp) src/bluetoothd-plugin.$(OBJEXT): src/$(am__dirstamp) \ src/$(DEPDIR)/$(am__dirstamp) src/bluetoothd-storage.$(OBJEXT): src/$(am__dirstamp) \ src/$(DEPDIR)/$(am__dirstamp) src/bluetoothd-advertising.$(OBJEXT): src/$(am__dirstamp) \ src/$(DEPDIR)/$(am__dirstamp) src/bluetoothd-agent.$(OBJEXT): src/$(am__dirstamp) \ src/$(DEPDIR)/$(am__dirstamp) src/bluetoothd-error.$(OBJEXT): src/$(am__dirstamp) \ src/$(DEPDIR)/$(am__dirstamp) src/bluetoothd-adapter.$(OBJEXT): src/$(am__dirstamp) \ src/$(DEPDIR)/$(am__dirstamp) src/bluetoothd-profile.$(OBJEXT): src/$(am__dirstamp) \ src/$(DEPDIR)/$(am__dirstamp) src/bluetoothd-service.$(OBJEXT): src/$(am__dirstamp) \ src/$(DEPDIR)/$(am__dirstamp) src/bluetoothd-gatt-client.$(OBJEXT): src/$(am__dirstamp) \ src/$(DEPDIR)/$(am__dirstamp) src/bluetoothd-device.$(OBJEXT): src/$(am__dirstamp) \ src/$(DEPDIR)/$(am__dirstamp) src/bluetoothd-dbus-common.$(OBJEXT): src/$(am__dirstamp) \ src/$(DEPDIR)/$(am__dirstamp) src/bluetoothd-eir.$(OBJEXT): src/$(am__dirstamp) \ src/$(DEPDIR)/$(am__dirstamp) src/bluetoothd-adv_monitor.$(OBJEXT): src/$(am__dirstamp) \ src/$(DEPDIR)/$(am__dirstamp) src/bluetoothd-battery.$(OBJEXT): src/$(am__dirstamp) \ src/$(DEPDIR)/$(am__dirstamp) src/bluetoothd-settings.$(OBJEXT): src/$(am__dirstamp) \ src/$(DEPDIR)/$(am__dirstamp) src/bluetoothd-set.$(OBJEXT): src/$(am__dirstamp) \ src/$(DEPDIR)/$(am__dirstamp) src/bluetoothd$(EXEEXT): $(src_bluetoothd_OBJECTS) $(src_bluetoothd_DEPENDENCIES) $(EXTRA_src_bluetoothd_DEPENDENCIES) src/$(am__dirstamp) @rm -f src/bluetoothd$(EXEEXT) $(AM_V_CCLD)$(src_bluetoothd_LINK) $(src_bluetoothd_OBJECTS) $(src_bluetoothd_LDADD) $(LIBS) tools/$(am__dirstamp): @$(MKDIR_P) tools @: > tools/$(am__dirstamp) tools/$(DEPDIR)/$(am__dirstamp): @$(MKDIR_P) tools/$(DEPDIR) @: > tools/$(DEPDIR)/$(am__dirstamp) tools/3dsp.$(OBJEXT): tools/$(am__dirstamp) \ tools/$(DEPDIR)/$(am__dirstamp) tools/3dsp$(EXEEXT): $(tools_3dsp_OBJECTS) $(tools_3dsp_DEPENDENCIES) $(EXTRA_tools_3dsp_DEPENDENCIES) tools/$(am__dirstamp) @rm -f tools/3dsp$(EXEEXT) $(AM_V_CCLD)$(LINK) $(tools_3dsp_OBJECTS) $(tools_3dsp_LDADD) $(LIBS) tools/advtest.$(OBJEXT): tools/$(am__dirstamp) \ tools/$(DEPDIR)/$(am__dirstamp) tools/advtest$(EXEEXT): $(tools_advtest_OBJECTS) $(tools_advtest_DEPENDENCIES) $(EXTRA_tools_advtest_DEPENDENCIES) tools/$(am__dirstamp) @rm -f tools/advtest$(EXEEXT) $(AM_V_CCLD)$(LINK) $(tools_advtest_OBJECTS) $(tools_advtest_LDADD) $(LIBS) tools/avinfo.$(OBJEXT): tools/$(am__dirstamp) \ tools/$(DEPDIR)/$(am__dirstamp) tools/avinfo$(EXEEXT): $(tools_avinfo_OBJECTS) $(tools_avinfo_DEPENDENCIES) $(EXTRA_tools_avinfo_DEPENDENCIES) tools/$(am__dirstamp) @rm -f tools/avinfo$(EXEEXT) $(AM_V_CCLD)$(LINK) $(tools_avinfo_OBJECTS) $(tools_avinfo_LDADD) $(LIBS) tools/avtest.$(OBJEXT): tools/$(am__dirstamp) \ tools/$(DEPDIR)/$(am__dirstamp) tools/avtest$(EXEEXT): $(tools_avtest_OBJECTS) $(tools_avtest_DEPENDENCIES) $(EXTRA_tools_avtest_DEPENDENCIES) tools/$(am__dirstamp) @rm -f tools/avtest$(EXEEXT) $(AM_V_CCLD)$(LINK) $(tools_avtest_OBJECTS) $(tools_avtest_LDADD) $(LIBS) tools/bcmfw.$(OBJEXT): tools/$(am__dirstamp) \ tools/$(DEPDIR)/$(am__dirstamp) tools/bcmfw$(EXEEXT): $(tools_bcmfw_OBJECTS) $(tools_bcmfw_DEPENDENCIES) $(EXTRA_tools_bcmfw_DEPENDENCIES) tools/$(am__dirstamp) @rm -f tools/bcmfw$(EXEEXT) $(AM_V_CCLD)$(LINK) $(tools_bcmfw_OBJECTS) $(tools_bcmfw_LDADD) $(LIBS) tools/bdaddr.$(OBJEXT): tools/$(am__dirstamp) \ tools/$(DEPDIR)/$(am__dirstamp) src/oui.$(OBJEXT): src/$(am__dirstamp) src/$(DEPDIR)/$(am__dirstamp) tools/bdaddr$(EXEEXT): $(tools_bdaddr_OBJECTS) $(tools_bdaddr_DEPENDENCIES) $(EXTRA_tools_bdaddr_DEPENDENCIES) tools/$(am__dirstamp) @rm -f tools/bdaddr$(EXEEXT) $(AM_V_CCLD)$(LINK) $(tools_bdaddr_OBJECTS) $(tools_bdaddr_LDADD) $(LIBS) tools/bluemoon.$(OBJEXT): tools/$(am__dirstamp) \ tools/$(DEPDIR)/$(am__dirstamp) tools/bluemoon$(EXEEXT): $(tools_bluemoon_OBJECTS) $(tools_bluemoon_DEPENDENCIES) $(EXTRA_tools_bluemoon_DEPENDENCIES) tools/$(am__dirstamp) @rm -f tools/bluemoon$(EXEEXT) $(AM_V_CCLD)$(LINK) $(tools_bluemoon_OBJECTS) $(tools_bluemoon_LDADD) $(LIBS) tools/bluetooth-player.$(OBJEXT): tools/$(am__dirstamp) \ tools/$(DEPDIR)/$(am__dirstamp) tools/bluetooth-player$(EXEEXT): $(tools_bluetooth_player_OBJECTS) $(tools_bluetooth_player_DEPENDENCIES) $(EXTRA_tools_bluetooth_player_DEPENDENCIES) tools/$(am__dirstamp) @rm -f tools/bluetooth-player$(EXEEXT) $(AM_V_CCLD)$(LINK) $(tools_bluetooth_player_OBJECTS) $(tools_bluetooth_player_LDADD) $(LIBS) tools/bnep-tester.$(OBJEXT): tools/$(am__dirstamp) \ tools/$(DEPDIR)/$(am__dirstamp) emulator/hciemu.$(OBJEXT): emulator/$(am__dirstamp) \ emulator/$(DEPDIR)/$(am__dirstamp) tools/bnep-tester$(EXEEXT): $(tools_bnep_tester_OBJECTS) $(tools_bnep_tester_DEPENDENCIES) $(EXTRA_tools_bnep_tester_DEPENDENCIES) tools/$(am__dirstamp) @rm -f tools/bnep-tester$(EXEEXT) $(AM_V_CCLD)$(LINK) $(tools_bnep_tester_OBJECTS) $(tools_bnep_tester_LDADD) $(LIBS) tools/bneptest.$(OBJEXT): tools/$(am__dirstamp) \ tools/$(DEPDIR)/$(am__dirstamp) tools/bneptest$(EXEEXT): $(tools_bneptest_OBJECTS) $(tools_bneptest_DEPENDENCIES) $(EXTRA_tools_bneptest_DEPENDENCIES) tools/$(am__dirstamp) @rm -f tools/bneptest$(EXEEXT) $(AM_V_CCLD)$(LINK) $(tools_bneptest_OBJECTS) $(tools_bneptest_LDADD) $(LIBS) tools/btattach.$(OBJEXT): tools/$(am__dirstamp) \ tools/$(DEPDIR)/$(am__dirstamp) tools/btattach$(EXEEXT): $(tools_btattach_OBJECTS) $(tools_btattach_DEPENDENCIES) $(EXTRA_tools_btattach_DEPENDENCIES) tools/$(am__dirstamp) @rm -f tools/btattach$(EXEEXT) $(AM_V_CCLD)$(LINK) $(tools_btattach_OBJECTS) $(tools_btattach_LDADD) $(LIBS) tools/btconfig.$(OBJEXT): tools/$(am__dirstamp) \ tools/$(DEPDIR)/$(am__dirstamp) tools/btconfig$(EXEEXT): $(tools_btconfig_OBJECTS) $(tools_btconfig_DEPENDENCIES) $(EXTRA_tools_btconfig_DEPENDENCIES) tools/$(am__dirstamp) @rm -f tools/btconfig$(EXEEXT) $(AM_V_CCLD)$(LINK) $(tools_btconfig_OBJECTS) $(tools_btconfig_LDADD) $(LIBS) tools/btgatt-client.$(OBJEXT): tools/$(am__dirstamp) \ tools/$(DEPDIR)/$(am__dirstamp) tools/btgatt-client$(EXEEXT): $(tools_btgatt_client_OBJECTS) $(tools_btgatt_client_DEPENDENCIES) $(EXTRA_tools_btgatt_client_DEPENDENCIES) tools/$(am__dirstamp) @rm -f tools/btgatt-client$(EXEEXT) $(AM_V_CCLD)$(LINK) $(tools_btgatt_client_OBJECTS) $(tools_btgatt_client_LDADD) $(LIBS) tools/btgatt-server.$(OBJEXT): tools/$(am__dirstamp) \ tools/$(DEPDIR)/$(am__dirstamp) tools/btgatt-server$(EXEEXT): $(tools_btgatt_server_OBJECTS) $(tools_btgatt_server_DEPENDENCIES) $(EXTRA_tools_btgatt_server_DEPENDENCIES) tools/$(am__dirstamp) @rm -f tools/btgatt-server$(EXEEXT) $(AM_V_CCLD)$(LINK) $(tools_btgatt_server_OBJECTS) $(tools_btgatt_server_LDADD) $(LIBS) tools/btinfo.$(OBJEXT): tools/$(am__dirstamp) \ tools/$(DEPDIR)/$(am__dirstamp) tools/btinfo$(EXEEXT): $(tools_btinfo_OBJECTS) $(tools_btinfo_DEPENDENCIES) $(EXTRA_tools_btinfo_DEPENDENCIES) tools/$(am__dirstamp) @rm -f tools/btinfo$(EXEEXT) $(AM_V_CCLD)$(LINK) $(tools_btinfo_OBJECTS) $(tools_btinfo_LDADD) $(LIBS) tools/btiotest.$(OBJEXT): tools/$(am__dirstamp) \ tools/$(DEPDIR)/$(am__dirstamp) tools/btiotest$(EXEEXT): $(tools_btiotest_OBJECTS) $(tools_btiotest_DEPENDENCIES) $(EXTRA_tools_btiotest_DEPENDENCIES) tools/$(am__dirstamp) @rm -f tools/btiotest$(EXEEXT) $(AM_V_CCLD)$(LINK) $(tools_btiotest_OBJECTS) $(tools_btiotest_LDADD) $(LIBS) tools/btmgmt.$(OBJEXT): tools/$(am__dirstamp) \ tools/$(DEPDIR)/$(am__dirstamp) tools/btmgmt$(EXEEXT): $(tools_btmgmt_OBJECTS) $(tools_btmgmt_DEPENDENCIES) $(EXTRA_tools_btmgmt_DEPENDENCIES) tools/$(am__dirstamp) @rm -f tools/btmgmt$(EXEEXT) $(AM_V_CCLD)$(LINK) $(tools_btmgmt_OBJECTS) $(tools_btmgmt_LDADD) $(LIBS) tools/btmon-logger.$(OBJEXT): tools/$(am__dirstamp) \ tools/$(DEPDIR)/$(am__dirstamp) tools/btmon-logger$(EXEEXT): $(tools_btmon_logger_OBJECTS) $(tools_btmon_logger_DEPENDENCIES) $(EXTRA_tools_btmon_logger_DEPENDENCIES) tools/$(am__dirstamp) @rm -f tools/btmon-logger$(EXEEXT) $(AM_V_CCLD)$(LINK) $(tools_btmon_logger_OBJECTS) $(tools_btmon_logger_LDADD) $(LIBS) tools/btpclient.$(OBJEXT): tools/$(am__dirstamp) \ tools/$(DEPDIR)/$(am__dirstamp) src/shared/btp.$(OBJEXT): src/shared/$(am__dirstamp) \ src/shared/$(DEPDIR)/$(am__dirstamp) tools/btpclient$(EXEEXT): $(tools_btpclient_OBJECTS) $(tools_btpclient_DEPENDENCIES) $(EXTRA_tools_btpclient_DEPENDENCIES) tools/$(am__dirstamp) @rm -f tools/btpclient$(EXEEXT) $(AM_V_CCLD)$(LINK) $(tools_btpclient_OBJECTS) $(tools_btpclient_LDADD) $(LIBS) tools/btpclientctl.$(OBJEXT): tools/$(am__dirstamp) \ tools/$(DEPDIR)/$(am__dirstamp) tools/btpclientctl$(EXEEXT): $(tools_btpclientctl_OBJECTS) $(tools_btpclientctl_DEPENDENCIES) $(EXTRA_tools_btpclientctl_DEPENDENCIES) tools/$(am__dirstamp) @rm -f tools/btpclientctl$(EXEEXT) $(AM_V_CCLD)$(LINK) $(tools_btpclientctl_OBJECTS) $(tools_btpclientctl_LDADD) $(LIBS) tools/btproxy.$(OBJEXT): tools/$(am__dirstamp) \ tools/$(DEPDIR)/$(am__dirstamp) tools/btproxy$(EXEEXT): $(tools_btproxy_OBJECTS) $(tools_btproxy_DEPENDENCIES) $(EXTRA_tools_btproxy_DEPENDENCIES) tools/$(am__dirstamp) @rm -f tools/btproxy$(EXEEXT) $(AM_V_CCLD)$(LINK) $(tools_btproxy_OBJECTS) $(tools_btproxy_LDADD) $(LIBS) tools/btsnoop.$(OBJEXT): tools/$(am__dirstamp) \ tools/$(DEPDIR)/$(am__dirstamp) tools/btsnoop$(EXEEXT): $(tools_btsnoop_OBJECTS) $(tools_btsnoop_DEPENDENCIES) $(EXTRA_tools_btsnoop_DEPENDENCIES) tools/$(am__dirstamp) @rm -f tools/btsnoop$(EXEEXT) $(AM_V_CCLD)$(LINK) $(tools_btsnoop_OBJECTS) $(tools_btsnoop_LDADD) $(LIBS) tools/check-selftest.$(OBJEXT): tools/$(am__dirstamp) \ tools/$(DEPDIR)/$(am__dirstamp) tools/check-selftest$(EXEEXT): $(tools_check_selftest_OBJECTS) $(tools_check_selftest_DEPENDENCIES) $(EXTRA_tools_check_selftest_DEPENDENCIES) tools/$(am__dirstamp) @rm -f tools/check-selftest$(EXEEXT) $(AM_V_CCLD)$(LINK) $(tools_check_selftest_OBJECTS) $(tools_check_selftest_LDADD) $(LIBS) tools/ciptool.$(OBJEXT): tools/$(am__dirstamp) \ tools/$(DEPDIR)/$(am__dirstamp) tools/ciptool$(EXEEXT): $(tools_ciptool_OBJECTS) $(tools_ciptool_DEPENDENCIES) $(EXTRA_tools_ciptool_DEPENDENCIES) tools/$(am__dirstamp) @rm -f tools/ciptool$(EXEEXT) $(AM_V_CCLD)$(LINK) $(tools_ciptool_OBJECTS) $(tools_ciptool_LDADD) $(LIBS) tools/cltest.$(OBJEXT): tools/$(am__dirstamp) \ tools/$(DEPDIR)/$(am__dirstamp) tools/cltest$(EXEEXT): $(tools_cltest_OBJECTS) $(tools_cltest_DEPENDENCIES) $(EXTRA_tools_cltest_DEPENDENCIES) tools/$(am__dirstamp) @rm -f tools/cltest$(EXEEXT) $(AM_V_CCLD)$(LINK) $(tools_cltest_OBJECTS) $(tools_cltest_LDADD) $(LIBS) tools/create-image.$(OBJEXT): tools/$(am__dirstamp) \ tools/$(DEPDIR)/$(am__dirstamp) tools/create-image$(EXEEXT): $(tools_create_image_OBJECTS) $(tools_create_image_DEPENDENCIES) $(EXTRA_tools_create_image_DEPENDENCIES) tools/$(am__dirstamp) @rm -f tools/create-image$(EXEEXT) $(AM_V_CCLD)$(LINK) $(tools_create_image_OBJECTS) $(tools_create_image_LDADD) $(LIBS) tools/eddystone.$(OBJEXT): tools/$(am__dirstamp) \ tools/$(DEPDIR)/$(am__dirstamp) tools/eddystone$(EXEEXT): $(tools_eddystone_OBJECTS) $(tools_eddystone_DEPENDENCIES) $(EXTRA_tools_eddystone_DEPENDENCIES) tools/$(am__dirstamp) @rm -f tools/eddystone$(EXEEXT) $(AM_V_CCLD)$(LINK) $(tools_eddystone_OBJECTS) $(tools_eddystone_LDADD) $(LIBS) tools/gap-tester.$(OBJEXT): tools/$(am__dirstamp) \ tools/$(DEPDIR)/$(am__dirstamp) tools/gap-tester$(EXEEXT): $(tools_gap_tester_OBJECTS) $(tools_gap_tester_DEPENDENCIES) $(EXTRA_tools_gap_tester_DEPENDENCIES) tools/$(am__dirstamp) @rm -f tools/gap-tester$(EXEEXT) $(AM_V_CCLD)$(LINK) $(tools_gap_tester_OBJECTS) $(tools_gap_tester_LDADD) $(LIBS) tools/gatt-service.$(OBJEXT): tools/$(am__dirstamp) \ tools/$(DEPDIR)/$(am__dirstamp) tools/gatt-service$(EXEEXT): $(tools_gatt_service_OBJECTS) $(tools_gatt_service_DEPENDENCIES) $(EXTRA_tools_gatt_service_DEPENDENCIES) tools/$(am__dirstamp) @rm -f tools/gatt-service$(EXEEXT) $(AM_V_CCLD)$(LINK) $(tools_gatt_service_OBJECTS) $(tools_gatt_service_LDADD) $(LIBS) tools/hci-tester.$(OBJEXT): tools/$(am__dirstamp) \ tools/$(DEPDIR)/$(am__dirstamp) tools/hci-tester$(EXEEXT): $(tools_hci_tester_OBJECTS) $(tools_hci_tester_DEPENDENCIES) $(EXTRA_tools_hci_tester_DEPENDENCIES) tools/$(am__dirstamp) @rm -f tools/hci-tester$(EXEEXT) $(AM_V_CCLD)$(LINK) $(tools_hci_tester_OBJECTS) $(tools_hci_tester_LDADD) $(LIBS) tools/hciattach.$(OBJEXT): tools/$(am__dirstamp) \ tools/$(DEPDIR)/$(am__dirstamp) tools/hciattach_st.$(OBJEXT): tools/$(am__dirstamp) \ tools/$(DEPDIR)/$(am__dirstamp) tools/hciattach_ti.$(OBJEXT): tools/$(am__dirstamp) \ tools/$(DEPDIR)/$(am__dirstamp) tools/hciattach_tialt.$(OBJEXT): tools/$(am__dirstamp) \ tools/$(DEPDIR)/$(am__dirstamp) tools/hciattach_ath3k.$(OBJEXT): tools/$(am__dirstamp) \ tools/$(DEPDIR)/$(am__dirstamp) tools/hciattach_qualcomm.$(OBJEXT): tools/$(am__dirstamp) \ tools/$(DEPDIR)/$(am__dirstamp) tools/hciattach_intel.$(OBJEXT): tools/$(am__dirstamp) \ tools/$(DEPDIR)/$(am__dirstamp) tools/hciattach_bcm43xx.$(OBJEXT): tools/$(am__dirstamp) \ tools/$(DEPDIR)/$(am__dirstamp) tools/hciattach$(EXEEXT): $(tools_hciattach_OBJECTS) $(tools_hciattach_DEPENDENCIES) $(EXTRA_tools_hciattach_DEPENDENCIES) tools/$(am__dirstamp) @rm -f tools/hciattach$(EXEEXT) $(AM_V_CCLD)$(LINK) $(tools_hciattach_OBJECTS) $(tools_hciattach_LDADD) $(LIBS) tools/hciconfig.$(OBJEXT): tools/$(am__dirstamp) \ tools/$(DEPDIR)/$(am__dirstamp) tools/hciconfig$(EXEEXT): $(tools_hciconfig_OBJECTS) $(tools_hciconfig_DEPENDENCIES) $(EXTRA_tools_hciconfig_DEPENDENCIES) tools/$(am__dirstamp) @rm -f tools/hciconfig$(EXEEXT) $(AM_V_CCLD)$(LINK) $(tools_hciconfig_OBJECTS) $(tools_hciconfig_LDADD) $(LIBS) tools/hcidump.$(OBJEXT): tools/$(am__dirstamp) \ tools/$(DEPDIR)/$(am__dirstamp) tools/parser/$(am__dirstamp): @$(MKDIR_P) tools/parser @: > tools/parser/$(am__dirstamp) tools/parser/$(DEPDIR)/$(am__dirstamp): @$(MKDIR_P) tools/parser/$(DEPDIR) @: > tools/parser/$(DEPDIR)/$(am__dirstamp) tools/parser/parser.$(OBJEXT): tools/parser/$(am__dirstamp) \ tools/parser/$(DEPDIR)/$(am__dirstamp) tools/parser/lmp.$(OBJEXT): tools/parser/$(am__dirstamp) \ tools/parser/$(DEPDIR)/$(am__dirstamp) tools/parser/hci.$(OBJEXT): tools/parser/$(am__dirstamp) \ tools/parser/$(DEPDIR)/$(am__dirstamp) tools/parser/l2cap.$(OBJEXT): tools/parser/$(am__dirstamp) \ tools/parser/$(DEPDIR)/$(am__dirstamp) tools/parser/smp.$(OBJEXT): tools/parser/$(am__dirstamp) \ tools/parser/$(DEPDIR)/$(am__dirstamp) tools/parser/att.$(OBJEXT): tools/parser/$(am__dirstamp) \ tools/parser/$(DEPDIR)/$(am__dirstamp) tools/parser/sdp.$(OBJEXT): tools/parser/$(am__dirstamp) \ tools/parser/$(DEPDIR)/$(am__dirstamp) tools/parser/rfcomm.$(OBJEXT): tools/parser/$(am__dirstamp) \ tools/parser/$(DEPDIR)/$(am__dirstamp) tools/parser/bnep.$(OBJEXT): tools/parser/$(am__dirstamp) \ tools/parser/$(DEPDIR)/$(am__dirstamp) tools/parser/cmtp.$(OBJEXT): tools/parser/$(am__dirstamp) \ tools/parser/$(DEPDIR)/$(am__dirstamp) tools/parser/hidp.$(OBJEXT): tools/parser/$(am__dirstamp) \ tools/parser/$(DEPDIR)/$(am__dirstamp) tools/parser/hcrp.$(OBJEXT): tools/parser/$(am__dirstamp) \ tools/parser/$(DEPDIR)/$(am__dirstamp) tools/parser/avdtp.$(OBJEXT): tools/parser/$(am__dirstamp) \ tools/parser/$(DEPDIR)/$(am__dirstamp) tools/parser/avctp.$(OBJEXT): tools/parser/$(am__dirstamp) \ tools/parser/$(DEPDIR)/$(am__dirstamp) tools/parser/avrcp.$(OBJEXT): tools/parser/$(am__dirstamp) \ tools/parser/$(DEPDIR)/$(am__dirstamp) tools/parser/sap.$(OBJEXT): tools/parser/$(am__dirstamp) \ tools/parser/$(DEPDIR)/$(am__dirstamp) tools/parser/obex.$(OBJEXT): tools/parser/$(am__dirstamp) \ tools/parser/$(DEPDIR)/$(am__dirstamp) tools/parser/capi.$(OBJEXT): tools/parser/$(am__dirstamp) \ tools/parser/$(DEPDIR)/$(am__dirstamp) tools/parser/ppp.$(OBJEXT): tools/parser/$(am__dirstamp) \ tools/parser/$(DEPDIR)/$(am__dirstamp) tools/parser/tcpip.$(OBJEXT): tools/parser/$(am__dirstamp) \ tools/parser/$(DEPDIR)/$(am__dirstamp) tools/parser/ericsson.$(OBJEXT): tools/parser/$(am__dirstamp) \ tools/parser/$(DEPDIR)/$(am__dirstamp) tools/parser/csr.$(OBJEXT): tools/parser/$(am__dirstamp) \ tools/parser/$(DEPDIR)/$(am__dirstamp) tools/parser/bpa.$(OBJEXT): tools/parser/$(am__dirstamp) \ tools/parser/$(DEPDIR)/$(am__dirstamp) tools/hcidump$(EXEEXT): $(tools_hcidump_OBJECTS) $(tools_hcidump_DEPENDENCIES) $(EXTRA_tools_hcidump_DEPENDENCIES) tools/$(am__dirstamp) @rm -f tools/hcidump$(EXEEXT) $(AM_V_CCLD)$(LINK) $(tools_hcidump_OBJECTS) $(tools_hcidump_LDADD) $(LIBS) tools/hcieventmask.$(OBJEXT): tools/$(am__dirstamp) \ tools/$(DEPDIR)/$(am__dirstamp) tools/hcieventmask$(EXEEXT): $(tools_hcieventmask_OBJECTS) $(tools_hcieventmask_DEPENDENCIES) $(EXTRA_tools_hcieventmask_DEPENDENCIES) tools/$(am__dirstamp) @rm -f tools/hcieventmask$(EXEEXT) $(AM_V_CCLD)$(LINK) $(tools_hcieventmask_OBJECTS) $(tools_hcieventmask_LDADD) $(LIBS) tools/hcisecfilter.$(OBJEXT): tools/$(am__dirstamp) \ tools/$(DEPDIR)/$(am__dirstamp) tools/hcisecfilter$(EXEEXT): $(tools_hcisecfilter_OBJECTS) $(tools_hcisecfilter_DEPENDENCIES) $(EXTRA_tools_hcisecfilter_DEPENDENCIES) tools/$(am__dirstamp) @rm -f tools/hcisecfilter$(EXEEXT) $(AM_V_CCLD)$(LINK) $(tools_hcisecfilter_OBJECTS) $(tools_hcisecfilter_LDADD) $(LIBS) tools/hcitool.$(OBJEXT): tools/$(am__dirstamp) \ tools/$(DEPDIR)/$(am__dirstamp) tools/hcitool$(EXEEXT): $(tools_hcitool_OBJECTS) $(tools_hcitool_DEPENDENCIES) $(EXTRA_tools_hcitool_DEPENDENCIES) tools/$(am__dirstamp) @rm -f tools/hcitool$(EXEEXT) $(AM_V_CCLD)$(LINK) $(tools_hcitool_OBJECTS) $(tools_hcitool_LDADD) $(LIBS) tools/hex2hcd.$(OBJEXT): tools/$(am__dirstamp) \ tools/$(DEPDIR)/$(am__dirstamp) tools/hex2hcd$(EXEEXT): $(tools_hex2hcd_OBJECTS) $(tools_hex2hcd_DEPENDENCIES) $(EXTRA_tools_hex2hcd_DEPENDENCIES) tools/$(am__dirstamp) @rm -f tools/hex2hcd$(EXEEXT) $(AM_V_CCLD)$(LINK) $(tools_hex2hcd_OBJECTS) $(tools_hex2hcd_LDADD) $(LIBS) tools/hid2hci.$(OBJEXT): tools/$(am__dirstamp) \ tools/$(DEPDIR)/$(am__dirstamp) tools/hid2hci$(EXEEXT): $(tools_hid2hci_OBJECTS) $(tools_hid2hci_DEPENDENCIES) $(EXTRA_tools_hid2hci_DEPENDENCIES) tools/$(am__dirstamp) @rm -f tools/hid2hci$(EXEEXT) $(AM_V_CCLD)$(LINK) $(tools_hid2hci_OBJECTS) $(tools_hid2hci_LDADD) $(LIBS) tools/hwdb.$(OBJEXT): tools/$(am__dirstamp) \ tools/$(DEPDIR)/$(am__dirstamp) tools/hwdb$(EXEEXT): $(tools_hwdb_OBJECTS) $(tools_hwdb_DEPENDENCIES) $(EXTRA_tools_hwdb_DEPENDENCIES) tools/$(am__dirstamp) @rm -f tools/hwdb$(EXEEXT) $(AM_V_CCLD)$(LINK) $(tools_hwdb_OBJECTS) $(tools_hwdb_LDADD) $(LIBS) tools/ibeacon.$(OBJEXT): tools/$(am__dirstamp) \ tools/$(DEPDIR)/$(am__dirstamp) tools/ibeacon$(EXEEXT): $(tools_ibeacon_OBJECTS) $(tools_ibeacon_DEPENDENCIES) $(EXTRA_tools_ibeacon_DEPENDENCIES) tools/$(am__dirstamp) @rm -f tools/ibeacon$(EXEEXT) $(AM_V_CCLD)$(LINK) $(tools_ibeacon_OBJECTS) $(tools_ibeacon_LDADD) $(LIBS) tools/ioctl-tester.$(OBJEXT): tools/$(am__dirstamp) \ tools/$(DEPDIR)/$(am__dirstamp) tools/ioctl-tester$(EXEEXT): $(tools_ioctl_tester_OBJECTS) $(tools_ioctl_tester_DEPENDENCIES) $(EXTRA_tools_ioctl_tester_DEPENDENCIES) tools/$(am__dirstamp) @rm -f tools/ioctl-tester$(EXEEXT) $(AM_V_CCLD)$(LINK) $(tools_ioctl_tester_OBJECTS) $(tools_ioctl_tester_LDADD) $(LIBS) tools/iso-tester.$(OBJEXT): tools/$(am__dirstamp) \ tools/$(DEPDIR)/$(am__dirstamp) tools/iso-tester$(EXEEXT): $(tools_iso_tester_OBJECTS) $(tools_iso_tester_DEPENDENCIES) $(EXTRA_tools_iso_tester_DEPENDENCIES) tools/$(am__dirstamp) @rm -f tools/iso-tester$(EXEEXT) $(AM_V_CCLD)$(LINK) $(tools_iso_tester_OBJECTS) $(tools_iso_tester_LDADD) $(LIBS) tools/isotest.$(OBJEXT): tools/$(am__dirstamp) \ tools/$(DEPDIR)/$(am__dirstamp) tools/isotest$(EXEEXT): $(tools_isotest_OBJECTS) $(tools_isotest_DEPENDENCIES) $(EXTRA_tools_isotest_DEPENDENCIES) tools/$(am__dirstamp) @rm -f tools/isotest$(EXEEXT) $(AM_V_CCLD)$(LINK) $(tools_isotest_OBJECTS) $(tools_isotest_LDADD) $(LIBS) tools/l2cap-tester.$(OBJEXT): tools/$(am__dirstamp) \ tools/$(DEPDIR)/$(am__dirstamp) tools/l2cap-tester$(EXEEXT): $(tools_l2cap_tester_OBJECTS) $(tools_l2cap_tester_DEPENDENCIES) $(EXTRA_tools_l2cap_tester_DEPENDENCIES) tools/$(am__dirstamp) @rm -f tools/l2cap-tester$(EXEEXT) $(AM_V_CCLD)$(LINK) $(tools_l2cap_tester_OBJECTS) $(tools_l2cap_tester_LDADD) $(LIBS) tools/l2ping.$(OBJEXT): tools/$(am__dirstamp) \ tools/$(DEPDIR)/$(am__dirstamp) tools/l2ping$(EXEEXT): $(tools_l2ping_OBJECTS) $(tools_l2ping_DEPENDENCIES) $(EXTRA_tools_l2ping_DEPENDENCIES) tools/$(am__dirstamp) @rm -f tools/l2ping$(EXEEXT) $(AM_V_CCLD)$(LINK) $(tools_l2ping_OBJECTS) $(tools_l2ping_LDADD) $(LIBS) tools/l2test.$(OBJEXT): tools/$(am__dirstamp) \ tools/$(DEPDIR)/$(am__dirstamp) tools/l2test$(EXEEXT): $(tools_l2test_OBJECTS) $(tools_l2test_DEPENDENCIES) $(EXTRA_tools_l2test_DEPENDENCIES) tools/$(am__dirstamp) @rm -f tools/l2test$(EXEEXT) $(AM_V_CCLD)$(LINK) $(tools_l2test_OBJECTS) $(tools_l2test_LDADD) $(LIBS) tools/mcaptest.$(OBJEXT): tools/$(am__dirstamp) \ tools/$(DEPDIR)/$(am__dirstamp) tools/mcaptest$(EXEEXT): $(tools_mcaptest_OBJECTS) $(tools_mcaptest_DEPENDENCIES) $(EXTRA_tools_mcaptest_DEPENDENCIES) tools/$(am__dirstamp) @rm -f tools/mcaptest$(EXEEXT) $(AM_V_CCLD)$(LINK) $(tools_mcaptest_OBJECTS) $(tools_mcaptest_LDADD) $(LIBS) tools/mesh-cfgclient.$(OBJEXT): tools/$(am__dirstamp) \ tools/$(DEPDIR)/$(am__dirstamp) tools/mesh/$(am__dirstamp): @$(MKDIR_P) tools/mesh @: > tools/mesh/$(am__dirstamp) tools/mesh/$(DEPDIR)/$(am__dirstamp): @$(MKDIR_P) tools/mesh/$(DEPDIR) @: > tools/mesh/$(DEPDIR)/$(am__dirstamp) tools/mesh/cfgcli.$(OBJEXT): tools/mesh/$(am__dirstamp) \ tools/mesh/$(DEPDIR)/$(am__dirstamp) tools/mesh/keys.$(OBJEXT): tools/mesh/$(am__dirstamp) \ tools/mesh/$(DEPDIR)/$(am__dirstamp) tools/mesh/util.$(OBJEXT): tools/mesh/$(am__dirstamp) \ tools/mesh/$(DEPDIR)/$(am__dirstamp) tools/mesh/remote.$(OBJEXT): tools/mesh/$(am__dirstamp) \ tools/mesh/$(DEPDIR)/$(am__dirstamp) tools/mesh/agent.$(OBJEXT): tools/mesh/$(am__dirstamp) \ tools/mesh/$(DEPDIR)/$(am__dirstamp) tools/mesh/mesh-db.$(OBJEXT): tools/mesh/$(am__dirstamp) \ tools/mesh/$(DEPDIR)/$(am__dirstamp) tools/mesh-cfgclient$(EXEEXT): $(tools_mesh_cfgclient_OBJECTS) $(tools_mesh_cfgclient_DEPENDENCIES) $(EXTRA_tools_mesh_cfgclient_DEPENDENCIES) tools/$(am__dirstamp) @rm -f tools/mesh-cfgclient$(EXEEXT) $(AM_V_CCLD)$(LINK) $(tools_mesh_cfgclient_OBJECTS) $(tools_mesh_cfgclient_LDADD) $(LIBS) tools/mesh-cfgtest.$(OBJEXT): tools/$(am__dirstamp) \ tools/$(DEPDIR)/$(am__dirstamp) tools/mesh-cfgtest$(EXEEXT): $(tools_mesh_cfgtest_OBJECTS) $(tools_mesh_cfgtest_DEPENDENCIES) $(EXTRA_tools_mesh_cfgtest_DEPENDENCIES) tools/$(am__dirstamp) @rm -f tools/mesh-cfgtest$(EXEEXT) $(AM_V_CCLD)$(LINK) $(tools_mesh_cfgtest_OBJECTS) $(tools_mesh_cfgtest_LDADD) $(LIBS) tools/mesh-tester.$(OBJEXT): tools/$(am__dirstamp) \ tools/$(DEPDIR)/$(am__dirstamp) tools/mesh-tester$(EXEEXT): $(tools_mesh_tester_OBJECTS) $(tools_mesh_tester_DEPENDENCIES) $(EXTRA_tools_mesh_tester_DEPENDENCIES) tools/$(am__dirstamp) @rm -f tools/mesh-tester$(EXEEXT) $(AM_V_CCLD)$(LINK) $(tools_mesh_tester_OBJECTS) $(tools_mesh_tester_LDADD) $(LIBS) tools/meshctl.$(OBJEXT): tools/$(am__dirstamp) \ tools/$(DEPDIR)/$(am__dirstamp) tools/mesh-gatt/$(am__dirstamp): @$(MKDIR_P) tools/mesh-gatt @: > tools/mesh-gatt/$(am__dirstamp) tools/mesh-gatt/$(DEPDIR)/$(am__dirstamp): @$(MKDIR_P) tools/mesh-gatt/$(DEPDIR) @: > tools/mesh-gatt/$(DEPDIR)/$(am__dirstamp) tools/mesh-gatt/node.$(OBJEXT): tools/mesh-gatt/$(am__dirstamp) \ tools/mesh-gatt/$(DEPDIR)/$(am__dirstamp) tools/mesh-gatt/gatt.$(OBJEXT): tools/mesh-gatt/$(am__dirstamp) \ tools/mesh-gatt/$(DEPDIR)/$(am__dirstamp) tools/mesh-gatt/crypto.$(OBJEXT): tools/mesh-gatt/$(am__dirstamp) \ tools/mesh-gatt/$(DEPDIR)/$(am__dirstamp) tools/mesh-gatt/net.$(OBJEXT): tools/mesh-gatt/$(am__dirstamp) \ tools/mesh-gatt/$(DEPDIR)/$(am__dirstamp) tools/mesh-gatt/prov.$(OBJEXT): tools/mesh-gatt/$(am__dirstamp) \ tools/mesh-gatt/$(DEPDIR)/$(am__dirstamp) tools/mesh-gatt/util.$(OBJEXT): tools/mesh-gatt/$(am__dirstamp) \ tools/mesh-gatt/$(DEPDIR)/$(am__dirstamp) tools/mesh-gatt/prov-db.$(OBJEXT): tools/mesh-gatt/$(am__dirstamp) \ tools/mesh-gatt/$(DEPDIR)/$(am__dirstamp) tools/mesh-gatt/config-client.$(OBJEXT): \ tools/mesh-gatt/$(am__dirstamp) \ tools/mesh-gatt/$(DEPDIR)/$(am__dirstamp) tools/mesh-gatt/config-server.$(OBJEXT): \ tools/mesh-gatt/$(am__dirstamp) \ tools/mesh-gatt/$(DEPDIR)/$(am__dirstamp) tools/mesh-gatt/onoff-model.$(OBJEXT): \ tools/mesh-gatt/$(am__dirstamp) \ tools/mesh-gatt/$(DEPDIR)/$(am__dirstamp) tools/meshctl$(EXEEXT): $(tools_meshctl_OBJECTS) $(tools_meshctl_DEPENDENCIES) $(EXTRA_tools_meshctl_DEPENDENCIES) tools/$(am__dirstamp) @rm -f tools/meshctl$(EXEEXT) $(AM_V_CCLD)$(LINK) $(tools_meshctl_OBJECTS) $(tools_meshctl_LDADD) $(LIBS) tools/mgmt-tester.$(OBJEXT): tools/$(am__dirstamp) \ tools/$(DEPDIR)/$(am__dirstamp) tools/mgmt-tester$(EXEEXT): $(tools_mgmt_tester_OBJECTS) $(tools_mgmt_tester_DEPENDENCIES) $(EXTRA_tools_mgmt_tester_DEPENDENCIES) tools/$(am__dirstamp) @rm -f tools/mgmt-tester$(EXEEXT) $(AM_V_CCLD)$(LINK) $(tools_mgmt_tester_OBJECTS) $(tools_mgmt_tester_LDADD) $(LIBS) tools/mpris-proxy.$(OBJEXT): tools/$(am__dirstamp) \ tools/$(DEPDIR)/$(am__dirstamp) tools/mpris-proxy$(EXEEXT): $(tools_mpris_proxy_OBJECTS) $(tools_mpris_proxy_DEPENDENCIES) $(EXTRA_tools_mpris_proxy_DEPENDENCIES) tools/$(am__dirstamp) @rm -f tools/mpris-proxy$(EXEEXT) $(AM_V_CCLD)$(LINK) $(tools_mpris_proxy_OBJECTS) $(tools_mpris_proxy_LDADD) $(LIBS) tools/nokfw.$(OBJEXT): tools/$(am__dirstamp) \ tools/$(DEPDIR)/$(am__dirstamp) tools/nokfw$(EXEEXT): $(tools_nokfw_OBJECTS) $(tools_nokfw_DEPENDENCIES) $(EXTRA_tools_nokfw_DEPENDENCIES) tools/$(am__dirstamp) @rm -f tools/nokfw$(EXEEXT) $(AM_V_CCLD)$(LINK) $(tools_nokfw_OBJECTS) $(tools_nokfw_LDADD) $(LIBS) gobex/gobex.$(OBJEXT): gobex/$(am__dirstamp) \ gobex/$(DEPDIR)/$(am__dirstamp) gobex/gobex-defs.$(OBJEXT): gobex/$(am__dirstamp) \ gobex/$(DEPDIR)/$(am__dirstamp) gobex/gobex-packet.$(OBJEXT): gobex/$(am__dirstamp) \ gobex/$(DEPDIR)/$(am__dirstamp) gobex/gobex-header.$(OBJEXT): gobex/$(am__dirstamp) \ gobex/$(DEPDIR)/$(am__dirstamp) gobex/gobex-transfer.$(OBJEXT): gobex/$(am__dirstamp) \ gobex/$(DEPDIR)/$(am__dirstamp) gobex/gobex-apparam.$(OBJEXT): gobex/$(am__dirstamp) \ gobex/$(DEPDIR)/$(am__dirstamp) tools/obex-client-tool.$(OBJEXT): tools/$(am__dirstamp) \ tools/$(DEPDIR)/$(am__dirstamp) tools/obex-client-tool$(EXEEXT): $(tools_obex_client_tool_OBJECTS) $(tools_obex_client_tool_DEPENDENCIES) $(EXTRA_tools_obex_client_tool_DEPENDENCIES) tools/$(am__dirstamp) @rm -f tools/obex-client-tool$(EXEEXT) $(AM_V_CCLD)$(LINK) $(tools_obex_client_tool_OBJECTS) $(tools_obex_client_tool_LDADD) $(LIBS) tools/obex-server-tool.$(OBJEXT): tools/$(am__dirstamp) \ tools/$(DEPDIR)/$(am__dirstamp) tools/obex-server-tool$(EXEEXT): $(tools_obex_server_tool_OBJECTS) $(tools_obex_server_tool_DEPENDENCIES) $(EXTRA_tools_obex_server_tool_DEPENDENCIES) tools/$(am__dirstamp) @rm -f tools/obex-server-tool$(EXEEXT) $(AM_V_CCLD)$(LINK) $(tools_obex_server_tool_OBJECTS) $(tools_obex_server_tool_LDADD) $(LIBS) tools/obexctl.$(OBJEXT): tools/$(am__dirstamp) \ tools/$(DEPDIR)/$(am__dirstamp) tools/obexctl$(EXEEXT): $(tools_obexctl_OBJECTS) $(tools_obexctl_DEPENDENCIES) $(EXTRA_tools_obexctl_DEPENDENCIES) tools/$(am__dirstamp) @rm -f tools/obexctl$(EXEEXT) $(AM_V_CCLD)$(LINK) $(tools_obexctl_OBJECTS) $(tools_obexctl_LDADD) $(LIBS) tools/oobtest.$(OBJEXT): tools/$(am__dirstamp) \ tools/$(DEPDIR)/$(am__dirstamp) tools/oobtest$(EXEEXT): $(tools_oobtest_OBJECTS) $(tools_oobtest_DEPENDENCIES) $(EXTRA_tools_oobtest_DEPENDENCIES) tools/$(am__dirstamp) @rm -f tools/oobtest$(EXEEXT) $(AM_V_CCLD)$(LINK) $(tools_oobtest_OBJECTS) $(tools_oobtest_LDADD) $(LIBS) tools/rctest.$(OBJEXT): tools/$(am__dirstamp) \ tools/$(DEPDIR)/$(am__dirstamp) tools/rctest$(EXEEXT): $(tools_rctest_OBJECTS) $(tools_rctest_DEPENDENCIES) $(EXTRA_tools_rctest_DEPENDENCIES) tools/$(am__dirstamp) @rm -f tools/rctest$(EXEEXT) $(AM_V_CCLD)$(LINK) $(tools_rctest_OBJECTS) $(tools_rctest_LDADD) $(LIBS) tools/rfcomm.$(OBJEXT): tools/$(am__dirstamp) \ tools/$(DEPDIR)/$(am__dirstamp) tools/rfcomm$(EXEEXT): $(tools_rfcomm_OBJECTS) $(tools_rfcomm_DEPENDENCIES) $(EXTRA_tools_rfcomm_DEPENDENCIES) tools/$(am__dirstamp) @rm -f tools/rfcomm$(EXEEXT) $(AM_V_CCLD)$(LINK) $(tools_rfcomm_OBJECTS) $(tools_rfcomm_LDADD) $(LIBS) tools/rfcomm-tester.$(OBJEXT): tools/$(am__dirstamp) \ tools/$(DEPDIR)/$(am__dirstamp) tools/rfcomm-tester$(EXEEXT): $(tools_rfcomm_tester_OBJECTS) $(tools_rfcomm_tester_DEPENDENCIES) $(EXTRA_tools_rfcomm_tester_DEPENDENCIES) tools/$(am__dirstamp) @rm -f tools/rfcomm-tester$(EXEEXT) $(AM_V_CCLD)$(LINK) $(tools_rfcomm_tester_OBJECTS) $(tools_rfcomm_tester_LDADD) $(LIBS) tools/rtlfw.$(OBJEXT): tools/$(am__dirstamp) \ tools/$(DEPDIR)/$(am__dirstamp) tools/rtlfw$(EXEEXT): $(tools_rtlfw_OBJECTS) $(tools_rtlfw_DEPENDENCIES) $(EXTRA_tools_rtlfw_DEPENDENCIES) tools/$(am__dirstamp) @rm -f tools/rtlfw$(EXEEXT) $(AM_V_CCLD)$(LINK) $(tools_rtlfw_OBJECTS) $(tools_rtlfw_LDADD) $(LIBS) tools/sco-tester.$(OBJEXT): tools/$(am__dirstamp) \ tools/$(DEPDIR)/$(am__dirstamp) tools/sco-tester$(EXEEXT): $(tools_sco_tester_OBJECTS) $(tools_sco_tester_DEPENDENCIES) $(EXTRA_tools_sco_tester_DEPENDENCIES) tools/$(am__dirstamp) @rm -f tools/sco-tester$(EXEEXT) $(AM_V_CCLD)$(LINK) $(tools_sco_tester_OBJECTS) $(tools_sco_tester_LDADD) $(LIBS) tools/scotest.$(OBJEXT): tools/$(am__dirstamp) \ tools/$(DEPDIR)/$(am__dirstamp) tools/scotest$(EXEEXT): $(tools_scotest_OBJECTS) $(tools_scotest_DEPENDENCIES) $(EXTRA_tools_scotest_DEPENDENCIES) tools/$(am__dirstamp) @rm -f tools/scotest$(EXEEXT) $(AM_V_CCLD)$(LINK) $(tools_scotest_OBJECTS) $(tools_scotest_LDADD) $(LIBS) tools/sdptool.$(OBJEXT): tools/$(am__dirstamp) \ tools/$(DEPDIR)/$(am__dirstamp) src/sdp-xml.$(OBJEXT): src/$(am__dirstamp) \ src/$(DEPDIR)/$(am__dirstamp) tools/sdptool$(EXEEXT): $(tools_sdptool_OBJECTS) $(tools_sdptool_DEPENDENCIES) $(EXTRA_tools_sdptool_DEPENDENCIES) tools/$(am__dirstamp) @rm -f tools/sdptool$(EXEEXT) $(AM_V_CCLD)$(LINK) $(tools_sdptool_OBJECTS) $(tools_sdptool_LDADD) $(LIBS) tools/seq2bseq.$(OBJEXT): tools/$(am__dirstamp) \ tools/$(DEPDIR)/$(am__dirstamp) tools/seq2bseq$(EXEEXT): $(tools_seq2bseq_OBJECTS) $(tools_seq2bseq_DEPENDENCIES) $(EXTRA_tools_seq2bseq_DEPENDENCIES) tools/$(am__dirstamp) @rm -f tools/seq2bseq$(EXEEXT) $(AM_V_CCLD)$(LINK) $(tools_seq2bseq_OBJECTS) $(tools_seq2bseq_LDADD) $(LIBS) tools/smp-tester.$(OBJEXT): tools/$(am__dirstamp) \ tools/$(DEPDIR)/$(am__dirstamp) tools/smp-tester$(EXEEXT): $(tools_smp_tester_OBJECTS) $(tools_smp_tester_DEPENDENCIES) $(EXTRA_tools_smp_tester_DEPENDENCIES) tools/$(am__dirstamp) @rm -f tools/smp-tester$(EXEEXT) $(AM_V_CCLD)$(LINK) $(tools_smp_tester_OBJECTS) $(tools_smp_tester_LDADD) $(LIBS) tools/test-runner.$(OBJEXT): tools/$(am__dirstamp) \ tools/$(DEPDIR)/$(am__dirstamp) tools/test-runner$(EXEEXT): $(tools_test_runner_OBJECTS) $(tools_test_runner_DEPENDENCIES) $(EXTRA_tools_test_runner_DEPENDENCIES) tools/$(am__dirstamp) @rm -f tools/test-runner$(EXEEXT) $(AM_V_CCLD)$(LINK) $(tools_test_runner_OBJECTS) $(tools_test_runner_LDADD) $(LIBS) tools/userchan-tester.$(OBJEXT): tools/$(am__dirstamp) \ tools/$(DEPDIR)/$(am__dirstamp) tools/userchan-tester$(EXEEXT): $(tools_userchan_tester_OBJECTS) $(tools_userchan_tester_DEPENDENCIES) $(EXTRA_tools_userchan_tester_DEPENDENCIES) tools/$(am__dirstamp) @rm -f tools/userchan-tester$(EXEEXT) $(AM_V_CCLD)$(LINK) $(tools_userchan_tester_OBJECTS) $(tools_userchan_tester_LDADD) $(LIBS) unit/$(am__dirstamp): @$(MKDIR_P) unit @: > unit/$(am__dirstamp) unit/$(DEPDIR)/$(am__dirstamp): @$(MKDIR_P) unit/$(DEPDIR) @: > unit/$(DEPDIR)/$(am__dirstamp) unit/test-avctp.$(OBJEXT): unit/$(am__dirstamp) \ unit/$(DEPDIR)/$(am__dirstamp) unit/test-avctp$(EXEEXT): $(unit_test_avctp_OBJECTS) $(unit_test_avctp_DEPENDENCIES) $(EXTRA_unit_test_avctp_DEPENDENCIES) unit/$(am__dirstamp) @rm -f unit/test-avctp$(EXEEXT) $(AM_V_CCLD)$(LINK) $(unit_test_avctp_OBJECTS) $(unit_test_avctp_LDADD) $(LIBS) unit/test-avdtp.$(OBJEXT): unit/$(am__dirstamp) \ unit/$(DEPDIR)/$(am__dirstamp) unit/test-avdtp$(EXEEXT): $(unit_test_avdtp_OBJECTS) $(unit_test_avdtp_DEPENDENCIES) $(EXTRA_unit_test_avdtp_DEPENDENCIES) unit/$(am__dirstamp) @rm -f unit/test-avdtp$(EXEEXT) $(AM_V_CCLD)$(LINK) $(unit_test_avdtp_OBJECTS) $(unit_test_avdtp_LDADD) $(LIBS) unit/test-avrcp.$(OBJEXT): unit/$(am__dirstamp) \ unit/$(DEPDIR)/$(am__dirstamp) unit/test-avrcp$(EXEEXT): $(unit_test_avrcp_OBJECTS) $(unit_test_avrcp_DEPENDENCIES) $(EXTRA_unit_test_avrcp_DEPENDENCIES) unit/$(am__dirstamp) @rm -f unit/test-avrcp$(EXEEXT) $(AM_V_CCLD)$(LINK) $(unit_test_avrcp_OBJECTS) $(unit_test_avrcp_LDADD) $(LIBS) unit/test-bap.$(OBJEXT): unit/$(am__dirstamp) \ unit/$(DEPDIR)/$(am__dirstamp) unit/test-bap$(EXEEXT): $(unit_test_bap_OBJECTS) $(unit_test_bap_DEPENDENCIES) $(EXTRA_unit_test_bap_DEPENDENCIES) unit/$(am__dirstamp) @rm -f unit/test-bap$(EXEEXT) $(AM_V_CCLD)$(LINK) $(unit_test_bap_OBJECTS) $(unit_test_bap_LDADD) $(LIBS) unit/test-bass.$(OBJEXT): unit/$(am__dirstamp) \ unit/$(DEPDIR)/$(am__dirstamp) unit/test-bass$(EXEEXT): $(unit_test_bass_OBJECTS) $(unit_test_bass_DEPENDENCIES) $(EXTRA_unit_test_bass_DEPENDENCIES) unit/$(am__dirstamp) @rm -f unit/test-bass$(EXEEXT) $(AM_V_CCLD)$(LINK) $(unit_test_bass_OBJECTS) $(unit_test_bass_LDADD) $(LIBS) unit/test-crc.$(OBJEXT): unit/$(am__dirstamp) \ unit/$(DEPDIR)/$(am__dirstamp) unit/test-crc$(EXEEXT): $(unit_test_crc_OBJECTS) $(unit_test_crc_DEPENDENCIES) $(EXTRA_unit_test_crc_DEPENDENCIES) unit/$(am__dirstamp) @rm -f unit/test-crc$(EXEEXT) $(AM_V_CCLD)$(LINK) $(unit_test_crc_OBJECTS) $(unit_test_crc_LDADD) $(LIBS) unit/test-crypto.$(OBJEXT): unit/$(am__dirstamp) \ unit/$(DEPDIR)/$(am__dirstamp) unit/test-crypto$(EXEEXT): $(unit_test_crypto_OBJECTS) $(unit_test_crypto_DEPENDENCIES) $(EXTRA_unit_test_crypto_DEPENDENCIES) unit/$(am__dirstamp) @rm -f unit/test-crypto$(EXEEXT) $(AM_V_CCLD)$(LINK) $(unit_test_crypto_OBJECTS) $(unit_test_crypto_LDADD) $(LIBS) unit/test-ecc.$(OBJEXT): unit/$(am__dirstamp) \ unit/$(DEPDIR)/$(am__dirstamp) unit/test-ecc$(EXEEXT): $(unit_test_ecc_OBJECTS) $(unit_test_ecc_DEPENDENCIES) $(EXTRA_unit_test_ecc_DEPENDENCIES) unit/$(am__dirstamp) @rm -f unit/test-ecc$(EXEEXT) $(AM_V_CCLD)$(LINK) $(unit_test_ecc_OBJECTS) $(unit_test_ecc_LDADD) $(LIBS) unit/test-eir.$(OBJEXT): unit/$(am__dirstamp) \ unit/$(DEPDIR)/$(am__dirstamp) unit/test-eir$(EXEEXT): $(unit_test_eir_OBJECTS) $(unit_test_eir_DEPENDENCIES) $(EXTRA_unit_test_eir_DEPENDENCIES) unit/$(am__dirstamp) @rm -f unit/test-eir$(EXEEXT) $(AM_V_CCLD)$(LINK) $(unit_test_eir_OBJECTS) $(unit_test_eir_LDADD) $(LIBS) unit/test-gatt.$(OBJEXT): unit/$(am__dirstamp) \ unit/$(DEPDIR)/$(am__dirstamp) unit/test-gatt$(EXEEXT): $(unit_test_gatt_OBJECTS) $(unit_test_gatt_DEPENDENCIES) $(EXTRA_unit_test_gatt_DEPENDENCIES) unit/$(am__dirstamp) @rm -f unit/test-gatt$(EXEEXT) $(AM_V_CCLD)$(LINK) $(unit_test_gatt_OBJECTS) $(unit_test_gatt_LDADD) $(LIBS) unit/test-gattrib.$(OBJEXT): unit/$(am__dirstamp) \ unit/$(DEPDIR)/$(am__dirstamp) unit/test-gattrib$(EXEEXT): $(unit_test_gattrib_OBJECTS) $(unit_test_gattrib_DEPENDENCIES) $(EXTRA_unit_test_gattrib_DEPENDENCIES) unit/$(am__dirstamp) @rm -f unit/test-gattrib$(EXEEXT) $(AM_V_CCLD)$(LINK) $(unit_test_gattrib_OBJECTS) $(unit_test_gattrib_LDADD) $(LIBS) unit/test-gdbus-client.$(OBJEXT): unit/$(am__dirstamp) \ unit/$(DEPDIR)/$(am__dirstamp) unit/test-gdbus-client$(EXEEXT): $(unit_test_gdbus_client_OBJECTS) $(unit_test_gdbus_client_DEPENDENCIES) $(EXTRA_unit_test_gdbus_client_DEPENDENCIES) unit/$(am__dirstamp) @rm -f unit/test-gdbus-client$(EXEEXT) $(AM_V_CCLD)$(LINK) $(unit_test_gdbus_client_OBJECTS) $(unit_test_gdbus_client_LDADD) $(LIBS) unit/util.$(OBJEXT): unit/$(am__dirstamp) \ unit/$(DEPDIR)/$(am__dirstamp) unit/test-gobex.$(OBJEXT): unit/$(am__dirstamp) \ unit/$(DEPDIR)/$(am__dirstamp) unit/test-gobex$(EXEEXT): $(unit_test_gobex_OBJECTS) $(unit_test_gobex_DEPENDENCIES) $(EXTRA_unit_test_gobex_DEPENDENCIES) unit/$(am__dirstamp) @rm -f unit/test-gobex$(EXEEXT) $(AM_V_CCLD)$(LINK) $(unit_test_gobex_OBJECTS) $(unit_test_gobex_LDADD) $(LIBS) unit/test-gobex-apparam.$(OBJEXT): unit/$(am__dirstamp) \ unit/$(DEPDIR)/$(am__dirstamp) unit/test-gobex-apparam$(EXEEXT): $(unit_test_gobex_apparam_OBJECTS) $(unit_test_gobex_apparam_DEPENDENCIES) $(EXTRA_unit_test_gobex_apparam_DEPENDENCIES) unit/$(am__dirstamp) @rm -f unit/test-gobex-apparam$(EXEEXT) $(AM_V_CCLD)$(LINK) $(unit_test_gobex_apparam_OBJECTS) $(unit_test_gobex_apparam_LDADD) $(LIBS) unit/test-gobex-header.$(OBJEXT): unit/$(am__dirstamp) \ unit/$(DEPDIR)/$(am__dirstamp) unit/test-gobex-header$(EXEEXT): $(unit_test_gobex_header_OBJECTS) $(unit_test_gobex_header_DEPENDENCIES) $(EXTRA_unit_test_gobex_header_DEPENDENCIES) unit/$(am__dirstamp) @rm -f unit/test-gobex-header$(EXEEXT) $(AM_V_CCLD)$(LINK) $(unit_test_gobex_header_OBJECTS) $(unit_test_gobex_header_LDADD) $(LIBS) unit/test-gobex-packet.$(OBJEXT): unit/$(am__dirstamp) \ unit/$(DEPDIR)/$(am__dirstamp) unit/test-gobex-packet$(EXEEXT): $(unit_test_gobex_packet_OBJECTS) $(unit_test_gobex_packet_DEPENDENCIES) $(EXTRA_unit_test_gobex_packet_DEPENDENCIES) unit/$(am__dirstamp) @rm -f unit/test-gobex-packet$(EXEEXT) $(AM_V_CCLD)$(LINK) $(unit_test_gobex_packet_OBJECTS) $(unit_test_gobex_packet_LDADD) $(LIBS) unit/test-gobex-transfer.$(OBJEXT): unit/$(am__dirstamp) \ unit/$(DEPDIR)/$(am__dirstamp) unit/test-gobex-transfer$(EXEEXT): $(unit_test_gobex_transfer_OBJECTS) $(unit_test_gobex_transfer_DEPENDENCIES) $(EXTRA_unit_test_gobex_transfer_DEPENDENCIES) unit/$(am__dirstamp) @rm -f unit/test-gobex-transfer$(EXEEXT) $(AM_V_CCLD)$(LINK) $(unit_test_gobex_transfer_OBJECTS) $(unit_test_gobex_transfer_LDADD) $(LIBS) unit/test-hfp.$(OBJEXT): unit/$(am__dirstamp) \ unit/$(DEPDIR)/$(am__dirstamp) unit/test-hfp$(EXEEXT): $(unit_test_hfp_OBJECTS) $(unit_test_hfp_DEPENDENCIES) $(EXTRA_unit_test_hfp_DEPENDENCIES) unit/$(am__dirstamp) @rm -f unit/test-hfp$(EXEEXT) $(AM_V_CCLD)$(LINK) $(unit_test_hfp_OBJECTS) $(unit_test_hfp_LDADD) $(LIBS) unit/test-hog.$(OBJEXT): unit/$(am__dirstamp) \ unit/$(DEPDIR)/$(am__dirstamp) unit/test-hog$(EXEEXT): $(unit_test_hog_OBJECTS) $(unit_test_hog_DEPENDENCIES) $(EXTRA_unit_test_hog_DEPENDENCIES) unit/$(am__dirstamp) @rm -f unit/test-hog$(EXEEXT) $(AM_V_CCLD)$(LINK) $(unit_test_hog_OBJECTS) $(unit_test_hog_LDADD) $(LIBS) unit/test-lib.$(OBJEXT): unit/$(am__dirstamp) \ unit/$(DEPDIR)/$(am__dirstamp) unit/test-lib$(EXEEXT): $(unit_test_lib_OBJECTS) $(unit_test_lib_DEPENDENCIES) $(EXTRA_unit_test_lib_DEPENDENCIES) unit/$(am__dirstamp) @rm -f unit/test-lib$(EXEEXT) $(AM_V_CCLD)$(LINK) $(unit_test_lib_OBJECTS) $(unit_test_lib_LDADD) $(LIBS) unit/test_mesh_crypto-test-mesh-crypto.$(OBJEXT): \ unit/$(am__dirstamp) unit/$(DEPDIR)/$(am__dirstamp) unit/test-mesh-crypto$(EXEEXT): $(unit_test_mesh_crypto_OBJECTS) $(unit_test_mesh_crypto_DEPENDENCIES) $(EXTRA_unit_test_mesh_crypto_DEPENDENCIES) unit/$(am__dirstamp) @rm -f unit/test-mesh-crypto$(EXEEXT) $(AM_V_CCLD)$(LINK) $(unit_test_mesh_crypto_OBJECTS) $(unit_test_mesh_crypto_LDADD) $(LIBS) unit/test-mgmt.$(OBJEXT): unit/$(am__dirstamp) \ unit/$(DEPDIR)/$(am__dirstamp) unit/test-mgmt$(EXEEXT): $(unit_test_mgmt_OBJECTS) $(unit_test_mgmt_DEPENDENCIES) $(EXTRA_unit_test_mgmt_DEPENDENCIES) unit/$(am__dirstamp) @rm -f unit/test-mgmt$(EXEEXT) $(AM_V_CCLD)$(LINK) $(unit_test_mgmt_OBJECTS) $(unit_test_mgmt_LDADD) $(LIBS) unit/test-micp.$(OBJEXT): unit/$(am__dirstamp) \ unit/$(DEPDIR)/$(am__dirstamp) unit/test-micp$(EXEEXT): $(unit_test_micp_OBJECTS) $(unit_test_micp_DEPENDENCIES) $(EXTRA_unit_test_micp_DEPENDENCIES) unit/$(am__dirstamp) @rm -f unit/test-micp$(EXEEXT) $(AM_V_CCLD)$(LINK) $(unit_test_micp_OBJECTS) $(unit_test_micp_LDADD) $(LIBS) unit/test_midi-test-midi.$(OBJEXT): unit/$(am__dirstamp) \ unit/$(DEPDIR)/$(am__dirstamp) profiles/midi/unit_test_midi-libmidi.$(OBJEXT): \ profiles/midi/$(am__dirstamp) \ profiles/midi/$(DEPDIR)/$(am__dirstamp) unit/test-midi$(EXEEXT): $(unit_test_midi_OBJECTS) $(unit_test_midi_DEPENDENCIES) $(EXTRA_unit_test_midi_DEPENDENCIES) unit/$(am__dirstamp) @rm -f unit/test-midi$(EXEEXT) $(AM_V_CCLD)$(LINK) $(unit_test_midi_OBJECTS) $(unit_test_midi_LDADD) $(LIBS) unit/test-queue.$(OBJEXT): unit/$(am__dirstamp) \ unit/$(DEPDIR)/$(am__dirstamp) unit/test-queue$(EXEEXT): $(unit_test_queue_OBJECTS) $(unit_test_queue_DEPENDENCIES) $(EXTRA_unit_test_queue_DEPENDENCIES) unit/$(am__dirstamp) @rm -f unit/test-queue$(EXEEXT) $(AM_V_CCLD)$(LINK) $(unit_test_queue_OBJECTS) $(unit_test_queue_LDADD) $(LIBS) unit/test-ringbuf.$(OBJEXT): unit/$(am__dirstamp) \ unit/$(DEPDIR)/$(am__dirstamp) unit/test-ringbuf$(EXEEXT): $(unit_test_ringbuf_OBJECTS) $(unit_test_ringbuf_DEPENDENCIES) $(EXTRA_unit_test_ringbuf_DEPENDENCIES) unit/$(am__dirstamp) @rm -f unit/test-ringbuf$(EXEEXT) $(AM_V_CCLD)$(LINK) $(unit_test_ringbuf_OBJECTS) $(unit_test_ringbuf_LDADD) $(LIBS) unit/test-sdp.$(OBJEXT): unit/$(am__dirstamp) \ unit/$(DEPDIR)/$(am__dirstamp) unit/test-sdp$(EXEEXT): $(unit_test_sdp_OBJECTS) $(unit_test_sdp_DEPENDENCIES) $(EXTRA_unit_test_sdp_DEPENDENCIES) unit/$(am__dirstamp) @rm -f unit/test-sdp$(EXEEXT) $(AM_V_CCLD)$(LINK) $(unit_test_sdp_OBJECTS) $(unit_test_sdp_LDADD) $(LIBS) unit/test-tester.$(OBJEXT): unit/$(am__dirstamp) \ unit/$(DEPDIR)/$(am__dirstamp) unit/test-tester$(EXEEXT): $(unit_test_tester_OBJECTS) $(unit_test_tester_DEPENDENCIES) $(EXTRA_unit_test_tester_DEPENDENCIES) unit/$(am__dirstamp) @rm -f unit/test-tester$(EXEEXT) $(AM_V_CCLD)$(LINK) $(unit_test_tester_OBJECTS) $(unit_test_tester_LDADD) $(LIBS) unit/test-textfile.$(OBJEXT): unit/$(am__dirstamp) \ unit/$(DEPDIR)/$(am__dirstamp) unit/test-textfile$(EXEEXT): $(unit_test_textfile_OBJECTS) $(unit_test_textfile_DEPENDENCIES) $(EXTRA_unit_test_textfile_DEPENDENCIES) unit/$(am__dirstamp) @rm -f unit/test-textfile$(EXEEXT) $(AM_V_CCLD)$(LINK) $(unit_test_textfile_OBJECTS) $(unit_test_textfile_LDADD) $(LIBS) unit/test-uhid.$(OBJEXT): unit/$(am__dirstamp) \ unit/$(DEPDIR)/$(am__dirstamp) unit/test-uhid$(EXEEXT): $(unit_test_uhid_OBJECTS) $(unit_test_uhid_DEPENDENCIES) $(EXTRA_unit_test_uhid_DEPENDENCIES) unit/$(am__dirstamp) @rm -f unit/test-uhid$(EXEEXT) $(AM_V_CCLD)$(LINK) $(unit_test_uhid_OBJECTS) $(unit_test_uhid_LDADD) $(LIBS) unit/test-uuid.$(OBJEXT): unit/$(am__dirstamp) \ unit/$(DEPDIR)/$(am__dirstamp) unit/test-uuid$(EXEEXT): $(unit_test_uuid_OBJECTS) $(unit_test_uuid_DEPENDENCIES) $(EXTRA_unit_test_uuid_DEPENDENCIES) unit/$(am__dirstamp) @rm -f unit/test-uuid$(EXEEXT) $(AM_V_CCLD)$(LINK) $(unit_test_uuid_OBJECTS) $(unit_test_uuid_LDADD) $(LIBS) unit/test-vcp.$(OBJEXT): unit/$(am__dirstamp) \ unit/$(DEPDIR)/$(am__dirstamp) unit/test-vcp$(EXEEXT): $(unit_test_vcp_OBJECTS) $(unit_test_vcp_DEPENDENCIES) $(EXTRA_unit_test_vcp_DEPENDENCIES) unit/$(am__dirstamp) @rm -f unit/test-vcp$(EXEEXT) $(AM_V_CCLD)$(LINK) $(unit_test_vcp_OBJECTS) $(unit_test_vcp_LDADD) $(LIBS) install-testSCRIPTS: $(test_SCRIPTS) @$(NORMAL_INSTALL) @list='$(test_SCRIPTS)'; test -n "$(testdir)" || list=; \ if test -n "$$list"; then \ echo " $(MKDIR_P) '$(DESTDIR)$(testdir)'"; \ $(MKDIR_P) "$(DESTDIR)$(testdir)" || exit 1; \ fi; \ for p in $$list; do \ if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \ if test -f "$$d$$p"; then echo "$$d$$p"; echo "$$p"; else :; fi; \ done | \ sed -e 'p;s,.*/,,;n' \ -e 'h;s|.*|.|' \ -e 'p;x;s,.*/,,;$(transform)' | sed 'N;N;N;s,\n, ,g' | \ $(AWK) 'BEGIN { files["."] = ""; dirs["."] = 1; } \ { d=$$3; if (dirs[d] != 1) { print "d", d; dirs[d] = 1 } \ if ($$2 == $$4) { files[d] = files[d] " " $$1; \ if (++n[d] == $(am__install_max)) { \ print "f", d, files[d]; n[d] = 0; files[d] = "" } } \ else { print "f", d "/" $$4, $$1 } } \ END { for (d in files) print "f", d, files[d] }' | \ while read type dir files; do \ if test "$$dir" = .; then dir=; else dir=/$$dir; fi; \ test -z "$$files" || { \ echo " $(INSTALL_SCRIPT) $$files '$(DESTDIR)$(testdir)$$dir'"; \ $(INSTALL_SCRIPT) $$files "$(DESTDIR)$(testdir)$$dir" || exit $$?; \ } \ ; done uninstall-testSCRIPTS: @$(NORMAL_UNINSTALL) @list='$(test_SCRIPTS)'; test -n "$(testdir)" || exit 0; \ files=`for p in $$list; do echo "$$p"; done | \ sed -e 's,.*/,,;$(transform)'`; \ dir='$(DESTDIR)$(testdir)'; $(am__uninstall_files_from_dir) mostlyclean-compile: -rm -f *.$(OBJEXT) -rm -f android/*.$(OBJEXT) -rm -f android/*.lo -rm -f android/audio_utils/*.$(OBJEXT) -rm -f android/audio_utils/*.lo -rm -f android/client/*.$(OBJEXT) -rm -f android/hardware/*.$(OBJEXT) -rm -f attrib/*.$(OBJEXT) -rm -f btio/*.$(OBJEXT) -rm -f client/*.$(OBJEXT) -rm -f ell/*.$(OBJEXT) -rm -f ell/*.lo -rm -f emulator/*.$(OBJEXT) -rm -f gdbus/*.$(OBJEXT) -rm -f gdbus/*.lo -rm -f gobex/*.$(OBJEXT) -rm -f lib/*.$(OBJEXT) -rm -f lib/*.lo -rm -f mesh/*.$(OBJEXT) -rm -f monitor/*.$(OBJEXT) -rm -f obexd/client/*.$(OBJEXT) -rm -f obexd/plugins/*.$(OBJEXT) -rm -f obexd/src/*.$(OBJEXT) -rm -f peripheral/*.$(OBJEXT) -rm -f plugins/*.$(OBJEXT) -rm -f profiles/audio/*.$(OBJEXT) -rm -f profiles/battery/*.$(OBJEXT) -rm -f profiles/cups/*.$(OBJEXT) -rm -f profiles/deviceinfo/*.$(OBJEXT) -rm -f profiles/gap/*.$(OBJEXT) -rm -f profiles/health/*.$(OBJEXT) -rm -f profiles/iap/*.$(OBJEXT) -rm -f profiles/input/*.$(OBJEXT) -rm -f profiles/midi/*.$(OBJEXT) -rm -f profiles/network/*.$(OBJEXT) -rm -f profiles/sap/*.$(OBJEXT) -rm -f profiles/scanparam/*.$(OBJEXT) -rm -f src/*.$(OBJEXT) -rm -f src/shared/*.$(OBJEXT) -rm -f src/shared/*.lo -rm -f tools/*.$(OBJEXT) -rm -f tools/mesh-gatt/*.$(OBJEXT) -rm -f tools/mesh/*.$(OBJEXT) -rm -f tools/parser/*.$(OBJEXT) -rm -f unit/*.$(OBJEXT) distclean-compile: -rm -f *.tab.c @AMDEP_TRUE@@am__include@ @am__quote@android/$(DEPDIR)/a2dp-sink.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@android/$(DEPDIR)/a2dp.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@android/$(DEPDIR)/android_tester-tester-a2dp.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@android/$(DEPDIR)/android_tester-tester-avrcp.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@android/$(DEPDIR)/android_tester-tester-bluetooth.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@android/$(DEPDIR)/android_tester-tester-gatt.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@android/$(DEPDIR)/android_tester-tester-hdp.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@android/$(DEPDIR)/android_tester-tester-hidhost.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@android/$(DEPDIR)/android_tester-tester-main.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@android/$(DEPDIR)/android_tester-tester-map-client.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@android/$(DEPDIR)/android_tester-tester-pan.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@android/$(DEPDIR)/android_tester-tester-socket.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@android/$(DEPDIR)/audio_a2dp_default_la-hal-audio-aptx.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@android/$(DEPDIR)/audio_a2dp_default_la-hal-audio-sbc.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@android/$(DEPDIR)/audio_a2dp_default_la-hal-audio.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@android/$(DEPDIR)/audio_sco_default_la-hal-sco.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@android/$(DEPDIR)/avctp.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@android/$(DEPDIR)/avdtp.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@android/$(DEPDIR)/avdtptest-avdtp.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@android/$(DEPDIR)/avdtptest-avdtptest.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@android/$(DEPDIR)/avrcp-lib.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@android/$(DEPDIR)/avrcp.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@android/$(DEPDIR)/bluetooth.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@android/$(DEPDIR)/bluetooth_default_la-hal-a2dp-sink.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@android/$(DEPDIR)/bluetooth_default_la-hal-a2dp.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@android/$(DEPDIR)/bluetooth_default_la-hal-avrcp-ctrl.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@android/$(DEPDIR)/bluetooth_default_la-hal-avrcp.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@android/$(DEPDIR)/bluetooth_default_la-hal-bluetooth.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@android/$(DEPDIR)/bluetooth_default_la-hal-gatt.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@android/$(DEPDIR)/bluetooth_default_la-hal-handsfree-client.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@android/$(DEPDIR)/bluetooth_default_la-hal-handsfree.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@android/$(DEPDIR)/bluetooth_default_la-hal-health.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@android/$(DEPDIR)/bluetooth_default_la-hal-hidhost.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@android/$(DEPDIR)/bluetooth_default_la-hal-ipc.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@android/$(DEPDIR)/bluetooth_default_la-hal-map-client.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@android/$(DEPDIR)/bluetooth_default_la-hal-pan.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@android/$(DEPDIR)/bluetooth_default_la-hal-socket.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@android/$(DEPDIR)/bluetooth_default_la-hal-utils.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@android/$(DEPDIR)/bluetoothd-snoop.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@android/$(DEPDIR)/gatt.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@android/$(DEPDIR)/haltest-hal-utils.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@android/$(DEPDIR)/handsfree-client.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@android/$(DEPDIR)/handsfree.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@android/$(DEPDIR)/health.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@android/$(DEPDIR)/hidhost.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@android/$(DEPDIR)/ipc.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@android/$(DEPDIR)/ipc_tester-hal-utils.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@android/$(DEPDIR)/ipc_tester-ipc-tester.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@android/$(DEPDIR)/main.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@android/$(DEPDIR)/map-client.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@android/$(DEPDIR)/pan.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@android/$(DEPDIR)/sco.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@android/$(DEPDIR)/socket.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@android/$(DEPDIR)/system-emulator.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@android/$(DEPDIR)/test-ipc.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@android/audio_utils/$(DEPDIR)/audio_sco_default_la-resampler.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@android/client/$(DEPDIR)/haltest-haltest.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@android/client/$(DEPDIR)/haltest-history.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@android/client/$(DEPDIR)/haltest-if-audio.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@android/client/$(DEPDIR)/haltest-if-av-sink.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@android/client/$(DEPDIR)/haltest-if-av.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@android/client/$(DEPDIR)/haltest-if-bt.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@android/client/$(DEPDIR)/haltest-if-gatt.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@android/client/$(DEPDIR)/haltest-if-hf-client.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@android/client/$(DEPDIR)/haltest-if-hf.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@android/client/$(DEPDIR)/haltest-if-hh.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@android/client/$(DEPDIR)/haltest-if-hl.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@android/client/$(DEPDIR)/haltest-if-mce.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@android/client/$(DEPDIR)/haltest-if-pan.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@android/client/$(DEPDIR)/haltest-if-rc-ctrl.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@android/client/$(DEPDIR)/haltest-if-rc.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@android/client/$(DEPDIR)/haltest-if-sco.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@android/client/$(DEPDIR)/haltest-if-sock.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@android/client/$(DEPDIR)/haltest-pollhandler.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@android/client/$(DEPDIR)/haltest-tabcompletion.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@android/client/$(DEPDIR)/haltest-terminal.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@android/hardware/$(DEPDIR)/android_tester-hardware.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@android/hardware/$(DEPDIR)/haltest-hardware.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@attrib/$(DEPDIR)/att.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@attrib/$(DEPDIR)/bluetoothd-att.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@attrib/$(DEPDIR)/bluetoothd-gatt.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@attrib/$(DEPDIR)/bluetoothd-gattrib.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@attrib/$(DEPDIR)/gatt.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@attrib/$(DEPDIR)/gattrib.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@attrib/$(DEPDIR)/gatttool.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@attrib/$(DEPDIR)/interactive.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@attrib/$(DEPDIR)/utils.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@btio/$(DEPDIR)/android_avdtptest-btio.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@btio/$(DEPDIR)/bluetoothd-btio.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@btio/$(DEPDIR)/btio.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@btio/$(DEPDIR)/obexd-btio.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@client/$(DEPDIR)/admin.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@client/$(DEPDIR)/adv_monitor.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@client/$(DEPDIR)/advertising.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@client/$(DEPDIR)/agent.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@client/$(DEPDIR)/assistant.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@client/$(DEPDIR)/display.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@client/$(DEPDIR)/gatt.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@client/$(DEPDIR)/hci.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@client/$(DEPDIR)/main.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@client/$(DEPDIR)/mgmt.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@client/$(DEPDIR)/player.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@client/$(DEPDIR)/print.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@ell/$(DEPDIR)/base64.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@ell/$(DEPDIR)/cert-crypto.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@ell/$(DEPDIR)/cert.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@ell/$(DEPDIR)/checksum.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@ell/$(DEPDIR)/cipher.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@ell/$(DEPDIR)/dbus-client.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@ell/$(DEPDIR)/dbus-filter.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@ell/$(DEPDIR)/dbus-message.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@ell/$(DEPDIR)/dbus-name-cache.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@ell/$(DEPDIR)/dbus-service.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@ell/$(DEPDIR)/dbus-util.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@ell/$(DEPDIR)/dbus.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@ell/$(DEPDIR)/ecc-external.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@ell/$(DEPDIR)/ecc.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@ell/$(DEPDIR)/ecdh.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@ell/$(DEPDIR)/gvariant-util.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@ell/$(DEPDIR)/hashmap.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@ell/$(DEPDIR)/idle.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@ell/$(DEPDIR)/io.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@ell/$(DEPDIR)/key.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@ell/$(DEPDIR)/log.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@ell/$(DEPDIR)/main.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@ell/$(DEPDIR)/pem.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@ell/$(DEPDIR)/queue.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@ell/$(DEPDIR)/random.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@ell/$(DEPDIR)/settings.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@ell/$(DEPDIR)/signal.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@ell/$(DEPDIR)/siphash.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@ell/$(DEPDIR)/string.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@ell/$(DEPDIR)/strv.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@ell/$(DEPDIR)/tester.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@ell/$(DEPDIR)/time.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@ell/$(DEPDIR)/timeout.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@ell/$(DEPDIR)/tls-extensions.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@ell/$(DEPDIR)/tls-record.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@ell/$(DEPDIR)/tls-suites.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@ell/$(DEPDIR)/tls.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@ell/$(DEPDIR)/utf8.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@ell/$(DEPDIR)/util.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@ell/$(DEPDIR)/uuid.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@emulator/$(DEPDIR)/android_android_tester-btdev.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@emulator/$(DEPDIR)/android_android_tester-bthost.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@emulator/$(DEPDIR)/android_android_tester-hciemu.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@emulator/$(DEPDIR)/android_android_tester-smp.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@emulator/$(DEPDIR)/android_android_tester-vhci.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@emulator/$(DEPDIR)/android_ipc_tester-btdev.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@emulator/$(DEPDIR)/android_ipc_tester-bthost.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@emulator/$(DEPDIR)/android_ipc_tester-hciemu.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@emulator/$(DEPDIR)/android_ipc_tester-smp.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@emulator/$(DEPDIR)/android_ipc_tester-vhci.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@emulator/$(DEPDIR)/b1ee.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@emulator/$(DEPDIR)/btdev.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@emulator/$(DEPDIR)/bthost.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@emulator/$(DEPDIR)/hciemu.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@emulator/$(DEPDIR)/hfp.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@emulator/$(DEPDIR)/le.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@emulator/$(DEPDIR)/main.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@emulator/$(DEPDIR)/phy.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@emulator/$(DEPDIR)/serial.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@emulator/$(DEPDIR)/server.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@emulator/$(DEPDIR)/smp.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@emulator/$(DEPDIR)/vhci.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@gdbus/$(DEPDIR)/client.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@gdbus/$(DEPDIR)/mainloop.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@gdbus/$(DEPDIR)/object.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@gdbus/$(DEPDIR)/polkit.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@gdbus/$(DEPDIR)/watch.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@gobex/$(DEPDIR)/gobex-apparam.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@gobex/$(DEPDIR)/gobex-defs.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@gobex/$(DEPDIR)/gobex-header.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@gobex/$(DEPDIR)/gobex-packet.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@gobex/$(DEPDIR)/gobex-transfer.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@gobex/$(DEPDIR)/gobex.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@gobex/$(DEPDIR)/obexd-gobex-apparam.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@gobex/$(DEPDIR)/obexd-gobex-defs.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@gobex/$(DEPDIR)/obexd-gobex-header.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@gobex/$(DEPDIR)/obexd-gobex-packet.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@gobex/$(DEPDIR)/obexd-gobex-transfer.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@gobex/$(DEPDIR)/obexd-gobex.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@lib/$(DEPDIR)/bluetooth.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@lib/$(DEPDIR)/hci.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@lib/$(DEPDIR)/sdp.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@lib/$(DEPDIR)/uuid.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@mesh/$(DEPDIR)/agent.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@mesh/$(DEPDIR)/appkey.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@mesh/$(DEPDIR)/cfgmod-server.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@mesh/$(DEPDIR)/crypto.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@mesh/$(DEPDIR)/dbus.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@mesh/$(DEPDIR)/friend.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@mesh/$(DEPDIR)/keyring.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@mesh/$(DEPDIR)/main.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@mesh/$(DEPDIR)/manager.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@mesh/$(DEPDIR)/mesh-config-json.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@mesh/$(DEPDIR)/mesh-io-generic.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@mesh/$(DEPDIR)/mesh-io-mgmt.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@mesh/$(DEPDIR)/mesh-io-unit.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@mesh/$(DEPDIR)/mesh-io.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@mesh/$(DEPDIR)/mesh-mgmt.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@mesh/$(DEPDIR)/mesh.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@mesh/$(DEPDIR)/model.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@mesh/$(DEPDIR)/net-keys.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@mesh/$(DEPDIR)/net.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@mesh/$(DEPDIR)/node.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@mesh/$(DEPDIR)/pb-adv.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@mesh/$(DEPDIR)/prov-acceptor.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@mesh/$(DEPDIR)/prov-initiator.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@mesh/$(DEPDIR)/prvbeac-server.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@mesh/$(DEPDIR)/remprv-server.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@mesh/$(DEPDIR)/rpl.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@mesh/$(DEPDIR)/util.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@monitor/$(DEPDIR)/a2dp.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@monitor/$(DEPDIR)/analyze.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@monitor/$(DEPDIR)/att.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@monitor/$(DEPDIR)/avctp.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@monitor/$(DEPDIR)/avdtp.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@monitor/$(DEPDIR)/bnep.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@monitor/$(DEPDIR)/broadcom.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@monitor/$(DEPDIR)/control.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@monitor/$(DEPDIR)/crc.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@monitor/$(DEPDIR)/display.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@monitor/$(DEPDIR)/ellisys.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@monitor/$(DEPDIR)/hcidump.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@monitor/$(DEPDIR)/hwdb.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@monitor/$(DEPDIR)/intel.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@monitor/$(DEPDIR)/jlink.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@monitor/$(DEPDIR)/keys.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@monitor/$(DEPDIR)/l2cap.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@monitor/$(DEPDIR)/ll.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@monitor/$(DEPDIR)/lmp.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@monitor/$(DEPDIR)/main.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@monitor/$(DEPDIR)/msft.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@monitor/$(DEPDIR)/packet.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@monitor/$(DEPDIR)/rfcomm.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@monitor/$(DEPDIR)/sdp.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@monitor/$(DEPDIR)/vendor.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@obexd/client/$(DEPDIR)/obexd-bip-common.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@obexd/client/$(DEPDIR)/obexd-bip.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@obexd/client/$(DEPDIR)/obexd-bluetooth.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@obexd/client/$(DEPDIR)/obexd-driver.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@obexd/client/$(DEPDIR)/obexd-ftp.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@obexd/client/$(DEPDIR)/obexd-manager.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@obexd/client/$(DEPDIR)/obexd-map-event.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@obexd/client/$(DEPDIR)/obexd-map.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@obexd/client/$(DEPDIR)/obexd-mns.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@obexd/client/$(DEPDIR)/obexd-opp.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@obexd/client/$(DEPDIR)/obexd-pbap.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@obexd/client/$(DEPDIR)/obexd-session.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@obexd/client/$(DEPDIR)/obexd-sync.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@obexd/client/$(DEPDIR)/obexd-transfer.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@obexd/client/$(DEPDIR)/obexd-transport.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@obexd/plugins/$(DEPDIR)/obexd-bluetooth.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@obexd/plugins/$(DEPDIR)/obexd-filesystem.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@obexd/plugins/$(DEPDIR)/obexd-ftp.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@obexd/plugins/$(DEPDIR)/obexd-irmc.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@obexd/plugins/$(DEPDIR)/obexd-mas.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@obexd/plugins/$(DEPDIR)/obexd-messages-dummy.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@obexd/plugins/$(DEPDIR)/obexd-opp.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@obexd/plugins/$(DEPDIR)/obexd-pbap.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@obexd/plugins/$(DEPDIR)/obexd-pcsuite.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@obexd/plugins/$(DEPDIR)/obexd-phonebook-@PLUGIN_PHONEBOOK@.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@obexd/plugins/$(DEPDIR)/obexd-vcard.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@obexd/src/$(DEPDIR)/obexd-log.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@obexd/src/$(DEPDIR)/obexd-main.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@obexd/src/$(DEPDIR)/obexd-manager.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@obexd/src/$(DEPDIR)/obexd-mimetype.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@obexd/src/$(DEPDIR)/obexd-obex.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@obexd/src/$(DEPDIR)/obexd-plugin.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@obexd/src/$(DEPDIR)/obexd-server.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@obexd/src/$(DEPDIR)/obexd-service.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@obexd/src/$(DEPDIR)/obexd-transport.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@peripheral/$(DEPDIR)/attach.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@peripheral/$(DEPDIR)/efivars.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@peripheral/$(DEPDIR)/gap.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@peripheral/$(DEPDIR)/gatt.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@peripheral/$(DEPDIR)/log.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@peripheral/$(DEPDIR)/main.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@plugins/$(DEPDIR)/bluetoothd-admin.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@plugins/$(DEPDIR)/bluetoothd-autopair.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@plugins/$(DEPDIR)/bluetoothd-hostname.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@plugins/$(DEPDIR)/bluetoothd-neard.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@plugins/$(DEPDIR)/bluetoothd-policy.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@plugins/$(DEPDIR)/bluetoothd-sixaxis.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@profiles/audio/$(DEPDIR)/bluetoothd-a2dp.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@profiles/audio/$(DEPDIR)/bluetoothd-asha.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@profiles/audio/$(DEPDIR)/bluetoothd-avctp.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@profiles/audio/$(DEPDIR)/bluetoothd-avdtp.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@profiles/audio/$(DEPDIR)/bluetoothd-avrcp.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@profiles/audio/$(DEPDIR)/bluetoothd-bap.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@profiles/audio/$(DEPDIR)/bluetoothd-bass.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@profiles/audio/$(DEPDIR)/bluetoothd-ccp.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@profiles/audio/$(DEPDIR)/bluetoothd-control.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@profiles/audio/$(DEPDIR)/bluetoothd-csip.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@profiles/audio/$(DEPDIR)/bluetoothd-mcp.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@profiles/audio/$(DEPDIR)/bluetoothd-media.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@profiles/audio/$(DEPDIR)/bluetoothd-micp.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@profiles/audio/$(DEPDIR)/bluetoothd-player.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@profiles/audio/$(DEPDIR)/bluetoothd-sink.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@profiles/audio/$(DEPDIR)/bluetoothd-source.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@profiles/audio/$(DEPDIR)/bluetoothd-transport.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@profiles/audio/$(DEPDIR)/bluetoothd-vcp.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@profiles/battery/$(DEPDIR)/bas.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@profiles/battery/$(DEPDIR)/bluetoothd-bas.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@profiles/battery/$(DEPDIR)/bluetoothd-battery.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@profiles/cups/$(DEPDIR)/hcrp.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@profiles/cups/$(DEPDIR)/main.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@profiles/cups/$(DEPDIR)/sdp.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@profiles/cups/$(DEPDIR)/spp.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@profiles/deviceinfo/$(DEPDIR)/bluetoothd-deviceinfo.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@profiles/deviceinfo/$(DEPDIR)/bluetoothd-dis.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@profiles/deviceinfo/$(DEPDIR)/dis.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@profiles/gap/$(DEPDIR)/bluetoothd-gas.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@profiles/health/$(DEPDIR)/bluetoothd-hdp.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@profiles/health/$(DEPDIR)/bluetoothd-hdp_main.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@profiles/health/$(DEPDIR)/bluetoothd-hdp_manager.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@profiles/health/$(DEPDIR)/bluetoothd-hdp_util.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@profiles/health/$(DEPDIR)/bluetoothd-mcap.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@profiles/health/$(DEPDIR)/mcap.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@profiles/iap/$(DEPDIR)/main.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@profiles/input/$(DEPDIR)/bluetoothd-device.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@profiles/input/$(DEPDIR)/bluetoothd-hog-lib.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@profiles/input/$(DEPDIR)/bluetoothd-hog.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@profiles/input/$(DEPDIR)/bluetoothd-manager.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@profiles/input/$(DEPDIR)/bluetoothd-server.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@profiles/input/$(DEPDIR)/bluetoothd-suspend-none.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@profiles/input/$(DEPDIR)/hog-lib.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@profiles/midi/$(DEPDIR)/bluetoothd-libmidi.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@profiles/midi/$(DEPDIR)/bluetoothd-midi.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@profiles/midi/$(DEPDIR)/unit_test_midi-libmidi.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@profiles/network/$(DEPDIR)/bluetoothd-bnep.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@profiles/network/$(DEPDIR)/bluetoothd-connection.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@profiles/network/$(DEPDIR)/bluetoothd-manager.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@profiles/network/$(DEPDIR)/bluetoothd-server.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@profiles/network/$(DEPDIR)/bnep.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@profiles/sap/$(DEPDIR)/bluetoothd-main.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@profiles/sap/$(DEPDIR)/bluetoothd-manager.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@profiles/sap/$(DEPDIR)/bluetoothd-sap-dummy.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@profiles/sap/$(DEPDIR)/bluetoothd-server.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@profiles/scanparam/$(DEPDIR)/bluetoothd-scan.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@profiles/scanparam/$(DEPDIR)/bluetoothd-scpp.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@profiles/scanparam/$(DEPDIR)/scpp.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@src/$(DEPDIR)/android_avdtptest-log.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@src/$(DEPDIR)/bluetoothd-adapter.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@src/$(DEPDIR)/bluetoothd-adv_monitor.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@src/$(DEPDIR)/bluetoothd-advertising.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@src/$(DEPDIR)/bluetoothd-agent.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@src/$(DEPDIR)/bluetoothd-backtrace.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@src/$(DEPDIR)/bluetoothd-battery.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@src/$(DEPDIR)/bluetoothd-dbus-common.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@src/$(DEPDIR)/bluetoothd-device.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@src/$(DEPDIR)/bluetoothd-eir.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@src/$(DEPDIR)/bluetoothd-error.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@src/$(DEPDIR)/bluetoothd-gatt-client.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@src/$(DEPDIR)/bluetoothd-gatt-database.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@src/$(DEPDIR)/bluetoothd-log.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@src/$(DEPDIR)/bluetoothd-main.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@src/$(DEPDIR)/bluetoothd-plugin.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@src/$(DEPDIR)/bluetoothd-profile.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@src/$(DEPDIR)/bluetoothd-rfkill.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@src/$(DEPDIR)/bluetoothd-sdp-client.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@src/$(DEPDIR)/bluetoothd-sdp-xml.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@src/$(DEPDIR)/bluetoothd-sdpd-database.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@src/$(DEPDIR)/bluetoothd-sdpd-request.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@src/$(DEPDIR)/bluetoothd-sdpd-server.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@src/$(DEPDIR)/bluetoothd-sdpd-service.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@src/$(DEPDIR)/bluetoothd-service.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@src/$(DEPDIR)/bluetoothd-set.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@src/$(DEPDIR)/bluetoothd-settings.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@src/$(DEPDIR)/bluetoothd-storage.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@src/$(DEPDIR)/bluetoothd-textfile.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@src/$(DEPDIR)/bluetoothd-uuid-helper.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@src/$(DEPDIR)/eir.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@src/$(DEPDIR)/log.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@src/$(DEPDIR)/oui.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@src/$(DEPDIR)/sdp-client.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@src/$(DEPDIR)/sdp-xml.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@src/$(DEPDIR)/sdpd-database.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@src/$(DEPDIR)/sdpd-request.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@src/$(DEPDIR)/sdpd-server.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@src/$(DEPDIR)/sdpd-service.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@src/$(DEPDIR)/settings.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@src/$(DEPDIR)/textfile.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@src/$(DEPDIR)/uuid-helper.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@src/shared/$(DEPDIR)/android_avdtptest-log.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@src/shared/$(DEPDIR)/android_avdtptest-queue.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@src/shared/$(DEPDIR)/android_avdtptest-util.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@src/shared/$(DEPDIR)/btp.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@src/shared/$(DEPDIR)/libshared_ell_la-ad.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@src/shared/$(DEPDIR)/libshared_ell_la-asha.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@src/shared/$(DEPDIR)/libshared_ell_la-att.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@src/shared/$(DEPDIR)/libshared_ell_la-bap-debug.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@src/shared/$(DEPDIR)/libshared_ell_la-bap.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@src/shared/$(DEPDIR)/libshared_ell_la-bass.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@src/shared/$(DEPDIR)/libshared_ell_la-btsnoop.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@src/shared/$(DEPDIR)/libshared_ell_la-ccp.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@src/shared/$(DEPDIR)/libshared_ell_la-crypto.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@src/shared/$(DEPDIR)/libshared_ell_la-csip.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@src/shared/$(DEPDIR)/libshared_ell_la-ecc.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@src/shared/$(DEPDIR)/libshared_ell_la-gap.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@src/shared/$(DEPDIR)/libshared_ell_la-gatt-client.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@src/shared/$(DEPDIR)/libshared_ell_la-gatt-db.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@src/shared/$(DEPDIR)/libshared_ell_la-gatt-helpers.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@src/shared/$(DEPDIR)/libshared_ell_la-gatt-server.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@src/shared/$(DEPDIR)/libshared_ell_la-hci-crypto.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@src/shared/$(DEPDIR)/libshared_ell_la-hci.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@src/shared/$(DEPDIR)/libshared_ell_la-hfp.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@src/shared/$(DEPDIR)/libshared_ell_la-io-ell.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@src/shared/$(DEPDIR)/libshared_ell_la-log.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@src/shared/$(DEPDIR)/libshared_ell_la-mainloop-ell.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@src/shared/$(DEPDIR)/libshared_ell_la-mcp.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@src/shared/$(DEPDIR)/libshared_ell_la-mgmt.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@src/shared/$(DEPDIR)/libshared_ell_la-micp.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@src/shared/$(DEPDIR)/libshared_ell_la-pcap.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@src/shared/$(DEPDIR)/libshared_ell_la-queue.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@src/shared/$(DEPDIR)/libshared_ell_la-ringbuf.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@src/shared/$(DEPDIR)/libshared_ell_la-shell.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@src/shared/$(DEPDIR)/libshared_ell_la-timeout-ell.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@src/shared/$(DEPDIR)/libshared_ell_la-uhid.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@src/shared/$(DEPDIR)/libshared_ell_la-util.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@src/shared/$(DEPDIR)/libshared_ell_la-vcp.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@src/shared/$(DEPDIR)/libshared_glib_la-ad.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@src/shared/$(DEPDIR)/libshared_glib_la-asha.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@src/shared/$(DEPDIR)/libshared_glib_la-att.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@src/shared/$(DEPDIR)/libshared_glib_la-bap-debug.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@src/shared/$(DEPDIR)/libshared_glib_la-bap.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@src/shared/$(DEPDIR)/libshared_glib_la-bass.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@src/shared/$(DEPDIR)/libshared_glib_la-btsnoop.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@src/shared/$(DEPDIR)/libshared_glib_la-ccp.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@src/shared/$(DEPDIR)/libshared_glib_la-crypto.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@src/shared/$(DEPDIR)/libshared_glib_la-csip.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@src/shared/$(DEPDIR)/libshared_glib_la-ecc.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@src/shared/$(DEPDIR)/libshared_glib_la-gap.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@src/shared/$(DEPDIR)/libshared_glib_la-gatt-client.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@src/shared/$(DEPDIR)/libshared_glib_la-gatt-db.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@src/shared/$(DEPDIR)/libshared_glib_la-gatt-helpers.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@src/shared/$(DEPDIR)/libshared_glib_la-gatt-server.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@src/shared/$(DEPDIR)/libshared_glib_la-hci-crypto.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@src/shared/$(DEPDIR)/libshared_glib_la-hci.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@src/shared/$(DEPDIR)/libshared_glib_la-hfp.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@src/shared/$(DEPDIR)/libshared_glib_la-io-glib.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@src/shared/$(DEPDIR)/libshared_glib_la-log.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@src/shared/$(DEPDIR)/libshared_glib_la-mainloop-glib.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@src/shared/$(DEPDIR)/libshared_glib_la-mainloop-notify.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@src/shared/$(DEPDIR)/libshared_glib_la-mcp.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@src/shared/$(DEPDIR)/libshared_glib_la-mgmt.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@src/shared/$(DEPDIR)/libshared_glib_la-micp.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@src/shared/$(DEPDIR)/libshared_glib_la-pcap.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@src/shared/$(DEPDIR)/libshared_glib_la-queue.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@src/shared/$(DEPDIR)/libshared_glib_la-ringbuf.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@src/shared/$(DEPDIR)/libshared_glib_la-shell.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@src/shared/$(DEPDIR)/libshared_glib_la-tester.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@src/shared/$(DEPDIR)/libshared_glib_la-timeout-glib.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@src/shared/$(DEPDIR)/libshared_glib_la-uhid.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@src/shared/$(DEPDIR)/libshared_glib_la-util.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@src/shared/$(DEPDIR)/libshared_glib_la-vcp.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@src/shared/$(DEPDIR)/libshared_mainloop_la-ad.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@src/shared/$(DEPDIR)/libshared_mainloop_la-asha.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@src/shared/$(DEPDIR)/libshared_mainloop_la-att.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@src/shared/$(DEPDIR)/libshared_mainloop_la-bap-debug.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@src/shared/$(DEPDIR)/libshared_mainloop_la-bap.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@src/shared/$(DEPDIR)/libshared_mainloop_la-bass.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@src/shared/$(DEPDIR)/libshared_mainloop_la-btsnoop.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@src/shared/$(DEPDIR)/libshared_mainloop_la-ccp.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@src/shared/$(DEPDIR)/libshared_mainloop_la-crypto.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@src/shared/$(DEPDIR)/libshared_mainloop_la-csip.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@src/shared/$(DEPDIR)/libshared_mainloop_la-ecc.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@src/shared/$(DEPDIR)/libshared_mainloop_la-gap.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@src/shared/$(DEPDIR)/libshared_mainloop_la-gatt-client.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@src/shared/$(DEPDIR)/libshared_mainloop_la-gatt-db.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@src/shared/$(DEPDIR)/libshared_mainloop_la-gatt-helpers.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@src/shared/$(DEPDIR)/libshared_mainloop_la-gatt-server.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@src/shared/$(DEPDIR)/libshared_mainloop_la-hci-crypto.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@src/shared/$(DEPDIR)/libshared_mainloop_la-hci.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@src/shared/$(DEPDIR)/libshared_mainloop_la-hfp.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@src/shared/$(DEPDIR)/libshared_mainloop_la-io-mainloop.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@src/shared/$(DEPDIR)/libshared_mainloop_la-log.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@src/shared/$(DEPDIR)/libshared_mainloop_la-mainloop-notify.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@src/shared/$(DEPDIR)/libshared_mainloop_la-mainloop.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@src/shared/$(DEPDIR)/libshared_mainloop_la-mcp.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@src/shared/$(DEPDIR)/libshared_mainloop_la-mgmt.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@src/shared/$(DEPDIR)/libshared_mainloop_la-micp.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@src/shared/$(DEPDIR)/libshared_mainloop_la-pcap.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@src/shared/$(DEPDIR)/libshared_mainloop_la-queue.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@src/shared/$(DEPDIR)/libshared_mainloop_la-ringbuf.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@src/shared/$(DEPDIR)/libshared_mainloop_la-shell.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@src/shared/$(DEPDIR)/libshared_mainloop_la-timeout-mainloop.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@src/shared/$(DEPDIR)/libshared_mainloop_la-uhid.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@src/shared/$(DEPDIR)/libshared_mainloop_la-util.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@src/shared/$(DEPDIR)/libshared_mainloop_la-vcp.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@tools/$(DEPDIR)/3dsp.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@tools/$(DEPDIR)/advtest.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@tools/$(DEPDIR)/avinfo.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@tools/$(DEPDIR)/avtest.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@tools/$(DEPDIR)/bcmfw.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@tools/$(DEPDIR)/bdaddr.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@tools/$(DEPDIR)/bluemoon.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@tools/$(DEPDIR)/bluetooth-player.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@tools/$(DEPDIR)/bnep-tester.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@tools/$(DEPDIR)/bneptest.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@tools/$(DEPDIR)/btattach.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@tools/$(DEPDIR)/btconfig.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@tools/$(DEPDIR)/btgatt-client.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@tools/$(DEPDIR)/btgatt-server.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@tools/$(DEPDIR)/btinfo.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@tools/$(DEPDIR)/btiotest.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@tools/$(DEPDIR)/btmgmt.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@tools/$(DEPDIR)/btmon-logger.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@tools/$(DEPDIR)/btpclient.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@tools/$(DEPDIR)/btpclientctl.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@tools/$(DEPDIR)/btproxy.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@tools/$(DEPDIR)/btsnoop.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@tools/$(DEPDIR)/check-selftest.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@tools/$(DEPDIR)/ciptool.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@tools/$(DEPDIR)/cltest.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@tools/$(DEPDIR)/create-image.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@tools/$(DEPDIR)/eddystone.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@tools/$(DEPDIR)/gap-tester.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@tools/$(DEPDIR)/gatt-service.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@tools/$(DEPDIR)/hci-tester.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@tools/$(DEPDIR)/hciattach.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@tools/$(DEPDIR)/hciattach_ath3k.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@tools/$(DEPDIR)/hciattach_bcm43xx.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@tools/$(DEPDIR)/hciattach_intel.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@tools/$(DEPDIR)/hciattach_qualcomm.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@tools/$(DEPDIR)/hciattach_st.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@tools/$(DEPDIR)/hciattach_ti.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@tools/$(DEPDIR)/hciattach_tialt.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@tools/$(DEPDIR)/hciconfig.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@tools/$(DEPDIR)/hcidump.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@tools/$(DEPDIR)/hcieventmask.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@tools/$(DEPDIR)/hcisecfilter.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@tools/$(DEPDIR)/hcitool.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@tools/$(DEPDIR)/hex2hcd.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@tools/$(DEPDIR)/hid2hci.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@tools/$(DEPDIR)/hwdb.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@tools/$(DEPDIR)/ibeacon.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@tools/$(DEPDIR)/ioctl-tester.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@tools/$(DEPDIR)/iso-tester.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@tools/$(DEPDIR)/isotest.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@tools/$(DEPDIR)/l2cap-tester.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@tools/$(DEPDIR)/l2ping.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@tools/$(DEPDIR)/l2test.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@tools/$(DEPDIR)/mcaptest.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@tools/$(DEPDIR)/mesh-cfgclient.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@tools/$(DEPDIR)/mesh-cfgtest.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@tools/$(DEPDIR)/mesh-tester.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@tools/$(DEPDIR)/meshctl.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@tools/$(DEPDIR)/mgmt-tester.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@tools/$(DEPDIR)/mpris-proxy.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@tools/$(DEPDIR)/nokfw.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@tools/$(DEPDIR)/obex-client-tool.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@tools/$(DEPDIR)/obex-server-tool.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@tools/$(DEPDIR)/obexctl.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@tools/$(DEPDIR)/oobtest.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@tools/$(DEPDIR)/rctest.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@tools/$(DEPDIR)/rfcomm-tester.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@tools/$(DEPDIR)/rfcomm.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@tools/$(DEPDIR)/rtlfw.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@tools/$(DEPDIR)/sco-tester.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@tools/$(DEPDIR)/scotest.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@tools/$(DEPDIR)/sdptool.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@tools/$(DEPDIR)/seq2bseq.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@tools/$(DEPDIR)/smp-tester.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@tools/$(DEPDIR)/test-runner.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@tools/$(DEPDIR)/userchan-tester.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@tools/mesh-gatt/$(DEPDIR)/config-client.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@tools/mesh-gatt/$(DEPDIR)/config-server.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@tools/mesh-gatt/$(DEPDIR)/crypto.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@tools/mesh-gatt/$(DEPDIR)/gatt.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@tools/mesh-gatt/$(DEPDIR)/net.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@tools/mesh-gatt/$(DEPDIR)/node.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@tools/mesh-gatt/$(DEPDIR)/onoff-model.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@tools/mesh-gatt/$(DEPDIR)/prov-db.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@tools/mesh-gatt/$(DEPDIR)/prov.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@tools/mesh-gatt/$(DEPDIR)/util.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@tools/mesh/$(DEPDIR)/agent.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@tools/mesh/$(DEPDIR)/cfgcli.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@tools/mesh/$(DEPDIR)/keys.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@tools/mesh/$(DEPDIR)/mesh-db.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@tools/mesh/$(DEPDIR)/remote.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@tools/mesh/$(DEPDIR)/util.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@tools/parser/$(DEPDIR)/att.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@tools/parser/$(DEPDIR)/avctp.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@tools/parser/$(DEPDIR)/avdtp.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@tools/parser/$(DEPDIR)/avrcp.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@tools/parser/$(DEPDIR)/bnep.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@tools/parser/$(DEPDIR)/bpa.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@tools/parser/$(DEPDIR)/capi.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@tools/parser/$(DEPDIR)/cmtp.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@tools/parser/$(DEPDIR)/csr.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@tools/parser/$(DEPDIR)/ericsson.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@tools/parser/$(DEPDIR)/hci.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@tools/parser/$(DEPDIR)/hcrp.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@tools/parser/$(DEPDIR)/hidp.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@tools/parser/$(DEPDIR)/l2cap.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@tools/parser/$(DEPDIR)/lmp.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@tools/parser/$(DEPDIR)/obex.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@tools/parser/$(DEPDIR)/parser.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@tools/parser/$(DEPDIR)/ppp.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@tools/parser/$(DEPDIR)/rfcomm.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@tools/parser/$(DEPDIR)/sap.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@tools/parser/$(DEPDIR)/sdp.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@tools/parser/$(DEPDIR)/smp.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@tools/parser/$(DEPDIR)/tcpip.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@unit/$(DEPDIR)/test-avctp.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@unit/$(DEPDIR)/test-avdtp.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@unit/$(DEPDIR)/test-avrcp.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@unit/$(DEPDIR)/test-bap.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@unit/$(DEPDIR)/test-bass.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@unit/$(DEPDIR)/test-crc.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@unit/$(DEPDIR)/test-crypto.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@unit/$(DEPDIR)/test-ecc.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@unit/$(DEPDIR)/test-eir.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@unit/$(DEPDIR)/test-gatt.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@unit/$(DEPDIR)/test-gattrib.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@unit/$(DEPDIR)/test-gdbus-client.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@unit/$(DEPDIR)/test-gobex-apparam.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@unit/$(DEPDIR)/test-gobex-header.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@unit/$(DEPDIR)/test-gobex-packet.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@unit/$(DEPDIR)/test-gobex-transfer.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@unit/$(DEPDIR)/test-gobex.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@unit/$(DEPDIR)/test-hfp.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@unit/$(DEPDIR)/test-hog.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@unit/$(DEPDIR)/test-lib.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@unit/$(DEPDIR)/test-mgmt.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@unit/$(DEPDIR)/test-micp.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@unit/$(DEPDIR)/test-queue.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@unit/$(DEPDIR)/test-ringbuf.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@unit/$(DEPDIR)/test-sdp.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@unit/$(DEPDIR)/test-tester.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@unit/$(DEPDIR)/test-textfile.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@unit/$(DEPDIR)/test-uhid.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@unit/$(DEPDIR)/test-uuid.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@unit/$(DEPDIR)/test-vcp.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@unit/$(DEPDIR)/test_mesh_crypto-test-mesh-crypto.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@unit/$(DEPDIR)/test_midi-test-midi.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@unit/$(DEPDIR)/util.Po@am__quote@ # am--include-marker $(am__depfiles_remade): @$(MKDIR_P) $(@D) @echo '# dummy' >$@-t && $(am__mv) $@-t $@ am--depfiles: $(am__depfiles_remade) .c.o: @am__fastdepCC_TRUE@ $(AM_V_CC)depbase=`echo $@ | sed 's|[^/]*$$|$(DEPDIR)/&|;s|\.o$$||'`;\ @am__fastdepCC_TRUE@ $(COMPILE) -MT $@ -MD -MP -MF $$depbase.Tpo -c -o $@ $< &&\ @am__fastdepCC_TRUE@ $(am__mv) $$depbase.Tpo $$depbase.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ $< .c.obj: @am__fastdepCC_TRUE@ $(AM_V_CC)depbase=`echo $@ | sed 's|[^/]*$$|$(DEPDIR)/&|;s|\.obj$$||'`;\ @am__fastdepCC_TRUE@ $(COMPILE) -MT $@ -MD -MP -MF $$depbase.Tpo -c -o $@ `$(CYGPATH_W) '$<'` &&\ @am__fastdepCC_TRUE@ $(am__mv) $$depbase.Tpo $$depbase.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ `$(CYGPATH_W) '$<'` .c.lo: @am__fastdepCC_TRUE@ $(AM_V_CC)depbase=`echo $@ | sed 's|[^/]*$$|$(DEPDIR)/&|;s|\.lo$$||'`;\ @am__fastdepCC_TRUE@ $(LTCOMPILE) -MT $@ -MD -MP -MF $$depbase.Tpo -c -o $@ $< &&\ @am__fastdepCC_TRUE@ $(am__mv) $$depbase.Tpo $$depbase.Plo @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=yes @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LTCOMPILE) -c -o $@ $< android/audio_a2dp_default_la-hal-audio.lo: android/hal-audio.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(android_audio_a2dp_default_la_CPPFLAGS) $(CPPFLAGS) $(android_audio_a2dp_default_la_CFLAGS) $(CFLAGS) -MT android/audio_a2dp_default_la-hal-audio.lo -MD -MP -MF android/$(DEPDIR)/audio_a2dp_default_la-hal-audio.Tpo -c -o android/audio_a2dp_default_la-hal-audio.lo `test -f 'android/hal-audio.c' || echo '$(srcdir)/'`android/hal-audio.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) android/$(DEPDIR)/audio_a2dp_default_la-hal-audio.Tpo android/$(DEPDIR)/audio_a2dp_default_la-hal-audio.Plo @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='android/hal-audio.c' object='android/audio_a2dp_default_la-hal-audio.lo' libtool=yes @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(android_audio_a2dp_default_la_CPPFLAGS) $(CPPFLAGS) $(android_audio_a2dp_default_la_CFLAGS) $(CFLAGS) -c -o android/audio_a2dp_default_la-hal-audio.lo `test -f 'android/hal-audio.c' || echo '$(srcdir)/'`android/hal-audio.c android/audio_a2dp_default_la-hal-audio-sbc.lo: android/hal-audio-sbc.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(android_audio_a2dp_default_la_CPPFLAGS) $(CPPFLAGS) $(android_audio_a2dp_default_la_CFLAGS) $(CFLAGS) -MT android/audio_a2dp_default_la-hal-audio-sbc.lo -MD -MP -MF android/$(DEPDIR)/audio_a2dp_default_la-hal-audio-sbc.Tpo -c -o android/audio_a2dp_default_la-hal-audio-sbc.lo `test -f 'android/hal-audio-sbc.c' || echo '$(srcdir)/'`android/hal-audio-sbc.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) android/$(DEPDIR)/audio_a2dp_default_la-hal-audio-sbc.Tpo android/$(DEPDIR)/audio_a2dp_default_la-hal-audio-sbc.Plo @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='android/hal-audio-sbc.c' object='android/audio_a2dp_default_la-hal-audio-sbc.lo' libtool=yes @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(android_audio_a2dp_default_la_CPPFLAGS) $(CPPFLAGS) $(android_audio_a2dp_default_la_CFLAGS) $(CFLAGS) -c -o android/audio_a2dp_default_la-hal-audio-sbc.lo `test -f 'android/hal-audio-sbc.c' || echo '$(srcdir)/'`android/hal-audio-sbc.c android/audio_a2dp_default_la-hal-audio-aptx.lo: android/hal-audio-aptx.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(android_audio_a2dp_default_la_CPPFLAGS) $(CPPFLAGS) $(android_audio_a2dp_default_la_CFLAGS) $(CFLAGS) -MT android/audio_a2dp_default_la-hal-audio-aptx.lo -MD -MP -MF android/$(DEPDIR)/audio_a2dp_default_la-hal-audio-aptx.Tpo -c -o android/audio_a2dp_default_la-hal-audio-aptx.lo `test -f 'android/hal-audio-aptx.c' || echo '$(srcdir)/'`android/hal-audio-aptx.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) android/$(DEPDIR)/audio_a2dp_default_la-hal-audio-aptx.Tpo android/$(DEPDIR)/audio_a2dp_default_la-hal-audio-aptx.Plo @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='android/hal-audio-aptx.c' object='android/audio_a2dp_default_la-hal-audio-aptx.lo' libtool=yes @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(android_audio_a2dp_default_la_CPPFLAGS) $(CPPFLAGS) $(android_audio_a2dp_default_la_CFLAGS) $(CFLAGS) -c -o android/audio_a2dp_default_la-hal-audio-aptx.lo `test -f 'android/hal-audio-aptx.c' || echo '$(srcdir)/'`android/hal-audio-aptx.c android/audio_sco_default_la-hal-sco.lo: android/hal-sco.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(android_audio_sco_default_la_CPPFLAGS) $(CPPFLAGS) $(android_audio_sco_default_la_CFLAGS) $(CFLAGS) -MT android/audio_sco_default_la-hal-sco.lo -MD -MP -MF android/$(DEPDIR)/audio_sco_default_la-hal-sco.Tpo -c -o android/audio_sco_default_la-hal-sco.lo `test -f 'android/hal-sco.c' || echo '$(srcdir)/'`android/hal-sco.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) android/$(DEPDIR)/audio_sco_default_la-hal-sco.Tpo android/$(DEPDIR)/audio_sco_default_la-hal-sco.Plo @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='android/hal-sco.c' object='android/audio_sco_default_la-hal-sco.lo' libtool=yes @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(android_audio_sco_default_la_CPPFLAGS) $(CPPFLAGS) $(android_audio_sco_default_la_CFLAGS) $(CFLAGS) -c -o android/audio_sco_default_la-hal-sco.lo `test -f 'android/hal-sco.c' || echo '$(srcdir)/'`android/hal-sco.c android/audio_utils/audio_sco_default_la-resampler.lo: android/audio_utils/resampler.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(android_audio_sco_default_la_CPPFLAGS) $(CPPFLAGS) $(android_audio_sco_default_la_CFLAGS) $(CFLAGS) -MT android/audio_utils/audio_sco_default_la-resampler.lo -MD -MP -MF android/audio_utils/$(DEPDIR)/audio_sco_default_la-resampler.Tpo -c -o android/audio_utils/audio_sco_default_la-resampler.lo `test -f 'android/audio_utils/resampler.c' || echo '$(srcdir)/'`android/audio_utils/resampler.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) android/audio_utils/$(DEPDIR)/audio_sco_default_la-resampler.Tpo android/audio_utils/$(DEPDIR)/audio_sco_default_la-resampler.Plo @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='android/audio_utils/resampler.c' object='android/audio_utils/audio_sco_default_la-resampler.lo' libtool=yes @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(android_audio_sco_default_la_CPPFLAGS) $(CPPFLAGS) $(android_audio_sco_default_la_CFLAGS) $(CFLAGS) -c -o android/audio_utils/audio_sco_default_la-resampler.lo `test -f 'android/audio_utils/resampler.c' || echo '$(srcdir)/'`android/audio_utils/resampler.c android/bluetooth_default_la-hal-bluetooth.lo: android/hal-bluetooth.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(android_bluetooth_default_la_CPPFLAGS) $(CPPFLAGS) $(android_bluetooth_default_la_CFLAGS) $(CFLAGS) -MT android/bluetooth_default_la-hal-bluetooth.lo -MD -MP -MF android/$(DEPDIR)/bluetooth_default_la-hal-bluetooth.Tpo -c -o android/bluetooth_default_la-hal-bluetooth.lo `test -f 'android/hal-bluetooth.c' || echo '$(srcdir)/'`android/hal-bluetooth.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) android/$(DEPDIR)/bluetooth_default_la-hal-bluetooth.Tpo android/$(DEPDIR)/bluetooth_default_la-hal-bluetooth.Plo @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='android/hal-bluetooth.c' object='android/bluetooth_default_la-hal-bluetooth.lo' libtool=yes @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(android_bluetooth_default_la_CPPFLAGS) $(CPPFLAGS) $(android_bluetooth_default_la_CFLAGS) $(CFLAGS) -c -o android/bluetooth_default_la-hal-bluetooth.lo `test -f 'android/hal-bluetooth.c' || echo '$(srcdir)/'`android/hal-bluetooth.c android/bluetooth_default_la-hal-socket.lo: android/hal-socket.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(android_bluetooth_default_la_CPPFLAGS) $(CPPFLAGS) $(android_bluetooth_default_la_CFLAGS) $(CFLAGS) -MT android/bluetooth_default_la-hal-socket.lo -MD -MP -MF android/$(DEPDIR)/bluetooth_default_la-hal-socket.Tpo -c -o android/bluetooth_default_la-hal-socket.lo `test -f 'android/hal-socket.c' || echo '$(srcdir)/'`android/hal-socket.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) android/$(DEPDIR)/bluetooth_default_la-hal-socket.Tpo android/$(DEPDIR)/bluetooth_default_la-hal-socket.Plo @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='android/hal-socket.c' object='android/bluetooth_default_la-hal-socket.lo' libtool=yes @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(android_bluetooth_default_la_CPPFLAGS) $(CPPFLAGS) $(android_bluetooth_default_la_CFLAGS) $(CFLAGS) -c -o android/bluetooth_default_la-hal-socket.lo `test -f 'android/hal-socket.c' || echo '$(srcdir)/'`android/hal-socket.c android/bluetooth_default_la-hal-hidhost.lo: android/hal-hidhost.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(android_bluetooth_default_la_CPPFLAGS) $(CPPFLAGS) $(android_bluetooth_default_la_CFLAGS) $(CFLAGS) -MT android/bluetooth_default_la-hal-hidhost.lo -MD -MP -MF android/$(DEPDIR)/bluetooth_default_la-hal-hidhost.Tpo -c -o android/bluetooth_default_la-hal-hidhost.lo `test -f 'android/hal-hidhost.c' || echo '$(srcdir)/'`android/hal-hidhost.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) android/$(DEPDIR)/bluetooth_default_la-hal-hidhost.Tpo android/$(DEPDIR)/bluetooth_default_la-hal-hidhost.Plo @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='android/hal-hidhost.c' object='android/bluetooth_default_la-hal-hidhost.lo' libtool=yes @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(android_bluetooth_default_la_CPPFLAGS) $(CPPFLAGS) $(android_bluetooth_default_la_CFLAGS) $(CFLAGS) -c -o android/bluetooth_default_la-hal-hidhost.lo `test -f 'android/hal-hidhost.c' || echo '$(srcdir)/'`android/hal-hidhost.c android/bluetooth_default_la-hal-health.lo: android/hal-health.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(android_bluetooth_default_la_CPPFLAGS) $(CPPFLAGS) $(android_bluetooth_default_la_CFLAGS) $(CFLAGS) -MT android/bluetooth_default_la-hal-health.lo -MD -MP -MF android/$(DEPDIR)/bluetooth_default_la-hal-health.Tpo -c -o android/bluetooth_default_la-hal-health.lo `test -f 'android/hal-health.c' || echo '$(srcdir)/'`android/hal-health.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) android/$(DEPDIR)/bluetooth_default_la-hal-health.Tpo android/$(DEPDIR)/bluetooth_default_la-hal-health.Plo @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='android/hal-health.c' object='android/bluetooth_default_la-hal-health.lo' libtool=yes @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(android_bluetooth_default_la_CPPFLAGS) $(CPPFLAGS) $(android_bluetooth_default_la_CFLAGS) $(CFLAGS) -c -o android/bluetooth_default_la-hal-health.lo `test -f 'android/hal-health.c' || echo '$(srcdir)/'`android/hal-health.c android/bluetooth_default_la-hal-pan.lo: android/hal-pan.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(android_bluetooth_default_la_CPPFLAGS) $(CPPFLAGS) $(android_bluetooth_default_la_CFLAGS) $(CFLAGS) -MT android/bluetooth_default_la-hal-pan.lo -MD -MP -MF android/$(DEPDIR)/bluetooth_default_la-hal-pan.Tpo -c -o android/bluetooth_default_la-hal-pan.lo `test -f 'android/hal-pan.c' || echo '$(srcdir)/'`android/hal-pan.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) android/$(DEPDIR)/bluetooth_default_la-hal-pan.Tpo android/$(DEPDIR)/bluetooth_default_la-hal-pan.Plo @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='android/hal-pan.c' object='android/bluetooth_default_la-hal-pan.lo' libtool=yes @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(android_bluetooth_default_la_CPPFLAGS) $(CPPFLAGS) $(android_bluetooth_default_la_CFLAGS) $(CFLAGS) -c -o android/bluetooth_default_la-hal-pan.lo `test -f 'android/hal-pan.c' || echo '$(srcdir)/'`android/hal-pan.c android/bluetooth_default_la-hal-a2dp.lo: android/hal-a2dp.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(android_bluetooth_default_la_CPPFLAGS) $(CPPFLAGS) $(android_bluetooth_default_la_CFLAGS) $(CFLAGS) -MT android/bluetooth_default_la-hal-a2dp.lo -MD -MP -MF android/$(DEPDIR)/bluetooth_default_la-hal-a2dp.Tpo -c -o android/bluetooth_default_la-hal-a2dp.lo `test -f 'android/hal-a2dp.c' || echo '$(srcdir)/'`android/hal-a2dp.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) android/$(DEPDIR)/bluetooth_default_la-hal-a2dp.Tpo android/$(DEPDIR)/bluetooth_default_la-hal-a2dp.Plo @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='android/hal-a2dp.c' object='android/bluetooth_default_la-hal-a2dp.lo' libtool=yes @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(android_bluetooth_default_la_CPPFLAGS) $(CPPFLAGS) $(android_bluetooth_default_la_CFLAGS) $(CFLAGS) -c -o android/bluetooth_default_la-hal-a2dp.lo `test -f 'android/hal-a2dp.c' || echo '$(srcdir)/'`android/hal-a2dp.c android/bluetooth_default_la-hal-a2dp-sink.lo: android/hal-a2dp-sink.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(android_bluetooth_default_la_CPPFLAGS) $(CPPFLAGS) $(android_bluetooth_default_la_CFLAGS) $(CFLAGS) -MT android/bluetooth_default_la-hal-a2dp-sink.lo -MD -MP -MF android/$(DEPDIR)/bluetooth_default_la-hal-a2dp-sink.Tpo -c -o android/bluetooth_default_la-hal-a2dp-sink.lo `test -f 'android/hal-a2dp-sink.c' || echo '$(srcdir)/'`android/hal-a2dp-sink.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) android/$(DEPDIR)/bluetooth_default_la-hal-a2dp-sink.Tpo android/$(DEPDIR)/bluetooth_default_la-hal-a2dp-sink.Plo @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='android/hal-a2dp-sink.c' object='android/bluetooth_default_la-hal-a2dp-sink.lo' libtool=yes @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(android_bluetooth_default_la_CPPFLAGS) $(CPPFLAGS) $(android_bluetooth_default_la_CFLAGS) $(CFLAGS) -c -o android/bluetooth_default_la-hal-a2dp-sink.lo `test -f 'android/hal-a2dp-sink.c' || echo '$(srcdir)/'`android/hal-a2dp-sink.c android/bluetooth_default_la-hal-avrcp.lo: android/hal-avrcp.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(android_bluetooth_default_la_CPPFLAGS) $(CPPFLAGS) $(android_bluetooth_default_la_CFLAGS) $(CFLAGS) -MT android/bluetooth_default_la-hal-avrcp.lo -MD -MP -MF android/$(DEPDIR)/bluetooth_default_la-hal-avrcp.Tpo -c -o android/bluetooth_default_la-hal-avrcp.lo `test -f 'android/hal-avrcp.c' || echo '$(srcdir)/'`android/hal-avrcp.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) android/$(DEPDIR)/bluetooth_default_la-hal-avrcp.Tpo android/$(DEPDIR)/bluetooth_default_la-hal-avrcp.Plo @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='android/hal-avrcp.c' object='android/bluetooth_default_la-hal-avrcp.lo' libtool=yes @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(android_bluetooth_default_la_CPPFLAGS) $(CPPFLAGS) $(android_bluetooth_default_la_CFLAGS) $(CFLAGS) -c -o android/bluetooth_default_la-hal-avrcp.lo `test -f 'android/hal-avrcp.c' || echo '$(srcdir)/'`android/hal-avrcp.c android/bluetooth_default_la-hal-avrcp-ctrl.lo: android/hal-avrcp-ctrl.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(android_bluetooth_default_la_CPPFLAGS) $(CPPFLAGS) $(android_bluetooth_default_la_CFLAGS) $(CFLAGS) -MT android/bluetooth_default_la-hal-avrcp-ctrl.lo -MD -MP -MF android/$(DEPDIR)/bluetooth_default_la-hal-avrcp-ctrl.Tpo -c -o android/bluetooth_default_la-hal-avrcp-ctrl.lo `test -f 'android/hal-avrcp-ctrl.c' || echo '$(srcdir)/'`android/hal-avrcp-ctrl.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) android/$(DEPDIR)/bluetooth_default_la-hal-avrcp-ctrl.Tpo android/$(DEPDIR)/bluetooth_default_la-hal-avrcp-ctrl.Plo @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='android/hal-avrcp-ctrl.c' object='android/bluetooth_default_la-hal-avrcp-ctrl.lo' libtool=yes @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(android_bluetooth_default_la_CPPFLAGS) $(CPPFLAGS) $(android_bluetooth_default_la_CFLAGS) $(CFLAGS) -c -o android/bluetooth_default_la-hal-avrcp-ctrl.lo `test -f 'android/hal-avrcp-ctrl.c' || echo '$(srcdir)/'`android/hal-avrcp-ctrl.c android/bluetooth_default_la-hal-handsfree.lo: android/hal-handsfree.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(android_bluetooth_default_la_CPPFLAGS) $(CPPFLAGS) $(android_bluetooth_default_la_CFLAGS) $(CFLAGS) -MT android/bluetooth_default_la-hal-handsfree.lo -MD -MP -MF android/$(DEPDIR)/bluetooth_default_la-hal-handsfree.Tpo -c -o android/bluetooth_default_la-hal-handsfree.lo `test -f 'android/hal-handsfree.c' || echo '$(srcdir)/'`android/hal-handsfree.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) android/$(DEPDIR)/bluetooth_default_la-hal-handsfree.Tpo android/$(DEPDIR)/bluetooth_default_la-hal-handsfree.Plo @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='android/hal-handsfree.c' object='android/bluetooth_default_la-hal-handsfree.lo' libtool=yes @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(android_bluetooth_default_la_CPPFLAGS) $(CPPFLAGS) $(android_bluetooth_default_la_CFLAGS) $(CFLAGS) -c -o android/bluetooth_default_la-hal-handsfree.lo `test -f 'android/hal-handsfree.c' || echo '$(srcdir)/'`android/hal-handsfree.c android/bluetooth_default_la-hal-handsfree-client.lo: android/hal-handsfree-client.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(android_bluetooth_default_la_CPPFLAGS) $(CPPFLAGS) $(android_bluetooth_default_la_CFLAGS) $(CFLAGS) -MT android/bluetooth_default_la-hal-handsfree-client.lo -MD -MP -MF android/$(DEPDIR)/bluetooth_default_la-hal-handsfree-client.Tpo -c -o android/bluetooth_default_la-hal-handsfree-client.lo `test -f 'android/hal-handsfree-client.c' || echo '$(srcdir)/'`android/hal-handsfree-client.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) android/$(DEPDIR)/bluetooth_default_la-hal-handsfree-client.Tpo android/$(DEPDIR)/bluetooth_default_la-hal-handsfree-client.Plo @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='android/hal-handsfree-client.c' object='android/bluetooth_default_la-hal-handsfree-client.lo' libtool=yes @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(android_bluetooth_default_la_CPPFLAGS) $(CPPFLAGS) $(android_bluetooth_default_la_CFLAGS) $(CFLAGS) -c -o android/bluetooth_default_la-hal-handsfree-client.lo `test -f 'android/hal-handsfree-client.c' || echo '$(srcdir)/'`android/hal-handsfree-client.c android/bluetooth_default_la-hal-gatt.lo: android/hal-gatt.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(android_bluetooth_default_la_CPPFLAGS) $(CPPFLAGS) $(android_bluetooth_default_la_CFLAGS) $(CFLAGS) -MT android/bluetooth_default_la-hal-gatt.lo -MD -MP -MF android/$(DEPDIR)/bluetooth_default_la-hal-gatt.Tpo -c -o android/bluetooth_default_la-hal-gatt.lo `test -f 'android/hal-gatt.c' || echo '$(srcdir)/'`android/hal-gatt.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) android/$(DEPDIR)/bluetooth_default_la-hal-gatt.Tpo android/$(DEPDIR)/bluetooth_default_la-hal-gatt.Plo @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='android/hal-gatt.c' object='android/bluetooth_default_la-hal-gatt.lo' libtool=yes @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(android_bluetooth_default_la_CPPFLAGS) $(CPPFLAGS) $(android_bluetooth_default_la_CFLAGS) $(CFLAGS) -c -o android/bluetooth_default_la-hal-gatt.lo `test -f 'android/hal-gatt.c' || echo '$(srcdir)/'`android/hal-gatt.c android/bluetooth_default_la-hal-map-client.lo: android/hal-map-client.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(android_bluetooth_default_la_CPPFLAGS) $(CPPFLAGS) $(android_bluetooth_default_la_CFLAGS) $(CFLAGS) -MT android/bluetooth_default_la-hal-map-client.lo -MD -MP -MF android/$(DEPDIR)/bluetooth_default_la-hal-map-client.Tpo -c -o android/bluetooth_default_la-hal-map-client.lo `test -f 'android/hal-map-client.c' || echo '$(srcdir)/'`android/hal-map-client.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) android/$(DEPDIR)/bluetooth_default_la-hal-map-client.Tpo android/$(DEPDIR)/bluetooth_default_la-hal-map-client.Plo @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='android/hal-map-client.c' object='android/bluetooth_default_la-hal-map-client.lo' libtool=yes @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(android_bluetooth_default_la_CPPFLAGS) $(CPPFLAGS) $(android_bluetooth_default_la_CFLAGS) $(CFLAGS) -c -o android/bluetooth_default_la-hal-map-client.lo `test -f 'android/hal-map-client.c' || echo '$(srcdir)/'`android/hal-map-client.c android/bluetooth_default_la-hal-ipc.lo: android/hal-ipc.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(android_bluetooth_default_la_CPPFLAGS) $(CPPFLAGS) $(android_bluetooth_default_la_CFLAGS) $(CFLAGS) -MT android/bluetooth_default_la-hal-ipc.lo -MD -MP -MF android/$(DEPDIR)/bluetooth_default_la-hal-ipc.Tpo -c -o android/bluetooth_default_la-hal-ipc.lo `test -f 'android/hal-ipc.c' || echo '$(srcdir)/'`android/hal-ipc.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) android/$(DEPDIR)/bluetooth_default_la-hal-ipc.Tpo android/$(DEPDIR)/bluetooth_default_la-hal-ipc.Plo @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='android/hal-ipc.c' object='android/bluetooth_default_la-hal-ipc.lo' libtool=yes @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(android_bluetooth_default_la_CPPFLAGS) $(CPPFLAGS) $(android_bluetooth_default_la_CFLAGS) $(CFLAGS) -c -o android/bluetooth_default_la-hal-ipc.lo `test -f 'android/hal-ipc.c' || echo '$(srcdir)/'`android/hal-ipc.c android/bluetooth_default_la-hal-utils.lo: android/hal-utils.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(android_bluetooth_default_la_CPPFLAGS) $(CPPFLAGS) $(android_bluetooth_default_la_CFLAGS) $(CFLAGS) -MT android/bluetooth_default_la-hal-utils.lo -MD -MP -MF android/$(DEPDIR)/bluetooth_default_la-hal-utils.Tpo -c -o android/bluetooth_default_la-hal-utils.lo `test -f 'android/hal-utils.c' || echo '$(srcdir)/'`android/hal-utils.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) android/$(DEPDIR)/bluetooth_default_la-hal-utils.Tpo android/$(DEPDIR)/bluetooth_default_la-hal-utils.Plo @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='android/hal-utils.c' object='android/bluetooth_default_la-hal-utils.lo' libtool=yes @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(android_bluetooth_default_la_CPPFLAGS) $(CPPFLAGS) $(android_bluetooth_default_la_CFLAGS) $(CFLAGS) -c -o android/bluetooth_default_la-hal-utils.lo `test -f 'android/hal-utils.c' || echo '$(srcdir)/'`android/hal-utils.c src/shared/libshared_ell_la-queue.lo: src/shared/queue.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_libshared_ell_la_CFLAGS) $(CFLAGS) -MT src/shared/libshared_ell_la-queue.lo -MD -MP -MF src/shared/$(DEPDIR)/libshared_ell_la-queue.Tpo -c -o src/shared/libshared_ell_la-queue.lo `test -f 'src/shared/queue.c' || echo '$(srcdir)/'`src/shared/queue.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) src/shared/$(DEPDIR)/libshared_ell_la-queue.Tpo src/shared/$(DEPDIR)/libshared_ell_la-queue.Plo @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='src/shared/queue.c' object='src/shared/libshared_ell_la-queue.lo' libtool=yes @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_libshared_ell_la_CFLAGS) $(CFLAGS) -c -o src/shared/libshared_ell_la-queue.lo `test -f 'src/shared/queue.c' || echo '$(srcdir)/'`src/shared/queue.c src/shared/libshared_ell_la-util.lo: src/shared/util.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_libshared_ell_la_CFLAGS) $(CFLAGS) -MT src/shared/libshared_ell_la-util.lo -MD -MP -MF src/shared/$(DEPDIR)/libshared_ell_la-util.Tpo -c -o src/shared/libshared_ell_la-util.lo `test -f 'src/shared/util.c' || echo '$(srcdir)/'`src/shared/util.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) src/shared/$(DEPDIR)/libshared_ell_la-util.Tpo src/shared/$(DEPDIR)/libshared_ell_la-util.Plo @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='src/shared/util.c' object='src/shared/libshared_ell_la-util.lo' libtool=yes @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_libshared_ell_la_CFLAGS) $(CFLAGS) -c -o src/shared/libshared_ell_la-util.lo `test -f 'src/shared/util.c' || echo '$(srcdir)/'`src/shared/util.c src/shared/libshared_ell_la-mgmt.lo: src/shared/mgmt.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_libshared_ell_la_CFLAGS) $(CFLAGS) -MT src/shared/libshared_ell_la-mgmt.lo -MD -MP -MF src/shared/$(DEPDIR)/libshared_ell_la-mgmt.Tpo -c -o src/shared/libshared_ell_la-mgmt.lo `test -f 'src/shared/mgmt.c' || echo '$(srcdir)/'`src/shared/mgmt.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) src/shared/$(DEPDIR)/libshared_ell_la-mgmt.Tpo src/shared/$(DEPDIR)/libshared_ell_la-mgmt.Plo @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='src/shared/mgmt.c' object='src/shared/libshared_ell_la-mgmt.lo' libtool=yes @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_libshared_ell_la_CFLAGS) $(CFLAGS) -c -o src/shared/libshared_ell_la-mgmt.lo `test -f 'src/shared/mgmt.c' || echo '$(srcdir)/'`src/shared/mgmt.c src/shared/libshared_ell_la-crypto.lo: src/shared/crypto.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_libshared_ell_la_CFLAGS) $(CFLAGS) -MT src/shared/libshared_ell_la-crypto.lo -MD -MP -MF src/shared/$(DEPDIR)/libshared_ell_la-crypto.Tpo -c -o src/shared/libshared_ell_la-crypto.lo `test -f 'src/shared/crypto.c' || echo '$(srcdir)/'`src/shared/crypto.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) src/shared/$(DEPDIR)/libshared_ell_la-crypto.Tpo src/shared/$(DEPDIR)/libshared_ell_la-crypto.Plo @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='src/shared/crypto.c' object='src/shared/libshared_ell_la-crypto.lo' libtool=yes @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_libshared_ell_la_CFLAGS) $(CFLAGS) -c -o src/shared/libshared_ell_la-crypto.lo `test -f 'src/shared/crypto.c' || echo '$(srcdir)/'`src/shared/crypto.c src/shared/libshared_ell_la-ecc.lo: src/shared/ecc.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_libshared_ell_la_CFLAGS) $(CFLAGS) -MT src/shared/libshared_ell_la-ecc.lo -MD -MP -MF src/shared/$(DEPDIR)/libshared_ell_la-ecc.Tpo -c -o src/shared/libshared_ell_la-ecc.lo `test -f 'src/shared/ecc.c' || echo '$(srcdir)/'`src/shared/ecc.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) src/shared/$(DEPDIR)/libshared_ell_la-ecc.Tpo src/shared/$(DEPDIR)/libshared_ell_la-ecc.Plo @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='src/shared/ecc.c' object='src/shared/libshared_ell_la-ecc.lo' libtool=yes @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_libshared_ell_la_CFLAGS) $(CFLAGS) -c -o src/shared/libshared_ell_la-ecc.lo `test -f 'src/shared/ecc.c' || echo '$(srcdir)/'`src/shared/ecc.c src/shared/libshared_ell_la-ringbuf.lo: src/shared/ringbuf.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_libshared_ell_la_CFLAGS) $(CFLAGS) -MT src/shared/libshared_ell_la-ringbuf.lo -MD -MP -MF src/shared/$(DEPDIR)/libshared_ell_la-ringbuf.Tpo -c -o src/shared/libshared_ell_la-ringbuf.lo `test -f 'src/shared/ringbuf.c' || echo '$(srcdir)/'`src/shared/ringbuf.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) src/shared/$(DEPDIR)/libshared_ell_la-ringbuf.Tpo src/shared/$(DEPDIR)/libshared_ell_la-ringbuf.Plo @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='src/shared/ringbuf.c' object='src/shared/libshared_ell_la-ringbuf.lo' libtool=yes @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_libshared_ell_la_CFLAGS) $(CFLAGS) -c -o src/shared/libshared_ell_la-ringbuf.lo `test -f 'src/shared/ringbuf.c' || echo '$(srcdir)/'`src/shared/ringbuf.c src/shared/libshared_ell_la-hci.lo: src/shared/hci.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_libshared_ell_la_CFLAGS) $(CFLAGS) -MT src/shared/libshared_ell_la-hci.lo -MD -MP -MF src/shared/$(DEPDIR)/libshared_ell_la-hci.Tpo -c -o src/shared/libshared_ell_la-hci.lo `test -f 'src/shared/hci.c' || echo '$(srcdir)/'`src/shared/hci.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) src/shared/$(DEPDIR)/libshared_ell_la-hci.Tpo src/shared/$(DEPDIR)/libshared_ell_la-hci.Plo @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='src/shared/hci.c' object='src/shared/libshared_ell_la-hci.lo' libtool=yes @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_libshared_ell_la_CFLAGS) $(CFLAGS) -c -o src/shared/libshared_ell_la-hci.lo `test -f 'src/shared/hci.c' || echo '$(srcdir)/'`src/shared/hci.c src/shared/libshared_ell_la-hci-crypto.lo: src/shared/hci-crypto.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_libshared_ell_la_CFLAGS) $(CFLAGS) -MT src/shared/libshared_ell_la-hci-crypto.lo -MD -MP -MF src/shared/$(DEPDIR)/libshared_ell_la-hci-crypto.Tpo -c -o src/shared/libshared_ell_la-hci-crypto.lo `test -f 'src/shared/hci-crypto.c' || echo '$(srcdir)/'`src/shared/hci-crypto.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) src/shared/$(DEPDIR)/libshared_ell_la-hci-crypto.Tpo src/shared/$(DEPDIR)/libshared_ell_la-hci-crypto.Plo @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='src/shared/hci-crypto.c' object='src/shared/libshared_ell_la-hci-crypto.lo' libtool=yes @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_libshared_ell_la_CFLAGS) $(CFLAGS) -c -o src/shared/libshared_ell_la-hci-crypto.lo `test -f 'src/shared/hci-crypto.c' || echo '$(srcdir)/'`src/shared/hci-crypto.c src/shared/libshared_ell_la-hfp.lo: src/shared/hfp.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_libshared_ell_la_CFLAGS) $(CFLAGS) -MT src/shared/libshared_ell_la-hfp.lo -MD -MP -MF src/shared/$(DEPDIR)/libshared_ell_la-hfp.Tpo -c -o src/shared/libshared_ell_la-hfp.lo `test -f 'src/shared/hfp.c' || echo '$(srcdir)/'`src/shared/hfp.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) src/shared/$(DEPDIR)/libshared_ell_la-hfp.Tpo src/shared/$(DEPDIR)/libshared_ell_la-hfp.Plo @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='src/shared/hfp.c' object='src/shared/libshared_ell_la-hfp.lo' libtool=yes @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_libshared_ell_la_CFLAGS) $(CFLAGS) -c -o src/shared/libshared_ell_la-hfp.lo `test -f 'src/shared/hfp.c' || echo '$(srcdir)/'`src/shared/hfp.c src/shared/libshared_ell_la-uhid.lo: src/shared/uhid.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_libshared_ell_la_CFLAGS) $(CFLAGS) -MT src/shared/libshared_ell_la-uhid.lo -MD -MP -MF src/shared/$(DEPDIR)/libshared_ell_la-uhid.Tpo -c -o src/shared/libshared_ell_la-uhid.lo `test -f 'src/shared/uhid.c' || echo '$(srcdir)/'`src/shared/uhid.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) src/shared/$(DEPDIR)/libshared_ell_la-uhid.Tpo src/shared/$(DEPDIR)/libshared_ell_la-uhid.Plo @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='src/shared/uhid.c' object='src/shared/libshared_ell_la-uhid.lo' libtool=yes @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_libshared_ell_la_CFLAGS) $(CFLAGS) -c -o src/shared/libshared_ell_la-uhid.lo `test -f 'src/shared/uhid.c' || echo '$(srcdir)/'`src/shared/uhid.c src/shared/libshared_ell_la-pcap.lo: src/shared/pcap.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_libshared_ell_la_CFLAGS) $(CFLAGS) -MT src/shared/libshared_ell_la-pcap.lo -MD -MP -MF src/shared/$(DEPDIR)/libshared_ell_la-pcap.Tpo -c -o src/shared/libshared_ell_la-pcap.lo `test -f 'src/shared/pcap.c' || echo '$(srcdir)/'`src/shared/pcap.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) src/shared/$(DEPDIR)/libshared_ell_la-pcap.Tpo src/shared/$(DEPDIR)/libshared_ell_la-pcap.Plo @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='src/shared/pcap.c' object='src/shared/libshared_ell_la-pcap.lo' libtool=yes @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_libshared_ell_la_CFLAGS) $(CFLAGS) -c -o src/shared/libshared_ell_la-pcap.lo `test -f 'src/shared/pcap.c' || echo '$(srcdir)/'`src/shared/pcap.c src/shared/libshared_ell_la-btsnoop.lo: src/shared/btsnoop.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_libshared_ell_la_CFLAGS) $(CFLAGS) -MT src/shared/libshared_ell_la-btsnoop.lo -MD -MP -MF src/shared/$(DEPDIR)/libshared_ell_la-btsnoop.Tpo -c -o src/shared/libshared_ell_la-btsnoop.lo `test -f 'src/shared/btsnoop.c' || echo '$(srcdir)/'`src/shared/btsnoop.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) src/shared/$(DEPDIR)/libshared_ell_la-btsnoop.Tpo src/shared/$(DEPDIR)/libshared_ell_la-btsnoop.Plo @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='src/shared/btsnoop.c' object='src/shared/libshared_ell_la-btsnoop.lo' libtool=yes @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_libshared_ell_la_CFLAGS) $(CFLAGS) -c -o src/shared/libshared_ell_la-btsnoop.lo `test -f 'src/shared/btsnoop.c' || echo '$(srcdir)/'`src/shared/btsnoop.c src/shared/libshared_ell_la-ad.lo: src/shared/ad.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_libshared_ell_la_CFLAGS) $(CFLAGS) -MT src/shared/libshared_ell_la-ad.lo -MD -MP -MF src/shared/$(DEPDIR)/libshared_ell_la-ad.Tpo -c -o src/shared/libshared_ell_la-ad.lo `test -f 'src/shared/ad.c' || echo '$(srcdir)/'`src/shared/ad.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) src/shared/$(DEPDIR)/libshared_ell_la-ad.Tpo src/shared/$(DEPDIR)/libshared_ell_la-ad.Plo @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='src/shared/ad.c' object='src/shared/libshared_ell_la-ad.lo' libtool=yes @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_libshared_ell_la_CFLAGS) $(CFLAGS) -c -o src/shared/libshared_ell_la-ad.lo `test -f 'src/shared/ad.c' || echo '$(srcdir)/'`src/shared/ad.c src/shared/libshared_ell_la-att.lo: src/shared/att.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_libshared_ell_la_CFLAGS) $(CFLAGS) -MT src/shared/libshared_ell_la-att.lo -MD -MP -MF src/shared/$(DEPDIR)/libshared_ell_la-att.Tpo -c -o src/shared/libshared_ell_la-att.lo `test -f 'src/shared/att.c' || echo '$(srcdir)/'`src/shared/att.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) src/shared/$(DEPDIR)/libshared_ell_la-att.Tpo src/shared/$(DEPDIR)/libshared_ell_la-att.Plo @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='src/shared/att.c' object='src/shared/libshared_ell_la-att.lo' libtool=yes @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_libshared_ell_la_CFLAGS) $(CFLAGS) -c -o src/shared/libshared_ell_la-att.lo `test -f 'src/shared/att.c' || echo '$(srcdir)/'`src/shared/att.c src/shared/libshared_ell_la-gatt-helpers.lo: src/shared/gatt-helpers.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_libshared_ell_la_CFLAGS) $(CFLAGS) -MT src/shared/libshared_ell_la-gatt-helpers.lo -MD -MP -MF src/shared/$(DEPDIR)/libshared_ell_la-gatt-helpers.Tpo -c -o src/shared/libshared_ell_la-gatt-helpers.lo `test -f 'src/shared/gatt-helpers.c' || echo '$(srcdir)/'`src/shared/gatt-helpers.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) src/shared/$(DEPDIR)/libshared_ell_la-gatt-helpers.Tpo src/shared/$(DEPDIR)/libshared_ell_la-gatt-helpers.Plo @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='src/shared/gatt-helpers.c' object='src/shared/libshared_ell_la-gatt-helpers.lo' libtool=yes @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_libshared_ell_la_CFLAGS) $(CFLAGS) -c -o src/shared/libshared_ell_la-gatt-helpers.lo `test -f 'src/shared/gatt-helpers.c' || echo '$(srcdir)/'`src/shared/gatt-helpers.c src/shared/libshared_ell_la-gatt-client.lo: src/shared/gatt-client.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_libshared_ell_la_CFLAGS) $(CFLAGS) -MT src/shared/libshared_ell_la-gatt-client.lo -MD -MP -MF src/shared/$(DEPDIR)/libshared_ell_la-gatt-client.Tpo -c -o src/shared/libshared_ell_la-gatt-client.lo `test -f 'src/shared/gatt-client.c' || echo '$(srcdir)/'`src/shared/gatt-client.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) src/shared/$(DEPDIR)/libshared_ell_la-gatt-client.Tpo src/shared/$(DEPDIR)/libshared_ell_la-gatt-client.Plo @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='src/shared/gatt-client.c' object='src/shared/libshared_ell_la-gatt-client.lo' libtool=yes @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_libshared_ell_la_CFLAGS) $(CFLAGS) -c -o src/shared/libshared_ell_la-gatt-client.lo `test -f 'src/shared/gatt-client.c' || echo '$(srcdir)/'`src/shared/gatt-client.c src/shared/libshared_ell_la-gatt-server.lo: src/shared/gatt-server.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_libshared_ell_la_CFLAGS) $(CFLAGS) -MT src/shared/libshared_ell_la-gatt-server.lo -MD -MP -MF src/shared/$(DEPDIR)/libshared_ell_la-gatt-server.Tpo -c -o src/shared/libshared_ell_la-gatt-server.lo `test -f 'src/shared/gatt-server.c' || echo '$(srcdir)/'`src/shared/gatt-server.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) src/shared/$(DEPDIR)/libshared_ell_la-gatt-server.Tpo src/shared/$(DEPDIR)/libshared_ell_la-gatt-server.Plo @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='src/shared/gatt-server.c' object='src/shared/libshared_ell_la-gatt-server.lo' libtool=yes @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_libshared_ell_la_CFLAGS) $(CFLAGS) -c -o src/shared/libshared_ell_la-gatt-server.lo `test -f 'src/shared/gatt-server.c' || echo '$(srcdir)/'`src/shared/gatt-server.c src/shared/libshared_ell_la-gatt-db.lo: src/shared/gatt-db.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_libshared_ell_la_CFLAGS) $(CFLAGS) -MT src/shared/libshared_ell_la-gatt-db.lo -MD -MP -MF src/shared/$(DEPDIR)/libshared_ell_la-gatt-db.Tpo -c -o src/shared/libshared_ell_la-gatt-db.lo `test -f 'src/shared/gatt-db.c' || echo '$(srcdir)/'`src/shared/gatt-db.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) src/shared/$(DEPDIR)/libshared_ell_la-gatt-db.Tpo src/shared/$(DEPDIR)/libshared_ell_la-gatt-db.Plo @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='src/shared/gatt-db.c' object='src/shared/libshared_ell_la-gatt-db.lo' libtool=yes @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_libshared_ell_la_CFLAGS) $(CFLAGS) -c -o src/shared/libshared_ell_la-gatt-db.lo `test -f 'src/shared/gatt-db.c' || echo '$(srcdir)/'`src/shared/gatt-db.c src/shared/libshared_ell_la-gap.lo: src/shared/gap.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_libshared_ell_la_CFLAGS) $(CFLAGS) -MT src/shared/libshared_ell_la-gap.lo -MD -MP -MF src/shared/$(DEPDIR)/libshared_ell_la-gap.Tpo -c -o src/shared/libshared_ell_la-gap.lo `test -f 'src/shared/gap.c' || echo '$(srcdir)/'`src/shared/gap.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) src/shared/$(DEPDIR)/libshared_ell_la-gap.Tpo src/shared/$(DEPDIR)/libshared_ell_la-gap.Plo @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='src/shared/gap.c' object='src/shared/libshared_ell_la-gap.lo' libtool=yes @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_libshared_ell_la_CFLAGS) $(CFLAGS) -c -o src/shared/libshared_ell_la-gap.lo `test -f 'src/shared/gap.c' || echo '$(srcdir)/'`src/shared/gap.c src/shared/libshared_ell_la-log.lo: src/shared/log.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_libshared_ell_la_CFLAGS) $(CFLAGS) -MT src/shared/libshared_ell_la-log.lo -MD -MP -MF src/shared/$(DEPDIR)/libshared_ell_la-log.Tpo -c -o src/shared/libshared_ell_la-log.lo `test -f 'src/shared/log.c' || echo '$(srcdir)/'`src/shared/log.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) src/shared/$(DEPDIR)/libshared_ell_la-log.Tpo src/shared/$(DEPDIR)/libshared_ell_la-log.Plo @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='src/shared/log.c' object='src/shared/libshared_ell_la-log.lo' libtool=yes @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_libshared_ell_la_CFLAGS) $(CFLAGS) -c -o src/shared/libshared_ell_la-log.lo `test -f 'src/shared/log.c' || echo '$(srcdir)/'`src/shared/log.c src/shared/libshared_ell_la-bap.lo: src/shared/bap.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_libshared_ell_la_CFLAGS) $(CFLAGS) -MT src/shared/libshared_ell_la-bap.lo -MD -MP -MF src/shared/$(DEPDIR)/libshared_ell_la-bap.Tpo -c -o src/shared/libshared_ell_la-bap.lo `test -f 'src/shared/bap.c' || echo '$(srcdir)/'`src/shared/bap.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) src/shared/$(DEPDIR)/libshared_ell_la-bap.Tpo src/shared/$(DEPDIR)/libshared_ell_la-bap.Plo @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='src/shared/bap.c' object='src/shared/libshared_ell_la-bap.lo' libtool=yes @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_libshared_ell_la_CFLAGS) $(CFLAGS) -c -o src/shared/libshared_ell_la-bap.lo `test -f 'src/shared/bap.c' || echo '$(srcdir)/'`src/shared/bap.c src/shared/libshared_ell_la-bap-debug.lo: src/shared/bap-debug.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_libshared_ell_la_CFLAGS) $(CFLAGS) -MT src/shared/libshared_ell_la-bap-debug.lo -MD -MP -MF src/shared/$(DEPDIR)/libshared_ell_la-bap-debug.Tpo -c -o src/shared/libshared_ell_la-bap-debug.lo `test -f 'src/shared/bap-debug.c' || echo '$(srcdir)/'`src/shared/bap-debug.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) src/shared/$(DEPDIR)/libshared_ell_la-bap-debug.Tpo src/shared/$(DEPDIR)/libshared_ell_la-bap-debug.Plo @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='src/shared/bap-debug.c' object='src/shared/libshared_ell_la-bap-debug.lo' libtool=yes @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_libshared_ell_la_CFLAGS) $(CFLAGS) -c -o src/shared/libshared_ell_la-bap-debug.lo `test -f 'src/shared/bap-debug.c' || echo '$(srcdir)/'`src/shared/bap-debug.c src/shared/libshared_ell_la-mcp.lo: src/shared/mcp.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_libshared_ell_la_CFLAGS) $(CFLAGS) -MT src/shared/libshared_ell_la-mcp.lo -MD -MP -MF src/shared/$(DEPDIR)/libshared_ell_la-mcp.Tpo -c -o src/shared/libshared_ell_la-mcp.lo `test -f 'src/shared/mcp.c' || echo '$(srcdir)/'`src/shared/mcp.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) src/shared/$(DEPDIR)/libshared_ell_la-mcp.Tpo src/shared/$(DEPDIR)/libshared_ell_la-mcp.Plo @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='src/shared/mcp.c' object='src/shared/libshared_ell_la-mcp.lo' libtool=yes @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_libshared_ell_la_CFLAGS) $(CFLAGS) -c -o src/shared/libshared_ell_la-mcp.lo `test -f 'src/shared/mcp.c' || echo '$(srcdir)/'`src/shared/mcp.c src/shared/libshared_ell_la-vcp.lo: src/shared/vcp.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_libshared_ell_la_CFLAGS) $(CFLAGS) -MT src/shared/libshared_ell_la-vcp.lo -MD -MP -MF src/shared/$(DEPDIR)/libshared_ell_la-vcp.Tpo -c -o src/shared/libshared_ell_la-vcp.lo `test -f 'src/shared/vcp.c' || echo '$(srcdir)/'`src/shared/vcp.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) src/shared/$(DEPDIR)/libshared_ell_la-vcp.Tpo src/shared/$(DEPDIR)/libshared_ell_la-vcp.Plo @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='src/shared/vcp.c' object='src/shared/libshared_ell_la-vcp.lo' libtool=yes @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_libshared_ell_la_CFLAGS) $(CFLAGS) -c -o src/shared/libshared_ell_la-vcp.lo `test -f 'src/shared/vcp.c' || echo '$(srcdir)/'`src/shared/vcp.c src/shared/libshared_ell_la-micp.lo: src/shared/micp.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_libshared_ell_la_CFLAGS) $(CFLAGS) -MT src/shared/libshared_ell_la-micp.lo -MD -MP -MF src/shared/$(DEPDIR)/libshared_ell_la-micp.Tpo -c -o src/shared/libshared_ell_la-micp.lo `test -f 'src/shared/micp.c' || echo '$(srcdir)/'`src/shared/micp.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) src/shared/$(DEPDIR)/libshared_ell_la-micp.Tpo src/shared/$(DEPDIR)/libshared_ell_la-micp.Plo @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='src/shared/micp.c' object='src/shared/libshared_ell_la-micp.lo' libtool=yes @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_libshared_ell_la_CFLAGS) $(CFLAGS) -c -o src/shared/libshared_ell_la-micp.lo `test -f 'src/shared/micp.c' || echo '$(srcdir)/'`src/shared/micp.c src/shared/libshared_ell_la-csip.lo: src/shared/csip.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_libshared_ell_la_CFLAGS) $(CFLAGS) -MT src/shared/libshared_ell_la-csip.lo -MD -MP -MF src/shared/$(DEPDIR)/libshared_ell_la-csip.Tpo -c -o src/shared/libshared_ell_la-csip.lo `test -f 'src/shared/csip.c' || echo '$(srcdir)/'`src/shared/csip.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) src/shared/$(DEPDIR)/libshared_ell_la-csip.Tpo src/shared/$(DEPDIR)/libshared_ell_la-csip.Plo @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='src/shared/csip.c' object='src/shared/libshared_ell_la-csip.lo' libtool=yes @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_libshared_ell_la_CFLAGS) $(CFLAGS) -c -o src/shared/libshared_ell_la-csip.lo `test -f 'src/shared/csip.c' || echo '$(srcdir)/'`src/shared/csip.c src/shared/libshared_ell_la-bass.lo: src/shared/bass.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_libshared_ell_la_CFLAGS) $(CFLAGS) -MT src/shared/libshared_ell_la-bass.lo -MD -MP -MF src/shared/$(DEPDIR)/libshared_ell_la-bass.Tpo -c -o src/shared/libshared_ell_la-bass.lo `test -f 'src/shared/bass.c' || echo '$(srcdir)/'`src/shared/bass.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) src/shared/$(DEPDIR)/libshared_ell_la-bass.Tpo src/shared/$(DEPDIR)/libshared_ell_la-bass.Plo @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='src/shared/bass.c' object='src/shared/libshared_ell_la-bass.lo' libtool=yes @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_libshared_ell_la_CFLAGS) $(CFLAGS) -c -o src/shared/libshared_ell_la-bass.lo `test -f 'src/shared/bass.c' || echo '$(srcdir)/'`src/shared/bass.c src/shared/libshared_ell_la-ccp.lo: src/shared/ccp.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_libshared_ell_la_CFLAGS) $(CFLAGS) -MT src/shared/libshared_ell_la-ccp.lo -MD -MP -MF src/shared/$(DEPDIR)/libshared_ell_la-ccp.Tpo -c -o src/shared/libshared_ell_la-ccp.lo `test -f 'src/shared/ccp.c' || echo '$(srcdir)/'`src/shared/ccp.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) src/shared/$(DEPDIR)/libshared_ell_la-ccp.Tpo src/shared/$(DEPDIR)/libshared_ell_la-ccp.Plo @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='src/shared/ccp.c' object='src/shared/libshared_ell_la-ccp.lo' libtool=yes @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_libshared_ell_la_CFLAGS) $(CFLAGS) -c -o src/shared/libshared_ell_la-ccp.lo `test -f 'src/shared/ccp.c' || echo '$(srcdir)/'`src/shared/ccp.c src/shared/libshared_ell_la-asha.lo: src/shared/asha.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_libshared_ell_la_CFLAGS) $(CFLAGS) -MT src/shared/libshared_ell_la-asha.lo -MD -MP -MF src/shared/$(DEPDIR)/libshared_ell_la-asha.Tpo -c -o src/shared/libshared_ell_la-asha.lo `test -f 'src/shared/asha.c' || echo '$(srcdir)/'`src/shared/asha.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) src/shared/$(DEPDIR)/libshared_ell_la-asha.Tpo src/shared/$(DEPDIR)/libshared_ell_la-asha.Plo @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='src/shared/asha.c' object='src/shared/libshared_ell_la-asha.lo' libtool=yes @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_libshared_ell_la_CFLAGS) $(CFLAGS) -c -o src/shared/libshared_ell_la-asha.lo `test -f 'src/shared/asha.c' || echo '$(srcdir)/'`src/shared/asha.c src/shared/libshared_ell_la-shell.lo: src/shared/shell.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_libshared_ell_la_CFLAGS) $(CFLAGS) -MT src/shared/libshared_ell_la-shell.lo -MD -MP -MF src/shared/$(DEPDIR)/libshared_ell_la-shell.Tpo -c -o src/shared/libshared_ell_la-shell.lo `test -f 'src/shared/shell.c' || echo '$(srcdir)/'`src/shared/shell.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) src/shared/$(DEPDIR)/libshared_ell_la-shell.Tpo src/shared/$(DEPDIR)/libshared_ell_la-shell.Plo @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='src/shared/shell.c' object='src/shared/libshared_ell_la-shell.lo' libtool=yes @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_libshared_ell_la_CFLAGS) $(CFLAGS) -c -o src/shared/libshared_ell_la-shell.lo `test -f 'src/shared/shell.c' || echo '$(srcdir)/'`src/shared/shell.c src/shared/libshared_ell_la-io-ell.lo: src/shared/io-ell.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_libshared_ell_la_CFLAGS) $(CFLAGS) -MT src/shared/libshared_ell_la-io-ell.lo -MD -MP -MF src/shared/$(DEPDIR)/libshared_ell_la-io-ell.Tpo -c -o src/shared/libshared_ell_la-io-ell.lo `test -f 'src/shared/io-ell.c' || echo '$(srcdir)/'`src/shared/io-ell.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) src/shared/$(DEPDIR)/libshared_ell_la-io-ell.Tpo src/shared/$(DEPDIR)/libshared_ell_la-io-ell.Plo @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='src/shared/io-ell.c' object='src/shared/libshared_ell_la-io-ell.lo' libtool=yes @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_libshared_ell_la_CFLAGS) $(CFLAGS) -c -o src/shared/libshared_ell_la-io-ell.lo `test -f 'src/shared/io-ell.c' || echo '$(srcdir)/'`src/shared/io-ell.c src/shared/libshared_ell_la-timeout-ell.lo: src/shared/timeout-ell.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_libshared_ell_la_CFLAGS) $(CFLAGS) -MT src/shared/libshared_ell_la-timeout-ell.lo -MD -MP -MF src/shared/$(DEPDIR)/libshared_ell_la-timeout-ell.Tpo -c -o src/shared/libshared_ell_la-timeout-ell.lo `test -f 'src/shared/timeout-ell.c' || echo '$(srcdir)/'`src/shared/timeout-ell.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) src/shared/$(DEPDIR)/libshared_ell_la-timeout-ell.Tpo src/shared/$(DEPDIR)/libshared_ell_la-timeout-ell.Plo @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='src/shared/timeout-ell.c' object='src/shared/libshared_ell_la-timeout-ell.lo' libtool=yes @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_libshared_ell_la_CFLAGS) $(CFLAGS) -c -o src/shared/libshared_ell_la-timeout-ell.lo `test -f 'src/shared/timeout-ell.c' || echo '$(srcdir)/'`src/shared/timeout-ell.c src/shared/libshared_ell_la-mainloop-ell.lo: src/shared/mainloop-ell.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_libshared_ell_la_CFLAGS) $(CFLAGS) -MT src/shared/libshared_ell_la-mainloop-ell.lo -MD -MP -MF src/shared/$(DEPDIR)/libshared_ell_la-mainloop-ell.Tpo -c -o src/shared/libshared_ell_la-mainloop-ell.lo `test -f 'src/shared/mainloop-ell.c' || echo '$(srcdir)/'`src/shared/mainloop-ell.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) src/shared/$(DEPDIR)/libshared_ell_la-mainloop-ell.Tpo src/shared/$(DEPDIR)/libshared_ell_la-mainloop-ell.Plo @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='src/shared/mainloop-ell.c' object='src/shared/libshared_ell_la-mainloop-ell.lo' libtool=yes @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_libshared_ell_la_CFLAGS) $(CFLAGS) -c -o src/shared/libshared_ell_la-mainloop-ell.lo `test -f 'src/shared/mainloop-ell.c' || echo '$(srcdir)/'`src/shared/mainloop-ell.c src/shared/libshared_glib_la-queue.lo: src/shared/queue.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_libshared_glib_la_CFLAGS) $(CFLAGS) -MT src/shared/libshared_glib_la-queue.lo -MD -MP -MF src/shared/$(DEPDIR)/libshared_glib_la-queue.Tpo -c -o src/shared/libshared_glib_la-queue.lo `test -f 'src/shared/queue.c' || echo '$(srcdir)/'`src/shared/queue.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) src/shared/$(DEPDIR)/libshared_glib_la-queue.Tpo src/shared/$(DEPDIR)/libshared_glib_la-queue.Plo @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='src/shared/queue.c' object='src/shared/libshared_glib_la-queue.lo' libtool=yes @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_libshared_glib_la_CFLAGS) $(CFLAGS) -c -o src/shared/libshared_glib_la-queue.lo `test -f 'src/shared/queue.c' || echo '$(srcdir)/'`src/shared/queue.c src/shared/libshared_glib_la-util.lo: src/shared/util.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_libshared_glib_la_CFLAGS) $(CFLAGS) -MT src/shared/libshared_glib_la-util.lo -MD -MP -MF src/shared/$(DEPDIR)/libshared_glib_la-util.Tpo -c -o src/shared/libshared_glib_la-util.lo `test -f 'src/shared/util.c' || echo '$(srcdir)/'`src/shared/util.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) src/shared/$(DEPDIR)/libshared_glib_la-util.Tpo src/shared/$(DEPDIR)/libshared_glib_la-util.Plo @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='src/shared/util.c' object='src/shared/libshared_glib_la-util.lo' libtool=yes @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_libshared_glib_la_CFLAGS) $(CFLAGS) -c -o src/shared/libshared_glib_la-util.lo `test -f 'src/shared/util.c' || echo '$(srcdir)/'`src/shared/util.c src/shared/libshared_glib_la-mgmt.lo: src/shared/mgmt.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_libshared_glib_la_CFLAGS) $(CFLAGS) -MT src/shared/libshared_glib_la-mgmt.lo -MD -MP -MF src/shared/$(DEPDIR)/libshared_glib_la-mgmt.Tpo -c -o src/shared/libshared_glib_la-mgmt.lo `test -f 'src/shared/mgmt.c' || echo '$(srcdir)/'`src/shared/mgmt.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) src/shared/$(DEPDIR)/libshared_glib_la-mgmt.Tpo src/shared/$(DEPDIR)/libshared_glib_la-mgmt.Plo @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='src/shared/mgmt.c' object='src/shared/libshared_glib_la-mgmt.lo' libtool=yes @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_libshared_glib_la_CFLAGS) $(CFLAGS) -c -o src/shared/libshared_glib_la-mgmt.lo `test -f 'src/shared/mgmt.c' || echo '$(srcdir)/'`src/shared/mgmt.c src/shared/libshared_glib_la-crypto.lo: src/shared/crypto.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_libshared_glib_la_CFLAGS) $(CFLAGS) -MT src/shared/libshared_glib_la-crypto.lo -MD -MP -MF src/shared/$(DEPDIR)/libshared_glib_la-crypto.Tpo -c -o src/shared/libshared_glib_la-crypto.lo `test -f 'src/shared/crypto.c' || echo '$(srcdir)/'`src/shared/crypto.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) src/shared/$(DEPDIR)/libshared_glib_la-crypto.Tpo src/shared/$(DEPDIR)/libshared_glib_la-crypto.Plo @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='src/shared/crypto.c' object='src/shared/libshared_glib_la-crypto.lo' libtool=yes @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_libshared_glib_la_CFLAGS) $(CFLAGS) -c -o src/shared/libshared_glib_la-crypto.lo `test -f 'src/shared/crypto.c' || echo '$(srcdir)/'`src/shared/crypto.c src/shared/libshared_glib_la-ecc.lo: src/shared/ecc.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_libshared_glib_la_CFLAGS) $(CFLAGS) -MT src/shared/libshared_glib_la-ecc.lo -MD -MP -MF src/shared/$(DEPDIR)/libshared_glib_la-ecc.Tpo -c -o src/shared/libshared_glib_la-ecc.lo `test -f 'src/shared/ecc.c' || echo '$(srcdir)/'`src/shared/ecc.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) src/shared/$(DEPDIR)/libshared_glib_la-ecc.Tpo src/shared/$(DEPDIR)/libshared_glib_la-ecc.Plo @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='src/shared/ecc.c' object='src/shared/libshared_glib_la-ecc.lo' libtool=yes @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_libshared_glib_la_CFLAGS) $(CFLAGS) -c -o src/shared/libshared_glib_la-ecc.lo `test -f 'src/shared/ecc.c' || echo '$(srcdir)/'`src/shared/ecc.c src/shared/libshared_glib_la-ringbuf.lo: src/shared/ringbuf.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_libshared_glib_la_CFLAGS) $(CFLAGS) -MT src/shared/libshared_glib_la-ringbuf.lo -MD -MP -MF src/shared/$(DEPDIR)/libshared_glib_la-ringbuf.Tpo -c -o src/shared/libshared_glib_la-ringbuf.lo `test -f 'src/shared/ringbuf.c' || echo '$(srcdir)/'`src/shared/ringbuf.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) src/shared/$(DEPDIR)/libshared_glib_la-ringbuf.Tpo src/shared/$(DEPDIR)/libshared_glib_la-ringbuf.Plo @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='src/shared/ringbuf.c' object='src/shared/libshared_glib_la-ringbuf.lo' libtool=yes @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_libshared_glib_la_CFLAGS) $(CFLAGS) -c -o src/shared/libshared_glib_la-ringbuf.lo `test -f 'src/shared/ringbuf.c' || echo '$(srcdir)/'`src/shared/ringbuf.c src/shared/libshared_glib_la-hci.lo: src/shared/hci.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_libshared_glib_la_CFLAGS) $(CFLAGS) -MT src/shared/libshared_glib_la-hci.lo -MD -MP -MF src/shared/$(DEPDIR)/libshared_glib_la-hci.Tpo -c -o src/shared/libshared_glib_la-hci.lo `test -f 'src/shared/hci.c' || echo '$(srcdir)/'`src/shared/hci.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) src/shared/$(DEPDIR)/libshared_glib_la-hci.Tpo src/shared/$(DEPDIR)/libshared_glib_la-hci.Plo @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='src/shared/hci.c' object='src/shared/libshared_glib_la-hci.lo' libtool=yes @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_libshared_glib_la_CFLAGS) $(CFLAGS) -c -o src/shared/libshared_glib_la-hci.lo `test -f 'src/shared/hci.c' || echo '$(srcdir)/'`src/shared/hci.c src/shared/libshared_glib_la-hci-crypto.lo: src/shared/hci-crypto.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_libshared_glib_la_CFLAGS) $(CFLAGS) -MT src/shared/libshared_glib_la-hci-crypto.lo -MD -MP -MF src/shared/$(DEPDIR)/libshared_glib_la-hci-crypto.Tpo -c -o src/shared/libshared_glib_la-hci-crypto.lo `test -f 'src/shared/hci-crypto.c' || echo '$(srcdir)/'`src/shared/hci-crypto.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) src/shared/$(DEPDIR)/libshared_glib_la-hci-crypto.Tpo src/shared/$(DEPDIR)/libshared_glib_la-hci-crypto.Plo @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='src/shared/hci-crypto.c' object='src/shared/libshared_glib_la-hci-crypto.lo' libtool=yes @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_libshared_glib_la_CFLAGS) $(CFLAGS) -c -o src/shared/libshared_glib_la-hci-crypto.lo `test -f 'src/shared/hci-crypto.c' || echo '$(srcdir)/'`src/shared/hci-crypto.c src/shared/libshared_glib_la-hfp.lo: src/shared/hfp.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_libshared_glib_la_CFLAGS) $(CFLAGS) -MT src/shared/libshared_glib_la-hfp.lo -MD -MP -MF src/shared/$(DEPDIR)/libshared_glib_la-hfp.Tpo -c -o src/shared/libshared_glib_la-hfp.lo `test -f 'src/shared/hfp.c' || echo '$(srcdir)/'`src/shared/hfp.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) src/shared/$(DEPDIR)/libshared_glib_la-hfp.Tpo src/shared/$(DEPDIR)/libshared_glib_la-hfp.Plo @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='src/shared/hfp.c' object='src/shared/libshared_glib_la-hfp.lo' libtool=yes @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_libshared_glib_la_CFLAGS) $(CFLAGS) -c -o src/shared/libshared_glib_la-hfp.lo `test -f 'src/shared/hfp.c' || echo '$(srcdir)/'`src/shared/hfp.c src/shared/libshared_glib_la-uhid.lo: src/shared/uhid.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_libshared_glib_la_CFLAGS) $(CFLAGS) -MT src/shared/libshared_glib_la-uhid.lo -MD -MP -MF src/shared/$(DEPDIR)/libshared_glib_la-uhid.Tpo -c -o src/shared/libshared_glib_la-uhid.lo `test -f 'src/shared/uhid.c' || echo '$(srcdir)/'`src/shared/uhid.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) src/shared/$(DEPDIR)/libshared_glib_la-uhid.Tpo src/shared/$(DEPDIR)/libshared_glib_la-uhid.Plo @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='src/shared/uhid.c' object='src/shared/libshared_glib_la-uhid.lo' libtool=yes @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_libshared_glib_la_CFLAGS) $(CFLAGS) -c -o src/shared/libshared_glib_la-uhid.lo `test -f 'src/shared/uhid.c' || echo '$(srcdir)/'`src/shared/uhid.c src/shared/libshared_glib_la-pcap.lo: src/shared/pcap.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_libshared_glib_la_CFLAGS) $(CFLAGS) -MT src/shared/libshared_glib_la-pcap.lo -MD -MP -MF src/shared/$(DEPDIR)/libshared_glib_la-pcap.Tpo -c -o src/shared/libshared_glib_la-pcap.lo `test -f 'src/shared/pcap.c' || echo '$(srcdir)/'`src/shared/pcap.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) src/shared/$(DEPDIR)/libshared_glib_la-pcap.Tpo src/shared/$(DEPDIR)/libshared_glib_la-pcap.Plo @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='src/shared/pcap.c' object='src/shared/libshared_glib_la-pcap.lo' libtool=yes @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_libshared_glib_la_CFLAGS) $(CFLAGS) -c -o src/shared/libshared_glib_la-pcap.lo `test -f 'src/shared/pcap.c' || echo '$(srcdir)/'`src/shared/pcap.c src/shared/libshared_glib_la-btsnoop.lo: src/shared/btsnoop.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_libshared_glib_la_CFLAGS) $(CFLAGS) -MT src/shared/libshared_glib_la-btsnoop.lo -MD -MP -MF src/shared/$(DEPDIR)/libshared_glib_la-btsnoop.Tpo -c -o src/shared/libshared_glib_la-btsnoop.lo `test -f 'src/shared/btsnoop.c' || echo '$(srcdir)/'`src/shared/btsnoop.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) src/shared/$(DEPDIR)/libshared_glib_la-btsnoop.Tpo src/shared/$(DEPDIR)/libshared_glib_la-btsnoop.Plo @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='src/shared/btsnoop.c' object='src/shared/libshared_glib_la-btsnoop.lo' libtool=yes @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_libshared_glib_la_CFLAGS) $(CFLAGS) -c -o src/shared/libshared_glib_la-btsnoop.lo `test -f 'src/shared/btsnoop.c' || echo '$(srcdir)/'`src/shared/btsnoop.c src/shared/libshared_glib_la-ad.lo: src/shared/ad.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_libshared_glib_la_CFLAGS) $(CFLAGS) -MT src/shared/libshared_glib_la-ad.lo -MD -MP -MF src/shared/$(DEPDIR)/libshared_glib_la-ad.Tpo -c -o src/shared/libshared_glib_la-ad.lo `test -f 'src/shared/ad.c' || echo '$(srcdir)/'`src/shared/ad.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) src/shared/$(DEPDIR)/libshared_glib_la-ad.Tpo src/shared/$(DEPDIR)/libshared_glib_la-ad.Plo @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='src/shared/ad.c' object='src/shared/libshared_glib_la-ad.lo' libtool=yes @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_libshared_glib_la_CFLAGS) $(CFLAGS) -c -o src/shared/libshared_glib_la-ad.lo `test -f 'src/shared/ad.c' || echo '$(srcdir)/'`src/shared/ad.c src/shared/libshared_glib_la-att.lo: src/shared/att.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_libshared_glib_la_CFLAGS) $(CFLAGS) -MT src/shared/libshared_glib_la-att.lo -MD -MP -MF src/shared/$(DEPDIR)/libshared_glib_la-att.Tpo -c -o src/shared/libshared_glib_la-att.lo `test -f 'src/shared/att.c' || echo '$(srcdir)/'`src/shared/att.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) src/shared/$(DEPDIR)/libshared_glib_la-att.Tpo src/shared/$(DEPDIR)/libshared_glib_la-att.Plo @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='src/shared/att.c' object='src/shared/libshared_glib_la-att.lo' libtool=yes @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_libshared_glib_la_CFLAGS) $(CFLAGS) -c -o src/shared/libshared_glib_la-att.lo `test -f 'src/shared/att.c' || echo '$(srcdir)/'`src/shared/att.c src/shared/libshared_glib_la-gatt-helpers.lo: src/shared/gatt-helpers.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_libshared_glib_la_CFLAGS) $(CFLAGS) -MT src/shared/libshared_glib_la-gatt-helpers.lo -MD -MP -MF src/shared/$(DEPDIR)/libshared_glib_la-gatt-helpers.Tpo -c -o src/shared/libshared_glib_la-gatt-helpers.lo `test -f 'src/shared/gatt-helpers.c' || echo '$(srcdir)/'`src/shared/gatt-helpers.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) src/shared/$(DEPDIR)/libshared_glib_la-gatt-helpers.Tpo src/shared/$(DEPDIR)/libshared_glib_la-gatt-helpers.Plo @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='src/shared/gatt-helpers.c' object='src/shared/libshared_glib_la-gatt-helpers.lo' libtool=yes @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_libshared_glib_la_CFLAGS) $(CFLAGS) -c -o src/shared/libshared_glib_la-gatt-helpers.lo `test -f 'src/shared/gatt-helpers.c' || echo '$(srcdir)/'`src/shared/gatt-helpers.c src/shared/libshared_glib_la-gatt-client.lo: src/shared/gatt-client.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_libshared_glib_la_CFLAGS) $(CFLAGS) -MT src/shared/libshared_glib_la-gatt-client.lo -MD -MP -MF src/shared/$(DEPDIR)/libshared_glib_la-gatt-client.Tpo -c -o src/shared/libshared_glib_la-gatt-client.lo `test -f 'src/shared/gatt-client.c' || echo '$(srcdir)/'`src/shared/gatt-client.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) src/shared/$(DEPDIR)/libshared_glib_la-gatt-client.Tpo src/shared/$(DEPDIR)/libshared_glib_la-gatt-client.Plo @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='src/shared/gatt-client.c' object='src/shared/libshared_glib_la-gatt-client.lo' libtool=yes @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_libshared_glib_la_CFLAGS) $(CFLAGS) -c -o src/shared/libshared_glib_la-gatt-client.lo `test -f 'src/shared/gatt-client.c' || echo '$(srcdir)/'`src/shared/gatt-client.c src/shared/libshared_glib_la-gatt-server.lo: src/shared/gatt-server.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_libshared_glib_la_CFLAGS) $(CFLAGS) -MT src/shared/libshared_glib_la-gatt-server.lo -MD -MP -MF src/shared/$(DEPDIR)/libshared_glib_la-gatt-server.Tpo -c -o src/shared/libshared_glib_la-gatt-server.lo `test -f 'src/shared/gatt-server.c' || echo '$(srcdir)/'`src/shared/gatt-server.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) src/shared/$(DEPDIR)/libshared_glib_la-gatt-server.Tpo src/shared/$(DEPDIR)/libshared_glib_la-gatt-server.Plo @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='src/shared/gatt-server.c' object='src/shared/libshared_glib_la-gatt-server.lo' libtool=yes @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_libshared_glib_la_CFLAGS) $(CFLAGS) -c -o src/shared/libshared_glib_la-gatt-server.lo `test -f 'src/shared/gatt-server.c' || echo '$(srcdir)/'`src/shared/gatt-server.c src/shared/libshared_glib_la-gatt-db.lo: src/shared/gatt-db.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_libshared_glib_la_CFLAGS) $(CFLAGS) -MT src/shared/libshared_glib_la-gatt-db.lo -MD -MP -MF src/shared/$(DEPDIR)/libshared_glib_la-gatt-db.Tpo -c -o src/shared/libshared_glib_la-gatt-db.lo `test -f 'src/shared/gatt-db.c' || echo '$(srcdir)/'`src/shared/gatt-db.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) src/shared/$(DEPDIR)/libshared_glib_la-gatt-db.Tpo src/shared/$(DEPDIR)/libshared_glib_la-gatt-db.Plo @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='src/shared/gatt-db.c' object='src/shared/libshared_glib_la-gatt-db.lo' libtool=yes @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_libshared_glib_la_CFLAGS) $(CFLAGS) -c -o src/shared/libshared_glib_la-gatt-db.lo `test -f 'src/shared/gatt-db.c' || echo '$(srcdir)/'`src/shared/gatt-db.c src/shared/libshared_glib_la-gap.lo: src/shared/gap.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_libshared_glib_la_CFLAGS) $(CFLAGS) -MT src/shared/libshared_glib_la-gap.lo -MD -MP -MF src/shared/$(DEPDIR)/libshared_glib_la-gap.Tpo -c -o src/shared/libshared_glib_la-gap.lo `test -f 'src/shared/gap.c' || echo '$(srcdir)/'`src/shared/gap.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) src/shared/$(DEPDIR)/libshared_glib_la-gap.Tpo src/shared/$(DEPDIR)/libshared_glib_la-gap.Plo @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='src/shared/gap.c' object='src/shared/libshared_glib_la-gap.lo' libtool=yes @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_libshared_glib_la_CFLAGS) $(CFLAGS) -c -o src/shared/libshared_glib_la-gap.lo `test -f 'src/shared/gap.c' || echo '$(srcdir)/'`src/shared/gap.c src/shared/libshared_glib_la-log.lo: src/shared/log.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_libshared_glib_la_CFLAGS) $(CFLAGS) -MT src/shared/libshared_glib_la-log.lo -MD -MP -MF src/shared/$(DEPDIR)/libshared_glib_la-log.Tpo -c -o src/shared/libshared_glib_la-log.lo `test -f 'src/shared/log.c' || echo '$(srcdir)/'`src/shared/log.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) src/shared/$(DEPDIR)/libshared_glib_la-log.Tpo src/shared/$(DEPDIR)/libshared_glib_la-log.Plo @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='src/shared/log.c' object='src/shared/libshared_glib_la-log.lo' libtool=yes @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_libshared_glib_la_CFLAGS) $(CFLAGS) -c -o src/shared/libshared_glib_la-log.lo `test -f 'src/shared/log.c' || echo '$(srcdir)/'`src/shared/log.c src/shared/libshared_glib_la-bap.lo: src/shared/bap.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_libshared_glib_la_CFLAGS) $(CFLAGS) -MT src/shared/libshared_glib_la-bap.lo -MD -MP -MF src/shared/$(DEPDIR)/libshared_glib_la-bap.Tpo -c -o src/shared/libshared_glib_la-bap.lo `test -f 'src/shared/bap.c' || echo '$(srcdir)/'`src/shared/bap.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) src/shared/$(DEPDIR)/libshared_glib_la-bap.Tpo src/shared/$(DEPDIR)/libshared_glib_la-bap.Plo @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='src/shared/bap.c' object='src/shared/libshared_glib_la-bap.lo' libtool=yes @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_libshared_glib_la_CFLAGS) $(CFLAGS) -c -o src/shared/libshared_glib_la-bap.lo `test -f 'src/shared/bap.c' || echo '$(srcdir)/'`src/shared/bap.c src/shared/libshared_glib_la-bap-debug.lo: src/shared/bap-debug.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_libshared_glib_la_CFLAGS) $(CFLAGS) -MT src/shared/libshared_glib_la-bap-debug.lo -MD -MP -MF src/shared/$(DEPDIR)/libshared_glib_la-bap-debug.Tpo -c -o src/shared/libshared_glib_la-bap-debug.lo `test -f 'src/shared/bap-debug.c' || echo '$(srcdir)/'`src/shared/bap-debug.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) src/shared/$(DEPDIR)/libshared_glib_la-bap-debug.Tpo src/shared/$(DEPDIR)/libshared_glib_la-bap-debug.Plo @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='src/shared/bap-debug.c' object='src/shared/libshared_glib_la-bap-debug.lo' libtool=yes @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_libshared_glib_la_CFLAGS) $(CFLAGS) -c -o src/shared/libshared_glib_la-bap-debug.lo `test -f 'src/shared/bap-debug.c' || echo '$(srcdir)/'`src/shared/bap-debug.c src/shared/libshared_glib_la-mcp.lo: src/shared/mcp.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_libshared_glib_la_CFLAGS) $(CFLAGS) -MT src/shared/libshared_glib_la-mcp.lo -MD -MP -MF src/shared/$(DEPDIR)/libshared_glib_la-mcp.Tpo -c -o src/shared/libshared_glib_la-mcp.lo `test -f 'src/shared/mcp.c' || echo '$(srcdir)/'`src/shared/mcp.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) src/shared/$(DEPDIR)/libshared_glib_la-mcp.Tpo src/shared/$(DEPDIR)/libshared_glib_la-mcp.Plo @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='src/shared/mcp.c' object='src/shared/libshared_glib_la-mcp.lo' libtool=yes @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_libshared_glib_la_CFLAGS) $(CFLAGS) -c -o src/shared/libshared_glib_la-mcp.lo `test -f 'src/shared/mcp.c' || echo '$(srcdir)/'`src/shared/mcp.c src/shared/libshared_glib_la-vcp.lo: src/shared/vcp.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_libshared_glib_la_CFLAGS) $(CFLAGS) -MT src/shared/libshared_glib_la-vcp.lo -MD -MP -MF src/shared/$(DEPDIR)/libshared_glib_la-vcp.Tpo -c -o src/shared/libshared_glib_la-vcp.lo `test -f 'src/shared/vcp.c' || echo '$(srcdir)/'`src/shared/vcp.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) src/shared/$(DEPDIR)/libshared_glib_la-vcp.Tpo src/shared/$(DEPDIR)/libshared_glib_la-vcp.Plo @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='src/shared/vcp.c' object='src/shared/libshared_glib_la-vcp.lo' libtool=yes @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_libshared_glib_la_CFLAGS) $(CFLAGS) -c -o src/shared/libshared_glib_la-vcp.lo `test -f 'src/shared/vcp.c' || echo '$(srcdir)/'`src/shared/vcp.c src/shared/libshared_glib_la-micp.lo: src/shared/micp.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_libshared_glib_la_CFLAGS) $(CFLAGS) -MT src/shared/libshared_glib_la-micp.lo -MD -MP -MF src/shared/$(DEPDIR)/libshared_glib_la-micp.Tpo -c -o src/shared/libshared_glib_la-micp.lo `test -f 'src/shared/micp.c' || echo '$(srcdir)/'`src/shared/micp.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) src/shared/$(DEPDIR)/libshared_glib_la-micp.Tpo src/shared/$(DEPDIR)/libshared_glib_la-micp.Plo @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='src/shared/micp.c' object='src/shared/libshared_glib_la-micp.lo' libtool=yes @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_libshared_glib_la_CFLAGS) $(CFLAGS) -c -o src/shared/libshared_glib_la-micp.lo `test -f 'src/shared/micp.c' || echo '$(srcdir)/'`src/shared/micp.c src/shared/libshared_glib_la-csip.lo: src/shared/csip.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_libshared_glib_la_CFLAGS) $(CFLAGS) -MT src/shared/libshared_glib_la-csip.lo -MD -MP -MF src/shared/$(DEPDIR)/libshared_glib_la-csip.Tpo -c -o src/shared/libshared_glib_la-csip.lo `test -f 'src/shared/csip.c' || echo '$(srcdir)/'`src/shared/csip.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) src/shared/$(DEPDIR)/libshared_glib_la-csip.Tpo src/shared/$(DEPDIR)/libshared_glib_la-csip.Plo @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='src/shared/csip.c' object='src/shared/libshared_glib_la-csip.lo' libtool=yes @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_libshared_glib_la_CFLAGS) $(CFLAGS) -c -o src/shared/libshared_glib_la-csip.lo `test -f 'src/shared/csip.c' || echo '$(srcdir)/'`src/shared/csip.c src/shared/libshared_glib_la-bass.lo: src/shared/bass.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_libshared_glib_la_CFLAGS) $(CFLAGS) -MT src/shared/libshared_glib_la-bass.lo -MD -MP -MF src/shared/$(DEPDIR)/libshared_glib_la-bass.Tpo -c -o src/shared/libshared_glib_la-bass.lo `test -f 'src/shared/bass.c' || echo '$(srcdir)/'`src/shared/bass.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) src/shared/$(DEPDIR)/libshared_glib_la-bass.Tpo src/shared/$(DEPDIR)/libshared_glib_la-bass.Plo @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='src/shared/bass.c' object='src/shared/libshared_glib_la-bass.lo' libtool=yes @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_libshared_glib_la_CFLAGS) $(CFLAGS) -c -o src/shared/libshared_glib_la-bass.lo `test -f 'src/shared/bass.c' || echo '$(srcdir)/'`src/shared/bass.c src/shared/libshared_glib_la-ccp.lo: src/shared/ccp.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_libshared_glib_la_CFLAGS) $(CFLAGS) -MT src/shared/libshared_glib_la-ccp.lo -MD -MP -MF src/shared/$(DEPDIR)/libshared_glib_la-ccp.Tpo -c -o src/shared/libshared_glib_la-ccp.lo `test -f 'src/shared/ccp.c' || echo '$(srcdir)/'`src/shared/ccp.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) src/shared/$(DEPDIR)/libshared_glib_la-ccp.Tpo src/shared/$(DEPDIR)/libshared_glib_la-ccp.Plo @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='src/shared/ccp.c' object='src/shared/libshared_glib_la-ccp.lo' libtool=yes @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_libshared_glib_la_CFLAGS) $(CFLAGS) -c -o src/shared/libshared_glib_la-ccp.lo `test -f 'src/shared/ccp.c' || echo '$(srcdir)/'`src/shared/ccp.c src/shared/libshared_glib_la-asha.lo: src/shared/asha.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_libshared_glib_la_CFLAGS) $(CFLAGS) -MT src/shared/libshared_glib_la-asha.lo -MD -MP -MF src/shared/$(DEPDIR)/libshared_glib_la-asha.Tpo -c -o src/shared/libshared_glib_la-asha.lo `test -f 'src/shared/asha.c' || echo '$(srcdir)/'`src/shared/asha.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) src/shared/$(DEPDIR)/libshared_glib_la-asha.Tpo src/shared/$(DEPDIR)/libshared_glib_la-asha.Plo @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='src/shared/asha.c' object='src/shared/libshared_glib_la-asha.lo' libtool=yes @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_libshared_glib_la_CFLAGS) $(CFLAGS) -c -o src/shared/libshared_glib_la-asha.lo `test -f 'src/shared/asha.c' || echo '$(srcdir)/'`src/shared/asha.c src/shared/libshared_glib_la-shell.lo: src/shared/shell.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_libshared_glib_la_CFLAGS) $(CFLAGS) -MT src/shared/libshared_glib_la-shell.lo -MD -MP -MF src/shared/$(DEPDIR)/libshared_glib_la-shell.Tpo -c -o src/shared/libshared_glib_la-shell.lo `test -f 'src/shared/shell.c' || echo '$(srcdir)/'`src/shared/shell.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) src/shared/$(DEPDIR)/libshared_glib_la-shell.Tpo src/shared/$(DEPDIR)/libshared_glib_la-shell.Plo @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='src/shared/shell.c' object='src/shared/libshared_glib_la-shell.lo' libtool=yes @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_libshared_glib_la_CFLAGS) $(CFLAGS) -c -o src/shared/libshared_glib_la-shell.lo `test -f 'src/shared/shell.c' || echo '$(srcdir)/'`src/shared/shell.c src/shared/libshared_glib_la-io-glib.lo: src/shared/io-glib.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_libshared_glib_la_CFLAGS) $(CFLAGS) -MT src/shared/libshared_glib_la-io-glib.lo -MD -MP -MF src/shared/$(DEPDIR)/libshared_glib_la-io-glib.Tpo -c -o src/shared/libshared_glib_la-io-glib.lo `test -f 'src/shared/io-glib.c' || echo '$(srcdir)/'`src/shared/io-glib.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) src/shared/$(DEPDIR)/libshared_glib_la-io-glib.Tpo src/shared/$(DEPDIR)/libshared_glib_la-io-glib.Plo @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='src/shared/io-glib.c' object='src/shared/libshared_glib_la-io-glib.lo' libtool=yes @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_libshared_glib_la_CFLAGS) $(CFLAGS) -c -o src/shared/libshared_glib_la-io-glib.lo `test -f 'src/shared/io-glib.c' || echo '$(srcdir)/'`src/shared/io-glib.c src/shared/libshared_glib_la-timeout-glib.lo: src/shared/timeout-glib.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_libshared_glib_la_CFLAGS) $(CFLAGS) -MT src/shared/libshared_glib_la-timeout-glib.lo -MD -MP -MF src/shared/$(DEPDIR)/libshared_glib_la-timeout-glib.Tpo -c -o src/shared/libshared_glib_la-timeout-glib.lo `test -f 'src/shared/timeout-glib.c' || echo '$(srcdir)/'`src/shared/timeout-glib.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) src/shared/$(DEPDIR)/libshared_glib_la-timeout-glib.Tpo src/shared/$(DEPDIR)/libshared_glib_la-timeout-glib.Plo @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='src/shared/timeout-glib.c' object='src/shared/libshared_glib_la-timeout-glib.lo' libtool=yes @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_libshared_glib_la_CFLAGS) $(CFLAGS) -c -o src/shared/libshared_glib_la-timeout-glib.lo `test -f 'src/shared/timeout-glib.c' || echo '$(srcdir)/'`src/shared/timeout-glib.c src/shared/libshared_glib_la-mainloop-glib.lo: src/shared/mainloop-glib.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_libshared_glib_la_CFLAGS) $(CFLAGS) -MT src/shared/libshared_glib_la-mainloop-glib.lo -MD -MP -MF src/shared/$(DEPDIR)/libshared_glib_la-mainloop-glib.Tpo -c -o src/shared/libshared_glib_la-mainloop-glib.lo `test -f 'src/shared/mainloop-glib.c' || echo '$(srcdir)/'`src/shared/mainloop-glib.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) src/shared/$(DEPDIR)/libshared_glib_la-mainloop-glib.Tpo src/shared/$(DEPDIR)/libshared_glib_la-mainloop-glib.Plo @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='src/shared/mainloop-glib.c' object='src/shared/libshared_glib_la-mainloop-glib.lo' libtool=yes @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_libshared_glib_la_CFLAGS) $(CFLAGS) -c -o src/shared/libshared_glib_la-mainloop-glib.lo `test -f 'src/shared/mainloop-glib.c' || echo '$(srcdir)/'`src/shared/mainloop-glib.c src/shared/libshared_glib_la-mainloop-notify.lo: src/shared/mainloop-notify.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_libshared_glib_la_CFLAGS) $(CFLAGS) -MT src/shared/libshared_glib_la-mainloop-notify.lo -MD -MP -MF src/shared/$(DEPDIR)/libshared_glib_la-mainloop-notify.Tpo -c -o src/shared/libshared_glib_la-mainloop-notify.lo `test -f 'src/shared/mainloop-notify.c' || echo '$(srcdir)/'`src/shared/mainloop-notify.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) src/shared/$(DEPDIR)/libshared_glib_la-mainloop-notify.Tpo src/shared/$(DEPDIR)/libshared_glib_la-mainloop-notify.Plo @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='src/shared/mainloop-notify.c' object='src/shared/libshared_glib_la-mainloop-notify.lo' libtool=yes @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_libshared_glib_la_CFLAGS) $(CFLAGS) -c -o src/shared/libshared_glib_la-mainloop-notify.lo `test -f 'src/shared/mainloop-notify.c' || echo '$(srcdir)/'`src/shared/mainloop-notify.c src/shared/libshared_glib_la-tester.lo: src/shared/tester.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_libshared_glib_la_CFLAGS) $(CFLAGS) -MT src/shared/libshared_glib_la-tester.lo -MD -MP -MF src/shared/$(DEPDIR)/libshared_glib_la-tester.Tpo -c -o src/shared/libshared_glib_la-tester.lo `test -f 'src/shared/tester.c' || echo '$(srcdir)/'`src/shared/tester.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) src/shared/$(DEPDIR)/libshared_glib_la-tester.Tpo src/shared/$(DEPDIR)/libshared_glib_la-tester.Plo @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='src/shared/tester.c' object='src/shared/libshared_glib_la-tester.lo' libtool=yes @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_libshared_glib_la_CFLAGS) $(CFLAGS) -c -o src/shared/libshared_glib_la-tester.lo `test -f 'src/shared/tester.c' || echo '$(srcdir)/'`src/shared/tester.c src/shared/libshared_mainloop_la-queue.lo: src/shared/queue.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_libshared_mainloop_la_CFLAGS) $(CFLAGS) -MT src/shared/libshared_mainloop_la-queue.lo -MD -MP -MF src/shared/$(DEPDIR)/libshared_mainloop_la-queue.Tpo -c -o src/shared/libshared_mainloop_la-queue.lo `test -f 'src/shared/queue.c' || echo '$(srcdir)/'`src/shared/queue.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) src/shared/$(DEPDIR)/libshared_mainloop_la-queue.Tpo src/shared/$(DEPDIR)/libshared_mainloop_la-queue.Plo @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='src/shared/queue.c' object='src/shared/libshared_mainloop_la-queue.lo' libtool=yes @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_libshared_mainloop_la_CFLAGS) $(CFLAGS) -c -o src/shared/libshared_mainloop_la-queue.lo `test -f 'src/shared/queue.c' || echo '$(srcdir)/'`src/shared/queue.c src/shared/libshared_mainloop_la-util.lo: src/shared/util.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_libshared_mainloop_la_CFLAGS) $(CFLAGS) -MT src/shared/libshared_mainloop_la-util.lo -MD -MP -MF src/shared/$(DEPDIR)/libshared_mainloop_la-util.Tpo -c -o src/shared/libshared_mainloop_la-util.lo `test -f 'src/shared/util.c' || echo '$(srcdir)/'`src/shared/util.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) src/shared/$(DEPDIR)/libshared_mainloop_la-util.Tpo src/shared/$(DEPDIR)/libshared_mainloop_la-util.Plo @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='src/shared/util.c' object='src/shared/libshared_mainloop_la-util.lo' libtool=yes @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_libshared_mainloop_la_CFLAGS) $(CFLAGS) -c -o src/shared/libshared_mainloop_la-util.lo `test -f 'src/shared/util.c' || echo '$(srcdir)/'`src/shared/util.c src/shared/libshared_mainloop_la-mgmt.lo: src/shared/mgmt.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_libshared_mainloop_la_CFLAGS) $(CFLAGS) -MT src/shared/libshared_mainloop_la-mgmt.lo -MD -MP -MF src/shared/$(DEPDIR)/libshared_mainloop_la-mgmt.Tpo -c -o src/shared/libshared_mainloop_la-mgmt.lo `test -f 'src/shared/mgmt.c' || echo '$(srcdir)/'`src/shared/mgmt.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) src/shared/$(DEPDIR)/libshared_mainloop_la-mgmt.Tpo src/shared/$(DEPDIR)/libshared_mainloop_la-mgmt.Plo @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='src/shared/mgmt.c' object='src/shared/libshared_mainloop_la-mgmt.lo' libtool=yes @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_libshared_mainloop_la_CFLAGS) $(CFLAGS) -c -o src/shared/libshared_mainloop_la-mgmt.lo `test -f 'src/shared/mgmt.c' || echo '$(srcdir)/'`src/shared/mgmt.c src/shared/libshared_mainloop_la-crypto.lo: src/shared/crypto.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_libshared_mainloop_la_CFLAGS) $(CFLAGS) -MT src/shared/libshared_mainloop_la-crypto.lo -MD -MP -MF src/shared/$(DEPDIR)/libshared_mainloop_la-crypto.Tpo -c -o src/shared/libshared_mainloop_la-crypto.lo `test -f 'src/shared/crypto.c' || echo '$(srcdir)/'`src/shared/crypto.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) src/shared/$(DEPDIR)/libshared_mainloop_la-crypto.Tpo src/shared/$(DEPDIR)/libshared_mainloop_la-crypto.Plo @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='src/shared/crypto.c' object='src/shared/libshared_mainloop_la-crypto.lo' libtool=yes @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_libshared_mainloop_la_CFLAGS) $(CFLAGS) -c -o src/shared/libshared_mainloop_la-crypto.lo `test -f 'src/shared/crypto.c' || echo '$(srcdir)/'`src/shared/crypto.c src/shared/libshared_mainloop_la-ecc.lo: src/shared/ecc.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_libshared_mainloop_la_CFLAGS) $(CFLAGS) -MT src/shared/libshared_mainloop_la-ecc.lo -MD -MP -MF src/shared/$(DEPDIR)/libshared_mainloop_la-ecc.Tpo -c -o src/shared/libshared_mainloop_la-ecc.lo `test -f 'src/shared/ecc.c' || echo '$(srcdir)/'`src/shared/ecc.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) src/shared/$(DEPDIR)/libshared_mainloop_la-ecc.Tpo src/shared/$(DEPDIR)/libshared_mainloop_la-ecc.Plo @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='src/shared/ecc.c' object='src/shared/libshared_mainloop_la-ecc.lo' libtool=yes @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_libshared_mainloop_la_CFLAGS) $(CFLAGS) -c -o src/shared/libshared_mainloop_la-ecc.lo `test -f 'src/shared/ecc.c' || echo '$(srcdir)/'`src/shared/ecc.c src/shared/libshared_mainloop_la-ringbuf.lo: src/shared/ringbuf.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_libshared_mainloop_la_CFLAGS) $(CFLAGS) -MT src/shared/libshared_mainloop_la-ringbuf.lo -MD -MP -MF src/shared/$(DEPDIR)/libshared_mainloop_la-ringbuf.Tpo -c -o src/shared/libshared_mainloop_la-ringbuf.lo `test -f 'src/shared/ringbuf.c' || echo '$(srcdir)/'`src/shared/ringbuf.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) src/shared/$(DEPDIR)/libshared_mainloop_la-ringbuf.Tpo src/shared/$(DEPDIR)/libshared_mainloop_la-ringbuf.Plo @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='src/shared/ringbuf.c' object='src/shared/libshared_mainloop_la-ringbuf.lo' libtool=yes @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_libshared_mainloop_la_CFLAGS) $(CFLAGS) -c -o src/shared/libshared_mainloop_la-ringbuf.lo `test -f 'src/shared/ringbuf.c' || echo '$(srcdir)/'`src/shared/ringbuf.c src/shared/libshared_mainloop_la-hci.lo: src/shared/hci.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_libshared_mainloop_la_CFLAGS) $(CFLAGS) -MT src/shared/libshared_mainloop_la-hci.lo -MD -MP -MF src/shared/$(DEPDIR)/libshared_mainloop_la-hci.Tpo -c -o src/shared/libshared_mainloop_la-hci.lo `test -f 'src/shared/hci.c' || echo '$(srcdir)/'`src/shared/hci.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) src/shared/$(DEPDIR)/libshared_mainloop_la-hci.Tpo src/shared/$(DEPDIR)/libshared_mainloop_la-hci.Plo @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='src/shared/hci.c' object='src/shared/libshared_mainloop_la-hci.lo' libtool=yes @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_libshared_mainloop_la_CFLAGS) $(CFLAGS) -c -o src/shared/libshared_mainloop_la-hci.lo `test -f 'src/shared/hci.c' || echo '$(srcdir)/'`src/shared/hci.c src/shared/libshared_mainloop_la-hci-crypto.lo: src/shared/hci-crypto.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_libshared_mainloop_la_CFLAGS) $(CFLAGS) -MT src/shared/libshared_mainloop_la-hci-crypto.lo -MD -MP -MF src/shared/$(DEPDIR)/libshared_mainloop_la-hci-crypto.Tpo -c -o src/shared/libshared_mainloop_la-hci-crypto.lo `test -f 'src/shared/hci-crypto.c' || echo '$(srcdir)/'`src/shared/hci-crypto.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) src/shared/$(DEPDIR)/libshared_mainloop_la-hci-crypto.Tpo src/shared/$(DEPDIR)/libshared_mainloop_la-hci-crypto.Plo @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='src/shared/hci-crypto.c' object='src/shared/libshared_mainloop_la-hci-crypto.lo' libtool=yes @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_libshared_mainloop_la_CFLAGS) $(CFLAGS) -c -o src/shared/libshared_mainloop_la-hci-crypto.lo `test -f 'src/shared/hci-crypto.c' || echo '$(srcdir)/'`src/shared/hci-crypto.c src/shared/libshared_mainloop_la-hfp.lo: src/shared/hfp.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_libshared_mainloop_la_CFLAGS) $(CFLAGS) -MT src/shared/libshared_mainloop_la-hfp.lo -MD -MP -MF src/shared/$(DEPDIR)/libshared_mainloop_la-hfp.Tpo -c -o src/shared/libshared_mainloop_la-hfp.lo `test -f 'src/shared/hfp.c' || echo '$(srcdir)/'`src/shared/hfp.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) src/shared/$(DEPDIR)/libshared_mainloop_la-hfp.Tpo src/shared/$(DEPDIR)/libshared_mainloop_la-hfp.Plo @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='src/shared/hfp.c' object='src/shared/libshared_mainloop_la-hfp.lo' libtool=yes @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_libshared_mainloop_la_CFLAGS) $(CFLAGS) -c -o src/shared/libshared_mainloop_la-hfp.lo `test -f 'src/shared/hfp.c' || echo '$(srcdir)/'`src/shared/hfp.c src/shared/libshared_mainloop_la-uhid.lo: src/shared/uhid.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_libshared_mainloop_la_CFLAGS) $(CFLAGS) -MT src/shared/libshared_mainloop_la-uhid.lo -MD -MP -MF src/shared/$(DEPDIR)/libshared_mainloop_la-uhid.Tpo -c -o src/shared/libshared_mainloop_la-uhid.lo `test -f 'src/shared/uhid.c' || echo '$(srcdir)/'`src/shared/uhid.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) src/shared/$(DEPDIR)/libshared_mainloop_la-uhid.Tpo src/shared/$(DEPDIR)/libshared_mainloop_la-uhid.Plo @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='src/shared/uhid.c' object='src/shared/libshared_mainloop_la-uhid.lo' libtool=yes @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_libshared_mainloop_la_CFLAGS) $(CFLAGS) -c -o src/shared/libshared_mainloop_la-uhid.lo `test -f 'src/shared/uhid.c' || echo '$(srcdir)/'`src/shared/uhid.c src/shared/libshared_mainloop_la-pcap.lo: src/shared/pcap.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_libshared_mainloop_la_CFLAGS) $(CFLAGS) -MT src/shared/libshared_mainloop_la-pcap.lo -MD -MP -MF src/shared/$(DEPDIR)/libshared_mainloop_la-pcap.Tpo -c -o src/shared/libshared_mainloop_la-pcap.lo `test -f 'src/shared/pcap.c' || echo '$(srcdir)/'`src/shared/pcap.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) src/shared/$(DEPDIR)/libshared_mainloop_la-pcap.Tpo src/shared/$(DEPDIR)/libshared_mainloop_la-pcap.Plo @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='src/shared/pcap.c' object='src/shared/libshared_mainloop_la-pcap.lo' libtool=yes @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_libshared_mainloop_la_CFLAGS) $(CFLAGS) -c -o src/shared/libshared_mainloop_la-pcap.lo `test -f 'src/shared/pcap.c' || echo '$(srcdir)/'`src/shared/pcap.c src/shared/libshared_mainloop_la-btsnoop.lo: src/shared/btsnoop.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_libshared_mainloop_la_CFLAGS) $(CFLAGS) -MT src/shared/libshared_mainloop_la-btsnoop.lo -MD -MP -MF src/shared/$(DEPDIR)/libshared_mainloop_la-btsnoop.Tpo -c -o src/shared/libshared_mainloop_la-btsnoop.lo `test -f 'src/shared/btsnoop.c' || echo '$(srcdir)/'`src/shared/btsnoop.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) src/shared/$(DEPDIR)/libshared_mainloop_la-btsnoop.Tpo src/shared/$(DEPDIR)/libshared_mainloop_la-btsnoop.Plo @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='src/shared/btsnoop.c' object='src/shared/libshared_mainloop_la-btsnoop.lo' libtool=yes @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_libshared_mainloop_la_CFLAGS) $(CFLAGS) -c -o src/shared/libshared_mainloop_la-btsnoop.lo `test -f 'src/shared/btsnoop.c' || echo '$(srcdir)/'`src/shared/btsnoop.c src/shared/libshared_mainloop_la-ad.lo: src/shared/ad.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_libshared_mainloop_la_CFLAGS) $(CFLAGS) -MT src/shared/libshared_mainloop_la-ad.lo -MD -MP -MF src/shared/$(DEPDIR)/libshared_mainloop_la-ad.Tpo -c -o src/shared/libshared_mainloop_la-ad.lo `test -f 'src/shared/ad.c' || echo '$(srcdir)/'`src/shared/ad.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) src/shared/$(DEPDIR)/libshared_mainloop_la-ad.Tpo src/shared/$(DEPDIR)/libshared_mainloop_la-ad.Plo @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='src/shared/ad.c' object='src/shared/libshared_mainloop_la-ad.lo' libtool=yes @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_libshared_mainloop_la_CFLAGS) $(CFLAGS) -c -o src/shared/libshared_mainloop_la-ad.lo `test -f 'src/shared/ad.c' || echo '$(srcdir)/'`src/shared/ad.c src/shared/libshared_mainloop_la-att.lo: src/shared/att.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_libshared_mainloop_la_CFLAGS) $(CFLAGS) -MT src/shared/libshared_mainloop_la-att.lo -MD -MP -MF src/shared/$(DEPDIR)/libshared_mainloop_la-att.Tpo -c -o src/shared/libshared_mainloop_la-att.lo `test -f 'src/shared/att.c' || echo '$(srcdir)/'`src/shared/att.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) src/shared/$(DEPDIR)/libshared_mainloop_la-att.Tpo src/shared/$(DEPDIR)/libshared_mainloop_la-att.Plo @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='src/shared/att.c' object='src/shared/libshared_mainloop_la-att.lo' libtool=yes @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_libshared_mainloop_la_CFLAGS) $(CFLAGS) -c -o src/shared/libshared_mainloop_la-att.lo `test -f 'src/shared/att.c' || echo '$(srcdir)/'`src/shared/att.c src/shared/libshared_mainloop_la-gatt-helpers.lo: src/shared/gatt-helpers.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_libshared_mainloop_la_CFLAGS) $(CFLAGS) -MT src/shared/libshared_mainloop_la-gatt-helpers.lo -MD -MP -MF src/shared/$(DEPDIR)/libshared_mainloop_la-gatt-helpers.Tpo -c -o src/shared/libshared_mainloop_la-gatt-helpers.lo `test -f 'src/shared/gatt-helpers.c' || echo '$(srcdir)/'`src/shared/gatt-helpers.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) src/shared/$(DEPDIR)/libshared_mainloop_la-gatt-helpers.Tpo src/shared/$(DEPDIR)/libshared_mainloop_la-gatt-helpers.Plo @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='src/shared/gatt-helpers.c' object='src/shared/libshared_mainloop_la-gatt-helpers.lo' libtool=yes @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_libshared_mainloop_la_CFLAGS) $(CFLAGS) -c -o src/shared/libshared_mainloop_la-gatt-helpers.lo `test -f 'src/shared/gatt-helpers.c' || echo '$(srcdir)/'`src/shared/gatt-helpers.c src/shared/libshared_mainloop_la-gatt-client.lo: src/shared/gatt-client.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_libshared_mainloop_la_CFLAGS) $(CFLAGS) -MT src/shared/libshared_mainloop_la-gatt-client.lo -MD -MP -MF src/shared/$(DEPDIR)/libshared_mainloop_la-gatt-client.Tpo -c -o src/shared/libshared_mainloop_la-gatt-client.lo `test -f 'src/shared/gatt-client.c' || echo '$(srcdir)/'`src/shared/gatt-client.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) src/shared/$(DEPDIR)/libshared_mainloop_la-gatt-client.Tpo src/shared/$(DEPDIR)/libshared_mainloop_la-gatt-client.Plo @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='src/shared/gatt-client.c' object='src/shared/libshared_mainloop_la-gatt-client.lo' libtool=yes @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_libshared_mainloop_la_CFLAGS) $(CFLAGS) -c -o src/shared/libshared_mainloop_la-gatt-client.lo `test -f 'src/shared/gatt-client.c' || echo '$(srcdir)/'`src/shared/gatt-client.c src/shared/libshared_mainloop_la-gatt-server.lo: src/shared/gatt-server.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_libshared_mainloop_la_CFLAGS) $(CFLAGS) -MT src/shared/libshared_mainloop_la-gatt-server.lo -MD -MP -MF src/shared/$(DEPDIR)/libshared_mainloop_la-gatt-server.Tpo -c -o src/shared/libshared_mainloop_la-gatt-server.lo `test -f 'src/shared/gatt-server.c' || echo '$(srcdir)/'`src/shared/gatt-server.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) src/shared/$(DEPDIR)/libshared_mainloop_la-gatt-server.Tpo src/shared/$(DEPDIR)/libshared_mainloop_la-gatt-server.Plo @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='src/shared/gatt-server.c' object='src/shared/libshared_mainloop_la-gatt-server.lo' libtool=yes @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_libshared_mainloop_la_CFLAGS) $(CFLAGS) -c -o src/shared/libshared_mainloop_la-gatt-server.lo `test -f 'src/shared/gatt-server.c' || echo '$(srcdir)/'`src/shared/gatt-server.c src/shared/libshared_mainloop_la-gatt-db.lo: src/shared/gatt-db.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_libshared_mainloop_la_CFLAGS) $(CFLAGS) -MT src/shared/libshared_mainloop_la-gatt-db.lo -MD -MP -MF src/shared/$(DEPDIR)/libshared_mainloop_la-gatt-db.Tpo -c -o src/shared/libshared_mainloop_la-gatt-db.lo `test -f 'src/shared/gatt-db.c' || echo '$(srcdir)/'`src/shared/gatt-db.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) src/shared/$(DEPDIR)/libshared_mainloop_la-gatt-db.Tpo src/shared/$(DEPDIR)/libshared_mainloop_la-gatt-db.Plo @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='src/shared/gatt-db.c' object='src/shared/libshared_mainloop_la-gatt-db.lo' libtool=yes @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_libshared_mainloop_la_CFLAGS) $(CFLAGS) -c -o src/shared/libshared_mainloop_la-gatt-db.lo `test -f 'src/shared/gatt-db.c' || echo '$(srcdir)/'`src/shared/gatt-db.c src/shared/libshared_mainloop_la-gap.lo: src/shared/gap.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_libshared_mainloop_la_CFLAGS) $(CFLAGS) -MT src/shared/libshared_mainloop_la-gap.lo -MD -MP -MF src/shared/$(DEPDIR)/libshared_mainloop_la-gap.Tpo -c -o src/shared/libshared_mainloop_la-gap.lo `test -f 'src/shared/gap.c' || echo '$(srcdir)/'`src/shared/gap.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) src/shared/$(DEPDIR)/libshared_mainloop_la-gap.Tpo src/shared/$(DEPDIR)/libshared_mainloop_la-gap.Plo @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='src/shared/gap.c' object='src/shared/libshared_mainloop_la-gap.lo' libtool=yes @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_libshared_mainloop_la_CFLAGS) $(CFLAGS) -c -o src/shared/libshared_mainloop_la-gap.lo `test -f 'src/shared/gap.c' || echo '$(srcdir)/'`src/shared/gap.c src/shared/libshared_mainloop_la-log.lo: src/shared/log.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_libshared_mainloop_la_CFLAGS) $(CFLAGS) -MT src/shared/libshared_mainloop_la-log.lo -MD -MP -MF src/shared/$(DEPDIR)/libshared_mainloop_la-log.Tpo -c -o src/shared/libshared_mainloop_la-log.lo `test -f 'src/shared/log.c' || echo '$(srcdir)/'`src/shared/log.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) src/shared/$(DEPDIR)/libshared_mainloop_la-log.Tpo src/shared/$(DEPDIR)/libshared_mainloop_la-log.Plo @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='src/shared/log.c' object='src/shared/libshared_mainloop_la-log.lo' libtool=yes @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_libshared_mainloop_la_CFLAGS) $(CFLAGS) -c -o src/shared/libshared_mainloop_la-log.lo `test -f 'src/shared/log.c' || echo '$(srcdir)/'`src/shared/log.c src/shared/libshared_mainloop_la-bap.lo: src/shared/bap.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_libshared_mainloop_la_CFLAGS) $(CFLAGS) -MT src/shared/libshared_mainloop_la-bap.lo -MD -MP -MF src/shared/$(DEPDIR)/libshared_mainloop_la-bap.Tpo -c -o src/shared/libshared_mainloop_la-bap.lo `test -f 'src/shared/bap.c' || echo '$(srcdir)/'`src/shared/bap.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) src/shared/$(DEPDIR)/libshared_mainloop_la-bap.Tpo src/shared/$(DEPDIR)/libshared_mainloop_la-bap.Plo @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='src/shared/bap.c' object='src/shared/libshared_mainloop_la-bap.lo' libtool=yes @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_libshared_mainloop_la_CFLAGS) $(CFLAGS) -c -o src/shared/libshared_mainloop_la-bap.lo `test -f 'src/shared/bap.c' || echo '$(srcdir)/'`src/shared/bap.c src/shared/libshared_mainloop_la-bap-debug.lo: src/shared/bap-debug.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_libshared_mainloop_la_CFLAGS) $(CFLAGS) -MT src/shared/libshared_mainloop_la-bap-debug.lo -MD -MP -MF src/shared/$(DEPDIR)/libshared_mainloop_la-bap-debug.Tpo -c -o src/shared/libshared_mainloop_la-bap-debug.lo `test -f 'src/shared/bap-debug.c' || echo '$(srcdir)/'`src/shared/bap-debug.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) src/shared/$(DEPDIR)/libshared_mainloop_la-bap-debug.Tpo src/shared/$(DEPDIR)/libshared_mainloop_la-bap-debug.Plo @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='src/shared/bap-debug.c' object='src/shared/libshared_mainloop_la-bap-debug.lo' libtool=yes @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_libshared_mainloop_la_CFLAGS) $(CFLAGS) -c -o src/shared/libshared_mainloop_la-bap-debug.lo `test -f 'src/shared/bap-debug.c' || echo '$(srcdir)/'`src/shared/bap-debug.c src/shared/libshared_mainloop_la-mcp.lo: src/shared/mcp.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_libshared_mainloop_la_CFLAGS) $(CFLAGS) -MT src/shared/libshared_mainloop_la-mcp.lo -MD -MP -MF src/shared/$(DEPDIR)/libshared_mainloop_la-mcp.Tpo -c -o src/shared/libshared_mainloop_la-mcp.lo `test -f 'src/shared/mcp.c' || echo '$(srcdir)/'`src/shared/mcp.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) src/shared/$(DEPDIR)/libshared_mainloop_la-mcp.Tpo src/shared/$(DEPDIR)/libshared_mainloop_la-mcp.Plo @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='src/shared/mcp.c' object='src/shared/libshared_mainloop_la-mcp.lo' libtool=yes @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_libshared_mainloop_la_CFLAGS) $(CFLAGS) -c -o src/shared/libshared_mainloop_la-mcp.lo `test -f 'src/shared/mcp.c' || echo '$(srcdir)/'`src/shared/mcp.c src/shared/libshared_mainloop_la-vcp.lo: src/shared/vcp.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_libshared_mainloop_la_CFLAGS) $(CFLAGS) -MT src/shared/libshared_mainloop_la-vcp.lo -MD -MP -MF src/shared/$(DEPDIR)/libshared_mainloop_la-vcp.Tpo -c -o src/shared/libshared_mainloop_la-vcp.lo `test -f 'src/shared/vcp.c' || echo '$(srcdir)/'`src/shared/vcp.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) src/shared/$(DEPDIR)/libshared_mainloop_la-vcp.Tpo src/shared/$(DEPDIR)/libshared_mainloop_la-vcp.Plo @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='src/shared/vcp.c' object='src/shared/libshared_mainloop_la-vcp.lo' libtool=yes @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_libshared_mainloop_la_CFLAGS) $(CFLAGS) -c -o src/shared/libshared_mainloop_la-vcp.lo `test -f 'src/shared/vcp.c' || echo '$(srcdir)/'`src/shared/vcp.c src/shared/libshared_mainloop_la-micp.lo: src/shared/micp.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_libshared_mainloop_la_CFLAGS) $(CFLAGS) -MT src/shared/libshared_mainloop_la-micp.lo -MD -MP -MF src/shared/$(DEPDIR)/libshared_mainloop_la-micp.Tpo -c -o src/shared/libshared_mainloop_la-micp.lo `test -f 'src/shared/micp.c' || echo '$(srcdir)/'`src/shared/micp.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) src/shared/$(DEPDIR)/libshared_mainloop_la-micp.Tpo src/shared/$(DEPDIR)/libshared_mainloop_la-micp.Plo @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='src/shared/micp.c' object='src/shared/libshared_mainloop_la-micp.lo' libtool=yes @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_libshared_mainloop_la_CFLAGS) $(CFLAGS) -c -o src/shared/libshared_mainloop_la-micp.lo `test -f 'src/shared/micp.c' || echo '$(srcdir)/'`src/shared/micp.c src/shared/libshared_mainloop_la-csip.lo: src/shared/csip.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_libshared_mainloop_la_CFLAGS) $(CFLAGS) -MT src/shared/libshared_mainloop_la-csip.lo -MD -MP -MF src/shared/$(DEPDIR)/libshared_mainloop_la-csip.Tpo -c -o src/shared/libshared_mainloop_la-csip.lo `test -f 'src/shared/csip.c' || echo '$(srcdir)/'`src/shared/csip.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) src/shared/$(DEPDIR)/libshared_mainloop_la-csip.Tpo src/shared/$(DEPDIR)/libshared_mainloop_la-csip.Plo @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='src/shared/csip.c' object='src/shared/libshared_mainloop_la-csip.lo' libtool=yes @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_libshared_mainloop_la_CFLAGS) $(CFLAGS) -c -o src/shared/libshared_mainloop_la-csip.lo `test -f 'src/shared/csip.c' || echo '$(srcdir)/'`src/shared/csip.c src/shared/libshared_mainloop_la-bass.lo: src/shared/bass.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_libshared_mainloop_la_CFLAGS) $(CFLAGS) -MT src/shared/libshared_mainloop_la-bass.lo -MD -MP -MF src/shared/$(DEPDIR)/libshared_mainloop_la-bass.Tpo -c -o src/shared/libshared_mainloop_la-bass.lo `test -f 'src/shared/bass.c' || echo '$(srcdir)/'`src/shared/bass.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) src/shared/$(DEPDIR)/libshared_mainloop_la-bass.Tpo src/shared/$(DEPDIR)/libshared_mainloop_la-bass.Plo @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='src/shared/bass.c' object='src/shared/libshared_mainloop_la-bass.lo' libtool=yes @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_libshared_mainloop_la_CFLAGS) $(CFLAGS) -c -o src/shared/libshared_mainloop_la-bass.lo `test -f 'src/shared/bass.c' || echo '$(srcdir)/'`src/shared/bass.c src/shared/libshared_mainloop_la-ccp.lo: src/shared/ccp.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_libshared_mainloop_la_CFLAGS) $(CFLAGS) -MT src/shared/libshared_mainloop_la-ccp.lo -MD -MP -MF src/shared/$(DEPDIR)/libshared_mainloop_la-ccp.Tpo -c -o src/shared/libshared_mainloop_la-ccp.lo `test -f 'src/shared/ccp.c' || echo '$(srcdir)/'`src/shared/ccp.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) src/shared/$(DEPDIR)/libshared_mainloop_la-ccp.Tpo src/shared/$(DEPDIR)/libshared_mainloop_la-ccp.Plo @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='src/shared/ccp.c' object='src/shared/libshared_mainloop_la-ccp.lo' libtool=yes @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_libshared_mainloop_la_CFLAGS) $(CFLAGS) -c -o src/shared/libshared_mainloop_la-ccp.lo `test -f 'src/shared/ccp.c' || echo '$(srcdir)/'`src/shared/ccp.c src/shared/libshared_mainloop_la-asha.lo: src/shared/asha.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_libshared_mainloop_la_CFLAGS) $(CFLAGS) -MT src/shared/libshared_mainloop_la-asha.lo -MD -MP -MF src/shared/$(DEPDIR)/libshared_mainloop_la-asha.Tpo -c -o src/shared/libshared_mainloop_la-asha.lo `test -f 'src/shared/asha.c' || echo '$(srcdir)/'`src/shared/asha.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) src/shared/$(DEPDIR)/libshared_mainloop_la-asha.Tpo src/shared/$(DEPDIR)/libshared_mainloop_la-asha.Plo @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='src/shared/asha.c' object='src/shared/libshared_mainloop_la-asha.lo' libtool=yes @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_libshared_mainloop_la_CFLAGS) $(CFLAGS) -c -o src/shared/libshared_mainloop_la-asha.lo `test -f 'src/shared/asha.c' || echo '$(srcdir)/'`src/shared/asha.c src/shared/libshared_mainloop_la-shell.lo: src/shared/shell.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_libshared_mainloop_la_CFLAGS) $(CFLAGS) -MT src/shared/libshared_mainloop_la-shell.lo -MD -MP -MF src/shared/$(DEPDIR)/libshared_mainloop_la-shell.Tpo -c -o src/shared/libshared_mainloop_la-shell.lo `test -f 'src/shared/shell.c' || echo '$(srcdir)/'`src/shared/shell.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) src/shared/$(DEPDIR)/libshared_mainloop_la-shell.Tpo src/shared/$(DEPDIR)/libshared_mainloop_la-shell.Plo @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='src/shared/shell.c' object='src/shared/libshared_mainloop_la-shell.lo' libtool=yes @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_libshared_mainloop_la_CFLAGS) $(CFLAGS) -c -o src/shared/libshared_mainloop_la-shell.lo `test -f 'src/shared/shell.c' || echo '$(srcdir)/'`src/shared/shell.c src/shared/libshared_mainloop_la-io-mainloop.lo: src/shared/io-mainloop.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_libshared_mainloop_la_CFLAGS) $(CFLAGS) -MT src/shared/libshared_mainloop_la-io-mainloop.lo -MD -MP -MF src/shared/$(DEPDIR)/libshared_mainloop_la-io-mainloop.Tpo -c -o src/shared/libshared_mainloop_la-io-mainloop.lo `test -f 'src/shared/io-mainloop.c' || echo '$(srcdir)/'`src/shared/io-mainloop.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) src/shared/$(DEPDIR)/libshared_mainloop_la-io-mainloop.Tpo src/shared/$(DEPDIR)/libshared_mainloop_la-io-mainloop.Plo @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='src/shared/io-mainloop.c' object='src/shared/libshared_mainloop_la-io-mainloop.lo' libtool=yes @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_libshared_mainloop_la_CFLAGS) $(CFLAGS) -c -o src/shared/libshared_mainloop_la-io-mainloop.lo `test -f 'src/shared/io-mainloop.c' || echo '$(srcdir)/'`src/shared/io-mainloop.c src/shared/libshared_mainloop_la-timeout-mainloop.lo: src/shared/timeout-mainloop.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_libshared_mainloop_la_CFLAGS) $(CFLAGS) -MT src/shared/libshared_mainloop_la-timeout-mainloop.lo -MD -MP -MF src/shared/$(DEPDIR)/libshared_mainloop_la-timeout-mainloop.Tpo -c -o src/shared/libshared_mainloop_la-timeout-mainloop.lo `test -f 'src/shared/timeout-mainloop.c' || echo '$(srcdir)/'`src/shared/timeout-mainloop.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) src/shared/$(DEPDIR)/libshared_mainloop_la-timeout-mainloop.Tpo src/shared/$(DEPDIR)/libshared_mainloop_la-timeout-mainloop.Plo @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='src/shared/timeout-mainloop.c' object='src/shared/libshared_mainloop_la-timeout-mainloop.lo' libtool=yes @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_libshared_mainloop_la_CFLAGS) $(CFLAGS) -c -o src/shared/libshared_mainloop_la-timeout-mainloop.lo `test -f 'src/shared/timeout-mainloop.c' || echo '$(srcdir)/'`src/shared/timeout-mainloop.c src/shared/libshared_mainloop_la-mainloop.lo: src/shared/mainloop.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_libshared_mainloop_la_CFLAGS) $(CFLAGS) -MT src/shared/libshared_mainloop_la-mainloop.lo -MD -MP -MF src/shared/$(DEPDIR)/libshared_mainloop_la-mainloop.Tpo -c -o src/shared/libshared_mainloop_la-mainloop.lo `test -f 'src/shared/mainloop.c' || echo '$(srcdir)/'`src/shared/mainloop.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) src/shared/$(DEPDIR)/libshared_mainloop_la-mainloop.Tpo src/shared/$(DEPDIR)/libshared_mainloop_la-mainloop.Plo @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='src/shared/mainloop.c' object='src/shared/libshared_mainloop_la-mainloop.lo' libtool=yes @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_libshared_mainloop_la_CFLAGS) $(CFLAGS) -c -o src/shared/libshared_mainloop_la-mainloop.lo `test -f 'src/shared/mainloop.c' || echo '$(srcdir)/'`src/shared/mainloop.c src/shared/libshared_mainloop_la-mainloop-notify.lo: src/shared/mainloop-notify.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_libshared_mainloop_la_CFLAGS) $(CFLAGS) -MT src/shared/libshared_mainloop_la-mainloop-notify.lo -MD -MP -MF src/shared/$(DEPDIR)/libshared_mainloop_la-mainloop-notify.Tpo -c -o src/shared/libshared_mainloop_la-mainloop-notify.lo `test -f 'src/shared/mainloop-notify.c' || echo '$(srcdir)/'`src/shared/mainloop-notify.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) src/shared/$(DEPDIR)/libshared_mainloop_la-mainloop-notify.Tpo src/shared/$(DEPDIR)/libshared_mainloop_la-mainloop-notify.Plo @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='src/shared/mainloop-notify.c' object='src/shared/libshared_mainloop_la-mainloop-notify.lo' libtool=yes @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_libshared_mainloop_la_CFLAGS) $(CFLAGS) -c -o src/shared/libshared_mainloop_la-mainloop-notify.lo `test -f 'src/shared/mainloop-notify.c' || echo '$(srcdir)/'`src/shared/mainloop-notify.c emulator/android_android_tester-hciemu.o: emulator/hciemu.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(android_android_tester_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT emulator/android_android_tester-hciemu.o -MD -MP -MF emulator/$(DEPDIR)/android_android_tester-hciemu.Tpo -c -o emulator/android_android_tester-hciemu.o `test -f 'emulator/hciemu.c' || echo '$(srcdir)/'`emulator/hciemu.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) emulator/$(DEPDIR)/android_android_tester-hciemu.Tpo emulator/$(DEPDIR)/android_android_tester-hciemu.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='emulator/hciemu.c' object='emulator/android_android_tester-hciemu.o' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(android_android_tester_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o emulator/android_android_tester-hciemu.o `test -f 'emulator/hciemu.c' || echo '$(srcdir)/'`emulator/hciemu.c emulator/android_android_tester-hciemu.obj: emulator/hciemu.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(android_android_tester_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT emulator/android_android_tester-hciemu.obj -MD -MP -MF emulator/$(DEPDIR)/android_android_tester-hciemu.Tpo -c -o emulator/android_android_tester-hciemu.obj `if test -f 'emulator/hciemu.c'; then $(CYGPATH_W) 'emulator/hciemu.c'; else $(CYGPATH_W) '$(srcdir)/emulator/hciemu.c'; fi` @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) emulator/$(DEPDIR)/android_android_tester-hciemu.Tpo emulator/$(DEPDIR)/android_android_tester-hciemu.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='emulator/hciemu.c' object='emulator/android_android_tester-hciemu.obj' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(android_android_tester_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o emulator/android_android_tester-hciemu.obj `if test -f 'emulator/hciemu.c'; then $(CYGPATH_W) 'emulator/hciemu.c'; else $(CYGPATH_W) '$(srcdir)/emulator/hciemu.c'; fi` emulator/android_android_tester-vhci.o: emulator/vhci.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(android_android_tester_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT emulator/android_android_tester-vhci.o -MD -MP -MF emulator/$(DEPDIR)/android_android_tester-vhci.Tpo -c -o emulator/android_android_tester-vhci.o `test -f 'emulator/vhci.c' || echo '$(srcdir)/'`emulator/vhci.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) emulator/$(DEPDIR)/android_android_tester-vhci.Tpo emulator/$(DEPDIR)/android_android_tester-vhci.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='emulator/vhci.c' object='emulator/android_android_tester-vhci.o' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(android_android_tester_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o emulator/android_android_tester-vhci.o `test -f 'emulator/vhci.c' || echo '$(srcdir)/'`emulator/vhci.c emulator/android_android_tester-vhci.obj: emulator/vhci.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(android_android_tester_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT emulator/android_android_tester-vhci.obj -MD -MP -MF emulator/$(DEPDIR)/android_android_tester-vhci.Tpo -c -o emulator/android_android_tester-vhci.obj `if test -f 'emulator/vhci.c'; then $(CYGPATH_W) 'emulator/vhci.c'; else $(CYGPATH_W) '$(srcdir)/emulator/vhci.c'; fi` @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) emulator/$(DEPDIR)/android_android_tester-vhci.Tpo emulator/$(DEPDIR)/android_android_tester-vhci.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='emulator/vhci.c' object='emulator/android_android_tester-vhci.obj' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(android_android_tester_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o emulator/android_android_tester-vhci.obj `if test -f 'emulator/vhci.c'; then $(CYGPATH_W) 'emulator/vhci.c'; else $(CYGPATH_W) '$(srcdir)/emulator/vhci.c'; fi` emulator/android_android_tester-btdev.o: emulator/btdev.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(android_android_tester_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT emulator/android_android_tester-btdev.o -MD -MP -MF emulator/$(DEPDIR)/android_android_tester-btdev.Tpo -c -o emulator/android_android_tester-btdev.o `test -f 'emulator/btdev.c' || echo '$(srcdir)/'`emulator/btdev.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) emulator/$(DEPDIR)/android_android_tester-btdev.Tpo emulator/$(DEPDIR)/android_android_tester-btdev.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='emulator/btdev.c' object='emulator/android_android_tester-btdev.o' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(android_android_tester_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o emulator/android_android_tester-btdev.o `test -f 'emulator/btdev.c' || echo '$(srcdir)/'`emulator/btdev.c emulator/android_android_tester-btdev.obj: emulator/btdev.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(android_android_tester_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT emulator/android_android_tester-btdev.obj -MD -MP -MF emulator/$(DEPDIR)/android_android_tester-btdev.Tpo -c -o emulator/android_android_tester-btdev.obj `if test -f 'emulator/btdev.c'; then $(CYGPATH_W) 'emulator/btdev.c'; else $(CYGPATH_W) '$(srcdir)/emulator/btdev.c'; fi` @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) emulator/$(DEPDIR)/android_android_tester-btdev.Tpo emulator/$(DEPDIR)/android_android_tester-btdev.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='emulator/btdev.c' object='emulator/android_android_tester-btdev.obj' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(android_android_tester_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o emulator/android_android_tester-btdev.obj `if test -f 'emulator/btdev.c'; then $(CYGPATH_W) 'emulator/btdev.c'; else $(CYGPATH_W) '$(srcdir)/emulator/btdev.c'; fi` emulator/android_android_tester-bthost.o: emulator/bthost.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(android_android_tester_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT emulator/android_android_tester-bthost.o -MD -MP -MF emulator/$(DEPDIR)/android_android_tester-bthost.Tpo -c -o emulator/android_android_tester-bthost.o `test -f 'emulator/bthost.c' || echo '$(srcdir)/'`emulator/bthost.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) emulator/$(DEPDIR)/android_android_tester-bthost.Tpo emulator/$(DEPDIR)/android_android_tester-bthost.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='emulator/bthost.c' object='emulator/android_android_tester-bthost.o' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(android_android_tester_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o emulator/android_android_tester-bthost.o `test -f 'emulator/bthost.c' || echo '$(srcdir)/'`emulator/bthost.c emulator/android_android_tester-bthost.obj: emulator/bthost.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(android_android_tester_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT emulator/android_android_tester-bthost.obj -MD -MP -MF emulator/$(DEPDIR)/android_android_tester-bthost.Tpo -c -o emulator/android_android_tester-bthost.obj `if test -f 'emulator/bthost.c'; then $(CYGPATH_W) 'emulator/bthost.c'; else $(CYGPATH_W) '$(srcdir)/emulator/bthost.c'; fi` @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) emulator/$(DEPDIR)/android_android_tester-bthost.Tpo emulator/$(DEPDIR)/android_android_tester-bthost.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='emulator/bthost.c' object='emulator/android_android_tester-bthost.obj' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(android_android_tester_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o emulator/android_android_tester-bthost.obj `if test -f 'emulator/bthost.c'; then $(CYGPATH_W) 'emulator/bthost.c'; else $(CYGPATH_W) '$(srcdir)/emulator/bthost.c'; fi` emulator/android_android_tester-smp.o: emulator/smp.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(android_android_tester_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT emulator/android_android_tester-smp.o -MD -MP -MF emulator/$(DEPDIR)/android_android_tester-smp.Tpo -c -o emulator/android_android_tester-smp.o `test -f 'emulator/smp.c' || echo '$(srcdir)/'`emulator/smp.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) emulator/$(DEPDIR)/android_android_tester-smp.Tpo emulator/$(DEPDIR)/android_android_tester-smp.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='emulator/smp.c' object='emulator/android_android_tester-smp.o' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(android_android_tester_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o emulator/android_android_tester-smp.o `test -f 'emulator/smp.c' || echo '$(srcdir)/'`emulator/smp.c emulator/android_android_tester-smp.obj: emulator/smp.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(android_android_tester_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT emulator/android_android_tester-smp.obj -MD -MP -MF emulator/$(DEPDIR)/android_android_tester-smp.Tpo -c -o emulator/android_android_tester-smp.obj `if test -f 'emulator/smp.c'; then $(CYGPATH_W) 'emulator/smp.c'; else $(CYGPATH_W) '$(srcdir)/emulator/smp.c'; fi` @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) emulator/$(DEPDIR)/android_android_tester-smp.Tpo emulator/$(DEPDIR)/android_android_tester-smp.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='emulator/smp.c' object='emulator/android_android_tester-smp.obj' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(android_android_tester_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o emulator/android_android_tester-smp.obj `if test -f 'emulator/smp.c'; then $(CYGPATH_W) 'emulator/smp.c'; else $(CYGPATH_W) '$(srcdir)/emulator/smp.c'; fi` android/hardware/android_tester-hardware.o: android/hardware/hardware.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(android_android_tester_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT android/hardware/android_tester-hardware.o -MD -MP -MF android/hardware/$(DEPDIR)/android_tester-hardware.Tpo -c -o android/hardware/android_tester-hardware.o `test -f 'android/hardware/hardware.c' || echo '$(srcdir)/'`android/hardware/hardware.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) android/hardware/$(DEPDIR)/android_tester-hardware.Tpo android/hardware/$(DEPDIR)/android_tester-hardware.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='android/hardware/hardware.c' object='android/hardware/android_tester-hardware.o' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(android_android_tester_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o android/hardware/android_tester-hardware.o `test -f 'android/hardware/hardware.c' || echo '$(srcdir)/'`android/hardware/hardware.c android/hardware/android_tester-hardware.obj: android/hardware/hardware.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(android_android_tester_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT android/hardware/android_tester-hardware.obj -MD -MP -MF android/hardware/$(DEPDIR)/android_tester-hardware.Tpo -c -o android/hardware/android_tester-hardware.obj `if test -f 'android/hardware/hardware.c'; then $(CYGPATH_W) 'android/hardware/hardware.c'; else $(CYGPATH_W) '$(srcdir)/android/hardware/hardware.c'; fi` @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) android/hardware/$(DEPDIR)/android_tester-hardware.Tpo android/hardware/$(DEPDIR)/android_tester-hardware.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='android/hardware/hardware.c' object='android/hardware/android_tester-hardware.obj' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(android_android_tester_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o android/hardware/android_tester-hardware.obj `if test -f 'android/hardware/hardware.c'; then $(CYGPATH_W) 'android/hardware/hardware.c'; else $(CYGPATH_W) '$(srcdir)/android/hardware/hardware.c'; fi` android/android_tester-tester-bluetooth.o: android/tester-bluetooth.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(android_android_tester_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT android/android_tester-tester-bluetooth.o -MD -MP -MF android/$(DEPDIR)/android_tester-tester-bluetooth.Tpo -c -o android/android_tester-tester-bluetooth.o `test -f 'android/tester-bluetooth.c' || echo '$(srcdir)/'`android/tester-bluetooth.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) android/$(DEPDIR)/android_tester-tester-bluetooth.Tpo android/$(DEPDIR)/android_tester-tester-bluetooth.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='android/tester-bluetooth.c' object='android/android_tester-tester-bluetooth.o' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(android_android_tester_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o android/android_tester-tester-bluetooth.o `test -f 'android/tester-bluetooth.c' || echo '$(srcdir)/'`android/tester-bluetooth.c android/android_tester-tester-bluetooth.obj: android/tester-bluetooth.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(android_android_tester_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT android/android_tester-tester-bluetooth.obj -MD -MP -MF android/$(DEPDIR)/android_tester-tester-bluetooth.Tpo -c -o android/android_tester-tester-bluetooth.obj `if test -f 'android/tester-bluetooth.c'; then $(CYGPATH_W) 'android/tester-bluetooth.c'; else $(CYGPATH_W) '$(srcdir)/android/tester-bluetooth.c'; fi` @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) android/$(DEPDIR)/android_tester-tester-bluetooth.Tpo android/$(DEPDIR)/android_tester-tester-bluetooth.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='android/tester-bluetooth.c' object='android/android_tester-tester-bluetooth.obj' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(android_android_tester_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o android/android_tester-tester-bluetooth.obj `if test -f 'android/tester-bluetooth.c'; then $(CYGPATH_W) 'android/tester-bluetooth.c'; else $(CYGPATH_W) '$(srcdir)/android/tester-bluetooth.c'; fi` android/android_tester-tester-socket.o: android/tester-socket.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(android_android_tester_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT android/android_tester-tester-socket.o -MD -MP -MF android/$(DEPDIR)/android_tester-tester-socket.Tpo -c -o android/android_tester-tester-socket.o `test -f 'android/tester-socket.c' || echo '$(srcdir)/'`android/tester-socket.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) android/$(DEPDIR)/android_tester-tester-socket.Tpo android/$(DEPDIR)/android_tester-tester-socket.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='android/tester-socket.c' object='android/android_tester-tester-socket.o' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(android_android_tester_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o android/android_tester-tester-socket.o `test -f 'android/tester-socket.c' || echo '$(srcdir)/'`android/tester-socket.c android/android_tester-tester-socket.obj: android/tester-socket.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(android_android_tester_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT android/android_tester-tester-socket.obj -MD -MP -MF android/$(DEPDIR)/android_tester-tester-socket.Tpo -c -o android/android_tester-tester-socket.obj `if test -f 'android/tester-socket.c'; then $(CYGPATH_W) 'android/tester-socket.c'; else $(CYGPATH_W) '$(srcdir)/android/tester-socket.c'; fi` @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) android/$(DEPDIR)/android_tester-tester-socket.Tpo android/$(DEPDIR)/android_tester-tester-socket.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='android/tester-socket.c' object='android/android_tester-tester-socket.obj' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(android_android_tester_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o android/android_tester-tester-socket.obj `if test -f 'android/tester-socket.c'; then $(CYGPATH_W) 'android/tester-socket.c'; else $(CYGPATH_W) '$(srcdir)/android/tester-socket.c'; fi` android/android_tester-tester-hidhost.o: android/tester-hidhost.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(android_android_tester_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT android/android_tester-tester-hidhost.o -MD -MP -MF android/$(DEPDIR)/android_tester-tester-hidhost.Tpo -c -o android/android_tester-tester-hidhost.o `test -f 'android/tester-hidhost.c' || echo '$(srcdir)/'`android/tester-hidhost.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) android/$(DEPDIR)/android_tester-tester-hidhost.Tpo android/$(DEPDIR)/android_tester-tester-hidhost.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='android/tester-hidhost.c' object='android/android_tester-tester-hidhost.o' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(android_android_tester_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o android/android_tester-tester-hidhost.o `test -f 'android/tester-hidhost.c' || echo '$(srcdir)/'`android/tester-hidhost.c android/android_tester-tester-hidhost.obj: android/tester-hidhost.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(android_android_tester_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT android/android_tester-tester-hidhost.obj -MD -MP -MF android/$(DEPDIR)/android_tester-tester-hidhost.Tpo -c -o android/android_tester-tester-hidhost.obj `if test -f 'android/tester-hidhost.c'; then $(CYGPATH_W) 'android/tester-hidhost.c'; else $(CYGPATH_W) '$(srcdir)/android/tester-hidhost.c'; fi` @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) android/$(DEPDIR)/android_tester-tester-hidhost.Tpo android/$(DEPDIR)/android_tester-tester-hidhost.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='android/tester-hidhost.c' object='android/android_tester-tester-hidhost.obj' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(android_android_tester_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o android/android_tester-tester-hidhost.obj `if test -f 'android/tester-hidhost.c'; then $(CYGPATH_W) 'android/tester-hidhost.c'; else $(CYGPATH_W) '$(srcdir)/android/tester-hidhost.c'; fi` android/android_tester-tester-pan.o: android/tester-pan.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(android_android_tester_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT android/android_tester-tester-pan.o -MD -MP -MF android/$(DEPDIR)/android_tester-tester-pan.Tpo -c -o android/android_tester-tester-pan.o `test -f 'android/tester-pan.c' || echo '$(srcdir)/'`android/tester-pan.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) android/$(DEPDIR)/android_tester-tester-pan.Tpo android/$(DEPDIR)/android_tester-tester-pan.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='android/tester-pan.c' object='android/android_tester-tester-pan.o' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(android_android_tester_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o android/android_tester-tester-pan.o `test -f 'android/tester-pan.c' || echo '$(srcdir)/'`android/tester-pan.c android/android_tester-tester-pan.obj: android/tester-pan.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(android_android_tester_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT android/android_tester-tester-pan.obj -MD -MP -MF android/$(DEPDIR)/android_tester-tester-pan.Tpo -c -o android/android_tester-tester-pan.obj `if test -f 'android/tester-pan.c'; then $(CYGPATH_W) 'android/tester-pan.c'; else $(CYGPATH_W) '$(srcdir)/android/tester-pan.c'; fi` @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) android/$(DEPDIR)/android_tester-tester-pan.Tpo android/$(DEPDIR)/android_tester-tester-pan.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='android/tester-pan.c' object='android/android_tester-tester-pan.obj' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(android_android_tester_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o android/android_tester-tester-pan.obj `if test -f 'android/tester-pan.c'; then $(CYGPATH_W) 'android/tester-pan.c'; else $(CYGPATH_W) '$(srcdir)/android/tester-pan.c'; fi` android/android_tester-tester-hdp.o: android/tester-hdp.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(android_android_tester_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT android/android_tester-tester-hdp.o -MD -MP -MF android/$(DEPDIR)/android_tester-tester-hdp.Tpo -c -o android/android_tester-tester-hdp.o `test -f 'android/tester-hdp.c' || echo '$(srcdir)/'`android/tester-hdp.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) android/$(DEPDIR)/android_tester-tester-hdp.Tpo android/$(DEPDIR)/android_tester-tester-hdp.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='android/tester-hdp.c' object='android/android_tester-tester-hdp.o' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(android_android_tester_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o android/android_tester-tester-hdp.o `test -f 'android/tester-hdp.c' || echo '$(srcdir)/'`android/tester-hdp.c android/android_tester-tester-hdp.obj: android/tester-hdp.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(android_android_tester_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT android/android_tester-tester-hdp.obj -MD -MP -MF android/$(DEPDIR)/android_tester-tester-hdp.Tpo -c -o android/android_tester-tester-hdp.obj `if test -f 'android/tester-hdp.c'; then $(CYGPATH_W) 'android/tester-hdp.c'; else $(CYGPATH_W) '$(srcdir)/android/tester-hdp.c'; fi` @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) android/$(DEPDIR)/android_tester-tester-hdp.Tpo android/$(DEPDIR)/android_tester-tester-hdp.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='android/tester-hdp.c' object='android/android_tester-tester-hdp.obj' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(android_android_tester_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o android/android_tester-tester-hdp.obj `if test -f 'android/tester-hdp.c'; then $(CYGPATH_W) 'android/tester-hdp.c'; else $(CYGPATH_W) '$(srcdir)/android/tester-hdp.c'; fi` android/android_tester-tester-a2dp.o: android/tester-a2dp.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(android_android_tester_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT android/android_tester-tester-a2dp.o -MD -MP -MF android/$(DEPDIR)/android_tester-tester-a2dp.Tpo -c -o android/android_tester-tester-a2dp.o `test -f 'android/tester-a2dp.c' || echo '$(srcdir)/'`android/tester-a2dp.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) android/$(DEPDIR)/android_tester-tester-a2dp.Tpo android/$(DEPDIR)/android_tester-tester-a2dp.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='android/tester-a2dp.c' object='android/android_tester-tester-a2dp.o' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(android_android_tester_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o android/android_tester-tester-a2dp.o `test -f 'android/tester-a2dp.c' || echo '$(srcdir)/'`android/tester-a2dp.c android/android_tester-tester-a2dp.obj: android/tester-a2dp.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(android_android_tester_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT android/android_tester-tester-a2dp.obj -MD -MP -MF android/$(DEPDIR)/android_tester-tester-a2dp.Tpo -c -o android/android_tester-tester-a2dp.obj `if test -f 'android/tester-a2dp.c'; then $(CYGPATH_W) 'android/tester-a2dp.c'; else $(CYGPATH_W) '$(srcdir)/android/tester-a2dp.c'; fi` @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) android/$(DEPDIR)/android_tester-tester-a2dp.Tpo android/$(DEPDIR)/android_tester-tester-a2dp.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='android/tester-a2dp.c' object='android/android_tester-tester-a2dp.obj' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(android_android_tester_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o android/android_tester-tester-a2dp.obj `if test -f 'android/tester-a2dp.c'; then $(CYGPATH_W) 'android/tester-a2dp.c'; else $(CYGPATH_W) '$(srcdir)/android/tester-a2dp.c'; fi` android/android_tester-tester-avrcp.o: android/tester-avrcp.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(android_android_tester_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT android/android_tester-tester-avrcp.o -MD -MP -MF android/$(DEPDIR)/android_tester-tester-avrcp.Tpo -c -o android/android_tester-tester-avrcp.o `test -f 'android/tester-avrcp.c' || echo '$(srcdir)/'`android/tester-avrcp.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) android/$(DEPDIR)/android_tester-tester-avrcp.Tpo android/$(DEPDIR)/android_tester-tester-avrcp.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='android/tester-avrcp.c' object='android/android_tester-tester-avrcp.o' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(android_android_tester_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o android/android_tester-tester-avrcp.o `test -f 'android/tester-avrcp.c' || echo '$(srcdir)/'`android/tester-avrcp.c android/android_tester-tester-avrcp.obj: android/tester-avrcp.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(android_android_tester_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT android/android_tester-tester-avrcp.obj -MD -MP -MF android/$(DEPDIR)/android_tester-tester-avrcp.Tpo -c -o android/android_tester-tester-avrcp.obj `if test -f 'android/tester-avrcp.c'; then $(CYGPATH_W) 'android/tester-avrcp.c'; else $(CYGPATH_W) '$(srcdir)/android/tester-avrcp.c'; fi` @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) android/$(DEPDIR)/android_tester-tester-avrcp.Tpo android/$(DEPDIR)/android_tester-tester-avrcp.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='android/tester-avrcp.c' object='android/android_tester-tester-avrcp.obj' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(android_android_tester_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o android/android_tester-tester-avrcp.obj `if test -f 'android/tester-avrcp.c'; then $(CYGPATH_W) 'android/tester-avrcp.c'; else $(CYGPATH_W) '$(srcdir)/android/tester-avrcp.c'; fi` android/android_tester-tester-gatt.o: android/tester-gatt.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(android_android_tester_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT android/android_tester-tester-gatt.o -MD -MP -MF android/$(DEPDIR)/android_tester-tester-gatt.Tpo -c -o android/android_tester-tester-gatt.o `test -f 'android/tester-gatt.c' || echo '$(srcdir)/'`android/tester-gatt.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) android/$(DEPDIR)/android_tester-tester-gatt.Tpo android/$(DEPDIR)/android_tester-tester-gatt.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='android/tester-gatt.c' object='android/android_tester-tester-gatt.o' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(android_android_tester_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o android/android_tester-tester-gatt.o `test -f 'android/tester-gatt.c' || echo '$(srcdir)/'`android/tester-gatt.c android/android_tester-tester-gatt.obj: android/tester-gatt.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(android_android_tester_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT android/android_tester-tester-gatt.obj -MD -MP -MF android/$(DEPDIR)/android_tester-tester-gatt.Tpo -c -o android/android_tester-tester-gatt.obj `if test -f 'android/tester-gatt.c'; then $(CYGPATH_W) 'android/tester-gatt.c'; else $(CYGPATH_W) '$(srcdir)/android/tester-gatt.c'; fi` @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) android/$(DEPDIR)/android_tester-tester-gatt.Tpo android/$(DEPDIR)/android_tester-tester-gatt.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='android/tester-gatt.c' object='android/android_tester-tester-gatt.obj' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(android_android_tester_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o android/android_tester-tester-gatt.obj `if test -f 'android/tester-gatt.c'; then $(CYGPATH_W) 'android/tester-gatt.c'; else $(CYGPATH_W) '$(srcdir)/android/tester-gatt.c'; fi` android/android_tester-tester-map-client.o: android/tester-map-client.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(android_android_tester_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT android/android_tester-tester-map-client.o -MD -MP -MF android/$(DEPDIR)/android_tester-tester-map-client.Tpo -c -o android/android_tester-tester-map-client.o `test -f 'android/tester-map-client.c' || echo '$(srcdir)/'`android/tester-map-client.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) android/$(DEPDIR)/android_tester-tester-map-client.Tpo android/$(DEPDIR)/android_tester-tester-map-client.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='android/tester-map-client.c' object='android/android_tester-tester-map-client.o' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(android_android_tester_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o android/android_tester-tester-map-client.o `test -f 'android/tester-map-client.c' || echo '$(srcdir)/'`android/tester-map-client.c android/android_tester-tester-map-client.obj: android/tester-map-client.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(android_android_tester_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT android/android_tester-tester-map-client.obj -MD -MP -MF android/$(DEPDIR)/android_tester-tester-map-client.Tpo -c -o android/android_tester-tester-map-client.obj `if test -f 'android/tester-map-client.c'; then $(CYGPATH_W) 'android/tester-map-client.c'; else $(CYGPATH_W) '$(srcdir)/android/tester-map-client.c'; fi` @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) android/$(DEPDIR)/android_tester-tester-map-client.Tpo android/$(DEPDIR)/android_tester-tester-map-client.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='android/tester-map-client.c' object='android/android_tester-tester-map-client.obj' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(android_android_tester_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o android/android_tester-tester-map-client.obj `if test -f 'android/tester-map-client.c'; then $(CYGPATH_W) 'android/tester-map-client.c'; else $(CYGPATH_W) '$(srcdir)/android/tester-map-client.c'; fi` android/android_tester-tester-main.o: android/tester-main.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(android_android_tester_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT android/android_tester-tester-main.o -MD -MP -MF android/$(DEPDIR)/android_tester-tester-main.Tpo -c -o android/android_tester-tester-main.o `test -f 'android/tester-main.c' || echo '$(srcdir)/'`android/tester-main.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) android/$(DEPDIR)/android_tester-tester-main.Tpo android/$(DEPDIR)/android_tester-tester-main.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='android/tester-main.c' object='android/android_tester-tester-main.o' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(android_android_tester_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o android/android_tester-tester-main.o `test -f 'android/tester-main.c' || echo '$(srcdir)/'`android/tester-main.c android/android_tester-tester-main.obj: android/tester-main.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(android_android_tester_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT android/android_tester-tester-main.obj -MD -MP -MF android/$(DEPDIR)/android_tester-tester-main.Tpo -c -o android/android_tester-tester-main.obj `if test -f 'android/tester-main.c'; then $(CYGPATH_W) 'android/tester-main.c'; else $(CYGPATH_W) '$(srcdir)/android/tester-main.c'; fi` @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) android/$(DEPDIR)/android_tester-tester-main.Tpo android/$(DEPDIR)/android_tester-tester-main.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='android/tester-main.c' object='android/android_tester-tester-main.obj' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(android_android_tester_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o android/android_tester-tester-main.obj `if test -f 'android/tester-main.c'; then $(CYGPATH_W) 'android/tester-main.c'; else $(CYGPATH_W) '$(srcdir)/android/tester-main.c'; fi` android/avdtptest-avdtptest.o: android/avdtptest.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(android_avdtptest_CFLAGS) $(CFLAGS) -MT android/avdtptest-avdtptest.o -MD -MP -MF android/$(DEPDIR)/avdtptest-avdtptest.Tpo -c -o android/avdtptest-avdtptest.o `test -f 'android/avdtptest.c' || echo '$(srcdir)/'`android/avdtptest.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) android/$(DEPDIR)/avdtptest-avdtptest.Tpo android/$(DEPDIR)/avdtptest-avdtptest.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='android/avdtptest.c' object='android/avdtptest-avdtptest.o' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(android_avdtptest_CFLAGS) $(CFLAGS) -c -o android/avdtptest-avdtptest.o `test -f 'android/avdtptest.c' || echo '$(srcdir)/'`android/avdtptest.c android/avdtptest-avdtptest.obj: android/avdtptest.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(android_avdtptest_CFLAGS) $(CFLAGS) -MT android/avdtptest-avdtptest.obj -MD -MP -MF android/$(DEPDIR)/avdtptest-avdtptest.Tpo -c -o android/avdtptest-avdtptest.obj `if test -f 'android/avdtptest.c'; then $(CYGPATH_W) 'android/avdtptest.c'; else $(CYGPATH_W) '$(srcdir)/android/avdtptest.c'; fi` @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) android/$(DEPDIR)/avdtptest-avdtptest.Tpo android/$(DEPDIR)/avdtptest-avdtptest.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='android/avdtptest.c' object='android/avdtptest-avdtptest.obj' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(android_avdtptest_CFLAGS) $(CFLAGS) -c -o android/avdtptest-avdtptest.obj `if test -f 'android/avdtptest.c'; then $(CYGPATH_W) 'android/avdtptest.c'; else $(CYGPATH_W) '$(srcdir)/android/avdtptest.c'; fi` src/android_avdtptest-log.o: src/log.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(android_avdtptest_CFLAGS) $(CFLAGS) -MT src/android_avdtptest-log.o -MD -MP -MF src/$(DEPDIR)/android_avdtptest-log.Tpo -c -o src/android_avdtptest-log.o `test -f 'src/log.c' || echo '$(srcdir)/'`src/log.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) src/$(DEPDIR)/android_avdtptest-log.Tpo src/$(DEPDIR)/android_avdtptest-log.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='src/log.c' object='src/android_avdtptest-log.o' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(android_avdtptest_CFLAGS) $(CFLAGS) -c -o src/android_avdtptest-log.o `test -f 'src/log.c' || echo '$(srcdir)/'`src/log.c src/android_avdtptest-log.obj: src/log.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(android_avdtptest_CFLAGS) $(CFLAGS) -MT src/android_avdtptest-log.obj -MD -MP -MF src/$(DEPDIR)/android_avdtptest-log.Tpo -c -o src/android_avdtptest-log.obj `if test -f 'src/log.c'; then $(CYGPATH_W) 'src/log.c'; else $(CYGPATH_W) '$(srcdir)/src/log.c'; fi` @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) src/$(DEPDIR)/android_avdtptest-log.Tpo src/$(DEPDIR)/android_avdtptest-log.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='src/log.c' object='src/android_avdtptest-log.obj' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(android_avdtptest_CFLAGS) $(CFLAGS) -c -o src/android_avdtptest-log.obj `if test -f 'src/log.c'; then $(CYGPATH_W) 'src/log.c'; else $(CYGPATH_W) '$(srcdir)/src/log.c'; fi` btio/android_avdtptest-btio.o: btio/btio.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(android_avdtptest_CFLAGS) $(CFLAGS) -MT btio/android_avdtptest-btio.o -MD -MP -MF btio/$(DEPDIR)/android_avdtptest-btio.Tpo -c -o btio/android_avdtptest-btio.o `test -f 'btio/btio.c' || echo '$(srcdir)/'`btio/btio.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) btio/$(DEPDIR)/android_avdtptest-btio.Tpo btio/$(DEPDIR)/android_avdtptest-btio.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='btio/btio.c' object='btio/android_avdtptest-btio.o' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(android_avdtptest_CFLAGS) $(CFLAGS) -c -o btio/android_avdtptest-btio.o `test -f 'btio/btio.c' || echo '$(srcdir)/'`btio/btio.c btio/android_avdtptest-btio.obj: btio/btio.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(android_avdtptest_CFLAGS) $(CFLAGS) -MT btio/android_avdtptest-btio.obj -MD -MP -MF btio/$(DEPDIR)/android_avdtptest-btio.Tpo -c -o btio/android_avdtptest-btio.obj `if test -f 'btio/btio.c'; then $(CYGPATH_W) 'btio/btio.c'; else $(CYGPATH_W) '$(srcdir)/btio/btio.c'; fi` @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) btio/$(DEPDIR)/android_avdtptest-btio.Tpo btio/$(DEPDIR)/android_avdtptest-btio.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='btio/btio.c' object='btio/android_avdtptest-btio.obj' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(android_avdtptest_CFLAGS) $(CFLAGS) -c -o btio/android_avdtptest-btio.obj `if test -f 'btio/btio.c'; then $(CYGPATH_W) 'btio/btio.c'; else $(CYGPATH_W) '$(srcdir)/btio/btio.c'; fi` src/shared/android_avdtptest-util.o: src/shared/util.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(android_avdtptest_CFLAGS) $(CFLAGS) -MT src/shared/android_avdtptest-util.o -MD -MP -MF src/shared/$(DEPDIR)/android_avdtptest-util.Tpo -c -o src/shared/android_avdtptest-util.o `test -f 'src/shared/util.c' || echo '$(srcdir)/'`src/shared/util.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) src/shared/$(DEPDIR)/android_avdtptest-util.Tpo src/shared/$(DEPDIR)/android_avdtptest-util.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='src/shared/util.c' object='src/shared/android_avdtptest-util.o' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(android_avdtptest_CFLAGS) $(CFLAGS) -c -o src/shared/android_avdtptest-util.o `test -f 'src/shared/util.c' || echo '$(srcdir)/'`src/shared/util.c src/shared/android_avdtptest-util.obj: src/shared/util.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(android_avdtptest_CFLAGS) $(CFLAGS) -MT src/shared/android_avdtptest-util.obj -MD -MP -MF src/shared/$(DEPDIR)/android_avdtptest-util.Tpo -c -o src/shared/android_avdtptest-util.obj `if test -f 'src/shared/util.c'; then $(CYGPATH_W) 'src/shared/util.c'; else $(CYGPATH_W) '$(srcdir)/src/shared/util.c'; fi` @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) src/shared/$(DEPDIR)/android_avdtptest-util.Tpo src/shared/$(DEPDIR)/android_avdtptest-util.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='src/shared/util.c' object='src/shared/android_avdtptest-util.obj' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(android_avdtptest_CFLAGS) $(CFLAGS) -c -o src/shared/android_avdtptest-util.obj `if test -f 'src/shared/util.c'; then $(CYGPATH_W) 'src/shared/util.c'; else $(CYGPATH_W) '$(srcdir)/src/shared/util.c'; fi` src/shared/android_avdtptest-queue.o: src/shared/queue.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(android_avdtptest_CFLAGS) $(CFLAGS) -MT src/shared/android_avdtptest-queue.o -MD -MP -MF src/shared/$(DEPDIR)/android_avdtptest-queue.Tpo -c -o src/shared/android_avdtptest-queue.o `test -f 'src/shared/queue.c' || echo '$(srcdir)/'`src/shared/queue.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) src/shared/$(DEPDIR)/android_avdtptest-queue.Tpo src/shared/$(DEPDIR)/android_avdtptest-queue.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='src/shared/queue.c' object='src/shared/android_avdtptest-queue.o' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(android_avdtptest_CFLAGS) $(CFLAGS) -c -o src/shared/android_avdtptest-queue.o `test -f 'src/shared/queue.c' || echo '$(srcdir)/'`src/shared/queue.c src/shared/android_avdtptest-queue.obj: src/shared/queue.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(android_avdtptest_CFLAGS) $(CFLAGS) -MT src/shared/android_avdtptest-queue.obj -MD -MP -MF src/shared/$(DEPDIR)/android_avdtptest-queue.Tpo -c -o src/shared/android_avdtptest-queue.obj `if test -f 'src/shared/queue.c'; then $(CYGPATH_W) 'src/shared/queue.c'; else $(CYGPATH_W) '$(srcdir)/src/shared/queue.c'; fi` @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) src/shared/$(DEPDIR)/android_avdtptest-queue.Tpo src/shared/$(DEPDIR)/android_avdtptest-queue.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='src/shared/queue.c' object='src/shared/android_avdtptest-queue.obj' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(android_avdtptest_CFLAGS) $(CFLAGS) -c -o src/shared/android_avdtptest-queue.obj `if test -f 'src/shared/queue.c'; then $(CYGPATH_W) 'src/shared/queue.c'; else $(CYGPATH_W) '$(srcdir)/src/shared/queue.c'; fi` src/shared/android_avdtptest-log.o: src/shared/log.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(android_avdtptest_CFLAGS) $(CFLAGS) -MT src/shared/android_avdtptest-log.o -MD -MP -MF src/shared/$(DEPDIR)/android_avdtptest-log.Tpo -c -o src/shared/android_avdtptest-log.o `test -f 'src/shared/log.c' || echo '$(srcdir)/'`src/shared/log.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) src/shared/$(DEPDIR)/android_avdtptest-log.Tpo src/shared/$(DEPDIR)/android_avdtptest-log.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='src/shared/log.c' object='src/shared/android_avdtptest-log.o' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(android_avdtptest_CFLAGS) $(CFLAGS) -c -o src/shared/android_avdtptest-log.o `test -f 'src/shared/log.c' || echo '$(srcdir)/'`src/shared/log.c src/shared/android_avdtptest-log.obj: src/shared/log.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(android_avdtptest_CFLAGS) $(CFLAGS) -MT src/shared/android_avdtptest-log.obj -MD -MP -MF src/shared/$(DEPDIR)/android_avdtptest-log.Tpo -c -o src/shared/android_avdtptest-log.obj `if test -f 'src/shared/log.c'; then $(CYGPATH_W) 'src/shared/log.c'; else $(CYGPATH_W) '$(srcdir)/src/shared/log.c'; fi` @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) src/shared/$(DEPDIR)/android_avdtptest-log.Tpo src/shared/$(DEPDIR)/android_avdtptest-log.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='src/shared/log.c' object='src/shared/android_avdtptest-log.obj' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(android_avdtptest_CFLAGS) $(CFLAGS) -c -o src/shared/android_avdtptest-log.obj `if test -f 'src/shared/log.c'; then $(CYGPATH_W) 'src/shared/log.c'; else $(CYGPATH_W) '$(srcdir)/src/shared/log.c'; fi` android/avdtptest-avdtp.o: android/avdtp.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(android_avdtptest_CFLAGS) $(CFLAGS) -MT android/avdtptest-avdtp.o -MD -MP -MF android/$(DEPDIR)/avdtptest-avdtp.Tpo -c -o android/avdtptest-avdtp.o `test -f 'android/avdtp.c' || echo '$(srcdir)/'`android/avdtp.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) android/$(DEPDIR)/avdtptest-avdtp.Tpo android/$(DEPDIR)/avdtptest-avdtp.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='android/avdtp.c' object='android/avdtptest-avdtp.o' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(android_avdtptest_CFLAGS) $(CFLAGS) -c -o android/avdtptest-avdtp.o `test -f 'android/avdtp.c' || echo '$(srcdir)/'`android/avdtp.c android/avdtptest-avdtp.obj: android/avdtp.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(android_avdtptest_CFLAGS) $(CFLAGS) -MT android/avdtptest-avdtp.obj -MD -MP -MF android/$(DEPDIR)/avdtptest-avdtp.Tpo -c -o android/avdtptest-avdtp.obj `if test -f 'android/avdtp.c'; then $(CYGPATH_W) 'android/avdtp.c'; else $(CYGPATH_W) '$(srcdir)/android/avdtp.c'; fi` @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) android/$(DEPDIR)/avdtptest-avdtp.Tpo android/$(DEPDIR)/avdtptest-avdtp.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='android/avdtp.c' object='android/avdtptest-avdtp.obj' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(android_avdtptest_CFLAGS) $(CFLAGS) -c -o android/avdtptest-avdtp.obj `if test -f 'android/avdtp.c'; then $(CYGPATH_W) 'android/avdtp.c'; else $(CYGPATH_W) '$(srcdir)/android/avdtp.c'; fi` android/client/haltest-haltest.o: android/client/haltest.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(android_haltest_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT android/client/haltest-haltest.o -MD -MP -MF android/client/$(DEPDIR)/haltest-haltest.Tpo -c -o android/client/haltest-haltest.o `test -f 'android/client/haltest.c' || echo '$(srcdir)/'`android/client/haltest.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) android/client/$(DEPDIR)/haltest-haltest.Tpo android/client/$(DEPDIR)/haltest-haltest.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='android/client/haltest.c' object='android/client/haltest-haltest.o' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(android_haltest_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o android/client/haltest-haltest.o `test -f 'android/client/haltest.c' || echo '$(srcdir)/'`android/client/haltest.c android/client/haltest-haltest.obj: android/client/haltest.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(android_haltest_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT android/client/haltest-haltest.obj -MD -MP -MF android/client/$(DEPDIR)/haltest-haltest.Tpo -c -o android/client/haltest-haltest.obj `if test -f 'android/client/haltest.c'; then $(CYGPATH_W) 'android/client/haltest.c'; else $(CYGPATH_W) '$(srcdir)/android/client/haltest.c'; fi` @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) android/client/$(DEPDIR)/haltest-haltest.Tpo android/client/$(DEPDIR)/haltest-haltest.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='android/client/haltest.c' object='android/client/haltest-haltest.obj' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(android_haltest_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o android/client/haltest-haltest.obj `if test -f 'android/client/haltest.c'; then $(CYGPATH_W) 'android/client/haltest.c'; else $(CYGPATH_W) '$(srcdir)/android/client/haltest.c'; fi` android/client/haltest-pollhandler.o: android/client/pollhandler.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(android_haltest_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT android/client/haltest-pollhandler.o -MD -MP -MF android/client/$(DEPDIR)/haltest-pollhandler.Tpo -c -o android/client/haltest-pollhandler.o `test -f 'android/client/pollhandler.c' || echo '$(srcdir)/'`android/client/pollhandler.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) android/client/$(DEPDIR)/haltest-pollhandler.Tpo android/client/$(DEPDIR)/haltest-pollhandler.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='android/client/pollhandler.c' object='android/client/haltest-pollhandler.o' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(android_haltest_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o android/client/haltest-pollhandler.o `test -f 'android/client/pollhandler.c' || echo '$(srcdir)/'`android/client/pollhandler.c android/client/haltest-pollhandler.obj: android/client/pollhandler.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(android_haltest_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT android/client/haltest-pollhandler.obj -MD -MP -MF android/client/$(DEPDIR)/haltest-pollhandler.Tpo -c -o android/client/haltest-pollhandler.obj `if test -f 'android/client/pollhandler.c'; then $(CYGPATH_W) 'android/client/pollhandler.c'; else $(CYGPATH_W) '$(srcdir)/android/client/pollhandler.c'; fi` @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) android/client/$(DEPDIR)/haltest-pollhandler.Tpo android/client/$(DEPDIR)/haltest-pollhandler.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='android/client/pollhandler.c' object='android/client/haltest-pollhandler.obj' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(android_haltest_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o android/client/haltest-pollhandler.obj `if test -f 'android/client/pollhandler.c'; then $(CYGPATH_W) 'android/client/pollhandler.c'; else $(CYGPATH_W) '$(srcdir)/android/client/pollhandler.c'; fi` android/client/haltest-terminal.o: android/client/terminal.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(android_haltest_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT android/client/haltest-terminal.o -MD -MP -MF android/client/$(DEPDIR)/haltest-terminal.Tpo -c -o android/client/haltest-terminal.o `test -f 'android/client/terminal.c' || echo '$(srcdir)/'`android/client/terminal.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) android/client/$(DEPDIR)/haltest-terminal.Tpo android/client/$(DEPDIR)/haltest-terminal.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='android/client/terminal.c' object='android/client/haltest-terminal.o' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(android_haltest_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o android/client/haltest-terminal.o `test -f 'android/client/terminal.c' || echo '$(srcdir)/'`android/client/terminal.c android/client/haltest-terminal.obj: android/client/terminal.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(android_haltest_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT android/client/haltest-terminal.obj -MD -MP -MF android/client/$(DEPDIR)/haltest-terminal.Tpo -c -o android/client/haltest-terminal.obj `if test -f 'android/client/terminal.c'; then $(CYGPATH_W) 'android/client/terminal.c'; else $(CYGPATH_W) '$(srcdir)/android/client/terminal.c'; fi` @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) android/client/$(DEPDIR)/haltest-terminal.Tpo android/client/$(DEPDIR)/haltest-terminal.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='android/client/terminal.c' object='android/client/haltest-terminal.obj' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(android_haltest_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o android/client/haltest-terminal.obj `if test -f 'android/client/terminal.c'; then $(CYGPATH_W) 'android/client/terminal.c'; else $(CYGPATH_W) '$(srcdir)/android/client/terminal.c'; fi` android/client/haltest-history.o: android/client/history.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(android_haltest_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT android/client/haltest-history.o -MD -MP -MF android/client/$(DEPDIR)/haltest-history.Tpo -c -o android/client/haltest-history.o `test -f 'android/client/history.c' || echo '$(srcdir)/'`android/client/history.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) android/client/$(DEPDIR)/haltest-history.Tpo android/client/$(DEPDIR)/haltest-history.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='android/client/history.c' object='android/client/haltest-history.o' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(android_haltest_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o android/client/haltest-history.o `test -f 'android/client/history.c' || echo '$(srcdir)/'`android/client/history.c android/client/haltest-history.obj: android/client/history.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(android_haltest_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT android/client/haltest-history.obj -MD -MP -MF android/client/$(DEPDIR)/haltest-history.Tpo -c -o android/client/haltest-history.obj `if test -f 'android/client/history.c'; then $(CYGPATH_W) 'android/client/history.c'; else $(CYGPATH_W) '$(srcdir)/android/client/history.c'; fi` @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) android/client/$(DEPDIR)/haltest-history.Tpo android/client/$(DEPDIR)/haltest-history.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='android/client/history.c' object='android/client/haltest-history.obj' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(android_haltest_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o android/client/haltest-history.obj `if test -f 'android/client/history.c'; then $(CYGPATH_W) 'android/client/history.c'; else $(CYGPATH_W) '$(srcdir)/android/client/history.c'; fi` android/client/haltest-tabcompletion.o: android/client/tabcompletion.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(android_haltest_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT android/client/haltest-tabcompletion.o -MD -MP -MF android/client/$(DEPDIR)/haltest-tabcompletion.Tpo -c -o android/client/haltest-tabcompletion.o `test -f 'android/client/tabcompletion.c' || echo '$(srcdir)/'`android/client/tabcompletion.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) android/client/$(DEPDIR)/haltest-tabcompletion.Tpo android/client/$(DEPDIR)/haltest-tabcompletion.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='android/client/tabcompletion.c' object='android/client/haltest-tabcompletion.o' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(android_haltest_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o android/client/haltest-tabcompletion.o `test -f 'android/client/tabcompletion.c' || echo '$(srcdir)/'`android/client/tabcompletion.c android/client/haltest-tabcompletion.obj: android/client/tabcompletion.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(android_haltest_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT android/client/haltest-tabcompletion.obj -MD -MP -MF android/client/$(DEPDIR)/haltest-tabcompletion.Tpo -c -o android/client/haltest-tabcompletion.obj `if test -f 'android/client/tabcompletion.c'; then $(CYGPATH_W) 'android/client/tabcompletion.c'; else $(CYGPATH_W) '$(srcdir)/android/client/tabcompletion.c'; fi` @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) android/client/$(DEPDIR)/haltest-tabcompletion.Tpo android/client/$(DEPDIR)/haltest-tabcompletion.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='android/client/tabcompletion.c' object='android/client/haltest-tabcompletion.obj' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(android_haltest_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o android/client/haltest-tabcompletion.obj `if test -f 'android/client/tabcompletion.c'; then $(CYGPATH_W) 'android/client/tabcompletion.c'; else $(CYGPATH_W) '$(srcdir)/android/client/tabcompletion.c'; fi` android/client/haltest-if-av.o: android/client/if-av.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(android_haltest_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT android/client/haltest-if-av.o -MD -MP -MF android/client/$(DEPDIR)/haltest-if-av.Tpo -c -o android/client/haltest-if-av.o `test -f 'android/client/if-av.c' || echo '$(srcdir)/'`android/client/if-av.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) android/client/$(DEPDIR)/haltest-if-av.Tpo android/client/$(DEPDIR)/haltest-if-av.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='android/client/if-av.c' object='android/client/haltest-if-av.o' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(android_haltest_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o android/client/haltest-if-av.o `test -f 'android/client/if-av.c' || echo '$(srcdir)/'`android/client/if-av.c android/client/haltest-if-av.obj: android/client/if-av.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(android_haltest_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT android/client/haltest-if-av.obj -MD -MP -MF android/client/$(DEPDIR)/haltest-if-av.Tpo -c -o android/client/haltest-if-av.obj `if test -f 'android/client/if-av.c'; then $(CYGPATH_W) 'android/client/if-av.c'; else $(CYGPATH_W) '$(srcdir)/android/client/if-av.c'; fi` @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) android/client/$(DEPDIR)/haltest-if-av.Tpo android/client/$(DEPDIR)/haltest-if-av.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='android/client/if-av.c' object='android/client/haltest-if-av.obj' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(android_haltest_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o android/client/haltest-if-av.obj `if test -f 'android/client/if-av.c'; then $(CYGPATH_W) 'android/client/if-av.c'; else $(CYGPATH_W) '$(srcdir)/android/client/if-av.c'; fi` android/client/haltest-if-av-sink.o: android/client/if-av-sink.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(android_haltest_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT android/client/haltest-if-av-sink.o -MD -MP -MF android/client/$(DEPDIR)/haltest-if-av-sink.Tpo -c -o android/client/haltest-if-av-sink.o `test -f 'android/client/if-av-sink.c' || echo '$(srcdir)/'`android/client/if-av-sink.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) android/client/$(DEPDIR)/haltest-if-av-sink.Tpo android/client/$(DEPDIR)/haltest-if-av-sink.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='android/client/if-av-sink.c' object='android/client/haltest-if-av-sink.o' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(android_haltest_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o android/client/haltest-if-av-sink.o `test -f 'android/client/if-av-sink.c' || echo '$(srcdir)/'`android/client/if-av-sink.c android/client/haltest-if-av-sink.obj: android/client/if-av-sink.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(android_haltest_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT android/client/haltest-if-av-sink.obj -MD -MP -MF android/client/$(DEPDIR)/haltest-if-av-sink.Tpo -c -o android/client/haltest-if-av-sink.obj `if test -f 'android/client/if-av-sink.c'; then $(CYGPATH_W) 'android/client/if-av-sink.c'; else $(CYGPATH_W) '$(srcdir)/android/client/if-av-sink.c'; fi` @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) android/client/$(DEPDIR)/haltest-if-av-sink.Tpo android/client/$(DEPDIR)/haltest-if-av-sink.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='android/client/if-av-sink.c' object='android/client/haltest-if-av-sink.obj' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(android_haltest_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o android/client/haltest-if-av-sink.obj `if test -f 'android/client/if-av-sink.c'; then $(CYGPATH_W) 'android/client/if-av-sink.c'; else $(CYGPATH_W) '$(srcdir)/android/client/if-av-sink.c'; fi` android/client/haltest-if-rc.o: android/client/if-rc.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(android_haltest_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT android/client/haltest-if-rc.o -MD -MP -MF android/client/$(DEPDIR)/haltest-if-rc.Tpo -c -o android/client/haltest-if-rc.o `test -f 'android/client/if-rc.c' || echo '$(srcdir)/'`android/client/if-rc.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) android/client/$(DEPDIR)/haltest-if-rc.Tpo android/client/$(DEPDIR)/haltest-if-rc.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='android/client/if-rc.c' object='android/client/haltest-if-rc.o' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(android_haltest_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o android/client/haltest-if-rc.o `test -f 'android/client/if-rc.c' || echo '$(srcdir)/'`android/client/if-rc.c android/client/haltest-if-rc.obj: android/client/if-rc.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(android_haltest_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT android/client/haltest-if-rc.obj -MD -MP -MF android/client/$(DEPDIR)/haltest-if-rc.Tpo -c -o android/client/haltest-if-rc.obj `if test -f 'android/client/if-rc.c'; then $(CYGPATH_W) 'android/client/if-rc.c'; else $(CYGPATH_W) '$(srcdir)/android/client/if-rc.c'; fi` @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) android/client/$(DEPDIR)/haltest-if-rc.Tpo android/client/$(DEPDIR)/haltest-if-rc.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='android/client/if-rc.c' object='android/client/haltest-if-rc.obj' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(android_haltest_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o android/client/haltest-if-rc.obj `if test -f 'android/client/if-rc.c'; then $(CYGPATH_W) 'android/client/if-rc.c'; else $(CYGPATH_W) '$(srcdir)/android/client/if-rc.c'; fi` android/client/haltest-if-rc-ctrl.o: android/client/if-rc-ctrl.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(android_haltest_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT android/client/haltest-if-rc-ctrl.o -MD -MP -MF android/client/$(DEPDIR)/haltest-if-rc-ctrl.Tpo -c -o android/client/haltest-if-rc-ctrl.o `test -f 'android/client/if-rc-ctrl.c' || echo '$(srcdir)/'`android/client/if-rc-ctrl.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) android/client/$(DEPDIR)/haltest-if-rc-ctrl.Tpo android/client/$(DEPDIR)/haltest-if-rc-ctrl.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='android/client/if-rc-ctrl.c' object='android/client/haltest-if-rc-ctrl.o' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(android_haltest_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o android/client/haltest-if-rc-ctrl.o `test -f 'android/client/if-rc-ctrl.c' || echo '$(srcdir)/'`android/client/if-rc-ctrl.c android/client/haltest-if-rc-ctrl.obj: android/client/if-rc-ctrl.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(android_haltest_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT android/client/haltest-if-rc-ctrl.obj -MD -MP -MF android/client/$(DEPDIR)/haltest-if-rc-ctrl.Tpo -c -o android/client/haltest-if-rc-ctrl.obj `if test -f 'android/client/if-rc-ctrl.c'; then $(CYGPATH_W) 'android/client/if-rc-ctrl.c'; else $(CYGPATH_W) '$(srcdir)/android/client/if-rc-ctrl.c'; fi` @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) android/client/$(DEPDIR)/haltest-if-rc-ctrl.Tpo android/client/$(DEPDIR)/haltest-if-rc-ctrl.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='android/client/if-rc-ctrl.c' object='android/client/haltest-if-rc-ctrl.obj' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(android_haltest_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o android/client/haltest-if-rc-ctrl.obj `if test -f 'android/client/if-rc-ctrl.c'; then $(CYGPATH_W) 'android/client/if-rc-ctrl.c'; else $(CYGPATH_W) '$(srcdir)/android/client/if-rc-ctrl.c'; fi` android/client/haltest-if-bt.o: android/client/if-bt.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(android_haltest_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT android/client/haltest-if-bt.o -MD -MP -MF android/client/$(DEPDIR)/haltest-if-bt.Tpo -c -o android/client/haltest-if-bt.o `test -f 'android/client/if-bt.c' || echo '$(srcdir)/'`android/client/if-bt.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) android/client/$(DEPDIR)/haltest-if-bt.Tpo android/client/$(DEPDIR)/haltest-if-bt.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='android/client/if-bt.c' object='android/client/haltest-if-bt.o' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(android_haltest_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o android/client/haltest-if-bt.o `test -f 'android/client/if-bt.c' || echo '$(srcdir)/'`android/client/if-bt.c android/client/haltest-if-bt.obj: android/client/if-bt.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(android_haltest_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT android/client/haltest-if-bt.obj -MD -MP -MF android/client/$(DEPDIR)/haltest-if-bt.Tpo -c -o android/client/haltest-if-bt.obj `if test -f 'android/client/if-bt.c'; then $(CYGPATH_W) 'android/client/if-bt.c'; else $(CYGPATH_W) '$(srcdir)/android/client/if-bt.c'; fi` @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) android/client/$(DEPDIR)/haltest-if-bt.Tpo android/client/$(DEPDIR)/haltest-if-bt.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='android/client/if-bt.c' object='android/client/haltest-if-bt.obj' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(android_haltest_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o android/client/haltest-if-bt.obj `if test -f 'android/client/if-bt.c'; then $(CYGPATH_W) 'android/client/if-bt.c'; else $(CYGPATH_W) '$(srcdir)/android/client/if-bt.c'; fi` android/client/haltest-if-gatt.o: android/client/if-gatt.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(android_haltest_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT android/client/haltest-if-gatt.o -MD -MP -MF android/client/$(DEPDIR)/haltest-if-gatt.Tpo -c -o android/client/haltest-if-gatt.o `test -f 'android/client/if-gatt.c' || echo '$(srcdir)/'`android/client/if-gatt.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) android/client/$(DEPDIR)/haltest-if-gatt.Tpo android/client/$(DEPDIR)/haltest-if-gatt.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='android/client/if-gatt.c' object='android/client/haltest-if-gatt.o' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(android_haltest_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o android/client/haltest-if-gatt.o `test -f 'android/client/if-gatt.c' || echo '$(srcdir)/'`android/client/if-gatt.c android/client/haltest-if-gatt.obj: android/client/if-gatt.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(android_haltest_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT android/client/haltest-if-gatt.obj -MD -MP -MF android/client/$(DEPDIR)/haltest-if-gatt.Tpo -c -o android/client/haltest-if-gatt.obj `if test -f 'android/client/if-gatt.c'; then $(CYGPATH_W) 'android/client/if-gatt.c'; else $(CYGPATH_W) '$(srcdir)/android/client/if-gatt.c'; fi` @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) android/client/$(DEPDIR)/haltest-if-gatt.Tpo android/client/$(DEPDIR)/haltest-if-gatt.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='android/client/if-gatt.c' object='android/client/haltest-if-gatt.obj' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(android_haltest_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o android/client/haltest-if-gatt.obj `if test -f 'android/client/if-gatt.c'; then $(CYGPATH_W) 'android/client/if-gatt.c'; else $(CYGPATH_W) '$(srcdir)/android/client/if-gatt.c'; fi` android/client/haltest-if-hf.o: android/client/if-hf.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(android_haltest_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT android/client/haltest-if-hf.o -MD -MP -MF android/client/$(DEPDIR)/haltest-if-hf.Tpo -c -o android/client/haltest-if-hf.o `test -f 'android/client/if-hf.c' || echo '$(srcdir)/'`android/client/if-hf.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) android/client/$(DEPDIR)/haltest-if-hf.Tpo android/client/$(DEPDIR)/haltest-if-hf.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='android/client/if-hf.c' object='android/client/haltest-if-hf.o' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(android_haltest_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o android/client/haltest-if-hf.o `test -f 'android/client/if-hf.c' || echo '$(srcdir)/'`android/client/if-hf.c android/client/haltest-if-hf.obj: android/client/if-hf.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(android_haltest_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT android/client/haltest-if-hf.obj -MD -MP -MF android/client/$(DEPDIR)/haltest-if-hf.Tpo -c -o android/client/haltest-if-hf.obj `if test -f 'android/client/if-hf.c'; then $(CYGPATH_W) 'android/client/if-hf.c'; else $(CYGPATH_W) '$(srcdir)/android/client/if-hf.c'; fi` @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) android/client/$(DEPDIR)/haltest-if-hf.Tpo android/client/$(DEPDIR)/haltest-if-hf.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='android/client/if-hf.c' object='android/client/haltest-if-hf.obj' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(android_haltest_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o android/client/haltest-if-hf.obj `if test -f 'android/client/if-hf.c'; then $(CYGPATH_W) 'android/client/if-hf.c'; else $(CYGPATH_W) '$(srcdir)/android/client/if-hf.c'; fi` android/client/haltest-if-hf-client.o: android/client/if-hf-client.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(android_haltest_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT android/client/haltest-if-hf-client.o -MD -MP -MF android/client/$(DEPDIR)/haltest-if-hf-client.Tpo -c -o android/client/haltest-if-hf-client.o `test -f 'android/client/if-hf-client.c' || echo '$(srcdir)/'`android/client/if-hf-client.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) android/client/$(DEPDIR)/haltest-if-hf-client.Tpo android/client/$(DEPDIR)/haltest-if-hf-client.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='android/client/if-hf-client.c' object='android/client/haltest-if-hf-client.o' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(android_haltest_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o android/client/haltest-if-hf-client.o `test -f 'android/client/if-hf-client.c' || echo '$(srcdir)/'`android/client/if-hf-client.c android/client/haltest-if-hf-client.obj: android/client/if-hf-client.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(android_haltest_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT android/client/haltest-if-hf-client.obj -MD -MP -MF android/client/$(DEPDIR)/haltest-if-hf-client.Tpo -c -o android/client/haltest-if-hf-client.obj `if test -f 'android/client/if-hf-client.c'; then $(CYGPATH_W) 'android/client/if-hf-client.c'; else $(CYGPATH_W) '$(srcdir)/android/client/if-hf-client.c'; fi` @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) android/client/$(DEPDIR)/haltest-if-hf-client.Tpo android/client/$(DEPDIR)/haltest-if-hf-client.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='android/client/if-hf-client.c' object='android/client/haltest-if-hf-client.obj' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(android_haltest_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o android/client/haltest-if-hf-client.obj `if test -f 'android/client/if-hf-client.c'; then $(CYGPATH_W) 'android/client/if-hf-client.c'; else $(CYGPATH_W) '$(srcdir)/android/client/if-hf-client.c'; fi` android/client/haltest-if-hh.o: android/client/if-hh.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(android_haltest_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT android/client/haltest-if-hh.o -MD -MP -MF android/client/$(DEPDIR)/haltest-if-hh.Tpo -c -o android/client/haltest-if-hh.o `test -f 'android/client/if-hh.c' || echo '$(srcdir)/'`android/client/if-hh.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) android/client/$(DEPDIR)/haltest-if-hh.Tpo android/client/$(DEPDIR)/haltest-if-hh.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='android/client/if-hh.c' object='android/client/haltest-if-hh.o' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(android_haltest_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o android/client/haltest-if-hh.o `test -f 'android/client/if-hh.c' || echo '$(srcdir)/'`android/client/if-hh.c android/client/haltest-if-hh.obj: android/client/if-hh.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(android_haltest_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT android/client/haltest-if-hh.obj -MD -MP -MF android/client/$(DEPDIR)/haltest-if-hh.Tpo -c -o android/client/haltest-if-hh.obj `if test -f 'android/client/if-hh.c'; then $(CYGPATH_W) 'android/client/if-hh.c'; else $(CYGPATH_W) '$(srcdir)/android/client/if-hh.c'; fi` @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) android/client/$(DEPDIR)/haltest-if-hh.Tpo android/client/$(DEPDIR)/haltest-if-hh.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='android/client/if-hh.c' object='android/client/haltest-if-hh.obj' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(android_haltest_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o android/client/haltest-if-hh.obj `if test -f 'android/client/if-hh.c'; then $(CYGPATH_W) 'android/client/if-hh.c'; else $(CYGPATH_W) '$(srcdir)/android/client/if-hh.c'; fi` android/client/haltest-if-pan.o: android/client/if-pan.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(android_haltest_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT android/client/haltest-if-pan.o -MD -MP -MF android/client/$(DEPDIR)/haltest-if-pan.Tpo -c -o android/client/haltest-if-pan.o `test -f 'android/client/if-pan.c' || echo '$(srcdir)/'`android/client/if-pan.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) android/client/$(DEPDIR)/haltest-if-pan.Tpo android/client/$(DEPDIR)/haltest-if-pan.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='android/client/if-pan.c' object='android/client/haltest-if-pan.o' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(android_haltest_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o android/client/haltest-if-pan.o `test -f 'android/client/if-pan.c' || echo '$(srcdir)/'`android/client/if-pan.c android/client/haltest-if-pan.obj: android/client/if-pan.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(android_haltest_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT android/client/haltest-if-pan.obj -MD -MP -MF android/client/$(DEPDIR)/haltest-if-pan.Tpo -c -o android/client/haltest-if-pan.obj `if test -f 'android/client/if-pan.c'; then $(CYGPATH_W) 'android/client/if-pan.c'; else $(CYGPATH_W) '$(srcdir)/android/client/if-pan.c'; fi` @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) android/client/$(DEPDIR)/haltest-if-pan.Tpo android/client/$(DEPDIR)/haltest-if-pan.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='android/client/if-pan.c' object='android/client/haltest-if-pan.obj' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(android_haltest_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o android/client/haltest-if-pan.obj `if test -f 'android/client/if-pan.c'; then $(CYGPATH_W) 'android/client/if-pan.c'; else $(CYGPATH_W) '$(srcdir)/android/client/if-pan.c'; fi` android/client/haltest-if-hl.o: android/client/if-hl.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(android_haltest_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT android/client/haltest-if-hl.o -MD -MP -MF android/client/$(DEPDIR)/haltest-if-hl.Tpo -c -o android/client/haltest-if-hl.o `test -f 'android/client/if-hl.c' || echo '$(srcdir)/'`android/client/if-hl.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) android/client/$(DEPDIR)/haltest-if-hl.Tpo android/client/$(DEPDIR)/haltest-if-hl.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='android/client/if-hl.c' object='android/client/haltest-if-hl.o' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(android_haltest_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o android/client/haltest-if-hl.o `test -f 'android/client/if-hl.c' || echo '$(srcdir)/'`android/client/if-hl.c android/client/haltest-if-hl.obj: android/client/if-hl.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(android_haltest_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT android/client/haltest-if-hl.obj -MD -MP -MF android/client/$(DEPDIR)/haltest-if-hl.Tpo -c -o android/client/haltest-if-hl.obj `if test -f 'android/client/if-hl.c'; then $(CYGPATH_W) 'android/client/if-hl.c'; else $(CYGPATH_W) '$(srcdir)/android/client/if-hl.c'; fi` @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) android/client/$(DEPDIR)/haltest-if-hl.Tpo android/client/$(DEPDIR)/haltest-if-hl.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='android/client/if-hl.c' object='android/client/haltest-if-hl.obj' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(android_haltest_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o android/client/haltest-if-hl.obj `if test -f 'android/client/if-hl.c'; then $(CYGPATH_W) 'android/client/if-hl.c'; else $(CYGPATH_W) '$(srcdir)/android/client/if-hl.c'; fi` android/client/haltest-if-sock.o: android/client/if-sock.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(android_haltest_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT android/client/haltest-if-sock.o -MD -MP -MF android/client/$(DEPDIR)/haltest-if-sock.Tpo -c -o android/client/haltest-if-sock.o `test -f 'android/client/if-sock.c' || echo '$(srcdir)/'`android/client/if-sock.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) android/client/$(DEPDIR)/haltest-if-sock.Tpo android/client/$(DEPDIR)/haltest-if-sock.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='android/client/if-sock.c' object='android/client/haltest-if-sock.o' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(android_haltest_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o android/client/haltest-if-sock.o `test -f 'android/client/if-sock.c' || echo '$(srcdir)/'`android/client/if-sock.c android/client/haltest-if-sock.obj: android/client/if-sock.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(android_haltest_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT android/client/haltest-if-sock.obj -MD -MP -MF android/client/$(DEPDIR)/haltest-if-sock.Tpo -c -o android/client/haltest-if-sock.obj `if test -f 'android/client/if-sock.c'; then $(CYGPATH_W) 'android/client/if-sock.c'; else $(CYGPATH_W) '$(srcdir)/android/client/if-sock.c'; fi` @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) android/client/$(DEPDIR)/haltest-if-sock.Tpo android/client/$(DEPDIR)/haltest-if-sock.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='android/client/if-sock.c' object='android/client/haltest-if-sock.obj' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(android_haltest_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o android/client/haltest-if-sock.obj `if test -f 'android/client/if-sock.c'; then $(CYGPATH_W) 'android/client/if-sock.c'; else $(CYGPATH_W) '$(srcdir)/android/client/if-sock.c'; fi` android/client/haltest-if-audio.o: android/client/if-audio.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(android_haltest_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT android/client/haltest-if-audio.o -MD -MP -MF android/client/$(DEPDIR)/haltest-if-audio.Tpo -c -o android/client/haltest-if-audio.o `test -f 'android/client/if-audio.c' || echo '$(srcdir)/'`android/client/if-audio.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) android/client/$(DEPDIR)/haltest-if-audio.Tpo android/client/$(DEPDIR)/haltest-if-audio.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='android/client/if-audio.c' object='android/client/haltest-if-audio.o' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(android_haltest_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o android/client/haltest-if-audio.o `test -f 'android/client/if-audio.c' || echo '$(srcdir)/'`android/client/if-audio.c android/client/haltest-if-audio.obj: android/client/if-audio.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(android_haltest_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT android/client/haltest-if-audio.obj -MD -MP -MF android/client/$(DEPDIR)/haltest-if-audio.Tpo -c -o android/client/haltest-if-audio.obj `if test -f 'android/client/if-audio.c'; then $(CYGPATH_W) 'android/client/if-audio.c'; else $(CYGPATH_W) '$(srcdir)/android/client/if-audio.c'; fi` @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) android/client/$(DEPDIR)/haltest-if-audio.Tpo android/client/$(DEPDIR)/haltest-if-audio.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='android/client/if-audio.c' object='android/client/haltest-if-audio.obj' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(android_haltest_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o android/client/haltest-if-audio.obj `if test -f 'android/client/if-audio.c'; then $(CYGPATH_W) 'android/client/if-audio.c'; else $(CYGPATH_W) '$(srcdir)/android/client/if-audio.c'; fi` android/client/haltest-if-sco.o: android/client/if-sco.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(android_haltest_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT android/client/haltest-if-sco.o -MD -MP -MF android/client/$(DEPDIR)/haltest-if-sco.Tpo -c -o android/client/haltest-if-sco.o `test -f 'android/client/if-sco.c' || echo '$(srcdir)/'`android/client/if-sco.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) android/client/$(DEPDIR)/haltest-if-sco.Tpo android/client/$(DEPDIR)/haltest-if-sco.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='android/client/if-sco.c' object='android/client/haltest-if-sco.o' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(android_haltest_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o android/client/haltest-if-sco.o `test -f 'android/client/if-sco.c' || echo '$(srcdir)/'`android/client/if-sco.c android/client/haltest-if-sco.obj: android/client/if-sco.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(android_haltest_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT android/client/haltest-if-sco.obj -MD -MP -MF android/client/$(DEPDIR)/haltest-if-sco.Tpo -c -o android/client/haltest-if-sco.obj `if test -f 'android/client/if-sco.c'; then $(CYGPATH_W) 'android/client/if-sco.c'; else $(CYGPATH_W) '$(srcdir)/android/client/if-sco.c'; fi` @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) android/client/$(DEPDIR)/haltest-if-sco.Tpo android/client/$(DEPDIR)/haltest-if-sco.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='android/client/if-sco.c' object='android/client/haltest-if-sco.obj' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(android_haltest_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o android/client/haltest-if-sco.obj `if test -f 'android/client/if-sco.c'; then $(CYGPATH_W) 'android/client/if-sco.c'; else $(CYGPATH_W) '$(srcdir)/android/client/if-sco.c'; fi` android/client/haltest-if-mce.o: android/client/if-mce.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(android_haltest_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT android/client/haltest-if-mce.o -MD -MP -MF android/client/$(DEPDIR)/haltest-if-mce.Tpo -c -o android/client/haltest-if-mce.o `test -f 'android/client/if-mce.c' || echo '$(srcdir)/'`android/client/if-mce.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) android/client/$(DEPDIR)/haltest-if-mce.Tpo android/client/$(DEPDIR)/haltest-if-mce.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='android/client/if-mce.c' object='android/client/haltest-if-mce.o' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(android_haltest_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o android/client/haltest-if-mce.o `test -f 'android/client/if-mce.c' || echo '$(srcdir)/'`android/client/if-mce.c android/client/haltest-if-mce.obj: android/client/if-mce.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(android_haltest_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT android/client/haltest-if-mce.obj -MD -MP -MF android/client/$(DEPDIR)/haltest-if-mce.Tpo -c -o android/client/haltest-if-mce.obj `if test -f 'android/client/if-mce.c'; then $(CYGPATH_W) 'android/client/if-mce.c'; else $(CYGPATH_W) '$(srcdir)/android/client/if-mce.c'; fi` @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) android/client/$(DEPDIR)/haltest-if-mce.Tpo android/client/$(DEPDIR)/haltest-if-mce.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='android/client/if-mce.c' object='android/client/haltest-if-mce.obj' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(android_haltest_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o android/client/haltest-if-mce.obj `if test -f 'android/client/if-mce.c'; then $(CYGPATH_W) 'android/client/if-mce.c'; else $(CYGPATH_W) '$(srcdir)/android/client/if-mce.c'; fi` android/hardware/haltest-hardware.o: android/hardware/hardware.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(android_haltest_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT android/hardware/haltest-hardware.o -MD -MP -MF android/hardware/$(DEPDIR)/haltest-hardware.Tpo -c -o android/hardware/haltest-hardware.o `test -f 'android/hardware/hardware.c' || echo '$(srcdir)/'`android/hardware/hardware.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) android/hardware/$(DEPDIR)/haltest-hardware.Tpo android/hardware/$(DEPDIR)/haltest-hardware.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='android/hardware/hardware.c' object='android/hardware/haltest-hardware.o' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(android_haltest_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o android/hardware/haltest-hardware.o `test -f 'android/hardware/hardware.c' || echo '$(srcdir)/'`android/hardware/hardware.c android/hardware/haltest-hardware.obj: android/hardware/hardware.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(android_haltest_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT android/hardware/haltest-hardware.obj -MD -MP -MF android/hardware/$(DEPDIR)/haltest-hardware.Tpo -c -o android/hardware/haltest-hardware.obj `if test -f 'android/hardware/hardware.c'; then $(CYGPATH_W) 'android/hardware/hardware.c'; else $(CYGPATH_W) '$(srcdir)/android/hardware/hardware.c'; fi` @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) android/hardware/$(DEPDIR)/haltest-hardware.Tpo android/hardware/$(DEPDIR)/haltest-hardware.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='android/hardware/hardware.c' object='android/hardware/haltest-hardware.obj' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(android_haltest_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o android/hardware/haltest-hardware.obj `if test -f 'android/hardware/hardware.c'; then $(CYGPATH_W) 'android/hardware/hardware.c'; else $(CYGPATH_W) '$(srcdir)/android/hardware/hardware.c'; fi` android/haltest-hal-utils.o: android/hal-utils.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(android_haltest_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT android/haltest-hal-utils.o -MD -MP -MF android/$(DEPDIR)/haltest-hal-utils.Tpo -c -o android/haltest-hal-utils.o `test -f 'android/hal-utils.c' || echo '$(srcdir)/'`android/hal-utils.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) android/$(DEPDIR)/haltest-hal-utils.Tpo android/$(DEPDIR)/haltest-hal-utils.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='android/hal-utils.c' object='android/haltest-hal-utils.o' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(android_haltest_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o android/haltest-hal-utils.o `test -f 'android/hal-utils.c' || echo '$(srcdir)/'`android/hal-utils.c android/haltest-hal-utils.obj: android/hal-utils.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(android_haltest_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT android/haltest-hal-utils.obj -MD -MP -MF android/$(DEPDIR)/haltest-hal-utils.Tpo -c -o android/haltest-hal-utils.obj `if test -f 'android/hal-utils.c'; then $(CYGPATH_W) 'android/hal-utils.c'; else $(CYGPATH_W) '$(srcdir)/android/hal-utils.c'; fi` @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) android/$(DEPDIR)/haltest-hal-utils.Tpo android/$(DEPDIR)/haltest-hal-utils.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='android/hal-utils.c' object='android/haltest-hal-utils.obj' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(android_haltest_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o android/haltest-hal-utils.obj `if test -f 'android/hal-utils.c'; then $(CYGPATH_W) 'android/hal-utils.c'; else $(CYGPATH_W) '$(srcdir)/android/hal-utils.c'; fi` emulator/android_ipc_tester-hciemu.o: emulator/hciemu.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(android_ipc_tester_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT emulator/android_ipc_tester-hciemu.o -MD -MP -MF emulator/$(DEPDIR)/android_ipc_tester-hciemu.Tpo -c -o emulator/android_ipc_tester-hciemu.o `test -f 'emulator/hciemu.c' || echo '$(srcdir)/'`emulator/hciemu.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) emulator/$(DEPDIR)/android_ipc_tester-hciemu.Tpo emulator/$(DEPDIR)/android_ipc_tester-hciemu.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='emulator/hciemu.c' object='emulator/android_ipc_tester-hciemu.o' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(android_ipc_tester_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o emulator/android_ipc_tester-hciemu.o `test -f 'emulator/hciemu.c' || echo '$(srcdir)/'`emulator/hciemu.c emulator/android_ipc_tester-hciemu.obj: emulator/hciemu.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(android_ipc_tester_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT emulator/android_ipc_tester-hciemu.obj -MD -MP -MF emulator/$(DEPDIR)/android_ipc_tester-hciemu.Tpo -c -o emulator/android_ipc_tester-hciemu.obj `if test -f 'emulator/hciemu.c'; then $(CYGPATH_W) 'emulator/hciemu.c'; else $(CYGPATH_W) '$(srcdir)/emulator/hciemu.c'; fi` @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) emulator/$(DEPDIR)/android_ipc_tester-hciemu.Tpo emulator/$(DEPDIR)/android_ipc_tester-hciemu.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='emulator/hciemu.c' object='emulator/android_ipc_tester-hciemu.obj' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(android_ipc_tester_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o emulator/android_ipc_tester-hciemu.obj `if test -f 'emulator/hciemu.c'; then $(CYGPATH_W) 'emulator/hciemu.c'; else $(CYGPATH_W) '$(srcdir)/emulator/hciemu.c'; fi` emulator/android_ipc_tester-vhci.o: emulator/vhci.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(android_ipc_tester_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT emulator/android_ipc_tester-vhci.o -MD -MP -MF emulator/$(DEPDIR)/android_ipc_tester-vhci.Tpo -c -o emulator/android_ipc_tester-vhci.o `test -f 'emulator/vhci.c' || echo '$(srcdir)/'`emulator/vhci.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) emulator/$(DEPDIR)/android_ipc_tester-vhci.Tpo emulator/$(DEPDIR)/android_ipc_tester-vhci.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='emulator/vhci.c' object='emulator/android_ipc_tester-vhci.o' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(android_ipc_tester_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o emulator/android_ipc_tester-vhci.o `test -f 'emulator/vhci.c' || echo '$(srcdir)/'`emulator/vhci.c emulator/android_ipc_tester-vhci.obj: emulator/vhci.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(android_ipc_tester_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT emulator/android_ipc_tester-vhci.obj -MD -MP -MF emulator/$(DEPDIR)/android_ipc_tester-vhci.Tpo -c -o emulator/android_ipc_tester-vhci.obj `if test -f 'emulator/vhci.c'; then $(CYGPATH_W) 'emulator/vhci.c'; else $(CYGPATH_W) '$(srcdir)/emulator/vhci.c'; fi` @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) emulator/$(DEPDIR)/android_ipc_tester-vhci.Tpo emulator/$(DEPDIR)/android_ipc_tester-vhci.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='emulator/vhci.c' object='emulator/android_ipc_tester-vhci.obj' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(android_ipc_tester_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o emulator/android_ipc_tester-vhci.obj `if test -f 'emulator/vhci.c'; then $(CYGPATH_W) 'emulator/vhci.c'; else $(CYGPATH_W) '$(srcdir)/emulator/vhci.c'; fi` emulator/android_ipc_tester-btdev.o: emulator/btdev.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(android_ipc_tester_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT emulator/android_ipc_tester-btdev.o -MD -MP -MF emulator/$(DEPDIR)/android_ipc_tester-btdev.Tpo -c -o emulator/android_ipc_tester-btdev.o `test -f 'emulator/btdev.c' || echo '$(srcdir)/'`emulator/btdev.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) emulator/$(DEPDIR)/android_ipc_tester-btdev.Tpo emulator/$(DEPDIR)/android_ipc_tester-btdev.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='emulator/btdev.c' object='emulator/android_ipc_tester-btdev.o' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(android_ipc_tester_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o emulator/android_ipc_tester-btdev.o `test -f 'emulator/btdev.c' || echo '$(srcdir)/'`emulator/btdev.c emulator/android_ipc_tester-btdev.obj: emulator/btdev.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(android_ipc_tester_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT emulator/android_ipc_tester-btdev.obj -MD -MP -MF emulator/$(DEPDIR)/android_ipc_tester-btdev.Tpo -c -o emulator/android_ipc_tester-btdev.obj `if test -f 'emulator/btdev.c'; then $(CYGPATH_W) 'emulator/btdev.c'; else $(CYGPATH_W) '$(srcdir)/emulator/btdev.c'; fi` @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) emulator/$(DEPDIR)/android_ipc_tester-btdev.Tpo emulator/$(DEPDIR)/android_ipc_tester-btdev.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='emulator/btdev.c' object='emulator/android_ipc_tester-btdev.obj' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(android_ipc_tester_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o emulator/android_ipc_tester-btdev.obj `if test -f 'emulator/btdev.c'; then $(CYGPATH_W) 'emulator/btdev.c'; else $(CYGPATH_W) '$(srcdir)/emulator/btdev.c'; fi` emulator/android_ipc_tester-bthost.o: emulator/bthost.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(android_ipc_tester_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT emulator/android_ipc_tester-bthost.o -MD -MP -MF emulator/$(DEPDIR)/android_ipc_tester-bthost.Tpo -c -o emulator/android_ipc_tester-bthost.o `test -f 'emulator/bthost.c' || echo '$(srcdir)/'`emulator/bthost.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) emulator/$(DEPDIR)/android_ipc_tester-bthost.Tpo emulator/$(DEPDIR)/android_ipc_tester-bthost.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='emulator/bthost.c' object='emulator/android_ipc_tester-bthost.o' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(android_ipc_tester_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o emulator/android_ipc_tester-bthost.o `test -f 'emulator/bthost.c' || echo '$(srcdir)/'`emulator/bthost.c emulator/android_ipc_tester-bthost.obj: emulator/bthost.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(android_ipc_tester_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT emulator/android_ipc_tester-bthost.obj -MD -MP -MF emulator/$(DEPDIR)/android_ipc_tester-bthost.Tpo -c -o emulator/android_ipc_tester-bthost.obj `if test -f 'emulator/bthost.c'; then $(CYGPATH_W) 'emulator/bthost.c'; else $(CYGPATH_W) '$(srcdir)/emulator/bthost.c'; fi` @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) emulator/$(DEPDIR)/android_ipc_tester-bthost.Tpo emulator/$(DEPDIR)/android_ipc_tester-bthost.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='emulator/bthost.c' object='emulator/android_ipc_tester-bthost.obj' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(android_ipc_tester_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o emulator/android_ipc_tester-bthost.obj `if test -f 'emulator/bthost.c'; then $(CYGPATH_W) 'emulator/bthost.c'; else $(CYGPATH_W) '$(srcdir)/emulator/bthost.c'; fi` emulator/android_ipc_tester-smp.o: emulator/smp.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(android_ipc_tester_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT emulator/android_ipc_tester-smp.o -MD -MP -MF emulator/$(DEPDIR)/android_ipc_tester-smp.Tpo -c -o emulator/android_ipc_tester-smp.o `test -f 'emulator/smp.c' || echo '$(srcdir)/'`emulator/smp.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) emulator/$(DEPDIR)/android_ipc_tester-smp.Tpo emulator/$(DEPDIR)/android_ipc_tester-smp.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='emulator/smp.c' object='emulator/android_ipc_tester-smp.o' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(android_ipc_tester_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o emulator/android_ipc_tester-smp.o `test -f 'emulator/smp.c' || echo '$(srcdir)/'`emulator/smp.c emulator/android_ipc_tester-smp.obj: emulator/smp.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(android_ipc_tester_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT emulator/android_ipc_tester-smp.obj -MD -MP -MF emulator/$(DEPDIR)/android_ipc_tester-smp.Tpo -c -o emulator/android_ipc_tester-smp.obj `if test -f 'emulator/smp.c'; then $(CYGPATH_W) 'emulator/smp.c'; else $(CYGPATH_W) '$(srcdir)/emulator/smp.c'; fi` @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) emulator/$(DEPDIR)/android_ipc_tester-smp.Tpo emulator/$(DEPDIR)/android_ipc_tester-smp.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='emulator/smp.c' object='emulator/android_ipc_tester-smp.obj' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(android_ipc_tester_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o emulator/android_ipc_tester-smp.obj `if test -f 'emulator/smp.c'; then $(CYGPATH_W) 'emulator/smp.c'; else $(CYGPATH_W) '$(srcdir)/emulator/smp.c'; fi` android/ipc_tester-hal-utils.o: android/hal-utils.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(android_ipc_tester_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT android/ipc_tester-hal-utils.o -MD -MP -MF android/$(DEPDIR)/ipc_tester-hal-utils.Tpo -c -o android/ipc_tester-hal-utils.o `test -f 'android/hal-utils.c' || echo '$(srcdir)/'`android/hal-utils.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) android/$(DEPDIR)/ipc_tester-hal-utils.Tpo android/$(DEPDIR)/ipc_tester-hal-utils.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='android/hal-utils.c' object='android/ipc_tester-hal-utils.o' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(android_ipc_tester_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o android/ipc_tester-hal-utils.o `test -f 'android/hal-utils.c' || echo '$(srcdir)/'`android/hal-utils.c android/ipc_tester-hal-utils.obj: android/hal-utils.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(android_ipc_tester_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT android/ipc_tester-hal-utils.obj -MD -MP -MF android/$(DEPDIR)/ipc_tester-hal-utils.Tpo -c -o android/ipc_tester-hal-utils.obj `if test -f 'android/hal-utils.c'; then $(CYGPATH_W) 'android/hal-utils.c'; else $(CYGPATH_W) '$(srcdir)/android/hal-utils.c'; fi` @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) android/$(DEPDIR)/ipc_tester-hal-utils.Tpo android/$(DEPDIR)/ipc_tester-hal-utils.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='android/hal-utils.c' object='android/ipc_tester-hal-utils.obj' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(android_ipc_tester_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o android/ipc_tester-hal-utils.obj `if test -f 'android/hal-utils.c'; then $(CYGPATH_W) 'android/hal-utils.c'; else $(CYGPATH_W) '$(srcdir)/android/hal-utils.c'; fi` android/ipc_tester-ipc-tester.o: android/ipc-tester.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(android_ipc_tester_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT android/ipc_tester-ipc-tester.o -MD -MP -MF android/$(DEPDIR)/ipc_tester-ipc-tester.Tpo -c -o android/ipc_tester-ipc-tester.o `test -f 'android/ipc-tester.c' || echo '$(srcdir)/'`android/ipc-tester.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) android/$(DEPDIR)/ipc_tester-ipc-tester.Tpo android/$(DEPDIR)/ipc_tester-ipc-tester.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='android/ipc-tester.c' object='android/ipc_tester-ipc-tester.o' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(android_ipc_tester_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o android/ipc_tester-ipc-tester.o `test -f 'android/ipc-tester.c' || echo '$(srcdir)/'`android/ipc-tester.c android/ipc_tester-ipc-tester.obj: android/ipc-tester.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(android_ipc_tester_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT android/ipc_tester-ipc-tester.obj -MD -MP -MF android/$(DEPDIR)/ipc_tester-ipc-tester.Tpo -c -o android/ipc_tester-ipc-tester.obj `if test -f 'android/ipc-tester.c'; then $(CYGPATH_W) 'android/ipc-tester.c'; else $(CYGPATH_W) '$(srcdir)/android/ipc-tester.c'; fi` @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) android/$(DEPDIR)/ipc_tester-ipc-tester.Tpo android/$(DEPDIR)/ipc_tester-ipc-tester.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='android/ipc-tester.c' object='android/ipc_tester-ipc-tester.obj' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(android_ipc_tester_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o android/ipc_tester-ipc-tester.obj `if test -f 'android/ipc-tester.c'; then $(CYGPATH_W) 'android/ipc-tester.c'; else $(CYGPATH_W) '$(srcdir)/android/ipc-tester.c'; fi` btio/obexd-btio.o: btio/btio.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(obexd_src_obexd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT btio/obexd-btio.o -MD -MP -MF btio/$(DEPDIR)/obexd-btio.Tpo -c -o btio/obexd-btio.o `test -f 'btio/btio.c' || echo '$(srcdir)/'`btio/btio.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) btio/$(DEPDIR)/obexd-btio.Tpo btio/$(DEPDIR)/obexd-btio.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='btio/btio.c' object='btio/obexd-btio.o' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(obexd_src_obexd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o btio/obexd-btio.o `test -f 'btio/btio.c' || echo '$(srcdir)/'`btio/btio.c btio/obexd-btio.obj: btio/btio.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(obexd_src_obexd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT btio/obexd-btio.obj -MD -MP -MF btio/$(DEPDIR)/obexd-btio.Tpo -c -o btio/obexd-btio.obj `if test -f 'btio/btio.c'; then $(CYGPATH_W) 'btio/btio.c'; else $(CYGPATH_W) '$(srcdir)/btio/btio.c'; fi` @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) btio/$(DEPDIR)/obexd-btio.Tpo btio/$(DEPDIR)/obexd-btio.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='btio/btio.c' object='btio/obexd-btio.obj' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(obexd_src_obexd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o btio/obexd-btio.obj `if test -f 'btio/btio.c'; then $(CYGPATH_W) 'btio/btio.c'; else $(CYGPATH_W) '$(srcdir)/btio/btio.c'; fi` gobex/obexd-gobex.o: gobex/gobex.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(obexd_src_obexd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT gobex/obexd-gobex.o -MD -MP -MF gobex/$(DEPDIR)/obexd-gobex.Tpo -c -o gobex/obexd-gobex.o `test -f 'gobex/gobex.c' || echo '$(srcdir)/'`gobex/gobex.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) gobex/$(DEPDIR)/obexd-gobex.Tpo gobex/$(DEPDIR)/obexd-gobex.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='gobex/gobex.c' object='gobex/obexd-gobex.o' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(obexd_src_obexd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o gobex/obexd-gobex.o `test -f 'gobex/gobex.c' || echo '$(srcdir)/'`gobex/gobex.c gobex/obexd-gobex.obj: gobex/gobex.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(obexd_src_obexd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT gobex/obexd-gobex.obj -MD -MP -MF gobex/$(DEPDIR)/obexd-gobex.Tpo -c -o gobex/obexd-gobex.obj `if test -f 'gobex/gobex.c'; then $(CYGPATH_W) 'gobex/gobex.c'; else $(CYGPATH_W) '$(srcdir)/gobex/gobex.c'; fi` @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) gobex/$(DEPDIR)/obexd-gobex.Tpo gobex/$(DEPDIR)/obexd-gobex.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='gobex/gobex.c' object='gobex/obexd-gobex.obj' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(obexd_src_obexd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o gobex/obexd-gobex.obj `if test -f 'gobex/gobex.c'; then $(CYGPATH_W) 'gobex/gobex.c'; else $(CYGPATH_W) '$(srcdir)/gobex/gobex.c'; fi` gobex/obexd-gobex-defs.o: gobex/gobex-defs.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(obexd_src_obexd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT gobex/obexd-gobex-defs.o -MD -MP -MF gobex/$(DEPDIR)/obexd-gobex-defs.Tpo -c -o gobex/obexd-gobex-defs.o `test -f 'gobex/gobex-defs.c' || echo '$(srcdir)/'`gobex/gobex-defs.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) gobex/$(DEPDIR)/obexd-gobex-defs.Tpo gobex/$(DEPDIR)/obexd-gobex-defs.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='gobex/gobex-defs.c' object='gobex/obexd-gobex-defs.o' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(obexd_src_obexd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o gobex/obexd-gobex-defs.o `test -f 'gobex/gobex-defs.c' || echo '$(srcdir)/'`gobex/gobex-defs.c gobex/obexd-gobex-defs.obj: gobex/gobex-defs.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(obexd_src_obexd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT gobex/obexd-gobex-defs.obj -MD -MP -MF gobex/$(DEPDIR)/obexd-gobex-defs.Tpo -c -o gobex/obexd-gobex-defs.obj `if test -f 'gobex/gobex-defs.c'; then $(CYGPATH_W) 'gobex/gobex-defs.c'; else $(CYGPATH_W) '$(srcdir)/gobex/gobex-defs.c'; fi` @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) gobex/$(DEPDIR)/obexd-gobex-defs.Tpo gobex/$(DEPDIR)/obexd-gobex-defs.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='gobex/gobex-defs.c' object='gobex/obexd-gobex-defs.obj' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(obexd_src_obexd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o gobex/obexd-gobex-defs.obj `if test -f 'gobex/gobex-defs.c'; then $(CYGPATH_W) 'gobex/gobex-defs.c'; else $(CYGPATH_W) '$(srcdir)/gobex/gobex-defs.c'; fi` gobex/obexd-gobex-packet.o: gobex/gobex-packet.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(obexd_src_obexd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT gobex/obexd-gobex-packet.o -MD -MP -MF gobex/$(DEPDIR)/obexd-gobex-packet.Tpo -c -o gobex/obexd-gobex-packet.o `test -f 'gobex/gobex-packet.c' || echo '$(srcdir)/'`gobex/gobex-packet.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) gobex/$(DEPDIR)/obexd-gobex-packet.Tpo gobex/$(DEPDIR)/obexd-gobex-packet.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='gobex/gobex-packet.c' object='gobex/obexd-gobex-packet.o' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(obexd_src_obexd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o gobex/obexd-gobex-packet.o `test -f 'gobex/gobex-packet.c' || echo '$(srcdir)/'`gobex/gobex-packet.c gobex/obexd-gobex-packet.obj: gobex/gobex-packet.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(obexd_src_obexd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT gobex/obexd-gobex-packet.obj -MD -MP -MF gobex/$(DEPDIR)/obexd-gobex-packet.Tpo -c -o gobex/obexd-gobex-packet.obj `if test -f 'gobex/gobex-packet.c'; then $(CYGPATH_W) 'gobex/gobex-packet.c'; else $(CYGPATH_W) '$(srcdir)/gobex/gobex-packet.c'; fi` @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) gobex/$(DEPDIR)/obexd-gobex-packet.Tpo gobex/$(DEPDIR)/obexd-gobex-packet.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='gobex/gobex-packet.c' object='gobex/obexd-gobex-packet.obj' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(obexd_src_obexd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o gobex/obexd-gobex-packet.obj `if test -f 'gobex/gobex-packet.c'; then $(CYGPATH_W) 'gobex/gobex-packet.c'; else $(CYGPATH_W) '$(srcdir)/gobex/gobex-packet.c'; fi` gobex/obexd-gobex-header.o: gobex/gobex-header.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(obexd_src_obexd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT gobex/obexd-gobex-header.o -MD -MP -MF gobex/$(DEPDIR)/obexd-gobex-header.Tpo -c -o gobex/obexd-gobex-header.o `test -f 'gobex/gobex-header.c' || echo '$(srcdir)/'`gobex/gobex-header.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) gobex/$(DEPDIR)/obexd-gobex-header.Tpo gobex/$(DEPDIR)/obexd-gobex-header.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='gobex/gobex-header.c' object='gobex/obexd-gobex-header.o' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(obexd_src_obexd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o gobex/obexd-gobex-header.o `test -f 'gobex/gobex-header.c' || echo '$(srcdir)/'`gobex/gobex-header.c gobex/obexd-gobex-header.obj: gobex/gobex-header.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(obexd_src_obexd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT gobex/obexd-gobex-header.obj -MD -MP -MF gobex/$(DEPDIR)/obexd-gobex-header.Tpo -c -o gobex/obexd-gobex-header.obj `if test -f 'gobex/gobex-header.c'; then $(CYGPATH_W) 'gobex/gobex-header.c'; else $(CYGPATH_W) '$(srcdir)/gobex/gobex-header.c'; fi` @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) gobex/$(DEPDIR)/obexd-gobex-header.Tpo gobex/$(DEPDIR)/obexd-gobex-header.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='gobex/gobex-header.c' object='gobex/obexd-gobex-header.obj' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(obexd_src_obexd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o gobex/obexd-gobex-header.obj `if test -f 'gobex/gobex-header.c'; then $(CYGPATH_W) 'gobex/gobex-header.c'; else $(CYGPATH_W) '$(srcdir)/gobex/gobex-header.c'; fi` gobex/obexd-gobex-transfer.o: gobex/gobex-transfer.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(obexd_src_obexd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT gobex/obexd-gobex-transfer.o -MD -MP -MF gobex/$(DEPDIR)/obexd-gobex-transfer.Tpo -c -o gobex/obexd-gobex-transfer.o `test -f 'gobex/gobex-transfer.c' || echo '$(srcdir)/'`gobex/gobex-transfer.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) gobex/$(DEPDIR)/obexd-gobex-transfer.Tpo gobex/$(DEPDIR)/obexd-gobex-transfer.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='gobex/gobex-transfer.c' object='gobex/obexd-gobex-transfer.o' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(obexd_src_obexd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o gobex/obexd-gobex-transfer.o `test -f 'gobex/gobex-transfer.c' || echo '$(srcdir)/'`gobex/gobex-transfer.c gobex/obexd-gobex-transfer.obj: gobex/gobex-transfer.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(obexd_src_obexd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT gobex/obexd-gobex-transfer.obj -MD -MP -MF gobex/$(DEPDIR)/obexd-gobex-transfer.Tpo -c -o gobex/obexd-gobex-transfer.obj `if test -f 'gobex/gobex-transfer.c'; then $(CYGPATH_W) 'gobex/gobex-transfer.c'; else $(CYGPATH_W) '$(srcdir)/gobex/gobex-transfer.c'; fi` @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) gobex/$(DEPDIR)/obexd-gobex-transfer.Tpo gobex/$(DEPDIR)/obexd-gobex-transfer.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='gobex/gobex-transfer.c' object='gobex/obexd-gobex-transfer.obj' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(obexd_src_obexd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o gobex/obexd-gobex-transfer.obj `if test -f 'gobex/gobex-transfer.c'; then $(CYGPATH_W) 'gobex/gobex-transfer.c'; else $(CYGPATH_W) '$(srcdir)/gobex/gobex-transfer.c'; fi` gobex/obexd-gobex-apparam.o: gobex/gobex-apparam.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(obexd_src_obexd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT gobex/obexd-gobex-apparam.o -MD -MP -MF gobex/$(DEPDIR)/obexd-gobex-apparam.Tpo -c -o gobex/obexd-gobex-apparam.o `test -f 'gobex/gobex-apparam.c' || echo '$(srcdir)/'`gobex/gobex-apparam.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) gobex/$(DEPDIR)/obexd-gobex-apparam.Tpo gobex/$(DEPDIR)/obexd-gobex-apparam.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='gobex/gobex-apparam.c' object='gobex/obexd-gobex-apparam.o' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(obexd_src_obexd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o gobex/obexd-gobex-apparam.o `test -f 'gobex/gobex-apparam.c' || echo '$(srcdir)/'`gobex/gobex-apparam.c gobex/obexd-gobex-apparam.obj: gobex/gobex-apparam.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(obexd_src_obexd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT gobex/obexd-gobex-apparam.obj -MD -MP -MF gobex/$(DEPDIR)/obexd-gobex-apparam.Tpo -c -o gobex/obexd-gobex-apparam.obj `if test -f 'gobex/gobex-apparam.c'; then $(CYGPATH_W) 'gobex/gobex-apparam.c'; else $(CYGPATH_W) '$(srcdir)/gobex/gobex-apparam.c'; fi` @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) gobex/$(DEPDIR)/obexd-gobex-apparam.Tpo gobex/$(DEPDIR)/obexd-gobex-apparam.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='gobex/gobex-apparam.c' object='gobex/obexd-gobex-apparam.obj' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(obexd_src_obexd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o gobex/obexd-gobex-apparam.obj `if test -f 'gobex/gobex-apparam.c'; then $(CYGPATH_W) 'gobex/gobex-apparam.c'; else $(CYGPATH_W) '$(srcdir)/gobex/gobex-apparam.c'; fi` obexd/plugins/obexd-filesystem.o: obexd/plugins/filesystem.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(obexd_src_obexd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT obexd/plugins/obexd-filesystem.o -MD -MP -MF obexd/plugins/$(DEPDIR)/obexd-filesystem.Tpo -c -o obexd/plugins/obexd-filesystem.o `test -f 'obexd/plugins/filesystem.c' || echo '$(srcdir)/'`obexd/plugins/filesystem.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) obexd/plugins/$(DEPDIR)/obexd-filesystem.Tpo obexd/plugins/$(DEPDIR)/obexd-filesystem.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='obexd/plugins/filesystem.c' object='obexd/plugins/obexd-filesystem.o' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(obexd_src_obexd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o obexd/plugins/obexd-filesystem.o `test -f 'obexd/plugins/filesystem.c' || echo '$(srcdir)/'`obexd/plugins/filesystem.c obexd/plugins/obexd-filesystem.obj: obexd/plugins/filesystem.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(obexd_src_obexd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT obexd/plugins/obexd-filesystem.obj -MD -MP -MF obexd/plugins/$(DEPDIR)/obexd-filesystem.Tpo -c -o obexd/plugins/obexd-filesystem.obj `if test -f 'obexd/plugins/filesystem.c'; then $(CYGPATH_W) 'obexd/plugins/filesystem.c'; else $(CYGPATH_W) '$(srcdir)/obexd/plugins/filesystem.c'; fi` @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) obexd/plugins/$(DEPDIR)/obexd-filesystem.Tpo obexd/plugins/$(DEPDIR)/obexd-filesystem.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='obexd/plugins/filesystem.c' object='obexd/plugins/obexd-filesystem.obj' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(obexd_src_obexd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o obexd/plugins/obexd-filesystem.obj `if test -f 'obexd/plugins/filesystem.c'; then $(CYGPATH_W) 'obexd/plugins/filesystem.c'; else $(CYGPATH_W) '$(srcdir)/obexd/plugins/filesystem.c'; fi` obexd/plugins/obexd-bluetooth.o: obexd/plugins/bluetooth.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(obexd_src_obexd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT obexd/plugins/obexd-bluetooth.o -MD -MP -MF obexd/plugins/$(DEPDIR)/obexd-bluetooth.Tpo -c -o obexd/plugins/obexd-bluetooth.o `test -f 'obexd/plugins/bluetooth.c' || echo '$(srcdir)/'`obexd/plugins/bluetooth.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) obexd/plugins/$(DEPDIR)/obexd-bluetooth.Tpo obexd/plugins/$(DEPDIR)/obexd-bluetooth.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='obexd/plugins/bluetooth.c' object='obexd/plugins/obexd-bluetooth.o' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(obexd_src_obexd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o obexd/plugins/obexd-bluetooth.o `test -f 'obexd/plugins/bluetooth.c' || echo '$(srcdir)/'`obexd/plugins/bluetooth.c obexd/plugins/obexd-bluetooth.obj: obexd/plugins/bluetooth.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(obexd_src_obexd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT obexd/plugins/obexd-bluetooth.obj -MD -MP -MF obexd/plugins/$(DEPDIR)/obexd-bluetooth.Tpo -c -o obexd/plugins/obexd-bluetooth.obj `if test -f 'obexd/plugins/bluetooth.c'; then $(CYGPATH_W) 'obexd/plugins/bluetooth.c'; else $(CYGPATH_W) '$(srcdir)/obexd/plugins/bluetooth.c'; fi` @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) obexd/plugins/$(DEPDIR)/obexd-bluetooth.Tpo obexd/plugins/$(DEPDIR)/obexd-bluetooth.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='obexd/plugins/bluetooth.c' object='obexd/plugins/obexd-bluetooth.obj' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(obexd_src_obexd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o obexd/plugins/obexd-bluetooth.obj `if test -f 'obexd/plugins/bluetooth.c'; then $(CYGPATH_W) 'obexd/plugins/bluetooth.c'; else $(CYGPATH_W) '$(srcdir)/obexd/plugins/bluetooth.c'; fi` obexd/plugins/obexd-pcsuite.o: obexd/plugins/pcsuite.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(obexd_src_obexd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT obexd/plugins/obexd-pcsuite.o -MD -MP -MF obexd/plugins/$(DEPDIR)/obexd-pcsuite.Tpo -c -o obexd/plugins/obexd-pcsuite.o `test -f 'obexd/plugins/pcsuite.c' || echo '$(srcdir)/'`obexd/plugins/pcsuite.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) obexd/plugins/$(DEPDIR)/obexd-pcsuite.Tpo obexd/plugins/$(DEPDIR)/obexd-pcsuite.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='obexd/plugins/pcsuite.c' object='obexd/plugins/obexd-pcsuite.o' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(obexd_src_obexd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o obexd/plugins/obexd-pcsuite.o `test -f 'obexd/plugins/pcsuite.c' || echo '$(srcdir)/'`obexd/plugins/pcsuite.c obexd/plugins/obexd-pcsuite.obj: obexd/plugins/pcsuite.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(obexd_src_obexd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT obexd/plugins/obexd-pcsuite.obj -MD -MP -MF obexd/plugins/$(DEPDIR)/obexd-pcsuite.Tpo -c -o obexd/plugins/obexd-pcsuite.obj `if test -f 'obexd/plugins/pcsuite.c'; then $(CYGPATH_W) 'obexd/plugins/pcsuite.c'; else $(CYGPATH_W) '$(srcdir)/obexd/plugins/pcsuite.c'; fi` @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) obexd/plugins/$(DEPDIR)/obexd-pcsuite.Tpo obexd/plugins/$(DEPDIR)/obexd-pcsuite.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='obexd/plugins/pcsuite.c' object='obexd/plugins/obexd-pcsuite.obj' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(obexd_src_obexd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o obexd/plugins/obexd-pcsuite.obj `if test -f 'obexd/plugins/pcsuite.c'; then $(CYGPATH_W) 'obexd/plugins/pcsuite.c'; else $(CYGPATH_W) '$(srcdir)/obexd/plugins/pcsuite.c'; fi` obexd/plugins/obexd-opp.o: obexd/plugins/opp.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(obexd_src_obexd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT obexd/plugins/obexd-opp.o -MD -MP -MF obexd/plugins/$(DEPDIR)/obexd-opp.Tpo -c -o obexd/plugins/obexd-opp.o `test -f 'obexd/plugins/opp.c' || echo '$(srcdir)/'`obexd/plugins/opp.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) obexd/plugins/$(DEPDIR)/obexd-opp.Tpo obexd/plugins/$(DEPDIR)/obexd-opp.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='obexd/plugins/opp.c' object='obexd/plugins/obexd-opp.o' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(obexd_src_obexd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o obexd/plugins/obexd-opp.o `test -f 'obexd/plugins/opp.c' || echo '$(srcdir)/'`obexd/plugins/opp.c obexd/plugins/obexd-opp.obj: obexd/plugins/opp.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(obexd_src_obexd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT obexd/plugins/obexd-opp.obj -MD -MP -MF obexd/plugins/$(DEPDIR)/obexd-opp.Tpo -c -o obexd/plugins/obexd-opp.obj `if test -f 'obexd/plugins/opp.c'; then $(CYGPATH_W) 'obexd/plugins/opp.c'; else $(CYGPATH_W) '$(srcdir)/obexd/plugins/opp.c'; fi` @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) obexd/plugins/$(DEPDIR)/obexd-opp.Tpo obexd/plugins/$(DEPDIR)/obexd-opp.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='obexd/plugins/opp.c' object='obexd/plugins/obexd-opp.obj' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(obexd_src_obexd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o obexd/plugins/obexd-opp.obj `if test -f 'obexd/plugins/opp.c'; then $(CYGPATH_W) 'obexd/plugins/opp.c'; else $(CYGPATH_W) '$(srcdir)/obexd/plugins/opp.c'; fi` obexd/plugins/obexd-ftp.o: obexd/plugins/ftp.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(obexd_src_obexd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT obexd/plugins/obexd-ftp.o -MD -MP -MF obexd/plugins/$(DEPDIR)/obexd-ftp.Tpo -c -o obexd/plugins/obexd-ftp.o `test -f 'obexd/plugins/ftp.c' || echo '$(srcdir)/'`obexd/plugins/ftp.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) obexd/plugins/$(DEPDIR)/obexd-ftp.Tpo obexd/plugins/$(DEPDIR)/obexd-ftp.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='obexd/plugins/ftp.c' object='obexd/plugins/obexd-ftp.o' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(obexd_src_obexd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o obexd/plugins/obexd-ftp.o `test -f 'obexd/plugins/ftp.c' || echo '$(srcdir)/'`obexd/plugins/ftp.c obexd/plugins/obexd-ftp.obj: obexd/plugins/ftp.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(obexd_src_obexd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT obexd/plugins/obexd-ftp.obj -MD -MP -MF obexd/plugins/$(DEPDIR)/obexd-ftp.Tpo -c -o obexd/plugins/obexd-ftp.obj `if test -f 'obexd/plugins/ftp.c'; then $(CYGPATH_W) 'obexd/plugins/ftp.c'; else $(CYGPATH_W) '$(srcdir)/obexd/plugins/ftp.c'; fi` @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) obexd/plugins/$(DEPDIR)/obexd-ftp.Tpo obexd/plugins/$(DEPDIR)/obexd-ftp.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='obexd/plugins/ftp.c' object='obexd/plugins/obexd-ftp.obj' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(obexd_src_obexd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o obexd/plugins/obexd-ftp.obj `if test -f 'obexd/plugins/ftp.c'; then $(CYGPATH_W) 'obexd/plugins/ftp.c'; else $(CYGPATH_W) '$(srcdir)/obexd/plugins/ftp.c'; fi` obexd/plugins/obexd-irmc.o: obexd/plugins/irmc.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(obexd_src_obexd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT obexd/plugins/obexd-irmc.o -MD -MP -MF obexd/plugins/$(DEPDIR)/obexd-irmc.Tpo -c -o obexd/plugins/obexd-irmc.o `test -f 'obexd/plugins/irmc.c' || echo '$(srcdir)/'`obexd/plugins/irmc.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) obexd/plugins/$(DEPDIR)/obexd-irmc.Tpo obexd/plugins/$(DEPDIR)/obexd-irmc.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='obexd/plugins/irmc.c' object='obexd/plugins/obexd-irmc.o' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(obexd_src_obexd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o obexd/plugins/obexd-irmc.o `test -f 'obexd/plugins/irmc.c' || echo '$(srcdir)/'`obexd/plugins/irmc.c obexd/plugins/obexd-irmc.obj: obexd/plugins/irmc.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(obexd_src_obexd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT obexd/plugins/obexd-irmc.obj -MD -MP -MF obexd/plugins/$(DEPDIR)/obexd-irmc.Tpo -c -o obexd/plugins/obexd-irmc.obj `if test -f 'obexd/plugins/irmc.c'; then $(CYGPATH_W) 'obexd/plugins/irmc.c'; else $(CYGPATH_W) '$(srcdir)/obexd/plugins/irmc.c'; fi` @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) obexd/plugins/$(DEPDIR)/obexd-irmc.Tpo obexd/plugins/$(DEPDIR)/obexd-irmc.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='obexd/plugins/irmc.c' object='obexd/plugins/obexd-irmc.obj' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(obexd_src_obexd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o obexd/plugins/obexd-irmc.obj `if test -f 'obexd/plugins/irmc.c'; then $(CYGPATH_W) 'obexd/plugins/irmc.c'; else $(CYGPATH_W) '$(srcdir)/obexd/plugins/irmc.c'; fi` obexd/plugins/obexd-pbap.o: obexd/plugins/pbap.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(obexd_src_obexd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT obexd/plugins/obexd-pbap.o -MD -MP -MF obexd/plugins/$(DEPDIR)/obexd-pbap.Tpo -c -o obexd/plugins/obexd-pbap.o `test -f 'obexd/plugins/pbap.c' || echo '$(srcdir)/'`obexd/plugins/pbap.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) obexd/plugins/$(DEPDIR)/obexd-pbap.Tpo obexd/plugins/$(DEPDIR)/obexd-pbap.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='obexd/plugins/pbap.c' object='obexd/plugins/obexd-pbap.o' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(obexd_src_obexd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o obexd/plugins/obexd-pbap.o `test -f 'obexd/plugins/pbap.c' || echo '$(srcdir)/'`obexd/plugins/pbap.c obexd/plugins/obexd-pbap.obj: obexd/plugins/pbap.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(obexd_src_obexd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT obexd/plugins/obexd-pbap.obj -MD -MP -MF obexd/plugins/$(DEPDIR)/obexd-pbap.Tpo -c -o obexd/plugins/obexd-pbap.obj `if test -f 'obexd/plugins/pbap.c'; then $(CYGPATH_W) 'obexd/plugins/pbap.c'; else $(CYGPATH_W) '$(srcdir)/obexd/plugins/pbap.c'; fi` @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) obexd/plugins/$(DEPDIR)/obexd-pbap.Tpo obexd/plugins/$(DEPDIR)/obexd-pbap.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='obexd/plugins/pbap.c' object='obexd/plugins/obexd-pbap.obj' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(obexd_src_obexd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o obexd/plugins/obexd-pbap.obj `if test -f 'obexd/plugins/pbap.c'; then $(CYGPATH_W) 'obexd/plugins/pbap.c'; else $(CYGPATH_W) '$(srcdir)/obexd/plugins/pbap.c'; fi` obexd/plugins/obexd-vcard.o: obexd/plugins/vcard.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(obexd_src_obexd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT obexd/plugins/obexd-vcard.o -MD -MP -MF obexd/plugins/$(DEPDIR)/obexd-vcard.Tpo -c -o obexd/plugins/obexd-vcard.o `test -f 'obexd/plugins/vcard.c' || echo '$(srcdir)/'`obexd/plugins/vcard.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) obexd/plugins/$(DEPDIR)/obexd-vcard.Tpo obexd/plugins/$(DEPDIR)/obexd-vcard.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='obexd/plugins/vcard.c' object='obexd/plugins/obexd-vcard.o' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(obexd_src_obexd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o obexd/plugins/obexd-vcard.o `test -f 'obexd/plugins/vcard.c' || echo '$(srcdir)/'`obexd/plugins/vcard.c obexd/plugins/obexd-vcard.obj: obexd/plugins/vcard.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(obexd_src_obexd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT obexd/plugins/obexd-vcard.obj -MD -MP -MF obexd/plugins/$(DEPDIR)/obexd-vcard.Tpo -c -o obexd/plugins/obexd-vcard.obj `if test -f 'obexd/plugins/vcard.c'; then $(CYGPATH_W) 'obexd/plugins/vcard.c'; else $(CYGPATH_W) '$(srcdir)/obexd/plugins/vcard.c'; fi` @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) obexd/plugins/$(DEPDIR)/obexd-vcard.Tpo obexd/plugins/$(DEPDIR)/obexd-vcard.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='obexd/plugins/vcard.c' object='obexd/plugins/obexd-vcard.obj' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(obexd_src_obexd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o obexd/plugins/obexd-vcard.obj `if test -f 'obexd/plugins/vcard.c'; then $(CYGPATH_W) 'obexd/plugins/vcard.c'; else $(CYGPATH_W) '$(srcdir)/obexd/plugins/vcard.c'; fi` obexd/plugins/obexd-phonebook-@PLUGIN_PHONEBOOK@.o: obexd/plugins/phonebook-@PLUGIN_PHONEBOOK@.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(obexd_src_obexd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT obexd/plugins/obexd-phonebook-@PLUGIN_PHONEBOOK@.o -MD -MP -MF obexd/plugins/$(DEPDIR)/obexd-phonebook-@PLUGIN_PHONEBOOK@.Tpo -c -o obexd/plugins/obexd-phonebook-@PLUGIN_PHONEBOOK@.o `test -f 'obexd/plugins/phonebook-@PLUGIN_PHONEBOOK@.c' || echo '$(srcdir)/'`obexd/plugins/phonebook-@PLUGIN_PHONEBOOK@.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) obexd/plugins/$(DEPDIR)/obexd-phonebook-@PLUGIN_PHONEBOOK@.Tpo obexd/plugins/$(DEPDIR)/obexd-phonebook-@PLUGIN_PHONEBOOK@.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='obexd/plugins/phonebook-@PLUGIN_PHONEBOOK@.c' object='obexd/plugins/obexd-phonebook-@PLUGIN_PHONEBOOK@.o' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(obexd_src_obexd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o obexd/plugins/obexd-phonebook-@PLUGIN_PHONEBOOK@.o `test -f 'obexd/plugins/phonebook-@PLUGIN_PHONEBOOK@.c' || echo '$(srcdir)/'`obexd/plugins/phonebook-@PLUGIN_PHONEBOOK@.c obexd/plugins/obexd-phonebook-@PLUGIN_PHONEBOOK@.obj: obexd/plugins/phonebook-@PLUGIN_PHONEBOOK@.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(obexd_src_obexd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT obexd/plugins/obexd-phonebook-@PLUGIN_PHONEBOOK@.obj -MD -MP -MF obexd/plugins/$(DEPDIR)/obexd-phonebook-@PLUGIN_PHONEBOOK@.Tpo -c -o obexd/plugins/obexd-phonebook-@PLUGIN_PHONEBOOK@.obj `if test -f 'obexd/plugins/phonebook-@PLUGIN_PHONEBOOK@.c'; then $(CYGPATH_W) 'obexd/plugins/phonebook-@PLUGIN_PHONEBOOK@.c'; else $(CYGPATH_W) '$(srcdir)/obexd/plugins/phonebook-@PLUGIN_PHONEBOOK@.c'; fi` @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) obexd/plugins/$(DEPDIR)/obexd-phonebook-@PLUGIN_PHONEBOOK@.Tpo obexd/plugins/$(DEPDIR)/obexd-phonebook-@PLUGIN_PHONEBOOK@.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='obexd/plugins/phonebook-@PLUGIN_PHONEBOOK@.c' object='obexd/plugins/obexd-phonebook-@PLUGIN_PHONEBOOK@.obj' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(obexd_src_obexd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o obexd/plugins/obexd-phonebook-@PLUGIN_PHONEBOOK@.obj `if test -f 'obexd/plugins/phonebook-@PLUGIN_PHONEBOOK@.c'; then $(CYGPATH_W) 'obexd/plugins/phonebook-@PLUGIN_PHONEBOOK@.c'; else $(CYGPATH_W) '$(srcdir)/obexd/plugins/phonebook-@PLUGIN_PHONEBOOK@.c'; fi` obexd/plugins/obexd-mas.o: obexd/plugins/mas.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(obexd_src_obexd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT obexd/plugins/obexd-mas.o -MD -MP -MF obexd/plugins/$(DEPDIR)/obexd-mas.Tpo -c -o obexd/plugins/obexd-mas.o `test -f 'obexd/plugins/mas.c' || echo '$(srcdir)/'`obexd/plugins/mas.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) obexd/plugins/$(DEPDIR)/obexd-mas.Tpo obexd/plugins/$(DEPDIR)/obexd-mas.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='obexd/plugins/mas.c' object='obexd/plugins/obexd-mas.o' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(obexd_src_obexd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o obexd/plugins/obexd-mas.o `test -f 'obexd/plugins/mas.c' || echo '$(srcdir)/'`obexd/plugins/mas.c obexd/plugins/obexd-mas.obj: obexd/plugins/mas.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(obexd_src_obexd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT obexd/plugins/obexd-mas.obj -MD -MP -MF obexd/plugins/$(DEPDIR)/obexd-mas.Tpo -c -o obexd/plugins/obexd-mas.obj `if test -f 'obexd/plugins/mas.c'; then $(CYGPATH_W) 'obexd/plugins/mas.c'; else $(CYGPATH_W) '$(srcdir)/obexd/plugins/mas.c'; fi` @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) obexd/plugins/$(DEPDIR)/obexd-mas.Tpo obexd/plugins/$(DEPDIR)/obexd-mas.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='obexd/plugins/mas.c' object='obexd/plugins/obexd-mas.obj' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(obexd_src_obexd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o obexd/plugins/obexd-mas.obj `if test -f 'obexd/plugins/mas.c'; then $(CYGPATH_W) 'obexd/plugins/mas.c'; else $(CYGPATH_W) '$(srcdir)/obexd/plugins/mas.c'; fi` obexd/plugins/obexd-messages-dummy.o: obexd/plugins/messages-dummy.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(obexd_src_obexd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT obexd/plugins/obexd-messages-dummy.o -MD -MP -MF obexd/plugins/$(DEPDIR)/obexd-messages-dummy.Tpo -c -o obexd/plugins/obexd-messages-dummy.o `test -f 'obexd/plugins/messages-dummy.c' || echo '$(srcdir)/'`obexd/plugins/messages-dummy.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) obexd/plugins/$(DEPDIR)/obexd-messages-dummy.Tpo obexd/plugins/$(DEPDIR)/obexd-messages-dummy.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='obexd/plugins/messages-dummy.c' object='obexd/plugins/obexd-messages-dummy.o' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(obexd_src_obexd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o obexd/plugins/obexd-messages-dummy.o `test -f 'obexd/plugins/messages-dummy.c' || echo '$(srcdir)/'`obexd/plugins/messages-dummy.c obexd/plugins/obexd-messages-dummy.obj: obexd/plugins/messages-dummy.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(obexd_src_obexd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT obexd/plugins/obexd-messages-dummy.obj -MD -MP -MF obexd/plugins/$(DEPDIR)/obexd-messages-dummy.Tpo -c -o obexd/plugins/obexd-messages-dummy.obj `if test -f 'obexd/plugins/messages-dummy.c'; then $(CYGPATH_W) 'obexd/plugins/messages-dummy.c'; else $(CYGPATH_W) '$(srcdir)/obexd/plugins/messages-dummy.c'; fi` @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) obexd/plugins/$(DEPDIR)/obexd-messages-dummy.Tpo obexd/plugins/$(DEPDIR)/obexd-messages-dummy.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='obexd/plugins/messages-dummy.c' object='obexd/plugins/obexd-messages-dummy.obj' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(obexd_src_obexd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o obexd/plugins/obexd-messages-dummy.obj `if test -f 'obexd/plugins/messages-dummy.c'; then $(CYGPATH_W) 'obexd/plugins/messages-dummy.c'; else $(CYGPATH_W) '$(srcdir)/obexd/plugins/messages-dummy.c'; fi` obexd/client/obexd-mns.o: obexd/client/mns.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(obexd_src_obexd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT obexd/client/obexd-mns.o -MD -MP -MF obexd/client/$(DEPDIR)/obexd-mns.Tpo -c -o obexd/client/obexd-mns.o `test -f 'obexd/client/mns.c' || echo '$(srcdir)/'`obexd/client/mns.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) obexd/client/$(DEPDIR)/obexd-mns.Tpo obexd/client/$(DEPDIR)/obexd-mns.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='obexd/client/mns.c' object='obexd/client/obexd-mns.o' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(obexd_src_obexd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o obexd/client/obexd-mns.o `test -f 'obexd/client/mns.c' || echo '$(srcdir)/'`obexd/client/mns.c obexd/client/obexd-mns.obj: obexd/client/mns.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(obexd_src_obexd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT obexd/client/obexd-mns.obj -MD -MP -MF obexd/client/$(DEPDIR)/obexd-mns.Tpo -c -o obexd/client/obexd-mns.obj `if test -f 'obexd/client/mns.c'; then $(CYGPATH_W) 'obexd/client/mns.c'; else $(CYGPATH_W) '$(srcdir)/obexd/client/mns.c'; fi` @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) obexd/client/$(DEPDIR)/obexd-mns.Tpo obexd/client/$(DEPDIR)/obexd-mns.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='obexd/client/mns.c' object='obexd/client/obexd-mns.obj' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(obexd_src_obexd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o obexd/client/obexd-mns.obj `if test -f 'obexd/client/mns.c'; then $(CYGPATH_W) 'obexd/client/mns.c'; else $(CYGPATH_W) '$(srcdir)/obexd/client/mns.c'; fi` obexd/src/obexd-main.o: obexd/src/main.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(obexd_src_obexd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT obexd/src/obexd-main.o -MD -MP -MF obexd/src/$(DEPDIR)/obexd-main.Tpo -c -o obexd/src/obexd-main.o `test -f 'obexd/src/main.c' || echo '$(srcdir)/'`obexd/src/main.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) obexd/src/$(DEPDIR)/obexd-main.Tpo obexd/src/$(DEPDIR)/obexd-main.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='obexd/src/main.c' object='obexd/src/obexd-main.o' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(obexd_src_obexd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o obexd/src/obexd-main.o `test -f 'obexd/src/main.c' || echo '$(srcdir)/'`obexd/src/main.c obexd/src/obexd-main.obj: obexd/src/main.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(obexd_src_obexd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT obexd/src/obexd-main.obj -MD -MP -MF obexd/src/$(DEPDIR)/obexd-main.Tpo -c -o obexd/src/obexd-main.obj `if test -f 'obexd/src/main.c'; then $(CYGPATH_W) 'obexd/src/main.c'; else $(CYGPATH_W) '$(srcdir)/obexd/src/main.c'; fi` @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) obexd/src/$(DEPDIR)/obexd-main.Tpo obexd/src/$(DEPDIR)/obexd-main.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='obexd/src/main.c' object='obexd/src/obexd-main.obj' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(obexd_src_obexd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o obexd/src/obexd-main.obj `if test -f 'obexd/src/main.c'; then $(CYGPATH_W) 'obexd/src/main.c'; else $(CYGPATH_W) '$(srcdir)/obexd/src/main.c'; fi` obexd/src/obexd-plugin.o: obexd/src/plugin.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(obexd_src_obexd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT obexd/src/obexd-plugin.o -MD -MP -MF obexd/src/$(DEPDIR)/obexd-plugin.Tpo -c -o obexd/src/obexd-plugin.o `test -f 'obexd/src/plugin.c' || echo '$(srcdir)/'`obexd/src/plugin.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) obexd/src/$(DEPDIR)/obexd-plugin.Tpo obexd/src/$(DEPDIR)/obexd-plugin.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='obexd/src/plugin.c' object='obexd/src/obexd-plugin.o' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(obexd_src_obexd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o obexd/src/obexd-plugin.o `test -f 'obexd/src/plugin.c' || echo '$(srcdir)/'`obexd/src/plugin.c obexd/src/obexd-plugin.obj: obexd/src/plugin.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(obexd_src_obexd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT obexd/src/obexd-plugin.obj -MD -MP -MF obexd/src/$(DEPDIR)/obexd-plugin.Tpo -c -o obexd/src/obexd-plugin.obj `if test -f 'obexd/src/plugin.c'; then $(CYGPATH_W) 'obexd/src/plugin.c'; else $(CYGPATH_W) '$(srcdir)/obexd/src/plugin.c'; fi` @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) obexd/src/$(DEPDIR)/obexd-plugin.Tpo obexd/src/$(DEPDIR)/obexd-plugin.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='obexd/src/plugin.c' object='obexd/src/obexd-plugin.obj' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(obexd_src_obexd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o obexd/src/obexd-plugin.obj `if test -f 'obexd/src/plugin.c'; then $(CYGPATH_W) 'obexd/src/plugin.c'; else $(CYGPATH_W) '$(srcdir)/obexd/src/plugin.c'; fi` obexd/src/obexd-log.o: obexd/src/log.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(obexd_src_obexd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT obexd/src/obexd-log.o -MD -MP -MF obexd/src/$(DEPDIR)/obexd-log.Tpo -c -o obexd/src/obexd-log.o `test -f 'obexd/src/log.c' || echo '$(srcdir)/'`obexd/src/log.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) obexd/src/$(DEPDIR)/obexd-log.Tpo obexd/src/$(DEPDIR)/obexd-log.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='obexd/src/log.c' object='obexd/src/obexd-log.o' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(obexd_src_obexd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o obexd/src/obexd-log.o `test -f 'obexd/src/log.c' || echo '$(srcdir)/'`obexd/src/log.c obexd/src/obexd-log.obj: obexd/src/log.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(obexd_src_obexd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT obexd/src/obexd-log.obj -MD -MP -MF obexd/src/$(DEPDIR)/obexd-log.Tpo -c -o obexd/src/obexd-log.obj `if test -f 'obexd/src/log.c'; then $(CYGPATH_W) 'obexd/src/log.c'; else $(CYGPATH_W) '$(srcdir)/obexd/src/log.c'; fi` @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) obexd/src/$(DEPDIR)/obexd-log.Tpo obexd/src/$(DEPDIR)/obexd-log.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='obexd/src/log.c' object='obexd/src/obexd-log.obj' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(obexd_src_obexd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o obexd/src/obexd-log.obj `if test -f 'obexd/src/log.c'; then $(CYGPATH_W) 'obexd/src/log.c'; else $(CYGPATH_W) '$(srcdir)/obexd/src/log.c'; fi` obexd/src/obexd-manager.o: obexd/src/manager.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(obexd_src_obexd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT obexd/src/obexd-manager.o -MD -MP -MF obexd/src/$(DEPDIR)/obexd-manager.Tpo -c -o obexd/src/obexd-manager.o `test -f 'obexd/src/manager.c' || echo '$(srcdir)/'`obexd/src/manager.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) obexd/src/$(DEPDIR)/obexd-manager.Tpo obexd/src/$(DEPDIR)/obexd-manager.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='obexd/src/manager.c' object='obexd/src/obexd-manager.o' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(obexd_src_obexd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o obexd/src/obexd-manager.o `test -f 'obexd/src/manager.c' || echo '$(srcdir)/'`obexd/src/manager.c obexd/src/obexd-manager.obj: obexd/src/manager.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(obexd_src_obexd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT obexd/src/obexd-manager.obj -MD -MP -MF obexd/src/$(DEPDIR)/obexd-manager.Tpo -c -o obexd/src/obexd-manager.obj `if test -f 'obexd/src/manager.c'; then $(CYGPATH_W) 'obexd/src/manager.c'; else $(CYGPATH_W) '$(srcdir)/obexd/src/manager.c'; fi` @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) obexd/src/$(DEPDIR)/obexd-manager.Tpo obexd/src/$(DEPDIR)/obexd-manager.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='obexd/src/manager.c' object='obexd/src/obexd-manager.obj' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(obexd_src_obexd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o obexd/src/obexd-manager.obj `if test -f 'obexd/src/manager.c'; then $(CYGPATH_W) 'obexd/src/manager.c'; else $(CYGPATH_W) '$(srcdir)/obexd/src/manager.c'; fi` obexd/src/obexd-obex.o: obexd/src/obex.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(obexd_src_obexd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT obexd/src/obexd-obex.o -MD -MP -MF obexd/src/$(DEPDIR)/obexd-obex.Tpo -c -o obexd/src/obexd-obex.o `test -f 'obexd/src/obex.c' || echo '$(srcdir)/'`obexd/src/obex.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) obexd/src/$(DEPDIR)/obexd-obex.Tpo obexd/src/$(DEPDIR)/obexd-obex.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='obexd/src/obex.c' object='obexd/src/obexd-obex.o' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(obexd_src_obexd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o obexd/src/obexd-obex.o `test -f 'obexd/src/obex.c' || echo '$(srcdir)/'`obexd/src/obex.c obexd/src/obexd-obex.obj: obexd/src/obex.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(obexd_src_obexd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT obexd/src/obexd-obex.obj -MD -MP -MF obexd/src/$(DEPDIR)/obexd-obex.Tpo -c -o obexd/src/obexd-obex.obj `if test -f 'obexd/src/obex.c'; then $(CYGPATH_W) 'obexd/src/obex.c'; else $(CYGPATH_W) '$(srcdir)/obexd/src/obex.c'; fi` @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) obexd/src/$(DEPDIR)/obexd-obex.Tpo obexd/src/$(DEPDIR)/obexd-obex.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='obexd/src/obex.c' object='obexd/src/obexd-obex.obj' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(obexd_src_obexd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o obexd/src/obexd-obex.obj `if test -f 'obexd/src/obex.c'; then $(CYGPATH_W) 'obexd/src/obex.c'; else $(CYGPATH_W) '$(srcdir)/obexd/src/obex.c'; fi` obexd/src/obexd-mimetype.o: obexd/src/mimetype.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(obexd_src_obexd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT obexd/src/obexd-mimetype.o -MD -MP -MF obexd/src/$(DEPDIR)/obexd-mimetype.Tpo -c -o obexd/src/obexd-mimetype.o `test -f 'obexd/src/mimetype.c' || echo '$(srcdir)/'`obexd/src/mimetype.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) obexd/src/$(DEPDIR)/obexd-mimetype.Tpo obexd/src/$(DEPDIR)/obexd-mimetype.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='obexd/src/mimetype.c' object='obexd/src/obexd-mimetype.o' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(obexd_src_obexd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o obexd/src/obexd-mimetype.o `test -f 'obexd/src/mimetype.c' || echo '$(srcdir)/'`obexd/src/mimetype.c obexd/src/obexd-mimetype.obj: obexd/src/mimetype.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(obexd_src_obexd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT obexd/src/obexd-mimetype.obj -MD -MP -MF obexd/src/$(DEPDIR)/obexd-mimetype.Tpo -c -o obexd/src/obexd-mimetype.obj `if test -f 'obexd/src/mimetype.c'; then $(CYGPATH_W) 'obexd/src/mimetype.c'; else $(CYGPATH_W) '$(srcdir)/obexd/src/mimetype.c'; fi` @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) obexd/src/$(DEPDIR)/obexd-mimetype.Tpo obexd/src/$(DEPDIR)/obexd-mimetype.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='obexd/src/mimetype.c' object='obexd/src/obexd-mimetype.obj' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(obexd_src_obexd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o obexd/src/obexd-mimetype.obj `if test -f 'obexd/src/mimetype.c'; then $(CYGPATH_W) 'obexd/src/mimetype.c'; else $(CYGPATH_W) '$(srcdir)/obexd/src/mimetype.c'; fi` obexd/src/obexd-service.o: obexd/src/service.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(obexd_src_obexd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT obexd/src/obexd-service.o -MD -MP -MF obexd/src/$(DEPDIR)/obexd-service.Tpo -c -o obexd/src/obexd-service.o `test -f 'obexd/src/service.c' || echo '$(srcdir)/'`obexd/src/service.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) obexd/src/$(DEPDIR)/obexd-service.Tpo obexd/src/$(DEPDIR)/obexd-service.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='obexd/src/service.c' object='obexd/src/obexd-service.o' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(obexd_src_obexd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o obexd/src/obexd-service.o `test -f 'obexd/src/service.c' || echo '$(srcdir)/'`obexd/src/service.c obexd/src/obexd-service.obj: obexd/src/service.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(obexd_src_obexd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT obexd/src/obexd-service.obj -MD -MP -MF obexd/src/$(DEPDIR)/obexd-service.Tpo -c -o obexd/src/obexd-service.obj `if test -f 'obexd/src/service.c'; then $(CYGPATH_W) 'obexd/src/service.c'; else $(CYGPATH_W) '$(srcdir)/obexd/src/service.c'; fi` @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) obexd/src/$(DEPDIR)/obexd-service.Tpo obexd/src/$(DEPDIR)/obexd-service.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='obexd/src/service.c' object='obexd/src/obexd-service.obj' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(obexd_src_obexd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o obexd/src/obexd-service.obj `if test -f 'obexd/src/service.c'; then $(CYGPATH_W) 'obexd/src/service.c'; else $(CYGPATH_W) '$(srcdir)/obexd/src/service.c'; fi` obexd/src/obexd-transport.o: obexd/src/transport.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(obexd_src_obexd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT obexd/src/obexd-transport.o -MD -MP -MF obexd/src/$(DEPDIR)/obexd-transport.Tpo -c -o obexd/src/obexd-transport.o `test -f 'obexd/src/transport.c' || echo '$(srcdir)/'`obexd/src/transport.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) obexd/src/$(DEPDIR)/obexd-transport.Tpo obexd/src/$(DEPDIR)/obexd-transport.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='obexd/src/transport.c' object='obexd/src/obexd-transport.o' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(obexd_src_obexd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o obexd/src/obexd-transport.o `test -f 'obexd/src/transport.c' || echo '$(srcdir)/'`obexd/src/transport.c obexd/src/obexd-transport.obj: obexd/src/transport.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(obexd_src_obexd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT obexd/src/obexd-transport.obj -MD -MP -MF obexd/src/$(DEPDIR)/obexd-transport.Tpo -c -o obexd/src/obexd-transport.obj `if test -f 'obexd/src/transport.c'; then $(CYGPATH_W) 'obexd/src/transport.c'; else $(CYGPATH_W) '$(srcdir)/obexd/src/transport.c'; fi` @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) obexd/src/$(DEPDIR)/obexd-transport.Tpo obexd/src/$(DEPDIR)/obexd-transport.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='obexd/src/transport.c' object='obexd/src/obexd-transport.obj' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(obexd_src_obexd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o obexd/src/obexd-transport.obj `if test -f 'obexd/src/transport.c'; then $(CYGPATH_W) 'obexd/src/transport.c'; else $(CYGPATH_W) '$(srcdir)/obexd/src/transport.c'; fi` obexd/src/obexd-server.o: obexd/src/server.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(obexd_src_obexd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT obexd/src/obexd-server.o -MD -MP -MF obexd/src/$(DEPDIR)/obexd-server.Tpo -c -o obexd/src/obexd-server.o `test -f 'obexd/src/server.c' || echo '$(srcdir)/'`obexd/src/server.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) obexd/src/$(DEPDIR)/obexd-server.Tpo obexd/src/$(DEPDIR)/obexd-server.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='obexd/src/server.c' object='obexd/src/obexd-server.o' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(obexd_src_obexd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o obexd/src/obexd-server.o `test -f 'obexd/src/server.c' || echo '$(srcdir)/'`obexd/src/server.c obexd/src/obexd-server.obj: obexd/src/server.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(obexd_src_obexd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT obexd/src/obexd-server.obj -MD -MP -MF obexd/src/$(DEPDIR)/obexd-server.Tpo -c -o obexd/src/obexd-server.obj `if test -f 'obexd/src/server.c'; then $(CYGPATH_W) 'obexd/src/server.c'; else $(CYGPATH_W) '$(srcdir)/obexd/src/server.c'; fi` @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) obexd/src/$(DEPDIR)/obexd-server.Tpo obexd/src/$(DEPDIR)/obexd-server.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='obexd/src/server.c' object='obexd/src/obexd-server.obj' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(obexd_src_obexd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o obexd/src/obexd-server.obj `if test -f 'obexd/src/server.c'; then $(CYGPATH_W) 'obexd/src/server.c'; else $(CYGPATH_W) '$(srcdir)/obexd/src/server.c'; fi` obexd/client/obexd-manager.o: obexd/client/manager.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(obexd_src_obexd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT obexd/client/obexd-manager.o -MD -MP -MF obexd/client/$(DEPDIR)/obexd-manager.Tpo -c -o obexd/client/obexd-manager.o `test -f 'obexd/client/manager.c' || echo '$(srcdir)/'`obexd/client/manager.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) obexd/client/$(DEPDIR)/obexd-manager.Tpo obexd/client/$(DEPDIR)/obexd-manager.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='obexd/client/manager.c' object='obexd/client/obexd-manager.o' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(obexd_src_obexd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o obexd/client/obexd-manager.o `test -f 'obexd/client/manager.c' || echo '$(srcdir)/'`obexd/client/manager.c obexd/client/obexd-manager.obj: obexd/client/manager.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(obexd_src_obexd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT obexd/client/obexd-manager.obj -MD -MP -MF obexd/client/$(DEPDIR)/obexd-manager.Tpo -c -o obexd/client/obexd-manager.obj `if test -f 'obexd/client/manager.c'; then $(CYGPATH_W) 'obexd/client/manager.c'; else $(CYGPATH_W) '$(srcdir)/obexd/client/manager.c'; fi` @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) obexd/client/$(DEPDIR)/obexd-manager.Tpo obexd/client/$(DEPDIR)/obexd-manager.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='obexd/client/manager.c' object='obexd/client/obexd-manager.obj' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(obexd_src_obexd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o obexd/client/obexd-manager.obj `if test -f 'obexd/client/manager.c'; then $(CYGPATH_W) 'obexd/client/manager.c'; else $(CYGPATH_W) '$(srcdir)/obexd/client/manager.c'; fi` obexd/client/obexd-session.o: obexd/client/session.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(obexd_src_obexd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT obexd/client/obexd-session.o -MD -MP -MF obexd/client/$(DEPDIR)/obexd-session.Tpo -c -o obexd/client/obexd-session.o `test -f 'obexd/client/session.c' || echo '$(srcdir)/'`obexd/client/session.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) obexd/client/$(DEPDIR)/obexd-session.Tpo obexd/client/$(DEPDIR)/obexd-session.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='obexd/client/session.c' object='obexd/client/obexd-session.o' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(obexd_src_obexd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o obexd/client/obexd-session.o `test -f 'obexd/client/session.c' || echo '$(srcdir)/'`obexd/client/session.c obexd/client/obexd-session.obj: obexd/client/session.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(obexd_src_obexd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT obexd/client/obexd-session.obj -MD -MP -MF obexd/client/$(DEPDIR)/obexd-session.Tpo -c -o obexd/client/obexd-session.obj `if test -f 'obexd/client/session.c'; then $(CYGPATH_W) 'obexd/client/session.c'; else $(CYGPATH_W) '$(srcdir)/obexd/client/session.c'; fi` @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) obexd/client/$(DEPDIR)/obexd-session.Tpo obexd/client/$(DEPDIR)/obexd-session.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='obexd/client/session.c' object='obexd/client/obexd-session.obj' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(obexd_src_obexd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o obexd/client/obexd-session.obj `if test -f 'obexd/client/session.c'; then $(CYGPATH_W) 'obexd/client/session.c'; else $(CYGPATH_W) '$(srcdir)/obexd/client/session.c'; fi` obexd/client/obexd-bluetooth.o: obexd/client/bluetooth.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(obexd_src_obexd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT obexd/client/obexd-bluetooth.o -MD -MP -MF obexd/client/$(DEPDIR)/obexd-bluetooth.Tpo -c -o obexd/client/obexd-bluetooth.o `test -f 'obexd/client/bluetooth.c' || echo '$(srcdir)/'`obexd/client/bluetooth.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) obexd/client/$(DEPDIR)/obexd-bluetooth.Tpo obexd/client/$(DEPDIR)/obexd-bluetooth.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='obexd/client/bluetooth.c' object='obexd/client/obexd-bluetooth.o' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(obexd_src_obexd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o obexd/client/obexd-bluetooth.o `test -f 'obexd/client/bluetooth.c' || echo '$(srcdir)/'`obexd/client/bluetooth.c obexd/client/obexd-bluetooth.obj: obexd/client/bluetooth.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(obexd_src_obexd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT obexd/client/obexd-bluetooth.obj -MD -MP -MF obexd/client/$(DEPDIR)/obexd-bluetooth.Tpo -c -o obexd/client/obexd-bluetooth.obj `if test -f 'obexd/client/bluetooth.c'; then $(CYGPATH_W) 'obexd/client/bluetooth.c'; else $(CYGPATH_W) '$(srcdir)/obexd/client/bluetooth.c'; fi` @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) obexd/client/$(DEPDIR)/obexd-bluetooth.Tpo obexd/client/$(DEPDIR)/obexd-bluetooth.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='obexd/client/bluetooth.c' object='obexd/client/obexd-bluetooth.obj' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(obexd_src_obexd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o obexd/client/obexd-bluetooth.obj `if test -f 'obexd/client/bluetooth.c'; then $(CYGPATH_W) 'obexd/client/bluetooth.c'; else $(CYGPATH_W) '$(srcdir)/obexd/client/bluetooth.c'; fi` obexd/client/obexd-sync.o: obexd/client/sync.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(obexd_src_obexd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT obexd/client/obexd-sync.o -MD -MP -MF obexd/client/$(DEPDIR)/obexd-sync.Tpo -c -o obexd/client/obexd-sync.o `test -f 'obexd/client/sync.c' || echo '$(srcdir)/'`obexd/client/sync.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) obexd/client/$(DEPDIR)/obexd-sync.Tpo obexd/client/$(DEPDIR)/obexd-sync.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='obexd/client/sync.c' object='obexd/client/obexd-sync.o' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(obexd_src_obexd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o obexd/client/obexd-sync.o `test -f 'obexd/client/sync.c' || echo '$(srcdir)/'`obexd/client/sync.c obexd/client/obexd-sync.obj: obexd/client/sync.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(obexd_src_obexd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT obexd/client/obexd-sync.obj -MD -MP -MF obexd/client/$(DEPDIR)/obexd-sync.Tpo -c -o obexd/client/obexd-sync.obj `if test -f 'obexd/client/sync.c'; then $(CYGPATH_W) 'obexd/client/sync.c'; else $(CYGPATH_W) '$(srcdir)/obexd/client/sync.c'; fi` @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) obexd/client/$(DEPDIR)/obexd-sync.Tpo obexd/client/$(DEPDIR)/obexd-sync.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='obexd/client/sync.c' object='obexd/client/obexd-sync.obj' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(obexd_src_obexd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o obexd/client/obexd-sync.obj `if test -f 'obexd/client/sync.c'; then $(CYGPATH_W) 'obexd/client/sync.c'; else $(CYGPATH_W) '$(srcdir)/obexd/client/sync.c'; fi` obexd/client/obexd-pbap.o: obexd/client/pbap.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(obexd_src_obexd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT obexd/client/obexd-pbap.o -MD -MP -MF obexd/client/$(DEPDIR)/obexd-pbap.Tpo -c -o obexd/client/obexd-pbap.o `test -f 'obexd/client/pbap.c' || echo '$(srcdir)/'`obexd/client/pbap.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) obexd/client/$(DEPDIR)/obexd-pbap.Tpo obexd/client/$(DEPDIR)/obexd-pbap.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='obexd/client/pbap.c' object='obexd/client/obexd-pbap.o' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(obexd_src_obexd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o obexd/client/obexd-pbap.o `test -f 'obexd/client/pbap.c' || echo '$(srcdir)/'`obexd/client/pbap.c obexd/client/obexd-pbap.obj: obexd/client/pbap.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(obexd_src_obexd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT obexd/client/obexd-pbap.obj -MD -MP -MF obexd/client/$(DEPDIR)/obexd-pbap.Tpo -c -o obexd/client/obexd-pbap.obj `if test -f 'obexd/client/pbap.c'; then $(CYGPATH_W) 'obexd/client/pbap.c'; else $(CYGPATH_W) '$(srcdir)/obexd/client/pbap.c'; fi` @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) obexd/client/$(DEPDIR)/obexd-pbap.Tpo obexd/client/$(DEPDIR)/obexd-pbap.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='obexd/client/pbap.c' object='obexd/client/obexd-pbap.obj' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(obexd_src_obexd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o obexd/client/obexd-pbap.obj `if test -f 'obexd/client/pbap.c'; then $(CYGPATH_W) 'obexd/client/pbap.c'; else $(CYGPATH_W) '$(srcdir)/obexd/client/pbap.c'; fi` obexd/client/obexd-ftp.o: obexd/client/ftp.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(obexd_src_obexd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT obexd/client/obexd-ftp.o -MD -MP -MF obexd/client/$(DEPDIR)/obexd-ftp.Tpo -c -o obexd/client/obexd-ftp.o `test -f 'obexd/client/ftp.c' || echo '$(srcdir)/'`obexd/client/ftp.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) obexd/client/$(DEPDIR)/obexd-ftp.Tpo obexd/client/$(DEPDIR)/obexd-ftp.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='obexd/client/ftp.c' object='obexd/client/obexd-ftp.o' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(obexd_src_obexd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o obexd/client/obexd-ftp.o `test -f 'obexd/client/ftp.c' || echo '$(srcdir)/'`obexd/client/ftp.c obexd/client/obexd-ftp.obj: obexd/client/ftp.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(obexd_src_obexd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT obexd/client/obexd-ftp.obj -MD -MP -MF obexd/client/$(DEPDIR)/obexd-ftp.Tpo -c -o obexd/client/obexd-ftp.obj `if test -f 'obexd/client/ftp.c'; then $(CYGPATH_W) 'obexd/client/ftp.c'; else $(CYGPATH_W) '$(srcdir)/obexd/client/ftp.c'; fi` @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) obexd/client/$(DEPDIR)/obexd-ftp.Tpo obexd/client/$(DEPDIR)/obexd-ftp.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='obexd/client/ftp.c' object='obexd/client/obexd-ftp.obj' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(obexd_src_obexd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o obexd/client/obexd-ftp.obj `if test -f 'obexd/client/ftp.c'; then $(CYGPATH_W) 'obexd/client/ftp.c'; else $(CYGPATH_W) '$(srcdir)/obexd/client/ftp.c'; fi` obexd/client/obexd-opp.o: obexd/client/opp.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(obexd_src_obexd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT obexd/client/obexd-opp.o -MD -MP -MF obexd/client/$(DEPDIR)/obexd-opp.Tpo -c -o obexd/client/obexd-opp.o `test -f 'obexd/client/opp.c' || echo '$(srcdir)/'`obexd/client/opp.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) obexd/client/$(DEPDIR)/obexd-opp.Tpo obexd/client/$(DEPDIR)/obexd-opp.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='obexd/client/opp.c' object='obexd/client/obexd-opp.o' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(obexd_src_obexd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o obexd/client/obexd-opp.o `test -f 'obexd/client/opp.c' || echo '$(srcdir)/'`obexd/client/opp.c obexd/client/obexd-opp.obj: obexd/client/opp.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(obexd_src_obexd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT obexd/client/obexd-opp.obj -MD -MP -MF obexd/client/$(DEPDIR)/obexd-opp.Tpo -c -o obexd/client/obexd-opp.obj `if test -f 'obexd/client/opp.c'; then $(CYGPATH_W) 'obexd/client/opp.c'; else $(CYGPATH_W) '$(srcdir)/obexd/client/opp.c'; fi` @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) obexd/client/$(DEPDIR)/obexd-opp.Tpo obexd/client/$(DEPDIR)/obexd-opp.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='obexd/client/opp.c' object='obexd/client/obexd-opp.obj' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(obexd_src_obexd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o obexd/client/obexd-opp.obj `if test -f 'obexd/client/opp.c'; then $(CYGPATH_W) 'obexd/client/opp.c'; else $(CYGPATH_W) '$(srcdir)/obexd/client/opp.c'; fi` obexd/client/obexd-map.o: obexd/client/map.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(obexd_src_obexd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT obexd/client/obexd-map.o -MD -MP -MF obexd/client/$(DEPDIR)/obexd-map.Tpo -c -o obexd/client/obexd-map.o `test -f 'obexd/client/map.c' || echo '$(srcdir)/'`obexd/client/map.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) obexd/client/$(DEPDIR)/obexd-map.Tpo obexd/client/$(DEPDIR)/obexd-map.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='obexd/client/map.c' object='obexd/client/obexd-map.o' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(obexd_src_obexd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o obexd/client/obexd-map.o `test -f 'obexd/client/map.c' || echo '$(srcdir)/'`obexd/client/map.c obexd/client/obexd-map.obj: obexd/client/map.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(obexd_src_obexd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT obexd/client/obexd-map.obj -MD -MP -MF obexd/client/$(DEPDIR)/obexd-map.Tpo -c -o obexd/client/obexd-map.obj `if test -f 'obexd/client/map.c'; then $(CYGPATH_W) 'obexd/client/map.c'; else $(CYGPATH_W) '$(srcdir)/obexd/client/map.c'; fi` @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) obexd/client/$(DEPDIR)/obexd-map.Tpo obexd/client/$(DEPDIR)/obexd-map.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='obexd/client/map.c' object='obexd/client/obexd-map.obj' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(obexd_src_obexd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o obexd/client/obexd-map.obj `if test -f 'obexd/client/map.c'; then $(CYGPATH_W) 'obexd/client/map.c'; else $(CYGPATH_W) '$(srcdir)/obexd/client/map.c'; fi` obexd/client/obexd-bip.o: obexd/client/bip.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(obexd_src_obexd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT obexd/client/obexd-bip.o -MD -MP -MF obexd/client/$(DEPDIR)/obexd-bip.Tpo -c -o obexd/client/obexd-bip.o `test -f 'obexd/client/bip.c' || echo '$(srcdir)/'`obexd/client/bip.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) obexd/client/$(DEPDIR)/obexd-bip.Tpo obexd/client/$(DEPDIR)/obexd-bip.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='obexd/client/bip.c' object='obexd/client/obexd-bip.o' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(obexd_src_obexd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o obexd/client/obexd-bip.o `test -f 'obexd/client/bip.c' || echo '$(srcdir)/'`obexd/client/bip.c obexd/client/obexd-bip.obj: obexd/client/bip.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(obexd_src_obexd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT obexd/client/obexd-bip.obj -MD -MP -MF obexd/client/$(DEPDIR)/obexd-bip.Tpo -c -o obexd/client/obexd-bip.obj `if test -f 'obexd/client/bip.c'; then $(CYGPATH_W) 'obexd/client/bip.c'; else $(CYGPATH_W) '$(srcdir)/obexd/client/bip.c'; fi` @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) obexd/client/$(DEPDIR)/obexd-bip.Tpo obexd/client/$(DEPDIR)/obexd-bip.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='obexd/client/bip.c' object='obexd/client/obexd-bip.obj' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(obexd_src_obexd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o obexd/client/obexd-bip.obj `if test -f 'obexd/client/bip.c'; then $(CYGPATH_W) 'obexd/client/bip.c'; else $(CYGPATH_W) '$(srcdir)/obexd/client/bip.c'; fi` obexd/client/obexd-bip-common.o: obexd/client/bip-common.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(obexd_src_obexd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT obexd/client/obexd-bip-common.o -MD -MP -MF obexd/client/$(DEPDIR)/obexd-bip-common.Tpo -c -o obexd/client/obexd-bip-common.o `test -f 'obexd/client/bip-common.c' || echo '$(srcdir)/'`obexd/client/bip-common.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) obexd/client/$(DEPDIR)/obexd-bip-common.Tpo obexd/client/$(DEPDIR)/obexd-bip-common.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='obexd/client/bip-common.c' object='obexd/client/obexd-bip-common.o' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(obexd_src_obexd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o obexd/client/obexd-bip-common.o `test -f 'obexd/client/bip-common.c' || echo '$(srcdir)/'`obexd/client/bip-common.c obexd/client/obexd-bip-common.obj: obexd/client/bip-common.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(obexd_src_obexd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT obexd/client/obexd-bip-common.obj -MD -MP -MF obexd/client/$(DEPDIR)/obexd-bip-common.Tpo -c -o obexd/client/obexd-bip-common.obj `if test -f 'obexd/client/bip-common.c'; then $(CYGPATH_W) 'obexd/client/bip-common.c'; else $(CYGPATH_W) '$(srcdir)/obexd/client/bip-common.c'; fi` @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) obexd/client/$(DEPDIR)/obexd-bip-common.Tpo obexd/client/$(DEPDIR)/obexd-bip-common.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='obexd/client/bip-common.c' object='obexd/client/obexd-bip-common.obj' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(obexd_src_obexd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o obexd/client/obexd-bip-common.obj `if test -f 'obexd/client/bip-common.c'; then $(CYGPATH_W) 'obexd/client/bip-common.c'; else $(CYGPATH_W) '$(srcdir)/obexd/client/bip-common.c'; fi` obexd/client/obexd-map-event.o: obexd/client/map-event.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(obexd_src_obexd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT obexd/client/obexd-map-event.o -MD -MP -MF obexd/client/$(DEPDIR)/obexd-map-event.Tpo -c -o obexd/client/obexd-map-event.o `test -f 'obexd/client/map-event.c' || echo '$(srcdir)/'`obexd/client/map-event.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) obexd/client/$(DEPDIR)/obexd-map-event.Tpo obexd/client/$(DEPDIR)/obexd-map-event.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='obexd/client/map-event.c' object='obexd/client/obexd-map-event.o' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(obexd_src_obexd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o obexd/client/obexd-map-event.o `test -f 'obexd/client/map-event.c' || echo '$(srcdir)/'`obexd/client/map-event.c obexd/client/obexd-map-event.obj: obexd/client/map-event.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(obexd_src_obexd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT obexd/client/obexd-map-event.obj -MD -MP -MF obexd/client/$(DEPDIR)/obexd-map-event.Tpo -c -o obexd/client/obexd-map-event.obj `if test -f 'obexd/client/map-event.c'; then $(CYGPATH_W) 'obexd/client/map-event.c'; else $(CYGPATH_W) '$(srcdir)/obexd/client/map-event.c'; fi` @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) obexd/client/$(DEPDIR)/obexd-map-event.Tpo obexd/client/$(DEPDIR)/obexd-map-event.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='obexd/client/map-event.c' object='obexd/client/obexd-map-event.obj' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(obexd_src_obexd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o obexd/client/obexd-map-event.obj `if test -f 'obexd/client/map-event.c'; then $(CYGPATH_W) 'obexd/client/map-event.c'; else $(CYGPATH_W) '$(srcdir)/obexd/client/map-event.c'; fi` obexd/client/obexd-transfer.o: obexd/client/transfer.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(obexd_src_obexd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT obexd/client/obexd-transfer.o -MD -MP -MF obexd/client/$(DEPDIR)/obexd-transfer.Tpo -c -o obexd/client/obexd-transfer.o `test -f 'obexd/client/transfer.c' || echo '$(srcdir)/'`obexd/client/transfer.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) obexd/client/$(DEPDIR)/obexd-transfer.Tpo obexd/client/$(DEPDIR)/obexd-transfer.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='obexd/client/transfer.c' object='obexd/client/obexd-transfer.o' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(obexd_src_obexd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o obexd/client/obexd-transfer.o `test -f 'obexd/client/transfer.c' || echo '$(srcdir)/'`obexd/client/transfer.c obexd/client/obexd-transfer.obj: obexd/client/transfer.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(obexd_src_obexd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT obexd/client/obexd-transfer.obj -MD -MP -MF obexd/client/$(DEPDIR)/obexd-transfer.Tpo -c -o obexd/client/obexd-transfer.obj `if test -f 'obexd/client/transfer.c'; then $(CYGPATH_W) 'obexd/client/transfer.c'; else $(CYGPATH_W) '$(srcdir)/obexd/client/transfer.c'; fi` @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) obexd/client/$(DEPDIR)/obexd-transfer.Tpo obexd/client/$(DEPDIR)/obexd-transfer.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='obexd/client/transfer.c' object='obexd/client/obexd-transfer.obj' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(obexd_src_obexd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o obexd/client/obexd-transfer.obj `if test -f 'obexd/client/transfer.c'; then $(CYGPATH_W) 'obexd/client/transfer.c'; else $(CYGPATH_W) '$(srcdir)/obexd/client/transfer.c'; fi` obexd/client/obexd-transport.o: obexd/client/transport.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(obexd_src_obexd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT obexd/client/obexd-transport.o -MD -MP -MF obexd/client/$(DEPDIR)/obexd-transport.Tpo -c -o obexd/client/obexd-transport.o `test -f 'obexd/client/transport.c' || echo '$(srcdir)/'`obexd/client/transport.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) obexd/client/$(DEPDIR)/obexd-transport.Tpo obexd/client/$(DEPDIR)/obexd-transport.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='obexd/client/transport.c' object='obexd/client/obexd-transport.o' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(obexd_src_obexd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o obexd/client/obexd-transport.o `test -f 'obexd/client/transport.c' || echo '$(srcdir)/'`obexd/client/transport.c obexd/client/obexd-transport.obj: obexd/client/transport.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(obexd_src_obexd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT obexd/client/obexd-transport.obj -MD -MP -MF obexd/client/$(DEPDIR)/obexd-transport.Tpo -c -o obexd/client/obexd-transport.obj `if test -f 'obexd/client/transport.c'; then $(CYGPATH_W) 'obexd/client/transport.c'; else $(CYGPATH_W) '$(srcdir)/obexd/client/transport.c'; fi` @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) obexd/client/$(DEPDIR)/obexd-transport.Tpo obexd/client/$(DEPDIR)/obexd-transport.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='obexd/client/transport.c' object='obexd/client/obexd-transport.obj' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(obexd_src_obexd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o obexd/client/obexd-transport.obj `if test -f 'obexd/client/transport.c'; then $(CYGPATH_W) 'obexd/client/transport.c'; else $(CYGPATH_W) '$(srcdir)/obexd/client/transport.c'; fi` obexd/client/obexd-driver.o: obexd/client/driver.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(obexd_src_obexd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT obexd/client/obexd-driver.o -MD -MP -MF obexd/client/$(DEPDIR)/obexd-driver.Tpo -c -o obexd/client/obexd-driver.o `test -f 'obexd/client/driver.c' || echo '$(srcdir)/'`obexd/client/driver.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) obexd/client/$(DEPDIR)/obexd-driver.Tpo obexd/client/$(DEPDIR)/obexd-driver.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='obexd/client/driver.c' object='obexd/client/obexd-driver.o' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(obexd_src_obexd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o obexd/client/obexd-driver.o `test -f 'obexd/client/driver.c' || echo '$(srcdir)/'`obexd/client/driver.c obexd/client/obexd-driver.obj: obexd/client/driver.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(obexd_src_obexd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT obexd/client/obexd-driver.obj -MD -MP -MF obexd/client/$(DEPDIR)/obexd-driver.Tpo -c -o obexd/client/obexd-driver.obj `if test -f 'obexd/client/driver.c'; then $(CYGPATH_W) 'obexd/client/driver.c'; else $(CYGPATH_W) '$(srcdir)/obexd/client/driver.c'; fi` @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) obexd/client/$(DEPDIR)/obexd-driver.Tpo obexd/client/$(DEPDIR)/obexd-driver.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='obexd/client/driver.c' object='obexd/client/obexd-driver.obj' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(obexd_src_obexd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o obexd/client/obexd-driver.obj `if test -f 'obexd/client/driver.c'; then $(CYGPATH_W) 'obexd/client/driver.c'; else $(CYGPATH_W) '$(srcdir)/obexd/client/driver.c'; fi` plugins/bluetoothd-hostname.o: plugins/hostname.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_bluetoothd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT plugins/bluetoothd-hostname.o -MD -MP -MF plugins/$(DEPDIR)/bluetoothd-hostname.Tpo -c -o plugins/bluetoothd-hostname.o `test -f 'plugins/hostname.c' || echo '$(srcdir)/'`plugins/hostname.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) plugins/$(DEPDIR)/bluetoothd-hostname.Tpo plugins/$(DEPDIR)/bluetoothd-hostname.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='plugins/hostname.c' object='plugins/bluetoothd-hostname.o' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_bluetoothd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o plugins/bluetoothd-hostname.o `test -f 'plugins/hostname.c' || echo '$(srcdir)/'`plugins/hostname.c plugins/bluetoothd-hostname.obj: plugins/hostname.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_bluetoothd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT plugins/bluetoothd-hostname.obj -MD -MP -MF plugins/$(DEPDIR)/bluetoothd-hostname.Tpo -c -o plugins/bluetoothd-hostname.obj `if test -f 'plugins/hostname.c'; then $(CYGPATH_W) 'plugins/hostname.c'; else $(CYGPATH_W) '$(srcdir)/plugins/hostname.c'; fi` @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) plugins/$(DEPDIR)/bluetoothd-hostname.Tpo plugins/$(DEPDIR)/bluetoothd-hostname.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='plugins/hostname.c' object='plugins/bluetoothd-hostname.obj' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_bluetoothd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o plugins/bluetoothd-hostname.obj `if test -f 'plugins/hostname.c'; then $(CYGPATH_W) 'plugins/hostname.c'; else $(CYGPATH_W) '$(srcdir)/plugins/hostname.c'; fi` plugins/bluetoothd-autopair.o: plugins/autopair.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_bluetoothd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT plugins/bluetoothd-autopair.o -MD -MP -MF plugins/$(DEPDIR)/bluetoothd-autopair.Tpo -c -o plugins/bluetoothd-autopair.o `test -f 'plugins/autopair.c' || echo '$(srcdir)/'`plugins/autopair.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) plugins/$(DEPDIR)/bluetoothd-autopair.Tpo plugins/$(DEPDIR)/bluetoothd-autopair.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='plugins/autopair.c' object='plugins/bluetoothd-autopair.o' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_bluetoothd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o plugins/bluetoothd-autopair.o `test -f 'plugins/autopair.c' || echo '$(srcdir)/'`plugins/autopair.c plugins/bluetoothd-autopair.obj: plugins/autopair.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_bluetoothd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT plugins/bluetoothd-autopair.obj -MD -MP -MF plugins/$(DEPDIR)/bluetoothd-autopair.Tpo -c -o plugins/bluetoothd-autopair.obj `if test -f 'plugins/autopair.c'; then $(CYGPATH_W) 'plugins/autopair.c'; else $(CYGPATH_W) '$(srcdir)/plugins/autopair.c'; fi` @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) plugins/$(DEPDIR)/bluetoothd-autopair.Tpo plugins/$(DEPDIR)/bluetoothd-autopair.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='plugins/autopair.c' object='plugins/bluetoothd-autopair.obj' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_bluetoothd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o plugins/bluetoothd-autopair.obj `if test -f 'plugins/autopair.c'; then $(CYGPATH_W) 'plugins/autopair.c'; else $(CYGPATH_W) '$(srcdir)/plugins/autopair.c'; fi` plugins/bluetoothd-policy.o: plugins/policy.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_bluetoothd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT plugins/bluetoothd-policy.o -MD -MP -MF plugins/$(DEPDIR)/bluetoothd-policy.Tpo -c -o plugins/bluetoothd-policy.o `test -f 'plugins/policy.c' || echo '$(srcdir)/'`plugins/policy.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) plugins/$(DEPDIR)/bluetoothd-policy.Tpo plugins/$(DEPDIR)/bluetoothd-policy.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='plugins/policy.c' object='plugins/bluetoothd-policy.o' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_bluetoothd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o plugins/bluetoothd-policy.o `test -f 'plugins/policy.c' || echo '$(srcdir)/'`plugins/policy.c plugins/bluetoothd-policy.obj: plugins/policy.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_bluetoothd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT plugins/bluetoothd-policy.obj -MD -MP -MF plugins/$(DEPDIR)/bluetoothd-policy.Tpo -c -o plugins/bluetoothd-policy.obj `if test -f 'plugins/policy.c'; then $(CYGPATH_W) 'plugins/policy.c'; else $(CYGPATH_W) '$(srcdir)/plugins/policy.c'; fi` @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) plugins/$(DEPDIR)/bluetoothd-policy.Tpo plugins/$(DEPDIR)/bluetoothd-policy.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='plugins/policy.c' object='plugins/bluetoothd-policy.obj' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_bluetoothd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o plugins/bluetoothd-policy.obj `if test -f 'plugins/policy.c'; then $(CYGPATH_W) 'plugins/policy.c'; else $(CYGPATH_W) '$(srcdir)/plugins/policy.c'; fi` profiles/audio/bluetoothd-media.o: profiles/audio/media.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_bluetoothd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT profiles/audio/bluetoothd-media.o -MD -MP -MF profiles/audio/$(DEPDIR)/bluetoothd-media.Tpo -c -o profiles/audio/bluetoothd-media.o `test -f 'profiles/audio/media.c' || echo '$(srcdir)/'`profiles/audio/media.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) profiles/audio/$(DEPDIR)/bluetoothd-media.Tpo profiles/audio/$(DEPDIR)/bluetoothd-media.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='profiles/audio/media.c' object='profiles/audio/bluetoothd-media.o' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_bluetoothd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o profiles/audio/bluetoothd-media.o `test -f 'profiles/audio/media.c' || echo '$(srcdir)/'`profiles/audio/media.c profiles/audio/bluetoothd-media.obj: profiles/audio/media.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_bluetoothd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT profiles/audio/bluetoothd-media.obj -MD -MP -MF profiles/audio/$(DEPDIR)/bluetoothd-media.Tpo -c -o profiles/audio/bluetoothd-media.obj `if test -f 'profiles/audio/media.c'; then $(CYGPATH_W) 'profiles/audio/media.c'; else $(CYGPATH_W) '$(srcdir)/profiles/audio/media.c'; fi` @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) profiles/audio/$(DEPDIR)/bluetoothd-media.Tpo profiles/audio/$(DEPDIR)/bluetoothd-media.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='profiles/audio/media.c' object='profiles/audio/bluetoothd-media.obj' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_bluetoothd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o profiles/audio/bluetoothd-media.obj `if test -f 'profiles/audio/media.c'; then $(CYGPATH_W) 'profiles/audio/media.c'; else $(CYGPATH_W) '$(srcdir)/profiles/audio/media.c'; fi` profiles/audio/bluetoothd-transport.o: profiles/audio/transport.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_bluetoothd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT profiles/audio/bluetoothd-transport.o -MD -MP -MF profiles/audio/$(DEPDIR)/bluetoothd-transport.Tpo -c -o profiles/audio/bluetoothd-transport.o `test -f 'profiles/audio/transport.c' || echo '$(srcdir)/'`profiles/audio/transport.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) profiles/audio/$(DEPDIR)/bluetoothd-transport.Tpo profiles/audio/$(DEPDIR)/bluetoothd-transport.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='profiles/audio/transport.c' object='profiles/audio/bluetoothd-transport.o' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_bluetoothd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o profiles/audio/bluetoothd-transport.o `test -f 'profiles/audio/transport.c' || echo '$(srcdir)/'`profiles/audio/transport.c profiles/audio/bluetoothd-transport.obj: profiles/audio/transport.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_bluetoothd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT profiles/audio/bluetoothd-transport.obj -MD -MP -MF profiles/audio/$(DEPDIR)/bluetoothd-transport.Tpo -c -o profiles/audio/bluetoothd-transport.obj `if test -f 'profiles/audio/transport.c'; then $(CYGPATH_W) 'profiles/audio/transport.c'; else $(CYGPATH_W) '$(srcdir)/profiles/audio/transport.c'; fi` @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) profiles/audio/$(DEPDIR)/bluetoothd-transport.Tpo profiles/audio/$(DEPDIR)/bluetoothd-transport.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='profiles/audio/transport.c' object='profiles/audio/bluetoothd-transport.obj' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_bluetoothd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o profiles/audio/bluetoothd-transport.obj `if test -f 'profiles/audio/transport.c'; then $(CYGPATH_W) 'profiles/audio/transport.c'; else $(CYGPATH_W) '$(srcdir)/profiles/audio/transport.c'; fi` profiles/audio/bluetoothd-player.o: profiles/audio/player.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_bluetoothd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT profiles/audio/bluetoothd-player.o -MD -MP -MF profiles/audio/$(DEPDIR)/bluetoothd-player.Tpo -c -o profiles/audio/bluetoothd-player.o `test -f 'profiles/audio/player.c' || echo '$(srcdir)/'`profiles/audio/player.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) profiles/audio/$(DEPDIR)/bluetoothd-player.Tpo profiles/audio/$(DEPDIR)/bluetoothd-player.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='profiles/audio/player.c' object='profiles/audio/bluetoothd-player.o' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_bluetoothd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o profiles/audio/bluetoothd-player.o `test -f 'profiles/audio/player.c' || echo '$(srcdir)/'`profiles/audio/player.c profiles/audio/bluetoothd-player.obj: profiles/audio/player.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_bluetoothd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT profiles/audio/bluetoothd-player.obj -MD -MP -MF profiles/audio/$(DEPDIR)/bluetoothd-player.Tpo -c -o profiles/audio/bluetoothd-player.obj `if test -f 'profiles/audio/player.c'; then $(CYGPATH_W) 'profiles/audio/player.c'; else $(CYGPATH_W) '$(srcdir)/profiles/audio/player.c'; fi` @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) profiles/audio/$(DEPDIR)/bluetoothd-player.Tpo profiles/audio/$(DEPDIR)/bluetoothd-player.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='profiles/audio/player.c' object='profiles/audio/bluetoothd-player.obj' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_bluetoothd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o profiles/audio/bluetoothd-player.obj `if test -f 'profiles/audio/player.c'; then $(CYGPATH_W) 'profiles/audio/player.c'; else $(CYGPATH_W) '$(srcdir)/profiles/audio/player.c'; fi` plugins/bluetoothd-admin.o: plugins/admin.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_bluetoothd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT plugins/bluetoothd-admin.o -MD -MP -MF plugins/$(DEPDIR)/bluetoothd-admin.Tpo -c -o plugins/bluetoothd-admin.o `test -f 'plugins/admin.c' || echo '$(srcdir)/'`plugins/admin.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) plugins/$(DEPDIR)/bluetoothd-admin.Tpo plugins/$(DEPDIR)/bluetoothd-admin.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='plugins/admin.c' object='plugins/bluetoothd-admin.o' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_bluetoothd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o plugins/bluetoothd-admin.o `test -f 'plugins/admin.c' || echo '$(srcdir)/'`plugins/admin.c plugins/bluetoothd-admin.obj: plugins/admin.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_bluetoothd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT plugins/bluetoothd-admin.obj -MD -MP -MF plugins/$(DEPDIR)/bluetoothd-admin.Tpo -c -o plugins/bluetoothd-admin.obj `if test -f 'plugins/admin.c'; then $(CYGPATH_W) 'plugins/admin.c'; else $(CYGPATH_W) '$(srcdir)/plugins/admin.c'; fi` @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) plugins/$(DEPDIR)/bluetoothd-admin.Tpo plugins/$(DEPDIR)/bluetoothd-admin.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='plugins/admin.c' object='plugins/bluetoothd-admin.obj' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_bluetoothd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o plugins/bluetoothd-admin.obj `if test -f 'plugins/admin.c'; then $(CYGPATH_W) 'plugins/admin.c'; else $(CYGPATH_W) '$(srcdir)/plugins/admin.c'; fi` plugins/bluetoothd-neard.o: plugins/neard.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_bluetoothd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT plugins/bluetoothd-neard.o -MD -MP -MF plugins/$(DEPDIR)/bluetoothd-neard.Tpo -c -o plugins/bluetoothd-neard.o `test -f 'plugins/neard.c' || echo '$(srcdir)/'`plugins/neard.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) plugins/$(DEPDIR)/bluetoothd-neard.Tpo plugins/$(DEPDIR)/bluetoothd-neard.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='plugins/neard.c' object='plugins/bluetoothd-neard.o' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_bluetoothd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o plugins/bluetoothd-neard.o `test -f 'plugins/neard.c' || echo '$(srcdir)/'`plugins/neard.c plugins/bluetoothd-neard.obj: plugins/neard.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_bluetoothd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT plugins/bluetoothd-neard.obj -MD -MP -MF plugins/$(DEPDIR)/bluetoothd-neard.Tpo -c -o plugins/bluetoothd-neard.obj `if test -f 'plugins/neard.c'; then $(CYGPATH_W) 'plugins/neard.c'; else $(CYGPATH_W) '$(srcdir)/plugins/neard.c'; fi` @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) plugins/$(DEPDIR)/bluetoothd-neard.Tpo plugins/$(DEPDIR)/bluetoothd-neard.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='plugins/neard.c' object='plugins/bluetoothd-neard.obj' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_bluetoothd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o plugins/bluetoothd-neard.obj `if test -f 'plugins/neard.c'; then $(CYGPATH_W) 'plugins/neard.c'; else $(CYGPATH_W) '$(srcdir)/plugins/neard.c'; fi` profiles/sap/bluetoothd-main.o: profiles/sap/main.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_bluetoothd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT profiles/sap/bluetoothd-main.o -MD -MP -MF profiles/sap/$(DEPDIR)/bluetoothd-main.Tpo -c -o profiles/sap/bluetoothd-main.o `test -f 'profiles/sap/main.c' || echo '$(srcdir)/'`profiles/sap/main.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) profiles/sap/$(DEPDIR)/bluetoothd-main.Tpo profiles/sap/$(DEPDIR)/bluetoothd-main.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='profiles/sap/main.c' object='profiles/sap/bluetoothd-main.o' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_bluetoothd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o profiles/sap/bluetoothd-main.o `test -f 'profiles/sap/main.c' || echo '$(srcdir)/'`profiles/sap/main.c profiles/sap/bluetoothd-main.obj: profiles/sap/main.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_bluetoothd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT profiles/sap/bluetoothd-main.obj -MD -MP -MF profiles/sap/$(DEPDIR)/bluetoothd-main.Tpo -c -o profiles/sap/bluetoothd-main.obj `if test -f 'profiles/sap/main.c'; then $(CYGPATH_W) 'profiles/sap/main.c'; else $(CYGPATH_W) '$(srcdir)/profiles/sap/main.c'; fi` @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) profiles/sap/$(DEPDIR)/bluetoothd-main.Tpo profiles/sap/$(DEPDIR)/bluetoothd-main.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='profiles/sap/main.c' object='profiles/sap/bluetoothd-main.obj' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_bluetoothd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o profiles/sap/bluetoothd-main.obj `if test -f 'profiles/sap/main.c'; then $(CYGPATH_W) 'profiles/sap/main.c'; else $(CYGPATH_W) '$(srcdir)/profiles/sap/main.c'; fi` profiles/sap/bluetoothd-manager.o: profiles/sap/manager.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_bluetoothd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT profiles/sap/bluetoothd-manager.o -MD -MP -MF profiles/sap/$(DEPDIR)/bluetoothd-manager.Tpo -c -o profiles/sap/bluetoothd-manager.o `test -f 'profiles/sap/manager.c' || echo '$(srcdir)/'`profiles/sap/manager.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) profiles/sap/$(DEPDIR)/bluetoothd-manager.Tpo profiles/sap/$(DEPDIR)/bluetoothd-manager.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='profiles/sap/manager.c' object='profiles/sap/bluetoothd-manager.o' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_bluetoothd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o profiles/sap/bluetoothd-manager.o `test -f 'profiles/sap/manager.c' || echo '$(srcdir)/'`profiles/sap/manager.c profiles/sap/bluetoothd-manager.obj: profiles/sap/manager.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_bluetoothd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT profiles/sap/bluetoothd-manager.obj -MD -MP -MF profiles/sap/$(DEPDIR)/bluetoothd-manager.Tpo -c -o profiles/sap/bluetoothd-manager.obj `if test -f 'profiles/sap/manager.c'; then $(CYGPATH_W) 'profiles/sap/manager.c'; else $(CYGPATH_W) '$(srcdir)/profiles/sap/manager.c'; fi` @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) profiles/sap/$(DEPDIR)/bluetoothd-manager.Tpo profiles/sap/$(DEPDIR)/bluetoothd-manager.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='profiles/sap/manager.c' object='profiles/sap/bluetoothd-manager.obj' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_bluetoothd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o profiles/sap/bluetoothd-manager.obj `if test -f 'profiles/sap/manager.c'; then $(CYGPATH_W) 'profiles/sap/manager.c'; else $(CYGPATH_W) '$(srcdir)/profiles/sap/manager.c'; fi` profiles/sap/bluetoothd-server.o: profiles/sap/server.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_bluetoothd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT profiles/sap/bluetoothd-server.o -MD -MP -MF profiles/sap/$(DEPDIR)/bluetoothd-server.Tpo -c -o profiles/sap/bluetoothd-server.o `test -f 'profiles/sap/server.c' || echo '$(srcdir)/'`profiles/sap/server.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) profiles/sap/$(DEPDIR)/bluetoothd-server.Tpo profiles/sap/$(DEPDIR)/bluetoothd-server.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='profiles/sap/server.c' object='profiles/sap/bluetoothd-server.o' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_bluetoothd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o profiles/sap/bluetoothd-server.o `test -f 'profiles/sap/server.c' || echo '$(srcdir)/'`profiles/sap/server.c profiles/sap/bluetoothd-server.obj: profiles/sap/server.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_bluetoothd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT profiles/sap/bluetoothd-server.obj -MD -MP -MF profiles/sap/$(DEPDIR)/bluetoothd-server.Tpo -c -o profiles/sap/bluetoothd-server.obj `if test -f 'profiles/sap/server.c'; then $(CYGPATH_W) 'profiles/sap/server.c'; else $(CYGPATH_W) '$(srcdir)/profiles/sap/server.c'; fi` @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) profiles/sap/$(DEPDIR)/bluetoothd-server.Tpo profiles/sap/$(DEPDIR)/bluetoothd-server.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='profiles/sap/server.c' object='profiles/sap/bluetoothd-server.obj' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_bluetoothd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o profiles/sap/bluetoothd-server.obj `if test -f 'profiles/sap/server.c'; then $(CYGPATH_W) 'profiles/sap/server.c'; else $(CYGPATH_W) '$(srcdir)/profiles/sap/server.c'; fi` profiles/sap/bluetoothd-sap-dummy.o: profiles/sap/sap-dummy.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_bluetoothd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT profiles/sap/bluetoothd-sap-dummy.o -MD -MP -MF profiles/sap/$(DEPDIR)/bluetoothd-sap-dummy.Tpo -c -o profiles/sap/bluetoothd-sap-dummy.o `test -f 'profiles/sap/sap-dummy.c' || echo '$(srcdir)/'`profiles/sap/sap-dummy.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) profiles/sap/$(DEPDIR)/bluetoothd-sap-dummy.Tpo profiles/sap/$(DEPDIR)/bluetoothd-sap-dummy.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='profiles/sap/sap-dummy.c' object='profiles/sap/bluetoothd-sap-dummy.o' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_bluetoothd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o profiles/sap/bluetoothd-sap-dummy.o `test -f 'profiles/sap/sap-dummy.c' || echo '$(srcdir)/'`profiles/sap/sap-dummy.c profiles/sap/bluetoothd-sap-dummy.obj: profiles/sap/sap-dummy.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_bluetoothd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT profiles/sap/bluetoothd-sap-dummy.obj -MD -MP -MF profiles/sap/$(DEPDIR)/bluetoothd-sap-dummy.Tpo -c -o profiles/sap/bluetoothd-sap-dummy.obj `if test -f 'profiles/sap/sap-dummy.c'; then $(CYGPATH_W) 'profiles/sap/sap-dummy.c'; else $(CYGPATH_W) '$(srcdir)/profiles/sap/sap-dummy.c'; fi` @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) profiles/sap/$(DEPDIR)/bluetoothd-sap-dummy.Tpo profiles/sap/$(DEPDIR)/bluetoothd-sap-dummy.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='profiles/sap/sap-dummy.c' object='profiles/sap/bluetoothd-sap-dummy.obj' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_bluetoothd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o profiles/sap/bluetoothd-sap-dummy.obj `if test -f 'profiles/sap/sap-dummy.c'; then $(CYGPATH_W) 'profiles/sap/sap-dummy.c'; else $(CYGPATH_W) '$(srcdir)/profiles/sap/sap-dummy.c'; fi` profiles/audio/bluetoothd-source.o: profiles/audio/source.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_bluetoothd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT profiles/audio/bluetoothd-source.o -MD -MP -MF profiles/audio/$(DEPDIR)/bluetoothd-source.Tpo -c -o profiles/audio/bluetoothd-source.o `test -f 'profiles/audio/source.c' || echo '$(srcdir)/'`profiles/audio/source.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) profiles/audio/$(DEPDIR)/bluetoothd-source.Tpo profiles/audio/$(DEPDIR)/bluetoothd-source.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='profiles/audio/source.c' object='profiles/audio/bluetoothd-source.o' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_bluetoothd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o profiles/audio/bluetoothd-source.o `test -f 'profiles/audio/source.c' || echo '$(srcdir)/'`profiles/audio/source.c profiles/audio/bluetoothd-source.obj: profiles/audio/source.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_bluetoothd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT profiles/audio/bluetoothd-source.obj -MD -MP -MF profiles/audio/$(DEPDIR)/bluetoothd-source.Tpo -c -o profiles/audio/bluetoothd-source.obj `if test -f 'profiles/audio/source.c'; then $(CYGPATH_W) 'profiles/audio/source.c'; else $(CYGPATH_W) '$(srcdir)/profiles/audio/source.c'; fi` @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) profiles/audio/$(DEPDIR)/bluetoothd-source.Tpo profiles/audio/$(DEPDIR)/bluetoothd-source.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='profiles/audio/source.c' object='profiles/audio/bluetoothd-source.obj' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_bluetoothd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o profiles/audio/bluetoothd-source.obj `if test -f 'profiles/audio/source.c'; then $(CYGPATH_W) 'profiles/audio/source.c'; else $(CYGPATH_W) '$(srcdir)/profiles/audio/source.c'; fi` profiles/audio/bluetoothd-sink.o: profiles/audio/sink.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_bluetoothd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT profiles/audio/bluetoothd-sink.o -MD -MP -MF profiles/audio/$(DEPDIR)/bluetoothd-sink.Tpo -c -o profiles/audio/bluetoothd-sink.o `test -f 'profiles/audio/sink.c' || echo '$(srcdir)/'`profiles/audio/sink.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) profiles/audio/$(DEPDIR)/bluetoothd-sink.Tpo profiles/audio/$(DEPDIR)/bluetoothd-sink.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='profiles/audio/sink.c' object='profiles/audio/bluetoothd-sink.o' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_bluetoothd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o profiles/audio/bluetoothd-sink.o `test -f 'profiles/audio/sink.c' || echo '$(srcdir)/'`profiles/audio/sink.c profiles/audio/bluetoothd-sink.obj: profiles/audio/sink.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_bluetoothd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT profiles/audio/bluetoothd-sink.obj -MD -MP -MF profiles/audio/$(DEPDIR)/bluetoothd-sink.Tpo -c -o profiles/audio/bluetoothd-sink.obj `if test -f 'profiles/audio/sink.c'; then $(CYGPATH_W) 'profiles/audio/sink.c'; else $(CYGPATH_W) '$(srcdir)/profiles/audio/sink.c'; fi` @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) profiles/audio/$(DEPDIR)/bluetoothd-sink.Tpo profiles/audio/$(DEPDIR)/bluetoothd-sink.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='profiles/audio/sink.c' object='profiles/audio/bluetoothd-sink.obj' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_bluetoothd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o profiles/audio/bluetoothd-sink.obj `if test -f 'profiles/audio/sink.c'; then $(CYGPATH_W) 'profiles/audio/sink.c'; else $(CYGPATH_W) '$(srcdir)/profiles/audio/sink.c'; fi` profiles/audio/bluetoothd-a2dp.o: profiles/audio/a2dp.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_bluetoothd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT profiles/audio/bluetoothd-a2dp.o -MD -MP -MF profiles/audio/$(DEPDIR)/bluetoothd-a2dp.Tpo -c -o profiles/audio/bluetoothd-a2dp.o `test -f 'profiles/audio/a2dp.c' || echo '$(srcdir)/'`profiles/audio/a2dp.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) profiles/audio/$(DEPDIR)/bluetoothd-a2dp.Tpo profiles/audio/$(DEPDIR)/bluetoothd-a2dp.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='profiles/audio/a2dp.c' object='profiles/audio/bluetoothd-a2dp.o' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_bluetoothd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o profiles/audio/bluetoothd-a2dp.o `test -f 'profiles/audio/a2dp.c' || echo '$(srcdir)/'`profiles/audio/a2dp.c profiles/audio/bluetoothd-a2dp.obj: profiles/audio/a2dp.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_bluetoothd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT profiles/audio/bluetoothd-a2dp.obj -MD -MP -MF profiles/audio/$(DEPDIR)/bluetoothd-a2dp.Tpo -c -o profiles/audio/bluetoothd-a2dp.obj `if test -f 'profiles/audio/a2dp.c'; then $(CYGPATH_W) 'profiles/audio/a2dp.c'; else $(CYGPATH_W) '$(srcdir)/profiles/audio/a2dp.c'; fi` @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) profiles/audio/$(DEPDIR)/bluetoothd-a2dp.Tpo profiles/audio/$(DEPDIR)/bluetoothd-a2dp.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='profiles/audio/a2dp.c' object='profiles/audio/bluetoothd-a2dp.obj' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_bluetoothd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o profiles/audio/bluetoothd-a2dp.obj `if test -f 'profiles/audio/a2dp.c'; then $(CYGPATH_W) 'profiles/audio/a2dp.c'; else $(CYGPATH_W) '$(srcdir)/profiles/audio/a2dp.c'; fi` profiles/audio/bluetoothd-avdtp.o: profiles/audio/avdtp.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_bluetoothd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT profiles/audio/bluetoothd-avdtp.o -MD -MP -MF profiles/audio/$(DEPDIR)/bluetoothd-avdtp.Tpo -c -o profiles/audio/bluetoothd-avdtp.o `test -f 'profiles/audio/avdtp.c' || echo '$(srcdir)/'`profiles/audio/avdtp.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) profiles/audio/$(DEPDIR)/bluetoothd-avdtp.Tpo profiles/audio/$(DEPDIR)/bluetoothd-avdtp.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='profiles/audio/avdtp.c' object='profiles/audio/bluetoothd-avdtp.o' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_bluetoothd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o profiles/audio/bluetoothd-avdtp.o `test -f 'profiles/audio/avdtp.c' || echo '$(srcdir)/'`profiles/audio/avdtp.c profiles/audio/bluetoothd-avdtp.obj: profiles/audio/avdtp.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_bluetoothd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT profiles/audio/bluetoothd-avdtp.obj -MD -MP -MF profiles/audio/$(DEPDIR)/bluetoothd-avdtp.Tpo -c -o profiles/audio/bluetoothd-avdtp.obj `if test -f 'profiles/audio/avdtp.c'; then $(CYGPATH_W) 'profiles/audio/avdtp.c'; else $(CYGPATH_W) '$(srcdir)/profiles/audio/avdtp.c'; fi` @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) profiles/audio/$(DEPDIR)/bluetoothd-avdtp.Tpo profiles/audio/$(DEPDIR)/bluetoothd-avdtp.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='profiles/audio/avdtp.c' object='profiles/audio/bluetoothd-avdtp.obj' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_bluetoothd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o profiles/audio/bluetoothd-avdtp.obj `if test -f 'profiles/audio/avdtp.c'; then $(CYGPATH_W) 'profiles/audio/avdtp.c'; else $(CYGPATH_W) '$(srcdir)/profiles/audio/avdtp.c'; fi` profiles/audio/bluetoothd-control.o: profiles/audio/control.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_bluetoothd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT profiles/audio/bluetoothd-control.o -MD -MP -MF profiles/audio/$(DEPDIR)/bluetoothd-control.Tpo -c -o profiles/audio/bluetoothd-control.o `test -f 'profiles/audio/control.c' || echo '$(srcdir)/'`profiles/audio/control.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) profiles/audio/$(DEPDIR)/bluetoothd-control.Tpo profiles/audio/$(DEPDIR)/bluetoothd-control.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='profiles/audio/control.c' object='profiles/audio/bluetoothd-control.o' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_bluetoothd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o profiles/audio/bluetoothd-control.o `test -f 'profiles/audio/control.c' || echo '$(srcdir)/'`profiles/audio/control.c profiles/audio/bluetoothd-control.obj: profiles/audio/control.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_bluetoothd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT profiles/audio/bluetoothd-control.obj -MD -MP -MF profiles/audio/$(DEPDIR)/bluetoothd-control.Tpo -c -o profiles/audio/bluetoothd-control.obj `if test -f 'profiles/audio/control.c'; then $(CYGPATH_W) 'profiles/audio/control.c'; else $(CYGPATH_W) '$(srcdir)/profiles/audio/control.c'; fi` @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) profiles/audio/$(DEPDIR)/bluetoothd-control.Tpo profiles/audio/$(DEPDIR)/bluetoothd-control.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='profiles/audio/control.c' object='profiles/audio/bluetoothd-control.obj' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_bluetoothd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o profiles/audio/bluetoothd-control.obj `if test -f 'profiles/audio/control.c'; then $(CYGPATH_W) 'profiles/audio/control.c'; else $(CYGPATH_W) '$(srcdir)/profiles/audio/control.c'; fi` profiles/audio/bluetoothd-avctp.o: profiles/audio/avctp.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_bluetoothd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT profiles/audio/bluetoothd-avctp.o -MD -MP -MF profiles/audio/$(DEPDIR)/bluetoothd-avctp.Tpo -c -o profiles/audio/bluetoothd-avctp.o `test -f 'profiles/audio/avctp.c' || echo '$(srcdir)/'`profiles/audio/avctp.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) profiles/audio/$(DEPDIR)/bluetoothd-avctp.Tpo profiles/audio/$(DEPDIR)/bluetoothd-avctp.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='profiles/audio/avctp.c' object='profiles/audio/bluetoothd-avctp.o' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_bluetoothd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o profiles/audio/bluetoothd-avctp.o `test -f 'profiles/audio/avctp.c' || echo '$(srcdir)/'`profiles/audio/avctp.c profiles/audio/bluetoothd-avctp.obj: profiles/audio/avctp.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_bluetoothd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT profiles/audio/bluetoothd-avctp.obj -MD -MP -MF profiles/audio/$(DEPDIR)/bluetoothd-avctp.Tpo -c -o profiles/audio/bluetoothd-avctp.obj `if test -f 'profiles/audio/avctp.c'; then $(CYGPATH_W) 'profiles/audio/avctp.c'; else $(CYGPATH_W) '$(srcdir)/profiles/audio/avctp.c'; fi` @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) profiles/audio/$(DEPDIR)/bluetoothd-avctp.Tpo profiles/audio/$(DEPDIR)/bluetoothd-avctp.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='profiles/audio/avctp.c' object='profiles/audio/bluetoothd-avctp.obj' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_bluetoothd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o profiles/audio/bluetoothd-avctp.obj `if test -f 'profiles/audio/avctp.c'; then $(CYGPATH_W) 'profiles/audio/avctp.c'; else $(CYGPATH_W) '$(srcdir)/profiles/audio/avctp.c'; fi` profiles/audio/bluetoothd-avrcp.o: profiles/audio/avrcp.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_bluetoothd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT profiles/audio/bluetoothd-avrcp.o -MD -MP -MF profiles/audio/$(DEPDIR)/bluetoothd-avrcp.Tpo -c -o profiles/audio/bluetoothd-avrcp.o `test -f 'profiles/audio/avrcp.c' || echo '$(srcdir)/'`profiles/audio/avrcp.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) profiles/audio/$(DEPDIR)/bluetoothd-avrcp.Tpo profiles/audio/$(DEPDIR)/bluetoothd-avrcp.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='profiles/audio/avrcp.c' object='profiles/audio/bluetoothd-avrcp.o' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_bluetoothd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o profiles/audio/bluetoothd-avrcp.o `test -f 'profiles/audio/avrcp.c' || echo '$(srcdir)/'`profiles/audio/avrcp.c profiles/audio/bluetoothd-avrcp.obj: profiles/audio/avrcp.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_bluetoothd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT profiles/audio/bluetoothd-avrcp.obj -MD -MP -MF profiles/audio/$(DEPDIR)/bluetoothd-avrcp.Tpo -c -o profiles/audio/bluetoothd-avrcp.obj `if test -f 'profiles/audio/avrcp.c'; then $(CYGPATH_W) 'profiles/audio/avrcp.c'; else $(CYGPATH_W) '$(srcdir)/profiles/audio/avrcp.c'; fi` @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) profiles/audio/$(DEPDIR)/bluetoothd-avrcp.Tpo profiles/audio/$(DEPDIR)/bluetoothd-avrcp.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='profiles/audio/avrcp.c' object='profiles/audio/bluetoothd-avrcp.obj' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_bluetoothd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o profiles/audio/bluetoothd-avrcp.obj `if test -f 'profiles/audio/avrcp.c'; then $(CYGPATH_W) 'profiles/audio/avrcp.c'; else $(CYGPATH_W) '$(srcdir)/profiles/audio/avrcp.c'; fi` profiles/network/bluetoothd-manager.o: profiles/network/manager.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_bluetoothd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT profiles/network/bluetoothd-manager.o -MD -MP -MF profiles/network/$(DEPDIR)/bluetoothd-manager.Tpo -c -o profiles/network/bluetoothd-manager.o `test -f 'profiles/network/manager.c' || echo '$(srcdir)/'`profiles/network/manager.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) profiles/network/$(DEPDIR)/bluetoothd-manager.Tpo profiles/network/$(DEPDIR)/bluetoothd-manager.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='profiles/network/manager.c' object='profiles/network/bluetoothd-manager.o' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_bluetoothd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o profiles/network/bluetoothd-manager.o `test -f 'profiles/network/manager.c' || echo '$(srcdir)/'`profiles/network/manager.c profiles/network/bluetoothd-manager.obj: profiles/network/manager.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_bluetoothd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT profiles/network/bluetoothd-manager.obj -MD -MP -MF profiles/network/$(DEPDIR)/bluetoothd-manager.Tpo -c -o profiles/network/bluetoothd-manager.obj `if test -f 'profiles/network/manager.c'; then $(CYGPATH_W) 'profiles/network/manager.c'; else $(CYGPATH_W) '$(srcdir)/profiles/network/manager.c'; fi` @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) profiles/network/$(DEPDIR)/bluetoothd-manager.Tpo profiles/network/$(DEPDIR)/bluetoothd-manager.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='profiles/network/manager.c' object='profiles/network/bluetoothd-manager.obj' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_bluetoothd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o profiles/network/bluetoothd-manager.obj `if test -f 'profiles/network/manager.c'; then $(CYGPATH_W) 'profiles/network/manager.c'; else $(CYGPATH_W) '$(srcdir)/profiles/network/manager.c'; fi` profiles/network/bluetoothd-bnep.o: profiles/network/bnep.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_bluetoothd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT profiles/network/bluetoothd-bnep.o -MD -MP -MF profiles/network/$(DEPDIR)/bluetoothd-bnep.Tpo -c -o profiles/network/bluetoothd-bnep.o `test -f 'profiles/network/bnep.c' || echo '$(srcdir)/'`profiles/network/bnep.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) profiles/network/$(DEPDIR)/bluetoothd-bnep.Tpo profiles/network/$(DEPDIR)/bluetoothd-bnep.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='profiles/network/bnep.c' object='profiles/network/bluetoothd-bnep.o' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_bluetoothd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o profiles/network/bluetoothd-bnep.o `test -f 'profiles/network/bnep.c' || echo '$(srcdir)/'`profiles/network/bnep.c profiles/network/bluetoothd-bnep.obj: profiles/network/bnep.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_bluetoothd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT profiles/network/bluetoothd-bnep.obj -MD -MP -MF profiles/network/$(DEPDIR)/bluetoothd-bnep.Tpo -c -o profiles/network/bluetoothd-bnep.obj `if test -f 'profiles/network/bnep.c'; then $(CYGPATH_W) 'profiles/network/bnep.c'; else $(CYGPATH_W) '$(srcdir)/profiles/network/bnep.c'; fi` @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) profiles/network/$(DEPDIR)/bluetoothd-bnep.Tpo profiles/network/$(DEPDIR)/bluetoothd-bnep.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='profiles/network/bnep.c' object='profiles/network/bluetoothd-bnep.obj' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_bluetoothd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o profiles/network/bluetoothd-bnep.obj `if test -f 'profiles/network/bnep.c'; then $(CYGPATH_W) 'profiles/network/bnep.c'; else $(CYGPATH_W) '$(srcdir)/profiles/network/bnep.c'; fi` profiles/network/bluetoothd-server.o: profiles/network/server.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_bluetoothd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT profiles/network/bluetoothd-server.o -MD -MP -MF profiles/network/$(DEPDIR)/bluetoothd-server.Tpo -c -o profiles/network/bluetoothd-server.o `test -f 'profiles/network/server.c' || echo '$(srcdir)/'`profiles/network/server.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) profiles/network/$(DEPDIR)/bluetoothd-server.Tpo profiles/network/$(DEPDIR)/bluetoothd-server.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='profiles/network/server.c' object='profiles/network/bluetoothd-server.o' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_bluetoothd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o profiles/network/bluetoothd-server.o `test -f 'profiles/network/server.c' || echo '$(srcdir)/'`profiles/network/server.c profiles/network/bluetoothd-server.obj: profiles/network/server.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_bluetoothd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT profiles/network/bluetoothd-server.obj -MD -MP -MF profiles/network/$(DEPDIR)/bluetoothd-server.Tpo -c -o profiles/network/bluetoothd-server.obj `if test -f 'profiles/network/server.c'; then $(CYGPATH_W) 'profiles/network/server.c'; else $(CYGPATH_W) '$(srcdir)/profiles/network/server.c'; fi` @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) profiles/network/$(DEPDIR)/bluetoothd-server.Tpo profiles/network/$(DEPDIR)/bluetoothd-server.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='profiles/network/server.c' object='profiles/network/bluetoothd-server.obj' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_bluetoothd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o profiles/network/bluetoothd-server.obj `if test -f 'profiles/network/server.c'; then $(CYGPATH_W) 'profiles/network/server.c'; else $(CYGPATH_W) '$(srcdir)/profiles/network/server.c'; fi` profiles/network/bluetoothd-connection.o: profiles/network/connection.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_bluetoothd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT profiles/network/bluetoothd-connection.o -MD -MP -MF profiles/network/$(DEPDIR)/bluetoothd-connection.Tpo -c -o profiles/network/bluetoothd-connection.o `test -f 'profiles/network/connection.c' || echo '$(srcdir)/'`profiles/network/connection.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) profiles/network/$(DEPDIR)/bluetoothd-connection.Tpo profiles/network/$(DEPDIR)/bluetoothd-connection.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='profiles/network/connection.c' object='profiles/network/bluetoothd-connection.o' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_bluetoothd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o profiles/network/bluetoothd-connection.o `test -f 'profiles/network/connection.c' || echo '$(srcdir)/'`profiles/network/connection.c profiles/network/bluetoothd-connection.obj: profiles/network/connection.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_bluetoothd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT profiles/network/bluetoothd-connection.obj -MD -MP -MF profiles/network/$(DEPDIR)/bluetoothd-connection.Tpo -c -o profiles/network/bluetoothd-connection.obj `if test -f 'profiles/network/connection.c'; then $(CYGPATH_W) 'profiles/network/connection.c'; else $(CYGPATH_W) '$(srcdir)/profiles/network/connection.c'; fi` @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) profiles/network/$(DEPDIR)/bluetoothd-connection.Tpo profiles/network/$(DEPDIR)/bluetoothd-connection.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='profiles/network/connection.c' object='profiles/network/bluetoothd-connection.obj' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_bluetoothd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o profiles/network/bluetoothd-connection.obj `if test -f 'profiles/network/connection.c'; then $(CYGPATH_W) 'profiles/network/connection.c'; else $(CYGPATH_W) '$(srcdir)/profiles/network/connection.c'; fi` profiles/input/bluetoothd-manager.o: profiles/input/manager.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_bluetoothd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT profiles/input/bluetoothd-manager.o -MD -MP -MF profiles/input/$(DEPDIR)/bluetoothd-manager.Tpo -c -o profiles/input/bluetoothd-manager.o `test -f 'profiles/input/manager.c' || echo '$(srcdir)/'`profiles/input/manager.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) profiles/input/$(DEPDIR)/bluetoothd-manager.Tpo profiles/input/$(DEPDIR)/bluetoothd-manager.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='profiles/input/manager.c' object='profiles/input/bluetoothd-manager.o' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_bluetoothd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o profiles/input/bluetoothd-manager.o `test -f 'profiles/input/manager.c' || echo '$(srcdir)/'`profiles/input/manager.c profiles/input/bluetoothd-manager.obj: profiles/input/manager.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_bluetoothd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT profiles/input/bluetoothd-manager.obj -MD -MP -MF profiles/input/$(DEPDIR)/bluetoothd-manager.Tpo -c -o profiles/input/bluetoothd-manager.obj `if test -f 'profiles/input/manager.c'; then $(CYGPATH_W) 'profiles/input/manager.c'; else $(CYGPATH_W) '$(srcdir)/profiles/input/manager.c'; fi` @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) profiles/input/$(DEPDIR)/bluetoothd-manager.Tpo profiles/input/$(DEPDIR)/bluetoothd-manager.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='profiles/input/manager.c' object='profiles/input/bluetoothd-manager.obj' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_bluetoothd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o profiles/input/bluetoothd-manager.obj `if test -f 'profiles/input/manager.c'; then $(CYGPATH_W) 'profiles/input/manager.c'; else $(CYGPATH_W) '$(srcdir)/profiles/input/manager.c'; fi` profiles/input/bluetoothd-server.o: profiles/input/server.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_bluetoothd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT profiles/input/bluetoothd-server.o -MD -MP -MF profiles/input/$(DEPDIR)/bluetoothd-server.Tpo -c -o profiles/input/bluetoothd-server.o `test -f 'profiles/input/server.c' || echo '$(srcdir)/'`profiles/input/server.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) profiles/input/$(DEPDIR)/bluetoothd-server.Tpo profiles/input/$(DEPDIR)/bluetoothd-server.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='profiles/input/server.c' object='profiles/input/bluetoothd-server.o' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_bluetoothd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o profiles/input/bluetoothd-server.o `test -f 'profiles/input/server.c' || echo '$(srcdir)/'`profiles/input/server.c profiles/input/bluetoothd-server.obj: profiles/input/server.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_bluetoothd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT profiles/input/bluetoothd-server.obj -MD -MP -MF profiles/input/$(DEPDIR)/bluetoothd-server.Tpo -c -o profiles/input/bluetoothd-server.obj `if test -f 'profiles/input/server.c'; then $(CYGPATH_W) 'profiles/input/server.c'; else $(CYGPATH_W) '$(srcdir)/profiles/input/server.c'; fi` @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) profiles/input/$(DEPDIR)/bluetoothd-server.Tpo profiles/input/$(DEPDIR)/bluetoothd-server.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='profiles/input/server.c' object='profiles/input/bluetoothd-server.obj' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_bluetoothd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o profiles/input/bluetoothd-server.obj `if test -f 'profiles/input/server.c'; then $(CYGPATH_W) 'profiles/input/server.c'; else $(CYGPATH_W) '$(srcdir)/profiles/input/server.c'; fi` profiles/input/bluetoothd-device.o: profiles/input/device.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_bluetoothd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT profiles/input/bluetoothd-device.o -MD -MP -MF profiles/input/$(DEPDIR)/bluetoothd-device.Tpo -c -o profiles/input/bluetoothd-device.o `test -f 'profiles/input/device.c' || echo '$(srcdir)/'`profiles/input/device.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) profiles/input/$(DEPDIR)/bluetoothd-device.Tpo profiles/input/$(DEPDIR)/bluetoothd-device.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='profiles/input/device.c' object='profiles/input/bluetoothd-device.o' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_bluetoothd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o profiles/input/bluetoothd-device.o `test -f 'profiles/input/device.c' || echo '$(srcdir)/'`profiles/input/device.c profiles/input/bluetoothd-device.obj: profiles/input/device.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_bluetoothd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT profiles/input/bluetoothd-device.obj -MD -MP -MF profiles/input/$(DEPDIR)/bluetoothd-device.Tpo -c -o profiles/input/bluetoothd-device.obj `if test -f 'profiles/input/device.c'; then $(CYGPATH_W) 'profiles/input/device.c'; else $(CYGPATH_W) '$(srcdir)/profiles/input/device.c'; fi` @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) profiles/input/$(DEPDIR)/bluetoothd-device.Tpo profiles/input/$(DEPDIR)/bluetoothd-device.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='profiles/input/device.c' object='profiles/input/bluetoothd-device.obj' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_bluetoothd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o profiles/input/bluetoothd-device.obj `if test -f 'profiles/input/device.c'; then $(CYGPATH_W) 'profiles/input/device.c'; else $(CYGPATH_W) '$(srcdir)/profiles/input/device.c'; fi` profiles/input/bluetoothd-hog.o: profiles/input/hog.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_bluetoothd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT profiles/input/bluetoothd-hog.o -MD -MP -MF profiles/input/$(DEPDIR)/bluetoothd-hog.Tpo -c -o profiles/input/bluetoothd-hog.o `test -f 'profiles/input/hog.c' || echo '$(srcdir)/'`profiles/input/hog.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) profiles/input/$(DEPDIR)/bluetoothd-hog.Tpo profiles/input/$(DEPDIR)/bluetoothd-hog.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='profiles/input/hog.c' object='profiles/input/bluetoothd-hog.o' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_bluetoothd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o profiles/input/bluetoothd-hog.o `test -f 'profiles/input/hog.c' || echo '$(srcdir)/'`profiles/input/hog.c profiles/input/bluetoothd-hog.obj: profiles/input/hog.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_bluetoothd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT profiles/input/bluetoothd-hog.obj -MD -MP -MF profiles/input/$(DEPDIR)/bluetoothd-hog.Tpo -c -o profiles/input/bluetoothd-hog.obj `if test -f 'profiles/input/hog.c'; then $(CYGPATH_W) 'profiles/input/hog.c'; else $(CYGPATH_W) '$(srcdir)/profiles/input/hog.c'; fi` @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) profiles/input/$(DEPDIR)/bluetoothd-hog.Tpo profiles/input/$(DEPDIR)/bluetoothd-hog.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='profiles/input/hog.c' object='profiles/input/bluetoothd-hog.obj' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_bluetoothd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o profiles/input/bluetoothd-hog.obj `if test -f 'profiles/input/hog.c'; then $(CYGPATH_W) 'profiles/input/hog.c'; else $(CYGPATH_W) '$(srcdir)/profiles/input/hog.c'; fi` profiles/input/bluetoothd-hog-lib.o: profiles/input/hog-lib.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_bluetoothd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT profiles/input/bluetoothd-hog-lib.o -MD -MP -MF profiles/input/$(DEPDIR)/bluetoothd-hog-lib.Tpo -c -o profiles/input/bluetoothd-hog-lib.o `test -f 'profiles/input/hog-lib.c' || echo '$(srcdir)/'`profiles/input/hog-lib.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) profiles/input/$(DEPDIR)/bluetoothd-hog-lib.Tpo profiles/input/$(DEPDIR)/bluetoothd-hog-lib.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='profiles/input/hog-lib.c' object='profiles/input/bluetoothd-hog-lib.o' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_bluetoothd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o profiles/input/bluetoothd-hog-lib.o `test -f 'profiles/input/hog-lib.c' || echo '$(srcdir)/'`profiles/input/hog-lib.c profiles/input/bluetoothd-hog-lib.obj: profiles/input/hog-lib.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_bluetoothd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT profiles/input/bluetoothd-hog-lib.obj -MD -MP -MF profiles/input/$(DEPDIR)/bluetoothd-hog-lib.Tpo -c -o profiles/input/bluetoothd-hog-lib.obj `if test -f 'profiles/input/hog-lib.c'; then $(CYGPATH_W) 'profiles/input/hog-lib.c'; else $(CYGPATH_W) '$(srcdir)/profiles/input/hog-lib.c'; fi` @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) profiles/input/$(DEPDIR)/bluetoothd-hog-lib.Tpo profiles/input/$(DEPDIR)/bluetoothd-hog-lib.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='profiles/input/hog-lib.c' object='profiles/input/bluetoothd-hog-lib.obj' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_bluetoothd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o profiles/input/bluetoothd-hog-lib.obj `if test -f 'profiles/input/hog-lib.c'; then $(CYGPATH_W) 'profiles/input/hog-lib.c'; else $(CYGPATH_W) '$(srcdir)/profiles/input/hog-lib.c'; fi` profiles/deviceinfo/bluetoothd-dis.o: profiles/deviceinfo/dis.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_bluetoothd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT profiles/deviceinfo/bluetoothd-dis.o -MD -MP -MF profiles/deviceinfo/$(DEPDIR)/bluetoothd-dis.Tpo -c -o profiles/deviceinfo/bluetoothd-dis.o `test -f 'profiles/deviceinfo/dis.c' || echo '$(srcdir)/'`profiles/deviceinfo/dis.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) profiles/deviceinfo/$(DEPDIR)/bluetoothd-dis.Tpo profiles/deviceinfo/$(DEPDIR)/bluetoothd-dis.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='profiles/deviceinfo/dis.c' object='profiles/deviceinfo/bluetoothd-dis.o' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_bluetoothd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o profiles/deviceinfo/bluetoothd-dis.o `test -f 'profiles/deviceinfo/dis.c' || echo '$(srcdir)/'`profiles/deviceinfo/dis.c profiles/deviceinfo/bluetoothd-dis.obj: profiles/deviceinfo/dis.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_bluetoothd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT profiles/deviceinfo/bluetoothd-dis.obj -MD -MP -MF profiles/deviceinfo/$(DEPDIR)/bluetoothd-dis.Tpo -c -o profiles/deviceinfo/bluetoothd-dis.obj `if test -f 'profiles/deviceinfo/dis.c'; then $(CYGPATH_W) 'profiles/deviceinfo/dis.c'; else $(CYGPATH_W) '$(srcdir)/profiles/deviceinfo/dis.c'; fi` @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) profiles/deviceinfo/$(DEPDIR)/bluetoothd-dis.Tpo profiles/deviceinfo/$(DEPDIR)/bluetoothd-dis.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='profiles/deviceinfo/dis.c' object='profiles/deviceinfo/bluetoothd-dis.obj' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_bluetoothd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o profiles/deviceinfo/bluetoothd-dis.obj `if test -f 'profiles/deviceinfo/dis.c'; then $(CYGPATH_W) 'profiles/deviceinfo/dis.c'; else $(CYGPATH_W) '$(srcdir)/profiles/deviceinfo/dis.c'; fi` profiles/battery/bluetoothd-bas.o: profiles/battery/bas.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_bluetoothd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT profiles/battery/bluetoothd-bas.o -MD -MP -MF profiles/battery/$(DEPDIR)/bluetoothd-bas.Tpo -c -o profiles/battery/bluetoothd-bas.o `test -f 'profiles/battery/bas.c' || echo '$(srcdir)/'`profiles/battery/bas.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) profiles/battery/$(DEPDIR)/bluetoothd-bas.Tpo profiles/battery/$(DEPDIR)/bluetoothd-bas.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='profiles/battery/bas.c' object='profiles/battery/bluetoothd-bas.o' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_bluetoothd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o profiles/battery/bluetoothd-bas.o `test -f 'profiles/battery/bas.c' || echo '$(srcdir)/'`profiles/battery/bas.c profiles/battery/bluetoothd-bas.obj: profiles/battery/bas.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_bluetoothd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT profiles/battery/bluetoothd-bas.obj -MD -MP -MF profiles/battery/$(DEPDIR)/bluetoothd-bas.Tpo -c -o profiles/battery/bluetoothd-bas.obj `if test -f 'profiles/battery/bas.c'; then $(CYGPATH_W) 'profiles/battery/bas.c'; else $(CYGPATH_W) '$(srcdir)/profiles/battery/bas.c'; fi` @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) profiles/battery/$(DEPDIR)/bluetoothd-bas.Tpo profiles/battery/$(DEPDIR)/bluetoothd-bas.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='profiles/battery/bas.c' object='profiles/battery/bluetoothd-bas.obj' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_bluetoothd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o profiles/battery/bluetoothd-bas.obj `if test -f 'profiles/battery/bas.c'; then $(CYGPATH_W) 'profiles/battery/bas.c'; else $(CYGPATH_W) '$(srcdir)/profiles/battery/bas.c'; fi` profiles/scanparam/bluetoothd-scpp.o: profiles/scanparam/scpp.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_bluetoothd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT profiles/scanparam/bluetoothd-scpp.o -MD -MP -MF profiles/scanparam/$(DEPDIR)/bluetoothd-scpp.Tpo -c -o profiles/scanparam/bluetoothd-scpp.o `test -f 'profiles/scanparam/scpp.c' || echo '$(srcdir)/'`profiles/scanparam/scpp.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) profiles/scanparam/$(DEPDIR)/bluetoothd-scpp.Tpo profiles/scanparam/$(DEPDIR)/bluetoothd-scpp.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='profiles/scanparam/scpp.c' object='profiles/scanparam/bluetoothd-scpp.o' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_bluetoothd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o profiles/scanparam/bluetoothd-scpp.o `test -f 'profiles/scanparam/scpp.c' || echo '$(srcdir)/'`profiles/scanparam/scpp.c profiles/scanparam/bluetoothd-scpp.obj: profiles/scanparam/scpp.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_bluetoothd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT profiles/scanparam/bluetoothd-scpp.obj -MD -MP -MF profiles/scanparam/$(DEPDIR)/bluetoothd-scpp.Tpo -c -o profiles/scanparam/bluetoothd-scpp.obj `if test -f 'profiles/scanparam/scpp.c'; then $(CYGPATH_W) 'profiles/scanparam/scpp.c'; else $(CYGPATH_W) '$(srcdir)/profiles/scanparam/scpp.c'; fi` @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) profiles/scanparam/$(DEPDIR)/bluetoothd-scpp.Tpo profiles/scanparam/$(DEPDIR)/bluetoothd-scpp.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='profiles/scanparam/scpp.c' object='profiles/scanparam/bluetoothd-scpp.obj' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_bluetoothd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o profiles/scanparam/bluetoothd-scpp.obj `if test -f 'profiles/scanparam/scpp.c'; then $(CYGPATH_W) 'profiles/scanparam/scpp.c'; else $(CYGPATH_W) '$(srcdir)/profiles/scanparam/scpp.c'; fi` profiles/input/bluetoothd-suspend-none.o: profiles/input/suspend-none.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_bluetoothd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT profiles/input/bluetoothd-suspend-none.o -MD -MP -MF profiles/input/$(DEPDIR)/bluetoothd-suspend-none.Tpo -c -o profiles/input/bluetoothd-suspend-none.o `test -f 'profiles/input/suspend-none.c' || echo '$(srcdir)/'`profiles/input/suspend-none.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) profiles/input/$(DEPDIR)/bluetoothd-suspend-none.Tpo profiles/input/$(DEPDIR)/bluetoothd-suspend-none.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='profiles/input/suspend-none.c' object='profiles/input/bluetoothd-suspend-none.o' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_bluetoothd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o profiles/input/bluetoothd-suspend-none.o `test -f 'profiles/input/suspend-none.c' || echo '$(srcdir)/'`profiles/input/suspend-none.c profiles/input/bluetoothd-suspend-none.obj: profiles/input/suspend-none.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_bluetoothd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT profiles/input/bluetoothd-suspend-none.obj -MD -MP -MF profiles/input/$(DEPDIR)/bluetoothd-suspend-none.Tpo -c -o profiles/input/bluetoothd-suspend-none.obj `if test -f 'profiles/input/suspend-none.c'; then $(CYGPATH_W) 'profiles/input/suspend-none.c'; else $(CYGPATH_W) '$(srcdir)/profiles/input/suspend-none.c'; fi` @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) profiles/input/$(DEPDIR)/bluetoothd-suspend-none.Tpo profiles/input/$(DEPDIR)/bluetoothd-suspend-none.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='profiles/input/suspend-none.c' object='profiles/input/bluetoothd-suspend-none.obj' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_bluetoothd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o profiles/input/bluetoothd-suspend-none.obj `if test -f 'profiles/input/suspend-none.c'; then $(CYGPATH_W) 'profiles/input/suspend-none.c'; else $(CYGPATH_W) '$(srcdir)/profiles/input/suspend-none.c'; fi` profiles/health/bluetoothd-mcap.o: profiles/health/mcap.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_bluetoothd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT profiles/health/bluetoothd-mcap.o -MD -MP -MF profiles/health/$(DEPDIR)/bluetoothd-mcap.Tpo -c -o profiles/health/bluetoothd-mcap.o `test -f 'profiles/health/mcap.c' || echo '$(srcdir)/'`profiles/health/mcap.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) profiles/health/$(DEPDIR)/bluetoothd-mcap.Tpo profiles/health/$(DEPDIR)/bluetoothd-mcap.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='profiles/health/mcap.c' object='profiles/health/bluetoothd-mcap.o' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_bluetoothd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o profiles/health/bluetoothd-mcap.o `test -f 'profiles/health/mcap.c' || echo '$(srcdir)/'`profiles/health/mcap.c profiles/health/bluetoothd-mcap.obj: profiles/health/mcap.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_bluetoothd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT profiles/health/bluetoothd-mcap.obj -MD -MP -MF profiles/health/$(DEPDIR)/bluetoothd-mcap.Tpo -c -o profiles/health/bluetoothd-mcap.obj `if test -f 'profiles/health/mcap.c'; then $(CYGPATH_W) 'profiles/health/mcap.c'; else $(CYGPATH_W) '$(srcdir)/profiles/health/mcap.c'; fi` @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) profiles/health/$(DEPDIR)/bluetoothd-mcap.Tpo profiles/health/$(DEPDIR)/bluetoothd-mcap.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='profiles/health/mcap.c' object='profiles/health/bluetoothd-mcap.obj' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_bluetoothd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o profiles/health/bluetoothd-mcap.obj `if test -f 'profiles/health/mcap.c'; then $(CYGPATH_W) 'profiles/health/mcap.c'; else $(CYGPATH_W) '$(srcdir)/profiles/health/mcap.c'; fi` profiles/health/bluetoothd-hdp_main.o: profiles/health/hdp_main.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_bluetoothd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT profiles/health/bluetoothd-hdp_main.o -MD -MP -MF profiles/health/$(DEPDIR)/bluetoothd-hdp_main.Tpo -c -o profiles/health/bluetoothd-hdp_main.o `test -f 'profiles/health/hdp_main.c' || echo '$(srcdir)/'`profiles/health/hdp_main.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) profiles/health/$(DEPDIR)/bluetoothd-hdp_main.Tpo profiles/health/$(DEPDIR)/bluetoothd-hdp_main.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='profiles/health/hdp_main.c' object='profiles/health/bluetoothd-hdp_main.o' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_bluetoothd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o profiles/health/bluetoothd-hdp_main.o `test -f 'profiles/health/hdp_main.c' || echo '$(srcdir)/'`profiles/health/hdp_main.c profiles/health/bluetoothd-hdp_main.obj: profiles/health/hdp_main.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_bluetoothd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT profiles/health/bluetoothd-hdp_main.obj -MD -MP -MF profiles/health/$(DEPDIR)/bluetoothd-hdp_main.Tpo -c -o profiles/health/bluetoothd-hdp_main.obj `if test -f 'profiles/health/hdp_main.c'; then $(CYGPATH_W) 'profiles/health/hdp_main.c'; else $(CYGPATH_W) '$(srcdir)/profiles/health/hdp_main.c'; fi` @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) profiles/health/$(DEPDIR)/bluetoothd-hdp_main.Tpo profiles/health/$(DEPDIR)/bluetoothd-hdp_main.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='profiles/health/hdp_main.c' object='profiles/health/bluetoothd-hdp_main.obj' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_bluetoothd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o profiles/health/bluetoothd-hdp_main.obj `if test -f 'profiles/health/hdp_main.c'; then $(CYGPATH_W) 'profiles/health/hdp_main.c'; else $(CYGPATH_W) '$(srcdir)/profiles/health/hdp_main.c'; fi` profiles/health/bluetoothd-hdp_manager.o: profiles/health/hdp_manager.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_bluetoothd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT profiles/health/bluetoothd-hdp_manager.o -MD -MP -MF profiles/health/$(DEPDIR)/bluetoothd-hdp_manager.Tpo -c -o profiles/health/bluetoothd-hdp_manager.o `test -f 'profiles/health/hdp_manager.c' || echo '$(srcdir)/'`profiles/health/hdp_manager.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) profiles/health/$(DEPDIR)/bluetoothd-hdp_manager.Tpo profiles/health/$(DEPDIR)/bluetoothd-hdp_manager.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='profiles/health/hdp_manager.c' object='profiles/health/bluetoothd-hdp_manager.o' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_bluetoothd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o profiles/health/bluetoothd-hdp_manager.o `test -f 'profiles/health/hdp_manager.c' || echo '$(srcdir)/'`profiles/health/hdp_manager.c profiles/health/bluetoothd-hdp_manager.obj: profiles/health/hdp_manager.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_bluetoothd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT profiles/health/bluetoothd-hdp_manager.obj -MD -MP -MF profiles/health/$(DEPDIR)/bluetoothd-hdp_manager.Tpo -c -o profiles/health/bluetoothd-hdp_manager.obj `if test -f 'profiles/health/hdp_manager.c'; then $(CYGPATH_W) 'profiles/health/hdp_manager.c'; else $(CYGPATH_W) '$(srcdir)/profiles/health/hdp_manager.c'; fi` @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) profiles/health/$(DEPDIR)/bluetoothd-hdp_manager.Tpo profiles/health/$(DEPDIR)/bluetoothd-hdp_manager.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='profiles/health/hdp_manager.c' object='profiles/health/bluetoothd-hdp_manager.obj' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_bluetoothd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o profiles/health/bluetoothd-hdp_manager.obj `if test -f 'profiles/health/hdp_manager.c'; then $(CYGPATH_W) 'profiles/health/hdp_manager.c'; else $(CYGPATH_W) '$(srcdir)/profiles/health/hdp_manager.c'; fi` profiles/health/bluetoothd-hdp.o: profiles/health/hdp.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_bluetoothd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT profiles/health/bluetoothd-hdp.o -MD -MP -MF profiles/health/$(DEPDIR)/bluetoothd-hdp.Tpo -c -o profiles/health/bluetoothd-hdp.o `test -f 'profiles/health/hdp.c' || echo '$(srcdir)/'`profiles/health/hdp.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) profiles/health/$(DEPDIR)/bluetoothd-hdp.Tpo profiles/health/$(DEPDIR)/bluetoothd-hdp.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='profiles/health/hdp.c' object='profiles/health/bluetoothd-hdp.o' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_bluetoothd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o profiles/health/bluetoothd-hdp.o `test -f 'profiles/health/hdp.c' || echo '$(srcdir)/'`profiles/health/hdp.c profiles/health/bluetoothd-hdp.obj: profiles/health/hdp.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_bluetoothd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT profiles/health/bluetoothd-hdp.obj -MD -MP -MF profiles/health/$(DEPDIR)/bluetoothd-hdp.Tpo -c -o profiles/health/bluetoothd-hdp.obj `if test -f 'profiles/health/hdp.c'; then $(CYGPATH_W) 'profiles/health/hdp.c'; else $(CYGPATH_W) '$(srcdir)/profiles/health/hdp.c'; fi` @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) profiles/health/$(DEPDIR)/bluetoothd-hdp.Tpo profiles/health/$(DEPDIR)/bluetoothd-hdp.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='profiles/health/hdp.c' object='profiles/health/bluetoothd-hdp.obj' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_bluetoothd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o profiles/health/bluetoothd-hdp.obj `if test -f 'profiles/health/hdp.c'; then $(CYGPATH_W) 'profiles/health/hdp.c'; else $(CYGPATH_W) '$(srcdir)/profiles/health/hdp.c'; fi` profiles/health/bluetoothd-hdp_util.o: profiles/health/hdp_util.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_bluetoothd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT profiles/health/bluetoothd-hdp_util.o -MD -MP -MF profiles/health/$(DEPDIR)/bluetoothd-hdp_util.Tpo -c -o profiles/health/bluetoothd-hdp_util.o `test -f 'profiles/health/hdp_util.c' || echo '$(srcdir)/'`profiles/health/hdp_util.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) profiles/health/$(DEPDIR)/bluetoothd-hdp_util.Tpo profiles/health/$(DEPDIR)/bluetoothd-hdp_util.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='profiles/health/hdp_util.c' object='profiles/health/bluetoothd-hdp_util.o' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_bluetoothd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o profiles/health/bluetoothd-hdp_util.o `test -f 'profiles/health/hdp_util.c' || echo '$(srcdir)/'`profiles/health/hdp_util.c profiles/health/bluetoothd-hdp_util.obj: profiles/health/hdp_util.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_bluetoothd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT profiles/health/bluetoothd-hdp_util.obj -MD -MP -MF profiles/health/$(DEPDIR)/bluetoothd-hdp_util.Tpo -c -o profiles/health/bluetoothd-hdp_util.obj `if test -f 'profiles/health/hdp_util.c'; then $(CYGPATH_W) 'profiles/health/hdp_util.c'; else $(CYGPATH_W) '$(srcdir)/profiles/health/hdp_util.c'; fi` @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) profiles/health/$(DEPDIR)/bluetoothd-hdp_util.Tpo profiles/health/$(DEPDIR)/bluetoothd-hdp_util.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='profiles/health/hdp_util.c' object='profiles/health/bluetoothd-hdp_util.obj' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_bluetoothd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o profiles/health/bluetoothd-hdp_util.obj `if test -f 'profiles/health/hdp_util.c'; then $(CYGPATH_W) 'profiles/health/hdp_util.c'; else $(CYGPATH_W) '$(srcdir)/profiles/health/hdp_util.c'; fi` profiles/gap/bluetoothd-gas.o: profiles/gap/gas.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_bluetoothd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT profiles/gap/bluetoothd-gas.o -MD -MP -MF profiles/gap/$(DEPDIR)/bluetoothd-gas.Tpo -c -o profiles/gap/bluetoothd-gas.o `test -f 'profiles/gap/gas.c' || echo '$(srcdir)/'`profiles/gap/gas.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) profiles/gap/$(DEPDIR)/bluetoothd-gas.Tpo profiles/gap/$(DEPDIR)/bluetoothd-gas.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='profiles/gap/gas.c' object='profiles/gap/bluetoothd-gas.o' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_bluetoothd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o profiles/gap/bluetoothd-gas.o `test -f 'profiles/gap/gas.c' || echo '$(srcdir)/'`profiles/gap/gas.c profiles/gap/bluetoothd-gas.obj: profiles/gap/gas.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_bluetoothd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT profiles/gap/bluetoothd-gas.obj -MD -MP -MF profiles/gap/$(DEPDIR)/bluetoothd-gas.Tpo -c -o profiles/gap/bluetoothd-gas.obj `if test -f 'profiles/gap/gas.c'; then $(CYGPATH_W) 'profiles/gap/gas.c'; else $(CYGPATH_W) '$(srcdir)/profiles/gap/gas.c'; fi` @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) profiles/gap/$(DEPDIR)/bluetoothd-gas.Tpo profiles/gap/$(DEPDIR)/bluetoothd-gas.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='profiles/gap/gas.c' object='profiles/gap/bluetoothd-gas.obj' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_bluetoothd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o profiles/gap/bluetoothd-gas.obj `if test -f 'profiles/gap/gas.c'; then $(CYGPATH_W) 'profiles/gap/gas.c'; else $(CYGPATH_W) '$(srcdir)/profiles/gap/gas.c'; fi` profiles/scanparam/bluetoothd-scan.o: profiles/scanparam/scan.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_bluetoothd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT profiles/scanparam/bluetoothd-scan.o -MD -MP -MF profiles/scanparam/$(DEPDIR)/bluetoothd-scan.Tpo -c -o profiles/scanparam/bluetoothd-scan.o `test -f 'profiles/scanparam/scan.c' || echo '$(srcdir)/'`profiles/scanparam/scan.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) profiles/scanparam/$(DEPDIR)/bluetoothd-scan.Tpo profiles/scanparam/$(DEPDIR)/bluetoothd-scan.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='profiles/scanparam/scan.c' object='profiles/scanparam/bluetoothd-scan.o' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_bluetoothd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o profiles/scanparam/bluetoothd-scan.o `test -f 'profiles/scanparam/scan.c' || echo '$(srcdir)/'`profiles/scanparam/scan.c profiles/scanparam/bluetoothd-scan.obj: profiles/scanparam/scan.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_bluetoothd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT profiles/scanparam/bluetoothd-scan.obj -MD -MP -MF profiles/scanparam/$(DEPDIR)/bluetoothd-scan.Tpo -c -o profiles/scanparam/bluetoothd-scan.obj `if test -f 'profiles/scanparam/scan.c'; then $(CYGPATH_W) 'profiles/scanparam/scan.c'; else $(CYGPATH_W) '$(srcdir)/profiles/scanparam/scan.c'; fi` @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) profiles/scanparam/$(DEPDIR)/bluetoothd-scan.Tpo profiles/scanparam/$(DEPDIR)/bluetoothd-scan.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='profiles/scanparam/scan.c' object='profiles/scanparam/bluetoothd-scan.obj' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_bluetoothd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o profiles/scanparam/bluetoothd-scan.obj `if test -f 'profiles/scanparam/scan.c'; then $(CYGPATH_W) 'profiles/scanparam/scan.c'; else $(CYGPATH_W) '$(srcdir)/profiles/scanparam/scan.c'; fi` profiles/deviceinfo/bluetoothd-deviceinfo.o: profiles/deviceinfo/deviceinfo.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_bluetoothd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT profiles/deviceinfo/bluetoothd-deviceinfo.o -MD -MP -MF profiles/deviceinfo/$(DEPDIR)/bluetoothd-deviceinfo.Tpo -c -o profiles/deviceinfo/bluetoothd-deviceinfo.o `test -f 'profiles/deviceinfo/deviceinfo.c' || echo '$(srcdir)/'`profiles/deviceinfo/deviceinfo.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) profiles/deviceinfo/$(DEPDIR)/bluetoothd-deviceinfo.Tpo profiles/deviceinfo/$(DEPDIR)/bluetoothd-deviceinfo.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='profiles/deviceinfo/deviceinfo.c' object='profiles/deviceinfo/bluetoothd-deviceinfo.o' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_bluetoothd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o profiles/deviceinfo/bluetoothd-deviceinfo.o `test -f 'profiles/deviceinfo/deviceinfo.c' || echo '$(srcdir)/'`profiles/deviceinfo/deviceinfo.c profiles/deviceinfo/bluetoothd-deviceinfo.obj: profiles/deviceinfo/deviceinfo.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_bluetoothd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT profiles/deviceinfo/bluetoothd-deviceinfo.obj -MD -MP -MF profiles/deviceinfo/$(DEPDIR)/bluetoothd-deviceinfo.Tpo -c -o profiles/deviceinfo/bluetoothd-deviceinfo.obj `if test -f 'profiles/deviceinfo/deviceinfo.c'; then $(CYGPATH_W) 'profiles/deviceinfo/deviceinfo.c'; else $(CYGPATH_W) '$(srcdir)/profiles/deviceinfo/deviceinfo.c'; fi` @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) profiles/deviceinfo/$(DEPDIR)/bluetoothd-deviceinfo.Tpo profiles/deviceinfo/$(DEPDIR)/bluetoothd-deviceinfo.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='profiles/deviceinfo/deviceinfo.c' object='profiles/deviceinfo/bluetoothd-deviceinfo.obj' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_bluetoothd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o profiles/deviceinfo/bluetoothd-deviceinfo.obj `if test -f 'profiles/deviceinfo/deviceinfo.c'; then $(CYGPATH_W) 'profiles/deviceinfo/deviceinfo.c'; else $(CYGPATH_W) '$(srcdir)/profiles/deviceinfo/deviceinfo.c'; fi` profiles/midi/bluetoothd-midi.o: profiles/midi/midi.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_bluetoothd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT profiles/midi/bluetoothd-midi.o -MD -MP -MF profiles/midi/$(DEPDIR)/bluetoothd-midi.Tpo -c -o profiles/midi/bluetoothd-midi.o `test -f 'profiles/midi/midi.c' || echo '$(srcdir)/'`profiles/midi/midi.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) profiles/midi/$(DEPDIR)/bluetoothd-midi.Tpo profiles/midi/$(DEPDIR)/bluetoothd-midi.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='profiles/midi/midi.c' object='profiles/midi/bluetoothd-midi.o' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_bluetoothd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o profiles/midi/bluetoothd-midi.o `test -f 'profiles/midi/midi.c' || echo '$(srcdir)/'`profiles/midi/midi.c profiles/midi/bluetoothd-midi.obj: profiles/midi/midi.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_bluetoothd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT profiles/midi/bluetoothd-midi.obj -MD -MP -MF profiles/midi/$(DEPDIR)/bluetoothd-midi.Tpo -c -o profiles/midi/bluetoothd-midi.obj `if test -f 'profiles/midi/midi.c'; then $(CYGPATH_W) 'profiles/midi/midi.c'; else $(CYGPATH_W) '$(srcdir)/profiles/midi/midi.c'; fi` @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) profiles/midi/$(DEPDIR)/bluetoothd-midi.Tpo profiles/midi/$(DEPDIR)/bluetoothd-midi.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='profiles/midi/midi.c' object='profiles/midi/bluetoothd-midi.obj' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_bluetoothd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o profiles/midi/bluetoothd-midi.obj `if test -f 'profiles/midi/midi.c'; then $(CYGPATH_W) 'profiles/midi/midi.c'; else $(CYGPATH_W) '$(srcdir)/profiles/midi/midi.c'; fi` profiles/midi/bluetoothd-libmidi.o: profiles/midi/libmidi.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_bluetoothd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT profiles/midi/bluetoothd-libmidi.o -MD -MP -MF profiles/midi/$(DEPDIR)/bluetoothd-libmidi.Tpo -c -o profiles/midi/bluetoothd-libmidi.o `test -f 'profiles/midi/libmidi.c' || echo '$(srcdir)/'`profiles/midi/libmidi.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) profiles/midi/$(DEPDIR)/bluetoothd-libmidi.Tpo profiles/midi/$(DEPDIR)/bluetoothd-libmidi.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='profiles/midi/libmidi.c' object='profiles/midi/bluetoothd-libmidi.o' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_bluetoothd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o profiles/midi/bluetoothd-libmidi.o `test -f 'profiles/midi/libmidi.c' || echo '$(srcdir)/'`profiles/midi/libmidi.c profiles/midi/bluetoothd-libmidi.obj: profiles/midi/libmidi.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_bluetoothd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT profiles/midi/bluetoothd-libmidi.obj -MD -MP -MF profiles/midi/$(DEPDIR)/bluetoothd-libmidi.Tpo -c -o profiles/midi/bluetoothd-libmidi.obj `if test -f 'profiles/midi/libmidi.c'; then $(CYGPATH_W) 'profiles/midi/libmidi.c'; else $(CYGPATH_W) '$(srcdir)/profiles/midi/libmidi.c'; fi` @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) profiles/midi/$(DEPDIR)/bluetoothd-libmidi.Tpo profiles/midi/$(DEPDIR)/bluetoothd-libmidi.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='profiles/midi/libmidi.c' object='profiles/midi/bluetoothd-libmidi.obj' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_bluetoothd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o profiles/midi/bluetoothd-libmidi.obj `if test -f 'profiles/midi/libmidi.c'; then $(CYGPATH_W) 'profiles/midi/libmidi.c'; else $(CYGPATH_W) '$(srcdir)/profiles/midi/libmidi.c'; fi` profiles/battery/bluetoothd-battery.o: profiles/battery/battery.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_bluetoothd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT profiles/battery/bluetoothd-battery.o -MD -MP -MF profiles/battery/$(DEPDIR)/bluetoothd-battery.Tpo -c -o profiles/battery/bluetoothd-battery.o `test -f 'profiles/battery/battery.c' || echo '$(srcdir)/'`profiles/battery/battery.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) profiles/battery/$(DEPDIR)/bluetoothd-battery.Tpo profiles/battery/$(DEPDIR)/bluetoothd-battery.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='profiles/battery/battery.c' object='profiles/battery/bluetoothd-battery.o' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_bluetoothd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o profiles/battery/bluetoothd-battery.o `test -f 'profiles/battery/battery.c' || echo '$(srcdir)/'`profiles/battery/battery.c profiles/battery/bluetoothd-battery.obj: profiles/battery/battery.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_bluetoothd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT profiles/battery/bluetoothd-battery.obj -MD -MP -MF profiles/battery/$(DEPDIR)/bluetoothd-battery.Tpo -c -o profiles/battery/bluetoothd-battery.obj `if test -f 'profiles/battery/battery.c'; then $(CYGPATH_W) 'profiles/battery/battery.c'; else $(CYGPATH_W) '$(srcdir)/profiles/battery/battery.c'; fi` @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) profiles/battery/$(DEPDIR)/bluetoothd-battery.Tpo profiles/battery/$(DEPDIR)/bluetoothd-battery.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='profiles/battery/battery.c' object='profiles/battery/bluetoothd-battery.obj' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_bluetoothd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o profiles/battery/bluetoothd-battery.obj `if test -f 'profiles/battery/battery.c'; then $(CYGPATH_W) 'profiles/battery/battery.c'; else $(CYGPATH_W) '$(srcdir)/profiles/battery/battery.c'; fi` plugins/bluetoothd-sixaxis.o: plugins/sixaxis.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_bluetoothd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT plugins/bluetoothd-sixaxis.o -MD -MP -MF plugins/$(DEPDIR)/bluetoothd-sixaxis.Tpo -c -o plugins/bluetoothd-sixaxis.o `test -f 'plugins/sixaxis.c' || echo '$(srcdir)/'`plugins/sixaxis.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) plugins/$(DEPDIR)/bluetoothd-sixaxis.Tpo plugins/$(DEPDIR)/bluetoothd-sixaxis.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='plugins/sixaxis.c' object='plugins/bluetoothd-sixaxis.o' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_bluetoothd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o plugins/bluetoothd-sixaxis.o `test -f 'plugins/sixaxis.c' || echo '$(srcdir)/'`plugins/sixaxis.c plugins/bluetoothd-sixaxis.obj: plugins/sixaxis.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_bluetoothd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT plugins/bluetoothd-sixaxis.obj -MD -MP -MF plugins/$(DEPDIR)/bluetoothd-sixaxis.Tpo -c -o plugins/bluetoothd-sixaxis.obj `if test -f 'plugins/sixaxis.c'; then $(CYGPATH_W) 'plugins/sixaxis.c'; else $(CYGPATH_W) '$(srcdir)/plugins/sixaxis.c'; fi` @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) plugins/$(DEPDIR)/bluetoothd-sixaxis.Tpo plugins/$(DEPDIR)/bluetoothd-sixaxis.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='plugins/sixaxis.c' object='plugins/bluetoothd-sixaxis.obj' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_bluetoothd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o plugins/bluetoothd-sixaxis.obj `if test -f 'plugins/sixaxis.c'; then $(CYGPATH_W) 'plugins/sixaxis.c'; else $(CYGPATH_W) '$(srcdir)/plugins/sixaxis.c'; fi` profiles/audio/bluetoothd-bap.o: profiles/audio/bap.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_bluetoothd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT profiles/audio/bluetoothd-bap.o -MD -MP -MF profiles/audio/$(DEPDIR)/bluetoothd-bap.Tpo -c -o profiles/audio/bluetoothd-bap.o `test -f 'profiles/audio/bap.c' || echo '$(srcdir)/'`profiles/audio/bap.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) profiles/audio/$(DEPDIR)/bluetoothd-bap.Tpo profiles/audio/$(DEPDIR)/bluetoothd-bap.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='profiles/audio/bap.c' object='profiles/audio/bluetoothd-bap.o' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_bluetoothd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o profiles/audio/bluetoothd-bap.o `test -f 'profiles/audio/bap.c' || echo '$(srcdir)/'`profiles/audio/bap.c profiles/audio/bluetoothd-bap.obj: profiles/audio/bap.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_bluetoothd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT profiles/audio/bluetoothd-bap.obj -MD -MP -MF profiles/audio/$(DEPDIR)/bluetoothd-bap.Tpo -c -o profiles/audio/bluetoothd-bap.obj `if test -f 'profiles/audio/bap.c'; then $(CYGPATH_W) 'profiles/audio/bap.c'; else $(CYGPATH_W) '$(srcdir)/profiles/audio/bap.c'; fi` @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) profiles/audio/$(DEPDIR)/bluetoothd-bap.Tpo profiles/audio/$(DEPDIR)/bluetoothd-bap.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='profiles/audio/bap.c' object='profiles/audio/bluetoothd-bap.obj' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_bluetoothd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o profiles/audio/bluetoothd-bap.obj `if test -f 'profiles/audio/bap.c'; then $(CYGPATH_W) 'profiles/audio/bap.c'; else $(CYGPATH_W) '$(srcdir)/profiles/audio/bap.c'; fi` profiles/audio/bluetoothd-bass.o: profiles/audio/bass.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_bluetoothd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT profiles/audio/bluetoothd-bass.o -MD -MP -MF profiles/audio/$(DEPDIR)/bluetoothd-bass.Tpo -c -o profiles/audio/bluetoothd-bass.o `test -f 'profiles/audio/bass.c' || echo '$(srcdir)/'`profiles/audio/bass.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) profiles/audio/$(DEPDIR)/bluetoothd-bass.Tpo profiles/audio/$(DEPDIR)/bluetoothd-bass.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='profiles/audio/bass.c' object='profiles/audio/bluetoothd-bass.o' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_bluetoothd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o profiles/audio/bluetoothd-bass.o `test -f 'profiles/audio/bass.c' || echo '$(srcdir)/'`profiles/audio/bass.c profiles/audio/bluetoothd-bass.obj: profiles/audio/bass.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_bluetoothd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT profiles/audio/bluetoothd-bass.obj -MD -MP -MF profiles/audio/$(DEPDIR)/bluetoothd-bass.Tpo -c -o profiles/audio/bluetoothd-bass.obj `if test -f 'profiles/audio/bass.c'; then $(CYGPATH_W) 'profiles/audio/bass.c'; else $(CYGPATH_W) '$(srcdir)/profiles/audio/bass.c'; fi` @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) profiles/audio/$(DEPDIR)/bluetoothd-bass.Tpo profiles/audio/$(DEPDIR)/bluetoothd-bass.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='profiles/audio/bass.c' object='profiles/audio/bluetoothd-bass.obj' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_bluetoothd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o profiles/audio/bluetoothd-bass.obj `if test -f 'profiles/audio/bass.c'; then $(CYGPATH_W) 'profiles/audio/bass.c'; else $(CYGPATH_W) '$(srcdir)/profiles/audio/bass.c'; fi` profiles/audio/bluetoothd-mcp.o: profiles/audio/mcp.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_bluetoothd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT profiles/audio/bluetoothd-mcp.o -MD -MP -MF profiles/audio/$(DEPDIR)/bluetoothd-mcp.Tpo -c -o profiles/audio/bluetoothd-mcp.o `test -f 'profiles/audio/mcp.c' || echo '$(srcdir)/'`profiles/audio/mcp.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) profiles/audio/$(DEPDIR)/bluetoothd-mcp.Tpo profiles/audio/$(DEPDIR)/bluetoothd-mcp.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='profiles/audio/mcp.c' object='profiles/audio/bluetoothd-mcp.o' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_bluetoothd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o profiles/audio/bluetoothd-mcp.o `test -f 'profiles/audio/mcp.c' || echo '$(srcdir)/'`profiles/audio/mcp.c profiles/audio/bluetoothd-mcp.obj: profiles/audio/mcp.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_bluetoothd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT profiles/audio/bluetoothd-mcp.obj -MD -MP -MF profiles/audio/$(DEPDIR)/bluetoothd-mcp.Tpo -c -o profiles/audio/bluetoothd-mcp.obj `if test -f 'profiles/audio/mcp.c'; then $(CYGPATH_W) 'profiles/audio/mcp.c'; else $(CYGPATH_W) '$(srcdir)/profiles/audio/mcp.c'; fi` @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) profiles/audio/$(DEPDIR)/bluetoothd-mcp.Tpo profiles/audio/$(DEPDIR)/bluetoothd-mcp.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='profiles/audio/mcp.c' object='profiles/audio/bluetoothd-mcp.obj' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_bluetoothd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o profiles/audio/bluetoothd-mcp.obj `if test -f 'profiles/audio/mcp.c'; then $(CYGPATH_W) 'profiles/audio/mcp.c'; else $(CYGPATH_W) '$(srcdir)/profiles/audio/mcp.c'; fi` profiles/audio/bluetoothd-vcp.o: profiles/audio/vcp.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_bluetoothd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT profiles/audio/bluetoothd-vcp.o -MD -MP -MF profiles/audio/$(DEPDIR)/bluetoothd-vcp.Tpo -c -o profiles/audio/bluetoothd-vcp.o `test -f 'profiles/audio/vcp.c' || echo '$(srcdir)/'`profiles/audio/vcp.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) profiles/audio/$(DEPDIR)/bluetoothd-vcp.Tpo profiles/audio/$(DEPDIR)/bluetoothd-vcp.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='profiles/audio/vcp.c' object='profiles/audio/bluetoothd-vcp.o' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_bluetoothd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o profiles/audio/bluetoothd-vcp.o `test -f 'profiles/audio/vcp.c' || echo '$(srcdir)/'`profiles/audio/vcp.c profiles/audio/bluetoothd-vcp.obj: profiles/audio/vcp.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_bluetoothd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT profiles/audio/bluetoothd-vcp.obj -MD -MP -MF profiles/audio/$(DEPDIR)/bluetoothd-vcp.Tpo -c -o profiles/audio/bluetoothd-vcp.obj `if test -f 'profiles/audio/vcp.c'; then $(CYGPATH_W) 'profiles/audio/vcp.c'; else $(CYGPATH_W) '$(srcdir)/profiles/audio/vcp.c'; fi` @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) profiles/audio/$(DEPDIR)/bluetoothd-vcp.Tpo profiles/audio/$(DEPDIR)/bluetoothd-vcp.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='profiles/audio/vcp.c' object='profiles/audio/bluetoothd-vcp.obj' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_bluetoothd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o profiles/audio/bluetoothd-vcp.obj `if test -f 'profiles/audio/vcp.c'; then $(CYGPATH_W) 'profiles/audio/vcp.c'; else $(CYGPATH_W) '$(srcdir)/profiles/audio/vcp.c'; fi` profiles/audio/bluetoothd-micp.o: profiles/audio/micp.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_bluetoothd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT profiles/audio/bluetoothd-micp.o -MD -MP -MF profiles/audio/$(DEPDIR)/bluetoothd-micp.Tpo -c -o profiles/audio/bluetoothd-micp.o `test -f 'profiles/audio/micp.c' || echo '$(srcdir)/'`profiles/audio/micp.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) profiles/audio/$(DEPDIR)/bluetoothd-micp.Tpo profiles/audio/$(DEPDIR)/bluetoothd-micp.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='profiles/audio/micp.c' object='profiles/audio/bluetoothd-micp.o' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_bluetoothd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o profiles/audio/bluetoothd-micp.o `test -f 'profiles/audio/micp.c' || echo '$(srcdir)/'`profiles/audio/micp.c profiles/audio/bluetoothd-micp.obj: profiles/audio/micp.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_bluetoothd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT profiles/audio/bluetoothd-micp.obj -MD -MP -MF profiles/audio/$(DEPDIR)/bluetoothd-micp.Tpo -c -o profiles/audio/bluetoothd-micp.obj `if test -f 'profiles/audio/micp.c'; then $(CYGPATH_W) 'profiles/audio/micp.c'; else $(CYGPATH_W) '$(srcdir)/profiles/audio/micp.c'; fi` @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) profiles/audio/$(DEPDIR)/bluetoothd-micp.Tpo profiles/audio/$(DEPDIR)/bluetoothd-micp.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='profiles/audio/micp.c' object='profiles/audio/bluetoothd-micp.obj' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_bluetoothd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o profiles/audio/bluetoothd-micp.obj `if test -f 'profiles/audio/micp.c'; then $(CYGPATH_W) 'profiles/audio/micp.c'; else $(CYGPATH_W) '$(srcdir)/profiles/audio/micp.c'; fi` profiles/audio/bluetoothd-ccp.o: profiles/audio/ccp.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_bluetoothd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT profiles/audio/bluetoothd-ccp.o -MD -MP -MF profiles/audio/$(DEPDIR)/bluetoothd-ccp.Tpo -c -o profiles/audio/bluetoothd-ccp.o `test -f 'profiles/audio/ccp.c' || echo '$(srcdir)/'`profiles/audio/ccp.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) profiles/audio/$(DEPDIR)/bluetoothd-ccp.Tpo profiles/audio/$(DEPDIR)/bluetoothd-ccp.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='profiles/audio/ccp.c' object='profiles/audio/bluetoothd-ccp.o' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_bluetoothd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o profiles/audio/bluetoothd-ccp.o `test -f 'profiles/audio/ccp.c' || echo '$(srcdir)/'`profiles/audio/ccp.c profiles/audio/bluetoothd-ccp.obj: profiles/audio/ccp.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_bluetoothd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT profiles/audio/bluetoothd-ccp.obj -MD -MP -MF profiles/audio/$(DEPDIR)/bluetoothd-ccp.Tpo -c -o profiles/audio/bluetoothd-ccp.obj `if test -f 'profiles/audio/ccp.c'; then $(CYGPATH_W) 'profiles/audio/ccp.c'; else $(CYGPATH_W) '$(srcdir)/profiles/audio/ccp.c'; fi` @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) profiles/audio/$(DEPDIR)/bluetoothd-ccp.Tpo profiles/audio/$(DEPDIR)/bluetoothd-ccp.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='profiles/audio/ccp.c' object='profiles/audio/bluetoothd-ccp.obj' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_bluetoothd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o profiles/audio/bluetoothd-ccp.obj `if test -f 'profiles/audio/ccp.c'; then $(CYGPATH_W) 'profiles/audio/ccp.c'; else $(CYGPATH_W) '$(srcdir)/profiles/audio/ccp.c'; fi` profiles/audio/bluetoothd-csip.o: profiles/audio/csip.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_bluetoothd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT profiles/audio/bluetoothd-csip.o -MD -MP -MF profiles/audio/$(DEPDIR)/bluetoothd-csip.Tpo -c -o profiles/audio/bluetoothd-csip.o `test -f 'profiles/audio/csip.c' || echo '$(srcdir)/'`profiles/audio/csip.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) profiles/audio/$(DEPDIR)/bluetoothd-csip.Tpo profiles/audio/$(DEPDIR)/bluetoothd-csip.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='profiles/audio/csip.c' object='profiles/audio/bluetoothd-csip.o' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_bluetoothd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o profiles/audio/bluetoothd-csip.o `test -f 'profiles/audio/csip.c' || echo '$(srcdir)/'`profiles/audio/csip.c profiles/audio/bluetoothd-csip.obj: profiles/audio/csip.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_bluetoothd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT profiles/audio/bluetoothd-csip.obj -MD -MP -MF profiles/audio/$(DEPDIR)/bluetoothd-csip.Tpo -c -o profiles/audio/bluetoothd-csip.obj `if test -f 'profiles/audio/csip.c'; then $(CYGPATH_W) 'profiles/audio/csip.c'; else $(CYGPATH_W) '$(srcdir)/profiles/audio/csip.c'; fi` @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) profiles/audio/$(DEPDIR)/bluetoothd-csip.Tpo profiles/audio/$(DEPDIR)/bluetoothd-csip.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='profiles/audio/csip.c' object='profiles/audio/bluetoothd-csip.obj' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_bluetoothd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o profiles/audio/bluetoothd-csip.obj `if test -f 'profiles/audio/csip.c'; then $(CYGPATH_W) 'profiles/audio/csip.c'; else $(CYGPATH_W) '$(srcdir)/profiles/audio/csip.c'; fi` profiles/audio/bluetoothd-asha.o: profiles/audio/asha.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_bluetoothd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT profiles/audio/bluetoothd-asha.o -MD -MP -MF profiles/audio/$(DEPDIR)/bluetoothd-asha.Tpo -c -o profiles/audio/bluetoothd-asha.o `test -f 'profiles/audio/asha.c' || echo '$(srcdir)/'`profiles/audio/asha.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) profiles/audio/$(DEPDIR)/bluetoothd-asha.Tpo profiles/audio/$(DEPDIR)/bluetoothd-asha.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='profiles/audio/asha.c' object='profiles/audio/bluetoothd-asha.o' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_bluetoothd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o profiles/audio/bluetoothd-asha.o `test -f 'profiles/audio/asha.c' || echo '$(srcdir)/'`profiles/audio/asha.c profiles/audio/bluetoothd-asha.obj: profiles/audio/asha.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_bluetoothd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT profiles/audio/bluetoothd-asha.obj -MD -MP -MF profiles/audio/$(DEPDIR)/bluetoothd-asha.Tpo -c -o profiles/audio/bluetoothd-asha.obj `if test -f 'profiles/audio/asha.c'; then $(CYGPATH_W) 'profiles/audio/asha.c'; else $(CYGPATH_W) '$(srcdir)/profiles/audio/asha.c'; fi` @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) profiles/audio/$(DEPDIR)/bluetoothd-asha.Tpo profiles/audio/$(DEPDIR)/bluetoothd-asha.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='profiles/audio/asha.c' object='profiles/audio/bluetoothd-asha.obj' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_bluetoothd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o profiles/audio/bluetoothd-asha.obj `if test -f 'profiles/audio/asha.c'; then $(CYGPATH_W) 'profiles/audio/asha.c'; else $(CYGPATH_W) '$(srcdir)/profiles/audio/asha.c'; fi` attrib/bluetoothd-att.o: attrib/att.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_bluetoothd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT attrib/bluetoothd-att.o -MD -MP -MF attrib/$(DEPDIR)/bluetoothd-att.Tpo -c -o attrib/bluetoothd-att.o `test -f 'attrib/att.c' || echo '$(srcdir)/'`attrib/att.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) attrib/$(DEPDIR)/bluetoothd-att.Tpo attrib/$(DEPDIR)/bluetoothd-att.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='attrib/att.c' object='attrib/bluetoothd-att.o' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_bluetoothd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o attrib/bluetoothd-att.o `test -f 'attrib/att.c' || echo '$(srcdir)/'`attrib/att.c attrib/bluetoothd-att.obj: attrib/att.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_bluetoothd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT attrib/bluetoothd-att.obj -MD -MP -MF attrib/$(DEPDIR)/bluetoothd-att.Tpo -c -o attrib/bluetoothd-att.obj `if test -f 'attrib/att.c'; then $(CYGPATH_W) 'attrib/att.c'; else $(CYGPATH_W) '$(srcdir)/attrib/att.c'; fi` @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) attrib/$(DEPDIR)/bluetoothd-att.Tpo attrib/$(DEPDIR)/bluetoothd-att.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='attrib/att.c' object='attrib/bluetoothd-att.obj' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_bluetoothd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o attrib/bluetoothd-att.obj `if test -f 'attrib/att.c'; then $(CYGPATH_W) 'attrib/att.c'; else $(CYGPATH_W) '$(srcdir)/attrib/att.c'; fi` attrib/bluetoothd-gatt.o: attrib/gatt.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_bluetoothd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT attrib/bluetoothd-gatt.o -MD -MP -MF attrib/$(DEPDIR)/bluetoothd-gatt.Tpo -c -o attrib/bluetoothd-gatt.o `test -f 'attrib/gatt.c' || echo '$(srcdir)/'`attrib/gatt.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) attrib/$(DEPDIR)/bluetoothd-gatt.Tpo attrib/$(DEPDIR)/bluetoothd-gatt.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='attrib/gatt.c' object='attrib/bluetoothd-gatt.o' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_bluetoothd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o attrib/bluetoothd-gatt.o `test -f 'attrib/gatt.c' || echo '$(srcdir)/'`attrib/gatt.c attrib/bluetoothd-gatt.obj: attrib/gatt.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_bluetoothd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT attrib/bluetoothd-gatt.obj -MD -MP -MF attrib/$(DEPDIR)/bluetoothd-gatt.Tpo -c -o attrib/bluetoothd-gatt.obj `if test -f 'attrib/gatt.c'; then $(CYGPATH_W) 'attrib/gatt.c'; else $(CYGPATH_W) '$(srcdir)/attrib/gatt.c'; fi` @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) attrib/$(DEPDIR)/bluetoothd-gatt.Tpo attrib/$(DEPDIR)/bluetoothd-gatt.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='attrib/gatt.c' object='attrib/bluetoothd-gatt.obj' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_bluetoothd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o attrib/bluetoothd-gatt.obj `if test -f 'attrib/gatt.c'; then $(CYGPATH_W) 'attrib/gatt.c'; else $(CYGPATH_W) '$(srcdir)/attrib/gatt.c'; fi` attrib/bluetoothd-gattrib.o: attrib/gattrib.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_bluetoothd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT attrib/bluetoothd-gattrib.o -MD -MP -MF attrib/$(DEPDIR)/bluetoothd-gattrib.Tpo -c -o attrib/bluetoothd-gattrib.o `test -f 'attrib/gattrib.c' || echo '$(srcdir)/'`attrib/gattrib.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) attrib/$(DEPDIR)/bluetoothd-gattrib.Tpo attrib/$(DEPDIR)/bluetoothd-gattrib.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='attrib/gattrib.c' object='attrib/bluetoothd-gattrib.o' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_bluetoothd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o attrib/bluetoothd-gattrib.o `test -f 'attrib/gattrib.c' || echo '$(srcdir)/'`attrib/gattrib.c attrib/bluetoothd-gattrib.obj: attrib/gattrib.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_bluetoothd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT attrib/bluetoothd-gattrib.obj -MD -MP -MF attrib/$(DEPDIR)/bluetoothd-gattrib.Tpo -c -o attrib/bluetoothd-gattrib.obj `if test -f 'attrib/gattrib.c'; then $(CYGPATH_W) 'attrib/gattrib.c'; else $(CYGPATH_W) '$(srcdir)/attrib/gattrib.c'; fi` @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) attrib/$(DEPDIR)/bluetoothd-gattrib.Tpo attrib/$(DEPDIR)/bluetoothd-gattrib.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='attrib/gattrib.c' object='attrib/bluetoothd-gattrib.obj' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_bluetoothd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o attrib/bluetoothd-gattrib.obj `if test -f 'attrib/gattrib.c'; then $(CYGPATH_W) 'attrib/gattrib.c'; else $(CYGPATH_W) '$(srcdir)/attrib/gattrib.c'; fi` btio/bluetoothd-btio.o: btio/btio.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_bluetoothd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT btio/bluetoothd-btio.o -MD -MP -MF btio/$(DEPDIR)/bluetoothd-btio.Tpo -c -o btio/bluetoothd-btio.o `test -f 'btio/btio.c' || echo '$(srcdir)/'`btio/btio.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) btio/$(DEPDIR)/bluetoothd-btio.Tpo btio/$(DEPDIR)/bluetoothd-btio.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='btio/btio.c' object='btio/bluetoothd-btio.o' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_bluetoothd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o btio/bluetoothd-btio.o `test -f 'btio/btio.c' || echo '$(srcdir)/'`btio/btio.c btio/bluetoothd-btio.obj: btio/btio.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_bluetoothd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT btio/bluetoothd-btio.obj -MD -MP -MF btio/$(DEPDIR)/bluetoothd-btio.Tpo -c -o btio/bluetoothd-btio.obj `if test -f 'btio/btio.c'; then $(CYGPATH_W) 'btio/btio.c'; else $(CYGPATH_W) '$(srcdir)/btio/btio.c'; fi` @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) btio/$(DEPDIR)/bluetoothd-btio.Tpo btio/$(DEPDIR)/bluetoothd-btio.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='btio/btio.c' object='btio/bluetoothd-btio.obj' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_bluetoothd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o btio/bluetoothd-btio.obj `if test -f 'btio/btio.c'; then $(CYGPATH_W) 'btio/btio.c'; else $(CYGPATH_W) '$(srcdir)/btio/btio.c'; fi` src/bluetoothd-main.o: src/main.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_bluetoothd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT src/bluetoothd-main.o -MD -MP -MF src/$(DEPDIR)/bluetoothd-main.Tpo -c -o src/bluetoothd-main.o `test -f 'src/main.c' || echo '$(srcdir)/'`src/main.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) src/$(DEPDIR)/bluetoothd-main.Tpo src/$(DEPDIR)/bluetoothd-main.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='src/main.c' object='src/bluetoothd-main.o' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_bluetoothd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o src/bluetoothd-main.o `test -f 'src/main.c' || echo '$(srcdir)/'`src/main.c src/bluetoothd-main.obj: src/main.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_bluetoothd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT src/bluetoothd-main.obj -MD -MP -MF src/$(DEPDIR)/bluetoothd-main.Tpo -c -o src/bluetoothd-main.obj `if test -f 'src/main.c'; then $(CYGPATH_W) 'src/main.c'; else $(CYGPATH_W) '$(srcdir)/src/main.c'; fi` @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) src/$(DEPDIR)/bluetoothd-main.Tpo src/$(DEPDIR)/bluetoothd-main.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='src/main.c' object='src/bluetoothd-main.obj' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_bluetoothd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o src/bluetoothd-main.obj `if test -f 'src/main.c'; then $(CYGPATH_W) 'src/main.c'; else $(CYGPATH_W) '$(srcdir)/src/main.c'; fi` src/bluetoothd-log.o: src/log.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_bluetoothd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT src/bluetoothd-log.o -MD -MP -MF src/$(DEPDIR)/bluetoothd-log.Tpo -c -o src/bluetoothd-log.o `test -f 'src/log.c' || echo '$(srcdir)/'`src/log.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) src/$(DEPDIR)/bluetoothd-log.Tpo src/$(DEPDIR)/bluetoothd-log.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='src/log.c' object='src/bluetoothd-log.o' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_bluetoothd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o src/bluetoothd-log.o `test -f 'src/log.c' || echo '$(srcdir)/'`src/log.c src/bluetoothd-log.obj: src/log.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_bluetoothd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT src/bluetoothd-log.obj -MD -MP -MF src/$(DEPDIR)/bluetoothd-log.Tpo -c -o src/bluetoothd-log.obj `if test -f 'src/log.c'; then $(CYGPATH_W) 'src/log.c'; else $(CYGPATH_W) '$(srcdir)/src/log.c'; fi` @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) src/$(DEPDIR)/bluetoothd-log.Tpo src/$(DEPDIR)/bluetoothd-log.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='src/log.c' object='src/bluetoothd-log.obj' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_bluetoothd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o src/bluetoothd-log.obj `if test -f 'src/log.c'; then $(CYGPATH_W) 'src/log.c'; else $(CYGPATH_W) '$(srcdir)/src/log.c'; fi` src/bluetoothd-backtrace.o: src/backtrace.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_bluetoothd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT src/bluetoothd-backtrace.o -MD -MP -MF src/$(DEPDIR)/bluetoothd-backtrace.Tpo -c -o src/bluetoothd-backtrace.o `test -f 'src/backtrace.c' || echo '$(srcdir)/'`src/backtrace.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) src/$(DEPDIR)/bluetoothd-backtrace.Tpo src/$(DEPDIR)/bluetoothd-backtrace.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='src/backtrace.c' object='src/bluetoothd-backtrace.o' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_bluetoothd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o src/bluetoothd-backtrace.o `test -f 'src/backtrace.c' || echo '$(srcdir)/'`src/backtrace.c src/bluetoothd-backtrace.obj: src/backtrace.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_bluetoothd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT src/bluetoothd-backtrace.obj -MD -MP -MF src/$(DEPDIR)/bluetoothd-backtrace.Tpo -c -o src/bluetoothd-backtrace.obj `if test -f 'src/backtrace.c'; then $(CYGPATH_W) 'src/backtrace.c'; else $(CYGPATH_W) '$(srcdir)/src/backtrace.c'; fi` @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) src/$(DEPDIR)/bluetoothd-backtrace.Tpo src/$(DEPDIR)/bluetoothd-backtrace.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='src/backtrace.c' object='src/bluetoothd-backtrace.obj' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_bluetoothd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o src/bluetoothd-backtrace.obj `if test -f 'src/backtrace.c'; then $(CYGPATH_W) 'src/backtrace.c'; else $(CYGPATH_W) '$(srcdir)/src/backtrace.c'; fi` src/bluetoothd-rfkill.o: src/rfkill.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_bluetoothd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT src/bluetoothd-rfkill.o -MD -MP -MF src/$(DEPDIR)/bluetoothd-rfkill.Tpo -c -o src/bluetoothd-rfkill.o `test -f 'src/rfkill.c' || echo '$(srcdir)/'`src/rfkill.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) src/$(DEPDIR)/bluetoothd-rfkill.Tpo src/$(DEPDIR)/bluetoothd-rfkill.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='src/rfkill.c' object='src/bluetoothd-rfkill.o' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_bluetoothd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o src/bluetoothd-rfkill.o `test -f 'src/rfkill.c' || echo '$(srcdir)/'`src/rfkill.c src/bluetoothd-rfkill.obj: src/rfkill.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_bluetoothd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT src/bluetoothd-rfkill.obj -MD -MP -MF src/$(DEPDIR)/bluetoothd-rfkill.Tpo -c -o src/bluetoothd-rfkill.obj `if test -f 'src/rfkill.c'; then $(CYGPATH_W) 'src/rfkill.c'; else $(CYGPATH_W) '$(srcdir)/src/rfkill.c'; fi` @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) src/$(DEPDIR)/bluetoothd-rfkill.Tpo src/$(DEPDIR)/bluetoothd-rfkill.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='src/rfkill.c' object='src/bluetoothd-rfkill.obj' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_bluetoothd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o src/bluetoothd-rfkill.obj `if test -f 'src/rfkill.c'; then $(CYGPATH_W) 'src/rfkill.c'; else $(CYGPATH_W) '$(srcdir)/src/rfkill.c'; fi` src/bluetoothd-sdpd-server.o: src/sdpd-server.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_bluetoothd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT src/bluetoothd-sdpd-server.o -MD -MP -MF src/$(DEPDIR)/bluetoothd-sdpd-server.Tpo -c -o src/bluetoothd-sdpd-server.o `test -f 'src/sdpd-server.c' || echo '$(srcdir)/'`src/sdpd-server.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) src/$(DEPDIR)/bluetoothd-sdpd-server.Tpo src/$(DEPDIR)/bluetoothd-sdpd-server.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='src/sdpd-server.c' object='src/bluetoothd-sdpd-server.o' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_bluetoothd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o src/bluetoothd-sdpd-server.o `test -f 'src/sdpd-server.c' || echo '$(srcdir)/'`src/sdpd-server.c src/bluetoothd-sdpd-server.obj: src/sdpd-server.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_bluetoothd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT src/bluetoothd-sdpd-server.obj -MD -MP -MF src/$(DEPDIR)/bluetoothd-sdpd-server.Tpo -c -o src/bluetoothd-sdpd-server.obj `if test -f 'src/sdpd-server.c'; then $(CYGPATH_W) 'src/sdpd-server.c'; else $(CYGPATH_W) '$(srcdir)/src/sdpd-server.c'; fi` @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) src/$(DEPDIR)/bluetoothd-sdpd-server.Tpo src/$(DEPDIR)/bluetoothd-sdpd-server.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='src/sdpd-server.c' object='src/bluetoothd-sdpd-server.obj' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_bluetoothd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o src/bluetoothd-sdpd-server.obj `if test -f 'src/sdpd-server.c'; then $(CYGPATH_W) 'src/sdpd-server.c'; else $(CYGPATH_W) '$(srcdir)/src/sdpd-server.c'; fi` src/bluetoothd-sdpd-request.o: src/sdpd-request.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_bluetoothd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT src/bluetoothd-sdpd-request.o -MD -MP -MF src/$(DEPDIR)/bluetoothd-sdpd-request.Tpo -c -o src/bluetoothd-sdpd-request.o `test -f 'src/sdpd-request.c' || echo '$(srcdir)/'`src/sdpd-request.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) src/$(DEPDIR)/bluetoothd-sdpd-request.Tpo src/$(DEPDIR)/bluetoothd-sdpd-request.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='src/sdpd-request.c' object='src/bluetoothd-sdpd-request.o' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_bluetoothd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o src/bluetoothd-sdpd-request.o `test -f 'src/sdpd-request.c' || echo '$(srcdir)/'`src/sdpd-request.c src/bluetoothd-sdpd-request.obj: src/sdpd-request.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_bluetoothd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT src/bluetoothd-sdpd-request.obj -MD -MP -MF src/$(DEPDIR)/bluetoothd-sdpd-request.Tpo -c -o src/bluetoothd-sdpd-request.obj `if test -f 'src/sdpd-request.c'; then $(CYGPATH_W) 'src/sdpd-request.c'; else $(CYGPATH_W) '$(srcdir)/src/sdpd-request.c'; fi` @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) src/$(DEPDIR)/bluetoothd-sdpd-request.Tpo src/$(DEPDIR)/bluetoothd-sdpd-request.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='src/sdpd-request.c' object='src/bluetoothd-sdpd-request.obj' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_bluetoothd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o src/bluetoothd-sdpd-request.obj `if test -f 'src/sdpd-request.c'; then $(CYGPATH_W) 'src/sdpd-request.c'; else $(CYGPATH_W) '$(srcdir)/src/sdpd-request.c'; fi` src/bluetoothd-sdpd-service.o: src/sdpd-service.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_bluetoothd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT src/bluetoothd-sdpd-service.o -MD -MP -MF src/$(DEPDIR)/bluetoothd-sdpd-service.Tpo -c -o src/bluetoothd-sdpd-service.o `test -f 'src/sdpd-service.c' || echo '$(srcdir)/'`src/sdpd-service.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) src/$(DEPDIR)/bluetoothd-sdpd-service.Tpo src/$(DEPDIR)/bluetoothd-sdpd-service.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='src/sdpd-service.c' object='src/bluetoothd-sdpd-service.o' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_bluetoothd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o src/bluetoothd-sdpd-service.o `test -f 'src/sdpd-service.c' || echo '$(srcdir)/'`src/sdpd-service.c src/bluetoothd-sdpd-service.obj: src/sdpd-service.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_bluetoothd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT src/bluetoothd-sdpd-service.obj -MD -MP -MF src/$(DEPDIR)/bluetoothd-sdpd-service.Tpo -c -o src/bluetoothd-sdpd-service.obj `if test -f 'src/sdpd-service.c'; then $(CYGPATH_W) 'src/sdpd-service.c'; else $(CYGPATH_W) '$(srcdir)/src/sdpd-service.c'; fi` @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) src/$(DEPDIR)/bluetoothd-sdpd-service.Tpo src/$(DEPDIR)/bluetoothd-sdpd-service.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='src/sdpd-service.c' object='src/bluetoothd-sdpd-service.obj' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_bluetoothd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o src/bluetoothd-sdpd-service.obj `if test -f 'src/sdpd-service.c'; then $(CYGPATH_W) 'src/sdpd-service.c'; else $(CYGPATH_W) '$(srcdir)/src/sdpd-service.c'; fi` src/bluetoothd-sdpd-database.o: src/sdpd-database.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_bluetoothd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT src/bluetoothd-sdpd-database.o -MD -MP -MF src/$(DEPDIR)/bluetoothd-sdpd-database.Tpo -c -o src/bluetoothd-sdpd-database.o `test -f 'src/sdpd-database.c' || echo '$(srcdir)/'`src/sdpd-database.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) src/$(DEPDIR)/bluetoothd-sdpd-database.Tpo src/$(DEPDIR)/bluetoothd-sdpd-database.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='src/sdpd-database.c' object='src/bluetoothd-sdpd-database.o' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_bluetoothd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o src/bluetoothd-sdpd-database.o `test -f 'src/sdpd-database.c' || echo '$(srcdir)/'`src/sdpd-database.c src/bluetoothd-sdpd-database.obj: src/sdpd-database.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_bluetoothd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT src/bluetoothd-sdpd-database.obj -MD -MP -MF src/$(DEPDIR)/bluetoothd-sdpd-database.Tpo -c -o src/bluetoothd-sdpd-database.obj `if test -f 'src/sdpd-database.c'; then $(CYGPATH_W) 'src/sdpd-database.c'; else $(CYGPATH_W) '$(srcdir)/src/sdpd-database.c'; fi` @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) src/$(DEPDIR)/bluetoothd-sdpd-database.Tpo src/$(DEPDIR)/bluetoothd-sdpd-database.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='src/sdpd-database.c' object='src/bluetoothd-sdpd-database.obj' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_bluetoothd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o src/bluetoothd-sdpd-database.obj `if test -f 'src/sdpd-database.c'; then $(CYGPATH_W) 'src/sdpd-database.c'; else $(CYGPATH_W) '$(srcdir)/src/sdpd-database.c'; fi` src/bluetoothd-gatt-database.o: src/gatt-database.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_bluetoothd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT src/bluetoothd-gatt-database.o -MD -MP -MF src/$(DEPDIR)/bluetoothd-gatt-database.Tpo -c -o src/bluetoothd-gatt-database.o `test -f 'src/gatt-database.c' || echo '$(srcdir)/'`src/gatt-database.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) src/$(DEPDIR)/bluetoothd-gatt-database.Tpo src/$(DEPDIR)/bluetoothd-gatt-database.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='src/gatt-database.c' object='src/bluetoothd-gatt-database.o' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_bluetoothd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o src/bluetoothd-gatt-database.o `test -f 'src/gatt-database.c' || echo '$(srcdir)/'`src/gatt-database.c src/bluetoothd-gatt-database.obj: src/gatt-database.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_bluetoothd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT src/bluetoothd-gatt-database.obj -MD -MP -MF src/$(DEPDIR)/bluetoothd-gatt-database.Tpo -c -o src/bluetoothd-gatt-database.obj `if test -f 'src/gatt-database.c'; then $(CYGPATH_W) 'src/gatt-database.c'; else $(CYGPATH_W) '$(srcdir)/src/gatt-database.c'; fi` @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) src/$(DEPDIR)/bluetoothd-gatt-database.Tpo src/$(DEPDIR)/bluetoothd-gatt-database.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='src/gatt-database.c' object='src/bluetoothd-gatt-database.obj' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_bluetoothd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o src/bluetoothd-gatt-database.obj `if test -f 'src/gatt-database.c'; then $(CYGPATH_W) 'src/gatt-database.c'; else $(CYGPATH_W) '$(srcdir)/src/gatt-database.c'; fi` src/bluetoothd-sdp-xml.o: src/sdp-xml.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_bluetoothd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT src/bluetoothd-sdp-xml.o -MD -MP -MF src/$(DEPDIR)/bluetoothd-sdp-xml.Tpo -c -o src/bluetoothd-sdp-xml.o `test -f 'src/sdp-xml.c' || echo '$(srcdir)/'`src/sdp-xml.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) src/$(DEPDIR)/bluetoothd-sdp-xml.Tpo src/$(DEPDIR)/bluetoothd-sdp-xml.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='src/sdp-xml.c' object='src/bluetoothd-sdp-xml.o' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_bluetoothd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o src/bluetoothd-sdp-xml.o `test -f 'src/sdp-xml.c' || echo '$(srcdir)/'`src/sdp-xml.c src/bluetoothd-sdp-xml.obj: src/sdp-xml.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_bluetoothd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT src/bluetoothd-sdp-xml.obj -MD -MP -MF src/$(DEPDIR)/bluetoothd-sdp-xml.Tpo -c -o src/bluetoothd-sdp-xml.obj `if test -f 'src/sdp-xml.c'; then $(CYGPATH_W) 'src/sdp-xml.c'; else $(CYGPATH_W) '$(srcdir)/src/sdp-xml.c'; fi` @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) src/$(DEPDIR)/bluetoothd-sdp-xml.Tpo src/$(DEPDIR)/bluetoothd-sdp-xml.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='src/sdp-xml.c' object='src/bluetoothd-sdp-xml.obj' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_bluetoothd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o src/bluetoothd-sdp-xml.obj `if test -f 'src/sdp-xml.c'; then $(CYGPATH_W) 'src/sdp-xml.c'; else $(CYGPATH_W) '$(srcdir)/src/sdp-xml.c'; fi` src/bluetoothd-sdp-client.o: src/sdp-client.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_bluetoothd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT src/bluetoothd-sdp-client.o -MD -MP -MF src/$(DEPDIR)/bluetoothd-sdp-client.Tpo -c -o src/bluetoothd-sdp-client.o `test -f 'src/sdp-client.c' || echo '$(srcdir)/'`src/sdp-client.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) src/$(DEPDIR)/bluetoothd-sdp-client.Tpo src/$(DEPDIR)/bluetoothd-sdp-client.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='src/sdp-client.c' object='src/bluetoothd-sdp-client.o' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_bluetoothd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o src/bluetoothd-sdp-client.o `test -f 'src/sdp-client.c' || echo '$(srcdir)/'`src/sdp-client.c src/bluetoothd-sdp-client.obj: src/sdp-client.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_bluetoothd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT src/bluetoothd-sdp-client.obj -MD -MP -MF src/$(DEPDIR)/bluetoothd-sdp-client.Tpo -c -o src/bluetoothd-sdp-client.obj `if test -f 'src/sdp-client.c'; then $(CYGPATH_W) 'src/sdp-client.c'; else $(CYGPATH_W) '$(srcdir)/src/sdp-client.c'; fi` @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) src/$(DEPDIR)/bluetoothd-sdp-client.Tpo src/$(DEPDIR)/bluetoothd-sdp-client.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='src/sdp-client.c' object='src/bluetoothd-sdp-client.obj' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_bluetoothd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o src/bluetoothd-sdp-client.obj `if test -f 'src/sdp-client.c'; then $(CYGPATH_W) 'src/sdp-client.c'; else $(CYGPATH_W) '$(srcdir)/src/sdp-client.c'; fi` src/bluetoothd-textfile.o: src/textfile.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_bluetoothd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT src/bluetoothd-textfile.o -MD -MP -MF src/$(DEPDIR)/bluetoothd-textfile.Tpo -c -o src/bluetoothd-textfile.o `test -f 'src/textfile.c' || echo '$(srcdir)/'`src/textfile.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) src/$(DEPDIR)/bluetoothd-textfile.Tpo src/$(DEPDIR)/bluetoothd-textfile.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='src/textfile.c' object='src/bluetoothd-textfile.o' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_bluetoothd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o src/bluetoothd-textfile.o `test -f 'src/textfile.c' || echo '$(srcdir)/'`src/textfile.c src/bluetoothd-textfile.obj: src/textfile.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_bluetoothd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT src/bluetoothd-textfile.obj -MD -MP -MF src/$(DEPDIR)/bluetoothd-textfile.Tpo -c -o src/bluetoothd-textfile.obj `if test -f 'src/textfile.c'; then $(CYGPATH_W) 'src/textfile.c'; else $(CYGPATH_W) '$(srcdir)/src/textfile.c'; fi` @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) src/$(DEPDIR)/bluetoothd-textfile.Tpo src/$(DEPDIR)/bluetoothd-textfile.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='src/textfile.c' object='src/bluetoothd-textfile.obj' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_bluetoothd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o src/bluetoothd-textfile.obj `if test -f 'src/textfile.c'; then $(CYGPATH_W) 'src/textfile.c'; else $(CYGPATH_W) '$(srcdir)/src/textfile.c'; fi` src/bluetoothd-uuid-helper.o: src/uuid-helper.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_bluetoothd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT src/bluetoothd-uuid-helper.o -MD -MP -MF src/$(DEPDIR)/bluetoothd-uuid-helper.Tpo -c -o src/bluetoothd-uuid-helper.o `test -f 'src/uuid-helper.c' || echo '$(srcdir)/'`src/uuid-helper.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) src/$(DEPDIR)/bluetoothd-uuid-helper.Tpo src/$(DEPDIR)/bluetoothd-uuid-helper.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='src/uuid-helper.c' object='src/bluetoothd-uuid-helper.o' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_bluetoothd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o src/bluetoothd-uuid-helper.o `test -f 'src/uuid-helper.c' || echo '$(srcdir)/'`src/uuid-helper.c src/bluetoothd-uuid-helper.obj: src/uuid-helper.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_bluetoothd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT src/bluetoothd-uuid-helper.obj -MD -MP -MF src/$(DEPDIR)/bluetoothd-uuid-helper.Tpo -c -o src/bluetoothd-uuid-helper.obj `if test -f 'src/uuid-helper.c'; then $(CYGPATH_W) 'src/uuid-helper.c'; else $(CYGPATH_W) '$(srcdir)/src/uuid-helper.c'; fi` @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) src/$(DEPDIR)/bluetoothd-uuid-helper.Tpo src/$(DEPDIR)/bluetoothd-uuid-helper.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='src/uuid-helper.c' object='src/bluetoothd-uuid-helper.obj' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_bluetoothd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o src/bluetoothd-uuid-helper.obj `if test -f 'src/uuid-helper.c'; then $(CYGPATH_W) 'src/uuid-helper.c'; else $(CYGPATH_W) '$(srcdir)/src/uuid-helper.c'; fi` src/bluetoothd-plugin.o: src/plugin.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_bluetoothd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT src/bluetoothd-plugin.o -MD -MP -MF src/$(DEPDIR)/bluetoothd-plugin.Tpo -c -o src/bluetoothd-plugin.o `test -f 'src/plugin.c' || echo '$(srcdir)/'`src/plugin.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) src/$(DEPDIR)/bluetoothd-plugin.Tpo src/$(DEPDIR)/bluetoothd-plugin.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='src/plugin.c' object='src/bluetoothd-plugin.o' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_bluetoothd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o src/bluetoothd-plugin.o `test -f 'src/plugin.c' || echo '$(srcdir)/'`src/plugin.c src/bluetoothd-plugin.obj: src/plugin.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_bluetoothd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT src/bluetoothd-plugin.obj -MD -MP -MF src/$(DEPDIR)/bluetoothd-plugin.Tpo -c -o src/bluetoothd-plugin.obj `if test -f 'src/plugin.c'; then $(CYGPATH_W) 'src/plugin.c'; else $(CYGPATH_W) '$(srcdir)/src/plugin.c'; fi` @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) src/$(DEPDIR)/bluetoothd-plugin.Tpo src/$(DEPDIR)/bluetoothd-plugin.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='src/plugin.c' object='src/bluetoothd-plugin.obj' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_bluetoothd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o src/bluetoothd-plugin.obj `if test -f 'src/plugin.c'; then $(CYGPATH_W) 'src/plugin.c'; else $(CYGPATH_W) '$(srcdir)/src/plugin.c'; fi` src/bluetoothd-storage.o: src/storage.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_bluetoothd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT src/bluetoothd-storage.o -MD -MP -MF src/$(DEPDIR)/bluetoothd-storage.Tpo -c -o src/bluetoothd-storage.o `test -f 'src/storage.c' || echo '$(srcdir)/'`src/storage.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) src/$(DEPDIR)/bluetoothd-storage.Tpo src/$(DEPDIR)/bluetoothd-storage.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='src/storage.c' object='src/bluetoothd-storage.o' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_bluetoothd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o src/bluetoothd-storage.o `test -f 'src/storage.c' || echo '$(srcdir)/'`src/storage.c src/bluetoothd-storage.obj: src/storage.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_bluetoothd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT src/bluetoothd-storage.obj -MD -MP -MF src/$(DEPDIR)/bluetoothd-storage.Tpo -c -o src/bluetoothd-storage.obj `if test -f 'src/storage.c'; then $(CYGPATH_W) 'src/storage.c'; else $(CYGPATH_W) '$(srcdir)/src/storage.c'; fi` @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) src/$(DEPDIR)/bluetoothd-storage.Tpo src/$(DEPDIR)/bluetoothd-storage.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='src/storage.c' object='src/bluetoothd-storage.obj' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_bluetoothd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o src/bluetoothd-storage.obj `if test -f 'src/storage.c'; then $(CYGPATH_W) 'src/storage.c'; else $(CYGPATH_W) '$(srcdir)/src/storage.c'; fi` src/bluetoothd-advertising.o: src/advertising.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_bluetoothd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT src/bluetoothd-advertising.o -MD -MP -MF src/$(DEPDIR)/bluetoothd-advertising.Tpo -c -o src/bluetoothd-advertising.o `test -f 'src/advertising.c' || echo '$(srcdir)/'`src/advertising.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) src/$(DEPDIR)/bluetoothd-advertising.Tpo src/$(DEPDIR)/bluetoothd-advertising.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='src/advertising.c' object='src/bluetoothd-advertising.o' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_bluetoothd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o src/bluetoothd-advertising.o `test -f 'src/advertising.c' || echo '$(srcdir)/'`src/advertising.c src/bluetoothd-advertising.obj: src/advertising.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_bluetoothd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT src/bluetoothd-advertising.obj -MD -MP -MF src/$(DEPDIR)/bluetoothd-advertising.Tpo -c -o src/bluetoothd-advertising.obj `if test -f 'src/advertising.c'; then $(CYGPATH_W) 'src/advertising.c'; else $(CYGPATH_W) '$(srcdir)/src/advertising.c'; fi` @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) src/$(DEPDIR)/bluetoothd-advertising.Tpo src/$(DEPDIR)/bluetoothd-advertising.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='src/advertising.c' object='src/bluetoothd-advertising.obj' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_bluetoothd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o src/bluetoothd-advertising.obj `if test -f 'src/advertising.c'; then $(CYGPATH_W) 'src/advertising.c'; else $(CYGPATH_W) '$(srcdir)/src/advertising.c'; fi` src/bluetoothd-agent.o: src/agent.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_bluetoothd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT src/bluetoothd-agent.o -MD -MP -MF src/$(DEPDIR)/bluetoothd-agent.Tpo -c -o src/bluetoothd-agent.o `test -f 'src/agent.c' || echo '$(srcdir)/'`src/agent.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) src/$(DEPDIR)/bluetoothd-agent.Tpo src/$(DEPDIR)/bluetoothd-agent.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='src/agent.c' object='src/bluetoothd-agent.o' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_bluetoothd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o src/bluetoothd-agent.o `test -f 'src/agent.c' || echo '$(srcdir)/'`src/agent.c src/bluetoothd-agent.obj: src/agent.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_bluetoothd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT src/bluetoothd-agent.obj -MD -MP -MF src/$(DEPDIR)/bluetoothd-agent.Tpo -c -o src/bluetoothd-agent.obj `if test -f 'src/agent.c'; then $(CYGPATH_W) 'src/agent.c'; else $(CYGPATH_W) '$(srcdir)/src/agent.c'; fi` @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) src/$(DEPDIR)/bluetoothd-agent.Tpo src/$(DEPDIR)/bluetoothd-agent.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='src/agent.c' object='src/bluetoothd-agent.obj' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_bluetoothd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o src/bluetoothd-agent.obj `if test -f 'src/agent.c'; then $(CYGPATH_W) 'src/agent.c'; else $(CYGPATH_W) '$(srcdir)/src/agent.c'; fi` src/bluetoothd-error.o: src/error.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_bluetoothd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT src/bluetoothd-error.o -MD -MP -MF src/$(DEPDIR)/bluetoothd-error.Tpo -c -o src/bluetoothd-error.o `test -f 'src/error.c' || echo '$(srcdir)/'`src/error.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) src/$(DEPDIR)/bluetoothd-error.Tpo src/$(DEPDIR)/bluetoothd-error.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='src/error.c' object='src/bluetoothd-error.o' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_bluetoothd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o src/bluetoothd-error.o `test -f 'src/error.c' || echo '$(srcdir)/'`src/error.c src/bluetoothd-error.obj: src/error.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_bluetoothd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT src/bluetoothd-error.obj -MD -MP -MF src/$(DEPDIR)/bluetoothd-error.Tpo -c -o src/bluetoothd-error.obj `if test -f 'src/error.c'; then $(CYGPATH_W) 'src/error.c'; else $(CYGPATH_W) '$(srcdir)/src/error.c'; fi` @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) src/$(DEPDIR)/bluetoothd-error.Tpo src/$(DEPDIR)/bluetoothd-error.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='src/error.c' object='src/bluetoothd-error.obj' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_bluetoothd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o src/bluetoothd-error.obj `if test -f 'src/error.c'; then $(CYGPATH_W) 'src/error.c'; else $(CYGPATH_W) '$(srcdir)/src/error.c'; fi` src/bluetoothd-adapter.o: src/adapter.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_bluetoothd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT src/bluetoothd-adapter.o -MD -MP -MF src/$(DEPDIR)/bluetoothd-adapter.Tpo -c -o src/bluetoothd-adapter.o `test -f 'src/adapter.c' || echo '$(srcdir)/'`src/adapter.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) src/$(DEPDIR)/bluetoothd-adapter.Tpo src/$(DEPDIR)/bluetoothd-adapter.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='src/adapter.c' object='src/bluetoothd-adapter.o' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_bluetoothd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o src/bluetoothd-adapter.o `test -f 'src/adapter.c' || echo '$(srcdir)/'`src/adapter.c src/bluetoothd-adapter.obj: src/adapter.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_bluetoothd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT src/bluetoothd-adapter.obj -MD -MP -MF src/$(DEPDIR)/bluetoothd-adapter.Tpo -c -o src/bluetoothd-adapter.obj `if test -f 'src/adapter.c'; then $(CYGPATH_W) 'src/adapter.c'; else $(CYGPATH_W) '$(srcdir)/src/adapter.c'; fi` @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) src/$(DEPDIR)/bluetoothd-adapter.Tpo src/$(DEPDIR)/bluetoothd-adapter.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='src/adapter.c' object='src/bluetoothd-adapter.obj' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_bluetoothd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o src/bluetoothd-adapter.obj `if test -f 'src/adapter.c'; then $(CYGPATH_W) 'src/adapter.c'; else $(CYGPATH_W) '$(srcdir)/src/adapter.c'; fi` src/bluetoothd-profile.o: src/profile.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_bluetoothd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT src/bluetoothd-profile.o -MD -MP -MF src/$(DEPDIR)/bluetoothd-profile.Tpo -c -o src/bluetoothd-profile.o `test -f 'src/profile.c' || echo '$(srcdir)/'`src/profile.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) src/$(DEPDIR)/bluetoothd-profile.Tpo src/$(DEPDIR)/bluetoothd-profile.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='src/profile.c' object='src/bluetoothd-profile.o' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_bluetoothd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o src/bluetoothd-profile.o `test -f 'src/profile.c' || echo '$(srcdir)/'`src/profile.c src/bluetoothd-profile.obj: src/profile.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_bluetoothd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT src/bluetoothd-profile.obj -MD -MP -MF src/$(DEPDIR)/bluetoothd-profile.Tpo -c -o src/bluetoothd-profile.obj `if test -f 'src/profile.c'; then $(CYGPATH_W) 'src/profile.c'; else $(CYGPATH_W) '$(srcdir)/src/profile.c'; fi` @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) src/$(DEPDIR)/bluetoothd-profile.Tpo src/$(DEPDIR)/bluetoothd-profile.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='src/profile.c' object='src/bluetoothd-profile.obj' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_bluetoothd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o src/bluetoothd-profile.obj `if test -f 'src/profile.c'; then $(CYGPATH_W) 'src/profile.c'; else $(CYGPATH_W) '$(srcdir)/src/profile.c'; fi` src/bluetoothd-service.o: src/service.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_bluetoothd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT src/bluetoothd-service.o -MD -MP -MF src/$(DEPDIR)/bluetoothd-service.Tpo -c -o src/bluetoothd-service.o `test -f 'src/service.c' || echo '$(srcdir)/'`src/service.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) src/$(DEPDIR)/bluetoothd-service.Tpo src/$(DEPDIR)/bluetoothd-service.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='src/service.c' object='src/bluetoothd-service.o' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_bluetoothd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o src/bluetoothd-service.o `test -f 'src/service.c' || echo '$(srcdir)/'`src/service.c src/bluetoothd-service.obj: src/service.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_bluetoothd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT src/bluetoothd-service.obj -MD -MP -MF src/$(DEPDIR)/bluetoothd-service.Tpo -c -o src/bluetoothd-service.obj `if test -f 'src/service.c'; then $(CYGPATH_W) 'src/service.c'; else $(CYGPATH_W) '$(srcdir)/src/service.c'; fi` @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) src/$(DEPDIR)/bluetoothd-service.Tpo src/$(DEPDIR)/bluetoothd-service.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='src/service.c' object='src/bluetoothd-service.obj' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_bluetoothd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o src/bluetoothd-service.obj `if test -f 'src/service.c'; then $(CYGPATH_W) 'src/service.c'; else $(CYGPATH_W) '$(srcdir)/src/service.c'; fi` src/bluetoothd-gatt-client.o: src/gatt-client.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_bluetoothd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT src/bluetoothd-gatt-client.o -MD -MP -MF src/$(DEPDIR)/bluetoothd-gatt-client.Tpo -c -o src/bluetoothd-gatt-client.o `test -f 'src/gatt-client.c' || echo '$(srcdir)/'`src/gatt-client.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) src/$(DEPDIR)/bluetoothd-gatt-client.Tpo src/$(DEPDIR)/bluetoothd-gatt-client.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='src/gatt-client.c' object='src/bluetoothd-gatt-client.o' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_bluetoothd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o src/bluetoothd-gatt-client.o `test -f 'src/gatt-client.c' || echo '$(srcdir)/'`src/gatt-client.c src/bluetoothd-gatt-client.obj: src/gatt-client.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_bluetoothd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT src/bluetoothd-gatt-client.obj -MD -MP -MF src/$(DEPDIR)/bluetoothd-gatt-client.Tpo -c -o src/bluetoothd-gatt-client.obj `if test -f 'src/gatt-client.c'; then $(CYGPATH_W) 'src/gatt-client.c'; else $(CYGPATH_W) '$(srcdir)/src/gatt-client.c'; fi` @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) src/$(DEPDIR)/bluetoothd-gatt-client.Tpo src/$(DEPDIR)/bluetoothd-gatt-client.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='src/gatt-client.c' object='src/bluetoothd-gatt-client.obj' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_bluetoothd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o src/bluetoothd-gatt-client.obj `if test -f 'src/gatt-client.c'; then $(CYGPATH_W) 'src/gatt-client.c'; else $(CYGPATH_W) '$(srcdir)/src/gatt-client.c'; fi` src/bluetoothd-device.o: src/device.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_bluetoothd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT src/bluetoothd-device.o -MD -MP -MF src/$(DEPDIR)/bluetoothd-device.Tpo -c -o src/bluetoothd-device.o `test -f 'src/device.c' || echo '$(srcdir)/'`src/device.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) src/$(DEPDIR)/bluetoothd-device.Tpo src/$(DEPDIR)/bluetoothd-device.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='src/device.c' object='src/bluetoothd-device.o' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_bluetoothd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o src/bluetoothd-device.o `test -f 'src/device.c' || echo '$(srcdir)/'`src/device.c src/bluetoothd-device.obj: src/device.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_bluetoothd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT src/bluetoothd-device.obj -MD -MP -MF src/$(DEPDIR)/bluetoothd-device.Tpo -c -o src/bluetoothd-device.obj `if test -f 'src/device.c'; then $(CYGPATH_W) 'src/device.c'; else $(CYGPATH_W) '$(srcdir)/src/device.c'; fi` @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) src/$(DEPDIR)/bluetoothd-device.Tpo src/$(DEPDIR)/bluetoothd-device.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='src/device.c' object='src/bluetoothd-device.obj' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_bluetoothd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o src/bluetoothd-device.obj `if test -f 'src/device.c'; then $(CYGPATH_W) 'src/device.c'; else $(CYGPATH_W) '$(srcdir)/src/device.c'; fi` src/bluetoothd-dbus-common.o: src/dbus-common.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_bluetoothd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT src/bluetoothd-dbus-common.o -MD -MP -MF src/$(DEPDIR)/bluetoothd-dbus-common.Tpo -c -o src/bluetoothd-dbus-common.o `test -f 'src/dbus-common.c' || echo '$(srcdir)/'`src/dbus-common.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) src/$(DEPDIR)/bluetoothd-dbus-common.Tpo src/$(DEPDIR)/bluetoothd-dbus-common.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='src/dbus-common.c' object='src/bluetoothd-dbus-common.o' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_bluetoothd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o src/bluetoothd-dbus-common.o `test -f 'src/dbus-common.c' || echo '$(srcdir)/'`src/dbus-common.c src/bluetoothd-dbus-common.obj: src/dbus-common.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_bluetoothd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT src/bluetoothd-dbus-common.obj -MD -MP -MF src/$(DEPDIR)/bluetoothd-dbus-common.Tpo -c -o src/bluetoothd-dbus-common.obj `if test -f 'src/dbus-common.c'; then $(CYGPATH_W) 'src/dbus-common.c'; else $(CYGPATH_W) '$(srcdir)/src/dbus-common.c'; fi` @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) src/$(DEPDIR)/bluetoothd-dbus-common.Tpo src/$(DEPDIR)/bluetoothd-dbus-common.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='src/dbus-common.c' object='src/bluetoothd-dbus-common.obj' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_bluetoothd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o src/bluetoothd-dbus-common.obj `if test -f 'src/dbus-common.c'; then $(CYGPATH_W) 'src/dbus-common.c'; else $(CYGPATH_W) '$(srcdir)/src/dbus-common.c'; fi` src/bluetoothd-eir.o: src/eir.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_bluetoothd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT src/bluetoothd-eir.o -MD -MP -MF src/$(DEPDIR)/bluetoothd-eir.Tpo -c -o src/bluetoothd-eir.o `test -f 'src/eir.c' || echo '$(srcdir)/'`src/eir.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) src/$(DEPDIR)/bluetoothd-eir.Tpo src/$(DEPDIR)/bluetoothd-eir.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='src/eir.c' object='src/bluetoothd-eir.o' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_bluetoothd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o src/bluetoothd-eir.o `test -f 'src/eir.c' || echo '$(srcdir)/'`src/eir.c src/bluetoothd-eir.obj: src/eir.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_bluetoothd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT src/bluetoothd-eir.obj -MD -MP -MF src/$(DEPDIR)/bluetoothd-eir.Tpo -c -o src/bluetoothd-eir.obj `if test -f 'src/eir.c'; then $(CYGPATH_W) 'src/eir.c'; else $(CYGPATH_W) '$(srcdir)/src/eir.c'; fi` @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) src/$(DEPDIR)/bluetoothd-eir.Tpo src/$(DEPDIR)/bluetoothd-eir.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='src/eir.c' object='src/bluetoothd-eir.obj' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_bluetoothd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o src/bluetoothd-eir.obj `if test -f 'src/eir.c'; then $(CYGPATH_W) 'src/eir.c'; else $(CYGPATH_W) '$(srcdir)/src/eir.c'; fi` src/bluetoothd-adv_monitor.o: src/adv_monitor.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_bluetoothd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT src/bluetoothd-adv_monitor.o -MD -MP -MF src/$(DEPDIR)/bluetoothd-adv_monitor.Tpo -c -o src/bluetoothd-adv_monitor.o `test -f 'src/adv_monitor.c' || echo '$(srcdir)/'`src/adv_monitor.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) src/$(DEPDIR)/bluetoothd-adv_monitor.Tpo src/$(DEPDIR)/bluetoothd-adv_monitor.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='src/adv_monitor.c' object='src/bluetoothd-adv_monitor.o' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_bluetoothd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o src/bluetoothd-adv_monitor.o `test -f 'src/adv_monitor.c' || echo '$(srcdir)/'`src/adv_monitor.c src/bluetoothd-adv_monitor.obj: src/adv_monitor.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_bluetoothd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT src/bluetoothd-adv_monitor.obj -MD -MP -MF src/$(DEPDIR)/bluetoothd-adv_monitor.Tpo -c -o src/bluetoothd-adv_monitor.obj `if test -f 'src/adv_monitor.c'; then $(CYGPATH_W) 'src/adv_monitor.c'; else $(CYGPATH_W) '$(srcdir)/src/adv_monitor.c'; fi` @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) src/$(DEPDIR)/bluetoothd-adv_monitor.Tpo src/$(DEPDIR)/bluetoothd-adv_monitor.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='src/adv_monitor.c' object='src/bluetoothd-adv_monitor.obj' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_bluetoothd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o src/bluetoothd-adv_monitor.obj `if test -f 'src/adv_monitor.c'; then $(CYGPATH_W) 'src/adv_monitor.c'; else $(CYGPATH_W) '$(srcdir)/src/adv_monitor.c'; fi` src/bluetoothd-battery.o: src/battery.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_bluetoothd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT src/bluetoothd-battery.o -MD -MP -MF src/$(DEPDIR)/bluetoothd-battery.Tpo -c -o src/bluetoothd-battery.o `test -f 'src/battery.c' || echo '$(srcdir)/'`src/battery.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) src/$(DEPDIR)/bluetoothd-battery.Tpo src/$(DEPDIR)/bluetoothd-battery.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='src/battery.c' object='src/bluetoothd-battery.o' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_bluetoothd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o src/bluetoothd-battery.o `test -f 'src/battery.c' || echo '$(srcdir)/'`src/battery.c src/bluetoothd-battery.obj: src/battery.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_bluetoothd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT src/bluetoothd-battery.obj -MD -MP -MF src/$(DEPDIR)/bluetoothd-battery.Tpo -c -o src/bluetoothd-battery.obj `if test -f 'src/battery.c'; then $(CYGPATH_W) 'src/battery.c'; else $(CYGPATH_W) '$(srcdir)/src/battery.c'; fi` @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) src/$(DEPDIR)/bluetoothd-battery.Tpo src/$(DEPDIR)/bluetoothd-battery.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='src/battery.c' object='src/bluetoothd-battery.obj' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_bluetoothd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o src/bluetoothd-battery.obj `if test -f 'src/battery.c'; then $(CYGPATH_W) 'src/battery.c'; else $(CYGPATH_W) '$(srcdir)/src/battery.c'; fi` src/bluetoothd-settings.o: src/settings.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_bluetoothd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT src/bluetoothd-settings.o -MD -MP -MF src/$(DEPDIR)/bluetoothd-settings.Tpo -c -o src/bluetoothd-settings.o `test -f 'src/settings.c' || echo '$(srcdir)/'`src/settings.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) src/$(DEPDIR)/bluetoothd-settings.Tpo src/$(DEPDIR)/bluetoothd-settings.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='src/settings.c' object='src/bluetoothd-settings.o' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_bluetoothd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o src/bluetoothd-settings.o `test -f 'src/settings.c' || echo '$(srcdir)/'`src/settings.c src/bluetoothd-settings.obj: src/settings.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_bluetoothd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT src/bluetoothd-settings.obj -MD -MP -MF src/$(DEPDIR)/bluetoothd-settings.Tpo -c -o src/bluetoothd-settings.obj `if test -f 'src/settings.c'; then $(CYGPATH_W) 'src/settings.c'; else $(CYGPATH_W) '$(srcdir)/src/settings.c'; fi` @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) src/$(DEPDIR)/bluetoothd-settings.Tpo src/$(DEPDIR)/bluetoothd-settings.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='src/settings.c' object='src/bluetoothd-settings.obj' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_bluetoothd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o src/bluetoothd-settings.obj `if test -f 'src/settings.c'; then $(CYGPATH_W) 'src/settings.c'; else $(CYGPATH_W) '$(srcdir)/src/settings.c'; fi` src/bluetoothd-set.o: src/set.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_bluetoothd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT src/bluetoothd-set.o -MD -MP -MF src/$(DEPDIR)/bluetoothd-set.Tpo -c -o src/bluetoothd-set.o `test -f 'src/set.c' || echo '$(srcdir)/'`src/set.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) src/$(DEPDIR)/bluetoothd-set.Tpo src/$(DEPDIR)/bluetoothd-set.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='src/set.c' object='src/bluetoothd-set.o' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_bluetoothd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o src/bluetoothd-set.o `test -f 'src/set.c' || echo '$(srcdir)/'`src/set.c src/bluetoothd-set.obj: src/set.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_bluetoothd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT src/bluetoothd-set.obj -MD -MP -MF src/$(DEPDIR)/bluetoothd-set.Tpo -c -o src/bluetoothd-set.obj `if test -f 'src/set.c'; then $(CYGPATH_W) 'src/set.c'; else $(CYGPATH_W) '$(srcdir)/src/set.c'; fi` @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) src/$(DEPDIR)/bluetoothd-set.Tpo src/$(DEPDIR)/bluetoothd-set.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='src/set.c' object='src/bluetoothd-set.obj' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_bluetoothd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o src/bluetoothd-set.obj `if test -f 'src/set.c'; then $(CYGPATH_W) 'src/set.c'; else $(CYGPATH_W) '$(srcdir)/src/set.c'; fi` unit/test_mesh_crypto-test-mesh-crypto.o: unit/test-mesh-crypto.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(unit_test_mesh_crypto_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT unit/test_mesh_crypto-test-mesh-crypto.o -MD -MP -MF unit/$(DEPDIR)/test_mesh_crypto-test-mesh-crypto.Tpo -c -o unit/test_mesh_crypto-test-mesh-crypto.o `test -f 'unit/test-mesh-crypto.c' || echo '$(srcdir)/'`unit/test-mesh-crypto.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) unit/$(DEPDIR)/test_mesh_crypto-test-mesh-crypto.Tpo unit/$(DEPDIR)/test_mesh_crypto-test-mesh-crypto.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='unit/test-mesh-crypto.c' object='unit/test_mesh_crypto-test-mesh-crypto.o' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(unit_test_mesh_crypto_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o unit/test_mesh_crypto-test-mesh-crypto.o `test -f 'unit/test-mesh-crypto.c' || echo '$(srcdir)/'`unit/test-mesh-crypto.c unit/test_mesh_crypto-test-mesh-crypto.obj: unit/test-mesh-crypto.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(unit_test_mesh_crypto_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT unit/test_mesh_crypto-test-mesh-crypto.obj -MD -MP -MF unit/$(DEPDIR)/test_mesh_crypto-test-mesh-crypto.Tpo -c -o unit/test_mesh_crypto-test-mesh-crypto.obj `if test -f 'unit/test-mesh-crypto.c'; then $(CYGPATH_W) 'unit/test-mesh-crypto.c'; else $(CYGPATH_W) '$(srcdir)/unit/test-mesh-crypto.c'; fi` @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) unit/$(DEPDIR)/test_mesh_crypto-test-mesh-crypto.Tpo unit/$(DEPDIR)/test_mesh_crypto-test-mesh-crypto.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='unit/test-mesh-crypto.c' object='unit/test_mesh_crypto-test-mesh-crypto.obj' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(unit_test_mesh_crypto_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o unit/test_mesh_crypto-test-mesh-crypto.obj `if test -f 'unit/test-mesh-crypto.c'; then $(CYGPATH_W) 'unit/test-mesh-crypto.c'; else $(CYGPATH_W) '$(srcdir)/unit/test-mesh-crypto.c'; fi` unit/test_midi-test-midi.o: unit/test-midi.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(unit_test_midi_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT unit/test_midi-test-midi.o -MD -MP -MF unit/$(DEPDIR)/test_midi-test-midi.Tpo -c -o unit/test_midi-test-midi.o `test -f 'unit/test-midi.c' || echo '$(srcdir)/'`unit/test-midi.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) unit/$(DEPDIR)/test_midi-test-midi.Tpo unit/$(DEPDIR)/test_midi-test-midi.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='unit/test-midi.c' object='unit/test_midi-test-midi.o' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(unit_test_midi_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o unit/test_midi-test-midi.o `test -f 'unit/test-midi.c' || echo '$(srcdir)/'`unit/test-midi.c unit/test_midi-test-midi.obj: unit/test-midi.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(unit_test_midi_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT unit/test_midi-test-midi.obj -MD -MP -MF unit/$(DEPDIR)/test_midi-test-midi.Tpo -c -o unit/test_midi-test-midi.obj `if test -f 'unit/test-midi.c'; then $(CYGPATH_W) 'unit/test-midi.c'; else $(CYGPATH_W) '$(srcdir)/unit/test-midi.c'; fi` @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) unit/$(DEPDIR)/test_midi-test-midi.Tpo unit/$(DEPDIR)/test_midi-test-midi.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='unit/test-midi.c' object='unit/test_midi-test-midi.obj' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(unit_test_midi_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o unit/test_midi-test-midi.obj `if test -f 'unit/test-midi.c'; then $(CYGPATH_W) 'unit/test-midi.c'; else $(CYGPATH_W) '$(srcdir)/unit/test-midi.c'; fi` profiles/midi/unit_test_midi-libmidi.o: profiles/midi/libmidi.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(unit_test_midi_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT profiles/midi/unit_test_midi-libmidi.o -MD -MP -MF profiles/midi/$(DEPDIR)/unit_test_midi-libmidi.Tpo -c -o profiles/midi/unit_test_midi-libmidi.o `test -f 'profiles/midi/libmidi.c' || echo '$(srcdir)/'`profiles/midi/libmidi.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) profiles/midi/$(DEPDIR)/unit_test_midi-libmidi.Tpo profiles/midi/$(DEPDIR)/unit_test_midi-libmidi.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='profiles/midi/libmidi.c' object='profiles/midi/unit_test_midi-libmidi.o' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(unit_test_midi_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o profiles/midi/unit_test_midi-libmidi.o `test -f 'profiles/midi/libmidi.c' || echo '$(srcdir)/'`profiles/midi/libmidi.c profiles/midi/unit_test_midi-libmidi.obj: profiles/midi/libmidi.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(unit_test_midi_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT profiles/midi/unit_test_midi-libmidi.obj -MD -MP -MF profiles/midi/$(DEPDIR)/unit_test_midi-libmidi.Tpo -c -o profiles/midi/unit_test_midi-libmidi.obj `if test -f 'profiles/midi/libmidi.c'; then $(CYGPATH_W) 'profiles/midi/libmidi.c'; else $(CYGPATH_W) '$(srcdir)/profiles/midi/libmidi.c'; fi` @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) profiles/midi/$(DEPDIR)/unit_test_midi-libmidi.Tpo profiles/midi/$(DEPDIR)/unit_test_midi-libmidi.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='profiles/midi/libmidi.c' object='profiles/midi/unit_test_midi-libmidi.obj' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(unit_test_midi_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o profiles/midi/unit_test_midi-libmidi.obj `if test -f 'profiles/midi/libmidi.c'; then $(CYGPATH_W) 'profiles/midi/libmidi.c'; else $(CYGPATH_W) '$(srcdir)/profiles/midi/libmidi.c'; fi` mostlyclean-libtool: -rm -f *.lo clean-libtool: -rm -rf .libs _libs -rm -rf android/.libs android/_libs -rm -rf android/audio_utils/.libs android/audio_utils/_libs -rm -rf attrib/.libs attrib/_libs -rm -rf client/.libs client/_libs -rm -rf ell/.libs ell/_libs -rm -rf emulator/.libs emulator/_libs -rm -rf gdbus/.libs gdbus/_libs -rm -rf lib/.libs lib/_libs -rm -rf mesh/.libs mesh/_libs -rm -rf monitor/.libs monitor/_libs -rm -rf obexd/src/.libs obexd/src/_libs -rm -rf peripheral/.libs peripheral/_libs -rm -rf profiles/cups/.libs profiles/cups/_libs -rm -rf profiles/iap/.libs profiles/iap/_libs -rm -rf src/.libs src/_libs -rm -rf src/shared/.libs src/shared/_libs -rm -rf tools/.libs tools/_libs -rm -rf unit/.libs unit/_libs distclean-libtool: -rm -f libtool config.lt install-man1: $(man_MANS) @$(NORMAL_INSTALL) @list1=''; \ list2='$(man_MANS)'; \ test -n "$(man1dir)" \ && test -n "`echo $$list1$$list2`" \ || exit 0; \ echo " $(MKDIR_P) '$(DESTDIR)$(man1dir)'"; \ $(MKDIR_P) "$(DESTDIR)$(man1dir)" || exit 1; \ { for i in $$list1; do echo "$$i"; done; \ if test -n "$$list2"; then \ for i in $$list2; do echo "$$i"; done \ | sed -n '/\.1[a-z]*$$/p'; \ fi; \ } | while read p; do \ if test -f $$p; then d=; else d="$(srcdir)/"; fi; \ echo "$$d$$p"; echo "$$p"; \ done | \ sed -e 'n;s,.*/,,;p;h;s,.*\.,,;s,^[^1][0-9a-z]*$$,1,;x' \ -e 's,\.[0-9a-z]*$$,,;$(transform);G;s,\n,.,' | \ sed 'N;N;s,\n, ,g' | { \ list=; while read file base inst; do \ if test "$$base" = "$$inst"; then list="$$list $$file"; else \ echo " $(INSTALL_DATA) '$$file' '$(DESTDIR)$(man1dir)/$$inst'"; \ $(INSTALL_DATA) "$$file" "$(DESTDIR)$(man1dir)/$$inst" || exit $$?; \ fi; \ done; \ for i in $$list; do echo "$$i"; done | $(am__base_list) | \ while read files; do \ test -z "$$files" || { \ echo " $(INSTALL_DATA) $$files '$(DESTDIR)$(man1dir)'"; \ $(INSTALL_DATA) $$files "$(DESTDIR)$(man1dir)" || exit $$?; }; \ done; } uninstall-man1: @$(NORMAL_UNINSTALL) @list=''; test -n "$(man1dir)" || exit 0; \ files=`{ for i in $$list; do echo "$$i"; done; \ l2='$(man_MANS)'; for i in $$l2; do echo "$$i"; done | \ sed -n '/\.1[a-z]*$$/p'; \ } | sed -e 's,.*/,,;h;s,.*\.,,;s,^[^1][0-9a-z]*$$,1,;x' \ -e 's,\.[0-9a-z]*$$,,;$(transform);G;s,\n,.,'`; \ dir='$(DESTDIR)$(man1dir)'; $(am__uninstall_files_from_dir) install-man5: $(man_MANS) @$(NORMAL_INSTALL) @list1=''; \ list2='$(man_MANS)'; \ test -n "$(man5dir)" \ && test -n "`echo $$list1$$list2`" \ || exit 0; \ echo " $(MKDIR_P) '$(DESTDIR)$(man5dir)'"; \ $(MKDIR_P) "$(DESTDIR)$(man5dir)" || exit 1; \ { for i in $$list1; do echo "$$i"; done; \ if test -n "$$list2"; then \ for i in $$list2; do echo "$$i"; done \ | sed -n '/\.5[a-z]*$$/p'; \ fi; \ } | while read p; do \ if test -f $$p; then d=; else d="$(srcdir)/"; fi; \ echo "$$d$$p"; echo "$$p"; \ done | \ sed -e 'n;s,.*/,,;p;h;s,.*\.,,;s,^[^5][0-9a-z]*$$,5,;x' \ -e 's,\.[0-9a-z]*$$,,;$(transform);G;s,\n,.,' | \ sed 'N;N;s,\n, ,g' | { \ list=; while read file base inst; do \ if test "$$base" = "$$inst"; then list="$$list $$file"; else \ echo " $(INSTALL_DATA) '$$file' '$(DESTDIR)$(man5dir)/$$inst'"; \ $(INSTALL_DATA) "$$file" "$(DESTDIR)$(man5dir)/$$inst" || exit $$?; \ fi; \ done; \ for i in $$list; do echo "$$i"; done | $(am__base_list) | \ while read files; do \ test -z "$$files" || { \ echo " $(INSTALL_DATA) $$files '$(DESTDIR)$(man5dir)'"; \ $(INSTALL_DATA) $$files "$(DESTDIR)$(man5dir)" || exit $$?; }; \ done; } uninstall-man5: @$(NORMAL_UNINSTALL) @list=''; test -n "$(man5dir)" || exit 0; \ files=`{ for i in $$list; do echo "$$i"; done; \ l2='$(man_MANS)'; for i in $$l2; do echo "$$i"; done | \ sed -n '/\.5[a-z]*$$/p'; \ } | sed -e 's,.*/,,;h;s,.*\.,,;s,^[^5][0-9a-z]*$$,5,;x' \ -e 's,\.[0-9a-z]*$$,,;$(transform);G;s,\n,.,'`; \ dir='$(DESTDIR)$(man5dir)'; $(am__uninstall_files_from_dir) install-man7: $(man_MANS) @$(NORMAL_INSTALL) @list1=''; \ list2='$(man_MANS)'; \ test -n "$(man7dir)" \ && test -n "`echo $$list1$$list2`" \ || exit 0; \ echo " $(MKDIR_P) '$(DESTDIR)$(man7dir)'"; \ $(MKDIR_P) "$(DESTDIR)$(man7dir)" || exit 1; \ { for i in $$list1; do echo "$$i"; done; \ if test -n "$$list2"; then \ for i in $$list2; do echo "$$i"; done \ | sed -n '/\.7[a-z]*$$/p'; \ fi; \ } | while read p; do \ if test -f $$p; then d=; else d="$(srcdir)/"; fi; \ echo "$$d$$p"; echo "$$p"; \ done | \ sed -e 'n;s,.*/,,;p;h;s,.*\.,,;s,^[^7][0-9a-z]*$$,7,;x' \ -e 's,\.[0-9a-z]*$$,,;$(transform);G;s,\n,.,' | \ sed 'N;N;s,\n, ,g' | { \ list=; while read file base inst; do \ if test "$$base" = "$$inst"; then list="$$list $$file"; else \ echo " $(INSTALL_DATA) '$$file' '$(DESTDIR)$(man7dir)/$$inst'"; \ $(INSTALL_DATA) "$$file" "$(DESTDIR)$(man7dir)/$$inst" || exit $$?; \ fi; \ done; \ for i in $$list; do echo "$$i"; done | $(am__base_list) | \ while read files; do \ test -z "$$files" || { \ echo " $(INSTALL_DATA) $$files '$(DESTDIR)$(man7dir)'"; \ $(INSTALL_DATA) $$files "$(DESTDIR)$(man7dir)" || exit $$?; }; \ done; } uninstall-man7: @$(NORMAL_UNINSTALL) @list=''; test -n "$(man7dir)" || exit 0; \ files=`{ for i in $$list; do echo "$$i"; done; \ l2='$(man_MANS)'; for i in $$l2; do echo "$$i"; done | \ sed -n '/\.7[a-z]*$$/p'; \ } | sed -e 's,.*/,,;h;s,.*\.,,;s,^[^7][0-9a-z]*$$,7,;x' \ -e 's,\.[0-9a-z]*$$,,;$(transform);G;s,\n,.,'`; \ dir='$(DESTDIR)$(man7dir)'; $(am__uninstall_files_from_dir) install-man8: $(man_MANS) @$(NORMAL_INSTALL) @list1=''; \ list2='$(man_MANS)'; \ test -n "$(man8dir)" \ && test -n "`echo $$list1$$list2`" \ || exit 0; \ echo " $(MKDIR_P) '$(DESTDIR)$(man8dir)'"; \ $(MKDIR_P) "$(DESTDIR)$(man8dir)" || exit 1; \ { for i in $$list1; do echo "$$i"; done; \ if test -n "$$list2"; then \ for i in $$list2; do echo "$$i"; done \ | sed -n '/\.8[a-z]*$$/p'; \ fi; \ } | while read p; do \ if test -f $$p; then d=; else d="$(srcdir)/"; fi; \ echo "$$d$$p"; echo "$$p"; \ done | \ sed -e 'n;s,.*/,,;p;h;s,.*\.,,;s,^[^8][0-9a-z]*$$,8,;x' \ -e 's,\.[0-9a-z]*$$,,;$(transform);G;s,\n,.,' | \ sed 'N;N;s,\n, ,g' | { \ list=; while read file base inst; do \ if test "$$base" = "$$inst"; then list="$$list $$file"; else \ echo " $(INSTALL_DATA) '$$file' '$(DESTDIR)$(man8dir)/$$inst'"; \ $(INSTALL_DATA) "$$file" "$(DESTDIR)$(man8dir)/$$inst" || exit $$?; \ fi; \ done; \ for i in $$list; do echo "$$i"; done | $(am__base_list) | \ while read files; do \ test -z "$$files" || { \ echo " $(INSTALL_DATA) $$files '$(DESTDIR)$(man8dir)'"; \ $(INSTALL_DATA) $$files "$(DESTDIR)$(man8dir)" || exit $$?; }; \ done; } uninstall-man8: @$(NORMAL_UNINSTALL) @list=''; test -n "$(man8dir)" || exit 0; \ files=`{ for i in $$list; do echo "$$i"; done; \ l2='$(man_MANS)'; for i in $$l2; do echo "$$i"; done | \ sed -n '/\.8[a-z]*$$/p'; \ } | sed -e 's,.*/,,;h;s,.*\.,,;s,^[^8][0-9a-z]*$$,8,;x' \ -e 's,\.[0-9a-z]*$$,,;$(transform);G;s,\n,.,'`; \ dir='$(DESTDIR)$(man8dir)'; $(am__uninstall_files_from_dir) install-confDATA: $(conf_DATA) @$(NORMAL_INSTALL) @list='$(conf_DATA)'; test -n "$(confdir)" || list=; \ if test -n "$$list"; then \ echo " $(MKDIR_P) '$(DESTDIR)$(confdir)'"; \ $(MKDIR_P) "$(DESTDIR)$(confdir)" || exit 1; \ fi; \ for p in $$list; do \ if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \ echo "$$d$$p"; \ done | $(am__base_list) | \ while read files; do \ echo " $(INSTALL_DATA) $$files '$(DESTDIR)$(confdir)'"; \ $(INSTALL_DATA) $$files "$(DESTDIR)$(confdir)" || exit $$?; \ done uninstall-confDATA: @$(NORMAL_UNINSTALL) @list='$(conf_DATA)'; test -n "$(confdir)" || list=; \ files=`for p in $$list; do echo $$p; done | sed -e 's|^.*/||'`; \ dir='$(DESTDIR)$(confdir)'; $(am__uninstall_files_from_dir) install-dbusDATA: $(dbus_DATA) @$(NORMAL_INSTALL) @list='$(dbus_DATA)'; test -n "$(dbusdir)" || list=; \ if test -n "$$list"; then \ echo " $(MKDIR_P) '$(DESTDIR)$(dbusdir)'"; \ $(MKDIR_P) "$(DESTDIR)$(dbusdir)" || exit 1; \ fi; \ for p in $$list; do \ if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \ echo "$$d$$p"; \ done | $(am__base_list) | \ while read files; do \ echo " $(INSTALL_DATA) $$files '$(DESTDIR)$(dbusdir)'"; \ $(INSTALL_DATA) $$files "$(DESTDIR)$(dbusdir)" || exit $$?; \ done uninstall-dbusDATA: @$(NORMAL_UNINSTALL) @list='$(dbus_DATA)'; test -n "$(dbusdir)" || list=; \ files=`for p in $$list; do echo $$p; done | sed -e 's|^.*/||'`; \ dir='$(DESTDIR)$(dbusdir)'; $(am__uninstall_files_from_dir) install-dbussessionbusDATA: $(dbussessionbus_DATA) @$(NORMAL_INSTALL) @list='$(dbussessionbus_DATA)'; test -n "$(dbussessionbusdir)" || list=; \ if test -n "$$list"; then \ echo " $(MKDIR_P) '$(DESTDIR)$(dbussessionbusdir)'"; \ $(MKDIR_P) "$(DESTDIR)$(dbussessionbusdir)" || exit 1; \ fi; \ for p in $$list; do \ if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \ echo "$$d$$p"; \ done | $(am__base_list) | \ while read files; do \ echo " $(INSTALL_DATA) $$files '$(DESTDIR)$(dbussessionbusdir)'"; \ $(INSTALL_DATA) $$files "$(DESTDIR)$(dbussessionbusdir)" || exit $$?; \ done uninstall-dbussessionbusDATA: @$(NORMAL_UNINSTALL) @list='$(dbussessionbus_DATA)'; test -n "$(dbussessionbusdir)" || list=; \ files=`for p in $$list; do echo $$p; done | sed -e 's|^.*/||'`; \ dir='$(DESTDIR)$(dbussessionbusdir)'; $(am__uninstall_files_from_dir) install-dbussystembusDATA: $(dbussystembus_DATA) @$(NORMAL_INSTALL) @list='$(dbussystembus_DATA)'; test -n "$(dbussystembusdir)" || list=; \ if test -n "$$list"; then \ echo " $(MKDIR_P) '$(DESTDIR)$(dbussystembusdir)'"; \ $(MKDIR_P) "$(DESTDIR)$(dbussystembusdir)" || exit 1; \ fi; \ for p in $$list; do \ if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \ echo "$$d$$p"; \ done | $(am__base_list) | \ while read files; do \ echo " $(INSTALL_DATA) $$files '$(DESTDIR)$(dbussystembusdir)'"; \ $(INSTALL_DATA) $$files "$(DESTDIR)$(dbussystembusdir)" || exit $$?; \ done uninstall-dbussystembusDATA: @$(NORMAL_UNINSTALL) @list='$(dbussystembus_DATA)'; test -n "$(dbussystembusdir)" || list=; \ files=`for p in $$list; do echo $$p; done | sed -e 's|^.*/||'`; \ dir='$(DESTDIR)$(dbussystembusdir)'; $(am__uninstall_files_from_dir) install-dist_zshcompletionDATA: $(dist_zshcompletion_DATA) @$(NORMAL_INSTALL) @list='$(dist_zshcompletion_DATA)'; test -n "$(zshcompletiondir)" || list=; \ if test -n "$$list"; then \ echo " $(MKDIR_P) '$(DESTDIR)$(zshcompletiondir)'"; \ $(MKDIR_P) "$(DESTDIR)$(zshcompletiondir)" || exit 1; \ fi; \ for p in $$list; do \ if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \ echo "$$d$$p"; \ done | $(am__base_list) | \ while read files; do \ echo " $(INSTALL_DATA) $$files '$(DESTDIR)$(zshcompletiondir)'"; \ $(INSTALL_DATA) $$files "$(DESTDIR)$(zshcompletiondir)" || exit $$?; \ done uninstall-dist_zshcompletionDATA: @$(NORMAL_UNINSTALL) @list='$(dist_zshcompletion_DATA)'; test -n "$(zshcompletiondir)" || list=; \ files=`for p in $$list; do echo $$p; done | sed -e 's|^.*/||'`; \ dir='$(DESTDIR)$(zshcompletiondir)'; $(am__uninstall_files_from_dir) install-pkgconfigDATA: $(pkgconfig_DATA) @$(NORMAL_INSTALL) @list='$(pkgconfig_DATA)'; test -n "$(pkgconfigdir)" || list=; \ if test -n "$$list"; then \ echo " $(MKDIR_P) '$(DESTDIR)$(pkgconfigdir)'"; \ $(MKDIR_P) "$(DESTDIR)$(pkgconfigdir)" || exit 1; \ fi; \ for p in $$list; do \ if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \ echo "$$d$$p"; \ done | $(am__base_list) | \ while read files; do \ echo " $(INSTALL_DATA) $$files '$(DESTDIR)$(pkgconfigdir)'"; \ $(INSTALL_DATA) $$files "$(DESTDIR)$(pkgconfigdir)" || exit $$?; \ done uninstall-pkgconfigDATA: @$(NORMAL_UNINSTALL) @list='$(pkgconfig_DATA)'; test -n "$(pkgconfigdir)" || list=; \ files=`for p in $$list; do echo $$p; done | sed -e 's|^.*/||'`; \ dir='$(DESTDIR)$(pkgconfigdir)'; $(am__uninstall_files_from_dir) install-rulesDATA: $(rules_DATA) @$(NORMAL_INSTALL) @list='$(rules_DATA)'; test -n "$(rulesdir)" || list=; \ if test -n "$$list"; then \ echo " $(MKDIR_P) '$(DESTDIR)$(rulesdir)'"; \ $(MKDIR_P) "$(DESTDIR)$(rulesdir)" || exit 1; \ fi; \ for p in $$list; do \ if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \ echo "$$d$$p"; \ done | $(am__base_list) | \ while read files; do \ echo " $(INSTALL_DATA) $$files '$(DESTDIR)$(rulesdir)'"; \ $(INSTALL_DATA) $$files "$(DESTDIR)$(rulesdir)" || exit $$?; \ done uninstall-rulesDATA: @$(NORMAL_UNINSTALL) @list='$(rules_DATA)'; test -n "$(rulesdir)" || list=; \ files=`for p in $$list; do echo $$p; done | sed -e 's|^.*/||'`; \ dir='$(DESTDIR)$(rulesdir)'; $(am__uninstall_files_from_dir) install-stateDATA: $(state_DATA) @$(NORMAL_INSTALL) @list='$(state_DATA)'; test -n "$(statedir)" || list=; \ if test -n "$$list"; then \ echo " $(MKDIR_P) '$(DESTDIR)$(statedir)'"; \ $(MKDIR_P) "$(DESTDIR)$(statedir)" || exit 1; \ fi; \ for p in $$list; do \ if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \ echo "$$d$$p"; \ done | $(am__base_list) | \ while read files; do \ echo " $(INSTALL_DATA) $$files '$(DESTDIR)$(statedir)'"; \ $(INSTALL_DATA) $$files "$(DESTDIR)$(statedir)" || exit $$?; \ done uninstall-stateDATA: @$(NORMAL_UNINSTALL) @list='$(state_DATA)'; test -n "$(statedir)" || list=; \ files=`for p in $$list; do echo $$p; done | sed -e 's|^.*/||'`; \ dir='$(DESTDIR)$(statedir)'; $(am__uninstall_files_from_dir) install-systemdsystemunitDATA: $(systemdsystemunit_DATA) @$(NORMAL_INSTALL) @list='$(systemdsystemunit_DATA)'; test -n "$(systemdsystemunitdir)" || list=; \ if test -n "$$list"; then \ echo " $(MKDIR_P) '$(DESTDIR)$(systemdsystemunitdir)'"; \ $(MKDIR_P) "$(DESTDIR)$(systemdsystemunitdir)" || exit 1; \ fi; \ for p in $$list; do \ if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \ echo "$$d$$p"; \ done | $(am__base_list) | \ while read files; do \ echo " $(INSTALL_DATA) $$files '$(DESTDIR)$(systemdsystemunitdir)'"; \ $(INSTALL_DATA) $$files "$(DESTDIR)$(systemdsystemunitdir)" || exit $$?; \ done uninstall-systemdsystemunitDATA: @$(NORMAL_UNINSTALL) @list='$(systemdsystemunit_DATA)'; test -n "$(systemdsystemunitdir)" || list=; \ files=`for p in $$list; do echo $$p; done | sed -e 's|^.*/||'`; \ dir='$(DESTDIR)$(systemdsystemunitdir)'; $(am__uninstall_files_from_dir) install-systemduserunitDATA: $(systemduserunit_DATA) @$(NORMAL_INSTALL) @list='$(systemduserunit_DATA)'; test -n "$(systemduserunitdir)" || list=; \ if test -n "$$list"; then \ echo " $(MKDIR_P) '$(DESTDIR)$(systemduserunitdir)'"; \ $(MKDIR_P) "$(DESTDIR)$(systemduserunitdir)" || exit 1; \ fi; \ for p in $$list; do \ if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \ echo "$$d$$p"; \ done | $(am__base_list) | \ while read files; do \ echo " $(INSTALL_DATA) $$files '$(DESTDIR)$(systemduserunitdir)'"; \ $(INSTALL_DATA) $$files "$(DESTDIR)$(systemduserunitdir)" || exit $$?; \ done uninstall-systemduserunitDATA: @$(NORMAL_UNINSTALL) @list='$(systemduserunit_DATA)'; test -n "$(systemduserunitdir)" || list=; \ files=`for p in $$list; do echo $$p; done | sed -e 's|^.*/||'`; \ dir='$(DESTDIR)$(systemduserunitdir)'; $(am__uninstall_files_from_dir) install-pkgincludeHEADERS: $(pkginclude_HEADERS) @$(NORMAL_INSTALL) @list='$(pkginclude_HEADERS)'; test -n "$(pkgincludedir)" || list=; \ if test -n "$$list"; then \ echo " $(MKDIR_P) '$(DESTDIR)$(pkgincludedir)'"; \ $(MKDIR_P) "$(DESTDIR)$(pkgincludedir)" || exit 1; \ fi; \ for p in $$list; do \ if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \ echo "$$d$$p"; \ done | $(am__base_list) | \ while read files; do \ echo " $(INSTALL_HEADER) $$files '$(DESTDIR)$(pkgincludedir)'"; \ $(INSTALL_HEADER) $$files "$(DESTDIR)$(pkgincludedir)" || exit $$?; \ done uninstall-pkgincludeHEADERS: @$(NORMAL_UNINSTALL) @list='$(pkginclude_HEADERS)'; test -n "$(pkgincludedir)" || list=; \ files=`for p in $$list; do echo $$p; done | sed -e 's|^.*/||'`; \ dir='$(DESTDIR)$(pkgincludedir)'; $(am__uninstall_files_from_dir) ID: $(am__tagged_files) $(am__define_uniq_tagged_files); mkid -fID $$unique tags: tags-am TAGS: tags tags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) set x; \ here=`pwd`; \ $(am__define_uniq_tagged_files); \ shift; \ if test -z "$(ETAGS_ARGS)$$*$$unique"; then :; else \ test -n "$$unique" || unique=$$empty_fix; \ if test $$# -gt 0; then \ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ "$$@" $$unique; \ else \ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ $$unique; \ fi; \ fi ctags: ctags-am CTAGS: ctags ctags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) $(am__define_uniq_tagged_files); \ test -z "$(CTAGS_ARGS)$$unique" \ || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \ $$unique GTAGS: here=`$(am__cd) $(top_builddir) && pwd` \ && $(am__cd) $(top_srcdir) \ && gtags -i $(GTAGS_ARGS) "$$here" cscope: cscope.files test ! -s cscope.files \ || $(CSCOPE) -b -q $(AM_CSCOPEFLAGS) $(CSCOPEFLAGS) -i cscope.files $(CSCOPE_ARGS) clean-cscope: -rm -f cscope.files cscope.files: clean-cscope cscopelist cscopelist: cscopelist-am cscopelist-am: $(am__tagged_files) list='$(am__tagged_files)'; \ case "$(srcdir)" in \ [\\/]* | ?:[\\/]*) sdir="$(srcdir)" ;; \ *) sdir=$(subdir)/$(srcdir) ;; \ esac; \ for i in $$list; do \ if test -f "$$i"; then \ echo "$(subdir)/$$i"; \ else \ echo "$$sdir/$$i"; \ fi; \ done >> $(top_builddir)/cscope.files distclean-tags: -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags -rm -f cscope.out cscope.in.out cscope.po.out cscope.files # Recover from deleted '.trs' file; this should ensure that # "rm -f foo.log; make foo.trs" re-run 'foo.test', and re-create # both 'foo.log' and 'foo.trs'. Break the recipe in two subshells # to avoid problems with "make -n". .log.trs: rm -f $< $@ $(MAKE) $(AM_MAKEFLAGS) $< # Leading 'am--fnord' is there to ensure the list of targets does not # expand to empty, as could happen e.g. with make check TESTS=''. am--fnord $(TEST_LOGS) $(TEST_LOGS:.log=.trs): $(am__force_recheck) am--force-recheck: @: $(TEST_SUITE_LOG): $(TEST_LOGS) @$(am__set_TESTS_bases); \ am__f_ok () { test -f "$$1" && test -r "$$1"; }; \ redo_bases=`for i in $$bases; do \ am__f_ok $$i.trs && am__f_ok $$i.log || echo $$i; \ done`; \ if test -n "$$redo_bases"; then \ redo_logs=`for i in $$redo_bases; do echo $$i.log; done`; \ redo_results=`for i in $$redo_bases; do echo $$i.trs; done`; \ if $(am__make_dryrun); then :; else \ rm -f $$redo_logs && rm -f $$redo_results || exit 1; \ fi; \ fi; \ if test -n "$$am__remaking_logs"; then \ echo "fatal: making $(TEST_SUITE_LOG): possible infinite" \ "recursion detected" >&2; \ elif test -n "$$redo_logs"; then \ am__remaking_logs=yes $(MAKE) $(AM_MAKEFLAGS) $$redo_logs; \ fi; \ if $(am__make_dryrun); then :; else \ st=0; \ errmsg="fatal: making $(TEST_SUITE_LOG): failed to create"; \ for i in $$redo_bases; do \ test -f $$i.trs && test -r $$i.trs \ || { echo "$$errmsg $$i.trs" >&2; st=1; }; \ test -f $$i.log && test -r $$i.log \ || { echo "$$errmsg $$i.log" >&2; st=1; }; \ done; \ test $$st -eq 0 || exit 1; \ fi @$(am__sh_e_setup); $(am__tty_colors); $(am__set_TESTS_bases); \ ws='[ ]'; \ results=`for b in $$bases; do echo $$b.trs; done`; \ test -n "$$results" || results=/dev/null; \ all=` grep "^$$ws*:test-result:" $$results | wc -l`; \ pass=` grep "^$$ws*:test-result:$$ws*PASS" $$results | wc -l`; \ fail=` grep "^$$ws*:test-result:$$ws*FAIL" $$results | wc -l`; \ skip=` grep "^$$ws*:test-result:$$ws*SKIP" $$results | wc -l`; \ xfail=`grep "^$$ws*:test-result:$$ws*XFAIL" $$results | wc -l`; \ xpass=`grep "^$$ws*:test-result:$$ws*XPASS" $$results | wc -l`; \ error=`grep "^$$ws*:test-result:$$ws*ERROR" $$results | wc -l`; \ if test `expr $$fail + $$xpass + $$error` -eq 0; then \ success=true; \ else \ success=false; \ fi; \ br='==================='; br=$$br$$br$$br$$br; \ result_count () \ { \ if test x"$$1" = x"--maybe-color"; then \ maybe_colorize=yes; \ elif test x"$$1" = x"--no-color"; then \ maybe_colorize=no; \ else \ echo "$@: invalid 'result_count' usage" >&2; exit 4; \ fi; \ shift; \ desc=$$1 count=$$2; \ if test $$maybe_colorize = yes && test $$count -gt 0; then \ color_start=$$3 color_end=$$std; \ else \ color_start= color_end=; \ fi; \ echo "$${color_start}# $$desc $$count$${color_end}"; \ }; \ create_testsuite_report () \ { \ result_count $$1 "TOTAL:" $$all "$$brg"; \ result_count $$1 "PASS: " $$pass "$$grn"; \ result_count $$1 "SKIP: " $$skip "$$blu"; \ result_count $$1 "XFAIL:" $$xfail "$$lgn"; \ result_count $$1 "FAIL: " $$fail "$$red"; \ result_count $$1 "XPASS:" $$xpass "$$red"; \ result_count $$1 "ERROR:" $$error "$$mgn"; \ }; \ { \ echo "$(PACKAGE_STRING): $(subdir)/$(TEST_SUITE_LOG)" | \ $(am__rst_title); \ create_testsuite_report --no-color; \ echo; \ echo ".. contents:: :depth: 2"; \ echo; \ for b in $$bases; do echo $$b; done \ | $(am__create_global_log); \ } >$(TEST_SUITE_LOG).tmp || exit 1; \ mv $(TEST_SUITE_LOG).tmp $(TEST_SUITE_LOG); \ if $$success; then \ col="$$grn"; \ else \ col="$$red"; \ test x"$$VERBOSE" = x || cat $(TEST_SUITE_LOG); \ fi; \ echo "$${col}$$br$${std}"; \ echo "$${col}Testsuite summary"$(AM_TESTSUITE_SUMMARY_HEADER)"$${std}"; \ echo "$${col}$$br$${std}"; \ create_testsuite_report --maybe-color; \ echo "$$col$$br$$std"; \ if $$success; then :; else \ echo "$${col}See $(subdir)/$(TEST_SUITE_LOG)$${std}"; \ if test -n "$(PACKAGE_BUGREPORT)"; then \ echo "$${col}Please report to $(PACKAGE_BUGREPORT)$${std}"; \ fi; \ echo "$$col$$br$$std"; \ fi; \ $$success || exit 1 check-TESTS: @list='$(RECHECK_LOGS)'; test -z "$$list" || rm -f $$list @list='$(RECHECK_LOGS:.log=.trs)'; test -z "$$list" || rm -f $$list @test -z "$(TEST_SUITE_LOG)" || rm -f $(TEST_SUITE_LOG) @set +e; $(am__set_TESTS_bases); \ log_list=`for i in $$bases; do echo $$i.log; done`; \ trs_list=`for i in $$bases; do echo $$i.trs; done`; \ log_list=`echo $$log_list`; trs_list=`echo $$trs_list`; \ $(MAKE) $(AM_MAKEFLAGS) $(TEST_SUITE_LOG) TEST_LOGS="$$log_list"; \ exit $$?; recheck: all @test -z "$(TEST_SUITE_LOG)" || rm -f $(TEST_SUITE_LOG) @set +e; $(am__set_TESTS_bases); \ bases=`for i in $$bases; do echo $$i; done \ | $(am__list_recheck_tests)` || exit 1; \ log_list=`for i in $$bases; do echo $$i.log; done`; \ log_list=`echo $$log_list`; \ $(MAKE) $(AM_MAKEFLAGS) $(TEST_SUITE_LOG) \ am__force_recheck=am--force-recheck \ TEST_LOGS="$$log_list"; \ exit $$? android/test-ipc.log: android/test-ipc$(EXEEXT) @p='android/test-ipc$(EXEEXT)'; \ b='android/test-ipc'; \ $(am__check_pre) $(LOG_DRIVER) --test-name "$$f" \ --log-file $$b.log --trs-file $$b.trs \ $(am__common_driver_flags) $(AM_LOG_DRIVER_FLAGS) $(LOG_DRIVER_FLAGS) -- $(LOG_COMPILE) \ "$$tst" $(AM_TESTS_FD_REDIRECT) unit/test-tester.log: unit/test-tester$(EXEEXT) @p='unit/test-tester$(EXEEXT)'; \ b='unit/test-tester'; \ $(am__check_pre) $(LOG_DRIVER) --test-name "$$f" \ --log-file $$b.log --trs-file $$b.trs \ $(am__common_driver_flags) $(AM_LOG_DRIVER_FLAGS) $(LOG_DRIVER_FLAGS) -- $(LOG_COMPILE) \ "$$tst" $(AM_TESTS_FD_REDIRECT) unit/test-eir.log: unit/test-eir$(EXEEXT) @p='unit/test-eir$(EXEEXT)'; \ b='unit/test-eir'; \ $(am__check_pre) $(LOG_DRIVER) --test-name "$$f" \ --log-file $$b.log --trs-file $$b.trs \ $(am__common_driver_flags) $(AM_LOG_DRIVER_FLAGS) $(LOG_DRIVER_FLAGS) -- $(LOG_COMPILE) \ "$$tst" $(AM_TESTS_FD_REDIRECT) unit/test-uuid.log: unit/test-uuid$(EXEEXT) @p='unit/test-uuid$(EXEEXT)'; \ b='unit/test-uuid'; \ $(am__check_pre) $(LOG_DRIVER) --test-name "$$f" \ --log-file $$b.log --trs-file $$b.trs \ $(am__common_driver_flags) $(AM_LOG_DRIVER_FLAGS) $(LOG_DRIVER_FLAGS) -- $(LOG_COMPILE) \ "$$tst" $(AM_TESTS_FD_REDIRECT) unit/test-textfile.log: unit/test-textfile$(EXEEXT) @p='unit/test-textfile$(EXEEXT)'; \ b='unit/test-textfile'; \ $(am__check_pre) $(LOG_DRIVER) --test-name "$$f" \ --log-file $$b.log --trs-file $$b.trs \ $(am__common_driver_flags) $(AM_LOG_DRIVER_FLAGS) $(LOG_DRIVER_FLAGS) -- $(LOG_COMPILE) \ "$$tst" $(AM_TESTS_FD_REDIRECT) unit/test-crc.log: unit/test-crc$(EXEEXT) @p='unit/test-crc$(EXEEXT)'; \ b='unit/test-crc'; \ $(am__check_pre) $(LOG_DRIVER) --test-name "$$f" \ --log-file $$b.log --trs-file $$b.trs \ $(am__common_driver_flags) $(AM_LOG_DRIVER_FLAGS) $(LOG_DRIVER_FLAGS) -- $(LOG_COMPILE) \ "$$tst" $(AM_TESTS_FD_REDIRECT) unit/test-crypto.log: unit/test-crypto$(EXEEXT) @p='unit/test-crypto$(EXEEXT)'; \ b='unit/test-crypto'; \ $(am__check_pre) $(LOG_DRIVER) --test-name "$$f" \ --log-file $$b.log --trs-file $$b.trs \ $(am__common_driver_flags) $(AM_LOG_DRIVER_FLAGS) $(LOG_DRIVER_FLAGS) -- $(LOG_COMPILE) \ "$$tst" $(AM_TESTS_FD_REDIRECT) unit/test-ecc.log: unit/test-ecc$(EXEEXT) @p='unit/test-ecc$(EXEEXT)'; \ b='unit/test-ecc'; \ $(am__check_pre) $(LOG_DRIVER) --test-name "$$f" \ --log-file $$b.log --trs-file $$b.trs \ $(am__common_driver_flags) $(AM_LOG_DRIVER_FLAGS) $(LOG_DRIVER_FLAGS) -- $(LOG_COMPILE) \ "$$tst" $(AM_TESTS_FD_REDIRECT) unit/test-ringbuf.log: unit/test-ringbuf$(EXEEXT) @p='unit/test-ringbuf$(EXEEXT)'; \ b='unit/test-ringbuf'; \ $(am__check_pre) $(LOG_DRIVER) --test-name "$$f" \ --log-file $$b.log --trs-file $$b.trs \ $(am__common_driver_flags) $(AM_LOG_DRIVER_FLAGS) $(LOG_DRIVER_FLAGS) -- $(LOG_COMPILE) \ "$$tst" $(AM_TESTS_FD_REDIRECT) unit/test-queue.log: unit/test-queue$(EXEEXT) @p='unit/test-queue$(EXEEXT)'; \ b='unit/test-queue'; \ $(am__check_pre) $(LOG_DRIVER) --test-name "$$f" \ --log-file $$b.log --trs-file $$b.trs \ $(am__common_driver_flags) $(AM_LOG_DRIVER_FLAGS) $(LOG_DRIVER_FLAGS) -- $(LOG_COMPILE) \ "$$tst" $(AM_TESTS_FD_REDIRECT) unit/test-mgmt.log: unit/test-mgmt$(EXEEXT) @p='unit/test-mgmt$(EXEEXT)'; \ b='unit/test-mgmt'; \ $(am__check_pre) $(LOG_DRIVER) --test-name "$$f" \ --log-file $$b.log --trs-file $$b.trs \ $(am__common_driver_flags) $(AM_LOG_DRIVER_FLAGS) $(LOG_DRIVER_FLAGS) -- $(LOG_COMPILE) \ "$$tst" $(AM_TESTS_FD_REDIRECT) unit/test-uhid.log: unit/test-uhid$(EXEEXT) @p='unit/test-uhid$(EXEEXT)'; \ b='unit/test-uhid'; \ $(am__check_pre) $(LOG_DRIVER) --test-name "$$f" \ --log-file $$b.log --trs-file $$b.trs \ $(am__common_driver_flags) $(AM_LOG_DRIVER_FLAGS) $(LOG_DRIVER_FLAGS) -- $(LOG_COMPILE) \ "$$tst" $(AM_TESTS_FD_REDIRECT) unit/test-sdp.log: unit/test-sdp$(EXEEXT) @p='unit/test-sdp$(EXEEXT)'; \ b='unit/test-sdp'; \ $(am__check_pre) $(LOG_DRIVER) --test-name "$$f" \ --log-file $$b.log --trs-file $$b.trs \ $(am__common_driver_flags) $(AM_LOG_DRIVER_FLAGS) $(LOG_DRIVER_FLAGS) -- $(LOG_COMPILE) \ "$$tst" $(AM_TESTS_FD_REDIRECT) unit/test-avdtp.log: unit/test-avdtp$(EXEEXT) @p='unit/test-avdtp$(EXEEXT)'; \ b='unit/test-avdtp'; \ $(am__check_pre) $(LOG_DRIVER) --test-name "$$f" \ --log-file $$b.log --trs-file $$b.trs \ $(am__common_driver_flags) $(AM_LOG_DRIVER_FLAGS) $(LOG_DRIVER_FLAGS) -- $(LOG_COMPILE) \ "$$tst" $(AM_TESTS_FD_REDIRECT) unit/test-avctp.log: unit/test-avctp$(EXEEXT) @p='unit/test-avctp$(EXEEXT)'; \ b='unit/test-avctp'; \ $(am__check_pre) $(LOG_DRIVER) --test-name "$$f" \ --log-file $$b.log --trs-file $$b.trs \ $(am__common_driver_flags) $(AM_LOG_DRIVER_FLAGS) $(LOG_DRIVER_FLAGS) -- $(LOG_COMPILE) \ "$$tst" $(AM_TESTS_FD_REDIRECT) unit/test-avrcp.log: unit/test-avrcp$(EXEEXT) @p='unit/test-avrcp$(EXEEXT)'; \ b='unit/test-avrcp'; \ $(am__check_pre) $(LOG_DRIVER) --test-name "$$f" \ --log-file $$b.log --trs-file $$b.trs \ $(am__common_driver_flags) $(AM_LOG_DRIVER_FLAGS) $(LOG_DRIVER_FLAGS) -- $(LOG_COMPILE) \ "$$tst" $(AM_TESTS_FD_REDIRECT) unit/test-hfp.log: unit/test-hfp$(EXEEXT) @p='unit/test-hfp$(EXEEXT)'; \ b='unit/test-hfp'; \ $(am__check_pre) $(LOG_DRIVER) --test-name "$$f" \ --log-file $$b.log --trs-file $$b.trs \ $(am__common_driver_flags) $(AM_LOG_DRIVER_FLAGS) $(LOG_DRIVER_FLAGS) -- $(LOG_COMPILE) \ "$$tst" $(AM_TESTS_FD_REDIRECT) unit/test-gdbus-client.log: unit/test-gdbus-client$(EXEEXT) @p='unit/test-gdbus-client$(EXEEXT)'; \ b='unit/test-gdbus-client'; \ $(am__check_pre) $(LOG_DRIVER) --test-name "$$f" \ --log-file $$b.log --trs-file $$b.trs \ $(am__common_driver_flags) $(AM_LOG_DRIVER_FLAGS) $(LOG_DRIVER_FLAGS) -- $(LOG_COMPILE) \ "$$tst" $(AM_TESTS_FD_REDIRECT) unit/test-gobex-header.log: unit/test-gobex-header$(EXEEXT) @p='unit/test-gobex-header$(EXEEXT)'; \ b='unit/test-gobex-header'; \ $(am__check_pre) $(LOG_DRIVER) --test-name "$$f" \ --log-file $$b.log --trs-file $$b.trs \ $(am__common_driver_flags) $(AM_LOG_DRIVER_FLAGS) $(LOG_DRIVER_FLAGS) -- $(LOG_COMPILE) \ "$$tst" $(AM_TESTS_FD_REDIRECT) unit/test-gobex-packet.log: unit/test-gobex-packet$(EXEEXT) @p='unit/test-gobex-packet$(EXEEXT)'; \ b='unit/test-gobex-packet'; \ $(am__check_pre) $(LOG_DRIVER) --test-name "$$f" \ --log-file $$b.log --trs-file $$b.trs \ $(am__common_driver_flags) $(AM_LOG_DRIVER_FLAGS) $(LOG_DRIVER_FLAGS) -- $(LOG_COMPILE) \ "$$tst" $(AM_TESTS_FD_REDIRECT) unit/test-gobex.log: unit/test-gobex$(EXEEXT) @p='unit/test-gobex$(EXEEXT)'; \ b='unit/test-gobex'; \ $(am__check_pre) $(LOG_DRIVER) --test-name "$$f" \ --log-file $$b.log --trs-file $$b.trs \ $(am__common_driver_flags) $(AM_LOG_DRIVER_FLAGS) $(LOG_DRIVER_FLAGS) -- $(LOG_COMPILE) \ "$$tst" $(AM_TESTS_FD_REDIRECT) unit/test-gobex-transfer.log: unit/test-gobex-transfer$(EXEEXT) @p='unit/test-gobex-transfer$(EXEEXT)'; \ b='unit/test-gobex-transfer'; \ $(am__check_pre) $(LOG_DRIVER) --test-name "$$f" \ --log-file $$b.log --trs-file $$b.trs \ $(am__common_driver_flags) $(AM_LOG_DRIVER_FLAGS) $(LOG_DRIVER_FLAGS) -- $(LOG_COMPILE) \ "$$tst" $(AM_TESTS_FD_REDIRECT) unit/test-gobex-apparam.log: unit/test-gobex-apparam$(EXEEXT) @p='unit/test-gobex-apparam$(EXEEXT)'; \ b='unit/test-gobex-apparam'; \ $(am__check_pre) $(LOG_DRIVER) --test-name "$$f" \ --log-file $$b.log --trs-file $$b.trs \ $(am__common_driver_flags) $(AM_LOG_DRIVER_FLAGS) $(LOG_DRIVER_FLAGS) -- $(LOG_COMPILE) \ "$$tst" $(AM_TESTS_FD_REDIRECT) unit/test-lib.log: unit/test-lib$(EXEEXT) @p='unit/test-lib$(EXEEXT)'; \ b='unit/test-lib'; \ $(am__check_pre) $(LOG_DRIVER) --test-name "$$f" \ --log-file $$b.log --trs-file $$b.trs \ $(am__common_driver_flags) $(AM_LOG_DRIVER_FLAGS) $(LOG_DRIVER_FLAGS) -- $(LOG_COMPILE) \ "$$tst" $(AM_TESTS_FD_REDIRECT) unit/test-gatt.log: unit/test-gatt$(EXEEXT) @p='unit/test-gatt$(EXEEXT)'; \ b='unit/test-gatt'; \ $(am__check_pre) $(LOG_DRIVER) --test-name "$$f" \ --log-file $$b.log --trs-file $$b.trs \ $(am__common_driver_flags) $(AM_LOG_DRIVER_FLAGS) $(LOG_DRIVER_FLAGS) -- $(LOG_COMPILE) \ "$$tst" $(AM_TESTS_FD_REDIRECT) unit/test-hog.log: unit/test-hog$(EXEEXT) @p='unit/test-hog$(EXEEXT)'; \ b='unit/test-hog'; \ $(am__check_pre) $(LOG_DRIVER) --test-name "$$f" \ --log-file $$b.log --trs-file $$b.trs \ $(am__common_driver_flags) $(AM_LOG_DRIVER_FLAGS) $(LOG_DRIVER_FLAGS) -- $(LOG_COMPILE) \ "$$tst" $(AM_TESTS_FD_REDIRECT) unit/test-gattrib.log: unit/test-gattrib$(EXEEXT) @p='unit/test-gattrib$(EXEEXT)'; \ b='unit/test-gattrib'; \ $(am__check_pre) $(LOG_DRIVER) --test-name "$$f" \ --log-file $$b.log --trs-file $$b.trs \ $(am__common_driver_flags) $(AM_LOG_DRIVER_FLAGS) $(LOG_DRIVER_FLAGS) -- $(LOG_COMPILE) \ "$$tst" $(AM_TESTS_FD_REDIRECT) unit/test-bap.log: unit/test-bap$(EXEEXT) @p='unit/test-bap$(EXEEXT)'; \ b='unit/test-bap'; \ $(am__check_pre) $(LOG_DRIVER) --test-name "$$f" \ --log-file $$b.log --trs-file $$b.trs \ $(am__common_driver_flags) $(AM_LOG_DRIVER_FLAGS) $(LOG_DRIVER_FLAGS) -- $(LOG_COMPILE) \ "$$tst" $(AM_TESTS_FD_REDIRECT) unit/test-micp.log: unit/test-micp$(EXEEXT) @p='unit/test-micp$(EXEEXT)'; \ b='unit/test-micp'; \ $(am__check_pre) $(LOG_DRIVER) --test-name "$$f" \ --log-file $$b.log --trs-file $$b.trs \ $(am__common_driver_flags) $(AM_LOG_DRIVER_FLAGS) $(LOG_DRIVER_FLAGS) -- $(LOG_COMPILE) \ "$$tst" $(AM_TESTS_FD_REDIRECT) unit/test-bass.log: unit/test-bass$(EXEEXT) @p='unit/test-bass$(EXEEXT)'; \ b='unit/test-bass'; \ $(am__check_pre) $(LOG_DRIVER) --test-name "$$f" \ --log-file $$b.log --trs-file $$b.trs \ $(am__common_driver_flags) $(AM_LOG_DRIVER_FLAGS) $(LOG_DRIVER_FLAGS) -- $(LOG_COMPILE) \ "$$tst" $(AM_TESTS_FD_REDIRECT) unit/test-vcp.log: unit/test-vcp$(EXEEXT) @p='unit/test-vcp$(EXEEXT)'; \ b='unit/test-vcp'; \ $(am__check_pre) $(LOG_DRIVER) --test-name "$$f" \ --log-file $$b.log --trs-file $$b.trs \ $(am__common_driver_flags) $(AM_LOG_DRIVER_FLAGS) $(LOG_DRIVER_FLAGS) -- $(LOG_COMPILE) \ "$$tst" $(AM_TESTS_FD_REDIRECT) unit/test-midi.log: unit/test-midi$(EXEEXT) @p='unit/test-midi$(EXEEXT)'; \ b='unit/test-midi'; \ $(am__check_pre) $(LOG_DRIVER) --test-name "$$f" \ --log-file $$b.log --trs-file $$b.trs \ $(am__common_driver_flags) $(AM_LOG_DRIVER_FLAGS) $(LOG_DRIVER_FLAGS) -- $(LOG_COMPILE) \ "$$tst" $(AM_TESTS_FD_REDIRECT) unit/test-mesh-crypto.log: unit/test-mesh-crypto$(EXEEXT) @p='unit/test-mesh-crypto$(EXEEXT)'; \ b='unit/test-mesh-crypto'; \ $(am__check_pre) $(LOG_DRIVER) --test-name "$$f" \ --log-file $$b.log --trs-file $$b.trs \ $(am__common_driver_flags) $(AM_LOG_DRIVER_FLAGS) $(LOG_DRIVER_FLAGS) -- $(LOG_COMPILE) \ "$$tst" $(AM_TESTS_FD_REDIRECT) .test.log: @p='$<'; \ $(am__set_b); \ $(am__check_pre) $(TEST_LOG_DRIVER) --test-name "$$f" \ --log-file $$b.log --trs-file $$b.trs \ $(am__common_driver_flags) $(AM_TEST_LOG_DRIVER_FLAGS) $(TEST_LOG_DRIVER_FLAGS) -- $(TEST_LOG_COMPILE) \ "$$tst" $(AM_TESTS_FD_REDIRECT) @am__EXEEXT_TRUE@.test$(EXEEXT).log: @am__EXEEXT_TRUE@ @p='$<'; \ @am__EXEEXT_TRUE@ $(am__set_b); \ @am__EXEEXT_TRUE@ $(am__check_pre) $(TEST_LOG_DRIVER) --test-name "$$f" \ @am__EXEEXT_TRUE@ --log-file $$b.log --trs-file $$b.trs \ @am__EXEEXT_TRUE@ $(am__common_driver_flags) $(AM_TEST_LOG_DRIVER_FLAGS) $(TEST_LOG_DRIVER_FLAGS) -- $(TEST_LOG_COMPILE) \ @am__EXEEXT_TRUE@ "$$tst" $(AM_TESTS_FD_REDIRECT) distdir: $(BUILT_SOURCES) $(MAKE) $(AM_MAKEFLAGS) distdir-am distdir-am: $(DISTFILES) $(am__remove_distdir) test -d "$(distdir)" || mkdir "$(distdir)" @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ list='$(DISTFILES)'; \ dist_files=`for file in $$list; do echo $$file; done | \ sed -e "s|^$$srcdirstrip/||;t" \ -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \ case $$dist_files in \ */*) $(MKDIR_P) `echo "$$dist_files" | \ sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \ sort -u` ;; \ esac; \ for file in $$dist_files; do \ if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \ if test -d $$d/$$file; then \ dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \ if test -d "$(distdir)/$$file"; then \ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ fi; \ if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \ cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ fi; \ cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \ else \ test -f "$(distdir)/$$file" \ || cp -p $$d/$$file "$(distdir)/$$file" \ || exit 1; \ fi; \ done -test -n "$(am__skip_mode_fix)" \ || find "$(distdir)" -type d ! -perm -755 \ -exec chmod u+rwx,go+rx {} \; -o \ ! -type d ! -perm -444 -links 1 -exec chmod a+r {} \; -o \ ! -type d ! -perm -400 -exec chmod a+r {} \; -o \ ! -type d ! -perm -444 -exec $(install_sh) -c -m a+r {} {} \; \ || chmod -R a+r "$(distdir)" dist-gzip: distdir tardir=$(distdir) && $(am__tar) | eval GZIP= gzip $(GZIP_ENV) -c >$(distdir).tar.gz $(am__post_remove_distdir) dist-bzip2: distdir tardir=$(distdir) && $(am__tar) | BZIP2=$${BZIP2--9} bzip2 -c >$(distdir).tar.bz2 $(am__post_remove_distdir) dist-lzip: distdir tardir=$(distdir) && $(am__tar) | lzip -c $${LZIP_OPT--9} >$(distdir).tar.lz $(am__post_remove_distdir) dist-xz: distdir tardir=$(distdir) && $(am__tar) | XZ_OPT=$${XZ_OPT--e} xz -c >$(distdir).tar.xz $(am__post_remove_distdir) dist-zstd: distdir tardir=$(distdir) && $(am__tar) | zstd -c $${ZSTD_CLEVEL-$${ZSTD_OPT--19}} >$(distdir).tar.zst $(am__post_remove_distdir) dist-tarZ: distdir @echo WARNING: "Support for distribution archives compressed with" \ "legacy program 'compress' is deprecated." >&2 @echo WARNING: "It will be removed altogether in Automake 2.0" >&2 tardir=$(distdir) && $(am__tar) | compress -c >$(distdir).tar.Z $(am__post_remove_distdir) dist-shar: distdir @echo WARNING: "Support for shar distribution archives is" \ "deprecated." >&2 @echo WARNING: "It will be removed altogether in Automake 2.0" >&2 shar $(distdir) | eval GZIP= gzip $(GZIP_ENV) -c >$(distdir).shar.gz $(am__post_remove_distdir) dist-zip: distdir -rm -f $(distdir).zip zip -rq $(distdir).zip $(distdir) $(am__post_remove_distdir) dist dist-all: $(MAKE) $(AM_MAKEFLAGS) $(DIST_TARGETS) am__post_remove_distdir='@:' $(am__post_remove_distdir) # This target untars the dist file and tries a VPATH configuration. Then # it guarantees that the distribution is self-contained by making another # tarfile. distcheck: dist case '$(DIST_ARCHIVES)' in \ *.tar.gz*) \ eval GZIP= gzip $(GZIP_ENV) -dc $(distdir).tar.gz | $(am__untar) ;;\ *.tar.bz2*) \ bzip2 -dc $(distdir).tar.bz2 | $(am__untar) ;;\ *.tar.lz*) \ lzip -dc $(distdir).tar.lz | $(am__untar) ;;\ *.tar.xz*) \ xz -dc $(distdir).tar.xz | $(am__untar) ;;\ *.tar.Z*) \ uncompress -c $(distdir).tar.Z | $(am__untar) ;;\ *.shar.gz*) \ eval GZIP= gzip $(GZIP_ENV) -dc $(distdir).shar.gz | unshar ;;\ *.zip*) \ unzip $(distdir).zip ;;\ *.tar.zst*) \ zstd -dc $(distdir).tar.zst | $(am__untar) ;;\ esac chmod -R a-w $(distdir) chmod u+w $(distdir) mkdir $(distdir)/_build $(distdir)/_build/sub $(distdir)/_inst chmod a-w $(distdir) test -d $(distdir)/_build || exit 0; \ dc_install_base=`$(am__cd) $(distdir)/_inst && pwd | sed -e 's,^[^:\\/]:[\\/],/,'` \ && dc_destdir="$${TMPDIR-/tmp}/am-dc-$$$$/" \ && am__cwd=`pwd` \ && $(am__cd) $(distdir)/_build/sub \ && ../../configure \ $(AM_DISTCHECK_CONFIGURE_FLAGS) \ $(DISTCHECK_CONFIGURE_FLAGS) \ --srcdir=../.. --prefix="$$dc_install_base" \ && $(MAKE) $(AM_MAKEFLAGS) \ && $(MAKE) $(AM_MAKEFLAGS) $(AM_DISTCHECK_DVI_TARGET) \ && $(MAKE) $(AM_MAKEFLAGS) check \ && $(MAKE) $(AM_MAKEFLAGS) install \ && $(MAKE) $(AM_MAKEFLAGS) installcheck \ && $(MAKE) $(AM_MAKEFLAGS) uninstall \ && $(MAKE) $(AM_MAKEFLAGS) distuninstallcheck_dir="$$dc_install_base" \ distuninstallcheck \ && chmod -R a-w "$$dc_install_base" \ && ({ \ (cd ../.. && umask 077 && mkdir "$$dc_destdir") \ && $(MAKE) $(AM_MAKEFLAGS) DESTDIR="$$dc_destdir" install \ && $(MAKE) $(AM_MAKEFLAGS) DESTDIR="$$dc_destdir" uninstall \ && $(MAKE) $(AM_MAKEFLAGS) DESTDIR="$$dc_destdir" \ distuninstallcheck_dir="$$dc_destdir" distuninstallcheck; \ } || { rm -rf "$$dc_destdir"; exit 1; }) \ && rm -rf "$$dc_destdir" \ && $(MAKE) $(AM_MAKEFLAGS) dist \ && rm -rf $(DIST_ARCHIVES) \ && $(MAKE) $(AM_MAKEFLAGS) distcleancheck \ && cd "$$am__cwd" \ || exit 1 $(am__post_remove_distdir) @(echo "$(distdir) archives ready for distribution: "; \ list='$(DIST_ARCHIVES)'; for i in $$list; do echo $$i; done) | \ sed -e 1h -e 1s/./=/g -e 1p -e 1x -e '$$p' -e '$$x' distuninstallcheck: @test -n '$(distuninstallcheck_dir)' || { \ echo 'ERROR: trying to run $@ with an empty' \ '$$(distuninstallcheck_dir)' >&2; \ exit 1; \ }; \ $(am__cd) '$(distuninstallcheck_dir)' || { \ echo 'ERROR: cannot chdir into $(distuninstallcheck_dir)' >&2; \ exit 1; \ }; \ test `$(am__distuninstallcheck_listfiles) | wc -l` -eq 0 \ || { echo "ERROR: files left after uninstall:" ; \ if test -n "$(DESTDIR)"; then \ echo " (check DESTDIR support)"; \ fi ; \ $(distuninstallcheck_listfiles) ; \ exit 1; } >&2 distcleancheck: distclean @if test '$(srcdir)' = . ; then \ echo "ERROR: distcleancheck can only run from a VPATH build" ; \ exit 1 ; \ fi @test `$(distcleancheck_listfiles) | wc -l` -eq 0 \ || { echo "ERROR: files left in build directory after distclean:" ; \ $(distcleancheck_listfiles) ; \ exit 1; } >&2 check-am: all-am $(MAKE) $(AM_MAKEFLAGS) check-TESTS check: $(BUILT_SOURCES) $(MAKE) $(AM_MAKEFLAGS) check-am all-am: Makefile $(PROGRAMS) $(LIBRARIES) $(LTLIBRARIES) $(SCRIPTS) \ $(MANS) $(DATA) $(HEADERS) config.h install-binPROGRAMS: install-libLTLIBRARIES install-cupsPROGRAMS: install-libLTLIBRARIES install-pkglibexecPROGRAMS: install-libLTLIBRARIES install-udevPROGRAMS: install-libLTLIBRARIES install-pluginLTLIBRARIES: install-libLTLIBRARIES installdirs: for dir in "$(DESTDIR)$(bindir)" "$(DESTDIR)$(cupsdir)" "$(DESTDIR)$(pkglibexecdir)" "$(DESTDIR)$(udevdir)" "$(DESTDIR)$(libdir)" "$(DESTDIR)$(plugindir)" "$(DESTDIR)$(testdir)" "$(DESTDIR)$(man1dir)" "$(DESTDIR)$(man5dir)" "$(DESTDIR)$(man7dir)" "$(DESTDIR)$(man8dir)" "$(DESTDIR)$(confdir)" "$(DESTDIR)$(dbusdir)" "$(DESTDIR)$(dbussessionbusdir)" "$(DESTDIR)$(dbussystembusdir)" "$(DESTDIR)$(zshcompletiondir)" "$(DESTDIR)$(pkgconfigdir)" "$(DESTDIR)$(rulesdir)" "$(DESTDIR)$(statedir)" "$(DESTDIR)$(systemdsystemunitdir)" "$(DESTDIR)$(systemduserunitdir)" "$(DESTDIR)$(pkgincludedir)"; do \ test -z "$$dir" || $(MKDIR_P) "$$dir"; \ done install: $(BUILT_SOURCES) $(MAKE) $(AM_MAKEFLAGS) install-am install-exec: $(BUILT_SOURCES) $(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data: install-data-am uninstall: uninstall-am install-am: all-am @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am installcheck: installcheck-am install-strip: if test -z '$(STRIP)'; then \ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ install; \ else \ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \ fi mostlyclean-generic: -test -z "$(TEST_LOGS)" || rm -f $(TEST_LOGS) -test -z "$(TEST_LOGS:.log=.trs)" || rm -f $(TEST_LOGS:.log=.trs) -test -z "$(TEST_SUITE_LOG)" || rm -f $(TEST_SUITE_LOG) clean-generic: -test -z "$(CLEANFILES)" || rm -f $(CLEANFILES) distclean-generic: -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES) -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES) -rm -f android/$(DEPDIR)/$(am__dirstamp) -rm -f android/$(am__dirstamp) -rm -f android/audio_utils/$(DEPDIR)/$(am__dirstamp) -rm -f android/audio_utils/$(am__dirstamp) -rm -f android/client/$(DEPDIR)/$(am__dirstamp) -rm -f android/client/$(am__dirstamp) -rm -f android/hardware/$(DEPDIR)/$(am__dirstamp) -rm -f android/hardware/$(am__dirstamp) -rm -f attrib/$(DEPDIR)/$(am__dirstamp) -rm -f attrib/$(am__dirstamp) -rm -f btio/$(DEPDIR)/$(am__dirstamp) -rm -f btio/$(am__dirstamp) -rm -f client/$(DEPDIR)/$(am__dirstamp) -rm -f client/$(am__dirstamp) -rm -f ell/$(DEPDIR)/$(am__dirstamp) -rm -f ell/$(am__dirstamp) -rm -f emulator/$(DEPDIR)/$(am__dirstamp) -rm -f emulator/$(am__dirstamp) -rm -f gdbus/$(DEPDIR)/$(am__dirstamp) -rm -f gdbus/$(am__dirstamp) -rm -f gobex/$(DEPDIR)/$(am__dirstamp) -rm -f gobex/$(am__dirstamp) -rm -f lib/$(DEPDIR)/$(am__dirstamp) -rm -f lib/$(am__dirstamp) -rm -f mesh/$(DEPDIR)/$(am__dirstamp) -rm -f mesh/$(am__dirstamp) -rm -f monitor/$(DEPDIR)/$(am__dirstamp) -rm -f monitor/$(am__dirstamp) -rm -f obexd/client/$(DEPDIR)/$(am__dirstamp) -rm -f obexd/client/$(am__dirstamp) -rm -f obexd/plugins/$(DEPDIR)/$(am__dirstamp) -rm -f obexd/plugins/$(am__dirstamp) -rm -f obexd/src/$(DEPDIR)/$(am__dirstamp) -rm -f obexd/src/$(am__dirstamp) -rm -f peripheral/$(DEPDIR)/$(am__dirstamp) -rm -f peripheral/$(am__dirstamp) -rm -f plugins/$(DEPDIR)/$(am__dirstamp) -rm -f plugins/$(am__dirstamp) -rm -f profiles/audio/$(DEPDIR)/$(am__dirstamp) -rm -f profiles/audio/$(am__dirstamp) -rm -f profiles/battery/$(DEPDIR)/$(am__dirstamp) -rm -f profiles/battery/$(am__dirstamp) -rm -f profiles/cups/$(DEPDIR)/$(am__dirstamp) -rm -f profiles/cups/$(am__dirstamp) -rm -f profiles/deviceinfo/$(DEPDIR)/$(am__dirstamp) -rm -f profiles/deviceinfo/$(am__dirstamp) -rm -f profiles/gap/$(DEPDIR)/$(am__dirstamp) -rm -f profiles/gap/$(am__dirstamp) -rm -f profiles/health/$(DEPDIR)/$(am__dirstamp) -rm -f profiles/health/$(am__dirstamp) -rm -f profiles/iap/$(DEPDIR)/$(am__dirstamp) -rm -f profiles/iap/$(am__dirstamp) -rm -f profiles/input/$(DEPDIR)/$(am__dirstamp) -rm -f profiles/input/$(am__dirstamp) -rm -f profiles/midi/$(DEPDIR)/$(am__dirstamp) -rm -f profiles/midi/$(am__dirstamp) -rm -f profiles/network/$(DEPDIR)/$(am__dirstamp) -rm -f profiles/network/$(am__dirstamp) -rm -f profiles/sap/$(DEPDIR)/$(am__dirstamp) -rm -f profiles/sap/$(am__dirstamp) -rm -f profiles/scanparam/$(DEPDIR)/$(am__dirstamp) -rm -f profiles/scanparam/$(am__dirstamp) -rm -f src/$(DEPDIR)/$(am__dirstamp) -rm -f src/$(am__dirstamp) -rm -f src/shared/$(DEPDIR)/$(am__dirstamp) -rm -f src/shared/$(am__dirstamp) -rm -f tools/$(DEPDIR)/$(am__dirstamp) -rm -f tools/$(am__dirstamp) -rm -f tools/mesh-gatt/$(DEPDIR)/$(am__dirstamp) -rm -f tools/mesh-gatt/$(am__dirstamp) -rm -f tools/mesh/$(DEPDIR)/$(am__dirstamp) -rm -f tools/mesh/$(am__dirstamp) -rm -f tools/parser/$(DEPDIR)/$(am__dirstamp) -rm -f tools/parser/$(am__dirstamp) -rm -f unit/$(DEPDIR)/$(am__dirstamp) -rm -f unit/$(am__dirstamp) -test -z "$(DISTCLEANFILES)" || rm -f $(DISTCLEANFILES) maintainer-clean-generic: @echo "This command is intended for maintainers to use" @echo "it deletes files that may require special tools to rebuild." -test -z "$(BUILT_SOURCES)" || rm -f $(BUILT_SOURCES) -test -z "$(MAINTAINERCLEANFILES)" || rm -f $(MAINTAINERCLEANFILES) clean: clean-am clean-am: clean-binPROGRAMS clean-cupsPROGRAMS clean-generic \ clean-libLTLIBRARIES clean-libtool clean-local \ clean-noinstLIBRARIES clean-noinstLTLIBRARIES \ clean-noinstPROGRAMS clean-pkglibexecPROGRAMS \ clean-pluginLTLIBRARIES clean-udevPROGRAMS mostlyclean-am distclean: distclean-am -rm -f $(am__CONFIG_DISTCLEAN_FILES) -rm -f android/$(DEPDIR)/a2dp-sink.Po -rm -f android/$(DEPDIR)/a2dp.Po -rm -f android/$(DEPDIR)/android_tester-tester-a2dp.Po -rm -f android/$(DEPDIR)/android_tester-tester-avrcp.Po -rm -f android/$(DEPDIR)/android_tester-tester-bluetooth.Po -rm -f android/$(DEPDIR)/android_tester-tester-gatt.Po -rm -f android/$(DEPDIR)/android_tester-tester-hdp.Po -rm -f android/$(DEPDIR)/android_tester-tester-hidhost.Po -rm -f android/$(DEPDIR)/android_tester-tester-main.Po -rm -f android/$(DEPDIR)/android_tester-tester-map-client.Po -rm -f android/$(DEPDIR)/android_tester-tester-pan.Po -rm -f android/$(DEPDIR)/android_tester-tester-socket.Po -rm -f android/$(DEPDIR)/audio_a2dp_default_la-hal-audio-aptx.Plo -rm -f android/$(DEPDIR)/audio_a2dp_default_la-hal-audio-sbc.Plo -rm -f android/$(DEPDIR)/audio_a2dp_default_la-hal-audio.Plo -rm -f android/$(DEPDIR)/audio_sco_default_la-hal-sco.Plo -rm -f android/$(DEPDIR)/avctp.Po -rm -f android/$(DEPDIR)/avdtp.Po -rm -f android/$(DEPDIR)/avdtptest-avdtp.Po -rm -f android/$(DEPDIR)/avdtptest-avdtptest.Po -rm -f android/$(DEPDIR)/avrcp-lib.Po -rm -f android/$(DEPDIR)/avrcp.Po -rm -f android/$(DEPDIR)/bluetooth.Po -rm -f android/$(DEPDIR)/bluetooth_default_la-hal-a2dp-sink.Plo -rm -f android/$(DEPDIR)/bluetooth_default_la-hal-a2dp.Plo -rm -f android/$(DEPDIR)/bluetooth_default_la-hal-avrcp-ctrl.Plo -rm -f android/$(DEPDIR)/bluetooth_default_la-hal-avrcp.Plo -rm -f android/$(DEPDIR)/bluetooth_default_la-hal-bluetooth.Plo -rm -f android/$(DEPDIR)/bluetooth_default_la-hal-gatt.Plo -rm -f android/$(DEPDIR)/bluetooth_default_la-hal-handsfree-client.Plo -rm -f android/$(DEPDIR)/bluetooth_default_la-hal-handsfree.Plo -rm -f android/$(DEPDIR)/bluetooth_default_la-hal-health.Plo -rm -f android/$(DEPDIR)/bluetooth_default_la-hal-hidhost.Plo -rm -f android/$(DEPDIR)/bluetooth_default_la-hal-ipc.Plo -rm -f android/$(DEPDIR)/bluetooth_default_la-hal-map-client.Plo -rm -f android/$(DEPDIR)/bluetooth_default_la-hal-pan.Plo -rm -f android/$(DEPDIR)/bluetooth_default_la-hal-socket.Plo -rm -f android/$(DEPDIR)/bluetooth_default_la-hal-utils.Plo -rm -f android/$(DEPDIR)/bluetoothd-snoop.Po -rm -f android/$(DEPDIR)/gatt.Po -rm -f android/$(DEPDIR)/haltest-hal-utils.Po -rm -f android/$(DEPDIR)/handsfree-client.Po -rm -f android/$(DEPDIR)/handsfree.Po -rm -f android/$(DEPDIR)/health.Po -rm -f android/$(DEPDIR)/hidhost.Po -rm -f android/$(DEPDIR)/ipc.Po -rm -f android/$(DEPDIR)/ipc_tester-hal-utils.Po -rm -f android/$(DEPDIR)/ipc_tester-ipc-tester.Po -rm -f android/$(DEPDIR)/main.Po -rm -f android/$(DEPDIR)/map-client.Po -rm -f android/$(DEPDIR)/pan.Po -rm -f android/$(DEPDIR)/sco.Po -rm -f android/$(DEPDIR)/socket.Po -rm -f android/$(DEPDIR)/system-emulator.Po -rm -f android/$(DEPDIR)/test-ipc.Po -rm -f android/audio_utils/$(DEPDIR)/audio_sco_default_la-resampler.Plo -rm -f android/client/$(DEPDIR)/haltest-haltest.Po -rm -f android/client/$(DEPDIR)/haltest-history.Po -rm -f android/client/$(DEPDIR)/haltest-if-audio.Po -rm -f android/client/$(DEPDIR)/haltest-if-av-sink.Po -rm -f android/client/$(DEPDIR)/haltest-if-av.Po -rm -f android/client/$(DEPDIR)/haltest-if-bt.Po -rm -f android/client/$(DEPDIR)/haltest-if-gatt.Po -rm -f android/client/$(DEPDIR)/haltest-if-hf-client.Po -rm -f android/client/$(DEPDIR)/haltest-if-hf.Po -rm -f android/client/$(DEPDIR)/haltest-if-hh.Po -rm -f android/client/$(DEPDIR)/haltest-if-hl.Po -rm -f android/client/$(DEPDIR)/haltest-if-mce.Po -rm -f android/client/$(DEPDIR)/haltest-if-pan.Po -rm -f android/client/$(DEPDIR)/haltest-if-rc-ctrl.Po -rm -f android/client/$(DEPDIR)/haltest-if-rc.Po -rm -f android/client/$(DEPDIR)/haltest-if-sco.Po -rm -f android/client/$(DEPDIR)/haltest-if-sock.Po -rm -f android/client/$(DEPDIR)/haltest-pollhandler.Po -rm -f android/client/$(DEPDIR)/haltest-tabcompletion.Po -rm -f android/client/$(DEPDIR)/haltest-terminal.Po -rm -f android/hardware/$(DEPDIR)/android_tester-hardware.Po -rm -f android/hardware/$(DEPDIR)/haltest-hardware.Po -rm -f attrib/$(DEPDIR)/att.Po -rm -f attrib/$(DEPDIR)/bluetoothd-att.Po -rm -f attrib/$(DEPDIR)/bluetoothd-gatt.Po -rm -f attrib/$(DEPDIR)/bluetoothd-gattrib.Po -rm -f attrib/$(DEPDIR)/gatt.Po -rm -f attrib/$(DEPDIR)/gattrib.Po -rm -f attrib/$(DEPDIR)/gatttool.Po -rm -f attrib/$(DEPDIR)/interactive.Po -rm -f attrib/$(DEPDIR)/utils.Po -rm -f btio/$(DEPDIR)/android_avdtptest-btio.Po -rm -f btio/$(DEPDIR)/bluetoothd-btio.Po -rm -f btio/$(DEPDIR)/btio.Po -rm -f btio/$(DEPDIR)/obexd-btio.Po -rm -f client/$(DEPDIR)/admin.Po -rm -f client/$(DEPDIR)/adv_monitor.Po -rm -f client/$(DEPDIR)/advertising.Po -rm -f client/$(DEPDIR)/agent.Po -rm -f client/$(DEPDIR)/assistant.Po -rm -f client/$(DEPDIR)/display.Po -rm -f client/$(DEPDIR)/gatt.Po -rm -f client/$(DEPDIR)/hci.Po -rm -f client/$(DEPDIR)/main.Po -rm -f client/$(DEPDIR)/mgmt.Po -rm -f client/$(DEPDIR)/player.Po -rm -f client/$(DEPDIR)/print.Po -rm -f ell/$(DEPDIR)/base64.Plo -rm -f ell/$(DEPDIR)/cert-crypto.Plo -rm -f ell/$(DEPDIR)/cert.Plo -rm -f ell/$(DEPDIR)/checksum.Plo -rm -f ell/$(DEPDIR)/cipher.Plo -rm -f ell/$(DEPDIR)/dbus-client.Plo -rm -f ell/$(DEPDIR)/dbus-filter.Plo -rm -f ell/$(DEPDIR)/dbus-message.Plo -rm -f ell/$(DEPDIR)/dbus-name-cache.Plo -rm -f ell/$(DEPDIR)/dbus-service.Plo -rm -f ell/$(DEPDIR)/dbus-util.Plo -rm -f ell/$(DEPDIR)/dbus.Plo -rm -f ell/$(DEPDIR)/ecc-external.Plo -rm -f ell/$(DEPDIR)/ecc.Plo -rm -f ell/$(DEPDIR)/ecdh.Plo -rm -f ell/$(DEPDIR)/gvariant-util.Plo -rm -f ell/$(DEPDIR)/hashmap.Plo -rm -f ell/$(DEPDIR)/idle.Plo -rm -f ell/$(DEPDIR)/io.Plo -rm -f ell/$(DEPDIR)/key.Plo -rm -f ell/$(DEPDIR)/log.Plo -rm -f ell/$(DEPDIR)/main.Plo -rm -f ell/$(DEPDIR)/pem.Plo -rm -f ell/$(DEPDIR)/queue.Plo -rm -f ell/$(DEPDIR)/random.Plo -rm -f ell/$(DEPDIR)/settings.Plo -rm -f ell/$(DEPDIR)/signal.Plo -rm -f ell/$(DEPDIR)/siphash.Plo -rm -f ell/$(DEPDIR)/string.Plo -rm -f ell/$(DEPDIR)/strv.Plo -rm -f ell/$(DEPDIR)/tester.Plo -rm -f ell/$(DEPDIR)/time.Plo -rm -f ell/$(DEPDIR)/timeout.Plo -rm -f ell/$(DEPDIR)/tls-extensions.Plo -rm -f ell/$(DEPDIR)/tls-record.Plo -rm -f ell/$(DEPDIR)/tls-suites.Plo -rm -f ell/$(DEPDIR)/tls.Plo -rm -f ell/$(DEPDIR)/utf8.Plo -rm -f ell/$(DEPDIR)/util.Plo -rm -f ell/$(DEPDIR)/uuid.Plo -rm -f emulator/$(DEPDIR)/android_android_tester-btdev.Po -rm -f emulator/$(DEPDIR)/android_android_tester-bthost.Po -rm -f emulator/$(DEPDIR)/android_android_tester-hciemu.Po -rm -f emulator/$(DEPDIR)/android_android_tester-smp.Po -rm -f emulator/$(DEPDIR)/android_android_tester-vhci.Po -rm -f emulator/$(DEPDIR)/android_ipc_tester-btdev.Po -rm -f emulator/$(DEPDIR)/android_ipc_tester-bthost.Po -rm -f emulator/$(DEPDIR)/android_ipc_tester-hciemu.Po -rm -f emulator/$(DEPDIR)/android_ipc_tester-smp.Po -rm -f emulator/$(DEPDIR)/android_ipc_tester-vhci.Po -rm -f emulator/$(DEPDIR)/b1ee.Po -rm -f emulator/$(DEPDIR)/btdev.Po -rm -f emulator/$(DEPDIR)/bthost.Po -rm -f emulator/$(DEPDIR)/hciemu.Po -rm -f emulator/$(DEPDIR)/hfp.Po -rm -f emulator/$(DEPDIR)/le.Po -rm -f emulator/$(DEPDIR)/main.Po -rm -f emulator/$(DEPDIR)/phy.Po -rm -f emulator/$(DEPDIR)/serial.Po -rm -f emulator/$(DEPDIR)/server.Po -rm -f emulator/$(DEPDIR)/smp.Po -rm -f emulator/$(DEPDIR)/vhci.Po -rm -f gdbus/$(DEPDIR)/client.Plo -rm -f gdbus/$(DEPDIR)/mainloop.Plo -rm -f gdbus/$(DEPDIR)/object.Plo -rm -f gdbus/$(DEPDIR)/polkit.Plo -rm -f gdbus/$(DEPDIR)/watch.Plo -rm -f gobex/$(DEPDIR)/gobex-apparam.Po -rm -f gobex/$(DEPDIR)/gobex-defs.Po -rm -f gobex/$(DEPDIR)/gobex-header.Po -rm -f gobex/$(DEPDIR)/gobex-packet.Po -rm -f gobex/$(DEPDIR)/gobex-transfer.Po -rm -f gobex/$(DEPDIR)/gobex.Po -rm -f gobex/$(DEPDIR)/obexd-gobex-apparam.Po -rm -f gobex/$(DEPDIR)/obexd-gobex-defs.Po -rm -f gobex/$(DEPDIR)/obexd-gobex-header.Po -rm -f gobex/$(DEPDIR)/obexd-gobex-packet.Po -rm -f gobex/$(DEPDIR)/obexd-gobex-transfer.Po -rm -f gobex/$(DEPDIR)/obexd-gobex.Po -rm -f lib/$(DEPDIR)/bluetooth.Plo -rm -f lib/$(DEPDIR)/hci.Plo -rm -f lib/$(DEPDIR)/sdp.Plo -rm -f lib/$(DEPDIR)/uuid.Plo -rm -f mesh/$(DEPDIR)/agent.Po -rm -f mesh/$(DEPDIR)/appkey.Po -rm -f mesh/$(DEPDIR)/cfgmod-server.Po -rm -f mesh/$(DEPDIR)/crypto.Po -rm -f mesh/$(DEPDIR)/dbus.Po -rm -f mesh/$(DEPDIR)/friend.Po -rm -f mesh/$(DEPDIR)/keyring.Po -rm -f mesh/$(DEPDIR)/main.Po -rm -f mesh/$(DEPDIR)/manager.Po -rm -f mesh/$(DEPDIR)/mesh-config-json.Po -rm -f mesh/$(DEPDIR)/mesh-io-generic.Po -rm -f mesh/$(DEPDIR)/mesh-io-mgmt.Po -rm -f mesh/$(DEPDIR)/mesh-io-unit.Po -rm -f mesh/$(DEPDIR)/mesh-io.Po -rm -f mesh/$(DEPDIR)/mesh-mgmt.Po -rm -f mesh/$(DEPDIR)/mesh.Po -rm -f mesh/$(DEPDIR)/model.Po -rm -f mesh/$(DEPDIR)/net-keys.Po -rm -f mesh/$(DEPDIR)/net.Po -rm -f mesh/$(DEPDIR)/node.Po -rm -f mesh/$(DEPDIR)/pb-adv.Po -rm -f mesh/$(DEPDIR)/prov-acceptor.Po -rm -f mesh/$(DEPDIR)/prov-initiator.Po -rm -f mesh/$(DEPDIR)/prvbeac-server.Po -rm -f mesh/$(DEPDIR)/remprv-server.Po -rm -f mesh/$(DEPDIR)/rpl.Po -rm -f mesh/$(DEPDIR)/util.Po -rm -f monitor/$(DEPDIR)/a2dp.Po -rm -f monitor/$(DEPDIR)/analyze.Po -rm -f monitor/$(DEPDIR)/att.Po -rm -f monitor/$(DEPDIR)/avctp.Po -rm -f monitor/$(DEPDIR)/avdtp.Po -rm -f monitor/$(DEPDIR)/bnep.Po -rm -f monitor/$(DEPDIR)/broadcom.Po -rm -f monitor/$(DEPDIR)/control.Po -rm -f monitor/$(DEPDIR)/crc.Po -rm -f monitor/$(DEPDIR)/display.Po -rm -f monitor/$(DEPDIR)/ellisys.Po -rm -f monitor/$(DEPDIR)/hcidump.Po -rm -f monitor/$(DEPDIR)/hwdb.Po -rm -f monitor/$(DEPDIR)/intel.Po -rm -f monitor/$(DEPDIR)/jlink.Po -rm -f monitor/$(DEPDIR)/keys.Po -rm -f monitor/$(DEPDIR)/l2cap.Po -rm -f monitor/$(DEPDIR)/ll.Po -rm -f monitor/$(DEPDIR)/lmp.Po -rm -f monitor/$(DEPDIR)/main.Po -rm -f monitor/$(DEPDIR)/msft.Po -rm -f monitor/$(DEPDIR)/packet.Po -rm -f monitor/$(DEPDIR)/rfcomm.Po -rm -f monitor/$(DEPDIR)/sdp.Po -rm -f monitor/$(DEPDIR)/vendor.Po -rm -f obexd/client/$(DEPDIR)/obexd-bip-common.Po -rm -f obexd/client/$(DEPDIR)/obexd-bip.Po -rm -f obexd/client/$(DEPDIR)/obexd-bluetooth.Po -rm -f obexd/client/$(DEPDIR)/obexd-driver.Po -rm -f obexd/client/$(DEPDIR)/obexd-ftp.Po -rm -f obexd/client/$(DEPDIR)/obexd-manager.Po -rm -f obexd/client/$(DEPDIR)/obexd-map-event.Po -rm -f obexd/client/$(DEPDIR)/obexd-map.Po -rm -f obexd/client/$(DEPDIR)/obexd-mns.Po -rm -f obexd/client/$(DEPDIR)/obexd-opp.Po -rm -f obexd/client/$(DEPDIR)/obexd-pbap.Po -rm -f obexd/client/$(DEPDIR)/obexd-session.Po -rm -f obexd/client/$(DEPDIR)/obexd-sync.Po -rm -f obexd/client/$(DEPDIR)/obexd-transfer.Po -rm -f obexd/client/$(DEPDIR)/obexd-transport.Po -rm -f obexd/plugins/$(DEPDIR)/obexd-bluetooth.Po -rm -f obexd/plugins/$(DEPDIR)/obexd-filesystem.Po -rm -f obexd/plugins/$(DEPDIR)/obexd-ftp.Po -rm -f obexd/plugins/$(DEPDIR)/obexd-irmc.Po -rm -f obexd/plugins/$(DEPDIR)/obexd-mas.Po -rm -f obexd/plugins/$(DEPDIR)/obexd-messages-dummy.Po -rm -f obexd/plugins/$(DEPDIR)/obexd-opp.Po -rm -f obexd/plugins/$(DEPDIR)/obexd-pbap.Po -rm -f obexd/plugins/$(DEPDIR)/obexd-pcsuite.Po -rm -f obexd/plugins/$(DEPDIR)/obexd-phonebook-@PLUGIN_PHONEBOOK@.Po -rm -f obexd/plugins/$(DEPDIR)/obexd-vcard.Po -rm -f obexd/src/$(DEPDIR)/obexd-log.Po -rm -f obexd/src/$(DEPDIR)/obexd-main.Po -rm -f obexd/src/$(DEPDIR)/obexd-manager.Po -rm -f obexd/src/$(DEPDIR)/obexd-mimetype.Po -rm -f obexd/src/$(DEPDIR)/obexd-obex.Po -rm -f obexd/src/$(DEPDIR)/obexd-plugin.Po -rm -f obexd/src/$(DEPDIR)/obexd-server.Po -rm -f obexd/src/$(DEPDIR)/obexd-service.Po -rm -f obexd/src/$(DEPDIR)/obexd-transport.Po -rm -f peripheral/$(DEPDIR)/attach.Po -rm -f peripheral/$(DEPDIR)/efivars.Po -rm -f peripheral/$(DEPDIR)/gap.Po -rm -f peripheral/$(DEPDIR)/gatt.Po -rm -f peripheral/$(DEPDIR)/log.Po -rm -f peripheral/$(DEPDIR)/main.Po -rm -f plugins/$(DEPDIR)/bluetoothd-admin.Po -rm -f plugins/$(DEPDIR)/bluetoothd-autopair.Po -rm -f plugins/$(DEPDIR)/bluetoothd-hostname.Po -rm -f plugins/$(DEPDIR)/bluetoothd-neard.Po -rm -f plugins/$(DEPDIR)/bluetoothd-policy.Po -rm -f plugins/$(DEPDIR)/bluetoothd-sixaxis.Po -rm -f profiles/audio/$(DEPDIR)/bluetoothd-a2dp.Po -rm -f profiles/audio/$(DEPDIR)/bluetoothd-asha.Po -rm -f profiles/audio/$(DEPDIR)/bluetoothd-avctp.Po -rm -f profiles/audio/$(DEPDIR)/bluetoothd-avdtp.Po -rm -f profiles/audio/$(DEPDIR)/bluetoothd-avrcp.Po -rm -f profiles/audio/$(DEPDIR)/bluetoothd-bap.Po -rm -f profiles/audio/$(DEPDIR)/bluetoothd-bass.Po -rm -f profiles/audio/$(DEPDIR)/bluetoothd-ccp.Po -rm -f profiles/audio/$(DEPDIR)/bluetoothd-control.Po -rm -f profiles/audio/$(DEPDIR)/bluetoothd-csip.Po -rm -f profiles/audio/$(DEPDIR)/bluetoothd-mcp.Po -rm -f profiles/audio/$(DEPDIR)/bluetoothd-media.Po -rm -f profiles/audio/$(DEPDIR)/bluetoothd-micp.Po -rm -f profiles/audio/$(DEPDIR)/bluetoothd-player.Po -rm -f profiles/audio/$(DEPDIR)/bluetoothd-sink.Po -rm -f profiles/audio/$(DEPDIR)/bluetoothd-source.Po -rm -f profiles/audio/$(DEPDIR)/bluetoothd-transport.Po -rm -f profiles/audio/$(DEPDIR)/bluetoothd-vcp.Po -rm -f profiles/battery/$(DEPDIR)/bas.Po -rm -f profiles/battery/$(DEPDIR)/bluetoothd-bas.Po -rm -f profiles/battery/$(DEPDIR)/bluetoothd-battery.Po -rm -f profiles/cups/$(DEPDIR)/hcrp.Po -rm -f profiles/cups/$(DEPDIR)/main.Po -rm -f profiles/cups/$(DEPDIR)/sdp.Po -rm -f profiles/cups/$(DEPDIR)/spp.Po -rm -f profiles/deviceinfo/$(DEPDIR)/bluetoothd-deviceinfo.Po -rm -f profiles/deviceinfo/$(DEPDIR)/bluetoothd-dis.Po -rm -f profiles/deviceinfo/$(DEPDIR)/dis.Po -rm -f profiles/gap/$(DEPDIR)/bluetoothd-gas.Po -rm -f profiles/health/$(DEPDIR)/bluetoothd-hdp.Po -rm -f profiles/health/$(DEPDIR)/bluetoothd-hdp_main.Po -rm -f profiles/health/$(DEPDIR)/bluetoothd-hdp_manager.Po -rm -f profiles/health/$(DEPDIR)/bluetoothd-hdp_util.Po -rm -f profiles/health/$(DEPDIR)/bluetoothd-mcap.Po -rm -f profiles/health/$(DEPDIR)/mcap.Po -rm -f profiles/iap/$(DEPDIR)/main.Po -rm -f profiles/input/$(DEPDIR)/bluetoothd-device.Po -rm -f profiles/input/$(DEPDIR)/bluetoothd-hog-lib.Po -rm -f profiles/input/$(DEPDIR)/bluetoothd-hog.Po -rm -f profiles/input/$(DEPDIR)/bluetoothd-manager.Po -rm -f profiles/input/$(DEPDIR)/bluetoothd-server.Po -rm -f profiles/input/$(DEPDIR)/bluetoothd-suspend-none.Po -rm -f profiles/input/$(DEPDIR)/hog-lib.Po -rm -f profiles/midi/$(DEPDIR)/bluetoothd-libmidi.Po -rm -f profiles/midi/$(DEPDIR)/bluetoothd-midi.Po -rm -f profiles/midi/$(DEPDIR)/unit_test_midi-libmidi.Po -rm -f profiles/network/$(DEPDIR)/bluetoothd-bnep.Po -rm -f profiles/network/$(DEPDIR)/bluetoothd-connection.Po -rm -f profiles/network/$(DEPDIR)/bluetoothd-manager.Po -rm -f profiles/network/$(DEPDIR)/bluetoothd-server.Po -rm -f profiles/network/$(DEPDIR)/bnep.Po -rm -f profiles/sap/$(DEPDIR)/bluetoothd-main.Po -rm -f profiles/sap/$(DEPDIR)/bluetoothd-manager.Po -rm -f profiles/sap/$(DEPDIR)/bluetoothd-sap-dummy.Po -rm -f profiles/sap/$(DEPDIR)/bluetoothd-server.Po -rm -f profiles/scanparam/$(DEPDIR)/bluetoothd-scan.Po -rm -f profiles/scanparam/$(DEPDIR)/bluetoothd-scpp.Po -rm -f profiles/scanparam/$(DEPDIR)/scpp.Po -rm -f src/$(DEPDIR)/android_avdtptest-log.Po -rm -f src/$(DEPDIR)/bluetoothd-adapter.Po -rm -f src/$(DEPDIR)/bluetoothd-adv_monitor.Po -rm -f src/$(DEPDIR)/bluetoothd-advertising.Po -rm -f src/$(DEPDIR)/bluetoothd-agent.Po -rm -f src/$(DEPDIR)/bluetoothd-backtrace.Po -rm -f src/$(DEPDIR)/bluetoothd-battery.Po -rm -f src/$(DEPDIR)/bluetoothd-dbus-common.Po -rm -f src/$(DEPDIR)/bluetoothd-device.Po -rm -f src/$(DEPDIR)/bluetoothd-eir.Po -rm -f src/$(DEPDIR)/bluetoothd-error.Po -rm -f src/$(DEPDIR)/bluetoothd-gatt-client.Po -rm -f src/$(DEPDIR)/bluetoothd-gatt-database.Po -rm -f src/$(DEPDIR)/bluetoothd-log.Po -rm -f src/$(DEPDIR)/bluetoothd-main.Po -rm -f src/$(DEPDIR)/bluetoothd-plugin.Po -rm -f src/$(DEPDIR)/bluetoothd-profile.Po -rm -f src/$(DEPDIR)/bluetoothd-rfkill.Po -rm -f src/$(DEPDIR)/bluetoothd-sdp-client.Po -rm -f src/$(DEPDIR)/bluetoothd-sdp-xml.Po -rm -f src/$(DEPDIR)/bluetoothd-sdpd-database.Po -rm -f src/$(DEPDIR)/bluetoothd-sdpd-request.Po -rm -f src/$(DEPDIR)/bluetoothd-sdpd-server.Po -rm -f src/$(DEPDIR)/bluetoothd-sdpd-service.Po -rm -f src/$(DEPDIR)/bluetoothd-service.Po -rm -f src/$(DEPDIR)/bluetoothd-set.Po -rm -f src/$(DEPDIR)/bluetoothd-settings.Po -rm -f src/$(DEPDIR)/bluetoothd-storage.Po -rm -f src/$(DEPDIR)/bluetoothd-textfile.Po -rm -f src/$(DEPDIR)/bluetoothd-uuid-helper.Po -rm -f src/$(DEPDIR)/eir.Po -rm -f src/$(DEPDIR)/log.Po -rm -f src/$(DEPDIR)/oui.Po -rm -f src/$(DEPDIR)/sdp-client.Po -rm -f src/$(DEPDIR)/sdp-xml.Po -rm -f src/$(DEPDIR)/sdpd-database.Po -rm -f src/$(DEPDIR)/sdpd-request.Po -rm -f src/$(DEPDIR)/sdpd-server.Po -rm -f src/$(DEPDIR)/sdpd-service.Po -rm -f src/$(DEPDIR)/settings.Po -rm -f src/$(DEPDIR)/textfile.Po -rm -f src/$(DEPDIR)/uuid-helper.Po -rm -f src/shared/$(DEPDIR)/android_avdtptest-log.Po -rm -f src/shared/$(DEPDIR)/android_avdtptest-queue.Po -rm -f src/shared/$(DEPDIR)/android_avdtptest-util.Po -rm -f src/shared/$(DEPDIR)/btp.Po -rm -f src/shared/$(DEPDIR)/libshared_ell_la-ad.Plo -rm -f src/shared/$(DEPDIR)/libshared_ell_la-asha.Plo -rm -f src/shared/$(DEPDIR)/libshared_ell_la-att.Plo -rm -f src/shared/$(DEPDIR)/libshared_ell_la-bap-debug.Plo -rm -f src/shared/$(DEPDIR)/libshared_ell_la-bap.Plo -rm -f src/shared/$(DEPDIR)/libshared_ell_la-bass.Plo -rm -f src/shared/$(DEPDIR)/libshared_ell_la-btsnoop.Plo -rm -f src/shared/$(DEPDIR)/libshared_ell_la-ccp.Plo -rm -f src/shared/$(DEPDIR)/libshared_ell_la-crypto.Plo -rm -f src/shared/$(DEPDIR)/libshared_ell_la-csip.Plo -rm -f src/shared/$(DEPDIR)/libshared_ell_la-ecc.Plo -rm -f src/shared/$(DEPDIR)/libshared_ell_la-gap.Plo -rm -f src/shared/$(DEPDIR)/libshared_ell_la-gatt-client.Plo -rm -f src/shared/$(DEPDIR)/libshared_ell_la-gatt-db.Plo -rm -f src/shared/$(DEPDIR)/libshared_ell_la-gatt-helpers.Plo -rm -f src/shared/$(DEPDIR)/libshared_ell_la-gatt-server.Plo -rm -f src/shared/$(DEPDIR)/libshared_ell_la-hci-crypto.Plo -rm -f src/shared/$(DEPDIR)/libshared_ell_la-hci.Plo -rm -f src/shared/$(DEPDIR)/libshared_ell_la-hfp.Plo -rm -f src/shared/$(DEPDIR)/libshared_ell_la-io-ell.Plo -rm -f src/shared/$(DEPDIR)/libshared_ell_la-log.Plo -rm -f src/shared/$(DEPDIR)/libshared_ell_la-mainloop-ell.Plo -rm -f src/shared/$(DEPDIR)/libshared_ell_la-mcp.Plo -rm -f src/shared/$(DEPDIR)/libshared_ell_la-mgmt.Plo -rm -f src/shared/$(DEPDIR)/libshared_ell_la-micp.Plo -rm -f src/shared/$(DEPDIR)/libshared_ell_la-pcap.Plo -rm -f src/shared/$(DEPDIR)/libshared_ell_la-queue.Plo -rm -f src/shared/$(DEPDIR)/libshared_ell_la-ringbuf.Plo -rm -f src/shared/$(DEPDIR)/libshared_ell_la-shell.Plo -rm -f src/shared/$(DEPDIR)/libshared_ell_la-timeout-ell.Plo -rm -f src/shared/$(DEPDIR)/libshared_ell_la-uhid.Plo -rm -f src/shared/$(DEPDIR)/libshared_ell_la-util.Plo -rm -f src/shared/$(DEPDIR)/libshared_ell_la-vcp.Plo -rm -f src/shared/$(DEPDIR)/libshared_glib_la-ad.Plo -rm -f src/shared/$(DEPDIR)/libshared_glib_la-asha.Plo -rm -f src/shared/$(DEPDIR)/libshared_glib_la-att.Plo -rm -f src/shared/$(DEPDIR)/libshared_glib_la-bap-debug.Plo -rm -f src/shared/$(DEPDIR)/libshared_glib_la-bap.Plo -rm -f src/shared/$(DEPDIR)/libshared_glib_la-bass.Plo -rm -f src/shared/$(DEPDIR)/libshared_glib_la-btsnoop.Plo -rm -f src/shared/$(DEPDIR)/libshared_glib_la-ccp.Plo -rm -f src/shared/$(DEPDIR)/libshared_glib_la-crypto.Plo -rm -f src/shared/$(DEPDIR)/libshared_glib_la-csip.Plo -rm -f src/shared/$(DEPDIR)/libshared_glib_la-ecc.Plo -rm -f src/shared/$(DEPDIR)/libshared_glib_la-gap.Plo -rm -f src/shared/$(DEPDIR)/libshared_glib_la-gatt-client.Plo -rm -f src/shared/$(DEPDIR)/libshared_glib_la-gatt-db.Plo -rm -f src/shared/$(DEPDIR)/libshared_glib_la-gatt-helpers.Plo -rm -f src/shared/$(DEPDIR)/libshared_glib_la-gatt-server.Plo -rm -f src/shared/$(DEPDIR)/libshared_glib_la-hci-crypto.Plo -rm -f src/shared/$(DEPDIR)/libshared_glib_la-hci.Plo -rm -f src/shared/$(DEPDIR)/libshared_glib_la-hfp.Plo -rm -f src/shared/$(DEPDIR)/libshared_glib_la-io-glib.Plo -rm -f src/shared/$(DEPDIR)/libshared_glib_la-log.Plo -rm -f src/shared/$(DEPDIR)/libshared_glib_la-mainloop-glib.Plo -rm -f src/shared/$(DEPDIR)/libshared_glib_la-mainloop-notify.Plo -rm -f src/shared/$(DEPDIR)/libshared_glib_la-mcp.Plo -rm -f src/shared/$(DEPDIR)/libshared_glib_la-mgmt.Plo -rm -f src/shared/$(DEPDIR)/libshared_glib_la-micp.Plo -rm -f src/shared/$(DEPDIR)/libshared_glib_la-pcap.Plo -rm -f src/shared/$(DEPDIR)/libshared_glib_la-queue.Plo -rm -f src/shared/$(DEPDIR)/libshared_glib_la-ringbuf.Plo -rm -f src/shared/$(DEPDIR)/libshared_glib_la-shell.Plo -rm -f src/shared/$(DEPDIR)/libshared_glib_la-tester.Plo -rm -f src/shared/$(DEPDIR)/libshared_glib_la-timeout-glib.Plo -rm -f src/shared/$(DEPDIR)/libshared_glib_la-uhid.Plo -rm -f src/shared/$(DEPDIR)/libshared_glib_la-util.Plo -rm -f src/shared/$(DEPDIR)/libshared_glib_la-vcp.Plo -rm -f src/shared/$(DEPDIR)/libshared_mainloop_la-ad.Plo -rm -f src/shared/$(DEPDIR)/libshared_mainloop_la-asha.Plo -rm -f src/shared/$(DEPDIR)/libshared_mainloop_la-att.Plo -rm -f src/shared/$(DEPDIR)/libshared_mainloop_la-bap-debug.Plo -rm -f src/shared/$(DEPDIR)/libshared_mainloop_la-bap.Plo -rm -f src/shared/$(DEPDIR)/libshared_mainloop_la-bass.Plo -rm -f src/shared/$(DEPDIR)/libshared_mainloop_la-btsnoop.Plo -rm -f src/shared/$(DEPDIR)/libshared_mainloop_la-ccp.Plo -rm -f src/shared/$(DEPDIR)/libshared_mainloop_la-crypto.Plo -rm -f src/shared/$(DEPDIR)/libshared_mainloop_la-csip.Plo -rm -f src/shared/$(DEPDIR)/libshared_mainloop_la-ecc.Plo -rm -f src/shared/$(DEPDIR)/libshared_mainloop_la-gap.Plo -rm -f src/shared/$(DEPDIR)/libshared_mainloop_la-gatt-client.Plo -rm -f src/shared/$(DEPDIR)/libshared_mainloop_la-gatt-db.Plo -rm -f src/shared/$(DEPDIR)/libshared_mainloop_la-gatt-helpers.Plo -rm -f src/shared/$(DEPDIR)/libshared_mainloop_la-gatt-server.Plo -rm -f src/shared/$(DEPDIR)/libshared_mainloop_la-hci-crypto.Plo -rm -f src/shared/$(DEPDIR)/libshared_mainloop_la-hci.Plo -rm -f src/shared/$(DEPDIR)/libshared_mainloop_la-hfp.Plo -rm -f src/shared/$(DEPDIR)/libshared_mainloop_la-io-mainloop.Plo -rm -f src/shared/$(DEPDIR)/libshared_mainloop_la-log.Plo -rm -f src/shared/$(DEPDIR)/libshared_mainloop_la-mainloop-notify.Plo -rm -f src/shared/$(DEPDIR)/libshared_mainloop_la-mainloop.Plo -rm -f src/shared/$(DEPDIR)/libshared_mainloop_la-mcp.Plo -rm -f src/shared/$(DEPDIR)/libshared_mainloop_la-mgmt.Plo -rm -f src/shared/$(DEPDIR)/libshared_mainloop_la-micp.Plo -rm -f src/shared/$(DEPDIR)/libshared_mainloop_la-pcap.Plo -rm -f src/shared/$(DEPDIR)/libshared_mainloop_la-queue.Plo -rm -f src/shared/$(DEPDIR)/libshared_mainloop_la-ringbuf.Plo -rm -f src/shared/$(DEPDIR)/libshared_mainloop_la-shell.Plo -rm -f src/shared/$(DEPDIR)/libshared_mainloop_la-timeout-mainloop.Plo -rm -f src/shared/$(DEPDIR)/libshared_mainloop_la-uhid.Plo -rm -f src/shared/$(DEPDIR)/libshared_mainloop_la-util.Plo -rm -f src/shared/$(DEPDIR)/libshared_mainloop_la-vcp.Plo -rm -f tools/$(DEPDIR)/3dsp.Po -rm -f tools/$(DEPDIR)/advtest.Po -rm -f tools/$(DEPDIR)/avinfo.Po -rm -f tools/$(DEPDIR)/avtest.Po -rm -f tools/$(DEPDIR)/bcmfw.Po -rm -f tools/$(DEPDIR)/bdaddr.Po -rm -f tools/$(DEPDIR)/bluemoon.Po -rm -f tools/$(DEPDIR)/bluetooth-player.Po -rm -f tools/$(DEPDIR)/bnep-tester.Po -rm -f tools/$(DEPDIR)/bneptest.Po -rm -f tools/$(DEPDIR)/btattach.Po -rm -f tools/$(DEPDIR)/btconfig.Po -rm -f tools/$(DEPDIR)/btgatt-client.Po -rm -f tools/$(DEPDIR)/btgatt-server.Po -rm -f tools/$(DEPDIR)/btinfo.Po -rm -f tools/$(DEPDIR)/btiotest.Po -rm -f tools/$(DEPDIR)/btmgmt.Po -rm -f tools/$(DEPDIR)/btmon-logger.Po -rm -f tools/$(DEPDIR)/btpclient.Po -rm -f tools/$(DEPDIR)/btpclientctl.Po -rm -f tools/$(DEPDIR)/btproxy.Po -rm -f tools/$(DEPDIR)/btsnoop.Po -rm -f tools/$(DEPDIR)/check-selftest.Po -rm -f tools/$(DEPDIR)/ciptool.Po -rm -f tools/$(DEPDIR)/cltest.Po -rm -f tools/$(DEPDIR)/create-image.Po -rm -f tools/$(DEPDIR)/eddystone.Po -rm -f tools/$(DEPDIR)/gap-tester.Po -rm -f tools/$(DEPDIR)/gatt-service.Po -rm -f tools/$(DEPDIR)/hci-tester.Po -rm -f tools/$(DEPDIR)/hciattach.Po -rm -f tools/$(DEPDIR)/hciattach_ath3k.Po -rm -f tools/$(DEPDIR)/hciattach_bcm43xx.Po -rm -f tools/$(DEPDIR)/hciattach_intel.Po -rm -f tools/$(DEPDIR)/hciattach_qualcomm.Po -rm -f tools/$(DEPDIR)/hciattach_st.Po -rm -f tools/$(DEPDIR)/hciattach_ti.Po -rm -f tools/$(DEPDIR)/hciattach_tialt.Po -rm -f tools/$(DEPDIR)/hciconfig.Po -rm -f tools/$(DEPDIR)/hcidump.Po -rm -f tools/$(DEPDIR)/hcieventmask.Po -rm -f tools/$(DEPDIR)/hcisecfilter.Po -rm -f tools/$(DEPDIR)/hcitool.Po -rm -f tools/$(DEPDIR)/hex2hcd.Po -rm -f tools/$(DEPDIR)/hid2hci.Po -rm -f tools/$(DEPDIR)/hwdb.Po -rm -f tools/$(DEPDIR)/ibeacon.Po -rm -f tools/$(DEPDIR)/ioctl-tester.Po -rm -f tools/$(DEPDIR)/iso-tester.Po -rm -f tools/$(DEPDIR)/isotest.Po -rm -f tools/$(DEPDIR)/l2cap-tester.Po -rm -f tools/$(DEPDIR)/l2ping.Po -rm -f tools/$(DEPDIR)/l2test.Po -rm -f tools/$(DEPDIR)/mcaptest.Po -rm -f tools/$(DEPDIR)/mesh-cfgclient.Po -rm -f tools/$(DEPDIR)/mesh-cfgtest.Po -rm -f tools/$(DEPDIR)/mesh-tester.Po -rm -f tools/$(DEPDIR)/meshctl.Po -rm -f tools/$(DEPDIR)/mgmt-tester.Po -rm -f tools/$(DEPDIR)/mpris-proxy.Po -rm -f tools/$(DEPDIR)/nokfw.Po -rm -f tools/$(DEPDIR)/obex-client-tool.Po -rm -f tools/$(DEPDIR)/obex-server-tool.Po -rm -f tools/$(DEPDIR)/obexctl.Po -rm -f tools/$(DEPDIR)/oobtest.Po -rm -f tools/$(DEPDIR)/rctest.Po -rm -f tools/$(DEPDIR)/rfcomm-tester.Po -rm -f tools/$(DEPDIR)/rfcomm.Po -rm -f tools/$(DEPDIR)/rtlfw.Po -rm -f tools/$(DEPDIR)/sco-tester.Po -rm -f tools/$(DEPDIR)/scotest.Po -rm -f tools/$(DEPDIR)/sdptool.Po -rm -f tools/$(DEPDIR)/seq2bseq.Po -rm -f tools/$(DEPDIR)/smp-tester.Po -rm -f tools/$(DEPDIR)/test-runner.Po -rm -f tools/$(DEPDIR)/userchan-tester.Po -rm -f tools/mesh-gatt/$(DEPDIR)/config-client.Po -rm -f tools/mesh-gatt/$(DEPDIR)/config-server.Po -rm -f tools/mesh-gatt/$(DEPDIR)/crypto.Po -rm -f tools/mesh-gatt/$(DEPDIR)/gatt.Po -rm -f tools/mesh-gatt/$(DEPDIR)/net.Po -rm -f tools/mesh-gatt/$(DEPDIR)/node.Po -rm -f tools/mesh-gatt/$(DEPDIR)/onoff-model.Po -rm -f tools/mesh-gatt/$(DEPDIR)/prov-db.Po -rm -f tools/mesh-gatt/$(DEPDIR)/prov.Po -rm -f tools/mesh-gatt/$(DEPDIR)/util.Po -rm -f tools/mesh/$(DEPDIR)/agent.Po -rm -f tools/mesh/$(DEPDIR)/cfgcli.Po -rm -f tools/mesh/$(DEPDIR)/keys.Po -rm -f tools/mesh/$(DEPDIR)/mesh-db.Po -rm -f tools/mesh/$(DEPDIR)/remote.Po -rm -f tools/mesh/$(DEPDIR)/util.Po -rm -f tools/parser/$(DEPDIR)/att.Po -rm -f tools/parser/$(DEPDIR)/avctp.Po -rm -f tools/parser/$(DEPDIR)/avdtp.Po -rm -f tools/parser/$(DEPDIR)/avrcp.Po -rm -f tools/parser/$(DEPDIR)/bnep.Po -rm -f tools/parser/$(DEPDIR)/bpa.Po -rm -f tools/parser/$(DEPDIR)/capi.Po -rm -f tools/parser/$(DEPDIR)/cmtp.Po -rm -f tools/parser/$(DEPDIR)/csr.Po -rm -f tools/parser/$(DEPDIR)/ericsson.Po -rm -f tools/parser/$(DEPDIR)/hci.Po -rm -f tools/parser/$(DEPDIR)/hcrp.Po -rm -f tools/parser/$(DEPDIR)/hidp.Po -rm -f tools/parser/$(DEPDIR)/l2cap.Po -rm -f tools/parser/$(DEPDIR)/lmp.Po -rm -f tools/parser/$(DEPDIR)/obex.Po -rm -f tools/parser/$(DEPDIR)/parser.Po -rm -f tools/parser/$(DEPDIR)/ppp.Po -rm -f tools/parser/$(DEPDIR)/rfcomm.Po -rm -f tools/parser/$(DEPDIR)/sap.Po -rm -f tools/parser/$(DEPDIR)/sdp.Po -rm -f tools/parser/$(DEPDIR)/smp.Po -rm -f tools/parser/$(DEPDIR)/tcpip.Po -rm -f unit/$(DEPDIR)/test-avctp.Po -rm -f unit/$(DEPDIR)/test-avdtp.Po -rm -f unit/$(DEPDIR)/test-avrcp.Po -rm -f unit/$(DEPDIR)/test-bap.Po -rm -f unit/$(DEPDIR)/test-bass.Po -rm -f unit/$(DEPDIR)/test-crc.Po -rm -f unit/$(DEPDIR)/test-crypto.Po -rm -f unit/$(DEPDIR)/test-ecc.Po -rm -f unit/$(DEPDIR)/test-eir.Po -rm -f unit/$(DEPDIR)/test-gatt.Po -rm -f unit/$(DEPDIR)/test-gattrib.Po -rm -f unit/$(DEPDIR)/test-gdbus-client.Po -rm -f unit/$(DEPDIR)/test-gobex-apparam.Po -rm -f unit/$(DEPDIR)/test-gobex-header.Po -rm -f unit/$(DEPDIR)/test-gobex-packet.Po -rm -f unit/$(DEPDIR)/test-gobex-transfer.Po -rm -f unit/$(DEPDIR)/test-gobex.Po -rm -f unit/$(DEPDIR)/test-hfp.Po -rm -f unit/$(DEPDIR)/test-hog.Po -rm -f unit/$(DEPDIR)/test-lib.Po -rm -f unit/$(DEPDIR)/test-mgmt.Po -rm -f unit/$(DEPDIR)/test-micp.Po -rm -f unit/$(DEPDIR)/test-queue.Po -rm -f unit/$(DEPDIR)/test-ringbuf.Po -rm -f unit/$(DEPDIR)/test-sdp.Po -rm -f unit/$(DEPDIR)/test-tester.Po -rm -f unit/$(DEPDIR)/test-textfile.Po -rm -f unit/$(DEPDIR)/test-uhid.Po -rm -f unit/$(DEPDIR)/test-uuid.Po -rm -f unit/$(DEPDIR)/test-vcp.Po -rm -f unit/$(DEPDIR)/test_mesh_crypto-test-mesh-crypto.Po -rm -f unit/$(DEPDIR)/test_midi-test-midi.Po -rm -f unit/$(DEPDIR)/util.Po -rm -f Makefile distclean-am: clean-am distclean-compile distclean-generic \ distclean-hdr distclean-libtool distclean-tags dvi: dvi-am dvi-am: html: html-am html-am: info: info-am info-am: install-data-am: install-confDATA install-cupsPROGRAMS \ install-dbusDATA install-dbussessionbusDATA \ install-dbussystembusDATA install-dist_zshcompletionDATA \ install-man install-pkgconfigDATA install-pkgincludeHEADERS \ install-pluginLTLIBRARIES install-rulesDATA install-stateDATA \ install-systemdsystemunitDATA install-systemduserunitDATA \ install-testSCRIPTS install-udevPROGRAMS @$(NORMAL_INSTALL) $(MAKE) $(AM_MAKEFLAGS) install-data-hook install-dvi: install-dvi-am install-dvi-am: install-exec-am: install-binPROGRAMS install-libLTLIBRARIES \ install-pkglibexecPROGRAMS install-html: install-html-am install-html-am: install-info: install-info-am install-info-am: install-man: install-man1 install-man5 install-man7 install-man8 install-pdf: install-pdf-am install-pdf-am: install-ps: install-ps-am install-ps-am: installcheck-am: maintainer-clean: maintainer-clean-am -rm -f $(am__CONFIG_DISTCLEAN_FILES) -rm -rf $(top_srcdir)/autom4te.cache -rm -f android/$(DEPDIR)/a2dp-sink.Po -rm -f android/$(DEPDIR)/a2dp.Po -rm -f android/$(DEPDIR)/android_tester-tester-a2dp.Po -rm -f android/$(DEPDIR)/android_tester-tester-avrcp.Po -rm -f android/$(DEPDIR)/android_tester-tester-bluetooth.Po -rm -f android/$(DEPDIR)/android_tester-tester-gatt.Po -rm -f android/$(DEPDIR)/android_tester-tester-hdp.Po -rm -f android/$(DEPDIR)/android_tester-tester-hidhost.Po -rm -f android/$(DEPDIR)/android_tester-tester-main.Po -rm -f android/$(DEPDIR)/android_tester-tester-map-client.Po -rm -f android/$(DEPDIR)/android_tester-tester-pan.Po -rm -f android/$(DEPDIR)/android_tester-tester-socket.Po -rm -f android/$(DEPDIR)/audio_a2dp_default_la-hal-audio-aptx.Plo -rm -f android/$(DEPDIR)/audio_a2dp_default_la-hal-audio-sbc.Plo -rm -f android/$(DEPDIR)/audio_a2dp_default_la-hal-audio.Plo -rm -f android/$(DEPDIR)/audio_sco_default_la-hal-sco.Plo -rm -f android/$(DEPDIR)/avctp.Po -rm -f android/$(DEPDIR)/avdtp.Po -rm -f android/$(DEPDIR)/avdtptest-avdtp.Po -rm -f android/$(DEPDIR)/avdtptest-avdtptest.Po -rm -f android/$(DEPDIR)/avrcp-lib.Po -rm -f android/$(DEPDIR)/avrcp.Po -rm -f android/$(DEPDIR)/bluetooth.Po -rm -f android/$(DEPDIR)/bluetooth_default_la-hal-a2dp-sink.Plo -rm -f android/$(DEPDIR)/bluetooth_default_la-hal-a2dp.Plo -rm -f android/$(DEPDIR)/bluetooth_default_la-hal-avrcp-ctrl.Plo -rm -f android/$(DEPDIR)/bluetooth_default_la-hal-avrcp.Plo -rm -f android/$(DEPDIR)/bluetooth_default_la-hal-bluetooth.Plo -rm -f android/$(DEPDIR)/bluetooth_default_la-hal-gatt.Plo -rm -f android/$(DEPDIR)/bluetooth_default_la-hal-handsfree-client.Plo -rm -f android/$(DEPDIR)/bluetooth_default_la-hal-handsfree.Plo -rm -f android/$(DEPDIR)/bluetooth_default_la-hal-health.Plo -rm -f android/$(DEPDIR)/bluetooth_default_la-hal-hidhost.Plo -rm -f android/$(DEPDIR)/bluetooth_default_la-hal-ipc.Plo -rm -f android/$(DEPDIR)/bluetooth_default_la-hal-map-client.Plo -rm -f android/$(DEPDIR)/bluetooth_default_la-hal-pan.Plo -rm -f android/$(DEPDIR)/bluetooth_default_la-hal-socket.Plo -rm -f android/$(DEPDIR)/bluetooth_default_la-hal-utils.Plo -rm -f android/$(DEPDIR)/bluetoothd-snoop.Po -rm -f android/$(DEPDIR)/gatt.Po -rm -f android/$(DEPDIR)/haltest-hal-utils.Po -rm -f android/$(DEPDIR)/handsfree-client.Po -rm -f android/$(DEPDIR)/handsfree.Po -rm -f android/$(DEPDIR)/health.Po -rm -f android/$(DEPDIR)/hidhost.Po -rm -f android/$(DEPDIR)/ipc.Po -rm -f android/$(DEPDIR)/ipc_tester-hal-utils.Po -rm -f android/$(DEPDIR)/ipc_tester-ipc-tester.Po -rm -f android/$(DEPDIR)/main.Po -rm -f android/$(DEPDIR)/map-client.Po -rm -f android/$(DEPDIR)/pan.Po -rm -f android/$(DEPDIR)/sco.Po -rm -f android/$(DEPDIR)/socket.Po -rm -f android/$(DEPDIR)/system-emulator.Po -rm -f android/$(DEPDIR)/test-ipc.Po -rm -f android/audio_utils/$(DEPDIR)/audio_sco_default_la-resampler.Plo -rm -f android/client/$(DEPDIR)/haltest-haltest.Po -rm -f android/client/$(DEPDIR)/haltest-history.Po -rm -f android/client/$(DEPDIR)/haltest-if-audio.Po -rm -f android/client/$(DEPDIR)/haltest-if-av-sink.Po -rm -f android/client/$(DEPDIR)/haltest-if-av.Po -rm -f android/client/$(DEPDIR)/haltest-if-bt.Po -rm -f android/client/$(DEPDIR)/haltest-if-gatt.Po -rm -f android/client/$(DEPDIR)/haltest-if-hf-client.Po -rm -f android/client/$(DEPDIR)/haltest-if-hf.Po -rm -f android/client/$(DEPDIR)/haltest-if-hh.Po -rm -f android/client/$(DEPDIR)/haltest-if-hl.Po -rm -f android/client/$(DEPDIR)/haltest-if-mce.Po -rm -f android/client/$(DEPDIR)/haltest-if-pan.Po -rm -f android/client/$(DEPDIR)/haltest-if-rc-ctrl.Po -rm -f android/client/$(DEPDIR)/haltest-if-rc.Po -rm -f android/client/$(DEPDIR)/haltest-if-sco.Po -rm -f android/client/$(DEPDIR)/haltest-if-sock.Po -rm -f android/client/$(DEPDIR)/haltest-pollhandler.Po -rm -f android/client/$(DEPDIR)/haltest-tabcompletion.Po -rm -f android/client/$(DEPDIR)/haltest-terminal.Po -rm -f android/hardware/$(DEPDIR)/android_tester-hardware.Po -rm -f android/hardware/$(DEPDIR)/haltest-hardware.Po -rm -f attrib/$(DEPDIR)/att.Po -rm -f attrib/$(DEPDIR)/bluetoothd-att.Po -rm -f attrib/$(DEPDIR)/bluetoothd-gatt.Po -rm -f attrib/$(DEPDIR)/bluetoothd-gattrib.Po -rm -f attrib/$(DEPDIR)/gatt.Po -rm -f attrib/$(DEPDIR)/gattrib.Po -rm -f attrib/$(DEPDIR)/gatttool.Po -rm -f attrib/$(DEPDIR)/interactive.Po -rm -f attrib/$(DEPDIR)/utils.Po -rm -f btio/$(DEPDIR)/android_avdtptest-btio.Po -rm -f btio/$(DEPDIR)/bluetoothd-btio.Po -rm -f btio/$(DEPDIR)/btio.Po -rm -f btio/$(DEPDIR)/obexd-btio.Po -rm -f client/$(DEPDIR)/admin.Po -rm -f client/$(DEPDIR)/adv_monitor.Po -rm -f client/$(DEPDIR)/advertising.Po -rm -f client/$(DEPDIR)/agent.Po -rm -f client/$(DEPDIR)/assistant.Po -rm -f client/$(DEPDIR)/display.Po -rm -f client/$(DEPDIR)/gatt.Po -rm -f client/$(DEPDIR)/hci.Po -rm -f client/$(DEPDIR)/main.Po -rm -f client/$(DEPDIR)/mgmt.Po -rm -f client/$(DEPDIR)/player.Po -rm -f client/$(DEPDIR)/print.Po -rm -f ell/$(DEPDIR)/base64.Plo -rm -f ell/$(DEPDIR)/cert-crypto.Plo -rm -f ell/$(DEPDIR)/cert.Plo -rm -f ell/$(DEPDIR)/checksum.Plo -rm -f ell/$(DEPDIR)/cipher.Plo -rm -f ell/$(DEPDIR)/dbus-client.Plo -rm -f ell/$(DEPDIR)/dbus-filter.Plo -rm -f ell/$(DEPDIR)/dbus-message.Plo -rm -f ell/$(DEPDIR)/dbus-name-cache.Plo -rm -f ell/$(DEPDIR)/dbus-service.Plo -rm -f ell/$(DEPDIR)/dbus-util.Plo -rm -f ell/$(DEPDIR)/dbus.Plo -rm -f ell/$(DEPDIR)/ecc-external.Plo -rm -f ell/$(DEPDIR)/ecc.Plo -rm -f ell/$(DEPDIR)/ecdh.Plo -rm -f ell/$(DEPDIR)/gvariant-util.Plo -rm -f ell/$(DEPDIR)/hashmap.Plo -rm -f ell/$(DEPDIR)/idle.Plo -rm -f ell/$(DEPDIR)/io.Plo -rm -f ell/$(DEPDIR)/key.Plo -rm -f ell/$(DEPDIR)/log.Plo -rm -f ell/$(DEPDIR)/main.Plo -rm -f ell/$(DEPDIR)/pem.Plo -rm -f ell/$(DEPDIR)/queue.Plo -rm -f ell/$(DEPDIR)/random.Plo -rm -f ell/$(DEPDIR)/settings.Plo -rm -f ell/$(DEPDIR)/signal.Plo -rm -f ell/$(DEPDIR)/siphash.Plo -rm -f ell/$(DEPDIR)/string.Plo -rm -f ell/$(DEPDIR)/strv.Plo -rm -f ell/$(DEPDIR)/tester.Plo -rm -f ell/$(DEPDIR)/time.Plo -rm -f ell/$(DEPDIR)/timeout.Plo -rm -f ell/$(DEPDIR)/tls-extensions.Plo -rm -f ell/$(DEPDIR)/tls-record.Plo -rm -f ell/$(DEPDIR)/tls-suites.Plo -rm -f ell/$(DEPDIR)/tls.Plo -rm -f ell/$(DEPDIR)/utf8.Plo -rm -f ell/$(DEPDIR)/util.Plo -rm -f ell/$(DEPDIR)/uuid.Plo -rm -f emulator/$(DEPDIR)/android_android_tester-btdev.Po -rm -f emulator/$(DEPDIR)/android_android_tester-bthost.Po -rm -f emulator/$(DEPDIR)/android_android_tester-hciemu.Po -rm -f emulator/$(DEPDIR)/android_android_tester-smp.Po -rm -f emulator/$(DEPDIR)/android_android_tester-vhci.Po -rm -f emulator/$(DEPDIR)/android_ipc_tester-btdev.Po -rm -f emulator/$(DEPDIR)/android_ipc_tester-bthost.Po -rm -f emulator/$(DEPDIR)/android_ipc_tester-hciemu.Po -rm -f emulator/$(DEPDIR)/android_ipc_tester-smp.Po -rm -f emulator/$(DEPDIR)/android_ipc_tester-vhci.Po -rm -f emulator/$(DEPDIR)/b1ee.Po -rm -f emulator/$(DEPDIR)/btdev.Po -rm -f emulator/$(DEPDIR)/bthost.Po -rm -f emulator/$(DEPDIR)/hciemu.Po -rm -f emulator/$(DEPDIR)/hfp.Po -rm -f emulator/$(DEPDIR)/le.Po -rm -f emulator/$(DEPDIR)/main.Po -rm -f emulator/$(DEPDIR)/phy.Po -rm -f emulator/$(DEPDIR)/serial.Po -rm -f emulator/$(DEPDIR)/server.Po -rm -f emulator/$(DEPDIR)/smp.Po -rm -f emulator/$(DEPDIR)/vhci.Po -rm -f gdbus/$(DEPDIR)/client.Plo -rm -f gdbus/$(DEPDIR)/mainloop.Plo -rm -f gdbus/$(DEPDIR)/object.Plo -rm -f gdbus/$(DEPDIR)/polkit.Plo -rm -f gdbus/$(DEPDIR)/watch.Plo -rm -f gobex/$(DEPDIR)/gobex-apparam.Po -rm -f gobex/$(DEPDIR)/gobex-defs.Po -rm -f gobex/$(DEPDIR)/gobex-header.Po -rm -f gobex/$(DEPDIR)/gobex-packet.Po -rm -f gobex/$(DEPDIR)/gobex-transfer.Po -rm -f gobex/$(DEPDIR)/gobex.Po -rm -f gobex/$(DEPDIR)/obexd-gobex-apparam.Po -rm -f gobex/$(DEPDIR)/obexd-gobex-defs.Po -rm -f gobex/$(DEPDIR)/obexd-gobex-header.Po -rm -f gobex/$(DEPDIR)/obexd-gobex-packet.Po -rm -f gobex/$(DEPDIR)/obexd-gobex-transfer.Po -rm -f gobex/$(DEPDIR)/obexd-gobex.Po -rm -f lib/$(DEPDIR)/bluetooth.Plo -rm -f lib/$(DEPDIR)/hci.Plo -rm -f lib/$(DEPDIR)/sdp.Plo -rm -f lib/$(DEPDIR)/uuid.Plo -rm -f mesh/$(DEPDIR)/agent.Po -rm -f mesh/$(DEPDIR)/appkey.Po -rm -f mesh/$(DEPDIR)/cfgmod-server.Po -rm -f mesh/$(DEPDIR)/crypto.Po -rm -f mesh/$(DEPDIR)/dbus.Po -rm -f mesh/$(DEPDIR)/friend.Po -rm -f mesh/$(DEPDIR)/keyring.Po -rm -f mesh/$(DEPDIR)/main.Po -rm -f mesh/$(DEPDIR)/manager.Po -rm -f mesh/$(DEPDIR)/mesh-config-json.Po -rm -f mesh/$(DEPDIR)/mesh-io-generic.Po -rm -f mesh/$(DEPDIR)/mesh-io-mgmt.Po -rm -f mesh/$(DEPDIR)/mesh-io-unit.Po -rm -f mesh/$(DEPDIR)/mesh-io.Po -rm -f mesh/$(DEPDIR)/mesh-mgmt.Po -rm -f mesh/$(DEPDIR)/mesh.Po -rm -f mesh/$(DEPDIR)/model.Po -rm -f mesh/$(DEPDIR)/net-keys.Po -rm -f mesh/$(DEPDIR)/net.Po -rm -f mesh/$(DEPDIR)/node.Po -rm -f mesh/$(DEPDIR)/pb-adv.Po -rm -f mesh/$(DEPDIR)/prov-acceptor.Po -rm -f mesh/$(DEPDIR)/prov-initiator.Po -rm -f mesh/$(DEPDIR)/prvbeac-server.Po -rm -f mesh/$(DEPDIR)/remprv-server.Po -rm -f mesh/$(DEPDIR)/rpl.Po -rm -f mesh/$(DEPDIR)/util.Po -rm -f monitor/$(DEPDIR)/a2dp.Po -rm -f monitor/$(DEPDIR)/analyze.Po -rm -f monitor/$(DEPDIR)/att.Po -rm -f monitor/$(DEPDIR)/avctp.Po -rm -f monitor/$(DEPDIR)/avdtp.Po -rm -f monitor/$(DEPDIR)/bnep.Po -rm -f monitor/$(DEPDIR)/broadcom.Po -rm -f monitor/$(DEPDIR)/control.Po -rm -f monitor/$(DEPDIR)/crc.Po -rm -f monitor/$(DEPDIR)/display.Po -rm -f monitor/$(DEPDIR)/ellisys.Po -rm -f monitor/$(DEPDIR)/hcidump.Po -rm -f monitor/$(DEPDIR)/hwdb.Po -rm -f monitor/$(DEPDIR)/intel.Po -rm -f monitor/$(DEPDIR)/jlink.Po -rm -f monitor/$(DEPDIR)/keys.Po -rm -f monitor/$(DEPDIR)/l2cap.Po -rm -f monitor/$(DEPDIR)/ll.Po -rm -f monitor/$(DEPDIR)/lmp.Po -rm -f monitor/$(DEPDIR)/main.Po -rm -f monitor/$(DEPDIR)/msft.Po -rm -f monitor/$(DEPDIR)/packet.Po -rm -f monitor/$(DEPDIR)/rfcomm.Po -rm -f monitor/$(DEPDIR)/sdp.Po -rm -f monitor/$(DEPDIR)/vendor.Po -rm -f obexd/client/$(DEPDIR)/obexd-bip-common.Po -rm -f obexd/client/$(DEPDIR)/obexd-bip.Po -rm -f obexd/client/$(DEPDIR)/obexd-bluetooth.Po -rm -f obexd/client/$(DEPDIR)/obexd-driver.Po -rm -f obexd/client/$(DEPDIR)/obexd-ftp.Po -rm -f obexd/client/$(DEPDIR)/obexd-manager.Po -rm -f obexd/client/$(DEPDIR)/obexd-map-event.Po -rm -f obexd/client/$(DEPDIR)/obexd-map.Po -rm -f obexd/client/$(DEPDIR)/obexd-mns.Po -rm -f obexd/client/$(DEPDIR)/obexd-opp.Po -rm -f obexd/client/$(DEPDIR)/obexd-pbap.Po -rm -f obexd/client/$(DEPDIR)/obexd-session.Po -rm -f obexd/client/$(DEPDIR)/obexd-sync.Po -rm -f obexd/client/$(DEPDIR)/obexd-transfer.Po -rm -f obexd/client/$(DEPDIR)/obexd-transport.Po -rm -f obexd/plugins/$(DEPDIR)/obexd-bluetooth.Po -rm -f obexd/plugins/$(DEPDIR)/obexd-filesystem.Po -rm -f obexd/plugins/$(DEPDIR)/obexd-ftp.Po -rm -f obexd/plugins/$(DEPDIR)/obexd-irmc.Po -rm -f obexd/plugins/$(DEPDIR)/obexd-mas.Po -rm -f obexd/plugins/$(DEPDIR)/obexd-messages-dummy.Po -rm -f obexd/plugins/$(DEPDIR)/obexd-opp.Po -rm -f obexd/plugins/$(DEPDIR)/obexd-pbap.Po -rm -f obexd/plugins/$(DEPDIR)/obexd-pcsuite.Po -rm -f obexd/plugins/$(DEPDIR)/obexd-phonebook-@PLUGIN_PHONEBOOK@.Po -rm -f obexd/plugins/$(DEPDIR)/obexd-vcard.Po -rm -f obexd/src/$(DEPDIR)/obexd-log.Po -rm -f obexd/src/$(DEPDIR)/obexd-main.Po -rm -f obexd/src/$(DEPDIR)/obexd-manager.Po -rm -f obexd/src/$(DEPDIR)/obexd-mimetype.Po -rm -f obexd/src/$(DEPDIR)/obexd-obex.Po -rm -f obexd/src/$(DEPDIR)/obexd-plugin.Po -rm -f obexd/src/$(DEPDIR)/obexd-server.Po -rm -f obexd/src/$(DEPDIR)/obexd-service.Po -rm -f obexd/src/$(DEPDIR)/obexd-transport.Po -rm -f peripheral/$(DEPDIR)/attach.Po -rm -f peripheral/$(DEPDIR)/efivars.Po -rm -f peripheral/$(DEPDIR)/gap.Po -rm -f peripheral/$(DEPDIR)/gatt.Po -rm -f peripheral/$(DEPDIR)/log.Po -rm -f peripheral/$(DEPDIR)/main.Po -rm -f plugins/$(DEPDIR)/bluetoothd-admin.Po -rm -f plugins/$(DEPDIR)/bluetoothd-autopair.Po -rm -f plugins/$(DEPDIR)/bluetoothd-hostname.Po -rm -f plugins/$(DEPDIR)/bluetoothd-neard.Po -rm -f plugins/$(DEPDIR)/bluetoothd-policy.Po -rm -f plugins/$(DEPDIR)/bluetoothd-sixaxis.Po -rm -f profiles/audio/$(DEPDIR)/bluetoothd-a2dp.Po -rm -f profiles/audio/$(DEPDIR)/bluetoothd-asha.Po -rm -f profiles/audio/$(DEPDIR)/bluetoothd-avctp.Po -rm -f profiles/audio/$(DEPDIR)/bluetoothd-avdtp.Po -rm -f profiles/audio/$(DEPDIR)/bluetoothd-avrcp.Po -rm -f profiles/audio/$(DEPDIR)/bluetoothd-bap.Po -rm -f profiles/audio/$(DEPDIR)/bluetoothd-bass.Po -rm -f profiles/audio/$(DEPDIR)/bluetoothd-ccp.Po -rm -f profiles/audio/$(DEPDIR)/bluetoothd-control.Po -rm -f profiles/audio/$(DEPDIR)/bluetoothd-csip.Po -rm -f profiles/audio/$(DEPDIR)/bluetoothd-mcp.Po -rm -f profiles/audio/$(DEPDIR)/bluetoothd-media.Po -rm -f profiles/audio/$(DEPDIR)/bluetoothd-micp.Po -rm -f profiles/audio/$(DEPDIR)/bluetoothd-player.Po -rm -f profiles/audio/$(DEPDIR)/bluetoothd-sink.Po -rm -f profiles/audio/$(DEPDIR)/bluetoothd-source.Po -rm -f profiles/audio/$(DEPDIR)/bluetoothd-transport.Po -rm -f profiles/audio/$(DEPDIR)/bluetoothd-vcp.Po -rm -f profiles/battery/$(DEPDIR)/bas.Po -rm -f profiles/battery/$(DEPDIR)/bluetoothd-bas.Po -rm -f profiles/battery/$(DEPDIR)/bluetoothd-battery.Po -rm -f profiles/cups/$(DEPDIR)/hcrp.Po -rm -f profiles/cups/$(DEPDIR)/main.Po -rm -f profiles/cups/$(DEPDIR)/sdp.Po -rm -f profiles/cups/$(DEPDIR)/spp.Po -rm -f profiles/deviceinfo/$(DEPDIR)/bluetoothd-deviceinfo.Po -rm -f profiles/deviceinfo/$(DEPDIR)/bluetoothd-dis.Po -rm -f profiles/deviceinfo/$(DEPDIR)/dis.Po -rm -f profiles/gap/$(DEPDIR)/bluetoothd-gas.Po -rm -f profiles/health/$(DEPDIR)/bluetoothd-hdp.Po -rm -f profiles/health/$(DEPDIR)/bluetoothd-hdp_main.Po -rm -f profiles/health/$(DEPDIR)/bluetoothd-hdp_manager.Po -rm -f profiles/health/$(DEPDIR)/bluetoothd-hdp_util.Po -rm -f profiles/health/$(DEPDIR)/bluetoothd-mcap.Po -rm -f profiles/health/$(DEPDIR)/mcap.Po -rm -f profiles/iap/$(DEPDIR)/main.Po -rm -f profiles/input/$(DEPDIR)/bluetoothd-device.Po -rm -f profiles/input/$(DEPDIR)/bluetoothd-hog-lib.Po -rm -f profiles/input/$(DEPDIR)/bluetoothd-hog.Po -rm -f profiles/input/$(DEPDIR)/bluetoothd-manager.Po -rm -f profiles/input/$(DEPDIR)/bluetoothd-server.Po -rm -f profiles/input/$(DEPDIR)/bluetoothd-suspend-none.Po -rm -f profiles/input/$(DEPDIR)/hog-lib.Po -rm -f profiles/midi/$(DEPDIR)/bluetoothd-libmidi.Po -rm -f profiles/midi/$(DEPDIR)/bluetoothd-midi.Po -rm -f profiles/midi/$(DEPDIR)/unit_test_midi-libmidi.Po -rm -f profiles/network/$(DEPDIR)/bluetoothd-bnep.Po -rm -f profiles/network/$(DEPDIR)/bluetoothd-connection.Po -rm -f profiles/network/$(DEPDIR)/bluetoothd-manager.Po -rm -f profiles/network/$(DEPDIR)/bluetoothd-server.Po -rm -f profiles/network/$(DEPDIR)/bnep.Po -rm -f profiles/sap/$(DEPDIR)/bluetoothd-main.Po -rm -f profiles/sap/$(DEPDIR)/bluetoothd-manager.Po -rm -f profiles/sap/$(DEPDIR)/bluetoothd-sap-dummy.Po -rm -f profiles/sap/$(DEPDIR)/bluetoothd-server.Po -rm -f profiles/scanparam/$(DEPDIR)/bluetoothd-scan.Po -rm -f profiles/scanparam/$(DEPDIR)/bluetoothd-scpp.Po -rm -f profiles/scanparam/$(DEPDIR)/scpp.Po -rm -f src/$(DEPDIR)/android_avdtptest-log.Po -rm -f src/$(DEPDIR)/bluetoothd-adapter.Po -rm -f src/$(DEPDIR)/bluetoothd-adv_monitor.Po -rm -f src/$(DEPDIR)/bluetoothd-advertising.Po -rm -f src/$(DEPDIR)/bluetoothd-agent.Po -rm -f src/$(DEPDIR)/bluetoothd-backtrace.Po -rm -f src/$(DEPDIR)/bluetoothd-battery.Po -rm -f src/$(DEPDIR)/bluetoothd-dbus-common.Po -rm -f src/$(DEPDIR)/bluetoothd-device.Po -rm -f src/$(DEPDIR)/bluetoothd-eir.Po -rm -f src/$(DEPDIR)/bluetoothd-error.Po -rm -f src/$(DEPDIR)/bluetoothd-gatt-client.Po -rm -f src/$(DEPDIR)/bluetoothd-gatt-database.Po -rm -f src/$(DEPDIR)/bluetoothd-log.Po -rm -f src/$(DEPDIR)/bluetoothd-main.Po -rm -f src/$(DEPDIR)/bluetoothd-plugin.Po -rm -f src/$(DEPDIR)/bluetoothd-profile.Po -rm -f src/$(DEPDIR)/bluetoothd-rfkill.Po -rm -f src/$(DEPDIR)/bluetoothd-sdp-client.Po -rm -f src/$(DEPDIR)/bluetoothd-sdp-xml.Po -rm -f src/$(DEPDIR)/bluetoothd-sdpd-database.Po -rm -f src/$(DEPDIR)/bluetoothd-sdpd-request.Po -rm -f src/$(DEPDIR)/bluetoothd-sdpd-server.Po -rm -f src/$(DEPDIR)/bluetoothd-sdpd-service.Po -rm -f src/$(DEPDIR)/bluetoothd-service.Po -rm -f src/$(DEPDIR)/bluetoothd-set.Po -rm -f src/$(DEPDIR)/bluetoothd-settings.Po -rm -f src/$(DEPDIR)/bluetoothd-storage.Po -rm -f src/$(DEPDIR)/bluetoothd-textfile.Po -rm -f src/$(DEPDIR)/bluetoothd-uuid-helper.Po -rm -f src/$(DEPDIR)/eir.Po -rm -f src/$(DEPDIR)/log.Po -rm -f src/$(DEPDIR)/oui.Po -rm -f src/$(DEPDIR)/sdp-client.Po -rm -f src/$(DEPDIR)/sdp-xml.Po -rm -f src/$(DEPDIR)/sdpd-database.Po -rm -f src/$(DEPDIR)/sdpd-request.Po -rm -f src/$(DEPDIR)/sdpd-server.Po -rm -f src/$(DEPDIR)/sdpd-service.Po -rm -f src/$(DEPDIR)/settings.Po -rm -f src/$(DEPDIR)/textfile.Po -rm -f src/$(DEPDIR)/uuid-helper.Po -rm -f src/shared/$(DEPDIR)/android_avdtptest-log.Po -rm -f src/shared/$(DEPDIR)/android_avdtptest-queue.Po -rm -f src/shared/$(DEPDIR)/android_avdtptest-util.Po -rm -f src/shared/$(DEPDIR)/btp.Po -rm -f src/shared/$(DEPDIR)/libshared_ell_la-ad.Plo -rm -f src/shared/$(DEPDIR)/libshared_ell_la-asha.Plo -rm -f src/shared/$(DEPDIR)/libshared_ell_la-att.Plo -rm -f src/shared/$(DEPDIR)/libshared_ell_la-bap-debug.Plo -rm -f src/shared/$(DEPDIR)/libshared_ell_la-bap.Plo -rm -f src/shared/$(DEPDIR)/libshared_ell_la-bass.Plo -rm -f src/shared/$(DEPDIR)/libshared_ell_la-btsnoop.Plo -rm -f src/shared/$(DEPDIR)/libshared_ell_la-ccp.Plo -rm -f src/shared/$(DEPDIR)/libshared_ell_la-crypto.Plo -rm -f src/shared/$(DEPDIR)/libshared_ell_la-csip.Plo -rm -f src/shared/$(DEPDIR)/libshared_ell_la-ecc.Plo -rm -f src/shared/$(DEPDIR)/libshared_ell_la-gap.Plo -rm -f src/shared/$(DEPDIR)/libshared_ell_la-gatt-client.Plo -rm -f src/shared/$(DEPDIR)/libshared_ell_la-gatt-db.Plo -rm -f src/shared/$(DEPDIR)/libshared_ell_la-gatt-helpers.Plo -rm -f src/shared/$(DEPDIR)/libshared_ell_la-gatt-server.Plo -rm -f src/shared/$(DEPDIR)/libshared_ell_la-hci-crypto.Plo -rm -f src/shared/$(DEPDIR)/libshared_ell_la-hci.Plo -rm -f src/shared/$(DEPDIR)/libshared_ell_la-hfp.Plo -rm -f src/shared/$(DEPDIR)/libshared_ell_la-io-ell.Plo -rm -f src/shared/$(DEPDIR)/libshared_ell_la-log.Plo -rm -f src/shared/$(DEPDIR)/libshared_ell_la-mainloop-ell.Plo -rm -f src/shared/$(DEPDIR)/libshared_ell_la-mcp.Plo -rm -f src/shared/$(DEPDIR)/libshared_ell_la-mgmt.Plo -rm -f src/shared/$(DEPDIR)/libshared_ell_la-micp.Plo -rm -f src/shared/$(DEPDIR)/libshared_ell_la-pcap.Plo -rm -f src/shared/$(DEPDIR)/libshared_ell_la-queue.Plo -rm -f src/shared/$(DEPDIR)/libshared_ell_la-ringbuf.Plo -rm -f src/shared/$(DEPDIR)/libshared_ell_la-shell.Plo -rm -f src/shared/$(DEPDIR)/libshared_ell_la-timeout-ell.Plo -rm -f src/shared/$(DEPDIR)/libshared_ell_la-uhid.Plo -rm -f src/shared/$(DEPDIR)/libshared_ell_la-util.Plo -rm -f src/shared/$(DEPDIR)/libshared_ell_la-vcp.Plo -rm -f src/shared/$(DEPDIR)/libshared_glib_la-ad.Plo -rm -f src/shared/$(DEPDIR)/libshared_glib_la-asha.Plo -rm -f src/shared/$(DEPDIR)/libshared_glib_la-att.Plo -rm -f src/shared/$(DEPDIR)/libshared_glib_la-bap-debug.Plo -rm -f src/shared/$(DEPDIR)/libshared_glib_la-bap.Plo -rm -f src/shared/$(DEPDIR)/libshared_glib_la-bass.Plo -rm -f src/shared/$(DEPDIR)/libshared_glib_la-btsnoop.Plo -rm -f src/shared/$(DEPDIR)/libshared_glib_la-ccp.Plo -rm -f src/shared/$(DEPDIR)/libshared_glib_la-crypto.Plo -rm -f src/shared/$(DEPDIR)/libshared_glib_la-csip.Plo -rm -f src/shared/$(DEPDIR)/libshared_glib_la-ecc.Plo -rm -f src/shared/$(DEPDIR)/libshared_glib_la-gap.Plo -rm -f src/shared/$(DEPDIR)/libshared_glib_la-gatt-client.Plo -rm -f src/shared/$(DEPDIR)/libshared_glib_la-gatt-db.Plo -rm -f src/shared/$(DEPDIR)/libshared_glib_la-gatt-helpers.Plo -rm -f src/shared/$(DEPDIR)/libshared_glib_la-gatt-server.Plo -rm -f src/shared/$(DEPDIR)/libshared_glib_la-hci-crypto.Plo -rm -f src/shared/$(DEPDIR)/libshared_glib_la-hci.Plo -rm -f src/shared/$(DEPDIR)/libshared_glib_la-hfp.Plo -rm -f src/shared/$(DEPDIR)/libshared_glib_la-io-glib.Plo -rm -f src/shared/$(DEPDIR)/libshared_glib_la-log.Plo -rm -f src/shared/$(DEPDIR)/libshared_glib_la-mainloop-glib.Plo -rm -f src/shared/$(DEPDIR)/libshared_glib_la-mainloop-notify.Plo -rm -f src/shared/$(DEPDIR)/libshared_glib_la-mcp.Plo -rm -f src/shared/$(DEPDIR)/libshared_glib_la-mgmt.Plo -rm -f src/shared/$(DEPDIR)/libshared_glib_la-micp.Plo -rm -f src/shared/$(DEPDIR)/libshared_glib_la-pcap.Plo -rm -f src/shared/$(DEPDIR)/libshared_glib_la-queue.Plo -rm -f src/shared/$(DEPDIR)/libshared_glib_la-ringbuf.Plo -rm -f src/shared/$(DEPDIR)/libshared_glib_la-shell.Plo -rm -f src/shared/$(DEPDIR)/libshared_glib_la-tester.Plo -rm -f src/shared/$(DEPDIR)/libshared_glib_la-timeout-glib.Plo -rm -f src/shared/$(DEPDIR)/libshared_glib_la-uhid.Plo -rm -f src/shared/$(DEPDIR)/libshared_glib_la-util.Plo -rm -f src/shared/$(DEPDIR)/libshared_glib_la-vcp.Plo -rm -f src/shared/$(DEPDIR)/libshared_mainloop_la-ad.Plo -rm -f src/shared/$(DEPDIR)/libshared_mainloop_la-asha.Plo -rm -f src/shared/$(DEPDIR)/libshared_mainloop_la-att.Plo -rm -f src/shared/$(DEPDIR)/libshared_mainloop_la-bap-debug.Plo -rm -f src/shared/$(DEPDIR)/libshared_mainloop_la-bap.Plo -rm -f src/shared/$(DEPDIR)/libshared_mainloop_la-bass.Plo -rm -f src/shared/$(DEPDIR)/libshared_mainloop_la-btsnoop.Plo -rm -f src/shared/$(DEPDIR)/libshared_mainloop_la-ccp.Plo -rm -f src/shared/$(DEPDIR)/libshared_mainloop_la-crypto.Plo -rm -f src/shared/$(DEPDIR)/libshared_mainloop_la-csip.Plo -rm -f src/shared/$(DEPDIR)/libshared_mainloop_la-ecc.Plo -rm -f src/shared/$(DEPDIR)/libshared_mainloop_la-gap.Plo -rm -f src/shared/$(DEPDIR)/libshared_mainloop_la-gatt-client.Plo -rm -f src/shared/$(DEPDIR)/libshared_mainloop_la-gatt-db.Plo -rm -f src/shared/$(DEPDIR)/libshared_mainloop_la-gatt-helpers.Plo -rm -f src/shared/$(DEPDIR)/libshared_mainloop_la-gatt-server.Plo -rm -f src/shared/$(DEPDIR)/libshared_mainloop_la-hci-crypto.Plo -rm -f src/shared/$(DEPDIR)/libshared_mainloop_la-hci.Plo -rm -f src/shared/$(DEPDIR)/libshared_mainloop_la-hfp.Plo -rm -f src/shared/$(DEPDIR)/libshared_mainloop_la-io-mainloop.Plo -rm -f src/shared/$(DEPDIR)/libshared_mainloop_la-log.Plo -rm -f src/shared/$(DEPDIR)/libshared_mainloop_la-mainloop-notify.Plo -rm -f src/shared/$(DEPDIR)/libshared_mainloop_la-mainloop.Plo -rm -f src/shared/$(DEPDIR)/libshared_mainloop_la-mcp.Plo -rm -f src/shared/$(DEPDIR)/libshared_mainloop_la-mgmt.Plo -rm -f src/shared/$(DEPDIR)/libshared_mainloop_la-micp.Plo -rm -f src/shared/$(DEPDIR)/libshared_mainloop_la-pcap.Plo -rm -f src/shared/$(DEPDIR)/libshared_mainloop_la-queue.Plo -rm -f src/shared/$(DEPDIR)/libshared_mainloop_la-ringbuf.Plo -rm -f src/shared/$(DEPDIR)/libshared_mainloop_la-shell.Plo -rm -f src/shared/$(DEPDIR)/libshared_mainloop_la-timeout-mainloop.Plo -rm -f src/shared/$(DEPDIR)/libshared_mainloop_la-uhid.Plo -rm -f src/shared/$(DEPDIR)/libshared_mainloop_la-util.Plo -rm -f src/shared/$(DEPDIR)/libshared_mainloop_la-vcp.Plo -rm -f tools/$(DEPDIR)/3dsp.Po -rm -f tools/$(DEPDIR)/advtest.Po -rm -f tools/$(DEPDIR)/avinfo.Po -rm -f tools/$(DEPDIR)/avtest.Po -rm -f tools/$(DEPDIR)/bcmfw.Po -rm -f tools/$(DEPDIR)/bdaddr.Po -rm -f tools/$(DEPDIR)/bluemoon.Po -rm -f tools/$(DEPDIR)/bluetooth-player.Po -rm -f tools/$(DEPDIR)/bnep-tester.Po -rm -f tools/$(DEPDIR)/bneptest.Po -rm -f tools/$(DEPDIR)/btattach.Po -rm -f tools/$(DEPDIR)/btconfig.Po -rm -f tools/$(DEPDIR)/btgatt-client.Po -rm -f tools/$(DEPDIR)/btgatt-server.Po -rm -f tools/$(DEPDIR)/btinfo.Po -rm -f tools/$(DEPDIR)/btiotest.Po -rm -f tools/$(DEPDIR)/btmgmt.Po -rm -f tools/$(DEPDIR)/btmon-logger.Po -rm -f tools/$(DEPDIR)/btpclient.Po -rm -f tools/$(DEPDIR)/btpclientctl.Po -rm -f tools/$(DEPDIR)/btproxy.Po -rm -f tools/$(DEPDIR)/btsnoop.Po -rm -f tools/$(DEPDIR)/check-selftest.Po -rm -f tools/$(DEPDIR)/ciptool.Po -rm -f tools/$(DEPDIR)/cltest.Po -rm -f tools/$(DEPDIR)/create-image.Po -rm -f tools/$(DEPDIR)/eddystone.Po -rm -f tools/$(DEPDIR)/gap-tester.Po -rm -f tools/$(DEPDIR)/gatt-service.Po -rm -f tools/$(DEPDIR)/hci-tester.Po -rm -f tools/$(DEPDIR)/hciattach.Po -rm -f tools/$(DEPDIR)/hciattach_ath3k.Po -rm -f tools/$(DEPDIR)/hciattach_bcm43xx.Po -rm -f tools/$(DEPDIR)/hciattach_intel.Po -rm -f tools/$(DEPDIR)/hciattach_qualcomm.Po -rm -f tools/$(DEPDIR)/hciattach_st.Po -rm -f tools/$(DEPDIR)/hciattach_ti.Po -rm -f tools/$(DEPDIR)/hciattach_tialt.Po -rm -f tools/$(DEPDIR)/hciconfig.Po -rm -f tools/$(DEPDIR)/hcidump.Po -rm -f tools/$(DEPDIR)/hcieventmask.Po -rm -f tools/$(DEPDIR)/hcisecfilter.Po -rm -f tools/$(DEPDIR)/hcitool.Po -rm -f tools/$(DEPDIR)/hex2hcd.Po -rm -f tools/$(DEPDIR)/hid2hci.Po -rm -f tools/$(DEPDIR)/hwdb.Po -rm -f tools/$(DEPDIR)/ibeacon.Po -rm -f tools/$(DEPDIR)/ioctl-tester.Po -rm -f tools/$(DEPDIR)/iso-tester.Po -rm -f tools/$(DEPDIR)/isotest.Po -rm -f tools/$(DEPDIR)/l2cap-tester.Po -rm -f tools/$(DEPDIR)/l2ping.Po -rm -f tools/$(DEPDIR)/l2test.Po -rm -f tools/$(DEPDIR)/mcaptest.Po -rm -f tools/$(DEPDIR)/mesh-cfgclient.Po -rm -f tools/$(DEPDIR)/mesh-cfgtest.Po -rm -f tools/$(DEPDIR)/mesh-tester.Po -rm -f tools/$(DEPDIR)/meshctl.Po -rm -f tools/$(DEPDIR)/mgmt-tester.Po -rm -f tools/$(DEPDIR)/mpris-proxy.Po -rm -f tools/$(DEPDIR)/nokfw.Po -rm -f tools/$(DEPDIR)/obex-client-tool.Po -rm -f tools/$(DEPDIR)/obex-server-tool.Po -rm -f tools/$(DEPDIR)/obexctl.Po -rm -f tools/$(DEPDIR)/oobtest.Po -rm -f tools/$(DEPDIR)/rctest.Po -rm -f tools/$(DEPDIR)/rfcomm-tester.Po -rm -f tools/$(DEPDIR)/rfcomm.Po -rm -f tools/$(DEPDIR)/rtlfw.Po -rm -f tools/$(DEPDIR)/sco-tester.Po -rm -f tools/$(DEPDIR)/scotest.Po -rm -f tools/$(DEPDIR)/sdptool.Po -rm -f tools/$(DEPDIR)/seq2bseq.Po -rm -f tools/$(DEPDIR)/smp-tester.Po -rm -f tools/$(DEPDIR)/test-runner.Po -rm -f tools/$(DEPDIR)/userchan-tester.Po -rm -f tools/mesh-gatt/$(DEPDIR)/config-client.Po -rm -f tools/mesh-gatt/$(DEPDIR)/config-server.Po -rm -f tools/mesh-gatt/$(DEPDIR)/crypto.Po -rm -f tools/mesh-gatt/$(DEPDIR)/gatt.Po -rm -f tools/mesh-gatt/$(DEPDIR)/net.Po -rm -f tools/mesh-gatt/$(DEPDIR)/node.Po -rm -f tools/mesh-gatt/$(DEPDIR)/onoff-model.Po -rm -f tools/mesh-gatt/$(DEPDIR)/prov-db.Po -rm -f tools/mesh-gatt/$(DEPDIR)/prov.Po -rm -f tools/mesh-gatt/$(DEPDIR)/util.Po -rm -f tools/mesh/$(DEPDIR)/agent.Po -rm -f tools/mesh/$(DEPDIR)/cfgcli.Po -rm -f tools/mesh/$(DEPDIR)/keys.Po -rm -f tools/mesh/$(DEPDIR)/mesh-db.Po -rm -f tools/mesh/$(DEPDIR)/remote.Po -rm -f tools/mesh/$(DEPDIR)/util.Po -rm -f tools/parser/$(DEPDIR)/att.Po -rm -f tools/parser/$(DEPDIR)/avctp.Po -rm -f tools/parser/$(DEPDIR)/avdtp.Po -rm -f tools/parser/$(DEPDIR)/avrcp.Po -rm -f tools/parser/$(DEPDIR)/bnep.Po -rm -f tools/parser/$(DEPDIR)/bpa.Po -rm -f tools/parser/$(DEPDIR)/capi.Po -rm -f tools/parser/$(DEPDIR)/cmtp.Po -rm -f tools/parser/$(DEPDIR)/csr.Po -rm -f tools/parser/$(DEPDIR)/ericsson.Po -rm -f tools/parser/$(DEPDIR)/hci.Po -rm -f tools/parser/$(DEPDIR)/hcrp.Po -rm -f tools/parser/$(DEPDIR)/hidp.Po -rm -f tools/parser/$(DEPDIR)/l2cap.Po -rm -f tools/parser/$(DEPDIR)/lmp.Po -rm -f tools/parser/$(DEPDIR)/obex.Po -rm -f tools/parser/$(DEPDIR)/parser.Po -rm -f tools/parser/$(DEPDIR)/ppp.Po -rm -f tools/parser/$(DEPDIR)/rfcomm.Po -rm -f tools/parser/$(DEPDIR)/sap.Po -rm -f tools/parser/$(DEPDIR)/sdp.Po -rm -f tools/parser/$(DEPDIR)/smp.Po -rm -f tools/parser/$(DEPDIR)/tcpip.Po -rm -f unit/$(DEPDIR)/test-avctp.Po -rm -f unit/$(DEPDIR)/test-avdtp.Po -rm -f unit/$(DEPDIR)/test-avrcp.Po -rm -f unit/$(DEPDIR)/test-bap.Po -rm -f unit/$(DEPDIR)/test-bass.Po -rm -f unit/$(DEPDIR)/test-crc.Po -rm -f unit/$(DEPDIR)/test-crypto.Po -rm -f unit/$(DEPDIR)/test-ecc.Po -rm -f unit/$(DEPDIR)/test-eir.Po -rm -f unit/$(DEPDIR)/test-gatt.Po -rm -f unit/$(DEPDIR)/test-gattrib.Po -rm -f unit/$(DEPDIR)/test-gdbus-client.Po -rm -f unit/$(DEPDIR)/test-gobex-apparam.Po -rm -f unit/$(DEPDIR)/test-gobex-header.Po -rm -f unit/$(DEPDIR)/test-gobex-packet.Po -rm -f unit/$(DEPDIR)/test-gobex-transfer.Po -rm -f unit/$(DEPDIR)/test-gobex.Po -rm -f unit/$(DEPDIR)/test-hfp.Po -rm -f unit/$(DEPDIR)/test-hog.Po -rm -f unit/$(DEPDIR)/test-lib.Po -rm -f unit/$(DEPDIR)/test-mgmt.Po -rm -f unit/$(DEPDIR)/test-micp.Po -rm -f unit/$(DEPDIR)/test-queue.Po -rm -f unit/$(DEPDIR)/test-ringbuf.Po -rm -f unit/$(DEPDIR)/test-sdp.Po -rm -f unit/$(DEPDIR)/test-tester.Po -rm -f unit/$(DEPDIR)/test-textfile.Po -rm -f unit/$(DEPDIR)/test-uhid.Po -rm -f unit/$(DEPDIR)/test-uuid.Po -rm -f unit/$(DEPDIR)/test-vcp.Po -rm -f unit/$(DEPDIR)/test_mesh_crypto-test-mesh-crypto.Po -rm -f unit/$(DEPDIR)/test_midi-test-midi.Po -rm -f unit/$(DEPDIR)/util.Po -rm -f Makefile maintainer-clean-am: distclean-am maintainer-clean-generic \ maintainer-clean-local mostlyclean: mostlyclean-am mostlyclean-am: mostlyclean-compile mostlyclean-generic \ mostlyclean-libtool pdf: pdf-am pdf-am: ps: ps-am ps-am: uninstall-am: uninstall-binPROGRAMS uninstall-confDATA \ uninstall-cupsPROGRAMS uninstall-dbusDATA \ uninstall-dbussessionbusDATA uninstall-dbussystembusDATA \ uninstall-dist_zshcompletionDATA uninstall-libLTLIBRARIES \ uninstall-man uninstall-pkgconfigDATA \ uninstall-pkgincludeHEADERS uninstall-pkglibexecPROGRAMS \ uninstall-pluginLTLIBRARIES uninstall-rulesDATA \ uninstall-stateDATA uninstall-systemdsystemunitDATA \ uninstall-systemduserunitDATA uninstall-testSCRIPTS \ uninstall-udevPROGRAMS @$(NORMAL_INSTALL) $(MAKE) $(AM_MAKEFLAGS) uninstall-hook uninstall-man: uninstall-man1 uninstall-man5 uninstall-man7 \ uninstall-man8 .MAKE: all check check-am install install-am install-data-am \ install-exec install-strip uninstall-am .PHONY: CTAGS GTAGS TAGS all all-am am--depfiles am--refresh check \ check-TESTS check-am clean clean-binPROGRAMS clean-cscope \ clean-cupsPROGRAMS clean-generic clean-libLTLIBRARIES \ clean-libtool clean-local clean-noinstLIBRARIES \ clean-noinstLTLIBRARIES clean-noinstPROGRAMS \ clean-pkglibexecPROGRAMS clean-pluginLTLIBRARIES \ clean-udevPROGRAMS cscope cscopelist-am ctags ctags-am dist \ dist-all dist-bzip2 dist-gzip dist-lzip dist-shar dist-tarZ \ dist-xz dist-zip dist-zstd distcheck distclean \ distclean-compile distclean-generic distclean-hdr \ distclean-libtool distclean-tags distcleancheck distdir \ distuninstallcheck dvi dvi-am html html-am info info-am \ install install-am install-binPROGRAMS install-confDATA \ install-cupsPROGRAMS install-data install-data-am \ install-data-hook install-dbusDATA install-dbussessionbusDATA \ install-dbussystembusDATA install-dist_zshcompletionDATA \ install-dvi install-dvi-am install-exec install-exec-am \ install-html install-html-am install-info install-info-am \ install-libLTLIBRARIES install-man install-man1 install-man5 \ install-man7 install-man8 install-pdf install-pdf-am \ install-pkgconfigDATA install-pkgincludeHEADERS \ install-pkglibexecPROGRAMS install-pluginLTLIBRARIES \ install-ps install-ps-am install-rulesDATA install-stateDATA \ install-strip install-systemdsystemunitDATA \ install-systemduserunitDATA install-testSCRIPTS \ install-udevPROGRAMS installcheck installcheck-am installdirs \ maintainer-clean maintainer-clean-generic \ maintainer-clean-local mostlyclean mostlyclean-compile \ mostlyclean-generic mostlyclean-libtool pdf pdf-am ps ps-am \ recheck tags tags-am uninstall uninstall-am \ uninstall-binPROGRAMS uninstall-confDATA \ uninstall-cupsPROGRAMS uninstall-dbusDATA \ uninstall-dbussessionbusDATA uninstall-dbussystembusDATA \ uninstall-dist_zshcompletionDATA uninstall-hook \ uninstall-libLTLIBRARIES uninstall-man uninstall-man1 \ uninstall-man5 uninstall-man7 uninstall-man8 \ uninstall-pkgconfigDATA uninstall-pkgincludeHEADERS \ uninstall-pkglibexecPROGRAMS uninstall-pluginLTLIBRARIES \ uninstall-rulesDATA uninstall-stateDATA \ uninstall-systemdsystemunitDATA uninstall-systemduserunitDATA \ uninstall-testSCRIPTS uninstall-udevPROGRAMS .PRECIOUS: Makefile bluetoothd-fix-permissions: install -dm755 $(DESTDIR)$(confdir) install -dm700 $(DESTDIR)$(statedir) @BTPCLIENT_TRUE@tools/btpclient.$(OBJEXT): src/libshared-ell.la ell/internal @OBEX_TRUE@@SYSTEMD_TRUE@obexd-add-service-symlink: @OBEX_TRUE@@SYSTEMD_TRUE@ $(LN_S) -f obex.service $(DESTDIR)$(SYSTEMD_USERUNITDIR)/dbus-org.bluez.obex.service @OBEX_TRUE@@SYSTEMD_TRUE@obexd-remove-service-symlink: @OBEX_TRUE@@SYSTEMD_TRUE@ rm -f $(DESTDIR)$(SYSTEMD_USERUNITDIR)/dbus-org.bluez.obex.service @OBEX_TRUE@@SYSTEMD_FALSE@obexd-add-service-symlink: @OBEX_TRUE@@SYSTEMD_FALSE@obexd-remove-service-symlink: @OBEX_FALSE@obexd-add-service-symlink: @OBEX_FALSE@obexd-remove-service-symlink: obexd/src/plugin.$(OBJEXT): obexd/src/builtin.h obexd/src/builtin.h: obexd/src/genbuiltin $(obexd_builtin_sources) $(AM_V_at)$(MKDIR_P) $(dir $@) $(AM_V_GEN)$(srcdir)/obexd/src/genbuiltin $(obexd_builtin_modules) > $@ @MESH_TRUE@mesh/mesh.$(OBJEXT): ell/internal @MESH_TRUE@mesh/main.$(OBJEXT): src/builtin.h lib/bluetooth/bluetooth.h @SYSTEMD_TRUE@install-data-hook: obexd-add-service-symlink @SYSTEMD_FALSE@install-data-hook: bluetoothd-fix-permissions obexd-add-service-symlink uninstall-hook: obexd-remove-service-symlink %.1: %.rst Makefile $(RST2MAN_PROCESS) %.5: %.rst Makefile $(RST2MAN_PROCESS) %.7: %.rst Makefile $(RST2MAN_PROCESS) %.8: %.rst Makefile $(RST2MAN_PROCESS) src/builtin.h: src/genbuiltin $(builtin_sources) $(AM_V_GEN)$(srcdir)/src/genbuiltin $(builtin_modules) > $@ tools/%.rules: $(AM_V_at)$(MKDIR_P) tools $(AM_V_GEN)cp $(srcdir)/$(subst 97-,,$@) $@ $(lib_libbluetooth_la_OBJECTS): $(local_headers) lib/bluetooth/%.h: lib/%.h $(AM_V_at)$(MKDIR_P) lib/bluetooth $(AM_V_GEN)$(LN_S) -f $(abspath $<) $@ ell/shared: Makefile $(AM_V_at)$(MKDIR_P) ell $(AM_V_GEN)for f in $(ell_shared) ; do \ if [ ! -f $$f ] ; then \ $(LN_S) -t ell -f $(abs_srcdir)/../ell/$$f ; \ fi \ done > $@ ell/internal: Makefile $(AM_V_at)$(MKDIR_P) ell $(AM_V_GEN)for f in $(ell_headers) $(ell_sources) ; do \ if [ ! -f $$f ] ; then \ $(LN_S) -t ell -f $(abs_srcdir)/../ell/$$f ; \ fi \ done > $@ ell/ell.h: Makefile $(AM_V_at)echo -n > $@ $(AM_V_GEN)for f in $(ell_headers) ; do \ echo "#include <$$f>" >> $@ ; \ done maintainer-clean-local: -rm -rf ell clean-coverage: @COVERAGE_TRUE@ @lcov --directory $(top_builddir) --zerocounters @COVERAGE_TRUE@ $(RM) -r coverage $(top_builddir)/coverage.info @COVERAGE_TRUE@coverage: check @COVERAGE_TRUE@ @lcov --compat-libtool --directory $(top_builddir) --capture \ @COVERAGE_TRUE@ --output-file $(top_builddir)/coverage.info @COVERAGE_TRUE@ $(AM_V_at)$(MKDIR_P) coverage @COVERAGE_TRUE@ @genhtml -o coverage/ $(top_builddir)/coverage.info clean-local: clean-coverage -find $(top_builddir) -name "*.gcno" -delete -find $(top_builddir) -name "*.gcda" -delete $(RM) -r lib/bluetooth # Tell versions [3.59,3.63) of GNU make to not export all variables. # Otherwise a system limit (for SysV at least) may be exceeded. .NOEXPORT: bluez-5.82/PaxHeaders/plugins0000644000000000000000000000005014773213564013266 xustar0020 atime=1743591291 20 ctime=1743591284 bluez-5.82/plugins/0000755000000000000000000000000014773213564013024 5ustar00rootrootbluez-5.82/plugins/PaxHeaders/sixaxis.c0000644000000000000000000000005014643061455015173 xustar0020 atime=1743516870 20 ctime=1743591284 bluez-5.82/plugins/sixaxis.c0000644000000000000000000003233614643061455014663 0ustar00rootroot// SPDX-License-Identifier: GPL-2.0-or-later /* * * BlueZ - Bluetooth protocol stack for Linux * * Copyright (C) 2009 Bastien Nocera * Copyright (C) 2011 Antonio Ospite * Copyright (C) 2013 Szymon Janc * * */ #ifdef HAVE_CONFIG_H #include #endif #define _GNU_SOURCE #include #include #include #include #include #include #include #include #include #include #include "lib/bluetooth.h" #include "lib/sdp.h" #include "lib/uuid.h" #include "src/adapter.h" #include "src/device.h" #include "src/agent.h" #include "src/plugin.h" #include "src/log.h" #include "src/shared/util.h" #include "profiles/input/sixaxis.h" struct authentication_closure { guint auth_id; char *sysfs_path; struct btd_adapter *adapter; struct btd_device *device; int fd; bdaddr_t bdaddr; /* device bdaddr */ CablePairingType type; }; struct authentication_destroy_closure { struct authentication_closure *closure; bool remove_device; }; static struct udev *ctx = NULL; static struct udev_monitor *monitor = NULL; static guint watch_id = 0; /* key = sysfs_path (const str), value = auth_closure */ static GHashTable *pending_auths = NULL; #define SIXAXIS_HID_SDP_RECORD "3601920900000A000100000900013503191124090004"\ "350D35061901000900113503190011090006350909656E09006A090100090009350"\ "8350619112409010009000D350F350D350619010009001335031900110901002513"\ "576972656C65737320436F6E74726F6C6C65720901012513576972656C657373204"\ "36F6E74726F6C6C6572090102251B536F6E7920436F6D707574657220456E746572"\ "7461696E6D656E74090200090100090201090100090202080009020308210902042"\ "8010902052801090206359A35980822259405010904A101A1028501750895011500"\ "26FF00810375019513150025013500450105091901291381027501950D0600FF810"\ "3150026FF0005010901A10075089504350046FF0009300931093209358102C00501"\ "75089527090181027508953009019102750895300901B102C0A1028502750895300"\ "901B102C0A10285EE750895300901B102C0A10285EF750895300901B102C0C00902"\ "07350835060904090901000902082800090209280109020A280109020B090100090"\ "20C093E8009020D280009020E2800" /* Make sure to unset auth_id if already handled */ static void auth_closure_destroy(struct authentication_closure *closure, bool remove_device) { if (closure->auth_id) btd_cancel_authorization(closure->auth_id); if (remove_device) btd_adapter_remove_device(closure->adapter, closure->device); close(closure->fd); g_free(closure->sysfs_path); g_free(closure); } static int sixaxis_get_device_bdaddr(int fd, bdaddr_t *bdaddr) { uint8_t buf[18]; int ret; memset(buf, 0, sizeof(buf)); buf[0] = 0xf2; ret = ioctl(fd, HIDIOCGFEATURE(sizeof(buf)), buf); if (ret < 0) { error("sixaxis: failed to read device address (%s)", strerror(errno)); return ret; } baswap(bdaddr, (bdaddr_t *) (buf + 4)); return 0; } static int ds4_get_device_bdaddr(int fd, bdaddr_t *bdaddr) { uint8_t buf[7]; int ret; memset(buf, 0, sizeof(buf)); buf[0] = 0x81; ret = ioctl(fd, HIDIOCGFEATURE(sizeof(buf)), buf); if (ret < 0) { error("sixaxis: failed to read DS4 device address (%s)", strerror(errno)); return ret; } /* address is little-endian on DS4 */ bacpy(bdaddr, (bdaddr_t*) (buf + 1)); return 0; } static int get_device_bdaddr(int fd, bdaddr_t *bdaddr, CablePairingType type) { if (type == CABLE_PAIRING_SIXAXIS) return sixaxis_get_device_bdaddr(fd, bdaddr); else if (type == CABLE_PAIRING_DS4) return ds4_get_device_bdaddr(fd, bdaddr); return -1; } static int sixaxis_get_central_bdaddr(int fd, bdaddr_t *bdaddr) { uint8_t buf[8]; int ret; memset(buf, 0, sizeof(buf)); buf[0] = 0xf5; ret = ioctl(fd, HIDIOCGFEATURE(sizeof(buf)), buf); if (ret < 0) { error("sixaxis: failed to read central address (%s)", strerror(errno)); return ret; } baswap(bdaddr, (bdaddr_t *) (buf + 2)); return 0; } static int ds4_get_central_bdaddr(int fd, bdaddr_t *bdaddr) { uint8_t buf[16]; int ret; memset(buf, 0, sizeof(buf)); buf[0] = 0x12; ret = ioctl(fd, HIDIOCGFEATURE(sizeof(buf)), buf); if (ret < 0) { error("sixaxis: failed to read DS4 central address (%s)", strerror(errno)); return ret; } /* address is little-endian on DS4 */ bacpy(bdaddr, (bdaddr_t*) (buf + 10)); return 0; } static int get_central_bdaddr(int fd, bdaddr_t *bdaddr, CablePairingType type) { if (type == CABLE_PAIRING_SIXAXIS) return sixaxis_get_central_bdaddr(fd, bdaddr); else if (type == CABLE_PAIRING_DS4) return ds4_get_central_bdaddr(fd, bdaddr); return -1; } static int sixaxis_set_central_bdaddr(int fd, const bdaddr_t *bdaddr) { uint8_t buf[8]; int ret; buf[0] = 0xf5; buf[1] = 0x01; baswap((bdaddr_t *) (buf + 2), bdaddr); ret = ioctl(fd, HIDIOCSFEATURE(sizeof(buf)), buf); if (ret < 0) error("sixaxis: failed to write central address (%s)", strerror(errno)); return ret; } static int ds4_set_central_bdaddr(int fd, const bdaddr_t *bdaddr) { uint8_t buf[23]; int ret; buf[0] = 0x13; bacpy((bdaddr_t*) (buf + 1), bdaddr); /* TODO: we could put the key here but there is no way to force a re-loading of link keys to the kernel from here. */ memset(buf + 7, 0, 16); ret = ioctl(fd, HIDIOCSFEATURE(sizeof(buf)), buf); if (ret < 0) error("sixaxis: failed to write DS4 central address (%s)", strerror(errno)); return ret; } static int set_central_bdaddr(int fd, const bdaddr_t *bdaddr, CablePairingType type) { if (type == CABLE_PAIRING_SIXAXIS) return sixaxis_set_central_bdaddr(fd, bdaddr); else if (type == CABLE_PAIRING_DS4) return ds4_set_central_bdaddr(fd, bdaddr); return -1; } static bool is_auth_pending(struct authentication_closure *closure) { GHashTableIter iter; gpointer value; g_hash_table_iter_init(&iter, pending_auths); while (g_hash_table_iter_next(&iter, NULL, &value)) { struct authentication_closure *c = value; if (c == closure) return true; } return false; } static gboolean auth_closure_destroy_idle(gpointer user_data) { struct authentication_destroy_closure *destroy = user_data; auth_closure_destroy(destroy->closure, destroy->remove_device); g_free(destroy); return false; } static void agent_auth_cb(DBusError *derr, void *user_data) { struct authentication_closure *closure = user_data; struct authentication_destroy_closure *destroy; char central_addr[18], adapter_addr[18], device_addr[18]; bdaddr_t central_bdaddr; const bdaddr_t *adapter_bdaddr; bool remove_device = true; if (!is_auth_pending(closure)) return; /* Don't try to remove this auth, we're handling it already */ closure->auth_id = 0; if (derr != NULL) { DBG("Agent replied negatively, removing temporary device"); goto out; } if (get_central_bdaddr(closure->fd, ¢ral_bdaddr, closure->type) < 0) goto out; adapter_bdaddr = btd_adapter_get_address(closure->adapter); if (bacmp(adapter_bdaddr, ¢ral_bdaddr)) { if (set_central_bdaddr(closure->fd, adapter_bdaddr, closure->type) < 0) goto out; } remove_device = false; btd_device_set_temporary(closure->device, false); if (closure->type == CABLE_PAIRING_SIXAXIS) btd_device_set_record(closure->device, HID_UUID, SIXAXIS_HID_SDP_RECORD); ba2str(&closure->bdaddr, device_addr); ba2str(¢ral_bdaddr, central_addr); ba2str(adapter_bdaddr, adapter_addr); DBG("remote %s old_central %s new_central %s", device_addr, central_addr, adapter_addr); out: g_hash_table_steal(pending_auths, closure->sysfs_path); /* btd_adapter_remove_device() cannot be called in this * callback or it would lead to a double-free in while * trying to cancel the authentication that's being processed, * so clean up in an idle */ destroy = g_new0(struct authentication_destroy_closure, 1); destroy->closure = closure; destroy->remove_device = remove_device; g_idle_add(auth_closure_destroy_idle, destroy); } static bool setup_device(int fd, const char *sysfs_path, const struct cable_pairing *cp, struct btd_adapter *adapter) { bdaddr_t device_bdaddr; const bdaddr_t *adapter_bdaddr; struct btd_device *device; struct authentication_closure *closure; if (get_device_bdaddr(fd, &device_bdaddr, cp->type) < 0) return false; /* This can happen if controller was plugged while already setup and * connected eg. to charge up battery. */ device = btd_adapter_find_device(adapter, &device_bdaddr, BDADDR_BREDR); if (device && btd_device_has_uuid(device, HID_UUID) && (btd_device_is_connected(device) || btd_device_is_trusted(device))) { char device_addr[18]; ba2str(&device_bdaddr, device_addr); DBG("device %s already known, skipping", device_addr); return false; } device = btd_adapter_get_device(adapter, &device_bdaddr, BDADDR_BREDR); if (!device) { error("sixaxis: unable to set up a new device"); return false; } info("sixaxis: setting up new device"); btd_device_device_set_name(device, cp->name); btd_device_set_pnpid(device, cp->source, cp->vid, cp->pid, cp->version); btd_device_set_temporary(device, true); closure = g_new0(struct authentication_closure, 1); if (!closure) { btd_adapter_remove_device(adapter, device); return false; } closure->adapter = adapter; closure->device = device; closure->sysfs_path = g_strdup(sysfs_path); closure->fd = fd; bacpy(&closure->bdaddr, &device_bdaddr); closure->type = cp->type; adapter_bdaddr = btd_adapter_get_address(adapter); closure->auth_id = btd_request_authorization_cable_configured( adapter_bdaddr, &device_bdaddr, HID_UUID, agent_auth_cb, closure); if (closure->auth_id == 0) { error("sixaxis: could not request cable authorization"); auth_closure_destroy(closure, true); return false; } g_hash_table_insert(pending_auths, closure->sysfs_path, closure); return true; } static const struct cable_pairing * get_pairing_type_for_device(struct udev_device *udevice, uint16_t *bus, char **sysfs_path) { struct udev_device *hid_parent; const char *hid_name; const char *hid_id; const struct cable_pairing *cp; uint16_t vid, pid; hid_parent = udev_device_get_parent_with_subsystem_devtype(udevice, "hid", NULL); if (!hid_parent) return NULL; hid_id = udev_device_get_property_value(hid_parent, "HID_ID"); if (!hid_id || sscanf(hid_id, "%hx:%hx:%hx", bus, &vid, &pid) != 3) return NULL; hid_name = udev_device_get_property_value(hid_parent, "HID_NAME"); cp = get_pairing(vid, pid, hid_name); *sysfs_path = g_strdup(udev_device_get_syspath(udevice)); return cp; } static void device_added(struct udev_device *udevice) { struct btd_adapter *adapter; uint16_t bus; char *sysfs_path = NULL; const struct cable_pairing *cp; int fd; adapter = btd_adapter_get_default(); if (!adapter) return; cp = get_pairing_type_for_device(udevice, &bus, &sysfs_path); if (!cp || (cp->type != CABLE_PAIRING_SIXAXIS && cp->type != CABLE_PAIRING_DS4)) { g_free(sysfs_path); return; } if (bus != BUS_USB) { g_free(sysfs_path); return; } info("sixaxis: compatible device connected: %s (%04X:%04X %s)", cp->name, cp->vid, cp->pid, sysfs_path); fd = open(udev_device_get_devnode(udevice), O_RDWR); if (fd < 0) { g_free(sysfs_path); return; } /* Only close the fd if an authentication is not pending */ if (!setup_device(fd, sysfs_path, cp, adapter)) close(fd); g_free(sysfs_path); } static void device_removed(struct udev_device *udevice) { struct authentication_closure *closure; const char *sysfs_path; sysfs_path = udev_device_get_syspath(udevice); if (!sysfs_path) return; closure = g_hash_table_lookup(pending_auths, sysfs_path); if (!closure) return; g_hash_table_steal(pending_auths, sysfs_path); auth_closure_destroy(closure, true); } static gboolean monitor_watch(GIOChannel *source, GIOCondition condition, gpointer data) { struct udev_device *udevice; udevice = udev_monitor_receive_device(monitor); if (!udevice) return TRUE; if (!g_strcmp0(udev_device_get_action(udevice), "add")) device_added(udevice); else if (!g_strcmp0(udev_device_get_action(udevice), "remove")) device_removed(udevice); udev_device_unref(udevice); return TRUE; } static int sixaxis_init(void) { GIOChannel *channel; DBG(""); ctx = udev_new(); if (!ctx) return -EIO; monitor = udev_monitor_new_from_netlink(ctx, "udev"); if (!monitor) { udev_unref(ctx); ctx = NULL; return -EIO; } /* Listen for newly connected hidraw interfaces */ udev_monitor_filter_add_match_subsystem_devtype(monitor, "hidraw", NULL); udev_monitor_enable_receiving(monitor); channel = g_io_channel_unix_new(udev_monitor_get_fd(monitor)); watch_id = g_io_add_watch(channel, G_IO_IN, monitor_watch, NULL); g_io_channel_unref(channel); pending_auths = g_hash_table_new(g_str_hash, g_str_equal); return 0; } static void sixaxis_exit(void) { GHashTableIter iter; gpointer value; DBG(""); g_hash_table_iter_init(&iter, pending_auths); while (g_hash_table_iter_next(&iter, NULL, &value)) { struct authentication_closure *closure = value; auth_closure_destroy(closure, true); } g_hash_table_destroy(pending_auths); pending_auths = NULL; g_source_remove(watch_id); watch_id = 0; udev_monitor_unref(monitor); monitor = NULL; udev_unref(ctx); ctx = NULL; } BLUETOOTH_PLUGIN_DEFINE(sixaxis, VERSION, BLUETOOTH_PLUGIN_PRIORITY_LOW, sixaxis_init, sixaxis_exit) bluez-5.82/plugins/PaxHeaders/autopair.c0000644000000000000000000000005014766002272015325 xustar0020 atime=1743515578 20 ctime=1743591283 bluez-5.82/plugins/autopair.c0000644000000000000000000001637614766002272015023 0ustar00rootroot// SPDX-License-Identifier: GPL-2.0-or-later /* * * BlueZ - Bluetooth protocol stack for Linux * * Copyright (C) 2012 Google Inc. * * */ #ifdef HAVE_CONFIG_H #include #endif #include #include #include #include #include #include #include "lib/bluetooth.h" #include "lib/sdp.h" #include "src/plugin.h" #include "src/adapter.h" #include "src/device.h" #include "src/log.h" #include "src/storage.h" #include "src/shared/util.h" /* * Nintendo Wii Remote devices require the bdaddr of the host as pin input for * authentication. This plugin registers a pin-callback and forces this pin * to be used for authentication. * * There are two ways to place the wiimote into discoverable mode. * - Pressing the red-sync button on the back of the wiimote. This module * supports pairing via this method. Auto-reconnect should be possible after * the device was paired once. * - Pressing the 1+2 buttons on the front of the wiimote. This module does * not support this method since this method never enables auto-reconnect. * Hence, pairing is not needed. Use it without pairing if you want. * After connecting the wiimote you should immediately connect to the input * service of the wiimote. If you don't, the wiimote will close the connection. * The wiimote waits about 5 seconds until it turns off again. * Auto-reconnect is only enabled when pairing with the wiimote via the red * sync-button and then connecting to the input service. If you do not connect * to the input service, then auto-reconnect is not enabled. * If enabled, the wiimote connects to the host automatically when any button * is pressed. */ static uint16_t wii_ids[][2] = { { 0x057e, 0x0306 }, /* 1st gen */ { 0x054c, 0x0306 }, /* LEGO wiimote */ { 0x057e, 0x0330 }, /* 2nd gen */ }; static const char *wii_names[] = { "Nintendo RVL-CNT-01", /* 1st gen */ "Nintendo RVL-CNT-01-TR", /* 2nd gen */ "Nintendo RVL-CNT-01-UC", /* Wii U Pro Controller */ "Nintendo RVL-WBC-01", /* Balance Board */ }; static ssize_t wii_pincb(struct btd_adapter *adapter, struct btd_device *device, char *pinbuf, bool *display, unsigned int attempt) { uint16_t vendor, product; char addr[18], name[25]; unsigned int i; /* Only try the pin code once per device. If it's not correct then it's * an unknown device. */ if (attempt > 1) return 0; ba2str(device_get_address(device), addr); vendor = btd_device_get_vendor(device); product = btd_device_get_product(device); device_get_name(device, name, sizeof(name)); for (i = 0; i < G_N_ELEMENTS(wii_ids); ++i) { if (vendor == wii_ids[i][0] && product == wii_ids[i][1]) goto found; } for (i = 0; i < G_N_ELEMENTS(wii_names); ++i) { if (g_str_equal(name, wii_names[i])) goto found; } return 0; found: DBG("Forcing fixed pin on detected wiimote %s", addr); memcpy(pinbuf, btd_adapter_get_address(adapter), 6); return 6; } /* * Plugin to handle automatic pairing of devices with reduced user * interaction, including implementing the recommendation of the HID spec * for keyboard devices. * * The plugin works by intercepting the PIN request for devices; if the * device is a keyboard a random six-digit numeric PIN is generated and * returned, flagged for displaying using DisplayPinCode. * */ static ssize_t autopair_pincb(struct btd_adapter *adapter, struct btd_device *device, char *pinbuf, bool *display, unsigned int attempt) { char addr[18]; char pinstr[7]; char name[25]; uint32_t class; uint32_t val; ssize_t ret; /* Try with the wii_pincb first */ ret = wii_pincb(adapter, device, pinbuf, display, attempt); if (ret > 0) return ret; ba2str(device_get_address(device), addr); class = btd_device_get_class(device); device_get_name(device, name, sizeof(name)); DBG("device '%s' (%s) class: 0x%x vid/pid: 0x%X/0x%X", name, addr, class, btd_device_get_vendor (device), btd_device_get_product (device)); /* The iCade shouldn't use random PINs like normal keyboards */ if (strstr(name, "iCade") != NULL) return 0; /* This is a class-based pincode guesser. Ignore devices with an * unknown class. */ if (class == 0) return 0; switch ((class & 0x1f00) >> 8) { case 0x04: /* Audio/Video */ switch ((class & 0xfc) >> 2) { case 0x01: /* Wearable Headset Device */ case 0x02: /* Hands-free Device */ case 0x06: /* Headphones */ case 0x07: /* Portable Audio */ case 0x0a: /* HiFi Audio Device */ { const char *pincodes[] = { "0000", "1234", "1111" }; const char *pincode; if (attempt > G_N_ELEMENTS(pincodes)) return 0; pincode = pincodes[attempt - 1]; memcpy(pinbuf, pincode, strlen(pincode)); return strlen(pincode); } } break; case 0x05: /* Peripheral */ switch ((class & 0xc0) >> 6) { case 0x00: switch ((class & 0x1e) >> 2) { case 0x01: /* Joystick */ case 0x02: /* Gamepad */ case 0x03: /* Remote Control */ if (attempt > 1) return 0; memcpy(pinbuf, "0000", 4); return 4; } break; case 0x01: /* Keyboard */ case 0x03: /* Combo keyboard/pointing device */ /* For keyboards rejecting the first random code * in less than 500ms, try a fixed code. */ if (attempt > 1 && device_bonding_last_duration(device) < 500) { /* Don't try more than one dumb code */ if (attempt > 2) return 0; /* Try "0000" as the code for the second * attempt. */ memcpy(pinbuf, "0000", 4); return 4; } /* Never try more than 3 random pincodes. */ if (attempt >= 4) return 0; if (util_getrandom(&val, sizeof(val), 0) < 0) { error("Failed to get a random pincode"); return 0; } snprintf(pinstr, sizeof(pinstr), "%06u", val % 1000000); *display = true; memcpy(pinbuf, pinstr, 6); return 6; case 0x02: /* Pointing device */ if (attempt > 1) return 0; memcpy(pinbuf, "0000", 4); return 4; } break; case 0x06: /* Imaging */ if (class & 0x80) { /* Printer */ if (attempt > 1) return 0; memcpy(pinbuf, "0000", 4); return 4; } break; } return 0; } static int autopair_probe(struct btd_adapter *adapter) { btd_adapter_register_pin_cb(adapter, autopair_pincb); return 0; } static void autopair_remove(struct btd_adapter *adapter) { btd_adapter_unregister_pin_cb(adapter, autopair_pincb); } static struct btd_adapter_driver autopair_driver = { .name = "autopair", .probe = autopair_probe, .remove = autopair_remove, }; static int autopair_init(void) { /* Initialize the random seed from /dev/urandom */ unsigned int seed; int fd, err; ssize_t n; fd = open("/dev/urandom", O_RDONLY); if (fd < 0) { err = -errno; error("Failed to open /dev/urandom: %s (%d)", strerror(-err), -err); return err; } n = read(fd, &seed, sizeof(seed)); if (n < (ssize_t) sizeof(seed)) { err = (n == -1) ? -errno : -EIO; error("Failed to read %zu bytes from /dev/urandom: %s (%d)", sizeof(seed), strerror(-err), -err); close(fd); return err; } close(fd); srand(seed); return btd_register_adapter_driver(&autopair_driver); } static void autopair_exit(void) { btd_unregister_adapter_driver(&autopair_driver); } BLUETOOTH_PLUGIN_DEFINE(autopair, VERSION, BLUETOOTH_PLUGIN_PRIORITY_DEFAULT, autopair_init, autopair_exit) bluez-5.82/plugins/PaxHeaders/neard.c0000644000000000000000000000005014214376354014575 xustar0020 atime=1743516870 20 ctime=1743591284 bluez-5.82/plugins/neard.c0000644000000000000000000005006214214376354014261 0ustar00rootroot// SPDX-License-Identifier: GPL-2.0-or-later /* * * BlueZ - Bluetooth protocol stack for Linux * * Copyright (C) 2012-2013 Tieto Poland * * */ #ifdef HAVE_CONFIG_H #include #endif #define _GNU_SOURCE #include #include #include "lib/bluetooth.h" #include "lib/hci.h" #include "lib/sdp.h" #include "gdbus/gdbus.h" #include "src/plugin.h" #include "src/log.h" #include "src/dbus-common.h" #include "src/adapter.h" #include "src/device.h" #include "src/eir.h" #include "src/agent.h" #include "src/btd.h" #include "src/shared/util.h" #define NEARD_NAME "org.neard" #define NEARD_PATH "/" #define NEARD_MANAGER_INTERFACE "org.neard.Manager" #define AGENT_INTERFACE "org.neard.HandoverAgent" #define AGENT_PATH "/org/bluez/neard_handover_agent" #define AGENT_CARRIER_TYPE "bluetooth" #define ERROR_INTERFACE "org.neard.HandoverAgent.Error" static guint watcher_id = 0; static char *neard_service = NULL; static bool agent_register_postpone = false; /* For NFC mimetype limits max OOB EIR size */ #define NFC_OOB_EIR_MAX UINT8_MAX enum cps { CPS_ACTIVE, CPS_INACTIVE, CPS_ACTIVATING, CPS_UNKNOWN, }; struct oob_params { bdaddr_t address; uint32_t class; char *name; GSList *services; uint8_t *hash; uint8_t *randomizer; uint8_t *pin; int pin_len; enum cps power_state; }; static void free_oob_params(struct oob_params *params) { g_slist_free_full(params->services, free); g_free(params->name); g_free(params->hash); g_free(params->randomizer); free(params->pin); } static DBusMessage *error_reply(DBusMessage *msg, int error) { const char *name; if (error == EINPROGRESS) name = ERROR_INTERFACE ".InProgress"; else name = ERROR_INTERFACE ".Failed"; return g_dbus_create_error(msg, name , "%s", strerror(error)); } static void register_agent(bool append_carrier); static void register_agent_cb(DBusPendingCall *call, void *user_data) { DBusMessage *reply; DBusError err; static bool try_fallback = true; reply = dbus_pending_call_steal_reply(call); dbus_error_init(&err); if (dbus_set_error_from_message(&err, reply)) { if (g_str_equal(DBUS_ERROR_UNKNOWN_METHOD, err.name) && try_fallback) { DBG("Register to neard failed, trying legacy way"); register_agent(false); try_fallback = false; } else { error("neard manager replied with an error: %s, %s", err.name, err.message); g_dbus_unregister_interface(btd_get_dbus_connection(), AGENT_PATH, AGENT_INTERFACE); try_fallback = true; } dbus_error_free(&err); dbus_message_unref(reply); return; } dbus_message_unref(reply); neard_service = g_strdup(dbus_message_get_sender(reply)); try_fallback = true; info("Registered as neard handover agent"); } static void register_agent(bool append_carrier) { DBusMessage *message; DBusPendingCall *call; const char *path = AGENT_PATH; const char *carrier = AGENT_CARRIER_TYPE; message = dbus_message_new_method_call(NEARD_NAME, NEARD_PATH, NEARD_MANAGER_INTERFACE, "RegisterHandoverAgent"); if (!message) { error("Couldn't allocate D-Bus message"); return; } dbus_message_append_args(message, DBUS_TYPE_OBJECT_PATH, &path, DBUS_TYPE_INVALID); if (append_carrier) dbus_message_append_args(message, DBUS_TYPE_STRING, &carrier, DBUS_TYPE_INVALID); if (!g_dbus_send_message_with_reply(btd_get_dbus_connection(), message, &call, -1)) { dbus_message_unref(message); error("D-Bus send failed"); return; } dbus_pending_call_set_notify(call, register_agent_cb, NULL, NULL); dbus_pending_call_unref(call); dbus_message_unref(message); } static void unregister_agent(void) { DBusMessage *message; const char *path = AGENT_PATH; const char *carrier = AGENT_CARRIER_TYPE; g_free(neard_service); neard_service = NULL; message = dbus_message_new_method_call(NEARD_NAME, NEARD_PATH, NEARD_MANAGER_INTERFACE, "UnregisterHandoverAgent"); if (!message) { error("Couldn't allocate D-Bus message"); goto unregister; } dbus_message_append_args(message, DBUS_TYPE_OBJECT_PATH, &path, DBUS_TYPE_INVALID); dbus_message_append_args(message, DBUS_TYPE_STRING, &carrier, DBUS_TYPE_INVALID); if (!g_dbus_send_message(btd_get_dbus_connection(), message)) error("D-Bus send failed"); unregister: g_dbus_unregister_interface(btd_get_dbus_connection(), AGENT_PATH, AGENT_INTERFACE); } static void add_power_state(DBusMessageIter *dict, struct btd_adapter *adapter) { const char *state; if (btd_adapter_get_powered(adapter) && btd_adapter_get_connectable(adapter)) state = "active"; else state = "inactive"; dict_append_entry(dict, "State", DBUS_TYPE_STRING, &state); } static DBusMessage *create_request_oob_reply(struct btd_adapter *adapter, const uint8_t *hash, const uint8_t *randomizer, DBusMessage *msg) { DBusMessage *reply; DBusMessageIter iter; DBusMessageIter dict; uint8_t eir[NFC_OOB_EIR_MAX]; uint8_t *peir = eir; int len; len = eir_create_oob(btd_adapter_get_address(adapter), btd_adapter_get_name(adapter), btd_adapter_get_class(adapter), hash, randomizer, btd_opts.did_vendor, btd_opts.did_product, btd_opts.did_version, btd_opts.did_source, btd_adapter_get_services(adapter), eir); reply = dbus_message_new_method_return(msg); if (!reply) return NULL; dbus_message_iter_init_append(reply, &iter); dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY, DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING DBUS_TYPE_STRING_AS_STRING DBUS_TYPE_VARIANT_AS_STRING DBUS_DICT_ENTRY_END_CHAR_AS_STRING, &dict); dict_append_array(&dict, "EIR", DBUS_TYPE_BYTE, &peir, len); add_power_state(&dict, adapter); dbus_message_iter_close_container(&iter, &dict); return reply; } static void read_local_complete(struct btd_adapter *adapter, const uint8_t *hash, const uint8_t *randomizer, void *user_data) { DBusMessage *msg = user_data; DBusMessage *reply; DBG(""); if (neard_service == NULL) { dbus_message_unref(msg); if (agent_register_postpone) { agent_register_postpone = false; register_agent(true); } return; } if (hash && randomizer) reply = create_request_oob_reply(adapter, hash, randomizer, msg); else reply = error_reply(msg, EIO); dbus_message_unref(msg); if (!g_dbus_send_message(btd_get_dbus_connection(), reply)) error("D-Bus send failed"); } static void bonding_complete(struct btd_adapter *adapter, const bdaddr_t *bdaddr, uint8_t status, void *user_data) { DBusMessage *msg = user_data; DBusMessage *reply; DBG(""); if (neard_service == NULL) { dbus_message_unref(msg); if (agent_register_postpone) { agent_register_postpone = false; register_agent(true); } return; } if (status) reply = error_reply(msg, EIO); else reply = g_dbus_create_reply(msg, DBUS_TYPE_INVALID); dbus_message_unref(msg); if (!g_dbus_send_message(btd_get_dbus_connection(), reply)) error("D-Bus send failed"); } static int check_device(struct btd_device *device) { if (!device) return -ENOENT; /* If already paired */ if (device_is_paired(device, BDADDR_BREDR)) { DBG("already paired"); return -EALREADY; } /* Pairing in progress... */ if (device_is_bonding(device, NULL)) { DBG("pairing in progress"); return -EINPROGRESS; } return 0; } static int process_eir(uint8_t *eir, size_t size, struct oob_params *remote) { struct eir_data eir_data; DBG("size %zu", size); memset(&eir_data, 0, sizeof(eir_data)); if (eir_parse_oob(&eir_data, eir, size) < 0) return -EINVAL; bacpy(&remote->address, &eir_data.addr); remote->class = eir_data.class; remote->name = eir_data.name; eir_data.name = NULL; remote->services = eir_data.services; eir_data.services = NULL; remote->hash = eir_data.hash; eir_data.hash = NULL; remote->randomizer = eir_data.randomizer; eir_data.randomizer = NULL; eir_data_free(&eir_data); return 0; } /* * This is (barely documented) Nokia extension format, most work was done by * reverse engineering. * * Binary format varies among different devices, type depends on first byte * 0x00 - BT address not reversed, 16 bytes authentication data (all zeros) * 0x01 - BT address not reversed, 16 bytes authentication data (4 digit PIN, * padded with zeros) * 0x02 - BT address not reversed, 16 bytes authentication data (not sure if * 16 digit PIN or link key?, Nokia refers to it as ' Public Key') * 0x10 - BT address reversed, no authentication data * 0x24 - BT address not reversed, 4 bytes authentication data (4 digit PIN) * * General structure: * 1 byte - marker * 6 bytes - BT address (reversed or not, depends on marker) * 3 bytes - Class of Device * 0, 4 or 16 bytes - authentication data, interpretation depends on marker * 1 bytes - name length * N bytes - name */ static int process_nokia_long (void *data, size_t size, uint8_t marker, struct oob_params *remote) { struct { bdaddr_t address; uint8_t class[3]; uint8_t authentication[16]; uint8_t name_len; uint8_t name[0]; } __attribute__((packed)) *n = data; if (size != sizeof(*n) + n->name_len) return -EINVAL; /* address is not reverted */ baswap(&remote->address, &n->address); remote->class = n->class[0] | (n->class[1] << 8) | (n->class[2] << 16); if (n->name_len > 0) remote->name = g_strndup((char *)n->name, n->name_len); if (marker == 0x01) { remote->pin = util_memdup(n->authentication, 4); remote->pin_len = 4; } else if (marker == 0x02) { remote->pin = util_memdup(n->authentication, 16); remote->pin_len = 16; } return 0; } static int process_nokia_short (void *data, size_t size, struct oob_params *remote) { struct { bdaddr_t address; uint8_t class[3]; uint8_t authentication[4]; uint8_t name_len; uint8_t name[0]; } __attribute__((packed)) *n = data; if (size != sizeof(*n) + n->name_len) return -EINVAL; /* address is not reverted */ baswap(&remote->address, &n->address); remote->class = n->class[0] | (n->class[1] << 8) | (n->class[2] << 16); if (n->name_len > 0) remote->name = g_strndup((char *)n->name, n->name_len); remote->pin = util_memdup(n->authentication, 4); remote->pin_len = 4; return 0; } static int process_nokia_extra_short (void *data, size_t size, struct oob_params *remote) { struct { bdaddr_t address; uint8_t class[3]; uint8_t name_len; uint8_t name[0]; } __attribute__((packed)) *n = data; if (size != sizeof(*n) + n->name_len) return -EINVAL; bacpy(&remote->address, &n->address); remote->class = n->class[0] | (n->class[1] << 8) | (n->class[2] << 16); if (n->name_len > 0) remote->name = g_strndup((char *)n->name, n->name_len); return 0; } static int process_nokia_com_bt(uint8_t *data, size_t size, struct oob_params *remote) { uint8_t marker; marker = *data++; size--; DBG("marker: 0x%.2x size: %zu", marker, size); switch (marker) { case 0x00: case 0x01: case 0x02: return process_nokia_long(data, size, marker, remote); case 0x10: return process_nokia_extra_short(data, size, remote); case 0x24: return process_nokia_short(data, size, remote); default: warn("Not supported Nokia NFC extension (0x%.2x)", marker); return -EPROTONOSUPPORT; } } static enum cps process_state(const char *state) { if (strcasecmp(state, "active") == 0) return CPS_ACTIVE; if (strcasecmp(state, "activating") == 0) return CPS_ACTIVATING; if (strcasecmp(state, "inactive") == 0) return CPS_INACTIVE; return CPS_UNKNOWN; } static int process_message(DBusMessage *msg, struct oob_params *remote) { DBusMessageIter iter; DBusMessageIter dict; dbus_message_iter_init(msg, &iter); if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_ARRAY) return -EINVAL; /* set CPS to unknown in case State was not provided */ remote->power_state = CPS_UNKNOWN; dbus_message_iter_recurse(&iter, &dict); while (dbus_message_iter_get_arg_type(&dict) == DBUS_TYPE_DICT_ENTRY) { DBusMessageIter value; DBusMessageIter entry; const char *key; dbus_message_iter_recurse(&dict, &entry); if (dbus_message_iter_get_arg_type(&entry) != DBUS_TYPE_STRING) goto error; dbus_message_iter_get_basic(&entry, &key); dbus_message_iter_next(&entry); dbus_message_iter_recurse(&entry, &value); if (strcasecmp(key, "EIR") == 0) { DBusMessageIter array; uint8_t *eir; int size; /* nokia.com:bt and EIR should not be passed together */ if (bacmp(&remote->address, BDADDR_ANY) != 0) goto error; if (dbus_message_iter_get_arg_type(&value) != DBUS_TYPE_ARRAY) goto error; dbus_message_iter_recurse(&value, &array); dbus_message_iter_get_fixed_array(&array, &eir, &size); if (process_eir(eir, size, remote) < 0) goto error; } else if (strcasecmp(key, "nokia.com:bt") == 0) { DBusMessageIter array; uint8_t *data; int size; /* nokia.com:bt and EIR should not be passed together */ if (bacmp(&remote->address, BDADDR_ANY) != 0) goto error; if (dbus_message_iter_get_arg_type(&value) != DBUS_TYPE_ARRAY) goto error; dbus_message_iter_recurse(&value, &array); dbus_message_iter_get_fixed_array(&array, &data, &size); if (process_nokia_com_bt(data, size, remote)) goto error; } else if (strcasecmp(key, "State") == 0) { const char *state; if (dbus_message_iter_get_arg_type(&value) != DBUS_TYPE_STRING) goto error; dbus_message_iter_get_basic(&value, &state); remote->power_state = process_state(state); if (remote->power_state == CPS_UNKNOWN) goto error; } dbus_message_iter_next(&dict); } /* Check if 'State' was passed along with one of other fields */ if (remote->power_state != CPS_UNKNOWN && bacmp(&remote->address, BDADDR_ANY) == 0) return -EINVAL; return 0; error: if (bacmp(&remote->address, BDADDR_ANY) != 0) { free_oob_params(remote); memset(remote, 0, sizeof(*remote)); } return -EINVAL; } static int check_adapter(struct btd_adapter *adapter) { if (!adapter) return -ENOENT; if (btd_adapter_check_oob_handler(adapter)) return -EINPROGRESS; if (!btd_adapter_ssp_enabled(adapter)) return -ENOTSUP; return 0; } static void store_params(struct btd_adapter *adapter, struct btd_device *device, struct oob_params *params) { if (params->class != 0) device_set_class(device, params->class); if (params->name) { device_store_cached_name(device, params->name); btd_device_device_set_name(device, params->name); } if (params->services) device_add_eir_uuids(device, params->services); if (params->hash) { btd_adapter_add_remote_oob_data(adapter, ¶ms->address, params->hash, params->randomizer); } else if (params->pin_len) { /* TODO * Handle PIN, for now only discovery mode and 'common' PINs * that might be provided by agent will work correctly. */ } } static DBusMessage *push_oob(DBusConnection *conn, DBusMessage *msg, void *data) { struct btd_adapter *adapter; struct agent *agent; struct oob_handler *handler; struct oob_params remote; struct btd_device *device; uint8_t io_cap; int err; if (neard_service == NULL || !g_str_equal(neard_service, dbus_message_get_sender(msg))) return error_reply(msg, EPERM); DBG(""); adapter = btd_adapter_get_default(); err = check_adapter(adapter); if (err < 0) return error_reply(msg, -err); if (!btd_adapter_get_powered(adapter)) return error_reply(msg, ENONET); agent = adapter_get_agent(adapter); if (!agent) return error_reply(msg, ENONET); io_cap = agent_get_io_capability(agent); agent_unref(agent); memset(&remote, 0, sizeof(remote)); err = process_message(msg, &remote); if (err < 0) return error_reply(msg, -err); if (bacmp(&remote.address, BDADDR_ANY) == 0) { free_oob_params(&remote); return error_reply(msg, EINVAL); } device = btd_adapter_get_device(adapter, &remote.address, BDADDR_BREDR); err = check_device(device); if (err < 0) { free_oob_params(&remote); /* already paired, reply immediately */ if (err == -EALREADY) return g_dbus_create_reply(msg, DBUS_TYPE_INVALID); return error_reply(msg, -err); } if (!btd_adapter_get_pairable(adapter)) { free_oob_params(&remote); return error_reply(msg, ENONET); } store_params(adapter, device, &remote); free_oob_params(&remote); err = adapter_create_bonding(adapter, device_get_address(device), BDADDR_BREDR, io_cap); if (err < 0) return error_reply(msg, -err); handler = g_new0(struct oob_handler, 1); handler->bonding_cb = bonding_complete; bacpy(&handler->remote_addr, device_get_address(device)); handler->user_data = dbus_message_ref(msg); btd_adapter_set_oob_handler(adapter, handler); return NULL; } static DBusMessage *request_oob(DBusConnection *conn, DBusMessage *msg, void *data) { struct btd_adapter *adapter; struct oob_handler *handler; struct oob_params remote; struct btd_device *device; int err; if (neard_service == NULL || !g_str_equal(neard_service, dbus_message_get_sender(msg))) return error_reply(msg, EPERM); DBG(""); adapter = btd_adapter_get_default(); err = check_adapter(adapter); if (err < 0) return error_reply(msg, -err); memset(&remote, 0, sizeof(remote)); err = process_message(msg, &remote); if (err < 0) return error_reply(msg, -err); if (bacmp(&remote.address, BDADDR_ANY) == 0) { if (btd_adapter_get_powered(adapter)) goto read_local; goto done; } device = btd_adapter_get_device(adapter, &remote.address, BDADDR_BREDR); err = check_device(device); if (err < 0) goto done; if (!btd_adapter_get_pairable(adapter)) { err = -ENONET; goto done; } store_params(adapter, device, &remote); if (remote.hash && btd_adapter_get_powered(adapter)) goto read_local; done: free_oob_params(&remote); if (err < 0 && err != -EALREADY) return error_reply(msg, -err); return create_request_oob_reply(adapter, NULL, NULL, msg); read_local: free_oob_params(&remote); err = btd_adapter_read_local_oob_data(adapter); if (err < 0) return error_reply(msg, -err); handler = g_new0(struct oob_handler, 1); handler->read_local_cb = read_local_complete; handler->user_data = dbus_message_ref(msg); btd_adapter_set_oob_handler(adapter, handler); return NULL; } static DBusMessage *release(DBusConnection *conn, DBusMessage *msg, void *user_data) { if (neard_service == NULL || !g_str_equal(neard_service, dbus_message_get_sender(msg))) return error_reply(msg, EPERM); DBG(""); g_free(neard_service); neard_service = NULL; g_dbus_unregister_interface(conn, AGENT_PATH, AGENT_INTERFACE); return g_dbus_create_reply(msg, DBUS_TYPE_INVALID); } static const GDBusMethodTable neard_methods[] = { { GDBUS_ASYNC_METHOD("RequestOOB", GDBUS_ARGS({ "data", "a{sv}" }), GDBUS_ARGS({ "data", "a{sv}" }), request_oob) }, { GDBUS_ASYNC_METHOD("PushOOB", GDBUS_ARGS({ "data", "a{sv}"}), NULL, push_oob) }, { GDBUS_METHOD("Release", NULL, NULL, release) }, { } }; static void neard_appeared(DBusConnection *conn, void *user_data) { struct btd_adapter *adapter; DBG(""); if (!g_dbus_register_interface(conn, AGENT_PATH, AGENT_INTERFACE, neard_methods, NULL, NULL, NULL, NULL)) { error("neard interface init failed on path " AGENT_PATH); return; } /* * If there is pending action ongoing when neard appeared, possibly * due to neard crash or release before action was completed, postpone * register until action is finished. */ adapter = btd_adapter_get_default(); if (adapter && btd_adapter_check_oob_handler(adapter)) agent_register_postpone = true; else register_agent(true); } static void neard_vanished(DBusConnection *conn, void *user_data) { DBG(""); /* neard existed without unregistering agent */ if (neard_service != NULL) { g_free(neard_service); neard_service = NULL; g_dbus_unregister_interface(conn, AGENT_PATH, AGENT_INTERFACE); } } static int neard_init(void) { DBG("Setup neard plugin"); watcher_id = g_dbus_add_service_watch(btd_get_dbus_connection(), NEARD_NAME, neard_appeared, neard_vanished, NULL, NULL); if (watcher_id == 0) return -ENOMEM; return 0; } static void neard_exit(void) { DBG("Cleanup neard plugin"); g_dbus_remove_watch(btd_get_dbus_connection(), watcher_id); watcher_id = 0; if (neard_service != NULL) unregister_agent(); } BLUETOOTH_PLUGIN_DEFINE(neard, VERSION, BLUETOOTH_PLUGIN_PRIORITY_DEFAULT, neard_init, neard_exit) bluez-5.82/plugins/PaxHeaders/hostname.c0000644000000000000000000000005014447506754015331 xustar0020 atime=1743516498 20 ctime=1743591283 bluez-5.82/plugins/hostname.c0000644000000000000000000001745314447506754015024 0ustar00rootroot// SPDX-License-Identifier: GPL-2.0-or-later /* * * BlueZ - Bluetooth protocol stack for Linux * * Copyright (C) 2004-2010 Marcel Holtmann * * */ #ifdef HAVE_CONFIG_H #include #endif #include #include #include #include #include #include #include "lib/bluetooth.h" #include "lib/sdp.h" #include "gdbus/gdbus.h" #include "src/dbus-common.h" #include "src/plugin.h" #include "src/adapter.h" #include "src/log.h" /* http://www.bluetooth.org/Technical/AssignedNumbers/baseband.htm */ #define MAJOR_CLASS_MISCELLANEOUS 0x00 #define MAJOR_CLASS_COMPUTER 0x01 #define MINOR_CLASS_UNCATEGORIZED 0x00 #define MINOR_CLASS_DESKTOP 0x01 #define MINOR_CLASS_SERVER 0x02 #define MINOR_CLASS_LAPTOP 0x03 #define MINOR_CLASS_HANDHELD 0x04 #define MINOR_CLASS_PALM_SIZED 0x05 #define MINOR_CLASS_WEARABLE 0x06 #define MINOR_CLASS_TABLET 0x07 static uint8_t major_class = MAJOR_CLASS_MISCELLANEOUS; static uint8_t minor_class = MINOR_CLASS_UNCATEGORIZED; static char *pretty_hostname = NULL; static char *static_hostname = NULL; static char *transient_hostname = NULL; static guint hostname_id = 0; /* * Fallback to static hostname only if empty pretty hostname was already * received. */ static const char *get_hostname(void) { if (pretty_hostname) { if (g_str_equal(pretty_hostname, "") == FALSE) return pretty_hostname; if (static_hostname && g_str_equal(static_hostname, "") == FALSE) return static_hostname; if (transient_hostname && g_str_equal(transient_hostname, "") == FALSE) return transient_hostname; } return NULL; } static void update_name(struct btd_adapter *adapter, gpointer user_data) { const char *hostname = get_hostname(); if (hostname == NULL) return; if (btd_adapter_is_default(adapter)) { DBG("name: %s", hostname); adapter_set_name(adapter, hostname); } else { uint16_t index = btd_adapter_get_index(adapter); char *str; /* Avoid "some device #0" names, start at #1 */ str = g_strdup_printf("%s #%u", hostname, index + 1); DBG("name: %s", str); adapter_set_name(adapter, str); g_free(str); } } static void update_class(struct btd_adapter *adapter, gpointer user_data) { if (major_class == MAJOR_CLASS_MISCELLANEOUS) return; DBG("major: 0x%02x minor: 0x%02x", major_class, minor_class); btd_adapter_set_class(adapter, major_class, minor_class); } static const struct { const char *chassis; uint8_t major_class; uint8_t minor_class; } chassis_table[] = { { "desktop", MAJOR_CLASS_COMPUTER, MINOR_CLASS_DESKTOP }, { "server", MAJOR_CLASS_COMPUTER, MINOR_CLASS_SERVER }, { "laptop", MAJOR_CLASS_COMPUTER, MINOR_CLASS_LAPTOP }, { "handset", MAJOR_CLASS_COMPUTER, MINOR_CLASS_HANDHELD }, { "tablet", MAJOR_CLASS_COMPUTER, MINOR_CLASS_TABLET }, { } }; static void property_changed(GDBusProxy *proxy, const char *name, DBusMessageIter *iter, void *user_data) { if (g_str_equal(name, "PrettyHostname") == TRUE) { if (iter == NULL) { g_dbus_proxy_refresh_property(proxy, name); return; } if (dbus_message_iter_get_arg_type(iter) == DBUS_TYPE_STRING) { const char *str; dbus_message_iter_get_basic(iter, &str); DBG("pretty hostname: '%s'", str); g_free(pretty_hostname); pretty_hostname = g_strdup(str); adapter_foreach(update_name, NULL); } } else if (g_str_equal(name, "StaticHostname") == TRUE) { if (iter == NULL) { g_dbus_proxy_refresh_property(proxy, name); return; } if (dbus_message_iter_get_arg_type(iter) == DBUS_TYPE_STRING) { const char *str; dbus_message_iter_get_basic(iter, &str); DBG("static hostname: '%s'", str); g_free(static_hostname); static_hostname = g_strdup(str); adapter_foreach(update_name, NULL); } } else if (g_str_equal(name, "Chassis") == TRUE) { if (iter == NULL) { g_dbus_proxy_refresh_property(proxy, name); return; } if (dbus_message_iter_get_arg_type(iter) == DBUS_TYPE_STRING) { const char *str; int i; dbus_message_iter_get_basic(iter, &str); DBG("chassis: '%s'", str); for (i = 0; chassis_table[i].chassis; i++) { if (strcmp(chassis_table[i].chassis, str)) continue; major_class = chassis_table[i].major_class; minor_class = chassis_table[i].minor_class; adapter_foreach(update_class, NULL); break; } } } } static void read_transient_hostname(void) { struct utsname u; if (uname(&u) != 0) { g_free(transient_hostname); transient_hostname = NULL; DBG("failed to read transient hostname"); return; } g_free(transient_hostname); transient_hostname = g_strdup(u.nodename); DBG("read transient hostname: '%s'", transient_hostname); } static gboolean hostname_cb(GIOChannel *io, GIOCondition cond, gpointer user_data) { DBG("transient hostname changed"); read_transient_hostname(); adapter_foreach(update_class, NULL); return TRUE; } static int hostname_probe(struct btd_adapter *adapter) { DBG(""); update_name(adapter, NULL); update_class(adapter, NULL); return 0; } static void hostname_remove(struct btd_adapter *adapter) { DBG(""); } static struct btd_adapter_driver hostname_driver = { .name = "hostname", .probe = hostname_probe, .remove = hostname_remove, }; static void read_dmi_fallback(void) { char *contents; int i, type; const char *str; if (g_file_get_contents("/sys/class/dmi/id/chassis_type", &contents, NULL, NULL) == FALSE) return; type = atoi(contents); g_free(contents); if (type < 0 || type > 0x1D) return; /* from systemd hostname chassis list */ switch (type) { case 0x3: case 0x4: case 0x6: case 0x7: str = "desktop"; break; case 0x8: case 0x9: case 0xA: case 0xE: str = "laptop"; break; case 0xB: str = "handset"; break; case 0x11: case 0x1C: str = "server"; break; default: return; } DBG("chassis: %s", str); for (i = 0; chassis_table[i].chassis; i++) { if (!strcmp(chassis_table[i].chassis, str)) { major_class = chassis_table[i].major_class; minor_class = chassis_table[i].minor_class; break; } } DBG("major: 0x%02x minor: 0x%02x", major_class, minor_class); } static GDBusClient *hostname_client = NULL; static GDBusProxy *hostname_proxy = NULL; static int hostname_init(void) { DBusConnection *conn = btd_get_dbus_connection(); int fd; int err; read_dmi_fallback(); read_transient_hostname(); hostname_client = g_dbus_client_new(conn, "org.freedesktop.hostname1", "/org/freedesktop/hostname1"); if (!hostname_client) return -EIO; hostname_proxy = g_dbus_proxy_new(hostname_client, "/org/freedesktop/hostname1", "org.freedesktop.hostname1"); if (!hostname_proxy) { g_dbus_client_unref(hostname_client); hostname_client = NULL; return -EIO; } g_dbus_proxy_set_property_watch(hostname_proxy, property_changed, NULL); err = btd_register_adapter_driver(&hostname_driver); if (err < 0) { g_dbus_proxy_unref(hostname_proxy); hostname_proxy = NULL; g_dbus_client_unref(hostname_client); hostname_client = NULL; } fd = open("/proc/sys/kernel/hostname", O_RDONLY); if (fd < 0) { error("open(/proc/sys/kernel/hostname): %s (%d)", strerror(errno), errno); } else { GIOChannel *io = g_io_channel_unix_new(fd); hostname_id = g_io_add_watch(io, G_IO_ERR, hostname_cb, NULL); g_io_channel_unref(io); } return err; } static void hostname_exit(void) { btd_unregister_adapter_driver(&hostname_driver); if (hostname_proxy) { g_dbus_proxy_unref(hostname_proxy); hostname_proxy = NULL; } if (hostname_client) { g_dbus_client_unref(hostname_client); hostname_client = NULL; } if (hostname_id != 0) { g_source_remove(hostname_id); hostname_id = 0; } g_free(pretty_hostname); g_free(static_hostname); g_free(transient_hostname); } BLUETOOTH_PLUGIN_DEFINE(hostname, VERSION, BLUETOOTH_PLUGIN_PRIORITY_DEFAULT, hostname_init, hostname_exit) bluez-5.82/plugins/PaxHeaders/policy.c0000644000000000000000000000005014766002272015000 xustar0020 atime=1743515578 20 ctime=1743591283 bluez-5.82/plugins/policy.c0000644000000000000000000005620314766002272014467 0ustar00rootroot// SPDX-License-Identifier: GPL-2.0-or-later /* * * BlueZ - Bluetooth protocol stack for Linux * * Copyright (C) 2013 Intel Corporation. * * */ #ifdef HAVE_CONFIG_H #include #endif #include #include #include #include #include #include "lib/bluetooth.h" #include "lib/sdp.h" #include "lib/uuid.h" #include "lib/mgmt.h" #include "src/log.h" #include "src/plugin.h" #include "src/adapter.h" #include "src/device.h" #include "src/service.h" #include "src/profile.h" #include "src/btd.h" #include "src/shared/timeout.h" #include "src/shared/util.h" #define CONTROL_CONNECT_TIMEOUT 2 #define SOURCE_RETRY_TIMEOUT 2 #define SINK_RETRY_TIMEOUT SOURCE_RETRY_TIMEOUT #define HS_RETRY_TIMEOUT SOURCE_RETRY_TIMEOUT #define CT_RETRY_TIMEOUT 1 #define TG_RETRY_TIMEOUT CT_RETRY_TIMEOUT #define SOURCE_RETRIES 1 #define SINK_RETRIES SOURCE_RETRIES #define HS_RETRIES SOURCE_RETRIES #define CT_RETRIES 1 #define TG_RETRIES CT_RETRIES struct reconnect_data { struct btd_device *dev; bool reconnect; GSList *services; unsigned int timer; bool active; unsigned int attempt; bool on_resume; }; static const char *default_reconnect[] = { HSP_AG_UUID, HFP_AG_UUID, A2DP_SOURCE_UUID, A2DP_SINK_UUID, NULL }; static char **reconnect_uuids = NULL; static const size_t default_attempts = 7; static size_t reconnect_attempts = 0; static const int default_intervals[] = { 1, 2, 4, 8, 16, 32, 64 }; static int *reconnect_intervals = NULL; static size_t reconnect_intervals_len = 0; static const int default_resume_delay = 2; static int resume_delay; static GSList *reconnects = NULL; static unsigned int service_id = 0; static GSList *devices = NULL; static const bool default_auto_enable = true; static bool auto_enable = false; struct policy_data { struct btd_device *dev; unsigned int source_timer; uint8_t source_retries; unsigned int sink_timer; uint8_t sink_retries; unsigned int ct_timer; uint8_t ct_retries; unsigned int tg_timer; uint8_t tg_retries; unsigned int hs_timer; uint8_t hs_retries; }; static struct reconnect_data *reconnect_find(struct btd_device *dev) { GSList *l; for (l = reconnects; l; l = g_slist_next(l)) { struct reconnect_data *reconnect = l->data; if (reconnect->dev == dev) return reconnect; } return NULL; } static void policy_connect(struct policy_data *data, struct btd_service *service) { struct btd_profile *profile = btd_service_get_profile(service); struct reconnect_data *reconnect; reconnect = reconnect_find(btd_service_get_device(service)); if (reconnect && reconnect->active) return; DBG("%s profile %s", device_get_path(data->dev), profile->name); btd_service_connect(service); } static void policy_disconnect(struct policy_data *data, struct btd_service *service) { struct btd_profile *profile = btd_service_get_profile(service); DBG("%s profile %s", device_get_path(data->dev), profile->name); btd_service_disconnect(service); } static bool policy_connect_ct(gpointer user_data) { struct policy_data *data = user_data; struct btd_service *service; data->ct_timer = 0; data->ct_retries++; service = btd_device_get_service(data->dev, AVRCP_REMOTE_UUID); if (service != NULL) policy_connect(data, service); return FALSE; } static void policy_set_ct_timer(struct policy_data *data, int timeout) { if (data->ct_timer > 0) timeout_remove(data->ct_timer); data->ct_timer = timeout_add_seconds(timeout, policy_connect_ct, data, NULL); } static struct policy_data *find_data(struct btd_device *dev) { GSList *l; for (l = devices; l; l = l->next) { struct policy_data *data = l->data; if (data->dev == dev) return data; } return NULL; } static void policy_remove(void *user_data) { struct policy_data *data = user_data; if (data->source_timer > 0) timeout_remove(data->source_timer); if (data->sink_timer > 0) timeout_remove(data->sink_timer); if (data->ct_timer > 0) timeout_remove(data->ct_timer); if (data->tg_timer > 0) timeout_remove(data->tg_timer); if (data->hs_timer > 0) timeout_remove(data->hs_timer); g_free(data); } static struct policy_data *policy_get_data(struct btd_device *dev) { struct policy_data *data; data = find_data(dev); if (data != NULL) return data; data = g_new0(struct policy_data, 1); data->dev = dev; devices = g_slist_prepend(devices, data); return data; } static bool policy_connect_hs(gpointer user_data) { struct policy_data *data = user_data; struct btd_service *service; data->hs_timer = 0; data->hs_retries++; service = btd_device_get_service(data->dev, HFP_HS_UUID); if (service == NULL) service = btd_device_get_service(data->dev, HSP_HS_UUID); if (service != NULL) policy_connect(data, service); return FALSE; } static void policy_set_hs_timer(struct policy_data *data) { if (data->hs_timer > 0) timeout_remove(data->hs_timer); data->hs_timer = timeout_add_seconds(HS_RETRY_TIMEOUT, policy_connect_hs, data, NULL); } static bool policy_connect_sink(gpointer user_data) { struct policy_data *data = user_data; struct btd_service *service; data->sink_timer = 0; data->sink_retries++; service = btd_device_get_service(data->dev, A2DP_SINK_UUID); if (service != NULL) policy_connect(data, service); return FALSE; } static void policy_set_sink_timer(struct policy_data *data) { if (data->sink_timer > 0) timeout_remove(data->sink_timer); data->sink_timer = timeout_add_seconds(SINK_RETRY_TIMEOUT, policy_connect_sink, data, NULL); } static void sink_cb(struct btd_service *service, btd_service_state_t old_state, btd_service_state_t new_state) { struct btd_device *dev = btd_service_get_device(service); struct policy_data *data; struct btd_service *controller, *hs; controller = btd_device_get_service(dev, AVRCP_REMOTE_UUID); if (controller == NULL) return; data = policy_get_data(dev); switch (new_state) { case BTD_SERVICE_STATE_UNAVAILABLE: if (data->sink_timer > 0) { timeout_remove(data->sink_timer); data->sink_timer = 0; } break; case BTD_SERVICE_STATE_DISCONNECTED: if (old_state == BTD_SERVICE_STATE_CONNECTING) { int err = btd_service_get_error(service); if (err == -EAGAIN) { if (data->sink_retries < SINK_RETRIES) policy_set_sink_timer(data); else data->sink_retries = 0; break; } else if (data->sink_timer > 0) { timeout_remove(data->sink_timer); data->sink_timer = 0; } } if (data->ct_timer > 0) { timeout_remove(data->ct_timer); data->ct_timer = 0; } else if (btd_service_get_state(controller) != BTD_SERVICE_STATE_DISCONNECTED) policy_disconnect(data, controller); break; case BTD_SERVICE_STATE_CONNECTING: break; case BTD_SERVICE_STATE_CONNECTED: if (data->sink_timer > 0) { timeout_remove(data->sink_timer); data->sink_timer = 0; } /* Check if service initiate the connection then proceed * immediatelly otherwise set timer */ if (btd_service_is_initiator(service)) policy_connect(data, controller); else if (btd_service_get_state(controller) != BTD_SERVICE_STATE_CONNECTED) policy_set_ct_timer(data, CONTROL_CONNECT_TIMEOUT); /* Also try connecting HSP/HFP if it is not connected */ hs = btd_device_get_service(dev, HFP_HS_UUID); if (hs) { if (btd_service_is_initiator(service)) policy_connect(data, hs); else if (btd_service_get_state(hs) != BTD_SERVICE_STATE_CONNECTED) policy_set_hs_timer(data); } break; case BTD_SERVICE_STATE_DISCONNECTING: break; } } static void hs_cb(struct btd_service *service, btd_service_state_t old_state, btd_service_state_t new_state) { struct btd_device *dev = btd_service_get_device(service); struct policy_data *data; struct btd_service *sink; /* If the device supports Sink set a timer to connect it as well */ sink = btd_device_get_service(dev, A2DP_SINK_UUID); if (sink == NULL) return; data = policy_get_data(dev); switch (new_state) { case BTD_SERVICE_STATE_UNAVAILABLE: if (data->hs_timer > 0) { timeout_remove(data->hs_timer); data->hs_timer = 0; } break; case BTD_SERVICE_STATE_DISCONNECTED: if (old_state == BTD_SERVICE_STATE_CONNECTING) { int err = btd_service_get_error(service); if (err == -EAGAIN) { if (data->hs_retries < HS_RETRIES) policy_set_hs_timer(data); else data->hs_retries = 0; break; } else if (data->hs_timer > 0) { timeout_remove(data->hs_timer); data->hs_timer = 0; } } break; case BTD_SERVICE_STATE_CONNECTING: break; case BTD_SERVICE_STATE_CONNECTED: /* Check if service initiate the connection then proceed * immediately otherwise set timer */ if (btd_service_is_initiator(service)) policy_connect(data, sink); else if (btd_service_get_state(sink) != BTD_SERVICE_STATE_CONNECTED) policy_set_sink_timer(data); break; case BTD_SERVICE_STATE_DISCONNECTING: break; } } static bool policy_connect_tg(gpointer user_data) { struct policy_data *data = user_data; struct btd_service *service; data->tg_timer = 0; data->tg_retries++; service = btd_device_get_service(data->dev, AVRCP_TARGET_UUID); if (service != NULL) policy_connect(data, service); return FALSE; } static void policy_set_tg_timer(struct policy_data *data, int timeout) { if (data->tg_timer > 0) timeout_remove(data->tg_timer); data->tg_timer = timeout_add_seconds(timeout, policy_connect_tg, data, NULL); } static bool policy_connect_source(gpointer user_data) { struct policy_data *data = user_data; struct btd_service *service; data->source_timer = 0; data->source_retries++; service = btd_device_get_service(data->dev, A2DP_SOURCE_UUID); if (service != NULL) policy_connect(data, service); return FALSE; } static void policy_set_source_timer(struct policy_data *data) { if (data->source_timer > 0) timeout_remove(data->source_timer); data->source_timer = timeout_add_seconds(SOURCE_RETRY_TIMEOUT, policy_connect_source, data, NULL); } static void source_cb(struct btd_service *service, btd_service_state_t old_state, btd_service_state_t new_state) { struct btd_device *dev = btd_service_get_device(service); struct policy_data *data; struct btd_service *target; target = btd_device_get_service(dev, AVRCP_TARGET_UUID); if (target == NULL) return; data = policy_get_data(dev); switch (new_state) { case BTD_SERVICE_STATE_UNAVAILABLE: if (data->source_timer > 0) { timeout_remove(data->source_timer); data->source_timer = 0; } break; case BTD_SERVICE_STATE_DISCONNECTED: if (old_state == BTD_SERVICE_STATE_CONNECTING) { int err = btd_service_get_error(service); if (err == -EAGAIN) { if (data->source_retries < SOURCE_RETRIES) policy_set_source_timer(data); else data->source_retries = 0; break; } else if (data->source_timer > 0) { timeout_remove(data->source_timer); data->source_timer = 0; } } if (data->tg_timer > 0) { timeout_remove(data->tg_timer); data->tg_timer = 0; } else if (btd_service_get_state(target) != BTD_SERVICE_STATE_DISCONNECTED) policy_disconnect(data, target); break; case BTD_SERVICE_STATE_CONNECTING: break; case BTD_SERVICE_STATE_CONNECTED: if (data->source_timer > 0) { timeout_remove(data->source_timer); data->source_timer = 0; } /* Check if service initiate the connection then proceed * immediatelly otherwise set timer */ if (btd_service_is_initiator(service)) policy_connect(data, target); else if (btd_service_get_state(target) != BTD_SERVICE_STATE_CONNECTED) policy_set_tg_timer(data, CONTROL_CONNECT_TIMEOUT); break; case BTD_SERVICE_STATE_DISCONNECTING: break; } } static void controller_cb(struct btd_service *service, btd_service_state_t old_state, btd_service_state_t new_state) { struct btd_device *dev = btd_service_get_device(service); struct policy_data *data; data = find_data(dev); if (data == NULL) return; switch (new_state) { case BTD_SERVICE_STATE_UNAVAILABLE: if (data->ct_timer > 0) { timeout_remove(data->ct_timer); data->ct_timer = 0; } break; case BTD_SERVICE_STATE_DISCONNECTED: if (old_state == BTD_SERVICE_STATE_CONNECTING) { int err = btd_service_get_error(service); if (err == -EAGAIN) { if (data->ct_retries < CT_RETRIES) policy_set_ct_timer(data, CT_RETRY_TIMEOUT); else data->ct_retries = 0; break; } else if (data->ct_timer > 0) { timeout_remove(data->ct_timer); data->ct_timer = 0; } } else if (old_state == BTD_SERVICE_STATE_CONNECTED) { data->ct_retries = 0; } break; case BTD_SERVICE_STATE_CONNECTING: break; case BTD_SERVICE_STATE_CONNECTED: if (data->ct_timer > 0) { timeout_remove(data->ct_timer); data->ct_timer = 0; } break; case BTD_SERVICE_STATE_DISCONNECTING: break; } } static void target_cb(struct btd_service *service, btd_service_state_t old_state, btd_service_state_t new_state) { struct btd_device *dev = btd_service_get_device(service); struct policy_data *data; data = find_data(dev); if (data == NULL) return; switch (new_state) { case BTD_SERVICE_STATE_UNAVAILABLE: if (data->tg_timer > 0) { timeout_remove(data->tg_timer); data->tg_timer = 0; } break; case BTD_SERVICE_STATE_DISCONNECTED: if (old_state == BTD_SERVICE_STATE_CONNECTING) { int err = btd_service_get_error(service); if (err == -EAGAIN) { if (data->tg_retries < TG_RETRIES) policy_set_tg_timer(data, TG_RETRY_TIMEOUT); else data->tg_retries = 0; break; } else if (data->tg_timer > 0) { timeout_remove(data->tg_timer); data->tg_timer = 0; } } else if (old_state == BTD_SERVICE_STATE_CONNECTED) { data->tg_retries = 0; } break; case BTD_SERVICE_STATE_CONNECTING: break; case BTD_SERVICE_STATE_CONNECTED: if (data->tg_timer > 0) { timeout_remove(data->tg_timer); data->tg_timer = 0; } break; case BTD_SERVICE_STATE_DISCONNECTING: break; } } static void reconnect_reset(struct reconnect_data *reconnect) { reconnect->attempt = 0; reconnect->active = false; if (reconnect->timer > 0) { timeout_remove(reconnect->timer); reconnect->timer = 0; } } static bool reconnect_match(const char *uuid) { char **str; if (!reconnect_uuids) return false; for (str = reconnect_uuids; *str; str++) { if (!bt_uuid_strcmp(uuid, *str)) return true; } return false; } static struct reconnect_data *reconnect_add(struct btd_service *service) { struct btd_device *dev = btd_service_get_device(service); struct reconnect_data *reconnect; reconnect = reconnect_find(dev); if (!reconnect) { reconnect = g_new0(struct reconnect_data, 1); reconnect->dev = dev; reconnects = g_slist_append(reconnects, reconnect); } if (g_slist_find(reconnect->services, service)) return reconnect; reconnect->services = g_slist_append(reconnect->services, btd_service_ref(service)); return reconnect; } static void reconnect_destroy(gpointer data) { struct reconnect_data *reconnect = data; if (reconnect->timer > 0) timeout_remove(reconnect->timer); g_slist_free_full(reconnect->services, (GDestroyNotify) btd_service_unref); g_free(reconnect); } static void reconnect_remove(struct btd_service *service) { struct btd_device *dev = btd_service_get_device(service); struct reconnect_data *reconnect; GSList *l; reconnect = reconnect_find(dev); if (!reconnect) return; l = g_slist_find(reconnect->services, service); if (!l) return; reconnect->services = g_slist_delete_link(reconnect->services, l); btd_service_unref(service); if (reconnect->services) return; reconnects = g_slist_remove(reconnects, reconnect); if (reconnect->timer > 0) timeout_remove(reconnect->timer); g_free(reconnect); } static void service_cb(struct btd_service *service, btd_service_state_t old_state, btd_service_state_t new_state, void *user_data) { struct btd_profile *profile = btd_service_get_profile(service); struct reconnect_data *reconnect; if (g_str_equal(profile->remote_uuid, A2DP_SINK_UUID)) sink_cb(service, old_state, new_state); else if (g_str_equal(profile->remote_uuid, A2DP_SOURCE_UUID)) source_cb(service, old_state, new_state); else if (g_str_equal(profile->remote_uuid, AVRCP_REMOTE_UUID)) controller_cb(service, old_state, new_state); else if (g_str_equal(profile->remote_uuid, AVRCP_TARGET_UUID)) target_cb(service, old_state, new_state); else if (g_str_equal(profile->remote_uuid, HFP_HS_UUID) || g_str_equal(profile->remote_uuid, HSP_HS_UUID)) hs_cb(service, old_state, new_state); /* * Return if the reconnection feature is not enabled (all * subsequent code in this function is about that). */ if (!reconnect_uuids || !reconnect_uuids[0]) return; /* * We're only interested in reconnecting profiles which have set * auto_connect to true. */ if (!profile->auto_connect) return; /* * If the service went away remove it from the reconnection * tracking. The function will remove the entire tracking data * if this was the last service for the device. */ if (new_state == BTD_SERVICE_STATE_UNAVAILABLE) { reconnect_remove(service); return; } if (new_state != BTD_SERVICE_STATE_CONNECTED) return; /* * Add an entry to track reconnections. The function will return * an existing entry if there is one. */ reconnect = reconnect_add(service); reconnect->active = false; /* * Should this device be reconnected? A matching UUID might not * be the first profile that's connected so we might have an * entry but with the reconnect flag set to false. */ if (!reconnect->reconnect) reconnect->reconnect = reconnect_match(profile->remote_uuid); DBG("Added %s reconnect %u", profile->name, reconnect->reconnect); } static bool reconnect_timeout(gpointer data) { struct reconnect_data *reconnect = data; int err; DBG("Reconnecting profiles"); /* Mark the GSource as invalid */ reconnect->timer = 0; /* Mark any reconnect on resume as handled */ reconnect->on_resume = false; err = btd_device_connect_services(reconnect->dev, reconnect->services); if (err < 0) { error("Reconnecting services failed: %s (%d)", strerror(-err), -err); reconnect_reset(reconnect); return FALSE; } reconnect->attempt++; return FALSE; } static void reconnect_set_timer(struct reconnect_data *reconnect, int timeout) { static int interval_timeout = 0; reconnect->active = true; if (reconnect->attempt < reconnect_intervals_len) interval_timeout = reconnect_intervals[reconnect->attempt]; if (timeout < 0) timeout = interval_timeout; DBG("attempt %u/%zu %d seconds", reconnect->attempt + 1, reconnect_attempts, timeout); reconnect->timer = timeout_add_seconds(timeout, reconnect_timeout, reconnect, NULL); } static void disconnect_cb(struct btd_device *dev, uint8_t reason) { struct reconnect_data *reconnect; DBG("reason %u", reason); /* Only attempt reconnect for the following reasons */ if (reason != MGMT_DEV_DISCONN_TIMEOUT && reason != MGMT_DEV_DISCONN_LOCAL_HOST_SUSPEND) return; reconnect = reconnect_find(dev); if (!reconnect || !reconnect->reconnect) return; reconnect_reset(reconnect); DBG("Device %s identified for auto-reconnection", device_get_path(dev)); switch (reason) { case MGMT_DEV_DISCONN_LOCAL_HOST_SUSPEND: if (btd_device_get_service(dev, A2DP_SINK_UUID)) { DBG("%s configured to reconnect on resume", device_get_path(dev)); reconnect->on_resume = true; /* If the kernel supports resume events, it is * preferable to set the reconnect timer there as it is * a more predictable delay. */ if (!btd_has_kernel_features(KERNEL_HAS_RESUME_EVT)) reconnect_set_timer(reconnect, resume_delay); } break; case MGMT_DEV_DISCONN_TIMEOUT: reconnect_set_timer(reconnect, -1); break; default: DBG("Developer error. Reason = %d", reason); break; } } static void policy_adapter_resume(struct btd_adapter *adapter) { GSList *l; /* Check if devices on this adapter need to be reconnected on resume */ for (l = reconnects; l; l = g_slist_next(l)) { struct reconnect_data *reconnect = l->data; if (reconnect->on_resume && device_get_adapter(reconnect->dev) == adapter) { reconnect_set_timer(reconnect, resume_delay); } } } static void conn_fail_cb(struct btd_device *dev, uint8_t status) { struct reconnect_data *reconnect; DBG("status %u", status); reconnect = reconnect_find(dev); if (!reconnect || !reconnect->reconnect) return; if (!reconnect->active) return; /* Give up if we were powered off */ if (status == MGMT_STATUS_NOT_POWERED) { reconnect_reset(reconnect); return; } /* Reset if ReconnectAttempts was reached */ if (reconnect->attempt == reconnect_attempts) { reconnect_reset(reconnect); return; } reconnect_set_timer(reconnect, -1); } static int policy_adapter_probe(struct btd_adapter *adapter) { DBG(""); if (auto_enable) btd_adapter_restore_powered(adapter); return 0; } static struct btd_adapter_driver policy_driver = { .name = "policy", .probe = policy_adapter_probe, .resume = policy_adapter_resume, }; static int policy_init(void) { GError *gerr = NULL; GKeyFile *conf; service_id = btd_service_add_state_cb(service_cb, NULL); conf = btd_get_main_conf(); if (!conf) { reconnect_uuids = g_strdupv((char **) default_reconnect); reconnect_attempts = default_attempts; reconnect_intervals_len = sizeof(default_intervals) / sizeof(*reconnect_intervals); reconnect_intervals = util_memdup(default_intervals, sizeof(default_intervals)); auto_enable = default_auto_enable; goto done; } g_key_file_set_list_separator(conf, ','); reconnect_uuids = g_key_file_get_string_list(conf, "Policy", "ReconnectUUIDs", NULL, &gerr); if (gerr) { g_clear_error(&gerr); reconnect_uuids = g_strdupv((char **) default_reconnect); } reconnect_attempts = g_key_file_get_integer(conf, "Policy", "ReconnectAttempts", &gerr); if (gerr) { g_clear_error(&gerr); reconnect_attempts = default_attempts; } reconnect_intervals = g_key_file_get_integer_list(conf, "Policy", "ReconnectIntervals", (size_t *) &reconnect_intervals_len, &gerr); if (gerr) { g_clear_error(&gerr); reconnect_intervals_len = sizeof(default_intervals) / sizeof(*reconnect_intervals); reconnect_intervals = util_memdup(default_intervals, sizeof(default_intervals)); } auto_enable = g_key_file_get_boolean(conf, "Policy", "AutoEnable", &gerr); if (gerr) { g_clear_error(&gerr); auto_enable = default_auto_enable; } resume_delay = g_key_file_get_integer( conf, "Policy", "ResumeDelay", &gerr); if (gerr) { g_clear_error(&gerr); resume_delay = default_resume_delay; } done: if (reconnect_uuids && reconnect_uuids[0] && reconnect_attempts) { btd_add_disconnect_cb(disconnect_cb); btd_add_conn_fail_cb(conn_fail_cb); } btd_register_adapter_driver(&policy_driver); return 0; } static void policy_exit(void) { btd_remove_disconnect_cb(disconnect_cb); btd_remove_conn_fail_cb(conn_fail_cb); if (reconnect_uuids) g_strfreev(reconnect_uuids); free(reconnect_intervals); g_slist_free_full(reconnects, reconnect_destroy); g_slist_free_full(devices, policy_remove); btd_service_remove_state_cb(service_id); btd_unregister_adapter_driver(&policy_driver); } BLUETOOTH_PLUGIN_DEFINE(policy, VERSION, BLUETOOTH_PLUGIN_PRIORITY_DEFAULT, policy_init, policy_exit) bluez-5.82/plugins/PaxHeaders/admin.c0000644000000000000000000000005014471706457014602 xustar0020 atime=1743516870 20 ctime=1743591284 bluez-5.82/plugins/admin.c0000644000000000000000000003501714471706457014271 0ustar00rootroot// SPDX-License-Identifier: LGPL-2.1-or-later /* * * BlueZ - Bluetooth protocol stack for Linux * * Copyright (C) 2021 Google LLC * * */ #ifdef HAVE_CONFIG_H #include #endif #include #include #include #include #include #include #include "lib/bluetooth.h" #include "lib/uuid.h" #include "src/adapter.h" #include "src/dbus-common.h" #include "src/device.h" #include "src/error.h" #include "src/log.h" #include "src/plugin.h" #include "src/textfile.h" #include "src/shared/queue.h" #define ADMIN_POLICY_SET_INTERFACE "org.bluez.AdminPolicySet1" #define ADMIN_POLICY_STATUS_INTERFACE "org.bluez.AdminPolicyStatus1" #define ADMIN_POLICY_STORAGE STORAGEDIR "/admin_policy_settings" #define DBUS_BLUEZ_SERVICE "org.bluez" #define BTD_DEVICE_INTERFACE "org.bluez.Device1" static DBusConnection *dbus_conn; static struct queue *devices; /* List of struct device_data objects */ /* |policy_data| has the same life cycle as btd_adapter */ static struct btd_admin_policy { struct btd_adapter *adapter; uint16_t adapter_id; struct queue *service_allowlist; } *policy_data = NULL; struct device_data { struct btd_device *device; char *path; bool affected; }; static struct btd_admin_policy *admin_policy_new(struct btd_adapter *adapter) { struct btd_admin_policy *admin_policy = NULL; admin_policy = g_try_malloc(sizeof(*admin_policy)); if (!admin_policy) { btd_error(btd_adapter_get_index(adapter), "Failed to allocate memory for admin_policy"); return NULL; } admin_policy->adapter = adapter; admin_policy->adapter_id = btd_adapter_get_index(adapter); admin_policy->service_allowlist = queue_new(); return admin_policy; } static void free_service_allowlist(struct queue *q) { queue_destroy(q, free); } static void admin_policy_free(void *data) { struct btd_admin_policy *admin_policy = data; free_service_allowlist(admin_policy->service_allowlist); g_free(admin_policy); } static void admin_policy_destroy(struct btd_admin_policy *admin_policy) { const char *path = adapter_get_path(admin_policy->adapter); g_dbus_unregister_interface(dbus_conn, path, ADMIN_POLICY_SET_INTERFACE); g_dbus_unregister_interface(dbus_conn, path, ADMIN_POLICY_STATUS_INTERFACE); admin_policy_free(admin_policy); } static bool uuid_match(const void *data, const void *match_data) { const bt_uuid_t *uuid = data; const bt_uuid_t *match_uuid = match_data; return bt_uuid_cmp(uuid, match_uuid) == 0; } static struct queue *parse_allow_service_list(struct btd_adapter *adapter, DBusMessage *msg) { DBusMessageIter iter, arr_iter; struct queue *uuid_list = NULL; dbus_message_iter_init(msg, &iter); if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_ARRAY) return NULL; uuid_list = queue_new(); dbus_message_iter_recurse(&iter, &arr_iter); do { const int type = dbus_message_iter_get_arg_type(&arr_iter); char *uuid_param; bt_uuid_t *uuid; if (type == DBUS_TYPE_INVALID) break; if (type != DBUS_TYPE_STRING) goto failed; dbus_message_iter_get_basic(&arr_iter, &uuid_param); uuid = g_try_malloc(sizeof(*uuid)); if (!uuid) goto failed; if (bt_string_to_uuid(uuid, uuid_param)) { g_free(uuid); goto failed; } dbus_message_iter_next(&arr_iter); if (queue_find(uuid_list, uuid_match, uuid)) { g_free(uuid); continue; } queue_push_head(uuid_list, uuid); } while (true); return uuid_list; failed: queue_destroy(uuid_list, g_free); return NULL; } static bool service_allowlist_set(struct btd_admin_policy *admin_policy, struct queue *uuid_list) { struct btd_adapter *adapter = admin_policy->adapter; if (!btd_adapter_set_allowed_uuids(adapter, uuid_list)) return false; free_service_allowlist(admin_policy->service_allowlist); admin_policy->service_allowlist = uuid_list; return true; } static void update_device_affected(void *data, void *user_data) { struct device_data *dev_data = data; bool affected; if (!dev_data) { error("Unexpected NULL device_data when updating device"); return; } affected = !btd_device_all_services_allowed(dev_data->device); if (affected == dev_data->affected) return; dev_data->affected = affected; g_dbus_emit_property_changed(dbus_conn, dev_data->path, ADMIN_POLICY_STATUS_INTERFACE, "AffectedByPolicy"); } static void free_uuid_strings(char **uuid_strs, gsize num) { gsize i; for (i = 0; i < num; i++) g_free(uuid_strs[i]); g_free(uuid_strs); } static char **new_uuid_strings(struct queue *allowlist, gsize *num) { const struct queue_entry *entry = NULL; bt_uuid_t *uuid = NULL; char **uuid_strs = NULL; gsize i = 0, allowlist_num; allowlist_num = queue_length(allowlist); if (!allowlist_num) { *num = 0; return NULL; } /* Set num to a non-zero number so that whoever call this could know if * this function success or not */ *num = 1; uuid_strs = g_try_malloc_n(allowlist_num, sizeof(char *)); if (!uuid_strs) return NULL; for (entry = queue_get_entries(allowlist); entry != NULL; entry = entry->next) { uuid = entry->data; uuid_strs[i] = g_try_malloc0(MAX_LEN_UUID_STR * sizeof(char)); if (!uuid_strs[i]) goto failed; bt_uuid_to_string(uuid, uuid_strs[i], MAX_LEN_UUID_STR); i++; } *num = allowlist_num; return uuid_strs; failed: free_uuid_strings(uuid_strs, i); return NULL; } static void store_policy_settings(struct btd_admin_policy *admin_policy) { GKeyFile *key_file = NULL; GError *gerr = NULL; char *filename = ADMIN_POLICY_STORAGE; char *key_file_data = NULL; char **uuid_strs = NULL; gsize length, num_uuids; key_file = g_key_file_new(); uuid_strs = new_uuid_strings(admin_policy->service_allowlist, &num_uuids); if (!uuid_strs && num_uuids) { btd_error(admin_policy->adapter_id, "Failed to allocate uuid strings"); goto failed; } g_key_file_set_string_list(key_file, "General", "ServiceAllowlist", (const gchar * const *)uuid_strs, num_uuids); if (create_file(ADMIN_POLICY_STORAGE, 0600) < 0) { btd_error(admin_policy->adapter_id, "create %s failed, %s", filename, strerror(errno)); goto failed; } key_file_data = g_key_file_to_data(key_file, &length, NULL); if (!g_file_set_contents(ADMIN_POLICY_STORAGE, key_file_data, length, &gerr)) { error("Unable set contents for %s: (%s)", ADMIN_POLICY_STORAGE, gerr->message); g_error_free(gerr); } g_free(key_file_data); free_uuid_strings(uuid_strs, num_uuids); failed: g_key_file_free(key_file); } static void key_file_load_service_allowlist(GKeyFile *key_file, struct btd_admin_policy *admin_policy) { GError *gerr = NULL; struct queue *uuid_list = NULL; gchar **uuids = NULL; gsize num, i; uuids = g_key_file_get_string_list(key_file, "General", "ServiceAllowlist", &num, &gerr); if (gerr) { btd_error(admin_policy->adapter_id, "Failed to load ServiceAllowlist"); g_error_free(gerr); return; } uuid_list = queue_new(); for (i = 0; i < num; i++) { bt_uuid_t *uuid = g_try_malloc(sizeof(*uuid)); if (!uuid) goto failed; if (bt_string_to_uuid(uuid, uuids[i])) { btd_error(admin_policy->adapter_id, "Failed to convert '%s' to uuid struct", *uuids); g_free(uuid); goto failed; } queue_push_tail(uuid_list, uuid); } if (!service_allowlist_set(admin_policy, uuid_list)) goto failed; g_strfreev(uuids); return; failed: g_strfreev(uuids); free_service_allowlist(uuid_list); } static void load_policy_settings(struct btd_admin_policy *admin_policy) { GKeyFile *key_file; GError *gerr = NULL; char *filename = ADMIN_POLICY_STORAGE; struct stat st; if (stat(filename, &st) < 0) store_policy_settings(policy_data); key_file = g_key_file_new(); if (!g_key_file_load_from_file(key_file, filename, 0, &gerr)) { error("Unable to load key file from %s: (%s)", filename, gerr->message); g_error_free(gerr); } key_file_load_service_allowlist(key_file, admin_policy); g_key_file_free(key_file); } static DBusMessage *set_service_allowlist(DBusConnection *conn, DBusMessage *msg, void *user_data) { struct btd_admin_policy *admin_policy = user_data; struct btd_adapter *adapter = admin_policy->adapter; struct queue *uuid_list = NULL; const char *sender = dbus_message_get_sender(msg); DBG("sender %s", sender); /* Parse parameters */ uuid_list = parse_allow_service_list(adapter, msg); if (!uuid_list) { btd_error(admin_policy->adapter_id, "Failed on parsing allowed service list"); return btd_error_invalid_args(msg); } if (service_allowlist_set(admin_policy, uuid_list)) { store_policy_settings(admin_policy); } else { free_service_allowlist(uuid_list); return btd_error_failed(msg, "service_allowlist_set failed"); } g_dbus_emit_property_changed(dbus_conn, adapter_get_path(policy_data->adapter), ADMIN_POLICY_STATUS_INTERFACE, "ServiceAllowList"); queue_foreach(devices, update_device_affected, NULL); return dbus_message_new_method_return(msg); } static const GDBusMethodTable admin_policy_adapter_methods[] = { { GDBUS_METHOD("SetServiceAllowList", GDBUS_ARGS({ "UUIDs", "as" }), NULL, set_service_allowlist) }, { } }; static void append_service_uuid(void *data, void *user_data) { bt_uuid_t *uuid = data; DBusMessageIter *entry = user_data; char uuid_str[MAX_LEN_UUID_STR]; const char *uuid_str_ptr = uuid_str; if (!uuid) { error("Unexpected NULL uuid data in service_allowlist"); return; } bt_uuid_to_string(uuid, uuid_str, MAX_LEN_UUID_STR); dbus_message_iter_append_basic(entry, DBUS_TYPE_STRING, &uuid_str_ptr); } static gboolean property_get_service_allowlist( const GDBusPropertyTable *property, DBusMessageIter *iter, void *user_data) { struct btd_admin_policy *admin_policy = user_data; DBusMessageIter entry; dbus_message_iter_open_container(iter, DBUS_TYPE_ARRAY, DBUS_TYPE_STRING_AS_STRING, &entry); queue_foreach(admin_policy->service_allowlist, append_service_uuid, &entry); dbus_message_iter_close_container(iter, &entry); return TRUE; } static const GDBusPropertyTable admin_policy_adapter_properties[] = { { "ServiceAllowList", "as", property_get_service_allowlist }, { } }; static bool device_data_match(const void *a, const void *b) { const struct device_data *data = a; const struct btd_device *dev = b; if (!data) { error("Unexpected NULL device_data"); return false; } return data->device == dev; } static gboolean property_get_affected_by_policy( const GDBusPropertyTable *property, DBusMessageIter *iter, void *user_data) { struct device_data *data = user_data; if (!data) { error("Unexpected error: device_data is NULL"); return FALSE; } dbus_message_iter_append_basic(iter, DBUS_TYPE_BOOLEAN, &data->affected); return TRUE; } static const GDBusPropertyTable admin_policy_device_properties[] = { { "AffectedByPolicy", "b", property_get_affected_by_policy }, { } }; static void free_device_data(void *data) { struct device_data *device_data = data; g_free(device_data->path); g_free(device_data); } static void remove_device_data(void *data) { struct device_data *device_data = data; DBG("device_data for %s removing", device_data->path); queue_remove(devices, device_data); free_device_data(device_data); } static int admin_policy_adapter_probe(struct btd_adapter *adapter) { const char *adapter_path; if (!devices) devices = queue_new(); if (policy_data) { btd_warn(policy_data->adapter_id, "Policy data already exists"); policy_data = NULL; } policy_data = admin_policy_new(adapter); if (!policy_data) return -ENOMEM; load_policy_settings(policy_data); adapter_path = adapter_get_path(adapter); if (!g_dbus_register_interface(dbus_conn, adapter_path, ADMIN_POLICY_SET_INTERFACE, admin_policy_adapter_methods, NULL, NULL, policy_data, NULL)) { btd_error(policy_data->adapter_id, "Admin Policy Set interface init failed on path %s", adapter_path); return -EINVAL; } btd_info(policy_data->adapter_id, "Admin Policy Set interface registered"); if (!g_dbus_register_interface(dbus_conn, adapter_path, ADMIN_POLICY_STATUS_INTERFACE, NULL, NULL, admin_policy_adapter_properties, policy_data, NULL)) { btd_error(policy_data->adapter_id, "Admin Policy Status interface init failed on path %s", adapter_path); return -EINVAL; } btd_info(policy_data->adapter_id, "Admin Policy Status interface registered"); return 0; } static void admin_policy_device_added(struct btd_adapter *adapter, struct btd_device *device) { struct device_data *data; if (queue_find(devices, device_data_match, device)) return; data = g_new0(struct device_data, 1); if (!data) { btd_error(btd_adapter_get_index(adapter), "Failed to allocate memory for device_data"); return; } data->device = device; data->path = g_strdup(device_get_path(device)); data->affected = !btd_device_all_services_allowed(data->device); if (!g_dbus_register_interface(dbus_conn, data->path, ADMIN_POLICY_STATUS_INTERFACE, NULL, NULL, admin_policy_device_properties, data, remove_device_data)) { btd_error(btd_adapter_get_index(adapter), "Admin Policy Status interface init failed on path %s", device_get_path(device)); free_device_data(data); return; } queue_push_tail(devices, data); DBG("device_data for %s added", data->path); } static void unregister_device_data(void *data, void *user_data) { struct device_data *dev_data = data; g_dbus_unregister_interface(dbus_conn, dev_data->path, ADMIN_POLICY_STATUS_INTERFACE); } static void admin_policy_device_removed(struct btd_adapter *adapter, struct btd_device *device) { struct device_data *data; data = queue_find(devices, device_data_match, device); if (data) unregister_device_data(data, NULL); } static void admin_policy_remove(struct btd_adapter *adapter) { DBG(""); queue_foreach(devices, unregister_device_data, NULL); queue_destroy(devices, g_free); devices = NULL; if (policy_data) { admin_policy_destroy(policy_data); policy_data = NULL; } } static struct btd_adapter_driver admin_policy_driver = { .name = "admin_policy", .probe = admin_policy_adapter_probe, .resume = NULL, .remove = admin_policy_remove, .device_resolved = admin_policy_device_added, .device_removed = admin_policy_device_removed, .experimental = true, }; static int admin_init(void) { dbus_conn = btd_get_dbus_connection(); return btd_register_adapter_driver(&admin_policy_driver); } static void admin_exit(void) { btd_unregister_adapter_driver(&admin_policy_driver); } BLUETOOTH_PLUGIN_DEFINE(admin, VERSION, BLUETOOTH_PLUGIN_PRIORITY_DEFAULT, admin_init, admin_exit) bluez-5.82/PaxHeaders/ltmain.sh0000644000000000000000000000005014773211367013501 xustar0020 atime=1743590182 20 ctime=1743591275 bluez-5.82/ltmain.sh0000755000000000000000000121236114773211367013173 0ustar00rootroot#! /usr/bin/env sh ## DO NOT EDIT - This file generated from ./build-aux/ltmain.in ## by inline-source v2019-02-19.15 # libtool (GNU libtool) 2.4.7 # Provide generalized library-building support services. # Written by Gordon Matzigkeit , 1996 # Copyright (C) 1996-2019, 2021-2022 Free Software Foundation, Inc. # This is free software; see the source for copying conditions. There is NO # warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. # GNU Libtool is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # As a special exception to the GNU General Public License, # if you distribute this file as part of a program or library that # is built using GNU Libtool, you may include this file under the # same distribution terms that you use for the rest of that program. # # GNU Libtool is distributed in the hope that it will be useful, but # WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program. If not, see . PROGRAM=libtool PACKAGE=libtool VERSION="2.4.7 Debian-2.4.7-8" package_revision=2.4.7 ## ------ ## ## Usage. ## ## ------ ## # Run './libtool --help' for help with using this script from the # command line. ## ------------------------------- ## ## User overridable command paths. ## ## ------------------------------- ## # After configure completes, it has a better idea of some of the # shell tools we need than the defaults used by the functions shared # with bootstrap, so set those here where they can still be over- # ridden by the user, but otherwise take precedence. : ${AUTOCONF="autoconf"} : ${AUTOMAKE="automake"} ## -------------------------- ## ## Source external libraries. ## ## -------------------------- ## # Much of our low-level functionality needs to be sourced from external # libraries, which are installed to $pkgauxdir. # Set a version string for this script. scriptversion=2019-02-19.15; # UTC # General shell script boiler plate, and helper functions. # Written by Gary V. Vaughan, 2004 # This is free software. There is NO warranty; not even for # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. # # Copyright (C) 2004-2019, 2021 Bootstrap Authors # # This file is dual licensed under the terms of the MIT license # , and GPL version 2 or later # . You must apply one of # these licenses when using or redistributing this software or any of # the files within it. See the URLs above, or the file `LICENSE` # included in the Bootstrap distribution for the full license texts. # Please report bugs or propose patches to: # ## ------ ## ## Usage. ## ## ------ ## # Evaluate this file near the top of your script to gain access to # the functions and variables defined here: # # . `echo "$0" | ${SED-sed} 's|[^/]*$||'`/build-aux/funclib.sh # # If you need to override any of the default environment variable # settings, do that before evaluating this file. ## -------------------- ## ## Shell normalisation. ## ## -------------------- ## # Some shells need a little help to be as Bourne compatible as possible. # Before doing anything else, make sure all that help has been provided! DUALCASE=1; export DUALCASE # for MKS sh if test -n "${ZSH_VERSION+set}" && (emulate sh) >/dev/null 2>&1; then : emulate sh NULLCMD=: # Pre-4.2 versions of Zsh do word splitting on ${1+"$@"}, which # is contrary to our usage. Disable this feature. alias -g '${1+"$@"}'='"$@"' setopt NO_GLOB_SUBST else case `(set -o) 2>/dev/null` in *posix*) set -o posix ;; esac fi # NLS nuisances: We save the old values in case they are required later. _G_user_locale= _G_safe_locale= for _G_var in LANG LANGUAGE LC_ALL LC_CTYPE LC_COLLATE LC_MESSAGES do eval "if test set = \"\${$_G_var+set}\"; then save_$_G_var=\$$_G_var $_G_var=C export $_G_var _G_user_locale=\"$_G_var=\\\$save_\$_G_var; \$_G_user_locale\" _G_safe_locale=\"$_G_var=C; \$_G_safe_locale\" fi" done # These NLS vars are set unconditionally (bootstrap issue #24). Unset those # in case the environment reset is needed later and the $save_* variant is not # defined (see the code above). LC_ALL=C LANGUAGE=C export LANGUAGE LC_ALL # Make sure IFS has a sensible default sp=' ' nl=' ' IFS="$sp $nl" # There are apparently some retarded systems that use ';' as a PATH separator! if test "${PATH_SEPARATOR+set}" != set; then PATH_SEPARATOR=: (PATH='/bin;/bin'; FPATH=$PATH; sh -c :) >/dev/null 2>&1 && { (PATH='/bin:/bin'; FPATH=$PATH; sh -c :) >/dev/null 2>&1 || PATH_SEPARATOR=';' } fi # func_unset VAR # -------------- # Portably unset VAR. # In some shells, an 'unset VAR' statement leaves a non-zero return # status if VAR is already unset, which might be problematic if the # statement is used at the end of a function (thus poisoning its return # value) or when 'set -e' is active (causing even a spurious abort of # the script in this case). func_unset () { { eval $1=; (eval unset $1) >/dev/null 2>&1 && eval unset $1 || : ; } } # Make sure CDPATH doesn't cause `cd` commands to output the target dir. func_unset CDPATH # Make sure ${,E,F}GREP behave sanely. func_unset GREP_OPTIONS ## ------------------------- ## ## Locate command utilities. ## ## ------------------------- ## # func_executable_p FILE # ---------------------- # Check that FILE is an executable regular file. func_executable_p () { test -f "$1" && test -x "$1" } # func_path_progs PROGS_LIST CHECK_FUNC [PATH] # -------------------------------------------- # Search for either a program that responds to --version with output # containing "GNU", or else returned by CHECK_FUNC otherwise, by # trying all the directories in PATH with each of the elements of # PROGS_LIST. # # CHECK_FUNC should accept the path to a candidate program, and # set $func_check_prog_result if it truncates its output less than # $_G_path_prog_max characters. func_path_progs () { _G_progs_list=$1 _G_check_func=$2 _G_PATH=${3-"$PATH"} _G_path_prog_max=0 _G_path_prog_found=false _G_save_IFS=$IFS; IFS=${PATH_SEPARATOR-:} for _G_dir in $_G_PATH; do IFS=$_G_save_IFS test -z "$_G_dir" && _G_dir=. for _G_prog_name in $_G_progs_list; do for _exeext in '' .EXE; do _G_path_prog=$_G_dir/$_G_prog_name$_exeext func_executable_p "$_G_path_prog" || continue case `"$_G_path_prog" --version 2>&1` in *GNU*) func_path_progs_result=$_G_path_prog _G_path_prog_found=: ;; *) $_G_check_func $_G_path_prog func_path_progs_result=$func_check_prog_result ;; esac $_G_path_prog_found && break 3 done done done IFS=$_G_save_IFS test -z "$func_path_progs_result" && { echo "no acceptable sed could be found in \$PATH" >&2 exit 1 } } # We want to be able to use the functions in this file before configure # has figured out where the best binaries are kept, which means we have # to search for them ourselves - except when the results are already set # where we skip the searches. # Unless the user overrides by setting SED, search the path for either GNU # sed, or the sed that truncates its output the least. test -z "$SED" && { _G_sed_script=s/aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa/bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb/ for _G_i in 1 2 3 4 5 6 7; do _G_sed_script=$_G_sed_script$nl$_G_sed_script done echo "$_G_sed_script" 2>/dev/null | sed 99q >conftest.sed _G_sed_script= func_check_prog_sed () { _G_path_prog=$1 _G_count=0 printf 0123456789 >conftest.in while : do cat conftest.in conftest.in >conftest.tmp mv conftest.tmp conftest.in cp conftest.in conftest.nl echo '' >> conftest.nl "$_G_path_prog" -f conftest.sed conftest.out 2>/dev/null || break diff conftest.out conftest.nl >/dev/null 2>&1 || break _G_count=`expr $_G_count + 1` if test "$_G_count" -gt "$_G_path_prog_max"; then # Best one so far, save it but keep looking for a better one func_check_prog_result=$_G_path_prog _G_path_prog_max=$_G_count fi # 10*(2^10) chars as input seems more than enough test 10 -lt "$_G_count" && break done rm -f conftest.in conftest.tmp conftest.nl conftest.out } func_path_progs "sed gsed" func_check_prog_sed "$PATH:/usr/xpg4/bin" rm -f conftest.sed SED=$func_path_progs_result } # Unless the user overrides by setting GREP, search the path for either GNU # grep, or the grep that truncates its output the least. test -z "$GREP" && { func_check_prog_grep () { _G_path_prog=$1 _G_count=0 _G_path_prog_max=0 printf 0123456789 >conftest.in while : do cat conftest.in conftest.in >conftest.tmp mv conftest.tmp conftest.in cp conftest.in conftest.nl echo 'GREP' >> conftest.nl "$_G_path_prog" -e 'GREP$' -e '-(cannot match)-' conftest.out 2>/dev/null || break diff conftest.out conftest.nl >/dev/null 2>&1 || break _G_count=`expr $_G_count + 1` if test "$_G_count" -gt "$_G_path_prog_max"; then # Best one so far, save it but keep looking for a better one func_check_prog_result=$_G_path_prog _G_path_prog_max=$_G_count fi # 10*(2^10) chars as input seems more than enough test 10 -lt "$_G_count" && break done rm -f conftest.in conftest.tmp conftest.nl conftest.out } func_path_progs "grep ggrep" func_check_prog_grep "$PATH:/usr/xpg4/bin" GREP=$func_path_progs_result } ## ------------------------------- ## ## User overridable command paths. ## ## ------------------------------- ## # All uppercase variable names are used for environment variables. These # variables can be overridden by the user before calling a script that # uses them if a suitable command of that name is not already available # in the command search PATH. : ${CP="cp -f"} : ${ECHO="printf %s\n"} : ${EGREP="$GREP -E"} : ${FGREP="$GREP -F"} : ${LN_S="ln -s"} : ${MAKE="make"} : ${MKDIR="mkdir"} : ${MV="mv -f"} : ${RM="rm -f"} : ${SHELL="${CONFIG_SHELL-/bin/sh}"} ## -------------------- ## ## Useful sed snippets. ## ## -------------------- ## sed_dirname='s|/[^/]*$||' sed_basename='s|^.*/||' # Sed substitution that helps us do robust quoting. It backslashifies # metacharacters that are still active within double-quoted strings. sed_quote_subst='s|\([`"$\\]\)|\\\1|g' # Same as above, but do not quote variable references. sed_double_quote_subst='s/\(["`\\]\)/\\\1/g' # Sed substitution that turns a string into a regex matching for the # string literally. sed_make_literal_regex='s|[].[^$\\*\/]|\\&|g' # Sed substitution that converts a w32 file name or path # that contains forward slashes, into one that contains # (escaped) backslashes. A very naive implementation. sed_naive_backslashify='s|\\\\*|\\|g;s|/|\\|g;s|\\|\\\\|g' # Re-'\' parameter expansions in output of sed_double_quote_subst that # were '\'-ed in input to the same. If an odd number of '\' preceded a # '$' in input to sed_double_quote_subst, that '$' was protected from # expansion. Since each input '\' is now two '\'s, look for any number # of runs of four '\'s followed by two '\'s and then a '$'. '\' that '$'. _G_bs='\\' _G_bs2='\\\\' _G_bs4='\\\\\\\\' _G_dollar='\$' sed_double_backslash="\ s/$_G_bs4/&\\ /g s/^$_G_bs2$_G_dollar/$_G_bs&/ s/\\([^$_G_bs]\\)$_G_bs2$_G_dollar/\\1$_G_bs2$_G_bs$_G_dollar/g s/\n//g" # require_check_ifs_backslash # --------------------------- # Check if we can use backslash as IFS='\' separator, and set # $check_ifs_backshlash_broken to ':' or 'false'. require_check_ifs_backslash=func_require_check_ifs_backslash func_require_check_ifs_backslash () { _G_save_IFS=$IFS IFS='\' _G_check_ifs_backshlash='a\\b' for _G_i in $_G_check_ifs_backshlash do case $_G_i in a) check_ifs_backshlash_broken=false ;; '') break ;; *) check_ifs_backshlash_broken=: break ;; esac done IFS=$_G_save_IFS require_check_ifs_backslash=: } ## ----------------- ## ## Global variables. ## ## ----------------- ## # Except for the global variables explicitly listed below, the following # functions in the '^func_' namespace, and the '^require_' namespace # variables initialised in the 'Resource management' section, sourcing # this file will not pollute your global namespace with anything # else. There's no portable way to scope variables in Bourne shell # though, so actually running these functions will sometimes place # results into a variable named after the function, and often use # temporary variables in the '^_G_' namespace. If you are careful to # avoid using those namespaces casually in your sourcing script, things # should continue to work as you expect. And, of course, you can freely # overwrite any of the functions or variables defined here before # calling anything to customize them. EXIT_SUCCESS=0 EXIT_FAILURE=1 EXIT_MISMATCH=63 # $? = 63 is used to indicate version mismatch to missing. EXIT_SKIP=77 # $? = 77 is used to indicate a skipped test to automake. # Allow overriding, eg assuming that you follow the convention of # putting '$debug_cmd' at the start of all your functions, you can get # bash to show function call trace with: # # debug_cmd='echo "${FUNCNAME[0]} $*" >&2' bash your-script-name debug_cmd=${debug_cmd-":"} exit_cmd=: # By convention, finish your script with: # # exit $exit_status # # so that you can set exit_status to non-zero if you want to indicate # something went wrong during execution without actually bailing out at # the point of failure. exit_status=$EXIT_SUCCESS # Work around backward compatibility issue on IRIX 6.5. On IRIX 6.4+, sh # is ksh but when the shell is invoked as "sh" and the current value of # the _XPG environment variable is not equal to 1 (one), the special # positional parameter $0, within a function call, is the name of the # function. progpath=$0 # The name of this program. progname=`$ECHO "$progpath" |$SED "$sed_basename"` # Make sure we have an absolute progpath for reexecution: case $progpath in [\\/]*|[A-Za-z]:\\*) ;; *[\\/]*) progdir=`$ECHO "$progpath" |$SED "$sed_dirname"` progdir=`cd "$progdir" && pwd` progpath=$progdir/$progname ;; *) _G_IFS=$IFS IFS=${PATH_SEPARATOR-:} for progdir in $PATH; do IFS=$_G_IFS test -x "$progdir/$progname" && break done IFS=$_G_IFS test -n "$progdir" || progdir=`pwd` progpath=$progdir/$progname ;; esac ## ----------------- ## ## Standard options. ## ## ----------------- ## # The following options affect the operation of the functions defined # below, and should be set appropriately depending on run-time para- # meters passed on the command line. opt_dry_run=false opt_quiet=false opt_verbose=false # Categories 'all' and 'none' are always available. Append any others # you will pass as the first argument to func_warning from your own # code. warning_categories= # By default, display warnings according to 'opt_warning_types'. Set # 'warning_func' to ':' to elide all warnings, or func_fatal_error to # treat the next displayed warning as a fatal error. warning_func=func_warn_and_continue # Set to 'all' to display all warnings, 'none' to suppress all # warnings, or a space delimited list of some subset of # 'warning_categories' to display only the listed warnings. opt_warning_types=all ## -------------------- ## ## Resource management. ## ## -------------------- ## # This section contains definitions for functions that each ensure a # particular resource (a file, or a non-empty configuration variable for # example) is available, and if appropriate to extract default values # from pertinent package files. Call them using their associated # 'require_*' variable to ensure that they are executed, at most, once. # # It's entirely deliberate that calling these functions can set # variables that don't obey the namespace limitations obeyed by the rest # of this file, in order that that they be as useful as possible to # callers. # require_term_colors # ------------------- # Allow display of bold text on terminals that support it. require_term_colors=func_require_term_colors func_require_term_colors () { $debug_cmd test -t 1 && { # COLORTERM and USE_ANSI_COLORS environment variables take # precedence, because most terminfo databases neglect to describe # whether color sequences are supported. test -n "${COLORTERM+set}" && : ${USE_ANSI_COLORS="1"} if test 1 = "$USE_ANSI_COLORS"; then # Standard ANSI escape sequences tc_reset='' tc_bold=''; tc_standout='' tc_red=''; tc_green='' tc_blue=''; tc_cyan='' else # Otherwise trust the terminfo database after all. test -n "`tput sgr0 2>/dev/null`" && { tc_reset=`tput sgr0` test -n "`tput bold 2>/dev/null`" && tc_bold=`tput bold` tc_standout=$tc_bold test -n "`tput smso 2>/dev/null`" && tc_standout=`tput smso` test -n "`tput setaf 1 2>/dev/null`" && tc_red=`tput setaf 1` test -n "`tput setaf 2 2>/dev/null`" && tc_green=`tput setaf 2` test -n "`tput setaf 4 2>/dev/null`" && tc_blue=`tput setaf 4` test -n "`tput setaf 5 2>/dev/null`" && tc_cyan=`tput setaf 5` } fi } require_term_colors=: } ## ----------------- ## ## Function library. ## ## ----------------- ## # This section contains a variety of useful functions to call in your # scripts. Take note of the portable wrappers for features provided by # some modern shells, which will fall back to slower equivalents on # less featureful shells. # func_append VAR VALUE # --------------------- # Append VALUE onto the existing contents of VAR. # _G_HAVE_PLUSEQ_OP # Can be empty, in which case the shell is probed, "yes" if += is # useable or anything else if it does not work. if test -z "$_G_HAVE_PLUSEQ_OP" && \ __PLUSEQ_TEST="a" && \ __PLUSEQ_TEST+=" b" 2>/dev/null && \ test "a b" = "$__PLUSEQ_TEST"; then _G_HAVE_PLUSEQ_OP=yes fi if test yes = "$_G_HAVE_PLUSEQ_OP" then # This is an XSI compatible shell, allowing a faster implementation... eval 'func_append () { $debug_cmd eval "$1+=\$2" }' else # ...otherwise fall back to using expr, which is often a shell builtin. func_append () { $debug_cmd eval "$1=\$$1\$2" } fi # func_append_quoted VAR VALUE # ---------------------------- # Quote VALUE and append to the end of shell variable VAR, separated # by a space. if test yes = "$_G_HAVE_PLUSEQ_OP"; then eval 'func_append_quoted () { $debug_cmd func_quote_arg pretty "$2" eval "$1+=\\ \$func_quote_arg_result" }' else func_append_quoted () { $debug_cmd func_quote_arg pretty "$2" eval "$1=\$$1\\ \$func_quote_arg_result" } fi # func_append_uniq VAR VALUE # -------------------------- # Append unique VALUE onto the existing contents of VAR, assuming # entries are delimited by the first character of VALUE. For example: # # func_append_uniq options " --another-option option-argument" # # will only append to $options if " --another-option option-argument " # is not already present somewhere in $options already (note spaces at # each end implied by leading space in second argument). func_append_uniq () { $debug_cmd eval _G_current_value='`$ECHO $'$1'`' _G_delim=`expr "$2" : '\(.\)'` case $_G_delim$_G_current_value$_G_delim in *"$2$_G_delim"*) ;; *) func_append "$@" ;; esac } # func_arith TERM... # ------------------ # Set func_arith_result to the result of evaluating TERMs. test -z "$_G_HAVE_ARITH_OP" \ && (eval 'test 2 = $(( 1 + 1 ))') 2>/dev/null \ && _G_HAVE_ARITH_OP=yes if test yes = "$_G_HAVE_ARITH_OP"; then eval 'func_arith () { $debug_cmd func_arith_result=$(( $* )) }' else func_arith () { $debug_cmd func_arith_result=`expr "$@"` } fi # func_basename FILE # ------------------ # Set func_basename_result to FILE with everything up to and including # the last / stripped. if test yes = "$_G_HAVE_XSI_OPS"; then # If this shell supports suffix pattern removal, then use it to avoid # forking. Hide the definitions single quotes in case the shell chokes # on unsupported syntax... _b='func_basename_result=${1##*/}' _d='case $1 in */*) func_dirname_result=${1%/*}$2 ;; * ) func_dirname_result=$3 ;; esac' else # ...otherwise fall back to using sed. _b='func_basename_result=`$ECHO "$1" |$SED "$sed_basename"`' _d='func_dirname_result=`$ECHO "$1" |$SED "$sed_dirname"` if test "X$func_dirname_result" = "X$1"; then func_dirname_result=$3 else func_append func_dirname_result "$2" fi' fi eval 'func_basename () { $debug_cmd '"$_b"' }' # func_dirname FILE APPEND NONDIR_REPLACEMENT # ------------------------------------------- # Compute the dirname of FILE. If nonempty, add APPEND to the result, # otherwise set result to NONDIR_REPLACEMENT. eval 'func_dirname () { $debug_cmd '"$_d"' }' # func_dirname_and_basename FILE APPEND NONDIR_REPLACEMENT # -------------------------------------------------------- # Perform func_basename and func_dirname in a single function # call: # dirname: Compute the dirname of FILE. If nonempty, # add APPEND to the result, otherwise set result # to NONDIR_REPLACEMENT. # value returned in "$func_dirname_result" # basename: Compute filename of FILE. # value retuned in "$func_basename_result" # For efficiency, we do not delegate to the functions above but instead # duplicate the functionality here. eval 'func_dirname_and_basename () { $debug_cmd '"$_b"' '"$_d"' }' # func_echo ARG... # ---------------- # Echo program name prefixed message. func_echo () { $debug_cmd _G_message=$* func_echo_IFS=$IFS IFS=$nl for _G_line in $_G_message; do IFS=$func_echo_IFS $ECHO "$progname: $_G_line" done IFS=$func_echo_IFS } # func_echo_all ARG... # -------------------- # Invoke $ECHO with all args, space-separated. func_echo_all () { $ECHO "$*" } # func_echo_infix_1 INFIX ARG... # ------------------------------ # Echo program name, followed by INFIX on the first line, with any # additional lines not showing INFIX. func_echo_infix_1 () { $debug_cmd $require_term_colors _G_infix=$1; shift _G_indent=$_G_infix _G_prefix="$progname: $_G_infix: " _G_message=$* # Strip color escape sequences before counting printable length for _G_tc in "$tc_reset" "$tc_bold" "$tc_standout" "$tc_red" "$tc_green" "$tc_blue" "$tc_cyan" do test -n "$_G_tc" && { _G_esc_tc=`$ECHO "$_G_tc" | $SED "$sed_make_literal_regex"` _G_indent=`$ECHO "$_G_indent" | $SED "s|$_G_esc_tc||g"` } done _G_indent="$progname: "`echo "$_G_indent" | $SED 's|.| |g'`" " ## exclude from sc_prohibit_nested_quotes func_echo_infix_1_IFS=$IFS IFS=$nl for _G_line in $_G_message; do IFS=$func_echo_infix_1_IFS $ECHO "$_G_prefix$tc_bold$_G_line$tc_reset" >&2 _G_prefix=$_G_indent done IFS=$func_echo_infix_1_IFS } # func_error ARG... # ----------------- # Echo program name prefixed message to standard error. func_error () { $debug_cmd $require_term_colors func_echo_infix_1 " $tc_standout${tc_red}error$tc_reset" "$*" >&2 } # func_fatal_error ARG... # ----------------------- # Echo program name prefixed message to standard error, and exit. func_fatal_error () { $debug_cmd func_error "$*" exit $EXIT_FAILURE } # func_grep EXPRESSION FILENAME # ----------------------------- # Check whether EXPRESSION matches any line of FILENAME, without output. func_grep () { $debug_cmd $GREP "$1" "$2" >/dev/null 2>&1 } # func_len STRING # --------------- # Set func_len_result to the length of STRING. STRING may not # start with a hyphen. test -z "$_G_HAVE_XSI_OPS" \ && (eval 'x=a/b/c; test 5aa/bb/cc = "${#x}${x%%/*}${x%/*}${x#*/}${x##*/}"') 2>/dev/null \ && _G_HAVE_XSI_OPS=yes if test yes = "$_G_HAVE_XSI_OPS"; then eval 'func_len () { $debug_cmd func_len_result=${#1} }' else func_len () { $debug_cmd func_len_result=`expr "$1" : ".*" 2>/dev/null || echo $max_cmd_len` } fi # func_mkdir_p DIRECTORY-PATH # --------------------------- # Make sure the entire path to DIRECTORY-PATH is available. func_mkdir_p () { $debug_cmd _G_directory_path=$1 _G_dir_list= if test -n "$_G_directory_path" && test : != "$opt_dry_run"; then # Protect directory names starting with '-' case $_G_directory_path in -*) _G_directory_path=./$_G_directory_path ;; esac # While some portion of DIR does not yet exist... while test ! -d "$_G_directory_path"; do # ...make a list in topmost first order. Use a colon delimited # list incase some portion of path contains whitespace. _G_dir_list=$_G_directory_path:$_G_dir_list # If the last portion added has no slash in it, the list is done case $_G_directory_path in */*) ;; *) break ;; esac # ...otherwise throw away the child directory and loop _G_directory_path=`$ECHO "$_G_directory_path" | $SED -e "$sed_dirname"` done _G_dir_list=`$ECHO "$_G_dir_list" | $SED 's|:*$||'` func_mkdir_p_IFS=$IFS; IFS=: for _G_dir in $_G_dir_list; do IFS=$func_mkdir_p_IFS # mkdir can fail with a 'File exist' error if two processes # try to create one of the directories concurrently. Don't # stop in that case! $MKDIR "$_G_dir" 2>/dev/null || : done IFS=$func_mkdir_p_IFS # Bail out if we (or some other process) failed to create a directory. test -d "$_G_directory_path" || \ func_fatal_error "Failed to create '$1'" fi } # func_mktempdir [BASENAME] # ------------------------- # Make a temporary directory that won't clash with other running # libtool processes, and avoids race conditions if possible. If # given, BASENAME is the basename for that directory. func_mktempdir () { $debug_cmd _G_template=${TMPDIR-/tmp}/${1-$progname} if test : = "$opt_dry_run"; then # Return a directory name, but don't create it in dry-run mode _G_tmpdir=$_G_template-$$ else # If mktemp works, use that first and foremost _G_tmpdir=`mktemp -d "$_G_template-XXXXXXXX" 2>/dev/null` if test ! -d "$_G_tmpdir"; then # Failing that, at least try and use $RANDOM to avoid a race _G_tmpdir=$_G_template-${RANDOM-0}$$ func_mktempdir_umask=`umask` umask 0077 $MKDIR "$_G_tmpdir" umask $func_mktempdir_umask fi # If we're not in dry-run mode, bomb out on failure test -d "$_G_tmpdir" || \ func_fatal_error "cannot create temporary directory '$_G_tmpdir'" fi $ECHO "$_G_tmpdir" } # func_normal_abspath PATH # ------------------------ # Remove doubled-up and trailing slashes, "." path components, # and cancel out any ".." path components in PATH after making # it an absolute path. func_normal_abspath () { $debug_cmd # These SED scripts presuppose an absolute path with a trailing slash. _G_pathcar='s|^/\([^/]*\).*$|\1|' _G_pathcdr='s|^/[^/]*||' _G_removedotparts=':dotsl s|/\./|/|g t dotsl s|/\.$|/|' _G_collapseslashes='s|/\{1,\}|/|g' _G_finalslash='s|/*$|/|' # Start from root dir and reassemble the path. func_normal_abspath_result= func_normal_abspath_tpath=$1 func_normal_abspath_altnamespace= case $func_normal_abspath_tpath in "") # Empty path, that just means $cwd. func_stripname '' '/' "`pwd`" func_normal_abspath_result=$func_stripname_result return ;; # The next three entries are used to spot a run of precisely # two leading slashes without using negated character classes; # we take advantage of case's first-match behaviour. ///*) # Unusual form of absolute path, do nothing. ;; //*) # Not necessarily an ordinary path; POSIX reserves leading '//' # and for example Cygwin uses it to access remote file shares # over CIFS/SMB, so we conserve a leading double slash if found. func_normal_abspath_altnamespace=/ ;; /*) # Absolute path, do nothing. ;; *) # Relative path, prepend $cwd. func_normal_abspath_tpath=`pwd`/$func_normal_abspath_tpath ;; esac # Cancel out all the simple stuff to save iterations. We also want # the path to end with a slash for ease of parsing, so make sure # there is one (and only one) here. func_normal_abspath_tpath=`$ECHO "$func_normal_abspath_tpath" | $SED \ -e "$_G_removedotparts" -e "$_G_collapseslashes" -e "$_G_finalslash"` while :; do # Processed it all yet? if test / = "$func_normal_abspath_tpath"; then # If we ascended to the root using ".." the result may be empty now. if test -z "$func_normal_abspath_result"; then func_normal_abspath_result=/ fi break fi func_normal_abspath_tcomponent=`$ECHO "$func_normal_abspath_tpath" | $SED \ -e "$_G_pathcar"` func_normal_abspath_tpath=`$ECHO "$func_normal_abspath_tpath" | $SED \ -e "$_G_pathcdr"` # Figure out what to do with it case $func_normal_abspath_tcomponent in "") # Trailing empty path component, ignore it. ;; ..) # Parent dir; strip last assembled component from result. func_dirname "$func_normal_abspath_result" func_normal_abspath_result=$func_dirname_result ;; *) # Actual path component, append it. func_append func_normal_abspath_result "/$func_normal_abspath_tcomponent" ;; esac done # Restore leading double-slash if one was found on entry. func_normal_abspath_result=$func_normal_abspath_altnamespace$func_normal_abspath_result } # func_notquiet ARG... # -------------------- # Echo program name prefixed message only when not in quiet mode. func_notquiet () { $debug_cmd $opt_quiet || func_echo ${1+"$@"} # A bug in bash halts the script if the last line of a function # fails when set -e is in force, so we need another command to # work around that: : } # func_relative_path SRCDIR DSTDIR # -------------------------------- # Set func_relative_path_result to the relative path from SRCDIR to DSTDIR. func_relative_path () { $debug_cmd func_relative_path_result= func_normal_abspath "$1" func_relative_path_tlibdir=$func_normal_abspath_result func_normal_abspath "$2" func_relative_path_tbindir=$func_normal_abspath_result # Ascend the tree starting from libdir while :; do # check if we have found a prefix of bindir case $func_relative_path_tbindir in $func_relative_path_tlibdir) # found an exact match func_relative_path_tcancelled= break ;; $func_relative_path_tlibdir*) # found a matching prefix func_stripname "$func_relative_path_tlibdir" '' "$func_relative_path_tbindir" func_relative_path_tcancelled=$func_stripname_result if test -z "$func_relative_path_result"; then func_relative_path_result=. fi break ;; *) func_dirname $func_relative_path_tlibdir func_relative_path_tlibdir=$func_dirname_result if test -z "$func_relative_path_tlibdir"; then # Have to descend all the way to the root! func_relative_path_result=../$func_relative_path_result func_relative_path_tcancelled=$func_relative_path_tbindir break fi func_relative_path_result=../$func_relative_path_result ;; esac done # Now calculate path; take care to avoid doubling-up slashes. func_stripname '' '/' "$func_relative_path_result" func_relative_path_result=$func_stripname_result func_stripname '/' '/' "$func_relative_path_tcancelled" if test -n "$func_stripname_result"; then func_append func_relative_path_result "/$func_stripname_result" fi # Normalisation. If bindir is libdir, return '.' else relative path. if test -n "$func_relative_path_result"; then func_stripname './' '' "$func_relative_path_result" func_relative_path_result=$func_stripname_result fi test -n "$func_relative_path_result" || func_relative_path_result=. : } # func_quote_portable EVAL ARG # ---------------------------- # Internal function to portably implement func_quote_arg. Note that we still # keep attention to performance here so we as much as possible try to avoid # calling sed binary (so far O(N) complexity as long as func_append is O(1)). func_quote_portable () { $debug_cmd $require_check_ifs_backslash func_quote_portable_result=$2 # one-time-loop (easy break) while true do if $1; then func_quote_portable_result=`$ECHO "$2" | $SED \ -e "$sed_double_quote_subst" -e "$sed_double_backslash"` break fi # Quote for eval. case $func_quote_portable_result in *[\\\`\"\$]*) # Fallback to sed for $func_check_bs_ifs_broken=:, or when the string # contains the shell wildcard characters. case $check_ifs_backshlash_broken$func_quote_portable_result in :*|*[\[\*\?]*) func_quote_portable_result=`$ECHO "$func_quote_portable_result" \ | $SED "$sed_quote_subst"` break ;; esac func_quote_portable_old_IFS=$IFS for _G_char in '\' '`' '"' '$' do # STATE($1) PREV($2) SEPARATOR($3) set start "" "" func_quote_portable_result=dummy"$_G_char$func_quote_portable_result$_G_char"dummy IFS=$_G_char for _G_part in $func_quote_portable_result do case $1 in quote) func_append func_quote_portable_result "$3$2" set quote "$_G_part" "\\$_G_char" ;; start) set first "" "" func_quote_portable_result= ;; first) set quote "$_G_part" "" ;; esac done done IFS=$func_quote_portable_old_IFS ;; *) ;; esac break done func_quote_portable_unquoted_result=$func_quote_portable_result case $func_quote_portable_result in # double-quote args containing shell metacharacters to delay # word splitting, command substitution and variable expansion # for a subsequent eval. # many bourne shells cannot handle close brackets correctly # in scan sets, so we specify it separately. *[\[\~\#\^\&\*\(\)\{\}\|\;\<\>\?\'\ \ ]*|*]*|"") func_quote_portable_result=\"$func_quote_portable_result\" ;; esac } # func_quotefast_eval ARG # ----------------------- # Quote one ARG (internal). This is equivalent to 'func_quote_arg eval ARG', # but optimized for speed. Result is stored in $func_quotefast_eval. if test xyes = `(x=; printf -v x %q yes; echo x"$x") 2>/dev/null`; then printf -v _GL_test_printf_tilde %q '~' if test '\~' = "$_GL_test_printf_tilde"; then func_quotefast_eval () { printf -v func_quotefast_eval_result %q "$1" } else # Broken older Bash implementations. Make those faster too if possible. func_quotefast_eval () { case $1 in '~'*) func_quote_portable false "$1" func_quotefast_eval_result=$func_quote_portable_result ;; *) printf -v func_quotefast_eval_result %q "$1" ;; esac } fi else func_quotefast_eval () { func_quote_portable false "$1" func_quotefast_eval_result=$func_quote_portable_result } fi # func_quote_arg MODEs ARG # ------------------------ # Quote one ARG to be evaled later. MODEs argument may contain zero or more # specifiers listed below separated by ',' character. This function returns two # values: # i) func_quote_arg_result # double-quoted (when needed), suitable for a subsequent eval # ii) func_quote_arg_unquoted_result # has all characters that are still active within double # quotes backslashified. Available only if 'unquoted' is specified. # # Available modes: # ---------------- # 'eval' (default) # - escape shell special characters # 'expand' # - the same as 'eval'; but do not quote variable references # 'pretty' # - request aesthetic output, i.e. '"a b"' instead of 'a\ b'. This might # be used later in func_quote to get output like: 'echo "a b"' instead # of 'echo a\ b'. This is slower than default on some shells. # 'unquoted' # - produce also $func_quote_arg_unquoted_result which does not contain # wrapping double-quotes. # # Examples for 'func_quote_arg pretty,unquoted string': # # string | *_result | *_unquoted_result # ------------+-----------------------+------------------- # " | \" | \" # a b | "a b" | a b # "a b" | "\"a b\"" | \"a b\" # * | "*" | * # z="${x-$y}" | "z=\"\${x-\$y}\"" | z=\"\${x-\$y}\" # # Examples for 'func_quote_arg pretty,unquoted,expand string': # # string | *_result | *_unquoted_result # --------------+---------------------+-------------------- # z="${x-$y}" | "z=\"${x-$y}\"" | z=\"${x-$y}\" func_quote_arg () { _G_quote_expand=false case ,$1, in *,expand,*) _G_quote_expand=: ;; esac case ,$1, in *,pretty,*|*,expand,*|*,unquoted,*) func_quote_portable $_G_quote_expand "$2" func_quote_arg_result=$func_quote_portable_result func_quote_arg_unquoted_result=$func_quote_portable_unquoted_result ;; *) # Faster quote-for-eval for some shells. func_quotefast_eval "$2" func_quote_arg_result=$func_quotefast_eval_result ;; esac } # func_quote MODEs ARGs... # ------------------------ # Quote all ARGs to be evaled later and join them into single command. See # func_quote_arg's description for more info. func_quote () { $debug_cmd _G_func_quote_mode=$1 ; shift func_quote_result= while test 0 -lt $#; do func_quote_arg "$_G_func_quote_mode" "$1" if test -n "$func_quote_result"; then func_append func_quote_result " $func_quote_arg_result" else func_append func_quote_result "$func_quote_arg_result" fi shift done } # func_stripname PREFIX SUFFIX NAME # --------------------------------- # strip PREFIX and SUFFIX from NAME, and store in func_stripname_result. # PREFIX and SUFFIX must not contain globbing or regex special # characters, hashes, percent signs, but SUFFIX may contain a leading # dot (in which case that matches only a dot). if test yes = "$_G_HAVE_XSI_OPS"; then eval 'func_stripname () { $debug_cmd # pdksh 5.2.14 does not do ${X%$Y} correctly if both X and Y are # positional parameters, so assign one to ordinary variable first. func_stripname_result=$3 func_stripname_result=${func_stripname_result#"$1"} func_stripname_result=${func_stripname_result%"$2"} }' else func_stripname () { $debug_cmd case $2 in .*) func_stripname_result=`$ECHO "$3" | $SED -e "s%^$1%%" -e "s%\\\\$2\$%%"`;; *) func_stripname_result=`$ECHO "$3" | $SED -e "s%^$1%%" -e "s%$2\$%%"`;; esac } fi # func_show_eval CMD [FAIL_EXP] # ----------------------------- # Unless opt_quiet is true, then output CMD. Then, if opt_dryrun is # not true, evaluate CMD. If the evaluation of CMD fails, and FAIL_EXP # is given, then evaluate it. func_show_eval () { $debug_cmd _G_cmd=$1 _G_fail_exp=${2-':'} func_quote_arg pretty,expand "$_G_cmd" eval "func_notquiet $func_quote_arg_result" $opt_dry_run || { eval "$_G_cmd" _G_status=$? if test 0 -ne "$_G_status"; then eval "(exit $_G_status); $_G_fail_exp" fi } } # func_show_eval_locale CMD [FAIL_EXP] # ------------------------------------ # Unless opt_quiet is true, then output CMD. Then, if opt_dryrun is # not true, evaluate CMD. If the evaluation of CMD fails, and FAIL_EXP # is given, then evaluate it. Use the saved locale for evaluation. func_show_eval_locale () { $debug_cmd _G_cmd=$1 _G_fail_exp=${2-':'} $opt_quiet || { func_quote_arg expand,pretty "$_G_cmd" eval "func_echo $func_quote_arg_result" } $opt_dry_run || { eval "$_G_user_locale $_G_cmd" _G_status=$? eval "$_G_safe_locale" if test 0 -ne "$_G_status"; then eval "(exit $_G_status); $_G_fail_exp" fi } } # func_tr_sh # ---------- # Turn $1 into a string suitable for a shell variable name. # Result is stored in $func_tr_sh_result. All characters # not in the set a-zA-Z0-9_ are replaced with '_'. Further, # if $1 begins with a digit, a '_' is prepended as well. func_tr_sh () { $debug_cmd case $1 in [0-9]* | *[!a-zA-Z0-9_]*) func_tr_sh_result=`$ECHO "$1" | $SED -e 's/^\([0-9]\)/_\1/' -e 's/[^a-zA-Z0-9_]/_/g'` ;; * ) func_tr_sh_result=$1 ;; esac } # func_verbose ARG... # ------------------- # Echo program name prefixed message in verbose mode only. func_verbose () { $debug_cmd $opt_verbose && func_echo "$*" : } # func_warn_and_continue ARG... # ----------------------------- # Echo program name prefixed warning message to standard error. func_warn_and_continue () { $debug_cmd $require_term_colors func_echo_infix_1 "${tc_red}warning$tc_reset" "$*" >&2 } # func_warning CATEGORY ARG... # ---------------------------- # Echo program name prefixed warning message to standard error. Warning # messages can be filtered according to CATEGORY, where this function # elides messages where CATEGORY is not listed in the global variable # 'opt_warning_types'. func_warning () { $debug_cmd # CATEGORY must be in the warning_categories list! case " $warning_categories " in *" $1 "*) ;; *) func_internal_error "invalid warning category '$1'" ;; esac _G_category=$1 shift case " $opt_warning_types " in *" $_G_category "*) $warning_func ${1+"$@"} ;; esac } # func_sort_ver VER1 VER2 # ----------------------- # 'sort -V' is not generally available. # Note this deviates from the version comparison in automake # in that it treats 1.5 < 1.5.0, and treats 1.4.4a < 1.4-p3a # but this should suffice as we won't be specifying old # version formats or redundant trailing .0 in bootstrap.conf. # If we did want full compatibility then we should probably # use m4_version_compare from autoconf. func_sort_ver () { $debug_cmd printf '%s\n%s\n' "$1" "$2" \ | sort -t. -k 1,1n -k 2,2n -k 3,3n -k 4,4n -k 5,5n -k 6,6n -k 7,7n -k 8,8n -k 9,9n } # func_lt_ver PREV CURR # --------------------- # Return true if PREV and CURR are in the correct order according to # func_sort_ver, otherwise false. Use it like this: # # func_lt_ver "$prev_ver" "$proposed_ver" || func_fatal_error "..." func_lt_ver () { $debug_cmd test "x$1" = x`func_sort_ver "$1" "$2" | $SED 1q` } # Local variables: # mode: shell-script # sh-indentation: 2 # eval: (add-hook 'before-save-hook 'time-stamp) # time-stamp-pattern: "10/scriptversion=%:y-%02m-%02d.%02H; # UTC" # time-stamp-time-zone: "UTC" # End: #! /bin/sh # A portable, pluggable option parser for Bourne shell. # Written by Gary V. Vaughan, 2010 # This is free software. There is NO warranty; not even for # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. # # Copyright (C) 2010-2019, 2021 Bootstrap Authors # # This file is dual licensed under the terms of the MIT license # , and GPL version 2 or later # . You must apply one of # these licenses when using or redistributing this software or any of # the files within it. See the URLs above, or the file `LICENSE` # included in the Bootstrap distribution for the full license texts. # Please report bugs or propose patches to: # # Set a version string for this script. scriptversion=2019-02-19.15; # UTC ## ------ ## ## Usage. ## ## ------ ## # This file is a library for parsing options in your shell scripts along # with assorted other useful supporting features that you can make use # of too. # # For the simplest scripts you might need only: # # #!/bin/sh # . relative/path/to/funclib.sh # . relative/path/to/options-parser # scriptversion=1.0 # func_options ${1+"$@"} # eval set dummy "$func_options_result"; shift # ...rest of your script... # # In order for the '--version' option to work, you will need to have a # suitably formatted comment like the one at the top of this file # starting with '# Written by ' and ending with '# Copyright'. # # For '-h' and '--help' to work, you will also need a one line # description of your script's purpose in a comment directly above the # '# Written by ' line, like the one at the top of this file. # # The default options also support '--debug', which will turn on shell # execution tracing (see the comment above debug_cmd below for another # use), and '--verbose' and the func_verbose function to allow your script # to display verbose messages only when your user has specified # '--verbose'. # # After sourcing this file, you can plug in processing for additional # options by amending the variables from the 'Configuration' section # below, and following the instructions in the 'Option parsing' # section further down. ## -------------- ## ## Configuration. ## ## -------------- ## # You should override these variables in your script after sourcing this # file so that they reflect the customisations you have added to the # option parser. # The usage line for option parsing errors and the start of '-h' and # '--help' output messages. You can embed shell variables for delayed # expansion at the time the message is displayed, but you will need to # quote other shell meta-characters carefully to prevent them being # expanded when the contents are evaled. usage='$progpath [OPTION]...' # Short help message in response to '-h' and '--help'. Add to this or # override it after sourcing this library to reflect the full set of # options your script accepts. usage_message="\ --debug enable verbose shell tracing -W, --warnings=CATEGORY report the warnings falling in CATEGORY [all] -v, --verbose verbosely report processing --version print version information and exit -h, --help print short or long help message and exit " # Additional text appended to 'usage_message' in response to '--help'. long_help_message=" Warning categories include: 'all' show all warnings 'none' turn off all the warnings 'error' warnings are treated as fatal errors" # Help message printed before fatal option parsing errors. fatal_help="Try '\$progname --help' for more information." ## ------------------------- ## ## Hook function management. ## ## ------------------------- ## # This section contains functions for adding, removing, and running hooks # in the main code. A hook is just a list of function names that can be # run in order later on. # func_hookable FUNC_NAME # ----------------------- # Declare that FUNC_NAME will run hooks added with # 'func_add_hook FUNC_NAME ...'. func_hookable () { $debug_cmd func_append hookable_fns " $1" } # func_add_hook FUNC_NAME HOOK_FUNC # --------------------------------- # Request that FUNC_NAME call HOOK_FUNC before it returns. FUNC_NAME must # first have been declared "hookable" by a call to 'func_hookable'. func_add_hook () { $debug_cmd case " $hookable_fns " in *" $1 "*) ;; *) func_fatal_error "'$1' does not accept hook functions." ;; esac eval func_append ${1}_hooks '" $2"' } # func_remove_hook FUNC_NAME HOOK_FUNC # ------------------------------------ # Remove HOOK_FUNC from the list of hook functions to be called by # FUNC_NAME. func_remove_hook () { $debug_cmd eval ${1}_hooks='`$ECHO "\$'$1'_hooks" |$SED "s| '$2'||"`' } # func_propagate_result FUNC_NAME_A FUNC_NAME_B # --------------------------------------------- # If the *_result variable of FUNC_NAME_A _is set_, assign its value to # *_result variable of FUNC_NAME_B. func_propagate_result () { $debug_cmd func_propagate_result_result=: if eval "test \"\${${1}_result+set}\" = set" then eval "${2}_result=\$${1}_result" else func_propagate_result_result=false fi } # func_run_hooks FUNC_NAME [ARG]... # --------------------------------- # Run all hook functions registered to FUNC_NAME. # It's assumed that the list of hook functions contains nothing more # than a whitespace-delimited list of legal shell function names, and # no effort is wasted trying to catch shell meta-characters or preserve # whitespace. func_run_hooks () { $debug_cmd _G_rc_run_hooks=false case " $hookable_fns " in *" $1 "*) ;; *) func_fatal_error "'$1' does not support hook functions." ;; esac eval _G_hook_fns=\$$1_hooks; shift for _G_hook in $_G_hook_fns; do func_unset "${_G_hook}_result" eval $_G_hook '${1+"$@"}' func_propagate_result $_G_hook func_run_hooks if $func_propagate_result_result; then eval set dummy "$func_run_hooks_result"; shift fi done } ## --------------- ## ## Option parsing. ## ## --------------- ## # In order to add your own option parsing hooks, you must accept the # full positional parameter list from your hook function. You may remove # or edit any options that you action, and then pass back the remaining # unprocessed options in '_result', escaped # suitably for 'eval'. # # The '_result' variable is automatically unset # before your hook gets called; for best performance, only set the # *_result variable when necessary (i.e. don't call the 'func_quote' # function unnecessarily because it can be an expensive operation on some # machines). # # Like this: # # my_options_prep () # { # $debug_cmd # # # Extend the existing usage message. # usage_message=$usage_message' # -s, --silent don'\''t print informational messages # ' # # No change in '$@' (ignored completely by this hook). Leave # # my_options_prep_result variable intact. # } # func_add_hook func_options_prep my_options_prep # # # my_silent_option () # { # $debug_cmd # # args_changed=false # # # Note that, for efficiency, we parse as many options as we can # # recognise in a loop before passing the remainder back to the # # caller on the first unrecognised argument we encounter. # while test $# -gt 0; do # opt=$1; shift # case $opt in # --silent|-s) opt_silent=: # args_changed=: # ;; # # Separate non-argument short options: # -s*) func_split_short_opt "$_G_opt" # set dummy "$func_split_short_opt_name" \ # "-$func_split_short_opt_arg" ${1+"$@"} # shift # args_changed=: # ;; # *) # Make sure the first unrecognised option "$_G_opt" # # is added back to "$@" in case we need it later, # # if $args_changed was set to 'true'. # set dummy "$_G_opt" ${1+"$@"}; shift; break ;; # esac # done # # # Only call 'func_quote' here if we processed at least one argument. # if $args_changed; then # func_quote eval ${1+"$@"} # my_silent_option_result=$func_quote_result # fi # } # func_add_hook func_parse_options my_silent_option # # # my_option_validation () # { # $debug_cmd # # $opt_silent && $opt_verbose && func_fatal_help "\ # '--silent' and '--verbose' options are mutually exclusive." # } # func_add_hook func_validate_options my_option_validation # # You'll also need to manually amend $usage_message to reflect the extra # options you parse. It's preferable to append if you can, so that # multiple option parsing hooks can be added safely. # func_options_finish [ARG]... # ---------------------------- # Finishing the option parse loop (call 'func_options' hooks ATM). func_options_finish () { $debug_cmd func_run_hooks func_options ${1+"$@"} func_propagate_result func_run_hooks func_options_finish } # func_options [ARG]... # --------------------- # All the functions called inside func_options are hookable. See the # individual implementations for details. func_hookable func_options func_options () { $debug_cmd _G_options_quoted=false for my_func in options_prep parse_options validate_options options_finish do func_unset func_${my_func}_result func_unset func_run_hooks_result eval func_$my_func '${1+"$@"}' func_propagate_result func_$my_func func_options if $func_propagate_result_result; then eval set dummy "$func_options_result"; shift _G_options_quoted=: fi done $_G_options_quoted || { # As we (func_options) are top-level options-parser function and # nobody quoted "$@" for us yet, we need to do it explicitly for # caller. func_quote eval ${1+"$@"} func_options_result=$func_quote_result } } # func_options_prep [ARG]... # -------------------------- # All initialisations required before starting the option parse loop. # Note that when calling hook functions, we pass through the list of # positional parameters. If a hook function modifies that list, and # needs to propagate that back to rest of this script, then the complete # modified list must be put in 'func_run_hooks_result' before returning. func_hookable func_options_prep func_options_prep () { $debug_cmd # Option defaults: opt_verbose=false opt_warning_types= func_run_hooks func_options_prep ${1+"$@"} func_propagate_result func_run_hooks func_options_prep } # func_parse_options [ARG]... # --------------------------- # The main option parsing loop. func_hookable func_parse_options func_parse_options () { $debug_cmd _G_parse_options_requote=false # this just eases exit handling while test $# -gt 0; do # Defer to hook functions for initial option parsing, so they # get priority in the event of reusing an option name. func_run_hooks func_parse_options ${1+"$@"} func_propagate_result func_run_hooks func_parse_options if $func_propagate_result_result; then eval set dummy "$func_parse_options_result"; shift # Even though we may have changed "$@", we passed the "$@" array # down into the hook and it quoted it for us (because we are in # this if-branch). No need to quote it again. _G_parse_options_requote=false fi # Break out of the loop if we already parsed every option. test $# -gt 0 || break # We expect that one of the options parsed in this function matches # and thus we remove _G_opt from "$@" and need to re-quote. _G_match_parse_options=: _G_opt=$1 shift case $_G_opt in --debug|-x) debug_cmd='set -x' func_echo "enabling shell trace mode" >&2 $debug_cmd ;; --no-warnings|--no-warning|--no-warn) set dummy --warnings none ${1+"$@"} shift ;; --warnings|--warning|-W) if test $# = 0 && func_missing_arg $_G_opt; then _G_parse_options_requote=: break fi case " $warning_categories $1" in *" $1 "*) # trailing space prevents matching last $1 above func_append_uniq opt_warning_types " $1" ;; *all) opt_warning_types=$warning_categories ;; *none) opt_warning_types=none warning_func=: ;; *error) opt_warning_types=$warning_categories warning_func=func_fatal_error ;; *) func_fatal_error \ "unsupported warning category: '$1'" ;; esac shift ;; --verbose|-v) opt_verbose=: ;; --version) func_version ;; -\?|-h) func_usage ;; --help) func_help ;; # Separate optargs to long options (plugins may need this): --*=*) func_split_equals "$_G_opt" set dummy "$func_split_equals_lhs" \ "$func_split_equals_rhs" ${1+"$@"} shift ;; # Separate optargs to short options: -W*) func_split_short_opt "$_G_opt" set dummy "$func_split_short_opt_name" \ "$func_split_short_opt_arg" ${1+"$@"} shift ;; # Separate non-argument short options: -\?*|-h*|-v*|-x*) func_split_short_opt "$_G_opt" set dummy "$func_split_short_opt_name" \ "-$func_split_short_opt_arg" ${1+"$@"} shift ;; --) _G_parse_options_requote=: ; break ;; -*) func_fatal_help "unrecognised option: '$_G_opt'" ;; *) set dummy "$_G_opt" ${1+"$@"}; shift _G_match_parse_options=false break ;; esac if $_G_match_parse_options; then _G_parse_options_requote=: fi done if $_G_parse_options_requote; then # save modified positional parameters for caller func_quote eval ${1+"$@"} func_parse_options_result=$func_quote_result fi } # func_validate_options [ARG]... # ------------------------------ # Perform any sanity checks on option settings and/or unconsumed # arguments. func_hookable func_validate_options func_validate_options () { $debug_cmd # Display all warnings if -W was not given. test -n "$opt_warning_types" || opt_warning_types=" $warning_categories" func_run_hooks func_validate_options ${1+"$@"} func_propagate_result func_run_hooks func_validate_options # Bail if the options were screwed! $exit_cmd $EXIT_FAILURE } ## ----------------- ## ## Helper functions. ## ## ----------------- ## # This section contains the helper functions used by the rest of the # hookable option parser framework in ascii-betical order. # func_fatal_help ARG... # ---------------------- # Echo program name prefixed message to standard error, followed by # a help hint, and exit. func_fatal_help () { $debug_cmd eval \$ECHO \""Usage: $usage"\" eval \$ECHO \""$fatal_help"\" func_error ${1+"$@"} exit $EXIT_FAILURE } # func_help # --------- # Echo long help message to standard output and exit. func_help () { $debug_cmd func_usage_message $ECHO "$long_help_message" exit 0 } # func_missing_arg ARGNAME # ------------------------ # Echo program name prefixed message to standard error and set global # exit_cmd. func_missing_arg () { $debug_cmd func_error "Missing argument for '$1'." exit_cmd=exit } # func_split_equals STRING # ------------------------ # Set func_split_equals_lhs and func_split_equals_rhs shell variables # after splitting STRING at the '=' sign. test -z "$_G_HAVE_XSI_OPS" \ && (eval 'x=a/b/c; test 5aa/bb/cc = "${#x}${x%%/*}${x%/*}${x#*/}${x##*/}"') 2>/dev/null \ && _G_HAVE_XSI_OPS=yes if test yes = "$_G_HAVE_XSI_OPS" then # This is an XSI compatible shell, allowing a faster implementation... eval 'func_split_equals () { $debug_cmd func_split_equals_lhs=${1%%=*} func_split_equals_rhs=${1#*=} if test "x$func_split_equals_lhs" = "x$1"; then func_split_equals_rhs= fi }' else # ...otherwise fall back to using expr, which is often a shell builtin. func_split_equals () { $debug_cmd func_split_equals_lhs=`expr "x$1" : 'x\([^=]*\)'` func_split_equals_rhs= test "x$func_split_equals_lhs=" = "x$1" \ || func_split_equals_rhs=`expr "x$1" : 'x[^=]*=\(.*\)$'` } fi #func_split_equals # func_split_short_opt SHORTOPT # ----------------------------- # Set func_split_short_opt_name and func_split_short_opt_arg shell # variables after splitting SHORTOPT after the 2nd character. if test yes = "$_G_HAVE_XSI_OPS" then # This is an XSI compatible shell, allowing a faster implementation... eval 'func_split_short_opt () { $debug_cmd func_split_short_opt_arg=${1#??} func_split_short_opt_name=${1%"$func_split_short_opt_arg"} }' else # ...otherwise fall back to using expr, which is often a shell builtin. func_split_short_opt () { $debug_cmd func_split_short_opt_name=`expr "x$1" : 'x\(-.\)'` func_split_short_opt_arg=`expr "x$1" : 'x-.\(.*\)$'` } fi #func_split_short_opt # func_usage # ---------- # Echo short help message to standard output and exit. func_usage () { $debug_cmd func_usage_message $ECHO "Run '$progname --help |${PAGER-more}' for full usage" exit 0 } # func_usage_message # ------------------ # Echo short help message to standard output. func_usage_message () { $debug_cmd eval \$ECHO \""Usage: $usage"\" echo $SED -n 's|^# || /^Written by/{ x;p;x } h /^Written by/q' < "$progpath" echo eval \$ECHO \""$usage_message"\" } # func_version # ------------ # Echo version message to standard output and exit. # The version message is extracted from the calling file's header # comments, with leading '# ' stripped: # 1. First display the progname and version # 2. Followed by the header comment line matching /^# Written by / # 3. Then a blank line followed by the first following line matching # /^# Copyright / # 4. Immediately followed by any lines between the previous matches, # except lines preceding the intervening completely blank line. # For example, see the header comments of this file. func_version () { $debug_cmd printf '%s\n' "$progname $scriptversion" $SED -n ' /^# Written by /!b s|^# ||; p; n :fwd2blnk /./ { n b fwd2blnk } p; n :holdwrnt s|^# || s|^# *$|| /^Copyright /!{ /./H n b holdwrnt } s|\((C)\)[ 0-9,-]*[ ,-]\([1-9][0-9]* \)|\1 \2| G s|\(\n\)\n*|\1|g p; q' < "$progpath" exit $? } # Local variables: # mode: shell-script # sh-indentation: 2 # eval: (add-hook 'before-save-hook 'time-stamp) # time-stamp-pattern: "30/scriptversion=%:y-%02m-%02d.%02H; # UTC" # time-stamp-time-zone: "UTC" # End: # Set a version string. scriptversion='(GNU libtool) 2.4.7' # func_echo ARG... # ---------------- # Libtool also displays the current mode in messages, so override # funclib.sh func_echo with this custom definition. func_echo () { $debug_cmd _G_message=$* func_echo_IFS=$IFS IFS=$nl for _G_line in $_G_message; do IFS=$func_echo_IFS $ECHO "$progname${opt_mode+: $opt_mode}: $_G_line" done IFS=$func_echo_IFS } # func_warning ARG... # ------------------- # Libtool warnings are not categorized, so override funclib.sh # func_warning with this simpler definition. func_warning () { $debug_cmd $warning_func ${1+"$@"} } ## ---------------- ## ## Options parsing. ## ## ---------------- ## # Hook in the functions to make sure our own options are parsed during # the option parsing loop. usage='$progpath [OPTION]... [MODE-ARG]...' # Short help message in response to '-h'. usage_message="Options: --config show all configuration variables --debug enable verbose shell tracing -n, --dry-run display commands without modifying any files --features display basic configuration information and exit --mode=MODE use operation mode MODE --no-warnings equivalent to '-Wnone' --preserve-dup-deps don't remove duplicate dependency libraries --quiet, --silent don't print informational messages --tag=TAG use configuration variables from tag TAG -v, --verbose print more informational messages than default --version print version information -W, --warnings=CATEGORY report the warnings falling in CATEGORY [all] -h, --help, --help-all print short, long, or detailed help message " # Additional text appended to 'usage_message' in response to '--help'. func_help () { $debug_cmd func_usage_message $ECHO "$long_help_message MODE must be one of the following: clean remove files from the build directory compile compile a source file into a libtool object execute automatically set library path, then run a program finish complete the installation of libtool libraries install install libraries or executables link create a library or an executable uninstall remove libraries from an installed directory MODE-ARGS vary depending on the MODE. When passed as first option, '--mode=MODE' may be abbreviated as 'MODE' or a unique abbreviation of that. Try '$progname --help --mode=MODE' for a more detailed description of MODE. When reporting a bug, please describe a test case to reproduce it and include the following information: host-triplet: $host shell: $SHELL compiler: $LTCC compiler flags: $LTCFLAGS linker: $LD (gnu? $with_gnu_ld) version: $progname $scriptversion Debian-2.4.7-8 automake: `($AUTOMAKE --version) 2>/dev/null |$SED 1q` autoconf: `($AUTOCONF --version) 2>/dev/null |$SED 1q` Report bugs to . GNU libtool home page: . General help using GNU software: ." exit 0 } # func_lo2o OBJECT-NAME # --------------------- # Transform OBJECT-NAME from a '.lo' suffix to the platform specific # object suffix. lo2o=s/\\.lo\$/.$objext/ o2lo=s/\\.$objext\$/.lo/ if test yes = "$_G_HAVE_XSI_OPS"; then eval 'func_lo2o () { case $1 in *.lo) func_lo2o_result=${1%.lo}.$objext ;; * ) func_lo2o_result=$1 ;; esac }' # func_xform LIBOBJ-OR-SOURCE # --------------------------- # Transform LIBOBJ-OR-SOURCE from a '.o' or '.c' (or otherwise) # suffix to a '.lo' libtool-object suffix. eval 'func_xform () { func_xform_result=${1%.*}.lo }' else # ...otherwise fall back to using sed. func_lo2o () { func_lo2o_result=`$ECHO "$1" | $SED "$lo2o"` } func_xform () { func_xform_result=`$ECHO "$1" | $SED 's|\.[^.]*$|.lo|'` } fi # func_fatal_configuration ARG... # ------------------------------- # Echo program name prefixed message to standard error, followed by # a configuration failure hint, and exit. func_fatal_configuration () { func_fatal_error ${1+"$@"} \ "See the $PACKAGE documentation for more information." \ "Fatal configuration error." } # func_config # ----------- # Display the configuration for all the tags in this script. func_config () { re_begincf='^# ### BEGIN LIBTOOL' re_endcf='^# ### END LIBTOOL' # Default configuration. $SED "1,/$re_begincf CONFIG/d;/$re_endcf CONFIG/,\$d" < "$progpath" # Now print the configurations for the tags. for tagname in $taglist; do $SED -n "/$re_begincf TAG CONFIG: $tagname\$/,/$re_endcf TAG CONFIG: $tagname\$/p" < "$progpath" done exit $? } # func_features # ------------- # Display the features supported by this script. func_features () { echo "host: $host" if test yes = "$build_libtool_libs"; then echo "enable shared libraries" else echo "disable shared libraries" fi if test yes = "$build_old_libs"; then echo "enable static libraries" else echo "disable static libraries" fi exit $? } # func_enable_tag TAGNAME # ----------------------- # Verify that TAGNAME is valid, and either flag an error and exit, or # enable the TAGNAME tag. We also add TAGNAME to the global $taglist # variable here. func_enable_tag () { # Global variable: tagname=$1 re_begincf="^# ### BEGIN LIBTOOL TAG CONFIG: $tagname\$" re_endcf="^# ### END LIBTOOL TAG CONFIG: $tagname\$" sed_extractcf=/$re_begincf/,/$re_endcf/p # Validate tagname. case $tagname in *[!-_A-Za-z0-9,/]*) func_fatal_error "invalid tag name: $tagname" ;; esac # Don't test for the "default" C tag, as we know it's # there but not specially marked. case $tagname in CC) ;; *) if $GREP "$re_begincf" "$progpath" >/dev/null 2>&1; then taglist="$taglist $tagname" # Evaluate the configuration. Be careful to quote the path # and the sed script, to avoid splitting on whitespace, but # also don't use non-portable quotes within backquotes within # quotes we have to do it in 2 steps: extractedcf=`$SED -n -e "$sed_extractcf" < "$progpath"` eval "$extractedcf" else func_error "ignoring unknown tag $tagname" fi ;; esac } # func_check_version_match # ------------------------ # Ensure that we are using m4 macros, and libtool script from the same # release of libtool. func_check_version_match () { if test "$package_revision" != "$macro_revision"; then if test "$VERSION" != "$macro_version"; then if test -z "$macro_version"; then cat >&2 <<_LT_EOF $progname: Version mismatch error. This is $PACKAGE $VERSION, but the $progname: definition of this LT_INIT comes from an older release. $progname: You should recreate aclocal.m4 with macros from $PACKAGE $VERSION $progname: and run autoconf again. _LT_EOF else cat >&2 <<_LT_EOF $progname: Version mismatch error. This is $PACKAGE $VERSION, but the $progname: definition of this LT_INIT comes from $PACKAGE $macro_version. $progname: You should recreate aclocal.m4 with macros from $PACKAGE $VERSION $progname: and run autoconf again. _LT_EOF fi else cat >&2 <<_LT_EOF $progname: Version mismatch error. This is $PACKAGE $VERSION, revision $package_revision, $progname: but the definition of this LT_INIT comes from revision $macro_revision. $progname: You should recreate aclocal.m4 with macros from revision $package_revision $progname: of $PACKAGE $VERSION and run autoconf again. _LT_EOF fi exit $EXIT_MISMATCH fi } # libtool_options_prep [ARG]... # ----------------------------- # Preparation for options parsed by libtool. libtool_options_prep () { $debug_mode # Option defaults: opt_config=false opt_dlopen= opt_dry_run=false opt_help=false opt_mode= opt_preserve_dup_deps=false opt_quiet=false nonopt= preserve_args= _G_rc_lt_options_prep=: _G_rc_lt_options_prep=: # Shorthand for --mode=foo, only valid as the first argument case $1 in clean|clea|cle|cl) shift; set dummy --mode clean ${1+"$@"}; shift ;; compile|compil|compi|comp|com|co|c) shift; set dummy --mode compile ${1+"$@"}; shift ;; execute|execut|execu|exec|exe|ex|e) shift; set dummy --mode execute ${1+"$@"}; shift ;; finish|finis|fini|fin|fi|f) shift; set dummy --mode finish ${1+"$@"}; shift ;; install|instal|insta|inst|ins|in|i) shift; set dummy --mode install ${1+"$@"}; shift ;; link|lin|li|l) shift; set dummy --mode link ${1+"$@"}; shift ;; uninstall|uninstal|uninsta|uninst|unins|unin|uni|un|u) shift; set dummy --mode uninstall ${1+"$@"}; shift ;; *) _G_rc_lt_options_prep=false ;; esac if $_G_rc_lt_options_prep; then # Pass back the list of options. func_quote eval ${1+"$@"} libtool_options_prep_result=$func_quote_result fi } func_add_hook func_options_prep libtool_options_prep # libtool_parse_options [ARG]... # --------------------------------- # Provide handling for libtool specific options. libtool_parse_options () { $debug_cmd _G_rc_lt_parse_options=false # Perform our own loop to consume as many options as possible in # each iteration. while test $# -gt 0; do _G_match_lt_parse_options=: _G_opt=$1 shift case $_G_opt in --dry-run|--dryrun|-n) opt_dry_run=: ;; --config) func_config ;; --dlopen|-dlopen) opt_dlopen="${opt_dlopen+$opt_dlopen }$1" shift ;; --preserve-dup-deps) opt_preserve_dup_deps=: ;; --features) func_features ;; --finish) set dummy --mode finish ${1+"$@"}; shift ;; --help) opt_help=: ;; --help-all) opt_help=': help-all' ;; --mode) test $# = 0 && func_missing_arg $_G_opt && break opt_mode=$1 case $1 in # Valid mode arguments: clean|compile|execute|finish|install|link|relink|uninstall) ;; # Catch anything else as an error *) func_error "invalid argument for $_G_opt" exit_cmd=exit break ;; esac shift ;; --no-silent|--no-quiet) opt_quiet=false func_append preserve_args " $_G_opt" ;; --no-warnings|--no-warning|--no-warn) opt_warning=false func_append preserve_args " $_G_opt" ;; --no-verbose) opt_verbose=false func_append preserve_args " $_G_opt" ;; --silent|--quiet) opt_quiet=: opt_verbose=false func_append preserve_args " $_G_opt" ;; --tag) test $# = 0 && func_missing_arg $_G_opt && break opt_tag=$1 func_append preserve_args " $_G_opt $1" func_enable_tag "$1" shift ;; --verbose|-v) opt_quiet=false opt_verbose=: func_append preserve_args " $_G_opt" ;; # An option not handled by this hook function: *) set dummy "$_G_opt" ${1+"$@"} ; shift _G_match_lt_parse_options=false break ;; esac $_G_match_lt_parse_options && _G_rc_lt_parse_options=: done if $_G_rc_lt_parse_options; then # save modified positional parameters for caller func_quote eval ${1+"$@"} libtool_parse_options_result=$func_quote_result fi } func_add_hook func_parse_options libtool_parse_options # libtool_validate_options [ARG]... # --------------------------------- # Perform any sanity checks on option settings and/or unconsumed # arguments. libtool_validate_options () { # save first non-option argument if test 0 -lt $#; then nonopt=$1 shift fi # preserve --debug test : = "$debug_cmd" || func_append preserve_args " --debug" case $host in # Solaris2 added to fix http://debbugs.gnu.org/cgi/bugreport.cgi?bug=16452 # see also: http://gcc.gnu.org/bugzilla/show_bug.cgi?id=59788 *cygwin* | *mingw* | *pw32* | *cegcc* | *solaris2* | *os2*) # don't eliminate duplications in $postdeps and $predeps opt_duplicate_compiler_generated_deps=: ;; *) opt_duplicate_compiler_generated_deps=$opt_preserve_dup_deps ;; esac $opt_help || { # Sanity checks first: func_check_version_match test yes != "$build_libtool_libs" \ && test yes != "$build_old_libs" \ && func_fatal_configuration "not configured to build any kind of library" # Darwin sucks eval std_shrext=\"$shrext_cmds\" # Only execute mode is allowed to have -dlopen flags. if test -n "$opt_dlopen" && test execute != "$opt_mode"; then func_error "unrecognized option '-dlopen'" $ECHO "$help" 1>&2 exit $EXIT_FAILURE fi # Change the help message to a mode-specific one. generic_help=$help help="Try '$progname --help --mode=$opt_mode' for more information." } # Pass back the unparsed argument list func_quote eval ${1+"$@"} libtool_validate_options_result=$func_quote_result } func_add_hook func_validate_options libtool_validate_options # Process options as early as possible so that --help and --version # can return quickly. func_options ${1+"$@"} eval set dummy "$func_options_result"; shift ## ----------- ## ## Main. ## ## ----------- ## magic='%%%MAGIC variable%%%' magic_exe='%%%MAGIC EXE variable%%%' # Global variables. extracted_archives= extracted_serial=0 # If this variable is set in any of the actions, the command in it # will be execed at the end. This prevents here-documents from being # left over by shells. exec_cmd= # A function that is used when there is no print builtin or printf. func_fallback_echo () { eval 'cat <<_LTECHO_EOF $1 _LTECHO_EOF' } # func_generated_by_libtool # True iff stdin has been generated by Libtool. This function is only # a basic sanity check; it will hardly flush out determined imposters. func_generated_by_libtool_p () { $GREP "^# Generated by .*$PACKAGE" > /dev/null 2>&1 } # func_lalib_p file # True iff FILE is a libtool '.la' library or '.lo' object file. # This function is only a basic sanity check; it will hardly flush out # determined imposters. func_lalib_p () { test -f "$1" && $SED -e 4q "$1" 2>/dev/null | func_generated_by_libtool_p } # func_lalib_unsafe_p file # True iff FILE is a libtool '.la' library or '.lo' object file. # This function implements the same check as func_lalib_p without # resorting to external programs. To this end, it redirects stdin and # closes it afterwards, without saving the original file descriptor. # As a safety measure, use it only where a negative result would be # fatal anyway. Works if 'file' does not exist. func_lalib_unsafe_p () { lalib_p=no if test -f "$1" && test -r "$1" && exec 5<&0 <"$1"; then for lalib_p_l in 1 2 3 4 do read lalib_p_line case $lalib_p_line in \#\ Generated\ by\ *$PACKAGE* ) lalib_p=yes; break;; esac done exec 0<&5 5<&- fi test yes = "$lalib_p" } # func_ltwrapper_script_p file # True iff FILE is a libtool wrapper script # This function is only a basic sanity check; it will hardly flush out # determined imposters. func_ltwrapper_script_p () { test -f "$1" && $lt_truncate_bin < "$1" 2>/dev/null | func_generated_by_libtool_p } # func_ltwrapper_executable_p file # True iff FILE is a libtool wrapper executable # This function is only a basic sanity check; it will hardly flush out # determined imposters. func_ltwrapper_executable_p () { func_ltwrapper_exec_suffix= case $1 in *.exe) ;; *) func_ltwrapper_exec_suffix=.exe ;; esac $GREP "$magic_exe" "$1$func_ltwrapper_exec_suffix" >/dev/null 2>&1 } # func_ltwrapper_scriptname file # Assumes file is an ltwrapper_executable # uses $file to determine the appropriate filename for a # temporary ltwrapper_script. func_ltwrapper_scriptname () { func_dirname_and_basename "$1" "" "." func_stripname '' '.exe' "$func_basename_result" func_ltwrapper_scriptname_result=$func_dirname_result/$objdir/${func_stripname_result}_ltshwrapper } # func_ltwrapper_p file # True iff FILE is a libtool wrapper script or wrapper executable # This function is only a basic sanity check; it will hardly flush out # determined imposters. func_ltwrapper_p () { func_ltwrapper_script_p "$1" || func_ltwrapper_executable_p "$1" } # func_execute_cmds commands fail_cmd # Execute tilde-delimited COMMANDS. # If FAIL_CMD is given, eval that upon failure. # FAIL_CMD may read-access the current command in variable CMD! func_execute_cmds () { $debug_cmd save_ifs=$IFS; IFS='~' for cmd in $1; do IFS=$sp$nl eval cmd=\"$cmd\" IFS=$save_ifs func_show_eval "$cmd" "${2-:}" done IFS=$save_ifs } # func_source file # Source FILE, adding directory component if necessary. # Note that it is not necessary on cygwin/mingw to append a dot to # FILE even if both FILE and FILE.exe exist: automatic-append-.exe # behavior happens only for exec(3), not for open(2)! Also, sourcing # 'FILE.' does not work on cygwin managed mounts. func_source () { $debug_cmd case $1 in */* | *\\*) . "$1" ;; *) . "./$1" ;; esac } # func_resolve_sysroot PATH # Replace a leading = in PATH with a sysroot. Store the result into # func_resolve_sysroot_result func_resolve_sysroot () { func_resolve_sysroot_result=$1 case $func_resolve_sysroot_result in =*) func_stripname '=' '' "$func_resolve_sysroot_result" func_resolve_sysroot_result=$lt_sysroot$func_stripname_result ;; esac } # func_replace_sysroot PATH # If PATH begins with the sysroot, replace it with = and # store the result into func_replace_sysroot_result. func_replace_sysroot () { case $lt_sysroot:$1 in ?*:"$lt_sysroot"*) func_stripname "$lt_sysroot" '' "$1" func_replace_sysroot_result='='$func_stripname_result ;; *) # Including no sysroot. func_replace_sysroot_result=$1 ;; esac } # func_infer_tag arg # Infer tagged configuration to use if any are available and # if one wasn't chosen via the "--tag" command line option. # Only attempt this if the compiler in the base compile # command doesn't match the default compiler. # arg is usually of the form 'gcc ...' func_infer_tag () { $debug_cmd if test -n "$available_tags" && test -z "$tagname"; then CC_quoted= for arg in $CC; do func_append_quoted CC_quoted "$arg" done CC_expanded=`func_echo_all $CC` CC_quoted_expanded=`func_echo_all $CC_quoted` case $@ in # Blanks in the command may have been stripped by the calling shell, # but not from the CC environment variable when configure was run. " $CC "* | "$CC "* | " $CC_expanded "* | "$CC_expanded "* | \ " $CC_quoted"* | "$CC_quoted "* | " $CC_quoted_expanded "* | "$CC_quoted_expanded "*) ;; # Blanks at the start of $base_compile will cause this to fail # if we don't check for them as well. *) for z in $available_tags; do if $GREP "^# ### BEGIN LIBTOOL TAG CONFIG: $z$" < "$progpath" > /dev/null; then # Evaluate the configuration. eval "`$SED -n -e '/^# ### BEGIN LIBTOOL TAG CONFIG: '$z'$/,/^# ### END LIBTOOL TAG CONFIG: '$z'$/p' < $progpath`" CC_quoted= for arg in $CC; do # Double-quote args containing other shell metacharacters. func_append_quoted CC_quoted "$arg" done CC_expanded=`func_echo_all $CC` CC_quoted_expanded=`func_echo_all $CC_quoted` case "$@ " in " $CC "* | "$CC "* | " $CC_expanded "* | "$CC_expanded "* | \ " $CC_quoted"* | "$CC_quoted "* | " $CC_quoted_expanded "* | "$CC_quoted_expanded "*) # The compiler in the base compile command matches # the one in the tagged configuration. # Assume this is the tagged configuration we want. tagname=$z break ;; esac fi done # If $tagname still isn't set, then no tagged configuration # was found and let the user know that the "--tag" command # line option must be used. if test -z "$tagname"; then func_echo "unable to infer tagged configuration" func_fatal_error "specify a tag with '--tag'" # else # func_verbose "using $tagname tagged configuration" fi ;; esac fi } # func_write_libtool_object output_name pic_name nonpic_name # Create a libtool object file (analogous to a ".la" file), # but don't create it if we're doing a dry run. func_write_libtool_object () { write_libobj=$1 if test yes = "$build_libtool_libs"; then write_lobj=\'$2\' else write_lobj=none fi if test yes = "$build_old_libs"; then write_oldobj=\'$3\' else write_oldobj=none fi $opt_dry_run || { cat >${write_libobj}T </dev/null` if test "$?" -eq 0 && test -n "$func_convert_core_file_wine_to_w32_tmp"; then func_convert_core_file_wine_to_w32_result=`$ECHO "$func_convert_core_file_wine_to_w32_tmp" | $SED -e "$sed_naive_backslashify"` else func_convert_core_file_wine_to_w32_result= fi fi } # end: func_convert_core_file_wine_to_w32 # func_convert_core_path_wine_to_w32 ARG # Helper function used by path conversion functions when $build is *nix, and # $host is mingw, cygwin, or some other w32 environment. Relies on a correctly # configured wine environment available, with the winepath program in $build's # $PATH. Assumes ARG has no leading or trailing path separator characters. # # ARG is path to be converted from $build format to win32. # Result is available in $func_convert_core_path_wine_to_w32_result. # Unconvertible file (directory) names in ARG are skipped; if no directory names # are convertible, then the result may be empty. func_convert_core_path_wine_to_w32 () { $debug_cmd # unfortunately, winepath doesn't convert paths, only file names func_convert_core_path_wine_to_w32_result= if test -n "$1"; then oldIFS=$IFS IFS=: for func_convert_core_path_wine_to_w32_f in $1; do IFS=$oldIFS func_convert_core_file_wine_to_w32 "$func_convert_core_path_wine_to_w32_f" if test -n "$func_convert_core_file_wine_to_w32_result"; then if test -z "$func_convert_core_path_wine_to_w32_result"; then func_convert_core_path_wine_to_w32_result=$func_convert_core_file_wine_to_w32_result else func_append func_convert_core_path_wine_to_w32_result ";$func_convert_core_file_wine_to_w32_result" fi fi done IFS=$oldIFS fi } # end: func_convert_core_path_wine_to_w32 # func_cygpath ARGS... # Wrapper around calling the cygpath program via LT_CYGPATH. This is used when # when (1) $build is *nix and Cygwin is hosted via a wine environment; or (2) # $build is MSYS and $host is Cygwin, or (3) $build is Cygwin. In case (1) or # (2), returns the Cygwin file name or path in func_cygpath_result (input # file name or path is assumed to be in w32 format, as previously converted # from $build's *nix or MSYS format). In case (3), returns the w32 file name # or path in func_cygpath_result (input file name or path is assumed to be in # Cygwin format). Returns an empty string on error. # # ARGS are passed to cygpath, with the last one being the file name or path to # be converted. # # Specify the absolute *nix (or w32) name to cygpath in the LT_CYGPATH # environment variable; do not put it in $PATH. func_cygpath () { $debug_cmd if test -n "$LT_CYGPATH" && test -f "$LT_CYGPATH"; then func_cygpath_result=`$LT_CYGPATH "$@" 2>/dev/null` if test "$?" -ne 0; then # on failure, ensure result is empty func_cygpath_result= fi else func_cygpath_result= func_error "LT_CYGPATH is empty or specifies non-existent file: '$LT_CYGPATH'" fi } #end: func_cygpath # func_convert_core_msys_to_w32 ARG # Convert file name or path ARG from MSYS format to w32 format. Return # result in func_convert_core_msys_to_w32_result. func_convert_core_msys_to_w32 () { $debug_cmd # awkward: cmd appends spaces to result func_convert_core_msys_to_w32_result=`( cmd //c echo "$1" ) 2>/dev/null | $SED -e 's/[ ]*$//' -e "$sed_naive_backslashify"` } #end: func_convert_core_msys_to_w32 # func_convert_file_check ARG1 ARG2 # Verify that ARG1 (a file name in $build format) was converted to $host # format in ARG2. Otherwise, emit an error message, but continue (resetting # func_to_host_file_result to ARG1). func_convert_file_check () { $debug_cmd if test -z "$2" && test -n "$1"; then func_error "Could not determine host file name corresponding to" func_error " '$1'" func_error "Continuing, but uninstalled executables may not work." # Fallback: func_to_host_file_result=$1 fi } # end func_convert_file_check # func_convert_path_check FROM_PATHSEP TO_PATHSEP FROM_PATH TO_PATH # Verify that FROM_PATH (a path in $build format) was converted to $host # format in TO_PATH. Otherwise, emit an error message, but continue, resetting # func_to_host_file_result to a simplistic fallback value (see below). func_convert_path_check () { $debug_cmd if test -z "$4" && test -n "$3"; then func_error "Could not determine the host path corresponding to" func_error " '$3'" func_error "Continuing, but uninstalled executables may not work." # Fallback. This is a deliberately simplistic "conversion" and # should not be "improved". See libtool.info. if test "x$1" != "x$2"; then lt_replace_pathsep_chars="s|$1|$2|g" func_to_host_path_result=`echo "$3" | $SED -e "$lt_replace_pathsep_chars"` else func_to_host_path_result=$3 fi fi } # end func_convert_path_check # func_convert_path_front_back_pathsep FRONTPAT BACKPAT REPL ORIG # Modifies func_to_host_path_result by prepending REPL if ORIG matches FRONTPAT # and appending REPL if ORIG matches BACKPAT. func_convert_path_front_back_pathsep () { $debug_cmd case $4 in $1 ) func_to_host_path_result=$3$func_to_host_path_result ;; esac case $4 in $2 ) func_append func_to_host_path_result "$3" ;; esac } # end func_convert_path_front_back_pathsep ################################################## # $build to $host FILE NAME CONVERSION FUNCTIONS # ################################################## # invoked via '$to_host_file_cmd ARG' # # In each case, ARG is the path to be converted from $build to $host format. # Result will be available in $func_to_host_file_result. # func_to_host_file ARG # Converts the file name ARG from $build format to $host format. Return result # in func_to_host_file_result. func_to_host_file () { $debug_cmd $to_host_file_cmd "$1" } # end func_to_host_file # func_to_tool_file ARG LAZY # converts the file name ARG from $build format to toolchain format. Return # result in func_to_tool_file_result. If the conversion in use is listed # in (the comma separated) LAZY, no conversion takes place. func_to_tool_file () { $debug_cmd case ,$2, in *,"$to_tool_file_cmd",*) func_to_tool_file_result=$1 ;; *) $to_tool_file_cmd "$1" func_to_tool_file_result=$func_to_host_file_result ;; esac } # end func_to_tool_file # func_convert_file_noop ARG # Copy ARG to func_to_host_file_result. func_convert_file_noop () { func_to_host_file_result=$1 } # end func_convert_file_noop # func_convert_file_msys_to_w32 ARG # Convert file name ARG from (mingw) MSYS to (mingw) w32 format; automatic # conversion to w32 is not available inside the cwrapper. Returns result in # func_to_host_file_result. func_convert_file_msys_to_w32 () { $debug_cmd func_to_host_file_result=$1 if test -n "$1"; then func_convert_core_msys_to_w32 "$1" func_to_host_file_result=$func_convert_core_msys_to_w32_result fi func_convert_file_check "$1" "$func_to_host_file_result" } # end func_convert_file_msys_to_w32 # func_convert_file_cygwin_to_w32 ARG # Convert file name ARG from Cygwin to w32 format. Returns result in # func_to_host_file_result. func_convert_file_cygwin_to_w32 () { $debug_cmd func_to_host_file_result=$1 if test -n "$1"; then # because $build is cygwin, we call "the" cygpath in $PATH; no need to use # LT_CYGPATH in this case. func_to_host_file_result=`cygpath -m "$1"` fi func_convert_file_check "$1" "$func_to_host_file_result" } # end func_convert_file_cygwin_to_w32 # func_convert_file_nix_to_w32 ARG # Convert file name ARG from *nix to w32 format. Requires a wine environment # and a working winepath. Returns result in func_to_host_file_result. func_convert_file_nix_to_w32 () { $debug_cmd func_to_host_file_result=$1 if test -n "$1"; then func_convert_core_file_wine_to_w32 "$1" func_to_host_file_result=$func_convert_core_file_wine_to_w32_result fi func_convert_file_check "$1" "$func_to_host_file_result" } # end func_convert_file_nix_to_w32 # func_convert_file_msys_to_cygwin ARG # Convert file name ARG from MSYS to Cygwin format. Requires LT_CYGPATH set. # Returns result in func_to_host_file_result. func_convert_file_msys_to_cygwin () { $debug_cmd func_to_host_file_result=$1 if test -n "$1"; then func_convert_core_msys_to_w32 "$1" func_cygpath -u "$func_convert_core_msys_to_w32_result" func_to_host_file_result=$func_cygpath_result fi func_convert_file_check "$1" "$func_to_host_file_result" } # end func_convert_file_msys_to_cygwin # func_convert_file_nix_to_cygwin ARG # Convert file name ARG from *nix to Cygwin format. Requires Cygwin installed # in a wine environment, working winepath, and LT_CYGPATH set. Returns result # in func_to_host_file_result. func_convert_file_nix_to_cygwin () { $debug_cmd func_to_host_file_result=$1 if test -n "$1"; then # convert from *nix to w32, then use cygpath to convert from w32 to cygwin. func_convert_core_file_wine_to_w32 "$1" func_cygpath -u "$func_convert_core_file_wine_to_w32_result" func_to_host_file_result=$func_cygpath_result fi func_convert_file_check "$1" "$func_to_host_file_result" } # end func_convert_file_nix_to_cygwin ############################################# # $build to $host PATH CONVERSION FUNCTIONS # ############################################# # invoked via '$to_host_path_cmd ARG' # # In each case, ARG is the path to be converted from $build to $host format. # The result will be available in $func_to_host_path_result. # # Path separators are also converted from $build format to $host format. If # ARG begins or ends with a path separator character, it is preserved (but # converted to $host format) on output. # # All path conversion functions are named using the following convention: # file name conversion function : func_convert_file_X_to_Y () # path conversion function : func_convert_path_X_to_Y () # where, for any given $build/$host combination the 'X_to_Y' value is the # same. If conversion functions are added for new $build/$host combinations, # the two new functions must follow this pattern, or func_init_to_host_path_cmd # will break. # func_init_to_host_path_cmd # Ensures that function "pointer" variable $to_host_path_cmd is set to the # appropriate value, based on the value of $to_host_file_cmd. to_host_path_cmd= func_init_to_host_path_cmd () { $debug_cmd if test -z "$to_host_path_cmd"; then func_stripname 'func_convert_file_' '' "$to_host_file_cmd" to_host_path_cmd=func_convert_path_$func_stripname_result fi } # func_to_host_path ARG # Converts the path ARG from $build format to $host format. Return result # in func_to_host_path_result. func_to_host_path () { $debug_cmd func_init_to_host_path_cmd $to_host_path_cmd "$1" } # end func_to_host_path # func_convert_path_noop ARG # Copy ARG to func_to_host_path_result. func_convert_path_noop () { func_to_host_path_result=$1 } # end func_convert_path_noop # func_convert_path_msys_to_w32 ARG # Convert path ARG from (mingw) MSYS to (mingw) w32 format; automatic # conversion to w32 is not available inside the cwrapper. Returns result in # func_to_host_path_result. func_convert_path_msys_to_w32 () { $debug_cmd func_to_host_path_result=$1 if test -n "$1"; then # Remove leading and trailing path separator characters from ARG. MSYS # behavior is inconsistent here; cygpath turns them into '.;' and ';.'; # and winepath ignores them completely. func_stripname : : "$1" func_to_host_path_tmp1=$func_stripname_result func_convert_core_msys_to_w32 "$func_to_host_path_tmp1" func_to_host_path_result=$func_convert_core_msys_to_w32_result func_convert_path_check : ";" \ "$func_to_host_path_tmp1" "$func_to_host_path_result" func_convert_path_front_back_pathsep ":*" "*:" ";" "$1" fi } # end func_convert_path_msys_to_w32 # func_convert_path_cygwin_to_w32 ARG # Convert path ARG from Cygwin to w32 format. Returns result in # func_to_host_file_result. func_convert_path_cygwin_to_w32 () { $debug_cmd func_to_host_path_result=$1 if test -n "$1"; then # See func_convert_path_msys_to_w32: func_stripname : : "$1" func_to_host_path_tmp1=$func_stripname_result func_to_host_path_result=`cygpath -m -p "$func_to_host_path_tmp1"` func_convert_path_check : ";" \ "$func_to_host_path_tmp1" "$func_to_host_path_result" func_convert_path_front_back_pathsep ":*" "*:" ";" "$1" fi } # end func_convert_path_cygwin_to_w32 # func_convert_path_nix_to_w32 ARG # Convert path ARG from *nix to w32 format. Requires a wine environment and # a working winepath. Returns result in func_to_host_file_result. func_convert_path_nix_to_w32 () { $debug_cmd func_to_host_path_result=$1 if test -n "$1"; then # See func_convert_path_msys_to_w32: func_stripname : : "$1" func_to_host_path_tmp1=$func_stripname_result func_convert_core_path_wine_to_w32 "$func_to_host_path_tmp1" func_to_host_path_result=$func_convert_core_path_wine_to_w32_result func_convert_path_check : ";" \ "$func_to_host_path_tmp1" "$func_to_host_path_result" func_convert_path_front_back_pathsep ":*" "*:" ";" "$1" fi } # end func_convert_path_nix_to_w32 # func_convert_path_msys_to_cygwin ARG # Convert path ARG from MSYS to Cygwin format. Requires LT_CYGPATH set. # Returns result in func_to_host_file_result. func_convert_path_msys_to_cygwin () { $debug_cmd func_to_host_path_result=$1 if test -n "$1"; then # See func_convert_path_msys_to_w32: func_stripname : : "$1" func_to_host_path_tmp1=$func_stripname_result func_convert_core_msys_to_w32 "$func_to_host_path_tmp1" func_cygpath -u -p "$func_convert_core_msys_to_w32_result" func_to_host_path_result=$func_cygpath_result func_convert_path_check : : \ "$func_to_host_path_tmp1" "$func_to_host_path_result" func_convert_path_front_back_pathsep ":*" "*:" : "$1" fi } # end func_convert_path_msys_to_cygwin # func_convert_path_nix_to_cygwin ARG # Convert path ARG from *nix to Cygwin format. Requires Cygwin installed in a # a wine environment, working winepath, and LT_CYGPATH set. Returns result in # func_to_host_file_result. func_convert_path_nix_to_cygwin () { $debug_cmd func_to_host_path_result=$1 if test -n "$1"; then # Remove leading and trailing path separator characters from # ARG. msys behavior is inconsistent here, cygpath turns them # into '.;' and ';.', and winepath ignores them completely. func_stripname : : "$1" func_to_host_path_tmp1=$func_stripname_result func_convert_core_path_wine_to_w32 "$func_to_host_path_tmp1" func_cygpath -u -p "$func_convert_core_path_wine_to_w32_result" func_to_host_path_result=$func_cygpath_result func_convert_path_check : : \ "$func_to_host_path_tmp1" "$func_to_host_path_result" func_convert_path_front_back_pathsep ":*" "*:" : "$1" fi } # end func_convert_path_nix_to_cygwin # func_dll_def_p FILE # True iff FILE is a Windows DLL '.def' file. # Keep in sync with _LT_DLL_DEF_P in libtool.m4 func_dll_def_p () { $debug_cmd func_dll_def_p_tmp=`$SED -n \ -e 's/^[ ]*//' \ -e '/^\(;.*\)*$/d' \ -e 's/^\(EXPORTS\|LIBRARY\)\([ ].*\)*$/DEF/p' \ -e q \ "$1"` test DEF = "$func_dll_def_p_tmp" } # func_mode_compile arg... func_mode_compile () { $debug_cmd # Get the compilation command and the source file. base_compile= srcfile=$nonopt # always keep a non-empty value in "srcfile" suppress_opt=yes suppress_output= arg_mode=normal libobj= later= pie_flag= for arg do case $arg_mode in arg ) # do not "continue". Instead, add this to base_compile lastarg=$arg arg_mode=normal ;; target ) libobj=$arg arg_mode=normal continue ;; normal ) # Accept any command-line options. case $arg in -o) test -n "$libobj" && \ func_fatal_error "you cannot specify '-o' more than once" arg_mode=target continue ;; -pie | -fpie | -fPIE) func_append pie_flag " $arg" continue ;; -shared | -static | -prefer-pic | -prefer-non-pic) func_append later " $arg" continue ;; -no-suppress) suppress_opt=no continue ;; -Xcompiler) arg_mode=arg # the next one goes into the "base_compile" arg list continue # The current "srcfile" will either be retained or ;; # replaced later. I would guess that would be a bug. -Wc,*) func_stripname '-Wc,' '' "$arg" args=$func_stripname_result lastarg= save_ifs=$IFS; IFS=, for arg in $args; do IFS=$save_ifs func_append_quoted lastarg "$arg" done IFS=$save_ifs func_stripname ' ' '' "$lastarg" lastarg=$func_stripname_result # Add the arguments to base_compile. func_append base_compile " $lastarg" continue ;; *) # Accept the current argument as the source file. # The previous "srcfile" becomes the current argument. # lastarg=$srcfile srcfile=$arg ;; esac # case $arg ;; esac # case $arg_mode # Aesthetically quote the previous argument. func_append_quoted base_compile "$lastarg" done # for arg case $arg_mode in arg) func_fatal_error "you must specify an argument for -Xcompile" ;; target) func_fatal_error "you must specify a target with '-o'" ;; *) # Get the name of the library object. test -z "$libobj" && { func_basename "$srcfile" libobj=$func_basename_result } ;; esac # Recognize several different file suffixes. # If the user specifies -o file.o, it is replaced with file.lo case $libobj in *.[cCFSifmso] | \ *.ada | *.adb | *.ads | *.asm | \ *.c++ | *.cc | *.ii | *.class | *.cpp | *.cxx | \ *.[fF][09]? | *.for | *.java | *.go | *.obj | *.sx | *.cu | *.cup) func_xform "$libobj" libobj=$func_xform_result ;; esac case $libobj in *.lo) func_lo2o "$libobj"; obj=$func_lo2o_result ;; *) func_fatal_error "cannot determine name of library object from '$libobj'" ;; esac func_infer_tag $base_compile for arg in $later; do case $arg in -shared) test yes = "$build_libtool_libs" \ || func_fatal_configuration "cannot build a shared library" build_old_libs=no continue ;; -static) build_libtool_libs=no build_old_libs=yes continue ;; -prefer-pic) pic_mode=yes continue ;; -prefer-non-pic) pic_mode=no continue ;; esac done func_quote_arg pretty "$libobj" test "X$libobj" != "X$func_quote_arg_result" \ && $ECHO "X$libobj" | $GREP '[]~#^*{};<>?"'"'"' &()|`$[]' \ && func_warning "libobj name '$libobj' may not contain shell special characters." func_dirname_and_basename "$obj" "/" "" objname=$func_basename_result xdir=$func_dirname_result lobj=$xdir$objdir/$objname test -z "$base_compile" && \ func_fatal_help "you must specify a compilation command" # Delete any leftover library objects. if test yes = "$build_old_libs"; then removelist="$obj $lobj $libobj ${libobj}T" else removelist="$lobj $libobj ${libobj}T" fi # On Cygwin there's no "real" PIC flag so we must build both object types case $host_os in cygwin* | mingw* | pw32* | os2* | cegcc*) pic_mode=default ;; esac if test no = "$pic_mode" && test pass_all != "$deplibs_check_method"; then # non-PIC code in shared libraries is not supported pic_mode=default fi # Calculate the filename of the output object if compiler does # not support -o with -c if test no = "$compiler_c_o"; then output_obj=`$ECHO "$srcfile" | $SED 's%^.*/%%; s%\.[^.]*$%%'`.$objext lockfile=$output_obj.lock else output_obj= need_locks=no lockfile= fi # Lock this critical section if it is needed # We use this script file to make the link, it avoids creating a new file if test yes = "$need_locks"; then until $opt_dry_run || ln "$progpath" "$lockfile" 2>/dev/null; do func_echo "Waiting for $lockfile to be removed" sleep 2 done elif test warn = "$need_locks"; then if test -f "$lockfile"; then $ECHO "\ *** ERROR, $lockfile exists and contains: `cat $lockfile 2>/dev/null` This indicates that another process is trying to use the same temporary object file, and libtool could not work around it because your compiler does not support '-c' and '-o' together. If you repeat this compilation, it may succeed, by chance, but you had better avoid parallel builds (make -j) in this platform, or get a better compiler." $opt_dry_run || $RM $removelist exit $EXIT_FAILURE fi func_append removelist " $output_obj" $ECHO "$srcfile" > "$lockfile" fi $opt_dry_run || $RM $removelist func_append removelist " $lockfile" trap '$opt_dry_run || $RM $removelist; exit $EXIT_FAILURE' 1 2 15 func_to_tool_file "$srcfile" func_convert_file_msys_to_w32 srcfile=$func_to_tool_file_result func_quote_arg pretty "$srcfile" qsrcfile=$func_quote_arg_result # Only build a PIC object if we are building libtool libraries. if test yes = "$build_libtool_libs"; then # Without this assignment, base_compile gets emptied. fbsd_hideous_sh_bug=$base_compile if test no != "$pic_mode"; then command="$base_compile $qsrcfile $pic_flag" else # Don't build PIC code command="$base_compile $qsrcfile" fi func_mkdir_p "$xdir$objdir" if test -z "$output_obj"; then # Place PIC objects in $objdir func_append command " -o $lobj" fi func_show_eval_locale "$command" \ 'test -n "$output_obj" && $RM $removelist; exit $EXIT_FAILURE' if test warn = "$need_locks" && test "X`cat $lockfile 2>/dev/null`" != "X$srcfile"; then $ECHO "\ *** ERROR, $lockfile contains: `cat $lockfile 2>/dev/null` but it should contain: $srcfile This indicates that another process is trying to use the same temporary object file, and libtool could not work around it because your compiler does not support '-c' and '-o' together. If you repeat this compilation, it may succeed, by chance, but you had better avoid parallel builds (make -j) in this platform, or get a better compiler." $opt_dry_run || $RM $removelist exit $EXIT_FAILURE fi # Just move the object if needed, then go on to compile the next one if test -n "$output_obj" && test "X$output_obj" != "X$lobj"; then func_show_eval '$MV "$output_obj" "$lobj"' \ 'error=$?; $opt_dry_run || $RM $removelist; exit $error' fi # Allow error messages only from the first compilation. if test yes = "$suppress_opt"; then suppress_output=' >/dev/null 2>&1' fi fi # Only build a position-dependent object if we build old libraries. if test yes = "$build_old_libs"; then if test yes != "$pic_mode"; then # Don't build PIC code command="$base_compile $qsrcfile$pie_flag" else command="$base_compile $qsrcfile $pic_flag" fi if test yes = "$compiler_c_o"; then func_append command " -o $obj" fi # Suppress compiler output if we already did a PIC compilation. func_append command "$suppress_output" func_show_eval_locale "$command" \ '$opt_dry_run || $RM $removelist; exit $EXIT_FAILURE' if test warn = "$need_locks" && test "X`cat $lockfile 2>/dev/null`" != "X$srcfile"; then $ECHO "\ *** ERROR, $lockfile contains: `cat $lockfile 2>/dev/null` but it should contain: $srcfile This indicates that another process is trying to use the same temporary object file, and libtool could not work around it because your compiler does not support '-c' and '-o' together. If you repeat this compilation, it may succeed, by chance, but you had better avoid parallel builds (make -j) in this platform, or get a better compiler." $opt_dry_run || $RM $removelist exit $EXIT_FAILURE fi # Just move the object if needed if test -n "$output_obj" && test "X$output_obj" != "X$obj"; then func_show_eval '$MV "$output_obj" "$obj"' \ 'error=$?; $opt_dry_run || $RM $removelist; exit $error' fi fi $opt_dry_run || { func_write_libtool_object "$libobj" "$objdir/$objname" "$objname" # Unlock the critical section if it was locked if test no != "$need_locks"; then removelist=$lockfile $RM "$lockfile" fi } exit $EXIT_SUCCESS } $opt_help || { test compile = "$opt_mode" && func_mode_compile ${1+"$@"} } func_mode_help () { # We need to display help for each of the modes. case $opt_mode in "") # Generic help is extracted from the usage comments # at the start of this file. func_help ;; clean) $ECHO \ "Usage: $progname [OPTION]... --mode=clean RM [RM-OPTION]... FILE... Remove files from the build directory. RM is the name of the program to use to delete files associated with each FILE (typically '/bin/rm'). RM-OPTIONS are options (such as '-f') to be passed to RM. If FILE is a libtool library, object or program, all the files associated with it are deleted. Otherwise, only FILE itself is deleted using RM." ;; compile) $ECHO \ "Usage: $progname [OPTION]... --mode=compile COMPILE-COMMAND... SOURCEFILE Compile a source file into a libtool library object. This mode accepts the following additional options: -o OUTPUT-FILE set the output file name to OUTPUT-FILE -no-suppress do not suppress compiler output for multiple passes -prefer-pic try to build PIC objects only -prefer-non-pic try to build non-PIC objects only -shared do not build a '.o' file suitable for static linking -static only build a '.o' file suitable for static linking -Wc,FLAG -Xcompiler FLAG pass FLAG directly to the compiler COMPILE-COMMAND is a command to be used in creating a 'standard' object file from the given SOURCEFILE. The output file name is determined by removing the directory component from SOURCEFILE, then substituting the C source code suffix '.c' with the library object suffix, '.lo'." ;; execute) $ECHO \ "Usage: $progname [OPTION]... --mode=execute COMMAND [ARGS]... Automatically set library path, then run a program. This mode accepts the following additional options: -dlopen FILE add the directory containing FILE to the library path This mode sets the library path environment variable according to '-dlopen' flags. If any of the ARGS are libtool executable wrappers, then they are translated into their corresponding uninstalled binary, and any of their required library directories are added to the library path. Then, COMMAND is executed, with ARGS as arguments." ;; finish) $ECHO \ "Usage: $progname [OPTION]... --mode=finish [LIBDIR]... Complete the installation of libtool libraries. Each LIBDIR is a directory that contains libtool libraries. The commands that this mode executes may require superuser privileges. Use the '--dry-run' option if you just want to see what would be executed." ;; install) $ECHO \ "Usage: $progname [OPTION]... --mode=install INSTALL-COMMAND... Install executables or libraries. INSTALL-COMMAND is the installation command. The first component should be either the 'install' or 'cp' program. The following components of INSTALL-COMMAND are treated specially: -inst-prefix-dir PREFIX-DIR Use PREFIX-DIR as a staging area for installation The rest of the components are interpreted as arguments to that command (only BSD-compatible install options are recognized)." ;; link) $ECHO \ "Usage: $progname [OPTION]... --mode=link LINK-COMMAND... Link object files or libraries together to form another library, or to create an executable program. LINK-COMMAND is a command using the C compiler that you would use to create a program from several object files. The following components of LINK-COMMAND are treated specially: -all-static do not do any dynamic linking at all -avoid-version do not add a version suffix if possible -bindir BINDIR specify path to binaries directory (for systems where libraries must be found in the PATH setting at runtime) -dlopen FILE '-dlpreopen' FILE if it cannot be dlopened at runtime -dlpreopen FILE link in FILE and add its symbols to lt_preloaded_symbols -export-dynamic allow symbols from OUTPUT-FILE to be resolved with dlsym(3) -export-symbols SYMFILE try to export only the symbols listed in SYMFILE -export-symbols-regex REGEX try to export only the symbols matching REGEX -LLIBDIR search LIBDIR for required installed libraries -lNAME OUTPUT-FILE requires the installed library libNAME -module build a library that can dlopened -no-fast-install disable the fast-install mode -no-install link a not-installable executable -no-undefined declare that a library does not refer to external symbols -o OUTPUT-FILE create OUTPUT-FILE from the specified objects -objectlist FILE use a list of object files found in FILE to specify objects -os2dllname NAME force a short DLL name on OS/2 (no effect on other OSes) -precious-files-regex REGEX don't remove output files matching REGEX -release RELEASE specify package release information -rpath LIBDIR the created library will eventually be installed in LIBDIR -R[ ]LIBDIR add LIBDIR to the runtime path of programs and libraries -shared only do dynamic linking of libtool libraries -shrext SUFFIX override the standard shared library file extension -static do not do any dynamic linking of uninstalled libtool libraries -static-libtool-libs do not do any dynamic linking of libtool libraries -version-info CURRENT[:REVISION[:AGE]] specify library version info [each variable defaults to 0] -weak LIBNAME declare that the target provides the LIBNAME interface -Wc,FLAG -Xcompiler FLAG pass linker-specific FLAG directly to the compiler -Wa,FLAG -Xassembler FLAG pass linker-specific FLAG directly to the assembler -Wl,FLAG -Xlinker FLAG pass linker-specific FLAG directly to the linker -XCClinker FLAG pass link-specific FLAG to the compiler driver (CC) All other options (arguments beginning with '-') are ignored. Every other argument is treated as a filename. Files ending in '.la' are treated as uninstalled libtool libraries, other files are standard or library object files. If the OUTPUT-FILE ends in '.la', then a libtool library is created, only library objects ('.lo' files) may be specified, and '-rpath' is required, except when creating a convenience library. If OUTPUT-FILE ends in '.a' or '.lib', then a standard library is created using 'ar' and 'ranlib', or on Windows using 'lib'. If OUTPUT-FILE ends in '.lo' or '.$objext', then a reloadable object file is created, otherwise an executable program is created." ;; uninstall) $ECHO \ "Usage: $progname [OPTION]... --mode=uninstall RM [RM-OPTION]... FILE... Remove libraries from an installation directory. RM is the name of the program to use to delete files associated with each FILE (typically '/bin/rm'). RM-OPTIONS are options (such as '-f') to be passed to RM. If FILE is a libtool library, all the files associated with it are deleted. Otherwise, only FILE itself is deleted using RM." ;; *) func_fatal_help "invalid operation mode '$opt_mode'" ;; esac echo $ECHO "Try '$progname --help' for more information about other modes." } # Now that we've collected a possible --mode arg, show help if necessary if $opt_help; then if test : = "$opt_help"; then func_mode_help else { func_help noexit for opt_mode in compile link execute install finish uninstall clean; do func_mode_help done } | $SED -n '1p; 2,$s/^Usage:/ or: /p' { func_help noexit for opt_mode in compile link execute install finish uninstall clean; do echo func_mode_help done } | $SED '1d /^When reporting/,/^Report/{ H d } $x /information about other modes/d /more detailed .*MODE/d s/^Usage:.*--mode=\([^ ]*\) .*/Description of \1 mode:/' fi exit $? fi # func_mode_execute arg... func_mode_execute () { $debug_cmd # The first argument is the command name. cmd=$nonopt test -z "$cmd" && \ func_fatal_help "you must specify a COMMAND" # Handle -dlopen flags immediately. for file in $opt_dlopen; do test -f "$file" \ || func_fatal_help "'$file' is not a file" dir= case $file in *.la) func_resolve_sysroot "$file" file=$func_resolve_sysroot_result # Check to see that this really is a libtool archive. func_lalib_unsafe_p "$file" \ || func_fatal_help "'$lib' is not a valid libtool archive" # Read the libtool library. dlname= library_names= func_source "$file" # Skip this library if it cannot be dlopened. if test -z "$dlname"; then # Warn if it was a shared library. test -n "$library_names" && \ func_warning "'$file' was not linked with '-export-dynamic'" continue fi func_dirname "$file" "" "." dir=$func_dirname_result if test -f "$dir/$objdir/$dlname"; then func_append dir "/$objdir" else if test ! -f "$dir/$dlname"; then func_fatal_error "cannot find '$dlname' in '$dir' or '$dir/$objdir'" fi fi ;; *.lo) # Just add the directory containing the .lo file. func_dirname "$file" "" "." dir=$func_dirname_result ;; *) func_warning "'-dlopen' is ignored for non-libtool libraries and objects" continue ;; esac # Get the absolute pathname. absdir=`cd "$dir" && pwd` test -n "$absdir" && dir=$absdir # Now add the directory to shlibpath_var. if eval "test -z \"\$$shlibpath_var\""; then eval "$shlibpath_var=\"\$dir\"" else eval "$shlibpath_var=\"\$dir:\$$shlibpath_var\"" fi done # This variable tells wrapper scripts just to set shlibpath_var # rather than running their programs. libtool_execute_magic=$magic # Check if any of the arguments is a wrapper script. args= for file do case $file in -* | *.la | *.lo ) ;; *) # Do a test to see if this is really a libtool program. if func_ltwrapper_script_p "$file"; then func_source "$file" # Transform arg to wrapped name. file=$progdir/$program elif func_ltwrapper_executable_p "$file"; then func_ltwrapper_scriptname "$file" func_source "$func_ltwrapper_scriptname_result" # Transform arg to wrapped name. file=$progdir/$program fi ;; esac # Quote arguments (to preserve shell metacharacters). func_append_quoted args "$file" done if $opt_dry_run; then # Display what would be done. if test -n "$shlibpath_var"; then eval "\$ECHO \"\$shlibpath_var=\$$shlibpath_var\"" echo "export $shlibpath_var" fi $ECHO "$cmd$args" exit $EXIT_SUCCESS else if test -n "$shlibpath_var"; then # Export the shlibpath_var. eval "export $shlibpath_var" fi # Restore saved environment variables for lt_var in LANG LANGUAGE LC_ALL LC_CTYPE LC_COLLATE LC_MESSAGES do eval "if test \"\${save_$lt_var+set}\" = set; then $lt_var=\$save_$lt_var; export $lt_var else $lt_unset $lt_var fi" done # Now prepare to actually exec the command. exec_cmd=\$cmd$args fi } test execute = "$opt_mode" && func_mode_execute ${1+"$@"} # func_mode_finish arg... func_mode_finish () { $debug_cmd libs= libdirs= admincmds= for opt in "$nonopt" ${1+"$@"} do if test -d "$opt"; then func_append libdirs " $opt" elif test -f "$opt"; then if func_lalib_unsafe_p "$opt"; then func_append libs " $opt" else func_warning "'$opt' is not a valid libtool archive" fi else func_fatal_error "invalid argument '$opt'" fi done if test -n "$libs"; then if test -n "$lt_sysroot"; then sysroot_regex=`$ECHO "$lt_sysroot" | $SED "$sed_make_literal_regex"` sysroot_cmd="s/\([ ']\)$sysroot_regex/\1/g;" else sysroot_cmd= fi # Remove sysroot references if $opt_dry_run; then for lib in $libs; do echo "removing references to $lt_sysroot and '=' prefixes from $lib" done else tmpdir=`func_mktempdir` for lib in $libs; do $SED -e "$sysroot_cmd s/\([ ']-[LR]\)=/\1/g; s/\([ ']\)=/\1/g" $lib \ > $tmpdir/tmp-la mv -f $tmpdir/tmp-la $lib done ${RM}r "$tmpdir" fi fi if test -n "$finish_cmds$finish_eval" && test -n "$libdirs"; then for libdir in $libdirs; do if test -n "$finish_cmds"; then # Do each command in the finish commands. func_execute_cmds "$finish_cmds" 'admincmds="$admincmds '"$cmd"'"' fi if test -n "$finish_eval"; then # Do the single finish_eval. eval cmds=\"$finish_eval\" $opt_dry_run || eval "$cmds" || func_append admincmds " $cmds" fi done fi # Exit here if they wanted silent mode. $opt_quiet && exit $EXIT_SUCCESS if test -n "$finish_cmds$finish_eval" && test -n "$libdirs"; then echo "----------------------------------------------------------------------" echo "Libraries have been installed in:" for libdir in $libdirs; do $ECHO " $libdir" done echo echo "If you ever happen to want to link against installed libraries" echo "in a given directory, LIBDIR, you must either use libtool, and" echo "specify the full pathname of the library, or use the '-LLIBDIR'" echo "flag during linking and do at least one of the following:" if test -n "$shlibpath_var"; then echo " - add LIBDIR to the '$shlibpath_var' environment variable" echo " during execution" fi if test -n "$runpath_var"; then echo " - add LIBDIR to the '$runpath_var' environment variable" echo " during linking" fi if test -n "$hardcode_libdir_flag_spec"; then libdir=LIBDIR eval flag=\"$hardcode_libdir_flag_spec\" $ECHO " - use the '$flag' linker flag" fi if test -n "$admincmds"; then $ECHO " - have your system administrator run these commands:$admincmds" fi if test -f /etc/ld.so.conf; then echo " - have your system administrator add LIBDIR to '/etc/ld.so.conf'" fi echo echo "See any operating system documentation about shared libraries for" case $host in solaris2.[6789]|solaris2.1[0-9]) echo "more information, such as the ld(1), crle(1) and ld.so(8) manual" echo "pages." ;; *) echo "more information, such as the ld(1) and ld.so(8) manual pages." ;; esac echo "----------------------------------------------------------------------" fi exit $EXIT_SUCCESS } test finish = "$opt_mode" && func_mode_finish ${1+"$@"} # func_mode_install arg... func_mode_install () { $debug_cmd # There may be an optional sh(1) argument at the beginning of # install_prog (especially on Windows NT). if test "$SHELL" = "$nonopt" || test /bin/sh = "$nonopt" || # Allow the use of GNU shtool's install command. case $nonopt in *shtool*) :;; *) false;; esac then # Aesthetically quote it. func_quote_arg pretty "$nonopt" install_prog="$func_quote_arg_result " arg=$1 shift else install_prog= arg=$nonopt fi # The real first argument should be the name of the installation program. # Aesthetically quote it. func_quote_arg pretty "$arg" func_append install_prog "$func_quote_arg_result" install_shared_prog=$install_prog case " $install_prog " in *[\\\ /]cp\ *) install_cp=: ;; *) install_cp=false ;; esac # We need to accept at least all the BSD install flags. dest= files= opts= prev= install_type= isdir=false stripme= no_mode=: for arg do arg2= if test -n "$dest"; then func_append files " $dest" dest=$arg continue fi case $arg in -d) isdir=: ;; -f) if $install_cp; then :; else prev=$arg fi ;; -g | -m | -o) prev=$arg ;; -s) stripme=" -s" continue ;; -*) ;; *) # If the previous option needed an argument, then skip it. if test -n "$prev"; then if test X-m = "X$prev" && test -n "$install_override_mode"; then arg2=$install_override_mode no_mode=false fi prev= else dest=$arg continue fi ;; esac # Aesthetically quote the argument. func_quote_arg pretty "$arg" func_append install_prog " $func_quote_arg_result" if test -n "$arg2"; then func_quote_arg pretty "$arg2" fi func_append install_shared_prog " $func_quote_arg_result" done test -z "$install_prog" && \ func_fatal_help "you must specify an install program" test -n "$prev" && \ func_fatal_help "the '$prev' option requires an argument" if test -n "$install_override_mode" && $no_mode; then if $install_cp; then :; else func_quote_arg pretty "$install_override_mode" func_append install_shared_prog " -m $func_quote_arg_result" fi fi if test -z "$files"; then if test -z "$dest"; then func_fatal_help "no file or destination specified" else func_fatal_help "you must specify a destination" fi fi # Strip any trailing slash from the destination. func_stripname '' '/' "$dest" dest=$func_stripname_result # Check to see that the destination is a directory. test -d "$dest" && isdir=: if $isdir; then destdir=$dest destname= else func_dirname_and_basename "$dest" "" "." destdir=$func_dirname_result destname=$func_basename_result # Not a directory, so check to see that there is only one file specified. set dummy $files; shift test "$#" -gt 1 && \ func_fatal_help "'$dest' is not a directory" fi case $destdir in [\\/]* | [A-Za-z]:[\\/]*) ;; *) for file in $files; do case $file in *.lo) ;; *) func_fatal_help "'$destdir' must be an absolute directory name" ;; esac done ;; esac # This variable tells wrapper scripts just to set variables rather # than running their programs. libtool_install_magic=$magic staticlibs= future_libdirs= current_libdirs= for file in $files; do # Do each installation. case $file in *.$libext) # Do the static libraries later. func_append staticlibs " $file" ;; *.la) func_resolve_sysroot "$file" file=$func_resolve_sysroot_result # Check to see that this really is a libtool archive. func_lalib_unsafe_p "$file" \ || func_fatal_help "'$file' is not a valid libtool archive" library_names= old_library= relink_command= func_source "$file" # Add the libdir to current_libdirs if it is the destination. if test "X$destdir" = "X$libdir"; then case "$current_libdirs " in *" $libdir "*) ;; *) func_append current_libdirs " $libdir" ;; esac else # Note the libdir as a future libdir. case "$future_libdirs " in *" $libdir "*) ;; *) func_append future_libdirs " $libdir" ;; esac fi func_dirname "$file" "/" "" dir=$func_dirname_result func_append dir "$objdir" if test -n "$relink_command"; then # Determine the prefix the user has applied to our future dir. inst_prefix_dir=`$ECHO "$destdir" | $SED -e "s%$libdir\$%%"` # Don't allow the user to place us outside of our expected # location b/c this prevents finding dependent libraries that # are installed to the same prefix. # At present, this check doesn't affect windows .dll's that # are installed into $libdir/../bin (currently, that works fine) # but it's something to keep an eye on. test "$inst_prefix_dir" = "$destdir" && \ func_fatal_error "error: cannot install '$file' to a directory not ending in $libdir" if test -n "$inst_prefix_dir"; then # Stick the inst_prefix_dir data into the link command. relink_command=`$ECHO "$relink_command" | $SED "s%@inst_prefix_dir@%-inst-prefix-dir $inst_prefix_dir%"` else relink_command=`$ECHO "$relink_command" | $SED "s%@inst_prefix_dir@%%"` fi func_warning "relinking '$file'" func_show_eval "$relink_command" \ 'func_fatal_error "error: relink '\''$file'\'' with the above command before installing it"' fi # See the names of the shared library. set dummy $library_names; shift if test -n "$1"; then realname=$1 shift srcname=$realname test -n "$relink_command" && srcname=${realname}T # Install the shared library and build the symlinks. func_show_eval "$install_shared_prog $dir/$srcname $destdir/$realname" \ 'exit $?' tstripme=$stripme case $host_os in cygwin* | mingw* | pw32* | cegcc*) case $realname in *.dll.a) tstripme= ;; esac ;; os2*) case $realname in *_dll.a) tstripme= ;; esac ;; esac if test -n "$tstripme" && test -n "$striplib"; then func_show_eval "$striplib $destdir/$realname" 'exit $?' fi if test "$#" -gt 0; then # Delete the old symlinks, and create new ones. # Try 'ln -sf' first, because the 'ln' binary might depend on # the symlink we replace! Solaris /bin/ln does not understand -f, # so we also need to try rm && ln -s. for linkname do test "$linkname" != "$realname" \ && func_show_eval "(cd $destdir && { $LN_S -f $realname $linkname || { $RM $linkname && $LN_S $realname $linkname; }; })" done fi # Do each command in the postinstall commands. lib=$destdir/$realname func_execute_cmds "$postinstall_cmds" 'exit $?' fi # Install the pseudo-library for information purposes. func_basename "$file" name=$func_basename_result instname=$dir/${name}i func_show_eval "$install_prog $instname $destdir/$name" 'exit $?' # Maybe install the static library, too. test -n "$old_library" && func_append staticlibs " $dir/$old_library" ;; *.lo) # Install (i.e. copy) a libtool object. # Figure out destination file name, if it wasn't already specified. if test -n "$destname"; then destfile=$destdir/$destname else func_basename "$file" destfile=$func_basename_result destfile=$destdir/$destfile fi # Deduce the name of the destination old-style object file. case $destfile in *.lo) func_lo2o "$destfile" staticdest=$func_lo2o_result ;; *.$objext) staticdest=$destfile destfile= ;; *) func_fatal_help "cannot copy a libtool object to '$destfile'" ;; esac # Install the libtool object if requested. test -n "$destfile" && \ func_show_eval "$install_prog $file $destfile" 'exit $?' # Install the old object if enabled. if test yes = "$build_old_libs"; then # Deduce the name of the old-style object file. func_lo2o "$file" staticobj=$func_lo2o_result func_show_eval "$install_prog \$staticobj \$staticdest" 'exit $?' fi exit $EXIT_SUCCESS ;; *) # Figure out destination file name, if it wasn't already specified. if test -n "$destname"; then destfile=$destdir/$destname else func_basename "$file" destfile=$func_basename_result destfile=$destdir/$destfile fi # If the file is missing, and there is a .exe on the end, strip it # because it is most likely a libtool script we actually want to # install stripped_ext= case $file in *.exe) if test ! -f "$file"; then func_stripname '' '.exe' "$file" file=$func_stripname_result stripped_ext=.exe fi ;; esac # Do a test to see if this is really a libtool program. case $host in *cygwin* | *mingw*) if func_ltwrapper_executable_p "$file"; then func_ltwrapper_scriptname "$file" wrapper=$func_ltwrapper_scriptname_result else func_stripname '' '.exe' "$file" wrapper=$func_stripname_result fi ;; *) wrapper=$file ;; esac if func_ltwrapper_script_p "$wrapper"; then notinst_deplibs= relink_command= func_source "$wrapper" # Check the variables that should have been set. test -z "$generated_by_libtool_version" && \ func_fatal_error "invalid libtool wrapper script '$wrapper'" finalize=: for lib in $notinst_deplibs; do # Check to see that each library is installed. libdir= if test -f "$lib"; then func_source "$lib" fi libfile=$libdir/`$ECHO "$lib" | $SED 's%^.*/%%g'` if test -n "$libdir" && test ! -f "$libfile"; then func_warning "'$lib' has not been installed in '$libdir'" finalize=false fi done relink_command= func_source "$wrapper" outputname= if test no = "$fast_install" && test -n "$relink_command"; then $opt_dry_run || { if $finalize; then tmpdir=`func_mktempdir` func_basename "$file$stripped_ext" file=$func_basename_result outputname=$tmpdir/$file # Replace the output file specification. relink_command=`$ECHO "$relink_command" | $SED 's%@OUTPUT@%'"$outputname"'%g'` $opt_quiet || { func_quote_arg expand,pretty "$relink_command" eval "func_echo $func_quote_arg_result" } if eval "$relink_command"; then : else func_error "error: relink '$file' with the above command before installing it" $opt_dry_run || ${RM}r "$tmpdir" continue fi file=$outputname else func_warning "cannot relink '$file'" fi } else # Install the binary that we compiled earlier. file=`$ECHO "$file$stripped_ext" | $SED "s%\([^/]*\)$%$objdir/\1%"` fi fi # remove .exe since cygwin /usr/bin/install will append another # one anyway case $install_prog,$host in */usr/bin/install*,*cygwin*) case $file:$destfile in *.exe:*.exe) # this is ok ;; *.exe:*) destfile=$destfile.exe ;; *:*.exe) func_stripname '' '.exe' "$destfile" destfile=$func_stripname_result ;; esac ;; esac func_show_eval "$install_prog\$stripme \$file \$destfile" 'exit $?' $opt_dry_run || if test -n "$outputname"; then ${RM}r "$tmpdir" fi ;; esac done for file in $staticlibs; do func_basename "$file" name=$func_basename_result # Set up the ranlib parameters. oldlib=$destdir/$name func_to_tool_file "$oldlib" func_convert_file_msys_to_w32 tool_oldlib=$func_to_tool_file_result func_show_eval "$install_prog \$file \$oldlib" 'exit $?' if test -n "$stripme" && test -n "$old_striplib"; then func_show_eval "$old_striplib $tool_oldlib" 'exit $?' fi # Do each command in the postinstall commands. func_execute_cmds "$old_postinstall_cmds" 'exit $?' done test -n "$future_libdirs" && \ func_warning "remember to run '$progname --finish$future_libdirs'" if test -n "$current_libdirs"; then # Maybe just do a dry run. $opt_dry_run && current_libdirs=" -n$current_libdirs" exec_cmd='$SHELL "$progpath" $preserve_args --finish$current_libdirs' else exit $EXIT_SUCCESS fi } test install = "$opt_mode" && func_mode_install ${1+"$@"} # func_generate_dlsyms outputname originator pic_p # Extract symbols from dlprefiles and create ${outputname}S.o with # a dlpreopen symbol table. func_generate_dlsyms () { $debug_cmd my_outputname=$1 my_originator=$2 my_pic_p=${3-false} my_prefix=`$ECHO "$my_originator" | $SED 's%[^a-zA-Z0-9]%_%g'` my_dlsyms= if test -n "$dlfiles$dlprefiles" || test no != "$dlself"; then if test -n "$NM" && test -n "$global_symbol_pipe"; then my_dlsyms=${my_outputname}S.c else func_error "not configured to extract global symbols from dlpreopened files" fi fi if test -n "$my_dlsyms"; then case $my_dlsyms in "") ;; *.c) # Discover the nlist of each of the dlfiles. nlist=$output_objdir/$my_outputname.nm func_show_eval "$RM $nlist ${nlist}S ${nlist}T" # Parse the name list into a source file. func_verbose "creating $output_objdir/$my_dlsyms" $opt_dry_run || $ECHO > "$output_objdir/$my_dlsyms" "\ /* $my_dlsyms - symbol resolution table for '$my_outputname' dlsym emulation. */ /* Generated by $PROGRAM (GNU $PACKAGE) $VERSION */ #ifdef __cplusplus extern \"C\" { #endif #if defined __GNUC__ && (((__GNUC__ == 4) && (__GNUC_MINOR__ >= 4)) || (__GNUC__ > 4)) #pragma GCC diagnostic ignored \"-Wstrict-prototypes\" #endif /* Keep this code in sync between libtool.m4, ltmain, lt_system.h, and tests. */ #if defined _WIN32 || defined __CYGWIN__ || defined _WIN32_WCE /* DATA imports from DLLs on WIN32 can't be const, because runtime relocations are performed -- see ld's documentation on pseudo-relocs. */ # define LT_DLSYM_CONST #elif defined __osf__ /* This system does not cope well with relocations in const data. */ # define LT_DLSYM_CONST #else # define LT_DLSYM_CONST const #endif #define STREQ(s1, s2) (strcmp ((s1), (s2)) == 0) /* External symbol declarations for the compiler. */\ " if test yes = "$dlself"; then func_verbose "generating symbol list for '$output'" $opt_dry_run || echo ': @PROGRAM@ ' > "$nlist" # Add our own program objects to the symbol list. progfiles=`$ECHO "$objs$old_deplibs" | $SP2NL | $SED "$lo2o" | $NL2SP` for progfile in $progfiles; do func_to_tool_file "$progfile" func_convert_file_msys_to_w32 func_verbose "extracting global C symbols from '$func_to_tool_file_result'" $opt_dry_run || eval "$NM $func_to_tool_file_result | $global_symbol_pipe >> '$nlist'" done if test -n "$exclude_expsyms"; then $opt_dry_run || { eval '$EGREP -v " ($exclude_expsyms)$" "$nlist" > "$nlist"T' eval '$MV "$nlist"T "$nlist"' } fi if test -n "$export_symbols_regex"; then $opt_dry_run || { eval '$EGREP -e "$export_symbols_regex" "$nlist" > "$nlist"T' eval '$MV "$nlist"T "$nlist"' } fi # Prepare the list of exported symbols if test -z "$export_symbols"; then export_symbols=$output_objdir/$outputname.exp $opt_dry_run || { $RM $export_symbols eval "$SED -n -e '/^: @PROGRAM@ $/d' -e 's/^.* \(.*\)$/\1/p' "'< "$nlist" > "$export_symbols"' case $host in *cygwin* | *mingw* | *cegcc* ) eval "echo EXPORTS "'> "$output_objdir/$outputname.def"' eval 'cat "$export_symbols" >> "$output_objdir/$outputname.def"' ;; esac } else $opt_dry_run || { eval "$SED -e 's/\([].[*^$]\)/\\\\\1/g' -e 's/^/ /' -e 's/$/$/'"' < "$export_symbols" > "$output_objdir/$outputname.exp"' eval '$GREP -f "$output_objdir/$outputname.exp" < "$nlist" > "$nlist"T' eval '$MV "$nlist"T "$nlist"' case $host in *cygwin* | *mingw* | *cegcc* ) eval "echo EXPORTS "'> "$output_objdir/$outputname.def"' eval 'cat "$nlist" >> "$output_objdir/$outputname.def"' ;; esac } fi fi for dlprefile in $dlprefiles; do func_verbose "extracting global C symbols from '$dlprefile'" func_basename "$dlprefile" name=$func_basename_result case $host in *cygwin* | *mingw* | *cegcc* ) # if an import library, we need to obtain dlname if func_win32_import_lib_p "$dlprefile"; then func_tr_sh "$dlprefile" eval "curr_lafile=\$libfile_$func_tr_sh_result" dlprefile_dlbasename= if test -n "$curr_lafile" && func_lalib_p "$curr_lafile"; then # Use subshell, to avoid clobbering current variable values dlprefile_dlname=`source "$curr_lafile" && echo "$dlname"` if test -n "$dlprefile_dlname"; then func_basename "$dlprefile_dlname" dlprefile_dlbasename=$func_basename_result else # no lafile. user explicitly requested -dlpreopen . $sharedlib_from_linklib_cmd "$dlprefile" dlprefile_dlbasename=$sharedlib_from_linklib_result fi fi $opt_dry_run || { if test -n "$dlprefile_dlbasename"; then eval '$ECHO ": $dlprefile_dlbasename" >> "$nlist"' else func_warning "Could not compute DLL name from $name" eval '$ECHO ": $name " >> "$nlist"' fi func_to_tool_file "$dlprefile" func_convert_file_msys_to_w32 eval "$NM \"$func_to_tool_file_result\" 2>/dev/null | $global_symbol_pipe | $SED -e '/I __imp/d' -e 's/I __nm_/D /;s/_nm__//' >> '$nlist'" } else # not an import lib $opt_dry_run || { eval '$ECHO ": $name " >> "$nlist"' func_to_tool_file "$dlprefile" func_convert_file_msys_to_w32 eval "$NM \"$func_to_tool_file_result\" 2>/dev/null | $global_symbol_pipe >> '$nlist'" } fi ;; *) $opt_dry_run || { eval '$ECHO ": $name " >> "$nlist"' func_to_tool_file "$dlprefile" func_convert_file_msys_to_w32 eval "$NM \"$func_to_tool_file_result\" 2>/dev/null | $global_symbol_pipe >> '$nlist'" } ;; esac done $opt_dry_run || { # Make sure we have at least an empty file. test -f "$nlist" || : > "$nlist" if test -n "$exclude_expsyms"; then $EGREP -v " ($exclude_expsyms)$" "$nlist" > "$nlist"T $MV "$nlist"T "$nlist" fi # Try sorting and uniquifying the output. if $GREP -v "^: " < "$nlist" | if sort -k 3 /dev/null 2>&1; then sort -k 3 else sort +2 fi | uniq > "$nlist"S; then : else $GREP -v "^: " < "$nlist" > "$nlist"S fi if test -f "$nlist"S; then eval "$global_symbol_to_cdecl"' < "$nlist"S >> "$output_objdir/$my_dlsyms"' else echo '/* NONE */' >> "$output_objdir/$my_dlsyms" fi func_show_eval '$RM "${nlist}I"' if test -n "$global_symbol_to_import"; then eval "$global_symbol_to_import"' < "$nlist"S > "$nlist"I' fi echo >> "$output_objdir/$my_dlsyms" "\ /* The mapping between symbol names and symbols. */ typedef struct { const char *name; void *address; } lt_dlsymlist; extern LT_DLSYM_CONST lt_dlsymlist lt_${my_prefix}_LTX_preloaded_symbols[];\ " if test -s "$nlist"I; then echo >> "$output_objdir/$my_dlsyms" "\ static void lt_syminit(void) { LT_DLSYM_CONST lt_dlsymlist *symbol = lt_${my_prefix}_LTX_preloaded_symbols; for (; symbol->name; ++symbol) {" $SED 's/.*/ if (STREQ (symbol->name, \"&\")) symbol->address = (void *) \&&;/' < "$nlist"I >> "$output_objdir/$my_dlsyms" echo >> "$output_objdir/$my_dlsyms" "\ } }" fi echo >> "$output_objdir/$my_dlsyms" "\ LT_DLSYM_CONST lt_dlsymlist lt_${my_prefix}_LTX_preloaded_symbols[] = { {\"$my_originator\", (void *) 0}," if test -s "$nlist"I; then echo >> "$output_objdir/$my_dlsyms" "\ {\"@INIT@\", (void *) <_syminit}," fi case $need_lib_prefix in no) eval "$global_symbol_to_c_name_address" < "$nlist" >> "$output_objdir/$my_dlsyms" ;; *) eval "$global_symbol_to_c_name_address_lib_prefix" < "$nlist" >> "$output_objdir/$my_dlsyms" ;; esac echo >> "$output_objdir/$my_dlsyms" "\ {0, (void *) 0} }; /* This works around a problem in FreeBSD linker */ #ifdef FREEBSD_WORKAROUND static const void *lt_preloaded_setup() { return lt_${my_prefix}_LTX_preloaded_symbols; } #endif #ifdef __cplusplus } #endif\ " } # !$opt_dry_run pic_flag_for_symtable= case "$compile_command " in *" -static "*) ;; *) case $host in # compiling the symbol table file with pic_flag works around # a FreeBSD bug that causes programs to crash when -lm is # linked before any other PIC object. But we must not use # pic_flag when linking with -static. The problem exists in # FreeBSD 2.2.6 and is fixed in FreeBSD 3.1. *-*-freebsd2.*|*-*-freebsd3.0*|*-*-freebsdelf3.0*) pic_flag_for_symtable=" $pic_flag -DFREEBSD_WORKAROUND" ;; *-*-hpux*) pic_flag_for_symtable=" $pic_flag" ;; *) $my_pic_p && pic_flag_for_symtable=" $pic_flag" ;; esac ;; esac symtab_cflags= for arg in $LTCFLAGS; do case $arg in -pie | -fpie | -fPIE) ;; *) func_append symtab_cflags " $arg" ;; esac done # Now compile the dynamic symbol file. func_show_eval '(cd $output_objdir && $LTCC$symtab_cflags -c$no_builtin_flag$pic_flag_for_symtable "$my_dlsyms")' 'exit $?' # Clean up the generated files. func_show_eval '$RM "$output_objdir/$my_dlsyms" "$nlist" "${nlist}S" "${nlist}T" "${nlist}I"' # Transform the symbol file into the correct name. symfileobj=$output_objdir/${my_outputname}S.$objext case $host in *cygwin* | *mingw* | *cegcc* ) if test -f "$output_objdir/$my_outputname.def"; then compile_command=`$ECHO "$compile_command" | $SED "s%@SYMFILE@%$output_objdir/$my_outputname.def $symfileobj%"` finalize_command=`$ECHO "$finalize_command" | $SED "s%@SYMFILE@%$output_objdir/$my_outputname.def $symfileobj%"` else compile_command=`$ECHO "$compile_command" | $SED "s%@SYMFILE@%$symfileobj%"` finalize_command=`$ECHO "$finalize_command" | $SED "s%@SYMFILE@%$symfileobj%"` fi ;; *) compile_command=`$ECHO "$compile_command" | $SED "s%@SYMFILE@%$symfileobj%"` finalize_command=`$ECHO "$finalize_command" | $SED "s%@SYMFILE@%$symfileobj%"` ;; esac ;; *) func_fatal_error "unknown suffix for '$my_dlsyms'" ;; esac else # We keep going just in case the user didn't refer to # lt_preloaded_symbols. The linker will fail if global_symbol_pipe # really was required. # Nullify the symbol file. compile_command=`$ECHO "$compile_command" | $SED "s% @SYMFILE@%%"` finalize_command=`$ECHO "$finalize_command" | $SED "s% @SYMFILE@%%"` fi } # func_cygming_gnu_implib_p ARG # This predicate returns with zero status (TRUE) if # ARG is a GNU/binutils-style import library. Returns # with nonzero status (FALSE) otherwise. func_cygming_gnu_implib_p () { $debug_cmd func_to_tool_file "$1" func_convert_file_msys_to_w32 func_cygming_gnu_implib_tmp=`$NM "$func_to_tool_file_result" | eval "$global_symbol_pipe" | $EGREP ' (_head_[A-Za-z0-9_]+_[ad]l*|[A-Za-z0-9_]+_[ad]l*_iname)$'` test -n "$func_cygming_gnu_implib_tmp" } # func_cygming_ms_implib_p ARG # This predicate returns with zero status (TRUE) if # ARG is an MS-style import library. Returns # with nonzero status (FALSE) otherwise. func_cygming_ms_implib_p () { $debug_cmd func_to_tool_file "$1" func_convert_file_msys_to_w32 func_cygming_ms_implib_tmp=`$NM "$func_to_tool_file_result" | eval "$global_symbol_pipe" | $GREP '_NULL_IMPORT_DESCRIPTOR'` test -n "$func_cygming_ms_implib_tmp" } # func_win32_libid arg # return the library type of file 'arg' # # Need a lot of goo to handle *both* DLLs and import libs # Has to be a shell function in order to 'eat' the argument # that is supplied when $file_magic_command is called. # Despite the name, also deal with 64 bit binaries. func_win32_libid () { $debug_cmd win32_libid_type=unknown win32_fileres=`file -L $1 2>/dev/null` case $win32_fileres in *ar\ archive\ import\ library*) # definitely import win32_libid_type="x86 archive import" ;; *ar\ archive*) # could be an import, or static # Keep the egrep pattern in sync with the one in _LT_CHECK_MAGIC_METHOD. if eval $OBJDUMP -f $1 | $SED -e '10q' 2>/dev/null | $EGREP 'file format (pei*-i386(.*architecture: i386)?|pe-arm-wince|pe-x86-64)' >/dev/null; then case $nm_interface in "MS dumpbin") if func_cygming_ms_implib_p "$1" || func_cygming_gnu_implib_p "$1" then win32_nmres=import else win32_nmres= fi ;; *) func_to_tool_file "$1" func_convert_file_msys_to_w32 win32_nmres=`eval $NM -f posix -A \"$func_to_tool_file_result\" | $SED -n -e ' 1,100{ / I /{ s|.*|import| p q } }'` ;; esac case $win32_nmres in import*) win32_libid_type="x86 archive import";; *) win32_libid_type="x86 archive static";; esac fi ;; *DLL*) win32_libid_type="x86 DLL" ;; *executable*) # but shell scripts are "executable" too... case $win32_fileres in *MS\ Windows\ PE\ Intel*) win32_libid_type="x86 DLL" ;; esac ;; esac $ECHO "$win32_libid_type" } # func_cygming_dll_for_implib ARG # # Platform-specific function to extract the # name of the DLL associated with the specified # import library ARG. # Invoked by eval'ing the libtool variable # $sharedlib_from_linklib_cmd # Result is available in the variable # $sharedlib_from_linklib_result func_cygming_dll_for_implib () { $debug_cmd sharedlib_from_linklib_result=`$DLLTOOL --identify-strict --identify "$1"` } # func_cygming_dll_for_implib_fallback_core SECTION_NAME LIBNAMEs # # The is the core of a fallback implementation of a # platform-specific function to extract the name of the # DLL associated with the specified import library LIBNAME. # # SECTION_NAME is either .idata$6 or .idata$7, depending # on the platform and compiler that created the implib. # # Echos the name of the DLL associated with the # specified import library. func_cygming_dll_for_implib_fallback_core () { $debug_cmd match_literal=`$ECHO "$1" | $SED "$sed_make_literal_regex"` $OBJDUMP -s --section "$1" "$2" 2>/dev/null | $SED '/^Contents of section '"$match_literal"':/{ # Place marker at beginning of archive member dllname section s/.*/====MARK====/ p d } # These lines can sometimes be longer than 43 characters, but # are always uninteresting /:[ ]*file format pe[i]\{,1\}-/d /^In archive [^:]*:/d # Ensure marker is printed /^====MARK====/p # Remove all lines with less than 43 characters /^.\{43\}/!d # From remaining lines, remove first 43 characters s/^.\{43\}//' | $SED -n ' # Join marker and all lines until next marker into a single line /^====MARK====/ b para H $ b para b :para x s/\n//g # Remove the marker s/^====MARK====// # Remove trailing dots and whitespace s/[\. \t]*$// # Print /./p' | # we now have a list, one entry per line, of the stringified # contents of the appropriate section of all members of the # archive that possess that section. Heuristic: eliminate # all those that have a first or second character that is # a '.' (that is, objdump's representation of an unprintable # character.) This should work for all archives with less than # 0x302f exports -- but will fail for DLLs whose name actually # begins with a literal '.' or a single character followed by # a '.'. # # Of those that remain, print the first one. $SED -e '/^\./d;/^.\./d;q' } # func_cygming_dll_for_implib_fallback ARG # Platform-specific function to extract the # name of the DLL associated with the specified # import library ARG. # # This fallback implementation is for use when $DLLTOOL # does not support the --identify-strict option. # Invoked by eval'ing the libtool variable # $sharedlib_from_linklib_cmd # Result is available in the variable # $sharedlib_from_linklib_result func_cygming_dll_for_implib_fallback () { $debug_cmd if func_cygming_gnu_implib_p "$1"; then # binutils import library sharedlib_from_linklib_result=`func_cygming_dll_for_implib_fallback_core '.idata$7' "$1"` elif func_cygming_ms_implib_p "$1"; then # ms-generated import library sharedlib_from_linklib_result=`func_cygming_dll_for_implib_fallback_core '.idata$6' "$1"` else # unknown sharedlib_from_linklib_result= fi } # func_extract_an_archive dir oldlib func_extract_an_archive () { $debug_cmd f_ex_an_ar_dir=$1; shift f_ex_an_ar_oldlib=$1 if test yes = "$lock_old_archive_extraction"; then lockfile=$f_ex_an_ar_oldlib.lock until $opt_dry_run || ln "$progpath" "$lockfile" 2>/dev/null; do func_echo "Waiting for $lockfile to be removed" sleep 2 done fi func_show_eval "(cd \$f_ex_an_ar_dir && $AR x \"\$f_ex_an_ar_oldlib\")" \ 'stat=$?; rm -f "$lockfile"; exit $stat' if test yes = "$lock_old_archive_extraction"; then $opt_dry_run || rm -f "$lockfile" fi if ($AR t "$f_ex_an_ar_oldlib" | sort | sort -uc >/dev/null 2>&1); then : else func_fatal_error "object name conflicts in archive: $f_ex_an_ar_dir/$f_ex_an_ar_oldlib" fi } # func_extract_archives gentop oldlib ... func_extract_archives () { $debug_cmd my_gentop=$1; shift my_oldlibs=${1+"$@"} my_oldobjs= my_xlib= my_xabs= my_xdir= for my_xlib in $my_oldlibs; do # Extract the objects. case $my_xlib in [\\/]* | [A-Za-z]:[\\/]*) my_xabs=$my_xlib ;; *) my_xabs=`pwd`"/$my_xlib" ;; esac func_basename "$my_xlib" my_xlib=$func_basename_result my_xlib_u=$my_xlib while :; do case " $extracted_archives " in *" $my_xlib_u "*) func_arith $extracted_serial + 1 extracted_serial=$func_arith_result my_xlib_u=lt$extracted_serial-$my_xlib ;; *) break ;; esac done extracted_archives="$extracted_archives $my_xlib_u" my_xdir=$my_gentop/$my_xlib_u func_mkdir_p "$my_xdir" case $host in *-darwin*) func_verbose "Extracting $my_xabs" # Do not bother doing anything if just a dry run $opt_dry_run || { darwin_orig_dir=`pwd` cd $my_xdir || exit $? darwin_archive=$my_xabs darwin_curdir=`pwd` func_basename "$darwin_archive" darwin_base_archive=$func_basename_result darwin_arches=`$LIPO -info "$darwin_archive" 2>/dev/null | $GREP Architectures 2>/dev/null || true` if test -n "$darwin_arches"; then darwin_arches=`$ECHO "$darwin_arches" | $SED -e 's/.*are://'` darwin_arch= func_verbose "$darwin_base_archive has multiple architectures $darwin_arches" for darwin_arch in $darwin_arches; do func_mkdir_p "unfat-$$/$darwin_base_archive-$darwin_arch" $LIPO -thin $darwin_arch -output "unfat-$$/$darwin_base_archive-$darwin_arch/$darwin_base_archive" "$darwin_archive" cd "unfat-$$/$darwin_base_archive-$darwin_arch" func_extract_an_archive "`pwd`" "$darwin_base_archive" cd "$darwin_curdir" $RM "unfat-$$/$darwin_base_archive-$darwin_arch/$darwin_base_archive" done # $darwin_arches ## Okay now we've a bunch of thin objects, gotta fatten them up :) darwin_filelist=`find unfat-$$ -type f -name \*.o -print -o -name \*.lo -print | $SED -e "$sed_basename" | sort -u` darwin_file= darwin_files= for darwin_file in $darwin_filelist; do darwin_files=`find unfat-$$ -name $darwin_file -print | sort | $NL2SP` $LIPO -create -output "$darwin_file" $darwin_files done # $darwin_filelist $RM -rf unfat-$$ cd "$darwin_orig_dir" else cd $darwin_orig_dir func_extract_an_archive "$my_xdir" "$my_xabs" fi # $darwin_arches } # !$opt_dry_run ;; *) func_extract_an_archive "$my_xdir" "$my_xabs" ;; esac my_oldobjs="$my_oldobjs "`find $my_xdir -name \*.$objext -print -o -name \*.lo -print | sort | $NL2SP` done func_extract_archives_result=$my_oldobjs } # func_emit_wrapper [arg=no] # # Emit a libtool wrapper script on stdout. # Don't directly open a file because we may want to # incorporate the script contents within a cygwin/mingw # wrapper executable. Must ONLY be called from within # func_mode_link because it depends on a number of variables # set therein. # # ARG is the value that the WRAPPER_SCRIPT_BELONGS_IN_OBJDIR # variable will take. If 'yes', then the emitted script # will assume that the directory where it is stored is # the $objdir directory. This is a cygwin/mingw-specific # behavior. func_emit_wrapper () { func_emit_wrapper_arg1=${1-no} $ECHO "\ #! $SHELL # $output - temporary wrapper script for $objdir/$outputname # Generated by $PROGRAM (GNU $PACKAGE) $VERSION # # The $output program cannot be directly executed until all the libtool # libraries that it depends on are installed. # # This wrapper script should never be moved out of the build directory. # If it is, it will not operate correctly. # Sed substitution that helps us do robust quoting. It backslashifies # metacharacters that are still active within double-quoted strings. sed_quote_subst='$sed_quote_subst' # Be Bourne compatible if test -n \"\${ZSH_VERSION+set}\" && (emulate sh) >/dev/null 2>&1; then emulate sh NULLCMD=: # Zsh 3.x and 4.x performs word splitting on \${1+\"\$@\"}, which # is contrary to our usage. Disable this feature. alias -g '\${1+\"\$@\"}'='\"\$@\"' setopt NO_GLOB_SUBST else case \`(set -o) 2>/dev/null\` in *posix*) set -o posix;; esac fi BIN_SH=xpg4; export BIN_SH # for Tru64 DUALCASE=1; export DUALCASE # for MKS sh # The HP-UX ksh and POSIX shell print the target directory to stdout # if CDPATH is set. (unset CDPATH) >/dev/null 2>&1 && unset CDPATH relink_command=\"$relink_command\" # This environment variable determines our operation mode. if test \"\$libtool_install_magic\" = \"$magic\"; then # install mode needs the following variables: generated_by_libtool_version='$macro_version' notinst_deplibs='$notinst_deplibs' else # When we are sourced in execute mode, \$file and \$ECHO are already set. if test \"\$libtool_execute_magic\" != \"$magic\"; then file=\"\$0\"" func_quote_arg pretty "$ECHO" qECHO=$func_quote_arg_result $ECHO "\ # A function that is used when there is no print builtin or printf. func_fallback_echo () { eval 'cat <<_LTECHO_EOF \$1 _LTECHO_EOF' } ECHO=$qECHO fi # Very basic option parsing. These options are (a) specific to # the libtool wrapper, (b) are identical between the wrapper # /script/ and the wrapper /executable/ that is used only on # windows platforms, and (c) all begin with the string "--lt-" # (application programs are unlikely to have options that match # this pattern). # # There are only two supported options: --lt-debug and # --lt-dump-script. There is, deliberately, no --lt-help. # # The first argument to this parsing function should be the # script's $0 value, followed by "$@". lt_option_debug= func_parse_lt_options () { lt_script_arg0=\$0 shift for lt_opt do case \"\$lt_opt\" in --lt-debug) lt_option_debug=1 ;; --lt-dump-script) lt_dump_D=\`\$ECHO \"X\$lt_script_arg0\" | $SED -e 's/^X//' -e 's%/[^/]*$%%'\` test \"X\$lt_dump_D\" = \"X\$lt_script_arg0\" && lt_dump_D=. lt_dump_F=\`\$ECHO \"X\$lt_script_arg0\" | $SED -e 's/^X//' -e 's%^.*/%%'\` cat \"\$lt_dump_D/\$lt_dump_F\" exit 0 ;; --lt-*) \$ECHO \"Unrecognized --lt- option: '\$lt_opt'\" 1>&2 exit 1 ;; esac done # Print the debug banner immediately: if test -n \"\$lt_option_debug\"; then echo \"$outputname:$output:\$LINENO: libtool wrapper (GNU $PACKAGE) $VERSION\" 1>&2 fi } # Used when --lt-debug. Prints its arguments to stdout # (redirection is the responsibility of the caller) func_lt_dump_args () { lt_dump_args_N=1; for lt_arg do \$ECHO \"$outputname:$output:\$LINENO: newargv[\$lt_dump_args_N]: \$lt_arg\" lt_dump_args_N=\`expr \$lt_dump_args_N + 1\` done } # Core function for launching the target application func_exec_program_core () { " case $host in # Backslashes separate directories on plain windows *-*-mingw | *-*-os2* | *-cegcc*) $ECHO "\ if test -n \"\$lt_option_debug\"; then \$ECHO \"$outputname:$output:\$LINENO: newargv[0]: \$progdir\\\\\$program\" 1>&2 func_lt_dump_args \${1+\"\$@\"} 1>&2 fi exec \"\$progdir\\\\\$program\" \${1+\"\$@\"} " ;; *) $ECHO "\ if test -n \"\$lt_option_debug\"; then \$ECHO \"$outputname:$output:\$LINENO: newargv[0]: \$progdir/\$program\" 1>&2 func_lt_dump_args \${1+\"\$@\"} 1>&2 fi exec \"\$progdir/\$program\" \${1+\"\$@\"} " ;; esac $ECHO "\ \$ECHO \"\$0: cannot exec \$program \$*\" 1>&2 exit 1 } # A function to encapsulate launching the target application # Strips options in the --lt-* namespace from \$@ and # launches target application with the remaining arguments. func_exec_program () { case \" \$* \" in *\\ --lt-*) for lt_wr_arg do case \$lt_wr_arg in --lt-*) ;; *) set x \"\$@\" \"\$lt_wr_arg\"; shift;; esac shift done ;; esac func_exec_program_core \${1+\"\$@\"} } # Parse options func_parse_lt_options \"\$0\" \${1+\"\$@\"} # Find the directory that this script lives in. thisdir=\`\$ECHO \"\$file\" | $SED 's%/[^/]*$%%'\` test \"x\$thisdir\" = \"x\$file\" && thisdir=. # Follow symbolic links until we get to the real thisdir. file=\`ls -ld \"\$file\" | $SED -n 's/.*-> //p'\` while test -n \"\$file\"; do destdir=\`\$ECHO \"\$file\" | $SED 's%/[^/]*\$%%'\` # If there was a directory component, then change thisdir. if test \"x\$destdir\" != \"x\$file\"; then case \"\$destdir\" in [\\\\/]* | [A-Za-z]:[\\\\/]*) thisdir=\"\$destdir\" ;; *) thisdir=\"\$thisdir/\$destdir\" ;; esac fi file=\`\$ECHO \"\$file\" | $SED 's%^.*/%%'\` file=\`ls -ld \"\$thisdir/\$file\" | $SED -n 's/.*-> //p'\` done # Usually 'no', except on cygwin/mingw when embedded into # the cwrapper. WRAPPER_SCRIPT_BELONGS_IN_OBJDIR=$func_emit_wrapper_arg1 if test \"\$WRAPPER_SCRIPT_BELONGS_IN_OBJDIR\" = \"yes\"; then # special case for '.' if test \"\$thisdir\" = \".\"; then thisdir=\`pwd\` fi # remove .libs from thisdir case \"\$thisdir\" in *[\\\\/]$objdir ) thisdir=\`\$ECHO \"\$thisdir\" | $SED 's%[\\\\/][^\\\\/]*$%%'\` ;; $objdir ) thisdir=. ;; esac fi # Try to get the absolute directory name. absdir=\`cd \"\$thisdir\" && pwd\` test -n \"\$absdir\" && thisdir=\"\$absdir\" " if test yes = "$fast_install"; then $ECHO "\ program=lt-'$outputname'$exeext progdir=\"\$thisdir/$objdir\" if test ! -f \"\$progdir/\$program\" || { file=\`ls -1dt \"\$progdir/\$program\" \"\$progdir/../\$program\" 2>/dev/null | $SED 1q\`; \\ test \"X\$file\" != \"X\$progdir/\$program\"; }; then file=\"\$\$-\$program\" if test ! -d \"\$progdir\"; then $MKDIR \"\$progdir\" else $RM \"\$progdir/\$file\" fi" $ECHO "\ # relink executable if necessary if test -n \"\$relink_command\"; then if relink_command_output=\`eval \$relink_command 2>&1\`; then : else \$ECHO \"\$relink_command_output\" >&2 $RM \"\$progdir/\$file\" exit 1 fi fi $MV \"\$progdir/\$file\" \"\$progdir/\$program\" 2>/dev/null || { $RM \"\$progdir/\$program\"; $MV \"\$progdir/\$file\" \"\$progdir/\$program\"; } $RM \"\$progdir/\$file\" fi" else $ECHO "\ program='$outputname' progdir=\"\$thisdir/$objdir\" " fi $ECHO "\ if test -f \"\$progdir/\$program\"; then" # fixup the dll searchpath if we need to. # # Fix the DLL searchpath if we need to. Do this before prepending # to shlibpath, because on Windows, both are PATH and uninstalled # libraries must come first. if test -n "$dllsearchpath"; then $ECHO "\ # Add the dll search path components to the executable PATH PATH=$dllsearchpath:\$PATH " fi # Export our shlibpath_var if we have one. if test yes = "$shlibpath_overrides_runpath" && test -n "$shlibpath_var" && test -n "$temp_rpath"; then $ECHO "\ # Add our own library path to $shlibpath_var $shlibpath_var=\"$temp_rpath\$$shlibpath_var\" # Some systems cannot cope with colon-terminated $shlibpath_var # The second colon is a workaround for a bug in BeOS R4 sed $shlibpath_var=\`\$ECHO \"\$$shlibpath_var\" | $SED 's/::*\$//'\` export $shlibpath_var " fi $ECHO "\ if test \"\$libtool_execute_magic\" != \"$magic\"; then # Run the actual program with our arguments. func_exec_program \${1+\"\$@\"} fi else # The program doesn't exist. \$ECHO \"\$0: error: '\$progdir/\$program' does not exist\" 1>&2 \$ECHO \"This script is just a wrapper for \$program.\" 1>&2 \$ECHO \"See the $PACKAGE documentation for more information.\" 1>&2 exit 1 fi fi\ " } # func_emit_cwrapperexe_src # emit the source code for a wrapper executable on stdout # Must ONLY be called from within func_mode_link because # it depends on a number of variable set therein. func_emit_cwrapperexe_src () { cat < #include #ifdef _MSC_VER # include # include # include #else # include # include # ifdef __CYGWIN__ # include # endif #endif #include #include #include #include #include #include #include #include #define STREQ(s1, s2) (strcmp ((s1), (s2)) == 0) /* declarations of non-ANSI functions */ #if defined __MINGW32__ # ifdef __STRICT_ANSI__ int _putenv (const char *); # endif #elif defined __CYGWIN__ # ifdef __STRICT_ANSI__ char *realpath (const char *, char *); int putenv (char *); int setenv (const char *, const char *, int); # endif /* #elif defined other_platform || defined ... */ #endif /* portability defines, excluding path handling macros */ #if defined _MSC_VER # define setmode _setmode # define stat _stat # define chmod _chmod # define getcwd _getcwd # define putenv _putenv # define S_IXUSR _S_IEXEC #elif defined __MINGW32__ # define setmode _setmode # define stat _stat # define chmod _chmod # define getcwd _getcwd # define putenv _putenv #elif defined __CYGWIN__ # define HAVE_SETENV # define FOPEN_WB "wb" /* #elif defined other platforms ... */ #endif #if defined PATH_MAX # define LT_PATHMAX PATH_MAX #elif defined MAXPATHLEN # define LT_PATHMAX MAXPATHLEN #else # define LT_PATHMAX 1024 #endif #ifndef S_IXOTH # define S_IXOTH 0 #endif #ifndef S_IXGRP # define S_IXGRP 0 #endif /* path handling portability macros */ #ifndef DIR_SEPARATOR # define DIR_SEPARATOR '/' # define PATH_SEPARATOR ':' #endif #if defined _WIN32 || defined __MSDOS__ || defined __DJGPP__ || \ defined __OS2__ # define HAVE_DOS_BASED_FILE_SYSTEM # define FOPEN_WB "wb" # ifndef DIR_SEPARATOR_2 # define DIR_SEPARATOR_2 '\\' # endif # ifndef PATH_SEPARATOR_2 # define PATH_SEPARATOR_2 ';' # endif #endif #ifndef DIR_SEPARATOR_2 # define IS_DIR_SEPARATOR(ch) ((ch) == DIR_SEPARATOR) #else /* DIR_SEPARATOR_2 */ # define IS_DIR_SEPARATOR(ch) \ (((ch) == DIR_SEPARATOR) || ((ch) == DIR_SEPARATOR_2)) #endif /* DIR_SEPARATOR_2 */ #ifndef PATH_SEPARATOR_2 # define IS_PATH_SEPARATOR(ch) ((ch) == PATH_SEPARATOR) #else /* PATH_SEPARATOR_2 */ # define IS_PATH_SEPARATOR(ch) ((ch) == PATH_SEPARATOR_2) #endif /* PATH_SEPARATOR_2 */ #ifndef FOPEN_WB # define FOPEN_WB "w" #endif #ifndef _O_BINARY # define _O_BINARY 0 #endif #define XMALLOC(type, num) ((type *) xmalloc ((num) * sizeof(type))) #define XFREE(stale) do { \ if (stale) { free (stale); stale = 0; } \ } while (0) #if defined LT_DEBUGWRAPPER static int lt_debug = 1; #else static int lt_debug = 0; #endif const char *program_name = "libtool-wrapper"; /* in case xstrdup fails */ void *xmalloc (size_t num); char *xstrdup (const char *string); const char *base_name (const char *name); char *find_executable (const char *wrapper); char *chase_symlinks (const char *pathspec); int make_executable (const char *path); int check_executable (const char *path); char *strendzap (char *str, const char *pat); void lt_debugprintf (const char *file, int line, const char *fmt, ...); void lt_fatal (const char *file, int line, const char *message, ...); static const char *nonnull (const char *s); static const char *nonempty (const char *s); void lt_setenv (const char *name, const char *value); char *lt_extend_str (const char *orig_value, const char *add, int to_end); void lt_update_exe_path (const char *name, const char *value); void lt_update_lib_path (const char *name, const char *value); char **prepare_spawn (char **argv); void lt_dump_script (FILE *f); EOF cat <= 0) && (st.st_mode & (S_IXUSR | S_IXGRP | S_IXOTH))) return 1; else return 0; } int make_executable (const char *path) { int rval = 0; struct stat st; lt_debugprintf (__FILE__, __LINE__, "(make_executable): %s\n", nonempty (path)); if ((!path) || (!*path)) return 0; if (stat (path, &st) >= 0) { rval = chmod (path, st.st_mode | S_IXOTH | S_IXGRP | S_IXUSR); } return rval; } /* Searches for the full path of the wrapper. Returns newly allocated full path name if found, NULL otherwise Does not chase symlinks, even on platforms that support them. */ char * find_executable (const char *wrapper) { int has_slash = 0; const char *p; const char *p_next; /* static buffer for getcwd */ char tmp[LT_PATHMAX + 1]; size_t tmp_len; char *concat_name; lt_debugprintf (__FILE__, __LINE__, "(find_executable): %s\n", nonempty (wrapper)); if ((wrapper == NULL) || (*wrapper == '\0')) return NULL; /* Absolute path? */ #if defined HAVE_DOS_BASED_FILE_SYSTEM if (isalpha ((unsigned char) wrapper[0]) && wrapper[1] == ':') { concat_name = xstrdup (wrapper); if (check_executable (concat_name)) return concat_name; XFREE (concat_name); } else { #endif if (IS_DIR_SEPARATOR (wrapper[0])) { concat_name = xstrdup (wrapper); if (check_executable (concat_name)) return concat_name; XFREE (concat_name); } #if defined HAVE_DOS_BASED_FILE_SYSTEM } #endif for (p = wrapper; *p; p++) if (*p == '/') { has_slash = 1; break; } if (!has_slash) { /* no slashes; search PATH */ const char *path = getenv ("PATH"); if (path != NULL) { for (p = path; *p; p = p_next) { const char *q; size_t p_len; for (q = p; *q; q++) if (IS_PATH_SEPARATOR (*q)) break; p_len = (size_t) (q - p); p_next = (*q == '\0' ? q : q + 1); if (p_len == 0) { /* empty path: current directory */ if (getcwd (tmp, LT_PATHMAX) == NULL) lt_fatal (__FILE__, __LINE__, "getcwd failed: %s", nonnull (strerror (errno))); tmp_len = strlen (tmp); concat_name = XMALLOC (char, tmp_len + 1 + strlen (wrapper) + 1); memcpy (concat_name, tmp, tmp_len); concat_name[tmp_len] = '/'; strcpy (concat_name + tmp_len + 1, wrapper); } else { concat_name = XMALLOC (char, p_len + 1 + strlen (wrapper) + 1); memcpy (concat_name, p, p_len); concat_name[p_len] = '/'; strcpy (concat_name + p_len + 1, wrapper); } if (check_executable (concat_name)) return concat_name; XFREE (concat_name); } } /* not found in PATH; assume curdir */ } /* Relative path | not found in path: prepend cwd */ if (getcwd (tmp, LT_PATHMAX) == NULL) lt_fatal (__FILE__, __LINE__, "getcwd failed: %s", nonnull (strerror (errno))); tmp_len = strlen (tmp); concat_name = XMALLOC (char, tmp_len + 1 + strlen (wrapper) + 1); memcpy (concat_name, tmp, tmp_len); concat_name[tmp_len] = '/'; strcpy (concat_name + tmp_len + 1, wrapper); if (check_executable (concat_name)) return concat_name; XFREE (concat_name); return NULL; } char * chase_symlinks (const char *pathspec) { #ifndef S_ISLNK return xstrdup (pathspec); #else char buf[LT_PATHMAX]; struct stat s; char *tmp_pathspec = xstrdup (pathspec); char *p; int has_symlinks = 0; while (strlen (tmp_pathspec) && !has_symlinks) { lt_debugprintf (__FILE__, __LINE__, "checking path component for symlinks: %s\n", tmp_pathspec); if (lstat (tmp_pathspec, &s) == 0) { if (S_ISLNK (s.st_mode) != 0) { has_symlinks = 1; break; } /* search backwards for last DIR_SEPARATOR */ p = tmp_pathspec + strlen (tmp_pathspec) - 1; while ((p > tmp_pathspec) && (!IS_DIR_SEPARATOR (*p))) p--; if ((p == tmp_pathspec) && (!IS_DIR_SEPARATOR (*p))) { /* no more DIR_SEPARATORS left */ break; } *p = '\0'; } else { lt_fatal (__FILE__, __LINE__, "error accessing file \"%s\": %s", tmp_pathspec, nonnull (strerror (errno))); } } XFREE (tmp_pathspec); if (!has_symlinks) { return xstrdup (pathspec); } tmp_pathspec = realpath (pathspec, buf); if (tmp_pathspec == 0) { lt_fatal (__FILE__, __LINE__, "could not follow symlinks for %s", pathspec); } return xstrdup (tmp_pathspec); #endif } char * strendzap (char *str, const char *pat) { size_t len, patlen; assert (str != NULL); assert (pat != NULL); len = strlen (str); patlen = strlen (pat); if (patlen <= len) { str += len - patlen; if (STREQ (str, pat)) *str = '\0'; } return str; } void lt_debugprintf (const char *file, int line, const char *fmt, ...) { va_list args; if (lt_debug) { (void) fprintf (stderr, "%s:%s:%d: ", program_name, file, line); va_start (args, fmt); (void) vfprintf (stderr, fmt, args); va_end (args); } } static void lt_error_core (int exit_status, const char *file, int line, const char *mode, const char *message, va_list ap) { fprintf (stderr, "%s:%s:%d: %s: ", program_name, file, line, mode); vfprintf (stderr, message, ap); fprintf (stderr, ".\n"); if (exit_status >= 0) exit (exit_status); } void lt_fatal (const char *file, int line, const char *message, ...) { va_list ap; va_start (ap, message); lt_error_core (EXIT_FAILURE, file, line, "FATAL", message, ap); va_end (ap); } static const char * nonnull (const char *s) { return s ? s : "(null)"; } static const char * nonempty (const char *s) { return (s && !*s) ? "(empty)" : nonnull (s); } void lt_setenv (const char *name, const char *value) { lt_debugprintf (__FILE__, __LINE__, "(lt_setenv) setting '%s' to '%s'\n", nonnull (name), nonnull (value)); { #ifdef HAVE_SETENV /* always make a copy, for consistency with !HAVE_SETENV */ char *str = xstrdup (value); setenv (name, str, 1); #else size_t len = strlen (name) + 1 + strlen (value) + 1; char *str = XMALLOC (char, len); sprintf (str, "%s=%s", name, value); if (putenv (str) != EXIT_SUCCESS) { XFREE (str); } #endif } } char * lt_extend_str (const char *orig_value, const char *add, int to_end) { char *new_value; if (orig_value && *orig_value) { size_t orig_value_len = strlen (orig_value); size_t add_len = strlen (add); new_value = XMALLOC (char, add_len + orig_value_len + 1); if (to_end) { strcpy (new_value, orig_value); strcpy (new_value + orig_value_len, add); } else { strcpy (new_value, add); strcpy (new_value + add_len, orig_value); } } else { new_value = xstrdup (add); } return new_value; } void lt_update_exe_path (const char *name, const char *value) { lt_debugprintf (__FILE__, __LINE__, "(lt_update_exe_path) modifying '%s' by prepending '%s'\n", nonnull (name), nonnull (value)); if (name && *name && value && *value) { char *new_value = lt_extend_str (getenv (name), value, 0); /* some systems can't cope with a ':'-terminated path #' */ size_t len = strlen (new_value); while ((len > 0) && IS_PATH_SEPARATOR (new_value[len-1])) { new_value[--len] = '\0'; } lt_setenv (name, new_value); XFREE (new_value); } } void lt_update_lib_path (const char *name, const char *value) { lt_debugprintf (__FILE__, __LINE__, "(lt_update_lib_path) modifying '%s' by prepending '%s'\n", nonnull (name), nonnull (value)); if (name && *name && value && *value) { char *new_value = lt_extend_str (getenv (name), value, 0); lt_setenv (name, new_value); XFREE (new_value); } } EOF case $host_os in mingw*) cat <<"EOF" /* Prepares an argument vector before calling spawn(). Note that spawn() does not by itself call the command interpreter (getenv ("COMSPEC") != NULL ? getenv ("COMSPEC") : ({ OSVERSIONINFO v; v.dwOSVersionInfoSize = sizeof(OSVERSIONINFO); GetVersionEx(&v); v.dwPlatformId == VER_PLATFORM_WIN32_NT; }) ? "cmd.exe" : "command.com"). Instead it simply concatenates the arguments, separated by ' ', and calls CreateProcess(). We must quote the arguments since Win32 CreateProcess() interprets characters like ' ', '\t', '\\', '"' (but not '<' and '>') in a special way: - Space and tab are interpreted as delimiters. They are not treated as delimiters if they are surrounded by double quotes: "...". - Unescaped double quotes are removed from the input. Their only effect is that within double quotes, space and tab are treated like normal characters. - Backslashes not followed by double quotes are not special. - But 2*n+1 backslashes followed by a double quote become n backslashes followed by a double quote (n >= 0): \" -> " \\\" -> \" \\\\\" -> \\" */ #define SHELL_SPECIAL_CHARS "\"\\ \001\002\003\004\005\006\007\010\011\012\013\014\015\016\017\020\021\022\023\024\025\026\027\030\031\032\033\034\035\036\037" #define SHELL_SPACE_CHARS " \001\002\003\004\005\006\007\010\011\012\013\014\015\016\017\020\021\022\023\024\025\026\027\030\031\032\033\034\035\036\037" char ** prepare_spawn (char **argv) { size_t argc; char **new_argv; size_t i; /* Count number of arguments. */ for (argc = 0; argv[argc] != NULL; argc++) ; /* Allocate new argument vector. */ new_argv = XMALLOC (char *, argc + 1); /* Put quoted arguments into the new argument vector. */ for (i = 0; i < argc; i++) { const char *string = argv[i]; if (string[0] == '\0') new_argv[i] = xstrdup ("\"\""); else if (strpbrk (string, SHELL_SPECIAL_CHARS) != NULL) { int quote_around = (strpbrk (string, SHELL_SPACE_CHARS) != NULL); size_t length; unsigned int backslashes; const char *s; char *quoted_string; char *p; length = 0; backslashes = 0; if (quote_around) length++; for (s = string; *s != '\0'; s++) { char c = *s; if (c == '"') length += backslashes + 1; length++; if (c == '\\') backslashes++; else backslashes = 0; } if (quote_around) length += backslashes + 1; quoted_string = XMALLOC (char, length + 1); p = quoted_string; backslashes = 0; if (quote_around) *p++ = '"'; for (s = string; *s != '\0'; s++) { char c = *s; if (c == '"') { unsigned int j; for (j = backslashes + 1; j > 0; j--) *p++ = '\\'; } *p++ = c; if (c == '\\') backslashes++; else backslashes = 0; } if (quote_around) { unsigned int j; for (j = backslashes; j > 0; j--) *p++ = '\\'; *p++ = '"'; } *p = '\0'; new_argv[i] = quoted_string; } else new_argv[i] = (char *) string; } new_argv[argc] = NULL; return new_argv; } EOF ;; esac cat <<"EOF" void lt_dump_script (FILE* f) { EOF func_emit_wrapper yes | $SED -n -e ' s/^\(.\{79\}\)\(..*\)/\1\ \2/ h s/\([\\"]\)/\\\1/g s/$/\\n/ s/\([^\n]*\).*/ fputs ("\1", f);/p g D' cat <<"EOF" } EOF } # end: func_emit_cwrapperexe_src # func_win32_import_lib_p ARG # True if ARG is an import lib, as indicated by $file_magic_cmd func_win32_import_lib_p () { $debug_cmd case `eval $file_magic_cmd \"\$1\" 2>/dev/null | $SED -e 10q` in *import*) : ;; *) false ;; esac } # func_suncc_cstd_abi # !!ONLY CALL THIS FOR SUN CC AFTER $compile_command IS FULLY EXPANDED!! # Several compiler flags select an ABI that is incompatible with the # Cstd library. Avoid specifying it if any are in CXXFLAGS. func_suncc_cstd_abi () { $debug_cmd case " $compile_command " in *" -compat=g "*|*\ -std=c++[0-9][0-9]\ *|*" -library=stdcxx4 "*|*" -library=stlport4 "*) suncc_use_cstd_abi=no ;; *) suncc_use_cstd_abi=yes ;; esac } # func_mode_link arg... func_mode_link () { $debug_cmd case $host in *-*-cygwin* | *-*-mingw* | *-*-pw32* | *-*-os2* | *-cegcc*) # It is impossible to link a dll without this setting, and # we shouldn't force the makefile maintainer to figure out # what system we are compiling for in order to pass an extra # flag for every libtool invocation. # allow_undefined=no # FIXME: Unfortunately, there are problems with the above when trying # to make a dll that has undefined symbols, in which case not # even a static library is built. For now, we need to specify # -no-undefined on the libtool link line when we can be certain # that all symbols are satisfied, otherwise we get a static library. allow_undefined=yes ;; *) allow_undefined=yes ;; esac libtool_args=$nonopt base_compile="$nonopt $@" compile_command=$nonopt finalize_command=$nonopt compile_rpath= finalize_rpath= compile_shlibpath= finalize_shlibpath= convenience= old_convenience= deplibs= old_deplibs= compiler_flags= linker_flags= dllsearchpath= lib_search_path=`pwd` inst_prefix_dir= new_inherited_linker_flags= avoid_version=no bindir= dlfiles= dlprefiles= dlself=no export_dynamic=no export_symbols= export_symbols_regex= generated= libobjs= ltlibs= module=no no_install=no objs= os2dllname= non_pic_objects= precious_files_regex= prefer_static_libs=no preload=false prev= prevarg= release= rpath= xrpath= perm_rpath= temp_rpath= thread_safe=no vinfo= vinfo_number=no weak_libs= single_module=$wl-single_module func_infer_tag $base_compile # We need to know -static, to get the right output filenames. for arg do case $arg in -shared) test yes != "$build_libtool_libs" \ && func_fatal_configuration "cannot build a shared library" build_old_libs=no break ;; -all-static | -static | -static-libtool-libs) case $arg in -all-static) if test yes = "$build_libtool_libs" && test -z "$link_static_flag"; then func_warning "complete static linking is impossible in this configuration" fi if test -n "$link_static_flag"; then dlopen_self=$dlopen_self_static fi prefer_static_libs=yes ;; -static) if test -z "$pic_flag" && test -n "$link_static_flag"; then dlopen_self=$dlopen_self_static fi prefer_static_libs=built ;; -static-libtool-libs) if test -z "$pic_flag" && test -n "$link_static_flag"; then dlopen_self=$dlopen_self_static fi prefer_static_libs=yes ;; esac build_libtool_libs=no build_old_libs=yes break ;; esac done # See if our shared archives depend on static archives. test -n "$old_archive_from_new_cmds" && build_old_libs=yes # Go through the arguments, transforming them on the way. while test "$#" -gt 0; do arg=$1 shift func_quote_arg pretty,unquoted "$arg" qarg=$func_quote_arg_unquoted_result func_append libtool_args " $func_quote_arg_result" # If the previous option needs an argument, assign it. if test -n "$prev"; then case $prev in output) func_append compile_command " @OUTPUT@" func_append finalize_command " @OUTPUT@" ;; esac case $prev in bindir) bindir=$arg prev= continue ;; dlfiles|dlprefiles) $preload || { # Add the symbol object into the linking commands. func_append compile_command " @SYMFILE@" func_append finalize_command " @SYMFILE@" preload=: } case $arg in *.la | *.lo) ;; # We handle these cases below. force) if test no = "$dlself"; then dlself=needless export_dynamic=yes fi prev= continue ;; self) if test dlprefiles = "$prev"; then dlself=yes elif test dlfiles = "$prev" && test yes != "$dlopen_self"; then dlself=yes else dlself=needless export_dynamic=yes fi prev= continue ;; *) if test dlfiles = "$prev"; then func_append dlfiles " $arg" else func_append dlprefiles " $arg" fi prev= continue ;; esac ;; expsyms) export_symbols=$arg test -f "$arg" \ || func_fatal_error "symbol file '$arg' does not exist" prev= continue ;; expsyms_regex) export_symbols_regex=$arg prev= continue ;; framework) case $host in *-*-darwin*) case "$deplibs " in *" $qarg.ltframework "*) ;; *) func_append deplibs " $qarg.ltframework" # this is fixed later ;; esac ;; esac prev= continue ;; inst_prefix) inst_prefix_dir=$arg prev= continue ;; mllvm) # Clang does not use LLVM to link, so we can simply discard any # '-mllvm $arg' options when doing the link step. prev= continue ;; objectlist) if test -f "$arg"; then save_arg=$arg moreargs= for fil in `cat "$save_arg"` do # func_append moreargs " $fil" arg=$fil # A libtool-controlled object. # Check to see that this really is a libtool object. if func_lalib_unsafe_p "$arg"; then pic_object= non_pic_object= # Read the .lo file func_source "$arg" if test -z "$pic_object" || test -z "$non_pic_object" || test none = "$pic_object" && test none = "$non_pic_object"; then func_fatal_error "cannot find name of object for '$arg'" fi # Extract subdirectory from the argument. func_dirname "$arg" "/" "" xdir=$func_dirname_result if test none != "$pic_object"; then # Prepend the subdirectory the object is found in. pic_object=$xdir$pic_object if test dlfiles = "$prev"; then if test yes = "$build_libtool_libs" && test yes = "$dlopen_support"; then func_append dlfiles " $pic_object" prev= continue else # If libtool objects are unsupported, then we need to preload. prev=dlprefiles fi fi # CHECK ME: I think I busted this. -Ossama if test dlprefiles = "$prev"; then # Preload the old-style object. func_append dlprefiles " $pic_object" prev= fi # A PIC object. func_append libobjs " $pic_object" arg=$pic_object fi # Non-PIC object. if test none != "$non_pic_object"; then # Prepend the subdirectory the object is found in. non_pic_object=$xdir$non_pic_object # A standard non-PIC object func_append non_pic_objects " $non_pic_object" if test -z "$pic_object" || test none = "$pic_object"; then arg=$non_pic_object fi else # If the PIC object exists, use it instead. # $xdir was prepended to $pic_object above. non_pic_object=$pic_object func_append non_pic_objects " $non_pic_object" fi else # Only an error if not doing a dry-run. if $opt_dry_run; then # Extract subdirectory from the argument. func_dirname "$arg" "/" "" xdir=$func_dirname_result func_lo2o "$arg" pic_object=$xdir$objdir/$func_lo2o_result non_pic_object=$xdir$func_lo2o_result func_append libobjs " $pic_object" func_append non_pic_objects " $non_pic_object" else func_fatal_error "'$arg' is not a valid libtool object" fi fi done else func_fatal_error "link input file '$arg' does not exist" fi arg=$save_arg prev= continue ;; os2dllname) os2dllname=$arg prev= continue ;; precious_regex) precious_files_regex=$arg prev= continue ;; release) release=-$arg prev= continue ;; rpath | xrpath) # We need an absolute path. case $arg in [\\/]* | [A-Za-z]:[\\/]*) ;; *) func_fatal_error "only absolute run-paths are allowed" ;; esac if test rpath = "$prev"; then case "$rpath " in *" $arg "*) ;; *) func_append rpath " $arg" ;; esac else case "$xrpath " in *" $arg "*) ;; *) func_append xrpath " $arg" ;; esac fi prev= continue ;; shrext) shrext_cmds=$arg prev= continue ;; weak) func_append weak_libs " $arg" prev= continue ;; xassembler) func_append compiler_flags " -Xassembler $qarg" prev= func_append compile_command " -Xassembler $qarg" func_append finalize_command " -Xassembler $qarg" continue ;; xcclinker) func_append linker_flags " $qarg" func_append compiler_flags " $qarg" prev= func_append compile_command " $qarg" func_append finalize_command " $qarg" continue ;; xcompiler) func_append compiler_flags " $qarg" prev= func_append compile_command " $qarg" func_append finalize_command " $qarg" continue ;; xlinker) func_append linker_flags " $qarg" func_append compiler_flags " $wl$qarg" prev= func_append compile_command " $wl$qarg" func_append finalize_command " $wl$qarg" continue ;; *) eval "$prev=\"\$arg\"" prev= continue ;; esac fi # test -n "$prev" prevarg=$arg case $arg in -all-static) if test -n "$link_static_flag"; then # See comment for -static flag below, for more details. func_append compile_command " $link_static_flag" func_append finalize_command " $link_static_flag" fi continue ;; -allow-undefined) # FIXME: remove this flag sometime in the future. func_fatal_error "'-allow-undefined' must not be used because it is the default" ;; -avoid-version) avoid_version=yes continue ;; -bindir) prev=bindir continue ;; -dlopen) prev=dlfiles continue ;; -dlpreopen) prev=dlprefiles continue ;; -export-dynamic) export_dynamic=yes continue ;; -export-symbols | -export-symbols-regex) if test -n "$export_symbols" || test -n "$export_symbols_regex"; then func_fatal_error "more than one -exported-symbols argument is not allowed" fi if test X-export-symbols = "X$arg"; then prev=expsyms else prev=expsyms_regex fi continue ;; -framework) prev=framework continue ;; -inst-prefix-dir) prev=inst_prefix continue ;; # The native IRIX linker understands -LANG:*, -LIST:* and -LNO:* # so, if we see these flags be careful not to treat them like -L -L[A-Z][A-Z]*:*) case $with_gcc/$host in no/*-*-irix* | /*-*-irix*) func_append compile_command " $arg" func_append finalize_command " $arg" ;; esac continue ;; -L*) func_stripname "-L" '' "$arg" if test -z "$func_stripname_result"; then if test "$#" -gt 0; then func_fatal_error "require no space between '-L' and '$1'" else func_fatal_error "need path for '-L' option" fi fi func_resolve_sysroot "$func_stripname_result" dir=$func_resolve_sysroot_result # We need an absolute path. case $dir in [\\/]* | [A-Za-z]:[\\/]*) ;; *) absdir=`cd "$dir" && pwd` test -z "$absdir" && \ func_fatal_error "cannot determine absolute directory name of '$dir'" dir=$absdir ;; esac case "$deplibs " in *" -L$dir "* | *" $arg "*) # Will only happen for absolute or sysroot arguments ;; *) # Preserve sysroot, but never include relative directories case $dir in [\\/]* | [A-Za-z]:[\\/]* | =*) func_append deplibs " $arg" ;; *) func_append deplibs " -L$dir" ;; esac func_append lib_search_path " $dir" ;; esac case $host in *-*-cygwin* | *-*-mingw* | *-*-pw32* | *-*-os2* | *-cegcc*) testbindir=`$ECHO "$dir" | $SED 's*/lib$*/bin*'` case :$dllsearchpath: in *":$dir:"*) ;; ::) dllsearchpath=$dir;; *) func_append dllsearchpath ":$dir";; esac case :$dllsearchpath: in *":$testbindir:"*) ;; ::) dllsearchpath=$testbindir;; *) func_append dllsearchpath ":$testbindir";; esac ;; esac continue ;; -l*) if test X-lc = "X$arg" || test X-lm = "X$arg"; then case $host in *-*-cygwin* | *-*-mingw* | *-*-pw32* | *-*-beos* | *-cegcc* | *-*-haiku*) # These systems don't actually have a C or math library (as such) continue ;; *-*-os2*) # These systems don't actually have a C library (as such) test X-lc = "X$arg" && continue ;; *-*-openbsd* | *-*-freebsd* | *-*-dragonfly* | *-*-bitrig* | *-*-midnightbsd*) # Do not include libc due to us having libc/libc_r. test X-lc = "X$arg" && continue ;; *-*-rhapsody* | *-*-darwin1.[012]) # Rhapsody C and math libraries are in the System framework func_append deplibs " System.ltframework" continue ;; *-*-sco3.2v5* | *-*-sco5v6*) # Causes problems with __ctype test X-lc = "X$arg" && continue ;; *-*-sysv4.2uw2* | *-*-sysv5* | *-*-unixware* | *-*-OpenUNIX*) # Compiler inserts libc in the correct place for threads to work test X-lc = "X$arg" && continue ;; esac elif test X-lc_r = "X$arg"; then case $host in *-*-openbsd* | *-*-freebsd* | *-*-dragonfly* | *-*-bitrig* | *-*-midnightbsd*) # Do not include libc_r directly, use -pthread flag. continue ;; esac fi func_append deplibs " $arg" continue ;; -mllvm) prev=mllvm continue ;; -module) module=yes continue ;; # Tru64 UNIX uses -model [arg] to determine the layout of C++ # classes, name mangling, and exception handling. # Darwin uses the -arch flag to determine output architecture. -model|-arch|-isysroot|--sysroot) func_append compiler_flags " $arg" func_append compile_command " $arg" func_append finalize_command " $arg" prev=xcompiler continue ;; # Solaris ld rejects as of 11.4. Refer to Oracle bug 22985199. -pthread) case $host in *solaris2*) ;; *) case "$new_inherited_linker_flags " in *" $arg "*) ;; * ) func_append new_inherited_linker_flags " $arg" ;; esac ;; esac continue ;; -mt|-mthreads|-kthread|-Kthread|-pthreads|--thread-safe \ |-threads|-fopenmp|-openmp|-mp|-xopenmp|-omp|-qsmp=*) func_append compiler_flags " $arg" func_append compile_command " $arg" func_append finalize_command " $arg" case "$new_inherited_linker_flags " in *" $arg "*) ;; * ) func_append new_inherited_linker_flags " $arg" ;; esac continue ;; -multi_module) single_module=$wl-multi_module continue ;; -no-fast-install) fast_install=no continue ;; -no-install) case $host in *-*-cygwin* | *-*-mingw* | *-*-pw32* | *-*-os2* | *-*-darwin* | *-cegcc*) # The PATH hackery in wrapper scripts is required on Windows # and Darwin in order for the loader to find any dlls it needs. func_warning "'-no-install' is ignored for $host" func_warning "assuming '-no-fast-install' instead" fast_install=no ;; *) no_install=yes ;; esac continue ;; -no-undefined) allow_undefined=no continue ;; -objectlist) prev=objectlist continue ;; -os2dllname) prev=os2dllname continue ;; -o) prev=output ;; -precious-files-regex) prev=precious_regex continue ;; -release) prev=release continue ;; -rpath) prev=rpath continue ;; -R) prev=xrpath continue ;; -R*) func_stripname '-R' '' "$arg" dir=$func_stripname_result # We need an absolute path. case $dir in [\\/]* | [A-Za-z]:[\\/]*) ;; =*) func_stripname '=' '' "$dir" dir=$lt_sysroot$func_stripname_result ;; *) func_fatal_error "only absolute run-paths are allowed" ;; esac case "$xrpath " in *" $dir "*) ;; *) func_append xrpath " $dir" ;; esac continue ;; -shared) # The effects of -shared are defined in a previous loop. continue ;; -shrext) prev=shrext continue ;; -static | -static-libtool-libs) # The effects of -static are defined in a previous loop. # We used to do the same as -all-static on platforms that # didn't have a PIC flag, but the assumption that the effects # would be equivalent was wrong. It would break on at least # Digital Unix and AIX. continue ;; -thread-safe) thread_safe=yes continue ;; -version-info) prev=vinfo continue ;; -version-number) prev=vinfo vinfo_number=yes continue ;; -weak) prev=weak continue ;; -Wc,*) func_stripname '-Wc,' '' "$arg" args=$func_stripname_result arg= save_ifs=$IFS; IFS=, for flag in $args; do IFS=$save_ifs func_quote_arg pretty "$flag" func_append arg " $func_quote_arg_result" func_append compiler_flags " $func_quote_arg_result" done IFS=$save_ifs func_stripname ' ' '' "$arg" arg=$func_stripname_result ;; -Wl,*) func_stripname '-Wl,' '' "$arg" args=$func_stripname_result arg= save_ifs=$IFS; IFS=, for flag in $args; do IFS=$save_ifs func_quote_arg pretty "$flag" func_append arg " $wl$func_quote_arg_result" func_append compiler_flags " $wl$func_quote_arg_result" func_append linker_flags " $func_quote_arg_result" done IFS=$save_ifs func_stripname ' ' '' "$arg" arg=$func_stripname_result ;; -Xassembler) prev=xassembler continue ;; -Xcompiler) prev=xcompiler continue ;; -Xlinker) prev=xlinker continue ;; -XCClinker) prev=xcclinker continue ;; # -msg_* for osf cc -msg_*) func_quote_arg pretty "$arg" arg=$func_quote_arg_result ;; # Flags to be passed through unchanged, with rationale: # -64, -mips[0-9] enable 64-bit mode for the SGI compiler # -r[0-9][0-9]* specify processor for the SGI compiler # -xarch=*, -xtarget=* enable 64-bit mode for the Sun compiler # +DA*, +DD* enable 64-bit mode for the HP compiler # -q* compiler args for the IBM compiler # -m*, -t[45]*, -txscale* architecture-specific flags for GCC # -F/path path to uninstalled frameworks, gcc on darwin # -p, -pg, --coverage, -fprofile-* profiling flags for GCC # -fstack-protector* stack protector flags for GCC # @file GCC response files # -tp=* Portland pgcc target processor selection # --sysroot=* for sysroot support # -O*, -g*, -flto*, -fwhopr*, -fuse-linker-plugin GCC link-time optimization # -specs=* GCC specs files # -stdlib=* select c++ std lib with clang # -fsanitize=* Clang/GCC memory and address sanitizer # -fuse-ld=* Linker select flags for GCC # -static-* direct GCC to link specific libraries statically # -fcilkplus Cilk Plus language extension features for C/C++ # -Wa,* Pass flags directly to the assembler -64|-mips[0-9]|-r[0-9][0-9]*|-xarch=*|-xtarget=*|+DA*|+DD*|-q*|-m*| \ -t[45]*|-txscale*|-p|-pg|--coverage|-fprofile-*|-F*|@*|-tp=*|--sysroot=*| \ -O*|-g*|-flto*|-fwhopr*|-fuse-linker-plugin|-fstack-protector*|-stdlib=*| \ -specs=*|-fsanitize=*|-fuse-ld=*|-static-*|-fcilkplus|-Wa,*) func_quote_arg pretty "$arg" arg=$func_quote_arg_result func_append compile_command " $arg" func_append finalize_command " $arg" func_append compiler_flags " $arg" continue ;; -Z*) if test os2 = "`expr $host : '.*\(os2\)'`"; then # OS/2 uses -Zxxx to specify OS/2-specific options compiler_flags="$compiler_flags $arg" func_append compile_command " $arg" func_append finalize_command " $arg" case $arg in -Zlinker | -Zstack) prev=xcompiler ;; esac continue else # Otherwise treat like 'Some other compiler flag' below func_quote_arg pretty "$arg" arg=$func_quote_arg_result fi ;; # Some other compiler flag. -* | +*) func_quote_arg pretty "$arg" arg=$func_quote_arg_result ;; *.$objext) # A standard object. func_append objs " $arg" ;; *.lo) # A libtool-controlled object. # Check to see that this really is a libtool object. if func_lalib_unsafe_p "$arg"; then pic_object= non_pic_object= # Read the .lo file func_source "$arg" if test -z "$pic_object" || test -z "$non_pic_object" || test none = "$pic_object" && test none = "$non_pic_object"; then func_fatal_error "cannot find name of object for '$arg'" fi # Extract subdirectory from the argument. func_dirname "$arg" "/" "" xdir=$func_dirname_result test none = "$pic_object" || { # Prepend the subdirectory the object is found in. pic_object=$xdir$pic_object if test dlfiles = "$prev"; then if test yes = "$build_libtool_libs" && test yes = "$dlopen_support"; then func_append dlfiles " $pic_object" prev= continue else # If libtool objects are unsupported, then we need to preload. prev=dlprefiles fi fi # CHECK ME: I think I busted this. -Ossama if test dlprefiles = "$prev"; then # Preload the old-style object. func_append dlprefiles " $pic_object" prev= fi # A PIC object. func_append libobjs " $pic_object" arg=$pic_object } # Non-PIC object. if test none != "$non_pic_object"; then # Prepend the subdirectory the object is found in. non_pic_object=$xdir$non_pic_object # A standard non-PIC object func_append non_pic_objects " $non_pic_object" if test -z "$pic_object" || test none = "$pic_object"; then arg=$non_pic_object fi else # If the PIC object exists, use it instead. # $xdir was prepended to $pic_object above. non_pic_object=$pic_object func_append non_pic_objects " $non_pic_object" fi else # Only an error if not doing a dry-run. if $opt_dry_run; then # Extract subdirectory from the argument. func_dirname "$arg" "/" "" xdir=$func_dirname_result func_lo2o "$arg" pic_object=$xdir$objdir/$func_lo2o_result non_pic_object=$xdir$func_lo2o_result func_append libobjs " $pic_object" func_append non_pic_objects " $non_pic_object" else func_fatal_error "'$arg' is not a valid libtool object" fi fi ;; *.$libext) # An archive. func_append deplibs " $arg" func_append old_deplibs " $arg" continue ;; *.la) # A libtool-controlled library. func_resolve_sysroot "$arg" if test dlfiles = "$prev"; then # This library was specified with -dlopen. func_append dlfiles " $func_resolve_sysroot_result" prev= elif test dlprefiles = "$prev"; then # The library was specified with -dlpreopen. func_append dlprefiles " $func_resolve_sysroot_result" prev= else func_append deplibs " $func_resolve_sysroot_result" fi continue ;; # Some other compiler argument. *) # Unknown arguments in both finalize_command and compile_command need # to be aesthetically quoted because they are evaled later. func_quote_arg pretty "$arg" arg=$func_quote_arg_result ;; esac # arg # Now actually substitute the argument into the commands. if test -n "$arg"; then func_append compile_command " $arg" func_append finalize_command " $arg" fi done # argument parsing loop test -n "$prev" && \ func_fatal_help "the '$prevarg' option requires an argument" if test yes = "$export_dynamic" && test -n "$export_dynamic_flag_spec"; then eval arg=\"$export_dynamic_flag_spec\" func_append compile_command " $arg" func_append finalize_command " $arg" fi oldlibs= # calculate the name of the file, without its directory func_basename "$output" outputname=$func_basename_result libobjs_save=$libobjs if test -n "$shlibpath_var"; then # get the directories listed in $shlibpath_var eval shlib_search_path=\`\$ECHO \"\$$shlibpath_var\" \| \$SED \'s/:/ /g\'\` else shlib_search_path= fi eval sys_lib_search_path=\"$sys_lib_search_path_spec\" eval sys_lib_dlsearch_path=\"$sys_lib_dlsearch_path_spec\" # Definition is injected by LT_CONFIG during libtool generation. func_munge_path_list sys_lib_dlsearch_path "$LT_SYS_LIBRARY_PATH" func_dirname "$output" "/" "" output_objdir=$func_dirname_result$objdir func_to_tool_file "$output_objdir/" tool_output_objdir=$func_to_tool_file_result # Create the object directory. func_mkdir_p "$output_objdir" # Determine the type of output case $output in "") func_fatal_help "you must specify an output file" ;; *.$libext) linkmode=oldlib ;; *.lo | *.$objext) linkmode=obj ;; *.la) linkmode=lib ;; *) linkmode=prog ;; # Anything else should be a program. esac specialdeplibs= libs= # Find all interdependent deplibs by searching for libraries # that are linked more than once (e.g. -la -lb -la) for deplib in $deplibs; do if $opt_preserve_dup_deps; then case "$libs " in *" $deplib "*) func_append specialdeplibs " $deplib" ;; esac fi func_append libs " $deplib" done if test lib = "$linkmode"; then libs="$predeps $libs $compiler_lib_search_path $postdeps" # Compute libraries that are listed more than once in $predeps # $postdeps and mark them as special (i.e., whose duplicates are # not to be eliminated). pre_post_deps= if $opt_duplicate_compiler_generated_deps; then for pre_post_dep in $predeps $postdeps; do case "$pre_post_deps " in *" $pre_post_dep "*) func_append specialdeplibs " $pre_post_deps" ;; esac func_append pre_post_deps " $pre_post_dep" done fi pre_post_deps= fi deplibs= newdependency_libs= newlib_search_path= need_relink=no # whether we're linking any uninstalled libtool libraries notinst_deplibs= # not-installed libtool libraries notinst_path= # paths that contain not-installed libtool libraries case $linkmode in lib) passes="conv dlpreopen link" for file in $dlfiles $dlprefiles; do case $file in *.la) ;; *) func_fatal_help "libraries can '-dlopen' only libtool libraries: $file" ;; esac done ;; prog) compile_deplibs= finalize_deplibs= alldeplibs=false newdlfiles= newdlprefiles= passes="conv scan dlopen dlpreopen link" ;; *) passes="conv" ;; esac for pass in $passes; do # The preopen pass in lib mode reverses $deplibs; put it back here # so that -L comes before libs that need it for instance... if test lib,link = "$linkmode,$pass"; then ## FIXME: Find the place where the list is rebuilt in the wrong ## order, and fix it there properly tmp_deplibs= for deplib in $deplibs; do tmp_deplibs="$deplib $tmp_deplibs" done deplibs=$tmp_deplibs fi if test lib,link = "$linkmode,$pass" || test prog,scan = "$linkmode,$pass"; then libs=$deplibs deplibs= fi if test prog = "$linkmode"; then case $pass in dlopen) libs=$dlfiles ;; dlpreopen) libs=$dlprefiles ;; link) libs="$deplibs %DEPLIBS%" test "X$link_all_deplibs" != Xno && libs="$libs $dependency_libs" ;; esac fi if test lib,dlpreopen = "$linkmode,$pass"; then # Collect and forward deplibs of preopened libtool libs for lib in $dlprefiles; do # Ignore non-libtool-libs dependency_libs= func_resolve_sysroot "$lib" case $lib in *.la) func_source "$func_resolve_sysroot_result" ;; esac # Collect preopened libtool deplibs, except any this library # has declared as weak libs for deplib in $dependency_libs; do func_basename "$deplib" deplib_base=$func_basename_result case " $weak_libs " in *" $deplib_base "*) ;; *) func_append deplibs " $deplib" ;; esac done done libs=$dlprefiles fi if test dlopen = "$pass"; then # Collect dlpreopened libraries save_deplibs=$deplibs deplibs= fi for deplib in $libs; do lib= found=false case $deplib in -mt|-mthreads|-kthread|-Kthread|-pthread|-pthreads|--thread-safe \ |-threads|-fopenmp|-openmp|-mp|-xopenmp|-omp|-qsmp=*) if test prog,link = "$linkmode,$pass"; then compile_deplibs="$deplib $compile_deplibs" finalize_deplibs="$deplib $finalize_deplibs" else func_append compiler_flags " $deplib" if test lib = "$linkmode"; then case "$new_inherited_linker_flags " in *" $deplib "*) ;; * ) func_append new_inherited_linker_flags " $deplib" ;; esac fi fi continue ;; -l*) if test lib != "$linkmode" && test prog != "$linkmode"; then func_warning "'-l' is ignored for archives/objects" continue fi func_stripname '-l' '' "$deplib" name=$func_stripname_result if test lib = "$linkmode"; then searchdirs="$newlib_search_path $lib_search_path $compiler_lib_search_dirs $sys_lib_search_path $shlib_search_path" else searchdirs="$newlib_search_path $lib_search_path $sys_lib_search_path $shlib_search_path" fi for searchdir in $searchdirs; do for search_ext in .la $std_shrext .so .a; do # Search the libtool library lib=$searchdir/lib$name$search_ext if test -f "$lib"; then if test .la = "$search_ext"; then found=: else found=false fi break 2 fi done done if $found; then # deplib is a libtool library # If $allow_libtool_libs_with_static_runtimes && $deplib is a stdlib, # We need to do some special things here, and not later. if test yes = "$allow_libtool_libs_with_static_runtimes"; then case " $predeps $postdeps " in *" $deplib "*) if func_lalib_p "$lib"; then library_names= old_library= func_source "$lib" for l in $old_library $library_names; do ll=$l done if test "X$ll" = "X$old_library"; then # only static version available found=false func_dirname "$lib" "" "." ladir=$func_dirname_result lib=$ladir/$old_library if test prog,link = "$linkmode,$pass"; then compile_deplibs="$deplib $compile_deplibs" finalize_deplibs="$deplib $finalize_deplibs" else deplibs="$deplib $deplibs" test lib = "$linkmode" && newdependency_libs="$deplib $newdependency_libs" fi continue fi fi ;; *) ;; esac fi else # deplib doesn't seem to be a libtool library if test prog,link = "$linkmode,$pass"; then compile_deplibs="$deplib $compile_deplibs" finalize_deplibs="$deplib $finalize_deplibs" else deplibs="$deplib $deplibs" test lib = "$linkmode" && newdependency_libs="$deplib $newdependency_libs" fi continue fi ;; # -l *.ltframework) if test prog,link = "$linkmode,$pass"; then compile_deplibs="$deplib $compile_deplibs" finalize_deplibs="$deplib $finalize_deplibs" else deplibs="$deplib $deplibs" if test lib = "$linkmode"; then case "$new_inherited_linker_flags " in *" $deplib "*) ;; * ) func_append new_inherited_linker_flags " $deplib" ;; esac fi fi continue ;; -L*) case $linkmode in lib) deplibs="$deplib $deplibs" test conv = "$pass" && continue newdependency_libs="$deplib $newdependency_libs" func_stripname '-L' '' "$deplib" func_resolve_sysroot "$func_stripname_result" func_append newlib_search_path " $func_resolve_sysroot_result" ;; prog) if test conv = "$pass"; then deplibs="$deplib $deplibs" continue fi if test scan = "$pass"; then deplibs="$deplib $deplibs" else compile_deplibs="$deplib $compile_deplibs" finalize_deplibs="$deplib $finalize_deplibs" fi func_stripname '-L' '' "$deplib" func_resolve_sysroot "$func_stripname_result" func_append newlib_search_path " $func_resolve_sysroot_result" ;; *) func_warning "'-L' is ignored for archives/objects" ;; esac # linkmode continue ;; # -L -R*) if test link = "$pass"; then func_stripname '-R' '' "$deplib" func_resolve_sysroot "$func_stripname_result" dir=$func_resolve_sysroot_result # Make sure the xrpath contains only unique directories. case "$xrpath " in *" $dir "*) ;; *) func_append xrpath " $dir" ;; esac fi deplibs="$deplib $deplibs" continue ;; *.la) func_resolve_sysroot "$deplib" lib=$func_resolve_sysroot_result ;; *.$libext) if test conv = "$pass"; then deplibs="$deplib $deplibs" continue fi case $linkmode in lib) # Linking convenience modules into shared libraries is allowed, # but linking other static libraries is non-portable. case " $dlpreconveniencelibs " in *" $deplib "*) ;; *) valid_a_lib=false case $deplibs_check_method in match_pattern*) set dummy $deplibs_check_method; shift match_pattern_regex=`expr "$deplibs_check_method" : "$1 \(.*\)"` if eval "\$ECHO \"$deplib\"" 2>/dev/null | $SED 10q \ | $EGREP "$match_pattern_regex" > /dev/null; then valid_a_lib=: fi ;; pass_all) valid_a_lib=: ;; esac if $valid_a_lib; then echo $ECHO "*** Warning: Linking the shared library $output against the" $ECHO "*** static library $deplib is not portable!" deplibs="$deplib $deplibs" else echo $ECHO "*** Warning: Trying to link with static lib archive $deplib." echo "*** I have the capability to make that library automatically link in when" echo "*** you link to this library. But I can only do this if you have a" echo "*** shared version of the library, which you do not appear to have" echo "*** because the file extensions .$libext of this argument makes me believe" echo "*** that it is just a static archive that I should not use here." fi ;; esac continue ;; prog) if test link != "$pass"; then deplibs="$deplib $deplibs" else compile_deplibs="$deplib $compile_deplibs" finalize_deplibs="$deplib $finalize_deplibs" fi continue ;; esac # linkmode ;; # *.$libext *.lo | *.$objext) if test conv = "$pass"; then deplibs="$deplib $deplibs" elif test prog = "$linkmode"; then if test dlpreopen = "$pass" || test yes != "$dlopen_support" || test no = "$build_libtool_libs"; then # If there is no dlopen support or we're linking statically, # we need to preload. func_append newdlprefiles " $deplib" compile_deplibs="$deplib $compile_deplibs" finalize_deplibs="$deplib $finalize_deplibs" else func_append newdlfiles " $deplib" fi fi continue ;; %DEPLIBS%) alldeplibs=: continue ;; esac # case $deplib $found || test -f "$lib" \ || func_fatal_error "cannot find the library '$lib' or unhandled argument '$deplib'" # Check to see that this really is a libtool archive. func_lalib_unsafe_p "$lib" \ || func_fatal_error "'$lib' is not a valid libtool archive" func_dirname "$lib" "" "." ladir=$func_dirname_result dlname= dlopen= dlpreopen= libdir= library_names= old_library= inherited_linker_flags= # If the library was installed with an old release of libtool, # it will not redefine variables installed, or shouldnotlink installed=yes shouldnotlink=no avoidtemprpath= # Read the .la file func_source "$lib" # Convert "-framework foo" to "foo.ltframework" if test -n "$inherited_linker_flags"; then tmp_inherited_linker_flags=`$ECHO "$inherited_linker_flags" | $SED 's/-framework \([^ $]*\)/\1.ltframework/g'` for tmp_inherited_linker_flag in $tmp_inherited_linker_flags; do case " $new_inherited_linker_flags " in *" $tmp_inherited_linker_flag "*) ;; *) func_append new_inherited_linker_flags " $tmp_inherited_linker_flag";; esac done fi dependency_libs=`$ECHO " $dependency_libs" | $SED 's% \([^ $]*\).ltframework% -framework \1%g'` if test lib,link = "$linkmode,$pass" || test prog,scan = "$linkmode,$pass" || { test prog != "$linkmode" && test lib != "$linkmode"; }; then test -n "$dlopen" && func_append dlfiles " $dlopen" test -n "$dlpreopen" && func_append dlprefiles " $dlpreopen" fi if test conv = "$pass"; then # Only check for convenience libraries deplibs="$lib $deplibs" if test -z "$libdir"; then if test -z "$old_library"; then func_fatal_error "cannot find name of link library for '$lib'" fi # It is a libtool convenience library, so add in its objects. func_append convenience " $ladir/$objdir/$old_library" func_append old_convenience " $ladir/$objdir/$old_library" tmp_libs= for deplib in $dependency_libs; do deplibs="$deplib $deplibs" if $opt_preserve_dup_deps; then case "$tmp_libs " in *" $deplib "*) func_append specialdeplibs " $deplib" ;; esac fi func_append tmp_libs " $deplib" done elif test prog != "$linkmode" && test lib != "$linkmode"; then func_fatal_error "'$lib' is not a convenience library" fi continue fi # $pass = conv # Get the name of the library we link against. linklib= if test -n "$old_library" && { test yes = "$prefer_static_libs" || test built,no = "$prefer_static_libs,$installed"; }; then linklib=$old_library else for l in $old_library $library_names; do linklib=$l done fi if test -z "$linklib"; then func_fatal_error "cannot find name of link library for '$lib'" fi # This library was specified with -dlopen. if test dlopen = "$pass"; then test -z "$libdir" \ && func_fatal_error "cannot -dlopen a convenience library: '$lib'" if test -z "$dlname" || test yes != "$dlopen_support" || test no = "$build_libtool_libs" then # If there is no dlname, no dlopen support or we're linking # statically, we need to preload. We also need to preload any # dependent libraries so libltdl's deplib preloader doesn't # bomb out in the load deplibs phase. func_append dlprefiles " $lib $dependency_libs" else func_append newdlfiles " $lib" fi continue fi # $pass = dlopen # We need an absolute path. case $ladir in [\\/]* | [A-Za-z]:[\\/]*) abs_ladir=$ladir ;; *) abs_ladir=`cd "$ladir" && pwd` if test -z "$abs_ladir"; then func_warning "cannot determine absolute directory name of '$ladir'" func_warning "passing it literally to the linker, although it might fail" abs_ladir=$ladir fi ;; esac func_basename "$lib" laname=$func_basename_result # Find the relevant object directory and library name. if test yes = "$installed"; then if test ! -f "$lt_sysroot$libdir/$linklib" && test -f "$abs_ladir/$linklib"; then func_warning "library '$lib' was moved." dir=$ladir absdir=$abs_ladir libdir=$abs_ladir else dir=$lt_sysroot$libdir absdir=$lt_sysroot$libdir fi test yes = "$hardcode_automatic" && avoidtemprpath=yes else if test ! -f "$ladir/$objdir/$linklib" && test -f "$abs_ladir/$linklib"; then dir=$ladir absdir=$abs_ladir # Remove this search path later func_append notinst_path " $abs_ladir" else dir=$ladir/$objdir absdir=$abs_ladir/$objdir # Remove this search path later func_append notinst_path " $abs_ladir" fi fi # $installed = yes func_stripname 'lib' '.la' "$laname" name=$func_stripname_result # This library was specified with -dlpreopen. if test dlpreopen = "$pass"; then if test -z "$libdir" && test prog = "$linkmode"; then func_fatal_error "only libraries may -dlpreopen a convenience library: '$lib'" fi case $host in # special handling for platforms with PE-DLLs. *cygwin* | *mingw* | *cegcc* ) # Linker will automatically link against shared library if both # static and shared are present. Therefore, ensure we extract # symbols from the import library if a shared library is present # (otherwise, the dlopen module name will be incorrect). We do # this by putting the import library name into $newdlprefiles. # We recover the dlopen module name by 'saving' the la file # name in a special purpose variable, and (later) extracting the # dlname from the la file. if test -n "$dlname"; then func_tr_sh "$dir/$linklib" eval "libfile_$func_tr_sh_result=\$abs_ladir/\$laname" func_append newdlprefiles " $dir/$linklib" else func_append newdlprefiles " $dir/$old_library" # Keep a list of preopened convenience libraries to check # that they are being used correctly in the link pass. test -z "$libdir" && \ func_append dlpreconveniencelibs " $dir/$old_library" fi ;; * ) # Prefer using a static library (so that no silly _DYNAMIC symbols # are required to link). if test -n "$old_library"; then func_append newdlprefiles " $dir/$old_library" # Keep a list of preopened convenience libraries to check # that they are being used correctly in the link pass. test -z "$libdir" && \ func_append dlpreconveniencelibs " $dir/$old_library" # Otherwise, use the dlname, so that lt_dlopen finds it. elif test -n "$dlname"; then func_append newdlprefiles " $dir/$dlname" else func_append newdlprefiles " $dir/$linklib" fi ;; esac fi # $pass = dlpreopen if test -z "$libdir"; then # Link the convenience library if test lib = "$linkmode"; then deplibs="$dir/$old_library $deplibs" elif test prog,link = "$linkmode,$pass"; then compile_deplibs="$dir/$old_library $compile_deplibs" finalize_deplibs="$dir/$old_library $finalize_deplibs" else deplibs="$lib $deplibs" # used for prog,scan pass fi continue fi if test prog = "$linkmode" && test link != "$pass"; then func_append newlib_search_path " $ladir" deplibs="$lib $deplibs" linkalldeplibs=false if test no != "$link_all_deplibs" || test -z "$library_names" || test no = "$build_libtool_libs"; then linkalldeplibs=: fi tmp_libs= for deplib in $dependency_libs; do case $deplib in -L*) func_stripname '-L' '' "$deplib" func_resolve_sysroot "$func_stripname_result" func_append newlib_search_path " $func_resolve_sysroot_result" ;; esac # Need to link against all dependency_libs? if $linkalldeplibs; then deplibs="$deplib $deplibs" else # Need to hardcode shared library paths # or/and link against static libraries newdependency_libs="$deplib $newdependency_libs" fi if $opt_preserve_dup_deps; then case "$tmp_libs " in *" $deplib "*) func_append specialdeplibs " $deplib" ;; esac fi func_append tmp_libs " $deplib" done # for deplib continue fi # $linkmode = prog... if test prog,link = "$linkmode,$pass"; then if test -n "$library_names" && { { test no = "$prefer_static_libs" || test built,yes = "$prefer_static_libs,$installed"; } || test -z "$old_library"; }; then # We need to hardcode the library path if test -n "$shlibpath_var" && test -z "$avoidtemprpath"; then # Make sure the rpath contains only unique directories. case $temp_rpath: in *"$absdir:"*) ;; *) func_append temp_rpath "$absdir:" ;; esac fi # Hardcode the library path. # Skip directories that are in the system default run-time # search path. case " $sys_lib_dlsearch_path " in *" $absdir "*) ;; *) case "$compile_rpath " in *" $absdir "*) ;; *) func_append compile_rpath " $absdir" ;; esac ;; esac case " $sys_lib_dlsearch_path " in *" $libdir "*) ;; *) case "$finalize_rpath " in *" $libdir "*) ;; *) func_append finalize_rpath " $libdir" ;; esac ;; esac fi # $linkmode,$pass = prog,link... if $alldeplibs && { test pass_all = "$deplibs_check_method" || { test yes = "$build_libtool_libs" && test -n "$library_names"; }; }; then # We only need to search for static libraries continue fi fi link_static=no # Whether the deplib will be linked statically use_static_libs=$prefer_static_libs if test built = "$use_static_libs" && test yes = "$installed"; then use_static_libs=no fi if test -n "$library_names" && { test no = "$use_static_libs" || test -z "$old_library"; }; then case $host in *cygwin* | *mingw* | *cegcc* | *os2*) # No point in relinking DLLs because paths are not encoded func_append notinst_deplibs " $lib" need_relink=no ;; *) if test no = "$installed"; then func_append notinst_deplibs " $lib" need_relink=yes fi ;; esac # This is a shared library # Warn about portability, can't link against -module's on some # systems (darwin). Don't bleat about dlopened modules though! dlopenmodule= for dlpremoduletest in $dlprefiles; do if test "X$dlpremoduletest" = "X$lib"; then dlopenmodule=$dlpremoduletest break fi done if test -z "$dlopenmodule" && test yes = "$shouldnotlink" && test link = "$pass"; then echo if test prog = "$linkmode"; then $ECHO "*** Warning: Linking the executable $output against the loadable module" else $ECHO "*** Warning: Linking the shared library $output against the loadable module" fi $ECHO "*** $linklib is not portable!" fi if test lib = "$linkmode" && test yes = "$hardcode_into_libs"; then # Hardcode the library path. # Skip directories that are in the system default run-time # search path. case " $sys_lib_dlsearch_path " in *" $absdir "*) ;; *) case "$compile_rpath " in *" $absdir "*) ;; *) func_append compile_rpath " $absdir" ;; esac ;; esac case " $sys_lib_dlsearch_path " in *" $libdir "*) ;; *) case "$finalize_rpath " in *" $libdir "*) ;; *) func_append finalize_rpath " $libdir" ;; esac ;; esac fi if test -n "$old_archive_from_expsyms_cmds"; then # figure out the soname set dummy $library_names shift realname=$1 shift libname=`eval "\\$ECHO \"$libname_spec\""` # use dlname if we got it. it's perfectly good, no? if test -n "$dlname"; then soname=$dlname elif test -n "$soname_spec"; then # bleh windows case $host in *cygwin* | mingw* | *cegcc* | *os2*) func_arith $current - $age major=$func_arith_result versuffix=-$major ;; esac eval soname=\"$soname_spec\" else soname=$realname fi # Make a new name for the extract_expsyms_cmds to use soroot=$soname func_basename "$soroot" soname=$func_basename_result func_stripname 'lib' '.dll' "$soname" newlib=libimp-$func_stripname_result.a # If the library has no export list, then create one now if test -f "$output_objdir/$soname-def"; then : else func_verbose "extracting exported symbol list from '$soname'" func_execute_cmds "$extract_expsyms_cmds" 'exit $?' fi # Create $newlib if test -f "$output_objdir/$newlib"; then :; else func_verbose "generating import library for '$soname'" func_execute_cmds "$old_archive_from_expsyms_cmds" 'exit $?' fi # make sure the library variables are pointing to the new library dir=$output_objdir linklib=$newlib fi # test -n "$old_archive_from_expsyms_cmds" if test prog = "$linkmode" || test relink != "$opt_mode"; then add_shlibpath= add_dir= add= lib_linked=yes case $hardcode_action in immediate | unsupported) if test no = "$hardcode_direct"; then add=$dir/$linklib case $host in *-*-sco3.2v5.0.[024]*) add_dir=-L$dir ;; *-*-sysv4*uw2*) add_dir=-L$dir ;; *-*-sysv5OpenUNIX* | *-*-sysv5UnixWare7.[01].[10]* | \ *-*-unixware7*) add_dir=-L$dir ;; *-*-darwin* ) # if the lib is a (non-dlopened) module then we cannot # link against it, someone is ignoring the earlier warnings if /usr/bin/file -L $add 2> /dev/null | $GREP ": [^:]* bundle" >/dev/null; then if test "X$dlopenmodule" != "X$lib"; then $ECHO "*** Warning: lib $linklib is a module, not a shared library" if test -z "$old_library"; then echo echo "*** And there doesn't seem to be a static archive available" echo "*** The link will probably fail, sorry" else add=$dir/$old_library fi elif test -n "$old_library"; then add=$dir/$old_library fi fi esac elif test no = "$hardcode_minus_L"; then case $host in *-*-sunos*) add_shlibpath=$dir ;; esac add_dir=-L$dir add=-l$name elif test no = "$hardcode_shlibpath_var"; then add_shlibpath=$dir add=-l$name else lib_linked=no fi ;; relink) if test yes = "$hardcode_direct" && test no = "$hardcode_direct_absolute"; then add=$dir/$linklib elif test yes = "$hardcode_minus_L"; then add_dir=-L$absdir # Try looking first in the location we're being installed to. if test -n "$inst_prefix_dir"; then case $libdir in [\\/]*) func_append add_dir " -L$inst_prefix_dir$libdir" ;; esac fi add=-l$name elif test yes = "$hardcode_shlibpath_var"; then add_shlibpath=$dir add=-l$name else lib_linked=no fi ;; *) lib_linked=no ;; esac if test yes != "$lib_linked"; then func_fatal_configuration "unsupported hardcode properties" fi if test -n "$add_shlibpath"; then case :$compile_shlibpath: in *":$add_shlibpath:"*) ;; *) func_append compile_shlibpath "$add_shlibpath:" ;; esac fi if test prog = "$linkmode"; then test -n "$add_dir" && compile_deplibs="$add_dir $compile_deplibs" test -n "$add" && compile_deplibs="$add $compile_deplibs" else test -n "$add_dir" && deplibs="$add_dir $deplibs" test -n "$add" && deplibs="$add $deplibs" if test yes != "$hardcode_direct" && test yes != "$hardcode_minus_L" && test yes = "$hardcode_shlibpath_var"; then case :$finalize_shlibpath: in *":$libdir:"*) ;; *) func_append finalize_shlibpath "$libdir:" ;; esac fi fi fi if test prog = "$linkmode" || test relink = "$opt_mode"; then add_shlibpath= add_dir= add= # Finalize command for both is simple: just hardcode it. if test yes = "$hardcode_direct" && test no = "$hardcode_direct_absolute"; then add=$libdir/$linklib elif test yes = "$hardcode_minus_L"; then add_dir=-L$libdir add=-l$name elif test yes = "$hardcode_shlibpath_var"; then case :$finalize_shlibpath: in *":$libdir:"*) ;; *) func_append finalize_shlibpath "$libdir:" ;; esac add=-l$name elif test yes = "$hardcode_automatic"; then if test -n "$inst_prefix_dir" && test -f "$inst_prefix_dir$libdir/$linklib"; then add=$inst_prefix_dir$libdir/$linklib else add=$libdir/$linklib fi else # We cannot seem to hardcode it, guess we'll fake it. add_dir=-L$libdir # Try looking first in the location we're being installed to. if test -n "$inst_prefix_dir"; then case $libdir in [\\/]*) func_append add_dir " -L$inst_prefix_dir$libdir" ;; esac fi add=-l$name fi if test prog = "$linkmode"; then test -n "$add_dir" && finalize_deplibs="$add_dir $finalize_deplibs" test -n "$add" && finalize_deplibs="$add $finalize_deplibs" else test -n "$add_dir" && deplibs="$add_dir $deplibs" test -n "$add" && deplibs="$add $deplibs" fi fi elif test prog = "$linkmode"; then # Here we assume that one of hardcode_direct or hardcode_minus_L # is not unsupported. This is valid on all known static and # shared platforms. if test unsupported != "$hardcode_direct"; then test -n "$old_library" && linklib=$old_library compile_deplibs="$dir/$linklib $compile_deplibs" finalize_deplibs="$dir/$linklib $finalize_deplibs" else compile_deplibs="-l$name -L$dir $compile_deplibs" finalize_deplibs="-l$name -L$dir $finalize_deplibs" fi elif test yes = "$build_libtool_libs"; then # Not a shared library if test pass_all != "$deplibs_check_method"; then # We're trying link a shared library against a static one # but the system doesn't support it. # Just print a warning and add the library to dependency_libs so # that the program can be linked against the static library. echo $ECHO "*** Warning: This system cannot link to static lib archive $lib." echo "*** I have the capability to make that library automatically link in when" echo "*** you link to this library. But I can only do this if you have a" echo "*** shared version of the library, which you do not appear to have." if test yes = "$module"; then echo "*** But as you try to build a module library, libtool will still create " echo "*** a static module, that should work as long as the dlopening application" echo "*** is linked with the -dlopen flag to resolve symbols at runtime." if test -z "$global_symbol_pipe"; then echo echo "*** However, this would only work if libtool was able to extract symbol" echo "*** lists from a program, using 'nm' or equivalent, but libtool could" echo "*** not find such a program. So, this module is probably useless." echo "*** 'nm' from GNU binutils and a full rebuild may help." fi if test no = "$build_old_libs"; then build_libtool_libs=module build_old_libs=yes else build_libtool_libs=no fi fi else deplibs="$dir/$old_library $deplibs" link_static=yes fi fi # link shared/static library? if test lib = "$linkmode"; then if test -n "$dependency_libs" && { test yes != "$hardcode_into_libs" || test yes = "$build_old_libs" || test yes = "$link_static"; }; then # Extract -R from dependency_libs temp_deplibs= for libdir in $dependency_libs; do case $libdir in -R*) func_stripname '-R' '' "$libdir" temp_xrpath=$func_stripname_result case " $xrpath " in *" $temp_xrpath "*) ;; *) func_append xrpath " $temp_xrpath";; esac;; *) func_append temp_deplibs " $libdir";; esac done dependency_libs=$temp_deplibs fi func_append newlib_search_path " $absdir" # Link against this library test no = "$link_static" && newdependency_libs="$abs_ladir/$laname $newdependency_libs" # ... and its dependency_libs tmp_libs= for deplib in $dependency_libs; do newdependency_libs="$deplib $newdependency_libs" case $deplib in -L*) func_stripname '-L' '' "$deplib" func_resolve_sysroot "$func_stripname_result";; *) func_resolve_sysroot "$deplib" ;; esac if $opt_preserve_dup_deps; then case "$tmp_libs " in *" $func_resolve_sysroot_result "*) func_append specialdeplibs " $func_resolve_sysroot_result" ;; esac fi func_append tmp_libs " $func_resolve_sysroot_result" done if test no != "$link_all_deplibs"; then # Add the search paths of all dependency libraries for deplib in $dependency_libs; do path= case $deplib in -L*) path=$deplib ;; *.la) func_resolve_sysroot "$deplib" deplib=$func_resolve_sysroot_result func_dirname "$deplib" "" "." dir=$func_dirname_result # We need an absolute path. case $dir in [\\/]* | [A-Za-z]:[\\/]*) absdir=$dir ;; *) absdir=`cd "$dir" && pwd` if test -z "$absdir"; then func_warning "cannot determine absolute directory name of '$dir'" absdir=$dir fi ;; esac if $GREP "^installed=no" $deplib > /dev/null; then case $host in *-*-darwin*) depdepl= eval deplibrary_names=`$SED -n -e 's/^library_names=\(.*\)$/\1/p' $deplib` if test -n "$deplibrary_names"; then for tmp in $deplibrary_names; do depdepl=$tmp done if test -f "$absdir/$objdir/$depdepl"; then depdepl=$absdir/$objdir/$depdepl darwin_install_name=`$OTOOL -L $depdepl | awk '{if (NR == 2) {print $1;exit}}'` if test -z "$darwin_install_name"; then darwin_install_name=`$OTOOL64 -L $depdepl | awk '{if (NR == 2) {print $1;exit}}'` fi func_append compiler_flags " $wl-dylib_file $wl$darwin_install_name:$depdepl" func_append linker_flags " -dylib_file $darwin_install_name:$depdepl" path= fi fi ;; *) path=-L$absdir/$objdir ;; esac else eval libdir=`$SED -n -e 's/^libdir=\(.*\)$/\1/p' $deplib` test -z "$libdir" && \ func_fatal_error "'$deplib' is not a valid libtool archive" test "$absdir" != "$libdir" && \ func_warning "'$deplib' seems to be moved" path=-L$absdir fi ;; esac case " $deplibs " in *" $path "*) ;; *) deplibs="$path $deplibs" ;; esac done fi # link_all_deplibs != no fi # linkmode = lib done # for deplib in $libs if test link = "$pass"; then if test prog = "$linkmode"; then compile_deplibs="$new_inherited_linker_flags $compile_deplibs" finalize_deplibs="$new_inherited_linker_flags $finalize_deplibs" else compiler_flags="$compiler_flags "`$ECHO " $new_inherited_linker_flags" | $SED 's% \([^ $]*\).ltframework% -framework \1%g'` fi fi dependency_libs=$newdependency_libs if test dlpreopen = "$pass"; then # Link the dlpreopened libraries before other libraries for deplib in $save_deplibs; do deplibs="$deplib $deplibs" done fi if test dlopen != "$pass"; then test conv = "$pass" || { # Make sure lib_search_path contains only unique directories. lib_search_path= for dir in $newlib_search_path; do case "$lib_search_path " in *" $dir "*) ;; *) func_append lib_search_path " $dir" ;; esac done newlib_search_path= } if test prog,link = "$linkmode,$pass"; then vars="compile_deplibs finalize_deplibs" else vars=deplibs fi for var in $vars dependency_libs; do # Add libraries to $var in reverse order eval tmp_libs=\"\$$var\" new_libs= for deplib in $tmp_libs; do # FIXME: Pedantically, this is the right thing to do, so # that some nasty dependency loop isn't accidentally # broken: #new_libs="$deplib $new_libs" # Pragmatically, this seems to cause very few problems in # practice: case $deplib in -L*) new_libs="$deplib $new_libs" ;; -R*) ;; *) # And here is the reason: when a library appears more # than once as an explicit dependence of a library, or # is implicitly linked in more than once by the # compiler, it is considered special, and multiple # occurrences thereof are not removed. Compare this # with having the same library being listed as a # dependency of multiple other libraries: in this case, # we know (pedantically, we assume) the library does not # need to be listed more than once, so we keep only the # last copy. This is not always right, but it is rare # enough that we require users that really mean to play # such unportable linking tricks to link the library # using -Wl,-lname, so that libtool does not consider it # for duplicate removal. case " $specialdeplibs " in *" $deplib "*) new_libs="$deplib $new_libs" ;; *) case " $new_libs " in *" $deplib "*) ;; *) new_libs="$deplib $new_libs" ;; esac ;; esac ;; esac done tmp_libs= for deplib in $new_libs; do case $deplib in -L*) case " $tmp_libs " in *" $deplib "*) ;; *) func_append tmp_libs " $deplib" ;; esac ;; *) func_append tmp_libs " $deplib" ;; esac done eval $var=\"$tmp_libs\" done # for var fi # Add Sun CC postdeps if required: test CXX = "$tagname" && { case $host_os in linux*) case `$CC -V 2>&1 | $SED 5q` in *Sun\ C*) # Sun C++ 5.9 func_suncc_cstd_abi if test no != "$suncc_use_cstd_abi"; then func_append postdeps ' -library=Cstd -library=Crun' fi ;; esac ;; solaris*) func_cc_basename "$CC" case $func_cc_basename_result in CC* | sunCC*) func_suncc_cstd_abi if test no != "$suncc_use_cstd_abi"; then func_append postdeps ' -library=Cstd -library=Crun' fi ;; esac ;; esac } # Last step: remove runtime libs from dependency_libs # (they stay in deplibs) tmp_libs= for i in $dependency_libs; do case " $predeps $postdeps $compiler_lib_search_path " in *" $i "*) i= ;; esac if test -n "$i"; then func_append tmp_libs " $i" fi done dependency_libs=$tmp_libs done # for pass if test prog = "$linkmode"; then dlfiles=$newdlfiles fi if test prog = "$linkmode" || test lib = "$linkmode"; then dlprefiles=$newdlprefiles fi case $linkmode in oldlib) if test -n "$dlfiles$dlprefiles" || test no != "$dlself"; then func_warning "'-dlopen' is ignored for archives" fi case " $deplibs" in *\ -l* | *\ -L*) func_warning "'-l' and '-L' are ignored for archives" ;; esac test -n "$rpath" && \ func_warning "'-rpath' is ignored for archives" test -n "$xrpath" && \ func_warning "'-R' is ignored for archives" test -n "$vinfo" && \ func_warning "'-version-info/-version-number' is ignored for archives" test -n "$release" && \ func_warning "'-release' is ignored for archives" test -n "$export_symbols$export_symbols_regex" && \ func_warning "'-export-symbols' is ignored for archives" # Now set the variables for building old libraries. build_libtool_libs=no oldlibs=$output func_append objs "$old_deplibs" ;; lib) # Make sure we only generate libraries of the form 'libNAME.la'. case $outputname in lib*) func_stripname 'lib' '.la' "$outputname" name=$func_stripname_result eval shared_ext=\"$shrext_cmds\" eval libname=\"$libname_spec\" ;; *) test no = "$module" \ && func_fatal_help "libtool library '$output' must begin with 'lib'" if test no != "$need_lib_prefix"; then # Add the "lib" prefix for modules if required func_stripname '' '.la' "$outputname" name=$func_stripname_result eval shared_ext=\"$shrext_cmds\" eval libname=\"$libname_spec\" else func_stripname '' '.la' "$outputname" libname=$func_stripname_result fi ;; esac if test -n "$objs"; then if test pass_all != "$deplibs_check_method"; then func_fatal_error "cannot build libtool library '$output' from non-libtool objects on this host:$objs" else echo $ECHO "*** Warning: Linking the shared library $output against the non-libtool" $ECHO "*** objects $objs is not portable!" func_append libobjs " $objs" fi fi test no = "$dlself" \ || func_warning "'-dlopen self' is ignored for libtool libraries" set dummy $rpath shift test 1 -lt "$#" \ && func_warning "ignoring multiple '-rpath's for a libtool library" install_libdir=$1 oldlibs= if test -z "$rpath"; then if test yes = "$build_libtool_libs"; then # Building a libtool convenience library. # Some compilers have problems with a '.al' extension so # convenience libraries should have the same extension an # archive normally would. oldlibs="$output_objdir/$libname.$libext $oldlibs" build_libtool_libs=convenience build_old_libs=yes fi test -n "$vinfo" && \ func_warning "'-version-info/-version-number' is ignored for convenience libraries" test -n "$release" && \ func_warning "'-release' is ignored for convenience libraries" else # Parse the version information argument. save_ifs=$IFS; IFS=: set dummy $vinfo 0 0 0 shift IFS=$save_ifs test -n "$7" && \ func_fatal_help "too many parameters to '-version-info'" # convert absolute version numbers to libtool ages # this retains compatibility with .la files and attempts # to make the code below a bit more comprehensible case $vinfo_number in yes) number_major=$1 number_minor=$2 number_revision=$3 # # There are really only two kinds -- those that # use the current revision as the major version # and those that subtract age and use age as # a minor version. But, then there is irix # that has an extra 1 added just for fun # case $version_type in # correct linux to gnu/linux during the next big refactor darwin|freebsd-elf|linux|midnightbsd-elf|osf|windows|none) func_arith $number_major + $number_minor current=$func_arith_result age=$number_minor revision=$number_revision ;; freebsd-aout|qnx|sunos) current=$number_major revision=$number_minor age=0 ;; irix|nonstopux) func_arith $number_major + $number_minor current=$func_arith_result age=$number_minor revision=$number_minor lt_irix_increment=no ;; *) func_fatal_configuration "$modename: unknown library version type '$version_type'" ;; esac ;; no) current=$1 revision=$2 age=$3 ;; esac # Check that each of the things are valid numbers. case $current in 0|[1-9]|[1-9][0-9]|[1-9][0-9][0-9]|[1-9][0-9][0-9][0-9]|[1-9][0-9][0-9][0-9][0-9]) ;; *) func_error "CURRENT '$current' must be a nonnegative integer" func_fatal_error "'$vinfo' is not valid version information" ;; esac case $revision in 0|[1-9]|[1-9][0-9]|[1-9][0-9][0-9]|[1-9][0-9][0-9][0-9]|[1-9][0-9][0-9][0-9][0-9]) ;; *) func_error "REVISION '$revision' must be a nonnegative integer" func_fatal_error "'$vinfo' is not valid version information" ;; esac case $age in 0|[1-9]|[1-9][0-9]|[1-9][0-9][0-9]|[1-9][0-9][0-9][0-9]|[1-9][0-9][0-9][0-9][0-9]) ;; *) func_error "AGE '$age' must be a nonnegative integer" func_fatal_error "'$vinfo' is not valid version information" ;; esac if test "$age" -gt "$current"; then func_error "AGE '$age' is greater than the current interface number '$current'" func_fatal_error "'$vinfo' is not valid version information" fi # Calculate the version variables. major= versuffix= verstring= case $version_type in none) ;; darwin) # Like Linux, but with the current version available in # verstring for coding it into the library header func_arith $current - $age major=.$func_arith_result versuffix=$major.$age.$revision # Darwin ld doesn't like 0 for these options... func_arith $current + 1 minor_current=$func_arith_result xlcverstring="$wl-compatibility_version $wl$minor_current $wl-current_version $wl$minor_current.$revision" verstring="-compatibility_version $minor_current -current_version $minor_current.$revision" # On Darwin other compilers case $CC in nagfor*) verstring="$wl-compatibility_version $wl$minor_current $wl-current_version $wl$minor_current.$revision" ;; *) verstring="-compatibility_version $minor_current -current_version $minor_current.$revision" ;; esac ;; freebsd-aout) major=.$current versuffix=.$current.$revision ;; freebsd-elf | midnightbsd-elf) func_arith $current - $age major=.$func_arith_result versuffix=$major.$age.$revision ;; irix | nonstopux) if test no = "$lt_irix_increment"; then func_arith $current - $age else func_arith $current - $age + 1 fi major=$func_arith_result case $version_type in nonstopux) verstring_prefix=nonstopux ;; *) verstring_prefix=sgi ;; esac verstring=$verstring_prefix$major.$revision # Add in all the interfaces that we are compatible with. loop=$revision while test 0 -ne "$loop"; do func_arith $revision - $loop iface=$func_arith_result func_arith $loop - 1 loop=$func_arith_result verstring=$verstring_prefix$major.$iface:$verstring done # Before this point, $major must not contain '.'. major=.$major versuffix=$major.$revision ;; linux) # correct to gnu/linux during the next big refactor func_arith $current - $age major=.$func_arith_result versuffix=$major.$age.$revision ;; osf) func_arith $current - $age major=.$func_arith_result versuffix=.$current.$age.$revision verstring=$current.$age.$revision # Add in all the interfaces that we are compatible with. loop=$age while test 0 -ne "$loop"; do func_arith $current - $loop iface=$func_arith_result func_arith $loop - 1 loop=$func_arith_result verstring=$verstring:$iface.0 done # Make executables depend on our current version. func_append verstring ":$current.0" ;; qnx) major=.$current versuffix=.$current ;; sco) major=.$current versuffix=.$current ;; sunos) major=.$current versuffix=.$current.$revision ;; windows) # Use '-' rather than '.', since we only want one # extension on DOS 8.3 file systems. func_arith $current - $age major=$func_arith_result versuffix=-$major ;; *) func_fatal_configuration "unknown library version type '$version_type'" ;; esac # Clear the version info if we defaulted, and they specified a release. if test -z "$vinfo" && test -n "$release"; then major= case $version_type in darwin) # we can't check for "0.0" in archive_cmds due to quoting # problems, so we reset it completely verstring= ;; *) verstring=0.0 ;; esac if test no = "$need_version"; then versuffix= else versuffix=.0.0 fi fi # Remove version info from name if versioning should be avoided if test yes,no = "$avoid_version,$need_version"; then major= versuffix= verstring= fi # Check to see if the archive will have undefined symbols. if test yes = "$allow_undefined"; then if test unsupported = "$allow_undefined_flag"; then if test yes = "$build_old_libs"; then func_warning "undefined symbols not allowed in $host shared libraries; building static only" build_libtool_libs=no else func_fatal_error "can't build $host shared library unless -no-undefined is specified" fi fi else # Don't allow undefined symbols. allow_undefined_flag=$no_undefined_flag fi fi func_generate_dlsyms "$libname" "$libname" : func_append libobjs " $symfileobj" test " " = "$libobjs" && libobjs= if test relink != "$opt_mode"; then # Remove our outputs, but don't remove object files since they # may have been created when compiling PIC objects. removelist= tempremovelist=`$ECHO "$output_objdir/*"` for p in $tempremovelist; do case $p in *.$objext | *.gcno) ;; $output_objdir/$outputname | $output_objdir/$libname.* | $output_objdir/$libname$release.*) if test -n "$precious_files_regex"; then if $ECHO "$p" | $EGREP -e "$precious_files_regex" >/dev/null 2>&1 then continue fi fi func_append removelist " $p" ;; *) ;; esac done test -n "$removelist" && \ func_show_eval "${RM}r \$removelist" fi # Now set the variables for building old libraries. if test yes = "$build_old_libs" && test convenience != "$build_libtool_libs"; then func_append oldlibs " $output_objdir/$libname.$libext" # Transform .lo files to .o files. oldobjs="$objs "`$ECHO "$libobjs" | $SP2NL | $SED "/\.$libext$/d; $lo2o" | $NL2SP` fi # Eliminate all temporary directories. #for path in $notinst_path; do # lib_search_path=`$ECHO "$lib_search_path " | $SED "s% $path % %g"` # deplibs=`$ECHO "$deplibs " | $SED "s% -L$path % %g"` # dependency_libs=`$ECHO "$dependency_libs " | $SED "s% -L$path % %g"` #done if test -n "$xrpath"; then # If the user specified any rpath flags, then add them. temp_xrpath= for libdir in $xrpath; do func_replace_sysroot "$libdir" func_append temp_xrpath " -R$func_replace_sysroot_result" case "$finalize_rpath " in *" $libdir "*) ;; *) func_append finalize_rpath " $libdir" ;; esac done if test yes != "$hardcode_into_libs" || test yes = "$build_old_libs"; then dependency_libs="$temp_xrpath $dependency_libs" fi fi # Make sure dlfiles contains only unique files that won't be dlpreopened old_dlfiles=$dlfiles dlfiles= for lib in $old_dlfiles; do case " $dlprefiles $dlfiles " in *" $lib "*) ;; *) func_append dlfiles " $lib" ;; esac done # Make sure dlprefiles contains only unique files old_dlprefiles=$dlprefiles dlprefiles= for lib in $old_dlprefiles; do case "$dlprefiles " in *" $lib "*) ;; *) func_append dlprefiles " $lib" ;; esac done if test yes = "$build_libtool_libs"; then if test -n "$rpath"; then case $host in *-*-cygwin* | *-*-mingw* | *-*-pw32* | *-*-os2* | *-*-beos* | *-cegcc* | *-*-haiku*) # these systems don't actually have a c library (as such)! ;; *-*-rhapsody* | *-*-darwin1.[012]) # Rhapsody C library is in the System framework func_append deplibs " System.ltframework" ;; *-*-netbsd*) # Don't link with libc until the a.out ld.so is fixed. ;; *-*-openbsd* | *-*-freebsd* | *-*-dragonfly* | *-*-midnightbsd*) # Do not include libc due to us having libc/libc_r. ;; *-*-sco3.2v5* | *-*-sco5v6*) # Causes problems with __ctype ;; *-*-sysv4.2uw2* | *-*-sysv5* | *-*-unixware* | *-*-OpenUNIX*) # Compiler inserts libc in the correct place for threads to work ;; *) # Add libc to deplibs on all other systems if necessary. if test yes = "$build_libtool_need_lc"; then func_append deplibs " -lc" fi ;; esac fi # Transform deplibs into only deplibs that can be linked in shared. name_save=$name libname_save=$libname release_save=$release versuffix_save=$versuffix major_save=$major # I'm not sure if I'm treating the release correctly. I think # release should show up in the -l (ie -lgmp5) so we don't want to # add it in twice. Is that correct? release= versuffix= major= newdeplibs= droppeddeps=no case $deplibs_check_method in pass_all) # Don't check for shared/static. Everything works. # This might be a little naive. We might want to check # whether the library exists or not. But this is on # osf3 & osf4 and I'm not really sure... Just # implementing what was already the behavior. newdeplibs=$deplibs ;; test_compile) # This code stresses the "libraries are programs" paradigm to its # limits. Maybe even breaks it. We compile a program, linking it # against the deplibs as a proxy for the library. Then we can check # whether they linked in statically or dynamically with ldd. $opt_dry_run || $RM conftest.c cat > conftest.c </dev/null` $nocaseglob else potential_libs=`ls $i/$libnameglob[.-]* 2>/dev/null` fi for potent_lib in $potential_libs; do # Follow soft links. if ls -lLd "$potent_lib" 2>/dev/null | $GREP " -> " >/dev/null; then continue fi # The statement above tries to avoid entering an # endless loop below, in case of cyclic links. # We might still enter an endless loop, since a link # loop can be closed while we follow links, # but so what? potlib=$potent_lib while test -h "$potlib" 2>/dev/null; do potliblink=`ls -ld $potlib | $SED 's/.* -> //'` case $potliblink in [\\/]* | [A-Za-z]:[\\/]*) potlib=$potliblink;; *) potlib=`$ECHO "$potlib" | $SED 's|[^/]*$||'`"$potliblink";; esac done if eval $file_magic_cmd \"\$potlib\" 2>/dev/null | $SED -e 10q | $EGREP "$file_magic_regex" > /dev/null; then func_append newdeplibs " $a_deplib" a_deplib= break 2 fi done done fi if test -n "$a_deplib"; then droppeddeps=yes echo $ECHO "*** Warning: linker path does not have real file for library $a_deplib." echo "*** I have the capability to make that library automatically link in when" echo "*** you link to this library. But I can only do this if you have a" echo "*** shared version of the library, which you do not appear to have" echo "*** because I did check the linker path looking for a file starting" if test -z "$potlib"; then $ECHO "*** with $libname but no candidates were found. (...for file magic test)" else $ECHO "*** with $libname and none of the candidates passed a file format test" $ECHO "*** using a file magic. Last file checked: $potlib" fi fi ;; *) # Add a -L argument. func_append newdeplibs " $a_deplib" ;; esac done # Gone through all deplibs. ;; match_pattern*) set dummy $deplibs_check_method; shift match_pattern_regex=`expr "$deplibs_check_method" : "$1 \(.*\)"` for a_deplib in $deplibs; do case $a_deplib in -l*) func_stripname -l '' "$a_deplib" name=$func_stripname_result if test yes = "$allow_libtool_libs_with_static_runtimes"; then case " $predeps $postdeps " in *" $a_deplib "*) func_append newdeplibs " $a_deplib" a_deplib= ;; esac fi if test -n "$a_deplib"; then libname=`eval "\\$ECHO \"$libname_spec\""` for i in $lib_search_path $sys_lib_search_path $shlib_search_path; do potential_libs=`ls $i/$libname[.-]* 2>/dev/null` for potent_lib in $potential_libs; do potlib=$potent_lib # see symlink-check above in file_magic test if eval "\$ECHO \"$potent_lib\"" 2>/dev/null | $SED 10q | \ $EGREP "$match_pattern_regex" > /dev/null; then func_append newdeplibs " $a_deplib" a_deplib= break 2 fi done done fi if test -n "$a_deplib"; then droppeddeps=yes echo $ECHO "*** Warning: linker path does not have real file for library $a_deplib." echo "*** I have the capability to make that library automatically link in when" echo "*** you link to this library. But I can only do this if you have a" echo "*** shared version of the library, which you do not appear to have" echo "*** because I did check the linker path looking for a file starting" if test -z "$potlib"; then $ECHO "*** with $libname but no candidates were found. (...for regex pattern test)" else $ECHO "*** with $libname and none of the candidates passed a file format test" $ECHO "*** using a regex pattern. Last file checked: $potlib" fi fi ;; *) # Add a -L argument. func_append newdeplibs " $a_deplib" ;; esac done # Gone through all deplibs. ;; none | unknown | *) newdeplibs= tmp_deplibs=`$ECHO " $deplibs" | $SED 's/ -lc$//; s/ -[LR][^ ]*//g'` if test yes = "$allow_libtool_libs_with_static_runtimes"; then for i in $predeps $postdeps; do # can't use Xsed below, because $i might contain '/' tmp_deplibs=`$ECHO " $tmp_deplibs" | $SED "s|$i||"` done fi case $tmp_deplibs in *[!\ \ ]*) echo if test none = "$deplibs_check_method"; then echo "*** Warning: inter-library dependencies are not supported in this platform." else echo "*** Warning: inter-library dependencies are not known to be supported." fi echo "*** All declared inter-library dependencies are being dropped." droppeddeps=yes ;; esac ;; esac versuffix=$versuffix_save major=$major_save release=$release_save libname=$libname_save name=$name_save case $host in *-*-rhapsody* | *-*-darwin1.[012]) # On Rhapsody replace the C library with the System framework newdeplibs=`$ECHO " $newdeplibs" | $SED 's/ -lc / System.ltframework /'` ;; esac if test yes = "$droppeddeps"; then if test yes = "$module"; then echo echo "*** Warning: libtool could not satisfy all declared inter-library" $ECHO "*** dependencies of module $libname. Therefore, libtool will create" echo "*** a static module, that should work as long as the dlopening" echo "*** application is linked with the -dlopen flag." if test -z "$global_symbol_pipe"; then echo echo "*** However, this would only work if libtool was able to extract symbol" echo "*** lists from a program, using 'nm' or equivalent, but libtool could" echo "*** not find such a program. So, this module is probably useless." echo "*** 'nm' from GNU binutils and a full rebuild may help." fi if test no = "$build_old_libs"; then oldlibs=$output_objdir/$libname.$libext build_libtool_libs=module build_old_libs=yes else build_libtool_libs=no fi else echo "*** The inter-library dependencies that have been dropped here will be" echo "*** automatically added whenever a program is linked with this library" echo "*** or is declared to -dlopen it." if test no = "$allow_undefined"; then echo echo "*** Since this library must not contain undefined symbols," echo "*** because either the platform does not support them or" echo "*** it was explicitly requested with -no-undefined," echo "*** libtool will only create a static version of it." if test no = "$build_old_libs"; then oldlibs=$output_objdir/$libname.$libext build_libtool_libs=module build_old_libs=yes else build_libtool_libs=no fi fi fi fi # Done checking deplibs! deplibs=$newdeplibs fi # Time to change all our "foo.ltframework" stuff back to "-framework foo" case $host in *-*-darwin*) newdeplibs=`$ECHO " $newdeplibs" | $SED 's% \([^ $]*\).ltframework% -framework \1%g'` new_inherited_linker_flags=`$ECHO " $new_inherited_linker_flags" | $SED 's% \([^ $]*\).ltframework% -framework \1%g'` deplibs=`$ECHO " $deplibs" | $SED 's% \([^ $]*\).ltframework% -framework \1%g'` ;; esac # move library search paths that coincide with paths to not yet # installed libraries to the beginning of the library search list new_libs= for path in $notinst_path; do case " $new_libs " in *" -L$path/$objdir "*) ;; *) case " $deplibs " in *" -L$path/$objdir "*) func_append new_libs " -L$path/$objdir" ;; esac ;; esac done for deplib in $deplibs; do case $deplib in -L*) case " $new_libs " in *" $deplib "*) ;; *) func_append new_libs " $deplib" ;; esac ;; *) func_append new_libs " $deplib" ;; esac done deplibs=$new_libs # All the library-specific variables (install_libdir is set above). library_names= old_library= dlname= # Test again, we may have decided not to build it any more if test yes = "$build_libtool_libs"; then # Remove $wl instances when linking with ld. # FIXME: should test the right _cmds variable. case $archive_cmds in *\$LD\ *) wl= ;; esac if test yes = "$hardcode_into_libs"; then # Hardcode the library paths hardcode_libdirs= dep_rpath= rpath=$finalize_rpath test relink = "$opt_mode" || rpath=$compile_rpath$rpath for libdir in $rpath; do if test -n "$hardcode_libdir_flag_spec"; then if test -n "$hardcode_libdir_separator"; then func_replace_sysroot "$libdir" libdir=$func_replace_sysroot_result if test -z "$hardcode_libdirs"; then hardcode_libdirs=$libdir else # Just accumulate the unique libdirs. case $hardcode_libdir_separator$hardcode_libdirs$hardcode_libdir_separator in *"$hardcode_libdir_separator$libdir$hardcode_libdir_separator"*) ;; *) func_append hardcode_libdirs "$hardcode_libdir_separator$libdir" ;; esac fi else eval flag=\"$hardcode_libdir_flag_spec\" func_append dep_rpath " $flag" fi elif test -n "$runpath_var"; then case "$perm_rpath " in *" $libdir "*) ;; *) func_append perm_rpath " $libdir" ;; esac fi done # Substitute the hardcoded libdirs into the rpath. if test -n "$hardcode_libdir_separator" && test -n "$hardcode_libdirs"; then libdir=$hardcode_libdirs eval "dep_rpath=\"$hardcode_libdir_flag_spec\"" fi if test -n "$runpath_var" && test -n "$perm_rpath"; then # We should set the runpath_var. rpath= for dir in $perm_rpath; do func_append rpath "$dir:" done eval "$runpath_var='$rpath\$$runpath_var'; export $runpath_var" fi test -n "$dep_rpath" && deplibs="$dep_rpath $deplibs" fi shlibpath=$finalize_shlibpath test relink = "$opt_mode" || shlibpath=$compile_shlibpath$shlibpath if test -n "$shlibpath"; then eval "$shlibpath_var='$shlibpath\$$shlibpath_var'; export $shlibpath_var" fi # Get the real and link names of the library. eval shared_ext=\"$shrext_cmds\" eval library_names=\"$library_names_spec\" set dummy $library_names shift realname=$1 shift if test -n "$soname_spec"; then eval soname=\"$soname_spec\" else soname=$realname fi if test -z "$dlname"; then dlname=$soname fi lib=$output_objdir/$realname linknames= for link do func_append linknames " $link" done # Use standard objects if they are pic test -z "$pic_flag" && libobjs=`$ECHO "$libobjs" | $SP2NL | $SED "$lo2o" | $NL2SP` test "X$libobjs" = "X " && libobjs= delfiles= if test -n "$export_symbols" && test -n "$include_expsyms"; then $opt_dry_run || cp "$export_symbols" "$output_objdir/$libname.uexp" export_symbols=$output_objdir/$libname.uexp func_append delfiles " $export_symbols" fi orig_export_symbols= case $host_os in cygwin* | mingw* | cegcc*) if test -n "$export_symbols" && test -z "$export_symbols_regex"; then # exporting using user supplied symfile func_dll_def_p "$export_symbols" || { # and it's NOT already a .def file. Must figure out # which of the given symbols are data symbols and tag # them as such. So, trigger use of export_symbols_cmds. # export_symbols gets reassigned inside the "prepare # the list of exported symbols" if statement, so the # include_expsyms logic still works. orig_export_symbols=$export_symbols export_symbols= always_export_symbols=yes } fi ;; esac # Prepare the list of exported symbols if test -z "$export_symbols"; then if test yes = "$always_export_symbols" || test -n "$export_symbols_regex"; then func_verbose "generating symbol list for '$libname.la'" export_symbols=$output_objdir/$libname.exp $opt_dry_run || $RM $export_symbols cmds=$export_symbols_cmds save_ifs=$IFS; IFS='~' for cmd1 in $cmds; do IFS=$save_ifs # Take the normal branch if the nm_file_list_spec branch # doesn't work or if tool conversion is not needed. case $nm_file_list_spec~$to_tool_file_cmd in *~func_convert_file_noop | *~func_convert_file_msys_to_w32 | ~*) try_normal_branch=yes eval cmd=\"$cmd1\" func_len " $cmd" len=$func_len_result ;; *) try_normal_branch=no ;; esac if test yes = "$try_normal_branch" \ && { test "$len" -lt "$max_cmd_len" \ || test "$max_cmd_len" -le -1; } then func_show_eval "$cmd" 'exit $?' skipped_export=false elif test -n "$nm_file_list_spec"; then func_basename "$output" output_la=$func_basename_result save_libobjs=$libobjs save_output=$output output=$output_objdir/$output_la.nm func_to_tool_file "$output" libobjs=$nm_file_list_spec$func_to_tool_file_result func_append delfiles " $output" func_verbose "creating $NM input file list: $output" for obj in $save_libobjs; do func_to_tool_file "$obj" $ECHO "$func_to_tool_file_result" done > "$output" eval cmd=\"$cmd1\" func_show_eval "$cmd" 'exit $?' output=$save_output libobjs=$save_libobjs skipped_export=false else # The command line is too long to execute in one step. func_verbose "using reloadable object file for export list..." skipped_export=: # Break out early, otherwise skipped_export may be # set to false by a later but shorter cmd. break fi done IFS=$save_ifs if test -n "$export_symbols_regex" && test : != "$skipped_export"; then func_show_eval '$EGREP -e "$export_symbols_regex" "$export_symbols" > "${export_symbols}T"' func_show_eval '$MV "${export_symbols}T" "$export_symbols"' fi fi fi if test -n "$export_symbols" && test -n "$include_expsyms"; then tmp_export_symbols=$export_symbols test -n "$orig_export_symbols" && tmp_export_symbols=$orig_export_symbols $opt_dry_run || eval '$ECHO "$include_expsyms" | $SP2NL >> "$tmp_export_symbols"' fi if test : != "$skipped_export" && test -n "$orig_export_symbols"; then # The given exports_symbols file has to be filtered, so filter it. func_verbose "filter symbol list for '$libname.la' to tag DATA exports" # FIXME: $output_objdir/$libname.filter potentially contains lots of # 's' commands, which not all seds can handle. GNU sed should be fine # though. Also, the filter scales superlinearly with the number of # global variables. join(1) would be nice here, but unfortunately # isn't a blessed tool. $opt_dry_run || $SED -e '/[ ,]DATA/!d;s,\(.*\)\([ \,].*\),s|^\1$|\1\2|,' < $export_symbols > $output_objdir/$libname.filter func_append delfiles " $export_symbols $output_objdir/$libname.filter" export_symbols=$output_objdir/$libname.def $opt_dry_run || $SED -f $output_objdir/$libname.filter < $orig_export_symbols > $export_symbols fi tmp_deplibs= for test_deplib in $deplibs; do case " $convenience " in *" $test_deplib "*) ;; *) func_append tmp_deplibs " $test_deplib" ;; esac done deplibs=$tmp_deplibs if test -n "$convenience"; then if test -n "$whole_archive_flag_spec" && test yes = "$compiler_needs_object" && test -z "$libobjs"; then # extract the archives, so we have objects to list. # TODO: could optimize this to just extract one archive. whole_archive_flag_spec= fi if test -n "$whole_archive_flag_spec"; then save_libobjs=$libobjs eval libobjs=\"\$libobjs $whole_archive_flag_spec\" test "X$libobjs" = "X " && libobjs= else gentop=$output_objdir/${outputname}x func_append generated " $gentop" func_extract_archives $gentop $convenience func_append libobjs " $func_extract_archives_result" test "X$libobjs" = "X " && libobjs= fi fi if test yes = "$thread_safe" && test -n "$thread_safe_flag_spec"; then eval flag=\"$thread_safe_flag_spec\" func_append linker_flags " $flag" fi # Make a backup of the uninstalled library when relinking if test relink = "$opt_mode"; then $opt_dry_run || eval '(cd $output_objdir && $RM ${realname}U && $MV $realname ${realname}U)' || exit $? fi # Do each of the archive commands. if test yes = "$module" && test -n "$module_cmds"; then if test -n "$export_symbols" && test -n "$module_expsym_cmds"; then eval test_cmds=\"$module_expsym_cmds\" cmds=$module_expsym_cmds else eval test_cmds=\"$module_cmds\" cmds=$module_cmds fi else if test -n "$export_symbols" && test -n "$archive_expsym_cmds"; then eval test_cmds=\"$archive_expsym_cmds\" cmds=$archive_expsym_cmds else eval test_cmds=\"$archive_cmds\" cmds=$archive_cmds fi fi if test : != "$skipped_export" && func_len " $test_cmds" && len=$func_len_result && test "$len" -lt "$max_cmd_len" || test "$max_cmd_len" -le -1; then : else # The command line is too long to link in one step, link piecewise # or, if using GNU ld and skipped_export is not :, use a linker # script. # Save the value of $output and $libobjs because we want to # use them later. If we have whole_archive_flag_spec, we # want to use save_libobjs as it was before # whole_archive_flag_spec was expanded, because we can't # assume the linker understands whole_archive_flag_spec. # This may have to be revisited, in case too many # convenience libraries get linked in and end up exceeding # the spec. if test -z "$convenience" || test -z "$whole_archive_flag_spec"; then save_libobjs=$libobjs fi save_output=$output func_basename "$output" output_la=$func_basename_result # Clear the reloadable object creation command queue and # initialize k to one. test_cmds= concat_cmds= objlist= last_robj= k=1 if test -n "$save_libobjs" && test : != "$skipped_export" && test yes = "$with_gnu_ld"; then output=$output_objdir/$output_la.lnkscript func_verbose "creating GNU ld script: $output" echo 'INPUT (' > $output for obj in $save_libobjs do func_to_tool_file "$obj" $ECHO "$func_to_tool_file_result" >> $output done echo ')' >> $output func_append delfiles " $output" func_to_tool_file "$output" output=$func_to_tool_file_result elif test -n "$save_libobjs" && test : != "$skipped_export" && test -n "$file_list_spec"; then output=$output_objdir/$output_la.lnk func_verbose "creating linker input file list: $output" : > $output set x $save_libobjs shift firstobj= if test yes = "$compiler_needs_object"; then firstobj="$1 " shift fi for obj do func_to_tool_file "$obj" $ECHO "$func_to_tool_file_result" >> $output done func_append delfiles " $output" func_to_tool_file "$output" output=$firstobj\"$file_list_spec$func_to_tool_file_result\" else if test -n "$save_libobjs"; then func_verbose "creating reloadable object files..." output=$output_objdir/$output_la-$k.$objext eval test_cmds=\"$reload_cmds\" func_len " $test_cmds" len0=$func_len_result len=$len0 # Loop over the list of objects to be linked. for obj in $save_libobjs do func_len " $obj" func_arith $len + $func_len_result len=$func_arith_result if test -z "$objlist" || test "$len" -lt "$max_cmd_len"; then func_append objlist " $obj" else # The command $test_cmds is almost too long, add a # command to the queue. if test 1 -eq "$k"; then # The first file doesn't have a previous command to add. reload_objs=$objlist eval concat_cmds=\"$reload_cmds\" else # All subsequent reloadable object files will link in # the last one created. reload_objs="$objlist $last_robj" eval concat_cmds=\"\$concat_cmds~$reload_cmds~\$RM $last_robj\" fi last_robj=$output_objdir/$output_la-$k.$objext func_arith $k + 1 k=$func_arith_result output=$output_objdir/$output_la-$k.$objext objlist=" $obj" func_len " $last_robj" func_arith $len0 + $func_len_result len=$func_arith_result fi done # Handle the remaining objects by creating one last # reloadable object file. All subsequent reloadable object # files will link in the last one created. test -z "$concat_cmds" || concat_cmds=$concat_cmds~ reload_objs="$objlist $last_robj" eval concat_cmds=\"\$concat_cmds$reload_cmds\" if test -n "$last_robj"; then eval concat_cmds=\"\$concat_cmds~\$RM $last_robj\" fi func_append delfiles " $output" else output= fi ${skipped_export-false} && { func_verbose "generating symbol list for '$libname.la'" export_symbols=$output_objdir/$libname.exp $opt_dry_run || $RM $export_symbols libobjs=$output # Append the command to create the export file. test -z "$concat_cmds" || concat_cmds=$concat_cmds~ eval concat_cmds=\"\$concat_cmds$export_symbols_cmds\" if test -n "$last_robj"; then eval concat_cmds=\"\$concat_cmds~\$RM $last_robj\" fi } test -n "$save_libobjs" && func_verbose "creating a temporary reloadable object file: $output" # Loop through the commands generated above and execute them. save_ifs=$IFS; IFS='~' for cmd in $concat_cmds; do IFS=$save_ifs $opt_quiet || { func_quote_arg expand,pretty "$cmd" eval "func_echo $func_quote_arg_result" } $opt_dry_run || eval "$cmd" || { lt_exit=$? # Restore the uninstalled library and exit if test relink = "$opt_mode"; then ( cd "$output_objdir" && \ $RM "${realname}T" && \ $MV "${realname}U" "$realname" ) fi exit $lt_exit } done IFS=$save_ifs if test -n "$export_symbols_regex" && ${skipped_export-false}; then func_show_eval '$EGREP -e "$export_symbols_regex" "$export_symbols" > "${export_symbols}T"' func_show_eval '$MV "${export_symbols}T" "$export_symbols"' fi fi ${skipped_export-false} && { if test -n "$export_symbols" && test -n "$include_expsyms"; then tmp_export_symbols=$export_symbols test -n "$orig_export_symbols" && tmp_export_symbols=$orig_export_symbols $opt_dry_run || eval '$ECHO "$include_expsyms" | $SP2NL >> "$tmp_export_symbols"' fi if test -n "$orig_export_symbols"; then # The given exports_symbols file has to be filtered, so filter it. func_verbose "filter symbol list for '$libname.la' to tag DATA exports" # FIXME: $output_objdir/$libname.filter potentially contains lots of # 's' commands, which not all seds can handle. GNU sed should be fine # though. Also, the filter scales superlinearly with the number of # global variables. join(1) would be nice here, but unfortunately # isn't a blessed tool. $opt_dry_run || $SED -e '/[ ,]DATA/!d;s,\(.*\)\([ \,].*\),s|^\1$|\1\2|,' < $export_symbols > $output_objdir/$libname.filter func_append delfiles " $export_symbols $output_objdir/$libname.filter" export_symbols=$output_objdir/$libname.def $opt_dry_run || $SED -f $output_objdir/$libname.filter < $orig_export_symbols > $export_symbols fi } libobjs=$output # Restore the value of output. output=$save_output if test -n "$convenience" && test -n "$whole_archive_flag_spec"; then eval libobjs=\"\$libobjs $whole_archive_flag_spec\" test "X$libobjs" = "X " && libobjs= fi # Expand the library linking commands again to reset the # value of $libobjs for piecewise linking. # Do each of the archive commands. if test yes = "$module" && test -n "$module_cmds"; then if test -n "$export_symbols" && test -n "$module_expsym_cmds"; then cmds=$module_expsym_cmds else cmds=$module_cmds fi else if test -n "$export_symbols" && test -n "$archive_expsym_cmds"; then cmds=$archive_expsym_cmds else cmds=$archive_cmds fi fi fi if test -n "$delfiles"; then # Append the command to remove temporary files to $cmds. eval cmds=\"\$cmds~\$RM $delfiles\" fi # Add any objects from preloaded convenience libraries if test -n "$dlprefiles"; then gentop=$output_objdir/${outputname}x func_append generated " $gentop" func_extract_archives $gentop $dlprefiles func_append libobjs " $func_extract_archives_result" test "X$libobjs" = "X " && libobjs= fi save_ifs=$IFS; IFS='~' for cmd in $cmds; do IFS=$sp$nl eval cmd=\"$cmd\" IFS=$save_ifs $opt_quiet || { func_quote_arg expand,pretty "$cmd" eval "func_echo $func_quote_arg_result" } $opt_dry_run || eval "$cmd" || { lt_exit=$? # Restore the uninstalled library and exit if test relink = "$opt_mode"; then ( cd "$output_objdir" && \ $RM "${realname}T" && \ $MV "${realname}U" "$realname" ) fi exit $lt_exit } done IFS=$save_ifs # Restore the uninstalled library and exit if test relink = "$opt_mode"; then $opt_dry_run || eval '(cd $output_objdir && $RM ${realname}T && $MV $realname ${realname}T && $MV ${realname}U $realname)' || exit $? if test -n "$convenience"; then if test -z "$whole_archive_flag_spec"; then func_show_eval '${RM}r "$gentop"' fi fi exit $EXIT_SUCCESS fi # Create links to the real library. for linkname in $linknames; do if test "$realname" != "$linkname"; then func_show_eval '(cd "$output_objdir" && $RM "$linkname" && $LN_S "$realname" "$linkname")' 'exit $?' fi done # If -module or -export-dynamic was specified, set the dlname. if test yes = "$module" || test yes = "$export_dynamic"; then # On all known operating systems, these are identical. dlname=$soname fi fi ;; obj) if test -n "$dlfiles$dlprefiles" || test no != "$dlself"; then func_warning "'-dlopen' is ignored for objects" fi case " $deplibs" in *\ -l* | *\ -L*) func_warning "'-l' and '-L' are ignored for objects" ;; esac test -n "$rpath" && \ func_warning "'-rpath' is ignored for objects" test -n "$xrpath" && \ func_warning "'-R' is ignored for objects" test -n "$vinfo" && \ func_warning "'-version-info' is ignored for objects" test -n "$release" && \ func_warning "'-release' is ignored for objects" case $output in *.lo) test -n "$objs$old_deplibs" && \ func_fatal_error "cannot build library object '$output' from non-libtool objects" libobj=$output func_lo2o "$libobj" obj=$func_lo2o_result ;; *) libobj= obj=$output ;; esac # Delete the old objects. $opt_dry_run || $RM $obj $libobj # Objects from convenience libraries. This assumes # single-version convenience libraries. Whenever we create # different ones for PIC/non-PIC, this we'll have to duplicate # the extraction. reload_conv_objs= gentop= # if reload_cmds runs $LD directly, get rid of -Wl from # whole_archive_flag_spec and hope we can get by with turning comma # into space. case $reload_cmds in *\$LD[\ \$]*) wl= ;; esac if test -n "$convenience"; then if test -n "$whole_archive_flag_spec"; then eval tmp_whole_archive_flags=\"$whole_archive_flag_spec\" test -n "$wl" || tmp_whole_archive_flags=`$ECHO "$tmp_whole_archive_flags" | $SED 's|,| |g'` reload_conv_objs=$reload_objs\ $tmp_whole_archive_flags else gentop=$output_objdir/${obj}x func_append generated " $gentop" func_extract_archives $gentop $convenience reload_conv_objs="$reload_objs $func_extract_archives_result" fi fi # If we're not building shared, we need to use non_pic_objs test yes = "$build_libtool_libs" || libobjs=$non_pic_objects # Create the old-style object. reload_objs=$objs$old_deplibs' '`$ECHO "$libobjs" | $SP2NL | $SED "/\.$libext$/d; /\.lib$/d; $lo2o" | $NL2SP`' '$reload_conv_objs output=$obj func_execute_cmds "$reload_cmds" 'exit $?' # Exit if we aren't doing a library object file. if test -z "$libobj"; then if test -n "$gentop"; then func_show_eval '${RM}r "$gentop"' fi exit $EXIT_SUCCESS fi test yes = "$build_libtool_libs" || { if test -n "$gentop"; then func_show_eval '${RM}r "$gentop"' fi # Create an invalid libtool object if no PIC, so that we don't # accidentally link it into a program. # $show "echo timestamp > $libobj" # $opt_dry_run || eval "echo timestamp > $libobj" || exit $? exit $EXIT_SUCCESS } if test -n "$pic_flag" || test default != "$pic_mode"; then # Only do commands if we really have different PIC objects. reload_objs="$libobjs $reload_conv_objs" output=$libobj func_execute_cmds "$reload_cmds" 'exit $?' fi if test -n "$gentop"; then func_show_eval '${RM}r "$gentop"' fi exit $EXIT_SUCCESS ;; prog) case $host in *cygwin*) func_stripname '' '.exe' "$output" output=$func_stripname_result.exe;; esac test -n "$vinfo" && \ func_warning "'-version-info' is ignored for programs" test -n "$release" && \ func_warning "'-release' is ignored for programs" $preload \ && test unknown,unknown,unknown = "$dlopen_support,$dlopen_self,$dlopen_self_static" \ && func_warning "'LT_INIT([dlopen])' not used. Assuming no dlopen support." case $host in *-*-rhapsody* | *-*-darwin1.[012]) # On Rhapsody replace the C library is the System framework compile_deplibs=`$ECHO " $compile_deplibs" | $SED 's/ -lc / System.ltframework /'` finalize_deplibs=`$ECHO " $finalize_deplibs" | $SED 's/ -lc / System.ltframework /'` ;; esac case $host in *-*-darwin*) # Don't allow lazy linking, it breaks C++ global constructors # But is supposedly fixed on 10.4 or later (yay!). if test CXX = "$tagname"; then case ${MACOSX_DEPLOYMENT_TARGET-10.0} in 10.[0123]) func_append compile_command " $wl-bind_at_load" func_append finalize_command " $wl-bind_at_load" ;; esac fi # Time to change all our "foo.ltframework" stuff back to "-framework foo" compile_deplibs=`$ECHO " $compile_deplibs" | $SED 's% \([^ $]*\).ltframework% -framework \1%g'` finalize_deplibs=`$ECHO " $finalize_deplibs" | $SED 's% \([^ $]*\).ltframework% -framework \1%g'` ;; esac # move library search paths that coincide with paths to not yet # installed libraries to the beginning of the library search list new_libs= for path in $notinst_path; do case " $new_libs " in *" -L$path/$objdir "*) ;; *) case " $compile_deplibs " in *" -L$path/$objdir "*) func_append new_libs " -L$path/$objdir" ;; esac ;; esac done for deplib in $compile_deplibs; do case $deplib in -L*) case " $new_libs " in *" $deplib "*) ;; *) func_append new_libs " $deplib" ;; esac ;; *) func_append new_libs " $deplib" ;; esac done compile_deplibs=$new_libs func_append compile_command " $compile_deplibs" func_append finalize_command " $finalize_deplibs" if test -n "$rpath$xrpath"; then # If the user specified any rpath flags, then add them. for libdir in $rpath $xrpath; do # This is the magic to use -rpath. case "$finalize_rpath " in *" $libdir "*) ;; *) func_append finalize_rpath " $libdir" ;; esac done fi # Now hardcode the library paths rpath= hardcode_libdirs= for libdir in $compile_rpath $finalize_rpath; do if test -n "$hardcode_libdir_flag_spec"; then if test -n "$hardcode_libdir_separator"; then if test -z "$hardcode_libdirs"; then hardcode_libdirs=$libdir else # Just accumulate the unique libdirs. case $hardcode_libdir_separator$hardcode_libdirs$hardcode_libdir_separator in *"$hardcode_libdir_separator$libdir$hardcode_libdir_separator"*) ;; *) func_append hardcode_libdirs "$hardcode_libdir_separator$libdir" ;; esac fi else eval flag=\"$hardcode_libdir_flag_spec\" func_append rpath " $flag" fi elif test -n "$runpath_var"; then case "$perm_rpath " in *" $libdir "*) ;; *) func_append perm_rpath " $libdir" ;; esac fi case $host in *-*-cygwin* | *-*-mingw* | *-*-pw32* | *-*-os2* | *-cegcc*) testbindir=`$ECHO "$libdir" | $SED -e 's*/lib$*/bin*'` case :$dllsearchpath: in *":$libdir:"*) ;; ::) dllsearchpath=$libdir;; *) func_append dllsearchpath ":$libdir";; esac case :$dllsearchpath: in *":$testbindir:"*) ;; ::) dllsearchpath=$testbindir;; *) func_append dllsearchpath ":$testbindir";; esac ;; esac done # Substitute the hardcoded libdirs into the rpath. if test -n "$hardcode_libdir_separator" && test -n "$hardcode_libdirs"; then libdir=$hardcode_libdirs eval rpath=\" $hardcode_libdir_flag_spec\" fi compile_rpath=$rpath rpath= hardcode_libdirs= for libdir in $finalize_rpath; do if test -n "$hardcode_libdir_flag_spec"; then if test -n "$hardcode_libdir_separator"; then if test -z "$hardcode_libdirs"; then hardcode_libdirs=$libdir else # Just accumulate the unique libdirs. case $hardcode_libdir_separator$hardcode_libdirs$hardcode_libdir_separator in *"$hardcode_libdir_separator$libdir$hardcode_libdir_separator"*) ;; *) func_append hardcode_libdirs "$hardcode_libdir_separator$libdir" ;; esac fi else eval flag=\"$hardcode_libdir_flag_spec\" func_append rpath " $flag" fi elif test -n "$runpath_var"; then case "$finalize_perm_rpath " in *" $libdir "*) ;; *) func_append finalize_perm_rpath " $libdir" ;; esac fi done # Substitute the hardcoded libdirs into the rpath. if test -n "$hardcode_libdir_separator" && test -n "$hardcode_libdirs"; then libdir=$hardcode_libdirs eval rpath=\" $hardcode_libdir_flag_spec\" fi finalize_rpath=$rpath if test -n "$libobjs" && test yes = "$build_old_libs"; then # Transform all the library objects into standard objects. compile_command=`$ECHO "$compile_command" | $SP2NL | $SED "$lo2o" | $NL2SP` finalize_command=`$ECHO "$finalize_command" | $SP2NL | $SED "$lo2o" | $NL2SP` fi func_generate_dlsyms "$outputname" "@PROGRAM@" false # template prelinking step if test -n "$prelink_cmds"; then func_execute_cmds "$prelink_cmds" 'exit $?' fi wrappers_required=: case $host in *cegcc* | *mingw32ce*) # Disable wrappers for cegcc and mingw32ce hosts, we are cross compiling anyway. wrappers_required=false ;; *cygwin* | *mingw* ) test yes = "$build_libtool_libs" || wrappers_required=false ;; *) if test no = "$need_relink" || test yes != "$build_libtool_libs"; then wrappers_required=false fi ;; esac $wrappers_required || { # Replace the output file specification. compile_command=`$ECHO "$compile_command" | $SED 's%@OUTPUT@%'"$output"'%g'` link_command=$compile_command$compile_rpath # We have no uninstalled library dependencies, so finalize right now. exit_status=0 func_show_eval "$link_command" 'exit_status=$?' if test -n "$postlink_cmds"; then func_to_tool_file "$output" postlink_cmds=`func_echo_all "$postlink_cmds" | $SED -e 's%@OUTPUT@%'"$output"'%g' -e 's%@TOOL_OUTPUT@%'"$func_to_tool_file_result"'%g'` func_execute_cmds "$postlink_cmds" 'exit $?' fi # Delete the generated files. if test -f "$output_objdir/${outputname}S.$objext"; then func_show_eval '$RM "$output_objdir/${outputname}S.$objext"' fi exit $exit_status } if test -n "$compile_shlibpath$finalize_shlibpath"; then compile_command="$shlibpath_var=\"$compile_shlibpath$finalize_shlibpath\$$shlibpath_var\" $compile_command" fi if test -n "$finalize_shlibpath"; then finalize_command="$shlibpath_var=\"$finalize_shlibpath\$$shlibpath_var\" $finalize_command" fi compile_var= finalize_var= if test -n "$runpath_var"; then if test -n "$perm_rpath"; then # We should set the runpath_var. rpath= for dir in $perm_rpath; do func_append rpath "$dir:" done compile_var="$runpath_var=\"$rpath\$$runpath_var\" " fi if test -n "$finalize_perm_rpath"; then # We should set the runpath_var. rpath= for dir in $finalize_perm_rpath; do func_append rpath "$dir:" done finalize_var="$runpath_var=\"$rpath\$$runpath_var\" " fi fi if test yes = "$no_install"; then # We don't need to create a wrapper script. link_command=$compile_var$compile_command$compile_rpath # Replace the output file specification. link_command=`$ECHO "$link_command" | $SED 's%@OUTPUT@%'"$output"'%g'` # Delete the old output file. $opt_dry_run || $RM $output # Link the executable and exit func_show_eval "$link_command" 'exit $?' if test -n "$postlink_cmds"; then func_to_tool_file "$output" postlink_cmds=`func_echo_all "$postlink_cmds" | $SED -e 's%@OUTPUT@%'"$output"'%g' -e 's%@TOOL_OUTPUT@%'"$func_to_tool_file_result"'%g'` func_execute_cmds "$postlink_cmds" 'exit $?' fi exit $EXIT_SUCCESS fi case $hardcode_action,$fast_install in relink,*) # Fast installation is not supported link_command=$compile_var$compile_command$compile_rpath relink_command=$finalize_var$finalize_command$finalize_rpath func_warning "this platform does not like uninstalled shared libraries" func_warning "'$output' will be relinked during installation" ;; *,yes) link_command=$finalize_var$compile_command$finalize_rpath relink_command=`$ECHO "$compile_var$compile_command$compile_rpath" | $SED 's%@OUTPUT@%\$progdir/\$file%g'` ;; *,no) link_command=$compile_var$compile_command$compile_rpath relink_command=$finalize_var$finalize_command$finalize_rpath ;; *,needless) link_command=$finalize_var$compile_command$finalize_rpath relink_command= ;; esac # Replace the output file specification. link_command=`$ECHO "$link_command" | $SED 's%@OUTPUT@%'"$output_objdir/$outputname"'%g'` # Delete the old output files. $opt_dry_run || $RM $output $output_objdir/$outputname $output_objdir/lt-$outputname func_show_eval "$link_command" 'exit $?' if test -n "$postlink_cmds"; then func_to_tool_file "$output_objdir/$outputname" postlink_cmds=`func_echo_all "$postlink_cmds" | $SED -e 's%@OUTPUT@%'"$output_objdir/$outputname"'%g' -e 's%@TOOL_OUTPUT@%'"$func_to_tool_file_result"'%g'` func_execute_cmds "$postlink_cmds" 'exit $?' fi # Now create the wrapper script. func_verbose "creating $output" # Quote the relink command for shipping. if test -n "$relink_command"; then # Preserve any variables that may affect compiler behavior for var in $variables_saved_for_relink; do if eval test -z \"\${$var+set}\"; then relink_command="{ test -z \"\${$var+set}\" || $lt_unset $var || { $var=; export $var; }; }; $relink_command" elif eval var_value=\$$var; test -z "$var_value"; then relink_command="$var=; export $var; $relink_command" else func_quote_arg pretty "$var_value" relink_command="$var=$func_quote_arg_result; export $var; $relink_command" fi done func_quote eval cd "`pwd`" func_quote_arg pretty,unquoted "($func_quote_result; $relink_command)" relink_command=$func_quote_arg_unquoted_result fi # Only actually do things if not in dry run mode. $opt_dry_run || { # win32 will think the script is a binary if it has # a .exe suffix, so we strip it off here. case $output in *.exe) func_stripname '' '.exe' "$output" output=$func_stripname_result ;; esac # test for cygwin because mv fails w/o .exe extensions case $host in *cygwin*) exeext=.exe func_stripname '' '.exe' "$outputname" outputname=$func_stripname_result ;; *) exeext= ;; esac case $host in *cygwin* | *mingw* ) func_dirname_and_basename "$output" "" "." output_name=$func_basename_result output_path=$func_dirname_result cwrappersource=$output_path/$objdir/lt-$output_name.c cwrapper=$output_path/$output_name.exe $RM $cwrappersource $cwrapper trap "$RM $cwrappersource $cwrapper; exit $EXIT_FAILURE" 1 2 15 func_emit_cwrapperexe_src > $cwrappersource # The wrapper executable is built using the $host compiler, # because it contains $host paths and files. If cross- # compiling, it, like the target executable, must be # executed on the $host or under an emulation environment. $opt_dry_run || { $LTCC $LTCFLAGS -o $cwrapper $cwrappersource $STRIP $cwrapper } # Now, create the wrapper script for func_source use: func_ltwrapper_scriptname $cwrapper $RM $func_ltwrapper_scriptname_result trap "$RM $func_ltwrapper_scriptname_result; exit $EXIT_FAILURE" 1 2 15 $opt_dry_run || { # note: this script will not be executed, so do not chmod. if test "x$build" = "x$host"; then $cwrapper --lt-dump-script > $func_ltwrapper_scriptname_result else func_emit_wrapper no > $func_ltwrapper_scriptname_result fi } ;; * ) $RM $output trap "$RM $output; exit $EXIT_FAILURE" 1 2 15 func_emit_wrapper no > $output chmod +x $output ;; esac } exit $EXIT_SUCCESS ;; esac # See if we need to build an old-fashioned archive. for oldlib in $oldlibs; do case $build_libtool_libs in convenience) oldobjs="$libobjs_save $symfileobj" addlibs=$convenience build_libtool_libs=no ;; module) oldobjs=$libobjs_save addlibs=$old_convenience build_libtool_libs=no ;; *) oldobjs="$old_deplibs $non_pic_objects" $preload && test -f "$symfileobj" \ && func_append oldobjs " $symfileobj" addlibs=$old_convenience ;; esac if test -n "$addlibs"; then gentop=$output_objdir/${outputname}x func_append generated " $gentop" func_extract_archives $gentop $addlibs func_append oldobjs " $func_extract_archives_result" fi # Do each command in the archive commands. if test -n "$old_archive_from_new_cmds" && test yes = "$build_libtool_libs"; then cmds=$old_archive_from_new_cmds else # Add any objects from preloaded convenience libraries if test -n "$dlprefiles"; then gentop=$output_objdir/${outputname}x func_append generated " $gentop" func_extract_archives $gentop $dlprefiles func_append oldobjs " $func_extract_archives_result" fi # POSIX demands no paths to be encoded in archives. We have # to avoid creating archives with duplicate basenames if we # might have to extract them afterwards, e.g., when creating a # static archive out of a convenience library, or when linking # the entirety of a libtool archive into another (currently # not supported by libtool). if (for obj in $oldobjs do func_basename "$obj" $ECHO "$func_basename_result" done | sort | sort -uc >/dev/null 2>&1); then : else echo "copying selected object files to avoid basename conflicts..." gentop=$output_objdir/${outputname}x func_append generated " $gentop" func_mkdir_p "$gentop" save_oldobjs=$oldobjs oldobjs= counter=1 for obj in $save_oldobjs do func_basename "$obj" objbase=$func_basename_result case " $oldobjs " in " ") oldobjs=$obj ;; *[\ /]"$objbase "*) while :; do # Make sure we don't pick an alternate name that also # overlaps. newobj=lt$counter-$objbase func_arith $counter + 1 counter=$func_arith_result case " $oldobjs " in *[\ /]"$newobj "*) ;; *) if test ! -f "$gentop/$newobj"; then break; fi ;; esac done func_show_eval "ln $obj $gentop/$newobj || cp $obj $gentop/$newobj" func_append oldobjs " $gentop/$newobj" ;; *) func_append oldobjs " $obj" ;; esac done fi func_to_tool_file "$oldlib" func_convert_file_msys_to_w32 tool_oldlib=$func_to_tool_file_result eval cmds=\"$old_archive_cmds\" func_len " $cmds" len=$func_len_result if test "$len" -lt "$max_cmd_len" || test "$max_cmd_len" -le -1; then cmds=$old_archive_cmds elif test -n "$archiver_list_spec"; then func_verbose "using command file archive linking..." for obj in $oldobjs do func_to_tool_file "$obj" $ECHO "$func_to_tool_file_result" done > $output_objdir/$libname.libcmd func_to_tool_file "$output_objdir/$libname.libcmd" oldobjs=" $archiver_list_spec$func_to_tool_file_result" cmds=$old_archive_cmds else # the command line is too long to link in one step, link in parts func_verbose "using piecewise archive linking..." save_RANLIB=$RANLIB RANLIB=: objlist= concat_cmds= save_oldobjs=$oldobjs oldobjs= # Is there a better way of finding the last object in the list? for obj in $save_oldobjs do last_oldobj=$obj done eval test_cmds=\"$old_archive_cmds\" func_len " $test_cmds" len0=$func_len_result len=$len0 for obj in $save_oldobjs do func_len " $obj" func_arith $len + $func_len_result len=$func_arith_result func_append objlist " $obj" if test "$len" -lt "$max_cmd_len"; then : else # the above command should be used before it gets too long oldobjs=$objlist if test "$obj" = "$last_oldobj"; then RANLIB=$save_RANLIB fi test -z "$concat_cmds" || concat_cmds=$concat_cmds~ eval concat_cmds=\"\$concat_cmds$old_archive_cmds\" objlist= len=$len0 fi done RANLIB=$save_RANLIB oldobjs=$objlist if test -z "$oldobjs"; then eval cmds=\"\$concat_cmds\" else eval cmds=\"\$concat_cmds~\$old_archive_cmds\" fi fi fi func_execute_cmds "$cmds" 'exit $?' done test -n "$generated" && \ func_show_eval "${RM}r$generated" # Now create the libtool archive. case $output in *.la) old_library= test yes = "$build_old_libs" && old_library=$libname.$libext func_verbose "creating $output" # Preserve any variables that may affect compiler behavior for var in $variables_saved_for_relink; do if eval test -z \"\${$var+set}\"; then relink_command="{ test -z \"\${$var+set}\" || $lt_unset $var || { $var=; export $var; }; }; $relink_command" elif eval var_value=\$$var; test -z "$var_value"; then relink_command="$var=; export $var; $relink_command" else func_quote_arg pretty,unquoted "$var_value" relink_command="$var=$func_quote_arg_unquoted_result; export $var; $relink_command" fi done # Quote the link command for shipping. func_quote eval cd "`pwd`" relink_command="($func_quote_result; $SHELL \"$progpath\" $preserve_args --mode=relink $libtool_args @inst_prefix_dir@)" func_quote_arg pretty,unquoted "$relink_command" relink_command=$func_quote_arg_unquoted_result if test yes = "$hardcode_automatic"; then relink_command= fi # Only create the output if not a dry run. $opt_dry_run || { for installed in no yes; do if test yes = "$installed"; then if test -z "$install_libdir"; then break fi output=$output_objdir/${outputname}i # Replace all uninstalled libtool libraries with the installed ones newdependency_libs= for deplib in $dependency_libs; do case $deplib in *.la) func_basename "$deplib" name=$func_basename_result func_resolve_sysroot "$deplib" eval libdir=`$SED -n -e 's/^libdir=\(.*\)$/\1/p' $func_resolve_sysroot_result` test -z "$libdir" && \ func_fatal_error "'$deplib' is not a valid libtool archive" func_append newdependency_libs " ${lt_sysroot:+=}$libdir/$name" ;; -L*) func_stripname -L '' "$deplib" func_replace_sysroot "$func_stripname_result" func_append newdependency_libs " -L$func_replace_sysroot_result" ;; -R*) func_stripname -R '' "$deplib" func_replace_sysroot "$func_stripname_result" func_append newdependency_libs " -R$func_replace_sysroot_result" ;; *) func_append newdependency_libs " $deplib" ;; esac done dependency_libs=$newdependency_libs newdlfiles= for lib in $dlfiles; do case $lib in *.la) func_basename "$lib" name=$func_basename_result eval libdir=`$SED -n -e 's/^libdir=\(.*\)$/\1/p' $lib` test -z "$libdir" && \ func_fatal_error "'$lib' is not a valid libtool archive" func_append newdlfiles " ${lt_sysroot:+=}$libdir/$name" ;; *) func_append newdlfiles " $lib" ;; esac done dlfiles=$newdlfiles newdlprefiles= for lib in $dlprefiles; do case $lib in *.la) # Only pass preopened files to the pseudo-archive (for # eventual linking with the app. that links it) if we # didn't already link the preopened objects directly into # the library: func_basename "$lib" name=$func_basename_result eval libdir=`$SED -n -e 's/^libdir=\(.*\)$/\1/p' $lib` test -z "$libdir" && \ func_fatal_error "'$lib' is not a valid libtool archive" func_append newdlprefiles " ${lt_sysroot:+=}$libdir/$name" ;; esac done dlprefiles=$newdlprefiles else newdlfiles= for lib in $dlfiles; do case $lib in [\\/]* | [A-Za-z]:[\\/]*) abs=$lib ;; *) abs=`pwd`"/$lib" ;; esac func_append newdlfiles " $abs" done dlfiles=$newdlfiles newdlprefiles= for lib in $dlprefiles; do case $lib in [\\/]* | [A-Za-z]:[\\/]*) abs=$lib ;; *) abs=`pwd`"/$lib" ;; esac func_append newdlprefiles " $abs" done dlprefiles=$newdlprefiles fi $RM $output # place dlname in correct position for cygwin # In fact, it would be nice if we could use this code for all target # systems that can't hard-code library paths into their executables # and that have no shared library path variable independent of PATH, # but it turns out we can't easily determine that from inspecting # libtool variables, so we have to hard-code the OSs to which it # applies here; at the moment, that means platforms that use the PE # object format with DLL files. See the long comment at the top of # tests/bindir.at for full details. tdlname=$dlname case $host,$output,$installed,$module,$dlname in *cygwin*,*lai,yes,no,*.dll | *mingw*,*lai,yes,no,*.dll | *cegcc*,*lai,yes,no,*.dll) # If a -bindir argument was supplied, place the dll there. if test -n "$bindir"; then func_relative_path "$install_libdir" "$bindir" tdlname=$func_relative_path_result/$dlname else # Otherwise fall back on heuristic. tdlname=../bin/$dlname fi ;; esac $ECHO > $output "\ # $outputname - a libtool library file # Generated by $PROGRAM (GNU $PACKAGE) $VERSION # # Please DO NOT delete this file! # It is necessary for linking the library. # The name that we can dlopen(3). dlname='$tdlname' # Names of this library. library_names='$library_names' # The name of the static archive. old_library='$old_library' # Linker flags that cannot go in dependency_libs. inherited_linker_flags='$new_inherited_linker_flags' # Libraries that this one depends upon. dependency_libs='$dependency_libs' # Names of additional weak libraries provided by this library weak_library_names='$weak_libs' # Version information for $libname. current=$current age=$age revision=$revision # Is this an already installed library? installed=$installed # Should we warn about portability when linking against -modules? shouldnotlink=$module # Files to dlopen/dlpreopen dlopen='$dlfiles' dlpreopen='$dlprefiles' # Directory that this library needs to be installed in: libdir='$install_libdir'" if test no,yes = "$installed,$need_relink"; then $ECHO >> $output "\ relink_command=\"$relink_command\"" fi done } # Do a symbolic link so that the libtool archive can be found in # LD_LIBRARY_PATH before the program is installed. func_show_eval '( cd "$output_objdir" && $RM "$outputname" && $LN_S "../$outputname" "$outputname" )' 'exit $?' ;; esac exit $EXIT_SUCCESS } if test link = "$opt_mode" || test relink = "$opt_mode"; then func_mode_link ${1+"$@"} fi # func_mode_uninstall arg... func_mode_uninstall () { $debug_cmd RM=$nonopt files= rmforce=false exit_status=0 # This variable tells wrapper scripts just to set variables rather # than running their programs. libtool_install_magic=$magic for arg do case $arg in -f) func_append RM " $arg"; rmforce=: ;; -*) func_append RM " $arg" ;; *) func_append files " $arg" ;; esac done test -z "$RM" && \ func_fatal_help "you must specify an RM program" rmdirs= for file in $files; do func_dirname "$file" "" "." dir=$func_dirname_result if test . = "$dir"; then odir=$objdir else odir=$dir/$objdir fi func_basename "$file" name=$func_basename_result test uninstall = "$opt_mode" && odir=$dir # Remember odir for removal later, being careful to avoid duplicates if test clean = "$opt_mode"; then case " $rmdirs " in *" $odir "*) ;; *) func_append rmdirs " $odir" ;; esac fi # Don't error if the file doesn't exist and rm -f was used. if { test -L "$file"; } >/dev/null 2>&1 || { test -h "$file"; } >/dev/null 2>&1 || test -f "$file"; then : elif test -d "$file"; then exit_status=1 continue elif $rmforce; then continue fi rmfiles=$file case $name in *.la) # Possibly a libtool archive, so verify it. if func_lalib_p "$file"; then func_source $dir/$name # Delete the libtool libraries and symlinks. for n in $library_names; do func_append rmfiles " $odir/$n" done test -n "$old_library" && func_append rmfiles " $odir/$old_library" case $opt_mode in clean) case " $library_names " in *" $dlname "*) ;; *) test -n "$dlname" && func_append rmfiles " $odir/$dlname" ;; esac test -n "$libdir" && func_append rmfiles " $odir/$name $odir/${name}i" ;; uninstall) if test -n "$library_names"; then # Do each command in the postuninstall commands. func_execute_cmds "$postuninstall_cmds" '$rmforce || exit_status=1' fi if test -n "$old_library"; then # Do each command in the old_postuninstall commands. func_execute_cmds "$old_postuninstall_cmds" '$rmforce || exit_status=1' fi # FIXME: should reinstall the best remaining shared library. ;; esac fi ;; *.lo) # Possibly a libtool object, so verify it. if func_lalib_p "$file"; then # Read the .lo file func_source $dir/$name # Add PIC object to the list of files to remove. if test -n "$pic_object" && test none != "$pic_object"; then func_append rmfiles " $dir/$pic_object" fi # Add non-PIC object to the list of files to remove. if test -n "$non_pic_object" && test none != "$non_pic_object"; then func_append rmfiles " $dir/$non_pic_object" fi fi ;; *) if test clean = "$opt_mode"; then noexename=$name case $file in *.exe) func_stripname '' '.exe' "$file" file=$func_stripname_result func_stripname '' '.exe' "$name" noexename=$func_stripname_result # $file with .exe has already been added to rmfiles, # add $file without .exe func_append rmfiles " $file" ;; esac # Do a test to see if this is a libtool program. if func_ltwrapper_p "$file"; then if func_ltwrapper_executable_p "$file"; then func_ltwrapper_scriptname "$file" relink_command= func_source $func_ltwrapper_scriptname_result func_append rmfiles " $func_ltwrapper_scriptname_result" else relink_command= func_source $dir/$noexename fi # note $name still contains .exe if it was in $file originally # as does the version of $file that was added into $rmfiles func_append rmfiles " $odir/$name $odir/${name}S.$objext" if test yes = "$fast_install" && test -n "$relink_command"; then func_append rmfiles " $odir/lt-$name" fi if test "X$noexename" != "X$name"; then func_append rmfiles " $odir/lt-$noexename.c" fi fi fi ;; esac func_show_eval "$RM $rmfiles" 'exit_status=1' done # Try to remove the $objdir's in the directories where we deleted files for dir in $rmdirs; do if test -d "$dir"; then func_show_eval "rmdir $dir >/dev/null 2>&1" fi done exit $exit_status } if test uninstall = "$opt_mode" || test clean = "$opt_mode"; then func_mode_uninstall ${1+"$@"} fi test -z "$opt_mode" && { help=$generic_help func_fatal_help "you must specify a MODE" } test -z "$exec_cmd" && \ func_fatal_help "invalid operation mode '$opt_mode'" if test -n "$exec_cmd"; then eval exec "$exec_cmd" exit $EXIT_FAILURE fi exit $exit_status # The TAGs below are defined such that we never get into a situation # where we disable both kinds of libraries. Given conflicting # choices, we go for a static library, that is the most portable, # since we can't tell whether shared libraries were disabled because # the user asked for that or because the platform doesn't support # them. This is particularly important on AIX, because we don't # support having both static and shared libraries enabled at the same # time on that platform, so we default to a shared-only configuration. # If a disable-shared tag is given, we'll fallback to a static-only # configuration. But we'll never go from static-only to shared-only. # ### BEGIN LIBTOOL TAG CONFIG: disable-shared build_libtool_libs=no build_old_libs=yes # ### END LIBTOOL TAG CONFIG: disable-shared # ### BEGIN LIBTOOL TAG CONFIG: disable-static build_old_libs=`case $build_libtool_libs in yes) echo no;; *) echo yes;; esac` # ### END LIBTOOL TAG CONFIG: disable-static # Local Variables: # mode:shell-script # sh-indentation:2 # End: bluez-5.82/PaxHeaders/compile0000644000000000000000000000005014773211371013227 xustar0020 atime=1743590137 20 ctime=1743591275 bluez-5.82/compile0000755000000000000000000001635014773211371012720 0ustar00rootroot#! /bin/sh # Wrapper for compilers which do not understand '-c -o'. scriptversion=2018-03-07.03; # UTC # Copyright (C) 1999-2021 Free Software Foundation, Inc. # Written by Tom Tromey . # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2, or (at your option) # any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program. If not, see . # As a special exception to the GNU General Public License, if you # distribute this file as part of a program that contains a # configuration script generated by Autoconf, you may include it under # the same distribution terms that you use for the rest of that program. # This file is maintained in Automake, please report # bugs to or send patches to # . nl=' ' # We need space, tab and new line, in precisely that order. Quoting is # there to prevent tools from complaining about whitespace usage. IFS=" "" $nl" file_conv= # func_file_conv build_file lazy # Convert a $build file to $host form and store it in $file # Currently only supports Windows hosts. If the determined conversion # type is listed in (the comma separated) LAZY, no conversion will # take place. func_file_conv () { file=$1 case $file in / | /[!/]*) # absolute file, and not a UNC file if test -z "$file_conv"; then # lazily determine how to convert abs files case `uname -s` in MINGW*) file_conv=mingw ;; CYGWIN* | MSYS*) file_conv=cygwin ;; *) file_conv=wine ;; esac fi case $file_conv/,$2, in *,$file_conv,*) ;; mingw/*) file=`cmd //C echo "$file " | sed -e 's/"\(.*\) " *$/\1/'` ;; cygwin/* | msys/*) file=`cygpath -m "$file" || echo "$file"` ;; wine/*) file=`winepath -w "$file" || echo "$file"` ;; esac ;; esac } # func_cl_dashL linkdir # Make cl look for libraries in LINKDIR func_cl_dashL () { func_file_conv "$1" if test -z "$lib_path"; then lib_path=$file else lib_path="$lib_path;$file" fi linker_opts="$linker_opts -LIBPATH:$file" } # func_cl_dashl library # Do a library search-path lookup for cl func_cl_dashl () { lib=$1 found=no save_IFS=$IFS IFS=';' for dir in $lib_path $LIB do IFS=$save_IFS if $shared && test -f "$dir/$lib.dll.lib"; then found=yes lib=$dir/$lib.dll.lib break fi if test -f "$dir/$lib.lib"; then found=yes lib=$dir/$lib.lib break fi if test -f "$dir/lib$lib.a"; then found=yes lib=$dir/lib$lib.a break fi done IFS=$save_IFS if test "$found" != yes; then lib=$lib.lib fi } # func_cl_wrapper cl arg... # Adjust compile command to suit cl func_cl_wrapper () { # Assume a capable shell lib_path= shared=: linker_opts= for arg do if test -n "$eat"; then eat= else case $1 in -o) # configure might choose to run compile as 'compile cc -o foo foo.c'. eat=1 case $2 in *.o | *.[oO][bB][jJ]) func_file_conv "$2" set x "$@" -Fo"$file" shift ;; *) func_file_conv "$2" set x "$@" -Fe"$file" shift ;; esac ;; -I) eat=1 func_file_conv "$2" mingw set x "$@" -I"$file" shift ;; -I*) func_file_conv "${1#-I}" mingw set x "$@" -I"$file" shift ;; -l) eat=1 func_cl_dashl "$2" set x "$@" "$lib" shift ;; -l*) func_cl_dashl "${1#-l}" set x "$@" "$lib" shift ;; -L) eat=1 func_cl_dashL "$2" ;; -L*) func_cl_dashL "${1#-L}" ;; -static) shared=false ;; -Wl,*) arg=${1#-Wl,} save_ifs="$IFS"; IFS=',' for flag in $arg; do IFS="$save_ifs" linker_opts="$linker_opts $flag" done IFS="$save_ifs" ;; -Xlinker) eat=1 linker_opts="$linker_opts $2" ;; -*) set x "$@" "$1" shift ;; *.cc | *.CC | *.cxx | *.CXX | *.[cC]++) func_file_conv "$1" set x "$@" -Tp"$file" shift ;; *.c | *.cpp | *.CPP | *.lib | *.LIB | *.Lib | *.OBJ | *.obj | *.[oO]) func_file_conv "$1" mingw set x "$@" "$file" shift ;; *) set x "$@" "$1" shift ;; esac fi shift done if test -n "$linker_opts"; then linker_opts="-link$linker_opts" fi exec "$@" $linker_opts exit 1 } eat= case $1 in '') echo "$0: No command. Try '$0 --help' for more information." 1>&2 exit 1; ;; -h | --h*) cat <<\EOF Usage: compile [--help] [--version] PROGRAM [ARGS] Wrapper for compilers which do not understand '-c -o'. Remove '-o dest.o' from ARGS, run PROGRAM with the remaining arguments, and rename the output as expected. If you are trying to build a whole package this is not the right script to run: please start by reading the file 'INSTALL'. Report bugs to . EOF exit $? ;; -v | --v*) echo "compile $scriptversion" exit $? ;; cl | *[/\\]cl | cl.exe | *[/\\]cl.exe | \ icl | *[/\\]icl | icl.exe | *[/\\]icl.exe ) func_cl_wrapper "$@" # Doesn't return... ;; esac ofile= cfile= for arg do if test -n "$eat"; then eat= else case $1 in -o) # configure might choose to run compile as 'compile cc -o foo foo.c'. # So we strip '-o arg' only if arg is an object. eat=1 case $2 in *.o | *.obj) ofile=$2 ;; *) set x "$@" -o "$2" shift ;; esac ;; *.c) cfile=$1 set x "$@" "$1" shift ;; *) set x "$@" "$1" shift ;; esac fi shift done if test -z "$ofile" || test -z "$cfile"; then # If no '-o' option was seen then we might have been invoked from a # pattern rule where we don't need one. That is ok -- this is a # normal compilation that the losing compiler can handle. If no # '.c' file was seen then we are probably linking. That is also # ok. exec "$@" fi # Name of file we expect compiler to create. cofile=`echo "$cfile" | sed 's|^.*[\\/]||; s|^[a-zA-Z]:||; s/\.c$/.o/'` # Create the lock directory. # Note: use '[/\\:.-]' here to ensure that we don't use the same name # that we are using for the .o file. Also, base the name on the expected # object file name, since that is what matters with a parallel build. lockdir=`echo "$cofile" | sed -e 's|[/\\:.-]|_|g'`.d while true; do if mkdir "$lockdir" >/dev/null 2>&1; then break fi sleep 1 done # FIXME: race condition here if user kills between mkdir and trap. trap "rmdir '$lockdir'; exit 1" 1 2 15 # Run the compile. "$@" ret=$? if test -f "$cofile"; then test "$cofile" = "$ofile" || mv "$cofile" "$ofile" elif test -f "${cofile}bj"; then test "${cofile}bj" = "$ofile" || mv "${cofile}bj" "$ofile" fi rmdir "$lockdir" exit $ret # Local Variables: # mode: shell-script # sh-indentation: 2 # eval: (add-hook 'before-save-hook 'time-stamp) # time-stamp-start: "scriptversion=" # time-stamp-format: "%:y-%02m-%02d.%02H" # time-stamp-time-zone: "UTC0" # time-stamp-end: "; # UTC" # End: bluez-5.82/PaxHeaders/AUTHORS0000644000000000000000000000005014447506753012735 xustar0020 atime=1743516861 20 ctime=1743591275 bluez-5.82/AUTHORS0000644000000000000000000001061614447506753012422 0ustar00rootrootMaxim Krasnyansky Marcel Holtmann Stephen Crane Jean Tourrilhes Jan Beutel Ilguiz Latypov Thomas Moser Nils Faerber Martin Leopold Wolfgang Heidrich Fabrizio Gennari Brad Midgley Henryk Ploetz Philip Blundell Johan Hedberg Claudio Takahasi Eduardo Rocha Denis Kenzior Frederic Dalleau Frederic Danis Luiz Augusto von Dentz Fabien Chevalier Ohad Ben-Cohen Daniel Gollub Tom Patzig Kai Vehmanen Vinicius Gomes Alok Barsode Bastien Nocera Albert Huang Glenn Durfee David Woodhouse Christian Hoene Pekka Pessi Siarhei Siamashka Nick Pelly Lennart Poettering Gustavo Padovan Marc-Andre Lureau Bea Lam Zygo Blaxell Forrest Zhao Scott Talbot Ilya Rubtsov Mario Limonciello Filippo Giunchedi Jaikumar Ganesh Elvis Pfutzenreuter Santiago Carot-Nemesio José Antonio Santos Cadenas Francisco Alecrim Daniel Orstadius Anderson Briglia Anderson Lizardo Bruna Moreira Brian Gix Andre Guedes Sheldon Demario Lucas De Marchi Szymon Janc Syam Sidhardhan Paulo Alcantara Jefferson Delfes Andrzej Kaczmarek Eder Ruiz Maria Mikel Astiz Chan-yeol Park João Paulo Rechi Vita Larry Junior Raymond Liu Radoslaw Jablonski Rafal Michalski Dmitriy Paliy Bartosz Szatkowski Lukasz Pawlik Slawomir Bochenski Wayne Lee Ricky Yuen Takashi Sasai Andre Dieb Martins Cristian Rodríguez Alex Deymo Petri Gynther Scott James Remnant Jakub Tyszkowski Grzegorz Kołodziejczyk Marcin Krąglak Łukasz Rymanowski Jerzy Kasenberg Arman Uguray Artem Rakhov Mike Ryan David Herrmann Jacob Siverskog Sebastian Chłąd Alex Gal Loic Poulain Gowtham Anandha Babu Bharat Panda Marie Janssen Jaganath Kanakkassery Michał Narajowski Inga Stotland Alain Michaud Robert Lubaś Michał Lowas-Rzechonek Jakub Witowski Rafał Gajda Szymon Czapracki bluez-5.82/PaxHeaders/completion0000644000000000000000000000005014773213553013754 xustar0020 atime=1743591291 20 ctime=1743591275 bluez-5.82/completion/0000755000000000000000000000000014773213553013512 5ustar00rootrootbluez-5.82/completion/PaxHeaders/zsh0000644000000000000000000000005014773213553014560 xustar0020 atime=1743591291 20 ctime=1743591275 bluez-5.82/completion/zsh/0000755000000000000000000000000014773213553014316 5ustar00rootrootbluez-5.82/completion/zsh/PaxHeaders/_bluetoothctl0000644000000000000000000000005014333256742017426 xustar0020 atime=1743516861 20 ctime=1743591275 bluez-5.82/completion/zsh/_bluetoothctl0000644000000000000000000000454214333256742017114 0ustar00rootroot#compdef bluetoothctl __bluetoothctl() { bluetoothctl "$@" 2>/dev/null } _bluezcomp_controller() { local -a controllers bluetoothctl list | while read _ MAC NAME; do controllers+="${MAC//:/\\:}:${NAME//:/\\:}" done _describe -t controllers 'controller' controllers } _bluezcomp_device() { local -a devices bluetoothctl devices | while read _ MAC NAME; do devices+="${MAC//:/\\:}:${NAME//:/\\:}" done _describe -t devices 'device' devices } _bluezcomp_agentcap() { local -a agent_options=(${(f)"$(__bluetoothctl agent help)"}) agent_options=( "${(@)agent_options:#(on|off)}" ) compadd -a agent_options } _bluetoothctl_agent() { local -a agent_options=(${(f)"$(__bluetoothctl agent help)"}) agent_options+=help compadd -a agent_options } _bluetoothctl_advertise() { local -a ad_options=(${(f)"$(__bluetoothctl advertise help)"}) ad_options+=help compadd -a ad_options } _bluetoothctl() { local -a toggle_commands=( "discoverable" "pairable" "power" "scan" ) local -a controller_commands=( "select" "show" ) local -a device_commands=( "block" "connect" "disconnect" "info" "pair" "remove" "trust" "unblock" "untrust" ) # Other commands may be handled by _bluetoothctl_$command local -a all_commands=( "${(@f)$(__bluetoothctl --zsh-complete help)}" ) local curcontext=$curcontext state line ret=1 _arguments -C \ + '(info)' \ {-h,--help}'[Show help message and exit]' \ {-v,--version}'--version[Show version info and exit]' \ + 'mod' \ '(info)'{-a+,--agent=}'[Register agent handler]:agent:_bluezcomp_agentcap' \ '(info)'{-t,--timeout}'[Timeout in seconds for non-interactive mode]' \ '(info)'{-m,--monitor}'[Enable monitor output]' \ + 'command' \ '(info):command:->command' \ '(info):: :->argument' if [[ $state == "command" ]]; then _describe -t commands 'command' all_commands elif [[ $state == "argument" ]]; then if (( ! ${"${(@)all_commands%%:*}"[(I)${line[1]}]} )); then _message "Unknown bluetoothctl command: $line[1]" return 1; fi curcontext="${curcontext%:*:*}:bluetoothctl-$line[1]:" if ! _call_function ret _bluetoothctl_$line[1]; then case $line[1] in (${(~j.|.)toggle_commands}) compadd on off ;; (${(~j.|.)device_commands}) _bluezcomp_device ;; (${(~j.|.)controller_commands}) _bluezcomp_controller ;; esac fi return ret fi } && _bluetoothctl bluez-5.82/PaxHeaders/monitor0000644000000000000000000000005014773213573013274 xustar0020 atime=1743591291 20 ctime=1743591291 bluez-5.82/monitor/0000755000000000000000000000000014773213573013032 5ustar00rootrootbluez-5.82/monitor/PaxHeaders/sdp.h0000644000000000000000000000005014015011623014265 xustar0020 atime=1743516019 20 ctime=1743591282 bluez-5.82/monitor/sdp.h0000644000000000000000000000043014015011623013743 0ustar00rootroot/* SPDX-License-Identifier: LGPL-2.1-or-later */ /* * * BlueZ - Bluetooth protocol stack for Linux * * Copyright (C) 2011-2014 Intel Corporation * Copyright (C) 2002-2010 Marcel Holtmann * * */ void sdp_packet(const struct l2cap_frame *frame); bluez-5.82/monitor/PaxHeaders/analyze.c0000644000000000000000000000005014471706457015163 xustar0020 atime=1743516051 20 ctime=1743591282 bluez-5.82/monitor/analyze.c0000644000000000000000000005724714471706457014663 0ustar00rootroot// SPDX-License-Identifier: LGPL-2.1-or-later /* * * BlueZ - Bluetooth protocol stack for Linux * * Copyright (C) 2011-2014 Intel Corporation * Copyright (C) 2002-2010 Marcel Holtmann * * */ #ifdef HAVE_CONFIG_H #include #endif #define _GNU_SOURCE #include #include #include #include #include "lib/bluetooth.h" #include "src/shared/util.h" #include "src/shared/queue.h" #include "src/shared/btsnoop.h" #include "monitor/bt.h" #include "monitor/display.h" #include "monitor/packet.h" #include "monitor/analyze.h" #define TIMEVAL_MSEC(_tv) \ (long long)((_tv)->tv_sec * 1000 + (_tv)->tv_usec / 1000) struct hci_dev { uint16_t index; uint8_t type; uint8_t bdaddr[6]; struct timeval time_added; struct timeval time_removed; unsigned long num_hci; unsigned long num_cmd; unsigned long num_evt; unsigned long num_acl; unsigned long num_sco; unsigned long num_iso; unsigned long vendor_diag; unsigned long system_note; unsigned long user_log; unsigned long ctrl_msg; unsigned long unknown; uint16_t manufacturer; struct queue *conn_list; }; #define CONN_BR_ACL 0x01 #define CONN_BR_SCO 0x02 #define CONN_BR_ESCO 0x03 #define CONN_LE_ACL 0x04 #define CONN_LE_ISO 0x05 struct hci_stats { size_t bytes; size_t num; size_t num_comp; struct packet_latency latency; struct queue *plot; uint16_t min; uint16_t max; }; struct hci_conn { uint16_t handle; uint16_t link; uint8_t type; uint8_t bdaddr[6]; bool setup_seen; bool terminated; struct queue *tx_queue; struct timeval last_rx; struct queue *chan_list; struct hci_stats rx; struct hci_stats tx; }; struct hci_conn_tx { struct timeval tv; struct l2cap_chan *chan; }; struct plot { long long x_msec; size_t y_count; }; struct l2cap_chan { uint16_t cid; uint16_t psm; bool out; struct timeval last_rx; struct hci_stats rx; struct hci_stats tx; }; static struct queue *dev_list; static void tmp_write(void *data, void *user_data) { struct plot *plot = data; FILE *tmp = user_data; fprintf(tmp, "%lld %zu\n", plot->x_msec, plot->y_count); } static void plot_draw(struct queue *queue, const char *tittle) { FILE *gplot; if (queue_length(queue) < 2) return; gplot = popen("gnuplot", "w"); if (!gplot) return; fprintf(gplot, "$data << EOD\n"); queue_foreach(queue, tmp_write, gplot); fprintf(gplot, "EOD\n"); fprintf(gplot, "set terminal dumb enhanced ansi\n"); fprintf(gplot, "set xlabel 'Latency (ms)'\n"); fprintf(gplot, "set tics out nomirror\n"); fprintf(gplot, "set log y\n"); fprintf(gplot, "set yrange [0.5:*]\n"); fprintf(gplot, "plot $data using 1:2 t '%s' w impulses\n", tittle); fflush(gplot); pclose(gplot); } static void print_stats(struct hci_stats *stats, const char *label) { if (!stats->num) return; print_field("%s packets: %zu/%zu", label, stats->num, stats->num_comp); print_field("%s Latency: %lld-%lld msec (~%lld msec)", label, TV_MSEC(stats->latency.min), TV_MSEC(stats->latency.max), TV_MSEC(stats->latency.med)); print_field("%s size: %u-%u octets (~%zd octets)", label, stats->min, stats->max, stats->bytes / stats->num); if (TV_MSEC(stats->latency.total)) print_field("%s speed: ~%lld Kb/s", label, stats->bytes * 8 / TV_MSEC(stats->latency.total)); plot_draw(stats->plot, label); } static void chan_destroy(void *data) { struct l2cap_chan *chan = data; if (!chan->rx.num && !chan->tx.num) goto done; printf(" Found %s L2CAP channel with CID %u\n", chan->out ? "TX" : "RX", chan->cid); if (chan->psm) print_field("PSM %u", chan->psm); print_stats(&chan->rx, "RX"); print_stats(&chan->tx, "TX"); done: free(chan); } static struct l2cap_chan *chan_alloc(struct hci_conn *conn, uint16_t cid, bool out) { struct l2cap_chan *chan; chan = new0(struct l2cap_chan, 1); chan->cid = cid; chan->out = out; chan->rx.plot = queue_new(); chan->tx.plot = queue_new(); return chan; } static bool chan_match_cid(const void *a, const void *b) { const struct l2cap_chan *chan = a; uint32_t val = PTR_TO_UINT(b); uint16_t cid = val & 0xffff; bool out = val & 0x10000; return chan->cid == cid && chan->out == out; } static struct l2cap_chan *chan_lookup(struct hci_conn *conn, uint16_t cid, bool out) { struct l2cap_chan *chan; uint32_t val = cid | (out ? 0x10000 : 0); chan = queue_find(conn->chan_list, chan_match_cid, UINT_TO_PTR(val)); if (!chan) { chan = chan_alloc(conn, cid, out); queue_push_tail(conn->chan_list, chan); } return chan; } static void conn_destroy(void *data) { struct hci_conn *conn = data; const char *str; switch (conn->type) { case CONN_BR_ACL: str = "BR-ACL"; break; case CONN_BR_SCO: str = "BR-SCO"; break; case CONN_BR_ESCO: str = "BR-ESCO"; break; case CONN_LE_ACL: str = "LE-ACL"; break; case CONN_LE_ISO: str = "LE-ISO"; break; default: str = "unknown"; break; } printf(" Found %s connection with handle %u\n", str, conn->handle); /* TODO: Store address type */ packet_print_addr("Address", conn->bdaddr, 0x00); if (!conn->setup_seen) print_field("Connection setup missing"); print_stats(&conn->rx, "RX"); print_stats(&conn->tx, "TX"); queue_destroy(conn->rx.plot, free); queue_destroy(conn->tx.plot, free); queue_destroy(conn->chan_list, chan_destroy); queue_destroy(conn->tx_queue, free); free(conn); } static struct hci_conn *conn_alloc(struct hci_dev *dev, uint16_t handle, uint8_t type) { struct hci_conn *conn; conn = new0(struct hci_conn, 1); conn->handle = handle; conn->type = type; conn->tx_queue = queue_new(); conn->tx.plot = queue_new(); conn->rx.plot = queue_new(); conn->chan_list = queue_new(); return conn; } static bool conn_match_handle(const void *a, const void *b) { const struct hci_conn *conn = a; uint16_t handle = PTR_TO_UINT(b); return (conn->handle == handle && !conn->terminated); } static struct hci_conn *conn_lookup(struct hci_dev *dev, uint16_t handle) { return queue_find(dev->conn_list, conn_match_handle, UINT_TO_PTR(handle)); } static bool link_match_handle(const void *a, const void *b) { const struct hci_conn *conn = a; uint16_t handle = PTR_TO_UINT(b); return (conn->link == handle && !conn->terminated); } static struct hci_conn *link_lookup(struct hci_dev *dev, uint16_t handle) { return queue_find(dev->conn_list, link_match_handle, UINT_TO_PTR(handle)); } static struct hci_conn *conn_lookup_type(struct hci_dev *dev, uint16_t handle, uint8_t type) { struct hci_conn *conn; conn = queue_find(dev->conn_list, conn_match_handle, UINT_TO_PTR(handle)); if (!conn || (type && conn->type != type)) { conn = conn_alloc(dev, handle, type); queue_push_tail(dev->conn_list, conn); } return conn; } static void dev_destroy(void *data) { struct hci_dev *dev = data; const char *str; switch (dev->type) { case 0x00: str = "BR/EDR"; break; case 0x01: str = "AMP"; break; default: str = "unknown"; break; } printf("Found %s controller with index %u\n", str, dev->index); printf(" BD_ADDR %2.2X:%2.2X:%2.2X:%2.2X:%2.2X:%2.2X", dev->bdaddr[5], dev->bdaddr[4], dev->bdaddr[3], dev->bdaddr[2], dev->bdaddr[1], dev->bdaddr[0]); if (dev->manufacturer != 0xffff) printf(" (%s)", bt_compidtostr(dev->manufacturer)); printf("\n"); printf(" %lu commands\n", dev->num_cmd); printf(" %lu events\n", dev->num_evt); printf(" %lu ACL packets\n", dev->num_acl); printf(" %lu SCO packets\n", dev->num_sco); printf(" %lu ISO packets\n", dev->num_iso); printf(" %lu vendor diagnostics\n", dev->vendor_diag); printf(" %lu system notes\n", dev->system_note); printf(" %lu user logs\n", dev->user_log); printf(" %lu control messages \n", dev->ctrl_msg); printf(" %lu unknown opcodes\n", dev->unknown); queue_destroy(dev->conn_list, conn_destroy); printf("\n"); free(dev); } static struct hci_dev *dev_alloc(uint16_t index) { struct hci_dev *dev; dev = new0(struct hci_dev, 1); dev->index = index; dev->manufacturer = 0xffff; dev->conn_list = queue_new(); return dev; } static bool dev_match_index(const void *a, const void *b) { const struct hci_dev *dev = a; uint16_t index = PTR_TO_UINT(b); return dev->index == index; } static struct hci_dev *dev_lookup(uint16_t index) { struct hci_dev *dev; dev = queue_find(dev_list, dev_match_index, UINT_TO_PTR(index)); if (!dev) { dev = dev_alloc(index); queue_push_tail(dev_list, dev); } return dev; } static void l2cap_sig(struct hci_conn *conn, bool out, const void *data, uint16_t size) { const struct bt_l2cap_hdr_sig *hdr = data; struct l2cap_chan *chan; uint16_t psm, scid, dcid; switch (hdr->code) { case BT_L2CAP_PDU_CONN_REQ: psm = get_le16(data + 4); scid = get_le16(data + 6); chan = chan_lookup(conn, scid, out); if (chan) chan->psm = psm; break; case BT_L2CAP_PDU_CONN_RSP: dcid = get_le16(data + 4); scid = get_le16(data + 6); chan = chan_lookup(conn, scid, !out); if (chan) { psm = chan->psm; chan = chan_lookup(conn, dcid, out); if (chan) chan->psm = psm; } break; } } static void new_index(struct timeval *tv, uint16_t index, const void *data, uint16_t size) { const struct btsnoop_opcode_new_index *ni = data; struct hci_dev *dev; dev = dev_alloc(index); dev->type = ni->type; memcpy(dev->bdaddr, ni->bdaddr, 6); queue_push_tail(dev_list, dev); } static void del_index(struct timeval *tv, uint16_t index, const void *data, uint16_t size) { struct hci_dev *dev; dev = queue_remove_if(dev_list, dev_match_index, UINT_TO_PTR(index)); if (!dev) { fprintf(stderr, "Remove for an unexisting device\n"); return; } dev_destroy(dev); } static void command_pkt(struct timeval *tv, uint16_t index, const void *data, uint16_t size) { struct hci_dev *dev; dev = dev_lookup(index); if (!dev) return; dev->num_hci++; dev->num_cmd++; } static void evt_conn_complete(struct hci_dev *dev, struct timeval *tv, const void *data, uint16_t size) { const struct bt_hci_evt_conn_complete *evt = data; struct hci_conn *conn; if (evt->status) return; conn = conn_lookup_type(dev, le16_to_cpu(evt->handle), CONN_BR_ACL); if (!conn) return; memcpy(conn->bdaddr, evt->bdaddr, 6); conn->setup_seen = true; } static void evt_disconnect_complete(struct hci_dev *dev, struct timeval *tv, const void *data, uint16_t size) { const struct bt_hci_evt_disconnect_complete *evt = data; struct hci_conn *conn; if (evt->status) return; conn = conn_lookup(dev, le16_to_cpu(evt->handle)); if (!conn) return; conn->terminated = true; } static void rsp_read_bd_addr(struct hci_dev *dev, struct timeval *tv, const void *data, uint16_t size) { const struct bt_hci_rsp_read_bd_addr *rsp = data; if (rsp->status) return; memcpy(dev->bdaddr, rsp->bdaddr, 6); } static void evt_cmd_complete(struct hci_dev *dev, struct timeval *tv, const void *data, uint16_t size) { const struct bt_hci_evt_cmd_complete *evt = data; uint16_t opcode; data += sizeof(*evt); size -= sizeof(*evt); opcode = le16_to_cpu(evt->opcode); switch (opcode) { case BT_HCI_CMD_READ_BD_ADDR: rsp_read_bd_addr(dev, tv, data, size); break; } } static bool match_plot_latency(const void *data, const void *user_data) { const struct plot *plot = data; const struct timeval *latency = user_data; return TIMEVAL_MSEC(latency) == plot->x_msec; } static void plot_add(struct queue *queue, struct timeval *latency, uint16_t count) { struct plot *plot; /* Use LRU ordering */ plot = queue_remove_if(queue, match_plot_latency, latency); if (plot) { plot->y_count += count; queue_push_head(queue, plot); return; } plot = new0(struct plot, 1); plot->x_msec = TIMEVAL_MSEC(latency); plot->y_count = count; queue_push_tail(queue, plot); } static void evt_le_conn_complete(struct hci_dev *dev, struct timeval *tv, struct iovec *iov) { const struct bt_hci_evt_le_conn_complete *evt; struct hci_conn *conn; evt = util_iov_pull_mem(iov, sizeof(*evt)); if (!evt || evt->status) return; conn = conn_lookup_type(dev, le16_to_cpu(evt->handle), CONN_LE_ACL); if (!conn) return; memcpy(conn->bdaddr, evt->peer_addr, 6); conn->setup_seen = true; } static void evt_le_enh_conn_complete(struct hci_dev *dev, struct timeval *tv, struct iovec *iov) { const struct bt_hci_evt_le_enhanced_conn_complete *evt; struct hci_conn *conn; evt = util_iov_pull_mem(iov, sizeof(*evt)); if (!evt || evt->status) return; conn = conn_lookup_type(dev, le16_to_cpu(evt->handle), CONN_LE_ACL); if (!conn) return; memcpy(conn->bdaddr, evt->peer_addr, 6); conn->setup_seen = true; } static void evt_num_completed_packets(struct hci_dev *dev, struct timeval *tv, const void *data, uint16_t size) { uint8_t num_handles = get_u8(data); int i; data += sizeof(num_handles); size -= sizeof(num_handles); for (i = 0; i < num_handles; i++) { uint16_t handle = get_le16(data); uint16_t count = get_le16(data + 2); struct hci_conn *conn; struct timeval res; struct hci_conn_tx *last_tx; int j; data += 4; size -= 4; conn = conn_lookup(dev, handle); if (!conn) continue; conn->tx.num_comp += count; for (j = 0; j < count; j++) { last_tx = queue_pop_head(conn->tx_queue); if (last_tx) { struct l2cap_chan *chan = last_tx->chan; timersub(tv, &last_tx->tv, &res); packet_latency_add(&conn->tx.latency, &res); plot_add(conn->tx.plot, &res, 1); if (chan) { chan->tx.num_comp += count; packet_latency_add(&chan->tx.latency, &res); plot_add(chan->tx.plot, &res, 1); } free(last_tx); } } } } static void evt_sync_conn_complete(struct hci_dev *dev, struct timeval *tv, const void *data, uint16_t size) { const struct bt_hci_evt_sync_conn_complete *evt = data; struct hci_conn *conn; if (evt->status) return; conn = conn_lookup_type(dev, le16_to_cpu(evt->handle), evt->link_type); if (!conn) return; memcpy(conn->bdaddr, evt->bdaddr, 6); conn->setup_seen = true; } static void evt_le_cis_established(struct hci_dev *dev, struct timeval *tv, struct iovec *iov) { const struct bt_hci_evt_le_cis_established *evt; struct hci_conn *conn, *link; evt = util_iov_pull_mem(iov, sizeof(*evt)); if (!evt || evt->status) return; conn = conn_lookup_type(dev, le16_to_cpu(evt->conn_handle), CONN_LE_ISO); if (!conn) return; conn->setup_seen = true; link = link_lookup(dev, conn->handle); if (link) memcpy(conn->bdaddr, link->bdaddr, 6); } static void evt_le_cis_req(struct hci_dev *dev, struct timeval *tv, struct iovec *iov) { const struct bt_hci_evt_le_cis_req *evt; struct hci_conn *conn; evt = util_iov_pull_mem(iov, sizeof(*evt)); if (!evt) return; conn = conn_lookup(dev, le16_to_cpu(evt->acl_handle)); if (!conn) return; conn->link = le16_to_cpu(evt->cis_handle); } static void evt_le_big_complete(struct hci_dev *dev, struct timeval *tv, struct iovec *iov) { const struct bt_hci_evt_le_big_complete *evt; int i; evt = util_iov_pull_mem(iov, sizeof(*evt)); if (!evt || evt->status) return; for (i = 0; i < evt->num_bis; i++) { struct hci_conn *conn; uint16_t handle; if (!util_iov_pull_le16(iov, &handle)) return; conn = conn_lookup_type(dev, handle, CONN_LE_ISO); if (conn) conn->setup_seen = true; } } static void evt_le_big_sync_established(struct hci_dev *dev, struct timeval *tv, struct iovec *iov) { const struct bt_hci_evt_le_big_sync_estabilished *evt; int i; evt = util_iov_pull_mem(iov, sizeof(*evt)); if (!evt || evt->status) return; for (i = 0; i < evt->num_bis; i++) { struct hci_conn *conn; uint16_t handle; if (!util_iov_pull_le16(iov, &handle)) return; conn = conn_lookup_type(dev, handle, CONN_LE_ISO); if (conn) conn->setup_seen = true; } } static void evt_le_meta_event(struct hci_dev *dev, struct timeval *tv, const void *data, uint16_t size) { struct iovec iov = { .iov_base = (void *)data, .iov_len = size, }; uint8_t subevt; if (!util_iov_pull_u8(&iov, &subevt)) return; switch (subevt) { case BT_HCI_EVT_LE_CONN_COMPLETE: evt_le_conn_complete(dev, tv, &iov); break; case BT_HCI_EVT_LE_ENHANCED_CONN_COMPLETE: evt_le_enh_conn_complete(dev, tv, &iov); break; case BT_HCI_EVT_LE_CIS_ESTABLISHED: evt_le_cis_established(dev, tv, &iov); break; case BT_HCI_EVT_LE_CIS_REQ: evt_le_cis_req(dev, tv, &iov); break; case BT_HCI_EVT_LE_BIG_COMPLETE: evt_le_big_complete(dev, tv, &iov); break; case BT_HCI_EVT_LE_BIG_SYNC_ESTABILISHED: evt_le_big_sync_established(dev, tv, &iov); break; } } static void event_pkt(struct timeval *tv, uint16_t index, const void *data, uint16_t size) { const struct bt_hci_evt_hdr *hdr = data; struct hci_dev *dev; data += sizeof(*hdr); size -= sizeof(*hdr); dev = dev_lookup(index); if (!dev) return; dev->num_hci++; dev->num_evt++; switch (hdr->evt) { case BT_HCI_EVT_CONN_COMPLETE: evt_conn_complete(dev, tv, data, size); break; case BT_HCI_EVT_DISCONNECT_COMPLETE: evt_disconnect_complete(dev, tv, data, size); break; case BT_HCI_EVT_CMD_COMPLETE: evt_cmd_complete(dev, tv, data, size); break; case BT_HCI_EVT_NUM_COMPLETED_PACKETS: evt_num_completed_packets(dev, tv, data, size); break; case BT_HCI_EVT_SYNC_CONN_COMPLETE: evt_sync_conn_complete(dev, tv, data, size); break; case BT_HCI_EVT_LE_META_EVENT: evt_le_meta_event(dev, tv, data, size); break; } } static void stats_add(struct hci_stats *stats, uint16_t size) { stats->num++; stats->bytes += size; if (!stats->min || size < stats->min) stats->min = size; if (!stats->max || size > stats->max) stats->max = size; } static void conn_pkt_tx(struct hci_conn *conn, struct timeval *tv, uint16_t size, struct l2cap_chan *chan) { struct hci_conn_tx *last_tx; last_tx = new0(struct hci_conn_tx, 1); memcpy(last_tx, tv, sizeof(*tv)); last_tx->chan = chan; queue_push_tail(conn->tx_queue, last_tx); stats_add(&conn->tx, size); if (chan) stats_add(&chan->tx, size); } static void conn_pkt_rx(struct hci_conn *conn, struct timeval *tv, uint16_t size, struct l2cap_chan *chan) { struct timeval res; if (timerisset(&conn->last_rx)) { timersub(tv, &conn->last_rx, &res); packet_latency_add(&conn->rx.latency, &res); plot_add(conn->rx.plot, &res, 1); } conn->last_rx = *tv; stats_add(&conn->rx, size); conn->rx.num_comp++; if (chan) { if (timerisset(&chan->last_rx)) { timersub(tv, &chan->last_rx, &res); packet_latency_add(&chan->rx.latency, &res); plot_add(chan->rx.plot, &res, 1); } chan->last_rx = *tv; stats_add(&chan->rx, size); chan->rx.num_comp++; } } static void acl_pkt(struct timeval *tv, uint16_t index, bool out, const void *data, uint16_t size) { const struct bt_hci_acl_hdr *hdr = data; struct hci_dev *dev; struct hci_conn *conn; struct l2cap_chan *chan = NULL; uint16_t cid; data += sizeof(*hdr); size -= sizeof(*hdr); dev = dev_lookup(index); if (!dev) return; dev->num_hci++; dev->num_acl++; conn = conn_lookup_type(dev, le16_to_cpu(hdr->handle) & 0x0fff, 0x00); if (!conn) return; switch (le16_to_cpu(hdr->handle) >> 12) { case 0x00: case 0x02: cid = get_le16(data + 2); chan = chan_lookup(conn, cid, out); if (cid == 1) l2cap_sig(conn, out, data + 4, size - 4); break; } if (out) { conn_pkt_tx(conn, tv, size, chan); } else { conn_pkt_rx(conn, tv, size, chan); } } static void sco_pkt(struct timeval *tv, uint16_t index, bool out, const void *data, uint16_t size) { const struct bt_hci_acl_hdr *hdr = data; struct hci_dev *dev; struct hci_conn *conn; dev = dev_lookup(index); if (!dev) return; dev->num_hci++; dev->num_sco++; conn = conn_lookup_type(dev, le16_to_cpu(hdr->handle) & 0x0fff, CONN_BR_SCO); if (!conn) return; if (out) { conn_pkt_tx(conn, tv, size - sizeof(*hdr), NULL); } else { conn_pkt_rx(conn, tv, size - sizeof(*hdr), NULL); } } static void info_index(struct timeval *tv, uint16_t index, const void *data, uint16_t size) { const struct btsnoop_opcode_index_info *hdr = data; struct hci_dev *dev; dev = dev_lookup(index); if (!dev) return; dev->manufacturer = hdr->manufacturer; } static void vendor_diag(struct timeval *tv, uint16_t index, const void *data, uint16_t size) { struct hci_dev *dev; dev = dev_lookup(index); if (!dev) return; dev->vendor_diag++; } static void system_note(struct timeval *tv, uint16_t index, const void *data, uint16_t size) { struct hci_dev *dev; dev = dev_lookup(index); if (!dev) return; dev->system_note++; } static void user_log(struct timeval *tv, uint16_t index, const void *data, uint16_t size) { struct hci_dev *dev; dev = dev_lookup(index); if (!dev) return; dev->user_log++; } static void ctrl_msg(struct timeval *tv, uint16_t index, const void *data, uint16_t size) { struct hci_dev *dev; dev = dev_lookup(index); if (!dev) return; dev->ctrl_msg++; } static void iso_pkt(struct timeval *tv, uint16_t index, bool out, const void *data, uint16_t size) { const struct bt_hci_iso_hdr *hdr = data; struct hci_conn *conn; struct hci_dev *dev; dev = dev_lookup(index); if (!dev) return; dev->num_hci++; dev->num_iso++; conn = conn_lookup_type(dev, le16_to_cpu(hdr->handle) & 0x0fff, CONN_LE_ISO); if (!conn) return; if (out) { conn_pkt_tx(conn, tv, size - sizeof(*hdr), NULL); } else { conn_pkt_rx(conn, tv, size - sizeof(*hdr), NULL); } } static void unknown_opcode(struct timeval *tv, uint16_t index, const void *data, uint16_t size) { struct hci_dev *dev; dev = dev_lookup(index); if (!dev) return; dev->unknown++; } void analyze_trace(const char *path) { struct btsnoop *btsnoop_file; unsigned long num_packets = 0; uint32_t format; btsnoop_file = btsnoop_open(path, BTSNOOP_FLAG_PKLG_SUPPORT); if (!btsnoop_file) return; format = btsnoop_get_format(btsnoop_file); switch (format) { case BTSNOOP_FORMAT_HCI: case BTSNOOP_FORMAT_UART: case BTSNOOP_FORMAT_MONITOR: break; default: fprintf(stderr, "Unsupported packet format\n"); goto done; } dev_list = queue_new(); while (1) { unsigned char buf[BTSNOOP_MAX_PACKET_SIZE]; struct timeval tv; uint16_t index, opcode, pktlen; if (!btsnoop_read_hci(btsnoop_file, &tv, &index, &opcode, buf, &pktlen)) break; switch (opcode) { case BTSNOOP_OPCODE_NEW_INDEX: new_index(&tv, index, buf, pktlen); break; case BTSNOOP_OPCODE_DEL_INDEX: del_index(&tv, index, buf, pktlen); break; case BTSNOOP_OPCODE_COMMAND_PKT: command_pkt(&tv, index, buf, pktlen); break; case BTSNOOP_OPCODE_EVENT_PKT: event_pkt(&tv, index, buf, pktlen); break; case BTSNOOP_OPCODE_ACL_TX_PKT: acl_pkt(&tv, index, true, buf, pktlen); break; case BTSNOOP_OPCODE_ACL_RX_PKT: acl_pkt(&tv, index, false, buf, pktlen); break; case BTSNOOP_OPCODE_SCO_TX_PKT: sco_pkt(&tv, index, true, buf, pktlen); break; case BTSNOOP_OPCODE_SCO_RX_PKT: sco_pkt(&tv, index, false, buf, pktlen); break; case BTSNOOP_OPCODE_OPEN_INDEX: case BTSNOOP_OPCODE_CLOSE_INDEX: break; case BTSNOOP_OPCODE_INDEX_INFO: info_index(&tv, index, buf, pktlen); break; case BTSNOOP_OPCODE_VENDOR_DIAG: vendor_diag(&tv, index, buf, pktlen); break; case BTSNOOP_OPCODE_SYSTEM_NOTE: system_note(&tv, index, buf, pktlen); break; case BTSNOOP_OPCODE_USER_LOGGING: user_log(&tv, index, buf, pktlen); break; case BTSNOOP_OPCODE_CTRL_OPEN: case BTSNOOP_OPCODE_CTRL_CLOSE: case BTSNOOP_OPCODE_CTRL_COMMAND: case BTSNOOP_OPCODE_CTRL_EVENT: ctrl_msg(&tv, index, buf, pktlen); break; case BTSNOOP_OPCODE_ISO_TX_PKT: iso_pkt(&tv, index, true, buf, pktlen); break; case BTSNOOP_OPCODE_ISO_RX_PKT: iso_pkt(&tv, index, false, buf, pktlen); break; default: unknown_opcode(&tv, index, buf, pktlen); break; } num_packets++; } printf("Trace contains %lu packets\n\n", num_packets); queue_destroy(dev_list, dev_destroy); done: btsnoop_unref(btsnoop_file); } bluez-5.82/monitor/PaxHeaders/vendor.h0000644000000000000000000000005014447506754015023 xustar0020 atime=1743515957 20 ctime=1743591282 bluez-5.82/monitor/vendor.h0000644000000000000000000000140614447506754014505 0ustar00rootroot/* SPDX-License-Identifier: LGPL-2.1-or-later */ /* * * BlueZ - Bluetooth protocol stack for Linux * * Copyright (C) 2011-2014 Intel Corporation * Copyright (C) 2002-2010 Marcel Holtmann * * */ #include struct vendor_ocf { uint16_t ocf; const char *str; void (*cmd_func) (uint16_t index, const void *data, uint8_t size); uint8_t cmd_size; bool cmd_fixed; void (*rsp_func) (uint16_t index, const void *data, uint8_t size); uint8_t rsp_size; bool rsp_fixed; }; struct vendor_evt { uint8_t evt; const char *str; void (*evt_func) (struct timeval *tv, uint16_t index, const void *data, uint8_t size); uint8_t evt_size; bool evt_fixed; }; void vendor_event(uint16_t manufacturer, const void *data, uint8_t size); bluez-5.82/monitor/PaxHeaders/btmon.10000644000000000000000000000005014773213510014541 xustar0020 atime=1743591240 20 ctime=1743591291 bluez-5.82/monitor/btmon.10000644000000000000000000001155114773213510014225 0ustar00rootroot'\" t .\" Man page generated from reStructuredText. . . .nr rst2man-indent-level 0 . .de1 rstReportMargin \\$1 \\n[an-margin] level \\n[rst2man-indent-level] level margin: \\n[rst2man-indent\\n[rst2man-indent-level]] - \\n[rst2man-indent0] \\n[rst2man-indent1] \\n[rst2man-indent2] .. .de1 INDENT .\" .rstReportMargin pre: . RS \\$1 . nr rst2man-indent\\n[rst2man-indent-level] \\n[an-margin] . nr rst2man-indent-level +1 .\" .rstReportMargin post: .. .de UNINDENT . RE .\" indent \\n[an-margin] .\" old: \\n[rst2man-indent\\n[rst2man-indent-level]] .nr rst2man-indent-level -1 .\" new: \\n[rst2man-indent\\n[rst2man-indent-level]] .in \\n[rst2man-indent\\n[rst2man-indent-level]]u .. .TH "BTMON" "1" "April 2021" "BlueZ" "Linux System Administration" .SH NAME btmon \- Bluetooth monitor .SH SYNOPSYS .sp \fBbtmon\fP [\fIOPTIONS\fP ...] .SH DESCRIPTION .sp The btmon(1) command provides access to the Bluetooth subsystem monitor infrastructure for reading HCI traces. .SH OPTIONS .INDENT 0.0 .TP .BI \-r \ FILE\fR,\fB \ \-\-read \ FILE Read traces in btsnoop format from \fIFILE\fP\&. .TP .BI \-w \ FILE\fR,\fB \ \-\-write \ FILE Save traces in btsnoop format to \fIFILE\fP\&. .TP .BI \-a \ FILE\fR,\fB \ \-\-analyze \ FILE Analyze traces in btsnoop format from \fIFILE\fP\&. It displays the devices found in the \fIFILE\fP with its packets by type. If gnuplot is installed on the system it also attempts to plot packet latency graph. .TP .BI \-s \ SOCKET\fR,\fB \ \-\-server \ SOCKET Start monitor server socket. .TP .BI \-p \ PRIORITY\fR,\fB \ \-\-priority \ PRIORITY Show only priority or lower for user log. .UNINDENT .TS box center; l|l. T{ \fIPRIORITY\fP T} T{ NAME T} _ T{ \fB3\fP T} T{ Error T} _ T{ \fB4\fP T} T{ Warning T} _ T{ \fB6\fP T} T{ Information (Default) T} _ T{ \fB7\fP T} T{ Debug. \fBdebug\fP can be used. T} .TE .INDENT 0.0 .TP .BI \-i \ NUM\fR,\fB \ \-\-index \ NUM Show only specified controller. \fIhciNUM\fP is also acceptable. This is useful to capture the traces from the specific controller when the multiple controllers are presented. .TP .BI \-d \ TTY\fR,\fB \ \-\-tty \ TTY Read data from \fITTY\fP\&. .TP .BI \-B \ SPEED\fR,\fB \ \-\-rate \ SPEED Set TTY speed. The default \fISPEED\fP is 115300 .TP .BI \-V \ COMPID\fR,\fB \ \-\-vendor \ COMPID Set the default company identifier. The \fICOMPID\fP is a unique number assigned by the Bluetooth SIG to a member company and can be found/searched from the Bluetooth SIG webpage. .sp For example, Intel is 2 and Realtek is 93. .TP .B \-M\fP,\fB \-\-mgmt Open channel for mgmt events. .TP .B \-t\fP,\fB \-\-time Show a time instead of time offset. .TP .B \-T\fP,\fB \-\-date Show a time and date information instead of time offset. .TP .B \-S\fP,\fB \-\-sco Dump SCO traffic in raw hex format. .TP .B \-A\fP,\fB \-\-a2dp Dump A2DP stream traffic in a raw hex format. .TP .BI \-E \ IP\fR,\fB \ \-\-ellisys \ IP Send Ellisys HCI Injection. .TP .B \-P\fP,\fB \-\-no\-pager Disable pager usage while reading the log file. .TP .BI \-J \ OPTIONS\fR,\fB \ \-\-jlink \ OPTIONS Read data from RTT. Each options are comma(,) seprated without spaces. .UNINDENT .TS box center; l|l. T{ \fIOPTIONS\fP T} T{ Description T} _ T{ \fBDEVICE\fP T} T{ Required. Set the target device. T} _ T{ \fBSERIALNO\fP T} T{ (Optional) Set the USB serial number. Default is \fB0\fP\&. T} _ T{ \fBINTERFACE\fP T} T{ (Optional) Target interface. Default is \fBswd\fP\&. T} _ T{ \fBSPEED\fP T} T{ (Optional) Set target interface speed in kHz. Default is \fB1000\fP\&. T} .TE .INDENT 0.0 .TP .BI \-R \ OPTIONS\fR,\fB \ \-\-rtt \ OPTIONS RTT control block parameters. Each options are comma(,) seprated without spaces. .UNINDENT .TS box center; l|l. T{ \fIOPTIONS\fP T} T{ Description T} _ T{ \fBADDRESS\fP T} T{ (Optional) Address of RTT buffer. Default is \fB0x00\fP T} _ T{ \fBAREA\fP T} T{ (Optional) Size of range to search in RTT buffer. Default is \fB0\fP T} _ T{ \fBNAME\fP T} T{ (Optional) Buffer name. Default is \fBbtmonitor\fP T} .TE .INDENT 0.0 .TP .BI \-C \ WIDTH\fR,\fB \ \-\-columns \ WIDTH Output width if not a terminal .TP .BI \-c \ MODE\fR,\fB \ \-\-color \ MODE Set output color. The possible \fIMODE\fP values are: \fBauto|always|never\fP\&. .sp Default value is \fBauto\fP .TP .B \-v\fP,\fB \-\-version Show version .TP .B \-h\fP,\fB \-\-help Show help options .UNINDENT .SH EXAMPLES .SS Capture the traces from hci0 to hcidump.log file .INDENT 0.0 .INDENT 3.5 .sp .EX $ btmon \-i hci0 \-w hcidump.log .EE .UNINDENT .UNINDENT .SS Open the trace file .INDENT 0.0 .INDENT 3.5 .sp .EX $ btmon \-r hcidump.log .EE .UNINDENT .UNINDENT .SH RESOURCES .sp .SH REPORTING BUGS .sp .SH AUTHOR Marcel Holtmann , Tedd Ho-Jeong An .SH COPYRIGHT Free use of this software is granted under the terms of the GNU Lesser General Public Licenses (LGPL). .\" Generated by docutils manpage writer. . bluez-5.82/monitor/PaxHeaders/hwdb.h0000644000000000000000000000005014015011623014423 xustar0020 atime=1743515957 20 ctime=1743591282 bluez-5.82/monitor/hwdb.h0000644000000000000000000000063514015011623014110 0ustar00rootroot/* SPDX-License-Identifier: LGPL-2.1-or-later */ /* * * BlueZ - Bluetooth protocol stack for Linux * * Copyright (C) 2011-2014 Intel Corporation * Copyright (C) 2002-2010 Marcel Holtmann * * */ #include #include bool hwdb_get_vendor_model(const char *modalias, char **vendor, char **model); bool hwdb_get_company(const uint8_t *bdaddr, char **company); bluez-5.82/monitor/PaxHeaders/a2dp.h0000644000000000000000000000005014015011623014325 xustar0020 atime=1743516040 20 ctime=1743591282 bluez-5.82/monitor/a2dp.h0000644000000000000000000000053114015011623014005 0ustar00rootroot/* SPDX-License-Identifier: LGPL-2.1-or-later */ /* * * BlueZ - Bluetooth protocol stack for Linux * * Copyright (C) 2015 Andrzej Kaczmarek * * */ bool a2dp_codec_cap(uint8_t codec, uint8_t losc, struct l2cap_frame *frame); bool a2dp_codec_cfg(uint8_t codec, uint8_t losc, struct l2cap_frame *frame); bluez-5.82/monitor/PaxHeaders/packet.c0000644000000000000000000000005014772767672015001 xustar0020 atime=1743515579 20 ctime=1743591282 bluez-5.82/monitor/packet.c0000644000000000000000000145312014772767672014470 0ustar00rootroot// SPDX-License-Identifier: LGPL-2.1-or-later /* * * BlueZ - Bluetooth protocol stack for Linux * * Copyright (C) 2011-2014 Intel Corporation * Copyright (C) 2002-2010 Marcel Holtmann * Copyright 2023 NXP * * */ #ifdef HAVE_CONFIG_H #include #endif #define _GNU_SOURCE #include #include #include #include #include #include #include #include #include #include #include #include #include "lib/bluetooth.h" #include "lib/uuid.h" #include "lib/hci.h" #include "lib/hci_lib.h" #include "src/shared/util.h" #include "src/shared/btsnoop.h" #include "src/shared/queue.h" #include "src/shared/bap-debug.h" #include "display.h" #include "bt.h" #include "ll.h" #include "hwdb.h" #include "keys.h" #include "packet.h" #include "l2cap.h" #include "control.h" #include "vendor.h" #include "msft.h" #include "intel.h" #include "broadcom.h" #define COLOR_CHANNEL_LABEL COLOR_WHITE #define COLOR_FRAME_LABEL COLOR_WHITE #define COLOR_INDEX_LABEL COLOR_WHITE #define COLOR_TIMESTAMP COLOR_YELLOW #define COLOR_NEW_INDEX COLOR_GREEN #define COLOR_DEL_INDEX COLOR_RED #define COLOR_OPEN_INDEX COLOR_GREEN #define COLOR_CLOSE_INDEX COLOR_RED #define COLOR_INDEX_INFO COLOR_GREEN #define COLOR_VENDOR_DIAG COLOR_YELLOW #define COLOR_SYSTEM_NOTE COLOR_OFF #define COLOR_HCI_COMMAND COLOR_BLUE #define COLOR_HCI_COMMAND_UNKNOWN COLOR_WHITE_BG #define COLOR_HCI_EVENT COLOR_MAGENTA #define COLOR_HCI_EVENT_UNKNOWN COLOR_WHITE_BG #define COLOR_HCI_ACLDATA COLOR_CYAN #define COLOR_HCI_SCODATA COLOR_YELLOW #define COLOR_HCI_ISODATA COLOR_YELLOW #define COLOR_UNKNOWN_ERROR COLOR_WHITE_BG #define COLOR_UNKNOWN_FEATURE_BIT COLOR_WHITE_BG #define COLOR_UNKNOWN_COMMAND_BIT COLOR_WHITE_BG #define COLOR_UNKNOWN_EVENT_MASK COLOR_WHITE_BG #define COLOR_UNKNOWN_LE_STATES COLOR_WHITE_BG #define COLOR_UNKNOWN_SERVICE_CLASS COLOR_WHITE_BG #define COLOR_UNKNOWN_PKT_TYPE_BIT COLOR_WHITE_BG #define COLOR_CTRL_OPEN COLOR_GREEN_BOLD #define COLOR_CTRL_CLOSE COLOR_RED_BOLD #define COLOR_CTRL_COMMAND COLOR_BLUE_BOLD #define COLOR_CTRL_COMMAND_UNKNOWN COLOR_WHITE_BG #define COLOR_CTRL_EVENT COLOR_MAGENTA_BOLD #define COLOR_CTRL_EVENT_UNKNOWN COLOR_WHITE_BG #define COLOR_UNKNOWN_OPTIONS_BIT COLOR_WHITE_BG #define COLOR_UNKNOWN_SETTINGS_BIT COLOR_WHITE_BG #define COLOR_UNKNOWN_ADDRESS_TYPE COLOR_WHITE_BG #define COLOR_UNKNOWN_DEVICE_FLAG COLOR_WHITE_BG #define COLOR_UNKNOWN_EXP_FEATURE_FLAG COLOR_WHITE_BG #define COLOR_UNKNOWN_ADV_FLAG COLOR_WHITE_BG #define COLOR_UNKNOWN_PHY COLOR_WHITE_BG #define COLOR_UNKNOWN_ADDED_DEVICE_FLAG COLOR_WHITE_BG #define COLOR_UNKNOWN_ADVMON_FEATURES COLOR_WHITE_BG #define COLOR_PHY_PACKET COLOR_BLUE #define UNKNOWN_MANUFACTURER 0xffff static time_t time_offset = ((time_t) -1); static int priority_level = BTSNOOP_PRIORITY_DEBUG; static unsigned long filter_mask = 0; static bool index_filter = false; static uint16_t index_current = 0; static uint16_t fallback_manufacturer = UNKNOWN_MANUFACTURER; #define CTRL_RAW 0x0000 #define CTRL_USER 0x0001 #define CTRL_MGMT 0x0002 #define MAX_CTRL 64 struct ctrl_data { bool used; uint32_t cookie; uint16_t format; char name[20]; }; static struct ctrl_data ctrl_list[MAX_CTRL]; static void assign_ctrl(uint32_t cookie, uint16_t format, const char *name) { int i; for (i = 0; i < MAX_CTRL; i++) { if (!ctrl_list[i].used) { ctrl_list[i].used = true; ctrl_list[i].cookie = cookie; ctrl_list[i].format = format; if (name) { strncpy(ctrl_list[i].name, name, 19); ctrl_list[i].name[19] = '\0'; } else strcpy(ctrl_list[i].name, "null"); break; } } } static void release_ctrl(uint32_t cookie, uint16_t *format, char *name) { int i; if (format) *format = 0xffff; for (i = 0; i < MAX_CTRL; i++) { if (ctrl_list[i].used && ctrl_list[i].cookie == cookie) { ctrl_list[i].used = false; if (format) *format = ctrl_list[i].format; if (name) strncpy(name, ctrl_list[i].name, 20); break; } } } static uint16_t get_format(uint32_t cookie) { int i; for (i = 0; i < MAX_CTRL; i++) { if (ctrl_list[i].used && ctrl_list[i].cookie == cookie) return ctrl_list[i].format; } return 0xffff; } #define MAX_CONN 16 static struct packet_conn_data conn_list[MAX_CONN] = { [0 ... MAX_CONN - 1].handle = 0xffff }; static struct packet_conn_data *lookup_parent(uint16_t handle) { int i; for (i = 0; i < MAX_CONN; i++) { if (conn_list[i].link == handle) return &conn_list[i]; } return NULL; } static struct packet_conn_data *release_handle(uint16_t handle) { int i; for (i = 0; i < MAX_CONN; i++) { struct packet_conn_data *conn = &conn_list[i]; if (conn->handle == handle) { if (conn->destroy) conn->destroy(conn, conn->data); queue_destroy(conn->tx_q, free); queue_destroy(conn->chan_q, free); memset(conn, 0, sizeof(*conn)); conn->handle = 0xffff; return conn; } } return NULL; } static void assign_handle(uint16_t index, uint16_t handle, uint8_t type, uint8_t *dst, uint8_t dst_type) { struct packet_conn_data *conn = release_handle(handle); int i; if (!conn) { for (i = 0; i < MAX_CONN; i++) { if (conn_list[i].handle == 0xffff) { conn = &conn_list[i]; break; } } } if (!conn) return; hci_devba(index, (bdaddr_t *)conn->src); conn->index = index; conn->handle = handle; conn->type = type; if (!dst) { struct packet_conn_data *p; /* If destination is not set attempt to use the parent one if * that exists. */ p = lookup_parent(handle); if (p) { memcpy(conn->dst, p->dst, sizeof(conn->dst)); conn->dst_type = p->dst_type; } } else { memcpy(conn->dst, dst, sizeof(conn->dst)); conn->dst_type = dst_type; } } struct packet_conn_data *packet_get_conn_data(uint16_t handle) { int i; for (i = 0; i < MAX_CONN; i++) { if (conn_list[i].handle == handle) return &conn_list[i]; } return NULL; } static uint8_t get_type(uint16_t handle) { struct packet_conn_data *conn; conn = packet_get_conn_data(handle); if (!conn) return 0xff; return conn->type; } bool packet_has_filter(unsigned long filter) { return filter_mask & filter; } void packet_set_filter(unsigned long filter) { filter_mask = filter; } void packet_add_filter(unsigned long filter) { if (index_filter) filter &= ~PACKET_FILTER_SHOW_INDEX; filter_mask |= filter; } void packet_del_filter(unsigned long filter) { filter_mask &= ~filter; } void packet_set_priority(const char *priority) { if (!priority) return; if (!strcasecmp(priority, "debug")) priority_level = BTSNOOP_PRIORITY_DEBUG; else priority_level = atoi(priority); } void packet_select_index(uint16_t index) { filter_mask &= ~PACKET_FILTER_SHOW_INDEX; control_filter_index(index); index_filter = true; } #define print_space(x) printf("%*c", (x), ' '); #define MAX_INDEX 16 struct index_data { uint8_t type; uint8_t bdaddr[6]; uint16_t manufacturer; uint16_t msft_opcode; uint8_t msft_evt_prefix[8]; uint8_t msft_evt_len; size_t frame; }; static struct index_data index_list[MAX_INDEX]; void packet_set_fallback_manufacturer(uint16_t manufacturer) { int i; for (i = 0; i < MAX_INDEX; i++) index_list[i].manufacturer = manufacturer; fallback_manufacturer = manufacturer; } void packet_set_msft_evt_prefix(const uint8_t *prefix, uint8_t len) { if (index_current < MAX_INDEX && len < 8) memcpy(index_list[index_current].msft_evt_prefix, prefix, len); } static void cred_pid(struct ucred *cred, char *str, size_t len) { char *path = alloca(24); char line[128]; FILE *fp; snprintf(path, 23, "/proc/%u/comm", cred->pid); fp = fopen(path, "re"); if (fp) { if (fgets(line, sizeof(line), fp)) { line[strcspn(line, "\r\n")] = '\0'; snprintf(str, len, "%s[%u]", line, cred->pid); } else snprintf(str, len, "[%u]", cred->pid); fclose(fp); } else snprintf(str, len, "[%u]", cred->pid); } static void print_packet(struct timeval *tv, struct ucred *cred, char ident, uint16_t index, const char *channel, const char *color, const char *label, const char *text, const char *extra) { int col = num_columns(); char line[LINE_MAX], ts_str[96], pid_str[140]; int n, ts_len = 0, ts_pos = 0, len = 0, pos = 0; static size_t last_frame; if (channel) { if (use_color()) { n = sprintf(ts_str + ts_pos, "%s", COLOR_CHANNEL_LABEL); if (n > 0) ts_pos += n; } n = sprintf(ts_str + ts_pos, " {%s}", channel); if (n > 0) { ts_pos += n; ts_len += n; } } else if (index != HCI_DEV_NONE && index < MAX_INDEX && index_list[index].frame != last_frame) { if (use_color()) { n = sprintf(ts_str + ts_pos, "%s", COLOR_FRAME_LABEL); if (n > 0) ts_pos += n; } n = sprintf(ts_str + ts_pos, " #%zu", index_list[index].frame); if (n > 0) { ts_pos += n; ts_len += n; } last_frame = index_list[index].frame; } if ((filter_mask & PACKET_FILTER_SHOW_INDEX) && index != HCI_DEV_NONE) { if (use_color()) { n = snprintf(ts_str + ts_pos, sizeof(ts_str) - ts_pos, "%s", COLOR_INDEX_LABEL); if (n > 0) ts_pos += n; } n = sprintf(ts_str + ts_pos, " [hci%d]", index); if (n > 0) { ts_pos += n; ts_len += n; } } if (tv) { time_t t = tv->tv_sec; struct tm tm; localtime_r(&t, &tm); if (use_color()) { n = sprintf(ts_str + ts_pos, "%s", COLOR_TIMESTAMP); if (n > 0) ts_pos += n; } if (filter_mask & PACKET_FILTER_SHOW_DATE) { n = sprintf(ts_str + ts_pos, " %04d-%02d-%02d", tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday); if (n > 0) { ts_pos += n; ts_len += n; } } if (filter_mask & PACKET_FILTER_SHOW_TIME) { n = sprintf(ts_str + ts_pos, " %02d:%02d:%02d.%06llu", tm.tm_hour, tm.tm_min, tm.tm_sec, (long long)tv->tv_usec); if (n > 0) { ts_pos += n; ts_len += n; } } if (filter_mask & PACKET_FILTER_SHOW_TIME_OFFSET) { n = sprintf(ts_str + ts_pos, " %llu.%06llu", (long long)(tv->tv_sec - time_offset), (long long)tv->tv_usec); if (n > 0) { ts_pos += n; ts_len += n; } } } if (use_color()) { sprintf(ts_str + ts_pos, "%s", COLOR_OFF); n = sprintf(line + pos, "%s", color); if (n > 0) pos += n; } if (cred && cred->pid) { cred_pid(cred, pid_str, sizeof(pid_str)); n = sprintf(line + pos, "%s: %c %s", pid_str, ident, label ? label : ""); } else n = sprintf(line + pos, "%c %s", ident, label ? label : ""); if (n > 0) { pos += n; len += n; } if (text) { int extra_len = extra ? strlen(extra) : 0; int max_len = col - len - extra_len - ts_len - 3; /* Check if there is enough space for the text and the label, if * there isn't then discard extra text since it won't fit. */ if (max_len <= 0) { extra = NULL; max_len = col - len - ts_len - 3; } n = snprintf(line + pos, max_len + 1, "%s%s", label ? ": " : "", text); if (n > max_len) { line[pos + max_len - 1] = '.'; line[pos + max_len - 2] = '.'; if (line[pos + max_len - 3] == ' ') line[pos + max_len - 3] = '.'; n = max_len; } if (n > 0) { pos += n; len += n; } } if (use_color()) { n = sprintf(line + pos, "%s", COLOR_OFF); if (n > 0) pos += n; } if (extra) { n = sprintf(line + pos, " %s", extra); if (n > 0) len += n; } if (ts_len > 0) { printf("%s", line); if (len < col) print_space(col - len - ts_len - 1); printf("%s%s\n", use_color() ? COLOR_TIMESTAMP : "", ts_str); } else printf("%s\n", line); } static const struct { uint8_t error; const char *str; } error2str_table[] = { { 0x00, "Success" }, { 0x01, "Unknown HCI Command" }, { 0x02, "Unknown Connection Identifier" }, { 0x03, "Hardware Failure" }, { 0x04, "Page Timeout" }, { 0x05, "Authentication Failure" }, { 0x06, "PIN or Key Missing" }, { 0x07, "Memory Capacity Exceeded" }, { 0x08, "Connection Timeout" }, { 0x09, "Connection Limit Exceeded" }, { 0x0a, "Synchronous Connection Limit to a Device Exceeded" }, { 0x0b, "ACL Connection Already Exists" }, { 0x0c, "Command Disallowed" }, { 0x0d, "Connection Rejected due to Limited Resources" }, { 0x0e, "Connection Rejected due to Security Reasons" }, { 0x0f, "Connection Rejected due to Unacceptable BD_ADDR" }, { 0x10, "Connection Accept Timeout Exceeded" }, { 0x11, "Unsupported Feature or Parameter Value" }, { 0x12, "Invalid HCI Command Parameters" }, { 0x13, "Remote User Terminated Connection" }, { 0x14, "Remote Device Terminated due to Low Resources" }, { 0x15, "Remote Device Terminated due to Power Off" }, { 0x16, "Connection Terminated By Local Host" }, { 0x17, "Repeated Attempts" }, { 0x18, "Pairing Not Allowed" }, { 0x19, "Unknown LMP PDU" }, { 0x1a, "Unsupported Remote Feature / Unsupported LMP Feature" }, { 0x1b, "SCO Offset Rejected" }, { 0x1c, "SCO Interval Rejected" }, { 0x1d, "SCO Air Mode Rejected" }, { 0x1e, "Invalid LMP Parameters / Invalid LL Parameters" }, { 0x1f, "Unspecified Error" }, { 0x20, "Unsupported LMP Parameter Value / " "Unsupported LL Parameter Value" }, { 0x21, "Role Change Not Allowed" }, { 0x22, "LMP Response Timeout / LL Response Timeout" }, { 0x23, "LMP Error Transaction Collision" }, { 0x24, "LMP PDU Not Allowed" }, { 0x25, "Encryption Mode Not Acceptable" }, { 0x26, "Link Key cannot be Changed" }, { 0x27, "Requested QoS Not Supported" }, { 0x28, "Instant Passed" }, { 0x29, "Pairing With Unit Key Not Supported" }, { 0x2a, "Different Transaction Collision" }, { 0x2b, "Reserved" }, { 0x2c, "QoS Unacceptable Parameter" }, { 0x2d, "QoS Rejected" }, { 0x2e, "Channel Classification Not Supported" }, { 0x2f, "Insufficient Security" }, { 0x30, "Parameter Out Of Manadatory Range" }, { 0x31, "Reserved" }, { 0x32, "Role Switch Pending" }, { 0x33, "Reserved" }, { 0x34, "Reserved Slot Violation" }, { 0x35, "Role Switch Failed" }, { 0x36, "Extended Inquiry Response Too Large" }, { 0x37, "Secure Simple Pairing Not Supported By Host" }, { 0x38, "Host Busy - Pairing" }, { 0x39, "Connection Rejected due to No Suitable Channel Found" }, { 0x3a, "Controller Busy" }, { 0x3b, "Unacceptable Connection Parameters" }, { 0x3c, "Advertising Timeout" }, { 0x3d, "Connection Terminated due to MIC Failure" }, { 0x3e, "Connection Failed to be Established" }, { 0x3f, "MAC Connection Failed" }, { 0x40, "Coarse Clock Adjustment Rejected " "but Will Try to Adjust Using Clock Dragging" }, { 0x41, "Type0 Submap Not Defined" }, { 0x42, "Unknown Advertising Identifier" }, { 0x43, "Limit Reached" }, { 0x44, "Operation Cancelled by Host" }, { 0x45, "Packet Too Long" }, { } }; static void print_error(const char *label, uint8_t error) { const char *str = "Unknown"; const char *color_on, *color_off; bool unknown = true; int i; for (i = 0; error2str_table[i].str; i++) { if (error2str_table[i].error == error) { str = error2str_table[i].str; unknown = false; break; } } if (use_color()) { if (error) { if (unknown) color_on = COLOR_UNKNOWN_ERROR; else color_on = COLOR_RED; } else color_on = COLOR_GREEN; color_off = COLOR_OFF; } else { color_on = ""; color_off = ""; } print_field("%s: %s%s%s (0x%2.2x)", label, color_on, str, color_off, error); } static void print_status(uint8_t status) { print_error("Status", status); } static void print_reason(uint8_t reason) { print_error("Reason", reason); } void packet_print_error(const char *label, uint8_t error) { print_error(label, error); } static void print_enable(const char *label, uint8_t enable) { const char *str; switch (enable) { case 0x00: str = "Disabled"; break; case 0x01: str = "Enabled"; break; default: str = "Reserved"; break; } print_field("%s: %s (0x%2.2x)", label, str, enable); } static void print_addr_type(const char *label, uint8_t addr_type) { const char *str; switch (addr_type) { case 0x00: str = "Public"; break; case 0x01: str = "Random"; break; default: str = "Reserved"; break; } print_field("%s: %s (0x%2.2x)", label, str, addr_type); } static void print_own_addr_type(uint8_t addr_type) { const char *str; switch (addr_type) { case 0x00: case 0x02: str = "Public"; break; case 0x01: case 0x03: str = "Random"; break; default: str = "Reserved"; break; } print_field("Own address type: %s (0x%2.2x)", str, addr_type); } static void print_peer_addr_type(const char *label, uint8_t addr_type) { const char *str; switch (addr_type) { case 0x00: str = "Public"; break; case 0x01: str = "Random"; break; case 0x02: str = "Resolved Public"; break; case 0x03: str = "Resolved Random"; break; default: str = "Reserved"; break; } print_field("%s: %s (0x%2.2x)", label, str, addr_type); } static void print_addr_resolve(const char *label, const uint8_t *addr, uint8_t addr_type, bool resolve) { const char *str; char *company; switch (addr_type) { case 0x00: case 0x02: if (!hwdb_get_company(addr, &company)) company = NULL; if (company) { print_field("%s: %2.2X:%2.2X:%2.2X:%2.2X:%2.2X:%2.2X" " (%s)", label, addr[5], addr[4], addr[3], addr[2], addr[1], addr[0], company); free(company); } else { print_field("%s: %2.2X:%2.2X:%2.2X:%2.2X:%2.2X:%2.2X" " (OUI %2.2X-%2.2X-%2.2X)", label, addr[5], addr[4], addr[3], addr[2], addr[1], addr[0], addr[5], addr[4], addr[3]); } break; case 0x01: case 0x03: switch ((addr[5] & 0xc0) >> 6) { case 0x00: str = "Non-Resolvable"; break; case 0x01: str = "Resolvable"; break; case 0x03: str = "Static"; break; default: str = "Reserved"; break; } print_field("%s: %2.2X:%2.2X:%2.2X:%2.2X:%2.2X:%2.2X (%s)", label, addr[5], addr[4], addr[3], addr[2], addr[1], addr[0], str); if (resolve && (addr[5] & 0xc0) == 0x40) { uint8_t ident[6], ident_type; if (keys_resolve_identity(addr, ident, &ident_type)) { print_addr_type(" Identity type", ident_type); print_addr_resolve(" Identity", ident, ident_type, false); } } break; default: print_field("%s: %2.2X-%2.2X-%2.2X-%2.2X-%2.2X-%2.2X", label, addr[5], addr[4], addr[3], addr[2], addr[1], addr[0]); break; } } static void print_addr(const char *label, const uint8_t *addr, uint8_t type) { print_addr_resolve(label, addr, type, true); } static void print_bdaddr(const uint8_t *bdaddr) { print_addr("Address", bdaddr, 0x00); } static void print_lt_addr(uint8_t lt_addr) { print_field("LT address: %d", lt_addr); } static void print_handle_native(uint16_t handle) { struct packet_conn_data *conn; char label[25]; conn = packet_get_conn_data(handle); if (!conn) { print_field("Handle: %d", handle); return; } sprintf(label, "Handle: %d Address", handle); print_addr(label, conn->dst, conn->dst_type); } static void print_handle(uint16_t handle) { print_handle_native(le16_to_cpu(handle)); } static void print_phy_handle(uint8_t phy_handle) { print_field("Physical handle: %d", phy_handle); } static const struct bitfield_data pkt_type_table[] = { { 1, "2-DH1 may not be used" }, { 2, "3-DH1 may not be used" }, { 3, "DM1 may be used" }, { 4, "DH1 may be used" }, { 8, "2-DH3 may not be used" }, { 9, "3-DH3 may not be used" }, { 10, "DM3 may be used" }, { 11, "DH3 may be used" }, { 12, "2-DH5 may not be used" }, { 13, "3-DH5 may not be used" }, { 14, "DM5 may be used" }, { 15, "DH5 may be used" }, { } }; static void print_pkt_type(uint16_t pkt_type) { uint16_t mask = le16_to_cpu(pkt_type); print_field("Packet type: 0x%4.4x", mask); mask = print_bitfield(2, mask, pkt_type_table); if (mask) print_text(COLOR_UNKNOWN_PKT_TYPE_BIT, " Unknown packet types (0x%4.4x)", mask); } static const struct bitfield_data pkt_type_sco_table[] = { { 0, "HV1 may be used" }, { 1, "HV2 may be used" }, { 2, "HV3 may be used" }, { 3, "EV3 may be used" }, { 4, "EV4 may be used" }, { 5, "EV5 may be used" }, { 6, "2-EV3 may not be used" }, { 7, "3-EV3 may not be used" }, { 8, "2-EV5 may not be used" }, { 9, "3-EV5 may not be used" }, { } }; static void print_pkt_type_sco(uint16_t pkt_type) { uint16_t mask = le16_to_cpu(pkt_type); print_field("Packet type: 0x%4.4x", mask); mask = print_bitfield(2, mask, pkt_type_sco_table); if (mask) print_text(COLOR_UNKNOWN_PKT_TYPE_BIT, " Unknown packet types (0x%4.4x)", mask); } static void print_iac(const uint8_t *lap) { const char *str = ""; if (lap[2] == 0x9e && lap[1] == 0x8b) { switch (lap[0]) { case 0x33: str = " (General Inquiry)"; break; case 0x00: str = " (Limited Inquiry)"; break; } } print_field("Access code: 0x%2.2x%2.2x%2.2x%s", lap[2], lap[1], lap[0], str); } static void print_auth_enable(uint8_t enable) { const char *str; switch (enable) { case 0x00: str = "Authentication not required"; break; case 0x01: str = "Authentication required for all connections"; break; default: str = "Reserved"; break; } print_field("Enable: %s (0x%2.2x)", str, enable); } static void print_encrypt_mode(uint8_t mode) { const char *str; switch (mode) { case 0x00: str = "Encryption not required"; break; case 0x01: str = "Encryption required for all connections"; break; default: str = "Reserved"; break; } print_field("Mode: %s (0x%2.2x)", str, mode); } static const struct bitfield_data svc_class_table[] = { { 0, "Positioning (Location identification)" }, { 1, "Networking (LAN, Ad hoc)" }, { 2, "Rendering (Printing, Speaker)" }, { 3, "Capturing (Scanner, Microphone)" }, { 4, "Object Transfer (v-Inbox, v-Folder)" }, { 5, "Audio (Speaker, Microphone, Headset)" }, { 6, "Telephony (Cordless telephony, Modem, Headset)" }, { 7, "Information (WEB-server, WAP-server)" }, { } }; static const struct { uint8_t val; const char *str; } major_class_computer_table[] = { { 0x00, "Uncategorized, code for device not assigned" }, { 0x01, "Desktop workstation" }, { 0x02, "Server-class computer" }, { 0x03, "Laptop" }, { 0x04, "Handheld PC/PDA (clam shell)" }, { 0x05, "Palm sized PC/PDA" }, { 0x06, "Wearable computer (Watch sized)" }, { 0x07, "Tablet" }, { } }; static const char *major_class_computer(uint8_t minor) { int i; for (i = 0; major_class_computer_table[i].str; i++) { if (major_class_computer_table[i].val == minor) return major_class_computer_table[i].str; } return NULL; } static const struct { uint8_t val; const char *str; } major_class_phone_table[] = { { 0x00, "Uncategorized, code for device not assigned" }, { 0x01, "Cellular" }, { 0x02, "Cordless" }, { 0x03, "Smart phone" }, { 0x04, "Wired modem or voice gateway" }, { 0x05, "Common ISDN Access" }, { } }; static const char *major_class_phone(uint8_t minor) { int i; for (i = 0; major_class_phone_table[i].str; i++) { if (major_class_phone_table[i].val == minor) return major_class_phone_table[i].str; } return NULL; } static const struct { uint8_t val; const char *str; } major_class_av_table[] = { { 0x00, "Uncategorized, code for device not assigned" }, { 0x01, "Wearable Headset Device" }, { 0x02, "Hands-free Device" }, { 0x04, "Microphone" }, { 0x05, "Loudspeaker" }, { 0x06, "Headphones" }, { 0x07, "Portable Audio" }, { 0x08, "Car audio" }, { 0x09, "Set-top box" }, { 0x0a, "HiFi Audio Device" }, { 0x0b, "VCR" }, { 0x0c, "Video Camera" }, { 0x0d, "Camcorder" }, { 0x0e, "Video Monitor" }, { 0x0f, "Video Display and Loudspeaker" }, { 0x10, "Video Conferencing" }, { 0x12, "Gaming/Toy" }, { } }; static const char *major_class_av(uint8_t minor) { int i; for (i = 0; major_class_av_table[i].str; i++) { if (major_class_av_table[i].val == minor) return major_class_av_table[i].str; } return NULL; } static const struct { uint8_t val; const char *str; } major_class_wearable_table[] = { { 0x01, "Wrist Watch" }, { 0x02, "Pager" }, { 0x03, "Jacket" }, { 0x04, "Helmet" }, { 0x05, "Glasses" }, { } }; static const char *major_class_wearable(uint8_t minor) { int i; for (i = 0; major_class_wearable_table[i].str; i++) { if (major_class_wearable_table[i].val == minor) return major_class_wearable_table[i].str; } return NULL; } static const struct { uint8_t val; const char *str; const char *(*func)(uint8_t minor); } major_class_table[] = { { 0x00, "Miscellaneous" }, { 0x01, "Computer (desktop, notebook, PDA, organizers)", major_class_computer }, { 0x02, "Phone (cellular, cordless, payphone, modem)", major_class_phone }, { 0x03, "LAN /Network Access point" }, { 0x04, "Audio/Video (headset, speaker, stereo, video, vcr)", major_class_av }, { 0x05, "Peripheral (mouse, joystick, keyboards)" }, { 0x06, "Imaging (printing, scanner, camera, display)" }, { 0x07, "Wearable", major_class_wearable }, { 0x08, "Toy" }, { 0x09, "Health" }, { 0x1f, "Uncategorized, specific device code not specified" }, { } }; static void print_dev_class(const uint8_t *dev_class) { uint8_t mask, major_cls, minor_cls; const char *major_str = NULL; const char *minor_str = NULL; int i; print_field("Class: 0x%2.2x%2.2x%2.2x", dev_class[2], dev_class[1], dev_class[0]); if ((dev_class[0] & 0x03) != 0x00) { print_field(" Format type: 0x%2.2x", dev_class[0] & 0x03); print_text(COLOR_ERROR, " invalid format type"); return; } major_cls = dev_class[1] & 0x1f; minor_cls = (dev_class[0] & 0xfc) >> 2; for (i = 0; major_class_table[i].str; i++) { if (major_class_table[i].val == major_cls) { major_str = major_class_table[i].str; if (!major_class_table[i].func) break; minor_str = major_class_table[i].func(minor_cls); break; } } if (major_str) { print_field(" Major class: %s", major_str); if (minor_str) print_field(" Minor class: %s", minor_str); else print_field(" Minor class: 0x%2.2x", minor_cls); } else { print_field(" Major class: 0x%2.2x", major_cls); print_field(" Minor class: 0x%2.2x", minor_cls); } if (dev_class[1] & 0x20) print_field(" Limited Discoverable Mode"); if ((dev_class[1] & 0xc0) != 0x00) { print_text(COLOR_ERROR, " invalid service class"); return; } mask = print_bitfield(2, dev_class[2], svc_class_table); if (mask) print_text(COLOR_UNKNOWN_SERVICE_CLASS, " Unknown service class (0x%2.2x)", mask); } static void print_appearance(uint16_t appearance) { print_field("Appearance: %s (0x%4.4x)", bt_appear_to_str(appearance), appearance); } static void print_num_broadcast_retrans(uint8_t num_retrans) { print_field("Number of broadcast retransmissions: %u", num_retrans); } static void print_hold_mode_activity(uint8_t activity) { print_field("Activity: 0x%2.2x", activity); if (activity == 0x00) { print_field(" Maintain current Power State"); return; } if (activity & 0x01) print_field(" Suspend Page Scan"); if (activity & 0x02) print_field(" Suspend Inquiry Scan"); if (activity & 0x04) print_field(" Suspend Periodic Inquiries"); } static void print_power_type(uint8_t type) { const char *str; switch (type) { case 0x00: str = "Current Transmit Power Level"; break; case 0x01: str = "Maximum Transmit Power Level"; break; default: str = "Reserved"; break; } print_field("Type: %s (0x%2.2x)", str, type); } static void print_power_level(int8_t level, const char *type) { print_field("TX power%s%s%s: %d dbm (0x%2.2x)", type ? " (" : "", type ? type : "", type ? ")" : "", level, (uint8_t) level); } static void print_host_flow_control(uint8_t enable) { const char *str; switch (enable) { case 0x00: str = "Off"; break; case 0x01: str = "ACL Data Packets"; break; case 0x02: str = "Synchronous Data Packets"; break; case 0x03: str = "ACL and Synchronous Data Packets"; break; default: str = "Reserved"; break; } print_field("Flow control: %s (0x%2.2x)", str, enable); } static void print_voice_setting(uint16_t setting) { uint8_t input_coding = (le16_to_cpu(setting) & 0x0300) >> 8; uint8_t input_data_format = (le16_to_cpu(setting) & 0xc0) >> 6; uint8_t air_coding_format = le16_to_cpu(setting) & 0x0003; const char *str; print_field("Setting: 0x%4.4x", le16_to_cpu(setting)); switch (input_coding) { case 0x00: str = "Linear"; break; case 0x01: str = "u-law"; break; case 0x02: str = "A-law"; break; default: str = "Reserved"; break; } print_field(" Input Coding: %s", str); switch (input_data_format) { case 0x00: str = "1's complement"; break; case 0x01: str = "2's complement"; break; case 0x02: str = "Sign-Magnitude"; break; case 0x03: str = "Unsigned"; break; default: str = "Reserved"; break; } print_field(" Input Data Format: %s", str); if (input_coding == 0x00) { print_field(" Input Sample Size: %s", le16_to_cpu(setting) & 0x20 ? "16-bit" : "8-bit"); print_field(" # of bits padding at MSB: %d", (le16_to_cpu(setting) & 0x1c) >> 2); } switch (air_coding_format) { case 0x00: str = "CVSD"; break; case 0x01: str = "u-law"; break; case 0x02: str = "A-law"; break; case 0x03: str = "Transparent Data"; break; default: str = "Reserved"; break; } print_field(" Air Coding Format: %s", str); } static void print_retransmission_effort(uint8_t effort) { const char *str; switch (effort) { case 0x00: str = "No retransmissions"; break; case 0x01: str = "Optimize for power consumption"; break; case 0x02: str = "Optimize for link quality"; break; case 0xff: str = "Don't care"; break; default: str = "Reserved"; break; } print_field("Retransmission effort: %s (0x%2.2x)", str, effort); } static void print_scan_enable(uint8_t scan_enable) { const char *str; switch (scan_enable) { case 0x00: str = "No Scans"; break; case 0x01: str = "Inquiry Scan"; break; case 0x02: str = "Page Scan"; break; case 0x03: str = "Inquiry Scan + Page Scan"; break; default: str = "Reserved"; break; } print_field("Scan enable: %s (0x%2.2x)", str, scan_enable); } static void print_link_policy(uint16_t link_policy) { uint16_t policy = le16_to_cpu(link_policy); print_field("Link policy: 0x%4.4x", policy); if (policy == 0x0000) { print_field(" Disable All Modes"); return; } if (policy & 0x0001) print_field(" Enable Role Switch"); if (policy & 0x0002) print_field(" Enable Hold Mode"); if (policy & 0x0004) print_field(" Enable Sniff Mode"); if (policy & 0x0008) print_field(" Enable Park State"); } static void print_air_mode(uint8_t mode) { const char *str; switch (mode) { case 0x00: str = "u-law log"; break; case 0x01: str = "A-law log"; break; case 0x02: str = "CVSD"; break; case 0x03: str = "Transparent"; break; default: str = "Reserved"; break; } print_field("Air mode: %s (0x%2.2x)", str, mode); } static void print_codec_id(const char *label, uint8_t codec) { const char *str; switch (codec) { case 0x00: str = "u-law log"; break; case 0x01: str = "A-law log"; break; case 0x02: str = "CVSD"; break; case 0x03: str = "Transparent"; break; case 0x04: str = "Linear PCM"; break; case 0x05: str = "mSBC"; break; case 0x06: str = "LC3"; break; case 0xff: str = "Vendor specific"; break; default: str = "Reserved"; break; } print_field("%s: %s (0x%2.2x)", label, str, codec); } void packet_print_codec_id(const char *label, uint8_t codec) { print_codec_id(label, codec); } static const struct bitfield_data codec_transport_table[] = { { 0, "Codec supported over BR/EDR ACL" }, { 1, "Codec supported over BR/EDR SCO and eSCO"}, { 2, "Codec supported over LE CIS" }, { 3, "Codec supported over LE BIS" }, { } }; static void print_codec(const char *label, const struct bt_hci_codec *codec) { uint8_t mask; print_codec_id(label, codec->id); print_field(" Logical Transport Type: 0x%02x", codec->transport); mask = print_bitfield(4, codec->transport, codec_transport_table); if (mask) print_text(COLOR_UNKNOWN_SERVICE_CLASS, " Unknown transport (0x%2.2x)", mask); } static void print_inquiry_mode(uint8_t mode) { const char *str; switch (mode) { case 0x00: str = "Standard Inquiry Result"; break; case 0x01: str = "Inquiry Result with RSSI"; break; case 0x02: str = "Inquiry Result with RSSI or Extended Inquiry Result"; break; default: str = "Reserved"; break; } print_field("Mode: %s (0x%2.2x)", str, mode); } static void print_inquiry_scan_type(uint8_t type) { const char *str; switch (type) { case 0x00: str = "Standard Scan"; break; case 0x01: str = "Interlaced Scan"; break; default: str = "Reserved"; break; } print_field("Type: %s (0x%2.2x)", str, type); } static void print_pscan_type(uint8_t type) { const char *str; switch (type) { case 0x00: str = "Standard Scan"; break; case 0x01: str = "Interlaced Scan"; break; default: str = "Reserved"; break; } print_field("Type: %s (0x%2.2x)", str, type); } static void print_loopback_mode(uint8_t mode) { const char *str; switch (mode) { case 0x00: str = "No Loopback"; break; case 0x01: str = "Local Loopback"; break; case 0x02: str = "Remote Loopback"; break; default: str = "Reserved"; break; } print_field("Mode: %s (0x%2.2x)", str, mode); } static void print_auth_payload_timeout(uint16_t timeout) { print_field("Timeout: %d msec (0x%4.4x)", le16_to_cpu(timeout) * 10, le16_to_cpu(timeout)); } static void print_pscan_rep_mode(uint8_t pscan_rep_mode) { const char *str; switch (pscan_rep_mode) { case 0x00: str = "R0"; break; case 0x01: str = "R1"; break; case 0x02: str = "R2"; break; default: str = "Reserved"; break; } print_field("Page scan repetition mode: %s (0x%2.2x)", str, pscan_rep_mode); } static void print_pscan_period_mode(uint8_t pscan_period_mode) { const char *str; switch (pscan_period_mode) { case 0x00: str = "P0"; break; case 0x01: str = "P1"; break; case 0x02: str = "P2"; break; default: str = "Reserved"; break; } print_field("Page period mode: %s (0x%2.2x)", str, pscan_period_mode); } static void print_pscan_mode(uint8_t pscan_mode) { const char *str; switch (pscan_mode) { case 0x00: str = "Mandatory"; break; case 0x01: str = "Optional I"; break; case 0x02: str = "Optional II"; break; case 0x03: str = "Optional III"; break; default: str = "Reserved"; break; } print_field("Page scan mode: %s (0x%2.2x)", str, pscan_mode); } static void print_clock_offset(uint16_t clock_offset) { print_field("Clock offset: 0x%4.4x", le16_to_cpu(clock_offset)); } static void print_clock(uint32_t clock) { print_field("Clock: 0x%8.8x", le32_to_cpu(clock)); } static void print_clock_type(uint8_t type) { const char *str; switch (type) { case 0x00: str = "Local clock"; break; case 0x01: str = "Piconet clock"; break; default: str = "Reserved"; break; } print_field("Type: %s (0x%2.2x)", str, type); } static void print_clock_accuracy(uint16_t accuracy) { if (le16_to_cpu(accuracy) == 0xffff) print_field("Accuracy: Unknown (0x%4.4x)", le16_to_cpu(accuracy)); else print_field("Accuracy: %.4f msec (0x%4.4x)", le16_to_cpu(accuracy) * 0.3125, le16_to_cpu(accuracy)); } static void print_lpo_allowed(uint8_t lpo_allowed) { print_field("LPO allowed: 0x%2.2x", lpo_allowed); } static void print_broadcast_fragment(uint8_t fragment) { const char *str; switch (fragment) { case 0x00: str = "Continuation fragment"; break; case 0x01: str = "Starting fragment"; break; case 0x02: str = "Ending fragment"; break; case 0x03: str = "No fragmentation"; break; default: str = "Reserved"; break; } print_field("Fragment: %s (0x%2.2x)", str, fragment); } static void print_link_type(uint8_t link_type) { const char *str; switch (link_type) { case 0x00: str = "SCO"; break; case 0x01: str = "ACL"; break; case 0x02: str = "eSCO"; break; default: str = "Reserved"; break; } print_field("Link type: %s (0x%2.2x)", str, link_type); } static void print_encr_mode_change(uint8_t encr_mode, uint16_t handle) { const char *str; uint8_t conn_type; conn_type = get_type(le16_to_cpu(handle)); switch (encr_mode) { case 0x00: str = "Disabled"; break; case 0x01: switch (conn_type) { case 0x00: str = "Enabled with E0"; break; case 0x01: str = "Enabled with AES-CCM"; break; default: str = "Enabled"; break; } break; case 0x02: str = "Enabled with AES-CCM"; break; default: str = "Reserved"; break; } print_field("Encryption: %s (0x%2.2x)", str, encr_mode); } static void print_pin_type(uint8_t pin_type) { const char *str; switch (pin_type) { case 0x00: str = "Variable"; break; case 0x01: str = "Fixed"; break; default: str = "Reserved"; break; } print_field("PIN type: %s (0x%2.2x)", str, pin_type); } static void print_key_flag(uint8_t key_flag) { const char *str; switch (key_flag) { case 0x00: str = "Semi-permanent"; break; case 0x01: str = "Temporary"; break; default: str = "Reserved"; break; } print_field("Key flag: %s (0x%2.2x)", str, key_flag); } static void print_key_len(uint8_t key_len) { const char *str; switch (key_len) { case 32: str = "802.11 PAL"; break; default: str = "Reserved"; break; } print_field("Key length: %s (%d)", str, key_len); } static void print_key_type(uint8_t key_type) { const char *str; switch (key_type) { case 0x00: str = "Combination key"; break; case 0x01: str = "Local Unit key"; break; case 0x02: str = "Remote Unit key"; break; case 0x03: str = "Debug Combination key"; break; case 0x04: str = "Unauthenticated Combination key from P-192"; break; case 0x05: str = "Authenticated Combination key from P-192"; break; case 0x06: str = "Changed Combination key"; break; case 0x07: str = "Unauthenticated Combination key from P-256"; break; case 0x08: str = "Authenticated Combination key from P-256"; break; default: str = "Reserved"; break; } print_field("Key type: %s (0x%2.2x)", str, key_type); } static void print_key_size(uint8_t key_size) { print_field("Key size: %d", key_size); } static void print_key(const char *label, const uint8_t *link_key) { print_hex_field(label, link_key, 16); } static void print_link_key(const uint8_t *link_key) { print_key("Link key", link_key); } static void print_pin_code(const uint8_t *pin_code, uint8_t pin_len) { char str[pin_len + 1]; uint8_t i; for (i = 0; i < pin_len; i++) sprintf(str + i, "%c", (const char) pin_code[i]); print_field("PIN code: %s", str); } static void print_hash_p192(const uint8_t *hash) { print_key("Hash C from P-192", hash); } static void print_hash_p256(const uint8_t *hash) { print_key("Hash C from P-256", hash); } static void print_randomizer_p192(const uint8_t *randomizer) { print_key("Randomizer R with P-192", randomizer); } static void print_randomizer_p256(const uint8_t *randomizer) { print_key("Randomizer R with P-256", randomizer); } static void print_pk256(const char *label, const uint8_t *key) { print_field("%s:", label); print_hex_field(" X", &key[0], 32); print_hex_field(" Y", &key[32], 32); } static void print_dhkey(const uint8_t *dhkey) { print_hex_field("Diffie-Hellman key", dhkey, 32); } static void print_passkey(uint32_t passkey) { print_field("Passkey: %06d", le32_to_cpu(passkey)); } static void print_io_capability(uint8_t capability) { const char *str; switch (capability) { case 0x00: str = "DisplayOnly"; break; case 0x01: str = "DisplayYesNo"; break; case 0x02: str = "KeyboardOnly"; break; case 0x03: str = "NoInputNoOutput"; break; default: str = "Reserved"; break; } print_field("IO capability: %s (0x%2.2x)", str, capability); } static void print_oob_data(uint8_t oob_data) { const char *str; switch (oob_data) { case 0x00: str = "Authentication data not present"; break; case 0x01: str = "P-192 authentication data present"; break; case 0x02: str = "P-256 authentication data present"; break; case 0x03: str = "P-192 and P-256 authentication data present"; break; default: str = "Reserved"; break; } print_field("OOB data: %s (0x%2.2x)", str, oob_data); } static void print_oob_data_response(uint8_t oob_data) { const char *str; switch (oob_data) { case 0x00: str = "Authentication data not present"; break; case 0x01: str = "Authentication data present"; break; default: str = "Reserved"; break; } print_field("OOB data: %s (0x%2.2x)", str, oob_data); } static void print_authentication(uint8_t authentication) { const char *str; switch (authentication) { case 0x00: str = "No Bonding - MITM not required"; break; case 0x01: str = "No Bonding - MITM required"; break; case 0x02: str = "Dedicated Bonding - MITM not required"; break; case 0x03: str = "Dedicated Bonding - MITM required"; break; case 0x04: str = "General Bonding - MITM not required"; break; case 0x05: str = "General Bonding - MITM required"; break; default: str = "Reserved"; break; } print_field("Authentication: %s (0x%2.2x)", str, authentication); } void packet_print_io_capability(uint8_t capability) { print_io_capability(capability); } void packet_print_io_authentication(uint8_t authentication) { print_authentication(authentication); } static void print_location_domain_aware(uint8_t aware) { const char *str; switch (aware) { case 0x00: str = "Regulatory domain unknown"; break; case 0x01: str = "Regulatory domain known"; break; default: str = "Reserved"; break; } print_field("Domain aware: %s (0x%2.2x)", str, aware); } static void print_location_domain(const uint8_t *domain) { print_field("Domain: %c%c (0x%2.2x%2.2x)", (char) domain[0], (char) domain[1], domain[0], domain[1]); } static void print_location_domain_options(uint8_t options) { print_field("Domain options: %c (0x%2.2x)", (char) options, options); } static void print_location_options(uint8_t options) { print_field("Options: 0x%2.2x", options); } static void print_flow_control_mode(uint8_t mode) { const char *str; switch (mode) { case 0x00: str = "Packet based"; break; case 0x01: str = "Data block based"; break; default: str = "Reserved"; break; } print_field("Flow control mode: %s (0x%2.2x)", str, mode); } static void print_flow_direction(uint8_t direction) { const char *str; switch (direction) { case 0x00: str = "Outgoing"; break; case 0x01: str = "Incoming"; break; default: str = "Reserved"; break; } print_field("Flow direction: %s (0x%2.2x)", str, direction); } static void print_service_type(uint8_t service_type) { const char *str; switch (service_type) { case 0x00: str = "No Traffic"; break; case 0x01: str = "Best Effort"; break; case 0x02: str = "Guaranteed"; break; default: str = "Reserved"; break; } print_field("Service type: %s (0x%2.2x)", str, service_type); } static void print_flow_spec(const char *label, const uint8_t *data) { const char *str; switch (data[1]) { case 0x00: str = "No traffic"; break; case 0x01: str = "Best effort"; break; case 0x02: str = "Guaranteed"; break; default: str = "Reserved"; break; } print_field("%s flow spec: 0x%2.2x", label, data[0]); print_field(" Service type: %s (0x%2.2x)", str, data[1]); print_field(" Maximum SDU size: 0x%4.4x", get_le16(data + 2)); print_field(" SDU inter-arrival time: 0x%8.8x", get_le32(data + 4)); print_field(" Access latency: 0x%8.8x", get_le32(data + 8)); print_field(" Flush timeout: 0x%8.8x", get_le32(data + 12)); } static void print_amp_status(uint8_t amp_status) { const char *str; switch (amp_status) { case 0x00: str = "Present"; break; case 0x01: str = "Bluetooth only"; break; case 0x02: str = "No capacity"; break; case 0x03: str = "Low capacity"; break; case 0x04: str = "Medium capacity"; break; case 0x05: str = "High capacity"; break; case 0x06: str = "Full capacity"; break; default: str = "Reserved"; break; } print_field("AMP status: %s (0x%2.2x)", str, amp_status); } static void print_num_resp(uint8_t num_resp) { print_field("Num responses: %d", num_resp); } static void print_num_reports(uint8_t num_reports) { print_field("Num reports: %d", num_reports); } static void print_adv_event_type(const char *label, uint8_t type) { const char *str; switch (type) { case 0x00: str = "Connectable undirected - ADV_IND"; break; case 0x01: str = "Connectable directed - ADV_DIRECT_IND"; break; case 0x02: str = "Scannable undirected - ADV_SCAN_IND"; break; case 0x03: str = "Non connectable undirected - ADV_NONCONN_IND"; break; case 0x04: str = "Scan response - SCAN_RSP"; break; default: str = "Reserved"; break; } print_field("%s: %s (0x%2.2x)", label, str, type); } static void print_adv_channel_map(const char *label, uint8_t value) { const char *str; switch (value) { case 0x01: str = "37"; break; case 0x02: str = "38"; break; case 0x03: str = "37, 38"; break; case 0x04: str = "39"; break; case 0x05: str = "37, 39"; break; case 0x06: str = "38, 39"; break; case 0x07: str = "37, 38, 39"; break; default: str = "Reserved"; break; } print_field("%s: %s (0x%2.2x)", label, str, value); } static void print_adv_filter_policy(const char *label, uint8_t value) { const char *str; switch (value) { case 0x00: str = "Allow Scan Request from Any, " "Allow Connect Request from Any"; break; case 0x01: str = "Allow Scan Request from Accept List Only, " "Allow Connect Request from Any"; break; case 0x02: str = "Allow Scan Request from Any, " "Allow Connect Request from Accept List Only"; break; case 0x03: str = "Allow Scan Request from Accept List Only, " "Allow Connect Request from Accept List Only"; break; default: str = "Reserved"; break; } print_field("%s: %s (0x%2.2x)", label, str, value); } static void print_rssi(int8_t rssi) { packet_print_rssi("RSSI", rssi); } static void print_slot_625(const char *label, uint16_t value) { print_field("%s: %.3f msec (0x%4.4x)", label, le16_to_cpu(value) * 0.625, le16_to_cpu(value)); } static void print_slot_125(const char *label, uint16_t value) { print_field("%s: %.2f msec (0x%4.4x)", label, le16_to_cpu(value) * 1.25, le16_to_cpu(value)); } static void print_timeout(uint16_t timeout) { print_slot_625("Timeout", timeout); } static void print_interval(uint16_t interval) { print_slot_625("Interval", interval); } static void print_window(uint16_t window) { print_slot_625("Window", window); } static void print_conn_latency(const char *label, uint16_t value) { print_field("%s: %u (0x%4.4x)", label, le16_to_cpu(value), le16_to_cpu(value)); } static void print_role(uint8_t role) { const char *str; switch (role) { case 0x00: str = "Central"; break; case 0x01: str = "Peripheral"; break; default: str = "Reserved"; break; } print_field("Role: %s (0x%2.2x)", str, role); } static void print_mode(uint8_t mode) { const char *str; switch (mode) { case 0x00: str = "Active"; break; case 0x01: str = "Hold"; break; case 0x02: str = "Sniff"; break; case 0x03: str = "Park"; break; default: str = "Reserved"; break; } print_field("Mode: %s (0x%2.2x)", str, mode); } static void print_name(const uint8_t *name) { char str[249]; memcpy(str, name, 248); str[248] = '\0'; print_field("Name: %s", str); } static void print_channel_map(const uint8_t *map) { unsigned int count = 0, start = 0; char str[21]; int i, n; for (i = 0; i < 10; i++) sprintf(str + (i * 2), "%2.2x", map[i]); print_field("Channel map: 0x%s", str); for (i = 0; i < 10; i++) { for (n = 0; n < 8; n++) { if (map[i] & (1 << n)) { if (count == 0) start = (i * 8) + n; count++; continue; } if (count > 1) { print_field(" Channel %u-%u", start, start + count - 1); count = 0; } else if (count > 0) { print_field(" Channel %u", start); count = 0; } } } } void packet_print_channel_map_lmp(const uint8_t *map) { print_channel_map(map); } static void print_flush_timeout(uint16_t timeout) { if (timeout) print_timeout(timeout); else print_field("Timeout: No Automatic Flush"); } void packet_print_version(const char *label, uint8_t version, const char *sublabel, uint16_t subversion) { const char *str; switch (version) { case 0x00: str = "Bluetooth 1.0b"; break; case 0x01: str = "Bluetooth 1.1"; break; case 0x02: str = "Bluetooth 1.2"; break; case 0x03: str = "Bluetooth 2.0"; break; case 0x04: str = "Bluetooth 2.1"; break; case 0x05: str = "Bluetooth 3.0"; break; case 0x06: str = "Bluetooth 4.0"; break; case 0x07: str = "Bluetooth 4.1"; break; case 0x08: str = "Bluetooth 4.2"; break; case 0x09: str = "Bluetooth 5.0"; break; case 0x0a: str = "Bluetooth 5.1"; break; case 0x0b: str = "Bluetooth 5.2"; break; case 0x0c: str = "Bluetooth 5.3"; break; case 0x0d: str = "Bluetooth 5.4"; break; default: str = "Reserved"; break; } if (sublabel) print_field("%s: %s (0x%2.2x) - %s %d (0x%4.4x)", label, str, version, sublabel, subversion, subversion); else print_field("%s: %s (0x%2.2x)", label, str, version); } static void print_hci_version(uint8_t version, uint16_t revision) { packet_print_version("HCI version", version, "Revision", le16_to_cpu(revision)); } static void print_lmp_version(uint8_t version, uint16_t subversion) { packet_print_version("LMP version", version, "Subversion", le16_to_cpu(subversion)); } static void print_pal_version(uint8_t version, uint16_t subversion) { const char *str; switch (version) { case 0x01: str = "Bluetooth 3.0"; break; default: str = "Reserved"; break; } print_field("PAL version: %s (0x%2.2x) - Subversion %d (0x%4.4x)", str, version, le16_to_cpu(subversion), le16_to_cpu(subversion)); } void packet_print_company(const char *label, uint16_t company) { print_field("%s: %s (%d)", label, bt_compidtostr(company), company); } static void print_manufacturer(uint16_t manufacturer) { packet_print_company("Manufacturer", le16_to_cpu(manufacturer)); } static const struct { uint16_t ver; const char *str; } broadcom_uart_subversion_table[] = { { 0x210b, "BCM43142A0" }, /* 001.001.011 */ { 0x410e, "BCM43341B0" }, /* 002.001.014 */ { 0x4406, "BCM4324B3" }, /* 002.004.006 */ { } }; static const struct { uint16_t ver; const char *str; } broadcom_usb_subversion_table[] = { { 0x210b, "BCM43142A0" }, /* 001.001.011 */ { 0x2112, "BCM4314A0" }, /* 001.001.018 */ { 0x2118, "BCM20702A0" }, /* 001.001.024 */ { 0x2126, "BCM4335A0" }, /* 001.001.038 */ { 0x220e, "BCM20702A1" }, /* 001.002.014 */ { 0x230f, "BCM4354A2" }, /* 001.003.015 */ { 0x4106, "BCM4335B0" }, /* 002.001.006 */ { 0x410e, "BCM20702B0" }, /* 002.001.014 */ { 0x6109, "BCM4335C0" }, /* 003.001.009 */ { 0x610c, "BCM4354" }, /* 003.001.012 */ { } }; static void print_manufacturer_broadcom(uint16_t subversion, uint16_t revision) { uint16_t ver = le16_to_cpu(subversion); uint16_t rev = le16_to_cpu(revision); const char *str = NULL; int i; switch ((rev & 0xf000) >> 12) { case 0: case 3: for (i = 0; broadcom_uart_subversion_table[i].str; i++) { if (broadcom_uart_subversion_table[i].ver == ver) { str = broadcom_uart_subversion_table[i].str; break; } } break; case 1: case 2: for (i = 0; broadcom_usb_subversion_table[i].str; i++) { if (broadcom_usb_subversion_table[i].ver == ver) { str = broadcom_usb_subversion_table[i].str; break; } } break; } if (str) print_field(" Firmware: %3.3u.%3.3u.%3.3u (%s)", (ver & 0xe000) >> 13, (ver & 0x1f00) >> 8, ver & 0x00ff, str); else print_field(" Firmware: %3.3u.%3.3u.%3.3u", (ver & 0xe000) >> 13, (ver & 0x1f00) >> 8, ver & 0x00ff); if (rev != 0xffff) print_field(" Build: %4.4u", rev & 0x0fff); } static const char *get_supported_command(int bit); static void print_commands(const uint8_t *commands) { unsigned int count = 0; int i, n; for (i = 0; i < 64; i++) { for (n = 0; n < 8; n++) { if (commands[i] & (1 << n)) count++; } } print_field("Commands: %u entr%s", count, count == 1 ? "y" : "ies"); for (i = 0; i < 64; i++) { for (n = 0; n < 8; n++) { const char *cmd; if (!(commands[i] & (1 << n))) continue; cmd = get_supported_command((i * 8) + n); if (cmd) print_field(" %s (Octet %d - Bit %d)", cmd, i, n); else print_text(COLOR_UNKNOWN_COMMAND_BIT, " Octet %d - Bit %d ", i, n); } } } static const struct bitfield_data features_page0[] = { { 0, "3 slot packets" }, { 1, "5 slot packets" }, { 2, "Encryption" }, { 3, "Slot offset" }, { 4, "Timing accuracy" }, { 5, "Role switch" }, { 6, "Hold mode" }, { 7, "Sniff mode" }, { 8, "Park state" }, { 9, "Power control requests" }, { 10, "Channel quality driven data rate (CQDDR)"}, { 11, "SCO link" }, { 12, "HV2 packets" }, { 13, "HV3 packets" }, { 14, "u-law log synchronous data" }, { 15, "A-law log synchronous data" }, { 16, "CVSD synchronous data" }, { 17, "Paging parameter negotiation" }, { 18, "Power control" }, { 19, "Transparent synchronous data" }, { 20, "Flow control lag (least significant bit)"}, { 21, "Flow control lag (middle bit)" }, { 22, "Flow control lag (most significant bit)" }, { 23, "Broadcast Encryption" }, { 25, "Enhanced Data Rate ACL 2 Mbps mode" }, { 26, "Enhanced Data Rate ACL 3 Mbps mode" }, { 27, "Enhanced inquiry scan" }, { 28, "Interlaced inquiry scan" }, { 29, "Interlaced page scan" }, { 30, "RSSI with inquiry results" }, { 31, "Extended SCO link (EV3 packets)" }, { 32, "EV4 packets" }, { 33, "EV5 packets" }, { 35, "AFH capable peripheral" }, { 36, "AFH classification peripheral" }, { 37, "BR/EDR Not Supported" }, { 38, "LE Supported (Controller)" }, { 39, "3-slot Enhanced Data Rate ACL packets" }, { 40, "5-slot Enhanced Data Rate ACL packets" }, { 41, "Sniff subrating" }, { 42, "Pause encryption" }, { 43, "AFH capable central" }, { 44, "AFH classification central" }, { 45, "Enhanced Data Rate eSCO 2 Mbps mode" }, { 46, "Enhanced Data Rate eSCO 3 Mbps mode" }, { 47, "3-slot Enhanced Data Rate eSCO packets" }, { 48, "Extended Inquiry Response" }, { 49, "Simultaneous LE and BR/EDR (Controller)" }, { 51, "Secure Simple Pairing" }, { 52, "Encapsulated PDU" }, { 53, "Erroneous Data Reporting" }, { 54, "Non-flushable Packet Boundary Flag" }, { 56, "Link Supervision Timeout Changed Event" }, { 57, "Inquiry TX Power Level" }, { 58, "Enhanced Power Control" }, { 63, "Extended features" }, { } }; static const struct bitfield_data features_page1[] = { { 0, "Secure Simple Pairing (Host Support)" }, { 1, "LE Supported (Host)" }, { 2, "Simultaneous LE and BR/EDR (Host)" }, { 3, "Secure Connections (Host Support)" }, { } }; static const struct bitfield_data features_page2[] = { { 0, "Connectionless Peripheral Broadcast - Central" }, { 1, "Connectionless Peripheral Broadcast - Peripheral"}, { 2, "Synchronization Train" }, { 3, "Synchronization Scan" }, { 4, "Inquiry Response Notification Event" }, { 5, "Generalized interlaced scan" }, { 6, "Coarse Clock Adjustment" }, { 8, "Secure Connections (Controller Support)" }, { 9, "Ping" }, { 10, "Slot Availability Mask" }, { 11, "Train nudging" }, { } }; static const struct bitfield_data features_le[] = { { 0, "LE Encryption" }, { 1, "Connection Parameter Request Procedure" }, { 2, "Extended Reject Indication" }, { 3, "Peripheral-initiated Features Exchange" }, { 4, "LE Ping" }, { 5, "LE Data Packet Length Extension" }, { 6, "LL Privacy" }, { 7, "Extended Scanner Filter Policies" }, { 8, "LE 2M PHY" }, { 9, "Stable Modulation Index - Transmitter" }, { 10, "Stable Modulation Index - Receiver" }, { 11, "LE Coded PHY" }, { 12, "LE Extended Advertising" }, { 13, "LE Periodic Advertising" }, { 14, "Channel Selection Algorithm #2" }, { 15, "LE Power Class 1" }, { 16, "Minimum Number of Used Channels Procedure" }, { 17, "Connection CTE Request" }, { 18, "Connection CTE Response" }, { 19, "Connectionless CTE Transmitter" }, { 20, "Connectionless CTE Receiver" }, { 21, "Antenna Switching During CTE Transmission (AoD)" }, { 22, "Antenna Switching During CTE Reception (AoA)" }, { 23, "Receiving Constant Tone Extensions" }, { 24, "Periodic Advertising Sync Transfer - Sender" }, { 25, "Periodic Advertising Sync Transfer - Recipient" }, { 26, "Sleep Clock Accuracy Updates" }, { 27, "Remote Public Key Validation" }, { 28, "Connected Isochronous Stream - Central" }, { 29, "Connected Isochronous Stream - Peripheral" }, { 30, "Isochronous Broadcaster" }, { 31, "Synchronized Receiver" }, { 32, "Connected Isochronous Stream (Host Support)" }, { 33, "LE Power Control Request" }, { 34, "LE Power Control Request" }, { 35, "LE Path Loss Monitoring" }, { 36, "Periodic Advertising ADI support" }, { 37, "Connection Subrating" }, { 38, "Connection Subrating (Host Support)" }, { 39, "Channel Classification" }, { } }; static const struct bitfield_data features_msft[] = { { 0, "RSSI Monitoring feature for BR/EDR" }, { 1, "RSSI Monitoring feature for LE connections" }, { 2, "RSSI Monitoring of LE advertisements" }, { 3, "Advertising Monitoring of LE advertisements" }, { 4, "Verifying the validity of P-192 and P-256 keys" }, { 5, "Continuous Advertising Monitoring" }, { } }; static void print_features(uint8_t page, const uint8_t *features_array, uint8_t type) { const struct bitfield_data *features_table = NULL; uint64_t mask, features = 0; char str[41]; int i; for (i = 0; i < 8; i++) { sprintf(str + (i * 5), " 0x%2.2x", features_array[i]); features |= ((uint64_t) features_array[i]) << (i * 8); } print_field("Features:%s", str); switch (type) { case 0x00: switch (page) { case 0: features_table = features_page0; break; case 1: features_table = features_page1; break; case 2: features_table = features_page2; break; } break; case 0x01: switch (page) { case 0: features_table = features_le; break; } break; case 0xf0: switch (page) { case 0: features_table = features_msft; break; } break; } if (!features_table) return; mask = print_bitfield(2, features, features_table); if (mask) print_text(COLOR_UNKNOWN_FEATURE_BIT, " Unknown features " "(0x%16.16" PRIx64 ")", mask); } void packet_print_features_lmp(const uint8_t *features, uint8_t page) { print_features(page, features, 0x00); } void packet_print_features_ll(const uint8_t *features) { print_features(0, features, 0x01); } void packet_print_features_msft(const uint8_t *features) { print_features(0, features, 0xf0); } #define LE_STATE_SCAN_ADV 0x0001 #define LE_STATE_CONN_ADV 0x0002 #define LE_STATE_NONCONN_ADV 0x0004 #define LE_STATE_HIGH_DIRECT_ADV 0x0008 #define LE_STATE_LOW_DIRECT_ADV 0x0010 #define LE_STATE_ACTIVE_SCAN 0x0020 #define LE_STATE_PASSIVE_SCAN 0x0040 #define LE_STATE_INITIATING 0x0080 #define LE_STATE_CONN_CENTRAL 0x0100 #define LE_STATE_CONN_PERIPHERAL 0x0200 #define LE_STATE_CENTRAL_CENTRAL 0x0400 #define LE_STATE_PERIPHERAL_PERIPHERAL 0x0800 #define LE_STATE_CENTRAL_PERIPHERAL 0x1000 static const struct bitfield_data le_states_desc_table[] = { { 0, "Scannable Advertising State" }, { 1, "Connectable Advertising State" }, { 2, "Non-connectable Advertising State" }, { 3, "High Duty Cycle Directed Advertising State" }, { 4, "Low Duty Cycle Directed Advertising State" }, { 5, "Active Scanning State" }, { 6, "Passive Scanning State" }, { 7, "Initiating State" }, { 8, "Connection State (Central Role)" }, { 9, "Connection State (Peripheral Role)" }, { 10, "Central Role & Central Role" }, { 11, "Peripheral Role & Peripheral Role" }, { 12, "Central Role & Peripheral Role" }, { } }; static const struct { uint8_t bit; uint16_t states; } le_states_comb_table[] = { { 0, LE_STATE_NONCONN_ADV }, { 1, LE_STATE_SCAN_ADV }, { 2, LE_STATE_CONN_ADV }, { 3, LE_STATE_HIGH_DIRECT_ADV }, { 4, LE_STATE_PASSIVE_SCAN }, { 5, LE_STATE_ACTIVE_SCAN }, { 6, LE_STATE_INITIATING | LE_STATE_CONN_CENTRAL }, { 7, LE_STATE_CONN_PERIPHERAL }, { 8, LE_STATE_PASSIVE_SCAN | LE_STATE_NONCONN_ADV }, { 9, LE_STATE_PASSIVE_SCAN | LE_STATE_SCAN_ADV }, { 10, LE_STATE_PASSIVE_SCAN | LE_STATE_CONN_ADV }, { 11, LE_STATE_PASSIVE_SCAN | LE_STATE_HIGH_DIRECT_ADV }, { 12, LE_STATE_ACTIVE_SCAN | LE_STATE_NONCONN_ADV }, { 13, LE_STATE_ACTIVE_SCAN | LE_STATE_SCAN_ADV }, { 14, LE_STATE_ACTIVE_SCAN | LE_STATE_CONN_ADV }, { 15, LE_STATE_ACTIVE_SCAN | LE_STATE_HIGH_DIRECT_ADV }, { 16, LE_STATE_INITIATING | LE_STATE_NONCONN_ADV }, { 17, LE_STATE_INITIATING | LE_STATE_SCAN_ADV }, { 18, LE_STATE_CONN_CENTRAL | LE_STATE_NONCONN_ADV }, { 19, LE_STATE_CONN_CENTRAL | LE_STATE_SCAN_ADV }, { 20, LE_STATE_CONN_PERIPHERAL | LE_STATE_NONCONN_ADV }, { 21, LE_STATE_CONN_PERIPHERAL | LE_STATE_SCAN_ADV }, { 22, LE_STATE_INITIATING | LE_STATE_PASSIVE_SCAN }, { 23, LE_STATE_INITIATING | LE_STATE_ACTIVE_SCAN }, { 24, LE_STATE_CONN_CENTRAL | LE_STATE_PASSIVE_SCAN }, { 25, LE_STATE_CONN_CENTRAL | LE_STATE_ACTIVE_SCAN }, { 26, LE_STATE_CONN_PERIPHERAL | LE_STATE_PASSIVE_SCAN }, { 27, LE_STATE_CONN_PERIPHERAL | LE_STATE_ACTIVE_SCAN }, { 28, LE_STATE_INITIATING | LE_STATE_CONN_CENTRAL | LE_STATE_CENTRAL_CENTRAL }, { 29, LE_STATE_LOW_DIRECT_ADV }, { 30, LE_STATE_LOW_DIRECT_ADV | LE_STATE_PASSIVE_SCAN }, { 31, LE_STATE_LOW_DIRECT_ADV | LE_STATE_ACTIVE_SCAN }, { 32, LE_STATE_INITIATING | LE_STATE_CONN_ADV | LE_STATE_CENTRAL_PERIPHERAL }, { 33, LE_STATE_INITIATING | LE_STATE_HIGH_DIRECT_ADV | LE_STATE_CENTRAL_PERIPHERAL }, { 34, LE_STATE_INITIATING | LE_STATE_LOW_DIRECT_ADV | LE_STATE_CENTRAL_PERIPHERAL }, { 35, LE_STATE_CONN_CENTRAL | LE_STATE_CONN_ADV | LE_STATE_CENTRAL_PERIPHERAL }, { 36, LE_STATE_CONN_CENTRAL | LE_STATE_HIGH_DIRECT_ADV | LE_STATE_CENTRAL_PERIPHERAL }, { 37, LE_STATE_CONN_CENTRAL | LE_STATE_LOW_DIRECT_ADV | LE_STATE_CENTRAL_PERIPHERAL }, { 38, LE_STATE_CONN_PERIPHERAL | LE_STATE_CONN_ADV | LE_STATE_CENTRAL_PERIPHERAL }, { 39, LE_STATE_CONN_PERIPHERAL | LE_STATE_HIGH_DIRECT_ADV | LE_STATE_PERIPHERAL_PERIPHERAL }, { 40, LE_STATE_CONN_PERIPHERAL | LE_STATE_LOW_DIRECT_ADV | LE_STATE_PERIPHERAL_PERIPHERAL }, { 41, LE_STATE_INITIATING | LE_STATE_CONN_PERIPHERAL | LE_STATE_CENTRAL_PERIPHERAL }, { } }; static void print_le_states(const uint8_t *states_array) { uint64_t mask, states = 0; int i = 0; size_t n = 0; for (i = 0; i < 8; i++) states |= ((uint64_t) states_array[i]) << (i * 8); print_field("States: 0x%16.16" PRIx64, states); mask = states; for (i = 0; le_states_comb_table[i].states; i++) { uint64_t val = (((uint64_t) 1) << le_states_comb_table[i].bit); const char *str[3] = { NULL, }; size_t num = 0; if (!(states & val)) continue; for (n = 0; n < ARRAY_SIZE(le_states_desc_table); n++) { if (le_states_comb_table[i].states & (1 << n)) str[num++] = le_states_desc_table[n].str; } if (num > 0) { print_field(" %s", str[0]); for (n = 1; n < num; n++) print_field(" and %s", str[n]); } mask &= ~val; } if (mask) print_text(COLOR_UNKNOWN_LE_STATES, " Unknown states " "(0x%16.16" PRIx64 ")", mask); } static void print_le_channel_map(const uint8_t *map) { unsigned int count = 0, start = 0; char str[11]; int i, n; for (i = 0; i < 5; i++) sprintf(str + (i * 2), "%2.2x", map[i]); print_field("Channel map: 0x%s", str); for (i = 0; i < 5; i++) { for (n = 0; n < 8; n++) { if (map[i] & (1 << n)) { if (count == 0) start = (i * 8) + n; count++; continue; } if (count > 1) { print_field(" Channel %u-%u", start, start + count - 1); count = 0; } else if (count > 0) { print_field(" Channel %u", start); count = 0; } } } } void packet_print_channel_map_ll(const uint8_t *map) { print_le_channel_map(map); } static void print_random_number(uint64_t rand) { print_field("Random number: 0x%16.16" PRIx64, le64_to_cpu(rand)); } static void print_encrypted_diversifier(uint16_t ediv) { print_field("Encrypted diversifier: 0x%4.4x", le16_to_cpu(ediv)); } static const struct bitfield_data events_table[] = { { 0, "Inquiry Complete" }, { 1, "Inquiry Result" }, { 2, "Connection Complete" }, { 3, "Connection Request" }, { 4, "Disconnection Complete" }, { 5, "Authentication Complete" }, { 6, "Remote Name Request Complete" }, { 7, "Encryption Change" }, { 8, "Change Connection Link Key Complete" }, { 9, "Link Key Type Changed" }, { 10, "Read Remote Supported Features Complete" }, { 11, "Read Remote Version Information Complete" }, { 12, "QoS Setup Complete" }, { 13, "Command Complete" }, { 14, "Command Status" }, { 15, "Hardware Error" }, { 16, "Flush Occurred" }, { 17, "Role Change" }, { 18, "Number of Completed Packets" }, { 19, "Mode Change" }, { 20, "Return Link Keys" }, { 21, "PIN Code Request" }, { 22, "Link Key Request" }, { 23, "Link Key Notification" }, { 24, "Loopback Command" }, { 25, "Data Buffer Overflow" }, { 26, "Max Slots Change" }, { 27, "Read Clock Offset Complete" }, { 28, "Connection Packet Type Changed" }, { 29, "QoS Violation" }, { 30, "Page Scan Mode Change" }, { 31, "Page Scan Repetition Mode Change" }, { 32, "Flow Specification Complete" }, { 33, "Inquiry Result with RSSI" }, { 34, "Read Remote Extended Features Complete" }, { 43, "Synchronous Connection Complete" }, { 44, "Synchronous Connection Changed" }, { 45, "Sniff Subrating" }, { 46, "Extended Inquiry Result" }, { 47, "Encryption Key Refresh Complete" }, { 48, "IO Capability Request" }, { 49, "IO Capability Request Reply" }, { 50, "User Confirmation Request" }, { 51, "User Passkey Request" }, { 52, "Remote OOB Data Request" }, { 53, "Simple Pairing Complete" }, { 55, "Link Supervision Timeout Changed" }, { 56, "Enhanced Flush Complete" }, { 58, "User Passkey Notification" }, { 59, "Keypress Notification" }, { 60, "Remote Host Supported Features Notification" }, { 61, "LE Meta" }, { } }; static void print_event_mask(const uint8_t *events_array, const struct bitfield_data *table) { uint64_t mask, events = 0; int i; for (i = 0; i < 8; i++) events |= ((uint64_t) events_array[i]) << (i * 8); print_field("Mask: 0x%16.16" PRIx64, events); mask = print_bitfield(2, events, table); if (mask) print_text(COLOR_UNKNOWN_EVENT_MASK, " Unknown mask " "(0x%16.16" PRIx64 ")", mask); } static const struct bitfield_data events_page2_table[] = { { 0, "Physical Link Complete" }, { 1, "Channel Selected" }, { 2, "Disconnection Physical Link Complete" }, { 3, "Physical Link Loss Early Warning" }, { 4, "Physical Link Recovery" }, { 5, "Logical Link Complete" }, { 6, "Disconnection Logical Link Complete" }, { 7, "Flow Specification Modify Complete" }, { 8, "Number of Completed Data Blocks" }, { 9, "AMP Start Test" }, { 10, "AMP Test End" }, { 11, "AMP Receiver Report" }, { 12, "Short Range Mode Change Complete" }, { 13, "AMP Status Change" }, { 14, "Triggered Clock Capture" }, { 15, "Synchronization Train Complete" }, { 16, "Synchronization Train Received" }, { 17, "Connectionless Peripheral Broadcast Receive" }, { 18, "Connectionless Peripheral Broadcast Timeout" }, { 19, "Truncated Page Complete" }, { 20, "Peripheral Page Response Timeout" }, { 21, "Connectionless Peripheral Broadcast Channel Map Change" }, { 22, "Inquiry Response Notification" }, { 23, "Authenticated Payload Timeout Expired" }, { 24, "SAM Status Change" }, { } }; static const struct bitfield_data events_le_table[] = { { 0, "LE Connection Complete" }, { 1, "LE Advertising Report" }, { 2, "LE Connection Update Complete" }, { 3, "LE Read Remote Used Features Complete" }, { 4, "LE Long Term Key Request" }, { 5, "LE Remote Connection Parameter Request" }, { 6, "LE Data Length Change" }, { 7, "LE Read Local P-256 Public Key Complete" }, { 8, "LE Generate DHKey Complete" }, { 9, "LE Enhanced Connection Complete" }, { 10, "LE Direct Advertising Report" }, { 11, "LE PHY Update Complete" }, { 12, "LE Extended Advertising Report" }, { 13, "LE Periodic Advertising Sync Established"}, { 14, "LE Periodic Advertising Report" }, { 15, "LE Periodic Advertising Sync Lost" }, { 16, "LE Extended Scan Timeout" }, { 17, "LE Extended Advertising Set Terminated" }, { 18, "LE Scan Request Received" }, { 19, "LE Channel Selection Algorithm" }, { 20, "LE Connectionless IQ Report" }, { 21, "LE Connection IQ Report" }, { 22, "LE CTE Request Failed" }, { 23, "LE Periodic Advertising Sync Transfer Rvc"}, { 24, "LE CIS Established" }, { 25, "LE CIS Request" }, { 26, "LE Create BIG Complete" }, { 27, "LE Terminate BIG Complete" }, { 28, "LE BIG Sync Estabilished Complete" }, { 29, "LE BIG Sync Lost" }, { 30, "LE Request Peer SCA Complete"}, { 31, "LE Path Loss Threshold" }, { 32, "LE Transmit Power Reporting" }, { 33, "LE BIG Info Advertising Report" }, { 34, "LE Subrate Change" }, { } }; static void print_fec(uint8_t fec) { const char *str; switch (fec) { case 0x00: str = "Not required"; break; case 0x01: str = "Required"; break; default: str = "Reserved"; break; } print_field("FEC: %s (0x%02x)", str, fec); } #define BT_EIR_FLAGS 0x01 #define BT_EIR_UUID16_SOME 0x02 #define BT_EIR_UUID16_ALL 0x03 #define BT_EIR_UUID32_SOME 0x04 #define BT_EIR_UUID32_ALL 0x05 #define BT_EIR_UUID128_SOME 0x06 #define BT_EIR_UUID128_ALL 0x07 #define BT_EIR_NAME_SHORT 0x08 #define BT_EIR_NAME_COMPLETE 0x09 #define BT_EIR_TX_POWER 0x0a #define BT_EIR_CLASS_OF_DEV 0x0d #define BT_EIR_SSP_HASH_P192 0x0e #define BT_EIR_SSP_RANDOMIZER_P192 0x0f #define BT_EIR_DEVICE_ID 0x10 #define BT_EIR_SMP_TK 0x10 #define BT_EIR_SMP_OOB_FLAGS 0x11 #define BT_EIR_PERIPHERAL_CONN_INTERVAL 0x12 #define BT_EIR_SERVICE_UUID16 0x14 #define BT_EIR_SERVICE_UUID128 0x15 #define BT_EIR_SERVICE_DATA 0x16 #define BT_EIR_PUBLIC_ADDRESS 0x17 #define BT_EIR_RANDOM_ADDRESS 0x18 #define BT_EIR_GAP_APPEARANCE 0x19 #define BT_EIR_ADVERTISING_INTERVAL 0x1a #define BT_EIR_LE_DEVICE_ADDRESS 0x1b #define BT_EIR_LE_ROLE 0x1c #define BT_EIR_SSP_HASH_P256 0x1d #define BT_EIR_SSP_RANDOMIZER_P256 0x1e #define BT_EIR_SERVICE_UUID32 0x1f #define BT_EIR_SERVICE_DATA32 0x20 #define BT_EIR_SERVICE_DATA128 0x21 #define BT_EIR_LE_SC_CONFIRM_VALUE 0x22 #define BT_EIR_LE_SC_RANDOM_VALUE 0x23 #define BT_EIR_URI 0x24 #define BT_EIR_INDOOR_POSITIONING 0x25 #define BT_EIR_TRANSPORT_DISCOVERY 0x26 #define BT_EIR_LE_SUPPORTED_FEATURES 0x27 #define BT_EIR_CHANNEL_MAP_UPDATE_IND 0x28 #define BT_EIR_MESH_PROV 0x29 #define BT_EIR_MESH_DATA 0x2a #define BT_EIR_MESH_BEACON 0x2b #define BT_EIR_CSIP_RSI 0x2e #define BT_EIR_3D_INFO_DATA 0x3d #define BT_EIR_MANUFACTURER_DATA 0xff static void print_manufacturer_apple(const void *data, uint8_t data_len) { uint8_t type = *((uint8_t *) data); if (data_len < 1) return; if (type == 0x01) { char identifier[100]; snprintf(identifier, sizeof(identifier) - 1, "%s", (const char *) (data + 1)); print_field(" Identifier: %s", identifier); return; } while (data_len > 0) { uint8_t len; const char *str; type = *((uint8_t *) data); data++; data_len--; if (type == 0x00) continue; if (data_len < 1) break; switch (type) { case 0x02: str = "iBeacon"; break; case 0x05: str = "AirDrop"; break; case 0x09: str = "Apple TV"; break; default: str = "Unknown"; break; } print_field(" Type: %s (%u)", str, type); len = *((uint8_t *) data); data++; data_len--; if (len < 1) continue; if (len > data_len) break; if (type == 0x02 && len == 0x15) { const uint8_t *uuid; uint16_t minor, major; int8_t tx_power; uuid = data; print_field(" UUID: %8.8x-%4.4x-%4.4x-%4.4x-%8.8x%4.4x", get_le32(&uuid[12]), get_le16(&uuid[10]), get_le16(&uuid[8]), get_le16(&uuid[6]), get_le32(&uuid[2]), get_le16(&uuid[0])); major = get_le16(data + 16); minor = get_le16(data + 18); print_field(" Version: %u.%u", major, minor); tx_power = *(int8_t *) (data + 20); print_field(" TX power: %d dB", tx_power); } else print_hex_field(" Data", data, len); data += len; data_len -= len; } packet_hexdump(data, data_len); } static void print_manufacturer_data(const void *data, uint8_t data_len) { uint16_t company = get_le16(data); packet_print_company("Company", company); switch (company) { case 76: case 19456: print_manufacturer_apple(data + 2, data_len - 2); break; default: print_hex_field(" Data", data + 2, data_len - 2); break; } } static void print_device_id(const void *data, uint8_t data_len) { uint16_t source, vendor, product, version; char modalias[26], *vendor_str, *product_str; const char *str; if (data_len < 8) return; source = get_le16(data); vendor = get_le16(data + 2); product = get_le16(data + 4); version = get_le16(data + 6); switch (source) { case 0x0001: str = "Bluetooth SIG assigned"; sprintf(modalias, "bluetooth:v%04Xp%04Xd%04X", vendor, product, version); break; case 0x0002: str = "USB Implementer's Forum assigned"; sprintf(modalias, "usb:v%04Xp%04Xd%04X", vendor, product, version); break; default: str = "Reserved"; modalias[0] = '\0'; break; } print_field("Device ID: %s (0x%4.4x)", str, source); if (!hwdb_get_vendor_model(modalias, &vendor_str, &product_str)) { vendor_str = NULL; product_str = NULL; } if (source != 0x0001) { if (vendor_str) print_field(" Vendor: %s (0x%4.4x)", vendor_str, vendor); else print_field(" Vendor: 0x%4.4x", vendor); } else packet_print_company(" Vendor", vendor); if (product_str) print_field(" Product: %s (0x%4.4x)", product_str, product); else print_field(" Product: 0x%4.4x", product); print_field(" Version: %u.%u.%u (0x%4.4x)", (version & 0xff00) >> 8, (version & 0x00f0) >> 4, (version & 0x000f), version); free(vendor_str); free(product_str); } static void print_uuid16_list(const char *label, const void *data, uint8_t data_len) { uint8_t count = data_len / sizeof(uint16_t); unsigned int i; print_field("%s: %u entr%s", label, count, count == 1 ? "y" : "ies"); for (i = 0; i < count; i++) { uint16_t uuid = get_le16(data + (i * 2)); print_field(" %s (0x%4.4x)", bt_uuid16_to_str(uuid), uuid); } } static void print_uuid32_list(const char *label, const void *data, uint8_t data_len) { uint8_t count = data_len / sizeof(uint32_t); unsigned int i; print_field("%s: %u entr%s", label, count, count == 1 ? "y" : "ies"); for (i = 0; i < count; i++) { uint32_t uuid = get_le32(data + (i * 4)); print_field(" %s (0x%8.8x)", bt_uuid32_to_str(uuid), uuid); } } static void print_uuid128_list(const char *label, const void *data, uint8_t data_len) { uint8_t count = data_len / 16; unsigned int i; print_field("%s: %u entr%s", label, count, count == 1 ? "y" : "ies"); for (i = 0; i < count; i++) { const uint8_t *uuid = data + (i * 16); print_field(" %s", bt_uuid128_to_str(uuid)); } } static void print_ltv(const char *str, void *user_data) { const char *label = user_data; print_field("%s: %s", label, str); } static void print_base_annoucement(const uint8_t *data, uint8_t data_len) { struct iovec iov; struct bt_hci_le_pa_base_data *base_data; uint8_t i; iov.iov_base = (void *) data; iov.iov_len = data_len; base_data = util_iov_pull_mem(&iov, sizeof(*base_data)); if (!base_data) goto done; /* Level 1 - BASE */ print_field(" Presetation Delay: %u", get_le24(base_data->pd)); print_field(" Number of Subgroups: %u", base_data->num_subgroups); /* Level 2 - Subgroups*/ for (i = 0; i < base_data->num_subgroups; i++) { struct bt_hci_le_pa_base_subgroup *subgroup; struct bt_hci_lv_data *codec_cfg; struct bt_hci_lv_data *metadata; uint8_t j; const char *label; print_field(" Subgroup #%u:", i); subgroup = util_iov_pull_mem(&iov, sizeof(*subgroup)); if (!subgroup) goto done; print_field(" Number of BIS(s): %u", subgroup->num_bis); print_codec_id(" Codec", subgroup->codec.id); if (subgroup->codec.id == 0xff) { uint16_t id; id = le16_to_cpu(subgroup->codec.vid); print_field(" Codec Company ID: %s (0x%04x)", bt_compidtostr(id), id); print_field(" Codec Vendor ID: 0x%04x", subgroup->codec.vid); } codec_cfg = util_iov_pull_mem(&iov, sizeof(*codec_cfg)); if (!codec_cfg) goto done; if (!util_iov_pull_mem(&iov, codec_cfg->len)) goto done; label = " Codec Specific Configuration"; bt_bap_debug_config(codec_cfg->data, codec_cfg->len, print_ltv, (void *)label); metadata = util_iov_pull_mem(&iov, sizeof(*metadata)); if (!metadata) goto done; if (!util_iov_pull(&iov, metadata->len)) goto done; label = " Metadata"; bt_bap_debug_metadata(metadata->data, metadata->len, print_ltv, (void *)label); label = " Codec Specific Configuration"; /* Level 3 - BIS(s)*/ for (j = 0; j < subgroup->num_bis; j++) { struct bt_hci_le_pa_base_bis *bis; print_field(" BIS #%u:", j); bis = util_iov_pull_mem(&iov, sizeof(*bis)); if (!bis) goto done; print_field(" Index: %u", bis->index); codec_cfg = util_iov_pull_mem(&iov, sizeof(*codec_cfg)); if (!codec_cfg) goto done; if (!util_iov_pull(&iov, codec_cfg->len)) goto done; bt_bap_debug_config(codec_cfg->data, codec_cfg->len, print_ltv, (void *)label); } } done: if (iov.iov_len) print_hex_field(" Data", iov.iov_base, iov.iov_len); } static void print_broadcast_annoucement(const uint8_t *data, uint8_t data_len) { uint32_t bid; if (data_len < 3) { print_hex_field(" Data", data, data_len); return; } bid = get_le24(data); print_field("Broadcast ID: %u (0x%06x)", bid, bid); } static const struct service_data_decoder { uint16_t uuid; void (*func)(const uint8_t *data, uint8_t data_len); } service_data_decoders[] = { { 0x1851, print_base_annoucement }, { 0x1852, print_broadcast_annoucement } }; static void print_service_data(const uint8_t *data, uint8_t data_len) { uint16_t uuid = get_le16(&data[0]); size_t i; print_field("Service Data: %s (0x%4.4x)", bt_uuid16_to_str(uuid), uuid); for (i = 0; i < ARRAY_SIZE(service_data_decoders); i++) { const struct service_data_decoder *decoder; decoder = &service_data_decoders[i]; if (decoder->uuid == uuid) { decoder->func(&data[2], data_len - 2); return; } } print_hex_field(" Data", &data[2], data_len - 2); } static const struct bitfield_data eir_flags_table[] = { { 0, "LE Limited Discoverable Mode" }, { 1, "LE General Discoverable Mode" }, { 2, "BR/EDR Not Supported" }, { 3, "Simultaneous LE and BR/EDR (Controller)" }, { 4, "Simultaneous LE and BR/EDR (Host)" }, { } }; static const struct bitfield_data eir_3d_table[] = { { 0, "Association Notification" }, { 1, "Battery Level Reporting" }, { 2, "Send Battery Level Report on Start-up Synchronization" }, { 7, "Factory Test Mode" }, { } }; static const struct bitfield_data mesh_oob_table[] = { { 0, "Other" }, { 1, "Electronic / URI" }, { 2, "2D machine-readable code" }, { 3, "Bar code" }, { 4, "Near Field Communication (NFC)" }, { 5, "Number" }, { 6, "String" }, { 11, "On box" }, { 12, "Inside box" }, { 13, "On piece of paper" }, { 14, "Inside manual" }, { 15, "On device" }, { } }; static void print_mesh_beacon(const uint8_t *data, uint8_t len) { uint16_t oob; print_hex_field("Mesh Beacon", data, len); if (len < 1) return; switch (data[0]) { case 0x00: print_field(" Unprovisioned Device Beacon (0x00)"); if (len < 18) { packet_hexdump(data + 1, len - 1); break; } print_hex_field(" Device UUID", data + 1, 16); oob = get_be16(data + 17); print_field(" OOB Information: 0x%4.4x", oob); print_bitfield(4, oob, mesh_oob_table); if (len < 23) { packet_hexdump(data + 18, len - 18); break; } print_field(" URI Hash: 0x%8.8x", get_be32(data + 19)); packet_hexdump(data + 23, len - 23); break; case 0x01: print_field(" Secure Network Beacon (0x01)"); if (len < 22) { packet_hexdump(data + 1, len - 1); break; } print_field(" Flags: 0x%2.2x", data[0]); if (data[1] & 0x01) print_field(" Key Refresh"); if (data[1] & 0x02) print_field(" IV Update"); print_hex_field(" Network Id", data + 2, 8); print_field(" IV Index: 0x%08x", get_be32(data + 10)); print_hex_field(" Authentication Value", data + 14, 8); packet_hexdump(data + 22, len - 22); break; default: print_field(" Invalid Beacon (0x%02x)", data[0]); packet_hexdump(data, len); break; } } static void print_mesh_prov(const uint8_t *data, uint8_t len) { print_hex_field("Mesh Provisioning", data, len); if (len < 6) { packet_hexdump(data, len); return; } print_field(" Link ID: 0x%08x", get_be32(data)); print_field(" Transaction Number: %u", data[4]); data += 5; len -= 5; switch (data[0] & 0x03) { case 0x00: print_field(" Transaction Start (0x00)"); if (len < 5) { packet_hexdump(data + 1, len - 1); return; } print_field(" SeqN: %u", data[0] & 0xfc >> 2); print_field(" TotalLength: %u", get_be16(data + 1)); print_field(" FCS: 0x%2.2x", data[3]); print_hex_field(" Data", data + 4, len - 4); packet_hexdump(data + 5, len - 5); break; case 0x01: print_field(" Transaction Acknowledgment (0x01)"); packet_hexdump(data + 1, len - 1); break; case 0x02: print_field(" Transaction Continuation (0x02)"); print_field(" SegmentIndex: %u", data[0] >> 2); if (len < 2) { packet_hexdump(data + 1, len - 1); return; } print_hex_field(" Data", data + 1, len - 1); packet_hexdump(data + 2, len - 2); break; case 0x03: print_field(" Provisioning Bearer Control (0x03)"); switch (data[0] >> 2) { case 0x00: print_field(" Link Open (0x00)"); if (len < 17) { packet_hexdump(data + 1, len - 1); break; } print_hex_field(" Device UUID", data, 16); break; case 0x01: print_field(" Link Ack (0x01)"); break; case 0x02: print_field(" Link Close (0x02)"); if (len < 2) { packet_hexdump(data + 1, len - 1); break; } switch (data[1]) { case 0x00: print_field(" Reason: Success (0x00)"); break; case 0x01: print_field(" Reason: Timeout (0x01)"); break; case 0x02: print_field(" Reason: Fail (0x02)"); break; default: print_field(" Reason: Unrecognized (0x%2.2x)", data[1]); } packet_hexdump(data + 2, len - 2); break; default: packet_hexdump(data + 1, len - 1); break; } break; default: print_field(" Invalid Command (0x%02x)", data[0]); packet_hexdump(data, len); break; } } static void print_mesh_data(const uint8_t *data, uint8_t len) { print_hex_field("Mesh Data", data, len); if (len < 1) return; print_field(" IVI: %u", data[0] >> 7); print_field(" NID: 0x%2.2x", data[0] & 0x7f); packet_hexdump(data + 1, len - 1); } static void print_transport_data(const uint8_t *data, uint8_t len) { print_field("Transport Discovery Data"); if (len < 3) return; print_field(" Organization: %s (0x%02x)", data[0] == 0x01 ? "Bluetooth SIG" : "RFU", data[0]); print_field(" Flags: 0x%2.2x", data[1]); print_field(" Role: 0x%2.2x", data[1] & 0x03); switch (data[1] & 0x03) { case 0x00: print_field(" Not Specified"); break; case 0x01: print_field(" Seeker Only"); break; case 0x02: print_field(" Provider Only"); break; case 0x03: print_field(" Both Seeker and Provider"); break; } print_field(" Transport Data Incomplete: %s (0x%2.2x)", data[1] & 0x04 ? "True" : "False", data[1] & 0x04); print_field(" Transport State: 0x%2.2x", data[1] & 0x18); switch (data[1] & 0x18) { case 0x00: print_field(" Off"); break; case 0x08: print_field(" On"); break; case 0x10: print_field(" Temporary Unavailable"); break; case 0x18: print_field(" RFU"); break; } print_field(" Length: %u", data[2]); print_hex_field(" Data", data + 3, len - 3); } static void print_eir(const uint8_t *eir, uint8_t eir_len, bool le) { uint16_t len = 0; if (eir_len == 0) return; while (len < eir_len - 1) { uint8_t field_len = eir[0]; const uint8_t *data = &eir[2]; uint8_t data_len; char name[239], label[100]; uint8_t flags, mask; /* Check for the end of EIR */ if (field_len == 0) break; len += field_len + 1; /* Do not continue EIR Data parsing if got incorrect length */ if (len > eir_len) { len -= field_len + 1; break; } data_len = field_len - 1; switch (eir[1]) { case BT_EIR_FLAGS: flags = *data; print_field("Flags: 0x%2.2x", flags); mask = print_bitfield(2, flags, eir_flags_table); if (mask) print_text(COLOR_UNKNOWN_SERVICE_CLASS, " Unknown flags (0x%2.2x)", mask); break; case BT_EIR_UUID16_SOME: if (data_len < sizeof(uint16_t)) break; print_uuid16_list("16-bit Service UUIDs (partial)", data, data_len); break; case BT_EIR_UUID16_ALL: if (data_len < sizeof(uint16_t)) break; print_uuid16_list("16-bit Service UUIDs (complete)", data, data_len); break; case BT_EIR_UUID32_SOME: if (data_len < sizeof(uint32_t)) break; print_uuid32_list("32-bit Service UUIDs (partial)", data, data_len); break; case BT_EIR_UUID32_ALL: if (data_len < sizeof(uint32_t)) break; print_uuid32_list("32-bit Service UUIDs (complete)", data, data_len); break; case BT_EIR_UUID128_SOME: if (data_len < 16) break; print_uuid128_list("128-bit Service UUIDs (partial)", data, data_len); break; case BT_EIR_UUID128_ALL: if (data_len < 16) break; print_uuid128_list("128-bit Service UUIDs (complete)", data, data_len); break; case BT_EIR_NAME_SHORT: memset(name, 0, sizeof(name)); memcpy(name, data, data_len); print_field("Name (short): %s", name); break; case BT_EIR_NAME_COMPLETE: memset(name, 0, sizeof(name)); memcpy(name, data, data_len); print_field("Name (complete): %s", name); break; case BT_EIR_TX_POWER: if (data_len < 1) break; print_field("TX power: %d dBm", (int8_t) *data); break; case BT_EIR_CLASS_OF_DEV: if (data_len < 3) break; print_dev_class(data); break; case BT_EIR_SSP_HASH_P192: if (data_len < 16) break; print_hash_p192(data); break; case BT_EIR_SSP_RANDOMIZER_P192: if (data_len < 16) break; print_randomizer_p192(data); break; case BT_EIR_DEVICE_ID: /* SMP TK has the same value as Device ID */ if (le) print_hex_field("SMP TK", data, data_len); else if (data_len >= 8) print_device_id(data, data_len); break; case BT_EIR_SMP_OOB_FLAGS: print_field("SMP OOB Flags: 0x%2.2x", *data); break; case BT_EIR_PERIPHERAL_CONN_INTERVAL: if (data_len < 4) break; print_field("Peripheral Conn. Interval: " "0x%4.4x - 0x%4.4x", get_le16(&data[0]), get_le16(&data[2])); break; case BT_EIR_SERVICE_UUID16: if (data_len < sizeof(uint16_t)) break; print_uuid16_list("16-bit Service UUIDs", data, data_len); break; case BT_EIR_SERVICE_UUID128: if (data_len < 16) break; print_uuid128_list("128-bit Service UUIDs", data, data_len); break; case BT_EIR_SERVICE_DATA: if (data_len < 2) break; print_service_data(data, data_len); break; case BT_EIR_SERVICE_DATA128: if (data_len <= 16) break; print_field("Service Data UUID 128: %s ", bt_uuid128_to_str(&data[0])); if (data_len > 16) print_hex_field(" Data", &data[16], data_len - 16); break; case BT_EIR_RANDOM_ADDRESS: if (data_len < 6) break; print_addr("Random Address", data, 0x01); break; case BT_EIR_PUBLIC_ADDRESS: if (data_len < 6) break; print_addr("Public Address", data, 0x00); break; case BT_EIR_GAP_APPEARANCE: if (data_len < 2) break; print_appearance(get_le16(data)); break; case BT_EIR_SSP_HASH_P256: if (data_len < 16) break; print_hash_p256(data); break; case BT_EIR_SSP_RANDOMIZER_P256: if (data_len < 16) break; print_randomizer_p256(data); break; case BT_EIR_TRANSPORT_DISCOVERY: print_transport_data(data, data_len); break; case BT_EIR_3D_INFO_DATA: print_hex_field("3D Information Data", data, data_len); if (data_len < 2) break; flags = *data; print_field(" Features: 0x%2.2x", flags); mask = print_bitfield(4, flags, eir_3d_table); if (mask) print_text(COLOR_UNKNOWN_FEATURE_BIT, " Unknown features (0x%2.2x)", mask); print_field(" Path Loss Threshold: %d", data[1]); break; case BT_EIR_MESH_DATA: print_mesh_data(data, data_len); break; case BT_EIR_MESH_PROV: print_mesh_prov(data, data_len); break; case BT_EIR_MESH_BEACON: print_mesh_beacon(data, data_len); break; case BT_EIR_CSIP_RSI: if (data_len < 6) break; print_addr("Resolvable Set Identifier", data, 0xff); print_field(" Hash: 0x%6x", get_le24(data)); print_field(" Random: 0x%6x", get_le24(data + 3)); break; case BT_EIR_MANUFACTURER_DATA: if (data_len < 2) break; print_manufacturer_data(data, data_len); break; default: sprintf(label, "Unknown EIR field 0x%2.2x", eir[1]); print_hex_field(label, data, data_len); break; } eir += field_len + 1; } if (len < eir_len && eir[0] != 0) packet_hexdump(eir, eir_len - len); } void packet_print_addr(const char *label, const void *data, uint8_t type) { print_addr(label ? : "Address", data, type); } void packet_print_handle(uint16_t handle) { print_handle_native(handle); } void packet_print_rssi(const char *label, int8_t rssi) { if ((uint8_t) rssi == 0x99 || rssi == 127) print_field("%s: invalid (0x%2.2x)", label, (uint8_t) rssi); else print_field("%s: %d dBm (0x%2.2x)", label, rssi, (uint8_t) rssi); } void packet_print_ad(const void *data, uint8_t size) { print_eir(data, size, true); } struct broadcast_message { uint32_t frame_sync_instant; uint16_t bluetooth_clock_phase; uint16_t left_open_offset; uint16_t left_close_offset; uint16_t right_open_offset; uint16_t right_close_offset; uint16_t frame_sync_period; uint8_t frame_sync_period_fraction; } __attribute__ ((packed)); static void print_3d_broadcast(const void *data, uint8_t size) { const struct broadcast_message *msg = data; uint32_t instant; uint16_t left_open, left_close, right_open, right_close; uint16_t phase, period; uint8_t period_frac; bool mode; instant = le32_to_cpu(msg->frame_sync_instant); mode = !!(instant & 0x40000000); phase = le16_to_cpu(msg->bluetooth_clock_phase); left_open = le16_to_cpu(msg->left_open_offset); left_close = le16_to_cpu(msg->left_close_offset); right_open = le16_to_cpu(msg->right_open_offset); right_close = le16_to_cpu(msg->right_close_offset); period = le16_to_cpu(msg->frame_sync_period); period_frac = msg->frame_sync_period_fraction; print_field(" Frame sync instant: 0x%8.8x", instant & 0x7fffffff); print_field(" Video mode: %s (%d)", mode ? "Dual View" : "3D", mode); print_field(" Bluetooth clock phase: %d usec (0x%4.4x)", phase, phase); print_field(" Left lense shutter open offset: %d usec (0x%4.4x)", left_open, left_open); print_field(" Left lense shutter close offset: %d usec (0x%4.4x)", left_close, left_close); print_field(" Right lense shutter open offset: %d usec (0x%4.4x)", right_open, right_open); print_field(" Right lense shutter close offset: %d usec (0x%4.4x)", right_close, right_close); print_field(" Frame sync period: %d.%d usec (0x%4.4x 0x%2.2x)", period, period_frac * 256, period, period_frac); } void packet_hexdump(const unsigned char *buf, uint16_t len) { static const char hexdigits[] = "0123456789abcdef"; char str[68]; uint16_t i; if (!len) return; for (i = 0; i < len; i++) { str[((i % 16) * 3) + 0] = hexdigits[buf[i] >> 4]; str[((i % 16) * 3) + 1] = hexdigits[buf[i] & 0xf]; str[((i % 16) * 3) + 2] = ' '; str[(i % 16) + 49] = isprint(buf[i]) ? buf[i] : '.'; if ((i + 1) % 16 == 0) { str[47] = ' '; str[48] = ' '; str[65] = '\0'; print_text(COLOR_WHITE, "%s", str); str[0] = ' '; } } if (i % 16 > 0) { uint16_t j; for (j = (i % 16); j < 16; j++) { str[(j * 3) + 0] = ' '; str[(j * 3) + 1] = ' '; str[(j * 3) + 2] = ' '; str[j + 49] = ' '; } str[47] = ' '; str[48] = ' '; str[65] = '\0'; print_text(COLOR_WHITE, "%s", str); } } void packet_control(struct timeval *tv, struct ucred *cred, uint16_t index, uint16_t opcode, const void *data, uint16_t size) { control_message(opcode, data, size); } static int addr2str(const uint8_t *addr, char *str) { return sprintf(str, "%2.2X:%2.2X:%2.2X:%2.2X:%2.2X:%2.2X", addr[5], addr[4], addr[3], addr[2], addr[1], addr[0]); } void packet_monitor(struct timeval *tv, struct ucred *cred, uint16_t index, uint16_t opcode, const void *data, uint16_t size) { const struct btsnoop_opcode_new_index *ni; const struct btsnoop_opcode_index_info *ii; const struct btsnoop_opcode_user_logging *ul; char str[18], extra_str[24]; uint16_t manufacturer; const char *ident; if (index != HCI_DEV_NONE) { index_current = index; } if (index != HCI_DEV_NONE && index >= MAX_INDEX) { print_field("Invalid index (%d)", index); return; } if (tv && time_offset == ((time_t) -1)) time_offset = tv->tv_sec; switch (opcode) { case BTSNOOP_OPCODE_NEW_INDEX: ni = data; if (index < MAX_INDEX) { index_list[index].type = ni->type; memcpy(index_list[index].bdaddr, ni->bdaddr, 6); index_list[index].manufacturer = fallback_manufacturer; index_list[index].msft_opcode = BT_HCI_CMD_NOP; } addr2str(ni->bdaddr, str); packet_new_index(tv, index, str, ni->type, ni->bus, ni->name); break; case BTSNOOP_OPCODE_DEL_INDEX: if (index < MAX_INDEX) addr2str(index_list[index].bdaddr, str); else sprintf(str, "00:00:00:00:00:00"); packet_del_index(tv, index, str); break; case BTSNOOP_OPCODE_COMMAND_PKT: packet_hci_command(tv, cred, index, data, size); break; case BTSNOOP_OPCODE_EVENT_PKT: packet_hci_event(tv, cred, index, data, size); break; case BTSNOOP_OPCODE_ACL_TX_PKT: packet_hci_acldata(tv, cred, index, false, data, size); break; case BTSNOOP_OPCODE_ACL_RX_PKT: packet_hci_acldata(tv, cred, index, true, data, size); break; case BTSNOOP_OPCODE_SCO_TX_PKT: packet_hci_scodata(tv, cred, index, false, data, size); break; case BTSNOOP_OPCODE_SCO_RX_PKT: packet_hci_scodata(tv, cred, index, true, data, size); break; case BTSNOOP_OPCODE_ISO_TX_PKT: packet_hci_isodata(tv, cred, index, false, data, size); break; case BTSNOOP_OPCODE_ISO_RX_PKT: packet_hci_isodata(tv, cred, index, true, data, size); break; case BTSNOOP_OPCODE_OPEN_INDEX: if (index < MAX_INDEX) addr2str(index_list[index].bdaddr, str); else sprintf(str, "00:00:00:00:00:00"); packet_open_index(tv, index, str); break; case BTSNOOP_OPCODE_CLOSE_INDEX: if (index < MAX_INDEX) addr2str(index_list[index].bdaddr, str); else sprintf(str, "00:00:00:00:00:00"); packet_close_index(tv, index, str); break; case BTSNOOP_OPCODE_INDEX_INFO: ii = data; manufacturer = le16_to_cpu(ii->manufacturer); if (index < MAX_INDEX) { memcpy(index_list[index].bdaddr, ii->bdaddr, 6); index_list[index].manufacturer = manufacturer; switch (manufacturer) { case 2: /* * Intel controllers that support the * Microsoft vendor extension are using * 0xFC1E for VsMsftOpCode. */ index_list[index].msft_opcode = 0xFC1E; break; case 29: /* * Qualcomm controllers that support the * Microsoft vendor extensions are using * 0xFD70 for VsMsftOpCode. */ index_list[index].msft_opcode = 0xFD70; break; case 70: /* * Mediatek controllers that support the * Microsoft vendor extensions are using * 0xFD30 for VsMsftOpCode. */ index_list[index].msft_opcode = 0xFD30; break; case 93: /* * Realtek controllers that support the * Microsoft vendor extensions are using * 0xFCF0 for VsMsftOpCode. */ index_list[index].msft_opcode = 0xFCF0; break; case 1521: /* * Emulator controllers use Linux Foundation as * manufacturer and support the * Microsoft vendor extensions using * 0xFC1E for VsMsftOpCode. */ index_list[index].msft_opcode = 0xFC1E; break; } } addr2str(ii->bdaddr, str); packet_index_info(tv, index, str, manufacturer); break; case BTSNOOP_OPCODE_VENDOR_DIAG: if (index < MAX_INDEX) manufacturer = index_list[index].manufacturer; else manufacturer = fallback_manufacturer; packet_vendor_diag(tv, index, manufacturer, data, size); break; case BTSNOOP_OPCODE_SYSTEM_NOTE: packet_system_note(tv, cred, index, data); break; case BTSNOOP_OPCODE_USER_LOGGING: ul = data; ident = ul->ident_len ? data + sizeof(*ul) : NULL; packet_user_logging(tv, cred, index, ul->priority, ident, data + sizeof(*ul) + ul->ident_len, size - (sizeof(*ul) + ul->ident_len)); break; case BTSNOOP_OPCODE_CTRL_OPEN: control_disable_decoding(); packet_ctrl_open(tv, cred, index, data, size); break; case BTSNOOP_OPCODE_CTRL_CLOSE: packet_ctrl_close(tv, cred, index, data, size); break; case BTSNOOP_OPCODE_CTRL_COMMAND: packet_ctrl_command(tv, cred, index, data, size); break; case BTSNOOP_OPCODE_CTRL_EVENT: packet_ctrl_event(tv, cred, index, data, size); break; default: sprintf(extra_str, "(code %d len %d)", opcode, size); print_packet(tv, cred, '*', index, NULL, COLOR_ERROR, "Unknown packet", NULL, extra_str); packet_hexdump(data, size); break; } } void packet_simulator(struct timeval *tv, uint16_t frequency, const void *data, uint16_t size) { char str[10]; if (tv && time_offset == ((time_t) -1)) time_offset = tv->tv_sec; sprintf(str, "%u MHz", frequency); print_packet(tv, NULL, '*', 0, NULL, COLOR_PHY_PACKET, "Physical packet:", NULL, str); ll_packet(frequency, data, size, false); } static void null_cmd(uint16_t index, const void *data, uint8_t size) { } static void status_rsp(uint16_t index, const void *data, uint8_t size) { uint8_t status = *((const uint8_t *) data); print_status(status); } static void status_handle_rsp(uint16_t index, const void *data, uint8_t size) { uint8_t status = *((const uint8_t *) data); print_status(status); print_field("Connection handle: %d", get_u8(data + 1)); } static void status_bdaddr_rsp(uint16_t index, const void *data, uint8_t size) { uint8_t status = *((const uint8_t *) data); print_status(status); print_bdaddr(data + 1); } static void inquiry_cmd(uint16_t index, const void *data, uint8_t size) { const struct bt_hci_cmd_inquiry *cmd = data; print_iac(cmd->lap); print_field("Length: %.2fs (0x%2.2x)", cmd->length * 1.28, cmd->length); print_num_resp(cmd->num_resp); } static void periodic_inquiry_cmd(uint16_t index, const void *data, uint8_t size) { const struct bt_hci_cmd_periodic_inquiry *cmd = data; print_field("Max period: %.2fs (0x%2.2x)", cmd->max_period * 1.28, cmd->max_period); print_field("Min period: %.2fs (0x%2.2x)", cmd->min_period * 1.28, cmd->min_period); print_iac(cmd->lap); print_field("Length: %.2fs (0x%2.2x)", cmd->length * 1.28, cmd->length); print_num_resp(cmd->num_resp); } static void create_conn_cmd(uint16_t index, const void *data, uint8_t size) { const struct bt_hci_cmd_create_conn *cmd = data; const char *str; print_bdaddr(cmd->bdaddr); print_pkt_type(cmd->pkt_type); print_pscan_rep_mode(cmd->pscan_rep_mode); print_pscan_mode(cmd->pscan_mode); print_clock_offset(cmd->clock_offset); switch (cmd->role_switch) { case 0x00: str = "Stay central"; break; case 0x01: str = "Allow peripheral"; break; default: str = "Reserved"; break; } print_field("Role switch: %s (0x%2.2x)", str, cmd->role_switch); } static void disconnect_cmd(uint16_t index, const void *data, uint8_t size) { const struct bt_hci_cmd_disconnect *cmd = data; print_handle(cmd->handle); print_reason(cmd->reason); } static void add_sco_conn_cmd(uint16_t index, const void *data, uint8_t size) { const struct bt_hci_cmd_add_sco_conn *cmd = data; print_handle(cmd->handle); print_pkt_type_sco(cmd->pkt_type); } static void create_conn_cancel_cmd(uint16_t index, const void *data, uint8_t size) { const struct bt_hci_cmd_create_conn_cancel *cmd = data; print_bdaddr(cmd->bdaddr); } static void accept_conn_request_cmd(uint16_t index, const void *data, uint8_t size) { const struct bt_hci_cmd_accept_conn_request *cmd = data; print_bdaddr(cmd->bdaddr); print_role(cmd->role); } static void reject_conn_request_cmd(uint16_t index, const void *data, uint8_t size) { const struct bt_hci_cmd_reject_conn_request *cmd = data; print_bdaddr(cmd->bdaddr); print_reason(cmd->reason); } static void link_key_request_reply_cmd(uint16_t index, const void *data, uint8_t size) { const struct bt_hci_cmd_link_key_request_reply *cmd = data; print_bdaddr(cmd->bdaddr); print_link_key(cmd->link_key); } static void link_key_request_neg_reply_cmd(uint16_t index, const void *data, uint8_t size) { const struct bt_hci_cmd_link_key_request_neg_reply *cmd = data; print_bdaddr(cmd->bdaddr); } static void pin_code_request_reply_cmd(uint16_t index, const void *data, uint8_t size) { const struct bt_hci_cmd_pin_code_request_reply *cmd = data; print_bdaddr(cmd->bdaddr); print_field("PIN length: %d", cmd->pin_len); print_pin_code(cmd->pin_code, cmd->pin_len); } static void pin_code_request_neg_reply_cmd(uint16_t index, const void *data, uint8_t size) { const struct bt_hci_cmd_pin_code_request_neg_reply *cmd = data; print_bdaddr(cmd->bdaddr); } static void change_conn_pkt_type_cmd(uint16_t index, const void *data, uint8_t size) { const struct bt_hci_cmd_change_conn_pkt_type *cmd = data; print_handle(cmd->handle); print_pkt_type(cmd->pkt_type); } static void auth_requested_cmd(uint16_t index, const void *data, uint8_t size) { const struct bt_hci_cmd_auth_requested *cmd = data; print_handle(cmd->handle); } static void set_conn_encrypt_cmd(uint16_t index, const void *data, uint8_t size) { const struct bt_hci_cmd_set_conn_encrypt *cmd = data; print_handle(cmd->handle); print_enable("Encryption", cmd->encr_mode); } static void change_conn_link_key_cmd(uint16_t index, const void *data, uint8_t size) { const struct bt_hci_cmd_change_conn_link_key *cmd = data; print_handle(cmd->handle); } static void link_key_selection_cmd(uint16_t index, const void *data, uint8_t size) { const struct bt_hci_cmd_link_key_selection *cmd = data; print_key_flag(cmd->key_flag); } static void remote_name_request_cmd(uint16_t index, const void *data, uint8_t size) { const struct bt_hci_cmd_remote_name_request *cmd = data; print_bdaddr(cmd->bdaddr); print_pscan_rep_mode(cmd->pscan_rep_mode); print_pscan_mode(cmd->pscan_mode); print_clock_offset(cmd->clock_offset); } static void remote_name_request_cancel_cmd(uint16_t index, const void *data, uint8_t size) { const struct bt_hci_cmd_remote_name_request_cancel *cmd = data; print_bdaddr(cmd->bdaddr); } static void read_remote_features_cmd(uint16_t index, const void *data, uint8_t size) { const struct bt_hci_cmd_read_remote_features *cmd = data; print_handle(cmd->handle); } static void read_remote_ext_features_cmd(uint16_t index, const void *data, uint8_t size) { const struct bt_hci_cmd_read_remote_ext_features *cmd = data; print_handle(cmd->handle); print_field("Page: %d", cmd->page); } static void read_remote_version_cmd(uint16_t index, const void *data, uint8_t size) { const struct bt_hci_cmd_read_remote_version *cmd = data; print_handle(cmd->handle); } static void read_clock_offset_cmd(uint16_t index, const void *data, uint8_t size) { const struct bt_hci_cmd_read_clock_offset *cmd = data; print_handle(cmd->handle); } static void read_lmp_handle_cmd(uint16_t index, const void *data, uint8_t size) { const struct bt_hci_cmd_read_lmp_handle *cmd = data; print_handle(cmd->handle); } static void read_lmp_handle_rsp(uint16_t index, const void *data, uint8_t size) { const struct bt_hci_rsp_read_lmp_handle *rsp = data; print_status(rsp->status); print_handle(rsp->handle); print_field("LMP handle: %d", rsp->lmp_handle); print_field("Reserved: %d", le32_to_cpu(rsp->reserved)); } static void setup_sync_conn_cmd(uint16_t index, const void *data, uint8_t size) { const struct bt_hci_cmd_setup_sync_conn *cmd = data; print_handle(cmd->handle); print_field("Transmit bandwidth: %d", le32_to_cpu(cmd->tx_bandwidth)); print_field("Receive bandwidth: %d", le32_to_cpu(cmd->rx_bandwidth)); print_field("Max latency: %d", le16_to_cpu(cmd->max_latency)); print_voice_setting(cmd->voice_setting); print_retransmission_effort(cmd->retrans_effort); print_pkt_type_sco(cmd->pkt_type); } static void accept_sync_conn_request_cmd(uint16_t index, const void *data, uint8_t size) { const struct bt_hci_cmd_accept_sync_conn_request *cmd = data; print_bdaddr(cmd->bdaddr); print_field("Transmit bandwidth: %d", le32_to_cpu(cmd->tx_bandwidth)); print_field("Receive bandwidth: %d", le32_to_cpu(cmd->rx_bandwidth)); print_field("Max latency: %d", le16_to_cpu(cmd->max_latency)); print_voice_setting(cmd->voice_setting); print_retransmission_effort(cmd->retrans_effort); print_pkt_type_sco(cmd->pkt_type); } static void reject_sync_conn_request_cmd(uint16_t index, const void *data, uint8_t size) { const struct bt_hci_cmd_reject_sync_conn_request *cmd = data; print_bdaddr(cmd->bdaddr); print_reason(cmd->reason); } static void io_capability_request_reply_cmd(uint16_t index, const void *data, uint8_t size) { const struct bt_hci_cmd_io_capability_request_reply *cmd = data; print_bdaddr(cmd->bdaddr); print_io_capability(cmd->capability); print_oob_data(cmd->oob_data); print_authentication(cmd->authentication); } static void user_confirm_request_reply_cmd(uint16_t index, const void *data, uint8_t size) { const struct bt_hci_cmd_user_confirm_request_reply *cmd = data; print_bdaddr(cmd->bdaddr); } static void user_confirm_request_neg_reply_cmd(uint16_t index, const void *data, uint8_t size) { const struct bt_hci_cmd_user_confirm_request_neg_reply *cmd = data; print_bdaddr(cmd->bdaddr); } static void user_passkey_request_reply_cmd(uint16_t index, const void *data, uint8_t size) { const struct bt_hci_cmd_user_passkey_request_reply *cmd = data; print_bdaddr(cmd->bdaddr); print_passkey(cmd->passkey); } static void user_passkey_request_neg_reply_cmd(uint16_t index, const void *data, uint8_t size) { const struct bt_hci_cmd_user_passkey_request_neg_reply *cmd = data; print_bdaddr(cmd->bdaddr); } static void remote_oob_data_request_reply_cmd(uint16_t index, const void *data, uint8_t size) { const struct bt_hci_cmd_remote_oob_data_request_reply *cmd = data; print_bdaddr(cmd->bdaddr); print_hash_p192(cmd->hash); print_randomizer_p192(cmd->randomizer); } static void remote_oob_data_request_neg_reply_cmd(uint16_t index, const void *data, uint8_t size) { const struct bt_hci_cmd_remote_oob_data_request_neg_reply *cmd = data; print_bdaddr(cmd->bdaddr); } static void io_capability_request_neg_reply_cmd(uint16_t index, const void *data, uint8_t size) { const struct bt_hci_cmd_io_capability_request_neg_reply *cmd = data; print_bdaddr(cmd->bdaddr); print_reason(cmd->reason); } static void create_phy_link_cmd(uint16_t index, const void *data, uint8_t size) { const struct bt_hci_cmd_create_phy_link *cmd = data; print_phy_handle(cmd->phy_handle); print_key_len(cmd->key_len); print_key_type(cmd->key_type); packet_hexdump(data + 3, size - 3); } static void accept_phy_link_cmd(uint16_t index, const void *data, uint8_t size) { const struct bt_hci_cmd_accept_phy_link *cmd = data; print_phy_handle(cmd->phy_handle); print_key_len(cmd->key_len); print_key_type(cmd->key_type); packet_hexdump(data + 3, size - 3); } static void disconn_phy_link_cmd(uint16_t index, const void *data, uint8_t size) { const struct bt_hci_cmd_disconn_phy_link *cmd = data; print_phy_handle(cmd->phy_handle); print_reason(cmd->reason); } static void create_logic_link_cmd(uint16_t index, const void *data, uint8_t size) { const struct bt_hci_cmd_create_logic_link *cmd = data; print_phy_handle(cmd->phy_handle); print_flow_spec("TX", cmd->tx_flow_spec); print_flow_spec("RX", cmd->rx_flow_spec); } static void accept_logic_link_cmd(uint16_t index, const void *data, uint8_t size) { const struct bt_hci_cmd_accept_logic_link *cmd = data; print_phy_handle(cmd->phy_handle); print_flow_spec("TX", cmd->tx_flow_spec); print_flow_spec("RX", cmd->rx_flow_spec); } static void disconn_logic_link_cmd(uint16_t index, const void *data, uint8_t size) { const struct bt_hci_cmd_disconn_logic_link *cmd = data; print_handle(cmd->handle); } static void logic_link_cancel_cmd(uint16_t index, const void *data, uint8_t size) { const struct bt_hci_cmd_logic_link_cancel *cmd = data; print_phy_handle(cmd->phy_handle); print_field("TX flow spec: 0x%2.2x", cmd->flow_spec); } static void logic_link_cancel_rsp(uint16_t index, const void *data, uint8_t size) { const struct bt_hci_rsp_logic_link_cancel *rsp = data; print_status(rsp->status); print_phy_handle(rsp->phy_handle); print_field("TX flow spec: 0x%2.2x", rsp->flow_spec); } static void flow_spec_modify_cmd(uint16_t index, const void *data, uint8_t size) { const struct bt_hci_cmd_flow_spec_modify *cmd = data; print_handle(cmd->handle); print_flow_spec("TX", cmd->tx_flow_spec); print_flow_spec("RX", cmd->rx_flow_spec); } static void print_coding_format(const char *label, const uint8_t coding_format[5]) { print_field("%s:", label); packet_print_codec_id(" Codec", coding_format[0]); if (coding_format[0] == 0xff) { print_field(" Company: %s", bt_compidtostr(get_le16(coding_format + 1))); print_field(" Vendor Specific Codec: 0x%4.4x", get_le16(coding_format + 3)); } } static void print_pcm_data_format(const char *label, uint8_t pcm) { switch (pcm) { case 0: print_field("%s: NA", label); break; case 1: print_field("%s: 1's complement", label); break; case 2: print_field("%s: 2's complement", label); break; case 3: print_field("%s: Sign-magnitude", label); break; case 4: print_field("%s: Unsigned", label); break; default: print_field("%s: Unknown (0x%2.2x)", label, pcm); } } static void print_data_path(const char *label, uint8_t data_path) { if (data_path == 0) print_field("%s: HCI", label); else print_field("%s: Vendor Specific (0x%2.2x)", label, data_path); } static void enhanced_setup_sync_conn_cmd(uint16_t index, const void *data, uint8_t size) { const struct bt_hci_cmd_enhanced_setup_sync_conn *cmd = data; print_handle(cmd->handle); print_field("Transmit bandwidth: %d", le32_to_cpu(cmd->tx_bandwidth)); print_field("Receive bandwidth: %d", le32_to_cpu(cmd->rx_bandwidth)); print_coding_format("Transmit Coding Format", cmd->tx_coding_format); print_coding_format("Receive Coding Format", cmd->rx_coding_format); print_field("Transmit Codec Frame Size: %d", le16_to_cpu(cmd->tx_codec_frame_size)); print_field("Receive Codec Frame Size: %d", le16_to_cpu(cmd->rx_codec_frame_size)); print_coding_format("Input Coding Format", cmd->input_coding_format); print_coding_format("Output Coding Format", cmd->output_coding_format); print_field("Input Coded Data Size: %d", le16_to_cpu(cmd->input_coded_data_size)); print_field("Output Coded Data Size: %d", le16_to_cpu(cmd->output_coded_data_size)); print_pcm_data_format("Input PCM Data Format", cmd->input_pcm_data_format); print_pcm_data_format("Output PCM Data Format", cmd->output_pcm_data_format); print_field("Input PCM Sample Payload MSB Position: %d", cmd->input_pcm_msb_position); print_field("Output PCM Sample Payload MSB Position: %d", cmd->output_pcm_msb_position); print_data_path("Input Data Path", cmd->input_data_path); print_data_path("Output Data Path", cmd->output_data_path); print_field("Input Transport Unit Size: %d", cmd->input_unit_size); print_field("Output Transport Unit Size: %d", cmd->output_unit_size); print_field("Max latency: %d", le16_to_cpu(cmd->max_latency)); print_pkt_type_sco(cmd->pkt_type); print_retransmission_effort(cmd->retrans_effort); } static void enhanced_accept_sync_conn_request_cmd(uint16_t index, const void *data, uint8_t size) { const struct bt_hci_cmd_enhanced_accept_sync_conn_request *cmd = data; print_bdaddr(cmd->bdaddr); print_field("Transmit bandwidth: %d", le32_to_cpu(cmd->tx_bandwidth)); print_field("Receive bandwidth: %d", le32_to_cpu(cmd->rx_bandwidth)); print_coding_format("Transmit Coding Format", cmd->tx_coding_format); print_coding_format("Receive Coding Format", cmd->rx_coding_format); print_field("Transmit Codec Frame Size: %d", le16_to_cpu(cmd->tx_codec_frame_size)); print_field("Receive Codec Frame Size: %d", le16_to_cpu(cmd->rx_codec_frame_size)); print_coding_format("Input Coding Format", cmd->input_coding_format); print_coding_format("Output Coding Format", cmd->output_coding_format); print_field("Input Coded Data Size: %d", le16_to_cpu(cmd->input_coded_data_size)); print_field("Output Coded Data Size: %d", le16_to_cpu(cmd->output_coded_data_size)); print_pcm_data_format("Input PCM Data Format", cmd->input_pcm_data_format); print_pcm_data_format("Output PCM Data Format", cmd->output_pcm_data_format); print_field("Input PCM Sample Payload MSB Position: %d", cmd->input_pcm_msb_position); print_field("Output PCM Sample Payload MSB Position: %d", cmd->output_pcm_msb_position); print_data_path("Input Data Path", cmd->input_data_path); print_data_path("Output Data Path", cmd->output_data_path); print_field("Input Transport Unit Size: %d", cmd->input_unit_size); print_field("Output Transport Unit Size: %d", cmd->output_unit_size); print_field("Max latency: %d", le16_to_cpu(cmd->max_latency)); print_pkt_type_sco(cmd->pkt_type); print_retransmission_effort(cmd->retrans_effort); } static void truncated_page_cmd(uint16_t index, const void *data, uint8_t size) { const struct bt_hci_cmd_truncated_page *cmd = data; print_bdaddr(cmd->bdaddr); print_pscan_rep_mode(cmd->pscan_rep_mode); print_clock_offset(cmd->clock_offset); } static void truncated_page_cancel_cmd(uint16_t index, const void *data, uint8_t size) { const struct bt_hci_cmd_truncated_page_cancel *cmd = data; print_bdaddr(cmd->bdaddr); } static void set_peripheral_broadcast_cmd(uint16_t index, const void *data, uint8_t size) { const struct bt_hci_cmd_set_peripheral_broadcast *cmd = data; print_field("Enable: 0x%2.2x", cmd->enable); print_lt_addr(cmd->lt_addr); print_lpo_allowed(cmd->lpo_allowed); print_pkt_type(cmd->pkt_type); print_slot_625("Min interval", cmd->min_interval); print_slot_625("Max interval", cmd->max_interval); print_slot_625("Supervision timeout", cmd->timeout); } static void set_peripheral_broadcast_rsp(uint16_t index, const void *data, uint8_t size) { const struct bt_hci_rsp_set_peripheral_broadcast *rsp = data; print_status(rsp->status); print_lt_addr(rsp->lt_addr); print_interval(rsp->interval); } static void set_peripheral_broadcast_receive_cmd(uint16_t index, const void *data, uint8_t size) { const struct bt_hci_cmd_set_peripheral_broadcast_receive *cmd = data; print_field("Enable: 0x%2.2x", cmd->enable); print_bdaddr(cmd->bdaddr); print_lt_addr(cmd->lt_addr); print_interval(cmd->interval); print_field("Offset: 0x%8.8x", le32_to_cpu(cmd->offset)); print_field("Next broadcast instant: 0x%4.4x", le16_to_cpu(cmd->instant)); print_slot_625("Supervision timeout", cmd->timeout); print_field("Remote timing accuracy: %d ppm", cmd->accuracy); print_field("Skip: 0x%2.2x", cmd->skip); print_pkt_type(cmd->pkt_type); print_channel_map(cmd->map); } static void set_peripheral_broadcast_receive_rsp(uint16_t index, const void *data, uint8_t size) { const struct bt_hci_rsp_set_peripheral_broadcast_receive *rsp = data; print_status(rsp->status); print_bdaddr(rsp->bdaddr); print_lt_addr(rsp->lt_addr); } static void receive_sync_train_cmd(uint16_t index, const void *data, uint8_t size) { const struct bt_hci_cmd_receive_sync_train *cmd = data; print_bdaddr(cmd->bdaddr); print_timeout(cmd->timeout); print_window(cmd->window); print_interval(cmd->interval); } static void remote_oob_ext_data_request_reply_cmd(uint16_t index, const void *data, uint8_t size) { const struct bt_hci_cmd_remote_oob_ext_data_request_reply *cmd = data; print_bdaddr(cmd->bdaddr); print_hash_p192(cmd->hash192); print_randomizer_p192(cmd->randomizer192); print_hash_p256(cmd->hash256); print_randomizer_p256(cmd->randomizer256); } static void hold_mode_cmd(uint16_t index, const void *data, uint8_t size) { const struct bt_hci_cmd_hold_mode *cmd = data; print_handle(cmd->handle); print_slot_625("Hold max interval", cmd->max_interval); print_slot_625("Hold min interval", cmd->min_interval); } static void sniff_mode_cmd(uint16_t index, const void *data, uint8_t size) { const struct bt_hci_cmd_sniff_mode *cmd = data; print_handle(cmd->handle); print_slot_625("Sniff max interval", cmd->max_interval); print_slot_625("Sniff min interval", cmd->min_interval); print_slot_125("Sniff attempt", cmd->attempt); print_slot_125("Sniff timeout", cmd->timeout); } static void exit_sniff_mode_cmd(uint16_t index, const void *data, uint8_t size) { const struct bt_hci_cmd_exit_sniff_mode *cmd = data; print_handle(cmd->handle); } static void park_state_cmd(uint16_t index, const void *data, uint8_t size) { const struct bt_hci_cmd_park_state *cmd = data; print_handle(cmd->handle); print_slot_625("Beacon max interval", cmd->max_interval); print_slot_625("Beacon min interval", cmd->min_interval); } static void exit_park_state_cmd(uint16_t index, const void *data, uint8_t size) { const struct bt_hci_cmd_exit_park_state *cmd = data; print_handle(cmd->handle); } static void qos_setup_cmd(uint16_t index, const void *data, uint8_t size) { const struct bt_hci_cmd_qos_setup *cmd = data; print_handle(cmd->handle); print_field("Flags: 0x%2.2x", cmd->flags); print_service_type(cmd->service_type); print_field("Token rate: %d", le32_to_cpu(cmd->token_rate)); print_field("Peak bandwidth: %d", le32_to_cpu(cmd->peak_bandwidth)); print_field("Latency: %d", le32_to_cpu(cmd->latency)); print_field("Delay variation: %d", le32_to_cpu(cmd->delay_variation)); } static void role_discovery_cmd(uint16_t index, const void *data, uint8_t size) { const struct bt_hci_cmd_role_discovery *cmd = data; print_handle(cmd->handle); } static void role_discovery_rsp(uint16_t index, const void *data, uint8_t size) { const struct bt_hci_rsp_role_discovery *rsp = data; print_status(rsp->status); print_handle(rsp->handle); print_role(rsp->role); } static void switch_role_cmd(uint16_t index, const void *data, uint8_t size) { const struct bt_hci_cmd_switch_role *cmd = data; print_bdaddr(cmd->bdaddr); print_role(cmd->role); } static void read_link_policy_cmd(uint16_t index, const void *data, uint8_t size) { const struct bt_hci_cmd_read_link_policy *cmd = data; print_handle(cmd->handle); } static void read_link_policy_rsp(uint16_t index, const void *data, uint8_t size) { const struct bt_hci_rsp_read_link_policy *rsp = data; print_status(rsp->status); print_handle(rsp->handle); print_link_policy(rsp->policy); } static void write_link_policy_cmd(uint16_t index, const void *data, uint8_t size) { const struct bt_hci_cmd_write_link_policy *cmd = data; print_handle(cmd->handle); print_link_policy(cmd->policy); } static void write_link_policy_rsp(uint16_t index, const void *data, uint8_t size) { const struct bt_hci_rsp_write_link_policy *rsp = data; print_status(rsp->status); print_handle(rsp->handle); } static void read_default_link_policy_rsp(uint16_t index, const void *data, uint8_t size) { const struct bt_hci_rsp_read_default_link_policy *rsp = data; print_status(rsp->status); print_link_policy(rsp->policy); } static void write_default_link_policy_cmd(uint16_t index, const void *data, uint8_t size) { const struct bt_hci_cmd_write_default_link_policy *cmd = data; print_link_policy(cmd->policy); } static void flow_spec_cmd(uint16_t index, const void *data, uint8_t size) { const struct bt_hci_cmd_flow_spec *cmd = data; print_handle(cmd->handle); print_field("Flags: 0x%2.2x", cmd->flags); print_flow_direction(cmd->direction); print_service_type(cmd->service_type); print_field("Token rate: %d", le32_to_cpu(cmd->token_rate)); print_field("Token bucket size: %d", le32_to_cpu(cmd->token_bucket_size)); print_field("Peak bandwidth: %d", le32_to_cpu(cmd->peak_bandwidth)); print_field("Access latency: %d", le32_to_cpu(cmd->access_latency)); } static void sniff_subrating_cmd(uint16_t index, const void *data, uint8_t size) { const struct bt_hci_cmd_sniff_subrating *cmd = data; print_handle(cmd->handle); print_slot_625("Max latency", cmd->max_latency); print_slot_625("Min remote timeout", cmd->min_remote_timeout); print_slot_625("Min local timeout", cmd->min_local_timeout); } static void sniff_subrating_rsp(uint16_t index, const void *data, uint8_t size) { const struct bt_hci_rsp_sniff_subrating *rsp = data; print_status(rsp->status); print_handle(rsp->handle); } static void set_event_mask_cmd(uint16_t index, const void *data, uint8_t size) { const struct bt_hci_cmd_set_event_mask *cmd = data; print_event_mask(cmd->mask, events_table); } static void set_event_filter_cmd(uint16_t index, const void *data, uint8_t size) { uint8_t type = *((const uint8_t *) data); uint8_t filter; const char *str; switch (type) { case 0x00: str = "Clear All Filters"; break; case 0x01: str = "Inquiry Result"; break; case 0x02: str = "Connection Setup"; break; default: str = "Reserved"; break; } print_field("Type: %s (0x%2.2x)", str, type); switch (type) { case 0x00: if (size > 1) { print_text(COLOR_ERROR, " invalid parameter size"); packet_hexdump(data + 1, size - 1); } break; case 0x01: if (size < 2) { print_text(COLOR_ERROR, " invalid parameter size"); break; } filter = *((const uint8_t *) (data + 1)); switch (filter) { case 0x00: str = "Return responses from all devices"; break; case 0x01: str = "Device with specific Class of Device"; break; case 0x02: str = "Device with specific BD_ADDR"; break; default: str = "Reserved"; break; } print_field("Filter: %s (0x%2.2x)", str, filter); packet_hexdump(data + 2, size - 2); break; case 0x02: filter = *((const uint8_t *) (data + 1)); switch (filter) { case 0x00: str = "Allow connections all devices"; break; case 0x01: str = "Allow connections with specific Class of Device"; break; case 0x02: str = "Allow connections with specific BD_ADDR"; break; default: str = "Reserved"; break; } if (size < 2) { print_text(COLOR_ERROR, " invalid parameter size"); break; } print_field("Filter: %s (0x%2.2x)", str, filter); packet_hexdump(data + 2, size - 2); break; default: if (size < 2) { print_text(COLOR_ERROR, " invalid parameter size"); break; } filter = *((const uint8_t *) (data + 1)); print_field("Filter: Reserved (0x%2.2x)", filter); packet_hexdump(data + 2, size - 2); break; } } static void flush_cmd(uint16_t index, const void *data, uint8_t size) { const struct bt_hci_cmd_flush *cmd = data; print_handle(cmd->handle); } static void flush_rsp(uint16_t index, const void *data, uint8_t size) { const struct bt_hci_rsp_flush *rsp = data; print_status(rsp->status); print_handle(rsp->handle); } static void read_pin_type_rsp(uint16_t index, const void *data, uint8_t size) { const struct bt_hci_rsp_read_pin_type *rsp = data; print_status(rsp->status); print_pin_type(rsp->pin_type); } static void write_pin_type_cmd(uint16_t index, const void *data, uint8_t size) { const struct bt_hci_cmd_write_pin_type *cmd = data; print_pin_type(cmd->pin_type); } static void read_stored_link_key_cmd(uint16_t index, const void *data, uint8_t size) { const struct bt_hci_cmd_read_stored_link_key *cmd = data; print_bdaddr(cmd->bdaddr); print_field("Read all: 0x%2.2x", cmd->read_all); } static void read_stored_link_key_rsp(uint16_t index, const void *data, uint8_t size) { const struct bt_hci_rsp_read_stored_link_key *rsp = data; print_status(rsp->status); print_field("Max num keys: %d", le16_to_cpu(rsp->max_num_keys)); print_field("Num keys: %d", le16_to_cpu(rsp->num_keys)); } static void write_stored_link_key_cmd(uint16_t index, const void *data, uint8_t size) { const struct bt_hci_cmd_write_stored_link_key *cmd = data; print_field("Num keys: %d", cmd->num_keys); packet_hexdump(data + 1, size - 1); } static void write_stored_link_key_rsp(uint16_t index, const void *data, uint8_t size) { const struct bt_hci_rsp_write_stored_link_key *rsp = data; print_status(rsp->status); print_field("Num keys: %d", rsp->num_keys); } static void delete_stored_link_key_cmd(uint16_t index, const void *data, uint8_t size) { const struct bt_hci_cmd_delete_stored_link_key *cmd = data; print_bdaddr(cmd->bdaddr); print_field("Delete all: 0x%2.2x", cmd->delete_all); } static void delete_stored_link_key_rsp(uint16_t index, const void *data, uint8_t size) { const struct bt_hci_rsp_delete_stored_link_key *rsp = data; print_status(rsp->status); print_field("Num keys: %d", le16_to_cpu(rsp->num_keys)); } static void write_local_name_cmd(uint16_t index, const void *data, uint8_t size) { const struct bt_hci_cmd_write_local_name *cmd = data; print_name(cmd->name); } static void read_local_name_rsp(uint16_t index, const void *data, uint8_t size) { const struct bt_hci_rsp_read_local_name *rsp = data; print_status(rsp->status); print_name(rsp->name); } static void read_conn_accept_timeout_rsp(uint16_t index, const void *data, uint8_t size) { const struct bt_hci_rsp_read_conn_accept_timeout *rsp = data; print_status(rsp->status); print_timeout(rsp->timeout); } static void write_conn_accept_timeout_cmd(uint16_t index, const void *data, uint8_t size) { const struct bt_hci_cmd_write_conn_accept_timeout *cmd = data; print_timeout(cmd->timeout); } static void read_page_timeout_rsp(uint16_t index, const void *data, uint8_t size) { const struct bt_hci_rsp_read_page_timeout *rsp = data; print_status(rsp->status); print_timeout(rsp->timeout); } static void write_page_timeout_cmd(uint16_t index, const void *data, uint8_t size) { const struct bt_hci_cmd_write_page_timeout *cmd = data; print_timeout(cmd->timeout); } static void read_scan_enable_rsp(uint16_t index, const void *data, uint8_t size) { const struct bt_hci_rsp_read_scan_enable *rsp = data; print_status(rsp->status); print_scan_enable(rsp->enable); } static void write_scan_enable_cmd(uint16_t index, const void *data, uint8_t size) { const struct bt_hci_cmd_write_scan_enable *cmd = data; print_scan_enable(cmd->enable); } static void read_page_scan_activity_rsp(uint16_t index, const void *data, uint8_t size) { const struct bt_hci_rsp_read_page_scan_activity *rsp = data; print_status(rsp->status); print_interval(rsp->interval); print_window(rsp->window); } static void write_page_scan_activity_cmd(uint16_t index, const void *data, uint8_t size) { const struct bt_hci_cmd_write_page_scan_activity *cmd = data; print_interval(cmd->interval); print_window(cmd->window); } static void read_inquiry_scan_activity_rsp(uint16_t index, const void *data, uint8_t size) { const struct bt_hci_rsp_read_inquiry_scan_activity *rsp = data; print_status(rsp->status); print_interval(rsp->interval); print_window(rsp->window); } static void write_inquiry_scan_activity_cmd(uint16_t index, const void *data, uint8_t size) { const struct bt_hci_cmd_write_inquiry_scan_activity *cmd = data; print_interval(cmd->interval); print_window(cmd->window); } static void read_auth_enable_rsp(uint16_t index, const void *data, uint8_t size) { const struct bt_hci_rsp_read_auth_enable *rsp = data; print_status(rsp->status); print_auth_enable(rsp->enable); } static void write_auth_enable_cmd(uint16_t index, const void *data, uint8_t size) { const struct bt_hci_cmd_write_auth_enable *cmd = data; print_auth_enable(cmd->enable); } static void read_encrypt_mode_rsp(uint16_t index, const void *data, uint8_t size) { const struct bt_hci_rsp_read_encrypt_mode *rsp = data; print_status(rsp->status); print_encrypt_mode(rsp->mode); } static void write_encrypt_mode_cmd(uint16_t index, const void *data, uint8_t size) { const struct bt_hci_cmd_write_encrypt_mode *cmd = data; print_encrypt_mode(cmd->mode); } static void read_class_of_dev_rsp(uint16_t index, const void *data, uint8_t size) { const struct bt_hci_rsp_read_class_of_dev *rsp = data; print_status(rsp->status); print_dev_class(rsp->dev_class); } static void write_class_of_dev_cmd(uint16_t index, const void *data, uint8_t size) { const struct bt_hci_cmd_write_class_of_dev *cmd = data; print_dev_class(cmd->dev_class); } static void read_voice_setting_rsp(uint16_t index, const void *data, uint8_t size) { const struct bt_hci_rsp_read_voice_setting *rsp = data; print_status(rsp->status); print_voice_setting(rsp->setting); } static void write_voice_setting_cmd(uint16_t index, const void *data, uint8_t size) { const struct bt_hci_cmd_write_voice_setting *cmd = data; print_voice_setting(cmd->setting); } static void read_auto_flush_timeout_cmd(uint16_t index, const void *data, uint8_t size) { const struct bt_hci_cmd_read_auto_flush_timeout *cmd = data; print_handle(cmd->handle); } static void read_auto_flush_timeout_rsp(uint16_t index, const void *data, uint8_t size) { const struct bt_hci_rsp_read_auto_flush_timeout *rsp = data; print_status(rsp->status); print_handle(rsp->handle); print_flush_timeout(rsp->timeout); } static void write_auto_flush_timeout_cmd(uint16_t index, const void *data, uint8_t size) { const struct bt_hci_cmd_write_auto_flush_timeout *cmd = data; print_handle(cmd->handle); print_flush_timeout(cmd->timeout); } static void write_auto_flush_timeout_rsp(uint16_t index, const void *data, uint8_t size) { const struct bt_hci_rsp_write_auto_flush_timeout *rsp = data; print_status(rsp->status); print_handle(rsp->handle); } static void read_num_broadcast_retrans_rsp(uint16_t index, const void *data, uint8_t size) { const struct bt_hci_rsp_read_num_broadcast_retrans *rsp = data; print_status(rsp->status); print_num_broadcast_retrans(rsp->num_retrans); } static void write_num_broadcast_retrans_cmd(uint16_t index, const void *data, uint8_t size) { const struct bt_hci_cmd_write_num_broadcast_retrans *cmd = data; print_num_broadcast_retrans(cmd->num_retrans); } static void read_hold_mode_activity_rsp(uint16_t index, const void *data, uint8_t size) { const struct bt_hci_rsp_read_hold_mode_activity *rsp = data; print_status(rsp->status); print_hold_mode_activity(rsp->activity); } static void write_hold_mode_activity_cmd(uint16_t index, const void *data, uint8_t size) { const struct bt_hci_cmd_write_hold_mode_activity *cmd = data; print_hold_mode_activity(cmd->activity); } static void read_tx_power_cmd(uint16_t index, const void *data, uint8_t size) { const struct bt_hci_cmd_read_tx_power *cmd = data; print_handle(cmd->handle); print_power_type(cmd->type); } static void read_tx_power_rsp(uint16_t index, const void *data, uint8_t size) { const struct bt_hci_rsp_read_tx_power *rsp = data; print_status(rsp->status); print_handle(rsp->handle); print_power_level(rsp->level, NULL); } static void read_sync_flow_control_rsp(uint16_t index, const void *data, uint8_t size) { const struct bt_hci_rsp_read_sync_flow_control *rsp = data; print_status(rsp->status); print_enable("Flow control", rsp->enable); } static void write_sync_flow_control_cmd(uint16_t index, const void *data, uint8_t size) { const struct bt_hci_cmd_write_sync_flow_control *cmd = data; print_enable("Flow control", cmd->enable); } static void set_host_flow_control_cmd(uint16_t index, const void *data, uint8_t size) { const struct bt_hci_cmd_set_host_flow_control *cmd = data; print_host_flow_control(cmd->enable); } static void host_buffer_size_cmd(uint16_t index, const void *data, uint8_t size) { const struct bt_hci_cmd_host_buffer_size *cmd = data; print_field("ACL MTU: %-4d ACL max packet: %d", le16_to_cpu(cmd->acl_mtu), le16_to_cpu(cmd->acl_max_pkt)); print_field("SCO MTU: %-4d SCO max packet: %d", cmd->sco_mtu, le16_to_cpu(cmd->sco_max_pkt)); } static void host_num_completed_packets_cmd(uint16_t index, const void *data, uint8_t size) { const struct bt_hci_cmd_host_num_completed_packets *cmd = data; print_field("Num handles: %d", cmd->num_handles); print_handle(cmd->handle); print_field("Count: %d", le16_to_cpu(cmd->count)); if (size > sizeof(*cmd)) packet_hexdump(data + sizeof(*cmd), size - sizeof(*cmd)); } static void read_link_supv_timeout_cmd(uint16_t index, const void *data, uint8_t size) { const struct bt_hci_cmd_read_link_supv_timeout *cmd = data; print_handle(cmd->handle); } static void read_link_supv_timeout_rsp(uint16_t index, const void *data, uint8_t size) { const struct bt_hci_rsp_read_link_supv_timeout *rsp = data; print_status(rsp->status); print_handle(rsp->handle); print_timeout(rsp->timeout); } static void write_link_supv_timeout_cmd(uint16_t index, const void *data, uint8_t size) { const struct bt_hci_cmd_write_link_supv_timeout *cmd = data; print_handle(cmd->handle); print_timeout(cmd->timeout); } static void write_link_supv_timeout_rsp(uint16_t index, const void *data, uint8_t size) { const struct bt_hci_rsp_write_link_supv_timeout *rsp = data; print_status(rsp->status); print_handle(rsp->handle); } static void read_num_supported_iac_rsp(uint16_t index, const void *data, uint8_t size) { const struct bt_hci_rsp_read_num_supported_iac *rsp = data; print_status(rsp->status); print_field("Number of IAC: %d", rsp->num_iac); } static void read_current_iac_lap_rsp(uint16_t index, const void *data, uint8_t size) { const struct bt_hci_rsp_read_current_iac_lap *rsp = data; uint8_t i; print_status(rsp->status); print_field("Number of IAC: %d", rsp->num_iac); for (i = 0; i < rsp->num_iac; i++) print_iac(rsp->iac_lap + (i * 3)); } static void write_current_iac_lap_cmd(uint16_t index, const void *data, uint8_t size) { const struct bt_hci_cmd_write_current_iac_lap *cmd = data; uint8_t i; print_field("Number of IAC: %d", cmd->num_iac); for (i = 0; i < cmd->num_iac; i++) print_iac(cmd->iac_lap + (i * 3)); } static void read_page_scan_period_mode_rsp(uint16_t index, const void *data, uint8_t size) { const struct bt_hci_rsp_read_page_scan_period_mode *rsp = data; print_status(rsp->status); print_pscan_period_mode(rsp->mode); } static void write_page_scan_period_mode_cmd(uint16_t index, const void *data, uint8_t size) { const struct bt_hci_cmd_write_page_scan_period_mode *cmd = data; print_pscan_period_mode(cmd->mode); } static void read_page_scan_mode_rsp(uint16_t index, const void *data, uint8_t size) { const struct bt_hci_rsp_read_page_scan_mode *rsp = data; print_status(rsp->status); print_pscan_mode(rsp->mode); } static void write_page_scan_mode_cmd(uint16_t index, const void *data, uint8_t size) { const struct bt_hci_cmd_write_page_scan_mode *cmd = data; print_pscan_mode(cmd->mode); } static void set_afh_host_classification_cmd(uint16_t index, const void *data, uint8_t size) { const struct bt_hci_cmd_set_afh_host_classification *cmd = data; print_channel_map(cmd->map); } static void read_inquiry_scan_type_rsp(uint16_t index, const void *data, uint8_t size) { const struct bt_hci_rsp_read_inquiry_scan_type *rsp = data; print_status(rsp->status); print_inquiry_scan_type(rsp->type); } static void write_inquiry_scan_type_cmd(uint16_t index, const void *data, uint8_t size) { const struct bt_hci_cmd_write_inquiry_scan_type *cmd = data; print_inquiry_scan_type(cmd->type); } static void read_inquiry_mode_rsp(uint16_t index, const void *data, uint8_t size) { const struct bt_hci_rsp_read_inquiry_mode *rsp = data; print_status(rsp->status); print_inquiry_mode(rsp->mode); } static void write_inquiry_mode_cmd(uint16_t index, const void *data, uint8_t size) { const struct bt_hci_cmd_write_inquiry_mode *cmd = data; print_inquiry_mode(cmd->mode); } static void read_page_scan_type_rsp(uint16_t index, const void *data, uint8_t size) { const struct bt_hci_rsp_read_page_scan_type *rsp = data; print_status(rsp->status); print_pscan_type(rsp->type); } static void write_page_scan_type_cmd(uint16_t index, const void *data, uint8_t size) { const struct bt_hci_cmd_write_page_scan_type *cmd = data; print_pscan_type(cmd->type); } static void read_afh_assessment_mode_rsp(uint16_t index, const void *data, uint8_t size) { const struct bt_hci_rsp_read_afh_assessment_mode *rsp = data; print_status(rsp->status); print_enable("Mode", rsp->mode); } static void write_afh_assessment_mode_cmd(uint16_t index, const void *data, uint8_t size) { const struct bt_hci_cmd_write_afh_assessment_mode *cmd = data; print_enable("Mode", cmd->mode); } static void read_ext_inquiry_response_rsp(uint16_t index, const void *data, uint8_t size) { const struct bt_hci_rsp_read_ext_inquiry_response *rsp = data; print_status(rsp->status); print_fec(rsp->fec); print_eir(rsp->data, sizeof(rsp->data), false); } static void write_ext_inquiry_response_cmd(uint16_t index, const void *data, uint8_t size) { const struct bt_hci_cmd_write_ext_inquiry_response *cmd = data; print_fec(cmd->fec); print_eir(cmd->data, sizeof(cmd->data), false); } static void refresh_encrypt_key_cmd(uint16_t index, const void *data, uint8_t size) { const struct bt_hci_cmd_refresh_encrypt_key *cmd = data; print_handle(cmd->handle); } static void read_simple_pairing_mode_rsp(uint16_t index, const void *data, uint8_t size) { const struct bt_hci_rsp_read_simple_pairing_mode *rsp = data; print_status(rsp->status); print_enable("Mode", rsp->mode); } static void write_simple_pairing_mode_cmd(uint16_t index, const void *data, uint8_t size) { const struct bt_hci_cmd_write_simple_pairing_mode *cmd = data; print_enable("Mode", cmd->mode); } static void read_local_oob_data_rsp(uint16_t index, const void *data, uint8_t size) { const struct bt_hci_rsp_read_local_oob_data *rsp = data; print_status(rsp->status); print_hash_p192(rsp->hash); print_randomizer_p192(rsp->randomizer); } static void read_inquiry_resp_tx_power_rsp(uint16_t index, const void *data, uint8_t size) { const struct bt_hci_rsp_read_inquiry_resp_tx_power *rsp = data; print_status(rsp->status); print_power_level(rsp->level, NULL); } static void write_inquiry_tx_power_cmd(uint16_t index, const void *data, uint8_t size) { const struct bt_hci_cmd_write_inquiry_tx_power *cmd = data; print_power_level(cmd->level, NULL); } static void read_erroneous_reporting_rsp(uint16_t index, const void *data, uint8_t size) { const struct bt_hci_rsp_read_erroneous_reporting *rsp = data; print_status(rsp->status); print_enable("Mode", rsp->mode); } static void write_erroneous_reporting_cmd(uint16_t index, const void *data, uint8_t size) { const struct bt_hci_cmd_write_erroneous_reporting *cmd = data; print_enable("Mode", cmd->mode); } static void enhanced_flush_cmd(uint16_t index, const void *data, uint8_t size) { const struct bt_hci_cmd_enhanced_flush *cmd = data; const char *str; print_handle(cmd->handle); switch (cmd->type) { case 0x00: str = "Automatic flushable only"; break; default: str = "Reserved"; break; } print_field("Type: %s (0x%2.2x)", str, cmd->type); } static void send_keypress_notify_cmd(uint16_t index, const void *data, uint8_t size) { const struct bt_hci_cmd_send_keypress_notify *cmd = data; const char *str; print_bdaddr(cmd->bdaddr); switch (cmd->type) { case 0x00: str = "Passkey entry started"; break; case 0x01: str = "Passkey digit entered"; break; case 0x02: str = "Passkey digit erased"; break; case 0x03: str = "Passkey cleared"; break; case 0x04: str = "Passkey entry completed"; break; default: str = "Reserved"; break; } print_field("Type: %s (0x%2.2x)", str, cmd->type); } static void send_keypress_notify_rsp(uint16_t index, const void *data, uint8_t size) { const struct bt_hci_rsp_send_keypress_notify *rsp = data; print_status(rsp->status); print_bdaddr(rsp->bdaddr); } static void set_event_mask_page2_cmd(uint16_t index, const void *data, uint8_t size) { const struct bt_hci_cmd_set_event_mask_page2 *cmd = data; print_event_mask(cmd->mask, events_page2_table); } static void read_location_data_rsp(uint16_t index, const void *data, uint8_t size) { const struct bt_hci_rsp_read_location_data *rsp = data; print_status(rsp->status); print_location_domain_aware(rsp->domain_aware); print_location_domain(rsp->domain); print_location_domain_options(rsp->domain_options); print_location_options(rsp->options); } static void write_location_data_cmd(uint16_t index, const void *data, uint8_t size) { const struct bt_hci_cmd_write_location_data *cmd = data; print_location_domain_aware(cmd->domain_aware); print_location_domain(cmd->domain); print_location_domain_options(cmd->domain_options); print_location_options(cmd->options); } static void read_flow_control_mode_rsp(uint16_t index, const void *data, uint8_t size) { const struct bt_hci_rsp_read_flow_control_mode *rsp = data; print_status(rsp->status); print_flow_control_mode(rsp->mode); } static void write_flow_control_mode_cmd(uint16_t index, const void *data, uint8_t size) { const struct bt_hci_cmd_write_flow_control_mode *cmd = data; print_flow_control_mode(cmd->mode); } static void read_enhanced_tx_power_cmd(uint16_t index, const void *data, uint8_t size) { const struct bt_hci_cmd_read_enhanced_tx_power *cmd = data; print_handle(cmd->handle); print_power_type(cmd->type); } static void read_enhanced_tx_power_rsp(uint16_t index, const void *data, uint8_t size) { const struct bt_hci_rsp_read_enhanced_tx_power *rsp = data; print_status(rsp->status); print_handle(rsp->handle); print_power_level(rsp->level_gfsk, "GFSK"); print_power_level(rsp->level_dqpsk, "DQPSK"); print_power_level(rsp->level_8dpsk, "8DPSK"); } static void short_range_mode_cmd(uint16_t index, const void *data, uint8_t size) { const struct bt_hci_cmd_short_range_mode *cmd = data; print_phy_handle(cmd->phy_handle); print_enable("Short range mode", cmd->mode); } static void read_le_host_supported_rsp(uint16_t index, const void *data, uint8_t size) { const struct bt_hci_rsp_read_le_host_supported *rsp = data; print_status(rsp->status); print_field("Supported: 0x%2.2x", rsp->supported); print_field("Simultaneous: 0x%2.2x", rsp->simultaneous); } static void write_le_host_supported_cmd(uint16_t index, const void *data, uint8_t size) { const struct bt_hci_cmd_write_le_host_supported *cmd = data; print_field("Supported: 0x%2.2x", cmd->supported); print_field("Simultaneous: 0x%2.2x", cmd->simultaneous); } static void set_reserved_lt_addr_cmd(uint16_t index, const void *data, uint8_t size) { const struct bt_hci_cmd_set_reserved_lt_addr *cmd = data; print_lt_addr(cmd->lt_addr); } static void set_reserved_lt_addr_rsp(uint16_t index, const void *data, uint8_t size) { const struct bt_hci_rsp_set_reserved_lt_addr *rsp = data; print_status(rsp->status); print_lt_addr(rsp->lt_addr); } static void delete_reserved_lt_addr_cmd(uint16_t index, const void *data, uint8_t size) { const struct bt_hci_cmd_delete_reserved_lt_addr *cmd = data; print_lt_addr(cmd->lt_addr); } static void delete_reserved_lt_addr_rsp(uint16_t index, const void *data, uint8_t size) { const struct bt_hci_rsp_delete_reserved_lt_addr *rsp = data; print_status(rsp->status); print_lt_addr(rsp->lt_addr); } static void set_peripheral_broadcast_data_cmd(uint16_t index, const void *data, uint8_t size) { const struct bt_hci_cmd_set_peripheral_broadcast_data *cmd = data; print_lt_addr(cmd->lt_addr); print_broadcast_fragment(cmd->fragment); print_field("Length: %d", cmd->length); if (size - 3 != cmd->length) print_text(COLOR_ERROR, "invalid data size (%d != %d)", size - 3, cmd->length); packet_hexdump(data + 3, size - 3); } static void set_peripheral_broadcast_data_rsp(uint16_t index, const void *data, uint8_t size) { const struct bt_hci_rsp_set_peripheral_broadcast_data *rsp = data; print_status(rsp->status); print_lt_addr(rsp->lt_addr); } static void read_sync_train_params_rsp(uint16_t index, const void *data, uint8_t size) { const struct bt_hci_rsp_read_sync_train_params *rsp = data; print_status(rsp->status); print_interval(rsp->interval); print_field("Timeout: %.3f msec (0x%8.8x)", le32_to_cpu(rsp->timeout) * 0.625, le32_to_cpu(rsp->timeout)); print_field("Service data: 0x%2.2x", rsp->service_data); } static void write_sync_train_params_cmd(uint16_t index, const void *data, uint8_t size) { const struct bt_hci_cmd_write_sync_train_params *cmd = data; print_slot_625("Min interval", cmd->min_interval); print_slot_625("Max interval", cmd->max_interval); print_field("Timeout: %.3f msec (0x%8.8x)", le32_to_cpu(cmd->timeout) * 0.625, le32_to_cpu(cmd->timeout)); print_field("Service data: 0x%2.2x", cmd->service_data); } static void write_sync_train_params_rsp(uint16_t index, const void *data, uint8_t size) { const struct bt_hci_rsp_write_sync_train_params *rsp = data; print_status(rsp->status); print_interval(rsp->interval); } static void read_secure_conn_support_rsp(uint16_t index, const void *data, uint8_t size) { const struct bt_hci_rsp_read_secure_conn_support *rsp = data; print_status(rsp->status); print_enable("Support", rsp->support); } static void write_secure_conn_support_cmd(uint16_t index, const void *data, uint8_t size) { const struct bt_hci_cmd_write_secure_conn_support *cmd = data; print_enable("Support", cmd->support); } static void read_auth_payload_timeout_cmd(uint16_t index, const void *data, uint8_t size) { const struct bt_hci_cmd_read_auth_payload_timeout *cmd = data; print_handle(cmd->handle); } static void read_auth_payload_timeout_rsp(uint16_t index, const void *data, uint8_t size) { const struct bt_hci_rsp_read_auth_payload_timeout *rsp = data; print_status(rsp->status); print_handle(rsp->handle); print_auth_payload_timeout(rsp->timeout); } static void write_auth_payload_timeout_cmd(uint16_t index, const void *data, uint8_t size) { const struct bt_hci_cmd_write_auth_payload_timeout *cmd = data; print_handle(cmd->handle); print_auth_payload_timeout(cmd->timeout); } static void write_auth_payload_timeout_rsp(uint16_t index, const void *data, uint8_t size) { const struct bt_hci_rsp_write_auth_payload_timeout *rsp = data; print_status(rsp->status); print_handle(rsp->handle); } static void read_local_oob_ext_data_rsp(uint16_t index, const void *data, uint8_t size) { const struct bt_hci_rsp_read_local_oob_ext_data *rsp = data; print_status(rsp->status); print_hash_p192(rsp->hash192); print_randomizer_p192(rsp->randomizer192); print_hash_p256(rsp->hash256); print_randomizer_p256(rsp->randomizer256); } static void read_ext_page_timeout_rsp(uint16_t index, const void *data, uint8_t size) { const struct bt_hci_rsp_read_ext_page_timeout *rsp = data; print_status(rsp->status); print_timeout(rsp->timeout); } static void write_ext_page_timeout_cmd(uint16_t index, const void *data, uint8_t size) { const struct bt_hci_cmd_write_ext_page_timeout *cmd = data; print_timeout(cmd->timeout); } static void read_ext_inquiry_length_rsp(uint16_t index, const void *data, uint8_t size) { const struct bt_hci_rsp_read_ext_inquiry_length *rsp = data; print_status(rsp->status); print_interval(rsp->interval); } static void write_ext_inquiry_length_cmd(uint16_t index, const void *data, uint8_t size) { const struct bt_hci_cmd_write_ext_inquiry_length *cmd = data; print_interval(cmd->interval); } static void read_local_version_rsp(uint16_t index, const void *data, uint8_t size) { const struct bt_hci_rsp_read_local_version *rsp = data; uint16_t manufacturer; print_status(rsp->status); print_hci_version(rsp->hci_ver, rsp->hci_rev); manufacturer = le16_to_cpu(rsp->manufacturer); if (index_current < MAX_INDEX) { switch (index_list[index_current].type) { case HCI_PRIMARY: print_lmp_version(rsp->lmp_ver, rsp->lmp_subver); break; case HCI_AMP: print_pal_version(rsp->lmp_ver, rsp->lmp_subver); break; } index_list[index_current].manufacturer = manufacturer; } print_manufacturer(rsp->manufacturer); switch (manufacturer) { case 15: print_manufacturer_broadcom(rsp->lmp_subver, rsp->hci_rev); break; } } static void read_local_commands_rsp(uint16_t index, const void *data, uint8_t size) { const struct bt_hci_rsp_read_local_commands *rsp = data; print_status(rsp->status); print_commands(rsp->commands); } static void read_local_features_rsp(uint16_t index, const void *data, uint8_t size) { const struct bt_hci_rsp_read_local_features *rsp = data; print_status(rsp->status); print_features(0, rsp->features, 0x00); } static void read_local_ext_features_cmd(uint16_t index, const void *data, uint8_t size) { const struct bt_hci_cmd_read_local_ext_features *cmd = data; print_field("Page: %d", cmd->page); } static void read_local_ext_features_rsp(uint16_t index, const void *data, uint8_t size) { const struct bt_hci_rsp_read_local_ext_features *rsp = data; print_status(rsp->status); print_field("Page: %d/%d", rsp->page, rsp->max_page); print_features(rsp->page, rsp->features, 0x00); } static void read_buffer_size_rsp(uint16_t index, const void *data, uint8_t size) { const struct bt_hci_rsp_read_buffer_size *rsp = data; print_status(rsp->status); print_field("ACL MTU: %-4d ACL max packet: %d", le16_to_cpu(rsp->acl_mtu), le16_to_cpu(rsp->acl_max_pkt)); print_field("SCO MTU: %-4d SCO max packet: %d", rsp->sco_mtu, le16_to_cpu(rsp->sco_max_pkt)); } static void read_country_code_rsp(uint16_t index, const void *data, uint8_t size) { const struct bt_hci_rsp_read_country_code *rsp = data; const char *str; print_status(rsp->status); switch (rsp->code) { case 0x00: str = "North America, Europe*, Japan"; break; case 0x01: str = "France"; break; default: str = "Reserved"; break; } print_field("Country code: %s (0x%2.2x)", str, rsp->code); } static void read_bd_addr_rsp(uint16_t index, const void *data, uint8_t size) { const struct bt_hci_rsp_read_bd_addr *rsp = data; print_status(rsp->status); print_bdaddr(rsp->bdaddr); if (index_current < MAX_INDEX) memcpy(index_list[index_current].bdaddr, rsp->bdaddr, 6); } static void read_data_block_size_rsp(uint16_t index, const void *data, uint8_t size) { const struct bt_hci_rsp_read_data_block_size *rsp = data; print_status(rsp->status); print_field("Max ACL length: %d", le16_to_cpu(rsp->max_acl_len)); print_field("Block length: %d", le16_to_cpu(rsp->block_len)); print_field("Num blocks: %d", le16_to_cpu(rsp->num_blocks)); } static void read_local_codecs_rsp(uint16_t index, const void *data, uint8_t size) { const struct bt_hci_rsp_read_local_codecs *rsp = data; uint8_t i, num_vnd_codecs; if (rsp->num_codecs + 3 > size) { print_field("Invalid number of codecs."); return; } print_status(rsp->status); print_field("Number of supported codecs: %d", rsp->num_codecs); for (i = 0; i < rsp->num_codecs; i++) print_codec_id(" Codec", rsp->codec[i]); num_vnd_codecs = rsp->codec[rsp->num_codecs]; print_field("Number of vendor codecs: %d", num_vnd_codecs); packet_hexdump(data + rsp->num_codecs + 3, size - rsp->num_codecs - 3); } static void print_codecs(const void *data, int i) { const struct bt_hci_codec *codec = data; print_codec(" Codec", codec); } typedef void (*print_list_func_t)(const void *data, int i); static void print_list(const void *data, uint8_t size, int num_items, size_t item_size, print_list_func_t func) { int i; for (i = 0; size >= item_size && num_items; i++) { if (func) func(data, i); data += item_size; size -= item_size; num_items--; } if (num_items) print_hex_field("", data, size); } static void print_vnd_codecs_v2(const void *data, int i) { const struct bt_hci_vnd_codec_v2 *codec = data; uint8_t mask; packet_print_company(" Company ID", le16_to_cpu(codec->cid)); print_field(" Vendor Codec ID: 0x%4.4x", le16_to_cpu(codec->vid)); print_field(" Logical Transport Type: 0x%02x", codec->transport); mask = print_bitfield(4, codec->transport, codec_transport_table); if (mask) print_text(COLOR_UNKNOWN_SERVICE_CLASS, " Unknown transport (0x%2.2x)", mask); } static void read_local_codecs_rsp_v2(uint16_t index, const void *data, uint8_t size) { const struct bt_hci_rsp_read_local_codecs_v2 *rsp = data; uint8_t num_vnd_codecs; if (rsp->num_codecs + 3 > size) { print_field("Invalid number of codecs."); return; } print_status(rsp->status); print_field("Number of supported codecs: %d", rsp->num_codecs); size -= sizeof(*rsp); if (size < rsp->num_codecs * sizeof(*rsp->codec)) { print_field("Invalid number of codecs."); return; } print_list(rsp->codec, size, rsp->num_codecs, sizeof(*rsp->codec), print_codecs); size -= rsp->num_codecs * sizeof(*rsp->codec); if (size < sizeof(uint8_t)) { print_field("Invalid number of vendor codecs."); return; } num_vnd_codecs = rsp->codec[rsp->num_codecs].id; size -= 1; print_field("Number of vendor codecs: %d", num_vnd_codecs); if (size < num_vnd_codecs * sizeof(*rsp->codec)) { print_field("Invalid number of vendor codecs."); return; } print_list(&rsp->codec[rsp->num_codecs] + 1, size, num_vnd_codecs, sizeof(struct bt_hci_vnd_codec_v2), print_vnd_codecs_v2); } static void print_path_direction(const char *prefix, uint8_t dir) { const char *str; switch (dir) { case 0x00: str = "Input (Host to Controller)"; break; case 0x01: str = "Output (Controller to Host)"; break; default: str = "Reserved"; break; } print_field("%s: %s (0x%2.2x)", prefix, str, dir); } static void print_vnd_codec(const char *label, const struct bt_hci_vnd_codec *codec) { uint8_t mask; print_codec_id(label, codec->id); if (codec->id == 0xff) { packet_print_company("Company Codec ID", le16_to_cpu(codec->cid)); print_field("Vendor Codec ID: %d", le16_to_cpu(codec->vid)); } print_field("Logical Transport Type: 0x%02x", codec->transport); mask = print_bitfield(2, codec->transport, codec_transport_table); if (mask) print_text(COLOR_UNKNOWN_SERVICE_CLASS, " Unknown transport (0x%2.2x)", mask); } static void read_local_codec_caps_cmd(uint16_t index, const void *data, uint8_t size) { const struct bt_hci_cmd_read_local_codec_caps *cmd = data; print_vnd_codec("Codec", &cmd->codec); print_path_direction("Direction", cmd->dir); } static void read_local_codec_caps_rsp(uint16_t index, const void *data, uint8_t size) { const struct bt_hci_rsp_read_local_codec_caps *rsp = data; uint8_t i; print_status(rsp->status); print_field("Number of codec capabilities: %d", rsp->num); data += sizeof(*rsp); size -= sizeof(*rsp); for (i = 0; i < rsp->num; i++) { const struct bt_hci_codec_caps *caps = data; if (size < sizeof(*caps)) { print_field("Invalid capabilities: %u < %zu", size, sizeof(*caps)); return; } print_field(" Capabilities #%u:", i); packet_hexdump(caps->data, caps->len); data += 1 + caps->len; size -= 1 + caps->len; } } static void read_local_ctrl_delay_cmd(uint16_t index, const void *data, uint8_t size) { const struct bt_hci_cmd_read_local_ctrl_delay *cmd = data; print_vnd_codec("Codec", &cmd->codec); print_path_direction("Direction", cmd->dir); print_field("Length Codec Configuration: %u", cmd->codec_cfg_len); } static void config_data_path_cmd(uint16_t index, const void *data, uint8_t size) { const struct bt_hci_cmd_config_data_path *cmd = data; print_path_direction("Direction", cmd->dir); print_field("ID: %u", cmd->id); print_field("Vendor Specific Config Length: %u", cmd->vnd_config_len); print_hex_field("Vendor Specific Config", cmd->vnd_config, cmd->vnd_config_len); } static void print_usec_interval(const char *prefix, const uint8_t interval[3]) { uint32_t value = get_le24(interval); print_field("%s: %u us (0x%6.6x)", prefix, value, value); } static void read_local_ctrl_delay_rsp(uint16_t index, const void *data, uint8_t size) { const struct bt_hci_rsp_read_local_ctrl_delay *rsp = data; print_status(rsp->status); print_usec_interval("Minimum Controller delay", rsp->min_delay); print_usec_interval("Maximum Controller delay", rsp->max_delay); } static void read_local_pairing_options_rsp(uint16_t index, const void *data, uint8_t size) { const struct bt_hci_rsp_read_local_pairing_options *rsp = data; print_status(rsp->status); print_field("Pairing options: 0x%2.2x", rsp->pairing_options); print_field("Max encryption key size: %u octets", rsp->max_key_size); } static void read_failed_contact_counter_cmd(uint16_t index, const void *data, uint8_t size) { const struct bt_hci_cmd_read_failed_contact_counter *cmd = data; print_handle(cmd->handle); } static void read_failed_contact_counter_rsp(uint16_t index, const void *data, uint8_t size) { const struct bt_hci_rsp_read_failed_contact_counter *rsp = data; print_status(rsp->status); print_handle(rsp->handle); print_field("Counter: %u", le16_to_cpu(rsp->counter)); } static void reset_failed_contact_counter_cmd(uint16_t index, const void *data, uint8_t size) { const struct bt_hci_cmd_reset_failed_contact_counter *cmd = data; print_handle(cmd->handle); } static void reset_failed_contact_counter_rsp(uint16_t index, const void *data, uint8_t size) { const struct bt_hci_rsp_reset_failed_contact_counter *rsp = data; print_status(rsp->status); print_handle(rsp->handle); } static void read_link_quality_cmd(uint16_t index, const void *data, uint8_t size) { const struct bt_hci_cmd_read_link_quality *cmd = data; print_handle(cmd->handle); } static void read_link_quality_rsp(uint16_t index, const void *data, uint8_t size) { const struct bt_hci_rsp_read_link_quality *rsp = data; print_status(rsp->status); print_handle(rsp->handle); print_field("Link quality: 0x%2.2x", rsp->link_quality); } static void read_rssi_cmd(uint16_t index, const void *data, uint8_t size) { const struct bt_hci_cmd_read_rssi *cmd = data; print_handle(cmd->handle); } static void read_rssi_rsp(uint16_t index, const void *data, uint8_t size) { const struct bt_hci_rsp_read_rssi *rsp = data; print_status(rsp->status); print_handle(rsp->handle); print_rssi(rsp->rssi); } static void read_afh_channel_map_cmd(uint16_t index, const void *data, uint8_t size) { const struct bt_hci_cmd_read_afh_channel_map *cmd = data; print_handle(cmd->handle); } static void read_afh_channel_map_rsp(uint16_t index, const void *data, uint8_t size) { const struct bt_hci_rsp_read_afh_channel_map *rsp = data; print_status(rsp->status); print_handle(rsp->handle); print_enable("Mode", rsp->mode); print_channel_map(rsp->map); } static void read_clock_cmd(uint16_t index, const void *data, uint8_t size) { const struct bt_hci_cmd_read_clock *cmd = data; print_handle(cmd->handle); print_clock_type(cmd->type); } static void read_clock_rsp(uint16_t index, const void *data, uint8_t size) { const struct bt_hci_rsp_read_clock *rsp = data; print_status(rsp->status); print_handle(rsp->handle); print_clock(rsp->clock); print_clock_accuracy(rsp->accuracy); } static void read_encrypt_key_size_cmd(uint16_t index, const void *data, uint8_t size) { const struct bt_hci_cmd_read_encrypt_key_size *cmd = data; print_handle(cmd->handle); } static void read_encrypt_key_size_rsp(uint16_t index, const void *data, uint8_t size) { const struct bt_hci_rsp_read_encrypt_key_size *rsp = data; print_status(rsp->status); print_handle(rsp->handle); print_key_size(rsp->key_size); } static void read_local_amp_info_rsp(uint16_t index, const void *data, uint8_t size) { const struct bt_hci_rsp_read_local_amp_info *rsp = data; const char *str; print_status(rsp->status); print_amp_status(rsp->amp_status); print_field("Total bandwidth: %d kbps", le32_to_cpu(rsp->total_bw)); print_field("Max guaranteed bandwidth: %d kbps", le32_to_cpu(rsp->max_bw)); print_field("Min latency: %d", le32_to_cpu(rsp->min_latency)); print_field("Max PDU size: %d", le32_to_cpu(rsp->max_pdu)); switch (rsp->amp_type) { case 0x00: str = "Primary BR/EDR Controller"; break; case 0x01: str = "802.11 AMP Controller"; break; default: str = "Reserved"; break; } print_field("Controller type: %s (0x%2.2x)", str, rsp->amp_type); print_field("PAL capabilities: 0x%4.4x", le16_to_cpu(rsp->pal_cap)); print_field("Max ASSOC length: %d", le16_to_cpu(rsp->max_assoc_len)); print_field("Max flush timeout: %d", le32_to_cpu(rsp->max_flush_to)); print_field("Best effort flush timeout: %d", le32_to_cpu(rsp->be_flush_to)); } static void read_local_amp_assoc_cmd(uint16_t index, const void *data, uint8_t size) { const struct bt_hci_cmd_read_local_amp_assoc *cmd = data; print_phy_handle(cmd->phy_handle); print_field("Length so far: %d", le16_to_cpu(cmd->len_so_far)); print_field("Max ASSOC length: %d", le16_to_cpu(cmd->max_assoc_len)); } static void read_local_amp_assoc_rsp(uint16_t index, const void *data, uint8_t size) { const struct bt_hci_rsp_read_local_amp_assoc *rsp = data; print_status(rsp->status); print_phy_handle(rsp->phy_handle); print_field("Remaining ASSOC length: %d", le16_to_cpu(rsp->remain_assoc_len)); packet_hexdump(data + 4, size - 4); } static void write_remote_amp_assoc_cmd(uint16_t index, const void *data, uint8_t size) { const struct bt_hci_cmd_write_remote_amp_assoc *cmd = data; print_phy_handle(cmd->phy_handle); print_field("Length so far: %d", le16_to_cpu(cmd->len_so_far)); print_field("Remaining ASSOC length: %d", le16_to_cpu(cmd->remain_assoc_len)); packet_hexdump(data + 5, size - 5); } static void write_remote_amp_assoc_rsp(uint16_t index, const void *data, uint8_t size) { const struct bt_hci_rsp_write_remote_amp_assoc *rsp = data; print_status(rsp->status); print_phy_handle(rsp->phy_handle); } static void get_mws_transport_config_rsp(uint16_t index, const void *data, uint8_t size) { const struct bt_hci_rsp_get_mws_transport_config *rsp = data; uint8_t sum_baud_rates = 0; int i; print_status(rsp->status); print_field("Number of transports: %d", rsp->num_transports); for (i = 0; i < rsp->num_transports; i++) { uint8_t transport = rsp->transport[0]; uint8_t num_baud_rates = rsp->transport[1]; const char *str; switch (transport) { case 0x00: str = "Disbabled"; break; case 0x01: str = "WCI-1"; break; case 0x02: str = "WCI-2"; break; default: str = "Reserved"; break; } print_field(" Transport layer: %s (0x%2.2x)", str, transport); print_field(" Number of baud rates: %d", num_baud_rates); sum_baud_rates += num_baud_rates; } print_field("Baud rate list: %u entr%s", sum_baud_rates, sum_baud_rates == 1 ? "y" : "ies"); for (i = 0; i < sum_baud_rates; i++) { uint32_t to_baud_rate, from_baud_rate; to_baud_rate = get_le32(data + 2 + rsp->num_transports * 2 + i * 4); from_baud_rate = get_le32(data + 2 + rsp->num_transports * 2 + sum_baud_rates * 4 + i * 4); print_field(" Bluetooth to MWS: %d", to_baud_rate); print_field(" MWS to Bluetooth: %d", from_baud_rate); } packet_hexdump(data + 2 + rsp->num_transports * 2 + sum_baud_rates * 8, size - 2 - rsp->num_transports * 2 - sum_baud_rates * 8); } static void set_triggered_clock_capture_cmd(uint16_t index, const void *data, uint8_t size) { const struct bt_hci_cmd_set_triggered_clock_capture *cmd = data; print_handle(cmd->handle); print_enable("Capture", cmd->enable); print_clock_type(cmd->type); print_lpo_allowed(cmd->lpo_allowed); print_field("Clock captures to filter: %u", cmd->num_filter); } static void read_loopback_mode_rsp(uint16_t index, const void *data, uint8_t size) { const struct bt_hci_rsp_read_loopback_mode *rsp = data; print_status(rsp->status); print_loopback_mode(rsp->mode); } static void write_loopback_mode_cmd(uint16_t index, const void *data, uint8_t size) { const struct bt_hci_cmd_write_loopback_mode *cmd = data; print_loopback_mode(cmd->mode); } static void write_ssp_debug_mode_cmd(uint16_t index, const void *data, uint8_t size) { const struct bt_hci_cmd_write_ssp_debug_mode *cmd = data; print_enable("Debug Mode", cmd->mode); } static void le_set_event_mask_cmd(uint16_t index, const void *data, uint8_t size) { const struct bt_hci_cmd_le_set_event_mask *cmd = data; print_event_mask(cmd->mask, events_le_table); } static void le_read_buffer_size_rsp(uint16_t index, const void *data, uint8_t size) { const struct bt_hci_rsp_le_read_buffer_size *rsp = data; print_status(rsp->status); print_field("Data packet length: %d", le16_to_cpu(rsp->le_mtu)); print_field("Num data packets: %d", rsp->le_max_pkt); } static void le_read_local_features_rsp(uint16_t index, const void *data, uint8_t size) { const struct bt_hci_rsp_le_read_local_features *rsp = data; print_status(rsp->status); print_features(0, rsp->features, 0x01); } static void le_set_random_address_cmd(uint16_t index, const void *data, uint8_t size) { const struct bt_hci_cmd_le_set_random_address *cmd = data; print_addr("Address", cmd->addr, 0x01); } static void le_set_adv_parameters_cmd(uint16_t index, const void *data, uint8_t size) { const struct bt_hci_cmd_le_set_adv_parameters *cmd = data; const char *str; print_slot_625("Min advertising interval", cmd->min_interval); print_slot_625("Max advertising interval", cmd->max_interval); switch (cmd->type) { case 0x00: str = "Connectable undirected - ADV_IND"; break; case 0x01: str = "Connectable directed - ADV_DIRECT_IND (high duty cycle)"; break; case 0x02: str = "Scannable undirected - ADV_SCAN_IND"; break; case 0x03: str = "Non connectable undirected - ADV_NONCONN_IND"; break; case 0x04: str = "Connectable directed - ADV_DIRECT_IND (low duty cycle)"; break; default: str = "Reserved"; break; } print_field("Type: %s (0x%2.2x)", str, cmd->type); print_own_addr_type(cmd->own_addr_type); print_addr_type("Direct address type", cmd->direct_addr_type); print_addr("Direct address", cmd->direct_addr, cmd->direct_addr_type); print_adv_channel_map("Channel map", cmd->channel_map); print_adv_filter_policy("Filter policy", cmd->filter_policy); } static void le_read_adv_tx_power_rsp(uint16_t index, const void *data, uint8_t size) { const struct bt_hci_rsp_le_read_adv_tx_power *rsp = data; print_status(rsp->status); print_power_level(rsp->level, NULL); } static void le_set_adv_data_cmd(uint16_t index, const void *data, uint8_t size) { const struct bt_hci_cmd_le_set_adv_data *cmd = data; print_field("Length: %d", cmd->len); print_eir(cmd->data, cmd->len, true); } static void le_set_scan_rsp_data_cmd(uint16_t index, const void *data, uint8_t size) { const struct bt_hci_cmd_le_set_scan_rsp_data *cmd = data; print_field("Length: %d", cmd->len); print_eir(cmd->data, cmd->len, true); } static void le_set_adv_enable_cmd(uint16_t index, const void *data, uint8_t size) { const struct bt_hci_cmd_le_set_adv_enable *cmd = data; print_enable("Advertising", cmd->enable); } static void print_scan_type(const char *label, uint8_t type) { const char *str; switch (type) { case 0x00: str = "Passive"; break; case 0x01: str = "Active"; break; default: str = "Reserved"; break; } print_field("%s: %s (0x%2.2x)", label, str, type); } static void print_scan_filter_policy(uint8_t policy) { const char *str; switch (policy) { case 0x00: str = "Accept all advertisement"; break; case 0x01: str = "Ignore not in accept list"; break; case 0x02: str = "Accept all advertisement, inc. directed unresolved RPA"; break; case 0x03: str = "Ignore not in accept list, exc. directed unresolved RPA"; break; default: str = "Reserved"; break; } print_field("Filter policy: %s (0x%2.2x)", str, policy); } static void le_set_scan_parameters_cmd(uint16_t index, const void *data, uint8_t size) { const struct bt_hci_cmd_le_set_scan_parameters *cmd = data; print_scan_type("Type", cmd->type); print_interval(cmd->interval); print_window(cmd->window); print_own_addr_type(cmd->own_addr_type); print_scan_filter_policy(cmd->filter_policy); } static void le_set_scan_enable_cmd(uint16_t index, const void *data, uint8_t size) { const struct bt_hci_cmd_le_set_scan_enable *cmd = data; print_enable("Scanning", cmd->enable); print_enable("Filter duplicates", cmd->filter_dup); } static void le_create_conn_cmd(uint16_t index, const void *data, uint8_t size) { const struct bt_hci_cmd_le_create_conn *cmd = data; const char *str; print_slot_625("Scan interval", cmd->scan_interval); print_slot_625("Scan window", cmd->scan_window); switch (cmd->filter_policy) { case 0x00: str = "Accept list is not used"; break; case 0x01: str = "Accept list is used"; break; default: str = "Reserved"; break; } print_field("Filter policy: %s (0x%2.2x)", str, cmd->filter_policy); print_peer_addr_type("Peer address type", cmd->peer_addr_type); print_addr("Peer address", cmd->peer_addr, cmd->peer_addr_type); print_own_addr_type(cmd->own_addr_type); print_slot_125("Min connection interval", cmd->min_interval); print_slot_125("Max connection interval", cmd->max_interval); print_conn_latency("Connection latency", cmd->latency); print_field("Supervision timeout: %d msec (0x%4.4x)", le16_to_cpu(cmd->supv_timeout) * 10, le16_to_cpu(cmd->supv_timeout)); print_slot_625("Min connection length", cmd->min_length); print_slot_625("Max connection length", cmd->max_length); } static void le_read_accept_list_size_rsp(uint16_t index, const void *data, uint8_t size) { const struct bt_hci_rsp_le_read_accept_list_size *rsp = data; print_status(rsp->status); print_field("Size: %u", rsp->size); } static void le_add_to_accept_list_cmd(uint16_t index, const void *data, uint8_t size) { const struct bt_hci_cmd_le_add_to_accept_list *cmd = data; print_addr_type("Address type", cmd->addr_type); print_addr("Address", cmd->addr, cmd->addr_type); } static void le_remove_from_accept_list_cmd(uint16_t index, const void *data, uint8_t size) { const struct bt_hci_cmd_le_remove_from_accept_list *cmd = data; print_addr_type("Address type", cmd->addr_type); print_addr("Address", cmd->addr, cmd->addr_type); } static void le_conn_update_cmd(uint16_t index, const void *data, uint8_t size) { const struct bt_hci_cmd_le_conn_update *cmd = data; print_handle(cmd->handle); print_slot_125("Min connection interval", cmd->min_interval); print_slot_125("Max connection interval", cmd->max_interval); print_conn_latency("Connection latency", cmd->latency); print_field("Supervision timeout: %d msec (0x%4.4x)", le16_to_cpu(cmd->supv_timeout) * 10, le16_to_cpu(cmd->supv_timeout)); print_slot_625("Min connection length", cmd->min_length); print_slot_625("Max connection length", cmd->max_length); } static void le_set_host_classification_cmd(uint16_t index, const void *data, uint8_t size) { const struct bt_hci_cmd_le_set_host_classification *cmd = data; print_le_channel_map(cmd->map); } static void le_read_channel_map_cmd(uint16_t index, const void *data, uint8_t size) { const struct bt_hci_cmd_le_read_channel_map *cmd = data; print_handle(cmd->handle); } static void le_read_channel_map_rsp(uint16_t index, const void *data, uint8_t size) { const struct bt_hci_rsp_le_read_channel_map *rsp = data; print_status(rsp->status); print_handle(rsp->handle); print_le_channel_map(rsp->map); } static void le_read_remote_features_cmd(uint16_t index, const void *data, uint8_t size) { const struct bt_hci_cmd_le_read_remote_features *cmd = data; print_handle(cmd->handle); } static void le_encrypt_cmd(uint16_t index, const void *data, uint8_t size) { const struct bt_hci_cmd_le_encrypt *cmd = data; print_key("Key", cmd->key); print_key("Plaintext data", cmd->plaintext); } static void le_encrypt_rsp(uint16_t index, const void *data, uint8_t size) { const struct bt_hci_rsp_le_encrypt *rsp = data; print_status(rsp->status); print_key("Encrypted data", rsp->data); } static void le_rand_rsp(uint16_t index, const void *data, uint8_t size) { const struct bt_hci_rsp_le_rand *rsp = data; print_status(rsp->status); print_random_number(rsp->number); } static void le_start_encrypt_cmd(uint16_t index, const void *data, uint8_t size) { const struct bt_hci_cmd_le_start_encrypt *cmd = data; print_handle(cmd->handle); print_random_number(cmd->rand); print_encrypted_diversifier(cmd->ediv); print_key("Long term key", cmd->ltk); } static void le_ltk_req_reply_cmd(uint16_t index, const void *data, uint8_t size) { const struct bt_hci_cmd_le_ltk_req_reply *cmd = data; print_handle(cmd->handle); print_key("Long term key", cmd->ltk); } static void le_ltk_req_reply_rsp(uint16_t index, const void *data, uint8_t size) { const struct bt_hci_rsp_le_ltk_req_reply *rsp = data; print_status(rsp->status); print_handle(rsp->handle); } static void le_ltk_req_neg_reply_cmd(uint16_t index, const void *data, uint8_t size) { const struct bt_hci_cmd_le_ltk_req_neg_reply *cmd = data; print_handle(cmd->handle); } static void le_ltk_req_neg_reply_rsp(uint16_t index, const void *data, uint8_t size) { const struct bt_hci_rsp_le_ltk_req_neg_reply *rsp = data; print_status(rsp->status); print_handle(rsp->handle); } static void le_read_supported_states_rsp(uint16_t index, const void *data, uint8_t size) { const struct bt_hci_rsp_le_read_supported_states *rsp = data; print_status(rsp->status); print_le_states(rsp->states); } static void le_receiver_test_cmd(uint16_t index, const void *data, uint8_t size) { const struct bt_hci_cmd_le_receiver_test *cmd = data; print_field("RX frequency: %d MHz (0x%2.2x)", (cmd->frequency * 2) + 2402, cmd->frequency); } static void le_transmitter_test_cmd(uint16_t index, const void *data, uint8_t size) { const struct bt_hci_cmd_le_transmitter_test *cmd = data; print_field("TX frequency: %d MHz (0x%2.2x)", (cmd->frequency * 2) + 2402, cmd->frequency); print_field("Test data length: %d bytes", cmd->data_len); print_field("Packet payload: 0x%2.2x", cmd->payload); } static void le_test_end_rsp(uint16_t index, const void *data, uint8_t size) { const struct bt_hci_rsp_le_test_end *rsp = data; print_status(rsp->status); print_field("Number of packets: %d", le16_to_cpu(rsp->num_packets)); } static void le_conn_param_req_reply_cmd(uint16_t index, const void *data, uint8_t size) { const struct bt_hci_cmd_le_conn_param_req_reply *cmd = data; print_handle(cmd->handle); print_slot_125("Min connection interval", cmd->min_interval); print_slot_125("Max connection interval", cmd->max_interval); print_conn_latency("Connection latency", cmd->latency); print_field("Supervision timeout: %d msec (0x%4.4x)", le16_to_cpu(cmd->supv_timeout) * 10, le16_to_cpu(cmd->supv_timeout)); print_slot_625("Min connection length", cmd->min_length); print_slot_625("Max connection length", cmd->max_length); } static void le_conn_param_req_reply_rsp(uint16_t index, const void *data, uint8_t size) { const struct bt_hci_rsp_le_conn_param_req_reply *rsp = data; print_status(rsp->status); print_handle(rsp->handle); } static void le_conn_param_req_neg_reply_cmd(uint16_t index, const void *data, uint8_t size) { const struct bt_hci_cmd_le_conn_param_req_neg_reply *cmd = data; print_handle(cmd->handle); print_reason(cmd->reason); } static void le_conn_param_req_neg_reply_rsp(uint16_t index, const void *data, uint8_t size) { const struct bt_hci_rsp_le_conn_param_req_neg_reply *rsp = data; print_status(rsp->status); print_handle(rsp->handle); } static void le_set_data_length_cmd(uint16_t index, const void *data, uint8_t size) { const struct bt_hci_cmd_le_set_data_length *cmd = data; print_handle(cmd->handle); print_field("TX octets: %d", le16_to_cpu(cmd->tx_len)); print_field("TX time: %d", le16_to_cpu(cmd->tx_time)); } static void le_set_data_length_rsp(uint16_t index, const void *data, uint8_t size) { const struct bt_hci_rsp_le_set_data_length *rsp = data; print_status(rsp->status); print_handle(rsp->handle); } static void le_read_default_data_length_rsp(uint16_t index, const void *data, uint8_t size) { const struct bt_hci_rsp_le_read_default_data_length *rsp = data; print_status(rsp->status); print_field("TX octets: %d", le16_to_cpu(rsp->tx_len)); print_field("TX time: %d", le16_to_cpu(rsp->tx_time)); } static void le_write_default_data_length_cmd(uint16_t index, const void *data, uint8_t size) { const struct bt_hci_cmd_le_write_default_data_length *cmd = data; print_field("TX octets: %d", le16_to_cpu(cmd->tx_len)); print_field("TX time: %d", le16_to_cpu(cmd->tx_time)); } static void le_generate_dhkey_cmd(uint16_t index, const void *data, uint8_t size) { const struct bt_hci_cmd_le_generate_dhkey *cmd = data; print_pk256("Remote P-256 public key", cmd->remote_pk256); } static void le_add_to_resolv_list_cmd(uint16_t index, const void *data, uint8_t size) { const struct bt_hci_cmd_le_add_to_resolv_list *cmd = data; print_addr_type("Address type", cmd->addr_type); print_addr("Address", cmd->addr, cmd->addr_type); print_key("Peer identity resolving key", cmd->peer_irk); print_key("Local identity resolving key", cmd->local_irk); } static void le_remove_from_resolv_list_cmd(uint16_t index, const void *data, uint8_t size) { const struct bt_hci_cmd_le_remove_from_resolv_list *cmd = data; print_addr_type("Address type", cmd->addr_type); print_addr("Address", cmd->addr, cmd->addr_type); } static void le_read_resolv_list_size_rsp(uint16_t index, const void *data, uint8_t size) { const struct bt_hci_rsp_le_read_resolv_list_size *rsp = data; print_status(rsp->status); print_field("Size: %u", rsp->size); } static void le_read_peer_resolv_addr_cmd(uint16_t index, const void *data, uint8_t size) { const struct bt_hci_cmd_le_read_peer_resolv_addr *cmd = data; print_addr_type("Address type", cmd->addr_type); print_addr("Address", cmd->addr, cmd->addr_type); } static void le_read_peer_resolv_addr_rsp(uint16_t index, const void *data, uint8_t size) { const struct bt_hci_rsp_le_read_peer_resolv_addr *rsp = data; print_status(rsp->status); print_addr("Address", rsp->addr, 0x01); } static void le_read_local_resolv_addr_cmd(uint16_t index, const void *data, uint8_t size) { const struct bt_hci_cmd_le_read_local_resolv_addr *cmd = data; print_addr_type("Address type", cmd->addr_type); print_addr("Address", cmd->addr, cmd->addr_type); } static void le_read_local_resolv_addr_rsp(uint16_t index, const void *data, uint8_t size) { const struct bt_hci_rsp_le_read_local_resolv_addr *rsp = data; print_status(rsp->status); print_addr("Address", rsp->addr, 0x01); } static void le_set_resolv_enable_cmd(uint16_t index, const void *data, uint8_t size) { const struct bt_hci_cmd_le_set_resolv_enable *cmd = data; print_enable("Address resolution", cmd->enable); } static void le_set_resolv_timeout_cmd(uint16_t index, const void *data, uint8_t size) { const struct bt_hci_cmd_le_set_resolv_timeout *cmd = data; print_field("Timeout: %u seconds", le16_to_cpu(cmd->timeout)); } static void le_read_max_data_length_rsp(uint16_t index, const void *data, uint8_t size) { const struct bt_hci_rsp_le_read_max_data_length *rsp = data; print_status(rsp->status); print_field("Max TX octets: %d", le16_to_cpu(rsp->max_tx_len)); print_field("Max TX time: %d", le16_to_cpu(rsp->max_tx_time)); print_field("Max RX octets: %d", le16_to_cpu(rsp->max_rx_len)); print_field("Max RX time: %d", le16_to_cpu(rsp->max_rx_time)); } static void le_read_phy_cmd(uint16_t index, const void *data, uint8_t size) { const struct bt_hci_cmd_le_read_phy *cmd = data; print_handle(cmd->handle); } static void print_le_phy(const char *prefix, uint8_t phy) { const char *str; switch (phy) { case 0x01: str = "LE 1M"; break; case 0x02: str = "LE 2M"; break; case 0x03: str = "LE Coded"; break; default: str = "Reserved"; break; } print_field("%s: %s (0x%2.2x)", prefix, str, phy); } static void le_read_phy_rsp(uint16_t index, const void *data, uint8_t size) { const struct bt_hci_rsp_le_read_phy *rsp = data; print_status(rsp->status); print_handle(rsp->handle); print_le_phy("TX PHY", rsp->tx_phy); print_le_phy("RX PHY", rsp->rx_phy); } static const struct bitfield_data le_phys[] = { { 0, "LE 1M" }, { 1, "LE 2M" }, { 2, "LE Coded"}, { } }; static const struct bitfield_data le_phy_preference[] = { { 0, "No TX PHY preference" }, { 1, "No RX PHY preference" }, { } }; static void print_le_phys_preference(uint8_t all_phys, uint8_t tx_phys, uint8_t rx_phys) { uint8_t mask; print_field("All PHYs preference: 0x%2.2x", all_phys); mask = print_bitfield(2, all_phys, le_phy_preference); if (mask) print_text(COLOR_UNKNOWN_OPTIONS_BIT, " Reserved" " (0x%2.2x)", mask); print_field("TX PHYs preference: 0x%2.2x", tx_phys); mask = print_bitfield(2, tx_phys, le_phys); if (mask) print_text(COLOR_UNKNOWN_OPTIONS_BIT, " Reserved" " (0x%2.2x)", mask); print_field("RX PHYs preference: 0x%2.2x", rx_phys); mask = print_bitfield(2, rx_phys, le_phys); if (mask) print_text(COLOR_UNKNOWN_OPTIONS_BIT, " Reserved" " (0x%2.2x)", mask); } static void le_set_default_phy_cmd(uint16_t index, const void *data, uint8_t size) { const struct bt_hci_cmd_le_set_default_phy *cmd = data; print_le_phys_preference(cmd->all_phys, cmd->tx_phys, cmd->rx_phys); } static void le_set_phy_cmd(uint16_t index, const void *data, uint8_t size) { const struct bt_hci_cmd_le_set_phy *cmd = data; const char *str; print_handle(cmd->handle); print_le_phys_preference(cmd->all_phys, cmd->tx_phys, cmd->rx_phys); switch (le16_to_cpu(cmd->phy_opts)) { case 0x0001: str = "S2 coding"; break; case 0x0002: str = "S8 coding"; break; default: str = "Reserved"; break; } print_field("PHY options preference: %s (0x%4.4x)", str, cmd->phy_opts); } static void le_enhanced_receiver_test_cmd(uint16_t index, const void *data, uint8_t size) { const struct bt_hci_cmd_le_enhanced_receiver_test *cmd = data; const char *str; print_field("RX channel frequency: %d MHz (0x%2.2x)", (cmd->rx_channel * 2) + 2402, cmd->rx_channel); print_le_phy("PHY", cmd->phy); switch (cmd->modulation_index) { case 0x00: str = "Standard"; break; case 0x01: str = "Stable"; break; default: str = "Reserved"; break; } print_field("Modulation index: %s (0x%2.2x)", str, cmd->modulation_index); } static void le_enhanced_transmitter_test_cmd(uint16_t index, const void *data, uint8_t size) { const struct bt_hci_cmd_le_enhanced_transmitter_test *cmd = data; const char *str; print_field("TX channel frequency: %d MHz (0x%2.2x)", (cmd->tx_channel * 2) + 2402, cmd->tx_channel); print_field("Test data length: %d bytes", cmd->data_len); print_field("Packet payload: 0x%2.2x", cmd->payload); switch (cmd->phy) { case 0x01: str = "LE 1M"; break; case 0x02: str = "LE 2M"; break; case 0x03: str = "LE Coded with S=8"; break; case 0x04: str = "LE Coded with S=2"; break; default: str = "Reserved"; break; } print_field("PHY: %s (0x%2.2x)", str, cmd->phy); } static void le_set_adv_set_rand_addr(uint16_t index, const void *data, uint8_t size) { const struct bt_hci_cmd_le_set_adv_set_rand_addr *cmd = data; print_field("Advertising handle: 0x%2.2x", cmd->handle); print_addr("Advertising random address", cmd->bdaddr, 0x01); } static const struct bitfield_data ext_adv_properties_table[] = { { 0, "Connectable" }, { 1, "Scannable" }, { 2, "Directed" }, { 3, "High Duty Cycle Directed Connectable" }, { 4, "Use legacy advertising PDUs" }, { 5, "Anonymous advertising" }, { 6, "Include TxPower" }, { } }; static const char *get_adv_pdu_desc(uint16_t flags) { const char *str; switch (flags) { case 0x10: str = "ADV_NONCONN_IND"; break; case 0x12: str = "ADV_SCAN_IND"; break; case 0x13: str = "ADV_IND"; break; case 0x15: str = "ADV_DIRECT_IND (low duty cycle)"; break; case 0x1d: str = "ADV_DIRECT_IND (high duty cycle)"; break; default: str = "Reserved"; break; } return str; } static void print_ext_adv_properties(uint16_t flags) { uint16_t mask = flags; const char *property; int i; print_field("Properties: 0x%4.4x", flags); for (i = 0; ext_adv_properties_table[i].str; i++) { if (flags & (1 << ext_adv_properties_table[i].bit)) { property = ext_adv_properties_table[i].str; if (ext_adv_properties_table[i].bit == 4) { print_field(" %s: %s", property, get_adv_pdu_desc(flags)); } else { print_field(" %s", property); } mask &= ~(1 << ext_adv_properties_table[i].bit); } } if (mask) print_text(COLOR_UNKNOWN_ADV_FLAG, " Unknown advertising properties (0x%4.4x)", mask); } static void print_ext_slot_625(const char *label, const uint8_t value[3]) { uint32_t value_cpu = value[0]; value_cpu |= value[1] << 8; value_cpu |= value[2] << 16; print_field("%s: %.3f msec (0x%4.4x)", label, value_cpu * 0.625, value_cpu); } static void le_set_ext_adv_params_cmd(uint16_t index, const void *data, uint8_t size) { const struct bt_hci_cmd_le_set_ext_adv_params *cmd = data; const char *str; print_field("Handle: 0x%2.2x", cmd->handle); print_ext_adv_properties(le16_to_cpu(cmd->evt_properties)); print_ext_slot_625("Min advertising interval", cmd->min_interval); print_ext_slot_625("Max advertising interval", cmd->max_interval); print_adv_channel_map("Channel map", cmd->channel_map); print_own_addr_type(cmd->own_addr_type); print_peer_addr_type("Peer address type", cmd->peer_addr_type); print_addr("Peer address", cmd->peer_addr, cmd->peer_addr_type); print_adv_filter_policy("Filter policy", cmd->filter_policy); if (cmd->tx_power == 0x7f) print_field("TX power: Host has no preference (0x7f)"); else print_power_level(cmd->tx_power, NULL); switch (cmd->primary_phy) { case 0x01: str = "LE 1M"; break; case 0x03: str = "LE Coded"; break; default: str = "Reserved"; break; } print_field("Primary PHY: %s (0x%2.2x)", str, cmd->primary_phy); print_field("Secondary max skip: 0x%2.2x", cmd->secondary_max_skip); print_le_phy("Secondary PHY", cmd->secondary_phy); print_field("SID: 0x%2.2x", cmd->sid); print_enable("Scan request notifications", cmd->notif_enable); } static void le_set_ext_adv_params_rsp(uint16_t index, const void *data, uint8_t size) { const struct bt_hci_rsp_le_set_ext_adv_params *rsp = data; print_status(rsp->status); print_power_level(rsp->tx_power, "selected"); } static void le_set_ext_adv_data_cmd(uint16_t index, const void *data, uint8_t size) { const struct bt_hci_cmd_le_set_ext_adv_data *cmd = data; const char *str; print_field("Handle: 0x%2.2x", cmd->handle); switch (cmd->operation) { case 0x00: str = "Immediate fragment"; break; case 0x01: str = "First fragment"; break; case 0x02: str = "Last fragment"; break; case 0x03: str = "Complete extended advertising data"; break; case 0x04: str = "Unchanged data"; break; default: str = "Reserved"; break; } print_field("Operation: %s (0x%2.2x)", str, cmd->operation); switch (cmd->fragment_preference) { case 0x00: str = "Fragment all"; break; case 0x01: str = "Minimize fragmentation"; break; default: str = "Reserved"; break; } print_field("Fragment preference: %s (0x%2.2x)", str, cmd->fragment_preference); print_field("Data length: 0x%2.2x", cmd->data_len); packet_print_ad(cmd->data, size - sizeof(*cmd)); } static void le_set_ext_scan_rsp_data_cmd(uint16_t index, const void *data, uint8_t size) { const struct bt_hci_cmd_le_set_ext_scan_rsp_data *cmd = data; const char *str; print_field("Handle: 0x%2.2x", cmd->handle); switch (cmd->operation) { case 0x00: str = "Immediate fragment"; break; case 0x01: str = "First fragment"; break; case 0x02: str = "Last fragment"; break; case 0x03: str = "Complete scan response data"; break; case 0x04: str = "Unchanged data"; break; default: str = "Reserved"; break; } print_field("Operation: %s (0x%2.2x)", str, cmd->operation); switch (cmd->fragment_preference) { case 0x00: str = "Fragment all"; break; case 0x01: str = "Minimize fragmentation"; break; default: str = "Reserved"; break; } print_field("Fragment preference: %s (0x%2.2x)", str, cmd->fragment_preference); print_field("Data length: 0x%2.2x", cmd->data_len); packet_print_ad(cmd->data, size - sizeof(*cmd)); } static void le_set_ext_adv_enable_cmd(uint16_t index, const void *data, uint8_t size) { const struct bt_hci_cmd_le_set_ext_adv_enable *cmd = data; const struct bt_hci_cmd_ext_adv_set *adv_set; int i; print_enable("Extended advertising", cmd->enable); if (cmd->num_of_sets == 0) print_field("Number of sets: Disable all sets (0x%2.2x)", cmd->num_of_sets); else if (cmd->num_of_sets > 0x3f) print_field("Number of sets: Reserved (0x%2.2x)", cmd->num_of_sets); else print_field("Number of sets: %u (0x%2.2x)", cmd->num_of_sets, cmd->num_of_sets); for (i = 0; i < cmd->num_of_sets; ++i) { adv_set = data + 2 + i * sizeof(struct bt_hci_cmd_ext_adv_set); print_field("Entry %d", i); print_field(" Handle: 0x%2.2x", adv_set->handle); print_field(" Duration: %d ms (0x%2.2x)", adv_set->duration * 10, adv_set->duration); print_field(" Max ext adv events: %d", adv_set->max_events); } } static void le_read_max_adv_data_len_rsp(uint16_t index, const void *data, uint8_t size) { const struct bt_hci_rsp_le_read_max_adv_data_len *rsp = data; print_status(rsp->status); print_field("Max length: %d", rsp->max_len); } static void le_read_num_supported_adv_sets_rsp(uint16_t index, const void *data, uint8_t size) { const struct bt_hci_rsp_le_read_num_supported_adv_sets *rsp = data; print_status(rsp->status); print_field("Num supported adv sets: %d", rsp->num_of_sets); } static void le_remove_adv_set_cmd(uint16_t index, const void *data, uint8_t size) { const struct bt_hci_cmd_le_remove_adv_set *cmd = data; print_handle(cmd->handle); } static const struct bitfield_data pa_properties_table[] = { { 6, "Include TxPower" }, { } }; static void print_pa_properties(uint16_t flags) { uint16_t mask; print_field("Properties: 0x%4.4x", flags); mask = print_bitfield(2, flags, pa_properties_table); if (mask) print_text(COLOR_UNKNOWN_ADV_FLAG, " Unknown advertising properties (0x%4.4x)", mask); } static void le_set_pa_params_cmd(uint16_t index, const void *data, uint8_t size) { const struct bt_hci_cmd_le_set_pa_params *cmd = data; print_handle(cmd->handle); print_slot_125("Min interval", cmd->min_interval); print_slot_125("Max interval", cmd->max_interval); print_pa_properties(cmd->properties); } static void le_set_pa_data_cmd(uint16_t index, const void *data, uint8_t size) { const struct bt_hci_cmd_le_set_pa_data *cmd = data; const char *str; print_handle(cmd->handle); switch (cmd->operation) { case 0x00: str = "Immediate fragment"; break; case 0x01: str = "First fragment"; break; case 0x02: str = "Last fragment"; break; case 0x03: str = "Complete ext advertising data"; break; default: str = "Reserved"; break; } print_field("Operation: %s (0x%2.2x)", str, cmd->operation); print_field("Data length: 0x%2.2x", cmd->data_len); print_eir(cmd->data, cmd->data_len, true); } static void le_set_pa_enable_cmd(uint16_t index, const void *data, uint8_t size) { const struct bt_hci_cmd_le_set_pa_enable *cmd = data; print_enable("Periodic advertising", cmd->enable); print_handle(cmd->handle); } static const struct bitfield_data ext_scan_phys_table[] = { { 0, "LE 1M" }, { 2, "LE Coded" }, { } }; static void print_ext_scan_phys(const void *data, uint8_t flags) { const struct bt_hci_le_scan_phy *scan_phy; uint8_t mask = flags; int bits_set = 0; int i; print_field("PHYs: 0x%2.2x", flags); for (i = 0; ext_scan_phys_table[i].str; i++) { if (flags & (1 << ext_scan_phys_table[i].bit)) { scan_phy = data + bits_set * sizeof(*scan_phy); mask &= ~(1 << ext_scan_phys_table[i].bit); print_field("Entry %d: %s", bits_set, ext_scan_phys_table[i].str); print_scan_type(" Type", scan_phy->type); print_slot_625(" Interval", scan_phy->interval); print_slot_625(" Window", scan_phy->window); ++bits_set; } } if (mask) print_text(COLOR_UNKNOWN_ADV_FLAG, " Unknown scanning PHYs" " (0x%2.2x)", mask); } static void le_set_ext_scan_params_cmd(uint16_t index, const void *data, uint8_t size) { const struct bt_hci_cmd_le_set_ext_scan_params *cmd = data; print_own_addr_type(cmd->own_addr_type); print_scan_filter_policy(cmd->filter_policy); print_ext_scan_phys(cmd->data, cmd->num_phys); } static void le_set_ext_scan_enable_cmd(uint16_t index, const void *data, uint8_t size) { const struct bt_hci_cmd_le_set_ext_scan_enable *cmd = data; print_enable("Extended scan", cmd->enable); print_enable("Filter duplicates", cmd->filter_dup); print_field("Duration: %d msec (0x%4.4x)", le16_to_cpu(cmd->duration) * 10, le16_to_cpu(cmd->duration)); print_field("Period: %.2f sec (0x%4.4x)", le16_to_cpu(cmd->period) * 1.28, le16_to_cpu(cmd->period)); } static const struct bitfield_data ext_conn_phys_table[] = { { 0, "LE 1M" }, { 1, "LE 2M" }, { 2, "LE Coded" }, { } }; static void print_ext_conn_phys(const void *data, uint8_t flags) { const struct bt_hci_le_ext_create_conn *entry; uint8_t mask = flags; int bits_set = 0; int i; print_field("Initiating PHYs: 0x%2.2x", flags); for (i = 0; ext_conn_phys_table[i].str; i++) { if (flags & (1 << ext_conn_phys_table[i].bit)) { entry = data + bits_set * sizeof(*entry); mask &= ~(1 << ext_conn_phys_table[i].bit); print_field("Entry %d: %s", bits_set, ext_conn_phys_table[i].str); print_slot_625(" Scan interval", entry->scan_interval); print_slot_625(" Scan window", entry->scan_window); print_slot_125(" Min connection interval", entry->min_interval); print_slot_125(" Max connection interval", entry->max_interval); print_conn_latency(" Connection latency", entry->latency); print_field(" Supervision timeout: %d msec (0x%4.4x)", le16_to_cpu(entry->supv_timeout) * 10, le16_to_cpu(entry->supv_timeout)); print_slot_625(" Min connection length", entry->min_length); print_slot_625(" Max connection length", entry->max_length); ++bits_set; } } if (mask) print_text(COLOR_UNKNOWN_ADV_FLAG, " Unknown scanning PHYs" " (0x%2.2x)", mask); } static void le_ext_create_conn_cmd(uint16_t index, const void *data, uint8_t size) { const struct bt_hci_cmd_le_ext_create_conn *cmd = data; const char *str; switch (cmd->filter_policy) { case 0x00: str = "Accept list is not used"; break; case 0x01: str = "Accept list is used"; break; default: str = "Reserved"; break; } print_field("Filter policy: %s (0x%2.2x)", str, cmd->filter_policy); print_own_addr_type(cmd->own_addr_type); print_peer_addr_type("Peer address type", cmd->peer_addr_type); print_addr("Peer address", cmd->peer_addr, cmd->peer_addr_type); print_ext_conn_phys(cmd->data, cmd->phys); } static const struct bitfield_data create_sync_cte_type[] = { { 0, "Do not sync to packets with AoA CTE" }, { 1, "Do not sync to packets with AoD CTE 1us" }, { 2, "Do not sync to packets with AoD CTE 2us" }, { 3, "Do not sync to packets with type 3 AoD" }, { 4, "Do not sync to packets without CTE" }, { }, }; static const struct bitfield_data create_sync_options[] = { { 0, "Use Periodic Advertiser List" }, { 1, "Reporting initially disabled" }, { }, }; static const struct bitfield_data create_sync_options_alt[] = { { 0, "Use advertising SID, Advertiser Address Type and address"}, { 1, "Reporting initially enabled" }, { }, }; static void print_create_sync_cte_type(uint8_t flags) { uint8_t mask = flags; print_field("Sync CTE type: 0x%4.4x", flags); mask = print_bitfield(2, flags, create_sync_cte_type); if (mask) { print_text(COLOR_UNKNOWN_ADV_FLAG, "Unknown sync CTE type properties (0x%4.4x)", mask); } } static void print_create_sync_options(uint8_t flags) { uint8_t mask = flags; int i; print_field("Options: 0x%4.4x", flags); for (i = 0; create_sync_options[i].str; i++) { if (flags & (1 << create_sync_options[i].bit)) { print_field("%s", create_sync_options[i].str); mask &= ~(1 << create_sync_options[i].bit); } else { print_field("%s", create_sync_options_alt[i].str); mask &= ~(1 << create_sync_options_alt[i].bit); } } if (mask) { print_text(COLOR_UNKNOWN_ADV_FLAG, " Unknown options (0x%4.4x)", mask); } } static void le_pa_create_sync_cmd(uint16_t index, const void *data, uint8_t size) { const struct bt_hci_cmd_le_pa_create_sync *cmd = data; print_create_sync_options(cmd->options); print_field("SID: 0x%2.2x", cmd->sid); print_addr_type("Adv address type", cmd->addr_type); print_addr("Adv address", cmd->addr, cmd->addr_type); print_field("Skip: 0x%4.4x", cmd->skip); print_field("Sync timeout: %d msec (0x%4.4x)", le16_to_cpu(cmd->sync_timeout) * 10, le16_to_cpu(cmd->sync_timeout)); print_create_sync_cte_type(cmd->sync_cte_type); } static void le_pa_term_sync_cmd(uint16_t index, const void *data, uint8_t size) { const struct bt_hci_cmd_le_pa_term_sync *cmd = data; print_field("Sync handle: 0x%4.4x", cmd->sync_handle); } static void le_add_dev_pa_list_cmd(uint16_t index, const void *data, uint8_t size) { const struct bt_hci_cmd_le_add_dev_pa_list *cmd = data; print_addr_type("Adv address type", cmd->addr_type); print_addr("Adv address", cmd->addr, cmd->addr_type); print_field("SID: 0x%2.2x", cmd->sid); } static void le_remove_dev_pa_list_cmd(uint16_t index, const void *data, uint8_t size) { const struct bt_hci_cmd_le_remove_dev_pa_list *cmd = data; print_addr_type("Adv address type", cmd->addr_type); print_addr("Adv address", cmd->addr, cmd->addr_type); print_field("SID: 0x%2.2x", cmd->sid); } static void le_read_pa_list_size_rsp(uint16_t index, const void *data, uint8_t size) { const struct bt_hci_rsp_le_read_dev_pa_list_size *rsp = data; print_status(rsp->status); print_field("List size: 0x%2.2x", rsp->list_size); } static void le_read_tx_power_rsp(uint16_t index, const void *data, uint8_t size) { const struct bt_hci_rsp_le_read_tx_power *rsp = data; print_status(rsp->status); print_field("Min Tx power: %d dBm", rsp->min_tx_power); print_field("Max Tx power: %d dBm", rsp->max_tx_power); } static void le_read_rf_path_comp_rsp(uint16_t index, const void *data, uint8_t size) { const struct bt_hci_rsp_le_read_rf_path_comp *rsp = data; print_status(rsp->status); print_field("RF Tx Path Compensation Value: 0x%4.4x", rsp->rf_tx_path_comp); print_field("RF Rx Path Compensation Value: 0x%4.4x", rsp->rf_rx_path_comp); } static void le_write_rf_path_comp_cmd(uint16_t index, const void *data, uint8_t size) { const struct bt_hci_cmd_le_write_rf_path_comp *cmd = data; print_field("RF Tx Path Compensation Value: 0x%4.4x", cmd->rf_tx_path_comp); print_field("RF Rx Path Compensation Value: 0x%4.4x", cmd->rf_rx_path_comp); } static void le_set_priv_mode_cmd(uint16_t index, const void *data, uint8_t size) { const struct bt_hci_cmd_le_set_priv_mode *cmd = data; const char *str; print_addr_type("Peer Identity address type", cmd->peer_id_addr_type); print_addr("Peer Identity address", cmd->peer_id_addr, cmd->peer_id_addr_type); switch (cmd->priv_mode) { case 0x00: str = "Use Network Privacy"; break; case 0x01: str = "Use Device Privacy"; break; default: str = "Reserved"; break; } print_field("Privacy Mode: %s (0x%2.2x)", str, cmd->priv_mode); } static void le_receiver_test_cmd_v3(uint16_t index, const void *data, uint8_t size) { const struct bt_hci_cmd_le_receiver_test_v3 *cmd = data; uint8_t i; print_field("RX Channel: %u MHz (0x%2.2x)", cmd->rx_chan * 2 + 2402, cmd->rx_chan); switch (cmd->phy) { case 0x01: print_field("PHY: LE 1M (0x%2.2x)", cmd->phy); break; case 0x02: print_field("PHY: LE 2M (0x%2.2x)", cmd->phy); break; case 0x03: print_field("PHY: LE Coded (0x%2.2x)", cmd->phy); break; } print_field("Modulation Index: %s (0x%2.2x)", cmd->mod_index ? "stable" : "standard", cmd->mod_index); print_field("Expected CTE Length: %u us (0x%2.2x)", cmd->cte_len * 8, cmd->cte_len); print_field("Expected CTE Type: %u us slots (0x%2.2x)", cmd->cte_type, cmd->cte_type); print_field("Slot Duration: %u us (0x%2.2x)", cmd->duration, cmd->duration); print_field("Number of Antenna IDs: %u", cmd->num_antenna_id); if (size < sizeof(*cmd) + cmd->num_antenna_id) return; for (i = 0; i < cmd->num_antenna_id; i++) print_field(" Antenna ID: %u", cmd->antenna_ids[i]); } static const char *parse_tx_test_payload(uint8_t payload) { switch (payload) { case 0x00: return "PRBS9 sequence 11111111100000111101..."; case 0x01: return "Repeated 11110000"; case 0x02: return "Repeated 10101010"; case 0x03: return "PRBS15"; case 0x04: return "Repeated 11111111"; case 0x05: return "Repeated 00000000"; case 0x06: return "Repeated 00001111"; case 0x07: return "Repeated 01010101"; default: return "Reserved"; } } static void le_tx_test_cmd_v3(uint16_t index, const void *data, uint8_t size) { const struct bt_hci_cmd_le_tx_test_v3 *cmd = data; uint8_t i; print_field("TX Channel: %u MHz (0x%2.2x)", cmd->chan * 2 + 2402, cmd->chan); print_field("Length of Test Data: %u", cmd->data_len); print_field("Packet Payload: %s (0x%2.2x)", parse_tx_test_payload(cmd->payload), cmd->payload); switch (cmd->phy) { case 0x01: print_field("PHY: LE 1M (0x%2.2x)", cmd->phy); break; case 0x02: print_field("PHY: LE 2M (0x%2.2x)", cmd->phy); break; case 0x03: print_field("PHY: LE Coded with S=8 (0x%2.2x)", cmd->phy); break; case 0x04: print_field("PHY: LE Coded with S=2 (0x%2.2x)", cmd->phy); break; } print_field("Expected CTE Length: %u us (0x%2.2x)", cmd->cte_len * 8, cmd->cte_len); print_field("Expected CTE Type: %u us slots (0x%2.2x)", cmd->cte_type, cmd->cte_type); print_field("Slot Duration: %u us (0x%2.2x)", cmd->duration, cmd->duration); print_field("Number of Antenna IDs: %u", cmd->num_antenna_id); if (size < sizeof(*cmd) + cmd->num_antenna_id) return; for (i = 0; i < cmd->num_antenna_id; i++) print_field(" Antenna ID: %u", cmd->antenna_ids[i]); } static void le_pa_rec_enable(uint16_t index, const void *data, uint8_t size) { const struct bt_hci_cmd_le_set_pa_enable *cmd = data; print_field("Sync handle: %d", cmd->handle); print_enable("Reporting", cmd->enable); } static void le_pa_sync_trans(uint16_t index, const void *data, uint8_t size) { const struct bt_hci_cmd_periodic_sync_trans *cmd = data; print_field("Connection handle: %d", cmd->handle); print_field("Service data: 0x%4.4x", cmd->service_data); print_field("Sync handle: %d", cmd->sync_handle); } static void le_pa_set_info_trans(uint16_t index, const void *data, uint8_t size) { const struct bt_hci_cmd_pa_set_info_trans *cmd = data; print_field("Connection handle: %d", cmd->handle); print_field("Service data: 0x%4.4x", cmd->service_data); print_field("Advertising handle: %d", cmd->adv_handle); } static void print_sync_mode(uint8_t mode) { const char *str; switch (mode) { case 0x00: str = "Disabled"; break; case 0x01: str = "Enabled with report events disabled"; break; case 0x02: str = "Enabled with report events enabled"; break; default: str = "RFU"; break; } print_field("Mode: %s (0x%2.2x)", str, mode); } static void le_pa_sync_trans_params(uint16_t index, const void *data, uint8_t size) { const struct bt_hci_cmd_pa_sync_trans_params *cmd = data; print_field("Connection handle: %d", cmd->handle); print_sync_mode(cmd->mode); print_field("Skip: 0x%2.2x", cmd->skip); print_field("Sync timeout: %d msec (0x%4.4x)", le16_to_cpu(cmd->sync_timeout) * 10, le16_to_cpu(cmd->sync_timeout)); print_create_sync_cte_type(cmd->cte_type); } static void le_set_default_pa_sync_trans_params(uint16_t index, const void *data, uint8_t size) { const struct bt_hci_cmd_default_pa_sync_trans_params *cmd = data; print_sync_mode(cmd->mode); print_field("Skip: 0x%2.2x", cmd->skip); print_field("Sync timeout: %d msec (0x%4.4x)", le16_to_cpu(cmd->sync_timeout) * 10, le16_to_cpu(cmd->sync_timeout)); print_create_sync_cte_type(cmd->cte_type); } static void print_sca(uint8_t sca) { switch (sca) { case 0x00: print_field("SCA: 201 - 500 ppm (0x%2.2x)", sca); return; case 0x01: print_field("SCA: 151 - 200 ppm (0x%2.2x)", sca); return; case 0x02: print_field("SCA: 101 - 150 ppm (0x%2.2x)", sca); return; case 0x03: print_field("SCA: 76 - 100 ppm (0x%2.2x)", sca); return; case 0x04: print_field("SCA: 51 - 75 ppm (0x%2.2x)", sca); return; case 0x05: print_field("SCA: 31 - 50 ppm (0x%2.2x)", sca); return; case 0x06: print_field("SCA: 21 - 30 ppm (0x%2.2x)", sca); return; case 0x07: print_field("SCA: 0 - 20 ppm (0x%2.2x)", sca); return; default: print_field("SCA: Reserved (0x%2.2x)", sca); } } static void print_packing(uint8_t value) { switch (value) { case 0x00: print_field("Packing: Sequential (0x%2.2x)", value); return; case 0x01: print_field("Packing: Interleaved (0x%2.2x)", value); return; default: print_field("Packing: Reserved (0x%2.2x)", value); } } static void print_framing(uint8_t value) { switch (value) { case 0x00: print_field("Framing: Unframed (0x%2.2x)", value); return; case 0x01: print_field("Framing: Framed (0x%2.2x)", value); return; default: print_field("Packing: Reserved (0x%2.2x)", value); } } static void le_read_buffer_size_v2_rsp(uint16_t index, const void *data, uint8_t size) { const struct bt_hci_rsp_le_read_buffer_size_v2 *rsp = data; print_status(rsp->status); if (size == 1) return; print_field("ACL MTU: %d", le16_to_cpu(rsp->acl_mtu)); print_field("ACL max packet: %d", rsp->acl_max_pkt); print_field("ISO MTU: %d", le16_to_cpu(rsp->iso_mtu)); print_field("ISO max packet: %d", rsp->iso_max_pkt); } static void le_read_iso_tx_sync_cmd(uint16_t index, const void *data, uint8_t size) { const struct bt_hci_cmd_le_read_iso_tx_sync *cmd = data; print_field("Handle: %d", le16_to_cpu(cmd->handle)); } static void le_read_iso_tx_sync_rsp(uint16_t index, const void *data, uint8_t size) { const struct bt_hci_rsp_le_read_iso_tx_sync *rsp = data; uint32_t offset = 0; print_status(rsp->status); if (size == 1) return; print_field("Handle: %d", le16_to_cpu(rsp->handle)); print_field("Sequence Number: %d", le16_to_cpu(rsp->seq)); print_field("Timestamp: %d", le32_to_cpu(rsp->timestamp)); memcpy(&offset, rsp->offset, sizeof(rsp->offset)); print_field("Offset: %d", le32_to_cpu(offset)); } static void print_cis_params(const void *data, int i) { const struct bt_hci_cis_params *cis = data; print_field("CIS ID: 0x%2.2x", cis->cis_id); print_field("Central to Peripheral Maximum SDU Size: %u", le16_to_cpu(cis->c_sdu)); print_field("Peripheral to Central Maximum SDU Size: %u", le16_to_cpu(cis->p_sdu)); print_le_phy("Central to Peripheral PHY", cis->c_phy); print_le_phy("Peripheral to Central PHY", cis->p_phy); print_field("Central to Peripheral Retransmission attempts: 0x%2.2x", cis->c_rtn); print_field("Peripheral to Central Retransmission attempts: 0x%2.2x", cis->p_rtn); } static void le_set_cig_params_cmd(uint16_t index, const void *data, uint8_t size) { const struct bt_hci_cmd_le_set_cig_params *cmd = data; print_field("CIG ID: 0x%2.2x", cmd->cig_id); print_usec_interval("Central to Peripheral SDU Interval", cmd->c_interval); print_usec_interval("Peripheral to Central SDU Interval", cmd->p_interval); print_sca(cmd->sca); print_packing(cmd->packing); print_framing(cmd->framing); print_field("Central to Peripheral Maximum Latency: %d ms (0x%4.4x)", le16_to_cpu(cmd->c_latency), le16_to_cpu(cmd->c_latency)); print_field("Peripheral to Central Maximum Latency: %d ms (0x%4.4x)", le16_to_cpu(cmd->p_latency), le16_to_cpu(cmd->p_latency)); print_field("Number of CIS: %u", cmd->num_cis); size -= sizeof(*cmd); print_list(cmd->cis, size, cmd->num_cis, sizeof(*cmd->cis), print_cis_params); } static void print_cis_params_test(const void *data, int i) { const struct bt_hci_cis_params_test *cis = data; print_field("CIS ID: 0x%2.2x", cis->cis_id); print_field("NSE: 0x%2.2x", cis->nse); print_field("Central to Peripheral Maximum SDU: 0x%4.4x", le16_to_cpu(cis->c_sdu)); print_field("Peripheral to Central Maximum SDU: 0x%4.4x", le16_to_cpu(cis->p_sdu)); print_field("Central to Peripheral Maximum PDU: 0x%4.4x", le16_to_cpu(cis->c_pdu)); print_field("Peripheral to Central Maximum PDU: 0x%4.4x", le16_to_cpu(cis->p_pdu)); print_le_phy("Central to Peripheral PHY", cis->c_phy); print_le_phy("Peripheral to Central PHY", cis->p_phy); print_field("Central to Peripheral Burst Number: 0x%2.2x", cis->c_bn); print_field("Peripheral to Central Burst Number: 0x%2.2x", cis->p_bn); } static void le_set_cig_params_test_cmd(uint16_t index, const void *data, uint8_t size) { const struct bt_hci_cmd_le_set_cig_params_test *cmd = data; print_field("CIG ID: 0x%2.2x", cmd->cig_id); print_usec_interval("Central to Peripheral SDU Interval", cmd->c_interval); print_usec_interval("Peripheral to Central SDU Interval", cmd->p_interval); print_field("Central to Peripheral Flush Timeout: 0x%2.2x", cmd->c_ft); print_field("Peripheral to Central Flush Timeout: 0x%2.2x", cmd->p_ft); print_field("ISO Interval: %.2f ms (0x%4.4x)", le16_to_cpu(cmd->iso_interval) * 1.25, le16_to_cpu(cmd->iso_interval)); print_sca(cmd->sca); print_packing(cmd->packing); print_framing(cmd->framing); print_field("Number of CIS: %u", cmd->num_cis); size -= sizeof(*cmd); print_list(cmd->cis, size, cmd->num_cis, sizeof(*cmd->cis), print_cis_params_test); } static void print_cig_handle(const void *data, int i) { uint16_t handle = get_le16(data); print_field("Connection Handle #%d: %d", i, handle); } static void le_set_cig_params_rsp(uint16_t index, const void *data, uint8_t size) { const struct bt_hci_rsp_le_set_cig_params *rsp = data; print_status(rsp->status); if (size == 1) return; print_field("CIG ID: 0x%2.2x", rsp->cig_id); print_field("Number of Handles: %u", rsp->num_handles); size -= sizeof(*rsp); print_list(rsp->handle, size, rsp->num_handles, sizeof(*rsp->handle), print_cig_handle); } static void print_cis(const void *data, int i) { const struct bt_hci_cis *cis = data; struct packet_conn_data *conn; print_field("CIS Handle: %d", cis->cis_handle); print_field("ACL Handle: %d", cis->acl_handle); conn = packet_get_conn_data(cis->acl_handle); if (conn) conn->link = cis->cis_handle; } static void le_create_cis_cmd(uint16_t index, const void *data, uint8_t size) { const struct bt_hci_cmd_le_create_cis *cmd = data; print_field("Number of CIS: %u", cmd->num_cis); size -= sizeof(*cmd); print_list(cmd->cis, size, cmd->num_cis, sizeof(*cmd->cis), print_cis); } static void le_remove_cig_cmd(uint16_t index, const void *data, uint8_t size) { const struct bt_hci_cmd_le_remove_cig *cmd = data; print_field("CIG ID: 0x%02x", cmd->cig_id); } static void le_remove_cig_rsp(uint16_t index, const void *data, uint8_t size) { const struct bt_hci_rsp_le_remove_cig *rsp = data; print_status(rsp->status); if (size == 1) return; print_field("CIG ID: 0x%2.2x", rsp->cig_id); } static void le_accept_cis_req_cmd(uint16_t index, const void *data, uint8_t size) { const struct bt_hci_cmd_le_accept_cis *cmd = data; print_field("CIS Handle: %d", le16_to_cpu(cmd->handle)); } static void le_reject_cis_req_cmd(uint16_t index, const void *data, uint8_t size) { const struct bt_hci_cmd_le_reject_cis *cmd = data; print_field("CIS Handle: %d", le16_to_cpu(cmd->handle)); print_reason(cmd->reason); } static void print_bis(const struct bt_hci_bis *bis) { print_usec_interval("SDU Interval", bis->sdu_interval); print_field("Maximum SDU size: %u", le16_to_cpu(bis->sdu)); print_field("Maximum Latency: %u ms (0x%4.4x)", le16_to_cpu(bis->latency), le16_to_cpu(bis->latency)); print_field("RTN: 0x%2.2x", bis->rtn); print_le_phy("PHY", bis->phy); print_packing(bis->packing); print_framing(bis->framing); print_field("Encryption: 0x%2.2x", bis->encryption); print_hex_field("Broadcast Code", bis->bcode, 16); } static void le_create_big_cmd(uint16_t index, const void *data, uint8_t size) { const struct bt_hci_cmd_le_create_big *cmd = data; print_field("Handle: 0x%2.2x", cmd->handle); print_field("Advertising Handle: 0x%2.2x", cmd->adv_handle); print_field("Number of BIS: %u", cmd->num_bis); print_bis(&cmd->bis); } static void print_bis_test(const void *data, int i) { const struct bt_hci_bis_test *bis = data; print_usec_interval("SDU Interval", bis->sdu_interval); print_field("ISO Interval: %.2f ms (0x%4.4x)", le16_to_cpu(bis->iso_interval) * 1.25, le16_to_cpu(bis->iso_interval)); print_field("Number of Subevents: %u", bis->nse); print_field("Maximum SDU: %u", bis->sdu); print_field("Maximum PDU: %u", bis->pdu); print_packing(bis->packing); print_framing(bis->framing); print_le_phy("PHY", bis->phy); print_field("Burst Number: %u", bis->bn); print_field("Immediate Repetition Count: %u", bis->irc); print_field("Pre Transmission Offset: 0x%2.2x", bis->pto); print_field("Encryption: 0x%2.2x", bis->encryption); print_hex_field("Broadcast Code", bis->bcode, 16); } static void le_create_big_cmd_test_cmd(uint16_t index, const void *data, uint8_t size) { const struct bt_hci_cmd_le_create_big_test *cmd = data; print_field("BIG Handle: 0x%2.2x", cmd->big_handle); print_field("Advertising Handle: 0x%2.2x", cmd->adv_handle); print_field("Number of BIS: %u", cmd->num_bis); size -= sizeof(*cmd); print_list(cmd->bis, size, cmd->num_bis, sizeof(*cmd->bis), print_bis_test); } static void le_terminate_big_cmd(uint16_t index, const void *data, uint8_t size) { const struct bt_hci_cmd_le_term_big *cmd = data; print_field("BIG Handle: 0x%2.2x", cmd->handle); print_reason(cmd->reason); } static void print_bis_sync(const void *data, int i) { const uint8_t *bis_id = data; print_field("BIS ID: 0x%2.2x", *bis_id); } static void le_big_create_sync_cmd(uint16_t index, const void *data, uint8_t size) { const struct bt_hci_cmd_le_big_create_sync *cmd = data; print_field("BIG Handle: 0x%2.2x", cmd->handle); print_field("BIG Sync Handle: 0x%4.4x", le16_to_cpu(cmd->sync_handle)); print_field("Encryption: %s (0x%2.2x)", cmd->encryption ? "Encrypted" : "Unencrypted", cmd->encryption); print_hex_field("Broadcast Code", cmd->bcode, 16); print_field("Maximum Number Subevents: 0x%2.2x", cmd->mse); print_field("Timeout: %d ms (0x%4.4x)", le16_to_cpu(cmd->timeout) * 10, le16_to_cpu(cmd->timeout)); print_field("Number of BIS: %u", cmd->num_bis); size -= sizeof(*cmd); print_list(cmd->bis, size, cmd->num_bis, sizeof(*cmd->bis), print_bis_sync); } static void le_big_term_sync_cmd(uint16_t index, const void *data, uint8_t size) { const struct bt_hci_cmd_le_big_term_sync *cmd = data; print_field("BIG Handle: 0x%2.2x", cmd->handle); } static void print_iso_path(const char *prefix, uint8_t path) { switch (path) { case 0x00: print_field("%s: HCI (0x%2.2x)", prefix, path); return; case 0xff: print_field("%s: Disabled (0x%2.2x)", prefix, path); return; default: print_field("%s: Logical Channel Number %u", prefix, path); } } static void le_setup_iso_path_cmd(uint16_t index, const void *data, uint8_t size) { const struct bt_hci_cmd_le_setup_iso_path *cmd = data; print_field("Handle: %d", le16_to_cpu(cmd->handle)); print_path_direction("Data Path Direction", cmd->direction); print_iso_path("Data Path", cmd->path); print_codec_id("Coding Format", cmd->codec); packet_print_company("Company Codec ID", le16_to_cpu(cmd->codec_cid)); print_field("Vendor Codec ID: %d", le16_to_cpu(cmd->codec_vid)); print_usec_interval("Controller Delay", cmd->delay); print_field("Codec Configuration Length: %d", cmd->codec_cfg_len); print_hex_field("Codec Configuration", cmd->codec_cfg, cmd->codec_cfg_len); } static void le_setup_iso_path_rsp(uint16_t index, const void *data, uint8_t size) { const struct bt_hci_rsp_le_setup_iso_path *rsp = data; print_status(rsp->status); if (size == 1) return; print_field("Handle: %d", le16_to_cpu(rsp->handle)); } static void le_remove_iso_path_cmd(uint16_t index, const void *data, uint8_t size) { const struct bt_hci_cmd_le_remove_iso_path *cmd = data; print_field("Connection Handle: %d", le16_to_cpu(cmd->handle)); print_path_direction("Data Path Direction", cmd->direction); } static void le_req_peer_sca_cmd(uint16_t index, const void *data, uint8_t size) { const struct bt_hci_cmd_le_req_peer_sca *cmd = data; print_field("Connection Handle: %d", le16_to_cpu(cmd->handle)); } static void le_set_host_feature_cmd(uint16_t index, const void *data, uint8_t size) { const struct bt_hci_cmd_le_set_host_feature *cmd = data; uint64_t mask; print_field("Bit Number: %u", cmd->bit_number); mask = print_bitfield(2, (((uint64_t) 1) << cmd->bit_number), features_le); if (mask) print_text(COLOR_UNKNOWN_FEATURE_BIT, " Unknown features " "(0x%16.16" PRIx64 ")", mask); print_field("Bit Value: %u", cmd->bit_value); } static void le_read_iso_link_quality_cmd(uint16_t index, const void *data, uint8_t size) { const struct bt_hci_cmd_le_read_iso_link_quality *cmd = data; print_field("Handle: %d", le16_to_cpu(cmd->handle)); } static void status_le_read_iso_link_quality_rsp(uint16_t index, const void *data, uint8_t size) { const struct bt_hci_rsp_le_read_iso_link_quality *rsp = data; print_status(rsp->status); if (size == 1) return; print_field("Handle: %d", le16_to_cpu(rsp->handle)); print_field("TX unacked packets %d", rsp->tx_unacked_packets); print_field("TX flushed packets %d", rsp->tx_flushed_packets); print_field("TX last subevent packets %d", rsp->tx_last_subevent_packets); print_field("TX retransmitted packets %d", rsp->retransmitted_packets); print_field("TX crc error packets %d", rsp->crc_error_packets); print_field("RX unreceived packets %d", rsp->rx_unreceived_packets); print_field("Duplicated packets %d", rsp->duplicated_packets); } struct opcode_data { uint16_t opcode; int bit; const char *str; void (*cmd_func) (uint16_t index, const void *data, uint8_t size); uint8_t cmd_size; bool cmd_fixed; void (*rsp_func) (uint16_t index, const void *data, uint8_t size); uint8_t rsp_size; bool rsp_fixed; }; static const struct opcode_data opcode_table[] = { { 0x0000, -1, "NOP" }, /* OGF 1 - Link Control */ { 0x0401, 0, "Inquiry", inquiry_cmd, 5, true }, { 0x0402, 1, "Inquiry Cancel", null_cmd, 0, true, status_rsp, 1, true }, { 0x0403, 2, "Periodic Inquiry Mode", periodic_inquiry_cmd, 9, true, status_rsp, 1, true }, { 0x0404, 3, "Exit Periodic Inquiry Mode", null_cmd, 0, true, status_rsp, 1, true }, { 0x0405, 4, "Create Connection", create_conn_cmd, 13, true }, { 0x0406, 5, "Disconnect", disconnect_cmd, 3, true }, { 0x0407, 6, "Add SCO Connection", add_sco_conn_cmd, 4, true }, { 0x0408, 7, "Create Connection Cancel", create_conn_cancel_cmd, 6, true, status_bdaddr_rsp, 7, true }, { 0x0409, 8, "Accept Connection Request", accept_conn_request_cmd, 7, true }, { 0x040a, 9, "Reject Connection Request", reject_conn_request_cmd, 7, true }, { 0x040b, 10, "Link Key Request Reply", link_key_request_reply_cmd, 22, true, status_bdaddr_rsp, 7, true }, { 0x040c, 11, "Link Key Request Negative Reply", link_key_request_neg_reply_cmd, 6, true, status_bdaddr_rsp, 7, true }, { 0x040d, 12, "PIN Code Request Reply", pin_code_request_reply_cmd, 23, true, status_bdaddr_rsp, 7, true }, { 0x040e, 13, "PIN Code Request Negative Reply", pin_code_request_neg_reply_cmd, 6, true, status_bdaddr_rsp, 7, true }, { 0x040f, 14, "Change Connection Packet Type", change_conn_pkt_type_cmd, 4, true }, { 0x0411, 15, "Authentication Requested", auth_requested_cmd, 2, true }, { 0x0413, 16, "Set Connection Encryption", set_conn_encrypt_cmd, 3, true }, { 0x0415, 17, "Change Connection Link Key", change_conn_link_key_cmd, 2, true }, { 0x0417, 18, "Temporary Link Key", link_key_selection_cmd, 1, true }, { 0x0419, 19, "Remote Name Request", remote_name_request_cmd, 10, true }, { 0x041a, 20, "Remote Name Request Cancel", remote_name_request_cancel_cmd, 6, true, status_bdaddr_rsp, 7, true }, { 0x041b, 21, "Read Remote Supported Features", read_remote_features_cmd, 2, true }, { 0x041c, 22, "Read Remote Extended Features", read_remote_ext_features_cmd, 3, true }, { 0x041d, 23, "Read Remote Version Information", read_remote_version_cmd, 2, true }, { 0x041f, 24, "Read Clock Offset", read_clock_offset_cmd, 2, true }, { 0x0420, 25, "Read LMP Handle", read_lmp_handle_cmd, 2, true, read_lmp_handle_rsp, 8, true }, { 0x0428, 131, "Setup Synchronous Connection", setup_sync_conn_cmd, 17, true }, { 0x0429, 132, "Accept Synchronous Connection Request", accept_sync_conn_request_cmd, 21, true }, { 0x042a, 133, "Reject Synchronous Connection Request", reject_sync_conn_request_cmd, 7, true }, { 0x042b, 151, "IO Capability Request Reply", io_capability_request_reply_cmd, 9, true, status_bdaddr_rsp, 7, true }, { 0x042c, 152, "User Confirmation Request Reply", user_confirm_request_reply_cmd, 6, true, status_bdaddr_rsp, 7, true }, { 0x042d, 153, "User Confirmation Request Neg Reply", user_confirm_request_neg_reply_cmd, 6, true, status_bdaddr_rsp, 7, true }, { 0x042e, 154, "User Passkey Request Reply", user_passkey_request_reply_cmd, 10, true, status_bdaddr_rsp, 7, true }, { 0x042f, 155, "User Passkey Request Negative Reply", user_passkey_request_neg_reply_cmd, 6, true, status_bdaddr_rsp, 7, true }, { 0x0430, 156, "Remote OOB Data Request Reply", remote_oob_data_request_reply_cmd, 38, true, status_bdaddr_rsp, 7, true }, { 0x0433, 159, "Remote OOB Data Request Neg Reply", remote_oob_data_request_neg_reply_cmd, 6, true, status_bdaddr_rsp, 7, true }, { 0x0434, 163, "IO Capability Request Negative Reply", io_capability_request_neg_reply_cmd, 7, true, status_bdaddr_rsp, 7, true }, { 0x0435, 168, "Create Physical Link", create_phy_link_cmd, 3, false }, { 0x0436, 169, "Accept Physical Link", accept_phy_link_cmd, 3, false }, { 0x0437, 170, "Disconnect Physical Link", disconn_phy_link_cmd, 2, true }, { 0x0438, 171, "Create Logical Link", create_logic_link_cmd, 33, true }, { 0x0439, 172, "Accept Logical Link", accept_logic_link_cmd, 33, true }, { 0x043a, 173, "Disconnect Logical Link", disconn_logic_link_cmd, 2, true }, { 0x043b, 174, "Logical Link Cancel", logic_link_cancel_cmd, 2, true, logic_link_cancel_rsp, 3, true }, { 0x043c, 175, "Flow Specifcation Modify", flow_spec_modify_cmd, 34, true }, { 0x043d, 235, "Enhanced Setup Synchronous Connection", enhanced_setup_sync_conn_cmd, 59, true }, { 0x043e, 236, "Enhanced Accept Synchronous Connection Request", enhanced_accept_sync_conn_request_cmd, 63, true }, { 0x043f, 246, "Truncated Page", truncated_page_cmd, 9, true }, { 0x0440, 247, "Truncated Page Cancel", truncated_page_cancel_cmd, 6, true, status_bdaddr_rsp, 7, true }, { 0x0441, 248, "Set Connectionless Peripheral Broadcast", set_peripheral_broadcast_cmd, 11, true, set_peripheral_broadcast_rsp, 4, true }, { 0x0442, 249, "Set Connectionless Peripheral Broadcast Receive", set_peripheral_broadcast_receive_cmd, 34, true, set_peripheral_broadcast_receive_rsp, 8, true }, { 0x0443, 250, "Start Synchronization Train", null_cmd, 0, true }, { 0x0444, 251, "Receive Synchronization Train", receive_sync_train_cmd, 12, true }, { 0x0445, 257, "Remote OOB Extended Data Request Reply", remote_oob_ext_data_request_reply_cmd, 70, true, status_bdaddr_rsp, 7, true }, /* OGF 2 - Link Policy */ { 0x0801, 33, "Hold Mode", hold_mode_cmd, 6, true }, { 0x0803, 34, "Sniff Mode", sniff_mode_cmd, 10, true }, { 0x0804, 35, "Exit Sniff Mode", exit_sniff_mode_cmd, 2, true }, { 0x0805, 36, "Park State", park_state_cmd, 6, true }, { 0x0806, 37, "Exit Park State", exit_park_state_cmd, 2, true }, { 0x0807, 38, "QoS Setup", qos_setup_cmd, 20, true }, { 0x0809, 39, "Role Discovery", role_discovery_cmd, 2, true, role_discovery_rsp, 4, true }, { 0x080b, 40, "Switch Role", switch_role_cmd, 7, true }, { 0x080c, 41, "Read Link Policy Settings", read_link_policy_cmd, 2, true, read_link_policy_rsp, 5, true }, { 0x080d, 42, "Write Link Policy Settings", write_link_policy_cmd, 4, true, write_link_policy_rsp, 3, true }, { 0x080e, 43, "Read Default Link Policy Settings", null_cmd, 0, true, read_default_link_policy_rsp, 3, true }, { 0x080f, 44, "Write Default Link Policy Settings", write_default_link_policy_cmd, 2, true, status_rsp, 1, true }, { 0x0810, 45, "Flow Specification", flow_spec_cmd, 21, true }, { 0x0811, 140, "Sniff Subrating", sniff_subrating_cmd, 8, true, sniff_subrating_rsp, 3, true }, /* OGF 3 - Host Control */ { 0x0c01, 46, "Set Event Mask", set_event_mask_cmd, 8, true, status_rsp, 1, true }, { 0x0c03, 47, "Reset", null_cmd, 0, true, status_rsp, 1, true }, { 0x0c05, 48, "Set Event Filter", set_event_filter_cmd, 1, false, status_rsp, 1, true }, { 0x0c08, 49, "Flush", flush_cmd, 2, true, flush_rsp, 3, true }, { 0x0c09, 50, "Read PIN Type", null_cmd, 0, true, read_pin_type_rsp, 2, true }, { 0x0c0a, 51, "Write PIN Type", write_pin_type_cmd, 1, true, status_rsp, 1, true }, { 0x0c0b, 52, "Create New Unit Key", null_cmd, 0, true, status_rsp, 1, true }, { 0x0c0d, 53, "Read Stored Link Key", read_stored_link_key_cmd, 7, true, read_stored_link_key_rsp, 5, true }, { 0x0c11, 54, "Write Stored Link Key", write_stored_link_key_cmd, 1, false, write_stored_link_key_rsp, 2, true }, { 0x0c12, 55, "Delete Stored Link Key", delete_stored_link_key_cmd, 7, true, delete_stored_link_key_rsp, 3, true }, { 0x0c13, 56, "Write Local Name", write_local_name_cmd, 248, true, status_rsp, 1, true }, { 0x0c14, 57, "Read Local Name", null_cmd, 0, true, read_local_name_rsp, 249, true }, { 0x0c15, 58, "Read Connection Accept Timeout", null_cmd, 0, true, read_conn_accept_timeout_rsp, 3, true }, { 0x0c16, 59, "Write Connection Accept Timeout", write_conn_accept_timeout_cmd, 2, true, status_rsp, 1, true }, { 0x0c17, 60, "Read Page Timeout", null_cmd, 0, true, read_page_timeout_rsp, 3, true }, { 0x0c18, 61, "Write Page Timeout", write_page_timeout_cmd, 2, true, status_rsp, 1, true }, { 0x0c19, 62, "Read Scan Enable", null_cmd, 0, true, read_scan_enable_rsp, 2, true }, { 0x0c1a, 63, "Write Scan Enable", write_scan_enable_cmd, 1, true, status_rsp, 1, true }, { 0x0c1b, 64, "Read Page Scan Activity", null_cmd, 0, true, read_page_scan_activity_rsp, 5, true }, { 0x0c1c, 65, "Write Page Scan Activity", write_page_scan_activity_cmd, 4, true, status_rsp, 1, true }, { 0x0c1d, 66, "Read Inquiry Scan Activity", null_cmd, 0, true, read_inquiry_scan_activity_rsp, 5, true }, { 0x0c1e, 67, "Write Inquiry Scan Activity", write_inquiry_scan_activity_cmd, 4, true, status_rsp, 1, true }, { 0x0c1f, 68, "Read Authentication Enable", null_cmd, 0, true, read_auth_enable_rsp, 2, true }, { 0x0c20, 69, "Write Authentication Enable", write_auth_enable_cmd, 1, true, status_rsp, 1, true }, { 0x0c21, 70, "Read Encryption Mode", null_cmd, 0, true, read_encrypt_mode_rsp, 2, true }, { 0x0c22, 71, "Write Encryption Mode", write_encrypt_mode_cmd, 1, true, status_rsp, 1, true }, { 0x0c23, 72, "Read Class of Device", null_cmd, 0, true, read_class_of_dev_rsp, 4, true }, { 0x0c24, 73, "Write Class of Device", write_class_of_dev_cmd, 3, true, status_rsp, 1, true }, { 0x0c25, 74, "Read Voice Setting", null_cmd, 0, true, read_voice_setting_rsp, 3, true }, { 0x0c26, 75, "Write Voice Setting", write_voice_setting_cmd, 2, true, status_rsp, 1, true }, { 0x0c27, 76, "Read Automatic Flush Timeout", read_auto_flush_timeout_cmd, 2, true, read_auto_flush_timeout_rsp, 5, true }, { 0x0c28, 77, "Write Automatic Flush Timeout", write_auto_flush_timeout_cmd, 4, true, write_auto_flush_timeout_rsp, 3, true }, { 0x0c29, 78, "Read Num Broadcast Retransmissions", null_cmd, 0, true, read_num_broadcast_retrans_rsp, 2, true }, { 0x0c2a, 79, "Write Num Broadcast Retransmissions", write_num_broadcast_retrans_cmd, 1, true, status_rsp, 1, true }, { 0x0c2b, 80, "Read Hold Mode Activity", null_cmd, 0, true, read_hold_mode_activity_rsp, 2, true }, { 0x0c2c, 81, "Write Hold Mode Activity", write_hold_mode_activity_cmd, 1, true, status_rsp, 1, true }, { 0x0c2d, 82, "Read Transmit Power Level", read_tx_power_cmd, 3, true, read_tx_power_rsp, 4, true }, { 0x0c2e, 83, "Read Sync Flow Control Enable", null_cmd, 0, true, read_sync_flow_control_rsp, 2, true }, { 0x0c2f, 84, "Write Sync Flow Control Enable", write_sync_flow_control_cmd, 1, true, status_rsp, 1, true }, { 0x0c31, 85, "Set Controller To Host Flow Control", set_host_flow_control_cmd, 1, true, status_rsp, 1, true }, { 0x0c33, 86, "Host Buffer Size", host_buffer_size_cmd, 7, true, status_rsp, 1, true }, { 0x0c35, 87, "Host Number of Completed Packets", host_num_completed_packets_cmd, 5, false }, { 0x0c36, 88, "Read Link Supervision Timeout", read_link_supv_timeout_cmd, 2, true, read_link_supv_timeout_rsp, 5, true }, { 0x0c37, 89, "Write Link Supervision Timeout", write_link_supv_timeout_cmd, 4, true, write_link_supv_timeout_rsp, 3, true }, { 0x0c38, 90, "Read Number of Supported IAC", null_cmd, 0, true, read_num_supported_iac_rsp, 2, true }, { 0x0c39, 91, "Read Current IAC LAP", null_cmd, 0, true, read_current_iac_lap_rsp, 2, false }, { 0x0c3a, 92, "Write Current IAC LAP", write_current_iac_lap_cmd, 1, false, status_rsp, 1, true }, { 0x0c3b, 93, "Read Page Scan Period Mode", null_cmd, 0, true, read_page_scan_period_mode_rsp, 2, true }, { 0x0c3c, 94, "Write Page Scan Period Mode", write_page_scan_period_mode_cmd, 1, true, status_rsp, 1, true }, { 0x0c3d, 95, "Read Page Scan Mode", null_cmd, 0, true, read_page_scan_mode_rsp, 2, true }, { 0x0c3e, 96, "Write Page Scan Mode", write_page_scan_mode_cmd, 1, true, status_rsp, 1, true }, { 0x0c3f, 97, "Set AFH Host Channel Classification", set_afh_host_classification_cmd, 10, true, status_rsp, 1, true }, { 0x0c42, 100, "Read Inquiry Scan Type", null_cmd, 0, true, read_inquiry_scan_type_rsp, 2, true }, { 0x0c43, 101, "Write Inquiry Scan Type", write_inquiry_scan_type_cmd, 1, true, status_rsp, 1, true }, { 0x0c44, 102, "Read Inquiry Mode", null_cmd, 0, true, read_inquiry_mode_rsp, 2, true }, { 0x0c45, 103, "Write Inquiry Mode", write_inquiry_mode_cmd, 1, true, status_rsp, 1, true }, { 0x0c46, 104, "Read Page Scan Type", null_cmd, 0, true, read_page_scan_type_rsp, 2, true }, { 0x0c47, 105, "Write Page Scan Type", write_page_scan_type_cmd, 1, true, status_rsp, 1, true }, { 0x0c48, 106, "Read AFH Channel Assessment Mode", null_cmd, 0, true, read_afh_assessment_mode_rsp, 2, true }, { 0x0c49, 107, "Write AFH Channel Assessment Mode", write_afh_assessment_mode_cmd, 1, true, status_rsp, 1, true }, { 0x0c51, 136, "Read Extended Inquiry Response", null_cmd, 0, true, read_ext_inquiry_response_rsp, 242, true }, { 0x0c52, 137, "Write Extended Inquiry Response", write_ext_inquiry_response_cmd, 241, true, status_rsp, 1, true }, { 0x0c53, 138, "Refresh Encryption Key", refresh_encrypt_key_cmd, 2, true }, { 0x0c55, 141, "Read Simple Pairing Mode", null_cmd, 0, true, read_simple_pairing_mode_rsp, 2, true }, { 0x0c56, 142, "Write Simple Pairing Mode", write_simple_pairing_mode_cmd, 1, true, status_rsp, 1, true }, { 0x0c57, 143, "Read Local OOB Data", null_cmd, 0, true, read_local_oob_data_rsp, 33, true }, { 0x0c58, 144, "Read Inquiry Response TX Power Level", null_cmd, 0, true, read_inquiry_resp_tx_power_rsp, 2, true }, { 0x0c59, 145, "Write Inquiry Transmit Power Level", write_inquiry_tx_power_cmd, 1, true, status_rsp, 1, true }, { 0x0c5a, 146, "Read Default Erroneous Data Reporting", null_cmd, 0, true, read_erroneous_reporting_rsp, 2, true }, { 0x0c5b, 147, "Write Default Erroneous Data Reporting", write_erroneous_reporting_cmd, 1, true, status_rsp, 1, true }, { 0x0c5f, 158, "Enhanced Flush", enhanced_flush_cmd, 3, true }, { 0x0c60, 162, "Send Keypress Notification", send_keypress_notify_cmd, 7, true, send_keypress_notify_rsp, 7, true }, { 0x0c61, 176, "Read Logical Link Accept Timeout" }, { 0x0c62, 177, "Write Logical Link Accept Timeout" }, { 0x0c63, 178, "Set Event Mask Page 2", set_event_mask_page2_cmd, 8, true, status_rsp, 1, true }, { 0x0c64, 179, "Read Location Data", null_cmd, 0, true, read_location_data_rsp, 6, true }, { 0x0c65, 180, "Write Location Data", write_location_data_cmd, 5, true, status_rsp, 1, true }, { 0x0c66, 184, "Read Flow Control Mode", null_cmd, 0, true, read_flow_control_mode_rsp, 2, true }, { 0x0c67, 185, "Write Flow Control Mode", write_flow_control_mode_cmd, 1, true, status_rsp, 1, true }, { 0x0c68, 192, "Read Enhanced Transmit Power Level", read_enhanced_tx_power_cmd, 3, true, read_enhanced_tx_power_rsp, 6, true }, { 0x0c69, 194, "Read Best Effort Flush Timeout" }, { 0x0c6a, 195, "Write Best Effort Flush Timeout" }, { 0x0c6b, 196, "Short Range Mode", short_range_mode_cmd, 2, true }, { 0x0c6c, 197, "Read LE Host Supported", null_cmd, 0, true, read_le_host_supported_rsp, 3, true }, { 0x0c6d, 198, "Write LE Host Supported", write_le_host_supported_cmd, 2, true, status_rsp, 1, true }, { 0x0c6e, 238, "Set MWS Channel Parameters" }, { 0x0c6f, 239, "Set External Frame Configuration" }, { 0x0c70, 240, "Set MWS Signaling" }, { 0x0c71, 241, "Set MWS Transport Layer" }, { 0x0c72, 242, "Set MWS Scan Frequency Table" }, { 0x0c73, 244, "Set MWS Pattern Configuration" }, { 0x0c74, 252, "Set Reserved LT_ADDR", set_reserved_lt_addr_cmd, 1, true, set_reserved_lt_addr_rsp, 2, true }, { 0x0c75, 253, "Delete Reserved LT_ADDR", delete_reserved_lt_addr_cmd, 1, true, delete_reserved_lt_addr_rsp, 2, true }, { 0x0c76, 254, "Set Connectionless Peripheral Broadcast Data", set_peripheral_broadcast_data_cmd, 3, false, set_peripheral_broadcast_data_rsp, 2, true }, { 0x0c77, 255, "Read Synchronization Train Parameters", null_cmd, 0, true, read_sync_train_params_rsp, 8, true }, { 0x0c78, 256, "Write Synchronization Train Parameters", write_sync_train_params_cmd, 9, true, write_sync_train_params_rsp, 3, true }, { 0x0c79, 258, "Read Secure Connections Host Support", null_cmd, 0, true, read_secure_conn_support_rsp, 2, true }, { 0x0c7a, 259, "Write Secure Connections Host Support", write_secure_conn_support_cmd, 1, true, status_rsp, 1, true }, { 0x0c7b, 260, "Read Authenticated Payload Timeout", read_auth_payload_timeout_cmd, 2, true, read_auth_payload_timeout_rsp, 5, true }, { 0x0c7c, 261, "Write Authenticated Payload Timeout", write_auth_payload_timeout_cmd, 4, true, write_auth_payload_timeout_rsp, 3, true }, { 0x0c7d, 262, "Read Local OOB Extended Data", null_cmd, 0, true, read_local_oob_ext_data_rsp, 65, true }, { 0x0c7e, 264, "Read Extended Page Timeout", null_cmd, 0, true, read_ext_page_timeout_rsp, 3, true }, { 0x0c7f, 265, "Write Extended Page Timeout", write_ext_page_timeout_cmd, 2, true, status_rsp, 1, true }, { 0x0c80, 266, "Read Extended Inquiry Length", null_cmd, 0, true, read_ext_inquiry_length_rsp, 3, true }, { 0x0c81, 267, "Write Extended Inquiry Length", write_ext_inquiry_length_cmd, 2, true, status_rsp, 1, true }, /* OGF 4 - Information Parameter */ { 0x1001, 115, "Read Local Version Information", null_cmd, 0, true, read_local_version_rsp, 9, true }, { 0x1002, 116, "Read Local Supported Commands", null_cmd, 0, true, read_local_commands_rsp, 65, true }, { 0x1003, 117, "Read Local Supported Features", null_cmd, 0, true, read_local_features_rsp, 9, true }, { 0x1004, 118, "Read Local Extended Features", read_local_ext_features_cmd, 1, true, read_local_ext_features_rsp, 11, true }, { 0x1005, 119, "Read Buffer Size", null_cmd, 0, true, read_buffer_size_rsp, 8, true }, { 0x1007, 120, "Read Country Code", null_cmd, 0, true, read_country_code_rsp, 2, true }, { 0x1009, 121, "Read BD ADDR", null_cmd, 0, true, read_bd_addr_rsp, 7, true }, { 0x100a, 186, "Read Data Block Size", null_cmd, 0, true, read_data_block_size_rsp, 7, true }, { 0x100b, 237, "Read Local Supported Codecs", null_cmd, 0, true, read_local_codecs_rsp, 3, false }, { 0x100c, 331, "Read Local Simple Pairing Options", null_cmd, 0, true, read_local_pairing_options_rsp, 3, true }, { BT_HCI_CMD_READ_LOCAL_CODECS_V2, BT_HCI_BIT_READ_LOCAL_CODECS_V2, "Read Local Supported Codecs V2", null_cmd, 0, true, read_local_codecs_rsp_v2, sizeof(struct bt_hci_rsp_read_local_codecs_v2), false }, { BT_HCI_CMD_READ_LOCAL_CODEC_CAPS, BT_HCI_BIT_READ_LOCAL_CODEC_CAPS, "Read Local Supported Codec Capabilities", read_local_codec_caps_cmd, sizeof(struct bt_hci_cmd_read_local_codec_caps), true, read_local_codec_caps_rsp, sizeof(struct bt_hci_rsp_read_local_codec_caps), false }, { BT_HCI_CMD_READ_LOCAL_CTRL_DELAY, BT_HCI_BIT_READ_LOCAL_CTRL_DELAY, "Read Local Supported Controller Delay", read_local_ctrl_delay_cmd, sizeof(struct bt_hci_cmd_read_local_ctrl_delay), false, read_local_ctrl_delay_rsp, sizeof(struct bt_hci_rsp_read_local_ctrl_delay), true }, { BT_HCI_CMD_CONFIG_DATA_PATH, BT_HCI_BIT_CONFIG_DATA_PATH, "Configure Data Path", config_data_path_cmd, sizeof(struct bt_hci_cmd_config_data_path), false, status_rsp, 1, true }, /* OGF 5 - Status Parameter */ { 0x1401, 122, "Read Failed Contact Counter", read_failed_contact_counter_cmd, 2, true, read_failed_contact_counter_rsp, 5, true }, { 0x1402, 123, "Reset Failed Contact Counter", reset_failed_contact_counter_cmd, 2, true, reset_failed_contact_counter_rsp, 3, true }, { 0x1403, 124, "Read Link Quality", read_link_quality_cmd, 2, true, read_link_quality_rsp, 4, true }, { 0x1405, 125, "Read RSSI", read_rssi_cmd, 2, true, read_rssi_rsp, 4, true }, { 0x1406, 126, "Read AFH Channel Map", read_afh_channel_map_cmd, 2, true, read_afh_channel_map_rsp, 14, true }, { 0x1407, 127, "Read Clock", read_clock_cmd, 3, true, read_clock_rsp, 9, true }, { 0x1408, 164, "Read Encryption Key Size", read_encrypt_key_size_cmd, 2, true, read_encrypt_key_size_rsp, 4, true }, { 0x1409, 181, "Read Local AMP Info", null_cmd, 0, true, read_local_amp_info_rsp, 31, true }, { 0x140a, 182, "Read Local AMP ASSOC", read_local_amp_assoc_cmd, 5, true, read_local_amp_assoc_rsp, 5, false }, { 0x140b, 183, "Write Remote AMP ASSOC", write_remote_amp_assoc_cmd, 6, false, write_remote_amp_assoc_rsp, 2, true }, { 0x140c, 243, "Get MWS Transport Layer Configuration", null_cmd, 0, true, get_mws_transport_config_rsp, 2, false }, { 0x140d, 245, "Set Triggered Clock Capture", set_triggered_clock_capture_cmd, 6, true, status_rsp, 1, true }, /* OGF 6 - Testing */ { 0x1801, 128, "Read Loopback Mode", null_cmd, 0, true, read_loopback_mode_rsp, 2, true }, { 0x1802, 129, "Write Loopback Mode", write_loopback_mode_cmd, 1, true, status_rsp, 1, true }, { 0x1803, 130, "Enable Device Under Test Mode", null_cmd, 0, true, status_rsp, 1, true }, { 0x1804, 157, "Write Simple Pairing Debug Mode", write_ssp_debug_mode_cmd, 1, true, status_rsp, 1, true }, { 0x1807, 189, "Enable AMP Receiver Reports" }, { 0x1808, 190, "AMP Test End" }, { 0x1809, 191, "AMP Test" }, { 0x180a, 263, "Write Secure Connections Test Mode" }, /* OGF 8 - LE Control */ { 0x2001, 200, "LE Set Event Mask", le_set_event_mask_cmd, 8, true, status_rsp, 1, true }, { 0x2002, 201, "LE Read Buffer Size", null_cmd, 0, true, le_read_buffer_size_rsp, 4, true }, { 0x2003, 202, "LE Read Local Supported Features", null_cmd, 0, true, le_read_local_features_rsp, 9, true }, { 0x2005, 204, "LE Set Random Address", le_set_random_address_cmd, 6, true, status_rsp, 1, true }, { 0x2006, 205, "LE Set Advertising Parameters", le_set_adv_parameters_cmd, 15, true, status_rsp, 1, true }, { 0x2007, 206, "LE Read Advertising Channel TX Power", null_cmd, 0, true, le_read_adv_tx_power_rsp, 2, true }, { 0x2008, 207, "LE Set Advertising Data", le_set_adv_data_cmd, 32, true, status_rsp, 1, true }, { 0x2009, 208, "LE Set Scan Response Data", le_set_scan_rsp_data_cmd, 32, true, status_rsp, 1, true }, { 0x200a, 209, "LE Set Advertise Enable", le_set_adv_enable_cmd, 1, true, status_rsp, 1, true }, { 0x200b, 210, "LE Set Scan Parameters", le_set_scan_parameters_cmd, 7, true, status_rsp, 1, true }, { 0x200c, 211, "LE Set Scan Enable", le_set_scan_enable_cmd, 2, true, status_rsp, 1, true }, { 0x200d, 212, "LE Create Connection", le_create_conn_cmd, 25, true }, { 0x200e, 213, "LE Create Connection Cancel", null_cmd, 0, true, status_rsp, 1, true }, { 0x200f, 214, "LE Read Accept List Size", null_cmd, 0, true, le_read_accept_list_size_rsp, 2, true }, { 0x2010, 215, "LE Clear Accept List", null_cmd, 0, true, status_rsp, 1, true }, { 0x2011, 216, "LE Add Device To Accept List", le_add_to_accept_list_cmd, 7, true, status_rsp, 1, true }, { 0x2012, 217, "LE Remove Device From Accept List", le_remove_from_accept_list_cmd, 7, true, status_rsp, 1, true }, { 0x2013, 218, "LE Connection Update", le_conn_update_cmd, 14, true }, { 0x2014, 219, "LE Set Host Channel Classification", le_set_host_classification_cmd, 5, true, status_rsp, 1, true }, { 0x2015, 220, "LE Read Channel Map", le_read_channel_map_cmd, 2, true, le_read_channel_map_rsp, 8, true }, { 0x2016, 221, "LE Read Remote Used Features", le_read_remote_features_cmd, 2, true }, { 0x2017, 222, "LE Encrypt", le_encrypt_cmd, 32, true, le_encrypt_rsp, 17, true }, { 0x2018, 223, "LE Rand", null_cmd, 0, true, le_rand_rsp, 9, true }, { 0x2019, 224, "LE Start Encryption", le_start_encrypt_cmd, 28, true }, { 0x201a, 225, "LE Long Term Key Request Reply", le_ltk_req_reply_cmd, 18, true, le_ltk_req_reply_rsp, 3, true }, { 0x201b, 226, "LE Long Term Key Request Neg Reply", le_ltk_req_neg_reply_cmd, 2, true, le_ltk_req_neg_reply_rsp, 3, true }, { 0x201c, 227, "LE Read Supported States", null_cmd, 0, true, le_read_supported_states_rsp, 9, true }, { 0x201d, 228, "LE Receiver Test", le_receiver_test_cmd, 1, true, status_rsp, 1, true }, { 0x201e, 229, "LE Transmitter Test", le_transmitter_test_cmd, 3, true, status_rsp, 1, true }, { 0x201f, 230, "LE Test End", null_cmd, 0, true, le_test_end_rsp, 3, true }, { 0x2020, 268, "LE Remote Connection Parameter Request Reply", le_conn_param_req_reply_cmd, 14, true, le_conn_param_req_reply_rsp, 3, true }, { 0x2021, 269, "LE Remote Connection Parameter Request Negative Reply", le_conn_param_req_neg_reply_cmd, 3, true, le_conn_param_req_neg_reply_rsp, 3, true }, { 0x2022, 270, "LE Set Data Length", le_set_data_length_cmd, 6, true, le_set_data_length_rsp, 3, true }, { 0x2023, 271, "LE Read Suggested Default Data Length", null_cmd, 0, true, le_read_default_data_length_rsp, 5, true }, { 0x2024, 272, "LE Write Suggested Default Data Length", le_write_default_data_length_cmd, 4, true, status_rsp, 1, true }, { 0x2025, 273, "LE Read Local P-256 Public Key", null_cmd, 0, true }, { 0x2026, 274, "LE Generate DHKey", le_generate_dhkey_cmd, 64, true }, { 0x2027, 275, "LE Add Device To Resolving List", le_add_to_resolv_list_cmd, 39, true, status_rsp, 1, true }, { 0x2028, 276, "LE Remove Device From Resolving List", le_remove_from_resolv_list_cmd, 7, true, status_rsp, 1, true }, { 0x2029, 277, "LE Clear Resolving List", null_cmd, 0, true, status_rsp, 1, true }, { 0x202a, 278, "LE Read Resolving List Size", null_cmd, 0, true, le_read_resolv_list_size_rsp, 2, true }, { 0x202b, 279, "LE Read Peer Resolvable Address", le_read_peer_resolv_addr_cmd, 7, true, le_read_peer_resolv_addr_rsp, 7, true }, { 0x202c, 280, "LE Read Local Resolvable Address", le_read_local_resolv_addr_cmd, 7, true, le_read_local_resolv_addr_rsp, 7, true }, { 0x202d, 281, "LE Set Address Resolution Enable", le_set_resolv_enable_cmd, 1, true, status_rsp, 1, true }, { 0x202e, 282, "LE Set Resolvable Private Address Timeout", le_set_resolv_timeout_cmd, 2, true, status_rsp, 1, true }, { 0x202f, 283, "LE Read Maximum Data Length", null_cmd, 0, true, le_read_max_data_length_rsp, 9, true }, { 0x2030, 284, "LE Read PHY", le_read_phy_cmd, 2, true, le_read_phy_rsp, 5, true}, { 0x2031, 285, "LE Set Default PHY", le_set_default_phy_cmd, 3, true, status_rsp, 1, true }, { 0x2032, 286, "LE Set PHY", le_set_phy_cmd, 7, true}, { 0x2033, 287, "LE Enhanced Receiver Test", le_enhanced_receiver_test_cmd, 3, true, status_rsp, 1, true }, { 0x2034, 288, "LE Enhanced Transmitter Test", le_enhanced_transmitter_test_cmd, 4, true, status_rsp, 1, true }, { 0x2035, 289, "LE Set Advertising Set Random Address", le_set_adv_set_rand_addr, 7, true, status_rsp, 1, true }, { 0x2036, 290, "LE Set Extended Advertising Parameters", le_set_ext_adv_params_cmd, 25, true, le_set_ext_adv_params_rsp, 2, true }, { 0x2037, 291, "LE Set Extended Advertising Data", le_set_ext_adv_data_cmd, 4, false, status_rsp, 1, true }, { 0x2038, 292, "LE Set Extended Scan Response Data", le_set_ext_scan_rsp_data_cmd, 4, false, status_rsp, 1, true }, { 0x2039, 293, "LE Set Extended Advertising Enable", le_set_ext_adv_enable_cmd, 2, false, status_rsp, 1, true }, { 0x203a, 294, "LE Read Maximum Advertising Data Length", null_cmd, 0, true, le_read_max_adv_data_len_rsp, 3, true }, { 0x203b, 295, "LE Read Number of Supported Advertising Sets", null_cmd, 0, true, le_read_num_supported_adv_sets_rsp, 2, true }, { 0x203c, 296, "LE Remove Advertising Set", le_remove_adv_set_cmd, 1, true, status_rsp, 1, true }, { 0x203d, 297, "LE Clear Advertising Sets", null_cmd, 0, true, status_rsp, 1, true }, { 0x203e, 298, "LE Set Periodic Advertising Parameters", le_set_pa_params_cmd, 7, true, status_rsp, 1, true }, { 0x203f, 299, "LE Set Periodic Advertising Data", le_set_pa_data_cmd, 3, false, status_rsp, 1, true }, { 0x2040, 300, "LE Set Periodic Advertising Enable", le_set_pa_enable_cmd, 2, true, status_rsp, 1, true }, { 0x2041, 301, "LE Set Extended Scan Parameters", le_set_ext_scan_params_cmd, 3, false, status_rsp, 1, true }, { 0x2042, 302, "LE Set Extended Scan Enable", le_set_ext_scan_enable_cmd, 6, true, status_rsp, 1, true }, { 0x2043, 303, "LE Extended Create Connection", le_ext_create_conn_cmd, 10, false, status_rsp, 1, true }, { 0x2044, 304, "LE Periodic Advertising Create Sync", le_pa_create_sync_cmd, 14, true, status_rsp, 1, true }, { 0x2045, 305, "LE Periodic Advertising Create Sync Cancel", null_cmd, 0, true, status_rsp, 1, true }, { 0x2046, 306, "LE Periodic Advertising Terminate Sync", le_pa_term_sync_cmd, 2, true, status_rsp, 1, true }, { 0x2047, 307, "LE Add Device To Periodic Advertiser List", le_add_dev_pa_list_cmd, 8, true, status_rsp, 1, true }, { 0x2048, 308, "LE Remove Device From Periodic Advertiser List", le_remove_dev_pa_list_cmd, 8, true, status_rsp, 1, true }, { 0x2049, 309, "LE Clear Periodic Advertiser List", null_cmd, 0, true, status_rsp, 1, true }, { 0x204a, 310, "LE Read Periodic Advertiser List Size", null_cmd, 0, true, le_read_pa_list_size_rsp, 2, true }, { 0x204b, 311, "LE Read Transmit Power", null_cmd, 0, true, le_read_tx_power_rsp, 3, true }, { 0x204c, 312, "LE Read RF Path Compensation", null_cmd, 0, true, le_read_rf_path_comp_rsp, 5, true }, { 0x204d, 313, "LE Write RF Path Compensation", le_write_rf_path_comp_cmd, 4, true, status_rsp, 1, true }, { 0x204e, 314, "LE Set Privacy Mode", le_set_priv_mode_cmd, 8, true, status_rsp, 1, true }, { 0x204f, 315, "LE Receiver Test command [v3]", le_receiver_test_cmd_v3, 7, false, status_rsp, 1, true }, { 0x2050, 316, "LE Transmitter Test command [v3]", le_tx_test_cmd_v3, 9, false, status_rsp, 1, true }, { 0x2059, 325, "LE Periodic Advertising Receive Enable", le_pa_rec_enable, 3, true, status_rsp, 1, true }, { 0x205a, 326, "LE Periodic Advertising Sync Transfer", le_pa_sync_trans, 6, true, status_handle_rsp, 3, true }, { 0x205b, 327, "LE Periodic Advertising Set Info Transfer", le_pa_set_info_trans, 5, true, status_handle_rsp, 3, true }, { 0x205c, 328, "LE Periodic Advertising Sync Transfer Parameters", le_pa_sync_trans_params, 8, true, status_handle_rsp, 3, true}, { 0x205d, 329, "LE Set Default Periodic Advertisng Sync Transfer " "Parameters", le_set_default_pa_sync_trans_params, 6, true, status_rsp, 1, true}, { BT_HCI_CMD_LE_READ_BUFFER_SIZE_V2, BT_HCI_BIT_LE_READ_BUFFER_SIZE_V2, "LE Read Buffer v2", null_cmd, 0, true, le_read_buffer_size_v2_rsp, sizeof( struct bt_hci_rsp_le_read_buffer_size_v2), true }, { BT_HCI_CMD_LE_READ_ISO_TX_SYNC, BT_HCI_BIT_LE_READ_ISO_TX_SYNC, "LE Read ISO TX Sync", le_read_iso_tx_sync_cmd, sizeof(struct bt_hci_cmd_le_read_iso_tx_sync), true, le_read_iso_tx_sync_rsp, sizeof(struct bt_hci_rsp_le_read_iso_tx_sync), true }, { BT_HCI_CMD_LE_SET_CIG_PARAMS, BT_HCI_BIT_LE_SET_CIG_PARAMS, "LE Set Connected Isochronous Group Parameters", le_set_cig_params_cmd, sizeof(struct bt_hci_cmd_le_set_cig_params), false, le_set_cig_params_rsp, sizeof(struct bt_hci_rsp_le_set_cig_params), false }, { BT_HCI_CMD_LE_SET_CIG_PARAMS_TEST, BT_HCI_BIT_LE_SET_CIG_PARAMS_TEST, "LE Set Connected Isochronous Group Parameters" " Test", le_set_cig_params_test_cmd, sizeof( struct bt_hci_cmd_le_set_cig_params_test), false, le_set_cig_params_rsp, sizeof(struct bt_hci_rsp_le_set_cig_params), false }, { BT_HCI_CMD_LE_CREATE_CIS, BT_HCI_BIT_LE_CREATE_CIS, "LE Create Connected Isochronous Stream", le_create_cis_cmd, sizeof(struct bt_hci_cmd_le_create_cis), false }, { BT_HCI_CMD_LE_REMOVE_CIG, BT_HCI_BIT_LE_REMOVE_CIG, "LE Remove Connected Isochronous Group", le_remove_cig_cmd, sizeof(struct bt_hci_cmd_le_remove_cig), false, le_remove_cig_rsp, sizeof(struct bt_hci_rsp_le_remove_cig), false }, { BT_HCI_CMD_LE_ACCEPT_CIS, BT_HCI_BIT_LE_ACCEPT_CIS, "LE Accept Connected Isochronous Stream Request", le_accept_cis_req_cmd, sizeof(struct bt_hci_cmd_le_accept_cis), true }, { BT_HCI_CMD_LE_REJECT_CIS, BT_HCI_BIT_LE_REJECT_CIS, "LE Reject Connected Isochronous Stream Request", le_reject_cis_req_cmd, sizeof(struct bt_hci_cmd_le_reject_cis), true, status_rsp, 1, true }, { BT_HCI_CMD_LE_CREATE_BIG, BT_HCI_BIT_LE_CREATE_BIG, "LE Create Broadcast Isochronous Group", le_create_big_cmd }, { BT_HCI_CMD_LE_CREATE_BIG_TEST, BT_HCI_BIT_LE_CREATE_BIG_TEST, "LE Create Broadcast Isochronous Group Test", le_create_big_cmd_test_cmd }, { BT_HCI_CMD_LE_TERM_BIG, BT_HCI_BIT_LE_TERM_BIG, "LE Terminate Broadcast Isochronous Group", le_terminate_big_cmd, sizeof(struct bt_hci_cmd_le_term_big), true, status_rsp, 1, true}, { BT_HCI_CMD_LE_BIG_CREATE_SYNC, BT_HCI_BIT_LE_BIG_CREATE_SYNC, "LE Broadcast Isochronous Group Create Sync", le_big_create_sync_cmd, sizeof(struct bt_hci_cmd_le_big_create_sync), false }, { BT_HCI_CMD_LE_BIG_TERM_SYNC, BT_HCI_BIT_LE_BIG_TERM_SYNC, "LE Broadcast Isochronous Group Terminate Sync", le_big_term_sync_cmd, sizeof(struct bt_hci_cmd_le_big_term_sync), true }, { BT_HCI_CMD_LE_REQ_PEER_SCA, BT_HCI_BIT_LE_REQ_PEER_SCA, "LE Request Peer SCA", le_req_peer_sca_cmd, sizeof(struct bt_hci_cmd_le_req_peer_sca), true }, { BT_HCI_CMD_LE_SETUP_ISO_PATH, BT_HCI_BIT_LE_SETUP_ISO_PATH, "LE Setup Isochronous Data Path", le_setup_iso_path_cmd, sizeof(struct bt_hci_cmd_le_setup_iso_path), true, le_setup_iso_path_rsp, sizeof(struct bt_hci_rsp_le_setup_iso_path), true }, { BT_HCI_CMD_LE_REMOVE_ISO_PATH, BT_HCI_BIT_LE_REMOVE_ISO_PATH, "LE Remove Isochronous Data Path", le_remove_iso_path_cmd, sizeof(struct bt_hci_cmd_le_remove_iso_path), true, status_handle_rsp, sizeof(struct bt_hci_rsp_le_remove_iso_path), true }, { BT_HCI_CMD_LE_ISO_TX_TEST, BT_HCI_BIT_LE_ISO_TX_TEST, "LE Isochronous Transmit Test", NULL, 0, false }, { BT_HCI_CMD_LE_ISO_RX_TEST, BT_HCI_BIT_LE_ISO_RX_TEST, "LE Isochronous Receive Test", NULL, 0, false }, { BT_HCI_CMD_LE_ISO_READ_TEST_COUNTER, BT_HCI_BIT_LE_ISO_READ_TEST_COUNTER, "LE Isochronous Read Test Counters", NULL, 0, false }, { BT_HCI_CMD_LE_ISO_TEST_END, BT_HCI_BIT_LE_ISO_TEST_END, "LE Isochronous Read Test Counters", NULL, 0, false }, { BT_HCI_CMD_LE_SET_HOST_FEATURE, BT_HCI_BIT_LE_SET_HOST_FEATURE, "LE Set Host Feature", le_set_host_feature_cmd, sizeof(struct bt_hci_cmd_le_set_host_feature), true, status_rsp, 1, true }, { BT_HCI_CMD_LE_READ_ISO_LINK_QUALITY, BT_HCI_BIT_LE_READ_ISO_LINK_QUALITY, "LE Read ISO link quality", le_read_iso_link_quality_cmd, sizeof( struct bt_hci_cmd_le_read_iso_link_quality), true, status_le_read_iso_link_quality_rsp, sizeof( struct bt_hci_rsp_le_read_iso_link_quality), true }, { } }; static const char *get_supported_command(int bit) { int i; for (i = 0; opcode_table[i].str; i++) { if (opcode_table[i].bit == bit) return opcode_table[i].str; } return NULL; } static const char *current_vendor_str(uint16_t ocf) { uint16_t manufacturer, msft_opcode; if (index_current < MAX_INDEX) { manufacturer = index_list[index_current].manufacturer; msft_opcode = index_list[index_current].msft_opcode; } else { manufacturer = fallback_manufacturer; msft_opcode = BT_HCI_CMD_NOP; } if (msft_opcode != BT_HCI_CMD_NOP && cmd_opcode_ocf(msft_opcode) == ocf) return "Microsoft"; switch (manufacturer) { case 2: return "Intel"; case 15: return "Broadcom"; case 93: return "Realtek"; } return NULL; } static const struct vendor_ocf *current_vendor_ocf(uint16_t ocf) { uint16_t manufacturer, msft_opcode; if (index_current < MAX_INDEX) { manufacturer = index_list[index_current].manufacturer; msft_opcode = index_list[index_current].msft_opcode; } else { manufacturer = fallback_manufacturer; msft_opcode = BT_HCI_CMD_NOP; } if (msft_opcode != BT_HCI_CMD_NOP && cmd_opcode_ocf(msft_opcode) == ocf) return msft_vendor_ocf(); switch (manufacturer) { case 2: return intel_vendor_ocf(ocf); case 15: return broadcom_vendor_ocf(ocf); } return NULL; } static const struct vendor_evt *current_vendor_evt(const void *data, int *consumed_size) { uint16_t manufacturer; uint8_t evt = *((const uint8_t *) data); /* A regular vendor event consumes 1 byte. */ *consumed_size = 1; if (index_current < MAX_INDEX) manufacturer = index_list[index_current].manufacturer; else manufacturer = fallback_manufacturer; switch (manufacturer) { case 2: return intel_vendor_evt(data, consumed_size); case 15: return broadcom_vendor_evt(evt); } return NULL; } static const char *current_vendor_evt_str(void) { uint16_t manufacturer; if (index_current < MAX_INDEX) manufacturer = index_list[index_current].manufacturer; else manufacturer = fallback_manufacturer; switch (manufacturer) { case 2: return "Intel"; case 15: return "Broadcom"; case 93: return "Realtek"; } return NULL; } static void inquiry_complete_evt(struct timeval *tv, uint16_t index, const void *data, uint8_t size) { const struct bt_hci_evt_inquiry_complete *evt = data; print_status(evt->status); } static void inquiry_result_evt(struct timeval *tv, uint16_t index, const void *data, uint8_t size) { const struct bt_hci_evt_inquiry_result *evt = data; print_num_resp(evt->num_resp); print_bdaddr(evt->bdaddr); print_pscan_rep_mode(evt->pscan_rep_mode); print_pscan_period_mode(evt->pscan_period_mode); print_pscan_mode(evt->pscan_mode); print_dev_class(evt->dev_class); print_clock_offset(evt->clock_offset); if (size > sizeof(*evt)) packet_hexdump(data + sizeof(*evt), size - sizeof(*evt)); } static void conn_complete_evt(struct timeval *tv, uint16_t index, const void *data, uint8_t size) { const struct bt_hci_evt_conn_complete *evt = data; print_status(evt->status); print_field("Handle: %d", le16_to_cpu(evt->handle)); print_bdaddr(evt->bdaddr); print_link_type(evt->link_type); print_enable("Encryption", evt->encr_mode); if (evt->status == 0x00) assign_handle(index, le16_to_cpu(evt->handle), 0x00, (void *)evt->bdaddr, BDADDR_BREDR); } static void conn_request_evt(struct timeval *tv, uint16_t index, const void *data, uint8_t size) { const struct bt_hci_evt_conn_request *evt = data; print_bdaddr(evt->bdaddr); print_dev_class(evt->dev_class); print_link_type(evt->link_type); } static void disconnect_complete_evt(struct timeval *tv, uint16_t index, const void *data, uint8_t size) { const struct bt_hci_evt_disconnect_complete *evt = data; print_status(evt->status); print_handle(evt->handle); print_reason(evt->reason); if (evt->status == 0x00) release_handle(le16_to_cpu(evt->handle)); } static void auth_complete_evt(struct timeval *tv, uint16_t index, const void *data, uint8_t size) { const struct bt_hci_evt_auth_complete *evt = data; print_status(evt->status); print_handle(evt->handle); } static void remote_name_request_complete_evt(struct timeval *tv, uint16_t index, const void *data, uint8_t size) { const struct bt_hci_evt_remote_name_request_complete *evt = data; print_status(evt->status); print_bdaddr(evt->bdaddr); print_name(evt->name); } static void encrypt_change_evt(struct timeval *tv, uint16_t index, const void *data, uint8_t size) { const struct bt_hci_evt_encrypt_change *evt = data; print_status(evt->status); print_handle(evt->handle); print_encr_mode_change(evt->encr_mode, evt->handle); } static void change_conn_link_key_complete_evt(struct timeval *tv, uint16_t index, const void *data, uint8_t size) { const struct bt_hci_evt_change_conn_link_key_complete *evt = data; print_status(evt->status); print_handle(evt->handle); } static void link_key_type_changed_evt(struct timeval *tv, uint16_t index, const void *data, uint8_t size) { const struct bt_hci_evt_link_key_type_changed *evt = data; print_status(evt->status); print_handle(evt->handle); print_key_flag(evt->key_flag); } static void remote_features_complete_evt(struct timeval *tv, uint16_t index, const void *data, uint8_t size) { const struct bt_hci_evt_remote_features_complete *evt = data; print_status(evt->status); print_handle(evt->handle); print_features(0, evt->features, 0x00); } static void remote_version_complete_evt(struct timeval *tv, uint16_t index, const void *data, uint8_t size) { const struct bt_hci_evt_remote_version_complete *evt = data; print_status(evt->status); print_handle(evt->handle); print_lmp_version(evt->lmp_ver, evt->lmp_subver); print_manufacturer(evt->manufacturer); switch (le16_to_cpu(evt->manufacturer)) { case 15: print_manufacturer_broadcom(evt->lmp_subver, 0xffff); break; } } static void qos_setup_complete_evt(struct timeval *tv, uint16_t index, const void *data, uint8_t size) { const struct bt_hci_evt_qos_setup_complete *evt = data; print_status(evt->status); print_handle(evt->handle); print_field("Flags: 0x%2.2x", evt->flags); print_service_type(evt->service_type); print_field("Token rate: %d", le32_to_cpu(evt->token_rate)); print_field("Peak bandwidth: %d", le32_to_cpu(evt->peak_bandwidth)); print_field("Latency: %d", le32_to_cpu(evt->latency)); print_field("Delay variation: %d", le32_to_cpu(evt->delay_variation)); } static void cmd_complete_evt(struct timeval *tv, uint16_t index, const void *data, uint8_t size) { const struct bt_hci_evt_cmd_complete *evt = data; uint16_t opcode = le16_to_cpu(evt->opcode); uint16_t ogf = cmd_opcode_ogf(opcode); uint16_t ocf = cmd_opcode_ocf(opcode); struct opcode_data vendor_data; const struct opcode_data *opcode_data = NULL; const char *opcode_color, *opcode_str; char vendor_str[150]; int i; for (i = 0; opcode_table[i].str; i++) { if (opcode_table[i].opcode == opcode) { opcode_data = &opcode_table[i]; break; } } if (opcode_data) { if (opcode_data->rsp_func) opcode_color = COLOR_HCI_COMMAND; else opcode_color = COLOR_HCI_COMMAND_UNKNOWN; opcode_str = opcode_data->str; } else { if (ogf == 0x3f) { const struct vendor_ocf *vnd = current_vendor_ocf(ocf); if (vnd) { const char *str = current_vendor_str(ocf); if (str) { snprintf(vendor_str, sizeof(vendor_str), "%s %s", str, vnd->str); vendor_data.str = vendor_str; } else vendor_data.str = vnd->str; vendor_data.rsp_func = vnd->rsp_func; vendor_data.rsp_size = vnd->rsp_size; vendor_data.rsp_fixed = vnd->rsp_fixed; opcode_data = &vendor_data; if (opcode_data->rsp_func) opcode_color = COLOR_HCI_COMMAND; else opcode_color = COLOR_HCI_COMMAND_UNKNOWN; opcode_str = opcode_data->str; } else { opcode_color = COLOR_HCI_COMMAND; opcode_str = "Vendor"; } } else { opcode_color = COLOR_HCI_COMMAND_UNKNOWN; opcode_str = "Unknown"; } } print_indent(6, opcode_color, "", opcode_str, COLOR_OFF, " (0x%2.2x|0x%4.4x) ncmd %d", ogf, ocf, evt->ncmd); if (!opcode_data || !opcode_data->rsp_func) { if (size > 3) { uint8_t status = *((uint8_t *) (data + 3)); print_status(status); packet_hexdump(data + 4, size - 4); } return; } if (opcode_data->rsp_size > 1 && size - 3 == 1) { uint8_t status = *((uint8_t *) (data + 3)); print_status(status); return; } if (opcode_data->rsp_fixed) { if (size - 3 != opcode_data->rsp_size) { print_text(COLOR_ERROR, "invalid packet size"); packet_hexdump(data + 3, size - 3); return; } } else { if (size - 3 < opcode_data->rsp_size) { print_text(COLOR_ERROR, "too short packet"); packet_hexdump(data + 3, size - 3); return; } } opcode_data->rsp_func(index, data + 3, size - 3); } static void cmd_status_evt(struct timeval *tv, uint16_t index, const void *data, uint8_t size) { const struct bt_hci_evt_cmd_status *evt = data; uint16_t opcode = le16_to_cpu(evt->opcode); uint16_t ogf = cmd_opcode_ogf(opcode); uint16_t ocf = cmd_opcode_ocf(opcode); const struct opcode_data *opcode_data = NULL; const char *opcode_color, *opcode_str; char vendor_str[150]; int i; for (i = 0; opcode_table[i].str; i++) { if (opcode_table[i].opcode == opcode) { opcode_data = &opcode_table[i]; break; } } if (opcode_data) { opcode_color = COLOR_HCI_COMMAND; opcode_str = opcode_data->str; } else { if (ogf == 0x3f) { const struct vendor_ocf *vnd = current_vendor_ocf(ocf); if (vnd) { const char *str = current_vendor_str(ocf); if (str) { snprintf(vendor_str, sizeof(vendor_str), "%s %s", str, vnd->str); opcode_str = vendor_str; } else opcode_str = vnd->str; opcode_color = COLOR_HCI_COMMAND; } else { opcode_color = COLOR_HCI_COMMAND; opcode_str = "Vendor"; } } else { opcode_color = COLOR_HCI_COMMAND_UNKNOWN; opcode_str = "Unknown"; } } print_indent(6, opcode_color, "", opcode_str, COLOR_OFF, " (0x%2.2x|0x%4.4x) ncmd %d", ogf, ocf, evt->ncmd); print_status(evt->status); } static void hardware_error_evt(struct timeval *tv, uint16_t index, const void *data, uint8_t size) { const struct bt_hci_evt_hardware_error *evt = data; print_field("Code: 0x%2.2x", evt->code); } static void flush_occurred_evt(struct timeval *tv, uint16_t index, const void *data, uint8_t size) { const struct bt_hci_evt_flush_occurred *evt = data; print_handle(evt->handle); } static void role_change_evt(struct timeval *tv, uint16_t index, const void *data, uint8_t size) { const struct bt_hci_evt_role_change *evt = data; print_status(evt->status); print_bdaddr(evt->bdaddr); print_role(evt->role); } void packet_latency_add(struct packet_latency *latency, struct timeval *delta) { timeradd(&latency->total, delta, &latency->total); if ((!timerisset(&latency->min) || timercmp(delta, &latency->min, <)) && delta->tv_sec >= 0 && delta->tv_usec >= 0) latency->min = *delta; if (!timerisset(&latency->max) || timercmp(delta, &latency->max, >)) latency->max = *delta; if (timerisset(&latency->med)) { struct timeval tmp; timeradd(&latency->med, delta, &tmp); tmp.tv_sec /= 2; tmp.tv_usec /= 2; if (tmp.tv_sec % 2) { tmp.tv_usec += 500000; if (tmp.tv_usec >= 1000000) { tmp.tv_sec++; tmp.tv_usec -= 1000000; } } latency->med = tmp; } else latency->med = *delta; } static void packet_dequeue_tx(struct timeval *tv, uint16_t handle) { struct packet_conn_data *conn; struct packet_frame *frame; struct timeval delta; conn = packet_get_conn_data(handle); if (!conn) return; frame = queue_pop_head(conn->tx_q); if (!frame) return; timersub(tv, &frame->tv, &delta); packet_latency_add(&conn->tx_l, &delta); if (TV_MSEC(delta)) { print_field("#%zu: len %zu (%lld Kb/s)", frame->num, frame->len, frame->len * 8 / TV_MSEC(delta)); print_field("Latency: %lld msec (%lld-%lld msec ~%lld msec)", TV_MSEC(delta), TV_MSEC(conn->tx_l.min), TV_MSEC(conn->tx_l.max), TV_MSEC(conn->tx_l.med)); } l2cap_dequeue_frame(&delta, conn); free(frame); } static void num_completed_packets_evt(struct timeval *tv, uint16_t index, const void *data, uint8_t size) { struct iovec iov = { (void *)data, size}; const struct bt_hci_evt_num_completed_packets *evt = data; int i; util_iov_pull(&iov, 1); print_field("Num handles: %d", evt->num_handles); for (i = 0; i < evt->num_handles; i++) { uint16_t handle; uint16_t count; int j; if (!util_iov_pull_le16(&iov, &handle)) break; print_handle_native(handle); if (!util_iov_pull_le16(&iov, &count)) break; print_field("Count: %d", count); for (j = 0; j < count; j++) packet_dequeue_tx(tv, handle); } if (iov.iov_len) packet_hexdump(iov.iov_base, iov.iov_len); } static void mode_change_evt(struct timeval *tv, uint16_t index, const void *data, uint8_t size) { const struct bt_hci_evt_mode_change *evt = data; print_status(evt->status); print_handle(evt->handle); print_mode(evt->mode); print_interval(evt->interval); } static void return_link_keys_evt(struct timeval *tv, uint16_t index, const void *data, uint8_t size) { const struct bt_hci_evt_return_link_keys *evt = data; uint8_t i; print_field("Num keys: %d", evt->num_keys); for (i = 0; i < evt->num_keys; i++) { print_bdaddr(evt->keys + (i * 22)); print_link_key(evt->keys + (i * 22) + 6); } } static void pin_code_request_evt(struct timeval *tv, uint16_t index, const void *data, uint8_t size) { const struct bt_hci_evt_pin_code_request *evt = data; print_bdaddr(evt->bdaddr); } static void link_key_request_evt(struct timeval *tv, uint16_t index, const void *data, uint8_t size) { const struct bt_hci_evt_link_key_request *evt = data; print_bdaddr(evt->bdaddr); } static void link_key_notify_evt(struct timeval *tv, uint16_t index, const void *data, uint8_t size) { const struct bt_hci_evt_link_key_notify *evt = data; print_bdaddr(evt->bdaddr); print_link_key(evt->link_key); print_key_type(evt->key_type); } static void loopback_command_evt(struct timeval *tv, uint16_t index, const void *data, uint8_t size) { packet_hexdump(data, size); } static void data_buffer_overflow_evt(struct timeval *tv, uint16_t index, const void *data, uint8_t size) { const struct bt_hci_evt_data_buffer_overflow *evt = data; print_link_type(evt->link_type); } static void max_slots_change_evt(struct timeval *tv, uint16_t index, const void *data, uint8_t size) { const struct bt_hci_evt_max_slots_change *evt = data; print_handle(evt->handle); print_field("Max slots: %d", evt->max_slots); } static void clock_offset_complete_evt(struct timeval *tv, uint16_t index, const void *data, uint8_t size) { const struct bt_hci_evt_clock_offset_complete *evt = data; print_status(evt->status); print_handle(evt->handle); print_clock_offset(evt->clock_offset); } static void conn_pkt_type_changed_evt(struct timeval *tv, uint16_t index, const void *data, uint8_t size) { const struct bt_hci_evt_conn_pkt_type_changed *evt = data; print_status(evt->status); print_handle(evt->handle); print_pkt_type(evt->pkt_type); } static void qos_violation_evt(struct timeval *tv, uint16_t index, const void *data, uint8_t size) { const struct bt_hci_evt_qos_violation *evt = data; print_handle(evt->handle); } static void pscan_mode_change_evt(struct timeval *tv, uint16_t index, const void *data, uint8_t size) { const struct bt_hci_evt_pscan_mode_change *evt = data; print_bdaddr(evt->bdaddr); print_pscan_mode(evt->pscan_mode); } static void pscan_rep_mode_change_evt(struct timeval *tv, uint16_t index, const void *data, uint8_t size) { const struct bt_hci_evt_pscan_rep_mode_change *evt = data; print_bdaddr(evt->bdaddr); print_pscan_rep_mode(evt->pscan_rep_mode); } static void flow_spec_complete_evt(struct timeval *tv, uint16_t index, const void *data, uint8_t size) { const struct bt_hci_evt_flow_spec_complete *evt = data; print_status(evt->status); print_handle(evt->handle); print_field("Flags: 0x%2.2x", evt->flags); print_flow_direction(evt->direction); print_service_type(evt->service_type); print_field("Token rate: %d", le32_to_cpu(evt->token_rate)); print_field("Token bucket size: %d", le32_to_cpu(evt->token_bucket_size)); print_field("Peak bandwidth: %d", le32_to_cpu(evt->peak_bandwidth)); print_field("Access latency: %d", le32_to_cpu(evt->access_latency)); } static void inquiry_result_with_rssi_evt(struct timeval *tv, uint16_t index, const void *data, uint8_t size) { const struct bt_hci_evt_inquiry_result_with_rssi *evt = data; print_num_resp(evt->num_resp); print_bdaddr(evt->bdaddr); print_pscan_rep_mode(evt->pscan_rep_mode); print_pscan_period_mode(evt->pscan_period_mode); print_dev_class(evt->dev_class); print_clock_offset(evt->clock_offset); print_rssi(evt->rssi); if (size > sizeof(*evt)) packet_hexdump(data + sizeof(*evt), size - sizeof(*evt)); } static void remote_ext_features_complete_evt(struct timeval *tv, uint16_t index, const void *data, uint8_t size) { const struct bt_hci_evt_remote_ext_features_complete *evt = data; print_status(evt->status); print_handle(evt->handle); print_field("Page: %d/%d", evt->page, evt->max_page); print_features(evt->page, evt->features, 0x00); } static void sync_conn_complete_evt(struct timeval *tv, uint16_t index, const void *data, uint8_t size) { const struct bt_hci_evt_sync_conn_complete *evt = data; print_status(evt->status); print_field("Handle: %d", le16_to_cpu(evt->handle)); print_bdaddr(evt->bdaddr); print_link_type(evt->link_type); print_field("Transmission interval: 0x%2.2x", evt->tx_interval); print_field("Retransmission window: 0x%2.2x", evt->retrans_window); print_field("RX packet length: %d", le16_to_cpu(evt->rx_pkt_len)); print_field("TX packet length: %d", le16_to_cpu(evt->tx_pkt_len)); print_air_mode(evt->air_mode); if (evt->status == 0x00) assign_handle(index, le16_to_cpu(evt->handle), evt->link_type, (void *)evt->bdaddr, BDADDR_BREDR); } static void sync_conn_changed_evt(struct timeval *tv, uint16_t index, const void *data, uint8_t size) { const struct bt_hci_evt_sync_conn_changed *evt = data; print_status(evt->status); print_handle(evt->handle); print_field("Transmission interval: 0x%2.2x", evt->tx_interval); print_field("Retransmission window: 0x%2.2x", evt->retrans_window); print_field("RX packet length: %d", le16_to_cpu(evt->rx_pkt_len)); print_field("TX packet length: %d", le16_to_cpu(evt->tx_pkt_len)); } static void sniff_subrating_evt(struct timeval *tv, uint16_t index, const void *data, uint8_t size) { const struct bt_hci_evt_sniff_subrating *evt = data; print_status(evt->status); print_handle(evt->handle); print_slot_625("Max transmit latency", evt->max_tx_latency); print_slot_625("Max receive latency", evt->max_rx_latency); print_slot_625("Min remote timeout", evt->min_remote_timeout); print_slot_625("Min local timeout", evt->min_local_timeout); } static void ext_inquiry_result_evt(struct timeval *tv, uint16_t index, const void *data, uint8_t size) { const struct bt_hci_evt_ext_inquiry_result *evt = data; print_num_resp(evt->num_resp); print_bdaddr(evt->bdaddr); print_pscan_rep_mode(evt->pscan_rep_mode); print_pscan_period_mode(evt->pscan_period_mode); print_dev_class(evt->dev_class); print_clock_offset(evt->clock_offset); print_rssi(evt->rssi); print_eir(evt->data, sizeof(evt->data), false); } static void encrypt_key_refresh_complete_evt(struct timeval *tv, uint16_t index, const void *data, uint8_t size) { const struct bt_hci_evt_encrypt_key_refresh_complete *evt = data; print_status(evt->status); print_handle(evt->handle); } static void io_capability_request_evt(struct timeval *tv, uint16_t index, const void *data, uint8_t size) { const struct bt_hci_evt_io_capability_request *evt = data; print_bdaddr(evt->bdaddr); } static void io_capability_response_evt(struct timeval *tv, uint16_t index, const void *data, uint8_t size) { const struct bt_hci_evt_io_capability_response *evt = data; print_bdaddr(evt->bdaddr); print_io_capability(evt->capability); print_oob_data_response(evt->oob_data); print_authentication(evt->authentication); } static void user_confirm_request_evt(struct timeval *tv, uint16_t index, const void *data, uint8_t size) { const struct bt_hci_evt_user_confirm_request *evt = data; print_bdaddr(evt->bdaddr); print_passkey(evt->passkey); } static void user_passkey_request_evt(struct timeval *tv, uint16_t index, const void *data, uint8_t size) { const struct bt_hci_evt_user_passkey_request *evt = data; print_bdaddr(evt->bdaddr); } static void remote_oob_data_request_evt(struct timeval *tv, uint16_t index, const void *data, uint8_t size) { const struct bt_hci_evt_remote_oob_data_request *evt = data; print_bdaddr(evt->bdaddr); } static void simple_pairing_complete_evt(struct timeval *tv, uint16_t index, const void *data, uint8_t size) { const struct bt_hci_evt_simple_pairing_complete *evt = data; print_status(evt->status); print_bdaddr(evt->bdaddr); } static void link_supv_timeout_changed_evt(struct timeval *tv, uint16_t index, const void *data, uint8_t size) { const struct bt_hci_evt_link_supv_timeout_changed *evt = data; print_handle(evt->handle); print_timeout(evt->timeout); } static void enhanced_flush_complete_evt(struct timeval *tv, uint16_t index, const void *data, uint8_t size) { const struct bt_hci_evt_enhanced_flush_complete *evt = data; print_handle(evt->handle); } static void user_passkey_notify_evt(struct timeval *tv, uint16_t index, const void *data, uint8_t size) { const struct bt_hci_evt_user_passkey_notify *evt = data; print_bdaddr(evt->bdaddr); print_passkey(evt->passkey); } static void keypress_notify_evt(struct timeval *tv, uint16_t index, const void *data, uint8_t size) { const struct bt_hci_evt_keypress_notify *evt = data; const char *str; print_bdaddr(evt->bdaddr); switch (evt->type) { case 0x00: str = "Passkey entry started"; break; case 0x01: str = "Passkey digit entered"; break; case 0x02: str = "Passkey digit erased"; break; case 0x03: str = "Passkey clared"; break; case 0x04: str = "Passkey entry completed"; break; default: str = "Reserved"; break; } print_field("Notification type: %s (0x%2.2x)", str, evt->type); } static void remote_host_features_notify_evt(struct timeval *tv, uint16_t index, const void *data, uint8_t size) { const struct bt_hci_evt_remote_host_features_notify *evt = data; print_bdaddr(evt->bdaddr); print_features(1, evt->features, 0x00); } static void phy_link_complete_evt(struct timeval *tv, uint16_t index, const void *data, uint8_t size) { const struct bt_hci_evt_phy_link_complete *evt = data; print_status(evt->status); print_phy_handle(evt->phy_handle); } static void channel_selected_evt(struct timeval *tv, uint16_t index, const void *data, uint8_t size) { const struct bt_hci_evt_channel_selected *evt = data; print_phy_handle(evt->phy_handle); } static void disconn_phy_link_complete_evt(struct timeval *tv, uint16_t index, const void *data, uint8_t size) { const struct bt_hci_evt_disconn_phy_link_complete *evt = data; print_status(evt->status); print_phy_handle(evt->phy_handle); print_reason(evt->reason); } static void phy_link_loss_early_warning_evt(struct timeval *tv, uint16_t index, const void *data, uint8_t size) { const struct bt_hci_evt_phy_link_loss_early_warning *evt = data; const char *str; print_phy_handle(evt->phy_handle); switch (evt->reason) { case 0x00: str = "Unknown"; break; case 0x01: str = "Range related"; break; case 0x02: str = "Bandwidth related"; break; case 0x03: str = "Resolving conflict"; break; case 0x04: str = "Interference"; break; default: str = "Reserved"; break; } print_field("Reason: %s (0x%2.2x)", str, evt->reason); } static void phy_link_recovery_evt(struct timeval *tv, uint16_t index, const void *data, uint8_t size) { const struct bt_hci_evt_phy_link_recovery *evt = data; print_phy_handle(evt->phy_handle); } static void logic_link_complete_evt(struct timeval *tv, uint16_t index, const void *data, uint8_t size) { const struct bt_hci_evt_logic_link_complete *evt = data; print_status(evt->status); print_handle(evt->handle); print_phy_handle(evt->phy_handle); print_field("TX flow spec: 0x%2.2x", evt->flow_spec); } static void disconn_logic_link_complete_evt(struct timeval *tv, uint16_t index, const void *data, uint8_t size) { const struct bt_hci_evt_disconn_logic_link_complete *evt = data; print_status(evt->status); print_handle(evt->handle); print_reason(evt->reason); } static void flow_spec_modify_complete_evt(struct timeval *tv, uint16_t index, const void *data, uint8_t size) { const struct bt_hci_evt_flow_spec_modify_complete *evt = data; print_status(evt->status); print_handle(evt->handle); } static void num_completed_data_blocks_evt(struct timeval *tv, uint16_t index, const void *data, uint8_t size) { const struct bt_hci_evt_num_completed_data_blocks *evt = data; print_field("Total num data blocks: %d", le16_to_cpu(evt->total_num_blocks)); print_field("Num handles: %d", evt->num_handles); print_handle(evt->handle); print_field("Num packets: %d", evt->num_packets); print_field("Num blocks: %d", evt->num_blocks); if (size > sizeof(*evt)) packet_hexdump(data + sizeof(*evt), size - sizeof(*evt)); } static void short_range_mode_change_evt(struct timeval *tv, uint16_t index, const void *data, uint8_t size) { const struct bt_hci_evt_short_range_mode_change *evt = data; print_status(evt->status); print_phy_handle(evt->phy_handle); print_enable("Short range mode", evt->mode); } static void amp_status_change_evt(struct timeval *tv, uint16_t index, const void *data, uint8_t size) { const struct bt_hci_evt_amp_status_change *evt = data; print_status(evt->status); print_amp_status(evt->amp_status); } static void triggered_clock_capture_evt(struct timeval *tv, uint16_t index, const void *data, uint8_t size) { const struct bt_hci_evt_triggered_clock_capture *evt = data; print_handle(evt->handle); print_clock_type(evt->type); print_clock(evt->clock); print_clock_offset(evt->clock_offset); } static void sync_train_complete_evt(struct timeval *tv, uint16_t index, const void *data, uint8_t size) { const struct bt_hci_evt_sync_train_complete *evt = data; print_status(evt->status); } static void sync_train_received_evt(struct timeval *tv, uint16_t index, const void *data, uint8_t size) { const struct bt_hci_evt_sync_train_received *evt = data; print_status(evt->status); print_bdaddr(evt->bdaddr); print_field("Offset: 0x%8.8x", le32_to_cpu(evt->offset)); print_channel_map(evt->map); print_lt_addr(evt->lt_addr); print_field("Next broadcast instant: 0x%4.4x", le16_to_cpu(evt->instant)); print_interval(evt->interval); print_field("Service Data: 0x%2.2x", evt->service_data); } static void peripheral_broadcast_receive_evt(struct timeval *tv, uint16_t index, const void *data, uint8_t size) { const struct bt_hci_evt_peripheral_broadcast_receive *evt = data; print_bdaddr(evt->bdaddr); print_lt_addr(evt->lt_addr); print_field("Clock: 0x%8.8x", le32_to_cpu(evt->clock)); print_field("Offset: 0x%8.8x", le32_to_cpu(evt->offset)); print_field("Receive status: 0x%2.2x", evt->status); print_broadcast_fragment(evt->fragment); print_field("Length: %d", evt->length); if (size - 18 != evt->length) print_text(COLOR_ERROR, "invalid data size (%d != %d)", size - 18, evt->length); if (evt->lt_addr == 0x01 && evt->length == 17) print_3d_broadcast(data + 18, size - 18); else packet_hexdump(data + 18, size - 18); } static void peripheral_broadcast_timeout_evt(struct timeval *tv, uint16_t index, const void *data, uint8_t size) { const struct bt_hci_evt_peripheral_broadcast_timeout *evt = data; print_bdaddr(evt->bdaddr); print_lt_addr(evt->lt_addr); } static void truncated_page_complete_evt(struct timeval *tv, uint16_t index, const void *data, uint8_t size) { const struct bt_hci_evt_truncated_page_complete *evt = data; print_status(evt->status); print_bdaddr(evt->bdaddr); } static void peripheral_page_response_timeout_evt(struct timeval *tv, uint16_t index, const void *data, uint8_t size) { } static void channel_map_change_evt(struct timeval *tv, uint16_t index, const void *data, uint8_t size) { const struct bt_hci_evt_channel_map_change *evt = data; print_channel_map(evt->map); } static void inquiry_response_notify_evt(struct timeval *tv, uint16_t index, const void *data, uint8_t size) { const struct bt_hci_evt_inquiry_response_notify *evt = data; print_iac(evt->lap); print_rssi(evt->rssi); } static void auth_payload_timeout_expired_evt(struct timeval *tv, uint16_t index, const void *data, uint8_t size) { const struct bt_hci_evt_auth_payload_timeout_expired *evt = data; print_handle(evt->handle); } static void le_conn_complete_evt(struct timeval *tv, uint16_t index, const void *data, uint8_t size) { const struct bt_hci_evt_le_conn_complete *evt = data; print_status(evt->status); print_field("Handle: %d", le16_to_cpu(evt->handle)); print_role(evt->role); print_peer_addr_type("Peer address type", evt->peer_addr_type); print_addr("Peer address", evt->peer_addr, evt->peer_addr_type); print_slot_125("Connection interval", evt->interval); print_conn_latency("Connection latency", evt->latency); print_field("Supervision timeout: %d msec (0x%4.4x)", le16_to_cpu(evt->supv_timeout) * 10, le16_to_cpu(evt->supv_timeout)); print_field("Central clock accuracy: 0x%2.2x", evt->clock_accuracy); if (evt->status == 0x00) assign_handle(index, le16_to_cpu(evt->handle), 0x01, (void *)evt->peer_addr, evt->peer_addr_type); } static void le_adv_report_evt(struct timeval *tv, uint16_t index, const void *data, uint8_t size) { const struct bt_hci_evt_le_adv_report *evt = data; uint8_t evt_len; int8_t *rssi; print_num_reports(evt->num_reports); report: print_adv_event_type("Event type", evt->event_type); print_peer_addr_type("Address type", evt->addr_type); print_addr("Address", evt->addr, evt->addr_type); print_field("Data length: %d", evt->data_len); print_eir(evt->data, evt->data_len, true); rssi = (int8_t *) (evt->data + evt->data_len); print_rssi(*rssi); evt_len = sizeof(*evt) + evt->data_len + 1; if (size > evt_len) { data += evt_len - 1; size -= evt_len - 1; evt = data; goto report; } } static void le_conn_update_complete_evt(struct timeval *tv, uint16_t index, const void *data, uint8_t size) { const struct bt_hci_evt_le_conn_update_complete *evt = data; print_status(evt->status); print_handle(evt->handle); print_slot_125("Connection interval", evt->interval); print_conn_latency("Connection latency", evt->latency); print_field("Supervision timeout: %d msec (0x%4.4x)", le16_to_cpu(evt->supv_timeout) * 10, le16_to_cpu(evt->supv_timeout)); } static void le_remote_features_complete_evt(struct timeval *tv, uint16_t index, const void *data, uint8_t size) { const struct bt_hci_evt_le_remote_features_complete *evt = data; print_status(evt->status); print_handle(evt->handle); print_features(0, evt->features, 0x01); } static void le_long_term_key_request_evt(struct timeval *tv, uint16_t index, const void *data, uint8_t size) { const struct bt_hci_evt_le_long_term_key_request *evt = data; print_handle(evt->handle); print_random_number(evt->rand); print_encrypted_diversifier(evt->ediv); } static void le_conn_param_request_evt(struct timeval *tv, uint16_t index, const void *data, uint8_t size) { const struct bt_hci_evt_le_conn_param_request *evt = data; print_handle(evt->handle); print_slot_125("Min connection interval", evt->min_interval); print_slot_125("Max connection interval", evt->max_interval); print_conn_latency("Connection latency", evt->latency); print_field("Supervision timeout: %d msec (0x%4.4x)", le16_to_cpu(evt->supv_timeout) * 10, le16_to_cpu(evt->supv_timeout)); } static void le_data_length_change_evt(struct timeval *tv, uint16_t index, const void *data, uint8_t size) { const struct bt_hci_evt_le_data_length_change *evt = data; print_handle(evt->handle); print_field("Max TX octets: %d", le16_to_cpu(evt->max_tx_len)); print_field("Max TX time: %d", le16_to_cpu(evt->max_tx_time)); print_field("Max RX octets: %d", le16_to_cpu(evt->max_rx_len)); print_field("Max RX time: %d", le16_to_cpu(evt->max_rx_time)); } static void le_read_local_pk256_complete_evt(struct timeval *tv, uint16_t index, const void *data, uint8_t size) { const struct bt_hci_evt_le_read_local_pk256_complete *evt = data; print_status(evt->status); print_pk256("Local P-256 public key", evt->local_pk256); } static void le_generate_dhkey_complete_evt(struct timeval *tv, uint16_t index, const void *data, uint8_t size) { const struct bt_hci_evt_le_generate_dhkey_complete *evt = data; print_status(evt->status); print_dhkey(evt->dhkey); } static void le_enhanced_conn_complete_evt(struct timeval *tv, uint16_t index, const void *data, uint8_t size) { const struct bt_hci_evt_le_enhanced_conn_complete *evt = data; print_status(evt->status); print_field("Handle: %d", le16_to_cpu(evt->handle)); print_role(evt->role); print_peer_addr_type("Peer address type", evt->peer_addr_type); print_addr("Peer address", evt->peer_addr, evt->peer_addr_type); print_addr("Local resolvable private address", evt->local_rpa, 0x01); print_addr("Peer resolvable private address", evt->peer_rpa, 0x01); print_slot_125("Connection interval", evt->interval); print_conn_latency("Connection latency", evt->latency); print_field("Supervision timeout: %d msec (0x%4.4x)", le16_to_cpu(evt->supv_timeout) * 10, le16_to_cpu(evt->supv_timeout)); print_field("Central clock accuracy: 0x%2.2x", evt->clock_accuracy); if (evt->status == 0x00) assign_handle(index, le16_to_cpu(evt->handle), 0x01, (void *)evt->peer_addr, evt->peer_addr_type); } static void le_direct_adv_report_evt(struct timeval *tv, uint16_t index, const void *data, uint8_t size) { const struct bt_hci_evt_le_direct_adv_report *evt = data; print_num_reports(evt->num_reports); print_adv_event_type("Event type", evt->event_type); print_peer_addr_type("Address type", evt->addr_type); print_addr("Address", evt->addr, evt->addr_type); print_addr_type("Direct address type", evt->direct_addr_type); print_addr("Direct address", evt->direct_addr, evt->direct_addr_type); print_rssi(evt->rssi); if (size > sizeof(*evt)) packet_hexdump(data + sizeof(*evt), size - sizeof(*evt)); } static void le_phy_update_complete_evt(struct timeval *tv, uint16_t index, const void *data, uint8_t size) { const struct bt_hci_evt_le_phy_update_complete *evt = data; print_status(evt->status); print_handle(evt->handle); print_le_phy("TX PHY", evt->tx_phy); print_le_phy("RX PHY", evt->rx_phy); } static const struct bitfield_data ext_adv_report_evt_type[] = { { 0, "Connectable" }, { 1, "Scannable" }, { 2, "Directed" }, { 3, "Scan response" }, { 4, "Use legacy advertising PDUs" }, { } }; static void print_ext_adv_report_evt_type(const char *indent, uint16_t flags) { uint16_t mask = flags; uint16_t props = flags; uint8_t data_status; const char *str; const char *color_on; int i; print_field("%sEvent type: 0x%4.4x", indent, flags); props &= 0x1f; print_field("%s Props: 0x%4.4x", indent, props); for (i = 0; ext_adv_report_evt_type[i].str; i++) { if (flags & (1 << ext_adv_report_evt_type[i].bit)) { print_field("%s %s", indent, ext_adv_report_evt_type[i].str); mask &= ~(1 << ext_adv_report_evt_type[i].bit); } } data_status = (flags >> 5) & 3; mask &= ~(data_status << 5); switch (data_status) { case 0x00: str = "Complete"; color_on = COLOR_GREEN; break; case 0x01: str = "Incomplete, more data to come"; color_on = COLOR_YELLOW; break; case 0x02: str = "Incomplete, data truncated, no more to come"; color_on = COLOR_RED; break; default: str = "Reserved"; color_on = COLOR_RED; break; } print_field("%s Data status: %s%s%s", indent, color_on, str, COLOR_OFF); if (mask) print_text(COLOR_UNKNOWN_ADV_FLAG, "%s Reserved (0x%4.4x)", indent, mask); } static void print_legacy_adv_report_pdu(uint16_t flags) { const char *str; if (!(flags & (1 << 4))) return; switch (flags) { case 0x10: str = "ADV_NONCONN_IND"; break; case 0x12: str = "ADV_SCAN_IND"; break; case 0x13: str = "ADV_IND"; break; case 0x15: str = "ADV_DIRECT_IND"; break; case 0x1a: str = "SCAN_RSP to an ADV_IND"; break; case 0x1b: str = "SCAN_RSP to an ADV_SCAN_IND"; break; default: str = "Reserved"; break; } print_field(" Legacy PDU Type: %s (0x%4.4x)", str, flags); } static void le_ext_adv_report_evt(struct timeval *tv, uint16_t index, const void *data, uint8_t size) { const struct bt_hci_evt_le_ext_adv_report *evt = data; const struct bt_hci_le_ext_adv_report *report; const char *str; int i; print_num_reports(evt->num_reports); data += sizeof(evt->num_reports); for (i = 0; i < evt->num_reports; ++i) { report = data; print_field("Entry %d", i); print_ext_adv_report_evt_type(" ", report->event_type); print_legacy_adv_report_pdu(report->event_type); print_peer_addr_type(" Address type", report->addr_type); print_addr(" Address", report->addr, report->addr_type); switch (report->primary_phy) { case 0x01: str = "LE 1M"; break; case 0x03: str = "LE Coded"; break; default: str = "Reserved"; break; } print_field(" Primary PHY: %s", str); switch (report->secondary_phy) { case 0x00: str = "No packets"; break; case 0x01: str = "LE 1M"; break; case 0x02: str = "LE 2M"; break; case 0x03: str = "LE Coded"; break; default: str = "Reserved"; break; } print_field(" Secondary PHY: %s", str); if (report->sid == 0xff) print_field(" SID: no ADI field (0x%2.2x)", report->sid); else if (report->sid > 0x0f) print_field(" SID: Reserved (0x%2.2x)", report->sid); else print_field(" SID: 0x%2.2x", report->sid); print_field(" TX power: %d dBm", report->tx_power); if (report->rssi == 127) print_field(" RSSI: not available (0x%2.2x)", (uint8_t) report->rssi); else if (report->rssi >= -127 && report->rssi <= 20) print_field(" RSSI: %d dBm (0x%2.2x)", report->rssi, (uint8_t) report->rssi); else print_field(" RSSI: reserved (0x%2.2x)", (uint8_t) report->rssi); print_slot_125(" Periodic advertising interval", report->interval); print_peer_addr_type(" Direct address type", report->direct_addr_type); print_addr(" Direct address", report->direct_addr, report->direct_addr_type); print_field(" Data length: 0x%2.2x", report->data_len); data += sizeof(struct bt_hci_le_ext_adv_report); packet_hexdump(data, report->data_len); print_eir(data, report->data_len, true); data += report->data_len; } } static void le_pa_sync_evt(struct timeval *tv, uint16_t index, const void *data, uint8_t size) { const struct bt_hci_evt_le_per_sync_established *evt = data; print_status(evt->status); print_field("Sync handle: %d", evt->handle); if (evt->sid > 0x0f) print_field("Advertising SID: Reserved (0x%2.2x)", evt->sid); else print_field("Advertising SID: 0x%2.2x", evt->sid); print_peer_addr_type("Advertiser address type", evt->addr_type); print_addr("Advertiser address", evt->addr, evt->addr_type); print_le_phy("Advertiser PHY", evt->phy); print_slot_125("Periodic advertising interval", evt->interval); print_field("Advertiser clock accuracy: 0x%2.2x", evt->clock_accuracy); } static void le_pa_report_evt(struct timeval *tv, uint16_t index, const void *data, uint8_t size) { const struct bt_hci_le_pa_report *evt = data; const char *color_on; const char *str; print_field("Sync handle: %d", evt->handle); print_power_level(evt->tx_power, NULL); if (evt->rssi == 127) print_field("RSSI: not available (0x%2.2x)", (uint8_t) evt->rssi); else if (evt->rssi >= -127 && evt->rssi <= 20) print_field("RSSI: %d dBm (0x%2.2x)", evt->rssi, (uint8_t) evt->rssi); else print_field("RSSI: reserved (0x%2.2x)", (uint8_t) evt->rssi); switch (evt->cte_type) { case 0x00: str = "AoA Constant Tone Extension"; break; case 0x01: str = "AoA Constant Tone Extension with 1us slots"; break; case 0x02: str = "AoD Constant Tone Extension with 2us slots"; break; case 0xff: str = "No Constant Tone Extension"; break; default: str = "Reserved"; break; } print_field("CTE Type: %s (0x%2x)", str, evt->cte_type); switch (evt->data_status) { case 0x00: str = "Complete"; color_on = COLOR_GREEN; break; case 0x01: str = "Incomplete, more data to come"; color_on = COLOR_YELLOW; break; case 0x02: str = "Incomplete, data truncated, no more to come"; color_on = COLOR_RED; break; default: str = "Reserved"; color_on = COLOR_RED; break; } print_field("Data status: %s%s%s", color_on, str, COLOR_OFF); print_field("Data length: 0x%2.2x", evt->data_len); print_eir(evt->data, evt->data_len, true); } static void le_pa_sync_lost_evt(struct timeval *tv, uint16_t index, const void *data, uint8_t size) { const struct bt_hci_evt_le_per_sync_lost *evt = data; print_field("Sync handle: %d", evt->handle); } static void le_adv_set_term_evt(struct timeval *tv, uint16_t index, const void *data, uint8_t size) { const struct bt_hci_evt_le_adv_set_term *evt = data; print_status(evt->status); print_field("Handle: %d", evt->handle); print_field("Connection handle: %d", evt->conn_handle); print_field("Number of completed extended advertising events: %d", evt->num_evts); } static void le_scan_req_received_evt(struct timeval *tv, uint16_t index, const void *data, uint8_t size) { const struct bt_hci_evt_le_scan_req_received *evt = data; print_field("Handle: %d", evt->handle); print_peer_addr_type("Scanner address type", evt->scanner_addr_type); print_addr("Scanner address", evt->scanner_addr, evt->scanner_addr_type); } static void le_chan_select_alg_evt(struct timeval *tv, uint16_t index, const void *data, uint8_t size) { const struct bt_hci_evt_le_chan_select_alg *evt = data; const char *str; print_handle(evt->handle); switch (evt->algorithm) { case 0x00: str = "#1"; break; case 0x01: str = "#2"; break; default: str = "Reserved"; break; } print_field("Algorithm: %s (0x%2.2x)", str, evt->algorithm); } static void le_cte_request_failed_evt(struct timeval *tv, uint16_t index, const void *data, uint8_t size) { const struct bt_hci_evt_le_cte_request_failed *evt = data; print_status(evt->status); print_field("Connection handle: %d", evt->handle); } static void le_pa_sync_trans_rec_evt(struct timeval *tv, uint16_t index, const void *data, uint8_t size) { const struct bt_hci_evt_le_pa_sync_trans_rec *evt = data; print_status(evt->status); print_field("Handle: %d", evt->handle); print_field("Connection handle: %d", evt->handle); print_field("Service data: 0x%4.4x", evt->service_data); print_field("Sync handle: %d", evt->sync_handle); print_field("SID: 0x%2.2x", evt->sid); print_peer_addr_type("Address type:", evt->addr_type); print_addr("Address:", evt->addr, evt->addr_type); print_le_phy("PHY:", evt->phy); print_field("Periodic advertising Interval: %.3f", 1.25 * evt->interval); print_clock_accuracy(evt->clock_accuracy); } static void le_cis_established_evt(struct timeval *tv, uint16_t index, const void *data, uint8_t size) { const struct bt_hci_evt_le_cis_established *evt = data; print_status(evt->status); print_field("Connection Handle: %d", le16_to_cpu(evt->conn_handle)); print_usec_interval("CIG Synchronization Delay", evt->cig_sync_delay); print_usec_interval("CIS Synchronization Delay", evt->cis_sync_delay); print_usec_interval("Central to Peripheral Latency", evt->c_latency); print_usec_interval("Peripheral to Central Latency", evt->p_latency); print_le_phy("Central to Peripheral PHY", evt->c_phy); print_le_phy("Peripheral to Central PHY", evt->p_phy); print_field("Number of Subevents: %u", evt->nse); print_field("Central to Peripheral Burst Number: %u", evt->c_bn); print_field("Peripheral to Central Burst Number: %u", evt->p_bn); print_field("Central to Peripheral Flush Timeout: %u", evt->c_ft); print_field("Peripheral to Central Flush Timeout: %u", evt->p_ft); print_field("Central to Peripheral MTU: %u", le16_to_cpu(evt->c_mtu)); print_field("Peripheral to Central MTU: %u", le16_to_cpu(evt->p_mtu)); print_slot_125("ISO Interval", evt->interval); if (!evt->status) assign_handle(index, le16_to_cpu(evt->conn_handle), 0x05, NULL, BDADDR_LE_PUBLIC); } static void le_req_cis_evt(struct timeval *tv, uint16_t index, const void *data, uint8_t size) { const struct bt_hci_evt_le_cis_req *evt = data; struct packet_conn_data *conn; print_field("ACL Handle: %d", le16_to_cpu(evt->acl_handle)); print_field("CIS Handle: %d", le16_to_cpu(evt->cis_handle)); print_field("CIG ID: 0x%2.2x", evt->cig_id); print_field("CIS ID: 0x%2.2x", evt->cis_id); conn = packet_get_conn_data(evt->acl_handle); if (conn) conn->link = evt->cis_handle; } static void print_bis_handle(const void *data, int i) { uint16_t handle = get_le16(data); print_field("Connection Handle #%d: %d", i, handle); } static void le_big_complete_evt(struct timeval *tv, uint16_t index, const void *data, uint8_t size) { const struct bt_hci_evt_le_big_complete *evt = data; print_status(evt->status); print_field("Handle: 0x%2.2x", evt->handle); print_usec_interval("BIG Synchronization Delay", evt->sync_delay); print_usec_interval("Transport Latency", evt->latency); print_le_phy("PHY", evt->phy); print_field("NSE: %u", evt->nse); print_field("BN: %u", evt->bn); print_field("PTO: %u", evt->pto); print_field("IRC: %u", evt->irc); print_field("Maximum PDU: %u", evt->max_pdu); print_slot_125("ISO Interval", evt->interval); print_list(evt->bis_handle, size, evt->num_bis, sizeof(*evt->bis_handle), print_bis_handle); if (!evt->status) { int i; for (i = 0; i < evt->num_bis; i++) assign_handle(index, le16_to_cpu(evt->bis_handle[i]), 0x05, NULL, BDADDR_LE_PUBLIC); } } static void le_big_terminate_evt(struct timeval *tv, uint16_t index, const void *data, uint8_t size) { const struct bt_hci_evt_le_big_terminate *evt = data; print_field("BIG Handle: 0x%2.2x", evt->handle); print_reason(evt->reason); } static void le_big_sync_estabilished_evt(struct timeval *tv, uint16_t index, const void *data, uint8_t size) { const struct bt_hci_evt_le_big_sync_estabilished *evt = data; print_status(evt->status); print_field("BIG Handle: 0x%2.2x", evt->handle); print_usec_interval("Transport Latency", evt->latency); print_field("NSE: %u", evt->nse); print_field("BN: %u", evt->bn); print_field("PTO: %u", evt->bn); print_field("IRC: %u", evt->irc); print_field("Maximum PDU: %u", evt->max_pdu); print_slot_125("ISO Interval", evt->interval); print_list(evt->bis, size, evt->num_bis, sizeof(*evt->bis), print_bis_handle); if (!evt->status) { int i; for (i = 0; i < evt->num_bis; i++) assign_handle(index, le16_to_cpu(evt->bis[i]), 0x05, NULL, BDADDR_LE_PUBLIC); } } static void le_big_sync_lost_evt(struct timeval *tv, uint16_t index, const void *data, uint8_t size) { const struct bt_hci_evt_le_big_sync_lost *evt = data; print_field("BIG Handle: 0x%2.2x", evt->big_handle); print_reason(evt->reason); } static void le_req_sca_complete_evt(struct timeval *tv, uint16_t index, const void *data, uint8_t size) { const struct bt_hci_evt_le_req_peer_sca_complete *evt = data; print_status(evt->status); print_field("Connection Handle: %d", le16_to_cpu(evt->handle)); print_sca(evt->sca); } static void le_big_info_evt(struct timeval *tv, uint16_t index, const void *data, uint8_t size) { const struct bt_hci_evt_le_big_info_adv_report *evt = data; print_field("Sync Handle: 0x%4.4x", evt->sync_handle); print_field("Number BIS: %u", evt->num_bis); print_field("NSE: %u", evt->nse); print_slot_125("ISO Interval", evt->iso_interval); print_field("BN: %u", evt->bn); print_field("PTO: %u", evt->bn); print_field("IRC: %u", evt->irc); print_field("Maximum PDU: %u", evt->max_pdu); print_usec_interval("SDU Interval", evt->sdu_interval); print_field("Maximum SDU: %u", evt->max_sdu); print_le_phy("PHY", evt->phy); print_framing(evt->framing); print_field("Encryption: 0x%02x", evt->encryption); } struct subevent_data { uint8_t subevent; const char *str; void (*func) (struct timeval *tv, uint16_t index, const void *data, uint8_t size); uint8_t size; bool fixed; }; static void print_subevent(struct timeval *tv, uint16_t index, const struct subevent_data *subevent_data, const void *data, uint8_t size) { const char *subevent_color; if (subevent_data->func) subevent_color = COLOR_HCI_EVENT; else subevent_color = COLOR_HCI_EVENT_UNKNOWN; print_indent(6, subevent_color, "", subevent_data->str, COLOR_OFF, " (0x%2.2x)", subevent_data->subevent); if (!subevent_data->func) { packet_hexdump(data, size); return; } if (subevent_data->fixed) { if (size != subevent_data->size) { print_text(COLOR_ERROR, "invalid packet size"); packet_hexdump(data, size); return; } } else { if (size < subevent_data->size) { print_text(COLOR_ERROR, "too short packet"); packet_hexdump(data, size); return; } } subevent_data->func(tv, index, data, size); } static const struct subevent_data le_meta_event_table[] = { { 0x01, "LE Connection Complete", le_conn_complete_evt, 18, true }, { 0x02, "LE Advertising Report", le_adv_report_evt, 1, false }, { 0x03, "LE Connection Update Complete", le_conn_update_complete_evt, 9, true }, { 0x04, "LE Read Remote Used Features", le_remote_features_complete_evt, 11, true }, { 0x05, "LE Long Term Key Request", le_long_term_key_request_evt, 12, true }, { 0x06, "LE Remote Connection Parameter Request", le_conn_param_request_evt, 10, true }, { 0x07, "LE Data Length Change", le_data_length_change_evt, 10, true }, { 0x08, "LE Read Local P-256 Public Key Complete", le_read_local_pk256_complete_evt, 65, true }, { 0x09, "LE Generate DHKey Complete", le_generate_dhkey_complete_evt, 33, true }, { 0x0a, "LE Enhanced Connection Complete", le_enhanced_conn_complete_evt, 30, true }, { 0x0b, "LE Direct Advertising Report", le_direct_adv_report_evt, 1, false }, { 0x0c, "LE PHY Update Complete", le_phy_update_complete_evt, 5, true}, { 0x0d, "LE Extended Advertising Report", le_ext_adv_report_evt, 1, false}, { 0x0e, "LE Periodic Advertising Sync Established", le_pa_sync_evt, 15, true }, { 0x0f, "LE Periodic Advertising Report", le_pa_report_evt, 7, false}, { 0x10, "LE Periodic Advertising Sync Lost", le_pa_sync_lost_evt, 2, true}, { 0x11, "LE Scan Timeout" }, { 0x12, "LE Advertising Set Terminated", le_adv_set_term_evt, 5, true}, { 0x13, "LE Scan Request Received", le_scan_req_received_evt, 8, true}, { 0x14, "LE Channel Selection Algorithm", le_chan_select_alg_evt, 3, true}, { 0x17, "LE CTE Request Failed", le_cte_request_failed_evt, 3, true}, { 0x18, "LE Periodic Advertising Sync Transfer Received", le_pa_sync_trans_rec_evt, 19, true}, { BT_HCI_EVT_LE_CIS_ESTABLISHED, "LE Connected Isochronous Stream Established", le_cis_established_evt, sizeof(struct bt_hci_evt_le_cis_established), true }, { BT_HCI_EVT_LE_CIS_REQ, "LE Connected Isochronous Stream Request", le_req_cis_evt, sizeof(struct bt_hci_evt_le_cis_req), true }, { BT_HCI_EVT_LE_BIG_COMPLETE, "LE Broadcast Isochronous Group Complete", le_big_complete_evt, sizeof(struct bt_hci_evt_le_big_complete) }, { BT_HCI_EVT_LE_BIG_TERMINATE, "LE Broadcast Isochronous Group Terminate", le_big_terminate_evt, sizeof(struct bt_hci_evt_le_big_terminate) }, { BT_HCI_EVT_LE_BIG_SYNC_ESTABILISHED, "LE Broadcast Isochronous Group Sync " "Estabilished", le_big_sync_estabilished_evt, sizeof(struct bt_hci_evt_le_big_sync_lost) }, { BT_HCI_EVT_LE_BIG_SYNC_LOST, "LE Broadcast Isochronous Group Sync Lost", le_big_sync_lost_evt, sizeof(struct bt_hci_evt_le_big_sync_lost) }, { BT_HCI_EVT_LE_REQ_PEER_SCA_COMPLETE, "LE Request Peer SCA Complete", le_req_sca_complete_evt, sizeof( struct bt_hci_evt_le_req_peer_sca_complete)}, { BT_HCI_EVT_LE_BIG_INFO_ADV_REPORT, "LE Broadcast Isochronous Group Info Advertising Report", le_big_info_evt, sizeof(struct bt_hci_evt_le_big_info_adv_report) }, { } }; static void le_meta_event_evt(struct timeval *tv, uint16_t index, const void *data, uint8_t size) { uint8_t subevent = *((const uint8_t *) data); struct subevent_data unknown; const struct subevent_data *subevent_data = &unknown; int i; unknown.subevent = subevent; unknown.str = "Unknown"; unknown.func = NULL; unknown.size = 0; unknown.fixed = true; for (i = 0; le_meta_event_table[i].str; i++) { if (le_meta_event_table[i].subevent == subevent) { subevent_data = &le_meta_event_table[i]; break; } } print_subevent(tv, index, subevent_data, data + 1, size - 1); } static void vendor_evt(struct timeval *tv, uint16_t index, const void *data, uint8_t size) { struct subevent_data vendor_data; char vendor_str[150]; int consumed_size; const struct vendor_evt *vnd = current_vendor_evt(data, &consumed_size); if (vnd) { const char *str = current_vendor_evt_str(); if (str) { snprintf(vendor_str, sizeof(vendor_str), "%s %s", str, vnd->str); vendor_data.str = vendor_str; } else vendor_data.str = vnd->str; vendor_data.subevent = vnd->evt; vendor_data.func = vnd->evt_func; vendor_data.size = vnd->evt_size; vendor_data.fixed = vnd->evt_fixed; print_subevent(tv, index, &vendor_data, data + consumed_size, size - consumed_size); } else { uint16_t manufacturer; if (index_current < MAX_INDEX) manufacturer = index_list[index_current].manufacturer; else manufacturer = fallback_manufacturer; vendor_event(manufacturer, data, size); } } struct event_data { uint8_t event; const char *str; void (*func) (struct timeval *tv, uint16_t index, const void *data, uint8_t size); uint8_t size; bool fixed; }; static const struct event_data event_table[] = { { 0x01, "Inquiry Complete", inquiry_complete_evt, 1, true }, { 0x02, "Inquiry Result", inquiry_result_evt, 1, false }, { 0x03, "Connect Complete", conn_complete_evt, 11, true }, { 0x04, "Connect Request", conn_request_evt, 10, true }, { 0x05, "Disconnect Complete", disconnect_complete_evt, 4, true }, { 0x06, "Auth Complete", auth_complete_evt, 3, true }, { 0x07, "Remote Name Req Complete", remote_name_request_complete_evt, 255, true }, { 0x08, "Encryption Change", encrypt_change_evt, 4, true }, { 0x09, "Change Connection Link Key Complete", change_conn_link_key_complete_evt, 3, true }, { 0x0a, "Link Key Type Changed", link_key_type_changed_evt, 4, true }, { 0x0b, "Read Remote Supported Features", remote_features_complete_evt, 11, true }, { 0x0c, "Read Remote Version Complete", remote_version_complete_evt, 8, true }, { 0x0d, "QoS Setup Complete", qos_setup_complete_evt, 21, true }, { 0x0e, "Command Complete", cmd_complete_evt, 3, false }, { 0x0f, "Command Status", cmd_status_evt, 4, true }, { 0x10, "Hardware Error", hardware_error_evt, 1, true }, { 0x11, "Flush Occurred", flush_occurred_evt, 2, true }, { 0x12, "Role Change", role_change_evt, 8, true }, { 0x13, "Number of Completed Packets", num_completed_packets_evt, 1, false }, { 0x14, "Mode Change", mode_change_evt, 6, true }, { 0x15, "Return Link Keys", return_link_keys_evt, 1, false }, { 0x16, "PIN Code Request", pin_code_request_evt, 6, true }, { 0x17, "Link Key Request", link_key_request_evt, 6, true }, { 0x18, "Link Key Notification", link_key_notify_evt, 23, true }, { 0x19, "Loopback Command", loopback_command_evt, 3, false }, { 0x1a, "Data Buffer Overflow", data_buffer_overflow_evt, 1, true }, { 0x1b, "Max Slots Change", max_slots_change_evt, 3, true }, { 0x1c, "Read Clock Offset Complete", clock_offset_complete_evt, 5, true }, { 0x1d, "Connection Packet Type Changed", conn_pkt_type_changed_evt, 5, true }, { 0x1e, "QoS Violation", qos_violation_evt, 2, true }, { 0x1f, "Page Scan Mode Change", pscan_mode_change_evt, 7, true }, { 0x20, "Page Scan Repetition Mode Change", pscan_rep_mode_change_evt, 7, true }, { 0x21, "Flow Specification Complete", flow_spec_complete_evt, 22, true }, { 0x22, "Inquiry Result with RSSI", inquiry_result_with_rssi_evt, 1, false }, { 0x23, "Read Remote Extended Features", remote_ext_features_complete_evt, 13, true }, { 0x2c, "Synchronous Connect Complete", sync_conn_complete_evt, 17, true }, { 0x2d, "Synchronous Connect Changed", sync_conn_changed_evt, 9, true }, { 0x2e, "Sniff Subrating", sniff_subrating_evt, 11, true }, { 0x2f, "Extended Inquiry Result", ext_inquiry_result_evt, 1, false }, { 0x30, "Encryption Key Refresh Complete", encrypt_key_refresh_complete_evt, 3, true }, { 0x31, "IO Capability Request", io_capability_request_evt, 6, true }, { 0x32, "IO Capability Response", io_capability_response_evt, 9, true }, { 0x33, "User Confirmation Request", user_confirm_request_evt, 10, true }, { 0x34, "User Passkey Request", user_passkey_request_evt, 6, true }, { 0x35, "Remote OOB Data Request", remote_oob_data_request_evt, 6, true }, { 0x36, "Simple Pairing Complete", simple_pairing_complete_evt, 7, true }, { 0x38, "Link Supervision Timeout Changed", link_supv_timeout_changed_evt, 4, true }, { 0x39, "Enhanced Flush Complete", enhanced_flush_complete_evt, 2, true }, { 0x3b, "User Passkey Notification", user_passkey_notify_evt, 10, true }, { 0x3c, "Keypress Notification", keypress_notify_evt, 7, true }, { 0x3d, "Remote Host Supported Features", remote_host_features_notify_evt, 14, true }, { 0x3e, "LE Meta Event", le_meta_event_evt, 1, false }, { 0x40, "Physical Link Complete", phy_link_complete_evt, 2, true }, { 0x41, "Channel Selected", channel_selected_evt, 1, true }, { 0x42, "Disconnect Physical Link Complete", disconn_phy_link_complete_evt, 3, true }, { 0x43, "Physical Link Loss Early Warning", phy_link_loss_early_warning_evt, 2, true }, { 0x44, "Physical Link Recovery", phy_link_recovery_evt, 1, true }, { 0x45, "Logical Link Complete", logic_link_complete_evt, 5, true }, { 0x46, "Disconnect Logical Link Complete", disconn_logic_link_complete_evt, 4, true }, { 0x47, "Flow Specification Modify Complete", flow_spec_modify_complete_evt, 3, true }, { 0x48, "Number of Completed Data Blocks", num_completed_data_blocks_evt, 3, false }, { 0x49, "AMP Start Test" }, { 0x4a, "AMP Test End" }, { 0x4b, "AMP Receiver Report" }, { 0x4c, "Short Range Mode Change Complete", short_range_mode_change_evt, 3, true }, { 0x4d, "AMP Status Change", amp_status_change_evt, 2, true }, { 0x4e, "Triggered Clock Capture", triggered_clock_capture_evt, 9, true }, { 0x4f, "Synchronization Train Complete", sync_train_complete_evt, 1, true }, { 0x50, "Synchronization Train Received", sync_train_received_evt, 29, true }, { 0x51, "Connectionless Peripheral Broadcast Receive", peripheral_broadcast_receive_evt, 18, false }, { 0x52, "Connectionless Peripheral Broadcast Timeout", peripheral_broadcast_timeout_evt, 7, true }, { 0x53, "Truncated Page Complete", truncated_page_complete_evt, 7, true }, { 0x54, "Peripheral Page Response Timeout", peripheral_page_response_timeout_evt, 0, true }, { 0x55, "Connectionless Peripheral Broadcast Channel Map Change", channel_map_change_evt, 10, true }, { 0x56, "Inquiry Response Notification", inquiry_response_notify_evt, 4, true }, { 0x57, "Authenticated Payload Timeout Expired", auth_payload_timeout_expired_evt, 2, true }, { 0x58, "SAM Status Change" }, { 0xfe, "Testing" }, { 0xff, "Vendor", vendor_evt, 0, false }, { } }; void packet_new_index(struct timeval *tv, uint16_t index, const char *label, uint8_t type, uint8_t bus, const char *name) { char details[48]; sprintf(details, "(%s,%s,%s)", hci_typetostr(type), hci_bustostr(bus), name); print_packet(tv, NULL, '=', index, NULL, COLOR_NEW_INDEX, "New Index", label, details); } void packet_del_index(struct timeval *tv, uint16_t index, const char *label) { print_packet(tv, NULL, '=', index, NULL, COLOR_DEL_INDEX, "Delete Index", label, NULL); } void packet_open_index(struct timeval *tv, uint16_t index, const char *label) { print_packet(tv, NULL, '=', index, NULL, COLOR_OPEN_INDEX, "Open Index", label, NULL); } void packet_close_index(struct timeval *tv, uint16_t index, const char *label) { print_packet(tv, NULL, '=', index, NULL, COLOR_CLOSE_INDEX, "Close Index", label, NULL); } void packet_index_info(struct timeval *tv, uint16_t index, const char *label, uint16_t manufacturer) { char details[128]; sprintf(details, "(%s)", bt_compidtostr(manufacturer)); print_packet(tv, NULL, '=', index, NULL, COLOR_INDEX_INFO, "Index Info", label, details); } void packet_vendor_diag(struct timeval *tv, uint16_t index, uint16_t manufacturer, const void *data, uint16_t size) { char extra_str[16]; sprintf(extra_str, "(len %d)", size); print_packet(tv, NULL, '=', index, NULL, COLOR_VENDOR_DIAG, "Vendor Diagnostic", NULL, extra_str); switch (manufacturer) { case 15: broadcom_lm_diag(data, size); break; default: packet_hexdump(data, size); break; } } void packet_system_note(struct timeval *tv, struct ucred *cred, uint16_t index, const void *message) { print_packet(tv, cred, '=', index, NULL, COLOR_SYSTEM_NOTE, "Note", message, NULL); } struct monitor_l2cap_hdr { uint16_t cid; uint16_t psm; } __attribute__((packed)); static void packet_decode(struct timeval *tv, struct ucred *cred, char dir, uint16_t index, const char *color, const char *label, const void *data, uint16_t size) { const struct monitor_l2cap_hdr *hdr = data; if (size < sizeof(*hdr)) { print_packet(tv, cred, '*', index, NULL, COLOR_ERROR, "Malformed User Data packet", NULL, NULL); } print_packet(tv, cred, dir, index, NULL, COLOR_HCI_ACLDATA, label, dir == '>' ? "User Data RX" : "User Data TX", NULL); /* Discard last byte since it just a filler */ l2cap_frame(index, dir == '>', 0, le16_to_cpu(hdr->cid), le16_to_cpu(hdr->psm), data + sizeof(*hdr), size - (sizeof(*hdr) + 1)); } void packet_user_logging(struct timeval *tv, struct ucred *cred, uint16_t index, uint8_t priority, const char *ident, const void *data, uint16_t size) { const char *label; const char *color; if (priority > priority_level) return; switch (priority) { case BTSNOOP_PRIORITY_ERR: color = COLOR_ERROR; break; case BTSNOOP_PRIORITY_WARNING: color = COLOR_WARN; break; case BTSNOOP_PRIORITY_INFO: color = COLOR_INFO; break; case BTSNOOP_PRIORITY_DEBUG: color = COLOR_DEBUG; break; default: color = COLOR_WHITE_BG; break; } if (cred) { label = NULL; } else { if (ident) label = ident; else label = "Message"; } if (ident && (ident[0] == '<' || ident[0] == '>')) { packet_decode(tv, cred, ident[0], index, color, label == ident ? &ident[2] : label, data, size); return; } print_packet(tv, cred, '=', index, NULL, color, label, data, NULL); } void packet_hci_command(struct timeval *tv, struct ucred *cred, uint16_t index, const void *data, uint16_t size) { const hci_command_hdr *hdr = data; uint16_t opcode = le16_to_cpu(hdr->opcode); uint16_t ogf = cmd_opcode_ogf(opcode); uint16_t ocf = cmd_opcode_ocf(opcode); struct opcode_data vendor_data; const struct opcode_data *opcode_data = NULL; const char *opcode_color, *opcode_str; char extra_str[25], vendor_str[150]; int i; if (index >= MAX_INDEX) { print_field("Invalid index (%d).", index); return; } index_list[index].frame++; if (size < HCI_COMMAND_HDR_SIZE || size > BTSNOOP_MAX_PACKET_SIZE) { sprintf(extra_str, "(len %d)", size); print_packet(tv, cred, '*', index, NULL, COLOR_ERROR, "Malformed HCI Command packet", NULL, extra_str); return; } data += HCI_COMMAND_HDR_SIZE; size -= HCI_COMMAND_HDR_SIZE; for (i = 0; opcode_table[i].str; i++) { if (opcode_table[i].opcode == opcode) { opcode_data = &opcode_table[i]; break; } } if (opcode_data) { if (opcode_data->cmd_func) opcode_color = COLOR_HCI_COMMAND; else opcode_color = COLOR_HCI_COMMAND_UNKNOWN; opcode_str = opcode_data->str; } else { if (ogf == 0x3f) { const struct vendor_ocf *vnd = current_vendor_ocf(ocf); if (vnd) { const char *str = current_vendor_str(ocf); if (str) { snprintf(vendor_str, sizeof(vendor_str), "%s %s", str, vnd->str); vendor_data.str = vendor_str; } else vendor_data.str = vnd->str; vendor_data.cmd_func = vnd->cmd_func; vendor_data.cmd_size = vnd->cmd_size; vendor_data.cmd_fixed = vnd->cmd_fixed; opcode_data = &vendor_data; if (opcode_data->cmd_func) opcode_color = COLOR_HCI_COMMAND; else opcode_color = COLOR_HCI_COMMAND_UNKNOWN; opcode_str = opcode_data->str; } else { opcode_color = COLOR_HCI_COMMAND; opcode_str = "Vendor"; } } else { opcode_color = COLOR_HCI_COMMAND_UNKNOWN; opcode_str = "Unknown"; } } sprintf(extra_str, "(0x%2.2x|0x%4.4x) plen %d", ogf, ocf, hdr->plen); print_packet(tv, cred, '<', index, NULL, opcode_color, "HCI Command", opcode_str, extra_str); if (!opcode_data || !opcode_data->cmd_func) { packet_hexdump(data, size); return; } if (size != hdr->plen) { print_text(COLOR_ERROR, "invalid packet size (%u != %u)", size, hdr->plen); packet_hexdump(data, size); return; } if (opcode_data->cmd_fixed) { if (hdr->plen != opcode_data->cmd_size) { print_text(COLOR_ERROR, "invalid packet size"); packet_hexdump(data, size); return; } } else { if (hdr->plen < opcode_data->cmd_size) { print_text(COLOR_ERROR, "too short packet"); packet_hexdump(data, size); return; } } opcode_data->cmd_func(index, data, hdr->plen); } void packet_hci_event(struct timeval *tv, struct ucred *cred, uint16_t index, const void *data, uint16_t size) { const hci_event_hdr *hdr = data; const struct event_data *event_data = NULL; const char *event_color, *event_str; char extra_str[25]; int i; if (index >= MAX_INDEX) { print_field("Invalid index (%d).", index); return; } index_list[index].frame++; if (size < HCI_EVENT_HDR_SIZE) { sprintf(extra_str, "(len %d)", size); print_packet(tv, cred, '*', index, NULL, COLOR_ERROR, "Malformed HCI Event packet", NULL, extra_str); packet_hexdump(data, size); return; } data += HCI_EVENT_HDR_SIZE; size -= HCI_EVENT_HDR_SIZE; for (i = 0; event_table[i].str; i++) { if (event_table[i].event == hdr->evt) { event_data = &event_table[i]; break; } } if (event_data) { if (event_data->func) event_color = COLOR_HCI_EVENT; else event_color = COLOR_HCI_EVENT_UNKNOWN; event_str = event_data->str; } else { event_color = COLOR_HCI_EVENT_UNKNOWN; event_str = "Unknown"; } sprintf(extra_str, "(0x%2.2x) plen %d", hdr->evt, hdr->plen); print_packet(tv, cred, '>', index, NULL, event_color, "HCI Event", event_str, extra_str); if (!event_data || !event_data->func) { packet_hexdump(data, size); return; } if (size != hdr->plen) { print_text(COLOR_ERROR, "invalid packet size (%u != %u)", size, hdr->plen); packet_hexdump(data, size); return; } if (event_data->fixed) { if (hdr->plen != event_data->size) { print_text(COLOR_ERROR, "invalid packet size"); packet_hexdump(data, size); return; } } else { if (hdr->plen < event_data->size) { print_text(COLOR_ERROR, "too short packet"); packet_hexdump(data, size); return; } } event_data->func(tv, index, data, hdr->plen); } static void packet_enqueue_tx(struct timeval *tv, uint16_t handle, size_t num, uint16_t len) { struct packet_conn_data *conn; struct packet_frame *frame; conn = packet_get_conn_data(handle); if (!conn) return; if (!conn->tx_q) conn->tx_q = queue_new(); frame = new0(struct packet_frame, 1); if (tv) memcpy(&frame->tv, tv, sizeof(*tv)); frame->num = num; frame->len = len; queue_push_tail(conn->tx_q, frame); } void packet_hci_acldata(struct timeval *tv, struct ucred *cred, uint16_t index, bool in, const void *data, uint16_t size) { const struct bt_hci_acl_hdr *hdr = data; uint16_t handle = le16_to_cpu(hdr->handle); uint16_t dlen = le16_to_cpu(hdr->dlen); uint8_t flags = acl_flags(handle); char handle_str[16], extra_str[32]; if (index >= MAX_INDEX) { print_field("Invalid index (%d).", index); return; } index_list[index].frame++; if (size < HCI_ACL_HDR_SIZE) { if (in) print_packet(tv, cred, '*', index, NULL, COLOR_ERROR, "Malformed ACL Data RX packet", NULL, NULL); else print_packet(tv, cred, '*', index, NULL, COLOR_ERROR, "Malformed ACL Data TX packet", NULL, NULL); packet_hexdump(data, size); return; } data += HCI_ACL_HDR_SIZE; size -= HCI_ACL_HDR_SIZE; sprintf(handle_str, "Handle %d", acl_handle(handle)); sprintf(extra_str, "flags 0x%2.2x dlen %d", flags, dlen); print_packet(tv, cred, in ? '>' : '<', index, NULL, COLOR_HCI_ACLDATA, in ? "ACL Data RX" : "ACL Data TX", handle_str, extra_str); if (!in) packet_enqueue_tx(tv, acl_handle(handle), index_list[index].frame, dlen); if (size != dlen) { print_text(COLOR_ERROR, "invalid packet size (%d != %d)", size, dlen); packet_hexdump(data, size); return; } if (filter_mask & PACKET_FILTER_SHOW_ACL_DATA) packet_hexdump(data, size); l2cap_packet(index, in, acl_handle(handle), flags, data, size); } void packet_hci_scodata(struct timeval *tv, struct ucred *cred, uint16_t index, bool in, const void *data, uint16_t size) { const hci_sco_hdr *hdr = data; uint16_t handle = le16_to_cpu(hdr->handle); uint8_t flags = acl_flags(handle); char handle_str[16], extra_str[32]; if (index >= MAX_INDEX) { print_field("Invalid index (%d).", index); return; } index_list[index].frame++; if (size < HCI_SCO_HDR_SIZE) { if (in) print_packet(tv, cred, '*', index, NULL, COLOR_ERROR, "Malformed SCO Data RX packet", NULL, NULL); else print_packet(tv, cred, '*', index, NULL, COLOR_ERROR, "Malformed SCO Data TX packet", NULL, NULL); packet_hexdump(data, size); return; } data += HCI_SCO_HDR_SIZE; size -= HCI_SCO_HDR_SIZE; sprintf(handle_str, "Handle %d", acl_handle(handle)); sprintf(extra_str, "flags 0x%2.2x dlen %d", flags, hdr->dlen); print_packet(tv, cred, in ? '>' : '<', index, NULL, COLOR_HCI_SCODATA, in ? "SCO Data RX" : "SCO Data TX", handle_str, extra_str); if (!in) packet_enqueue_tx(tv, acl_handle(handle), index_list[index].frame, hdr->dlen); if (size != hdr->dlen) { print_text(COLOR_ERROR, "invalid packet size (%d != %d)", size, hdr->dlen); packet_hexdump(data, size); return; } if (filter_mask & PACKET_FILTER_SHOW_SCO_DATA) packet_hexdump(data, size); } void packet_hci_isodata(struct timeval *tv, struct ucred *cred, uint16_t index, bool in, const void *data, uint16_t size) { const struct bt_hci_iso_hdr *hdr = data; uint16_t handle = le16_to_cpu(hdr->handle); uint8_t flags = acl_flags(handle); char handle_str[16], extra_str[32]; if (index >= MAX_INDEX) { print_field("Invalid index (%d).", index); return; } index_list[index].frame++; if (size < sizeof(*hdr)) { if (in) print_packet(tv, cred, '*', index, NULL, COLOR_ERROR, "Malformed ISO Data RX packet", NULL, NULL); else print_packet(tv, cred, '*', index, NULL, COLOR_ERROR, "Malformed ISO Data TX packet", NULL, NULL); packet_hexdump(data, size); return; } data += sizeof(*hdr); size -= sizeof(*hdr); sprintf(handle_str, "Handle %d", acl_handle(handle)); sprintf(extra_str, "flags 0x%2.2x dlen %d", flags, hdr->dlen); print_packet(tv, cred, in ? '>' : '<', index, NULL, COLOR_HCI_ISODATA, in ? "ISO Data RX" : "ISO Data TX", handle_str, extra_str); if (!in) packet_enqueue_tx(tv, acl_handle(handle), index_list[index].frame, hdr->dlen); if (size != hdr->dlen) { print_text(COLOR_ERROR, "invalid packet size (%d != %d)", size, hdr->dlen); packet_hexdump(data, size); return; } if (filter_mask & PACKET_FILTER_SHOW_ISO_DATA) packet_hexdump(data, size); } void packet_ctrl_open(struct timeval *tv, struct ucred *cred, uint16_t index, const void *data, uint16_t size) { uint32_t cookie; uint16_t format; char channel[11]; if (size < 6) { print_packet(tv, cred, '*', index, NULL, COLOR_ERROR, "Malformed Control Open packet", NULL, NULL); packet_hexdump(data, size); return; } cookie = get_le32(data); format = get_le16(data + 4); data += 6; size -= 6; sprintf(channel, "0x%4.4x", cookie); if ((format == CTRL_RAW || format == CTRL_USER || format == CTRL_MGMT) && size >= 8) { uint8_t version; uint16_t revision; uint32_t flags; uint8_t ident_len; const char *comm; char details[48]; const char *title; version = get_u8(data); revision = get_le16(data + 1); flags = get_le32(data + 3); ident_len = get_u8(data + 7); if ((8 + ident_len) > size) { print_packet(tv, cred, '*', index, NULL, COLOR_ERROR, "Malformed Control Open packet", NULL, NULL); return; } data += 8; size -= 8; comm = ident_len > 0 ? data : "unknown"; data += ident_len; size -= ident_len; assign_ctrl(cookie, format, comm); sprintf(details, "%sversion %u.%u", flags & 0x0001 ? "(privileged) " : "", version, revision); switch (format) { case CTRL_RAW: title = "RAW Open"; break; case CTRL_USER: title = "USER Open"; break; case CTRL_MGMT: title = "MGMT Open"; break; default: title = "Control Open"; break; } print_packet(tv, cred, '@', index, channel, COLOR_CTRL_OPEN, title, comm, details); } else { char label[7]; assign_ctrl(cookie, format, NULL); sprintf(label, "0x%4.4x", format); print_packet(tv, cred, '@', index, channel, COLOR_CTRL_OPEN, "Control Open", label, NULL); } packet_hexdump(data, size); } void packet_ctrl_close(struct timeval *tv, struct ucred *cred, uint16_t index, const void *data, uint16_t size) { uint32_t cookie; uint16_t format; char channel[11], label[22]; const char *title; if (size < 4) { print_packet(tv, cred, '*', index, NULL, COLOR_ERROR, "Malformed Control Close packet", NULL, NULL); packet_hexdump(data, size); return; } cookie = get_le32(data); data += 4; size -= 4; sprintf(channel, "0x%4.4x", cookie); release_ctrl(cookie, &format, label); switch (format) { case CTRL_RAW: title = "RAW Close"; break; case CTRL_USER: title = "USER Close"; break; case CTRL_MGMT: title = "MGMT Close"; break; default: sprintf(label, "0x%4.4x", format); title = "Control Close"; break; } print_packet(tv, cred, '@', index, channel, COLOR_CTRL_CLOSE, title, label, NULL); packet_hexdump(data, size); } static const struct { uint8_t status; const char *str; } mgmt_status_table[] = { { 0x00, "Success" }, { 0x01, "Unknown Command" }, { 0x02, "Not Connected" }, { 0x03, "Failed" }, { 0x04, "Connect Failed" }, { 0x05, "Authentication Failed" }, { 0x06, "Not Paired" }, { 0x07, "No Resources" }, { 0x08, "Timeout" }, { 0x09, "Already Connected" }, { 0x0a, "Busy" }, { 0x0b, "Rejected" }, { 0x0c, "Not Supported" }, { 0x0d, "Invalid Parameters" }, { 0x0e, "Disconnected" }, { 0x0f, "Not Powered" }, { 0x10, "Cancelled" }, { 0x11, "Invalid Index" }, { 0x12, "RFKilled" }, { 0x13, "Already Paired" }, { 0x14, "Permission Denied" }, { } }; static void mgmt_print_status(uint8_t status) { const char *str = "Unknown"; const char *color_on, *color_off; bool unknown = true; int i; for (i = 0; mgmt_status_table[i].str; i++) { if (mgmt_status_table[i].status == status) { str = mgmt_status_table[i].str; unknown = false; break; } } if (use_color()) { if (status) { if (unknown) color_on = COLOR_UNKNOWN_ERROR; else color_on = COLOR_RED; } else color_on = COLOR_GREEN; color_off = COLOR_OFF; } else { color_on = ""; color_off = ""; } print_field("Status: %s%s%s (0x%2.2x)", color_on, str, color_off, status); } static void mgmt_print_address(const uint8_t *address, uint8_t type) { switch (type) { case 0x00: print_addr_resolve("BR/EDR Address", address, 0x00, false); break; case 0x01: print_addr_resolve("LE Address", address, 0x00, false); break; case 0x02: print_addr_resolve("LE Address", address, 0x01, false); break; default: print_addr_resolve("Address", address, 0xff, false); break; } } static const struct bitfield_data mgmt_address_type_table[] = { { 0, "BR/EDR" }, { 1, "LE Public" }, { 2, "LE Random" }, { } }; static void mgmt_print_address_type(uint8_t type) { uint8_t mask; print_field("Address type: 0x%2.2x", type); mask = print_bitfield(2, type, mgmt_address_type_table); if (mask) print_text(COLOR_UNKNOWN_ADDRESS_TYPE, " Unknown address type" " (0x%2.2x)", mask); } static void mgmt_print_version(uint8_t version) { packet_print_version("Version", version, NULL, 0x0000); } static void mgmt_print_manufacturer(uint16_t manufacturer) { packet_print_company("Manufacturer", manufacturer); } static const struct bitfield_data mgmt_options_table[] = { { 0, "External configuration" }, { 1, "Bluetooth public address configuration" }, { } }; static void mgmt_print_options(const char *label, uint32_t options) { uint32_t mask; print_field("%s: 0x%8.8x", label, options); mask = print_bitfield(2, options, mgmt_options_table); if (mask) print_text(COLOR_UNKNOWN_OPTIONS_BIT, " Unknown options" " (0x%8.8x)", mask); } static const struct bitfield_data mgmt_settings_table[] = { { 0, "Powered" }, { 1, "Connectable" }, { 2, "Fast Connectable" }, { 3, "Discoverable" }, { 4, "Bondable" }, { 5, "Link Security" }, { 6, "Secure Simple Pairing" }, { 7, "BR/EDR" }, { 8, "High Speed" }, { 9, "Low Energy" }, { 10, "Advertising" }, { 11, "Secure Connections" }, { 12, "Debug Keys" }, { 13, "Privacy" }, { 14, "Controller Configuration"}, { 15, "Static Address" }, { 16, "PHY Configuration" }, { 17, "Wideband Speech" }, { 18, "CIS Central" }, { 19, "CIS Peripheral" }, { 20, "ISO Broadcaster" }, { 21, "Sync Receiver" }, { 22, "LL Privacy" }, {} }; static void mgmt_print_settings(const char *label, uint32_t settings) { uint32_t mask; print_field("%s: 0x%8.8x", label, settings); mask = print_bitfield(2, settings, mgmt_settings_table); if (mask) print_text(COLOR_UNKNOWN_SETTINGS_BIT, " Unknown settings" " (0x%8.8x)", mask); } static void mgmt_print_name(const void *data) { print_field("Name: %s", (char *) data); print_field("Short name: %s", (char *) (data + 249)); } static void mgmt_print_io_capability(uint8_t capability) { const char *str; switch (capability) { case 0x00: str = "DisplayOnly"; break; case 0x01: str = "DisplayYesNo"; break; case 0x02: str = "KeyboardOnly"; break; case 0x03: str = "NoInputNoOutput"; break; case 0x04: str = "KeyboardDisplay"; break; default: str = "Reserved"; break; } print_field("Capability: %s (0x%2.2x)", str, capability); } static const struct bitfield_data mgmt_device_flags_table[] = { { 0, "Confirm Name" }, { 1, "Legacy Pairing" }, { 2, "Not Connectable" }, { 3, "Connection Locally Initiated" }, { 4, "Name Request Failed" }, { 5, "Scan Response" }, { } }; static void mgmt_print_device_flags(uint32_t flags) { uint32_t mask; print_field("Flags: 0x%8.8x", flags); mask = print_bitfield(2, flags, mgmt_device_flags_table); if (mask) print_text(COLOR_UNKNOWN_DEVICE_FLAG, " Unknown device flag" " (0x%8.8x)", mask); } static void mgmt_print_device_action(uint8_t action) { const char *str; switch (action) { case 0x00: str = "Background scan for device"; break; case 0x01: str = "Allow incoming connection"; break; case 0x02: str = "Auto-connect remote device"; break; default: str = "Reserved"; break; } print_field("Action: %s (0x%2.2x)", str, action); } static const struct bitfield_data mgmt_adv_flags_table[] = { { 0, "Switch into Connectable mode" }, { 1, "Advertise as Discoverable" }, { 2, "Advertise as Limited Discoverable" }, { 3, "Add Flags field to Advertising Data" }, { 4, "Add TX Power field to Advertising Data" }, { 5, "Add Appearance field to Scan Response" }, { 6, "Add Local Name in Scan Response" }, { 7, "Advertise in 1M on Secondary channel" }, { 8, "Advertise in 2M on Secondary channel" }, { 9, "Advertise in CODED on Secondary channel" }, { 10, "Support setting Tx Power" }, { 11, "Support HW offload" }, { 12, "Use provided duration parameter" }, { 13, "Use provided timeout parameter" }, { 14, "Use provided interval parameters" }, { 15, "Use provided tx power parameter" }, { 16, "Contain Scan Response Data" }, { } }; #define MGMT_ADV_PARAM_DURATION (1 << 12) #define MGMT_ADV_PARAM_TIMEOUT (1 << 13) #define MGMT_ADV_PARAM_INTERVALS (1 << 14) #define MGMT_ADV_PARAM_TX_POWER (1 << 15) static void mgmt_print_adv_flags(uint32_t flags) { uint32_t mask; print_field("Flags: 0x%8.8x", flags); mask = print_bitfield(2, flags, mgmt_adv_flags_table); if (mask) print_text(COLOR_UNKNOWN_ADV_FLAG, " Unknown advertising flag" " (0x%8.8x)", mask); } static void mgmt_print_store_hint(uint8_t hint) { const char *str; switch (hint) { case 0x00: str = "No"; break; case 0x01: str = "Yes"; break; default: str = "Reserved"; break; } print_field("Store hint: %s (0x%2.2x)", str, hint); } static void mgmt_print_connection_parameter(const void *data) { uint8_t address_type = get_u8(data + 6); uint16_t min_conn_interval = get_le16(data + 7); uint16_t max_conn_interval = get_le16(data + 9); uint16_t conn_latency = get_le16(data + 11); uint16_t supv_timeout = get_le16(data + 13); mgmt_print_address(data, address_type); print_field("Min connection interval: %u", min_conn_interval); print_field("Max connection interval: %u", max_conn_interval); print_conn_latency("Connection latency", conn_latency); print_field("Supervision timeout: %u", supv_timeout); } static void mgmt_print_link_key(const void *data) { uint8_t address_type = get_u8(data + 6); uint8_t key_type = get_u8(data + 7); uint8_t pin_len = get_u8(data + 24); mgmt_print_address(data, address_type); print_key_type(key_type); print_link_key(data + 8); print_field("PIN length: %d", pin_len); } static void mgmt_print_long_term_key(const void *data) { uint8_t address_type = get_u8(data + 6); uint8_t key_type = get_u8(data + 7); uint8_t central = get_u8(data + 8); uint8_t enc_size = get_u8(data + 9); const char *str; mgmt_print_address(data, address_type); switch (key_type) { case 0x00: str = "Unauthenticated legacy key"; break; case 0x01: str = "Authenticated legacy key"; break; case 0x02: str = "Unauthenticated key from P-256"; break; case 0x03: str = "Authenticated key from P-256"; break; case 0x04: str = "Debug key from P-256"; break; default: str = "Reserved"; break; } print_field("Key type: %s (0x%2.2x)", str, key_type); print_field("Central: 0x%2.2x", central); print_field("Encryption size: %u", enc_size); print_hex_field("Diversifier", data + 10, 2); print_hex_field("Randomizer", data + 12, 8); print_hex_field("Key", data + 20, 16); } static void mgmt_print_identity_resolving_key(const void *data) { uint8_t address_type = get_u8(data + 6); mgmt_print_address(data, address_type); print_hex_field("Key", data + 7, 16); keys_add_identity(data, address_type, data + 7); } static void mgmt_print_signature_resolving_key(const void *data) { uint8_t address_type = get_u8(data + 6); uint8_t key_type = get_u8(data + 7); const char *str; mgmt_print_address(data, address_type); switch (key_type) { case 0x00: str = "Unauthenticated local CSRK"; break; case 0x01: str = "Unauthenticated remote CSRK"; break; case 0x02: str = "Authenticated local CSRK"; break; case 0x03: str = "Authenticated remote CSRK"; break; default: str = "Reserved"; break; } print_field("Key type: %s (0x%2.2x)", str, key_type); print_hex_field("Key", data + 8, 16); } static void mgmt_print_oob_data(const void *data) { print_hash_p192(data); print_randomizer_p192(data + 16); print_hash_p256(data + 32); print_randomizer_p256(data + 48); } static const struct bitfield_data mgmt_exp_feature_flags_table[] = { { 0, "Active" }, { 1, "Settings change" }, { } }; static void mgmt_print_exp_feature(const void *data) { uint32_t flags = get_le32(data + 16); uint32_t mask; print_field("UUID: %s", bt_uuid128_to_str(data)); print_field("Flags: 0x%8.8x", flags); mask = print_bitfield(2, flags, mgmt_exp_feature_flags_table); if (mask) print_text(COLOR_UNKNOWN_EXP_FEATURE_FLAG, " Unknown feature flag (0x%8.8x)", mask); } static void mgmt_null_cmd(const void *data, uint16_t size) { } static void mgmt_null_rsp(const void *data, uint16_t size) { } static void mgmt_read_version_info_rsp(const void *data, uint16_t size) { uint8_t version; uint16_t revision; version = get_u8(data); revision = get_le16(data + 1); print_field("Version: %u.%u", version, revision); } static void mgmt_print_commands(const void *data, uint16_t num); static void mgmt_print_events(const void *data, uint16_t num); static void mgmt_read_supported_commands_rsp(const void *data, uint16_t size) { uint16_t num_commands = get_le16(data); uint16_t num_events = get_le16(data + 2); if (size - 4 != (num_commands * 2) + (num_events *2)) { packet_hexdump(data, size); return; } mgmt_print_commands(data + 4, num_commands); mgmt_print_events(data + 4 + num_commands * 2, num_events); } static void mgmt_read_index_list_rsp(const void *data, uint16_t size) { uint16_t num_controllers = get_le16(data); int i; print_field("Controllers: %u", num_controllers); if (size - 2 != num_controllers * 2) { packet_hexdump(data + 2, size - 2); return; } for (i = 0; i < num_controllers; i++) { uint16_t index = get_le16(data + 2 + (i * 2)); print_field(" hci%u", index); } } static void mgmt_read_controller_info_rsp(const void *data, uint16_t size) { uint8_t version = get_u8(data + 6); uint16_t manufacturer = get_le16(data + 7); uint32_t supported_settings = get_le32(data + 9); uint32_t current_settings = get_le32(data + 13); print_addr_resolve("Address", data, 0x00, false); mgmt_print_version(version); mgmt_print_manufacturer(manufacturer); mgmt_print_settings("Supported settings", supported_settings); mgmt_print_settings("Current settings", current_settings); print_dev_class(data + 17); mgmt_print_name(data + 20); } static void mgmt_set_powered_cmd(const void *data, uint16_t size) { uint8_t enable = get_u8(data); print_enable("Powered", enable); } static void mgmt_set_discoverable_cmd(const void *data, uint16_t size) { uint8_t enable = get_u8(data); uint16_t timeout = get_le16(data + 1); const char *str; switch (enable) { case 0x00: str = "Disabled"; break; case 0x01: str = "General"; break; case 0x02: str = "Limited"; break; default: str = "Reserved"; break; } print_field("Discoverable: %s (0x%2.2x)", str, enable); print_field("Timeout: %u", timeout); } static void mgmt_set_connectable_cmd(const void *data, uint16_t size) { uint8_t enable = get_u8(data); print_enable("Connectable", enable); } static void mgmt_set_fast_connectable_cmd(const void *data, uint16_t size) { uint8_t enable = get_u8(data); print_enable("Fast Connectable", enable); } static void mgmt_set_bondable_cmd(const void *data, uint16_t size) { uint8_t enable = get_u8(data); print_enable("Bondable", enable); } static void mgmt_set_link_security_cmd(const void *data, uint16_t size) { uint8_t enable = get_u8(data); print_enable("Link Security", enable); } static void mgmt_set_secure_simple_pairing_cmd(const void *data, uint16_t size) { uint8_t enable = get_u8(data); print_enable("Secure Simple Pairing", enable); } static void mgmt_set_high_speed_cmd(const void *data, uint16_t size) { uint8_t enable = get_u8(data); print_enable("High Speed", enable); } static void mgmt_set_low_energy_cmd(const void *data, uint16_t size) { uint8_t enable = get_u8(data); print_enable("Low Energy", enable); } static void mgmt_set_blocked_keys_cmd(const void *data, uint16_t size) { struct iovec frame = { (void *)data, size }; uint16_t num_keys; int i; if (!util_iov_pull_le16(&frame, &num_keys)) { print_field("Keys: invalid size"); return; } print_field("Keys: %u", num_keys); for (i = 0; i < num_keys; i++) { uint8_t type; uint8_t *key; if (!util_iov_pull_u8(&frame, &type)) { print_field("Key type[%u]: invalid size", i); return; } switch (type) { case 0x00: print_field("type: Link Key (0x00)"); break; case 0x01: print_field("type: Long Term Key (0x01)"); break; case 0x02: print_field("type: Identity Resolving Key (0x02)"); break; } key = util_iov_pull_mem(&frame, 16); if (!key) { print_field("Key[%u]: invalid size", i); return; } print_link_key(key); } } static void mgmt_set_wbs_cmd(const void *data, uint16_t size) { uint8_t enable = get_u8(data); print_enable("Wideband Speech", enable); } static void mgmt_new_settings_rsp(const void *data, uint16_t size) { uint32_t current_settings = get_le32(data); mgmt_print_settings("Current settings", current_settings); } static void mgmt_set_device_class_cmd(const void *data, uint16_t size) { uint8_t major = get_u8(data); uint8_t minor = get_u8(data + 1); print_field("Major class: 0x%2.2x", major); print_field("Minor class: 0x%2.2x", minor); } static void mgmt_set_device_class_rsp(const void *data, uint16_t size) { print_dev_class(data); } static void mgmt_set_local_name_cmd(const void *data, uint16_t size) { mgmt_print_name(data); } static void mgmt_set_local_name_rsp(const void *data, uint16_t size) { mgmt_print_name(data); } static void mgmt_add_uuid_cmd(const void *data, uint16_t size) { uint8_t service_class = get_u8(data + 16); print_field("UUID: %s", bt_uuid128_to_str(data)); print_field("Service class: 0x%2.2x", service_class); } static void mgmt_add_uuid_rsp(const void *data, uint16_t size) { print_dev_class(data); } static void mgmt_remove_uuid_cmd(const void *data, uint16_t size) { print_field("UUID: %s", bt_uuid128_to_str(data)); } static void mgmt_remove_uuid_rsp(const void *data, uint16_t size) { print_dev_class(data); } static void mgmt_load_link_keys_cmd(const void *data, uint16_t size) { uint8_t debug_keys = get_u8(data); uint16_t num_keys = get_le16(data + 1); int i; print_enable("Debug keys", debug_keys); print_field("Keys: %u", num_keys); if (size - 3 != num_keys * 25) { packet_hexdump(data + 3, size - 3); return; } for (i = 0; i < num_keys; i++) mgmt_print_link_key(data + 3 + (i * 25)); } static void mgmt_load_long_term_keys_cmd(const void *data, uint16_t size) { uint16_t num_keys = get_le16(data); int i; print_field("Keys: %u", num_keys); if (size - 2 != num_keys * 36) { packet_hexdump(data + 2, size - 2); return; } for (i = 0; i < num_keys; i++) mgmt_print_long_term_key(data + 2 + (i * 36)); } static void mgmt_disconnect_cmd(const void *data, uint16_t size) { uint8_t address_type = get_u8(data + 6); mgmt_print_address(data, address_type); } static void mgmt_disconnect_rsp(const void *data, uint16_t size) { uint8_t address_type = get_u8(data + 6); mgmt_print_address(data, address_type); } static void mgmt_get_connections_rsp(const void *data, uint16_t size) { uint16_t num_connections = get_le16(data); int i; print_field("Connections: %u", num_connections); if (size - 2 != num_connections * 7) { packet_hexdump(data + 2, size - 2); return; } for (i = 0; i < num_connections; i++) { uint8_t address_type = get_u8(data + 2 + (i * 7) + 6); mgmt_print_address(data + 2 + (i * 7), address_type); } } static void mgmt_pin_code_reply_cmd(const void *data, uint16_t size) { uint8_t address_type = get_u8(data + 6); uint8_t pin_len = get_u8(data + 7); mgmt_print_address(data, address_type); print_field("PIN length: %u", pin_len); print_hex_field("PIN code", data + 8, 16); } static void mgmt_pin_code_reply_rsp(const void *data, uint16_t size) { uint8_t address_type = get_u8(data + 6); mgmt_print_address(data, address_type); } static void mgmt_pin_code_neg_reply_cmd(const void *data, uint16_t size) { uint8_t address_type = get_u8(data + 6); mgmt_print_address(data, address_type); } static void mgmt_pin_code_neg_reply_rsp(const void *data, uint16_t size) { uint8_t address_type = get_u8(data + 6); mgmt_print_address(data, address_type); } static void mgmt_set_io_capability_cmd(const void *data, uint16_t size) { uint8_t capability = get_u8(data); mgmt_print_io_capability(capability); } static void mgmt_pair_device_cmd(const void *data, uint16_t size) { uint8_t address_type = get_u8(data + 6); uint8_t capability = get_u8(data + 7); mgmt_print_address(data, address_type); mgmt_print_io_capability(capability); } static void mgmt_pair_device_rsp(const void *data, uint16_t size) { uint8_t address_type = get_u8(data + 6); mgmt_print_address(data, address_type); } static void mgmt_cancel_pair_device_cmd(const void *data, uint16_t size) { uint8_t address_type = get_u8(data + 6); mgmt_print_address(data, address_type); } static void mgmt_cancel_pair_device_rsp(const void *data, uint16_t size) { uint8_t address_type = get_u8(data + 6); mgmt_print_address(data, address_type); } static void mgmt_unpair_device_cmd(const void *data, uint16_t size) { uint8_t address_type = get_u8(data + 6); uint8_t disconnect = get_u8(data + 7); mgmt_print_address(data, address_type); print_enable("Disconnect", disconnect); } static void mgmt_unpair_device_rsp(const void *data, uint16_t size) { uint8_t address_type = get_u8(data + 6); mgmt_print_address(data, address_type); } static void mgmt_user_confirmation_reply_cmd(const void *data, uint16_t size) { uint8_t address_type = get_u8(data + 6); mgmt_print_address(data, address_type); } static void mgmt_user_confirmation_reply_rsp(const void *data, uint16_t size) { uint8_t address_type = get_u8(data + 6); mgmt_print_address(data, address_type); } static void mgmt_user_confirmation_neg_reply_cmd(const void *data, uint16_t size) { uint8_t address_type = get_u8(data + 6); mgmt_print_address(data, address_type); } static void mgmt_user_confirmation_neg_reply_rsp(const void *data, uint16_t size) { uint8_t address_type = get_u8(data + 6); mgmt_print_address(data, address_type); } static void mgmt_user_passkey_reply_cmd(const void *data, uint16_t size) { uint8_t address_type = get_u8(data + 6); uint32_t passkey = get_le32(data + 7); mgmt_print_address(data, address_type); print_field("Passkey: 0x%4.4x", passkey); } static void mgmt_user_passkey_reply_rsp(const void *data, uint16_t size) { uint8_t address_type = get_u8(data + 6); mgmt_print_address(data, address_type); } static void mgmt_user_passkey_neg_reply_cmd(const void *data, uint16_t size) { uint8_t address_type = get_u8(data + 6); mgmt_print_address(data, address_type); } static void mgmt_user_passkey_neg_reply_rsp(const void *data, uint16_t size) { uint8_t address_type = get_u8(data + 6); mgmt_print_address(data, address_type); } static void mgmt_read_local_oob_data_rsp(const void *data, uint16_t size) { mgmt_print_oob_data(data); } static void mgmt_add_remote_oob_data_cmd(const void *data, uint16_t size) { uint8_t address_type = get_u8(data + 6); mgmt_print_address(data, address_type); mgmt_print_oob_data(data + 7); } static void mgmt_add_remote_oob_data_rsp(const void *data, uint16_t size) { uint8_t address_type = get_u8(data + 6); mgmt_print_address(data, address_type); } static void mgmt_remove_remote_oob_data_cmd(const void *data, uint16_t size) { uint8_t address_type = get_u8(data + 6); mgmt_print_address(data, address_type); } static void mgmt_remove_remote_oob_data_rsp(const void *data, uint16_t size) { uint8_t address_type = get_u8(data + 6); mgmt_print_address(data, address_type); } static void mgmt_start_discovery_cmd(const void *data, uint16_t size) { uint8_t type = get_u8(data); mgmt_print_address_type(type); } static void mgmt_start_discovery_rsp(const void *data, uint16_t size) { uint8_t type = get_u8(data); mgmt_print_address_type(type); } static void mgmt_stop_discovery_cmd(const void *data, uint16_t size) { uint8_t type = get_u8(data); mgmt_print_address_type(type); } static void mgmt_stop_discovery_rsp(const void *data, uint16_t size) { uint8_t type = get_u8(data); mgmt_print_address_type(type); } static void mgmt_confirm_name_cmd(const void *data, uint16_t size) { uint8_t address_type = get_u8(data + 6); uint8_t name_known = get_u8(data + 7); const char *str; mgmt_print_address(data, address_type); switch (name_known) { case 0x00: str = "No"; break; case 0x01: str = "Yes"; break; default: str = "Reserved"; break; } print_field("Name known: %s (0x%2.2x)", str, name_known); } static void mgmt_confirm_name_rsp(const void *data, uint16_t size) { uint8_t address_type = get_u8(data + 6); mgmt_print_address(data, address_type); } static void mgmt_block_device_cmd(const void *data, uint16_t size) { uint8_t address_type = get_u8(data + 6); mgmt_print_address(data, address_type); } static void mgmt_block_device_rsp(const void *data, uint16_t size) { uint8_t address_type = get_u8(data + 6); mgmt_print_address(data, address_type); } static void mgmt_unblock_device_cmd(const void *data, uint16_t size) { uint8_t address_type = get_u8(data + 6); mgmt_print_address(data, address_type); } static void mgmt_unblock_device_rsp(const void *data, uint16_t size) { uint8_t address_type = get_u8(data + 6); mgmt_print_address(data, address_type); } static void mgmt_set_device_id_cmd(const void *data, uint16_t size) { print_device_id(data, size); } static void mgmt_set_advertising_cmd(const void *data, uint16_t size) { uint8_t enable = get_u8(data); const char *str; switch (enable) { case 0x00: str = "Disabled"; break; case 0x01: str = "Enabled"; break; case 0x02: str = "Connectable"; break; default: str = "Reserved"; break; } print_field("Advertising: %s (0x%2.2x)", str, enable); } static void mgmt_set_bredr_cmd(const void *data, uint16_t size) { uint8_t enable = get_u8(data); print_enable("BR/EDR", enable); } static void mgmt_set_static_address_cmd(const void *data, uint16_t size) { print_addr_resolve("Address", data, 0x01, false); } static void mgmt_set_scan_parameters_cmd(const void *data, uint16_t size) { uint16_t interval = get_le16(data); uint16_t window = get_le16(data + 2); print_field("Interval: %u (0x%2.2x)", interval, interval); print_field("Window: %u (0x%2.2x)", window, window); } static void mgmt_set_secure_connections_cmd(const void *data, uint16_t size) { uint8_t enable = get_u8(data); const char *str; switch (enable) { case 0x00: str = "Disabled"; break; case 0x01: str = "Enabled"; break; case 0x02: str = "Only"; break; default: str = "Reserved"; break; } print_field("Secure Connections: %s (0x%2.2x)", str, enable); } static void mgmt_set_debug_keys_cmd(const void *data, uint16_t size) { uint8_t enable = get_u8(data); const char *str; switch (enable) { case 0x00: str = "Disabled"; break; case 0x01: str = "Enabled"; break; case 0x02: str = "Generate"; break; default: str = "Reserved"; break; } print_field("Debug Keys: %s (0x%2.2x)", str, enable); } static void mgmt_set_privacy_cmd(const void *data, uint16_t size) { uint8_t enable = get_u8(data); const char *str; switch (enable) { case 0x00: str = "Disabled"; break; case 0x01: str = "Enabled"; break; case 0x02: str = "Limited"; break; default: str = "Reserved"; break; } print_field("Privacy: %s (0x%2.2x)", str, enable); print_hex_field("Key", data + 1, 16); } static void mgmt_load_identity_resolving_keys_cmd(const void *data, uint16_t size) { uint16_t num_keys = get_le16(data); int i; print_field("Keys: %u", num_keys); if (size - 2 != num_keys * 23) { packet_hexdump(data + 2, size - 2); return; } for (i = 0; i < num_keys; i++) mgmt_print_identity_resolving_key(data + 2 + (i * 23)); } static void mgmt_get_connection_information_cmd(const void *data, uint16_t size) { uint8_t address_type = get_u8(data + 6); mgmt_print_address(data, address_type); } static void mgmt_get_connection_information_rsp(const void *data, uint16_t size) { uint8_t address_type = get_u8(data + 6); int8_t rssi = get_s8(data + 7); int8_t tx_power = get_s8(data + 8); int8_t max_tx_power = get_s8(data + 9); mgmt_print_address(data, address_type); print_rssi(rssi); print_power_level(tx_power, NULL); print_power_level(max_tx_power, "max"); } static void mgmt_get_clock_information_cmd(const void *data, uint16_t size) { uint8_t address_type = get_u8(data + 6); mgmt_print_address(data, address_type); } static void mgmt_get_clock_information_rsp(const void *data, uint16_t size) { uint8_t address_type = get_u8(data + 6); uint32_t local_clock = get_le32(data + 7); uint32_t piconet_clock = get_le32(data + 11); uint16_t accuracy = get_le16(data + 15); mgmt_print_address(data, address_type); print_field("Local clock: 0x%8.8x", local_clock); print_field("Piconet clock: 0x%8.8x", piconet_clock); print_field("Accuracy: 0x%4.4x", accuracy); } static void mgmt_add_device_cmd(const void *data, uint16_t size) { uint8_t address_type = get_u8(data + 6); uint8_t action = get_u8(data + 7); mgmt_print_address(data, address_type); mgmt_print_device_action(action); } static void mgmt_add_device_rsp(const void *data, uint16_t size) { uint8_t address_type = get_u8(data + 6); mgmt_print_address(data, address_type); } static void mgmt_remove_device_cmd(const void *data, uint16_t size) { uint8_t address_type = get_u8(data + 6); mgmt_print_address(data, address_type); } static void mgmt_remove_device_rsp(const void *data, uint16_t size) { uint8_t address_type = get_u8(data + 6); mgmt_print_address(data, address_type); } static void mgmt_load_connection_parameters_cmd(const void *data, uint16_t size) { uint16_t num_parameters = get_le16(data); int i; print_field("Parameters: %u", num_parameters); if (size - 2 != num_parameters * 15) { packet_hexdump(data + 2, size - 2); return; } for (i = 0; i < num_parameters; i++) mgmt_print_connection_parameter(data + 2 + (i * 15)); } static void mgmt_read_unconf_index_list_rsp(const void *data, uint16_t size) { uint16_t num_controllers = get_le16(data); int i; print_field("Controllers: %u", num_controllers); if (size - 2 != num_controllers * 2) { packet_hexdump(data + 2, size - 2); return; } for (i = 0; i < num_controllers; i++) { uint16_t index = get_le16(data + 2 + (i * 2)); print_field(" hci%u", index); } } static void mgmt_read_controller_conf_info_rsp(const void *data, uint16_t size) { uint16_t manufacturer = get_le16(data); uint32_t supported_options = get_le32(data + 2); uint32_t missing_options = get_le32(data + 6); mgmt_print_manufacturer(manufacturer); mgmt_print_options("Supported options", supported_options); mgmt_print_options("Missing options", missing_options); } static void mgmt_set_external_configuration_cmd(const void *data, uint16_t size) { uint8_t enable = get_u8(data); print_enable("Configuration", enable); } static void mgmt_set_public_address_cmd(const void *data, uint16_t size) { print_addr_resolve("Address", data, 0x00, false); } static void mgmt_new_options_rsp(const void *data, uint16_t size) { uint32_t missing_options = get_le32(data); mgmt_print_options("Missing options", missing_options); } static void mgmt_start_service_discovery_cmd(const void *data, uint16_t size) { uint8_t type = get_u8(data); int8_t rssi = get_s8(data + 1); uint16_t num_uuids = get_le16(data + 2); int i; mgmt_print_address_type(type); print_rssi(rssi); print_field("UUIDs: %u", num_uuids); if (size - 4 != num_uuids * 16) { packet_hexdump(data + 4, size - 4); return; } for (i = 0; i < num_uuids; i++) print_field("UUID: %s", bt_uuid128_to_str(data + 4 + (i * 16))); } static void mgmt_start_service_discovery_rsp(const void *data, uint16_t size) { uint8_t type = get_u8(data); mgmt_print_address_type(type); } static void mgmt_read_ext_index_list_rsp(const void *data, uint16_t size) { uint16_t num_controllers = get_le16(data); int i; print_field("Controllers: %u", num_controllers); if (size - 2 != num_controllers * 4) { packet_hexdump(data + 2, size - 2); return; } for (i = 0; i < num_controllers; i++) { uint16_t index = get_le16(data + 2 + (i * 4)); uint8_t type = get_u8(data + 4 + (i * 4)); uint8_t bus = get_u8(data + 5 + (i * 4)); const char *str; switch (type) { case 0x00: str = "Primary"; break; case 0x01: str = "Unconfigured"; break; case 0x02: str = "AMP"; break; default: str = "Reserved"; break; } print_field(" hci%u (%s,%s)", index, str, hci_bustostr(bus)); } } static void mgmt_read_local_oob_ext_data_cmd(const void *data, uint16_t size) { uint8_t type = get_u8(data); mgmt_print_address_type(type); } static void mgmt_read_local_oob_ext_data_rsp(const void *data, uint16_t size) { uint8_t type = get_u8(data); uint16_t data_len = get_le16(data + 1); mgmt_print_address_type(type); print_field("Data length: %u", data_len); print_eir(data + 3, size - 3, true); } static void mgmt_read_advertising_features_rsp(const void *data, uint16_t size) { uint32_t flags = get_le32(data); uint8_t adv_data_len = get_u8(data + 4); uint8_t scan_rsp_len = get_u8(data + 5); uint8_t max_instances = get_u8(data + 6); uint8_t num_instances = get_u8(data + 7); int i; mgmt_print_adv_flags(flags); print_field("Advertising data length: %u", adv_data_len); print_field("Scan response length: %u", scan_rsp_len); print_field("Max instances: %u", max_instances); print_field("Instances: %u", num_instances); if (size - 8 != num_instances) { packet_hexdump(data + 8, size - 8); return; } for (i = 0; i < num_instances; i++) { uint8_t instance = get_u8(data + 8 + i); print_field(" %u", instance); } } static void mgmt_add_advertising_cmd(const void *data, uint16_t size) { uint8_t instance = get_u8(data); uint32_t flags = get_le32(data + 1); uint16_t duration = get_le16(data + 5); uint16_t timeout = get_le16(data + 7); uint8_t adv_data_len = get_u8(data + 9); uint8_t scan_rsp_len = get_u8(data + 10); print_field("Instance: %u", instance); mgmt_print_adv_flags(flags); print_field("Duration: %u", duration); print_field("Timeout: %u", timeout); print_field("Advertising data length: %u", adv_data_len); print_eir(data + 11, adv_data_len, false); print_field("Scan response length: %u", scan_rsp_len); print_eir(data + 11 + adv_data_len, scan_rsp_len, false); } static void mgmt_add_advertising_rsp(const void *data, uint16_t size) { uint8_t instance = get_u8(data); print_field("Instance: %u", instance); } static void mgmt_remove_advertising_cmd(const void *data, uint16_t size) { uint8_t instance = get_u8(data); print_field("Instance: %u", instance); } static void mgmt_remove_advertising_rsp(const void *data, uint16_t size) { uint8_t instance = get_u8(data); print_field("Instance: %u", instance); } static void mgmt_get_advertising_size_info_cmd(const void *data, uint16_t size) { uint8_t instance = get_u8(data); uint32_t flags = get_le32(data + 1); print_field("Instance: %u", instance); mgmt_print_adv_flags(flags); } static void mgmt_get_advertising_size_info_rsp(const void *data, uint16_t size) { uint8_t instance = get_u8(data); uint32_t flags = get_le32(data + 1); uint8_t adv_data_len = get_u8(data + 5); uint8_t scan_rsp_len = get_u8(data + 6); print_field("Instance: %u", instance); mgmt_print_adv_flags(flags); print_field("Advertising data length: %u", adv_data_len); print_field("Scan response length: %u", scan_rsp_len); } static void mgmt_start_limited_discovery_cmd(const void *data, uint16_t size) { uint8_t type = get_u8(data); mgmt_print_address_type(type); } static void mgmt_start_limited_discovery_rsp(const void *data, uint16_t size) { uint8_t type = get_u8(data); mgmt_print_address_type(type); } static void mgmt_read_ext_controller_info_rsp(const void *data, uint16_t size) { uint8_t version = get_u8(data + 6); uint16_t manufacturer = get_le16(data + 7); uint32_t supported_settings = get_le32(data + 9); uint32_t current_settings = get_le32(data + 13); uint16_t data_len = get_le16(data + 17); print_addr_resolve("Address", data, 0x00, false); mgmt_print_version(version); mgmt_print_manufacturer(manufacturer); mgmt_print_settings("Supported settings", supported_settings); mgmt_print_settings("Current settings", current_settings); print_field("Data length: %u", data_len); print_eir(data + 19, size - 19, false); } static void mgmt_set_apperance_cmd(const void *data, uint16_t size) { uint16_t appearance = get_le16(data); print_appearance(appearance); } static const struct bitfield_data mgmt_phy_table[] = { { 0, "BR 1M 1SLOT" }, { 1, "BR 1M 3SLOT" }, { 2, "BR 1M 5SLOT" }, { 3, "EDR 2M 1SLOT" }, { 4, "EDR 2M 3SLOT" }, { 5, "EDR 2M 5SLOT" }, { 6, "EDR 3M 1SLOT" }, { 7, "EDR 3M 3SLOT" }, { 8, "EDR 3M 5SLOT" }, { 9, "LE 1M TX" }, { 10, "LE 1M RX" }, { 11, "LE 2M TX" }, { 12, "LE 2M RX" }, { 13, "LE CODED TX" }, { 14, "LE CODED RX" }, { } }; static void mgmt_print_phys(const char *label, uint16_t phys) { uint16_t mask; print_field("%s: 0x%4.4x", label, phys); mask = print_bitfield(2, phys, mgmt_phy_table); if (mask) print_text(COLOR_UNKNOWN_PHY, " Unknown PHYs" " (0x%8.8x)", mask); } static void mgmt_get_phy_rsp(const void *data, uint16_t size) { uint32_t supported_phys = get_le32(data); uint32_t configurable_phys = get_le32(data + 4); uint32_t selected_phys = get_le32(data + 8); mgmt_print_phys("Supported PHYs", supported_phys); mgmt_print_phys("Configurable PHYs", configurable_phys); mgmt_print_phys("Selected PHYs", selected_phys); } static void mgmt_set_phy_cmd(const void *data, uint16_t size) { uint32_t selected_phys = get_le32(data); mgmt_print_phys("Selected PHYs", selected_phys); } static void mgmt_read_exp_features_info_rsp(const void *data, uint16_t size) { uint16_t num_features = get_le16(data); int i; print_field("Features: %u", num_features); if (size - 2 != num_features * 20) { packet_hexdump(data + 2, size - 2); return; } for (i = 0; i < num_features; i++) mgmt_print_exp_feature(data + 2 + (i * 20)); } static void mgmt_set_exp_feature_cmd(const void *data, uint16_t size) { uint8_t enable = get_u8(data + 16); print_field("UUID: %s", bt_uuid128_to_str(data)); print_enable("Action", enable); } static void mgmt_set_exp_feature_rsp(const void *data, uint16_t size) { mgmt_print_exp_feature(data); } static const struct bitfield_data mgmt_added_device_flags_table[] = { { 0, "Remote Wakeup" }, { 1, "Device Privacy Mode" }, { 2, "Address Resolution" }, { } }; static void mgmt_print_added_device_flags(char *label, uint32_t flags) { uint32_t mask; print_field("%s: 0x%8.8x", label, flags); mask = print_bitfield(2, flags, mgmt_added_device_flags_table); if (mask) print_text(COLOR_UNKNOWN_ADDED_DEVICE_FLAG, " Unknown Flags (0x%8.8x)", mask); } static void mgmt_get_device_flags_cmd(const void *data, uint16_t size) { uint8_t type = get_u8(data + 6); mgmt_print_address(data, type); } static void mgmt_get_device_flags_rsp(const void *data, uint16_t size) { uint8_t type = get_u8(data + 6); uint32_t supported_flags = get_le32(data + 7); uint32_t current_flags = get_le32(data + 11); mgmt_print_address(data, type); mgmt_print_added_device_flags("Supported Flags", supported_flags); mgmt_print_added_device_flags("Current Flags", current_flags); } static void mgmt_set_device_flags_cmd(const void *data, uint16_t size) { uint8_t type = get_u8(data + 6); uint32_t current_flags = get_le32(data + 7); mgmt_print_address(data, type); mgmt_print_added_device_flags("Current Flags", current_flags); } static void mgmt_set_device_flags_rsp(const void *data, uint16_t size) { uint8_t type = get_u8(data + 6); mgmt_print_address(data, type); } static void mgmt_add_ext_adv_params_cmd(const void *data, uint16_t size) { uint8_t instance = get_u8(data); uint32_t flags = get_le32(data + 1); uint16_t duration = get_le16(data + 5); uint16_t timeout = get_le16(data + 7); uint8_t *min_interval = (uint8_t *)(data + 9); uint8_t *max_interval = (uint8_t *)(data + 13); int8_t tx_power = get_s8(data + 17); print_field("Instance: %u", instance); mgmt_print_adv_flags(flags); print_field("Duration: %u", duration); print_field("Timeout: %u", timeout); print_ext_slot_625("Min advertising interval", min_interval); print_ext_slot_625("Max advertising interval", max_interval); print_power_level(tx_power, NULL); } static void mgmt_add_ext_adv_params_rsp(const void *data, uint16_t size) { uint8_t instance = get_u8(data); int8_t tx_power = get_s8(data + 1); uint8_t max_adv_data_len = get_u8(data+2); uint8_t max_scan_rsp_len = get_u8(data+3); print_field("Instance: %u", instance); print_power_level(tx_power, NULL); print_field("Available adv data len: %u", max_adv_data_len); print_field("Available scan rsp data len: %u", max_scan_rsp_len); } static void mgmt_add_ext_adv_data_cmd(const void *data, uint16_t size) { uint8_t instance = get_u8(data); uint8_t adv_data_len = get_u8(data + 1); uint8_t scan_rsp_len = get_u8(data + 2); print_field("Instance: %u", instance); print_field("Advertising data length: %u", adv_data_len); print_eir(data + 3, adv_data_len, false); print_field("Scan response length: %u", scan_rsp_len); print_eir(data + 3 + adv_data_len, scan_rsp_len, false); } static void mgmt_add_ext_adv_data_rsp(const void *data, uint16_t size) { uint8_t instance = get_u8(data); print_field("Instance: %u", instance); } static const struct bitfield_data mgmt_adv_monitor_features_table[] = { { 1, "OR Patterns" }, { } }; static void mgmt_print_adv_monitor_features(char *label, uint32_t flags) { uint32_t mask; print_field("%s: 0x%8.8x", label, flags); mask = print_bitfield(2, flags, mgmt_adv_monitor_features_table); if (mask) print_text(COLOR_UNKNOWN_ADVMON_FEATURES, " Unknown Flags (0x%8.8x)", mask); } static void mgmt_print_adv_monitor_handles(const void *data, uint8_t len) { uint8_t idx = 0; while (idx + 2 <= len) { print_field(" Handle: %d", get_le16(data + idx)); idx += 2; } } static void mgmt_read_adv_monitor_features_rsp(const void *data, uint16_t size) { uint32_t supported_features = get_le32(data); uint32_t enabled_features = get_le32(data + 4); uint16_t max_num_handles = get_le16(data + 8); uint8_t max_num_patterns = get_u8(data + 10); uint16_t num_handles = get_le16(data + 11); mgmt_print_adv_monitor_features("Supported Features", supported_features); mgmt_print_adv_monitor_features("Enabled Features", enabled_features); print_field("Max number of handles: %d", max_num_handles); print_field("Max number of patterns: %d", max_num_patterns); print_field("Number of handles: %d", num_handles); mgmt_print_adv_monitor_handles(data + 13, size - 13); } static void mgmt_print_adv_monitor_patterns(const void *data, uint8_t len) { uint8_t data_idx = 0, pattern_idx = 1; /* Reference: struct mgmt_adv_pattern in lib/mgmt.h. */ while (data_idx + 34 <= len) { uint8_t ad_type = get_u8(data); uint8_t offset = get_u8(data + 1); uint8_t length = get_u8(data + 2); print_field(" Pattern %d:", pattern_idx); print_field(" AD type: %d", ad_type); print_field(" Offset: %d", offset); print_field(" Length: %d", length); if (length <= 31) print_hex_field(" Value ", data + 3, length); else print_text(COLOR_ERROR, " invalid length"); pattern_idx += 1; data_idx += 34; data += 34; } } static void mgmt_add_adv_monitor_patterns_cmd(const void *data, uint16_t size) { uint8_t pattern_count = get_u8(data); print_field("Number of patterns: %d", pattern_count); mgmt_print_adv_monitor_patterns(data + 1, size - 1); } static void mgmt_add_adv_monitor_patterns_rssi_cmd(const void *data, uint16_t size) { int8_t high_rssi = get_s8(data); uint16_t high_rssi_timeout = get_le16(data + 1); int8_t low_rssi = get_s8(data + 3); uint16_t low_rssi_timeout = get_le16(data + 4); uint8_t sampling_period = get_u8(data + 6); uint8_t pattern_count = get_u8(data + 7); print_field("RSSI data:"); print_field(" high threshold: %d dBm", high_rssi); print_field(" high timeout: %d seconds", high_rssi_timeout); print_field(" low threshold: %d dBm", low_rssi); print_field(" low timeout: %d seconds", low_rssi_timeout); if (sampling_period == 0) print_field(" sampling: propagate all (0x00)"); else if (sampling_period == 0xff) print_field(" sampling: just once (0xFF)"); else print_field(" sampling: every %d ms", 100 * sampling_period); print_field("Number of patterns: %d", pattern_count); mgmt_print_adv_monitor_patterns(data + 8, size - 8); } static void mgmt_add_adv_monitor_patterns_rsp(const void *data, uint16_t size) { uint16_t handle = get_le16(data); print_field("Handle: %d", handle); } static void mgmt_remove_adv_monitor_patterns_cmd(const void *data, uint16_t size) { uint16_t handle = get_le16(data); print_field("Handle: %d", handle); } static void mgmt_remove_adv_monitor_patterns_rsp(const void *data, uint16_t size) { uint16_t handle = get_le16(data); print_field("Handle: %d", handle); } static void mgmt_set_mesh_receiver_cmd(const void *data, uint16_t size) { uint8_t enable = get_u8(data); uint16_t window = get_le16(data + 1); uint16_t period = get_le16(data + 3); uint8_t num_ad_types = get_u8(data + 5); const uint8_t *ad_types = data + 6; print_field("Enable: %d", enable); print_field("Window: %d", window); print_field("Period: %d", period); print_field("Num AD Types: %d", num_ad_types); size -= 6; while (size--) print_field(" AD Type: %d", *ad_types++); } static void mgmt_read_mesh_features_rsp(const void *data, uint16_t size) { uint16_t index = get_le16(data); uint8_t max_handles = get_u8(data + 2); uint8_t used_handles = get_u8(data + 3); const uint8_t *handles = data + 4; print_field("Index: %d", index); print_field("Max Handles: %d", max_handles); print_field("Used Handles: %d", used_handles); size -= 4; while (size--) print_field(" Used Handle: %d", *handles++); } static void mgmt_mesh_send_cmd(const void *data, uint16_t size) { const uint8_t *addr = data; uint8_t addr_type = get_u8(data + 6); uint64_t instant = get_le64(data + 7); uint16_t delay = get_le16(data + 15); uint8_t cnt = get_u8(data + 17); uint8_t adv_data_len = get_u8(data + 18); data += 19; size -= 19; print_bdaddr(addr); print_field("Addr Type: %d", addr_type); print_field("Instant: 0x%16.16" PRIx64, instant); print_field("Delay: %d", delay); print_field("Count: %d", cnt); print_field("Data Length: %d", adv_data_len); print_hex_field("Data", data, size); } static void mgmt_mesh_send_rsp(const void *data, uint16_t size) { uint8_t handle = get_u8(data); print_field("Handle: %d", handle); } static void mgmt_mesh_send_cancel_cmd(const void *data, uint16_t size) { uint8_t handle = get_u8(data); print_field("Handle: %d", handle); } static void mgmt_hci_cmd_sync_cmd(const void *data, uint16_t size) { struct iovec iov = { (void *)data, size }; uint16_t opcode, len; uint8_t event; uint8_t timeout; if (!util_iov_pull_le16(&iov, &opcode)) { print_text(COLOR_ERROR, " invalid opcode"); return; } print_field("Opcode: 0x%4.4x", opcode); if (!util_iov_pull_u8(&iov, &event)) { print_text(COLOR_ERROR, " invalid event"); return; } print_field("Event: 0x%2.2x", event); if (!util_iov_pull_u8(&iov, &timeout)) { print_text(COLOR_ERROR, " invalid timeout"); return; } print_field("Timeout: %d seconds", timeout); if (!util_iov_pull_le16(&iov, &len)) { print_text(COLOR_ERROR, " invalid parameters length"); return; } print_field("Parameters Length: %d", len); if (iov.iov_len != len) { print_text(COLOR_ERROR, " length mismatch (%zu != %d)", iov.iov_len, len); return; } print_hex_field("Parameters", iov.iov_base, iov.iov_len); } static void mgmt_hci_cmd_sync_rsp(const void *data, uint16_t size) { print_hex_field("Response", data, size); } struct mgmt_data { uint16_t opcode; const char *str; void (*func) (const void *data, uint16_t size); uint16_t size; bool fixed; void (*rsp_func) (const void *data, uint16_t size); uint16_t rsp_size; bool rsp_fixed; }; static const struct mgmt_data mgmt_command_table[] = { { 0x0001, "Read Management Version Information", mgmt_null_cmd, 0, true, mgmt_read_version_info_rsp, 3, true }, { 0x0002, "Read Management Supported Commands", mgmt_null_cmd, 0, true, mgmt_read_supported_commands_rsp, 4, false }, { 0x0003, "Read Controller Index List", mgmt_null_cmd, 0, true, mgmt_read_index_list_rsp, 2, false }, { 0x0004, "Read Controller Information", mgmt_null_cmd, 0, true, mgmt_read_controller_info_rsp, 280, true }, { 0x0005, "Set Powered", mgmt_set_powered_cmd, 1, true, mgmt_new_settings_rsp, 4, true }, { 0x0006, "Set Discoverable", mgmt_set_discoverable_cmd, 3, true, mgmt_new_settings_rsp, 4, true }, { 0x0007, "Set Connectable", mgmt_set_connectable_cmd, 1, true, mgmt_new_settings_rsp, 4, true }, { 0x0008, "Set Fast Connectable", mgmt_set_fast_connectable_cmd, 1, true, mgmt_new_settings_rsp, 4, true }, { 0x0009, "Set Bondable", mgmt_set_bondable_cmd, 1, true, mgmt_new_settings_rsp, 4, true }, { 0x000a, "Set Link Security", mgmt_set_link_security_cmd, 1, true, mgmt_new_settings_rsp, 4, true }, { 0x000b, "Set Secure Simple Pairing", mgmt_set_secure_simple_pairing_cmd, 1, true, mgmt_new_settings_rsp, 4, true }, { 0x000c, "Set High Speed", mgmt_set_high_speed_cmd, 1, true, mgmt_new_settings_rsp, 4, true }, { 0x000d, "Set Low Energy", mgmt_set_low_energy_cmd, 1, true, mgmt_new_settings_rsp, 4, true }, { 0x000e, "Set Device Class", mgmt_set_device_class_cmd, 2, true, mgmt_set_device_class_rsp, 3, true }, { 0x000f, "Set Local Name", mgmt_set_local_name_cmd, 260, true, mgmt_set_local_name_rsp, 260, true }, { 0x0010, "Add UUID", mgmt_add_uuid_cmd, 17, true, mgmt_add_uuid_rsp, 3, true }, { 0x0011, "Remove UUID", mgmt_remove_uuid_cmd, 16, true, mgmt_remove_uuid_rsp, 3, true }, { 0x0012, "Load Link Keys", mgmt_load_link_keys_cmd, 3, false, mgmt_null_rsp, 0, true }, { 0x0013, "Load Long Term Keys", mgmt_load_long_term_keys_cmd, 2, false, mgmt_null_rsp, 0, true }, { 0x0014, "Disconnect", mgmt_disconnect_cmd, 7, true, mgmt_disconnect_rsp, 7, true }, { 0x0015, "Get Connections", mgmt_null_cmd, 0, true, mgmt_get_connections_rsp, 2, false }, { 0x0016, "PIN Code Reply", mgmt_pin_code_reply_cmd, 24, true, mgmt_pin_code_reply_rsp, 7, true }, { 0x0017, "PIN Code Negative Reply", mgmt_pin_code_neg_reply_cmd, 7, true, mgmt_pin_code_neg_reply_rsp, 7, true }, { 0x0018, "Set IO Capability", mgmt_set_io_capability_cmd, 1, true, mgmt_null_rsp, 0, true }, { 0x0019, "Pair Device", mgmt_pair_device_cmd, 8, true, mgmt_pair_device_rsp, 7, true }, { 0x001a, "Cancel Pair Device", mgmt_cancel_pair_device_cmd, 7, true, mgmt_cancel_pair_device_rsp, 7, true }, { 0x001b, "Unpair Device", mgmt_unpair_device_cmd, 8, true, mgmt_unpair_device_rsp, 7, true }, { 0x001c, "User Confirmation Reply", mgmt_user_confirmation_reply_cmd, 7, true, mgmt_user_confirmation_reply_rsp, 7, true }, { 0x001d, "User Confirmation Negative Reply", mgmt_user_confirmation_neg_reply_cmd, 7, true, mgmt_user_confirmation_neg_reply_rsp, 7, true }, { 0x001e, "User Passkey Reply", mgmt_user_passkey_reply_cmd, 11, true, mgmt_user_passkey_reply_rsp, 7, true }, { 0x001f, "User Passkey Negative Reply", mgmt_user_passkey_neg_reply_cmd, 7, true, mgmt_user_passkey_neg_reply_rsp, 7, true }, { 0x0020, "Read Local Out Of Band Data", mgmt_null_cmd, 0, true, mgmt_read_local_oob_data_rsp, 64, true }, { 0x0021, "Add Remote Out Of Band Data", mgmt_add_remote_oob_data_cmd, 71, true, mgmt_add_remote_oob_data_rsp, 7, true }, { 0x0022, "Remove Remote Out Of Band Data", mgmt_remove_remote_oob_data_cmd, 7, true, mgmt_remove_remote_oob_data_rsp, 7, true }, { 0x0023, "Start Discovery", mgmt_start_discovery_cmd, 1, true, mgmt_start_discovery_rsp, 1, true }, { 0x0024, "Stop Discovery", mgmt_stop_discovery_cmd, 1, true, mgmt_stop_discovery_rsp, 1, true }, { 0x0025, "Confirm Name", mgmt_confirm_name_cmd, 8, true, mgmt_confirm_name_rsp, 7, true }, { 0x0026, "Block Device", mgmt_block_device_cmd, 7, true, mgmt_block_device_rsp, 7, true }, { 0x0027, "Unblock Device", mgmt_unblock_device_cmd, 7, true, mgmt_unblock_device_rsp, 7, true }, { 0x0028, "Set Device ID", mgmt_set_device_id_cmd, 8, true, mgmt_null_rsp, 0, true }, { 0x0029, "Set Advertising", mgmt_set_advertising_cmd, 1, true, mgmt_new_settings_rsp, 4, true }, { 0x002a, "Set BR/EDR", mgmt_set_bredr_cmd, 1, true, mgmt_new_settings_rsp, 4, true }, { 0x002b, "Set Static Address", mgmt_set_static_address_cmd, 6, true, mgmt_new_settings_rsp, 4, true }, { 0x002c, "Set Scan Parameters", mgmt_set_scan_parameters_cmd, 4, true, mgmt_null_rsp, 0, true }, { 0x002d, "Set Secure Connections", mgmt_set_secure_connections_cmd, 1, true, mgmt_new_settings_rsp, 4, true }, { 0x002e, "Set Debug Keys", mgmt_set_debug_keys_cmd, 1, true, mgmt_new_settings_rsp, 4, true }, { 0x002f, "Set Privacy", mgmt_set_privacy_cmd, 17, true, mgmt_new_settings_rsp, 4, true }, { 0x0030, "Load Identity Resolving Keys", mgmt_load_identity_resolving_keys_cmd, 2, false, mgmt_null_rsp, 0, true }, { 0x0031, "Get Connection Information", mgmt_get_connection_information_cmd, 7, true, mgmt_get_connection_information_rsp, 10, true }, { 0x0032, "Get Clock Information", mgmt_get_clock_information_cmd, 7, true, mgmt_get_clock_information_rsp, 17, true }, { 0x0033, "Add Device", mgmt_add_device_cmd, 8, true, mgmt_add_device_rsp, 7, true }, { 0x0034, "Remove Device", mgmt_remove_device_cmd, 7, true, mgmt_remove_device_rsp, 7, true }, { 0x0035, "Load Connection Parameters", mgmt_load_connection_parameters_cmd, 2, false, mgmt_null_rsp, 0, true }, { 0x0036, "Read Unconfigured Controller Index List", mgmt_null_cmd, 0, true, mgmt_read_unconf_index_list_rsp, 2, false }, { 0x0037, "Read Controller Configuration Information", mgmt_null_cmd, 0, true, mgmt_read_controller_conf_info_rsp, 10, true }, { 0x0038, "Set External Configuration", mgmt_set_external_configuration_cmd, 1, true, mgmt_new_options_rsp, 4, true }, { 0x0039, "Set Public Address", mgmt_set_public_address_cmd, 6, true, mgmt_new_options_rsp, 4, true }, { 0x003a, "Start Service Discovery", mgmt_start_service_discovery_cmd, 3, false, mgmt_start_service_discovery_rsp, 1, true }, { 0x003b, "Read Local Out Of Band Extended Data", mgmt_read_local_oob_ext_data_cmd, 1, true, mgmt_read_local_oob_ext_data_rsp, 3, false }, { 0x003c, "Read Extended Controller Index List", mgmt_null_cmd, 0, true, mgmt_read_ext_index_list_rsp, 2, false }, { 0x003d, "Read Advertising Features", mgmt_null_cmd, 0, true, mgmt_read_advertising_features_rsp, 8, false }, { 0x003e, "Add Advertising", mgmt_add_advertising_cmd, 11, false, mgmt_add_advertising_rsp, 1, true }, { 0x003f, "Remove Advertising", mgmt_remove_advertising_cmd, 1, true, mgmt_remove_advertising_rsp, 1, true }, { 0x0040, "Get Advertising Size Information", mgmt_get_advertising_size_info_cmd, 5, true, mgmt_get_advertising_size_info_rsp, 7, true }, { 0x0041, "Start Limited Discovery", mgmt_start_limited_discovery_cmd, 1, true, mgmt_start_limited_discovery_rsp, 1, true }, { 0x0042, "Read Extended Controller Information", mgmt_null_cmd, 0, true, mgmt_read_ext_controller_info_rsp, 19, false }, { 0x0043, "Set Appearance", mgmt_set_apperance_cmd, 2, true, mgmt_null_rsp, 0, true }, { 0x0044, "Get PHY Configuration", mgmt_null_cmd, 0, true, mgmt_get_phy_rsp, 12, true }, { 0x0045, "Set PHY Configuration", mgmt_set_phy_cmd, 4, true, mgmt_null_rsp, 0, true }, { 0x0046, "Set Blocked Keys", mgmt_set_blocked_keys_cmd, 2, false, mgmt_null_rsp, 0, true }, { 0x0047, "Set Wideband Speech", mgmt_set_wbs_cmd, 1, true, mgmt_new_settings_rsp, 4, true }, { 0x0048, "Read Controller Capabilities" }, { 0x0049, "Read Experimental Features Information", mgmt_null_cmd, 0, true, mgmt_read_exp_features_info_rsp, 2, false }, { 0x004a, "Set Experimental Feature", mgmt_set_exp_feature_cmd, 17, true, mgmt_set_exp_feature_rsp, 20, true }, { 0x004b, "Read Default System Configuration" }, { 0x004c, "Set Default System Configuration" }, { 0x004d, "Read Default Runtime Configuration" }, { 0x004e, "Set Default Runtime Configuration" }, { 0x004f, "Get Device Flags", mgmt_get_device_flags_cmd, 7, true, mgmt_get_device_flags_rsp, 15, true}, { 0x0050, "Set Device Flags", mgmt_set_device_flags_cmd, 11, true, mgmt_set_device_flags_rsp, 7, true}, { 0x0051, "Read Advertisement Monitor Features", mgmt_null_cmd, 0, true, mgmt_read_adv_monitor_features_rsp, 13, false}, { 0x0052, "Add Advertisement Patterns Monitor", mgmt_add_adv_monitor_patterns_cmd, 1, false, mgmt_add_adv_monitor_patterns_rsp, 2, true}, { 0x0053, "Remove Advertisement Monitor", mgmt_remove_adv_monitor_patterns_cmd, 2, true, mgmt_remove_adv_monitor_patterns_rsp, 2, true}, { 0x0054, "Add Extended Advertising Parameters", mgmt_add_ext_adv_params_cmd, 18, false, mgmt_add_ext_adv_params_rsp, 4, true }, { 0x0055, "Add Extended Advertising Data", mgmt_add_ext_adv_data_cmd, 3, false, mgmt_add_ext_adv_data_rsp, 1, true }, { 0x0056, "Add Advertisement Patterns Monitor With RSSI Threshold", mgmt_add_adv_monitor_patterns_rssi_cmd, 8, false, mgmt_add_adv_monitor_patterns_rsp, 2, true}, { 0x0057, "Set Mesh Receiver", mgmt_set_mesh_receiver_cmd, 6, false, mgmt_null_rsp, 0, true}, { 0x0058, "Read Mesh Features", mgmt_null_cmd, 0, true, mgmt_read_mesh_features_rsp, 4, false}, { 0x0059, "Mesh Send", mgmt_mesh_send_cmd, 19, false, mgmt_mesh_send_rsp, 1, true}, { 0x005A, "Mesh Send Cancel", mgmt_mesh_send_cancel_cmd, 1, true, mgmt_null_rsp, 0, true}, { 0x005B, "Send HCI command and wait for event", mgmt_hci_cmd_sync_cmd, 6, false, mgmt_hci_cmd_sync_rsp, 0, false}, { } }; static void mgmt_null_evt(const void *data, uint16_t size) { } static void mgmt_command_complete_evt(const void *data, uint16_t size) { uint16_t opcode; uint8_t status; const struct mgmt_data *mgmt_data = NULL; const char *mgmt_color, *mgmt_str; int i; opcode = get_le16(data); status = get_u8(data + 2); data += 3; size -= 3; for (i = 0; mgmt_command_table[i].str; i++) { if (mgmt_command_table[i].opcode == opcode) { mgmt_data = &mgmt_command_table[i]; break; } } if (mgmt_data) { if (mgmt_data->rsp_func) mgmt_color = COLOR_CTRL_COMMAND; else mgmt_color = COLOR_CTRL_COMMAND_UNKNOWN; mgmt_str = mgmt_data->str; } else { mgmt_color = COLOR_CTRL_COMMAND_UNKNOWN; mgmt_str = "Unknown"; } print_indent(6, mgmt_color, "", mgmt_str, COLOR_OFF, " (0x%4.4x) plen %u", opcode, size); mgmt_print_status(status); if (!mgmt_data || !mgmt_data->rsp_func) { packet_hexdump(data, size); return; } if (mgmt_data->rsp_fixed) { if (size != mgmt_data->rsp_size) { print_text(COLOR_ERROR, "invalid packet size"); packet_hexdump(data, size); return; } } else { if (size < mgmt_data->rsp_size) { print_text(COLOR_ERROR, "too short packet"); packet_hexdump(data, size); return; } } mgmt_data->rsp_func(data, size); } static void mgmt_command_status_evt(const void *data, uint16_t size) { uint16_t opcode; uint8_t status; const struct mgmt_data *mgmt_data = NULL; const char *mgmt_color, *mgmt_str; int i; opcode = get_le16(data); status = get_u8(data + 2); for (i = 0; mgmt_command_table[i].str; i++) { if (mgmt_command_table[i].opcode == opcode) { mgmt_data = &mgmt_command_table[i]; break; } } if (mgmt_data) { mgmt_color = COLOR_CTRL_COMMAND; mgmt_str = mgmt_data->str; } else { mgmt_color = COLOR_CTRL_COMMAND_UNKNOWN; mgmt_str = "Unknown"; } print_indent(6, mgmt_color, "", mgmt_str, COLOR_OFF, " (0x%4.4x)", opcode); mgmt_print_status(status); } static void mgmt_controller_error_evt(const void *data, uint16_t size) { uint8_t error = get_u8(data); print_field("Error: 0x%2.2x", error); } static void mgmt_new_settings_evt(const void *data, uint16_t size) { uint32_t settings = get_le32(data); mgmt_print_settings("Current settings", settings); } static void mgmt_class_of_dev_changed_evt(const void *data, uint16_t size) { print_dev_class(data); } static void mgmt_local_name_changed_evt(const void *data, uint16_t size) { mgmt_print_name(data); } static void mgmt_new_link_key_evt(const void *data, uint16_t size) { uint8_t store_hint = get_u8(data); mgmt_print_store_hint(store_hint); mgmt_print_link_key(data + 1); } static void mgmt_new_long_term_key_evt(const void *data, uint16_t size) { uint8_t store_hint = get_u8(data); mgmt_print_store_hint(store_hint); mgmt_print_long_term_key(data + 1); } static void mgmt_device_connected_evt(const void *data, uint16_t size) { uint8_t address_type = get_u8(data + 6); uint32_t flags = get_le32(data + 7); uint16_t data_len = get_le16(data + 11); mgmt_print_address(data, address_type); mgmt_print_device_flags(flags); print_field("Data length: %u", data_len); print_eir(data + 13, size - 13, false); } static void mgmt_device_disconnected_evt(const void *data, uint16_t size) { uint8_t address_type = get_u8(data + 6); uint8_t reason = get_u8(data + 7); const char *str; mgmt_print_address(data, address_type); switch (reason) { case 0x00: str = "Unspecified"; break; case 0x01: str = "Connection timeout"; break; case 0x02: str = "Connection terminated by local host"; break; case 0x03: str = "Connection terminated by remote host"; break; case 0x04: str = "Connection terminated due to authentication failure"; break; case 0x05: str = "Connection terminated by local host for suspend"; break; default: str = "Reserved"; break; } print_field("Reason: %s (0x%2.2x)", str, reason); } static void mgmt_connect_failed_evt(const void *data, uint16_t size) { uint8_t address_type = get_u8(data + 6); uint8_t status = get_u8(data + 7); mgmt_print_address(data, address_type); mgmt_print_status(status); } static void mgmt_pin_code_request_evt(const void *data, uint16_t size) { uint8_t address_type = get_u8(data + 6); uint8_t secure_pin = get_u8(data + 7); mgmt_print_address(data, address_type); print_field("Secure PIN: 0x%2.2x", secure_pin); } static void mgmt_user_confirmation_request_evt(const void *data, uint16_t size) { uint8_t address_type = get_u8(data + 6); uint8_t confirm_hint = get_u8(data + 7); uint32_t value = get_le32(data + 8); mgmt_print_address(data, address_type); print_field("Confirm hint: 0x%2.2x", confirm_hint); print_field("Value: 0x%8.8x", value); } static void mgmt_user_passkey_request_evt(const void *data, uint16_t size) { uint8_t address_type = get_u8(data + 6); mgmt_print_address(data, address_type); } static void mgmt_authentication_failed_evt(const void *data, uint16_t size) { uint8_t address_type = get_u8(data + 6); uint8_t status = get_u8(data + 7); mgmt_print_address(data, address_type); mgmt_print_status(status); } static void mgmt_device_found_evt(const void *data, uint16_t size) { uint8_t address_type = get_u8(data + 6); int8_t rssi = get_s8(data + 7); uint32_t flags = get_le32(data + 8); uint16_t data_len = get_le16(data + 12); mgmt_print_address(data, address_type); print_rssi(rssi); mgmt_print_device_flags(flags); print_field("Data length: %u", data_len); print_eir(data + 14, size - 14, false); } static void mgmt_discovering_evt(const void *data, uint16_t size) { uint8_t type = get_u8(data); uint8_t enable = get_u8(data + 1); mgmt_print_address_type(type); print_enable("Discovery", enable); } static void mgmt_device_blocked_evt(const void *data, uint16_t size) { uint8_t address_type = get_u8(data + 6); mgmt_print_address(data, address_type); } static void mgmt_device_unblocked_evt(const void *data, uint16_t size) { uint8_t address_type = get_u8(data + 6); mgmt_print_address(data, address_type); } static void mgmt_device_unpaired_evt(const void *data, uint16_t size) { uint8_t address_type = get_u8(data + 6); mgmt_print_address(data, address_type); } static void mgmt_passkey_notify_evt(const void *data, uint16_t size) { uint8_t address_type = get_u8(data + 6); uint32_t passkey = get_le32(data + 7); uint8_t entered = get_u8(data + 11); mgmt_print_address(data, address_type); print_field("Passkey: 0x%8.8x", passkey); print_field("Entered: %u", entered); } static void mgmt_new_identity_resolving_key_evt(const void *data, uint16_t size) { uint8_t store_hint = get_u8(data); mgmt_print_store_hint(store_hint); print_addr_resolve("Random address", data + 1, 0x01, false); mgmt_print_identity_resolving_key(data + 7); } static void mgmt_new_signature_resolving_key_evt(const void *data, uint16_t size) { uint8_t store_hint = get_u8(data); mgmt_print_store_hint(store_hint); mgmt_print_signature_resolving_key(data + 1); } static void mgmt_device_added_evt(const void *data, uint16_t size) { uint8_t address_type = get_u8(data + 6); uint8_t action = get_u8(data + 7); mgmt_print_address(data, address_type); mgmt_print_device_action(action); } static void mgmt_device_removed_evt(const void *data, uint16_t size) { uint8_t address_type = get_u8(data + 6); mgmt_print_address(data, address_type); } static void mgmt_new_connection_parameter_evt(const void *data, uint16_t size) { uint8_t store_hint = get_u8(data); mgmt_print_store_hint(store_hint); mgmt_print_connection_parameter(data + 1); } static void mgmt_new_conf_options_evt(const void *data, uint16_t size) { uint32_t missing_options = get_le32(data); mgmt_print_options("Missing options", missing_options); } static void mgmt_ext_index_added_evt(const void *data, uint16_t size) { uint8_t type = get_u8(data); uint8_t bus = get_u8(data + 1); print_field("type 0x%2.2x - bus 0x%2.2x", type, bus); } static void mgmt_ext_index_removed_evt(const void *data, uint16_t size) { uint8_t type = get_u8(data); uint8_t bus = get_u8(data + 1); print_field("type 0x%2.2x - bus 0x%2.2x", type, bus); } static void mgmt_local_oob_ext_data_updated_evt(const void *data, uint16_t size) { uint8_t type = get_u8(data); uint16_t data_len = get_le16(data + 1); mgmt_print_address_type(type); print_field("Data length: %u", data_len); print_eir(data + 3, size - 3, true); } static void mgmt_advertising_added_evt(const void *data, uint16_t size) { uint8_t instance = get_u8(data); print_field("Instance: %u", instance); } static void mgmt_advertising_removed_evt(const void *data, uint16_t size) { uint8_t instance = get_u8(data); print_field("Instance: %u", instance); } static void mgmt_ext_controller_info_changed_evt(const void *data, uint16_t size) { uint16_t data_len = get_le16(data); print_field("Data length: %u", data_len); print_eir(data + 2, size - 2, false); } static void mgmt_phy_changed_evt(const void *data, uint16_t size) { uint32_t selected_phys = get_le32(data); mgmt_print_phys("Selected PHYs", selected_phys); } static void mgmt_exp_feature_changed_evt(const void *data, uint16_t size) { mgmt_print_exp_feature(data); } static void mgmt_device_flags_changed_evt(const void *data, uint16_t size) { uint8_t type = get_u8(data + 6); uint32_t supported_flags = get_le32(data + 7); uint32_t current_flags = get_le32(data + 11); mgmt_print_address(data, type); mgmt_print_added_device_flags("Supported Flags", supported_flags); mgmt_print_added_device_flags("Current Flags", current_flags); } static void mgmt_adv_monitor_added_evt(const void *data, uint16_t size) { uint16_t handle = get_le16(data); print_field("Handle: %d", handle); } static void mgmt_adv_monitor_removed_evt(const void *data, uint16_t size) { uint16_t handle = get_le16(data); print_field("Handle: %d", handle); } static void mgmt_controller_suspend_evt(const void *data, uint16_t size) { uint8_t state = get_u8(data); char *str; switch (state) { case 0x0: str = "Controller running (failed to suspend)"; break; case 0x1: str = "Disconnected and not scanning"; break; case 0x2: str = "Page scanning and/or passive scanning"; break; default: str = "Unknown suspend state"; break; } print_field("Suspend state: %s (%d)", str, state); } static void mgmt_controller_resume_evt(const void *data, uint16_t size) { uint8_t addr_type = get_u8(data + 6); uint8_t wake_reason = get_u8(data + 7); char *str; switch (wake_reason) { case 0x0: str = "Resume from non-Bluetooth wake source"; break; case 0x1: str = "Wake due to unexpected event"; break; case 0x2: str = "Remote wake due to peer device connection"; break; default: str = "Unknown wake reason"; break; } print_field("Wake reason: %s (%d)", str, wake_reason); mgmt_print_address(data, addr_type); } static void mgmt_adv_monitor_device_found_evt(const void *data, uint16_t size) { uint16_t handle = get_le16(data); const uint8_t *addr = data + 2; uint8_t addr_type = get_u8(data + 8); int8_t rssi = get_s8(data + 9); uint32_t flags = get_le32(data + 10); uint16_t ad_data_len = get_le16(data + 14); const uint8_t *ad_data = data + 16; print_field("Handle: %d", handle); print_bdaddr(addr); print_field("Addr Type: %d", addr_type); print_field("RSSI: %d", rssi); mgmt_print_device_flags(flags); print_field("AD Data Len: %d", ad_data_len); size -= 16; print_hex_field("AD Data", ad_data, size); } static void mgmt_adv_monitor_device_lost_evt(const void *data, uint16_t size) { uint16_t handle = get_le16(data); const uint8_t *addr = data + 2; uint8_t addr_type = get_u8(data + 8); print_field("Handle: %d", handle); print_bdaddr(addr); print_field("Addr Type: %d", addr_type); } static void mgmt_mesh_device_found_evt(const void *data, uint16_t size) { const uint8_t *addr = data; uint8_t addr_type = get_u8(data + 6); int8_t rssi = get_s8(data + 7); uint64_t instant = get_le64(data + 8); uint32_t flags = get_le32(data + 16); uint16_t eir_len = get_le16(data + 20); const uint8_t *eir_data = data + 22; print_bdaddr(addr); print_field("Addr Type: %d", addr_type); print_field("RSSI: %d", rssi); print_field("Instant: 0x%16.16" PRIx64, instant); mgmt_print_device_flags(flags); print_field("EIR Length: %d", eir_len); size -= 22; print_hex_field("EIR Data", eir_data, size); } static void mgmt_mesh_packet_cmplt_evt(const void *data, uint16_t size) { uint8_t handle = get_u8(data); print_field("Handle: %d", handle); } static const struct mgmt_data mgmt_event_table[] = { { 0x0001, "Command Complete", mgmt_command_complete_evt, 3, false }, { 0x0002, "Command Status", mgmt_command_status_evt, 3, true }, { 0x0003, "Controller Error", mgmt_controller_error_evt, 1, true }, { 0x0004, "Index Added", mgmt_null_evt, 0, true }, { 0x0005, "Index Removed", mgmt_null_evt, 0, true }, { 0x0006, "New Settings", mgmt_new_settings_evt, 4, true }, { 0x0007, "Class Of Device Changed", mgmt_class_of_dev_changed_evt, 3, true }, { 0x0008, "Local Name Changed", mgmt_local_name_changed_evt, 260, true }, { 0x0009, "New Link Key", mgmt_new_link_key_evt, 26, true }, { 0x000a, "New Long Term Key", mgmt_new_long_term_key_evt, 37, true }, { 0x000b, "Device Connected", mgmt_device_connected_evt, 13, false }, { 0x000c, "Device Disconnected", mgmt_device_disconnected_evt, 8, true }, { 0x000d, "Connect Failed", mgmt_connect_failed_evt, 8, true }, { 0x000e, "PIN Code Request", mgmt_pin_code_request_evt, 8, true }, { 0x000f, "User Confirmation Request", mgmt_user_confirmation_request_evt, 12, true }, { 0x0010, "User Passkey Request", mgmt_user_passkey_request_evt, 7, true }, { 0x0011, "Authentication Failed", mgmt_authentication_failed_evt, 8, true }, { 0x0012, "Device Found", mgmt_device_found_evt, 14, false }, { 0x0013, "Discovering", mgmt_discovering_evt, 2, true }, { 0x0014, "Device Blocked", mgmt_device_blocked_evt, 7, true }, { 0x0015, "Device Unblocked", mgmt_device_unblocked_evt, 7, true }, { 0x0016, "Device Unpaired", mgmt_device_unpaired_evt, 7, true }, { 0x0017, "Passkey Notify", mgmt_passkey_notify_evt, 12, true }, { 0x0018, "New Identity Resolving Key", mgmt_new_identity_resolving_key_evt, 30, true }, { 0x0019, "New Signature Resolving Key", mgmt_new_signature_resolving_key_evt, 25, true }, { 0x001a, "Device Added", mgmt_device_added_evt, 8, true }, { 0x001b, "Device Removed", mgmt_device_removed_evt, 7, true }, { 0x001c, "New Connection Parameter", mgmt_new_connection_parameter_evt, 16, true }, { 0x001d, "Unconfigured Index Added", mgmt_null_evt, 0, true }, { 0x001e, "Unconfigured Index Removed", mgmt_null_evt, 0, true }, { 0x001f, "New Configuration Options", mgmt_new_conf_options_evt, 4, true }, { 0x0020, "Extended Index Added", mgmt_ext_index_added_evt, 2, true }, { 0x0021, "Extended Index Removed", mgmt_ext_index_removed_evt, 2, true }, { 0x0022, "Local Out Of Band Extended Data Updated", mgmt_local_oob_ext_data_updated_evt, 3, false }, { 0x0023, "Advertising Added", mgmt_advertising_added_evt, 1, true }, { 0x0024, "Advertising Removed", mgmt_advertising_removed_evt, 1, true }, { 0x0025, "Extended Controller Information Changed", mgmt_ext_controller_info_changed_evt, 2, false }, { 0x0026, "PHY Configuration Changed", mgmt_phy_changed_evt, 4, true }, { 0x0027, "Experimental Feature Changed", mgmt_exp_feature_changed_evt, 20, true }, { 0x002a, "Device Flags Changed", mgmt_device_flags_changed_evt, 15, true }, { 0x002b, "Advertisement Monitor Added", mgmt_adv_monitor_added_evt, 2, true }, { 0x002c, "Advertisement Monitor Removed", mgmt_adv_monitor_removed_evt, 2, true }, { 0x002d, "Controller Suspended", mgmt_controller_suspend_evt, 1, true }, { 0x002e, "Controller Resumed", mgmt_controller_resume_evt, 8, true }, { 0x002f, "ADV Monitor Device Found", mgmt_adv_monitor_device_found_evt, 16, false }, { 0x0030, "ADV Monitor Device Lost", mgmt_adv_monitor_device_lost_evt, 9, true }, { 0x0031, "Mesh Device Found", mgmt_mesh_device_found_evt, 22, false }, { 0x0032, "Mesh Packet Complete", mgmt_mesh_packet_cmplt_evt, 1, true }, { } }; static void mgmt_print_commands(const void *data, uint16_t num) { int i; print_field("Commands: %u", num); for (i = 0; i < num; i++) { uint16_t opcode = get_le16(data + (i * 2)); const char *str = NULL; int n; for (n = 0; mgmt_command_table[n].str; n++) { if (mgmt_command_table[n].opcode == opcode) { str = mgmt_command_table[n].str; break; } } print_field(" %s (0x%4.4x)", str ?: "Reserved", opcode); } } static void mgmt_print_events(const void *data, uint16_t num) { int i; print_field("Events: %u", num); for (i = 0; i < num; i++) { uint16_t opcode = get_le16(data + (i * 2)); const char *str = NULL; int n; for (n = 0; mgmt_event_table[n].str; n++) { if (mgmt_event_table[n].opcode == opcode) { str = mgmt_event_table[n].str; break; } } print_field(" %s (0x%4.4x)", str ?: "Reserved", opcode); } } void packet_ctrl_command(struct timeval *tv, struct ucred *cred, uint16_t index, const void *data, uint16_t size) { uint32_t cookie; uint16_t format, opcode; const struct mgmt_data *mgmt_data = NULL; const char *mgmt_color, *mgmt_str; char channel[11], extra_str[25]; int i; if (size < 4) { print_packet(tv, cred, '*', index, NULL, COLOR_ERROR, "Malformed Control Command packet", NULL, NULL); packet_hexdump(data, size); return; } cookie = get_le32(data); data += 4; size -= 4; sprintf(channel, "0x%4.4x", cookie); format = get_format(cookie); if (format != CTRL_MGMT) { char label[7]; sprintf(label, "0x%4.4x", format); print_packet(tv, cred, '@', index, channel, COLOR_CTRL_CLOSE, "Control Command", label, NULL); packet_hexdump(data, size); return; } if (size < 2) { print_packet(tv, cred, '*', index, NULL, COLOR_ERROR, "Malformed MGMT Command packet", NULL, NULL); packet_hexdump(data, size); return; } opcode = get_le16(data); data += 2; size -= 2; for (i = 0; mgmt_command_table[i].str; i++) { if (mgmt_command_table[i].opcode == opcode) { mgmt_data = &mgmt_command_table[i]; break; } } if (mgmt_data) { if (mgmt_data->func) mgmt_color = COLOR_CTRL_COMMAND; else mgmt_color = COLOR_CTRL_COMMAND_UNKNOWN; mgmt_str = mgmt_data->str; } else { mgmt_color = COLOR_CTRL_COMMAND_UNKNOWN; mgmt_str = "Unknown"; } sprintf(extra_str, "(0x%4.4x) plen %d", opcode, size); print_packet(tv, cred, '@', index, channel, mgmt_color, "MGMT Command", mgmt_str, extra_str); if (!mgmt_data || !mgmt_data->func) { packet_hexdump(data, size); return; } if (mgmt_data->fixed) { if (size != mgmt_data->size) { print_text(COLOR_ERROR, "invalid packet size"); packet_hexdump(data, size); return; } } else { if (size < mgmt_data->size) { print_text(COLOR_ERROR, "too short packet"); packet_hexdump(data, size); return; } } mgmt_data->func(data, size); } void packet_ctrl_event(struct timeval *tv, struct ucred *cred, uint16_t index, const void *data, uint16_t size) { uint32_t cookie; uint16_t format, opcode; const struct mgmt_data *mgmt_data = NULL; const char *mgmt_color, *mgmt_str; char channel[11], extra_str[25]; int i; if (size < 4) { print_packet(tv, cred, '*', index, NULL, COLOR_ERROR, "Malformed Control Event packet", NULL, NULL); packet_hexdump(data, size); return; } cookie = get_le32(data); data += 4; size -= 4; sprintf(channel, "0x%4.4x", cookie); format = get_format(cookie); if (format != CTRL_MGMT) { char label[7]; sprintf(label, "0x%4.4x", format); print_packet(tv, cred, '@', index, channel, COLOR_CTRL_CLOSE, "Control Event", label, NULL); packet_hexdump(data, size); return; } if (size < 2) { print_packet(tv, cred, '*', index, NULL, COLOR_ERROR, "Malformed MGMT Event packet", NULL, NULL); packet_hexdump(data, size); return; } opcode = get_le16(data); data += 2; size -= 2; for (i = 0; mgmt_event_table[i].str; i++) { if (mgmt_event_table[i].opcode == opcode) { mgmt_data = &mgmt_event_table[i]; break; } } if (mgmt_data) { if (mgmt_data->func) mgmt_color = COLOR_CTRL_EVENT; else mgmt_color = COLOR_CTRL_EVENT_UNKNOWN; mgmt_str = mgmt_data->str; } else { mgmt_color = COLOR_CTRL_EVENT_UNKNOWN; mgmt_str = "Unknown"; } sprintf(extra_str, "(0x%4.4x) plen %d", opcode, size); print_packet(tv, cred, '@', index, channel, mgmt_color, "MGMT Event", mgmt_str, extra_str); if (!mgmt_data || !mgmt_data->func) { packet_hexdump(data, size); return; } if (mgmt_data->fixed) { if (size != mgmt_data->size) { print_text(COLOR_ERROR, "invalid packet size"); packet_hexdump(data, size); return; } } else { if (size < mgmt_data->size) { print_text(COLOR_ERROR, "too short packet"); packet_hexdump(data, size); return; } } mgmt_data->func(data, size); } void packet_todo(void) { int i; printf("HCI commands with missing decodings:\n"); for (i = 0; opcode_table[i].str; i++) { if (opcode_table[i].bit < 0) continue; if (opcode_table[i].cmd_func) continue; printf("\t%s\n", opcode_table[i].str); } printf("HCI events with missing decodings:\n"); for (i = 0; event_table[i].str; i++) { if (event_table[i].func) continue; printf("\t%s\n", event_table[i].str); } for (i = 0; le_meta_event_table[i].str; i++) { if (le_meta_event_table[i].func) continue; printf("\t%s\n", le_meta_event_table[i].str); } } bluez-5.82/monitor/PaxHeaders/bt.h0000644000000000000000000000005014766002272014121 xustar0020 atime=1743515578 20 ctime=1743591281 bluez-5.82/monitor/bt.h0000644000000000000000000031323314766002272013607 0ustar00rootroot/* SPDX-License-Identifier: LGPL-2.1-or-later */ /* * * BlueZ - Bluetooth protocol stack for Linux * * Copyright (C) 2011-2014 Intel Corporation * Copyright (C) 2002-2010 Marcel Holtmann * Copyright 2023 NXP * * */ #include #define BT_HCI_CMD_BIT(_byte, _bit) ((8 * _byte) + _bit) struct bt_ll_hdr { uint8_t preamble; uint32_t access_addr; } __attribute__ ((packed)); #define BT_LL_CONN_UPDATE_REQ 0x00 struct bt_ll_conn_update_req { uint8_t win_size; uint16_t win_offset; uint16_t interval; uint16_t latency; uint16_t timeout; uint16_t instant; } __attribute__ ((packed)); #define BT_LL_CHANNEL_MAP_REQ 0x01 struct bt_ll_channel_map_req { uint8_t map[5]; uint16_t instant; } __attribute__ ((packed)); #define BT_LL_TERMINATE_IND 0x02 struct bt_ll_terminate_ind { uint8_t error; } __attribute__ ((packed)); #define BT_LL_ENC_REQ 0x03 struct bt_ll_enc_req { uint64_t rand; uint16_t ediv; uint64_t skd; uint32_t iv; } __attribute__ ((packed)); #define BT_LL_ENC_RSP 0x04 struct bt_ll_enc_rsp { uint64_t skd; uint32_t iv; } __attribute__ ((packed)); #define BT_LL_START_ENC_REQ 0x05 #define BT_LL_START_ENC_RSP 0x06 #define BT_LL_UNKNOWN_RSP 0x07 struct bt_ll_unknown_rsp { uint8_t type; } __attribute__ ((packed)); #define BT_LL_FEATURE_REQ 0x08 struct bt_ll_feature_req { uint8_t features[8]; } __attribute__ ((packed)); #define BT_LL_FEATURE_RSP 0x09 struct bt_ll_feature_rsp { uint8_t features[8]; } __attribute__ ((packed)); #define BT_LL_PAUSE_ENC_REQ 0x0a #define BT_LL_PAUSE_ENC_RSP 0x0b #define BT_LL_VERSION_IND 0x0c struct bt_ll_version_ind { uint8_t version; uint16_t company; uint16_t subversion; } __attribute__ ((packed)); #define BT_LL_REJECT_IND 0x0d struct bt_ll_reject_ind { uint8_t error; } __attribute__ ((packed)); #define BT_LL_PERIPHERAL_FEATURE_REQ 0x0e struct bt_ll_peripheral_feature_req { uint8_t features[8]; } __attribute__ ((packed)); #define BT_LL_CONN_PARAM_REQ 0x0f struct bt_ll_conn_param_req { uint16_t interval_min; uint16_t interval_max; uint16_t latency; uint16_t timeout; uint8_t pref_period; uint16_t pref_conn_evt_count; uint8_t offset_0; uint8_t offset_1; uint8_t offset_2; uint8_t offset_3; uint8_t offset_4; uint8_t offset_5; } __attribute__ ((packed)); #define BT_LL_CONN_PARAM_RSP 0x10 struct bt_ll_conn_param_rsp { uint16_t interval_min; uint16_t interval_max; uint16_t latency; uint16_t timeout; uint8_t pref_period; uint16_t pref_conn_evt_count; uint8_t offset_0; uint8_t offset_1; uint8_t offset_2; uint8_t offset_3; uint8_t offset_4; uint8_t offset_5; } __attribute__ ((packed)); #define BT_LL_REJECT_IND_EXT 0x11 struct bt_ll_reject_ind_ext { uint8_t opcode; uint8_t error; } __attribute__ ((packed)); #define BT_LL_PING_REQ 0x12 #define BT_LL_PING_RSP 0x13 #define BT_LL_LENGTH_REQ 0x14 struct bt_ll_length { uint16_t rx_len; uint16_t rx_time; uint16_t tx_len; uint16_t tx_time; } __attribute__ ((packed)); #define BT_LL_LENGTH_RSP 0x15 #define BT_LL_PHY_REQ 0x16 struct bt_ll_phy { uint8_t tx_phys; uint8_t rx_phys; } __attribute__ ((packed)); #define BT_LL_PHY_RSP 0x17 #define BT_LL_PHY_UPDATE_IND 0x18 struct bt_ll_phy_update_ind { uint8_t c_phy; uint8_t p_phy; uint16_t instant; } __attribute__ ((packed)); #define BT_LL_MIN_USED_CHANNELS 0x19 struct bt_ll_min_used_channels { uint8_t phys; uint8_t min_channels; } __attribute__ ((packed)); #define BT_LL_CTE_REQ 0x1a struct bt_ll_cte_req { uint8_t cte; } __attribute__ ((packed)); #define BT_LL_CTE_RSP 0x1b #define BT_LL_PERIODIC_SYNC_IND 0x1c struct bt_ll_periodic_sync_ind { uint16_t id; uint8_t info[18]; uint16_t event_count; uint16_t last_counter; uint8_t adv_info; uint8_t phy; uint8_t adv_addr[6]; uint16_t sync_counter; } __attribute__ ((packed)); #define BT_LL_CLOCK_ACCURACY_REQ 0x1d struct bt_ll_clock_acc { uint8_t sca; } __attribute__ ((packed)); #define BT_LL_CLOCK_ACCURACY_RSP 0x1e #define BT_LL_CIS_REQ 0x1f struct bt_ll_cis_req { uint8_t cig; uint8_t cis; uint8_t c_phy; uint8_t p_phy; uint16_t c_sdu; uint16_t p_sdu; uint8_t c_interval[3]; uint8_t p_interval[3]; uint8_t c_pdu; uint8_t p_pdu; uint8_t nse; uint8_t sub_interval[3]; uint8_t bn; uint8_t c_ft; uint8_t p_ft; uint16_t iso_interval; uint8_t offset_min[3]; uint8_t offset_max[3]; uint16_t conn_event_count; } __attribute__ ((packed)); #define BT_LL_CIS_RSP 0x20 struct bt_ll_cis_rsp { uint8_t offset_min[3]; uint8_t offset_max[3]; uint16_t conn_event_count; } __attribute__ ((packed)); #define BT_LL_CIS_IND 0x21 struct bt_ll_cis_ind { uint32_t addr; uint8_t cis_offset[3]; uint8_t cig_sync_delay[3]; uint8_t cis_sync_delay[3]; uint16_t conn_event_count; } __attribute__ ((packed)); #define BT_LL_CIS_TERMINATE_IND 0x22 struct bt_ll_cis_term_ind { uint8_t cig; uint8_t cis; uint8_t reason; } __attribute__ ((packed)); #define LMP_ESC4(x) ((127 << 8) | (x)) #define BT_LMP_NAME_REQ 1 struct bt_lmp_name_req { uint8_t offset; } __attribute__ ((packed)); #define BT_LMP_NAME_RSP 2 struct bt_lmp_name_rsp { uint8_t offset; uint8_t length; uint8_t fragment[14]; } __attribute__ ((packed)); #define BT_LMP_ACCEPTED 3 struct bt_lmp_accepted { uint8_t opcode; } __attribute__ ((packed)); #define BT_LMP_NOT_ACCEPTED 4 struct bt_lmp_not_accepted { uint8_t opcode; uint8_t error; } __attribute__ ((packed)); #define BT_LMP_CLKOFFSET_REQ 5 #define BT_LMP_CLKOFFSET_RSP 6 struct bt_lmp_clkoffset_rsp { uint16_t offset; } __attribute__ ((packed)); #define BT_LMP_DETACH 7 struct bt_lmp_detach { uint8_t error; } __attribute__ ((packed)); #define BT_LMP_AU_RAND 11 struct bt_lmp_au_rand { uint8_t number[16]; } __attribute__ ((packed)); #define BT_LMP_SRES 12 struct bt_lmp_sres { uint8_t response[4]; } __attribute__ ((packed)); #define BT_LMP_ENCRYPTION_MODE_REQ 15 struct bt_lmp_encryption_mode_req { uint8_t mode; } __attribute__ ((packed)); #define BT_LMP_ENCRYPTION_KEY_SIZE_REQ 16 struct bt_lmp_encryption_key_size_req { uint8_t key_size; } __attribute__ ((packed)); #define BT_LMP_START_ENCRYPTION_REQ 17 struct bt_lmp_start_encryption_req { uint8_t number[16]; } __attribute__ ((packed)); #define BT_LMP_STOP_ENCRYPTION_REQ 18 #define BT_LMP_SWITCH_REQ 19 struct bt_lmp_switch_req { uint32_t instant; } __attribute__ ((packed)); #define BT_LMP_UNSNIFF_REQ 24 #define BT_LMP_MAX_POWER 33 #define BT_LMP_MIN_POWER 34 #define BT_LMP_AUTO_RATE 35 #define BT_LMP_PREFERRED_RATE 36 struct bt_lmp_preferred_rate { uint8_t rate; } __attribute__ ((packed)); #define BT_LMP_VERSION_REQ 37 struct bt_lmp_version_req { uint8_t version; uint16_t company; uint16_t subversion; } __attribute__ ((packed)); #define BT_LMP_VERSION_RES 38 struct bt_lmp_version_res { uint8_t version; uint16_t company; uint16_t subversion; } __attribute__ ((packed)); #define BT_LMP_FEATURES_REQ 39 struct bt_lmp_features_req { uint8_t features[8]; } __attribute__ ((packed)); #define BT_LMP_FEATURES_RES 40 struct bt_lmp_features_res { uint8_t features[8]; } __attribute__ ((packed)); #define BT_LMP_MAX_SLOT 45 struct bt_lmp_max_slot { uint8_t slots; } __attribute__ ((packed)); #define BT_LMP_MAX_SLOT_REQ 46 struct bt_lmp_max_slot_req { uint8_t slots; } __attribute__ ((packed)); #define BT_LMP_TIMING_ACCURACY_REQ 47 #define BT_LMP_TIMING_ACCURACY_RES 48 struct bt_lmp_timing_accuracy_res { uint8_t drift; uint8_t jitter; } __attribute__ ((packed)); #define BT_LMP_SETUP_COMPLETE 49 #define BT_LMP_USE_SEMI_PERMANENT_KEY 50 #define BT_LMP_HOST_CONNECTION_REQ 51 #define BT_LMP_SLOT_OFFSET 52 struct bt_lmp_slot_offset { uint16_t offset; uint8_t bdaddr[6]; } __attribute__ ((packed)); #define BT_LMP_PAGE_SCAN_MODE_REQ 54 struct bt_lmp_page_scan_mode_req { uint8_t scheme; uint8_t settings; } __attribute__ ((packed)); #define BT_LMP_TEST_ACTIVATE 56 #define BT_LMP_ENCRYPTION_KEY_SIZE_MASK_REQ 58 #define BT_LMP_SET_AFH 60 struct bt_lmp_set_afh { uint32_t instant; uint8_t mode; uint8_t map[10]; } __attribute__ ((packed)); #define BT_LMP_ENCAPSULATED_HEADER 61 struct bt_lmp_encapsulated_header { uint8_t major; uint8_t minor; uint8_t length; } __attribute__ ((packed)); #define BT_LMP_ENCAPSULATED_PAYLOAD 62 struct bt_lmp_encapsulated_payload { uint8_t data[16]; } __attribute__ ((packed)); #define BT_LMP_SIMPLE_PAIRING_CONFIRM 63 struct bt_lmp_simple_pairing_confirm { uint8_t value[16]; } __attribute__ ((packed)); #define BT_LMP_SIMPLE_PAIRING_NUMBER 64 struct bt_lmp_simple_pairing_number { uint8_t value[16]; } __attribute__ ((packed)); #define BT_LMP_DHKEY_CHECK 65 struct bt_lmp_dhkey_check { uint8_t value[16]; } __attribute__ ((packed)); #define BT_LMP_PAUSE_ENCRYPTION_AES_REQ 66 #define BT_LMP_ACCEPTED_EXT LMP_ESC4(1) struct bt_lmp_accepted_ext { uint8_t escape; uint8_t opcode; } __attribute__ ((packed)); #define BT_LMP_NOT_ACCEPTED_EXT LMP_ESC4(2) struct bt_lmp_not_accepted_ext { uint8_t escape; uint8_t opcode; uint8_t error; } __attribute__ ((packed)); #define BT_LMP_FEATURES_REQ_EXT LMP_ESC4(3) struct bt_lmp_features_req_ext { uint8_t page; uint8_t max_page; uint8_t features[8]; } __attribute__ ((packed)); #define BT_LMP_FEATURES_RES_EXT LMP_ESC4(4) struct bt_lmp_features_res_ext { uint8_t page; uint8_t max_page; uint8_t features[8]; } __attribute__ ((packed)); #define BT_LMP_PACKET_TYPE_TABLE_REQ LMP_ESC4(11) struct bt_lmp_packet_type_table_req { uint8_t table; } __attribute__ ((packed)); #define BT_LMP_CHANNEL_CLASSIFICATION_REQ LMP_ESC4(16) struct bt_lmp_channel_classification_req { uint8_t mode; uint16_t min_interval; uint16_t max_interval; } __attribute__ ((packed)); #define BT_LMP_CHANNEL_CLASSIFICATION LMP_ESC4(17) struct bt_lmp_channel_classification { uint8_t classification[10]; } __attribute__ ((packed)); #define BT_LMP_PAUSE_ENCRYPTION_REQ LMP_ESC4(23) #define BT_LMP_RESUME_ENCRYPTION_REQ LMP_ESC4(24) #define BT_LMP_IO_CAPABILITY_REQ LMP_ESC4(25) struct bt_lmp_io_capability_req { uint8_t capability; uint8_t oob_data; uint8_t authentication; } __attribute__ ((packed)); #define BT_LMP_IO_CAPABILITY_RES LMP_ESC4(26) struct bt_lmp_io_capability_res { uint8_t capability; uint8_t oob_data; uint8_t authentication; } __attribute__ ((packed)); #define BT_LMP_NUMERIC_COMPARISON_FAILED LMP_ESC(27) #define BT_LMP_PASSKEY_FAILED LMP_ESC4(28) #define BT_LMP_OOB_FAILED LMP_ESC(29) #define BT_LMP_POWER_CONTROL_REQ LMP_ESC4(31) struct bt_lmp_power_control_req { uint8_t request; } __attribute__ ((packed)); #define BT_LMP_POWER_CONTROL_RES LMP_ESC4(32) struct bt_lmp_power_control_res { uint8_t response; } __attribute__ ((packed)); #define BT_LMP_PING_REQ LMP_ESC4(33) #define BT_LMP_PING_RES LMP_ESC4(34) #define BT_H4_CMD_PKT 0x01 #define BT_H4_ACL_PKT 0x02 #define BT_H4_SCO_PKT 0x03 #define BT_H4_EVT_PKT 0x04 #define BT_H4_ISO_PKT 0x05 struct bt_hci_cmd_hdr { uint16_t opcode; uint8_t plen; } __attribute__ ((packed)); struct bt_hci_acl_hdr { uint16_t handle; uint16_t dlen; uint8_t data[]; } __attribute__ ((packed)); struct bt_hci_sco_hdr { uint16_t handle; uint8_t dlen; uint8_t data[]; } __attribute__ ((packed)); struct bt_hci_iso_hdr { uint16_t handle; uint16_t dlen; uint8_t data[]; } __attribute__ ((packed)); struct bt_hci_iso_data_start { uint16_t sn; uint16_t slen; uint8_t data[]; } __attribute__ ((packed)); struct bt_hci_evt_hdr { uint8_t evt; uint8_t plen; } __attribute__ ((packed)); #define BT_HCI_CMD_NOP 0x0000 #define BT_HCI_CMD_INQUIRY 0x0401 struct bt_hci_cmd_inquiry { uint8_t lap[3]; uint8_t length; uint8_t num_resp; } __attribute__ ((packed)); #define BT_HCI_CMD_INQUIRY_CANCEL 0x0402 #define BT_HCI_CMD_PERIODIC_INQUIRY 0x0403 struct bt_hci_cmd_periodic_inquiry { uint16_t max_period; uint16_t min_period; uint8_t lap[3]; uint8_t length; uint8_t num_resp; } __attribute__ ((packed)); #define BT_HCI_CMD_EXIT_PERIODIC_INQUIRY 0x0404 #define BT_HCI_CMD_CREATE_CONN 0x0405 struct bt_hci_cmd_create_conn { uint8_t bdaddr[6]; uint16_t pkt_type; uint8_t pscan_rep_mode; uint8_t pscan_mode; uint16_t clock_offset; uint8_t role_switch; } __attribute__ ((packed)); #define BT_HCI_CMD_DISCONNECT 0x0406 struct bt_hci_cmd_disconnect { uint16_t handle; uint8_t reason; } __attribute__ ((packed)); #define BT_HCI_CMD_ADD_SCO_CONN 0x0407 struct bt_hci_cmd_add_sco_conn { uint16_t handle; uint16_t pkt_type; } __attribute__ ((packed)); #define BT_HCI_CMD_CREATE_CONN_CANCEL 0x0408 struct bt_hci_cmd_create_conn_cancel { uint8_t bdaddr[6]; } __attribute__ ((packed)); struct bt_hci_rsp_create_conn_cancel { uint8_t status; uint8_t bdaddr[6]; } __attribute__ ((packed)); #define BT_HCI_CMD_ACCEPT_CONN_REQUEST 0x0409 struct bt_hci_cmd_accept_conn_request { uint8_t bdaddr[6]; uint8_t role; } __attribute__ ((packed)); #define BT_HCI_CMD_REJECT_CONN_REQUEST 0x040a struct bt_hci_cmd_reject_conn_request { uint8_t bdaddr[6]; uint8_t reason; } __attribute__ ((packed)); #define BT_HCI_CMD_LINK_KEY_REQUEST_REPLY 0x040b struct bt_hci_cmd_link_key_request_reply { uint8_t bdaddr[6]; uint8_t link_key[16]; } __attribute__ ((packed)); struct bt_hci_rsp_link_key_request_reply { uint8_t status; uint8_t bdaddr[6]; } __attribute__ ((packed)); #define BT_HCI_CMD_LINK_KEY_REQUEST_NEG_REPLY 0x040c struct bt_hci_cmd_link_key_request_neg_reply { uint8_t bdaddr[6]; } __attribute__ ((packed)); struct bt_hci_rsp_link_key_request_neg_reply { uint8_t status; uint8_t bdaddr[6]; } __attribute__ ((packed)); #define BT_HCI_CMD_PIN_CODE_REQUEST_REPLY 0x040d struct bt_hci_cmd_pin_code_request_reply { uint8_t bdaddr[6]; uint8_t pin_len; uint8_t pin_code[16]; } __attribute__ ((packed)); #define BT_HCI_CMD_PIN_CODE_REQUEST_NEG_REPLY 0x040e struct bt_hci_cmd_pin_code_request_neg_reply { uint8_t bdaddr[6]; } __attribute__ ((packed)); struct bt_hci_rsp_pin_code_request_neg_reply { uint8_t status; uint8_t bdaddr[6]; } __attribute__ ((packed)); #define BT_HCI_CMD_CHANGE_CONN_PKT_TYPE 0x040f struct bt_hci_cmd_change_conn_pkt_type { uint16_t handle; uint16_t pkt_type; } __attribute__ ((packed)); #define BT_HCI_CMD_AUTH_REQUESTED 0x0411 struct bt_hci_cmd_auth_requested { uint16_t handle; } __attribute__ ((packed)); #define BT_HCI_CMD_SET_CONN_ENCRYPT 0x0413 struct bt_hci_cmd_set_conn_encrypt { uint16_t handle; uint8_t encr_mode; } __attribute__ ((packed)); #define BT_HCI_CMD_CHANGE_CONN_LINK_KEY 0x0415 struct bt_hci_cmd_change_conn_link_key { uint16_t handle; } __attribute__ ((packed)); #define BT_HCI_CMD_LINK_KEY_SELECTION 0x0417 struct bt_hci_cmd_link_key_selection { uint8_t key_flag; } __attribute__ ((packed)); #define BT_HCI_CMD_REMOTE_NAME_REQUEST 0x0419 struct bt_hci_cmd_remote_name_request { uint8_t bdaddr[6]; uint8_t pscan_rep_mode; uint8_t pscan_mode; uint16_t clock_offset; } __attribute__ ((packed)); #define BT_HCI_CMD_REMOTE_NAME_REQUEST_CANCEL 0x041a struct bt_hci_cmd_remote_name_request_cancel { uint8_t bdaddr[6]; } __attribute__ ((packed)); struct bt_hci_rsp_remote_name_request_cancel { uint8_t status; uint8_t bdaddr[6]; } __attribute__ ((packed)); #define BT_HCI_CMD_READ_REMOTE_FEATURES 0x041b struct bt_hci_cmd_read_remote_features { uint16_t handle; } __attribute__ ((packed)); #define BT_HCI_CMD_READ_REMOTE_EXT_FEATURES 0x041c struct bt_hci_cmd_read_remote_ext_features { uint16_t handle; uint8_t page; } __attribute__ ((packed)); #define BT_HCI_CMD_READ_REMOTE_VERSION 0x041d struct bt_hci_cmd_read_remote_version { uint16_t handle; } __attribute__ ((packed)); #define BT_HCI_CMD_READ_CLOCK_OFFSET 0x041f struct bt_hci_cmd_read_clock_offset { uint16_t handle; } __attribute__ ((packed)); #define BT_HCI_CMD_READ_LMP_HANDLE 0x0420 struct bt_hci_cmd_read_lmp_handle { uint16_t handle; } __attribute__ ((packed)); struct bt_hci_rsp_read_lmp_handle { uint8_t status; uint16_t handle; uint8_t lmp_handle; uint32_t reserved; } __attribute__ ((packed)); #define BT_HCI_CMD_SETUP_SYNC_CONN 0x0428 struct bt_hci_cmd_setup_sync_conn { uint16_t handle; uint32_t tx_bandwidth; uint32_t rx_bandwidth; uint16_t max_latency; uint16_t voice_setting; uint8_t retrans_effort; uint16_t pkt_type; } __attribute__ ((packed)); #define BT_HCI_CMD_ACCEPT_SYNC_CONN_REQUEST 0x0429 struct bt_hci_cmd_accept_sync_conn_request { uint8_t bdaddr[6]; uint32_t tx_bandwidth; uint32_t rx_bandwidth; uint16_t max_latency; uint16_t voice_setting; uint8_t retrans_effort; uint16_t pkt_type; } __attribute__ ((packed)); #define BT_HCI_CMD_REJECT_SYNC_CONN_REQUEST 0x042a struct bt_hci_cmd_reject_sync_conn_request { uint8_t bdaddr[6]; uint8_t reason; } __attribute__ ((packed)); #define BT_HCI_CMD_IO_CAPABILITY_REQUEST_REPLY 0x042b struct bt_hci_cmd_io_capability_request_reply { uint8_t bdaddr[6]; uint8_t capability; uint8_t oob_data; uint8_t authentication; } __attribute__ ((packed)); struct bt_hci_rsp_io_capability_request_reply { uint8_t status; uint8_t bdaddr[6]; } __attribute__ ((packed)); #define BT_HCI_CMD_USER_CONFIRM_REQUEST_REPLY 0x042c struct bt_hci_cmd_user_confirm_request_reply { uint8_t bdaddr[6]; } __attribute__ ((packed)); struct bt_hci_rsp_user_confirm_request_reply { uint8_t status; uint8_t bdaddr[6]; } __attribute__ ((packed)); #define BT_HCI_CMD_USER_CONFIRM_REQUEST_NEG_REPLY 0x042d struct bt_hci_cmd_user_confirm_request_neg_reply { uint8_t bdaddr[6]; } __attribute__ ((packed)); struct bt_hci_rsp_user_confirm_request_neg_reply { uint8_t status; uint8_t bdaddr[6]; } __attribute__ ((packed)); #define BT_HCI_CMD_USER_PASSKEY_REQUEST_REPLY 0x042e struct bt_hci_cmd_user_passkey_request_reply { uint8_t bdaddr[6]; uint32_t passkey; } __attribute__ ((packed)); #define BT_HCI_CMD_USER_PASSKEY_REQUEST_NEG_REPLY 0x042f struct bt_hci_cmd_user_passkey_request_neg_reply { uint8_t bdaddr[6]; } __attribute__ ((packed)); #define BT_HCI_CMD_REMOTE_OOB_DATA_REQUEST_REPLY 0x0430 struct bt_hci_cmd_remote_oob_data_request_reply { uint8_t bdaddr[6]; uint8_t hash[16]; uint8_t randomizer[16]; } __attribute__ ((packed)); #define BT_HCI_CMD_REMOTE_OOB_DATA_REQUEST_NEG_REPLY 0x0433 struct bt_hci_cmd_remote_oob_data_request_neg_reply { uint8_t bdaddr[6]; } __attribute__ ((packed)); #define BT_HCI_CMD_IO_CAPABILITY_REQUEST_NEG_REPLY 0x0434 struct bt_hci_cmd_io_capability_request_neg_reply { uint8_t bdaddr[6]; uint8_t reason; } __attribute__ ((packed)); struct bt_hci_rsp_io_capability_request_neg_reply { uint8_t status; uint8_t bdaddr[6]; } __attribute__ ((packed)); #define BT_HCI_CMD_CREATE_PHY_LINK 0x0435 struct bt_hci_cmd_create_phy_link { uint8_t phy_handle; uint8_t key_len; uint8_t key_type; } __attribute__ ((packed)); #define BT_HCI_CMD_ACCEPT_PHY_LINK 0x0436 struct bt_hci_cmd_accept_phy_link { uint8_t phy_handle; uint8_t key_len; uint8_t key_type; } __attribute__ ((packed)); #define BT_HCI_CMD_DISCONN_PHY_LINK 0x0437 struct bt_hci_cmd_disconn_phy_link { uint8_t phy_handle; uint8_t reason; } __attribute__ ((packed)); #define BT_HCI_CMD_CREATE_LOGIC_LINK 0x0438 struct bt_hci_cmd_create_logic_link { uint8_t phy_handle; uint8_t tx_flow_spec[16]; uint8_t rx_flow_spec[16]; } __attribute__ ((packed)); #define BT_HCI_CMD_ACCEPT_LOGIC_LINK 0x0439 struct bt_hci_cmd_accept_logic_link { uint8_t phy_handle; uint8_t tx_flow_spec[16]; uint8_t rx_flow_spec[16]; } __attribute__ ((packed)); #define BT_HCI_CMD_DISCONN_LOGIC_LINK 0x043a struct bt_hci_cmd_disconn_logic_link { uint16_t handle; } __attribute__ ((packed)); #define BT_HCI_CMD_LOGIC_LINK_CANCEL 0x043b struct bt_hci_cmd_logic_link_cancel { uint8_t phy_handle; uint8_t flow_spec; } __attribute__ ((packed)); struct bt_hci_rsp_logic_link_cancel { uint8_t status; uint8_t phy_handle; uint8_t flow_spec; } __attribute__ ((packed)); #define BT_HCI_CMD_FLOW_SPEC_MODIFY 0x043c struct bt_hci_cmd_flow_spec_modify { uint16_t handle; uint8_t tx_flow_spec[16]; uint8_t rx_flow_spec[16]; } __attribute__ ((packed)); #define BT_HCI_CMD_ENHANCED_SETUP_SYNC_CONN 0x043d struct bt_hci_cmd_enhanced_setup_sync_conn { uint16_t handle; uint32_t tx_bandwidth; uint32_t rx_bandwidth; uint8_t tx_coding_format[5]; uint8_t rx_coding_format[5]; uint16_t tx_codec_frame_size; uint16_t rx_codec_frame_size; uint32_t input_bandwidth; uint32_t output_bandwidth; uint8_t input_coding_format[5]; uint8_t output_coding_format[5]; uint16_t input_coded_data_size; uint16_t output_coded_data_size; uint8_t input_pcm_data_format; uint8_t output_pcm_data_format; uint8_t input_pcm_msb_position; uint8_t output_pcm_msb_position; uint8_t input_data_path; uint8_t output_data_path; uint8_t input_unit_size; uint8_t output_unit_size; uint16_t max_latency; uint16_t pkt_type; uint8_t retrans_effort; } __attribute__ ((packed)); #define BT_HCI_CMD_ENHANCED_ACCEPT_SYNC_CONN_REQUEST 0x043e struct bt_hci_cmd_enhanced_accept_sync_conn_request { uint8_t bdaddr[6]; uint32_t tx_bandwidth; uint32_t rx_bandwidth; uint8_t tx_coding_format[5]; uint8_t rx_coding_format[5]; uint16_t tx_codec_frame_size; uint16_t rx_codec_frame_size; uint32_t input_bandwidth; uint32_t output_bandwidth; uint8_t input_coding_format[5]; uint8_t output_coding_format[5]; uint16_t input_coded_data_size; uint16_t output_coded_data_size; uint8_t input_pcm_data_format; uint8_t output_pcm_data_format; uint8_t input_pcm_msb_position; uint8_t output_pcm_msb_position; uint8_t input_data_path; uint8_t output_data_path; uint8_t input_unit_size; uint8_t output_unit_size; uint16_t max_latency; uint16_t pkt_type; uint8_t retrans_effort; } __attribute__ ((packed)); #define BT_HCI_CMD_TRUNCATED_PAGE 0x043f struct bt_hci_cmd_truncated_page { uint8_t bdaddr[6]; uint8_t pscan_rep_mode; uint16_t clock_offset; } __attribute__ ((packed)); #define BT_HCI_CMD_TRUNCATED_PAGE_CANCEL 0x0440 struct bt_hci_cmd_truncated_page_cancel { uint8_t bdaddr[6]; } __attribute__ ((packed)); #define BT_HCI_CMD_SET_PERIPHERAL_BROADCAST 0x0441 struct bt_hci_cmd_set_peripheral_broadcast { uint8_t enable; uint8_t lt_addr; uint8_t lpo_allowed; uint16_t pkt_type; uint16_t min_interval; uint16_t max_interval; uint16_t timeout; } __attribute__ ((packed)); struct bt_hci_rsp_set_peripheral_broadcast { uint8_t status; uint8_t lt_addr; uint16_t interval; } __attribute__ ((packed)); #define BT_HCI_CMD_SET_PERIPHERAL_BROADCAST_RECEIVE 0x0442 struct bt_hci_cmd_set_peripheral_broadcast_receive { uint8_t enable; uint8_t bdaddr[6]; uint8_t lt_addr; uint16_t interval; uint32_t offset; uint32_t instant; uint16_t timeout; uint8_t accuracy; uint8_t skip; uint16_t pkt_type; uint8_t map[10]; } __attribute__ ((packed)); struct bt_hci_rsp_set_peripheral_broadcast_receive { uint8_t status; uint8_t bdaddr[6]; uint8_t lt_addr; } __attribute__ ((packed)); #define BT_HCI_CMD_START_SYNC_TRAIN 0x0443 #define BT_HCI_CMD_RECEIVE_SYNC_TRAIN 0x0444 struct bt_hci_cmd_receive_sync_train { uint8_t bdaddr[6]; uint16_t timeout; uint16_t window; uint16_t interval; } __attribute__ ((packed)); #define BT_HCI_CMD_REMOTE_OOB_EXT_DATA_REQUEST_REPLY 0x0445 struct bt_hci_cmd_remote_oob_ext_data_request_reply { uint8_t bdaddr[6]; uint8_t hash192[16]; uint8_t randomizer192[16]; uint8_t hash256[16]; uint8_t randomizer256[16]; } __attribute__ ((packed)); #define BT_HCI_CMD_HOLD_MODE 0x0801 struct bt_hci_cmd_hold_mode { uint16_t handle; uint16_t max_interval; uint16_t min_interval; } __attribute__ ((packed)); #define BT_HCI_CMD_SNIFF_MODE 0x0803 struct bt_hci_cmd_sniff_mode { uint16_t handle; uint16_t max_interval; uint16_t min_interval; uint16_t attempt; uint16_t timeout; } __attribute__ ((packed)); #define BT_HCI_CMD_EXIT_SNIFF_MODE 0x0804 struct bt_hci_cmd_exit_sniff_mode { uint16_t handle; } __attribute__ ((packed)); #define BT_HCI_CMD_PARK_STATE 0x0805 struct bt_hci_cmd_park_state { uint16_t handle; uint16_t max_interval; uint16_t min_interval; } __attribute__ ((packed)); #define BT_HCI_CMD_EXIT_PARK_STATE 0x0806 struct bt_hci_cmd_exit_park_state { uint16_t handle; } __attribute__ ((packed)); #define BT_HCI_CMD_QOS_SETUP 0x0807 struct bt_hci_cmd_qos_setup { uint16_t handle; uint8_t flags; uint8_t service_type; uint32_t token_rate; uint32_t peak_bandwidth; uint32_t latency; uint32_t delay_variation; } __attribute__ ((packed)); #define BT_HCI_CMD_ROLE_DISCOVERY 0x0809 struct bt_hci_cmd_role_discovery { uint16_t handle; } __attribute__ ((packed)); struct bt_hci_rsp_role_discovery { uint8_t status; uint16_t handle; uint8_t role; } __attribute__ ((packed)); #define BT_HCI_CMD_SWITCH_ROLE 0x080b struct bt_hci_cmd_switch_role { uint8_t bdaddr[6]; uint8_t role; } __attribute__ ((packed)); #define BT_HCI_CMD_READ_LINK_POLICY 0x080c struct bt_hci_cmd_read_link_policy { uint16_t handle; } __attribute__ ((packed)); struct bt_hci_rsp_read_link_policy { uint8_t status; uint16_t handle; uint16_t policy; } __attribute__ ((packed)); #define BT_HCI_CMD_WRITE_LINK_POLICY 0x080d struct bt_hci_cmd_write_link_policy { uint16_t handle; uint16_t policy; } __attribute__ ((packed)); struct bt_hci_rsp_write_link_policy { uint8_t status; uint16_t handle; } __attribute__ ((packed)); #define BT_HCI_CMD_READ_DEFAULT_LINK_POLICY 0x080e struct bt_hci_rsp_read_default_link_policy { uint8_t status; uint16_t policy; } __attribute__ ((packed)); #define BT_HCI_CMD_WRITE_DEFAULT_LINK_POLICY 0x080f struct bt_hci_cmd_write_default_link_policy { uint16_t policy; } __attribute__ ((packed)); #define BT_HCI_CMD_FLOW_SPEC 0x0810 struct bt_hci_cmd_flow_spec { uint16_t handle; uint8_t flags; uint8_t direction; uint8_t service_type; uint32_t token_rate; uint32_t token_bucket_size; uint32_t peak_bandwidth; uint32_t access_latency; } __attribute__ ((packed)); #define BT_HCI_CMD_SNIFF_SUBRATING 0x0811 struct bt_hci_cmd_sniff_subrating { uint16_t handle; uint16_t max_latency; uint16_t min_remote_timeout; uint16_t min_local_timeout; } __attribute__ ((packed)); struct bt_hci_rsp_sniff_subrating { uint8_t status; uint16_t handle; } __attribute__ ((packed)); #define BT_HCI_CMD_SET_EVENT_MASK 0x0c01 struct bt_hci_cmd_set_event_mask { uint8_t mask[8]; } __attribute__ ((packed)); #define BT_HCI_CMD_RESET 0x0c03 #define BT_HCI_CMD_SET_EVENT_FILTER 0x0c05 struct bt_hci_cmd_set_event_filter { uint8_t type; uint8_t cond_type; uint8_t cond[0]; } __attribute__ ((packed)); #define BT_HCI_CMD_FLUSH 0x0c08 struct bt_hci_cmd_flush { uint16_t handle; } __attribute__ ((packed)); struct bt_hci_rsp_flush { uint8_t status; uint16_t handle; } __attribute__ ((packed)); #define BT_HCI_CMD_READ_PIN_TYPE 0x0c09 struct bt_hci_rsp_read_pin_type { uint8_t status; uint8_t pin_type; } __attribute__ ((packed)); #define BT_HCI_CMD_WRITE_PIN_TYPE 0x0c0a struct bt_hci_cmd_write_pin_type { uint8_t pin_type; } __attribute__ ((packed)); #define BT_HCI_CMD_CREATE_NEW_UNIT_KEY 0x0c0b #define BT_HCI_CMD_READ_STORED_LINK_KEY 0x0c0d struct bt_hci_cmd_read_stored_link_key { uint8_t bdaddr[6]; uint8_t read_all; } __attribute__ ((packed)); struct bt_hci_rsp_read_stored_link_key { uint8_t status; uint16_t max_num_keys; uint16_t num_keys; } __attribute__ ((packed)); #define BT_HCI_CMD_WRITE_STORED_LINK_KEY 0x0c11 struct bt_hci_cmd_write_stored_link_key { uint8_t num_keys; } __attribute__ ((packed)); struct bt_hci_rsp_write_stored_link_key { uint8_t status; uint8_t num_keys; } __attribute__ ((packed)); #define BT_HCI_CMD_DELETE_STORED_LINK_KEY 0x0c12 struct bt_hci_cmd_delete_stored_link_key { uint8_t bdaddr[6]; uint8_t delete_all; } __attribute__ ((packed)); struct bt_hci_rsp_delete_stored_link_key { uint8_t status; uint16_t num_keys; } __attribute__ ((packed)); #define BT_HCI_CMD_WRITE_LOCAL_NAME 0x0c13 struct bt_hci_cmd_write_local_name { uint8_t name[248]; } __attribute__ ((packed)); #define BT_HCI_CMD_READ_LOCAL_NAME 0x0c14 struct bt_hci_rsp_read_local_name { uint8_t status; uint8_t name[248]; } __attribute__ ((packed)); #define BT_HCI_CMD_READ_CONN_ACCEPT_TIMEOUT 0x0c15 struct bt_hci_rsp_read_conn_accept_timeout { uint8_t status; uint16_t timeout; } __attribute__ ((packed)); #define BT_HCI_CMD_WRITE_CONN_ACCEPT_TIMEOUT 0x0c16 struct bt_hci_cmd_write_conn_accept_timeout { uint16_t timeout; } __attribute__ ((packed)); #define BT_HCI_CMD_READ_PAGE_TIMEOUT 0x0c17 struct bt_hci_rsp_read_page_timeout { uint8_t status; uint16_t timeout; } __attribute__ ((packed)); #define BT_HCI_CMD_WRITE_PAGE_TIMEOUT 0x0c18 struct bt_hci_cmd_write_page_timeout { uint16_t timeout; } __attribute__ ((packed)); #define BT_HCI_CMD_READ_SCAN_ENABLE 0x0c19 struct bt_hci_rsp_read_scan_enable { uint8_t status; uint8_t enable; } __attribute__ ((packed)); #define BT_HCI_CMD_WRITE_SCAN_ENABLE 0x0c1a struct bt_hci_cmd_write_scan_enable { uint8_t enable; } __attribute__ ((packed)); #define BT_HCI_CMD_READ_PAGE_SCAN_ACTIVITY 0x0c1b struct bt_hci_rsp_read_page_scan_activity { uint8_t status; uint16_t interval; uint16_t window; } __attribute__ ((packed)); #define BT_HCI_CMD_WRITE_PAGE_SCAN_ACTIVITY 0x0c1c struct bt_hci_cmd_write_page_scan_activity { uint16_t interval; uint16_t window; } __attribute__ ((packed)); #define BT_HCI_CMD_READ_INQUIRY_SCAN_ACTIVITY 0x0c1d struct bt_hci_rsp_read_inquiry_scan_activity { uint8_t status; uint16_t interval; uint16_t window; } __attribute__ ((packed)); #define BT_HCI_CMD_WRITE_INQUIRY_SCAN_ACTIVITY 0x0c1e struct bt_hci_cmd_write_inquiry_scan_activity { uint16_t interval; uint16_t window; } __attribute__ ((packed)); #define BT_HCI_CMD_READ_AUTH_ENABLE 0x0c1f struct bt_hci_rsp_read_auth_enable { uint8_t status; uint8_t enable; } __attribute__ ((packed)); #define BT_HCI_CMD_WRITE_AUTH_ENABLE 0x0c20 struct bt_hci_cmd_write_auth_enable { uint8_t enable; } __attribute__ ((packed)); #define BT_HCI_CMD_READ_ENCRYPT_MODE 0x0c21 struct bt_hci_rsp_read_encrypt_mode { uint8_t status; uint8_t mode; } __attribute__ ((packed)); #define BT_HCI_CMD_WRITE_ENCRYPT_MODE 0x0c22 struct bt_hci_cmd_write_encrypt_mode { uint8_t mode; } __attribute__ ((packed)); #define BT_HCI_CMD_READ_CLASS_OF_DEV 0x0c23 struct bt_hci_rsp_read_class_of_dev { uint8_t status; uint8_t dev_class[3]; } __attribute__ ((packed)); #define BT_HCI_CMD_WRITE_CLASS_OF_DEV 0x0c24 struct bt_hci_cmd_write_class_of_dev { uint8_t dev_class[3]; } __attribute__ ((packed)); #define BT_HCI_CMD_READ_VOICE_SETTING 0x0c25 struct bt_hci_rsp_read_voice_setting { uint8_t status; uint16_t setting; } __attribute__ ((packed)); #define BT_HCI_CMD_WRITE_VOICE_SETTING 0x0c26 struct bt_hci_cmd_write_voice_setting { uint16_t setting; } __attribute__ ((packed)); #define BT_HCI_CMD_READ_AUTO_FLUSH_TIMEOUT 0x0c27 struct bt_hci_cmd_read_auto_flush_timeout { uint16_t handle; } __attribute__ ((packed)); struct bt_hci_rsp_read_auto_flush_timeout { uint8_t status; uint16_t handle; uint16_t timeout; } __attribute__ ((packed)); #define BT_HCI_CMD_WRITE_AUTO_FLUSH_TIMEOUT 0x0c28 struct bt_hci_cmd_write_auto_flush_timeout { uint16_t handle; uint16_t timeout; } __attribute__ ((packed)); struct bt_hci_rsp_write_auto_flush_timeout { uint8_t status; uint16_t handle; } __attribute__ ((packed)); #define BT_HCI_CMD_READ_NUM_BROADCAST_RETRANS 0x0c29 struct bt_hci_rsp_read_num_broadcast_retrans { uint8_t status; uint8_t num_retrans; } __attribute__ ((packed)); #define BT_HCI_CMD_WRITE_NUM_BROADCAST_RETRANS 0x0c2a struct bt_hci_cmd_write_num_broadcast_retrans { uint8_t num_retrans; } __attribute__ ((packed)); #define BT_HCI_CMD_READ_HOLD_MODE_ACTIVITY 0x0c2b struct bt_hci_rsp_read_hold_mode_activity { uint8_t status; uint8_t activity; } __attribute__ ((packed)); #define BT_HCI_CMD_WRITE_HOLD_MODE_ACTIVITY 0x0c2c struct bt_hci_cmd_write_hold_mode_activity { uint8_t activity; } __attribute__ ((packed)); #define BT_HCI_CMD_READ_TX_POWER 0x0c2d struct bt_hci_cmd_read_tx_power { uint16_t handle; uint8_t type; } __attribute__ ((packed)); struct bt_hci_rsp_read_tx_power { uint8_t status; uint16_t handle; int8_t level; } __attribute__ ((packed)); #define BT_HCI_CMD_READ_SYNC_FLOW_CONTROL 0x0c2e struct bt_hci_rsp_read_sync_flow_control { uint8_t status; uint8_t enable; } __attribute__ ((packed)); #define BT_HCI_CMD_WRITE_SYNC_FLOW_CONTROL 0x0c2f struct bt_hci_cmd_write_sync_flow_control { uint8_t enable; } __attribute__ ((packed)); #define BT_HCI_CMD_SET_HOST_FLOW_CONTROL 0x0c31 struct bt_hci_cmd_set_host_flow_control { uint8_t enable; } __attribute__ ((packed)); #define BT_HCI_CMD_HOST_BUFFER_SIZE 0x0c33 struct bt_hci_cmd_host_buffer_size { uint16_t acl_mtu; uint8_t sco_mtu; uint16_t acl_max_pkt; uint16_t sco_max_pkt; } __attribute__ ((packed)); #define BT_HCI_CMD_HOST_NUM_COMPLETED_PACKETS 0x0c35 struct bt_hci_cmd_host_num_completed_packets { uint8_t num_handles; uint16_t handle; uint16_t count; } __attribute__ ((packed)); #define BT_HCI_CMD_READ_LINK_SUPV_TIMEOUT 0x0c36 struct bt_hci_cmd_read_link_supv_timeout { uint16_t handle; } __attribute__ ((packed)); struct bt_hci_rsp_read_link_supv_timeout { uint8_t status; uint16_t handle; uint16_t timeout; } __attribute__ ((packed)); #define BT_HCI_CMD_WRITE_LINK_SUPV_TIMEOUT 0x0c37 struct bt_hci_cmd_write_link_supv_timeout { uint16_t handle; uint16_t timeout; } __attribute__ ((packed)); struct bt_hci_rsp_write_link_supv_timeout { uint8_t status; uint16_t handle; } __attribute__ ((packed)); #define BT_HCI_CMD_READ_NUM_SUPPORTED_IAC 0x0c38 struct bt_hci_rsp_read_num_supported_iac { uint8_t status; uint8_t num_iac; } __attribute__ ((packed)); #define BT_HCI_CMD_READ_CURRENT_IAC_LAP 0x0c39 struct bt_hci_rsp_read_current_iac_lap { uint8_t status; uint8_t num_iac; uint8_t iac_lap[0]; } __attribute__ ((packed)); #define BT_HCI_CMD_WRITE_CURRENT_IAC_LAP 0x0c3a struct bt_hci_cmd_write_current_iac_lap { uint8_t num_iac; uint8_t iac_lap[0]; } __attribute__ ((packed)); #define BT_HCI_CMD_READ_PAGE_SCAN_PERIOD_MODE 0x0c3b struct bt_hci_rsp_read_page_scan_period_mode { uint8_t status; uint8_t mode; } __attribute__ ((packed)); #define BT_HCI_CMD_WRITE_PAGE_SCAN_PERIOD_MODE 0x0c3c struct bt_hci_cmd_write_page_scan_period_mode { uint8_t mode; } __attribute__ ((packed)); #define BT_HCI_CMD_READ_PAGE_SCAN_MODE 0x0c3d struct bt_hci_rsp_read_page_scan_mode { uint8_t status; uint8_t mode; } __attribute__ ((packed)); #define BT_HCI_CMD_WRITE_PAGE_SCAN_MODE 0x0c3e struct bt_hci_cmd_write_page_scan_mode { uint8_t mode; } __attribute__ ((packed)); #define BT_HCI_CMD_SET_AFH_HOST_CLASSIFICATION 0x0c3f struct bt_hci_cmd_set_afh_host_classification { uint8_t map[10]; } __attribute__ ((packed)); #define BT_HCI_CMD_READ_INQUIRY_SCAN_TYPE 0x0c42 struct bt_hci_rsp_read_inquiry_scan_type { uint8_t status; uint8_t type; } __attribute__ ((packed)); #define BT_HCI_CMD_WRITE_INQUIRY_SCAN_TYPE 0x0c43 struct bt_hci_cmd_write_inquiry_scan_type { uint8_t type; } __attribute__ ((packed)); #define BT_HCI_CMD_READ_INQUIRY_MODE 0x0c44 struct bt_hci_rsp_read_inquiry_mode { uint8_t status; uint8_t mode; } __attribute__ ((packed)); #define BT_HCI_CMD_WRITE_INQUIRY_MODE 0x0c45 struct bt_hci_cmd_write_inquiry_mode { uint8_t mode; } __attribute__ ((packed)); #define BT_HCI_CMD_READ_PAGE_SCAN_TYPE 0x0c46 struct bt_hci_rsp_read_page_scan_type { uint8_t status; uint8_t type; } __attribute__ ((packed)); #define BT_HCI_CMD_WRITE_PAGE_SCAN_TYPE 0x0c47 struct bt_hci_cmd_write_page_scan_type { uint8_t type; } __attribute__ ((packed)); #define BT_HCI_CMD_READ_AFH_ASSESSMENT_MODE 0x0c48 struct bt_hci_rsp_read_afh_assessment_mode { uint8_t status; uint8_t mode; } __attribute__ ((packed)); #define BT_HCI_CMD_WRITE_AFH_ASSESSMENT_MODE 0x0c49 struct bt_hci_cmd_write_afh_assessment_mode { uint8_t mode; } __attribute__ ((packed)); #define BT_HCI_CMD_READ_EXT_INQUIRY_RESPONSE 0x0c51 struct bt_hci_rsp_read_ext_inquiry_response { uint8_t status; uint8_t fec; uint8_t data[240]; } __attribute__ ((packed)); #define BT_HCI_CMD_WRITE_EXT_INQUIRY_RESPONSE 0x0c52 struct bt_hci_cmd_write_ext_inquiry_response { uint8_t fec; uint8_t data[240]; } __attribute__ ((packed)); #define BT_HCI_CMD_REFRESH_ENCRYPT_KEY 0x0c53 struct bt_hci_cmd_refresh_encrypt_key { uint16_t handle; } __attribute__ ((packed)); #define BT_HCI_CMD_READ_SIMPLE_PAIRING_MODE 0x0c55 struct bt_hci_rsp_read_simple_pairing_mode { uint8_t status; uint8_t mode; } __attribute__ ((packed)); #define BT_HCI_CMD_WRITE_SIMPLE_PAIRING_MODE 0x0c56 struct bt_hci_cmd_write_simple_pairing_mode { uint8_t mode; } __attribute__ ((packed)); #define BT_HCI_CMD_READ_LOCAL_OOB_DATA 0x0c57 struct bt_hci_rsp_read_local_oob_data { uint8_t status; uint8_t hash[16]; uint8_t randomizer[16]; } __attribute__ ((packed)); #define BT_HCI_CMD_READ_INQUIRY_RESP_TX_POWER 0x0c58 struct bt_hci_rsp_read_inquiry_resp_tx_power { uint8_t status; int8_t level; } __attribute__ ((packed)); #define BT_HCI_CMD_WRITE_INQUIRY_TX_POWER 0x0c59 struct bt_hci_cmd_write_inquiry_tx_power { int8_t level; } __attribute__ ((packed)); #define BT_HCI_CMD_READ_ERRONEOUS_REPORTING 0x0c5a struct bt_hci_rsp_read_erroneous_reporting { uint8_t status; uint8_t mode; } __attribute__ ((packed)); #define BT_HCI_CMD_WRITE_ERRONEOUS_REPORTING 0x0c5b struct bt_hci_cmd_write_erroneous_reporting { uint8_t mode; } __attribute__ ((packed)); #define BT_HCI_CMD_ENHANCED_FLUSH 0x0c5f struct bt_hci_cmd_enhanced_flush { uint16_t handle; uint8_t type; } __attribute__ ((packed)); #define BT_HCI_CMD_SEND_KEYPRESS_NOTIFY 0x0c60 struct bt_hci_cmd_send_keypress_notify { uint8_t bdaddr[6]; uint8_t type; } __attribute__ ((packed)); struct bt_hci_rsp_send_keypress_notify { uint8_t status; uint8_t bdaddr[6]; } __attribute__ ((packed)); #define BT_HCI_CMD_SET_EVENT_MASK_PAGE2 0x0c63 struct bt_hci_cmd_set_event_mask_page2 { uint8_t mask[8]; } __attribute__ ((packed)); #define BT_HCI_CMD_READ_LOCATION_DATA 0x0c64 struct bt_hci_rsp_read_location_data { uint8_t status; uint8_t domain_aware; uint8_t domain[2]; uint8_t domain_options; uint8_t options; } __attribute__ ((packed)); #define BT_HCI_CMD_WRITE_LOCATION_DATA 0x0c65 struct bt_hci_cmd_write_location_data { uint8_t domain_aware; uint8_t domain[2]; uint8_t domain_options; uint8_t options; } __attribute__ ((packed)); #define BT_HCI_CMD_READ_FLOW_CONTROL_MODE 0x0c66 struct bt_hci_rsp_read_flow_control_mode { uint8_t status; uint8_t mode; } __attribute__ ((packed)); #define BT_HCI_CMD_WRITE_FLOW_CONTROL_MODE 0x0c67 struct bt_hci_cmd_write_flow_control_mode { uint8_t mode; } __attribute__ ((packed)); #define BT_HCI_CMD_READ_ENHANCED_TX_POWER 0x0c68 struct bt_hci_cmd_read_enhanced_tx_power { uint16_t handle; uint8_t type; } __attribute__ ((packed)); struct bt_hci_rsp_read_enhanced_tx_power { uint8_t status; uint16_t handle; int8_t level_gfsk; int8_t level_dqpsk; int8_t level_8dpsk; } __attribute__ ((packed)); #define BT_HCI_CMD_SHORT_RANGE_MODE 0x0c6b struct bt_hci_cmd_short_range_mode { uint8_t phy_handle; uint8_t mode; } __attribute__ ((packed)); #define BT_HCI_CMD_READ_LE_HOST_SUPPORTED 0x0c6c struct bt_hci_rsp_read_le_host_supported { uint8_t status; uint8_t supported; uint8_t simultaneous; } __attribute__ ((packed)); #define BT_HCI_CMD_WRITE_LE_HOST_SUPPORTED 0x0c6d struct bt_hci_cmd_write_le_host_supported { uint8_t supported; uint8_t simultaneous; } __attribute__ ((packed)); #define BT_HCI_CMD_SET_RESERVED_LT_ADDR 0x0c74 struct bt_hci_cmd_set_reserved_lt_addr { uint8_t lt_addr; } __attribute__ ((packed)); struct bt_hci_rsp_set_reserved_lt_addr { uint8_t status; uint8_t lt_addr; } __attribute__ ((packed)); #define BT_HCI_CMD_DELETE_RESERVED_LT_ADDR 0x0c75 struct bt_hci_cmd_delete_reserved_lt_addr { uint8_t lt_addr; } __attribute__ ((packed)); struct bt_hci_rsp_delete_reserved_lt_addr { uint8_t status; uint8_t lt_addr; } __attribute__ ((packed)); #define BT_HCI_CMD_SET_PERIPHERAL_BROADCAST_DATA 0x0c76 struct bt_hci_cmd_set_peripheral_broadcast_data { uint8_t lt_addr; uint8_t fragment; uint8_t length; } __attribute__ ((packed)); struct bt_hci_rsp_set_peripheral_broadcast_data { uint8_t status; uint8_t lt_addr; } __attribute__ ((packed)); #define BT_HCI_CMD_READ_SYNC_TRAIN_PARAMS 0x0c77 struct bt_hci_rsp_read_sync_train_params { uint8_t status; uint16_t interval; uint32_t timeout; uint8_t service_data; } __attribute__ ((packed)); #define BT_HCI_CMD_WRITE_SYNC_TRAIN_PARAMS 0x0c78 struct bt_hci_cmd_write_sync_train_params { uint16_t min_interval; uint16_t max_interval; uint32_t timeout; uint8_t service_data; } __attribute__ ((packed)); struct bt_hci_rsp_write_sync_train_params { uint8_t status; uint16_t interval; } __attribute__ ((packed)); #define BT_HCI_CMD_READ_SECURE_CONN_SUPPORT 0x0c79 struct bt_hci_rsp_read_secure_conn_support { uint8_t status; uint8_t support; } __attribute__ ((packed)); #define BT_HCI_CMD_WRITE_SECURE_CONN_SUPPORT 0x0c7a struct bt_hci_cmd_write_secure_conn_support { uint8_t support; } __attribute__ ((packed)); #define BT_HCI_CMD_READ_AUTH_PAYLOAD_TIMEOUT 0x0c7b struct bt_hci_cmd_read_auth_payload_timeout { uint16_t handle; } __attribute__ ((packed)); struct bt_hci_rsp_read_auth_payload_timeout { uint8_t status; uint16_t handle; uint16_t timeout; } __attribute__ ((packed)); #define BT_HCI_CMD_WRITE_AUTH_PAYLOAD_TIMEOUT 0x0c7c struct bt_hci_cmd_write_auth_payload_timeout { uint16_t handle; uint16_t timeout; } __attribute__ ((packed)); struct bt_hci_rsp_write_auth_payload_timeout { uint8_t status; uint16_t handle; } __attribute__ ((packed)); #define BT_HCI_CMD_READ_LOCAL_OOB_EXT_DATA 0x0c7d struct bt_hci_rsp_read_local_oob_ext_data { uint8_t status; uint8_t hash192[16]; uint8_t randomizer192[16]; uint8_t hash256[16]; uint8_t randomizer256[16]; } __attribute__ ((packed)); #define BT_HCI_CMD_READ_EXT_PAGE_TIMEOUT 0x0c7e struct bt_hci_rsp_read_ext_page_timeout { uint8_t status; uint16_t timeout; } __attribute__ ((packed)); #define BT_HCI_CMD_WRITE_EXT_PAGE_TIMEOUT 0x0c7f struct bt_hci_cmd_write_ext_page_timeout { uint16_t timeout; } __attribute__ ((packed)); #define BT_HCI_CMD_READ_EXT_INQUIRY_LENGTH 0x0c80 struct bt_hci_rsp_read_ext_inquiry_length { uint8_t status; uint16_t interval; } __attribute__ ((packed)); #define BT_HCI_CMD_WRITE_EXT_INQUIRY_LENGTH 0x0c81 struct bt_hci_cmd_write_ext_inquiry_length { uint16_t interval; } __attribute__ ((packed)); #define BT_HCI_CMD_CONFIG_DATA_PATH 0x0c83 #define BT_HCI_BIT_CONFIG_DATA_PATH BT_HCI_CMD_BIT(45, 5) struct bt_hci_cmd_config_data_path { uint8_t dir; uint8_t id; uint8_t vnd_config_len; uint8_t vnd_config[0]; } __attribute__ ((packed)); #define BT_HCI_CMD_READ_LOCAL_VERSION 0x1001 struct bt_hci_rsp_read_local_version { uint8_t status; uint8_t hci_ver; uint16_t hci_rev; uint8_t lmp_ver; uint16_t manufacturer; uint16_t lmp_subver; } __attribute__ ((packed)); #define BT_HCI_CMD_READ_LOCAL_COMMANDS 0x1002 struct bt_hci_rsp_read_local_commands { uint8_t status; uint8_t commands[64]; } __attribute__ ((packed)); #define BT_HCI_CMD_READ_LOCAL_FEATURES 0x1003 struct bt_hci_rsp_read_local_features { uint8_t status; uint8_t features[8]; } __attribute__ ((packed)); #define BT_HCI_CMD_READ_LOCAL_EXT_FEATURES 0x1004 struct bt_hci_cmd_read_local_ext_features { uint8_t page; } __attribute__ ((packed)); struct bt_hci_rsp_read_local_ext_features { uint8_t status; uint8_t page; uint8_t max_page; uint8_t features[8]; } __attribute__ ((packed)); #define BT_HCI_CMD_READ_BUFFER_SIZE 0x1005 struct bt_hci_rsp_read_buffer_size { uint8_t status; uint16_t acl_mtu; uint8_t sco_mtu; uint16_t acl_max_pkt; uint16_t sco_max_pkt; } __attribute__ ((packed)); #define BT_HCI_CMD_READ_COUNTRY_CODE 0x1007 struct bt_hci_rsp_read_country_code { uint8_t status; uint8_t code; } __attribute__ ((packed)); #define BT_HCI_CMD_READ_BD_ADDR 0x1009 struct bt_hci_rsp_read_bd_addr { uint8_t status; uint8_t bdaddr[6]; } __attribute__ ((packed)); #define BT_HCI_CMD_READ_DATA_BLOCK_SIZE 0x100a struct bt_hci_rsp_read_data_block_size { uint8_t status; uint16_t max_acl_len; uint16_t block_len; uint16_t num_blocks; } __attribute__ ((packed)); #define BT_HCI_CMD_READ_LOCAL_CODECS 0x100b struct bt_hci_rsp_read_local_codecs { uint8_t status; uint8_t num_codecs; uint8_t codec[0]; } __attribute__ ((packed)); #define BT_HCI_CMD_READ_LOCAL_PAIRING_OPTIONS 0x100c struct bt_hci_rsp_read_local_pairing_options { uint8_t status; uint8_t pairing_options; uint8_t max_key_size; } __attribute__ ((packed)); #define BT_HCI_CMD_READ_LOCAL_CODECS_V2 0x100d #define BT_HCI_BIT_READ_LOCAL_CODECS_V2 BT_HCI_CMD_BIT(45, 2) #define BT_HCI_LOCAL_CODEC_BREDR_ACL BIT(0) #define BT_HCI_LOCAL_CODEC_BREDR_SCO BIT(1) #define BT_HCI_LOCAL_CODEC_LE_CIS BIT(2) #define BT_HCI_LOCAL_CODEC_LE_BIS BIT(3) struct bt_hci_vnd_codec_v2 { uint16_t cid; uint16_t vid; uint8_t transport; } __attribute__ ((packed)); struct bt_hci_vnd_codec { uint8_t id; uint16_t cid; uint16_t vid; uint8_t transport; } __attribute__ ((packed)); struct bt_hci_codec { uint8_t id; uint8_t transport; } __attribute__ ((packed)); struct bt_hci_rsp_read_local_codecs_v2 { uint8_t status; uint8_t num_codecs; struct bt_hci_codec codec[0]; } __attribute__ ((packed)); #define BT_HCI_CMD_READ_LOCAL_CODEC_CAPS 0x100e #define BT_HCI_BIT_READ_LOCAL_CODEC_CAPS BT_HCI_CMD_BIT(45, 3) struct bt_hci_cmd_read_local_codec_caps { struct bt_hci_vnd_codec codec; uint8_t dir; } __attribute__ ((packed)); struct bt_hci_codec_caps { uint8_t len; uint8_t data[0]; } __attribute__ ((packed)); struct bt_hci_rsp_read_local_codec_caps { uint8_t status; uint8_t num; struct bt_hci_codec_caps caps[0]; } __attribute__ ((packed)); #define BT_HCI_CMD_READ_LOCAL_CTRL_DELAY 0x100f #define BT_HCI_BIT_READ_LOCAL_CTRL_DELAY BT_HCI_CMD_BIT(45, 4) struct bt_hci_cmd_read_local_ctrl_delay { struct bt_hci_vnd_codec codec; uint8_t dir; uint8_t codec_cfg_len; uint8_t codec_cfg[0]; } __attribute__ ((packed)); struct bt_hci_rsp_read_local_ctrl_delay { uint8_t status; uint8_t min_delay[3]; uint8_t max_delay[3]; } __attribute__ ((packed)); #define BT_HCI_CMD_READ_FAILED_CONTACT_COUNTER 0x1401 struct bt_hci_cmd_read_failed_contact_counter { uint16_t handle; } __attribute__ ((packed)); struct bt_hci_rsp_read_failed_contact_counter { uint8_t status; uint16_t handle; uint16_t counter; } __attribute__ ((packed)); #define BT_HCI_CMD_RESET_FAILED_CONTACT_COUNTER 0x1402 struct bt_hci_cmd_reset_failed_contact_counter { uint16_t handle; } __attribute__ ((packed)); struct bt_hci_rsp_reset_failed_contact_counter { uint8_t status; uint16_t handle; } __attribute__ ((packed)); #define BT_HCI_CMD_READ_LINK_QUALITY 0x1403 struct bt_hci_cmd_read_link_quality { uint16_t handle; } __attribute__ ((packed)); struct bt_hci_rsp_read_link_quality { uint8_t status; uint16_t handle; uint8_t link_quality; } __attribute__ ((packed)); #define BT_HCI_CMD_READ_RSSI 0x1405 struct bt_hci_cmd_read_rssi { uint16_t handle; } __attribute__ ((packed)); struct bt_hci_rsp_read_rssi { uint8_t status; uint16_t handle; int8_t rssi; } __attribute__ ((packed)); #define BT_HCI_CMD_READ_AFH_CHANNEL_MAP 0x1406 struct bt_hci_cmd_read_afh_channel_map { uint16_t handle; } __attribute__ ((packed)); struct bt_hci_rsp_read_afh_channel_map { uint8_t status; uint16_t handle; uint8_t mode; uint8_t map[10]; } __attribute__ ((packed)); #define BT_HCI_CMD_READ_CLOCK 0x1407 struct bt_hci_cmd_read_clock { uint16_t handle; uint8_t type; } __attribute__ ((packed)); struct bt_hci_rsp_read_clock { uint8_t status; uint16_t handle; uint32_t clock; uint16_t accuracy; } __attribute__ ((packed)); #define BT_HCI_CMD_READ_ENCRYPT_KEY_SIZE 0x1408 struct bt_hci_cmd_read_encrypt_key_size { uint16_t handle; } __attribute__ ((packed)); struct bt_hci_rsp_read_encrypt_key_size { uint8_t status; uint16_t handle; uint8_t key_size; } __attribute__ ((packed)); #define BT_HCI_CMD_READ_LOCAL_AMP_INFO 0x1409 struct bt_hci_rsp_read_local_amp_info { uint8_t status; uint8_t amp_status; uint32_t total_bw; uint32_t max_bw; uint32_t min_latency; uint32_t max_pdu; uint8_t amp_type; uint16_t pal_cap; uint16_t max_assoc_len; uint32_t max_flush_to; uint32_t be_flush_to; } __attribute__ ((packed)); #define BT_HCI_CMD_READ_LOCAL_AMP_ASSOC 0x140a struct bt_hci_cmd_read_local_amp_assoc { uint8_t phy_handle; uint16_t len_so_far; uint16_t max_assoc_len; } __attribute__ ((packed)); struct bt_hci_rsp_read_local_amp_assoc { uint8_t status; uint8_t phy_handle; uint16_t remain_assoc_len; uint8_t assoc_fragment[248]; } __attribute__ ((packed)); #define BT_HCI_CMD_WRITE_REMOTE_AMP_ASSOC 0x140b struct bt_hci_cmd_write_remote_amp_assoc { uint8_t phy_handle; uint16_t len_so_far; uint16_t remain_assoc_len; uint8_t assoc_fragment[248]; } __attribute__ ((packed)); struct bt_hci_rsp_write_remote_amp_assoc { uint8_t status; uint8_t phy_handle; } __attribute__ ((packed)); #define BT_HCI_CMD_GET_MWS_TRANSPORT_CONFIG 0x140c struct bt_hci_rsp_get_mws_transport_config { uint8_t status; uint8_t num_transports; uint8_t transport[0]; } __attribute__ ((packed)); #define BT_HCI_CMD_SET_TRIGGERED_CLOCK_CAPTURE 0x140d struct bt_hci_cmd_set_triggered_clock_capture { uint16_t handle; uint8_t enable; uint8_t type; uint8_t lpo_allowed; uint8_t num_filter; } __attribute__ ((packed)); #define BT_HCI_CMD_READ_LOOPBACK_MODE 0x1801 struct bt_hci_rsp_read_loopback_mode { uint8_t status; uint8_t mode; } __attribute__ ((packed)); #define BT_HCI_CMD_WRITE_LOOPBACK_MODE 0x1802 struct bt_hci_cmd_write_loopback_mode { uint8_t mode; } __attribute__ ((packed)); #define BT_HCI_CMD_ENABLE_DUT_MODE 0x1803 #define BT_HCI_CMD_WRITE_SSP_DEBUG_MODE 0x1804 struct bt_hci_cmd_write_ssp_debug_mode { uint8_t mode; } __attribute__ ((packed)); #define BT_HCI_CMD_LE_SET_EVENT_MASK 0x2001 struct bt_hci_cmd_le_set_event_mask { uint8_t mask[8]; } __attribute__ ((packed)); #define BT_HCI_CMD_LE_READ_BUFFER_SIZE 0x2002 struct bt_hci_rsp_le_read_buffer_size { uint8_t status; uint16_t le_mtu; uint8_t le_max_pkt; } __attribute__ ((packed)); #define BT_HCI_CMD_LE_READ_LOCAL_FEATURES 0x2003 struct bt_hci_rsp_le_read_local_features { uint8_t status; uint8_t features[8]; } __attribute__ ((packed)); #define BT_HCI_CMD_LE_SET_RANDOM_ADDRESS 0x2005 struct bt_hci_cmd_le_set_random_address { uint8_t addr[6]; } __attribute__ ((packed)); #define BT_HCI_CMD_LE_SET_ADV_PARAMETERS 0x2006 struct bt_hci_cmd_le_set_adv_parameters { uint16_t min_interval; uint16_t max_interval; uint8_t type; uint8_t own_addr_type; uint8_t direct_addr_type; uint8_t direct_addr[6]; uint8_t channel_map; uint8_t filter_policy; } __attribute__ ((packed)); #define BT_HCI_CMD_LE_READ_ADV_TX_POWER 0x2007 struct bt_hci_rsp_le_read_adv_tx_power { uint8_t status; int8_t level; } __attribute__ ((packed)); #define BT_HCI_CMD_LE_SET_ADV_DATA 0x2008 struct bt_hci_cmd_le_set_adv_data { uint8_t len; uint8_t data[31]; } __attribute__ ((packed)); #define BT_HCI_CMD_LE_SET_SCAN_RSP_DATA 0x2009 struct bt_hci_cmd_le_set_scan_rsp_data { uint8_t len; uint8_t data[31]; } __attribute__ ((packed)); #define BT_HCI_CMD_LE_SET_ADV_ENABLE 0x200a struct bt_hci_cmd_le_set_adv_enable { uint8_t enable; } __attribute__ ((packed)); #define BT_HCI_CMD_LE_SET_SCAN_PARAMETERS 0x200b struct bt_hci_cmd_le_set_scan_parameters { uint8_t type; uint16_t interval; uint16_t window; uint8_t own_addr_type; uint8_t filter_policy; } __attribute__ ((packed)); #define BT_HCI_CMD_LE_SET_SCAN_ENABLE 0x200c struct bt_hci_cmd_le_set_scan_enable { uint8_t enable; uint8_t filter_dup; } __attribute__ ((packed)); #define BT_HCI_CMD_LE_CREATE_CONN 0x200d struct bt_hci_cmd_le_create_conn { uint16_t scan_interval; uint16_t scan_window; uint8_t filter_policy; uint8_t peer_addr_type; uint8_t peer_addr[6]; uint8_t own_addr_type; uint16_t min_interval; uint16_t max_interval; uint16_t latency; uint16_t supv_timeout; uint16_t min_length; uint16_t max_length; } __attribute__ ((packed)); #define BT_HCI_CMD_LE_CREATE_CONN_CANCEL 0x200e #define BT_HCI_CMD_LE_READ_ACCEPT_LIST_SIZE 0x200f struct bt_hci_rsp_le_read_accept_list_size { uint8_t status; uint8_t size; } __attribute__ ((packed)); #define BT_HCI_CMD_LE_CLEAR_ACCEPT_LIST 0x2010 #define BT_HCI_CMD_LE_ADD_TO_ACCEPT_LIST 0x2011 struct bt_hci_cmd_le_add_to_accept_list { uint8_t addr_type; uint8_t addr[6]; } __attribute__ ((packed)); #define BT_HCI_CMD_LE_REMOVE_FROM_ACCEPT_LIST 0x2012 struct bt_hci_cmd_le_remove_from_accept_list { uint8_t addr_type; uint8_t addr[6]; } __attribute__ ((packed)); #define BT_HCI_CMD_LE_CONN_UPDATE 0x2013 struct bt_hci_cmd_le_conn_update { uint16_t handle; uint16_t min_interval; uint16_t max_interval; uint16_t latency; uint16_t supv_timeout; uint16_t min_length; uint16_t max_length; } __attribute__ ((packed)); #define BT_HCI_CMD_LE_SET_HOST_CLASSIFICATION 0x2014 struct bt_hci_cmd_le_set_host_classification { uint8_t map[5]; } __attribute__ ((packed)); #define BT_HCI_CMD_LE_READ_CHANNEL_MAP 0x2015 struct bt_hci_cmd_le_read_channel_map { uint16_t handle; } __attribute__ ((packed)); struct bt_hci_rsp_le_read_channel_map { uint8_t status; uint16_t handle; uint8_t map[5]; } __attribute__ ((packed)); #define BT_HCI_CMD_LE_READ_REMOTE_FEATURES 0x2016 struct bt_hci_cmd_le_read_remote_features { uint16_t handle; } __attribute__ ((packed)); #define BT_HCI_CMD_LE_ENCRYPT 0x2017 struct bt_hci_cmd_le_encrypt { uint8_t key[16]; uint8_t plaintext[16]; } __attribute__ ((packed)); struct bt_hci_rsp_le_encrypt { uint8_t status; uint8_t data[16]; } __attribute__ ((packed)); #define BT_HCI_CMD_LE_RAND 0x2018 struct bt_hci_rsp_le_rand { uint8_t status; uint64_t number; } __attribute__ ((packed)); #define BT_HCI_CMD_LE_START_ENCRYPT 0x2019 struct bt_hci_cmd_le_start_encrypt { uint16_t handle; uint64_t rand; uint16_t ediv; uint8_t ltk[16]; } __attribute__ ((packed)); #define BT_HCI_CMD_LE_LTK_REQ_REPLY 0x201a struct bt_hci_cmd_le_ltk_req_reply { uint16_t handle; uint8_t ltk[16]; } __attribute__ ((packed)); struct bt_hci_rsp_le_ltk_req_reply { uint8_t status; uint16_t handle; } __attribute__ ((packed)); #define BT_HCI_CMD_LE_LTK_REQ_NEG_REPLY 0x201b struct bt_hci_cmd_le_ltk_req_neg_reply { uint16_t handle; } __attribute__ ((packed)); struct bt_hci_rsp_le_ltk_req_neg_reply { uint8_t status; uint16_t handle; } __attribute__ ((packed)); #define BT_HCI_CMD_LE_READ_SUPPORTED_STATES 0x201c struct bt_hci_rsp_le_read_supported_states { uint8_t status; uint8_t states[8]; } __attribute__ ((packed)); #define BT_HCI_CMD_LE_RECEIVER_TEST 0x201d struct bt_hci_cmd_le_receiver_test { uint8_t frequency; } __attribute__ ((packed)); #define BT_HCI_CMD_LE_TRANSMITTER_TEST 0x201e struct bt_hci_cmd_le_transmitter_test { uint8_t frequency; uint8_t data_len; uint8_t payload; } __attribute__ ((packed)); #define BT_HCI_CMD_LE_TEST_END 0x201f struct bt_hci_rsp_le_test_end { uint8_t status; uint16_t num_packets; } __attribute__ ((packed)); #define BT_HCI_CMD_LE_CONN_PARAM_REQ_REPLY 0x2020 struct bt_hci_cmd_le_conn_param_req_reply { uint16_t handle; uint16_t min_interval; uint16_t max_interval; uint16_t latency; uint16_t supv_timeout; uint16_t min_length; uint16_t max_length; } __attribute__ ((packed)); struct bt_hci_rsp_le_conn_param_req_reply { uint8_t status; uint16_t handle; } __attribute__ ((packed)); #define BT_HCI_CMD_LE_CONN_PARAM_REQ_NEG_REPLY 0x2021 struct bt_hci_cmd_le_conn_param_req_neg_reply { uint16_t handle; uint8_t reason; } __attribute__ ((packed)); struct bt_hci_rsp_le_conn_param_req_neg_reply { uint8_t status; uint16_t handle; } __attribute__ ((packed)); #define BT_HCI_CMD_LE_SET_DATA_LENGTH 0x2022 struct bt_hci_cmd_le_set_data_length { uint16_t handle; uint16_t tx_len; uint16_t tx_time; } __attribute__ ((packed)); struct bt_hci_rsp_le_set_data_length { uint8_t status; uint16_t handle; } __attribute__ ((packed)); #define BT_HCI_CMD_LE_READ_DEFAULT_DATA_LENGTH 0x2023 struct bt_hci_rsp_le_read_default_data_length { uint8_t status; uint16_t tx_len; uint16_t tx_time; } __attribute__ ((packed)); #define BT_HCI_CMD_LE_WRITE_DEFAULT_DATA_LENGTH 0x2024 struct bt_hci_cmd_le_write_default_data_length { uint16_t tx_len; uint16_t tx_time; } __attribute__ ((packed)); #define BT_HCI_CMD_LE_READ_LOCAL_PK256 0x2025 #define BT_HCI_CMD_LE_GENERATE_DHKEY 0x2026 struct bt_hci_cmd_le_generate_dhkey { uint8_t remote_pk256[64]; } __attribute__ ((packed)); #define BT_HCI_CMD_LE_ADD_TO_RESOLV_LIST 0x2027 struct bt_hci_cmd_le_add_to_resolv_list { uint8_t addr_type; uint8_t addr[6]; uint8_t peer_irk[16]; uint8_t local_irk[16]; } __attribute__ ((packed)); #define BT_HCI_CMD_LE_REMOVE_FROM_RESOLV_LIST 0x2028 struct bt_hci_cmd_le_remove_from_resolv_list { uint8_t addr_type; uint8_t addr[6]; } __attribute__ ((packed)); #define BT_HCI_CMD_LE_CLEAR_RESOLV_LIST 0x2029 #define BT_HCI_CMD_LE_READ_RESOLV_LIST_SIZE 0x202a struct bt_hci_rsp_le_read_resolv_list_size { uint8_t status; uint8_t size; } __attribute__ ((packed)); #define BT_HCI_CMD_LE_READ_PEER_RESOLV_ADDR 0x202b struct bt_hci_cmd_le_read_peer_resolv_addr { uint8_t addr_type; uint8_t addr[6]; } __attribute__ ((packed)); struct bt_hci_rsp_le_read_peer_resolv_addr { uint8_t status; uint8_t addr[6]; } __attribute__ ((packed)); #define BT_HCI_CMD_LE_READ_LOCAL_RESOLV_ADDR 0x202c struct bt_hci_cmd_le_read_local_resolv_addr { uint8_t addr_type; uint8_t addr[6]; } __attribute__ ((packed)); struct bt_hci_rsp_le_read_local_resolv_addr { uint8_t status; uint8_t addr[6]; } __attribute__ ((packed)); #define BT_HCI_CMD_LE_SET_RESOLV_ENABLE 0x202d struct bt_hci_cmd_le_set_resolv_enable { uint8_t enable; } __attribute__ ((packed)); #define BT_HCI_CMD_LE_SET_RESOLV_TIMEOUT 0x202e struct bt_hci_cmd_le_set_resolv_timeout { uint16_t timeout; } __attribute__ ((packed)); #define BT_HCI_CMD_LE_READ_MAX_DATA_LENGTH 0x202f struct bt_hci_rsp_le_read_max_data_length { uint8_t status; uint16_t max_tx_len; uint16_t max_tx_time; uint16_t max_rx_len; uint16_t max_rx_time; } __attribute__ ((packed)); #define BT_HCI_CMD_LE_READ_PHY 0x2030 struct bt_hci_cmd_le_read_phy { uint16_t handle; } __attribute__((packed)); struct bt_hci_rsp_le_read_phy { uint8_t status; uint16_t handle; uint8_t tx_phy; uint8_t rx_phy; } __attribute__((packed)); #define BT_HCI_CMD_LE_SET_DEFAULT_PHY 0x2031 struct bt_hci_cmd_le_set_default_phy { uint8_t all_phys; uint8_t tx_phys; uint8_t rx_phys; } __attribute__((packed)); #define BT_HCI_CMD_LE_SET_PHY 0x2032 struct bt_hci_cmd_le_set_phy { uint16_t handle; uint8_t all_phys; uint8_t tx_phys; uint8_t rx_phys; uint16_t phy_opts; } __attribute__((packed)); #define BT_HCI_CMD_LE_ENHANCED_RECEIVER_TEST 0x2033 struct bt_hci_cmd_le_enhanced_receiver_test { uint8_t rx_channel; uint8_t phy; uint8_t modulation_index; } __attribute__((packed)); #define BT_HCI_CMD_LE_ENHANCED_TRANSMITTER_TEST 0x2034 struct bt_hci_cmd_le_enhanced_transmitter_test { uint8_t tx_channel; uint8_t data_len; uint8_t payload; uint8_t phy; } __attribute__((packed)); #define BT_HCI_CMD_LE_SET_ADV_SET_RAND_ADDR 0x2035 struct bt_hci_cmd_le_set_adv_set_rand_addr { uint8_t handle; uint8_t bdaddr[6]; } __attribute__ ((packed)); #define BT_HCI_CMD_LE_SET_EXT_ADV_PARAMS 0x2036 struct bt_hci_cmd_le_set_ext_adv_params { uint8_t handle; uint16_t evt_properties; uint8_t min_interval[3]; uint8_t max_interval[3]; uint8_t channel_map; uint8_t own_addr_type; uint8_t peer_addr_type; uint8_t peer_addr[6]; uint8_t filter_policy; uint8_t tx_power; uint8_t primary_phy; uint8_t secondary_max_skip; uint8_t secondary_phy; uint8_t sid; uint8_t notif_enable; } __attribute__ ((packed)); struct bt_hci_rsp_le_set_ext_adv_params { uint8_t status; uint8_t tx_power; } __attribute__ ((packed)); #define BT_HCI_CMD_LE_SET_EXT_ADV_DATA 0x2037 struct bt_hci_cmd_le_set_ext_adv_data { uint8_t handle; uint8_t operation; uint8_t fragment_preference; uint8_t data_len; uint8_t data[0]; } __attribute__ ((packed)); #define BT_HCI_CMD_LE_SET_EXT_SCAN_RSP_DATA 0x2038 struct bt_hci_cmd_le_set_ext_scan_rsp_data { uint8_t handle; uint8_t operation; uint8_t fragment_preference; uint8_t data_len; uint8_t data[0]; } __attribute__ ((packed)); #define BT_HCI_CMD_LE_SET_EXT_ADV_ENABLE 0x2039 struct bt_hci_cmd_le_set_ext_adv_enable { uint8_t enable; uint8_t num_of_sets; } __attribute__ ((packed)); struct bt_hci_cmd_ext_adv_set { uint8_t handle; uint16_t duration; uint8_t max_events; } __attribute__ ((packed)); #define BT_HCI_CMD_LE_READ_MAX_ADV_DATA_LEN 0x203a struct bt_hci_rsp_le_read_max_adv_data_len { uint8_t status; uint16_t max_len; } __attribute__ ((packed)); #define BT_HCI_CMD_LE_READ_NUM_SUPPORTED_ADV_SETS 0x203b struct bt_hci_rsp_le_read_num_supported_adv_sets { uint8_t status; uint8_t num_of_sets; } __attribute__ ((packed)); #define BT_HCI_CMD_LE_REMOVE_ADV_SET 0x203c struct bt_hci_cmd_le_remove_adv_set { uint8_t handle; } __attribute__ ((packed)); #define BT_HCI_CMD_LE_CLEAR_ADV_SETS 0x203d #define BT_HCI_CMD_LE_SET_PA_PARAMS 0x203e struct bt_hci_cmd_le_set_pa_params { uint8_t handle; uint16_t min_interval; uint16_t max_interval; uint16_t properties; } __attribute__ ((packed)); #define BT_HCI_CMD_LE_SET_PA_DATA 0x203f struct bt_hci_cmd_le_set_pa_data { uint8_t handle; uint8_t operation; uint8_t data_len; uint8_t data[0]; } __attribute__ ((packed)); #define BT_HCI_CMD_LE_SET_PA_ENABLE 0x2040 struct bt_hci_cmd_le_set_pa_enable { uint8_t enable; uint8_t handle; } __attribute__ ((packed)); #define BT_HCI_CMD_LE_SET_EXT_SCAN_PARAMS 0x2041 struct bt_hci_cmd_le_set_ext_scan_params { uint8_t own_addr_type; uint8_t filter_policy; uint8_t num_phys; uint8_t data[0]; } __attribute__ ((packed)); struct bt_hci_le_scan_phy { uint8_t type; uint16_t interval; uint16_t window; } __attribute__ ((packed)); #define BT_HCI_CMD_LE_SET_EXT_SCAN_ENABLE 0x2042 struct bt_hci_cmd_le_set_ext_scan_enable { uint8_t enable; uint8_t filter_dup; uint16_t duration; uint16_t period; } __attribute__ ((packed)); #define BT_HCI_CMD_LE_EXT_CREATE_CONN 0x2043 struct bt_hci_cmd_le_ext_create_conn { uint8_t filter_policy; uint8_t own_addr_type; uint8_t peer_addr_type; uint8_t peer_addr[6]; uint8_t phys; uint8_t data[0]; } __attribute__ ((packed)); struct bt_hci_le_ext_create_conn { uint16_t scan_interval; uint16_t scan_window; uint16_t min_interval; uint16_t max_interval; uint16_t latency; uint16_t supv_timeout; uint16_t min_length; uint16_t max_length; } __attribute__ ((packed)); #define BT_HCI_CMD_LE_PA_CREATE_SYNC 0x2044 struct bt_hci_cmd_le_pa_create_sync { uint8_t options; uint8_t sid; uint8_t addr_type; uint8_t addr[6]; uint16_t skip; uint16_t sync_timeout; uint8_t sync_cte_type; } __attribute__ ((packed)); #define BT_HCI_CMD_LE_PA_CREATE_SYNC_CANCEL 0x2045 #define BT_HCI_CMD_LE_PA_TERM_SYNC 0x2046 struct bt_hci_cmd_le_pa_term_sync { uint16_t sync_handle; } __attribute__ ((packed)); #define BT_HCI_CMD_LE_ADD_DEV_PA_LIST 0x2047 struct bt_hci_cmd_le_add_dev_pa_list { uint8_t addr_type; uint8_t addr[6]; uint8_t sid; } __attribute__ ((packed)); #define BT_HCI_CMD_LE_REMOVE_DEV_PA_LIST 0x2048 struct bt_hci_cmd_le_remove_dev_pa_list { uint8_t addr_type; uint8_t addr[6]; uint8_t sid; } __attribute__ ((packed)); #define BT_HCI_CMD_LE_CLEAR_PA_LIST 0x2049 #define BT_HCI_CMD_LE_READ_PA_LIST_SIZE 0x204a struct bt_hci_rsp_le_read_dev_pa_list_size { uint8_t status; uint8_t list_size; } __attribute__ ((packed)); #define BT_HCI_CMD_LE_READ_TX_POWER 0x204b struct bt_hci_rsp_le_read_tx_power { uint8_t status; int8_t min_tx_power; int8_t max_tx_power; } __attribute__ ((packed)); #define BT_HCI_CMD_LE_READ_RF_PATH_COMPENSATION 0x204c struct bt_hci_rsp_le_read_rf_path_comp { uint8_t status; uint16_t rf_tx_path_comp; uint16_t rf_rx_path_comp; } __attribute__ ((packed)); #define BT_HCI_CMD_LE_WRITE_RF_PATH_COMPENSATION 0x204d struct bt_hci_cmd_le_write_rf_path_comp { uint16_t rf_tx_path_comp; uint16_t rf_rx_path_comp; } __attribute__ ((packed)); #define BT_HCI_CMD_LE_SET_PRIV_MODE 0x204e struct bt_hci_cmd_le_set_priv_mode { uint8_t peer_id_addr_type; uint8_t peer_id_addr[6]; uint8_t priv_mode; } __attribute__ ((packed)); #define BT_HCI_CMD_LE_RECEIVER_TEST_V3 0x204f struct bt_hci_cmd_le_receiver_test_v3 { uint8_t rx_chan; uint8_t phy; uint8_t mod_index; uint8_t cte_len; uint8_t cte_type; uint8_t duration; uint8_t num_antenna_id; uint8_t antenna_ids[0]; } __attribute__ ((packed)); #define BT_HCI_CMD_LE_TX_TEST_V3 0x2050 struct bt_hci_cmd_le_tx_test_v3 { uint8_t chan; uint8_t data_len; uint8_t payload; uint8_t phy; uint8_t cte_len; uint8_t cte_type; uint8_t duration; uint8_t num_antenna_id; uint8_t antenna_ids[0]; } __attribute__ ((packed)); #define BT_HCI_CMD_SET_PA_REC_ENABLE 0x2059 struct bt_hci_cmd_set_pa_rec_enable { uint16_t sync_handle; uint8_t enable; } __attribute__ ((packed)); #define BT_HCI_CMD_PERIODIC_SYNC_TRANS 0x205a struct bt_hci_cmd_periodic_sync_trans { uint16_t handle; uint16_t service_data; uint16_t sync_handle; } __attribute__ ((packed)); #define BT_HCI_CMD_PA_SET_INFO_TRANS 0x205b struct bt_hci_cmd_pa_set_info_trans { uint16_t handle; uint16_t service_data; uint8_t adv_handle; } __attribute__ ((packed)); #define BT_HCI_CMD_PA_SYNC_TRANS_PARAMS 0x205c struct bt_hci_cmd_pa_sync_trans_params { uint16_t handle; uint8_t mode; uint16_t skip; uint16_t sync_timeout; uint8_t cte_type; } __attribute__ ((packed)); #define BT_HCI_CMD_DEFAULT_PA_SYNC_TRANS_PARAMS 0x205d struct bt_hci_cmd_default_pa_sync_trans_params { uint8_t mode; uint16_t skip; uint16_t sync_timeout; uint8_t cte_type; } __attribute__ ((packed)); #define BT_HCI_CMD_LE_READ_BUFFER_SIZE_V2 0x2060 #define BT_HCI_BIT_LE_READ_BUFFER_SIZE_V2 BT_HCI_CMD_BIT(41, 5) struct bt_hci_rsp_le_read_buffer_size_v2 { uint8_t status; uint16_t acl_mtu; uint8_t acl_max_pkt; uint16_t iso_mtu; uint8_t iso_max_pkt; } __attribute__ ((packed)); #define BT_HCI_CMD_LE_READ_ISO_TX_SYNC 0x2061 #define BT_HCI_BIT_LE_READ_ISO_TX_SYNC BT_HCI_CMD_BIT(41, 6) struct bt_hci_cmd_le_read_iso_tx_sync { uint16_t handle; } __attribute__ ((packed)); struct bt_hci_rsp_le_read_iso_tx_sync { uint8_t status; uint16_t handle; uint16_t seq; uint32_t timestamp; uint8_t offset[3]; } __attribute__ ((packed)); #define BT_HCI_CMD_LE_SET_CIG_PARAMS 0x2062 #define BT_HCI_BIT_LE_SET_CIG_PARAMS BT_HCI_CMD_BIT(41, 7) struct bt_hci_cis_params { uint8_t cis_id; uint16_t c_sdu; uint16_t p_sdu; uint8_t c_phy; uint8_t p_phy; uint8_t c_rtn; uint8_t p_rtn; } __attribute__ ((packed)); struct bt_hci_cmd_le_set_cig_params { uint8_t cig_id; uint8_t c_interval[3]; uint8_t p_interval[3]; uint8_t sca; uint8_t packing; uint8_t framing; uint16_t c_latency; uint16_t p_latency; uint8_t num_cis; struct bt_hci_cis_params cis[0]; } __attribute__ ((packed)); struct bt_hci_rsp_le_set_cig_params { uint8_t status; uint8_t cig_id; uint8_t num_handles; uint16_t handle[0]; } __attribute__ ((packed)); #define BT_HCI_CMD_LE_SET_CIG_PARAMS_TEST 0x2063 #define BT_HCI_BIT_LE_SET_CIG_PARAMS_TEST BT_HCI_CMD_BIT(42, 0) struct bt_hci_cis_params_test { uint8_t cis_id; uint8_t nse; uint16_t c_sdu; uint16_t p_sdu; uint16_t c_pdu; uint16_t p_pdu; uint8_t c_phy; uint8_t p_phy; uint8_t c_bn; uint8_t p_bn; } __attribute__ ((packed)); struct bt_hci_cmd_le_set_cig_params_test { uint8_t cig_id; uint8_t c_interval[3]; uint8_t p_interval[3]; uint8_t c_ft; uint8_t p_ft; uint16_t iso_interval; uint8_t sca; uint8_t packing; uint8_t framing; uint8_t num_cis; struct bt_hci_cis_params_test cis[0]; } __attribute__ ((packed)); #define BT_HCI_CMD_LE_CREATE_CIS 0x2064 #define BT_HCI_BIT_LE_CREATE_CIS BT_HCI_CMD_BIT(42, 1) struct bt_hci_cis { uint16_t cis_handle; uint16_t acl_handle; } __attribute__ ((packed)); struct bt_hci_cmd_le_create_cis { uint8_t num_cis; struct bt_hci_cis cis[0]; } __attribute__ ((packed)); #define BT_HCI_CMD_LE_REMOVE_CIG 0x2065 #define BT_HCI_BIT_LE_REMOVE_CIG BT_HCI_CMD_BIT(42, 2) struct bt_hci_cmd_le_remove_cig { uint8_t cig_id; } __attribute__ ((packed)); struct bt_hci_rsp_le_remove_cig { uint8_t status; uint8_t cig_id; } __attribute__ ((packed)); #define BT_HCI_CMD_LE_ACCEPT_CIS 0x2066 #define BT_HCI_BIT_LE_ACCEPT_CIS BT_HCI_CMD_BIT(42, 3) struct bt_hci_cmd_le_accept_cis { uint16_t handle; } __attribute__ ((packed)); #define BT_HCI_CMD_LE_REJECT_CIS 0x2067 #define BT_HCI_BIT_LE_REJECT_CIS BT_HCI_CMD_BIT(42, 4) struct bt_hci_cmd_le_reject_cis { uint16_t handle; uint8_t reason; } __attribute__ ((packed)); #define BT_HCI_CMD_LE_CREATE_BIG 0x2068 #define BT_HCI_BIT_LE_CREATE_BIG BT_HCI_CMD_BIT(42, 5) struct bt_hci_bis { uint8_t sdu_interval[3]; uint16_t sdu; uint16_t latency; uint8_t rtn; uint8_t phy; uint8_t packing; uint8_t framing; uint8_t encryption; uint8_t bcode[16]; } __attribute__ ((packed)); struct bt_hci_cmd_le_create_big { uint8_t handle; uint8_t adv_handle; uint8_t num_bis; struct bt_hci_bis bis; } __attribute__ ((packed)); #define BT_HCI_CMD_LE_CREATE_BIG_TEST 0x2069 #define BT_HCI_BIT_LE_CREATE_BIG_TEST BT_HCI_CMD_BIT(42, 6) struct bt_hci_bis_test { uint8_t sdu_interval[3]; uint16_t iso_interval; uint8_t nse; uint16_t sdu; uint16_t pdu; uint8_t phy; uint8_t packing; uint8_t framing; uint8_t bn; uint8_t irc; uint8_t pto; uint8_t encryption; uint8_t bcode[16]; } __attribute__ ((packed)); struct bt_hci_cmd_le_create_big_test { uint8_t big_handle; uint8_t adv_handle; uint8_t num_bis; struct bt_hci_bis_test bis[0]; } __attribute__ ((packed)); #define BT_HCI_CMD_LE_TERM_BIG 0x206a #define BT_HCI_BIT_LE_TERM_BIG BT_HCI_CMD_BIT(42, 7) struct bt_hci_cmd_le_term_big { uint8_t handle; uint8_t reason; } __attribute__ ((packed)); #define BT_HCI_CMD_LE_BIG_CREATE_SYNC 0x206b #define BT_HCI_BIT_LE_BIG_CREATE_SYNC BT_HCI_CMD_BIT(43, 0) struct bt_hci_bis_sync { uint8_t index; } __attribute__ ((packed)); struct bt_hci_cmd_le_big_create_sync { uint8_t handle; uint16_t sync_handle; uint8_t encryption; uint8_t bcode[16]; uint8_t mse; uint16_t timeout; uint8_t num_bis; struct bt_hci_bis_sync bis[0]; } __attribute__ ((packed)); #define BT_HCI_CMD_LE_BIG_TERM_SYNC 0x206c #define BT_HCI_BIT_LE_BIG_TERM_SYNC BT_HCI_CMD_BIT(43, 1) struct bt_hci_cmd_le_big_term_sync { uint8_t handle; } __attribute__ ((packed)); struct bt_hci_rsp_le_big_term_sync { uint8_t status; uint8_t handle; } __attribute__ ((packed)); #define BT_HCI_CMD_LE_REQ_PEER_SCA 0x206d #define BT_HCI_BIT_LE_REQ_PEER_SCA BT_HCI_CMD_BIT(43, 2) struct bt_hci_cmd_le_req_peer_sca { uint16_t handle; } __attribute__ ((packed)); #define BT_HCI_CMD_LE_SETUP_ISO_PATH 0x206e #define BT_HCI_BIT_LE_SETUP_ISO_PATH BT_HCI_CMD_BIT(43, 3) struct bt_hci_cmd_le_setup_iso_path { uint16_t handle; uint8_t direction; uint8_t path; uint8_t codec; uint16_t codec_cid; uint16_t codec_vid; uint8_t delay[3]; uint8_t codec_cfg_len; uint8_t codec_cfg[0]; } __attribute__ ((packed)); struct bt_hci_rsp_le_setup_iso_path { uint8_t status; uint16_t handle; } __attribute__ ((packed)); #define BT_HCI_CMD_LE_REMOVE_ISO_PATH 0x206f #define BT_HCI_BIT_LE_REMOVE_ISO_PATH BT_HCI_CMD_BIT(43, 4) struct bt_hci_cmd_le_remove_iso_path { uint16_t handle; uint8_t direction; } __attribute__ ((packed)); struct bt_hci_rsp_le_remove_iso_path { uint8_t status; uint16_t handle; } __attribute__ ((packed)); #define BT_HCI_CMD_LE_ISO_TX_TEST 0x2070 #define BT_HCI_BIT_LE_ISO_TX_TEST BT_HCI_CMD_BIT(43, 5) #define BT_HCI_CMD_LE_ISO_RX_TEST 0x2071 #define BT_HCI_BIT_LE_ISO_RX_TEST BT_HCI_CMD_BIT(43, 6) #define BT_HCI_CMD_LE_ISO_READ_TEST_COUNTER 0x2072 #define BT_HCI_BIT_LE_ISO_READ_TEST_COUNTER BT_HCI_CMD_BIT(43, 7) #define BT_HCI_CMD_LE_ISO_TEST_END 0x2073 #define BT_HCI_BIT_LE_ISO_TEST_END BT_HCI_CMD_BIT(44, 0) #define BT_HCI_CMD_LE_SET_HOST_FEATURE 0x2074 #define BT_HCI_BIT_LE_SET_HOST_FEATURE BT_HCI_CMD_BIT(44, 1) struct bt_hci_cmd_le_set_host_feature { uint8_t bit_number; uint8_t bit_value; } __attribute__ ((packed)); #define BT_HCI_CMD_LE_READ_ISO_LINK_QUALITY 0x2075 #define BT_HCI_BIT_LE_READ_ISO_LINK_QUALITY BT_HCI_CMD_BIT(45, 1) struct bt_hci_cmd_le_read_iso_link_quality { uint16_t handle; } __attribute__ ((packed)); struct bt_hci_rsp_le_read_iso_link_quality { uint8_t status; uint16_t handle; uint32_t tx_unacked_packets; uint32_t tx_flushed_packets; uint32_t tx_last_subevent_packets; uint32_t retransmitted_packets; uint32_t crc_error_packets; uint32_t rx_unreceived_packets; uint32_t duplicated_packets; } __attribute__ ((packed)); #define BT_HCI_EVT_INQUIRY_COMPLETE 0x01 struct bt_hci_evt_inquiry_complete { uint8_t status; } __attribute__ ((packed)); #define BT_HCI_EVT_INQUIRY_RESULT 0x02 struct bt_hci_evt_inquiry_result { uint8_t num_resp; uint8_t bdaddr[6]; uint8_t pscan_rep_mode; uint8_t pscan_period_mode; uint8_t pscan_mode; uint8_t dev_class[3]; uint16_t clock_offset; } __attribute__ ((packed)); #define BT_HCI_EVT_CONN_COMPLETE 0x03 struct bt_hci_evt_conn_complete { uint8_t status; uint16_t handle; uint8_t bdaddr[6]; uint8_t link_type; uint8_t encr_mode; } __attribute__ ((packed)); #define BT_HCI_EVT_CONN_REQUEST 0x04 struct bt_hci_evt_conn_request { uint8_t bdaddr[6]; uint8_t dev_class[3]; uint8_t link_type; } __attribute__ ((packed)); #define BT_HCI_EVT_DISCONNECT_COMPLETE 0x05 struct bt_hci_evt_disconnect_complete { uint8_t status; uint16_t handle; uint8_t reason; } __attribute__ ((packed)); #define BT_HCI_EVT_AUTH_COMPLETE 0x06 struct bt_hci_evt_auth_complete { uint8_t status; uint16_t handle; } __attribute__ ((packed)); #define BT_HCI_EVT_REMOTE_NAME_REQUEST_COMPLETE 0x07 struct bt_hci_evt_remote_name_request_complete { uint8_t status; uint8_t bdaddr[6]; uint8_t name[248]; } __attribute__ ((packed)); #define BT_HCI_EVT_ENCRYPT_CHANGE 0x08 struct bt_hci_evt_encrypt_change { uint8_t status; uint16_t handle; uint8_t encr_mode; } __attribute__ ((packed)); #define BT_HCI_EVT_CHANGE_CONN_LINK_KEY_COMPLETE 0x09 struct bt_hci_evt_change_conn_link_key_complete { uint8_t status; uint16_t handle; } __attribute__ ((packed)); #define BT_HCI_EVT_LINK_KEY_TYPE_CHANGED 0x0a struct bt_hci_evt_link_key_type_changed { uint8_t status; uint16_t handle; uint8_t key_flag; } __attribute__ ((packed)); #define BT_HCI_EVT_REMOTE_FEATURES_COMPLETE 0x0b struct bt_hci_evt_remote_features_complete { uint8_t status; uint16_t handle; uint8_t features[8]; } __attribute__ ((packed)); #define BT_HCI_EVT_REMOTE_VERSION_COMPLETE 0x0c struct bt_hci_evt_remote_version_complete { uint8_t status; uint16_t handle; uint8_t lmp_ver; uint16_t manufacturer; uint16_t lmp_subver; } __attribute__ ((packed)); #define BT_HCI_EVT_QOS_SETUP_COMPLETE 0x0d struct bt_hci_evt_qos_setup_complete { uint8_t status; uint16_t handle; uint8_t flags; uint8_t service_type; uint32_t token_rate; uint32_t peak_bandwidth; uint32_t latency; uint32_t delay_variation; } __attribute__ ((packed)); #define BT_HCI_EVT_CMD_COMPLETE 0x0e struct bt_hci_evt_cmd_complete { uint8_t ncmd; uint16_t opcode; } __attribute__ ((packed)); #define BT_HCI_EVT_CMD_STATUS 0x0f struct bt_hci_evt_cmd_status { uint8_t status; uint8_t ncmd; uint16_t opcode; } __attribute__ ((packed)); #define BT_HCI_EVT_HARDWARE_ERROR 0x10 struct bt_hci_evt_hardware_error { uint8_t code; } __attribute__ ((packed)); #define BT_HCI_EVT_FLUSH_OCCURRED 0x11 struct bt_hci_evt_flush_occurred { uint16_t handle; } __attribute__ ((packed)); #define BT_HCI_EVT_ROLE_CHANGE 0x12 struct bt_hci_evt_role_change { uint8_t status; uint8_t bdaddr[6]; uint8_t role; } __attribute__ ((packed)); #define BT_HCI_EVT_NUM_COMPLETED_PACKETS 0x13 struct bt_hci_evt_num_completed_packets { uint8_t num_handles; uint16_t handle; uint16_t count; } __attribute__ ((packed)); #define BT_HCI_EVT_MODE_CHANGE 0x14 struct bt_hci_evt_mode_change { uint8_t status; uint16_t handle; uint8_t mode; uint16_t interval; } __attribute__ ((packed)); #define BT_HCI_EVT_RETURN_LINK_KEYS 0x15 struct bt_hci_evt_return_link_keys { uint8_t num_keys; uint8_t keys[0]; } __attribute__ ((packed)); #define BT_HCI_EVT_PIN_CODE_REQUEST 0x16 struct bt_hci_evt_pin_code_request { uint8_t bdaddr[6]; } __attribute__ ((packed)); #define BT_HCI_EVT_LINK_KEY_REQUEST 0x17 struct bt_hci_evt_link_key_request { uint8_t bdaddr[6]; } __attribute__ ((packed)); #define BT_HCI_EVT_LINK_KEY_NOTIFY 0x18 struct bt_hci_evt_link_key_notify { uint8_t bdaddr[6]; uint8_t link_key[16]; uint8_t key_type; } __attribute__ ((packed)); #define BT_HCI_EVT_LOOPBACK_COMMAND 0x19 #define BT_HCI_EVT_DATA_BUFFER_OVERFLOW 0x1a struct bt_hci_evt_data_buffer_overflow { uint8_t link_type; } __attribute__ ((packed)); #define BT_HCI_EVT_MAX_SLOTS_CHANGE 0x1b struct bt_hci_evt_max_slots_change { uint16_t handle; uint8_t max_slots; } __attribute__ ((packed)); #define BT_HCI_EVT_CLOCK_OFFSET_COMPLETE 0x1c struct bt_hci_evt_clock_offset_complete { uint8_t status; uint16_t handle; uint16_t clock_offset; } __attribute__ ((packed)); #define BT_HCI_EVT_CONN_PKT_TYPE_CHANGED 0x1d struct bt_hci_evt_conn_pkt_type_changed { uint8_t status; uint16_t handle; uint16_t pkt_type; } __attribute__ ((packed)); #define BT_HCI_EVT_QOS_VIOLATION 0x1e struct bt_hci_evt_qos_violation { uint16_t handle; } __attribute__ ((packed)); #define BT_HCI_EVT_PSCAN_MODE_CHANGE 0x1f struct bt_hci_evt_pscan_mode_change { uint8_t bdaddr[6]; uint8_t pscan_mode; } __attribute__ ((packed)); #define BT_HCI_EVT_PSCAN_REP_MODE_CHANGE 0x20 struct bt_hci_evt_pscan_rep_mode_change { uint8_t bdaddr[6]; uint8_t pscan_rep_mode; } __attribute__ ((packed)); #define BT_HCI_EVT_FLOW_SPEC_COMPLETE 0x21 struct bt_hci_evt_flow_spec_complete { uint8_t status; uint16_t handle; uint8_t flags; uint8_t direction; uint8_t service_type; uint32_t token_rate; uint32_t token_bucket_size; uint32_t peak_bandwidth; uint32_t access_latency; } __attribute__ ((packed)); #define BT_HCI_EVT_INQUIRY_RESULT_WITH_RSSI 0x22 struct bt_hci_evt_inquiry_result_with_rssi { uint8_t num_resp; uint8_t bdaddr[6]; uint8_t pscan_rep_mode; uint8_t pscan_period_mode; uint8_t dev_class[3]; uint16_t clock_offset; int8_t rssi; } __attribute__ ((packed)); #define BT_HCI_EVT_REMOTE_EXT_FEATURES_COMPLETE 0x23 struct bt_hci_evt_remote_ext_features_complete { uint8_t status; uint16_t handle; uint8_t page; uint8_t max_page; uint8_t features[8]; } __attribute__ ((packed)); #define BT_HCI_EVT_SYNC_CONN_COMPLETE 0x2c struct bt_hci_evt_sync_conn_complete { uint8_t status; uint16_t handle; uint8_t bdaddr[6]; uint8_t link_type; uint8_t tx_interval; uint8_t retrans_window; uint16_t rx_pkt_len; uint16_t tx_pkt_len; uint8_t air_mode; } __attribute__ ((packed)); #define BT_HCI_EVT_SYNC_CONN_CHANGED 0x2d struct bt_hci_evt_sync_conn_changed { uint8_t status; uint16_t handle; uint8_t tx_interval; uint8_t retrans_window; uint16_t rx_pkt_len; uint16_t tx_pkt_len; } __attribute__ ((packed)); #define BT_HCI_EVT_SNIFF_SUBRATING 0x2e struct bt_hci_evt_sniff_subrating { uint8_t status; uint16_t handle; uint16_t max_tx_latency; uint16_t max_rx_latency; uint16_t min_remote_timeout; uint16_t min_local_timeout; } __attribute__ ((packed)); #define BT_HCI_EVT_EXT_INQUIRY_RESULT 0x2f struct bt_hci_evt_ext_inquiry_result { uint8_t num_resp; uint8_t bdaddr[6]; uint8_t pscan_rep_mode; uint8_t pscan_period_mode; uint8_t dev_class[3]; uint16_t clock_offset; int8_t rssi; uint8_t data[240]; } __attribute__ ((packed)); #define BT_HCI_EVT_ENCRYPT_KEY_REFRESH_COMPLETE 0x30 struct bt_hci_evt_encrypt_key_refresh_complete { uint8_t status; uint16_t handle; } __attribute__ ((packed)); #define BT_HCI_EVT_IO_CAPABILITY_REQUEST 0x31 struct bt_hci_evt_io_capability_request { uint8_t bdaddr[6]; } __attribute__ ((packed)); #define BT_HCI_EVT_IO_CAPABILITY_RESPONSE 0x32 struct bt_hci_evt_io_capability_response { uint8_t bdaddr[6]; uint8_t capability; uint8_t oob_data; uint8_t authentication; } __attribute__ ((packed)); #define BT_HCI_EVT_USER_CONFIRM_REQUEST 0x33 struct bt_hci_evt_user_confirm_request { uint8_t bdaddr[6]; uint32_t passkey; } __attribute__ ((packed)); #define BT_HCI_EVT_USER_PASSKEY_REQUEST 0x34 struct bt_hci_evt_user_passkey_request { uint8_t bdaddr[6]; } __attribute__ ((packed)); #define BT_HCI_EVT_REMOTE_OOB_DATA_REQUEST 0x35 struct bt_hci_evt_remote_oob_data_request { uint8_t bdaddr[6]; } __attribute__ ((packed)); #define BT_HCI_EVT_SIMPLE_PAIRING_COMPLETE 0x36 struct bt_hci_evt_simple_pairing_complete { uint8_t status; uint8_t bdaddr[6]; } __attribute__ ((packed)); #define BT_HCI_EVT_LINK_SUPV_TIMEOUT_CHANGED 0x38 struct bt_hci_evt_link_supv_timeout_changed { uint16_t handle; uint16_t timeout; } __attribute__ ((packed)); #define BT_HCI_EVT_ENHANCED_FLUSH_COMPLETE 0x39 struct bt_hci_evt_enhanced_flush_complete { uint16_t handle; } __attribute__ ((packed)); #define BT_HCI_EVT_USER_PASSKEY_NOTIFY 0x3b struct bt_hci_evt_user_passkey_notify { uint8_t bdaddr[6]; uint32_t passkey; } __attribute__ ((packed)); #define BT_HCI_EVT_KEYPRESS_NOTIFY 0x3c struct bt_hci_evt_keypress_notify { uint8_t bdaddr[6]; uint8_t type; } __attribute__ ((packed)); #define BT_HCI_EVT_REMOTE_HOST_FEATURES_NOTIFY 0x3d struct bt_hci_evt_remote_host_features_notify { uint8_t bdaddr[6]; uint8_t features[8]; } __attribute__ ((packed)); #define BT_HCI_EVT_LE_META_EVENT 0x3e #define BT_HCI_EVT_PHY_LINK_COMPLETE 0x40 struct bt_hci_evt_phy_link_complete { uint8_t status; uint8_t phy_handle; } __attribute__ ((packed)); #define BT_HCI_EVT_CHANNEL_SELECTED 0x41 struct bt_hci_evt_channel_selected { uint8_t phy_handle; } __attribute__ ((packed)); #define BT_HCI_EVT_DISCONN_PHY_LINK_COMPLETE 0x42 struct bt_hci_evt_disconn_phy_link_complete { uint8_t status; uint8_t phy_handle; uint8_t reason; } __attribute__ ((packed)); #define BT_HCI_EVT_PHY_LINK_LOSS_EARLY_WARNING 0x43 struct bt_hci_evt_phy_link_loss_early_warning { uint8_t phy_handle; uint8_t reason; } __attribute__ ((packed)); #define BT_HCI_EVT_PHY_LINK_RECOVERY 0x44 struct bt_hci_evt_phy_link_recovery { uint8_t phy_handle; } __attribute__ ((packed)); #define BT_HCI_EVT_LOGIC_LINK_COMPLETE 0x45 struct bt_hci_evt_logic_link_complete { uint8_t status; uint16_t handle; uint8_t phy_handle; uint8_t flow_spec; } __attribute__ ((packed)); #define BT_HCI_EVT_DISCONN_LOGIC_LINK_COMPLETE 0x46 struct bt_hci_evt_disconn_logic_link_complete { uint8_t status; uint16_t handle; uint8_t reason; } __attribute__ ((packed)); #define BT_HCI_EVT_FLOW_SPEC_MODIFY_COMPLETE 0x47 struct bt_hci_evt_flow_spec_modify_complete { uint8_t status; uint16_t handle; } __attribute__ ((packed)); #define BT_HCI_EVT_NUM_COMPLETED_DATA_BLOCKS 0x48 struct bt_hci_evt_num_completed_data_blocks { uint16_t total_num_blocks; uint8_t num_handles; uint16_t handle; uint16_t num_packets; uint16_t num_blocks; } __attribute__ ((packed)); #define BT_HCI_EVT_SHORT_RANGE_MODE_CHANGE 0x4c struct bt_hci_evt_short_range_mode_change { uint8_t status; uint8_t phy_handle; uint8_t mode; } __attribute__ ((packed)); #define BT_HCI_EVT_AMP_STATUS_CHANGE 0x4d struct bt_hci_evt_amp_status_change { uint8_t status; uint8_t amp_status; } __attribute__ ((packed)); #define BT_HCI_EVT_TRIGGERED_CLOCK_CAPTURE 0x4e struct bt_hci_evt_triggered_clock_capture { uint16_t handle; uint8_t type; uint32_t clock; uint16_t clock_offset; } __attribute__ ((packed)); #define BT_HCI_EVT_SYNC_TRAIN_COMPLETE 0x4f struct bt_hci_evt_sync_train_complete { uint8_t status; } __attribute__ ((packed)); #define BT_HCI_EVT_SYNC_TRAIN_RECEIVED 0x50 struct bt_hci_evt_sync_train_received { uint8_t status; uint8_t bdaddr[6]; uint32_t offset; uint8_t map[10]; uint8_t lt_addr; uint32_t instant; uint16_t interval; uint8_t service_data; } __attribute__ ((packed)); #define BT_HCI_EVT_PERIPHERAL_BROADCAST_RECEIVE 0x51 struct bt_hci_evt_peripheral_broadcast_receive { uint8_t bdaddr[6]; uint8_t lt_addr; uint32_t clock; uint32_t offset; uint8_t status; uint8_t fragment; uint8_t length; } __attribute__ ((packed)); #define BT_HCI_EVT_PERIPHERAL_BROADCAST_TIMEOUT 0x52 struct bt_hci_evt_peripheral_broadcast_timeout { uint8_t bdaddr[6]; uint8_t lt_addr; } __attribute__ ((packed)); #define BT_HCI_EVT_TRUNCATED_PAGE_COMPLETE 0x53 struct bt_hci_evt_truncated_page_complete { uint8_t status; uint8_t bdaddr[6]; } __attribute__ ((packed)); #define BT_HCI_EVT_PERIPHERAL_PAGE_RESPONSE_TIMEOUT 0x54 #define BT_HCI_EVT_PERIPHERAL_BROADCAST_CHANNEL_MAP_CHANGE 0x55 struct bt_hci_evt_channel_map_change { uint8_t map[10]; } __attribute__ ((packed)); #define BT_HCI_EVT_INQUIRY_RESPONSE_NOTIFY 0x56 struct bt_hci_evt_inquiry_response_notify { uint8_t lap[3]; int8_t rssi; } __attribute__ ((packed)); #define BT_HCI_EVT_AUTH_PAYLOAD_TIMEOUT_EXPIRED 0x57 struct bt_hci_evt_auth_payload_timeout_expired { uint16_t handle; } __attribute__ ((packed)); #define BT_HCI_EVT_LE_CONN_COMPLETE 0x01 struct bt_hci_evt_le_conn_complete { uint8_t status; uint16_t handle; uint8_t role; uint8_t peer_addr_type; uint8_t peer_addr[6]; uint16_t interval; uint16_t latency; uint16_t supv_timeout; uint8_t clock_accuracy; } __attribute__ ((packed)); #define BT_HCI_EVT_LE_ADV_REPORT 0x02 struct bt_hci_evt_le_adv_report { uint8_t num_reports; uint8_t event_type; uint8_t addr_type; uint8_t addr[6]; uint8_t data_len; uint8_t data[0]; } __attribute__ ((packed)); #define BT_HCI_EVT_LE_CONN_UPDATE_COMPLETE 0x03 struct bt_hci_evt_le_conn_update_complete { uint8_t status; uint16_t handle; uint16_t interval; uint16_t latency; uint16_t supv_timeout; } __attribute__ ((packed)); #define BT_HCI_EVT_LE_REMOTE_FEATURES_COMPLETE 0x04 struct bt_hci_evt_le_remote_features_complete { uint8_t status; uint16_t handle; uint8_t features[8]; } __attribute__ ((packed)); #define BT_HCI_EVT_LE_LONG_TERM_KEY_REQUEST 0x05 struct bt_hci_evt_le_long_term_key_request { uint16_t handle; uint64_t rand; uint16_t ediv; } __attribute__ ((packed)); #define BT_HCI_EVT_LE_CONN_PARAM_REQUEST 0x06 struct bt_hci_evt_le_conn_param_request { uint16_t handle; uint16_t min_interval; uint16_t max_interval; uint16_t latency; uint16_t supv_timeout; } __attribute__ ((packed)); #define BT_HCI_EVT_LE_DATA_LENGTH_CHANGE 0x07 struct bt_hci_evt_le_data_length_change { uint16_t handle; uint16_t max_tx_len; uint16_t max_tx_time; uint16_t max_rx_len; uint16_t max_rx_time; } __attribute__ ((packed)); #define BT_HCI_EVT_LE_READ_LOCAL_PK256_COMPLETE 0x08 struct bt_hci_evt_le_read_local_pk256_complete { uint8_t status; uint8_t local_pk256[64]; } __attribute__ ((packed)); #define BT_HCI_EVT_LE_GENERATE_DHKEY_COMPLETE 0x09 struct bt_hci_evt_le_generate_dhkey_complete { uint8_t status; uint8_t dhkey[32]; } __attribute__ ((packed)); #define BT_HCI_EVT_LE_ENHANCED_CONN_COMPLETE 0x0a struct bt_hci_evt_le_enhanced_conn_complete { uint8_t status; uint16_t handle; uint8_t role; uint8_t peer_addr_type; uint8_t peer_addr[6]; uint8_t local_rpa[6]; uint8_t peer_rpa[6]; uint16_t interval; uint16_t latency; uint16_t supv_timeout; uint8_t clock_accuracy; } __attribute__ ((packed)); #define BT_HCI_EVT_LE_DIRECT_ADV_REPORT 0x0b struct bt_hci_evt_le_direct_adv_report { uint8_t num_reports; uint8_t event_type; uint8_t addr_type; uint8_t addr[6]; uint8_t direct_addr_type; uint8_t direct_addr[6]; int8_t rssi; } __attribute__ ((packed)); #define BT_HCI_EVT_LE_PHY_UPDATE_COMPLETE 0x0c struct bt_hci_evt_le_phy_update_complete { uint8_t status; uint16_t handle; uint8_t tx_phy; uint8_t rx_phy; } __attribute__ ((packed)); #define BT_HCI_EVT_LE_EXT_ADV_REPORT 0x0d struct bt_hci_evt_le_ext_adv_report { uint8_t num_reports; } __attribute__ ((packed)); struct bt_hci_le_ext_adv_report { uint16_t event_type; uint8_t addr_type; uint8_t addr[6]; uint8_t primary_phy; uint8_t secondary_phy; uint8_t sid; uint8_t tx_power; int8_t rssi; uint16_t interval; uint8_t direct_addr_type; uint8_t direct_addr[6]; uint8_t data_len; uint8_t data[0]; } __attribute__ ((packed)); #define BT_HCI_EVT_LE_PA_SYNC_ESTABLISHED 0x0e struct bt_hci_evt_le_per_sync_established { uint8_t status; uint16_t handle; uint8_t sid; uint8_t addr_type; uint8_t addr[6]; uint8_t phy; uint16_t interval; uint8_t clock_accuracy; } __attribute__ ((packed)); struct bt_hci_le_pa_base_codec { uint8_t id; uint16_t cid; uint16_t vid; } __attribute__ ((packed)); struct bt_hci_lv_data { uint8_t len; uint8_t data[]; } __attribute__ ((packed)); struct bt_hci_le_pa_base_bis { uint8_t index; struct bt_hci_lv_data codec_cfg[]; } __attribute__ ((packed)); struct bt_hci_le_pa_base_subgroup { uint8_t num_bis; struct bt_hci_le_pa_base_codec codec; uint8_t data[]; } __attribute__ ((packed)); struct bt_hci_le_pa_base_data { uint8_t pd[3]; uint8_t num_subgroups; struct bt_hci_le_pa_base_subgroup subgroups[]; } __attribute__ ((packed)); #define BT_HCI_EVT_LE_PA_REPORT 0x0f struct bt_hci_le_pa_report { uint16_t handle; uint8_t tx_power; int8_t rssi; uint8_t cte_type; uint8_t data_status; uint8_t data_len; uint8_t data[0]; } __attribute__ ((packed)); #define BT_HCI_EVT_LE_PA_SYNC_LOST 0x10 struct bt_hci_evt_le_per_sync_lost { uint16_t handle; } __attribute__ ((packed)); #define BT_HCI_EVT_LE_ADV_SET_TERM 0x12 struct bt_hci_evt_le_adv_set_term { uint8_t status; uint8_t handle; uint16_t conn_handle; uint8_t num_evts; } __attribute__ ((packed)); #define BT_HCI_EVT_LE_SCAN_REQ_RECEIVED 0x13 struct bt_hci_evt_le_scan_req_received { uint8_t handle; uint8_t scanner_addr_type; uint8_t scanner_addr[6]; } __attribute__ ((packed)); #define BT_HCI_EVT_LE_CHAN_SELECT_ALG 0x14 struct bt_hci_evt_le_chan_select_alg { uint16_t handle; uint8_t algorithm; } __attribute__ ((packed)); #define BT_HCI_EVT_LE_CTE_REQUEST_FAILED 0x17 struct bt_hci_evt_le_cte_request_failed { uint8_t status; uint16_t handle; } __attribute__ ((packed)); #define BT_HCI_EVT_LE_PA_SYNC_TRANS_REC 0x18 struct bt_hci_evt_le_pa_sync_trans_rec { uint8_t status; uint16_t handle; uint16_t service_data; uint16_t sync_handle; uint8_t sid; uint8_t addr_type; uint8_t addr[6]; uint8_t phy; uint16_t interval; uint8_t clock_accuracy; } __attribute__ ((packed)); #define BT_HCI_EVT_LE_CIS_ESTABLISHED 0x19 struct bt_hci_evt_le_cis_established { uint8_t status; uint16_t conn_handle; uint8_t cig_sync_delay[3]; uint8_t cis_sync_delay[3]; uint8_t c_latency[3]; uint8_t p_latency[3]; uint8_t c_phy; uint8_t p_phy; uint8_t nse; uint8_t c_bn; uint8_t p_bn; uint8_t c_ft; uint8_t p_ft; uint16_t c_mtu; uint16_t p_mtu; uint16_t interval; } __attribute__ ((packed)); #define BT_HCI_EVT_LE_CIS_REQ 0x1a struct bt_hci_evt_le_cis_req { uint16_t acl_handle; uint16_t cis_handle; uint8_t cig_id; uint8_t cis_id; } __attribute__ ((packed)); #define BT_HCI_EVT_LE_BIG_COMPLETE 0x1b struct bt_hci_evt_le_big_complete { uint8_t status; uint8_t handle; uint8_t sync_delay[3]; uint8_t latency[3]; uint8_t phy; uint8_t nse; uint8_t bn; uint8_t pto; uint8_t irc; uint16_t max_pdu; uint16_t interval; uint8_t num_bis; uint16_t bis_handle[0]; } __attribute__ ((packed)); #define BT_HCI_EVT_LE_BIG_TERMINATE 0x1c struct bt_hci_evt_le_big_terminate { uint8_t handle; uint8_t reason; } __attribute__ ((packed)); #define BT_HCI_EVT_LE_BIG_SYNC_ESTABILISHED 0x1d struct bt_hci_evt_le_big_sync_estabilished { uint8_t status; uint8_t handle; uint8_t latency[3]; uint8_t nse; uint8_t bn; uint8_t pto; uint8_t irc; uint16_t max_pdu; uint16_t interval; uint8_t num_bis; uint16_t bis[0]; } __attribute__ ((packed)); #define BT_HCI_EVT_LE_BIG_SYNC_LOST 0x1e struct bt_hci_evt_le_big_sync_lost { uint8_t big_handle; uint8_t reason; } __attribute__ ((packed)); #define BT_HCI_EVT_LE_REQ_PEER_SCA_COMPLETE 0x1f struct bt_hci_evt_le_req_peer_sca_complete { uint8_t status; uint16_t handle; uint8_t sca; } __attribute__ ((packed)); #define BT_HCI_EVT_LE_BIG_INFO_ADV_REPORT 0x22 struct bt_hci_evt_le_big_info_adv_report { uint16_t sync_handle; uint8_t num_bis; uint8_t nse; uint16_t iso_interval; uint8_t bn; uint8_t pto; uint8_t irc; uint16_t max_pdu; uint8_t sdu_interval[3]; uint16_t max_sdu; uint8_t phy; uint8_t framing; uint8_t encryption; } __attribute__ ((packed)); #define BT_HCI_ERR_SUCCESS 0x00 #define BT_HCI_ERR_UNKNOWN_COMMAND 0x01 #define BT_HCI_ERR_UNKNOWN_CONN_ID 0x02 #define BT_HCI_ERR_HARDWARE_FAILURE 0x03 #define BT_HCI_ERR_PAGE_TIMEOUT 0x04 #define BT_HCI_ERR_AUTH_FAILURE 0x05 #define BT_HCI_ERR_PIN_OR_KEY_MISSING 0x06 #define BT_HCI_ERR_MEM_CAPACITY_EXCEEDED 0x07 #define BT_HCI_ERR_CONN_ALREADY_EXISTS 0x0b #define BT_HCI_ERR_COMMAND_DISALLOWED 0x0c #define BT_HCI_ERR_UNSUPPORTED_FEATURE 0x11 #define BT_HCI_ERR_INVALID_PARAMETERS 0x12 #define BT_HCI_ERR_LOCAL_HOST_TERM 0x16 #define BT_HCI_ERR_UNSPECIFIED_ERROR 0x1f #define BT_HCI_ERR_ADV_TIMEOUT 0x3c #define BT_HCI_ERR_CONN_FAILED_TO_ESTABLISH 0x3e #define BT_HCI_ERR_UNKNOWN_ADVERTISING_ID 0x42 #define BT_HCI_ERR_CANCELLED 0x44 #define BT_HCI_ERR_ENC_MODE_NOT_ACCEPTABLE 0x25 struct bt_l2cap_hdr { uint16_t len; uint16_t cid; uint8_t data[]; } __attribute__ ((packed)); struct bt_l2cap_hdr_sig { uint8_t code; uint8_t ident; uint16_t len; } __attribute__ ((packed)); #define BT_L2CAP_PDU_CMD_REJECT 0x01 struct bt_l2cap_pdu_cmd_reject { uint16_t reason; } __attribute__ ((packed)); #define BT_L2CAP_PDU_CONN_REQ 0x02 struct bt_l2cap_pdu_conn_req { uint16_t psm; uint16_t scid; } __attribute__ ((packed)); #define BT_L2CAP_PDU_CONN_RSP 0x03 struct bt_l2cap_pdu_conn_rsp { uint16_t dcid; uint16_t scid; uint16_t result; uint16_t status; } __attribute__ ((packed)); #define BT_L2CAP_PDU_CONFIG_REQ 0x04 struct bt_l2cap_pdu_config_req { uint16_t dcid; uint16_t flags; } __attribute__ ((packed)); #define BT_L2CAP_PDU_CONFIG_RSP 0x05 struct bt_l2cap_pdu_config_rsp { uint16_t scid; uint16_t flags; uint16_t result; } __attribute__ ((packed)); #define BT_L2CAP_PDU_DISCONN_REQ 0x06 struct bt_l2cap_pdu_disconn_req { uint16_t dcid; uint16_t scid; } __attribute__ ((packed)); #define BT_L2CAP_PDU_DISCONN_RSP 0x07 struct bt_l2cap_pdu_disconn_rsp { uint16_t dcid; uint16_t scid; } __attribute__ ((packed)); #define BT_L2CAP_PDU_ECHO_REQ 0x08 #define BT_L2CAP_PDU_ECHO_RSP 0x09 #define BT_L2CAP_PDU_INFO_REQ 0x0a struct bt_l2cap_pdu_info_req { uint16_t type; } __attribute__ ((packed)); #define BT_L2CAP_PDU_INFO_RSP 0x0b struct bt_l2cap_pdu_info_rsp { uint16_t type; uint16_t result; uint8_t data[0]; } __attribute__ ((packed)); #define BT_L2CAP_PDU_CREATE_CHAN_REQ 0x0c struct bt_l2cap_pdu_create_chan_req { uint16_t psm; uint16_t scid; uint8_t ctrlid; } __attribute__ ((packed)); #define BT_L2CAP_PDU_CREATE_CHAN_RSP 0x0d struct bt_l2cap_pdu_create_chan_rsp { uint16_t dcid; uint16_t scid; uint16_t result; uint16_t status; } __attribute__ ((packed)); #define BT_L2CAP_PDU_MOVE_CHAN_REQ 0x0e struct bt_l2cap_pdu_move_chan_req { uint16_t icid; uint8_t ctrlid; } __attribute__ ((packed)); #define BT_L2CAP_PDU_MOVE_CHAN_RSP 0x0f struct bt_l2cap_pdu_move_chan_rsp { uint16_t icid; uint16_t result; } __attribute__ ((packed)); #define BT_L2CAP_PDU_MOVE_CHAN_CFM 0x10 struct bt_l2cap_pdu_move_chan_cfm { uint16_t icid; uint16_t result; } __attribute__ ((packed)); #define BT_L2CAP_PDU_MOVE_CHAN_CFM_RSP 0x11 struct bt_l2cap_pdu_move_chan_cfm_rsp { uint16_t icid; } __attribute__ ((packed)); #define BT_L2CAP_PDU_CONN_PARAM_REQ 0x12 struct bt_l2cap_pdu_conn_param_req { uint16_t min_interval; uint16_t max_interval; uint16_t latency; uint16_t timeout; } __attribute__ ((packed)); #define BT_L2CAP_PDU_CONN_PARAM_RSP 0x13 struct bt_l2cap_pdu_conn_param_rsp { uint16_t result; } __attribute__ ((packed)); #define BT_L2CAP_PDU_LE_CONN_REQ 0x14 struct bt_l2cap_pdu_le_conn_req { uint16_t psm; uint16_t scid; uint16_t mtu; uint16_t mps; uint16_t credits; } __attribute__ ((packed)); #define BT_L2CAP_PDU_LE_CONN_RSP 0x15 struct bt_l2cap_pdu_le_conn_rsp { uint16_t dcid; uint16_t mtu; uint16_t mps; uint16_t credits; uint16_t result; } __attribute__ ((packed)); #define BT_L2CAP_PDU_LE_FLOWCTL_CREDS 0x16 struct bt_l2cap_pdu_le_flowctl_creds { uint16_t cid; uint16_t credits; } __attribute__ ((packed)); #define BT_L2CAP_PDU_ECRED_CONN_REQ 0x17 struct bt_l2cap_pdu_ecred_conn_req { uint16_t psm; uint16_t mtu; uint16_t mps; uint16_t credits; uint16_t scid[0]; } __attribute__ ((packed)); #define BT_L2CAP_PDU_ECRED_CONN_RSP 0x18 struct bt_l2cap_pdu_ecred_conn_rsp { uint16_t mtu; uint16_t mps; uint16_t credits; uint16_t result; uint16_t dcid[0]; } __attribute__ ((packed)); #define BT_L2CAP_PDU_ECRED_RECONF_REQ 0x19 struct bt_l2cap_pdu_ecred_reconf_req { uint16_t mtu; uint16_t mps; uint16_t scid[0]; } __attribute__ ((packed)); #define BT_L2CAP_PDU_ECRED_RECONF_RSP 0x1a struct bt_l2cap_pdu_ecred_reconf_rsp { uint16_t result; } __attribute__ ((packed)); struct bt_l2cap_hdr_connless { uint16_t psm; } __attribute__ ((packed)); struct bt_l2cap_hdr_amp { uint8_t code; uint8_t ident; uint16_t len; } __attribute__ ((packed)); #define BT_L2CAP_AMP_CMD_REJECT 0x01 struct bt_l2cap_amp_cmd_reject { uint16_t reason; } __attribute__ ((packed)); #define BT_L2CAP_AMP_DISCOVER_REQ 0x02 struct bt_l2cap_amp_discover_req { uint16_t size; uint16_t features; } __attribute__ ((packed)); #define BT_L2CAP_AMP_DISCOVER_RSP 0x03 struct bt_l2cap_amp_discover_rsp { uint16_t size; uint16_t features; } __attribute__ ((packed)); #define BT_L2CAP_AMP_CHANGE_NOTIFY 0x04 #define BT_L2CAP_AMP_CHANGE_RESPONSE 0x05 #define BT_L2CAP_AMP_GET_INFO_REQ 0x06 struct bt_l2cap_amp_get_info_req { uint8_t ctrlid; } __attribute__ ((packed)); #define BT_L2CAP_AMP_GET_INFO_RSP 0x07 struct bt_l2cap_amp_get_info_rsp { uint8_t ctrlid; uint8_t status; uint32_t total_bw; uint32_t max_bw; uint32_t min_latency; uint16_t pal_cap; uint16_t max_assoc_len; } __attribute__ ((packed)); #define BT_L2CAP_AMP_GET_ASSOC_REQ 0x08 struct bt_l2cap_amp_get_assoc_req { uint8_t ctrlid; } __attribute__ ((packed)); #define BT_L2CAP_AMP_GET_ASSOC_RSP 0x09 struct bt_l2cap_amp_get_assoc_rsp { uint8_t ctrlid; uint8_t status; } __attribute__ ((packed)); #define BT_L2CAP_AMP_CREATE_PHY_LINK_REQ 0x0a struct bt_l2cap_amp_create_phy_link_req { uint8_t local_ctrlid; uint8_t remote_ctrlid; } __attribute__ ((packed)); #define BT_L2CAP_AMP_CREATE_PHY_LINK_RSP 0x0b struct bt_l2cap_amp_create_phy_link_rsp { uint8_t local_ctrlid; uint8_t remote_ctrlid; uint8_t status; } __attribute__ ((packed)); #define BT_L2CAP_AMP_DISCONN_PHY_LINK_REQ 0x0c struct bt_l2cap_amp_disconn_phy_link_req { uint8_t local_ctrlid; uint8_t remote_ctrlid; } __attribute__ ((packed)); #define BT_L2CAP_AMP_DISCONN_PHY_LINK_RSP 0x0d struct bt_l2cap_amp_disconn_phy_link_rsp { uint8_t local_ctrlid; uint8_t remote_ctrlid; uint8_t status; } __attribute__ ((packed)); struct bt_l2cap_hdr_att { uint8_t code; } __attribute__ ((packed)); #define BT_L2CAP_ATT_ERROR_RESPONSE 0x01 struct bt_l2cap_att_error_response { uint8_t request; uint16_t handle; uint8_t error; } __attribute__ ((packed)); #define BT_L2CAP_ATT_EXCHANGE_MTU_REQ 0x02 struct bt_l2cap_att_exchange_mtu_req { uint16_t mtu; } __attribute__ ((packed)); #define BT_L2CAP_ATT_EXCHANGE_MTU_RSP 0x03 struct bt_l2cap_att_exchange_mtu_rsp { uint16_t mtu; } __attribute__ ((packed)); #define BT_L2CAP_ATT_READ_TYPE_REQ 0x08 struct bt_l2cap_att_read_type_req { uint16_t start_handle; uint16_t end_handle; } __attribute__ ((packed)); #define BT_L2CAP_ATT_READ_TYPE_RSP 0x09 struct bt_l2cap_att_read_type_rsp { uint8_t length; } __attribute__ ((packed)); #define BT_L2CAP_ATT_READ_REQ 0x0a struct bt_l2cap_att_read_req { uint16_t handle; } __attribute__ ((packed)); #define BT_L2CAP_ATT_READ_RSP 0x0b #define BT_L2CAP_ATT_READ_GROUP_TYPE_REQ 0x10 struct bt_l2cap_att_read_group_type_req { uint16_t start_handle; uint16_t end_handle; } __attribute__ ((packed)); #define BT_L2CAP_ATT_READ_GROUP_TYPE_RSP 0x11 struct bt_l2cap_att_read_group_type_rsp { uint8_t length; } __attribute__ ((packed)); #define BT_L2CAP_ATT_HANDLE_VALUE_NOTIFY 0x1b struct bt_l2cap_att_handle_value_notify { uint16_t handle; } __attribute__ ((packed)); #define BT_L2CAP_ATT_HANDLE_VALUE_IND 0x1d struct bt_l2cap_att_handle_value_ind { uint16_t handle; } __attribute__ ((packed)); #define BT_L2CAP_ATT_HANDLE_VALUE_CONF 0x1e struct bt_l2cap_hdr_smp { uint8_t code; } __attribute__ ((packed)); #define BT_L2CAP_SMP_PAIRING_REQUEST 0x01 struct bt_l2cap_smp_pairing_request { uint8_t io_capa; uint8_t oob_data; uint8_t auth_req; uint8_t max_key_size; uint8_t init_key_dist; uint8_t resp_key_dist; } __attribute__ ((packed)); #define BT_L2CAP_SMP_PAIRING_RESPONSE 0x02 struct bt_l2cap_smp_pairing_response { uint8_t io_capa; uint8_t oob_data; uint8_t auth_req; uint8_t max_key_size; uint8_t init_key_dist; uint8_t resp_key_dist; } __attribute__ ((packed)); #define BT_L2CAP_SMP_PAIRING_CONFIRM 0x03 struct bt_l2cap_smp_pairing_confirm { uint8_t value[16]; } __attribute__ ((packed)); #define BT_L2CAP_SMP_PAIRING_RANDOM 0x04 struct bt_l2cap_smp_pairing_random { uint8_t value[16]; } __attribute__ ((packed)); #define BT_L2CAP_SMP_PAIRING_FAILED 0x05 struct bt_l2cap_smp_pairing_failed { uint8_t reason; } __attribute__ ((packed)); #define BT_L2CAP_SMP_ENCRYPT_INFO 0x06 struct bt_l2cap_smp_encrypt_info { uint8_t ltk[16]; } __attribute__ ((packed)); #define BT_L2CAP_SMP_CENTRAL_IDENT 0x07 struct bt_l2cap_smp_central_ident { uint16_t ediv; uint64_t rand; } __attribute__ ((packed)); #define BT_L2CAP_SMP_IDENT_INFO 0x08 struct bt_l2cap_smp_ident_info { uint8_t irk[16]; } __attribute__ ((packed)); #define BT_L2CAP_SMP_IDENT_ADDR_INFO 0x09 struct bt_l2cap_smp_ident_addr_info { uint8_t addr_type; uint8_t addr[6]; } __attribute__ ((packed)); #define BT_L2CAP_SMP_SIGNING_INFO 0x0a struct bt_l2cap_smp_signing_info { uint8_t csrk[16]; } __attribute__ ((packed)); #define BT_L2CAP_SMP_SECURITY_REQUEST 0x0b struct bt_l2cap_smp_security_request { uint8_t auth_req; } __attribute__ ((packed)); #define BT_L2CAP_SMP_PUBLIC_KEY 0x0c struct bt_l2cap_smp_public_key { uint8_t x[32]; uint8_t y[32]; } __attribute__ ((packed)); #define BT_L2CAP_SMP_DHKEY_CHECK 0x0d struct bt_l2cap_smp_dhkey_check { uint8_t e[16]; } __attribute__ ((packed)); #define BT_L2CAP_SMP_KEYPRESS_NOTIFY 0x0e struct bt_l2cap_smp_keypress_notify { uint8_t type; } __attribute__ ((packed)); struct bt_sdp_hdr { uint8_t pdu; uint16_t tid; uint16_t plen; } __attribute__ ((packed)); bluez-5.82/monitor/PaxHeaders/lmp.c0000644000000000000000000000005014131623652014274 xustar0020 atime=1743516011 20 ctime=1743591282 bluez-5.82/monitor/lmp.c0000644000000000000000000005114114131623652013757 0ustar00rootroot// SPDX-License-Identifier: LGPL-2.1-or-later /* * * BlueZ - Bluetooth protocol stack for Linux * * Copyright (C) 2011-2014 Intel Corporation * Copyright (C) 2002-2010 Marcel Holtmann * * */ #ifdef HAVE_CONFIG_H #include #endif #define _GNU_SOURCE #include #include #include "src/shared/util.h" #include "display.h" #include "packet.h" #include "bt.h" #include "lmp.h" #define COLOR_OPCODE COLOR_MAGENTA #define COLOR_OPCODE_UNKNOWN COLOR_WHITE_BG static const char *get_opcode_str(uint16_t opcode); static void print_opcode(uint16_t opcode) { const char *str; str = get_opcode_str(opcode); if (!str) str = "Unknown"; if (opcode & 0xff00) print_field("Operation: %s (%u/%u)", str, opcode >> 8, opcode & 0xff); else print_field("Operation: %s (%u)", str, opcode); } static void name_req(const void *data, uint8_t size) { const struct bt_lmp_name_req *pdu = data; print_field("Offset: %u", pdu->offset); } static void name_rsp(const void *data, uint8_t size) { const struct bt_lmp_name_rsp *pdu = data; char str[15]; memcpy(str, pdu->fragment, 14); str[14] = '\0'; print_field("Offset: %u", pdu->offset); print_field("Length: %u", pdu->length); print_field("Fragment: %s", str); } static void accepted(const void *data, uint8_t size) { const struct bt_lmp_accepted *pdu = data; print_opcode(pdu->opcode); } static void not_accepted(const void *data, uint8_t size) { const struct bt_lmp_not_accepted *pdu = data; print_opcode(pdu->opcode); packet_print_error("Error code", pdu->error); } static void clkoffset_req(const void *data, uint8_t size) { } static void clkoffset_rsp(const void *data, uint8_t size) { const struct bt_lmp_clkoffset_rsp *pdu = data; print_field("Clock offset: 0x%4.4x", le16_to_cpu(pdu->offset)); } static void detach(const void *data, uint8_t size) { const struct bt_lmp_detach *pdu = data; packet_print_error("Error code", pdu->error); } static void au_rand(const void *data, uint8_t size) { const struct bt_lmp_au_rand *pdu = data; packet_hexdump(pdu->number, 16); } static void sres(const void *data, uint8_t size) { const struct bt_lmp_sres *pdu = data; packet_hexdump(pdu->response, 4); } static void encryption_mode_req(const void *data, uint8_t size) { const struct bt_lmp_encryption_mode_req *pdu = data; const char *str; switch (pdu->mode) { case 0x00: str = "No encryption"; break; case 0x01: str = "Encryption"; break; case 0x02: str = "Encryption"; break; default: str = "Reserved"; break; } print_field("Mode: %s (%u)", str, pdu->mode); } static void encryption_key_size_req(const void *data, uint8_t size) { const struct bt_lmp_encryption_key_size_req *pdu = data; print_field("Key size: %u", pdu->key_size); } static void start_encryption_req(const void *data, uint8_t size) { const struct bt_lmp_start_encryption_req *pdu = data; packet_hexdump(pdu->number, 16); } static void stop_encryption_req(const void *data, uint8_t size) { } static void switch_req(const void *data, uint8_t size) { const struct bt_lmp_switch_req *pdu = data; print_field("Instant: 0x%8.8x", le32_to_cpu(pdu->instant)); } static void unsniff_req(const void *data, uint8_t size) { } static void max_power(const void *data, uint8_t size) { } static void min_power(const void *data, uint8_t size) { } static void auto_rate(const void *data, uint8_t size) { } static void preferred_rate(const void *data, uint8_t size) { const struct bt_lmp_preferred_rate *pdu = data; const char *str; str = (pdu->rate & 0x01) ? "do not use FEC" : "use FEC"; print_field("Basic data rate: %s (0x%02x)", str, pdu->rate & 0x01); switch ((pdu->rate & 0x06) >> 1) { case 0: str = "No packet-size preference available"; break; case 1: str = "use 1-slot packets"; break; case 2: str = "use 3-slot packets"; break; case 3: str = "use 5-slot packets"; break; } print_field("Basic data rate: %s (0x%02x)", str, pdu->rate & 0x06); switch ((pdu->rate & 0x11) >> 3) { case 0: str = "use DM1 packets"; break; case 1: str = "use 2 Mb/s packets"; break; case 2: str = "use 3 MB/s packets"; break; case 3: str = "reserved"; break; } print_field("Enhanced data rate: %s (0x%2.2x)", str, pdu->rate & 0x11); switch ((pdu->rate & 0x60) >> 5) { case 0: str = "No packet-size preference available"; break; case 1: str = "use 1-slot packets"; break; case 2: str = "use 3-slot packets"; break; case 3: str = "use 5-slot packets"; break; } print_field("Enhanced data rate: %s (0x%2.2x)", str, pdu->rate & 0x60); } static void version_req(const void *data, uint8_t size) { const struct bt_lmp_version_req *pdu = data; packet_print_version("Version", pdu->version, "Subversion", le16_to_cpu(pdu->subversion)); packet_print_company("Company", le16_to_cpu(pdu->company)); } static void version_res(const void *data, uint8_t size) { const struct bt_lmp_version_res *pdu = data; packet_print_version("Version", pdu->version, "Subversion", le16_to_cpu(pdu->subversion)); packet_print_company("Company", le16_to_cpu(pdu->company)); } static void features_req(const void *data, uint8_t size) { const struct bt_lmp_features_req *pdu = data; packet_print_features_lmp(pdu->features, 0x00); } static void features_res(const void *data, uint8_t size) { const struct bt_lmp_features_res *pdu = data; packet_print_features_lmp(pdu->features, 0x00); } static void max_slot(const void *data, uint8_t size) { const struct bt_lmp_max_slot *pdu = data; print_field("Slots: 0x%4.4x", pdu->slots); } static void max_slot_req(const void *data, uint8_t size) { const struct bt_lmp_max_slot_req *pdu = data; print_field("Slots: 0x%4.4x", pdu->slots); } static void timing_accuracy_req(const void *data, uint8_t size) { } static void timing_accuracy_res(const void *data, uint8_t size) { const struct bt_lmp_timing_accuracy_res *pdu = data; print_field("Drift: %u ppm", pdu->drift); print_field("Jitter: %u usec", pdu->jitter); } static void setup_complete(const void *data, uint8_t size) { } static void use_semi_permanent_key(const void *data, uint8_t size) { } static void host_connection_req(const void *data, uint8_t size) { } static void slot_offset(const void *data, uint8_t size) { const struct bt_lmp_slot_offset *pdu = data; print_field("Offset: %u usec", le16_to_cpu(pdu->offset)); packet_print_addr("Address", pdu->bdaddr, 0x00); } static void page_scan_mode_req(const void *data, uint8_t size) { const struct bt_lmp_page_scan_mode_req *pdu = data; const char *str; switch (pdu->scheme) { case 0x00: str = "Mandatory"; break; default: str = "Reserved"; break; } print_field("Paging scheme: %s (%u)", str, pdu->scheme); if (pdu->scheme == 0x00) { switch (pdu->settings) { case 0x00: str = "R0"; break; case 0x01: str = "R1"; break; case 0x02: str = "R2"; break; default: str = "Reserved"; break; } } else str = "Reserved"; print_field("Paging scheme settings: %s (%u)", str, pdu->settings); } static void test_activate(const void *data, uint8_t size) { } static void encryption_key_size_mask_req(const void *data, uint8_t size) { } static void set_afh(const void *data, uint8_t size) { const struct bt_lmp_set_afh *pdu = data; const char *str; print_field("Instant: %u", le32_to_cpu(pdu->instant)); switch (pdu->mode) { case 0x00: str = "Disabled"; break; case 0x01: str = "Enabled"; break; default: str = "Reserved"; break; } print_field("Mode: %s (0x%2.2x)", str, pdu->mode); packet_print_channel_map_lmp(pdu->map); } static void encapsulated_header(const void *data, uint8_t size) { const struct bt_lmp_encapsulated_header *pdu = data; const char *str; print_field("Major type: %u", pdu->major); print_field("Minor type: %u", pdu->minor); if (pdu->major == 0x01) { switch (pdu->minor) { case 0x01: str = "P-192 Public Key"; break; case 0x02: str = "P-256 Public Key"; break; default: str = "Reserved"; break; } print_field(" %s", str); } print_field("Length: %u", pdu->length); } static void encapsulated_payload(const void *data, uint8_t size) { const struct bt_lmp_encapsulated_payload *pdu = data; packet_hexdump(pdu->data, 16); } static void simple_pairing_confirm(const void *data, uint8_t size) { const struct bt_lmp_simple_pairing_confirm *pdu = data; packet_hexdump(pdu->value, 16); } static void simple_pairing_number(const void *data, uint8_t size) { const struct bt_lmp_simple_pairing_number *pdu = data; packet_hexdump(pdu->value, 16); } static void dhkey_check(const void *data, uint8_t size) { const struct bt_lmp_dhkey_check *pdu = data; packet_hexdump(pdu->value, 16); } static void accepted_ext(const void *data, uint8_t size) { const struct bt_lmp_accepted_ext *pdu = data; uint16_t opcode; switch (pdu->escape) { case 127: opcode = LMP_ESC4(pdu->opcode); break; default: return; } print_opcode(opcode); } static void not_accepted_ext(const void *data, uint8_t size) { const struct bt_lmp_not_accepted_ext *pdu = data; uint16_t opcode; switch (pdu->escape) { case 127: opcode = LMP_ESC4(pdu->opcode); break; default: return; } print_opcode(opcode); print_field("Error code: %u", pdu->error); } static void features_req_ext(const void *data, uint8_t size) { const struct bt_lmp_features_req_ext *pdu = data; print_field("Features page: %u", pdu->page); print_field("Max supported page: %u", pdu->max_page); packet_print_features_lmp(pdu->features, pdu->page); } static void features_res_ext(const void *data, uint8_t size) { const struct bt_lmp_features_res_ext *pdu = data; print_field("Features page: %u", pdu->page); print_field("Max supported page: %u", pdu->max_page); packet_print_features_lmp(pdu->features, pdu->page); } static void packet_type_table_req(const void *data, uint8_t size) { const struct bt_lmp_packet_type_table_req *pdu = data; const char *str; switch (pdu->table) { case 0x00: str = "1 Mbps only"; break; case 0x01: str = "2/3 Mbps"; break; default: str = "Reserved"; break; } print_field("Table: %s (0x%2.2x)", str, pdu->table); } static void channel_classification_req(const void *data, uint8_t size) { const struct bt_lmp_channel_classification_req *pdu = data; const char *str; switch (pdu->mode) { case 0x00: str = "Disabled"; break; case 0x01: str = "Enabled"; break; default: str = "Reserved"; break; } print_field("Reporting mode: %s (0x%2.2x)", str, pdu->mode); print_field("Min interval: 0x%2.2x", pdu->min_interval); print_field("Max interval: 0x%2.2x", pdu->max_interval); } static void channel_classification(const void *data, uint8_t size) { const struct bt_lmp_channel_classification *pdu = data; char str[21]; int i; for (i = 0; i < 10; i++) sprintf(str + (i * 2), "%2.2x", pdu->classification[i]); print_field("Classification: 0x%s", str); } static void pause_encryption_req(const void *data, uint8_t size) { } static void resume_encryption_req(const void *data, uint8_t size) { } static void io_capability_req(const void *data, uint8_t size) { const struct bt_lmp_io_capability_req *pdu = data; const char *str; packet_print_io_capability(pdu->capability); switch (pdu->oob_data) { case 0x00: str = "No authentication data received"; break; case 0x01: str = "Authentication data received"; break; default: str = "Reserved"; break; } print_field("OOB data: %s (0x%2.2x)", str, pdu->oob_data); packet_print_io_authentication(pdu->authentication); } static void io_capability_res(const void *data, uint8_t size) { const struct bt_lmp_io_capability_res *pdu = data; const char *str; packet_print_io_capability(pdu->capability); switch (pdu->oob_data) { case 0x00: str = "No authentication data received"; break; case 0x01: str = "Authentication data received"; break; default: str = "Reserved"; break; } print_field("OOB data: %s (0x%2.2x)", str, pdu->oob_data); packet_print_io_authentication(pdu->authentication); } static void numeric_comparison_failed(const void *data, uint8_t size) { } static void passkey_failed(const void *data, uint8_t size) { } static void oob_failed(const void *data, uint8_t size) { } static void power_control_req(const void *data, uint8_t size) { const struct bt_lmp_power_control_req *pdu = data; const char *str; switch (pdu->request) { case 0x00: str = "Decrement power one step"; break; case 0x01: str = "Increment power one step"; break; case 0x02: str = "Increase to maximum power"; break; default: str = "Reserved"; break; } print_field("Request: %s (0x%2.2x)", str, pdu->request); } static void power_control_res(const void *data, uint8_t size) { const struct bt_lmp_power_control_res *pdu = data; const char *str; print_field("Response: 0x%2.2x", pdu->response); switch (pdu->response & 0x03) { case 0x00: str = "Not supported"; break; case 0x01: str = "Changed one step"; break; case 0x02: str = "Max power"; break; case 0x03: str = "Min power"; break; default: str = "Reserved"; break; } print_field(" GFSK: %s", str); switch ((pdu->response & 0x0c) >> 2) { case 0x00: str = "Not supported"; break; case 0x01: str = "Changed one step"; break; case 0x02: str = "Max power"; break; case 0x03: str = "Min power"; break; default: str = "Reserved"; break; } print_field(" DQPSK: %s", str); switch ((pdu->response & 0x30) >> 4) { case 0x00: str = "Not supported"; break; case 0x01: str = "Changed one step"; break; case 0x02: str = "Max power"; break; case 0x03: str = "Min power"; break; default: str = "Reserved"; break; } print_field(" 8DPSK: %s", str); } static void ping_req(const void *data, uint8_t size) { } static void ping_res(const void *data, uint8_t size) { } struct lmp_data { uint16_t opcode; const char *str; void (*func) (const void *data, uint8_t size); uint8_t size; bool fixed; }; static const struct lmp_data lmp_table[] = { { 1, "LMP_name_req", name_req, 1, true }, { 2, "LMP_name_res", name_rsp, 16, true }, { 3, "LMP_accepted", accepted, 1, true }, { 4, "LMP_not_accepted", not_accepted, 2, true }, { 5, "LMP_clkoffset_req", clkoffset_req, 0, true }, { 6, "LMP_clkoffset_res", clkoffset_rsp, 2, true }, { 7, "LMP_detach", detach, 1, true }, { 8, "LMP_in_rand" }, { 9, "LMP_comb_key" }, { 10, "LMP_unit_key" }, { 11, "LMP_au_rand", au_rand, 16, true }, { 12, "LMP_sres", sres, 4, true }, { 13, "LMP_temp_rand" }, { 14, "LMP_temp_key" }, { 15, "LMP_encryption_mode_req", encryption_mode_req, 1, true }, { 16, "LMP_encryption_key_size_req", encryption_key_size_req, 1, true }, { 17, "LMP_start_encryption_req", start_encryption_req, 16, true }, { 18, "LMP_stop_encryption_req", stop_encryption_req, 0, true }, { 19, "LMP_switch_req", switch_req, 4, true }, { 20, "LMP_hold" }, { 21, "LMP_hold_req" }, { 22, "LMP_sniff" }, { 23, "LMP_sniff_req" }, { 24, "LMP_unsniff_req", unsniff_req, 0, true }, { 25, "LMP_park_req" }, { 26, "LMP_park" }, { 27, "LMP_set_broadcast_scan_window" }, { 28, "LMP_modify_beacon" }, { 29, "LMP_unpark_BD_ADDR_req" }, { 30, "LMP_unpark_PM_ADDR_req" }, { 31, "LMP_incr_power_req" }, { 32, "LMP_decr_power_req" }, { 33, "LMP_max_power", max_power, 0, true }, { 34, "LMP_min_power", min_power, 0, true }, { 35, "LMP_auto_rate", auto_rate, 0, true }, { 36, "LMP_preferred_rate", preferred_rate, 1, true }, { 37, "LMP_version_req", version_req, 5, true }, { 38, "LMP_version_res", version_res, 5, true }, { 39, "LMP_features_req", features_req, 8, true }, { 40, "LMP_features_res", features_res, 8, true }, { 41, "LMP_quality_of_service" }, { 42, "LMP_quality_of_service_req" }, { 43, "LMP_SCO_link_req" }, { 44, "LMP_remove_SCO_link_req" }, { 45, "LMP_max_slot", max_slot, 1, true }, { 46, "LMP_max_slot_req", max_slot_req, 1, true }, { 47, "LMP_timing_accuracy_req", timing_accuracy_req, 0, true }, { 48, "LMP_timing_accuracy_res", timing_accuracy_res, 2, true }, { 49, "LMP_setup_complete", setup_complete, 0, true }, { 50, "LMP_use_semi_permanent_key", use_semi_permanent_key, 0, true }, { 51, "LMP_host_connection_req", host_connection_req, 0, true }, { 52, "LMP_slot_offset", slot_offset, 8, true }, { 53, "LMP_page_mode_req" }, { 54, "LMP_page_scan_mode_req", page_scan_mode_req, 2, true }, { 55, "LMP_supervision_timeout" }, { 56, "LMP_test_activate", test_activate, 0, true }, { 57, "LMP_test_control" }, { 58, "LMP_encryption_key_size_mask_req", encryption_key_size_mask_req, 0, true }, { 59, "LMP_encryption_key_size_mask_res" }, { 60, "LMP_set_AFH", set_afh, 15, true }, { 61, "LMP_encapsulated_header", encapsulated_header, 3, true }, { 62, "LMP_encapsulated_payload", encapsulated_payload, 16, true }, { 63, "LMP_simple_pairing_confirm", simple_pairing_confirm, 16, true }, { 64, "LMP_simple_pairing_number", simple_pairing_number, 16, true }, { 65, "LMP_DHkey_check", dhkey_check, 16, true }, { 66, "LMP_pause_encryption_aes_req" }, { LMP_ESC4(1), "LMP_accepted_ext", accepted_ext, 2, true }, { LMP_ESC4(2), "LMP_not_accepted_ext", not_accepted_ext, 3, true }, { LMP_ESC4(3), "LMP_features_req_ext", features_req_ext, 10, true }, { LMP_ESC4(4), "LMP_features_res_ext", features_res_ext, 10, true }, { LMP_ESC4(5), "LMP_clk_adj" }, { LMP_ESC4(6), "LMP_clk_adj_ack" }, { LMP_ESC4(7), "LMP_clk_adj_req" }, { LMP_ESC4(11), "LMP_packet_type_table_req", packet_type_table_req, 1, true }, { LMP_ESC4(12), "LMP_eSCO_link_req" }, { LMP_ESC4(13), "LMP_remove_eSCO_link_req" }, { LMP_ESC4(16), "LMP_channel_classification_req", channel_classification_req, 5, true }, { LMP_ESC4(17), "LMP_channel_classification", channel_classification, 10, true }, { LMP_ESC4(21), "LMP_sniff_subrating_req" }, { LMP_ESC4(22), "LMP_sniff_subrating_res" }, { LMP_ESC4(23), "LMP_pause_encryption_req", pause_encryption_req, 0, true }, { LMP_ESC4(24), "LMP_resume_encryption_req", resume_encryption_req, 0, true }, { LMP_ESC4(25), "LMP_IO_capability_req", io_capability_req, 3, true }, { LMP_ESC4(26), "LMP_IO_capability_res", io_capability_res, 3, true }, { LMP_ESC4(27), "LMP_numeric_comparison_failed", numeric_comparison_failed, 0, true }, { LMP_ESC4(28), "LMP_passkey_failed", passkey_failed, 0, true }, { LMP_ESC4(29), "LMP_oob_failed", oob_failed, 0, true }, { LMP_ESC4(30), "LMP_keypress_notification" }, { LMP_ESC4(31), "LMP_power_control_req", power_control_req, 1, true }, { LMP_ESC4(32), "LMP_power_control_res", power_control_res, 1, true }, { LMP_ESC4(33), "LMP_ping_req", ping_req, 0, true }, { LMP_ESC4(34), "LMP_ping_res", ping_res, 0, true }, { LMP_ESC4(35), "LMP_SAM_set_type0" }, { LMP_ESC4(36), "LMP_SAM_define_map" }, { LMP_ESC4(37), "LMP_SAM_switch" }, { } }; static const char *get_opcode_str(uint16_t opcode) { int i; for (i = 0; lmp_table[i].str; i++) { if (lmp_table[i].opcode == opcode) return lmp_table[i].str; } return NULL; } void lmp_packet(const void *data, uint8_t size, bool padded) { const struct lmp_data *lmp_data = NULL; const char *opcode_color, *opcode_str; uint16_t opcode; uint8_t tid, off; const char *tid_str; int i; tid = ((const uint8_t *) data)[0] & 0x01; opcode = (((const uint8_t *) data)[0] & 0xfe) >> 1; tid_str = tid == 0x00 ? "Central" : "Peripheral"; switch (opcode) { case 127: if (size < 2) { print_text(COLOR_ERROR, "extended opcode too short"); packet_hexdump(data, size); return; } opcode = LMP_ESC4(((const uint8_t *) data)[1]); off = 2; break; case 126: case 125: case 124: return; default: off = 1; break; } for (i = 0; lmp_table[i].str; i++) { if (lmp_table[i].opcode == opcode) { lmp_data = &lmp_table[i]; break; } } if (lmp_data) { if (lmp_data->func) opcode_color = COLOR_OPCODE; else opcode_color = COLOR_OPCODE_UNKNOWN; opcode_str = lmp_data->str; } else { opcode_color = COLOR_OPCODE_UNKNOWN; opcode_str = "Unknown"; } if (opcode & 0xff00) print_indent(6, opcode_color, "", opcode_str, COLOR_OFF, " (%u/%u) %s transaction (%u)", opcode >> 8, opcode & 0xff, tid_str, tid); else print_indent(6, opcode_color, "", opcode_str, COLOR_OFF, " (%u) %s transaction (%d)", opcode, tid_str, tid); if (!lmp_data || !lmp_data->func) { packet_hexdump(data + off, size - off); return; } if (lmp_data->fixed && !padded) { if (size - off != lmp_data->size) { print_text(COLOR_ERROR, "invalid packet size"); packet_hexdump(data + off, size - off); return; } } else { if (size - off < lmp_data->size) { print_text(COLOR_ERROR, "too short packet"); packet_hexdump(data + off, size - off); return; } } lmp_data->func(data + off, size - off); } void lmp_todo(void) { int i; printf("LMP operations with missing decodings:\n"); for (i = 0; lmp_table[i].str; i++) { if (lmp_table[i].func) continue; printf("\t%s\n", lmp_table[i].str); } } bluez-5.82/monitor/PaxHeaders/bnep.h0000644000000000000000000000005014015011623014423 xustar0020 atime=1743516019 20 ctime=1743591282 bluez-5.82/monitor/bnep.h0000644000000000000000000000043114015011623014102 0ustar00rootroot/* SPDX-License-Identifier: LGPL-2.1-or-later */ /* * * BlueZ - Bluetooth protocol stack for Linux * * Copyright (C) 2011-2014 Intel Corporation * Copyright (C) 2002-2010 Marcel Holtmann * * */ void bnep_packet(const struct l2cap_frame *frame); bluez-5.82/monitor/PaxHeaders/a2dp.c0000644000000000000000000000005014572354773014350 xustar0020 atime=1743516042 20 ctime=1743591282 bluez-5.82/monitor/a2dp.c0000644000000000000000000005535214572354773014043 0ustar00rootroot// SPDX-License-Identifier: LGPL-2.1-or-later /* * * BlueZ - Bluetooth protocol stack for Linux * * Copyright (C) 2015 Andrzej Kaczmarek * Copyright (C) 2018 Pali Rohár * * */ #ifdef HAVE_CONFIG_H #include #endif #define _GNU_SOURCE #include #include #include #include "lib/bluetooth.h" #include "src/shared/util.h" #include "bt.h" #include "packet.h" #include "display.h" #include "l2cap.h" #include "a2dp.h" #define BASE_INDENT 4 /* Codec Types */ #define A2DP_CODEC_SBC 0x00 #define A2DP_CODEC_MPEG12 0x01 #define A2DP_CODEC_MPEG24 0x02 #define A2DP_CODEC_ATRAC 0x04 #define A2DP_CODEC_VENDOR 0xff /* Vendor Specific A2DP Codecs */ #define APTX_VENDOR_ID 0x0000004f #define APTX_CODEC_ID 0x0001 #define FASTSTREAM_VENDOR_ID 0x0000000a #define FASTSTREAM_CODEC_ID 0x0001 #define APTX_LL_VENDOR_ID 0x0000000a #define APTX_LL_CODEC_ID 0x0002 #define APTX_HD_VENDOR_ID 0x000000D7 #define APTX_HD_CODEC_ID 0x0024 #define LDAC_VENDOR_ID 0x0000012d #define LDAC_CODEC_ID 0x00aa #define OPUS_G_VENDOR_ID 0x000000e0 #define OPUS_G_CODEC_ID 0x0001 struct bit_desc { uint8_t bit_num; const char *str; }; static const struct bit_desc sbc_frequency_table[] = { { 7, "16000" }, { 6, "32000" }, { 5, "44100" }, { 4, "48000" }, { } }; static const struct bit_desc sbc_channel_mode_table[] = { { 3, "Mono" }, { 2, "Dual Channel" }, { 1, "Stereo" }, { 0, "Joint Stereo" }, { } }; static const struct bit_desc sbc_blocklen_table[] = { { 7, "4" }, { 6, "8" }, { 5, "12" }, { 4, "16" }, { } }; static const struct bit_desc sbc_subbands_table[] = { { 3, "4" }, { 2, "8" }, { } }; static const struct bit_desc sbc_allocation_table[] = { { 1, "SNR" }, { 0, "Loudness" }, { } }; static const struct bit_desc mpeg12_layer_table[] = { { 7, "Layer I (mp1)" }, { 6, "Layer II (mp2)" }, { 5, "Layer III (mp3)" }, { } }; static const struct bit_desc mpeg12_channel_mode_table[] = { { 3, "Mono" }, { 2, "Dual Channel" }, { 1, "Stereo" }, { 0, "Joint Stereo" }, { } }; static const struct bit_desc mpeg12_frequency_table[] = { { 5, "16000" }, { 4, "22050" }, { 3, "24000" }, { 2, "32000" }, { 1, "44100" }, { 0, "48000" }, { } }; static const struct bit_desc mpeg12_bitrate_table[] = { { 14, "1110" }, { 13, "1101" }, { 12, "1100" }, { 11, "1011" }, { 10, "1010" }, { 9, "1001" }, { 8, "1000" }, { 7, "0111" }, { 6, "0110" }, { 5, "0101" }, { 4, "0100" }, { 3, "0011" }, { 2, "0010" }, { 1, "0001" }, { 0, "0000" }, { } }; static const struct bit_desc aac_object_type_table[] = { { 7, "MPEG-2 AAC LC" }, { 6, "MPEG-4 AAC LC" }, { 5, "MPEG-4 AAC LTP" }, { 4, "MPEG-4 AAC scalable" }, { 3, "RFA (b3)" }, { 2, "RFA (b2)" }, { 1, "RFA (b1)" }, { 0, "RFA (b0)" }, { } }; static const struct bit_desc aac_frequency_table[] = { { 15, "8000" }, { 14, "11025" }, { 13, "12000" }, { 12, "16000" }, { 11, "22050" }, { 10, "24000" }, { 9, "32000" }, { 8, "44100" }, { 7, "48000" }, { 6, "64000" }, { 5, "88200" }, { 4, "96000" }, { } }; static const struct bit_desc aac_channels_table[] = { { 3, "1" }, { 2, "2" }, { } }; static const struct bit_desc aptx_frequency_table[] = { { 7, "16000" }, { 6, "32000" }, { 5, "44100" }, { 4, "48000" }, { } }; static const struct bit_desc aptx_channel_mode_table[] = { { 0, "Mono" }, { 1, "Stereo" }, { } }; static const struct bit_desc faststream_direction_table[] = { { 0, "Sink" }, { 1, "Source" }, { } }; static const struct bit_desc faststream_sink_frequency_table[] = { /* in config buffer, there may be more frequency bits * and 48kHz takes precedence over 41kHz */ { 0, "48000" }, { 1, "44100" }, { } }; static const struct bit_desc faststream_source_frequency_table[] = { { 5, "16000" }, { } }; static const struct bit_desc opus_g_frequency_table[] = { { 7, "48000" }, { } }; static const struct bit_desc opus_g_duration_table[] = { { 3, "10 ms" }, { 4, "20 ms" }, { } }; static const struct bit_desc opus_g_channels_table[] = { { 0, "Mono" }, { 1, "Stereo" }, { 2, "Dual Mono" }, { } }; static void print_value_bits(uint8_t indent, uint32_t value, const struct bit_desc *table) { int i; for (i = 0; table[i].str; i++) { if (value & (1 << table[i].bit_num)) print_field("%*c%s", indent + 2, ' ', table[i].str); } } static const char *find_value_bit(uint32_t value, const struct bit_desc *table) { int i; for (i = 0; table[i].str; i++) { if (value & (1 << table[i].bit_num)) return table[i].str; } return "Unknown"; } struct vndcodec { uint32_t vendor_id; uint16_t codec_id; char *codec_name; bool (*codec_vendor_cap)(uint8_t losc, struct l2cap_frame *frame); bool (*codec_vendor_cfg)(uint8_t losc, struct l2cap_frame *frame); }; static bool codec_vendor_aptx_cap(uint8_t losc, struct l2cap_frame *frame); static bool codec_vendor_aptx_cfg(uint8_t losc, struct l2cap_frame *frame); static bool codec_vendor_faststream_cap(uint8_t losc, struct l2cap_frame *frame); static bool codec_vendor_faststream_cfg(uint8_t losc, struct l2cap_frame *frame); static bool codec_vendor_aptx_ll_cap(uint8_t losc, struct l2cap_frame *frame); static bool codec_vendor_aptx_ll_cfg(uint8_t losc, struct l2cap_frame *frame); static bool codec_vendor_aptx_hd_cap(uint8_t losc, struct l2cap_frame *frame); static bool codec_vendor_aptx_hd_cfg(uint8_t losc, struct l2cap_frame *frame); static bool codec_vendor_ldac(uint8_t losc, struct l2cap_frame *frame); static bool codec_vendor_opus_g(uint8_t losc, struct l2cap_frame *frame); static const struct vndcodec vndcodecs[] = { { APTX_VENDOR_ID, APTX_CODEC_ID, "aptX", codec_vendor_aptx_cap, codec_vendor_aptx_cfg }, { FASTSTREAM_VENDOR_ID, FASTSTREAM_CODEC_ID, "FastStream", codec_vendor_faststream_cap, codec_vendor_faststream_cfg }, { APTX_LL_VENDOR_ID, APTX_LL_CODEC_ID, "aptX Low Latency", codec_vendor_aptx_ll_cap, codec_vendor_aptx_ll_cfg }, { APTX_HD_VENDOR_ID, APTX_HD_CODEC_ID, "aptX HD", codec_vendor_aptx_hd_cap, codec_vendor_aptx_hd_cfg }, { LDAC_VENDOR_ID, LDAC_CODEC_ID, "LDAC", codec_vendor_ldac, codec_vendor_ldac }, { OPUS_G_VENDOR_ID, OPUS_G_CODEC_ID, "Opus (Google)", codec_vendor_opus_g, codec_vendor_opus_g }, { } }; static const char *vndcodec2str(uint32_t vendor_id, uint16_t codec_id) { size_t i; for (i = 0; i < sizeof(vndcodecs)/sizeof(*vndcodecs); i++) { if (vndcodecs[i].vendor_id == vendor_id && vndcodecs[i].codec_id == codec_id) return vndcodecs[i].codec_name; } return "Unknown"; } static bool codec_sbc_cap(uint8_t losc, struct l2cap_frame *frame) { uint8_t cap = 0; if (losc != 4) return false; l2cap_frame_get_u8(frame, &cap); print_field("%*cFrequency: 0x%02x", BASE_INDENT, ' ', cap & 0xf0); print_value_bits(BASE_INDENT, cap & 0xf0, sbc_frequency_table); print_field("%*cChannel Mode: 0x%02x", BASE_INDENT, ' ', cap & 0x0f); print_value_bits(BASE_INDENT, cap & 0x0f, sbc_channel_mode_table); l2cap_frame_get_u8(frame, &cap); print_field("%*cBlock Length: 0x%02x", BASE_INDENT, ' ', cap & 0xf0); print_value_bits(BASE_INDENT, cap & 0xf0, sbc_blocklen_table); print_field("%*cSubbands: 0x%02x", BASE_INDENT, ' ', cap & 0x0c); print_value_bits(BASE_INDENT, cap & 0x0c, sbc_subbands_table); print_field("%*cAllocation Method: 0x%02x", BASE_INDENT, ' ', cap & 0x03); print_value_bits(BASE_INDENT, cap & 0x03, sbc_allocation_table); l2cap_frame_get_u8(frame, &cap); print_field("%*cMinimum Bitpool: %d", BASE_INDENT, ' ', cap); l2cap_frame_get_u8(frame, &cap); print_field("%*cMaximum Bitpool: %d", BASE_INDENT, ' ', cap); return true; } static bool codec_sbc_cfg(uint8_t losc, struct l2cap_frame *frame) { uint8_t cap = 0; if (losc != 4) return false; l2cap_frame_get_u8(frame, &cap); print_field("%*cFrequency: %s (0x%02x)", BASE_INDENT, ' ', find_value_bit(cap & 0xf0, sbc_frequency_table), cap & 0xf0); print_field("%*cChannel Mode: %s (0x%02x)", BASE_INDENT, ' ', find_value_bit(cap & 0x0f, sbc_channel_mode_table), cap & 0x0f); l2cap_frame_get_u8(frame, &cap); print_field("%*cBlock Length: %s (0x%02x)", BASE_INDENT, ' ', find_value_bit(cap & 0xf0, sbc_blocklen_table), cap & 0xf0); print_field("%*cSubbands: %s (0x%02x)", BASE_INDENT, ' ', find_value_bit(cap & 0x0c, sbc_subbands_table), cap & 0x0c); print_field("%*cAllocation Method: %s (0x%02x)", BASE_INDENT, ' ', find_value_bit(cap & 0x03, sbc_allocation_table), cap & 0x03); l2cap_frame_get_u8(frame, &cap); print_field("%*cMinimum Bitpool: %d", BASE_INDENT, ' ', cap); l2cap_frame_get_u8(frame, &cap); print_field("%*cMaximum Bitpool: %d", BASE_INDENT, ' ', cap); return true; } static bool codec_mpeg12_cap(uint8_t losc, struct l2cap_frame *frame) { uint16_t cap = 0; uint8_t layer; uint8_t chan; uint8_t freq; uint16_t bitrate; bool crc, mpf, vbr; if (losc != 4) return false; if (!l2cap_frame_get_be16(frame, &cap)) return false; layer = (cap >> 8) & 0xe0; crc = cap & 0x1000; chan = (cap >> 8) & 0x0f; mpf = cap & 0x0040; freq = cap & 0x003f; if (!l2cap_frame_get_be16(frame, &cap)) return false; vbr = cap & 0x8000; bitrate = cap & 0x7fff; print_field("%*cLayer: 0x%02x", BASE_INDENT, ' ', layer); print_value_bits(BASE_INDENT, layer, mpeg12_layer_table); print_field("%*cCRC: %s", BASE_INDENT, ' ', crc ? "Yes" : "No"); print_field("%*cChannel Mode: 0x%02x", BASE_INDENT, ' ', chan); print_value_bits(BASE_INDENT, chan, mpeg12_channel_mode_table); print_field("%*cMedia Payload Format: %s", BASE_INDENT, ' ', mpf ? "RFC-2250 RFC-3119" : "RFC-2250"); print_field("%*cFrequency: 0x%02x", BASE_INDENT, ' ', freq); print_value_bits(BASE_INDENT, freq, mpeg12_frequency_table); if (!vbr) { print_field("%*cBitrate Index: 0x%04x", BASE_INDENT, ' ', bitrate); print_value_bits(BASE_INDENT, freq, mpeg12_bitrate_table); } print_field("%*cVBR: %s", BASE_INDENT, ' ', vbr ? "Yes" : "No"); return true; } static bool codec_mpeg12_cfg(uint8_t losc, struct l2cap_frame *frame) { uint16_t cap = 0; uint8_t layer; uint8_t chan; uint8_t freq; uint16_t bitrate; bool crc, mpf, vbr; if (losc != 4) return false; if (!l2cap_frame_get_be16(frame, &cap)) return false; layer = (cap >> 8) & 0xe0; crc = cap & 0x1000; chan = (cap >> 8) & 0x0f; mpf = cap & 0x0040; freq = cap & 0x003f; if (!l2cap_frame_get_be16(frame, &cap)) return false; vbr = cap & 0x8000; bitrate = cap & 0x7fff; print_field("%*cLayer: %s (0x%02x)", BASE_INDENT, ' ', find_value_bit(layer, mpeg12_layer_table), layer); print_field("%*cCRC: %s", BASE_INDENT, ' ', crc ? "Yes" : "No"); print_field("%*cChannel Mode: %s (0x%02x)", BASE_INDENT, ' ', find_value_bit(chan, mpeg12_channel_mode_table), chan); print_field("%*cMedia Payload Format: %s", BASE_INDENT, ' ', mpf ? "RFC-2250 RFC-3119" : "RFC-2250"); print_field("%*cFrequency: %s (0x%02x)", BASE_INDENT, ' ', find_value_bit(freq, mpeg12_frequency_table), freq); if (!vbr) print_field("%*cBitrate Index: %s (0x%04x)", BASE_INDENT, ' ', find_value_bit(freq, mpeg12_bitrate_table), bitrate); print_field("%*cVBR: %s", BASE_INDENT, ' ', vbr ? "Yes" : "No"); return true; } static bool codec_aac_cap(uint8_t losc, struct l2cap_frame *frame) { uint16_t cap = 0; uint8_t type; uint16_t freq; uint8_t chan; uint32_t bitrate; bool vbr; if (losc != 6) return false; if (!l2cap_frame_get_be16(frame, &cap)) return false; type = cap >> 8; freq = cap << 8; if (!l2cap_frame_get_be16(frame, &cap)) return false; freq |= (cap >> 8) & 0xf0; chan = (cap >> 8) & 0x0c; bitrate = (cap << 16) & 0x7f0000; vbr = cap & 0x0080; if (!l2cap_frame_get_be16(frame, &cap)) return false; bitrate |= cap; print_field("%*cObject Type: 0x%02x", BASE_INDENT, ' ', type); print_value_bits(BASE_INDENT, type, aac_object_type_table); print_field("%*cFrequency: 0x%02x", BASE_INDENT, ' ', freq); print_value_bits(BASE_INDENT, freq, aac_frequency_table); print_field("%*cChannels: 0x%02x", BASE_INDENT, ' ', chan); print_value_bits(BASE_INDENT, chan, aac_channels_table); print_field("%*cBitrate: %ubps", BASE_INDENT, ' ', bitrate); print_field("%*cVBR: %s", BASE_INDENT, ' ', vbr ? "Yes" : "No"); return true; } static bool codec_aac_cfg(uint8_t losc, struct l2cap_frame *frame) { uint16_t cap = 0; uint8_t type; uint16_t freq; uint8_t chan; uint32_t bitrate; bool vbr; if (losc != 6) return false; if (!l2cap_frame_get_be16(frame, &cap)) return false; type = cap >> 8; freq = cap << 8; if (!l2cap_frame_get_be16(frame, &cap)) return false; freq |= (cap >> 8) & 0xf0; chan = (cap >> 8) & 0x0c; bitrate = (cap << 16) & 0x7f0000; vbr = cap & 0x0080; if (!l2cap_frame_get_be16(frame, &cap)) return false; bitrate |= cap; print_field("%*cObject Type: %s (0x%02x)", BASE_INDENT, ' ', find_value_bit(type, aac_object_type_table), type); print_field("%*cFrequency: %s (0x%02x)", BASE_INDENT, ' ', find_value_bit(freq, aac_frequency_table), freq); print_field("%*cChannels: %s (0x%02x)", BASE_INDENT, ' ', find_value_bit(chan, aac_channels_table), chan); print_field("%*cBitrate: %ubps", BASE_INDENT, ' ', bitrate); print_field("%*cVBR: %s", BASE_INDENT, ' ', vbr ? "Yes" : "No"); return true; } static bool codec_vendor_aptx_cap(uint8_t losc, struct l2cap_frame *frame) { uint8_t cap = 0; if (losc != 1) return false; l2cap_frame_get_u8(frame, &cap); print_field("%*cFrequency: 0x%02x", BASE_INDENT + 2, ' ', cap & 0xf0); print_value_bits(BASE_INDENT + 2, cap & 0xf0, aptx_frequency_table); print_field("%*cChannel Mode: 0x%02x", BASE_INDENT + 2, ' ', cap & 0x0f); print_value_bits(BASE_INDENT + 2, cap & 0x0f, aptx_channel_mode_table); return true; } static bool codec_vendor_faststream_cap(uint8_t losc, struct l2cap_frame *frame) { uint8_t cap = 0; if (losc != 2) return false; l2cap_frame_get_u8(frame, &cap); print_field("%*cDirection: 0x%02x", BASE_INDENT + 2, ' ', cap); print_value_bits(BASE_INDENT + 2, cap, faststream_direction_table); l2cap_frame_get_u8(frame, &cap); print_field("%*cSink Frequency: 0x%02x", BASE_INDENT + 2, ' ', cap & 0x0f); print_value_bits(BASE_INDENT + 2, cap & 0x0f, faststream_sink_frequency_table); print_field("%*cSource Frequency: 0x%02x", BASE_INDENT + 2, ' ', cap & 0xf0); print_value_bits(BASE_INDENT + 2, cap & 0xf0, faststream_source_frequency_table); return true; } static bool codec_vendor_aptx_ll_cap(uint8_t losc, struct l2cap_frame *frame) { uint8_t cap = 0; uint16_t level = 0; if (losc != 2 && losc != 11) return false; l2cap_frame_get_u8(frame, &cap); print_field("%*cFrequency: 0x%02x", BASE_INDENT + 2, ' ', cap & 0xf0); print_value_bits(BASE_INDENT + 2, cap & 0xf0, aptx_frequency_table); print_field("%*cChannel Mode: 0x%02x", BASE_INDENT + 2, ' ', cap & 0x0f); print_value_bits(BASE_INDENT + 2, cap & 0x0f, aptx_channel_mode_table); l2cap_frame_get_u8(frame, &cap); print_field("%*cBidirectional link: %s", BASE_INDENT, ' ', (cap & 1) ? "Yes" : "No"); if ((cap & 2) && losc == 11) { /* reserved */ l2cap_frame_get_u8(frame, &cap); l2cap_frame_get_le16(frame, &level); print_field("%*cTarget codec buffer level: %u (0x%02x)", BASE_INDENT + 2, ' ', level, level); l2cap_frame_get_le16(frame, &level); print_field("%*cInitial codec buffer level: %u (0x%02x)", BASE_INDENT + 2, ' ', level, level); l2cap_frame_get_u8(frame, &cap); print_field("%*cSRA max rate: %g (0x%02x)", BASE_INDENT + 2, ' ', cap / 10000.0, cap); l2cap_frame_get_u8(frame, &cap); print_field("%*cSRA averaging time: %us (0x%02x)", BASE_INDENT + 2, ' ', cap, cap); l2cap_frame_get_le16(frame, &level); print_field("%*cGood working codec buffer level: %u (0x%02x)", BASE_INDENT + 2, ' ', level, level); } return true; } static bool codec_vendor_aptx_hd_cap(uint8_t losc, struct l2cap_frame *frame) { uint8_t cap = 0; if (losc != 5) return false; l2cap_frame_get_u8(frame, &cap); print_field("%*cFrequency: 0x%02x", BASE_INDENT + 2, ' ', cap & 0xf0); print_value_bits(BASE_INDENT + 2, cap & 0xf0, aptx_frequency_table); print_field("%*cChannel Mode: 0x%02x", BASE_INDENT + 2, ' ', cap & 0x0f); print_value_bits(BASE_INDENT + 2, cap & 0x0f, aptx_channel_mode_table); /* reserved */ l2cap_frame_get_u8(frame, &cap); l2cap_frame_get_u8(frame, &cap); l2cap_frame_get_u8(frame, &cap); l2cap_frame_get_u8(frame, &cap); return true; } static bool codec_vendor_ldac(uint8_t losc, struct l2cap_frame *frame) { uint16_t cap = 0; if (losc != 2) return false; l2cap_frame_get_le16(frame, &cap); print_field("%*cUnknown: 0x%04x", BASE_INDENT + 2, ' ', cap); return true; } static bool codec_vendor_opus_g(uint8_t losc, struct l2cap_frame *frame) { uint8_t cap = 0; if (losc != 1) return false; l2cap_frame_get_u8(frame, &cap); print_field("%*cFrequency: 0x%02x", BASE_INDENT + 2, ' ', cap & 0x80); print_value_bits(BASE_INDENT + 2, cap, opus_g_frequency_table); print_field("%*cFrame Duration: 0x%02x", BASE_INDENT + 2, ' ', cap & 0x18); print_value_bits(BASE_INDENT + 2, cap, opus_g_duration_table); print_field("%*cChannel Mode: 0x%02x", BASE_INDENT + 2, ' ', cap & 0x07); print_value_bits(BASE_INDENT + 2, cap, opus_g_channels_table); print_field("%*cReserved: 0x%02x", BASE_INDENT + 2, ' ', cap & 0x60); return true; } static bool codec_vendor_cap(uint8_t losc, struct l2cap_frame *frame) { uint32_t vendor_id = 0; uint16_t codec_id = 0; size_t i; if (losc < 6) return false; l2cap_frame_get_le32(frame, &vendor_id); l2cap_frame_get_le16(frame, &codec_id); losc -= 6; print_field("%*cVendor ID: %s (0x%08x)", BASE_INDENT, ' ', bt_compidtostr(vendor_id), vendor_id); print_field("%*cVendor Specific Codec ID: %s (0x%04x)", BASE_INDENT, ' ', vndcodec2str(vendor_id, codec_id), codec_id); for (i = 0; i < sizeof(vndcodecs)/sizeof(*vndcodecs); i++) { if (vndcodecs[i].vendor_id == vendor_id && vndcodecs[i].codec_id == codec_id) return vndcodecs[i].codec_vendor_cap(losc, frame); } packet_hexdump(frame->data, losc); l2cap_frame_pull(frame, frame, losc); return true; } static bool codec_vendor_aptx_cfg(uint8_t losc, struct l2cap_frame *frame) { uint8_t cap = 0; if (losc != 1) return false; l2cap_frame_get_u8(frame, &cap); print_field("%*cFrequency: %s (0x%02x)", BASE_INDENT + 2, ' ', find_value_bit(cap & 0xf0, aptx_frequency_table), cap & 0xf0); print_field("%*cChannel Mode: %s (0x%02x)", BASE_INDENT + 2, ' ', find_value_bit(cap & 0x0f, aptx_channel_mode_table), cap & 0x0f); return true; } static bool codec_vendor_faststream_cfg(uint8_t losc, struct l2cap_frame *frame) { uint8_t cap = 0; if (losc != 2) return false; l2cap_frame_get_u8(frame, &cap); /* FastStream codec is bi-directional */ print_field("%*cDirection: 0x%02x", BASE_INDENT + 2, ' ', cap); print_value_bits(BASE_INDENT + 2, cap, faststream_direction_table); l2cap_frame_get_u8(frame, &cap); print_field("%*cSink Frequency: %s (0x%02x)", BASE_INDENT + 2, ' ', find_value_bit(cap & 0x0f, faststream_sink_frequency_table), cap & 0x0f); print_field("%*cSource Frequency: %s (0x%02x)", BASE_INDENT + 2, ' ', find_value_bit(cap & 0xf0, faststream_source_frequency_table), cap & 0xf0); return true; } static bool codec_vendor_aptx_ll_cfg(uint8_t losc, struct l2cap_frame *frame) { uint8_t cap = 0; uint16_t level = 0; if (losc != 2 && losc != 11) return false; l2cap_frame_get_u8(frame, &cap); print_field("%*cFrequency: %s (0x%02x)", BASE_INDENT + 2, ' ', find_value_bit(cap & 0xf0, aptx_frequency_table), cap & 0xf0); print_field("%*cChannel Mode: %s (0x%02x)", BASE_INDENT + 2, ' ', find_value_bit(cap & 0x0f, aptx_channel_mode_table), cap & 0x0f); l2cap_frame_get_u8(frame, &cap); print_field("%*cBidirectional link: %s", BASE_INDENT, ' ', (cap & 1) ? "Yes" : "No"); if ((cap & 2) && losc == 11) { /* reserved */ l2cap_frame_get_u8(frame, &cap); l2cap_frame_get_le16(frame, &level); print_field("%*cTarget codec buffer level: %u (0x%02x)", BASE_INDENT + 2, ' ', level, level); l2cap_frame_get_le16(frame, &level); print_field("%*cInitial codec buffer level: %u (0x%02x)", BASE_INDENT + 2, ' ', level, level); l2cap_frame_get_u8(frame, &cap); print_field("%*cSRA max rate: %g (0x%02x)", BASE_INDENT + 2, ' ', cap / 10000.0, cap); l2cap_frame_get_u8(frame, &cap); print_field("%*cSRA averaging time: %us (0x%02x)", BASE_INDENT + 2, ' ', cap, cap); l2cap_frame_get_le16(frame, &level); print_field("%*cGood working codec buffer level: %u (0x%02x)", BASE_INDENT + 2, ' ', level, level); } return true; } static bool codec_vendor_aptx_hd_cfg(uint8_t losc, struct l2cap_frame *frame) { uint8_t cap = 0; if (losc != 5) return false; l2cap_frame_get_u8(frame, &cap); print_field("%*cFrequency: %s (0x%02x)", BASE_INDENT + 2, ' ', find_value_bit(cap & 0xf0, aptx_frequency_table), cap & 0xf0); print_field("%*cChannel Mode: %s (0x%02x)", BASE_INDENT + 2, ' ', find_value_bit(cap & 0x0f, aptx_channel_mode_table), cap & 0x0f); /* reserved */ l2cap_frame_get_u8(frame, &cap); l2cap_frame_get_u8(frame, &cap); l2cap_frame_get_u8(frame, &cap); l2cap_frame_get_u8(frame, &cap); return true; } static bool codec_vendor_cfg(uint8_t losc, struct l2cap_frame *frame) { uint32_t vendor_id = 0; uint16_t codec_id = 0; size_t i; if (losc < 6) return false; l2cap_frame_get_le32(frame, &vendor_id); l2cap_frame_get_le16(frame, &codec_id); losc -= 6; print_field("%*cVendor ID: %s (0x%08x)", BASE_INDENT, ' ', bt_compidtostr(vendor_id), vendor_id); print_field("%*cVendor Specific Codec ID: %s (0x%04x)", BASE_INDENT, ' ', vndcodec2str(vendor_id, codec_id), codec_id); for (i = 0; i < sizeof(vndcodecs)/sizeof(*vndcodecs); i++) { if (vndcodecs[i].vendor_id == vendor_id && vndcodecs[i].codec_id == codec_id) return vndcodecs[i].codec_vendor_cfg(losc, frame); } packet_hexdump(frame->data, losc); l2cap_frame_pull(frame, frame, losc); return true; } bool a2dp_codec_cap(uint8_t codec, uint8_t losc, struct l2cap_frame *frame) { switch (codec) { case A2DP_CODEC_SBC: return codec_sbc_cap(losc, frame); case A2DP_CODEC_MPEG12: return codec_mpeg12_cap(losc, frame); case A2DP_CODEC_MPEG24: return codec_aac_cap(losc, frame); case A2DP_CODEC_VENDOR: return codec_vendor_cap(losc, frame); default: packet_hexdump(frame->data, losc); l2cap_frame_pull(frame, frame, losc); return true; } } bool a2dp_codec_cfg(uint8_t codec, uint8_t losc, struct l2cap_frame *frame) { switch (codec) { case A2DP_CODEC_SBC: return codec_sbc_cfg(losc, frame); case A2DP_CODEC_MPEG12: return codec_mpeg12_cfg(losc, frame); case A2DP_CODEC_MPEG24: return codec_aac_cfg(losc, frame); case A2DP_CODEC_VENDOR: return codec_vendor_cfg(losc, frame); default: packet_hexdump(frame->data, losc); l2cap_frame_pull(frame, frame, losc); return true; } } bluez-5.82/monitor/PaxHeaders/intel.h0000644000000000000000000000005014110351364014617 xustar0020 atime=1743515957 20 ctime=1743591282 bluez-5.82/monitor/intel.h0000644000000000000000000000065414110351364014305 0ustar00rootroot/* SPDX-License-Identifier: LGPL-2.1-or-later */ /* * * BlueZ - Bluetooth protocol stack for Linux * * Copyright (C) 2011-2014 Intel Corporation * Copyright (C) 2002-2010 Marcel Holtmann * * */ #include struct vendor_ocf; struct vendor_evt; const struct vendor_ocf *intel_vendor_ocf(uint16_t ocf); const struct vendor_evt *intel_vendor_evt(const void *data, int *consumed_size); bluez-5.82/monitor/PaxHeaders/rfcomm.c0000644000000000000000000000005014572354773015005 xustar0020 atime=1743516047 20 ctime=1743591282 bluez-5.82/monitor/rfcomm.c0000644000000000000000000002717014572354773014475 0ustar00rootroot// SPDX-License-Identifier: LGPL-2.1-or-later /* * * BlueZ - Bluetooth protocol stack for Linux * * Copyright (C) 2011-2014 Intel Corporation * Copyright (C) 2002-2010 Marcel Holtmann * * */ #ifdef HAVE_CONFIG_H #include #endif #define _GNU_SOURCE #include #include #include #include #include #include "lib/bluetooth.h" #include "lib/uuid.h" #include "src/shared/util.h" #include "bt.h" #include "packet.h" #include "display.h" #include "l2cap.h" #include "keys.h" #include "sdp.h" #include "rfcomm.h" static const char *cr_str[] = { "RSP", "CMD" }; /* RFCOMM frame parsing macros */ #define CR_STR(type) cr_str[GET_CR(type)] #define GET_LEN8(length) ((length & 0xfe) >> 1) #define GET_LEN16(length) ((length & 0xfffe) >> 1) #define GET_CR(type) ((type & 0x02) >> 1) #define GET_PF(ctr) (((ctr) >> 4) & 0x1) /* MSC macros */ #define GET_V24_FC(sigs) ((sigs & 0x02) >> 1) #define GET_V24_RTC(sigs) ((sigs & 0x04) >> 2) #define GET_V24_RTR(sigs) ((sigs & 0x08) >> 3) #define GET_V24_IC(sigs) ((sigs & 0x40) >> 6) #define GET_V24_DV(sigs) ((sigs & 0x80) >> 7) /* RPN macros */ #define GET_RPN_DB(parity) (parity & 0x03) #define GET_RPN_SB(parity) ((parity & 0x04) >> 2) #define GET_RPN_PARITY(parity) ((parity & 0x08) >> 3) #define GET_RPN_PTYPE(parity) ((parity & 0x30) >> 4) #define GET_RPN_XIN(io) (io & 0x01) #define GET_RPN_XOUT(io) ((io & 0x02) >> 1) #define GET_RPN_RTRI(io) ((io & 0x04) >> 2) #define GET_RPN_RTRO(io) ((io & 0x08) >> 3) #define GET_RPN_RTCI(io) ((io & 0x10) >> 4) #define GET_RPN_RTCO(io) ((io & 0x20) >> 5) /* RLS macro */ #define GET_ERROR(err) (err & 0x0f) /* PN macros */ #define GET_FRM_TYPE(ctrl) ((ctrl & 0x0f)) #define GET_CRT_FLOW(ctrl) ((ctrl & 0xf0) >> 4) #define GET_PRIORITY(prio) ((prio & 0x3f)) #define GET_PN_DLCI(dlci) ((dlci & 0x3f)) struct rfcomm_lhdr { uint8_t address; uint8_t control; uint16_t length; uint8_t fcs; uint8_t credits; /* only for UIH frame */ }; struct rfcomm_lmsc { uint8_t dlci; uint8_t v24_sig; uint8_t break_sig; }; struct rfcomm_rpn { uint8_t dlci; uint8_t bit_rate; uint8_t parity; uint8_t io; uint8_t xon; uint8_t xoff; uint16_t pm; }; struct rfcomm_rls { uint8_t dlci; uint8_t error; }; struct rfcomm_nsc { uint8_t cmd_type; }; struct rfcomm_lmcc { uint8_t type; uint16_t length; }; struct rfcomm_frame { struct rfcomm_lhdr hdr; struct rfcomm_lmcc mcc; struct l2cap_frame l2cap_frame; }; static void print_rfcomm_hdr(struct rfcomm_frame *rfcomm_frame, uint8_t indent) { struct rfcomm_lhdr hdr = rfcomm_frame->hdr; /* Address field */ print_field("%*cAddress: 0x%2.2x cr %d dlci 0x%2.2x", indent, ' ', hdr.address, GET_CR(hdr.address), RFCOMM_GET_DLCI(hdr.address)); /* Control field */ print_field("%*cControl: 0x%2.2x poll/final %d", indent, ' ', hdr.control, GET_PF(hdr.control)); /* Length and FCS */ print_field("%*cLength: %d", indent, ' ', hdr.length); print_field("%*cFCS: 0x%2.2x", indent, ' ', hdr.fcs); } static inline bool mcc_test(struct rfcomm_frame *rfcomm_frame, uint8_t indent) { struct l2cap_frame *frame = &rfcomm_frame->l2cap_frame; uint8_t data; printf("%*cTest Data: 0x ", indent, ' '); while (frame->size > 1) { if (!l2cap_frame_get_u8(frame, &data)) return false; printf("%2.2x ", data); } printf("\n"); return true; } static inline bool mcc_msc(struct rfcomm_frame *rfcomm_frame, uint8_t indent) { struct l2cap_frame *frame = &rfcomm_frame->l2cap_frame; struct rfcomm_lmsc msc; if (!l2cap_frame_get_u8(frame, &msc.dlci)) return false; print_field("%*cdlci %d ", indent, ' ', RFCOMM_GET_DLCI(msc.dlci)); if (!l2cap_frame_get_u8(frame, &msc.v24_sig)) return false; /* v24 control signals */ print_field("%*cfc %d rtc %d rtr %d ic %d dv %d", indent, ' ', GET_V24_FC(msc.v24_sig), GET_V24_RTC(msc.v24_sig), GET_V24_RTR(msc.v24_sig), GET_V24_IC(msc.v24_sig), GET_V24_DV(msc.v24_sig)); if (frame->size < 2) goto done; /* * TODO: Implement the break signals decoding. */ packet_hexdump(frame->data, frame->size); done: return true; } static inline bool mcc_rpn(struct rfcomm_frame *rfcomm_frame, uint8_t indent) { struct l2cap_frame *frame = &rfcomm_frame->l2cap_frame; struct rfcomm_rpn rpn; if (!l2cap_frame_get_u8(frame, &rpn.dlci)) return false; print_field("%*cdlci %d", indent, ' ', RFCOMM_GET_DLCI(rpn.dlci)); if (frame->size < 7) goto done; /* port value octets (optional) */ if (!l2cap_frame_get_u8(frame, &rpn.bit_rate)) return false; if (!l2cap_frame_get_u8(frame, &rpn.parity)) return false; if (!l2cap_frame_get_u8(frame, &rpn.io)) return false; print_field("%*cbr %d db %d sb %d p %d pt %d xi %d xo %d", indent, ' ', rpn.bit_rate, GET_RPN_DB(rpn.parity), GET_RPN_SB(rpn.parity), GET_RPN_PARITY(rpn.parity), GET_RPN_PTYPE(rpn.parity), GET_RPN_XIN(rpn.io), GET_RPN_XOUT(rpn.io)); if (!l2cap_frame_get_u8(frame, &rpn.xon)) return false; if (!l2cap_frame_get_u8(frame, &rpn.xoff)) return false; print_field("%*crtri %d rtro %d rtci %d rtco %d xon %d xoff %d", indent, ' ', GET_RPN_RTRI(rpn.io), GET_RPN_RTRO(rpn.io), GET_RPN_RTCI(rpn.io), GET_RPN_RTCO(rpn.io), rpn.xon, rpn.xoff); if (!l2cap_frame_get_le16(frame, &rpn.pm)) return false; print_field("%*cpm 0x%04x", indent, ' ', rpn.pm); done: return true; } static inline bool mcc_rls(struct rfcomm_frame *rfcomm_frame, uint8_t indent) { struct l2cap_frame *frame = &rfcomm_frame->l2cap_frame; struct rfcomm_rls rls; if (!l2cap_frame_get_u8(frame, &rls.dlci)) return false; if (!l2cap_frame_get_u8(frame, &rls.error)) return false; print_field("%*cdlci %d error: %d", indent, ' ', RFCOMM_GET_DLCI(rls.dlci), GET_ERROR(rls.error)); return true; } static inline bool mcc_pn(struct rfcomm_frame *rfcomm_frame, uint8_t indent) { struct l2cap_frame *frame = &rfcomm_frame->l2cap_frame; struct rfcomm_pn pn; uint16_t mtu; /* rfcomm_pn struct is defined in rfcomm.h */ if (!l2cap_frame_get_u8(frame, &pn.dlci)) return false; if (!l2cap_frame_get_u8(frame, &pn.flow_ctrl)) return false; if (!l2cap_frame_get_u8(frame, &pn.priority)) return false; print_field("%*cdlci %d frame_type %d credit_flow %d pri %d", indent, ' ', GET_PN_DLCI(pn.dlci), GET_FRM_TYPE(pn.flow_ctrl), GET_CRT_FLOW(pn.flow_ctrl), GET_PRIORITY(pn.priority)); if (!l2cap_frame_get_u8(frame, &pn.ack_timer)) return false; if (!l2cap_frame_get_le16(frame, &mtu)) return false; pn.mtu = mtu; if (!l2cap_frame_get_u8(frame, &pn.max_retrans)) return false; if (!l2cap_frame_get_u8(frame, &pn.credits)) return false; print_field("%*cack_timer %d frame_size %d max_retrans %d credits %d", indent, ' ', pn.ack_timer, pn.mtu, pn.max_retrans, pn.credits); return true; } static inline bool mcc_nsc(struct rfcomm_frame *rfcomm_frame, uint8_t indent) { struct l2cap_frame *frame = &rfcomm_frame->l2cap_frame; struct rfcomm_nsc nsc; if (!l2cap_frame_get_u8(frame, &nsc.cmd_type)) return false; print_field("%*ccr %d, mcc_cmd_type %x", indent, ' ', GET_CR(nsc.cmd_type), RFCOMM_GET_MCC_TYPE(nsc.cmd_type)); return true; } struct mcc_data { uint8_t type; const char *str; }; static const struct mcc_data mcc_table[] = { { 0x08, "Test Command" }, { 0x28, "Flow Control On Command" }, { 0x18, "Flow Control Off Command" }, { 0x38, "Modem Status Command" }, { 0x24, "Remote Port Negotiation Command" }, { 0x14, "Remote Line Status" }, { 0x20, "DLC Parameter Negotiation" }, { 0x04, "Non Supported Command" }, { } }; static inline bool mcc_frame(struct rfcomm_frame *rfcomm_frame, uint8_t indent) { uint8_t length, ex_length, type; const char *type_str; int i; struct l2cap_frame *frame = &rfcomm_frame->l2cap_frame; struct rfcomm_lmcc mcc; const struct mcc_data *mcc_data = NULL; if (!l2cap_frame_get_u8(frame, &mcc.type) || !l2cap_frame_get_u8(frame, &length)) return false; if (RFCOMM_TEST_EA(length)) mcc.length = (uint16_t) GET_LEN8(length); else { if (!l2cap_frame_get_u8(frame, &ex_length)) return false; mcc.length = ((uint16_t) length << 8) | ex_length; mcc.length = GET_LEN16(mcc.length); } type = RFCOMM_GET_MCC_TYPE(mcc.type); for (i = 0; mcc_table[i].str; i++) { if (mcc_table[i].type == type) { mcc_data = &mcc_table[i]; break; } } if (mcc_data) type_str = mcc_data->str; else type_str = "Unknown"; print_field("%*cMCC Message type: %s %s (0x%2.2x)", indent, ' ', type_str, CR_STR(mcc.type), type); print_field("%*cLength: %d", indent+2, ' ', mcc.length); rfcomm_frame->mcc = mcc; switch (type) { case RFCOMM_TEST: return mcc_test(rfcomm_frame, indent+10); case RFCOMM_MSC: return mcc_msc(rfcomm_frame, indent+2); case RFCOMM_RPN: return mcc_rpn(rfcomm_frame, indent+2); case RFCOMM_RLS: return mcc_rls(rfcomm_frame, indent+2); case RFCOMM_PN: return mcc_pn(rfcomm_frame, indent+2); case RFCOMM_NSC: return mcc_nsc(rfcomm_frame, indent+2); default: packet_hexdump(frame->data, frame->size); } return true; } static bool uih_frame(struct rfcomm_frame *rfcomm_frame, uint8_t indent) { uint8_t credits; struct l2cap_frame *frame = &rfcomm_frame->l2cap_frame; struct rfcomm_lhdr *hdr = &rfcomm_frame->hdr; if (!RFCOMM_GET_CHANNEL(hdr->address)) return mcc_frame(rfcomm_frame, indent); /* fetching credits from UIH frame */ if (GET_PF(hdr->control)) { if (!l2cap_frame_get_u8(frame, &credits)) return false; hdr->credits = credits; print_field("%*cCredits: %d", indent, ' ', hdr->credits); } packet_hexdump(frame->data, frame->size); return true; } struct rfcomm_data { uint8_t frame; const char *str; }; static const struct rfcomm_data rfcomm_table[] = { { 0x2f, "Set Async Balance Mode (SABM)" }, { 0x63, "Unnumbered Ack (UA)" }, { 0x0f, "Disconnect Mode (DM)" }, { 0x43, "Disconnect (DISC)" }, { 0xef, "Unnumbered Info with Header Check (UIH)" }, { } }; void rfcomm_packet(const struct l2cap_frame *frame) { uint8_t ctype, length, ex_length, indent = 1; const char *frame_str, *frame_color; struct l2cap_frame *l2cap_frame, tmp_frame; struct rfcomm_frame rfcomm_frame; struct rfcomm_lhdr hdr; const struct rfcomm_data *rfcomm_data = NULL; int i; l2cap_frame_pull(&rfcomm_frame.l2cap_frame, frame, 0); l2cap_frame = &rfcomm_frame.l2cap_frame; if (frame->size < 4) goto fail; memset(&hdr, 0, sizeof(hdr)); if (!l2cap_frame_get_u8(l2cap_frame, &hdr.address) || !l2cap_frame_get_u8(l2cap_frame, &hdr.control) || !l2cap_frame_get_u8(l2cap_frame, &length)) goto fail; /* length maybe 1 or 2 octets */ if (RFCOMM_TEST_EA(length)) hdr.length = (uint16_t) GET_LEN8(length); else { if (!l2cap_frame_get_u8(l2cap_frame, &ex_length)) goto fail; hdr.length = ((uint16_t)ex_length << 8) | length; hdr.length = GET_LEN16(hdr.length); } if (!l2cap_frame->size) goto fail; l2cap_frame_pull(&tmp_frame, l2cap_frame, l2cap_frame->size-1); if (!l2cap_frame_get_u8(&tmp_frame, &hdr.fcs)) goto fail; /* Decoding frame type */ ctype = RFCOMM_GET_TYPE(hdr.control); for (i = 0; rfcomm_table[i].str; i++) { if (rfcomm_table[i].frame == ctype) { rfcomm_data = &rfcomm_table[i]; break; } } if (rfcomm_data) { if (frame->in) frame_color = COLOR_MAGENTA; else frame_color = COLOR_BLUE; frame_str = rfcomm_data->str; } else { frame_color = COLOR_WHITE_BG; frame_str = "Unknown"; } if (!rfcomm_data) { packet_hexdump(frame->data, frame->size); return; } print_indent(6, frame_color, "RFCOMM: ", frame_str, COLOR_OFF, " (0x%2.2x)", ctype); rfcomm_frame.hdr = hdr; print_rfcomm_hdr(&rfcomm_frame, indent); /* UIH frame */ if (ctype == 0xef) if (!uih_frame(&rfcomm_frame, indent)) goto fail; return; fail: print_text(COLOR_ERROR, "Frame too short"); packet_hexdump(frame->data, frame->size); return; } bluez-5.82/monitor/PaxHeaders/ellisys.c0000644000000000000000000000005014333256742015176 xustar0020 atime=1743515954 20 ctime=1743591282 bluez-5.82/monitor/ellisys.c0000644000000000000000000000623514333256742014665 0ustar00rootroot// SPDX-License-Identifier: LGPL-2.1-or-later /* * * BlueZ - Bluetooth protocol stack for Linux * * Copyright (C) 2011-2014 Intel Corporation * Copyright (C) 2002-2010 Marcel Holtmann * * */ #ifdef HAVE_CONFIG_H #include #endif #define _GNU_SOURCE #include #include #include #include #include #include #include #include #include #include "src/shared/btsnoop.h" #include "ellisys.h" static int ellisys_fd = -1; static uint16_t ellisys_index = 0xffff; void ellisys_enable(const char *server, uint16_t port) { struct sockaddr_in addr; int fd; if (ellisys_fd >= 0) { fprintf(stderr, "Ellisys injection already enabled\n"); return; } fd = socket(PF_INET, SOCK_DGRAM | SOCK_CLOEXEC, 0); if (fd < 0) { perror("Failed to open UDP injection socket"); return; } memset(&addr, 0, sizeof(addr)); addr.sin_family = AF_INET; addr.sin_addr.s_addr = inet_addr(server); addr.sin_port = htons(port); if (connect(fd, (struct sockaddr *) &addr, sizeof(addr)) < 0) { perror("Failed to connect UDP injection socket"); close(fd); return; } ellisys_fd = fd; } void ellisys_inject_hci(struct timeval *tv, uint16_t index, uint16_t opcode, const void *data, uint16_t size) { uint8_t msg[] = { /* HCI Injection Service, Version 1 */ 0x02, 0x00, 0x01, /* DateTimeNs Object */ 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* Bitrate Object, 12000000 bps */ 0x80, 0x00, 0x1b, 0x37, 0x4b, /* HCI Packet Type Object */ 0x81, 0x00, /* HCI Packet Data Object */ 0x82 }; long nsec; time_t t; struct tm tm; struct iovec iov[2]; int iovcnt; if (!tv) return; if (ellisys_fd < 0) return; if (ellisys_index == 0xffff) ellisys_index = index; if (index != ellisys_index) return; t = tv->tv_sec; localtime_r(&t, &tm); nsec = ((tm.tm_sec + (tm.tm_min * 60) + (tm.tm_hour * 3600)) * 1000000l + tv->tv_usec) * 1000l; msg[4] = (1900 + tm.tm_year) & 0xff; msg[5] = (1900 + tm.tm_year) >> 8; msg[6] = (tm.tm_mon + 1) & 0xff; msg[7] = tm.tm_mday & 0xff; msg[8] = (nsec & 0x0000000000ffl); msg[9] = (nsec & 0x00000000ff00l) >> 8; msg[10] = (nsec & 0x000000ff0000l) >> 16; msg[11] = (nsec & 0x0000ff000000l) >> 24; msg[12] = (nsec & 0x00ff00000000l) >> 32; msg[13] = (nsec & 0xff0000000000l) >> 40; switch (opcode) { case BTSNOOP_OPCODE_COMMAND_PKT: msg[20] = 0x01; break; case BTSNOOP_OPCODE_EVENT_PKT: msg[20] = 0x84; break; case BTSNOOP_OPCODE_ACL_TX_PKT: msg[20] = 0x02; break; case BTSNOOP_OPCODE_ACL_RX_PKT: msg[20] = 0x82; break; case BTSNOOP_OPCODE_SCO_TX_PKT: msg[20] = 0x03; break; case BTSNOOP_OPCODE_SCO_RX_PKT: msg[20] = 0x83; break; case BTSNOOP_OPCODE_ISO_TX_PKT: msg[20] = 0x05; break; case BTSNOOP_OPCODE_ISO_RX_PKT: msg[20] = 0x85; break; default: return; } iov[0].iov_base = msg; iov[0].iov_len = sizeof(msg); if (size > 0) { iov[1].iov_base = (void *) data; iov[1].iov_len = size; iovcnt = 2; } else iovcnt = 1; if (writev(ellisys_fd, iov, iovcnt) < 0) perror("Failed to send Ellisys injection packet"); } bluez-5.82/monitor/PaxHeaders/ll.c0000644000000000000000000000005014471706457014127 xustar0020 atime=1743516014 20 ctime=1743591282 bluez-5.82/monitor/ll.c0000644000000000000000000005662114471706457013622 0ustar00rootroot// SPDX-License-Identifier: LGPL-2.1-or-later /* * * BlueZ - Bluetooth protocol stack for Linux * * Copyright (C) 2011-2014 Intel Corporation * Copyright (C) 2002-2010 Marcel Holtmann * * */ #ifdef HAVE_CONFIG_H #include #endif #define _GNU_SOURCE #include #include #include "src/shared/util.h" #include "display.h" #include "packet.h" #include "crc.h" #include "bt.h" #include "ll.h" #define COLOR_OPCODE COLOR_MAGENTA #define COLOR_OPCODE_UNKNOWN COLOR_WHITE_BG #define COLOR_UNKNOWN_OPTIONS_BIT COLOR_WHITE_BG #define MAX_CHANNEL 16 struct channel_data { uint32_t access_addr; uint32_t crc_init; }; static struct channel_data channel_list[MAX_CHANNEL]; static void set_crc_init(uint32_t access_addr, uint32_t crc_init) { int i; for (i = 0; i < MAX_CHANNEL; i++) { if (channel_list[i].access_addr == 0x00000000 || channel_list[i].access_addr == access_addr) { channel_list[i].access_addr = access_addr; channel_list[i].crc_init = crc_init; break; } } } static uint32_t get_crc_init(uint32_t access_addr) { int i; for (i = 0; i < MAX_CHANNEL; i++) { if (channel_list[i].access_addr == access_addr) return channel_list[i].crc_init; } return 0x00000000; } static void advertising_packet(const void *data, uint8_t size) { const uint8_t *ptr = data; uint8_t pdu_type, length, win_size, hop, sca; bool tx_add, rx_add; uint32_t access_addr, crc_init; uint16_t win_offset, interval, latency, timeout; const char *str; if (size < 2) { print_text(COLOR_ERROR, "packet too short"); packet_hexdump(data, size); return; } pdu_type = ptr[0] & 0x0f; tx_add = !!(ptr[0] & 0x40); rx_add = !!(ptr[0] & 0x80); length = ptr[1] & 0x3f; switch (pdu_type) { case 0x00: str = "ADV_IND"; break; case 0x01: str = "ADV_DIRECT_IND"; break; case 0x02: str = "ADV_NONCONN_IND"; break; case 0x03: str = "SCAN_REQ"; break; case 0x04: str = "SCAN_RSP"; break; case 0x05: str = "CONNECT_REQ"; break; case 0x06: str = "ADV_SCAN_IND"; break; default: str = "Reserved"; break; } print_field("Type: %s (0x%2.2x)", str, pdu_type); print_field("TxAdd: %u", tx_add); print_field("RxAdd: %u", rx_add); print_field("Length: %u", length); if (length != size - 2) { print_text(COLOR_ERROR, "packet size mismatch"); packet_hexdump(data + 2, size - 2); return; } switch (pdu_type) { case 0x00: /* ADV_IND */ case 0x02: /* AVD_NONCONN_IND */ case 0x06: /* ADV_SCAN_IND */ case 0x04: /* SCAN_RSP */ if (length < 6) { print_text(COLOR_ERROR, "payload too short"); packet_hexdump(data + 2, length); return; } packet_print_addr("Advertiser address", data + 2, tx_add); packet_print_ad(data + 8, length - 6); break; case 0x01: /* ADV_DIRECT_IND */ if (length < 12) { print_text(COLOR_ERROR, "payload too short"); packet_hexdump(data + 2, length); return; } packet_print_addr("Advertiser address", data + 2, tx_add); packet_print_addr("Inititator address", data + 8, rx_add); break; case 0x03: /* SCAN_REQ */ if (length < 12) { print_text(COLOR_ERROR, "payload too short"); packet_hexdump(data + 2, length); return; } packet_print_addr("Scanner address", data + 2, tx_add); packet_print_addr("Advertiser address", data + 8, rx_add); break; case 0x05: /* CONNECT_REQ */ if (length < 34) { print_text(COLOR_ERROR, "payload too short"); packet_hexdump(data + 2, length); return; } packet_print_addr("Inititator address", data + 2, tx_add); packet_print_addr("Advertiser address", data + 8, rx_add); access_addr = ptr[14] | ptr[15] << 8 | ptr[16] << 16 | ptr[17] << 24; crc_init = ptr[18] | ptr[19] << 8 | ptr[20] << 16; print_field("Access address: 0x%8.8x", access_addr); print_field("CRC init: 0x%6.6x", crc_init); set_crc_init(access_addr, crc24_bit_reverse(crc_init)); win_size = ptr[21]; win_offset = ptr[22] | ptr[23] << 8; interval = ptr[24] | ptr[25] << 8; latency = ptr[26] | ptr[27] << 8; timeout = ptr[28] | ptr[29] << 8; print_field("Transmit window size: %u", win_size); print_field("Transmit window offset: %u", win_offset); print_field("Connection interval: %u", interval); print_field("Connection peripheral latency: %u", latency); print_field("Connection supervision timeout: %u", timeout); packet_print_channel_map_ll(ptr + 30); hop = ptr[35] & 0x1f; sca = (ptr[35] & 0xe0) >> 5; switch (sca) { case 0: str = "251 ppm to 500 ppm"; break; case 1: str = "151 ppm to 250 ppm"; break; case 2: str = "101 ppm to 150ppm"; break; case 3: str = "76 ppm to 100 ppm"; break; case 4: str = "51 ppm to 75 ppm"; break; case 5: str = "31 ppm to 50 ppm"; break; case 6: str = "21 ppm to 30 ppm"; break; case 7: str = "0 ppm to 20 ppm"; break; default: str = "Invalid"; break; } print_field("Hop increment: %u", hop); print_field("Sleep clock accuracy: %s (%u)", str, sca); break; default: packet_hexdump(data + 2, length); break; } } static void data_packet(const void *data, uint8_t size, bool padded) { const uint8_t *ptr = data; uint8_t llid, length; bool nesn, sn, md; const char *str; if (size < 2) { print_text(COLOR_ERROR, "packet too short"); packet_hexdump(data, size); return; } llid = ptr[0] & 0x03; nesn = !!(ptr[0] & 0x04); sn = !!(ptr[0] & 0x08); md = !!(ptr[0] & 0x10); length = ptr[1] & 0x1f; switch (llid) { case 0x01: if (length > 0) str = "Continuation fragement of L2CAP message"; else str = "Empty message"; break; case 0x02: str = "Start of L2CAP message"; break; case 0x03: str = "Control"; break; default: str = "Reserved"; break; } print_field("LLID: %s (0x%2.2x)", str, llid); print_field("Next expected sequence number: %u", nesn); print_field("Sequence number: %u", sn); print_field("More data: %u", md); print_field("Length: %u", length); switch (llid) { case 0x03: llcp_packet(data + 2, size - 2, padded); break; default: packet_hexdump(data + 2, size - 2); break; } } void ll_packet(uint16_t frequency, const void *data, uint8_t size, bool padded) { const struct bt_ll_hdr *hdr = data; uint8_t channel = (frequency - 2402) / 2; uint32_t access_addr; char access_str[12]; const char *channel_label, *channel_color; const uint8_t *pdu_data; uint8_t pdu_len; uint32_t pdu_crc, crc, crc_init; if (size < sizeof(*hdr)) { print_text(COLOR_ERROR, "packet missing header"); packet_hexdump(data, size); return; } if (size < sizeof(*hdr) + 3) { print_text(COLOR_ERROR, "packet missing checksum"); packet_hexdump(data, size); return; } if (hdr->preamble != 0xaa && hdr->preamble != 0x55) { print_text(COLOR_ERROR, "invalid preamble"); packet_hexdump(data, size); return; } access_addr = le32_to_cpu(hdr->access_addr); pdu_data = data + sizeof(*hdr); pdu_len = size - sizeof(*hdr) - 3; pdu_crc = pdu_data[pdu_len + 0] | (pdu_data[pdu_len + 1] << 8) | (pdu_data[pdu_len + 2] << 16); if (access_addr == 0x8e89bed6) { channel_label = "Advertising channel: "; channel_color = COLOR_MAGENTA; } else { channel_label = "Data channel: "; channel_color = COLOR_CYAN; } sprintf(access_str, "0x%8.8x", access_addr); print_indent(6, channel_color, channel_label, access_str, COLOR_OFF, " (channel %d) len %d crc 0x%6.6x", channel, pdu_len, pdu_crc); if (access_addr == 0x8e89bed6) crc_init = 0xaaaaaa; else crc_init = get_crc_init(access_addr); if (crc_init) { crc = crc24_calculate(crc_init, pdu_data, pdu_len); if (crc != pdu_crc) { print_text(COLOR_ERROR, "invalid checksum"); packet_hexdump(pdu_data, pdu_len); return; } } else print_text(COLOR_ERROR, "unknown access address"); if (access_addr == 0x8e89bed6) advertising_packet(pdu_data, pdu_len); else data_packet(pdu_data, pdu_len, padded); } static void null_pdu(const void *data, uint8_t size) { } static void conn_update_req(const void *data, uint8_t size) { const struct bt_ll_conn_update_req *pdu = data; print_field("Transmit window size: %u", pdu->win_size); print_field("Transmit window offset: %u", le16_to_cpu(pdu->win_offset)); print_field("Connection interval: %u", le16_to_cpu(pdu->interval)); print_field("Connection peripheral latency: %u", le16_to_cpu(pdu->latency)); print_field("Connection supervision timeout: %u", le16_to_cpu(pdu->timeout)); print_field("Connection instant: %u", le16_to_cpu(pdu->instant)); } static void channel_map_req(const void *data, uint8_t size) { const struct bt_ll_channel_map_req *pdu = data; packet_print_channel_map_ll(pdu->map); print_field("Connection instant: %u", le16_to_cpu(pdu->instant)); } static void terminate_ind(const void *data, uint8_t size) { const struct bt_ll_terminate_ind *pdu = data; packet_print_error("Error code", pdu->error); } static void enc_req(const void *data, uint8_t size) { const struct bt_ll_enc_req *pdu = data; print_field("Rand: 0x%16.16" PRIx64, le64_to_cpu(pdu->rand)); print_field("EDIV: 0x%4.4x", le16_to_cpu(pdu->ediv)); print_field("SKD (central): 0x%16.16" PRIx64, le64_to_cpu(pdu->skd)); print_field("IV (central): 0x%8.8x", le32_to_cpu(pdu->iv)); } static void enc_rsp(const void *data, uint8_t size) { const struct bt_ll_enc_rsp *pdu = data; print_field("SKD (peripheral): 0x%16.16" PRIx64, le64_to_cpu(pdu->skd)); print_field("IV (peripheral): 0x%8.8x", le32_to_cpu(pdu->iv)); } static const char *opcode_to_string(uint8_t opcode); static void unknown_rsp(const void *data, uint8_t size) { const struct bt_ll_unknown_rsp *pdu = data; print_field("Unknown type: %s (0x%2.2x)", opcode_to_string(pdu->type), pdu->type); } static void feature_req(const void *data, uint8_t size) { const struct bt_ll_feature_req *pdu = data; packet_print_features_ll(pdu->features); } static void feature_rsp(const void *data, uint8_t size) { const struct bt_ll_feature_rsp *pdu = data; packet_print_features_ll(pdu->features); } static void version_ind(const void *data, uint8_t size) { const struct bt_ll_version_ind *pdu = data; packet_print_version("Version", pdu->version, "Subversion", le16_to_cpu(pdu->subversion)); packet_print_company("Company", le16_to_cpu(pdu->company)); } static void reject_ind(const void *data, uint8_t size) { const struct bt_ll_reject_ind *pdu = data; packet_print_error("Error code", pdu->error); } static void peripheral_feature_req(const void *data, uint8_t size) { const struct bt_ll_peripheral_feature_req *pdu = data; packet_print_features_ll(pdu->features); } static void conn_param_req(const void *data, uint8_t size) { const struct bt_ll_conn_param_req *pdu = data; print_field("Interval min: %.2f msec (0x%4.4x)", pdu->interval_min * 1.25, pdu->interval_min); print_field("Interval max: %.2f msec (0x%4.4x)", pdu->interval_max * 1.25, pdu->interval_max); print_field("Latency: %d (0x%4.4x)", pdu->latency, pdu->latency); print_field("Timeout: %d msec (0x%4.4x)", pdu->timeout * 10, pdu->timeout); print_field("Preffered periodicity: %.2f (0x%2.2x)", pdu->pref_period * 1.25, pdu->pref_period); print_field("Reference connection event count: %d (0x%2.2x)", pdu->pref_conn_evt_count, pdu->pref_conn_evt_count); print_field("Offset 0: %.2f msec (0x%2.2x)", pdu->offset_0 * 1.25, pdu->offset_0); print_field("Offset 1: %.2f msec (0x%2.2x)", pdu->offset_1 * 1.25, pdu->offset_1); print_field("Offset 2: %.2f msec (0x%2.2x)", pdu->offset_2 * 1.25, pdu->offset_2); print_field("Offset 3: %.2f msec (0x%2.2x)", pdu->offset_3 * 1.25, pdu->offset_3); print_field("Offset 4: %.2f msec (0x%2.2x)", pdu->offset_4 * 1.25, pdu->offset_4); print_field("Offset 5: %.2f msec (0x%2.2x)", pdu->offset_5 * 1.25, pdu->offset_5); } static void conn_param_rsp(const void *data, uint8_t size) { const struct bt_ll_conn_param_rsp *pdu = data; print_field("Interval min: %.2f msec (0x%4.4x)", pdu->interval_min * 1.25, pdu->interval_min); print_field("Interval max: %.2f msec (0x%4.4x)", pdu->interval_max * 1.25, pdu->interval_max); print_field("Latency: %d (0x%4.4x)", pdu->latency, pdu->latency); print_field("Timeout: %d msec (0x%4.4x)", pdu->timeout * 10, pdu->timeout); print_field("Preffered periodicity: %.2f (0x%2.2x)", pdu->pref_period * 1.25, pdu->pref_period); print_field("Reference connection event count: %d (0x%2.2x)", pdu->pref_conn_evt_count, pdu->pref_conn_evt_count); print_field("Offset 0: %.2f msec (0x%2.2x)", pdu->offset_0 * 1.25, pdu->offset_0); print_field("Offset 1: %.2f msec (0x%2.2x)", pdu->offset_1 * 1.25, pdu->offset_1); print_field("Offset 2: %.2f msec (0x%2.2x)", pdu->offset_2 * 1.25, pdu->offset_2); print_field("Offset 3: %.2f msec (0x%2.2x)", pdu->offset_3 * 1.25, pdu->offset_3); print_field("Offset 4: %.2f msec (0x%2.2x)", pdu->offset_4 * 1.25, pdu->offset_4); print_field("Offset 5: %.2f msec (0x%2.2x)", pdu->offset_5 * 1.25, pdu->offset_5); } static void reject_ind_ext(const void *data, uint8_t size) { const struct bt_ll_reject_ind_ext *pdu = data; print_field("Reject opcode: %u (0x%2.2x)", pdu->opcode, pdu->opcode); packet_print_error("Error code", pdu->error); } static void length_req_rsp(const void *data, uint8_t size) { const struct bt_ll_length *pdu = data; print_field("MaxRxOctets: %u", pdu->rx_len); print_field("MaxRxTime: %u", pdu->rx_time); print_field("MaxTxOctets: %u", pdu->tx_len); print_field("MaxtxTime: %u", pdu->tx_time); } static const struct bitfield_data le_phys[] = { { 0, "LE 1M" }, { 1, "LE 2M" }, { 2, "LE Coded"}, { } }; static void phy_req_rsp(const void *data, uint8_t size) { const struct bt_ll_phy *pdu = data; uint8_t mask; print_field("RX PHYs: 0x%2.2x", pdu->rx_phys); mask = print_bitfield(2, pdu->rx_phys, le_phys); if (mask) print_text(COLOR_UNKNOWN_OPTIONS_BIT, " Reserved" " (0x%2.2x)", mask); print_field("TX PHYs: 0x%2.2x", pdu->tx_phys); mask = print_bitfield(2, pdu->tx_phys, le_phys); if (mask) print_text(COLOR_UNKNOWN_OPTIONS_BIT, " Reserved" " (0x%2.2x)", mask); } static void phy_update_ind(const void *data, uint8_t size) { const struct bt_ll_phy_update_ind *pdu = data; uint8_t mask; print_field("C_TO_P_PHY: 0x%2.2x", pdu->c_phy); mask = print_bitfield(2, pdu->c_phy, le_phys); if (mask) print_text(COLOR_UNKNOWN_OPTIONS_BIT, " Reserved" " (0x%2.2x)", mask); print_field("P_TO_C_PHY: 0x%2.2x", pdu->p_phy); mask = print_bitfield(2, pdu->p_phy, le_phys); if (mask) print_text(COLOR_UNKNOWN_OPTIONS_BIT, " Reserved" " (0x%2.2x)", mask); print_field("Instant: 0x%4.4x", pdu->instant); } static void min_used_channels(const void *data, uint8_t size) { const struct bt_ll_min_used_channels *pdu = data; uint8_t mask; print_field("PHYS: 0x%2.2x", pdu->phys); mask = print_bitfield(2, pdu->phys, le_phys); if (mask) print_text(COLOR_UNKNOWN_OPTIONS_BIT, " Reserved" " (0x%2.2x)", mask); print_field("MinUsedChannels: 0x%2.2x", pdu->min_channels); } static void cte_req(const void *data, uint8_t size) { const struct bt_ll_cte_req *pdu = data; print_field("MinCTELenReq: 0x%2.2x", pdu->cte & 0xf8); print_field("CTETypeReq: 0x%2.2x", pdu->cte & 0x03); switch (pdu->cte & 0x03) { case 0x00: print_field(" AoA Constant Tone Extension"); break; case 0x01: print_field(" AoD Constant Tone Extension with 1 μs slots"); break; case 0x02: print_field(" AoD Constant Tone Extension with 2 μs slots"); break; } } static void periodic_sync_ind(const void *data, uint8_t size) { const struct bt_ll_periodic_sync_ind *pdu = data; uint8_t mask; print_field("ID: 0x%4.4x", pdu->id); print_field("SyncInfo:"); packet_hexdump(pdu->info, sizeof(pdu->info)); print_field("connEventCount: 0x%4.4x", pdu->event_count); print_field("lastPaEventCounter: 0x%4.4x", pdu->last_counter); print_field("SID: 0x%2.2x", pdu->adv_info & 0xf0); print_field("AType: %s", pdu->adv_info & 0x08 ? "random" : "public"); print_field("SCA: 0x%2.2x", pdu->adv_info & 0x07); print_field("PHY: 0x%2.2x", pdu->phy); mask = print_bitfield(2, pdu->phy, le_phys); if (mask) print_text(COLOR_UNKNOWN_OPTIONS_BIT, " Reserved" " (0x%2.2x)", mask); packet_print_addr("AdvA", pdu->adv_addr, pdu->adv_info & 0x08); print_field("syncConnEventCount: 0x%4.4x", pdu->sync_counter); } static void clock_acc_req_rsp(const void *data, uint8_t size) { const struct bt_ll_clock_acc *pdu = data; print_field("SCA: 0x%2.2x", pdu->sca); } static void cis_req(const void *data, uint8_t size) { const struct bt_ll_cis_req *cmd = data; uint32_t interval; uint8_t mask; print_field("CIG ID: 0x%2.2x", cmd->cig); print_field("CIS ID: 0x%2.2x", cmd->cis); print_field("Central to Peripheral PHY: 0x%2.2x", cmd->c_phy); mask = print_bitfield(2, cmd->c_phy, le_phys); if (mask) print_text(COLOR_UNKNOWN_OPTIONS_BIT, " Reserved" " (0x%2.2x)", mask); print_field("Peripheral To Central PHY: 0x%2.2x", cmd->p_phy); mask = print_bitfield(2, cmd->p_phy, le_phys); if (mask) print_text(COLOR_UNKNOWN_OPTIONS_BIT, " Reserved" " (0x%2.2x)", mask); print_field("Central to Peripheral Maximum SDU: %u", cmd->c_sdu); print_field("Peripheral to Central Maximum SDU: %u", cmd->p_sdu); memcpy(&interval, cmd->c_interval, sizeof(cmd->c_interval)); print_field("Central to Peripheral Interval: 0x%6.6x", le32_to_cpu(interval)); memcpy(&interval, cmd->p_interval, sizeof(cmd->p_interval)); print_field("Peripheral to Central Interval: 0x%6.6x", le32_to_cpu(interval)); print_field("Central to Peripheral Maximum PDU: %u", cmd->c_pdu); print_field("Peripheral to Central Maximum PDU: %u", cmd->p_pdu); print_field("Burst Number: %u us", cmd->bn); memcpy(&interval, cmd->sub_interval, sizeof(cmd->sub_interval)); print_field("Sub-Interval: 0x%6.6x", le32_to_cpu(interval)); print_field("Central to Peripheral Flush Timeout: %u", cmd->c_ft); print_field("Peripheral to Central Flush Timeout: %u", cmd->p_ft); print_field("ISO Interval: 0x%4.4x", le16_to_cpu(cmd->iso_interval)); memcpy(&interval, cmd->offset_min, sizeof(cmd->offset_min)); print_field("CIS Offset Minimum: 0x%6.6x", le32_to_cpu(interval)); memcpy(&interval, cmd->offset_max, sizeof(cmd->offset_max)); print_field("CIS Offset Maximum: 0x%6.6x", le32_to_cpu(interval)); print_field("Connection Event Count: %u", cmd->conn_event_count); } static void cis_rsp(const void *data, uint8_t size) { const struct bt_ll_cis_rsp *rsp = data; uint32_t interval; memcpy(&interval, rsp->offset_min, sizeof(rsp->offset_min)); print_field("CIS Offset Minimum: 0x%6.6x", le32_to_cpu(interval)); memcpy(&interval, rsp->offset_max, sizeof(rsp->offset_max)); print_field("CIS Offset Maximum: 0x%6.6x", le32_to_cpu(interval)); print_field("Connection Event Count: %u", rsp->conn_event_count); } static void cis_ind(const void *data, uint8_t size) { const struct bt_ll_cis_ind *ind = data; uint32_t interval; print_field("CIS Access Address: 0x%4.4x", le32_to_cpu(ind->addr)); memcpy(&interval, ind->cis_offset, sizeof(ind->cis_offset)); print_field("CIS Offset: 0x%6.6x", le32_to_cpu(interval)); memcpy(&interval, ind->cig_sync_delay, sizeof(ind->cig_sync_delay)); print_field("CIG Synchronization Delay: 0x%6.6x", le32_to_cpu(interval)); memcpy(&interval, ind->cis_sync_delay, sizeof(ind->cis_sync_delay)); print_field("CIS Synchronization Delay: %u us", le32_to_cpu(interval)); print_field("Connection Event Count: %u", ind->conn_event_count); } static void cis_term_ind(const void *data, uint8_t size) { const struct bt_ll_cis_term_ind *ind = data; print_field("CIG ID: 0x%2.2x", ind->cig); print_field("CIS ID: 0x%2.2x", ind->cis); packet_print_error("Reason", ind->reason); } struct llcp_data { uint8_t opcode; const char *str; void (*func) (const void *data, uint8_t size); uint8_t size; bool fixed; }; static const struct llcp_data llcp_table[] = { { 0x00, "LL_CONNECTION_UPDATE_REQ", conn_update_req, 11, true }, { 0x01, "LL_CHANNEL_MAP_REQ", channel_map_req, 7, true }, { 0x02, "LL_TERMINATE_IND", terminate_ind, 1, true }, { 0x03, "LL_ENC_REQ", enc_req, 22, true }, { 0x04, "LL_ENC_RSP", enc_rsp, 12, true }, { 0x05, "LL_START_ENC_REQ", null_pdu, 0, true }, { 0x06, "LL_START_ENC_RSP", null_pdu, 0, true }, { 0x07, "LL_UNKNOWN_RSP", unknown_rsp, 1, true }, { 0x08, "LL_FEATURE_REQ", feature_req, 8, true }, { 0x09, "LL_FEATURE_RSP", feature_rsp, 8, true }, { 0x0a, "LL_PAUSE_ENC_REQ", null_pdu, 0, true }, { 0x0b, "LL_PAUSE_ENC_RSP", null_pdu, 0, true }, { 0x0c, "LL_VERSION_IND", version_ind, 5, true }, { 0x0d, "LL_REJECT_IND", reject_ind, 1, true }, { 0x0e, "LL_PERIPHERAL_FEATURE_REQ", peripheral_feature_req, 8, true }, { 0x0f, "LL_CONNECTION_PARAM_REQ", conn_param_req, 23, true }, { 0x10, "LL_CONNECTION_PARAM_RSP", conn_param_rsp, 23, true }, { 0x11, "LL_REJECT_IND_EXT", reject_ind_ext, 2, true }, { 0x12, "LL_PING_REQ", null_pdu, 0, true }, { 0x13, "LL_PING_RSP", null_pdu, 0, true }, { 0x14, "LL_LENGTH_REQ", length_req_rsp, 8, true }, { 0x15, "LL_LENGTH_RSP", length_req_rsp, 8, true }, { 0x16, "LL_PHY_REQ", phy_req_rsp, 2, true }, { 0x17, "LL_PHY_RSP", phy_req_rsp, 2, true }, { 0x18, "LL_PHY_UPDATE_IND", phy_update_ind, 4, true }, { 0x19, "LL_MIN_USED_CHANNELS_IND", min_used_channels, 2, true }, { 0x1a, "LL_CTE_REQ", cte_req, 1, true }, { 0x1b, "LL_CTE_RSP", null_pdu, 0, true }, { 0x1c, "LL_PERIODIC_SYNC_IND", periodic_sync_ind, 34, true }, { 0x1d, "LL_CLOCK_ACCURACY_REQ", clock_acc_req_rsp, 1, true }, { 0x1e, "LL_CLOCK_ACCURACY_RSP", clock_acc_req_rsp, 1, true }, { BT_LL_CIS_REQ, "LL_CIS_REQ", cis_req, sizeof(struct bt_ll_cis_req), true }, { BT_LL_CIS_RSP, "LL_CIS_RSP", cis_rsp, sizeof(struct bt_ll_cis_rsp), true }, { BT_LL_CIS_IND, "LL_CIS_IND", cis_ind, sizeof(struct bt_ll_cis_ind), true }, { BT_LL_CIS_TERMINATE_IND, "LL_CIS_TERMINATE_IND", cis_term_ind, sizeof(struct bt_ll_cis_term_ind), true }, { } }; static const char *opcode_to_string(uint8_t opcode) { int i; for (i = 0; llcp_table[i].str; i++) { if (llcp_table[i].opcode == opcode) return llcp_table[i].str; } return "Unknown"; } void llcp_packet(const void *data, uint8_t size, bool padded) { uint8_t opcode = ((const uint8_t *) data)[0]; const struct llcp_data *llcp_data = NULL; const char *opcode_color, *opcode_str; int i; for (i = 0; llcp_table[i].str; i++) { if (llcp_table[i].opcode == opcode) { llcp_data = &llcp_table[i]; break; } } if (llcp_data) { if (llcp_data->func) opcode_color = COLOR_OPCODE; else opcode_color = COLOR_OPCODE_UNKNOWN; opcode_str = llcp_data->str; } else { opcode_color = COLOR_OPCODE_UNKNOWN; opcode_str = "Unknown"; } print_indent(6, opcode_color, "", opcode_str, COLOR_OFF, " (0x%2.2x)", opcode); if (!llcp_data || !llcp_data->func) { packet_hexdump(data + 1, size - 1); return; } if (llcp_data->fixed && !padded) { if (size - 1 != llcp_data->size) { print_text(COLOR_ERROR, "invalid packet size"); packet_hexdump(data + 1, size - 1); return; } } else { if (size - 1 < llcp_data->size) { print_text(COLOR_ERROR, "too short packet"); packet_hexdump(data + 1, size - 1); return; } } llcp_data->func(data + 1, size - 1); } bluez-5.82/monitor/PaxHeaders/broadcom.h0000644000000000000000000000005014015011623015265 xustar0020 atime=1743515957 20 ctime=1743591282 bluez-5.82/monitor/broadcom.h0000644000000000000000000000072014015011623014745 0ustar00rootroot/* SPDX-License-Identifier: LGPL-2.1-or-later */ /* * * BlueZ - Bluetooth protocol stack for Linux * * Copyright (C) 2011-2014 Intel Corporation * Copyright (C) 2002-2010 Marcel Holtmann * * */ #include struct vendor_ocf; struct vendor_evt; const struct vendor_ocf *broadcom_vendor_ocf(uint16_t ocf); const struct vendor_evt *broadcom_vendor_evt(uint8_t evt); void broadcom_lm_diag(const void *data, uint8_t size); bluez-5.82/monitor/PaxHeaders/msft.c0000644000000000000000000000005014447506754014472 xustar0020 atime=1743516059 20 ctime=1743591282 bluez-5.82/monitor/msft.c0000644000000000000000000002014014447506754014150 0ustar00rootroot/* * * BlueZ - Bluetooth protocol stack for Linux * * Copyright (C) 2011-2014 Intel Corporation * Copyright (C) 2002-2010 Marcel Holtmann * * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA * */ #ifdef HAVE_CONFIG_H #include #endif #define _GNU_SOURCE #include #include #include "lib/bluetooth.h" #include "lib/uuid.h" #include "src/shared/util.h" #include "display.h" #include "packet.h" #include "vendor.h" #include "msft.h" #define COLOR_COMMAND COLOR_BLUE #define COLOR_COMMAND_UNKNOWN COLOR_WHITE_BG static void null_cmd(const void *data, uint16_t size) { } static void null_rsp(const void *data, uint16_t size) { } static void read_supported_features_rsp(const void *data, uint16_t size) { const struct msft_rsp_read_supported_features *rsp = data; packet_print_features_msft(rsp->features); print_field("Event prefix length: %u", rsp->evt_prefix_len); print_field("Event prefix:"); packet_hexdump(rsp->evt_prefix, rsp->evt_prefix_len); packet_set_msft_evt_prefix(rsp->evt_prefix, rsp->evt_prefix_len); } static void monitor_rssi_cmd(const void *data, uint16_t size) { const struct msft_cmd_monitor_rssi *cmd = data; print_field("Connection handle: 0x%04x", cmd->handle); packet_print_rssi("RSSI threshold high", cmd->rssi_high); packet_print_rssi("RSSI threshold low", cmd->rssi_low); print_field("RSSI threshold low time interval: %u sec (0x%2.2x)", cmd->rssi_low_interval, cmd->rssi_low_interval); print_field("RSSI sampling period: %u msec (0x%2.2x)", cmd->rssi_period * 100, cmd->rssi_period); } static void cancel_monitor_rssi_cmd(const void *data, uint16_t size) { const struct msft_cmd_cancel_monitor_rssi *cmd = data; print_field("Connection handle: 0x%04x", cmd->handle); } static void le_monitor_advertisement_cmd(const void *data, uint16_t size) { const struct msft_cmd_le_monitor_adv *cmd = data; const struct msft_le_monitor_adv_patterns *patterns; const struct msft_le_monitor_adv_uuid *uuid; const struct msft_le_monitor_adv_irk *irk; const struct msft_le_monitor_adv_addr *addr; const char *str; char uuidstr[MAX_LEN_UUID_STR]; packet_print_rssi("RSSI threshold high", cmd->rssi_high); packet_print_rssi("RSSI threshold low", cmd->rssi_low); print_field("RSSI threshold low time interval: %u sec (0x%2.2x)", cmd->rssi_low_interval, cmd->rssi_low_interval); print_field("RSSI sampling period: %u msec (0x%2.2x)", cmd->rssi_period * 100, cmd->rssi_period); switch (cmd->type) { case MSFT_LE_MONITOR_ADV_PATTERN: print_field("Type: Pattern (0x%2.2x)", cmd->type); patterns = (void *)cmd->data; print_field("Number of patterns: %u", patterns->num); packet_hexdump((void *)patterns->data, size - (sizeof(*cmd) + sizeof(*patterns))); break; case MSFT_LE_MONITOR_ADV_UUID: print_field("Type: UUID (0x%2.2x)", cmd->type); uuid = (void *)cmd->data; switch (uuid->type) { case 0x01: str = bt_uuid16_to_str(uuid->value.u16); print_field("UUID: %s (0x%4.4x)", str, uuid->value.u16); break; case 0x02: str = bt_uuid32_to_str(uuid->value.u32); print_field("UUID: %s (0x%8.8x)", str, uuid->value.u32); break; case 0x03: sprintf(uuidstr, "%8.8x-%4.4x-%4.4x-%4.4x-%8.8x%4.4x", get_le32(uuid->value.u128 + 12), get_le16(uuid->value.u128 + 10), get_le16(uuid->value.u128 + 8), get_le16(uuid->value.u128 + 6), get_le32(uuid->value.u128 + 2), get_le16(uuid->value.u128 + 0)); str = bt_uuidstr_to_str(uuidstr); print_field("UUID: %s (%s)", str, uuidstr); break; default: packet_hexdump((void *)&uuid->value, size - sizeof(*cmd)); break; } break; case MSFT_LE_MONITOR_ADV_IRK: print_field("Type: IRK (0x%2.2x)", cmd->type); irk = (void *)cmd->data; print_field("IRK:"); packet_hexdump(irk->irk, size - sizeof(*cmd)); break; case MSFT_LE_MONITOR_ADV_ADDR: print_field("Type: Adderss (0x%2.2x)", cmd->type); addr = (void *)cmd->data; packet_print_addr(NULL, addr->addr, addr->type); break; default: print_field("Type: Unknown (0x%2.2x)", cmd->type); packet_hexdump(cmd->data, size - sizeof(*cmd)); break; } } static void le_monitor_advertisement_rsp(const void *data, uint16_t size) { const struct msft_rsp_le_monitor_adv *rsp = data; print_field("Monitor handle: %u", rsp->handle); } static void le_cancel_monitor_adv_cmd(const void *data, uint16_t size) { const struct msft_cmd_le_cancel_monitor_adv *cmd = data; print_field("Monitor handle: %u", cmd->handle); } static void set_adv_filter_enable_cmd(const void *data, uint16_t size) { const struct msft_cmd_le_monitor_adv_enable *cmd = data; const char *str; switch (cmd->enable) { case 0x00: str = "Current allow list"; break; case 0x01: str = "All filter conditions"; break; default: str = "Reserved"; break; } print_field("Enable: %s (0x%2.2x)", str, cmd->enable); } typedef void (*func_t) (const void *data, uint16_t size); static const struct { uint8_t code; const char *str; func_t cmd_func; func_t rsp_func; } cmd_table[] = { { 0x00, "Read Supported Features", null_cmd, read_supported_features_rsp }, { 0x01, "Monitor RSSI", monitor_rssi_cmd }, { 0x02, "Cancel Monitor RSSI", cancel_monitor_rssi_cmd }, { 0x03, "LE Monitor Advertisement", le_monitor_advertisement_cmd, le_monitor_advertisement_rsp }, { 0x04, "LE Cancel Monitor Advertisement", le_cancel_monitor_adv_cmd }, { 0x05, "LE Set Advertisement Filter Enable", set_adv_filter_enable_cmd, null_rsp }, { 0x06, "Read Absolute RSSI" }, { } }; static void msft_cmd(uint16_t index, const void *data, uint8_t size) { uint8_t code = get_u8(data); const char *code_color, *code_str = NULL; func_t code_func = NULL; int i; for (i = 0; cmd_table[i].str; i++) { if (cmd_table[i].code == code) { code_str = cmd_table[i].str; code_func = cmd_table[i].cmd_func; break; } } if (code_str) { if (code_func) code_color = COLOR_COMMAND; else code_color = COLOR_COMMAND_UNKNOWN; } else { code_color = COLOR_COMMAND_UNKNOWN; code_str = "Unknown"; } print_indent(6, code_color, "", code_str, COLOR_OFF, " (0x%2.2x)", code); if (code_func) code_func(data, size); else packet_hexdump(data + 1, size - 1); } static void msft_rsp(uint16_t index, const void *data, uint8_t size) { uint8_t status = get_u8(data); uint8_t code = get_u8(data + 1); const char *code_color, *code_str = NULL; func_t code_func = NULL; int i; for (i = 0; cmd_table[i].str; i++) { if (cmd_table[i].code == code) { code_str = cmd_table[i].str; code_func = cmd_table[i].rsp_func; break; } } if (code_str) { if (code_func) code_color = COLOR_COMMAND; else code_color = COLOR_COMMAND_UNKNOWN; } else { code_color = COLOR_COMMAND_UNKNOWN; code_str = "Unknown"; } print_indent(6, code_color, "", code_str, COLOR_OFF, " (0x%2.2x)", code); packet_print_error("Status", status); if (code_func) code_func(data, size); else packet_hexdump(data + 2, size - 2); } static const struct vendor_ocf vendor_ocf_entry = { 0x000, "Extension", msft_cmd, 1, false, msft_rsp, 2, false }; const struct vendor_ocf *msft_vendor_ocf(void) { return &vendor_ocf_entry; } static void msft_evt(struct timeval *tv, uint16_t index, const void *data, uint8_t size) { packet_hexdump(data, size); } static const struct vendor_evt vendor_evt_entry = { 0x00, "Extension", msft_evt, 1, false }; const struct vendor_evt *msft_vendor_evt(void) { return &vendor_evt_entry; } bluez-5.82/monitor/PaxHeaders/lmp.h0000644000000000000000000000005014015011623014267 xustar0020 atime=1743515951 20 ctime=1743591282 bluez-5.82/monitor/lmp.h0000644000000000000000000000051714015011623013753 0ustar00rootroot/* SPDX-License-Identifier: LGPL-2.1-or-later */ /* * * BlueZ - Bluetooth protocol stack for Linux * * Copyright (C) 2011-2014 Intel Corporation * Copyright (C) 2002-2010 Marcel Holtmann * * */ #include void lmp_packet(const void *data, uint8_t size, bool padded); void lmp_todo(void); bluez-5.82/monitor/PaxHeaders/display.c0000644000000000000000000000005014031556404015150 xustar0020 atime=1743515952 20 ctime=1743591282 bluez-5.82/monitor/display.c0000644000000000000000000000577714031556404014651 0ustar00rootroot// SPDX-License-Identifier: LGPL-2.1-or-later /* * * BlueZ - Bluetooth protocol stack for Linux * * Copyright (C) 2011-2014 Intel Corporation * Copyright (C) 2002-2010 Marcel Holtmann * * */ #ifdef HAVE_CONFIG_H #include #endif #define _GNU_SOURCE #include #include #include #include #include #include #include #include #include #include #include "display.h" static pid_t pager_pid = 0; int default_pager_num_columns = FALLBACK_TERMINAL_WIDTH; enum monitor_color setting_monitor_color = COLOR_AUTO; void set_monitor_color(enum monitor_color color) { setting_monitor_color = color; } bool use_color(void) { static int cached_use_color = -1; if (setting_monitor_color == COLOR_ALWAYS) cached_use_color = 1; else if (setting_monitor_color == COLOR_NEVER) cached_use_color = 0; else if (__builtin_expect(!!(cached_use_color < 0), 0)) cached_use_color = isatty(STDOUT_FILENO) > 0 || pager_pid > 0; return cached_use_color; } void set_default_pager_num_columns(int num_columns) { default_pager_num_columns = num_columns; } int num_columns(void) { static int cached_num_columns = -1; if (__builtin_expect(!!(cached_num_columns < 0), 0)) { struct winsize ws; if (ioctl(STDOUT_FILENO, TIOCGWINSZ, &ws) < 0 || ws.ws_col == 0) cached_num_columns = default_pager_num_columns; else cached_num_columns = ws.ws_col; } return cached_num_columns; } static void close_pipe(int p[]) { if (p[0] >= 0) close(p[0]); if (p[1] >= 0) close(p[1]); } static void wait_for_terminate(pid_t pid) { siginfo_t dummy; for (;;) { memset(&dummy, 0, sizeof(dummy)); if (waitid(P_PID, pid, &dummy, WEXITED) < 0) { if (errno == EINTR) continue; return; } return; } } void open_pager(void) { const char *pager; pid_t parent_pid; int fd[2]; if (pager_pid > 0) return; pager = getenv("PAGER"); if (pager) { if (!*pager || strcmp(pager, "cat") == 0) return; } if (!(isatty(STDOUT_FILENO) > 0)) return; num_columns(); if (pipe(fd) < 0) { perror("Failed to create pager pipe"); return; } parent_pid = getpid(); pager_pid = fork(); if (pager_pid < 0) { perror("Failed to fork pager"); close_pipe(fd); return; } if (pager_pid == 0) { dup2(fd[0], STDIN_FILENO); close_pipe(fd); setenv("LESS", "FRSX", 0); if (prctl(PR_SET_PDEATHSIG, SIGTERM) < 0) _exit(EXIT_FAILURE); if (getppid() != parent_pid) _exit(EXIT_SUCCESS); if (pager) { execlp(pager, pager, NULL); execl("/bin/sh", "sh", "-c", pager, NULL); } execlp("pager", "pager", NULL); execlp("less", "less", NULL); execlp("more", "more", NULL); _exit(EXIT_FAILURE); } if (dup2(fd[1], STDOUT_FILENO) < 0) { perror("Failed to duplicate pager pipe"); return; } close_pipe(fd); } void close_pager(void) { if (pager_pid <= 0) return; fclose(stdout); kill(pager_pid, SIGCONT); wait_for_terminate(pager_pid); pager_pid = 0; } bluez-5.82/monitor/PaxHeaders/hcidump.h0000644000000000000000000000005014015011623015130 xustar0020 atime=1743515953 20 ctime=1743591282 bluez-5.82/monitor/hcidump.h0000644000000000000000000000040114015011623014604 0ustar00rootroot/* SPDX-License-Identifier: LGPL-2.1-or-later */ /* * * BlueZ - Bluetooth protocol stack for Linux * * Copyright (C) 2011-2014 Intel Corporation * Copyright (C) 2002-2010 Marcel Holtmann * * */ int hcidump_tracing(void); bluez-5.82/monitor/PaxHeaders/jlink.c0000644000000000000000000000005014621503015014605 xustar0020 atime=1743516061 20 ctime=1743591282 bluez-5.82/monitor/jlink.c0000644000000000000000000001370014621503015014267 0ustar00rootroot// SPDX-License-Identifier: LGPL-2.1-or-later /* * * BlueZ - Bluetooth protocol stack for Linux * * Copyright (C) 2018 Codecoup * * */ #ifdef HAVE_CONFIG_H #include #endif #include #include #include #include #include #include #include #include "jlink.h" #define RTT_CONTROL_START 0 #define RTT_CONTROL_STOP 1 #define RTT_CONTROL_GET_DESC 2 #define RTT_CONTROL_GET_NUM_BUF 3 #define RTT_CONTROL_GET_STAT 4 #define RTT_DIRECTION_UP 0 #define RTT_DIRECTION_DOWN 1 static const char * const jlink_so_name[] = { "/usr/lib/libjlinkarm.so", "/usr/lib/libjlinkarm.so.6", "/opt/SEGGER/JLink/libjlinkarm.so", "/opt/SEGGER/JLink/libjlinkarm.so.6", }; struct rtt_desc { uint32_t index; uint32_t direction; char name[32]; uint32_t size; uint32_t flags; }; static struct rtt_desc rtt_desc; static void *so = NULL; typedef int (*jlink_emu_selectbyusbsn_func) (unsigned int sn); typedef int (*jlink_open_func) (void); typedef int (*jlink_execcommand_func) (char *in, char *out, int size); typedef int (*jlink_tif_select_func) (int); typedef void (*jlink_setspeed_func) (long int speed); typedef int (*jlink_connect_func) (void); typedef unsigned int (*jlink_getsn_func) (void); typedef void (*jlink_emu_getproductname_func) (char *out, int size); typedef int (*jlink_rtterminal_control_func) (int cmd, void *data); typedef int (*jlink_rtterminal_read_func) (int cmd, char *buf, int size); struct jlink { jlink_emu_selectbyusbsn_func emu_selectbyusbsn; jlink_open_func open; jlink_execcommand_func execcommand; jlink_tif_select_func tif_select; jlink_setspeed_func setspeed; jlink_connect_func connect; jlink_getsn_func getsn; jlink_emu_getproductname_func emu_getproductname; jlink_rtterminal_control_func rtterminal_control; jlink_rtterminal_read_func rtterminal_read; }; static struct jlink jlink; #ifndef NELEM #define NELEM(x) (sizeof(x) / sizeof((x)[0])) #endif int jlink_init(void) { unsigned int i; for (i = 0; i < NELEM(jlink_so_name); i++) { so = dlopen(jlink_so_name[i], RTLD_LAZY); if (so) break; } if (!so) return -EIO; jlink.emu_selectbyusbsn = dlsym(so, "JLINK_EMU_SelectByUSBSN"); jlink.open = dlsym(so, "JLINK_Open"); jlink.execcommand = dlsym(so, "JLINK_ExecCommand"); jlink.tif_select = dlsym(so, "JLINK_TIF_Select"); jlink.setspeed = dlsym(so, "JLINK_SetSpeed"); jlink.connect = dlsym(so, "JLINK_Connect"); jlink.getsn = dlsym(so, "JLINK_GetSN"); jlink.emu_getproductname = dlsym(so, "JLINK_EMU_GetProductName"); jlink.rtterminal_control = dlsym(so, "JLINK_RTTERMINAL_Control"); jlink.rtterminal_read = dlsym(so, "JLINK_RTTERMINAL_Read"); if (!jlink.emu_selectbyusbsn || !jlink.open || !jlink.execcommand || !jlink.tif_select || !jlink.setspeed || !jlink.connect || !jlink.getsn || !jlink.emu_getproductname || !jlink.rtterminal_control || !jlink.rtterminal_read) { dlclose(so); so = NULL; return -EIO; } /* don't dlclose(so) here cause symbols from it are in use now */ return 0; } int jlink_connect(char *cfg) { const char *device = NULL; int tif = 1; unsigned int speed = 1000; unsigned int serial_no = 0; char *tok; char buf[64]; tok = strtok(cfg, ","); device = tok; tok = strtok(NULL, ","); if (!tok) goto connect; if (strlen(tok)) serial_no = atoi(tok); tok = strtok(NULL, ","); if (!tok) goto connect; if (strlen(tok)) { if (!strcasecmp("swd", tok)) tif = 1; else return -EINVAL; } tok = strtok(NULL, ","); if (!tok) goto connect; if (strlen(tok)) speed = atoi(tok); connect: if (serial_no) if (jlink.emu_selectbyusbsn(serial_no) < 0) { fprintf(stderr, "Failed to select emu by SN\n"); return -ENODEV; } if (jlink.open() < 0) { fprintf(stderr, "Failed to open J-Link\n"); return -ENODEV; } snprintf(buf, sizeof(buf), "device=%s", device); if (jlink.execcommand(buf, NULL, 0) < 0) { fprintf(stderr, "Failed to select target device\n"); return -ENODEV; } if (jlink.tif_select(tif) < 0) { fprintf(stderr, "Failed to select target interface\n"); return -ENODEV; } jlink.setspeed(speed); if (jlink.connect() < 0) { fprintf(stderr, "Failed to open target\n"); return -EIO; } serial_no = jlink.getsn(); jlink.emu_getproductname(buf, sizeof(buf)); printf("Connected to %s (S/N: %u)\n", buf, serial_no); return 0; } int jlink_start_rtt(char *cfg) { unsigned int address = 0; unsigned int area_size = 0; const char *buffer = "btmonitor"; char *tok; char cmd[64]; int rtt_dir; int count; int i; if (!cfg) goto find_rttcb; tok = strtok(cfg, ","); if (strlen(tok)) { address = strtol(tok, NULL, 0); area_size = 0x1000; } tok = strtok(NULL, ","); if (!tok) goto find_rttcb; if (strlen(tok)) area_size = strtol(tok, NULL, 0); tok = strtok(NULL, ","); if (!tok) goto find_rttcb; if (strlen(tok)) buffer = tok; find_rttcb: if (address || area_size) { if (!area_size) snprintf(cmd, sizeof(cmd), "SetRTTAddr 0x%x", address); else snprintf(cmd, sizeof(cmd), "SetRTTSearchRanges 0x%x 0x%x", address, area_size); if (jlink.execcommand(cmd, NULL, 0) < 0) return -EIO; } if (jlink.rtterminal_control(RTT_CONTROL_START, NULL) < 0) { fprintf(stderr, "Failed to initialize RTT\n"); return -1; } /* RTT may need some time to find control block so we need to wait */ do { usleep(100); rtt_dir = RTT_DIRECTION_UP; count = jlink.rtterminal_control(RTT_CONTROL_GET_NUM_BUF, &rtt_dir); } while (count < 0); for (i = 0; i < count; i++) { memset(&rtt_desc, 0, sizeof(rtt_desc)); rtt_desc.index = i; rtt_desc.direction = RTT_DIRECTION_UP; if (jlink.rtterminal_control(RTT_CONTROL_GET_DESC, &rtt_desc) < 0) continue; if (rtt_desc.size > 0 && !strcmp(buffer, rtt_desc.name)) break; } if (i == count) return -ENODEV; printf("Using RTT up buffer #%d (size: %d)\n", i, rtt_desc.size); return 0; } int jlink_rtt_read(void *buf, size_t size) { return jlink.rtterminal_read(rtt_desc.index, buf, size); } bluez-5.82/monitor/PaxHeaders/ll.h0000644000000000000000000000005014015011623014106 xustar0020 atime=1743515957 20 ctime=1743591282 bluez-5.82/monitor/ll.h0000644000000000000000000000061314015011623013567 0ustar00rootroot/* SPDX-License-Identifier: LGPL-2.1-or-later */ /* * * BlueZ - Bluetooth protocol stack for Linux * * Copyright (C) 2011-2014 Intel Corporation * Copyright (C) 2002-2010 Marcel Holtmann * * */ #include void ll_packet(uint16_t frequency, const void *data, uint8_t size, bool padded); void llcp_packet(const void *data, uint8_t size, bool padded); bluez-5.82/monitor/PaxHeaders/ellisys.h0000644000000000000000000000005014015011623015163 xustar0020 atime=1743515951 20 ctime=1743591282 bluez-5.82/monitor/ellisys.h0000644000000000000000000000065014015011623014645 0ustar00rootroot/* SPDX-License-Identifier: LGPL-2.1-or-later */ /* * * BlueZ - Bluetooth protocol stack for Linux * * Copyright (C) 2011-2014 Intel Corporation * Copyright (C) 2002-2010 Marcel Holtmann * * */ #include void ellisys_enable(const char *server, uint16_t port); void ellisys_inject_hci(struct timeval *tv, uint16_t index, uint16_t opcode, const void *data, uint16_t size); bluez-5.82/monitor/PaxHeaders/tty.h0000644000000000000000000000005014015011623014317 xustar0020 atime=1743515954 20 ctime=1743591282 bluez-5.82/monitor/tty.h0000644000000000000000000000114114015011623013775 0ustar00rootroot/* SPDX-License-Identifier: LGPL-2.1-or-later */ /* * * BlueZ - Bluetooth protocol stack for Linux * * Copyright (C) 2016 Intel Corporation * * */ #include struct tty_hdr { uint16_t data_len; uint16_t opcode; uint8_t flags; uint8_t hdr_len; uint8_t ext_hdr[0]; } __attribute__ ((packed)); #define TTY_EXTHDR_COMMAND_DROPS 1 #define TTY_EXTHDR_EVENT_DROPS 2 #define TTY_EXTHDR_ACL_TX_DROPS 3 #define TTY_EXTHDR_ACL_RX_DROPS 4 #define TTY_EXTHDR_SCO_TX_DROPS 5 #define TTY_EXTHDR_SCO_RX_DROPS 6 #define TTY_EXTHDR_OTHER_DROPS 7 #define TTY_EXTHDR_TS32 8 bluez-5.82/monitor/PaxHeaders/control.c0000644000000000000000000000005014131623652015164 xustar0020 atime=1743515954 20 ctime=1743591282 bluez-5.82/monitor/control.c0000644000000000000000000007751214131623652014661 0ustar00rootroot// SPDX-License-Identifier: LGPL-2.1-or-later /* * * BlueZ - Bluetooth protocol stack for Linux * * Copyright (C) 2011-2014 Intel Corporation * Copyright (C) 2002-2010 Marcel Holtmann * * */ #ifdef HAVE_CONFIG_H #include #endif #define _GNU_SOURCE #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "lib/bluetooth.h" #include "lib/hci.h" #include "lib/mgmt.h" #include "src/shared/util.h" #include "src/shared/btsnoop.h" #include "src/shared/mainloop.h" #include "display.h" #include "packet.h" #include "hcidump.h" #include "ellisys.h" #include "tty.h" #include "control.h" #include "jlink.h" static struct btsnoop *btsnoop_file = NULL; static bool hcidump_fallback = false; static bool decode_control = true; static uint16_t filter_index = HCI_DEV_NONE; struct control_data { uint16_t channel; int fd; unsigned char buf[BTSNOOP_MAX_PACKET_SIZE]; uint16_t offset; }; static void free_data(void *user_data) { struct control_data *data = user_data; close(data->fd); free(data); } static void mgmt_index_added(uint16_t len, const void *buf) { printf("@ Index Added\n"); packet_hexdump(buf, len); } static void mgmt_index_removed(uint16_t len, const void *buf) { printf("@ Index Removed\n"); packet_hexdump(buf, len); } static void mgmt_unconf_index_added(uint16_t len, const void *buf) { printf("@ Unconfigured Index Added\n"); packet_hexdump(buf, len); } static void mgmt_unconf_index_removed(uint16_t len, const void *buf) { printf("@ Unconfigured Index Removed\n"); packet_hexdump(buf, len); } static void mgmt_ext_index_added(uint16_t len, const void *buf) { const struct mgmt_ev_ext_index_added *ev = buf; if (len < sizeof(*ev)) { printf("* Malformed Extended Index Added control\n"); return; } printf("@ Extended Index Added: %u (%u)\n", ev->type, ev->bus); buf += sizeof(*ev); len -= sizeof(*ev); packet_hexdump(buf, len); } static void mgmt_ext_index_removed(uint16_t len, const void *buf) { const struct mgmt_ev_ext_index_removed *ev = buf; if (len < sizeof(*ev)) { printf("* Malformed Extended Index Removed control\n"); return; } printf("@ Extended Index Removed: %u (%u)\n", ev->type, ev->bus); buf += sizeof(*ev); len -= sizeof(*ev); packet_hexdump(buf, len); } static void mgmt_controller_error(uint16_t len, const void *buf) { const struct mgmt_ev_controller_error *ev = buf; if (len < sizeof(*ev)) { printf("* Malformed Controller Error control\n"); return; } printf("@ Controller Error: 0x%2.2x\n", ev->error_code); buf += sizeof(*ev); len -= sizeof(*ev); packet_hexdump(buf, len); } #ifndef NELEM #define NELEM(x) (sizeof(x) / sizeof((x)[0])) #endif static const char *config_options_str[] = { "external", "public-address", }; static void mgmt_new_config_options(uint16_t len, const void *buf) { uint32_t options; unsigned int i; if (len < 4) { printf("* Malformed New Configuration Options control\n"); return; } options = get_le32(buf); printf("@ New Configuration Options: 0x%4.4x\n", options); if (options) { printf("%-12c", ' '); for (i = 0; i < NELEM(config_options_str); i++) { if (options & (1 << i)) printf("%s ", config_options_str[i]); } printf("\n"); } buf += 4; len -= 4; packet_hexdump(buf, len); } static const char *settings_str[] = { "powered", "connectable", "fast-connectable", "discoverable", "bondable", "link-security", "ssp", "br/edr", "hs", "le", "advertising", "secure-conn", "debug-keys", "privacy", "configuration", "static-addr", "phy", "wbs" }; static void mgmt_new_settings(uint16_t len, const void *buf) { uint32_t settings; unsigned int i; if (len < 4) { printf("* Malformed New Settings control\n"); return; } settings = get_le32(buf); printf("@ New Settings: 0x%4.4x\n", settings); if (settings) { printf("%-12c", ' '); for (i = 0; i < NELEM(settings_str); i++) { if (settings & (1 << i)) printf("%s ", settings_str[i]); } printf("\n"); } buf += 4; len -= 4; packet_hexdump(buf, len); } static void mgmt_class_of_dev_changed(uint16_t len, const void *buf) { const struct mgmt_ev_class_of_dev_changed *ev = buf; if (len < sizeof(*ev)) { printf("* Malformed Class of Device Changed control\n"); return; } printf("@ Class of Device Changed: 0x%2.2x%2.2x%2.2x\n", ev->dev_class[2], ev->dev_class[1], ev->dev_class[0]); buf += sizeof(*ev); len -= sizeof(*ev); packet_hexdump(buf, len); } static void mgmt_local_name_changed(uint16_t len, const void *buf) { const struct mgmt_ev_local_name_changed *ev = buf; if (len < sizeof(*ev)) { printf("* Malformed Local Name Changed control\n"); return; } printf("@ Local Name Changed: %s (%s)\n", ev->name, ev->short_name); buf += sizeof(*ev); len -= sizeof(*ev); packet_hexdump(buf, len); } static void mgmt_new_link_key(uint16_t len, const void *buf) { const struct mgmt_ev_new_link_key *ev = buf; const char *type; char str[18]; static const char *types[] = { "Combination key", "Local Unit key", "Remote Unit key", "Debug Combination key", "Unauthenticated Combination key from P-192", "Authenticated Combination key from P-192", "Changed Combination key", "Unauthenticated Combination key from P-256", "Authenticated Combination key from P-256", }; if (len < sizeof(*ev)) { printf("* Malformed New Link Key control\n"); return; } if (ev->key.type < NELEM(types)) type = types[ev->key.type]; else type = "Reserved"; ba2str(&ev->key.addr.bdaddr, str); printf("@ New Link Key: %s (%d) %s (%u)\n", str, ev->key.addr.type, type, ev->key.type); buf += sizeof(*ev); len -= sizeof(*ev); packet_hexdump(buf, len); } static void mgmt_new_long_term_key(uint16_t len, const void *buf) { const struct mgmt_ev_new_long_term_key *ev = buf; const char *type; char str[18]; if (len < sizeof(*ev)) { printf("* Malformed New Long Term Key control\n"); return; } /* LE SC keys are both for central and peripheral */ switch (ev->key.type) { case 0x00: if (ev->key.central) type = "Central (Unauthenticated)"; else type = "Peripheral (Unauthenticated)"; break; case 0x01: if (ev->key.central) type = "Central (Authenticated)"; else type = "Peripheral (Authenticated)"; break; case 0x02: type = "SC (Unauthenticated)"; break; case 0x03: type = "SC (Authenticated)"; break; case 0x04: type = "SC (Debug)"; break; default: type = ""; break; } ba2str(&ev->key.addr.bdaddr, str); printf("@ New Long Term Key: %s (%d) %s 0x%02x\n", str, ev->key.addr.type, type, ev->key.type); buf += sizeof(*ev); len -= sizeof(*ev); packet_hexdump(buf, len); } static void mgmt_device_connected(uint16_t len, const void *buf) { const struct mgmt_ev_device_connected *ev = buf; uint32_t flags; char str[18]; if (len < sizeof(*ev)) { printf("* Malformed Device Connected control\n"); return; } flags = le32_to_cpu(ev->flags); ba2str(&ev->addr.bdaddr, str); printf("@ Device Connected: %s (%d) flags 0x%4.4x\n", str, ev->addr.type, flags); buf += sizeof(*ev); len -= sizeof(*ev); packet_hexdump(buf, len); } static void mgmt_device_disconnected(uint16_t len, const void *buf) { const struct mgmt_ev_device_disconnected *ev = buf; char str[18]; uint8_t reason; uint16_t consumed_len; if (len < sizeof(struct mgmt_addr_info)) { printf("* Malformed Device Disconnected control\n"); return; } if (len < sizeof(*ev)) { reason = MGMT_DEV_DISCONN_UNKNOWN; consumed_len = len; } else { reason = ev->reason; consumed_len = sizeof(*ev); } ba2str(&ev->addr.bdaddr, str); printf("@ Device Disconnected: %s (%d) reason %u\n", str, ev->addr.type, reason); buf += consumed_len; len -= consumed_len; packet_hexdump(buf, len); } static void mgmt_connect_failed(uint16_t len, const void *buf) { const struct mgmt_ev_connect_failed *ev = buf; char str[18]; if (len < sizeof(*ev)) { printf("* Malformed Connect Failed control\n"); return; } ba2str(&ev->addr.bdaddr, str); printf("@ Connect Failed: %s (%d) status 0x%2.2x\n", str, ev->addr.type, ev->status); buf += sizeof(*ev); len -= sizeof(*ev); packet_hexdump(buf, len); } static void mgmt_pin_code_request(uint16_t len, const void *buf) { const struct mgmt_ev_pin_code_request *ev = buf; char str[18]; if (len < sizeof(*ev)) { printf("* Malformed PIN Code Request control\n"); return; } ba2str(&ev->addr.bdaddr, str); printf("@ PIN Code Request: %s (%d) secure 0x%2.2x\n", str, ev->addr.type, ev->secure); buf += sizeof(*ev); len -= sizeof(*ev); packet_hexdump(buf, len); } static void mgmt_user_confirm_request(uint16_t len, const void *buf) { const struct mgmt_ev_user_confirm_request *ev = buf; char str[18]; if (len < sizeof(*ev)) { printf("* Malformed User Confirmation Request control\n"); return; } ba2str(&ev->addr.bdaddr, str); printf("@ User Confirmation Request: %s (%d) hint %d value %d\n", str, ev->addr.type, ev->confirm_hint, ev->value); buf += sizeof(*ev); len -= sizeof(*ev); packet_hexdump(buf, len); } static void mgmt_user_passkey_request(uint16_t len, const void *buf) { const struct mgmt_ev_user_passkey_request *ev = buf; char str[18]; if (len < sizeof(*ev)) { printf("* Malformed User Passkey Request control\n"); return; } ba2str(&ev->addr.bdaddr, str); printf("@ User Passkey Request: %s (%d)\n", str, ev->addr.type); buf += sizeof(*ev); len -= sizeof(*ev); packet_hexdump(buf, len); } static void mgmt_auth_failed(uint16_t len, const void *buf) { const struct mgmt_ev_auth_failed *ev = buf; char str[18]; if (len < sizeof(*ev)) { printf("* Malformed Authentication Failed control\n"); return; } ba2str(&ev->addr.bdaddr, str); printf("@ Authentication Failed: %s (%d) status 0x%2.2x\n", str, ev->addr.type, ev->status); buf += sizeof(*ev); len -= sizeof(*ev); packet_hexdump(buf, len); } static void mgmt_device_found(uint16_t len, const void *buf) { const struct mgmt_ev_device_found *ev = buf; uint32_t flags; char str[18]; if (len < sizeof(*ev)) { printf("* Malformed Device Found control\n"); return; } flags = le32_to_cpu(ev->flags); ba2str(&ev->addr.bdaddr, str); printf("@ Device Found: %s (%d) rssi %d flags 0x%4.4x\n", str, ev->addr.type, ev->rssi, flags); buf += sizeof(*ev); len -= sizeof(*ev); packet_hexdump(buf, len); } static void mgmt_discovering(uint16_t len, const void *buf) { const struct mgmt_ev_discovering *ev = buf; if (len < sizeof(*ev)) { printf("* Malformed Discovering control\n"); return; } printf("@ Discovering: 0x%2.2x (%d)\n", ev->discovering, ev->type); buf += sizeof(*ev); len -= sizeof(*ev); packet_hexdump(buf, len); } static void mgmt_device_blocked(uint16_t len, const void *buf) { const struct mgmt_ev_device_blocked *ev = buf; char str[18]; if (len < sizeof(*ev)) { printf("* Malformed Device Blocked control\n"); return; } ba2str(&ev->addr.bdaddr, str); printf("@ Device Blocked: %s (%d)\n", str, ev->addr.type); buf += sizeof(*ev); len -= sizeof(*ev); packet_hexdump(buf, len); } static void mgmt_device_unblocked(uint16_t len, const void *buf) { const struct mgmt_ev_device_unblocked *ev = buf; char str[18]; if (len < sizeof(*ev)) { printf("* Malformed Device Unblocked control\n"); return; } ba2str(&ev->addr.bdaddr, str); printf("@ Device Unblocked: %s (%d)\n", str, ev->addr.type); buf += sizeof(*ev); len -= sizeof(*ev); packet_hexdump(buf, len); } static void mgmt_device_unpaired(uint16_t len, const void *buf) { const struct mgmt_ev_device_unpaired *ev = buf; char str[18]; if (len < sizeof(*ev)) { printf("* Malformed Device Unpaired control\n"); return; } ba2str(&ev->addr.bdaddr, str); printf("@ Device Unpaired: %s (%d)\n", str, ev->addr.type); buf += sizeof(*ev); len -= sizeof(*ev); packet_hexdump(buf, len); } static void mgmt_passkey_notify(uint16_t len, const void *buf) { const struct mgmt_ev_passkey_notify *ev = buf; uint32_t passkey; char str[18]; if (len < sizeof(*ev)) { printf("* Malformed Passkey Notify control\n"); return; } ba2str(&ev->addr.bdaddr, str); passkey = le32_to_cpu(ev->passkey); printf("@ Passkey Notify: %s (%d) passkey %06u entered %u\n", str, ev->addr.type, passkey, ev->entered); buf += sizeof(*ev); len -= sizeof(*ev); packet_hexdump(buf, len); } static void mgmt_new_irk(uint16_t len, const void *buf) { const struct mgmt_ev_new_irk *ev = buf; char addr[18], rpa[18]; if (len < sizeof(*ev)) { printf("* Malformed New IRK control\n"); return; } ba2str(&ev->rpa, rpa); ba2str(&ev->key.addr.bdaddr, addr); printf("@ New IRK: %s (%d) %s\n", addr, ev->key.addr.type, rpa); buf += sizeof(*ev); len -= sizeof(*ev); packet_hexdump(buf, len); } static void mgmt_new_csrk(uint16_t len, const void *buf) { const struct mgmt_ev_new_csrk *ev = buf; const char *type; char addr[18]; if (len < sizeof(*ev)) { printf("* Malformed New CSRK control\n"); return; } ba2str(&ev->key.addr.bdaddr, addr); switch (ev->key.type) { case 0x00: type = "Local Unauthenticated"; break; case 0x01: type = "Remote Unauthenticated"; break; case 0x02: type = "Local Authenticated"; break; case 0x03: type = "Remote Authenticated"; break; default: type = ""; break; } printf("@ New CSRK: %s (%d) %s (%u)\n", addr, ev->key.addr.type, type, ev->key.type); buf += sizeof(*ev); len -= sizeof(*ev); packet_hexdump(buf, len); } static void mgmt_device_added(uint16_t len, const void *buf) { const struct mgmt_ev_device_added *ev = buf; char str[18]; if (len < sizeof(*ev)) { printf("* Malformed Device Added control\n"); return; } ba2str(&ev->addr.bdaddr, str); printf("@ Device Added: %s (%d) %d\n", str, ev->addr.type, ev->action); buf += sizeof(*ev); len -= sizeof(*ev); packet_hexdump(buf, len); } static void mgmt_device_removed(uint16_t len, const void *buf) { const struct mgmt_ev_device_removed *ev = buf; char str[18]; if (len < sizeof(*ev)) { printf("* Malformed Device Removed control\n"); return; } ba2str(&ev->addr.bdaddr, str); printf("@ Device Removed: %s (%d)\n", str, ev->addr.type); buf += sizeof(*ev); len -= sizeof(*ev); packet_hexdump(buf, len); } static void mgmt_new_conn_param(uint16_t len, const void *buf) { const struct mgmt_ev_new_conn_param *ev = buf; char addr[18]; uint16_t min, max, latency, timeout; if (len < sizeof(*ev)) { printf("* Malformed New Connection Parameter control\n"); return; } ba2str(&ev->addr.bdaddr, addr); min = le16_to_cpu(ev->min_interval); max = le16_to_cpu(ev->max_interval); latency = le16_to_cpu(ev->latency); timeout = le16_to_cpu(ev->timeout); printf("@ New Conn Param: %s (%d) hint %d min 0x%4.4x max 0x%4.4x " "latency 0x%4.4x timeout 0x%4.4x\n", addr, ev->addr.type, ev->store_hint, min, max, latency, timeout); buf += sizeof(*ev); len -= sizeof(*ev); packet_hexdump(buf, len); } static void mgmt_advertising_added(uint16_t len, const void *buf) { const struct mgmt_ev_advertising_added *ev = buf; if (len < sizeof(*ev)) { printf("* Malformed Advertising Added control\n"); return; } printf("@ Advertising Added: %u\n", ev->instance); buf += sizeof(*ev); len -= sizeof(*ev); packet_hexdump(buf, len); } static void mgmt_advertising_removed(uint16_t len, const void *buf) { const struct mgmt_ev_advertising_removed *ev = buf; if (len < sizeof(*ev)) { printf("* Malformed Advertising Removed control\n"); return; } printf("@ Advertising Removed: %u\n", ev->instance); buf += sizeof(*ev); len -= sizeof(*ev); packet_hexdump(buf, len); } void control_message(uint16_t opcode, const void *data, uint16_t size) { if (!decode_control) return; switch (opcode) { case MGMT_EV_INDEX_ADDED: mgmt_index_added(size, data); break; case MGMT_EV_INDEX_REMOVED: mgmt_index_removed(size, data); break; case MGMT_EV_CONTROLLER_ERROR: mgmt_controller_error(size, data); break; case MGMT_EV_NEW_SETTINGS: mgmt_new_settings(size, data); break; case MGMT_EV_CLASS_OF_DEV_CHANGED: mgmt_class_of_dev_changed(size, data); break; case MGMT_EV_LOCAL_NAME_CHANGED: mgmt_local_name_changed(size, data); break; case MGMT_EV_NEW_LINK_KEY: mgmt_new_link_key(size, data); break; case MGMT_EV_NEW_LONG_TERM_KEY: mgmt_new_long_term_key(size, data); break; case MGMT_EV_DEVICE_CONNECTED: mgmt_device_connected(size, data); break; case MGMT_EV_DEVICE_DISCONNECTED: mgmt_device_disconnected(size, data); break; case MGMT_EV_CONNECT_FAILED: mgmt_connect_failed(size, data); break; case MGMT_EV_PIN_CODE_REQUEST: mgmt_pin_code_request(size, data); break; case MGMT_EV_USER_CONFIRM_REQUEST: mgmt_user_confirm_request(size, data); break; case MGMT_EV_USER_PASSKEY_REQUEST: mgmt_user_passkey_request(size, data); break; case MGMT_EV_AUTH_FAILED: mgmt_auth_failed(size, data); break; case MGMT_EV_DEVICE_FOUND: mgmt_device_found(size, data); break; case MGMT_EV_DISCOVERING: mgmt_discovering(size, data); break; case MGMT_EV_DEVICE_BLOCKED: mgmt_device_blocked(size, data); break; case MGMT_EV_DEVICE_UNBLOCKED: mgmt_device_unblocked(size, data); break; case MGMT_EV_DEVICE_UNPAIRED: mgmt_device_unpaired(size, data); break; case MGMT_EV_PASSKEY_NOTIFY: mgmt_passkey_notify(size, data); break; case MGMT_EV_NEW_IRK: mgmt_new_irk(size, data); break; case MGMT_EV_NEW_CSRK: mgmt_new_csrk(size, data); break; case MGMT_EV_DEVICE_ADDED: mgmt_device_added(size, data); break; case MGMT_EV_DEVICE_REMOVED: mgmt_device_removed(size, data); break; case MGMT_EV_NEW_CONN_PARAM: mgmt_new_conn_param(size, data); break; case MGMT_EV_UNCONF_INDEX_ADDED: mgmt_unconf_index_added(size, data); break; case MGMT_EV_UNCONF_INDEX_REMOVED: mgmt_unconf_index_removed(size, data); break; case MGMT_EV_NEW_CONFIG_OPTIONS: mgmt_new_config_options(size, data); break; case MGMT_EV_EXT_INDEX_ADDED: mgmt_ext_index_added(size, data); break; case MGMT_EV_EXT_INDEX_REMOVED: mgmt_ext_index_removed(size, data); break; case MGMT_EV_ADVERTISING_ADDED: mgmt_advertising_added(size, data); break; case MGMT_EV_ADVERTISING_REMOVED: mgmt_advertising_removed(size, data); break; default: printf("* Unknown control (code %d len %d)\n", opcode, size); packet_hexdump(data, size); break; } } static void data_callback(int fd, uint32_t events, void *user_data) { struct control_data *data = user_data; unsigned char control[64]; struct mgmt_hdr hdr; struct msghdr msg; struct iovec iov[2]; if (events & (EPOLLERR | EPOLLHUP)) { mainloop_remove_fd(data->fd); return; } iov[0].iov_base = &hdr; iov[0].iov_len = MGMT_HDR_SIZE; iov[1].iov_base = data->buf; iov[1].iov_len = sizeof(data->buf); memset(&msg, 0, sizeof(msg)); msg.msg_iov = iov; msg.msg_iovlen = 2; msg.msg_control = control; msg.msg_controllen = sizeof(control); while (1) { struct cmsghdr *cmsg; struct timeval *tv = NULL; struct timeval ctv; struct ucred *cred = NULL; struct ucred ccred; uint16_t opcode, index, pktlen; ssize_t len; len = recvmsg(data->fd, &msg, MSG_DONTWAIT); if (len < 0) break; if (len < MGMT_HDR_SIZE) break; for (cmsg = CMSG_FIRSTHDR(&msg); cmsg != NULL; cmsg = CMSG_NXTHDR(&msg, cmsg)) { if (cmsg->cmsg_level != SOL_SOCKET) continue; if (cmsg->cmsg_type == SCM_TIMESTAMP) { memcpy(&ctv, CMSG_DATA(cmsg), sizeof(ctv)); tv = &ctv; } if (cmsg->cmsg_type == SCM_CREDENTIALS) { memcpy(&ccred, CMSG_DATA(cmsg), sizeof(ccred)); cred = &ccred; } } opcode = le16_to_cpu(hdr.opcode); index = le16_to_cpu(hdr.index); pktlen = le16_to_cpu(hdr.len); switch (data->channel) { case HCI_CHANNEL_CONTROL: packet_control(tv, cred, index, opcode, data->buf, pktlen); break; case HCI_CHANNEL_MONITOR: btsnoop_write_hci(btsnoop_file, tv, index, opcode, 0, data->buf, pktlen); ellisys_inject_hci(tv, index, opcode, data->buf, pktlen); packet_monitor(tv, cred, index, opcode, data->buf, pktlen); break; } } } static int open_socket(uint16_t channel) { struct sockaddr_hci addr; int fd, opt = 1; fd = socket(AF_BLUETOOTH, SOCK_RAW | SOCK_CLOEXEC, BTPROTO_HCI); if (fd < 0) { perror("Failed to open channel"); return -1; } memset(&addr, 0, sizeof(addr)); addr.hci_family = AF_BLUETOOTH; addr.hci_dev = HCI_DEV_NONE; addr.hci_channel = channel; if (bind(fd, (struct sockaddr *) &addr, sizeof(addr)) < 0) { if (errno == EINVAL) { /* Fallback to hcidump support */ hcidump_fallback = true; close(fd); return -1; } perror("Failed to bind channel"); close(fd); return -1; } if (setsockopt(fd, SOL_SOCKET, SO_TIMESTAMP, &opt, sizeof(opt)) < 0) { perror("Failed to enable timestamps"); close(fd); return -1; } if (setsockopt(fd, SOL_SOCKET, SO_PASSCRED, &opt, sizeof(opt)) < 0) { perror("Failed to enable credentials"); close(fd); return -1; } return fd; } static void attach_index_filter(int fd, uint16_t index) { struct sock_filter filters[] = { /* Load MGMT index: * A <- MGMT index */ BPF_STMT(BPF_LD + BPF_B + BPF_ABS, offsetof(struct mgmt_hdr, index)), /* Accept if index is HCI_DEV_NONE: * A == HCI_DEV_NONE */ BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, HCI_DEV_NONE, 0, 1), /* return */ BPF_STMT(BPF_RET|BPF_K, 0x0fffffff), /* pass */ /* Accept if index match: * A == index */ BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, index, 0, 1), /* returns */ BPF_STMT(BPF_RET|BPF_K, 0x0fffffff), /* pass */ BPF_STMT(BPF_RET|BPF_K, 0), /* reject */ }; struct sock_fprog fprog = { .len = sizeof(filters) / sizeof(filters[0]), /* casting const away: */ .filter = filters, }; setsockopt(fd, SOL_SOCKET, SO_ATTACH_FILTER, &fprog, sizeof(fprog)); } static int open_channel(uint16_t channel) { struct control_data *data; data = malloc(sizeof(*data)); if (!data) return -1; memset(data, 0, sizeof(*data)); data->channel = channel; data->fd = open_socket(channel); if (data->fd < 0) { free(data); return -1; } if (filter_index != HCI_DEV_NONE) attach_index_filter(data->fd, filter_index); if (mainloop_add_fd(data->fd, EPOLLIN, data_callback, data, free_data) < 0) { close(data->fd); free(data); return -1; }; return 0; } static void client_callback(int fd, uint32_t events, void *user_data) { struct control_data *data = user_data; ssize_t len; if (events & (EPOLLERR | EPOLLHUP)) { mainloop_remove_fd(data->fd); return; } len = recv(data->fd, data->buf + data->offset, sizeof(data->buf) - data->offset, MSG_DONTWAIT); if (len < 0) return; data->offset += len; while (data->offset >= MGMT_HDR_SIZE) { struct mgmt_hdr *hdr = (struct mgmt_hdr *) data->buf; uint16_t pktlen = le16_to_cpu(hdr->len); uint16_t opcode, index; if (data->offset < pktlen + MGMT_HDR_SIZE) return; opcode = le16_to_cpu(hdr->opcode); index = le16_to_cpu(hdr->index); packet_monitor(NULL, NULL, index, opcode, data->buf + MGMT_HDR_SIZE, pktlen); data->offset -= pktlen + MGMT_HDR_SIZE; if (data->offset > 0) memmove(data->buf, data->buf + MGMT_HDR_SIZE + pktlen, data->offset); } } static void server_accept_callback(int fd, uint32_t events, void *user_data) { struct control_data *data; struct sockaddr_un addr; socklen_t len; int nfd; if (events & (EPOLLERR | EPOLLHUP)) { mainloop_remove_fd(fd); return; } memset(&addr, 0, sizeof(addr)); len = sizeof(addr); nfd = accept(fd, (struct sockaddr *) &addr, &len); if (nfd < 0) { perror("Failed to accept client socket"); return; } printf("--- New monitor connection ---\n"); data = malloc(sizeof(*data)); if (!data) { close(nfd); return; } memset(data, 0, sizeof(*data)); data->channel = HCI_CHANNEL_MONITOR; data->fd = nfd; if (mainloop_add_fd(data->fd, EPOLLIN, client_callback, data, free_data) < 0) { close(data->fd); free(data); } } static int server_fd = -1; void control_server(const char *path) { struct sockaddr_un addr; int fd; if (server_fd >= 0) return; if (strlen(path) > sizeof(addr.sun_path) - 1) { fprintf(stderr, "Socket name too long\n"); return; } unlink(path); fd = socket(PF_UNIX, SOCK_STREAM | SOCK_CLOEXEC, 0); if (fd < 0) { perror("Failed to open server socket"); return; } memset(&addr, 0, sizeof(addr)); addr.sun_family = AF_UNIX; strcpy(addr.sun_path, path); if (bind(fd, (struct sockaddr *) &addr, sizeof(addr)) < 0) { perror("Failed to bind server socket"); close(fd); return; } if (listen(fd, 5) < 0) { perror("Failed to listen server socket"); close(fd); return; } if (mainloop_add_fd(fd, EPOLLIN, server_accept_callback, NULL, NULL) < 0) { close(fd); return; } server_fd = fd; } static bool parse_drops(uint8_t **data, uint8_t *len, uint8_t *drops, uint32_t *total) { if (*len < 1) return false; *drops = **data; *total += *drops; (*data)++; (*len)--; return true; } static bool tty_parse_header(uint8_t *hdr, uint8_t len, struct timeval **tv, struct timeval *ctv, uint32_t *drops) { uint8_t cmd = 0; uint8_t evt = 0; uint8_t acl_tx = 0; uint8_t acl_rx = 0; uint8_t sco_tx = 0; uint8_t sco_rx = 0; uint8_t other = 0; uint32_t total = 0; uint32_t ts32; while (len) { uint8_t type = hdr[0]; hdr++; len--; switch (type) { case TTY_EXTHDR_COMMAND_DROPS: if (!parse_drops(&hdr, &len, &cmd, &total)) return false; break; case TTY_EXTHDR_EVENT_DROPS: if (!parse_drops(&hdr, &len, &evt, &total)) return false; break; case TTY_EXTHDR_ACL_TX_DROPS: if (!parse_drops(&hdr, &len, &acl_tx, &total)) return false; break; case TTY_EXTHDR_ACL_RX_DROPS: if (!parse_drops(&hdr, &len, &acl_rx, &total)) return false; break; case TTY_EXTHDR_SCO_TX_DROPS: if (!parse_drops(&hdr, &len, &sco_tx, &total)) return false; break; case TTY_EXTHDR_SCO_RX_DROPS: if (!parse_drops(&hdr, &len, &sco_rx, &total)) return false; break; case TTY_EXTHDR_OTHER_DROPS: if (!parse_drops(&hdr, &len, &other, &total)) return false; break; case TTY_EXTHDR_TS32: if (len < sizeof(ts32)) return false; ts32 = get_le32(hdr); hdr += sizeof(ts32); len -= sizeof(ts32); /* ts32 is in units of 1/10th of a millisecond */ ctv->tv_sec = ts32 / 10000; ctv->tv_usec = (ts32 % 10000) * 100; *tv = ctv; break; default: printf("Unknown extended header type %u\n", type); return false; } } if (total) { *drops += total; printf("* Drops: cmd %u evt %u acl_tx %u acl_rx %u sco_tx %u " "sco_rx %u other %u\n", cmd, evt, acl_tx, acl_rx, sco_tx, sco_rx, other); } return true; } static void process_data(struct control_data *data) { while (data->offset >= sizeof(struct tty_hdr)) { struct tty_hdr *hdr = (struct tty_hdr *) data->buf; uint16_t pktlen, opcode, data_len; struct timeval *tv = NULL; struct timeval ctv; uint32_t drops = 0; data_len = le16_to_cpu(hdr->data_len); if (data->offset < 2 + data_len) return; if (data->offset < sizeof(*hdr) + hdr->hdr_len) { fprintf(stderr, "Received corrupted data from TTY\n"); memmove(data->buf, data->buf + 2 + data_len, data->offset); return; } if (!tty_parse_header(hdr->ext_hdr, hdr->hdr_len, &tv, &ctv, &drops)) fprintf(stderr, "Unable to parse extended header\n"); opcode = le16_to_cpu(hdr->opcode); pktlen = data_len - 4 - hdr->hdr_len; btsnoop_write_hci(btsnoop_file, tv, 0, opcode, drops, hdr->ext_hdr + hdr->hdr_len, pktlen); ellisys_inject_hci(tv, 0, opcode, hdr->ext_hdr + hdr->hdr_len, pktlen); packet_monitor(tv, NULL, 0, opcode, hdr->ext_hdr + hdr->hdr_len, pktlen); data->offset -= 2 + data_len; if (data->offset > 0) memmove(data->buf, data->buf + 2 + data_len, data->offset); } } static void tty_callback(int fd, uint32_t events, void *user_data) { struct control_data *data = user_data; ssize_t len; if (events & (EPOLLERR | EPOLLHUP)) { mainloop_remove_fd(data->fd); return; } len = read(data->fd, data->buf + data->offset, sizeof(data->buf) - data->offset); if (len < 0) return; data->offset += len; process_data(data); } int control_tty(const char *path, unsigned int speed) { struct control_data *data; struct termios ti; int fd, err; fd = open(path, O_RDWR | O_NOCTTY | O_NONBLOCK); if (fd < 0) { err = -errno; perror("Failed to open serial port"); return err; } if (tcflush(fd, TCIOFLUSH) < 0) { err = -errno; perror("Failed to flush serial port"); close(fd); return err; } memset(&ti, 0, sizeof(ti)); /* Switch TTY to raw mode */ cfmakeraw(&ti); ti.c_cflag |= (CLOCAL | CREAD); ti.c_cflag &= ~CRTSCTS; cfsetspeed(&ti, speed); if (tcsetattr(fd, TCSANOW, &ti) < 0) { err = -errno; perror("Failed to set serial port settings"); close(fd); return err; } printf("--- %s opened ---\n", path); data = malloc(sizeof(*data)); if (!data) { close(fd); return -ENOMEM; } memset(data, 0, sizeof(*data)); data->channel = HCI_CHANNEL_MONITOR; data->fd = fd; if (mainloop_add_fd(data->fd, EPOLLIN, tty_callback, data, free_data) < 0) { close(data->fd); free(data); return -1; } return 0; } static void rtt_callback(int id, void *user_data) { struct control_data *data = user_data; ssize_t len; do { len = jlink_rtt_read(data->buf + data->offset, sizeof(data->buf) - data->offset); data->offset += len; process_data(data); } while (len > 0); if (mainloop_modify_timeout(id, 1) < 0) mainloop_exit_failure(); } int control_rtt(char *jlink, char *rtt) { struct control_data *data; if (jlink_init() < 0) { fprintf(stderr, "Failed to initialize J-Link library\n"); return -EIO; } if (jlink_connect(jlink) < 0) { fprintf(stderr, "Failed to connect to target device\n"); return -ENODEV; } if (jlink_start_rtt(rtt) < 0) { fprintf(stderr, "Failed to initialize RTT\n"); return -ENODEV; } printf("--- RTT opened ---\n"); data = new0(struct control_data, 1); data->channel = HCI_CHANNEL_MONITOR; data->fd = -1; if (mainloop_add_timeout(1, rtt_callback, data, free_data) < 0) { free(data); return -EIO; } return 0; } bool control_writer(const char *path) { btsnoop_file = btsnoop_create(path, 0, 0, BTSNOOP_FORMAT_MONITOR); return !!btsnoop_file; } void control_reader(const char *path, bool pager) { unsigned char buf[BTSNOOP_MAX_PACKET_SIZE]; uint16_t pktlen; uint32_t format; struct timeval tv; btsnoop_file = btsnoop_open(path, BTSNOOP_FLAG_PKLG_SUPPORT); if (!btsnoop_file) return; format = btsnoop_get_format(btsnoop_file); switch (format) { case BTSNOOP_FORMAT_HCI: case BTSNOOP_FORMAT_UART: case BTSNOOP_FORMAT_SIMULATOR: packet_del_filter(PACKET_FILTER_SHOW_INDEX); break; case BTSNOOP_FORMAT_MONITOR: packet_add_filter(PACKET_FILTER_SHOW_INDEX); break; } if (pager) open_pager(); switch (format) { case BTSNOOP_FORMAT_HCI: case BTSNOOP_FORMAT_UART: case BTSNOOP_FORMAT_MONITOR: while (1) { uint16_t index, opcode; if (!btsnoop_read_hci(btsnoop_file, &tv, &index, &opcode, buf, &pktlen)) break; if (opcode == 0xffff) continue; packet_monitor(&tv, NULL, index, opcode, buf, pktlen); ellisys_inject_hci(&tv, index, opcode, buf, pktlen); } break; case BTSNOOP_FORMAT_SIMULATOR: while (1) { uint16_t frequency; if (!btsnoop_read_phy(btsnoop_file, &tv, &frequency, buf, &pktlen)) break; packet_simulator(&tv, frequency, buf, pktlen); } break; } if (pager) close_pager(); btsnoop_unref(btsnoop_file); } int control_tracing(void) { packet_add_filter(PACKET_FILTER_SHOW_INDEX); if (server_fd >= 0) return 0; if (open_channel(HCI_CHANNEL_MONITOR) < 0) { if (!hcidump_fallback) return -1; if (hcidump_tracing() < 0) return -1; return 0; } if (packet_has_filter(PACKET_FILTER_SHOW_MGMT_SOCKET)) open_channel(HCI_CHANNEL_CONTROL); return 0; } void control_disable_decoding(void) { decode_control = false; } void control_filter_index(uint16_t index) { filter_index = index; } bluez-5.82/monitor/PaxHeaders/sdp.c0000644000000000000000000000005014572354773014310 xustar0020 atime=1743516028 20 ctime=1743591282 bluez-5.82/monitor/sdp.c0000644000000000000000000004262314572354773014000 0ustar00rootroot// SPDX-License-Identifier: LGPL-2.1-or-later /* * * BlueZ - Bluetooth protocol stack for Linux * * Copyright (C) 2011-2014 Intel Corporation * Copyright (C) 2002-2010 Marcel Holtmann * * */ #ifdef HAVE_CONFIG_H #include #endif #define _GNU_SOURCE #include #include #include #include #include "lib/bluetooth.h" #include "lib/uuid.h" #include "src/shared/util.h" #include "bt.h" #include "packet.h" #include "display.h" #include "l2cap.h" #include "sdp.h" #define MAX_TID 16 #define MAX_CONT_SIZE 17 struct tid_data { bool inuse; uint16_t tid; uint16_t channel; uint8_t cont[MAX_CONT_SIZE]; }; static struct tid_data tid_list[MAX_TID]; static struct tid_data *get_tid(uint16_t tid, uint16_t channel) { int i, n = -1; for (i = 0; i < MAX_TID; i++) { if (!tid_list[i].inuse) { if (n < 0) n = i; continue; } if (tid_list[i].tid == tid && tid_list[i].channel == channel) return &tid_list[i]; } if (n < 0) return NULL; tid_list[n].inuse = true; tid_list[n].tid = tid; tid_list[n].channel = channel; return &tid_list[n]; } static void clear_tid(struct tid_data *tid) { if (tid) tid->inuse = false; } static void print_uint(uint8_t indent, const uint8_t *data, uint32_t size) { switch (size) { case 1: print_field("%*c0x%2.2x", indent, ' ', data[0]); break; case 2: print_field("%*c0x%4.4x", indent, ' ', get_be16(data)); break; case 4: print_field("%*c0x%8.8x", indent, ' ', get_be32(data)); break; case 8: print_field("%*c0x%16.16" PRIx64, indent, ' ', get_be64(data)); break; default: packet_hexdump(data, size); break; } } static void print_sint(uint8_t indent, const uint8_t *data, uint32_t size) { packet_hexdump(data, size); } static void print_uuid(uint8_t indent, const uint8_t *data, uint32_t size) { switch (size) { case 2: print_field("%*c%s (0x%4.4x)", indent, ' ', bt_uuid16_to_str(get_be16(data)), get_be16(data)); break; case 4: print_field("%*c%s (0x%8.8x)", indent, ' ', bt_uuid32_to_str(get_be32(data)), get_be32(data)); break; case 16: /* BASE_UUID = 00000000-0000-1000-8000-00805F9B34FB */ print_field("%*c%8.8x-%4.4x-%4.4x-%4.4x-%4.4x%8.4x", indent, ' ', get_be32(data), get_be16(data + 4), get_be16(data + 6), get_be16(data + 8), get_be16(data + 10), get_be32(data + 12)); if (get_be16(data + 4) == 0x0000 && get_be16(data + 6) == 0x1000 && get_be16(data + 8) == 0x8000 && get_be16(data + 10) == 0x0080 && get_be32(data + 12) == 0x5F9B34FB) print_field("%*c%s", indent, ' ', bt_uuid32_to_str(get_be32(data))); break; default: packet_hexdump(data, size); break; } } static void print_string(uint8_t indent, const uint8_t *data, uint32_t size) { char *str = alloca(size + 1); str[size] = '\0'; strncpy(str, (const char *) data, size); print_field("%*c%s [len %d]", indent, ' ', str, size); } static void print_boolean(uint8_t indent, const uint8_t *data, uint32_t size) { print_field("%*c%s", indent, ' ', data[0] ? "true" : "false"); } #define SIZES(args...) ((uint8_t[]) { args, 0xff } ) static const struct { uint8_t value; const uint8_t *sizes; bool recurse; const char *str; void (*print) (uint8_t indent, const uint8_t *data, uint32_t size); } type_table[] = { { 0, SIZES(0), false, "Nil" }, { 1, SIZES(0, 1, 2, 3, 4), false, "Unsigned Integer", print_uint }, { 2, SIZES(0, 1, 2, 3, 4), false, "Signed Integer", print_sint }, { 3, SIZES(1, 2, 4), false, "UUID", print_uuid }, { 4, SIZES(5, 6, 7), false, "String", print_string }, { 5, SIZES(0), false, "Boolean", print_boolean }, { 6, SIZES(5, 6, 7), true, "Sequence" }, { 7, SIZES(5, 6, 7), true, "Alternative" }, { 8, SIZES(5, 6, 7), false, "URL", print_string }, { } }; static const struct { uint8_t index; uint8_t bits; uint8_t size; const char *str; } size_table[] = { { 0, 0, 1, "1 byte" }, { 1, 0, 2, "2 bytes" }, { 2, 0, 4, "4 bytes" }, { 3, 0, 8, "8 bytes" }, { 4, 0, 16, "16 bytes" }, { 5, 8, 0, "8 bits" }, { 6, 16, 0, "16 bits" }, { 7, 32, 0, "32 bits" }, { } }; static bool valid_size(uint8_t size, const uint8_t *sizes) { int i; for (i = 0; sizes[i] != 0xff; i++) { if (sizes[i] == size) return true; } return false; } static uint8_t get_bits(const uint8_t *data, uint32_t size) { int i; for (i = 0; size_table[i].str; i++) { if (size_table[i].index == (data[0] & 0x07)) return size_table[i].bits; } return 0; } static uint32_t get_size(const uint8_t *data, uint32_t size) { int i; for (i = 0; size_table[i].str; i++) { if (size_table[i].index == (data[0] & 0x07)) { switch (size_table[i].bits) { case 0: if ((data[0] & 0xf8) == 0) return 0; else return size_table[i].size; case 8: return data[1]; case 16: return get_be16(data + 1); case 32: return get_be32(data + 1); default: return 0; } } } return 0; } static void decode_data_elements(uint32_t position, uint8_t indent, const uint8_t *data, uint32_t size, void (*print_func) (uint32_t, uint8_t, uint8_t, const uint8_t *, uint32_t)) { uint32_t datalen, elemlen, extrabits; int i; if (!size) return; extrabits = get_bits(data, size); if (size < 1 + (extrabits / 8)) { print_text(COLOR_ERROR, "data element descriptor too short"); packet_hexdump(data, size); return; } datalen = get_size(data, size); if (size < 1 + (extrabits / 8) + datalen) { print_text(COLOR_ERROR, "data element size too short"); packet_hexdump(data, size); return; } elemlen = 1 + (extrabits / 8) + datalen; for (i = 0; type_table[i].str; i++) { uint8_t type = (data[0] & 0xf8) >> 3; if (type_table[i].value != type) continue; if (print_func) { print_func(position, indent, type, data + 1 + (extrabits / 8), datalen); break; } print_field("%*c%s (%d) with %u byte%s [%u extra bits] len %u", indent, ' ', type_table[i].str, type, datalen, datalen == 1 ? "" : "s", extrabits, elemlen); if (!valid_size(data[0] & 0x07, type_table[i].sizes)) { print_text(COLOR_ERROR, "invalid data element size"); packet_hexdump(data + 1 + (extrabits / 8), datalen); break; } if (type_table[i].recurse) decode_data_elements(0, indent + 2, data + 1 + (extrabits / 8), datalen, print_func); else if (type_table[i].print) type_table[i].print(indent + 2, data + 1 + (extrabits / 8), datalen); break; } if (elemlen > size) { print_text(COLOR_ERROR, "invalid data element size"); return; } data += elemlen; size -= elemlen; decode_data_elements(position + 1, indent, data, size, print_func); } static uint32_t get_bytes(const uint8_t *data, uint32_t size) { switch (data[0] & 0x07) { case 5: return 2 + data[1]; case 6: return 3 + get_be16(data + 1); case 7: return 5 + get_be32(data + 1); } return 0; } static const struct { uint16_t id; const char *str; } attribute_table[] = { { 0x0000, "Service Record Handle" }, { 0x0001, "Service Class ID List" }, { 0x0002, "Service Record State" }, { 0x0003, "Service ID" }, { 0x0004, "Protocol Descriptor List" }, { 0x0005, "Browse Group List" }, { 0x0006, "Language Base Attribute ID List" }, { 0x0007, "Service Info Time To Live" }, { 0x0008, "Service Availability" }, { 0x0009, "Bluetooth Profile Descriptor List" }, { 0x000a, "Documentation URL" }, { 0x000b, "Client Executable URL" }, { 0x000c, "Icon URL" }, { 0x000d, "Additional Protocol Descriptor List" }, { } }; static void print_attr(uint32_t position, uint8_t indent, uint8_t type, const uint8_t *data, uint32_t size) { int i; if ((position % 2) == 0) { uint16_t id = get_be16(data); const char *str = "Unknown"; for (i = 0; attribute_table[i].str; i++) { if (attribute_table[i].id == id) str = attribute_table[i].str; } print_field("%*cAttribute: %s (0x%4.4x) [len %d]", indent, ' ', str, id, size); return; } for (i = 0; type_table[i].str; i++) { if (type_table[i].value != type) continue; if (type_table[i].recurse) decode_data_elements(0, indent + 2, data, size, NULL); else if (type_table[i].print) type_table[i].print(indent + 2, data, size); break; } } static void print_attr_list(uint32_t position, uint8_t indent, uint8_t type, const uint8_t *data, uint32_t size) { print_field("%*cAttribute list: [len %d] {position %d}", indent, ' ', size, position); decode_data_elements(0, indent + 2, data, size, print_attr); } static void print_attr_lists(uint32_t position, uint8_t indent, uint8_t type, const uint8_t *data, uint32_t size) { decode_data_elements(0, indent, data, size, print_attr_list); } static void print_continuation(const uint8_t *data, uint16_t size) { if (data[0] != size - 1) { print_text(COLOR_ERROR, "invalid continuation state"); packet_hexdump(data, size); return; } print_field("Continuation state: %d", data[0]); packet_hexdump(data + 1, size - 1); } static void store_continuation(struct tid_data *tid, const uint8_t *data, uint16_t size) { if (size > MAX_CONT_SIZE) { print_text(COLOR_ERROR, "invalid continuation size"); return; } memcpy(tid->cont, data, size); print_continuation(data, size); } #define MAX_CONT 8 struct cont_data { uint16_t channel; uint8_t cont[17]; void *data; uint32_t size; }; static struct cont_data cont_list[MAX_CONT]; static void handle_continuation(struct tid_data *tid, bool nested, uint16_t bytes, const uint8_t *data, uint16_t size) { uint8_t *newdata; int i, n = -1; if (bytes + 1 > size) { print_text(COLOR_ERROR, "missing continuation state"); return; } if (tid->cont[0] == 0x00 && data[bytes] == 0x00) { decode_data_elements(0, 2, data, bytes, nested ? print_attr_lists : print_attr_list); print_continuation(data + bytes, size - bytes); return; } for (i = 0; i < MAX_CONT; i++) { if (cont_list[i].cont[0] == 0x00) { if (n < 0) n = i; continue; } if (cont_list[i].channel != tid->channel) continue; if (cont_list[i].cont[0] != tid->cont[0]) continue; if (!memcmp(cont_list[i].cont + 1, tid->cont + 1, tid->cont[0])) { n = i; break; } } print_continuation(data + bytes, size - bytes); if (n < 0) return; newdata = realloc(cont_list[n].data, cont_list[n].size + bytes); if (!newdata) { print_text(COLOR_ERROR, "failed buffer allocation"); free(cont_list[n].data); cont_list[n].data = NULL; cont_list[n].size = 0; return; } cont_list[n].channel = tid->channel; cont_list[n].data = newdata; if (bytes > 0) { memcpy(cont_list[n].data + cont_list[n].size, data, bytes); cont_list[n].size += bytes; } if (data[bytes] == 0x00) { print_field("Combined attribute bytes: %d", cont_list[n].size); decode_data_elements(0, 2, cont_list[n].data, cont_list[n].size, nested ? print_attr_lists : print_attr_list); free(cont_list[n].data); cont_list[n].data = NULL; cont_list[n].size = 0; } else memcpy(cont_list[n].cont, data + bytes, data[bytes] + 1); } static uint16_t common_rsp(const struct l2cap_frame *frame, struct tid_data *tid) { uint16_t bytes; if (frame->size < 2) { print_text(COLOR_ERROR, "invalid size"); packet_hexdump(frame->data, frame->size); return 0; } bytes = get_be16(frame->data); print_field("Attribute bytes: %d", bytes); if (bytes > frame->size - 2) { print_text(COLOR_ERROR, "invalid attribute size"); packet_hexdump(frame->data + 2, frame->size - 2); return 0; } return bytes; } static const char *error_str(uint16_t code) { switch (code) { case 0x0001: return "Invalid Version"; case 0x0002: return "Invalid Record Handle"; case 0x0003: return "Invalid Syntax"; case 0x0004: return "Invalid PDU Size"; case 0x0005: return "Invalid Continuation State"; default: return "Unknown"; } } static void error_rsp(const struct l2cap_frame *frame, struct tid_data *tid) { uint16_t error; clear_tid(tid); if (frame->size < 2) { print_text(COLOR_ERROR, "invalid size"); packet_hexdump(frame->data, frame->size); return; } error = get_be16(frame->data); print_field("Error code: %s (0x%4.4x)", error_str(error), error); } static void service_req(const struct l2cap_frame *frame, struct tid_data *tid) { uint32_t search_bytes; search_bytes = get_bytes(frame->data, frame->size); print_field("Search pattern: [len %d]", search_bytes); if (search_bytes + 2 > frame->size) { print_text(COLOR_ERROR, "invalid search list length"); packet_hexdump(frame->data, frame->size); return; } decode_data_elements(0, 2, frame->data, search_bytes, NULL); print_field("Max record count: %d", get_be16(frame->data + search_bytes)); print_continuation(frame->data + search_bytes + 2, frame->size - search_bytes - 2); } static void service_rsp(const struct l2cap_frame *frame, struct tid_data *tid) { uint16_t count; int i; clear_tid(tid); if (frame->size < 4) { print_text(COLOR_ERROR, "invalid size"); packet_hexdump(frame->data, frame->size); return; } count = get_be16(frame->data + 2); if (count * 4 > frame->size) { print_text(COLOR_ERROR, "invalid record count"); return; } print_field("Total record count: %d", get_be16(frame->data)); print_field("Current record count: %d", count); for (i = 0; i < count; i++) print_field("Record handle: 0x%4.4x", get_be32(frame->data + 4 + (i * 4))); print_continuation(frame->data + 4 + (count * 4), frame->size - 4 - (count * 4)); } static void attr_req(const struct l2cap_frame *frame, struct tid_data *tid) { uint32_t attr_bytes; if (frame->size < 6) { print_text(COLOR_ERROR, "invalid size"); packet_hexdump(frame->data, frame->size); return; } print_field("Record handle: 0x%4.4x", get_be32(frame->data)); print_field("Max attribute bytes: %d", get_be16(frame->data + 4)); attr_bytes = get_bytes(frame->data + 6, frame->size - 6); print_field("Attribute list: [len %d]", attr_bytes); if (attr_bytes + 6 > frame->size) { print_text(COLOR_ERROR, "invalid attribute list length"); packet_hexdump(frame->data, frame->size); return; } decode_data_elements(0, 2, frame->data + 6, attr_bytes, NULL); store_continuation(tid, frame->data + 6 + attr_bytes, frame->size - 6 - attr_bytes); } static void attr_rsp(const struct l2cap_frame *frame, struct tid_data *tid) { uint16_t bytes; bytes = common_rsp(frame, tid); handle_continuation(tid, false, bytes, frame->data + 2, frame->size - 2); clear_tid(tid); } static void search_attr_req(const struct l2cap_frame *frame, struct tid_data *tid) { uint32_t search_bytes, attr_bytes; search_bytes = get_bytes(frame->data, frame->size); print_field("Search pattern: [len %d]", search_bytes); if (search_bytes + 2 > frame->size) { print_text(COLOR_ERROR, "invalid search list length"); packet_hexdump(frame->data, frame->size); return; } decode_data_elements(0, 2, frame->data, search_bytes, NULL); print_field("Max record count: %d", get_be16(frame->data + search_bytes)); attr_bytes = get_bytes(frame->data + search_bytes + 2, frame->size - search_bytes - 2); print_field("Attribute list: [len %d]", attr_bytes); if (search_bytes + attr_bytes > frame->size) { print_text(COLOR_ERROR, "invalid attribute list length"); return; } decode_data_elements(0, 2, frame->data + search_bytes + 2, attr_bytes, NULL); store_continuation(tid, frame->data + search_bytes + 2 + attr_bytes, frame->size - search_bytes - 2 - attr_bytes); } static void search_attr_rsp(const struct l2cap_frame *frame, struct tid_data *tid) { uint16_t bytes; bytes = common_rsp(frame, tid); handle_continuation(tid, true, bytes, frame->data + 2, frame->size - 2); clear_tid(tid); } struct sdp_data { uint8_t pdu; const char *str; void (*func) (const struct l2cap_frame *frame, struct tid_data *tid); }; static const struct sdp_data sdp_table[] = { { 0x01, "Error Response", error_rsp }, { 0x02, "Service Search Request", service_req }, { 0x03, "Service Search Response", service_rsp }, { 0x04, "Service Attribute Request", attr_req }, { 0x05, "Service Attribute Response", attr_rsp }, { 0x06, "Service Search Attribute Request", search_attr_req }, { 0x07, "Service Search Attribute Response", search_attr_rsp }, { } }; void sdp_packet(const struct l2cap_frame *frame) { uint8_t pdu; uint16_t tid, plen; struct l2cap_frame sdp_frame; struct tid_data *tid_info; const struct sdp_data *sdp_data = NULL; const char *pdu_color, *pdu_str; int i; l2cap_frame_pull(&sdp_frame, frame, 0); if (!l2cap_frame_get_u8(&sdp_frame, &pdu) || !l2cap_frame_get_be16(&sdp_frame, &tid) || !l2cap_frame_get_be16(&sdp_frame, &plen)) { print_text(COLOR_ERROR, "frame too short"); packet_hexdump(frame->data, frame->size); return; } if (sdp_frame.size != plen) { print_text(COLOR_ERROR, "invalid frame size"); packet_hexdump(sdp_frame.data, sdp_frame.size); return; } for (i = 0; sdp_table[i].str; i++) { if (sdp_table[i].pdu == pdu) { sdp_data = &sdp_table[i]; break; } } if (sdp_data) { if (sdp_data->func) { if (frame->in) pdu_color = COLOR_MAGENTA; else pdu_color = COLOR_BLUE; } else pdu_color = COLOR_WHITE_BG; pdu_str = sdp_data->str; } else { pdu_color = COLOR_WHITE_BG; pdu_str = "Unknown"; } print_indent(6, pdu_color, "SDP: ", pdu_str, COLOR_OFF, " (0x%2.2x) tid %d len %d", pdu, tid, plen); tid_info = get_tid(tid, frame->chan); if (!sdp_data || !sdp_data->func || !tid_info) { packet_hexdump(sdp_frame.data, sdp_frame.size); return; } sdp_data->func(&sdp_frame, tid_info); } bluez-5.82/monitor/PaxHeaders/keys.c0000644000000000000000000000005014447506754014474 xustar0020 atime=1743516050 20 ctime=1743591282 bluez-5.82/monitor/keys.c0000644000000000000000000000514214447506754014157 0ustar00rootroot// SPDX-License-Identifier: LGPL-2.1-or-later /* * * BlueZ - Bluetooth protocol stack for Linux * * Copyright (C) 2011-2014 Intel Corporation * Copyright (C) 2002-2010 Marcel Holtmann * * */ #ifdef HAVE_CONFIG_H #include #endif #include #include "src/shared/util.h" #include "src/shared/queue.h" #include "src/shared/crypto.h" #include "keys.h" static const uint8_t empty_key[16] = { 0x00, }; static const uint8_t empty_addr[6] = { 0x00, }; static struct bt_crypto *crypto; struct irk_data { uint8_t key[16]; uint8_t addr[6]; uint8_t addr_type; }; static struct queue *irk_list; void keys_setup(void) { crypto = bt_crypto_new(); irk_list = queue_new(); } void keys_cleanup(void) { bt_crypto_unref(crypto); queue_destroy(irk_list, free); } void keys_update_identity_key(const uint8_t key[16]) { struct irk_data *irk; irk = queue_peek_tail(irk_list); if (irk && !memcmp(irk->key, empty_key, 16)) { memcpy(irk->key, key, 16); return; } irk = new0(struct irk_data, 1); if (irk) { memcpy(irk->key, key, 16); if (!queue_push_tail(irk_list, irk)) free(irk); } } void keys_update_identity_addr(const uint8_t addr[6], uint8_t addr_type) { struct irk_data *irk; irk = queue_peek_tail(irk_list); if (irk && !memcmp(irk->addr, empty_addr, 6)) { memcpy(irk->addr, addr, 6); irk->addr_type = addr_type; return; } irk = new0(struct irk_data, 1); if (irk) { memcpy(irk->addr, addr, 6); irk->addr_type = addr_type; if (!queue_push_tail(irk_list, irk)) free(irk); } } static bool match_resolve_irk(const void *data, const void *match_data) { const struct irk_data *irk = data; const uint8_t *addr = match_data; uint8_t local_hash[3]; bt_crypto_ah(crypto, irk->key, addr + 3, local_hash); return !memcmp(addr, local_hash, 3); } bool keys_resolve_identity(const uint8_t addr[6], uint8_t ident[6], uint8_t *ident_type) { struct irk_data *irk; irk = queue_find(irk_list, match_resolve_irk, addr); if (irk) { memcpy(ident, irk->addr, 6); *ident_type = irk->addr_type; return true; } return false; } static bool match_key(const void *data, const void *match_data) { const struct irk_data *irk = data; const uint8_t *key = match_data; return !memcmp(irk->key, key, 16); } bool keys_add_identity(const uint8_t addr[6], uint8_t addr_type, const uint8_t key[16]) { struct irk_data *irk; irk = queue_find(irk_list, match_key, key); if (!irk) { irk = new0(struct irk_data, 1); memcpy(irk->key, key, 16); queue_push_tail(irk_list, irk); } memcpy(irk->addr, addr, 6); irk->addr_type = addr_type; return true; } bluez-5.82/monitor/PaxHeaders/msft.h0000644000000000000000000000005014165411566014470 xustar0020 atime=1743515957 20 ctime=1743591282 bluez-5.82/monitor/msft.h0000644000000000000000000001054314165411566014154 0ustar00rootroot/* * * BlueZ - Bluetooth protocol stack for Linux * * Copyright (C) 2011-2014 Intel Corporation * Copyright (C) 2002-2010 Marcel Holtmann * * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA * */ #include #define MSFT_SUBCMD_READ_SUPPORTED_FEATURES 0x00 struct msft_cmd_read_supported_features { uint8_t subcmd; } __attribute__((packed)); #define MSFT_MONITOR_BREDR_RSSI BIT(0) #define MSFT_MONITOR_LE_RSSI BIT(1) #define MSFT_MONITOR_LE_LEGACY_RSSI BIT(2) #define MSFT_MONITOR_LE_ADV BIT(3) #define MSFT_MONITOR_SSP_VALIDATION BIT(4) #define MSFT_MONITOR_LE_ADV_CONTINUOS BIT(5) struct msft_rsp_read_supported_features { uint8_t status; uint8_t subcmd; uint8_t features[8]; uint8_t evt_prefix_len; uint8_t evt_prefix[]; } __attribute__((packed)); #define MSFT_SUBCMD_MONITOR_RSSI 0x01 struct msft_cmd_monitor_rssi { uint8_t subcmd; uint16_t handle; int8_t rssi_high; int8_t rssi_low; uint8_t rssi_low_interval; uint8_t rssi_period; } __attribute__((packed)); struct msft_rsp_monitor_rssi { uint8_t status; uint8_t subcmd; } __attribute__((packed)); #define MSFT_SUBCMD_CANCEL_MONITOR_RSSI 0x02 struct msft_cmd_cancel_monitor_rssi { uint8_t subcmd; uint16_t handle; } __attribute__((packed)); struct msft_rsp_cancel_monitor_rssi { uint8_t status; uint8_t subcmd; } __attribute__((packed)); #define MSFT_SUBCMD_LE_MONITOR_ADV 0x03 #define MSFT_LE_MONITOR_ADV_PATTERN 0x01 struct msft_le_monitor_pattern { uint8_t len; uint8_t type; uint8_t start; uint8_t data[]; } __attribute__((packed)); struct msft_le_monitor_adv_patterns { uint8_t num; struct msft_le_monitor_pattern data[]; } __attribute__((packed)); #define MSFT_LE_MONITOR_ADV_UUID 0x02 struct msft_le_monitor_adv_uuid { uint8_t type; union { uint16_t u16; uint32_t u32; uint8_t u128[16]; } value; } __attribute__((packed)); #define MSFT_LE_MONITOR_ADV_IRK 0x03 struct msft_le_monitor_adv_irk { uint8_t irk[8]; } __attribute__((packed)); #define MSFT_LE_MONITOR_ADV_ADDR 0x04 struct msft_le_monitor_adv_addr { uint8_t type; uint8_t addr[6]; } __attribute__((packed)); struct msft_cmd_le_monitor_adv { uint8_t subcmd; int8_t rssi_high; int8_t rssi_low; uint8_t rssi_low_interval; uint8_t rssi_period; uint8_t type; uint8_t data[]; } __attribute__((packed)); struct msft_rsp_le_monitor_adv { uint8_t status; uint8_t subcmd; uint8_t handle; } __attribute__((packed)); #define MSFT_SUBCMD_LE_CANCEL_MONITOR_ADV 0x04 struct msft_cmd_le_cancel_monitor_adv { uint8_t subcmd; uint8_t handle; } __attribute__((packed)); struct msft_rsp_le_cancel_monitor_adv { uint8_t status; uint8_t subcmd; } __attribute__((packed)); #define MSFT_SUBCMD_LE_MONITOR_ADV_ENABLE 0x05 struct msft_cmd_le_monitor_adv_enable { uint8_t subcmd; uint8_t enable; } __attribute__((packed)); struct msft_rsp_le_monitor_adv_enable { uint8_t status; uint8_t subcmd; } __attribute__((packed)); #define MSFT_SUBCMD_READ_ABS_RSSI 0x06 struct msft_cmd_read_abs_rssi { uint8_t subcmd; uint16_t handle; } __attribute__((packed)); struct msft_rsp_read_abs_rssi { uint8_t status; uint8_t subcmd; uint16_t handle; int8_t rssi; } __attribute__((packed)); #define MSFT_SUBEVT_RSSI 0x01 struct msft_evt_rssi { uint8_t subevt; uint8_t status; uint16_t handle; int8_t rssi; } __attribute__((packed)); #define MSFT_SUBEVT_MONITOR_DEVICE 0x02 struct msft_evt_monitor_device { uint8_t subevt; uint8_t addr_type; uint8_t addr[6]; uint8_t handle; uint8_t state; } __attribute__((packed)); struct vendor_ocf; struct vendor_evt; const struct vendor_ocf *msft_vendor_ocf(void); const struct vendor_evt *msft_vendor_evt(void); bluez-5.82/monitor/PaxHeaders/crc.h0000644000000000000000000000005014015011623014246 xustar0020 atime=1743516014 20 ctime=1743591282 bluez-5.82/monitor/crc.h0000644000000000000000000000067514015011623013737 0ustar00rootroot/* SPDX-License-Identifier: LGPL-2.1-or-later */ /* * * BlueZ - Bluetooth protocol stack for Linux * * Copyright (C) 2011-2014 Intel Corporation * Copyright (C) 2002-2010 Marcel Holtmann * * */ #include uint32_t crc24_bit_reverse(uint32_t value); uint32_t crc24_calculate(uint32_t preset, const uint8_t *data, uint8_t len); uint32_t crc24_reverse(uint32_t crc, const uint8_t *data, uint8_t len); bluez-5.82/monitor/PaxHeaders/keys.h0000644000000000000000000000005014447506754014501 xustar0020 atime=1743515951 20 ctime=1743591282 bluez-5.82/monitor/keys.h0000644000000000000000000000120114447506754014154 0ustar00rootroot/* SPDX-License-Identifier: LGPL-2.1-or-later */ /* * * BlueZ - Bluetooth protocol stack for Linux * * Copyright (C) 2011-2014 Intel Corporation * Copyright (C) 2002-2010 Marcel Holtmann * * */ #include #include void keys_setup(void); void keys_cleanup(void); void keys_update_identity_key(const uint8_t key[16]); void keys_update_identity_addr(const uint8_t addr[6], uint8_t addr_type); bool keys_resolve_identity(const uint8_t addr[6], uint8_t ident[6], uint8_t *ident_type); bool keys_add_identity(const uint8_t addr[6], uint8_t addr_type, const uint8_t key[16]); bluez-5.82/monitor/PaxHeaders/intel.c0000644000000000000000000000005014572354773014635 xustar0020 atime=1743516053 20 ctime=1743591282 bluez-5.82/monitor/intel.c0000644000000000000000000012267614572354773014334 0ustar00rootroot// SPDX-License-Identifier: LGPL-2.1-or-later /* * * BlueZ - Bluetooth protocol stack for Linux * * Copyright (C) 2011-2014 Intel Corporation * Copyright (C) 2002-2010 Marcel Holtmann * * */ #ifdef HAVE_CONFIG_H #include #endif #define _GNU_SOURCE #include #include #include "lib/bluetooth.h" #include "lib/hci.h" #include "src/shared/util.h" #include "display.h" #include "packet.h" #include "lmp.h" #include "ll.h" #include "vendor.h" #include "intel.h" #define COLOR_UNKNOWN_EVENT_MASK COLOR_WHITE_BG #define COLOR_UNKNOWN_SCAN_STATUS COLOR_WHITE_BG #define COLOR_UNKNOWN_EXT_EVENT COLOR_WHITE_BG static void print_status(uint8_t status) { packet_print_error("Status", status); } static void print_module(uint8_t module) { const char *str; switch (module) { case 0x01: str = "BC"; break; case 0x02: str = "HCI"; break; case 0x03: str = "LLC"; break; case 0x04: str = "OS"; break; case 0x05: str = "LM"; break; case 0x06: str = "SC"; break; case 0x07: str = "SP"; break; case 0x08: str = "OSAL"; break; case 0x09: str = "LC"; break; case 0x0a: str = "APP"; break; case 0x0b: str = "TLD"; break; case 0xf0: str = "Debug"; break; default: str = "Reserved"; break; } print_field("Module: %s (0x%2.2x)", str, module); } static void null_cmd(uint16_t index, const void *data, uint8_t size) { } static void status_rsp(uint16_t index, const void *data, uint8_t size) { uint8_t status = get_u8(data); print_status(status); } static void reset_cmd(uint16_t index, const void *data, uint8_t size) { uint8_t reset_type = get_u8(data); uint8_t patch_enable = get_u8(data + 1); uint8_t ddc_reload = get_u8(data + 2); uint8_t boot_option = get_u8(data + 3); uint32_t boot_addr = get_le32(data + 4); const char *str; switch (reset_type) { case 0x00: str = "Soft software reset"; break; case 0x01: str = "Hard software reset"; break; default: str = "Reserved"; break; } print_field("Reset type: %s (0x%2.2x)", str, reset_type); switch (patch_enable) { case 0x00: str = "Do not enable"; break; case 0x01: str = "Enable"; break; default: str = "Reserved"; break; } print_field("Patch vectors: %s (0x%2.2x)", str, patch_enable); switch (ddc_reload) { case 0x00: str = "Do not reload"; break; case 0x01: str = "Reload from OTP"; break; default: str = "Reserved"; break; } print_field("DDC parameters: %s (0x%2.2x)", str, ddc_reload); switch (boot_option) { case 0x00: str = "Current image"; break; case 0x01: str = "Specified address"; break; default: str = "Reserved"; break; } print_field("Boot option: %s (0x%2.2x)", str, boot_option); print_field("Boot address: 0x%8.8x", boot_addr); } struct intel_version_tlv { uint8_t type; uint8_t len; uint8_t val[]; }; static void print_version_tlv_u32(const struct intel_version_tlv *tlv, const char *type_str) { print_field("%s(%u): 0x%8.8x", type_str, tlv->type, get_le32(tlv->val)); } static void print_version_tlv_u16(const struct intel_version_tlv *tlv, const char *type_str) { print_field("%s(%u): 0x%4.4x", type_str, tlv->type, get_le16(tlv->val)); } static void print_version_tlv_u8(const struct intel_version_tlv *tlv, const char *type_str) { print_field("%s(%u): 0x%2.2x", type_str, tlv->type, get_u8(tlv->val)); } static void print_version_tlv_enabled(const struct intel_version_tlv *tlv, const char *type_str) { print_field("%s(%u): %s(%u)", type_str, tlv->type, tlv->val[0] ? "Enabled" : "Disabled", tlv->val[0]); } static void print_version_tlv_img_type(const struct intel_version_tlv *tlv, const char *type_str) { const char *str; switch (get_u8(tlv->val)) { case 0x01: str = "Bootloader"; break; case 0x03: str = "Firmware"; break; default: str = "Unknown"; break; } print_field("%s(%u): %s(0x%2.2x)", type_str, tlv->type, str, get_u8(tlv->val)); } static void print_version_tlv_timestamp(const struct intel_version_tlv *tlv, const char *type_str) { print_field("%s(%u): %u-%u", type_str, tlv->type, tlv->val[1], tlv->val[0]); } static void print_version_tlv_min_fw(const struct intel_version_tlv *tlv, const char *type_str) { print_field("%s(%u): %u-%u.%u", type_str, tlv->type, tlv->val[0], tlv->val[1], 2000 + tlv->val[2]); } static void print_version_tlv_otp_bdaddr(const struct intel_version_tlv *tlv, const char *type_str) { packet_print_addr(type_str, tlv->val, 0x00); } static void print_version_tlv_unknown(const struct intel_version_tlv *tlv, const char *type_str) { print_field("%s(%u): ", type_str, tlv->type); packet_hexdump(tlv->val, tlv->len); } static void print_version_tlv_mfg(const struct intel_version_tlv *tlv, const char *type_str) { uint16_t mfg_id = get_le16(tlv->val); print_field("%s(%u): %s (%u)", type_str, tlv->type, bt_compidtostr(mfg_id), mfg_id); } static const struct intel_version_tlv_desc { uint8_t type; const char *type_str; void (*func)(const struct intel_version_tlv *tlv, const char *type_str); } intel_version_tlv_table[] = { { 16, "CNVi TOP", print_version_tlv_u32 }, { 17, "CNVr TOP", print_version_tlv_u32 }, { 18, "CNVi BT", print_version_tlv_u32 }, { 19, "CNVr BT", print_version_tlv_u32 }, { 20, "CNVi OTP", print_version_tlv_u16 }, { 21, "CNVr OTP", print_version_tlv_u16 }, { 22, "Device Rev ID", print_version_tlv_u16 }, { 23, "USB VID", print_version_tlv_u16 }, { 24, "USB PID", print_version_tlv_u16 }, { 25, "PCIE VID", print_version_tlv_u16 }, { 26, "PCIe DID", print_version_tlv_u16 }, { 27, "PCIe Subsystem ID", print_version_tlv_u16 }, { 28, "Image Type", print_version_tlv_img_type }, { 29, "Time Stamp", print_version_tlv_timestamp }, { 30, "Build Type", print_version_tlv_u8 }, { 31, "Build Num", print_version_tlv_u32 }, { 32, "FW Build Product", print_version_tlv_u8 }, { 33, "FW Build HW", print_version_tlv_u8 }, { 34, "FW Build Step", print_version_tlv_u8 }, { 35, "BT Spec", print_version_tlv_u8 }, { 36, "Manufacturer", print_version_tlv_mfg }, { 37, "HCI Revision", print_version_tlv_u16 }, { 38, "LMP SubVersion", print_version_tlv_u16 }, { 39, "OTP Patch Version", print_version_tlv_u8 }, { 40, "Secure Boot", print_version_tlv_enabled }, { 41, "Key From Header", print_version_tlv_enabled }, { 42, "OTP Lock", print_version_tlv_enabled }, { 43, "API Lock", print_version_tlv_enabled }, { 44, "Debug Lock", print_version_tlv_enabled }, { 45, "Minimum FW", print_version_tlv_min_fw }, { 46, "Limited CCE", print_version_tlv_enabled }, { 47, "SBE Type", print_version_tlv_u8 }, { 48, "OTP BDADDR", print_version_tlv_otp_bdaddr }, { 49, "Unlocked State", print_version_tlv_enabled }, { 0, NULL, NULL }, }; static void read_version_tlv_rsp(const void *data, uint8_t size) { uint8_t status = get_u8(data); print_status(status); /* Consume the status */ data++; size--; while (size > 0) { const struct intel_version_tlv *tlv = data; const struct intel_version_tlv_desc *desc = NULL; int i; for (i = 0; intel_version_tlv_table[i].type > 0; i++) { if (intel_version_tlv_table[i].type == tlv->type) { desc = &intel_version_tlv_table[i]; break; } } if (desc) desc->func(tlv, desc->type_str); else print_version_tlv_unknown(tlv, "Unknown Type"); data += sizeof(*tlv) + tlv->len; size -= sizeof(*tlv) + tlv->len; } } static void read_version_rsp(uint16_t index, const void *data, uint8_t size) { uint8_t status = get_u8(data); uint8_t hw_platform = get_u8(data + 1); uint8_t hw_variant = get_u8(data + 2); uint8_t hw_revision = get_u8(data + 3); uint8_t fw_variant = get_u8(data + 4); uint8_t fw_revision = get_u8(data + 5); uint8_t fw_build_nn = get_u8(data + 6); uint8_t fw_build_cw = get_u8(data + 7); uint8_t fw_build_yy = get_u8(data + 8); uint8_t fw_patch = get_u8(data + 9); /* There are two different formats of the response for the * HCI_Intel_Read_version command depends on the command parameters * If the size is fixed to 10 and hw_platform is 0x37, then it is the * legacy format, otherwise use the tlv based format. */ if (size != 10 && hw_platform != 0x37) { read_version_tlv_rsp(data, size); return; } print_status(status); print_field("Hardware platform: 0x%2.2x", hw_platform); print_field("Hardware variant: 0x%2.2x", hw_variant); print_field("Hardware revision: %u.%u", hw_revision >> 4, hw_revision & 0x0f); print_field("Firmware variant: 0x%2.2x", fw_variant); print_field("Firmware revision: %u.%u", fw_revision >> 4, fw_revision & 0x0f); print_field("Firmware build: %u-%u.%u", fw_build_nn, fw_build_cw, 2000 + fw_build_yy); print_field("Firmware patch: %u", fw_patch); } static void read_version_cmd(uint16_t index, const void *data, uint8_t size) { const char *str; uint8_t type; /* This is the legacy read version command format and no further action * is needed */ if (size == 0) return; print_field("Requested Type:"); while (size > 0) { const struct intel_version_tlv_desc *desc = NULL; int i; type = get_u8(data); /* Get all supported types */ if (type == 0xff) str = "All Supported Types"; else { for (i = 0; intel_version_tlv_table[i].type > 0; i++) { if (intel_version_tlv_table[i].type == type) { desc = &intel_version_tlv_table[i]; break; } } if (desc) str = desc->type_str; else str = "Unknown Type"; } print_field(" %s(0x%2.2x)", str, type); data += sizeof(type); size -= sizeof(type); } } static void set_uart_baudrate_cmd(uint16_t index, const void *data, uint8_t size) { uint8_t baudrate = get_u8(data); const char *str; switch (baudrate) { case 0x00: str = "9600 Baud"; break; case 0x01: str = "19200 Baud"; break; case 0x02: str = "38400 Baud"; break; case 0x03: str = "57600 Baud"; break; case 0x04: str = "115200 Baud"; break; case 0x05: str = "230400 Baud"; break; case 0x06: str = "460800 Baud"; break; case 0x07: str = "921600 Baud"; break; case 0x08: str = "1843200 Baud"; break; case 0x09: str = "3250000 baud"; break; case 0x0a: str = "2000000 baud"; break; case 0x0b: str = "3000000 baud"; break; case 0x0c: str = "3714286 baud"; break; case 0x0d: str = "4333333 baud"; break; case 0x0e: str = "6500000 baud"; break; default: str = "Reserved"; break; } print_field("Baudrate: %s (0x%2.2x)", str, baudrate); } static void secure_send_cmd(uint16_t index, const void *data, uint8_t size) { uint8_t type = get_u8(data); const char *str; switch (type) { case 0x00: str = "Init"; break; case 0x01: str = "Data"; break; case 0x02: str = "Sign"; break; case 0x03: str = "PKey"; break; default: str = "Reserved"; break; } print_field("Type: %s fragment (0x%2.2x)", str, type); packet_hexdump(data + 1, size - 1); } static void manufacturer_mode_cmd(uint16_t index, const void *data, uint8_t size) { uint8_t mode = get_u8(data); uint8_t reset = get_u8(data + 1); const char *str; switch (mode) { case 0x00: str = "Disabled"; break; case 0x01: str = "Enabled"; break; default: str = "Reserved"; break; } print_field("Mode switch: %s (0x%2.2x)", str, mode); switch (reset) { case 0x00: str = "No reset"; break; case 0x01: str = "Reset and deactivate patches"; break; case 0x02: str = "Reset and activate patches"; break; default: str = "Reserved"; break; } print_field("Reset behavior: %s (0x%2.2x)", str, reset); } static void write_bd_data_cmd(uint16_t index, const void *data, uint8_t size) { uint8_t features[8]; packet_print_addr("Address", data, 0x00); packet_hexdump(data + 6, 6); memcpy(features, data + 12, 8); packet_print_features_lmp(features, 0); memcpy(features, data + 20, 1); memset(features + 1, 0, 7); packet_print_features_ll(features); packet_hexdump(data + 21, size - 21); } static void read_bd_data_rsp(uint16_t index, const void *data, uint8_t size) { uint8_t status = get_u8(data); print_status(status); packet_print_addr("Address", data + 1, 0x00); packet_hexdump(data + 7, size - 7); } static void write_bd_address_cmd(uint16_t index, const void *data, uint8_t size) { packet_print_addr("Address", data, 0x00); } static void act_deact_traces_cmd(uint16_t index, const void *data, uint8_t size) { uint8_t tx = get_u8(data); uint8_t tx_arq = get_u8(data + 1); uint8_t rx = get_u8(data + 2); print_field("Transmit traces: 0x%2.2x", tx); print_field("Transmit ARQ: 0x%2.2x", tx_arq); print_field("Receive traces: 0x%2.2x", rx); } static void stimulate_exception_cmd(uint16_t index, const void *data, uint8_t size) { uint8_t type = get_u8(data); const char *str; switch (type) { case 0x00: str = "Fatal Exception"; break; case 0x01: str = "Debug Exception"; break; default: str = "Reserved"; break; } print_field("Type: %s (0x%2.2x)", str, type); } static const struct { uint8_t bit; const char *str; } events_table[] = { { 0, "Bootup" }, { 1, "SCO Rejected via LMP" }, { 2, "PTT Switch Notification" }, { 7, "Scan Status" }, { 9, "Debug Exception" }, { 10, "Fatal Exception" }, { 11, "System Exception" }, { 13, "LE Link Established" }, { 14, "FW Trace String" }, { } }; static void set_event_mask_cmd(uint16_t index, const void *data, uint8_t size) { const uint8_t *events_array = data; uint64_t mask, events = 0; int i; for (i = 0; i < 8; i++) events |= ((uint64_t) events_array[i]) << (i * 8); print_field("Mask: 0x%16.16" PRIx64, events); mask = events; for (i = 0; events_table[i].str; i++) { if (events & (((uint64_t) 1) << events_table[i].bit)) { print_field(" %s", events_table[i].str); mask &= ~(((uint64_t) 1) << events_table[i].bit); } } if (mask) print_text(COLOR_UNKNOWN_EVENT_MASK, " Unknown mask " "(0x%16.16" PRIx64 ")", mask); } static void ddc_config_write_cmd(uint16_t index, const void *data, uint8_t size) { while (size > 0) { uint8_t param_len = get_u8(data); uint16_t param_id = get_le16(data + 1); print_field("Identifier: 0x%4.4x", param_id); packet_hexdump(data + 3, param_len - 2); data += param_len + 1; size -= param_len + 1; } } static void ddc_config_write_rsp(uint16_t index, const void *data, uint8_t size) { uint8_t status = get_u8(data); uint16_t param_id = get_le16(data + 1); print_status(status); print_field("Identifier: 0x%4.4x", param_id); } static void memory_write_cmd(uint16_t index, const void *data, uint8_t size) { uint32_t addr = get_le32(data); uint8_t mode = get_u8(data + 4); uint8_t length = get_u8(data + 5); const char *str; print_field("Address: 0x%8.8x", addr); switch (mode) { case 0x00: str = "Byte access"; break; case 0x01: str = "Half word access"; break; case 0x02: str = "Word access"; break; default: str = "Reserved"; break; } print_field("Mode: %s (0x%2.2x)", str, mode); print_field("Length: %u", length); packet_hexdump(data + 6, size - 6); } static void read_supported_features_cmd(uint16_t index, const void *data, uint8_t size) { uint8_t page = get_u8(data); print_field("Page: 0x%2.2x", page); } static void read_supported_features_rsp(uint16_t index, const void *data, uint8_t size) { uint8_t status = get_u8(data); uint8_t page = get_u8(data + 1); uint8_t max_pages = get_u8(data + 2); print_status(status); print_field("Page: 0x%2.2x", page); print_field("Max Pages: 0x%2.2x", max_pages); print_field("Supported Features:"); packet_hexdump(data + 3, size - 3); } static void ppag_enable(uint16_t index, const void *data, uint8_t size) { uint32_t enable = get_le32(data); char *ppag_enable_flags; switch (enable) { case 0x01: ppag_enable_flags = "EU"; break; case 0x02: ppag_enable_flags = "China"; break; case 0x03: ppag_enable_flags = "EU and China"; break; default: ppag_enable_flags = "Unknown"; break; } print_field("Enable: %s (0x%8.8x)", ppag_enable_flags, enable); } static const struct vendor_ocf vendor_ocf_table[] = { { 0x001, "Reset", reset_cmd, 8, true, status_rsp, 1, true }, { 0x002, "No Operation" }, { 0x005, "Read Version", read_version_cmd, 0, false, read_version_rsp, 1, false }, { 0x006, "Set UART Baudrate", set_uart_baudrate_cmd, 1, true, status_rsp, 1, true }, { 0x007, "Enable LPM" }, { 0x008, "PCM Write Configuration" }, { 0x009, "Secure Send", secure_send_cmd, 1, false, status_rsp, 1, true }, { 0x00d, "Read Secure Boot Params", null_cmd, 0, true }, { 0x00e, "Write Secure Boot Params" }, { 0x00f, "Unlock" }, { 0x010, "Change UART Baudrate" }, { 0x011, "Manufacturer Mode", manufacturer_mode_cmd, 2, true, status_rsp, 1, true }, { 0x012, "Read Link RSSI" }, { 0x022, "Get Exception Info" }, { 0x024, "Clear Exception Info" }, { 0x02f, "Write BD Data", write_bd_data_cmd, 6, false }, { 0x030, "Read BD Data", null_cmd, 0, true, read_bd_data_rsp, 7, false }, { 0x031, "Write BD Address", write_bd_address_cmd, 6, true, status_rsp, 1, true }, { 0x032, "Flow Specification" }, { 0x034, "Read Secure ID" }, { 0x038, "Set Synchronous USB Interface Type" }, { 0x039, "Config Synchronous Interface" }, { 0x03f, "SW RF Kill", null_cmd, 0, true, status_rsp, 1, true }, { 0x043, "Activate Deactivate Traces", act_deact_traces_cmd, 3, true }, { 0x04d, "Stimulate Exception", stimulate_exception_cmd, 1, true, status_rsp, 1, true }, { 0x050, "Read HW Version" }, { 0x052, "Set Event Mask", set_event_mask_cmd, 8, true, status_rsp, 1, true }, { 0x053, "Config_Link_Controller" }, { 0x089, "DDC Write" }, { 0x08a, "DDC Read" }, { 0x08b, "DDC Config Write", ddc_config_write_cmd, 3, false, ddc_config_write_rsp, 3, true }, { 0x08c, "DDC Config Read" }, { 0x08d, "Memory Read" }, { 0x08e, "Memory Write", memory_write_cmd, 6, false, status_rsp, 1, true }, { 0x0a6, "Read Supported Features", read_supported_features_cmd, 1, true, read_supported_features_rsp, 19, true }, { 0x20b, "PPAG Enable", ppag_enable, 4, true, status_rsp, 1, true }, { } }; const struct vendor_ocf *intel_vendor_ocf(uint16_t ocf) { int i; for (i = 0; vendor_ocf_table[i].str; i++) { if (vendor_ocf_table[i].ocf == ocf) return &vendor_ocf_table[i]; } return NULL; } static void startup_evt(struct timeval *tv, uint16_t index, const void *data, uint8_t size) { } static void fatal_exception_evt(struct timeval *tv, uint16_t index, const void *data, uint8_t size) { uint16_t line = get_le16(data); uint8_t module = get_u8(data + 2); uint8_t reason = get_u8(data + 3); print_field("Line: %u", line); print_module(module); print_field("Reason: 0x%2.2x", reason); } static void bootup_evt(struct timeval *tv, uint16_t index, const void *data, uint8_t size) { uint8_t zero = get_u8(data); uint8_t num_packets = get_u8(data + 1); uint8_t source = get_u8(data + 2); uint8_t reset_type = get_u8(data + 3); uint8_t reset_reason = get_u8(data + 4); uint8_t ddc_status = get_u8(data + 5); const char *str; print_field("Zero: 0x%2.2x", zero); print_field("Number of packets: %d", num_packets); switch (source) { case 0x00: str = "Bootloader"; break; case 0x01: str = "Operational firmware"; break; case 0x02: str = "Self test firmware"; break; default: str = "Reserved"; break; } print_field("Source: %s (0x%2.2x)", str, source); switch (reset_type) { case 0x00: str = "Hardware reset"; break; case 0x01: str = "Soft watchdog reset"; break; case 0x02: str = "Soft software reset"; break; case 0x03: str = "Hard watchdog reset"; break; case 0x04: str = "Hard software reset"; break; default: str = "Reserved"; break; } print_field("Reset type: %s (0x%2.2x)", str, reset_type); switch (reset_reason) { case 0x00: str = "Power on"; break; case 0x01: str = "Reset command"; break; case 0x02: str = "Intel reset command"; break; case 0x03: str = "Watchdog"; break; case 0x04: str = "Fatal exception"; break; case 0x05: str = "System exception"; break; case 0xff: str = "Unknown"; break; default: str = "Reserved"; break; } print_field("Reset reason: %s (0x%2.2x)", str, reset_reason); switch (ddc_status) { case 0x00: str = "Firmware default"; break; case 0x01: str = "Firmware default plus OTP"; break; case 0x02: str = "Persistent RAM"; break; case 0x03: str = "Not used"; break; default: str = "Reserved"; break; } print_field("DDC status: %s (0x%2.2x)", str, ddc_status); } static void default_bd_data_evt(struct timeval *tv, uint16_t index, const void *data, uint8_t size) { uint8_t mem_status = get_u8(data); const char *str; switch (mem_status) { case 0x02: str = "Invalid manufacturing data"; break; default: str = "Reserved"; break; } print_field("Memory status: %s (0x%2.2x)", str, mem_status); } static void secure_send_commands_result_evt(struct timeval *tv, uint16_t index, const void *data, uint8_t size) { uint8_t result = get_u8(data); uint16_t opcode = get_le16(data + 1); uint16_t ogf = cmd_opcode_ogf(opcode); uint16_t ocf = cmd_opcode_ocf(opcode); uint8_t status = get_u8(data + 3); const char *str; switch (result) { case 0x00: str = "Success"; break; case 0x01: str = "General failure"; break; case 0x02: str = "Hardware failure"; break; case 0x03: str = "Signature verification failed"; break; case 0x04: str = "Parsing error of command buffer"; break; case 0x05: str = "Command execution failure"; break; case 0x06: str = "Command parameters error"; break; case 0x07: str = "Command missing"; break; default: str = "Reserved"; break; } print_field("Result: %s (0x%2.2x)", str, result); print_field("Opcode: 0x%4.4x (0x%2.2x|0x%4.4x)", opcode, ogf, ocf); print_status(status); } static void debug_exception_evt(struct timeval *tv, uint16_t index, const void *data, uint8_t size) { uint16_t line = get_le16(data); uint8_t module = get_u8(data + 2); uint8_t reason = get_u8(data + 3); print_field("Line: %u", line); print_module(module); print_field("Reason: 0x%2.2x", reason); } static void le_link_established_evt(struct timeval *tv, uint16_t index, const void *data, uint8_t size) { uint16_t handle = get_le16(data); uint32_t access_addr = get_le32(data + 10); print_field("Handle: %u", handle); packet_hexdump(data + 2, 8); print_field("Access address: 0x%8.8x", access_addr); packet_hexdump(data + 14, size - 14); } static void scan_status_evt(struct timeval *tv, uint16_t index, const void *data, uint8_t size) { uint8_t enable = get_u8(data); print_field("Inquiry scan: %s", (enable & 0x01) ? "Enabled" : "Disabled"); print_field("Page scan: %s", (enable & 0x02) ? "Enabled" : "Disabled"); if (enable & 0xfc) print_text(COLOR_UNKNOWN_SCAN_STATUS, " Unknown status (0x%2.2x)", enable & 0xfc); } static void act_deact_traces_complete_evt(struct timeval *tv, uint16_t index, const void *data, uint8_t size) { uint8_t status = get_u8(data); print_status(status); } static void lmp_pdu_trace_evt(struct timeval *tv, uint16_t index, const void *data, uint8_t size) { uint8_t type, len, id; uint16_t handle, count; uint32_t clock; const char *str; type = get_u8(data); handle = get_le16(data + 1); switch (type) { case 0x00: str = "RX LMP"; break; case 0x01: str = "TX LMP"; break; case 0x02: str = "ACK LMP"; break; case 0x03: str = "RX LL"; break; case 0x04: str = "TX LL"; break; case 0x05: str = "ACK LL"; break; default: str = "Unknown"; break; } print_field("Type: %s (0x%2.2x)", str, type); print_field("Handle: %u", handle); switch (type) { case 0x00: len = size - 8; clock = get_le32(data + 4 + len); packet_hexdump(data + 3, 1); lmp_packet(data + 4, len, false); print_field("Clock: 0x%8.8x", clock); break; case 0x01: len = size - 9; clock = get_le32(data + 4 + len); id = get_u8(data + 4 + len + 4); packet_hexdump(data + 3, 1); lmp_packet(data + 4, len, false); print_field("Clock: 0x%8.8x", clock); print_field("ID: 0x%2.2x", id); break; case 0x02: clock = get_le32(data + 3); id = get_u8(data + 3 + 4); print_field("Clock: 0x%8.8x", clock); print_field("ID: 0x%2.2x", id); break; case 0x03: len = size - 8; count = get_le16(data + 3); print_field("Count: 0x%4.4x", count); packet_hexdump(data + 3 + 2 + 1, 2); llcp_packet(data + 8, len, false); break; case 0x04: len = size - 8; count = get_le16(data + 3); id = get_u8(data + 3 + 2); print_field("Count: 0x%4.4x", count); print_field("ID: 0x%2.2x", id); packet_hexdump(data + 3 + 2 + 1, 2); llcp_packet(data + 8, len, false); break; case 0x05: count = get_le16(data + 3); id = get_u8(data + 3 + 2); print_field("Count: 0x%4.4x", count); print_field("ID: 0x%2.2x", id); break; default: packet_hexdump(data + 3, size - 3); break; } } static void write_bd_data_complete_evt(struct timeval *tv, uint16_t index, const void *data, uint8_t size) { uint8_t status = get_u8(data); print_status(status); } static void sco_rejected_via_lmp_evt(struct timeval *tv, uint16_t index, const void *data, uint8_t size) { uint8_t reason = get_u8(data + 6); packet_print_addr("Address", data, 0x00); packet_print_error("Reason", reason); } static void ptt_switch_notification_evt(struct timeval *tv, uint16_t index, const void *data, uint8_t size) { uint16_t handle = get_le16(data); uint8_t table = get_u8(data + 2); const char *str; print_field("Handle: %u", handle); switch (table) { case 0x00: str = "Basic rate"; break; case 0x01: str = "Enhanced data rate"; break; default: str = "Reserved"; break; } print_field("Packet type table: %s (0x%2.2x)", str, table); } static void system_exception_evt(struct timeval *tv, uint16_t index, const void *data, uint8_t size) { uint8_t type = get_u8(data); const char *str; switch (type) { case 0x00: str = "No Exception"; break; case 0x01: str = "Undefined Instruction"; break; case 0x02: str = "Prefetch abort"; break; case 0x03: str = "Data abort"; break; default: str = "Reserved"; break; } print_field("Type: %s (0x%2.2x)", str, type); packet_hexdump(data + 1, size - 1); } static const struct vendor_evt vendor_evt_table[] = { { 0x00, "Startup", startup_evt, 0, true }, { 0x01, "Fatal Exception", fatal_exception_evt, 4, true }, { 0x02, "Bootup", bootup_evt, 6, true }, { 0x05, "Default BD Data", default_bd_data_evt, 1, true }, { 0x06, "Secure Send Commands Result", secure_send_commands_result_evt, 4, true }, { 0x08, "Debug Exception", debug_exception_evt, 4, true }, { 0x0f, "LE Link Established", le_link_established_evt, 26, true }, { 0x11, "Scan Status", scan_status_evt, 1, true }, { 0x16, "Activate Deactivate Traces Complete", act_deact_traces_complete_evt, 1, true }, { 0x17, "LMP PDU Trace", lmp_pdu_trace_evt, 3, false }, { 0x19, "Write BD Data Complete", write_bd_data_complete_evt, 1, true }, { 0x25, "SCO Rejected via LMP", sco_rejected_via_lmp_evt, 7, true }, { 0x26, "PTT Switch Notification", ptt_switch_notification_evt, 3, true }, { 0x29, "System Exception", system_exception_evt, 133, true }, { 0x2c, "FW Trace String" }, { 0x2e, "FW Trace Binary" }, { } }; /* * An Intel telemetry subevent is of the TLV format. * - Type: takes 1 byte. This is the subevent_id. * - Length: takes 1 byte. * - Value: takes |Length| bytes. */ struct intel_tlv { uint8_t subevent_id; uint8_t length; uint8_t value[]; }; #define TLV_SIZE(tlv) (*((const uint8_t *) tlv + 1) + 2 * sizeof(uint8_t)) #define NEXT_TLV(tlv) (const struct intel_tlv *) \ ((const uint8_t *) tlv + TLV_SIZE(tlv)) static void ext_evt_type(const struct intel_tlv *tlv) { uint8_t evt_type = get_u8(tlv->value); const char *str; switch (evt_type) { case 0x00: str = "System Exception"; break; case 0x01: str = "Fatal Exception"; break; case 0x02: str = "Debug Exception"; break; case 0x03: str = "Connection Event for BR/EDR Link Type"; break; case 0x04: str = "Disconnection Event"; break; case 0x05: str = "Performance Stats"; break; default: print_text(COLOR_UNKNOWN_EXT_EVENT, "Unknown extended telemetry event type (0x%2.2x)", evt_type); packet_hexdump((const void *) tlv, tlv->length + 2 * sizeof(uint8_t)); return; } print_field("Extended event type (0x%2.2x): %s (0x%2.2x)", tlv->subevent_id, str, evt_type); } static void ext_acl_evt_conn_handle(const struct intel_tlv *tlv) { uint16_t conn_handle = get_le16(tlv->value); print_field("ACL connection handle (0x%2.2x): 0x%4.4x", tlv->subevent_id, conn_handle); } static void ext_acl_evt_hec_errors(const struct intel_tlv *tlv) { uint32_t num = get_le32(tlv->value); /* Skip if 0 */ if (!num) return; print_field("Rx HEC errors (0x%2.2x): %d", tlv->subevent_id, num); } static void ext_acl_evt_crc_errors(const struct intel_tlv *tlv) { uint32_t num = get_le32(tlv->value); /* Skip if 0 */ if (!num) return; print_field("Rx CRC errors (0x%2.2x): %d", tlv->subevent_id, num); } static void ext_acl_evt_num_pkt_from_host(const struct intel_tlv *tlv) { uint32_t num = get_le32(tlv->value); /* Skip if 0 */ if (!num) return; print_field("Packets from host (0x%2.2x): %d", tlv->subevent_id, num); } static void ext_acl_evt_num_tx_pkt_to_air(const struct intel_tlv *tlv) { uint32_t num = get_le32(tlv->value); /* Skip if 0 */ if (!num) return; print_field("Tx packets (0x%2.2x): %d", tlv->subevent_id, num); } static void ext_acl_evt_num_tx_pkt_retry(const struct intel_tlv *tlv) { char *subevent_str; uint32_t num = get_le32(tlv->value); /* Skip if 0 */ if (!num) return; switch (tlv->subevent_id) { case 0x4f: subevent_str = "Tx packets 0 retries"; break; case 0x50: subevent_str = "Tx packets 1 retries"; break; case 0x51: subevent_str = "Tx packets 2 retries"; break; case 0x52: subevent_str = "Tx packets 3 retries"; break; case 0x53: subevent_str = "Tx packets 4 retries and more"; break; default: subevent_str = "Unknown"; break; } print_field("%s (0x%2.2x): %d", subevent_str, tlv->subevent_id, num); } static void ext_acl_evt_num_tx_pkt_type(const struct intel_tlv *tlv) { char *packet_type_str; uint32_t num = get_le32(tlv->value); /* Skip if 0 */ if (!num) return; switch (tlv->subevent_id) { case 0x54: packet_type_str = "DH1"; break; case 0x55: packet_type_str = "DH3"; break; case 0x56: packet_type_str = "DH5"; break; case 0x57: packet_type_str = "2DH1"; break; case 0x58: packet_type_str = "2DH3"; break; case 0x59: packet_type_str = "2DH5"; break; case 0x5a: packet_type_str = "3DH1"; break; case 0x5b: packet_type_str = "3DH3"; break; case 0x5c: packet_type_str = "3DH5"; break; default: packet_type_str = "Unknown"; break; } print_field("Tx %s packets (0x%2.2x): %d", packet_type_str, tlv->subevent_id, num); } static void ext_acl_evt_num_rx_pkt_from_air(const struct intel_tlv *tlv) { uint32_t num = get_le32(tlv->value); /* Skip if 0 */ if (!num) return; print_field("Rx packets (0x%2.2x): %d", tlv->subevent_id, num); } static void ext_acl_evt_link_throughput(const struct intel_tlv *tlv) { uint32_t num = get_le32(tlv->value); /* Skip if 0 */ if (!num) return; print_field("ACL link throughput (bps) (0x%2.2x): %d", tlv->subevent_id, num); } static void ext_acl_evt_max_packet_latency(const struct intel_tlv *tlv) { uint32_t num = get_le32(tlv->value); /* Skip if 0 */ if (!num) return; print_field("ACL max packet latency (us) (0x%2.2x): %d", tlv->subevent_id, num); } static void ext_acl_evt_avg_packet_latency(const struct intel_tlv *tlv) { uint32_t num = get_le32(tlv->value); /* Skip if 0 */ if (!num) return; print_field("ACL avg packet latency (us) (0x%2.2x): %d", tlv->subevent_id, num); } static void ext_acl_evt_rssi_moving_avg(const struct intel_tlv *tlv) { uint32_t num = get_le16(tlv->value); /* Skip if 0 */ if (!num) return; print_field("ACL RX RSSI moving avg (0x%2.2x): %d", tlv->subevent_id, num); } static void ext_acl_evt_bad_cnt(const char *prefix, const struct intel_tlv *tlv) { uint32_t c_1m = get_le32(tlv->value); uint32_t c_2m = get_le32(tlv->value + 4); uint32_t c_3m = get_le32(tlv->value + 8); /* Skip if all 0 */ if (!c_1m && !c_2m && !c_3m) return; print_field("%s (0x%2.2x): 1M %d 2M %d 3M %d", prefix, tlv->subevent_id, c_1m, c_2m, c_3m); } static void ext_acl_evt_snr_bad_cnt(const struct intel_tlv *tlv) { ext_acl_evt_bad_cnt("ACL RX SNR Bad Margin Counter", tlv); } static void ext_acl_evt_rx_rssi_bad_cnt(const struct intel_tlv *tlv) { ext_acl_evt_bad_cnt("ACL RX RSSI Bad Counter", tlv); } static void ext_acl_evt_tx_rssi_bad_cnt(const struct intel_tlv *tlv) { ext_acl_evt_bad_cnt("ACL TX RSSI Bad Counter", tlv); } static void ext_sco_evt_conn_handle(const struct intel_tlv *tlv) { uint16_t conn_handle = get_le16(tlv->value); print_field("SCO/eSCO connection handle (0x%2.2x): 0x%4.4x", tlv->subevent_id, conn_handle); } static void ext_sco_evt_num_rx_pkt_from_air(const struct intel_tlv *tlv) { uint32_t num = get_le32(tlv->value); /* Skip if 0 */ if (!num) return; print_field("Packets from host (0x%2.2x): %d", tlv->subevent_id, num); } static void ext_sco_evt_num_tx_pkt_to_air(const struct intel_tlv *tlv) { uint32_t num = get_le32(tlv->value); /* Skip if 0 */ if (!num) return; print_field("Tx packets (0x%2.2x): %d", tlv->subevent_id, num); } static void ext_sco_evt_num_rx_payloads_lost(const struct intel_tlv *tlv) { uint32_t num = get_le32(tlv->value); /* Skip if 0 */ if (!num) return; print_field("Rx payload lost (0x%2.2x): %d", tlv->subevent_id, num); } static void ext_sco_evt_num_tx_payloads_lost(const struct intel_tlv *tlv) { uint32_t num = get_le32(tlv->value); /* Skip if 0 */ if (!num) return; print_field("Tx payload lost (0x%2.2x): %d", tlv->subevent_id, num); } static void slots_errors(const struct intel_tlv *tlv, const char *type_str) { /* The subevent has 5 slots where each slot is of the uint32_t type. */ uint32_t num[5]; const uint8_t *data = tlv->value; int i; if (tlv->length != 5 * sizeof(uint32_t)) { print_text(COLOR_UNKNOWN_EXT_EVENT, " Invalid subevent length (%d)", tlv->length); return; } for (i = 0; i < 5; i++) { num[i] = get_le32(data); data += sizeof(uint32_t); } print_field("%s (0x%2.2x): %d %d %d %d %d", type_str, tlv->subevent_id, num[0], num[1], num[2], num[3], num[4]); } static void ext_sco_evt_num_no_sync_errors(const struct intel_tlv *tlv) { slots_errors(tlv, "Rx No SYNC errors"); } static void ext_sco_evt_num_hec_errors(const struct intel_tlv *tlv) { slots_errors(tlv, "Rx HEC errors"); } static void ext_sco_evt_num_crc_errors(const struct intel_tlv *tlv) { slots_errors(tlv, "Rx CRC errors"); } static void ext_sco_evt_num_naks(const struct intel_tlv *tlv) { slots_errors(tlv, "Rx NAK errors"); } static void ext_sco_evt_num_failed_tx_by_wifi(const struct intel_tlv *tlv) { slots_errors(tlv, "Failed Tx due to Wifi coex"); } static void ext_sco_evt_num_failed_rx_by_wifi(const struct intel_tlv *tlv) { slots_errors(tlv, "Failed Rx due to Wifi coex"); } static void ext_sco_evt_samples_inserted(const struct intel_tlv *tlv) { uint32_t num = get_le32(tlv->value); /* Skip if 0 */ if (!num) return; print_field("Late samples inserted based on CDC (0x%2.2x): %d", tlv->subevent_id, num); } static void ext_sco_evt_samples_dropped(const struct intel_tlv *tlv) { uint32_t num = get_le32(tlv->value); /* Skip if 0 */ if (!num) return; print_field("Samples dropped (0x%2.2x): %d", tlv->subevent_id, num); } static void ext_sco_evt_mute_samples(const struct intel_tlv *tlv) { uint32_t num = get_le32(tlv->value); /* Skip if 0 */ if (!num) return; print_field("Mute samples sent at initial connection (0x%2.2x): %d", tlv->subevent_id, num); } static void ext_sco_evt_plc_injection_data(const struct intel_tlv *tlv) { uint32_t num = get_le32(tlv->value); /* Skip if 0 */ if (!num) return; print_field("PLC injection data (0x%2.2x): %d", tlv->subevent_id, num); } static const struct intel_ext_subevent { uint8_t subevent_id; uint8_t length; void (*func)(const struct intel_tlv *tlv); } intel_ext_subevent_table[] = { { 0x01, 1, ext_evt_type }, /* ACL audio link quality subevents */ { 0x4a, 2, ext_acl_evt_conn_handle }, { 0x4b, 4, ext_acl_evt_hec_errors }, { 0x4c, 4, ext_acl_evt_crc_errors }, { 0x4d, 4, ext_acl_evt_num_pkt_from_host }, { 0x4e, 4, ext_acl_evt_num_tx_pkt_to_air }, { 0x4f, 4, ext_acl_evt_num_tx_pkt_retry }, { 0x50, 4, ext_acl_evt_num_tx_pkt_retry }, { 0x51, 4, ext_acl_evt_num_tx_pkt_retry }, { 0x52, 4, ext_acl_evt_num_tx_pkt_retry }, { 0x53, 4, ext_acl_evt_num_tx_pkt_retry }, { 0x54, 4, ext_acl_evt_num_tx_pkt_type }, { 0x55, 4, ext_acl_evt_num_tx_pkt_type }, { 0x56, 4, ext_acl_evt_num_tx_pkt_type }, { 0x57, 4, ext_acl_evt_num_tx_pkt_type }, { 0x58, 4, ext_acl_evt_num_tx_pkt_type }, { 0x59, 4, ext_acl_evt_num_tx_pkt_type }, { 0x5a, 4, ext_acl_evt_num_tx_pkt_type }, { 0x5b, 4, ext_acl_evt_num_tx_pkt_type }, { 0x5c, 4, ext_acl_evt_num_tx_pkt_type }, { 0x5d, 4, ext_acl_evt_num_rx_pkt_from_air }, { 0x5e, 4, ext_acl_evt_link_throughput }, { 0x5f, 4, ext_acl_evt_max_packet_latency }, { 0x60, 4, ext_acl_evt_avg_packet_latency }, { 0x61, 2, ext_acl_evt_rssi_moving_avg }, { 0x62, 12, ext_acl_evt_snr_bad_cnt }, { 0x63, 12, ext_acl_evt_rx_rssi_bad_cnt }, { 0x64, 12, ext_acl_evt_tx_rssi_bad_cnt }, /* SCO/eSCO audio link quality subevents */ { 0x6a, 2, ext_sco_evt_conn_handle }, { 0x6b, 4, ext_sco_evt_num_rx_pkt_from_air }, { 0x6c, 4, ext_sco_evt_num_tx_pkt_to_air }, { 0x6d, 4, ext_sco_evt_num_rx_payloads_lost }, { 0x6e, 4, ext_sco_evt_num_tx_payloads_lost }, { 0x6f, 20, ext_sco_evt_num_no_sync_errors }, { 0x70, 20, ext_sco_evt_num_hec_errors }, { 0x71, 20, ext_sco_evt_num_crc_errors }, { 0x72, 20, ext_sco_evt_num_naks }, { 0x73, 20, ext_sco_evt_num_failed_tx_by_wifi }, { 0x74, 20, ext_sco_evt_num_failed_rx_by_wifi }, { 0x75, 4, ext_sco_evt_samples_inserted }, { 0x76, 4, ext_sco_evt_samples_dropped }, { 0x77, 4, ext_sco_evt_mute_samples }, { 0x78, 4, ext_sco_evt_plc_injection_data }, /* end */ { 0x0, 0} }; static const struct intel_tlv *process_ext_subevent(const struct intel_tlv *tlv, const struct intel_tlv *last_tlv) { const struct intel_tlv *next_tlv = NEXT_TLV(tlv); const struct intel_ext_subevent *subevent = NULL; int i; for (i = 0; intel_ext_subevent_table[i].length > 0; i++) { if (intel_ext_subevent_table[i].subevent_id == tlv->subevent_id) { subevent = &intel_ext_subevent_table[i]; break; } } if (!subevent) { print_text(COLOR_UNKNOWN_EXT_EVENT, "Unknown extended subevent 0x%2.2x", tlv->subevent_id); packet_hexdump(tlv->value, tlv->length); return next_tlv; } if (tlv->length != subevent->length) { print_text(COLOR_ERROR, "Invalid length %d of subevent 0x%2.2x", tlv->length, tlv->subevent_id); return NULL; } if (next_tlv > last_tlv) { print_text(COLOR_ERROR, "Subevent exceeds the buffer size."); return NULL; } subevent->func(tlv); return next_tlv; } static void intel_vendor_ext_evt(struct timeval *tv, uint16_t index, const void *data, uint8_t size) { /* The data pointer points to a number of tlv.*/ const struct intel_tlv *tlv = data; const struct intel_tlv *last_tlv = data + size; /* Process every tlv subevent until reaching last_tlv. * The decoding process terminates normally when tlv == last_tlv. */ while (tlv && tlv < last_tlv) tlv = process_ext_subevent(tlv, last_tlv); /* If an error occurs in decoding the subevents, hexdump the packet. */ if (!tlv) packet_hexdump(data, size); } /* Vendor extended events with a vendor prefix. */ static const struct vendor_evt vendor_prefix_evt_table[] = { { 0x03, "Extended Telemetry", intel_vendor_ext_evt }, { } }; static const uint8_t intel_vendor_prefix[] = {0x87, 0x80}; #define INTEL_VENDOR_PREFIX_SIZE sizeof(intel_vendor_prefix) /* * The vendor event with Intel vendor prefix. * Its format looks like * 0xff * where Intel's is 0x8780. * * When == 0x03, it is a telemetry event; and * is a number of tlv data. */ struct vendor_prefix_evt { uint8_t prefix_data[INTEL_VENDOR_PREFIX_SIZE]; uint8_t subopcode; }; static const struct vendor_evt *intel_vendor_prefix_evt(const void *data, int *consumed_size) { unsigned int i; const struct vendor_prefix_evt *vnd = data; char prefix_string[INTEL_VENDOR_PREFIX_SIZE * 2 + 1] = { 0 }; /* Check if the vendor prefix matches. */ for (i = 0; i < INTEL_VENDOR_PREFIX_SIZE; i++) { if (vnd->prefix_data[i] != intel_vendor_prefix[i]) return NULL; sprintf(prefix_string + i * 2, "%02x", vnd->prefix_data[i]); } print_field("Vendor Prefix (0x%s)", prefix_string); /* * Handle the vendor event with a vendor prefix. * 0xff * This loop checks whether the exists in the * vendor_prefix_evt_table. */ for (i = 0; vendor_prefix_evt_table[i].str; i++) { if (vendor_prefix_evt_table[i].evt == vnd->subopcode) { *consumed_size = sizeof(struct vendor_prefix_evt); return &vendor_prefix_evt_table[i]; } } return NULL; } const struct vendor_evt *intel_vendor_evt(const void *data, int *consumed_size) { uint8_t evt = *((const uint8_t *) data); int i; /* * Handle the vendor event without a vendor prefix. * 0xff * This loop checks whether the exists in the vendor_evt_table. */ for (i = 0; vendor_evt_table[i].str; i++) { if (vendor_evt_table[i].evt == evt) return &vendor_evt_table[i]; } /* * It is not a regular event. Check whether it is a vendor extended * event that comes with a vendor prefix followed by a subopcode. */ return intel_vendor_prefix_evt(data, consumed_size); } bluez-5.82/monitor/PaxHeaders/broadcom.c0000644000000000000000000000005014447506754015307 xustar0020 atime=1743516058 20 ctime=1743591282 bluez-5.82/monitor/broadcom.c0000644000000000000000000003404414447506754014775 0ustar00rootroot// SPDX-License-Identifier: LGPL-2.1-or-later /* * * BlueZ - Bluetooth protocol stack for Linux * * Copyright (C) 2011-2014 Intel Corporation * Copyright (C) 2002-2010 Marcel Holtmann * * */ #ifdef HAVE_CONFIG_H #include #endif #define _GNU_SOURCE #include #include #include "src/shared/util.h" #include "display.h" #include "packet.h" #include "lmp.h" #include "ll.h" #include "vendor.h" #include "broadcom.h" #define COLOR_UNKNOWN_FEATURE_BIT COLOR_WHITE_BG static void print_status(uint8_t status) { packet_print_error("Status", status); } static void print_handle(uint16_t handle) { packet_print_handle(handle); } static void print_rssi(int8_t rssi) { packet_print_rssi("RSSI", rssi); } static void print_sco_routing(uint8_t routing) { const char *str; switch (routing) { case 0x00: str = "PCM"; break; case 0x01: str = "Transport"; break; case 0x02: str = "Codec"; break; case 0x03: str = "I2S"; break; default: str = "Reserved"; break; } print_field("SCO routing: %s (0x%2.2x)", str, routing); } static void print_pcm_interface_rate(uint8_t rate) { const char *str; switch (rate) { case 0x00: str = "128 KBps"; break; case 0x01: str = "256 KBps"; break; case 0x02: str = "512 KBps"; break; case 0x03: str = "1024 KBps"; break; case 0x04: str = "2048 KBps"; break; default: str = "Reserved"; break; } print_field("PCM interface rate: %s (0x%2.2x)", str, rate); } static void print_frame_type(uint8_t type) { const char *str; switch (type) { case 0x00: str = "Short"; break; case 0x01: str = "Long"; break; default: str = "Reserved"; break; } print_field("Frame type: %s (0x%2.2x)", str, type); } static void print_sync_mode(uint8_t mode) { const char *str; switch (mode) { case 0x00: str = "Peripheral"; break; case 0x01: str = "Central"; break; default: str = "Reserved"; break; } print_field("Sync mode: %s (0x%2.2x)", str, mode); } static void print_clock_mode(uint8_t mode) { const char *str; switch (mode) { case 0x00: str = "Peripheral"; break; case 0x01: str = "Central"; break; default: str = "Reserved"; break; } print_field("Clock mode: %s (0x%2.2x)", str, mode); } static void print_sleep_mode(uint8_t mode) { const char *str; switch (mode) { case 0x00: str = "No sleep mode"; break; case 0x01: str = "UART"; break; case 0x02: str = "UART with messaging"; break; case 0x03: str = "USB"; break; case 0x04: str = "H4IBSS"; break; case 0x05: str = "USB with Host wake"; break; case 0x06: str = "SDIO"; break; case 0x07: str = "UART CS-N"; break; case 0x08: str = "SPI"; break; case 0x09: str = "H5"; break; case 0x0a: str = "H4DS"; break; case 0x0c: str = "UART with BREAK"; break; default: str = "Reserved"; break; } print_field("Sleep mode: %s (0x%2.2x)", str, mode); } static void print_clock_setting(uint8_t clock) { const char *str; switch (clock) { case 0x01: str = "48 Mhz"; break; case 0x02: str = "24 Mhz"; break; default: str = "Reserved"; break; } print_field("UART clock: %s (0x%2.2x)", str, clock); } static void null_cmd(uint16_t index, const void *data, uint8_t size) { } static void status_rsp(uint16_t index, const void *data, uint8_t size) { uint8_t status = get_u8(data); print_status(status); } static void write_bd_addr_cmd(uint16_t index, const void *data, uint8_t size) { packet_print_addr("Address", data, 0x00); } static void update_uart_baud_rate_cmd(uint16_t index, const void *data, uint8_t size) { uint16_t enc_rate = get_le16(data); uint32_t exp_rate = get_le32(data + 2); if (enc_rate == 0x0000) print_field("Encoded baud rate: Not used (0x0000)"); else print_field("Encoded baud rate: 0x%4.4x", enc_rate); print_field("Explicit baud rate: %u Mbps", exp_rate); } static void write_sco_pcm_int_param_cmd(uint16_t index, const void *data, uint8_t size) { uint8_t routing = get_u8(data); uint8_t rate = get_u8(data + 1); uint8_t frame_type = get_u8(data + 2); uint8_t sync_mode = get_u8(data + 3); uint8_t clock_mode = get_u8(data + 4); print_sco_routing(routing); print_pcm_interface_rate(rate); print_frame_type(frame_type); print_sync_mode(sync_mode); print_clock_mode(clock_mode); } static void read_sco_pcm_int_param_rsp(uint16_t index, const void *data, uint8_t size) { uint8_t status = get_u8(data); uint8_t routing = get_u8(data + 1); uint8_t rate = get_u8(data + 2); uint8_t frame_type = get_u8(data + 3); uint8_t sync_mode = get_u8(data + 4); uint8_t clock_mode = get_u8(data + 5); print_status(status); print_sco_routing(routing); print_pcm_interface_rate(rate); print_frame_type(frame_type); print_sync_mode(sync_mode); print_clock_mode(clock_mode); } static void set_sleepmode_param_cmd(uint16_t index, const void *data, uint8_t size) { uint8_t mode = get_u8(data); print_sleep_mode(mode); packet_hexdump(data + 1, size - 1); } static void read_sleepmode_param_rsp(uint16_t index, const void *data, uint8_t size) { uint8_t status = get_u8(data); uint8_t mode = get_u8(data + 1); print_status(status); print_sleep_mode(mode); packet_hexdump(data + 2, size - 2); } static void enable_radio_cmd(uint16_t index, const void *data, uint8_t size) { uint8_t mode = get_u8(data); const char *str; switch (mode) { case 0x00: str = "Disable the radio"; break; case 0x01: str = "Enable the radio"; break; default: str = "Reserved"; break; } print_field("Mode: %s (0x%2.2x)", str, mode); } static void enable_usb_hid_emulation_cmd(uint16_t index, const void *data, uint8_t size) { uint8_t enable = get_u8(data); const char *str; switch (enable) { case 0x00: str = "Bluetooth mode"; break; case 0x01: str = "HID Mode"; break; default: str = "Reserved"; break; } print_field("Enable: %s (0x%2.2x)", str, enable); } static void read_uart_clock_setting_rsp(uint16_t index, const void *data, uint8_t size) { uint8_t status = get_u8(data); uint8_t clock = get_u8(data + 1); print_status(status); print_clock_setting(clock); } static void write_uart_clock_setting_cmd(uint16_t index, const void *data, uint8_t size) { uint8_t clock = get_u8(data); print_clock_setting(clock); } static void read_raw_rssi_cmd(uint16_t index, const void *data, uint8_t size) { uint16_t handle = get_le16(data); print_handle(handle); } static void read_raw_rssi_rsp(uint16_t index, const void *data, uint8_t size) { uint8_t status = get_u8(data); uint16_t handle = get_le16(data + 1); int8_t rssi = get_s8(data + 3); print_status(status); print_handle(handle); print_rssi(rssi); } static void write_ram_cmd(uint16_t index, const void *data, uint8_t size) { uint32_t addr = get_le32(data); print_field("Address: 0x%8.8x", addr); packet_hexdump(data + 4, size - 4); } static void read_ram_cmd(uint16_t index, const void *data, uint8_t size) { uint32_t addr = get_le32(data); uint8_t length = get_u8(data + 4); print_field("Address: 0x%8.8x", addr); print_field("Length: %u", length); } static void read_ram_rsp(uint16_t index, const void *data, uint8_t size) { uint8_t status = get_u8(data); print_status(status); packet_hexdump(data + 1, size - 1); } static void launch_ram_cmd(uint16_t index, const void *data, uint8_t size) { uint32_t addr = get_le32(data); print_field("Address: 0x%8.8x", addr); } static void read_vid_pid_rsp(uint16_t index, const void *data, uint8_t size) { uint8_t status = get_u8(data); uint16_t vid = get_le16(data + 1); uint16_t pid = get_le16(data + 3); print_status(status); print_field("Product: %4.4x:%4.4x", vid, pid); } static void write_high_priority_connection_cmd(uint16_t index, const void *data, uint8_t size) { uint16_t handle = get_le16(data); uint8_t priority = get_u8(data + 2); const char *str; print_handle(handle); switch (priority) { case 0x00: str = "Low"; break; case 0x01: str = "High"; break; default: str = "Reserved"; break; } print_field("Priority: %s (0x%2.2x)", str, priority); } static const struct { uint8_t bit; const char *str; } features_table[] = { { 0, "Multi-AV transport bandwidth reducer" }, { 1, "WBS SBC" }, { 2, "FW LC-PLC" }, { 3, "FM SBC internal stack" }, { } }; static void print_features(const uint8_t *features_array) { uint64_t mask, features = 0; char str[41]; int i; for (i = 0; i < 8; i++) { sprintf(str + (i * 5), " 0x%2.2x", features_array[i]); features |= ((uint64_t) features_array[i]) << (i * 8); } print_field("Features:%s", str); mask = features; for (i = 0; features_table[i].str; i++) { if (features & (((uint64_t) 1) << features_table[i].bit)) { print_field(" %s", features_table[i].str); mask &= ~(((uint64_t) 1) << features_table[i].bit); } } if (mask) print_text(COLOR_UNKNOWN_FEATURE_BIT, " Unknown features " "(0x%16.16" PRIx64 ")", mask); } static void read_controller_features_rsp(uint16_t index, const void *data, uint8_t size) { uint8_t status = get_u8(data); print_status(status); print_features(data + 1); } static void read_verbose_version_info_rsp(uint16_t index, const void *data, uint8_t size) { uint8_t status = get_u8(data); uint8_t chip_id = get_u8(data + 1); uint8_t target_id = get_u8(data + 2); uint16_t build_base = get_le16(data + 3); uint16_t build_num = get_le16(data + 5); const char *str; print_status(status); print_field("Chip ID: %u (0x%2.2x)", chip_id, chip_id); switch (target_id) { case 254: str = "Invalid"; break; case 255: str = "Undefined"; break; default: str = "Reserved"; break; } print_field("Build target: %s (%u)", str, target_id); print_field("Build baseline: %u (0x%4.4x)", build_base, build_base); print_field("Build number: %u (0x%4.4x)", build_num, build_num); } static void enable_wbs_cmd(uint16_t index, const void *data, uint8_t size) { uint8_t mode = get_u8(data); uint16_t codec = get_le16(data + 1); const char *str; switch (mode) { case 0x00: str = "Disable WBS"; break; case 0x01: str = "Enable WBS"; break; default: str = "Reserved"; break; } print_field("Mode: %s (0x%2.2x)", str, mode); switch (codec) { case 0x0000: str = "None"; break; case 0x0001: str = "CVSD"; break; case 0x0002: str = "mSBC"; break; default: str = "Reserved"; break; } print_field("Codec: %s (0x%4.4x)", str, codec); } static const struct vendor_ocf vendor_ocf_table[] = { { 0x001, "Write BD ADDR", write_bd_addr_cmd, 6, true, status_rsp, 1, true }, { 0x018, "Update UART Baud Rate", update_uart_baud_rate_cmd, 6, true, status_rsp, 1, true }, { 0x01c, "Write SCO PCM Int Param", write_sco_pcm_int_param_cmd, 5, true, status_rsp, 1, true }, { 0x01d, "Read SCO PCM Int Param", null_cmd, 0, true, read_sco_pcm_int_param_rsp, 6, true }, { 0x027, "Set Sleepmode Param", set_sleepmode_param_cmd, 12, true, status_rsp, 1, true }, { 0x028, "Read Sleepmode Param", null_cmd, 0, true, read_sleepmode_param_rsp, 13, true }, { 0x02e, "Download Minidriver", null_cmd, 0, true, status_rsp, 1, true }, { 0x034, "Enable Radio", enable_radio_cmd, 1, true, status_rsp, 1, true }, { 0x03b, "Enable USB HID Emulation", enable_usb_hid_emulation_cmd, 1, true, status_rsp, 1, true }, { 0x044, "Read UART Clock Setting", null_cmd, 0, true, read_uart_clock_setting_rsp, 1, true }, { 0x045, "Write UART Clock Setting", write_uart_clock_setting_cmd, 1, true, status_rsp, 1, true }, { 0x048, "Read Raw RSSI", read_raw_rssi_cmd, 2, true, read_raw_rssi_rsp, 4, true }, { 0x04c, "Write RAM", write_ram_cmd, 4, false, status_rsp, 1, true }, { 0x04d, "Read RAM", read_ram_cmd, 5, true, read_ram_rsp, 1, false }, { 0x04e, "Launch RAM", launch_ram_cmd, 4, true, status_rsp, 1, true }, { 0x05a, "Read VID PID", null_cmd, 0, true, read_vid_pid_rsp, 5, true }, { 0x057, "Write High Priority Connection", write_high_priority_connection_cmd, 3, true, status_rsp, 1, true }, { 0x06d, "Write I2SPCM Interface Param" }, { 0x06e, "Read Controller Features", null_cmd, 0, true, read_controller_features_rsp, 9, true }, { 0x079, "Read Verbose Config Version Info", null_cmd, 0, true, read_verbose_version_info_rsp, 7, true }, { 0x07e, "Enable WBS", enable_wbs_cmd, 3, true, status_rsp, 1, true }, { } }; const struct vendor_ocf *broadcom_vendor_ocf(uint16_t ocf) { int i; for (i = 0; vendor_ocf_table[i].str; i++) { if (vendor_ocf_table[i].ocf == ocf) return &vendor_ocf_table[i]; } return NULL; } void broadcom_lm_diag(const void *data, uint8_t size) { uint8_t type; uint32_t clock; const uint8_t *addr; const char *str; if (size != 63) { packet_hexdump(data, size); return; } type = *((uint8_t *) data); clock = get_be32(data + 1); switch (type) { case 0x00: str = "LMP sent"; break; case 0x01: str = "LMP receive"; break; case 0x80: str = "LL sent"; break; case 0x81: str = "LL receive"; break; default: str = "Unknown"; break; } print_field("Type: %s (%u)", str, type); print_field("Clock: 0x%8.8x", clock); switch (type) { case 0x00: addr = data + 5; print_field("Address: --:--:%2.2X:%2.2X:%2.2X:%2.2X", addr[0], addr[1], addr[2], addr[3]); packet_hexdump(data + 9, 1); lmp_packet(data + 10, size - 10, true); break; case 0x01: addr = data + 5; print_field("Address: --:--:%2.2X:%2.2X:%2.2X:%2.2X", addr[0], addr[1], addr[2], addr[3]); packet_hexdump(data + 9, 4); lmp_packet(data + 13, size - 13, true); break; case 0x80: case 0x81: packet_hexdump(data + 5, 7); llcp_packet(data + 12, size - 12, true); break; default: packet_hexdump(data + 9, size - 9); break; } } static void lm_diag_evt(struct timeval *tv, uint16_t index, const void *data, uint8_t size) { broadcom_lm_diag(data, 63); } static const struct vendor_evt vendor_evt_table[] = { { 0xb4, "LM Diag", lm_diag_evt, 64, true }, { } }; const struct vendor_evt *broadcom_vendor_evt(uint8_t evt) { int i; for (i = 0; vendor_evt_table[i].str; i++) { if (vendor_evt_table[i].evt == evt) return &vendor_evt_table[i]; } return NULL; } bluez-5.82/monitor/PaxHeaders/jlink.h0000644000000000000000000000005014015011623014606 xustar0020 atime=1743515954 20 ctime=1743591282 bluez-5.82/monitor/jlink.h0000644000000000000000000000042514015011623014270 0ustar00rootroot/* SPDX-License-Identifier: LGPL-2.1-or-later */ /* * * BlueZ - Bluetooth protocol stack for Linux * * Copyright (C) 2018 Codecoup * * */ int jlink_init(void); int jlink_connect(char *cfg); int jlink_start_rtt(char *cfg); int jlink_rtt_read(void *buf, size_t size); bluez-5.82/monitor/PaxHeaders/btmon.rst0000644000000000000000000000005014766002272015214 xustar0020 atime=1743515578 20 ctime=1743591291 bluez-5.82/monitor/btmon.rst0000644000000000000000000001052714766002272014702 0ustar00rootroot===== btmon ===== ----------------- Bluetooth monitor ----------------- :Authors: - Marcel Holtmann - Tedd Ho-Jeong An :Copyright: Free use of this software is granted under the terms of the GNU Lesser General Public Licenses (LGPL). :Version: BlueZ :Date: April 2021 :Manual section: 1 :Manual group: Linux System Administration SYNOPSYS ======== **btmon** [*OPTIONS* ...] DESCRIPTION =========== The btmon(1) command provides access to the Bluetooth subsystem monitor infrastructure for reading HCI traces. OPTIONS ======= -r FILE, --read FILE Read traces in btsnoop format from *FILE*. -w FILE, --write FILE Save traces in btsnoop format to *FILE*. -a FILE, --analyze FILE Analyze traces in btsnoop format from *FILE*. It displays the devices found in the *FILE* with its packets by type. If gnuplot is installed on the system it also attempts to plot packet latency graph. -s SOCKET, --server SOCKET Start monitor server socket. -p PRIORITY, --priority PRIORITY Show only priority or lower for user log. .. list-table:: :header-rows: 1 :widths: auto :stub-columns: 1 * - *PRIORITY* - NAME * - **3** - Error * - **4** - Warning * - **6** - Information (Default) * - **7** - Debug. **debug** can be used. -i NUM, --index NUM Show only specified controller. *hciNUM* is also acceptable. This is useful to capture the traces from the specific controller when the multiple controllers are presented. -d TTY, --tty TTY Read data from *TTY*. -B SPEED, --rate SPEED Set TTY speed. The default *SPEED* is 115300 -V COMPID, --vendor COMPID Set the default company identifier. The *COMPID* is a unique number assigned by the Bluetooth SIG to a member company and can be found/searched from the Bluetooth SIG webpage. For example, Intel is 2 and Realtek is 93. -M, --mgmt Open channel for mgmt events. -t, --time Show a time instead of time offset. -T, --date Show a time and date information instead of time offset. -S, --sco Dump SCO traffic in raw hex format. -A, --a2dp Dump A2DP stream traffic in a raw hex format. -E IP, --ellisys IP Send Ellisys HCI Injection. -P, --no-pager Disable pager usage while reading the log file. -J OPTIONS, --jlink OPTIONS Read data from RTT. Each options are comma(,) seprated without spaces. .. list-table:: :header-rows: 1 :widths: auto :stub-columns: 1 * - *OPTIONS* - Description * - **DEVICE** - Required. Set the target device. * - **SERIALNO** - (Optional) Set the USB serial number. Default is **0**. * - **INTERFACE** - (Optional) Target interface. Default is **swd**. * - **SPEED** - (Optional) Set target interface speed in kHz. Default is **1000**. -R OPTIONS, --rtt OPTIONS RTT control block parameters. Each options are comma(,) seprated without spaces. .. list-table:: :header-rows: 1 :widths: auto :stub-columns: 1 * - *OPTIONS* - Description * - **ADDRESS** - (Optional) Address of RTT buffer. Default is **0x00** * - **AREA** - (Optional) Size of range to search in RTT buffer. Default is **0** * - **NAME** - (Optional) Buffer name. Default is **btmonitor** -C WIDTH, --columns WIDTH Output width if not a terminal -c MODE, --color MODE Set output color. The possible *MODE* values are: **auto|always|never**. Default value is **auto** -v, --version Show version -h, --help Show help options EXAMPLES ======== Capture the traces from hci0 to hcidump.log file ------------------------------------------------ .. code-block:: $ btmon -i hci0 -w hcidump.log Open the trace file ------------------- .. code-block:: $ btmon -r hcidump.log RESOURCES ========= http://www.bluez.org REPORTING BUGS ============== linux-bluetooth@vger.kernel.org bluez-5.82/monitor/PaxHeaders/packet.h0000644000000000000000000000005014766002272014763 xustar0020 atime=1743515578 20 ctime=1743591282 bluez-5.82/monitor/packet.h0000644000000000000000000001212714766002272014447 0ustar00rootroot/* SPDX-License-Identifier: LGPL-2.1-or-later */ /* * * BlueZ - Bluetooth protocol stack for Linux * * Copyright (C) 2011-2014 Intel Corporation * Copyright (C) 2002-2010 Marcel Holtmann * * */ #include #include #include #include #define PACKET_FILTER_SHOW_INDEX (1 << 0) #define PACKET_FILTER_SHOW_DATE (1 << 1) #define PACKET_FILTER_SHOW_TIME (1 << 2) #define PACKET_FILTER_SHOW_TIME_OFFSET (1 << 3) #define PACKET_FILTER_SHOW_ACL_DATA (1 << 4) #define PACKET_FILTER_SHOW_SCO_DATA (1 << 5) #define PACKET_FILTER_SHOW_A2DP_STREAM (1 << 6) #define PACKET_FILTER_SHOW_MGMT_SOCKET (1 << 7) #define PACKET_FILTER_SHOW_ISO_DATA (1 << 8) #define TV_MSEC(_tv) (long long)((_tv).tv_sec * 1000 + (_tv).tv_usec / 1000) struct packet_latency { struct timeval total; struct timeval min; struct timeval max; struct timeval med; }; struct packet_frame { struct timeval tv; size_t num; size_t len; }; struct packet_conn_data { uint16_t index; uint8_t src[6]; uint16_t handle; uint16_t link; uint8_t type; uint8_t dst[6]; uint8_t dst_type; struct queue *tx_q; struct queue *chan_q; struct packet_latency tx_l; void *data; void (*destroy)(struct packet_conn_data *conn, void *data); }; struct packet_conn_data *packet_get_conn_data(uint16_t handle); void packet_latency_add(struct packet_latency *latency, struct timeval *delta); bool packet_has_filter(unsigned long filter); void packet_set_filter(unsigned long filter); void packet_add_filter(unsigned long filter); void packet_del_filter(unsigned long filter); void packet_set_priority(const char *priority); void packet_select_index(uint16_t index); void packet_set_fallback_manufacturer(uint16_t manufacturer); void packet_set_msft_evt_prefix(const uint8_t *prefix, uint8_t len); void packet_hexdump(const unsigned char *buf, uint16_t len); void packet_print_error(const char *label, uint8_t error); void packet_print_version(const char *label, uint8_t version, const char *sublabel, uint16_t subversion); void packet_print_company(const char *label, uint16_t company); void packet_print_addr(const char *label, const void *data, uint8_t type); void packet_print_handle(uint16_t handle); void packet_print_rssi(const char *label, int8_t rssi); void packet_print_ad(const void *data, uint8_t size); void packet_print_features_lmp(const uint8_t *features, uint8_t page); void packet_print_features_ll(const uint8_t *features); void packet_print_features_msft(const uint8_t *features); void packet_print_channel_map_lmp(const uint8_t *map); void packet_print_channel_map_ll(const uint8_t *map); void packet_print_io_capability(uint8_t capability); void packet_print_io_authentication(uint8_t authentication); void packet_print_codec_id(const char *label, uint8_t codec); void packet_control(struct timeval *tv, struct ucred *cred, uint16_t index, uint16_t opcode, const void *data, uint16_t size); void packet_monitor(struct timeval *tv, struct ucred *cred, uint16_t index, uint16_t opcode, const void *data, uint16_t size); void packet_simulator(struct timeval *tv, uint16_t frequency, const void *data, uint16_t size); void packet_new_index(struct timeval *tv, uint16_t index, const char *label, uint8_t type, uint8_t bus, const char *name); void packet_del_index(struct timeval *tv, uint16_t index, const char *label); void packet_open_index(struct timeval *tv, uint16_t index, const char *label); void packet_close_index(struct timeval *tv, uint16_t index, const char *label); void packet_index_info(struct timeval *tv, uint16_t index, const char *label, uint16_t manufacturer); void packet_vendor_diag(struct timeval *tv, uint16_t index, uint16_t manufacturer, const void *data, uint16_t size); void packet_system_note(struct timeval *tv, struct ucred *cred, uint16_t index, const void *message); void packet_user_logging(struct timeval *tv, struct ucred *cred, uint16_t index, uint8_t priority, const char *ident, const void *data, uint16_t size); void packet_hci_command(struct timeval *tv, struct ucred *cred, uint16_t index, const void *data, uint16_t size); void packet_hci_event(struct timeval *tv, struct ucred *cred, uint16_t index, const void *data, uint16_t size); void packet_hci_acldata(struct timeval *tv, struct ucred *cred, uint16_t index, bool in, const void *data, uint16_t size); void packet_hci_scodata(struct timeval *tv, struct ucred *cred, uint16_t index, bool in, const void *data, uint16_t size); void packet_hci_isodata(struct timeval *tv, struct ucred *cred, uint16_t index, bool in, const void *data, uint16_t size); void packet_ctrl_open(struct timeval *tv, struct ucred *cred, uint16_t index, const void *data, uint16_t size); void packet_ctrl_close(struct timeval *tv, struct ucred *cred, uint16_t index, const void *data, uint16_t size); void packet_ctrl_command(struct timeval *tv, struct ucred *cred, uint16_t index, const void *data, uint16_t size); void packet_ctrl_event(struct timeval *tv, struct ucred *cred, uint16_t index, const void *data, uint16_t size); void packet_todo(void); bluez-5.82/monitor/PaxHeaders/crc.c0000644000000000000000000000005014015011623014241 xustar0020 atime=1743516014 20 ctime=1743591282 bluez-5.82/monitor/crc.c0000644000000000000000000000227414015011623013727 0ustar00rootroot// SPDX-License-Identifier: LGPL-2.1-or-later /* * * BlueZ - Bluetooth protocol stack for Linux * * Copyright (C) 2011-2014 Intel Corporation * Copyright (C) 2002-2010 Marcel Holtmann * * */ #ifdef HAVE_CONFIG_H #include #endif #include "crc.h" uint32_t crc24_bit_reverse(uint32_t value) { uint32_t result = 0; uint8_t i; for (i = 0; i < 24; i++) result |= ((value >> i) & 1) << (23 - i); return result; } uint32_t crc24_calculate(uint32_t preset, const uint8_t *data, uint8_t len) { uint32_t state = preset; uint8_t i; for (i = 0; i < len; i++) { uint8_t n, cur = data[i]; for (n = 0; n < 8; n++) { int next_bit = (state ^ cur) & 1; cur >>= 1; state >>= 1; if (next_bit) { state |= 1 << 23; state ^= 0x5a6000; } } } return state; } uint32_t crc24_reverse(uint32_t crc, const uint8_t *data, uint8_t len) { uint32_t state = crc; uint8_t i; for (i = 0; i < len; i++) { uint8_t n, cur = data[len - i - 1]; for (n = 0; n < 8; n++) { int top_bit = state >> 23; state = (state << 1) & 0xffffff; state |= top_bit ^ ((cur >> (7 - n)) & 1); if (top_bit) state ^= 0xb4c000; } } return state; } bluez-5.82/monitor/PaxHeaders/main.c0000644000000000000000000000005014471706457014444 xustar0020 atime=1743515951 20 ctime=1743591282 bluez-5.82/monitor/main.c0000644000000000000000000001727714471706457014143 0ustar00rootroot// SPDX-License-Identifier: LGPL-2.1-or-later /* * * BlueZ - Bluetooth protocol stack for Linux * * Copyright (C) 2011-2014 Intel Corporation * Copyright (C) 2002-2010 Marcel Holtmann * * */ #ifdef HAVE_CONFIG_H #include #endif #define _GNU_SOURCE #include #include #include #include #include #include #include "src/shared/mainloop.h" #include "src/shared/tty.h" #include "packet.h" #include "lmp.h" #include "keys.h" #include "analyze.h" #include "ellisys.h" #include "control.h" #include "display.h" static void signal_callback(int signum, void *user_data) { switch (signum) { case SIGINT: case SIGTERM: mainloop_quit(); break; } } static void usage(void) { printf("btmon - Bluetooth monitor\n" "Usage:\n"); printf("\tbtmon [options]\n"); printf("options:\n" "\t-r, --read Read traces in btsnoop format\n" "\t-w, --write Save traces in btsnoop format\n" "\t-a, --analyze Analyze traces in btsnoop format\n" "\t If gnuplot is installed on the\n" "\t system it will also attempt to plot\n" "\t packet latency graph.\n" "\t-s, --server Start monitor server socket\n" "\t-p, --priority Show only priority or lower\n" "\t-i, --index Show only specified controller\n" "\t-d, --tty Read data from TTY\n" "\t-B, --tty-speed Set TTY speed (default 115200)\n" "\t-V, --vendor Set default company identifier\n" "\t-M, --mgmt Open channel for mgmt events\n" "\t-t, --time Show time instead of time offset\n" "\t-T, --date Show time and date information\n" "\t-S, --sco Dump SCO traffic\n" "\t-A, --a2dp Dump A2DP stream traffic\n" "\t-I, --iso Dump ISO traffic\n" "\t-E, --ellisys [ip] Send Ellisys HCI Injection\n" "\t-P, --no-pager Disable pager usage\n" "\t-J --jlink ,[],[],[]\n" "\t Read data from RTT\n" "\t-R --rtt [
],[],[]\n" "\t RTT control block parameters\n" "\t-C, --columns [width] Output width if not a terminal\n" "\t-c, --color [mode] Output color: auto/always/never\n" "\t-h, --help Show help options\n"); } static const struct option main_options[] = { { "read", required_argument, NULL, 'r' }, { "write", required_argument, NULL, 'w' }, { "analyze", required_argument, NULL, 'a' }, { "server", required_argument, NULL, 's' }, { "priority", required_argument, NULL, 'p' }, { "index", required_argument, NULL, 'i' }, { "tty", required_argument, NULL, 'd' }, { "tty-speed", required_argument, NULL, 'B' }, { "vendor", required_argument, NULL, 'V' }, { "mgmt", no_argument, NULL, 'M' }, { "no-time", no_argument, NULL, 'N' }, { "time", no_argument, NULL, 't' }, { "date", no_argument, NULL, 'T' }, { "sco", no_argument, NULL, 'S' }, { "a2dp", no_argument, NULL, 'A' }, { "iso", no_argument, NULL, 'I' }, { "ellisys", required_argument, NULL, 'E' }, { "no-pager", no_argument, NULL, 'P' }, { "jlink", required_argument, NULL, 'J' }, { "rtt", required_argument, NULL, 'R' }, { "columns", required_argument, NULL, 'C' }, { "color", required_argument, NULL, 'c' }, { "todo", no_argument, NULL, '#' }, { "version", no_argument, NULL, 'v' }, { "help", no_argument, NULL, 'h' }, { } }; int main(int argc, char *argv[]) { unsigned long filter_mask = 0; bool use_pager = true; const char *reader_path = NULL; const char *writer_path = NULL; const char *analyze_path = NULL; const char *ellisys_server = NULL; const char *tty = NULL; unsigned int tty_speed = B115200; unsigned short ellisys_port = 0; const char *str; char *jlink = NULL; char *rtt = NULL; int exit_status; mainloop_init(); filter_mask |= PACKET_FILTER_SHOW_TIME_OFFSET; for (;;) { int opt; struct sockaddr_un addr; opt = getopt_long(argc, argv, "r:w:a:s:p:i:d:B:V:MNtTSAIE:PJ:R:C:c:vh", main_options, NULL); if (opt < 0) break; switch (opt) { case 'r': reader_path = optarg; break; case 'w': writer_path = optarg; break; case 'a': analyze_path = optarg; break; case 's': if (strlen(optarg) > sizeof(addr.sun_path) - 1) { fprintf(stderr, "Socket name too long\n"); return EXIT_FAILURE; } control_server(optarg); break; case 'p': packet_set_priority(optarg); break; case 'i': if (strlen(optarg) > 3 && !strncmp(optarg, "hci", 3)) str = optarg + 3; else str = optarg; if (!isdigit(*str)) { usage(); return EXIT_FAILURE; } packet_select_index(atoi(str)); break; case 'd': tty = optarg; break; case 'B': tty_speed = tty_get_speed(atoi(optarg)); if (!tty_speed) { fprintf(stderr, "Unknown speed: %s\n", optarg); return EXIT_FAILURE; } break; case 'V': str = optarg; packet_set_fallback_manufacturer(atoi(str)); break; case 'M': filter_mask |= PACKET_FILTER_SHOW_MGMT_SOCKET; break; case 'N': filter_mask &= ~PACKET_FILTER_SHOW_TIME_OFFSET; break; case 't': filter_mask &= ~PACKET_FILTER_SHOW_TIME_OFFSET; filter_mask |= PACKET_FILTER_SHOW_TIME; break; case 'T': filter_mask &= ~PACKET_FILTER_SHOW_TIME_OFFSET; filter_mask |= PACKET_FILTER_SHOW_TIME; filter_mask |= PACKET_FILTER_SHOW_DATE; break; case 'S': filter_mask |= PACKET_FILTER_SHOW_SCO_DATA; break; case 'A': filter_mask |= PACKET_FILTER_SHOW_A2DP_STREAM; break; case 'I': filter_mask |= PACKET_FILTER_SHOW_ISO_DATA; break; case 'E': ellisys_server = optarg; ellisys_port = 24352; break; case 'P': use_pager = false; break; case 'J': jlink = optarg; break; case 'R': rtt = optarg; break; case 'C': set_default_pager_num_columns(atoi(optarg)); break; case 'c': if (strcmp("always", optarg) == 0) set_monitor_color(COLOR_ALWAYS); else if (strcmp("never", optarg) == 0) set_monitor_color(COLOR_NEVER); else if (strcmp("auto", optarg) == 0) set_monitor_color(COLOR_AUTO); else { fprintf(stderr, "Color option must be one of " "auto/always/never\n"); return EXIT_FAILURE; } break; case '#': packet_todo(); lmp_todo(); return EXIT_SUCCESS; case 'v': printf("%s\n", VERSION); return EXIT_SUCCESS; case 'h': usage(); return EXIT_SUCCESS; default: return EXIT_FAILURE; } } if (argc - optind > 0) { fprintf(stderr, "Invalid command line parameters\n"); return EXIT_FAILURE; } if (reader_path && analyze_path) { fprintf(stderr, "Display and analyze can't be combined\n"); return EXIT_FAILURE; } printf("Bluetooth monitor ver %s\n", VERSION); keys_setup(); packet_set_filter(filter_mask); if (analyze_path) { analyze_trace(analyze_path); return EXIT_SUCCESS; } if (reader_path) { if (ellisys_server) ellisys_enable(ellisys_server, ellisys_port); control_reader(reader_path, use_pager); return EXIT_SUCCESS; } if (writer_path && !control_writer(writer_path)) { printf("Failed to open '%s'\n", writer_path); return EXIT_FAILURE; } if (ellisys_server) ellisys_enable(ellisys_server, ellisys_port); if (!tty && !jlink && control_tracing() < 0) return EXIT_FAILURE; if (tty && control_tty(tty, tty_speed) < 0) return EXIT_FAILURE; if (jlink && control_rtt(jlink, rtt) < 0) return EXIT_FAILURE; exit_status = mainloop_run_with_signal(signal_callback, NULL); keys_cleanup(); return exit_status; } bluez-5.82/monitor/PaxHeaders/hwdb.c0000644000000000000000000000005014572354773014446 xustar0020 atime=1743516050 20 ctime=1743591282 bluez-5.82/monitor/hwdb.c0000644000000000000000000000440514572354773014132 0ustar00rootroot// SPDX-License-Identifier: LGPL-2.1-or-later /* * * BlueZ - Bluetooth protocol stack for Linux * * Copyright (C) 2011-2014 Intel Corporation * Copyright (C) 2002-2010 Marcel Holtmann * * */ #ifdef HAVE_CONFIG_H #include #endif #define _GNU_SOURCE #include #include #include "hwdb.h" #ifdef HAVE_UDEV #include bool hwdb_get_vendor_model(const char *modalias, char **vendor, char **model) { struct udev *udev; struct udev_hwdb *hwdb; struct udev_list_entry *head, *entry; bool result; udev = udev_new(); if (!udev) return false; hwdb = udev_hwdb_new(udev); if (!hwdb) { result = false; goto done; } *vendor = NULL; *model = NULL; head = udev_hwdb_get_properties_list_entry(hwdb, modalias, 0); udev_list_entry_foreach(entry, head) { const char *name = udev_list_entry_get_name(entry); if (!name) continue; if (!*vendor && !strcmp(name, "ID_VENDOR_FROM_DATABASE")) *vendor = strdup(udev_list_entry_get_value(entry)); else if (!*model && !strcmp(name, "ID_MODEL_FROM_DATABASE")) *model = strdup(udev_list_entry_get_value(entry)); } hwdb = udev_hwdb_unref(hwdb); result = true; done: udev = udev_unref(udev); return result; } bool hwdb_get_company(const uint8_t *bdaddr, char **company) { struct udev *udev; struct udev_hwdb *hwdb; struct udev_list_entry *head, *entry; char modalias[11]; bool result; if (!bdaddr[2] && !bdaddr[1] && !bdaddr[0]) return false; sprintf(modalias, "OUI:%2.2X%2.2X%2.2X", bdaddr[5], bdaddr[4], bdaddr[3]); udev = udev_new(); if (!udev) return false; hwdb = udev_hwdb_new(udev); if (!hwdb) { result = false; goto done; } *company = NULL; head = udev_hwdb_get_properties_list_entry(hwdb, modalias, 0); udev_list_entry_foreach(entry, head) { const char *name = udev_list_entry_get_name(entry); if (name && !strcmp(name, "ID_OUI_FROM_DATABASE")) { *company = strdup(udev_list_entry_get_value(entry)); break; } } hwdb = udev_hwdb_unref(hwdb); result = true; done: udev = udev_unref(udev); return result; } #else bool hwdb_get_vendor_model(const char *modalias, char **vendor, char **model) { return false; } bool hwdb_get_company(const uint8_t *bdaddr, char **company) { return false; } #endif bluez-5.82/monitor/PaxHeaders/hcidump.c0000644000000000000000000000005014015011623015123 xustar0020 atime=1743515953 20 ctime=1743591282 bluez-5.82/monitor/hcidump.c0000644000000000000000000001775314015011623014621 0ustar00rootroot// SPDX-License-Identifier: LGPL-2.1-or-later /* * * BlueZ - Bluetooth protocol stack for Linux * * Copyright (C) 2011-2014 Intel Corporation * Copyright (C) 2002-2010 Marcel Holtmann * * */ #ifdef HAVE_CONFIG_H #include #endif #define _GNU_SOURCE #include #include #include #include #include #include #include #include "lib/bluetooth.h" #include "lib/hci.h" #include "lib/hci_lib.h" #include "src/shared/mainloop.h" #include "packet.h" #include "hcidump.h" struct hcidump_data { uint16_t index; int fd; }; static void free_data(void *user_data) { struct hcidump_data *data = user_data; close(data->fd); free(data); } static int open_hci_dev(uint16_t index) { struct sockaddr_hci addr; struct hci_filter flt; int fd, opt = 1; fd = socket(AF_BLUETOOTH, SOCK_RAW | SOCK_CLOEXEC, BTPROTO_HCI); if (fd < 0) { perror("Failed to open channel"); return -1; } /* Setup filter */ hci_filter_clear(&flt); hci_filter_all_ptypes(&flt); hci_filter_all_events(&flt); if (setsockopt(fd, SOL_HCI, HCI_FILTER, &flt, sizeof(flt)) < 0) { perror("Failed to set HCI filter"); close(fd); return -1; } if (setsockopt(fd, SOL_HCI, HCI_DATA_DIR, &opt, sizeof(opt)) < 0) { perror("Failed to enable HCI data direction info"); close(fd); return -1; } if (setsockopt(fd, SOL_HCI, HCI_TIME_STAMP, &opt, sizeof(opt)) < 0) { perror("Failed to enable HCI time stamps"); close(fd); return -1; } memset(&addr, 0, sizeof(addr)); addr.hci_family = AF_BLUETOOTH; addr.hci_dev = index; addr.hci_channel = HCI_CHANNEL_RAW; if (bind(fd, (struct sockaddr *) &addr, sizeof(addr)) < 0) { perror("Failed to bind channel"); close(fd); return -1; } return fd; } static void device_callback(int fd, uint32_t events, void *user_data) { struct hcidump_data *data = user_data; unsigned char buf[HCI_MAX_FRAME_SIZE * 2]; unsigned char control[64]; struct msghdr msg; struct iovec iov; if (events & (EPOLLERR | EPOLLHUP)) { mainloop_remove_fd(fd); return; } iov.iov_base = buf; iov.iov_len = sizeof(buf); memset(&msg, 0, sizeof(msg)); msg.msg_iov = &iov; msg.msg_iovlen = 1; msg.msg_control = control; msg.msg_controllen = sizeof(control); while (1) { struct cmsghdr *cmsg; struct timeval *tv = NULL; struct timeval ctv; int dir = -1; ssize_t len; len = recvmsg(fd, &msg, MSG_DONTWAIT); if (len < 0) break; for (cmsg = CMSG_FIRSTHDR(&msg); cmsg != NULL; cmsg = CMSG_NXTHDR(&msg, cmsg)) { if (cmsg->cmsg_level != SOL_HCI) continue; switch (cmsg->cmsg_type) { case HCI_DATA_DIR: memcpy(&dir, CMSG_DATA(cmsg), sizeof(dir)); break; case HCI_CMSG_TSTAMP: memcpy(&ctv, CMSG_DATA(cmsg), sizeof(ctv)); tv = &ctv; break; } } if (dir < 0 || len < 1) continue; switch (buf[0]) { case HCI_COMMAND_PKT: packet_hci_command(tv, NULL, data->index, buf + 1, len - 1); break; case HCI_EVENT_PKT: packet_hci_event(tv, NULL, data->index, buf + 1, len - 1); break; case HCI_ACLDATA_PKT: packet_hci_acldata(tv, NULL, data->index, !!dir, buf + 1, len - 1); break; case HCI_SCODATA_PKT: packet_hci_scodata(tv, NULL, data->index, !!dir, buf + 1, len - 1); break; } } } static void open_device(uint16_t index) { struct hcidump_data *data; data = malloc(sizeof(*data)); if (!data) return; memset(data, 0, sizeof(*data)); data->index = index; data->fd = open_hci_dev(index); if (data->fd < 0) { free(data); return; } if (mainloop_add_fd(data->fd, EPOLLIN, device_callback, data, free_data) < 0) { close(data->fd); free(data); } } static void device_info(int fd, uint16_t index, uint8_t *type, uint8_t *bus, bdaddr_t *bdaddr, char *name) { struct hci_dev_info di; memset(&di, 0, sizeof(di)); di.dev_id = index; if (ioctl(fd, HCIGETDEVINFO, (void *) &di) < 0) { perror("Failed to get device information"); return; } *type = di.type >> 4; *bus = di.type & 0x0f; bacpy(bdaddr, &di.bdaddr); memcpy(name, di.name, 8); } static void device_list(int fd, int max_dev) { struct hci_dev_list_req *dl; struct hci_dev_req *dr; int i; dl = malloc(max_dev * sizeof(*dr) + sizeof(*dl)); if (!dl) { perror("Failed to allocate device list memory"); return; } memset(dl, 0, max_dev * sizeof(*dr) + sizeof(*dl)); dl->dev_num = max_dev; dr = dl->dev_req; if (ioctl(fd, HCIGETDEVLIST, (void *) dl) < 0) { perror("Failed to get device list"); goto done; } for (i = 0; i < dl->dev_num; i++, dr++) { struct timeval tmp_tv, *tv = NULL; uint8_t type = 0xff, bus = 0xff; char str[18], name[8] = ""; bdaddr_t bdaddr; bacpy(&bdaddr, BDADDR_ANY); if (!gettimeofday(&tmp_tv, NULL)) tv = &tmp_tv; device_info(fd, dr->dev_id, &type, &bus, &bdaddr, name); ba2str(&bdaddr, str); packet_new_index(tv, dr->dev_id, str, type, bus, name); open_device(dr->dev_id); } done: free(dl); } static int open_stack_internal(void) { struct sockaddr_hci addr; struct hci_filter flt; int fd, opt = 1; fd = socket(AF_BLUETOOTH, SOCK_RAW | SOCK_CLOEXEC, BTPROTO_HCI); if (fd < 0) { perror("Failed to open channel"); return -1; } /* Setup filter */ hci_filter_clear(&flt); hci_filter_set_ptype(HCI_EVENT_PKT, &flt); hci_filter_set_event(EVT_STACK_INTERNAL, &flt); if (setsockopt(fd, SOL_HCI, HCI_FILTER, &flt, sizeof(flt)) < 0) { perror("Failed to set HCI filter"); close(fd); return -1; } if (setsockopt(fd, SOL_HCI, HCI_TIME_STAMP, &opt, sizeof(opt)) < 0) { perror("Failed to enable HCI time stamps"); close(fd); return -1; } memset(&addr, 0, sizeof(addr)); addr.hci_family = AF_BLUETOOTH; addr.hci_dev = HCI_DEV_NONE; addr.hci_channel = HCI_CHANNEL_RAW; if (bind(fd, (struct sockaddr *) &addr, sizeof(addr)) < 0) { perror("Failed to bind channel"); close(fd); return -1; } device_list(fd, HCI_MAX_DEV); return fd; } static void stack_internal_callback(int fd, uint32_t events, void *user_data) { unsigned char buf[HCI_MAX_FRAME_SIZE]; unsigned char control[32]; struct msghdr msg; struct iovec iov; struct cmsghdr *cmsg; ssize_t len; hci_event_hdr *eh; evt_stack_internal *si; evt_si_device *sd; struct timeval *tv = NULL; struct timeval ctv; uint8_t type = 0xff, bus = 0xff; char str[18], name[8] = ""; bdaddr_t bdaddr; bacpy(&bdaddr, BDADDR_ANY); if (events & (EPOLLERR | EPOLLHUP)) { mainloop_remove_fd(fd); return; } iov.iov_base = buf; iov.iov_len = sizeof(buf); memset(&msg, 0, sizeof(msg)); msg.msg_iov = &iov; msg.msg_iovlen = 1; msg.msg_control = control; msg.msg_controllen = sizeof(control); len = recvmsg(fd, &msg, MSG_DONTWAIT); if (len < 0) return; for (cmsg = CMSG_FIRSTHDR(&msg); cmsg != NULL; cmsg = CMSG_NXTHDR(&msg, cmsg)) { if (cmsg->cmsg_level != SOL_HCI) continue; switch (cmsg->cmsg_type) { case HCI_CMSG_TSTAMP: memcpy(&ctv, CMSG_DATA(cmsg), sizeof(ctv)); tv = &ctv; break; } } if (len < 1 + HCI_EVENT_HDR_SIZE + EVT_STACK_INTERNAL_SIZE + EVT_SI_DEVICE_SIZE) return; if (buf[0] != HCI_EVENT_PKT) return; eh = (hci_event_hdr *) (buf + 1); if (eh->evt != EVT_STACK_INTERNAL) return; si = (evt_stack_internal *) (buf + 1 + HCI_EVENT_HDR_SIZE); if (si->type != EVT_SI_DEVICE) return; sd = (evt_si_device *) &si->data; switch (sd->event) { case HCI_DEV_REG: device_info(fd, sd->dev_id, &type, &bus, &bdaddr, name); ba2str(&bdaddr, str); packet_new_index(tv, sd->dev_id, str, type, bus, name); open_device(sd->dev_id); break; case HCI_DEV_UNREG: ba2str(&bdaddr, str); packet_del_index(tv, sd->dev_id, str); break; } } int hcidump_tracing(void) { struct hcidump_data *data; data = malloc(sizeof(*data)); if (!data) return -1; memset(data, 0, sizeof(*data)); data->index = HCI_DEV_NONE; data->fd = open_stack_internal(); if (data->fd < 0) { free(data); return -1; } if (mainloop_add_fd(data->fd, EPOLLIN, stack_internal_callback, data, free_data) < 0) { close(data->fd); free(data); return -1; } return 0; } bluez-5.82/monitor/PaxHeaders/avctp.c0000644000000000000000000000005014711225434014621 xustar0020 atime=1743516031 20 ctime=1743591282 bluez-5.82/monitor/avctp.c0000644000000000000000000016245514711225434014317 0ustar00rootroot// SPDX-License-Identifier: LGPL-2.1-or-later /* * * BlueZ - Bluetooth protocol stack for Linux * * Copyright (C) 2011-2014 Intel Corporation * Copyright (C) 2002-2010 Marcel Holtmann * * */ #ifdef HAVE_CONFIG_H #include #endif #define _GNU_SOURCE #include #include #include #include #include #include "lib/bluetooth.h" #include "lib/uuid.h" #include "src/shared/util.h" #include "bt.h" #include "packet.h" #include "display.h" #include "l2cap.h" #include "keys.h" #include "sdp.h" #include "avctp.h" /* ctype entries */ #define AVC_CTYPE_CONTROL 0x0 #define AVC_CTYPE_STATUS 0x1 #define AVC_CTYPE_SPECIFIC_INQUIRY 0x2 #define AVC_CTYPE_NOTIFY 0x3 #define AVC_CTYPE_GENERAL_INQUIRY 0x4 #define AVC_CTYPE_NOT_IMPLEMENTED 0x8 #define AVC_CTYPE_ACCEPTED 0x9 #define AVC_CTYPE_REJECTED 0xA #define AVC_CTYPE_IN_TRANSITION 0xB #define AVC_CTYPE_STABLE 0xC #define AVC_CTYPE_CHANGED 0xD #define AVC_CTYPE_INTERIM 0xF /* subunit type */ #define AVC_SUBUNIT_MONITOR 0x00 #define AVC_SUBUNIT_AUDIO 0x01 #define AVC_SUBUNIT_PRINTER 0x02 #define AVC_SUBUNIT_DISC 0x03 #define AVC_SUBUNIT_TAPE 0x04 #define AVC_SUBUNIT_TUNER 0x05 #define AVC_SUBUNIT_CA 0x06 #define AVC_SUBUNIT_CAMERA 0x07 #define AVC_SUBUNIT_PANEL 0x09 #define AVC_SUBUNIT_BULLETIN_BOARD 0x0a #define AVC_SUBUNIT_CAMERA_STORAGE 0x0b #define AVC_SUBUNIT_VENDOR_UNIQUE 0x0c #define AVC_SUBUNIT_EXTENDED 0x1e #define AVC_SUBUNIT_UNIT 0x1f /* opcodes */ #define AVC_OP_VENDORDEP 0x00 #define AVC_OP_UNITINFO 0x30 #define AVC_OP_SUBUNITINFO 0x31 #define AVC_OP_PASSTHROUGH 0x7c /* notification events */ #define AVRCP_EVENT_PLAYBACK_STATUS_CHANGED 0x01 #define AVRCP_EVENT_TRACK_CHANGED 0x02 #define AVRCP_EVENT_TRACK_REACHED_END 0x03 #define AVRCP_EVENT_TRACK_REACHED_START 0x04 #define AVRCP_EVENT_PLAYBACK_POS_CHANGED 0x05 #define AVRCP_EVENT_BATT_STATUS_CHANGED 0x06 #define AVRCP_EVENT_SYSTEM_STATUS_CHANGED 0x07 #define AVRCP_EVENT_PLAYER_APPLICATION_SETTING_CHANGED 0x08 #define AVRCP_EVENT_NOW_PLAYING_CONTENT_CHANGED 0x09 #define AVRCP_EVENT_AVAILABLE_PLAYERS_CHANGED 0x0a #define AVRCP_EVENT_ADDRESSED_PLAYER_CHANGED 0x0b #define AVRCP_EVENT_UIDS_CHANGED 0x0c #define AVRCP_EVENT_VOLUME_CHANGED 0x0d /* error statuses */ #define AVRCP_STATUS_INVALID_COMMAND 0x00 #define AVRCP_STATUS_INVALID_PARAMETER 0x01 #define AVRCP_STATUS_NOT_FOUND 0x02 #define AVRCP_STATUS_INTERNAL_ERROR 0x03 #define AVRCP_STATUS_SUCCESS 0x04 #define AVRCP_STATUS_UID_CHANGED 0x05 #define AVRCP_STATUS_INVALID_DIRECTION 0x07 #define AVRCP_STATUS_NOT_DIRECTORY 0x08 #define AVRCP_STATUS_DOES_NOT_EXIST 0x09 #define AVRCP_STATUS_INVALID_SCOPE 0x0a #define AVRCP_STATUS_OUT_OF_BOUNDS 0x0b #define AVRCP_STATUS_IS_DIRECTORY 0x0c #define AVRCP_STATUS_MEDIA_IN_USE 0x0d #define AVRCP_STATUS_NOW_PLAYING_LIST_FULL 0x0e #define AVRCP_STATUS_SEARCH_NOT_SUPPORTED 0x0f #define AVRCP_STATUS_SEARCH_IN_PROGRESS 0x10 #define AVRCP_STATUS_INVALID_PLAYER_ID 0x11 #define AVRCP_STATUS_PLAYER_NOT_BROWSABLE 0x12 #define AVRCP_STATUS_PLAYER_NOT_ADDRESSED 0x13 #define AVRCP_STATUS_NO_VALID_SEARCH_RESULTS 0x14 #define AVRCP_STATUS_NO_AVAILABLE_PLAYERS 0x15 #define AVRCP_STATUS_ADDRESSED_PLAYER_CHANGED 0x16 /* pdu ids */ #define AVRCP_GET_CAPABILITIES 0x10 #define AVRCP_LIST_PLAYER_ATTRIBUTES 0x11 #define AVRCP_LIST_PLAYER_VALUES 0x12 #define AVRCP_GET_CURRENT_PLAYER_VALUE 0x13 #define AVRCP_SET_PLAYER_VALUE 0x14 #define AVRCP_GET_PLAYER_ATTRIBUTE_TEXT 0x15 #define AVRCP_GET_PLAYER_VALUE_TEXT 0x16 #define AVRCP_DISPLAYABLE_CHARSET 0x17 #define AVRCP_CT_BATTERY_STATUS 0x18 #define AVRCP_GET_ELEMENT_ATTRIBUTES 0x20 #define AVRCP_GET_PLAY_STATUS 0x30 #define AVRCP_REGISTER_NOTIFICATION 0x31 #define AVRCP_REQUEST_CONTINUING 0x40 #define AVRCP_ABORT_CONTINUING 0x41 #define AVRCP_SET_ABSOLUTE_VOLUME 0x50 #define AVRCP_SET_ADDRESSED_PLAYER 0x60 #define AVRCP_SET_BROWSED_PLAYER 0x70 #define AVRCP_GET_FOLDER_ITEMS 0x71 #define AVRCP_CHANGE_PATH 0x72 #define AVRCP_GET_ITEM_ATTRIBUTES 0x73 #define AVRCP_PLAY_ITEM 0x74 #define AVRCP_GET_TOTAL_NUMBER_OF_ITEMS 0x75 #define AVRCP_SEARCH 0x80 #define AVRCP_ADD_TO_NOW_PLAYING 0x90 #define AVRCP_GENERAL_REJECT 0xA0 /* Packet types */ #define AVRCP_PACKET_TYPE_SINGLE 0x00 #define AVRCP_PACKET_TYPE_START 0x01 #define AVRCP_PACKET_TYPE_CONTINUING 0x02 #define AVRCP_PACKET_TYPE_END 0x03 /* player attributes */ #define AVRCP_ATTRIBUTE_ILEGAL 0x00 #define AVRCP_ATTRIBUTE_EQUALIZER 0x01 #define AVRCP_ATTRIBUTE_REPEAT_MODE 0x02 #define AVRCP_ATTRIBUTE_SHUFFLE 0x03 #define AVRCP_ATTRIBUTE_SCAN 0x04 /* media attributes */ #define AVRCP_MEDIA_ATTRIBUTE_ILLEGAL 0x00 #define AVRCP_MEDIA_ATTRIBUTE_TITLE 0x01 #define AVRCP_MEDIA_ATTRIBUTE_ARTIST 0x02 #define AVRCP_MEDIA_ATTRIBUTE_ALBUM 0x03 #define AVRCP_MEDIA_ATTRIBUTE_TRACK 0x04 #define AVRCP_MEDIA_ATTRIBUTE_TOTAL 0x05 #define AVRCP_MEDIA_ATTRIBUTE_GENRE 0x06 #define AVRCP_MEDIA_ATTRIBUTE_DURATION 0x07 #define AVRCP_MEDIA_ATTRIBUTE_IMG_HANDLE 0x08 /* play status */ #define AVRCP_PLAY_STATUS_STOPPED 0x00 #define AVRCP_PLAY_STATUS_PLAYING 0x01 #define AVRCP_PLAY_STATUS_PAUSED 0x02 #define AVRCP_PLAY_STATUS_FWD_SEEK 0x03 #define AVRCP_PLAY_STATUS_REV_SEEK 0x04 #define AVRCP_PLAY_STATUS_ERROR 0xFF /* media scope */ #define AVRCP_MEDIA_PLAYER_LIST 0x00 #define AVRCP_MEDIA_PLAYER_VFS 0x01 #define AVRCP_MEDIA_SEARCH 0x02 #define AVRCP_MEDIA_NOW_PLAYING 0x03 /* Media Item Type */ #define AVRCP_MEDIA_PLAYER_ITEM_TYPE 0x01 #define AVRCP_FOLDER_ITEM_TYPE 0x02 #define AVRCP_MEDIA_ELEMENT_ITEM_TYPE 0x03 /* operands in passthrough commands */ #define AVC_PANEL_VOLUME_UP 0x41 #define AVC_PANEL_VOLUME_DOWN 0x42 #define AVC_PANEL_MUTE 0x43 #define AVC_PANEL_PLAY 0x44 #define AVC_PANEL_STOP 0x45 #define AVC_PANEL_PAUSE 0x46 #define AVC_PANEL_RECORD 0x47 #define AVC_PANEL_REWIND 0x48 #define AVC_PANEL_FAST_FORWARD 0x49 #define AVC_PANEL_EJECT 0x4a #define AVC_PANEL_FORWARD 0x4b #define AVC_PANEL_BACKWARD 0x4c struct avctp_frame { uint8_t hdr; uint8_t pt; uint16_t pid; struct l2cap_frame l2cap_frame; }; static struct avrcp_continuing { uint16_t num; uint16_t size; } avrcp_continuing; static const char *ctype2str(uint8_t ctype) { switch (ctype & 0x0f) { case AVC_CTYPE_CONTROL: return "Control"; case AVC_CTYPE_STATUS: return "Status"; case AVC_CTYPE_SPECIFIC_INQUIRY: return "Specific Inquiry"; case AVC_CTYPE_NOTIFY: return "Notify"; case AVC_CTYPE_GENERAL_INQUIRY: return "General Inquiry"; case AVC_CTYPE_NOT_IMPLEMENTED: return "Not Implemented"; case AVC_CTYPE_ACCEPTED: return "Accepted"; case AVC_CTYPE_REJECTED: return "Rejected"; case AVC_CTYPE_IN_TRANSITION: return "In Transition"; case AVC_CTYPE_STABLE: return "Stable"; case AVC_CTYPE_CHANGED: return "Changed"; case AVC_CTYPE_INTERIM: return "Interim"; default: return "Unknown"; } } static const char *subunit2str(uint8_t subunit) { switch (subunit) { case AVC_SUBUNIT_MONITOR: return "Monitor"; case AVC_SUBUNIT_AUDIO: return "Audio"; case AVC_SUBUNIT_PRINTER: return "Printer"; case AVC_SUBUNIT_DISC: return "Disc"; case AVC_SUBUNIT_TAPE: return "Tape"; case AVC_SUBUNIT_TUNER: return "Tuner"; case AVC_SUBUNIT_CA: return "CA"; case AVC_SUBUNIT_CAMERA: return "Camera"; case AVC_SUBUNIT_PANEL: return "Panel"; case AVC_SUBUNIT_BULLETIN_BOARD: return "Bulletin Board"; case AVC_SUBUNIT_CAMERA_STORAGE: return "Camera Storage"; case AVC_SUBUNIT_VENDOR_UNIQUE: return "Vendor Unique"; case AVC_SUBUNIT_EXTENDED: return "Extended to next byte"; case AVC_SUBUNIT_UNIT: return "Unit"; default: return "Reserved"; } } static const char *opcode2str(uint8_t opcode) { switch (opcode) { case AVC_OP_VENDORDEP: return "Vendor Dependent"; case AVC_OP_UNITINFO: return "Unit Info"; case AVC_OP_SUBUNITINFO: return "Subunit Info"; case AVC_OP_PASSTHROUGH: return "Passthrough"; default: return "Unknown"; } } static char *cap2str(uint8_t cap) { switch (cap) { case 0x2: return "CompanyID"; case 0x3: return "EventsID"; default: return "Unknown"; } } static char *event2str(uint8_t event) { switch (event) { case AVRCP_EVENT_PLAYBACK_STATUS_CHANGED: return "EVENT_PLAYBACK_STATUS_CHANGED"; case AVRCP_EVENT_TRACK_CHANGED: return "EVENT_TRACK_CHANGED"; case AVRCP_EVENT_TRACK_REACHED_END: return "EVENT_TRACK_REACHED_END"; case AVRCP_EVENT_TRACK_REACHED_START: return "EVENT_TRACK_REACHED_START"; case AVRCP_EVENT_PLAYBACK_POS_CHANGED: return "EVENT_PLAYBACK_POS_CHANGED"; case AVRCP_EVENT_BATT_STATUS_CHANGED: return "EVENT_BATT_STATUS_CHANGED"; case AVRCP_EVENT_SYSTEM_STATUS_CHANGED: return "EVENT_SYSTEM_STATUS_CHANGED"; case AVRCP_EVENT_PLAYER_APPLICATION_SETTING_CHANGED: return "EVENT_PLAYER_APPLICATION_SETTING_CHANGED"; case AVRCP_EVENT_NOW_PLAYING_CONTENT_CHANGED: return "EVENT_NOW_PLAYING_CONTENT_CHANGED"; case AVRCP_EVENT_AVAILABLE_PLAYERS_CHANGED: return "EVENT_AVAILABLE_PLAYERS_CHANGED"; case AVRCP_EVENT_ADDRESSED_PLAYER_CHANGED: return "EVENT_ADDRESSED_PLAYER_CHANGED"; case AVRCP_EVENT_UIDS_CHANGED: return "EVENT_UIDS_CHANGED"; case AVRCP_EVENT_VOLUME_CHANGED: return "EVENT_VOLUME_CHANGED"; default: return "Reserved"; } } static const char *error2str(uint8_t status) { switch (status) { case AVRCP_STATUS_INVALID_COMMAND: return "Invalid Command"; case AVRCP_STATUS_INVALID_PARAMETER: return "Invalid Parameter"; case AVRCP_STATUS_NOT_FOUND: return "Not Found"; case AVRCP_STATUS_INTERNAL_ERROR: return "Internal Error"; case AVRCP_STATUS_SUCCESS: return "Success"; case AVRCP_STATUS_UID_CHANGED: return "UID Changed"; case AVRCP_STATUS_INVALID_DIRECTION: return "Invalid Direction"; case AVRCP_STATUS_NOT_DIRECTORY: return "Not a Directory"; case AVRCP_STATUS_DOES_NOT_EXIST: return "Does Not Exist"; case AVRCP_STATUS_INVALID_SCOPE: return "Invalid Scope"; case AVRCP_STATUS_OUT_OF_BOUNDS: return "Range Out of Bounds"; case AVRCP_STATUS_MEDIA_IN_USE: return "Media in Use"; case AVRCP_STATUS_IS_DIRECTORY: return "UID is a Directory"; case AVRCP_STATUS_NOW_PLAYING_LIST_FULL: return "Now Playing List Full"; case AVRCP_STATUS_SEARCH_NOT_SUPPORTED: return "Search Not Supported"; case AVRCP_STATUS_SEARCH_IN_PROGRESS: return "Search in Progress"; case AVRCP_STATUS_INVALID_PLAYER_ID: return "Invalid Player ID"; case AVRCP_STATUS_PLAYER_NOT_BROWSABLE: return "Player Not Browsable"; case AVRCP_STATUS_PLAYER_NOT_ADDRESSED: return "Player Not Addressed"; case AVRCP_STATUS_NO_VALID_SEARCH_RESULTS: return "No Valid Search Result"; case AVRCP_STATUS_NO_AVAILABLE_PLAYERS: return "No Available Players"; case AVRCP_STATUS_ADDRESSED_PLAYER_CHANGED: return "Addressed Player Changed"; default: return "Unknown"; } } static const char *pdu2str(uint8_t pduid) { switch (pduid) { case AVRCP_GET_CAPABILITIES: return "GetCapabilities"; case AVRCP_LIST_PLAYER_ATTRIBUTES: return "ListPlayerApplicationSettingAttributes"; case AVRCP_LIST_PLAYER_VALUES: return "ListPlayerApplicationSettingValues"; case AVRCP_GET_CURRENT_PLAYER_VALUE: return "GetCurrentPlayerApplicationSettingValue"; case AVRCP_SET_PLAYER_VALUE: return "SetPlayerApplicationSettingValue"; case AVRCP_GET_PLAYER_ATTRIBUTE_TEXT: return "GetPlayerApplicationSettingAttributeText"; case AVRCP_GET_PLAYER_VALUE_TEXT: return "GetPlayerApplicationSettingValueText"; case AVRCP_DISPLAYABLE_CHARSET: return "InformDisplayableCharacterSet"; case AVRCP_CT_BATTERY_STATUS: return "InformBatteryStatusOfCT"; case AVRCP_GET_ELEMENT_ATTRIBUTES: return "GetElementAttributes"; case AVRCP_GET_PLAY_STATUS: return "GetPlayStatus"; case AVRCP_REGISTER_NOTIFICATION: return "RegisterNotification"; case AVRCP_REQUEST_CONTINUING: return "RequestContinuingResponse"; case AVRCP_ABORT_CONTINUING: return "AbortContinuingResponse"; case AVRCP_SET_ABSOLUTE_VOLUME: return "SetAbsoluteVolume"; case AVRCP_SET_ADDRESSED_PLAYER: return "SetAddressedPlayer"; case AVRCP_SET_BROWSED_PLAYER: return "SetBrowsedPlayer"; case AVRCP_GET_FOLDER_ITEMS: return "GetFolderItems"; case AVRCP_CHANGE_PATH: return "ChangePath"; case AVRCP_GET_ITEM_ATTRIBUTES: return "GetItemAttributes"; case AVRCP_PLAY_ITEM: return "PlayItem"; case AVRCP_GET_TOTAL_NUMBER_OF_ITEMS: return "GetTotalNumOfItems"; case AVRCP_SEARCH: return "Search"; case AVRCP_ADD_TO_NOW_PLAYING: return "AddToNowPlaying"; case AVRCP_GENERAL_REJECT: return "GeneralReject"; default: return "Unknown"; } } static const char *pt2str(uint8_t pt) { switch (pt) { case AVRCP_PACKET_TYPE_SINGLE: return "Single"; case AVRCP_PACKET_TYPE_START: return "Start"; case AVRCP_PACKET_TYPE_CONTINUING: return "Continuing"; case AVRCP_PACKET_TYPE_END: return "End"; default: return "Unknown"; } } static const char *attr2str(uint8_t attr) { switch (attr) { case AVRCP_ATTRIBUTE_ILEGAL: return "Illegal"; case AVRCP_ATTRIBUTE_EQUALIZER: return "Equalizer ON/OFF Status"; case AVRCP_ATTRIBUTE_REPEAT_MODE: return "Repeat Mode Status"; case AVRCP_ATTRIBUTE_SHUFFLE: return "Shuffle ON/OFF Status"; case AVRCP_ATTRIBUTE_SCAN: return "Scan ON/OFF Status"; default: return "Unknown"; } } static const char *value2str(uint8_t attr, uint8_t value) { switch (attr) { case AVRCP_ATTRIBUTE_ILEGAL: return "Illegal"; case AVRCP_ATTRIBUTE_EQUALIZER: switch (value) { case 0x01: return "OFF"; case 0x02: return "ON"; default: return "Reserved"; } case AVRCP_ATTRIBUTE_REPEAT_MODE: switch (value) { case 0x01: return "OFF"; case 0x02: return "Single Track Repeat"; case 0x03: return "All Track Repeat"; case 0x04: return "Group Repeat"; default: return "Reserved"; } case AVRCP_ATTRIBUTE_SHUFFLE: switch (value) { case 0x01: return "OFF"; case 0x02: return "All Track Shuffle"; case 0x03: return "Group Shuffle"; default: return "Reserved"; } case AVRCP_ATTRIBUTE_SCAN: switch (value) { case 0x01: return "OFF"; case 0x02: return "All Track Scan"; case 0x03: return "Group Scan"; default: return "Reserved"; } default: return "Unknown"; } } static const char *charset2str(uint16_t charset) { switch (charset) { case 1: case 2: return "Reserved"; case 3: return "ASCII"; case 4: return "ISO_8859-1"; case 5: return "ISO_8859-2"; case 6: return "ISO_8859-3"; case 7: return "ISO_8859-4"; case 8: return "ISO_8859-5"; case 9: return "ISO_8859-6"; case 10: return "ISO_8859-7"; case 11: return "ISO_8859-8"; case 12: return "ISO_8859-9"; case 106: return "UTF-8"; default: return "Unknown"; } } static const char *mediattr2str(uint32_t attr) { switch (attr) { case AVRCP_MEDIA_ATTRIBUTE_ILLEGAL: return "Illegal"; case AVRCP_MEDIA_ATTRIBUTE_TITLE: return "Title"; case AVRCP_MEDIA_ATTRIBUTE_ARTIST: return "Artist"; case AVRCP_MEDIA_ATTRIBUTE_ALBUM: return "Album"; case AVRCP_MEDIA_ATTRIBUTE_TRACK: return "Track"; case AVRCP_MEDIA_ATTRIBUTE_TOTAL: return "Track Total"; case AVRCP_MEDIA_ATTRIBUTE_GENRE: return "Genre"; case AVRCP_MEDIA_ATTRIBUTE_DURATION: return "Track duration"; case AVRCP_MEDIA_ATTRIBUTE_IMG_HANDLE: return "Imaging handle"; default: return "Reserved"; } } static const char *playstatus2str(uint8_t status) { switch (status) { case AVRCP_PLAY_STATUS_STOPPED: return "STOPPED"; case AVRCP_PLAY_STATUS_PLAYING: return "PLAYING"; case AVRCP_PLAY_STATUS_PAUSED: return "PAUSED"; case AVRCP_PLAY_STATUS_FWD_SEEK: return "FWD_SEEK"; case AVRCP_PLAY_STATUS_REV_SEEK: return "REV_SEEK"; case AVRCP_PLAY_STATUS_ERROR: return "ERROR"; default: return "Unknown"; } } static const char *status2str(uint8_t status) { switch (status) { case 0x0: return "NORMAL"; case 0x1: return "WARNING"; case 0x2: return "CRITICAL"; case 0x3: return "EXTERNAL"; case 0x4: return "FULL_CHARGE"; default: return "Reserved"; } } static const char *scope2str(uint8_t scope) { switch (scope) { case AVRCP_MEDIA_PLAYER_LIST: return "Media Player List"; case AVRCP_MEDIA_PLAYER_VFS: return "Media Player Virtual Filesystem"; case AVRCP_MEDIA_SEARCH: return "Search"; case AVRCP_MEDIA_NOW_PLAYING: return "Now Playing"; default: return "Unknown"; } } static char *op2str(uint8_t op) { switch (op & 0x7f) { case AVC_PANEL_VOLUME_UP: return "VOLUME UP"; case AVC_PANEL_VOLUME_DOWN: return "VOLUME DOWN"; case AVC_PANEL_MUTE: return "MUTE"; case AVC_PANEL_PLAY: return "PLAY"; case AVC_PANEL_STOP: return "STOP"; case AVC_PANEL_PAUSE: return "PAUSE"; case AVC_PANEL_RECORD: return "RECORD"; case AVC_PANEL_REWIND: return "REWIND"; case AVC_PANEL_FAST_FORWARD: return "FAST FORWARD"; case AVC_PANEL_EJECT: return "EJECT"; case AVC_PANEL_FORWARD: return "FORWARD"; case AVC_PANEL_BACKWARD: return "BACKWARD"; default: return "UNKNOWN"; } } static const char *type2str(uint8_t type) { switch (type) { case AVRCP_MEDIA_PLAYER_ITEM_TYPE: return "Media Player"; case AVRCP_FOLDER_ITEM_TYPE: return "Folder"; case AVRCP_MEDIA_ELEMENT_ITEM_TYPE: return "Media Element"; default: return "Unknown"; } } static const char *playertype2str(uint8_t type) { switch (type & 0x0F) { case 0x01: return "Audio"; case 0x02: return "Video"; case 0x03: return "Audio, Video"; case 0x04: return "Audio Broadcasting"; case 0x05: return "Audio, Audio Broadcasting"; case 0x06: return "Video, Audio Broadcasting"; case 0x07: return "Audio, Video, Audio Broadcasting"; case 0x08: return "Video Broadcasting"; case 0x09: return "Audio, Video Broadcasting"; case 0x0A: return "Video, Video Broadcasting"; case 0x0B: return "Audio, Video, Video Broadcasting"; case 0x0C: return "Audio Broadcasting, Video Broadcasting"; case 0x0D: return "Audio, Audio Broadcasting, Video Broadcasting"; case 0x0E: return "Video, Audio Broadcasting, Video Broadcasting"; case 0x0F: return "Audio, Video, Audio Broadcasting, Video Broadcasting"; } return "None"; } static const char *playersubtype2str(uint32_t subtype) { switch (subtype & 0x03) { case 0x01: return "Audio Book"; case 0x02: return "Podcast"; case 0x03: return "Audio Book, Podcast"; } return "None"; } static const char *foldertype2str(uint8_t type) { switch (type) { case 0x00: return "Mixed"; case 0x01: return "Titles"; case 0x02: return "Albums"; case 0x03: return "Artists"; case 0x04: return "Genres"; case 0x05: return "Playlists"; case 0x06: return "Years"; } return "Reserved"; } static const char *elementtype2str(uint8_t type) { switch (type) { case 0x00: return "Audio"; case 0x01: return "Video"; } return "Reserved"; } static bool avrcp_passthrough_packet(struct avctp_frame *avctp_frame, uint8_t indent) { struct l2cap_frame *frame = &avctp_frame->l2cap_frame; uint8_t op, len; if (!l2cap_frame_get_u8(frame, &op)) return false; print_field("%*cOperation: 0x%02x (%s %s)", (indent - 8), ' ', op, op2str(op), op & 0x80 ? "Released" : "Pressed"); if (!l2cap_frame_get_u8(frame, &len)) return false; print_field("%*cLength: 0x%02x", (indent - 8), ' ', len); packet_hexdump(frame->data, frame->size); return true; } static bool avrcp_get_capabilities(struct avctp_frame *avctp_frame, uint8_t ctype, uint8_t len, uint8_t indent) { struct l2cap_frame *frame = &avctp_frame->l2cap_frame; uint8_t cap, count; int i; if (!l2cap_frame_get_u8(frame, &cap)) return false; print_field("%*cCapabilityID: 0x%02x (%s)", (indent - 8), ' ', cap, cap2str(cap)); if (len == 1) return true; if (!l2cap_frame_get_u8(frame, &count)) return false; print_field("%*cCapabilityCount: 0x%02x", (indent - 8), ' ', count); switch (cap) { case 0x2: for (; count > 0; count--) { uint8_t company[3]; if (!l2cap_frame_get_u8(frame, &company[0]) || !l2cap_frame_get_u8(frame, &company[1]) || !l2cap_frame_get_u8(frame, &company[2])) return false; print_field("%*c%s: 0x%02x%02x%02x", (indent - 8), ' ', cap2str(cap), company[0], company[1], company[2]); } break; case 0x3: for (i = 0; count > 0; count--, i++) { uint8_t event; if (!l2cap_frame_get_u8(frame, &event)) return false; print_field("%*c%s: 0x%02x (%s)", (indent - 8), ' ', cap2str(cap), event, event2str(event)); } break; default: packet_hexdump(frame->data, frame->size); } return true; } static bool avrcp_list_player_attributes(struct avctp_frame *avctp_frame, uint8_t ctype, uint8_t len, uint8_t indent) { struct l2cap_frame *frame = &avctp_frame->l2cap_frame; uint8_t num; int i; if (len == 0) return true; if (!l2cap_frame_get_u8(frame, &num)) return false; print_field("%*cAttributeCount: 0x%02x", (indent - 8), ' ', num); for (i = 0; num > 0; num--, i++) { uint8_t attr; if (!l2cap_frame_get_u8(frame, &attr)) return false; print_field("%*cAttributeID: 0x%02x (%s)", (indent - 8), ' ', attr, attr2str(attr)); } return true; } static bool avrcp_list_player_values(struct avctp_frame *avctp_frame, uint8_t ctype, uint8_t len, uint8_t indent) { struct l2cap_frame *frame = &avctp_frame->l2cap_frame; static uint8_t attr = 0; uint8_t num; if (ctype > AVC_CTYPE_GENERAL_INQUIRY) goto response; if (!l2cap_frame_get_u8(frame, &attr)) return false; print_field("%*cAttributeID: 0x%02x (%s)", (indent - 8), ' ', attr, attr2str(attr)); return true; response: if (!l2cap_frame_get_u8(frame, &num)) return false; print_field("%*cValueCount: 0x%02x", (indent - 8), ' ', num); for (; num > 0; num--) { uint8_t value; if (!l2cap_frame_get_u8(frame, &value)) return false; print_field("%*cValueID: 0x%02x (%s)", (indent - 8), ' ', value, value2str(attr, value)); } return true; } static bool avrcp_get_current_player_value(struct avctp_frame *avctp_frame, uint8_t ctype, uint8_t len, uint8_t indent) { struct l2cap_frame *frame = &avctp_frame->l2cap_frame; uint8_t num; if (!l2cap_frame_get_u8(frame, &num)) return false; if (ctype > AVC_CTYPE_GENERAL_INQUIRY) goto response; print_field("%*cAttributeCount: 0x%02x", (indent - 8), ' ', num); for (; num > 0; num--) { uint8_t attr; if (!l2cap_frame_get_u8(frame, &attr)) return false; print_field("%*cAttributeID: 0x%02x (%s)", (indent - 8), ' ', attr, attr2str(attr)); } return true; response: print_field("%*cValueCount: 0x%02x", (indent - 8), ' ', num); for (; num > 0; num--) { uint8_t attr, value; if (!l2cap_frame_get_u8(frame, &attr)) return false; print_field("%*cAttributeID: 0x%02x (%s)", (indent - 8), ' ', attr, attr2str(attr)); if (!l2cap_frame_get_u8(frame, &value)) return false; print_field("%*cValueID: 0x%02x (%s)", (indent - 8), ' ', value, value2str(attr, value)); } return true; } static bool avrcp_set_player_value(struct avctp_frame *avctp_frame, uint8_t ctype, uint8_t len, uint8_t indent) { struct l2cap_frame *frame = &avctp_frame->l2cap_frame; uint8_t num; if (ctype > AVC_CTYPE_GENERAL_INQUIRY) return true; if (!l2cap_frame_get_u8(frame, &num)) return false; print_field("%*cAttributeCount: 0x%02x", (indent - 8), ' ', num); for (; num > 0; num--) { uint8_t attr, value; if (!l2cap_frame_get_u8(frame, &attr)) return false; print_field("%*cAttributeID: 0x%02x (%s)", (indent - 8), ' ', attr, attr2str(attr)); if (!l2cap_frame_get_u8(frame, &value)) return false; print_field("%*cValueID: 0x%02x (%s)", (indent - 8), ' ', value, value2str(attr, value)); } return true; } static bool avrcp_get_player_attribute_text(struct avctp_frame *avctp_frame, uint8_t ctype, uint8_t len, uint8_t indent) { struct l2cap_frame *frame = &avctp_frame->l2cap_frame; uint8_t num; if (!l2cap_frame_get_u8(frame, &num)) return false; print_field("%*cAttributeCount: 0x%02x", (indent - 8), ' ', num); if (ctype > AVC_CTYPE_GENERAL_INQUIRY) goto response; for (; num > 0; num--) { uint8_t attr; if (!l2cap_frame_get_u8(frame, &attr)) return false; print_field("%*cAttributeID: 0x%02x (%s)", (indent - 8), ' ', attr, attr2str(attr)); } return true; response: for (; num > 0; num--) { uint8_t attr, len; uint16_t charset; if (!l2cap_frame_get_u8(frame, &attr)) return false; print_field("%*cAttributeID: 0x%02x (%s)", (indent - 8), ' ', attr, attr2str(attr)); if (!l2cap_frame_get_be16(frame, &charset)) return false; print_field("%*cCharsetID: 0x%04x (%s)", (indent - 8), ' ', charset, charset2str(charset)); if (!l2cap_frame_get_u8(frame, &len)) return false; print_field("%*cStringLength: 0x%02x", (indent - 8), ' ', len); printf("String: "); for (; len > 0; len--) { uint8_t c; if (!l2cap_frame_get_u8(frame, &c)) return false; printf("%1c", isprint(c) ? c : '.'); } printf("\n"); } return true; } static bool avrcp_get_player_value_text(struct avctp_frame *avctp_frame, uint8_t ctype, uint8_t len, uint8_t indent) { struct l2cap_frame *frame = &avctp_frame->l2cap_frame; static uint8_t attr = 0; uint8_t num; if (ctype > AVC_CTYPE_GENERAL_INQUIRY) goto response; if (!l2cap_frame_get_u8(frame, &attr)) return false; print_field("%*cAttributeID: 0x%02x (%s)", (indent - 8), ' ', attr, attr2str(attr)); if (!l2cap_frame_get_u8(frame, &num)) return false; print_field("%*cValueCount: 0x%02x", (indent - 8), ' ', num); for (; num > 0; num--) { uint8_t value; if (!l2cap_frame_get_u8(frame, &value)) return false; print_field("%*cValueID: 0x%02x (%s)", (indent - 8), ' ', value, value2str(attr, value)); } return true; response: if (!l2cap_frame_get_u8(frame, &num)) return false; print_field("%*cValueCount: 0x%02x", (indent - 8), ' ', num); for (; num > 0; num--) { uint8_t value, len; uint16_t charset; if (!l2cap_frame_get_u8(frame, &value)) return false; print_field("%*cValueID: 0x%02x (%s)", (indent - 8), ' ', value, value2str(attr, value)); if (!l2cap_frame_get_be16(frame, &charset)) return false; print_field("%*cCharsetIDID: 0x%02x (%s)", (indent - 8), ' ', charset, charset2str(charset)); if (!l2cap_frame_get_u8(frame, &len)) return false; print_field("%*cStringLength: 0x%02x", (indent - 8), ' ', len); printf("String: "); for (; len > 0; len--) { uint8_t c; if (!l2cap_frame_get_u8(frame, &c)) return false; printf("%1c", isprint(c) ? c : '.'); } printf("\n"); } return true; } static bool avrcp_displayable_charset(struct avctp_frame *avctp_frame, uint8_t ctype, uint8_t len, uint8_t indent) { struct l2cap_frame *frame = &avctp_frame->l2cap_frame; uint8_t num; if (ctype > AVC_CTYPE_GENERAL_INQUIRY) return true; if (!l2cap_frame_get_u8(frame, &num)) return false; print_field("%*cCharsetCount: 0x%02x", (indent - 8), ' ', num); for (; num > 0; num--) { uint16_t charset; if (!l2cap_frame_get_be16(frame, &charset)) return false; print_field("%*cCharsetID: 0x%04x (%s)", (indent - 8), ' ', charset, charset2str(charset)); } return true; } static bool avrcp_get_element_attributes(struct avctp_frame *avctp_frame, uint8_t ctype, uint8_t len, uint8_t indent) { struct l2cap_frame *frame = &avctp_frame->l2cap_frame; uint64_t id; uint8_t num; if (ctype > AVC_CTYPE_GENERAL_INQUIRY) goto response; if (!l2cap_frame_get_be64(frame, &id)) return false; print_field("%*cIdentifier: 0x%jx (%s)", (indent - 8), ' ', id, id ? "Reserved" : "PLAYING"); if (!l2cap_frame_get_u8(frame, &num)) return false; print_field("%*cAttributeCount: 0x%02x", (indent - 8), ' ', num); for (; num > 0; num--) { uint32_t attr; if (!l2cap_frame_get_be32(frame, &attr)) return false; print_field("%*cAttributeID: 0x%08x (%s)", (indent - 8), ' ', attr, mediattr2str(attr)); } return true; response: switch (avctp_frame->pt) { case AVRCP_PACKET_TYPE_SINGLE: case AVRCP_PACKET_TYPE_START: if (!l2cap_frame_get_u8(frame, &num)) return false; avrcp_continuing.num = num; print_field("%*cAttributeCount: 0x%02x", (indent - 8), ' ', num); len--; break; case AVRCP_PACKET_TYPE_CONTINUING: case AVRCP_PACKET_TYPE_END: num = avrcp_continuing.num; if (avrcp_continuing.size > 0) { char attrval[UINT8_MAX] = {0}; uint16_t size; uint8_t idx; if (avrcp_continuing.size > len) { size = len; avrcp_continuing.size -= len; } else { size = avrcp_continuing.size; avrcp_continuing.size = 0; } for (idx = 0; size > 0; idx++, size--) { uint8_t c; if (!l2cap_frame_get_u8(frame, &c)) goto failed; sprintf(&attrval[idx], "%1c", isprint(c) ? c : '.'); } print_field("%*cContinuingAttributeValue: %s", (indent - 8), ' ', attrval); len -= size; } break; default: goto failed; } while (num > 0 && len > 0) { uint32_t attr; uint16_t charset, attrlen; uint8_t idx; char attrval[UINT8_MAX] = {0}; if (!l2cap_frame_get_be32(frame, &attr)) goto failed; print_field("%*cAttribute: 0x%08x (%s)", (indent - 8), ' ', attr, mediattr2str(attr)); if (!l2cap_frame_get_be16(frame, &charset)) goto failed; print_field("%*cCharsetID: 0x%04x (%s)", (indent - 8), ' ', charset, charset2str(charset)); if (!l2cap_frame_get_be16(frame, &attrlen)) goto failed; print_field("%*cAttributeValueLength: 0x%04x", (indent - 8), ' ', attrlen); len -= sizeof(attr) + sizeof(charset) + sizeof(attrlen); num--; for (idx = 0; attrlen > 0 && len > 0; idx++, attrlen--, len--) { uint8_t c; if (!l2cap_frame_get_u8(frame, &c)) goto failed; sprintf(&attrval[idx], "%1c", isprint(c) ? c : '.'); } print_field("%*cAttributeValue: %s", (indent - 8), ' ', attrval); if (attrlen > 0) avrcp_continuing.size = attrlen; } avrcp_continuing.num = num; return true; failed: avrcp_continuing.num = 0; avrcp_continuing.size = 0; return false; } static bool avrcp_get_play_status(struct avctp_frame *avctp_frame, uint8_t ctype, uint8_t len, uint8_t indent) { struct l2cap_frame *frame = &avctp_frame->l2cap_frame; uint32_t interval; uint8_t status; if (ctype <= AVC_CTYPE_GENERAL_INQUIRY) return true; if (!l2cap_frame_get_be32(frame, &interval)) return false; print_field("%*cSongLength: 0x%08x (%u miliseconds)", (indent - 8), ' ', interval, interval); if (!l2cap_frame_get_be32(frame, &interval)) return false; print_field("%*cSongPosition: 0x%08x (%u miliseconds)", (indent - 8), ' ', interval, interval); if (!l2cap_frame_get_u8(frame, &status)) return false; print_field("%*cPlayStatus: 0x%02x (%s)", (indent - 8), ' ', status, playstatus2str(status)); return true; } static bool avrcp_register_notification(struct avctp_frame *avctp_frame, uint8_t ctype, uint8_t len, uint8_t indent) { struct l2cap_frame *frame = &avctp_frame->l2cap_frame; uint8_t event, status; uint16_t uid; uint32_t interval; uint64_t id; if (ctype > AVC_CTYPE_GENERAL_INQUIRY) goto response; if (!l2cap_frame_get_u8(frame, &event)) return false; print_field("%*cEventID: 0x%02x (%s)", (indent - 8), ' ', event, event2str(event)); if (!l2cap_frame_get_be32(frame, &interval)) return false; print_field("%*cInterval: 0x%08x (%u seconds)", (indent - 8), ' ', interval, interval); return true; response: if (!l2cap_frame_get_u8(frame, &event)) return false; print_field("%*cEventID: 0x%02x (%s)", (indent - 8), ' ', event, event2str(event)); switch (event) { case AVRCP_EVENT_PLAYBACK_STATUS_CHANGED: if (!l2cap_frame_get_u8(frame, &status)) return false; print_field("%*cPlayStatus: 0x%02x (%s)", (indent - 8), ' ', status, playstatus2str(status)); break; case AVRCP_EVENT_TRACK_CHANGED: if (!l2cap_frame_get_be64(frame, &id)) return false; print_field("%*cIdentifier: 0x%16" PRIx64 " (%" PRIu64 ")", (indent - 8), ' ', id, id); break; case AVRCP_EVENT_PLAYBACK_POS_CHANGED: if (!l2cap_frame_get_be32(frame, &interval)) return false; print_field("%*cPosition: 0x%08x (%u miliseconds)", (indent - 8), ' ', interval, interval); break; case AVRCP_EVENT_BATT_STATUS_CHANGED: if (!l2cap_frame_get_u8(frame, &status)) return false; print_field("%*cBatteryStatus: 0x%02x (%s)", (indent - 8), ' ', status, status2str(status)); break; case AVRCP_EVENT_SYSTEM_STATUS_CHANGED: if (!l2cap_frame_get_u8(frame, &status)) return false; print_field("%*cSystemStatus: 0x%02x ", (indent - 8), ' ', status); switch (status) { case 0x00: printf("(POWER_ON)\n"); break; case 0x01: printf("(POWER_OFF)\n"); break; case 0x02: printf("(UNPLUGGED)\n"); break; default: printf("(UNKNOWN)\n"); break; } break; case AVRCP_EVENT_PLAYER_APPLICATION_SETTING_CHANGED: if (!l2cap_frame_get_u8(frame, &status)) return false; print_field("%*cAttributeCount: 0x%02x", (indent - 8), ' ', status); for (; status > 0; status--) { uint8_t attr, value; if (!l2cap_frame_get_u8(frame, &attr)) return false; print_field("%*cAttributeID: 0x%02x (%s)", (indent - 8), ' ', attr, attr2str(attr)); if (!l2cap_frame_get_u8(frame, &value)) return false; print_field("%*cValueID: 0x%02x (%s)", (indent - 8), ' ', value, value2str(attr, value)); } break; case AVRCP_EVENT_VOLUME_CHANGED: if (!l2cap_frame_get_u8(frame, &status)) return false; status &= 0x7F; print_field("%*cVolume: %.2f%% (%d/127)", (indent - 8), ' ', status/1.27, status); break; case AVRCP_EVENT_ADDRESSED_PLAYER_CHANGED: if (!l2cap_frame_get_be16(frame, &uid)) return false; print_field("%*cPlayerID: 0x%04x (%u)", (indent - 8), ' ', uid, uid); if (!l2cap_frame_get_be16(frame, &uid)) return false; print_field("%*cUIDCounter: 0x%04x (%u)", (indent - 8), ' ', uid, uid); break; case AVRCP_EVENT_UIDS_CHANGED: if (!l2cap_frame_get_be16(frame, &uid)) return false; print_field("%*cUIDCounter: 0x%04x (%u)", (indent - 8), ' ', uid, uid); break; } return true; } static bool avrcp_set_absolute_volume(struct avctp_frame *avctp_frame, uint8_t ctype, uint8_t len, uint8_t indent) { struct l2cap_frame *frame = &avctp_frame->l2cap_frame; uint8_t value; if (!l2cap_frame_get_u8(frame, &value)) return false; value &= 0x7F; print_field("%*cVolume: %.2f%% (%d/127)", (indent - 8), ' ', value/1.27, value); return true; } static bool avrcp_set_addressed_player(struct avctp_frame *avctp_frame, uint8_t ctype, uint8_t len, uint8_t indent) { struct l2cap_frame *frame = &avctp_frame->l2cap_frame; uint16_t id; uint8_t status; if (ctype > AVC_CTYPE_GENERAL_INQUIRY) goto response; if (!l2cap_frame_get_be16(frame, &id)) return false; print_field("%*cPlayerID: 0x%04x (%u)", (indent - 8), ' ', id, id); return true; response: if (!l2cap_frame_get_u8(frame, &status)) return false; print_field("%*cStatus: 0x%02x (%s)", (indent - 8), ' ', status, error2str(status)); return true; } static bool avrcp_play_item(struct avctp_frame *avctp_frame, uint8_t ctype, uint8_t len, uint8_t indent) { struct l2cap_frame *frame = &avctp_frame->l2cap_frame; uint64_t uid; uint16_t uidcounter; uint8_t scope, status; if (ctype > AVC_CTYPE_GENERAL_INQUIRY) goto response; if (!l2cap_frame_get_u8(frame, &scope)) return false; print_field("%*cScope: 0x%02x (%s)", (indent - 8), ' ', scope, scope2str(scope)); if (!l2cap_frame_get_be64(frame, &uid)) return false; print_field("%*cUID: 0x%16" PRIx64 " (%" PRIu64 ")", (indent - 8), ' ', uid, uid); if (!l2cap_frame_get_be16(frame, &uidcounter)) return false; print_field("%*cUIDCounter: 0x%04x (%u)", (indent - 8), ' ', uidcounter, uidcounter); return true; response: if (!l2cap_frame_get_u8(frame, &status)) return false; print_field("%*cStatus: 0x%02x (%s)", (indent - 8), ' ', status, error2str(status)); return true; } static bool avrcp_add_to_now_playing(struct avctp_frame *avctp_frame, uint8_t ctype, uint8_t len, uint8_t indent) { struct l2cap_frame *frame = &avctp_frame->l2cap_frame; uint64_t uid; uint16_t uidcounter; uint8_t scope, status; if (ctype > AVC_CTYPE_GENERAL_INQUIRY) goto response; if (!l2cap_frame_get_u8(frame, &scope)) return false; print_field("%*cScope: 0x%02x (%s)", (indent - 8), ' ', scope, scope2str(scope)); if (!l2cap_frame_get_be64(frame, &uid)) return false; print_field("%*cUID: 0x%16" PRIx64 " (%" PRIu64 ")", (indent - 8), ' ', uid, uid); if (!l2cap_frame_get_be16(frame, &uidcounter)) return false; print_field("%*cUIDCounter: 0x%04x (%u)", (indent - 8), ' ', uidcounter, uidcounter); return true; response: if (!l2cap_frame_get_u8(frame, &status)) return false; print_field("%*cStatus: 0x%02x (%s)", (indent - 8), ' ', status, error2str(status)); return true; } struct avrcp_ctrl_pdu_data { uint8_t pduid; bool (*func) (struct avctp_frame *avctp_frame, uint8_t ctype, uint8_t len, uint8_t indent); }; static const struct avrcp_ctrl_pdu_data avrcp_ctrl_pdu_table[] = { { 0x10, avrcp_get_capabilities }, { 0x11, avrcp_list_player_attributes }, { 0x12, avrcp_list_player_values }, { 0x13, avrcp_get_current_player_value }, { 0x14, avrcp_set_player_value }, { 0x15, avrcp_get_player_attribute_text }, { 0x16, avrcp_get_player_value_text }, { 0x17, avrcp_displayable_charset }, { 0x20, avrcp_get_element_attributes }, { 0x30, avrcp_get_play_status }, { 0x31, avrcp_register_notification }, { 0x50, avrcp_set_absolute_volume }, { 0x60, avrcp_set_addressed_player }, { 0x74, avrcp_play_item }, { 0x90, avrcp_add_to_now_playing }, { } }; static bool avrcp_rejected_packet(struct l2cap_frame *frame, uint8_t indent) { uint8_t status; if (!l2cap_frame_get_u8(frame, &status)) return false; print_field("%*cError: 0x%02x (%s)", (indent - 8), ' ', status, error2str(status)); return true; } static bool avrcp_pdu_packet(struct avctp_frame *avctp_frame, uint8_t ctype, uint8_t indent) { struct l2cap_frame *frame = &avctp_frame->l2cap_frame; uint8_t pduid; uint16_t len; int i; const struct avrcp_ctrl_pdu_data *ctrl_pdu_data = NULL; if (!l2cap_frame_get_u8(frame, &pduid)) return false; if (!l2cap_frame_get_u8(frame, &avctp_frame->pt)) return false; if (!l2cap_frame_get_be16(frame, &len)) return false; print_indent(indent, COLOR_OFF, "AVRCP: ", pdu2str(pduid), COLOR_OFF, " pt %s len 0x%04x", pt2str(avctp_frame->pt), len); if (frame->size != len) return false; if (ctype == 0xA) return avrcp_rejected_packet(frame, indent + 2); for (i = 0; avrcp_ctrl_pdu_table[i].func; i++) { if (avrcp_ctrl_pdu_table[i].pduid == pduid) { ctrl_pdu_data = &avrcp_ctrl_pdu_table[i]; break; } } if (!ctrl_pdu_data || !ctrl_pdu_data->func) { packet_hexdump(frame->data, frame->size); return true; } return ctrl_pdu_data->func(avctp_frame, ctype, len, indent + 2); } static bool avrcp_control_packet(struct avctp_frame *avctp_frame) { struct l2cap_frame *frame = &avctp_frame->l2cap_frame; uint8_t ctype, address, subunit, opcode, company[3], indent = 2; if (!l2cap_frame_get_u8(frame, &ctype) || !l2cap_frame_get_u8(frame, &address) || !l2cap_frame_get_u8(frame, &opcode)) return false; print_field("AV/C: %s: address 0x%02x opcode 0x%02x", ctype2str(ctype), address, opcode); subunit = address >> 3; print_field("%*cSubunit: %s", indent, ' ', subunit2str(subunit)); print_field("%*cOpcode: %s", indent, ' ', opcode2str(opcode)); /* Skip non-panel subunit packets */ if (subunit != 0x09) { packet_hexdump(frame->data, frame->size); return true; } /* Not implemented should not contain any operand */ if (ctype == 0x8) { packet_hexdump(frame->data, frame->size); return true; } switch (opcode) { case 0x7c: return avrcp_passthrough_packet(avctp_frame, 10); case 0x00: if (!l2cap_frame_get_u8(frame, &company[0]) || !l2cap_frame_get_u8(frame, &company[1]) || !l2cap_frame_get_u8(frame, &company[2])) return false; print_field("%*cCompany ID: 0x%02x%02x%02x", indent, ' ', company[0], company[1], company[2]); return avrcp_pdu_packet(avctp_frame, ctype, 10); default: packet_hexdump(frame->data, frame->size); return true; } } static const char *dir2str(uint8_t dir) { switch (dir) { case 0x00: return "Folder Up"; case 0x01: return "Folder Down"; } return "Reserved"; } static bool avrcp_change_path(struct avctp_frame *avctp_frame) { struct l2cap_frame *frame = &avctp_frame->l2cap_frame; uint64_t uid; uint32_t items; uint16_t uidcounter; uint8_t dir, status, indent = 2; if (avctp_frame->hdr & 0x02) goto response; if (frame->size < 11) { print_field("%*cPDU Malformed", indent, ' '); packet_hexdump(frame->data, frame->size); return false; } if (!l2cap_frame_get_be16(frame, &uidcounter)) return false; print_field("%*cUIDCounter: 0x%04x (%u)", indent, ' ', uidcounter, uidcounter); if (!l2cap_frame_get_u8(frame, &dir)) return false; print_field("%*cDirection: 0x%02x (%s)", indent, ' ', dir, dir2str(dir)); if (!l2cap_frame_get_be64(frame, &uid)) return false; print_field("%*cFolderUID: 0x%16" PRIx64 " (%" PRIu64 ")", indent, ' ', uid, uid); return true; response: if (!l2cap_frame_get_u8(frame, &status)) return false; print_field("%*cStatus: 0x%02x (%s)", indent, ' ', status, error2str(status)); if (frame->size == 1) return false; if (!l2cap_frame_get_be32(frame, &items)) return false; print_field("%*cNumber of Items: 0x%04x (%u)", indent, ' ', items, items); return true; } static const struct { const char *str; bool reserved; } features_table[] = { /* Ignore passthrough bits */ [58] = { "Advanced Control Player" }, [59] = { "Browsing" }, [60] = { "Searching" }, [61] = { "AddToNowPlaying" }, [62] = { "Unique UIDs" }, [63] = { "OnlyBrowsableWhenAddressed" }, [64] = { "OnlySearchableWhenAddressed" }, [65] = { "NowPlaying" }, [66] = { "UIDPersistency" }, /* 67-127 reserved */ [67 ... 127] = { .reserved = true }, }; static void print_features(uint8_t features[16], uint8_t indent) { int i; for (i = 0; i < 127; i++) { if (!(features[i / 8] & (1 << (i % 8)))) continue; if (features_table[i].reserved) { print_text(COLOR_WHITE_BG, "Unknown bit %u", i); continue; } if (!features_table[i].str) continue; print_field("%*c%s", indent, ' ', features_table[i].str); } } static bool avrcp_media_player_item(struct avctp_frame *avctp_frame, uint8_t indent) { struct l2cap_frame *frame = &avctp_frame->l2cap_frame; uint16_t id, charset, namelen; uint8_t type, status, i; uint32_t subtype; uint8_t features[16]; if (!l2cap_frame_get_be16(frame, &id)) return false; print_field("%*cPlayerID: 0x%04x (%u)", indent, ' ', id, id); if (!l2cap_frame_get_u8(frame, &type)) return false; print_field("%*cPlayerType: 0x%04x (%s)", indent, ' ', type, playertype2str(type)); if (!l2cap_frame_get_be32(frame, &subtype)) return false; print_field("%*cPlayerSubType: 0x%08x (%s)", indent, ' ', subtype, playersubtype2str(subtype)); if (!l2cap_frame_get_u8(frame, &status)) return false; print_field("%*cPlayStatus: 0x%02x (%s)", indent, ' ', status, playstatus2str(status)); printf("%*cFeatures: 0x", indent+8, ' '); for (i = 0; i < 16; i++) { if (!l2cap_frame_get_u8(frame, &features[i])) return false; printf("%02x", features[i]); } printf("\n"); print_features(features, indent + 2); if (!l2cap_frame_get_be16(frame, &charset)) return false; print_field("%*cCharsetID: 0x%04x (%s)", indent, ' ', charset, charset2str(charset)); if (!l2cap_frame_get_be16(frame, &namelen)) return false; print_field("%*cNameLength: 0x%04x (%u)", indent, ' ', namelen, namelen); printf("%*cName: ", indent+8, ' '); for (; namelen > 0; namelen--) { uint8_t c; if (!l2cap_frame_get_u8(frame, &c)) return false; printf("%1c", isprint(c) ? c : '.'); } printf("\n"); return true; } static bool avrcp_folder_item(struct avctp_frame *avctp_frame, uint8_t indent) { struct l2cap_frame *frame = &avctp_frame->l2cap_frame; uint8_t type, playable; uint16_t charset, namelen; uint64_t uid; if (frame->size < 14) { printf("PDU Malformed\n"); return false; } if (!l2cap_frame_get_be64(frame, &uid)) return false; print_field("%*cFolderUID: 0x%16" PRIx64 " (%" PRIu64 ")", indent, ' ', uid, uid); if (!l2cap_frame_get_u8(frame, &type)) return false; print_field("%*cFolderType: 0x%02x (%s)", indent, ' ', type, foldertype2str(type)); if (!l2cap_frame_get_u8(frame, &playable)) return false; print_field("%*cIsPlayable: 0x%02x (%s)", indent, ' ', playable, playable & 0x01 ? "True" : "False"); if (!l2cap_frame_get_be16(frame, &charset)) return false; print_field("%*cCharsetID: 0x%04x (%s)", indent, ' ', charset, charset2str(charset)); if (!l2cap_frame_get_be16(frame, &namelen)) return false; print_field("%*cNameLength: 0x%04x (%u)", indent, ' ', namelen, namelen); printf("%*cName: ", indent+8, ' '); for (; namelen > 0; namelen--) { uint8_t c; if (!l2cap_frame_get_u8(frame, &c)) return false; printf("%1c", isprint(c) ? c : '.'); } printf("\n"); return true; } static bool avrcp_attribute_entry_list(struct avctp_frame *avctp_frame, uint8_t indent, uint8_t count) { struct l2cap_frame *frame = &avctp_frame->l2cap_frame; for (; count > 0; count--) { uint32_t attr; uint16_t charset, len; if (!l2cap_frame_get_be32(frame, &attr)) return false; print_field("%*cAttributeID: 0x%08x (%s)", indent, ' ', attr, mediattr2str(attr)); if (!l2cap_frame_get_be16(frame, &charset)) return false; print_field("%*cCharsetID: 0x%04x (%s)", indent, ' ', charset, charset2str(charset)); if (!l2cap_frame_get_be16(frame, &len)) return false; print_field("%*cAttributeLength: 0x%04x (%u)", indent, ' ', len, len); printf("%*cAttributeValue: ", indent+8, ' '); for (; len > 0; len--) { uint8_t c; if (!l2cap_frame_get_u8(frame, &c)) return false; printf("%1c", isprint(c) ? c : '.'); } printf("\n"); } return true; } static bool avrcp_media_element_item(struct avctp_frame *avctp_frame, uint8_t indent) { struct l2cap_frame *frame = &avctp_frame->l2cap_frame; uint64_t uid; uint16_t charset, namelen; uint8_t type, count; if (!l2cap_frame_get_be64(frame, &uid)) return false; print_field("%*cElementUID: 0x%16" PRIx64 " (%" PRIu64 ")", indent, ' ', uid, uid); if (!l2cap_frame_get_u8(frame, &type)) return false; print_field("%*cElementType: 0x%02x (%s)", indent, ' ', type, elementtype2str(type)); if (!l2cap_frame_get_be16(frame, &charset)) return false; print_field("%*cCharsetID: 0x%04x (%s)", indent, ' ', charset, charset2str(charset)); if (!l2cap_frame_get_be16(frame, &namelen)) return false; print_field("%*cNameLength: 0x%04x (%u)", indent, ' ', namelen, namelen); printf("%*cName: ", indent+8, ' '); for (; namelen > 0; namelen--) { uint8_t c; if (!l2cap_frame_get_u8(frame, &c)) return false; printf("%1c", isprint(c) ? c : '.'); } printf("\n"); if (!l2cap_frame_get_u8(frame, &count)) return false; print_field("%*cAttributeCount: 0x%02x (%u)", indent, ' ', count, count); if (!avrcp_attribute_entry_list(avctp_frame, indent, count)) return false; return true; } static bool avrcp_general_reject(struct avctp_frame *avctp_frame) { struct l2cap_frame *frame = &avctp_frame->l2cap_frame; uint8_t status, indent = 2; if (avctp_frame->hdr & 0x02) goto response; print_field("%*cPDU Malformed", indent, ' '); packet_hexdump(frame->data, frame->size); return true; response: if (!l2cap_frame_get_u8(frame, &status)) return false; print_field("%*cStatus: 0x%02x (%s)", indent, ' ', status, error2str(status)); return true; } static bool avrcp_get_total_number_of_items(struct avctp_frame *avctp_frame) { struct l2cap_frame *frame = &avctp_frame->l2cap_frame; uint32_t num_of_items; uint16_t uidcounter; uint8_t scope, status, indent = 2; if (avctp_frame->hdr & 0x02) goto response; if (frame->size < 4) { printf("PDU Malformed\n"); packet_hexdump(frame->data, frame->size); return false; } if (!l2cap_frame_get_u8(frame, &scope)) return false; print_field("%*cScope: 0x%02x (%s)", (indent - 8), ' ', scope, scope2str(scope)); return true; response: if (!l2cap_frame_get_u8(frame, &status)) return false; print_field("%*cStatus: 0x%02x (%s)", indent, ' ', status, error2str(status)); if (frame->size == 1) return false; if (!l2cap_frame_get_be16(frame, &uidcounter)) return false; print_field("%*cUIDCounter: 0x%04x (%u)", indent, ' ', uidcounter, uidcounter); if (!l2cap_frame_get_be32(frame, &num_of_items)) return false; print_field("%*cNumber of Items: 0x%04x (%u)", indent, ' ', num_of_items, num_of_items); return true; } static bool avrcp_search_item(struct avctp_frame *avctp_frame) { struct l2cap_frame *frame = &avctp_frame->l2cap_frame; uint32_t items; uint16_t charset, namelen, uidcounter; uint8_t status, indent = 2; if (avctp_frame->hdr & 0x02) goto response; if (frame->size < 4) { printf("PDU Malformed\n"); packet_hexdump(frame->data, frame->size); return false; } if (!l2cap_frame_get_be16(frame, &charset)) return false; print_field("%*cCharsetID: 0x%04x (%s)", indent, ' ', charset, charset2str(charset)); if (!l2cap_frame_get_be16(frame, &namelen)) return false; print_field("%*cLength: 0x%04x (%u)", indent, ' ', namelen, namelen); printf("%*cString: ", indent+8, ' '); for (; namelen > 0; namelen--) { uint8_t c; if (!l2cap_frame_get_u8(frame, &c)) return false; printf("%1c", isprint(c) ? c : '.'); } printf("\n"); return true; response: if (!l2cap_frame_get_u8(frame, &status)) return false; print_field("%*cStatus: 0x%02x (%s)", indent, ' ', status, error2str(status)); if (frame->size == 1) return false; if (!l2cap_frame_get_be16(frame, &uidcounter)) return false; print_field("%*cUIDCounter: 0x%04x (%u)", indent, ' ', uidcounter, uidcounter); if (!l2cap_frame_get_be32(frame, &items)) return false; print_field("%*cNumber of Items: 0x%04x (%u)", indent, ' ', items, items); return true; } static bool avrcp_get_item_attributes(struct avctp_frame *avctp_frame) { struct l2cap_frame *frame = &avctp_frame->l2cap_frame; uint64_t uid; uint16_t uidcounter; uint8_t scope, count, status, indent = 2; if (avctp_frame->hdr & 0x02) goto response; if (frame->size < 12) { print_field("%*cPDU Malformed", indent, ' '); packet_hexdump(frame->data, frame->size); return false; } if (!l2cap_frame_get_u8(frame, &scope)) return false; print_field("%*cScope: 0x%02x (%s)", indent, ' ', scope, scope2str(scope)); if (!l2cap_frame_get_be64(frame, &uid)) return false; print_field("%*cUID: 0x%016" PRIx64 " (%" PRIu64 ")", indent, ' ', uid, uid); if (!l2cap_frame_get_be16(frame, &uidcounter)) return false; print_field("%*cUIDCounter: 0x%04x (%u)", indent, ' ', uidcounter, uidcounter); if (!l2cap_frame_get_u8(frame, &count)) return false; print_field("%*cAttributeCount: 0x%02x (%u)", indent, ' ', count, count); for (; count > 0; count--) { uint32_t attr; if (!l2cap_frame_get_be32(frame, &attr)) return false; print_field("%*cAttributeID: 0x%08x (%s)", indent, ' ', attr, mediattr2str(attr)); } return true; response: if (!l2cap_frame_get_u8(frame, &status)) return false; print_field("%*cStatus: 0x%02x (%s)", indent, ' ', status, error2str(status)); if (frame->size == 1) return false; if (!l2cap_frame_get_u8(frame, &count)) return false; print_field("%*cAttributeCount: 0x%02x (%u)", indent, ' ', count, count); if (!avrcp_attribute_entry_list(avctp_frame, indent, count)) return false; return true; } static bool avrcp_get_folder_items(struct avctp_frame *avctp_frame) { struct l2cap_frame *frame = &avctp_frame->l2cap_frame; uint8_t scope, count, status, indent = 2; uint32_t start, end; uint16_t uid, num; if (avctp_frame->hdr & 0x02) goto response; if (!l2cap_frame_get_u8(frame, &scope)) return false; print_field("%*cScope: 0x%02x (%s)", indent, ' ', scope, scope2str(scope)); if (!l2cap_frame_get_be32(frame, &start)) return false; print_field("%*cStartItem: 0x%08x (%u)", indent, ' ', start, start); if (!l2cap_frame_get_be32(frame, &end)) return false; print_field("%*cEndItem: 0x%08x (%u)", indent, ' ', end, end); if (!l2cap_frame_get_u8(frame, &count)) return false; print_field("%*cAttributeCount: 0x%02x (%u)", indent, ' ', count, count); for (; count > 0; count--) { uint32_t attr; if (!l2cap_frame_get_be32(frame, &attr)) return false; print_field("%*cAttributeID: 0x%08x (%s)", indent, ' ', attr, mediattr2str(attr)); } return false; response: if (!l2cap_frame_get_u8(frame, &status)) return false; print_field("%*cStatus: 0x%02x (%s)", indent, ' ', status, error2str(status)); if (!l2cap_frame_get_be16(frame, &uid)) return false; print_field("%*cUIDCounter: 0x%04x (%u)", indent, ' ', uid, uid); if (!l2cap_frame_get_be16(frame, &num)) return false; print_field("%*cNumOfItems: 0x%04x (%u)", indent, ' ', num, num); for (; num > 0; num--) { uint8_t type; uint16_t len; if (!l2cap_frame_get_u8(frame, &type)) return false; if (!l2cap_frame_get_be16(frame, &len)) return false; print_field("%*cItem: 0x%02x (%s) ", indent, ' ', type, type2str(type)); print_field("%*cLength: 0x%04x (%u)", indent, ' ', len, len); switch (type) { case AVRCP_MEDIA_PLAYER_ITEM_TYPE: avrcp_media_player_item(avctp_frame, indent); break; case AVRCP_FOLDER_ITEM_TYPE: avrcp_folder_item(avctp_frame, indent); break; case AVRCP_MEDIA_ELEMENT_ITEM_TYPE: avrcp_media_element_item(avctp_frame, indent); break; default: print_field("%*cUnknown Media Item type", indent, ' '); packet_hexdump(frame->data, frame->size); break; } } return true; } static bool avrcp_set_browsed_player(struct avctp_frame *avctp_frame) { struct l2cap_frame *frame = &avctp_frame->l2cap_frame; uint32_t items; uint16_t id, uids, charset; uint8_t status, folders, indent = 2; if (avctp_frame->hdr & 0x02) goto response; if (!l2cap_frame_get_be16(frame, &id)) return false; print_field("%*cPlayerID: 0x%04x (%u)", indent, ' ', id, id); return true; response: if (!l2cap_frame_get_u8(frame, &status)) return false; print_field("%*cStatus: 0x%02x (%s)", indent, ' ', status, error2str(status)); if (!l2cap_frame_get_be16(frame, &uids)) return false; print_field("%*cUIDCounter: 0x%04x (%u)", indent, ' ', uids, uids); if (!l2cap_frame_get_be32(frame, &items)) return false; print_field("%*cNumber of Items: 0x%08x (%u)", indent, ' ', items, items); if (!l2cap_frame_get_be16(frame, &charset)) return false; print_field("%*cCharsetID: 0x%04x (%s)", indent, ' ', charset, charset2str(charset)); if (!l2cap_frame_get_u8(frame, &folders)) return false; print_field("%*cFolder Depth: 0x%02x (%u)", indent, ' ', folders, folders); for (; folders > 0; folders--) { uint8_t len; if (!l2cap_frame_get_u8(frame, &len)) return false; if (!len) { print_field("%*cFolder: ", indent, ' '); continue; } printf("%*cFolder: ", indent+8, ' '); for (; len > 0; len--) { uint8_t c; if (!l2cap_frame_get_u8(frame, &c)) return false; printf("%1c", isprint(c) ? c : '.'); } printf("\n"); } return true; } static bool avrcp_browsing_packet(struct avctp_frame *avctp_frame) { struct l2cap_frame *frame = &avctp_frame->l2cap_frame; uint16_t len; uint8_t pduid; if (!l2cap_frame_get_u8(frame, &pduid)) return false; if (!l2cap_frame_get_be16(frame, &len)) return false; print_field("AVRCP: %s: len 0x%04x", pdu2str(pduid), len); switch (pduid) { case AVRCP_SET_BROWSED_PLAYER: avrcp_set_browsed_player(avctp_frame); break; case AVRCP_GET_FOLDER_ITEMS: avrcp_get_folder_items(avctp_frame); break; case AVRCP_CHANGE_PATH: avrcp_change_path(avctp_frame); break; case AVRCP_GET_ITEM_ATTRIBUTES: avrcp_get_item_attributes(avctp_frame); break; case AVRCP_GET_TOTAL_NUMBER_OF_ITEMS: avrcp_get_total_number_of_items(avctp_frame); break; case AVRCP_SEARCH: avrcp_search_item(avctp_frame); break; case AVRCP_GENERAL_REJECT: avrcp_general_reject(avctp_frame); break; default: packet_hexdump(frame->data, frame->size); } return true; } static void avrcp_packet(struct avctp_frame *avctp_frame) { struct l2cap_frame *frame = &avctp_frame->l2cap_frame; bool ret; switch (frame->psm) { case 0x17: ret = avrcp_control_packet(avctp_frame); break; case 0x1B: ret = avrcp_browsing_packet(avctp_frame); break; default: packet_hexdump(frame->data, frame->size); return; } if (!ret) { print_text(COLOR_ERROR, "PDU malformed"); packet_hexdump(frame->data, frame->size); } } void avctp_packet(const struct l2cap_frame *frame) { struct l2cap_frame *l2cap_frame; struct avctp_frame avctp_frame; const char *pdu_color; l2cap_frame_pull(&avctp_frame.l2cap_frame, frame, 0); l2cap_frame = &avctp_frame.l2cap_frame; if (!l2cap_frame_get_u8(l2cap_frame, &avctp_frame.hdr) || !l2cap_frame_get_be16(l2cap_frame, &avctp_frame.pid)) { print_text(COLOR_ERROR, "frame too short"); packet_hexdump(frame->data, frame->size); return; } if (frame->in) pdu_color = COLOR_MAGENTA; else pdu_color = COLOR_BLUE; print_indent(6, pdu_color, "AVCTP", "", COLOR_OFF, " %s: %s: type 0x%02x label %d PID 0x%04x", frame->psm == 23 ? "Control" : "Browsing", avctp_frame.hdr & 0x02 ? "Response" : "Command", avctp_frame.hdr & 0x0c, avctp_frame.hdr >> 4, avctp_frame.pid); if (avctp_frame.pid == 0x110e || avctp_frame.pid == 0x110c) avrcp_packet(&avctp_frame); else packet_hexdump(frame->data, frame->size); } bluez-5.82/monitor/PaxHeaders/analyze.h0000644000000000000000000000005014015011623015142 xustar0020 atime=1743515951 20 ctime=1743591282 bluez-5.82/monitor/analyze.h0000644000000000000000000000041414015011623014622 0ustar00rootroot/* SPDX-License-Identifier: LGPL-2.1-or-later */ /* * * BlueZ - Bluetooth protocol stack for Linux * * Copyright (C) 2011-2014 Intel Corporation * Copyright (C) 2002-2010 Marcel Holtmann * * */ void analyze_trace(const char *path); bluez-5.82/monitor/PaxHeaders/l2cap.h0000644000000000000000000000005014447506754014527 xustar0020 atime=1743515957 20 ctime=1743591282 bluez-5.82/monitor/l2cap.h0000644000000000000000000001617714447506754014224 0ustar00rootroot/* SPDX-License-Identifier: LGPL-2.1-or-later */ /* * * BlueZ - Bluetooth protocol stack for Linux * * Copyright (C) 2011-2014 Intel Corporation * Copyright (C) 2002-2010 Marcel Holtmann * * */ #include #include struct l2cap_frame { uint16_t index; bool in; uint16_t handle; uint8_t ident; uint16_t cid; uint16_t psm; uint16_t chan; uint8_t mode; uint8_t seq_num; const void *data; uint16_t size; }; void l2cap_frame_init(struct l2cap_frame *frame, uint16_t index, bool in, uint16_t handle, uint8_t ident, uint16_t cid, uint16_t psm, const void *data, uint16_t size); static inline void l2cap_frame_clone_size(struct l2cap_frame *frame, const struct l2cap_frame *source, uint16_t size) { if (frame != source) { frame->index = source->index; frame->in = source->in; frame->handle = source->handle; frame->ident = source->ident; frame->cid = source->cid; frame->psm = source->psm; frame->chan = source->chan; frame->mode = source->mode; frame->data = source->data; frame->size = size; } } static inline void l2cap_frame_clone(struct l2cap_frame *frame, const struct l2cap_frame *source) { l2cap_frame_clone_size(frame, source, source->size); } static inline void *l2cap_frame_pull(struct l2cap_frame *frame, const struct l2cap_frame *source, uint16_t len) { void *data; l2cap_frame_clone(frame, source); if (source->size < len) return NULL; data = (void *)frame->data; frame->data = source->data + len; frame->size = source->size - len; return data; } static inline bool l2cap_frame_get_u8(struct l2cap_frame *frame, uint8_t *value) { if (frame->size < sizeof(*value)) return false; if (value) *value = *((uint8_t *) frame->data); l2cap_frame_pull(frame, frame, sizeof(*value)); return true; } static inline bool l2cap_frame_print_u8(struct l2cap_frame *frame, const char *label) { uint8_t u8; if (!l2cap_frame_get_u8(frame, &u8)) { print_text(COLOR_ERROR, "%s: invalid size", label); return false; } print_field("%s: 0x%2.2x", label, u8); return true; } static inline bool l2cap_frame_get_be16(struct l2cap_frame *frame, uint16_t *value) { if (frame->size < sizeof(*value)) return false; if (value) *value = get_be16(frame->data); l2cap_frame_pull(frame, frame, sizeof(*value)); return true; } static inline bool l2cap_frame_print_be16(struct l2cap_frame *frame, const char *label) { uint16_t u16; if (!l2cap_frame_get_be16(frame, &u16)) { print_text(COLOR_ERROR, "%s: invalid size", label); return false; } print_field("%s: 0x%4.4x", label, u16); return true; } static inline bool l2cap_frame_get_le16(struct l2cap_frame *frame, uint16_t *value) { if (frame->size < sizeof(*value)) return false; if (value) *value = get_le16(frame->data); l2cap_frame_pull(frame, frame, sizeof(*value)); return true; } static inline bool l2cap_frame_print_le16(struct l2cap_frame *frame, const char *label) { uint16_t u16; if (!l2cap_frame_get_le16(frame, &u16)) { print_text(COLOR_ERROR, "%s: invalid size", label); return false; } print_field("%s: 0x%4.4x", label, u16); return true; } static inline bool l2cap_frame_get_be24(struct l2cap_frame *frame, uint32_t *value) { if (frame->size < sizeof(uint24_t)) return false; if (value) *value = get_be24(frame->data); l2cap_frame_pull(frame, frame, sizeof(uint24_t)); return true; } static inline bool l2cap_frame_print_be24(struct l2cap_frame *frame, const char *label) { uint32_t u24; if (!l2cap_frame_get_be24(frame, &u24)) { print_text(COLOR_ERROR, "%s: invalid size", label); return false; } print_field("%s: 0x%6.6x", label, u24); return true; } static inline bool l2cap_frame_get_le24(struct l2cap_frame *frame, uint32_t *value) { if (frame->size < sizeof(uint24_t)) return false; if (value) *value = get_le24(frame->data); l2cap_frame_pull(frame, frame, sizeof(uint24_t)); return true; } static inline bool l2cap_frame_print_le24(struct l2cap_frame *frame, const char *label) { uint32_t u24; if (!l2cap_frame_get_le24(frame, &u24)) { print_text(COLOR_ERROR, "%s: invalid size", label); return false; } print_field("%s: 0x%6.6x", label, u24); return true; } static inline bool l2cap_frame_get_be32(struct l2cap_frame *frame, uint32_t *value) { if (frame->size < sizeof(*value)) return false; if (value) *value = get_be32(frame->data); l2cap_frame_pull(frame, frame, sizeof(*value)); return true; } static inline bool l2cap_frame_print_be32(struct l2cap_frame *frame, const char *label) { uint32_t u32; if (!l2cap_frame_get_be32(frame, &u32)) { print_text(COLOR_ERROR, "%s: invalid size", label); return false; } print_field("%s: 0x%8.8x", label, u32); return true; } static inline bool l2cap_frame_get_le32(struct l2cap_frame *frame, uint32_t *value) { if (frame->size < sizeof(*value)) return false; if (value) *value = get_le32(frame->data); l2cap_frame_pull(frame, frame, sizeof(*value)); return true; } static inline bool l2cap_frame_print_le32(struct l2cap_frame *frame, const char *label) { uint32_t u32; if (!l2cap_frame_get_le32(frame, &u32)) { print_text(COLOR_ERROR, "%s: invalid size", label); return false; } print_field("%s: 0x%8.8x", label, u32); return true; } static inline bool l2cap_frame_get_be64(struct l2cap_frame *frame, uint64_t *value) { if (frame->size < sizeof(*value)) return false; if (value) *value = get_be64(frame->data); l2cap_frame_pull(frame, frame, sizeof(*value)); return true; } static inline bool l2cap_frame_print_be64(struct l2cap_frame *frame, const char *label) { uint64_t u64; if (!l2cap_frame_get_be64(frame, &u64)) { print_text(COLOR_ERROR, "%s: invalid size", label); return false; } print_field("%s: 0x%" PRIx64, label, u64); return true; } static inline bool l2cap_frame_get_le64(struct l2cap_frame *frame, uint64_t *value) { if (frame->size < sizeof(*value)) return false; if (value) *value = get_le64(frame->data); l2cap_frame_pull(frame, frame, sizeof(*value)); return true; } static inline bool l2cap_frame_print_le64(struct l2cap_frame *frame, const char *label) { uint64_t u64; if (!l2cap_frame_get_le64(frame, &u64)) { print_text(COLOR_ERROR, "%s: invalid size", label); return false; } print_field("%s: 0x%" PRIx64, label, u64); return true; } static inline bool l2cap_frame_get_be128(struct l2cap_frame *frame, uint64_t *lvalue, uint64_t *rvalue) { if (frame->size < (sizeof(*lvalue) + sizeof(*rvalue))) return false; if (lvalue && rvalue) { *lvalue = get_be64(frame->data); *rvalue = get_be64(frame->data); } l2cap_frame_pull(frame, frame, (sizeof(*lvalue) + sizeof(*rvalue))); return true; } void l2cap_frame(uint16_t index, bool in, uint16_t handle, uint16_t cid, uint16_t psm, const void *data, uint16_t size); void l2cap_packet(uint16_t index, bool in, uint16_t handle, uint8_t flags, const void *data, uint16_t size); void rfcomm_packet(const struct l2cap_frame *frame); void l2cap_dequeue_frame(struct timeval *delta, struct packet_conn_data *conn); bluez-5.82/monitor/PaxHeaders/l2cap.c0000644000000000000000000000005014572354773014523 xustar0020 atime=1743516019 20 ctime=1743591282 bluez-5.82/monitor/l2cap.c0000644000000000000000000017546214572354773014223 0ustar00rootroot// SPDX-License-Identifier: LGPL-2.1-or-later /* * * BlueZ - Bluetooth protocol stack for Linux * * Copyright (C) 2011-2014 Intel Corporation * Copyright (C) 2002-2010 Marcel Holtmann * * */ #ifdef HAVE_CONFIG_H #include #endif #define _GNU_SOURCE #include #include #include #include #include "lib/bluetooth.h" #include "lib/uuid.h" #include "src/shared/util.h" #include "src/shared/queue.h" #include "bt.h" #include "packet.h" #include "display.h" #include "l2cap.h" #include "keys.h" #include "sdp.h" #include "avctp.h" #include "avdtp.h" #include "rfcomm.h" #include "bnep.h" #include "att.h" #define L2CAP_MODE_BASIC 0x00 #define L2CAP_MODE_RETRANS 0x01 #define L2CAP_MODE_FLOWCTL 0x02 #define L2CAP_MODE_ERTM 0x03 #define L2CAP_MODE_STREAMING 0x04 #define L2CAP_MODE_LE_FLOWCTL 0x80 #define L2CAP_MODE_ECRED 0x81 /* L2CAP Control Field bit masks */ #define L2CAP_CTRL_SAR_MASK 0xC000 #define L2CAP_CTRL_REQSEQ_MASK 0x3F00 #define L2CAP_CTRL_TXSEQ_MASK 0x007E #define L2CAP_CTRL_SUPERVISE_MASK 0x000C #define L2CAP_CTRL_RETRANS 0x0080 #define L2CAP_CTRL_FINAL 0x0080 #define L2CAP_CTRL_POLL 0x0010 #define L2CAP_CTRL_FRAME_TYPE 0x0001 /* I- or S-Frame */ #define L2CAP_CTRL_TXSEQ_SHIFT 1 #define L2CAP_CTRL_SUPER_SHIFT 2 #define L2CAP_CTRL_REQSEQ_SHIFT 8 #define L2CAP_CTRL_SAR_SHIFT 14 #define L2CAP_EXT_CTRL_TXSEQ_MASK 0xFFFC0000 #define L2CAP_EXT_CTRL_SAR_MASK 0x00030000 #define L2CAP_EXT_CTRL_SUPERVISE_MASK 0x00030000 #define L2CAP_EXT_CTRL_REQSEQ_MASK 0x0000FFFC #define L2CAP_EXT_CTRL_POLL 0x00040000 #define L2CAP_EXT_CTRL_FINAL 0x00000002 #define L2CAP_EXT_CTRL_FRAME_TYPE 0x00000001 /* I- or S-Frame */ #define L2CAP_EXT_CTRL_REQSEQ_SHIFT 2 #define L2CAP_EXT_CTRL_SAR_SHIFT 16 #define L2CAP_EXT_CTRL_SUPER_SHIFT 16 #define L2CAP_EXT_CTRL_TXSEQ_SHIFT 18 /* L2CAP Supervisory Function */ #define L2CAP_SUPER_RR 0x00 #define L2CAP_SUPER_REJ 0x01 #define L2CAP_SUPER_RNR 0x02 #define L2CAP_SUPER_SREJ 0x03 /* L2CAP Segmentation and Reassembly */ #define L2CAP_SAR_UNSEGMENTED 0x00 #define L2CAP_SAR_START 0x01 #define L2CAP_SAR_END 0x02 #define L2CAP_SAR_CONTINUE 0x03 #define MAX_CHAN 64 struct chan_data { uint16_t index; uint16_t handle; uint8_t ident; uint16_t scid; uint16_t dcid; uint16_t psm; uint8_t ctrlid; uint8_t mode; uint8_t ext_ctrl; uint8_t seq_num; uint16_t sdu; struct packet_latency tx_l; }; static struct chan_data chan_list[MAX_CHAN]; static void assign_scid(const struct l2cap_frame *frame, uint16_t scid, uint16_t psm, uint8_t mode, uint8_t ctrlid) { int i, n = -1; uint8_t seq_num = 1; if (!scid) return; for (i = 0; i < MAX_CHAN; i++) { if (n < 0 && chan_list[i].handle == 0x0000) { n = i; continue; } if (chan_list[i].index != frame->index) continue; if (chan_list[i].handle != frame->handle) continue; if (chan_list[i].psm == psm) seq_num++; /* Don't break on match - we still need to go through all * channels to find proper seq_num. */ if (frame->in) { if (chan_list[i].dcid == scid) n = i; } else { if (chan_list[i].scid == scid) n = i; } } if (n < 0) return; memset(&chan_list[n], 0, sizeof(chan_list[n])); chan_list[n].index = frame->index; chan_list[n].handle = frame->handle; chan_list[n].ident = frame->ident; if (frame->in) chan_list[n].dcid = scid; else chan_list[n].scid = scid; chan_list[n].psm = psm; chan_list[n].ctrlid = ctrlid; chan_list[n].mode = mode; chan_list[n].seq_num = seq_num; } static void release_scid(const struct l2cap_frame *frame, uint16_t scid) { int i; for (i = 0; i < MAX_CHAN; i++) { if (chan_list[i].index != frame->index) continue; if (chan_list[i].handle != frame->handle) continue; if (frame->in) { if (chan_list[i].scid == scid) { chan_list[i].handle = 0; break; } } else { if (chan_list[i].dcid == scid) { chan_list[i].handle = 0; break; } } } } static void assign_dcid(const struct l2cap_frame *frame, uint16_t dcid, uint16_t scid) { int i; for (i = 0; i < MAX_CHAN; i++) { if (chan_list[i].index != frame->index) continue; if (chan_list[i].handle != frame->handle) continue; if (frame->ident != 0 && chan_list[i].ident != frame->ident) continue; if (frame->in) { if (scid) { if (chan_list[i].scid == scid) { chan_list[i].dcid = dcid; break; } } else { if (chan_list[i].scid && !chan_list[i].dcid) { chan_list[i].dcid = dcid; break; } } } else { if (scid) { if (chan_list[i].dcid == scid) { chan_list[i].scid = dcid; break; } } else { if (chan_list[i].dcid && !chan_list[i].scid) { chan_list[i].scid = dcid; break; } } } } } static void assign_mode(const struct l2cap_frame *frame, uint8_t mode, uint16_t dcid) { int i; for (i = 0; i < MAX_CHAN; i++) { if (chan_list[i].index != frame->index) continue; if (chan_list[i].handle != frame->handle) continue; if (frame->in) { if (chan_list[i].scid == dcid) { chan_list[i].mode = mode; break; } } else { if (chan_list[i].dcid == dcid) { chan_list[i].mode = mode; break; } } } } static int get_chan_data_index(const struct l2cap_frame *frame) { int i; for (i = 0; i < MAX_CHAN; i++) { if (chan_list[i].index != frame->index && chan_list[i].ctrlid == 0) continue; if (chan_list[i].ctrlid != 0 && chan_list[i].ctrlid != frame->index) continue; if (chan_list[i].handle != frame->handle) continue; if (frame->in) { if (chan_list[i].scid == frame->cid) return i; } else { if (chan_list[i].dcid == frame->cid) return i; } } return -1; } static struct chan_data *get_chan(const struct l2cap_frame *frame) { int i; if (frame->chan != UINT16_MAX) return &chan_list[frame->chan]; i = get_chan_data_index(frame); if (i < 0) return NULL; return &chan_list[i]; } static uint16_t get_psm(const struct l2cap_frame *frame) { struct chan_data *data = get_chan(frame); if (!data) return 0; return data->psm; } static uint8_t get_mode(const struct l2cap_frame *frame) { struct chan_data *data = get_chan(frame); if (!data) return 0; return data->mode; } static uint8_t get_seq_num(const struct l2cap_frame *frame) { struct chan_data *data = get_chan(frame); if (!data) return 0; return data->seq_num; } static void assign_ext_ctrl(const struct l2cap_frame *frame, uint8_t ext_ctrl, uint16_t dcid) { int i; for (i = 0; i < MAX_CHAN; i++) { if (chan_list[i].index != frame->index) continue; if (chan_list[i].handle != frame->handle) continue; if (frame->in) { if (chan_list[i].scid == dcid) { chan_list[i].ext_ctrl = ext_ctrl; break; } } else { if (chan_list[i].dcid == dcid) { chan_list[i].ext_ctrl = ext_ctrl; break; } } } } static uint8_t get_ext_ctrl(const struct l2cap_frame *frame) { struct chan_data *data = get_chan(frame); if (!data) return 0; return data->ext_ctrl; } static char *sar2str(uint8_t sar) { switch (sar) { case L2CAP_SAR_UNSEGMENTED: return "Unsegmented"; case L2CAP_SAR_START: return "Start"; case L2CAP_SAR_END: return "End"; case L2CAP_SAR_CONTINUE: return "Continuation"; default: return "Bad SAR"; } } static char *supervisory2str(uint8_t supervisory) { switch (supervisory) { case L2CAP_SUPER_RR: return "Receiver Ready (RR)"; case L2CAP_SUPER_REJ: return "Reject (REJ)"; case L2CAP_SUPER_RNR: return "Receiver Not Ready (RNR)"; case L2CAP_SUPER_SREJ: return "Select Reject (SREJ)"; default: return "Bad Supervisory"; } } static char *mode2str(uint8_t mode) { switch (mode) { case L2CAP_MODE_BASIC: return "Basic"; case L2CAP_MODE_RETRANS: return "Retransmission"; case L2CAP_MODE_FLOWCTL: return "Flow Control"; case L2CAP_MODE_ERTM: return "Enhanced Retransmission"; case L2CAP_MODE_STREAMING: return "Streaming"; case L2CAP_MODE_LE_FLOWCTL: return "LE Flow Control"; case L2CAP_MODE_ECRED: return "Enhanced Credit"; default: return "Unknown"; } } static void l2cap_ctrl_ext_parse(struct l2cap_frame *frame, uint32_t ctrl) { printf(" %s:", ctrl & L2CAP_EXT_CTRL_FRAME_TYPE ? "S-frame" : "I-frame"); if (ctrl & L2CAP_EXT_CTRL_FRAME_TYPE) { printf(" %s", supervisory2str((ctrl & L2CAP_EXT_CTRL_SUPERVISE_MASK) >> L2CAP_EXT_CTRL_SUPER_SHIFT)); if (ctrl & L2CAP_EXT_CTRL_POLL) printf(" P-bit"); } else { uint8_t sar = (ctrl & L2CAP_EXT_CTRL_SAR_MASK) >> L2CAP_EXT_CTRL_SAR_SHIFT; printf(" %s", sar2str(sar)); if (sar == L2CAP_SAR_START) { uint16_t len; if (!l2cap_frame_get_le16(frame, &len)) return; printf(" (len %d)", len); } printf(" TxSeq %d", (ctrl & L2CAP_EXT_CTRL_TXSEQ_MASK) >> L2CAP_EXT_CTRL_TXSEQ_SHIFT); } printf(" ReqSeq %d", (ctrl & L2CAP_EXT_CTRL_REQSEQ_MASK) >> L2CAP_EXT_CTRL_REQSEQ_SHIFT); if (ctrl & L2CAP_EXT_CTRL_FINAL) printf(" F-bit"); } static void l2cap_ctrl_parse(struct l2cap_frame *frame, uint32_t ctrl) { printf(" %s:", ctrl & L2CAP_CTRL_FRAME_TYPE ? "S-frame" : "I-frame"); if (ctrl & 0x01) { printf(" %s", supervisory2str((ctrl & L2CAP_CTRL_SUPERVISE_MASK) >> L2CAP_CTRL_SUPER_SHIFT)); if (ctrl & L2CAP_CTRL_POLL) printf(" P-bit"); } else { uint8_t sar; sar = (ctrl & L2CAP_CTRL_SAR_MASK) >> L2CAP_CTRL_SAR_SHIFT; printf(" %s", sar2str(sar)); if (sar == L2CAP_SAR_START) { uint16_t len; if (!l2cap_frame_get_le16(frame, &len)) return; printf(" (len %d)", len); } printf(" TxSeq %d", (ctrl & L2CAP_CTRL_TXSEQ_MASK) >> L2CAP_CTRL_TXSEQ_SHIFT); } printf(" ReqSeq %d", (ctrl & L2CAP_CTRL_REQSEQ_MASK) >> L2CAP_CTRL_REQSEQ_SHIFT); if (ctrl & L2CAP_CTRL_FINAL) printf(" F-bit"); } #define MAX_INDEX 16 struct index_data { void *frag_buf; uint16_t frag_pos; uint16_t frag_len; uint16_t frag_cid; }; static struct index_data index_list[MAX_INDEX][2]; static void clear_fragment_buffer(uint16_t index, bool in) { free(index_list[index][in].frag_buf); index_list[index][in].frag_buf = NULL; index_list[index][in].frag_pos = 0; index_list[index][in].frag_len = 0; } static void print_psm(uint16_t psm) { print_field("PSM: %d (0x%4.4x)", le16_to_cpu(psm), le16_to_cpu(psm)); } static void print_cid(const char *type, uint16_t cid) { print_field("%s CID: %d", type, le16_to_cpu(cid)); } static void print_reject_reason(uint16_t reason) { const char *str; switch (le16_to_cpu(reason)) { case 0x0000: str = "Command not understood"; break; case 0x0001: str = "Signaling MTU exceeded"; break; case 0x0002: str = "Invalid CID in request"; break; default: str = "Reserved"; break; } print_field("Reason: %s (0x%4.4x)", str, le16_to_cpu(reason)); } static void print_conn_result(uint16_t result) { const char *str; switch (le16_to_cpu(result)) { case 0x0000: str = "Connection successful"; break; case 0x0001: str = "Connection pending"; break; case 0x0002: str = "Connection refused - PSM not supported"; break; case 0x0003: str = "Connection refused - security block"; break; case 0x0004: str = "Connection refused - no resources available"; break; case 0x0006: str = "Connection refused - Invalid Source CID"; break; case 0x0007: str = "Connection refused - Source CID already allocated"; break; default: str = "Reserved"; break; } print_field("Result: %s (0x%4.4x)", str, le16_to_cpu(result)); } static void print_le_conn_result(uint16_t result) { const char *str; switch (le16_to_cpu(result)) { case 0x0000: str = "Connection successful"; break; case 0x0002: str = "Connection refused - PSM not supported"; break; case 0x0004: str = "Connection refused - no resources available"; break; case 0x0005: str = "Connection refused - insufficient authentication"; break; case 0x0006: str = "Connection refused - insufficient authorization"; break; case 0x0007: str = "Connection refused - insufficient encryption key size"; break; case 0x0008: str = "Connection refused - insufficient encryption"; break; case 0x0009: str = "Connection refused - Invalid Source CID"; break; case 0x000a: str = "Connection refused - Source CID already allocated"; break; case 0x000b: str = "Connection refused - unacceptable parameters"; break; default: str = "Reserved"; break; } print_field("Result: %s (0x%4.4x)", str, le16_to_cpu(result)); } static void print_create_chan_result(uint16_t result) { const char *str; switch (le16_to_cpu(result)) { case 0x0000: str = "Connection successful"; break; case 0x0001: str = "Connection pending"; break; case 0x0002: str = "Connection refused - PSM not supported"; break; case 0x0003: str = "Connection refused - security block"; break; case 0x0004: str = "Connection refused - no resources available"; break; case 0x0005: str = "Connection refused - Controller ID not supported"; break; case 0x0006: str = "Connection refused - Invalid Source CID"; break; case 0x0007: str = "Connection refused - Source CID already allocated"; break; default: str = "Reserved"; break; } print_field("Result: %s (0x%4.4x)", str, le16_to_cpu(result)); } static void print_conn_status(uint16_t status) { const char *str; switch (le16_to_cpu(status)) { case 0x0000: str = "No further information available"; break; case 0x0001: str = "Authentication pending"; break; case 0x0002: str = "Authorization pending"; break; default: str = "Reserved"; break; } print_field("Status: %s (0x%4.4x)", str, le16_to_cpu(status)); } static void print_config_flags(uint16_t flags) { const char *str; if (le16_to_cpu(flags) & 0x0001) str = " (continuation)"; else str = ""; print_field("Flags: 0x%4.4x%s", le16_to_cpu(flags), str); } static void print_config_result(uint16_t result) { const char *str; switch (le16_to_cpu(result)) { case 0x0000: str = "Success"; break; case 0x0001: str = "Failure - unacceptable parameters"; break; case 0x0002: str = "Failure - rejected"; break; case 0x0003: str = "Failure - unknown options"; break; case 0x0004: str = "Pending"; break; case 0x0005: str = "Failure - flow spec rejected"; break; default: str = "Reserved"; break; } print_field("Result: %s (0x%4.4x)", str, le16_to_cpu(result)); } static const struct { uint8_t type; uint8_t len; const char *str; } options_table[] = { { 0x01, 2, "Maximum Transmission Unit" }, { 0x02, 2, "Flush Timeout" }, { 0x03, 22, "Quality of Service" }, { 0x04, 9, "Retransmission and Flow Control" }, { 0x05, 1, "Frame Check Sequence" }, { 0x06, 16, "Extended Flow Specification" }, { 0x07, 2, "Extended Window Size" }, { } }; static void print_config_options(const struct l2cap_frame *frame, uint8_t offset, uint16_t cid, bool response) { const uint8_t *data = frame->data + offset; uint16_t size = frame->size - offset; uint16_t consumed = 0; while (consumed < size - 2) { const char *str = "Unknown"; uint8_t type = data[consumed] & 0x7f; uint8_t hint = data[consumed] & 0x80; uint8_t len = data[consumed + 1]; uint8_t expect_len = 0; int i; for (i = 0; options_table[i].str; i++) { if (options_table[i].type == type) { str = options_table[i].str; expect_len = options_table[i].len; break; } } print_field("Option: %s (0x%2.2x) [%s]", str, type, hint ? "hint" : "mandatory"); if (expect_len == 0) { consumed += 2; break; } if (len != expect_len) { print_text(COLOR_ERROR, "wrong option size (%d != %d)", len, expect_len); consumed += 2; break; } switch (type) { case 0x01: print_field(" MTU: %d", get_le16(data + consumed + 2)); break; case 0x02: print_field(" Flush timeout: %d", get_le16(data + consumed + 2)); break; case 0x03: switch (data[consumed + 3]) { case 0x00: str = "No Traffic"; break; case 0x01: str = "Best Effort"; break; case 0x02: str = "Guaranteed"; break; default: str = "Reserved"; break; } print_field(" Flags: 0x%2.2x", data[consumed + 2]); print_field(" Service type: %s (0x%2.2x)", str, data[consumed + 3]); print_field(" Token rate: 0x%8.8x", get_le32(data + consumed + 4)); print_field(" Token bucket size: 0x%8.8x", get_le32(data + consumed + 8)); print_field(" Peak bandwidth: 0x%8.8x", get_le32(data + consumed + 12)); print_field(" Latency: 0x%8.8x", get_le32(data + consumed + 16)); print_field(" Delay variation: 0x%8.8x", get_le32(data + consumed + 20)); break; case 0x04: if (response) assign_mode(frame, data[consumed + 2], cid); print_field(" Mode: %s (0x%2.2x)", mode2str(data[consumed + 2]), data[consumed + 2]); print_field(" TX window size: %d", data[consumed + 3]); print_field(" Max transmit: %d", data[consumed + 4]); print_field(" Retransmission timeout: %d", get_le16(data + consumed + 5)); print_field(" Monitor timeout: %d", get_le16(data + consumed + 7)); print_field(" Maximum PDU size: %d", get_le16(data + consumed + 9)); break; case 0x05: switch (data[consumed + 2]) { case 0x00: str = "No FCS"; break; case 0x01: str = "16-bit FCS"; break; default: str = "Reserved"; break; } print_field(" FCS: %s (0x%2.2d)", str, data[consumed + 2]); break; case 0x06: switch (data[consumed + 3]) { case 0x00: str = "No traffic"; break; case 0x01: str = "Best effort"; break; case 0x02: str = "Guaranteed"; break; default: str = "Reserved"; break; } print_field(" Identifier: 0x%2.2x", data[consumed + 2]); print_field(" Service type: %s (0x%2.2x)", str, data[consumed + 3]); print_field(" Maximum SDU size: 0x%4.4x", get_le16(data + consumed + 4)); print_field(" SDU inter-arrival time: 0x%8.8x", get_le32(data + consumed + 6)); print_field(" Access latency: 0x%8.8x", get_le32(data + consumed + 10)); print_field(" Flush timeout: 0x%8.8x", get_le32(data + consumed + 14)); break; case 0x07: print_field(" Extended window size: %d", get_le16(data + consumed + 2)); assign_ext_ctrl(frame, 1, cid); break; default: packet_hexdump(data + consumed + 2, len); break; } consumed += len + 2; } if (consumed < size) packet_hexdump(data + consumed, size - consumed); } static void print_info_type(uint16_t type) { const char *str; switch (le16_to_cpu(type)) { case 0x0001: str = "Connectionless MTU"; break; case 0x0002: str = "Extended features supported"; break; case 0x0003: str = "Fixed channels supported"; break; default: str = "Reserved"; break; } print_field("Type: %s (0x%4.4x)", str, le16_to_cpu(type)); } static void print_info_result(uint16_t result) { const char *str; switch (le16_to_cpu(result)) { case 0x0000: str = "Success"; break; case 0x0001: str = "Not supported"; break; default: str = "Reserved"; break; } print_field("Result: %s (0x%4.4x)", str, le16_to_cpu(result)); } static const struct bitfield_data features_table[] = { { 0, "Flow control mode" }, { 1, "Retransmission mode" }, { 2, "Bi-directional QoS" }, { 3, "Enhanced Retransmission Mode" }, { 4, "Streaming Mode" }, { 5, "FCS Option" }, { 6, "Extended Flow Specification for BR/EDR" }, { 7, "Fixed Channels" }, { 8, "Extended Window Size" }, { 9, "Unicast Connectionless Data Reception" }, { 31, "Reserved for feature mask extension" }, { } }; static void print_features(uint32_t features) { uint32_t mask; print_field("Features: 0x%8.8x", features); mask = print_bitfield(2, features, features_table); if (mask) print_field(" Unknown features (0x%8.8x)", mask); } static const struct bitfield_data channels_table[] = { { 0x0000, "Null identifier" }, { 0x0001, "L2CAP Signaling (BR/EDR)" }, { 0x0002, "Connectionless reception" }, { 0x0003, "AMP Manager Protocol" }, { 0x0004, "Attribute Protocol" }, { 0x0005, "L2CAP Signaling (LE)" }, { 0x0006, "Security Manager (LE)" }, { 0x0007, "Security Manager (BR/EDR)" }, { 0x003f, "AMP Test Manager" }, { } }; static void print_channels(uint64_t channels) { uint64_t mask; print_field("Channels: 0x%16.16" PRIx64, channels); mask = print_bitfield(2, channels, channels_table); if (mask) print_field(" Unknown channels (0x%8.8" PRIx64 ")", mask); } static void print_move_result(uint16_t result) { const char *str; switch (le16_to_cpu(result)) { case 0x0000: str = "Move success"; break; case 0x0001: str = "Move pending"; break; case 0x0002: str = "Move refused - Controller ID not supported"; break; case 0x0003: str = "Move refused - new Controller ID is same"; break; case 0x0004: str = "Move refused - Configuration not supported"; break; case 0x0005: str = "Move refused - Move Channel collision"; break; case 0x0006: str = "Move refused - Channel not allowed to be moved"; break; default: str = "Reserved"; break; } print_field("Result: %s (0x%4.4x)", str, le16_to_cpu(result)); } static void print_move_cfm_result(uint16_t result) { const char *str; switch (le16_to_cpu(result)) { case 0x0000: str = "Move success - both sides succeed"; break; case 0x0001: str = "Move failure - one or both sides refuse"; break; default: str = "Reserved"; break; } print_field("Result: %s (0x%4.4x)", str, le16_to_cpu(result)); } static void print_conn_param_result(uint16_t result) { const char *str; switch (le16_to_cpu(result)) { case 0x0000: str = "Connection Parameters accepted"; break; case 0x0001: str = "Connection Parameters rejected"; break; default: str = "Reserved"; break; } print_field("Result: %s (0x%4.4x)", str, le16_to_cpu(result)); } static void sig_cmd_reject(const struct l2cap_frame *frame) { const struct bt_l2cap_pdu_cmd_reject *pdu = frame->data; const void *data = frame->data; uint16_t size = frame->size; uint16_t scid, dcid; print_reject_reason(pdu->reason); data += sizeof(*pdu); size -= sizeof(*pdu); switch (le16_to_cpu(pdu->reason)) { case 0x0000: if (size != 0) { print_text(COLOR_ERROR, "invalid data size"); packet_hexdump(data, size); break; } break; case 0x0001: if (size != 2) { print_text(COLOR_ERROR, "invalid data size"); packet_hexdump(data, size); break; } print_field("MTU: %d", get_le16(data)); break; case 0x0002: if (size != 4) { print_text(COLOR_ERROR, "invalid data size"); packet_hexdump(data, size); break; } dcid = get_le16(data); scid = get_le16(data + 2); print_cid("Destination", cpu_to_le16(dcid)); print_cid("Source", cpu_to_le16(scid)); break; default: packet_hexdump(data, size); break; } } static void sig_conn_req(const struct l2cap_frame *frame) { const struct bt_l2cap_pdu_conn_req *pdu = frame->data; print_psm(pdu->psm); print_cid("Source", pdu->scid); assign_scid(frame, le16_to_cpu(pdu->scid), le16_to_cpu(pdu->psm), L2CAP_MODE_BASIC, 0); } static void sig_conn_rsp(const struct l2cap_frame *frame) { const struct bt_l2cap_pdu_conn_rsp *pdu = frame->data; print_cid("Destination", pdu->dcid); print_cid("Source", pdu->scid); print_conn_result(pdu->result); print_conn_status(pdu->status); assign_dcid(frame, le16_to_cpu(pdu->dcid), le16_to_cpu(pdu->scid)); } static void sig_config_req(const struct l2cap_frame *frame) { const struct bt_l2cap_pdu_config_req *pdu = frame->data; print_cid("Destination", pdu->dcid); print_config_flags(pdu->flags); print_config_options(frame, 4, le16_to_cpu(pdu->dcid), false); } static void sig_config_rsp(const struct l2cap_frame *frame) { const struct bt_l2cap_pdu_config_rsp *pdu = frame->data; print_cid("Source", pdu->scid); print_config_flags(pdu->flags); print_config_result(pdu->result); print_config_options(frame, 6, le16_to_cpu(pdu->scid), true); } static void sig_disconn_req(const struct l2cap_frame *frame) { const struct bt_l2cap_pdu_disconn_req *pdu = frame->data; print_cid("Destination", pdu->dcid); print_cid("Source", pdu->scid); } static void sig_disconn_rsp(const struct l2cap_frame *frame) { const struct bt_l2cap_pdu_disconn_rsp *pdu = frame->data; print_cid("Destination", pdu->dcid); print_cid("Source", pdu->scid); release_scid(frame, le16_to_cpu(pdu->scid)); } static void sig_echo_req(const struct l2cap_frame *frame) { packet_hexdump(frame->data, frame->size); } static void sig_echo_rsp(const struct l2cap_frame *frame) { packet_hexdump(frame->data, frame->size); } static void sig_info_req(const struct l2cap_frame *frame) { const struct bt_l2cap_pdu_info_req *pdu = frame->data; print_info_type(pdu->type); } static void sig_info_rsp(const struct l2cap_frame *frame) { const struct bt_l2cap_pdu_info_rsp *pdu = frame->data; const void *data = frame->data; uint16_t size = frame->size; print_info_type(pdu->type); print_info_result(pdu->result); data += sizeof(*pdu); size -= sizeof(*pdu); if (le16_to_cpu(pdu->result) != 0x0000) { if (size > 0) { print_text(COLOR_ERROR, "invalid data size"); packet_hexdump(data, size); } return; } switch (le16_to_cpu(pdu->type)) { case 0x0001: if (size != 2) { print_text(COLOR_ERROR, "invalid data size"); packet_hexdump(data, size); break; } print_field("MTU: %d", get_le16(data)); break; case 0x0002: if (size != 4) { print_text(COLOR_ERROR, "invalid data size"); packet_hexdump(data, size); break; } print_features(get_le32(data)); break; case 0x0003: if (size != 8) { print_text(COLOR_ERROR, "invalid data size"); packet_hexdump(data, size); break; } print_channels(get_le64(data)); break; default: packet_hexdump(data, size); break; } } static void sig_create_chan_req(const struct l2cap_frame *frame) { const struct bt_l2cap_pdu_create_chan_req *pdu = frame->data; print_psm(pdu->psm); print_cid("Source", pdu->scid); print_field("Controller ID: %d", pdu->ctrlid); assign_scid(frame, le16_to_cpu(pdu->scid), le16_to_cpu(pdu->psm), L2CAP_MODE_BASIC, pdu->ctrlid); } static void sig_create_chan_rsp(const struct l2cap_frame *frame) { const struct bt_l2cap_pdu_create_chan_rsp *pdu = frame->data; print_cid("Destination", pdu->dcid); print_cid("Source", pdu->scid); print_create_chan_result(pdu->result); print_conn_status(pdu->status); assign_dcid(frame, le16_to_cpu(pdu->dcid), le16_to_cpu(pdu->scid)); } static void sig_move_chan_req(const struct l2cap_frame *frame) { const struct bt_l2cap_pdu_move_chan_req *pdu = frame->data; print_cid("Initiator", pdu->icid); print_field("Controller ID: %d", pdu->ctrlid); } static void sig_move_chan_rsp(const struct l2cap_frame *frame) { const struct bt_l2cap_pdu_move_chan_rsp *pdu = frame->data; print_cid("Initiator", pdu->icid); print_move_result(pdu->result); } static void sig_move_chan_cfm(const struct l2cap_frame *frame) { const struct bt_l2cap_pdu_move_chan_cfm *pdu = frame->data; print_cid("Initiator", pdu->icid); print_move_cfm_result(pdu->result); } static void sig_move_chan_cfm_rsp(const struct l2cap_frame *frame) { const struct bt_l2cap_pdu_move_chan_cfm_rsp *pdu = frame->data; print_cid("Initiator", pdu->icid); } static void sig_conn_param_req(const struct l2cap_frame *frame) { const struct bt_l2cap_pdu_conn_param_req *pdu = frame->data; print_field("Min interval: %d", le16_to_cpu(pdu->min_interval)); print_field("Max interval: %d", le16_to_cpu(pdu->max_interval)); print_field("Peripheral latency: %d", le16_to_cpu(pdu->latency)); print_field("Timeout multiplier: %d", le16_to_cpu(pdu->timeout)); } static void sig_conn_param_rsp(const struct l2cap_frame *frame) { const struct bt_l2cap_pdu_conn_param_rsp *pdu = frame->data; print_conn_param_result(pdu->result); } static void sig_le_conn_req(const struct l2cap_frame *frame) { const struct bt_l2cap_pdu_le_conn_req *pdu = frame->data; print_psm(pdu->psm); print_cid("Source", pdu->scid); print_field("MTU: %u", le16_to_cpu(pdu->mtu)); print_field("MPS: %u", le16_to_cpu(pdu->mps)); print_field("Credits: %u", le16_to_cpu(pdu->credits)); assign_scid(frame, le16_to_cpu(pdu->scid), le16_to_cpu(pdu->psm), L2CAP_MODE_LE_FLOWCTL, 0); } static void sig_le_conn_rsp(const struct l2cap_frame *frame) { const struct bt_l2cap_pdu_le_conn_rsp *pdu = frame->data; print_cid("Destination", pdu->dcid); print_field("MTU: %u", le16_to_cpu(pdu->mtu)); print_field("MPS: %u", le16_to_cpu(pdu->mps)); print_field("Credits: %u", le16_to_cpu(pdu->credits)); print_le_conn_result(pdu->result); assign_dcid(frame, le16_to_cpu(pdu->dcid), 0); } static void sig_le_flowctl_creds(const struct l2cap_frame *frame) { const struct bt_l2cap_pdu_le_flowctl_creds *pdu = frame->data; print_cid("Source", pdu->cid); print_field("Credits: %u", le16_to_cpu(pdu->credits)); } static void sig_ecred_conn_req(const struct l2cap_frame *frame) { const struct bt_l2cap_pdu_ecred_conn_req *pdu = frame->data; uint16_t scid; l2cap_frame_pull((void *)frame, frame, sizeof(*pdu)); print_psm(pdu->psm); print_field("MTU: %u", le16_to_cpu(pdu->mtu)); print_field("MPS: %u", le16_to_cpu(pdu->mps)); print_field("Credits: %u", le16_to_cpu(pdu->credits)); while (l2cap_frame_get_le16((void *)frame, &scid)) { print_cid("Source", scid); assign_scid(frame, scid, le16_to_cpu(pdu->psm), L2CAP_MODE_ECRED, 0); } } static void print_ecred_conn_result(uint16_t result) { const char *str; switch (le16_to_cpu(result)) { case 0x0000: str = "Connection successful"; break; case 0x0002: str = "Connection refused - PSM not supported"; break; case 0x0004: str = "Some connections refused – not enough resources " "available"; break; case 0x0005: str = "All Connections refused - insufficient authentication"; break; case 0x0006: str = "All Connections refused - insufficient authorization"; break; case 0x0007: str = "All Connection refused - insufficient encryption key " "size"; break; case 0x0008: str = "All Connections refused - insufficient encryption"; break; case 0x0009: str = "Some Connections refused - Invalid Source CID"; break; case 0x000a: str = "Some Connections refused - Source CID already allocated"; break; case 0x000b: str = "All Connections refused - unacceptable parameters"; break; default: str = "Reserved"; break; } print_field("Result: %s (0x%4.4x)", str, le16_to_cpu(result)); } static void sig_ecred_conn_rsp(const struct l2cap_frame *frame) { const struct bt_l2cap_pdu_ecred_conn_rsp *pdu = frame->data; uint16_t dcid; l2cap_frame_pull((void *)frame, frame, sizeof(*pdu)); print_field("MTU: %u", le16_to_cpu(pdu->mtu)); print_field("MPS: %u", le16_to_cpu(pdu->mps)); print_field("Credits: %u", le16_to_cpu(pdu->credits)); print_ecred_conn_result(pdu->result); while (l2cap_frame_get_le16((void *)frame, &dcid)) { print_cid("Destination", dcid); assign_dcid(frame, dcid, 0); } } static void sig_ecred_reconf_req(const struct l2cap_frame *frame) { const struct bt_l2cap_pdu_ecred_reconf_req *pdu = frame->data; uint16_t scid; l2cap_frame_pull((void *)frame, frame, sizeof(*pdu)); print_field("MTU: %u", le16_to_cpu(pdu->mtu)); print_field("MPS: %u", le16_to_cpu(pdu->mps)); while (l2cap_frame_get_le16((void *)frame, &scid)) print_cid("Source", scid); } static void print_ecred_reconf_result(uint16_t result) { const char *str; switch (le16_to_cpu(result)) { case 0x0000: str = "Reconfiguration successful"; break; case 0x0001: str = "Reconfiguration failed - reduction in size of MTU not " "allowed"; break; case 0x0002: str = "Reconfiguration failed - reduction in size of MPS not " "allowed for more than one channel at a time"; break; default: str = "Reserved"; } print_field("Result: %s (0x%4.4x)", str, le16_to_cpu(result)); } static void sig_ecred_reconf_rsp(const struct l2cap_frame *frame) { const struct bt_l2cap_pdu_ecred_reconf_rsp *pdu = frame->data; print_ecred_reconf_result(pdu->result); } struct sig_opcode_data { uint8_t opcode; const char *str; void (*func) (const struct l2cap_frame *frame); uint16_t size; bool fixed; }; #define SIG_ECRED \ { BT_L2CAP_PDU_ECRED_CONN_REQ, \ "Enhanced Credit Connection Request", \ sig_ecred_conn_req, sizeof(struct bt_l2cap_pdu_ecred_conn_req), \ false }, \ { BT_L2CAP_PDU_ECRED_CONN_RSP, \ "Enhanced Credit Connection Response", \ sig_ecred_conn_rsp, sizeof(struct bt_l2cap_pdu_ecred_conn_rsp), \ false }, \ { BT_L2CAP_PDU_ECRED_RECONF_REQ, \ "Enhanced Credit Reconfigure Request", \ sig_ecred_reconf_req, sizeof(struct bt_l2cap_pdu_ecred_reconf_req), \ false }, \ { BT_L2CAP_PDU_ECRED_RECONF_RSP, \ "Enhanced Credit Reconfigure Respond", \ sig_ecred_reconf_rsp, sizeof(struct bt_l2cap_pdu_ecred_reconf_rsp), \ true }, static const struct sig_opcode_data bredr_sig_opcode_table[] = { { 0x01, "Command Reject", sig_cmd_reject, 2, false }, { 0x02, "Connection Request", sig_conn_req, 4, true }, { 0x03, "Connection Response", sig_conn_rsp, 8, true }, { 0x04, "Configure Request", sig_config_req, 4, false }, { 0x05, "Configure Response", sig_config_rsp, 6, false }, { 0x06, "Disconnection Request", sig_disconn_req, 4, true }, { 0x07, "Disconnection Response", sig_disconn_rsp, 4, true }, { 0x08, "Echo Request", sig_echo_req, 0, false }, { 0x09, "Echo Response", sig_echo_rsp, 0, false }, { 0x0a, "Information Request", sig_info_req, 2, true }, { 0x0b, "Information Response", sig_info_rsp, 4, false }, { 0x0c, "Create Channel Request", sig_create_chan_req, 5, true }, { 0x0d, "Create Channel Response", sig_create_chan_rsp, 8, true }, { 0x0e, "Move Channel Request", sig_move_chan_req, 3, true }, { 0x0f, "Move Channel Response", sig_move_chan_rsp, 4, true }, { 0x10, "Move Channel Confirmation", sig_move_chan_cfm, 4, true }, { 0x11, "Move Channel Confirmation Response", sig_move_chan_cfm_rsp, 2, true }, SIG_ECRED { }, }; static const struct sig_opcode_data le_sig_opcode_table[] = { { 0x01, "Command Reject", sig_cmd_reject, 2, false }, { 0x06, "Disconnection Request", sig_disconn_req, 4, true }, { 0x07, "Disconnection Response", sig_disconn_rsp, 4, true }, { 0x12, "Connection Parameter Update Request", sig_conn_param_req, 8, true }, { 0x13, "Connection Parameter Update Response", sig_conn_param_rsp, 2, true }, { 0x14, "LE Connection Request", sig_le_conn_req, 10, true }, { 0x15, "LE Connection Response", sig_le_conn_rsp, 10, true }, { 0x16, "LE Flow Control Credit", sig_le_flowctl_creds, 4, true }, SIG_ECRED { }, }; static void l2cap_queue_frame(struct l2cap_frame *frame) { struct packet_conn_data *conn; struct l2cap_frame *tx; conn = packet_get_conn_data(frame->handle); if (!conn) return; if (!conn->chan_q) conn->chan_q = queue_new(); tx = new0(struct l2cap_frame, 1); memcpy(tx, frame, sizeof(*frame)); queue_push_tail(conn->chan_q, tx); } void l2cap_frame_init(struct l2cap_frame *frame, uint16_t index, bool in, uint16_t handle, uint8_t ident, uint16_t cid, uint16_t psm, const void *data, uint16_t size) { frame->index = index; frame->in = in; frame->handle = handle; frame->ident = ident; frame->cid = cid; frame->data = data; frame->size = size; frame->chan = get_chan_data_index(frame); frame->psm = psm ? psm : get_psm(frame); frame->mode = get_mode(frame); frame->seq_num = psm ? 1 : get_seq_num(frame); if (!in) l2cap_queue_frame(frame); } static void bredr_sig_packet(uint16_t index, bool in, uint16_t handle, uint16_t cid, const void *data, uint16_t size) { struct l2cap_frame frame; while (size > 0) { const struct bt_l2cap_hdr_sig *hdr = data; const struct sig_opcode_data *opcode_data = NULL; const char *opcode_color, *opcode_str; uint16_t len; int i; if (size < 4) { print_text(COLOR_ERROR, "malformed signal packet"); packet_hexdump(data, size); return; } len = le16_to_cpu(hdr->len); data += 4; size -= 4; if (size < len) { print_text(COLOR_ERROR, "invalid signal packet size"); packet_hexdump(data, size); return; } for (i = 0; bredr_sig_opcode_table[i].str; i++) { if (bredr_sig_opcode_table[i].opcode == hdr->code) { opcode_data = &bredr_sig_opcode_table[i]; break; } } if (opcode_data) { if (opcode_data->func) { if (in) opcode_color = COLOR_MAGENTA; else opcode_color = COLOR_BLUE; } else opcode_color = COLOR_WHITE_BG; opcode_str = opcode_data->str; } else { opcode_color = COLOR_WHITE_BG; opcode_str = "Unknown"; } print_indent(6, opcode_color, "L2CAP: ", opcode_str, COLOR_OFF, " (0x%2.2x) ident %d len %d", hdr->code, hdr->ident, len); if (!opcode_data || !opcode_data->func) { packet_hexdump(data, len); data += len; size -= len; return; } if (opcode_data->fixed) { if (len != opcode_data->size) { print_text(COLOR_ERROR, "invalid size"); packet_hexdump(data, len); data += len; size -= len; continue; } } else { if (len < opcode_data->size) { print_text(COLOR_ERROR, "too short packet"); packet_hexdump(data, size); data += len; size -= len; continue; } } l2cap_frame_init(&frame, index, in, handle, hdr->ident, cid, 0, data, len); opcode_data->func(&frame); data += len; size -= len; } packet_hexdump(data, size); } static void le_sig_packet(uint16_t index, bool in, uint16_t handle, uint16_t cid, const void *data, uint16_t size) { struct l2cap_frame frame; const struct bt_l2cap_hdr_sig *hdr = data; const struct sig_opcode_data *opcode_data = NULL; const char *opcode_color, *opcode_str; uint16_t len; int i; if (size < 4) { print_text(COLOR_ERROR, "malformed signal packet"); packet_hexdump(data, size); return; } len = le16_to_cpu(hdr->len); data += 4; size -= 4; if (size != len) { print_text(COLOR_ERROR, "invalid signal packet size"); packet_hexdump(data, size); return; } for (i = 0; le_sig_opcode_table[i].str; i++) { if (le_sig_opcode_table[i].opcode == hdr->code) { opcode_data = &le_sig_opcode_table[i]; break; } } if (opcode_data) { if (opcode_data->func) { if (in) opcode_color = COLOR_MAGENTA; else opcode_color = COLOR_BLUE; } else opcode_color = COLOR_WHITE_BG; opcode_str = opcode_data->str; } else { opcode_color = COLOR_WHITE_BG; opcode_str = "Unknown"; } print_indent(6, opcode_color, "LE L2CAP: ", opcode_str, COLOR_OFF, " (0x%2.2x) ident %d len %d", hdr->code, hdr->ident, len); if (!opcode_data || !opcode_data->func) { packet_hexdump(data, len); return; } if (opcode_data->fixed) { if (len != opcode_data->size) { print_text(COLOR_ERROR, "invalid size"); packet_hexdump(data, len); return; } } else { if (len < opcode_data->size) { print_text(COLOR_ERROR, "too short packet"); packet_hexdump(data, size); return; } } l2cap_frame_init(&frame, index, in, handle, hdr->ident, cid, 0, data, len); opcode_data->func(&frame); } static void connless_packet(uint16_t index, bool in, uint16_t handle, uint16_t cid, const void *data, uint16_t size) { struct l2cap_frame frame; const struct bt_l2cap_hdr_connless *hdr = data; uint16_t psm; if (size < 2) { print_text(COLOR_ERROR, "malformed connectionless packet"); packet_hexdump(data, size); return; } psm = le16_to_cpu(hdr->psm); data += 2; size -= 2; print_indent(6, COLOR_CYAN, "L2CAP: Connectionless", "", COLOR_OFF, " len %d [PSM %d]", size, psm); switch (psm) { default: packet_hexdump(data, size); break; } l2cap_frame_init(&frame, index, in, handle, 0, cid, 0, data, size); } static void print_controller_list(const uint8_t *data, uint16_t size) { while (size > 2) { const char *str; print_field("Controller ID: %d", data[0]); switch (data[1]) { case 0x00: str = "Primary BR/EDR Controller"; break; case 0x01: str = "802.11 AMP Controller"; break; default: str = "Reserved"; break; } print_field(" Type: %s (0x%2.2x)", str, data[1]); switch (data[2]) { case 0x00: str = "Present"; break; case 0x01: str = "Bluetooth only"; break; case 0x02: str = "No capacity"; break; case 0x03: str = "Low capacity"; break; case 0x04: str = "Medium capacity"; break; case 0x05: str = "High capacity"; break; case 0x06: str = "Full capacity"; break; default: str = "Reserved"; break; } print_field(" Status: %s (0x%2.2x)", str, data[2]); data += 3; size -= 3; } packet_hexdump(data, size); } static void amp_cmd_reject(const struct l2cap_frame *frame) { const struct bt_l2cap_amp_cmd_reject *pdu = frame->data; print_field("Reason: 0x%4.4x", le16_to_cpu(pdu->reason)); } static void amp_discover_req(const struct l2cap_frame *frame) { const struct bt_l2cap_amp_discover_req *pdu = frame->data; print_field("MTU/MPS size: %d", le16_to_cpu(pdu->size)); print_field("Extended feature mask: 0x%4.4x", le16_to_cpu(pdu->features)); } static void amp_discover_rsp(const struct l2cap_frame *frame) { const struct bt_l2cap_amp_discover_rsp *pdu = frame->data; print_field("MTU/MPS size: %d", le16_to_cpu(pdu->size)); print_field("Extended feature mask: 0x%4.4x", le16_to_cpu(pdu->features)); print_controller_list(frame->data + 4, frame->size - 4); } static void amp_change_notify(const struct l2cap_frame *frame) { print_controller_list(frame->data, frame->size); } static void amp_change_response(const struct l2cap_frame *frame) { } static void amp_get_info_req(const struct l2cap_frame *frame) { const struct bt_l2cap_amp_get_info_req *pdu = frame->data; print_field("Controller ID: %d", pdu->ctrlid); } static void amp_get_info_rsp(const struct l2cap_frame *frame) { const struct bt_l2cap_amp_get_info_rsp *pdu = frame->data; const char *str; print_field("Controller ID: %d", pdu->ctrlid); switch (pdu->status) { case 0x00: str = "Success"; break; case 0x01: str = "Invalid Controller ID"; break; default: str = "Reserved"; break; } print_field("Status: %s (0x%2.2x)", str, pdu->status); print_field("Total bandwidth: %d kbps", le32_to_cpu(pdu->total_bw)); print_field("Max guaranteed bandwidth: %d kbps", le32_to_cpu(pdu->max_bw)); print_field("Min latency: %d", le32_to_cpu(pdu->min_latency)); print_field("PAL capabilities: 0x%4.4x", le16_to_cpu(pdu->pal_cap)); print_field("Max ASSOC length: %d", le16_to_cpu(pdu->max_assoc_len)); } static void amp_get_assoc_req(const struct l2cap_frame *frame) { const struct bt_l2cap_amp_get_assoc_req *pdu = frame->data; print_field("Controller ID: %d", pdu->ctrlid); } static void amp_get_assoc_rsp(const struct l2cap_frame *frame) { const struct bt_l2cap_amp_get_assoc_rsp *pdu = frame->data; const char *str; print_field("Controller ID: %d", pdu->ctrlid); switch (pdu->status) { case 0x00: str = "Success"; break; case 0x01: str = "Invalid Controller ID"; break; default: str = "Reserved"; break; } print_field("Status: %s (0x%2.2x)", str, pdu->status); packet_hexdump(frame->data + 2, frame->size - 2); } static void amp_create_phy_link_req(const struct l2cap_frame *frame) { const struct bt_l2cap_amp_create_phy_link_req *pdu = frame->data; print_field("Local controller ID: %d", pdu->local_ctrlid); print_field("Remote controller ID: %d", pdu->remote_ctrlid); packet_hexdump(frame->data + 2, frame->size - 2); } static void amp_create_phy_link_rsp(const struct l2cap_frame *frame) { const struct bt_l2cap_amp_create_phy_link_rsp *pdu = frame->data; const char *str; print_field("Local controller ID: %d", pdu->local_ctrlid); print_field("Remote controller ID: %d", pdu->remote_ctrlid); switch (pdu->status) { case 0x00: str = "Success"; break; case 0x01: str = "Invalid Controller ID"; break; case 0x02: str = "Failed - Unable to start link creation"; break; case 0x03: str = "Failed - Collision occurred"; break; case 0x04: str = "Failed - Disconnected link packet received"; break; case 0x05: str = "Failed - Link already exists"; break; case 0x06: str = "Failed - Security violation"; break; default: str = "Reserved"; break; } print_field("Status: %s (0x%2.2x)", str, pdu->status); } static void amp_disconn_phy_link_req(const struct l2cap_frame *frame) { const struct bt_l2cap_amp_disconn_phy_link_req *pdu = frame->data; print_field("Local controller ID: %d", pdu->local_ctrlid); print_field("Remote controller ID: %d", pdu->remote_ctrlid); } static void amp_disconn_phy_link_rsp(const struct l2cap_frame *frame) { const struct bt_l2cap_amp_disconn_phy_link_rsp *pdu = frame->data; const char *str; print_field("Local controller ID: %d", pdu->local_ctrlid); print_field("Remote controller ID: %d", pdu->remote_ctrlid); switch (pdu->status) { case 0x00: str = "Success"; break; case 0x01: str = "Invalid Controller ID"; break; case 0x02: str = "Failed - No link exists"; break; default: str = "Reserved"; break; } print_field("Status: %s (0x%2.2x)", str, pdu->status); } struct amp_opcode_data { uint8_t opcode; const char *str; void (*func) (const struct l2cap_frame *frame); uint16_t size; bool fixed; }; static const struct amp_opcode_data amp_opcode_table[] = { { 0x01, "Command Reject", amp_cmd_reject, 2, false }, { 0x02, "Discover Request", amp_discover_req, 4, true }, { 0x03, "Discover Response", amp_discover_rsp, 7, false }, { 0x04, "Change Notify", amp_change_notify, 3, false }, { 0x05, "Change Response", amp_change_response, 0, true }, { 0x06, "Get Info Request", amp_get_info_req, 1, true }, { 0x07, "Get Info Response", amp_get_info_rsp, 18, true }, { 0x08, "Get Assoc Request", amp_get_assoc_req, 1, true }, { 0x09, "Get Assoc Response", amp_get_assoc_rsp, 2, false }, { 0x0a, "Create Physical Link Request", amp_create_phy_link_req, 2, false }, { 0x0b, "Create Physical Link Response", amp_create_phy_link_rsp, 3, true }, { 0x0c, "Disconnect Physical Link Request", amp_disconn_phy_link_req, 2, true }, { 0x0d, "Disconnect Physical Link Response", amp_disconn_phy_link_rsp, 3, true }, { }, }; static void amp_packet(uint16_t index, bool in, uint16_t handle, uint16_t cid, const void *data, uint16_t size) { struct l2cap_frame frame; uint16_t control, fcs, len; uint8_t opcode, ident; const struct amp_opcode_data *opcode_data = NULL; const char *opcode_color, *opcode_str; int i; if (size < 4) { print_text(COLOR_ERROR, "malformed info frame packet"); packet_hexdump(data, size); return; } control = get_le16(data); fcs = get_le16(data + size - 2); print_indent(6, COLOR_CYAN, "Channel:", "", COLOR_OFF, " %d dlen %d control 0x%4.4x fcs 0x%4.4x", 3, size, control, fcs); if (control & 0x01) return; if (size < 8) { print_text(COLOR_ERROR, "malformed manager packet"); packet_hexdump(data, size); return; } opcode = *((const uint8_t *) (data + 2)); ident = *((const uint8_t *) (data + 3)); len = get_le16(data + 4); if (len != size - 8) { print_text(COLOR_ERROR, "invalid manager packet size"); packet_hexdump(data + 2, size - 4); return; } for (i = 0; amp_opcode_table[i].str; i++) { if (amp_opcode_table[i].opcode == opcode) { opcode_data = &_opcode_table[i]; break; } } if (opcode_data) { if (opcode_data->func) { if (in) opcode_color = COLOR_MAGENTA; else opcode_color = COLOR_BLUE; } else opcode_color = COLOR_WHITE_BG; opcode_str = opcode_data->str; } else { opcode_color = COLOR_WHITE_BG; opcode_str = "Unknown"; } print_indent(6, opcode_color, "AMP: ", opcode_str, COLOR_OFF, " (0x%2.2x) ident %d len %d", opcode, ident, len); if (!opcode_data || !opcode_data->func) { packet_hexdump(data + 6, size - 8); return; } if (opcode_data->fixed) { if (len != opcode_data->size) { print_text(COLOR_ERROR, "invalid size"); packet_hexdump(data + 6, size - 8); return; } } else { if (len < opcode_data->size) { print_text(COLOR_ERROR, "too short packet"); packet_hexdump(data + 6, size - 8); return; } } l2cap_frame_init(&frame, index, in, handle, 0, cid, 0, data + 6, len); opcode_data->func(&frame); } static void print_smp_io_capa(uint8_t io_capa) { const char *str; switch (io_capa) { case 0x00: str = "DisplayOnly"; break; case 0x01: str = "DisplayYesNo"; break; case 0x02: str = "KeyboardOnly"; break; case 0x03: str = "NoInputNoOutput"; break; case 0x04: str = "KeyboardDisplay"; break; default: str = "Reserved"; break; } print_field("IO capability: %s (0x%2.2x)", str, io_capa); } static void print_smp_oob_data(uint8_t oob_data) { const char *str; switch (oob_data) { case 0x00: str = "Authentication data not present"; break; case 0x01: str = "Authentication data from remote device present"; break; default: str = "Reserved"; break; } print_field("OOB data: %s (0x%2.2x)", str, oob_data); } static void print_smp_auth_req(uint8_t auth_req) { const char *bond, *mitm, *sc, *kp, *ct2; switch (auth_req & 0x03) { case 0x00: bond = "No bonding"; break; case 0x01: bond = "Bonding"; break; default: bond = "Reserved"; break; } if (auth_req & 0x04) mitm = "MITM"; else mitm = "No MITM"; if (auth_req & 0x08) sc = "SC"; else sc = "Legacy"; if (auth_req & 0x10) kp = "Keypresses"; else kp = "No Keypresses"; if (auth_req & 0x20) ct2 = ", CT2"; else ct2 = ""; print_field("Authentication requirement: %s, %s, %s, %s%s (0x%2.2x)", bond, mitm, sc, kp, ct2, auth_req); } static void print_smp_key_dist(const char *label, uint8_t dist) { char str[27]; if (!(dist & 0x07)) { strcpy(str, " "); } else { str[0] = '\0'; if (dist & 0x01) strcat(str, "EncKey "); if (dist & 0x02) strcat(str, "IdKey "); if (dist & 0x04) strcat(str, "Sign "); if (dist & 0x08) strcat(str, "LinkKey "); } print_field("%s: %s(0x%2.2x)", label, str, dist); } static void smp_pairing_request(const struct l2cap_frame *frame) { const struct bt_l2cap_smp_pairing_request *pdu = frame->data; print_smp_io_capa(pdu->io_capa); print_smp_oob_data(pdu->oob_data); print_smp_auth_req(pdu->auth_req); print_field("Max encryption key size: %d", pdu->max_key_size); print_smp_key_dist("Initiator key distribution", pdu->init_key_dist); print_smp_key_dist("Responder key distribution", pdu->resp_key_dist); } static void smp_pairing_response(const struct l2cap_frame *frame) { const struct bt_l2cap_smp_pairing_response *pdu = frame->data; print_smp_io_capa(pdu->io_capa); print_smp_oob_data(pdu->oob_data); print_smp_auth_req(pdu->auth_req); print_field("Max encryption key size: %d", pdu->max_key_size); print_smp_key_dist("Initiator key distribution", pdu->init_key_dist); print_smp_key_dist("Responder key distribution", pdu->resp_key_dist); } static void smp_pairing_confirm(const struct l2cap_frame *frame) { const struct bt_l2cap_smp_pairing_confirm *pdu = frame->data; print_hex_field("Confim value", pdu->value, 16); } static void smp_pairing_random(const struct l2cap_frame *frame) { const struct bt_l2cap_smp_pairing_random *pdu = frame->data; print_hex_field("Random value", pdu->value, 16); } static void smp_pairing_failed(const struct l2cap_frame *frame) { const struct bt_l2cap_smp_pairing_failed *pdu = frame->data; const char *str; switch (pdu->reason) { case 0x01: str = "Passkey entry failed"; break; case 0x02: str = "OOB not available"; break; case 0x03: str = "Authentication requirements"; break; case 0x04: str = "Confirm value failed"; break; case 0x05: str = "Pairing not supported"; break; case 0x06: str = "Encryption key size"; break; case 0x07: str = "Command not supported"; break; case 0x08: str = "Unspecified reason"; break; case 0x09: str = "Repeated attempts"; break; case 0x0a: str = "Invalid parameters"; break; case 0x0b: str = "DHKey check failed"; break; case 0x0c: str = "Numeric comparison failed"; break; case 0x0d: str = "BR/EDR pairing in progress"; break; case 0x0e: str = "Cross-transport Key Derivation/Generation not allowed"; break; default: str = "Reserved"; break; } print_field("Reason: %s (0x%2.2x)", str, pdu->reason); } static void smp_encrypt_info(const struct l2cap_frame *frame) { const struct bt_l2cap_smp_encrypt_info *pdu = frame->data; print_hex_field("Long term key", pdu->ltk, 16); } static void smp_central_ident(const struct l2cap_frame *frame) { const struct bt_l2cap_smp_central_ident *pdu = frame->data; print_field("EDIV: 0x%4.4x", le16_to_cpu(pdu->ediv)); print_field("Rand: 0x%16.16" PRIx64, le64_to_cpu(pdu->rand)); } static void smp_ident_info(const struct l2cap_frame *frame) { const struct bt_l2cap_smp_ident_info *pdu = frame->data; print_hex_field("Identity resolving key", pdu->irk, 16); keys_update_identity_key(pdu->irk); } static void smp_ident_addr_info(const struct l2cap_frame *frame) { const struct bt_l2cap_smp_ident_addr_info *pdu = frame->data; packet_print_addr("Address", pdu->addr, pdu->addr_type); keys_update_identity_addr(pdu->addr, pdu->addr_type); } static void smp_signing_info(const struct l2cap_frame *frame) { const struct bt_l2cap_smp_signing_info *pdu = frame->data; print_hex_field("Signature key", pdu->csrk, 16); } static void smp_security_request(const struct l2cap_frame *frame) { const struct bt_l2cap_smp_security_request *pdu = frame->data; print_smp_auth_req(pdu->auth_req); } static void smp_pairing_public_key(const struct l2cap_frame *frame) { const struct bt_l2cap_smp_public_key *pdu = frame->data; print_hex_field("X", pdu->x, 32); print_hex_field("Y", pdu->y, 32); } static void smp_pairing_dhkey_check(const struct l2cap_frame *frame) { const struct bt_l2cap_smp_dhkey_check *pdu = frame->data; print_hex_field("E", pdu->e, 16); } static void smp_pairing_keypress_notification(const struct l2cap_frame *frame) { const struct bt_l2cap_smp_keypress_notify *pdu = frame->data; const char *str; switch (pdu->type) { case 0x00: str = "Passkey entry started"; break; case 0x01: str = "Passkey digit entered"; break; case 0x02: str = "Passkey digit erased"; break; case 0x03: str = "Passkey cleared"; break; case 0x04: str = "Passkey entry completed"; break; default: str = "Reserved"; break; } print_field("Type: %s (0x%2.2x)", str, pdu->type); } struct smp_opcode_data { uint8_t opcode; const char *str; void (*func) (const struct l2cap_frame *frame); uint8_t size; bool fixed; }; static const struct smp_opcode_data smp_opcode_table[] = { { 0x01, "Pairing Request", smp_pairing_request, 6, true }, { 0x02, "Pairing Response", smp_pairing_response, 6, true }, { 0x03, "Pairing Confirm", smp_pairing_confirm, 16, true }, { 0x04, "Pairing Random", smp_pairing_random, 16, true }, { 0x05, "Pairing Failed", smp_pairing_failed, 1, true }, { 0x06, "Encryption Information", smp_encrypt_info, 16, true }, { 0x07, "Central Identification", smp_central_ident, 10, true }, { 0x08, "Identity Information", smp_ident_info, 16, true }, { 0x09, "Identity Address Information", smp_ident_addr_info, 7, true }, { 0x0a, "Signing Information", smp_signing_info, 16, true }, { 0x0b, "Security Request", smp_security_request, 1, true }, { 0x0c, "Pairing Public Key", smp_pairing_public_key, 64, true }, { 0x0d, "Pairing DHKey Check", smp_pairing_dhkey_check, 16, true }, { 0x0e, "Pairing Keypress Notification", smp_pairing_keypress_notification, 1, true }, { } }; static void smp_packet(uint16_t index, bool in, uint16_t handle, uint16_t cid, const void *data, uint16_t size) { struct l2cap_frame frame; uint8_t opcode = *((const uint8_t *) data); const struct smp_opcode_data *opcode_data = NULL; const char *opcode_color, *opcode_str; int i; if (size < 1) { print_text(COLOR_ERROR, "malformed attribute packet"); packet_hexdump(data, size); return; } for (i = 0; smp_opcode_table[i].str; i++) { if (smp_opcode_table[i].opcode == opcode) { opcode_data = &smp_opcode_table[i]; break; } } if (opcode_data) { if (opcode_data->func) { if (in) opcode_color = COLOR_MAGENTA; else opcode_color = COLOR_BLUE; } else opcode_color = COLOR_WHITE_BG; opcode_str = opcode_data->str; } else { opcode_color = COLOR_WHITE_BG; opcode_str = "Unknown"; } print_indent(6, opcode_color, cid == 0x0006 ? "SMP: " : "BR/EDR SMP: ", opcode_str, COLOR_OFF, " (0x%2.2x) len %d", opcode, size - 1); if (!opcode_data || !opcode_data->func) { packet_hexdump(data + 1, size - 1); return; } if (opcode_data->fixed) { if (size - 1 != opcode_data->size) { print_text(COLOR_ERROR, "invalid size"); packet_hexdump(data + 1, size - 1); return; } } else { if (size - 1 < opcode_data->size) { print_text(COLOR_ERROR, "too short packet"); packet_hexdump(data + 1, size - 1); return; } } l2cap_frame_init(&frame, index, in, handle, 0, cid, 0, data + 1, size - 1); opcode_data->func(&frame); } void l2cap_frame(uint16_t index, bool in, uint16_t handle, uint16_t cid, uint16_t psm, const void *data, uint16_t size) { struct l2cap_frame frame; struct chan_data *chan; uint32_t ctrl32 = 0; uint16_t ctrl16 = 0; uint8_t ext_ctrl; switch (cid) { case 0x0001: bredr_sig_packet(index, in, handle, cid, data, size); break; case 0x0002: connless_packet(index, in, handle, cid, data, size); break; case 0x0003: amp_packet(index, in, handle, cid, data, size); break; case 0x0004: att_packet(index, in, handle, cid, data, size); break; case 0x0005: le_sig_packet(index, in, handle, cid, data, size); break; case 0x0006: case 0x0007: smp_packet(index, in, handle, cid, data, size); break; default: l2cap_frame_init(&frame, index, in, handle, 0, cid, psm, data, size); switch (frame.mode) { case L2CAP_MODE_LE_FLOWCTL: case L2CAP_MODE_ECRED: chan = get_chan(&frame); if (!chan) return; if (!chan->sdu) { if (!l2cap_frame_get_le16(&frame, &chan->sdu)) return; } print_indent(6, COLOR_CYAN, "Channel:", "", COLOR_OFF, " %d len %d sdu %d" " [PSM %d mode %s (0x%02x)] {chan %d}", cid, size, chan->sdu, frame.psm, mode2str(frame.mode), frame.mode, frame.chan); chan->sdu -= frame.size; break; case L2CAP_MODE_BASIC: print_indent(6, COLOR_CYAN, "Channel:", "", COLOR_OFF, " %d len %d [PSM %d mode %s (0x%02x)] " "{chan %d}", cid, size, frame.psm, mode2str(frame.mode), frame.mode, frame.chan); break; default: ext_ctrl = get_ext_ctrl(&frame); if (ext_ctrl) { if (!l2cap_frame_get_le32(&frame, &ctrl32)) return; print_indent(6, COLOR_CYAN, "Channel:", "", COLOR_OFF, " %d len %d" " ext_ctrl 0x%8.8x" " [PSM %d mode %s (0x%02x)] " "{chan %d}", cid, size, ctrl32, frame.psm, mode2str(frame.mode), frame.mode, frame.chan); l2cap_ctrl_ext_parse(&frame, ctrl32); } else { if (!l2cap_frame_get_le16(&frame, &ctrl16)) return; print_indent(6, COLOR_CYAN, "Channel:", "", COLOR_OFF, " %d len %d" " ctrl 0x%4.4x" " [PSM %d mode %s (0x%02x)] " "{chan %d}", cid, size, ctrl16, frame.psm, mode2str(frame.mode), frame.mode, frame.chan); l2cap_ctrl_parse(&frame, ctrl16); } printf("\n"); break; } switch (frame.psm) { case 0x0001: sdp_packet(&frame); break; case 0x0003: rfcomm_packet(&frame); break; case 0x000f: bnep_packet(&frame); break; case 0x001f: att_packet(index, in, handle, cid, data, size); break; case 0x0027: att_packet(index, in, handle, cid, data + 2, size - 2); break; case 0x0017: case 0x001B: avctp_packet(&frame); break; case 0x0019: avdtp_packet(&frame); break; default: packet_hexdump(data, size); break; } break; } } void l2cap_packet(uint16_t index, bool in, uint16_t handle, uint8_t flags, const void *data, uint16_t size) { const struct bt_l2cap_hdr *hdr = data; uint16_t len, cid; if (index > MAX_INDEX - 1) { print_text(COLOR_ERROR, "controller index too large"); packet_hexdump(data, size); return; } switch (flags) { case 0x00: /* start of a non-automatically-flushable PDU */ case 0x02: /* start of an automatically-flushable PDU */ if (index_list[index][in].frag_len) { print_text(COLOR_ERROR, "unexpected start frame"); packet_hexdump(data, size); clear_fragment_buffer(index, in); return; } if (size < sizeof(*hdr)) { print_text(COLOR_ERROR, "frame too short"); packet_hexdump(data, size); return; } len = le16_to_cpu(hdr->len); cid = le16_to_cpu(hdr->cid); data += sizeof(*hdr); size -= sizeof(*hdr); if (len == size) { /* complete frame */ l2cap_frame(index, in, handle, cid, 0, data, len); return; } if (size > len) { print_text(COLOR_ERROR, "frame too long"); packet_hexdump(data, size); return; } index_list[index][in].frag_buf = malloc(len); if (!index_list[index][in].frag_buf) { print_text(COLOR_ERROR, "failed buffer allocation"); packet_hexdump(data, size); return; } memcpy(index_list[index][in].frag_buf, data, size); index_list[index][in].frag_pos = size; index_list[index][in].frag_len = len - size; index_list[index][in].frag_cid = cid; break; case 0x01: /* continuing fragment */ if (!index_list[index][in].frag_len) { print_text(COLOR_ERROR, "unexpected continuation"); packet_hexdump(data, size); return; } if (size > index_list[index][in].frag_len) { print_text(COLOR_ERROR, "fragment too long"); packet_hexdump(data, size); clear_fragment_buffer(index, in); return; } memcpy(index_list[index][in].frag_buf + index_list[index][in].frag_pos, data, size); index_list[index][in].frag_pos += size; index_list[index][in].frag_len -= size; if (!index_list[index][in].frag_len) { /* complete frame */ l2cap_frame(index, in, handle, index_list[index][in].frag_cid, 0, index_list[index][in].frag_buf, index_list[index][in].frag_pos); clear_fragment_buffer(index, in); return; } break; case 0x03: /* complete automatically-flushable PDU */ if (index_list[index][in].frag_len) { print_text(COLOR_ERROR, "unexpected complete frame"); packet_hexdump(data, size); clear_fragment_buffer(index, in); return; } if (size < sizeof(*hdr)) { print_text(COLOR_ERROR, "frame too short"); packet_hexdump(data, size); return; } len = le16_to_cpu(hdr->len); cid = le16_to_cpu(hdr->cid); data += sizeof(*hdr); size -= sizeof(*hdr); if (len != size) { print_text(COLOR_ERROR, "wrong frame size"); packet_hexdump(data, size); return; } /* complete frame */ l2cap_frame(index, in, handle, cid, 0, data, len); break; default: print_text(COLOR_ERROR, "invalid packet flags (0x%2.2x)", flags); packet_hexdump(data, size); return; } } void l2cap_dequeue_frame(struct timeval *delta, struct packet_conn_data *conn) { struct l2cap_frame *frame; struct chan_data *chan; frame = queue_pop_head(conn->chan_q); if (!frame) return; chan = get_chan(frame); if (!chan) return; packet_latency_add(&chan->tx_l, delta); print_field("Channel: %d [PSM %d mode %s (0x%02x)] {chan %d}", frame->cid, frame->psm, mode2str(frame->mode), frame->mode, frame->chan); print_field("Channel Latency: %lld msec (%lld-%lld msec ~%lld msec)", TV_MSEC(*delta), TV_MSEC(chan->tx_l.min), TV_MSEC(chan->tx_l.max), TV_MSEC(chan->tx_l.med)); free(frame); } bluez-5.82/monitor/PaxHeaders/bnep.c0000644000000000000000000000005014015011623014416 xustar0020 atime=1743516048 20 ctime=1743591282 bluez-5.82/monitor/bnep.c0000644000000000000000000002435014015011623014103 0ustar00rootroot// SPDX-License-Identifier: LGPL-2.1-or-later /* * * BlueZ - Bluetooth protocol stack for Linux * * Copyright (C) 2011-2014 Intel Corporation * Copyright (C) 2002-2010 Marcel Holtmann * * */ #ifdef HAVE_CONFIG_H #include #endif #define _GNU_SOURCE #include #include #include #include #include #include "lib/bluetooth.h" #include "lib/uuid.h" #include "src/shared/util.h" #include "bt.h" #include "packet.h" #include "display.h" #include "l2cap.h" #include "keys.h" #include "sdp.h" #include "bnep.h" #define GET_PKT_TYPE(type) (type & 0x7f) #define GET_EXTENSION(type) (type & 0x80) /* BNEP Extension Type */ #define BNEP_EXTENSION_CONTROL 0x00 #define BNEP_CONTROL 0x01 uint16_t proto = 0x0000; struct bnep_frame { uint8_t type; int extension; struct l2cap_frame l2cap_frame; }; static bool get_macaddr(struct bnep_frame *bnep_frame, char *str) { uint8_t addr[6]; struct l2cap_frame *frame = &bnep_frame->l2cap_frame; int i; for (i = 0; i < 6; i++) if (!l2cap_frame_get_u8(frame, &addr[i])) return false; sprintf(str, "%02x:%02x:%02x:%02x:%02x:%02x", addr[0], addr[1], addr[2], addr[3], addr[4], addr[5]); return true; } static bool bnep_general(struct bnep_frame *bnep_frame, uint8_t indent, int hdr_len) { struct l2cap_frame *frame; char src_addr[20], dest_addr[20]; if (!get_macaddr(bnep_frame, dest_addr)) return false; if (!get_macaddr(bnep_frame, src_addr)) return false; frame = &bnep_frame->l2cap_frame; if (!l2cap_frame_get_be16(frame, &proto)) return false; print_field("%*cdst %s src %s [proto 0x%04x] ", indent, ' ', dest_addr, src_addr, proto); return true; } static bool cmd_nt_understood(struct bnep_frame *bnep_frame, uint8_t indent) { struct l2cap_frame *frame = &bnep_frame->l2cap_frame; uint8_t ptype; if (!l2cap_frame_get_u8(frame, &ptype)) return false; print_field("%*cType: 0x%02x ", indent, ' ', ptype); return true; } static bool setup_conn_req(struct bnep_frame *bnep_frame, uint8_t indent) { struct l2cap_frame *frame = &bnep_frame->l2cap_frame; uint8_t uuid_size; uint32_t src_uuid = 0, dst_uuid = 0; if (!l2cap_frame_get_u8(frame, &uuid_size)) return false; print_field("%*cSize: 0x%02x ", indent, ' ', uuid_size); switch (uuid_size) { case 2: if (!l2cap_frame_get_be16(frame, (uint16_t *) &dst_uuid)) return false; if (!l2cap_frame_get_be16(frame, (uint16_t *) &src_uuid)) return false; break; case 4: if (!l2cap_frame_get_be32(frame, &dst_uuid)) return false; if (!l2cap_frame_get_be32(frame, &src_uuid)) return false; break; case 16: if (!l2cap_frame_get_be32(frame, &dst_uuid)) return false; l2cap_frame_pull(frame, frame, 12); if (!l2cap_frame_get_be32(frame, &src_uuid)) return false; l2cap_frame_pull(frame, frame, 12); break; default: l2cap_frame_pull(frame, frame, (uuid_size * 2)); return true; } print_field("%*cDst: 0x%x(%s)", indent, ' ', dst_uuid, bt_uuid32_to_str(dst_uuid)); print_field("%*cSrc: 0x%x(%s)", indent, ' ', src_uuid, bt_uuid32_to_str(src_uuid)); return true; } static const char *value2str(uint16_t value) { switch (value) { case 0x00: return "Operation Successful"; case 0x01: return "Operation Failed - Invalid Dst Srv UUID"; case 0x02: return "Operation Failed - Invalid Src Srv UUID"; case 0x03: return "Operation Failed - Invalid Srv UUID size"; case 0x04: return "Operation Failed - Conn not allowed"; default: return "Unknown"; } } static bool print_rsp_msg(struct bnep_frame *bnep_frame, uint8_t indent) { struct l2cap_frame *frame = &bnep_frame->l2cap_frame; uint16_t rsp_msg; if (!l2cap_frame_get_be16(frame, &rsp_msg)) return false; print_field("%*cRsp msg: %s(0x%04x) ", indent, ' ', value2str(rsp_msg), rsp_msg); return true; } static bool filter_nettype_req(struct bnep_frame *bnep_frame, uint8_t indent) { struct l2cap_frame *frame = &bnep_frame->l2cap_frame; uint16_t length, start_range, end_range; int i; if (!l2cap_frame_get_be16(frame, &length)) return false; print_field("%*cLength: 0x%04x", indent, ' ', length); for (i = 0; i < length / 4; i++) { if (!l2cap_frame_get_be16(frame, &start_range)) return false; if (!l2cap_frame_get_be16(frame, &end_range)) return false; print_field("%*c0x%04x - 0x%04x", indent, ' ', start_range, end_range); } return true; } static bool filter_multaddr_req(struct bnep_frame *bnep_frame, uint8_t indent) { struct l2cap_frame *frame = &bnep_frame->l2cap_frame; uint16_t length; char start_addr[20], end_addr[20]; int i; if (!l2cap_frame_get_be16(frame, &length)) return false; print_field("%*cLength: 0x%04x", indent, ' ', length); for (i = 0; i < length / 12; i++) { if (!get_macaddr(bnep_frame, start_addr)) return false; if (!get_macaddr(bnep_frame, end_addr)) return false; print_field("%*c%s - %s", indent, ' ', start_addr, end_addr); } return true; } struct bnep_control_data { uint8_t type; const char *str; bool (*func) (struct bnep_frame *frame, uint8_t indent); }; static const struct bnep_control_data bnep_control_table[] = { { 0x00, "Command Not Understood", cmd_nt_understood }, { 0x01, "Setup Conn Req", setup_conn_req }, { 0x02, "Setup Conn Rsp", print_rsp_msg }, { 0x03, "Filter NetType Set", filter_nettype_req }, { 0x04, "Filter NetType Rsp", print_rsp_msg }, { 0x05, "Filter MultAddr Set", filter_multaddr_req }, { 0x06, "Filter MultAddr Rsp", print_rsp_msg }, { } }; static bool bnep_control(struct bnep_frame *bnep_frame, uint8_t indent, int hdr_len) { uint8_t ctype; struct l2cap_frame *frame = &bnep_frame->l2cap_frame; const struct bnep_control_data *bnep_control_data = NULL; const char *type_str; int i; if (!l2cap_frame_get_u8(frame, &ctype)) return false; for (i = 0; bnep_control_table[i].str; i++) { if (bnep_control_table[i].type == ctype) { bnep_control_data = &bnep_control_table[i]; break; } } if (bnep_control_data) type_str = bnep_control_data->str; else type_str = "Unknown control type"; print_field("%*c%s (0x%02x) ", indent, ' ', type_str, ctype); if (!bnep_control_data || !bnep_control_data->func) { packet_hexdump(frame->data, hdr_len - 1); l2cap_frame_pull(frame, frame, hdr_len - 1); goto done; } if (!bnep_control_data->func(bnep_frame, indent+2)) return false; done: return true; } static bool bnep_compressed(struct bnep_frame *bnep_frame, uint8_t indent, int hdr_len) { struct l2cap_frame *frame = &bnep_frame->l2cap_frame; if (!l2cap_frame_get_be16(frame, &proto)) return false; print_field("%*c[proto 0x%04x] ", indent, ' ', proto); return true; } static bool bnep_src_only(struct bnep_frame *bnep_frame, uint8_t indent, int hdr_len) { struct l2cap_frame *frame; char src_addr[20]; if (!get_macaddr(bnep_frame, src_addr)) return false; frame = &bnep_frame->l2cap_frame; if (!l2cap_frame_get_be16(frame, &proto)) return false; print_field("%*csrc %s [proto 0x%04x] ", indent, ' ', src_addr, proto); return true; } static bool bnep_dst_only(struct bnep_frame *bnep_frame, uint8_t indent, int hdr_len) { struct l2cap_frame *frame; char dest_addr[20]; if (!get_macaddr(bnep_frame, dest_addr)) return false; frame = &bnep_frame->l2cap_frame; if (!l2cap_frame_get_be16(frame, &proto)) return false; print_field("%*cdst %s [proto 0x%04x] ", indent, ' ', dest_addr, proto); return true; } static bool bnep_eval_extension(struct bnep_frame *bnep_frame, uint8_t indent) { struct l2cap_frame *frame = &bnep_frame->l2cap_frame; uint8_t type, length; int extension; if (!l2cap_frame_get_u8(frame, &type)) return false; if (!l2cap_frame_get_u8(frame, &length)) return false; extension = GET_EXTENSION(type); type = GET_PKT_TYPE(type); switch (type) { case BNEP_EXTENSION_CONTROL: print_field("%*cExt Control(0x%02x|%s) len 0x%02x", indent, ' ', type, extension ? "1" : "0", length); if (!bnep_control(bnep_frame, indent+2, length)) return false; break; default: print_field("%*cExt Unknown(0x%02x|%s) len 0x%02x", indent, ' ', type, extension ? "1" : "0", length); packet_hexdump(frame->data, length); l2cap_frame_pull(frame, frame, length); } if (extension) if (!bnep_eval_extension(bnep_frame, indent)) return false; return true; } struct bnep_data { uint8_t type; const char *str; bool (*func) (struct bnep_frame *frame, uint8_t indent, int hdr_len); }; static const struct bnep_data bnep_table[] = { { 0x00, "General Ethernet", bnep_general }, { 0x01, "Control", bnep_control }, { 0x02, "Compressed Ethernet", bnep_compressed }, { 0x03, "Compressed Ethernet SrcOnly", bnep_src_only }, { 0x04, "Compressed Ethernet DestOnly", bnep_dst_only }, { } }; void bnep_packet(const struct l2cap_frame *frame) { uint8_t type, indent = 1; struct bnep_frame bnep_frame; struct l2cap_frame *l2cap_frame; const struct bnep_data *bnep_data = NULL; const char *pdu_color, *pdu_str; int i; l2cap_frame_pull(&bnep_frame.l2cap_frame, frame, 0); l2cap_frame = &bnep_frame.l2cap_frame; if (!l2cap_frame_get_u8(l2cap_frame, &type)) goto fail; bnep_frame.extension = GET_EXTENSION(type); bnep_frame.type = GET_PKT_TYPE(type); for (i = 0; bnep_table[i].str; i++) { if (bnep_table[i].type == bnep_frame.type) { bnep_data = &bnep_table[i]; break; } } if (bnep_data) { if (bnep_data->func) { if (frame->in) pdu_color = COLOR_MAGENTA; else pdu_color = COLOR_BLUE; } else pdu_color = COLOR_WHITE_BG; pdu_str = bnep_data->str; } else { pdu_color = COLOR_WHITE_BG; pdu_str = "Unknown packet type"; } print_indent(6, pdu_color, "BNEP: ", pdu_str, COLOR_OFF, " (0x%02x|%s)", bnep_frame.type, bnep_frame.extension ? "1" : "0"); if (!bnep_data || !bnep_data->func) { packet_hexdump(l2cap_frame->data, l2cap_frame->size); return; } if (!bnep_data->func(&bnep_frame, indent, -1)) goto fail; /* Extension info */ if (bnep_frame.extension) if (!bnep_eval_extension(&bnep_frame, indent+2)) goto fail; /* Control packet => No payload info */ if (bnep_frame.type == BNEP_CONTROL) return; /* TODO: Handle BNEP IP packet */ packet_hexdump(l2cap_frame->data, l2cap_frame->size); return; fail: print_text(COLOR_ERROR, "frame too short"); packet_hexdump(frame->data, frame->size); } bluez-5.82/monitor/PaxHeaders/avdtp.h0000644000000000000000000000005014015011623014615 xustar0020 atime=1743516019 20 ctime=1743591282 bluez-5.82/monitor/avdtp.h0000644000000000000000000000036214015011623014277 0ustar00rootroot/* SPDX-License-Identifier: LGPL-2.1-or-later */ /* * * BlueZ - Bluetooth protocol stack for Linux * * Copyright (C) 2015 Andrzej Kaczmarek * * */ void avdtp_packet(const struct l2cap_frame *frame); bluez-5.82/monitor/PaxHeaders/att.c0000644000000000000000000000005014766002272014277 xustar0020 atime=1743515578 20 ctime=1743591283 bluez-5.82/monitor/att.c0000644000000000000000000033615214766002272013772 0ustar00rootroot// SPDX-License-Identifier: LGPL-2.1-or-later /* * * BlueZ - Bluetooth protocol stack for Linux * * Copyright (C) 2011-2014 Intel Corporation * Copyright (C) 2002-2010 Marcel Holtmann * Copyright 2023 NXP * * */ #ifdef HAVE_CONFIG_H #include #endif #define _GNU_SOURCE #include #include #include #include #include #include #include #include #include #include #include "lib/bluetooth.h" #include "lib/uuid.h" #include "lib/hci.h" #include "lib/hci_lib.h" #include "src/shared/util.h" #include "src/shared/queue.h" #include "src/shared/att.h" #include "src/shared/gatt-db.h" #include "src/textfile.h" #include "src/settings.h" #include "bt.h" #include "packet.h" #include "display.h" #include "l2cap.h" #include "att.h" #include "keys.h" struct att_read { struct att_conn_data *conn; struct gatt_db_attribute *attr; bool in; uint16_t chan; void (*func)(const struct l2cap_frame *frame); struct iovec *iov; }; struct att_conn_data { struct gatt_db *ldb; struct timespec ldb_mtim; struct gatt_db *rdb; struct timespec rdb_mtim; struct queue *reads; uint16_t mtu; }; struct gatt_cache { bdaddr_t id; struct gatt_db *db; }; static struct queue *cache_list; static void print_uuid(const char *label, const void *data, uint16_t size) { const char *str; char uuidstr[MAX_LEN_UUID_STR]; switch (size) { case 2: str = bt_uuid16_to_str(get_le16(data)); print_field("%s: %s (0x%4.4x)", label, str, get_le16(data)); break; case 4: str = bt_uuid32_to_str(get_le32(data)); print_field("%s: %s (0x%8.8x)", label, str, get_le32(data)); break; case 16: sprintf(uuidstr, "%8.8x-%4.4x-%4.4x-%4.4x-%8.8x%4.4x", get_le32(data + 12), get_le16(data + 10), get_le16(data + 8), get_le16(data + 6), get_le32(data + 2), get_le16(data + 0)); str = bt_uuidstr_to_str(uuidstr); print_field("%s: %s (%s)", label, str, uuidstr); break; default: packet_hexdump(data, size); break; } } static void print_handle_range(const char *label, const void *data) { print_field("%s: 0x%4.4x-0x%4.4x", label, get_le16(data), get_le16(data + 2)); } static bool match_read_frame(const void *data, const void *match_data) { const struct att_read *read = data; const struct l2cap_frame *frame = match_data; /* Read frame and response frame shall be in the opposite direction to * match. */ if (read->in == frame->in) return false; return read->chan == frame->chan; } static struct att_read *att_get_read(const struct l2cap_frame *frame) { struct packet_conn_data *conn; struct att_conn_data *data; conn = packet_get_conn_data(frame->handle); if (!conn) return NULL; data = conn->data; if (!data) return NULL; return queue_remove_if(data->reads, match_read_frame, (void *)frame); } static void print_value(struct gatt_db_attribute *attr) { uint16_t handle; struct gatt_db_attribute *val; const bt_uuid_t *uuid; bt_uuid_t chrc = { .type = BT_UUID16, .value.u16 = 0x2803, }; char label[27]; uuid = gatt_db_attribute_get_type(attr); if (!uuid) return; /* Skip in case of characteristic declaration since it already prints * the value handle and properties. */ if (!bt_uuid_cmp(uuid, &chrc)) return; val = gatt_db_attribute_get_value(attr); if (!val || val == attr) return; uuid = gatt_db_attribute_get_type(val); if (!uuid) return; handle = gatt_db_attribute_get_handle(val); if (!handle) return; switch (uuid->type) { case BT_UUID16: sprintf(label, "Value Handle: 0x%4.4x Type", handle); print_field("%s: %s (0x%4.4x)", label, bt_uuid16_to_str(uuid->value.u16), uuid->value.u16); return; case BT_UUID128: sprintf(label, "Value Handle: 0x%4.4x Type", handle); print_uuid(label, &uuid->value.u128, 16); return; case BT_UUID_UNSPEC: case BT_UUID32: break; } } static void print_attribute(struct gatt_db_attribute *attr) { uint16_t handle; const bt_uuid_t *uuid; char label[21]; handle = gatt_db_attribute_get_handle(attr); if (!handle) goto done; uuid = gatt_db_attribute_get_type(attr); if (!uuid) goto done; switch (uuid->type) { case BT_UUID16: sprintf(label, "Handle: 0x%4.4x Type", handle); print_field("%s: %s (0x%4.4x)", label, bt_uuid16_to_str(uuid->value.u16), uuid->value.u16); print_value(attr); return; case BT_UUID128: sprintf(label, "Handle: 0x%4.4x Type", handle); print_uuid(label, &uuid->value.u128, 16); print_value(attr); return; case BT_UUID_UNSPEC: case BT_UUID32: break; } done: print_field("Handle: 0x%4.4x", handle); } static void att_read_free(struct att_read *read) { if (!read) return; util_iov_free(read->iov, 1); free(read); } static void print_data_list(const char *label, uint8_t length, const struct l2cap_frame *frame) { struct att_read *read; uint8_t count; if (length == 0) return; read = att_get_read(frame); count = frame->size / length; print_field("%s: %u entr%s", label, count, count == 1 ? "y" : "ies"); while (frame->size >= length) { if (!l2cap_frame_print_le16((void *)frame, "Handle")) break; print_hex_field("Value", frame->data, length - 2); if (read && read->func) { struct l2cap_frame f; l2cap_frame_clone_size(&f, frame, length - 2); read->func(&f); } if (!l2cap_frame_pull((void *)frame, frame, length - 2)) break; } packet_hexdump(frame->data, frame->size); att_read_free(read); } static void print_attribute_info(uint16_t type, const void *data, uint16_t len) { const char *str = bt_uuid16_to_str(type); print_field("%s: %s (0x%4.4x)", "Attribute type", str, type); switch (type) { case 0x2800: /* Primary Service */ case 0x2801: /* Secondary Service */ print_uuid(" UUID", data, len); break; case 0x2802: /* Include */ if (len < 4) { print_hex_field(" Value", data, len); break; } print_handle_range(" Handle range", data); print_uuid(" UUID", data + 4, len - 4); break; case 0x2803: /* Characteristic */ if (len < 3) { print_hex_field(" Value", data, len); break; } print_field(" Properties: 0x%2.2x", *((uint8_t *) data)); print_field(" Handle: 0x%2.2x", get_le16(data + 1)); print_uuid(" UUID", data + 3, len - 3); break; default: print_hex_field("Value", data, len); break; } } static const char *att_opcode_to_str(uint8_t opcode); static void att_error_response(const struct l2cap_frame *frame) { const struct bt_l2cap_att_error_response *pdu = frame->data; const char *str; switch (pdu->error) { case 0x01: str = "Invalid Handle"; break; case 0x02: str = "Read Not Permitted"; break; case 0x03: str = "Write Not Permitted"; break; case 0x04: str = "Invalid PDU"; break; case 0x05: str = "Insufficient Authentication"; break; case 0x06: str = "Request Not Supported"; break; case 0x07: str = "Invalid Offset"; break; case 0x08: str = "Insufficient Authorization"; break; case 0x09: str = "Prepare Queue Full"; break; case 0x0a: str = "Attribute Not Found"; break; case 0x0b: str = "Attribute Not Long"; break; case 0x0c: str = "Insufficient Encryption Key Size"; break; case 0x0d: str = "Invalid Attribute Value Length"; break; case 0x0e: str = "Unlikely Error"; break; case 0x0f: str = "Insufficient Encryption"; break; case 0x10: str = "Unsupported Group Type"; break; case 0x11: str = "Insufficient Resources"; break; case 0x12: str = "Database Out of Sync"; break; case 0x13: str = "Value Not Allowed"; break; case 0xfd: str = "CCC Improperly Configured"; break; case 0xfe: str = "Procedure Already in Progress"; break; case 0xff: str = "Out of Range"; break; default: str = "Reserved"; break; } print_field("%s (0x%2.2x)", att_opcode_to_str(pdu->request), pdu->request); print_field("Handle: 0x%4.4x", le16_to_cpu(pdu->handle)); print_field("Error: %s (0x%2.2x)", str, pdu->error); /* Read/Read By Type/Read By Group Type may create a read object which * needs to be dequeued and freed in case the operation fails. */ if (pdu->request == 0x08 || pdu->request == 0x0a || pdu->request == 0x10) att_read_free(att_get_read(frame)); } static const struct bitfield_data chrc_prop_table[] = { { 0, "Broadcast (0x01)" }, { 1, "Read (0x02)" }, { 2, "Write Without Response (0x04)" }, { 3, "Write (0x08)" }, { 4, "Notify (0x10)" }, { 5, "Indicate (0x20)" }, { 6, "Authorize (0x40)" }, { 6, "Extended Properties (0x80)" }, { } }; static bool match_cache_id(const void *data, const void *match_data) { const struct gatt_cache *cache = data; const bdaddr_t *id = match_data; return !bacmp(&cache->id, id); } static void gatt_cache_add(struct packet_conn_data *conn, struct gatt_db *db) { struct gatt_cache *cache; bdaddr_t id; uint8_t id_type; if (!keys_resolve_identity(conn->dst, id.b, &id_type)) bacpy(&id, (bdaddr_t *)conn->dst); if (queue_find(cache_list, match_cache_id, &id)) return; if (!cache_list) cache_list = queue_new(); cache = new0(struct gatt_cache, 1); bacpy(&cache->id, &id); cache->db = gatt_db_ref(db); queue_push_tail(cache_list, cache); } static void att_conn_data_free(struct packet_conn_data *conn, void *data) { struct att_conn_data *att_data = data; if (!gatt_db_isempty(att_data->rdb)) gatt_cache_add(conn, att_data->rdb); gatt_db_unref(att_data->rdb); gatt_db_unref(att_data->ldb); queue_destroy(att_data->reads, free); free(att_data); } static struct att_conn_data *att_get_conn_data(struct packet_conn_data *conn) { struct att_conn_data *data; if (!conn) return NULL; data = conn->data; if (data) return data; data = new0(struct att_conn_data, 1); data->rdb = gatt_db_new(); data->ldb = gatt_db_new(); conn->data = data; conn->destroy = att_conn_data_free; return data; } static void gatt_load_db(struct gatt_db *db, const char *filename, struct timespec *mtim) { struct stat st; if (lstat(filename, &st)) return; if (!gatt_db_isempty(db)) { /* Check if file has been modified since last time */ if (st.st_mtim.tv_sec == mtim->tv_sec && st.st_mtim.tv_nsec == mtim->tv_nsec) return; /* Clear db before reloading */ gatt_db_clear(db); } *mtim = st.st_mtim; btd_settings_gatt_db_load(db, filename); } static void load_gatt_db(struct packet_conn_data *conn) { struct att_conn_data *data = att_get_conn_data(conn); char filename[PATH_MAX]; char local[18]; char peer[18]; bdaddr_t id; uint8_t id_type; ba2str((bdaddr_t *)conn->src, local); if (keys_resolve_identity(conn->dst, id.b, &id_type)) { ba2str(&id, peer); } else { bacpy(&id, (bdaddr_t *)conn->dst); ba2str((bdaddr_t *)conn->dst, peer); } create_filename(filename, PATH_MAX, "/%s/attributes", local); gatt_load_db(data->ldb, filename, &data->ldb_mtim); create_filename(filename, PATH_MAX, "/%s/cache/%s", local, peer); gatt_load_db(data->rdb, filename, &data->rdb_mtim); /* If rdb cannot be loaded from file try local cache */ if (gatt_db_isempty(data->rdb)) { struct gatt_cache *cache; cache = queue_find(cache_list, match_cache_id, &id); if (cache) data->rdb = gatt_db_ref(cache->db); } } static struct gatt_db *get_db(const struct l2cap_frame *frame, bool rsp) { struct packet_conn_data *conn; struct att_conn_data *data; struct gatt_db *db; conn = packet_get_conn_data(frame->handle); if (!conn) return NULL; /* Try loading local and remote gatt_db if not loaded yet */ load_gatt_db(conn); data = conn->data; if (!data) return NULL; if (frame->in) { if (rsp) db = data->rdb; else db = data->ldb; } else { if (rsp) db = data->ldb; else db = data->rdb; } return db; } static struct gatt_db_attribute *insert_chrc(const struct l2cap_frame *frame, uint16_t handle, uint16_t value_handle, bt_uuid_t *uuid, uint8_t prop, bool rsp) { struct gatt_db *db; db = get_db(frame, rsp); if (!db) return NULL; return gatt_db_insert_characteristic(db, handle, value_handle, uuid, 0, prop, NULL, NULL, NULL); } static int bt_uuid_from_data(bt_uuid_t *uuid, const void *data, uint16_t size) { uint128_t u128; if (!uuid) return -EINVAL; switch (size) { case 2: return bt_uuid16_create(uuid, get_le16(data)); case 4: return bt_uuid32_create(uuid, get_le32(data)); case 16: memcpy(u128.data, data, sizeof(u128.data)); return bt_uuid128_create(uuid, u128); } return -EINVAL; } static bool svc_read(const struct l2cap_frame *frame, uint16_t *start, uint16_t *end, bt_uuid_t *uuid) { if (!l2cap_frame_get_le16((void *)frame, start)) return false; if (!l2cap_frame_get_le16((void *)frame, end)) return false; return !bt_uuid_from_data(uuid, frame->data, frame->size); } static struct gatt_db_attribute *insert_svc(const struct l2cap_frame *frame, uint16_t handle, bt_uuid_t *uuid, bool primary, bool rsp, uint16_t num_handles) { struct gatt_db *db; db = get_db(frame, rsp); if (!db) return NULL; return gatt_db_insert_service(db, handle, uuid, primary, num_handles); } static void pri_svc_read(const struct l2cap_frame *frame) { uint16_t start, end; bt_uuid_t uuid; if (!svc_read(frame, &start, &end, &uuid)) return; insert_svc(frame, start, &uuid, true, true, end - start + 1); } static void sec_svc_read(const struct l2cap_frame *frame) { uint16_t start, end; bt_uuid_t uuid; if (!svc_read(frame, &start, &end, &uuid)) return; insert_svc(frame, start, &uuid, true, false, end - start + 1); } static void print_chrc(const struct l2cap_frame *frame) { uint8_t prop; uint8_t mask; uint16_t handle; bt_uuid_t uuid; if (!l2cap_frame_get_u8((void *)frame, &prop)) { print_text(COLOR_ERROR, "Property: invalid size"); return; } print_field(" Properties: 0x%2.2x", prop); mask = print_bitfield(6, prop, chrc_prop_table); if (mask) print_text(COLOR_WHITE_BG, " Unknown fields (0x%2.2x)", mask); if (!l2cap_frame_get_le16((void *)frame, &handle)) { print_text(COLOR_ERROR, " Value Handle: invalid size"); return; } print_field(" Value Handle: 0x%4.4x", handle); print_uuid(" Value UUID", frame->data, frame->size); bt_uuid_from_data(&uuid, frame->data, frame->size); insert_chrc(frame, handle - 1, handle, &uuid, prop, true); } static void chrc_read(const struct l2cap_frame *frame) { print_chrc(frame); } static const struct bitfield_data ccc_value_table[] = { { 0, "Notification (0x01)" }, { 1, "Indication (0x02)" }, { } }; static void print_ccc_value(const struct l2cap_frame *frame) { uint8_t value; uint8_t mask; if (!l2cap_frame_get_u8((void *)frame, &value)) { print_text(COLOR_ERROR, "invalid size"); return; } mask = print_bitfield(4, value, ccc_value_table); if (mask) print_text(COLOR_WHITE_BG, " Unknown fields (0x%2.2x)", mask); } static void ccc_read(const struct l2cap_frame *frame) { print_ccc_value(frame); } static void ccc_write(const struct l2cap_frame *frame) { print_ccc_value(frame); } static bool print_ase_codec(const struct l2cap_frame *frame) { uint8_t codec_id; uint16_t codec_cid, codec_vid; if (!l2cap_frame_get_u8((void *)frame, &codec_id)) { print_text(COLOR_ERROR, "Codec: invalid size"); return false; } packet_print_codec_id(" Codec", codec_id); if (!l2cap_frame_get_le16((void *)frame, &codec_cid)) { print_text(COLOR_ERROR, "Codec Company ID: invalid size"); return false; } if (!l2cap_frame_get_le16((void *)frame, &codec_vid)) { print_text(COLOR_ERROR, "Codec Vendor ID: invalid size"); return false; } if (codec_id == 0xff) { print_field(" Codec Company ID: %s (0x%04x)", bt_compidtostr(codec_cid), codec_cid); print_field(" Codec Vendor ID: 0x%04x", codec_vid); } return true; } static void print_ltv(const char *str, void *user_data) { const char *label = user_data; print_field("%s: %s", label, str); } static bool print_ase_lv(const struct l2cap_frame *frame, const char *label, const struct util_ltv_debugger *decoder, size_t decoder_len) { struct bt_hci_lv_data *lv; lv = l2cap_frame_pull((void *)frame, frame, sizeof(*lv)); if (!lv) { print_text(COLOR_ERROR, "%s: invalid size", label); return false; } if (!l2cap_frame_pull((void *)frame, frame, lv->len)) { print_text(COLOR_ERROR, "%s: invalid size", label); return false; } util_debug_ltv(lv->data, lv->len, decoder, decoder_len, print_ltv, (void *) label); return true; } static bool print_ase_cc(const struct l2cap_frame *frame, const char *label, const struct util_ltv_debugger *decoder, size_t decoder_len) { return print_ase_lv(frame, label, decoder, decoder_len); } static const struct bitfield_data pac_context_table[] = { { 0, "Unspecified (0x0001)" }, { 1, "Conversational (0x0002)" }, { 2, "Media (0x0004)" }, { 3, "Game (0x0008)" }, { 4, "Instructional (0x0010)" }, { 5, "Voice Assistants (0x0020)" }, { 6, "Live (0x0040)" }, { 7, "Sound Effects (0x0080)" }, { 8, "Notifications (0x0100)" }, { 9, "Ringtone (0x0200)" }, { 10, "Alerts (0x0400)" }, { 11, "Emergency alarm (0x0800)" }, { 12, "RFU (0x1000)" }, { 13, "RFU (0x2000)" }, { 14, "RFU (0x4000)" }, { 15, "RFU (0x8000)" }, { } }; static void print_context(const struct l2cap_frame *frame, const char *label) { uint16_t value; uint16_t mask; if (!l2cap_frame_get_le16((void *)frame, &value)) { print_text(COLOR_ERROR, " value: invalid size"); goto done; } print_field("%s: 0x%4.4x", label, value); mask = print_bitfield(8, value, pac_context_table); if (mask) print_text(COLOR_WHITE_BG, " Unknown fields (0x%4.4x)", mask); done: if (frame->size) print_hex_field(" Data", frame->data, frame->size); } static void ase_debug_preferred_context(const uint8_t *data, uint8_t len, util_debug_func_t func, void *user_data) { struct l2cap_frame frame; l2cap_frame_init(&frame, 0, 0, 0, 0, 0, 0, data, len); print_context(&frame, " Preferred Context"); } static void ase_debug_context(const uint8_t *data, uint8_t len, util_debug_func_t func, void *user_data) { struct l2cap_frame frame; l2cap_frame_init(&frame, 0, 0, 0, 0, 0, 0, data, len); print_context(&frame, " Context"); } static void ase_debug_program_info(const uint8_t *data, uint8_t len, util_debug_func_t func, void *user_data) { struct l2cap_frame frame; const char *str; l2cap_frame_init(&frame, 0, 0, 0, 0, 0, 0, data, len); str = l2cap_frame_pull(&frame, &frame, len); if (!str) { print_text(COLOR_ERROR, " value: invalid size"); goto done; } print_field(" Program Info: %s", str); done: if (frame.size) print_hex_field(" Data", frame.data, frame.size); } static void ase_debug_language(const uint8_t *data, uint8_t len, util_debug_func_t func, void *user_data) { struct l2cap_frame frame; uint32_t value; l2cap_frame_init(&frame, 0, 0, 0, 0, 0, 0, data, len); if (!l2cap_frame_get_le24(&frame, &value)) { print_text(COLOR_ERROR, " value: invalid size"); goto done; } print_field(" Language: 0x%6.6x", value); done: if (frame.size) print_hex_field(" Data", frame.data, frame.size); } static const struct util_ltv_debugger ase_metadata_table[] = { UTIL_LTV_DEBUG(0x01, ase_debug_preferred_context), UTIL_LTV_DEBUG(0x02, ase_debug_context), UTIL_LTV_DEBUG(0x03, ase_debug_program_info), UTIL_LTV_DEBUG(0x04, ase_debug_language) }; static bool print_ase_metadata(const struct l2cap_frame *frame) { return print_ase_lv(frame, " Metadata", ase_metadata_table, ARRAY_SIZE(ase_metadata_table)); } static const struct bitfield_data pac_freq_table[] = { { 0, "8 Khz (0x0001)" }, { 1, "11.25 Khz (0x0002)" }, { 2, "16 Khz (0x0004)" }, { 3, "22.05 Khz (0x0008)" }, { 4, "24 Khz (0x0010)" }, { 5, "32 Khz (0x0020)" }, { 6, "44.1 Khz (0x0040)" }, { 7, "48 Khz (0x0080)" }, { 8, "88.2 Khz (0x0100)" }, { 9, "96 Khz (0x0200)" }, { 10, "176.4 Khz (0x0400)" }, { 11, "192 Khz (0x0800)" }, { 12, "384 Khz (0x1000)" }, { 13, "RFU (0x2000)" }, { 14, "RFU (0x4000)" }, { 15, "RFU (0x8000)" }, { } }; static void pac_decode_freq(const uint8_t *data, uint8_t len, util_debug_func_t func, void *user_data) { struct l2cap_frame frame; uint16_t value; uint16_t mask; l2cap_frame_init(&frame, 0, 0, 0, 0, 0, 0, data, len); if (!l2cap_frame_get_le16(&frame, &value)) { print_text(COLOR_ERROR, " value: invalid size"); goto done; } print_field(" Sampling Frequencies: 0x%4.4x", value); mask = print_bitfield(8, value, pac_freq_table); if (mask) print_text(COLOR_WHITE_BG, " Unknown fields (0x%4.4x)", mask); done: if (frame.size) print_hex_field(" Data", frame.data, frame.size); } static const struct bitfield_data pac_duration_table[] = { { 0, "7.5 ms (0x01)" }, { 1, "10 ms (0x02)" }, { 2, "RFU (0x04)" }, { 3, "RFU (0x08)" }, { 4, "7.5 ms preferred (0x10)" }, { 5, "10 ms preferred (0x20)" }, { 6, "RFU (0x40)" }, { 7, "RFU (0x80)" }, { } }; static void pac_decode_duration(const uint8_t *data, uint8_t len, util_debug_func_t func, void *user_data) { struct l2cap_frame frame; uint8_t value; uint8_t mask; l2cap_frame_init(&frame, 0, 0, 0, 0, 0, 0, data, len); if (!l2cap_frame_get_u8(&frame, &value)) { print_text(COLOR_ERROR, " value: invalid size"); goto done; } print_field(" Frame Duration: 0x%4.4x", value); mask = print_bitfield(8, value, pac_duration_table); if (mask) print_text(COLOR_WHITE_BG, " Unknown fields (0x%2.2x)", mask); done: if (frame.size) print_hex_field(" Data", frame.data, frame.size); } static const struct bitfield_data pac_channel_table[] = { { 0, "1 channel (0x01)" }, { 1, "2 channels (0x02)" }, { 2, "3 channels (0x04)" }, { 3, "4 chanenls (0x08)" }, { 4, "5 channels (0x10)" }, { 5, "6 channels (0x20)" }, { 6, "7 channels (0x40)" }, { 7, "8 channels (0x80)" }, { } }; static void pac_decode_channels(const uint8_t *data, uint8_t len, util_debug_func_t func, void *user_data) { struct l2cap_frame frame; uint8_t value; uint8_t mask; l2cap_frame_init(&frame, 0, 0, 0, 0, 0, 0, data, len); if (!l2cap_frame_get_u8(&frame, &value)) { print_text(COLOR_ERROR, " value: invalid size"); goto done; } print_field(" Audio Channel Count: 0x%2.2x", value); mask = print_bitfield(8, value, pac_channel_table); if (mask) print_text(COLOR_WHITE_BG, " Unknown fields (0x%2.2x)", mask); done: if (frame.size) print_hex_field(" Data", frame.data, frame.size); } static void pac_decode_frame_length(const uint8_t *data, uint8_t len, util_debug_func_t func, void *user_data) { struct l2cap_frame frame; uint16_t min, max; l2cap_frame_init(&frame, 0, 0, 0, 0, 0, 0, data, len); if (!l2cap_frame_get_le16(&frame, &min)) { print_text(COLOR_ERROR, " min: invalid size"); goto done; } if (!l2cap_frame_get_le16(&frame, &max)) { print_text(COLOR_ERROR, " min: invalid size"); goto done; } print_field(" Frame Length: %u (0x%4.4x) - %u (0x%4.4x)", min, min, max, max); done: if (frame.size) print_hex_field(" Data", frame.data, frame.size); } static void pac_decode_sdu(const uint8_t *data, uint8_t len, util_debug_func_t func, void *user_data) { struct l2cap_frame frame; uint8_t value; l2cap_frame_init(&frame, 0, 0, 0, 0, 0, 0, data, len); if (!l2cap_frame_get_u8(&frame, &value)) { print_text(COLOR_ERROR, " value: invalid size"); goto done; } print_field(" Max SDU: %u (0x%2.2x)", value, value); done: if (frame.size) print_hex_field(" Data", frame.data, frame.size); } static const struct util_ltv_debugger pac_cap_table[] = { UTIL_LTV_DEBUG(0x01, pac_decode_freq), UTIL_LTV_DEBUG(0x02, pac_decode_duration), UTIL_LTV_DEBUG(0x03, pac_decode_channels), UTIL_LTV_DEBUG(0x04, pac_decode_frame_length), UTIL_LTV_DEBUG(0x05, pac_decode_sdu) }; static void print_pac(const struct l2cap_frame *frame) { uint8_t num = 0, i; if (!l2cap_frame_get_u8((void *)frame, &num)) { print_text(COLOR_ERROR, "Number of PAC(s): invalid size"); goto done; } print_field(" Number of PAC(s): %u", num); for (i = 0; i < num; i++) { print_field(" PAC #%u:", i); if (!print_ase_codec(frame)) goto done; if (!print_ase_cc(frame, " Codec Specific Capabilities", pac_cap_table, ARRAY_SIZE(pac_cap_table))) break; if (!print_ase_metadata(frame)) break; } done: if (frame->size) print_hex_field(" Data", frame->data, frame->size); } static void pac_read(const struct l2cap_frame *frame) { print_pac(frame); } static void pac_notify(const struct l2cap_frame *frame) { print_pac(frame); } static bool print_prefer_framing(const struct l2cap_frame *frame) { uint8_t framing; if (!l2cap_frame_get_u8((void *)frame, &framing)) { print_text(COLOR_ERROR, " Framing: invalid size"); return false; } switch (framing) { case 0x00: print_field(" Framing: Unframed PDUs supported (0x00)"); break; case 0x01: print_field(" Framing: Unframed PDUs not supported (0x01)"); break; default: print_field(" Framing: Reserved (0x%2.2x)", framing); break; } return true; } static const struct bitfield_data prefer_phy_table[] = { { 0, "LE 1M PHY preffered (0x01)" }, { 1, "LE 2M PHY preffered (0x02)" }, { 2, "LE Codec PHY preffered (0x04)" }, { } }; static bool print_prefer_phy(const struct l2cap_frame *frame) { uint8_t phy, mask; if (!l2cap_frame_get_u8((void *)frame, &phy)) { print_text(COLOR_ERROR, "PHY: invalid size"); return false; } print_field(" PHY: 0x%2.2x", phy); mask = print_bitfield(4, phy, prefer_phy_table); if (mask) print_text(COLOR_WHITE_BG, " Unknown fields (0x%2.2x)", mask); return true; } static bool print_ase_rtn(const struct l2cap_frame *frame, const char *label) { uint8_t rtn; if (!l2cap_frame_get_u8((void *)frame, &rtn)) { print_text(COLOR_ERROR, "%s: invalid size", label); return false; } print_field("%s: %u", label, rtn); return true; } static bool print_ase_latency(const struct l2cap_frame *frame, const char *label) { uint16_t latency; if (!l2cap_frame_get_le16((void *)frame, &latency)) { print_text(COLOR_ERROR, "%s: invalid size", label); return false; } print_field("%s: %u", label, latency); return true; } static bool print_ase_pd(const struct l2cap_frame *frame, const char *label) { uint32_t pd; if (!l2cap_frame_get_le24((void *)frame, &pd)) { print_text(COLOR_ERROR, "%s: invalid size", label); return false; } print_field("%s: %u us", label, pd); return true; } static void ase_debug_freq(const uint8_t *data, uint8_t len, util_debug_func_t func, void *user_data) { struct l2cap_frame frame; uint8_t value; l2cap_frame_init(&frame, 0, 0, 0, 0, 0, 0, data, len); if (!l2cap_frame_get_u8(&frame, &value)) { print_text(COLOR_ERROR, " value: invalid size"); goto done; } switch (value) { case 0x01: print_field(" Sampling Frequency: 8 Khz (0x01)"); break; case 0x02: print_field(" Sampling Frequency: 11.25 Khz (0x02)"); break; case 0x03: print_field(" Sampling Frequency: 16 Khz (0x03)"); break; case 0x04: print_field(" Sampling Frequency: 22.05 Khz (0x04)"); break; case 0x05: print_field(" Sampling Frequency: 24 Khz (0x05)"); break; case 0x06: print_field(" Sampling Frequency: 32 Khz (0x06)"); break; case 0x07: print_field(" Sampling Frequency: 44.1 Khz (0x07)"); break; case 0x08: print_field(" Sampling Frequency: 48 Khz (0x08)"); break; case 0x09: print_field(" Sampling Frequency: 88.2 Khz (0x09)"); break; case 0x0a: print_field(" Sampling Frequency: 96 Khz (0x0a)"); break; case 0x0b: print_field(" Sampling Frequency: 176.4 Khz (0x0b)"); break; case 0x0c: print_field(" Sampling Frequency: 192 Khz (0x0c)"); break; case 0x0d: print_field(" Sampling Frequency: 384 Khz (0x0d)"); break; default: print_field(" Sampling Frequency: RFU (0x%2.2x)", value); break; } done: if (frame.size) print_hex_field(" Data", frame.data, frame.size); } static void ase_debug_duration(const uint8_t *data, uint8_t len, util_debug_func_t func, void *user_data) { struct l2cap_frame frame; uint8_t value; l2cap_frame_init(&frame, 0, 0, 0, 0, 0, 0, data, len); if (!l2cap_frame_get_u8(&frame, &value)) { print_text(COLOR_ERROR, " value: invalid size"); goto done; } switch (value) { case 0x00: print_field(" Frame Duration: 7.5 ms (0x00)"); break; case 0x01: print_field(" Frame Duration: 10 ms (0x01)"); break; default: print_field(" Frame Duration: RFU (0x%2.2x)", value); break; } done: if (frame.size) print_hex_field(" Data", frame.data, frame.size); } static const struct bitfield_data channel_location_table[] = { { 0, "Front Left (0x00000001)" }, { 1, "Front Right (0x00000002)" }, { 2, "Front Center (0x00000004)" }, { 3, "Low Frequency Effects 1 (0x00000008)" }, { 4, "Back Left (0x00000010)" }, { 5, "Back Right (0x00000020)" }, { 6, "Front Left of Center (0x00000040)" }, { 7, "Front Right of Center (0x00000080)" }, { 8, "Back Center (0x00000100)" }, { 9, "Low Frequency Effects 2 (0x00000200)" }, { 10, "Side Left (0x00000400)" }, { 11, "Side Right (0x00000800)" }, { 12, "Top Front Left (0x00001000)" }, { 13, "Top Front Right (0x00002000)" }, { 14, "Top Front Center (0x00004000)" }, { 15, "Top Center (0x00008000)" }, { 16, "Top Back Left (0x00010000)" }, { 17, "Top Back Right (0x00020000)" }, { 18, "Top Side Left (0x00040000)" }, { 19, "Top Side Right (0x00080000)" }, { 20, "Top Back Center (0x00100000)" }, { 21, "Bottom Front Center (0x00200000)" }, { 22, "Bottom Front Left (0x00400000)" }, { 23, "Bottom Front Right (0x00800000)" }, { 24, "Front Left Wide (0x01000000)" }, { 25, "Front Right Wide (0x02000000)" }, { 26, "Left Surround (0x04000000)" }, { 27, "Right Surround (0x08000000)" }, { 28, "RFU (0x10000000)" }, { 29, "RFU (0x20000000)" }, { 30, "RFU (0x40000000)" }, { 31, "RFU (0x80000000)" }, { } }; static void print_location(const struct l2cap_frame *frame) { uint32_t value; uint32_t mask; if (!l2cap_frame_get_le32((void *)frame, &value)) { print_text(COLOR_ERROR, " value: invalid size"); goto done; } print_field(" Location: 0x%8.8x", value); mask = print_bitfield(6, value, channel_location_table); if (mask) print_text(COLOR_WHITE_BG, " Unknown fields (0x%8.8x)", mask); done: if (frame->size) print_hex_field(" Data", frame->data, frame->size); } static void ase_debug_location(const uint8_t *data, uint8_t len, util_debug_func_t func, void *user_data) { struct l2cap_frame frame; l2cap_frame_init(&frame, 0, 0, 0, 0, 0, 0, data, len); print_location(&frame); } static void ase_debug_frame_length(const uint8_t *data, uint8_t len, util_debug_func_t func, void *user_data) { struct l2cap_frame frame; uint16_t value; l2cap_frame_init(&frame, 0, 0, 0, 0, 0, 0, data, len); if (!l2cap_frame_get_le16(&frame, &value)) { print_text(COLOR_ERROR, " value: invalid size"); goto done; } print_field(" Frame Length: %u (0x%4.4x)", value, value); done: if (frame.size) print_hex_field(" Data", frame.data, frame.size); } static void ase_debug_blocks(const uint8_t *data, uint8_t len, util_debug_func_t func, void *user_data) { struct l2cap_frame frame; uint8_t value; l2cap_frame_init(&frame, 0, 0, 0, 0, 0, 0, data, len); if (!l2cap_frame_get_u8(&frame, &value)) { print_text(COLOR_ERROR, " value: invalid size"); goto done; } print_field(" Frame Blocks per SDU: %u (0x%2.2x)", value, value); done: if (frame.size) print_hex_field(" Data", frame.data, frame.size); } static const struct util_ltv_debugger ase_cc_table[] = { UTIL_LTV_DEBUG(0x01, ase_debug_freq), UTIL_LTV_DEBUG(0x02, ase_debug_duration), UTIL_LTV_DEBUG(0x03, ase_debug_location), UTIL_LTV_DEBUG(0x04, ase_debug_frame_length), UTIL_LTV_DEBUG(0x05, ase_debug_blocks) }; static void print_ase_config(const struct l2cap_frame *frame) { if (!print_prefer_framing(frame)) return; if (!print_prefer_phy(frame)) return; if (!print_ase_rtn(frame, " RTN")) return; if (!print_ase_latency(frame, " Max Transport Latency")) return; if (!print_ase_pd(frame, " Presentation Delay Min")) return; if (!print_ase_pd(frame, " Presentation Delay Max")) return; if (!print_ase_pd(frame, " Preferred Presentation Delay Min")) return; if (!print_ase_pd(frame, " Preferred Presentation Delay Max")) return; if (!print_ase_codec(frame)) return; print_ase_cc(frame, " Codec Specific Configuration", ase_cc_table, ARRAY_SIZE(ase_cc_table)); } static bool print_ase_framing(const struct l2cap_frame *frame, const char *label) { uint8_t framing; if (!l2cap_frame_get_u8((void *)frame, &framing)) { print_text(COLOR_ERROR, "%s: invalid size", label); return false; } switch (framing) { case 0x00: print_field("%s: Unframed (0x00)", label); break; case 0x01: print_field("%s: Framed (0x01)", label); break; default: print_field("%s: Reserved (0x%2.2x)", label, framing); } return true; } static const struct bitfield_data phy_table[] = { { 0, "LE 1M PHY (0x01)" }, { 1, "LE 2M PHY (0x02)" }, { 2, "LE Codec PHY (0x04)" }, { } }; static bool print_ase_phy(const struct l2cap_frame *frame, const char *label) { uint8_t phy, mask; if (!l2cap_frame_get_u8((void *)frame, &phy)) { print_text(COLOR_ERROR, "%s: invalid size", label); return false; } print_field("%s: 0x%2.2x", label, phy); mask = print_bitfield(4, phy, phy_table); if (mask) print_text(COLOR_WHITE_BG, " Unknown fields (0x%2.2x)", mask); return true; } static bool print_ase_interval(const struct l2cap_frame *frame, const char *label) { uint32_t interval; if (!l2cap_frame_get_le24((void *)frame, &interval)) { print_text(COLOR_ERROR, "%s: invalid size", label); return false; } print_field("%s: %u usec", label, interval); return true; } static bool print_ase_sdu(const struct l2cap_frame *frame, const char *label) { uint16_t sdu; if (!l2cap_frame_get_le16((void *)frame, &sdu)) { print_text(COLOR_ERROR, "%s: invalid size", label); return false; } print_field("%s: %u", label, sdu); return true; } static void print_ase_qos(const struct l2cap_frame *frame) { if (!l2cap_frame_print_u8((void *)frame, " CIG ID")) return; if (!l2cap_frame_print_u8((void *)frame, " CIS ID")) return; if (!print_ase_interval(frame, " SDU Interval")) return; if (!print_ase_framing(frame, " Framing")) return; if (!print_ase_phy(frame, " PHY")) return; if (!print_ase_sdu(frame, " Max SDU")) return; if (!print_ase_rtn(frame, " RTN")) return; if (!print_ase_latency(frame, " Max Transport Latency")) return; print_ase_pd(frame, " Presentation Delay"); } static void print_ase_metadata_status(const struct l2cap_frame *frame) { if (!l2cap_frame_print_u8((void *)frame, " CIG ID")) return; if (!l2cap_frame_print_u8((void *)frame, " CIS ID")) return; print_ase_metadata(frame); } static void print_ase_status(const struct l2cap_frame *frame) { uint8_t id, state; if (!l2cap_frame_get_u8((void *)frame, &id)) { print_text(COLOR_ERROR, "ASE ID: invalid size"); goto done; } print_field(" ASE ID: %u", id); if (!l2cap_frame_get_u8((void *)frame, &state)) { print_text(COLOR_ERROR, "ASE State: invalid size"); goto done; } switch (state) { /* ASE_State = 0x00 (Idle) */ case 0x00: print_field(" State: Idle (0x00)"); break; /* ASE_State = 0x01 (Codec Configured) */ case 0x01: print_field(" State: Codec Configured (0x01)"); print_ase_config(frame); break; /* ASE_State = 0x02 (QoS Configured) */ case 0x02: print_field(" State: QoS Configured (0x02)"); print_ase_qos(frame); break; /* ASE_Status = 0x03 (Enabling) */ case 0x03: print_field(" State: Enabling (0x03)"); print_ase_metadata_status(frame); break; /* ASE_Status = 0x04 (Streaming) */ case 0x04: print_field(" State: Streaming (0x04)"); print_ase_metadata_status(frame); break; /* ASE_Status = 0x05 (Disabling) */ case 0x05: print_field(" State: Disabling (0x05)"); print_ase_metadata_status(frame); break; /* ASE_Status = 0x06 (Releasing) */ case 0x06: print_field(" State: Releasing (0x06)"); break; default: print_field(" State: Reserved (0x%2.2x)", state); break; } done: if (frame->size) print_hex_field(" Data", frame->data, frame->size); } static void ase_read(const struct l2cap_frame *frame) { print_ase_status(frame); } static void ase_notify(const struct l2cap_frame *frame) { print_ase_status(frame); } static bool print_ase_target_latency(const struct l2cap_frame *frame) { uint8_t latency; if (!l2cap_frame_get_u8((void *)frame, &latency)) { print_text(COLOR_ERROR, " Target Latency: invalid size"); return false; } switch (latency) { case 0x01: print_field(" Target Latency: Low Latency (0x01)"); break; case 0x02: print_field(" Target Latency: Balance Latency/Reliability " "(0x02)"); break; case 0x03: print_field(" Target Latency: High Reliability (0x03)"); break; default: print_field(" Target Latency: Reserved (0x%2.2x)", latency); break; } return true; } static bool ase_config_cmd(const struct l2cap_frame *frame) { if (!l2cap_frame_print_u8((void *)frame, " ASE ID")) return false; if (!print_ase_target_latency(frame)) return false; if (!print_ase_phy(frame, " PHY")) return false; if (!print_ase_codec(frame)) return false; if (!print_ase_cc(frame, " Codec Specific Configuration", ase_cc_table, ARRAY_SIZE(ase_cc_table))) return false; return true; } static bool ase_qos_cmd(const struct l2cap_frame *frame) { if (!l2cap_frame_print_u8((void *)frame, " ASE ID")) return false; if (!l2cap_frame_print_u8((void *)frame, " CIG ID")) return false; if (!l2cap_frame_print_u8((void *)frame, " CIS ID")) return false; if (!print_ase_interval(frame, " SDU Interval")) return false; if (!print_ase_framing(frame, " Framing")) return false; if (!print_ase_phy(frame, " PHY")) return false; if (!print_ase_sdu(frame, " Max SDU")) return false; if (!print_ase_rtn(frame, " RTN")) return false; if (!print_ase_latency(frame, " Max Transport Latency")) return false; if (!print_ase_pd(frame, " Presentation Delay")) return false; return true; } static bool ase_enable_cmd(const struct l2cap_frame *frame) { if (!l2cap_frame_print_u8((void *)frame, " ASE ID")) return false; if (!print_ase_metadata(frame)) return false; return true; } static bool ase_start_cmd(const struct l2cap_frame *frame) { if (!l2cap_frame_print_u8((void *)frame, " ASE ID")) return false; return true; } static bool ase_disable_cmd(const struct l2cap_frame *frame) { if (!l2cap_frame_print_u8((void *)frame, " ASE ID")) return false; return true; } static bool ase_stop_cmd(const struct l2cap_frame *frame) { if (!l2cap_frame_print_u8((void *)frame, " ASE ID")) return false; return true; } static bool ase_metadata_cmd(const struct l2cap_frame *frame) { if (!l2cap_frame_print_u8((void *)frame, " ASE ID")) return false; if (!print_ase_metadata(frame)) return false; return true; } static bool ase_release_cmd(const struct l2cap_frame *frame) { if (!l2cap_frame_print_u8((void *)frame, " ASE ID")) return false; return true; } #define ASE_CMD(_op, _desc, _func) \ [_op] = { \ .desc = _desc, \ .func = _func, \ } static const struct ase_cmd { const char *desc; bool (*func)(const struct l2cap_frame *frame); } ase_cmd_table[] = { /* Opcode = 0x01 (Codec Configuration) */ ASE_CMD(0x01, "Codec Configuration", ase_config_cmd), /* Opcode = 0x02 (QoS Configuration) */ ASE_CMD(0x02, "QoS Configuration", ase_qos_cmd), /* Opcode = 0x03 (Enable) */ ASE_CMD(0x03, "Enable", ase_enable_cmd), /* Opcode = 0x04 (Receiver Start Ready) */ ASE_CMD(0x04, "Receiver Start Ready", ase_start_cmd), /* Opcode = 0x05 (Disable) */ ASE_CMD(0x05, "Disable", ase_disable_cmd), /* Opcode = 0x06 (Receiver Stop Ready) */ ASE_CMD(0x06, "Receiver Stop Ready", ase_stop_cmd), /* Opcode = 0x07 (Update Metadata) */ ASE_CMD(0x07, "Update Metadata", ase_metadata_cmd), /* Opcode = 0x08 (Release) */ ASE_CMD(0x08, "Release", ase_release_cmd), }; static const struct ase_cmd *ase_get_cmd(uint8_t op) { if (op > ARRAY_SIZE(ase_cmd_table)) return NULL; return &ase_cmd_table[op]; } static void print_ase_cmd(const struct l2cap_frame *frame) { uint8_t op, num, i; const struct ase_cmd *cmd; if (!l2cap_frame_get_u8((void *)frame, &op)) { print_text(COLOR_ERROR, "opcode: invalid size"); goto done; } if (!l2cap_frame_get_u8((void *)frame, &num)) { print_text(COLOR_ERROR, "num: invalid size"); goto done; } cmd = ase_get_cmd(op); if (!cmd) { print_field(" Opcode: Reserved (0x%2.2x)", op); goto done; } print_field(" Opcode: %s (0x%2.2x)", cmd->desc, op); print_field(" Number of ASE(s): %u", num); for (i = 0; i < num && frame->size; i++) { print_field(" ASE: #%u", i); if (!cmd->func(frame)) break; } done: if (frame->size) print_hex_field(" Data", frame->data, frame->size); } static void ase_cp_write(const struct l2cap_frame *frame) { print_ase_cmd(frame); } static bool print_ase_cp_rsp_code(const struct l2cap_frame *frame) { uint8_t code; if (!l2cap_frame_get_u8((void *)frame, &code)) { print_text(COLOR_ERROR, " ASE Response Code: invalid size"); return false; } switch (code) { case 0x00: print_field(" ASE Response Code: Success (0x00)"); break; case 0x01: print_field(" ASE Response Code: Unsupported Opcode (0x01)"); break; case 0x02: print_field(" ASE Response Code: Invalid Length (0x02)"); break; case 0x03: print_field(" ASE Response Code: Invalid ASE ID (0x03)"); break; case 0x04: print_field(" ASE Response Code: Invalid ASE State (0x04)"); break; case 0x05: print_field(" ASE Response Code: Invalid ASE Direction " "(0x05)"); break; case 0x06: print_field(" ASE Response Code: Unsupported Audio " "Capabilities (0x06)"); break; case 0x07: print_field(" ASE Response Code: Unsupported Configuration " "(0x07)"); break; case 0x08: print_field(" ASE Response Code: Rejected Configuration " "(0x08)"); break; case 0x09: print_field(" ASE Response Code: Invalid Configuration " "(0x09)"); break; case 0x0a: print_field(" ASE Response Code: Unsupported Metadata " "(0x0a)"); break; case 0x0b: print_field(" ASE Response Code: Rejected Metadata (0x0b)"); break; case 0x0c: print_field(" ASE Response Code: Invalid Metadata (0x0c)"); break; case 0x0d: print_field(" ASE Response Code: Insufficient Resources " "(0x0d)"); break; case 0x0e: print_field(" ASE Response Code: Unspecified Error (0x0e)"); break; default: print_field(" ASE Response Code: Reserved (0x%2.2x)", code); break; } return true; } static bool print_ase_cp_rsp_reason(const struct l2cap_frame *frame) { uint8_t reason; if (!l2cap_frame_get_u8((void *)frame, &reason)) { print_text(COLOR_ERROR, " ASE Response Reason: invalid size"); return false; } switch (reason) { case 0x00: print_field(" ASE Response Reason: None (0x00)"); break; case 0x01: print_field(" ASE Response Reason: ASE ID (0x01)"); break; case 0x02: print_field(" ASE Response Reason: Codec Specific " "Configuration (0x02)"); break; case 0x03: print_field(" ASE Response Reason: SDU Interval (0x03)"); break; case 0x04: print_field(" ASE Response Reason: Framing (0x04)"); break; case 0x05: print_field(" ASE Response Reason: PHY (0x05)"); break; case 0x06: print_field(" ASE Response Reason: Max SDU (0x06)"); break; case 0x07: print_field(" ASE Response Reason: RTN (0x07)"); break; case 0x08: print_field(" ASE Response Reason: Max Transport Latency " "(0x08)"); break; case 0x09: print_field(" ASE Response Reason: Presentation Delay " "(0x09)"); break; case 0x0a: print_field(" ASE Response Reason: Invalid ASE/CIS Mapping " "(0x0a)"); break; default: print_field(" ASE Response Reason: Reserved (0x%2.2x)", reason); break; } return true; } static void print_ase_cp_rsp(const struct l2cap_frame *frame) { uint8_t op, num, i; const struct ase_cmd *cmd; if (!l2cap_frame_get_u8((void *)frame, &op)) { print_text(COLOR_ERROR, " opcode: invalid size"); goto done; } if (!l2cap_frame_get_u8((void *)frame, &num)) { print_text(COLOR_ERROR, " Number of ASE(s): invalid size"); goto done; } cmd = ase_get_cmd(op); if (!cmd) { print_field(" Opcode: Reserved (0x%2.2x)", op); goto done; } print_field(" Opcode: %s (0x%2.2x)", cmd->desc, op); print_field(" Number of ASE(s): %u", num); for (i = 0; i < num && frame->size; i++) { print_field(" ASE: #%u", i); if (!l2cap_frame_print_u8((void *)frame, " ASE ID")) break; if (!print_ase_cp_rsp_code(frame)) break; if (!print_ase_cp_rsp_reason(frame)) break; } done: if (frame->size) print_hex_field(" Data", frame->data, frame->size); } static void ase_cp_notify(const struct l2cap_frame *frame) { print_ase_cp_rsp(frame); } static void pac_loc_read(const struct l2cap_frame *frame) { print_location(frame); } static void pac_loc_notify(const struct l2cap_frame *frame) { print_location(frame); } static void print_pac_context(const struct l2cap_frame *frame) { uint16_t snk, src; uint16_t mask; if (!l2cap_frame_get_le16((void *)frame, &snk)) { print_text(COLOR_ERROR, " sink: invalid size"); goto done; } print_field(" Sink Context: 0x%4.4x", snk); mask = print_bitfield(4, snk, pac_context_table); if (mask) print_text(COLOR_WHITE_BG, " Unknown fields (0x%4.4x)", mask); if (!l2cap_frame_get_le16((void *)frame, &src)) { print_text(COLOR_ERROR, " source: invalid size"); goto done; } print_field(" Source Context: 0x%4.4x", src); mask = print_bitfield(4, src, pac_context_table); if (mask) print_text(COLOR_WHITE_BG, " Unknown fields (0x%4.4x)", mask); done: if (frame->size) print_hex_field(" Data", frame->data, frame->size); } static void pac_context_read(const struct l2cap_frame *frame) { print_pac_context(frame); } static void pac_context_notify(const struct l2cap_frame *frame) { print_pac_context(frame); } static void csip_rank_read(const struct l2cap_frame *frame) { uint8_t rank; if (!l2cap_frame_get_u8((void *)frame, &rank)) { print_text(COLOR_ERROR, "Rank: invalid size"); goto done; } print_field(" Rank: 0x%02x", rank); done: if (frame->size) print_hex_field(" Data", frame->data, frame->size); } static void csip_lock_read(const struct l2cap_frame *frame) { uint8_t lock; if (!l2cap_frame_get_u8((void *)frame, &lock)) { print_text(COLOR_ERROR, "Lock: invalid size"); goto done; } switch (lock) { case 0x01: print_field(" Unlocked (0x%02x)", lock); break; case 0x02: print_field(" Locked (0x%02x)", lock); break; default: print_field(" RFU (0x%02x)", lock); break; } done: if (frame->size) print_hex_field(" Data", frame->data, frame->size); } static void print_csip_size(const struct l2cap_frame *frame) { uint8_t size; if (!l2cap_frame_get_u8((void *)frame, &size)) { print_text(COLOR_ERROR, "Size: invalid size"); goto done; } print_field(" Size: 0x%02x", size); done: if (frame->size) print_hex_field(" Data", frame->data, frame->size); } static void csip_size_read(const struct l2cap_frame *frame) { print_csip_size(frame); } static void csip_size_notify(const struct l2cap_frame *frame) { print_csip_size(frame); } static void csip_sirk_read(const struct l2cap_frame *frame) { if (frame->size) print_hex_field(" SIRK", frame->data, frame->size); } static void csip_sirk_notify(const struct l2cap_frame *frame) { if (frame->size) print_hex_field(" SIRK", frame->data, frame->size); } static void print_vcs_state(const struct l2cap_frame *frame) { uint8_t vol_set, mute, chng_ctr; if (!l2cap_frame_get_u8((void *)frame, &vol_set)) { print_text(COLOR_ERROR, "Volume Settings: invalid size"); goto done; } print_field(" Volume Setting: %u", vol_set); if (!l2cap_frame_get_u8((void *)frame, &mute)) { print_text(COLOR_ERROR, "Mute Filed: invalid size"); goto done; } switch (mute) { case 0x00: print_field(" Not Muted: %u", mute); break; case 0x01: print_field(" Muted: %u", mute); break; default: print_field(" Unknown Mute Value: %u", mute); break; } if (!l2cap_frame_get_u8((void *)frame, &chng_ctr)) { print_text(COLOR_ERROR, "Change Counter: invalid size"); goto done; } print_field(" Change Counter: %u", chng_ctr); done: if (frame->size) print_hex_field(" Data", frame->data, frame->size); } static void vol_state_read(const struct l2cap_frame *frame) { print_vcs_state(frame); } static void vol_state_notify(const struct l2cap_frame *frame) { print_vcs_state(frame); } static bool vcs_config_cmd(const struct l2cap_frame *frame) { if (!l2cap_frame_print_u8((void *)frame, " Change Counter")) return false; return true; } static bool vcs_absolute_cmd(const struct l2cap_frame *frame) { if (!l2cap_frame_print_u8((void *)frame, " Change Counter")) return false; if (!l2cap_frame_print_u8((void *)frame, " Volume Setting")) return false; return true; } #define VCS_CMD(_op, _desc, _func) \ [_op] = { \ .desc = _desc, \ .func = _func, \ } static const struct vcs_cmd { const char *desc; bool (*func)(const struct l2cap_frame *frame); } vcs_cmd_table[] = { /* Opcode = 0x00 (Relative Volume Down) */ VCS_CMD(0x00, "Relative Volume Down", vcs_config_cmd), /* Opcode = 0x01 (Relative Volume Up) */ VCS_CMD(0x01, "Relative Volume Up", vcs_config_cmd), /* Opcode = 0x02 (Unmute/Relative Volume Down) */ VCS_CMD(0x02, "Unmute/Relative Volume Down", vcs_config_cmd), /* Opcode = 0x03 (Unmute/Relative Volume Up) */ VCS_CMD(0x03, "Unmute/Relative Volume Up", vcs_config_cmd), /* Opcode = 0x04 (Set Absolute Volume) */ VCS_CMD(0x04, "Set Absolute Volume", vcs_absolute_cmd), /* Opcode = 0x05 (Unmute) */ VCS_CMD(0x05, "Unmute", vcs_config_cmd), /* Opcode = 0x06 (Mute) */ VCS_CMD(0x06, "Mute", vcs_config_cmd), }; static const struct vcs_cmd *vcs_get_cmd(uint8_t op) { if (op > ARRAY_SIZE(vcs_cmd_table)) return NULL; return &vcs_cmd_table[op]; } static void print_vcs_cmd(const struct l2cap_frame *frame) { uint8_t op; const struct vcs_cmd *cmd; if (!l2cap_frame_get_u8((void *)frame, &op)) { print_text(COLOR_ERROR, "opcode: invalid size"); goto done; } cmd = vcs_get_cmd(op); if (!cmd) { print_field(" Opcode: Reserved (0x%2.2x)", op); goto done; } print_field(" Opcode: %s (0x%2.2x)", cmd->desc, op); if (!cmd->func(frame)) print_field(" Unknown Opcode"); done: if (frame->size) print_hex_field(" Data", frame->data, frame->size); } static void vol_cp_write(const struct l2cap_frame *frame) { print_vcs_cmd(frame); } static void print_vcs_flag(const struct l2cap_frame *frame) { uint8_t vol_flag; if (!l2cap_frame_get_u8((void *)frame, &vol_flag)) { print_text(COLOR_ERROR, "Volume Flag: invalid size"); goto done; } print_field(" Volume Falg: %u", vol_flag); done: if (frame->size) print_hex_field(" Data", frame->data, frame->size); } static void vol_flag_read(const struct l2cap_frame *frame) { print_vcs_flag(frame); } static void vol_flag_notify(const struct l2cap_frame *frame) { print_vcs_flag(frame); } static char *name2utf8(const uint8_t *name, uint16_t len) { char utf8_name[HCI_MAX_NAME_LENGTH + 2]; int i; if (g_utf8_validate((const char *) name, len, NULL)) return g_strndup((char *) name, len); len = MIN(len, sizeof(utf8_name) - 1); memset(utf8_name, 0, sizeof(utf8_name)); strncpy(utf8_name, (char *) name, len); /* Assume ASCII, and replace all non-ASCII with spaces */ for (i = 0; utf8_name[i] != '\0'; i++) { if (!isascii(utf8_name[i])) utf8_name[i] = ' '; } /* Remove leading and trailing whitespace characters */ g_strstrip(utf8_name); return g_strdup(utf8_name); } static void print_mp_name(const struct l2cap_frame *frame) { char *name; name = name2utf8((uint8_t *)frame->data, frame->size); print_field(" Media Player Name: %s", name); g_free(name); } static void mp_name_read(const struct l2cap_frame *frame) { print_mp_name(frame); } static void mp_name_notify(const struct l2cap_frame *frame) { print_mp_name(frame); } static void print_track_changed(const struct l2cap_frame *frame) { print_field(" Track Changed"); } static void track_changed_notify(const struct l2cap_frame *frame) { print_track_changed(frame); } static void print_track_title(const struct l2cap_frame *frame) { char *name; name = name2utf8((uint8_t *)frame->data, frame->size); print_field(" Track Title: %s", name); g_free(name); } static void track_title_read(const struct l2cap_frame *frame) { print_track_title(frame); } static void track_title_notify(const struct l2cap_frame *frame) { print_track_title(frame); } static void print_track_duration(const struct l2cap_frame *frame) { int32_t duration; if (!l2cap_frame_get_le32((void *)frame, (uint32_t *)&duration)) { print_text(COLOR_ERROR, " Track Duration: invalid size"); goto done; } print_field(" Track Duration: %u", duration); done: if (frame->size) print_hex_field(" Data", frame->data, frame->size); } static void track_duration_read(const struct l2cap_frame *frame) { print_track_duration(frame); } static void track_duration_notify(const struct l2cap_frame *frame) { print_track_duration(frame); } static void print_track_position(const struct l2cap_frame *frame) { int32_t position; if (!l2cap_frame_get_le32((void *)frame, (uint32_t *)&position)) { print_text(COLOR_ERROR, " Track Position: invalid size"); goto done; } print_field(" Track Position: %u", position); done: if (frame->size) print_hex_field(" Data", frame->data, frame->size); } static void track_position_read(const struct l2cap_frame *frame) { print_track_position(frame); } static void track_position_write(const struct l2cap_frame *frame) { print_track_position(frame); } static void track_position_notify(const struct l2cap_frame *frame) { print_track_position(frame); } static void print_playback_speed(const struct l2cap_frame *frame) { int8_t playback_speed; if (!l2cap_frame_get_u8((void *)frame, (uint8_t *)&playback_speed)) { print_text(COLOR_ERROR, " Playback Speed: invalid size"); goto done; } print_field(" Playback Speed: %u", playback_speed); done: if (frame->size) print_hex_field(" Data", frame->data, frame->size); } static void playback_speed_read(const struct l2cap_frame *frame) { print_playback_speed(frame); } static void playback_speed_write(const struct l2cap_frame *frame) { print_playback_speed(frame); } static void playback_speed_notify(const struct l2cap_frame *frame) { print_playback_speed(frame); } static void print_seeking_speed(const struct l2cap_frame *frame) { int8_t seeking_speed; if (!l2cap_frame_get_u8((void *)frame, (uint8_t *)&seeking_speed)) { print_text(COLOR_ERROR, " Seeking Speed: invalid size"); goto done; } print_field(" Seeking Speed: %u", seeking_speed); done: if (frame->size) print_hex_field(" Data", frame->data, frame->size); } static void seeking_speed_read(const struct l2cap_frame *frame) { print_seeking_speed(frame); } static void seeking_speed_notify(const struct l2cap_frame *frame) { print_seeking_speed(frame); } static void print_bearer_name(const struct l2cap_frame *frame) { char *name; name = name2utf8((uint8_t *)frame->data, frame->size); print_field(" Bearer Name: %s", name); g_free(name); } static void bearer_name_read(const struct l2cap_frame *frame) { print_bearer_name(frame); } static void bearer_name_notify(const struct l2cap_frame *frame) { print_bearer_name(frame); } static void bearer_uci_read(const struct l2cap_frame *frame) { char *name; name = name2utf8((uint8_t *)frame->data, frame->size); print_field(" Bearer Uci Name: %s", name); g_free(name); } static void print_technology_name(const struct l2cap_frame *frame) { int8_t tech_id; const char *str; if (!l2cap_frame_get_u8((void *)frame, (uint8_t *)&tech_id)) { print_text(COLOR_ERROR, " Technology id:: invalid size"); goto done; } switch (tech_id) { case 0x01: str = "3G"; break; case 0x02: str = "4G"; break; case 0x03: str = "LTE"; break; case 0x04: str = "WiFi"; break; case 0x05: str = "5G"; break; case 0x06: str = "GSM"; break; case 0x07: str = "CDMA"; break; case 0x08: str = "2G"; break; case 0x09: str = "WCDMA"; break; default: str = "Reserved"; break; } print_field("Technology: %s (0x%2.2x)", str, tech_id); done: if (frame->size) print_hex_field(" Data", frame->data, frame->size); } static void bearer_technology_read(const struct l2cap_frame *frame) { print_technology_name(frame); } static void bearer_technology_notify(const struct l2cap_frame *frame) { print_technology_name(frame); } static void print_uri_scheme_list(const struct l2cap_frame *frame) { char *name; name = name2utf8((uint8_t *)frame->data, frame->size); print_field(" Uri scheme Name: %s", name); g_free(name); } static void bearer_uri_schemes_list_read(const struct l2cap_frame *frame) { print_uri_scheme_list(frame); } static void print_signal_strength(const struct l2cap_frame *frame) { uint8_t signal_strength; if (!l2cap_frame_get_u8((void *)frame, (uint8_t *)&signal_strength)) { print_text(COLOR_ERROR, " signal_strength:: invalid size"); goto done; } print_field(" signal_strength: %x", signal_strength); if (signal_strength == 0) print_field(" No Service"); else if (signal_strength == 0x64) print_field(" Maximum signal strength"); else if ((signal_strength > 0) && (signal_strength < 0x64)) print_field(" Implementation specific"); else if (signal_strength == 0xFF) print_field(" Signal strength is unavailable"); else print_field(" RFU"); done: if (frame->size) print_hex_field(" Data", frame->data, frame->size); } static void bearer_signal_strength_read(const struct l2cap_frame *frame) { print_signal_strength(frame); } static void bearer_signal_strength_notify(const struct l2cap_frame *frame) { print_signal_strength(frame); } static void print_signal_strength_rep_intrvl(const struct l2cap_frame *frame) { int8_t reporting_intrvl; if (!l2cap_frame_get_u8((void *)frame, (uint8_t *)&reporting_intrvl)) { print_text(COLOR_ERROR, "Reporting_interval:: invalid size"); goto done; } print_field(" Reporting_interval: 0x%x", reporting_intrvl); done: if (frame->size) print_hex_field(" Data", frame->data, frame->size); } static void bearer_signal_strength_rep_intrvl_read(const struct l2cap_frame *frame) { print_signal_strength_rep_intrvl(frame); } static void bearer_signal_strength_rep_intrvl_write(const struct l2cap_frame *frame) { print_signal_strength_rep_intrvl(frame); } static void print_call_list(const struct l2cap_frame *frame) { uint8_t list_item_length; uint8_t call_index; uint8_t call_state; uint8_t call_flag; char *call_uri; if (!l2cap_frame_get_u8((void *)frame, (uint8_t *)&list_item_length)) { print_text(COLOR_ERROR, " list_item_length:: invalid size"); goto done; } print_field(" list_item_length: 0x%x", list_item_length); if (!l2cap_frame_get_u8((void *)frame, (uint8_t *)&call_index)) { print_text(COLOR_ERROR, " call_index:: invalid size"); goto done; } print_field(" call_index: 0x%x", call_index); if (!l2cap_frame_get_u8((void *)frame, (uint8_t *)&call_state)) { print_text(COLOR_ERROR, " call_state:: invalid size"); goto done; } print_field(" call_state: 0x%x", call_state); if (!l2cap_frame_get_u8((void *)frame, (uint8_t *)&call_flag)) { print_text(COLOR_ERROR, " call_flag:: invalid size"); goto done; } print_field(" call_flag: 0x%x", call_flag); call_uri = name2utf8((uint8_t *)frame->data, frame->size); print_field(" call_uri: %s", call_uri); g_free(call_uri); done: if (frame->size) print_hex_field(" call_list Data", frame->data, frame->size); } static void bearer_current_call_list_read(const struct l2cap_frame *frame) { print_call_list(frame); } static void bearer_current_call_list_notify(const struct l2cap_frame *frame) { print_call_list(frame); } static void print_ccid(const struct l2cap_frame *frame) { int8_t ccid; if (!l2cap_frame_get_u8((void *)frame, (uint8_t *)&ccid)) { print_text(COLOR_ERROR, " ccid:: invalid size"); goto done; } print_field(" ccid: %x", ccid); done: if (frame->size) print_hex_field(" Data", frame->data, frame->size); } static void call_content_control_id_read(const struct l2cap_frame *frame) { print_ccid(frame); } static void print_status_flag(const struct l2cap_frame *frame) { int16_t flag; if (!l2cap_frame_get_le16((void *)frame, (uint16_t *)&flag)) { print_text(COLOR_ERROR, " status flag:: invalid size"); goto done; } print_field(" status flag:"); if (flag & 0x1) print_field(" Inband Ringtone Enabled:"); else print_field(" Inband Ringtone Disabled:"); if (flag & 0x2) print_field(" Server in silent Mode"); else print_field(" Server Not in silent Mode"); done: if (frame->size) print_hex_field(" Data", frame->data, frame->size); } static void status_flag_read(const struct l2cap_frame *frame) { print_status_flag(frame); } static void status_flag_notify(const struct l2cap_frame *frame) { print_status_flag(frame); } static void print_target_uri(const struct l2cap_frame *frame) { char *name; uint8_t call_idx; if (!l2cap_frame_get_u8((void *)frame, (uint8_t *)&call_idx)) { print_text(COLOR_ERROR, " call_idx:: invalid size"); goto done; } print_field(" call_idx: %x", call_idx); name = name2utf8((uint8_t *)frame->data, frame->size); print_field(" Uri: %s", name); g_free(name); done: if (frame->size) print_hex_field(" Data", frame->data, frame->size); } static void incom_target_bearer_uri_read(const struct l2cap_frame *frame) { print_target_uri(frame); } static void incom_target_bearer_uri_notify(const struct l2cap_frame *frame) { print_target_uri(frame); } static void print_call_state(const struct l2cap_frame *frame) { uint8_t call_Index; uint8_t call_state; uint8_t call_flag; if (!l2cap_frame_get_u8((void *)frame, (uint8_t *)&call_Index)) { print_text(COLOR_ERROR, " call_Index:: invalid index"); goto done; } print_field(" call_Index: 0x%2.2x", call_Index); if (!l2cap_frame_get_u8((void *)frame, (uint8_t *)&call_state)) { print_text(COLOR_ERROR, " call_state:: invalid state"); goto done; } print_field(" call_state: 0x%2.2x", call_state); if (!l2cap_frame_get_u8((void *)frame, (uint8_t *)&call_flag)) { print_text(COLOR_ERROR, " call_flag:: invalid flag"); goto done; } print_field(" call_flag: 0x%2.2x", call_flag); done: if (frame->size) print_hex_field(" call_state Data", frame->data, frame->size); } static void call_state_read(const struct l2cap_frame *frame) { print_call_state(frame); } static void call_state_notify(const struct l2cap_frame *frame) { print_call_state(frame); } static void print_call_cp(const struct l2cap_frame *frame) { uint8_t opcode; uint8_t parameter; const char *str; char *name; if (!l2cap_frame_get_u8((void *)frame, (uint8_t *)&opcode)) { print_text(COLOR_ERROR, " opcode:: invalid size"); goto done; } print_field(" opcode: 0x%2.2x", opcode); switch (opcode) { case 0x00: str = "Accept"; if (!l2cap_frame_get_u8((void *)frame, (uint8_t *)¶meter)) { print_text(COLOR_ERROR, " parameter:: invalid size"); goto done; } print_field(" Operation: %s (0x%2.2x)", str, parameter); break; case 0x01: str = "Terminate"; if (!l2cap_frame_get_u8((void *)frame, (uint8_t *)¶meter)) { print_text(COLOR_ERROR, " parameter:: invalid size"); goto done; } print_field(" Operation: %s (0x%2.2x)", str, parameter); break; case 0x02: str = "Local Hold"; if (!l2cap_frame_get_u8((void *)frame, (uint8_t *)¶meter)) { print_text(COLOR_ERROR, " parameter:: invalid size"); goto done; } print_field(" Operation: %s (0x%2.2x)", str, parameter); break; case 0x03: str = "Local Retrieve"; if (!l2cap_frame_get_u8((void *)frame, (uint8_t *)¶meter)) { print_text(COLOR_ERROR, " parameter:: invalid size"); goto done; } print_field(" Operation: %s (0x%2.2x)", str, parameter); break; case 0x04: str = "Originate"; name = name2utf8((uint8_t *)frame->data, frame->size); print_field(" Operation: %s Uri: %s", str, name); g_free(name); break; case 0x05: str = "Join"; if (!l2cap_frame_get_u8((void *)frame, (uint8_t *)¶meter)) { print_text(COLOR_ERROR, " parameter:: invalid size"); goto done; } print_field(" Operation: %s (0x%2.2x)", str, parameter); break; default: str = "RFU"; print_field(" Operation: %s", str); break; } done: if (frame->size) print_hex_field("call_cp Data", frame->data, frame->size); } static void print_call_cp_notification(const struct l2cap_frame *frame) { uint8_t opcode; uint8_t result_code; const char *str; if (!l2cap_frame_get_u8((void *)frame, (uint8_t *)&opcode)) { print_text(COLOR_ERROR, " result_code:: invalid opcode"); goto done; } print_field(" opcode: 0x%2.2x", opcode); if (!l2cap_frame_get_u8((void *)frame, (uint8_t *)&result_code)) { print_text(COLOR_ERROR, " result_code:: invalid result_code"); goto done; } print_field(" result_code: 0x%2.2x", result_code); switch (result_code) { case 0x00: str = "SUCCESS"; break; case 0x01: str = "OPCODE NOT SUPPORTED"; break; case 0x02: str = "OPERATION NOT POSSIBLE"; break; case 0x03: str = "INVALID CALL INDEX"; break; case 0x04: str = "STATE MISMATCH"; break; case 0x05: str = "LACK OF RESOURCES"; break; case 0x06: str = "INVALID OUTGOING URI"; break; default: str = "RFU"; break; } print_field(" Status: %s", str); done: if (frame->size) print_hex_field(" call_cp Data", frame->data, frame->size); } static void call_cp_write(const struct l2cap_frame *frame) { print_call_cp(frame); } static void call_cp_notify(const struct l2cap_frame *frame) { print_call_cp_notification(frame); } static void print_call_cp_opt(const struct l2cap_frame *frame) { uint16_t operation; if (!l2cap_frame_get_le16((void *)frame, (uint16_t *)&operation)) { print_text(COLOR_ERROR, " status operation:: invalid size"); goto done; } print_field(" operation: 0x%2x", operation); if (operation & 0x1) { print_field(" Local Hold and Local Retrieve " "Call Control Point Opcodes supported"); } else { print_field(" Local Hold and Local Retrieve " "Call Control Point Opcodes not supported"); } if (operation & 0x2) print_field(" Join Call Control Point Opcode supported"); else print_field(" Join Call Control Point Opcode not supported"); done: if (frame->size) print_hex_field(" Data", frame->data, frame->size); } static void call_cp_opt_opcodes_read(const struct l2cap_frame *frame) { print_call_cp_opt(frame); } static void print_term_reason(const struct l2cap_frame *frame) { uint8_t call_id, reason; if (!l2cap_frame_get_u8((void *)frame, &call_id)) { print_text(COLOR_ERROR, "Call Index: invalid size"); goto done; } print_field(" call Index: %u", call_id); if (!l2cap_frame_get_u8((void *)frame, &reason)) { print_text(COLOR_ERROR, "Reason: invalid size"); goto done; } print_field(" Reason:"); switch (reason) { case 0x00: print_field(" Improper URI"); break; case 0x01: print_field(" Call Failed"); break; case 0x02: print_field(" Remote party ended the call"); break; case 0x03: print_field(" Server ended the call"); break; case 0x04: print_field(" Line was Busy"); break; case 0x05: print_field(" Network Congestion"); break; case 0x06: print_field(" Client terminated the call"); break; case 0x07: print_field(" No service"); break; case 0x08: print_field(" No answer"); break; case 0x09: print_field(" Unspecified"); break; default: print_field(" RFU"); break; } done: if (frame->size) print_hex_field(" Data", frame->data, frame->size); } static void call_termination_reason_notify(const struct l2cap_frame *frame) { print_term_reason(frame); } static void print_incom_call(const struct l2cap_frame *frame) { char *name; uint8_t call_id; if (!l2cap_frame_get_u8((void *)frame, &call_id)) { print_text(COLOR_ERROR, "Call Index: invalid size"); goto done; } print_field(" Call Index: %u", call_id); name = name2utf8((uint8_t *)frame->data, frame->size); print_field(" call_string: %s", name); g_free(name); done: if (frame->size) print_hex_field(" Data", frame->data, frame->size); } static void incoming_call_read(const struct l2cap_frame *frame) { print_incom_call(frame); } static void incoming_call_notify(const struct l2cap_frame *frame) { print_incom_call(frame); } static void print_call_friendly_name(const struct l2cap_frame *frame) { char *name; uint8_t call_id; if (!l2cap_frame_get_u8((void *)frame, &call_id)) { print_text(COLOR_ERROR, "Call Index: invalid size"); goto done; } print_field(" Call Index: %u", call_id); name = name2utf8((uint8_t *)frame->data, frame->size); print_field(" Friendly Name: %s", name); g_free(name); done: if (frame->size) print_hex_field(" Data", frame->data, frame->size); } static void call_friendly_name_read(const struct l2cap_frame *frame) { print_call_friendly_name(frame); } static void call_friendly_name_notify(const struct l2cap_frame *frame) { print_call_friendly_name(frame); } static const char *play_order_str(uint8_t order) { switch (order) { case 0x01: return "Single once"; case 0x02: return "Single repeat"; case 0x03: return "In order once"; case 0x04: return "In order repeat"; case 0x05: return "Oldest once"; case 0x06: return "Oldest repeat"; case 0x07: return "Newest once"; case 0x08: return "Newest repeat"; case 0x09: return "Shuffle once"; case 0x0A: return "Shuffle repeat"; default: return "RFU"; } } static void print_playing_order(const struct l2cap_frame *frame) { int8_t playing_order; if (!l2cap_frame_get_u8((void *)frame, (uint8_t *)&playing_order)) { print_text(COLOR_ERROR, " Playing Order: invalid size"); goto done; } print_field(" Playing Order: %s", play_order_str(playing_order)); done: if (frame->size) print_hex_field(" Data", frame->data, frame->size); } static void playing_order_read(const struct l2cap_frame *frame) { print_playing_order(frame); } static void playing_order_write(const struct l2cap_frame *frame) { print_playing_order(frame); } static void playing_order_notify(const struct l2cap_frame *frame) { print_playing_order(frame); } static const struct bitfield_data playing_orders_table[] = { { 0, "Single once (0x0001)" }, { 1, "Single repeat (0x0002)" }, { 2, "In order once (0x0004)" }, { 3, "In Order Repeat (0x0008)" }, { 4, "Oldest once (0x0010)" }, { 5, "Oldest repeat (0x0020)" }, { 6, "Newest once (0x0040)" }, { 7, "Newest repeat (0x0080)" }, { 8, "Shuffle once (0x0100)" }, { 9, "Shuffle repeat (0x0200)" }, { 10, "RFU (0x0400)" }, { 11, "RFU (0x0800)" }, { 12, "RFU (0x1000)" }, { 13, "RFU (0x2000)" }, { 14, "RFU (0x4000)" }, { 15, "RFU (0x8000)" }, { } }; static void print_playing_orders_supported(const struct l2cap_frame *frame) { uint16_t supported_orders; uint16_t mask; if (!l2cap_frame_get_le16((void *)frame, &supported_orders)) { print_text(COLOR_ERROR, " Supported Playing Orders: invalid size"); goto done; } print_field(" Supported Playing Orders: 0x%4.4x", supported_orders); mask = print_bitfield(8, supported_orders, playing_orders_table); if (mask) print_text(COLOR_WHITE_BG, " Unknown fields (0x%4.4x)", mask); done: if (frame->size) print_hex_field(" Data", frame->data, frame->size); } static void playing_orders_supported_read(const struct l2cap_frame *frame) { print_playing_orders_supported(frame); } static const char *media_state_str(uint8_t state) { switch (state) { case 0x00: return "Inactive"; case 0x01: return "Playing"; case 0x02: return "Paused"; case 0x03: return "Seeking"; default: return "RFU"; } } static void print_media_state(const struct l2cap_frame *frame) { int8_t state; if (!l2cap_frame_get_u8((void *)frame, (uint8_t *)&state)) { print_text(COLOR_ERROR, " Media State: invalid size"); goto done; } print_field(" Media State: %s", media_state_str(state)); done: if (frame->size) print_hex_field(" Data", frame->data, frame->size); } static void media_state_read(const struct l2cap_frame *frame) { print_media_state(frame); } static void media_state_notify(const struct l2cap_frame *frame) { print_media_state(frame); } static const struct media_cp_opcode { uint8_t opcode; const char *opcode_str; } media_cp_opcode_table[] = { {0x01, "Play"}, {0x02, "Pause"}, {0x03, "Fast Rewind"}, {0x04, "Fast Forward"}, {0x05, "Stop"}, {0x10, "Move Relative"}, {0x20, "Previous Segment"}, {0x21, "Next Segment"}, {0x22, "First Segment"}, {0x23, "Last Segment"}, {0x24, "Goto Segment"}, {0x30, "Previous Track"}, {0x31, "Next Track"}, {0x32, "First Track"}, {0x33, "Last Track"}, {0x34, "Goto Track"}, {0x40, "Previous Group"}, {0x41, "Next Group"}, {0x42, "First Group"}, {0x43, "Last Group"}, {0x44, "Goto Group"}, }; static const char *cp_opcode_str(uint8_t opcode) { size_t i; for (i = 0; i < ARRAY_SIZE(media_cp_opcode_table); i++) { const char *str = media_cp_opcode_table[i].opcode_str; if (opcode == media_cp_opcode_table[i].opcode) return str; } return "RFU"; } static void print_media_cp(const struct l2cap_frame *frame) { int8_t opcode; if (!l2cap_frame_get_u8((void *)frame, (uint8_t *)&opcode)) { print_text(COLOR_ERROR, " Media Control Point: invalid size"); goto done; } print_field(" Media Control Point: %s", cp_opcode_str(opcode)); done: if (frame->size) print_hex_field(" Data", frame->data, frame->size); } static void media_cp_write(const struct l2cap_frame *frame) { print_media_cp(frame); } static void media_cp_notify(const struct l2cap_frame *frame) { print_media_cp(frame); } static const struct bitfield_data supported_opcodes_table[] = { {0, "Play (0x00000001)" }, {1, "Pause (0x00000002)" }, {2, "Fast Rewind (0x00000004)" }, {3, "Fast Forward (0x00000008)" }, {4, "Stop (0x00000010)" }, {5, "Move Relative (0x00000020)" }, {6, "Previous Segment (0x00000040)" }, {7, "Next Segment (0x00000080)" }, {8, "First Segment (0x00000100)" }, {9, "Last Segment (0x00000200)" }, {10, "Goto Segment (0x00000400)" }, {11, "Previous Track (0x00000800)" }, {12, "Next Track (0x00001000)" }, {13, "First Track (0x00002000)" }, {14, "Last Track (0x00004000)" }, {15, "Goto Track (0x00008000)" }, {16, "Previous Group (0x00010000)" }, {17, "Next Group (0x00020000)" }, {18, "First Group (0x00040000)" }, {19, "Last Group (0x00080000)" }, {20, "Goto Group (0x00100000)" }, {21, "RFU (0x00200000)" }, {22, "RFU (0x00400000)" }, {23, "RFU (0x00800000)" }, {24, "RFU (0x01000000)" }, {25, "RFU (0x02000000)" }, {26, "RFU (0x04000000)" }, {27, "RFU (0x08000000)" }, {28, "RFU (0x10000000)" }, {29, "RFU (0x20000000)" }, {30, "RFU (0x40000000)" }, {31, "RFU (0x80000000)" }, { } }; static void print_media_cp_op_supported(const struct l2cap_frame *frame) { uint32_t supported_opcodes; uint32_t mask; if (!l2cap_frame_get_le32((void *)frame, &supported_opcodes)) { print_text(COLOR_ERROR, " value: invalid size"); goto done; } print_field(" Supported Opcodes: 0x%8.8x", supported_opcodes); mask = print_bitfield(8, supported_opcodes, supported_opcodes_table); if (mask) print_text(COLOR_WHITE_BG, " Unknown fields (0x%4.4x)", mask); done: if (frame->size) print_hex_field(" Data", frame->data, frame->size); } static void media_cp_op_supported_read(const struct l2cap_frame *frame) { print_media_cp_op_supported(frame); } static void media_cp_op_supported_notify(const struct l2cap_frame *frame) { print_media_cp_op_supported(frame); } static void print_content_control_id(const struct l2cap_frame *frame) { int8_t ccid; if (!l2cap_frame_get_u8((void *)frame, (uint8_t *)&ccid)) { print_text(COLOR_ERROR, " Content Control ID: invalid size"); goto done; } print_field(" Content Control ID: 0x%2.2x", ccid); done: if (frame->size) print_hex_field(" Data", frame->data, frame->size); } static void content_control_id_read(const struct l2cap_frame *frame) { print_content_control_id(frame); } static const struct pa_sync_state_decoder { uint8_t code; const char *value; } pa_sync_state_decoders[] = { { 0x00, "Not synchronized to PA" }, { 0x01, "SyncInfo Request" }, { 0x02, "Synchronized to PA" }, { 0x03, "Failed to synchronize to PA" }, { 0x04, "No PAST" }, }; static const struct cp_pa_sync_state_decoder { uint8_t code; const char *value; } cp_pa_sync_state_decoders[] = { { 0x00, "Do not synchronize to PA" }, { 0x01, "Synchronize to PA - PAST available" }, { 0x02, "Synchronize to PA - PAST not available" }, }; static const struct big_enc_decoder { uint8_t code; const char *value; } big_enc_decoders[] = { { 0x00, "Not encrypted" }, { 0x01, "Broadcast_Code required" }, { 0x02, "Decrypting" }, { 0x03, "Bad_Code (incorrect encryption key)" }, }; static bool print_subgroup_lv(const struct l2cap_frame *frame, const char *label, const struct util_ltv_debugger *debugger, size_t debugger_len) { struct bt_hci_lv_data *lv; lv = l2cap_frame_pull((void *)frame, frame, sizeof(*lv)); if (!lv) { print_text(COLOR_ERROR, "%s: invalid size", label); return false; } if (!l2cap_frame_pull((void *)frame, frame, lv->len)) { print_text(COLOR_ERROR, "%s: invalid size", label); return false; } util_debug_ltv(lv->data, lv->len, debugger, debugger_len, print_ltv, (void *)label); return true; } static bool print_subgroup_metadata(const char *label, const struct l2cap_frame *frame) { return print_subgroup_lv(frame, label, NULL, 0); } static void print_bcast_recv_state(const struct l2cap_frame *frame) { uint8_t i; uint8_t id; uint8_t addr_type; uint8_t *addr; uint8_t sid; uint32_t bid; uint8_t pa_sync_state; uint8_t enc; uint8_t *bad_code; uint8_t num_subgroups = 0; uint32_t bis_sync_state; if (frame->size == 0) { print_field(" Empty characteristic"); goto done; } if (!l2cap_frame_get_u8((void *)frame, &id)) { print_text(COLOR_ERROR, "Source_ID: invalid size"); goto done; } print_field(" Source_ID: %u", id); if (!l2cap_frame_get_u8((void *)frame, &addr_type)) { print_text(COLOR_ERROR, "Source_Address_Type: invalid size"); goto done; } print_field(" Source_Address_Type: %u", addr_type); addr = l2cap_frame_pull((void *)frame, frame, sizeof(bdaddr_t)); if (!addr) { print_text(COLOR_ERROR, "Source_Address: invalid size"); goto done; } print_field(" Source_Address: %2.2X:%2.2X:%2.2X:%2.2X:%2.2X:%2.2X", addr[5], addr[4], addr[3], addr[2], addr[1], addr[0]); if (!l2cap_frame_get_u8((void *)frame, &sid)) { print_text(COLOR_ERROR, "Source_Adv_SID: invalid size"); goto done; } print_field(" Source_Adv_SID: %u", sid); if (!l2cap_frame_get_le24((void *)frame, &bid)) { print_text(COLOR_ERROR, "Broadcast_ID: invalid size"); goto done; } print_field(" Broadcast_ID: 0x%06x", bid); if (!l2cap_frame_get_u8((void *)frame, &pa_sync_state)) { print_text(COLOR_ERROR, "PA_Sync_State: invalid size"); goto done; } for (i = 0; i < ARRAY_SIZE(pa_sync_state_decoders); i++) { const struct pa_sync_state_decoder *decoder; decoder = &pa_sync_state_decoders[i]; if (decoder->code == pa_sync_state) { print_field(" PA_Sync_State: %s", decoder->value); break; } } if (i == ARRAY_SIZE(pa_sync_state_decoders)) print_field(" PA_Sync_State: %s", "Invalid value"); if (!l2cap_frame_get_u8((void *)frame, &enc)) { print_text(COLOR_ERROR, "BIG_Encryption: invalid size"); goto done; } for (i = 0; i < ARRAY_SIZE(big_enc_decoders); i++) { const struct big_enc_decoder *decoder; decoder = &big_enc_decoders[i]; if (decoder->code == enc) { print_field(" BIG_Encryption: %s", decoder->value); break; } } if (i == ARRAY_SIZE(big_enc_decoders)) print_field(" BIG_Encryption: %s", "Invalid value"); if (enc == 0x03) { bad_code = l2cap_frame_pull((void *)frame, frame, 16); if (!bad_code) { print_text(COLOR_ERROR, "Bad_Code: invalid size"); goto done; } print_hex_field(" Bad_Code", bad_code, 16); } if (!l2cap_frame_get_u8((void *)frame, &num_subgroups)) { print_text(COLOR_ERROR, "Num_Subgroups: invalid size"); goto done; } print_field(" Num_Subgroups: %u", num_subgroups); for (i = 0; i < num_subgroups; i++) { print_field(" Subgroup #%u:", i); if (!l2cap_frame_get_le32((void *)frame, &bis_sync_state)) { print_text(COLOR_ERROR, "BIS_Sync State: invalid size"); goto done; } print_field(" BIS_Sync State: 0x%8.8x", bis_sync_state); if (!print_subgroup_metadata(" Metadata", frame)) goto done; } done: if (frame->size) print_hex_field(" Data", frame->data, frame->size); } static void bcast_recv_state_read(const struct l2cap_frame *frame) { print_bcast_recv_state(frame); } static void bcast_recv_state_notify(const struct l2cap_frame *frame) { print_bcast_recv_state(frame); } #define BCAST_AUDIO_SCAN_CP_CMD(_op, _desc, _func) \ [_op] = { \ .desc = _desc, \ .func = _func, \ } static void bcast_audio_scan_cp_add_src_cmd(const struct l2cap_frame *frame) { uint8_t i; uint8_t addr_type; uint8_t *addr; uint8_t sid; uint32_t bid; uint8_t pa_sync_state; uint16_t pa_interval; uint8_t num_subgroups = 0; uint32_t bis_sync_state; if (!l2cap_frame_get_u8((void *)frame, &addr_type)) { print_text(COLOR_ERROR, "Source_Address_Type: invalid size"); return; } print_field(" Source_Address_Type: %u", addr_type); addr = l2cap_frame_pull((void *)frame, frame, sizeof(bdaddr_t)); if (!addr) { print_text(COLOR_ERROR, "Source_Address: invalid size"); return; } print_field(" Source_Address: %2.2X:%2.2X:%2.2X:%2.2X:%2.2X:%2.2X", addr[5], addr[4], addr[3], addr[2], addr[1], addr[0]); if (!l2cap_frame_get_u8((void *)frame, &sid)) { print_text(COLOR_ERROR, "Source_Adv_SID: invalid size"); return; } print_field(" Source_Adv_SID: %u", sid); if (!l2cap_frame_get_le24((void *)frame, &bid)) { print_text(COLOR_ERROR, "Broadcast_ID: invalid size"); return; } print_field(" Broadcast_ID: 0x%06x", bid); if (!l2cap_frame_get_u8((void *)frame, &pa_sync_state)) { print_text(COLOR_ERROR, "PA_Sync_State: invalid size"); return; } for (i = 0; i < ARRAY_SIZE(cp_pa_sync_state_decoders); i++) { const struct cp_pa_sync_state_decoder *decoder; decoder = &cp_pa_sync_state_decoders[i]; if (decoder->code == pa_sync_state) { print_field(" PA_Sync_State: %s", decoder->value); break; } } if (i == ARRAY_SIZE(cp_pa_sync_state_decoders)) print_field(" PA_Sync_State: %s", "Invalid value"); if (!l2cap_frame_get_le16((void *)frame, &pa_interval)) { print_text(COLOR_ERROR, "PA_Interval: invalid size"); return; } print_field(" PA_Interval: 0x%04x", pa_interval); if (!l2cap_frame_get_u8((void *)frame, &num_subgroups)) { print_text(COLOR_ERROR, "Num_Subgroups: invalid size"); return; } print_field(" Num_Subgroups: %u", num_subgroups); for (i = 0; i < num_subgroups; i++) { print_field(" Subgroup #%u:", i); if (!l2cap_frame_get_le32((void *)frame, &bis_sync_state)) { print_text(COLOR_ERROR, "BIS_Sync State: invalid size"); return; } print_field(" BIS_Sync State: 0x%8.8x", bis_sync_state); if (!print_subgroup_metadata(" Metadata", frame)) return; } } static void bcast_audio_scan_cp_mod_src_cmd(const struct l2cap_frame *frame) { uint8_t i; uint8_t id; uint8_t pa_sync_state; uint16_t pa_interval; uint8_t num_subgroups = 0; uint32_t bis_sync_state; if (!l2cap_frame_get_u8((void *)frame, &id)) { print_text(COLOR_ERROR, "Source_ID: invalid size"); return; } print_field(" Source_ID: %u", id); if (!l2cap_frame_get_u8((void *)frame, &pa_sync_state)) { print_text(COLOR_ERROR, "PA_Sync_State: invalid size"); return; } for (i = 0; i < ARRAY_SIZE(cp_pa_sync_state_decoders); i++) { const struct cp_pa_sync_state_decoder *decoder; decoder = &cp_pa_sync_state_decoders[i]; if (decoder->code == pa_sync_state) { print_field(" PA_Sync_State: %s", decoder->value); break; } } if (i == ARRAY_SIZE(cp_pa_sync_state_decoders)) print_field(" PA_Sync_State: %s", "Invalid value"); if (!l2cap_frame_get_le16((void *)frame, &pa_interval)) { print_text(COLOR_ERROR, "PA_Interval: invalid size"); return; } print_field(" PA_Interval: 0x%04x", pa_interval); if (!l2cap_frame_get_u8((void *)frame, &num_subgroups)) { print_text(COLOR_ERROR, "Num_Subgroups: invalid size"); return; } print_field(" Num_Subgroups: %u", num_subgroups); for (i = 0; i < num_subgroups; i++) { print_field(" Subgroup #%u:", i); if (!l2cap_frame_get_le32((void *)frame, &bis_sync_state)) { print_text(COLOR_ERROR, "BIS_Sync State: invalid size"); return; } print_field(" BIS_Sync State: 0x%8.8x", bis_sync_state); if (!print_subgroup_metadata(" Metadata", frame)) return; } } static void bcast_audio_scan_cp_set_bcode_cmd(const struct l2cap_frame *frame) { uint8_t id; uint8_t *bcast_code; if (!l2cap_frame_get_u8((void *)frame, &id)) { print_text(COLOR_ERROR, "Source_ID: invalid size"); return; } print_field(" Source_ID: %u", id); bcast_code = l2cap_frame_pull((void *)frame, frame, 16); if (!bcast_code) { print_text(COLOR_ERROR, "Broadcast_Code: invalid size"); return; } print_hex_field(" Broadcast_Code", bcast_code, 16); } static void bcast_audio_scan_cp_remove_src_cmd(const struct l2cap_frame *frame) { uint8_t id; if (!l2cap_frame_get_u8((void *)frame, &id)) { print_text(COLOR_ERROR, "Source_ID: invalid size"); return; } print_field(" Source_ID: %u", id); } static const struct bcast_audio_scan_cp_cmd { const char *desc; void (*func)(const struct l2cap_frame *frame); } bcast_audio_scan_cp_cmd_table[] = { /* Opcode = 0x00 (Remote Scan Stopped) */ BCAST_AUDIO_SCAN_CP_CMD(0x00, "Remote Scan Stopped", NULL), /* Opcode = 0x01 (Remote Scan Started) */ BCAST_AUDIO_SCAN_CP_CMD(0x01, "Remote Scan Started", NULL), /* Opcode = 0x02 (Add Source) */ BCAST_AUDIO_SCAN_CP_CMD(0x02, "Add Source", bcast_audio_scan_cp_add_src_cmd), /* Opcode = 0x03 (Modify Source) */ BCAST_AUDIO_SCAN_CP_CMD(0x03, "Modify Source", bcast_audio_scan_cp_mod_src_cmd), /* Opcode = 0x04 (Set Broadcast_Code) */ BCAST_AUDIO_SCAN_CP_CMD(0x04, "Set Broadcast_Code", bcast_audio_scan_cp_set_bcode_cmd), /* Opcode = 0x05 (Remove Source) */ BCAST_AUDIO_SCAN_CP_CMD(0x05, "Remove Source", bcast_audio_scan_cp_remove_src_cmd), }; static const struct bcast_audio_scan_cp_cmd * bcast_audio_scan_cp_get_cmd(uint8_t op) { if (op > ARRAY_SIZE(bcast_audio_scan_cp_cmd_table)) return NULL; return &bcast_audio_scan_cp_cmd_table[op]; } static void print_bcast_audio_scan_cp_cmd(const struct l2cap_frame *frame) { uint8_t op; const struct bcast_audio_scan_cp_cmd *cmd; if (!l2cap_frame_get_u8((void *)frame, &op)) { print_text(COLOR_ERROR, "Opcode: invalid size"); goto done; } cmd = bcast_audio_scan_cp_get_cmd(op); if (!cmd) { print_field(" Opcode: Reserved (0x%2.2x)", op); goto done; } print_field(" Opcode: %s (0x%2.2x)", cmd->desc, op); if (cmd->func) cmd->func(frame); done: if (frame->size) print_hex_field(" Data", frame->data, frame->size); } static void bcast_audio_scan_cp_write(const struct l2cap_frame *frame) { print_bcast_audio_scan_cp_cmd(frame); } static const struct bitfield_data gmap_role_table[] = { { 0, "Unicast Game Gateway (UGG) (0x0001)" }, { 1, "Unicast Game Terminal (UGT) (0x0002)" }, { 2, "Broadcast Game Sender (BGS) (0x0004)" }, { 3, "Broadcast Game Receiver (BGR) (0x0008)" }, { } }; static void gmap_role_read(const struct l2cap_frame *frame) { uint8_t role; uint8_t mask; if (!l2cap_frame_get_u8((void *)frame, &role)) { print_text(COLOR_ERROR, " invalid size"); return; } print_field(" Role: 0x%2.2x", role); mask = print_bitfield(6, role, gmap_role_table); if (mask) print_text(COLOR_WHITE_BG, " Unknown fields (0x%2.2x)", mask); } static const struct bitfield_data ugg_features_table[] = { { 0, "UGG Multiplex (0x0001)" }, { 1, "UGG 96 kbps Source (0x0002)" }, { 2, "UGG Multilink (0x0004)" }, { } }; static void ugg_features_read(const struct l2cap_frame *frame) { uint8_t value; uint8_t mask; if (!l2cap_frame_get_u8((void *)frame, &value)) { print_text(COLOR_ERROR, " invalid size"); return; } print_field(" Value: 0x%2.2x", value); mask = print_bitfield(6, value, ugg_features_table); if (mask) print_text(COLOR_WHITE_BG, " Unknown fields (0x%2.2x)", mask); } static const struct bitfield_data ugt_features_table[] = { { 0, "UGT Source (0x0001)" }, { 1, "UGT 80 kbps Source (0x0002)" }, { 2, "UGT Sink (0x0004)" }, { 3, "UGT 64 kbps Sink (0x0008)" }, { 4, "UGT Multiplex (0x0010)" }, { 5, "UGT Multisink (0x0020)" }, { 6, "UGT Multisource (0x0040)" }, { } }; static void ugt_features_read(const struct l2cap_frame *frame) { uint8_t value; uint8_t mask; if (!l2cap_frame_get_u8((void *)frame, &value)) { print_text(COLOR_ERROR, " invalid size"); return; } print_field(" Value: 0x%2.2x", value); mask = print_bitfield(6, value, ugt_features_table); if (mask) print_text(COLOR_WHITE_BG, " Unknown fields (0x%2.2x)", mask); } static const struct bitfield_data bgs_features_table[] = { { 0, "BGS 96 kbps (0x0001)" }, { } }; static void bgs_features_read(const struct l2cap_frame *frame) { uint8_t value; uint8_t mask; if (!l2cap_frame_get_u8((void *)frame, &value)) { print_text(COLOR_ERROR, " invalid size"); return; } print_field(" Value: 0x%2.2x", value); mask = print_bitfield(6, value, bgs_features_table); if (mask) print_text(COLOR_WHITE_BG, " Unknown fields (0x%2.2x)", mask); } static const struct bitfield_data bgr_features_table[] = { { 0, "BGR Multisink (0x0001)" }, { 1, "BGR Multiplex (0x0002)" }, { } }; static void bgr_features_read(const struct l2cap_frame *frame) { uint8_t value; uint8_t mask; if (!l2cap_frame_get_u8((void *)frame, &value)) { print_text(COLOR_ERROR, " invalid size"); return; } print_field(" Value: 0x%2.2x", value); mask = print_bitfield(6, value, bgr_features_table); if (mask) print_text(COLOR_WHITE_BG, " Unknown fields (0x%2.2x)", mask); } #define GMAS \ GATT_HANDLER(0x2c00, gmap_role_read, NULL, NULL), \ GATT_HANDLER(0x2c01, ugg_features_read, NULL, NULL), \ GATT_HANDLER(0x2c02, ugt_features_read, NULL, NULL), \ GATT_HANDLER(0x2c02, bgs_features_read, NULL, NULL), \ GATT_HANDLER(0x2c03, bgr_features_read, NULL, NULL) #define GATT_HANDLER(_uuid, _read, _write, _notify) \ { \ .uuid = { \ .type = BT_UUID16, \ .value.u16 = _uuid, \ }, \ .read = _read, \ .write = _write, \ .notify = _notify \ } static const struct gatt_handler { bt_uuid_t uuid; void (*read)(const struct l2cap_frame *frame); void (*write)(const struct l2cap_frame *frame); void (*notify)(const struct l2cap_frame *frame); } gatt_handlers[] = { GATT_HANDLER(0x2800, pri_svc_read, NULL, NULL), GATT_HANDLER(0x2801, sec_svc_read, NULL, NULL), GATT_HANDLER(0x2803, chrc_read, NULL, NULL), GATT_HANDLER(0x2902, ccc_read, ccc_write, NULL), GATT_HANDLER(0x2bc4, ase_read, NULL, ase_notify), GATT_HANDLER(0x2bc5, ase_read, NULL, ase_notify), GATT_HANDLER(0x2bc6, NULL, ase_cp_write, ase_cp_notify), GATT_HANDLER(0x2bc9, pac_read, NULL, pac_notify), GATT_HANDLER(0x2bca, pac_loc_read, NULL, pac_loc_notify), GATT_HANDLER(0x2bcb, pac_read, NULL, pac_notify), GATT_HANDLER(0x2bcc, pac_loc_read, NULL, pac_loc_notify), GATT_HANDLER(0x2bcd, pac_context_read, NULL, pac_context_notify), GATT_HANDLER(0x2bce, pac_context_read, NULL, pac_context_notify), GATT_HANDLER(0x2b7d, vol_state_read, NULL, vol_state_notify), GATT_HANDLER(0x2b7e, NULL, vol_cp_write, NULL), GATT_HANDLER(0x2b7f, vol_flag_read, NULL, vol_flag_notify), GATT_HANDLER(0x2b84, csip_sirk_read, NULL, csip_sirk_notify), GATT_HANDLER(0x2b85, csip_size_read, NULL, csip_size_notify), GATT_HANDLER(0x2b86, csip_lock_read, NULL, NULL), GATT_HANDLER(0x2b87, csip_rank_read, NULL, NULL), GATT_HANDLER(0x2b93, mp_name_read, NULL, mp_name_notify), GATT_HANDLER(0x2b96, NULL, NULL, track_changed_notify), GATT_HANDLER(0x2b97, track_title_read, NULL, track_title_notify), GATT_HANDLER(0x2b98, track_duration_read, NULL, track_duration_notify), GATT_HANDLER(0x2b99, track_position_read, track_position_write, track_position_notify), GATT_HANDLER(0x2b9a, playback_speed_read, playback_speed_write, playback_speed_notify), GATT_HANDLER(0x2b9b, seeking_speed_read, NULL, seeking_speed_notify), GATT_HANDLER(0x2ba1, playing_order_read, playing_order_write, playing_order_notify), GATT_HANDLER(0x2ba2, playing_orders_supported_read, NULL, NULL), GATT_HANDLER(0x2ba3, media_state_read, NULL, media_state_notify), GATT_HANDLER(0x2ba4, NULL, media_cp_write, media_cp_notify), GATT_HANDLER(0x2ba5, media_cp_op_supported_read, NULL, media_cp_op_supported_notify), GATT_HANDLER(0x2bba, content_control_id_read, NULL, NULL), GATT_HANDLER(0x2bc7, NULL, bcast_audio_scan_cp_write, NULL), GATT_HANDLER(0x2bc8, bcast_recv_state_read, NULL, bcast_recv_state_notify), GATT_HANDLER(0x2bb3, bearer_name_read, NULL, bearer_name_notify), GATT_HANDLER(0x2bb4, bearer_uci_read, NULL, NULL), GATT_HANDLER(0x2bb5, bearer_technology_read, NULL, bearer_technology_notify), GATT_HANDLER(0x2bb6, bearer_uri_schemes_list_read, NULL, NULL), GATT_HANDLER(0x2bb7, bearer_signal_strength_read, NULL, bearer_signal_strength_notify), GATT_HANDLER(0x2bb8, bearer_signal_strength_rep_intrvl_read, bearer_signal_strength_rep_intrvl_write, NULL), GATT_HANDLER(0x2bb9, bearer_current_call_list_read, NULL, bearer_current_call_list_notify), GATT_HANDLER(0x2bba, call_content_control_id_read, NULL, NULL), GATT_HANDLER(0x2bbb, status_flag_read, NULL, status_flag_notify), GATT_HANDLER(0x2bbc, incom_target_bearer_uri_read, NULL, incom_target_bearer_uri_notify), GATT_HANDLER(0x2bbd, call_state_read, NULL, call_state_notify), GATT_HANDLER(0x2bbe, NULL, call_cp_write, call_cp_notify), GATT_HANDLER(0x2bbf, call_cp_opt_opcodes_read, NULL, NULL), GATT_HANDLER(0x2bc0, NULL, NULL, call_termination_reason_notify), GATT_HANDLER(0x2bc1, incoming_call_read, NULL, incoming_call_notify), GATT_HANDLER(0x2bc2, call_friendly_name_read, NULL, call_friendly_name_notify), GMAS }; static const struct gatt_handler *get_handler_uuid(const bt_uuid_t *uuid) { size_t i; if (!uuid) return NULL; for (i = 0; i < ARRAY_SIZE(gatt_handlers); i++) { const struct gatt_handler *handler = &gatt_handlers[i]; if (!bt_uuid_cmp(&handler->uuid, uuid)) return handler; } return NULL; } static const struct gatt_handler *get_handler(struct gatt_db_attribute *attr) { return get_handler_uuid(gatt_db_attribute_get_type(attr)); } static void att_exchange_mtu_req(const struct l2cap_frame *frame) { const struct bt_l2cap_att_exchange_mtu_req *pdu = frame->data; print_field("Client RX MTU: %d", le16_to_cpu(pdu->mtu)); } static void att_exchange_mtu_rsp(const struct l2cap_frame *frame) { struct packet_conn_data *conn; struct att_conn_data *data; uint16_t mtu; if (!l2cap_frame_get_le16((void *)frame, &mtu)) { print_text(COLOR_ERROR, " invalid size"); return; } print_field("Server RX MTU: %d", mtu); conn = packet_get_conn_data(frame->handle); data = att_get_conn_data(conn); if (!data) return; data->mtu = mtu; } static void att_find_info_req(const struct l2cap_frame *frame) { print_handle_range("Handle range", frame->data); } static const char *att_format_str(uint8_t format) { switch (format) { case 0x01: return "UUID-16"; case 0x02: return "UUID-128"; default: return "unknown"; } } static struct gatt_db_attribute *insert_desc(const struct l2cap_frame *frame, uint16_t handle, bt_uuid_t *uuid, bool rsp) { struct gatt_db *db; db = get_db(frame, rsp); if (!db) return NULL; return gatt_db_insert_descriptor(db, handle, uuid, 0, NULL, NULL, NULL); } static void att_find_info_rsp_16(const struct l2cap_frame *frame) { while (frame->size >= 4) { uint16_t handle; uint16_t u16; bt_uuid_t uuid; if (!l2cap_frame_get_le16((void *)frame, &handle)) { print_text(COLOR_ERROR, " Handle: invalid size"); return; } if (!l2cap_frame_get_le16((void *)frame, &u16)) { print_text(COLOR_ERROR, " UUID: invalid size"); return; } print_field("Handle: 0x%4.4x", handle); print_uuid("UUID", &u16, 2); bt_uuid16_create(&uuid, u16); insert_desc(frame, handle, &uuid, true); } } static void att_find_info_rsp_128(const struct l2cap_frame *frame) { while (frame->size >= 18) { uint16_t handle; bt_uuid_t uuid; if (!l2cap_frame_get_le16((void *)frame, &handle)) { print_text(COLOR_ERROR, " Handle: invalid size"); return; } if (frame->size < 16) { print_text(COLOR_ERROR, " UUID: invalid size"); return; } print_field("Handle: 0x%4.4x", handle); print_uuid("UUID", frame->data, 16); bt_uuid_from_data(&uuid, frame->data, 16); if (!l2cap_frame_pull((void *)frame, frame, 16)) return; insert_desc(frame, handle, &uuid, true); } } static void att_find_info_rsp(const struct l2cap_frame *frame) { uint8_t format; if (!l2cap_frame_get_u8((void *)frame, &format)) { print_text(COLOR_ERROR, " Format: invalid size"); goto done; } print_field("Format: %s (0x%2.2x)", att_format_str(format), format); switch (format) { case 0x01: att_find_info_rsp_16(frame); break; case 0x02: att_find_info_rsp_128(frame); break; } done: if (frame->size) packet_hexdump(frame->data, frame->size); } static void att_find_by_type_val_req(const struct l2cap_frame *frame) { uint16_t type; print_handle_range("Handle range", frame->data); type = get_le16(frame->data + 4); print_attribute_info(type, frame->data + 6, frame->size - 6); } static void att_find_by_type_val_rsp(const struct l2cap_frame *frame) { const uint8_t *ptr = frame->data; uint16_t len = frame->size; while (len >= 4) { print_handle_range("Handle range", ptr); ptr += 4; len -= 4; } packet_hexdump(ptr, len); } static struct gatt_db_attribute *get_attribute(const struct l2cap_frame *frame, uint16_t handle, bool rsp) { struct gatt_db *db; db = get_db(frame, rsp); if (!db) return NULL; return gatt_db_get_attribute(db, handle); } static void queue_read(const struct l2cap_frame *frame, bt_uuid_t *uuid, uint16_t handle) { struct packet_conn_data *conn; struct att_conn_data *data; struct att_read *read; struct gatt_db_attribute *attr = NULL; const struct gatt_handler *handler; if (handle) { attr = get_attribute(frame, handle, false); if (!attr) return; } handler = attr ? get_handler(attr) : get_handler_uuid(uuid); conn = packet_get_conn_data(frame->handle); data = att_get_conn_data(conn); if (!data) return; if (!data->reads) data->reads = queue_new(); read = new0(struct att_read, 1); read->conn = data; read->attr = attr; read->in = frame->in; read->chan = frame->chan; read->func = handler ? handler->read : NULL; queue_push_tail(data->reads, read); } static void att_read_type_req(const struct l2cap_frame *frame) { bt_uuid_t uuid; print_handle_range("Handle range", frame->data); print_uuid("Attribute type", frame->data + 4, frame->size - 4); if (bt_uuid_from_data(&uuid, frame->data + 4, frame->size - 4)) return; queue_read(frame, &uuid, 0x0000); } static void att_read_type_rsp(const struct l2cap_frame *frame) { uint8_t len; if (!l2cap_frame_get_u8((void *)frame, &len)) { print_text(COLOR_ERROR, "invalid size"); return; } print_field("Attribute data length: %d", len); print_data_list("Attribute data list", len, frame); } static void print_handle(const struct l2cap_frame *frame, uint16_t handle, bool rsp) { struct gatt_db_attribute *attr; attr = get_attribute(frame, handle, rsp); if (!attr) { print_field("Handle: 0x%4.4x", handle); return; } print_attribute(attr); } static void att_read_req(const struct l2cap_frame *frame) { const struct bt_l2cap_att_read_req *pdu = frame->data; uint16_t handle; l2cap_frame_pull((void *)frame, frame, sizeof(*pdu)); handle = le16_to_cpu(pdu->handle); print_handle(frame, handle, false); queue_read(frame, NULL, handle); } static void att_read_append(struct att_read *read, const struct l2cap_frame *frame) { if (!read->iov) read->iov = new0(struct iovec, 1); util_iov_append(read->iov, frame->data, frame->size); } static void att_read_func(struct att_read *read, const struct l2cap_frame *frame) { att_read_append(read, frame); print_attribute(read->attr); print_hex_field("Value", read->iov->iov_base, read->iov->iov_len); if (read->func) { struct l2cap_frame f = *frame; f.data = read->iov->iov_base; f.size = read->iov->iov_len; read->func(&f); } att_read_free(read); } static void att_read_rsp(const struct l2cap_frame *frame) { struct att_read *read; print_hex_field("Value", frame->data, frame->size); read = att_get_read(frame); if (!read) return; /* Check if the data size is equal to the MTU then read long procedure * maybe used. */ if (frame->size == read->conn->mtu - 1) { att_read_append(read, frame); print_hex_field("Long Value", read->iov->iov_base, read->iov->iov_len); queue_push_head(read->conn->reads, read); return; } att_read_func(read, frame); } static void att_read_blob_req(const struct l2cap_frame *frame) { uint16_t handle, offset; struct att_read *read; if (!l2cap_frame_get_le16((void *)frame, &handle)) { print_text(COLOR_ERROR, "invalid size"); return; } if (!l2cap_frame_get_le16((void *)frame, &offset)) { print_text(COLOR_ERROR, "invalid size"); return; } print_handle(frame, handle, false); print_field("Offset: 0x%4.4x", offset); read = att_get_read(frame); if (!read) return; /* Check if attribute handle and offset match so the read object shall * be keeped. */ if (gatt_db_attribute_get_handle(read->attr) == handle && offset == read->iov->iov_len) { queue_push_head(read->conn->reads, read); return; } att_read_func(read, frame); } static void att_read_blob_rsp(const struct l2cap_frame *frame) { att_read_rsp(frame); } static void att_read_multiple_req(const struct l2cap_frame *frame) { int i, count; count = frame->size / 2; for (i = 0; i < count; i++) print_handle(frame, get_le16(frame->data + (i * 2)), false); } static void att_read_group_type_req(const struct l2cap_frame *frame) { bt_uuid_t uuid; print_handle_range("Handle range", frame->data); print_uuid("Attribute group type", frame->data + 4, frame->size - 4); if (bt_uuid_from_data(&uuid, frame->data + 4, frame->size - 4)) return; queue_read(frame, &uuid, 0x0000); } static void print_group_list(const char *label, uint8_t length, const struct l2cap_frame *frame) { struct att_read *read; uint8_t count; if (length == 0) return; read = att_get_read(frame); count = frame->size / length; print_field("%s: %u entr%s", label, count, count == 1 ? "y" : "ies"); while (frame->size >= length) { print_handle_range("Handle range", frame->data); print_uuid("UUID", frame->data + 4, length - 4); if (read && read->func) { struct l2cap_frame f; l2cap_frame_clone_size(&f, frame, length); read->func(&f); } if (!l2cap_frame_pull((void *)frame, frame, length)) break; } packet_hexdump(frame->data, frame->size); att_read_free(read); } static void att_read_group_type_rsp(const struct l2cap_frame *frame) { const struct bt_l2cap_att_read_group_type_rsp *pdu = frame->data; l2cap_frame_pull((void *)frame, frame, sizeof(*pdu)); print_field("Attribute data length: %d", pdu->length); print_group_list("Attribute group list", pdu->length, frame); } static void print_write(const struct l2cap_frame *frame, uint16_t handle, size_t len) { struct gatt_db_attribute *attr; const struct gatt_handler *handler; print_handle(frame, handle, false); if (len > frame->size) { print_text(COLOR_ERROR, "invalid size"); return; } print_hex_field(" Data", frame->data, len); attr = get_attribute(frame, handle, false); if (!attr) return; handler = get_handler(attr); if (!handler || !handler->write) return; handler->write(frame); } static void att_write_req(const struct l2cap_frame *frame) { uint16_t handle; if (!l2cap_frame_get_le16((void *)frame, &handle)) { print_text(COLOR_ERROR, "invalid size"); return; } print_write(frame, handle, frame->size); } static void att_write_rsp(const struct l2cap_frame *frame) { } static void att_prepare_write_req(const struct l2cap_frame *frame) { print_handle(frame, get_le16(frame->data), false); print_field("Offset: 0x%4.4x", get_le16(frame->data + 2)); print_hex_field(" Data", frame->data + 4, frame->size - 4); } static void att_prepare_write_rsp(const struct l2cap_frame *frame) { print_handle(frame, get_le16(frame->data), true); print_field("Offset: 0x%4.4x", get_le16(frame->data + 2)); print_hex_field(" Data", frame->data + 4, frame->size - 4); } static void att_execute_write_req(const struct l2cap_frame *frame) { uint8_t flags = *(uint8_t *) frame->data; const char *flags_str; switch (flags) { case 0x00: flags_str = "Cancel all prepared writes"; break; case 0x01: flags_str = "Immediately write all pending values"; break; default: flags_str = "Unknown"; break; } print_field("Flags: %s (0x%02x)", flags_str, flags); } static void print_notify(const struct l2cap_frame *frame, uint16_t handle, size_t len) { struct gatt_db_attribute *attr; const struct gatt_handler *handler; struct l2cap_frame clone; print_handle(frame, handle, true); print_hex_field(" Data", frame->data, len); if (len > frame->size) { print_text(COLOR_ERROR, "invalid size"); return; } attr = get_attribute(frame, handle, true); if (!attr) return; handler = get_handler(attr); if (!handler) return; /* Use a clone if the callback is not expected to parse the whole * frame. */ if (len != frame->size) { l2cap_frame_clone(&clone, frame); clone.size = len; frame = &clone; } if (handler->notify) handler->notify(frame); } static void att_handle_value_notify(const struct l2cap_frame *frame) { uint16_t handle; const struct bt_l2cap_att_handle_value_notify *pdu = frame->data; l2cap_frame_pull((void *)frame, frame, sizeof(*pdu)); handle = le16_to_cpu(pdu->handle); print_notify(frame, handle, frame->size); } static void att_handle_value_ind(const struct l2cap_frame *frame) { const struct bt_l2cap_att_handle_value_ind *pdu = frame->data; l2cap_frame_pull((void *)frame, frame, sizeof(*pdu)); print_notify(frame, le16_to_cpu(pdu->handle), frame->size); } static void att_handle_value_conf(const struct l2cap_frame *frame) { } static void att_multiple_vl_rsp(const struct l2cap_frame *frame) { struct l2cap_frame *f = (void *) frame; while (frame->size) { uint16_t handle; uint16_t len; if (!l2cap_frame_get_le16(f, &handle)) return; if (!l2cap_frame_get_le16(f, &len)) return; print_field("Length: 0x%4.4x", len); print_notify(frame, handle, len); l2cap_frame_pull(f, f, len); } } static void att_write_command(const struct l2cap_frame *frame) { uint16_t handle; if (!l2cap_frame_get_le16((void *)frame, &handle)) { print_text(COLOR_ERROR, "invalid size"); return; } print_write(frame, handle, frame->size); } static void att_signed_write_command(const struct l2cap_frame *frame) { uint16_t handle; if (!l2cap_frame_get_le16((void *)frame, &handle)) { print_text(COLOR_ERROR, "invalid size"); return; } print_write(frame, handle, frame->size - 12); print_hex_field(" Signature", frame->data + frame->size - 12, 12); } struct att_opcode_data { uint8_t opcode; const char *str; void (*func) (const struct l2cap_frame *frame); uint8_t size; bool fixed; }; static const struct att_opcode_data att_opcode_table[] = { { 0x01, "Error Response", att_error_response, 4, true }, { 0x02, "Exchange MTU Request", att_exchange_mtu_req, 2, true }, { 0x03, "Exchange MTU Response", att_exchange_mtu_rsp, 2, true }, { 0x04, "Find Information Request", att_find_info_req, 4, true }, { 0x05, "Find Information Response", att_find_info_rsp, 5, false }, { 0x06, "Find By Type Value Request", att_find_by_type_val_req, 6, false }, { 0x07, "Find By Type Value Response", att_find_by_type_val_rsp, 4, false }, { 0x08, "Read By Type Request", att_read_type_req, 6, false }, { 0x09, "Read By Type Response", att_read_type_rsp, 3, false }, { 0x0a, "Read Request", att_read_req, 2, true }, { 0x0b, "Read Response", att_read_rsp, 0, false }, { 0x0c, "Read Blob Request", att_read_blob_req, 4, true }, { 0x0d, "Read Blob Response", att_read_blob_rsp, 0, false }, { 0x0e, "Read Multiple Request", att_read_multiple_req, 4, false }, { 0x0f, "Read Multiple Response" }, { 0x10, "Read By Group Type Request", att_read_group_type_req, 6, false }, { 0x11, "Read By Group Type Response", att_read_group_type_rsp, 4, false }, { 0x12, "Write Request" , att_write_req, 2, false }, { 0x13, "Write Response", att_write_rsp, 0, true }, { 0x16, "Prepare Write Request", att_prepare_write_req, 4, false }, { 0x17, "Prepare Write Response", att_prepare_write_rsp, 4, false }, { 0x18, "Execute Write Request", att_execute_write_req, 1, true }, { 0x19, "Execute Write Response" }, { 0x1b, "Handle Value Notification", att_handle_value_notify, 2, false }, { 0x1d, "Handle Value Indication", att_handle_value_ind, 2, false }, { 0x1e, "Handle Value Confirmation", att_handle_value_conf, 0, true }, { 0x20, "Read Multiple Request Variable Length", att_read_multiple_req, 4, false }, { 0x21, "Read Multiple Response Variable Length", att_multiple_vl_rsp, 4, false }, { 0x23, "Handle Multiple Value Notification", att_multiple_vl_rsp, 4, false }, { 0x52, "Write Command", att_write_command, 2, false }, { 0xd2, "Signed Write Command", att_signed_write_command, 14, false }, { } }; static const char *att_opcode_to_str(uint8_t opcode) { int i; for (i = 0; att_opcode_table[i].str; i++) { if (att_opcode_table[i].opcode == opcode) return att_opcode_table[i].str; } return "Unknown"; } void att_packet(uint16_t index, bool in, uint16_t handle, uint16_t cid, const void *data, uint16_t size) { struct l2cap_frame frame; uint8_t opcode = *((const uint8_t *) data); const struct att_opcode_data *opcode_data = NULL; const char *opcode_color, *opcode_str; int i; if (size < 1) { print_text(COLOR_ERROR, "malformed attribute packet"); packet_hexdump(data, size); return; } for (i = 0; att_opcode_table[i].str; i++) { if (att_opcode_table[i].opcode == opcode) { opcode_data = &att_opcode_table[i]; break; } } if (opcode_data) { if (opcode_data->func) { if (in) opcode_color = COLOR_MAGENTA; else opcode_color = COLOR_BLUE; } else opcode_color = COLOR_WHITE_BG; opcode_str = opcode_data->str; } else { opcode_color = COLOR_WHITE_BG; opcode_str = "Unknown"; } print_indent(6, opcode_color, "ATT: ", opcode_str, COLOR_OFF, " (0x%2.2x) len %d", opcode, size - 1); if (!opcode_data || !opcode_data->func) { packet_hexdump(data + 1, size - 1); return; } if (opcode_data->fixed) { if (size - 1 != opcode_data->size) { print_text(COLOR_ERROR, "invalid size"); packet_hexdump(data + 1, size - 1); return; } } else { if (size - 1 < opcode_data->size) { print_text(COLOR_ERROR, "too short packet"); packet_hexdump(data + 1, size - 1); return; } } l2cap_frame_init(&frame, index, in, handle, 0, cid, 0, data + 1, size - 1); opcode_data->func(&frame); } bluez-5.82/monitor/PaxHeaders/rfcomm.h0000644000000000000000000000005014015011623014762 xustar0020 atime=1743516019 20 ctime=1743591278 bluez-5.82/monitor/rfcomm.h0000644000000000000000000000275114015011623014450 0ustar00rootroot/* SPDX-License-Identifier: LGPL-2.1-or-later */ /* * * BlueZ - Bluetooth protocol stack for Linux * * Copyright (C) 2011-2014 Intel Corporation * Copyright (C) 2002-2010 Marcel Holtmann * * */ #define RFCOMM_SABM 0x2f #define RFCOMM_DISC 0x43 #define RFCOMM_UA 0x63 #define RFCOMM_DM 0x0f #define RFCOMM_UIH 0xef #define RFCOMM_GET_TYPE(control) ((control) & 0xef) #define RFCOMM_GET_DLCI(address) ((address & 0xfc) >> 2) #define RFCOMM_GET_CHANNEL(address) ((address & 0xf8) >> 3) #define RFCOMM_GET_DIR(address) ((address & 0x04) >> 2) #define RFCOMM_TEST_EA(length) ((length & 0x01)) struct rfcomm_hdr { uint8_t address; uint8_t control; uint8_t length; } __attribute__((packed)); struct rfcomm_cmd { uint8_t address; uint8_t control; uint8_t length; uint8_t fcs; } __attribute__((packed)); #define RFCOMM_TEST 0x08 #define RFCOMM_FCON 0x28 #define RFCOMM_FCOFF 0x18 #define RFCOMM_MSC 0x38 #define RFCOMM_RPN 0x24 #define RFCOMM_RLS 0x14 #define RFCOMM_PN 0x20 #define RFCOMM_NSC 0x04 #define RFCOMM_TEST_CR(type) ((type & 0x02)) #define RFCOMM_GET_MCC_TYPE(type) ((type & 0xfc) >> 2) struct rfcomm_mcc { uint8_t type; uint8_t length; } __attribute__((packed)); struct rfcomm_msc { uint8_t dlci; uint8_t v24_sig; } __attribute__((packed)); struct rfcomm_pn { uint8_t dlci; uint8_t flow_ctrl; uint8_t priority; uint8_t ack_timer; uint16_t mtu; uint8_t max_retrans; uint8_t credits; } __attribute__((packed)); bluez-5.82/monitor/PaxHeaders/avctp.h0000644000000000000000000000005014015011623014614 xustar0020 atime=1743516019 20 ctime=1743591282 bluez-5.82/monitor/avctp.h0000644000000000000000000000043214015011623014274 0ustar00rootroot/* SPDX-License-Identifier: LGPL-2.1-or-later */ /* * * BlueZ - Bluetooth protocol stack for Linux * * Copyright (C) 2011-2014 Intel Corporation * Copyright (C) 2002-2010 Marcel Holtmann * * */ void avctp_packet(const struct l2cap_frame *frame); bluez-5.82/monitor/PaxHeaders/vendor.c0000644000000000000000000000005014015011623014767 xustar0020 atime=1743516011 20 ctime=1743591282 bluez-5.82/monitor/vendor.c0000644000000000000000000000067314015011623014456 0ustar00rootroot// SPDX-License-Identifier: LGPL-2.1-or-later /* * * BlueZ - Bluetooth protocol stack for Linux * * Copyright (C) 2011-2014 Intel Corporation * Copyright (C) 2002-2010 Marcel Holtmann * * */ #ifdef HAVE_CONFIG_H #include #endif #define _GNU_SOURCE #include "packet.h" #include "vendor.h" void vendor_event(uint16_t manufacturer, const void *data, uint8_t size) { packet_hexdump(data, size); } bluez-5.82/monitor/PaxHeaders/avdtp.c0000644000000000000000000000005014061461364014624 xustar0020 atime=1743516040 20 ctime=1743591282 bluez-5.82/monitor/avdtp.c0000644000000000000000000004124214061461364014310 0ustar00rootroot// SPDX-License-Identifier: LGPL-2.1-or-later /* * * BlueZ - Bluetooth protocol stack for Linux * * Copyright (C) 2015 Andrzej Kaczmarek * * */ #ifdef HAVE_CONFIG_H #include #endif #define _GNU_SOURCE #include #include #include #include "lib/bluetooth.h" #include "src/shared/util.h" #include "bt.h" #include "packet.h" #include "display.h" #include "l2cap.h" #include "avdtp.h" #include "a2dp.h" /* Message Types */ #define AVDTP_MSG_TYPE_COMMAND 0x00 #define AVDTP_MSG_TYPE_GENERAL_REJECT 0x01 #define AVDTP_MSG_TYPE_RESPONSE_ACCEPT 0x02 #define AVDTP_MSG_TYPE_RESPONSE_REJECT 0x03 /* Signal Identifiers */ #define AVDTP_DISCOVER 0x01 #define AVDTP_GET_CAPABILITIES 0x02 #define AVDTP_SET_CONFIGURATION 0x03 #define AVDTP_GET_CONFIGURATION 0x04 #define AVDTP_RECONFIGURE 0x05 #define AVDTP_OPEN 0x06 #define AVDTP_START 0x07 #define AVDTP_CLOSE 0x08 #define AVDTP_SUSPEND 0x09 #define AVDTP_ABORT 0x0a #define AVDTP_SECURITY_CONTROL 0x0b #define AVDTP_GET_ALL_CAPABILITIES 0x0c #define AVDTP_DELAYREPORT 0x0d /* Service Categories */ #define AVDTP_MEDIA_TRANSPORT 0x01 #define AVDTP_REPORTING 0x02 #define AVDTP_RECOVERY 0x03 #define AVDTP_CONTENT_PROTECTION 0x04 #define AVDTP_HEADER_COMPRESSION 0x05 #define AVDTP_MULTIPLEXING 0x06 #define AVDTP_MEDIA_CODEC 0x07 #define AVDTP_DELAY_REPORTING 0x08 struct avdtp_frame { uint8_t hdr; uint8_t sig_id; struct l2cap_frame l2cap_frame; }; static inline bool is_configuration_sig_id(uint8_t sig_id) { return (sig_id == AVDTP_SET_CONFIGURATION) || (sig_id == AVDTP_GET_CONFIGURATION) || (sig_id == AVDTP_RECONFIGURE); } static const char *msgtype2str(uint8_t msgtype) { switch (msgtype) { case 0: return "Command"; case 1: return "General Reject"; case 2: return "Response Accept"; case 3: return "Response Reject"; } return ""; } static const char *sigid2str(uint8_t sigid) { switch (sigid) { case AVDTP_DISCOVER: return "Discover"; case AVDTP_GET_CAPABILITIES: return "Get Capabilities"; case AVDTP_SET_CONFIGURATION: return "Set Configuration"; case AVDTP_GET_CONFIGURATION: return "Get Configuration"; case AVDTP_RECONFIGURE: return "Reconfigure"; case AVDTP_OPEN: return "Open"; case AVDTP_START: return "Start"; case AVDTP_CLOSE: return "Close"; case AVDTP_SUSPEND: return "Suspend"; case AVDTP_ABORT: return "Abort"; case AVDTP_SECURITY_CONTROL: return "Security Control"; case AVDTP_GET_ALL_CAPABILITIES: return "Get All Capabilities"; case AVDTP_DELAYREPORT: return "Delay Report"; default: return "Reserved"; } } static const char *error2str(uint8_t error) { switch (error) { case 0x01: return "BAD_HEADER_FORMAT"; case 0x11: return "BAD_LENGTH"; case 0x12: return "BAD_ACP_SEID"; case 0x13: return "SEP_IN_USE"; case 0x14: return "SEP_NOT_IN_USER"; case 0x17: return "BAD_SERV_CATEGORY"; case 0x18: return "BAD_PAYLOAD_FORMAT"; case 0x19: return "NOT_SUPPORTED_COMMAND"; case 0x1a: return "INVALID_CAPABILITIES"; case 0x22: return "BAD_RECOVERY_TYPE"; case 0x23: return "BAD_MEDIA_TRANSPORT_FORMAT"; case 0x25: return "BAD_RECOVERY_FORMAT"; case 0x26: return "BAD_ROHC_FORMAT"; case 0x27: return "BAD_CP_FORMAT"; case 0x28: return "BAD_MULTIPLEXING_FORMAT"; case 0x29: return "UNSUPPORTED_CONFIGURATION"; case 0x31: return "BAD_STATE"; default: return "Unknown"; } } static const char *mediatype2str(uint8_t media_type) { switch (media_type) { case 0x00: return "Audio"; case 0x01: return "Video"; case 0x02: return "Multimedia"; default: return "Reserved"; } } static const char *mediacodec2str(uint8_t codec) { switch (codec) { case 0x00: return "SBC"; case 0x01: return "MPEG-1,2 Audio"; case 0x02: return "MPEG-2,4 AAC"; case 0x04: return "ATRAC Family"; case 0xff: return "Non-A2DP"; default: return "Reserved"; } } static const char *cptype2str(uint8_t cp) { switch (cp) { case 0x0001: return "DTCP"; case 0x0002: return "SCMS-T"; default: return "Reserved"; } } static const char *servicecat2str(uint8_t service_cat) { switch (service_cat) { case AVDTP_MEDIA_TRANSPORT: return "Media Transport"; case AVDTP_REPORTING: return "Reporting"; case AVDTP_RECOVERY: return "Recovery"; case AVDTP_CONTENT_PROTECTION: return "Content Protection"; case AVDTP_HEADER_COMPRESSION: return "Header Compression"; case AVDTP_MULTIPLEXING: return "Multiplexing"; case AVDTP_MEDIA_CODEC: return "Media Codec"; case AVDTP_DELAY_REPORTING: return "Delay Reporting"; default: return "Reserved"; } } static bool avdtp_reject_common(struct avdtp_frame *avdtp_frame) { struct l2cap_frame *frame = &avdtp_frame->l2cap_frame; uint8_t error; if (!l2cap_frame_get_u8(frame, &error)) return false; print_field("Error code: %s (0x%02x)", error2str(error), error); return true; } static bool service_content_protection(struct avdtp_frame *avdtp_frame, uint8_t losc) { struct l2cap_frame *frame = &avdtp_frame->l2cap_frame; uint16_t type = 0; if (losc < 2) return false; if (!l2cap_frame_get_le16(frame, &type)) return false; losc -= 2; print_field("%*cContent Protection Type: %s (0x%04x)", 2, ' ', cptype2str(type), type); /* TODO: decode protection specific information */ packet_hexdump(frame->data, losc); l2cap_frame_pull(frame, frame, losc); return true; } static bool service_media_codec(struct avdtp_frame *avdtp_frame, uint8_t losc) { struct l2cap_frame *frame = &avdtp_frame->l2cap_frame; uint8_t type = 0; uint8_t codec = 0; if (losc < 2) return false; l2cap_frame_get_u8(frame, &type); l2cap_frame_get_u8(frame, &codec); losc -= 2; print_field("%*cMedia Type: %s (0x%02x)", 2, ' ', mediatype2str(type >> 4), type >> 4); print_field("%*cMedia Codec: %s (0x%02x)", 2, ' ', mediacodec2str(codec), codec); if (is_configuration_sig_id(avdtp_frame->sig_id)) return a2dp_codec_cfg(codec, losc, frame); else return a2dp_codec_cap(codec, losc, frame); } static bool decode_capabilities(struct avdtp_frame *avdtp_frame) { struct l2cap_frame *frame = &avdtp_frame->l2cap_frame; uint8_t service_cat; uint8_t losc; while (l2cap_frame_get_u8(frame, &service_cat)) { print_field("Service Category: %s (0x%02x)", servicecat2str(service_cat), service_cat); if (!l2cap_frame_get_u8(frame, &losc)) return false; if (frame->size < losc) return false; switch (service_cat) { case AVDTP_CONTENT_PROTECTION: if (!service_content_protection(avdtp_frame, losc)) return false; break; case AVDTP_MEDIA_CODEC: if (!service_media_codec(avdtp_frame, losc)) return false; break; case AVDTP_MEDIA_TRANSPORT: case AVDTP_REPORTING: case AVDTP_RECOVERY: case AVDTP_HEADER_COMPRESSION: case AVDTP_MULTIPLEXING: case AVDTP_DELAY_REPORTING: default: packet_hexdump(frame->data, losc); l2cap_frame_pull(frame, frame, losc); } } return true; } static bool avdtp_discover(struct avdtp_frame *avdtp_frame) { struct l2cap_frame *frame = &avdtp_frame->l2cap_frame; uint8_t type = avdtp_frame->hdr & 0x03; uint8_t seid; uint8_t info; switch (type) { case AVDTP_MSG_TYPE_COMMAND: return true; case AVDTP_MSG_TYPE_RESPONSE_ACCEPT: while (l2cap_frame_get_u8(frame, &seid)) { print_field("ACP SEID: %d", seid >> 2); if (!l2cap_frame_get_u8(frame, &info)) return false; print_field("%*cMedia Type: %s (0x%02x)", 2, ' ', mediatype2str(info >> 4), info >> 4); print_field("%*cSEP Type: %s (0x%02x)", 2, ' ', info & 0x08 ? "SNK" : "SRC", (info >> 3) & 0x01); print_field("%*cIn use: %s", 2, ' ', seid & 0x02 ? "Yes" : "No"); } return true; case AVDTP_MSG_TYPE_RESPONSE_REJECT: return avdtp_reject_common(avdtp_frame); } return false; } static bool avdtp_get_capabilities(struct avdtp_frame *avdtp_frame) { struct l2cap_frame *frame = &avdtp_frame->l2cap_frame; uint8_t type = avdtp_frame->hdr & 0x03; uint8_t seid; switch (type) { case AVDTP_MSG_TYPE_COMMAND: if (!l2cap_frame_get_u8(frame, &seid)) return false; print_field("ACP SEID: %d", seid >> 2); return true; case AVDTP_MSG_TYPE_RESPONSE_ACCEPT: return decode_capabilities(avdtp_frame); case AVDTP_MSG_TYPE_RESPONSE_REJECT: return avdtp_reject_common(avdtp_frame); } return false; } static bool avdtp_set_configuration(struct avdtp_frame *avdtp_frame) { struct l2cap_frame *frame = &avdtp_frame->l2cap_frame; uint8_t type = avdtp_frame->hdr & 0x03; uint8_t acp_seid, int_seid; uint8_t service_cat; switch (type) { case AVDTP_MSG_TYPE_COMMAND: if (!l2cap_frame_get_u8(frame, &acp_seid)) return false; print_field("ACP SEID: %d", acp_seid >> 2); if (!l2cap_frame_get_u8(frame, &int_seid)) return false; print_field("INT SEID: %d", int_seid >> 2); return decode_capabilities(avdtp_frame); case AVDTP_MSG_TYPE_RESPONSE_ACCEPT: return true; case AVDTP_MSG_TYPE_RESPONSE_REJECT: if (!l2cap_frame_get_u8(frame, &service_cat)) return false; print_field("Service Category: %s (0x%02x)", servicecat2str(service_cat), service_cat); return avdtp_reject_common(avdtp_frame); } return false; } static bool avdtp_get_configuration(struct avdtp_frame *avdtp_frame) { struct l2cap_frame *frame = &avdtp_frame->l2cap_frame; uint8_t type = avdtp_frame->hdr & 0x03; uint8_t seid; switch (type) { case AVDTP_MSG_TYPE_COMMAND: if (!l2cap_frame_get_u8(frame, &seid)) return false; print_field("ACP SEID: %d", seid >> 2); return true; case AVDTP_MSG_TYPE_RESPONSE_ACCEPT: return decode_capabilities(avdtp_frame); case AVDTP_MSG_TYPE_RESPONSE_REJECT: return avdtp_reject_common(avdtp_frame); } return false; } static bool avdtp_reconfigure(struct avdtp_frame *avdtp_frame) { struct l2cap_frame *frame = &avdtp_frame->l2cap_frame; uint8_t type = avdtp_frame->hdr & 0x03; uint8_t seid; uint8_t service_cat; switch (type) { case AVDTP_MSG_TYPE_COMMAND: if (!l2cap_frame_get_u8(frame, &seid)) return false; print_field("ACP SEID: %d", seid >> 2); return decode_capabilities(avdtp_frame); case AVDTP_MSG_TYPE_RESPONSE_ACCEPT: return true; case AVDTP_MSG_TYPE_RESPONSE_REJECT: if (!l2cap_frame_get_u8(frame, &service_cat)) return false; print_field("Service Category: %s (0x%02x)", servicecat2str(service_cat), service_cat); return avdtp_reject_common(avdtp_frame); } return false; } static bool avdtp_open(struct avdtp_frame *avdtp_frame) { struct l2cap_frame *frame = &avdtp_frame->l2cap_frame; uint8_t type = avdtp_frame->hdr & 0x03; uint8_t seid; switch (type) { case AVDTP_MSG_TYPE_COMMAND: if (!l2cap_frame_get_u8(frame, &seid)) return false; print_field("ACP SEID: %d", seid >> 2); return true; case AVDTP_MSG_TYPE_RESPONSE_ACCEPT: return true; case AVDTP_MSG_TYPE_RESPONSE_REJECT: return avdtp_reject_common(avdtp_frame); } return false; } static bool avdtp_start(struct avdtp_frame *avdtp_frame) { struct l2cap_frame *frame = &avdtp_frame->l2cap_frame; uint8_t type = avdtp_frame->hdr & 0x03; uint8_t seid; switch (type) { case AVDTP_MSG_TYPE_COMMAND: if (!l2cap_frame_get_u8(frame, &seid)) return false; print_field("ACP SEID: %d", seid >> 2); while (l2cap_frame_get_u8(frame, &seid)) print_field("ACP SEID: %d", seid >> 2); return true; case AVDTP_MSG_TYPE_RESPONSE_ACCEPT: return true; case AVDTP_MSG_TYPE_RESPONSE_REJECT: if (!l2cap_frame_get_u8(frame, &seid)) return false; print_field("ACP SEID: %d", seid >> 2); return avdtp_reject_common(avdtp_frame); } return false; } static bool avdtp_close(struct avdtp_frame *avdtp_frame) { struct l2cap_frame *frame = &avdtp_frame->l2cap_frame; uint8_t type = avdtp_frame->hdr & 0x03; uint8_t seid; switch (type) { case AVDTP_MSG_TYPE_COMMAND: if (!l2cap_frame_get_u8(frame, &seid)) return false; print_field("ACP SEID: %d", seid >> 2); return true; case AVDTP_MSG_TYPE_RESPONSE_ACCEPT: return true; case AVDTP_MSG_TYPE_RESPONSE_REJECT: return avdtp_reject_common(avdtp_frame); } return false; } static bool avdtp_suspend(struct avdtp_frame *avdtp_frame) { struct l2cap_frame *frame = &avdtp_frame->l2cap_frame; uint8_t type = avdtp_frame->hdr & 0x03; uint8_t seid; switch (type) { case AVDTP_MSG_TYPE_COMMAND: if (!l2cap_frame_get_u8(frame, &seid)) return false; print_field("ACP SEID: %d", seid >> 2); while (l2cap_frame_get_u8(frame, &seid)) print_field("ACP SEID: %d", seid >> 2); return true; case AVDTP_MSG_TYPE_RESPONSE_ACCEPT: return true; case AVDTP_MSG_TYPE_RESPONSE_REJECT: if (!l2cap_frame_get_u8(frame, &seid)) return false; print_field("ACP SEID: %d", seid >> 2); return avdtp_reject_common(avdtp_frame); } return false; } static bool avdtp_abort(struct avdtp_frame *avdtp_frame) { struct l2cap_frame *frame = &avdtp_frame->l2cap_frame; uint8_t type = avdtp_frame->hdr & 0x03; uint8_t seid; switch (type) { case AVDTP_MSG_TYPE_COMMAND: if (!l2cap_frame_get_u8(frame, &seid)) return false; print_field("ACP SEID: %d", seid >> 2); return true; case AVDTP_MSG_TYPE_RESPONSE_ACCEPT: return true; } return false; } static bool avdtp_security_control(struct avdtp_frame *avdtp_frame) { struct l2cap_frame *frame = &avdtp_frame->l2cap_frame; uint8_t type = avdtp_frame->hdr & 0x03; uint8_t seid; switch (type) { case AVDTP_MSG_TYPE_COMMAND: if (!l2cap_frame_get_u8(frame, &seid)) return false; print_field("ACP SEID: %d", seid >> 2); /* TODO: decode more information */ packet_hexdump(frame->data, frame->size); return true; case AVDTP_MSG_TYPE_RESPONSE_ACCEPT: /* TODO: decode more information */ packet_hexdump(frame->data, frame->size); return true; case AVDTP_MSG_TYPE_RESPONSE_REJECT: return avdtp_reject_common(avdtp_frame); } return false; } static bool avdtp_delayreport(struct avdtp_frame *avdtp_frame) { struct l2cap_frame *frame = &avdtp_frame->l2cap_frame; uint8_t type = avdtp_frame->hdr & 0x03; uint8_t seid; uint16_t delay; switch (type) { case AVDTP_MSG_TYPE_COMMAND: if (!l2cap_frame_get_u8(frame, &seid)) return false; print_field("ACP SEID: %d", seid >> 2); if (!l2cap_frame_get_be16(frame, &delay)) return false; print_field("Delay: %d.%dms", delay / 10, delay % 10); return true; case AVDTP_MSG_TYPE_RESPONSE_ACCEPT: return true; case AVDTP_MSG_TYPE_RESPONSE_REJECT: return avdtp_reject_common(avdtp_frame); } return false; } static bool avdtp_signalling_packet(struct avdtp_frame *avdtp_frame) { struct l2cap_frame *frame = &avdtp_frame->l2cap_frame; const char *pdu_color; uint8_t hdr; uint8_t sig_id; uint8_t nosp = 0; if (frame->in) pdu_color = COLOR_MAGENTA; else pdu_color = COLOR_BLUE; if (!l2cap_frame_get_u8(frame, &hdr)) return false; avdtp_frame->hdr = hdr; /* Continue Packet || End Packet */ if (((hdr & 0x0c) == 0x08) || ((hdr & 0x0c) == 0x0c)) { /* TODO: handle fragmentation */ packet_hexdump(frame->data, frame->size); return true; } /* Start Packet */ if ((hdr & 0x0c) == 0x04) { if (!l2cap_frame_get_u8(frame, &nosp)) return false; } if (!l2cap_frame_get_u8(frame, &sig_id)) return false; sig_id &= 0x3f; avdtp_frame->sig_id = sig_id; print_indent(6, pdu_color, "AVDTP: ", sigid2str(sig_id), COLOR_OFF, " (0x%02x) %s (0x%02x) type 0x%02x label %d nosp %d", sig_id, msgtype2str(hdr & 0x03), hdr & 0x03, hdr & 0x0c, hdr >> 4, nosp); /* Start Packet */ if ((hdr & 0x0c) == 0x04) { /* TODO: handle fragmentation */ packet_hexdump(frame->data, frame->size); return true; } switch (sig_id) { case AVDTP_DISCOVER: return avdtp_discover(avdtp_frame); case AVDTP_GET_CAPABILITIES: case AVDTP_GET_ALL_CAPABILITIES: return avdtp_get_capabilities(avdtp_frame); case AVDTP_SET_CONFIGURATION: return avdtp_set_configuration(avdtp_frame); case AVDTP_GET_CONFIGURATION: return avdtp_get_configuration(avdtp_frame); case AVDTP_RECONFIGURE: return avdtp_reconfigure(avdtp_frame); case AVDTP_OPEN: return avdtp_open(avdtp_frame); case AVDTP_START: return avdtp_start(avdtp_frame); case AVDTP_CLOSE: return avdtp_close(avdtp_frame); case AVDTP_SUSPEND: return avdtp_suspend(avdtp_frame); case AVDTP_ABORT: return avdtp_abort(avdtp_frame); case AVDTP_SECURITY_CONTROL: return avdtp_security_control(avdtp_frame); case AVDTP_DELAYREPORT: return avdtp_delayreport(avdtp_frame); } packet_hexdump(frame->data, frame->size); return true; } void avdtp_packet(const struct l2cap_frame *frame) { struct avdtp_frame avdtp_frame; bool ret; l2cap_frame_pull(&avdtp_frame.l2cap_frame, frame, 0); switch (frame->seq_num) { case 1: ret = avdtp_signalling_packet(&avdtp_frame); break; default: if (packet_has_filter(PACKET_FILTER_SHOW_A2DP_STREAM)) packet_hexdump(frame->data, frame->size); return; } if (!ret) { print_text(COLOR_ERROR, "PDU malformed"); packet_hexdump(frame->data, frame->size); } } bluez-5.82/monitor/PaxHeaders/emulator.h0000644000000000000000000000005014214376354015347 xustar0020 atime=1743516869 20 ctime=1743591282 bluez-5.82/monitor/emulator.h0000644000000000000000000000056114214376354015032 0ustar00rootroot/* SPDX-License-Identifier: LGPL-2.1-or-later */ /* * * BlueZ - Bluetooth protocol stack for Linux * * Copyright (C) 2011-2014 Intel Corporation * Copyright (C) 2002-2010 Marcel Holtmann * * */ #define EMU_SUBCMD_TEST_EVENT 0x00 struct emu_cmd_test_event { uint8_t subcmd; uint8_t evt; uint8_t data[]; } __attribute__((packed)); bluez-5.82/monitor/PaxHeaders/att.h0000644000000000000000000000005014267331527014310 xustar0020 atime=1743516019 20 ctime=1743591282 bluez-5.82/monitor/att.h0000644000000000000000000000057714267331527014002 0ustar00rootroot/* SPDX-License-Identifier: LGPL-2.1-or-later */ /* * * BlueZ - Bluetooth protocol stack for Linux * * Copyright (C) 2011-2014 Intel Corporation * Copyright (C) 2002-2010 Marcel Holtmann * * */ #include #include void att_packet(uint16_t index, bool in, uint16_t handle, uint16_t cid, const void *data, uint16_t size); bluez-5.82/monitor/PaxHeaders/display.h0000644000000000000000000000005014606723200015153 xustar0020 atime=1743515951 20 ctime=1743591282 bluez-5.82/monitor/display.h0000644000000000000000000000446414606723200014644 0ustar00rootroot/* SPDX-License-Identifier: LGPL-2.1-or-later */ /* * * BlueZ - Bluetooth protocol stack for Linux * * Copyright (C) 2011-2014 Intel Corporation * Copyright (C) 2002-2010 Marcel Holtmann * * */ #include #include bool use_color(void); enum monitor_color { COLOR_AUTO, COLOR_ALWAYS, COLOR_NEVER }; void set_monitor_color(enum monitor_color); #define COLOR_OFF "\x1B[0m" #define COLOR_BLACK "\x1B[0;30m" #define COLOR_RED "\x1B[0;31m" #define COLOR_GREEN "\x1B[0;32m" #define COLOR_YELLOW "\x1B[0;33m" #define COLOR_BLUE "\x1B[0;34m" #define COLOR_MAGENTA "\x1B[0;35m" #define COLOR_CYAN "\x1B[0;36m" #define COLOR_WHITE "\x1B[0;37m" #define COLOR_WHITE_BG "\x1B[0;47;30m" #define COLOR_HIGHLIGHT "\x1B[1;39m" #define COLOR_RED_BOLD "\x1B[1;31m" #define COLOR_GREEN_BOLD "\x1B[1;32m" #define COLOR_BLUE_BOLD "\x1B[1;34m" #define COLOR_MAGENTA_BOLD "\x1B[1;35m" #define COLOR_ERROR "\x1B[1;31m" #define COLOR_WARN "\x1B[1m" #define COLOR_INFO COLOR_OFF #define COLOR_DEBUG COLOR_WHITE #define FALLBACK_TERMINAL_WIDTH 80 #define print_indent(indent, color1, prefix, title, color2, fmt, args...) \ do { \ printf("%*c%s%s%s%s" fmt "%s\n", (indent), ' ', \ use_color() ? (color1) : "", prefix, title, \ use_color() ? (color2) : "", ## args, \ use_color() ? COLOR_OFF : ""); \ } while (0) #define print_text(color, fmt, args...) \ print_indent(8, COLOR_OFF, "", "", color, fmt, ## args) #define print_field(fmt, args...) \ print_indent(8, COLOR_OFF, "", "", COLOR_OFF, fmt, ## args) struct bitfield_data { uint64_t bit; const char *str; }; static inline uint64_t print_bitfield(int indent, uint64_t val, const struct bitfield_data *table) { uint64_t mask = val; int i; for (i = 0; table[i].str; i++) { if (val & (((uint64_t) 1) << table[i].bit)) { print_field("%*c%s", indent, ' ', table[i].str); mask &= ~(((uint64_t) 1) << table[i].bit); } } return mask; } static inline void print_hex_field(const char *label, const uint8_t *data, uint8_t len) { char str[len * 2 + 1]; uint8_t i; str[0] = '\0'; for (i = 0; i < len; i++) sprintf(str + (i * 2), "%2.2x", data[i]); print_field("%s[%u]: %s", label, len, str); } void set_default_pager_num_columns(int num_columns); int num_columns(void); void open_pager(void); void close_pager(void); bluez-5.82/monitor/PaxHeaders/control.h0000644000000000000000000000005014015011623015157 xustar0020 atime=1743515951 20 ctime=1743591282 bluez-5.82/monitor/control.h0000644000000000000000000000122014015011623014633 0ustar00rootroot/* SPDX-License-Identifier: LGPL-2.1-or-later */ /* * * BlueZ - Bluetooth protocol stack for Linux * * Copyright (C) 2011-2014 Intel Corporation * Copyright (C) 2002-2010 Marcel Holtmann * * */ #include bool control_writer(const char *path); void control_reader(const char *path, bool pager); void control_server(const char *path); int control_tty(const char *path, unsigned int speed); int control_rtt(char *jlink, char *rtt); int control_tracing(void); void control_disable_decoding(void); void control_filter_index(uint16_t index); void control_message(uint16_t opcode, const void *data, uint16_t size); bluez-5.82/PaxHeaders/client0000644000000000000000000000005014773213573013063 xustar0020 atime=1743591291 20 ctime=1743591291 bluez-5.82/client/0000755000000000000000000000000014773213573012621 5ustar00rootrootbluez-5.82/client/PaxHeaders/bluetoothctl-mgmt.10000644000000000000000000000005014773213521016665 xustar0020 atime=1743591249 20 ctime=1743591291 bluez-5.82/client/bluetoothctl-mgmt.10000644000000000000000000002163314773213521016353 0ustar00rootroot.\" Man page generated from reStructuredText. . . .nr rst2man-indent-level 0 . .de1 rstReportMargin \\$1 \\n[an-margin] level \\n[rst2man-indent-level] level margin: \\n[rst2man-indent\\n[rst2man-indent-level]] - \\n[rst2man-indent0] \\n[rst2man-indent1] \\n[rst2man-indent2] .. .de1 INDENT .\" .rstReportMargin pre: . RS \\$1 . nr rst2man-indent\\n[rst2man-indent-level] \\n[an-margin] . nr rst2man-indent-level +1 .\" .rstReportMargin post: .. .de UNINDENT . RE .\" indent \\n[an-margin] .\" old: \\n[rst2man-indent\\n[rst2man-indent-level]] .nr rst2man-indent-level -1 .\" new: \\n[rst2man-indent\\n[rst2man-indent-level]] .in \\n[rst2man-indent\\n[rst2man-indent-level]]u .. .TH "BLUETOOTHCTL-MGMT" "1" "July 2023" "BlueZ" "Linux System Administration" .SH NAME bluetoothctl-mgmt \- Management Submenu .SH SYNOPSIS .sp \fBbluetoothctl\fP [\-\-options] [mgmt.commands] .SH MGMT COMMANDS .SS select .sp Select a different index .INDENT 0.0 .TP .B Usage \fB> select \fP .UNINDENT .SS revision .sp Get the MGMT Revision .INDENT 0.0 .TP .B Usage \fB> revision\fP .UNINDENT .SS commands .sp List supported commands .INDENT 0.0 .TP .B Usage \fB> commands\fP .UNINDENT .SS config .sp Show configuration info .INDENT 0.0 .TP .B Usage \fB> config\fP .UNINDENT .SS info .sp Show controller info .INDENT 0.0 .TP .B Usage \fB> info\fP .UNINDENT .SS extinfo .sp Show extended controller info .INDENT 0.0 .TP .B Usage \fB> extinfo\fP .UNINDENT .SS auto\-power .sp Power all available features .INDENT 0.0 .TP .B Usage \fB> auto\-power\fP .UNINDENT .SS power .sp Toggle powered state .INDENT 0.0 .TP .B Usage \fB> power \fP .UNINDENT .SS discov .sp Toggle discoverable state .INDENT 0.0 .TP .B Usage \fB> discov [timeout]\fP .UNINDENT .SS connectable .sp Toggle connectable state .INDENT 0.0 .TP .B Usage \fB> connectable \fP .UNINDENT .SS fast\-conn .sp Toggle fast connectable state .INDENT 0.0 .TP .B Usage \fB> fast\-conn \fP .UNINDENT .SS bondable .sp Toggle bondable state .INDENT 0.0 .TP .B Usage \fB> bondable \fP .UNINDENT .SS pairable .sp Toggle bondable state .INDENT 0.0 .TP .B Usage \fB> pairable \fP .UNINDENT .SS linksec .sp Toggle link level security .INDENT 0.0 .TP .B Usage \fB> linksec \fP .UNINDENT .SS ssp .sp Toggle SSP mode .INDENT 0.0 .TP .B Usage \fB> spp \fP .UNINDENT .SS sc .sp Toggle SC support .INDENT 0.0 .TP .B Usage \fB> sc \fP .UNINDENT .SS hs .sp Toggle HS support .INDENT 0.0 .TP .B Usage \fB> hs \fP .UNINDENT .SS le .sp Toggle LE support .INDENT 0.0 .TP .B Usage \fB> le \fP .UNINDENT .SS advertising .sp Toggle LE advertising .INDENT 0.0 .TP .B Usage \fB> advertise \fP .UNINDENT .SS bredr .sp Toggle BR/EDR support .INDENT 0.0 .TP .B Usage \fB> bredr \fP .UNINDENT .SS privacy .sp Toggle privacy support .INDENT 0.0 .TP .B Usage \fB> privacy [irk]\fP .UNINDENT .SS class .sp Set device major/minor class .INDENT 0.0 .TP .B Usage \fB> class \fP .UNINDENT .SS disconnect .sp Disconnect device .INDENT 0.0 .TP .B Usage \fB> disconnect [\-t type] \fP .UNINDENT .SS con .sp List connections .INDENT 0.0 .TP .B Usage \fB> con\fP .UNINDENT .SS find .sp Discover nearby devices .INDENT 0.0 .TP .B Usage \fB> find [\-l|\-b] [\-L]\fP .UNINDENT .SS find\-service .sp Discover nearby service .INDENT 0.0 .TP .B Usage \fB> find\-service [\-u UUID] [\-r RSSI_Threshold] [\-l|\-b]\fP .UNINDENT .SS stop\-find .sp Stop discovery .INDENT 0.0 .TP .B Usage \fB> stop\-find [\-l|\-b]\fP .UNINDENT .SS name .sp Set local name .INDENT 0.0 .TP .B Usage \fB> name [shortname]\fP .UNINDENT .SS pair .sp Pair with a remote device .INDENT 0.0 .TP .B Usage \fB> pair [\-c cap] [\-t type] \fP .UNINDENT .SS cancelpair .sp Cancel pairing .INDENT 0.0 .TP .B Usage \fB> cancelpair [\-t type] \fP .UNINDENT .SS unpair .sp Unpair device .INDENT 0.0 .TP .B Usage \fB> unpair [\-t type] \fP .UNINDENT .SS keys .sp Load Link Keys .INDENT 0.0 .TP .B Usage \fBkeys\fP .UNINDENT .SS ltks .sp Load Long Term Keys .INDENT 0.0 .TP .B Usage \fB> ltks\fP .UNINDENT .SS irks .sp Load Identity Resolving Keys .INDENT 0.0 .TP .B Usage \fB> irks [\-\-local index] [\-\-file file path]\fP .UNINDENT .SS block .sp Block Device .INDENT 0.0 .TP .B Usage \fB> block [\-t type] \fP .UNINDENT .SS unblock .sp Unblock Device .INDENT 0.0 .TP .B Usage \fB> unblock [\-t type] \fP .UNINDENT .SS add\-uuid .sp Add UUID .INDENT 0.0 .TP .B Usage \fB> add\-uuid \fP .UNINDENT .SS rm\-uuid .sp Remove UUID .INDENT 0.0 .TP .B Usage \fB> rm\-uuid \fP .UNINDENT .SS clr\-uuids .sp Clear UUIDs .INDENT 0.0 .TP .B Usage \fB> clear\-uuids\fP .UNINDENT .SS local\-oob .sp Local OOB data .INDENT 0.0 .TP .B Usage \fB> local\-oob\fP .UNINDENT .SS remote\-oob .sp Remote OOB data .INDENT 0.0 .TP .B Usage \fB> remote\-oob [\-t ] [\-r ] [\-h ] [\-R ] [\-H ] \fP .UNINDENT .SS did .sp Set Device ID .INDENT 0.0 .TP .B Usage \fB> did :::\fP .UNINDENT .SS static\-addr .sp Set static address .INDENT 0.0 .TP .B Usage \fB> static\-addr
\fP .UNINDENT .SS public\-addr .sp Set public address .INDENT 0.0 .TP .B Usage \fB> public\-addr
\fP .UNINDENT .SS ext\-config .sp External configuration .INDENT 0.0 .TP .B Usage \fB> ext\-config \fP .UNINDENT .SS debug\-keys .sp Toggle debug keys .INDENT 0.0 .TP .B Usage \fB> debug\-keys \fP .UNINDENT .SS conn\-info .sp Get connection information .INDENT 0.0 .TP .B Usage \fB> conn\-info [\-t type] \fP .UNINDENT .SS io\-cap .sp Set IO Capability .INDENT 0.0 .TP .B Usage \fB> io\-cap \fP .UNINDENT .SS scan\-params .sp Set Scan Parameters .INDENT 0.0 .TP .B Usage \fB> scan\-params \fP .UNINDENT .SS get\-clock .sp Get Clock Information .INDENT 0.0 .TP .B Usage \fB> get\-clock [address]\fP .UNINDENT .SS add\-device .sp Add Device .INDENT 0.0 .TP .B Usage \fB> add\-device [\-a action] [\-t type]
\fP .UNINDENT .SS del\-device .sp Remove Device .INDENT 0.0 .TP .B Usage \fB> del\-device [\-t type]
\fP .UNINDENT .SS clr\-devices .sp Clear Devices .INDENT 0.0 .TP .B Usage \fB> clr\-devices\fP .UNINDENT .SS bredr\-oob .sp Local OOB data (BR/EDR) .INDENT 0.0 .TP .B Usage \fB> bredr\-oob\fP .UNINDENT .SS le\-oob .sp Local OOB data (LE) .INDENT 0.0 .TP .B Usage \fB> le\-oob\fP .UNINDENT .SS advinfo .sp Show advertising features .INDENT 0.0 .TP .B Usage \fB> advinfo\fP .UNINDENT .SS advsize .sp Show advertising size info .INDENT 0.0 .TP .B Usage \fB> advsize [options] \fP .UNINDENT .SS add\-adv .sp Add advertising instance .INDENT 0.0 .TP .B Usage \fB> add\-adv [options] \fP .UNINDENT .SS rm\-adv .sp Remove advertising instance .INDENT 0.0 .TP .B Usage \fB> rm\-adv \fP .UNINDENT .SS clr\-adv .sp Clear advertising instances .INDENT 0.0 .TP .B Usage \fB> clr\-adv\fP .UNINDENT .SS add\-ext\-adv\-params .sp Add extended advertising params .INDENT 0.0 .TP .B Usage \fB> add\-ext\-adv\-parms [options] \fP .UNINDENT .SS add\-ext\-adv\-data .sp Add extended advertising data .INDENT 0.0 .TP .B Usage \fB> add\-ext\-adv\-data [options] \fP .UNINDENT .SS appearance .sp Set appearance .INDENT 0.0 .TP .B Usage \fB> appearance \fP .UNINDENT .SS phy .sp Get/Set PHY Configuration .INDENT 0.0 .TP .B Usage \fB> phy [LE1MTX] [LE1MRX] [LE2MTX] [LE2MRX] [LECODEDTX] [LECODEDRX] [BR1M1SLOT] [BR1M3SLOT] [BR1M5SLOT][EDR2M1SLOT] [EDR2M3SLOT] [EDR2M5SLOT][EDR3M1SLOT] [EDR3M3SLOT] [EDR3M5SLOT]\fP .UNINDENT .SS wbs .sp Toggle Wideband\-Speech support .INDENT 0.0 .TP .B Usage \fB> wbs \fP .UNINDENT .SS secinfo .sp Show security information .INDENT 0.0 .TP .B Usage \fB> secinfo\fP .UNINDENT .SS expinfo .sp Show experimental features .INDENT 0.0 .TP .B Usage \fB> expinfo\fP .UNINDENT .SS exp\-debug .sp Set debug feature .INDENT 0.0 .TP .B Usage \fB> exp\-debug \fP .UNINDENT .SS exp\-privacy .sp Set LL privacy feature .INDENT 0.0 .TP .B Usage \fB> exp\-privacy \fP .UNINDENT .SS exp\-quality .sp Set bluetooth quality report feature .INDENT 0.0 .TP .B Usage \fB> exp\-quality \fP .UNINDENT .SS exp\-offload .sp Toggle codec support .INDENT 0.0 .TP .B Usage \fB> exp\-offload \fP .UNINDENT .SS read\-sysconfig .sp Read System Configuration .INDENT 0.0 .TP .B Usage \fB> read\-sysconfig\fP .UNINDENT .SS set\-sysconfig .sp Set System Configuration .INDENT 0.0 .TP .B Usage \fB> set\-sysconfig <\-v|\-h> [options...]\fP .UNINDENT .SS get\-flags .sp Get device flags .SS set\-flags .sp Set device flags .INDENT 0.0 .TP .B Usage \fB> set\-flags [\-f flags] [\-t type]
\fP .UNINDENT .SH RESOURCES .sp .SH REPORTING BUGS .sp .SH COPYRIGHT Free use of this software is granted under the terms of the GNU Lesser General Public Licenses (LGPL). .\" Generated by docutils manpage writer. . bluez-5.82/client/PaxHeaders/player.h0000644000000000000000000000005014267331526014602 xustar0020 atime=1743515768 20 ctime=1743591281 bluez-5.82/client/player.h0000644000000000000000000000036014267331526014262 0ustar00rootroot// SPDX-License-Identifier: GPL-2.0-or-later /* * * BlueZ - Bluetooth protocol stack for Linux * * Copyright (C) 2020 Intel Corporation. All rights reserved. * * */ void player_add_submenu(void); void player_remove_submenu(void); bluez-5.82/client/PaxHeaders/bluetoothctl-player.10000644000000000000000000000005014773213527017223 xustar0020 atime=1743591255 20 ctime=1743591291 bluez-5.82/client/bluetoothctl-player.10000644000000000000000000000570114773213527016707 0ustar00rootroot.\" Man page generated from reStructuredText. . . .nr rst2man-indent-level 0 . .de1 rstReportMargin \\$1 \\n[an-margin] level \\n[rst2man-indent-level] level margin: \\n[rst2man-indent\\n[rst2man-indent-level]] - \\n[rst2man-indent0] \\n[rst2man-indent1] \\n[rst2man-indent2] .. .de1 INDENT .\" .rstReportMargin pre: . RS \\$1 . nr rst2man-indent\\n[rst2man-indent-level] \\n[an-margin] . nr rst2man-indent-level +1 .\" .rstReportMargin post: .. .de UNINDENT . RE .\" indent \\n[an-margin] .\" old: \\n[rst2man-indent\\n[rst2man-indent-level]] .nr rst2man-indent-level -1 .\" new: \\n[rst2man-indent\\n[rst2man-indent-level]] .in \\n[rst2man-indent\\n[rst2man-indent-level]]u .. .TH "BLUETOOTHCTL-PLAYER" "1" "November 2022" "BlueZ" "Linux System Administration" .SH NAME bluetoothctl-player \- Media Player Submenu .SH SYNOPSIS .sp \fBbluetoothctl\fP [\-\-options] [player.commands] .SH MEDIA PLAYER COMMANDS .SS list .sp List available players. .INDENT 0.0 .TP .B Usage \fB> list\fP .UNINDENT .SS show .sp Show player information. .INDENT 0.0 .TP .B Usage \fB> show [player]\fP .UNINDENT .SS select .sp Select default player. .INDENT 0.0 .TP .B Usage \fB> select \fP .UNINDENT .SS play .sp Start playback. .INDENT 0.0 .TP .B Usage \fB> play [item]\fP .UNINDENT .SS pause .sp Pause playback. .INDENT 0.0 .TP .B Usage \fB> pause\fP .UNINDENT .SS stop .sp Stop playback. .INDENT 0.0 .TP .B Usage \fB> stop\fP .UNINDENT .SS next .sp Jump to next item. .INDENT 0.0 .TP .B Usage \fB> next\fP .UNINDENT .SS previous .sp Jump to previous item. .INDENT 0.0 .TP .B Usage \fB> previous\fP .UNINDENT .SS fast\-forward .sp Fast forward playback. .INDENT 0.0 .TP .B Usage \fB> fast\-forward\fP .UNINDENT .SS rewind .sp Rewind playback. .INDENT 0.0 .TP .B Usage \fB> rewind\fP .UNINDENT .SS equalizer .sp Enable/Disable equalizer. .INDENT 0.0 .TP .B Usage \fB> equalizer \fP .UNINDENT .SS repeat .sp Set repeat mode. .INDENT 0.0 .TP .B Usage \fB> repeat \fP .UNINDENT .SS shuffle .sp Set shuffle mode. .INDENT 0.0 .TP .B Usage \fB> shuffle \fP .UNINDENT .SS scan .sp Set scan mode. .INDENT 0.0 .TP .B Usage \fB> scan \fP .UNINDENT .SS change\-folder .sp Change current folder. .INDENT 0.0 .TP .B Usage \fB> change\-folder \fP .UNINDENT .SS list\-items .sp List items of current folder. .INDENT 0.0 .TP .B Usage \fB> list\-items [start] [end]\fP .UNINDENT .SS search .sp Search items containing string. .INDENT 0.0 .TP .B Usage \fB> search \fP .UNINDENT .SS queue .sp Add item to playlist queue. .INDENT 0.0 .TP .B Usage \fB> queue \fP .UNINDENT .SS show\-item .sp Show item information. .INDENT 0.0 .TP .B Usage \fB> show\-item \fP .UNINDENT .SH RESOURCES .sp .SH REPORTING BUGS .sp .SH COPYRIGHT Free use of this software is granted under the terms of the GNU Lesser General Public Licenses (LGPL). .\" Generated by docutils manpage writer. . bluez-5.82/client/PaxHeaders/bluetoothctl.10000644000000000000000000000005014773213517015730 xustar0020 atime=1743591247 20 ctime=1743591291 bluez-5.82/client/bluetoothctl.10000644000000000000000000002352314773213517015416 0ustar00rootroot.\" Man page generated from reStructuredText. . . .nr rst2man-indent-level 0 . .de1 rstReportMargin \\$1 \\n[an-margin] level \\n[rst2man-indent-level] level margin: \\n[rst2man-indent\\n[rst2man-indent-level]] - \\n[rst2man-indent0] \\n[rst2man-indent1] \\n[rst2man-indent2] .. .de1 INDENT .\" .rstReportMargin pre: . RS \\$1 . nr rst2man-indent\\n[rst2man-indent-level] \\n[an-margin] . nr rst2man-indent-level +1 .\" .rstReportMargin post: .. .de UNINDENT . RE .\" indent \\n[an-margin] .\" old: \\n[rst2man-indent\\n[rst2man-indent-level]] .nr rst2man-indent-level -1 .\" new: \\n[rst2man-indent\\n[rst2man-indent-level]] .in \\n[rst2man-indent\\n[rst2man-indent-level]]u .. .TH "BLUETOOTHCTL" "1" "March 2024" "BlueZ" "Linux System Administration" .SH NAME bluetoothctl \- Bluetooth Control Command Line Tool .SH SYNOPSIS .sp \fBbluetoothctl\fP [\fB\-a\fP \fIcapability\fP] [\fB\-e\fP] [\fB\-m\fP] [\fB\-t\fP \fIseconds\fP] [\fB\-v\fP] [\fB\-h\fP] .SH DESCRIPTION .sp \fBbluetoothctl(1)\fP interactive bluetooth control tool. The tool works with Bluetooth Classic (BR/EDR) and Bluetooth Low Energy (LE) controllers. .sp The tool is menu driven but can be automated from the command line. Examples are given in the automation section. .SH OPTIONS .INDENT 0.0 .TP .BI \-a \ capability\fR,\fB \ \-\-agent \ capability Register agent handler: .TP .B \-e\fP,\fB \-\-endpoints Register Media endpoints .TP .B \-m\fP,\fB \-\-monitor Enable monitor output .TP .BI \-t \ seconds\fR,\fB \ \-\-timeout \ seconds Timeout in seconds for non\-interactive mode .TP .B \-v\fP,\fB \-\-version Display version .TP .B \-h\fP,\fB \-\-help Display help .UNINDENT .SH COMMANDS .SS list .sp List available controllers. .INDENT 0.0 .TP .B Usage \fB> list\fP .UNINDENT .SS show .sp Controller information. .INDENT 0.0 .TP .B Usage \fB> show [ctrl]\fP .UNINDENT .SS select .sp Select default controller. .INDENT 0.0 .TP .B Usage \fB> select \fP .UNINDENT .SS devices .sp List available devices, with an optional property as the filter. .INDENT 0.0 .TP .B Usage \fB> devices [Paired/Bonded/Trusted/Connected]\fP .UNINDENT .SS system\-alias .sp Set controller alias. .INDENT 0.0 .TP .B Usage \fB> system\-alias \fP .UNINDENT .SS reset\-alias .sp Reset controller alias. .INDENT 0.0 .TP .B Usage \fB> reset\-alias\fP .UNINDENT .SS power .sp Set controller power. .sp When the controller is powered off, the USB port the controller is attached to is put into a suspend state. .INDENT 0.0 .TP .B Usage \fB> power \fP .UNINDENT .SS advertise .sp Enable/disable advertising with given type. .sp If you exit the program advertising will be disabled. .sp When advertising the controller should advertise with random address but may use its public address if it does not support the feature (address of the device). .sp A device can advertise if it initiated the connection to another advertising device. .INDENT 0.0 .TP .B Usage \fB> advertise \fP .UNINDENT .SS set\-alias .sp Set device alias. .INDENT 0.0 .TP .B Usage \fB> set\-alias \fP .UNINDENT .SS scan .sp Scan for devices. .sp For LE, scanning is an important requirement before connecting or pairing. .sp The purpose of scanning is to find devices that are advertising with their discoverable flag set (either limited or general). Once you have found the address then you can connect or pair. .sp Note the following when scanning: .INDENT 0.0 .INDENT 3.5 .INDENT 0.0 .IP \(bu 2 When scanning the controller will use a random address that is not resolvable so the public address is not leaked. A new random address is created every time scan on is used. .IP \(bu 2 When turning on scanning the device will start receiving advertising reports of what devices are advertising. .IP \(bu 2 The filtering of duplicate advertising reports may be enabled depending on the filtering settings. .IP \(bu 2 Device objects found during a scan session will only be persisted if they are connected/paired otherwise they are removed after some time. .UNINDENT .UNINDENT .UNINDENT .INDENT 0.0 .TP .B Usage \fB> scan \fP .UNINDENT .SS pair .sp Pair with device. .sp This will pair with a device and then trust and connect to it. If the device is already paired this will first remove the pairing. .sp The command can either be used while the controller is in the connected or not connected state. .sp If the controller is already connected then the pair command can be used without an arguments. If the controller is not connected, the pair command can be given the address of a device with an active scan report and it will initiate the connection before pairing. .sp Before pairing the agent must be selected to choose the authentication mechanism. .INDENT 0.0 .TP .B Usage \fB> pair \fP .UNINDENT .SS pairable .sp Set controller pairable mode. .sp This enables/disables pairing. If pairing is disabled then the controller will not accept any pairing requests. .INDENT 0.0 .TP .B Usage \fB> pairable \fP .UNINDENT .SS discoverable .sp Set discoverable mode. .sp This enables/disables discoverable mode. If discoverable is disabled then the controller will not respond to any scan requests. .sp In LE if discoverable if off the controller will just passively scan and not make scan requests to advertising devices. If on it will make the advertising requests. .sp It will use a random address if supported by the controller. The length of time \(dqdiscoverable on\(dq is valid is determined by discoverable\-timeout command. .INDENT 0.0 .TP .B Usage \fB> discoverable \fP .UNINDENT .SS discoverable\-timeout .sp Set discoverable timeout. .sp The time in seconds that \(dqdiscoverable on\(dq is valid. .INDENT 0.0 .TP .B Usage \fB> discoverable\-timeout [value]\fP .UNINDENT .SS agent .sp Enable/disable agent with given capability. .sp This chooses the local authentication mechanism of the controller. It is needed for pairing and allows you to choose the IO capabilities of the controller. .sp The valid agent capabilities are: DisplayOnly, DisplayYesNo, KeyboardDisplay, KeyboardOnly, NoInputNoOutput. .INDENT 0.0 .TP .B Usage \fB> agent \fP .UNINDENT .SS default\-agent .sp Set current agent as the default one. .sp After selecting the agent this will make it the default agent. .INDENT 0.0 .TP .B Usage \fB> default\-agent\fP .UNINDENT .SS trust .sp Trust device. .INDENT 0.0 .TP .B Usage \fB> trust \fP .UNINDENT .SS untrust .sp Untrust device. .INDENT 0.0 .TP .B Usage \fB> untrust \fP .UNINDENT .SS block .sp Block device. .INDENT 0.0 .TP .B Usage \fB> block \fP .UNINDENT .SS unblock .sp Unblock device .INDENT 0.0 .TP .B Usage \fB> unblock \fP .UNINDENT .SS remove .sp Remove device. .INDENT 0.0 .TP .B Usage \fB> remove \fP .UNINDENT .SS connect .sp Connect device. .sp This will initiate a connection to a device. .sp By default this commands tries to connect all the profiles the remote device supports and have been flagged as auto\-connectable. In case when the UUID of the remote service is given only that service will be connected. The UUID can be either a short form (16\-bit UUID) or a long form (128\-bit UUID). There are also some special values for well\-known profiles like \(dqa2dp\-sink\(dq, \(dqa2dp\-source\(dq, \(dqhfp\-hf\(dq, \(dqhfp\-ag\(dq, \(dqftp\(dq or \(dqspp\(dq. .sp To connect with an LE device the controller must have an active scan report of the device it wants to connect to. .sp If no advertising report is received before the timeout a le\-connection\-abort\-by\-local error will be issued. In that case either try again to connect assuming the device is advertising. .INDENT 0.0 .TP .B Usage \fB> connect [uuid]\fP .TP .B Example \fB> connect 1C:48:F9:9D:81:5C\fP .TP .B Example \fB> connect 1C:48:F9:9D:81:5C hsp\-hs\fP .TP .B Example \fB> connect 1C:48:F9:9D:81:5C 00001108\-0000\-1000\-8000\-00805f9b34fb\fP .TP .B Example \fB> connect 1C:48:F9:9D:81:5C 0x1108\fP .UNINDENT .SS disconnect .sp Disconnect device. .sp By default this commands disconnects all profiles and then terminates the connection. In case when the UUID of the remote service is given only that service will be disconnected. .sp For LE when disconnecting from an active connection the device address is not needed. .INDENT 0.0 .TP .B Usage \fB> disconnect [uuid]\fP .UNINDENT .SS info .sp Device information. .INDENT 0.0 .TP .B Usage \fB> info \fP .UNINDENT .SS bearer .sp Get/Set preferred bearer. .INDENT 0.0 .TP .B Usage \fB> bearer [last\-seen/bredr/le]\fP .TP .B Example get preferred bearer .nf > bearer .in +2 PreferredBearer: last\-seen .in -2 .fi .sp .TP .B Example set preferred bearer to LE .nf > bearer le [CHG] Device PreferredBearer: le Changing le succeeded .fi .sp .TP .B Example set preferred bearer to BREDR .nf > bearer bredr [CHG] Device PreferredBearer: bredr Changing bredr succeeded .fi .sp .UNINDENT .SH ADVERTISE SUBMENU .sp See \fBbluetoothctl\-advertise(1)\fP\&. .SH MONITOR SUBMENU .sp See \fBbluetoothctl\-monitor(1)\fP .SH SCAN SUBMENU .sp See \fBbluetoothctl\-scan(1)\fP .SH GATT SUBMENU .sp See \fBbluetoothctl\-gatt(1)\fP .SH ADMIN SUBMENU .sp See \fBbluetoothctl\-admin(1)\fP .SH PLAYER SUBMENU .sp See \fBbluetoothctl\-player(1)\fP .SH ENDPOINT SUBMENU .sp See \fBbluetoothctl\-endpoint(1)\fP .SH TRANSPORT SUBMENU .sp See \fBbluetoothctl\-transport(1)\fP .SH MANAGEMENT SUBMENU .sp See \fBbluetoothctl\-mgmt(1)\fP .SH ASSISTANT SUBMENU .sp See \fBbluetoothctl\-assistant(1)\fP .SH AUTOMATION .sp Two common ways to automate the tool are to use Here Docs or the program expect. Using Here Docs to show information about the Bluetooth controller. .INDENT 0.0 .INDENT 3.5 .sp .EX bluetoothctl < .SH REPORTING BUGS .sp .SH COPYRIGHT Free use of this software is granted under the terms of the GNU Lesser General Public Licenses (LGPL). .\" Generated by docutils manpage writer. . bluez-5.82/client/PaxHeaders/gatt.h0000644000000000000000000000005014447506754014254 xustar0020 atime=1743515768 20 ctime=1743591281 bluez-5.82/client/gatt.h0000644000000000000000000000453214447506754013741 0ustar00rootroot/* SPDX-License-Identifier: GPL-2.0-or-later */ /* * * BlueZ - Bluetooth protocol stack for Linux * * Copyright (C) 2014 Intel Corporation. All rights reserved. * * */ void gatt_add_service(GDBusProxy *proxy); void gatt_remove_service(GDBusProxy *proxy); void gatt_add_characteristic(GDBusProxy *proxy); void gatt_remove_characteristic(GDBusProxy *proxy); void gatt_add_descriptor(GDBusProxy *proxy); void gatt_remove_descriptor(GDBusProxy *proxy); void gatt_list_attributes(const char *device); GDBusProxy *gatt_select_attribute(GDBusProxy *parent, const char *path); char *gatt_attribute_generator(const char *text, int state); void gatt_read_attribute(GDBusProxy *proxy, int argc, char *argv[]); void gatt_write_attribute(GDBusProxy *proxy, int argc, char *argv[]); void gatt_notify_attribute(GDBusProxy *proxy, bool enable); void gatt_clone_attribute(GDBusProxy *proxy, int argc, char *argv[]); void gatt_acquire_write(GDBusProxy *proxy, const char *arg); void gatt_release_write(GDBusProxy *proxy, const char *arg); void gatt_acquire_notify(GDBusProxy *proxy, const char *arg); void gatt_release_notify(GDBusProxy *proxy, const char *arg); char *gatt_select_local_attribute(const char *arg); void gatt_read_local_attribute(char *data, int argc, char *argv[]); void gatt_write_local_attribute(char *data, int argc, char *argv[]); void gatt_add_manager(GDBusProxy *proxy); void gatt_remove_manager(GDBusProxy *proxy); void gatt_register_app(DBusConnection *conn, GDBusProxy *proxy, int argc, char *argv[]); void gatt_unregister_app(DBusConnection *conn, GDBusProxy *proxy); void gatt_register_service(DBusConnection *conn, GDBusProxy *proxy, int argc, char *argv[]); void gatt_unregister_service(DBusConnection *conn, GDBusProxy *proxy, int argc, char *argv[]); void gatt_register_chrc(DBusConnection *conn, GDBusProxy *proxy, int argc, char *argv[]); void gatt_unregister_chrc(DBusConnection *conn, GDBusProxy *proxy, int argc, char *argv[]); void gatt_register_desc(DBusConnection *conn, GDBusProxy *proxy, int argc, char *argv[]); void gatt_unregister_desc(DBusConnection *conn, GDBusProxy *proxy, int argc, char *argv[]); void gatt_register_include(DBusConnection *conn, GDBusProxy *proxy, int argc, char *argv[]); void gatt_unregister_include(DBusConnection *conn, GDBusProxy *proxy, int argc, char *argv[]); bluez-5.82/client/PaxHeaders/admin.h0000644000000000000000000000005014766002272014373 xustar0020 atime=1743515577 20 ctime=1743591281 bluez-5.82/client/admin.h0000644000000000000000000000032114766002272014050 0ustar00rootroot// SPDX-License-Identifier: GPL-2.0-or-later /* * * BlueZ - Bluetooth protocol stack for Linux * * Copyright (C) 2021 Google LLC * * */ void admin_add_submenu(void); void admin_remove_submenu(void); bluez-5.82/client/PaxHeaders/print.c0000644000000000000000000000005014711225433014426 xustar0020 atime=1743515774 20 ctime=1743591281 bluez-5.82/client/print.c0000644000000000000000000001263114711225433014112 0ustar00rootroot // SPDX-License-Identifier: GPL-2.0-or-later /* * * BlueZ - Bluetooth protocol stack for Linux * * Copyright (C) 2020 Intel Corporation. All rights reserved. * * */ #ifdef HAVE_CONFIG_H #include #endif #define _GNU_SOURCE #include #include #include #include #include #include #include #include #include "gdbus/gdbus.h" #include "src/shared/util.h" #include "src/shared/shell.h" #include "print.h" static void print_fixed_iter(const char *label, const char *name, DBusMessageIter *iter) { dbus_bool_t *valbool; dbus_uint32_t *valu32; dbus_uint16_t *valu16; dbus_int16_t *vals16; unsigned char *byte; int len; switch (dbus_message_iter_get_arg_type(iter)) { case DBUS_TYPE_BOOLEAN: dbus_message_iter_get_fixed_array(iter, &valbool, &len); if (len <= 0) return; bt_shell_printf("%s%s:\n", label, name); bt_shell_hexdump((void *)valbool, len * sizeof(*valbool)); break; case DBUS_TYPE_UINT32: dbus_message_iter_get_fixed_array(iter, &valu32, &len); if (len <= 0) return; bt_shell_printf("%s%s:\n", label, name); bt_shell_hexdump((void *)valu32, len * sizeof(*valu32)); break; case DBUS_TYPE_UINT16: dbus_message_iter_get_fixed_array(iter, &valu16, &len); if (len <= 0) return; bt_shell_printf("%s%s:\n", label, name); bt_shell_hexdump((void *)valu16, len * sizeof(*valu16)); break; case DBUS_TYPE_INT16: dbus_message_iter_get_fixed_array(iter, &vals16, &len); if (len <= 0) return; bt_shell_printf("%s%s:\n", label, name); bt_shell_hexdump((void *)vals16, len * sizeof(*vals16)); break; case DBUS_TYPE_BYTE: dbus_message_iter_get_fixed_array(iter, &byte, &len); if (len <= 0) return; bt_shell_printf("%s%s:\n", label, name); bt_shell_hexdump((void *)byte, len * sizeof(*byte)); break; default: return; }; } void print_iter(const char *label, const char *name, DBusMessageIter *iter) { dbus_bool_t valbool; dbus_uint32_t valu32; dbus_uint16_t valu16; dbus_int16_t vals16; unsigned char byte; const char *valstr; DBusMessageIter subiter; char *entry; if (iter == NULL) { bt_shell_printf("%s%s is nil\n", label, name); return; } switch (dbus_message_iter_get_arg_type(iter)) { case DBUS_TYPE_INVALID: bt_shell_printf("%s%s is invalid\n", label, name); break; case DBUS_TYPE_STRING: if (!strcasecmp(name, "UUID")) { dbus_message_iter_get_basic(iter, &valstr); print_uuid(label, name, valstr); break; } /* fall through */ case DBUS_TYPE_OBJECT_PATH: dbus_message_iter_get_basic(iter, &valstr); bt_shell_printf("%s%s: %s\n", label, name, valstr); break; case DBUS_TYPE_BOOLEAN: dbus_message_iter_get_basic(iter, &valbool); bt_shell_printf("%s%s: %s\n", label, name, valbool == TRUE ? "yes" : "no"); break; case DBUS_TYPE_UINT32: dbus_message_iter_get_basic(iter, &valu32); bt_shell_printf("%s%s: 0x%08x (%d)\n", label, name, valu32, valu32); break; case DBUS_TYPE_UINT16: dbus_message_iter_get_basic(iter, &valu16); bt_shell_printf("%s%s: 0x%04x (%d)\n", label, name, valu16, valu16); break; case DBUS_TYPE_INT16: dbus_message_iter_get_basic(iter, &vals16); bt_shell_printf("%s%s: 0x%04x (%d)\n", label, name, vals16, vals16); break; case DBUS_TYPE_BYTE: dbus_message_iter_get_basic(iter, &byte); bt_shell_printf("%s%s: 0x%02x (%d)\n", label, name, byte, byte); break; case DBUS_TYPE_VARIANT: dbus_message_iter_recurse(iter, &subiter); print_iter(label, name, &subiter); break; case DBUS_TYPE_ARRAY: dbus_message_iter_recurse(iter, &subiter); if (dbus_type_is_fixed( dbus_message_iter_get_arg_type(&subiter))) { print_fixed_iter(label, name, &subiter); break; } while (dbus_message_iter_get_arg_type(&subiter) != DBUS_TYPE_INVALID) { print_iter(label, name, &subiter); dbus_message_iter_next(&subiter); } break; case DBUS_TYPE_DICT_ENTRY: dbus_message_iter_recurse(iter, &subiter); if (dbus_message_iter_get_arg_type(&subiter) == DBUS_TYPE_STRING) { dbus_message_iter_get_basic(&subiter, &valstr); entry = g_strconcat(name, ".", valstr, NULL); } else { entry = g_strconcat(name, ".Key", NULL); print_iter(label, entry, &subiter); g_free(entry); entry = g_strconcat(name, ".Value", NULL); } dbus_message_iter_next(&subiter); print_iter(label, entry, &subiter); g_free(entry); break; default: bt_shell_printf("%s%s has unsupported type\n", label, name); break; } } void print_property_with_label(GDBusProxy *proxy, const char *name, const char *label) { DBusMessageIter iter; if (g_dbus_proxy_get_property(proxy, name, &iter) == FALSE) return; print_iter("\t", label ? label : name, &iter); } void print_property(GDBusProxy *proxy, const char *name) { print_property_with_label(proxy, name, NULL); } void print_uuid(const char *label, const char *name, const char *uuid) { const char *text; text = bt_uuidstr_to_str(uuid); if (text) { char str[26]; unsigned int n; str[sizeof(str) - 1] = '\0'; n = snprintf(str, sizeof(str), "%s", text); if (n > sizeof(str) - 1) { str[sizeof(str) - 2] = '.'; str[sizeof(str) - 3] = '.'; if (str[sizeof(str) - 4] == ' ') str[sizeof(str) - 4] = '.'; n = sizeof(str) - 1; } bt_shell_printf("%s%s: %s%*c(%s)\n", label, name, str, 26 - n, ' ', uuid); } else bt_shell_printf("%s%s: %*c(%s)\n", label, name, 26, ' ', uuid); } bluez-5.82/client/PaxHeaders/adv_monitor.c0000644000000000000000000000005014606723200015611 xustar0020 atime=1743515780 20 ctime=1743591281 bluez-5.82/client/adv_monitor.c0000644000000000000000000004625714606723200015310 0ustar00rootroot// SPDX-License-Identifier: GPL-2.0-or-later /* * * BlueZ - Bluetooth protocol stack for Linux * * Copyright (C) 2020 Google LLC * Copyright 2024 NXP * * */ #ifdef HAVE_CONFIG_H #include #endif #define _GNU_SOURCE #include #include #include #include #include #include "gdbus/gdbus.h" #include "src/shared/ad.h" #include "src/shared/util.h" #include "src/shared/shell.h" #include "adv_monitor.h" #define ADV_MONITOR_APP_PATH "/org/bluez/adv_monitor_app" #define ADV_MONITOR_INTERFACE "org.bluez.AdvertisementMonitor1" #define RSSI_UNSET_THRESHOLD 127 #define RSSI_UNSET_TIMEOUT 0 #define RSSI_UNSET_SAMPLING_PERIOD 256 struct rssi_setting { int16_t high_threshold; uint16_t high_timeout; int16_t low_threshold; uint16_t low_timeout; uint16_t sampling_period; }; struct pattern { uint8_t start_pos; uint8_t ad_data_type; uint8_t content_len; uint8_t content[BT_AD_MAX_DATA_LEN]; }; struct adv_monitor { uint8_t idx; char *path; char *type; struct rssi_setting *rssi; GSList *patterns; }; static struct adv_monitor_manager { GSList *supported_types; GSList *supported_features; GDBusProxy *proxy; gboolean app_registered; } manager = { NULL, NULL, NULL, FALSE }; static uint8_t adv_mon_idx; static GSList *adv_mons; static struct rssi_setting *current_rssi; static void remove_adv_monitor(void *data, void *user_data); static DBusMessage *release_adv_monitor(DBusConnection *conn, DBusMessage *msg, void *user_data) { struct adv_monitor *adv_monitor = user_data; bt_shell_printf("Advertisement monitor %d released\n", adv_monitor->idx); remove_adv_monitor(adv_monitor, conn); return dbus_message_new_method_return(msg); } static DBusMessage *activate_adv_monitor(DBusConnection *conn, DBusMessage *msg, void *user_data) { struct adv_monitor *adv_monitor = user_data; bt_shell_printf("Advertisement monitor %d activated\n", adv_monitor->idx); return dbus_message_new_method_return(msg); } static DBusMessage *device_found_adv_monitor(DBusConnection *conn, DBusMessage *msg, void *user_data) { struct adv_monitor *adv_monitor = user_data; const char *device; dbus_message_get_args(msg, NULL, DBUS_TYPE_OBJECT_PATH, &device, DBUS_TYPE_INVALID); bt_shell_printf("Advertisement monitor %d found device %s\n", adv_monitor->idx, device); return dbus_message_new_method_return(msg); } static DBusMessage *device_lost_adv_monitor(DBusConnection *conn, DBusMessage *msg, void *user_data) { struct adv_monitor *adv_monitor = user_data; const char *device; dbus_message_get_args(msg, NULL, DBUS_TYPE_OBJECT_PATH, &device, DBUS_TYPE_INVALID); bt_shell_printf("Advertisement monitor %d lost device %s\n", adv_monitor->idx, device); return dbus_message_new_method_return(msg); } static const GDBusMethodTable adv_monitor_methods[] = { { GDBUS_ASYNC_METHOD("Release", NULL, NULL, release_adv_monitor) }, { GDBUS_ASYNC_METHOD("Activate", NULL, NULL, activate_adv_monitor) }, { GDBUS_ASYNC_METHOD("DeviceFound", GDBUS_ARGS({ "device", "o" }), NULL, device_found_adv_monitor) }, { GDBUS_ASYNC_METHOD("DeviceLost", GDBUS_ARGS({ "device", "o" }), NULL, device_lost_adv_monitor) }, { } }; static gboolean get_type(const GDBusPropertyTable *property, DBusMessageIter *iter, void *user_data) { struct adv_monitor *adv_monitor = user_data; dbus_message_iter_append_basic(iter, DBUS_TYPE_STRING, &adv_monitor->type); return TRUE; } static gboolean get_low_threshold(const GDBusPropertyTable *property, DBusMessageIter *iter, void *user_data) { struct adv_monitor *adv_monitor = user_data; struct rssi_setting *rssi = adv_monitor->rssi; dbus_message_iter_append_basic(iter, DBUS_TYPE_INT16, &rssi->low_threshold); return TRUE; } static gboolean get_high_threshold(const GDBusPropertyTable *property, DBusMessageIter *iter, void *user_data) { struct adv_monitor *adv_monitor = user_data; struct rssi_setting *rssi = adv_monitor->rssi; dbus_message_iter_append_basic(iter, DBUS_TYPE_INT16, &rssi->high_threshold); return TRUE; } static gboolean get_low_timeout(const GDBusPropertyTable *property, DBusMessageIter *iter, void *user_data) { struct adv_monitor *adv_monitor = user_data; struct rssi_setting *rssi = adv_monitor->rssi; dbus_message_iter_append_basic(iter, DBUS_TYPE_UINT16, &rssi->low_timeout); return TRUE; } static gboolean get_high_timeout(const GDBusPropertyTable *property, DBusMessageIter *iter, void *user_data) { struct adv_monitor *adv_monitor = user_data; struct rssi_setting *rssi = adv_monitor->rssi; dbus_message_iter_append_basic(iter, DBUS_TYPE_UINT16, &rssi->high_timeout); return TRUE; } static gboolean get_sampling_period(const GDBusPropertyTable *property, DBusMessageIter *iter, void *user_data) { struct adv_monitor *adv_monitor = user_data; struct rssi_setting *rssi = adv_monitor->rssi; dbus_message_iter_append_basic(iter, DBUS_TYPE_UINT16, &rssi->sampling_period); return TRUE; } static gboolean low_threshold_exists(const GDBusPropertyTable *property, void *data) { struct adv_monitor *adv_monitor = data; return adv_monitor->rssi != NULL && adv_monitor->rssi->low_threshold != RSSI_UNSET_THRESHOLD; } static gboolean high_threshold_exists(const GDBusPropertyTable *property, void *data) { struct adv_monitor *adv_monitor = data; return adv_monitor->rssi != NULL && adv_monitor->rssi->high_threshold != RSSI_UNSET_THRESHOLD; } static gboolean low_timeout_exists(const GDBusPropertyTable *property, void *data) { struct adv_monitor *adv_monitor = data; return adv_monitor->rssi != NULL && adv_monitor->rssi->low_timeout != RSSI_UNSET_TIMEOUT; } static gboolean high_timeout_exists(const GDBusPropertyTable *property, void *data) { struct adv_monitor *adv_monitor = data; return adv_monitor->rssi != NULL && adv_monitor->rssi->high_timeout != RSSI_UNSET_TIMEOUT; } static gboolean sampling_period_exists(const GDBusPropertyTable *property, void *data) { struct adv_monitor *adv_monitor = data; return adv_monitor->rssi != NULL && adv_monitor->rssi->sampling_period != RSSI_UNSET_SAMPLING_PERIOD; } static void append_pattern_content_to_dbus(DBusMessageIter *iter, struct pattern *pattern) { DBusMessageIter data_iter; int idx; dbus_message_iter_open_container(iter, DBUS_TYPE_ARRAY, DBUS_TYPE_BYTE_AS_STRING, &data_iter); for (idx = 0; idx < pattern->content_len; idx++) dbus_message_iter_append_basic(&data_iter, DBUS_TYPE_BYTE, &pattern->content[idx]); dbus_message_iter_close_container(iter, &data_iter); } static void append_pattern_to_dbus(void *data, void *user_data) { struct pattern *pattern = data; DBusMessageIter *array_iter = user_data; DBusMessageIter data_iter; dbus_message_iter_open_container(array_iter, DBUS_TYPE_STRUCT, NULL, &data_iter); dbus_message_iter_append_basic(&data_iter, DBUS_TYPE_BYTE, &pattern->start_pos); dbus_message_iter_append_basic(&data_iter, DBUS_TYPE_BYTE, &pattern->ad_data_type); append_pattern_content_to_dbus(&data_iter, pattern); dbus_message_iter_close_container(array_iter, &data_iter); } static gboolean get_patterns(const GDBusPropertyTable *property, DBusMessageIter *iter, void *user_data) { struct adv_monitor *adv_monitor = user_data; DBusMessageIter array_iter; dbus_message_iter_open_container(iter, DBUS_TYPE_ARRAY, "(yyay)", &array_iter); g_slist_foreach(adv_monitor->patterns, append_pattern_to_dbus, &array_iter); dbus_message_iter_close_container(iter, &array_iter); return TRUE; } static gboolean pattern_exists(const GDBusPropertyTable *property, void *data) { struct adv_monitor *adv_monitor = data; return adv_monitor->patterns != NULL; } static const GDBusPropertyTable adv_monitor_props[] = { { "Type", "s", get_type }, { "RSSILowThreshold", "n", get_low_threshold, NULL, low_threshold_exists }, { "RSSIHighThreshold", "n", get_high_threshold, NULL, high_threshold_exists }, { "RSSILowTimeout", "q", get_low_timeout, NULL, low_timeout_exists }, { "RSSIHighTimeout", "q", get_high_timeout, NULL, high_timeout_exists }, { "RSSISamplingPeriod", "q", get_sampling_period, NULL, sampling_period_exists }, { "Patterns", "a(yyay)", get_patterns, NULL, pattern_exists }, { } }; static void set_supported_list(GSList **list, DBusMessageIter *iter) { char *str; DBusMessageIter subiter; dbus_message_iter_recurse(iter, &subiter); while (dbus_message_iter_get_arg_type(&subiter) == DBUS_TYPE_STRING) { dbus_message_iter_get_basic(&subiter, &str); *list = g_slist_append(*list, str); dbus_message_iter_next(&subiter); } } void adv_monitor_add_manager(DBusConnection *conn, GDBusProxy *proxy) { DBusMessageIter iter; if (manager.proxy != NULL || manager.supported_types != NULL || manager.supported_features != NULL) { bt_shell_printf("advertisement monitor manager already " "added\n"); return; } manager.proxy = proxy; if (g_dbus_proxy_get_property(proxy, "SupportedMonitorTypes", &iter)) set_supported_list(&(manager.supported_types), &iter); if (g_dbus_proxy_get_property(proxy, "SupportedFeatures", &iter)) set_supported_list(&(manager.supported_features), &iter); } void adv_monitor_remove_manager(DBusConnection *conn) { if (manager.supported_types != NULL) { g_slist_free(manager.supported_types); manager.supported_types = NULL; } if (manager.supported_features != NULL) { g_slist_free(manager.supported_features); manager.supported_features = NULL; } manager.proxy = NULL; manager.app_registered = FALSE; g_free(current_rssi); current_rssi = NULL; } static void register_setup(DBusMessageIter *iter, void *user_data) { const char *path = "/"; dbus_message_iter_append_basic(iter, DBUS_TYPE_OBJECT_PATH, &path); } static void register_reply(DBusMessage *message, void *user_data) { DBusError error; dbus_error_init(&error); if (!dbus_set_error_from_message(&error, message)) { bt_shell_printf("AdvertisementMonitor path registered\n"); return; } bt_shell_printf("Failed to register path: %s\n", error.name); dbus_error_free(&error); } static void unregister_setup(DBusMessageIter *iter, void *user_data) { const char *path = "/"; dbus_message_iter_append_basic(iter, DBUS_TYPE_OBJECT_PATH, &path); } static void unregister_reply(DBusMessage *message, void *user_data) { DBusError error; dbus_error_init(&error); if (!dbus_set_error_from_message(&error, message)) { bt_shell_printf("AdvertisementMonitor path unregistered\n"); return bt_shell_noninteractive_quit(EXIT_SUCCESS); } bt_shell_printf("Failed to unregister Advertisement Monitor:" " %s\n", error.name); dbus_error_free(&error); return bt_shell_noninteractive_quit(EXIT_FAILURE); } void adv_monitor_register_app(DBusConnection *conn) { if (manager.app_registered) { bt_shell_printf("Advertisement Monitor already registered\n"); return; } else if (manager.supported_types == NULL || !g_dbus_proxy_method_call(manager.proxy, "RegisterMonitor", register_setup, register_reply, NULL, NULL)) { bt_shell_printf("Failed to register Advertisement Monitor\n"); return; } manager.app_registered = TRUE; } void adv_monitor_unregister_app(DBusConnection *conn) { if (!manager.app_registered) { bt_shell_printf("Advertisement Monitor not registered\n"); return bt_shell_noninteractive_quit(EXIT_FAILURE); } else if (!g_dbus_proxy_method_call(manager.proxy, "UnregisterMonitor", unregister_setup, unregister_reply, NULL, NULL)) { bt_shell_printf("Failed to unregister Advertisement Monitor\n"); return bt_shell_noninteractive_quit(EXIT_FAILURE); } manager.app_registered = FALSE; } static void free_pattern(void *user_data) { struct pattern *p = user_data; g_free(p); } static void free_adv_monitor(void *user_data) { struct adv_monitor *adv_monitor = user_data; g_free(adv_monitor->path); g_free(adv_monitor->type); g_free(adv_monitor->rssi); g_slist_free_full(adv_monitor->patterns, free_pattern); g_free(adv_monitor); } static uint8_t str2bytearray(char *str, uint8_t *arr) { int idx, len = strlen(str), arr_len = 0; if (len%2 != 0) return 0; for (idx = 0; idx < len; idx += 2) { if (sscanf(str+idx, "%2hhx", &arr[arr_len++]) < 1) return 0; } return arr_len; } static struct pattern *parse_pattern(char *parameter_list[]) { struct pattern *pat; pat = g_malloc0(sizeof(struct pattern)); if (!pat) { bt_shell_printf("Failed to allocate pattern\n"); bt_shell_noninteractive_quit(EXIT_FAILURE); return NULL; } pat->start_pos = atoi(parameter_list[0]); pat->ad_data_type = atoi(parameter_list[1]); pat->content_len = str2bytearray(parameter_list[2], pat->content); if (pat->content_len == 0) { free_pattern(pat); return NULL; } return pat; } static GSList *parse_patterns(char *pattern_list[], int num) { GSList *patterns = NULL; int cnt; if (num == 0) { bt_shell_printf("No pattern provided\n"); return NULL; } if (num%3) { bt_shell_printf("Expected %d more arguments\n", 3 - num%3); return NULL; } for (cnt = 0; cnt < num; cnt += 3) { struct pattern *pattern; pattern = parse_pattern(pattern_list+cnt); if (pattern == NULL) { g_slist_free_full(patterns, free_pattern); return NULL; } patterns = g_slist_append(patterns, pattern); } return patterns; } static void remove_adv_monitor(void *data, void *user_data) { struct adv_monitor *adv_monitor = data; DBusConnection *conn = user_data; adv_mons = g_slist_remove(adv_mons, adv_monitor); g_dbus_unregister_interface(conn, adv_monitor->path, ADV_MONITOR_INTERFACE); } static gint cmp_adv_monitor_with_idx(gconstpointer a, gconstpointer b) { const struct adv_monitor *adv_monitor = a; uint8_t idx = *(uint8_t *)b; return adv_monitor->idx != idx; } static struct adv_monitor *find_adv_monitor_with_idx(uint8_t monitor_idx) { GSList *list; list = g_slist_find_custom(adv_mons, &monitor_idx, cmp_adv_monitor_with_idx); if (list) return (struct adv_monitor *)list->data; return NULL; } static void print_bytearray(char *prefix, uint8_t *arr, uint8_t len) { int idx; bt_shell_printf("%s", prefix); for (idx = 0; idx < len; idx++) bt_shell_printf("%02hhx", arr[idx]); bt_shell_printf("\n"); } static void print_adv_monitor(struct adv_monitor *adv_monitor) { GSList *l; bt_shell_printf("Advertisement Monitor %d\n", adv_monitor->idx); bt_shell_printf("\tpath: %s\n", adv_monitor->path); bt_shell_printf("\ttype: %s\n", adv_monitor->type); if (adv_monitor->rssi) { bt_shell_printf("\trssi:\n"); bt_shell_printf("\t\thigh threshold: %hd\n", adv_monitor->rssi->high_threshold); bt_shell_printf("\t\thigh threshold timeout: %hu\n", adv_monitor->rssi->high_timeout); bt_shell_printf("\t\tlow threshold: %hd\n", adv_monitor->rssi->low_threshold); bt_shell_printf("\t\tlow threshold timeout: %hu\n", adv_monitor->rssi->low_timeout); bt_shell_printf("\t\tsampling period: %hu\n", adv_monitor->rssi->sampling_period); } if (adv_monitor->patterns) { int idx = 1; for (l = adv_monitor->patterns; l; l = g_slist_next(l), idx++) { struct pattern *pattern = l->data; bt_shell_printf("\tpattern %d:\n", idx); bt_shell_printf("\t\tstart position: %hhu\n", pattern->start_pos); bt_shell_printf("\t\tAD data type: %hhu\n", pattern->ad_data_type); print_bytearray("\t\tcontent: ", pattern->content, pattern->content_len); } } } static struct rssi_setting *get_current_rssi(void) { if (current_rssi) return current_rssi; current_rssi = g_malloc0(sizeof(struct rssi_setting)); if (!current_rssi) bt_shell_printf("Failed to allocate rssi setting"); current_rssi->low_threshold = RSSI_UNSET_THRESHOLD; current_rssi->high_threshold = RSSI_UNSET_THRESHOLD; current_rssi->low_timeout = RSSI_UNSET_TIMEOUT; current_rssi->high_timeout = RSSI_UNSET_TIMEOUT; current_rssi->sampling_period = RSSI_UNSET_SAMPLING_PERIOD; return current_rssi; } void adv_monitor_set_rssi_threshold(int16_t low_threshold, int16_t high_threshold) { struct rssi_setting *rssi = get_current_rssi(); if (!rssi) return; rssi->low_threshold = low_threshold; rssi->high_threshold = high_threshold; } void adv_monitor_set_rssi_timeout(uint16_t low_timeout, uint16_t high_timeout) { struct rssi_setting *rssi = get_current_rssi(); if (!rssi) return; rssi->low_timeout = low_timeout; rssi->high_timeout = high_timeout; } void adv_monitor_set_rssi_sampling_period(uint16_t sampling) { struct rssi_setting *rssi = get_current_rssi(); if (!rssi) return; rssi->sampling_period = sampling; } void adv_monitor_add_monitor(DBusConnection *conn, char *type, int argc, char *argv[]) { struct adv_monitor *adv_monitor; GSList *patterns = NULL; if (g_slist_length(adv_mons) >= UINT8_MAX) { bt_shell_printf("Number of advertisement monitor exceeds " "the limit"); return; } while (find_adv_monitor_with_idx(adv_mon_idx)) adv_mon_idx += 1; patterns = parse_patterns(argv+1, argc-1); if (patterns == NULL) { bt_shell_printf("pattern-list malformed\n"); return; } adv_monitor = g_malloc0(sizeof(struct adv_monitor)); if (!adv_monitor) { bt_shell_printf("Failed to allocate adv_monitor"); return bt_shell_noninteractive_quit(EXIT_FAILURE); } adv_monitor->idx = adv_mon_idx; adv_monitor->type = g_strdup(type); adv_monitor->rssi = current_rssi; adv_monitor->patterns = patterns; adv_monitor->path = g_strdup_printf("%s/%hhu", ADV_MONITOR_APP_PATH, adv_mon_idx); current_rssi = NULL; if (g_dbus_register_interface(conn, adv_monitor->path, ADV_MONITOR_INTERFACE, adv_monitor_methods, NULL, adv_monitor_props, adv_monitor, free_adv_monitor) == FALSE) { bt_shell_printf("Failed to register advertisement monitor\n"); free_adv_monitor(adv_monitor); return bt_shell_noninteractive_quit(EXIT_FAILURE); } adv_mons = g_slist_append(adv_mons, adv_monitor); bt_shell_printf("Advertisement Monitor %d added\n", adv_monitor->idx); } void adv_monitor_print_monitor(DBusConnection *conn, int monitor_idx) { struct adv_monitor *adv_monitor; GSList *l; if (monitor_idx < 0) { for (l = adv_mons; l; l = g_slist_next(l)) { adv_monitor = l->data; print_adv_monitor(adv_monitor); } return; } adv_monitor = find_adv_monitor_with_idx(monitor_idx); if (adv_monitor == NULL) { bt_shell_printf("Can't find monitor with index %d\n", monitor_idx); return; } print_adv_monitor(adv_monitor); } void adv_monitor_remove_monitor(DBusConnection *conn, int monitor_idx) { struct adv_monitor *adv_monitor; if (monitor_idx < 0) { g_slist_foreach(adv_mons, remove_adv_monitor, conn); return; } adv_monitor = find_adv_monitor_with_idx(monitor_idx); if (adv_monitor == NULL) { bt_shell_printf("Can't find monitor with index %d\n", monitor_idx); return; } remove_adv_monitor(adv_monitor, conn); bt_shell_printf("Monitor %d deleted\n", monitor_idx); } static void print_supported_list(GSList *list) { GSList *iter; for (iter = list; iter; iter = g_slist_next(iter)) { char *data = iter->data; printf(" %s", data); } } void adv_monitor_get_supported_info(void) { bt_shell_printf("Supported Features:"); print_supported_list(manager.supported_features); bt_shell_printf("\n"); bt_shell_printf("Supported Moniter Types:"); print_supported_list(manager.supported_types); bt_shell_printf("\n"); } bluez-5.82/client/PaxHeaders/bluetoothctl-monitor.10000644000000000000000000000005014773213522017411 xustar0020 atime=1743591250 20 ctime=1743591291 bluez-5.82/client/bluetoothctl-monitor.10000644000000000000000000000446414773213522017102 0ustar00rootroot.\" Man page generated from reStructuredText. . . .nr rst2man-indent-level 0 . .de1 rstReportMargin \\$1 \\n[an-margin] level \\n[rst2man-indent-level] level margin: \\n[rst2man-indent\\n[rst2man-indent-level]] - \\n[rst2man-indent0] \\n[rst2man-indent1] \\n[rst2man-indent2] .. .de1 INDENT .\" .rstReportMargin pre: . RS \\$1 . nr rst2man-indent\\n[rst2man-indent-level] \\n[an-margin] . nr rst2man-indent-level +1 .\" .rstReportMargin post: .. .de UNINDENT . RE .\" indent \\n[an-margin] .\" old: \\n[rst2man-indent\\n[rst2man-indent-level]] .nr rst2man-indent-level -1 .\" new: \\n[rst2man-indent\\n[rst2man-indent-level]] .in \\n[rst2man-indent\\n[rst2man-indent-level]]u .. .TH "BLUETOOTHCTL-MONITOR" "1" "July 2023" "BlueZ" "Linux System Administration" .SH NAME bluetoothctl-monitor \- Monitor Submenu .SH SYNOPSIS .sp \fBbluetoothctl\fP [\-\-options] [monitor.commands] .SH MONITOR COMMANDS .SS set\-rssi\-threshold .sp Set RSSI threshold parameter .INDENT 0.0 .TP .B Usage \fB> set\-rssi\-threshold \fP .UNINDENT .SS set\-rssi\-timeout .sp Set RSSI timeout parameter .INDENT 0.0 .TP .B Usage \fB> set\-rssi\-timeout \fP .UNINDENT .SS set\-rssi\-sampling\-period .sp Set RSSI sampling period parameter .INDENT 0.0 .TP .B Usage \fB> set\-rssi\-timeout \fP .UNINDENT .SS add\-or\-pattern .sp Register \(aqor pattern\(aq type monitor with the specified RSSI parameters .INDENT 0.0 .TP .B Usage \fB> add\-or\-pattern [patterns=pattern1 pattern2 ...]\fP .UNINDENT .SS get\-pattern .sp Get advertisement monitor .INDENT 0.0 .TP .B Usage \fB> get\-pattern \fP .UNINDENT .SS remove\-pattern .sp Remove advertisement monitor .INDENT 0.0 .TP .B Usage \fB> remove\-pattern \fP .UNINDENT .SS get\-supported\-info .sp Get advertisement manager supported features and supported monitor types .INDENT 0.0 .TP .B Usage \fB> get\-supported\-info\fP .UNINDENT .SS print\-usage .sp Print the command usage .INDENT 0.0 .TP .B Usage \fB> print\-usage \fP .UNINDENT .SH RESOURCES .sp .SH REPORTING BUGS .sp .SH COPYRIGHT Free use of this software is granted under the terms of the GNU Lesser General Public Licenses (LGPL). .\" Generated by docutils manpage writer. . bluez-5.82/client/PaxHeaders/bluetoothctl-hci.rst0000644000000000000000000000005014766002272017135 xustar0020 atime=1743515577 20 ctime=1743591291 bluez-5.82/client/bluetoothctl-hci.rst0000644000000000000000000000245314766002272016622 0ustar00rootroot================ bluetoothctl-hci ================ ----------- HCI Submenu ----------- :Version: BlueZ :Copyright: Free use of this software is granted under the terms of the GNU Lesser General Public Licenses (LGPL). :Date: December 2024 :Manual section: 1 :Manual group: Linux System Administration SYNOPSIS ======== **bluetoothctl** [--options] [hci.commands] Commands ======== open ---- Open HCI channel. :Usage: **> open ** :Example open user channel: | In order to open a user channel the controller needs to be power off | first: | > power off | > hci.open 0 user | HCI index 0 user channel opened cmd --- Send HCI command. :Usage: **> cmd [parameters...]** :Example send HCI Reset command: | > hci.cmd 0x0c03 | HCI Command complete: | 00 send ---- Send HCI data packet. :Usage: **> send [data...]** :Example send ACL data packet to connection handle 0x0000: | > hci.send acl 0x0000 register -------- Register HCI event handler. :Usage: **> register ** unregister ---------- Unregister HCI event handler. :Usage: **> unregister ** close ----- Close HCI channel. :Usage: **> close ** RESOURCES ========= http://www.bluez.org REPORTING BUGS ============== linux-bluetooth@vger.kernel.org bluez-5.82/client/PaxHeaders/assistant.c0000644000000000000000000000005014711225433015303 xustar0020 atime=1743515813 20 ctime=1743591281 bluez-5.82/client/assistant.c0000644000000000000000000002210114711225433014760 0ustar00rootroot// SPDX-License-Identifier: GPL-2.0-or-later /* * * BlueZ - Bluetooth protocol stack for Linux * * Copyright 2024 NXP * * */ #ifdef HAVE_CONFIG_H #include #endif #define _GNU_SOURCE #include #include #include #include #include #include #include #include #include #include "gdbus/gdbus.h" #include "lib/bluetooth.h" #include "lib/uuid.h" #include "src/shared/util.h" #include "src/shared/shell.h" #include "src/shared/io.h" #include "src/shared/queue.h" #include "print.h" #include "assistant.h" /* String display constants */ #define COLORED_NEW COLOR_GREEN "NEW" COLOR_OFF #define COLORED_CHG COLOR_YELLOW "CHG" COLOR_OFF #define COLORED_DEL COLOR_RED "DEL" COLOR_OFF #define MEDIA_ASSISTANT_INTERFACE "org.bluez.MediaAssistant1" #define BCODE_LEN 16 struct assistant_config { GDBusProxy *proxy; /* DBus object reference */ struct iovec *meta; /* Stream metadata LTVs */ struct bt_iso_qos qos; /* Stream QoS parameters */ }; static DBusConnection *dbus_conn; static GList *assistants; static char *proxy_description(GDBusProxy *proxy, const char *title, const char *description) { const char *path; path = g_dbus_proxy_get_path(proxy); return g_strdup_printf("%s%s%s%s %s ", description ? "[" : "", description ? : "", description ? "] " : "", title, path); } static void print_assistant(GDBusProxy *proxy, const char *description) { char *str; str = proxy_description(proxy, "Assistant", description); bt_shell_printf("%s\n", str); g_free(str); } static void assistant_added(GDBusProxy *proxy) { assistants = g_list_append(assistants, proxy); print_assistant(proxy, COLORED_NEW); } static void proxy_added(GDBusProxy *proxy, void *user_data) { const char *interface; interface = g_dbus_proxy_get_interface(proxy); if (!strcmp(interface, MEDIA_ASSISTANT_INTERFACE)) assistant_added(proxy); } static void assistant_removed(GDBusProxy *proxy) { assistants = g_list_remove(assistants, proxy); print_assistant(proxy, COLORED_DEL); } static void proxy_removed(GDBusProxy *proxy, void *user_data) { const char *interface; interface = g_dbus_proxy_get_interface(proxy); if (!strcmp(interface, MEDIA_ASSISTANT_INTERFACE)) assistant_removed(proxy); } static void assistant_property_changed(GDBusProxy *proxy, const char *name, DBusMessageIter *iter) { char *str; str = proxy_description(proxy, "Assistant", COLORED_CHG); print_iter(str, name, iter); g_free(str); } static void property_changed(GDBusProxy *proxy, const char *name, DBusMessageIter *iter, void *user_data) { const char *interface; interface = g_dbus_proxy_get_interface(proxy); if (!strcmp(interface, MEDIA_ASSISTANT_INTERFACE)) assistant_property_changed(proxy, name, iter); } static void assistant_unregister(void *data) { GDBusProxy *proxy = data; bt_shell_printf("Assistant %s unregistered\n", g_dbus_proxy_get_path(proxy)); } static void disconnect_handler(DBusConnection *connection, void *user_data) { g_list_free_full(assistants, assistant_unregister); assistants = NULL; } static uint8_t *str2bytearray(char *arg, size_t *val_len) { uint8_t value[UINT8_MAX]; char *entry; unsigned int i; for (i = 0; (entry = strsep(&arg, " \t")) != NULL; i++) { long val; char *endptr = NULL; if (*entry == '\0') continue; if (i >= G_N_ELEMENTS(value)) { bt_shell_printf("Too much data\n"); return NULL; } val = strtol(entry, &endptr, 0); if (!endptr || *endptr != '\0' || val > UINT8_MAX) { bt_shell_printf("Invalid value at index %d\n", i); return NULL; } value[i] = val; } *val_len = i; return util_memdup(value, i); } static void append_qos(DBusMessageIter *iter, struct assistant_config *cfg) { DBusMessageIter entry, var, dict; const char *key = "QoS"; const char *bcode_key = "BCode"; uint8_t *bcode = cfg->qos.bcast.bcode; dbus_message_iter_open_container(iter, DBUS_TYPE_DICT_ENTRY, NULL, &entry); dbus_message_iter_append_basic(&entry, DBUS_TYPE_STRING, &key); dbus_message_iter_open_container(&entry, DBUS_TYPE_VARIANT, "a{sv}", &var); dbus_message_iter_open_container(&var, DBUS_TYPE_ARRAY, "{sv}", &dict); g_dbus_dict_append_basic_array(&dict, DBUS_TYPE_STRING, &bcode_key, DBUS_TYPE_BYTE, &bcode, BCODE_LEN); dbus_message_iter_close_container(&var, &dict); dbus_message_iter_close_container(&entry, &var); dbus_message_iter_close_container(iter, &entry); } static void push_setup(DBusMessageIter *iter, void *user_data) { struct assistant_config *cfg = user_data; DBusMessageIter dict; const char *meta = "Metadata"; dbus_message_iter_open_container(iter, DBUS_TYPE_ARRAY, "{sv}", &dict); if (cfg->meta) g_dbus_dict_append_basic_array(&dict, DBUS_TYPE_STRING, &meta, DBUS_TYPE_BYTE, &cfg->meta->iov_base, cfg->meta->iov_len); if (cfg->qos.bcast.encryption) append_qos(&dict, cfg); dbus_message_iter_close_container(iter, &dict); } static void push_reply(DBusMessage *message, void *user_data) { struct assistant_config *cfg = user_data; DBusError error; dbus_error_init(&error); if (dbus_set_error_from_message(&error, message)) { bt_shell_printf("Failed to push assistant: %s\n", error.name); dbus_error_free(&error); return bt_shell_noninteractive_quit(EXIT_FAILURE); } bt_shell_printf("Assistant %s pushed\n", g_dbus_proxy_get_path(cfg->proxy)); free(cfg->meta); g_free(cfg); return bt_shell_noninteractive_quit(EXIT_SUCCESS); } static void assistant_set_bcode_cfg(const char *input, void *user_data) { struct assistant_config *cfg = user_data; if (!strcasecmp(input, "a") || !strcasecmp(input, "auto")) { memset(cfg->qos.bcast.bcode, 0, BCODE_LEN); } else { if (strlen(input) > BCODE_LEN) { bt_shell_printf("Input string too long %s\n", input); goto fail; } memcpy(cfg->qos.bcast.bcode, input, strlen(input)); } if (!g_dbus_proxy_method_call(cfg->proxy, "Push", push_setup, push_reply, cfg, NULL)) { bt_shell_printf("Failed to push assistant\n"); goto fail; } return; fail: free(cfg->meta); g_free(cfg); return bt_shell_noninteractive_quit(EXIT_FAILURE); } static void assistant_set_metadata_cfg(const char *input, void *user_data) { struct assistant_config *cfg = user_data; DBusMessageIter iter, dict, entry, value; const char *key; if (!strcasecmp(input, "a") || !strcasecmp(input, "auto")) goto done; if (!cfg->meta) cfg->meta = g_new0(struct iovec, 1); cfg->meta->iov_base = str2bytearray((char *) input, &cfg->meta->iov_len); if (!cfg->meta->iov_base) { free(cfg->meta); cfg->meta = NULL; } done: /* Get QoS property to check if the stream is encrypted */ if (!g_dbus_proxy_get_property(cfg->proxy, "QoS", &iter)) goto fail; if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_ARRAY) goto fail; dbus_message_iter_recurse(&iter, &dict); if (dbus_message_iter_get_arg_type(&dict) != DBUS_TYPE_DICT_ENTRY) goto fail; dbus_message_iter_recurse(&dict, &entry); dbus_message_iter_get_basic(&entry, &key); if (strcasecmp(key, "Encryption") != 0) goto fail; dbus_message_iter_next(&entry); dbus_message_iter_recurse(&entry, &value); if (dbus_message_iter_get_arg_type(&value) != DBUS_TYPE_BYTE) goto fail; dbus_message_iter_get_basic(&value, &cfg->qos.bcast.encryption); if (cfg->qos.bcast.encryption) /* Prompt user to enter the Broadcast Code to decrypt * the stream */ bt_shell_prompt_input("Assistant", "Enter Broadcast Code (auto/value):", assistant_set_bcode_cfg, cfg); else if (!g_dbus_proxy_method_call(cfg->proxy, "Push", push_setup, push_reply, cfg, NULL)) { bt_shell_printf("Failed to push assistant\n"); goto fail; } return; fail: free(cfg->meta); g_free(cfg); return bt_shell_noninteractive_quit(EXIT_FAILURE); } static void cmd_push_assistant(int argc, char *argv[]) { struct assistant_config *cfg; cfg = new0(struct assistant_config, 1); if (!cfg) goto fail; /* Search for DBus object */ cfg->proxy = g_dbus_proxy_lookup(assistants, NULL, argv[1], MEDIA_ASSISTANT_INTERFACE); if (!cfg->proxy) { bt_shell_printf("Assistant %s not found\n", argv[1]); goto fail; } /* Prompt user to enter metadata */ bt_shell_prompt_input("Assistant", "Enter Metadata (auto/value):", assistant_set_metadata_cfg, cfg); return; fail: g_free(cfg); return bt_shell_noninteractive_quit(EXIT_FAILURE); } static const struct bt_shell_menu assistant_menu = { .name = "assistant", .desc = "Media Assistant Submenu", .entries = { { "push", "", cmd_push_assistant, "Send stream information to peer" }, {} }, }; static GDBusClient * client; void assistant_add_submenu(void) { bt_shell_add_submenu(&assistant_menu); dbus_conn = bt_shell_get_env("DBUS_CONNECTION"); if (!dbus_conn || client) return; client = g_dbus_client_new(dbus_conn, "org.bluez", "/org/bluez"); g_dbus_client_set_proxy_handlers(client, proxy_added, proxy_removed, property_changed, NULL); g_dbus_client_set_disconnect_watch(client, disconnect_handler, NULL); } void assistant_remove_submenu(void) { g_dbus_client_unref(client); client = NULL; } bluez-5.82/client/PaxHeaders/bluetoothctl-gatt.rst0000644000000000000000000000005014766002272017331 xustar0020 atime=1743515577 20 ctime=1743591291 bluez-5.82/client/bluetoothctl-gatt.rst0000644000000000000000000000546114766002272017020 0ustar00rootroot================= bluetoothctl-gatt ================= ------------------------- Generic Attribute Submenu ------------------------- :Version: BlueZ :Copyright: Free use of this software is granted under the terms of the GNU Lesser General Public Licenses (LGPL). :Date: November 2022 :Manual section: 1 :Manual group: Linux System Administration SYNOPSIS ======== **bluetoothctl** [--options] [gatt.commands] Generic Attribute Commands ========================== list-attributes --------------- List attributes. :Usage: **> list-attributes ** select-attribute ---------------- Select attribute. :Usage: **> select-attribute ** attribute-info -------------- Select attribute. :Usage: **> attribute-info [attribute/UUID]** read ---- Read attribute value. :Usage: **> read [offset]** write ----- Write attribute value. :Usage: **> write [offset] [type]** acquire-write ------------- Acquire Write file descriptor. :Usage: **> acquire-write** release-write ------------- Release Write file descriptor. :Usage: **> release-write** acquire-notify -------------- Acquire Notify file descriptor. :Usage: **> acquire-notify** release-notify -------------- Release Notify file descriptor. :Usage: **> release-notify** notify ------ Notify attribute value. :Usage: **> notify ** clone ----- Clone a device or attribute. :Usage: **> clone [dev/attribute/UUID]** register-application -------------------- Register application. :Usage: **> register-application [UUID ...]** unregister-application ---------------------- Unregister application :Usage: **> unregister-application** register-service ---------------- Register application service. :Usage: **> register-service [handle]** unregister-service ------------------ Unregister application service :Usage: **> unregister-service ** register-includes ----------------- Register as Included service. :Usage: **>r egister-includes [handle]** unregister-includes ------------------- Unregister Included service. :Usage: **> unregister-includes ** register-characteristic ----------------------- Register service characteristic. :Usage: **> register-characteristic [handle]** unregister-characteristic ------------------------- Unregister service characteristic. :Usage: **> unregister-characteristic ** register-descriptor ------------------- Register characteristic descriptor. :Usage: **> register-descriptor [handle]** unregister-descriptor --------------------- Unregister characteristic descriptor. :Usage: **> unregister-descriptor ** RESOURCES ========= http://www.bluez.org REPORTING BUGS ============== linux-bluetooth@vger.kernel.org bluez-5.82/client/PaxHeaders/bluetoothctl-hci.10000644000000000000000000000005014773213534016470 xustar0020 atime=1743591260 20 ctime=1743591291 bluez-5.82/client/bluetoothctl-hci.10000644000000000000000000000416214773213534016154 0ustar00rootroot.\" Man page generated from reStructuredText. . . .nr rst2man-indent-level 0 . .de1 rstReportMargin \\$1 \\n[an-margin] level \\n[rst2man-indent-level] level margin: \\n[rst2man-indent\\n[rst2man-indent-level]] - \\n[rst2man-indent0] \\n[rst2man-indent1] \\n[rst2man-indent2] .. .de1 INDENT .\" .rstReportMargin pre: . RS \\$1 . nr rst2man-indent\\n[rst2man-indent-level] \\n[an-margin] . nr rst2man-indent-level +1 .\" .rstReportMargin post: .. .de UNINDENT . RE .\" indent \\n[an-margin] .\" old: \\n[rst2man-indent\\n[rst2man-indent-level]] .nr rst2man-indent-level -1 .\" new: \\n[rst2man-indent\\n[rst2man-indent-level]] .in \\n[rst2man-indent\\n[rst2man-indent-level]]u .. .TH "BLUETOOTHCTL-HCI" "1" "December 2024" "BlueZ" "Linux System Administration" .SH NAME bluetoothctl-hci \- HCI Submenu .SH SYNOPSIS .sp \fBbluetoothctl\fP [\-\-options] [hci.commands] .SH COMMANDS .SS open .sp Open HCI channel. .INDENT 0.0 .TP .B Usage \fB> open \fP .TP .B Example open user channel .nf In order to open a user channel the controller needs to be power off first: > power off > hci.open 0 user HCI index 0 user channel opened .fi .sp .UNINDENT .SS cmd .sp Send HCI command. .INDENT 0.0 .TP .B Usage \fB> cmd [parameters...]\fP .TP .B Example send HCI Reset command .nf > hci.cmd 0x0c03 HCI Command complete: .in +2 00 .in -2 .fi .sp .UNINDENT .SS send .sp Send HCI data packet. .INDENT 0.0 .TP .B Usage \fB> send [data...]\fP .TP .B Example send ACL data packet to connection handle 0x0000 .nf > hci.send acl 0x0000 .fi .sp .UNINDENT .SS register .sp Register HCI event handler. .INDENT 0.0 .TP .B Usage \fB> register \fP .UNINDENT .SS unregister .sp Unregister HCI event handler. .INDENT 0.0 .TP .B Usage \fB> unregister \fP .UNINDENT .SS close .sp Close HCI channel. .INDENT 0.0 .TP .B Usage \fB> close \fP .UNINDENT .SH RESOURCES .sp .SH REPORTING BUGS .sp .SH COPYRIGHT Free use of this software is granted under the terms of the GNU Lesser General Public Licenses (LGPL). .\" Generated by docutils manpage writer. . bluez-5.82/client/PaxHeaders/bluetoothctl-endpoint.rst0000644000000000000000000000005014766002272020212 xustar0020 atime=1743515577 20 ctime=1743591291 bluez-5.82/client/bluetoothctl-endpoint.rst0000644000000000000000000001200314766002272017667 0ustar00rootroot===================== bluetoothctl-endpoint ===================== ---------------- Endpoint Submenu ---------------- :Version: BlueZ :Copyright: Free use of this software is granted under the terms of the GNU Lesser General Public Licenses (LGPL). :Date: November 2022 :Manual section: 1 :Manual group: Linux System Administration SYNOPSIS ======== **bluetoothctl** [--options] [endpoint.commands] Endpoint Commands ================= list ---- List available endpoints. :Usage: **> list [local]** show ---- Endpoint information. :Usage: **> show [endpoint]** register -------- Register Endpoint. :Usage: **> register [capabilities...]** :Example LC3 BAP source: | **>endpoint.register 00002bcb-0000-1000-8000-00805f9b34fb 0x06** | **>Auto Accept (yes/no):** y | **>Max Transports (auto/value):** a | **>Locations:** a | **>Supported Context (value):** 3 | **>Context (value):** 3 | **>CIG (auto/value):** a | **>CIS (auto/value):** a :Example LC3 BAP sink with extra capabilities: | **>endpoint.register 00002bc9-0000-1000-8000-00805f9b34fb 0x06 "0x03 0xe5 0x03 0x00 0x02 0xe6 0x07"** | **>Enter Metadata (value/no):** n | **>Auto Accept (yes/no):** y | **>Max Transports (auto/value):** a | **>Locations:** a | **>Supported Context (value):** 3 | **>Context (value):** 3 | **>CIG (auto/value):** a | **>CIS (auto/value):** a :Example LC3 BAP Broadcast source: | **>endpoint.register 00001852-0000-1000-8000-00805f9b34fb 0x06** | **>Auto Accept (yes/no):** y | **>Max Transports (auto/value):** a | **>Locations:** 3 | **>Supported Context (value):** 1 :Example LC3 BAP Broadcast sink: | **>endpoint.register 00001851-0000-1000-8000-00805f9b34fb 0x06** | **>Auto Accept (yes/no):** y | **>Max Transports (auto/value):** a | **>Locations:** 3 | **>Supported Context (value):** 1 Note: If running the setup with an audio server that has LE Audio support (such as PipeWire) it will automatically register endpoints according to the configured roles. For more details about configuring a Broadcast Source with PipeWire check: https://gitlab.freedesktop.org/pipewire/pipewire/-/blob/master/doc/dox/config/pipewire-props.7.md unregister ---------- Unregister Endpoint. :Usage: **> unregister ** Note: If the endpoint was registered by an audio server, it can't be unregistered from bluetoothctl. This must be done by the audio server as well. config ------ Configure Endpoint. :Usage: **> config [preset]** Note: If the endpoint was registered by an audio server, it can't be configured from bluetoothctl. This must be done by the audio server as well. presets ------- List available presets. :Usage: **> presets / [codec[:company]] [preset] [codec config] [metadata]** :Example using endpoint: | **>presets /local/endpoint/ep0 32_1_1** | **>presets /local/endpoint/ep0** | Preset 32_1_1 | Configuration.**>0: len 0x02 type 0x01 | Configuration.Sampling Frequency: 32 Khz (0x06) | Configuration.**>1: len 0x02 type 0x02 | Configuration.Frame Duration: 7.5 ms (0x00) | Configuration.**>2: len 0x03 type 0x04 | Configuration.Frame Length: 60 (0x003c) :Example using UUID: | **>presets 00002bc9-0000-1000-8000-00805f9b34fb 0x06 32_1_1** | **>presets 00002bc9-0000-1000-8000-00805f9b34fb 0x06** | ... | ***32_1_1** :Example setting up LC3 custom preset: | **>presets 00002bc9-0000-1000-8000-00805f9b34fb 0x06 custom** | **>[Codec] Enter frequency (Khz):** 48 | **>[Codec] Enter frame duration (ms):** 10 | **>[Codec] Enter channel allocation:** 3 | **>[Codec] Enter frame length:** 100 | **>[QoS] Enter Target Latency (Low, Balance, High):** Low | **>[QoS] Enter SDU Interval (us):** 1000 | **>[QoS] Enter Framing (Unframed, Framed):** Unframed | **>[QoS] Enter PHY (1M, 2M):** 2M | **>[QoS] Enter Max SDU:** 200 | **>[QoS] Enter RTN:** 3 | **>[QoS] Enter Max Transport Latency (ms):** 10 | **>[QoS] Enter Presentation Delay (us):** 20000 | **>presets 00002bc9-0000-1000-8000-00805f9b34fb 0x06** | ... | ***custom** :Example setting up LC3 custom preset with extra configuration: | **>presets 00002bc9-0000-1000-8000-00805f9b34fb 0x06 custom "0x03 0xe8 0x00 0x00 0x02 0xe9 0x00"** | **>[Codec] Enter frequency (Khz):** 48 | **>[Codec] Enter frame duration (ms):** 10 | **>[Codec] Enter channel allocation:** 3 | **>[Codec] Enter frame length:** 100 | **>[QoS] Enter Target Latency (Low, Balance, High):** Low | **>[QoS] Enter SDU Interval (us):** 1000 | **>[QoS] Enter Framing (Unframed, Framed):** Unframed | **>[QoS] Enter PHY (1M, 2M):** 2M | **>[QoS] Enter Max SDU:** 200 | **>[QoS] Enter RTN:** 3 | **>[QoS] Enter Max Transport Latency (ms):** 10 | **>[QoS] Enter Presentation Delay (us):** 20000 | **>presets 00002bc9-0000-1000-8000-00805f9b34fb 0x06** | ... | ***custom** RESOURCES ========= http://www.bluez.org REPORTING BUGS ============== linux-bluetooth@vger.kernel.org bluez-5.82/client/PaxHeaders/hci.c0000644000000000000000000000005014766002272014041 xustar0020 atime=1743515577 20 ctime=1743591281 bluez-5.82/client/hci.c0000644000000000000000000001712214766002272013525 0ustar00rootroot// SPDX-License-Identifier: GPL-2.0-or-later /* * * BlueZ - Bluetooth protocol stack for Linux * * Copyright (C) 2024 Intel Corporation * * */ #ifdef HAVE_CONFIG_H #include #endif #include #include #include #include "src/shared/util.h" #include "src/shared/queue.h" #include "src/shared/shell.h" #include "src/shared/hci.h" #include "monitor/bt.h" #include "hci.h" static struct bt_hci *hci; static struct queue *events; struct hci_event { uint8_t event; unsigned int id; }; static void hci_open(int argc, char *argv[]) { long index; char *endptr = NULL; if (hci) { bt_shell_printf("HCI channel already open\n"); return bt_shell_noninteractive_quit(EXIT_FAILURE); } index = strtol(argv[1], &endptr, 0); if (!endptr || *endptr != '\0' || index < 0 || index > UINT16_MAX) { bt_shell_printf("Invalid index: %s\n", argv[1]); return bt_shell_noninteractive_quit(EXIT_FAILURE); } if (!strcasecmp(argv[2], "raw")) { hci = bt_hci_new_raw_device(index); if (!hci) { bt_shell_printf("Unable to open raw channel\n"); return bt_shell_noninteractive_quit(EXIT_FAILURE); } } else if (!strcasecmp(argv[2], "user")) { hci = bt_hci_new_user_channel(index); if (!hci) { bt_shell_printf("Unable to open user channel\n"); return bt_shell_noninteractive_quit(EXIT_FAILURE); } } else { bt_shell_printf("Invalid channel: %s\n", argv[2]); return bt_shell_noninteractive_quit(EXIT_FAILURE); } bt_shell_printf("HCI index %ld %s channel opened\n", index, argv[2]); return bt_shell_noninteractive_quit(EXIT_SUCCESS); } static uint8_t *str2bytearray(char *arg, size_t *val_len) { uint8_t value[UINT8_MAX]; char *entry; unsigned int i; for (i = 0; (entry = strsep(&arg, " \t")) != NULL; i++) { long val; char *endptr = NULL; if (*entry == '\0') continue; if (i >= G_N_ELEMENTS(value)) { bt_shell_printf("Too much data\n"); return NULL; } val = strtol(entry, &endptr, 0); if (!endptr || *endptr != '\0' || val > UINT8_MAX) { bt_shell_printf("Invalid value at index %d\n", i); return NULL; } value[i] = val; } *val_len = i; return util_memdup(value, i); } static void hci_cmd_complete(const void *data, uint8_t size, void *user_data) { bt_shell_printf("HCI Command complete:\n"); bt_shell_hexdump(data, size); return bt_shell_noninteractive_quit(EXIT_SUCCESS); } static void hci_cmd(int argc, char *argv[]) { long opcode; struct iovec iov = {}; char *endptr = NULL; unsigned int ret; if (!hci) { bt_shell_printf("HCI channel not open\n"); return bt_shell_noninteractive_quit(EXIT_FAILURE); } opcode = strtol(argv[1], &endptr, 0); if (!endptr || *endptr != '\0' || opcode < 0 || opcode > UINT16_MAX) { bt_shell_printf("Invalid opcode: %s\n", argv[1]); return bt_shell_noninteractive_quit(EXIT_FAILURE); } if (argc > 2) { iov.iov_base = str2bytearray(argv[2], &iov.iov_len); if (!iov.iov_base) { bt_shell_printf("Invalid parameters: %s\n", argv[2]); return bt_shell_noninteractive_quit(EXIT_FAILURE); } } ret = bt_hci_send(hci, opcode, iov.iov_base, iov.iov_len, hci_cmd_complete, NULL, NULL); free(iov.iov_base); if (!ret) return bt_shell_noninteractive_quit(EXIT_FAILURE); } static void hci_send(int argc, char *argv[]) { uint8_t type; long handle; struct iovec iov = {}; char *endptr = NULL; bool ret; if (!hci) { bt_shell_printf("HCI channel not open\n"); return bt_shell_noninteractive_quit(EXIT_FAILURE); } if (!strcasecmp(argv[1], "acl")) { type = BT_H4_ACL_PKT; } else if (!strcasecmp(argv[1], "sco")) { type = BT_H4_SCO_PKT; } else if (!strcasecmp(argv[1], "iso")) { type = BT_H4_ISO_PKT; } else { bt_shell_printf("Invalid type: %s\n", argv[1]); return bt_shell_noninteractive_quit(EXIT_FAILURE); } handle = strtol(argv[2], &endptr, 0); if (!endptr || *endptr != '\0' || handle < 0 || handle > UINT16_MAX) { bt_shell_printf("Invalid handle: %s\n", argv[2]); return bt_shell_noninteractive_quit(EXIT_FAILURE); } if (argc > 3) { iov.iov_base = str2bytearray(argv[3], &iov.iov_len); if (!iov.iov_base) { bt_shell_printf("Invalid data: %s\n", argv[3]); return bt_shell_noninteractive_quit(EXIT_FAILURE); } } ret = bt_hci_send_data(hci, type, handle, iov.iov_base, iov.iov_len); free(iov.iov_base); return bt_shell_noninteractive_quit(ret ? EXIT_SUCCESS : EXIT_FAILURE); } static bool match_event(const void *data, const void *match_data) { const struct hci_event *evt = data; uint8_t event = PTR_TO_UINT(match_data); return evt->event == event; } static void hci_evt_received(const void *data, uint8_t size, void *user_data) { struct hci_event *evt = user_data; bt_shell_printf("HCI Event 0x%02x received:\n", evt->event); bt_shell_hexdump(data, size); } static void hci_register(int argc, char *argv[]) { struct hci_event *evt; long event; char *endptr = NULL; if (!hci) { bt_shell_printf("HCI channel not open\n"); return bt_shell_noninteractive_quit(EXIT_FAILURE); } event = strtol(argv[1], &endptr, 0); if (!endptr || *endptr != '\0' || event < 0 || event > UINT8_MAX) { bt_shell_printf("Invalid event: %s\n", argv[1]); return bt_shell_noninteractive_quit(EXIT_FAILURE); } if (!events) events = queue_new(); evt = queue_find(events, match_event, UINT_TO_PTR(event)); if (evt) { bt_shell_printf("Event already registered\n"); return bt_shell_noninteractive_quit(EXIT_FAILURE); } evt = new0(struct hci_event, 1); evt->event = event; evt->id = bt_hci_register(hci, event, hci_evt_received, evt, NULL); if (!evt->id) { free(evt); return bt_shell_noninteractive_quit(EXIT_FAILURE); } bt_shell_printf("HCI Event 0x%02x registered\n", (uint8_t)event); queue_push_tail(events, evt); return bt_shell_noninteractive_quit(EXIT_SUCCESS); } static void hci_unregister(int argc, char *argv[]) { struct hci_event *evt; long event; char *endptr = NULL; if (!hci) { bt_shell_printf("HCI channel not open\n"); return bt_shell_noninteractive_quit(EXIT_FAILURE); } event = strtol(argv[1], &endptr, 0); if (!endptr || *endptr != '\0' || event < 0 || event > UINT8_MAX) { bt_shell_printf("Invalid event: %s\n", argv[1]); return bt_shell_noninteractive_quit(EXIT_FAILURE); } evt = queue_find(events, match_event, UINT_TO_PTR(event)); if (!evt) { bt_shell_printf("Event not registered\n"); return bt_shell_noninteractive_quit(EXIT_FAILURE); } bt_hci_unregister(hci, evt->id); queue_remove(events, evt); free(evt); bt_shell_printf("HCI Event 0x%02x unregistered\n", (uint8_t)event); return bt_shell_noninteractive_quit(EXIT_SUCCESS); } static void hci_close(int argc, char *argv[]) { if (!hci) { bt_shell_printf("HCI channel not open\n"); return bt_shell_noninteractive_quit(EXIT_FAILURE); } bt_hci_unref(hci); hci = NULL; bt_shell_printf("HCI channel closed\n"); return bt_shell_noninteractive_quit(EXIT_SUCCESS); } static const struct bt_shell_menu hci_menu = { .name = "hci", .desc = "HCI Submenu", .entries = { { "open", " ", hci_open, "Open HCI channel" }, { "cmd", " [parameters...]", hci_cmd, "Send HCI command" }, { "send", " [data...]", hci_send, "Send HCI data" }, { "register", "", hci_register, "Register HCI event handler" }, { "unregister", "", hci_unregister, "Unregister HCI event handler" }, { "close", NULL, hci_close, "Close HCI channel" }, {} }, }; void hci_add_submenu(void) { bt_shell_add_submenu(&hci_menu); } void hci_remove_submenu(void) { if (!hci) return; if (events) { queue_destroy(events, free); events = NULL; } bt_hci_unref(hci); hci = NULL; } bluez-5.82/client/PaxHeaders/bluetoothctl.rst0000644000000000000000000000005014766002272016374 xustar0020 atime=1743515577 20 ctime=1743591291 bluez-5.82/client/bluetoothctl.rst0000644000000000000000000002046314766002272016062 0ustar00rootroot============ bluetoothctl ============ ----------------------------------- Bluetooth Control Command Line Tool ----------------------------------- :Version: BlueZ :Copyright: Free use of this software is granted under the terms of the GNU Lesser General Public Licenses (LGPL). :Date: March 2024 :Manual section: 1 :Manual group: Linux System Administration SYNOPSIS ======== **bluetoothctl** [**-a** *capability*] [**-e**] [**-m**] [**-t** *seconds*] [**-v**] [**-h**] DESCRIPTION =========== **bluetoothctl(1)** interactive bluetooth control tool. The tool works with Bluetooth Classic (BR/EDR) and Bluetooth Low Energy (LE) controllers. The tool is menu driven but can be automated from the command line. Examples are given in the automation section. OPTIONS ======= -a capability, --agent capability Register agent handler: -e, --endpoints Register Media endpoints -m, --monitor Enable monitor output -t seconds, --timeout seconds Timeout in seconds for non-interactive mode -v, --version Display version -h, --help Display help Commands ======== list ---- List available controllers. :Usage: **> list** show ---- Controller information. :Usage: **> show [ctrl]** select ------ Select default controller. :Usage: **> select ** devices ------- List available devices, with an optional property as the filter. :Usage: **> devices [Paired/Bonded/Trusted/Connected]** system-alias ------------ Set controller alias. :Usage: **> system-alias ** reset-alias ----------- Reset controller alias. :Usage: **> reset-alias** power ----- Set controller power. When the controller is powered off, the USB port the controller is attached to is put into a suspend state. :Usage: **> power ** advertise --------- Enable/disable advertising with given type. If you exit the program advertising will be disabled. When advertising the controller should advertise with random address but may use its public address if it does not support the feature (address of the device). A device can advertise if it initiated the connection to another advertising device. :Usage: **> advertise ** set-alias --------- Set device alias. :Usage: **> set-alias ** scan ---- Scan for devices. For LE, scanning is an important requirement before connecting or pairing. The purpose of scanning is to find devices that are advertising with their discoverable flag set (either limited or general). Once you have found the address then you can connect or pair. Note the following when scanning: - When scanning the controller will use a random address that is not resolvable so the public address is not leaked. A new random address is created every time scan on is used. - When turning on scanning the device will start receiving advertising reports of what devices are advertising. - The filtering of duplicate advertising reports may be enabled depending on the filtering settings. - Device objects found during a scan session will only be persisted if they are connected/paired otherwise they are removed after some time. :Usage: **> scan ** pair ---- Pair with device. This will pair with a device and then trust and connect to it. If the device is already paired this will first remove the pairing. The command can either be used while the controller is in the connected or not connected state. If the controller is already connected then the pair command can be used without an arguments. If the controller is not connected, the pair command can be given the address of a device with an active scan report and it will initiate the connection before pairing. Before pairing the agent must be selected to choose the authentication mechanism. :Usage: **> pair ** pairable -------- Set controller pairable mode. This enables/disables pairing. If pairing is disabled then the controller will not accept any pairing requests. :Usage: **> pairable ** discoverable ------------ Set discoverable mode. This enables/disables discoverable mode. If discoverable is disabled then the controller will not respond to any scan requests. In LE if discoverable if off the controller will just passively scan and not make scan requests to advertising devices. If on it will make the advertising requests. It will use a random address if supported by the controller. The length of time "discoverable on" is valid is determined by discoverable-timeout command. :Usage: **> discoverable ** discoverable-timeout -------------------- Set discoverable timeout. The time in seconds that "discoverable on" is valid. :Usage: **> discoverable-timeout [value]** agent ----- Enable/disable agent with given capability. This chooses the local authentication mechanism of the controller. It is needed for pairing and allows you to choose the IO capabilities of the controller. The valid agent capabilities are: DisplayOnly, DisplayYesNo, KeyboardDisplay, KeyboardOnly, NoInputNoOutput. :Usage: **> agent ** default-agent ------------- Set current agent as the default one. After selecting the agent this will make it the default agent. :Usage: **> default-agent** trust ----- Trust device. :Usage: **> trust ** untrust ------- Untrust device. :Usage: **> untrust ** block ----- Block device. :Usage: **> block ** unblock ------- Unblock device :Usage: **> unblock ** remove ------ Remove device. :Usage: **> remove ** connect ------- Connect device. This will initiate a connection to a device. By default this commands tries to connect all the profiles the remote device supports and have been flagged as auto-connectable. In case when the UUID of the remote service is given only that service will be connected. The UUID can be either a short form (16-bit UUID) or a long form (128-bit UUID). There are also some special values for well-known profiles like "a2dp-sink", "a2dp-source", "hfp-hf", "hfp-ag", "ftp" or "spp". To connect with an LE device the controller must have an active scan report of the device it wants to connect to. If no advertising report is received before the timeout a le-connection-abort-by-local error will be issued. In that case either try again to connect assuming the device is advertising. :Usage: **> connect [uuid]** :Example: **> connect 1C:48:F9:9D:81:5C** :Example: **> connect 1C:48:F9:9D:81:5C hsp-hs** :Example: **> connect 1C:48:F9:9D:81:5C 00001108-0000-1000-8000-00805f9b34fb** :Example: **> connect 1C:48:F9:9D:81:5C 0x1108** disconnect ---------- Disconnect device. By default this commands disconnects all profiles and then terminates the connection. In case when the UUID of the remote service is given only that service will be disconnected. For LE when disconnecting from an active connection the device address is not needed. :Usage: **> disconnect [uuid]** info ---- Device information. :Usage: **> info ** bearer ------ Get/Set preferred bearer. :Usage: **> bearer [last-seen/bredr/le]** :Example get preferred bearer: | > bearer | PreferredBearer: last-seen :Example set preferred bearer to LE: | > bearer le | [CHG] Device PreferredBearer: le | Changing le succeeded :Example set preferred bearer to BREDR: | > bearer bredr | [CHG] Device PreferredBearer: bredr | Changing bredr succeeded Advertise Submenu ================= See **bluetoothctl-advertise(1)**. Monitor Submenu =============== See **bluetoothctl-monitor(1)** Scan Submenu ============ See **bluetoothctl-scan(1)** Gatt Submenu ============ See **bluetoothctl-gatt(1)** Admin Submenu ============= See **bluetoothctl-admin(1)** Player Submenu ============== See **bluetoothctl-player(1)** Endpoint Submenu ================ See **bluetoothctl-endpoint(1)** Transport Submenu ================= See **bluetoothctl-transport(1)** Management Submenu ================== See **bluetoothctl-mgmt(1)** Assistant Submenu ================== See **bluetoothctl-assistant(1)** AUTOMATION ========== Two common ways to automate the tool are to use Here Docs or the program expect. Using Here Docs to show information about the Bluetooth controller. .. code:: bluetoothctl < list\-attributes \fP .UNINDENT .SS select\-attribute .sp Select attribute. .INDENT 0.0 .TP .B Usage \fB> select\-attribute \fP .UNINDENT .SS attribute\-info .sp Select attribute. .INDENT 0.0 .TP .B Usage \fB> attribute\-info [attribute/UUID]\fP .UNINDENT .SS read .sp Read attribute value. .INDENT 0.0 .TP .B Usage \fB> read [offset]\fP .UNINDENT .SS write .sp Write attribute value. .INDENT 0.0 .TP .B Usage \fB> write [offset] [type]\fP .UNINDENT .SS acquire\-write .sp Acquire Write file descriptor. .INDENT 0.0 .TP .B Usage \fB> acquire\-write\fP .UNINDENT .SS release\-write .sp Release Write file descriptor. .INDENT 0.0 .TP .B Usage \fB> release\-write\fP .UNINDENT .SS acquire\-notify .sp Acquire Notify file descriptor. .INDENT 0.0 .TP .B Usage \fB> acquire\-notify\fP .UNINDENT .SS release\-notify .sp Release Notify file descriptor. .INDENT 0.0 .TP .B Usage \fB> release\-notify\fP .UNINDENT .SS notify .sp Notify attribute value. .INDENT 0.0 .TP .B Usage \fB> notify \fP .UNINDENT .SS clone .sp Clone a device or attribute. .INDENT 0.0 .TP .B Usage \fB> clone [dev/attribute/UUID]\fP .UNINDENT .SS register\-application .sp Register application. .INDENT 0.0 .TP .B Usage \fB> register\-application [UUID ...]\fP .UNINDENT .SS unregister\-application .sp Unregister application .INDENT 0.0 .TP .B Usage \fB> unregister\-application\fP .UNINDENT .SS register\-service .sp Register application service. .INDENT 0.0 .TP .B Usage \fB> register\-service [handle]\fP .UNINDENT .SS unregister\-service .sp Unregister application service .INDENT 0.0 .TP .B Usage \fB> unregister\-service \fP .UNINDENT .SS register\-includes .sp Register as Included service. .INDENT 0.0 .TP .B Usage \fB>r egister\-includes [handle]\fP .UNINDENT .SS unregister\-includes .sp Unregister Included service. .INDENT 0.0 .TP .B Usage \fB> unregister\-includes \fP .UNINDENT .SS register\-characteristic .sp Register service characteristic. .INDENT 0.0 .TP .B Usage \fB> register\-characteristic [handle]\fP .UNINDENT .SS unregister\-characteristic .sp Unregister service characteristic. .INDENT 0.0 .TP .B Usage \fB> unregister\-characteristic \fP .UNINDENT .SS register\-descriptor .sp Register characteristic descriptor. .INDENT 0.0 .TP .B Usage \fB> register\-descriptor [handle]\fP .UNINDENT .SS unregister\-descriptor .sp Unregister characteristic descriptor. .INDENT 0.0 .TP .B Usage \fB> unregister\-descriptor \fP .UNINDENT .SH RESOURCES .sp .SH REPORTING BUGS .sp .SH COPYRIGHT Free use of this software is granted under the terms of the GNU Lesser General Public Licenses (LGPL). .\" Generated by docutils manpage writer. . bluez-5.82/client/PaxHeaders/bluetoothctl-assistant.rst0000644000000000000000000000005014766002272020403 xustar0020 atime=1743515577 20 ctime=1743591291 bluez-5.82/client/bluetoothctl-assistant.rst0000644000000000000000000000364314766002272020072 0ustar00rootroot====================== bluetoothctl-assistant ====================== ----------------- Assistant Submenu ----------------- :Version: BlueZ :Copyright: Free use of this software is granted under the terms of the GNU Lesser General Public Licenses (LGPL). :Date: August 2024 :Manual section: 1 :Manual group: Linux System Administration SYNOPSIS ======== **bluetoothctl** [--options] [assistant.commands] Assistant Commands ================== push ---- Send stream information to peer. This command is used by a BAP Broadcast Assistant to send information about a broadcast stream to a peer BAP Scan Delegator. The information is sent via a GATT Write Command for the BASS Broadcast Audio Scan Control Point characteristic. After issuing the command, the user is prompted to enter stream metadata LTVs to send to the peer. If the auto option is chosen, the Broadcast Assistant will send the default metadata discovered about the stream. Otherwise, the default metadata will be overwritten by the LTVs entered by the user. If the stream is encrypted, the user will also be prompted to enter the Broadcast Code. This is the key to decrypt the stream. On the UI level, the Broadcast Code shall be represented as a string of at least 4 octets, and no more than 16 octets when represented in UTF-8. The string will be sent to the peer via GATT as an array of 16 octets. If the auto value is chosen when prompted for the Broadcast Code, a zero filled array will be sent to the peer. Otherwise, the string entered by the user will be sent as an array of bytes. :Usage: **> push ** :Example: | **> push /org/bluez/hci0/src_05_1F_EE_F3_F8_7D/dev_00_60_37_31_7E_3F/bis1** | **[Assistant] Enter Metadata (auto/value): 0x03 0x02 0x04 0x00** | **[Assistant] Enter Broadcast Code (auto/value): Borne House** RESOURCES ========= http://www.bluez.org REPORTING BUGS ============== linux-bluetooth@vger.kernel.org bluez-5.82/client/PaxHeaders/display.c0000644000000000000000000000005014015011623014726 xustar0020 atime=1743515775 20 ctime=1743591280 bluez-5.82/client/display.c0000644000000000000000000000570714015011623014420 0ustar00rootroot// SPDX-License-Identifier: GPL-2.0-or-later /* * * BlueZ - Bluetooth protocol stack for Linux * * Copyright (C) 2012 Intel Corporation. All rights reserved. * * */ #ifdef HAVE_CONFIG_H #include #endif #define _GNU_SOURCE #include #include #include #include #include #include #include "display.h" static char *saved_prompt = NULL; static int saved_point = 0; static rl_prompt_input_func saved_func = NULL; static void *saved_user_data = NULL; void rl_printf(const char *fmt, ...) { va_list args; bool save_input; char *saved_line; int saved_point; save_input = !RL_ISSTATE(RL_STATE_DONE); if (save_input) { saved_point = rl_point; saved_line = rl_copy_text(0, rl_end); rl_save_prompt(); rl_replace_line("", 0); rl_redisplay(); } va_start(args, fmt); vprintf(fmt, args); va_end(args); if (save_input) { rl_restore_prompt(); rl_replace_line(saved_line, 0); rl_point = saved_point; rl_forced_update_display(); free(saved_line); } } void rl_hexdump(const unsigned char *buf, size_t len) { static const char hexdigits[] = "0123456789abcdef"; char str[68]; size_t i; if (!len) return; str[0] = ' '; for (i = 0; i < len; i++) { str[((i % 16) * 3) + 1] = ' '; str[((i % 16) * 3) + 2] = hexdigits[buf[i] >> 4]; str[((i % 16) * 3) + 3] = hexdigits[buf[i] & 0xf]; str[(i % 16) + 51] = isprint(buf[i]) ? buf[i] : '.'; if ((i + 1) % 16 == 0) { str[49] = ' '; str[50] = ' '; str[67] = '\0'; rl_printf("%s\n", str); str[0] = ' '; } } if (i % 16 > 0) { size_t j; for (j = (i % 16); j < 16; j++) { str[(j * 3) + 1] = ' '; str[(j * 3) + 2] = ' '; str[(j * 3) + 3] = ' '; str[j + 51] = ' '; } str[49] = ' '; str[50] = ' '; str[67] = '\0'; rl_printf("%s\n", str); } } void rl_prompt_input(const char *label, const char *msg, rl_prompt_input_func func, void *user_data) { char prompt[256]; /* Normal use should not prompt for user input to the value a second * time before it releases the prompt, but we take a safe action. */ if (saved_prompt) return; saved_point = rl_point; saved_prompt = strdup(rl_prompt); saved_func = func; saved_user_data = user_data; rl_set_prompt(""); rl_redisplay(); memset(prompt, 0, sizeof(prompt)); snprintf(prompt, sizeof(prompt), COLOR_RED "[%s]" COLOR_OFF " %s ", label, msg); rl_set_prompt(prompt); rl_replace_line("", 0); rl_redisplay(); } int rl_release_prompt(const char *input) { rl_prompt_input_func func; void *user_data; if (!saved_prompt) return -1; /* This will cause rl_expand_prompt to re-run over the last prompt, but * our prompt doesn't expand anyway. */ rl_set_prompt(saved_prompt); rl_replace_line("", 0); rl_point = saved_point; rl_redisplay(); free(saved_prompt); saved_prompt = NULL; func = saved_func; user_data = saved_user_data; saved_func = NULL; saved_user_data = NULL; func(input, user_data); return 0; } bluez-5.82/client/PaxHeaders/agent.c0000644000000000000000000000005014550336161014372 xustar0020 atime=1743515776 20 ctime=1743591281 bluez-5.82/client/agent.c0000644000000000000000000003212414550336161014055 0ustar00rootroot// SPDX-License-Identifier: GPL-2.0-or-later /* * * BlueZ - Bluetooth protocol stack for Linux * * Copyright (C) 2012 Intel Corporation. All rights reserved. * * */ #ifdef HAVE_CONFIG_H #include #endif #include #include #include #include #include #include #include "src/shared/shell.h" #include "gdbus/gdbus.h" #include "agent.h" #define AGENT_PATH "/org/bluez/agent" #define AGENT_INTERFACE "org.bluez.Agent1" #define AGENT_PROMPT COLOR_RED "[agent]" COLOR_OFF " " static gboolean agent_registered = FALSE; static const char *agent_capability = NULL; static DBusMessage *pending_message = NULL; static void agent_release_prompt(void) { if (!pending_message) return; bt_shell_release_prompt(""); } dbus_bool_t agent_completion(void) { if (!pending_message) return FALSE; return TRUE; } static void pincode_response(const char *input, void *user_data) { DBusConnection *conn = user_data; g_dbus_send_reply(conn, pending_message, DBUS_TYPE_STRING, &input, DBUS_TYPE_INVALID); } static void passkey_response(const char *input, void *user_data) { DBusConnection *conn = user_data; dbus_uint32_t passkey; if (sscanf(input, "%u", &passkey) == 1) g_dbus_send_reply(conn, pending_message, DBUS_TYPE_UINT32, &passkey, DBUS_TYPE_INVALID); else if (!strcmp(input, "no")) g_dbus_send_error(conn, pending_message, "org.bluez.Error.Rejected", NULL); else g_dbus_send_error(conn, pending_message, "org.bluez.Error.Canceled", NULL); } static void confirm_response(const char *input, void *user_data) { DBusConnection *conn = user_data; if (pending_message != NULL) { if (!strcmp(input, "yes")) g_dbus_send_reply(conn, pending_message, DBUS_TYPE_INVALID); else if (!strcmp(input, "no")) g_dbus_send_error(conn, pending_message, "org.bluez.Error.Rejected", NULL); else g_dbus_send_error(conn, pending_message, "org.bluez.Error.Canceled", NULL); } } static void agent_release(DBusConnection *conn) { agent_registered = FALSE; agent_capability = NULL; if (pending_message) { dbus_message_unref(pending_message); pending_message = NULL; } agent_release_prompt(); g_dbus_unregister_interface(conn, AGENT_PATH, AGENT_INTERFACE); } static DBusMessage *release_agent(DBusConnection *conn, DBusMessage *msg, void *user_data) { bt_shell_printf("Agent released\n"); agent_release(conn); return dbus_message_new_method_return(msg); } static DBusMessage *request_pincode(DBusConnection *conn, DBusMessage *msg, void *user_data) { const char *device; bt_shell_printf("Request PIN code\n"); dbus_message_get_args(msg, NULL, DBUS_TYPE_OBJECT_PATH, &device, DBUS_TYPE_INVALID); bt_shell_prompt_input("agent", "Enter PIN code:", pincode_response, conn); pending_message = dbus_message_ref(msg); return NULL; } static DBusMessage *display_pincode(DBusConnection *conn, DBusMessage *msg, void *user_data) { const char *device; const char *pincode; dbus_message_get_args(msg, NULL, DBUS_TYPE_OBJECT_PATH, &device, DBUS_TYPE_STRING, &pincode, DBUS_TYPE_INVALID); bt_shell_printf(AGENT_PROMPT "PIN code: %s\n", pincode); return dbus_message_new_method_return(msg); } static DBusMessage *request_passkey(DBusConnection *conn, DBusMessage *msg, void *user_data) { const char *device; bt_shell_printf("Request passkey\n"); dbus_message_get_args(msg, NULL, DBUS_TYPE_OBJECT_PATH, &device, DBUS_TYPE_INVALID); bt_shell_prompt_input("agent", "Enter passkey (number in 0-999999):", passkey_response, conn); pending_message = dbus_message_ref(msg); return NULL; } static DBusMessage *display_passkey(DBusConnection *conn, DBusMessage *msg, void *user_data) { const char *device; dbus_uint32_t passkey; dbus_uint16_t entered; char passkey_full[7]; dbus_message_get_args(msg, NULL, DBUS_TYPE_OBJECT_PATH, &device, DBUS_TYPE_UINT32, &passkey, DBUS_TYPE_UINT16, &entered, DBUS_TYPE_INVALID); snprintf(passkey_full, sizeof(passkey_full), "%.6u", passkey); passkey_full[6] = '\0'; if (entered > strlen(passkey_full)) entered = strlen(passkey_full); bt_shell_printf(AGENT_PROMPT "Passkey: " COLOR_BOLDGRAY "%.*s" COLOR_BOLDWHITE "%s\n" COLOR_OFF, entered, passkey_full, passkey_full + entered); return dbus_message_new_method_return(msg); } static DBusMessage *request_confirmation(DBusConnection *conn, DBusMessage *msg, void *user_data) { const char *device; dbus_uint32_t passkey; char *str; bt_shell_printf("Request confirmation\n"); dbus_message_get_args(msg, NULL, DBUS_TYPE_OBJECT_PATH, &device, DBUS_TYPE_UINT32, &passkey, DBUS_TYPE_INVALID); str = g_strdup_printf("Confirm passkey %06u (yes/no):", passkey); bt_shell_prompt_input("agent", str, confirm_response, conn); g_free(str); pending_message = dbus_message_ref(msg); return NULL; } static DBusMessage *request_authorization(DBusConnection *conn, DBusMessage *msg, void *user_data) { const char *device; bt_shell_printf("Request authorization\n"); dbus_message_get_args(msg, NULL, DBUS_TYPE_OBJECT_PATH, &device, DBUS_TYPE_INVALID); bt_shell_prompt_input("agent", "Accept pairing (yes/no):", confirm_response, conn); pending_message = dbus_message_ref(msg); return NULL; } static DBusMessage *authorize_service(DBusConnection *conn, DBusMessage *msg, void *user_data) { const char *device, *uuid; char *str; bt_shell_printf("Authorize service\n"); dbus_message_get_args(msg, NULL, DBUS_TYPE_OBJECT_PATH, &device, DBUS_TYPE_STRING, &uuid, DBUS_TYPE_INVALID); str = g_strdup_printf("Authorize service %s (yes/no):", uuid); bt_shell_prompt_input("agent", str, confirm_response, conn); g_free(str); pending_message = dbus_message_ref(msg); return NULL; } static DBusMessage *cancel_request(DBusConnection *conn, DBusMessage *msg, void *user_data) { bt_shell_printf("Request canceled\n"); agent_release_prompt(); dbus_message_unref(pending_message); pending_message = NULL; return dbus_message_new_method_return(msg); } static const GDBusMethodTable agent_methods[] = { { GDBUS_METHOD("Release", NULL, NULL, release_agent) }, { GDBUS_ASYNC_METHOD("RequestPinCode", GDBUS_ARGS({ "device", "o" }), GDBUS_ARGS({ "pincode", "s" }), request_pincode) }, { GDBUS_METHOD("DisplayPinCode", GDBUS_ARGS({ "device", "o" }, { "pincode", "s" }), NULL, display_pincode) }, { GDBUS_ASYNC_METHOD("RequestPasskey", GDBUS_ARGS({ "device", "o" }), GDBUS_ARGS({ "passkey", "u" }), request_passkey) }, { GDBUS_METHOD("DisplayPasskey", GDBUS_ARGS({ "device", "o" }, { "passkey", "u" }, { "entered", "q" }), NULL, display_passkey) }, { GDBUS_ASYNC_METHOD("RequestConfirmation", GDBUS_ARGS({ "device", "o" }, { "passkey", "u" }), NULL, request_confirmation) }, { GDBUS_ASYNC_METHOD("RequestAuthorization", GDBUS_ARGS({ "device", "o" }), NULL, request_authorization) }, { GDBUS_ASYNC_METHOD("AuthorizeService", GDBUS_ARGS({ "device", "o" }, { "uuid", "s" }), NULL, authorize_service) }, { GDBUS_METHOD("Cancel", NULL, NULL, cancel_request) }, { } }; static DBusMessage *auto_confirmation(DBusConnection *conn, DBusMessage *msg, void *user_data) { const char *device; dbus_uint32_t passkey; bt_shell_printf("Request confirmation\n"); dbus_message_get_args(msg, NULL, DBUS_TYPE_OBJECT_PATH, &device, DBUS_TYPE_UINT32, &passkey, DBUS_TYPE_INVALID); bt_shell_printf("Confirm passkey %06u (auto)", passkey); return dbus_message_new_method_return(msg); } static DBusMessage *auto_authorization(DBusConnection *conn, DBusMessage *msg, void *user_data) { const char *device; bt_shell_printf("Request authorization\n"); dbus_message_get_args(msg, NULL, DBUS_TYPE_OBJECT_PATH, &device, DBUS_TYPE_INVALID); bt_shell_printf("Accept pairing (auto)"); return dbus_message_new_method_return(msg); } static DBusMessage *auto_authorize_service(DBusConnection *conn, DBusMessage *msg, void *user_data) { const char *device, *uuid; dbus_message_get_args(msg, NULL, DBUS_TYPE_OBJECT_PATH, &device, DBUS_TYPE_STRING, &uuid, DBUS_TYPE_INVALID); bt_shell_printf("Authorize service %s (auto)", uuid); return dbus_message_new_method_return(msg); } static const GDBusMethodTable auto_methods[] = { { GDBUS_METHOD("Release", NULL, NULL, release_agent) }, { GDBUS_ASYNC_METHOD("RequestPinCode", GDBUS_ARGS({ "device", "o" }), GDBUS_ARGS({ "pincode", "s" }), request_pincode) }, { GDBUS_METHOD("DisplayPinCode", GDBUS_ARGS({ "device", "o" }, { "pincode", "s" }), NULL, display_pincode) }, { GDBUS_ASYNC_METHOD("RequestPasskey", GDBUS_ARGS({ "device", "o" }), GDBUS_ARGS({ "passkey", "u" }), request_passkey) }, { GDBUS_METHOD("DisplayPasskey", GDBUS_ARGS({ "device", "o" }, { "passkey", "u" }, { "entered", "q" }), NULL, display_passkey) }, { GDBUS_ASYNC_METHOD("RequestConfirmation", GDBUS_ARGS({ "device", "o" }, { "passkey", "u" }), NULL, auto_confirmation) }, { GDBUS_ASYNC_METHOD("RequestAuthorization", GDBUS_ARGS({ "device", "o" }), NULL, auto_authorization) }, { GDBUS_ASYNC_METHOD("AuthorizeService", GDBUS_ARGS({ "device", "o" }, { "uuid", "s" }), NULL, auto_authorize_service) }, { GDBUS_METHOD("Cancel", NULL, NULL, cancel_request) }, { } }; static void register_agent_setup(DBusMessageIter *iter, void *user_data) { const char *path = AGENT_PATH; const char *capability = agent_capability; dbus_message_iter_append_basic(iter, DBUS_TYPE_OBJECT_PATH, &path); dbus_message_iter_append_basic(iter, DBUS_TYPE_STRING, &capability); } static void register_agent_reply(DBusMessage *message, void *user_data) { DBusConnection *conn = user_data; DBusError error; dbus_error_init(&error); if (dbus_set_error_from_message(&error, message) == FALSE) { agent_registered = TRUE; bt_shell_printf("Agent registered\n"); } else { bt_shell_printf("Failed to register agent: %s\n", error.name); dbus_error_free(&error); if (g_dbus_unregister_interface(conn, AGENT_PATH, AGENT_INTERFACE) == FALSE) bt_shell_printf("Failed to unregister agent object\n"); } } void agent_register(DBusConnection *conn, GDBusProxy *manager, const char *capability) { const GDBusMethodTable *methods = agent_methods; if (agent_registered == TRUE) { bt_shell_printf("Agent is already registered\n"); return; } agent_capability = capability; if (!strcasecmp(agent_capability, "auto")) { bt_shell_printf("Warning: setting auto response is not secure, " "it bypass user confirmation/authorization, it " "shall only be used for test automation.\n"); agent_capability = ""; methods = auto_methods; } if (g_dbus_register_interface(conn, AGENT_PATH, AGENT_INTERFACE, methods, NULL, NULL, NULL, NULL) == FALSE) { bt_shell_printf("Failed to register agent object\n"); return; } if (g_dbus_proxy_method_call(manager, "RegisterAgent", register_agent_setup, register_agent_reply, conn, NULL) == FALSE) { bt_shell_printf("Failed to call register agent method\n"); return; } agent_capability = NULL; } static void unregister_agent_setup(DBusMessageIter *iter, void *user_data) { const char *path = AGENT_PATH; dbus_message_iter_append_basic(iter, DBUS_TYPE_OBJECT_PATH, &path); } static void unregister_agent_reply(DBusMessage *message, void *user_data) { DBusConnection *conn = user_data; DBusError error; dbus_error_init(&error); if (dbus_set_error_from_message(&error, message) == FALSE) { bt_shell_printf("Agent unregistered\n"); agent_release(conn); } else { bt_shell_printf("Failed to unregister agent: %s\n", error.name); dbus_error_free(&error); } } void agent_unregister(DBusConnection *conn, GDBusProxy *manager) { if (agent_registered == FALSE) { bt_shell_printf("No agent is registered\n"); return; } if (!manager) { bt_shell_printf("Agent unregistered\n"); agent_release(conn); return; } if (g_dbus_proxy_method_call(manager, "UnregisterAgent", unregister_agent_setup, unregister_agent_reply, conn, NULL) == FALSE) { bt_shell_printf("Failed to call unregister agent method\n"); return; } } static void request_default_setup(DBusMessageIter *iter, void *user_data) { const char *path = AGENT_PATH; dbus_message_iter_append_basic(iter, DBUS_TYPE_OBJECT_PATH, &path); } static void request_default_reply(DBusMessage *message, void *user_data) { DBusError error; dbus_error_init(&error); if (dbus_set_error_from_message(&error, message) == TRUE) { bt_shell_printf("Failed to request default agent: %s\n", error.name); dbus_error_free(&error); return bt_shell_noninteractive_quit(EXIT_FAILURE); } bt_shell_printf("Default agent request successful\n"); return bt_shell_noninteractive_quit(EXIT_SUCCESS); } void agent_default(DBusConnection *conn, GDBusProxy *manager) { if (agent_registered == FALSE) { bt_shell_printf("No agent is registered\n"); return bt_shell_noninteractive_quit(EXIT_FAILURE); } if (g_dbus_proxy_method_call(manager, "RequestDefaultAgent", request_default_setup, request_default_reply, NULL, NULL) == FALSE) { bt_shell_printf("Failed to call RequestDefaultAgent method\n"); return bt_shell_noninteractive_quit(EXIT_FAILURE); } } bluez-5.82/client/PaxHeaders/bluetoothctl-scan.rst0000644000000000000000000000005014766002272017316 xustar0020 atime=1743515577 20 ctime=1743591291 bluez-5.82/client/bluetoothctl-scan.rst0000644000000000000000000000562614766002272017010 0ustar00rootroot================= bluetoothctl-scan ================= ------------ Scan Submenu ------------ :Version: BlueZ :Copyright: Free use of this software is granted under the terms of the GNU Lesser General Public Licenses (LGPL). :Date: July 2023 :Manual section: 1 :Manual group: Linux System Administration SYNOPSIS ======== **bluetoothctl** [--options] [scan.commands] Scan Commands ============= uuids ----- Set/Get UUIDs filter. :Usage: **> uuids [all/uuid1 uuid2 ...]** rssi ---- Set/Get RSSI filter, and clears pathloss. This sets the minimum rssi value for reporting device advertisements. The value is in dBm. If one or more discovery filters have been set, the RSSI delta-threshold imposed by starting discovery by default will not be applied. :Usage: **> rssi [rssi]** :Example: **> rssi -60** pathloss -------- Set/Get Pathloss filter, and clears RSSI. This sets the maximum pathloss value for reporting device advertisements. The value is in dB. If one or more discovery filters have been set, the RSSI delta-threshold imposed by starting discovery by default will not be applied. :Usage: **> pathloss [pathloss]** :Example: **> pathloss 4** transport --------- Set/Get transport filter. Transport parameter determines the type of scan. The default is auto. Possible values: - "auto": interleaved scan - "bredr": BR/EDR inquiry - "le": LE scan only If "le" or "bredr" Transport is requested and the controller doesn't support it, an org.bluez.Error.Failed error will be returned. If "auto" transport is requested, the scan will use LE, BREDR, or both, depending on what's currently enabled on the controller. :Usage: **> transport [auto/bredr/le]** duplicate-data -------------- Set/Get duplicate data filter. Disables duplicate detection of advertisement data. When enabled, PropertiesChanged signals will be generated for ManufacturerData and ServiceData every time they are discovered. :Usage: **> duplicate-data [on/off]** discoverable ------------ Set/Get discoverable filter. Makes the adapter discoverable while discovering. If the adapter is already discoverable, setting this filter won't have any effect. :Usage: **> discoverable [on/off]** pattern ------- Set/Get pattern filter. Discover devices where the pattern matches either the prefix of the address or the device name, which is a convenient way to limit the number of device objects created during a discovery. When set, it disregards device discoverable flags. :Note: The pattern matching is ignored if there are other clients that don't set any pattern, as it works as a logical OR. Also, setting an empty string "" pattern will match any device found. :Usage: **> pattern [value]** clear ----- Clears discovery filter. :Usage: **> clear [uuids/rssi/pathloss/transport/duplicate-data/discoverable/pattern]** RESOURCES ========= http://www.bluez.org REPORTING BUGS ============== linux-bluetooth@vger.kernel.org bluez-5.82/client/PaxHeaders/bluetoothctl-transport.10000644000000000000000000000005014773213532017757 xustar0020 atime=1743591258 20 ctime=1743591291 bluez-5.82/client/bluetoothctl-transport.10000644000000000000000000000733614773213532017451 0ustar00rootroot.\" Man page generated from reStructuredText. . . .nr rst2man-indent-level 0 . .de1 rstReportMargin \\$1 \\n[an-margin] level \\n[rst2man-indent-level] level margin: \\n[rst2man-indent\\n[rst2man-indent-level]] - \\n[rst2man-indent0] \\n[rst2man-indent1] \\n[rst2man-indent2] .. .de1 INDENT .\" .rstReportMargin pre: . RS \\$1 . nr rst2man-indent\\n[rst2man-indent-level] \\n[an-margin] . nr rst2man-indent-level +1 .\" .rstReportMargin post: .. .de UNINDENT . RE .\" indent \\n[an-margin] .\" old: \\n[rst2man-indent\\n[rst2man-indent-level]] .nr rst2man-indent-level -1 .\" new: \\n[rst2man-indent\\n[rst2man-indent-level]] .in \\n[rst2man-indent\\n[rst2man-indent-level]]u .. .TH "BLUETOOTHCTL-TRANSPORT" "1" "November 2022" "BlueZ" "Linux System Administration" .SH NAME bluetoothctl-transport \- Media Transport Submenu .SH SYNOPSIS .sp \fBbluetoothctl\fP [\-\-options] [transport.commands] .SH MEDIA TRANSPORT COMMANDS .SS list .sp List available transports. .INDENT 0.0 .TP .B Usage \fB> list\fP .UNINDENT .SS show .sp Show transport information. .INDENT 0.0 .TP .B Usage \fB> show [transport]\fP .UNINDENT .SS acquire .sp Acquire transport. .INDENT 0.0 .TP .B Usage \fB> acquire [transport1...]\fP .UNINDENT .sp Note: .sp If running the setup with an audio server that has LE Audio support (such as PipeWire) it will automatically acquire transports according to the configured roles. .SS select .sp Select transport. For transports created on a Broadcast Sink device only. This moves the transport to the \(dqbroadcasting\(dq state, pending acquire. .INDENT 0.0 .TP .B Usage \fB> select [transport1...]\fP .UNINDENT .sp Note: .sp If the select command receives a list of transports, they will first be linked using the \(dqLinks\(dq MediaTransport property. They will then be selected one by one, by calling the \(dqSelect\(dq MediaTransport method. After the first transport is acquired, the Broadcast Sink will create fds for the associated stream and all its links. Each link can then be acquired one by one, setting the fd for the transport and starting to receive audio. .sp The select command does not require a local endpoint to be registered beforehand. This is because if the setup runs with an audio server that has LE Audio support (such as PipeWire), the audio server is the one to register endpoints and the transports are created as a result. Once a transport is selected, the audio server will automatically acquire. .SS unselect .sp Unselect transport. For transports created on a Broadcast Sink device only. This moves the transport to the \(dqidle\(dq state, pending release by the audio server. If the transport was acquired by bluetoothctl it can be released straight away, without having to be unselected. .INDENT 0.0 .TP .B Usage \fB> unselect [transport1...]\fP .UNINDENT .sp Note: If running the setup with an audio server that has LE Audio support (such as PipeWire), it will prompt it to automatically release the transport. .SS release .sp Release transport. .INDENT 0.0 .TP .B Usage \fB> release [transport1...]\fP .UNINDENT .sp Note: .sp Transports acquired by an audio server, can only be released by said audio server. .SS send .sp Send contents of a file. .INDENT 0.0 .TP .B Usage \fB> send \fP .UNINDENT .SS receive .sp Get/Set file to receive. .INDENT 0.0 .TP .B Usage \fB> receive [filename]\fP .UNINDENT .SS volume .sp Get/Set transport volume. .INDENT 0.0 .TP .B Usage \fB> volume [value]\fP .UNINDENT .SH RESOURCES .sp .SH REPORTING BUGS .sp .SH COPYRIGHT Free use of this software is granted under the terms of the GNU Lesser General Public Licenses (LGPL). .\" Generated by docutils manpage writer. . bluez-5.82/client/PaxHeaders/bluetoothctl-advertise.rst0000644000000000000000000000005014766002272020360 xustar0020 atime=1743515577 20 ctime=1743591291 bluez-5.82/client/bluetoothctl-advertise.rst0000644000000000000000000001476614766002272020057 0ustar00rootroot====================== bluetoothctl-advertise ====================== ----------------- Advertise Submenu ----------------- :Version: BlueZ :Copyright: Free use of this software is granted under the terms of the GNU Lesser General Public Licenses (LGPL). :Date: November 2022 :Manual section: 1 :Manual group: Linux System Administration SYNOPSIS ======== **bluetoothctl** [--options] [advertise.commands] Advertise Options Commands ========================== uuids ----- Set/Get advertise uuids. :Usage: **> uuids [all/uuid1 uuid2 ...]** :Example: **> uuids 0x1234** :Example: **> uuids 0x12345678** :Example: **> uuids 90f95193-35de-4306-a6e9-699328f15059** solicit ------- Set/Get advertise solicit uuids. :Usage: **# solicit [all/uuid1 uuid2 ...]** service ------- Set/Get advertise service data. :Usage: **> service [uuid] [data=xx xx ...]** manufacturer ------------ Set/Get advertise manufacturer data. Updating is in real time while advertising. This is currently limited to 25 bytes and will return an error message of "Too much data" if that maximum has been exceeded. However, this does not check if the advertising payload length maximum has been exceeded so you may receive an error from bluetoothd that it "Failed to register advertisement" which means you need to reduce your manufacturer data length. :Usage: **> manufacturer [id] [data=xx xx ...]** data ---- Set/Get advertise data. This allows you to advertise data with a given type. You cannot use a registered data type value {1} with this command. For LE the advertising shows up in the primary advertisements. If you set only the type of the data without any data (data 0x0c) this will cause a parse error when turning advertise on. You can modify the advertising data while it is advertising. To get the currently set data use the command data without any arguments. :Usage: **> data [type] [data=xx xx ...]** :Example: **> data 0x0C 01 0x0F 13** sr-uuids -------- Set/Get scan response uuids. :Usage: **# sr-uuids [all/uuid1 uuid2 ...]** sr-solicit ---------- Set/Get scan response solicit uuids. :Usage: **# sr-solicit [all/uuid1 uuid2 ...]** sr-service ---------- Set/Get scan response service data. :Usage: **# sr-service [uuid] [data=xx xx ...]** sr-manufacturer --------------- Set/Get scan response manufacturer data. :Usage: **# sr-manufacturer [id] [data=xx xx ...]** sr-data ------- Set/Get scan response data. :Usage: **# sr-data [type] [data=xx xx ...]** discoverable ------------ Set/Get advertise discoverable. For LE discoverable on will set the LE General Discoverable Mode flag to true in the primary advertisement if on. This feature can be changed during advertising, but will only trigger LE General Discoverable Mode even if you had previously selected discoverable-timeout this will be ignored. Entering the command by itself will show the status of the setting :Usage: **> discoverable [on/off]** discoverable-timeout -------------------- Set/Get advertise discoverable timeout. Using this feature in LE will cause the LE Limited Discoverable Mode flag to be set in the primary advertisement and The LE General Discoverable Mode flag will not be set. The LE Limited Discoverable Mode flag will automatically turn off after [seconds] discoverable [on] must be set to use this feature. Entering the command by itself will show the current value set. :Usage: **> discoverable-timeout [seconds]** tx-power -------- Show/Enable/Disable TX power to be advertised. This sets the TX Power Level field in the advertising packet. The value is in dBm and can be between -127 and 127. When this feature is turned on the LE device will advertise its transmit power in the primary advertisement. This feature can be modified while advertising. Entering the command by itself will show the current value set. :Usage: **> tx-power [on/off] [power]** name ---- Configure local name to be advertised. Local name to be used in the advertising report. If the string is too big to fit into the packet it will be truncated. It will either advertise as a complete local name or if it has to be truncated then a shortened local name. :Usage: **> name [on/off/name]** :Example: **> name "0123456789abcdef0123456789abcdef"** appearance ---------- Configure custom appearance to be advertised. :Usage: **> appearance [on/off/value]** duration -------- Set/Get advertise duration. The Duration parameter configures the length of an Instance. The value is in seconds. A value of 0 indicates a default value is chosen for the Duration. The default is 2 seconds. If only one advertising Instance has been added, then the Duration value will be ignored. If multiple advertising Instances have been added, then the Duration value will be used to determine the length of time each Instance is advertised for. The Duration value is used to calculate the number of advertising events that will be used to advertise each Instance. The number of advertising events is calculated by dividing the Duration value by the advertising interval. The advertising interval is determined by the advertising parameters that are set for each Instance. The advertising interval is the maximum of the advertising intervals set for each Instance. :Usage: **> duration [seconds]** timeout ------- Set/Get advertise timeout. :Usage: **> timeout [seconds]** secondary --------- Set/Get advertise secondary channel. :Usage: **> secondary [1M/2M/Coded]** interval -------- Set/Get advertise interval. The Interval parameter configures the advertising interval of an Instance. The value is in milliseconds. A value of 0 indicates a default value is chosen for the Interval. The default is 100 milliseconds. The Interval value is used to calculate the number of advertising events that will be used to advertise each Instance. The number of advertising events is calculated by dividing the Duration value by the advertising interval. The advertising interval is determined by the advertising parameters that are set for each Instance. The advertising interval is the maximum of the advertising intervals set for each Instance. :Usage: **> interval [milliseconds]** clear ----- Clear advertise config. This will stop advertising if it is currently advertising. If you want to change the advertise configuration while advertising you must first clear the advertise configuration and then set the new advertise configuration. :Usage: **> clear [uuids/service/manufacturer/config-name...]** RESOURCES ========= http://www.bluez.org REPORTING BUGS ============== linux-bluetooth@vger.kernel.org bluez-5.82/client/PaxHeaders/bluetoothctl-admin.rst0000644000000000000000000000005014766002272017462 xustar0020 atime=1743515577 20 ctime=1743591291 bluez-5.82/client/bluetoothctl-admin.rst0000644000000000000000000000134414766002272017145 0ustar00rootroot================== bluetoothctl-admin ================== -------------------- Admin Policy Submenu -------------------- :Version: BlueZ :Copyright: Free use of this software is granted under the terms of the GNU Lesser General Public Licenses (LGPL). :Date: November 2022 :Manual section: 1 :Manual group: Linux System Administration SYNOPSIS ======== **bluetoothctl** [--options] [admin.commands] Admin Policy Commands ===================== allow ----- Allow service UUIDs and block rest of them. :Usage: **> allow [clear/uuid1 uuid2 ...]** :Example: **> allow 0x1101 0x1102 0x1103** :Example: **> allow clear** RESOURCES ========= http://www.bluez.org REPORTING BUGS ============== linux-bluetooth@vger.kernel.org bluez-5.82/client/PaxHeaders/bluetoothctl-transport.rst0000644000000000000000000000005014766002272020426 xustar0020 atime=1743515577 20 ctime=1743591291 bluez-5.82/client/bluetoothctl-transport.rst0000644000000000000000000000555414766002272020120 0ustar00rootroot====================== bluetoothctl-transport ====================== ----------------------- Media Transport Submenu ----------------------- :Version: BlueZ :Copyright: Free use of this software is granted under the terms of the GNU Lesser General Public Licenses (LGPL). :Date: November 2022 :Manual section: 1 :Manual group: Linux System Administration SYNOPSIS ======== **bluetoothctl** [--options] [transport.commands] Media Transport Commands ========================= list ---- List available transports. :Usage: **> list** show ---- Show transport information. :Usage: **> show [transport]** acquire ------- Acquire transport. :Usage: **> acquire [transport1...]** Note: If running the setup with an audio server that has LE Audio support (such as PipeWire) it will automatically acquire transports according to the configured roles. select ------- Select transport. For transports created on a Broadcast Sink device only. This moves the transport to the "broadcasting" state, pending acquire. :Usage: **> select [transport1...]** Note: If the select command receives a list of transports, they will first be linked using the "Links" MediaTransport property. They will then be selected one by one, by calling the "Select" MediaTransport method. After the first transport is acquired, the Broadcast Sink will create fds for the associated stream and all its links. Each link can then be acquired one by one, setting the fd for the transport and starting to receive audio. The select command does not require a local endpoint to be registered beforehand. This is because if the setup runs with an audio server that has LE Audio support (such as PipeWire), the audio server is the one to register endpoints and the transports are created as a result. Once a transport is selected, the audio server will automatically acquire. unselect -------- Unselect transport. For transports created on a Broadcast Sink device only. This moves the transport to the "idle" state, pending release by the audio server. If the transport was acquired by bluetoothctl it can be released straight away, without having to be unselected. :Usage: **> unselect [transport1...]** Note: If running the setup with an audio server that has LE Audio support (such as PipeWire), it will prompt it to automatically release the transport. release ------- Release transport. :Usage: **> release [transport1...]** Note: Transports acquired by an audio server, can only be released by said audio server. send ---- Send contents of a file. :Usage: **> send ** receive ------- Get/Set file to receive. :Usage: **> receive [filename]** volume ------ Get/Set transport volume. :Usage: **> volume [value]** RESOURCES ========= http://www.bluez.org REPORTING BUGS ============== linux-bluetooth@vger.kernel.org bluez-5.82/client/PaxHeaders/bluetoothctl-endpoint.10000644000000000000000000000005014773213525017545 xustar0020 atime=1743591253 20 ctime=1743591291 bluez-5.82/client/bluetoothctl-endpoint.10000644000000000000000000001336414773213525017235 0ustar00rootroot.\" Man page generated from reStructuredText. . . .nr rst2man-indent-level 0 . .de1 rstReportMargin \\$1 \\n[an-margin] level \\n[rst2man-indent-level] level margin: \\n[rst2man-indent\\n[rst2man-indent-level]] - \\n[rst2man-indent0] \\n[rst2man-indent1] \\n[rst2man-indent2] .. .de1 INDENT .\" .rstReportMargin pre: . RS \\$1 . nr rst2man-indent\\n[rst2man-indent-level] \\n[an-margin] . nr rst2man-indent-level +1 .\" .rstReportMargin post: .. .de UNINDENT . RE .\" indent \\n[an-margin] .\" old: \\n[rst2man-indent\\n[rst2man-indent-level]] .nr rst2man-indent-level -1 .\" new: \\n[rst2man-indent\\n[rst2man-indent-level]] .in \\n[rst2man-indent\\n[rst2man-indent-level]]u .. .TH "BLUETOOTHCTL-ENDPOINT" "1" "November 2022" "BlueZ" "Linux System Administration" .SH NAME bluetoothctl-endpoint \- Endpoint Submenu .SH SYNOPSIS .sp \fBbluetoothctl\fP [\-\-options] [endpoint.commands] .SH ENDPOINT COMMANDS .SS list .sp List available endpoints. .INDENT 0.0 .TP .B Usage \fB> list [local]\fP .UNINDENT .SS show .sp Endpoint information. .INDENT 0.0 .TP .B Usage \fB> show [endpoint]\fP .UNINDENT .SS register .sp Register Endpoint. .INDENT 0.0 .TP .B Usage \fB> register [capabilities...]\fP .TP .B Example LC3 BAP source .nf \fB>endpoint.register 00002bcb\-0000\-1000\-8000\-00805f9b34fb 0x06\fP \fB>Auto Accept (yes/no):\fP y \fB>Max Transports (auto/value):\fP a \fB>Locations:\fP a \fB>Supported Context (value):\fP 3 \fB>Context (value):\fP 3 \fB>CIG (auto/value):\fP a \fB>CIS (auto/value):\fP a .fi .sp .TP .B Example LC3 BAP sink with extra capabilities .nf \fB>endpoint.register 00002bc9\-0000\-1000\-8000\-00805f9b34fb 0x06 \(dq0x03 0xe5 0x03 0x00 0x02 0xe6 0x07\(dq\fP \fB>Enter Metadata (value/no):\fP n \fB>Auto Accept (yes/no):\fP y \fB>Max Transports (auto/value):\fP a \fB>Locations:\fP a \fB>Supported Context (value):\fP 3 \fB>Context (value):\fP 3 \fB>CIG (auto/value):\fP a \fB>CIS (auto/value):\fP a .fi .sp .TP .B Example LC3 BAP Broadcast source .nf \fB>endpoint.register 00001852\-0000\-1000\-8000\-00805f9b34fb 0x06\fP \fB>Auto Accept (yes/no):\fP y \fB>Max Transports (auto/value):\fP a \fB>Locations:\fP 3 \fB>Supported Context (value):\fP 1 .fi .sp .TP .B Example LC3 BAP Broadcast sink .nf \fB>endpoint.register 00001851\-0000\-1000\-8000\-00805f9b34fb 0x06\fP \fB>Auto Accept (yes/no):\fP y \fB>Max Transports (auto/value):\fP a \fB>Locations:\fP 3 \fB>Supported Context (value):\fP 1 .fi .sp .UNINDENT .sp Note: .sp If running the setup with an audio server that has LE Audio support (such as PipeWire) it will automatically register endpoints according to the configured roles. For more details about configuring a Broadcast Source with PipeWire check: .SS unregister .sp Unregister Endpoint. .INDENT 0.0 .TP .B Usage \fB> unregister \fP .UNINDENT .sp Note: .sp If the endpoint was registered by an audio server, it can\(aqt be unregistered from bluetoothctl. This must be done by the audio server as well. .SS config .sp Configure Endpoint. .INDENT 0.0 .TP .B Usage \fB> config [preset]\fP .UNINDENT .sp Note: .sp If the endpoint was registered by an audio server, it can\(aqt be configured from bluetoothctl. This must be done by the audio server as well. .SS presets .sp List available presets. .INDENT 0.0 .TP .B Usage \fB> presets / [codec[:company]] [preset] [codec config] [metadata]\fP .TP .B Example using endpoint .nf \fB>presets /local/endpoint/ep0 32_1_1\fP \fB>presets /local/endpoint/ep0\fP Preset 32_1_1 Configuration.**>0: len 0x02 type 0x01 Configuration.Sampling Frequency: 32 Khz (0x06) Configuration.**>1: len 0x02 type 0x02 Configuration.Frame Duration: 7.5 ms (0x00) Configuration.**>2: len 0x03 type 0x04 Configuration.Frame Length: 60 (0x003c) .fi .sp .TP .B Example using UUID .nf \fB>presets 00002bc9\-0000\-1000\-8000\-00805f9b34fb 0x06 32_1_1\fP \fB>presets 00002bc9\-0000\-1000\-8000\-00805f9b34fb 0x06\fP \&... \fB*32_1_1\fP .fi .sp .TP .B Example setting up LC3 custom preset .nf \fB>presets 00002bc9\-0000\-1000\-8000\-00805f9b34fb 0x06 custom\fP \fB>[Codec] Enter frequency (Khz):\fP 48 \fB>[Codec] Enter frame duration (ms):\fP 10 \fB>[Codec] Enter channel allocation:\fP 3 \fB>[Codec] Enter frame length:\fP 100 \fB>[QoS] Enter Target Latency (Low, Balance, High):\fP Low \fB>[QoS] Enter SDU Interval (us):\fP 1000 \fB>[QoS] Enter Framing (Unframed, Framed):\fP Unframed \fB>[QoS] Enter PHY (1M, 2M):\fP 2M \fB>[QoS] Enter Max SDU:\fP 200 \fB>[QoS] Enter RTN:\fP 3 \fB>[QoS] Enter Max Transport Latency (ms):\fP 10 \fB>[QoS] Enter Presentation Delay (us):\fP 20000 \fB>presets 00002bc9\-0000\-1000\-8000\-00805f9b34fb 0x06\fP \&... \fB*custom\fP .fi .sp .TP .B Example setting up LC3 custom preset with extra configuration .nf \fB>presets 00002bc9\-0000\-1000\-8000\-00805f9b34fb 0x06 custom \(dq0x03 0xe8 0x00 0x00 0x02 0xe9 0x00\(dq\fP \fB>[Codec] Enter frequency (Khz):\fP 48 \fB>[Codec] Enter frame duration (ms):\fP 10 \fB>[Codec] Enter channel allocation:\fP 3 \fB>[Codec] Enter frame length:\fP 100 \fB>[QoS] Enter Target Latency (Low, Balance, High):\fP Low \fB>[QoS] Enter SDU Interval (us):\fP 1000 \fB>[QoS] Enter Framing (Unframed, Framed):\fP Unframed \fB>[QoS] Enter PHY (1M, 2M):\fP 2M \fB>[QoS] Enter Max SDU:\fP 200 \fB>[QoS] Enter RTN:\fP 3 \fB>[QoS] Enter Max Transport Latency (ms):\fP 10 \fB>[QoS] Enter Presentation Delay (us):\fP 20000 \fB>presets 00002bc9\-0000\-1000\-8000\-00805f9b34fb 0x06\fP \&... \fB*custom\fP .fi .sp .UNINDENT .SH RESOURCES .sp .SH REPORTING BUGS .sp .SH COPYRIGHT Free use of this software is granted under the terms of the GNU Lesser General Public Licenses (LGPL). .\" Generated by docutils manpage writer. . bluez-5.82/client/PaxHeaders/bluetoothctl-monitor.rst0000644000000000000000000000005014766002272020061 xustar0020 atime=1743515577 20 ctime=1743591291 bluez-5.82/client/bluetoothctl-monitor.rst0000644000000000000000000000303414766002272017542 0ustar00rootroot==================== bluetoothctl-monitor ==================== --------------- Monitor Submenu --------------- :Version: BlueZ :Copyright: Free use of this software is granted under the terms of the GNU Lesser General Public Licenses (LGPL). :Date: July 2023 :Manual section: 1 :Manual group: Linux System Administration SYNOPSIS ======== **bluetoothctl** [--options] [monitor.commands] Monitor Commands ================ set-rssi-threshold ------------------ Set RSSI threshold parameter :Usage: **> set-rssi-threshold ** set-rssi-timeout ---------------- Set RSSI timeout parameter :Usage: **> set-rssi-timeout ** set-rssi-sampling-period ------------------------- Set RSSI sampling period parameter :Usage: **> set-rssi-timeout ** add-or-pattern -------------- Register 'or pattern' type monitor with the specified RSSI parameters :Usage: **> add-or-pattern [patterns=pattern1 pattern2 ...]** get-pattern ----------- Get advertisement monitor :Usage: **> get-pattern ** remove-pattern -------------- Remove advertisement monitor :Usage: **> remove-pattern ** get-supported-info ------------------ Get advertisement manager supported features and supported monitor types :Usage: **> get-supported-info** print-usage ----------- Print the command usage :Usage: **> print-usage ** RESOURCES ========= http://www.bluez.org REPORTING BUGS ============== linux-bluetooth@vger.kernel.org bluez-5.82/client/PaxHeaders/mgmt.h0000644000000000000000000000005014471706457014260 xustar0020 atime=1743515768 20 ctime=1743591281 bluez-5.82/client/mgmt.h0000644000000000000000000000042214471706457013737 0ustar00rootroot// SPDX-License-Identifier: GPL-2.0-or-later /* * * BlueZ - Bluetooth protocol stack for Linux * * Copyright (C) 2023 Intel Corporation. All rights reserved. * * */ bool mgmt_add_submenu(void); void mgmt_remove_submenu(void); void mgmt_set_index(const char *arg); bluez-5.82/client/PaxHeaders/gatt.c0000644000000000000000000000005014772767672014260 xustar0020 atime=1743515579 20 ctime=1743591281 bluez-5.82/client/gatt.c0000644000000000000000000023727614772767672013762 0ustar00rootroot// SPDX-License-Identifier: GPL-2.0-or-later /* * * BlueZ - Bluetooth protocol stack for Linux * * Copyright (C) 2014 Intel Corporation. All rights reserved. * Copyright 2024 NXP * * */ #ifdef HAVE_CONFIG_H #include #endif #define _GNU_SOURCE #include #include #include #include #include #include #include #include #include #include #include #include "src/shared/util.h" #include "src/shared/queue.h" #include "src/shared/io.h" #include "src/shared/shell.h" #include "gdbus/gdbus.h" #include "gatt.h" #define APP_PATH "/org/bluez/app" #define DEVICE_INTERFACE "org.bluez.Device1" #define PROFILE_INTERFACE "org.bluez.GattProfile1" #define SERVICE_INTERFACE "org.bluez.GattService1" #define CHRC_INTERFACE "org.bluez.GattCharacteristic1" #define DESC_INTERFACE "org.bluez.GattDescriptor1" /* String display constants */ #define COLORED_NEW COLOR_GREEN "NEW" COLOR_OFF #define COLORED_CHG COLOR_YELLOW "CHG" COLOR_OFF #define COLORED_DEL COLOR_RED "DEL" COLOR_OFF #define MAX_ATTR_VAL_LEN 512 struct desc { struct chrc *chrc; char *path; uint16_t handle; char *uuid; char **flags; size_t value_len; unsigned int max_val_len; uint8_t *value; }; struct chrc { struct service *service; GDBusProxy *proxy; char *path; uint16_t handle; char *uuid; char **flags; bool notifying; GList *descs; size_t value_len; unsigned int max_val_len; uint8_t *value; uint16_t mtu; struct io *write_io; struct io *notify_io; bool authorization_req; }; struct service { DBusConnection *conn; GDBusProxy *proxy; char *path; uint16_t handle; char *uuid; bool primary; GList *chrcs; GList *inc; }; static GList *local_services; static GList *services; static GList *characteristics; static GList *descriptors; static GList *managers; static GList *uuids; static DBusMessage *pending_message = NULL; struct sock_io { GDBusProxy *proxy; struct io *io; uint16_t mtu; }; static struct sock_io write_io; static struct sock_io notify_io; static void print_service(struct service *service, const char *description) { const char *text; text = bt_uuidstr_to_str(service->uuid); if (!text) bt_shell_printf("%s%s%s%s Service (Handle 0x%04x)\n\t%s\n\t" "%s\n", description ? "[" : "", description ? : "", description ? "] " : "", service->primary ? "Primary" : "Secondary", service->handle, service->path, service->uuid); else bt_shell_printf("%s%s%s%s Service (Handle 0x%04x)\n\t%s\n\t%s" "\n\t%s\n", description ? "[" : "", description ? : "", description ? "] " : "", service->primary ? "Primary" : "Secondary", service->handle, service->path, service->uuid, text); } static void print_inc_service(struct service *service, const char *description) { const char *text; text = bt_uuidstr_to_str(service->uuid); if (!text) bt_shell_printf("%s%s%s%s Included Service (Handle 0x%04x)\n\t" "%s\n\t%s\n", description ? "[" : "", description ? : "", description ? "] " : "", service->primary ? "Primary" : "Secondary", service->handle, service->path, service->uuid); else bt_shell_printf("%s%s%s%s Included Service (Handle 0x%04x)\n\t" "%s\n\t%s\n\t%s\n", description ? "[" : "", description ? : "", description ? "] " : "", service->primary ? "Primary" : "Secondary", service->handle, service->path, service->uuid, text); } static void print_service_proxy(GDBusProxy *proxy, const char *description) { struct service service; DBusMessageIter iter; const char *uuid; dbus_bool_t primary; uint16_t handle; if (g_dbus_proxy_get_property(proxy, "UUID", &iter) == FALSE) return; dbus_message_iter_get_basic(&iter, &uuid); if (g_dbus_proxy_get_property(proxy, "Primary", &iter) == FALSE) return; dbus_message_iter_get_basic(&iter, &primary); if (g_dbus_proxy_get_property(proxy, "Handle", &iter) == FALSE) return; dbus_message_iter_get_basic(&iter, &handle); memset(&service, 0, sizeof(service)); service.path = (char *) g_dbus_proxy_get_path(proxy); service.uuid = (char *) uuid; service.primary = primary; service.handle = handle; print_service(&service, description); } void gatt_add_service(GDBusProxy *proxy) { services = g_list_append(services, proxy); print_service_proxy(proxy, COLORED_NEW); } static struct service *remove_service_by_proxy(struct GDBusProxy *proxy) { GList *l; for (l = local_services; l; l = g_list_next(l)) { struct service *service = l->data; if (service->proxy == proxy) { local_services = g_list_delete_link(local_services, l); return service; } } return NULL; } void gatt_remove_service(GDBusProxy *proxy) { struct service *service; GList *l; l = g_list_find(services, proxy); if (!l) return; services = g_list_delete_link(services, l); print_service_proxy(proxy, COLORED_DEL); service = remove_service_by_proxy(proxy); if (service) g_dbus_unregister_interface(service->conn, service->path, SERVICE_INTERFACE); } static void print_chrc(struct chrc *chrc, const char *description) { const char *text; text = bt_uuidstr_to_str(chrc->uuid); if (!text) bt_shell_printf("%s%s%sCharacteristic (Handle 0x%04x)\n\t%s\n\t" "%s\n", description ? "[" : "", description ? : "", description ? "] " : "", chrc->handle, chrc->path, chrc->uuid); else bt_shell_printf("%s%s%sCharacteristic (Handle 0x%04x)\n\t%s\n\t" "%s\n\t%s\n", description ? "[" : "", description ? : "", description ? "] " : "", chrc->handle, chrc->path, chrc->uuid, text); } static void print_characteristic(GDBusProxy *proxy, const char *description) { struct chrc chrc; DBusMessageIter iter; const char *uuid; uint16_t handle; if (g_dbus_proxy_get_property(proxy, "UUID", &iter) == FALSE) return; dbus_message_iter_get_basic(&iter, &uuid); if (g_dbus_proxy_get_property(proxy, "Handle", &iter) == FALSE) return; dbus_message_iter_get_basic(&iter, &handle); memset(&chrc, 0, sizeof(chrc)); chrc.path = (char *) g_dbus_proxy_get_path(proxy); chrc.uuid = (char *) uuid; chrc.handle = handle; print_chrc(&chrc, description); } static gboolean chrc_is_child(GDBusProxy *characteristic) { DBusMessageIter iter; const char *service; if (!g_dbus_proxy_get_property(characteristic, "Service", &iter)) return FALSE; dbus_message_iter_get_basic(&iter, &service); return g_dbus_proxy_lookup(services, NULL, service, "org.bluez.GattService1") != NULL; } void gatt_add_characteristic(GDBusProxy *proxy) { if (!chrc_is_child(proxy)) return; characteristics = g_list_append(characteristics, proxy); print_characteristic(proxy, COLORED_NEW); } static void notify_io_destroy(void) { io_destroy(notify_io.io); memset(¬ify_io, 0, sizeof(notify_io)); } static void write_io_destroy(void) { io_destroy(write_io.io); memset(&write_io, 0, sizeof(write_io)); } void gatt_remove_characteristic(GDBusProxy *proxy) { GList *l; l = g_list_find(characteristics, proxy); if (!l) return; characteristics = g_list_delete_link(characteristics, l); print_characteristic(proxy, COLORED_DEL); if (write_io.proxy == proxy) write_io_destroy(); else if (notify_io.proxy == proxy) notify_io_destroy(); } static void print_desc(struct desc *desc, const char *description) { const char *text; text = bt_uuidstr_to_str(desc->uuid); if (!text) bt_shell_printf("%s%s%sDescriptor (Handle 0x%04x)\n\t%s\n\t" "%s\n", description ? "[" : "", description ? : "", description ? "] " : "", desc->handle, desc->path, desc->uuid); else bt_shell_printf("%s%s%sDescriptor (Handle 0x%04x)\n\t%s\n\t" "%s\n\t%s\n", description ? "[" : "", description ? : "", description ? "] " : "", desc->handle, desc->path, desc->uuid, text); } static void print_descriptor(GDBusProxy *proxy, const char *description) { struct desc desc; DBusMessageIter iter; const char *uuid; uint16_t handle; if (g_dbus_proxy_get_property(proxy, "UUID", &iter) == FALSE) return; dbus_message_iter_get_basic(&iter, &uuid); if (g_dbus_proxy_get_property(proxy, "Handle", &iter) == FALSE) return; dbus_message_iter_get_basic(&iter, &handle); memset(&desc, 0, sizeof(desc)); desc.path = (char *) g_dbus_proxy_get_path(proxy); desc.uuid = (char *) uuid; desc.handle = handle; print_desc(&desc, description); } static gboolean descriptor_is_child(GDBusProxy *characteristic) { GList *l; DBusMessageIter iter; const char *service, *path; if (!g_dbus_proxy_get_property(characteristic, "Characteristic", &iter)) return FALSE; dbus_message_iter_get_basic(&iter, &service); for (l = characteristics; l; l = g_list_next(l)) { GDBusProxy *proxy = l->data; path = g_dbus_proxy_get_path(proxy); if (!strcmp(path, service)) return TRUE; } return FALSE; } void gatt_add_descriptor(GDBusProxy *proxy) { if (!descriptor_is_child(proxy)) return; descriptors = g_list_append(descriptors, proxy); print_descriptor(proxy, COLORED_NEW); } void gatt_remove_descriptor(GDBusProxy *proxy) { GList *l; l = g_list_find(descriptors, proxy); if (!l) return; descriptors = g_list_delete_link(descriptors, l); print_descriptor(proxy, COLORED_DEL); } static void list_attributes(const char *path, GList *source) { GList *l; for (l = source; l; l = g_list_next(l)) { GDBusProxy *proxy = l->data; const char *proxy_path; proxy_path = g_dbus_proxy_get_path(proxy); if (!g_str_has_prefix(proxy_path, path)) continue; if (source == services) { print_service_proxy(proxy, NULL); list_attributes(proxy_path, characteristics); } else if (source == characteristics) { print_characteristic(proxy, NULL); list_attributes(proxy_path, descriptors); } else if (source == descriptors) print_descriptor(proxy, NULL); } } static void list_descs(GList *descs) { GList *l; for (l = descs; l; l = g_list_next(l)) { struct desc *desc = l->data; print_desc(desc, NULL); } } static void list_chrcs(GList *chrcs) { GList *l; for (l = chrcs; l; l = g_list_next(l)) { struct chrc *chrc = l->data; print_chrc(chrc, NULL); list_descs(chrc->descs); } } static void list_services(void) { GList *l; for (l = local_services; l; l = g_list_next(l)) { struct service *service = l->data; print_service(service, NULL); list_chrcs(service->chrcs); } } void gatt_list_attributes(const char *path) { if (path && !strcmp(path, "local")) { list_services(); return bt_shell_noninteractive_quit(EXIT_SUCCESS); } list_attributes(path, services); return bt_shell_noninteractive_quit(EXIT_SUCCESS); } static GDBusProxy *select_attribute(const char *path) { GDBusProxy *proxy; proxy = g_dbus_proxy_lookup(services, NULL, path, "org.bluez.GattService1"); if (proxy) return proxy; proxy = g_dbus_proxy_lookup(characteristics, NULL, path, "org.bluez.GattCharacteristic1"); if (proxy) return proxy; return g_dbus_proxy_lookup(descriptors, NULL, path, "org.bluez.GattDescriptor1"); } static GDBusProxy *select_proxy_by_uuid(GDBusProxy *parent, const char *uuid, GList *source) { GList *l; const char *value; DBusMessageIter iter; for (l = source; l; l = g_list_next(l)) { GDBusProxy *proxy = l->data; if (parent && !g_str_has_prefix(g_dbus_proxy_get_path(proxy), g_dbus_proxy_get_path(parent))) continue; if (g_dbus_proxy_get_property(proxy, "UUID", &iter) == FALSE) continue; dbus_message_iter_get_basic(&iter, &value); if (strcasecmp(uuid, value) == 0) return proxy; if (strlen(uuid) == 4 && !strncasecmp(value + 4, uuid, 4)) return proxy; } return NULL; } static GDBusProxy *select_attribute_by_uuid(GDBusProxy *parent, const char *uuid) { GDBusProxy *proxy; proxy = select_proxy_by_uuid(parent, uuid, services); if (proxy) return proxy; proxy = select_proxy_by_uuid(parent, uuid, characteristics); if (proxy) return proxy; return select_proxy_by_uuid(parent, uuid, descriptors); } GDBusProxy *gatt_select_attribute(GDBusProxy *parent, const char *arg) { if (arg[0] == '/') return select_attribute(arg); if (parent) { GDBusProxy *proxy = select_attribute_by_uuid(parent, arg); if (proxy) return proxy; } return select_attribute_by_uuid(NULL, arg); } static char *find_local_attribute(const char *arg, struct service **service, struct chrc **chrc, struct desc **desc) { GList *l; for (l = local_services; l; l = g_list_next(l)) { struct service *s = l->data; GList *cl; if (!strcmp(arg, s->path)) { if (service) *service = s; return s->path; } if (!strcmp(arg, s->uuid)) { if (service) *service = s; return s->path; } for (cl = s->chrcs; cl; cl = g_list_next(cl)) { struct chrc *c = cl->data; GList *dl; if (!strcmp(arg, c->path)) { if (chrc) *chrc = c; return c->path; } if (!strcmp(arg, c->uuid)) { if (chrc) *chrc = c; return c->path; } for (dl = c->descs; dl; dl = g_list_next(dl)) { struct desc *d = dl->data; if (!strcmp(arg, d->path)) { if (desc) *desc = d; return d->path; } if (!strcmp(arg, d->uuid)) { if (desc) *desc = d; return d->path; } } } } return NULL; } char *gatt_select_local_attribute(const char *arg) { return find_local_attribute(arg, NULL, NULL, NULL); } static int parse_offset(const char *arg) { char *endptr = NULL; unsigned long offset; offset = strtoul(arg, &endptr, 0); if (!endptr || *endptr != '\0' || offset > UINT16_MAX) { bt_shell_printf("Invalid offload: %s", arg); return -EINVAL; } return offset; } void gatt_read_local_attribute(char *data, int argc, char *argv[]) { int offset = 0; struct service *s = NULL; struct chrc *c = NULL; struct desc *d = NULL; if (!find_local_attribute(data, &s, &c, &d)) { bt_shell_printf("Unable to find local attribute %s\n", data); return bt_shell_noninteractive_quit(EXIT_FAILURE); } if (argc > 1) { offset = parse_offset(argv[1]); if (offset < 0) return bt_shell_noninteractive_quit(EXIT_FAILURE); } if (s) { bt_shell_printf("UUID %s", s->uuid); return bt_shell_noninteractive_quit(EXIT_SUCCESS); } if (c) { if ((size_t)offset >= c->value_len) { bt_shell_printf("Invalid offset: %d >= %zd", offset, c->value_len); return bt_shell_noninteractive_quit(EXIT_FAILURE); } bt_shell_hexdump(&c->value[offset], c->value_len - offset); return bt_shell_noninteractive_quit(EXIT_SUCCESS); } if (d) { if ((size_t)offset >= d->value_len) { bt_shell_printf("Invalid offset: %d >= %zd", offset, d->value_len); return bt_shell_noninteractive_quit(EXIT_FAILURE); } bt_shell_hexdump(&d->value[offset], d->value_len - offset); return bt_shell_noninteractive_quit(EXIT_SUCCESS); } } static uint8_t *str2bytearray(char *arg, size_t *val_len) { uint8_t value[MAX_ATTR_VAL_LEN]; char *entry; unsigned int i; for (i = 0; (entry = strsep(&arg, " \t")) != NULL; i++) { long val; char *endptr = NULL; if (*entry == '\0') continue; if (i >= G_N_ELEMENTS(value)) { bt_shell_printf("Too much data\n"); return NULL; } val = strtol(entry, &endptr, 0); if (!endptr || *endptr != '\0' || val > UINT8_MAX) { bt_shell_printf("Invalid value at index %d\n", i); return NULL; } value[i] = val; } *val_len = i; return util_memdup(value, i); } static int write_value(size_t *dst_len, uint8_t **dst_value, uint8_t *src_val, size_t src_len, uint16_t offset, uint16_t max_len) { if ((offset + src_len) > max_len) return -EOVERFLOW; if ((offset + src_len) != *dst_len) { *dst_len = offset + src_len; *dst_value = g_realloc(*dst_value, *dst_len); } if (src_val && src_len) memcpy(*dst_value + offset, src_val, src_len); return 0; } void gatt_write_local_attribute(char *data, int argc, char *argv[]) { int offset = 0; struct service *s = NULL; struct chrc *c = NULL; struct desc *d = NULL; uint8_t *value; size_t value_len; if (!find_local_attribute(data, &s, &c, &d)) { bt_shell_printf("Unable to find local attribute %s\n", data); return bt_shell_noninteractive_quit(EXIT_FAILURE); } value = str2bytearray(argv[1], &value_len); if (!value) return bt_shell_noninteractive_quit(EXIT_FAILURE); if (argc > 2) { offset = parse_offset(argv[2]); if (offset < 0) return bt_shell_noninteractive_quit(EXIT_FAILURE); } if (s) { bt_shell_printf("Unable to overwrite local service %s\n", data); return bt_shell_noninteractive_quit(EXIT_FAILURE); } if (c) { if (write_value(&c->value_len, &c->value, value, value_len, offset, c->max_val_len)) { bt_shell_printf("Unable to write local attribute %s\n", data); return bt_shell_noninteractive_quit(EXIT_FAILURE); } bt_shell_printf("[" COLORED_CHG "] Attribute %s (%s) written\n", c->path, bt_uuidstr_to_str(c->uuid)); g_dbus_emit_property_changed(c->service->conn, c->path, CHRC_INTERFACE, "Value"); } if (d) { if (write_value(&d->value_len, &d->value, value, value_len, offset, d->max_val_len)) { bt_shell_printf("Unable to write local attribute %s\n", data); return bt_shell_noninteractive_quit(EXIT_FAILURE); } bt_shell_printf("[" COLORED_CHG "] Attribute %s (%s) written\n", d->path, bt_uuidstr_to_str(d->uuid)); g_dbus_emit_property_changed(d->chrc->service->conn, d->path, DESC_INTERFACE, "Value"); } free(value); } static char *attribute_generator(const char *text, int state, GList *source) { static int index; if (!state) { index = 0; } return g_dbus_proxy_path_lookup(source, &index, text); } char *gatt_attribute_generator(const char *text, int state) { static GList *list = NULL; if (!state) { GList *list1; if (list) { g_list_free(list); list = NULL; } list1 = g_list_copy(characteristics); list1 = g_list_concat(list1, g_list_copy(descriptors)); list = g_list_copy(services); list = g_list_concat(list, list1); } return attribute_generator(text, state, list); } static void read_reply(DBusMessage *message, void *user_data) { DBusError error; DBusMessageIter iter, array; uint8_t *value; int len; dbus_error_init(&error); if (dbus_set_error_from_message(&error, message) == TRUE) { bt_shell_printf("Failed to read: %s\n", error.name); dbus_error_free(&error); return bt_shell_noninteractive_quit(EXIT_FAILURE); } dbus_message_iter_init(message, &iter); if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_ARRAY) { bt_shell_printf("Invalid response to read\n"); return bt_shell_noninteractive_quit(EXIT_FAILURE); } dbus_message_iter_recurse(&iter, &array); dbus_message_iter_get_fixed_array(&array, &value, &len); if (len < 0) { bt_shell_printf("Unable to parse value\n"); return bt_shell_noninteractive_quit(EXIT_FAILURE); } bt_shell_hexdump(value, len); return bt_shell_noninteractive_quit(EXIT_SUCCESS); } static void read_setup(DBusMessageIter *iter, void *user_data) { DBusMessageIter dict; uint16_t *offset = user_data; dbus_message_iter_open_container(iter, DBUS_TYPE_ARRAY, DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING DBUS_TYPE_STRING_AS_STRING DBUS_TYPE_VARIANT_AS_STRING DBUS_DICT_ENTRY_END_CHAR_AS_STRING, &dict); g_dbus_dict_append_entry(&dict, "offset", DBUS_TYPE_UINT16, offset); dbus_message_iter_close_container(iter, &dict); } static void read_attribute(GDBusProxy *proxy, uint16_t offset) { if (g_dbus_proxy_method_call(proxy, "ReadValue", read_setup, read_reply, &offset, NULL) == FALSE) { bt_shell_printf("Failed to read\n"); return bt_shell_noninteractive_quit(EXIT_FAILURE); } bt_shell_printf("Attempting to read %s\n", g_dbus_proxy_get_path(proxy)); } void gatt_read_attribute(GDBusProxy *proxy, int argc, char *argv[]) { const char *iface; int offset = 0; iface = g_dbus_proxy_get_interface(proxy); if (!strcmp(iface, "org.bluez.GattCharacteristic1") || !strcmp(iface, "org.bluez.GattDescriptor1")) { if (argc == 2) { offset = parse_offset(argv[1]); if (offset < 0) goto fail; } read_attribute(proxy, offset); return; } fail: bt_shell_printf("Unable to read attribute %s\n", g_dbus_proxy_get_path(proxy)); return bt_shell_noninteractive_quit(EXIT_FAILURE); } static void write_reply(DBusMessage *message, void *user_data) { DBusError error; dbus_error_init(&error); if (dbus_set_error_from_message(&error, message) == TRUE) { bt_shell_printf("Failed to write: %s\n", error.name); dbus_error_free(&error); return bt_shell_noninteractive_quit(EXIT_FAILURE); } return bt_shell_noninteractive_quit(EXIT_SUCCESS); } struct write_attribute_data { DBusMessage *msg; struct iovec iov; char *type; uint16_t offset; }; static void write_setup(DBusMessageIter *iter, void *user_data) { struct write_attribute_data *wd = user_data; DBusMessageIter array, dict; dbus_message_iter_open_container(iter, DBUS_TYPE_ARRAY, "y", &array); dbus_message_iter_append_fixed_array(&array, DBUS_TYPE_BYTE, &wd->iov.iov_base, wd->iov.iov_len); dbus_message_iter_close_container(iter, &array); dbus_message_iter_open_container(iter, DBUS_TYPE_ARRAY, DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING DBUS_TYPE_STRING_AS_STRING DBUS_TYPE_VARIANT_AS_STRING DBUS_DICT_ENTRY_END_CHAR_AS_STRING, &dict); if (wd->type) g_dbus_dict_append_entry(&dict, "type", DBUS_TYPE_STRING, &wd->type); g_dbus_dict_append_entry(&dict, "offset", DBUS_TYPE_UINT16, &wd->offset); dbus_message_iter_close_container(iter, &dict); } static int sock_send(struct io *io, struct iovec *iov, size_t iovlen) { struct msghdr msg; int ret; ret = io_get_fd(io); if (ret < 0) return ret; memset(&msg, 0, sizeof(msg)); msg.msg_iov = iov; msg.msg_iovlen = iovlen; ret = sendmsg(ret, &msg, MSG_NOSIGNAL); if (ret < 0) { ret = -errno; bt_shell_printf("sendmsg: %s", strerror(-ret)); } return ret; } static void write_attribute(GDBusProxy *proxy, struct write_attribute_data *data) { /* Write using the fd if it has been acquired and fit the MTU */ if (proxy == write_io.proxy && (write_io.io && write_io.mtu >= data->iov.iov_len)) { bt_shell_printf("Attempting to write fd %d\n", io_get_fd(write_io.io)); if (sock_send(write_io.io, &data->iov, 1) < 0) { bt_shell_printf("Failed to write: %s", strerror(errno)); return bt_shell_noninteractive_quit(EXIT_FAILURE); } return; } if (g_dbus_proxy_method_call(proxy, "WriteValue", write_setup, write_reply, data, NULL) == FALSE) { bt_shell_printf("Failed to write\n"); return bt_shell_noninteractive_quit(EXIT_FAILURE); } bt_shell_printf("Attempting to write %s\n", g_dbus_proxy_get_path(proxy)); } void gatt_write_attribute(GDBusProxy *proxy, int argc, char *argv[]) { const char *iface; struct write_attribute_data data; memset(&data, 0, sizeof(data)); iface = g_dbus_proxy_get_interface(proxy); if (!strcmp(iface, "org.bluez.GattCharacteristic1") || !strcmp(iface, "org.bluez.GattDescriptor1")) { data.iov.iov_base = str2bytearray(argv[1], &data.iov.iov_len); if (argc > 2) { int offset; offset = parse_offset(argv[2]); if (offset < 0) goto fail; data.offset = offset; } if (argc > 3) data.type = argv[3]; write_attribute(proxy, &data); return; } fail: bt_shell_printf("Unable to write attribute %s\n", g_dbus_proxy_get_path(proxy)); return bt_shell_noninteractive_quit(EXIT_FAILURE); } static bool sock_read(struct io *io, void *user_data) { struct chrc *chrc = user_data; struct msghdr msg; struct iovec iov; uint8_t buf[MAX_ATTR_VAL_LEN]; int fd = io_get_fd(io); ssize_t bytes_read; if (io != notify_io.io && !chrc) return true; if (fd < 0) { bt_shell_printf("recvmsg: %s", strerror(-fd)); return false; } iov.iov_base = buf; iov.iov_len = sizeof(buf); memset(&msg, 0, sizeof(msg)); msg.msg_iov = &iov; msg.msg_iovlen = 1; bytes_read = recvmsg(fd, &msg, MSG_DONTWAIT); if (bytes_read < 0) { bt_shell_printf("recvmsg: %s", strerror(errno)); return false; } if (!bytes_read) return false; if (chrc) bt_shell_printf("[" COLORED_CHG "] Attribute %s (%s) " "written:\n", chrc->path, bt_uuidstr_to_str(chrc->uuid)); else bt_shell_printf("[" COLORED_CHG "] %s Notification:\n", g_dbus_proxy_get_path(notify_io.proxy)); bt_shell_hexdump(buf, bytes_read); return true; } static bool sock_hup(struct io *io, void *user_data) { struct chrc *chrc = user_data; if (chrc) { bt_shell_printf("Attribute %s %s sock closed\n", chrc->path, io == chrc->write_io ? "Write" : "Notify"); if (io == chrc->write_io) { io_destroy(chrc->write_io); chrc->write_io = NULL; } else { io_destroy(chrc->notify_io); chrc->notify_io = NULL; } return false; } bt_shell_printf("%s closed\n", io == notify_io.io ? "Notify" : "Write"); if (io == notify_io.io) notify_io_destroy(); else write_io_destroy(); return false; } static struct io *sock_io_new(int fd, void *user_data) { struct io *io; io = io_new(fd); io_set_close_on_destroy(io, true); io_set_read_handler(io, sock_read, user_data, NULL); io_set_disconnect_handler(io, sock_hup, user_data, NULL); return io; } static void acquire_write_reply(DBusMessage *message, void *user_data) { DBusError error; int fd; dbus_error_init(&error); if (dbus_set_error_from_message(&error, message) == TRUE) { bt_shell_printf("Failed to acquire write: %s\n", error.name); dbus_error_free(&error); write_io.proxy = NULL; return bt_shell_noninteractive_quit(EXIT_FAILURE); } if (write_io.io) write_io_destroy(); if ((dbus_message_get_args(message, NULL, DBUS_TYPE_UNIX_FD, &fd, DBUS_TYPE_UINT16, &write_io.mtu, DBUS_TYPE_INVALID) == false)) { bt_shell_printf("Invalid AcquireWrite response\n"); return bt_shell_noninteractive_quit(EXIT_FAILURE); } bt_shell_printf("AcquireWrite success: fd %d MTU %u\n", fd, write_io.mtu); write_io.io = sock_io_new(fd, NULL); return bt_shell_noninteractive_quit(EXIT_SUCCESS); } static void acquire_setup(DBusMessageIter *iter, void *user_data) { DBusMessageIter dict; dbus_message_iter_open_container(iter, DBUS_TYPE_ARRAY, DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING DBUS_TYPE_STRING_AS_STRING DBUS_TYPE_VARIANT_AS_STRING DBUS_DICT_ENTRY_END_CHAR_AS_STRING, &dict); dbus_message_iter_close_container(iter, &dict); } void gatt_acquire_write(GDBusProxy *proxy, const char *arg) { const char *iface; iface = g_dbus_proxy_get_interface(proxy); if (strcmp(iface, "org.bluez.GattCharacteristic1")) { bt_shell_printf("Unable to acquire write: %s not a" " characteristic\n", g_dbus_proxy_get_path(proxy)); return bt_shell_noninteractive_quit(EXIT_FAILURE); } if (g_dbus_proxy_method_call(proxy, "AcquireWrite", acquire_setup, acquire_write_reply, NULL, NULL) == FALSE) { bt_shell_printf("Failed to AcquireWrite\n"); return bt_shell_noninteractive_quit(EXIT_FAILURE); } write_io.proxy = proxy; } void gatt_release_write(GDBusProxy *proxy, const char *arg) { if (proxy != write_io.proxy || !write_io.io) { bt_shell_printf("Write not acquired\n"); return bt_shell_noninteractive_quit(EXIT_FAILURE); } write_io_destroy(); return bt_shell_noninteractive_quit(EXIT_SUCCESS); } static void acquire_notify_reply(DBusMessage *message, void *user_data) { DBusError error; int fd; dbus_error_init(&error); if (dbus_set_error_from_message(&error, message) == TRUE) { bt_shell_printf("Failed to acquire notify: %s\n", error.name); dbus_error_free(&error); write_io.proxy = NULL; return bt_shell_noninteractive_quit(EXIT_FAILURE); } if (notify_io.io) { io_destroy(notify_io.io); notify_io.io = NULL; } notify_io.mtu = 0; if ((dbus_message_get_args(message, NULL, DBUS_TYPE_UNIX_FD, &fd, DBUS_TYPE_UINT16, ¬ify_io.mtu, DBUS_TYPE_INVALID) == false)) { bt_shell_printf("Invalid AcquireNotify response\n"); return bt_shell_noninteractive_quit(EXIT_FAILURE); } bt_shell_printf("AcquireNotify success: fd %d MTU %u\n", fd, notify_io.mtu); notify_io.io = sock_io_new(fd, NULL); return bt_shell_noninteractive_quit(EXIT_SUCCESS); } void gatt_acquire_notify(GDBusProxy *proxy, const char *arg) { const char *iface; iface = g_dbus_proxy_get_interface(proxy); if (strcmp(iface, "org.bluez.GattCharacteristic1")) { bt_shell_printf("Unable to acquire notify: %s not a" " characteristic\n", g_dbus_proxy_get_path(proxy)); return bt_shell_noninteractive_quit(EXIT_FAILURE); } if (g_dbus_proxy_method_call(proxy, "AcquireNotify", acquire_setup, acquire_notify_reply, NULL, NULL) == FALSE) { bt_shell_printf("Failed to AcquireNotify\n"); return bt_shell_noninteractive_quit(EXIT_FAILURE); } notify_io.proxy = proxy; } void gatt_release_notify(GDBusProxy *proxy, const char *arg) { if (proxy != notify_io.proxy || !notify_io.io) { bt_shell_printf("Notify not acquired\n"); return bt_shell_noninteractive_quit(EXIT_FAILURE); } notify_io_destroy(); return bt_shell_noninteractive_quit(EXIT_SUCCESS); } static void notify_reply(DBusMessage *message, void *user_data) { bool enable = GPOINTER_TO_UINT(user_data); DBusError error; dbus_error_init(&error); if (dbus_set_error_from_message(&error, message) == TRUE) { bt_shell_printf("Failed to %s notify: %s\n", enable ? "start" : "stop", error.name); dbus_error_free(&error); return bt_shell_noninteractive_quit(EXIT_FAILURE); } bt_shell_printf("Notify %s\n", enable == TRUE ? "started" : "stopped"); return bt_shell_noninteractive_quit(EXIT_SUCCESS); } static void notify_attribute(GDBusProxy *proxy, bool enable) { const char *method; if (enable == TRUE) method = "StartNotify"; else method = "StopNotify"; if (g_dbus_proxy_method_call(proxy, method, NULL, notify_reply, GUINT_TO_POINTER(enable), NULL) == FALSE) { bt_shell_printf("Failed to %s notify\n", enable ? "start" : "stop"); return bt_shell_noninteractive_quit(EXIT_FAILURE); } return bt_shell_noninteractive_quit(EXIT_SUCCESS); } void gatt_notify_attribute(GDBusProxy *proxy, bool enable) { const char *iface; iface = g_dbus_proxy_get_interface(proxy); if (!strcmp(iface, "org.bluez.GattCharacteristic1")) { notify_attribute(proxy, enable); return; } bt_shell_printf("Unable to notify attribute %s\n", g_dbus_proxy_get_path(proxy)); return bt_shell_noninteractive_quit(EXIT_FAILURE); } static void register_app_setup(DBusMessageIter *iter, void *user_data) { DBusMessageIter opt; const char *path = "/"; dbus_message_iter_append_basic(iter, DBUS_TYPE_OBJECT_PATH, &path); dbus_message_iter_open_container(iter, DBUS_TYPE_ARRAY, DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING DBUS_TYPE_STRING_AS_STRING DBUS_TYPE_VARIANT_AS_STRING DBUS_DICT_ENTRY_END_CHAR_AS_STRING, &opt); dbus_message_iter_close_container(iter, &opt); } static void register_app_reply(DBusMessage *message, void *user_data) { DBusError error; dbus_error_init(&error); if (dbus_set_error_from_message(&error, message) == TRUE) { bt_shell_printf("Failed to register application: %s\n", error.name); dbus_error_free(&error); return bt_shell_noninteractive_quit(EXIT_FAILURE); } bt_shell_printf("Application registered\n"); return bt_shell_noninteractive_quit(EXIT_SUCCESS); } void gatt_add_manager(GDBusProxy *proxy) { managers = g_list_append(managers, proxy); } void gatt_remove_manager(GDBusProxy *proxy) { managers = g_list_remove(managers, proxy); } static int match_proxy(const void *a, const void *b) { GDBusProxy *proxy1 = (void *) a; GDBusProxy *proxy2 = (void *) b; return strcmp(g_dbus_proxy_get_path(proxy1), g_dbus_proxy_get_path(proxy2)); } static DBusMessage *release_profile(DBusConnection *conn, DBusMessage *msg, void *user_data) { g_dbus_unregister_interface(conn, APP_PATH, PROFILE_INTERFACE); return dbus_message_new_method_return(msg); } static const GDBusMethodTable methods[] = { { GDBUS_METHOD("Release", NULL, NULL, release_profile) }, { } }; static gboolean get_uuids(const GDBusPropertyTable *property, DBusMessageIter *iter, void *data) { DBusMessageIter entry; GList *uuid; dbus_message_iter_open_container(iter, DBUS_TYPE_ARRAY, DBUS_TYPE_STRING_AS_STRING, &entry); for (uuid = uuids; uuid; uuid = g_list_next(uuid)) dbus_message_iter_append_basic(&entry, DBUS_TYPE_STRING, &uuid->data); dbus_message_iter_close_container(iter, &entry); return TRUE; } static const GDBusPropertyTable properties[] = { { "UUIDs", "as", get_uuids }, { } }; void gatt_register_app(DBusConnection *conn, GDBusProxy *proxy, int argc, char *argv[]) { GList *l; int i; l = g_list_find_custom(managers, proxy, match_proxy); if (!l) { bt_shell_printf("Unable to find GattManager proxy\n"); return bt_shell_noninteractive_quit(EXIT_FAILURE); } for (i = 1; i < argc; i++) uuids = g_list_append(uuids, g_strdup(argv[i])); if (uuids) { if (g_dbus_register_interface(conn, APP_PATH, PROFILE_INTERFACE, methods, NULL, properties, NULL, NULL) == FALSE) { bt_shell_printf("Failed to register application" " object\n"); return bt_shell_noninteractive_quit(EXIT_FAILURE); } } if (g_dbus_proxy_method_call(l->data, "RegisterApplication", register_app_setup, register_app_reply, NULL, NULL) == FALSE) { bt_shell_printf("Failed register application\n"); g_dbus_unregister_interface(conn, APP_PATH, PROFILE_INTERFACE); return bt_shell_noninteractive_quit(EXIT_FAILURE); } } static void unregister_app_reply(DBusMessage *message, void *user_data) { DBusConnection *conn = user_data; DBusError error; dbus_error_init(&error); if (dbus_set_error_from_message(&error, message) == TRUE) { bt_shell_printf("Failed to unregister application: %s\n", error.name); dbus_error_free(&error); return bt_shell_noninteractive_quit(EXIT_FAILURE); } bt_shell_printf("Application unregistered\n"); if (!uuids) return bt_shell_noninteractive_quit(EXIT_SUCCESS); g_list_free_full(uuids, g_free); uuids = NULL; g_dbus_unregister_interface(conn, APP_PATH, PROFILE_INTERFACE); return bt_shell_noninteractive_quit(EXIT_SUCCESS); } static void unregister_app_setup(DBusMessageIter *iter, void *user_data) { const char *path = "/"; dbus_message_iter_append_basic(iter, DBUS_TYPE_OBJECT_PATH, &path); } void gatt_unregister_app(DBusConnection *conn, GDBusProxy *proxy) { GList *l; l = g_list_find_custom(managers, proxy, match_proxy); if (!l) { bt_shell_printf("Unable to find GattManager proxy\n"); return bt_shell_noninteractive_quit(EXIT_FAILURE); } if (g_dbus_proxy_method_call(l->data, "UnregisterApplication", unregister_app_setup, unregister_app_reply, conn, NULL) == FALSE) { bt_shell_printf("Failed unregister profile\n"); return bt_shell_noninteractive_quit(EXIT_FAILURE); } } static void desc_free(void *data) { struct desc *desc = data; g_free(desc->path); g_free(desc->uuid); g_strfreev(desc->flags); g_free(desc->value); g_free(desc); } static void desc_unregister(void *data) { struct desc *desc = data; print_desc(desc, COLORED_DEL); g_dbus_unregister_interface(desc->chrc->service->conn, desc->path, DESC_INTERFACE); } static void chrc_free(void *data) { struct chrc *chrc = data; g_list_free_full(chrc->descs, desc_unregister); g_free(chrc->path); g_free(chrc->uuid); g_strfreev(chrc->flags); g_free(chrc->value); g_free(chrc); } static void chrc_unregister(void *data) { struct chrc *chrc = data; print_chrc(chrc, COLORED_DEL); g_dbus_unregister_interface(chrc->service->conn, chrc->path, CHRC_INTERFACE); } static void inc_unregister(void *data) { char *path = data; g_free(path); } static void service_free(void *data) { struct service *service = data; g_list_free_full(service->chrcs, chrc_unregister); g_list_free_full(service->inc, inc_unregister); g_free(service->path); g_free(service->uuid); g_free(service); } static gboolean service_get_handle(const GDBusPropertyTable *property, DBusMessageIter *iter, void *data) { struct service *service = data; dbus_message_iter_append_basic(iter, DBUS_TYPE_UINT16, &service->handle); return TRUE; } static void service_set_handle(const GDBusPropertyTable *property, DBusMessageIter *value, GDBusPendingPropertySet id, void *data) { struct service *service = data; if (dbus_message_iter_get_arg_type(value) != DBUS_TYPE_UINT16) { g_dbus_pending_property_error(id, "org.bluez.InvalidArguments", "Invalid arguments in method call"); return; } dbus_message_iter_get_basic(value, &service->handle); print_service(service, COLORED_CHG); g_dbus_pending_property_success(id); } static gboolean service_get_uuid(const GDBusPropertyTable *property, DBusMessageIter *iter, void *data) { struct service *service = data; dbus_message_iter_append_basic(iter, DBUS_TYPE_STRING, &service->uuid); return TRUE; } static gboolean service_get_primary(const GDBusPropertyTable *property, DBusMessageIter *iter, void *data) { struct service *service = data; dbus_bool_t primary; primary = service->primary ? TRUE : FALSE; dbus_message_iter_append_basic(iter, DBUS_TYPE_BOOLEAN, &primary); return TRUE; } static gboolean service_get_includes(const GDBusPropertyTable *property, DBusMessageIter *iter, void *data) { DBusMessageIter array; struct service *service = data; char *inc = NULL; GList *l; if (service->inc) { for (l = service->inc ; l; l = g_list_next(l)) { inc = l->data; dbus_message_iter_open_container(iter, DBUS_TYPE_ARRAY, DBUS_TYPE_OBJECT_PATH_AS_STRING, &array); dbus_message_iter_append_basic(&array, DBUS_TYPE_OBJECT_PATH, &inc); } dbus_message_iter_close_container(iter, &array); return TRUE; } return FALSE; } static gboolean service_exist_includes(const GDBusPropertyTable *property, void *data) { struct service *service = data; if (service->inc) return TRUE; else return FALSE; } static const GDBusPropertyTable service_properties[] = { { "Handle", "q", service_get_handle, service_set_handle }, { "UUID", "s", service_get_uuid }, { "Primary", "b", service_get_primary }, { "Includes", "ao", service_get_includes, NULL, service_exist_includes }, { } }; static void service_set_primary(const char *input, void *user_data) { struct service *service = user_data; if (!strcmp(input, "yes")) service->primary = true; else if (!strcmp(input, "no")) { service->primary = false; } else { bt_shell_printf("Invalid option: %s\n", input); local_services = g_list_remove(local_services, service); print_service(service, COLORED_DEL); g_dbus_unregister_interface(service->conn, service->path, SERVICE_INTERFACE); } return bt_shell_noninteractive_quit(EXIT_SUCCESS); } static uint16_t parse_handle(const char *arg) { char *endptr = NULL; unsigned long handle; handle = strtoul(arg, &endptr, 0); if (!endptr || *endptr != '\0' || !handle || handle > UINT16_MAX) { bt_shell_printf("Invalid handle: %s", arg); return 0; } return handle; } void gatt_register_service(DBusConnection *conn, GDBusProxy *proxy, int argc, char *argv[]) { struct service *service; bool primary = true; service = g_new0(struct service, 1); service->conn = conn; service->uuid = g_strdup(argv[1]); service->path = g_strdup_printf("%s/service%u", APP_PATH, g_list_length(local_services)); service->primary = primary; if (argc > 2) { service->handle = parse_handle(argv[2]); if (!service->handle) { service_free(service); return bt_shell_noninteractive_quit(EXIT_FAILURE); } } if (g_dbus_register_interface(conn, service->path, SERVICE_INTERFACE, NULL, NULL, service_properties, service, service_free) == FALSE) { bt_shell_printf("Failed to register service object\n"); service_free(service); return bt_shell_noninteractive_quit(EXIT_FAILURE); } print_service(service, COLORED_NEW); local_services = g_list_append(local_services, service); bt_shell_prompt_input(service->path, "Primary (yes/no):", service_set_primary, service); } static struct service *service_find(const char *pattern) { GList *l; for (l = local_services; l; l = g_list_next(l)) { struct service *service = l->data; /* match object path */ if (!strcmp(service->path, pattern)) return service; /* match UUID */ if (!strcmp(service->uuid, pattern)) return service; } return NULL; } void gatt_unregister_service(DBusConnection *conn, GDBusProxy *proxy, int argc, char *argv[]) { struct service *service; service = service_find(argv[1]); if (!service) { bt_shell_printf("Failed to unregister service object\n"); return bt_shell_noninteractive_quit(EXIT_FAILURE); } local_services = g_list_remove(local_services, service); print_service(service, COLORED_DEL); g_dbus_unregister_interface(service->conn, service->path, SERVICE_INTERFACE); return bt_shell_noninteractive_quit(EXIT_SUCCESS); } static char *inc_find(struct service *serv, char *path) { GList *lc; for (lc = serv->inc; lc; lc = g_list_next(lc)) { char *incp = lc->data; /* match object path */ if (!strcmp(incp, path)) return incp; } return NULL; } void gatt_register_include(DBusConnection *conn, GDBusProxy *proxy, int argc, char *argv[]) { struct service *service, *inc_service; char *inc_path; if (!local_services) { bt_shell_printf("No service registered\n"); return bt_shell_noninteractive_quit(EXIT_FAILURE); } service = g_list_last(local_services)->data; inc_service = service_find(argv[1]); if (!inc_service) { bt_shell_printf("Failed to find service object\n"); return bt_shell_noninteractive_quit(EXIT_FAILURE); } inc_path = g_strdup(service->path); inc_service->inc = g_list_append(inc_service->inc, inc_path); print_service(inc_service, COLORED_NEW); print_inc_service(service, COLORED_NEW); return bt_shell_noninteractive_quit(EXIT_SUCCESS); } void gatt_unregister_include(DBusConnection *conn, GDBusProxy *proxy, int argc, char *argv[]) { struct service *ser_inc, *service; char *path = NULL; service = service_find(argv[1]); if (!service) { bt_shell_printf("Failed to unregister include service" " object\n"); return bt_shell_noninteractive_quit(EXIT_FAILURE); } ser_inc = service_find(argv[2]); if (!ser_inc) { bt_shell_printf("Failed to find include service object\n"); return bt_shell_noninteractive_quit(EXIT_FAILURE); } path = inc_find(service, ser_inc->path); if (path) { service->inc = g_list_remove(service->inc, path); inc_unregister(path); } return bt_shell_noninteractive_quit(EXIT_SUCCESS); } static gboolean chrc_get_handle(const GDBusPropertyTable *property, DBusMessageIter *iter, void *data) { struct chrc *chrc = data; dbus_message_iter_append_basic(iter, DBUS_TYPE_UINT16, &chrc->handle); return TRUE; } static void chrc_set_handle(const GDBusPropertyTable *property, DBusMessageIter *value, GDBusPendingPropertySet id, void *data) { struct chrc *chrc = data; if (dbus_message_iter_get_arg_type(value) != DBUS_TYPE_UINT16) { g_dbus_pending_property_error(id, "org.bluez.InvalidArguments", "Invalid arguments in method call"); return; } dbus_message_iter_get_basic(value, &chrc->handle); print_chrc(chrc, COLORED_CHG); g_dbus_pending_property_success(id); } static gboolean chrc_get_uuid(const GDBusPropertyTable *property, DBusMessageIter *iter, void *data) { struct chrc *chrc = data; dbus_message_iter_append_basic(iter, DBUS_TYPE_STRING, &chrc->uuid); return TRUE; } static gboolean chrc_get_service(const GDBusPropertyTable *property, DBusMessageIter *iter, void *data) { struct chrc *chrc = data; dbus_message_iter_append_basic(iter, DBUS_TYPE_OBJECT_PATH, &chrc->service->path); return TRUE; } static gboolean chrc_get_value(const GDBusPropertyTable *property, DBusMessageIter *iter, void *data) { struct chrc *chrc = data; DBusMessageIter array; dbus_message_iter_open_container(iter, DBUS_TYPE_ARRAY, "y", &array); dbus_message_iter_append_fixed_array(&array, DBUS_TYPE_BYTE, &chrc->value, chrc->value_len); dbus_message_iter_close_container(iter, &array); return TRUE; } static gboolean chrc_get_notifying(const GDBusPropertyTable *property, DBusMessageIter *iter, void *data) { struct chrc *chrc = data; dbus_bool_t value; value = chrc->notifying ? TRUE : FALSE; dbus_message_iter_append_basic(iter, DBUS_TYPE_BOOLEAN, &value); return TRUE; } static gboolean chrc_get_flags(const GDBusPropertyTable *property, DBusMessageIter *iter, void *data) { struct chrc *chrc = data; int i; DBusMessageIter array; dbus_message_iter_open_container(iter, DBUS_TYPE_ARRAY, "s", &array); for (i = 0; chrc->flags[i]; i++) dbus_message_iter_append_basic(&array, DBUS_TYPE_STRING, &chrc->flags[i]); dbus_message_iter_close_container(iter, &array); return TRUE; } static gboolean chrc_get_write_acquired(const GDBusPropertyTable *property, DBusMessageIter *iter, void *data) { struct chrc *chrc = data; dbus_bool_t value; value = chrc->write_io ? TRUE : FALSE; dbus_message_iter_append_basic(iter, DBUS_TYPE_BOOLEAN, &value); return TRUE; } static gboolean chrc_write_acquired_exists(const GDBusPropertyTable *property, void *data) { struct chrc *chrc = data; int i; if (chrc->proxy) return FALSE; for (i = 0; chrc->flags[i]; i++) { if (!strcmp("write-without-response", chrc->flags[i])) return TRUE; } return FALSE; } static gboolean chrc_get_notify_acquired(const GDBusPropertyTable *property, DBusMessageIter *iter, void *data) { struct chrc *chrc = data; dbus_bool_t value; value = chrc->notify_io ? TRUE : FALSE; dbus_message_iter_append_basic(iter, DBUS_TYPE_BOOLEAN, &value); return TRUE; } static gboolean chrc_notify_acquired_exists(const GDBusPropertyTable *property, void *data) { struct chrc *chrc = data; int i; if (chrc->proxy) return FALSE; for (i = 0; chrc->flags[i]; i++) { if (!strcmp("notify", chrc->flags[i])) return TRUE; } return FALSE; } static const GDBusPropertyTable chrc_properties[] = { { "Handle", "q", chrc_get_handle, chrc_set_handle, NULL }, { "UUID", "s", chrc_get_uuid, NULL, NULL }, { "Service", "o", chrc_get_service, NULL, NULL }, { "Value", "ay", chrc_get_value, NULL, NULL }, { "Notifying", "b", chrc_get_notifying, NULL, NULL }, { "Flags", "as", chrc_get_flags, NULL, NULL }, { "WriteAcquired", "b", chrc_get_write_acquired, NULL, chrc_write_acquired_exists }, { "NotifyAcquired", "b", chrc_get_notify_acquired, NULL, chrc_notify_acquired_exists }, { } }; static const char *path_to_address(const char *path) { GDBusProxy *proxy; DBusMessageIter iter; const char *address = path; proxy = bt_shell_get_env(path); if (g_dbus_proxy_get_property(proxy, "Address", &iter)) dbus_message_iter_get_basic(&iter, &address); return address; } static int parse_options(DBusMessageIter *iter, uint16_t *offset, uint16_t *mtu, char **device, char **link, bool *prep_authorize) { DBusMessageIter dict; if (dbus_message_iter_get_arg_type(iter) != DBUS_TYPE_ARRAY) return -EINVAL; dbus_message_iter_recurse(iter, &dict); while (dbus_message_iter_get_arg_type(&dict) == DBUS_TYPE_DICT_ENTRY) { const char *key; DBusMessageIter value, entry; int var; dbus_message_iter_recurse(&dict, &entry); dbus_message_iter_get_basic(&entry, &key); dbus_message_iter_next(&entry); dbus_message_iter_recurse(&entry, &value); var = dbus_message_iter_get_arg_type(&value); if (strcasecmp(key, "offset") == 0) { if (var != DBUS_TYPE_UINT16) return -EINVAL; if (offset) dbus_message_iter_get_basic(&value, offset); } else if (strcasecmp(key, "MTU") == 0) { if (var != DBUS_TYPE_UINT16) return -EINVAL; if (mtu) dbus_message_iter_get_basic(&value, mtu); } else if (strcasecmp(key, "device") == 0) { if (var != DBUS_TYPE_OBJECT_PATH) return -EINVAL; if (device) dbus_message_iter_get_basic(&value, device); } else if (strcasecmp(key, "link") == 0) { if (var != DBUS_TYPE_STRING) return -EINVAL; if (link) dbus_message_iter_get_basic(&value, link); } else if (strcasecmp(key, "prepare-authorize") == 0) { if (var != DBUS_TYPE_BOOLEAN) return -EINVAL; if (prep_authorize) { int tmp; dbus_message_iter_get_basic(&value, &tmp); *prep_authorize = !!tmp; } } dbus_message_iter_next(&dict); } return 0; } static DBusMessage *read_value(DBusMessage *msg, uint8_t *value, uint16_t value_len) { DBusMessage *reply; DBusMessageIter iter, array; reply = g_dbus_create_reply(msg, DBUS_TYPE_INVALID); dbus_message_iter_init_append(reply, &iter); dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY, "y", &array); dbus_message_iter_append_fixed_array(&array, DBUS_TYPE_BYTE, &value, value_len); dbus_message_iter_close_container(&iter, &array); return reply; } struct authorize_attribute_data { DBusConnection *conn; void *attribute; uint16_t offset; }; static void authorize_read_response(const char *input, void *user_data) { struct authorize_attribute_data *aad = user_data; struct chrc *chrc = aad->attribute; DBusMessage *reply; char *err; if (!strcmp(input, "no")) { err = "org.bluez.Error.NotAuthorized"; goto error; } if (aad->offset > chrc->value_len) { err = "org.bluez.Error.InvalidOffset"; goto error; } reply = read_value(pending_message, &chrc->value[aad->offset], chrc->value_len - aad->offset); g_dbus_send_message(aad->conn, reply); g_free(aad); return; error: g_dbus_send_error(aad->conn, pending_message, err, NULL); g_free(aad); } static bool is_device_trusted(const char *path) { GDBusProxy *proxy; DBusMessageIter iter; bool trusted = false; proxy = bt_shell_get_env(path); if (g_dbus_proxy_get_property(proxy, "Trusted", &iter)) dbus_message_iter_get_basic(&iter, &trusted); return trusted; } struct read_attribute_data { DBusMessage *msg; uint16_t offset; }; static void proxy_read_reply(DBusMessage *message, void *user_data) { struct read_attribute_data *data = user_data; DBusConnection *conn = bt_shell_get_env("DBUS_CONNECTION"); DBusError error; DBusMessageIter iter, array; DBusMessage *reply; uint8_t *value; int len; dbus_error_init(&error); if (dbus_set_error_from_message(&error, message) == TRUE) { bt_shell_printf("Failed to read: %s\n", error.name); dbus_error_free(&error); g_dbus_send_error(conn, data->msg, error.name, "%s", error.message); goto done; } dbus_message_iter_init(message, &iter); if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_ARRAY) { bt_shell_printf("Invalid response to read\n"); g_dbus_send_error(conn, data->msg, "org.bluez.Error.InvalidArguments", NULL); goto done; } dbus_message_iter_recurse(&iter, &array); dbus_message_iter_get_fixed_array(&array, &value, &len); if (len < 0) { bt_shell_printf("Unable to parse value\n"); g_dbus_send_error(conn, data->msg, "org.bluez.Error.InvalidArguments", NULL); } reply = read_value(data->msg, value, len); g_dbus_send_message(conn, reply); done: dbus_message_unref(data->msg); free(data); } static void proxy_read_setup(DBusMessageIter *iter, void *user_data) { DBusMessageIter dict; struct read_attribute_data *data = user_data; dbus_message_iter_open_container(iter, DBUS_TYPE_ARRAY, DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING DBUS_TYPE_STRING_AS_STRING DBUS_TYPE_VARIANT_AS_STRING DBUS_DICT_ENTRY_END_CHAR_AS_STRING, &dict); g_dbus_dict_append_entry(&dict, "offset", DBUS_TYPE_UINT16, &data->offset); dbus_message_iter_close_container(iter, &dict); } static DBusMessage *proxy_read_value(struct GDBusProxy *proxy, DBusMessage *msg, uint16_t offset) { struct read_attribute_data *data; data = new0(struct read_attribute_data, 1); data->msg = dbus_message_ref(msg); data->offset = offset; if (g_dbus_proxy_method_call(proxy, "ReadValue", proxy_read_setup, proxy_read_reply, data, NULL)) return NULL; bt_shell_printf("Failed to read\n"); return g_dbus_create_error(msg, "org.bluez.Error.InvalidArguments", NULL); } static DBusMessage *chrc_read_value(DBusConnection *conn, DBusMessage *msg, void *user_data) { struct chrc *chrc = user_data; DBusMessageIter iter; uint16_t offset = 0; char *device, *link; char *str; dbus_message_iter_init(msg, &iter); if (parse_options(&iter, &offset, NULL, &device, &link, NULL)) return g_dbus_create_error(msg, "org.bluez.Error.InvalidArguments", NULL); bt_shell_printf("[%s (%s)] ReadValue: %s offset %u link %s\n", chrc->path, bt_uuidstr_to_str(chrc->uuid), path_to_address(device), offset, link); if (chrc->proxy) { return proxy_read_value(chrc->proxy, msg, offset); } if (!is_device_trusted(device) && chrc->authorization_req) { struct authorize_attribute_data *aad; aad = g_new0(struct authorize_attribute_data, 1); aad->conn = conn; aad->attribute = chrc; aad->offset = offset; str = g_strdup_printf("Authorize attribute(%s) read (yes/no):", chrc->path); bt_shell_prompt_input("gatt", str, authorize_read_response, aad); g_free(str); pending_message = dbus_message_ref(msg); return NULL; } if (offset > chrc->value_len) return g_dbus_create_error(msg, "org.bluez.Error.InvalidOffset", NULL); return read_value(msg, &chrc->value[offset], chrc->value_len - offset); } static int parse_value_arg(DBusMessageIter *iter, uint8_t **value, int *len) { DBusMessageIter array; if (dbus_message_iter_get_arg_type(iter) != DBUS_TYPE_ARRAY) return -EINVAL; dbus_message_iter_recurse(iter, &array); dbus_message_iter_get_fixed_array(&array, value, len); return 0; } static void authorize_write_response(const char *input, void *user_data) { struct authorize_attribute_data *aad = user_data; struct chrc *chrc = aad->attribute; bool prep_authorize = false; DBusMessageIter iter; DBusMessage *reply; int value_len; uint8_t *value; char *err; dbus_message_iter_init(pending_message, &iter); if (parse_value_arg(&iter, &value, &value_len)) { err = "org.bluez.Error.InvalidArguments"; goto error; } dbus_message_iter_next(&iter); if (parse_options(&iter, NULL, NULL, NULL, NULL, &prep_authorize)) { err = "org.bluez.Error.InvalidArguments"; goto error; } if (!strcmp(input, "no")) { err = "org.bluez.Error.NotAuthorized"; goto error; } if (aad->offset > chrc->value_len) { err = "org.bluez.Error.InvalidOffset"; goto error; } /* Authorization check of prepare writes */ if (prep_authorize) { reply = g_dbus_create_reply(pending_message, DBUS_TYPE_INVALID); g_dbus_send_message(aad->conn, reply); g_free(aad); return; } if (write_value(&chrc->value_len, &chrc->value, value, value_len, aad->offset, chrc->max_val_len)) { err = "org.bluez.Error.InvalidValueLength"; goto error; } bt_shell_printf("[" COLORED_CHG "] Attribute %s (%s) written", chrc->path, bt_uuidstr_to_str(chrc->uuid)); g_dbus_emit_property_changed(aad->conn, chrc->path, CHRC_INTERFACE, "Value"); reply = g_dbus_create_reply(pending_message, DBUS_TYPE_INVALID); g_dbus_send_message(aad->conn, reply); g_free(aad); return; error: g_dbus_send_error(aad->conn, pending_message, err, NULL); g_free(aad); } static void proxy_write_reply(DBusMessage *message, void *user_data) { struct write_attribute_data *data = user_data; DBusConnection *conn = bt_shell_get_env("DBUS_CONNECTION"); DBusError error; dbus_error_init(&error); if (dbus_set_error_from_message(&error, message)) { bt_shell_printf("Failed to write: %s\n", error.name); g_dbus_send_error(conn, data->msg, error.name, "%s", error.message); } else g_dbus_send_reply(conn, data->msg, DBUS_TYPE_INVALID); dbus_message_unref(data->msg); free(data); } static DBusMessage *proxy_write_value(struct GDBusProxy *proxy, DBusMessage *msg, uint8_t *value, int value_len, uint16_t offset) { struct write_attribute_data *data; data = new0(struct write_attribute_data, 1); data->msg = dbus_message_ref(msg); data->iov.iov_base = (void *) value; data->iov.iov_len = value_len; data->offset = offset; if (g_dbus_proxy_method_call(proxy, "WriteValue", write_setup, proxy_write_reply, data, NULL)) return NULL; bt_shell_printf("Failed to write\n"); return g_dbus_create_error(msg, "org.bluez.Error.InvalidArguments", NULL); } static DBusMessage *chrc_write_value(DBusConnection *conn, DBusMessage *msg, void *user_data) { struct chrc *chrc = user_data; uint16_t offset = 0; bool prep_authorize = false; char *device = NULL, *link = NULL; DBusMessageIter iter; int value_len; uint8_t *value; char *str; dbus_message_iter_init(msg, &iter); if (parse_value_arg(&iter, &value, &value_len)) return g_dbus_create_error(msg, "org.bluez.Error.InvalidArguments", NULL); dbus_message_iter_next(&iter); if (parse_options(&iter, &offset, NULL, &device, &link, &prep_authorize)) return g_dbus_create_error(msg, "org.bluez.Error.InvalidArguments", NULL); bt_shell_printf("[%s (%s)] WriteValue: %s offset %u link %s\n", chrc->path, bt_uuidstr_to_str(chrc->uuid), path_to_address(device), offset, link); bt_shell_hexdump(value, value_len); if (chrc->proxy) return proxy_write_value(chrc->proxy, msg, value, value_len, offset); if (!is_device_trusted(device) && chrc->authorization_req) { struct authorize_attribute_data *aad; aad = g_new0(struct authorize_attribute_data, 1); aad->conn = conn; aad->attribute = chrc; aad->offset = offset; str = g_strdup_printf("Authorize attribute(%s) write (yes/no):", chrc->path); bt_shell_prompt_input("gatt", str, authorize_write_response, aad); g_free(str); pending_message = dbus_message_ref(msg); return NULL; } if (offset > chrc->value_len) return g_dbus_create_error(msg, "org.bluez.Error.InvalidOffset", NULL); /* Authorization check of prepare writes */ if (prep_authorize) return g_dbus_create_reply(msg, DBUS_TYPE_INVALID); if (write_value(&chrc->value_len, &chrc->value, value, value_len, offset, chrc->max_val_len)) return g_dbus_create_error(msg, "org.bluez.Error.InvalidValueLength", NULL); bt_shell_printf("[" COLORED_CHG "] Attribute %s (%s) written", chrc->path, bt_uuidstr_to_str(chrc->uuid)); g_dbus_emit_property_changed(conn, chrc->path, CHRC_INTERFACE, "Value"); return g_dbus_create_reply(msg, DBUS_TYPE_INVALID); } static DBusMessage *create_sock(struct chrc *chrc, DBusMessage *msg) { int fds[2]; struct io *io; bool dir; DBusMessage *reply; if (socketpair(AF_LOCAL, SOCK_SEQPACKET | SOCK_NONBLOCK | SOCK_CLOEXEC, 0, fds) < 0) return g_dbus_create_error(msg, "org.bluez.Error.Failed", "%s", strerror(errno)); dir = dbus_message_has_member(msg, "AcquireWrite"); io = sock_io_new(fds[!dir], chrc); if (!io) { close(fds[0]); close(fds[1]); return g_dbus_create_error(msg, "org.bluez.Error.Failed", "%s", strerror(errno)); } reply = g_dbus_create_reply(msg, DBUS_TYPE_UNIX_FD, &fds[dir], DBUS_TYPE_UINT16, &chrc->mtu, DBUS_TYPE_INVALID); close(fds[dir]); if (dir) chrc->write_io = io; else chrc->notify_io = io; bt_shell_printf("[" COLORED_CHG "] Attribute %s %s sock acquired\n", chrc->path, dir ? "Write" : "Notify"); return reply; } static DBusMessage *chrc_acquire_write(DBusConnection *conn, DBusMessage *msg, void *user_data) { struct chrc *chrc = user_data; DBusMessageIter iter; DBusMessage *reply; char *device = NULL, *link= NULL; dbus_message_iter_init(msg, &iter); if (chrc->write_io) return g_dbus_create_error(msg, "org.bluez.Error.NotPermitted", NULL); if (parse_options(&iter, NULL, &chrc->mtu, &device, &link, NULL)) return g_dbus_create_error(msg, "org.bluez.Error.InvalidArguments", NULL); bt_shell_printf("AcquireWrite: %s link %s\n", path_to_address(device), link); reply = create_sock(chrc, msg); if (chrc->write_io) g_dbus_emit_property_changed(conn, chrc->path, CHRC_INTERFACE, "WriteAcquired"); return reply; } static DBusMessage *chrc_acquire_notify(DBusConnection *conn, DBusMessage *msg, void *user_data) { struct chrc *chrc = user_data; DBusMessageIter iter; DBusMessage *reply; char *device = NULL, *link = NULL; dbus_message_iter_init(msg, &iter); if (chrc->notify_io) return g_dbus_create_error(msg, "org.bluez.Error.NotPermitted", NULL); if (parse_options(&iter, NULL, &chrc->mtu, &device, &link, NULL)) return g_dbus_create_error(msg, "org.bluez.Error.InvalidArguments", NULL); bt_shell_printf("AcquireNotify: %s link %s\n", path_to_address(device), link); reply = create_sock(chrc, msg); if (chrc->notify_io) g_dbus_emit_property_changed(conn, chrc->path, CHRC_INTERFACE, "NotifyAcquired"); return reply; } struct notify_attribute_data { struct chrc *chrc; DBusMessage *msg; bool enable; }; static void proxy_notify_reply(DBusMessage *message, void *user_data) { struct notify_attribute_data *data = user_data; DBusConnection *conn = bt_shell_get_env("DBUS_CONNECTION"); DBusError error; dbus_error_init(&error); if (dbus_set_error_from_message(&error, message) == TRUE) { bt_shell_printf("Failed to %s: %s\n", data->enable ? "StartNotify" : "StopNotify", error.name); dbus_error_free(&error); g_dbus_send_error(conn, data->msg, error.name, "%s", error.message); goto done; } g_dbus_send_reply(conn, data->msg, DBUS_TYPE_INVALID); data->chrc->notifying = data->enable; bt_shell_printf("[" COLORED_CHG "] Attribute %s (%s) " "notifications %s\n", data->chrc->path, bt_uuidstr_to_str(data->chrc->uuid), data->enable ? "enabled" : "disabled"); g_dbus_emit_property_changed(conn, data->chrc->path, CHRC_INTERFACE, "Notifying"); done: dbus_message_unref(data->msg); free(data); } static DBusMessage *proxy_notify(struct chrc *chrc, DBusMessage *msg, bool enable) { struct notify_attribute_data *data; const char *method; if (enable == TRUE) method = "StartNotify"; else method = "StopNotify"; data = new0(struct notify_attribute_data, 1); data->chrc = chrc; data->msg = dbus_message_ref(msg); data->enable = enable; if (g_dbus_proxy_method_call(chrc->proxy, method, NULL, proxy_notify_reply, data, NULL)) return NULL; return g_dbus_create_error(msg, "org.bluez.Error.InvalidArguments", NULL); } static DBusMessage *chrc_start_notify(DBusConnection *conn, DBusMessage *msg, void *user_data) { struct chrc *chrc = user_data; if (chrc->notifying) return g_dbus_create_reply(msg, DBUS_TYPE_INVALID); if (chrc->proxy) return proxy_notify(chrc, msg, true); chrc->notifying = true; bt_shell_printf("[" COLORED_CHG "] Attribute %s (%s) notifications " "enabled\n", chrc->path, bt_uuidstr_to_str(chrc->uuid)); g_dbus_emit_property_changed(conn, chrc->path, CHRC_INTERFACE, "Notifying"); return g_dbus_create_reply(msg, DBUS_TYPE_INVALID); } static DBusMessage *chrc_stop_notify(DBusConnection *conn, DBusMessage *msg, void *user_data) { struct chrc *chrc = user_data; if (!chrc->notifying) return g_dbus_create_reply(msg, DBUS_TYPE_INVALID); if (chrc->proxy) return proxy_notify(chrc, msg, false); chrc->notifying = false; bt_shell_printf("[" COLORED_CHG "] Attribute %s (%s) notifications " "disabled\n", chrc->path, bt_uuidstr_to_str(chrc->uuid)); g_dbus_emit_property_changed(conn, chrc->path, CHRC_INTERFACE, "Notifying"); return g_dbus_create_reply(msg, DBUS_TYPE_INVALID); } static DBusMessage *chrc_confirm(DBusConnection *conn, DBusMessage *msg, void *user_data) { struct chrc *chrc = user_data; bt_shell_printf("Attribute %s (%s) indication confirm received", chrc->path, bt_uuidstr_to_str(chrc->uuid)); return dbus_message_new_method_return(msg); } static const GDBusMethodTable chrc_methods[] = { { GDBUS_ASYNC_METHOD("ReadValue", GDBUS_ARGS({ "options", "a{sv}" }), GDBUS_ARGS({ "value", "ay" }), chrc_read_value) }, { GDBUS_ASYNC_METHOD("WriteValue", GDBUS_ARGS({ "value", "ay" }, { "options", "a{sv}" }), NULL, chrc_write_value) }, { GDBUS_METHOD("AcquireWrite", GDBUS_ARGS({ "options", "a{sv}" }), NULL, chrc_acquire_write) }, { GDBUS_METHOD("AcquireNotify", GDBUS_ARGS({ "options", "a{sv}" }), NULL, chrc_acquire_notify) }, { GDBUS_ASYNC_METHOD("StartNotify", NULL, NULL, chrc_start_notify) }, { GDBUS_METHOD("StopNotify", NULL, NULL, chrc_stop_notify) }, { GDBUS_METHOD("Confirm", NULL, NULL, chrc_confirm) }, { } }; static void chrc_set_value(const char *input, void *user_data) { struct chrc *chrc = user_data; g_free(chrc->value); chrc->value = str2bytearray((char *) input, &chrc->value_len); if (!chrc->value) { print_chrc(chrc, COLORED_DEL); chrc_unregister(chrc); } chrc->max_val_len = chrc->value_len; return bt_shell_noninteractive_quit(EXIT_SUCCESS); } static gboolean attr_authorization_flag_exists(char **flags) { int i; for (i = 0; flags[i]; i++) { if (!strcmp("authorize", flags[i])) return TRUE; } return FALSE; } void gatt_register_chrc(DBusConnection *conn, GDBusProxy *proxy, int argc, char *argv[]) { struct service *service; struct chrc *chrc; if (!local_services) { bt_shell_printf("No service registered\n"); return bt_shell_noninteractive_quit(EXIT_FAILURE); } service = g_list_last(local_services)->data; chrc = g_new0(struct chrc, 1); chrc->service = service; chrc->uuid = g_strdup(argv[1]); chrc->path = g_strdup_printf("%s/chrc%u", service->path, g_list_length(service->chrcs)); chrc->flags = g_strsplit(argv[2], ",", -1); chrc->authorization_req = attr_authorization_flag_exists(chrc->flags); if (argc > 3) { chrc->handle = parse_handle(argv[3]); if (!chrc->handle) { chrc_free(chrc); return bt_shell_noninteractive_quit(EXIT_FAILURE); } } if (g_dbus_register_interface(conn, chrc->path, CHRC_INTERFACE, chrc_methods, NULL, chrc_properties, chrc, chrc_free) == FALSE) { bt_shell_printf("Failed to register characteristic object\n"); chrc_free(chrc); return bt_shell_noninteractive_quit(EXIT_FAILURE); } service->chrcs = g_list_append(service->chrcs, chrc); print_chrc(chrc, COLORED_NEW); bt_shell_prompt_input(chrc->path, "Enter value:", chrc_set_value, chrc); } static struct chrc *chrc_find(const char *pattern) { GList *l, *lc; struct service *service; struct chrc *chrc; for (l = local_services; l; l = g_list_next(l)) { service = l->data; for (lc = service->chrcs; lc; lc = g_list_next(lc)) { chrc = lc->data; /* match object path */ if (!strcmp(chrc->path, pattern)) return chrc; /* match UUID */ if (!strcmp(chrc->uuid, pattern)) return chrc; } } return NULL; } void gatt_unregister_chrc(DBusConnection *conn, GDBusProxy *proxy, int argc, char *argv[]) { struct chrc *chrc; chrc = chrc_find(argv[1]); if (!chrc) { bt_shell_printf("Failed to unregister characteristic object\n"); return bt_shell_noninteractive_quit(EXIT_FAILURE); } chrc->service->chrcs = g_list_remove(chrc->service->chrcs, chrc); chrc_unregister(chrc); return bt_shell_noninteractive_quit(EXIT_SUCCESS); } static DBusMessage *desc_read_value(DBusConnection *conn, DBusMessage *msg, void *user_data) { struct desc *desc = user_data; DBusMessageIter iter; uint16_t offset = 0; char *device = NULL, *link = NULL; dbus_message_iter_init(msg, &iter); if (parse_options(&iter, &offset, NULL, &device, &link, NULL)) return g_dbus_create_error(msg, "org.bluez.Error.InvalidArguments", NULL); bt_shell_printf("[%s (%s)] ReadValue: %s offset %u link %s\n", desc->path, bt_uuidstr_to_str(desc->uuid), path_to_address(device), offset, link); if (offset > desc->value_len) return g_dbus_create_error(msg, "org.bluez.Error.InvalidOffset", NULL); return read_value(msg, &desc->value[offset], desc->value_len - offset); } static DBusMessage *desc_write_value(DBusConnection *conn, DBusMessage *msg, void *user_data) { struct desc *desc = user_data; DBusMessageIter iter; uint16_t offset = 0; char *device = NULL, *link = NULL; int value_len; uint8_t *value; dbus_message_iter_init(msg, &iter); if (parse_value_arg(&iter, &value, &value_len)) return g_dbus_create_error(msg, "org.bluez.Error.InvalidArguments", NULL); dbus_message_iter_next(&iter); if (parse_options(&iter, &offset, NULL, &device, &link, NULL)) return g_dbus_create_error(msg, "org.bluez.Error.InvalidArguments", NULL); if (offset > desc->value_len) return g_dbus_create_error(msg, "org.bluez.Error.InvalidOffset", NULL); if (write_value(&desc->value_len, &desc->value, value, value_len, offset, desc->max_val_len)) return g_dbus_create_error(msg, "org.bluez.Error.InvalidValueLength", NULL); bt_shell_printf("[%s (%s)] WriteValue: %s offset %u link %s\n", desc->path, bt_uuidstr_to_str(desc->uuid), path_to_address(device), offset, link); bt_shell_printf("[" COLORED_CHG "] Attribute %s (%s) written", desc->path, bt_uuidstr_to_str(desc->uuid)); g_dbus_emit_property_changed(conn, desc->path, DESC_INTERFACE, "Value"); return g_dbus_create_reply(msg, DBUS_TYPE_INVALID); } static const GDBusMethodTable desc_methods[] = { { GDBUS_ASYNC_METHOD("ReadValue", GDBUS_ARGS({ "options", "a{sv}" }), GDBUS_ARGS({ "value", "ay" }), desc_read_value) }, { GDBUS_ASYNC_METHOD("WriteValue", GDBUS_ARGS({ "value", "ay" }, { "options", "a{sv}" }), NULL, desc_write_value) }, { } }; static gboolean desc_get_handle(const GDBusPropertyTable *property, DBusMessageIter *iter, void *data) { struct desc *desc = data; dbus_message_iter_append_basic(iter, DBUS_TYPE_UINT16, &desc->handle); return TRUE; } static void desc_set_handle(const GDBusPropertyTable *property, DBusMessageIter *value, GDBusPendingPropertySet id, void *data) { struct desc *desc = data; if (dbus_message_iter_get_arg_type(value) != DBUS_TYPE_UINT16) { g_dbus_pending_property_error(id, "org.bluez.InvalidArguments", "Invalid arguments in method call"); return; } dbus_message_iter_get_basic(value, &desc->handle); print_desc(desc, COLORED_CHG); g_dbus_pending_property_success(id); } static gboolean desc_get_uuid(const GDBusPropertyTable *property, DBusMessageIter *iter, void *data) { struct desc *desc = data; dbus_message_iter_append_basic(iter, DBUS_TYPE_STRING, &desc->uuid); return TRUE; } static gboolean desc_get_chrc(const GDBusPropertyTable *property, DBusMessageIter *iter, void *data) { struct desc *desc = data; dbus_message_iter_append_basic(iter, DBUS_TYPE_OBJECT_PATH, &desc->chrc->path); return TRUE; } static gboolean desc_get_value(const GDBusPropertyTable *property, DBusMessageIter *iter, void *data) { struct desc *desc = data; DBusMessageIter array; dbus_message_iter_open_container(iter, DBUS_TYPE_ARRAY, "y", &array); if (desc->value) dbus_message_iter_append_fixed_array(&array, DBUS_TYPE_BYTE, &desc->value, desc->value_len); dbus_message_iter_close_container(iter, &array); return TRUE; } static gboolean desc_get_flags(const GDBusPropertyTable *property, DBusMessageIter *iter, void *data) { struct desc *desc = data; int i; DBusMessageIter array; dbus_message_iter_open_container(iter, DBUS_TYPE_ARRAY, "s", &array); for (i = 0; desc->flags[i]; i++) dbus_message_iter_append_basic(&array, DBUS_TYPE_STRING, &desc->flags[i]); dbus_message_iter_close_container(iter, &array); return TRUE; } static const GDBusPropertyTable desc_properties[] = { { "Handle", "q", desc_get_handle, desc_set_handle, NULL }, { "UUID", "s", desc_get_uuid, NULL, NULL }, { "Characteristic", "o", desc_get_chrc, NULL, NULL }, { "Value", "ay", desc_get_value, NULL, NULL }, { "Flags", "as", desc_get_flags, NULL, NULL }, { } }; static void desc_set_value(const char *input, void *user_data) { struct desc *desc = user_data; g_free(desc->value); desc->value = str2bytearray((char *) input, &desc->value_len); if (!desc->value) { print_desc(desc, COLORED_DEL); desc_unregister(desc); } desc->max_val_len = desc->value_len; } void gatt_register_desc(DBusConnection *conn, GDBusProxy *proxy, int argc, char *argv[]) { struct service *service; struct desc *desc; if (!local_services) { bt_shell_printf("No service registered\n"); return bt_shell_noninteractive_quit(EXIT_FAILURE); } service = g_list_last(local_services)->data; if (!service->chrcs) { bt_shell_printf("No characteristic registered\n"); return bt_shell_noninteractive_quit(EXIT_FAILURE); } desc = g_new0(struct desc, 1); desc->chrc = g_list_last(service->chrcs)->data; desc->uuid = g_strdup(argv[1]); desc->path = g_strdup_printf("%s/desc%u", desc->chrc->path, g_list_length(desc->chrc->descs)); desc->flags = g_strsplit(argv[2], ",", -1); if (argc > 3) { desc->handle = parse_handle(argv[3]); if (!desc->handle) { desc_free(desc); return bt_shell_noninteractive_quit(EXIT_FAILURE); } } if (g_dbus_register_interface(conn, desc->path, DESC_INTERFACE, desc_methods, NULL, desc_properties, desc, desc_free) == FALSE) { bt_shell_printf("Failed to register descriptor object\n"); desc_free(desc); return bt_shell_noninteractive_quit(EXIT_FAILURE); } desc->chrc->descs = g_list_append(desc->chrc->descs, desc); print_desc(desc, COLORED_NEW); bt_shell_prompt_input(desc->path, "Enter value:", desc_set_value, desc); return bt_shell_noninteractive_quit(EXIT_SUCCESS); } static struct desc *desc_find(const char *pattern) { GList *l, *lc, *ld; struct service *service; struct chrc *chrc; struct desc *desc; for (l = local_services; l; l = g_list_next(l)) { service = l->data; for (lc = service->chrcs; lc; lc = g_list_next(lc)) { chrc = lc->data; for (ld = chrc->descs; ld; ld = g_list_next(ld)) { desc = ld->data; /* match object path */ if (!strcmp(desc->path, pattern)) return desc; /* match UUID */ if (!strcmp(desc->uuid, pattern)) return desc; } } } return NULL; } void gatt_unregister_desc(DBusConnection *conn, GDBusProxy *proxy, int argc, char *argv[]) { struct desc *desc; desc = desc_find(argv[1]); if (!desc) { bt_shell_printf("Failed to unregister descriptor object\n"); return bt_shell_noninteractive_quit(EXIT_FAILURE); } desc->chrc->descs = g_list_remove(desc->chrc->descs, desc); desc_unregister(desc); return bt_shell_noninteractive_quit(EXIT_SUCCESS); } static GDBusProxy *select_service(GDBusProxy *proxy) { GList *l; for (l = services; l; l = g_list_next(l)) { GDBusProxy *p = l->data; if (proxy == p || g_str_has_prefix(g_dbus_proxy_get_path(proxy), g_dbus_proxy_get_path(p))) return p; } return NULL; } static void proxy_property_changed(GDBusProxy *proxy, const char *name, DBusMessageIter *iter, void *user_data) { DBusConnection *conn = bt_shell_get_env("DBUS_CONNECTION"); struct chrc *chrc = user_data; bt_shell_printf("[" COLORED_CHG "] Attribute %s (%s) %s:\n", chrc->path, bt_uuidstr_to_str(chrc->uuid), name); if (!strcmp(name, "Value")) { uint8_t *value = NULL; int len = 0; if (iter && dbus_message_iter_get_arg_type(iter) == DBUS_TYPE_ARRAY) { DBusMessageIter array; dbus_message_iter_recurse(iter, &array); dbus_message_iter_get_fixed_array(&array, &value, &len); } if (write_value(&chrc->value_len, &chrc->value, value, len, 0, chrc->max_val_len)) { bt_shell_printf("Unable to update property value for " "%s\n", name); } else { bt_shell_hexdump(value, len); } } g_dbus_emit_property_changed(conn, chrc->path, CHRC_INTERFACE, name); } static void clone_chrc(struct GDBusProxy *proxy) { struct service *service; struct chrc *chrc; DBusMessageIter iter; DBusMessageIter array; const char *uuid; char *flags[17]; int i; if (g_dbus_proxy_get_property(proxy, "UUID", &iter) == FALSE) return; dbus_message_iter_get_basic(&iter, &uuid); if (g_dbus_proxy_get_property(proxy, "Flags", &iter) == FALSE) return; dbus_message_iter_recurse(&iter, &array); for (i = 0; dbus_message_iter_get_arg_type(&array) == DBUS_TYPE_STRING; i++) { dbus_message_iter_get_basic(&array, &flags[i]); dbus_message_iter_next(&array); } flags[i] = NULL; service = g_list_last(local_services)->data; chrc = g_new0(struct chrc, 1); chrc->service = service; chrc->proxy = proxy; chrc->uuid = g_strdup(uuid); chrc->path = g_strdup_printf("%s/chrc%u", service->path, g_list_length(service->chrcs)); chrc->flags = g_strdupv(flags); if (g_dbus_register_interface(service->conn, chrc->path, CHRC_INTERFACE, chrc_methods, NULL, chrc_properties, chrc, chrc_free) == FALSE) { bt_shell_printf("Failed to register characteristic object\n"); chrc_free(chrc); return bt_shell_noninteractive_quit(EXIT_FAILURE); } g_dbus_proxy_set_property_watch(proxy, proxy_property_changed, chrc); service->chrcs = g_list_append(service->chrcs, chrc); print_chrc(chrc, COLORED_NEW); } static void clone_chrcs(struct GDBusProxy *proxy) { GList *l; for (l = characteristics; l; l = g_list_next(l)) { GDBusProxy *p = l->data; if (g_str_has_prefix(g_dbus_proxy_get_path(p), g_dbus_proxy_get_path(proxy))) clone_chrc(p); } } static void clone_service(struct GDBusProxy *proxy) { struct service *service; DBusMessageIter iter; const char *uuid; dbus_bool_t primary; if (g_dbus_proxy_get_property(proxy, "UUID", &iter) == FALSE) return; dbus_message_iter_get_basic(&iter, &uuid); if (g_dbus_proxy_get_property(proxy, "Primary", &iter) == FALSE) return; dbus_message_iter_get_basic(&iter, &primary); if (!strcmp(uuid, "00001800-0000-1000-8000-00805f9b34fb") || !strcmp(uuid, "00001801-0000-1000-8000-00805f9b34fb")) return; service = g_new0(struct service, 1); service->conn = bt_shell_get_env("DBUS_CONNECTION"); service->proxy = proxy; service->path = g_strdup_printf("%s/service%u", APP_PATH, g_list_length(local_services)); service->uuid = g_strdup(uuid); service->primary = primary; if (g_dbus_register_interface(service->conn, service->path, SERVICE_INTERFACE, NULL, NULL, service_properties, service, service_free) == FALSE) { bt_shell_printf("Failed to register service object\n"); service_free(service); return bt_shell_noninteractive_quit(EXIT_FAILURE); } print_service(service, COLORED_NEW); local_services = g_list_append(local_services, service); clone_chrcs(proxy); } static void clone_device(struct GDBusProxy *proxy) { GList *l; for (l = services; l; l = g_list_next(l)) { struct GDBusProxy *p = l->data; if (g_str_has_prefix(g_dbus_proxy_get_path(p), g_dbus_proxy_get_path(proxy))) clone_service(p); } } static void service_clone(const char *input, void *user_data) { struct GDBusProxy *proxy = user_data; if (!strcmp(input, "yes")) return clone_service(proxy); else if (!strcmp(input, "no")) return bt_shell_noninteractive_quit(EXIT_FAILURE); else if (!strcmp(input, "all")) return clone_device(proxy); bt_shell_printf("Invalid option: %s\n", input); return bt_shell_noninteractive_quit(EXIT_FAILURE); } static void device_clone(const char *input, void *user_data) { struct GDBusProxy *proxy = user_data; if (!strcmp(input, "yes")) return clone_device(proxy); else if (!strcmp(input, "no")) return bt_shell_noninteractive_quit(EXIT_FAILURE); bt_shell_printf("Invalid option: %s\n", input); return bt_shell_noninteractive_quit(EXIT_FAILURE); } static const char *proxy_get_name(struct GDBusProxy *proxy) { DBusMessageIter iter; const char *uuid; const char *str; if (g_dbus_proxy_get_property(proxy, "UUID", &iter) == FALSE) return NULL; dbus_message_iter_get_basic(&iter, &uuid); str = bt_uuidstr_to_str(uuid); return str ? str : uuid; } static const char *proxy_get_alias(struct GDBusProxy *proxy) { DBusMessageIter iter; const char *alias; if (g_dbus_proxy_get_property(proxy, "Alias", &iter) == FALSE) return NULL; dbus_message_iter_get_basic(&iter, &alias); return alias; } void gatt_clone_attribute(GDBusProxy *proxy, int argc, char *argv[]) { GDBusProxy *service = NULL; if (argc > 1) { proxy = gatt_select_attribute(proxy, argv[1]); if (!proxy) { bt_shell_printf("Unable to find attribute %s\n", argv[1]); return bt_shell_noninteractive_quit(EXIT_FAILURE); } } if (!strcmp(g_dbus_proxy_get_interface(proxy), DEVICE_INTERFACE)) { bt_shell_prompt_input(proxy_get_alias(proxy), "Clone (yes/no):", device_clone, proxy); } /* Only clone services */ service = select_service(proxy); if (service) { bt_shell_prompt_input(proxy_get_name(proxy), "Clone (yes/no/all):", service_clone, service); return; } return bt_shell_noninteractive_quit(EXIT_FAILURE); } bluez-5.82/client/PaxHeaders/main.c0000644000000000000000000000005014772767672014245 xustar0020 atime=1743515579 20 ctime=1743591281 bluez-5.82/client/main.c0000644000000000000000000024411514772767672013735 0ustar00rootroot// SPDX-License-Identifier: GPL-2.0-or-later /* * * BlueZ - Bluetooth protocol stack for Linux * * Copyright (C) 2012 Intel Corporation. All rights reserved. * Copyright 2024 NXP * */ #ifdef HAVE_CONFIG_H #include #endif #define _GNU_SOURCE #include #include #include #include #include #include #include #include "src/shared/mainloop.h" #include "src/shared/shell.h" #include "src/shared/timeout.h" #include "src/shared/util.h" #include "src/shared/ad.h" #include "gdbus/gdbus.h" #include "print.h" #include "agent.h" #include "gatt.h" #include "advertising.h" #include "adv_monitor.h" #include "admin.h" #include "player.h" #include "mgmt.h" #include "assistant.h" #include "hci.h" /* String display constants */ #define COLORED_NEW COLOR_GREEN "NEW" COLOR_OFF #define COLORED_CHG COLOR_YELLOW "CHG" COLOR_OFF #define COLORED_DEL COLOR_RED "DEL" COLOR_OFF #define PROMPT_ON "[bluetoothctl]> " #define PROMPT_OFF "Waiting to connect to bluetoothd..." static DBusConnection *dbus_conn; static GDBusProxy *agent_manager; static char *auto_register_agent = NULL; struct adapter { GDBusProxy *proxy; GDBusProxy *ad_proxy; GDBusProxy *adv_monitor_proxy; GList *devices; GList *sets; }; static struct adapter *default_ctrl; static GDBusProxy *default_dev; static char *default_local_attr; static GDBusProxy *default_attr; static GList *ctrl_list; static GList *battery_proxies; static const char *agent_arguments[] = { "on", "off", "auto", "DisplayOnly", "DisplayYesNo", "KeyboardDisplay", "KeyboardOnly", "NoInputNoOutput", NULL }; static const char *ad_arguments[] = { "on", "off", "peripheral", "broadcast", NULL }; static const char * const device_arguments[] = { "Paired", "Bonded", "Trusted", "Connected", NULL }; static void proxy_leak(gpointer data) { printf("Leaking proxy %p\n", data); } static void setup_standard_input(void) { bt_shell_attach(fileno(stdin)); } static void connect_handler(DBusConnection *connection, void *user_data) { bt_shell_set_prompt(PROMPT_ON, COLOR_BLUE); } static void disconnect_handler(DBusConnection *connection, void *user_data) { bt_shell_detach(); bt_shell_set_prompt(PROMPT_OFF, NULL); g_list_free_full(ctrl_list, proxy_leak); g_list_free_full(battery_proxies, proxy_leak); ctrl_list = NULL; battery_proxies = NULL; default_ctrl = NULL; } static void print_adapter(GDBusProxy *proxy, const char *description) { DBusMessageIter iter; const char *address, *name; if (g_dbus_proxy_get_property(proxy, "Address", &iter) == FALSE) return; dbus_message_iter_get_basic(&iter, &address); if (g_dbus_proxy_get_property(proxy, "Alias", &iter) == TRUE) dbus_message_iter_get_basic(&iter, &name); else name = ""; bt_shell_printf("%s%s%sController %s %s %s\n", description ? "[" : "", description ? : "", description ? "] " : "", address, name, default_ctrl && default_ctrl->proxy == proxy ? "[default]" : ""); } #define DISTANCE_VAL_INVALID 0x7FFF static struct set_discovery_filter_args { char *transport; char *pattern; dbus_uint16_t rssi; dbus_int16_t pathloss; char **uuids; size_t uuids_len; dbus_bool_t duplicate; dbus_bool_t discoverable; bool set; bool active; unsigned int timeout; } filter = { .rssi = DISTANCE_VAL_INVALID, .pathloss = DISTANCE_VAL_INVALID, .set = true, }; static void print_device(GDBusProxy *proxy, const char *description) { DBusMessageIter iter; const char *address, *name; uint8_t *flags; int flags_len = 0; if (g_dbus_proxy_get_property(proxy, "Address", &iter) == FALSE) return; dbus_message_iter_get_basic(&iter, &address); if (g_dbus_proxy_get_property(proxy, "Alias", &iter) == TRUE) dbus_message_iter_get_basic(&iter, &name); else name = ""; if (g_dbus_proxy_get_property(proxy, "AdvertisingFlags", &iter)) { DBusMessageIter array; dbus_message_iter_recurse(&iter, &array); dbus_message_iter_get_fixed_array(&array, &flags, &flags_len); } if (!flags_len) goto done; if (!(flags[0] & (BT_AD_FLAG_LIMITED | BT_AD_FLAG_GENERAL))) { /* Only print hidden/non-discoverable if filter.discoverable is * not set. */ if (filter.discoverable) return; bt_shell_printf("%s%s%s" COLOR_BOLDGRAY "Device %s %s" COLOR_OFF "\n", description ? "[" : "", description ? : "", description ? "] " : "", address, name); return; } done: bt_shell_printf("%s%s%sDevice %s %s\n", description ? "[" : "", description ? : "", description ? "] " : "", address, name); } static void print_uuids(GDBusProxy *proxy) { DBusMessageIter iter, value; if (g_dbus_proxy_get_property(proxy, "UUIDs", &iter) == FALSE) return; dbus_message_iter_recurse(&iter, &value); while (dbus_message_iter_get_arg_type(&value) == DBUS_TYPE_STRING) { const char *uuid; dbus_message_iter_get_basic(&value, &uuid); print_uuid("\t", "UUID", uuid); dbus_message_iter_next(&value); } } static void print_experimental(GDBusProxy *proxy) { DBusMessageIter iter, value; if (g_dbus_proxy_get_property(proxy, "ExperimentalFeatures", &iter) == FALSE) return; dbus_message_iter_recurse(&iter, &value); while (dbus_message_iter_get_arg_type(&value) == DBUS_TYPE_STRING) { const char *uuid; dbus_message_iter_get_basic(&value, &uuid); print_uuid("\t", "ExperimentalFeatures", uuid); dbus_message_iter_next(&value); } } static gboolean proxy_is_child(GDBusProxy *device, GDBusProxy *parent) { DBusMessageIter iter; const char *adapter, *path; if (!parent) return FALSE; if (g_dbus_proxy_get_property(device, "Adapter", &iter) == FALSE) return FALSE; dbus_message_iter_get_basic(&iter, &adapter); path = g_dbus_proxy_get_path(parent); if (!strcmp(path, adapter)) return TRUE; return FALSE; } static gboolean service_is_child(GDBusProxy *service) { DBusMessageIter iter; const char *device; if (g_dbus_proxy_get_property(service, "Device", &iter) == FALSE) return FALSE; dbus_message_iter_get_basic(&iter, &device); if (!default_ctrl) return FALSE; return g_dbus_proxy_lookup(default_ctrl->devices, NULL, device, "org.bluez.Device1") != NULL; } static struct adapter *find_parent(GDBusProxy *proxy) { GList *list; for (list = g_list_first(ctrl_list); list; list = g_list_next(list)) { struct adapter *adapter = list->data; if (proxy_is_child(proxy, adapter->proxy) == TRUE) return adapter; } return NULL; } static void set_default_device(GDBusProxy *proxy, const char *attribute) { char *desc = NULL; DBusMessageIter iter; const char *path; default_dev = proxy; if (proxy == NULL) { default_attr = NULL; goto done; } if (!g_dbus_proxy_get_property(proxy, "Alias", &iter)) { if (!g_dbus_proxy_get_property(proxy, "Address", &iter)) goto done; } path = g_dbus_proxy_get_path(proxy); dbus_message_iter_get_basic(&iter, &desc); desc = g_strdup_printf("[%s%s%s]> ", desc, attribute ? ":" : "", attribute ? attribute + strlen(path) : ""); done: bt_shell_set_prompt(desc ? desc : PROMPT_ON, COLOR_BLUE); g_free(desc); } static void battery_added(GDBusProxy *proxy) { battery_proxies = g_list_append(battery_proxies, proxy); } static void battery_removed(GDBusProxy *proxy) { battery_proxies = g_list_remove(battery_proxies, proxy); } static void device_added(GDBusProxy *proxy) { DBusMessageIter iter; struct adapter *adapter = find_parent(proxy); if (!adapter) { /* TODO: Error */ return; } adapter->devices = g_list_append(adapter->devices, proxy); print_device(proxy, COLORED_NEW); bt_shell_set_env(g_dbus_proxy_get_path(proxy), proxy); if (default_dev) return; if (g_dbus_proxy_get_property(proxy, "Connected", &iter)) { dbus_bool_t connected; dbus_message_iter_get_basic(&iter, &connected); if (connected) set_default_device(proxy, NULL); } } static struct adapter *find_ctrl(GList *source, const char *path); static struct adapter *adapter_new(GDBusProxy *proxy) { struct adapter *adapter = g_malloc0(sizeof(struct adapter)); ctrl_list = g_list_append(ctrl_list, adapter); if (!default_ctrl) default_ctrl = adapter; return adapter; } static void adapter_added(GDBusProxy *proxy) { struct adapter *adapter; adapter = find_ctrl(ctrl_list, g_dbus_proxy_get_path(proxy)); if (!adapter) adapter = adapter_new(proxy); adapter->proxy = proxy; print_adapter(proxy, COLORED_NEW); bt_shell_set_env(g_dbus_proxy_get_path(proxy), proxy); } static void ad_manager_added(GDBusProxy *proxy) { struct adapter *adapter; adapter = find_ctrl(ctrl_list, g_dbus_proxy_get_path(proxy)); if (!adapter) adapter = adapter_new(proxy); adapter->ad_proxy = proxy; } static void admon_manager_added(GDBusProxy *proxy) { struct adapter *adapter; adapter = find_ctrl(ctrl_list, g_dbus_proxy_get_path(proxy)); if (!adapter) adapter = adapter_new(proxy); adapter->adv_monitor_proxy = proxy; adv_monitor_add_manager(dbus_conn, proxy); adv_monitor_register_app(dbus_conn); } static void print_set(GDBusProxy *proxy, const char *description) { bt_shell_printf("%s%s%sDeviceSet %s\n", description ? "[" : "", description ? : "", description ? "] " : "", g_dbus_proxy_get_path(proxy)); } static void set_added(GDBusProxy *proxy) { struct adapter *adapter = find_parent(proxy); if (!adapter) return; adapter->sets = g_list_append(adapter->sets, proxy); print_set(proxy, COLORED_NEW); bt_shell_set_env(g_dbus_proxy_get_path(proxy), proxy); } static void proxy_added(GDBusProxy *proxy, void *user_data) { const char *interface; interface = g_dbus_proxy_get_interface(proxy); if (!strcmp(interface, "org.bluez.Device1")) { device_added(proxy); } else if (!strcmp(interface, "org.bluez.Adapter1")) { adapter_added(proxy); } else if (!strcmp(interface, "org.bluez.AgentManager1")) { if (!agent_manager) { agent_manager = proxy; if (auto_register_agent && !bt_shell_get_env("NON_INTERACTIVE")) agent_register(dbus_conn, agent_manager, auto_register_agent); } } else if (!strcmp(interface, "org.bluez.GattService1")) { if (service_is_child(proxy)) gatt_add_service(proxy); } else if (!strcmp(interface, "org.bluez.GattCharacteristic1")) { gatt_add_characteristic(proxy); } else if (!strcmp(interface, "org.bluez.GattDescriptor1")) { gatt_add_descriptor(proxy); } else if (!strcmp(interface, "org.bluez.GattManager1")) { gatt_add_manager(proxy); } else if (!strcmp(interface, "org.bluez.LEAdvertisingManager1")) { ad_manager_added(proxy); } else if (!strcmp(interface, "org.bluez.Battery1")) { battery_added(proxy); } else if (!strcmp(interface, "org.bluez.AdvertisementMonitorManager1")) { admon_manager_added(proxy); } else if (!strcmp(interface, "org.bluez.DeviceSet1")) { set_added(proxy); } } static void set_default_attribute(GDBusProxy *proxy) { const char *path; default_local_attr = NULL; default_attr = proxy; path = g_dbus_proxy_get_path(proxy); set_default_device(default_dev, path); } static void device_removed(GDBusProxy *proxy) { struct adapter *adapter = find_parent(proxy); if (!adapter) { /* TODO: Error */ return; } adapter->devices = g_list_remove(adapter->devices, proxy); print_device(proxy, COLORED_DEL); bt_shell_set_env(g_dbus_proxy_get_path(proxy), NULL); if (default_dev == proxy) set_default_device(NULL, NULL); } static void adapter_removed(GDBusProxy *proxy) { GList *ll; for (ll = g_list_first(ctrl_list); ll; ll = g_list_next(ll)) { struct adapter *adapter = ll->data; if (adapter->proxy == proxy) { print_adapter(proxy, COLORED_DEL); bt_shell_set_env(g_dbus_proxy_get_path(proxy), NULL); if (default_ctrl && default_ctrl->proxy == proxy) { default_ctrl = NULL; set_default_device(NULL, NULL); } ctrl_list = g_list_remove_link(ctrl_list, ll); g_list_free(adapter->devices); g_list_free(adapter->sets); g_free(adapter); g_list_free(ll); return; } } } static void set_removed(GDBusProxy *proxy) { struct adapter *adapter = find_parent(proxy); if (!adapter) return; adapter->sets = g_list_remove(adapter->sets, proxy); print_set(proxy, COLORED_DEL); bt_shell_set_env(g_dbus_proxy_get_path(proxy), NULL); } static void proxy_removed(GDBusProxy *proxy, void *user_data) { const char *interface; interface = g_dbus_proxy_get_interface(proxy); if (!strcmp(interface, "org.bluez.Device1")) { device_removed(proxy); } else if (!strcmp(interface, "org.bluez.Adapter1")) { adapter_removed(proxy); } else if (!strcmp(interface, "org.bluez.AgentManager1")) { if (agent_manager == proxy) { agent_manager = NULL; if (auto_register_agent) agent_unregister(dbus_conn, NULL); } } else if (!strcmp(interface, "org.bluez.GattService1")) { gatt_remove_service(proxy); if (default_attr == proxy) set_default_attribute(NULL); } else if (!strcmp(interface, "org.bluez.GattCharacteristic1")) { gatt_remove_characteristic(proxy); if (default_attr == proxy) set_default_attribute(NULL); } else if (!strcmp(interface, "org.bluez.GattDescriptor1")) { gatt_remove_descriptor(proxy); if (default_attr == proxy) set_default_attribute(NULL); } else if (!strcmp(interface, "org.bluez.GattManager1")) { gatt_remove_manager(proxy); } else if (!strcmp(interface, "org.bluez.LEAdvertisingManager1")) { ad_unregister(dbus_conn, NULL); } else if (!strcmp(interface, "org.bluez.Battery1")) { battery_removed(proxy); } else if (!strcmp(interface, "org.bluez.AdvertisementMonitorManager1")) { adv_monitor_remove_manager(dbus_conn); } else if (!strcmp(interface, "org.bluez.DeviceSet1")) { set_removed(proxy); } } static struct adapter *find_ctrl(GList *source, const char *path) { GList *list; for (list = g_list_first(source); list; list = g_list_next(list)) { struct adapter *adapter = list->data; if (!strcasecmp(g_dbus_proxy_get_path(adapter->proxy), path)) return adapter; } return NULL; } static void property_changed(GDBusProxy *proxy, const char *name, DBusMessageIter *iter, void *user_data) { const char *interface; struct adapter *ctrl; interface = g_dbus_proxy_get_interface(proxy); if (!strcmp(interface, "org.bluez.Device1")) { if (default_ctrl && proxy_is_child(proxy, default_ctrl->proxy) == TRUE) { DBusMessageIter addr_iter; char *str; if (g_dbus_proxy_get_property(proxy, "Address", &addr_iter) == TRUE) { const char *address; dbus_message_iter_get_basic(&addr_iter, &address); str = g_strdup_printf("[" COLORED_CHG "] Device %s ", address); } else str = g_strdup(""); if (strcmp(name, "Connected") == 0) { dbus_bool_t connected; dbus_message_iter_get_basic(iter, &connected); if (connected && default_dev == NULL) set_default_device(proxy, NULL); else if (!connected && default_dev == proxy) set_default_device(NULL, NULL); } print_iter(str, name, iter); g_free(str); } } else if (!strcmp(interface, "org.bluez.Adapter1")) { DBusMessageIter addr_iter; char *str; if (g_dbus_proxy_get_property(proxy, "Address", &addr_iter) == TRUE) { const char *address; dbus_message_iter_get_basic(&addr_iter, &address); str = g_strdup_printf("[" COLORED_CHG "] Controller %s ", address); } else str = g_strdup(""); print_iter(str, name, iter); g_free(str); } else if (!strcmp(interface, "org.bluez.LEAdvertisingManager1")) { DBusMessageIter addr_iter; char *str; ctrl = find_ctrl(ctrl_list, g_dbus_proxy_get_path(proxy)); if (!ctrl) return; if (g_dbus_proxy_get_property(ctrl->proxy, "Address", &addr_iter) == TRUE) { const char *address; dbus_message_iter_get_basic(&addr_iter, &address); str = g_strdup_printf("[" COLORED_CHG "] Controller %s ", address); } else str = g_strdup(""); print_iter(str, name, iter); g_free(str); } else if (proxy == default_attr) { char *str; str = g_strdup_printf("[" COLORED_CHG "] Attribute %s ", g_dbus_proxy_get_path(proxy)); print_iter(str, name, iter); g_free(str); } } static void message_handler(DBusConnection *connection, DBusMessage *message, void *user_data) { bt_shell_printf("[SIGNAL] %s.%s\n", dbus_message_get_interface(message), dbus_message_get_member(message)); } static struct adapter *find_ctrl_by_address(GList *source, const char *address) { GList *list; for (list = g_list_first(source); list; list = g_list_next(list)) { struct adapter *adapter = list->data; DBusMessageIter iter; const char *str; if (g_dbus_proxy_get_property(adapter->proxy, "Address", &iter) == FALSE) continue; dbus_message_iter_get_basic(&iter, &str); if (!strcasecmp(str, address)) return adapter; } return NULL; } static GDBusProxy *find_proxies_by_path(GList *source, const char *path) { GList *list; for (list = g_list_first(source); list; list = g_list_next(list)) { GDBusProxy *proxy = list->data; if (strcmp(g_dbus_proxy_get_path(proxy), path) == 0) return proxy; } return NULL; } static GDBusProxy *find_proxy_by_address(GList *source, const char *address) { GList *list; for (list = g_list_first(source); list; list = g_list_next(list)) { GDBusProxy *proxy = list->data; DBusMessageIter iter; const char *str; if (g_dbus_proxy_get_property(proxy, "Address", &iter) == FALSE) continue; dbus_message_iter_get_basic(&iter, &str); if (!strcasecmp(str, address)) return proxy; } return NULL; } static gboolean check_default_ctrl(void) { if (!default_ctrl) { bt_shell_printf("No default controller available\n"); return FALSE; } return TRUE; } static gboolean parse_argument_devices(int argc, char *argv[], const char * const *arg_table, const char **option) { const char * const *opt; if (argc < 2) { *option = NULL; return TRUE; } for (opt = arg_table; opt && *opt; opt++) { if (strcmp(argv[1], *opt) == 0) { *option = *opt; return TRUE; } } bt_shell_printf("Invalid argument %s\n", argv[1]); return FALSE; } static gboolean parse_argument(int argc, char *argv[], const char **arg_table, const char *msg, dbus_bool_t *value, const char **option) { const char **opt; if (argc < 2) { bt_shell_printf("Missing argument to %s\n", argv[0]); return FALSE; } if (!strcmp(argv[1], "help")) { for (opt = arg_table; opt && *opt; opt++) bt_shell_printf("%s\n", *opt); bt_shell_noninteractive_quit(EXIT_SUCCESS); return FALSE; } if (!strcmp(argv[1], "on") || !strcmp(argv[1], "yes")) { *value = TRUE; if (option) *option = ""; return TRUE; } if (!strcmp(argv[1], "off") || !strcmp(argv[1], "no")) { *value = FALSE; return TRUE; } for (opt = arg_table; opt && *opt; opt++) { if (strcmp(argv[1], *opt) == 0) { *value = TRUE; *option = *opt; return TRUE; } } bt_shell_printf("Invalid argument %s\n", argv[1]); return FALSE; } static void cmd_list(int argc, char *argv[]) { GList *list; for (list = g_list_first(ctrl_list); list; list = g_list_next(list)) { struct adapter *adapter = list->data; print_adapter(adapter->proxy, NULL); } return bt_shell_noninteractive_quit(EXIT_SUCCESS); } static void cmd_show(int argc, char *argv[]) { struct adapter *adapter; DBusMessageIter iter; const char *address; if (argc < 2 || !strlen(argv[1])) { if (check_default_ctrl() == FALSE) return bt_shell_noninteractive_quit(EXIT_FAILURE); adapter = default_ctrl; } else { adapter = find_ctrl_by_address(ctrl_list, argv[1]); if (!adapter) { bt_shell_printf("Controller %s not available\n", argv[1]); return bt_shell_noninteractive_quit(EXIT_FAILURE); } } if (!g_dbus_proxy_get_property(adapter->proxy, "Address", &iter)) return bt_shell_noninteractive_quit(EXIT_FAILURE); dbus_message_iter_get_basic(&iter, &address); if (g_dbus_proxy_get_property(adapter->proxy, "AddressType", &iter)) { const char *type; dbus_message_iter_get_basic(&iter, &type); bt_shell_printf("Controller %s (%s)\n", address, type); } else { bt_shell_printf("Controller %s\n", address); } print_property(adapter->proxy, "Manufacturer"); print_property(adapter->proxy, "Version"); print_property(adapter->proxy, "Name"); print_property(adapter->proxy, "Alias"); print_property(adapter->proxy, "Class"); print_property(adapter->proxy, "Powered"); print_property(adapter->proxy, "PowerState"); print_property(adapter->proxy, "Discoverable"); print_property(adapter->proxy, "DiscoverableTimeout"); print_property(adapter->proxy, "Pairable"); print_uuids(adapter->proxy); print_property(adapter->proxy, "Modalias"); print_property(adapter->proxy, "Discovering"); print_property(adapter->proxy, "Roles"); print_experimental(adapter->proxy); if (adapter->ad_proxy) { bt_shell_printf("Advertising Features:\n"); print_property(adapter->ad_proxy, "ActiveInstances"); print_property(adapter->ad_proxy, "SupportedInstances"); print_property(adapter->ad_proxy, "SupportedIncludes"); print_property(adapter->ad_proxy, "SupportedSecondaryChannels"); print_property(adapter->ad_proxy, "SupportedCapabilities"); print_property(adapter->ad_proxy, "SupportedFeatures"); } if (adapter->adv_monitor_proxy) { bt_shell_printf("Advertisement Monitor Features:\n"); print_property(adapter->adv_monitor_proxy, "SupportedMonitorTypes"); print_property(adapter->adv_monitor_proxy, "SupportedFeatures"); } return bt_shell_noninteractive_quit(EXIT_SUCCESS); } static void cmd_select(int argc, char *argv[]) { struct adapter *adapter; adapter = find_ctrl_by_address(ctrl_list, argv[1]); if (!adapter) { bt_shell_printf("Controller %s not available\n", argv[1]); return bt_shell_noninteractive_quit(EXIT_FAILURE); } if (default_ctrl && default_ctrl->proxy == adapter->proxy) return bt_shell_noninteractive_quit(EXIT_SUCCESS); default_ctrl = adapter; print_adapter(adapter->proxy, NULL); return bt_shell_noninteractive_quit(EXIT_SUCCESS); } static void cmd_devices(int argc, char *argv[]) { GList *ll; const char *property; if (!parse_argument_devices(argc, argv, device_arguments, &property)) return bt_shell_noninteractive_quit(EXIT_FAILURE); if (check_default_ctrl() == FALSE) return bt_shell_noninteractive_quit(EXIT_SUCCESS); for (ll = g_list_first(default_ctrl->devices); ll; ll = g_list_next(ll)) { GDBusProxy *proxy = ll->data; DBusMessageIter iter; dbus_bool_t status; if (property) { if (g_dbus_proxy_get_property(proxy, property, &iter) == FALSE) continue; dbus_message_iter_get_basic(&iter, &status); if (!status) continue; } print_device(proxy, NULL); } return bt_shell_noninteractive_quit(EXIT_SUCCESS); } static void generic_callback(const DBusError *error, void *user_data) { char *str = user_data; if (dbus_error_is_set(error)) { bt_shell_printf("Failed to set %s: %s\n", str, error->name); return bt_shell_noninteractive_quit(EXIT_FAILURE); } else { bt_shell_printf("Changing %s succeeded\n", str); return bt_shell_noninteractive_quit(EXIT_SUCCESS); } } static void cmd_system_alias(int argc, char *argv[]) { char *name; if (check_default_ctrl() == FALSE) return bt_shell_noninteractive_quit(EXIT_FAILURE); name = g_strdup(argv[1]); if (g_dbus_proxy_set_property_basic(default_ctrl->proxy, "Alias", DBUS_TYPE_STRING, &name, generic_callback, name, g_free) == TRUE) return; g_free(name); } static void cmd_reset_alias(int argc, char *argv[]) { char *name; if (check_default_ctrl() == FALSE) return bt_shell_noninteractive_quit(EXIT_FAILURE); name = g_strdup(""); if (g_dbus_proxy_set_property_basic(default_ctrl->proxy, "Alias", DBUS_TYPE_STRING, &name, generic_callback, name, g_free) == TRUE) return; g_free(name); } static void cmd_power(int argc, char *argv[]) { dbus_bool_t powered; char *str; if (!parse_argument(argc, argv, NULL, NULL, &powered, NULL)) return bt_shell_noninteractive_quit(EXIT_FAILURE); if (check_default_ctrl() == FALSE) return bt_shell_noninteractive_quit(EXIT_FAILURE); str = g_strdup_printf("power %s", powered == TRUE ? "on" : "off"); if (g_dbus_proxy_set_property_basic(default_ctrl->proxy, "Powered", DBUS_TYPE_BOOLEAN, &powered, generic_callback, str, g_free) == TRUE) return; g_free(str); } static void cmd_pairable(int argc, char *argv[]) { dbus_bool_t pairable; char *str; if (!parse_argument(argc, argv, NULL, NULL, &pairable, NULL)) return bt_shell_noninteractive_quit(EXIT_FAILURE); if (check_default_ctrl() == FALSE) return bt_shell_noninteractive_quit(EXIT_FAILURE); str = g_strdup_printf("pairable %s", pairable == TRUE ? "on" : "off"); if (g_dbus_proxy_set_property_basic(default_ctrl->proxy, "Pairable", DBUS_TYPE_BOOLEAN, &pairable, generic_callback, str, g_free) == TRUE) return; g_free(str); return bt_shell_noninteractive_quit(EXIT_FAILURE); } static void cmd_discoverable(int argc, char *argv[]) { DBusMessageIter iter; dbus_bool_t discoverable; char *str; if (!parse_argument(argc, argv, NULL, NULL, &discoverable, NULL)) return bt_shell_noninteractive_quit(EXIT_FAILURE); if (check_default_ctrl() == FALSE) return bt_shell_noninteractive_quit(EXIT_FAILURE); if (discoverable && g_dbus_proxy_get_property(default_ctrl->proxy, "DiscoverableTimeout", &iter)) { uint32_t value; dbus_message_iter_get_basic(&iter, &value); if (!value) bt_shell_printf("Warning: setting discoverable while " "discoverable-timeout not set(0) is not" " recommended\n"); } str = g_strdup_printf("discoverable %s", discoverable == TRUE ? "on" : "off"); if (g_dbus_proxy_set_property_basic(default_ctrl->proxy, "Discoverable", DBUS_TYPE_BOOLEAN, &discoverable, generic_callback, str, g_free) == TRUE) return; g_free(str); return bt_shell_noninteractive_quit(EXIT_FAILURE); } static void cmd_discoverable_timeout(int argc, char *argv[]) { uint32_t value; char *endptr = NULL; char *str; if (argc < 2) { DBusMessageIter iter; if (!g_dbus_proxy_get_property(default_ctrl->proxy, "DiscoverableTimeout", &iter)) { bt_shell_printf("Unable to get DiscoverableTimeout\n"); return bt_shell_noninteractive_quit(EXIT_FAILURE); } dbus_message_iter_get_basic(&iter, &value); bt_shell_printf("DiscoverableTimeout: %d seconds\n", value); return; } value = strtol(argv[1], &endptr, 0); if (!endptr || *endptr != '\0' || value > UINT32_MAX) { bt_shell_printf("Invalid argument\n"); return bt_shell_noninteractive_quit(EXIT_FAILURE); } str = g_strdup_printf("discoverable-timeout %d", value); if (g_dbus_proxy_set_property_basic(default_ctrl->proxy, "DiscoverableTimeout", DBUS_TYPE_UINT32, &value, generic_callback, str, g_free)) return; g_free(str); return bt_shell_noninteractive_quit(EXIT_FAILURE); } static void cmd_agent(int argc, char *argv[]) { dbus_bool_t enable; const char *capability; if (!parse_argument(argc, argv, agent_arguments, "capability", &enable, &capability)) return bt_shell_noninteractive_quit(EXIT_FAILURE); if (enable == TRUE) { g_free(auto_register_agent); auto_register_agent = g_strdup(capability); if (agent_manager) agent_register(dbus_conn, agent_manager, auto_register_agent); else bt_shell_printf("Agent registration enabled\n"); } else { g_free(auto_register_agent); auto_register_agent = NULL; if (agent_manager) agent_unregister(dbus_conn, agent_manager); else bt_shell_printf("Agent registration disabled\n"); } return bt_shell_noninteractive_quit(EXIT_SUCCESS); } static void cmd_default_agent(int argc, char *argv[]) { agent_default(dbus_conn, agent_manager); } static void start_discovery_reply(DBusMessage *message, void *user_data) { dbus_bool_t enable = GPOINTER_TO_UINT(user_data); DBusError error; dbus_error_init(&error); if (dbus_set_error_from_message(&error, message) == TRUE) { bt_shell_printf("Failed to %s discovery: %s\n", enable == TRUE ? "start" : "stop", error.name); dbus_error_free(&error); return bt_shell_noninteractive_quit(EXIT_FAILURE); } bt_shell_printf("Discovery %s\n", enable ? "started" : "stopped"); filter.active = enable; /* Leave the discovery running even on noninteractive mode */ } static void clear_discovery_filter(DBusMessageIter *iter, void *user_data) { DBusMessageIter dict; dbus_message_iter_open_container(iter, DBUS_TYPE_ARRAY, DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING DBUS_TYPE_STRING_AS_STRING DBUS_TYPE_VARIANT_AS_STRING DBUS_DICT_ENTRY_END_CHAR_AS_STRING, &dict); dbus_message_iter_close_container(iter, &dict); } static void set_discovery_filter_setup(DBusMessageIter *iter, void *user_data) { struct set_discovery_filter_args *args = user_data; DBusMessageIter dict; dbus_message_iter_open_container(iter, DBUS_TYPE_ARRAY, DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING DBUS_TYPE_STRING_AS_STRING DBUS_TYPE_VARIANT_AS_STRING DBUS_DICT_ENTRY_END_CHAR_AS_STRING, &dict); g_dbus_dict_append_array(&dict, "UUIDs", DBUS_TYPE_STRING, &args->uuids, args->uuids_len); if (args->pathloss != DISTANCE_VAL_INVALID) g_dbus_dict_append_entry(&dict, "Pathloss", DBUS_TYPE_UINT16, &args->pathloss); if (args->rssi != DISTANCE_VAL_INVALID) g_dbus_dict_append_entry(&dict, "RSSI", DBUS_TYPE_INT16, &args->rssi); if (args->transport != NULL) g_dbus_dict_append_entry(&dict, "Transport", DBUS_TYPE_STRING, &args->transport); if (args->duplicate) g_dbus_dict_append_entry(&dict, "DuplicateData", DBUS_TYPE_BOOLEAN, &args->duplicate); if (args->discoverable) g_dbus_dict_append_entry(&dict, "Discoverable", DBUS_TYPE_BOOLEAN, &args->discoverable); if (args->pattern != NULL) g_dbus_dict_append_entry(&dict, "Pattern", DBUS_TYPE_STRING, &args->pattern); dbus_message_iter_close_container(iter, &dict); } static void set_discovery_filter_reply(DBusMessage *message, void *user_data) { DBusError error; dbus_error_init(&error); if (dbus_set_error_from_message(&error, message) == TRUE) { bt_shell_printf("SetDiscoveryFilter failed: %s\n", error.name); dbus_error_free(&error); return bt_shell_noninteractive_quit(EXIT_FAILURE); } filter.set = true; bt_shell_printf("SetDiscoveryFilter success\n"); return bt_shell_noninteractive_quit(EXIT_SUCCESS); } static void set_discovery_filter(bool cleared) { GDBusSetupFunction func; if (check_default_ctrl() == FALSE || filter.set) return; func = cleared ? clear_discovery_filter : set_discovery_filter_setup; if (g_dbus_proxy_method_call(default_ctrl->proxy, "SetDiscoveryFilter", func, set_discovery_filter_reply, &filter, NULL) == FALSE) { bt_shell_printf("Failed to set discovery filter\n"); return bt_shell_noninteractive_quit(EXIT_FAILURE); } filter.set = true; } static const char *scan_arguments[] = { "on", "off", "bredr", "le", NULL }; static void cmd_scan(int argc, char *argv[]) { dbus_bool_t enable; const char *method; const char *mode; if (!parse_argument(argc, argv, scan_arguments, "Mode", &enable, &mode)) return bt_shell_noninteractive_quit(EXIT_FAILURE); if (check_default_ctrl() == FALSE) return bt_shell_noninteractive_quit(EXIT_FAILURE); if (enable == TRUE) { if (!g_strcmp0(mode, "")) { g_free(filter.transport); filter.transport = NULL; filter.set = false; } else { g_free(filter.transport); filter.transport = g_strdup(mode); filter.set = false; } set_discovery_filter(false); method = "StartDiscovery"; } else method = "StopDiscovery"; if (g_dbus_proxy_method_call(default_ctrl->proxy, method, NULL, start_discovery_reply, GUINT_TO_POINTER(enable), NULL) == FALSE) { bt_shell_printf("Failed to %s discovery\n", enable == TRUE ? "start" : "stop"); return bt_shell_noninteractive_quit(EXIT_FAILURE); } } static void cmd_scan_filter_uuids(int argc, char *argv[]) { if (argc < 2 || !strlen(argv[1])) { char **uuid; for (uuid = filter.uuids; uuid && *uuid; uuid++) print_uuid("\t", "UUID", *uuid); return bt_shell_noninteractive_quit(EXIT_SUCCESS); } g_strfreev(filter.uuids); filter.uuids = NULL; filter.uuids_len = 0; if (!strcmp(argv[1], "all")) goto commit; filter.uuids = g_strdupv(&argv[1]); if (!filter.uuids) { bt_shell_printf("Failed to parse input\n"); return bt_shell_noninteractive_quit(EXIT_FAILURE); } filter.uuids_len = g_strv_length(filter.uuids); commit: filter.set = false; if (filter.active) set_discovery_filter(false); } static void cmd_scan_filter_rssi(int argc, char *argv[]) { if (argc < 2 || !strlen(argv[1])) { if (filter.rssi != DISTANCE_VAL_INVALID) bt_shell_printf("RSSI: %d\n", filter.rssi); return bt_shell_noninteractive_quit(EXIT_SUCCESS); } filter.pathloss = DISTANCE_VAL_INVALID; filter.rssi = atoi(argv[1]); filter.set = false; if (filter.active) set_discovery_filter(false); } static void cmd_scan_filter_pathloss(int argc, char *argv[]) { if (argc < 2 || !strlen(argv[1])) { if (filter.pathloss != DISTANCE_VAL_INVALID) bt_shell_printf("Pathloss: %d\n", filter.pathloss); return bt_shell_noninteractive_quit(EXIT_SUCCESS); } filter.rssi = DISTANCE_VAL_INVALID; filter.pathloss = atoi(argv[1]); filter.set = false; if (filter.active) set_discovery_filter(false); } static void cmd_scan_filter_transport(int argc, char *argv[]) { if (argc < 2 || !strlen(argv[1])) { if (filter.transport) bt_shell_printf("Transport: %s\n", filter.transport); return bt_shell_noninteractive_quit(EXIT_SUCCESS); } g_free(filter.transport); filter.transport = g_strdup(argv[1]); filter.set = false; if (filter.active) set_discovery_filter(false); } static void cmd_scan_filter_duplicate_data(int argc, char *argv[]) { if (argc < 2 || !strlen(argv[1])) { bt_shell_printf("DuplicateData: %s\n", filter.duplicate ? "on" : "off"); return bt_shell_noninteractive_quit(EXIT_SUCCESS); } if (!strcmp(argv[1], "on")) filter.duplicate = true; else if (!strcmp(argv[1], "off")) filter.duplicate = false; else { bt_shell_printf("Invalid option: %s\n", argv[1]); return bt_shell_noninteractive_quit(EXIT_FAILURE); } filter.set = false; if (filter.active) set_discovery_filter(false); } static void cmd_scan_filter_discoverable(int argc, char *argv[]) { if (argc < 2 || !strlen(argv[1])) { bt_shell_printf("Discoverable: %s\n", filter.discoverable ? "on" : "off"); return bt_shell_noninteractive_quit(EXIT_SUCCESS); } if (!strcmp(argv[1], "on")) filter.discoverable = true; else if (!strcmp(argv[1], "off")) filter.discoverable = false; else { bt_shell_printf("Invalid option: %s\n", argv[1]); return bt_shell_noninteractive_quit(EXIT_FAILURE); } filter.set = false; if (filter.active) set_discovery_filter(false); } static void cmd_scan_filter_pattern(int argc, char *argv[]) { if (argc < 2 || !strlen(argv[1])) { bt_shell_printf("Pattern: %s\n", filter.pattern); return bt_shell_noninteractive_quit(EXIT_SUCCESS); } free(filter.pattern); filter.pattern = strdup(argv[1]); filter.set = false; if (filter.active) set_discovery_filter(false); } static void filter_clear_uuids(void) { g_strfreev(filter.uuids); filter.uuids = NULL; filter.uuids_len = 0; } static void filter_clear_rssi(void) { filter.rssi = DISTANCE_VAL_INVALID; } static void filter_clear_pathloss(void) { filter.pathloss = DISTANCE_VAL_INVALID; } static void filter_clear_transport(void) { g_free(filter.transport); filter.transport = NULL; } static void filter_clear_duplicate(void) { filter.duplicate = false; } static void filter_clear_discoverable(void) { filter.discoverable = false; } static void filter_clear_pattern(void) { free(filter.pattern); filter.pattern = NULL; } struct clear_entry { const char *name; void (*clear) (void); }; static const struct clear_entry filter_clear[] = { { "uuids", filter_clear_uuids }, { "rssi", filter_clear_rssi }, { "pathloss", filter_clear_pathloss }, { "transport", filter_clear_transport }, { "duplicate-data", filter_clear_duplicate }, { "discoverable", filter_clear_discoverable }, { "pattern", filter_clear_pattern }, {} }; static char *filter_clear_generator(const char *text, int state) { static int index, len; const char *arg; if (!state) { index = 0; len = strlen(text); } while ((arg = filter_clear[index].name)) { index++; if (!strncmp(arg, text, len)) return strdup(arg); } return NULL; } static gboolean data_clear(const struct clear_entry *entry_table, const char *name) { const struct clear_entry *entry; bool all = false; if (!name || !strlen(name) || !strcmp("all", name)) all = true; for (entry = entry_table; entry && entry->name; entry++) { if (all || !strcmp(entry->name, name)) { entry->clear(); if (!all) goto done; } } if (!all) { bt_shell_printf("Invalid argument %s\n", name); return FALSE; } done: return TRUE; } static void cmd_scan_filter_clear(int argc, char *argv[]) { bool all = false; if (argc < 2 || !strlen(argv[1])) all = true; if (!data_clear(filter_clear, all ? "all" : argv[1])) return bt_shell_noninteractive_quit(EXIT_FAILURE); filter.set = false; if (check_default_ctrl() == FALSE) return bt_shell_noninteractive_quit(EXIT_FAILURE); set_discovery_filter(all); } static struct GDBusProxy *find_device(int argc, char *argv[]) { GDBusProxy *proxy; if (argc < 2 || !strlen(argv[1])) { if (default_dev) return default_dev; bt_shell_printf("Missing device address argument\n"); return NULL; } if (check_default_ctrl() == FALSE) return NULL; proxy = find_proxy_by_address(default_ctrl->devices, argv[1]); if (!proxy) { bt_shell_printf("Device %s not available\n", argv[1]); return NULL; } return proxy; } static struct GDBusProxy *find_set(int argc, char *argv[]) { GDBusProxy *proxy; if (check_default_ctrl() == FALSE) return NULL; proxy = find_proxies_by_path(default_ctrl->sets, argv[1]); if (!proxy) { bt_shell_printf("DeviceSet %s not available\n", argv[1]); return NULL; } return proxy; } static void cmd_set_info(int argc, char *argv[]) { GDBusProxy *proxy; proxy = find_set(argc, argv); if (!proxy) return bt_shell_noninteractive_quit(EXIT_FAILURE); bt_shell_printf("DeviceSet %s\n", g_dbus_proxy_get_path(proxy)); print_property(proxy, "AutoConnect"); print_property(proxy, "Devices"); print_property(proxy, "Size"); return bt_shell_noninteractive_quit(EXIT_SUCCESS); } static void cmd_info(int argc, char *argv[]) { GDBusProxy *proxy; GDBusProxy *battery_proxy; DBusMessageIter iter; const char *address; proxy = find_device(argc, argv); if (!proxy) return cmd_set_info(argc, argv); if (g_dbus_proxy_get_property(proxy, "Address", &iter) == FALSE) return bt_shell_noninteractive_quit(EXIT_FAILURE); dbus_message_iter_get_basic(&iter, &address); if (g_dbus_proxy_get_property(proxy, "AddressType", &iter) == TRUE) { const char *type; dbus_message_iter_get_basic(&iter, &type); bt_shell_printf("Device %s (%s)\n", address, type); } else { bt_shell_printf("Device %s\n", address); } print_property(proxy, "Name"); print_property(proxy, "Alias"); print_property(proxy, "Class"); print_property(proxy, "Appearance"); print_property(proxy, "Icon"); print_property(proxy, "Paired"); print_property(proxy, "Bonded"); print_property(proxy, "Trusted"); print_property(proxy, "Blocked"); print_property(proxy, "Connected"); print_property(proxy, "WakeAllowed"); print_property(proxy, "LegacyPairing"); print_uuids(proxy); print_property(proxy, "Modalias"); print_property(proxy, "ManufacturerData"); print_property(proxy, "ServiceData"); print_property(proxy, "RSSI"); print_property(proxy, "TxPower"); print_property(proxy, "AdvertisingFlags"); print_property(proxy, "AdvertisingData"); print_property(proxy, "Sets"); print_property(proxy, "PreferredBearer"); battery_proxy = find_proxies_by_path(battery_proxies, g_dbus_proxy_get_path(proxy)); print_property_with_label(battery_proxy, "Percentage", "Battery Percentage"); return bt_shell_noninteractive_quit(EXIT_SUCCESS); } static void pair_reply(DBusMessage *message, void *user_data) { DBusError error; dbus_error_init(&error); if (dbus_set_error_from_message(&error, message) == TRUE) { bt_shell_printf("Failed to pair: %s\n", error.name); dbus_error_free(&error); return bt_shell_noninteractive_quit(EXIT_FAILURE); } bt_shell_printf("Pairing successful\n"); return bt_shell_noninteractive_quit(EXIT_SUCCESS); } static const char *proxy_address(GDBusProxy *proxy) { DBusMessageIter iter; const char *addr; if (!g_dbus_proxy_get_property(proxy, "Address", &iter)) return NULL; dbus_message_iter_get_basic(&iter, &addr); return addr; } static void cmd_pair(int argc, char *argv[]) { GDBusProxy *proxy; proxy = find_device(argc, argv); if (!proxy) return bt_shell_noninteractive_quit(EXIT_FAILURE); if (g_dbus_proxy_method_call(proxy, "Pair", NULL, pair_reply, NULL, NULL) == FALSE) { bt_shell_printf("Failed to pair\n"); return bt_shell_noninteractive_quit(EXIT_FAILURE); } bt_shell_printf("Attempting to pair with %s\n", proxy_address(proxy)); } static void cancel_pairing_reply(DBusMessage *message, void *user_data) { DBusError error; dbus_error_init(&error); if (dbus_set_error_from_message(&error, message) == TRUE) { bt_shell_printf("Failed to cancel pairing: %s\n", error.name); dbus_error_free(&error); return; } bt_shell_printf("Cancel pairing successful\n"); } static void cmd_cancel_pairing(int argc, char *argv[]) { GDBusProxy *proxy; proxy = find_device(argc, argv); if (!proxy) return; if (g_dbus_proxy_method_call(proxy, "CancelPairing", NULL, cancel_pairing_reply, NULL, NULL) == FALSE) { bt_shell_printf("Failed to cancel pairing\n"); return; } bt_shell_printf("Attempting to cancel pairing with %s\n", proxy_address(proxy)); } static void cmd_trust(int argc, char *argv[]) { GDBusProxy *proxy; dbus_bool_t trusted; char *str; proxy = find_device(argc, argv); if (!proxy) return bt_shell_noninteractive_quit(EXIT_FAILURE); trusted = TRUE; str = g_strdup_printf("%s trust", proxy_address(proxy)); if (g_dbus_proxy_set_property_basic(proxy, "Trusted", DBUS_TYPE_BOOLEAN, &trusted, generic_callback, str, g_free) == TRUE) return; g_free(str); return bt_shell_noninteractive_quit(EXIT_FAILURE); } static void cmd_untrust(int argc, char *argv[]) { GDBusProxy *proxy; dbus_bool_t trusted; char *str; proxy = find_device(argc, argv); if (!proxy) return bt_shell_noninteractive_quit(EXIT_FAILURE); trusted = FALSE; str = g_strdup_printf("%s untrust", proxy_address(proxy)); if (g_dbus_proxy_set_property_basic(proxy, "Trusted", DBUS_TYPE_BOOLEAN, &trusted, generic_callback, str, g_free) == TRUE) return; g_free(str); return bt_shell_noninteractive_quit(EXIT_FAILURE); } static void cmd_block(int argc, char *argv[]) { GDBusProxy *proxy; dbus_bool_t blocked; char *str; proxy = find_device(argc, argv); if (!proxy) return bt_shell_noninteractive_quit(EXIT_FAILURE); blocked = TRUE; str = g_strdup_printf("%s block", proxy_address(proxy)); if (g_dbus_proxy_set_property_basic(proxy, "Blocked", DBUS_TYPE_BOOLEAN, &blocked, generic_callback, str, g_free) == TRUE) return; g_free(str); return bt_shell_noninteractive_quit(EXIT_FAILURE); } static void cmd_unblock(int argc, char *argv[]) { GDBusProxy *proxy; dbus_bool_t blocked; char *str; proxy = find_device(argc, argv); if (!proxy) return bt_shell_noninteractive_quit(EXIT_FAILURE); blocked = FALSE; str = g_strdup_printf("%s unblock", proxy_address(proxy)); if (g_dbus_proxy_set_property_basic(proxy, "Blocked", DBUS_TYPE_BOOLEAN, &blocked, generic_callback, str, g_free) == TRUE) return; g_free(str); return bt_shell_noninteractive_quit(EXIT_FAILURE); } static void remove_device_reply(DBusMessage *message, void *user_data) { DBusError error; dbus_error_init(&error); if (dbus_set_error_from_message(&error, message) == TRUE) { bt_shell_printf("Failed to remove device: %s\n", error.name); dbus_error_free(&error); return bt_shell_noninteractive_quit(EXIT_FAILURE); } bt_shell_printf("Device has been removed\n"); return bt_shell_noninteractive_quit(EXIT_SUCCESS); } static void remove_device_setup(DBusMessageIter *iter, void *user_data) { const char *path = user_data; dbus_message_iter_append_basic(iter, DBUS_TYPE_OBJECT_PATH, &path); } static void remove_device(GDBusProxy *proxy) { char *path; if (!default_ctrl) return; path = g_strdup(g_dbus_proxy_get_path(proxy)); if (g_dbus_proxy_method_call(default_ctrl->proxy, "RemoveDevice", remove_device_setup, remove_device_reply, path, g_free) == FALSE) { bt_shell_printf("Failed to remove device\n"); g_free(path); return bt_shell_noninteractive_quit(EXIT_FAILURE); } } static void cmd_remove(int argc, char *argv[]) { GDBusProxy *proxy; if (check_default_ctrl() == FALSE) return bt_shell_noninteractive_quit(EXIT_FAILURE); if (strcmp(argv[1], "*") == 0) { GList *list; for (list = default_ctrl->devices; list; list = g_list_next(list)) { GDBusProxy *proxy = list->data; remove_device(proxy); } return; } proxy = find_proxy_by_address(default_ctrl->devices, argv[1]); if (!proxy) { bt_shell_printf("Device %s not available\n", argv[1]); return bt_shell_noninteractive_quit(EXIT_FAILURE); } remove_device(proxy); } struct connection_data { GDBusProxy *proxy; char *uuid; }; static void connection_setup(DBusMessageIter *iter, void *user_data) { struct connection_data *data = user_data; if (!data->uuid) return; dbus_message_iter_append_basic(iter, DBUS_TYPE_STRING, &data->uuid); } static void format_connection_profile(char *output, size_t size, const char *uuid) { const char *text; text = bt_uuidstr_to_str(uuid); if (!text) text = uuid; snprintf(output, size, " profile \"%s\"", text); } static void connect_reply(DBusMessage *message, void *user_data) { struct connection_data *data = user_data; GDBusProxy *proxy = data->proxy; DBusError error; dbus_error_init(&error); g_free(data->uuid); g_free(data); if (dbus_set_error_from_message(&error, message) == TRUE) { bt_shell_printf("Failed to connect: %s %s\n", error.name, error.message); dbus_error_free(&error); return bt_shell_noninteractive_quit(EXIT_FAILURE); } bt_shell_printf("Connection successful\n"); set_default_device(proxy, NULL); return bt_shell_noninteractive_quit(EXIT_SUCCESS); } static void cmd_connect(int argc, char *argv[]) { struct connection_data *data; const char *method = "Connect"; char profile[128] = ""; GDBusProxy *proxy; if (check_default_ctrl() == FALSE) return bt_shell_noninteractive_quit(EXIT_FAILURE); proxy = find_proxy_by_address(default_ctrl->devices, argv[1]); if (!proxy) { bt_shell_printf("Device %s not available\n", argv[1]); return bt_shell_noninteractive_quit(EXIT_FAILURE); } data = new0(struct connection_data, 1); data->proxy = proxy; if (argc == 3) { method = "ConnectProfile"; data->uuid = g_strdup(argv[2]); format_connection_profile(profile, sizeof(profile), argv[2]); } if (g_dbus_proxy_method_call(proxy, method, connection_setup, connect_reply, data, NULL) == FALSE) { bt_shell_printf("Failed to connect\n"); return bt_shell_noninteractive_quit(EXIT_FAILURE); } bt_shell_printf("Attempting to connect%s to %s\n", profile, argv[1]); } static void disconn_reply(DBusMessage *message, void *user_data) { struct connection_data *data = user_data; const bool profile_disconnected = data->uuid != NULL; GDBusProxy *proxy = data->proxy; DBusError error; dbus_error_init(&error); g_free(data->uuid); g_free(data); if (dbus_set_error_from_message(&error, message) == TRUE) { bt_shell_printf("Failed to disconnect: %s\n", error.name); dbus_error_free(&error); return bt_shell_noninteractive_quit(EXIT_FAILURE); } bt_shell_printf("Disconnection successful\n"); /* If only a single profile was disconnected, the device itself might * still be connected. In that case, let the property change handler * take care of setting the default device to NULL. */ if (proxy == default_dev && !profile_disconnected) set_default_device(NULL, NULL); return bt_shell_noninteractive_quit(EXIT_SUCCESS); } static void cmd_disconn(int argc, char *argv[]) { struct connection_data *data; const char *method = "Disconnect"; char profile[128] = ""; GDBusProxy *proxy; proxy = find_device(argc, argv); if (!proxy) return bt_shell_noninteractive_quit(EXIT_FAILURE); data = new0(struct connection_data, 1); data->proxy = proxy; if (argc == 3) { method = "DisconnectProfile"; data->uuid = g_strdup(argv[2]); format_connection_profile(profile, sizeof(profile), argv[2]); } if (g_dbus_proxy_method_call(proxy, method, connection_setup, disconn_reply, data, NULL) == FALSE) { bt_shell_printf("Failed to disconnect\n"); return bt_shell_noninteractive_quit(EXIT_FAILURE); } bt_shell_printf("Attempting to disconnect%s from %s\n", profile, proxy_address(proxy)); } static void cmd_wake(int argc, char *argv[]) { GDBusProxy *proxy; dbus_bool_t value; char *str; proxy = find_device(argc, argv); if (!proxy) return bt_shell_noninteractive_quit(EXIT_FAILURE); if (argc <= 2) { print_property(proxy, "WakeAllowed"); return; } if (!strcasecmp(argv[2], "on")) { value = TRUE; } else if (!strcasecmp(argv[2], "off")) { value = FALSE; } else { bt_shell_printf("Invalid value %s\n", argv[2]); return bt_shell_noninteractive_quit(EXIT_FAILURE); } str = g_strdup_printf("wake %s", value == TRUE ? "on" : "off"); if (g_dbus_proxy_set_property_basic(proxy, "WakeAllowed", DBUS_TYPE_BOOLEAN, &value, generic_callback, str, g_free)) return; g_free(str); return bt_shell_noninteractive_quit(EXIT_FAILURE); } static void cmd_bearer(int argc, char *argv[]) { GDBusProxy *proxy; char *str; proxy = find_device(argc, argv); if (!proxy) return bt_shell_noninteractive_quit(EXIT_FAILURE); if (argc <= 2) { print_property(proxy, "PreferredBearer"); return; } str = strdup(argv[2]); if (g_dbus_proxy_set_property_basic(proxy, "PreferredBearer", DBUS_TYPE_STRING, &str, generic_callback, str, free)) return; return bt_shell_noninteractive_quit(EXIT_FAILURE); } static void cmd_list_attributes(int argc, char *argv[]) { GDBusProxy *proxy; const char *path; if (argc > 1 && !strcmp(argv[1], "local")) { path = argv[1]; goto done; } proxy = find_device(argc, argv); if (!proxy) return bt_shell_noninteractive_quit(EXIT_FAILURE); path = g_dbus_proxy_get_path(proxy); done: gatt_list_attributes(path); return bt_shell_noninteractive_quit(EXIT_SUCCESS); } static void cmd_set_alias(int argc, char *argv[]) { char *name; if (!default_dev) { bt_shell_printf("No device connected\n"); return bt_shell_noninteractive_quit(EXIT_FAILURE); } name = g_strdup(argv[1]); if (g_dbus_proxy_set_property_basic(default_dev, "Alias", DBUS_TYPE_STRING, &name, generic_callback, name, g_free) == TRUE) return; g_free(name); return bt_shell_noninteractive_quit(EXIT_FAILURE); } static void set_default_local_attribute(char *attr) { char *desc = NULL; default_local_attr = attr; default_attr = NULL; desc = g_strdup_printf("[%s]> ", attr); bt_shell_set_prompt(desc, COLOR_BLUE); g_free(desc); } static void cmd_select_attribute(int argc, char *argv[]) { GDBusProxy *proxy; if (!strcasecmp("local", argv[1])) { char *attr; if (argc < 2) { bt_shell_printf("attribute/UUID required\n"); return bt_shell_noninteractive_quit(EXIT_FAILURE); } attr = gatt_select_local_attribute(argv[2]); if (!attr) { bt_shell_printf("Unable to find %s\n", argv[2]); return bt_shell_noninteractive_quit(EXIT_FAILURE); } set_default_local_attribute(attr); return bt_shell_noninteractive_quit(EXIT_SUCCESS); } if (!default_dev) { bt_shell_printf("No device connected\n"); return bt_shell_noninteractive_quit(EXIT_FAILURE); } proxy = gatt_select_attribute(default_attr, argv[1]); if (proxy) { set_default_attribute(proxy); return bt_shell_noninteractive_quit(EXIT_SUCCESS); } return bt_shell_noninteractive_quit(EXIT_FAILURE); } static struct GDBusProxy *find_attribute(int argc, char *argv[]) { GDBusProxy *proxy; if (argc < 2 || !strlen(argv[1])) { if (default_attr) return default_attr; bt_shell_printf("Missing attribute argument\n"); return NULL; } proxy = gatt_select_attribute(default_attr, argv[1]); if (!proxy) { bt_shell_printf("Attribute %s not available\n", argv[1]); return NULL; } return proxy; } static void cmd_attribute_info(int argc, char *argv[]) { GDBusProxy *proxy; DBusMessageIter iter; const char *iface, *uuid, *text; proxy = find_attribute(argc, argv); if (!proxy) return bt_shell_noninteractive_quit(EXIT_FAILURE); if (g_dbus_proxy_get_property(proxy, "UUID", &iter) == FALSE) return bt_shell_noninteractive_quit(EXIT_FAILURE); dbus_message_iter_get_basic(&iter, &uuid); text = bt_uuidstr_to_str(uuid); if (!text) text = g_dbus_proxy_get_path(proxy); iface = g_dbus_proxy_get_interface(proxy); if (!strcmp(iface, "org.bluez.GattService1")) { bt_shell_printf("Service - %s\n", text); print_property(proxy, "UUID"); print_property(proxy, "Primary"); print_property(proxy, "Characteristics"); print_property(proxy, "Includes"); } else if (!strcmp(iface, "org.bluez.GattCharacteristic1")) { bt_shell_printf("Characteristic - %s\n", text); print_property(proxy, "UUID"); print_property(proxy, "Service"); print_property(proxy, "Value"); print_property(proxy, "Notifying"); print_property(proxy, "Flags"); print_property(proxy, "MTU"); print_property(proxy, "Descriptors"); } else if (!strcmp(iface, "org.bluez.GattDescriptor1")) { bt_shell_printf("Descriptor - %s\n", text); print_property(proxy, "UUID"); print_property(proxy, "Characteristic"); print_property(proxy, "Value"); } return bt_shell_noninteractive_quit(EXIT_SUCCESS); } static void cmd_read(int argc, char *argv[]) { if (default_local_attr) { gatt_read_local_attribute(default_local_attr, argc, argv); return; } if (!default_attr) { bt_shell_printf("No attribute selected\n"); return bt_shell_noninteractive_quit(EXIT_FAILURE); } gatt_read_attribute(default_attr, argc, argv); } static void cmd_write(int argc, char *argv[]) { if (default_local_attr) { gatt_write_local_attribute(default_local_attr, argc, argv); return; } if (!default_attr) { bt_shell_printf("No attribute selected\n"); return bt_shell_noninteractive_quit(EXIT_FAILURE); } gatt_write_attribute(default_attr, argc, argv); } static void cmd_acquire_write(int argc, char *argv[]) { if (!default_attr) { bt_shell_printf("No attribute selected\n"); return bt_shell_noninteractive_quit(EXIT_FAILURE); } gatt_acquire_write(default_attr, argv[1]); } static void cmd_release_write(int argc, char *argv[]) { if (!default_attr) { bt_shell_printf("No attribute selected\n"); return bt_shell_noninteractive_quit(EXIT_FAILURE); } gatt_release_write(default_attr, argv[1]); } static void cmd_acquire_notify(int argc, char *argv[]) { if (!default_attr) { bt_shell_printf("No attribute selected\n"); return bt_shell_noninteractive_quit(EXIT_FAILURE); } gatt_acquire_notify(default_attr, argv[1]); } static void cmd_release_notify(int argc, char *argv[]) { if (!default_attr) { bt_shell_printf("No attribute selected\n"); return bt_shell_noninteractive_quit(EXIT_FAILURE); } gatt_release_notify(default_attr, argv[1]); } static void cmd_notify(int argc, char *argv[]) { dbus_bool_t enable; if (!parse_argument(argc, argv, NULL, NULL, &enable, NULL)) return bt_shell_noninteractive_quit(EXIT_FAILURE); if (!default_attr) { bt_shell_printf("No attribute selected\n"); return bt_shell_noninteractive_quit(EXIT_FAILURE); } gatt_notify_attribute(default_attr, enable ? true : false); } static void cmd_clone(int argc, char *argv[]) { GDBusProxy *proxy; proxy = default_attr ? default_attr : default_dev; if (!proxy) { bt_shell_printf("Not connected\n"); return bt_shell_noninteractive_quit(EXIT_FAILURE); } gatt_clone_attribute(proxy, argc, argv); } static void cmd_register_app(int argc, char *argv[]) { if (check_default_ctrl() == FALSE) return bt_shell_noninteractive_quit(EXIT_FAILURE); gatt_register_app(dbus_conn, default_ctrl->proxy, argc, argv); } static void cmd_unregister_app(int argc, char *argv[]) { if (check_default_ctrl() == FALSE) return bt_shell_noninteractive_quit(EXIT_FAILURE); gatt_unregister_app(dbus_conn, default_ctrl->proxy); } static void cmd_register_service(int argc, char *argv[]) { if (check_default_ctrl() == FALSE) return bt_shell_noninteractive_quit(EXIT_FAILURE); gatt_register_service(dbus_conn, default_ctrl->proxy, argc, argv); } static void cmd_register_includes(int argc, char *argv[]) { if (check_default_ctrl() == FALSE) return bt_shell_noninteractive_quit(EXIT_FAILURE); gatt_register_include(dbus_conn, default_ctrl->proxy, argc, argv); } static void cmd_unregister_includes(int argc, char *argv[]) { if (check_default_ctrl() == FALSE) return bt_shell_noninteractive_quit(EXIT_FAILURE); gatt_unregister_include(dbus_conn, default_ctrl->proxy, argc, argv); } static void cmd_unregister_service(int argc, char *argv[]) { if (check_default_ctrl() == FALSE) return bt_shell_noninteractive_quit(EXIT_FAILURE); gatt_unregister_service(dbus_conn, default_ctrl->proxy, argc, argv); } static void cmd_register_characteristic(int argc, char *argv[]) { if (check_default_ctrl() == FALSE) return bt_shell_noninteractive_quit(EXIT_FAILURE); gatt_register_chrc(dbus_conn, default_ctrl->proxy, argc, argv); } static void cmd_unregister_characteristic(int argc, char *argv[]) { if (check_default_ctrl() == FALSE) return bt_shell_noninteractive_quit(EXIT_FAILURE); gatt_unregister_chrc(dbus_conn, default_ctrl->proxy, argc, argv); } static void cmd_register_descriptor(int argc, char *argv[]) { if (check_default_ctrl() == FALSE) return bt_shell_noninteractive_quit(EXIT_FAILURE); gatt_register_desc(dbus_conn, default_ctrl->proxy, argc, argv); } static void cmd_unregister_descriptor(int argc, char *argv[]) { if (check_default_ctrl() == FALSE) return bt_shell_noninteractive_quit(EXIT_FAILURE); gatt_unregister_desc(dbus_conn, default_ctrl->proxy, argc, argv); } static char *generic_generator(const char *text, int state, GList *source, const char *property) { static int index, len; GList *list; if (!state) { index = 0; len = strlen(text); } for (list = g_list_nth(source, index); list; list = g_list_next(list)) { GDBusProxy *proxy = list->data; DBusMessageIter iter; const char *str; index++; if (!property) str = g_dbus_proxy_get_path(proxy); else if (g_dbus_proxy_get_property(proxy, property, &iter)) dbus_message_iter_get_basic(&iter, &str); else continue; if (!strncasecmp(str, text, len)) return strdup(str); } return NULL; } static char *ctrl_generator(const char *text, int state) { static int index = 0; static int len = 0; GList *list; if (!state) { index = 0; len = strlen(text); } for (list = g_list_nth(ctrl_list, index); list; list = g_list_next(list)) { struct adapter *adapter = list->data; DBusMessageIter iter; const char *str; index++; if (g_dbus_proxy_get_property(adapter->proxy, "Address", &iter) == FALSE) continue; dbus_message_iter_get_basic(&iter, &str); if (!strncasecmp(str, text, len)) return strdup(str); } return NULL; } static char *dev_generator(const char *text, int state) { return generic_generator(text, state, default_ctrl ? default_ctrl->devices : NULL, "Address"); } static char *set_generator(const char *text, int state) { return generic_generator(text, state, default_ctrl ? default_ctrl->sets : NULL, NULL); } static char *dev_set_generator(const char *text, int state) { char *str; str = dev_generator(text, state); if (str) return str; return set_generator(text, state); } static char *attribute_generator(const char *text, int state) { return gatt_attribute_generator(text, state); } static char *argument_generator(const char *text, int state, const char *args_list[]) { static int index, len; const char *arg; if (!state) { index = 0; len = strlen(text); } while ((arg = args_list[index])) { index++; if (!strncmp(arg, text, len)) return strdup(arg); } return NULL; } static char *capability_generator(const char *text, int state) { return argument_generator(text, state, agent_arguments); } static char *scan_generator(const char *text, int state) { return argument_generator(text, state, scan_arguments); } static void cmd_advertise(int argc, char *argv[]) { dbus_bool_t enable; const char *type; if (!parse_argument(argc, argv, ad_arguments, "type", &enable, &type)) return bt_shell_noninteractive_quit(EXIT_FAILURE); if (!default_ctrl || !default_ctrl->ad_proxy) { bt_shell_printf("LEAdvertisingManager not found\n"); return bt_shell_noninteractive_quit(EXIT_FAILURE); } if (enable == TRUE) ad_register(dbus_conn, default_ctrl->ad_proxy, type); else ad_unregister(dbus_conn, default_ctrl->ad_proxy); } static char *ad_generator(const char *text, int state) { return argument_generator(text, state, ad_arguments); } static void cmd_advertise_uuids(int argc, char *argv[]) { ad_advertise_uuids(dbus_conn, AD_TYPE_AD, argc, argv); } static void cmd_advertise_solicit(int argc, char *argv[]) { ad_advertise_solicit(dbus_conn, AD_TYPE_AD, argc, argv); } static void cmd_advertise_service(int argc, char *argv[]) { ad_advertise_service(dbus_conn, AD_TYPE_AD, argc, argv); } static void cmd_advertise_manufacturer(int argc, char *argv[]) { ad_advertise_manufacturer(dbus_conn, AD_TYPE_AD, argc, argv); } static void cmd_advertise_data(int argc, char *argv[]) { ad_advertise_data(dbus_conn, AD_TYPE_AD, argc, argv); } static void cmd_advertise_sr_uuids(int argc, char *argv[]) { ad_advertise_uuids(dbus_conn, AD_TYPE_SRD, argc, argv); } static void cmd_advertise_sr_solicit(int argc, char *argv[]) { ad_advertise_solicit(dbus_conn, AD_TYPE_SRD, argc, argv); } static void cmd_advertise_sr_service(int argc, char *argv[]) { ad_advertise_service(dbus_conn, AD_TYPE_SRD, argc, argv); } static void cmd_advertise_sr_manufacturer(int argc, char *argv[]) { ad_advertise_manufacturer(dbus_conn, AD_TYPE_SRD, argc, argv); } static void cmd_advertise_sr_data(int argc, char *argv[]) { ad_advertise_data(dbus_conn, AD_TYPE_SRD, argc, argv); } static void cmd_advertise_discoverable(int argc, char *argv[]) { dbus_bool_t discoverable; if (argc < 2) { ad_advertise_discoverable(dbus_conn, NULL); return; } if (!parse_argument(argc, argv, NULL, NULL, &discoverable, NULL)) return bt_shell_noninteractive_quit(EXIT_FAILURE); ad_advertise_discoverable(dbus_conn, &discoverable); } static void cmd_advertise_discoverable_timeout(int argc, char *argv[]) { long int value; char *endptr = NULL; if (argc < 2) { ad_advertise_discoverable_timeout(dbus_conn, NULL); return; } value = strtol(argv[1], &endptr, 0); if (!endptr || *endptr != '\0' || value > UINT16_MAX) { bt_shell_printf("Invalid argument\n"); return bt_shell_noninteractive_quit(EXIT_FAILURE); } ad_advertise_discoverable_timeout(dbus_conn, &value); } static void cmd_advertise_tx_power(int argc, char *argv[]) { dbus_bool_t powered; if (argc < 2) { ad_advertise_tx_power(dbus_conn, NULL); return; } if (!parse_argument(argc, argv, NULL, NULL, &powered, NULL)) return bt_shell_noninteractive_quit(EXIT_FAILURE); ad_advertise_tx_power(dbus_conn, &powered); } static void cmd_advertise_name(int argc, char *argv[]) { if (argc < 2) { ad_advertise_local_name(dbus_conn, NULL); return; } if (strcmp(argv[1], "on") == 0 || strcmp(argv[1], "yes") == 0) { ad_advertise_name(dbus_conn, true); return; } if (strcmp(argv[1], "off") == 0 || strcmp(argv[1], "no") == 0) { ad_advertise_name(dbus_conn, false); return; } ad_advertise_local_name(dbus_conn, argv[1]); } static void cmd_advertise_appearance(int argc, char *argv[]) { long int value; char *endptr = NULL; if (argc < 2) { ad_advertise_local_appearance(dbus_conn, NULL); return; } if (strcmp(argv[1], "on") == 0 || strcmp(argv[1], "yes") == 0) { ad_advertise_appearance(dbus_conn, true); return; } if (strcmp(argv[1], "off") == 0 || strcmp(argv[1], "no") == 0) { ad_advertise_appearance(dbus_conn, false); return; } value = strtol(argv[1], &endptr, 0); if (!endptr || *endptr != '\0' || value > UINT16_MAX) { bt_shell_printf("Invalid argument\n"); return bt_shell_noninteractive_quit(EXIT_FAILURE); } ad_advertise_local_appearance(dbus_conn, &value); } static void cmd_advertise_duration(int argc, char *argv[]) { long int value; char *endptr = NULL; if (argc < 2) { ad_advertise_duration(dbus_conn, NULL); return; } value = strtol(argv[1], &endptr, 0); if (!endptr || *endptr != '\0' || value > UINT16_MAX) { bt_shell_printf("Invalid argument\n"); return bt_shell_noninteractive_quit(EXIT_FAILURE); } ad_advertise_duration(dbus_conn, &value); } static void cmd_advertise_timeout(int argc, char *argv[]) { long int value; char *endptr = NULL; if (argc < 2) { ad_advertise_timeout(dbus_conn, NULL); return; } value = strtol(argv[1], &endptr, 0); if (!endptr || *endptr != '\0' || value > UINT16_MAX) { bt_shell_printf("Invalid argument\n"); return bt_shell_noninteractive_quit(EXIT_FAILURE); } ad_advertise_timeout(dbus_conn, &value); } static void cmd_advertise_secondary(int argc, char *argv[]) { if (argc < 2) { ad_advertise_secondary(dbus_conn, NULL); return; } ad_advertise_secondary(dbus_conn, argv[1]); } static void cmd_advertise_interval(int argc, char *argv[]) { uint32_t min, max; char *endptr = NULL; if (argc < 2) { ad_advertise_interval(dbus_conn, NULL, NULL); return; } min = strtol(argv[1], &endptr, 0); if (!endptr || *endptr != '\0' || min < 20 || min > 10485) { bt_shell_printf("Invalid argument\n"); return bt_shell_noninteractive_quit(EXIT_FAILURE); } max = min; if (argc > 2) { max = strtol(argv[2], &endptr, 0); if (!endptr || *endptr != '\0' || max < 20 || max > 10485) { bt_shell_printf("Invalid argument\n"); return bt_shell_noninteractive_quit(EXIT_FAILURE); } } if (min > max) { bt_shell_printf("Invalid argument: %u > %u\n", min, max); return bt_shell_noninteractive_quit(EXIT_FAILURE); } ad_advertise_interval(dbus_conn, &min, &max); } static void cmd_advertise_rsi(int argc, char *argv[]) { dbus_bool_t value; if (argc < 2) { ad_advertise_rsi(dbus_conn, NULL); return; } if (!parse_argument(argc, argv, NULL, NULL, &value, NULL)) return bt_shell_noninteractive_quit(EXIT_FAILURE); ad_advertise_rsi(dbus_conn, &value); } static void ad_clear_uuids(void) { ad_disable_uuids(dbus_conn, AD_TYPE_AD); } static void ad_clear_solicit(void) { ad_disable_solicit(dbus_conn, AD_TYPE_AD); } static void ad_clear_service(void) { ad_disable_service(dbus_conn, AD_TYPE_AD); } static void ad_clear_manufacturer(void) { ad_disable_manufacturer(dbus_conn, AD_TYPE_AD); } static void ad_clear_data(void) { ad_disable_data(dbus_conn, AD_TYPE_AD); } static void ad_clear_sr_uuids(void) { ad_disable_uuids(dbus_conn, AD_TYPE_SRD); } static void ad_clear_sr_solicit(void) { ad_disable_solicit(dbus_conn, AD_TYPE_SRD); } static void ad_clear_sr_service(void) { ad_disable_service(dbus_conn, AD_TYPE_SRD); } static void ad_clear_sr_manufacturer(void) { ad_disable_manufacturer(dbus_conn, AD_TYPE_SRD); } static void ad_clear_sr_data(void) { ad_disable_data(dbus_conn, AD_TYPE_SRD); } static void ad_clear_tx_power(void) { dbus_bool_t powered = false; ad_advertise_tx_power(dbus_conn, &powered); } static void ad_clear_name(void) { ad_advertise_name(dbus_conn, false); } static void ad_clear_appearance(void) { ad_advertise_appearance(dbus_conn, false); } static void ad_clear_duration(void) { long int value = 0; ad_advertise_duration(dbus_conn, &value); } static void ad_clear_timeout(void) { long int value = 0; ad_advertise_timeout(dbus_conn, &value); } static void ad_clear_secondary(void) { const char *value = ""; ad_advertise_secondary(dbus_conn, value); } static void ad_clear_interval(void) { uint32_t min = 0; uint32_t max = 0; ad_advertise_interval(dbus_conn, &min, &max); } static const struct clear_entry ad_clear[] = { { "uuids", ad_clear_uuids }, { "solicit", ad_clear_solicit }, { "service", ad_clear_service }, { "manufacturer", ad_clear_manufacturer }, { "data", ad_clear_data }, { "sr-uuids", ad_clear_sr_uuids }, { "sr-solicit", ad_clear_sr_solicit }, { "sr-service", ad_clear_sr_service }, { "sr-manufacturer", ad_clear_sr_manufacturer }, { "sr-data", ad_clear_sr_data }, { "tx-power", ad_clear_tx_power }, { "name", ad_clear_name }, { "appearance", ad_clear_appearance }, { "duration", ad_clear_duration }, { "timeout", ad_clear_timeout }, { "secondary", ad_clear_secondary }, { "interval", ad_clear_interval }, {} }; static void cmd_ad_clear(int argc, char *argv[]) { bool all = false; if (argc < 2 || !strlen(argv[1])) all = true; if(!data_clear(ad_clear, all ? "all" : argv[1])) return bt_shell_noninteractive_quit(EXIT_FAILURE); } static void print_add_or_pattern_usage(void) { bt_shell_printf("pattern format:\n" "\t \n"); bt_shell_printf("e.g.\n" "\tadd-or-pattern 1 2 01ab55 3 4 23cd66\n"); } static void cmd_adv_monitor_print_usage(int argc, char *argv[]) { if (strcmp(argv[1], "add-or-pattern") == 0) print_add_or_pattern_usage(); else bt_shell_printf("Invalid argument %s", argv[1]); } static void cmd_adv_monitor_set_rssi_threshold(int argc, char *argv[]) { int low_threshold, high_threshold; low_threshold = atoi(argv[1]); high_threshold = atoi(argv[2]); adv_monitor_set_rssi_threshold(low_threshold, high_threshold); } static void cmd_adv_monitor_set_rssi_timeout(int argc, char *argv[]) { int low_timeout, high_timeout; low_timeout = atoi(argv[1]); high_timeout = atoi(argv[2]); adv_monitor_set_rssi_timeout(low_timeout, high_timeout); } static void cmd_adv_monitor_set_rssi_sampling_period(int argc, char *argv[]) { int sampling = atoi(argv[1]); adv_monitor_set_rssi_sampling_period(sampling); } static void cmd_adv_monitor_add_or_monitor(int argc, char *argv[]) { adv_monitor_add_monitor(dbus_conn, "or_patterns", argc, argv); } static void cmd_adv_monitor_print_monitor(int argc, char *argv[]) { int monitor_idx; if (strcmp(argv[1], "all") == 0) monitor_idx = -1; else monitor_idx = atoi(argv[1]); adv_monitor_print_monitor(dbus_conn, monitor_idx); } static void cmd_adv_monitor_remove_monitor(int argc, char *argv[]) { int monitor_idx; if (strcmp(argv[1], "all") == 0) monitor_idx = -1; else monitor_idx = atoi(argv[1]); adv_monitor_remove_monitor(dbus_conn, monitor_idx); } static void cmd_adv_monitor_get_supported_info(int argc, char *argv[]) { adv_monitor_get_supported_info(); } static const struct bt_shell_menu advertise_menu = { .name = "advertise", .desc = "Advertise Options Submenu", .entries = { { "uuids", "[uuid1 uuid2 ...]", cmd_advertise_uuids, "Set/Get advertise uuids" }, { "solicit", "[uuid1 uuid2 ...]", cmd_advertise_solicit, "Set/Get advertise solicit uuids" }, { "service", "[uuid] [data=xx xx ...]", cmd_advertise_service, "Set/Get advertise service data" }, { "manufacturer", "[id] [data=xx xx ...]", cmd_advertise_manufacturer, "Set/Get advertise manufacturer data" }, { "data", "[type] [data=xx xx ...]", cmd_advertise_data, "Set/Get advertise data" }, { "sr-uuids", "[uuid1 uuid2 ...]", cmd_advertise_sr_uuids, "Set/Get scan response uuids" }, { "sr-solicit", "[uuid1 uuid2 ...]", cmd_advertise_sr_solicit, "Set/Get scan response solicit uuids" }, { "sr-service", "[uuid] [data=xx xx ...]", cmd_advertise_sr_service, "Set/Get scan response service data" }, { "sr-manufacturer", "[id] [data=xx xx ...]", cmd_advertise_sr_manufacturer, "Set/Get scan response manufacturer data" }, { "sr-data", "[type] [data=xx xx ...]", cmd_advertise_sr_data, "Set/Get scan response data" }, { "discoverable", "[on/off]", cmd_advertise_discoverable, "Set/Get advertise discoverable" }, { "discoverable-timeout", "[seconds]", cmd_advertise_discoverable_timeout, "Set/Get advertise discoverable timeout" }, { "tx-power", "[on/off]", cmd_advertise_tx_power, "Show/Enable/Disable TX power to be advertised", NULL }, { "name", "[on/off/name]", cmd_advertise_name, "Configure local name to be advertised" }, { "appearance", "[on/off/value]", cmd_advertise_appearance, "Configure custom appearance to be advertised" }, { "duration", "[seconds]", cmd_advertise_duration, "Set/Get advertise duration" }, { "timeout", "[seconds]", cmd_advertise_timeout, "Set/Get advertise timeout" }, { "secondary", "[1M/2M/Coded]", cmd_advertise_secondary, "Set/Get advertise secondary channel" }, { "interval", "[min] [max] ", cmd_advertise_interval, "Set/Get advertise interval range" }, { "rsi", "[on/off]", cmd_advertise_rsi, "Show/Enable/Disable RSI to be advertised", NULL }, { "clear", "[uuids/service/manufacturer/config-name...]", cmd_ad_clear, "Clear advertise config" }, { } }, }; static const struct bt_shell_menu advertise_monitor_menu = { .name = "monitor", .desc = "Advertisement Monitor Options Submenu", .entries = { { "set-rssi-threshold", " ", cmd_adv_monitor_set_rssi_threshold, "Set RSSI threshold parameter" }, { "set-rssi-timeout", " ", cmd_adv_monitor_set_rssi_timeout, "Set RSSI timeout parameter" }, { "set-rssi-sampling-period", "", cmd_adv_monitor_set_rssi_sampling_period, "Set RSSI sampling period parameter" }, { "add-or-pattern", "[patterns=pattern1 pattern2 ...]", cmd_adv_monitor_add_or_monitor, "Register 'or pattern' type monitor with the " "specified RSSI parameters" }, { "get-pattern", "", cmd_adv_monitor_print_monitor, "Get advertisement monitor" }, { "remove-pattern", "", cmd_adv_monitor_remove_monitor, "Remove advertisement monitor" }, { "get-supported-info", NULL, cmd_adv_monitor_get_supported_info, "Get advertisement manager supported " "features and supported monitor types" }, { "print-usage", "", cmd_adv_monitor_print_usage, "Print the command usage"}, { } }, }; static const struct bt_shell_menu scan_menu = { .name = "scan", .desc = "Scan Options Submenu", .entries = { { "uuids", "[all/uuid1 uuid2 ...]", cmd_scan_filter_uuids, "Set/Get UUIDs filter" }, { "rssi", "[rssi]", cmd_scan_filter_rssi, "Set/Get RSSI filter, and clears pathloss" }, { "pathloss", "[pathloss]", cmd_scan_filter_pathloss, "Set/Get Pathloss filter, and clears RSSI" }, { "transport", "[transport]", cmd_scan_filter_transport, "Set/Get transport filter" }, { "duplicate-data", "[on/off]", cmd_scan_filter_duplicate_data, "Set/Get duplicate data filter", NULL }, { "discoverable", "[on/off]", cmd_scan_filter_discoverable, "Set/Get discoverable filter", NULL }, { "pattern", "[value]", cmd_scan_filter_pattern, "Set/Get pattern filter", NULL }, { "clear", "[uuids/rssi/pathloss/transport/duplicate-data/discoverable/pattern]", cmd_scan_filter_clear, "Clears discovery filter.", filter_clear_generator }, { } }, }; static const struct bt_shell_menu gatt_menu = { .name = "gatt", .desc = "Generic Attribute Submenu", .entries = { { "list-attributes", "[dev/local]", cmd_list_attributes, "List attributes", dev_generator }, { "select-attribute", " [attribute/UUID]", cmd_select_attribute, "Select attribute", attribute_generator }, { "attribute-info", "[attribute/UUID]", cmd_attribute_info, "Select attribute", attribute_generator }, { "read", "[offset]", cmd_read, "Read attribute value" }, { "write", " [offset] [type]", cmd_write, "Write attribute value" }, { "acquire-write", NULL, cmd_acquire_write, "Acquire Write file descriptor" }, { "release-write", NULL, cmd_release_write, "Release Write file descriptor" }, { "acquire-notify", NULL, cmd_acquire_notify, "Acquire Notify file descriptor" }, { "release-notify", NULL, cmd_release_notify, "Release Notify file descriptor" }, { "notify", "", cmd_notify, "Notify attribute value", NULL }, { "clone", "[dev/attribute/UUID]", cmd_clone, "Clone a device or attribute" }, { "register-application", "[UUID ...]", cmd_register_app, "Register profile to connect" }, { "unregister-application", NULL, cmd_unregister_app, "Unregister profile" }, { "register-service", " [handle]", cmd_register_service, "Register application service." }, { "unregister-service", "", cmd_unregister_service, "Unregister application service" }, { "register-includes", " [handle]", cmd_register_includes, "Register as Included service in." }, { "unregister-includes", " ", cmd_unregister_includes, "Unregister Included service." }, { "register-characteristic", " [handle]", cmd_register_characteristic, "Register application characteristic" }, { "unregister-characteristic", "", cmd_unregister_characteristic, "Unregister application characteristic" }, { "register-descriptor", " [handle]", cmd_register_descriptor, "Register application descriptor" }, { "unregister-descriptor", "", cmd_unregister_descriptor, "Unregister application descriptor" }, { } }, }; static const struct bt_shell_menu main_menu = { .name = "main", .entries = { { "list", NULL, cmd_list, "List available controllers" }, { "show", "[ctrl]", cmd_show, "Controller information", ctrl_generator }, { "select", "", cmd_select, "Select default controller", ctrl_generator }, { "devices", "[Paired/Bonded/Trusted/Connected]", cmd_devices, "List available devices, with an " "optional property as the filter" }, { "system-alias", "", cmd_system_alias, "Set controller alias" }, { "reset-alias", NULL, cmd_reset_alias, "Reset controller alias" }, { "power", "", cmd_power, "Set controller power", NULL }, { "pairable", "", cmd_pairable, "Set controller pairable mode", NULL }, { "discoverable", "", cmd_discoverable, "Set controller discoverable mode", NULL }, { "discoverable-timeout", "[value]", cmd_discoverable_timeout, "Set discoverable timeout", NULL }, { "agent", "", cmd_agent, "Enable/disable agent with given capability", capability_generator}, { "default-agent",NULL, cmd_default_agent, "Set agent as the default one" }, { "advertise", "", cmd_advertise, "Enable/disable advertising with given type", ad_generator}, { "set-alias", "", cmd_set_alias, "Set device alias" }, { "scan", "", cmd_scan, "Scan for devices", scan_generator }, { "info", "[dev/set]", cmd_info, "Device/Set information", dev_set_generator }, { "pair", "[dev]", cmd_pair, "Pair with device", dev_generator }, { "cancel-pairing", "[dev]", cmd_cancel_pairing, "Cancel pairing with device", dev_generator }, { "trust", "[dev]", cmd_trust, "Trust device", dev_generator }, { "untrust", "[dev]", cmd_untrust, "Untrust device", dev_generator }, { "block", "[dev]", cmd_block, "Block device", dev_generator }, { "unblock", "[dev]", cmd_unblock, "Unblock device", dev_generator }, { "remove", "", cmd_remove, "Remove device", dev_generator }, { "connect", " [uuid]", cmd_connect, "Connect a device and all its profiles or " "optionally connect a single profile only", dev_generator }, { "disconnect", "[dev] [uuid]", cmd_disconn, "Disconnect a device or optionally disconnect " "a single profile only", dev_generator }, { "wake", "[dev] [on/off]", cmd_wake, "Get/Set wake support", dev_generator }, { "bearer", " [last-seen/bredr/le]", cmd_bearer, "Get/Set preferred bearer", dev_generator }, { } }, }; static const struct option options[] = { { "agent", required_argument, 0, 'a' }, { "endpoints", no_argument, 0, 'e' }, { 0, 0, 0, 0 } }; static const char *agent_option; static const char *endpoint_option; static const char **optargs[] = { &agent_option, &endpoint_option }; static const char *help[] = { "Register agent handler: ", "Register Media endpoints" }; static const struct bt_shell_opt opt = { .options = options, .optno = sizeof(options) / sizeof(struct option), .optstr = "a:e", .optarg = optargs, .help = help, }; static void client_ready(GDBusClient *client, void *user_data) { unsigned int *timeout_id = user_data; if (*timeout_id > 0) timeout_remove(*timeout_id); setup_standard_input(); } static bool timeout_quit(void *user_data) { mainloop_exit_failure(); return true; } int main(int argc, char *argv[]) { GDBusClient *client; int status; int timeout; unsigned int timeout_id; if (!argsisutf8(argc, argv)) return -EINVAL; bt_shell_init(argc, argv, &opt); bt_shell_set_menu(&main_menu); bt_shell_add_submenu(&advertise_menu); bt_shell_add_submenu(&advertise_monitor_menu); bt_shell_add_submenu(&scan_menu); bt_shell_add_submenu(&gatt_menu); bt_shell_set_prompt(PROMPT_OFF, NULL); if (agent_option) auto_register_agent = g_strdup(agent_option); else auto_register_agent = g_strdup(""); dbus_conn = g_dbus_setup_bus(DBUS_BUS_SYSTEM, NULL, NULL); g_dbus_attach_object_manager(dbus_conn); bt_shell_set_env("DBUS_CONNECTION", dbus_conn); if (endpoint_option) bt_shell_set_env("AUTO_REGISTER_ENDPOINT", (void *)endpoint_option); admin_add_submenu(); player_add_submenu(); mgmt_add_submenu(); assistant_add_submenu(); hci_add_submenu(); client = g_dbus_client_new(dbus_conn, "org.bluez", "/org/bluez"); g_dbus_client_set_connect_watch(client, connect_handler, NULL); g_dbus_client_set_disconnect_watch(client, disconnect_handler, NULL); g_dbus_client_set_signal_watch(client, message_handler, NULL); g_dbus_client_set_proxy_handlers(client, proxy_added, proxy_removed, property_changed, NULL); timeout = bt_shell_get_timeout(); timeout_id = 0; if (timeout > 0) timeout_id = timeout_add(timeout * 1000, timeout_quit, NULL, NULL); g_dbus_client_set_ready_watch(client, client_ready, &timeout_id); status = bt_shell_run(); admin_remove_submenu(); player_remove_submenu(); mgmt_remove_submenu(); assistant_remove_submenu(); hci_remove_submenu(); g_dbus_client_unref(client); dbus_connection_unref(dbus_conn); g_list_free_full(ctrl_list, proxy_leak); g_free(auto_register_agent); return status; } bluez-5.82/client/PaxHeaders/bluetoothctl-player.rst0000644000000000000000000000005014766002272017666 xustar0020 atime=1743515577 20 ctime=1743591291 bluez-5.82/client/bluetoothctl-player.rst0000644000000000000000000000355014766002272017352 0ustar00rootroot=================== bluetoothctl-player =================== -------------------- Media Player Submenu -------------------- :Version: BlueZ :Copyright: Free use of this software is granted under the terms of the GNU Lesser General Public Licenses (LGPL). :Date: November 2022 :Manual section: 1 :Manual group: Linux System Administration SYNOPSIS ======== **bluetoothctl** [--options] [player.commands] Media Player Commands ===================== list ---- List available players. :Usage: **> list** show ---- Show player information. :Usage: **> show [player]** select ------ Select default player. :Usage: **> select ** play ---- Start playback. :Usage: **> play [item]** pause ----- Pause playback. :Usage: **> pause** stop ---- Stop playback. :Usage: **> stop** next ---- Jump to next item. :Usage: **> next** previous -------- Jump to previous item. :Usage: **> previous** fast-forward ------------ Fast forward playback. :Usage: **> fast-forward** rewind ------ Rewind playback. :Usage: **> rewind** equalizer --------- Enable/Disable equalizer. :Usage: **> equalizer ** repeat ------ Set repeat mode. :Usage: **> repeat ** shuffle ------- Set shuffle mode. :Usage: **> shuffle ** scan ---- Set scan mode. :Usage: **> scan ** change-folder ------------- Change current folder. :Usage: **> change-folder ** list-items ---------- List items of current folder. :Usage: **> list-items [start] [end]** search ------ Search items containing string. :Usage: **> search ** queue ----- Add item to playlist queue. :Usage: **> queue ** show-item --------- Show item information. :Usage: **> show-item ** RESOURCES ========= http://www.bluez.org REPORTING BUGS ============== linux-bluetooth@vger.kernel.org bluez-5.82/client/PaxHeaders/mgmt.c0000644000000000000000000000005014766002272014242 xustar0020 atime=1743515577 20 ctime=1743591281 bluez-5.82/client/mgmt.c0000644000000000000000000044602614766002272013737 0ustar00rootroot// SPDX-License-Identifier: GPL-2.0-or-later /* * BlueZ - Bluetooth protocol stack for Linux * * Copyright (C) 2011 Intel Corporation. All rights reserved. * */ #ifdef HAVE_CONFIG_H #include #endif #define _GNU_SOURCE #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "lib/bluetooth.h" #include "lib/hci.h" #include "lib/hci_lib.h" #include "lib/sdp.h" #include "lib/sdp_lib.h" #include "lib/uuid.h" #include "src/uuid-helper.h" #include "lib/mgmt.h" #include "src/shared/mainloop.h" #include "src/shared/io.h" #include "src/shared/util.h" #include "src/shared/mgmt.h" #include "src/shared/shell.h" #include "client/mgmt.h" #define SCAN_TYPE_BREDR (1 << BDADDR_BREDR) #define SCAN_TYPE_LE ((1 << BDADDR_LE_PUBLIC) | (1 << BDADDR_LE_RANDOM)) #define SCAN_TYPE_DUAL (SCAN_TYPE_BREDR | SCAN_TYPE_LE) static struct mgmt *mgmt = NULL; static uint16_t mgmt_index = MGMT_INDEX_NONE; static bool discovery = false; static bool resolve_names = true; static struct { uint16_t index; uint16_t req; struct mgmt_addr_info addr; } prompt = { .index = MGMT_INDEX_NONE, }; static int pending_index = 0; #ifndef MIN #define MIN(x, y) ((x) < (y) ? (x) : (y)) #endif #define PROMPT_ON COLOR_BLUE "[mgmt]" COLOR_OFF "> " static void update_prompt(uint16_t index) { char str[32]; if (index == MGMT_INDEX_NONE) snprintf(str, sizeof(str), "[mgmt]> "); else snprintf(str, sizeof(str), "[hci%u]> ", index); bt_shell_set_prompt(str, COLOR_BLUE); } void mgmt_set_index(const char *arg) { if (!arg || !strcmp(arg, "none") || !strcmp(arg, "any") || !strcmp(arg, "all")) mgmt_index = MGMT_INDEX_NONE; else if (strlen(arg) > 3 && !strncasecmp(arg, "hci", 3)) mgmt_index = atoi(&arg[3]); else mgmt_index = atoi(arg); update_prompt(mgmt_index); } static bool parse_setting(int argc, char **argv, uint8_t *val) { if (strcasecmp(argv[1], "on") == 0 || strcasecmp(argv[1], "yes") == 0) *val = 1; else if (strcasecmp(argv[1], "off") == 0) *val = 0; else *val = atoi(argv[1]); return true; } #define print(fmt, arg...) do { \ bt_shell_printf(fmt "\n", ## arg); \ } while (0) #define error(fmt, arg...) do { \ bt_shell_printf(COLOR_RED fmt "\n" COLOR_OFF, ## arg); \ } while (0) static size_t hex2bin(const char *hexstr, uint8_t *buf, size_t buflen) { size_t i, len; len = MIN((strlen(hexstr) / 2), buflen); memset(buf, 0, len); for (i = 0; i < len; i++) sscanf(hexstr + (i * 2), "%02hhX", &buf[i]); return len; } static size_t bin2hex(const uint8_t *buf, size_t buflen, char *str, size_t strlen) { size_t i; for (i = 0; i < buflen && i < (strlen / 2); i++) sprintf(str + (i * 2), "%02x", buf[i]); return i; } static void print_eir(const uint8_t *eir, uint16_t eir_len) { uint16_t parsed = 0; char str[33]; while (parsed < eir_len - 1) { uint8_t field_len = eir[0]; if (field_len == 0) break; parsed += field_len + 1; if (parsed > eir_len) break; switch (eir[1]) { case 0x01: print("Flags: 0x%02x", eir[2]); break; case 0x0d: print("Class of Device: 0x%02x%02x%02x", eir[4], eir[3], eir[2]); break; case 0x0e: bin2hex(eir + 2, 16, str, sizeof(str)); print("SSP Hash C-192: %s", str); break; case 0x0f: bin2hex(eir + 2, 16, str, sizeof(str)); print("SSP Rand R-192: %s", str); break; case 0x1b: ba2str((bdaddr_t *) (eir + 2), str); print("LE Device Address: %s (%s)", str, eir[8] ? "random" : "public"); break; case 0x1c: print("LE Role: 0x%02x", eir[2]); break; case 0x1d: bin2hex(eir + 2, 16, str, sizeof(str)); print("SSP Hash C-256: %s", str); break; case 0x1e: bin2hex(eir + 2, 16, str, sizeof(str)); print("SSP Rand R-256: %s", str); break; case 0x22: bin2hex(eir + 2, 16, str, sizeof(str)); print("LE SC Confirmation Value: %s", str); break; case 0x23: bin2hex(eir + 2, 16, str, sizeof(str)); print("LE SC Random Value: %s", str); break; default: print("Type %u: %u byte%s", eir[1], field_len - 1, (field_len - 1) == 1 ? "" : "s"); break; } eir += field_len + 1; } } static bool load_identity(const char *path, struct mgmt_irk_info *irk) { char *addr, *key; unsigned int type; int n; FILE *fp; fp = fopen(path, "r"); if (!fp) { error("Failed to open identity file: %s", strerror(errno)); return false; } n = fscanf(fp, "%m[0-9a-f:] (type %u) %m[0-9a-f]", &addr, &type, &key); fclose(fp); if (n != 3) return false; str2ba(addr, &irk->addr.bdaddr); hex2bin(key, irk->val, sizeof(irk->val)); free(addr); free(key); switch (type) { case 0: irk->addr.type = BDADDR_LE_PUBLIC; break; case 1: irk->addr.type = BDADDR_LE_RANDOM; break; default: error("Invalid address type %u", type); return false; } return true; } static void controller_error(uint16_t index, uint16_t len, const void *param, void *user_data) { const struct mgmt_ev_controller_error *ev = param; if (len < sizeof(*ev)) { error("Too short (%u bytes) controller error event", len); return; } print("hci%u error 0x%02x", index, ev->error_code); } static void index_added(uint16_t index, uint16_t len, const void *param, void *user_data) { print("hci%u added", index); } static void index_removed(uint16_t index, uint16_t len, const void *param, void *user_data) { print("hci%u removed", index); } static void unconf_index_added(uint16_t index, uint16_t len, const void *param, void *user_data) { print("hci%u added (unconfigured)", index); } static void unconf_index_removed(uint16_t index, uint16_t len, const void *param, void *user_data) { print("hci%u removed (unconfigured)", index); } static void ext_index_added(uint16_t index, uint16_t len, const void *param, void *user_data) { const struct mgmt_ev_ext_index_added *ev = param; print("hci%u added (type %u bus %u)", index, ev->type, ev->bus); } static void ext_index_removed(uint16_t index, uint16_t len, const void *param, void *user_data) { const struct mgmt_ev_ext_index_removed *ev = param; print("hci%u removed (type %u bus %u)", index, ev->type, ev->bus); } static const char *options_str[] = { "external", "public-address", }; static const char *options2str(uint32_t options) { static char str[256]; unsigned i; int off; off = 0; str[0] = '\0'; for (i = 0; i < NELEM(options_str); i++) { if ((options & (1 << i)) != 0) off += snprintf(str + off, sizeof(str) - off, "%s ", options_str[i]); } return str; } static void new_config_options(uint16_t index, uint16_t len, const void *param, void *user_data) { const uint32_t *ev = param; if (len < sizeof(*ev)) { error("Too short new_config_options event (%u)", len); return; } print("hci%u new_config_options: %s", index, options2str(get_le32(ev))); } static const char *settings_str[] = { "powered", "connectable", "fast-connectable", "discoverable", "bondable", "link-security", "ssp", "br/edr", "hs", "le", "advertising", "secure-conn", "debug-keys", "privacy", "configuration", "static-addr", "phy-configuration", "wide-band-speech", "cis-central", "cis-peripheral", "iso-broadcaster", "sync-receiver" }; static const char *settings2str(uint32_t settings) { static char str[256]; unsigned i; int off; off = 0; str[0] = '\0'; for (i = 0; i < NELEM(settings_str); i++) { if ((settings & (1 << i)) != 0) off += snprintf(str + off, sizeof(str) - off, "%s ", settings_str[i]); } return str; } static void new_settings(uint16_t index, uint16_t len, const void *param, void *user_data) { const uint32_t *ev = param; if (len < sizeof(*ev)) { error("Too short new_settings event (%u)", len); return; } print("hci%u new_settings: %s", index, settings2str(get_le32(ev))); } static void discovering(uint16_t index, uint16_t len, const void *param, void *user_data) { const struct mgmt_ev_discovering *ev = param; if (len < sizeof(*ev)) { error("Too short (%u bytes) discovering event", len); return; } print("hci%u type %u discovering %s", index, ev->type, ev->discovering ? "on" : "off"); if (ev->discovering == 0 && discovery) return bt_shell_noninteractive_quit(EXIT_SUCCESS); } static void new_link_key(uint16_t index, uint16_t len, const void *param, void *user_data) { const struct mgmt_ev_new_link_key *ev = param; char addr[18]; if (len != sizeof(*ev)) { error("Invalid new_link_key length (%u bytes)", len); return; } ba2str(&ev->key.addr.bdaddr, addr); print("hci%u new_link_key %s type 0x%02x pin_len %d store_hint %u", index, addr, ev->key.type, ev->key.pin_len, ev->store_hint); } static const char *typestr(uint8_t type) { static const char *str[] = { "BR/EDR", "LE Public", "LE Random" }; if (type <= BDADDR_LE_RANDOM) return str[type]; return "(unknown)"; } static void connected(uint16_t index, uint16_t len, const void *param, void *user_data) { const struct mgmt_ev_device_connected *ev = param; uint16_t eir_len; char addr[18]; if (len < sizeof(*ev)) { error("Invalid connected event length (%u bytes)", len); return; } eir_len = get_le16(&ev->eir_len); if (len != sizeof(*ev) + eir_len) { error("Invalid connected event length (%u != eir_len %u)", len, eir_len); return; } ba2str(&ev->addr.bdaddr, addr); print("hci%u %s type %s connected eir_len %u", index, addr, typestr(ev->addr.type), eir_len); } static void disconnected(uint16_t index, uint16_t len, const void *param, void *user_data) { const struct mgmt_ev_device_disconnected *ev = param; char addr[18]; uint8_t reason; if (len < sizeof(struct mgmt_addr_info)) { error("Invalid disconnected event length (%u bytes)", len); return; } if (len < sizeof(*ev)) reason = MGMT_DEV_DISCONN_UNKNOWN; else reason = ev->reason; ba2str(&ev->addr.bdaddr, addr); print("hci%u %s type %s disconnected with reason %u", index, addr, typestr(ev->addr.type), reason); } static void conn_failed(uint16_t index, uint16_t len, const void *param, void *user_data) { const struct mgmt_ev_connect_failed *ev = param; char addr[18]; if (len != sizeof(*ev)) { error("Invalid connect_failed event length (%u bytes)", len); return; } ba2str(&ev->addr.bdaddr, addr); print("hci%u %s type %s connect failed (status 0x%02x, %s)", index, addr, typestr(ev->addr.type), ev->status, mgmt_errstr(ev->status)); } static void auth_failed(uint16_t index, uint16_t len, const void *param, void *user_data) { const struct mgmt_ev_auth_failed *ev = param; char addr[18]; if (len != sizeof(*ev)) { error("Invalid auth_failed event length (%u bytes)", len); return; } ba2str(&ev->addr.bdaddr, addr); print("hci%u %s auth failed with status 0x%02x (%s)", index, addr, ev->status, mgmt_errstr(ev->status)); } static void class_of_dev_changed(uint16_t index, uint16_t len, const void *param, void *user_data) { const struct mgmt_ev_class_of_dev_changed *ev = param; if (len != sizeof(*ev)) { error("Invalid class_of_dev_changed length (%u bytes)", len); return; } print("hci%u class of device changed: 0x%02x%02x%02x", index, ev->dev_class[2], ev->dev_class[1], ev->dev_class[0]); } static void local_name_changed(uint16_t index, uint16_t len, const void *param, void *user_data) { const struct mgmt_ev_local_name_changed *ev = param; if (len != sizeof(*ev)) { error("Invalid local_name_changed length (%u bytes)", len); return; } print("hci%u name changed: %s", index, ev->name); } static void confirm_name_rsp(uint8_t status, uint16_t len, const void *param, void *user_data) { const struct mgmt_rp_confirm_name *rp = param; char addr[18]; if (len == 0 && status != 0) { error("confirm_name failed with status 0x%02x (%s)", status, mgmt_errstr(status)); return; } if (len != sizeof(*rp)) { error("confirm_name rsp length %u instead of %zu", len, sizeof(*rp)); return; } ba2str(&rp->addr.bdaddr, addr); if (status != 0) error("confirm_name for %s failed: 0x%02x (%s)", addr, status, mgmt_errstr(status)); else print("confirm_name succeeded for %s", addr); } static char *eir_get_name(const uint8_t *eir, uint16_t eir_len) { uint8_t parsed = 0; if (eir_len < 2) return NULL; while (parsed < eir_len - 1) { uint8_t field_len = eir[0]; if (field_len == 0) break; parsed += field_len + 1; if (parsed > eir_len) break; /* Check for short of complete name */ if (eir[1] == 0x09 || eir[1] == 0x08) return strndup((char *) &eir[2], field_len - 1); eir += field_len + 1; } return NULL; } static unsigned int eir_get_flags(const uint8_t *eir, uint16_t eir_len) { uint8_t parsed = 0; if (eir_len < 2) return 0; while (parsed < eir_len - 1) { uint8_t field_len = eir[0]; if (field_len == 0) break; parsed += field_len + 1; if (parsed > eir_len) break; /* Check for flags */ if (eir[1] == 0x01) return eir[2]; eir += field_len + 1; } return 0; } static void device_found(uint16_t index, uint16_t len, const void *param, void *user_data) { const struct mgmt_ev_device_found *ev = param; struct mgmt *mgmt = user_data; uint16_t eir_len; uint32_t flags; if (len < sizeof(*ev)) { error("Too short device_found length (%u bytes)", len); return; } flags = btohl(ev->flags); eir_len = get_le16(&ev->eir_len); if (len != sizeof(*ev) + eir_len) { error("dev_found: expected %zu bytes, got %u bytes", sizeof(*ev) + eir_len, len); return; } if (discovery) { char addr[18], *name; ba2str(&ev->addr.bdaddr, addr); print("hci%u dev_found: %s type %s rssi %d " "flags 0x%04x ", index, addr, typestr(ev->addr.type), ev->rssi, flags); if (ev->addr.type != BDADDR_BREDR) print("AD flags 0x%02x ", eir_get_flags(ev->eir, eir_len)); name = eir_get_name(ev->eir, eir_len); if (name) print("name %s", name); else print("eir_len %u", eir_len); free(name); } if (discovery && (flags & MGMT_DEV_FOUND_CONFIRM_NAME)) { struct mgmt_cp_confirm_name cp; memset(&cp, 0, sizeof(cp)); memcpy(&cp.addr, &ev->addr, sizeof(cp.addr)); if (resolve_names) cp.name_known = 0; else cp.name_known = 1; mgmt_reply(mgmt, MGMT_OP_CONFIRM_NAME, index, sizeof(cp), &cp, confirm_name_rsp, NULL, NULL); } } static void pin_rsp(uint8_t status, uint16_t len, const void *param, void *user_data) { if (status != 0) { error("PIN Code reply failed with status 0x%02x (%s)", status, mgmt_errstr(status)); return bt_shell_noninteractive_quit(EXIT_FAILURE); } print("PIN Reply successful"); } static int mgmt_pin_reply(uint16_t index, const struct mgmt_addr_info *addr, const char *pin, size_t len) { struct mgmt_cp_pin_code_reply cp; memset(&cp, 0, sizeof(cp)); memcpy(&cp.addr, addr, sizeof(cp.addr)); cp.pin_len = len; memcpy(cp.pin_code, pin, len); return mgmt_reply(mgmt, MGMT_OP_PIN_CODE_REPLY, index, sizeof(cp), &cp, pin_rsp, NULL, NULL); } static void pin_neg_rsp(uint8_t status, uint16_t len, const void *param, void *user_data) { if (status != 0) { error("PIN Neg reply failed with status 0x%02x (%s)", status, mgmt_errstr(status)); return bt_shell_noninteractive_quit(EXIT_FAILURE); } print("PIN Negative Reply successful"); } static int mgmt_pin_neg_reply(uint16_t index, const struct mgmt_addr_info *addr) { struct mgmt_cp_pin_code_neg_reply cp; memset(&cp, 0, sizeof(cp)); memcpy(&cp.addr, addr, sizeof(cp.addr)); return mgmt_reply(mgmt, MGMT_OP_PIN_CODE_NEG_REPLY, index, sizeof(cp), &cp, pin_neg_rsp, NULL, NULL); } static void confirm_rsp(uint8_t status, uint16_t len, const void *param, void *user_data) { if (status != 0) { error("User Confirm reply failed. status 0x%02x (%s)", status, mgmt_errstr(status)); return bt_shell_noninteractive_quit(EXIT_FAILURE); } print("User Confirm Reply successful"); } static int mgmt_confirm_reply(uint16_t index, const struct mgmt_addr_info *addr) { struct mgmt_cp_user_confirm_reply cp; memset(&cp, 0, sizeof(cp)); memcpy(&cp.addr, addr, sizeof(*addr)); return mgmt_reply(mgmt, MGMT_OP_USER_CONFIRM_REPLY, index, sizeof(cp), &cp, confirm_rsp, NULL, NULL); } static void confirm_neg_rsp(uint8_t status, uint16_t len, const void *param, void *user_data) { if (status != 0) { error("Confirm Neg reply failed. status 0x%02x (%s)", status, mgmt_errstr(status)); return bt_shell_noninteractive_quit(EXIT_FAILURE); } print("User Confirm Negative Reply successful"); } static int mgmt_confirm_neg_reply(uint16_t index, const struct mgmt_addr_info *addr) { struct mgmt_cp_user_confirm_reply cp; memset(&cp, 0, sizeof(cp)); memcpy(&cp.addr, addr, sizeof(*addr)); return mgmt_reply(mgmt, MGMT_OP_USER_CONFIRM_NEG_REPLY, index, sizeof(cp), &cp, confirm_neg_rsp, NULL, NULL); } static void passkey_rsp(uint8_t status, uint16_t len, const void *param, void *user_data) { if (status != 0) { error("User Passkey reply failed. status 0x%02x (%s)", status, mgmt_errstr(status)); return bt_shell_noninteractive_quit(EXIT_FAILURE); } print("User Passkey Reply successful"); } static int mgmt_passkey_reply(uint16_t index, const struct mgmt_addr_info *addr, uint32_t passkey) { struct mgmt_cp_user_passkey_reply cp; memset(&cp, 0, sizeof(cp)); memcpy(&cp.addr, addr, sizeof(*addr)); put_le32(passkey, &cp.passkey); return mgmt_reply(mgmt, MGMT_OP_USER_PASSKEY_REPLY, index, sizeof(cp), &cp, passkey_rsp, NULL, NULL); } static void passkey_neg_rsp(uint8_t status, uint16_t len, const void *param, void *user_data) { if (status != 0) { error("Passkey Neg reply failed. status 0x%02x (%s)", status, mgmt_errstr(status)); return bt_shell_noninteractive_quit(EXIT_FAILURE); } print("User Passkey Negative Reply successful"); } static int mgmt_passkey_neg_reply(uint16_t index, const struct mgmt_addr_info *addr) { struct mgmt_cp_user_passkey_reply cp; memset(&cp, 0, sizeof(cp)); memcpy(&cp.addr, addr, sizeof(*addr)); return mgmt_reply(mgmt, MGMT_OP_USER_PASSKEY_NEG_REPLY, index, sizeof(cp), &cp, passkey_neg_rsp, NULL, NULL); } static void prompt_input(const char *input, void *user_data) { size_t len; len = strlen(input); switch (prompt.req) { case MGMT_EV_PIN_CODE_REQUEST: if (len) mgmt_pin_reply(prompt.index, &prompt.addr, input, len); else mgmt_pin_neg_reply(prompt.index, &prompt.addr); break; case MGMT_EV_USER_PASSKEY_REQUEST: if (strlen(input) > 0) mgmt_passkey_reply(prompt.index, &prompt.addr, atoi(input)); else mgmt_passkey_neg_reply(prompt.index, &prompt.addr); break; case MGMT_EV_USER_CONFIRM_REQUEST: if (len) { if (input[0] == 'y' || input[0] == 'Y') mgmt_confirm_reply(prompt.index, &prompt.addr); else mgmt_confirm_neg_reply(prompt.index, &prompt.addr); } else { mgmt_confirm_neg_reply(prompt.index, &prompt.addr); bt_shell_set_prompt(PROMPT_ON, COLOR_BLUE); } break; } } static void ask(uint16_t index, uint16_t req, const struct mgmt_addr_info *addr, const char *fmt, ...) { char msg[256]; va_list ap; int off; prompt.index = index; prompt.req = req; memcpy(&prompt.addr, addr, sizeof(*addr)); va_start(ap, fmt); off = vsnprintf(msg, sizeof(msg), fmt, ap); va_end(ap); snprintf(msg + off, sizeof(msg) - off, " %s ", COLOR_BOLDGRAY ">>" COLOR_OFF); bt_shell_prompt_input("", msg, prompt_input, NULL); } static void request_pin(uint16_t index, uint16_t len, const void *param, void *user_data) { const struct mgmt_ev_pin_code_request *ev = param; char addr[18]; if (len != sizeof(*ev)) { error("Invalid pin_code request length (%u bytes)", len); return; } ba2str(&ev->addr.bdaddr, addr); print("hci%u %s request PIN", index, addr); ask(index, MGMT_EV_PIN_CODE_REQUEST, &ev->addr, "PIN Request (press enter to reject)"); } static void user_confirm(uint16_t index, uint16_t len, const void *param, void *user_data) { const struct mgmt_ev_user_confirm_request *ev = param; uint32_t val; char addr[18]; if (len != sizeof(*ev)) { error("Invalid user_confirm request length (%u)", len); return; } ba2str(&ev->addr.bdaddr, addr); val = get_le32(&ev->value); print("hci%u %s User Confirm %06u hint %u", index, addr, val, ev->confirm_hint); if (ev->confirm_hint) ask(index, MGMT_EV_USER_CONFIRM_REQUEST, &ev->addr, "Accept pairing with %s (yes/no)", addr); else ask(index, MGMT_EV_USER_CONFIRM_REQUEST, &ev->addr, "Confirm value %06u for %s (yes/no)", val, addr); } static void request_passkey(uint16_t index, uint16_t len, const void *param, void *user_data) { const struct mgmt_ev_user_passkey_request *ev = param; char addr[18]; if (len != sizeof(*ev)) { error("Invalid passkey request length (%u bytes)", len); return; } ba2str(&ev->addr.bdaddr, addr); print("hci%u %s request passkey", index, addr); ask(index, MGMT_EV_USER_PASSKEY_REQUEST, &ev->addr, "Passkey Request (press enter to reject)"); } static void passkey_notify(uint16_t index, uint16_t len, const void *param, void *user_data) { const struct mgmt_ev_passkey_notify *ev = param; char addr[18]; if (len != sizeof(*ev)) { error("Invalid passkey request length (%u bytes)", len); return; } ba2str(&ev->addr.bdaddr, addr); print("hci%u %s request passkey", index, addr); print("Passkey Notify: %06u (entered %u)", get_le32(&ev->passkey), ev->entered); } static void local_oob_data_updated(uint16_t index, uint16_t len, const void *param, void *user_data) { const struct mgmt_ev_local_oob_data_updated *ev = param; uint16_t eir_len; if (len < sizeof(*ev)) { error("Too small (%u bytes) local_oob_updated event", len); return; } eir_len = le16_to_cpu(ev->eir_len); if (len != sizeof(*ev) + eir_len) { error("local_oob_updated: expected %zu bytes, got %u bytes", sizeof(*ev) + eir_len, len); return; } print("hci%u oob data updated: type %u len %u", index, ev->type, eir_len); } static void advertising_added(uint16_t index, uint16_t len, const void *param, void *user_data) { const struct mgmt_ev_advertising_added *ev = param; if (len < sizeof(*ev)) { error("Too small (%u bytes) advertising_added event", len); return; } print("hci%u advertising_added: instance %u", index, ev->instance); } static void advertising_removed(uint16_t index, uint16_t len, const void *param, void *user_data) { const struct mgmt_ev_advertising_removed *ev = param; if (len < sizeof(*ev)) { error("Too small (%u bytes) advertising_removed event", len); return; } print("hci%u advertising_removed: instance %u", index, ev->instance); } static void flags_changed(uint16_t index, uint16_t len, const void *param, void *user_data) { const struct mgmt_ev_device_flags_changed *ev = param; char addr[18]; if (len < sizeof(*ev)) { error("Too small (%u bytes) %s event", len, __func__); return; } ba2str(&ev->addr.bdaddr, addr); print("hci%u device_flags_changed: %s (%s)", index, addr, typestr(ev->addr.type)); print(" supp: 0x%08x curr: 0x%08x", ev->supported_flags, ev->current_flags); } static void advmon_added(uint16_t index, uint16_t len, const void *param, void *user_data) { const struct mgmt_ev_adv_monitor_added *ev = param; if (len < sizeof(*ev)) { error("Too small (%u bytes) %s event", len, __func__); return; } print("hci%u %s: handle %u", index, __func__, le16_to_cpu(ev->monitor_handle)); } static void advmon_removed(uint16_t index, uint16_t len, const void *param, void *user_data) { const struct mgmt_ev_adv_monitor_removed *ev = param; if (len < sizeof(*ev)) { error("Too small (%u bytes) %s event", len, __func__); return; } print("hci%u %s: handle %u", index, __func__, le16_to_cpu(ev->monitor_handle)); } static void version_rsp(uint8_t status, uint16_t len, const void *param, void *user_data) { const struct mgmt_rp_read_version *rp = param; if (status != 0) { error("Reading mgmt version failed with status 0x%02x (%s)", status, mgmt_errstr(status)); goto done; } if (len < sizeof(*rp)) { error("Too small version reply (%u bytes)", len); goto done; } print("MGMT Version %u, revision %u", rp->version, get_le16(&rp->revision)); done: bt_shell_noninteractive_quit(EXIT_SUCCESS); } static void cmd_revision(int argc, char **argv) { if (mgmt_send(mgmt, MGMT_OP_READ_VERSION, MGMT_INDEX_NONE, 0, NULL, version_rsp, NULL, NULL) == 0) { error("Unable to send read_version cmd"); return bt_shell_noninteractive_quit(EXIT_FAILURE); } } static void commands_rsp(uint8_t status, uint16_t len, const void *param, void *user_data) { const struct mgmt_rp_read_commands *rp = param; uint16_t num_commands, num_events; size_t expected_len; int i; if (status != 0) { error("Read Supported Commands failed: status 0x%02x (%s)", status, mgmt_errstr(status)); goto done; } if (len < sizeof(*rp)) { error("Too small commands reply (%u bytes)", len); goto done; } num_commands = get_le16(&rp->num_commands); num_events = get_le16(&rp->num_events); expected_len = sizeof(*rp) + num_commands * sizeof(uint16_t) + num_events * sizeof(uint16_t); if (len < expected_len) { error("Too small commands reply (%u != %zu)", len, expected_len); goto done; } print("%u commands:", num_commands); for (i = 0; i < num_commands; i++) { uint16_t op = get_le16(rp->opcodes + i); print("\t%s (0x%04x)", mgmt_opstr(op), op); } print("%u events:", num_events); for (i = 0; i < num_events; i++) { uint16_t ev = get_le16(rp->opcodes + num_commands + i); print("\t%s (0x%04x)", mgmt_evstr(ev), ev); } done: bt_shell_noninteractive_quit(EXIT_SUCCESS); } static void cmd_commands(int argc, char **argv) { if (mgmt_send(mgmt, MGMT_OP_READ_COMMANDS, MGMT_INDEX_NONE, 0, NULL, commands_rsp, NULL, NULL) == 0) { error("Unable to send read_commands cmd"); return bt_shell_noninteractive_quit(EXIT_FAILURE); } } static void config_info_rsp(uint8_t status, uint16_t len, const void *param, void *user_data) { const struct mgmt_rp_read_config_info *rp = param; uint16_t index = PTR_TO_UINT(user_data); uint32_t supported_options, missing_options; if (status != 0) { error("Reading hci%u config failed with status 0x%02x (%s)", index, status, mgmt_errstr(status)); goto done; } if (len < sizeof(*rp)) { error("Too small info reply (%u bytes)", len); goto done; } print("hci%u:\tUnconfigured controller", index); print("\tmanufacturer %u", le16_to_cpu(rp->manufacturer)); supported_options = le32_to_cpu(rp->supported_options); print("\tsupported options: %s", options2str(supported_options)); missing_options = le32_to_cpu(rp->missing_options); print("\tmissing options: %s", options2str(missing_options)); done: pending_index--; if (pending_index > 0) return; bt_shell_noninteractive_quit(EXIT_SUCCESS); } static void unconf_index_rsp(uint8_t status, uint16_t len, const void *param, void *user_data) { const struct mgmt_rp_read_unconf_index_list *rp = param; uint16_t count; unsigned int i; if (status != 0) { error("Reading index list failed with status 0x%02x (%s)", status, mgmt_errstr(status)); return bt_shell_noninteractive_quit(EXIT_FAILURE); } if (len < sizeof(*rp)) { error("Too small index list reply (%u bytes)", len); return bt_shell_noninteractive_quit(EXIT_FAILURE); } count = le16_to_cpu(rp->num_controllers); if (len < sizeof(*rp) + count * sizeof(uint16_t)) { error("Index count (%u) doesn't match reply length (%u)", count, len); return bt_shell_noninteractive_quit(EXIT_FAILURE); } print("Unconfigured index list with %u item%s", count, count != 1 ? "s" : ""); for (i = 0; i < count; i++) { uint16_t index = le16_to_cpu(rp->index[i]); if (!mgmt_send(mgmt, MGMT_OP_READ_CONFIG_INFO, index, 0, NULL, config_info_rsp, UINT_TO_PTR(index), NULL)) { error("Unable to send read_config_info cmd"); return bt_shell_noninteractive_quit(EXIT_FAILURE); } pending_index++; } if (!count) bt_shell_noninteractive_quit(EXIT_SUCCESS); } static void cmd_config(int argc, char **argv) { if (mgmt_index == MGMT_INDEX_NONE) { if (!mgmt_send(mgmt, MGMT_OP_READ_UNCONF_INDEX_LIST, MGMT_INDEX_NONE, 0, NULL, unconf_index_rsp, mgmt, NULL)) { error("Unable to send unconf_index_list cmd"); return bt_shell_noninteractive_quit(EXIT_FAILURE); } return; } if (!mgmt_send(mgmt, MGMT_OP_READ_CONFIG_INFO, mgmt_index, 0, NULL, config_info_rsp, UINT_TO_PTR(index), NULL)) { error("Unable to send read_config_info cmd"); return bt_shell_noninteractive_quit(EXIT_FAILURE); } } static void config_options_rsp(uint8_t status, uint16_t len, const void *param, void *user_data) { const struct mgmt_rp_read_config_info *rp = param; uint16_t index = PTR_TO_UINT(user_data); uint32_t supported_options, missing_options; if (status != 0) { error("Reading hci%u config failed with status 0x%02x (%s)", index, status, mgmt_errstr(status)); goto done; } if (len < sizeof(*rp)) { error("Too small info reply (%u bytes)", len); goto done; } print("hci%u:\tConfiguration options", index); supported_options = le32_to_cpu(rp->supported_options); print("\tsupported options: %s", options2str(supported_options)); missing_options = le32_to_cpu(rp->missing_options); print("\tmissing options: %s", options2str(missing_options)); done: pending_index--; if (pending_index > 0) return; bt_shell_noninteractive_quit(EXIT_SUCCESS); } static void info_rsp(uint8_t status, uint16_t len, const void *param, void *user_data) { const struct mgmt_rp_read_info *rp = param; uint16_t index = PTR_TO_UINT(user_data); uint32_t supported_settings, current_settings; char addr[18]; if (status != 0) { error("Reading hci%u info failed with status 0x%02x (%s)", index, status, mgmt_errstr(status)); goto done; } if (len < sizeof(*rp)) { error("Too small info reply (%u bytes)", len); goto done; } print("hci%u:\tPrimary controller", index); ba2str(&rp->bdaddr, addr); print("\taddr %s version %u manufacturer %u class 0x%02x%02x%02x", addr, rp->version, le16_to_cpu(rp->manufacturer), rp->dev_class[2], rp->dev_class[1], rp->dev_class[0]); supported_settings = le32_to_cpu(rp->supported_settings); print("\tsupported settings: %s", settings2str(supported_settings)); current_settings = le32_to_cpu(rp->current_settings); print("\tcurrent settings: %s", settings2str(current_settings)); print("\tname %s", rp->name); print("\tshort name %s", rp->short_name); if (supported_settings & MGMT_SETTING_CONFIGURATION) { if (!mgmt_send(mgmt, MGMT_OP_READ_CONFIG_INFO, index, 0, NULL, config_options_rsp, UINT_TO_PTR(index), NULL)) { error("Unable to send read_config cmd"); goto done; } return; } done: pending_index--; if (pending_index > 0) return; bt_shell_noninteractive_quit(EXIT_SUCCESS); } static void ext_info_rsp(uint8_t status, uint16_t len, const void *param, void *user_data) { const struct mgmt_rp_read_ext_info *rp = param; uint16_t index = PTR_TO_UINT(user_data); uint32_t supported_settings, current_settings; char addr[18]; if (status != 0) { error("Reading hci%u info failed with status 0x%02x (%s)", index, status, mgmt_errstr(status)); goto done; } if (len < sizeof(*rp)) { error("Too small info reply (%u bytes)", len); goto done; } print("hci%u:\tPrimary controller", index); ba2str(&rp->bdaddr, addr); print("\taddr %s version %u manufacturer %u", addr, rp->version, le16_to_cpu(rp->manufacturer)); supported_settings = le32_to_cpu(rp->supported_settings); print("\tsupported settings: %s", settings2str(supported_settings)); current_settings = le32_to_cpu(rp->current_settings); print("\tcurrent settings: %s", settings2str(current_settings)); if (supported_settings & MGMT_SETTING_CONFIGURATION) { if (!mgmt_send(mgmt, MGMT_OP_READ_CONFIG_INFO, index, 0, NULL, config_options_rsp, UINT_TO_PTR(index), NULL)) { error("Unable to send read_config cmd"); goto done; } return; } done: pending_index--; if (pending_index > 0) return; bt_shell_noninteractive_quit(EXIT_SUCCESS); } static void index_rsp(uint8_t status, uint16_t len, const void *param, void *user_data) { const struct mgmt_rp_read_index_list *rp = param; struct mgmt *mgmt = user_data; uint16_t count; unsigned int i; if (status != 0) { error("Reading index list failed with status 0x%02x (%s)", status, mgmt_errstr(status)); return bt_shell_noninteractive_quit(EXIT_FAILURE); } if (len < sizeof(*rp)) { error("Too small index list reply (%u bytes)", len); return bt_shell_noninteractive_quit(EXIT_FAILURE); } count = le16_to_cpu(rp->num_controllers); if (len < sizeof(*rp) + count * sizeof(uint16_t)) { error("Index count (%u) doesn't match reply length (%u)", count, len); return bt_shell_noninteractive_quit(EXIT_FAILURE); } print("Index list with %u item%s", count, count != 1 ? "s" : ""); for (i = 0; i < count; i++) { uint16_t index = le16_to_cpu(rp->index[i]); if (!mgmt_send(mgmt, MGMT_OP_READ_INFO, index, 0, NULL, info_rsp, UINT_TO_PTR(index), NULL)) { error("Unable to send read_info cmd"); return bt_shell_noninteractive_quit(EXIT_FAILURE); } pending_index++; } if (!count) bt_shell_noninteractive_quit(EXIT_SUCCESS); } static void cmd_info(int argc, char **argv) { if (mgmt_index == MGMT_INDEX_NONE) { if (!mgmt_send(mgmt, MGMT_OP_READ_INDEX_LIST, MGMT_INDEX_NONE, 0, NULL, index_rsp, mgmt, NULL)) { error("Unable to send index_list cmd"); return bt_shell_noninteractive_quit(EXIT_FAILURE); } return; } if (!mgmt_send(mgmt, MGMT_OP_READ_INFO, mgmt_index, 0, NULL, info_rsp, UINT_TO_PTR(mgmt_index), NULL)) { error("Unable to send read_info cmd"); return bt_shell_noninteractive_quit(EXIT_FAILURE); } } static void ext_index_rsp(uint8_t status, uint16_t len, const void *param, void *user_data) { const struct mgmt_rp_read_ext_index_list *rp = param; uint16_t count; unsigned int i; if (status != 0) { error("Reading ext index list failed with status 0x%02x (%s)", status, mgmt_errstr(status)); return bt_shell_noninteractive_quit(EXIT_FAILURE); } if (len < sizeof(*rp)) { error("Too small ext index list reply (%u bytes)", len); return bt_shell_noninteractive_quit(EXIT_FAILURE); } count = get_le16(&rp->num_controllers); if (len < sizeof(*rp) + count * (sizeof(uint16_t) + sizeof(uint8_t))) { error("Index count (%u) doesn't match reply length (%u)", count, len); return bt_shell_noninteractive_quit(EXIT_FAILURE); } print("Extended index list with %u item%s", count, count != 1 ? "s" : ""); for (i = 0; i < count; i++) { uint16_t index = le16_to_cpu(rp->entry[i].index); const char *busstr = hci_bustostr(rp->entry[i].bus); switch (rp->entry[i].type) { case 0x00: print("Primary controller (hci%u,%s)", index, busstr); if (!mgmt_send(mgmt, MGMT_OP_READ_EXT_INFO, index, 0, NULL, ext_info_rsp, UINT_TO_PTR(index), NULL)) { error("Unable to send read_ext_info cmd"); return bt_shell_noninteractive_quit(EXIT_FAILURE); } pending_index++; break; case 0x01: print("Unconfigured controller (hci%u,%s)", index, busstr); if (!mgmt_send(mgmt, MGMT_OP_READ_CONFIG_INFO, index, 0, NULL, config_info_rsp, UINT_TO_PTR(index), NULL)) { error("Unable to send read_config cmd"); return bt_shell_noninteractive_quit(EXIT_FAILURE); } pending_index++; break; case 0x02: print("AMP controller (hci%u,%s)", index, busstr); break; default: print("Type %u controller (hci%u,%s)", rp->entry[i].type, index, busstr); break; } } print(""); if (!count) bt_shell_noninteractive_quit(EXIT_SUCCESS); } static void cmd_extinfo(int argc, char **argv) { if (mgmt_index == MGMT_INDEX_NONE) { if (!mgmt_send(mgmt, MGMT_OP_READ_EXT_INDEX_LIST, MGMT_INDEX_NONE, 0, NULL, ext_index_rsp, mgmt, NULL)) { error("Unable to send ext_index_list cmd"); return bt_shell_noninteractive_quit(EXIT_FAILURE); } return; } if (!mgmt_send(mgmt, MGMT_OP_READ_EXT_INFO, mgmt_index, 0, NULL, ext_info_rsp, UINT_TO_PTR(mgmt_index), NULL)) { error("Unable to send ext_read_info cmd"); return bt_shell_noninteractive_quit(EXIT_FAILURE); } } static void print_cap(const uint8_t *cap, uint16_t cap_len) { uint16_t parsed = 0; while (parsed < cap_len - 1) { uint8_t field_len = cap[0]; if (field_len == 0) break; parsed += field_len + 1; if (parsed > cap_len) break; switch (cap[1]) { case 0x01: print("\tFlags: 0x%02x", cap[2]); break; case 0x02: print("\tMax Key Size (BR/EDR): %u", cap[2]); break; case 0x03: print("\tMax Key Size (LE): %u", cap[2]); break; default: print("\tType %u: %u byte%s", cap[1], field_len - 1, (field_len - 1) == 1 ? "" : "s"); break; } cap += field_len + 1; } } static void sec_info_rsp(uint8_t status, uint16_t len, const void *param, void *user_data) { const struct mgmt_rp_read_controller_cap *rp = param; uint16_t index = PTR_TO_UINT(user_data); if (status != 0) { error("Reading hci%u security failed with status 0x%02x (%s)", index, status, mgmt_errstr(status)); goto done; } if (len < sizeof(*rp)) { error("Too small info reply (%u bytes)", len); goto done; } print("Primary controller (hci%u)", index); print("\tInfo length: %u", le16_to_cpu(rp->cap_len)); print_cap(rp->cap, le16_to_cpu(rp->cap_len)); done: pending_index--; if (pending_index > 0) return; bt_shell_noninteractive_quit(EXIT_SUCCESS); } static void sec_index_rsp(uint8_t status, uint16_t len, const void *param, void *user_data) { const struct mgmt_rp_read_ext_index_list *rp = param; uint16_t count; unsigned int i; if (status != 0) { error("Reading ext index list failed with status 0x%02x (%s)", status, mgmt_errstr(status)); return bt_shell_noninteractive_quit(EXIT_FAILURE); } if (len < sizeof(*rp)) { error("Too small ext index list reply (%u bytes)", len); return bt_shell_noninteractive_quit(EXIT_FAILURE); } count = get_le16(&rp->num_controllers); if (len < sizeof(*rp) + count * (sizeof(uint16_t) + sizeof(uint8_t))) { error("Index count (%u) doesn't match reply length (%u)", count, len); return bt_shell_noninteractive_quit(EXIT_FAILURE); } for (i = 0; i < count; i++) { uint16_t index = le16_to_cpu(rp->entry[i].index); if (rp->entry[i].type != 0x00) continue; if (!mgmt_send(mgmt, MGMT_OP_READ_CONTROLLER_CAP, index, 0, NULL, sec_info_rsp, UINT_TO_PTR(index), NULL)) { error("Unable to send read_security_info cmd"); return bt_shell_noninteractive_quit(EXIT_FAILURE); } pending_index++; } if (!count) bt_shell_noninteractive_quit(EXIT_SUCCESS); } static void cmd_secinfo(int argc, char **argv) { if (mgmt_index == MGMT_INDEX_NONE) { if (!mgmt_send(mgmt, MGMT_OP_READ_EXT_INDEX_LIST, MGMT_INDEX_NONE, 0, NULL, sec_index_rsp, mgmt, NULL)) { error("Unable to send ext_index_list cmd"); return bt_shell_noninteractive_quit(EXIT_FAILURE); } return; } if (!mgmt_send(mgmt, MGMT_OP_READ_CONTROLLER_CAP, mgmt_index, 0, NULL, sec_info_rsp, UINT_TO_PTR(mgmt_index), NULL)) { error("Unable to send read_security_info cmd"); return bt_shell_noninteractive_quit(EXIT_FAILURE); } } static void exp_info_rsp(uint8_t status, uint16_t len, const void *param, void *user_data) { const struct mgmt_rp_read_exp_features_info *rp = param; uint16_t index = PTR_TO_UINT(user_data); if (status != 0) { error("Reading hci%u exp features failed with status 0x%02x (%s)", index, status, mgmt_errstr(status)); goto done; } if (len < sizeof(*rp)) { error("Too small info reply (%u bytes)", len); goto done; } if (index == MGMT_INDEX_NONE) print("Global"); else print("Primary controller (hci%u)", index); print("\tNumber of experimental features: %u", le16_to_cpu(rp->feature_count)); done: pending_index--; if (pending_index > 0) return; bt_shell_noninteractive_quit(EXIT_SUCCESS); } static void exp_index_rsp(uint8_t status, uint16_t len, const void *param, void *user_data) { const struct mgmt_rp_read_ext_index_list *rp = param; uint16_t count; unsigned int i; if (status != 0) { error("Reading ext index list failed with status 0x%02x (%s)", status, mgmt_errstr(status)); return bt_shell_noninteractive_quit(EXIT_FAILURE); } if (len < sizeof(*rp)) { error("Too small ext index list reply (%u bytes)", len); return bt_shell_noninteractive_quit(EXIT_FAILURE); } count = get_le16(&rp->num_controllers); if (len < sizeof(*rp) + count * (sizeof(uint16_t) + sizeof(uint8_t))) { error("Index count (%u) doesn't match reply length (%u)", count, len); return bt_shell_noninteractive_quit(EXIT_FAILURE); } for (i = 0; i < count; i++) { uint16_t index = le16_to_cpu(rp->entry[i].index); if (rp->entry[i].type != 0x00) continue; if (!mgmt_send(mgmt, MGMT_OP_READ_EXP_FEATURES_INFO, index, 0, NULL, exp_info_rsp, UINT_TO_PTR(index), NULL)) { error("Unable to send read_exp_features_info cmd"); return bt_shell_noninteractive_quit(EXIT_FAILURE); } pending_index++; } } static void cmd_expinfo(int argc, char **argv) { if (mgmt_index == MGMT_INDEX_NONE) { if (!mgmt_send(mgmt, MGMT_OP_READ_EXT_INDEX_LIST, MGMT_INDEX_NONE, 0, NULL, exp_index_rsp, mgmt, NULL)) { error("Unable to send ext_index_list cmd"); return bt_shell_noninteractive_quit(EXIT_FAILURE); } if (!mgmt_send(mgmt, MGMT_OP_READ_EXP_FEATURES_INFO, MGMT_INDEX_NONE, 0, NULL, exp_info_rsp, UINT_TO_PTR(MGMT_INDEX_NONE), NULL)) { error("Unable to send read_exp_features_info cmd"); return bt_shell_noninteractive_quit(EXIT_FAILURE); } pending_index++; return; } if (!mgmt_send(mgmt, MGMT_OP_READ_EXP_FEATURES_INFO, mgmt_index, 0, NULL, exp_info_rsp, UINT_TO_PTR(mgmt_index), NULL)) { error("Unable to send read_exp_features_info cmd"); return bt_shell_noninteractive_quit(EXIT_FAILURE); } } static void exp_debug_rsp(uint8_t status, uint16_t len, const void *param, void *user_data) { if (status != 0) error("Set debug feature failed with status 0x%02x (%s)", status, mgmt_errstr(status)); else print("Debug feature successfully set"); bt_shell_noninteractive_quit(EXIT_SUCCESS); } static void cmd_exp_debug(int argc, char **argv) { /* d4992530-b9ec-469f-ab01-6c481c47da1c */ static const uint8_t uuid[16] = { 0x1c, 0xda, 0x47, 0x1c, 0x48, 0x6c, 0x01, 0xab, 0x9f, 0x46, 0xec, 0xb9, 0x30, 0x25, 0x99, 0xd4, }; struct mgmt_cp_set_exp_feature cp; uint8_t val; if (parse_setting(argc, argv, &val) == false) return bt_shell_noninteractive_quit(EXIT_FAILURE); memset(&cp, 0, sizeof(cp)); memcpy(cp.uuid, uuid, 16); cp.action = val; if (mgmt_send(mgmt, MGMT_OP_SET_EXP_FEATURE, mgmt_index, sizeof(cp), &cp, exp_debug_rsp, NULL, NULL) == 0) { error("Unable to send debug feature cmd"); return bt_shell_noninteractive_quit(EXIT_FAILURE); } } static void exp_privacy_rsp(uint8_t status, uint16_t len, const void *param, void *user_data) { if (status != 0) error("Set LL privacy feature failed with status 0x%02x (%s)", status, mgmt_errstr(status)); else print("LL privacy feature successfully set"); bt_shell_noninteractive_quit(EXIT_SUCCESS); } static void cmd_exp_privacy(int argc, char **argv) { /* 15c0a148-c273-11ea-b3de-0242ac130004 */ static const uint8_t uuid[16] = { 0x04, 0x00, 0x13, 0xac, 0x42, 0x02, 0xde, 0xb3, 0xea, 0x11, 0x73, 0xc2, 0x48, 0xa1, 0xc0, 0x15, }; struct mgmt_cp_set_exp_feature cp; uint8_t val; if (parse_setting(argc, argv, &val) == false) return bt_shell_noninteractive_quit(EXIT_FAILURE); memset(&cp, 0, sizeof(cp)); memcpy(cp.uuid, uuid, 16); cp.action = val; if (mgmt_send(mgmt, MGMT_OP_SET_EXP_FEATURE, mgmt_index, sizeof(cp), &cp, exp_privacy_rsp, NULL, NULL) == 0) { error("Unable to send LL privacy feature cmd"); return bt_shell_noninteractive_quit(EXIT_FAILURE); } } static void exp_quality_rsp(uint8_t status, uint16_t len, const void *param, void *user_data) { if (status != 0) error("Set Quality Report feature failed: 0x%02x (%s)", status, mgmt_errstr(status)); else print("Quality Report feature successfully set"); bt_shell_noninteractive_quit(EXIT_SUCCESS); } static void cmd_exp_quality(int argc, char **argv) { /* 330859bc-7506-492d-9370-9a6f0614037f */ static const uint8_t uuid[16] = { 0x7f, 0x03, 0x14, 0x06, 0x6f, 0x9a, 0x70, 0x93, 0x2d, 0x49, 0x06, 0x75, 0xbc, 0x59, 0x08, 0x33, }; struct mgmt_cp_set_exp_feature cp; uint8_t val; if (mgmt_index == MGMT_INDEX_NONE) { error("BQR feature requires a valid controller index"); return; } if (parse_setting(argc, argv, &val) == false) return bt_shell_noninteractive_quit(EXIT_FAILURE); if (val != 0 && val != 1) { error("Invalid value %u", val); return; } memset(&cp, 0, sizeof(cp)); memcpy(cp.uuid, uuid, 16); cp.action = val; if (mgmt_send(mgmt, MGMT_OP_SET_EXP_FEATURE, mgmt_index, sizeof(cp), &cp, exp_quality_rsp, NULL, NULL) == 0) { error("Unable to send quality report feature cmd"); return bt_shell_noninteractive_quit(EXIT_FAILURE); } } static void print_mgmt_tlv(void *data, void *user_data) { const struct mgmt_tlv *entry = data; char buf[256]; bin2hex(entry->value, entry->length, buf, sizeof(buf)); print("Type: 0x%04x\tLength: %02hhu\tValue: %s", entry->type, entry->length, buf); } static void read_sysconfig_rsp(uint8_t status, uint16_t len, const void *param, void *user_data) { struct mgmt_tlv_list *tlv_list; if (status != 0) { error("Read system configuration failed with status " "0x%02x (%s)", status, mgmt_errstr(status)); return bt_shell_noninteractive_quit(EXIT_FAILURE); } tlv_list = mgmt_tlv_list_load_from_buf(param, len); if (!tlv_list) { error("Unable to parse response of read system configuration"); return bt_shell_noninteractive_quit(EXIT_FAILURE); } mgmt_tlv_list_foreach(tlv_list, print_mgmt_tlv, NULL); mgmt_tlv_list_free(tlv_list); bt_shell_noninteractive_quit(EXIT_SUCCESS); } static void cmd_read_sysconfig(int argc, char **argv) { uint16_t index; index = mgmt_index; if (index == MGMT_INDEX_NONE) index = 0; if (!mgmt_send(mgmt, MGMT_OP_READ_DEF_SYSTEM_CONFIG, index, 0, NULL, read_sysconfig_rsp, NULL, NULL)) { error("Unable to send read system configuration cmd"); return bt_shell_noninteractive_quit(EXIT_FAILURE); } } static bool parse_mgmt_tlv(const char *input, uint16_t *type, uint8_t *length, uint8_t *value) { int i, value_starting_pos; if (sscanf(input, "%4hx:%1hhu:%n", type, length, &value_starting_pos) < 2) { return false; } input += value_starting_pos; if (*length * 2 != strlen(input)) return false; for (i = 0; i < *length; i++) { if (sscanf(input + i * 2, "%2hhx", &value[i]) < 1) return false; } return true; } static void set_sysconfig_rsp(uint8_t status, uint16_t len, const void *param, void *user_data) { if (status != MGMT_STATUS_SUCCESS) { error("Could not set default system configuration with status " "0x%02x (%s)", status, mgmt_errstr(status)); return bt_shell_noninteractive_quit(EXIT_FAILURE); } print("Set default system configuration success"); return bt_shell_noninteractive_quit(EXIT_SUCCESS); } static bool set_sysconfig(int argc, char **argv) { struct mgmt_tlv_list *tlv_list = NULL; int i; uint16_t index, type; uint8_t length; uint8_t value[256] = {}; bool success = false; index = mgmt_index; if (index == MGMT_INDEX_NONE) index = 0; tlv_list = mgmt_tlv_list_new(); if (!tlv_list) { error("tlv_list failed to init"); goto failed; } for (i = 0; i < argc; i++) { if (!parse_mgmt_tlv(argv[i], &type, &length, value)) { error("failed to parse"); goto failed; } if (!mgmt_tlv_add(tlv_list, type, length, value)) { error("failed to add"); goto failed; } } if (!mgmt_send_tlv(mgmt, MGMT_OP_SET_DEF_SYSTEM_CONFIG, index, tlv_list, set_sysconfig_rsp, NULL, NULL)) { error("Failed to send \"Set Default System Configuration\"" " command"); goto failed; } success = true; failed: if (tlv_list) mgmt_tlv_list_free(tlv_list); return success; } static void set_sysconfig_usage(void) { bt_shell_usage(); print("Parameters:\n\t-v ...\n" "e.g.:\n\tset-sysconfig -v 001a:2:1234 001f:1:00"); } static void cmd_set_sysconfig(int argc, char **argv) { bool success = false; if (strcasecmp(argv[1], "-v") == 0 && argc > 2) { argc -= 2; argv += 2; success = set_sysconfig(argc, argv); } if (!success) { set_sysconfig_usage(); bt_shell_noninteractive_quit(EXIT_FAILURE); } } static void auto_power_enable_rsp(uint8_t status, uint16_t len, const void *param, void *user_data) { uint16_t index = PTR_TO_UINT(user_data); print("Successfully enabled controller with index %u", index); bt_shell_noninteractive_quit(EXIT_SUCCESS); } static void auto_power_info_rsp(uint8_t status, uint16_t len, const void *param, void *user_data) { const struct mgmt_rp_read_info *rp = param; uint16_t index = PTR_TO_UINT(user_data); uint32_t supported_settings, current_settings, missing_settings; uint8_t val = 0x01; if (status) { error("Reading info failed with status 0x%02x (%s)", status, mgmt_errstr(status)); return bt_shell_noninteractive_quit(EXIT_FAILURE); } supported_settings = le32_to_cpu(rp->supported_settings); current_settings = le32_to_cpu(rp->current_settings); missing_settings = current_settings ^ supported_settings; if (missing_settings & MGMT_SETTING_BREDR) mgmt_send(mgmt, MGMT_OP_SET_BREDR, index, sizeof(val), &val, NULL, NULL, NULL); if (missing_settings & MGMT_SETTING_SSP) mgmt_send(mgmt, MGMT_OP_SET_SSP, index, sizeof(val), &val, NULL, NULL, NULL); if (missing_settings & MGMT_SETTING_LE) mgmt_send(mgmt, MGMT_OP_SET_LE, index, sizeof(val), &val, NULL, NULL, NULL); if (missing_settings & MGMT_SETTING_SECURE_CONN) mgmt_send(mgmt, MGMT_OP_SET_SECURE_CONN, index, sizeof(val), &val, NULL, NULL, NULL); if (missing_settings & MGMT_SETTING_BONDABLE) mgmt_send(mgmt, MGMT_OP_SET_BONDABLE, index, sizeof(val), &val, NULL, NULL, NULL); if (current_settings & MGMT_SETTING_POWERED) return bt_shell_noninteractive_quit(EXIT_SUCCESS); if (!mgmt_send(mgmt, MGMT_OP_SET_POWERED, index, sizeof(val), &val, auto_power_enable_rsp, UINT_TO_PTR(index), NULL)) { error("Unable to send set powerd cmd"); return bt_shell_noninteractive_quit(EXIT_FAILURE); } } static void auto_power_index_evt(uint16_t index, uint16_t len, const void *param, void *user_data) { uint16_t index_filter = PTR_TO_UINT(user_data); if (index != index_filter) return; print("New controller with index %u", index); if (!mgmt_send(mgmt, MGMT_OP_READ_INFO, index, 0, NULL, auto_power_info_rsp, UINT_TO_PTR(index), NULL)) { error("Unable to send read info cmd"); return bt_shell_noninteractive_quit(EXIT_FAILURE); } } static void auto_power_index_rsp(uint8_t status, uint16_t len, const void *param, void *user_data) { const struct mgmt_rp_read_index_list *rp = param; uint16_t index = PTR_TO_UINT(user_data); uint16_t i, count; bool found = false; if (status) { error("Reading index list failed with status 0x%02x (%s)", status, mgmt_errstr(status)); return bt_shell_noninteractive_quit(EXIT_FAILURE); } count = le16_to_cpu(rp->num_controllers); for (i = 0; i < count; i++) { if (le16_to_cpu(rp->index[i]) == index) found = true; } if (!found) { print("Waiting for index %u to appear", index); mgmt_register(mgmt, MGMT_EV_INDEX_ADDED, index, auto_power_index_evt, UINT_TO_PTR(index), NULL); return; } print("Found controller with index %u", index); if (!mgmt_send(mgmt, MGMT_OP_READ_INFO, index, 0, NULL, auto_power_info_rsp, UINT_TO_PTR(index), NULL)) { error("Unable to send read info cmd"); return bt_shell_noninteractive_quit(EXIT_FAILURE); } } static void cmd_auto_power(int argc, char **argv) { int index; index = mgmt_index; if (index == MGMT_INDEX_NONE) index = 0; if (!mgmt_send(mgmt, MGMT_OP_READ_INDEX_LIST, MGMT_INDEX_NONE, 0, NULL, auto_power_index_rsp, UINT_TO_PTR(index), NULL)) { error("Unable to send read index list cmd"); return bt_shell_noninteractive_quit(EXIT_FAILURE); } } static void get_flags_rsp(uint8_t status, uint16_t len, const void *param, void *user_data) { const struct mgmt_rp_get_device_flags *rp = param; if (status != 0) { error("Get device flags failed with status 0x%02x (%s)", status, mgmt_errstr(status)); return bt_shell_noninteractive_quit(EXIT_FAILURE); } print("Supported Flags: 0x%08x", rp->supported_flags); print("Current Flags: 0x%08x", rp->current_flags); bt_shell_noninteractive_quit(EXIT_SUCCESS); } static const struct option get_flags_options[] = { { "help", 0, 0, 'h' }, { "type", 1, 0, 't' }, { 0, 0, 0, 0 } }; static void cmd_get_flags(int argc, char **argv) { struct mgmt_cp_get_device_flags cp; uint8_t type = BDADDR_BREDR; char addr[18]; int opt; uint16_t index; while ((opt = getopt_long(argc, argv, "+t:h", get_flags_options, NULL)) != -1) { switch (opt) { case 't': type = strtol(optarg, NULL, 0); break; case 'h': bt_shell_usage(); optind = 0; return bt_shell_noninteractive_quit(EXIT_SUCCESS); default: bt_shell_usage(); optind = 0; return bt_shell_noninteractive_quit(EXIT_FAILURE); } } argc -= optind; argv += optind; optind = 0; if (argc < 1) { bt_shell_usage(); return bt_shell_noninteractive_quit(EXIT_FAILURE); } index = mgmt_index; if (index == MGMT_INDEX_NONE) index = 0; memset(&cp, 0, sizeof(cp)); str2ba(argv[0], &cp.addr.bdaddr); cp.addr.type = type; ba2str(&cp.addr.bdaddr, addr); print("Get device flag of %s (%s)", addr, typestr(cp.addr.type)); if (mgmt_send(mgmt, MGMT_OP_GET_DEVICE_FLAGS, index, sizeof(cp), &cp, get_flags_rsp, NULL, NULL) == 0) { error("Unable to send Get Device Flags command"); return bt_shell_noninteractive_quit(EXIT_FAILURE); } } static void set_flags_rsp(uint8_t status, uint16_t len, const void *param, void *user_data) { if (status != 0) { error("Set device flags failed with status 0x%02x (%s)", status, mgmt_errstr(status)); bt_shell_noninteractive_quit(EXIT_FAILURE); } bt_shell_noninteractive_quit(EXIT_SUCCESS); } static const struct option set_flags_options[] = { { "help", 0, 0, 'h' }, { "type", 1, 0, 't' }, { "flags", 1, 0, 'f' }, { 0, 0, 0, 0 } }; static void cmd_set_flags(int argc, char **argv) { struct mgmt_cp_set_device_flags cp; uint8_t type = BDADDR_BREDR; uint32_t flags = 0; char addr[18]; int opt; uint16_t index; while ((opt = getopt_long(argc, argv, "+f:t:h", set_flags_options, NULL)) != -1) { switch (opt) { case 'f': flags = strtol(optarg, NULL, 0); break; case 't': type = strtol(optarg, NULL, 0); break; case 'h': bt_shell_usage(); optind = 0; return bt_shell_noninteractive_quit(EXIT_SUCCESS); default: bt_shell_usage(); optind = 0; return bt_shell_noninteractive_quit(EXIT_FAILURE); } } argc -= optind; argv += optind; optind = 0; if (argc < 1) { bt_shell_usage(); return bt_shell_noninteractive_quit(EXIT_FAILURE); } index = mgmt_index; if (index == MGMT_INDEX_NONE) index = 0; memset(&cp, 0, sizeof(cp)); str2ba(argv[0], &cp.addr.bdaddr); cp.addr.type = type; cp.current_flags = flags; ba2str(&cp.addr.bdaddr, addr); print("Set device flag of %s (%s)", addr, typestr(cp.addr.type)); if (mgmt_send(mgmt, MGMT_OP_SET_DEVICE_FLAGS, index, sizeof(cp), &cp, set_flags_rsp, NULL, NULL) == 0) { error("Unable to send Set Device Flags command"); return bt_shell_noninteractive_quit(EXIT_FAILURE); } } static uint8_t *str2bytearray(char *arg, uint8_t *val, long *val_len) { char *entry; unsigned int i; for (i = 0; (entry = strsep(&arg, " \t")) != NULL; i++) { long v; char *endptr = NULL; if (*entry == '\0') continue; if (i >= *val_len) { bt_shell_printf("Too much data\n"); return NULL; } v = strtol(entry, &endptr, 0); if (!endptr || *endptr != '\0' || v > UINT8_MAX) { bt_shell_printf("Invalid value at index %d\n", i); return NULL; } val[i] = v; } *val_len = i; return val; } static void hci_cmd_rsp(uint8_t status, uint16_t len, const void *param, void *user_data) { if (status != 0) { error("HCI command failed with status 0x%02x (%s)", status, mgmt_errstr(status)); bt_shell_noninteractive_quit(EXIT_FAILURE); } if (len > 0) { bt_shell_printf("Response: "); bt_shell_hexdump(param, len); } bt_shell_noninteractive_quit(EXIT_SUCCESS); } static void cmd_hci_cmd(int argc, char **argv) { struct { struct mgmt_cp_hci_cmd_sync cp; uint8_t data[UINT8_MAX]; } pkt; char *endptr = NULL; long value; uint16_t index; value = strtoul(argv[1], &endptr, 0); if (!endptr || *endptr != '\0' || value > UINT16_MAX) { bt_shell_printf("Invalid opcode: %s", argv[1]); return bt_shell_noninteractive_quit(EXIT_FAILURE); } memset(&pkt, 0, sizeof(pkt)); pkt.cp.opcode = cpu_to_le16(value); if (argc > 2) { endptr = NULL; value = strtoul(argv[2], &endptr, 0); if (!endptr || *endptr != '\0' || value > UINT8_MAX) { bt_shell_printf("Invalid event: %s", argv[2]); return bt_shell_noninteractive_quit(EXIT_FAILURE); } pkt.cp.event = value; } if (argc > 3) { endptr = NULL; value = strtoul(argv[3], &endptr, 0); if (!endptr || *endptr != '\0' || value > UINT8_MAX) { bt_shell_printf("Invalid timeout: %s", argv[2]); return bt_shell_noninteractive_quit(EXIT_FAILURE); } pkt.cp.timeout = value; } if (argc > 4) { value = sizeof(pkt.data); if (!str2bytearray(argv[4], pkt.data, &value)) return bt_shell_noninteractive_quit(EXIT_FAILURE); pkt.cp.params_len = value; } index = mgmt_index; if (index == MGMT_INDEX_NONE) index = 0; if (mgmt_send(mgmt, MGMT_OP_HCI_CMD_SYNC, index, sizeof(pkt.cp) + pkt.cp.params_len, &pkt, hci_cmd_rsp, NULL, NULL) == 0) { error("Unable to send HCI command"); return bt_shell_noninteractive_quit(EXIT_FAILURE); } } /* Wrapper to get the index and opcode to the response callback */ struct command_data { uint16_t id; uint16_t op; void (*callback) (uint16_t id, uint16_t op, uint8_t status, uint16_t len, const void *param); }; static void cmd_rsp(uint8_t status, uint16_t len, const void *param, void *user_data) { struct command_data *data = user_data; data->callback(data->op, data->id, status, len, param); } static unsigned int send_cmd(struct mgmt *mgmt, uint16_t op, uint16_t id, uint16_t len, const void *param, void (*cb)(uint16_t id, uint16_t op, uint8_t status, uint16_t len, const void *param)) { struct command_data *data; unsigned int send_id; data = new0(struct command_data, 1); if (!data) return 0; data->id = id; data->op = op; data->callback = cb; send_id = mgmt_send(mgmt, op, id, len, param, cmd_rsp, data, free); if (send_id == 0) free(data); return send_id; } static void setting_rsp(uint16_t op, uint16_t id, uint8_t status, uint16_t len, const void *param) { const uint32_t *rp = param; if (status != 0) { error("%s for hci%u failed with status 0x%02x (%s)", mgmt_opstr(op), id, status, mgmt_errstr(status)); goto done; } if (len < sizeof(*rp)) { error("Too small %s response (%u bytes)", mgmt_opstr(op), len); goto done; } print("hci%u %s complete, settings: %s", id, mgmt_opstr(op), settings2str(get_le32(rp))); done: bt_shell_noninteractive_quit(EXIT_SUCCESS); } static void cmd_setting(uint16_t op, int argc, char **argv) { int index; uint8_t val; if (parse_setting(argc, argv, &val) == false) return bt_shell_noninteractive_quit(EXIT_FAILURE); index = mgmt_index; if (index == MGMT_INDEX_NONE) index = 0; if (send_cmd(mgmt, op, index, sizeof(val), &val, setting_rsp) == 0) { error("Unable to send %s cmd", mgmt_opstr(op)); return bt_shell_noninteractive_quit(EXIT_FAILURE); } } static void cmd_power(int argc, char **argv) { cmd_setting(MGMT_OP_SET_POWERED, argc, argv); } static void cmd_discov(int argc, char **argv) { struct mgmt_cp_set_discoverable cp; uint16_t index; memset(&cp, 0, sizeof(cp)); if (strcasecmp(argv[1], "on") == 0 || strcasecmp(argv[1], "yes") == 0) cp.val = 1; else if (strcasecmp(argv[1], "off") == 0) cp.val = 0; else if (strcasecmp(argv[1], "limited") == 0) cp.val = 2; else cp.val = atoi(argv[1]); if (argc > 2) cp.timeout = htobs(atoi(argv[2])); index = mgmt_index; if (index == MGMT_INDEX_NONE) index = 0; if (send_cmd(mgmt, MGMT_OP_SET_DISCOVERABLE, index, sizeof(cp), &cp, setting_rsp) == 0) { error("Unable to send set_discoverable cmd"); return bt_shell_noninteractive_quit(EXIT_FAILURE); } } static void cmd_connectable(int argc, char **argv) { cmd_setting(MGMT_OP_SET_CONNECTABLE, argc, argv); } static void cmd_fast_conn(int argc, char **argv) { cmd_setting(MGMT_OP_SET_FAST_CONNECTABLE, argc, argv); } static void cmd_bondable(int argc, char **argv) { cmd_setting(MGMT_OP_SET_BONDABLE, argc, argv); } static void cmd_linksec(int argc, char **argv) { cmd_setting(MGMT_OP_SET_LINK_SECURITY, argc, argv); } static void cmd_ssp(int argc, char **argv) { cmd_setting(MGMT_OP_SET_SSP, argc, argv); } static void cmd_sc(int argc, char **argv) { uint8_t val; uint16_t index; if (strcasecmp(argv[1], "on") == 0 || strcasecmp(argv[1], "yes") == 0) val = 1; else if (strcasecmp(argv[1], "off") == 0) val = 0; else if (strcasecmp(argv[1], "only") == 0) val = 2; else val = atoi(argv[1]); index = mgmt_index; if (index == MGMT_INDEX_NONE) index = 0; if (send_cmd(mgmt, MGMT_OP_SET_SECURE_CONN, index, sizeof(val), &val, setting_rsp) == 0) { error("Unable to send set_secure_conn cmd"); return bt_shell_noninteractive_quit(EXIT_FAILURE); } } static void cmd_hs(int argc, char **argv) { cmd_setting(MGMT_OP_SET_HS, argc, argv); } static void cmd_le(int argc, char **argv) { cmd_setting(MGMT_OP_SET_LE, argc, argv); } static void cmd_advertising(int argc, char **argv) { cmd_setting(MGMT_OP_SET_ADVERTISING, argc, argv); } static void cmd_bredr(int argc, char **argv) { cmd_setting(MGMT_OP_SET_BREDR, argc, argv); } static void cmd_privacy(int argc, char **argv) { struct mgmt_cp_set_privacy cp; uint16_t index; if (parse_setting(argc, argv, &cp.privacy) == false) return bt_shell_noninteractive_quit(EXIT_FAILURE); index = mgmt_index; if (index == MGMT_INDEX_NONE) index = 0; if (argc > 2) { if (hex2bin(argv[2], cp.irk, sizeof(cp.irk)) != sizeof(cp.irk)) { error("Invalid key format"); return bt_shell_noninteractive_quit(EXIT_FAILURE); } } else { int fd; fd = open("/dev/urandom", O_RDONLY); if (fd < 0) { error("open(/dev/urandom): %s", strerror(errno)); return bt_shell_noninteractive_quit(EXIT_FAILURE); } if (read(fd, cp.irk, sizeof(cp.irk)) != sizeof(cp.irk)) { error("Reading from urandom failed"); close(fd); return bt_shell_noninteractive_quit(EXIT_FAILURE); } close(fd); } if (send_cmd(mgmt, MGMT_OP_SET_PRIVACY, index, sizeof(cp), &cp, setting_rsp) == 0) { error("Unable to send Set Privacy command"); return bt_shell_noninteractive_quit(EXIT_FAILURE); } } static void exp_offload_rsp(uint8_t status, uint16_t len, const void *param, void *user_data) { if (status != 0) error("Set offload codec failed with status 0x%02x (%s)", status, mgmt_errstr(status)); else print("Offload codec feature successfully set"); bt_shell_noninteractive_quit(EXIT_SUCCESS); } static void cmd_exp_offload_codecs(int argc, char **argv) { /* a6695ace-ee7f-4fb9-881a-5fac66c629af */ static const uint8_t uuid[16] = { 0xaf, 0x29, 0xc6, 0x66, 0xac, 0x5f, 0x1a, 0x88, 0xb9, 0x4f, 0x7f, 0xee, 0xce, 0x5a, 0x69, 0xa6, }; struct mgmt_cp_set_exp_feature cp; uint8_t val; uint16_t index; if (parse_setting(argc, argv, &val) == false) return bt_shell_noninteractive_quit(EXIT_FAILURE); index = mgmt_index; if (index == MGMT_INDEX_NONE) index = 0; memset(&cp, 0, sizeof(cp)); memcpy(cp.uuid, uuid, 16); cp.action = val; if (mgmt_send(mgmt, MGMT_OP_SET_EXP_FEATURE, index, sizeof(cp), &cp, exp_offload_rsp, NULL, NULL) == 0) { error("Unable to send offload codecs feature cmd"); return bt_shell_noninteractive_quit(EXIT_FAILURE); } } static void class_rsp(uint16_t op, uint16_t id, uint8_t status, uint16_t len, const void *param) { const struct mgmt_ev_class_of_dev_changed *rp = param; if (len == 0 && status != 0) { error("%s failed, status 0x%02x (%s)", mgmt_opstr(op), status, mgmt_errstr(status)); return bt_shell_noninteractive_quit(EXIT_FAILURE); } if (len != sizeof(*rp)) { error("Unexpected %s len %u", mgmt_opstr(op), len); return bt_shell_noninteractive_quit(EXIT_FAILURE); } print("%s succeeded. Class 0x%02x%02x%02x", mgmt_opstr(op), rp->dev_class[2], rp->dev_class[1], rp->dev_class[0]); bt_shell_noninteractive_quit(EXIT_SUCCESS); } static void cmd_class(int argc, char **argv) { uint8_t class[2]; uint16_t index; class[0] = atoi(argv[1]); class[1] = atoi(argv[2]); index = mgmt_index; if (index == MGMT_INDEX_NONE) index = 0; if (send_cmd(mgmt, MGMT_OP_SET_DEV_CLASS, index, sizeof(class), class, class_rsp) == 0) { error("Unable to send set_dev_class cmd"); return bt_shell_noninteractive_quit(EXIT_FAILURE); } } static void disconnect_rsp(uint8_t status, uint16_t len, const void *param, void *user_data) { const struct mgmt_rp_disconnect *rp = param; char addr[18]; if (len == 0 && status != 0) { error("Disconnect failed with status 0x%02x (%s)", status, mgmt_errstr(status)); return bt_shell_noninteractive_quit(EXIT_FAILURE); } if (len != sizeof(*rp)) { error("Invalid disconnect response length (%u)", len); return bt_shell_noninteractive_quit(EXIT_FAILURE); } ba2str(&rp->addr.bdaddr, addr); if (status == 0) print("%s disconnected", addr); else error("Disconnecting %s failed with status 0x%02x (%s)", addr, status, mgmt_errstr(status)); bt_shell_noninteractive_quit(EXIT_SUCCESS); } static const struct option disconnect_options[] = { { "help", 0, 0, 'h' }, { "type", 1, 0, 't' }, { 0, 0, 0, 0 } }; static void cmd_disconnect(int argc, char **argv) { struct mgmt_cp_disconnect cp; uint8_t type = BDADDR_BREDR; int opt; uint16_t index; while ((opt = getopt_long(argc, argv, "+t:h", disconnect_options, NULL)) != -1) { switch (opt) { case 't': type = strtol(optarg, NULL, 0); break; case 'h': bt_shell_usage(); optind = 0; return bt_shell_noninteractive_quit(EXIT_SUCCESS); default: bt_shell_usage(); optind = 0; return bt_shell_noninteractive_quit(EXIT_FAILURE); } } argv += optind; optind = 0; index = mgmt_index; if (index == MGMT_INDEX_NONE) index = 0; memset(&cp, 0, sizeof(cp)); str2ba(argv[0], &cp.addr.bdaddr); cp.addr.type = type; if (mgmt_send(mgmt, MGMT_OP_DISCONNECT, index, sizeof(cp), &cp, disconnect_rsp, NULL, NULL) == 0) { error("Unable to send disconnect cmd"); return bt_shell_noninteractive_quit(EXIT_FAILURE); } } static void con_rsp(uint8_t status, uint16_t len, const void *param, void *user_data) { const struct mgmt_rp_get_connections *rp = param; uint16_t count, i; if (len < sizeof(*rp)) { error("Too small (%u bytes) get_connections rsp", len); return bt_shell_noninteractive_quit(EXIT_FAILURE); } count = get_le16(&rp->conn_count); if (len != sizeof(*rp) + count * sizeof(struct mgmt_addr_info)) { error("Invalid get_connections length (count=%u, len=%u)", count, len); return bt_shell_noninteractive_quit(EXIT_FAILURE); } for (i = 0; i < count; i++) { char addr[18]; ba2str(&rp->addr[i].bdaddr, addr); print("%s type %s", addr, typestr(rp->addr[i].type)); } bt_shell_noninteractive_quit(EXIT_SUCCESS); } static void cmd_con(int argc, char **argv) { uint16_t index; index = mgmt_index; if (index == MGMT_INDEX_NONE) index = 0; if (mgmt_send(mgmt, MGMT_OP_GET_CONNECTIONS, index, 0, NULL, con_rsp, NULL, NULL) == 0) { error("Unable to send get_connections cmd"); return bt_shell_noninteractive_quit(EXIT_FAILURE); } } static void find_service_rsp(uint8_t status, uint16_t len, const void *param, void *user_data) { if (status != 0) { error("Start Service Discovery failed: status 0x%02x (%s)", status, mgmt_errstr(status)); return bt_shell_noninteractive_quit(EXIT_FAILURE); } print("Service discovery started"); discovery = true; } static const struct option find_service_options[] = { { "help", no_argument, 0, 'h' }, { "le-only", no_argument, 0, 'l' }, { "bredr-only", no_argument, 0, 'b' }, { "uuid", required_argument, 0, 'u' }, { "rssi", required_argument, 0, 'r' }, { 0, 0, 0, 0 } }; #define MAX_UUIDS 4 static void cmd_find_service(int argc, char **argv) { struct mgmt_cp_start_service_discovery *cp; uint8_t buf[sizeof(*cp) + 16 * MAX_UUIDS]; bt_uuid_t uuid; uint8_t type = SCAN_TYPE_DUAL; int8_t rssi; uint16_t count; int opt; uint16_t index; index = mgmt_index; if (index == MGMT_INDEX_NONE) index = 0; rssi = 127; count = 0; while ((opt = getopt_long(argc, argv, "+lbu:r:h", find_service_options, NULL)) != -1) { switch (opt) { case 'l': type &= ~SCAN_TYPE_BREDR; type |= SCAN_TYPE_LE; break; case 'b': type |= SCAN_TYPE_BREDR; type &= ~SCAN_TYPE_LE; break; case 'u': if (count == MAX_UUIDS) { print("Max %u UUIDs supported", MAX_UUIDS); optind = 0; return bt_shell_noninteractive_quit(EXIT_FAILURE); } if (bt_string_to_uuid(&uuid, optarg) < 0) { print("Invalid UUID: %s", optarg); optind = 0; return bt_shell_noninteractive_quit(EXIT_FAILURE); } cp = (void *) buf; bt_uuid_to_le(&uuid, cp->uuids[count++]); break; case 'r': rssi = atoi(optarg); break; case 'h': bt_shell_usage(); optind = 0; return bt_shell_noninteractive_quit(EXIT_SUCCESS); default: bt_shell_usage(); optind = 0; return bt_shell_noninteractive_quit(EXIT_FAILURE); } } optind = 0; cp = (void *) buf; cp->type = type; cp->rssi = rssi; cp->uuid_count = cpu_to_le16(count); if (mgmt_send(mgmt, MGMT_OP_START_SERVICE_DISCOVERY, index, sizeof(*cp) + count * 16, cp, find_service_rsp, NULL, NULL) == 0) { error("Unable to send start_service_discovery cmd"); return bt_shell_noninteractive_quit(EXIT_FAILURE); } } static void find_rsp(uint8_t status, uint16_t len, const void *param, void *user_data) { if (status != 0) { error("Unable to start discovery. status 0x%02x (%s)", status, mgmt_errstr(status)); return bt_shell_noninteractive_quit(EXIT_FAILURE); } print("Discovery started"); discovery = true; } static const struct option find_options[] = { { "help", 0, 0, 'h' }, { "le-only", 1, 0, 'l' }, { "bredr-only", 1, 0, 'b' }, { "limited", 1, 0, 'L' }, { 0, 0, 0, 0 } }; static void cmd_find(int argc, char **argv) { struct mgmt_cp_start_discovery cp; uint8_t op = MGMT_OP_START_DISCOVERY; uint8_t type = SCAN_TYPE_DUAL; int opt; uint16_t index; index = mgmt_index; if (index == MGMT_INDEX_NONE) index = 0; while ((opt = getopt_long(argc, argv, "+lbLh", find_options, NULL)) != -1) { switch (opt) { case 'l': type &= ~SCAN_TYPE_BREDR; type |= SCAN_TYPE_LE; break; case 'b': type |= SCAN_TYPE_BREDR; type &= ~SCAN_TYPE_LE; break; case 'L': op = MGMT_OP_START_LIMITED_DISCOVERY; break; case 'h': bt_shell_usage(); optind = 0; return bt_shell_noninteractive_quit(EXIT_SUCCESS); default: bt_shell_usage(); optind = 0; return bt_shell_noninteractive_quit(EXIT_FAILURE); } } optind = 0; memset(&cp, 0, sizeof(cp)); cp.type = type; if (mgmt_send(mgmt, op, index, sizeof(cp), &cp, find_rsp, NULL, NULL) == 0) { error("Unable to send start_discovery cmd"); return bt_shell_noninteractive_quit(EXIT_FAILURE); } } static void stop_find_rsp(uint8_t status, uint16_t len, const void *param, void *user_data) { if (status != 0) { error("Stop Discovery failed: status 0x%02x (%s)", status, mgmt_errstr(status)); return bt_shell_noninteractive_quit(EXIT_SUCCESS); } print("Discovery stopped"); discovery = false; bt_shell_noninteractive_quit(EXIT_SUCCESS); } static const struct option stop_find_options[] = { { "help", 0, 0, 'h' }, { "le-only", 1, 0, 'l' }, { "bredr-only", 1, 0, 'b' }, { 0, 0, 0, 0 } }; static void cmd_stop_find(int argc, char **argv) { struct mgmt_cp_stop_discovery cp; uint8_t type = SCAN_TYPE_DUAL; int opt; uint16_t index; index = mgmt_index; if (index == MGMT_INDEX_NONE) index = 0; while ((opt = getopt_long(argc, argv, "+lbh", stop_find_options, NULL)) != -1) { switch (opt) { case 'l': type &= ~SCAN_TYPE_BREDR; type |= SCAN_TYPE_LE; break; case 'b': type |= SCAN_TYPE_BREDR; type &= ~SCAN_TYPE_LE; break; case 'h': default: bt_shell_usage(); optind = 0; return bt_shell_noninteractive_quit(EXIT_SUCCESS); } } optind = 0; memset(&cp, 0, sizeof(cp)); cp.type = type; if (mgmt_send(mgmt, MGMT_OP_STOP_DISCOVERY, index, sizeof(cp), &cp, stop_find_rsp, NULL, NULL) == 0) { error("Unable to send stop_discovery cmd"); return bt_shell_noninteractive_quit(EXIT_FAILURE); } } static void name_rsp(uint8_t status, uint16_t len, const void *param, void *user_data) { if (status != 0) error("Unable to set local name with status 0x%02x (%s)", status, mgmt_errstr(status)); bt_shell_noninteractive_quit(EXIT_SUCCESS); } static void cmd_name(int argc, char **argv) { struct mgmt_cp_set_local_name cp; uint16_t index; index = mgmt_index; if (index == MGMT_INDEX_NONE) index = 0; memset(&cp, 0, sizeof(cp)); strncpy((char *) cp.name, argv[1], HCI_MAX_NAME_LENGTH); if (argc > 2) strncpy((char *) cp.short_name, argv[2], MGMT_MAX_SHORT_NAME_LENGTH - 1); if (mgmt_send(mgmt, MGMT_OP_SET_LOCAL_NAME, index, sizeof(cp), &cp, name_rsp, NULL, NULL) == 0) { error("Unable to send set_name cmd"); return bt_shell_noninteractive_quit(EXIT_FAILURE); } } static void pair_rsp(uint8_t status, uint16_t len, const void *param, void *user_data) { const struct mgmt_rp_pair_device *rp = param; char addr[18]; if (len == 0 && status != 0) { error("Pairing failed with status 0x%02x (%s)", status, mgmt_errstr(status)); return bt_shell_noninteractive_quit(EXIT_FAILURE); } if (len != sizeof(*rp)) { error("Unexpected pair_rsp len %u", len); return bt_shell_noninteractive_quit(EXIT_FAILURE); } ba2str(&rp->addr.bdaddr, addr); if (status) error("Pairing with %s (%s) failed. status 0x%02x (%s)", addr, typestr(rp->addr.type), status, mgmt_errstr(status)); else print("Paired with %s (%s)", addr, typestr(rp->addr.type)); bt_shell_noninteractive_quit(EXIT_SUCCESS); } static void register_pair_callbacks(struct mgmt *mgmt, uint16_t index) { mgmt_register(mgmt, MGMT_EV_PIN_CODE_REQUEST, index, request_pin, mgmt, NULL); mgmt_register(mgmt, MGMT_EV_USER_CONFIRM_REQUEST, index, user_confirm, mgmt, NULL); mgmt_register(mgmt, MGMT_EV_USER_PASSKEY_REQUEST, index, request_passkey, mgmt, NULL); mgmt_register(mgmt, MGMT_EV_PASSKEY_NOTIFY, index, passkey_notify, mgmt, NULL); } static const struct option pair_options[] = { { "help", 0, 0, 'h' }, { "capability", 1, 0, 'c' }, { "type", 1, 0, 't' }, { 0, 0, 0, 0 } }; static void cmd_pair(int argc, char **argv) { struct mgmt_cp_pair_device cp; uint8_t cap = 0x01; uint8_t type = BDADDR_BREDR; char addr[18]; int opt; uint16_t index; while ((opt = getopt_long(argc, argv, "+c:t:h", pair_options, NULL)) != -1) { switch (opt) { case 'c': cap = strtol(optarg, NULL, 0); break; case 't': type = strtol(optarg, NULL, 0); break; case 'h': bt_shell_usage(); optind = 0; return bt_shell_noninteractive_quit(EXIT_SUCCESS); default: bt_shell_usage(); optind = 0; return bt_shell_noninteractive_quit(EXIT_FAILURE); } } argc -= optind; argv += optind; optind = 0; if (argc < 1) { bt_shell_usage(); return bt_shell_noninteractive_quit(EXIT_FAILURE); } index = mgmt_index; if (index == MGMT_INDEX_NONE) index = 0; register_pair_callbacks(mgmt, index); memset(&cp, 0, sizeof(cp)); str2ba(argv[0], &cp.addr.bdaddr); cp.addr.type = type; cp.io_cap = cap; ba2str(&cp.addr.bdaddr, addr); print("Pairing with %s (%s)", addr, typestr(cp.addr.type)); if (mgmt_send(mgmt, MGMT_OP_PAIR_DEVICE, index, sizeof(cp), &cp, pair_rsp, NULL, NULL) == 0) { error("Unable to send pair_device cmd"); return bt_shell_noninteractive_quit(EXIT_FAILURE); } } static void cancel_pair_rsp(uint8_t status, uint16_t len, const void *param, void *user_data) { const struct mgmt_addr_info *rp = param; char addr[18]; if (len == 0 && status != 0) { error("Cancel Pairing failed with 0x%02x (%s)", status, mgmt_errstr(status)); return bt_shell_noninteractive_quit(EXIT_FAILURE); } if (len != sizeof(*rp)) { error("Unexpected cancel_pair_rsp len %u", len); return bt_shell_noninteractive_quit(EXIT_FAILURE); } ba2str(&rp->bdaddr, addr); if (status) error("Cancel Pairing with %s (%s) failed. 0x%02x (%s)", addr, typestr(rp->type), status, mgmt_errstr(status)); else print("Pairing Cancelled with %s", addr); bt_shell_noninteractive_quit(EXIT_SUCCESS); } static const struct option cancel_pair_options[] = { { "help", 0, 0, 'h' }, { "type", 1, 0, 't' }, { 0, 0, 0, 0 } }; static void cmd_cancel_pair(int argc, char **argv) { struct mgmt_addr_info cp; uint8_t type = BDADDR_BREDR; int opt; uint16_t index; while ((opt = getopt_long(argc, argv, "+t:h", cancel_pair_options, NULL)) != -1) { switch (opt) { case 't': type = strtol(optarg, NULL, 0); break; case 'h': bt_shell_usage(); optind = 0; return bt_shell_noninteractive_quit(EXIT_SUCCESS); default: bt_shell_usage(); optind = 0; return bt_shell_noninteractive_quit(EXIT_FAILURE); } } argc -= optind; argv += optind; optind = 0; if (argc < 1) { bt_shell_usage(); return bt_shell_noninteractive_quit(EXIT_FAILURE); } index = mgmt_index; if (index == MGMT_INDEX_NONE) index = 0; memset(&cp, 0, sizeof(cp)); str2ba(argv[0], &cp.bdaddr); cp.type = type; if (mgmt_reply(mgmt, MGMT_OP_CANCEL_PAIR_DEVICE, index, sizeof(cp), &cp, cancel_pair_rsp, NULL, NULL) == 0) { error("Unable to send cancel_pair_device cmd"); return bt_shell_noninteractive_quit(EXIT_FAILURE); } } static void unpair_rsp(uint8_t status, uint16_t len, const void *param, void *user_data) { const struct mgmt_rp_unpair_device *rp = param; char addr[18]; if (len == 0 && status != 0) { error("Unpair device failed. status 0x%02x (%s)", status, mgmt_errstr(status)); return bt_shell_noninteractive_quit(EXIT_FAILURE); } if (len != sizeof(*rp)) { error("Unexpected unpair_device_rsp len %u", len); return bt_shell_noninteractive_quit(EXIT_FAILURE); } ba2str(&rp->addr.bdaddr, addr); if (status) error("Unpairing %s failed. status 0x%02x (%s)", addr, status, mgmt_errstr(status)); else print("%s unpaired", addr); bt_shell_noninteractive_quit(EXIT_SUCCESS); } static const struct option unpair_options[] = { { "help", 0, 0, 'h' }, { "type", 1, 0, 't' }, { 0, 0, 0, 0 } }; static void cmd_unpair(int argc, char **argv) { struct mgmt_cp_unpair_device cp; uint8_t type = BDADDR_BREDR; int opt; uint16_t index; while ((opt = getopt_long(argc, argv, "+t:h", unpair_options, NULL)) != -1) { switch (opt) { case 't': type = strtol(optarg, NULL, 0); break; case 'h': bt_shell_usage(); optind = 0; return bt_shell_noninteractive_quit(EXIT_SUCCESS); default: bt_shell_usage(); optind = 0; return bt_shell_noninteractive_quit(EXIT_FAILURE); } } argc -= optind; argv += optind; optind = 0; if (argc < 1) { bt_shell_usage(); return bt_shell_noninteractive_quit(EXIT_FAILURE); } index = mgmt_index; if (index == MGMT_INDEX_NONE) index = 0; memset(&cp, 0, sizeof(cp)); str2ba(argv[0], &cp.addr.bdaddr); cp.addr.type = type; cp.disconnect = 1; if (mgmt_send(mgmt, MGMT_OP_UNPAIR_DEVICE, index, sizeof(cp), &cp, unpair_rsp, NULL, NULL) == 0) { error("Unable to send unpair_device cmd"); return bt_shell_noninteractive_quit(EXIT_FAILURE); } } static void keys_rsp(uint8_t status, uint16_t len, const void *param, void *user_data) { if (status != 0) error("Load keys failed with status 0x%02x (%s)", status, mgmt_errstr(status)); else print("Keys successfully loaded"); bt_shell_noninteractive_quit(EXIT_SUCCESS); } static void cmd_keys(int argc, char **argv) { struct mgmt_cp_load_link_keys cp; uint16_t index; index = mgmt_index; if (index == MGMT_INDEX_NONE) index = 0; memset(&cp, 0, sizeof(cp)); if (mgmt_send(mgmt, MGMT_OP_LOAD_LINK_KEYS, index, sizeof(cp), &cp, keys_rsp, NULL, NULL) == 0) { error("Unable to send load_keys cmd"); return bt_shell_noninteractive_quit(EXIT_FAILURE); } } static void ltks_rsp(uint8_t status, uint16_t len, const void *param, void *user_data) { if (status != 0) error("Load keys failed with status 0x%02x (%s)", status, mgmt_errstr(status)); else print("Long term keys successfully loaded"); bt_shell_noninteractive_quit(EXIT_SUCCESS); } static void cmd_ltks(int argc, char **argv) { struct mgmt_cp_load_long_term_keys cp; uint16_t index; index = mgmt_index; if (index == MGMT_INDEX_NONE) index = 0; memset(&cp, 0, sizeof(cp)); if (mgmt_send(mgmt, MGMT_OP_LOAD_LONG_TERM_KEYS, index, sizeof(cp), &cp, ltks_rsp, NULL, NULL) == 0) { error("Unable to send load_ltks cmd"); return bt_shell_noninteractive_quit(EXIT_SUCCESS); } } static void irks_rsp(uint8_t status, uint16_t len, const void *param, void *user_data) { if (status != 0) error("Load IRKs failed with status 0x%02x (%s)", status, mgmt_errstr(status)); else print("Identity Resolving Keys successfully loaded"); bt_shell_noninteractive_quit(EXIT_SUCCESS); } static const struct option irks_options[] = { { "help", 0, 0, 'h' }, { "local", 1, 0, 'l' }, { "file", 1, 0, 'f' }, { 0, 0, 0, 0 } }; #define MAX_IRKS 4 static void cmd_irks(int argc, char **argv) { struct mgmt_cp_load_irks *cp; uint8_t buf[sizeof(*cp) + 23 * MAX_IRKS]; uint16_t count, local_index; char path[PATH_MAX]; int opt; uint16_t index; index = mgmt_index; if (index == MGMT_INDEX_NONE) index = 0; cp = (void *) buf; count = 0; while ((opt = getopt_long(argc, argv, "+l:f:h", irks_options, NULL)) != -1) { switch (opt) { case 'l': if (count >= MAX_IRKS) { error("Number of IRKs exceeded"); optind = 0; return bt_shell_noninteractive_quit(EXIT_FAILURE); } if (strlen(optarg) > 3 && strncasecmp(optarg, "hci", 3) == 0) local_index = atoi(optarg + 3); else local_index = atoi(optarg); snprintf(path, sizeof(path), "/sys/kernel/debug/bluetooth/hci%u/identity", local_index); if (!load_identity(path, &cp->irks[count])) { error("Unable to load identity"); optind = 0; return bt_shell_noninteractive_quit(EXIT_FAILURE); } count++; break; case 'f': if (count >= MAX_IRKS) { error("Number of IRKs exceeded"); optind = 0; return bt_shell_noninteractive_quit(EXIT_FAILURE); } if (!load_identity(optarg, &cp->irks[count])) { error("Unable to load identities"); optind = 0; return bt_shell_noninteractive_quit(EXIT_FAILURE); } count++; break; case 'h': bt_shell_usage(); optind = 0; return bt_shell_noninteractive_quit(EXIT_SUCCESS); default: bt_shell_usage(); optind = 0; return bt_shell_noninteractive_quit(EXIT_FAILURE); } } optind = 0; cp->irk_count = cpu_to_le16(count); if (mgmt_send(mgmt, MGMT_OP_LOAD_IRKS, index, sizeof(*cp) + count * 23, cp, irks_rsp, NULL, NULL) == 0) { error("Unable to send load_irks cmd"); return bt_shell_noninteractive_quit(EXIT_FAILURE); } } static void block_rsp(uint16_t op, uint16_t id, uint8_t status, uint16_t len, const void *param) { const struct mgmt_addr_info *rp = param; char addr[18]; if (len == 0 && status != 0) { error("%s failed, status 0x%02x (%s)", mgmt_opstr(op), status, mgmt_errstr(status)); return bt_shell_noninteractive_quit(EXIT_FAILURE); } if (len != sizeof(*rp)) { error("Unexpected %s len %u", mgmt_opstr(op), len); return bt_shell_noninteractive_quit(EXIT_FAILURE); } ba2str(&rp->bdaddr, addr); if (status) error("%s %s (%s) failed. status 0x%02x (%s)", mgmt_opstr(op), addr, typestr(rp->type), status, mgmt_errstr(status)); else print("%s %s succeeded", mgmt_opstr(op), addr); bt_shell_noninteractive_quit(EXIT_SUCCESS); } static const struct option block_options[] = { { "help", 0, 0, 'h' }, { "type", 1, 0, 't' }, { 0, 0, 0, 0 } }; static void cmd_block(int argc, char **argv) { struct mgmt_cp_block_device cp; uint8_t type = BDADDR_BREDR; int opt; uint16_t index; while ((opt = getopt_long(argc, argv, "+t:h", block_options, NULL)) != -1) { switch (opt) { case 't': type = strtol(optarg, NULL, 0); break; case 'h': bt_shell_usage(); optind = 0; return bt_shell_noninteractive_quit(EXIT_SUCCESS); default: bt_shell_usage(); optind = 0; return bt_shell_noninteractive_quit(EXIT_FAILURE); } } argc -= optind; argv += optind; optind = 0; if (argc < 1) { bt_shell_usage(); return bt_shell_noninteractive_quit(EXIT_FAILURE); } index = mgmt_index; if (index == MGMT_INDEX_NONE) index = 0; memset(&cp, 0, sizeof(cp)); str2ba(argv[0], &cp.addr.bdaddr); cp.addr.type = type; if (send_cmd(mgmt, MGMT_OP_BLOCK_DEVICE, index, sizeof(cp), &cp, block_rsp) == 0) { error("Unable to send block_device cmd"); return bt_shell_noninteractive_quit(EXIT_FAILURE); } } static void cmd_unblock(int argc, char **argv) { struct mgmt_cp_unblock_device cp; uint8_t type = BDADDR_BREDR; int opt; uint16_t index; while ((opt = getopt_long(argc, argv, "+t:h", block_options, NULL)) != -1) { switch (opt) { case 't': type = strtol(optarg, NULL, 0); break; case 'h': bt_shell_usage(); optind = 0; return bt_shell_noninteractive_quit(EXIT_SUCCESS); default: bt_shell_usage(); optind = 0; return bt_shell_noninteractive_quit(EXIT_FAILURE); } } argc -= optind; argv += optind; optind = 0; if (argc < 1) { bt_shell_usage(); return bt_shell_noninteractive_quit(EXIT_FAILURE); } index = mgmt_index; if (index == MGMT_INDEX_NONE) index = 0; memset(&cp, 0, sizeof(cp)); str2ba(argv[0], &cp.addr.bdaddr); cp.addr.type = type; if (send_cmd(mgmt, MGMT_OP_UNBLOCK_DEVICE, index, sizeof(cp), &cp, block_rsp) == 0) { error("Unable to send unblock_device cmd"); return bt_shell_noninteractive_quit(EXIT_FAILURE); } } static void cmd_add_uuid(int argc, char **argv) { struct mgmt_cp_add_uuid cp; bt_uuid_t uuid; uint16_t index; if (argc < 3) { print("UUID and service hint needed"); return bt_shell_noninteractive_quit(EXIT_FAILURE); } index = mgmt_index; if (index == MGMT_INDEX_NONE) index = 0; if (bt_string_to_uuid(&uuid, argv[1]) < 0) { print("Invalid UUID: %s", argv[1]); return bt_shell_noninteractive_quit(EXIT_FAILURE); } memset(&cp, 0, sizeof(cp)); bt_uuid_to_le(&uuid, cp.uuid); cp.svc_hint = atoi(argv[2]); if (send_cmd(mgmt, MGMT_OP_ADD_UUID, index, sizeof(cp), &cp, class_rsp) == 0) { error("Unable to send add_uuid cmd"); return bt_shell_noninteractive_quit(EXIT_FAILURE); } } static void cmd_remove_uuid(int argc, char **argv) { struct mgmt_cp_remove_uuid cp; bt_uuid_t uuid; uint16_t index; if (argc < 2) { print("UUID needed"); return bt_shell_noninteractive_quit(EXIT_FAILURE); } index = mgmt_index; if (index == MGMT_INDEX_NONE) index = 0; if (bt_string_to_uuid(&uuid, argv[1]) < 0) { print("Invalid UUID: %s", argv[1]); return bt_shell_noninteractive_quit(EXIT_FAILURE); } memset(&cp, 0, sizeof(cp)); bt_uuid_to_le(&uuid, cp.uuid); if (send_cmd(mgmt, MGMT_OP_REMOVE_UUID, index, sizeof(cp), &cp, class_rsp) == 0) { error("Unable to send remove_uuid cmd"); return bt_shell_noninteractive_quit(EXIT_FAILURE); } } static void cmd_clr_uuids(int argc, char **argv) { char *uuid_any = "00000000-0000-0000-0000-000000000000"; char *rm_argv[] = { "rm-uuid", uuid_any, NULL }; cmd_remove_uuid(2, rm_argv); } static void local_oob_rsp(uint8_t status, uint16_t len, const void *param, void *user_data) { const struct mgmt_rp_read_local_oob_data *rp = param; char str[33]; if (status != 0) { error("Read Local OOB Data failed with status 0x%02x (%s)", status, mgmt_errstr(status)); return bt_shell_noninteractive_quit(EXIT_FAILURE); } if (len < sizeof(*rp)) { error("Too small (%u bytes) read_local_oob rsp", len); return bt_shell_noninteractive_quit(EXIT_FAILURE); } bin2hex(rp->hash192, 16, str, sizeof(str)); print("Hash C from P-192: %s", str); bin2hex(rp->rand192, 16, str, sizeof(str)); print("Randomizer R with P-192: %s", str); if (len < sizeof(*rp)) return bt_shell_noninteractive_quit(EXIT_SUCCESS); bin2hex(rp->hash256, 16, str, sizeof(str)); print("Hash C from P-256: %s", str); bin2hex(rp->rand256, 16, str, sizeof(str)); print("Randomizer R with P-256: %s", str); bt_shell_noninteractive_quit(EXIT_SUCCESS); } static void cmd_local_oob(int argc, char **argv) { uint16_t index; index = mgmt_index; if (index == MGMT_INDEX_NONE) index = 0; if (mgmt_send(mgmt, MGMT_OP_READ_LOCAL_OOB_DATA, index, 0, NULL, local_oob_rsp, NULL, NULL) == 0) { error("Unable to send read_local_oob cmd"); return bt_shell_noninteractive_quit(EXIT_FAILURE); } } static void remote_oob_rsp(uint8_t status, uint16_t len, const void *param, void *user_data) { const struct mgmt_addr_info *rp = param; char addr[18]; if (status != 0) { error("Add Remote OOB Data failed: 0x%02x (%s)", status, mgmt_errstr(status)); return; } if (len < sizeof(*rp)) { error("Too small (%u bytes) add_remote_oob rsp", len); return; } ba2str(&rp->bdaddr, addr); print("Remote OOB data added for %s (%u)", addr, rp->type); } static const struct option remote_oob_opt[] = { { "help", 0, 0, '?' }, { "type", 1, 0, 't' }, { 0, 0, 0, 0 } }; static void cmd_remote_oob(int argc, char **argv) { struct mgmt_cp_add_remote_oob_data cp; int opt; uint16_t index; memset(&cp, 0, sizeof(cp)); cp.addr.type = BDADDR_BREDR; while ((opt = getopt_long(argc, argv, "+t:r:R:h:H:", remote_oob_opt, NULL)) != -1) { switch (opt) { case 't': cp.addr.type = strtol(optarg, NULL, 0); break; case 'r': hex2bin(optarg, cp.rand192, 16); break; case 'h': hex2bin(optarg, cp.hash192, 16); break; case 'R': hex2bin(optarg, cp.rand256, 16); break; case 'H': hex2bin(optarg, cp.hash256, 16); break; default: bt_shell_usage(); optind = 0; return bt_shell_noninteractive_quit(EXIT_FAILURE); } } argc -= optind; argv += optind; optind = 0; if (argc < 1) { bt_shell_usage(); return bt_shell_noninteractive_quit(EXIT_FAILURE); } index = mgmt_index; if (index == MGMT_INDEX_NONE) index = 0; str2ba(argv[0], &cp.addr.bdaddr); print("Adding OOB data for %s (%s)", argv[0], typestr(cp.addr.type)); if (mgmt_send(mgmt, MGMT_OP_ADD_REMOTE_OOB_DATA, index, sizeof(cp), &cp, remote_oob_rsp, NULL, NULL) == 0) { error("Unable to send add_remote_oob cmd"); return bt_shell_noninteractive_quit(EXIT_FAILURE); } } static void did_rsp(uint8_t status, uint16_t len, const void *param, void *user_data) { if (status != 0) error("Set Device ID failed with status 0x%02x (%s)", status, mgmt_errstr(status)); else print("Device ID successfully set"); bt_shell_noninteractive_quit(EXIT_SUCCESS); } static void cmd_did(int argc, char **argv) { struct mgmt_cp_set_device_id cp; uint16_t vendor, product, version , source; int result; uint16_t index; result = sscanf(argv[1], "bluetooth:%4hx:%4hx:%4hx", &vendor, &product, &version); if (result == 3) { source = 0x0001; goto done; } result = sscanf(argv[1], "usb:%4hx:%4hx:%4hx", &vendor, &product, &version); if (result == 3) { source = 0x0002; goto done; } return; done: index = mgmt_index; if (index == MGMT_INDEX_NONE) index = 0; cp.source = htobs(source); cp.vendor = htobs(vendor); cp.product = htobs(product); cp.version = htobs(version); if (mgmt_send(mgmt, MGMT_OP_SET_DEVICE_ID, index, sizeof(cp), &cp, did_rsp, NULL, NULL) == 0) { error("Unable to send set_device_id cmd"); return bt_shell_noninteractive_quit(EXIT_FAILURE); } } static void static_addr_rsp(uint8_t status, uint16_t len, const void *param, void *user_data) { if (status != 0) error("Set static address failed with status 0x%02x (%s)", status, mgmt_errstr(status)); else print("Static address successfully set"); bt_shell_noninteractive_quit(EXIT_SUCCESS); } static void cmd_static_addr(int argc, char **argv) { struct mgmt_cp_set_static_address cp; uint16_t index; index = mgmt_index; if (index == MGMT_INDEX_NONE) index = 0; str2ba(argv[1], &cp.bdaddr); if (mgmt_send(mgmt, MGMT_OP_SET_STATIC_ADDRESS, index, sizeof(cp), &cp, static_addr_rsp, NULL, NULL) == 0) { error("Unable to send set_static_address cmd"); return bt_shell_noninteractive_quit(EXIT_FAILURE); } } static void options_rsp(uint16_t op, uint16_t id, uint8_t status, uint16_t len, const void *param) { const uint32_t *rp = param; if (status != 0) { error("%s for hci%u failed with status 0x%02x (%s)", mgmt_opstr(op), id, status, mgmt_errstr(status)); return bt_shell_noninteractive_quit(EXIT_FAILURE); } if (len < sizeof(*rp)) { error("Too small %s response (%u bytes)", mgmt_opstr(op), len); return bt_shell_noninteractive_quit(EXIT_FAILURE); } print("hci%u %s complete, options: %s", id, mgmt_opstr(op), options2str(get_le32(rp))); bt_shell_noninteractive_quit(EXIT_SUCCESS); } static void cmd_public_addr(int argc, char **argv) { struct mgmt_cp_set_public_address cp; uint16_t index; index = mgmt_index; if (index == MGMT_INDEX_NONE) index = 0; str2ba(argv[1], &cp.bdaddr); if (send_cmd(mgmt, MGMT_OP_SET_PUBLIC_ADDRESS, index, sizeof(cp), &cp, options_rsp) == 0) { error("Unable to send Set Public Address cmd"); return bt_shell_noninteractive_quit(EXIT_FAILURE); } } static void cmd_ext_config(int argc, char **argv) { struct mgmt_cp_set_external_config cp; uint16_t index; if (parse_setting(argc, argv, &cp.config) == false) return bt_shell_noninteractive_quit(EXIT_FAILURE); index = mgmt_index; if (index == MGMT_INDEX_NONE) index = 0; if (send_cmd(mgmt, MGMT_OP_SET_EXTERNAL_CONFIG, index, sizeof(cp), &cp, options_rsp) == 0) { error("Unable to send Set External Config cmd"); return bt_shell_noninteractive_quit(EXIT_FAILURE); } } static void cmd_debug_keys(int argc, char **argv) { cmd_setting(MGMT_OP_SET_DEBUG_KEYS, argc, argv); } static void conn_info_rsp(uint8_t status, uint16_t len, const void *param, void *user_data) { const struct mgmt_rp_get_conn_info *rp = param; char addr[18]; if (len == 0 && status != 0) { error("Get Conn Info failed, status 0x%02x (%s)", status, mgmt_errstr(status)); return bt_shell_noninteractive_quit(EXIT_FAILURE); } if (len < sizeof(*rp)) { error("Unexpected Get Conn Info len %u", len); return bt_shell_noninteractive_quit(EXIT_FAILURE); } ba2str(&rp->addr.bdaddr, addr); if (status) { error("Get Conn Info for %s (%s) failed. status 0x%02x (%s)", addr, typestr(rp->addr.type), status, mgmt_errstr(status)); } else { print("Connection Information for %s (%s)", addr, typestr(rp->addr.type)); print("\tRSSI %d\tTX power %d\tmaximum TX power %d", rp->rssi, rp->tx_power, rp->max_tx_power); } bt_shell_noninteractive_quit(EXIT_SUCCESS); } static const struct option conn_info_options[] = { { "help", 0, 0, 'h' }, { "type", 1, 0, 't' }, { 0, 0, 0, 0 } }; static void cmd_conn_info(int argc, char **argv) { struct mgmt_cp_get_conn_info cp; uint8_t type = BDADDR_BREDR; int opt; uint16_t index; while ((opt = getopt_long(argc, argv, "+t:h", conn_info_options, NULL)) != -1) { switch (opt) { case 't': type = strtol(optarg, NULL, 0); break; case 'h': bt_shell_usage(); optind = 0; return bt_shell_noninteractive_quit(EXIT_SUCCESS); default: bt_shell_usage(); optind = 0; return bt_shell_noninteractive_quit(EXIT_FAILURE); } } argc -= optind; argv += optind; optind = 0; if (argc < 1) { bt_shell_usage(); return bt_shell_noninteractive_quit(EXIT_FAILURE); } index = mgmt_index; if (index == MGMT_INDEX_NONE) index = 0; memset(&cp, 0, sizeof(cp)); str2ba(argv[0], &cp.addr.bdaddr); cp.addr.type = type; if (mgmt_send(mgmt, MGMT_OP_GET_CONN_INFO, index, sizeof(cp), &cp, conn_info_rsp, NULL, NULL) == 0) { error("Unable to send get_conn_info cmd"); return bt_shell_noninteractive_quit(EXIT_FAILURE); } } static void io_cap_rsp(uint8_t status, uint16_t len, const void *param, void *user_data) { if (status != 0) error("Could not set IO Capability with status 0x%02x (%s)", status, mgmt_errstr(status)); else print("IO Capabilities successfully set"); bt_shell_noninteractive_quit(EXIT_SUCCESS); } static void cmd_io_cap(int argc, char **argv) { struct mgmt_cp_set_io_capability cp; uint8_t cap; uint16_t index; index = mgmt_index; if (index == MGMT_INDEX_NONE) index = 0; cap = strtol(argv[1], NULL, 0); memset(&cp, 0, sizeof(cp)); cp.io_capability = cap; if (mgmt_send(mgmt, MGMT_OP_SET_IO_CAPABILITY, index, sizeof(cp), &cp, io_cap_rsp, NULL, NULL) == 0) { error("Unable to send set-io-cap cmd"); return bt_shell_noninteractive_quit(EXIT_FAILURE); } } static void scan_params_rsp(uint8_t status, uint16_t len, const void *param, void *user_data) { if (status != 0) error("Set scan parameters failed with status 0x%02x (%s)", status, mgmt_errstr(status)); else print("Scan parameters successfully set"); bt_shell_noninteractive_quit(EXIT_SUCCESS); } static void cmd_scan_params(int argc, char **argv) { struct mgmt_cp_set_scan_params cp; uint16_t index; index = mgmt_index; if (index == MGMT_INDEX_NONE) index = 0; cp.interval = strtol(argv[1], NULL, 0); cp.window = strtol(argv[2], NULL, 0); if (mgmt_send(mgmt, MGMT_OP_SET_SCAN_PARAMS, index, sizeof(cp), &cp, scan_params_rsp, NULL, NULL) == 0) { error("Unable to send set_scan_params cmd"); return bt_shell_noninteractive_quit(EXIT_FAILURE); } } static void clock_info_rsp(uint8_t status, uint16_t len, const void *param, void *user_data) { const struct mgmt_rp_get_clock_info *rp = param; if (len < sizeof(*rp)) { error("Unexpected Get Clock Info len %u", len); return bt_shell_noninteractive_quit(EXIT_FAILURE); } if (status) { error("Get Clock Info failed with status 0x%02x (%s)", status, mgmt_errstr(status)); return bt_shell_noninteractive_quit(EXIT_FAILURE); } print("Local Clock: %u", le32_to_cpu(rp->local_clock)); print("Piconet Clock: %u", le32_to_cpu(rp->piconet_clock)); print("Accurary: %u", le16_to_cpu(rp->accuracy)); bt_shell_noninteractive_quit(EXIT_SUCCESS); } static void cmd_clock_info(int argc, char **argv) { struct mgmt_cp_get_clock_info cp; uint16_t index; index = mgmt_index; if (index == MGMT_INDEX_NONE) index = 0; memset(&cp, 0, sizeof(cp)); if (argc > 1) str2ba(argv[1], &cp.addr.bdaddr); if (mgmt_send(mgmt, MGMT_OP_GET_CLOCK_INFO, index, sizeof(cp), &cp, clock_info_rsp, NULL, NULL) == 0) { error("Unable to send get_clock_info cmd"); return bt_shell_noninteractive_quit(EXIT_FAILURE); } } static void add_device_rsp(uint8_t status, uint16_t len, const void *param, void *user_data) { if (status != 0) error("Add device failed with status 0x%02x (%s)", status, mgmt_errstr(status)); bt_shell_noninteractive_quit(EXIT_SUCCESS); } static const struct option add_device_options[] = { { "help", 0, 0, 'h' }, { "action", 1, 0, 'a' }, { "type", 1, 0, 't' }, { 0, 0, 0, 0 } }; static void cmd_add_device(int argc, char **argv) { struct mgmt_cp_add_device cp; uint8_t action = 0x00; uint8_t type = BDADDR_BREDR; char addr[18]; int opt; uint16_t index; while ((opt = getopt_long(argc, argv, "+a:t:h", add_device_options, NULL)) != -1) { switch (opt) { case 'a': action = strtol(optarg, NULL, 0); break; case 't': type = strtol(optarg, NULL, 0); break; case 'h': bt_shell_usage(); optind = 0; return bt_shell_noninteractive_quit(EXIT_SUCCESS); default: bt_shell_usage(); optind = 0; return bt_shell_noninteractive_quit(EXIT_FAILURE); } } argc -= optind; argv += optind; optind = 0; if (argc < 1) { bt_shell_usage(); return bt_shell_noninteractive_quit(EXIT_FAILURE); } index = mgmt_index; if (index == MGMT_INDEX_NONE) index = 0; memset(&cp, 0, sizeof(cp)); str2ba(argv[0], &cp.addr.bdaddr); cp.addr.type = type; cp.action = action; ba2str(&cp.addr.bdaddr, addr); print("Adding device with %s (%s)", addr, typestr(cp.addr.type)); if (mgmt_send(mgmt, MGMT_OP_ADD_DEVICE, index, sizeof(cp), &cp, add_device_rsp, NULL, NULL) == 0) { error("Unable to send add device command"); return bt_shell_noninteractive_quit(EXIT_FAILURE); } } static void remove_device_rsp(uint8_t status, uint16_t len, const void *param, void *user_data) { if (status != 0) error("Remove device failed with status 0x%02x (%s)", status, mgmt_errstr(status)); bt_shell_noninteractive_quit(EXIT_SUCCESS); } static const struct option del_device_options[] = { { "help", 0, 0, 'h' }, { "type", 1, 0, 't' }, { 0, 0, 0, 0 } }; static void cmd_del_device(int argc, char **argv) { struct mgmt_cp_remove_device cp; uint8_t type = BDADDR_BREDR; char addr[18]; int opt; uint16_t index; while ((opt = getopt_long(argc, argv, "+t:h", del_device_options, NULL)) != -1) { switch (opt) { case 't': type = strtol(optarg, NULL, 0); break; case 'h': bt_shell_usage(); optind = 0; return bt_shell_noninteractive_quit(EXIT_SUCCESS); default: bt_shell_usage(); optind = 0; return bt_shell_noninteractive_quit(EXIT_FAILURE); } } argc -= optind; argv += optind; optind = 0; if (argc < 1) { bt_shell_usage(); return bt_shell_noninteractive_quit(EXIT_FAILURE); } index = mgmt_index; if (index == MGMT_INDEX_NONE) index = 0; memset(&cp, 0, sizeof(cp)); str2ba(argv[0], &cp.addr.bdaddr); cp.addr.type = type; ba2str(&cp.addr.bdaddr, addr); print("Removing device with %s (%s)", addr, typestr(cp.addr.type)); if (mgmt_send(mgmt, MGMT_OP_REMOVE_DEVICE, index, sizeof(cp), &cp, remove_device_rsp, NULL, NULL) == 0) { error("Unable to send remove device command"); return bt_shell_noninteractive_quit(EXIT_FAILURE); } } static void cmd_clr_devices(int argc, char **argv) { char *bdaddr_any = "00:00:00:00:00:00"; char *rm_argv[] = { "del-device", bdaddr_any, NULL }; cmd_del_device(2, rm_argv); } static void local_oob_ext_rsp(uint8_t status, uint16_t len, const void *param, void *user_data) { const struct mgmt_rp_read_local_oob_ext_data *rp = param; uint16_t eir_len; if (status != 0) { error("Read Local OOB Ext Data failed with status 0x%02x (%s)", status, mgmt_errstr(status)); return bt_shell_noninteractive_quit(EXIT_FAILURE); } if (len < sizeof(*rp)) { error("Too small (%u bytes) read_local_oob_ext rsp", len); return bt_shell_noninteractive_quit(EXIT_FAILURE); } eir_len = le16_to_cpu(rp->eir_len); if (len != sizeof(*rp) + eir_len) { error("local_oob_ext: expected %zu bytes, got %u bytes", sizeof(*rp) + eir_len, len); return bt_shell_noninteractive_quit(EXIT_FAILURE); } print_eir(rp->eir, eir_len); bt_shell_noninteractive_quit(EXIT_SUCCESS); } static void cmd_bredr_oob(int argc, char **argv) { struct mgmt_cp_read_local_oob_ext_data cp; uint16_t index; index = mgmt_index; if (index == MGMT_INDEX_NONE) index = 0; cp.type = SCAN_TYPE_BREDR; if (!mgmt_send(mgmt, MGMT_OP_READ_LOCAL_OOB_EXT_DATA, index, sizeof(cp), &cp, local_oob_ext_rsp, NULL, NULL)) { error("Unable to send read_local_oob_ext cmd"); return bt_shell_noninteractive_quit(EXIT_FAILURE); } } static void cmd_le_oob(int argc, char **argv) { struct mgmt_cp_read_local_oob_ext_data cp; uint16_t index; index = mgmt_index; if (index == MGMT_INDEX_NONE) index = 0; cp.type = SCAN_TYPE_LE; if (!mgmt_send(mgmt, MGMT_OP_READ_LOCAL_OOB_EXT_DATA, index, sizeof(cp), &cp, local_oob_ext_rsp, NULL, NULL)) { error("Unable to send read_local_oob_ext cmd"); return bt_shell_noninteractive_quit(EXIT_FAILURE); } } static const char *adv_flags_str[] = { "connectable", "general-discoverable", "limited-discoverable", "managed-flags", "tx-power", "scan-rsp-appearance", "scan-rsp-local-name", "Secondary-channel-1M", "Secondary-channel-2M", "Secondary-channel-CODED", }; static const char *adv_flags2str(uint32_t flags) { static char str[256]; unsigned i; int off; off = 0; str[0] = '\0'; for (i = 0; i < NELEM(adv_flags_str); i++) { if ((flags & (1 << i)) != 0) off += snprintf(str + off, sizeof(str) - off, "%s ", adv_flags_str[i]); } return str; } static void adv_features_rsp(uint8_t status, uint16_t len, const void *param, void *user_data) { const struct mgmt_rp_read_adv_features *rp = param; uint32_t supported_flags; if (status != 0) { error("Reading adv features failed with status 0x%02x (%s)", status, mgmt_errstr(status)); return bt_shell_noninteractive_quit(EXIT_FAILURE); } if (len < sizeof(*rp)) { error("Too small adv features reply (%u bytes)", len); return bt_shell_noninteractive_quit(EXIT_FAILURE); } if (len < sizeof(*rp) + rp->num_instances * sizeof(uint8_t)) { error("Instances count (%u) doesn't match reply length (%u)", rp->num_instances, len); return bt_shell_noninteractive_quit(EXIT_FAILURE); } supported_flags = le32_to_cpu(rp->supported_flags); print("Supported flags: %s", adv_flags2str(supported_flags)); print("Max advertising data len: %u", rp->max_adv_data_len); print("Max scan response data len: %u", rp->max_scan_rsp_len); print("Max instances: %u", rp->max_instances); print("Instances list with %u item%s", rp->num_instances, rp->num_instances != 1 ? "s" : ""); return bt_shell_noninteractive_quit(EXIT_SUCCESS); } static void cmd_advinfo(int argc, char **argv) { uint16_t index; index = mgmt_index; if (index == MGMT_INDEX_NONE) index = 0; if (!mgmt_send(mgmt, MGMT_OP_READ_ADV_FEATURES, index, 0, NULL, adv_features_rsp, NULL, NULL)) { error("Unable to send advertising features command"); return bt_shell_noninteractive_quit(EXIT_FAILURE); } } static void adv_size_info_rsp(uint8_t status, uint16_t len, const void *param, void *user_data) { const struct mgmt_rp_get_adv_size_info *rp = param; uint32_t flags; if (status != 0) { error("Reading adv size info failed with status 0x%02x (%s)", status, mgmt_errstr(status)); return bt_shell_noninteractive_quit(EXIT_FAILURE); } if (len < sizeof(*rp)) { error("Too small adv size info reply (%u bytes)", len); return bt_shell_noninteractive_quit(EXIT_FAILURE); } flags = le32_to_cpu(rp->flags); print("Instance: %u", rp->instance); print("Flags: %s", adv_flags2str(flags)); print("Max advertising data len: %u", rp->max_adv_data_len); print("Max scan response data len: %u", rp->max_scan_rsp_len); return bt_shell_noninteractive_quit(EXIT_SUCCESS); } static void advsize_usage(void) { bt_shell_usage(); print("Options:\n" "\t -c, --connectable \"connectable\" flag\n" "\t -g, --general-discov \"general-discoverable\" flag\n" "\t -l, --limited-discov \"limited-discoverable\" flag\n" "\t -m, --managed-flags \"managed-flags\" flag\n" "\t -p, --tx-power \"tx-power\" flag\n" "\t -a, --appearance \"appearance\" flag\n" "\t -n, --local-name \"local-name\" flag"); } static const struct option advsize_options[] = { { "help", 0, 0, 'h' }, { "connectable", 0, 0, 'c' }, { "general-discov", 0, 0, 'g' }, { "limited-discov", 0, 0, 'l' }, { "managed-flags", 0, 0, 'm' }, { "tx-power", 0, 0, 'p' }, { "appearance", 0, 0, 'a' }, { "local-name", 0, 0, 'n' }, { 0, 0, 0, 0} }; static void cmd_advsize(int argc, char **argv) { struct mgmt_cp_get_adv_size_info cp; uint8_t instance; uint32_t flags = 0; int opt; uint16_t index; while ((opt = getopt_long(argc, argv, "+cglmphna", advsize_options, NULL)) != -1) { switch (opt) { case 'c': flags |= MGMT_ADV_FLAG_CONNECTABLE; break; case 'g': flags |= MGMT_ADV_FLAG_DISCOV; break; case 'l': flags |= MGMT_ADV_FLAG_LIMITED_DISCOV; break; case 'm': flags |= MGMT_ADV_FLAG_MANAGED_FLAGS; break; case 'p': flags |= MGMT_ADV_FLAG_TX_POWER; break; case 'a': flags |= MGMT_ADV_FLAG_APPEARANCE; break; case 'n': flags |= MGMT_ADV_FLAG_LOCAL_NAME; break; default: advsize_usage(); optind = 0; return bt_shell_noninteractive_quit(EXIT_FAILURE); } } argc -= optind; argv += optind; optind = 0; if (argc != 1) { advsize_usage(); return bt_shell_noninteractive_quit(EXIT_FAILURE); } instance = strtol(argv[0], NULL, 0); index = mgmt_index; if (index == MGMT_INDEX_NONE) index = 0; memset(&cp, 0, sizeof(cp)); cp.instance = instance; cp.flags = cpu_to_le32(flags); if (!mgmt_send(mgmt, MGMT_OP_GET_ADV_SIZE_INFO, index, sizeof(cp), &cp, adv_size_info_rsp, NULL, NULL)) { error("Unable to send advertising size info command"); return bt_shell_noninteractive_quit(EXIT_FAILURE); } } static void add_adv_rsp(uint8_t status, uint16_t len, const void *param, void *user_data) { const struct mgmt_rp_add_advertising *rp = param; if (status != 0) { error("Add Advertising failed with status 0x%02x (%s)", status, mgmt_errstr(status)); return bt_shell_noninteractive_quit(EXIT_FAILURE); } if (len != sizeof(*rp)) { error("Invalid Add Advertising response length (%u)", len); return bt_shell_noninteractive_quit(EXIT_FAILURE); } print("Instance added: %u", rp->instance); return bt_shell_noninteractive_quit(EXIT_SUCCESS); } static void add_adv_usage(void) { bt_shell_usage(); print("Options:\n" "\t -u, --uuid Service UUID\n" "\t -d, --adv-data Advertising Data bytes\n" "\t -s, --scan-rsp Scan Response Data bytes\n" "\t -t, --timeout Timeout in seconds\n" "\t -D, --duration Duration in seconds\n" "\t -P, --phy Phy type, Specify 1M/2M/CODED\n" "\t -c, --connectable \"connectable\" flag\n" "\t -g, --general-discov \"general-discoverable\" flag\n" "\t -l, --limited-discov \"limited-discoverable\" flag\n" "\t -n, --scan-rsp-local-name \"local-name\" flag\n" "\t -a, --scan-rsp-appearance \"appearance\" flag\n" "\t -m, --managed-flags \"managed-flags\" flag\n" "\t -p, --tx-power \"tx-power\" flag\n" "e.g.:\n" "\tadd-adv -u 180d -u 180f -d 080954657374204C45 1"); } static const struct option add_adv_options[] = { { "help", 0, 0, 'h' }, { "uuid", 1, 0, 'u' }, { "adv-data", 1, 0, 'd' }, { "scan-rsp", 1, 0, 's' }, { "timeout", 1, 0, 't' }, { "duration", 1, 0, 'D' }, { "phy", 1, 0, 'P' }, { "connectable", 0, 0, 'c' }, { "general-discov", 0, 0, 'g' }, { "limited-discov", 0, 0, 'l' }, { "managed-flags", 0, 0, 'm' }, { "tx-power", 0, 0, 'p' }, { 0, 0, 0, 0} }; static bool parse_bytes(char *optarg, uint8_t **bytes, size_t *len) { unsigned i; if (!optarg) { add_adv_usage(); return false; } *len = strlen(optarg); if (*len % 2) { error("Malformed data"); return false; } *len /= 2; if (*len > UINT8_MAX) { error("Data too long"); return false; } *bytes = malloc(*len); if (!*bytes) { error("Failed to allocate memory"); return false; } for (i = 0; i < *len; i++) { if (sscanf(optarg + (i * 2), "%2hhx", *bytes + i) != 1) { error("Invalid data"); free(*bytes); *bytes = NULL; return false; } } return true; } #define MAX_AD_UUID_BYTES 32 static void cmd_add_adv(int argc, char **argv) { struct mgmt_cp_add_advertising *cp = NULL; int opt; uint8_t *adv_data = NULL, *scan_rsp = NULL; size_t adv_len = 0, scan_rsp_len = 0; size_t cp_len; uint8_t uuids[MAX_AD_UUID_BYTES]; size_t uuid_bytes = 0; uint8_t uuid_type = 0; uint16_t timeout = 0, duration = 0; uint8_t instance; bt_uuid_t uuid; bool success = false; bool quit = true; uint32_t flags = 0; uint16_t index; while ((opt = getopt_long(argc, argv, "+u:d:s:t:D:P:cglmphna", add_adv_options, NULL)) != -1) { switch (opt) { case 'u': if (bt_string_to_uuid(&uuid, optarg) < 0) { print("Invalid UUID: %s", optarg); goto done; } if (uuid_type && uuid_type != uuid.type) { print("UUID types must be consistent"); goto done; } if (uuid.type == BT_UUID16) { if (uuid_bytes + 2 >= MAX_AD_UUID_BYTES) { print("Too many UUIDs"); goto done; } bt_uuid_to_le(&uuid, uuids + uuid_bytes); uuid_bytes += 2; } else if (uuid.type == BT_UUID128) { if (uuid_bytes + 16 >= MAX_AD_UUID_BYTES) { print("Too many UUIDs"); goto done; } bt_uuid_to_le(&uuid, uuids + uuid_bytes); uuid_bytes += 16; } else { printf("Unsupported UUID type"); goto done; } if (!uuid_type) uuid_type = uuid.type; break; case 'd': if (adv_len) { print("Only one adv-data option allowed"); goto done; } if (!parse_bytes(optarg, &adv_data, &adv_len)) goto done; break; case 's': if (scan_rsp_len) { print("Only one scan-rsp option allowed"); goto done; } if (!parse_bytes(optarg, &scan_rsp, &scan_rsp_len)) goto done; break; case 't': timeout = strtol(optarg, NULL, 0); break; case 'D': duration = strtol(optarg, NULL, 0); break; case 'c': flags |= MGMT_ADV_FLAG_CONNECTABLE; break; case 'g': flags |= MGMT_ADV_FLAG_DISCOV; break; case 'l': flags |= MGMT_ADV_FLAG_LIMITED_DISCOV; break; case 'm': flags |= MGMT_ADV_FLAG_MANAGED_FLAGS; break; case 'p': flags |= MGMT_ADV_FLAG_TX_POWER; break; case 'n': flags |= MGMT_ADV_FLAG_LOCAL_NAME; break; case 'a': flags |= MGMT_ADV_FLAG_APPEARANCE; break; case 'P': if (strcasecmp(optarg, "1M") == 0) flags |= MGMT_ADV_FLAG_SEC_1M; else if (strcasecmp(optarg, "2M") == 0) flags |= MGMT_ADV_FLAG_SEC_2M; else if (strcasecmp(optarg, "CODED") == 0) flags |= MGMT_ADV_FLAG_SEC_CODED; else goto done; break; case 'h': success = true; /* fall through */ default: add_adv_usage(); optind = 0; goto done; } } argc -= optind; argv += optind; optind = 0; if (argc != 1) { add_adv_usage(); goto done; } if (uuid_bytes) uuid_bytes += 2; instance = strtol(argv[0], NULL, 0); index = mgmt_index; if (index == MGMT_INDEX_NONE) index = 0; cp_len = sizeof(*cp) + uuid_bytes + adv_len + scan_rsp_len; cp = malloc0(cp_len); if (!cp) goto done; cp->instance = instance; put_le32(flags, &cp->flags); put_le16(timeout, &cp->timeout); put_le16(duration, &cp->duration); cp->adv_data_len = adv_len + uuid_bytes; cp->scan_rsp_len = scan_rsp_len; if (uuid_bytes) { cp->data[0] = uuid_bytes - 1; cp->data[1] = uuid_type == SDP_UUID16 ? 0x03 : 0x07; memcpy(cp->data + 2, uuids, uuid_bytes - 2); } if (adv_len) memcpy(cp->data + uuid_bytes, adv_data, adv_len); if (scan_rsp_len) memcpy(cp->data + uuid_bytes + adv_len, scan_rsp, scan_rsp_len); if (!mgmt_send(mgmt, MGMT_OP_ADD_ADVERTISING, index, cp_len, cp, add_adv_rsp, NULL, NULL)) { error("Unable to send \"Add Advertising\" command"); goto done; } quit = false; done: free(adv_data); free(scan_rsp); free(cp); if (quit) bt_shell_noninteractive_quit(success ? EXIT_SUCCESS : EXIT_FAILURE); } static void rm_adv_rsp(uint8_t status, uint16_t len, const void *param, void *user_data) { const struct mgmt_rp_remove_advertising *rp = param; if (status != 0) { error("Remove Advertising failed with status 0x%02x (%s)", status, mgmt_errstr(status)); return bt_shell_noninteractive_quit(EXIT_FAILURE); } if (len != sizeof(*rp)) { error("Invalid Remove Advertising response length (%u)", len); return bt_shell_noninteractive_quit(EXIT_FAILURE); } print("Instance removed: %u", rp->instance); return bt_shell_noninteractive_quit(EXIT_SUCCESS); } static void cmd_rm_adv(int argc, char **argv) { struct mgmt_cp_remove_advertising cp; uint8_t instance; uint16_t index; instance = strtol(argv[1], NULL, 0); index = mgmt_index; if (index == MGMT_INDEX_NONE) index = 0; memset(&cp, 0, sizeof(cp)); cp.instance = instance; if (!mgmt_send(mgmt, MGMT_OP_REMOVE_ADVERTISING, index, sizeof(cp), &cp, rm_adv_rsp, NULL, NULL)) { error("Unable to send \"Remove Advertising\" command"); return bt_shell_noninteractive_quit(EXIT_FAILURE); } } static void cmd_clr_adv(int argc, char **argv) { char *all_instances = "0"; char *rm_argv[] = { "rm-adv", all_instances, NULL }; cmd_rm_adv(2, rm_argv); } static void add_ext_adv_params_rsp(uint8_t status, uint16_t len, const void *param, void *user_data) { const struct mgmt_rp_add_ext_adv_params *rp = param; if (status != 0) { error("Add Ext Adv Params failed status 0x%02x (%s)", status, mgmt_errstr(status)); return bt_shell_noninteractive_quit(EXIT_FAILURE); } if (len != sizeof(*rp)) { error("Invalid Add Ext Adv Params response length (%u)", len); return bt_shell_noninteractive_quit(EXIT_FAILURE); } print("Instance added: %u", rp->instance); print("Tx Power: %u", rp->tx_power); print("Max adv data len: %u", rp->max_adv_data_len); print("Max scan resp len: %u", rp->max_scan_rsp_len); return bt_shell_noninteractive_quit(EXIT_SUCCESS); } static void add_ext_adv_params_usage(void) { bt_shell_usage(); print("Options:\n" "\t -d, --duration Duration in seconds\n" "\t -t, --timeout Timeout in seconds\n" "\t -r, --min-interval Minimum interval\n" "\t -x, --max-interval Maximum interval\n" "\t -w, --tx-power Tx power\n" "\t -P, --phy Phy type, Specify 1M/2M/CODED\n" "\t -c, --connectable \"connectable\" flag\n" "\t -g, --general-discov \"general-discoverable\" flag\n" "\t -l, --limited-discov \"limited-discoverable\" flag\n" "\t -m, --managed-flags \"managed-flags\" flag\n" "\t -p, --add-tx-power \"tx-power\" flag\n" "\t -a, --scan-rsp-appearance \"appearance\" flag\n" "\t -n, --scan-rsp-local-name \"local-name\" flag\n" "\t -s, --adv-scan-rsp \"scan resp in adv\" flag\n" "\t -h, --help Show help\n" "e.g.:\n" "\tadd-ext-adv-params -r 0x801 -x 0x802 -P 2M -g 1"); } static const struct option add_ext_adv_params_options[] = { { "help", 0, 0, 'h' }, { "duration", 1, 0, 'd' }, { "timeout", 1, 0, 't' }, { "min-internal", 1, 0, 'r' }, { "max-interval", 1, 0, 'x' }, { "tx-power", 1, 0, 'w' }, { "phy", 1, 0, 'P' }, { "connectable", 0, 0, 'c' }, { "general-discov", 0, 0, 'g' }, { "limited-discov", 0, 0, 'l' }, { "scan-rsp-local-name", 0, 0, 'n' }, { "scan-rsp-appearance", 0, 0, 'a' }, { "managed-flags", 0, 0, 'm' }, { "add-tx-power", 0, 0, 'p' }, { "adv-scan-rsp", 0, 0, 's' }, { 0, 0, 0, 0} }; static void cmd_add_ext_adv_params(int argc, char **argv) { struct mgmt_cp_add_ext_adv_params *cp = NULL; int opt; uint16_t timeout = 0, duration = 0; uint8_t instance; bool success = false; bool quit = true; uint32_t flags = 0; uint32_t min_interval = 0; uint32_t max_interval = 0; uint8_t tx_power = 0; uint16_t index; while ((opt = getopt_long(argc, argv, "d:t:r:x:w:P:cglmpansh", add_ext_adv_params_options, NULL)) != -1) { switch (opt) { case 'd': duration = strtol(optarg, NULL, 0); flags |= MGMT_ADV_PARAM_DURATION; break; case 't': timeout = strtol(optarg, NULL, 0); flags |= MGMT_ADV_PARAM_TIMEOUT; break; case 'r': min_interval = strtol(optarg, NULL, 0); break; case 'x': max_interval = strtol(optarg, NULL, 0); break; case 'w': tx_power = strtol(optarg, NULL, 0); flags |= MGMT_ADV_PARAM_TX_POWER; break; case 'P': if (strcasecmp(optarg, "1M") == 0) flags |= MGMT_ADV_FLAG_SEC_1M; else if (strcasecmp(optarg, "2M") == 0) flags |= MGMT_ADV_FLAG_SEC_2M; else if (strcasecmp(optarg, "CODED") == 0) flags |= MGMT_ADV_FLAG_SEC_CODED; else goto done; break; case 'c': flags |= MGMT_ADV_FLAG_CONNECTABLE; break; case 'g': flags |= MGMT_ADV_FLAG_DISCOV; break; case 'l': flags |= MGMT_ADV_FLAG_LIMITED_DISCOV; break; case 'n': flags |= MGMT_ADV_FLAG_LOCAL_NAME; break; case 'a': flags |= MGMT_ADV_FLAG_APPEARANCE; break; case 'm': flags |= MGMT_ADV_FLAG_MANAGED_FLAGS; break; case 'p': flags |= MGMT_ADV_FLAG_TX_POWER; break; case 's': flags |= MGMT_ADV_PARAM_SCAN_RSP; break; case 'h': success = true; /* fall through */ default: add_ext_adv_params_usage(); optind = 0; goto done; } } argc -= optind; argv += optind; optind = 0; if (argc != 1) { add_ext_adv_params_usage(); goto done; } /* Only if both min_interval and max_interval are defined */ if (min_interval && max_interval) flags |= MGMT_ADV_PARAM_INTERVALS; instance = strtol(argv[0], NULL, 0); index = mgmt_index; if (index == MGMT_INDEX_NONE) index = 0; cp = malloc0(sizeof(*cp)); if (!cp) goto done; cp->instance = instance; put_le32(flags, &cp->flags); put_le16(timeout, &cp->timeout); put_le16(duration, &cp->duration); put_le32(min_interval, &cp->min_interval); put_le32(max_interval, &cp->max_interval); cp->tx_power = tx_power; if (!mgmt_send(mgmt, MGMT_OP_ADD_EXT_ADV_PARAMS, index, sizeof(*cp), cp, add_ext_adv_params_rsp, NULL, NULL)) { error("Unable to send \"Add Ext Advertising Params\" command"); goto done; } quit = false; done: free(cp); if (quit) bt_shell_noninteractive_quit(success ? EXIT_SUCCESS : EXIT_FAILURE); } static void add_ext_adv_data_rsp(uint8_t status, uint16_t len, const void *param, void *user_data) { const struct mgmt_rp_add_ext_adv_data *rp = param; if (status != 0) { error("Add Ext Advertising Data failed with status 0x%02x (%s)", status, mgmt_errstr(status)); return bt_shell_noninteractive_quit(EXIT_FAILURE); } if (len != sizeof(*rp)) { error("Invalid Add Ext Advertising Data response length (%u)", len); return bt_shell_noninteractive_quit(EXIT_FAILURE); } print("Instance added: %u", rp->instance); return bt_shell_noninteractive_quit(EXIT_SUCCESS); } static void add_ext_adv_data_usage(void) { bt_shell_usage(); print("Options:\n" "\t -u, --uuid Service UUID\n" "\t -d, --adv-data Advertising Data bytes\n" "\t -s, --scan-rsp Scan Response Data bytes\n" "e.g.:\n" "\tadd-ext-adv-data -u 180d -u 180f -d 080954657374204C45 1"); } static const struct option add_ext_adv_data_options[] = { { "help", 0, 0, 'h' }, { "uuid", 1, 0, 'u' }, { "adv-data", 1, 0, 'd' }, { "scan-rsp", 1, 0, 's' }, { 0, 0, 0, 0} }; static void cmd_add_ext_adv_data(int argc, char **argv) { struct mgmt_cp_add_ext_adv_data *cp = NULL; int opt; uint8_t *adv_data = NULL, *scan_rsp = NULL; size_t adv_len = 0, scan_rsp_len = 0; size_t cp_len; uint8_t uuids[MAX_AD_UUID_BYTES]; size_t uuid_bytes = 0; uint8_t uuid_type = 0; uint8_t instance; bt_uuid_t uuid; bool success = false; bool quit = true; uint16_t index; while ((opt = getopt_long(argc, argv, "+u:d:s:h", add_ext_adv_data_options, NULL)) != -1) { switch (opt) { case 'u': if (bt_string_to_uuid(&uuid, optarg) < 0) { print("Invalid UUID: %s", optarg); goto done; } if (uuid_type && uuid_type != uuid.type) { print("UUID types must be consistent"); goto done; } if (uuid.type == BT_UUID16) { if (uuid_bytes + 2 >= MAX_AD_UUID_BYTES) { print("Too many UUIDs"); goto done; } bt_uuid_to_le(&uuid, uuids + uuid_bytes); uuid_bytes += 2; } else if (uuid.type == BT_UUID128) { if (uuid_bytes + 16 >= MAX_AD_UUID_BYTES) { print("Too many UUIDs"); goto done; } bt_uuid_to_le(&uuid, uuids + uuid_bytes); uuid_bytes += 16; } else { printf("Unsupported UUID type"); goto done; } if (!uuid_type) uuid_type = uuid.type; break; case 'd': if (adv_len) { print("Only one adv-data option allowed"); goto done; } if (!parse_bytes(optarg, &adv_data, &adv_len)) goto done; break; case 's': if (scan_rsp_len) { print("Only one scan-rsp option allowed"); goto done; } if (!parse_bytes(optarg, &scan_rsp, &scan_rsp_len)) goto done; break; case 'h': success = true; /* fall through */ default: add_ext_adv_data_usage(); optind = 0; goto done; } } argc -= optind; argv += optind; optind = 0; if (argc != 1) { add_ext_adv_data_usage(); goto done; } if (uuid_bytes) uuid_bytes += 2; instance = strtol(argv[0], NULL, 0); index = mgmt_index; if (index == MGMT_INDEX_NONE) index = 0; cp_len = sizeof(*cp) + uuid_bytes + adv_len + scan_rsp_len; cp = malloc0(cp_len); if (!cp) goto done; cp->instance = instance; cp->adv_data_len = adv_len + uuid_bytes; cp->scan_rsp_len = scan_rsp_len; if (uuid_bytes) { cp->data[0] = uuid_bytes - 1; cp->data[1] = uuid_type == SDP_UUID16 ? 0x03 : 0x07; memcpy(cp->data + 2, uuids, uuid_bytes - 2); } if (adv_len) memcpy(cp->data + uuid_bytes, adv_data, adv_len); if (scan_rsp_len) memcpy(cp->data + uuid_bytes + adv_len, scan_rsp, scan_rsp_len); if (!mgmt_send(mgmt, MGMT_OP_ADD_EXT_ADV_DATA, index, cp_len, cp, add_ext_adv_data_rsp, NULL, NULL)) { error("Unable to send \"Add Ext Advertising Data\" command"); goto done; } quit = false; done: free(adv_data); free(scan_rsp); free(cp); if (quit) bt_shell_noninteractive_quit(success ? EXIT_SUCCESS : EXIT_FAILURE); } static void appearance_rsp(uint8_t status, uint16_t len, const void *param, void *user_data) { if (status != 0) error("Could not set Appearance with status 0x%02x (%s)", status, mgmt_errstr(status)); else print("Appearance successfully set"); bt_shell_noninteractive_quit(EXIT_SUCCESS); } static void cmd_appearance(int argc, char **argv) { struct mgmt_cp_set_appearance cp; uint16_t index; index = mgmt_index; if (index == MGMT_INDEX_NONE) index = 0; cp.appearance = cpu_to_le16(strtol(argv[1], NULL, 0)); if (mgmt_send(mgmt, MGMT_OP_SET_APPEARANCE, index, sizeof(cp), &cp, appearance_rsp, NULL, NULL) == 0) { error("Unable to send appearance cmd"); return bt_shell_noninteractive_quit(EXIT_FAILURE); } } static const char *phys_str[] = { "BR1M1SLOT", "BR1M3SLOT", "BR1M5SLOT", "EDR2M1SLOT", "EDR2M3SLOT", "EDR2M5SLOT", "EDR3M1SLOT", "EDR3M3SLOT", "EDR3M5SLOT", "LE1MTX", "LE1MRX", "LE2MTX", "LE2MRX", "LECODEDTX", "LECODEDRX", }; static const char *phys2str(uint32_t phys) { static char str[256]; unsigned int i; int off; off = 0; str[0] = '\0'; for (i = 0; i < NELEM(phys_str); i++) { if ((phys & (1 << i)) != 0) off += snprintf(str + off, sizeof(str) - off, "%s ", phys_str[i]); } return str; } static bool str2phy(const char *phy_str, uint32_t *phy_val) { unsigned int i; for (i = 0; i < NELEM(phys_str); i++) { if (strcasecmp(phys_str[i], phy_str) == 0) { *phy_val = (1 << i); return true; } } return false; } static void get_phy_rsp(uint8_t status, uint16_t len, const void *param, void *user_data) { const struct mgmt_rp_get_phy_confguration *rp = param; uint32_t supported_phys, selected_phys, configurable_phys; if (status != 0) { error("Get PHY Configuration failed with status 0x%02x (%s)", status, mgmt_errstr(status)); return bt_shell_noninteractive_quit(EXIT_FAILURE); } if (len < sizeof(*rp)) { error("Too small get-phy reply (%u bytes)", len); return bt_shell_noninteractive_quit(EXIT_FAILURE); } supported_phys = get_le32(&rp->supported_phys); configurable_phys = get_le32(&rp->configurable_phys); selected_phys = get_le32(&rp->selected_phys); print("Supported phys: %s", phys2str(supported_phys)); print("Configurable phys: %s", phys2str(configurable_phys)); print("Selected phys: %s", phys2str(selected_phys)); bt_shell_noninteractive_quit(EXIT_SUCCESS); } static void get_phy(void) { uint16_t index; index = mgmt_index; if (index == MGMT_INDEX_NONE) index = 0; if (mgmt_send(mgmt, MGMT_OP_GET_PHY_CONFIGURATION, index, 0, NULL, get_phy_rsp, NULL, NULL) == 0) { error("Unable to send Get PHY cmd"); return bt_shell_noninteractive_quit(EXIT_FAILURE); } } static void set_phy_rsp(uint8_t status, uint16_t len, const void *param, void *user_data) { if (status != 0) { error("Could not set PHY Configuration with status 0x%02x (%s)", status, mgmt_errstr(status)); return bt_shell_noninteractive_quit(EXIT_FAILURE); } print("PHY Configuration successfully set"); bt_shell_noninteractive_quit(EXIT_SUCCESS); } static void cmd_phy(int argc, char **argv) { struct mgmt_cp_set_phy_confguration cp; int i; uint32_t phys = 0; uint16_t index; if (argc < 2) return get_phy(); for (i = 1; i < argc; i++) { uint32_t phy_val; if (str2phy(argv[i], &phy_val)) phys |= phy_val; } cp.selected_phys = cpu_to_le32(phys); index = mgmt_index; if (index == MGMT_INDEX_NONE) index = 0; if (mgmt_send(mgmt, MGMT_OP_SET_PHY_CONFIGURATION, index, sizeof(cp), &cp, set_phy_rsp, NULL, NULL) == 0) { error("Unable to send %s cmd", mgmt_opstr(MGMT_OP_SET_PHY_CONFIGURATION)); return bt_shell_noninteractive_quit(EXIT_FAILURE); } } static void cmd_wbs(int argc, char **argv) { cmd_setting(MGMT_OP_SET_WIDEBAND_SPEECH, argc, argv); } static const char * const advmon_features_str[] = { "Pattern monitor with logic OR.", }; static const char *advmon_features2str(uint32_t features) { static char str[512]; unsigned int off, i; off = 0; snprintf(str, sizeof(str), "\n\tNone"); for (i = 0; i < NELEM(advmon_features_str); i++) { if ((features & (1 << i)) != 0 && off < sizeof(str)) off += snprintf(str + off, sizeof(str) - off, "\n\t%s", advmon_features_str[i]); } return str; } static void advmon_features_rsp(uint8_t status, uint16_t len, const void *param, void *user_data) { const struct mgmt_rp_read_adv_monitor_features *rp = param; uint32_t supported_features, enabled_features; uint16_t num_handles; int i; if (status != MGMT_STATUS_SUCCESS) { error("Reading adv monitor features failed with status 0x%02x " "(%s)", status, mgmt_errstr(status)); return bt_shell_noninteractive_quit(EXIT_FAILURE); } if (len < sizeof(*rp)) { error("Too small adv monitor features reply (%u bytes)", len); return bt_shell_noninteractive_quit(EXIT_FAILURE); } supported_features = le32_to_cpu(rp->supported_features); enabled_features = le32_to_cpu(rp->enabled_features); num_handles = le16_to_cpu(rp->num_handles); if (len < sizeof(*rp) + num_handles * sizeof(uint16_t)) { error("Handles count (%u) doesn't match reply length (%u)", num_handles, len); return bt_shell_noninteractive_quit(EXIT_FAILURE); } print("Supported features:%s", advmon_features2str(supported_features)); print("Enabled features:%s", advmon_features2str(enabled_features)); print("Max number of handles: %u", le16_to_cpu(rp->max_num_handles)); print("Max number of patterns: %u", rp->max_num_patterns); print("Handles list with %u item%s", num_handles, num_handles == 0 ? "" : num_handles == 1 ? ":" : "s:"); for (i = 0; i < num_handles; i++) print("\t0x%04x ", le16_to_cpu(rp->handles[i])); return bt_shell_noninteractive_quit(EXIT_SUCCESS); } static void cmd_advmon_features(int argc, char **argv) { uint16_t index; index = mgmt_index; if (index == MGMT_INDEX_NONE) index = 0; if (!mgmt_send(mgmt, MGMT_OP_READ_ADV_MONITOR_FEATURES, index, 0, NULL, advmon_features_rsp, NULL, NULL)) { error("Unable to send advertising monitor features command"); return bt_shell_noninteractive_quit(EXIT_FAILURE); } } static void advmon_add_rsp(uint8_t status, uint16_t len, const void *param, void *user_data) { const struct mgmt_rp_add_adv_patterns_monitor *rp = param; if (status != MGMT_STATUS_SUCCESS) { error("Could not add advertisement monitor with status " "0x%02x (%s)", status, mgmt_errstr(status)); return bt_shell_noninteractive_quit(EXIT_FAILURE); } print("Advertisement monitor with handle:0x%04x added", le16_to_cpu(rp->monitor_handle)); return bt_shell_noninteractive_quit(EXIT_SUCCESS); } static bool str2pattern(struct mgmt_adv_pattern *pattern, const char *str) { int type_len, offset_len, offset_end_pos, str_len; int i, j; char pattern_str[62] = { 0 }; char tmp; if (sscanf(str, "%2hhx%n:%2hhx%n:%61s", &pattern->ad_type, &type_len, &pattern->offset, &offset_end_pos, pattern_str) != 3) return false; offset_len = offset_end_pos - type_len - 1; str_len = strlen(pattern_str); pattern->length = str_len / 2 + str_len % 2; if (type_len > 2 || offset_len > 2 || pattern->offset + pattern->length > 31) return false; for (i = 0, j = 0; i < str_len; i++, j++) { if (sscanf(&pattern_str[i++], "%2hhx", &pattern->value[j]) != 1) return false; if (i < str_len && sscanf(&pattern_str[i], "%1hhx", &tmp) != 1) return false; } return true; } static const struct option add_monitor_rssi_options[] = { { "help", 0, 0, 'h' }, { "high-threshold", 1, 0, 'R' }, { "low-threshold", 1, 0, 'r' }, { "high-timeout", 1, 0, 'T' }, { "low-timeout", 1, 0, 't' }, { "sampling", 1, 0, 's' }, { 0, 0, 0, 0 } }; static void advmon_add_pattern_usage(void) { bt_shell_usage(); print("patterns format:\n" "\t [patterns]\n" "e.g.:\n" "\tadd-pattern 0:1:c504 ff:a:9a55beef"); } static void advmon_add_pattern_rssi_usage(void) { bt_shell_usage(); print("RSSI options:\n" "\t -R, --high-threshold " "RSSI high threshold. Default: -70\n" "\t -r, --low-threshold " "RSSI low threshold. Default: -50\n" "\t -T, --high-timeout " "RSSI high threshold duration. Default: 0\n" "\t -t, --low-timeout " "RSSI low threshold duration. Default: 5\n" "\t -s, --sampling " "RSSI sampling period. Default: 0\n" "patterns format:\n" "\t [patterns]\n" "e.g.:\n" "\tadd-pattern-rssi -R 0xb2 -r -102 0:1:c504 ff:a:9a55beef"); } static void cmd_advmon_add_pattern(int argc, char **argv) { bool success = true; uint16_t index; int i, cp_len; struct mgmt_cp_add_adv_monitor *cp = NULL; if (!strcmp(argv[1], "-h")) goto done; argc -= 1; argv += 1; cp_len = sizeof(*cp) + argc * sizeof(struct mgmt_adv_pattern); cp = malloc0(cp_len); if (!cp) { error("Failed to alloc patterns."); success = false; goto done; } cp->pattern_count = argc; for (i = 0; i < argc; i++) { if (!str2pattern(&cp->patterns[i], argv[i])) { error("Failed to parse monitor patterns."); success = false; goto done; } } index = mgmt_index; if (index == MGMT_INDEX_NONE) index = 0; if (!mgmt_send(mgmt, MGMT_OP_ADD_ADV_PATTERNS_MONITOR, index, cp_len, cp, advmon_add_rsp, NULL, NULL)) { error("Unable to send Add Advertising Monitor command"); success = false; goto done; } free(cp); return; done: free(cp); advmon_add_pattern_usage(); bt_shell_noninteractive_quit(success ? EXIT_SUCCESS : EXIT_FAILURE); } static void cmd_advmon_add_pattern_rssi(int argc, char **argv) { bool success = true; int opt; int8_t rssi_low = -70; int8_t rssi_high = -50; uint16_t rssi_low_timeout = 5; uint16_t rssi_high_timeout = 0; uint8_t rssi_sampling_period = 0; uint16_t index; int i, cp_len; struct mgmt_cp_add_adv_patterns_monitor_rssi *cp = NULL; while ((opt = getopt_long(argc, argv, "+hr:R:t:T:s:", add_monitor_rssi_options, NULL)) != -1) { switch (opt) { case 'h': goto done; case 'r': rssi_low = strtol(optarg, NULL, 0); break; case 'R': rssi_high = strtol(optarg, NULL, 0); break; case 't': rssi_low_timeout = strtol(optarg, NULL, 0); break; case 'T': rssi_high_timeout = strtol(optarg, NULL, 0); break; case 's': rssi_sampling_period = strtol(optarg, NULL, 0); break; default: success = false; goto done; } } argc -= optind; argv += optind; optind = 0; cp_len = sizeof(*cp) + argc * sizeof(struct mgmt_adv_pattern); cp = malloc0(cp_len); if (!cp) { error("Failed to alloc patterns."); success = false; goto done; } cp->pattern_count = argc; cp->rssi.high_threshold = rssi_high; cp->rssi.low_threshold = rssi_low; cp->rssi.high_threshold_timeout = htobs(rssi_high_timeout); cp->rssi.low_threshold_timeout = htobs(rssi_low_timeout); cp->rssi.sampling_period = rssi_sampling_period; for (i = 0; i < argc; i++) { if (!str2pattern(&cp->patterns[i], argv[i])) { error("Failed to parse monitor patterns."); success = false; goto done; } } index = mgmt_index; if (index == MGMT_INDEX_NONE) index = 0; if (!mgmt_send(mgmt, MGMT_OP_ADD_ADV_PATTERNS_MONITOR_RSSI, index, cp_len, cp, advmon_add_rsp, NULL, NULL)) { error("Unable to send Add Advertising Monitor RSSI command"); success = false; goto done; } free(cp); return; done: free(cp); optind = 0; advmon_add_pattern_rssi_usage(); bt_shell_noninteractive_quit(success ? EXIT_SUCCESS : EXIT_FAILURE); } static void advmon_remove_rsp(uint8_t status, uint16_t len, const void *param, void *user_data) { const struct mgmt_rp_remove_adv_monitor *rp = param; if (status != MGMT_STATUS_SUCCESS) { error("Could not remove advertisement monitor with status " "0x%02x (%s)", status, mgmt_errstr(status)); return bt_shell_noninteractive_quit(EXIT_FAILURE); } print("Advertisement monitor with handle: 0x%04x removed", le16_to_cpu(rp->monitor_handle)); return bt_shell_noninteractive_quit(EXIT_SUCCESS); } static void cmd_advmon_remove(int argc, char **argv) { struct mgmt_cp_remove_adv_monitor cp; uint16_t index, monitor_handle; index = mgmt_index; if (index == MGMT_INDEX_NONE) index = 0; if (sscanf(argv[1], "%hx", &monitor_handle) != 1) { error("Wrong formatted handle argument"); return bt_shell_noninteractive_quit(EXIT_FAILURE); } cp.monitor_handle = cpu_to_le16(monitor_handle); if (mgmt_send(mgmt, MGMT_OP_REMOVE_ADV_MONITOR, index, sizeof(cp), &cp, advmon_remove_rsp, NULL, NULL) == 0) { error("Unable to send appearance cmd"); return bt_shell_noninteractive_quit(EXIT_FAILURE); } } static void register_mgmt_callbacks(struct mgmt *mgmt, uint16_t index) { mgmt_register(mgmt, MGMT_EV_CONTROLLER_ERROR, index, controller_error, NULL, NULL); mgmt_register(mgmt, MGMT_EV_INDEX_ADDED, index, index_added, NULL, NULL); mgmt_register(mgmt, MGMT_EV_INDEX_REMOVED, index, index_removed, NULL, NULL); mgmt_register(mgmt, MGMT_EV_NEW_SETTINGS, index, new_settings, NULL, NULL); mgmt_register(mgmt, MGMT_EV_DISCOVERING, index, discovering, NULL, NULL); mgmt_register(mgmt, MGMT_EV_NEW_LINK_KEY, index, new_link_key, NULL, NULL); mgmt_register(mgmt, MGMT_EV_DEVICE_CONNECTED, index, connected, NULL, NULL); mgmt_register(mgmt, MGMT_EV_DEVICE_DISCONNECTED, index, disconnected, NULL, NULL); mgmt_register(mgmt, MGMT_EV_CONNECT_FAILED, index, conn_failed, NULL, NULL); mgmt_register(mgmt, MGMT_EV_AUTH_FAILED, index, auth_failed, NULL, NULL); mgmt_register(mgmt, MGMT_EV_CLASS_OF_DEV_CHANGED, index, class_of_dev_changed, NULL, NULL); mgmt_register(mgmt, MGMT_EV_LOCAL_NAME_CHANGED, index, local_name_changed, NULL, NULL); mgmt_register(mgmt, MGMT_EV_DEVICE_FOUND, index, device_found, mgmt, NULL); mgmt_register(mgmt, MGMT_EV_UNCONF_INDEX_ADDED, index, unconf_index_added, NULL, NULL); mgmt_register(mgmt, MGMT_EV_UNCONF_INDEX_REMOVED, index, unconf_index_removed, NULL, NULL); mgmt_register(mgmt, MGMT_EV_NEW_CONFIG_OPTIONS, index, new_config_options, NULL, NULL); mgmt_register(mgmt, MGMT_EV_EXT_INDEX_ADDED, index, ext_index_added, NULL, NULL); mgmt_register(mgmt, MGMT_EV_EXT_INDEX_REMOVED, index, ext_index_removed, NULL, NULL); mgmt_register(mgmt, MGMT_EV_LOCAL_OOB_DATA_UPDATED, index, local_oob_data_updated, NULL, NULL); mgmt_register(mgmt, MGMT_EV_ADVERTISING_ADDED, index, advertising_added, NULL, NULL); mgmt_register(mgmt, MGMT_EV_ADVERTISING_REMOVED, index, advertising_removed, NULL, NULL); mgmt_register(mgmt, MGMT_EV_DEVICE_FLAGS_CHANGED, index, flags_changed, NULL, NULL); mgmt_register(mgmt, MGMT_EV_ADV_MONITOR_ADDED, index, advmon_added, NULL, NULL); mgmt_register(mgmt, MGMT_EV_ADV_MONITOR_REMOVED, index, advmon_removed, NULL, NULL); } static void cmd_select(int argc, char **argv) { mgmt_cancel_all(mgmt); mgmt_unregister_all(mgmt); mgmt_set_index(argv[1]); register_mgmt_callbacks(mgmt, mgmt_index); print("Selected index %u", mgmt_index); } static const struct bt_shell_menu monitor_menu = { .name = "monitor", .desc = "Advertisement Monitor Submenu", .entries = { { "features", NULL, cmd_advmon_features, "Show advertisement monitor " "features" }, { "remove", "", cmd_advmon_remove, "Remove advertisement monitor " }, { "add-pattern", "[-,h] ", cmd_advmon_add_pattern, "Add advertisement monitor pattern" }, { "add-pattern-rssi", "[options] ", cmd_advmon_add_pattern_rssi, "Add advertisement monitor pattern with RSSI options" }, { } }, }; static const struct bt_shell_menu mgmt_menu = { .name = "mgmt", .desc = "Management Submenu", .entries = { { "select", "", cmd_select, "Select a different index" }, { "revision", NULL, cmd_revision, "Get the MGMT Revision" }, { "commands", NULL, cmd_commands, "List supported commands" }, { "config", NULL, cmd_config, "Show configuration info" }, { "info", NULL, cmd_info, "Show controller info" }, { "extinfo", NULL, cmd_extinfo, "Show extended controller info" }, { "auto-power", NULL, cmd_auto_power, "Power all available features" }, { "power", "", cmd_power, "Toggle powered state" }, { "discov", " [timeout]", cmd_discov, "Toggle discoverable state" }, { "connectable", "", cmd_connectable, "Toggle connectable state" }, { "fast-conn", "", cmd_fast_conn, "Toggle fast connectable state" }, { "bondable", "", cmd_bondable, "Toggle bondable state" }, { "pairable", "", cmd_bondable, "Toggle bondable state" }, { "linksec", "", cmd_linksec, "Toggle link level security" }, { "ssp", "", cmd_ssp, "Toggle SSP mode" }, { "sc", "", cmd_sc, "Toogle SC support" }, { "hs", "", cmd_hs, "Toggle HS support" }, { "le", "", cmd_le, "Toggle LE support" }, { "advertising", "", cmd_advertising, "Toggle LE advertising", }, { "bredr", "", cmd_bredr, "Toggle BR/EDR support", }, { "privacy", " [irk]", cmd_privacy, "Toggle privacy support" }, { "class", " ", cmd_class, "Set device major/minor class" }, { "disconnect", "[-t type] ", cmd_disconnect, "Disconnect device" }, { "con", NULL, cmd_con, "List connections" }, { "find", "[-l|-b] [-L]", cmd_find, "Discover nearby devices" }, { "find-service", "[-u UUID] [-r RSSI_Threshold] [-l|-b]", cmd_find_service, "Discover nearby service" }, { "stop-find", "[-l|-b]", cmd_stop_find, "Stop discovery" }, { "name", " [shortname]", cmd_name, "Set local name" }, { "pair", "[-c cap] [-t type] ", cmd_pair, "Pair with a remote device" }, { "cancelpair", "[-t type] ", cmd_cancel_pair, "Cancel pairing" }, { "unpair", "[-t type] ", cmd_unpair, "Unpair device" }, { "keys", NULL, cmd_keys, "Load Link Keys" }, { "ltks", NULL, cmd_ltks, "Load Long Term Keys" }, { "irks", "[--local index] [--file file path]", cmd_irks, "Load Identity Resolving Keys" }, { "block", "[-t type] ", cmd_block, "Block Device" }, { "unblock", "[-t type] ", cmd_unblock, "Unblock Device" }, { "add-uuid", " ", cmd_add_uuid, "Add UUID" }, { "rm-uuid", "", cmd_remove_uuid, "Remove UUID" }, { "clr-uuids", NULL, cmd_clr_uuids, "Clear UUIDs" }, { "local-oob", NULL, cmd_local_oob, "Local OOB data" }, { "remote-oob", "[-t ] [-r ] " "[-h ] [-R ] " "[-H ] ", cmd_remote_oob, "Remote OOB data" }, { "did", ":::", cmd_did, "Set Device ID" }, { "static-addr", "
", cmd_static_addr, "Set static address" }, { "public-addr", "
", cmd_public_addr, "Set public address" }, { "ext-config", "", cmd_ext_config, "External configuration" }, { "debug-keys", "", cmd_debug_keys, "Toogle debug keys" }, { "conn-info", "[-t type] ", cmd_conn_info, "Get connection information" }, { "io-cap", "", cmd_io_cap, "Set IO Capability" }, { "scan-params", " ", cmd_scan_params, "Set Scan Parameters" }, { "get-clock", "[address]", cmd_clock_info, "Get Clock Information" }, { "add-device", "[-a action] [-t type]
", cmd_add_device, "Add Device" }, { "del-device", "[-t type]
", cmd_del_device, "Remove Device" }, { "clr-devices", NULL, cmd_clr_devices, "Clear Devices" }, { "bredr-oob", NULL, cmd_bredr_oob, "Local OOB data (BR/EDR)" }, { "le-oob", NULL, cmd_le_oob, "Local OOB data (LE)" }, { "advinfo", NULL, cmd_advinfo, "Show advertising features" }, { "advsize", "[options] ", cmd_advsize, "Show advertising size info" }, { "add-adv", "[options] ", cmd_add_adv, "Add advertising instance" }, { "rm-adv", "", cmd_rm_adv, "Remove advertising instance" }, { "clr-adv", NULL, cmd_clr_adv, "Clear advertising instances" }, { "add-ext-adv-params", "[options] ", cmd_add_ext_adv_params, "Add extended advertising params" }, { "add-ext-adv-data", "[options] ", cmd_add_ext_adv_data, "Add extended advertising data" }, { "appearance", "", cmd_appearance, "Set appearance" }, { "phy", "[LE1MTX] [LE1MRX] [LE2MTX] [LE2MRX] " "[LECODEDTX] [LECODEDRX] " "[BR1M1SLOT] [BR1M3SLOT] [BR1M5SLOT]" "[EDR2M1SLOT] [EDR2M3SLOT] [EDR2M5SLOT]" "[EDR3M1SLOT] [EDR3M3SLOT] [EDR3M5SLOT]", cmd_phy, "Get/Set PHY Configuration" }, { "wbs", "", cmd_wbs, "Toggle Wideband-Speech support"}, { "secinfo", NULL, cmd_secinfo, "Show security information" }, { "expinfo", NULL, cmd_expinfo, "Show experimental features" }, { "exp-debug", "", cmd_exp_debug, "Set debug feature" }, { "exp-privacy", "", cmd_exp_privacy, "Set LL privacy feature" }, { "exp-quality", "", cmd_exp_quality, "Set bluetooth quality report feature" }, { "exp-offload", "", cmd_exp_offload_codecs, "Toggle codec support" }, { "read-sysconfig", NULL, cmd_read_sysconfig, "Read System Configuration" }, { "set-sysconfig", "<-v|-h> [options...]", cmd_set_sysconfig, "Set System Configuration" }, { "get-flags", "[-t type]
", cmd_get_flags, "Get device flags" }, { "set-flags", "[-f flags] [-t type]
", cmd_set_flags, "Set device flags" }, { "hci-cmd", " [event] [timeout] [param...]", cmd_hci_cmd, "Send HCI Command and wait for Event" }, {} }, }; static void mgmt_debug(const char *str, void *user_data) { const char *prefix = user_data; print("%s%s", prefix, str); } bool mgmt_add_submenu(void) { mgmt = mgmt_new_default(); if (!mgmt) { fprintf(stderr, "Unable to open mgmt_socket\n"); return false; } bt_shell_add_submenu(&mgmt_menu); bt_shell_add_submenu(&monitor_menu); if (getenv("MGMT_DEBUG")) mgmt_set_debug(mgmt, mgmt_debug, "mgmt: ", NULL); register_mgmt_callbacks(mgmt, mgmt_index); return true; } void mgmt_remove_submenu(void) { mgmt_cancel_all(mgmt); mgmt_unregister_all(mgmt); mgmt_unref(mgmt); } bluez-5.82/client/PaxHeaders/hci.h0000644000000000000000000000005014766002272014046 xustar0020 atime=1743515577 20 ctime=1743591281 bluez-5.82/client/hci.h0000644000000000000000000000032414766002272013526 0ustar00rootroot// SPDX-License-Identifier: GPL-2.0-or-later /* * * BlueZ - Bluetooth protocol stack for Linux * * Copyright (C) 2024 Intel Corporation * * */ void hci_add_submenu(void); void hci_remove_submenu(void); bluez-5.82/client/PaxHeaders/bluetoothctl-mgmt.rst0000644000000000000000000000005014766002272017336 xustar0020 atime=1743515577 20 ctime=1743591291 bluez-5.82/client/bluetoothctl-mgmt.rst0000644000000000000000000001457314766002272017031 0ustar00rootroot================= bluetoothctl-mgmt ================= ------------------ Management Submenu ------------------ :Version: BlueZ :Copyright: Free use of this software is granted under the terms of the GNU Lesser General Public Licenses (LGPL). :Date: July 2023 :Manual section: 1 :Manual group: Linux System Administration SYNOPSIS ======== **bluetoothctl** [--options] [mgmt.commands] Mgmt Commands ============= select ------ Select a different index :Usage: **> select ** revision -------- Get the MGMT Revision :Usage: **> revision** commands -------- List supported commands :Usage: **> commands** config ------ Show configuration info :Usage: **> config** info ---- Show controller info :Usage: **> info** extinfo ------- Show extended controller info :Usage: **> extinfo** auto-power ---------- Power all available features :Usage: **> auto-power** power ----- Toggle powered state :Usage: **> power ** discov ------ Toggle discoverable state :Usage: **> discov [timeout]** connectable ----------- Toggle connectable state :Usage: **> connectable ** fast-conn --------- Toggle fast connectable state :Usage: **> fast-conn ** bondable -------- Toggle bondable state :Usage: **> bondable ** pairable -------- Toggle bondable state :Usage: **> pairable ** linksec ------- Toggle link level security :Usage: **> linksec ** ssp --- Toggle SSP mode :Usage: **> spp ** sc -- Toggle SC support :Usage: **> sc ** hs -- Toggle HS support :Usage: **> hs ** le -- Toggle LE support :Usage: **> le ** advertising ----------- Toggle LE advertising :Usage: **> advertise ** bredr ----- Toggle BR/EDR support :Usage: **> bredr ** privacy ------- Toggle privacy support :Usage: **> privacy [irk]** class ----- Set device major/minor class :Usage: **> class ** disconnect ---------- Disconnect device :Usage: **> disconnect [-t type] ** con --- List connections :Usage: **> con** find ---- Discover nearby devices :Usage: **> find [-l|-b] [-L]** find-service ------------ Discover nearby service :Usage: **> find-service [-u UUID] [-r RSSI_Threshold] [-l|-b]** stop-find --------- Stop discovery :Usage: **> stop-find [-l|-b]** name ---- Set local name :Usage: **> name [shortname]** pair ---- Pair with a remote device :Usage: **> pair [-c cap] [-t type] ** cancelpair ---------- Cancel pairing :Usage: **> cancelpair [-t type] ** unpair ------ Unpair device :Usage: **> unpair [-t type] ** keys ---- Load Link Keys :Usage: **keys** ltks ---- Load Long Term Keys :Usage: **> ltks** irks ---- Load Identity Resolving Keys :Usage: **> irks [--local index] [--file file path]** block ----- Block Device :Usage: **> block [-t type] ** unblock ------- Unblock Device :Usage: **> unblock [-t type] ** add-uuid -------- Add UUID :Usage: **> add-uuid ** rm-uuid ------- Remove UUID :Usage: **> rm-uuid ** clr-uuids --------- Clear UUIDs :Usage: **> clear-uuids** local-oob --------- Local OOB data :Usage: **> local-oob** remote-oob ---------- Remote OOB data :Usage: **> remote-oob [-t ] [-r ] [-h ] [-R ] [-H ] ** did --- Set Device ID :Usage: **> did :::** static-addr ----------- Set static address :Usage: **> static-addr
** public-addr ----------- Set public address :Usage: **> public-addr
** ext-config ---------- External configuration :Usage: **> ext-config ** debug-keys ---------- Toggle debug keys :Usage: **> debug-keys ** conn-info --------- Get connection information :Usage: **> conn-info [-t type] ** io-cap ------ Set IO Capability :Usage: **> io-cap ** scan-params ----------- Set Scan Parameters :Usage: **> scan-params ** get-clock --------- Get Clock Information :Usage: **> get-clock [address]** add-device ---------- Add Device :Usage: **> add-device [-a action] [-t type]
** del-device ---------- Remove Device :Usage: **> del-device [-t type]
** clr-devices ----------- Clear Devices :Usage: **> clr-devices** bredr-oob --------- Local OOB data (BR/EDR) :Usage: **> bredr-oob** le-oob ------ Local OOB data (LE) :Usage: **> le-oob** advinfo ------- Show advertising features :Usage: **> advinfo** advsize ------- Show advertising size info :Usage: **> advsize [options] ** add-adv ------- Add advertising instance :Usage: **> add-adv [options] ** rm-adv ------ Remove advertising instance :Usage: **> rm-adv ** clr-adv ------- Clear advertising instances :Usage: **> clr-adv** add-ext-adv-params ------------------ Add extended advertising params :Usage: **> add-ext-adv-parms [options] ** add-ext-adv-data ---------------- Add extended advertising data :Usage: **> add-ext-adv-data [options] ** appearance ---------- Set appearance :Usage: **> appearance ** phy --- Get/Set PHY Configuration :Usage: **> phy [LE1MTX] [LE1MRX] [LE2MTX] [LE2MRX] [LECODEDTX] [LECODEDRX] [BR1M1SLOT] [BR1M3SLOT] [BR1M5SLOT][EDR2M1SLOT] [EDR2M3SLOT] [EDR2M5SLOT][EDR3M1SLOT] [EDR3M3SLOT] [EDR3M5SLOT]** wbs --- Toggle Wideband-Speech support :Usage: **> wbs ** secinfo ------- Show security information :Usage: **> secinfo** expinfo ------- Show experimental features :Usage: **> expinfo** exp-debug --------- Set debug feature :Usage: **> exp-debug ** exp-privacy ----------- Set LL privacy feature :Usage: **> exp-privacy ** exp-quality ----------- Set bluetooth quality report feature :Usage: **> exp-quality ** exp-offload ----------- Toggle codec support :Usage: **> exp-offload ** read-sysconfig -------------- Read System Configuration :Usage: **> read-sysconfig** set-sysconfig ------------- Set System Configuration :Usage: **> set-sysconfig <-v|-h> [options...]** get-flags --------- Get device flags set-flags --------- Set device flags :Usage: **> set-flags [-f flags] [-t type]
** RESOURCES ========= http://www.bluez.org REPORTING BUGS ============== linux-bluetooth@vger.kernel.org bluez-5.82/client/PaxHeaders/assistant.h0000644000000000000000000000005014667536076015332 xustar0020 atime=1743515768 20 ctime=1743591281 bluez-5.82/client/assistant.h0000644000000000000000000000031714667536076015014 0ustar00rootroot// SPDX-License-Identifier: GPL-2.0-or-later /* * * BlueZ - Bluetooth protocol stack for Linux * * Copyright 2024 NXP * * */ void assistant_add_submenu(void); void assistant_remove_submenu(void); bluez-5.82/client/PaxHeaders/bluetoothctl-admin.10000644000000000000000000000005014773213523017013 xustar0020 atime=1743591251 20 ctime=1743591291 bluez-5.82/client/bluetoothctl-admin.10000644000000000000000000000255314773213523016501 0ustar00rootroot.\" Man page generated from reStructuredText. . . .nr rst2man-indent-level 0 . .de1 rstReportMargin \\$1 \\n[an-margin] level \\n[rst2man-indent-level] level margin: \\n[rst2man-indent\\n[rst2man-indent-level]] - \\n[rst2man-indent0] \\n[rst2man-indent1] \\n[rst2man-indent2] .. .de1 INDENT .\" .rstReportMargin pre: . RS \\$1 . nr rst2man-indent\\n[rst2man-indent-level] \\n[an-margin] . nr rst2man-indent-level +1 .\" .rstReportMargin post: .. .de UNINDENT . RE .\" indent \\n[an-margin] .\" old: \\n[rst2man-indent\\n[rst2man-indent-level]] .nr rst2man-indent-level -1 .\" new: \\n[rst2man-indent\\n[rst2man-indent-level]] .in \\n[rst2man-indent\\n[rst2man-indent-level]]u .. .TH "BLUETOOTHCTL-ADMIN" "1" "November 2022" "BlueZ" "Linux System Administration" .SH NAME bluetoothctl-admin \- Admin Policy Submenu .SH SYNOPSIS .sp \fBbluetoothctl\fP [\-\-options] [admin.commands] .SH ADMIN POLICY COMMANDS .SS allow .sp Allow service UUIDs and block rest of them. .INDENT 0.0 .TP .B Usage \fB> allow [clear/uuid1 uuid2 ...]\fP .TP .B Example \fB> allow 0x1101 0x1102 0x1103\fP .TP .B Example \fB> allow clear\fP .UNINDENT .SH RESOURCES .sp .SH REPORTING BUGS .sp .SH COPYRIGHT Free use of this software is granted under the terms of the GNU Lesser General Public Licenses (LGPL). .\" Generated by docutils manpage writer. . bluez-5.82/client/PaxHeaders/bluetoothctl-scan.10000644000000000000000000000005014773213531016646 xustar0020 atime=1743591257 20 ctime=1743591291 bluez-5.82/client/bluetoothctl-scan.10000644000000000000000000000756714773213531016346 0ustar00rootroot.\" Man page generated from reStructuredText. . . .nr rst2man-indent-level 0 . .de1 rstReportMargin \\$1 \\n[an-margin] level \\n[rst2man-indent-level] level margin: \\n[rst2man-indent\\n[rst2man-indent-level]] - \\n[rst2man-indent0] \\n[rst2man-indent1] \\n[rst2man-indent2] .. .de1 INDENT .\" .rstReportMargin pre: . RS \\$1 . nr rst2man-indent\\n[rst2man-indent-level] \\n[an-margin] . nr rst2man-indent-level +1 .\" .rstReportMargin post: .. .de UNINDENT . RE .\" indent \\n[an-margin] .\" old: \\n[rst2man-indent\\n[rst2man-indent-level]] .nr rst2man-indent-level -1 .\" new: \\n[rst2man-indent\\n[rst2man-indent-level]] .in \\n[rst2man-indent\\n[rst2man-indent-level]]u .. .TH "BLUETOOTHCTL-SCAN" "1" "July 2023" "BlueZ" "Linux System Administration" .SH NAME bluetoothctl-scan \- Scan Submenu .SH SYNOPSIS .sp \fBbluetoothctl\fP [\-\-options] [scan.commands] .SH SCAN COMMANDS .SS uuids .sp Set/Get UUIDs filter. .INDENT 0.0 .TP .B Usage \fB> uuids [all/uuid1 uuid2 ...]\fP .UNINDENT .SS rssi .sp Set/Get RSSI filter, and clears pathloss. .sp This sets the minimum rssi value for reporting device advertisements. .sp The value is in dBm. .sp If one or more discovery filters have been set, the RSSI delta\-threshold imposed by starting discovery by default will not be applied. .INDENT 0.0 .TP .B Usage \fB> rssi [rssi]\fP .TP .B Example \fB> rssi \-60\fP .UNINDENT .SS pathloss .sp Set/Get Pathloss filter, and clears RSSI. .sp This sets the maximum pathloss value for reporting device advertisements. .sp The value is in dB. .sp If one or more discovery filters have been set, the RSSI delta\-threshold imposed by starting discovery by default will not be applied. .INDENT 0.0 .TP .B Usage \fB> pathloss [pathloss]\fP .TP .B Example \fB> pathloss 4\fP .UNINDENT .SS transport .sp Set/Get transport filter. .sp Transport parameter determines the type of scan. .sp The default is auto. .sp Possible values: .INDENT 0.0 .IP \(bu 2 \(dqauto\(dq: interleaved scan .IP \(bu 2 \(dqbredr\(dq: BR/EDR inquiry .IP \(bu 2 \(dqle\(dq: LE scan only .UNINDENT .sp If \(dqle\(dq or \(dqbredr\(dq Transport is requested and the controller doesn\(aqt support it, an org.bluez.Error.Failed error will be returned. .sp If \(dqauto\(dq transport is requested, the scan will use LE, BREDR, or both, depending on what\(aqs currently enabled on the controller. .INDENT 0.0 .TP .B Usage \fB> transport [auto/bredr/le]\fP .UNINDENT .SS duplicate\-data .sp Set/Get duplicate data filter. .sp Disables duplicate detection of advertisement data. .sp When enabled, PropertiesChanged signals will be generated for ManufacturerData and ServiceData every time they are discovered. .INDENT 0.0 .TP .B Usage \fB> duplicate\-data [on/off]\fP .UNINDENT .SS discoverable .sp Set/Get discoverable filter. .sp Makes the adapter discoverable while discovering. .sp If the adapter is already discoverable, setting this filter won\(aqt have any effect. .INDENT 0.0 .TP .B Usage \fB> discoverable [on/off]\fP .UNINDENT .SS pattern .sp Set/Get pattern filter. .sp Discover devices where the pattern matches either the prefix of the address or the device name, which is a convenient way to limit the number of device objects created during a discovery. .sp When set, it disregards device discoverable flags. .INDENT 0.0 .TP .B Note The pattern matching is ignored if there are other clients that don\(aqt set any pattern, as it works as a logical OR. Also, setting an empty string \(dq\(dq pattern will match any device found. .TP .B Usage \fB> pattern [value]\fP .UNINDENT .SS clear .sp Clears discovery filter. .INDENT 0.0 .TP .B Usage \fB> clear [uuids/rssi/pathloss/transport/duplicate\-data/discoverable/pattern]\fP .UNINDENT .SH RESOURCES .sp .SH REPORTING BUGS .sp .SH COPYRIGHT Free use of this software is granted under the terms of the GNU Lesser General Public Licenses (LGPL). .\" Generated by docutils manpage writer. . bluez-5.82/client/PaxHeaders/advertising.h0000644000000000000000000000005014766002272015622 xustar0020 atime=1743515577 20 ctime=1743591281 bluez-5.82/client/advertising.h0000644000000000000000000000363514766002272015312 0ustar00rootroot/* SPDX-License-Identifier: GPL-2.0-or-later */ /* * * BlueZ - Bluetooth protocol stack for Linux * * Copyright (C) 2016 Intel Corporation. All rights reserved. * * */ #define AD_TYPE_AD 0 #define AD_TYPE_SRD 1 #define AD_TYPE_COUNT 2 void ad_register(DBusConnection *conn, GDBusProxy *manager, const char *type); void ad_unregister(DBusConnection *conn, GDBusProxy *manager); void ad_advertise_uuids(DBusConnection *conn, int type, int argc, char *argv[]); void ad_disable_uuids(DBusConnection *conn, int type); void ad_advertise_solicit(DBusConnection *conn, int type, int argc, char *argv[]); void ad_disable_solicit(DBusConnection *conn, int type); void ad_advertise_service(DBusConnection *conn, int type, int argc, char *argv[]); void ad_disable_service(DBusConnection *conn, int type); void ad_advertise_manufacturer(DBusConnection *conn, int type, int argc, char *argv[]); void ad_disable_manufacturer(DBusConnection *conn, int type); void ad_advertise_tx_power(DBusConnection *conn, dbus_bool_t *value); void ad_advertise_name(DBusConnection *conn, bool value); void ad_advertise_appearance(DBusConnection *conn, bool value); void ad_advertise_local_name(DBusConnection *conn, const char *name); void ad_advertise_local_appearance(DBusConnection *conn, long int *value); void ad_advertise_duration(DBusConnection *conn, long int *value); void ad_advertise_timeout(DBusConnection *conn, long int *value); void ad_advertise_data(DBusConnection *conn, int type, int argc, char *argv[]); void ad_disable_data(DBusConnection *conn, int type); void ad_advertise_discoverable(DBusConnection *conn, dbus_bool_t *value); void ad_advertise_discoverable_timeout(DBusConnection *conn, long int *value); void ad_advertise_secondary(DBusConnection *conn, const char *value); void ad_advertise_interval(DBusConnection *conn, uint32_t *min, uint32_t *max); void ad_advertise_rsi(DBusConnection *conn, dbus_bool_t *value); bluez-5.82/client/PaxHeaders/player.c0000644000000000000000000000005014772767672014615 xustar0020 atime=1743515579 20 ctime=1743591281 bluez-5.82/client/player.c0000644000000000000000000044422314772767672014307 0ustar00rootroot// SPDX-License-Identifier: GPL-2.0-or-later /* * * BlueZ - Bluetooth protocol stack for Linux * * Copyright (C) 2020 Intel Corporation. All rights reserved. * Copyright 2023-2025 NXP * * */ #ifdef HAVE_CONFIG_H #include #endif #define _GNU_SOURCE #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "gdbus/gdbus.h" #include "lib/bluetooth.h" #include "lib/uuid.h" #include "lib/iso.h" #include "profiles/audio/a2dp-codecs.h" #include "src/shared/lc3.h" #include "src/shared/util.h" #include "src/shared/shell.h" #include "src/shared/io.h" #include "src/shared/queue.h" #include "src/shared/bap-debug.h" #include "print.h" #include "player.h" /* String display constants */ #define COLORED_NEW COLOR_GREEN "NEW" COLOR_OFF #define COLORED_CHG COLOR_YELLOW "CHG" COLOR_OFF #define COLORED_DEL COLOR_RED "DEL" COLOR_OFF #define BLUEZ_MEDIA_INTERFACE "org.bluez.Media1" #define BLUEZ_MEDIA_PLAYER_INTERFACE "org.bluez.MediaPlayer1" #define BLUEZ_MEDIA_FOLDER_INTERFACE "org.bluez.MediaFolder1" #define BLUEZ_MEDIA_ITEM_INTERFACE "org.bluez.MediaItem1" #define BLUEZ_MEDIA_ENDPOINT_INTERFACE "org.bluez.MediaEndpoint1" #define BLUEZ_MEDIA_TRANSPORT_INTERFACE "org.bluez.MediaTransport1" #define BLUEZ_MEDIA_ENDPOINT_PATH "/local/endpoint" #define NSEC_USEC(_t) (_t / 1000L) #define SEC_USEC(_t) (_t * 1000000L) #define TS_USEC(_ts) (SEC_USEC((_ts)->tv_sec) + NSEC_USEC((_ts)->tv_nsec)) #define ROUND_CLOSEST(_x, _y) (((_x) + (_y / 2)) / (_y)) #define EP_SRC_LOCATIONS 0x00000003 #define EP_SNK_LOCATIONS 0x00000003 #define EP_SRC_CTXT 0x000f #define EP_SUPPORTED_SRC_CTXT EP_SRC_CTXT #define EP_SNK_CTXT 0x0fff #define EP_SUPPORTED_SNK_CTXT EP_SNK_CTXT #if __BYTE_ORDER == __LITTLE_ENDIAN struct avdtp_media_codec_capability { uint8_t rfa0:4; uint8_t media_type:4; uint8_t media_codec_type; uint8_t data[0]; } __attribute__ ((packed)); #elif __BYTE_ORDER == __BIG_ENDIAN struct avdtp_media_codec_capability { uint8_t media_type:4; uint8_t rfa0:4; uint8_t media_codec_type; uint8_t data[0]; } __attribute__ ((packed)); #else #error "Unknown byte order" #endif #define BCAST_CODE {0x01, 0x02, 0x68, 0x05, 0x53, 0xf1, 0x41, 0x5a, \ 0xa2, 0x65, 0xbb, 0xaf, 0xc6, 0xea, 0x03, 0xb8} struct endpoint { char *path; char *uuid; uint8_t codec; uint16_t cid; uint16_t vid; struct iovec *caps; struct iovec *meta; uint32_t locations; uint16_t supported_context; uint16_t context; bool auto_accept; uint8_t max_transports; uint8_t iso_group; uint8_t iso_stream; struct queue *acquiring; struct queue *transports; DBusMessage *msg; struct preset *preset; struct codec_preset *codec_preset; bool broadcast; struct iovec *bcode; }; static DBusConnection *dbus_conn; static GDBusProxy *default_player; static GList *medias = NULL; static GList *players = NULL; static GList *folders = NULL; static GList *items = NULL; static GList *endpoints = NULL; static GList *local_endpoints = NULL; static GList *transports = NULL; static struct queue *ios = NULL; static uint8_t bcast_code[] = BCAST_CODE; struct transport { GDBusProxy *proxy; int sk; uint16_t mtu[2]; char *filename; int fd; struct stat stat; struct io *io; uint32_t seq; struct io *timer_io; int num; }; struct transport_select_args { GDBusProxy *proxy; struct queue *links; struct queue *selecting; }; static void transport_set_links(struct transport_select_args *args); static void transport_select(struct transport_select_args *args); static void endpoint_unregister(void *data) { struct endpoint *ep = data; bt_shell_printf("Endpoint %s unregistered\n", ep->path); g_dbus_unregister_interface(dbus_conn, ep->path, BLUEZ_MEDIA_ENDPOINT_INTERFACE); } static void disconnect_handler(DBusConnection *connection, void *user_data) { g_list_free_full(local_endpoints, endpoint_unregister); local_endpoints = NULL; } static bool check_default_player(void) { if (!default_player) { bt_shell_printf("No default player available\n"); return FALSE; } return TRUE; } static char *generic_generator(const char *text, int state, GList *source) { static int index = 0; if (!source) return NULL; if (!state) index = 0; return g_dbus_proxy_path_lookup(source, &index, text); } static char *player_generator(const char *text, int state) { return generic_generator(text, state, players); } static char *item_generator(const char *text, int state) { return generic_generator(text, state, items); } static void play_reply(DBusMessage *message, void *user_data) { DBusError error; dbus_error_init(&error); if (dbus_set_error_from_message(&error, message) == TRUE) { bt_shell_printf("Failed to play: %s\n", error.name); dbus_error_free(&error); return bt_shell_noninteractive_quit(EXIT_FAILURE); } bt_shell_printf("Play successful\n"); return bt_shell_noninteractive_quit(EXIT_FAILURE); } static void cmd_play(int argc, char *argv[]) { GDBusProxy *proxy; if (argc > 1) { proxy = g_dbus_proxy_lookup(items, NULL, argv[1], BLUEZ_MEDIA_ITEM_INTERFACE); if (proxy == NULL) { bt_shell_printf("Item %s not available\n", argv[1]); return bt_shell_noninteractive_quit(EXIT_FAILURE); } } else { if (!check_default_player()) return bt_shell_noninteractive_quit(EXIT_FAILURE); proxy = default_player; } if (g_dbus_proxy_method_call(proxy, "Play", NULL, play_reply, NULL, NULL) == FALSE) { bt_shell_printf("Failed to play\n"); return bt_shell_noninteractive_quit(EXIT_FAILURE); } bt_shell_printf("Attempting to play %s\n", argv[1] ? : ""); } static void pause_reply(DBusMessage *message, void *user_data) { DBusError error; dbus_error_init(&error); if (dbus_set_error_from_message(&error, message) == TRUE) { bt_shell_printf("Failed to pause: %s\n", error.name); dbus_error_free(&error); return bt_shell_noninteractive_quit(EXIT_FAILURE); } bt_shell_printf("Pause successful\n"); return bt_shell_noninteractive_quit(EXIT_SUCCESS); } static void cmd_pause(int argc, char *argv[]) { if (!check_default_player()) return bt_shell_noninteractive_quit(EXIT_FAILURE); if (g_dbus_proxy_method_call(default_player, "Pause", NULL, pause_reply, NULL, NULL) == FALSE) { bt_shell_printf("Failed to play\n"); return bt_shell_noninteractive_quit(EXIT_FAILURE); } bt_shell_printf("Attempting to pause\n"); } static void stop_reply(DBusMessage *message, void *user_data) { DBusError error; dbus_error_init(&error); if (dbus_set_error_from_message(&error, message) == TRUE) { bt_shell_printf("Failed to stop: %s\n", error.name); dbus_error_free(&error); return bt_shell_noninteractive_quit(EXIT_FAILURE); } bt_shell_printf("Stop successful\n"); return bt_shell_noninteractive_quit(EXIT_SUCCESS); } static void cmd_stop(int argc, char *argv[]) { if (!check_default_player()) return bt_shell_noninteractive_quit(EXIT_FAILURE); if (g_dbus_proxy_method_call(default_player, "Stop", NULL, stop_reply, NULL, NULL) == FALSE) { bt_shell_printf("Failed to stop\n"); return bt_shell_noninteractive_quit(EXIT_FAILURE); } bt_shell_printf("Attempting to stop\n"); } static void next_reply(DBusMessage *message, void *user_data) { DBusError error; dbus_error_init(&error); if (dbus_set_error_from_message(&error, message) == TRUE) { bt_shell_printf("Failed to jump to next: %s\n", error.name); dbus_error_free(&error); return bt_shell_noninteractive_quit(EXIT_FAILURE); } bt_shell_printf("Next successful\n"); return bt_shell_noninteractive_quit(EXIT_SUCCESS); } static void cmd_next(int argc, char *argv[]) { if (!check_default_player()) return bt_shell_noninteractive_quit(EXIT_FAILURE); if (g_dbus_proxy_method_call(default_player, "Next", NULL, next_reply, NULL, NULL) == FALSE) { bt_shell_printf("Failed to jump to next\n"); return bt_shell_noninteractive_quit(EXIT_FAILURE); } bt_shell_printf("Attempting to jump to next\n"); } static void previous_reply(DBusMessage *message, void *user_data) { DBusError error; dbus_error_init(&error); if (dbus_set_error_from_message(&error, message) == TRUE) { bt_shell_printf("Failed to jump to previous: %s\n", error.name); dbus_error_free(&error); return bt_shell_noninteractive_quit(EXIT_FAILURE); } bt_shell_printf("Previous successful\n"); return bt_shell_noninteractive_quit(EXIT_SUCCESS); } static void cmd_previous(int argc, char *argv[]) { if (!check_default_player()) return bt_shell_noninteractive_quit(EXIT_FAILURE); if (g_dbus_proxy_method_call(default_player, "Previous", NULL, previous_reply, NULL, NULL) == FALSE) { bt_shell_printf("Failed to jump to previous\n"); return bt_shell_noninteractive_quit(EXIT_FAILURE); } bt_shell_printf("Attempting to jump to previous\n"); return bt_shell_noninteractive_quit(EXIT_SUCCESS); } static void fast_forward_reply(DBusMessage *message, void *user_data) { DBusError error; dbus_error_init(&error); if (dbus_set_error_from_message(&error, message) == TRUE) { bt_shell_printf("Failed to fast forward: %s\n", error.name); dbus_error_free(&error); return bt_shell_noninteractive_quit(EXIT_FAILURE); } bt_shell_printf("FastForward successful\n"); return bt_shell_noninteractive_quit(EXIT_SUCCESS); } static void cmd_fast_forward(int argc, char *argv[]) { if (!check_default_player()) return bt_shell_noninteractive_quit(EXIT_FAILURE); if (g_dbus_proxy_method_call(default_player, "FastForward", NULL, fast_forward_reply, NULL, NULL) == FALSE) { bt_shell_printf("Failed to jump to previous\n"); return bt_shell_noninteractive_quit(EXIT_FAILURE); } bt_shell_printf("Fast forward playback\n"); return bt_shell_noninteractive_quit(EXIT_SUCCESS); } static void rewind_reply(DBusMessage *message, void *user_data) { DBusError error; dbus_error_init(&error); if (dbus_set_error_from_message(&error, message) == TRUE) { bt_shell_printf("Failed to rewind: %s\n", error.name); dbus_error_free(&error); return bt_shell_noninteractive_quit(EXIT_FAILURE); } bt_shell_printf("Rewind successful\n"); return bt_shell_noninteractive_quit(EXIT_SUCCESS); } static void cmd_rewind(int argc, char *argv[]) { if (!check_default_player()) return bt_shell_noninteractive_quit(EXIT_FAILURE); if (g_dbus_proxy_method_call(default_player, "Rewind", NULL, rewind_reply, NULL, NULL) == FALSE) { bt_shell_printf("Failed to rewind\n"); return bt_shell_noninteractive_quit(EXIT_FAILURE); } bt_shell_printf("Rewind playback\n"); } static void generic_callback(const DBusError *error, void *user_data) { char *str = user_data; if (dbus_error_is_set(error)) { bt_shell_printf("Failed to set %s: %s\n", str, error->name); return bt_shell_noninteractive_quit(EXIT_FAILURE); } else { bt_shell_printf("Changing %s succeeded\n", str); return bt_shell_noninteractive_quit(EXIT_SUCCESS); } } static void cmd_equalizer(int argc, char *argv[]) { char *value; DBusMessageIter iter; if (!check_default_player()) return bt_shell_noninteractive_quit(EXIT_FAILURE); if (!g_dbus_proxy_get_property(default_player, "Equalizer", &iter)) { bt_shell_printf("Operation not supported\n"); return bt_shell_noninteractive_quit(EXIT_FAILURE); } value = g_strdup(argv[1]); if (g_dbus_proxy_set_property_basic(default_player, "Equalizer", DBUS_TYPE_STRING, &value, generic_callback, value, g_free) == FALSE) { bt_shell_printf("Failed to setting equalizer\n"); g_free(value); return bt_shell_noninteractive_quit(EXIT_FAILURE); } bt_shell_printf("Attempting to set equalizer\n"); return bt_shell_noninteractive_quit(EXIT_SUCCESS); } static void cmd_repeat(int argc, char *argv[]) { char *value; DBusMessageIter iter; if (!check_default_player()) return bt_shell_noninteractive_quit(EXIT_FAILURE); if (!g_dbus_proxy_get_property(default_player, "Repeat", &iter)) { bt_shell_printf("Operation not supported\n"); return bt_shell_noninteractive_quit(EXIT_FAILURE); } value = g_strdup(argv[1]); if (g_dbus_proxy_set_property_basic(default_player, "Repeat", DBUS_TYPE_STRING, &value, generic_callback, value, g_free) == FALSE) { bt_shell_printf("Failed to set repeat\n"); g_free(value); return bt_shell_noninteractive_quit(EXIT_FAILURE); } bt_shell_printf("Attempting to set repeat\n"); } static void cmd_shuffle(int argc, char *argv[]) { char *value; DBusMessageIter iter; if (!check_default_player()) return bt_shell_noninteractive_quit(EXIT_FAILURE); if (!g_dbus_proxy_get_property(default_player, "Shuffle", &iter)) { bt_shell_printf("Operation not supported\n"); return bt_shell_noninteractive_quit(EXIT_FAILURE); } value = g_strdup(argv[1]); if (g_dbus_proxy_set_property_basic(default_player, "Shuffle", DBUS_TYPE_STRING, &value, generic_callback, value, g_free) == FALSE) { bt_shell_printf("Failed to set shuffle\n"); g_free(value); return bt_shell_noninteractive_quit(EXIT_FAILURE); } bt_shell_printf("Attempting to set shuffle\n"); } static void cmd_scan(int argc, char *argv[]) { char *value; DBusMessageIter iter; if (!check_default_player()) return bt_shell_noninteractive_quit(EXIT_FAILURE); if (!g_dbus_proxy_get_property(default_player, "Shuffle", &iter)) { bt_shell_printf("Operation not supported\n"); return bt_shell_noninteractive_quit(EXIT_FAILURE); } value = g_strdup(argv[1]); if (g_dbus_proxy_set_property_basic(default_player, "Shuffle", DBUS_TYPE_STRING, &value, generic_callback, value, g_free) == FALSE) { bt_shell_printf("Failed to set scan\n"); g_free(value); return bt_shell_noninteractive_quit(EXIT_FAILURE); } bt_shell_printf("Attempting to set scan\n"); } static char *proxy_description(GDBusProxy *proxy, const char *title, const char *description) { const char *path; path = g_dbus_proxy_get_path(proxy); return g_strdup_printf("%s%s%s%s %s ", description ? "[" : "", description ? : "", description ? "] " : "", title, path); } static void print_media(GDBusProxy *proxy, const char *description) { char *str; str = proxy_description(proxy, "Media", description); bt_shell_printf("%s\n", str); print_property(proxy, "SupportedUUIDs"); g_free(str); } static void print_player(void *data, void *user_data) { GDBusProxy *proxy = data; const char *description = user_data; char *str; str = proxy_description(proxy, "Player", description); bt_shell_printf("%s%s\n", str, default_player == proxy ? "[default]" : ""); g_free(str); } static void cmd_list(int argc, char *arg[]) { g_list_foreach(players, print_player, NULL); return bt_shell_noninteractive_quit(EXIT_SUCCESS); } static void cmd_show_item(int argc, char *argv[]) { GDBusProxy *proxy; proxy = g_dbus_proxy_lookup(items, NULL, argv[1], BLUEZ_MEDIA_ITEM_INTERFACE); if (!proxy) { bt_shell_printf("Item %s not available\n", argv[1]); return bt_shell_noninteractive_quit(EXIT_SUCCESS); } bt_shell_printf("Item %s\n", g_dbus_proxy_get_path(proxy)); print_property(proxy, "Player"); print_property(proxy, "Name"); print_property(proxy, "Type"); print_property(proxy, "FolderType"); print_property(proxy, "Playable"); print_property(proxy, "Metadata"); return bt_shell_noninteractive_quit(EXIT_SUCCESS); } static void cmd_show(int argc, char *argv[]) { GDBusProxy *proxy; GDBusProxy *folder; GDBusProxy *item; DBusMessageIter iter; const char *path; if (argc < 2) { if (check_default_player() == FALSE) return bt_shell_noninteractive_quit(EXIT_FAILURE); proxy = default_player; } else { proxy = g_dbus_proxy_lookup(players, NULL, argv[1], BLUEZ_MEDIA_PLAYER_INTERFACE); if (!proxy) { bt_shell_printf("Player %s not available\n", argv[1]); return bt_shell_noninteractive_quit(EXIT_FAILURE); } } bt_shell_printf("Player %s\n", g_dbus_proxy_get_path(proxy)); print_property(proxy, "Name"); print_property(proxy, "Repeat"); print_property(proxy, "Equalizer"); print_property(proxy, "Shuffle"); print_property(proxy, "Scan"); print_property(proxy, "Status"); print_property(proxy, "Position"); print_property(proxy, "Track"); folder = g_dbus_proxy_lookup(folders, NULL, g_dbus_proxy_get_path(proxy), BLUEZ_MEDIA_FOLDER_INTERFACE); if (folder == NULL) return bt_shell_noninteractive_quit(EXIT_SUCCESS); bt_shell_printf("Folder %s\n", g_dbus_proxy_get_path(proxy)); print_property(folder, "Name"); print_property(folder, "NumberOfItems"); if (!g_dbus_proxy_get_property(proxy, "Playlist", &iter)) return bt_shell_noninteractive_quit(EXIT_SUCCESS); dbus_message_iter_get_basic(&iter, &path); item = g_dbus_proxy_lookup(items, NULL, path, BLUEZ_MEDIA_ITEM_INTERFACE); if (item == NULL) return bt_shell_noninteractive_quit(EXIT_SUCCESS); bt_shell_printf("Playlist %s\n", path); print_property(item, "Name"); return bt_shell_noninteractive_quit(EXIT_SUCCESS); } static void cmd_select(int argc, char *argv[]) { GDBusProxy *proxy; proxy = g_dbus_proxy_lookup(players, NULL, argv[1], BLUEZ_MEDIA_PLAYER_INTERFACE); if (proxy == NULL) { bt_shell_printf("Player %s not available\n", argv[1]); return bt_shell_noninteractive_quit(EXIT_FAILURE); } if (default_player == proxy) return bt_shell_noninteractive_quit(EXIT_SUCCESS); default_player = proxy; print_player(proxy, NULL); return bt_shell_noninteractive_quit(EXIT_SUCCESS); } static void change_folder_reply(DBusMessage *message, void *user_data) { DBusError error; dbus_error_init(&error); if (dbus_set_error_from_message(&error, message) == TRUE) { bt_shell_printf("Failed to change folder: %s\n", error.name); dbus_error_free(&error); return bt_shell_noninteractive_quit(EXIT_FAILURE); } bt_shell_printf("ChangeFolder successful\n"); return bt_shell_noninteractive_quit(EXIT_SUCCESS); } static void change_folder_setup(DBusMessageIter *iter, void *user_data) { const char *path = user_data; dbus_message_iter_append_basic(iter, DBUS_TYPE_OBJECT_PATH, &path); } static void cmd_change_folder(int argc, char *argv[]) { GDBusProxy *proxy; if (dbus_validate_path(argv[1], NULL) == FALSE) { bt_shell_printf("Not a valid path\n"); return bt_shell_noninteractive_quit(EXIT_FAILURE); } if (check_default_player() == FALSE) return bt_shell_noninteractive_quit(EXIT_FAILURE); proxy = g_dbus_proxy_lookup(folders, NULL, g_dbus_proxy_get_path(default_player), BLUEZ_MEDIA_FOLDER_INTERFACE); if (proxy == NULL) { bt_shell_printf("Operation not supported\n"); return bt_shell_noninteractive_quit(EXIT_FAILURE); } if (g_dbus_proxy_method_call(proxy, "ChangeFolder", change_folder_setup, change_folder_reply, argv[1], NULL) == FALSE) { bt_shell_printf("Failed to change current folder\n"); return bt_shell_noninteractive_quit(EXIT_FAILURE); } bt_shell_printf("Attempting to change folder\n"); } struct list_items_args { int start; int end; }; static void list_items_setup(DBusMessageIter *iter, void *user_data) { struct list_items_args *args = user_data; DBusMessageIter dict; dbus_message_iter_open_container(iter, DBUS_TYPE_ARRAY, DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING DBUS_TYPE_STRING_AS_STRING DBUS_TYPE_VARIANT_AS_STRING DBUS_DICT_ENTRY_END_CHAR_AS_STRING, &dict); if (args->start < 0) goto done; g_dbus_dict_append_entry(&dict, "Start", DBUS_TYPE_UINT32, &args->start); if (args->end < 0) goto done; g_dbus_dict_append_entry(&dict, "End", DBUS_TYPE_UINT32, &args->end); done: dbus_message_iter_close_container(iter, &dict); } static void list_items_reply(DBusMessage *message, void *user_data) { DBusError error; dbus_error_init(&error); if (dbus_set_error_from_message(&error, message) == TRUE) { bt_shell_printf("Failed to list items: %s\n", error.name); dbus_error_free(&error); return bt_shell_noninteractive_quit(EXIT_FAILURE); } bt_shell_printf("ListItems successful\n"); return bt_shell_noninteractive_quit(EXIT_SUCCESS); } static void cmd_list_items(int argc, char *argv[]) { GDBusProxy *proxy; struct list_items_args *args; if (check_default_player() == FALSE) return bt_shell_noninteractive_quit(EXIT_FAILURE); proxy = g_dbus_proxy_lookup(folders, NULL, g_dbus_proxy_get_path(default_player), BLUEZ_MEDIA_FOLDER_INTERFACE); if (proxy == NULL) { bt_shell_printf("Operation not supported\n"); return bt_shell_noninteractive_quit(EXIT_FAILURE); } args = g_new0(struct list_items_args, 1); args->start = -1; args->end = -1; if (argc < 2) goto done; errno = 0; args->start = strtol(argv[1], NULL, 10); if (errno != 0) { bt_shell_printf("%s(%d)\n", strerror(errno), errno); g_free(args); return bt_shell_noninteractive_quit(EXIT_FAILURE); } if (argc < 3) goto done; errno = 0; args->end = strtol(argv[2], NULL, 10); if (errno != 0) { bt_shell_printf("%s(%d)\n", strerror(errno), errno); g_free(args); return bt_shell_noninteractive_quit(EXIT_FAILURE); } done: if (g_dbus_proxy_method_call(proxy, "ListItems", list_items_setup, list_items_reply, args, g_free) == FALSE) { bt_shell_printf("Failed to change current folder\n"); g_free(args); return bt_shell_noninteractive_quit(EXIT_FAILURE); } bt_shell_printf("Attempting to list items\n"); } static void search_setup(DBusMessageIter *iter, void *user_data) { char *string = user_data; DBusMessageIter dict; dbus_message_iter_append_basic(iter, DBUS_TYPE_STRING, &string); dbus_message_iter_open_container(iter, DBUS_TYPE_ARRAY, DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING DBUS_TYPE_STRING_AS_STRING DBUS_TYPE_VARIANT_AS_STRING DBUS_DICT_ENTRY_END_CHAR_AS_STRING, &dict); dbus_message_iter_close_container(iter, &dict); } static void search_reply(DBusMessage *message, void *user_data) { DBusError error; dbus_error_init(&error); if (dbus_set_error_from_message(&error, message) == TRUE) { bt_shell_printf("Failed to search: %s\n", error.name); dbus_error_free(&error); return bt_shell_noninteractive_quit(EXIT_FAILURE); } bt_shell_printf("Search successful\n"); return bt_shell_noninteractive_quit(EXIT_SUCCESS); } static void cmd_search(int argc, char *argv[]) { GDBusProxy *proxy; char *string; if (check_default_player() == FALSE) return bt_shell_noninteractive_quit(EXIT_FAILURE); proxy = g_dbus_proxy_lookup(folders, NULL, g_dbus_proxy_get_path(default_player), BLUEZ_MEDIA_FOLDER_INTERFACE); if (proxy == NULL) { bt_shell_printf("Operation not supported\n"); return bt_shell_noninteractive_quit(EXIT_FAILURE); } string = g_strdup(argv[1]); if (g_dbus_proxy_method_call(proxy, "Search", search_setup, search_reply, string, g_free) == FALSE) { bt_shell_printf("Failed to search\n"); g_free(string); return bt_shell_noninteractive_quit(EXIT_FAILURE); } bt_shell_printf("Attempting to search\n"); } static void add_to_nowplaying_reply(DBusMessage *message, void *user_data) { DBusError error; dbus_error_init(&error); if (dbus_set_error_from_message(&error, message) == TRUE) { bt_shell_printf("Failed to queue: %s\n", error.name); dbus_error_free(&error); return bt_shell_noninteractive_quit(EXIT_FAILURE); } bt_shell_printf("AddToNowPlaying successful\n"); return bt_shell_noninteractive_quit(EXIT_SUCCESS); } static void cmd_queue(int argc, char *argv[]) { GDBusProxy *proxy; proxy = g_dbus_proxy_lookup(items, NULL, argv[1], BLUEZ_MEDIA_ITEM_INTERFACE); if (proxy == NULL) { bt_shell_printf("Item %s not available\n", argv[1]); return bt_shell_noninteractive_quit(EXIT_FAILURE); } if (g_dbus_proxy_method_call(proxy, "AddtoNowPlaying", NULL, add_to_nowplaying_reply, NULL, NULL) == FALSE) { bt_shell_printf("Failed to play\n"); return bt_shell_noninteractive_quit(EXIT_FAILURE); } bt_shell_printf("Attempting to queue %s\n", argv[1]); } static const struct bt_shell_menu player_menu = { .name = "player", .desc = "Media Player Submenu", .entries = { { "list", NULL, cmd_list, "List available players" }, { "show", "[player]", cmd_show, "Player information", player_generator}, { "select", "", cmd_select, "Select default player", player_generator}, { "play", "[item]", cmd_play, "Start playback", item_generator}, { "pause", NULL, cmd_pause, "Pause playback" }, { "stop", NULL, cmd_stop, "Stop playback" }, { "next", NULL, cmd_next, "Jump to next item" }, { "previous", NULL, cmd_previous, "Jump to previous item" }, { "fast-forward", NULL, cmd_fast_forward, "Fast forward playback" }, { "rewind", NULL, cmd_rewind, "Rewind playback" }, { "equalizer", "", cmd_equalizer, "Enable/Disable equalizer"}, { "repeat", "", cmd_repeat, "Set repeat mode"}, { "shuffle", "", cmd_shuffle, "Set shuffle mode"}, { "scan", "", cmd_scan, "Set scan mode"}, { "change-folder", "", cmd_change_folder, "Change current folder", item_generator}, { "list-items", "[start] [end]", cmd_list_items, "List items of current folder" }, { "search", "", cmd_search, "Search items containing string" }, { "queue", "", cmd_queue, "Add item to playlist queue", item_generator}, { "show-item", "", cmd_show_item, "Show item information", item_generator}, {} }, }; static char *local_endpoint_generator(const char *text, int state) { int len = strlen(text); GList *l; static int index = 0; if (!state) index = 0; for (l = g_list_nth(local_endpoints, index); l; l = g_list_next(l)) { struct endpoint *ep = l->data; index++; if (!strncasecmp(ep->path, text, len)) return strdup(ep->path); } return NULL; } static char *endpoint_generator(const char *text, int state) { char *ret; ret = generic_generator(text, state, endpoints); if (ret) return ret; return local_endpoint_generator(text, state); } static void print_endpoint(void *data, void *user_data) { GDBusProxy *proxy = data; const char *description = user_data; char *str; str = proxy_description(proxy, "Endpoint", description); bt_shell_printf("%s\n", str); g_free(str); } static void cmd_list_endpoints(int argc, char *argv[]) { GList *l; if (argc > 1) { if (strcmp("local", argv[1])) { bt_shell_printf("Endpoint list %s not available\n", argv[1]); return bt_shell_noninteractive_quit(EXIT_SUCCESS); } for (l = local_endpoints; l; l = g_list_next(l)) { struct endpoint *ep = l->data; bt_shell_printf("%s\n", ep->path); } return bt_shell_noninteractive_quit(EXIT_SUCCESS); } for (l = endpoints; l; l = g_list_next(l)) { GDBusProxy *proxy = l->data; print_endpoint(proxy, NULL); } return bt_shell_noninteractive_quit(EXIT_SUCCESS); } static void confirm_response(const char *input, void *user_data) { DBusMessage *msg = user_data; if (!strcasecmp(input, "y") || !strcasecmp(input, "yes")) g_dbus_send_reply(dbus_conn, msg, DBUS_TYPE_INVALID); else if (!strcasecmp(input, "n") || !strcmp(input, "no")) g_dbus_send_error(dbus_conn, msg, "org.bluez.Error.Rejected", NULL); else g_dbus_send_error(dbus_conn, msg, "org.bluez.Error.Canceled", NULL); } static DBusMessage *endpoint_set_configuration(DBusConnection *conn, DBusMessage *msg, void *user_data) { struct endpoint *ep = user_data; DBusMessageIter args, props; const char *path; dbus_message_iter_init(msg, &args); dbus_message_iter_get_basic(&args, &path); dbus_message_iter_next(&args); dbus_message_iter_recurse(&args, &props); if (dbus_message_iter_get_arg_type(&props) != DBUS_TYPE_DICT_ENTRY) return g_dbus_create_error(msg, "org.bluez.Error.InvalidArguments", NULL); bt_shell_printf("Endpoint: SetConfiguration\n"); bt_shell_printf("\tTransport %s\n", path); print_iter("\t", "Properties", &props); if (!ep->max_transports) { bt_shell_printf("Maximum transports reached: rejecting\n"); return g_dbus_create_error(msg, "org.bluez.Error.Rejected", "Maximum transports reached"); } ep->max_transports--; if (!ep->transports) ep->transports = queue_new(); queue_push_tail(ep->transports, strdup(path)); if (ep->auto_accept) { bt_shell_printf("Auto Accepting...\n"); return g_dbus_create_reply(msg, DBUS_TYPE_INVALID); } bt_shell_prompt_input("Endpoint", "Accept (yes/no):", confirm_response, dbus_message_ref(msg)); return NULL; } #define CODEC_CAPABILITIES(_name, _uuid, _codec_id, _data, _meta) \ { \ .name = _name, \ .uuid = _uuid, \ .codec_id = _codec_id, \ .data = _data, \ .meta = _meta, \ } #define LC3_DATA(_freq, _duration, _len_min, _len_max) \ UTIL_IOV_INIT(0x03, LC3_FREQ, _freq, _freq >> 8, \ 0x02, LC3_DURATION, _duration, \ 0x05, LC3_FRAME_LEN, _len_min, _len_min >> 8, \ _len_max, _len_max >> 8) static const struct capabilities { const char *name; const char *uuid; uint8_t codec_id; struct iovec data; struct iovec meta; } caps[] = { /* A2DP SBC Source: * * Channel Modes: Mono DualChannel Stereo JointStereo * Frequencies: 16Khz 32Khz 44.1Khz 48Khz * Subbands: 4 8 * Blocks: 4 8 12 16 * Bitpool Range: 2-64 */ CODEC_CAPABILITIES("a2dp_src/sbc", A2DP_SOURCE_UUID, A2DP_CODEC_SBC, UTIL_IOV_INIT(0xff, 0xff, 2, 64), UTIL_IOV_INIT()), /* A2DP SBC Sink: * * Channel Modes: Mono DualChannel Stereo JointStereo * Frequencies: 16Khz 32Khz 44.1Khz 48Khz * Subbands: 4 8 * Blocks: 4 8 12 16 * Bitpool Range: 2-64 */ CODEC_CAPABILITIES("a2dp_snk/sbc", A2DP_SINK_UUID, A2DP_CODEC_SBC, UTIL_IOV_INIT(0xff, 0xff, 2, 64), UTIL_IOV_INIT()), /* PAC LC3 Sink: * * Frequencies: 8Khz 11Khz 16Khz 22Khz 24Khz 32Khz 44.1Khz 48Khz * Duration: 7.5 ms 10 ms * Frame length: 26-240 */ CODEC_CAPABILITIES("pac_snk/lc3", PAC_SINK_UUID, LC3_ID, LC3_DATA(LC3_FREQ_ANY, LC3_DURATION_ANY, 26, 240), UTIL_IOV_INIT()), /* PAC LC3 Source: * * Frequencies: 8Khz 11Khz 16Khz 22Khz 24Khz 32Khz 44.1Khz 48Khz * Duration: 7.5 ms 10 ms * Channel count: 3 * Frame length: 26-240 */ CODEC_CAPABILITIES("pac_src/lc3", PAC_SOURCE_UUID, LC3_ID, LC3_DATA(LC3_FREQ_ANY, LC3_DURATION_ANY, 26, 240), UTIL_IOV_INIT()), /* Broadcast LC3 Source: * * Frequencies: 8Khz 11Khz 16Khz 22Khz 24Khz 32Khz 44.1Khz 48Khz * Duration: 7.5 ms 10 ms * Channel count: 3 * Frame length: 26-240 */ CODEC_CAPABILITIES("bcaa/lc3", BCAA_SERVICE_UUID, LC3_ID, LC3_DATA(LC3_FREQ_ANY, LC3_DURATION_ANY, 26, 240), UTIL_IOV_INIT()), /* Broadcast LC3 Sink: * * Frequencies: 8Khz 11Khz 16Khz 22Khz 24Khz 32Khz 44.1Khz 48Khz * Duration: 7.5 ms 10 ms * Channel count: 3 * Frame length: 26-240 */ CODEC_CAPABILITIES("baa/lc3", BAA_SERVICE_UUID, LC3_ID, LC3_DATA(LC3_FREQ_ANY, LC3_DURATION_ANY, 26, 240), UTIL_IOV_INIT()), }; struct codec_preset { char *name; const struct iovec data; const struct iovec meta; struct bt_bap_qos qos; uint8_t target_latency; uint32_t chan_alloc; bool custom; bool alt; struct codec_preset *alt_preset; }; #define SBC_PRESET(_name, _data) \ { \ .name = _name, \ .data = _data, \ } static struct codec_preset sbc_presets[] = { /* Table 4.7: Recommended sets of SBC parameters in the SRC device * Other settings: Block length = 16, Allocation method = Loudness, * Subbands = 8. * A2DP spec sets maximum bitrates as follows: * This profile limits the available maximum bit rate to 320kb/s for * mono, and 512kb/s for two-channel modes. */ SBC_PRESET("MQ_MONO_44_1", UTIL_IOV_INIT(0x28, 0x15, 2, SBC_BITPOOL_MQ_MONO_44100)), SBC_PRESET("MQ_MONO_48", UTIL_IOV_INIT(0x18, 0x15, 2, SBC_BITPOOL_MQ_MONO_48000)), SBC_PRESET("MQ_STEREO_44_1", UTIL_IOV_INIT(0x21, 0x15, 2, SBC_BITPOOL_MQ_JOINT_STEREO_44100)), SBC_PRESET("MQ_STEREO_48", UTIL_IOV_INIT(0x11, 0x15, 2, SBC_BITPOOL_MQ_JOINT_STEREO_48000)), SBC_PRESET("HQ_MONO_44_1", UTIL_IOV_INIT(0x28, 0x15, 2, SBC_BITPOOL_HQ_MONO_44100)), SBC_PRESET("HQ_MONO_48", UTIL_IOV_INIT(0x18, 0x15, 2, SBC_BITPOOL_HQ_MONO_48000)), SBC_PRESET("HQ_STEREO_44_1", UTIL_IOV_INIT(0x21, 0x15, 2, SBC_BITPOOL_HQ_JOINT_STEREO_44100)), SBC_PRESET("HQ_STEREO_48", UTIL_IOV_INIT(0x11, 0x15, 2, SBC_BITPOOL_HQ_JOINT_STEREO_48000)), /* Higher bitrates not recommended by A2DP spec, it dual channel to * avoid going above 53 bitpool: * * https://habr.com/en/post/456476/ * https://gitlab.freedesktop.org/pulseaudio/pulseaudio/-/issues/1092 */ SBC_PRESET("XQ_DUAL_44_1", UTIL_IOV_INIT(0x24, 0x15, 2, 43)), SBC_PRESET("XQ_DUAL_48", UTIL_IOV_INIT(0x14, 0x15, 2, 39)), /* Ultra high bitpool that fits in 512 kbps mandatory bitrate */ SBC_PRESET("UQ_STEREO_44_1", UTIL_IOV_INIT(0x21, 0x15, 2, 64)), SBC_PRESET("UQ_STEREO_48", UTIL_IOV_INIT(0x11, 0x15, 2, 58)), }; #define LC3_PRESET_LL(_name, _data, _qos) \ { \ .name = _name, \ .data = _data, \ .qos = _qos, \ .target_latency = 0x01, \ } #define LC3_PRESET(_name, _data, _qos) \ { \ .name = _name, \ .data = _data, \ .qos = _qos, \ .target_latency = 0x02, \ } #define LC3_PRESET_HR(_name, _data, _qos) \ { \ .name = _name, \ .data = _data, \ .qos = _qos, \ .target_latency = 0x03, \ } #define LC3_PRESET_B(_name, _data, _qos) \ { \ .name = _name, \ .data = _data, \ .qos = _qos, \ .target_latency = 0x00, \ } static struct codec_preset lc3_ucast_presets[] = { /* Table 4.43: QoS configuration support setting requirements */ LC3_PRESET("8_1_1", LC3_CONFIG_8_1, LC3_QOS_8_1_1), LC3_PRESET("8_2_1", LC3_CONFIG_8_2, LC3_QOS_8_2_1), LC3_PRESET("16_1_1", LC3_CONFIG_16_1, LC3_QOS_16_1_1), LC3_PRESET("16_2_1", LC3_CONFIG_16_2, LC3_QOS_16_2_1), LC3_PRESET("24_1_1", LC3_CONFIG_24_1, LC3_QOS_24_1_1), LC3_PRESET("24_2_1", LC3_CONFIG_24_2, LC3_QOS_24_2_1), LC3_PRESET("32_1_1", LC3_CONFIG_32_1, LC3_QOS_32_1_1), LC3_PRESET("32_2_1", LC3_CONFIG_32_2, LC3_QOS_32_1_1), LC3_PRESET("44_1_1", LC3_CONFIG_44_1, LC3_QOS_44_1_1), LC3_PRESET("44_2_1", LC3_CONFIG_44_2, LC3_QOS_44_2_1), LC3_PRESET("48_1_1", LC3_CONFIG_48_1, LC3_QOS_48_1_1), LC3_PRESET("48_2_1", LC3_CONFIG_48_2, LC3_QOS_48_2_1), LC3_PRESET("48_3_1", LC3_CONFIG_48_3, LC3_QOS_48_3_1), LC3_PRESET("48_4_1", LC3_CONFIG_48_4, LC3_QOS_48_4_1), LC3_PRESET("48_5_1", LC3_CONFIG_48_5, LC3_QOS_48_5_1), LC3_PRESET("48_6_1", LC3_CONFIG_48_6, LC3_QOS_48_6_1), /* QoS Configuration settings for high reliability audio data */ LC3_PRESET_HR("8_1_2", LC3_CONFIG_8_1, LC3_QOS_8_1_2), LC3_PRESET_HR("8_2_2", LC3_CONFIG_8_2, LC3_QOS_8_2_2), LC3_PRESET_HR("16_1_2", LC3_CONFIG_16_1, LC3_QOS_16_1_2), LC3_PRESET_HR("16_2_2", LC3_CONFIG_16_2, LC3_QOS_16_2_2), LC3_PRESET_HR("24_1_2", LC3_CONFIG_24_1, LC3_QOS_24_1_2), LC3_PRESET_HR("24_2_2", LC3_CONFIG_24_2, LC3_QOS_24_2_2), LC3_PRESET_HR("32_1_2", LC3_CONFIG_32_1, LC3_QOS_32_1_2), LC3_PRESET_HR("32_2_2", LC3_CONFIG_32_2, LC3_QOS_32_2_2), LC3_PRESET_HR("44_1_2", LC3_CONFIG_44_1, LC3_QOS_44_1_2), LC3_PRESET_HR("44_2_2", LC3_CONFIG_44_2, LC3_QOS_44_2_2), LC3_PRESET_HR("48_1_2", LC3_CONFIG_48_1, LC3_QOS_48_1_2), LC3_PRESET_HR("48_2_2", LC3_CONFIG_48_2, LC3_QOS_48_2_2), LC3_PRESET_HR("48_3_2", LC3_CONFIG_48_3, LC3_QOS_48_3_2), LC3_PRESET_HR("48_4_2", LC3_CONFIG_48_4, LC3_QOS_48_4_2), LC3_PRESET_HR("48_5_2", LC3_CONFIG_48_5, LC3_QOS_48_5_2), LC3_PRESET_HR("48_6_2", LC3_CONFIG_48_6, LC3_QOS_48_6_2), /* QoS configuration support setting requirements for the UGG and UGT */ LC3_PRESET_LL("16_1_gs", LC3_CONFIG_16_1, LC3_QOS_16_1_GS), LC3_PRESET_LL("16_2_gs", LC3_CONFIG_16_2, LC3_QOS_16_2_GS), LC3_PRESET_LL("32_1_gs", LC3_CONFIG_32_1, LC3_QOS_32_1_GS), LC3_PRESET_LL("32_2_gs", LC3_CONFIG_32_2, LC3_QOS_32_2_GS), LC3_PRESET_LL("48_1_gs", LC3_CONFIG_48_1, LC3_QOS_48_1_GS), LC3_PRESET_LL("48_2_gs", LC3_CONFIG_48_2, LC3_QOS_48_2_GS), LC3_PRESET_LL("32_1_gr", LC3_CONFIG_32_1, LC3_QOS_32_1_GR), LC3_PRESET_LL("32_2_gr", LC3_CONFIG_32_2, LC3_QOS_32_2_GR), LC3_PRESET_LL("48_1_gr", LC3_CONFIG_48_1, LC3_QOS_48_1_GR), LC3_PRESET_LL("48_2_gr", LC3_CONFIG_48_2, LC3_QOS_48_2_GR), LC3_PRESET_LL("48_3_gr", LC3_CONFIG_48_3, LC3_QOS_48_3_GR), LC3_PRESET_LL("48_4_gr", LC3_CONFIG_48_4, LC3_QOS_48_4_GR), LC3_PRESET_LL("32_1_gr_l+r", LC3_CONFIG_32_1_AC(2), LC3_QOS_32_1_GR_AC(2)), LC3_PRESET_LL("32_2_gr_l+r", LC3_CONFIG_32_2_AC(2), LC3_QOS_32_2_GR_AC(2)), LC3_PRESET_LL("48_1_gr_l+r", LC3_CONFIG_48_1_AC(2), LC3_QOS_48_1_GR_AC(2)), LC3_PRESET_LL("48_2_gr_l+r", LC3_CONFIG_48_2_AC(2), LC3_QOS_48_2_GR_AC(2)), LC3_PRESET_LL("48_3_gr_l+r", LC3_CONFIG_48_3_AC(2), LC3_QOS_48_3_GR_AC(2)), LC3_PRESET_LL("48_4_gr_l+r", LC3_CONFIG_48_4_AC(2), LC3_QOS_48_4_GR_AC(2)), }; static struct codec_preset lc3_bcast_presets[] = { /* Table 6.4: Broadcast Audio Stream configuration support requirements * for the Broadcast Source and Broadcast Sink */ LC3_PRESET_B("8_1_1", LC3_CONFIG_8_1, LC3_QOS_8_1_1_B), LC3_PRESET_B("8_2_1", LC3_CONFIG_8_2, LC3_QOS_8_2_1_B), LC3_PRESET_B("16_1_1", LC3_CONFIG_16_1, LC3_QOS_16_1_1_B), LC3_PRESET_B("16_2_1", LC3_CONFIG_16_2, LC3_QOS_16_2_1_B), LC3_PRESET_B("24_1_1", LC3_CONFIG_24_1, LC3_QOS_24_1_1_B), LC3_PRESET_B("24_2_1", LC3_CONFIG_24_2, LC3_QOS_24_2_1_B), LC3_PRESET_B("32_1_1", LC3_CONFIG_32_1, LC3_QOS_32_1_1_B), LC3_PRESET_B("32_2_1", LC3_CONFIG_32_2, LC3_QOS_32_2_1_B), LC3_PRESET_B("44_1_1", LC3_CONFIG_44_1, LC3_QOS_44_1_1_B), LC3_PRESET_B("44_2_1", LC3_CONFIG_44_2, LC3_QOS_44_2_1_B), LC3_PRESET_B("48_1_1", LC3_CONFIG_48_1, LC3_QOS_48_1_1_B), LC3_PRESET_B("48_2_1", LC3_CONFIG_48_2, LC3_QOS_48_2_1_B), LC3_PRESET_B("48_3_1", LC3_CONFIG_48_3, LC3_QOS_48_3_1_B), LC3_PRESET_B("48_4_1", LC3_CONFIG_48_4, LC3_QOS_48_4_1_B), LC3_PRESET_B("48_5_1", LC3_CONFIG_48_5, LC3_QOS_48_5_1_B), LC3_PRESET_B("48_6_1", LC3_CONFIG_48_6, LC3_QOS_48_6_1_B), /* Broadcast Audio Stream configuration settings for high-reliability * audio data. */ LC3_PRESET_B("8_1_2", LC3_CONFIG_8_1, LC3_QOS_8_1_1_B), LC3_PRESET_B("8_2_2", LC3_CONFIG_8_2, LC3_QOS_8_2_2_B), LC3_PRESET_B("16_1_2", LC3_CONFIG_16_1, LC3_QOS_16_1_2_B), LC3_PRESET_B("16_2_2", LC3_CONFIG_16_2, LC3_QOS_16_2_2_B), LC3_PRESET_B("24_1_2", LC3_CONFIG_24_1, LC3_QOS_24_1_2_B), LC3_PRESET_B("24_2_2", LC3_CONFIG_24_2, LC3_QOS_24_2_2_B), LC3_PRESET_B("32_1_2", LC3_CONFIG_32_1, LC3_QOS_32_1_2_B), LC3_PRESET_B("32_2_2", LC3_CONFIG_32_2, LC3_QOS_32_2_2_B), LC3_PRESET_B("44_1_2", LC3_CONFIG_44_1, LC3_QOS_44_1_2_B), LC3_PRESET_B("44_2_2", LC3_CONFIG_44_2, LC3_QOS_44_2_2_B), LC3_PRESET_B("48_1_2", LC3_CONFIG_48_1, LC3_QOS_48_1_2_B), LC3_PRESET_B("48_2_2", LC3_CONFIG_48_2, LC3_QOS_48_2_2_B), LC3_PRESET_B("48_3_2", LC3_CONFIG_48_3, LC3_QOS_48_3_2_B), LC3_PRESET_B("48_4_2", LC3_CONFIG_48_4, LC3_QOS_48_4_2_B), LC3_PRESET_B("48_5_2", LC3_CONFIG_48_5, LC3_QOS_48_5_2_B), LC3_PRESET_B("48_6_2", LC3_CONFIG_48_6, LC3_QOS_48_6_2_B), }; static void print_ltv(const char *str, void *user_data) { const char *label = user_data; bt_shell_printf("\t%s.%s\n", label, str); } static void print_lc3_caps(uint8_t *data, int len) { const char *label = "Capabilities"; bt_bap_debug_caps(data, len, print_ltv, (void *)label); } static void print_lc3_cfg(void *data, int len) { const char *label = "Configuration"; bt_bap_debug_config(data, len, print_ltv, (void *)label); } static void print_lc3_meta(void *data, int len) { const char *label = "Metadata"; bt_bap_debug_metadata(data, len, print_ltv, (void *)label); } #define PRESET(_uuid, _codec, _presets, _default_index) \ { \ .uuid = _uuid, \ .codec = _codec, \ .default_preset = &_presets[_default_index], \ .presets = _presets, \ .num_presets = ARRAY_SIZE(_presets), \ } static struct preset { const char *uuid; uint8_t codec; uint16_t cid; uint16_t vid; struct queue *custom; struct codec_preset *default_preset; struct codec_preset *presets; size_t num_presets; } presets[] = { PRESET(A2DP_SOURCE_UUID, A2DP_CODEC_SBC, sbc_presets, 6), PRESET(A2DP_SINK_UUID, A2DP_CODEC_SBC, sbc_presets, 6), PRESET(PAC_SINK_UUID, LC3_ID, lc3_ucast_presets, 3), PRESET(PAC_SOURCE_UUID, LC3_ID, lc3_ucast_presets, 3), PRESET(BCAA_SERVICE_UUID, LC3_ID, lc3_bcast_presets, 3), PRESET(BAA_SERVICE_UUID, LC3_ID, lc3_bcast_presets, 3), }; static void parse_vendor_codec(const char *codec, uint16_t *vid, uint16_t *cid) { char **list; char *endptr = NULL; if (!codec) return; list = g_strsplit(codec, ":", 2); if (vid) *vid = strtol(list[0], &endptr, 0); if (cid) *cid = strtol(list[1], &endptr, 0); g_strfreev(list); } static struct preset *find_presets(const char *uuid, uint8_t codec, uint16_t vid, uint16_t cid) { size_t i; if (codec == 0xff) { GList *l; for (l = local_endpoints; l; l = g_list_next(l)) { struct endpoint *ep = l->data; if (strcasecmp(ep->uuid, uuid) || ep->codec != codec) continue; if (ep->codec == 0xff && (ep->vid != vid || ep->cid != cid)) continue; return ep->preset; } return NULL; } for (i = 0; i < ARRAY_SIZE(presets); i++) { struct preset *preset = &presets[i]; if (preset->codec != codec) continue; if (!strcasecmp(preset->uuid, uuid)) return preset; } return NULL; } static struct preset *find_vendor_presets(const char *uuid, const char *codec) { uint16_t cid; uint16_t vid; if (!uuid || !codec) return NULL; parse_vendor_codec(codec, &vid, &cid); return find_presets(uuid, 0xff, vid, cid); } static struct preset *find_presets_name(const char *uuid, const char *codec) { uint8_t id; char *endptr = NULL; if (!uuid || !codec) return NULL; if (strrchr(codec, ':')) return find_vendor_presets(uuid, codec); id = strtol(codec, &endptr, 0); return find_presets(uuid, id, 0x0000, 0x0000); } static bool match_custom_name(const void *data, const void *match_data) { const struct codec_preset *preset = data; const char *name = match_data; return !strcmp(preset->name, name); } static struct codec_preset *preset_find_name(struct preset *preset, const char *name) { size_t i; if (!preset) return NULL; if (!name) return preset->default_preset; for (i = 0; i < preset->num_presets; i++) { struct codec_preset *p; p = &preset->presets[i]; if (!strcmp(p->name, name)) return p; } return queue_find(preset->custom, match_custom_name, name); } static DBusMessage *endpoint_select_config_reply(DBusMessage *msg, uint8_t *data, size_t len) { DBusMessage *reply; DBusMessageIter args, array; reply = dbus_message_new_method_return(msg); if (!reply) return NULL; dbus_message_iter_init_append(reply, &args); dbus_message_iter_open_container(&args, DBUS_TYPE_ARRAY, DBUS_TYPE_BYTE_AS_STRING, &array); dbus_message_iter_append_fixed_array(&array, DBUS_TYPE_BYTE, &data, len); dbus_message_iter_close_container(&args, &array); return reply; } static uint8_t *str2bytearray(char *arg, size_t *val_len) { uint8_t value[UINT8_MAX]; char *entry; unsigned int i; for (i = 0; (entry = strsep(&arg, " \t")) != NULL; i++) { long val; char *endptr = NULL; if (*entry == '\0') continue; if (i >= G_N_ELEMENTS(value)) { bt_shell_printf("Too much data\n"); return NULL; } val = strtol(entry, &endptr, 0); if (!endptr || *endptr != '\0' || val > UINT8_MAX) { bt_shell_printf("Invalid value at index %d\n", i); return NULL; } value[i] = val; } *val_len = i; return util_memdup(value, i); } static void select_config_response(const char *input, void *user_data) { struct endpoint *ep = user_data; struct codec_preset *p; DBusMessage *reply; uint8_t *data; size_t len; p = preset_find_name(ep->preset, input); if (p) { data = p->data.iov_base; len = p->data.iov_len; goto done; } data = str2bytearray((void *) input, &len); if (!data) { g_dbus_send_error(dbus_conn, ep->msg, "org.bluez.Error.Rejected", NULL); ep->msg = NULL; return; } done: reply = endpoint_select_config_reply(ep->msg, data, len); if (!reply) return; if (!p) free(data); g_dbus_send_message(dbus_conn, reply); dbus_message_unref(ep->msg); ep->msg = NULL; } static DBusMessage *endpoint_select_configuration(DBusConnection *conn, DBusMessage *msg, void *user_data) { struct endpoint *ep = user_data; struct codec_preset *p; DBusMessageIter args; DBusMessage *reply; dbus_message_iter_init(msg, &args); bt_shell_printf("Endpoint: SelectConfiguration\n"); print_iter("\t", "Capabilities", &args); if (!ep->max_transports) { bt_shell_printf("Maximum transports reached: rejecting\n"); return g_dbus_create_error(msg, "org.bluez.Error.Rejected", "Maximum transports reached"); } if (!ep->auto_accept) { ep->msg = dbus_message_ref(msg); bt_shell_prompt_input("Endpoint", "Enter preset/configuration:", select_config_response, ep); return NULL; } p = preset_find_name(ep->preset, NULL); if (!p) { reply = g_dbus_create_error(msg, "org.bluez.Error.Rejected", NULL); return reply; } reply = endpoint_select_config_reply(msg, p->data.iov_base, p->data.iov_len); if (!reply) { reply = g_dbus_create_error(msg, "org.bluez.Error.Rejected", NULL); return reply; } bt_shell_printf("Auto Accepting using %s...\n", p->name); return reply; } struct endpoint_config { GDBusProxy *proxy; struct endpoint *ep; struct iovec *caps; /* Codec Specific Configuration LTVs */ struct iovec *meta; /* Metadata LTVs*/ uint8_t target_latency; struct bt_bap_qos qos; /* BAP QOS configuration parameters */ }; static void append_io_qos(DBusMessageIter *iter, struct bt_bap_io_qos *qos) { bt_shell_printf("Interval %u\n", qos->interval); g_dbus_dict_append_entry(iter, "Interval", DBUS_TYPE_UINT32, &qos->interval); bt_shell_printf("PHY 0x%02x\n", qos->phy); g_dbus_dict_append_entry(iter, "PHY", DBUS_TYPE_BYTE, &qos->phy); bt_shell_printf("SDU %u\n", qos->sdu); g_dbus_dict_append_entry(iter, "SDU", DBUS_TYPE_UINT16, &qos->sdu); bt_shell_printf("Retransmissions %u\n", qos->rtn); g_dbus_dict_append_entry(iter, "Retransmissions", DBUS_TYPE_BYTE, &qos->rtn); bt_shell_printf("Latency %u\n", qos->latency); g_dbus_dict_append_entry(iter, "Latency", DBUS_TYPE_UINT16, &qos->latency); } static void append_ucast_qos(DBusMessageIter *iter, struct endpoint_config *cfg) { struct bt_bap_ucast_qos *qos = &cfg->qos.ucast; if (cfg->ep->iso_group != BT_ISO_QOS_GROUP_UNSET) { bt_shell_printf("CIG 0x%2.2x\n", cfg->ep->iso_group); g_dbus_dict_append_entry(iter, "CIG", DBUS_TYPE_BYTE, &cfg->ep->iso_group); } if (cfg->ep->iso_stream != BT_ISO_QOS_STREAM_UNSET) { bt_shell_printf("CIS 0x%2.2x\n", cfg->ep->iso_stream); g_dbus_dict_append_entry(iter, "CIS", DBUS_TYPE_BYTE, &cfg->ep->iso_stream); } bt_shell_printf("Framing 0x%02x\n", qos->framing); g_dbus_dict_append_entry(iter, "Framing", DBUS_TYPE_BYTE, &qos->framing); bt_shell_printf("PresentationDelay %u\n", qos->delay); g_dbus_dict_append_entry(iter, "PresentationDelay", DBUS_TYPE_UINT32, &qos->delay); if (cfg->target_latency) { bt_shell_printf("TargetLatency 0x%02x\n", cfg->target_latency); g_dbus_dict_append_entry(iter, "TargetLatency", DBUS_TYPE_BYTE, &cfg->target_latency); } append_io_qos(iter, &qos->io_qos); } static void append_bcast_qos(DBusMessageIter *iter, struct endpoint_config *cfg) { struct bt_bap_bcast_qos *qos = &cfg->qos.bcast; if (cfg->ep->iso_group != BT_ISO_QOS_BIG_UNSET) { bt_shell_printf("BIG 0x%2.2x\n", cfg->ep->iso_group); g_dbus_dict_append_entry(iter, "BIG", DBUS_TYPE_BYTE, &cfg->ep->iso_group); } if (cfg->ep->iso_stream != BT_ISO_QOS_BIS_UNSET) { bt_shell_printf("BIS 0x%2.2x\n", cfg->ep->iso_stream); g_dbus_dict_append_entry(iter, "BIS", DBUS_TYPE_BYTE, &cfg->ep->iso_stream); } if (qos->sync_factor) { bt_shell_printf("SyncFactor %u\n", qos->sync_factor); g_dbus_dict_append_entry(iter, "SyncFactor", DBUS_TYPE_BYTE, &qos->sync_factor); } if (qos->options) { bt_shell_printf("Options %u\n", qos->options); g_dbus_dict_append_entry(iter, "Options", DBUS_TYPE_BYTE, &qos->options); } if (qos->skip) { bt_shell_printf("Skip %u\n", qos->skip); g_dbus_dict_append_entry(iter, "Skip", DBUS_TYPE_UINT16, &qos->skip); } if (qos->sync_timeout) { bt_shell_printf("SyncTimeout %u\n", qos->sync_timeout); g_dbus_dict_append_entry(iter, "SyncTimeout", DBUS_TYPE_UINT16, &qos->sync_timeout); } if (qos->sync_cte_type) { bt_shell_printf("SyncCteType %u\n", qos->sync_cte_type); g_dbus_dict_append_entry(iter, "SyncCteType", DBUS_TYPE_BYTE, &qos->sync_cte_type); } if (qos->mse) { bt_shell_printf("MSE %u\n", qos->mse); g_dbus_dict_append_entry(iter, "MSE", DBUS_TYPE_BYTE, &qos->mse); } if (qos->timeout) { bt_shell_printf("Timeout %u\n", qos->timeout); g_dbus_dict_append_entry(iter, "Timeout", DBUS_TYPE_UINT16, &qos->timeout); } if (cfg->ep->bcode->iov_len != 0) { const char *key = "BCode"; bt_shell_printf("BCode:\n"); bt_shell_hexdump(cfg->ep->bcode->iov_base, cfg->ep->bcode->iov_len); g_dbus_dict_append_basic_array(iter, DBUS_TYPE_STRING, &key, DBUS_TYPE_BYTE, &cfg->ep->bcode->iov_base, cfg->ep->bcode->iov_len); } bt_shell_printf("Framing 0x%02x\n", qos->framing); g_dbus_dict_append_entry(iter, "Framing", DBUS_TYPE_BYTE, &qos->framing); bt_shell_printf("PresentationDelay %u\n", qos->delay); g_dbus_dict_append_entry(iter, "PresentationDelay", DBUS_TYPE_UINT32, &qos->delay); /* Add BAP codec QOS configuration */ append_io_qos(iter, &qos->io_qos); } static void append_qos(DBusMessageIter *iter, struct endpoint_config *cfg) { DBusMessageIter entry, var, dict; const char *key = "QoS"; dbus_message_iter_open_container(iter, DBUS_TYPE_DICT_ENTRY, NULL, &entry); dbus_message_iter_append_basic(&entry, DBUS_TYPE_STRING, &key); dbus_message_iter_open_container(&entry, DBUS_TYPE_VARIANT, "a{sv}", &var); dbus_message_iter_open_container(&var, DBUS_TYPE_ARRAY, "{sv}", &dict); if (cfg->ep->broadcast) append_bcast_qos(&dict, cfg); else append_ucast_qos(&dict, cfg); dbus_message_iter_close_container(&var, &dict); dbus_message_iter_close_container(&entry, &var); dbus_message_iter_close_container(iter, &entry); } static void append_properties(DBusMessageIter *iter, struct endpoint_config *cfg) { DBusMessageIter dict; const char *key = "Capabilities"; dbus_message_iter_open_container(iter, DBUS_TYPE_ARRAY, "{sv}", &dict); if (cfg->ep->codec == LC3_ID) { print_lc3_cfg(cfg->caps->iov_base, cfg->caps->iov_len); } else { bt_shell_printf("Capabilities: "); bt_shell_hexdump(cfg->caps->iov_base, cfg->caps->iov_len); } g_dbus_dict_append_basic_array(&dict, DBUS_TYPE_STRING, &key, DBUS_TYPE_BYTE, &cfg->caps->iov_base, cfg->caps->iov_len); if (cfg->meta && cfg->meta->iov_len) { const char *meta = "Metadata"; g_dbus_dict_append_basic_array(&dict, DBUS_TYPE_STRING, &meta, DBUS_TYPE_BYTE, &cfg->meta->iov_base, cfg->meta->iov_len); if (cfg->ep->codec == LC3_ID) { print_lc3_meta(cfg->meta->iov_base, cfg->meta->iov_len); } else { bt_shell_printf("Metadata:\n"); bt_shell_hexdump(cfg->meta->iov_base, cfg->meta->iov_len); } } append_qos(&dict, cfg); dbus_message_iter_close_container(iter, &dict); } static int parse_chan_alloc(DBusMessageIter *iter, uint32_t *location, uint8_t *channels) { while (dbus_message_iter_get_arg_type(iter) == DBUS_TYPE_DICT_ENTRY) { const char *key; DBusMessageIter value, entry; int var; dbus_message_iter_recurse(iter, &entry); dbus_message_iter_get_basic(&entry, &key); dbus_message_iter_next(&entry); dbus_message_iter_recurse(&entry, &value); var = dbus_message_iter_get_arg_type(&value); if (!strcasecmp(key, "ChannelAllocation")) { if (var != DBUS_TYPE_UINT32) return -EINVAL; dbus_message_iter_get_basic(&value, location); if (*channels) *channels = __builtin_popcount(*location); return 0; } else if (!strcasecmp(key, "Locations")) { uint32_t tmp; if (var != DBUS_TYPE_UINT32) return -EINVAL; dbus_message_iter_get_basic(&value, &tmp); *location &= tmp; if (*channels) *channels = __builtin_popcount(*location); } dbus_message_iter_next(iter); } return *location ? 0 : -EINVAL; } static void ltv_find(size_t i, uint8_t l, uint8_t t, uint8_t *v, void *user_data) { bool *found = user_data; *found = true; } static DBusMessage *endpoint_select_properties_reply(struct endpoint *ep, DBusMessage *msg, struct codec_preset *preset) { DBusMessage *reply; DBusMessageIter iter, props; struct endpoint_config *cfg; struct bt_bap_io_qos *qos; uint32_t location = ep->locations; uint8_t channels = 1; if (!preset) return NULL; reply = dbus_message_new_method_return(msg); if (!reply) return NULL; cfg = new0(struct endpoint_config, 1); cfg->ep = ep; /* Copy capabilities */ cfg->caps = util_iov_dup(&preset->data, 1); cfg->target_latency = preset->target_latency; dbus_message_iter_init(msg, &iter); dbus_message_iter_recurse(&iter, &props); if (!parse_chan_alloc(&props, &location, &channels)) { uint32_t chan_alloc = 0; uint8_t type = LC3_CONFIG_CHAN_ALLOC; bool found = false; if (preset->chan_alloc & location) chan_alloc = preset->chan_alloc & location; else if (preset->alt_preset && preset->alt_preset->chan_alloc & location) { chan_alloc = preset->alt_preset->chan_alloc & location; preset = preset->alt_preset; /* Copy alternate capabilities */ util_iov_free(cfg->caps, 1); cfg->caps = util_iov_dup(&preset->data, 1); cfg->target_latency = preset->target_latency; } else chan_alloc = location; /* Check if Channel Allocation is present in caps */ util_ltv_foreach(cfg->caps->iov_base, cfg->caps->iov_len, &type, ltv_find, &found); /* If Channel Allocation has not been set directly via * preset->data then attempt to set it if chan_alloc has been * set. */ if (!found && chan_alloc) { uint8_t chan_alloc_ltv[] = { 0x05, LC3_CONFIG_CHAN_ALLOC, chan_alloc & 0xff, chan_alloc >> 8, chan_alloc >> 16, chan_alloc >> 24 }; put_le32(chan_alloc, &chan_alloc_ltv[2]); util_iov_append(cfg->caps, &chan_alloc_ltv, sizeof(chan_alloc_ltv)); } } /* Copy metadata */ if (preset->meta.iov_len) cfg->meta = util_iov_dup(&preset->meta, 1); else cfg->meta = util_iov_dup(ep->meta, 1); if (ep->broadcast) qos = &preset->qos.bcast.io_qos; else qos = &preset->qos.ucast.io_qos; if (qos->phy) { /* Set QoS parameters */ cfg->qos = preset->qos; /* Adjust the SDU size based on the number of * locations/channels that is being requested. */ if (channels > 1) { if (ep->broadcast) cfg->qos.bcast.io_qos.sdu *= channels; else cfg->qos.ucast.io_qos.sdu *= channels; } } dbus_message_iter_init_append(reply, &iter); bt_shell_printf("selecting %s...\n", preset->name); append_properties(&iter, cfg); free(cfg); return reply; } static struct codec_preset *endpoint_find_codec_preset(struct endpoint *ep, const char *name) { if (ep->codec_preset && (!name || !strcmp(ep->codec_preset->name, name))) return ep->codec_preset; return preset_find_name(ep->preset, name); } static void select_properties_response(const char *input, void *user_data) { struct endpoint *ep = user_data; struct codec_preset *p; DBusMessage *reply; p = endpoint_find_codec_preset(ep, input); if (p) { reply = endpoint_select_properties_reply(ep, ep->msg, p); goto done; } bt_shell_printf("Preset %s not found\n", input); reply = g_dbus_create_error(ep->msg, "org.bluez.Error.Rejected", NULL); done: g_dbus_send_message(dbus_conn, reply); dbus_message_unref(ep->msg); ep->msg = NULL; } static DBusMessage *endpoint_select_properties(DBusConnection *conn, DBusMessage *msg, void *user_data) { struct endpoint *ep = user_data; struct codec_preset *p; DBusMessageIter args; DBusMessage *reply; dbus_message_iter_init(msg, &args); bt_shell_printf("Endpoint: SelectProperties\n"); print_iter("\t", "Properties", &args); if (!ep->max_transports) { bt_shell_printf("Maximum transports reached: rejecting\n"); return g_dbus_create_error(msg, "org.bluez.Error.Rejected", "Maximum transports reached"); } if (!ep->auto_accept) { ep->msg = dbus_message_ref(msg); bt_shell_prompt_input("Endpoint", "Enter preset/configuration:", select_properties_response, ep); return NULL; } p = endpoint_find_codec_preset(ep, NULL); if (!p) return NULL; reply = endpoint_select_properties_reply(ep, msg, p); if (!reply) return NULL; return reply; } static bool match_str(const void *data, const void *user_data) { return !strcmp(data, user_data); } static DBusMessage *endpoint_clear_configuration(DBusConnection *conn, DBusMessage *msg, void *user_data) { struct endpoint *ep = user_data; DBusMessageIter args; const char *path; dbus_message_iter_init(msg, &args); dbus_message_iter_get_basic(&args, &path); if (ep->max_transports != UINT8_MAX) ep->max_transports++; queue_remove_if(ep->transports, match_str, (void *)path); return g_dbus_create_reply(msg, DBUS_TYPE_INVALID); } static struct endpoint *endpoint_find(const char *pattern) { GList *l; for (l = local_endpoints; l; l = g_list_next(l)) { struct endpoint *ep = l->data; /* match object path */ if (!strcmp(ep->path, pattern)) return ep; /* match UUID */ if (!strcmp(ep->uuid, pattern)) return ep; } return NULL; } static void print_aptx_common(a2dp_aptx_t *aptx) { bt_shell_printf("\n\t\tFrequencies: "); if (aptx->frequency & APTX_SAMPLING_FREQ_16000) bt_shell_printf("16kHz "); if (aptx->frequency & APTX_SAMPLING_FREQ_32000) bt_shell_printf("32kHz "); if (aptx->frequency & APTX_SAMPLING_FREQ_44100) bt_shell_printf("44.1kHz "); if (aptx->frequency & APTX_SAMPLING_FREQ_48000) bt_shell_printf("48kHz "); bt_shell_printf("\n\t\tChannel modes: "); if (aptx->channel_mode & APTX_CHANNEL_MODE_MONO) bt_shell_printf("Mono "); if (aptx->channel_mode & APTX_CHANNEL_MODE_STEREO) bt_shell_printf("Stereo "); } static void print_aptx(a2dp_aptx_t *aptx, uint8_t size) { bt_shell_printf("\t\tVendor Specific Value (aptX)"); if (size < sizeof(*aptx)) { bt_shell_printf(" (broken)\n"); return; } print_aptx_common(aptx); bt_shell_printf("\n"); } static void print_faststream(a2dp_faststream_t *faststream, uint8_t size) { bt_shell_printf("\t\tVendor Specific Value (FastStream)"); if (size < sizeof(*faststream)) { bt_shell_printf(" (broken)\n"); return; } bt_shell_printf("\n\t\tDirections: "); if (faststream->direction & FASTSTREAM_DIRECTION_SINK) bt_shell_printf("sink "); if (faststream->direction & FASTSTREAM_DIRECTION_SOURCE) bt_shell_printf("source "); if (faststream->direction & FASTSTREAM_DIRECTION_SINK) { bt_shell_printf("\n\t\tSink Frequencies: "); if (faststream->sink_frequency & FASTSTREAM_SINK_SAMPLING_FREQ_44100) bt_shell_printf("44.1kHz "); if (faststream->sink_frequency & FASTSTREAM_SINK_SAMPLING_FREQ_48000) bt_shell_printf("48kHz "); } if (faststream->direction & FASTSTREAM_DIRECTION_SOURCE) { bt_shell_printf("\n\t\tSource Frequencies: "); if (faststream->source_frequency & FASTSTREAM_SOURCE_SAMPLING_FREQ_16000) bt_shell_printf("16kHz "); } bt_shell_printf("\n"); } static void print_aptx_ll(a2dp_aptx_ll_t *aptx_ll, uint8_t size) { a2dp_aptx_ll_new_caps_t *aptx_ll_new; bt_shell_printf("\t\tVendor Specific Value (aptX Low Latency)"); if (size < sizeof(*aptx_ll)) { bt_shell_printf(" (broken)\n"); return; } print_aptx_common(&aptx_ll->aptx); bt_shell_printf("\n\tBidirectional link: %s", aptx_ll->bidirect_link ? "Yes" : "No"); aptx_ll_new = &aptx_ll->new_caps[0]; if (aptx_ll->has_new_caps && size >= sizeof(*aptx_ll) + sizeof(*aptx_ll_new)) { bt_shell_printf("\n\tTarget codec buffer level: %u", (unsigned int)aptx_ll_new->target_level2 | ((unsigned int)(aptx_ll_new->target_level1) << 8)); bt_shell_printf("\n\tInitial codec buffer level: %u", (unsigned int)aptx_ll_new->initial_level2 | ((unsigned int)(aptx_ll_new->initial_level1) << 8)); bt_shell_printf("\n\tSRA max rate: %g", aptx_ll_new->sra_max_rate / 10000.0); bt_shell_printf("\n\tSRA averaging time: %us", (unsigned int)aptx_ll_new->sra_avg_time); bt_shell_printf("\n\tGood working codec buffer level: %u", (unsigned int)aptx_ll_new->good_working_level2 | ((unsigned int)(aptx_ll_new->good_working_level1) << 8) ); } bt_shell_printf("\n"); } static void print_aptx_hd(a2dp_aptx_hd_t *aptx_hd, uint8_t size) { bt_shell_printf("\t\tVendor Specific Value (aptX HD)"); if (size < sizeof(*aptx_hd)) { bt_shell_printf(" (broken)\n"); return; } print_aptx_common(&aptx_hd->aptx); bt_shell_printf("\n"); } static void print_ldac(a2dp_ldac_t *ldac, uint8_t size) { bt_shell_printf("\t\tVendor Specific Value (LDAC)"); if (size < sizeof(*ldac)) { bt_shell_printf(" (broken)\n"); return; } bt_shell_printf("\n\t\tFrequencies: "); if (ldac->frequency & LDAC_SAMPLING_FREQ_44100) bt_shell_printf("44.1kHz "); if (ldac->frequency & LDAC_SAMPLING_FREQ_48000) bt_shell_printf("48kHz "); if (ldac->frequency & LDAC_SAMPLING_FREQ_88200) bt_shell_printf("88.2kHz "); if (ldac->frequency & LDAC_SAMPLING_FREQ_96000) bt_shell_printf("96kHz "); if (ldac->frequency & LDAC_SAMPLING_FREQ_176400) bt_shell_printf("176.4kHz "); if (ldac->frequency & LDAC_SAMPLING_FREQ_192000) bt_shell_printf("192kHz "); bt_shell_printf("\n\t\tChannel modes: "); if (ldac->channel_mode & LDAC_CHANNEL_MODE_MONO) bt_shell_printf("Mono "); if (ldac->channel_mode & LDAC_CHANNEL_MODE_DUAL) bt_shell_printf("Dual "); if (ldac->channel_mode & LDAC_CHANNEL_MODE_STEREO) bt_shell_printf("Stereo "); bt_shell_printf("\n"); } static void print_opus_g(a2dp_opus_g_t *opus, uint8_t size) { bt_shell_printf("\t\tVendor Specific Value (Opus [Google])"); if (size < sizeof(*opus)) { bt_shell_printf(" (broken)\n"); return; } bt_shell_printf("\n\t\tFrequencies: "); if (opus->data & OPUS_G_FREQUENCY_48000) bt_shell_printf("48kHz "); bt_shell_printf("\n\t\tChannel modes: "); if (opus->data & OPUS_G_CHANNELS_MONO) bt_shell_printf("Mono "); if (opus->data & OPUS_G_CHANNELS_STEREO) bt_shell_printf("Stereo "); if (opus->data & OPUS_G_CHANNELS_DUAL) bt_shell_printf("Dual Mono "); bt_shell_printf("\n\t\tFrame durations: "); if (opus->data & OPUS_G_DURATION_100) bt_shell_printf("10 ms "); if (opus->data & OPUS_G_DURATION_200) bt_shell_printf("20 ms "); bt_shell_printf("\n"); } static void print_vendor(a2dp_vendor_codec_t *vendor, uint8_t size) { uint32_t vendor_id; uint16_t codec_id; int i; if (size < sizeof(*vendor)) { bt_shell_printf("\tMedia Codec: Vendor Specific A2DP Codec " "(broken)"); return; } vendor_id = A2DP_GET_VENDOR_ID(*vendor); codec_id = A2DP_GET_CODEC_ID(*vendor); bt_shell_printf("\tMedia Codec: Vendor Specific A2DP Codec"); bt_shell_printf("\n\tVendor ID 0x%08x", vendor_id); bt_shell_printf("\n\tVendor Specific Codec ID 0x%04x", codec_id); bt_shell_printf("\n\tVendor Specific Data:"); for (i = 6; i < size; ++i) bt_shell_printf(" 0x%.02x", ((unsigned char *)vendor)[i]); bt_shell_printf("\n"); if (vendor_id == APTX_VENDOR_ID && codec_id == APTX_CODEC_ID) print_aptx((void *) vendor, size); else if (vendor_id == FASTSTREAM_VENDOR_ID && codec_id == FASTSTREAM_CODEC_ID) print_faststream((void *) vendor, size); else if (vendor_id == APTX_LL_VENDOR_ID && codec_id == APTX_LL_CODEC_ID) print_aptx_ll((void *) vendor, size); else if (vendor_id == APTX_HD_VENDOR_ID && codec_id == APTX_HD_CODEC_ID) print_aptx_hd((void *) vendor, size); else if (vendor_id == LDAC_VENDOR_ID && codec_id == LDAC_CODEC_ID) print_ldac((void *) vendor, size); else if (vendor_id == OPUS_G_VENDOR_ID && codec_id == OPUS_G_CODEC_ID) print_opus_g((void *) vendor, size); } static void print_mpeg24(a2dp_aac_t *aac, uint8_t size) { unsigned int freq, bitrate; if (size < sizeof(*aac)) { bt_shell_printf("\tMedia Codec: MPEG24 (broken)\n"); return; } freq = AAC_GET_FREQUENCY(*aac); bitrate = AAC_GET_BITRATE(*aac); bt_shell_printf("\tMedia Codec: MPEG24\n\tObject Types: "); if (aac->object_type & AAC_OBJECT_TYPE_MPEG2_AAC_LC) bt_shell_printf("MPEG-2 AAC LC "); if (aac->object_type & AAC_OBJECT_TYPE_MPEG4_AAC_LC) bt_shell_printf("MPEG-4 AAC LC "); if (aac->object_type & AAC_OBJECT_TYPE_MPEG4_AAC_LTP) bt_shell_printf("MPEG-4 AAC LTP "); if (aac->object_type & AAC_OBJECT_TYPE_MPEG4_AAC_SCA) bt_shell_printf("MPEG-4 AAC scalable "); bt_shell_printf("\n\tFrequencies: "); if (freq & AAC_SAMPLING_FREQ_8000) bt_shell_printf("8kHz "); if (freq & AAC_SAMPLING_FREQ_11025) bt_shell_printf("11.025kHz "); if (freq & AAC_SAMPLING_FREQ_12000) bt_shell_printf("12kHz "); if (freq & AAC_SAMPLING_FREQ_16000) bt_shell_printf("16kHz "); if (freq & AAC_SAMPLING_FREQ_22050) bt_shell_printf("22.05kHz "); if (freq & AAC_SAMPLING_FREQ_24000) bt_shell_printf("24kHz "); if (freq & AAC_SAMPLING_FREQ_32000) bt_shell_printf("32kHz "); if (freq & AAC_SAMPLING_FREQ_44100) bt_shell_printf("44.1kHz "); if (freq & AAC_SAMPLING_FREQ_48000) bt_shell_printf("48kHz "); if (freq & AAC_SAMPLING_FREQ_64000) bt_shell_printf("64kHz "); if (freq & AAC_SAMPLING_FREQ_88200) bt_shell_printf("88.2kHz "); if (freq & AAC_SAMPLING_FREQ_96000) bt_shell_printf("96kHz "); bt_shell_printf("\n\tChannels: "); if (aac->channels & AAC_CHANNELS_1) bt_shell_printf("1 "); if (aac->channels & AAC_CHANNELS_2) bt_shell_printf("2 "); bt_shell_printf("\n\tBitrate: %u", bitrate); bt_shell_printf("\n\tVBR: %s", aac->vbr ? "Yes\n" : "No\n"); } static void print_mpeg12(a2dp_mpeg_t *mpeg, uint8_t size) { uint16_t bitrate; if (size < sizeof(*mpeg)) { bt_shell_printf("\tMedia Codec: MPEG12 (broken)\n"); return; } bitrate = MPEG_GET_BITRATE(*mpeg); bt_shell_printf("\tMedia Codec: MPEG12\n\tChannel Modes: "); if (mpeg->channel_mode & MPEG_CHANNEL_MODE_MONO) bt_shell_printf("Mono "); if (mpeg->channel_mode & MPEG_CHANNEL_MODE_DUAL_CHANNEL) bt_shell_printf("DualChannel "); if (mpeg->channel_mode & MPEG_CHANNEL_MODE_STEREO) bt_shell_printf("Stereo "); if (mpeg->channel_mode & MPEG_CHANNEL_MODE_JOINT_STEREO) bt_shell_printf("JointStereo"); bt_shell_printf("\n\tFrequencies: "); if (mpeg->frequency & MPEG_SAMPLING_FREQ_16000) bt_shell_printf("16Khz "); if (mpeg->frequency & MPEG_SAMPLING_FREQ_22050) bt_shell_printf("22.05Khz "); if (mpeg->frequency & MPEG_SAMPLING_FREQ_24000) bt_shell_printf("24Khz "); if (mpeg->frequency & MPEG_SAMPLING_FREQ_32000) bt_shell_printf("32Khz "); if (mpeg->frequency & MPEG_SAMPLING_FREQ_44100) bt_shell_printf("44.1Khz "); if (mpeg->frequency & MPEG_SAMPLING_FREQ_48000) bt_shell_printf("48Khz "); bt_shell_printf("\n\tCRC: %s", mpeg->crc ? "Yes" : "No"); bt_shell_printf("\n\tLayer: "); if (mpeg->layer & MPEG_LAYER_MP1) bt_shell_printf("1 "); if (mpeg->layer & MPEG_LAYER_MP2) bt_shell_printf("2 "); if (mpeg->layer & MPEG_LAYER_MP3) bt_shell_printf("3 "); if (bitrate & MPEG_BIT_RATE_FREE) { bt_shell_printf("\n\tBit Rate: Free format"); } else { if (mpeg->layer & MPEG_LAYER_MP1) { bt_shell_printf("\n\tLayer 1 Bit Rate: "); if (bitrate & MPEG_MP1_BIT_RATE_32000) bt_shell_printf("32kbps "); if (bitrate & MPEG_MP1_BIT_RATE_64000) bt_shell_printf("64kbps "); if (bitrate & MPEG_MP1_BIT_RATE_96000) bt_shell_printf("96kbps "); if (bitrate & MPEG_MP1_BIT_RATE_128000) bt_shell_printf("128kbps "); if (bitrate & MPEG_MP1_BIT_RATE_160000) bt_shell_printf("160kbps "); if (bitrate & MPEG_MP1_BIT_RATE_192000) bt_shell_printf("192kbps "); if (bitrate & MPEG_MP1_BIT_RATE_224000) bt_shell_printf("224kbps "); if (bitrate & MPEG_MP1_BIT_RATE_256000) bt_shell_printf("256kbps "); if (bitrate & MPEG_MP1_BIT_RATE_320000) bt_shell_printf("320kbps "); if (bitrate & MPEG_MP1_BIT_RATE_352000) bt_shell_printf("352kbps "); if (bitrate & MPEG_MP1_BIT_RATE_384000) bt_shell_printf("384kbps "); if (bitrate & MPEG_MP1_BIT_RATE_416000) bt_shell_printf("416kbps "); if (bitrate & MPEG_MP1_BIT_RATE_448000) bt_shell_printf("448kbps "); } if (mpeg->layer & MPEG_LAYER_MP2) { bt_shell_printf("\n\tLayer 2 Bit Rate: "); if (bitrate & MPEG_MP2_BIT_RATE_32000) bt_shell_printf("32kbps "); if (bitrate & MPEG_MP2_BIT_RATE_48000) bt_shell_printf("48kbps "); if (bitrate & MPEG_MP2_BIT_RATE_56000) bt_shell_printf("56kbps "); if (bitrate & MPEG_MP2_BIT_RATE_64000) bt_shell_printf("64kbps "); if (bitrate & MPEG_MP2_BIT_RATE_80000) bt_shell_printf("80kbps "); if (bitrate & MPEG_MP2_BIT_RATE_96000) bt_shell_printf("96kbps "); if (bitrate & MPEG_MP2_BIT_RATE_112000) bt_shell_printf("112kbps "); if (bitrate & MPEG_MP2_BIT_RATE_128000) bt_shell_printf("128kbps "); if (bitrate & MPEG_MP2_BIT_RATE_160000) bt_shell_printf("160kbps "); if (bitrate & MPEG_MP2_BIT_RATE_192000) bt_shell_printf("192kbps "); if (bitrate & MPEG_MP2_BIT_RATE_224000) bt_shell_printf("224kbps "); if (bitrate & MPEG_MP2_BIT_RATE_256000) bt_shell_printf("256kbps "); if (bitrate & MPEG_MP2_BIT_RATE_320000) bt_shell_printf("320kbps "); if (bitrate & MPEG_MP2_BIT_RATE_384000) bt_shell_printf("384kbps "); } if (mpeg->layer & MPEG_LAYER_MP3) { bt_shell_printf("\n\tLayer 3 Bit Rate: "); if (bitrate & MPEG_MP3_BIT_RATE_32000) bt_shell_printf("32kbps "); if (bitrate & MPEG_MP3_BIT_RATE_40000) bt_shell_printf("40kbps "); if (bitrate & MPEG_MP3_BIT_RATE_48000) bt_shell_printf("48kbps "); if (bitrate & MPEG_MP3_BIT_RATE_56000) bt_shell_printf("56kbps "); if (bitrate & MPEG_MP3_BIT_RATE_64000) bt_shell_printf("64kbps "); if (bitrate & MPEG_MP3_BIT_RATE_80000) bt_shell_printf("80kbps "); if (bitrate & MPEG_MP3_BIT_RATE_96000) bt_shell_printf("96kbps "); if (bitrate & MPEG_MP3_BIT_RATE_112000) bt_shell_printf("112kbps "); if (bitrate & MPEG_MP3_BIT_RATE_128000) bt_shell_printf("128kbps "); if (bitrate & MPEG_MP3_BIT_RATE_160000) bt_shell_printf("160kbps "); if (bitrate & MPEG_MP3_BIT_RATE_192000) bt_shell_printf("192kbps "); if (bitrate & MPEG_MP3_BIT_RATE_224000) bt_shell_printf("224kbps "); if (bitrate & MPEG_MP3_BIT_RATE_256000) bt_shell_printf("256kbps "); if (bitrate & MPEG_MP3_BIT_RATE_320000) bt_shell_printf("320kbps "); } } bt_shell_printf("\n\tVBR: %s", mpeg->vbr ? "Yes" : "No"); bt_shell_printf("\n\tPayload Format: "); if (mpeg->mpf) bt_shell_printf("RFC-2250 RFC-3119\n"); else bt_shell_printf("RFC-2250\n"); } static void print_sbc(a2dp_sbc_t *sbc, uint8_t size) { if (size < sizeof(*sbc)) { bt_shell_printf("\tMedia Codec: SBC (broken)\n"); return; } bt_shell_printf("\tMedia Codec: SBC\n\tChannel Modes: "); if (sbc->channel_mode & SBC_CHANNEL_MODE_MONO) bt_shell_printf("Mono "); if (sbc->channel_mode & SBC_CHANNEL_MODE_DUAL_CHANNEL) bt_shell_printf("DualChannel "); if (sbc->channel_mode & SBC_CHANNEL_MODE_STEREO) bt_shell_printf("Stereo "); if (sbc->channel_mode & SBC_CHANNEL_MODE_JOINT_STEREO) bt_shell_printf("JointStereo"); bt_shell_printf("\n\tFrequencies: "); if (sbc->frequency & SBC_SAMPLING_FREQ_16000) bt_shell_printf("16Khz "); if (sbc->frequency & SBC_SAMPLING_FREQ_32000) bt_shell_printf("32Khz "); if (sbc->frequency & SBC_SAMPLING_FREQ_44100) bt_shell_printf("44.1Khz "); if (sbc->frequency & SBC_SAMPLING_FREQ_48000) bt_shell_printf("48Khz "); bt_shell_printf("\n\tSubbands: "); if (sbc->allocation_method & SBC_SUBBANDS_4) bt_shell_printf("4 "); if (sbc->allocation_method & SBC_SUBBANDS_8) bt_shell_printf("8"); bt_shell_printf("\n\tBlocks: "); if (sbc->block_length & SBC_BLOCK_LENGTH_4) bt_shell_printf("4 "); if (sbc->block_length & SBC_BLOCK_LENGTH_8) bt_shell_printf("8 "); if (sbc->block_length & SBC_BLOCK_LENGTH_12) bt_shell_printf("12 "); if (sbc->block_length & SBC_BLOCK_LENGTH_16) bt_shell_printf("16 "); bt_shell_printf("\n\tBitpool Range: %d-%d\n", sbc->min_bitpool, sbc->max_bitpool); } static int print_a2dp_codec(uint8_t codec, void *data, uint8_t size) { int i; switch (codec) { case A2DP_CODEC_SBC: print_sbc(data, size); break; case A2DP_CODEC_MPEG12: print_mpeg12(data, size); break; case A2DP_CODEC_MPEG24: print_mpeg24(data, size); break; case A2DP_CODEC_VENDOR: print_vendor(data, size); break; default: bt_shell_printf("\tMedia Codec: Unknown\n"); bt_shell_printf("\t\tCodec Data:"); for (i = 0; i < size - 2; ++i) bt_shell_printf(" 0x%.02x", ((unsigned char *)data)[i]); bt_shell_printf("\n"); } return 0; } static void print_hexdump(const char *label, struct iovec *iov) { if (!iov) return; bt_shell_printf("%s:\n", label); bt_shell_hexdump(iov->iov_base, iov->iov_len); } static void print_codec(const char *uuid, uint8_t codec, struct iovec *caps, struct iovec *meta) { if (!strcasecmp(uuid, A2DP_SINK_UUID) || !strcasecmp(uuid, A2DP_SOURCE_UUID)) { print_a2dp_codec(codec, caps->iov_base, caps->iov_len); return; } if (codec != LC3_ID) { print_hexdump("Capabilities", caps); print_hexdump("Metadata", meta); return; } print_lc3_caps(caps->iov_base, caps->iov_len); if (!meta) return; print_lc3_meta(meta->iov_base, meta->iov_len); } static void print_capabilities(GDBusProxy *proxy) { DBusMessageIter iter, subiter; const char *uuid; uint8_t codec; struct iovec caps, meta; if (!g_dbus_proxy_get_property(proxy, "UUID", &iter)) return; dbus_message_iter_get_basic(&iter, &uuid); if (!g_dbus_proxy_get_property(proxy, "Codec", &iter)) return; dbus_message_iter_get_basic(&iter, &codec); if (!g_dbus_proxy_get_property(proxy, "Capabilities", &iter)) return; dbus_message_iter_recurse(&iter, &subiter); dbus_message_iter_get_fixed_array(&subiter, &caps.iov_base, (int *)&caps.iov_len); if (g_dbus_proxy_get_property(proxy, "Metadata", &iter)) { dbus_message_iter_recurse(&iter, &subiter); dbus_message_iter_get_fixed_array(&subiter, &meta.iov_base, (int *)&meta.iov_len); } else { meta.iov_base = NULL; meta.iov_len = 0; } print_codec(uuid, codec, &caps, &meta); } static void print_preset(struct codec_preset *codec, uint8_t codec_id) { bt_shell_printf("\tPreset %s\n", codec->name); if (codec_id == LC3_ID) print_lc3_cfg(codec->data.iov_base, codec->data.iov_len); } static void print_local_endpoint(struct endpoint *ep) { bt_shell_printf("Endpoint %s\n", ep->path); bt_shell_printf("\tUUID %s\n", ep->uuid); bt_shell_printf("\tCodec 0x%02x (%u)\n", ep->codec, ep->codec); if (ep->caps) print_codec(ep->uuid, ep->codec, ep->caps, ep->meta); if (ep->codec_preset) print_preset(ep->codec_preset, ep->codec); if (ep->locations) bt_shell_printf("\tLocations 0x%08x (%u)\n", ep->locations, ep->locations); if (ep->supported_context) bt_shell_printf("\tSupportedContext 0x%08x (%u)\n", ep->supported_context, ep->supported_context); if (ep->context) bt_shell_printf("\tContext 0x%08x (%u)\n", ep->context, ep->context); } static void print_endpoint_properties(GDBusProxy *proxy) { bt_shell_printf("Endpoint %s\n", g_dbus_proxy_get_path(proxy)); print_property(proxy, "UUID"); print_property(proxy, "Codec"); print_capabilities(proxy); print_property(proxy, "Device"); print_property(proxy, "DelayReporting"); print_property(proxy, "Locations"); print_property(proxy, "SupportedContext"); print_property(proxy, "Context"); print_property(proxy, "QoS"); } static void print_endpoints(void *data, void *user_data) { print_endpoint_properties(data); } static void print_local_endpoints(void *data, void *user_data) { print_local_endpoint(data); } static void cmd_show_endpoint(int argc, char *argv[]) { GDBusProxy *proxy; /* Show all endpoints if no argument is given */ if (argc != 2) { g_list_foreach(endpoints, print_endpoints, NULL); g_list_foreach(local_endpoints, print_local_endpoints, NULL); return bt_shell_noninteractive_quit(EXIT_SUCCESS); } proxy = g_dbus_proxy_lookup(endpoints, NULL, argv[1], BLUEZ_MEDIA_ENDPOINT_INTERFACE); if (!proxy) { struct endpoint *ep; ep = endpoint_find(argv[1]); if (ep) return print_local_endpoint(ep); bt_shell_printf("Endpoint %s not found\n", argv[1]); return bt_shell_noninteractive_quit(EXIT_SUCCESS); } print_endpoint_properties(proxy); return bt_shell_noninteractive_quit(EXIT_SUCCESS); } static const GDBusMethodTable endpoint_methods[] = { { GDBUS_ASYNC_METHOD("SetConfiguration", GDBUS_ARGS({ "endpoint", "o" }, { "properties", "a{sv}" } ), NULL, endpoint_set_configuration) }, { GDBUS_ASYNC_METHOD("SelectConfiguration", GDBUS_ARGS({ "caps", "ay" } ), GDBUS_ARGS({ "cfg", "ay" } ), endpoint_select_configuration) }, { GDBUS_ASYNC_METHOD("SelectProperties", GDBUS_ARGS({ "properties", "a{sv}" } ), GDBUS_ARGS({ "properties", "a{sv}" } ), endpoint_select_properties) }, { GDBUS_ASYNC_METHOD("ClearConfiguration", GDBUS_ARGS({ "transport", "o" } ), NULL, endpoint_clear_configuration) }, { }, }; static void endpoint_free(void *data) { struct endpoint *ep = data; util_iov_free(ep->caps, 1); util_iov_free(ep->meta, 1); if (ep->msg) dbus_message_unref(ep->msg); queue_destroy(ep->preset->custom, free); ep->preset->custom = NULL; if (ep->codec == 0xff) free(ep->preset); queue_destroy(ep->acquiring, NULL); queue_destroy(ep->transports, free); g_free(ep->path); g_free(ep->uuid); g_free(ep); } static gboolean endpoint_get_uuid(const GDBusPropertyTable *property, DBusMessageIter *iter, void *data) { struct endpoint *ep = data; dbus_message_iter_append_basic(iter, DBUS_TYPE_STRING, &ep->uuid); return TRUE; } static gboolean endpoint_get_codec(const GDBusPropertyTable *property, DBusMessageIter *iter, void *data) { struct endpoint *ep = data; dbus_message_iter_append_basic(iter, DBUS_TYPE_BYTE, &ep->codec); return TRUE; } static gboolean endpoint_get_capabilities(const GDBusPropertyTable *property, DBusMessageIter *iter, void *data) { struct endpoint *ep = data; DBusMessageIter array; dbus_message_iter_open_container(iter, DBUS_TYPE_ARRAY, DBUS_TYPE_BYTE_AS_STRING, &array); dbus_message_iter_append_fixed_array(&array, DBUS_TYPE_BYTE, &ep->caps->iov_base, ep->caps->iov_len); dbus_message_iter_close_container(iter, &array); return TRUE; } struct vendor { uint16_t cid; uint16_t vid; } __packed; static gboolean endpoint_get_vendor(const GDBusPropertyTable *property, DBusMessageIter *iter, void *data) { struct endpoint *ep = data; struct vendor vendor = { ep->cid, ep->vid }; dbus_message_iter_append_basic(iter, DBUS_TYPE_UINT32, &vendor); return TRUE; } static gboolean endpoint_vendor_exists(const GDBusPropertyTable *property, void *data) { struct endpoint *ep = data; return ep->cid && ep->vid; } static gboolean endpoint_get_metadata(const GDBusPropertyTable *property, DBusMessageIter *iter, void *data) { struct endpoint *ep = data; DBusMessageIter array; dbus_message_iter_open_container(iter, DBUS_TYPE_ARRAY, DBUS_TYPE_BYTE_AS_STRING, &array); dbus_message_iter_append_fixed_array(&array, DBUS_TYPE_BYTE, &ep->meta->iov_base, ep->meta->iov_len); dbus_message_iter_close_container(iter, &array); return TRUE; } static gboolean endpoint_metadata_exists(const GDBusPropertyTable *property, void *data) { struct endpoint *ep = data; return ep->meta ? TRUE : FALSE; } static gboolean endpoint_get_locations(const GDBusPropertyTable *property, DBusMessageIter *iter, void *data) { struct endpoint *ep = data; dbus_message_iter_append_basic(iter, DBUS_TYPE_UINT32, &ep->locations); return TRUE; } static gboolean endpoint_locations_exists(const GDBusPropertyTable *property, void *data) { struct endpoint *ep = data; return ep->supported_context ? TRUE : FALSE; } static gboolean endpoint_get_supported_context(const GDBusPropertyTable *property, DBusMessageIter *iter, void *data) { struct endpoint *ep = data; dbus_message_iter_append_basic(iter, DBUS_TYPE_UINT16, &ep->supported_context); return TRUE; } static gboolean endpoint_supported_context_exists(const GDBusPropertyTable *property, void *data) { struct endpoint *ep = data; return ep->supported_context ? TRUE : FALSE; } static gboolean endpoint_get_context(const GDBusPropertyTable *property, DBusMessageIter *iter, void *data) { struct endpoint *ep = data; dbus_message_iter_append_basic(iter, DBUS_TYPE_UINT16, &ep->context); return TRUE; } static gboolean endpoint_context_exists(const GDBusPropertyTable *property, void *data) { struct endpoint *ep = data; return ep->context ? TRUE : FALSE; } static const GDBusPropertyTable endpoint_properties[] = { { "UUID", "s", endpoint_get_uuid, NULL, NULL }, { "Codec", "y", endpoint_get_codec, NULL, NULL }, { "Capabilities", "ay", endpoint_get_capabilities, NULL, NULL }, { "Metadata", "ay", endpoint_get_metadata, NULL, endpoint_metadata_exists }, { "Vendor", "u", endpoint_get_vendor, NULL, endpoint_vendor_exists }, { "Locations", "u", endpoint_get_locations, NULL, endpoint_locations_exists }, { "SupportedContext", "q", endpoint_get_supported_context, NULL, endpoint_supported_context_exists }, { "Context", "q", endpoint_get_context, NULL, endpoint_context_exists }, { } }; static void register_endpoint_setup(DBusMessageIter *iter, void *user_data) { struct endpoint *ep = user_data; DBusMessageIter dict; const char *key = "Capabilities"; const char *meta = "Metadata"; dbus_message_iter_append_basic(iter, DBUS_TYPE_OBJECT_PATH, &ep->path); dbus_message_iter_open_container(iter, DBUS_TYPE_ARRAY, "{sv}", &dict); g_dbus_dict_append_entry(&dict, "UUID", DBUS_TYPE_STRING, &ep->uuid); g_dbus_dict_append_entry(&dict, "Codec", DBUS_TYPE_BYTE, &ep->codec); if (ep->cid && ep->vid) { struct vendor vendor = { ep->cid, ep->vid }; g_dbus_dict_append_entry(&dict, "Vendor", DBUS_TYPE_UINT32, &vendor); } if (ep->caps) { g_dbus_dict_append_basic_array(&dict, DBUS_TYPE_STRING, &key, DBUS_TYPE_BYTE, &ep->caps->iov_base, ep->caps->iov_len); bt_shell_printf("Capabilities:\n"); bt_shell_hexdump(ep->caps->iov_base, ep->caps->iov_len); } if (ep->meta) { g_dbus_dict_append_basic_array(&dict, DBUS_TYPE_STRING, &meta, DBUS_TYPE_BYTE, &ep->meta->iov_base, ep->meta->iov_len); bt_shell_printf("Metadata:\n"); bt_shell_hexdump(ep->meta->iov_base, ep->meta->iov_len); } if (ep->locations) g_dbus_dict_append_entry(&dict, "Locations", DBUS_TYPE_UINT32, &ep->locations); if (ep->supported_context) g_dbus_dict_append_entry(&dict, "SupportedContext", DBUS_TYPE_UINT16, &ep->supported_context); if (ep->context) g_dbus_dict_append_entry(&dict, "Context", DBUS_TYPE_UINT16, &ep->context); dbus_message_iter_close_container(iter, &dict); } static void register_endpoint_reply(DBusMessage *message, void *user_data) { struct endpoint *ep = user_data; DBusError error; dbus_error_init(&error); if (dbus_set_error_from_message(&error, message)) { bt_shell_printf("Failed to register endpoint: %s\n", error.name); dbus_error_free(&error); if (g_list_find(local_endpoints, ep)) { local_endpoints = g_list_remove(local_endpoints, ep); g_dbus_unregister_interface(dbus_conn, ep->path, BLUEZ_MEDIA_ENDPOINT_INTERFACE); } return bt_shell_noninteractive_quit(EXIT_FAILURE); } bt_shell_printf("Endpoint %s registered\n", ep->path); return bt_shell_noninteractive_quit(EXIT_SUCCESS); } static bool media_supports_uuid(GDBusProxy *proxy, const char *uuid) { DBusMessageIter iter, array; if (!g_dbus_proxy_get_property(proxy, "SupportedUUIDs", &iter)) return false; dbus_message_iter_recurse(&iter, &array); while (dbus_message_iter_get_arg_type(&array) == DBUS_TYPE_STRING) { const char *support_uuid; dbus_message_iter_get_basic(&array, &support_uuid); if (!strcasecmp(uuid, support_uuid)) return true; dbus_message_iter_next(&array); } return false; } static void endpoint_register(struct endpoint *ep) { GList *l; int registered = 0; if (!g_dbus_register_interface(dbus_conn, ep->path, BLUEZ_MEDIA_ENDPOINT_INTERFACE, endpoint_methods, NULL, endpoint_properties, ep, endpoint_free)) { goto fail; } for (l = medias; l; l = g_list_next(l)) { if (!media_supports_uuid(l->data, ep->uuid)) continue; if (!g_dbus_proxy_method_call(l->data, "RegisterEndpoint", register_endpoint_setup, register_endpoint_reply, ep, NULL)) { g_dbus_unregister_interface(dbus_conn, ep->path, BLUEZ_MEDIA_ENDPOINT_INTERFACE); goto fail; } registered++; } if (!registered) goto fail; return; fail: bt_shell_printf("Failed register endpoint\n"); local_endpoints = g_list_remove(local_endpoints, ep); return bt_shell_noninteractive_quit(EXIT_FAILURE); } static void endpoint_iso_stream(const char *input, void *user_data) { struct endpoint *ep = user_data; char *endptr = NULL; int value; if (!strcasecmp(input, "a") || !strcasecmp(input, "auto")) { ep->iso_stream = BT_ISO_QOS_STREAM_UNSET; } else { value = strtol(input, &endptr, 0); if (!endptr || *endptr != '\0' || value > UINT8_MAX) { bt_shell_printf("Invalid argument: %s\n", input); return bt_shell_noninteractive_quit(EXIT_FAILURE); } ep->iso_stream = value; } endpoint_register(ep); } static void endpoint_iso_group(const char *input, void *user_data) { struct endpoint *ep = user_data; char *endptr = NULL; int value; if (!strcasecmp(input, "a") || !strcasecmp(input, "auto")) { ep->iso_group = BT_ISO_QOS_GROUP_UNSET; } else { value = strtol(input, &endptr, 0); if (!endptr || *endptr != '\0' || value > UINT8_MAX) { bt_shell_printf("Invalid argument: %s\n", input); return bt_shell_noninteractive_quit(EXIT_FAILURE); } ep->iso_group = value; } bt_shell_prompt_input(ep->path, "CIS (auto/value):", endpoint_iso_stream, ep); } static void endpoint_context(const char *input, void *user_data) { struct endpoint *ep = user_data; char *endptr = NULL; int value; value = strtol(input, &endptr, 0); if (!endptr || *endptr != '\0' || value > UINT16_MAX) { bt_shell_printf("Invalid argument: %s\n", input); return bt_shell_noninteractive_quit(EXIT_FAILURE); } ep->context = value; bt_shell_prompt_input(ep->path, "CIG (auto/value):", endpoint_iso_group, ep); } static void endpoint_supported_context(const char *input, void *user_data) { struct endpoint *ep = user_data; char *endptr = NULL; int value; value = strtol(input, &endptr, 0); if (!endptr || *endptr != '\0' || value > UINT16_MAX) { bt_shell_printf("Invalid argument: %s\n", input); return bt_shell_noninteractive_quit(EXIT_FAILURE); } ep->supported_context = value; if (ep->broadcast) { endpoint_register(ep); return; } bt_shell_prompt_input(ep->path, "Context (value):", endpoint_context, ep); } static void endpoint_locations(const char *input, void *user_data) { struct endpoint *ep = user_data; char *endptr = NULL; int value; uint8_t channels; value = strtol(input, &endptr, 0); if (!endptr || *endptr != '\0') { bt_shell_printf("Invalid argument: %s\n", input); return bt_shell_noninteractive_quit(EXIT_FAILURE); } ep->locations = value; channels = __builtin_popcount(value); /* Automatically set LC3_CHAN_COUNT if only 1 location is supported */ if (channels == 1) util_ltv_push(ep->caps, sizeof(channels), LC3_CHAN_COUNT, &channels); bt_shell_prompt_input(ep->path, "Supported Context (value):", endpoint_supported_context, ep); } static void endpoint_max_transports(const char *input, void *user_data) { struct endpoint *ep = user_data; char *endptr = NULL; int value; if (!strcasecmp(input, "a") || !strcasecmp(input, "auto")) { ep->max_transports = UINT8_MAX; } else { value = strtol(input, &endptr, 0); if (!endptr || *endptr != '\0' || value > UINT8_MAX) { bt_shell_printf("Invalid argument: %s\n", input); return bt_shell_noninteractive_quit(EXIT_FAILURE); } ep->max_transports = value; } bt_shell_prompt_input(ep->path, "Locations:", endpoint_locations, ep); } static void endpoint_auto_accept(const char *input, void *user_data) { struct endpoint *ep = user_data; if (!strcasecmp(input, "y") || !strcasecmp(input, "yes")) { ep->auto_accept = true; bt_shell_prompt_input(ep->path, "Max Transports (auto/value):", endpoint_max_transports, ep); return; } else if (!strcasecmp(input, "n") || !strcasecmp(input, "no")) { ep->auto_accept = false; bt_shell_prompt_input(ep->path, "Max Transports (auto/value):", endpoint_max_transports, ep); return; } else { bt_shell_printf("Invalid input for Auto Accept\n"); return bt_shell_noninteractive_quit(EXIT_FAILURE); } } static void endpoint_set_metadata(const char *input, void *user_data) { struct endpoint *ep = user_data; struct iovec iov; if (!strcasecmp(input, "n") || !strcasecmp(input, "no")) { util_iov_free(ep->meta, 1); ep->meta = NULL; goto done; } iov.iov_base = str2bytearray((char *) input, &iov.iov_len); if (iov.iov_base) { util_iov_free(ep->meta, 1); ep->meta = util_iov_dup(&iov, 1); } done: bt_shell_prompt_input(ep->path, "Auto Accept (yes/no):", endpoint_auto_accept, ep); } static void endpoint_set_capabilities(const char *input, void *user_data) { struct endpoint *ep = user_data; struct iovec iov; if (!strcasecmp(input, "n") || !strcasecmp(input, "no")) { util_iov_free(ep->caps, 1); ep->caps = NULL; goto done; } iov.iov_base = str2bytearray((char *) input, &iov.iov_len); if (iov.iov_base) { util_iov_free(ep->caps, 1); ep->caps = util_iov_dup(&iov, 1); } done: bt_shell_prompt_input(ep->path, "Enter Metadata (value/no):", endpoint_set_metadata, ep); } static char *uuid_generator(const char *text, int state) { int len = strlen(text); static int index = 0; size_t i; if (!state) { index = 0; } for (i = index; i < ARRAY_SIZE(caps); i++) { const struct capabilities *cap = &caps[i]; index++; if (!strncasecmp(cap->uuid, text, len)) return strdup(cap->uuid); } return NULL; } static const struct capabilities *find_capabilities(const char *uuid, uint8_t codec_id) { size_t i; for (i = 0; i < ARRAY_SIZE(caps); i++) { const struct capabilities *cap = &caps[i]; if (strcasecmp(cap->uuid, uuid)) continue; if (cap->codec_id == codec_id) return cap; } return NULL; } static struct codec_preset *codec_preset_new(const char *name) { struct codec_preset *codec; codec = new0(struct codec_preset, 1); codec->name = strdup(name); codec->custom = true; return codec; } static struct codec_preset *codec_preset_add(struct preset *preset, const char *name) { struct codec_preset *codec; codec = preset_find_name(preset, name); if (codec) return codec; codec = codec_preset_new(name); if (!preset->custom) preset->custom = queue_new(); queue_push_tail(preset->custom, codec); return codec; } static void cmd_register_endpoint(int argc, char *argv[]) { struct endpoint *ep; char *endptr = NULL; ep = g_new0(struct endpoint, 1); ep->uuid = g_strdup(argv[1]); ep->codec = strtol(argv[2], &endptr, 0); ep->cid = 0x0000; ep->vid = 0x0000; ep->path = g_strdup_printf("%s/ep%u", BLUEZ_MEDIA_ENDPOINT_PATH, g_list_length(local_endpoints)); local_endpoints = g_list_append(local_endpoints, ep); if (!strcmp(ep->uuid, BCAA_SERVICE_UUID) || !strcmp(ep->uuid, BAA_SERVICE_UUID)) { ep->broadcast = true; } else { ep->broadcast = false; } if (strrchr(argv[2], ':')) { ep->codec = 0xff; parse_vendor_codec(argv[2], &ep->vid, &ep->cid); ep->preset = new0(struct preset, 1); ep->preset->default_preset = codec_preset_add(ep->preset, "custom"); } else { ep->preset = find_presets_name(ep->uuid, argv[2]); } if (argc > 3) endpoint_set_capabilities(argv[3], ep); else { const struct capabilities *cap; cap = find_capabilities(ep->uuid, ep->codec); if (cap) { /* Copy capabilities */ util_iov_free(ep->caps, 1); ep->caps = util_iov_dup(&cap->data, 1); /* Copy metadata */ util_iov_free(ep->meta, 1); ep->meta = util_iov_dup(&cap->meta, 1); bt_shell_prompt_input(ep->path, "Auto Accept (yes/no):", endpoint_auto_accept, ep); } else bt_shell_prompt_input(ep->path, "Enter capabilities:", endpoint_set_capabilities, ep); } } static void unregister_endpoint_setup(DBusMessageIter *iter, void *user_data) { struct endpoint *ep = user_data; dbus_message_iter_append_basic(iter, DBUS_TYPE_OBJECT_PATH, &ep->path); } static void unregister_endpoint_reply(DBusMessage *message, void *user_data) { struct endpoint *ep = user_data; DBusError error; dbus_error_init(&error); if (dbus_set_error_from_message(&error, message)) { bt_shell_printf("Failed to unregister endpoint: %s\n", error.name); dbus_error_free(&error); return bt_shell_noninteractive_quit(EXIT_FAILURE); } bt_shell_printf("Endpoint %s unregistered\n", ep->path); local_endpoints = g_list_remove(local_endpoints, ep); g_dbus_unregister_interface(dbus_conn, ep->path, BLUEZ_MEDIA_ENDPOINT_INTERFACE); return bt_shell_noninteractive_quit(EXIT_SUCCESS); } static void cmd_unregister_endpoint(int argc, char *argv[]) { struct endpoint *ep; GList *l; ep = endpoint_find(argv[1]); if (!ep) { bt_shell_printf("Unable to find endpoint object: %s\n", argv[1]); return bt_shell_noninteractive_quit(EXIT_FAILURE); } for (l = medias; l; l = g_list_next(l)) { if (!g_dbus_proxy_method_call(l->data, "UnregisterEndpoint", unregister_endpoint_setup, unregister_endpoint_reply, ep, NULL)) { bt_shell_printf("Failed unregister endpoint\n"); return bt_shell_noninteractive_quit(EXIT_FAILURE); } } return bt_shell_noninteractive_quit(EXIT_SUCCESS); } static void config_endpoint_setup(DBusMessageIter *iter, void *user_data) { struct endpoint_config *cfg = user_data; dbus_message_iter_append_basic(iter, DBUS_TYPE_OBJECT_PATH, &cfg->ep->path); append_properties(iter, cfg); } static void config_endpoint_reply(DBusMessage *message, void *user_data) { struct endpoint_config *cfg = user_data; struct endpoint *ep = cfg->ep; DBusError error; free(cfg->caps->iov_base); free(cfg->caps); free(cfg); dbus_error_init(&error); if (dbus_set_error_from_message(&error, message)) { bt_shell_printf("Failed to config endpoint: %s\n", error.name); dbus_error_free(&error); return bt_shell_noninteractive_quit(EXIT_FAILURE); } bt_shell_printf("Endpoint %s configured\n", ep->path); return bt_shell_noninteractive_quit(EXIT_SUCCESS); } static void endpoint_set_config(struct endpoint_config *cfg) { if (!g_dbus_proxy_method_call(cfg->proxy, "SetConfiguration", config_endpoint_setup, config_endpoint_reply, cfg, NULL)) { bt_shell_printf("Failed to config endpoint\n"); return bt_shell_noninteractive_quit(EXIT_FAILURE); } } static void endpoint_config(const char *input, void *user_data) { struct endpoint_config *cfg = user_data; uint8_t *data; size_t len = 0; data = str2bytearray((char *) input, &len); util_iov_append(cfg->caps, data, len); free(data); endpoint_set_config(cfg); } static struct endpoint *endpoint_new(const struct capabilities *cap); static void endpoint_set_metadata_cfg(const char *input, void *user_data) { struct endpoint_config *cfg = user_data; if (!strcasecmp(input, "n") || !strcasecmp(input, "no")) goto done; if (!cfg->meta) cfg->meta = g_new0(struct iovec, 1); cfg->meta->iov_base = str2bytearray((char *) input, &cfg->meta->iov_len); if (!cfg->meta->iov_base) { free(cfg->meta); cfg->meta = NULL; } done: endpoint_set_config(cfg); } static void config_endpoint_channel_location(const char *input, void *user_data) { struct endpoint_config *cfg = user_data; char *endptr = NULL; uint32_t location; uint8_t channels = 1; if (!strcasecmp(input, "n") || !strcasecmp(input, "no")) goto add_meta; location = strtol(input, &endptr, 0); if (!endptr || *endptr != '\0') { bt_shell_printf("Invalid argument: %s\n", input); return bt_shell_noninteractive_quit(EXIT_FAILURE); } /* Add Channel Allocation LTV in capabilities */ location = cpu_to_le32(location); util_ltv_push(cfg->caps, LC3_CONFIG_CHAN_ALLOC_LEN - 1, LC3_CONFIG_CHAN_ALLOC, &location); /* Adjust the SDU size based on the number of * locations/channels that is being requested. */ channels = __builtin_popcount(location); if (channels > 1) cfg->qos.bcast.io_qos.sdu *= channels; add_meta: /* Add metadata */ bt_shell_prompt_input(cfg->ep->path, "Enter Metadata (value/no):", endpoint_set_metadata_cfg, cfg); } static void config_endpoint_sync_factor(const char *input, void *user_data) { struct endpoint_config *cfg = user_data; char *endptr = NULL; int value; uint8_t type = LC3_CONFIG_CHAN_ALLOC; bool found = false; if (!strcasecmp(input, "a") || !strcasecmp(input, "auto")) { cfg->qos.bcast.sync_factor = BT_ISO_SYNC_FACTOR; } else { value = strtol(input, &endptr, 0); if (!endptr || *endptr != '\0' || value > UINT8_MAX) { bt_shell_printf("Invalid argument: %s\n", input); return bt_shell_noninteractive_quit(EXIT_FAILURE); } cfg->qos.bcast.sync_factor = value; } /* Check if Channel Allocation is present in caps */ util_ltv_foreach(cfg->caps->iov_base, cfg->caps->iov_len, &type, ltv_find, &found); /* Add Channel Allocation if it is not present in caps */ if (!found) { bt_shell_prompt_input(cfg->ep->path, "Enter channel location (value/no):", config_endpoint_channel_location, cfg); } else { /* Add metadata */ bt_shell_prompt_input(cfg->ep->path, "Enter Metadata (value/no):", endpoint_set_metadata_cfg, cfg); } } static void config_endpoint_iso_stream(const char *input, void *user_data) { struct endpoint_config *cfg = user_data; char *endptr = NULL; int value; if (!strcasecmp(input, "a") || !strcasecmp(input, "auto")) { cfg->ep->iso_stream = BT_ISO_QOS_STREAM_UNSET; } else { value = strtol(input, &endptr, 0); if (!endptr || *endptr != '\0' || value > UINT8_MAX) { bt_shell_printf("Invalid argument: %s\n", input); return bt_shell_noninteractive_quit(EXIT_FAILURE); } cfg->ep->iso_stream = value; } bt_shell_prompt_input(cfg->ep->path, "Enter sync factor (value/auto):", config_endpoint_sync_factor, cfg); } static void config_endpoint_iso_group(const char *input, void *user_data) { struct endpoint_config *cfg = user_data; char *endptr = NULL; int value; if (!strcasecmp(input, "a") || !strcasecmp(input, "auto")) { cfg->ep->iso_group = BT_ISO_QOS_GROUP_UNSET; } else { value = strtol(input, &endptr, 0); if (!endptr || *endptr != '\0' || value > UINT8_MAX) { bt_shell_printf("Invalid argument: %s\n", input); return bt_shell_noninteractive_quit(EXIT_FAILURE); } cfg->ep->iso_group = value; } bt_shell_prompt_input(cfg->ep->path, "BIS (auto/value):", config_endpoint_iso_stream, cfg); } static void endpoint_set_config_bcast(struct endpoint_config *cfg) { cfg->ep->bcode = g_new0(struct iovec, 1); util_iov_append(cfg->ep->bcode, bcast_code, sizeof(bcast_code)); if ((strcmp(cfg->ep->uuid, BAA_SERVICE_UUID) == 0)) { /* A broadcast sink endpoint config does not need * user input. */ endpoint_set_config(cfg); return; } bt_shell_prompt_input(cfg->ep->path, "BIG (auto/value):", config_endpoint_iso_group, cfg); } static void cmd_config_endpoint(int argc, char *argv[]) { struct endpoint_config *cfg; const struct codec_preset *preset; cfg = new0(struct endpoint_config, 1); /* Search for the remote endpoint name on DBUS */ cfg->proxy = g_dbus_proxy_lookup(endpoints, NULL, argv[1], BLUEZ_MEDIA_ENDPOINT_INTERFACE); if (!cfg->proxy) { bt_shell_printf("Endpoint %s not found\n", argv[1]); goto fail; } /* Search for the local endpoint */ cfg->ep = endpoint_find(argv[2]); if (!cfg->ep) { bt_shell_printf("Local Endpoint %s not found\n", argv[2]); goto fail; } if (argc > 3) { preset = preset_find_name(cfg->ep->preset, argv[3]); if (!preset) { bt_shell_printf("Preset %s not found\n", argv[3]); goto fail; } cfg->caps = g_new0(struct iovec, 1); /* Copy capabilities */ util_iov_append(cfg->caps, preset->data.iov_base, preset->data.iov_len); cfg->target_latency = preset->target_latency; /* Set QoS parameters */ cfg->qos = preset->qos; if (cfg->ep->broadcast) endpoint_set_config_bcast(cfg); else endpoint_set_config(cfg); return; } bt_shell_prompt_input(cfg->ep->path, "Enter configuration:", endpoint_config, cfg); return; fail: g_free(cfg); return bt_shell_noninteractive_quit(EXIT_FAILURE); } static void custom_delay(const char *input, void *user_data) { struct codec_preset *p = user_data; struct bt_bap_qos *qos = (void *)&p->qos; char *endptr = NULL; if (!p->target_latency) qos->bcast.delay = strtol(input, &endptr, 0); else qos->ucast.delay = strtol(input, &endptr, 0); if (!endptr || *endptr != '\0') { bt_shell_printf("Invalid argument: %s\n", input); return bt_shell_noninteractive_quit(EXIT_FAILURE); } return bt_shell_noninteractive_quit(EXIT_SUCCESS); } static void custom_latency(const char *input, void *user_data) { struct codec_preset *p = user_data; struct bt_bap_qos *qos = (void *)&p->qos; char *endptr = NULL; if (!p->target_latency) qos->bcast.io_qos.latency = strtol(input, &endptr, 0); else qos->ucast.io_qos.latency = strtol(input, &endptr, 0); if (!endptr || *endptr != '\0') { bt_shell_printf("Invalid argument: %s\n", input); return bt_shell_noninteractive_quit(EXIT_FAILURE); } bt_shell_prompt_input("QoS", "Enter Presentation Delay (us):", custom_delay, user_data); } static void custom_rtn(const char *input, void *user_data) { struct codec_preset *p = user_data; struct bt_bap_qos *qos = (void *)&p->qos; char *endptr = NULL; if (!p->target_latency) qos->bcast.io_qos.rtn = strtol(input, &endptr, 0); else qos->ucast.io_qos.rtn = strtol(input, &endptr, 0); if (!endptr || *endptr != '\0') { bt_shell_printf("Invalid argument: %s\n", input); return bt_shell_noninteractive_quit(EXIT_FAILURE); } bt_shell_prompt_input("QoS", "Enter Max Transport Latency (ms):", custom_latency, user_data); } static void custom_sdu(const char *input, void *user_data) { struct codec_preset *p = user_data; struct bt_bap_qos *qos = (void *)&p->qos; char *endptr = NULL; if (!p->target_latency) qos->bcast.io_qos.sdu = strtol(input, &endptr, 0); else qos->ucast.io_qos.sdu = strtol(input, &endptr, 0); if (!endptr || *endptr != '\0') { bt_shell_printf("Invalid argument: %s\n", input); return bt_shell_noninteractive_quit(EXIT_FAILURE); } bt_shell_prompt_input("QoS", "Enter RTN:", custom_rtn, user_data); } static void custom_phy(const char *input, void *user_data) { struct codec_preset *p = user_data; struct bt_bap_io_qos *qos; if (!p->target_latency) qos = &p->qos.bcast.io_qos; else qos = &p->qos.ucast.io_qos; if (!strcmp(input, "1M")) qos->phy = 0x01; else if (!strcmp(input, "2M")) qos->phy = 0x02; else { char *endptr = NULL; uint8_t phy = strtol(input, &endptr, 0); if (!endptr || *endptr != '\0') { bt_shell_printf("Invalid argument: %s\n", input); return bt_shell_noninteractive_quit(EXIT_FAILURE); } switch (phy) { case 0x01: case 0x02: qos->phy = phy; break; default: bt_shell_printf("Invalid argument: %s\n", input); return bt_shell_noninteractive_quit(EXIT_FAILURE); } } bt_shell_prompt_input("QoS", "Enter Max SDU:", custom_sdu, user_data); } static void custom_framing(const char *input, void *user_data) { struct codec_preset *p = user_data; uint8_t *framing; if (!p->target_latency) framing = &p->qos.bcast.framing; else framing = &p->qos.ucast.framing; if (!strcasecmp(input, "Unframed")) *framing = 0x00; else if (!strcasecmp(input, "Framed")) *framing = 0x01; else { char *endptr = NULL; *framing = strtol(input, &endptr, 0); if (!endptr || *endptr != '\0') { bt_shell_printf("Invalid argument: %s\n", input); return bt_shell_noninteractive_quit(EXIT_FAILURE); } } bt_shell_prompt_input("QoS", "Enter PHY (1M, 2M):", custom_phy, user_data); } static void custom_interval(const char *input, void *user_data) { struct codec_preset *p = user_data; char *endptr = NULL; struct bt_bap_io_qos *qos; if (!p->target_latency) qos = &p->qos.bcast.io_qos; else qos = &p->qos.ucast.io_qos; qos->interval = strtol(input, &endptr, 0); if (!endptr || *endptr != '\0') { bt_shell_printf("Invalid argument: %s\n", input); return bt_shell_noninteractive_quit(EXIT_FAILURE); } bt_shell_prompt_input("QoS", "Enter Framing (Unframed, Framed):", custom_framing, user_data); } static void custom_target_latency(const char *input, void *user_data) { struct codec_preset *p = user_data; if (!strcasecmp(input, "Low")) p->target_latency = 0x01; else if (!strcasecmp(input, "Balance")) p->target_latency = 0x02; else if (!strcasecmp(input, "High")) p->target_latency = 0x03; else { char *endptr = NULL; p->target_latency = strtol(input, &endptr, 0); if (!endptr || *endptr != '\0') { bt_shell_printf("Invalid argument: %s\n", input); return bt_shell_noninteractive_quit(EXIT_FAILURE); } } bt_shell_prompt_input("QoS", "Enter SDU Interval (us):", custom_interval, user_data); } static void custom_length(const char *input, void *user_data) { struct codec_preset *p = user_data; struct iovec *iov = (void *)&p->data; uint8_t ltv[4] = { 0x03, LC3_CONFIG_FRAME_LEN }; uint16_t len; char *endptr = NULL; len = strtol(input, &endptr, 0); if (!endptr || *endptr != '\0') { bt_shell_printf("Invalid argument: %s\n", input); return bt_shell_noninteractive_quit(EXIT_FAILURE); } ltv[2] = len; ltv[3] = len >> 8; util_iov_append(iov, ltv, sizeof(ltv)); bt_shell_prompt_input("QoS", "Enter Target Latency " "(Low, Balance, High):", custom_target_latency, user_data); } static void custom_location(const char *input, void *user_data) { struct codec_preset *p = user_data; struct iovec *iov = (void *)&p->data; uint32_t location; char *endptr = NULL; location = strtol(input, &endptr, 0); if (!endptr || *endptr != '\0') { bt_shell_printf("Invalid argument: %s\n", input); return bt_shell_noninteractive_quit(EXIT_FAILURE); } /* Only add Channel Allocation if set */ if (location) { uint8_t ltv[6] = { 0x05, LC3_CONFIG_CHAN_ALLOC }; location = cpu_to_le32(location); memcpy(<v[2], &location, sizeof(location)); util_iov_append(iov, ltv, sizeof(ltv)); } bt_shell_prompt_input("Codec", "Enter frame length:", custom_length, user_data); } static uint8_t val2duration(uint32_t val) { switch (val) { case 7: return 0x00; case 10: return 0x01; default: return 0xff; } } static void custom_duration(const char *input, void *user_data) { struct codec_preset *p = user_data; struct iovec *iov = (void *)&p->data; uint8_t ltv[3] = { 0x02, LC3_CONFIG_DURATION, 0x00 }; char *endptr = NULL; uint32_t val; val = strtol(input, &endptr, 0); if (!endptr || *endptr != '\0') { bt_shell_printf("Invalid argument: %s\n", input); return bt_shell_noninteractive_quit(EXIT_FAILURE); } if (strncmp(input, "0x", 2)) ltv[2] = val2duration(val); else ltv[2] = val; if (ltv[2] == 0xff) { bt_shell_printf("Invalid argument: %s\n", input); return bt_shell_noninteractive_quit(EXIT_FAILURE); } util_iov_append(iov, ltv, sizeof(ltv)); bt_shell_prompt_input("Codec", "Enter channel allocation:", custom_location, user_data); } static uint8_t val2freq(uint32_t val) { switch (val) { case 8: return 0x01; case 11: return 0x02; case 16: return 0x03; case 22: return 0x04; case 24: return 0x05; case 32: return 0x06; case 44: return 0x07; case 48: return 0x08; case 88: return 0x09; case 96: return 0x0a; case 174: return 0x0b; case 192: return 0x0c; case 384: return 0x0d; default: return 0x00; } } static void custom_frequency(const char *input, void *user_data) { struct codec_preset *p = user_data; struct iovec *iov = (void *)&p->data; uint8_t ltv[3] = { 0x02, LC3_CONFIG_FREQ, 0x00 }; uint32_t val; char *endptr = NULL; val = strtol(input, &endptr, 0); if (!endptr || *endptr != '\0') { bt_shell_printf("Invalid argument: %s\n", input); return bt_shell_noninteractive_quit(EXIT_FAILURE); } if (strncmp(input, "0x", 2)) ltv[2] = val2freq(val); else ltv[2] = val; if (!ltv[2]) { bt_shell_printf("Invalid argument: %s\n", input); return bt_shell_noninteractive_quit(EXIT_FAILURE); } /* Reset iov to start over the codec configuration */ free(iov->iov_base); iov->iov_base = NULL; iov->iov_len = 0; util_iov_append(iov, ltv, sizeof(ltv)); bt_shell_prompt_input("Codec", "Enter frame duration (ms):", custom_duration, user_data); } static void foreach_custom_preset_print(void *data, void *user_data) { struct codec_preset *p = data; struct preset *preset = user_data; bt_shell_printf("%s%s\n", p == preset->default_preset ? "*" : "", p->name); } static void print_presets(struct preset *preset) { size_t i; struct codec_preset *p; for (i = 0; i < preset->num_presets; i++) { p = &preset->presets[i]; if (p == preset->default_preset) bt_shell_printf("*%s\n", p->name); else if (preset->default_preset && p == preset->default_preset->alt_preset) bt_shell_printf("**%s\n", p->name); else bt_shell_printf("%s\n", p->name); } queue_foreach(preset->custom, foreach_custom_preset_print, preset); } static void custom_chan_alloc(const char *input, void *user_data) { struct codec_preset *p = user_data; char *endptr = NULL; p->chan_alloc = strtol(input, &endptr, 0); if (!endptr || *endptr != '\0') { bt_shell_printf("Invalid argument: %s\n", input); return bt_shell_noninteractive_quit(EXIT_FAILURE); } if (p->alt_preset) bt_shell_prompt_input(p->alt_preset->name, "Enter Channel Allocation: ", custom_chan_alloc, p->alt_preset); else return bt_shell_noninteractive_quit(EXIT_SUCCESS); } static void cmd_presets_endpoint(int argc, char *argv[]) { struct preset *preset; struct codec_preset *default_preset = NULL; struct endpoint *ep = NULL; preset = find_presets_name(argv[1], argv[2]); if (!preset) { ep = endpoint_find(argv[1]); if (!ep) { bt_shell_printf("No preset found\n"); return bt_shell_noninteractive_quit(EXIT_FAILURE); } preset = ep->preset; argv++; argc--; } else { argv += 2; argc -= 2; } if (argc > 1) { default_preset = codec_preset_add(preset, argv[1]); if (!default_preset) { bt_shell_printf("Preset %s not found\n", argv[1]); return bt_shell_noninteractive_quit(EXIT_FAILURE); } if (ep) ep->codec_preset = default_preset; else preset->default_preset = default_preset; if (argc > 2) { struct codec_preset *alt_preset; struct iovec *caps = (void *)&default_preset->data; struct iovec *meta = (void *)&default_preset->meta; /* Check if and alternative preset was given */ alt_preset = preset_find_name(preset, argv[2]); if (alt_preset) { default_preset->alt_preset = alt_preset; bt_shell_prompt_input(default_preset->name, "Enter Channel Allocation: ", custom_chan_alloc, default_preset); return; } /* Check if Codec Configuration was entered */ if (strlen(argv[2])) { caps->iov_base = str2bytearray(argv[2], &caps->iov_len); if (!caps->iov_base) { bt_shell_printf("Invalid configuration " "%s\n", argv[2]); return bt_shell_noninteractive_quit( EXIT_FAILURE); } } /* Check if metadata was entered */ if (argc > 3) { meta->iov_base = str2bytearray(argv[3], &meta->iov_len); if (!meta->iov_base) { bt_shell_printf("Invalid metadata %s\n", argv[5]); return bt_shell_noninteractive_quit( EXIT_FAILURE); } } /* If configuration was left empty then ask the * parameters. */ if (!caps->iov_base || !caps->iov_len) goto enter_cc; bt_shell_prompt_input("QoS", "Enter Target Latency " "(Low, Balance, High):", custom_target_latency, default_preset); return; } } else if (ep && (ep->codec_preset)) print_preset(ep->codec_preset, ep->codec); else print_presets(preset); enter_cc: if (default_preset && default_preset->custom) { bt_shell_prompt_input("Codec", "Enter frequency (Khz):", custom_frequency, default_preset); return; } return bt_shell_noninteractive_quit(EXIT_SUCCESS); } static const struct bt_shell_menu endpoint_menu = { .name = "endpoint", .desc = "Media Endpoint Submenu", .entries = { { "list", "[local]", cmd_list_endpoints, "List available endpoints" }, { "show", "[endpoint]", cmd_show_endpoint, "Endpoint information", endpoint_generator }, { "register", " [capabilities...]", cmd_register_endpoint, "Register Endpoint", uuid_generator }, { "unregister", "", cmd_unregister_endpoint, "Register Endpoint", local_endpoint_generator }, { "config", " [local endpoint] [preset]", cmd_config_endpoint, "Configure Endpoint", endpoint_generator }, { "presets", "/ [codec[:company]] [preset] " "[codec config] [metadata]", cmd_presets_endpoint, "List or add presets", uuid_generator }, {} }, }; static void endpoint_init_bcast(struct endpoint *ep) { if (!strcmp(ep->uuid, BAA_SERVICE_UUID)) { ep->locations = EP_SNK_LOCATIONS; ep->supported_context = EP_SUPPORTED_SNK_CTXT; } else { ep->locations = EP_SRC_LOCATIONS; ep->supported_context = EP_SUPPORTED_SRC_CTXT; } } static void endpoint_init_ucast(struct endpoint *ep) { if (!strcmp(ep->uuid, PAC_SINK_UUID)) { ep->locations = EP_SNK_LOCATIONS; ep->supported_context = EP_SUPPORTED_SNK_CTXT; ep->context = EP_SNK_CTXT; } else if (!strcmp(ep->uuid, PAC_SOURCE_UUID)) { ep->locations = EP_SRC_LOCATIONS; ep->supported_context = EP_SUPPORTED_SRC_CTXT; ep->context = EP_SRC_CTXT; } } static void endpoint_init_defaults(struct endpoint *ep) { ep->preset = find_presets(ep->uuid, ep->codec, ep->vid, ep->cid); ep->max_transports = UINT8_MAX; ep->auto_accept = true; if (!strcmp(ep->uuid, A2DP_SOURCE_UUID) || !strcmp(ep->uuid, A2DP_SOURCE_UUID)) return; ep->iso_group = BT_ISO_QOS_GROUP_UNSET; ep->iso_stream = BT_ISO_QOS_STREAM_UNSET; ep->broadcast = (strcmp(ep->uuid, BCAA_SERVICE_UUID) && strcmp(ep->uuid, BAA_SERVICE_UUID)) ? false : true; if (ep->broadcast) endpoint_init_bcast(ep); else endpoint_init_ucast(ep); } static struct endpoint *endpoint_new(const struct capabilities *cap) { struct endpoint *ep; ep = new0(struct endpoint, 1); ep->uuid = g_strdup(cap->uuid); ep->codec = cap->codec_id; ep->path = g_strdup_printf("%s/%s", BLUEZ_MEDIA_ENDPOINT_PATH, cap->name); /* Copy capabilities */ ep->caps = util_iov_dup(&cap->data, 1); /* Copy metadata */ ep->meta = util_iov_dup(&cap->meta, 1); local_endpoints = g_list_append(local_endpoints, ep); return ep; } static void register_endpoints(GDBusProxy *proxy) { struct endpoint *ep; size_t i; for (i = 0; i < ARRAY_SIZE(caps); i++) { const struct capabilities *cap = &caps[i]; if (!media_supports_uuid(proxy, cap->uuid)) continue; ep = endpoint_new(cap); endpoint_init_defaults(ep); endpoint_register(ep); } } static void media_added(GDBusProxy *proxy) { medias = g_list_append(medias, proxy); print_media(proxy, COLORED_NEW); if (bt_shell_get_env("AUTO_REGISTER_ENDPOINT")) register_endpoints(proxy); } static void player_added(GDBusProxy *proxy) { players = g_list_append(players, proxy); if (default_player == NULL) default_player = proxy; print_player(proxy, COLORED_NEW); } static void print_folder(GDBusProxy *proxy, const char *description) { const char *path; path = g_dbus_proxy_get_path(proxy); bt_shell_printf("%s%s%sFolder %s\n", description ? "[" : "", description ? : "", description ? "] " : "", path); } static void folder_added(GDBusProxy *proxy) { folders = g_list_append(folders, proxy); print_folder(proxy, COLORED_NEW); } static void print_item(GDBusProxy *proxy, const char *description) { const char *path, *name; DBusMessageIter iter; path = g_dbus_proxy_get_path(proxy); if (g_dbus_proxy_get_property(proxy, "Name", &iter)) dbus_message_iter_get_basic(&iter, &name); else name = ""; bt_shell_printf("%s%s%sItem %s %s\n", description ? "[" : "", description ? : "", description ? "] " : "", path, name); } static void item_added(GDBusProxy *proxy) { items = g_list_append(items, proxy); print_item(proxy, COLORED_NEW); } static void endpoint_added(GDBusProxy *proxy) { endpoints = g_list_append(endpoints, proxy); print_endpoint(proxy, COLORED_NEW); } static void print_transport(void *data, void *user_data) { GDBusProxy *proxy = data; const char *description = user_data; char *str; str = proxy_description(proxy, "Transport", description); bt_shell_printf("%s\n", str); g_free(str); } static void transport_added(GDBusProxy *proxy) { transports = g_list_append(transports, proxy); print_transport(proxy, COLORED_NEW); } static void proxy_added(GDBusProxy *proxy, void *user_data) { const char *interface; interface = g_dbus_proxy_get_interface(proxy); if (!strcmp(interface, BLUEZ_MEDIA_INTERFACE)) media_added(proxy); else if (!strcmp(interface, BLUEZ_MEDIA_PLAYER_INTERFACE)) player_added(proxy); else if (!strcmp(interface, BLUEZ_MEDIA_FOLDER_INTERFACE)) folder_added(proxy); else if (!strcmp(interface, BLUEZ_MEDIA_ITEM_INTERFACE)) item_added(proxy); else if (!strcmp(interface, BLUEZ_MEDIA_ENDPOINT_INTERFACE)) endpoint_added(proxy); else if (!strcmp(interface, BLUEZ_MEDIA_TRANSPORT_INTERFACE)) transport_added(proxy); } static void media_removed(GDBusProxy *proxy) { print_media(proxy, COLORED_DEL); medias = g_list_remove(medias, proxy); } static void player_removed(GDBusProxy *proxy) { print_player(proxy, COLORED_DEL); if (default_player == proxy) default_player = NULL; players = g_list_remove(players, proxy); } static void folder_removed(GDBusProxy *proxy) { folders = g_list_remove(folders, proxy); print_folder(proxy, COLORED_DEL); } static void item_removed(GDBusProxy *proxy) { items = g_list_remove(items, proxy); print_item(proxy, COLORED_DEL); } static void endpoint_removed(GDBusProxy *proxy) { endpoints = g_list_remove(endpoints, proxy); print_endpoint(proxy, COLORED_DEL); } static void transport_removed(GDBusProxy *proxy) { transports = g_list_remove(transports, proxy); print_transport(proxy, COLORED_DEL); } static void proxy_removed(GDBusProxy *proxy, void *user_data) { const char *interface; interface = g_dbus_proxy_get_interface(proxy); if (!strcmp(interface, BLUEZ_MEDIA_INTERFACE)) media_removed(proxy); if (!strcmp(interface, BLUEZ_MEDIA_PLAYER_INTERFACE)) player_removed(proxy); if (!strcmp(interface, BLUEZ_MEDIA_FOLDER_INTERFACE)) folder_removed(proxy); if (!strcmp(interface, BLUEZ_MEDIA_ITEM_INTERFACE)) item_removed(proxy); if (!strcmp(interface, BLUEZ_MEDIA_ENDPOINT_INTERFACE)) endpoint_removed(proxy); if (!strcmp(interface, BLUEZ_MEDIA_TRANSPORT_INTERFACE)) transport_removed(proxy); } static void player_property_changed(GDBusProxy *proxy, const char *name, DBusMessageIter *iter) { char *str; str = proxy_description(proxy, "Player", COLORED_CHG); print_iter(str, name, iter); g_free(str); } static void folder_property_changed(GDBusProxy *proxy, const char *name, DBusMessageIter *iter) { char *str; str = proxy_description(proxy, "Folder", COLORED_CHG); print_iter(str, name, iter); g_free(str); } static void item_property_changed(GDBusProxy *proxy, const char *name, DBusMessageIter *iter) { char *str; str = proxy_description(proxy, "Item", COLORED_CHG); print_iter(str, name, iter); g_free(str); } static void endpoint_property_changed(GDBusProxy *proxy, const char *name, DBusMessageIter *iter) { char *str; str = proxy_description(proxy, "Endpoint", COLORED_CHG); print_iter(str, name, iter); g_free(str); } static struct endpoint *find_ep_by_transport(const char *path) { GList *l; for (l = local_endpoints; l; l = g_list_next(l)) { struct endpoint *ep = l->data; if (queue_find(ep->transports, match_str, path)) return ep; } return NULL; } static GDBusProxy *find_link_by_proxy(GDBusProxy *proxy) { DBusMessageIter iter, array; if (!g_dbus_proxy_get_property(proxy, "Links", &iter)) return NULL; dbus_message_iter_recurse(&iter, &array); while (dbus_message_iter_get_arg_type(&array) == DBUS_TYPE_OBJECT_PATH) { const char *transport; dbus_message_iter_get_basic(&array, &transport); proxy = g_dbus_proxy_lookup(transports, NULL, transport, BLUEZ_MEDIA_TRANSPORT_INTERFACE); if (proxy) return proxy; } return NULL; } static void transport_close(struct transport *transport) { if (transport->fd < 0) return; close(transport->fd); transport->fd = -1; free(transport->filename); } static void transport_free(void *data) { struct transport *transport = data; io_destroy(transport->timer_io); io_destroy(transport->io); free(transport); } static bool transport_disconnected(struct io *io, void *user_data) { struct transport *transport = user_data; bt_shell_printf("Transport fd disconnected\n"); if (queue_remove(ios, transport)) transport_free(transport); return false; } static bool transport_recv(struct io *io, void *user_data) { struct transport *transport = user_data; uint8_t buf[1024]; int ret, len; ret = io_get_fd(io); if (ret < 0) { bt_shell_printf("io_get_fd() returned %d\n", ret); return true; } ret = read(ret, buf, sizeof(buf)); if (ret < 0) { bt_shell_printf("Failed to read: %s (%d)\n", strerror(errno), -errno); return true; } bt_shell_echo("[seq %d] recv: %u bytes", transport->seq, ret); transport->seq++; if (transport->filename) { len = write(transport->fd, buf, ret); if (len < 0) bt_shell_printf("Unable to write: %s (%d)\n", strerror(errno), -errno); } return true; } static void transport_new(GDBusProxy *proxy, int sk, uint16_t mtu[2]) { struct transport *transport; transport = new0(struct transport, 1); transport->proxy = proxy; transport->sk = sk; transport->mtu[0] = mtu[0]; transport->mtu[1] = mtu[1]; transport->io = io_new(sk); transport->fd = -1; io_set_disconnect_handler(transport->io, transport_disconnected, transport, NULL); io_set_read_handler(transport->io, transport_recv, transport, NULL); if (!ios) ios = queue_new(); queue_push_tail(ios, transport); } static void ep_set_acquiring(struct endpoint *ep, GDBusProxy *proxy, bool value) { bt_shell_printf("Transport %s %s\n", g_dbus_proxy_get_path(proxy), value ? "acquiring" : "acquiring complete"); if (value && !ep->acquiring) ep->acquiring = queue_new(); if (value) queue_push_tail(ep->acquiring, proxy); else queue_remove(ep->acquiring, proxy); } static void transport_set_acquiring(GDBusProxy *proxy, bool value) { struct endpoint *ep; GDBusProxy *link; ep = find_ep_by_transport(g_dbus_proxy_get_path(proxy)); if (!ep) return; ep_set_acquiring(ep, proxy, value); if (!ep->broadcast) { link = find_link_by_proxy(proxy); if (link) { ep = find_ep_by_transport(g_dbus_proxy_get_path(link)); if (!ep) return; ep_set_acquiring(ep, link, value); } } } static void acquire_reply(DBusMessage *message, void *user_data) { GDBusProxy *proxy = user_data; DBusError error; int sk; uint16_t mtu[2]; transport_set_acquiring(proxy, false); dbus_error_init(&error); if (dbus_set_error_from_message(&error, message) == TRUE) { bt_shell_printf("Failed to acquire: %s\n", error.name); dbus_error_free(&error); return bt_shell_noninteractive_quit(EXIT_FAILURE); } if (!dbus_message_get_args(message, &error, DBUS_TYPE_UNIX_FD, &sk, DBUS_TYPE_UINT16, &mtu[0], DBUS_TYPE_UINT16, &mtu[1], DBUS_TYPE_INVALID)) { bt_shell_printf("Failed to parse Acquire() reply: %s", error.name); dbus_error_free(&error); return bt_shell_noninteractive_quit(EXIT_FAILURE); } bt_shell_printf("Acquire successful: fd %d MTU %u:%u\n", sk, mtu[0], mtu[1]); transport_new(proxy, sk, mtu); return bt_shell_noninteractive_quit(EXIT_FAILURE); } static void free_transport_select_args(struct transport_select_args *args) { queue_destroy(args->links, NULL); queue_destroy(args->selecting, NULL); g_free(args); } static void select_reply(DBusMessage *message, void *user_data) { DBusError error; struct transport_select_args *args = user_data; GDBusProxy *link; dbus_error_init(&error); if (dbus_set_error_from_message(&error, message) == TRUE) { bt_shell_printf("Failed to select: %s\n", error.name); dbus_error_free(&error); free_transport_select_args(args); return bt_shell_noninteractive_quit(EXIT_FAILURE); } bt_shell_printf("Select successful\n"); /* Select next link */ link = queue_pop_head(args->selecting); if (link) { args->proxy = link; transport_select(args); } else { free_transport_select_args(args); return bt_shell_noninteractive_quit(EXIT_SUCCESS); } } static void unselect_reply(DBusMessage *message, void *user_data) { DBusError error; dbus_error_init(&error); if (dbus_set_error_from_message(&error, message) == TRUE) { bt_shell_printf("Failed to unselect: %s\n", error.name); dbus_error_free(&error); return bt_shell_noninteractive_quit(EXIT_FAILURE); } bt_shell_printf("Unselect successful\n"); return bt_shell_noninteractive_quit(EXIT_SUCCESS); } static void prompt_acquire(const char *input, void *user_data) { GDBusProxy *proxy = user_data; if (!strcasecmp(input, "y") || !strcasecmp(input, "yes")) { if (g_dbus_proxy_method_call(proxy, "Acquire", NULL, acquire_reply, proxy, NULL)) { transport_set_acquiring(proxy, true); return; } bt_shell_printf("Failed acquire transport\n"); } } static void transport_acquire(GDBusProxy *proxy, bool prompt) { struct endpoint *ep; GDBusProxy *link; /* only attempt to acquire if transport is configured with a local * endpoint. */ ep = find_ep_by_transport(g_dbus_proxy_get_path(proxy)); if (!ep || queue_find(ep->acquiring, NULL, proxy)) return; if (!ep->broadcast) { link = find_link_by_proxy(proxy); if (link) { ep = find_ep_by_transport(g_dbus_proxy_get_path(link)); /* if link already acquiring wait it to be complete */ if (!ep || queue_find(ep->acquiring, NULL, link)) return; } } if (ep->auto_accept || !prompt) { if (!prompt) bt_shell_printf("auto acquiring...\n"); if (!g_dbus_proxy_method_call(proxy, "Acquire", NULL, acquire_reply, proxy, NULL)) { bt_shell_printf("failed acquire transport\n"); return; } transport_set_acquiring(proxy, true); return; } bt_shell_prompt_input(g_dbus_proxy_get_path(proxy), "acquire (yes/no):", prompt_acquire, proxy); } static void transport_property_changed(GDBusProxy *proxy, const char *name, DBusMessageIter *iter) { char *str; str = proxy_description(proxy, "Transport", COLORED_CHG); print_iter(str, name, iter); g_free(str); if (strcmp(name, "State")) return; dbus_message_iter_get_basic(iter, &str); if (strcmp(str, "pending")) return; transport_acquire(proxy, true); } static void property_changed(GDBusProxy *proxy, const char *name, DBusMessageIter *iter, void *user_data) { const char *interface; interface = g_dbus_proxy_get_interface(proxy); if (!strcmp(interface, BLUEZ_MEDIA_PLAYER_INTERFACE)) player_property_changed(proxy, name, iter); else if (!strcmp(interface, BLUEZ_MEDIA_FOLDER_INTERFACE)) folder_property_changed(proxy, name, iter); else if (!strcmp(interface, BLUEZ_MEDIA_ITEM_INTERFACE)) item_property_changed(proxy, name, iter); else if (!strcmp(interface, BLUEZ_MEDIA_ENDPOINT_INTERFACE)) endpoint_property_changed(proxy, name, iter); else if (!strcmp(interface, BLUEZ_MEDIA_TRANSPORT_INTERFACE)) transport_property_changed(proxy, name, iter); } static char *transport_generator(const char *text, int state) { return generic_generator(text, state, transports); } static void cmd_list_transport(int argc, char *argv[]) { GList *l; for (l = transports; l; l = g_list_next(l)) { GDBusProxy *proxy = l->data; print_transport(proxy, NULL); } return bt_shell_noninteractive_quit(EXIT_SUCCESS); } static void print_configuration(GDBusProxy *proxy) { DBusMessageIter iter, subiter; const char *uuid; uint8_t codec; uint8_t *data; int len; if (!g_dbus_proxy_get_property(proxy, "UUID", &iter)) return; dbus_message_iter_get_basic(&iter, &uuid); if (!g_dbus_proxy_get_property(proxy, "Codec", &iter)) return; dbus_message_iter_get_basic(&iter, &codec); if (!g_dbus_proxy_get_property(proxy, "Configuration", &iter)) return; dbus_message_iter_recurse(&iter, &subiter); dbus_message_iter_get_fixed_array(&subiter, &data, &len); if (!strcasecmp(uuid, A2DP_SINK_UUID) || !strcasecmp(uuid, A2DP_SOURCE_UUID)) { print_a2dp_codec(codec, (void *)data, len); return; } if (codec != LC3_ID) { print_property(proxy, "Configuration"); return; } print_lc3_cfg(data, len); if (!g_dbus_proxy_get_property(proxy, "Metadata", &iter)) return; dbus_message_iter_recurse(&iter, &subiter); dbus_message_iter_get_fixed_array(&subiter, &data, &len); print_lc3_meta(data, len); } static void print_transport_properties(GDBusProxy *proxy) { bt_shell_printf("Transport %s\n", g_dbus_proxy_get_path(proxy)); print_property(proxy, "UUID"); print_property(proxy, "Codec"); print_configuration(proxy); print_property(proxy, "Device"); print_property(proxy, "State"); print_property(proxy, "Delay"); print_property(proxy, "Volume"); print_property(proxy, "Endpoint"); print_property(proxy, "QoS"); print_property(proxy, "Location"); print_property(proxy, "Links"); } static void print_transports(void *data, void *user_data) { print_transport_properties(data); } static void cmd_show_transport(int argc, char *argv[]) { GDBusProxy *proxy; /* Show all transports if no argument is given */ if (argc != 2) { g_list_foreach(transports, print_transports, NULL); return bt_shell_noninteractive_quit(EXIT_SUCCESS); } proxy = g_dbus_proxy_lookup(transports, NULL, argv[1], BLUEZ_MEDIA_TRANSPORT_INTERFACE); if (!proxy) { bt_shell_printf("Transport %s not found\n", argv[1]); return bt_shell_noninteractive_quit(EXIT_FAILURE); } print_transport_properties(proxy); return bt_shell_noninteractive_quit(EXIT_SUCCESS); } static bool match_proxy(const void *data, const void *user_data) { const struct transport *transport = data; const GDBusProxy *proxy = user_data; return transport->proxy == proxy; } static struct transport *find_transport(GDBusProxy *proxy) { return queue_find(ios, match_proxy, proxy); } static void cmd_acquire_transport(int argc, char *argv[]) { GDBusProxy *proxy; int i; for (i = 1; i < argc; i++) { proxy = g_dbus_proxy_lookup(transports, NULL, argv[i], BLUEZ_MEDIA_TRANSPORT_INTERFACE); if (!proxy) { bt_shell_printf("Transport %s not found\n", argv[i]); return bt_shell_noninteractive_quit(EXIT_FAILURE); } if (find_transport(proxy)) { bt_shell_printf("Transport %s already acquired\n", argv[i]); return bt_shell_noninteractive_quit(EXIT_FAILURE); } transport_acquire(proxy, false); } return bt_shell_noninteractive_quit(EXIT_SUCCESS); } static void set_bcode_cb(const DBusError *error, void *user_data) { struct transport_select_args *args = user_data; if (dbus_error_is_set(error)) { bt_shell_printf("Failed to set broadcast code: %s\n", error->name); free_transport_select_args(args); return bt_shell_noninteractive_quit(EXIT_FAILURE); } bt_shell_printf("Setting broadcast code succeeded\n"); transport_select(args); } static void set_bcode(const char *input, void *user_data) { struct transport_select_args *args = user_data; char *bcode; if (!strcasecmp(input, "n") || !strcasecmp(input, "no")) bcode = g_new0(char, 16); else bcode = g_strdup(input); if (g_dbus_proxy_set_property_dict(args->proxy, "QoS", set_bcode_cb, user_data, NULL, "BCode", DBUS_TYPE_ARRAY, DBUS_TYPE_BYTE, strlen(bcode), bcode, NULL) == FALSE) { bt_shell_printf("Setting broadcast code failed\n"); g_free(bcode); free_transport_select_args(args); return bt_shell_noninteractive_quit(EXIT_FAILURE); } g_free(bcode); } static void transport_select(struct transport_select_args *args) { if (!g_dbus_proxy_method_call(args->proxy, "Select", NULL, select_reply, args, NULL)) { bt_shell_printf("Failed select transport\n"); free_transport_select_args(args); return bt_shell_noninteractive_quit(EXIT_FAILURE); } } static void transport_set_bcode(struct transport_select_args *args) { DBusMessageIter iter, array, entry, value; unsigned char encryption; const char *key; if (g_dbus_proxy_get_property(args->proxy, "QoS", &iter) == FALSE) { free_transport_select_args(args); return bt_shell_noninteractive_quit(EXIT_FAILURE); } dbus_message_iter_recurse(&iter, &array); while (dbus_message_iter_get_arg_type(&array) != DBUS_TYPE_INVALID) { dbus_message_iter_recurse(&array, &entry); dbus_message_iter_get_basic(&entry, &key); if (!strcasecmp(key, "Encryption")) { dbus_message_iter_next(&entry); dbus_message_iter_recurse(&entry, &value); dbus_message_iter_get_basic(&value, &encryption); if (encryption == 1) { bt_shell_prompt_input("", "Enter brocast code[value/no]:", set_bcode, args); return; } break; } dbus_message_iter_next(&array); } /* Go straight to selecting transport, if Broadcast Code * is not required. */ transport_select(args); } static void transport_unselect(GDBusProxy *proxy, bool prompt) { if (!g_dbus_proxy_method_call(proxy, "Unselect", NULL, unselect_reply, proxy, NULL)) { bt_shell_printf("Failed unselect transport\n"); return; } } static void set_links_cb(const DBusError *error, void *user_data) { struct transport_select_args *args = user_data; GDBusProxy *link; link = queue_pop_head(args->links); if (queue_isempty(args->links)) { queue_destroy(args->links, NULL); args->links = NULL; } if (dbus_error_is_set(error)) { bt_shell_printf("Failed to set link %s: %s\n", g_dbus_proxy_get_path(link), error->name); free_transport_select_args(args); return bt_shell_noninteractive_quit(EXIT_FAILURE); } bt_shell_printf("Successfully linked transport %s\n", g_dbus_proxy_get_path(link)); if (!args->selecting) args->selecting = queue_new(); /* Enqueue link to mark that it is ready to be selected */ queue_push_tail(args->selecting, link); /* Continue setting the remanining links */ transport_set_links(args); } static void transport_set_links(struct transport_select_args *args) { GDBusProxy *link; const char *path; link = queue_peek_head(args->links); if (link) { path = g_dbus_proxy_get_path(link); if (g_dbus_proxy_set_property_array(args->proxy, "Links", DBUS_TYPE_OBJECT_PATH, &path, 1, set_links_cb, args, NULL) == FALSE) { bt_shell_printf("Linking transport %s failed\n", path); free_transport_select_args(args); return bt_shell_noninteractive_quit(EXIT_FAILURE); } return; } /* If all links have been set, check is transport requires the * user to provide a Broadcast Code. */ transport_set_bcode(args); } static void cmd_select_transport(int argc, char *argv[]) { GDBusProxy *link = NULL; struct transport_select_args *args; int i; args = g_new0(struct transport_select_args, 1); for (i = 1; i < argc; i++) { link = g_dbus_proxy_lookup(transports, NULL, argv[i], BLUEZ_MEDIA_TRANSPORT_INTERFACE); if (!link) { bt_shell_printf("Transport %s not found\n", argv[i]); goto fail; } if (find_transport(link)) { bt_shell_printf("Transport %s already acquired\n", argv[i]); goto fail; } if (!args->proxy) { args->proxy = link; continue; } if (!args->links) args->links = queue_new(); /* Enqueue all links */ queue_push_tail(args->links, link); } /* Link streams before selecting one by one */ transport_set_links(args); return; fail: free_transport_select_args(args); return bt_shell_noninteractive_quit(EXIT_FAILURE); } static void cmd_unselect_transport(int argc, char *argv[]) { GDBusProxy *proxy; int i; for (i = 1; i < argc; i++) { proxy = g_dbus_proxy_lookup(transports, NULL, argv[i], BLUEZ_MEDIA_TRANSPORT_INTERFACE); if (!proxy) { bt_shell_printf("Transport %s not found\n", argv[i]); return bt_shell_noninteractive_quit(EXIT_FAILURE); } transport_unselect(proxy, false); } } static void release_reply(DBusMessage *message, void *user_data) { struct transport *transport = user_data; DBusError error; dbus_error_init(&error); if (dbus_set_error_from_message(&error, message) == TRUE) { bt_shell_printf("Failed to release: %s\n", error.name); dbus_error_free(&error); return bt_shell_noninteractive_quit(EXIT_FAILURE); } if (queue_remove(ios, transport)) transport_free(transport); bt_shell_printf("Release successful\n"); return bt_shell_noninteractive_quit(EXIT_FAILURE); } static void cmd_release_transport(int argc, char *argv[]) { GDBusProxy *proxy; int i; for (i = 1; i < argc; i++) { struct transport *transport; proxy = g_dbus_proxy_lookup(transports, NULL, argv[i], BLUEZ_MEDIA_TRANSPORT_INTERFACE); if (!proxy) { bt_shell_printf("Transport %s not found\n", argv[1]); return bt_shell_noninteractive_quit(EXIT_FAILURE); } transport = find_transport(proxy); if (!transport) { bt_shell_printf("Transport %s not acquired\n", argv[i]); return bt_shell_noninteractive_quit(EXIT_FAILURE); } if (!g_dbus_proxy_method_call(proxy, "Release", NULL, release_reply, transport, NULL)) { bt_shell_printf("Failed release transport\n"); return bt_shell_noninteractive_quit(EXIT_FAILURE); } } return bt_shell_noninteractive_quit(EXIT_SUCCESS); } static int open_file(const char *filename, int flags) { int fd = -1; bt_shell_printf("Opening %s ...\n", filename); if (flags & O_CREAT) fd = open(filename, flags, 0755); else fd = open(filename, flags); if (fd <= 0) bt_shell_printf("Can't open file %s: %s\n", filename, strerror(errno)); return fd; } static int elapsed_time(bool reset, int *secs, int *nsecs) { static struct timespec start; struct timespec curr; if (reset) { if (clock_gettime(CLOCK_MONOTONIC, &start) < 0) { bt_shell_printf("clock_gettime: %s (%d)", strerror(errno), errno); return -errno; } } if (clock_gettime(CLOCK_MONOTONIC, &curr) < 0) { bt_shell_printf("clock_gettime: %s (%d)", strerror(errno), errno); return -errno; } *secs = curr.tv_sec - start.tv_sec; *nsecs = curr.tv_nsec - start.tv_nsec; if (*nsecs < 0) { (*secs)--; *nsecs += 1000000000; } return 0; } static int transport_send_seq(struct transport *transport, int fd, uint32_t num) { uint8_t *buf; uint32_t i; if (!num) return 0; buf = malloc(transport->mtu[1]); if (!buf) return -ENOMEM; for (i = 0; i < num; i++, transport->seq++) { ssize_t ret; int secs = 0, nsecs = 0; off_t offset; ret = read(fd, buf, transport->mtu[1]); if (ret <= 0) { if (ret < 0) bt_shell_printf("read failed: %s (%d)", strerror(errno), errno); free(buf); return ret; } ret = send(transport->sk, buf, ret, 0); if (ret <= 0) { bt_shell_printf("send failed: %s (%d)", strerror(errno), errno); free(buf); return -errno; } elapsed_time(!transport->seq, &secs, &nsecs); if (!transport->seq && fstat(fd, &transport->stat) < 0) { bt_shell_printf("fstat failed: %s (%d)", strerror(errno), errno); free(buf); return -errno; } offset = lseek(fd, 0, SEEK_CUR); bt_shell_echo("[seq %d %d.%03ds] send: %lld/%lld bytes", transport->seq, secs, (nsecs + 500000) / 1000000, (long long)offset, (long long)transport->stat.st_size); } free(buf); return i; } static bool transport_timer_read(struct io *io, void *user_data) { struct transport *transport = user_data; struct bt_iso_qos qos; socklen_t len; int ret, fd; uint64_t exp; if (transport->fd < 0) return false; fd = io_get_fd(io); if (fd < 0) { bt_shell_printf("io_get_fd() returned %d\n", fd); return false; } ret = read(fd, &exp, sizeof(exp)); if (ret < 0) { bt_shell_printf("Failed to read: %s (%d)\n", strerror(errno), -errno); return false; } /* Read QoS if available */ memset(&qos, 0, sizeof(qos)); len = sizeof(qos); if (getsockopt(transport->sk, SOL_BLUETOOTH, BT_ISO_QOS, &qos, &len) < 0) { bt_shell_printf("Failed to getsockopt(BT_ISO_QOS): %s (%d)\n", strerror(errno), -errno); return false; } ret = transport_send_seq(transport, transport->fd, transport->num); if (ret < 0) { bt_shell_printf("Unable to send: %s (%d)\n", strerror(-ret), ret); return false; } if (!ret) { transport_close(transport); return false; } return true; } static int transport_send(struct transport *transport, int fd, struct bt_iso_io_qos *qos) { struct itimerspec ts; int timer_fd; transport->seq = 0; if (!qos) return transport_send_seq(transport, fd, UINT32_MAX); if (transport->fd >= 0) return -EALREADY; timer_fd = timerfd_create(CLOCK_MONOTONIC, 0); if (timer_fd < 0) return -errno; /* Send data in bursts of * num = ROUND_CLOSEST(Transport_Latency (ms) / SDU_Interval (us)) * with average data rate = 1 packet / SDU_Interval */ transport->num = ROUND_CLOSEST(qos->latency * 1000, qos->interval); if (!transport->num) transport->num = 1; memset(&ts, 0, sizeof(ts)); ts.it_value.tv_nsec = 1; ts.it_interval.tv_nsec = transport->num * qos->interval * 1000; if (timerfd_settime(timer_fd, TFD_TIMER_ABSTIME, &ts, NULL) < 0) { close(timer_fd); return -errno; } transport->fd = fd; transport->timer_io = io_new(timer_fd); io_set_read_handler(transport->timer_io, transport_timer_read, transport, NULL); /* One extra packet to buffers immediately */ return transport_send_seq(transport, fd, 1); } static void cmd_send_transport(int argc, char *argv[]) { GDBusProxy *proxy; struct transport *transport; int fd = -1, err; struct bt_iso_qos qos; socklen_t len; int i; for (i = 1; i < argc; i++) { proxy = g_dbus_proxy_lookup(transports, NULL, argv[i], BLUEZ_MEDIA_TRANSPORT_INTERFACE); if (!proxy) { bt_shell_printf("Transport %s not found\n", argv[i]); return bt_shell_noninteractive_quit(EXIT_FAILURE); } transport = find_transport(proxy); if (!transport) { bt_shell_printf("Transport %s not acquired\n", argv[i]); return bt_shell_noninteractive_quit(EXIT_FAILURE); } if (transport->sk < 0) { bt_shell_printf("No Transport Socked found\n"); return bt_shell_noninteractive_quit(EXIT_FAILURE); } if (i + 1 < argc) { fd = open_file(argv[++i], O_RDONLY); if (fd < 0) return bt_shell_noninteractive_quit( EXIT_FAILURE); } bt_shell_printf("Sending ...\n"); /* Read QoS if available */ memset(&qos, 0, sizeof(qos)); len = sizeof(qos); if (getsockopt(transport->sk, SOL_BLUETOOTH, BT_ISO_QOS, &qos, &len) < 0) { bt_shell_printf("Unable to getsockopt(BT_ISO_QOS): %s", strerror(errno)); err = transport_send(transport, fd, NULL); } else { struct sockaddr_iso addr; socklen_t optlen = sizeof(addr); err = getpeername(transport->sk, (struct sockaddr *)&addr, &optlen); if (!err) { if (!(bacmp(&addr.iso_bdaddr, BDADDR_ANY))) err = transport_send(transport, fd, &qos.bcast.out); else err = transport_send(transport, fd, &qos.ucast.out); } } if (err < 0) { bt_shell_printf("Unable to send: %s (%d)\n", strerror(-err), -err); close(fd); return bt_shell_noninteractive_quit(EXIT_FAILURE); } } return bt_shell_noninteractive_quit(EXIT_SUCCESS); } static void cmd_receive_transport(int argc, char *argv[]) { GDBusProxy *proxy; struct transport *transport; proxy = g_dbus_proxy_lookup(transports, NULL, argv[1], BLUEZ_MEDIA_TRANSPORT_INTERFACE); if (!proxy) { bt_shell_printf("Transport %s not found\n", argv[1]); return bt_shell_noninteractive_quit(EXIT_FAILURE); } transport = find_transport(proxy); if (!transport) { bt_shell_printf("Transport %s not acquired\n", argv[1]); return bt_shell_noninteractive_quit(EXIT_FAILURE); } if (transport->sk < 0) { bt_shell_printf("No Transport Socked found\n"); return bt_shell_noninteractive_quit(EXIT_FAILURE); } transport_close(transport); transport->fd = open_file(argv[2], O_RDWR | O_CREAT); if (transport->fd < 0) return bt_shell_noninteractive_quit(EXIT_FAILURE); transport->filename = strdup(argv[2]); bt_shell_printf("Filename: %s\n", transport->filename); return bt_shell_noninteractive_quit(EXIT_SUCCESS); } static void volume_callback(const DBusError *error, void *user_data) { if (dbus_error_is_set(error)) { bt_shell_printf("Failed to set Volume: %s\n", error->name); return bt_shell_noninteractive_quit(EXIT_FAILURE); } bt_shell_printf("Changing Volume succeeded\n"); return bt_shell_noninteractive_quit(EXIT_SUCCESS); } static void cmd_volume_transport(int argc, char *argv[]) { GDBusProxy *proxy; char *endptr = NULL; int volume; proxy = g_dbus_proxy_lookup(transports, NULL, argv[1], BLUEZ_MEDIA_TRANSPORT_INTERFACE); if (!proxy) { bt_shell_printf("Transport %s not found\n", argv[1]); return bt_shell_noninteractive_quit(EXIT_FAILURE); } if (argc == 2) { print_property(proxy, "Volume"); return bt_shell_noninteractive_quit(EXIT_FAILURE); } volume = strtol(argv[2], &endptr, 0); if (!endptr || *endptr != '\0' || volume > UINT16_MAX) { bt_shell_printf("Invalid argument: %s\n", argv[2]); return bt_shell_noninteractive_quit(EXIT_FAILURE); } if (!g_dbus_proxy_set_property_basic(proxy, "Volume", DBUS_TYPE_UINT16, &volume, volume_callback, NULL, NULL)) { bt_shell_printf("Failed release transport\n"); return bt_shell_noninteractive_quit(EXIT_FAILURE); } } static const struct bt_shell_menu transport_menu = { .name = "transport", .desc = "Media Transport Submenu", .entries = { { "list", NULL, cmd_list_transport, "List available transports" }, { "show", "[transport]", cmd_show_transport, "Transport information", transport_generator }, { "acquire", " [transport1...]", cmd_acquire_transport, "Acquire Transport", transport_generator }, { "release", " [transport1...]", cmd_release_transport, "Release Transport", transport_generator }, { "send", " [transport1...]", cmd_send_transport, "Send contents of a file", transport_generator }, { "receive", " [filename]", cmd_receive_transport, "Get/Set file to receive", transport_generator }, { "volume", " [value]", cmd_volume_transport, "Get/Set transport volume", transport_generator }, { "select", " [transport1...]", cmd_select_transport, "Select Transport", transport_generator }, { "unselect", " [transport1...]", cmd_unselect_transport, "Unselect Transport", transport_generator }, {} }, }; static GDBusClient *client; void player_add_submenu(void) { bt_shell_add_submenu(&player_menu); bt_shell_add_submenu(&endpoint_menu); bt_shell_add_submenu(&transport_menu); dbus_conn = bt_shell_get_env("DBUS_CONNECTION"); if (!dbus_conn || client) return; client = g_dbus_client_new(dbus_conn, "org.bluez", "/org/bluez"); g_dbus_client_set_proxy_handlers(client, proxy_added, proxy_removed, property_changed, NULL); g_dbus_client_set_disconnect_watch(client, disconnect_handler, NULL); } void player_remove_submenu(void) { g_dbus_client_unref(client); queue_destroy(ios, transport_free); } bluez-5.82/client/PaxHeaders/agent.h0000644000000000000000000000005014015011623014364 xustar0020 atime=1743515768 20 ctime=1743591281 bluez-5.82/client/agent.h0000644000000000000000000000066714015011623014056 0ustar00rootroot/* SPDX-License-Identifier: GPL-2.0-or-later */ /* * * BlueZ - Bluetooth protocol stack for Linux * * Copyright (C) 2012 Intel Corporation. All rights reserved. * * */ void agent_register(DBusConnection *conn, GDBusProxy *manager, const char *capability); void agent_unregister(DBusConnection *conn, GDBusProxy *manager); void agent_default(DBusConnection *conn, GDBusProxy *manager); dbus_bool_t agent_completion(void); bluez-5.82/client/PaxHeaders/adv_monitor.h0000644000000000000000000000005014015011623015607 xustar0020 atime=1743515768 20 ctime=1743591281 bluez-5.82/client/adv_monitor.h0000644000000000000000000000160114015011623015266 0ustar00rootroot/* SPDX-License-Identifier: GPL-2.0-or-later */ /* * * BlueZ - Bluetooth protocol stack for Linux * * Copyright (C) 2020 Google LLC * * */ void adv_monitor_add_manager(DBusConnection *conn, GDBusProxy *proxy); void adv_monitor_remove_manager(DBusConnection *conn); void adv_monitor_register_app(DBusConnection *conn); void adv_monitor_unregister_app(DBusConnection *conn); void adv_monitor_set_rssi_threshold(int16_t low_threshold, int16_t high_threshold); void adv_monitor_set_rssi_timeout(uint16_t low_timeout, uint16_t high_timeout); void adv_monitor_set_rssi_sampling_period(uint16_t sampling); void adv_monitor_add_monitor(DBusConnection *conn, char *type, int argc, char *argv[]); void adv_monitor_print_monitor(DBusConnection *conn, int monitor_idx); void adv_monitor_remove_monitor(DBusConnection *conn, int monitor_idx); void adv_monitor_get_supported_info(void); bluez-5.82/client/PaxHeaders/bluetoothctl-advertise.10000644000000000000000000000005014773213524017712 xustar0020 atime=1743591252 20 ctime=1743591291 bluez-5.82/client/bluetoothctl-advertise.10000644000000000000000000001724214773213524017401 0ustar00rootroot.\" Man page generated from reStructuredText. . . .nr rst2man-indent-level 0 . .de1 rstReportMargin \\$1 \\n[an-margin] level \\n[rst2man-indent-level] level margin: \\n[rst2man-indent\\n[rst2man-indent-level]] - \\n[rst2man-indent0] \\n[rst2man-indent1] \\n[rst2man-indent2] .. .de1 INDENT .\" .rstReportMargin pre: . RS \\$1 . nr rst2man-indent\\n[rst2man-indent-level] \\n[an-margin] . nr rst2man-indent-level +1 .\" .rstReportMargin post: .. .de UNINDENT . RE .\" indent \\n[an-margin] .\" old: \\n[rst2man-indent\\n[rst2man-indent-level]] .nr rst2man-indent-level -1 .\" new: \\n[rst2man-indent\\n[rst2man-indent-level]] .in \\n[rst2man-indent\\n[rst2man-indent-level]]u .. .TH "BLUETOOTHCTL-ADVERTISE" "1" "November 2022" "BlueZ" "Linux System Administration" .SH NAME bluetoothctl-advertise \- Advertise Submenu .SH SYNOPSIS .sp \fBbluetoothctl\fP [\-\-options] [advertise.commands] .SH ADVERTISE OPTIONS COMMANDS .SS uuids .sp Set/Get advertise uuids. .INDENT 0.0 .TP .B Usage \fB> uuids [all/uuid1 uuid2 ...]\fP .TP .B Example \fB> uuids 0x1234\fP .TP .B Example \fB> uuids 0x12345678\fP .TP .B Example \fB> uuids 90f95193\-35de\-4306\-a6e9\-699328f15059\fP .UNINDENT .SS solicit .sp Set/Get advertise solicit uuids. :Usage: \fB# solicit [all/uuid1 uuid2 ...]\fP .SS service .sp Set/Get advertise service data. .INDENT 0.0 .TP .B Usage \fB> service [uuid] [data=xx xx ...]\fP .UNINDENT .SS manufacturer .sp Set/Get advertise manufacturer data. .sp Updating is in real time while advertising. This is currently limited to 25 bytes and will return an error message of \(dqToo much data\(dq if that maximum has been exceeded. However, this does not check if the advertising payload length maximum has been exceeded so you may receive an error from bluetoothd that it \(dqFailed to register advertisement\(dq which means you need to reduce your manufacturer data length. .INDENT 0.0 .TP .B Usage \fB> manufacturer [id] [data=xx xx ...]\fP .UNINDENT .SS data .sp Set/Get advertise data. .sp This allows you to advertise data with a given type. You cannot use a registered data type value {1} with this command. For LE the advertising shows up in the primary advertisements. .sp If you set only the type of the data without any data (data 0x0c) this will cause a parse error when turning advertise on. .sp You can modify the advertising data while it is advertising. .sp To get the currently set data use the command data without any arguments. .INDENT 0.0 .TP .B Usage \fB> data [type] [data=xx xx ...]\fP .TP .B Example \fB> data 0x0C 01 0x0F 13\fP .UNINDENT .SS sr\-uuids .sp Set/Get scan response uuids. .INDENT 0.0 .TP .B Usage \fB# sr\-uuids [all/uuid1 uuid2 ...]\fP .UNINDENT .SS sr\-solicit .sp Set/Get scan response solicit uuids. :Usage: \fB# sr\-solicit [all/uuid1 uuid2 ...]\fP .SS sr\-service .sp Set/Get scan response service data. .INDENT 0.0 .TP .B Usage \fB# sr\-service [uuid] [data=xx xx ...]\fP .UNINDENT .SS sr\-manufacturer .sp Set/Get scan response manufacturer data. .INDENT 0.0 .TP .B Usage \fB# sr\-manufacturer [id] [data=xx xx ...]\fP .UNINDENT .SS sr\-data .sp Set/Get scan response data. :Usage: \fB# sr\-data [type] [data=xx xx ...]\fP .SS discoverable .sp Set/Get advertise discoverable. .sp For LE discoverable on will set the LE General Discoverable Mode flag to true in the primary advertisement if on. .sp This feature can be changed during advertising, but will only trigger LE General Discoverable Mode even if you had previously selected discoverable\-timeout this will be ignored. .sp Entering the command by itself will show the status of the setting .INDENT 0.0 .TP .B Usage \fB> discoverable [on/off]\fP .UNINDENT .SS discoverable\-timeout .sp Set/Get advertise discoverable timeout. .sp Using this feature in LE will cause the LE Limited Discoverable Mode flag to be set in the primary advertisement and The LE General Discoverable Mode flag will not be set. .sp The LE Limited Discoverable Mode flag will automatically turn off after [seconds] discoverable [on] must be set to use this feature. .sp Entering the command by itself will show the current value set. .INDENT 0.0 .TP .B Usage \fB> discoverable\-timeout [seconds]\fP .UNINDENT .SS tx\-power .sp Show/Enable/Disable TX power to be advertised. .sp This sets the TX Power Level field in the advertising packet. .sp The value is in dBm and can be between \-127 and 127. .sp When this feature is turned on the LE device will advertise its transmit power in the primary advertisement. .sp This feature can be modified while advertising. .sp Entering the command by itself will show the current value set. .INDENT 0.0 .TP .B Usage \fB> tx\-power [on/off] [power]\fP .UNINDENT .SS name .sp Configure local name to be advertised. .sp Local name to be used in the advertising report. .sp If the string is too big to fit into the packet it will be truncated. .sp It will either advertise as a complete local name or if it has to be truncated then a shortened local name. .INDENT 0.0 .TP .B Usage \fB> name [on/off/name]\fP .TP .B Example \fB> name \(dq0123456789abcdef0123456789abcdef\(dq\fP .UNINDENT .SS appearance .sp Configure custom appearance to be advertised. .INDENT 0.0 .TP .B Usage \fB> appearance [on/off/value]\fP .UNINDENT .SS duration .sp Set/Get advertise duration. .sp The Duration parameter configures the length of an Instance. .sp The value is in seconds. .sp A value of 0 indicates a default value is chosen for the Duration. .sp The default is 2 seconds. .sp If only one advertising Instance has been added, then the Duration value will be ignored. .sp If multiple advertising Instances have been added, then the Duration value will be used to determine the length of time each Instance is advertised for. .sp The Duration value is used to calculate the number of advertising events that will be used to advertise each Instance. .sp The number of advertising events is calculated by dividing the Duration value by the advertising interval. .sp The advertising interval is determined by the advertising parameters that are set for each Instance. The advertising interval is the maximum of the advertising intervals set for each Instance. .INDENT 0.0 .TP .B Usage \fB> duration [seconds]\fP .UNINDENT .SS timeout .sp Set/Get advertise timeout. .INDENT 0.0 .TP .B Usage \fB> timeout [seconds]\fP .UNINDENT .SS secondary .sp Set/Get advertise secondary channel. .INDENT 0.0 .TP .B Usage \fB> secondary [1M/2M/Coded]\fP .UNINDENT .SS interval .sp Set/Get advertise interval. .sp The Interval parameter configures the advertising interval of an Instance. .sp The value is in milliseconds. .sp A value of 0 indicates a default value is chosen for the Interval. .sp The default is 100 milliseconds. .sp The Interval value is used to calculate the number of advertising events that will be used to advertise each Instance. .sp The number of advertising events is calculated by dividing the Duration value by the advertising interval. .sp The advertising interval is determined by the advertising parameters that are set for each Instance. .sp The advertising interval is the maximum of the advertising intervals set for each Instance. .INDENT 0.0 .TP .B Usage \fB> interval [milliseconds]\fP .UNINDENT .SS clear .sp Clear advertise config. .sp This will stop advertising if it is currently advertising. .sp If you want to change the advertise configuration while advertising you must first clear the advertise configuration and then set the new advertise configuration. .INDENT 0.0 .TP .B Usage \fB> clear [uuids/service/manufacturer/config\-name...]\fP .UNINDENT .SH RESOURCES .sp .SH REPORTING BUGS .sp .SH COPYRIGHT Free use of this software is granted under the terms of the GNU Lesser General Public Licenses (LGPL). .\" Generated by docutils manpage writer. . bluez-5.82/client/PaxHeaders/bluetoothctl-assistant.10000644000000000000000000000005014773213533017735 xustar0020 atime=1743591259 20 ctime=1743591291 bluez-5.82/client/bluetoothctl-assistant.10000644000000000000000000000505714773213533017425 0ustar00rootroot.\" Man page generated from reStructuredText. . . .nr rst2man-indent-level 0 . .de1 rstReportMargin \\$1 \\n[an-margin] level \\n[rst2man-indent-level] level margin: \\n[rst2man-indent\\n[rst2man-indent-level]] - \\n[rst2man-indent0] \\n[rst2man-indent1] \\n[rst2man-indent2] .. .de1 INDENT .\" .rstReportMargin pre: . RS \\$1 . nr rst2man-indent\\n[rst2man-indent-level] \\n[an-margin] . nr rst2man-indent-level +1 .\" .rstReportMargin post: .. .de UNINDENT . RE .\" indent \\n[an-margin] .\" old: \\n[rst2man-indent\\n[rst2man-indent-level]] .nr rst2man-indent-level -1 .\" new: \\n[rst2man-indent\\n[rst2man-indent-level]] .in \\n[rst2man-indent\\n[rst2man-indent-level]]u .. .TH "BLUETOOTHCTL-ASSISTANT" "1" "August 2024" "BlueZ" "Linux System Administration" .SH NAME bluetoothctl-assistant \- Assistant Submenu .SH SYNOPSIS .sp \fBbluetoothctl\fP [\-\-options] [assistant.commands] .SH ASSISTANT COMMANDS .SS push .sp Send stream information to peer. .sp This command is used by a BAP Broadcast Assistant to send information about a broadcast stream to a peer BAP Scan Delegator. .sp The information is sent via a GATT Write Command for the BASS Broadcast Audio Scan Control Point characteristic. .sp After issuing the command, the user is prompted to enter stream metadata LTVs to send to the peer. If the auto option is chosen, the Broadcast Assistant will send the default metadata discovered about the stream. Otherwise, the default metadata will be overwritten by the LTVs entered by the user. .sp If the stream is encrypted, the user will also be prompted to enter the Broadcast Code. This is the key to decrypt the stream. On the UI level, the Broadcast Code shall be represented as a string of at least 4 octets, and no more than 16 octets when represented in UTF\-8. The string will be sent to the peer via GATT as an array of 16 octets. .sp If the auto value is chosen when prompted for the Broadcast Code, a zero filled array will be sent to the peer. Otherwise, the string entered by the user will be sent as an array of bytes. .INDENT 0.0 .TP .B Usage \fB> push \fP .TP .B Example .nf \fB> push /org/bluez/hci0/src_05_1F_EE_F3_F8_7D/dev_00_60_37_31_7E_3F/bis1\fP \fB[Assistant] Enter Metadata (auto/value): 0x03 0x02 0x04 0x00\fP \fB[Assistant] Enter Broadcast Code (auto/value): Borne House\fP .fi .sp .UNINDENT .SH RESOURCES .sp .SH REPORTING BUGS .sp .SH COPYRIGHT Free use of this software is granted under the terms of the GNU Lesser General Public Licenses (LGPL). .\" Generated by docutils manpage writer. . bluez-5.82/client/PaxHeaders/display.h0000644000000000000000000000005014015011623014733 xustar0020 atime=1743515775 20 ctime=1743591281 bluez-5.82/client/display.h0000644000000000000000000000137614015011623014423 0ustar00rootroot/* SPDX-License-Identifier: GPL-2.0-or-later */ /* * * BlueZ - Bluetooth protocol stack for Linux * * Copyright (C) 2012 Intel Corporation. All rights reserved. * * */ #define COLOR_OFF "\x1B[0m" #define COLOR_RED "\x1B[0;91m" #define COLOR_GREEN "\x1B[0;92m" #define COLOR_YELLOW "\x1B[0;93m" #define COLOR_BLUE "\x1B[0;94m" #define COLOR_BOLDGRAY "\x1B[1;30m" #define COLOR_BOLDWHITE "\x1B[1;37m" void rl_printf(const char *fmt, ...) __attribute__((format(printf, 1, 2))); void rl_hexdump(const unsigned char *buf, size_t len); typedef void (*rl_prompt_input_func) (const char *input, void *user_data); void rl_prompt_input(const char *label, const char *msg, rl_prompt_input_func func, void *user_data); int rl_release_prompt(const char *input); bluez-5.82/client/PaxHeaders/advertising.c0000644000000000000000000000005014766002272015615 xustar0020 atime=1743515577 20 ctime=1743591281 bluez-5.82/client/advertising.c0000644000000000000000000010131014766002272015272 0ustar00rootroot// SPDX-License-Identifier: GPL-2.0-or-later /* * * BlueZ - Bluetooth protocol stack for Linux * * Copyright (C) 2016 Intel Corporation. All rights reserved. * * */ #ifdef HAVE_CONFIG_H #include #endif #define _GNU_SOURCE #include #include #include #include #include #include "gdbus/gdbus.h" #include "src/shared/util.h" #include "src/shared/shell.h" #include "advertising.h" #define AD_PATH "/org/bluez/advertising" #define AD_IFACE "org.bluez.LEAdvertisement1" struct ad_data { uint8_t data[245]; uint8_t len; }; struct service_data { char *uuid; struct ad_data data; }; struct manufacturer_data { uint16_t id; struct ad_data data; }; struct data { bool valid; uint8_t type; struct ad_data data; }; static struct ad { bool registered; char *type; char *local_name; char *secondary; uint32_t min_interval; uint32_t max_interval; uint16_t local_appearance; uint16_t duration; uint16_t timeout; uint16_t discoverable_to; char **uuids[AD_TYPE_COUNT]; size_t uuids_len[AD_TYPE_COUNT]; char **solicit[AD_TYPE_COUNT]; size_t solicit_len[AD_TYPE_COUNT]; struct service_data service[AD_TYPE_COUNT]; struct manufacturer_data manufacturer[AD_TYPE_COUNT]; struct data data[AD_TYPE_COUNT]; bool discoverable; bool tx_power; bool name; bool appearance; bool rsi; } ad = { .local_appearance = UINT16_MAX, .discoverable = true, .rsi = true, }; static void ad_release(DBusConnection *conn) { ad.registered = false; g_dbus_unregister_interface(conn, AD_PATH, AD_IFACE); } static DBusMessage *release_advertising(DBusConnection *conn, DBusMessage *msg, void *user_data) { bt_shell_printf("Advertising released\n"); ad_release(conn); return dbus_message_new_method_return(msg); } static const GDBusMethodTable ad_methods[] = { { GDBUS_METHOD("Release", NULL, NULL, release_advertising) }, { } }; static void register_setup(DBusMessageIter *iter, void *user_data) { DBusMessageIter dict; const char *path = AD_PATH; dbus_message_iter_append_basic(iter, DBUS_TYPE_OBJECT_PATH, &path); dbus_message_iter_open_container(iter, DBUS_TYPE_ARRAY, DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING DBUS_TYPE_STRING_AS_STRING DBUS_TYPE_VARIANT_AS_STRING DBUS_DICT_ENTRY_END_CHAR_AS_STRING, &dict); dbus_message_iter_close_container(iter, &dict); } static void print_uuid(const char *prefix, const char *uuid) { const char *text; text = bt_uuidstr_to_str(uuid); if (text) { char str[26]; unsigned int n; str[sizeof(str) - 1] = '\0'; n = snprintf(str, sizeof(str), "%s", text); if (n > sizeof(str) - 1) { str[sizeof(str) - 2] = '.'; str[sizeof(str) - 3] = '.'; if (str[sizeof(str) - 4] == ' ') str[sizeof(str) - 4] = '.'; } bt_shell_printf("%s: %s(%s)\n", prefix, str, uuid); } else bt_shell_printf("%s: (%s)\n", prefix, uuid ? uuid : ""); } static const struct { const char *uuid[AD_TYPE_COUNT]; const char *solicit[AD_TYPE_COUNT]; const char *service[AD_TYPE_COUNT]; const char *manufacturer[AD_TYPE_COUNT]; const char *data[AD_TYPE_COUNT]; } ad_names = { .uuid = { "UUID", "Scan Response UUID" }, .solicit = { "Solicit UUID", "Scan Response Solicit UUID" }, .service = { "UUID", "Scan Response UUID" }, .manufacturer = { "Manufacturer", "Scan Response Manufacturer" }, .data = { "Data", "Scan Response Data" } }; static void print_ad_uuids(int type) { char **uuid; for (uuid = ad.uuids[type]; uuid && *uuid; uuid++) print_uuid(ad_names.uuid[type], *uuid); } static void print_ad_solicit(int type) { char **uuid; for (uuid = ad.solicit[type]; uuid && *uuid; uuid++) print_uuid(ad_names.solicit[type], *uuid); } static void print_ad(void) { int type; for (type = AD_TYPE_AD; type <= AD_TYPE_SRD; type++) { print_ad_uuids(type); print_ad_solicit(type); if (ad.service[type].uuid) { print_uuid(ad_names.service[type], ad.service[type].uuid); bt_shell_hexdump(ad.service[type].data.data, ad.service[type].data.len); } if (ad.manufacturer[type].data.len) { bt_shell_printf("%s: %u\n", ad_names.manufacturer[type], ad.manufacturer[type].id); bt_shell_hexdump(ad.manufacturer[type].data.data, ad.manufacturer[type].data.len); } if (ad.data[type].valid) { bt_shell_printf("%s Type: 0x%02x\n", ad_names.data[type], ad.data[type].type); bt_shell_hexdump(ad.data[type].data.data, ad.data[type].data.len); } } bt_shell_printf("Tx Power: %s\n", ad.tx_power ? "on" : "off"); if (ad.local_name) bt_shell_printf("LocalName: %s\n", ad.local_name); else bt_shell_printf("Name: %s\n", ad.name ? "on" : "off"); if (ad.local_appearance != UINT16_MAX) bt_shell_printf("Appearance: %s (0x%04x)\n", bt_appear_to_str(ad.local_appearance), ad.local_appearance); else bt_shell_printf("Appearance: %s\n", ad.appearance ? "on" : "off"); bt_shell_printf("Discoverable: %s\n", ad.discoverable ? "on" : "off"); bt_shell_printf("RSI: %s\n", ad.rsi ? "on" : "off"); if (ad.duration) bt_shell_printf("Duration: %u sec\n", ad.duration); if (ad.timeout) bt_shell_printf("Timeout: %u sec\n", ad.timeout); if (ad.min_interval) bt_shell_printf("Interval: %u-%u msec\n", ad.min_interval, ad.max_interval); } static void register_reply(DBusMessage *message, void *user_data) { DBusConnection *conn = user_data; DBusError error; dbus_error_init(&error); if (dbus_set_error_from_message(&error, message) == FALSE) { ad.registered = true; bt_shell_printf("Advertising object registered\n"); print_ad(); /* Leave advertise running even on noninteractive mode */ } else { bt_shell_printf("Failed to register advertisement: %s\n", error.name); dbus_error_free(&error); if (g_dbus_unregister_interface(conn, AD_PATH, AD_IFACE) == FALSE) bt_shell_printf("Failed to unregister advertising object\n"); return bt_shell_noninteractive_quit(EXIT_FAILURE); } } static gboolean get_type(const GDBusPropertyTable *property, DBusMessageIter *iter, void *user_data) { const char *type = "peripheral"; if (ad.type && strlen(ad.type) > 0) type = ad.type; dbus_message_iter_append_basic(iter, DBUS_TYPE_STRING, &type); return TRUE; } static gboolean uuids_exists(int type, const GDBusPropertyTable *property, void *data) { return ad.uuids_len[type] != 0; } static gboolean get_uuids(int type, const GDBusPropertyTable *property, DBusMessageIter *iter, void *user_data) { DBusMessageIter array; size_t i; dbus_message_iter_open_container(iter, DBUS_TYPE_ARRAY, "as", &array); for (i = 0; i < ad.uuids_len[type]; i++) dbus_message_iter_append_basic(&array, DBUS_TYPE_STRING, &ad.uuids[type][i]); dbus_message_iter_close_container(iter, &array); return TRUE; } static gboolean ad_uuids_exists(const GDBusPropertyTable *property, void *data) { return uuids_exists(AD_TYPE_AD, property, data); } static gboolean get_ad_uuids(const GDBusPropertyTable *property, DBusMessageIter *iter, void *user_data) { return get_uuids(AD_TYPE_AD, property, iter, user_data); } static gboolean sr_uuids_exists(const GDBusPropertyTable *property, void *data) { return uuids_exists(AD_TYPE_SRD, property, data); } static gboolean get_sr_uuids(const GDBusPropertyTable *property, DBusMessageIter *iter, void *user_data) { return get_uuids(AD_TYPE_SRD, property, iter, user_data); } static gboolean solicit_uuids_exists(int type, const GDBusPropertyTable *property, void *data) { return ad.solicit_len[type] != 0; } static gboolean get_solicit_uuids(int type, const GDBusPropertyTable *property, DBusMessageIter *iter, void *user_data) { DBusMessageIter array; size_t i; dbus_message_iter_open_container(iter, DBUS_TYPE_ARRAY, "as", &array); for (i = 0; i < ad.solicit_len[type]; i++) dbus_message_iter_append_basic(&array, DBUS_TYPE_STRING, &ad.solicit[type][i]); dbus_message_iter_close_container(iter, &array); return TRUE; } static gboolean ad_solicit_uuids_exists(const GDBusPropertyTable *property, void *data) { return solicit_uuids_exists(AD_TYPE_AD, property, data); } static gboolean get_ad_solicit_uuids(const GDBusPropertyTable *property, DBusMessageIter *iter, void *user_data) { return get_solicit_uuids(AD_TYPE_AD, property, iter, user_data); } static gboolean sr_solicit_uuids_exists(const GDBusPropertyTable *property, void *data) { return solicit_uuids_exists(AD_TYPE_SRD, property, data); } static gboolean get_sr_solicit_uuids(const GDBusPropertyTable *property, DBusMessageIter *iter, void *user_data) { return get_solicit_uuids(AD_TYPE_SRD, property, iter, user_data); } static gboolean service_data_exists(int type, const GDBusPropertyTable *property, void *data) { return ad.service[type].uuid != NULL; } static gboolean get_service_data(int type, const GDBusPropertyTable *property, DBusMessageIter *iter, void *user_data) { DBusMessageIter dict; struct ad_data *data = &ad.service[type].data; uint8_t *val = data->data; dbus_message_iter_open_container(iter, DBUS_TYPE_ARRAY, "{sv}", &dict); g_dbus_dict_append_array(&dict, ad.service[type].uuid, DBUS_TYPE_BYTE, &val, data->len); dbus_message_iter_close_container(iter, &dict); return TRUE; } static gboolean ad_service_data_exists(const GDBusPropertyTable *property, void *data) { return service_data_exists(AD_TYPE_AD, property, data); } static gboolean get_ad_service_data(const GDBusPropertyTable *property, DBusMessageIter *iter, void *user_data) { return get_service_data(AD_TYPE_AD, property, iter, user_data); } static gboolean sr_service_data_exists(const GDBusPropertyTable *property, void *data) { return service_data_exists(AD_TYPE_SRD, property, data); } static gboolean get_sr_service_data(const GDBusPropertyTable *property, DBusMessageIter *iter, void *user_data) { return get_service_data(AD_TYPE_SRD, property, iter, user_data); } static gboolean manufacturer_data_exists(int type, const GDBusPropertyTable *property, void *data) { return ad.manufacturer[type].id != 0; } static gboolean get_manufacturer_data(int type, const GDBusPropertyTable *property, DBusMessageIter *iter, void *user_data) { DBusMessageIter dict; struct ad_data *data = &ad.manufacturer[type].data; uint8_t *val = data->data; dbus_message_iter_open_container(iter, DBUS_TYPE_ARRAY, "{qv}", &dict); g_dbus_dict_append_basic_array(&dict, DBUS_TYPE_UINT16, &ad.manufacturer[type].id, DBUS_TYPE_BYTE, &val, data->len); dbus_message_iter_close_container(iter, &dict); return TRUE; } static gboolean ad_manufacturer_data_exists(const GDBusPropertyTable *property, void *data) { return manufacturer_data_exists(AD_TYPE_AD, property, data); } static gboolean get_ad_manufacturer_data(const GDBusPropertyTable *property, DBusMessageIter *iter, void *user_data) { return get_manufacturer_data(AD_TYPE_AD, property, iter, user_data); } static gboolean sr_manufacturer_data_exists(const GDBusPropertyTable *property, void *data) { return manufacturer_data_exists(AD_TYPE_SRD, property, data); } static gboolean get_sr_manufacturer_data(const GDBusPropertyTable *property, DBusMessageIter *iter, void *user_data) { return get_manufacturer_data(AD_TYPE_SRD, property, iter, user_data); } static gboolean includes_exists(const GDBusPropertyTable *property, void *data) { return ad.tx_power || ad.name || ad.appearance || ad.rsi; } static gboolean get_includes(const GDBusPropertyTable *property, DBusMessageIter *iter, void *user_data) { DBusMessageIter array; dbus_message_iter_open_container(iter, DBUS_TYPE_ARRAY, "as", &array); if (ad.tx_power) { const char *str = "tx-power"; dbus_message_iter_append_basic(&array, DBUS_TYPE_STRING, &str); } if (ad.name) { const char *str = "local-name"; dbus_message_iter_append_basic(&array, DBUS_TYPE_STRING, &str); } if (ad.appearance) { const char *str = "appearance"; dbus_message_iter_append_basic(&array, DBUS_TYPE_STRING, &str); } if (ad.rsi) { const char *str = "rsi"; dbus_message_iter_append_basic(&array, DBUS_TYPE_STRING, &str); } dbus_message_iter_close_container(iter, &array); return TRUE; } static gboolean local_name_exists(const GDBusPropertyTable *property, void *data) { return ad.local_name ? TRUE : FALSE; } static gboolean get_local_name(const GDBusPropertyTable *property, DBusMessageIter *iter, void *user_data) { dbus_message_iter_append_basic(iter, DBUS_TYPE_STRING, &ad.local_name); return TRUE; } static gboolean appearance_exists(const GDBusPropertyTable *property, void *data) { return ad.local_appearance != UINT16_MAX ? TRUE : FALSE; } static gboolean get_appearance(const GDBusPropertyTable *property, DBusMessageIter *iter, void *user_data) { dbus_message_iter_append_basic(iter, DBUS_TYPE_UINT16, &ad.local_appearance); return TRUE; } static gboolean duration_exists(const GDBusPropertyTable *property, void *data) { return ad.duration; } static gboolean get_duration(const GDBusPropertyTable *property, DBusMessageIter *iter, void *user_data) { dbus_message_iter_append_basic(iter, DBUS_TYPE_UINT16, &ad.duration); return TRUE; } static gboolean timeout_exists(const GDBusPropertyTable *property, void *data) { return ad.timeout; } static gboolean get_timeout(const GDBusPropertyTable *property, DBusMessageIter *iter, void *user_data) { dbus_message_iter_append_basic(iter, DBUS_TYPE_UINT16, &ad.timeout); return TRUE; } static gboolean data_exists(int type, const GDBusPropertyTable *property, void *data) { return ad.data[type].valid; } static gboolean get_data(int type, const GDBusPropertyTable *property, DBusMessageIter *iter, void *user_data) { DBusMessageIter dict; struct ad_data *data = &ad.data[type].data; uint8_t *val = data->data; dbus_message_iter_open_container(iter, DBUS_TYPE_ARRAY, "{yv}", &dict); g_dbus_dict_append_basic_array(&dict, DBUS_TYPE_BYTE, &ad.data[type].type, DBUS_TYPE_BYTE, &val, data->len); dbus_message_iter_close_container(iter, &dict); return TRUE; } static gboolean ad_data_exists(const GDBusPropertyTable *property, void *data) { return data_exists(AD_TYPE_AD, property, data); } static gboolean get_ad_data(const GDBusPropertyTable *property, DBusMessageIter *iter, void *user_data) { return get_data(AD_TYPE_AD, property, iter, user_data); } static gboolean sr_data_exists(const GDBusPropertyTable *property, void *data) { return data_exists(AD_TYPE_SRD, property, data); } static gboolean get_sr_data(const GDBusPropertyTable *property, DBusMessageIter *iter, void *user_data) { return get_data(AD_TYPE_SRD, property, iter, user_data); } static gboolean get_discoverable(const GDBusPropertyTable *property, DBusMessageIter *iter, void *user_data) { dbus_bool_t value = ad.discoverable; dbus_message_iter_append_basic(iter, DBUS_TYPE_BOOLEAN, &value); return TRUE; } static gboolean discoverable_timeout_exists(const GDBusPropertyTable *property, void *data) { return ad.discoverable_to; } static gboolean get_discoverable_timeout(const GDBusPropertyTable *property, DBusMessageIter *iter, void *user_data) { dbus_message_iter_append_basic(iter, DBUS_TYPE_UINT16, &ad.discoverable_to); return TRUE; } static gboolean secondary_exists(const GDBusPropertyTable *property, void *data) { return ad.secondary ? TRUE : FALSE; } static gboolean get_secondary(const GDBusPropertyTable *property, DBusMessageIter *iter, void *user_data) { dbus_message_iter_append_basic(iter, DBUS_TYPE_STRING, &ad.secondary); return TRUE; } static gboolean min_interval_exists(const GDBusPropertyTable *property, void *data) { return ad.min_interval; } static gboolean get_min_interval(const GDBusPropertyTable *property, DBusMessageIter *iter, void *user_data) { dbus_message_iter_append_basic(iter, DBUS_TYPE_UINT32, &ad.min_interval); return TRUE; } static gboolean max_interval_exists(const GDBusPropertyTable *property, void *data) { return ad.max_interval; } static gboolean get_max_interval(const GDBusPropertyTable *property, DBusMessageIter *iter, void *user_data) { dbus_message_iter_append_basic(iter, DBUS_TYPE_UINT32, &ad.max_interval); return TRUE; } static const GDBusPropertyTable ad_props[] = { { "Type", "s", get_type }, { "ServiceUUIDs", "as", get_ad_uuids, NULL, ad_uuids_exists }, { "SolicitUUIDs", "as", get_ad_solicit_uuids, NULL, ad_solicit_uuids_exists }, { "ServiceData", "a{sv}", get_ad_service_data, NULL, ad_service_data_exists }, { "ManufacturerData", "a{qv}", get_ad_manufacturer_data, NULL, ad_manufacturer_data_exists }, { "Data", "a{yv}", get_ad_data, NULL, ad_data_exists }, { "ScanResponseServiceUUIDs", "as", get_sr_uuids, NULL, sr_uuids_exists }, { "ScanResponseSolicitUUIDs", "as", get_sr_solicit_uuids, NULL, sr_solicit_uuids_exists }, { "ScanResponseServiceData", "a{sv}", get_sr_service_data, NULL, sr_service_data_exists }, { "ScanResponseManufacturerData", "a{qv}", get_sr_manufacturer_data, NULL, sr_manufacturer_data_exists }, { "ScanResponseData", "a{yv}", get_sr_data, NULL, sr_data_exists }, { "Discoverable", "b", get_discoverable, NULL, NULL }, { "DiscoverableTimeout", "q", get_discoverable_timeout, NULL, discoverable_timeout_exists }, { "Includes", "as", get_includes, NULL, includes_exists }, { "LocalName", "s", get_local_name, NULL, local_name_exists }, { "Appearance", "q", get_appearance, NULL, appearance_exists }, { "Duration", "q", get_duration, NULL, duration_exists }, { "Timeout", "q", get_timeout, NULL, timeout_exists }, { "MinInterval", "u", get_min_interval, NULL, min_interval_exists }, { "MaxInterval", "u", get_max_interval, NULL, max_interval_exists }, { "SecondaryChannel", "s", get_secondary, NULL, secondary_exists }, { } }; void ad_register(DBusConnection *conn, GDBusProxy *manager, const char *type) { if (ad.registered) { bt_shell_printf("Advertisement is already registered\n"); return bt_shell_noninteractive_quit(EXIT_FAILURE); } g_free(ad.type); ad.type = g_strdup(type); if (!strcasecmp(ad.type, "Broadcast")) ad.discoverable = false; if (g_dbus_register_interface(conn, AD_PATH, AD_IFACE, ad_methods, NULL, ad_props, NULL, NULL) == FALSE) { bt_shell_printf("Failed to register advertising object\n"); return bt_shell_noninteractive_quit(EXIT_FAILURE); } if (g_dbus_proxy_method_call(manager, "RegisterAdvertisement", register_setup, register_reply, conn, NULL) == FALSE) { bt_shell_printf("Failed to register advertising\n"); return bt_shell_noninteractive_quit(EXIT_FAILURE); } } static void unregister_setup(DBusMessageIter *iter, void *user_data) { const char *path = AD_PATH; dbus_message_iter_append_basic(iter, DBUS_TYPE_OBJECT_PATH, &path); } static void unregister_reply(DBusMessage *message, void *user_data) { DBusConnection *conn = user_data; DBusError error; dbus_error_init(&error); if (dbus_set_error_from_message(&error, message) == FALSE) { ad.registered = false; bt_shell_printf("Advertising object unregistered\n"); if (g_dbus_unregister_interface(conn, AD_PATH, AD_IFACE) == FALSE) bt_shell_printf("Failed to unregister advertising" " object\n"); return bt_shell_noninteractive_quit(EXIT_SUCCESS); } else { bt_shell_printf("Failed to unregister advertisement: %s\n", error.name); dbus_error_free(&error); return bt_shell_noninteractive_quit(EXIT_FAILURE); } } void ad_unregister(DBusConnection *conn, GDBusProxy *manager) { if (!manager) ad_release(conn); if (!ad.registered) return bt_shell_noninteractive_quit(EXIT_SUCCESS); g_free(ad.type); ad.type = NULL; if (g_dbus_proxy_method_call(manager, "UnregisterAdvertisement", unregister_setup, unregister_reply, conn, NULL) == FALSE) { bt_shell_printf("Failed to unregister advertisement method\n"); return bt_shell_noninteractive_quit(EXIT_FAILURE); } } static const struct { const char *uuid[AD_TYPE_COUNT]; const char *solicit[AD_TYPE_COUNT]; const char *service[AD_TYPE_COUNT]; const char *manufacturer[AD_TYPE_COUNT]; const char *data[AD_TYPE_COUNT]; } prop_names = { .uuid = { "ServiceUUIDs", "ScanResponseServiceUUIDs" }, .solicit = { "SolicitUUIDs", "ScanResponseSolicitUUIDs" }, .service = { "ServiceData", "ScanResponseServiceData" }, .manufacturer = { "ManufacturerData", "ScanResponseManufacturerData" }, .data = { "Data", "ScanResponseData" } }; static void ad_clear_uuids(int type) { g_strfreev(ad.uuids[type]); ad.uuids[type] = NULL; ad.uuids_len[type] = 0; } void ad_advertise_uuids(DBusConnection *conn, int type, int argc, char *argv[]) { if (argc < 2 || !strlen(argv[1])) { print_ad_uuids(type); return bt_shell_noninteractive_quit(EXIT_SUCCESS); } ad_clear_uuids(type); ad.uuids[type] = g_strdupv(&argv[1]); if (!ad.uuids[type]) { bt_shell_printf("Failed to parse input\n"); return bt_shell_noninteractive_quit(EXIT_FAILURE); } ad.uuids_len[type] = g_strv_length(ad.uuids[type]); g_dbus_emit_property_changed(conn, AD_PATH, AD_IFACE, prop_names.uuid[type]); return bt_shell_noninteractive_quit(EXIT_SUCCESS); } void ad_disable_uuids(DBusConnection *conn, int type) { if (!ad.uuids[type]) return bt_shell_noninteractive_quit(EXIT_SUCCESS); ad_clear_uuids(type); g_dbus_emit_property_changed(conn, AD_PATH, AD_IFACE, prop_names.uuid[type]); return bt_shell_noninteractive_quit(EXIT_SUCCESS); } static void ad_clear_solicit(int type) { g_strfreev(ad.solicit[type]); ad.solicit[type] = NULL; ad.solicit_len[type] = 0; } void ad_advertise_solicit(DBusConnection *conn, int type, int argc, char *argv[]) { if (argc < 2 || !strlen(argv[1])) { print_ad_solicit(type); return bt_shell_noninteractive_quit(EXIT_SUCCESS); } ad_clear_solicit(type); ad.solicit[type] = g_strdupv(&argv[1]); if (!ad.solicit[type]) { bt_shell_printf("Failed to parse input\n"); return bt_shell_noninteractive_quit(EXIT_FAILURE); } ad.solicit_len[type] = g_strv_length(ad.solicit[type]); g_dbus_emit_property_changed(conn, AD_PATH, AD_IFACE, prop_names.solicit[type]); return bt_shell_noninteractive_quit(EXIT_SUCCESS); } void ad_disable_solicit(DBusConnection *conn, int type) { if (!ad.solicit[type]) return bt_shell_noninteractive_quit(EXIT_SUCCESS); ad_clear_solicit(type); g_dbus_emit_property_changed(conn, AD_PATH, AD_IFACE, prop_names.solicit[type]); return bt_shell_noninteractive_quit(EXIT_SUCCESS); } static void ad_clear_service(int type) { g_free(ad.service[type].uuid); memset(&ad.service[type], 0, sizeof(ad.service[type])); return bt_shell_noninteractive_quit(EXIT_SUCCESS); } static bool ad_add_data(struct ad_data *data, int argc, char *argv[]) { unsigned int i; memset(data, 0, sizeof(*data)); for (i = 0; i < (unsigned int) argc; i++) { long int val; char *endptr = NULL; if (i >= G_N_ELEMENTS(data->data)) { bt_shell_printf("Too much data\n"); return false; } val = strtol(argv[i], &endptr, 0); if (!endptr || *endptr != '\0' || val > UINT8_MAX) { bt_shell_printf("Invalid value at index %d\n", i); return false; } data->data[data->len] = val; data->len++; } return true; } void ad_advertise_service(DBusConnection *conn, int type, int argc, char *argv[]) { struct ad_data data; if (argc < 2 || !strlen(argv[1])) { if (ad.service[type].uuid) { print_uuid(ad_names.service[type], ad.service[type].uuid); bt_shell_hexdump(ad.service[type].data.data, ad.service[type].data.len); } return bt_shell_noninteractive_quit(EXIT_SUCCESS); } if (!ad_add_data(&data, argc - 2, argv + 2)) return bt_shell_noninteractive_quit(EXIT_FAILURE); ad_clear_service(type); ad.service[type].uuid = g_strdup(argv[1]); ad.service[type].data = data; g_dbus_emit_property_changed(conn, AD_PATH, AD_IFACE, prop_names.service[type]); return bt_shell_noninteractive_quit(EXIT_SUCCESS); } void ad_disable_service(DBusConnection *conn, int type) { if (!ad.service[type].uuid) return bt_shell_noninteractive_quit(EXIT_SUCCESS); ad_clear_service(type); g_dbus_emit_property_changed(conn, AD_PATH, AD_IFACE, prop_names.service[type]); return bt_shell_noninteractive_quit(EXIT_SUCCESS); } static void ad_clear_manufacturer(int type) { memset(&ad.manufacturer[type], 0, sizeof(ad.manufacturer[type])); return bt_shell_noninteractive_quit(EXIT_SUCCESS); } void ad_advertise_manufacturer(DBusConnection *conn, int type, int argc, char *argv[]) { char *endptr = NULL; long int val; struct ad_data data; if (argc < 2 || !strlen(argv[1])) { if (ad.manufacturer[type].data.len) { bt_shell_printf("%s: %u\n", ad_names.manufacturer[type], ad.manufacturer[type].id); bt_shell_hexdump(ad.manufacturer[type].data.data, ad.manufacturer[type].data.len); } return bt_shell_noninteractive_quit(EXIT_SUCCESS); } val = strtol(argv[1], &endptr, 0); if (!endptr || *endptr != '\0' || val > UINT16_MAX) { bt_shell_printf("Invalid manufacture id\n"); return bt_shell_noninteractive_quit(EXIT_FAILURE); } if (!ad_add_data(&data, argc - 2, argv + 2)) return bt_shell_noninteractive_quit(EXIT_FAILURE); ad_clear_manufacturer(type); ad.manufacturer[type].id = val; ad.manufacturer[type].data = data; g_dbus_emit_property_changed(conn, AD_PATH, AD_IFACE, prop_names.manufacturer[type]); return bt_shell_noninteractive_quit(EXIT_SUCCESS); } void ad_disable_manufacturer(DBusConnection *conn, int type) { if (!ad.manufacturer[type].id && !ad.manufacturer[type].data.len) return bt_shell_noninteractive_quit(EXIT_SUCCESS); ad_clear_manufacturer(type); g_dbus_emit_property_changed(conn, AD_PATH, AD_IFACE, prop_names.manufacturer[type]); return bt_shell_noninteractive_quit(EXIT_SUCCESS); } static void ad_clear_data(int type) { memset(&ad.data[type], 0, sizeof(ad.data[type])); return bt_shell_noninteractive_quit(EXIT_SUCCESS); } void ad_advertise_data(DBusConnection *conn, int type, int argc, char *argv[]) { char *endptr = NULL; long int val; struct ad_data data; if (argc < 2 || !strlen(argv[1])) { if (ad.data[type].data.len) { bt_shell_printf("%s Type: 0x%02x\n", ad_names.data[type], ad.data[type].type); bt_shell_hexdump(ad.data[type].data.data, ad.data[type].data.len); } return bt_shell_noninteractive_quit(EXIT_SUCCESS); } val = strtol(argv[1], &endptr, 0); if (!endptr || *endptr != '\0' || val > UINT8_MAX) { bt_shell_printf("Invalid type\n"); return bt_shell_noninteractive_quit(EXIT_FAILURE); } if (!ad_add_data(&data, argc - 2, argv + 2)) return bt_shell_noninteractive_quit(EXIT_FAILURE); ad_clear_data(type); ad.data[type].valid = true; ad.data[type].type = val; ad.data[type].data = data; g_dbus_emit_property_changed(conn, AD_PATH, AD_IFACE, prop_names.data[type]); return bt_shell_noninteractive_quit(EXIT_SUCCESS); } void ad_disable_data(DBusConnection *conn, int type) { if (!ad.data[type].type && !ad.data[type].data.len) return bt_shell_noninteractive_quit(EXIT_SUCCESS); ad_clear_data(type); g_dbus_emit_property_changed(conn, AD_PATH, AD_IFACE, prop_names.data[type]); return bt_shell_noninteractive_quit(EXIT_SUCCESS); } void ad_advertise_discoverable(DBusConnection *conn, dbus_bool_t *value) { if (!value) { bt_shell_printf("Discoverable: %s\n", ad.discoverable ? "on" : "off"); return bt_shell_noninteractive_quit(EXIT_SUCCESS); } if (ad.discoverable == *value) return bt_shell_noninteractive_quit(EXIT_SUCCESS); ad.discoverable = *value; g_dbus_emit_property_changed(conn, AD_PATH, AD_IFACE, "Discoverable"); return bt_shell_noninteractive_quit(EXIT_SUCCESS); } void ad_advertise_discoverable_timeout(DBusConnection *conn, long int *value) { if (!value) { if (ad.discoverable_to) bt_shell_printf("Timeout: %u sec\n", ad.discoverable_to); return bt_shell_noninteractive_quit(EXIT_SUCCESS); } if (ad.discoverable_to == *value) return bt_shell_noninteractive_quit(EXIT_SUCCESS); ad.discoverable_to = *value; g_dbus_emit_property_changed(conn, AD_PATH, AD_IFACE, "DiscoverableTimeout"); return bt_shell_noninteractive_quit(EXIT_SUCCESS); } void ad_advertise_tx_power(DBusConnection *conn, dbus_bool_t *value) { if (!value) { bt_shell_printf("Tx Power: %s\n", ad.tx_power ? "on" : "off"); return bt_shell_noninteractive_quit(EXIT_SUCCESS); } if (ad.tx_power == *value) return bt_shell_noninteractive_quit(EXIT_SUCCESS); ad.tx_power = *value; g_dbus_emit_property_changed(conn, AD_PATH, AD_IFACE, "Includes"); return bt_shell_noninteractive_quit(EXIT_SUCCESS); } void ad_advertise_name(DBusConnection *conn, bool value) { if (ad.name == value) return bt_shell_noninteractive_quit(EXIT_SUCCESS); ad.name = value; if (!value) { free(ad.local_name); ad.local_name = NULL; } g_dbus_emit_property_changed(conn, AD_PATH, AD_IFACE, "Includes"); return bt_shell_noninteractive_quit(EXIT_SUCCESS); } void ad_advertise_local_name(DBusConnection *conn, const char *name) { if (!name) { if (ad.local_name) bt_shell_printf("LocalName: %s\n", ad.local_name); else bt_shell_printf("Name: %s\n", ad.name ? "on" : "off"); return bt_shell_noninteractive_quit(EXIT_SUCCESS); } if (ad.local_name && !strcmp(name, ad.local_name)) return; g_free(ad.local_name); ad.local_name = strdup(name); g_dbus_emit_property_changed(conn, AD_PATH, AD_IFACE, "LocalName"); /* Remove local-name from Includes since LocalName would be set */ if (ad.name) { ad.name = false; g_dbus_emit_property_changed(conn, AD_PATH, AD_IFACE, "Includes"); } return bt_shell_noninteractive_quit(EXIT_SUCCESS); } void ad_advertise_appearance(DBusConnection *conn, bool value) { if (ad.appearance == value) return bt_shell_noninteractive_quit(EXIT_SUCCESS); ad.appearance = value; if (!value) ad.local_appearance = UINT16_MAX; g_dbus_emit_property_changed(conn, AD_PATH, AD_IFACE, "Includes"); return bt_shell_noninteractive_quit(EXIT_SUCCESS); } void ad_advertise_local_appearance(DBusConnection *conn, long int *value) { if (!value) { if (ad.local_appearance != UINT16_MAX) bt_shell_printf("Appearance: %s (0x%04x)\n", bt_appear_to_str(ad.local_appearance), ad.local_appearance); else bt_shell_printf("Appearance: %s\n", ad.appearance ? "on" : "off"); return bt_shell_noninteractive_quit(EXIT_SUCCESS); } if (ad.local_appearance == *value) return bt_shell_noninteractive_quit(EXIT_SUCCESS); ad.local_appearance = *value; g_dbus_emit_property_changed(conn, AD_PATH, AD_IFACE, "Appearance"); return bt_shell_noninteractive_quit(EXIT_SUCCESS); } void ad_advertise_duration(DBusConnection *conn, long int *value) { if (!value) { if (ad.duration) bt_shell_printf("Duration: %u sec\n", ad.duration); return bt_shell_noninteractive_quit(EXIT_SUCCESS); } if (ad.duration == *value) return bt_shell_noninteractive_quit(EXIT_SUCCESS); ad.duration = *value; g_dbus_emit_property_changed(conn, AD_PATH, AD_IFACE, "Duration"); return bt_shell_noninteractive_quit(EXIT_SUCCESS); } void ad_advertise_timeout(DBusConnection *conn, long int *value) { if (!value) { if (ad.timeout) bt_shell_printf("Timeout: %u sec\n", ad.timeout); return bt_shell_noninteractive_quit(EXIT_SUCCESS); } if (ad.timeout == *value) return bt_shell_noninteractive_quit(EXIT_SUCCESS); ad.timeout = *value; g_dbus_emit_property_changed(conn, AD_PATH, AD_IFACE, "Timeout"); return bt_shell_noninteractive_quit(EXIT_SUCCESS); } void ad_advertise_secondary(DBusConnection *conn, const char *value) { if (!value) { if (ad.secondary) bt_shell_printf("Secondary Channel: %s\n", ad.secondary); return bt_shell_noninteractive_quit(EXIT_SUCCESS); } if (ad.secondary && !strcmp(value, ad.secondary)) return bt_shell_noninteractive_quit(EXIT_SUCCESS); free(ad.secondary); if (value[0] == '\0') { ad.secondary = NULL; return bt_shell_noninteractive_quit(EXIT_SUCCESS); } ad.secondary = strdup(value); g_dbus_emit_property_changed(conn, AD_PATH, AD_IFACE, "SecondaryChannel"); return bt_shell_noninteractive_quit(EXIT_SUCCESS); } void ad_advertise_interval(DBusConnection *conn, uint32_t *min, uint32_t *max) { if (!min && !max) { if (ad.min_interval && ad.max_interval) bt_shell_printf("Interval: %u-%u msec\n", ad.min_interval, ad.max_interval); return bt_shell_noninteractive_quit(EXIT_SUCCESS); } if (min && ad.min_interval != *min) { ad.min_interval = *min; g_dbus_emit_property_changed(conn, AD_PATH, AD_IFACE, "MinInterval"); } if (max && ad.max_interval != *max) { ad.max_interval = *max; g_dbus_emit_property_changed(conn, AD_PATH, AD_IFACE, "MaxInterval"); } return bt_shell_noninteractive_quit(EXIT_SUCCESS); } void ad_advertise_rsi(DBusConnection *conn, dbus_bool_t *value) { if (!value) { bt_shell_printf("RSI: %s\n", ad.rsi ? "on" : "off"); return bt_shell_noninteractive_quit(EXIT_SUCCESS); } if (ad.rsi == *value) return bt_shell_noninteractive_quit(EXIT_SUCCESS); ad.rsi = *value; g_dbus_emit_property_changed(conn, AD_PATH, AD_IFACE, "Includes"); return bt_shell_noninteractive_quit(EXIT_SUCCESS); } bluez-5.82/client/PaxHeaders/admin.c0000644000000000000000000000005014766002272014366 xustar0020 atime=1743515577 20 ctime=1743591281 bluez-5.82/client/admin.c0000644000000000000000000001165114766002272014053 0ustar00rootroot// SPDX-License-Identifier: GPL-2.0-or-later /* * * BlueZ - Bluetooth protocol stack for Linux * * Copyright (C) 2021 Google LLC * * */ #ifdef HAVE_CONFIG_H #include #endif #include #include #include #include "gdbus/gdbus.h" #include "src/shared/shell.h" #include "admin.h" #define _GNU_SOURCE static DBusConnection *dbus_conn; static GList *admin_proxies; static GDBusProxy *set_proxy; static GDBusProxy *status_proxy; static void admin_policy_set_set_proxy(GDBusProxy *proxy) { set_proxy = proxy; } static void admin_policy_set_status_proxy(GDBusProxy *proxy) { status_proxy = proxy; } static void admin_policy_read_service_allowlist(DBusConnection *dbus_conn) { DBusMessageIter iter, subiter; char *uuid = NULL; if (!status_proxy || !g_dbus_proxy_get_property(status_proxy, "ServiceAllowList", &iter)) { bt_shell_printf("Failed to get property\n"); return bt_shell_noninteractive_quit(EXIT_FAILURE); } if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_ARRAY) { bt_shell_printf("Unexpected return type\n"); return bt_shell_noninteractive_quit(EXIT_FAILURE); } bt_shell_printf("Service AllowedList:\n"); dbus_message_iter_recurse(&iter, &subiter); while (dbus_message_iter_get_arg_type(&subiter) == DBUS_TYPE_STRING) { dbus_message_iter_get_basic(&subiter, &uuid); bt_shell_printf("\t%s\n", uuid); dbus_message_iter_next(&subiter); } return bt_shell_noninteractive_quit(EXIT_SUCCESS); } struct uuid_list_data { char **uuid_list; size_t num; }; static void set_service_setup(DBusMessageIter *iter, void *user_data) { struct uuid_list_data *data = user_data; DBusMessageIter arr_iter; size_t i; dbus_message_iter_open_container(iter, DBUS_TYPE_ARRAY, DBUS_TYPE_STRING_AS_STRING, &arr_iter); for (i = 0; i < data->num; i++) { dbus_message_iter_append_basic(&arr_iter, DBUS_TYPE_STRING, &data->uuid_list[i]); } dbus_message_iter_close_container(iter, &arr_iter); } static void set_service_reply(DBusMessage *message, void *user_data) { DBusError error; dbus_error_init(&error); if (!dbus_set_error_from_message(&error, message)) { bt_shell_printf("Set allowed service successfully\n"); return bt_shell_noninteractive_quit(EXIT_SUCCESS); } bt_shell_printf("Failed to set service allowed list: %s\n", error.name); dbus_error_free(&error); return bt_shell_noninteractive_quit(EXIT_FAILURE); } static void admin_policy_set_service_allowlist(int argc, char *argv[]) { struct uuid_list_data data; if (!set_proxy) { bt_shell_printf("Set proxy not ready\n"); return bt_shell_noninteractive_quit(EXIT_FAILURE); } data.uuid_list = argv; data.num = argc; if (!g_dbus_proxy_method_call(set_proxy, "SetServiceAllowList", set_service_setup, set_service_reply, &data, NULL)) { bt_shell_printf("Failed to call method\n"); return bt_shell_noninteractive_quit(EXIT_FAILURE); } } static void cmd_admin_allow(int argc, char *argv[]) { if (argc <= 1) { admin_policy_read_service_allowlist(dbus_conn); return; } if (strcmp(argv[1], "clear") == 0) argc--; admin_policy_set_service_allowlist(argc - 1, argv + 1); } static const struct bt_shell_menu admin_menu = { .name = "admin", .desc = "Admin Policy Submenu", .entries = { { "allow", "[clear/uuid1 uuid2 ...]", cmd_admin_allow, "Allow service UUIDs and block rest of them"}, {} }, }; static void admin_policy_status_added(GDBusProxy *proxy) { admin_proxies = g_list_append(admin_proxies, proxy); admin_policy_set_status_proxy(proxy); } static void proxy_added(GDBusProxy *proxy, void *user_data) { const char *interface; interface = g_dbus_proxy_get_interface(proxy); if (!strcmp(interface, "org.bluez.AdminPolicySet1")) admin_policy_set_set_proxy(proxy); else if (!strcmp(interface, "org.bluez.AdminPolicyStatus1")) admin_policy_status_added(proxy); } static void admin_policy_status_removed(GDBusProxy *proxy) { admin_proxies = g_list_remove(admin_proxies, proxy); admin_policy_set_status_proxy(NULL); } static void proxy_removed(GDBusProxy *proxy, void *user_data) { const char *interface; interface = g_dbus_proxy_get_interface(proxy); if (!strcmp(interface, "org.bluez.AdminPolicySet1")) admin_policy_set_set_proxy(NULL); else if (!strcmp(interface, "org.bluez.AdminPolicyStatus1")) admin_policy_status_removed(proxy); } static GDBusClient *client; static void disconnect_handler(DBusConnection *connection, void *user_data) { g_list_free_full(admin_proxies, NULL); admin_proxies = NULL; } void admin_add_submenu(void) { bt_shell_add_submenu(&admin_menu); dbus_conn = bt_shell_get_env("DBUS_CONNECTION"); if (!dbus_conn || client) return; client = g_dbus_client_new(dbus_conn, "org.bluez", "/org/bluez"); g_dbus_client_set_proxy_handlers(client, proxy_added, proxy_removed, NULL, NULL); g_dbus_client_set_disconnect_watch(client, disconnect_handler, NULL); } void admin_remove_submenu(void) { g_dbus_client_unref(client); client = NULL; } bluez-5.82/client/PaxHeaders/print.h0000644000000000000000000000005014711225433014433 xustar0020 atime=1743515768 20 ctime=1743591281 bluez-5.82/client/print.h0000644000000000000000000000073314711225433014117 0ustar00rootroot// SPDX-License-Identifier: GPL-2.0-or-later /* * * BlueZ - Bluetooth protocol stack for Linux * * Copyright (C) 2022 Intel Corporation. All rights reserved. * * */ void print_property(GDBusProxy *proxy, const char *name); void print_property_with_label(GDBusProxy *proxy, const char *name, const char *label); void print_iter(const char *label, const char *name, DBusMessageIter *iter); void print_uuid(const char *label, const char *name, const char *uuid); bluez-5.82/PaxHeaders/gobex0000644000000000000000000000005014773213563012710 xustar0020 atime=1743591291 20 ctime=1743591283 bluez-5.82/gobex/0000755000000000000000000000000014773213563012446 5ustar00rootrootbluez-5.82/gobex/PaxHeaders/gobex-packet.c0000644000000000000000000000005014214376354015500 xustar0020 atime=1743516289 20 ctime=1743591283 bluez-5.82/gobex/gobex-packet.c0000644000000000000000000002240714214376354015166 0ustar00rootroot// SPDX-License-Identifier: GPL-2.0-or-later /* * * OBEX library with GLib integration * * Copyright (C) 2011 Intel Corporation. All rights reserved. * */ #ifdef HAVE_CONFIG_H #include #endif #include #include #include "gobex-defs.h" #include "gobex-packet.h" #include "gobex-debug.h" #include "src/shared/util.h" #define FINAL_BIT 0x80 struct _GObexPacket { guint8 opcode; gboolean final; GObexDataPolicy data_policy; union { void *buf; /* Non-header data */ const void *buf_ref; /* Reference to non-header data */ } data; gsize data_len; gsize hlen; /* Length of all encoded headers */ GSList *headers; GObexDataProducer get_body; gpointer get_body_data; }; GObexHeader *g_obex_packet_get_header(GObexPacket *pkt, guint8 id) { GSList *l; g_obex_debug(G_OBEX_DEBUG_PACKET, "opcode 0x%02x", pkt->opcode); for (l = pkt->headers; l != NULL; l = g_slist_next(l)) { GObexHeader *hdr = l->data; if (g_obex_header_get_id(hdr) == id) return hdr; } return NULL; } GObexHeader *g_obex_packet_get_body(GObexPacket *pkt) { GObexHeader *body; g_obex_debug(G_OBEX_DEBUG_PACKET, "opcode 0x%02x", pkt->opcode); body = g_obex_packet_get_header(pkt, G_OBEX_HDR_BODY); if (body != NULL) return body; return g_obex_packet_get_header(pkt, G_OBEX_HDR_BODY_END); } guint8 g_obex_packet_get_operation(GObexPacket *pkt, gboolean *final) { g_obex_debug(G_OBEX_DEBUG_PACKET, "opcode 0x%02x", pkt->opcode); if (final) *final = pkt->final; return pkt->opcode; } gboolean g_obex_packet_prepend_header(GObexPacket *pkt, GObexHeader *header) { g_obex_debug(G_OBEX_DEBUG_PACKET, "opcode 0x%02x", pkt->opcode); pkt->headers = g_slist_prepend(pkt->headers, header); pkt->hlen += g_obex_header_get_length(header); return TRUE; } gboolean g_obex_packet_add_header(GObexPacket *pkt, GObexHeader *header) { g_obex_debug(G_OBEX_DEBUG_PACKET, "opcode 0x%02x", pkt->opcode); pkt->headers = g_slist_append(pkt->headers, header); pkt->hlen += g_obex_header_get_length(header); return TRUE; } gboolean g_obex_packet_add_body(GObexPacket *pkt, GObexDataProducer func, gpointer user_data) { g_obex_debug(G_OBEX_DEBUG_PACKET, "opcode 0x%02x", pkt->opcode); if (pkt->get_body != NULL) return FALSE; pkt->get_body = func; pkt->get_body_data = user_data; return TRUE; } gboolean g_obex_packet_add_unicode(GObexPacket *pkt, guint8 id, const char *str) { GObexHeader *hdr; g_obex_debug(G_OBEX_DEBUG_PACKET, "opcode 0x%02x", pkt->opcode); hdr = g_obex_header_new_unicode(id, str); if (hdr == NULL) return FALSE; return g_obex_packet_add_header(pkt, hdr); } gboolean g_obex_packet_add_bytes(GObexPacket *pkt, guint8 id, const void *data, gsize len) { GObexHeader *hdr; g_obex_debug(G_OBEX_DEBUG_PACKET, "opcode 0x%02x", pkt->opcode); hdr = g_obex_header_new_bytes(id, data, len); if (hdr == NULL) return FALSE; return g_obex_packet_add_header(pkt, hdr); } gboolean g_obex_packet_add_uint8(GObexPacket *pkt, guint8 id, guint8 val) { GObexHeader *hdr; g_obex_debug(G_OBEX_DEBUG_PACKET, "opcode 0x%02x", pkt->opcode); hdr = g_obex_header_new_uint8(id, val); if (hdr == NULL) return FALSE; return g_obex_packet_add_header(pkt, hdr); } gboolean g_obex_packet_add_uint32(GObexPacket *pkt, guint8 id, guint32 val) { GObexHeader *hdr; g_obex_debug(G_OBEX_DEBUG_PACKET, "opcode 0x%02x", pkt->opcode); hdr = g_obex_header_new_uint32(id, val); if (hdr == NULL) return FALSE; return g_obex_packet_add_header(pkt, hdr); } const void *g_obex_packet_get_data(GObexPacket *pkt, gsize *len) { g_obex_debug(G_OBEX_DEBUG_PACKET, "opcode 0x%02x", pkt->opcode); if (pkt->data_len == 0) { *len = 0; return NULL; } *len = pkt->data_len; switch (pkt->data_policy) { case G_OBEX_DATA_INHERIT: case G_OBEX_DATA_COPY: return pkt->data.buf; case G_OBEX_DATA_REF: return pkt->data.buf_ref; } g_assert_not_reached(); } gboolean g_obex_packet_set_data(GObexPacket *pkt, const void *data, gsize len, GObexDataPolicy data_policy) { g_obex_debug(G_OBEX_DEBUG_PACKET, "opcode 0x%02x", pkt->opcode); if (pkt->data.buf || pkt->data.buf_ref) return FALSE; pkt->data_policy = data_policy; pkt->data_len = len; switch (data_policy) { case G_OBEX_DATA_COPY: pkt->data.buf = util_memdup(data, len); break; case G_OBEX_DATA_REF: pkt->data.buf_ref = data; break; case G_OBEX_DATA_INHERIT: pkt->data.buf = (void *) data; break; } return TRUE; } GObexPacket *g_obex_packet_new_valist(guint8 opcode, gboolean final, guint first_hdr_id, va_list args) { GObexPacket *pkt; g_obex_debug(G_OBEX_DEBUG_PACKET, "opcode 0x%02x", opcode); pkt = g_new0(GObexPacket, 1); pkt->opcode = opcode; pkt->final = final; pkt->headers = g_obex_header_create_list(first_hdr_id, args, &pkt->hlen); pkt->data_policy = G_OBEX_DATA_COPY; return pkt; } GObexPacket *g_obex_packet_new(guint8 opcode, gboolean final, guint first_hdr_id, ...) { GObexPacket *pkt; va_list args; g_obex_debug(G_OBEX_DEBUG_PACKET, "opcode 0x%02x", opcode); va_start(args, first_hdr_id); pkt = g_obex_packet_new_valist(opcode, final, first_hdr_id, args); va_end(args); return pkt; } static void header_free(void *data, void *user_data) { g_obex_header_free(data); } void g_obex_packet_free(GObexPacket *pkt) { g_obex_debug(G_OBEX_DEBUG_PACKET, "opcode 0x%02x", pkt->opcode); switch (pkt->data_policy) { case G_OBEX_DATA_INHERIT: case G_OBEX_DATA_COPY: free(pkt->data.buf); break; case G_OBEX_DATA_REF: break; } g_slist_foreach(pkt->headers, header_free, NULL); g_slist_free(pkt->headers); g_free(pkt); } static gboolean parse_headers(GObexPacket *pkt, const void *data, gsize len, GObexDataPolicy data_policy, GError **err) { const guint8 *buf = data; g_obex_debug(G_OBEX_DEBUG_PACKET, "opcode 0x%02x", pkt->opcode); while (len > 0) { GObexHeader *header; gsize parsed; header = g_obex_header_decode(buf, len, data_policy, &parsed, err); if (header == NULL) return FALSE; pkt->headers = g_slist_append(pkt->headers, header); pkt->hlen += parsed; len -= parsed; buf += parsed; } return TRUE; } static const guint8 *get_bytes(void *to, const guint8 *from, gsize count) { memcpy(to, from, count); return (from + count); } GObexPacket *g_obex_packet_decode(const void *data, gsize len, gsize header_offset, GObexDataPolicy data_policy, GError **err) { const guint8 *buf = data; guint16 packet_len; guint8 opcode; GObexPacket *pkt; gboolean final; g_obex_debug(G_OBEX_DEBUG_PACKET, ""); if (data_policy == G_OBEX_DATA_INHERIT) { if (!err) return NULL; g_set_error(err, G_OBEX_ERROR, G_OBEX_ERROR_INVALID_ARGS, "Invalid data policy"); g_obex_debug(G_OBEX_DEBUG_ERROR, "%s", (*err)->message); return NULL; } if (len < 3 + header_offset) { if (!err) return NULL; g_set_error(err, G_OBEX_ERROR, G_OBEX_ERROR_PARSE_ERROR, "Not enough data to decode packet"); g_obex_debug(G_OBEX_DEBUG_ERROR, "%s", (*err)->message); return NULL; } buf = get_bytes(&opcode, buf, sizeof(opcode)); buf = get_bytes(&packet_len, buf, sizeof(packet_len)); packet_len = g_ntohs(packet_len); if (packet_len != len) { if (!err) return NULL; g_set_error(err, G_OBEX_ERROR, G_OBEX_ERROR_PARSE_ERROR, "Incorrect packet length (%u != %zu)", packet_len, len); g_obex_debug(G_OBEX_DEBUG_ERROR, "%s", (*err)->message); return NULL; } final = (opcode & FINAL_BIT) ? TRUE : FALSE; opcode &= ~FINAL_BIT; pkt = g_obex_packet_new(opcode, final, G_OBEX_HDR_INVALID); if (header_offset == 0) goto headers; g_obex_packet_set_data(pkt, buf, header_offset, data_policy); buf += header_offset; headers: if (!parse_headers(pkt, buf, len - (3 + header_offset), data_policy, err)) goto failed; return pkt; failed: g_obex_packet_free(pkt); return NULL; } static gssize get_body(GObexPacket *pkt, guint8 *buf, gsize len) { guint16 u16; gssize ret; g_obex_debug(G_OBEX_DEBUG_PACKET, "opcode 0x%02x", pkt->opcode); if (len < 3) return -ENOBUFS; ret = pkt->get_body(buf + 3, len - 3, pkt->get_body_data); if (ret < 0) return ret; if (ret > 0) buf[0] = G_OBEX_HDR_BODY; else buf[0] = G_OBEX_HDR_BODY_END; u16 = g_htons(ret + 3); memcpy(&buf[1], &u16, sizeof(u16)); return ret; } gssize g_obex_packet_encode(GObexPacket *pkt, guint8 *buf, gsize len) { gssize ret; gsize count; guint16 u16; GSList *l; g_obex_debug(G_OBEX_DEBUG_PACKET, "opcode 0x%02x", pkt->opcode); if (3 + pkt->data_len + pkt->hlen > len) return -ENOBUFS; buf[0] = pkt->opcode; if (pkt->final) buf[0] |= FINAL_BIT; if (pkt->data_len > 0) { if (pkt->data_policy == G_OBEX_DATA_REF) memcpy(&buf[3], pkt->data.buf_ref, pkt->data_len); else memcpy(&buf[3], pkt->data.buf, pkt->data_len); } count = 3 + pkt->data_len; for (l = pkt->headers; l != NULL; l = g_slist_next(l)) { GObexHeader *hdr = l->data; if (count >= len) return -ENOBUFS; ret = g_obex_header_encode(hdr, buf + count, len - count); if (ret < 0) return ret; count += ret; } if (pkt->get_body) { ret = get_body(pkt, buf + count, len - count); if (ret < 0) return ret; if (ret == 0) { if (pkt->opcode == G_OBEX_RSP_CONTINUE) buf[0] = G_OBEX_RSP_SUCCESS; buf[0] |= FINAL_BIT; } count += ret + 3; } u16 = g_htons(count); memcpy(&buf[1], &u16, sizeof(u16)); return count; } bluez-5.82/gobex/PaxHeaders/gobex-apparam.h0000644000000000000000000000005014015011623015637 xustar0020 atime=1743516285 20 ctime=1743591283 bluez-5.82/gobex/gobex-apparam.h0000644000000000000000000000313714015011623015324 0ustar00rootroot/* SPDX-License-Identifier: GPL-2.0-or-later */ /* * * OBEX library with GLib integration * * Copyright (C) 2012 Intel Corporation. * */ #ifndef __GOBEX_APPARAM_H #define __GOBEX_APPARAM_H #include typedef struct _GObexApparam GObexApparam; GObexApparam *g_obex_apparam_decode(const void *data, gsize size); gssize g_obex_apparam_encode(GObexApparam *apparam, void *buf, gsize size); GObexApparam *g_obex_apparam_set_bytes(GObexApparam *apparam, guint8 id, const void *value, gsize size); GObexApparam *g_obex_apparam_set_uint8(GObexApparam *apparam, guint8 id, guint8 value); GObexApparam *g_obex_apparam_set_uint16(GObexApparam *apparam, guint8 id, guint16 value); GObexApparam *g_obex_apparam_set_uint32(GObexApparam *apparam, guint8 id, guint32 value); GObexApparam *g_obex_apparam_set_uint64(GObexApparam *apparam, guint8 id, guint64 value); GObexApparam *g_obex_apparam_set_string(GObexApparam *apparam, guint8 id, const char *value); gboolean g_obex_apparam_get_bytes(GObexApparam *apparam, guint8 id, const guint8 **val, gsize *len); gboolean g_obex_apparam_get_uint8(GObexApparam *apparam, guint8 id, guint8 *value); gboolean g_obex_apparam_get_uint16(GObexApparam *apparam, guint8 id, guint16 *value); gboolean g_obex_apparam_get_uint32(GObexApparam *apparam, guint8 id, guint32 *value); gboolean g_obex_apparam_get_uint64(GObexApparam *apparam, guint8 id, guint64 *value); char *g_obex_apparam_get_string(GObexApparam *apparam, guint8 id); void g_obex_apparam_free(GObexApparam *apparam); #endif /* __GOBEX_APPARAM_H */ bluez-5.82/gobex/PaxHeaders/gobex.h0000644000000000000000000000005014015011623014220 xustar0020 atime=1743516285 20 ctime=1743591283 bluez-5.82/gobex/gobex.h0000644000000000000000000000765314015011623013714 0ustar00rootroot/* SPDX-License-Identifier: GPL-2.0-or-later */ /* * * OBEX library with GLib integration * * Copyright (C) 2011 Intel Corporation. All rights reserved. * */ #ifndef __GOBEX_H #define __GOBEX_H #include #include #include "gobex/gobex-defs.h" #include "gobex/gobex-packet.h" typedef enum { G_OBEX_TRANSPORT_STREAM, G_OBEX_TRANSPORT_PACKET, } GObexTransportType; typedef struct _GObex GObex; typedef void (*GObexFunc) (GObex *obex, GError *err, gpointer user_data); typedef void (*GObexRequestFunc) (GObex *obex, GObexPacket *req, gpointer user_data); typedef void (*GObexResponseFunc) (GObex *obex, GError *err, GObexPacket *rsp, gpointer user_data); gboolean g_obex_send(GObex *obex, GObexPacket *pkt, GError **err); guint g_obex_send_req(GObex *obex, GObexPacket *req, int timeout, GObexResponseFunc func, gpointer user_data, GError **err); gboolean g_obex_cancel_req(GObex *obex, guint req_id, gboolean remove_callback); gboolean g_obex_send_rsp(GObex *obex, guint8 rspcode, GError **err, guint first_hdr_type, ...); void g_obex_set_disconnect_function(GObex *obex, GObexFunc func, gpointer user_data); guint g_obex_add_request_function(GObex *obex, guint8 opcode, GObexRequestFunc func, gpointer user_data); gboolean g_obex_remove_request_function(GObex *obex, guint id); void g_obex_suspend(GObex *obex); void g_obex_resume(GObex *obex); gboolean g_obex_srm_active(GObex *obex); void g_obex_drop_tx_queue(GObex *obex); GObex *g_obex_new(GIOChannel *io, GObexTransportType transport_type, gssize rx_mtu, gssize tx_mtu); GObex *g_obex_ref(GObex *obex); void g_obex_unref(GObex *obex); /* High level client functions */ guint g_obex_connect(GObex *obex, GObexResponseFunc func, gpointer user_data, GError **err, guint first_hdr_id, ...); guint g_obex_disconnect(GObex *obex, GObexResponseFunc func, gpointer user_data, GError **err); guint g_obex_setpath(GObex *obex, const char *path, GObexResponseFunc func, gpointer user_data, GError **err); guint g_obex_mkdir(GObex *obex, const char *path, GObexResponseFunc func, gpointer user_data, GError **err); guint g_obex_delete(GObex *obex, const char *name, GObexResponseFunc func, gpointer user_data, GError **err); guint g_obex_copy(GObex *obex, const char *name, const char *dest, GObexResponseFunc func, gpointer user_data, GError **err); guint g_obex_move(GObex *obex, const char *name, const char *dest, GObexResponseFunc func, gpointer user_data, GError **err); guint g_obex_abort(GObex *obex, GObexResponseFunc func, gpointer user_data, GError **err); /* Transfer related high-level functions */ guint g_obex_put_req(GObex *obex, GObexDataProducer data_func, GObexFunc complete_func, gpointer user_data, GError **err, guint first_hdr_id, ...); guint g_obex_put_req_pkt(GObex *obex, GObexPacket *req, GObexDataProducer data_func, GObexFunc complete_func, gpointer user_data, GError **err); guint g_obex_get_req(GObex *obex, GObexDataConsumer data_func, GObexFunc complete_func, gpointer user_data, GError **err, guint first_hdr_id, ...); guint g_obex_get_req_pkt(GObex *obex, GObexPacket *req, GObexDataConsumer data_func, GObexFunc complete_func, gpointer user_data, GError **err); guint g_obex_put_rsp(GObex *obex, GObexPacket *req, GObexDataConsumer data_func, GObexFunc complete_func, gpointer user_data, GError **err, guint first_hdr_id, ...); guint g_obex_get_rsp(GObex *obex, GObexDataProducer data_func, GObexFunc complete_func, gpointer user_data, GError **err, guint first_hdr_id, ...); guint g_obex_get_rsp_pkt(GObex *obex, GObexPacket *rsp, GObexDataProducer data_func, GObexFunc complete_func, gpointer user_data, GError **err); gboolean g_obex_cancel_transfer(guint id, GObexFunc complete_func, gpointer user_data); const char *g_obex_strerror(guint8 err_code); guint8 g_obex_errno_to_rsp(int err); #endif /* __GOBEX_H */ bluez-5.82/gobex/PaxHeaders/gobex-defs.h0000644000000000000000000000005014015011623015137 xustar0020 atime=1743516285 20 ctime=1743591283 bluez-5.82/gobex/gobex-defs.h0000644000000000000000000000162214015011623014621 0ustar00rootroot/* SPDX-License-Identifier: GPL-2.0-or-later */ /* * * OBEX library with GLib integration * * Copyright (C) 2011 Intel Corporation. All rights reserved. * */ #ifndef __GOBEX_DEFS_H #define __GOBEX_DEFS_H #include typedef enum { G_OBEX_DATA_INHERIT, G_OBEX_DATA_COPY, G_OBEX_DATA_REF, } GObexDataPolicy; #define G_OBEX_ERROR_FIRST (0xff + 1) #define G_OBEX_PROTO_ERROR(code) ((code) < G_OBEX_ERROR_FIRST) typedef enum { G_OBEX_ERROR_PARSE_ERROR = G_OBEX_ERROR_FIRST, G_OBEX_ERROR_INVALID_ARGS, G_OBEX_ERROR_DISCONNECTED, G_OBEX_ERROR_TIMEOUT, G_OBEX_ERROR_CANCELLED, G_OBEX_ERROR_FAILED, } GObexError; typedef gssize (*GObexDataProducer) (void *buf, gsize len, gpointer user_data); typedef gboolean (*GObexDataConsumer) (const void *buf, gsize len, gpointer user_data); #define G_OBEX_ERROR g_obex_error_quark() GQuark g_obex_error_quark(void); #endif /* __GOBEX_DEFS_H */ bluez-5.82/gobex/PaxHeaders/gobex-header.h0000644000000000000000000000005014015011623015446 xustar0020 atime=1743516285 20 ctime=1743591283 bluez-5.82/gobex/gobex-header.h0000644000000000000000000000545714015011623015142 0ustar00rootroot/* SPDX-License-Identifier: GPL-2.0-or-later */ /* * * OBEX library with GLib integration * * Copyright (C) 2011 Intel Corporation. All rights reserved. * */ #ifndef __GOBEX_HEADER_H #define __GOBEX_HEADER_H #include #include "gobex/gobex-defs.h" #include "gobex/gobex-apparam.h" /* Header ID's */ #define G_OBEX_HDR_INVALID 0x00 #define G_OBEX_HDR_COUNT 0xc0 #define G_OBEX_HDR_NAME 0x01 #define G_OBEX_HDR_TYPE 0x42 #define G_OBEX_HDR_LENGTH 0xc3 #define G_OBEX_HDR_TIME 0x44 #define G_OBEX_HDR_DESCRIPTION 0x05 #define G_OBEX_HDR_TARGET 0x46 #define G_OBEX_HDR_HTTP 0x47 #define G_OBEX_HDR_BODY 0x48 #define G_OBEX_HDR_BODY_END 0x49 #define G_OBEX_HDR_WHO 0x4a #define G_OBEX_HDR_CONNECTION 0xcb #define G_OBEX_HDR_APPARAM 0x4c #define G_OBEX_HDR_AUTHCHAL 0x4d #define G_OBEX_HDR_AUTHRESP 0x4e #define G_OBEX_HDR_CREATOR 0xcf #define G_OBEX_HDR_WANUUID 0x50 #define G_OBEX_HDR_OBJECTCLASS 0x51 #define G_OBEX_HDR_SESSIONPARAM 0x52 #define G_OBEX_HDR_SESSIONSEQ 0x93 #define G_OBEX_HDR_ACTION 0x94 #define G_OBEX_HDR_DESTNAME 0x15 #define G_OBEX_HDR_PERMISSIONS 0xd6 #define G_OBEX_HDR_SRM 0x97 #define G_OBEX_HDR_SRMP 0x98 /* Action header values */ #define G_OBEX_ACTION_COPY 0x00 #define G_OBEX_ACTION_MOVE 0x01 #define G_OBEX_ACTION_SETPERM 0x02 /* SRM header values */ #define G_OBEX_SRM_DISABLE 0x00 #define G_OBEX_SRM_ENABLE 0x01 #define G_OBEX_SRM_INDICATE 0x02 /* SRMP header values */ #define G_OBEX_SRMP_NEXT 0x00 #define G_OBEX_SRMP_WAIT 0x01 #define G_OBEX_SRMP_NEXT_WAIT 0x02 typedef struct _GObexHeader GObexHeader; gboolean g_obex_header_get_unicode(GObexHeader *header, const char **str); gboolean g_obex_header_get_bytes(GObexHeader *header, const guint8 **val, gsize *len); gboolean g_obex_header_get_uint8(GObexHeader *header, guint8 *val); gboolean g_obex_header_get_uint32(GObexHeader *header, guint32 *val); GObexApparam *g_obex_header_get_apparam(GObexHeader *header); GObexHeader *g_obex_header_new_unicode(guint8 id, const char *str); GObexHeader *g_obex_header_new_bytes(guint8 id, const void *data, gsize len); GObexHeader *g_obex_header_new_uint8(guint8 id, guint8 val); GObexHeader *g_obex_header_new_uint32(guint8 id, guint32 val); GObexHeader *g_obex_header_new_tag(guint8 id, GObexApparam *apparam); GObexHeader *g_obex_header_new_apparam(GObexApparam *apparam); GSList *g_obex_header_create_list(guint8 first_hdr_id, va_list args, gsize *total_len); guint8 g_obex_header_get_id(GObexHeader *header); guint16 g_obex_header_get_length(GObexHeader *header); gssize g_obex_header_encode(GObexHeader *header, void *buf, gsize buf_len); GObexHeader *g_obex_header_decode(const void *data, gsize len, GObexDataPolicy data_policy, gsize *parsed, GError **err); void g_obex_header_free(GObexHeader *header); #endif /* __GOBEX_HEADER_H */ bluez-5.82/gobex/PaxHeaders/gobex-debug.h0000644000000000000000000000005014015011623015304 xustar0020 atime=1743516285 20 ctime=1743591283 bluez-5.82/gobex/gobex-debug.h0000644000000000000000000000253114015011623014766 0ustar00rootroot/* SPDX-License-Identifier: GPL-2.0-or-later */ /* * OBEX library with GLib integration * * Copyright (C) 2011 Intel Corporation. All rights reserved. * */ #ifndef __GOBEX_DEBUG_H #define __GOBEX_DEBUG_H #include #include #include #define G_OBEX_DEBUG_NONE 1 #define G_OBEX_DEBUG_ERROR (1 << 1) #define G_OBEX_DEBUG_COMMAND (1 << 2) #define G_OBEX_DEBUG_TRANSFER (1 << 3) #define G_OBEX_DEBUG_HEADER (1 << 4) #define G_OBEX_DEBUG_PACKET (1 << 5) #define G_OBEX_DEBUG_DATA (1 << 6) #define G_OBEX_DEBUG_APPARAM (1 << 7) extern guint gobex_debug; #define g_obex_debug(level, format, ...) \ if (gobex_debug & level) \ g_log("gobex", G_LOG_LEVEL_DEBUG, "%s:%s() " format, __FILE__, \ __func__, ## __VA_ARGS__) static inline void g_obex_dump(guint level, const char *prefix, const void *buf, gsize len) { const guint8 *data = buf; int n = 0; if (!(gobex_debug & level)) return; while (len > 0) { int i, size; printf("%s %04x:", prefix, n); size = len > 16 ? 16 : len; for (i = 0; i < size; i++) printf("%02x%s", data[i], (i + 1) % 8 ? " " : " "); for (; i < 16; i++) printf(" %s", (i + 1) % 8 ? " " : " "); for (i = 0; i < size; i++) printf("%1c", isprint(data[i]) ? data[i] : '.'); printf("\n"); data += size; len -= size; n += size; } } #endif /* __GOBEX_DEBUG_H */ bluez-5.82/gobex/PaxHeaders/gobex-apparam.c0000644000000000000000000000005014015011623015632 xustar0020 atime=1743516294 20 ctime=1743591283 bluez-5.82/gobex/gobex-apparam.c0000644000000000000000000001537714015011623015330 0ustar00rootroot// SPDX-License-Identifier: GPL-2.0-or-later /* * * OBEX library with GLib integration * * Copyright (C) 2012 Intel Corporation. * */ #ifdef HAVE_CONFIG_H #include #endif #include #include #include #include "gobex-apparam.h" #include "gobex-debug.h" struct _GObexApparam { GHashTable *tags; }; struct apparam_tag { guint8 id; guint8 len; union { /* Data is stored in network order */ char string[0]; guint8 data[0]; guint8 u8; guint16 u16; guint32 u32; guint64 u64; } value; } __attribute__ ((packed)); static struct apparam_tag *tag_new(guint8 id, guint8 len, const void *data) { struct apparam_tag *tag; tag = g_malloc0(2 + len); tag->id = id; tag->len = len; memcpy(tag->value.data, data, len); return tag; } static GObexApparam *g_obex_apparam_new(void) { GObexApparam *apparam; apparam = g_new0(GObexApparam, 1); apparam->tags = g_hash_table_new_full(g_direct_hash, g_direct_equal, NULL, g_free); return apparam; } static struct apparam_tag *apparam_tag_decode(const void *data, gsize size, gsize *parsed) { struct apparam_tag *tag; const guint8 *ptr = data; guint8 id; guint8 len; if (size < 2) return NULL; id = ptr[0]; len = ptr[1]; if (len > size - 2) return NULL; tag = tag_new(id, len, ptr + 2); if (tag == NULL) return NULL; *parsed = 2 + tag->len; return tag; } GObexApparam *g_obex_apparam_decode(const void *data, gsize size) { GObexApparam *apparam; GHashTable *tags; gsize count = 0; if (size < 2) return NULL; apparam = g_obex_apparam_new(); tags = apparam->tags; while (count < size) { struct apparam_tag *tag; gsize parsed; guint id; tag = apparam_tag_decode(data + count, size - count, &parsed); if (tag == NULL) break; id = tag->id; g_hash_table_insert(tags, GUINT_TO_POINTER(id), tag); count += parsed; } if (count != size) { g_obex_apparam_free(apparam); return NULL; } return apparam; } static gssize tag_encode(struct apparam_tag *tag, void *buf, gsize len) { gsize count = 2 + tag->len; if (len < count) return -ENOBUFS; memcpy(buf, tag, count); return count; } gssize g_obex_apparam_encode(GObexApparam *apparam, void *buf, gsize len) { gsize count = 0; gssize ret; GHashTableIter iter; gpointer key, value; if (!apparam) return 0; g_hash_table_iter_init(&iter, apparam->tags); while (g_hash_table_iter_next(&iter, &key, &value)) { struct apparam_tag *tag = value; ret = tag_encode(tag, buf + count, len - count); if (ret < 0) return ret; count += ret; } return count; } GObexApparam *g_obex_apparam_set_bytes(GObexApparam *apparam, guint8 id, const void *value, gsize len) { struct apparam_tag *tag; guint uid = id; if (apparam == NULL) apparam = g_obex_apparam_new(); tag = tag_new(id, len, value); g_hash_table_replace(apparam->tags, GUINT_TO_POINTER(uid), tag); return apparam; } GObexApparam *g_obex_apparam_set_uint8(GObexApparam *apparam, guint8 id, guint8 value) { g_obex_debug(G_OBEX_DEBUG_APPARAM, "tag 0x%02x value %u", id, value); return g_obex_apparam_set_bytes(apparam, id, &value, 1); } GObexApparam *g_obex_apparam_set_uint16(GObexApparam *apparam, guint8 id, guint16 value) { guint16 num = g_htons(value); g_obex_debug(G_OBEX_DEBUG_APPARAM, "tag 0x%02x value %u", id, value); return g_obex_apparam_set_bytes(apparam, id, &num, 2); } GObexApparam *g_obex_apparam_set_uint32(GObexApparam *apparam, guint8 id, guint32 value) { guint32 num = g_htonl(value); g_obex_debug(G_OBEX_DEBUG_APPARAM, "tag 0x%02x value %u", id, value); return g_obex_apparam_set_bytes(apparam, id, &num, 4); } GObexApparam *g_obex_apparam_set_uint64(GObexApparam *apparam, guint8 id, guint64 value) { guint64 num = GUINT64_TO_BE(value); g_obex_debug(G_OBEX_DEBUG_APPARAM, "tag 0x%02x value %" G_GUINT64_FORMAT, id, value); return g_obex_apparam_set_bytes(apparam, id, &num, 8); } GObexApparam *g_obex_apparam_set_string(GObexApparam *apparam, guint8 id, const char *value) { gsize len; g_obex_debug(G_OBEX_DEBUG_APPARAM, "tag 0x%02x value %s", id, value); len = strlen(value) + 1; if (len > G_MAXUINT8) { ((char *) value)[G_MAXUINT8 - 1] = '\0'; len = G_MAXUINT8; } return g_obex_apparam_set_bytes(apparam, id, value, len); } static struct apparam_tag *g_obex_apparam_find_tag(GObexApparam *apparam, guint id) { return g_hash_table_lookup(apparam->tags, GUINT_TO_POINTER(id)); } gboolean g_obex_apparam_get_uint8(GObexApparam *apparam, guint8 id, guint8 *dest) { struct apparam_tag *tag; g_obex_debug(G_OBEX_DEBUG_APPARAM, "tag 0x%02x", id); tag = g_obex_apparam_find_tag(apparam, id); if (tag == NULL) return FALSE; *dest = tag->value.u8; g_obex_debug(G_OBEX_DEBUG_APPARAM, "%u", *dest); return TRUE; } gboolean g_obex_apparam_get_uint16(GObexApparam *apparam, guint8 id, guint16 *dest) { struct apparam_tag *tag; g_obex_debug(G_OBEX_DEBUG_APPARAM, "tag 0x%02x", id); tag = g_obex_apparam_find_tag(apparam, id); if (tag == NULL) return FALSE; if (tag->len < sizeof(*dest)) return FALSE; *dest = g_ntohs(tag->value.u16); g_obex_debug(G_OBEX_DEBUG_APPARAM, "%u", *dest); return TRUE; } gboolean g_obex_apparam_get_uint32(GObexApparam *apparam, guint8 id, guint32 *dest) { struct apparam_tag *tag; g_obex_debug(G_OBEX_DEBUG_APPARAM, "tag 0x%02x", id); tag = g_obex_apparam_find_tag(apparam, id); if (tag == NULL) return FALSE; if (tag->len < sizeof(*dest)) return FALSE; *dest = g_ntohl(tag->value.u32); g_obex_debug(G_OBEX_DEBUG_APPARAM, "%u", *dest); return TRUE; } gboolean g_obex_apparam_get_uint64(GObexApparam *apparam, guint8 id, guint64 *dest) { struct apparam_tag *tag; g_obex_debug(G_OBEX_DEBUG_APPARAM, "tag 0x%02x", id); tag = g_obex_apparam_find_tag(apparam, id); if (tag == NULL) return FALSE; if (tag->len < sizeof(*dest)) return FALSE; *dest = GUINT64_FROM_BE(tag->value.u64); g_obex_debug(G_OBEX_DEBUG_APPARAM, "%" G_GUINT64_FORMAT, *dest); return TRUE; } char *g_obex_apparam_get_string(GObexApparam *apparam, guint8 id) { struct apparam_tag *tag; char *string; g_obex_debug(G_OBEX_DEBUG_APPARAM, "tag 0x%02x", id); tag = g_obex_apparam_find_tag(apparam, id); if (tag == NULL) return NULL; string = g_strndup(tag->value.string, tag->len); g_obex_debug(G_OBEX_DEBUG_APPARAM, "%s", string); return string; } gboolean g_obex_apparam_get_bytes(GObexApparam *apparam, guint8 id, const guint8 **val, gsize *len) { struct apparam_tag *tag; g_obex_debug(G_OBEX_DEBUG_APPARAM, "tag 0x%02x", id); tag = g_obex_apparam_find_tag(apparam, id); if (tag == NULL) return FALSE; *len = tag->len; *val = tag->value.data; return TRUE; } void g_obex_apparam_free(GObexApparam *apparam) { g_hash_table_unref(apparam->tags); g_free(apparam); } bluez-5.82/gobex/PaxHeaders/gobex-packet.h0000644000000000000000000000005014015011623015465 xustar0020 atime=1743516285 20 ctime=1743591283 bluez-5.82/gobex/gobex-packet.h0000644000000000000000000000710714015011623015153 0ustar00rootroot/* SPDX-License-Identifier: GPL-2.0-or-later */ /* * * OBEX library with GLib integration * * Copyright (C) 2011 Intel Corporation. All rights reserved. * */ #ifndef __GOBEX_PACKET_H #define __GOBEX_PACKET_H #include #include #include "gobex/gobex-defs.h" #include "gobex/gobex-header.h" /* Request opcodes */ #define G_OBEX_OP_CONNECT 0x00 #define G_OBEX_OP_DISCONNECT 0x01 #define G_OBEX_OP_PUT 0x02 #define G_OBEX_OP_GET 0x03 #define G_OBEX_OP_SETPATH 0x05 #define G_OBEX_OP_ACTION 0x06 #define G_OBEX_OP_SESSION 0x07 #define G_OBEX_OP_ABORT 0x7f /* Response codes */ #define G_OBEX_RSP_CONTINUE 0x10 #define G_OBEX_RSP_SUCCESS 0x20 #define G_OBEX_RSP_CREATED 0x21 #define G_OBEX_RSP_ACCEPTED 0x22 #define G_OBEX_RSP_NON_AUTHORITATIVE 0x23 #define G_OBEX_RSP_NO_CONTENT 0x24 #define G_OBEX_RSP_RESET_CONTENT 0x25 #define G_OBEX_RSP_PARTIAL_CONTENT 0x26 #define G_OBEX_RSP_MULTIPLE_CHOICES 0x30 #define G_OBEX_RSP_MOVED_PERMANENTLY 0x31 #define G_OBEX_RSP_MOVED_TEMPORARILY 0x32 #define G_OBEX_RSP_SEE_OTHER 0x33 #define G_OBEX_RSP_NOT_MODIFIED 0x34 #define G_OBEX_RSP_USE_PROXY 0x35 #define G_OBEX_RSP_BAD_REQUEST 0x40 #define G_OBEX_RSP_UNAUTHORIZED 0x41 #define G_OBEX_RSP_PAYMENT_REQUIRED 0x42 #define G_OBEX_RSP_FORBIDDEN 0x43 #define G_OBEX_RSP_NOT_FOUND 0x44 #define G_OBEX_RSP_METHOD_NOT_ALLOWED 0x45 #define G_OBEX_RSP_NOT_ACCEPTABLE 0x46 #define G_OBEX_RSP_PROXY_AUTH_REQUIRED 0x47 #define G_OBEX_RSP_REQUEST_TIME_OUT 0x48 #define G_OBEX_RSP_CONFLICT 0x49 #define G_OBEX_RSP_GONE 0x4a #define G_OBEX_RSP_LENGTH_REQUIRED 0x4b #define G_OBEX_RSP_PRECONDITION_FAILED 0x4c #define G_OBEX_RSP_REQ_ENTITY_TOO_LARGE 0x4d #define G_OBEX_RSP_REQ_URL_TOO_LARGE 0x4e #define G_OBEX_RSP_UNSUPPORTED_MEDIA_TYPE 0x4f #define G_OBEX_RSP_INTERNAL_SERVER_ERROR 0x50 #define G_OBEX_RSP_NOT_IMPLEMENTED 0x51 #define G_OBEX_RSP_BAD_GATEWAY 0x52 #define G_OBEX_RSP_SERVICE_UNAVAILABLE 0x53 #define G_OBEX_RSP_GATEWAY_TIMEOUT 0x54 #define G_OBEX_RSP_VERSION_NOT_SUPPORTED 0x55 #define G_OBEX_RSP_DATABASE_FULL 0x60 #define G_OBEX_RSP_DATABASE_LOCKED 0x61 typedef struct _GObexPacket GObexPacket; GObexHeader *g_obex_packet_get_header(GObexPacket *pkt, guint8 id); GObexHeader *g_obex_packet_get_body(GObexPacket *pkt); guint8 g_obex_packet_get_operation(GObexPacket *pkt, gboolean *final); gboolean g_obex_packet_prepend_header(GObexPacket *pkt, GObexHeader *header); gboolean g_obex_packet_add_header(GObexPacket *pkt, GObexHeader *header); gboolean g_obex_packet_add_body(GObexPacket *pkt, GObexDataProducer func, gpointer user_data); gboolean g_obex_packet_add_unicode(GObexPacket *pkt, guint8 id, const char *str); gboolean g_obex_packet_add_bytes(GObexPacket *pkt, guint8 id, const void *data, gsize len); gboolean g_obex_packet_add_uint8(GObexPacket *pkt, guint8 id, guint8 val); gboolean g_obex_packet_add_uint32(GObexPacket *pkt, guint8 id, guint32 val); gboolean g_obex_packet_set_data(GObexPacket *pkt, const void *data, gsize len, GObexDataPolicy data_policy); const void *g_obex_packet_get_data(GObexPacket *pkt, gsize *len); GObexPacket *g_obex_packet_new(guint8 opcode, gboolean final, guint first_hdr_id, ...); GObexPacket *g_obex_packet_new_valist(guint8 opcode, gboolean final, guint first_hdr_id, va_list args); void g_obex_packet_free(GObexPacket *pkt); GObexPacket *g_obex_packet_decode(const void *data, gsize len, gsize header_offset, GObexDataPolicy data_policy, GError **err); gssize g_obex_packet_encode(GObexPacket *pkt, guint8 *buf, gsize len); #endif /* __GOBEX_PACKET_H */ bluez-5.82/gobex/PaxHeaders/gobex-header.c0000644000000000000000000000005014711225434015453 xustar0020 atime=1743516290 20 ctime=1743591283 bluez-5.82/gobex/gobex-header.c0000644000000000000000000003001714711225434015135 0ustar00rootroot// SPDX-License-Identifier: GPL-2.0-or-later /* * * OBEX library with GLib integration * * Copyright (C) 2011 Intel Corporation. All rights reserved. * */ #ifdef HAVE_CONFIG_H #include #endif #include #include "gobex-header.h" #include "gobex-debug.h" #include "src/shared/util.h" /* Header types */ #define G_OBEX_HDR_ENC_UNICODE (0 << 6) #define G_OBEX_HDR_ENC_BYTES (1 << 6) #define G_OBEX_HDR_ENC_UINT8 (2 << 6) #define G_OBEX_HDR_ENC_UINT32 (3 << 6) #define G_OBEX_HDR_ENC(id) ((id) & 0xc0) struct _GObexHeader { guint8 id; gboolean extdata; gsize vlen; /* Length of value */ gsize hlen; /* Length of full encoded header */ union { char *string; /* UTF-8 converted from UTF-16 */ guint8 *data; /* Own buffer */ const guint8 *extdata; /* Reference to external buffer */ guint8 u8; guint32 u32; } v; }; static glong utf8_to_utf16(gunichar2 **utf16, const char *utf8) { glong utf16_len; int i; if (*utf8 == '\0') { *utf16 = NULL; return 0; } *utf16 = g_utf8_to_utf16(utf8, -1, NULL, &utf16_len, NULL); if (*utf16 == NULL) return -1; /* g_utf8_to_utf16 produces host-byteorder UTF-16, * but OBEX requires network byteorder (big endian) */ for (i = 0; i < utf16_len; i++) (*utf16)[i] = g_htons((*utf16)[i]); utf16_len = (utf16_len + 1) << 1; return utf16_len; } static glong utf16_to_utf8(char **utf8, const gunichar2 *utf16, guint16 len, GError **err) { glong utf8_len; guint16 utf16_len, i; gunichar2 *buf; if (*utf16 == '\0') { *utf8 = NULL; return 0; } /* OBEX requires network byteorder (big endian) UTF-16 * but g_utf16_to_utf8 expects host-byteorder UTF-8 */ utf16_len = len / sizeof(gunichar2); buf = alloca(sizeof(gunichar2) * (utf16_len + 1)); for (i = 0; i < utf16_len; i++) (buf)[i] = g_ntohs(utf16[i]); buf[utf16_len] = '\0'; *utf8 = g_utf16_to_utf8(buf, -1, NULL, &utf8_len, err); if (*utf8 == NULL) utf8_len = -1; return utf8_len; } static guint8 *put_bytes(guint8 *to, const void *from, gsize count) { memcpy(to, from, count); return (to + count); } static const guint8 *get_bytes(void *to, const guint8 *from, gsize count) { memcpy(to, from, count); return (from + count); } gssize g_obex_header_encode(GObexHeader *header, void *buf, gsize buf_len) { guint8 *ptr = buf; guint16 u16; guint32 u32; gunichar2 *utf16; glong utf16_len; g_obex_debug(G_OBEX_DEBUG_HEADER, "header 0x%02x", G_OBEX_HDR_ENC(header->id)); if (buf_len < header->hlen) return -1; ptr = put_bytes(ptr, &header->id, sizeof(header->id)); switch (G_OBEX_HDR_ENC(header->id)) { case G_OBEX_HDR_ENC_UNICODE: utf16_len = utf8_to_utf16(&utf16, header->v.string); if (utf16_len < 0 || (guint16) utf16_len > buf_len) return -1; g_assert_cmpuint(utf16_len + 3, ==, header->hlen); u16 = g_htons(utf16_len + 3); ptr = put_bytes(ptr, &u16, sizeof(u16)); put_bytes(ptr, utf16, utf16_len); g_free(utf16); break; case G_OBEX_HDR_ENC_BYTES: u16 = g_htons(header->hlen); ptr = put_bytes(ptr, &u16, sizeof(u16)); if (header->extdata) put_bytes(ptr, header->v.extdata, header->vlen); else put_bytes(ptr, header->v.data, header->vlen); break; case G_OBEX_HDR_ENC_UINT8: *ptr = header->v.u8; break; case G_OBEX_HDR_ENC_UINT32: u32 = g_htonl(header->v.u32); put_bytes(ptr, &u32, sizeof(u32)); break; default: g_assert_not_reached(); } return header->hlen; } GObexHeader *g_obex_header_decode(const void *data, gsize len, GObexDataPolicy data_policy, gsize *parsed, GError **err) { GObexHeader *header; const guint8 *ptr = data; guint16 hdr_len; glong str_len; GError *conv_err = NULL; if (len < 2) { if (!err) return NULL; g_set_error(err, G_OBEX_ERROR, G_OBEX_ERROR_PARSE_ERROR, "Too short header in packet"); g_obex_debug(G_OBEX_DEBUG_ERROR, "%s", (*err)->message); return NULL; } header = g_new0(GObexHeader, 1); ptr = get_bytes(&header->id, ptr, sizeof(header->id)); g_obex_debug(G_OBEX_DEBUG_HEADER, "header 0x%02x", G_OBEX_HDR_ENC(header->id)); switch (G_OBEX_HDR_ENC(header->id)) { case G_OBEX_HDR_ENC_UNICODE: if (len < 3) { g_set_error(err, G_OBEX_ERROR, G_OBEX_ERROR_PARSE_ERROR, "Not enough data for unicode header (0x%02x)", header->id); goto failed; } ptr = get_bytes(&hdr_len, ptr, sizeof(hdr_len)); hdr_len = g_ntohs(hdr_len); if (hdr_len == 3) { header->v.string = g_strdup(""); header->vlen = 0; header->hlen = hdr_len; *parsed = hdr_len; break; } if (hdr_len > len || hdr_len < 5) { g_set_error(err, G_OBEX_ERROR, G_OBEX_ERROR_PARSE_ERROR, "Invalid unicode header (0x%02x) length (%u)", header->id, hdr_len); goto failed; } str_len = utf16_to_utf8(&header->v.string, (const gunichar2 *) ptr, hdr_len - 5, &conv_err); if (str_len < 0) { g_set_error(err, G_OBEX_ERROR, G_OBEX_ERROR_PARSE_ERROR, "UTF16 to UTF8 conversion failed: %s", conv_err->message); g_error_free(conv_err); goto failed; } header->vlen = (gsize) str_len; header->hlen = hdr_len; *parsed = hdr_len; break; case G_OBEX_HDR_ENC_BYTES: if (len < 3) { g_set_error(err, G_OBEX_ERROR, G_OBEX_ERROR_PARSE_ERROR, "Too short byte array header"); goto failed; } ptr = get_bytes(&hdr_len, ptr, sizeof(hdr_len)); hdr_len = g_ntohs(hdr_len); if (hdr_len > len) { g_set_error(err, G_OBEX_ERROR, G_OBEX_ERROR_PARSE_ERROR, "Too long byte array header"); goto failed; } if (hdr_len < 3) { g_set_error(err, G_OBEX_ERROR, G_OBEX_ERROR_PARSE_ERROR, "Too small byte array length"); goto failed; } header->vlen = hdr_len - 3; header->hlen = hdr_len; switch (data_policy) { case G_OBEX_DATA_COPY: header->v.data = util_memdup(ptr, header->vlen); break; case G_OBEX_DATA_REF: header->extdata = TRUE; header->v.extdata = ptr; break; case G_OBEX_DATA_INHERIT: default: g_set_error(err, G_OBEX_ERROR, G_OBEX_ERROR_INVALID_ARGS, "Invalid data policy"); goto failed; } *parsed = hdr_len; break; case G_OBEX_HDR_ENC_UINT8: header->vlen = 1; header->hlen = 2; header->v.u8 = *ptr; *parsed = 2; break; case G_OBEX_HDR_ENC_UINT32: if (len < 5) { g_set_error(err, G_OBEX_ERROR, G_OBEX_ERROR_PARSE_ERROR, "Too short uint32 header"); goto failed; } header->vlen = 4; header->hlen = 5; get_bytes(&header->v.u32, ptr, sizeof(header->v.u32)); header->v.u32 = g_ntohl(header->v.u32); *parsed = 5; break; default: g_assert_not_reached(); } return header; failed: if (*err) g_obex_debug(G_OBEX_DEBUG_ERROR, "%s", (*err)->message); g_obex_header_free(header); return NULL; } void g_obex_header_free(GObexHeader *header) { g_obex_debug(G_OBEX_DEBUG_HEADER, "header 0x%02x", G_OBEX_HDR_ENC(header->id)); switch (G_OBEX_HDR_ENC(header->id)) { case G_OBEX_HDR_ENC_UNICODE: g_free(header->v.string); break; case G_OBEX_HDR_ENC_BYTES: if (!header->extdata) free(header->v.data); break; case G_OBEX_HDR_ENC_UINT8: case G_OBEX_HDR_ENC_UINT32: break; default: g_assert_not_reached(); } g_free(header); } gboolean g_obex_header_get_unicode(GObexHeader *header, const char **str) { g_obex_debug(G_OBEX_DEBUG_HEADER, "header 0x%02x", G_OBEX_HDR_ENC(header->id)); if (G_OBEX_HDR_ENC(header->id) != G_OBEX_HDR_ENC_UNICODE) return FALSE; *str = header->v.string; g_obex_debug(G_OBEX_DEBUG_HEADER, "%s", *str); return TRUE; } gboolean g_obex_header_get_bytes(GObexHeader *header, const guint8 **val, gsize *len) { g_obex_debug(G_OBEX_DEBUG_HEADER, "header 0x%02x", G_OBEX_HDR_ENC(header->id)); if (G_OBEX_HDR_ENC(header->id) != G_OBEX_HDR_ENC_BYTES) return FALSE; *len = header->vlen; if (header->extdata) *val = header->v.extdata; else *val = header->v.data; return TRUE; } GObexApparam *g_obex_header_get_apparam(GObexHeader *header) { gboolean ret; const guint8 *val; gsize len; ret = g_obex_header_get_bytes(header, &val, &len); if (!ret) return NULL; return g_obex_apparam_decode(val, len); } gboolean g_obex_header_get_uint8(GObexHeader *header, guint8 *val) { g_obex_debug(G_OBEX_DEBUG_HEADER, "header 0x%02x", G_OBEX_HDR_ENC(header->id)); if (G_OBEX_HDR_ENC(header->id) != G_OBEX_HDR_ENC_UINT8) return FALSE; *val = header->v.u8; g_obex_debug(G_OBEX_DEBUG_HEADER, "%u", *val); return TRUE; } gboolean g_obex_header_get_uint32(GObexHeader *header, guint32 *val) { g_obex_debug(G_OBEX_DEBUG_HEADER, "header 0x%02x", G_OBEX_HDR_ENC(header->id)); if (G_OBEX_HDR_ENC(header->id) != G_OBEX_HDR_ENC_UINT32) return FALSE; *val = header->v.u32; g_obex_debug(G_OBEX_DEBUG_HEADER, "%u", *val); return TRUE; } GObexHeader *g_obex_header_new_unicode(guint8 id, const char *str) { GObexHeader *header; gsize len; g_obex_debug(G_OBEX_DEBUG_HEADER, "header 0x%02x", G_OBEX_HDR_ENC(id)); if (G_OBEX_HDR_ENC(id) != G_OBEX_HDR_ENC_UNICODE) return NULL; header = g_new0(GObexHeader, 1); header->id = id; len = g_utf8_strlen(str, -1); header->vlen = len; header->hlen = len == 0 ? 3 : 3 + ((len + 1) * 2); header->v.string = g_strdup(str); g_obex_debug(G_OBEX_DEBUG_HEADER, "%s", header->v.string); return header; } GObexHeader *g_obex_header_new_bytes(guint8 id, const void *data, gsize len) { GObexHeader *header; g_obex_debug(G_OBEX_DEBUG_HEADER, "header 0x%02x", G_OBEX_HDR_ENC(id)); if (G_OBEX_HDR_ENC(id) != G_OBEX_HDR_ENC_BYTES) return NULL; header = g_new0(GObexHeader, 1); header->id = id; header->vlen = len; header->hlen = len + 3; header->v.data = util_memdup(data, len); return header; } GObexHeader *g_obex_header_new_tag(guint8 id, GObexApparam *apparam) { guint8 buf[1024]; gssize len; len = g_obex_apparam_encode(apparam, buf, sizeof(buf)); if (len < 0) return NULL; return g_obex_header_new_bytes(id, buf, len); } GObexHeader *g_obex_header_new_apparam(GObexApparam *apparam) { return g_obex_header_new_tag(G_OBEX_HDR_APPARAM, apparam); } GObexHeader *g_obex_header_new_uint8(guint8 id, guint8 val) { GObexHeader *header; g_obex_debug(G_OBEX_DEBUG_HEADER, "header 0x%02x", G_OBEX_HDR_ENC(id)); if (G_OBEX_HDR_ENC(id) != G_OBEX_HDR_ENC_UINT8) return NULL; header = g_new0(GObexHeader, 1); header->id = id; header->vlen = 1; header->hlen = 2; header->v.u8 = val; g_obex_debug(G_OBEX_DEBUG_HEADER, "%u", header->v.u8); return header; } GObexHeader *g_obex_header_new_uint32(guint8 id, guint32 val) { GObexHeader *header; g_obex_debug(G_OBEX_DEBUG_HEADER, "header 0x%02x", G_OBEX_HDR_ENC(id)); if (G_OBEX_HDR_ENC(id) != G_OBEX_HDR_ENC_UINT32) return NULL; header = g_new0(GObexHeader, 1); header->id = id; header->vlen = 4; header->hlen = 5; header->v.u32 = val; g_obex_debug(G_OBEX_DEBUG_HEADER, "%u", header->v.u32); return header; } guint8 g_obex_header_get_id(GObexHeader *header) { g_obex_debug(G_OBEX_DEBUG_HEADER, "header 0x%02x id 0x%02x", G_OBEX_HDR_ENC(header->id), header->id); return header->id; } guint16 g_obex_header_get_length(GObexHeader *header) { g_obex_debug(G_OBEX_DEBUG_HEADER, "header 0x%02x length %zu", G_OBEX_HDR_ENC(header->id), header->hlen); return header->hlen; } GSList *g_obex_header_create_list(guint8 first_hdr_id, va_list args, gsize *total_len) { unsigned int id = first_hdr_id; GSList *l = NULL; g_obex_debug(G_OBEX_DEBUG_HEADER, ""); *total_len = 0; while (id != G_OBEX_HDR_INVALID) { GObexHeader *hdr; const char *str; const void *bytes; unsigned int val; gsize len; switch (G_OBEX_HDR_ENC(id)) { case G_OBEX_HDR_ENC_UNICODE: str = va_arg(args, const char *); hdr = g_obex_header_new_unicode(id, str); break; case G_OBEX_HDR_ENC_BYTES: bytes = va_arg(args, void *); len = va_arg(args, gsize); hdr = g_obex_header_new_bytes(id, bytes, len); break; case G_OBEX_HDR_ENC_UINT8: val = va_arg(args, unsigned int); hdr = g_obex_header_new_uint8(id, val); break; case G_OBEX_HDR_ENC_UINT32: val = va_arg(args, unsigned int); hdr = g_obex_header_new_uint32(id, val); break; default: g_assert_not_reached(); } l = g_slist_append(l, hdr); *total_len += hdr->hlen; id = va_arg(args, int); } return l; } bluez-5.82/gobex/PaxHeaders/gobex-defs.c0000644000000000000000000000005014015011623015132 xustar0020 atime=1743516288 20 ctime=1743591283 bluez-5.82/gobex/gobex-defs.c0000644000000000000000000000054014015011623014612 0ustar00rootroot// SPDX-License-Identifier: GPL-2.0-or-later /* * * OBEX library with GLib integration * * Copyright (C) 2011 Intel Corporation. All rights reserved. * */ #ifdef HAVE_CONFIG_H #include #endif #include #include "gobex-defs.h" GQuark g_obex_error_quark(void) { return g_quark_from_static_string("g-obex-error-quark"); } bluez-5.82/gobex/PaxHeaders/gobex.c0000644000000000000000000000005014711225434014225 xustar0020 atime=1743516285 20 ctime=1743591283 bluez-5.82/gobex/gobex.c0000644000000000000000000011176214711225434013716 0ustar00rootroot// SPDX-License-Identifier: GPL-2.0-or-later /* * * OBEX library with GLib integration * * Copyright (C) 2011 Intel Corporation. All rights reserved. * */ #ifdef HAVE_CONFIG_H #include #endif #include #include #include #include "gobex.h" #include "gobex-debug.h" #define G_OBEX_DEFAULT_MTU 4096 #define G_OBEX_MINIMUM_MTU 255 #define G_OBEX_MAXIMUM_MTU 65535 #define G_OBEX_DEFAULT_TIMEOUT 10 #define G_OBEX_ABORT_TIMEOUT 5 #define G_OBEX_OP_NONE 0xff #define FINAL_BIT 0x80 #define CONNID_INVALID 0xffffffff /* Challenge request */ #define NONCE_TAG 0x00 #define NONCE_LEN 16 /* Challenge response */ #define DIGEST_TAG 0x00 guint gobex_debug = 0; struct srm_config { guint8 op; gboolean enabled; guint8 srm; guint8 srmp; gboolean outgoing; }; struct _GObex { int ref_count; GIOChannel *io; guint io_source; gboolean (*read) (GObex *obex, GError **err); gboolean (*write) (GObex *obex, GError **err); guint8 *rx_buf; size_t rx_data; guint16 rx_pkt_len; guint8 rx_last_op; guint8 *tx_buf; size_t tx_data; size_t tx_sent; gboolean suspended; gboolean use_srm; struct srm_config *srm; guint write_source; gssize io_rx_mtu; gssize io_tx_mtu; guint16 rx_mtu; guint16 tx_mtu; guint32 conn_id; GObexApparam *authchal; GQueue *tx_queue; GSList *req_handlers; GObexFunc disconn_func; gpointer disconn_func_data; struct pending_pkt *pending_req; }; struct pending_pkt { guint id; GObex *obex; GObexPacket *pkt; guint timeout; guint timeout_id; GObexResponseFunc rsp_func; gpointer rsp_data; gboolean cancelled; gboolean suspended; gboolean authenticating; }; struct req_handler { guint id; guint8 opcode; GObexRequestFunc func; gpointer user_data; }; struct connect_data { guint8 version; guint8 flags; guint16 mtu; } __attribute__ ((packed)); struct setpath_data { guint8 flags; guint8 constants; } __attribute__ ((packed)); static const struct error_code { guint8 code; const char *name; } obex_errors[] = { { G_OBEX_RSP_CONTINUE, "Continue" }, { G_OBEX_RSP_SUCCESS, "Success" }, { G_OBEX_RSP_CREATED, "Created" }, { G_OBEX_RSP_ACCEPTED, "Accepted" }, { G_OBEX_RSP_NON_AUTHORITATIVE, "Non Authoritative" }, { G_OBEX_RSP_NO_CONTENT, "No Content" }, { G_OBEX_RSP_RESET_CONTENT, "Reset Content" }, { G_OBEX_RSP_PARTIAL_CONTENT, "Partial Content" }, { G_OBEX_RSP_MULTIPLE_CHOICES, "Multiple Choices" }, { G_OBEX_RSP_MOVED_PERMANENTLY, "Moved Permanently" }, { G_OBEX_RSP_MOVED_TEMPORARILY, "Moved Temporarily" }, { G_OBEX_RSP_SEE_OTHER, "See Other" }, { G_OBEX_RSP_NOT_MODIFIED, "Not Modified" }, { G_OBEX_RSP_USE_PROXY, "Use Proxy" }, { G_OBEX_RSP_BAD_REQUEST, "Bad Request" }, { G_OBEX_RSP_UNAUTHORIZED, "Unauthorized" }, { G_OBEX_RSP_PAYMENT_REQUIRED, "Payment Required" }, { G_OBEX_RSP_FORBIDDEN, "Forbidden" }, { G_OBEX_RSP_NOT_FOUND, "Not Found" }, { G_OBEX_RSP_METHOD_NOT_ALLOWED, "Method Not Allowed" }, { G_OBEX_RSP_NOT_ACCEPTABLE, "Not Acceptable" }, { G_OBEX_RSP_PROXY_AUTH_REQUIRED, "Proxy Authentication Required" }, { G_OBEX_RSP_REQUEST_TIME_OUT, "Request Time Out" }, { G_OBEX_RSP_CONFLICT, "Conflict" }, { G_OBEX_RSP_GONE, "Gone" }, { G_OBEX_RSP_LENGTH_REQUIRED, "Length Required" }, { G_OBEX_RSP_PRECONDITION_FAILED, "Precondition Failed" }, { G_OBEX_RSP_REQ_ENTITY_TOO_LARGE, "Request Entity Too Large" }, { G_OBEX_RSP_REQ_URL_TOO_LARGE, "Request URL Too Large" }, { G_OBEX_RSP_UNSUPPORTED_MEDIA_TYPE, "Unsupported Media Type" }, { G_OBEX_RSP_INTERNAL_SERVER_ERROR, "Internal Server Error" }, { G_OBEX_RSP_NOT_IMPLEMENTED, "Not Implemented" }, { G_OBEX_RSP_BAD_GATEWAY, "Bad Gateway" }, { G_OBEX_RSP_SERVICE_UNAVAILABLE, "Service Unavailable" }, { G_OBEX_RSP_GATEWAY_TIMEOUT, "Gateway Timeout" }, { G_OBEX_RSP_VERSION_NOT_SUPPORTED, "Version Not Supported" }, { G_OBEX_RSP_DATABASE_FULL, "Database Full" }, { G_OBEX_RSP_DATABASE_LOCKED, "Database Locked" }, { 0x00, NULL } }; const char *g_obex_strerror(guint8 err_code) { const struct error_code *error; for (error = obex_errors; error->name != NULL; error++) { if (error->code == err_code) return error->name; } return ""; } static ssize_t req_header_offset(guint8 opcode) { switch (opcode) { case G_OBEX_OP_CONNECT: return sizeof(struct connect_data); case G_OBEX_OP_SETPATH: return sizeof(struct setpath_data); case G_OBEX_OP_DISCONNECT: case G_OBEX_OP_PUT: case G_OBEX_OP_GET: case G_OBEX_OP_SESSION: case G_OBEX_OP_ABORT: case G_OBEX_OP_ACTION: return 0; default: return -1; } } static ssize_t rsp_header_offset(guint8 opcode) { switch (opcode) { case G_OBEX_OP_CONNECT: return sizeof(struct connect_data); case G_OBEX_OP_SETPATH: case G_OBEX_OP_DISCONNECT: case G_OBEX_OP_PUT: case G_OBEX_OP_GET: case G_OBEX_OP_SESSION: case G_OBEX_OP_ABORT: case G_OBEX_OP_ACTION: return 0; default: return -1; } } static void pending_pkt_free(struct pending_pkt *p) { if (p->obex != NULL) g_obex_unref(p->obex); if (p->timeout_id > 0) g_source_remove(p->timeout_id); g_obex_packet_free(p->pkt); g_free(p); } static gboolean req_timeout(gpointer user_data) { GObex *obex = user_data; struct pending_pkt *p = obex->pending_req; GError *err; g_assert(p != NULL); p->timeout_id = 0; obex->pending_req = NULL; err = g_error_new(G_OBEX_ERROR, G_OBEX_ERROR_TIMEOUT, "Timed out waiting for response"); g_obex_debug(G_OBEX_DEBUG_ERROR, "%s", err->message); if (p->rsp_func) p->rsp_func(obex, err, NULL, p->rsp_data); g_error_free(err); pending_pkt_free(p); return FALSE; } static gboolean write_stream(GObex *obex, GError **err) { GIOStatus status; gsize bytes_written; char *buf; buf = (char *) &obex->tx_buf[obex->tx_sent]; status = g_io_channel_write_chars(obex->io, buf, obex->tx_data, &bytes_written, err); if (status != G_IO_STATUS_NORMAL) return FALSE; g_obex_dump(G_OBEX_DEBUG_DATA, "<", buf, bytes_written); obex->tx_sent += bytes_written; obex->tx_data -= bytes_written; return TRUE; } static gboolean write_packet(GObex *obex, GError **err) { GIOStatus status; gsize bytes_written; char *buf; buf = (char *) &obex->tx_buf[obex->tx_sent]; status = g_io_channel_write_chars(obex->io, buf, obex->tx_data, &bytes_written, err); if (status != G_IO_STATUS_NORMAL) return FALSE; if (bytes_written != obex->tx_data) return FALSE; g_obex_dump(G_OBEX_DEBUG_DATA, "<", buf, bytes_written); obex->tx_sent += bytes_written; obex->tx_data -= bytes_written; return TRUE; } static void set_srmp(GObex *obex, guint8 srmp, gboolean outgoing) { struct srm_config *config = obex->srm; if (config == NULL) return; /* Dont't reset if direction doesn't match */ if (srmp > G_OBEX_SRMP_NEXT_WAIT && config->outgoing != outgoing) return; config->srmp = srmp; config->outgoing = outgoing; } static void set_srm(GObex *obex, guint8 op, guint8 srm) { struct srm_config *config = obex->srm; gboolean enable; switch (srm) { case G_OBEX_SRM_ENABLE: case G_OBEX_SRM_DISABLE: case G_OBEX_SRM_INDICATE: break; default: return; } if (config == NULL) { if (srm == G_OBEX_SRM_DISABLE) return; config = g_new0(struct srm_config, 1); config->op = op; config->srm = srm; obex->srm = config; return; } /* Indicate response, treat it as request */ if (config->srm == G_OBEX_SRM_INDICATE) { if (srm != G_OBEX_SRM_ENABLE) goto done; config->srm = srm; return; } enable = (srm == G_OBEX_SRM_ENABLE); if (config->enabled == enable) goto done; config->enabled = enable; g_obex_debug(G_OBEX_DEBUG_COMMAND, "SRM %s", config->enabled ? "Enabled" : "Disabled"); done: if (config->enabled) return; g_free(obex->srm); obex->srm = NULL; } static gboolean g_obex_srm_enabled(GObex *obex) { if (!obex->use_srm) return FALSE; if (obex->srm == NULL) return FALSE; return obex->srm->enabled; } static void check_srm_final(GObex *obex, guint8 op) { if (!g_obex_srm_enabled(obex)) return; switch (obex->srm->op) { case G_OBEX_OP_CONNECT: return; default: if (op <= G_OBEX_RSP_CONTINUE) return; } set_srm(obex, op, G_OBEX_SRM_DISABLE); } static void setup_srm(GObex *obex, GObexPacket *pkt, gboolean outgoing) { GObexHeader *hdr; guint8 op; gboolean final; if (!obex->use_srm) return; op = g_obex_packet_get_operation(pkt, &final); hdr = g_obex_packet_get_header(pkt, G_OBEX_HDR_SRM); if (hdr != NULL) { guint8 srm; g_obex_header_get_uint8(hdr, &srm); g_obex_debug(G_OBEX_DEBUG_COMMAND, "srm 0x%02x", srm); set_srm(obex, op, srm); } else if (!g_obex_srm_enabled(obex)) set_srm(obex, op, G_OBEX_SRM_DISABLE); hdr = g_obex_packet_get_header(pkt, G_OBEX_HDR_SRMP); if (hdr != NULL) { guint8 srmp; g_obex_header_get_uint8(hdr, &srmp); g_obex_debug(G_OBEX_DEBUG_COMMAND, "srmp 0x%02x", srmp); set_srmp(obex, srmp, outgoing); } else if (obex->pending_req && obex->pending_req->suspended) g_obex_packet_add_uint8(pkt, G_OBEX_HDR_SRMP, G_OBEX_SRMP_WAIT); else set_srmp(obex, -1, outgoing); if (final) check_srm_final(obex, op); } static gboolean write_data(GIOChannel *io, GIOCondition cond, gpointer user_data) { GObex *obex = user_data; struct pending_pkt *p = NULL; GError *err = NULL; if (cond & G_IO_NVAL) return FALSE; if (cond & (G_IO_HUP | G_IO_ERR)) goto stop_tx; if (obex->tx_data == 0) { ssize_t len; p = g_queue_pop_head(obex->tx_queue); if (p == NULL) goto stop_tx; setup_srm(obex, p->pkt, TRUE); if (g_obex_srm_enabled(obex)) goto encode; /* Can't send a request while there's a pending one */ if (obex->pending_req && p->id > 0) { g_queue_push_head(obex->tx_queue, p); goto stop_tx; } encode: len = g_obex_packet_encode(p->pkt, obex->tx_buf, obex->tx_mtu); if (len == -EAGAIN) { g_queue_push_head(obex->tx_queue, p); g_obex_suspend(obex); goto stop_tx; } if (len < 0) { pending_pkt_free(p); goto done; } if (p->id > 0) { if (obex->pending_req != NULL) pending_pkt_free(obex->pending_req); obex->pending_req = p; p->timeout_id = g_timeout_add_seconds(p->timeout, req_timeout, obex); } else { /* During packet encode final bit can be set */ if (obex->tx_buf[0] & FINAL_BIT) check_srm_final(obex, obex->tx_buf[0] & ~FINAL_BIT); pending_pkt_free(p); /* g_free() doesn't set the pointer to NULL */ p = NULL; } obex->tx_data = len; obex->tx_sent = 0; } if (obex->suspended) { obex->write_source = 0; return FALSE; } if (!obex->write(obex, &err)) { g_obex_debug(G_OBEX_DEBUG_ERROR, "%s", err->message); if (p) { if (p->rsp_func) p->rsp_func(obex, err, NULL, p->rsp_data); pending_pkt_free(p); } g_error_free(err); goto stop_tx; } done: if (obex->tx_data > 0 || g_queue_get_length(obex->tx_queue) > 0) return TRUE; stop_tx: obex->rx_last_op = G_OBEX_OP_NONE; obex->tx_data = 0; obex->write_source = 0; return FALSE; } static void enable_tx(GObex *obex) { GIOCondition cond; if (obex->suspended) return; if (!obex->io || obex->write_source > 0) return; cond = G_IO_OUT | G_IO_HUP | G_IO_ERR | G_IO_NVAL; obex->write_source = g_io_add_watch(obex->io, cond, write_data, obex); } void g_obex_drop_tx_queue(GObex *obex) { struct pending_pkt *p; g_obex_debug(G_OBEX_DEBUG_COMMAND, ""); while ((p = g_queue_pop_head(obex->tx_queue))) pending_pkt_free(p); } static gboolean g_obex_send_internal(GObex *obex, struct pending_pkt *p, GError **err) { if (obex->io == NULL) { if (!err) return FALSE; g_set_error(err, G_OBEX_ERROR, G_OBEX_ERROR_DISCONNECTED, "The transport is not connected"); g_obex_debug(G_OBEX_DEBUG_ERROR, "%s", (*err)->message); return FALSE; } if (g_obex_packet_get_operation(p->pkt, NULL) == G_OBEX_OP_ABORT) g_queue_push_head(obex->tx_queue, p); else g_queue_push_tail(obex->tx_queue, p); if (obex->pending_req == NULL || p->id == 0) enable_tx(obex); return TRUE; } static void init_connect_data(GObex *obex, struct connect_data *data) { guint16 u16; memset(data, 0, sizeof(*data)); data->version = 0x10; data->flags = 0; u16 = g_htons(obex->rx_mtu); memcpy(&data->mtu, &u16, sizeof(u16)); } static guint8 *digest_response(const guint8 *nonce) { GChecksum *md5; guint8 *result; gsize size; result = g_new0(guint8, NONCE_LEN); md5 = g_checksum_new(G_CHECKSUM_MD5); if (md5 == NULL) return result; g_checksum_update(md5, nonce, NONCE_LEN); g_checksum_update(md5, (guint8 *) ":BlueZ", 6); size = NONCE_LEN; g_checksum_get_digest(md5, result, &size); g_checksum_free(md5); return result; } static void prepare_auth_rsp(GObex *obex, GObexPacket *rsp) { GObexHeader *hdr; GObexApparam *authrsp; const guint8 *nonce; guint8 *result; gsize len; /* Check if client is already responding to authentication challenge */ hdr = g_obex_packet_get_header(rsp, G_OBEX_HDR_AUTHRESP); if (hdr) goto done; if (!g_obex_apparam_get_bytes(obex->authchal, NONCE_TAG, &nonce, &len)) goto done; if (len != NONCE_LEN) goto done; result = digest_response(nonce); authrsp = g_obex_apparam_set_bytes(NULL, DIGEST_TAG, result, NONCE_LEN); hdr = g_obex_header_new_tag(G_OBEX_HDR_AUTHRESP, authrsp); g_obex_packet_add_header(rsp, hdr); g_obex_apparam_free(authrsp); g_free(result); done: g_obex_apparam_free(obex->authchal); obex->authchal = NULL; } static void prepare_connect_rsp(GObex *obex, GObexPacket *rsp) { GObexHeader *hdr; struct connect_data data; static guint32 next_connid = 1; init_connect_data(obex, &data); g_obex_packet_set_data(rsp, &data, sizeof(data), G_OBEX_DATA_COPY); hdr = g_obex_packet_get_header(rsp, G_OBEX_HDR_CONNECTION); if (hdr) { g_obex_header_get_uint32(hdr, &obex->conn_id); goto done; } obex->conn_id = next_connid++; hdr = g_obex_header_new_uint32(G_OBEX_HDR_CONNECTION, obex->conn_id); g_obex_packet_prepend_header(rsp, hdr); done: if (obex->authchal) prepare_auth_rsp(obex, rsp); } static void prepare_srm_rsp(GObex *obex, GObexPacket *pkt) { GObexHeader *hdr; if (!obex->use_srm || obex->srm == NULL) return; if (obex->srm->enabled) return; hdr = g_obex_packet_get_header(pkt, G_OBEX_HDR_SRM); if (hdr != NULL) return; hdr = g_obex_header_new_uint8(G_OBEX_HDR_SRM, G_OBEX_SRM_ENABLE); g_obex_packet_prepend_header(pkt, hdr); } gboolean g_obex_send(GObex *obex, GObexPacket *pkt, GError **err) { struct pending_pkt *p; gboolean ret; g_obex_debug(G_OBEX_DEBUG_COMMAND, "conn %u", obex->conn_id); if (obex == NULL || pkt == NULL) { if (!err) return FALSE; g_set_error(err, G_OBEX_ERROR, G_OBEX_ERROR_INVALID_ARGS, "Invalid arguments"); g_obex_debug(G_OBEX_DEBUG_ERROR, "%s", (*err)->message); return FALSE; } switch (obex->rx_last_op) { case G_OBEX_OP_CONNECT: prepare_connect_rsp(obex, pkt); break; case G_OBEX_OP_GET: case G_OBEX_OP_PUT: prepare_srm_rsp(obex, pkt); break; } p = g_new0(struct pending_pkt, 1); p->pkt = pkt; ret = g_obex_send_internal(obex, p, err); if (ret == FALSE) pending_pkt_free(p); return ret; } static void prepare_srm_req(GObex *obex, GObexPacket *pkt) { GObexHeader *hdr; if (!obex->use_srm) return; if (obex->srm != NULL && obex->srm->enabled) return; hdr = g_obex_packet_get_header(pkt, G_OBEX_HDR_SRM); if (hdr != NULL) return; hdr = g_obex_header_new_uint8(G_OBEX_HDR_SRM, G_OBEX_SRM_ENABLE); g_obex_packet_prepend_header(pkt, hdr); } guint g_obex_send_req(GObex *obex, GObexPacket *req, int timeout, GObexResponseFunc func, gpointer user_data, GError **err) { GObexHeader *hdr; struct pending_pkt *p; static guint id = 1; guint8 op; g_obex_debug(G_OBEX_DEBUG_COMMAND, "conn %u", obex->conn_id); op = g_obex_packet_get_operation(req, NULL); if (op == G_OBEX_OP_PUT || op == G_OBEX_OP_GET) { /* Only enable SRM automatically for GET and PUT */ prepare_srm_req(obex, req); } if (obex->conn_id == CONNID_INVALID) goto create_pending; if (obex->rx_last_op == G_OBEX_RSP_CONTINUE) goto create_pending; if (g_obex_srm_enabled(obex) && obex->pending_req != NULL) goto create_pending; hdr = g_obex_packet_get_header(req, G_OBEX_HDR_CONNECTION); if (hdr != NULL) goto create_pending; hdr = g_obex_header_new_uint32(G_OBEX_HDR_CONNECTION, obex->conn_id); g_obex_packet_prepend_header(req, hdr); create_pending: p = g_new0(struct pending_pkt, 1); p->pkt = req; p->id = id++; p->rsp_func = func; p->rsp_data = user_data; if (timeout < 0) p->timeout = G_OBEX_DEFAULT_TIMEOUT; else p->timeout = timeout; if (!g_obex_send_internal(obex, p, err)) { pending_pkt_free(p); return 0; } return p->id; } static int pending_pkt_cmp(gconstpointer a, gconstpointer b) { const struct pending_pkt *p = a; guint id = GPOINTER_TO_UINT(b); return (p->id - id); } static gboolean pending_req_abort(GObex *obex, GError **err) { struct pending_pkt *p = obex->pending_req; GObexPacket *req; if (p->cancelled) return TRUE; p->cancelled = TRUE; if (p->timeout_id > 0) g_source_remove(p->timeout_id); p->timeout = G_OBEX_ABORT_TIMEOUT; p->timeout_id = g_timeout_add_seconds(p->timeout, req_timeout, obex); req = g_obex_packet_new(G_OBEX_OP_ABORT, TRUE, G_OBEX_HDR_INVALID); return g_obex_send(obex, req, err); } static gboolean cancel_complete(gpointer user_data) { struct pending_pkt *p = user_data; GObex *obex = p->obex; GError *err; g_assert(p->rsp_func != NULL); err = g_error_new(G_OBEX_ERROR, G_OBEX_ERROR_CANCELLED, "The request was cancelled"); p->rsp_func(obex, err, NULL, p->rsp_data); g_error_free(err); pending_pkt_free(p); return FALSE; } gboolean g_obex_cancel_req(GObex *obex, guint req_id, gboolean remove_callback) { GList *match; struct pending_pkt *p; if (obex->pending_req && obex->pending_req->id == req_id) { if (!pending_req_abort(obex, NULL)) { p = obex->pending_req; obex->pending_req = NULL; goto immediate_completion; } if (remove_callback) obex->pending_req->rsp_func = NULL; return TRUE; } match = g_queue_find_custom(obex->tx_queue, GUINT_TO_POINTER(req_id), pending_pkt_cmp); if (match == NULL) return FALSE; p = match->data; g_queue_delete_link(obex->tx_queue, match); immediate_completion: p->cancelled = TRUE; p->obex = g_obex_ref(obex); if (remove_callback || p->rsp_func == NULL) pending_pkt_free(p); else g_idle_add(cancel_complete, p); return TRUE; } gboolean g_obex_send_rsp(GObex *obex, guint8 rspcode, GError **err, guint first_hdr_type, ...) { GObexPacket *rsp; va_list args; va_start(args, first_hdr_type); rsp = g_obex_packet_new_valist(rspcode, TRUE, first_hdr_type, args); va_end(args); return g_obex_send(obex, rsp, err); } void g_obex_set_disconnect_function(GObex *obex, GObexFunc func, gpointer user_data) { obex->disconn_func = func; obex->disconn_func_data = user_data; } static int req_handler_cmpop(gconstpointer a, gconstpointer b) { const struct req_handler *handler = a; guint opcode = GPOINTER_TO_UINT(b); return (int) handler->opcode - (int) opcode; } static int req_handler_cmpid(gconstpointer a, gconstpointer b) { const struct req_handler *handler = a; guint id = GPOINTER_TO_UINT(b); return (int) handler->id - (int) id; } guint g_obex_add_request_function(GObex *obex, guint8 opcode, GObexRequestFunc func, gpointer user_data) { struct req_handler *handler; static guint next_id = 1; handler = g_new0(struct req_handler, 1); handler->id = next_id++; handler->opcode = opcode; handler->func = func; handler->user_data = user_data; obex->req_handlers = g_slist_prepend(obex->req_handlers, handler); return handler->id; } gboolean g_obex_remove_request_function(GObex *obex, guint id) { struct req_handler *handler; GSList *match; match = g_slist_find_custom(obex->req_handlers, GUINT_TO_POINTER(id), req_handler_cmpid); if (match == NULL) return FALSE; handler = match->data; obex->req_handlers = g_slist_delete_link(obex->req_handlers, match); g_free(handler); return TRUE; } static void g_obex_srm_suspend(GObex *obex) { struct pending_pkt *p = obex->pending_req; GObexPacket *req; if (p->timeout_id > 0) { g_source_remove(p->timeout_id); p->timeout_id = 0; } p->suspended = TRUE; req = g_obex_packet_new(G_OBEX_OP_GET, TRUE, G_OBEX_HDR_SRMP, G_OBEX_SRMP_WAIT, G_OBEX_HDR_INVALID); g_obex_send(obex, req, NULL); } void g_obex_suspend(GObex *obex) { struct pending_pkt *req = obex->pending_req; g_obex_debug(G_OBEX_DEBUG_COMMAND, "conn %u", obex->conn_id); if (!g_obex_srm_active(obex) || !req) goto done; /* Send SRMP wait in case of GET */ if (g_obex_packet_get_operation(req->pkt, NULL) == G_OBEX_OP_GET) { g_obex_srm_suspend(obex); return; } done: obex->suspended = TRUE; if (obex->write_source > 0) { g_source_remove(obex->write_source); obex->write_source = 0; } } static void g_obex_srm_resume(GObex *obex) { struct pending_pkt *p = obex->pending_req; GObexPacket *req; p->timeout_id = g_timeout_add_seconds(p->timeout, req_timeout, obex); p->suspended = FALSE; req = g_obex_packet_new(G_OBEX_OP_GET, TRUE, G_OBEX_HDR_INVALID); g_obex_send(obex, req, NULL); } void g_obex_resume(GObex *obex) { struct pending_pkt *req = obex->pending_req; g_obex_debug(G_OBEX_DEBUG_COMMAND, "conn %u", obex->conn_id); obex->suspended = FALSE; if (g_obex_srm_active(obex) || !req) goto done; if (g_obex_packet_get_operation(req->pkt, NULL) == G_OBEX_OP_GET) g_obex_srm_resume(obex); done: if (g_queue_get_length(obex->tx_queue) > 0 || obex->tx_data > 0) enable_tx(obex); } gboolean g_obex_srm_active(GObex *obex) { gboolean ret = FALSE; if (!g_obex_srm_enabled(obex)) goto done; if (obex->srm->srmp <= G_OBEX_SRMP_NEXT_WAIT) goto done; ret = TRUE; done: g_obex_debug(G_OBEX_DEBUG_COMMAND, "%s", ret ? "yes" : "no"); return ret; } static void auth_challenge(GObex *obex) { struct pending_pkt *p = obex->pending_req; if (p->authenticating) return; p->authenticating = TRUE; prepare_auth_rsp(obex, p->pkt); /* Remove it as pending and add it back to the queue so it gets sent * again */ if (p->timeout_id > 0) { g_source_remove(p->timeout_id); p->timeout_id = 0; } obex->pending_req = NULL; g_obex_send_internal(obex, p, NULL); } static void parse_connect_data(GObex *obex, GObexPacket *pkt) { const struct connect_data *data; GObexHeader *hdr; guint16 u16; size_t data_len; data = g_obex_packet_get_data(pkt, &data_len); if (data == NULL || data_len != sizeof(*data)) return; memcpy(&u16, &data->mtu, sizeof(u16)); obex->tx_mtu = g_ntohs(u16); if (obex->io_tx_mtu > 0 && obex->tx_mtu > obex->io_tx_mtu) obex->tx_mtu = obex->io_tx_mtu; obex->tx_buf = g_realloc(obex->tx_buf, obex->tx_mtu); hdr = g_obex_packet_get_header(pkt, G_OBEX_HDR_CONNECTION); if (hdr) g_obex_header_get_uint32(hdr, &obex->conn_id); hdr = g_obex_packet_get_header(pkt, G_OBEX_HDR_AUTHCHAL); if (hdr) obex->authchal = g_obex_header_get_apparam(hdr); } static gboolean parse_response(GObex *obex, GObexPacket *rsp) { struct pending_pkt *p = obex->pending_req; guint8 opcode, rspcode; gboolean final; rspcode = g_obex_packet_get_operation(rsp, &final); opcode = g_obex_packet_get_operation(p->pkt, NULL); if (opcode == G_OBEX_OP_CONNECT) { parse_connect_data(obex, rsp); if (rspcode == G_OBEX_RSP_UNAUTHORIZED && obex->authchal) auth_challenge(obex); } setup_srm(obex, rsp, FALSE); if (!g_obex_srm_enabled(obex)) return final; /* * Resposes have final bit set but in case of GET with SRM * we should not remove the request since the remote side will * continue sending responses until the transfer is finished */ if (opcode == G_OBEX_OP_GET && rspcode == G_OBEX_RSP_CONTINUE) { if (p->timeout_id > 0) g_source_remove(p->timeout_id); p->timeout_id = g_timeout_add_seconds(p->timeout, req_timeout, obex); return FALSE; } return final; } static void handle_response(GObex *obex, GError *err, GObexPacket *rsp) { struct pending_pkt *p; gboolean disconn = err ? TRUE : FALSE, final_rsp = TRUE; if (rsp != NULL) final_rsp = parse_response(obex, rsp); if (!obex->pending_req) return; p = obex->pending_req; /* Reset if final so it can no longer be cancelled */ if (final_rsp) obex->pending_req = NULL; if (p->cancelled) err = g_error_new(G_OBEX_ERROR, G_OBEX_ERROR_CANCELLED, "The operation was cancelled"); if (err) g_obex_debug(G_OBEX_DEBUG_ERROR, "%s", err->message); if (p->rsp_func) { p->rsp_func(obex, err, rsp, p->rsp_data); /* Check if user callback removed the request */ if (!final_rsp && p != obex->pending_req) return; } if (p->cancelled) g_error_free(err); if (final_rsp) pending_pkt_free(p); if (!disconn && g_queue_get_length(obex->tx_queue) > 0) enable_tx(obex); } static gboolean check_connid(GObex *obex, GObexPacket *pkt) { GObexHeader *hdr; guint32 id; if (obex->conn_id == CONNID_INVALID) return TRUE; hdr = g_obex_packet_get_header(pkt, G_OBEX_HDR_CONNECTION); if (hdr == NULL) return TRUE; g_obex_header_get_uint32(hdr, &id); return obex->conn_id == id; } static int parse_request(GObex *obex, GObexPacket *req) { guint8 op; gboolean final; op = g_obex_packet_get_operation(req, &final); switch (op) { case G_OBEX_OP_CONNECT: parse_connect_data(obex, req); break; case G_OBEX_OP_ABORT: break; default: if (check_connid(obex, req)) break; return -G_OBEX_RSP_SERVICE_UNAVAILABLE; } setup_srm(obex, req, FALSE); return op; } static void handle_request(GObex *obex, GObexPacket *req) { GSList *match; int op; op = parse_request(obex, req); if (op < 0) goto fail; match = g_slist_find_custom(obex->req_handlers, GUINT_TO_POINTER(op), req_handler_cmpop); if (match) { struct req_handler *handler = match->data; handler->func(obex, req, handler->user_data); return; } op = -G_OBEX_RSP_NOT_IMPLEMENTED; fail: g_obex_debug(G_OBEX_DEBUG_ERROR, "%s", g_obex_strerror(-op)); g_obex_send_rsp(obex, -op, NULL, G_OBEX_HDR_INVALID); } static gboolean read_stream(GObex *obex, GError **err) { GIOChannel *io = obex->io; GIOStatus status; gsize rbytes, toread; guint16 u16; char *buf; if (obex->rx_data >= 3) goto read_body; rbytes = 0; toread = 3 - obex->rx_data; buf = (char *) &obex->rx_buf[obex->rx_data]; status = g_io_channel_read_chars(io, buf, toread, &rbytes, NULL); if (status != G_IO_STATUS_NORMAL) return TRUE; obex->rx_data += rbytes; if (obex->rx_data < 3) goto done; memcpy(&u16, &buf[1], sizeof(u16)); obex->rx_pkt_len = g_ntohs(u16); if (obex->rx_pkt_len > obex->rx_mtu) { if (!err) return FALSE; g_set_error(err, G_OBEX_ERROR, G_OBEX_ERROR_PARSE_ERROR, "Too big incoming packet"); g_obex_debug(G_OBEX_DEBUG_ERROR, "%s", (*err)->message); return FALSE; } read_body: if (obex->rx_data >= obex->rx_pkt_len) goto done; do { toread = obex->rx_pkt_len - obex->rx_data; buf = (char *) &obex->rx_buf[obex->rx_data]; status = g_io_channel_read_chars(io, buf, toread, &rbytes, NULL); if (status != G_IO_STATUS_NORMAL) goto done; obex->rx_data += rbytes; } while (rbytes > 0 && obex->rx_data < obex->rx_pkt_len); done: g_obex_dump(G_OBEX_DEBUG_DATA, ">", obex->rx_buf, obex->rx_data); return TRUE; } static gboolean read_packet(GObex *obex, GError **err) { GIOChannel *io = obex->io; GError *read_err = NULL; GIOStatus status; gsize rbytes; guint16 u16; if (obex->rx_data > 0) { g_set_error(err, G_OBEX_ERROR, G_OBEX_ERROR_PARSE_ERROR, "RX buffer not empty before reading packet"); goto fail; } status = g_io_channel_read_chars(io, (char *) obex->rx_buf, obex->rx_mtu, &rbytes, &read_err); if (status != G_IO_STATUS_NORMAL) { g_set_error(err, G_OBEX_ERROR, G_OBEX_ERROR_PARSE_ERROR, "Unable to read data: %s", read_err->message); g_error_free(read_err); goto fail; } obex->rx_data += rbytes; if (rbytes < 3) { g_set_error(err, G_OBEX_ERROR, G_OBEX_ERROR_PARSE_ERROR, "Incomplete packet received"); goto fail; } memcpy(&u16, &obex->rx_buf[1], sizeof(u16)); obex->rx_pkt_len = g_ntohs(u16); if (obex->rx_pkt_len != rbytes) { g_set_error(err, G_OBEX_ERROR, G_OBEX_ERROR_PARSE_ERROR, "Data size doesn't match packet size (%zu != %u)", rbytes, obex->rx_pkt_len); return FALSE; } g_obex_dump(G_OBEX_DEBUG_DATA, ">", obex->rx_buf, obex->rx_data); return TRUE; fail: if (err) g_obex_debug(G_OBEX_DEBUG_ERROR, "%s", (*err)->message); return FALSE; } static gboolean incoming_data(GIOChannel *io, GIOCondition cond, gpointer user_data) { GObex *obex = user_data; GObexPacket *pkt; ssize_t header_offset; GError *err = NULL; guint8 opcode; if (cond & G_IO_NVAL) return FALSE; if (cond & (G_IO_HUP | G_IO_ERR)) { err = g_error_new(G_OBEX_ERROR, G_OBEX_ERROR_DISCONNECTED, "Transport got disconnected"); goto failed; } if (!obex->read(obex, &err)) goto failed; if (obex->rx_data < 3 || obex->rx_data < obex->rx_pkt_len) return TRUE; obex->rx_last_op = obex->rx_buf[0] & ~FINAL_BIT; if (obex->pending_req) { struct pending_pkt *p = obex->pending_req; opcode = g_obex_packet_get_operation(p->pkt, NULL); header_offset = rsp_header_offset(opcode); } else { opcode = obex->rx_last_op; /* Unexpected response -- fail silently */ if (opcode > 0x1f && opcode != G_OBEX_OP_ABORT) { obex->rx_data = 0; return TRUE; } header_offset = req_header_offset(opcode); } if (header_offset < 0) { err = g_error_new(G_OBEX_ERROR, G_OBEX_ERROR_PARSE_ERROR, "Unknown header offset for opcode 0x%02x", opcode); goto failed; } pkt = g_obex_packet_decode(obex->rx_buf, obex->rx_data, header_offset, G_OBEX_DATA_REF, &err); if (pkt == NULL) goto failed; /* Protect against user callback freeing the object */ g_obex_ref(obex); if (obex->pending_req) handle_response(obex, NULL, pkt); else handle_request(obex, pkt); obex->rx_data = 0; g_obex_unref(obex); if (err != NULL) g_error_free(err); if (pkt != NULL) g_obex_packet_free(pkt); return TRUE; failed: if (err) g_obex_debug(G_OBEX_DEBUG_ERROR, "%s", err->message); g_io_channel_unref(obex->io); obex->io = NULL; obex->io_source = 0; obex->rx_data = 0; /* Protect against user callback freeing the object */ g_obex_ref(obex); if (obex->pending_req) handle_response(obex, err, NULL); if (obex->disconn_func) obex->disconn_func(obex, err, obex->disconn_func_data); g_obex_unref(obex); g_error_free(err); return FALSE; } static const GDebugKey keys[] = { { "error", G_OBEX_DEBUG_ERROR }, { "command", G_OBEX_DEBUG_COMMAND }, { "transfer", G_OBEX_DEBUG_TRANSFER }, { "header", G_OBEX_DEBUG_HEADER }, { "packet", G_OBEX_DEBUG_PACKET }, { "data", G_OBEX_DEBUG_DATA }, { "apparam", G_OBEX_DEBUG_APPARAM }, }; GObex *g_obex_new(GIOChannel *io, GObexTransportType transport_type, gssize io_rx_mtu, gssize io_tx_mtu) { GObex *obex; GIOCondition cond; if (gobex_debug == 0) { const char *env = g_getenv("GOBEX_DEBUG"); if (env) { gobex_debug = g_parse_debug_string(env, keys, G_N_ELEMENTS(keys)); g_setenv("G_MESSAGES_DEBUG", "gobex", FALSE); } else gobex_debug = G_OBEX_DEBUG_NONE; } g_obex_debug(G_OBEX_DEBUG_COMMAND, ""); if (io == NULL) return NULL; if (io_rx_mtu >= 0 && io_rx_mtu < G_OBEX_MINIMUM_MTU) return NULL; if (io_tx_mtu >= 0 && io_tx_mtu < G_OBEX_MINIMUM_MTU) return NULL; obex = g_new0(GObex, 1); obex->io = g_io_channel_ref(io); obex->ref_count = 1; obex->conn_id = CONNID_INVALID; obex->rx_last_op = G_OBEX_OP_NONE; obex->io_rx_mtu = io_rx_mtu; obex->io_tx_mtu = io_tx_mtu; if (io_rx_mtu > G_OBEX_MAXIMUM_MTU) obex->rx_mtu = G_OBEX_MAXIMUM_MTU; else if (io_rx_mtu < G_OBEX_MINIMUM_MTU) obex->rx_mtu = G_OBEX_DEFAULT_MTU; else obex->rx_mtu = io_rx_mtu; obex->tx_mtu = G_OBEX_MINIMUM_MTU; obex->tx_queue = g_queue_new(); obex->rx_buf = g_malloc(obex->rx_mtu); obex->tx_buf = g_malloc(obex->tx_mtu); switch (transport_type) { case G_OBEX_TRANSPORT_STREAM: obex->read = read_stream; obex->write = write_stream; break; case G_OBEX_TRANSPORT_PACKET: obex->use_srm = TRUE; obex->read = read_packet; obex->write = write_packet; break; default: g_obex_unref(obex); return NULL; } g_io_channel_set_encoding(io, NULL, NULL); g_io_channel_set_buffered(io, FALSE); cond = G_IO_IN | G_IO_HUP | G_IO_ERR | G_IO_NVAL; obex->io_source = g_io_add_watch(io, cond, incoming_data, obex); return obex; } GObex *g_obex_ref(GObex *obex) { int refs; if (obex == NULL) return NULL; refs = __sync_add_and_fetch(&obex->ref_count, 1); g_obex_debug(G_OBEX_DEBUG_COMMAND, "ref %u", refs); return obex; } static void tx_queue_free(void *data, void *user_data) { pending_pkt_free(data); } void g_obex_unref(GObex *obex) { int refs; refs = __sync_sub_and_fetch(&obex->ref_count, 1); g_obex_debug(G_OBEX_DEBUG_COMMAND, "ref %u", refs); if (refs > 0) return; g_slist_free_full(obex->req_handlers, g_free); g_queue_foreach(obex->tx_queue, tx_queue_free, NULL); g_queue_free(obex->tx_queue); if (obex->io != NULL) g_io_channel_unref(obex->io); if (obex->io_source > 0) g_source_remove(obex->io_source); if (obex->write_source > 0) g_source_remove(obex->write_source); g_free(obex->rx_buf); g_free(obex->tx_buf); g_free(obex->srm); if (obex->pending_req) pending_pkt_free(obex->pending_req); if (obex->authchal) g_obex_apparam_free(obex->authchal); g_free(obex); } /* Higher level functions */ guint g_obex_connect(GObex *obex, GObexResponseFunc func, gpointer user_data, GError **err, guint first_hdr_id, ...) { GObexPacket *req; struct connect_data data; va_list args; g_obex_debug(G_OBEX_DEBUG_COMMAND, ""); va_start(args, first_hdr_id); req = g_obex_packet_new_valist(G_OBEX_OP_CONNECT, TRUE, first_hdr_id, args); va_end(args); init_connect_data(obex, &data); g_obex_packet_set_data(req, &data, sizeof(data), G_OBEX_DATA_COPY); return g_obex_send_req(obex, req, -1, func, user_data, err); } guint g_obex_disconnect(GObex *obex, GObexResponseFunc func, gpointer user_data, GError **err) { GObexPacket *req; g_obex_debug(G_OBEX_DEBUG_COMMAND, ""); req = g_obex_packet_new(G_OBEX_OP_DISCONNECT, TRUE, G_OBEX_HDR_INVALID); return g_obex_send_req(obex, req, -1, func, user_data, err); } guint g_obex_setpath(GObex *obex, const char *path, GObexResponseFunc func, gpointer user_data, GError **err) { GObexPacket *req; struct setpath_data data; const char *folder; g_obex_debug(G_OBEX_DEBUG_COMMAND, "conn %u", obex->conn_id); req = g_obex_packet_new(G_OBEX_OP_SETPATH, TRUE, G_OBEX_HDR_INVALID); memset(&data, 0, sizeof(data)); if (path != NULL && strlen(path) >= 2 && strncmp("..", path, 2) == 0) { data.flags = 0x03; folder = (path[2] == '/') ? &path[3] : NULL; } else { data.flags = 0x02; folder = path; } if (folder != NULL) { GObexHeader *hdr; hdr = g_obex_header_new_unicode(G_OBEX_HDR_NAME, folder); g_obex_packet_add_header(req, hdr); } g_obex_packet_set_data(req, &data, sizeof(data), G_OBEX_DATA_COPY); return g_obex_send_req(obex, req, -1, func, user_data, err); } guint g_obex_mkdir(GObex *obex, const char *path, GObexResponseFunc func, gpointer user_data, GError **err) { GObexPacket *req; struct setpath_data data; g_obex_debug(G_OBEX_DEBUG_COMMAND, "conn %u", obex->conn_id); req = g_obex_packet_new(G_OBEX_OP_SETPATH, TRUE, G_OBEX_HDR_NAME, path, G_OBEX_HDR_INVALID); memset(&data, 0, sizeof(data)); g_obex_packet_set_data(req, &data, sizeof(data), G_OBEX_DATA_COPY); return g_obex_send_req(obex, req, -1, func, user_data, err); } guint g_obex_delete(GObex *obex, const char *name, GObexResponseFunc func, gpointer user_data, GError **err) { GObexPacket *req; g_obex_debug(G_OBEX_DEBUG_COMMAND, "conn %u", obex->conn_id); req = g_obex_packet_new(G_OBEX_OP_PUT, TRUE, G_OBEX_HDR_NAME, name, G_OBEX_HDR_INVALID); return g_obex_send_req(obex, req, -1, func, user_data, err); } guint g_obex_copy(GObex *obex, const char *name, const char *dest, GObexResponseFunc func, gpointer user_data, GError **err) { GObexPacket *req; g_obex_debug(G_OBEX_DEBUG_COMMAND, "conn %u", obex->conn_id); req = g_obex_packet_new(G_OBEX_OP_ACTION, TRUE, G_OBEX_HDR_ACTION, G_OBEX_ACTION_COPY, G_OBEX_HDR_NAME, name, G_OBEX_HDR_DESTNAME, dest, G_OBEX_HDR_INVALID); return g_obex_send_req(obex, req, -1, func, user_data, err); } guint g_obex_move(GObex *obex, const char *name, const char *dest, GObexResponseFunc func, gpointer user_data, GError **err) { GObexPacket *req; g_obex_debug(G_OBEX_DEBUG_COMMAND, "conn %u", obex->conn_id); req = g_obex_packet_new(G_OBEX_OP_ACTION, TRUE, G_OBEX_HDR_ACTION, G_OBEX_ACTION_MOVE, G_OBEX_HDR_NAME, name, G_OBEX_HDR_DESTNAME, dest, G_OBEX_HDR_INVALID); return g_obex_send_req(obex, req, -1, func, user_data, err); } guint g_obex_abort(GObex *obex, GObexResponseFunc func, gpointer user_data, GError **err) { GObexPacket *req; req = g_obex_packet_new(G_OBEX_OP_ABORT, TRUE, G_OBEX_HDR_INVALID); return g_obex_send_req(obex, req, -1, func, user_data, err); } guint8 g_obex_errno_to_rsp(int err) { switch (err) { case 0: return G_OBEX_RSP_SUCCESS; case -EPERM: case -EACCES: return G_OBEX_RSP_FORBIDDEN; case -ENOENT: return G_OBEX_RSP_NOT_FOUND; case -EINVAL: case -EBADR: return G_OBEX_RSP_BAD_REQUEST; case -EFAULT: return G_OBEX_RSP_SERVICE_UNAVAILABLE; case -ENOSYS: return G_OBEX_RSP_NOT_IMPLEMENTED; case -ENOTEMPTY: case -EEXIST: return G_OBEX_RSP_PRECONDITION_FAILED; default: return G_OBEX_RSP_INTERNAL_SERVER_ERROR; } } bluez-5.82/gobex/PaxHeaders/gobex-transfer.c0000644000000000000000000000005014015011623016035 xustar0020 atime=1743516292 20 ctime=1743591283 bluez-5.82/gobex/gobex-transfer.c0000644000000000000000000003660214015011623015525 0ustar00rootroot// SPDX-License-Identifier: GPL-2.0-or-later /* * * OBEX library with GLib integration * * Copyright (C) 2011 Intel Corporation. All rights reserved. * */ #ifdef HAVE_CONFIG_H #include #endif #include #include #include "gobex/gobex.h" #include "gobex/gobex-debug.h" #define FIRST_PACKET_TIMEOUT 60 static GSList *transfers = NULL; static void transfer_response(GObex *obex, GError *err, GObexPacket *rsp, gpointer user_data); struct transfer { guint id; guint8 opcode; GObex *obex; guint req_id; guint put_id; guint get_id; guint abort_id; GObexDataProducer data_producer; GObexDataConsumer data_consumer; GObexFunc complete_func; gpointer user_data; }; static void transfer_free(struct transfer *transfer) { g_obex_debug(G_OBEX_DEBUG_TRANSFER, "transfer %u", transfer->id); transfers = g_slist_remove(transfers, transfer); if (transfer->req_id > 0) g_obex_cancel_req(transfer->obex, transfer->req_id, TRUE); if (transfer->put_id > 0) g_obex_remove_request_function(transfer->obex, transfer->put_id); if (transfer->get_id > 0) g_obex_remove_request_function(transfer->obex, transfer->get_id); if (transfer->abort_id > 0) g_obex_remove_request_function(transfer->obex, transfer->abort_id); g_obex_unref(transfer->obex); g_free(transfer); } static struct transfer *find_transfer(guint id) { GSList *l; for (l = transfers; l != NULL; l = g_slist_next(l)) { struct transfer *t = l->data; if (t->id == id) return t; } return NULL; } static void transfer_complete(struct transfer *transfer, GError *err) { guint id = transfer->id; g_obex_debug(G_OBEX_DEBUG_TRANSFER, "transfer %u", id); if (err) { /* No further tx must be performed */ g_obex_drop_tx_queue(transfer->obex); } transfer->complete_func(transfer->obex, err, transfer->user_data); /* Check if the complete_func removed the transfer */ if (find_transfer(id) == NULL) return; transfer_free(transfer); } static void transfer_abort_response(GObex *obex, GError *err, GObexPacket *rsp, gpointer user_data) { struct transfer *transfer = user_data; g_obex_debug(G_OBEX_DEBUG_TRANSFER, "transfer %u", transfer->id); transfer->req_id = 0; /* Intentionally override error */ err = g_error_new(G_OBEX_ERROR, G_OBEX_ERROR_CANCELLED, "Operation was aborted"); g_obex_debug(G_OBEX_DEBUG_ERROR, "%s", err->message); transfer_complete(transfer, err); g_error_free(err); } static gssize put_get_data(void *buf, gsize len, gpointer user_data) { struct transfer *transfer = user_data; GObexPacket *req; GError *err = NULL; gssize ret; ret = transfer->data_producer(buf, len, transfer->user_data); if (ret == 0 || ret == -EAGAIN) return ret; if (ret > 0) { /* Check if SRM is active */ if (!g_obex_srm_active(transfer->obex)) return ret; /* Generate next packet */ req = g_obex_packet_new(transfer->opcode, FALSE, G_OBEX_HDR_INVALID); g_obex_packet_add_body(req, put_get_data, transfer); transfer->req_id = g_obex_send_req(transfer->obex, req, -1, transfer_response, transfer, &err); goto done; } transfer->req_id = g_obex_abort(transfer->obex, transfer_abort_response, transfer, &err); done: if (err != NULL) { transfer_complete(transfer, err); g_error_free(err); } return ret; } static gboolean handle_get_body(struct transfer *transfer, GObexPacket *rsp, GError **err) { GObexHeader *body = g_obex_packet_get_body(rsp); gboolean ret; const guint8 *buf; gsize len; if (body == NULL) return TRUE; g_obex_header_get_bytes(body, &buf, &len); if (len == 0) return TRUE; ret = transfer->data_consumer(buf, len, transfer->user_data); if (ret == FALSE) g_set_error(err, G_OBEX_ERROR, G_OBEX_ERROR_CANCELLED, "Data consumer callback failed"); return ret; } static void transfer_response(GObex *obex, GError *err, GObexPacket *rsp, gpointer user_data) { struct transfer *transfer = user_data; GObexPacket *req; gboolean rspcode, final; guint id; g_obex_debug(G_OBEX_DEBUG_TRANSFER, "transfer %u", transfer->id); id = transfer->req_id; transfer->req_id = 0; if (err != NULL) { transfer_complete(transfer, err); return; } rspcode = g_obex_packet_get_operation(rsp, &final); if (rspcode != G_OBEX_RSP_SUCCESS && rspcode != G_OBEX_RSP_CONTINUE) { err = g_error_new(G_OBEX_ERROR, rspcode, "%s", g_obex_strerror(rspcode)); goto failed; } if (transfer->opcode == G_OBEX_OP_GET) { handle_get_body(transfer, rsp, &err); if (err != NULL) goto failed; } if (rspcode == G_OBEX_RSP_SUCCESS) { transfer_complete(transfer, NULL); return; } if (transfer->opcode == G_OBEX_OP_PUT) { req = g_obex_packet_new(transfer->opcode, FALSE, G_OBEX_HDR_INVALID); g_obex_packet_add_body(req, put_get_data, transfer); } else if (!g_obex_srm_active(transfer->obex)) { req = g_obex_packet_new(transfer->opcode, TRUE, G_OBEX_HDR_INVALID); } else { /* Keep id since request still outstanting */ transfer->req_id = id; return; } transfer->req_id = g_obex_send_req(obex, req, -1, transfer_response, transfer, &err); failed: if (err != NULL) { g_obex_debug(G_OBEX_DEBUG_ERROR, "%s", err->message); transfer_complete(transfer, err); g_error_free(err); } } static struct transfer *transfer_new(GObex *obex, guint8 opcode, GObexFunc complete_func, gpointer user_data) { static guint next_id = 1; struct transfer *transfer; g_obex_debug(G_OBEX_DEBUG_TRANSFER, "obex %p opcode %u", obex, opcode); transfer = g_new0(struct transfer, 1); transfer->id = next_id++; transfer->opcode = opcode; transfer->obex = g_obex_ref(obex); transfer->complete_func = complete_func; transfer->user_data = user_data; transfers = g_slist_append(transfers, transfer); return transfer; } guint g_obex_put_req_pkt(GObex *obex, GObexPacket *req, GObexDataProducer data_func, GObexFunc complete_func, gpointer user_data, GError **err) { struct transfer *transfer; g_obex_debug(G_OBEX_DEBUG_TRANSFER, "obex %p", obex); if (g_obex_packet_get_operation(req, NULL) != G_OBEX_OP_PUT) return 0; transfer = transfer_new(obex, G_OBEX_OP_PUT, complete_func, user_data); transfer->data_producer = data_func; g_obex_packet_add_body(req, put_get_data, transfer); transfer->req_id = g_obex_send_req(obex, req, FIRST_PACKET_TIMEOUT, transfer_response, transfer, err); if (transfer->req_id == 0) { transfer_free(transfer); return 0; } g_obex_debug(G_OBEX_DEBUG_TRANSFER, "transfer %u", transfer->id); return transfer->id; } guint g_obex_put_req(GObex *obex, GObexDataProducer data_func, GObexFunc complete_func, gpointer user_data, GError **err, guint first_hdr_id, ...) { GObexPacket *req; va_list args; g_obex_debug(G_OBEX_DEBUG_TRANSFER, "obex %p", obex); va_start(args, first_hdr_id); req = g_obex_packet_new_valist(G_OBEX_OP_PUT, FALSE, first_hdr_id, args); va_end(args); return g_obex_put_req_pkt(obex, req, data_func, complete_func, user_data, err); } static void transfer_abort_req(GObex *obex, GObexPacket *req, gpointer user_data) { struct transfer *transfer = user_data; GObexPacket *rsp; GError *err; g_obex_debug(G_OBEX_DEBUG_TRANSFER, "transfer %u", transfer->id); err = g_error_new(G_OBEX_ERROR, G_OBEX_ERROR_CANCELLED, "Request was aborted"); rsp = g_obex_packet_new(G_OBEX_RSP_SUCCESS, TRUE, G_OBEX_HDR_INVALID); g_obex_send(obex, rsp, NULL); transfer_complete(transfer, err); g_error_free(err); } static guint8 put_get_bytes(struct transfer *transfer, GObexPacket *req) { GObexHeader *body; gboolean final; guint8 rsp; const guint8 *buf; gsize len; g_obex_debug(G_OBEX_DEBUG_TRANSFER, "transfer %u", transfer->id); g_obex_packet_get_operation(req, &final); if (final) rsp = G_OBEX_RSP_SUCCESS; else rsp = G_OBEX_RSP_CONTINUE; body = g_obex_packet_get_body(req); if (body == NULL) return rsp; g_obex_header_get_bytes(body, &buf, &len); if (len == 0) return rsp; if (transfer->data_consumer(buf, len, transfer->user_data) == FALSE) rsp = G_OBEX_RSP_FORBIDDEN; return rsp; } static void transfer_put_req_first(struct transfer *transfer, GObexPacket *req, guint8 first_hdr_id, va_list args) { GError *err = NULL; GObexPacket *rsp; guint8 rspcode; g_obex_debug(G_OBEX_DEBUG_TRANSFER, "transfer %u", transfer->id); rspcode = put_get_bytes(transfer, req); rsp = g_obex_packet_new_valist(rspcode, TRUE, first_hdr_id, args); if (!g_obex_send(transfer->obex, rsp, &err)) { transfer_complete(transfer, err); g_error_free(err); return; } if (rspcode != G_OBEX_RSP_CONTINUE) transfer_complete(transfer, NULL); } static void transfer_put_req(GObex *obex, GObexPacket *req, gpointer user_data) { struct transfer *transfer = user_data; GError *err = NULL; GObexPacket *rsp; guint8 rspcode; g_obex_debug(G_OBEX_DEBUG_TRANSFER, "transfer %u", transfer->id); rspcode = put_get_bytes(transfer, req); /* Don't send continue while SRM is active */ if (g_obex_srm_active(transfer->obex) && rspcode == G_OBEX_RSP_CONTINUE) goto done; rsp = g_obex_packet_new(rspcode, TRUE, G_OBEX_HDR_INVALID); if (!g_obex_send(obex, rsp, &err)) { transfer_complete(transfer, err); g_error_free(err); return; } done: if (rspcode != G_OBEX_RSP_CONTINUE) transfer_complete(transfer, NULL); } guint g_obex_put_rsp(GObex *obex, GObexPacket *req, GObexDataConsumer data_func, GObexFunc complete_func, gpointer user_data, GError **err, guint first_hdr_id, ...) { struct transfer *transfer; va_list args; guint id; g_obex_debug(G_OBEX_DEBUG_TRANSFER, "obex %p", obex); transfer = transfer_new(obex, G_OBEX_OP_PUT, complete_func, user_data); transfer->data_consumer = data_func; va_start(args, first_hdr_id); transfer_put_req_first(transfer, req, first_hdr_id, args); va_end(args); if (!g_slist_find(transfers, transfer)) return 0; id = g_obex_add_request_function(obex, G_OBEX_OP_PUT, transfer_put_req, transfer); transfer->put_id = id; id = g_obex_add_request_function(obex, G_OBEX_OP_ABORT, transfer_abort_req, transfer); transfer->abort_id = id; g_obex_debug(G_OBEX_DEBUG_TRANSFER, "transfer %u", transfer->id); return transfer->id; } guint g_obex_get_req_pkt(GObex *obex, GObexPacket *req, GObexDataConsumer data_func, GObexFunc complete_func, gpointer user_data, GError **err) { struct transfer *transfer; g_obex_debug(G_OBEX_DEBUG_TRANSFER, "obex %p", obex); if (g_obex_packet_get_operation(req, NULL) != G_OBEX_OP_GET) return 0; transfer = transfer_new(obex, G_OBEX_OP_GET, complete_func, user_data); transfer->data_consumer = data_func; transfer->req_id = g_obex_send_req(obex, req, FIRST_PACKET_TIMEOUT, transfer_response, transfer, err); if (transfer->req_id == 0) { transfer_free(transfer); return 0; } g_obex_debug(G_OBEX_DEBUG_TRANSFER, "transfer %u", transfer->id); return transfer->id; } guint g_obex_get_req(GObex *obex, GObexDataConsumer data_func, GObexFunc complete_func, gpointer user_data, GError **err, guint first_hdr_id, ...) { struct transfer *transfer; GObexPacket *req; va_list args; g_obex_debug(G_OBEX_DEBUG_TRANSFER, "obex %p", obex); transfer = transfer_new(obex, G_OBEX_OP_GET, complete_func, user_data); transfer->data_consumer = data_func; va_start(args, first_hdr_id); req = g_obex_packet_new_valist(G_OBEX_OP_GET, TRUE, first_hdr_id, args); va_end(args); transfer->req_id = g_obex_send_req(obex, req, FIRST_PACKET_TIMEOUT, transfer_response, transfer, err); if (transfer->req_id == 0) { transfer_free(transfer); return 0; } g_obex_debug(G_OBEX_DEBUG_TRANSFER, "transfer %u", transfer->id); return transfer->id; } static gssize get_get_data(void *buf, gsize len, gpointer user_data) { struct transfer *transfer = user_data; GObexPacket *req, *rsp; GError *err = NULL; gssize ret; guint8 op; g_obex_debug(G_OBEX_DEBUG_TRANSFER, "transfer %u", transfer->id); ret = transfer->data_producer(buf, len, transfer->user_data); if (ret > 0) { if (!g_obex_srm_active(transfer->obex)) return ret; /* Generate next response */ rsp = g_obex_packet_new(G_OBEX_RSP_CONTINUE, TRUE, G_OBEX_HDR_INVALID); g_obex_packet_add_body(rsp, get_get_data, transfer); if (!g_obex_send(transfer->obex, rsp, &err)) { transfer_complete(transfer, err); g_error_free(err); } return ret; } if (ret == -EAGAIN) return ret; if (ret == 0) { transfer_complete(transfer, NULL); return ret; } op = g_obex_errno_to_rsp(ret); req = g_obex_packet_new(op, TRUE, G_OBEX_HDR_INVALID); g_obex_send(transfer->obex, req, NULL); err = g_error_new(G_OBEX_ERROR, G_OBEX_ERROR_CANCELLED, "Data producer function failed"); g_obex_debug(G_OBEX_DEBUG_ERROR, "%s", err->message); transfer_complete(transfer, err); g_error_free(err); return ret; } static gboolean transfer_get_req_first(struct transfer *transfer, GObexPacket *rsp) { GError *err = NULL; g_obex_debug(G_OBEX_DEBUG_TRANSFER, "transfer %u", transfer->id); g_obex_packet_add_body(rsp, get_get_data, transfer); if (!g_obex_send(transfer->obex, rsp, &err)) { transfer_complete(transfer, err); g_error_free(err); return FALSE; } return TRUE; } static void transfer_get_req(GObex *obex, GObexPacket *req, gpointer user_data) { struct transfer *transfer = user_data; GError *err = NULL; GObexPacket *rsp; g_obex_debug(G_OBEX_DEBUG_TRANSFER, "transfer %u", transfer->id); rsp = g_obex_packet_new(G_OBEX_RSP_CONTINUE, TRUE, G_OBEX_HDR_INVALID); g_obex_packet_add_body(rsp, get_get_data, transfer); if (!g_obex_send(obex, rsp, &err)) { transfer_complete(transfer, err); g_error_free(err); } } guint g_obex_get_rsp_pkt(GObex *obex, GObexPacket *rsp, GObexDataProducer data_func, GObexFunc complete_func, gpointer user_data, GError **err) { struct transfer *transfer; guint id; g_obex_debug(G_OBEX_DEBUG_TRANSFER, "obex %p", obex); transfer = transfer_new(obex, G_OBEX_OP_GET, complete_func, user_data); transfer->data_producer = data_func; if (!transfer_get_req_first(transfer, rsp)) return 0; if (!g_slist_find(transfers, transfer)) return 0; id = g_obex_add_request_function(obex, G_OBEX_OP_GET, transfer_get_req, transfer); transfer->get_id = id; id = g_obex_add_request_function(obex, G_OBEX_OP_ABORT, transfer_abort_req, transfer); transfer->abort_id = id; g_obex_debug(G_OBEX_DEBUG_TRANSFER, "transfer %u", transfer->id); return transfer->id; } guint g_obex_get_rsp(GObex *obex, GObexDataProducer data_func, GObexFunc complete_func, gpointer user_data, GError **err, guint first_hdr_id, ...) { GObexPacket *rsp; va_list args; g_obex_debug(G_OBEX_DEBUG_TRANSFER, "obex %p", obex); va_start(args, first_hdr_id); rsp = g_obex_packet_new_valist(G_OBEX_RSP_CONTINUE, TRUE, first_hdr_id, args); va_end(args); return g_obex_get_rsp_pkt(obex, rsp, data_func, complete_func, user_data, err); } gboolean g_obex_cancel_transfer(guint id, GObexFunc complete_func, gpointer user_data) { struct transfer *transfer = NULL; gboolean ret = TRUE; g_obex_debug(G_OBEX_DEBUG_TRANSFER, "transfer %u", id); transfer = find_transfer(id); if (transfer == NULL) return FALSE; if (complete_func == NULL) goto done; transfer->complete_func = complete_func; transfer->user_data = user_data; if (!transfer->req_id) { transfer->req_id = g_obex_abort(transfer->obex, transfer_abort_response, transfer, NULL); if (transfer->req_id) return TRUE; } ret = g_obex_cancel_req(transfer->obex, transfer->req_id, FALSE); if (ret) return TRUE; done: transfer_free(transfer); return ret; } bluez-5.82/PaxHeaders/lib0000644000000000000000000000005014773213555012353 xustar0020 atime=1743591291 20 ctime=1743591277 bluez-5.82/lib/0000755000000000000000000000000014773213555012111 5ustar00rootrootbluez-5.82/lib/PaxHeaders/sdp.h0000644000000000000000000000005014015011623013344 xustar0020 atime=1743515801 20 ctime=1743591275 bluez-5.82/lib/sdp.h0000644000000000000000000004215514015011623013034 0ustar00rootroot/* SPDX-License-Identifier: GPL-2.0-or-later */ /* * * BlueZ - Bluetooth protocol stack for Linux * * Copyright (C) 2001-2002 Nokia Corporation * Copyright (C) 2002-2003 Maxim Krasnyansky * Copyright (C) 2002-2010 Marcel Holtmann * Copyright (C) 2002-2003 Stephen Crane * * */ #ifndef __SDP_H #define __SDP_H #ifdef __cplusplus extern "C" { #endif #include #include #define SDP_UNIX_PATH "/var/run/sdp" #define SDP_RESPONSE_TIMEOUT 20 #define SDP_REQ_BUFFER_SIZE 2048 #define SDP_RSP_BUFFER_SIZE 65535 #define SDP_PDU_CHUNK_SIZE 1024 /* * All definitions are based on Bluetooth Assigned Numbers * of the Bluetooth Specification */ #define SDP_PSM 0x0001 /* * Protocol UUIDs */ #define SDP_UUID 0x0001 #define UDP_UUID 0x0002 #define RFCOMM_UUID 0x0003 #define TCP_UUID 0x0004 #define TCS_BIN_UUID 0x0005 #define TCS_AT_UUID 0x0006 #define ATT_UUID 0x0007 #define OBEX_UUID 0x0008 #define IP_UUID 0x0009 #define FTP_UUID 0x000a #define HTTP_UUID 0x000c #define WSP_UUID 0x000e #define BNEP_UUID 0x000f #define UPNP_UUID 0x0010 #define HIDP_UUID 0x0011 #define HCRP_CTRL_UUID 0x0012 #define HCRP_DATA_UUID 0x0014 #define HCRP_NOTE_UUID 0x0016 #define AVCTP_UUID 0x0017 #define AVDTP_UUID 0x0019 #define CMTP_UUID 0x001b #define UDI_UUID 0x001d #define MCAP_CTRL_UUID 0x001e #define MCAP_DATA_UUID 0x001f #define L2CAP_UUID 0x0100 /* * Service class identifiers of standard services and service groups */ #define SDP_SERVER_SVCLASS_ID 0x1000 #define BROWSE_GRP_DESC_SVCLASS_ID 0x1001 #define PUBLIC_BROWSE_GROUP 0x1002 #define SERIAL_PORT_SVCLASS_ID 0x1101 #define LAN_ACCESS_SVCLASS_ID 0x1102 #define DIALUP_NET_SVCLASS_ID 0x1103 #define IRMC_SYNC_SVCLASS_ID 0x1104 #define OBEX_OBJPUSH_SVCLASS_ID 0x1105 #define OBEX_FILETRANS_SVCLASS_ID 0x1106 #define IRMC_SYNC_CMD_SVCLASS_ID 0x1107 #define HEADSET_SVCLASS_ID 0x1108 #define CORDLESS_TELEPHONY_SVCLASS_ID 0x1109 #define AUDIO_SOURCE_SVCLASS_ID 0x110a #define AUDIO_SINK_SVCLASS_ID 0x110b #define AV_REMOTE_TARGET_SVCLASS_ID 0x110c #define ADVANCED_AUDIO_SVCLASS_ID 0x110d #define AV_REMOTE_SVCLASS_ID 0x110e #define AV_REMOTE_CONTROLLER_SVCLASS_ID 0x110f #define INTERCOM_SVCLASS_ID 0x1110 #define FAX_SVCLASS_ID 0x1111 #define HEADSET_AGW_SVCLASS_ID 0x1112 #define WAP_SVCLASS_ID 0x1113 #define WAP_CLIENT_SVCLASS_ID 0x1114 #define PANU_SVCLASS_ID 0x1115 #define NAP_SVCLASS_ID 0x1116 #define GN_SVCLASS_ID 0x1117 #define DIRECT_PRINTING_SVCLASS_ID 0x1118 #define REFERENCE_PRINTING_SVCLASS_ID 0x1119 #define IMAGING_SVCLASS_ID 0x111a #define IMAGING_RESPONDER_SVCLASS_ID 0x111b #define IMAGING_ARCHIVE_SVCLASS_ID 0x111c #define IMAGING_REFOBJS_SVCLASS_ID 0x111d #define HANDSFREE_SVCLASS_ID 0x111e #define HANDSFREE_AGW_SVCLASS_ID 0x111f #define DIRECT_PRT_REFOBJS_SVCLASS_ID 0x1120 #define REFLECTED_UI_SVCLASS_ID 0x1121 #define BASIC_PRINTING_SVCLASS_ID 0x1122 #define PRINTING_STATUS_SVCLASS_ID 0x1123 #define HID_SVCLASS_ID 0x1124 #define HCR_SVCLASS_ID 0x1125 #define HCR_PRINT_SVCLASS_ID 0x1126 #define HCR_SCAN_SVCLASS_ID 0x1127 #define CIP_SVCLASS_ID 0x1128 #define VIDEO_CONF_GW_SVCLASS_ID 0x1129 #define UDI_MT_SVCLASS_ID 0x112a #define UDI_TA_SVCLASS_ID 0x112b #define AV_SVCLASS_ID 0x112c #define SAP_SVCLASS_ID 0x112d #define PBAP_PCE_SVCLASS_ID 0x112e #define PBAP_PSE_SVCLASS_ID 0x112f #define PBAP_SVCLASS_ID 0x1130 #define MAP_MSE_SVCLASS_ID 0x1132 #define MAP_MCE_SVCLASS_ID 0x1133 #define MAP_SVCLASS_ID 0x1134 #define GNSS_SVCLASS_ID 0x1135 #define GNSS_SERVER_SVCLASS_ID 0x1136 #define MPS_SC_SVCLASS_ID 0x113A #define MPS_SVCLASS_ID 0x113B #define PNP_INFO_SVCLASS_ID 0x1200 #define GENERIC_NETWORKING_SVCLASS_ID 0x1201 #define GENERIC_FILETRANS_SVCLASS_ID 0x1202 #define GENERIC_AUDIO_SVCLASS_ID 0x1203 #define GENERIC_TELEPHONY_SVCLASS_ID 0x1204 #define UPNP_SVCLASS_ID 0x1205 #define UPNP_IP_SVCLASS_ID 0x1206 #define UPNP_PAN_SVCLASS_ID 0x1300 #define UPNP_LAP_SVCLASS_ID 0x1301 #define UPNP_L2CAP_SVCLASS_ID 0x1302 #define VIDEO_SOURCE_SVCLASS_ID 0x1303 #define VIDEO_SINK_SVCLASS_ID 0x1304 #define VIDEO_DISTRIBUTION_SVCLASS_ID 0x1305 #define HDP_SVCLASS_ID 0x1400 #define HDP_SOURCE_SVCLASS_ID 0x1401 #define HDP_SINK_SVCLASS_ID 0x1402 #define GENERIC_ACCESS_SVCLASS_ID 0x1800 #define GENERIC_ATTRIB_SVCLASS_ID 0x1801 #define APPLE_AGENT_SVCLASS_ID 0x2112 /* * Standard profile descriptor identifiers; note these * may be identical to some of the service classes defined above */ #define SDP_SERVER_PROFILE_ID SDP_SERVER_SVCLASS_ID #define BROWSE_GRP_DESC_PROFILE_ID BROWSE_GRP_DESC_SVCLASS_ID #define SERIAL_PORT_PROFILE_ID SERIAL_PORT_SVCLASS_ID #define LAN_ACCESS_PROFILE_ID LAN_ACCESS_SVCLASS_ID #define DIALUP_NET_PROFILE_ID DIALUP_NET_SVCLASS_ID #define IRMC_SYNC_PROFILE_ID IRMC_SYNC_SVCLASS_ID #define OBEX_OBJPUSH_PROFILE_ID OBEX_OBJPUSH_SVCLASS_ID #define OBEX_FILETRANS_PROFILE_ID OBEX_FILETRANS_SVCLASS_ID #define IRMC_SYNC_CMD_PROFILE_ID IRMC_SYNC_CMD_SVCLASS_ID #define HEADSET_PROFILE_ID HEADSET_SVCLASS_ID #define CORDLESS_TELEPHONY_PROFILE_ID CORDLESS_TELEPHONY_SVCLASS_ID #define AUDIO_SOURCE_PROFILE_ID AUDIO_SOURCE_SVCLASS_ID #define AUDIO_SINK_PROFILE_ID AUDIO_SINK_SVCLASS_ID #define AV_REMOTE_TARGET_PROFILE_ID AV_REMOTE_TARGET_SVCLASS_ID #define ADVANCED_AUDIO_PROFILE_ID ADVANCED_AUDIO_SVCLASS_ID #define AV_REMOTE_PROFILE_ID AV_REMOTE_SVCLASS_ID #define INTERCOM_PROFILE_ID INTERCOM_SVCLASS_ID #define FAX_PROFILE_ID FAX_SVCLASS_ID #define HEADSET_AGW_PROFILE_ID HEADSET_AGW_SVCLASS_ID #define WAP_PROFILE_ID WAP_SVCLASS_ID #define WAP_CLIENT_PROFILE_ID WAP_CLIENT_SVCLASS_ID #define PANU_PROFILE_ID PANU_SVCLASS_ID #define NAP_PROFILE_ID NAP_SVCLASS_ID #define GN_PROFILE_ID GN_SVCLASS_ID #define DIRECT_PRINTING_PROFILE_ID DIRECT_PRINTING_SVCLASS_ID #define REFERENCE_PRINTING_PROFILE_ID REFERENCE_PRINTING_SVCLASS_ID #define IMAGING_PROFILE_ID IMAGING_SVCLASS_ID #define IMAGING_RESPONDER_PROFILE_ID IMAGING_RESPONDER_SVCLASS_ID #define IMAGING_ARCHIVE_PROFILE_ID IMAGING_ARCHIVE_SVCLASS_ID #define IMAGING_REFOBJS_PROFILE_ID IMAGING_REFOBJS_SVCLASS_ID #define HANDSFREE_PROFILE_ID HANDSFREE_SVCLASS_ID #define HANDSFREE_AGW_PROFILE_ID HANDSFREE_AGW_SVCLASS_ID #define DIRECT_PRT_REFOBJS_PROFILE_ID DIRECT_PRT_REFOBJS_SVCLASS_ID #define REFLECTED_UI_PROFILE_ID REFLECTED_UI_SVCLASS_ID #define BASIC_PRINTING_PROFILE_ID BASIC_PRINTING_SVCLASS_ID #define PRINTING_STATUS_PROFILE_ID PRINTING_STATUS_SVCLASS_ID #define HID_PROFILE_ID HID_SVCLASS_ID #define HCR_PROFILE_ID HCR_SCAN_SVCLASS_ID #define HCR_PRINT_PROFILE_ID HCR_PRINT_SVCLASS_ID #define HCR_SCAN_PROFILE_ID HCR_SCAN_SVCLASS_ID #define CIP_PROFILE_ID CIP_SVCLASS_ID #define VIDEO_CONF_GW_PROFILE_ID VIDEO_CONF_GW_SVCLASS_ID #define UDI_MT_PROFILE_ID UDI_MT_SVCLASS_ID #define UDI_TA_PROFILE_ID UDI_TA_SVCLASS_ID #define AV_PROFILE_ID AV_SVCLASS_ID #define SAP_PROFILE_ID SAP_SVCLASS_ID #define PBAP_PCE_PROFILE_ID PBAP_PCE_SVCLASS_ID #define PBAP_PSE_PROFILE_ID PBAP_PSE_SVCLASS_ID #define PBAP_PROFILE_ID PBAP_SVCLASS_ID #define MAP_PROFILE_ID MAP_SVCLASS_ID #define PNP_INFO_PROFILE_ID PNP_INFO_SVCLASS_ID #define GENERIC_NETWORKING_PROFILE_ID GENERIC_NETWORKING_SVCLASS_ID #define GENERIC_FILETRANS_PROFILE_ID GENERIC_FILETRANS_SVCLASS_ID #define GENERIC_AUDIO_PROFILE_ID GENERIC_AUDIO_SVCLASS_ID #define GENERIC_TELEPHONY_PROFILE_ID GENERIC_TELEPHONY_SVCLASS_ID #define UPNP_PROFILE_ID UPNP_SVCLASS_ID #define UPNP_IP_PROFILE_ID UPNP_IP_SVCLASS_ID #define UPNP_PAN_PROFILE_ID UPNP_PAN_SVCLASS_ID #define UPNP_LAP_PROFILE_ID UPNP_LAP_SVCLASS_ID #define UPNP_L2CAP_PROFILE_ID UPNP_L2CAP_SVCLASS_ID #define VIDEO_SOURCE_PROFILE_ID VIDEO_SOURCE_SVCLASS_ID #define VIDEO_SINK_PROFILE_ID VIDEO_SINK_SVCLASS_ID #define VIDEO_DISTRIBUTION_PROFILE_ID VIDEO_DISTRIBUTION_SVCLASS_ID #define HDP_PROFILE_ID HDP_SVCLASS_ID #define HDP_SOURCE_PROFILE_ID HDP_SOURCE_SVCLASS_ID #define HDP_SINK_PROFILE_ID HDP_SINK_SVCLASS_ID #define GENERIC_ACCESS_PROFILE_ID GENERIC_ACCESS_SVCLASS_ID #define GENERIC_ATTRIB_PROFILE_ID GENERIC_ATTRIB_SVCLASS_ID #define APPLE_AGENT_PROFILE_ID APPLE_AGENT_SVCLASS_ID #define MPS_PROFILE_ID MPS_SC_SVCLASS_ID /* * Compatibility macros for the old MDP acronym */ #define MDP_SVCLASS_ID HDP_SVCLASS_ID #define MDP_SOURCE_SVCLASS_ID HDP_SOURCE_SVCLASS_ID #define MDP_SINK_SVCLASS_ID HDP_SINK_SVCLASS_ID #define MDP_PROFILE_ID HDP_PROFILE_ID #define MDP_SOURCE_PROFILE_ID HDP_SOURCE_PROFILE_ID #define MDP_SINK_PROFILE_ID HDP_SINK_PROFILE_ID /* * Attribute identifier codes */ #define SDP_SERVER_RECORD_HANDLE 0x0000 /* * Possible values for attribute-id are listed below. * See SDP Spec, section "Service Attribute Definitions" for more details. */ #define SDP_ATTR_RECORD_HANDLE 0x0000 #define SDP_ATTR_SVCLASS_ID_LIST 0x0001 #define SDP_ATTR_RECORD_STATE 0x0002 #define SDP_ATTR_SERVICE_ID 0x0003 #define SDP_ATTR_PROTO_DESC_LIST 0x0004 #define SDP_ATTR_BROWSE_GRP_LIST 0x0005 #define SDP_ATTR_LANG_BASE_ATTR_ID_LIST 0x0006 #define SDP_ATTR_SVCINFO_TTL 0x0007 #define SDP_ATTR_SERVICE_AVAILABILITY 0x0008 #define SDP_ATTR_PFILE_DESC_LIST 0x0009 #define SDP_ATTR_DOC_URL 0x000a #define SDP_ATTR_CLNT_EXEC_URL 0x000b #define SDP_ATTR_ICON_URL 0x000c #define SDP_ATTR_ADD_PROTO_DESC_LIST 0x000d #define SDP_ATTR_GROUP_ID 0x0200 #define SDP_ATTR_IP_SUBNET 0x0200 #define SDP_ATTR_VERSION_NUM_LIST 0x0200 #define SDP_ATTR_SUPPORTED_FEATURES_LIST 0x0200 #define SDP_ATTR_GOEP_L2CAP_PSM 0x0200 #define SDP_ATTR_SVCDB_STATE 0x0201 #define SDP_ATTR_MPSD_SCENARIOS 0x0200 #define SDP_ATTR_MPMD_SCENARIOS 0x0201 #define SDP_ATTR_MPS_DEPENDENCIES 0x0202 #define SDP_ATTR_SERVICE_VERSION 0x0300 #define SDP_ATTR_EXTERNAL_NETWORK 0x0301 #define SDP_ATTR_SUPPORTED_DATA_STORES_LIST 0x0301 #define SDP_ATTR_DATA_EXCHANGE_SPEC 0x0301 #define SDP_ATTR_NETWORK 0x0301 #define SDP_ATTR_FAX_CLASS1_SUPPORT 0x0302 #define SDP_ATTR_REMOTE_AUDIO_VOLUME_CONTROL 0x0302 #define SDP_ATTR_MCAP_SUPPORTED_PROCEDURES 0x0302 #define SDP_ATTR_FAX_CLASS20_SUPPORT 0x0303 #define SDP_ATTR_SUPPORTED_FORMATS_LIST 0x0303 #define SDP_ATTR_FAX_CLASS2_SUPPORT 0x0304 #define SDP_ATTR_AUDIO_FEEDBACK_SUPPORT 0x0305 #define SDP_ATTR_NETWORK_ADDRESS 0x0306 #define SDP_ATTR_WAP_GATEWAY 0x0307 #define SDP_ATTR_HOMEPAGE_URL 0x0308 #define SDP_ATTR_WAP_STACK_TYPE 0x0309 #define SDP_ATTR_SECURITY_DESC 0x030a #define SDP_ATTR_NET_ACCESS_TYPE 0x030b #define SDP_ATTR_MAX_NET_ACCESSRATE 0x030c #define SDP_ATTR_IP4_SUBNET 0x030d #define SDP_ATTR_IP6_SUBNET 0x030e #define SDP_ATTR_SUPPORTED_CAPABILITIES 0x0310 #define SDP_ATTR_SUPPORTED_FEATURES 0x0311 #define SDP_ATTR_SUPPORTED_FUNCTIONS 0x0312 #define SDP_ATTR_TOTAL_IMAGING_DATA_CAPACITY 0x0313 #define SDP_ATTR_SUPPORTED_REPOSITORIES 0x0314 #define SDP_ATTR_MAS_INSTANCE_ID 0x0315 #define SDP_ATTR_SUPPORTED_MESSAGE_TYPES 0x0316 #define SDP_ATTR_PBAP_SUPPORTED_FEATURES 0x0317 #define SDP_ATTR_MAP_SUPPORTED_FEATURES 0x0317 #define SDP_ATTR_SPECIFICATION_ID 0x0200 #define SDP_ATTR_VENDOR_ID 0x0201 #define SDP_ATTR_PRODUCT_ID 0x0202 #define SDP_ATTR_VERSION 0x0203 #define SDP_ATTR_PRIMARY_RECORD 0x0204 #define SDP_ATTR_VENDOR_ID_SOURCE 0x0205 #define SDP_ATTR_HID_DEVICE_RELEASE_NUMBER 0x0200 #define SDP_ATTR_HID_PARSER_VERSION 0x0201 #define SDP_ATTR_HID_DEVICE_SUBCLASS 0x0202 #define SDP_ATTR_HID_COUNTRY_CODE 0x0203 #define SDP_ATTR_HID_VIRTUAL_CABLE 0x0204 #define SDP_ATTR_HID_RECONNECT_INITIATE 0x0205 #define SDP_ATTR_HID_DESCRIPTOR_LIST 0x0206 #define SDP_ATTR_HID_LANG_ID_BASE_LIST 0x0207 #define SDP_ATTR_HID_SDP_DISABLE 0x0208 #define SDP_ATTR_HID_BATTERY_POWER 0x0209 #define SDP_ATTR_HID_REMOTE_WAKEUP 0x020a #define SDP_ATTR_HID_PROFILE_VERSION 0x020b #define SDP_ATTR_HID_SUPERVISION_TIMEOUT 0x020c #define SDP_ATTR_HID_NORMALLY_CONNECTABLE 0x020d #define SDP_ATTR_HID_BOOT_DEVICE 0x020e /* * These identifiers are based on the SDP spec stating that * "base attribute id of the primary (universal) language must be 0x0100" * * Other languages should have their own offset; e.g.: * #define XXXLangBase yyyy * #define AttrServiceName_XXX 0x0000+XXXLangBase */ #define SDP_PRIMARY_LANG_BASE 0x0100 #define SDP_ATTR_SVCNAME_PRIMARY 0x0000 + SDP_PRIMARY_LANG_BASE #define SDP_ATTR_SVCDESC_PRIMARY 0x0001 + SDP_PRIMARY_LANG_BASE #define SDP_ATTR_PROVNAME_PRIMARY 0x0002 + SDP_PRIMARY_LANG_BASE /* * The Data representation in SDP PDUs (pps 339, 340 of BT SDP Spec) * These are the exact data type+size descriptor values * that go into the PDU buffer. * * The datatype (leading 5bits) + size descriptor (last 3 bits) * is 8 bits. The size descriptor is critical to extract the * right number of bytes for the data value from the PDU. * * For most basic types, the datatype+size descriptor is * straightforward. However for constructed types and strings, * the size of the data is in the next "n" bytes following the * 8 bits (datatype+size) descriptor. Exactly what the "n" is * specified in the 3 bits of the data size descriptor. * * TextString and URLString can be of size 2^{8, 16, 32} bytes * DataSequence and DataSequenceAlternates can be of size 2^{8, 16, 32} * The size are computed post-facto in the API and are not known apriori */ #define SDP_DATA_NIL 0x00 #define SDP_UINT8 0x08 #define SDP_UINT16 0x09 #define SDP_UINT32 0x0A #define SDP_UINT64 0x0B #define SDP_UINT128 0x0C #define SDP_INT8 0x10 #define SDP_INT16 0x11 #define SDP_INT32 0x12 #define SDP_INT64 0x13 #define SDP_INT128 0x14 #define SDP_UUID_UNSPEC 0x18 #define SDP_UUID16 0x19 #define SDP_UUID32 0x1A #define SDP_UUID128 0x1C #define SDP_TEXT_STR_UNSPEC 0x20 #define SDP_TEXT_STR8 0x25 #define SDP_TEXT_STR16 0x26 #define SDP_TEXT_STR32 0x27 #define SDP_BOOL 0x28 #define SDP_SEQ_UNSPEC 0x30 #define SDP_SEQ8 0x35 #define SDP_SEQ16 0x36 #define SDP_SEQ32 0x37 #define SDP_ALT_UNSPEC 0x38 #define SDP_ALT8 0x3D #define SDP_ALT16 0x3E #define SDP_ALT32 0x3F #define SDP_URL_STR_UNSPEC 0x40 #define SDP_URL_STR8 0x45 #define SDP_URL_STR16 0x46 #define SDP_URL_STR32 0x47 /* * The PDU identifiers of SDP packets between client and server */ #define SDP_ERROR_RSP 0x01 #define SDP_SVC_SEARCH_REQ 0x02 #define SDP_SVC_SEARCH_RSP 0x03 #define SDP_SVC_ATTR_REQ 0x04 #define SDP_SVC_ATTR_RSP 0x05 #define SDP_SVC_SEARCH_ATTR_REQ 0x06 #define SDP_SVC_SEARCH_ATTR_RSP 0x07 /* * Some additions to support service registration. * These are outside the scope of the Bluetooth specification */ #define SDP_SVC_REGISTER_REQ 0x75 #define SDP_SVC_REGISTER_RSP 0x76 #define SDP_SVC_UPDATE_REQ 0x77 #define SDP_SVC_UPDATE_RSP 0x78 #define SDP_SVC_REMOVE_REQ 0x79 #define SDP_SVC_REMOVE_RSP 0x80 /* * SDP Error codes */ #define SDP_INVALID_VERSION 0x0001 #define SDP_INVALID_RECORD_HANDLE 0x0002 #define SDP_INVALID_SYNTAX 0x0003 #define SDP_INVALID_PDU_SIZE 0x0004 #define SDP_INVALID_CSTATE 0x0005 /* * SDP PDU */ typedef struct { uint8_t pdu_id; uint16_t tid; uint16_t plen; } __attribute__ ((packed)) sdp_pdu_hdr_t; /* * Common definitions for attributes in the SDP. * Should the type of any of these change, you need only make a change here. */ typedef struct { uint8_t type; union { uint16_t uuid16; uint32_t uuid32; uint128_t uuid128; } value; } uuid_t; #define SDP_IS_UUID(x) ((x) == SDP_UUID16 || (x) == SDP_UUID32 || \ (x) == SDP_UUID128) #define SDP_IS_ALT(x) ((x) == SDP_ALT8 || (x) == SDP_ALT16 || (x) == SDP_ALT32) #define SDP_IS_SEQ(x) ((x) == SDP_SEQ8 || (x) == SDP_SEQ16 || (x) == SDP_SEQ32) #define SDP_IS_TEXT_STR(x) ((x) == SDP_TEXT_STR8 || (x) == SDP_TEXT_STR16 || \ (x) == SDP_TEXT_STR32) typedef struct _sdp_list sdp_list_t; struct _sdp_list { sdp_list_t *next; void *data; }; /* * User-visible strings can be in many languages * in addition to the universal language. * * Language meta-data includes language code in ISO639 * followed by the encoding format. The third field in this * structure is the attribute offset for the language. * User-visible strings in the specified language can be * obtained at this offset. */ typedef struct { uint16_t code_ISO639; uint16_t encoding; uint16_t base_offset; } sdp_lang_attr_t; /* * Profile descriptor is the Bluetooth profile metadata. If a * service conforms to a well-known profile, then its profile * identifier (UUID) is an attribute of the service. In addition, * if the profile has a version number it is specified here. */ typedef struct { uuid_t uuid; uint16_t version; } sdp_profile_desc_t; typedef struct { uint8_t major; uint8_t minor; } sdp_version_t; typedef struct { uint8_t *data; uint32_t data_size; uint32_t buf_size; } sdp_buf_t; typedef struct { uint32_t handle; /* Search pattern: a sequence of all UUIDs seen in this record */ sdp_list_t *pattern; sdp_list_t *attrlist; /* Main service class for Extended Inquiry Response */ uuid_t svclass; } sdp_record_t; typedef struct sdp_data_struct sdp_data_t; struct sdp_data_struct { uint8_t dtd; uint16_t attrId; union { int8_t int8; int16_t int16; int32_t int32; int64_t int64; uint128_t int128; uint8_t uint8; uint16_t uint16; uint32_t uint32; uint64_t uint64; uint128_t uint128; uuid_t uuid; char *str; sdp_data_t *dataseq; } val; sdp_data_t *next; int unitSize; }; #ifdef __cplusplus } #endif #endif /* __SDP_H */ bluez-5.82/lib/PaxHeaders/iso.h0000644000000000000000000000005014267331527013371 xustar0020 atime=1743515790 20 ctime=1743591277 bluez-5.82/lib/iso.h0000644000000000000000000000127614267331527013060 0ustar00rootroot/* SPDX-License-Identifier: GPL-2.0-or-later */ /* * * BlueZ - Bluetooth protocol stack for Linux * * Copyright (C) 2022 Intel Corporation. * */ #ifndef __ISO_H #define __ISO_H #ifdef __cplusplus extern "C" { #endif /* ISO defaults */ #define ISO_DEFAULT_MTU 251 #define ISO_MAX_NUM_BIS 0x1f /* ISO socket broadcast address */ struct sockaddr_iso_bc { bdaddr_t bc_bdaddr; uint8_t bc_bdaddr_type; uint8_t bc_sid; uint8_t bc_num_bis; uint8_t bc_bis[ISO_MAX_NUM_BIS]; }; /* ISO socket address */ struct sockaddr_iso { sa_family_t iso_family; bdaddr_t iso_bdaddr; uint8_t iso_bdaddr_type; struct sockaddr_iso_bc iso_bc[]; }; #ifdef __cplusplus } #endif #endif /* __ISO_H */ bluez-5.82/lib/PaxHeaders/bluetooth.c0000644000000000000000000000005014015011623014556 xustar0020 atime=1743515815 20 ctime=1743591277 bluez-5.82/lib/bluetooth.c0000644000000000000000000032547214015011623014254 0ustar00rootroot// SPDX-License-Identifier: GPL-2.0-or-later /* * * BlueZ - Bluetooth protocol stack for Linux * * Copyright (C) 2000-2001 Qualcomm Incorporated * Copyright (C) 2002-2003 Maxim Krasnyansky * Copyright (C) 2002-2010 Marcel Holtmann * * */ #ifdef HAVE_CONFIG_H #include #endif #include #include #include #include #include #include #include #include #include "bluetooth.h" #include "hci.h" void baswap(bdaddr_t *dst, const bdaddr_t *src) { register unsigned char *d = (unsigned char *) dst; register const unsigned char *s = (const unsigned char *) src; register int i; for (i = 0; i < 6; i++) d[i] = s[5-i]; } char *batostr(const bdaddr_t *ba) { char *str = bt_malloc(18); if (!str) return NULL; sprintf(str, "%2.2X:%2.2X:%2.2X:%2.2X:%2.2X:%2.2X", ba->b[0], ba->b[1], ba->b[2], ba->b[3], ba->b[4], ba->b[5]); return str; } bdaddr_t *strtoba(const char *str) { bdaddr_t b; bdaddr_t *ba = bt_malloc(sizeof(*ba)); if (ba) { str2ba(str, &b); baswap(ba, &b); } return ba; } int ba2str(const bdaddr_t *ba, char *str) { return sprintf(str, "%2.2X:%2.2X:%2.2X:%2.2X:%2.2X:%2.2X", ba->b[5], ba->b[4], ba->b[3], ba->b[2], ba->b[1], ba->b[0]); } /* Match kernel's lowercase printing of mac address (%pMR) */ int ba2strlc(const bdaddr_t *ba, char *str) { return sprintf(str, "%2.2x:%2.2x:%2.2x:%2.2x:%2.2x:%2.2x", ba->b[5], ba->b[4], ba->b[3], ba->b[2], ba->b[1], ba->b[0]); } int str2ba(const char *str, bdaddr_t *ba) { int i; if (bachk(str) < 0) { memset(ba, 0, sizeof(*ba)); return -1; } for (i = 5; i >= 0; i--, str += 3) ba->b[i] = strtol(str, NULL, 16); return 0; } int ba2oui(const bdaddr_t *ba, char *str) { return sprintf(str, "%2.2X-%2.2X-%2.2X", ba->b[5], ba->b[4], ba->b[3]); } int bachk(const char *str) { if (!str) return -1; if (strlen(str) != 17) return -1; while (*str) { if (!isxdigit(*str++)) return -1; if (!isxdigit(*str++)) return -1; if (*str == 0) break; if (*str++ != ':') return -1; } return 0; } int baprintf(const char *format, ...) { va_list ap; int len; va_start(ap, format); len = vprintf(format, ap); va_end(ap); return len; } int bafprintf(FILE *stream, const char *format, ...) { va_list ap; int len; va_start(ap, format); len = vfprintf(stream, format, ap); va_end(ap); return len; } int basprintf(char *str, const char *format, ...) { va_list ap; int len; va_start(ap, format); len = vsnprintf(str, (~0U) >> 1, format, ap); va_end(ap); return len; } int basnprintf(char *str, size_t size, const char *format, ...) { va_list ap; int len; va_start(ap, format); len = vsnprintf(str, size, format, ap); va_end(ap); return len; } void *bt_malloc(size_t size) { return malloc(size); } void *bt_malloc0(size_t size) { return calloc(size, 1); } void bt_free(void *ptr) { free(ptr); } /* Bluetooth error codes to Unix errno mapping */ int bt_error(uint16_t code) { switch (code) { case 0: return 0; case HCI_UNKNOWN_COMMAND: return EBADRQC; case HCI_NO_CONNECTION: return ENOTCONN; case HCI_HARDWARE_FAILURE: return EIO; case HCI_PAGE_TIMEOUT: return EHOSTDOWN; case HCI_AUTHENTICATION_FAILURE: return EACCES; case HCI_PIN_OR_KEY_MISSING: return EINVAL; case HCI_MEMORY_FULL: return ENOMEM; case HCI_CONNECTION_TIMEOUT: return ETIMEDOUT; case HCI_MAX_NUMBER_OF_CONNECTIONS: case HCI_MAX_NUMBER_OF_SCO_CONNECTIONS: return EMLINK; case HCI_ACL_CONNECTION_EXISTS: return EALREADY; case HCI_COMMAND_DISALLOWED: case HCI_TRANSACTION_COLLISION: case HCI_ROLE_SWITCH_PENDING: return EBUSY; case HCI_REJECTED_LIMITED_RESOURCES: case HCI_REJECTED_PERSONAL: case HCI_QOS_REJECTED: return ECONNREFUSED; case HCI_HOST_TIMEOUT: return ETIMEDOUT; case HCI_UNSUPPORTED_FEATURE: case HCI_QOS_NOT_SUPPORTED: case HCI_PAIRING_NOT_SUPPORTED: case HCI_CLASSIFICATION_NOT_SUPPORTED: case HCI_UNSUPPORTED_LMP_PARAMETER_VALUE: case HCI_PARAMETER_OUT_OF_RANGE: case HCI_QOS_UNACCEPTABLE_PARAMETER: return EOPNOTSUPP; case HCI_INVALID_PARAMETERS: case HCI_SLOT_VIOLATION: return EINVAL; case HCI_OE_USER_ENDED_CONNECTION: case HCI_OE_LOW_RESOURCES: case HCI_OE_POWER_OFF: return ECONNRESET; case HCI_CONNECTION_TERMINATED: return ECONNABORTED; case HCI_REPEATED_ATTEMPTS: return ELOOP; case HCI_REJECTED_SECURITY: case HCI_PAIRING_NOT_ALLOWED: case HCI_INSUFFICIENT_SECURITY: return EACCES; case HCI_UNSUPPORTED_REMOTE_FEATURE: return EPROTONOSUPPORT; case HCI_SCO_OFFSET_REJECTED: return ECONNREFUSED; case HCI_UNKNOWN_LMP_PDU: case HCI_INVALID_LMP_PARAMETERS: case HCI_LMP_ERROR_TRANSACTION_COLLISION: case HCI_LMP_PDU_NOT_ALLOWED: case HCI_ENCRYPTION_MODE_NOT_ACCEPTED: return EPROTO; default: return ENOSYS; } } const char *bt_compidtostr(int compid) { switch (compid) { case 0: return "Ericsson Technology Licensing"; case 1: return "Nokia Mobile Phones"; case 2: return "Intel Corp."; case 3: return "IBM Corp."; case 4: return "Toshiba Corp."; case 5: return "3Com"; case 6: return "Microsoft"; case 7: return "Lucent"; case 8: return "Motorola"; case 9: return "Infineon Technologies AG"; case 10: return "Cambridge Silicon Radio"; case 11: return "Silicon Wave"; case 12: return "Digianswer A/S"; case 13: return "Texas Instruments Inc."; case 14: return "Parthus Technologies Inc."; case 15: return "Broadcom Corporation"; case 16: return "Mitel Semiconductor"; case 17: return "Widcomm, Inc."; case 18: return "Zeevo, Inc."; case 19: return "Atmel Corporation"; case 20: return "Mitsubishi Electric Corporation"; case 21: return "RTX Telecom A/S"; case 22: return "KC Technology Inc."; case 23: return "Newlogic"; case 24: return "Transilica, Inc."; case 25: return "Rohde & Schwarz GmbH & Co. KG"; case 26: return "TTPCom Limited"; case 27: return "Signia Technologies, Inc."; case 28: return "Conexant Systems Inc."; case 29: return "Qualcomm"; case 30: return "Inventel"; case 31: return "AVM Berlin"; case 32: return "BandSpeed, Inc."; case 33: return "Mansella Ltd"; case 34: return "NEC Corporation"; case 35: return "WavePlus Technology Co., Ltd."; case 36: return "Alcatel"; case 37: return "NXP Semiconductors (formerly Philips Semiconductors)"; case 38: return "C Technologies"; case 39: return "Open Interface"; case 40: return "R F Micro Devices"; case 41: return "Hitachi Ltd"; case 42: return "Symbol Technologies, Inc."; case 43: return "Tenovis"; case 44: return "Macronix International Co. Ltd."; case 45: return "GCT Semiconductor"; case 46: return "Norwood Systems"; case 47: return "MewTel Technology Inc."; case 48: return "ST Microelectronics"; case 49: return "Synopsys, Inc."; case 50: return "Red-M (Communications) Ltd"; case 51: return "Commil Ltd"; case 52: return "Computer Access Technology Corporation (CATC)"; case 53: return "Eclipse (HQ Espana) S.L."; case 54: return "Renesas Electronics Corporation"; case 55: return "Mobilian Corporation"; case 56: return "Syntronix Corporation"; case 57: return "Integrated System Solution Corp."; case 58: return "Panasonic Corporation (formerly Matsushita Electric Industrial Co., Ltd.)"; case 59: return "Gennum Corporation"; case 60: return "BlackBerry Limited (formerly Research In Motion)"; case 61: return "IPextreme, Inc."; case 62: return "Systems and Chips, Inc"; case 63: return "Bluetooth SIG, Inc"; case 64: return "Seiko Epson Corporation"; case 65: return "Integrated Silicon Solution Taiwan, Inc."; case 66: return "CONWISE Technology Corporation Ltd"; case 67: return "PARROT AUTOMOTIVE SAS"; case 68: return "Socket Mobile"; case 69: return "Atheros Communications, Inc."; case 70: return "MediaTek, Inc."; case 71: return "Bluegiga"; case 72: return "Marvell Technology Group Ltd."; case 73: return "3DSP Corporation"; case 74: return "Accel Semiconductor Ltd."; case 75: return "Continental Automotive Systems"; case 76: return "Apple, Inc."; case 77: return "Staccato Communications, Inc."; case 78: return "Avago Technologies"; case 79: return "APT Ltd."; case 80: return "SiRF Technology, Inc."; case 81: return "Tzero Technologies, Inc."; case 82: return "J&M Corporation"; case 83: return "Free2move AB"; case 84: return "3DiJoy Corporation"; case 85: return "Plantronics, Inc."; case 86: return "Sony Ericsson Mobile Communications"; case 87: return "Harman International Industries, Inc."; case 88: return "Vizio, Inc."; case 89: return "Nordic Semiconductor ASA"; case 90: return "EM Microelectronic-Marin SA"; case 91: return "Ralink Technology Corporation"; case 92: return "Belkin International, Inc."; case 93: return "Realtek Semiconductor Corporation"; case 94: return "Stonestreet One, LLC"; case 95: return "Wicentric, Inc."; case 96: return "RivieraWaves S.A.S"; case 97: return "RDA Microelectronics"; case 98: return "Gibson Guitars"; case 99: return "MiCommand Inc."; case 100: return "Band XI International, LLC"; case 101: return "Hewlett-Packard Company"; case 102: return "9Solutions Oy"; case 103: return "GN Netcom A/S"; case 104: return "General Motors"; case 105: return "A&D Engineering, Inc."; case 106: return "MindTree Ltd."; case 107: return "Polar Electro OY"; case 108: return "Beautiful Enterprise Co., Ltd."; case 109: return "BriarTek, Inc"; case 110: return "Summit Data Communications, Inc."; case 111: return "Sound ID"; case 112: return "Monster, LLC"; case 113: return "connectBlue AB"; case 114: return "ShangHai Super Smart Electronics Co. Ltd."; case 115: return "Group Sense Ltd."; case 116: return "Zomm, LLC"; case 117: return "Samsung Electronics Co. Ltd."; case 118: return "Creative Technology Ltd."; case 119: return "Laird Technologies"; case 120: return "Nike, Inc."; case 121: return "lesswire AG"; case 122: return "MStar Semiconductor, Inc."; case 123: return "Hanlynn Technologies"; case 124: return "A & R Cambridge"; case 125: return "Seers Technology Co., Ltd."; case 126: return "Sports Tracking Technologies Ltd."; case 127: return "Autonet Mobile"; case 128: return "DeLorme Publishing Company, Inc."; case 129: return "WuXi Vimicro"; case 130: return "Sennheiser Communications A/S"; case 131: return "TimeKeeping Systems, Inc."; case 132: return "Ludus Helsinki Ltd."; case 133: return "BlueRadios, Inc."; case 134: return "Equinux AG"; case 135: return "Garmin International, Inc."; case 136: return "Ecotest"; case 137: return "GN ReSound A/S"; case 138: return "Jawbone"; case 139: return "Topcon Positioning Systems, LLC"; case 140: return "Gimbal Inc. (formerly Qualcomm Labs, Inc. and Qualcomm Retail Solutions, Inc.)"; case 141: return "Zscan Software"; case 142: return "Quintic Corp"; case 143: return "Telit Wireless Solutions GmbH (formerly Stollmann E+V GmbH)"; case 144: return "Funai Electric Co., Ltd."; case 145: return "Advanced PANMOBIL systems GmbH & Co. KG"; case 146: return "ThinkOptics, Inc."; case 147: return "Universal Electronics, Inc."; case 148: return "Airoha Technology Corp."; case 149: return "NEC Lighting, Ltd."; case 150: return "ODM Technology, Inc."; case 151: return "ConnecteDevice Ltd."; case 152: return "zero1.tv GmbH"; case 153: return "i.Tech Dynamic Global Distribution Ltd."; case 154: return "Alpwise"; case 155: return "Jiangsu Toppower Automotive Electronics Co., Ltd."; case 156: return "Colorfy, Inc."; case 157: return "Geoforce Inc."; case 158: return "Bose Corporation"; case 159: return "Suunto Oy"; case 160: return "Kensington Computer Products Group"; case 161: return "SR-Medizinelektronik"; case 162: return "Vertu Corporation Limited"; case 163: return "Meta Watch Ltd."; case 164: return "LINAK A/S"; case 165: return "OTL Dynamics LLC"; case 166: return "Panda Ocean Inc."; case 167: return "Visteon Corporation"; case 168: return "ARP Devices Limited"; case 169: return "MARELLI EUROPE S.P.A. (formerly Magneti Marelli S.p.A.)"; case 170: return "CAEN RFID srl"; case 171: return "Ingenieur-Systemgruppe Zahn GmbH"; case 172: return "Green Throttle Games"; case 173: return "Peter Systemtechnik GmbH"; case 174: return "Omegawave Oy"; case 175: return "Cinetix"; case 176: return "Passif Semiconductor Corp"; case 177: return "Saris Cycling Group, Inc"; case 178: return "Bekey A/S"; case 179: return "Clarinox Technologies Pty. Ltd."; case 180: return "BDE Technology Co., Ltd."; case 181: return "Swirl Networks"; case 182: return "Meso international"; case 183: return "TreLab Ltd"; case 184: return "Qualcomm Innovation Center, Inc. (QuIC)"; case 185: return "Johnson Controls, Inc."; case 186: return "Starkey Laboratories Inc."; case 187: return "S-Power Electronics Limited"; case 188: return "Ace Sensor Inc"; case 189: return "Aplix Corporation"; case 190: return "AAMP of America"; case 191: return "Stalmart Technology Limited"; case 192: return "AMICCOM Electronics Corporation"; case 193: return "Shenzhen Excelsecu Data Technology Co.,Ltd"; case 194: return "Geneq Inc."; case 195: return "adidas AG"; case 196: return "LG Electronics"; case 197: return "Onset Computer Corporation"; case 198: return "Selfly BV"; case 199: return "Quuppa Oy."; case 200: return "GeLo Inc"; case 201: return "Evluma"; case 202: return "MC10"; case 203: return "Binauric SE"; case 204: return "Beats Electronics"; case 205: return "Microchip Technology Inc."; case 206: return "Elgato Systems GmbH"; case 207: return "ARCHOS SA"; case 208: return "Dexcom, Inc."; case 209: return "Polar Electro Europe B.V."; case 210: return "Dialog Semiconductor B.V."; case 211: return "Taixingbang Technology (HK) Co,. LTD."; case 212: return "Kawantech"; case 213: return "Austco Communication Systems"; case 214: return "Timex Group USA, Inc."; case 215: return "Qualcomm Technologies, Inc."; case 216: return "Qualcomm Connected Experiences, Inc."; case 217: return "Voyetra Turtle Beach"; case 218: return "txtr GmbH"; case 219: return "Biosentronics"; case 220: return "Procter & Gamble"; case 221: return "Hosiden Corporation"; case 222: return "Muzik LLC"; case 223: return "Misfit Wearables Corp"; case 224: return "Google"; case 225: return "Danlers Ltd"; case 226: return "Semilink Inc"; case 227: return "inMusic Brands, Inc"; case 228: return "L.S. Research Inc."; case 229: return "Eden Software Consultants Ltd."; case 230: return "Freshtemp"; case 231: return "KS Technologies"; case 232: return "ACTS Technologies"; case 233: return "Vtrack Systems"; case 234: return "Nielsen-Kellerman Company"; case 235: return "Server Technology Inc."; case 236: return "BioResearch Associates"; case 237: return "Jolly Logic, LLC"; case 238: return "Above Average Outcomes, Inc."; case 239: return "Bitsplitters GmbH"; case 240: return "PayPal, Inc."; case 241: return "Witron Technology Limited"; case 242: return "Morse Project Inc."; case 243: return "Kent Displays Inc."; case 244: return "Nautilus Inc."; case 245: return "Smartifier Oy"; case 246: return "Elcometer Limited"; case 247: return "VSN Technologies, Inc."; case 248: return "AceUni Corp., Ltd."; case 249: return "StickNFind"; case 250: return "Crystal Code AB"; case 251: return "KOUKAAM a.s."; case 252: return "Delphi Corporation"; case 253: return "ValenceTech Limited"; case 254: return "Stanley Black and Decker"; case 255: return "Typo Products, LLC"; case 256: return "TomTom International BV"; case 257: return "Fugoo, Inc."; case 258: return "Keiser Corporation"; case 259: return "Bang & Olufsen A/S"; case 260: return "PLUS Location Systems Pty Ltd"; case 261: return "Ubiquitous Computing Technology Corporation"; case 262: return "Innovative Yachtter Solutions"; case 263: return "William Demant Holding A/S"; case 264: return "Chicony Electronics Co., Ltd."; case 265: return "Atus BV"; case 266: return "Codegate Ltd"; case 267: return "ERi, Inc"; case 268: return "Transducers Direct, LLC"; case 269: return "DENSO TEN LIMITED (formerly Fujitsu Ten LImited)"; case 270: return "Audi AG"; case 271: return "HiSilicon Technologies CO., LIMITED"; case 272: return "Nippon Seiki Co., Ltd."; case 273: return "Steelseries ApS"; case 274: return "Visybl Inc."; case 275: return "Openbrain Technologies, Co., Ltd."; case 276: return "Xensr"; case 277: return "e.solutions"; case 278: return "10AK Technologies"; case 279: return "Wimoto Technologies Inc"; case 280: return "Radius Networks, Inc."; case 281: return "Wize Technology Co., Ltd."; case 282: return "Qualcomm Labs, Inc."; case 283: return "Aruba Networks"; case 284: return "Baidu"; case 285: return "Arendi AG"; case 286: return "Skoda Auto a.s."; case 287: return "Volkswagen AG"; case 288: return "Porsche AG"; case 289: return "Sino Wealth Electronic Ltd."; case 290: return "AirTurn, Inc."; case 291: return "Kinsa, Inc"; case 292: return "HID Global"; case 293: return "SEAT es"; case 294: return "Promethean Ltd."; case 295: return "Salutica Allied Solutions"; case 296: return "GPSI Group Pty Ltd"; case 297: return "Nimble Devices Oy"; case 298: return "Changzhou Yongse Infotech Co., Ltd."; case 299: return "SportIQ"; case 300: return "TEMEC Instruments B.V."; case 301: return "Sony Corporation"; case 302: return "ASSA ABLOY"; case 303: return "Clarion Co. Inc."; case 304: return "Warehouse Innovations"; case 305: return "Cypress Semiconductor"; case 306: return "MADS Inc"; case 307: return "Blue Maestro Limited"; case 308: return "Resolution Products, Ltd."; case 309: return "Aireware LLC"; case 310: return "Silvair, Inc."; case 311: return "Prestigio Plaza Ltd."; case 312: return "NTEO Inc."; case 313: return "Focus Systems Corporation"; case 314: return "Tencent Holdings Ltd."; case 315: return "Allegion"; case 316: return "Murata Manufacturing Co., Ltd."; case 317: return "WirelessWERX"; case 318: return "Nod, Inc."; case 319: return "B&B Manufacturing Company"; case 320: return "Alpine Electronics (China) Co., Ltd"; case 321: return "FedEx Services"; case 322: return "Grape Systems Inc."; case 323: return "Bkon Connect"; case 324: return "Lintech GmbH"; case 325: return "Novatel Wireless"; case 326: return "Ciright"; case 327: return "Mighty Cast, Inc."; case 328: return "Ambimat Electronics"; case 329: return "Perytons Ltd."; case 330: return "Tivoli Audio, LLC"; case 331: return "Master Lock"; case 332: return "Mesh-Net Ltd"; case 333: return "HUIZHOU DESAY SV AUTOMOTIVE CO., LTD."; case 334: return "Tangerine, Inc."; case 335: return "B&W Group Ltd."; case 336: return "Pioneer Corporation"; case 337: return "OnBeep"; case 338: return "Vernier Software & Technology"; case 339: return "ROL Ergo"; case 340: return "Pebble Technology"; case 341: return "NETATMO"; case 342: return "Accumulate AB"; case 343: return "Anhui Huami Information Technology Co., Ltd."; case 344: return "Inmite s.r.o."; case 345: return "ChefSteps, Inc."; case 346: return "micas AG"; case 347: return "Biomedical Research Ltd."; case 348: return "Pitius Tec S.L."; case 349: return "Estimote, Inc."; case 350: return "Unikey Technologies, Inc."; case 351: return "Timer Cap Co."; case 352: return "AwoX"; case 353: return "yikes"; case 354: return "MADSGlobalNZ Ltd."; case 355: return "PCH International"; case 356: return "Qingdao Yeelink Information Technology Co., Ltd."; case 357: return "Milwaukee Tool (Formally Milwaukee Electric Tools)"; case 358: return "MISHIK Pte Ltd"; case 359: return "Ascensia Diabetes Care US Inc."; case 360: return "Spicebox LLC"; case 361: return "emberlight"; case 362: return "Cooper-Atkins Corporation"; case 363: return "Qblinks"; case 364: return "MYSPHERA"; case 365: return "LifeScan Inc"; case 366: return "Volantic AB"; case 367: return "Podo Labs, Inc"; case 368: return "Roche Diabetes Care AG"; case 369: return "Amazon.com Services, LLC (formerly Amazon Fulfillment Service)"; case 370: return "Connovate Technology Private Limited"; case 371: return "Kocomojo, LLC"; case 372: return "Everykey Inc."; case 373: return "Dynamic Controls"; case 374: return "SentriLock"; case 375: return "I-SYST inc."; case 376: return "CASIO COMPUTER CO., LTD."; case 377: return "LAPIS Semiconductor Co., Ltd."; case 378: return "Telemonitor, Inc."; case 379: return "taskit GmbH"; case 380: return "Daimler AG"; case 381: return "BatAndCat"; case 382: return "BluDotz Ltd"; case 383: return "XTel Wireless ApS"; case 384: return "Gigaset Communications GmbH"; case 385: return "Gecko Health Innovations, Inc."; case 386: return "HOP Ubiquitous"; case 387: return "Walt Disney"; case 388: return "Nectar"; case 389: return "bel'apps LLC"; case 390: return "CORE Lighting Ltd"; case 391: return "Seraphim Sense Ltd"; case 392: return "Unico RBC"; case 393: return "Physical Enterprises Inc."; case 394: return "Able Trend Technology Limited"; case 395: return "Konica Minolta, Inc."; case 396: return "Wilo SE"; case 397: return "Extron Design Services"; case 398: return "Fitbit, Inc."; case 399: return "Fireflies Systems"; case 400: return "Intelletto Technologies Inc."; case 401: return "FDK CORPORATION"; case 402: return "Cloudleaf, Inc"; case 403: return "Maveric Automation LLC"; case 404: return "Acoustic Stream Corporation"; case 405: return "Zuli"; case 406: return "Paxton Access Ltd"; case 407: return "WiSilica Inc."; case 408: return "VENGIT Korlatolt Felelossegu Tarsasag"; case 409: return "SALTO SYSTEMS S.L."; case 410: return "TRON Forum (formerly T-Engine Forum)"; case 411: return "CUBETECH s.r.o."; case 412: return "Cokiya Incorporated"; case 413: return "CVS Health"; case 414: return "Ceruus"; case 415: return "Strainstall Ltd"; case 416: return "Channel Enterprises (HK) Ltd."; case 417: return "FIAMM"; case 418: return "GIGALANE.CO.,LTD"; case 419: return "EROAD"; case 420: return "Mine Safety Appliances"; case 421: return "Icon Health and Fitness"; case 422: return "Wille Engineering (formely as Asandoo GmbH)"; case 423: return "ENERGOUS CORPORATION"; case 424: return "Taobao"; case 425: return "Canon Inc."; case 426: return "Geophysical Technology Inc."; case 427: return "Facebook, Inc."; case 428: return "Trividia Health, Inc."; case 429: return "FlightSafety International"; case 430: return "Earlens Corporation"; case 431: return "Sunrise Micro Devices, Inc."; case 432: return "Star Micronics Co., Ltd."; case 433: return "Netizens Sp. z o.o."; case 434: return "Nymi Inc."; case 435: return "Nytec, Inc."; case 436: return "Trineo Sp. z o.o."; case 437: return "Nest Labs Inc."; case 438: return "LM Technologies Ltd"; case 439: return "General Electric Company"; case 440: return "i+D3 S.L."; case 441: return "HANA Micron"; case 442: return "Stages Cycling LLC"; case 443: return "Cochlear Bone Anchored Solutions AB"; case 444: return "SenionLab AB"; case 445: return "Syszone Co., Ltd"; case 446: return "Pulsate Mobile Ltd."; case 447: return "Hong Kong HunterSun Electronic Limited"; case 448: return "pironex GmbH"; case 449: return "BRADATECH Corp."; case 450: return "Transenergooil AG"; case 451: return "Bunch"; case 452: return "DME Microelectronics"; case 453: return "Bitcraze AB"; case 454: return "HASWARE Inc."; case 455: return "Abiogenix Inc."; case 456: return "Poly-Control ApS"; case 457: return "Avi-on"; case 458: return "Laerdal Medical AS"; case 459: return "Fetch My Pet"; case 460: return "Sam Labs Ltd."; case 461: return "Chengdu Synwing Technology Ltd"; case 462: return "HOUWA SYSTEM DESIGN, k.k."; case 463: return "BSH"; case 464: return "Primus Inter Pares Ltd"; case 465: return "August Home, Inc"; case 466: return "Gill Electronics"; case 467: return "Sky Wave Design"; case 468: return "Newlab S.r.l."; case 469: return "ELAD srl"; case 470: return "G-wearables inc."; case 471: return "Squadrone Systems Inc."; case 472: return "Code Corporation"; case 473: return "Savant Systems LLC"; case 474: return "Logitech International SA"; case 475: return "Innblue Consulting"; case 476: return "iParking Ltd."; case 477: return "Koninklijke Philips Electronics N.V."; case 478: return "Minelab Electronics Pty Limited"; case 479: return "Bison Group Ltd."; case 480: return "Widex A/S"; case 481: return "Jolla Ltd"; case 482: return "Lectronix, Inc."; case 483: return "Caterpillar Inc"; case 484: return "Freedom Innovations"; case 485: return "Dynamic Devices Ltd"; case 486: return "Technology Solutions (UK) Ltd"; case 487: return "IPS Group Inc."; case 488: return "STIR"; case 489: return "Sano, Inc."; case 490: return "Advanced Application Design, Inc."; case 491: return "AutoMap LLC"; case 492: return "Spreadtrum Communications Shanghai Ltd"; case 493: return "CuteCircuit LTD"; case 494: return "Valeo Service"; case 495: return "Fullpower Technologies, Inc."; case 496: return "KloudNation"; case 497: return "Zebra Technologies Corporation"; case 498: return "Itron, Inc."; case 499: return "The University of Tokyo"; case 500: return "UTC Fire and Security"; case 501: return "Cool Webthings Limited"; case 502: return "DJO Global"; case 503: return "Gelliner Limited"; case 504: return "Anyka (Guangzhou) Microelectronics Technology Co, LTD"; case 505: return "Medtronic Inc."; case 506: return "Gozio Inc."; case 507: return "Form Lifting, LLC"; case 508: return "Wahoo Fitness, LLC"; case 509: return "Kontakt Micro-Location Sp. z o.o."; case 510: return "Radio Systems Corporation"; case 511: return "Freescale Semiconductor, Inc."; case 512: return "Verifone Systems Pte Ltd. Taiwan Branch"; case 513: return "AR Timing"; case 514: return "Rigado LLC"; case 515: return "Kemppi Oy"; case 516: return "Tapcentive Inc."; case 517: return "Smartbotics Inc."; case 518: return "Otter Products, LLC"; case 519: return "STEMP Inc."; case 520: return "LumiGeek LLC"; case 521: return "InvisionHeart Inc."; case 522: return "Macnica Inc."; case 523: return "Jaguar Land Rover Limited"; case 524: return "CoroWare Technologies, Inc"; case 525: return "Simplo Technology Co., LTD"; case 526: return "Omron Healthcare Co., LTD"; case 527: return "Comodule GMBH"; case 528: return "ikeGPS"; case 529: return "Telink Semiconductor Co. Ltd"; case 530: return "Interplan Co., Ltd"; case 531: return "Wyler AG"; case 532: return "IK Multimedia Production srl"; case 533: return "Lukoton Experience Oy"; case 534: return "MTI Ltd"; case 535: return "Tech4home, Lda"; case 536: return "Hiotech AB"; case 537: return "DOTT Limited"; case 538: return "Blue Speck Labs, LLC"; case 539: return "Cisco Systems, Inc"; case 540: return "Mobicomm Inc"; case 541: return "Edamic"; case 542: return "Goodnet, Ltd"; case 543: return "Luster Leaf Products Inc"; case 544: return "Manus Machina BV"; case 545: return "Mobiquity Networks Inc"; case 546: return "Praxis Dynamics"; case 547: return "Philip Morris Products S.A."; case 548: return "Comarch SA"; case 549: return "Nestlé Nespresso S.A."; case 550: return "Merlinia A/S"; case 551: return "LifeBEAM Technologies"; case 552: return "Twocanoes Labs, LLC"; case 553: return "Muoverti Limited"; case 554: return "Stamer Musikanlagen GMBH"; case 555: return "Tesla Motors"; case 556: return "Pharynks Corporation"; case 557: return "Lupine"; case 558: return "Siemens AG"; case 559: return "Huami (Shanghai) Culture Communication CO., LTD"; case 560: return "Foster Electric Company, Ltd"; case 561: return "ETA SA"; case 562: return "x-Senso Solutions Kft"; case 563: return "Shenzhen SuLong Communication Ltd"; case 564: return "FengFan (BeiJing) Technology Co, Ltd"; case 565: return "Qrio Inc"; case 566: return "Pitpatpet Ltd"; case 567: return "MSHeli s.r.l."; case 568: return "Trakm8 Ltd"; case 569: return "JIN CO, Ltd"; case 570: return "Alatech Tehnology"; case 571: return "Beijing CarePulse Electronic Technology Co, Ltd"; case 572: return "Awarepoint"; case 573: return "ViCentra B.V."; case 574: return "Raven Industries"; case 575: return "WaveWare Technologies Inc."; case 576: return "Argenox Technologies"; case 577: return "Bragi GmbH"; case 578: return "16Lab Inc"; case 579: return "Masimo Corp"; case 580: return "Iotera Inc"; case 581: return "Endress+Hauser "; case 582: return "ACKme Networks, Inc."; case 583: return "FiftyThree Inc."; case 584: return "Parker Hannifin Corp"; case 585: return "Transcranial Ltd"; case 586: return "Uwatec AG"; case 587: return "Orlan LLC"; case 588: return "Blue Clover Devices"; case 589: return "M-Way Solutions GmbH"; case 590: return "Microtronics Engineering GmbH"; case 591: return "Schneider Schreibgeräte GmbH"; case 592: return "Sapphire Circuits LLC"; case 593: return "Lumo Bodytech Inc."; case 594: return "UKC Technosolution"; case 595: return "Xicato Inc."; case 596: return "Playbrush"; case 597: return "Dai Nippon Printing Co., Ltd."; case 598: return "G24 Power Limited"; case 599: return "AdBabble Local Commerce Inc."; case 600: return "Devialet SA"; case 601: return "ALTYOR"; case 602: return "University of Applied Sciences Valais/Haute Ecole Valaisanne"; case 603: return "Five Interactive, LLC dba Zendo"; case 604: return "NetEase(Hangzhou)Network co.Ltd."; case 605: return "Lexmark International Inc."; case 606: return "Fluke Corporation"; case 607: return "Yardarm Technologies"; case 608: return "SensaRx"; case 609: return "SECVRE GmbH"; case 610: return "Glacial Ridge Technologies"; case 611: return "Identiv, Inc."; case 612: return "DDS, Inc."; case 613: return "SMK Corporation"; case 614: return "Schawbel Technologies LLC"; case 615: return "XMI Systems SA"; case 616: return "Cerevo"; case 617: return "Torrox GmbH & Co KG"; case 618: return "Gemalto"; case 619: return "DEKA Research & Development Corp."; case 620: return "Domster Tadeusz Szydlowski"; case 621: return "Technogym SPA"; case 622: return "FLEURBAEY BVBA"; case 623: return "Aptcode Solutions"; case 624: return "LSI ADL Technology"; case 625: return "Animas Corp"; case 626: return "Alps Electric Co., Ltd."; case 627: return "OCEASOFT"; case 628: return "Motsai Research"; case 629: return "Geotab"; case 630: return "E.G.O. Elektro-Geraetebau GmbH"; case 631: return "bewhere inc"; case 632: return "Johnson Outdoors Inc"; case 633: return "steute Schaltgerate GmbH & Co. KG"; case 634: return "Ekomini inc."; case 635: return "DEFA AS"; case 636: return "Aseptika Ltd"; case 637: return "HUAWEI Technologies Co., Ltd."; case 638: return "HabitAware, LLC"; case 639: return "ruwido austria gmbh"; case 640: return "ITEC corporation"; case 641: return "StoneL"; case 642: return "Sonova AG"; case 643: return "Maven Machines, Inc."; case 644: return "Synapse Electronics"; case 645: return "Standard Innovation Inc."; case 646: return "RF Code, Inc."; case 647: return "Wally Ventures S.L."; case 648: return "Willowbank Electronics Ltd"; case 649: return "SK Telecom"; case 650: return "Jetro AS"; case 651: return "Code Gears LTD"; case 652: return "NANOLINK APS"; case 653: return "IF, LLC"; case 654: return "RF Digital Corp"; case 655: return "Church & Dwight Co., Inc"; case 656: return "Multibit Oy"; case 657: return "CliniCloud Inc"; case 658: return "SwiftSensors"; case 659: return "Blue Bite"; case 660: return "ELIAS GmbH"; case 661: return "Sivantos GmbH"; case 662: return "Petzl"; case 663: return "storm power ltd"; case 664: return "EISST Ltd"; case 665: return "Inexess Technology Simma KG"; case 666: return "Currant, Inc."; case 667: return "C2 Development, Inc."; case 668: return "Blue Sky Scientific, LLC"; case 669: return "ALOTTAZS LABS, LLC"; case 670: return "Kupson spol. s r.o."; case 671: return "Areus Engineering GmbH"; case 672: return "Impossible Camera GmbH"; case 673: return "InventureTrack Systems"; case 674: return "LockedUp"; case 675: return "Itude"; case 676: return "Pacific Lock Company"; case 677: return "Tendyron Corporation ( 天地融科技股份有限公司 )"; case 678: return "Robert Bosch GmbH"; case 679: return "Illuxtron international B.V."; case 680: return "miSport Ltd."; case 681: return "Chargelib"; case 682: return "Doppler Lab"; case 683: return "BBPOS Limited"; case 684: return "RTB Elektronik GmbH & Co. KG"; case 685: return "Rx Networks, Inc."; case 686: return "WeatherFlow, Inc."; case 687: return "Technicolor USA Inc."; case 688: return "Bestechnic(Shanghai),Ltd"; case 689: return "Raden Inc"; case 690: return "JouZen Oy"; case 691: return "CLABER S.P.A."; case 692: return "Hyginex, Inc."; case 693: return "HANSHIN ELECTRIC RAILWAY CO.,LTD."; case 694: return "Schneider Electric"; case 695: return "Oort Technologies LLC"; case 696: return "Chrono Therapeutics"; case 697: return "Rinnai Corporation"; case 698: return "Swissprime Technologies AG"; case 699: return "Koha.,Co.Ltd"; case 700: return "Genevac Ltd"; case 701: return "Chemtronics"; case 702: return "Seguro Technology Sp. z o.o."; case 703: return "Redbird Flight Simulations"; case 704: return "Dash Robotics"; case 705: return "LINE Corporation"; case 706: return "Guillemot Corporation"; case 707: return "Techtronic Power Tools Technology Limited"; case 708: return "Wilson Sporting Goods"; case 709: return "Lenovo (Singapore) Pte Ltd. ( 联想(新加坡) )"; case 710: return "Ayatan Sensors"; case 711: return "Electronics Tomorrow Limited"; case 712: return "VASCO Data Security International, Inc."; case 713: return "PayRange Inc."; case 714: return "ABOV Semiconductor"; case 715: return "AINA-Wireless Inc."; case 716: return "Eijkelkamp Soil & Water"; case 717: return "BMA ergonomics b.v."; case 718: return "Teva Branded Pharmaceutical Products R&D, Inc."; case 719: return "Anima"; case 720: return "3M"; case 721: return "Empatica Srl"; case 722: return "Afero, Inc."; case 723: return "Powercast Corporation"; case 724: return "Secuyou ApS"; case 725: return "OMRON Corporation"; case 726: return "Send Solutions"; case 727: return "NIPPON SYSTEMWARE CO.,LTD."; case 728: return "Neosfar"; case 729: return "Fliegl Agrartechnik GmbH"; case 730: return "Gilvader"; case 731: return "Digi International Inc (R)"; case 732: return "DeWalch Technologies, Inc."; case 733: return "Flint Rehabilitation Devices, LLC"; case 734: return "Samsung SDS Co., Ltd."; case 735: return "Blur Product Development"; case 736: return "University of Michigan"; case 737: return "Victron Energy BV"; case 738: return "NTT docomo"; case 739: return "Carmanah Technologies Corp."; case 740: return "Bytestorm Ltd."; case 741: return "Espressif Incorporated ( 乐鑫信息科技(上海)有限公司 )"; case 742: return "Unwire"; case 743: return "Connected Yard, Inc."; case 744: return "American Music Environments"; case 745: return "Sensogram Technologies, Inc."; case 746: return "Fujitsu Limited"; case 747: return "Ardic Technology"; case 748: return "Delta Systems, Inc"; case 749: return "HTC Corporation"; case 750: return "Citizen Holdings Co., Ltd."; case 751: return "SMART-INNOVATION.inc"; case 752: return "Blackrat Software"; case 753: return "The Idea Cave, LLC"; case 754: return "GoPro, Inc."; case 755: return "AuthAir, Inc"; case 756: return "Vensi, Inc."; case 757: return "Indagem Tech LLC"; case 758: return "Intemo Technologies"; case 759: return "DreamVisions co., Ltd."; case 760: return "Runteq Oy Ltd"; case 761: return "IMAGINATION TECHNOLOGIES LTD"; case 762: return "CoSTAR Technologies"; case 763: return "Clarius Mobile Health Corp."; case 764: return "Shanghai Frequen Microelectronics Co., Ltd."; case 765: return "Uwanna, Inc."; case 766: return "Lierda Science & Technology Group Co., Ltd."; case 767: return "Silicon Laboratories"; case 768: return "World Moto Inc."; case 769: return "Giatec Scientific Inc."; case 770: return "Loop Devices, Inc"; case 771: return "IACA electronique"; case 772: return "Proxy Technologies, Inc."; case 773: return "Swipp ApS"; case 774: return "Life Laboratory Inc."; case 775: return "FUJI INDUSTRIAL CO.,LTD."; case 776: return "Surefire, LLC"; case 777: return "Dolby Labs"; case 778: return "Ellisys"; case 779: return "Magnitude Lighting Converters"; case 780: return "Hilti AG"; case 781: return "Devdata S.r.l."; case 782: return "Deviceworx"; case 783: return "Shortcut Labs"; case 784: return "SGL Italia S.r.l."; case 785: return "PEEQ DATA"; case 786: return "Ducere Technologies Pvt Ltd"; case 787: return "DiveNav, Inc."; case 788: return "RIIG AI Sp. z o.o."; case 789: return "Thermo Fisher Scientific"; case 790: return "AG Measurematics Pvt. Ltd."; case 791: return "CHUO Electronics CO., LTD."; case 792: return "Aspenta International"; case 793: return "Eugster Frismag AG"; case 794: return "Amber wireless GmbH"; case 795: return "HQ Inc"; case 796: return "Lab Sensor Solutions"; case 797: return "Enterlab ApS"; case 798: return "Eyefi, Inc."; case 799: return "MetaSystem S.p.A"; case 800: return "SONO ELECTRONICS. CO., LTD"; case 801: return "Jewelbots"; case 802: return "Compumedics Limited"; case 803: return "Rotor Bike Components"; case 804: return "Astro, Inc."; case 805: return "Amotus Solutions"; case 806: return "Healthwear Technologies (Changzhou)Ltd"; case 807: return "Essex Electronics"; case 808: return "Grundfos A/S"; case 809: return "Eargo, Inc."; case 810: return "Electronic Design Lab"; case 811: return "ESYLUX"; case 812: return "NIPPON SMT.CO.,Ltd"; case 813: return "BM innovations GmbH"; case 814: return "indoormap"; case 815: return "OttoQ Inc"; case 816: return "North Pole Engineering"; case 817: return "3flares Technologies Inc."; case 818: return "Electrocompaniet A.S."; case 819: return "Mul-T-Lock"; case 820: return "Corentium AS"; case 821: return "Enlighted Inc"; case 822: return "GISTIC"; case 823: return "AJP2 Holdings, LLC"; case 824: return "COBI GmbH"; case 825: return "Blue Sky Scientific, LLC"; case 826: return "Appception, Inc."; case 827: return "Courtney Thorne Limited"; case 828: return "Virtuosys"; case 829: return "TPV Technology Limited"; case 830: return "Monitra SA"; case 831: return "Automation Components, Inc."; case 832: return "Letsense s.r.l."; case 833: return "Etesian Technologies LLC"; case 834: return "GERTEC BRASIL LTDA."; case 835: return "Drekker Development Pty. Ltd."; case 836: return "Whirl Inc"; case 837: return "Locus Positioning"; case 838: return "Acuity Brands Lighting, Inc"; case 839: return "Prevent Biometrics"; case 840: return "Arioneo"; case 841: return "VersaMe"; case 842: return "Vaddio"; case 843: return "Libratone A/S"; case 844: return "HM Electronics, Inc."; case 845: return "TASER International, Inc."; case 846: return "Safe Trust Inc."; case 847: return "Heartland Payment Systems"; case 848: return "Bitstrata Systems Inc."; case 849: return "Pieps GmbH"; case 850: return "iRiding(Xiamen)Technology Co.,Ltd."; case 851: return "Alpha Audiotronics, Inc."; case 852: return "TOPPAN FORMS CO.,LTD."; case 853: return "Sigma Designs, Inc."; case 854: return "Spectrum Brands, Inc."; case 855: return "Polymap Wireless"; case 856: return "MagniWare Ltd."; case 857: return "Novotec Medical GmbH"; case 858: return "Medicom Innovation Partner a/s"; case 859: return "Matrix Inc."; case 860: return "Eaton Corporation"; case 861: return "KYS"; case 862: return "Naya Health, Inc."; case 863: return "Acromag"; case 864: return "Insulet Corporation"; case 865: return "Wellinks Inc."; case 866: return "ON Semiconductor"; case 867: return "FREELAP SA"; case 868: return "Favero Electronics Srl"; case 869: return "BioMech Sensor LLC"; case 870: return "BOLTT Sports technologies Private limited"; case 871: return "Saphe International"; case 872: return "Metormote AB"; case 873: return "littleBits"; case 874: return "SetPoint Medical"; case 875: return "BRControls Products BV"; case 876: return "Zipcar"; case 877: return "AirBolt Pty Ltd"; case 878: return "KeepTruckin Inc"; case 879: return "Motiv, Inc."; case 880: return "Wazombi Labs OÜ"; case 881: return "ORBCOMM"; case 882: return "Nixie Labs, Inc."; case 883: return "AppNearMe Ltd"; case 884: return "Holman Industries"; case 885: return "Expain AS"; case 886: return "Electronic Temperature Instruments Ltd"; case 887: return "Plejd AB"; case 888: return "Propeller Health"; case 889: return "Shenzhen iMCO Electronic Technology Co.,Ltd"; case 890: return "Algoria"; case 891: return "Apption Labs Inc."; case 892: return "Cronologics Corporation"; case 893: return "MICRODIA Ltd."; case 894: return "lulabytes S.L."; case 895: return "Société des Produits Nestlé S.A. (formerly Nestec S.A.)"; case 896: return "LLC \"MEGA-F service\""; case 897: return "Sharp Corporation"; case 898: return "Precision Outcomes Ltd"; case 899: return "Kronos Incorporated"; case 900: return "OCOSMOS Co., Ltd."; case 901: return "Embedded Electronic Solutions Ltd. dba e2Solutions"; case 902: return "Aterica Inc."; case 903: return "BluStor PMC, Inc."; case 904: return "Kapsch TrafficCom AB"; case 905: return "ActiveBlu Corporation"; case 906: return "Kohler Mira Limited"; case 907: return "Noke"; case 908: return "Appion Inc."; case 909: return "Resmed Ltd"; case 910: return "Crownstone B.V."; case 911: return "Xiaomi Inc."; case 912: return "INFOTECH s.r.o."; case 913: return "Thingsquare AB"; case 914: return "T&D"; case 915: return "LAVAZZA S.p.A."; case 916: return "Netclearance Systems, Inc."; case 917: return "SDATAWAY"; case 918: return "BLOKS GmbH"; case 919: return "LEGO System A/S"; case 920: return "Thetatronics Ltd"; case 921: return "Nikon Corporation"; case 922: return "NeST"; case 923: return "South Silicon Valley Microelectronics"; case 924: return "ALE International"; case 925: return "CareView Communications, Inc."; case 926: return "SchoolBoard Limited"; case 927: return "Molex Corporation"; case 928: return "IVT Wireless Limited"; case 929: return "Alpine Labs LLC"; case 930: return "Candura Instruments"; case 931: return "SmartMovt Technology Co., Ltd"; case 932: return "Token Zero Ltd"; case 933: return "ACE CAD Enterprise Co., Ltd. (ACECAD)"; case 934: return "Medela, Inc"; case 935: return "AeroScout"; case 936: return "Esrille Inc."; case 937: return "THINKERLY SRL"; case 938: return "Exon Sp. z o.o."; case 939: return "Meizu Technology Co., Ltd."; case 940: return "Smablo LTD"; case 941: return "XiQ"; case 942: return "Allswell Inc."; case 943: return "Comm-N-Sense Corp DBA Verigo"; case 944: return "VIBRADORM GmbH"; case 945: return "Otodata Wireless Network Inc."; case 946: return "Propagation Systems Limited"; case 947: return "Midwest Instruments & Controls"; case 948: return "Alpha Nodus, inc."; case 949: return "petPOMM, Inc"; case 950: return "Mattel"; case 951: return "Airbly Inc."; case 952: return "A-Safe Limited"; case 953: return "FREDERIQUE CONSTANT SA"; case 954: return "Maxscend Microelectronics Company Limited"; case 955: return "Abbott"; case 956: return "ASB Bank Ltd"; case 957: return "amadas"; case 958: return "Applied Science, Inc."; case 959: return "iLumi Solutions Inc."; case 960: return "Arch Systems Inc."; case 961: return "Ember Technologies, Inc."; case 962: return "Snapchat Inc"; case 963: return "Casambi Technologies Oy"; case 964: return "Pico Technology Inc."; case 965: return "St. Jude Medical, Inc."; case 966: return "Intricon"; case 967: return "Structural Health Systems, Inc."; case 968: return "Avvel International"; case 969: return "Gallagher Group"; case 970: return "In2things Automation Pvt. Ltd."; case 971: return "SYSDEV Srl"; case 972: return "Vonkil Technologies Ltd"; case 973: return "Wynd Technologies, Inc."; case 974: return "CONTRINEX S.A."; case 975: return "MIRA, Inc."; case 976: return "Watteam Ltd"; case 977: return "Density Inc."; case 978: return "IOT Pot India Private Limited"; case 979: return "Sigma Connectivity AB"; case 980: return "PEG PEREGO SPA"; case 981: return "Wyzelink Systems Inc."; case 982: return "Yota Devices LTD"; case 983: return "FINSECUR"; case 984: return "Zen-Me Labs Ltd"; case 985: return "3IWare Co., Ltd."; case 986: return "EnOcean GmbH"; case 987: return "Instabeat, Inc"; case 988: return "Nima Labs"; case 989: return "Andreas Stihl AG & Co. KG"; case 990: return "Nathan Rhoades LLC"; case 991: return "Grob Technologies, LLC"; case 992: return "Actions (Zhuhai) Technology Co., Limited"; case 993: return "SPD Development Company Ltd"; case 994: return "Sensoan Oy"; case 995: return "Qualcomm Life Inc"; case 996: return "Chip-ing AG"; case 997: return "ffly4u"; case 998: return "IoT Instruments Oy"; case 999: return "TRUE Fitness Technology"; case 1000: return "Reiner Kartengeraete GmbH & Co. KG."; case 1001: return "SHENZHEN LEMONJOY TECHNOLOGY CO., LTD."; case 1002: return "Hello Inc."; case 1003: return "Evollve Inc."; case 1004: return "Jigowatts Inc."; case 1005: return "BASIC MICRO.COM,INC."; case 1006: return "CUBE TECHNOLOGIES"; case 1007: return "foolography GmbH"; case 1008: return "CLINK"; case 1009: return "Hestan Smart Cooking Inc."; case 1010: return "WindowMaster A/S"; case 1011: return "Flowscape AB"; case 1012: return "PAL Technologies Ltd"; case 1013: return "WHERE, Inc."; case 1014: return "Iton Technology Corp."; case 1015: return "Owl Labs Inc."; case 1016: return "Rockford Corp."; case 1017: return "Becon Technologies Co.,Ltd."; case 1018: return "Vyassoft Technologies Inc"; case 1019: return "Nox Medical"; case 1020: return "Kimberly-Clark"; case 1021: return "Trimble Navigation Ltd."; case 1022: return "Littelfuse"; case 1023: return "Withings"; case 1024: return "i-developer IT Beratung UG"; case 1025: return "Relations Inc."; case 1026: return "Sears Holdings Corporation"; case 1027: return "Gantner Electronic GmbH"; case 1028: return "Authomate Inc"; case 1029: return "Vertex International, Inc."; case 1030: return "Airtago"; case 1031: return "Swiss Audio SA"; case 1032: return "ToGetHome Inc."; case 1033: return "AXIS"; case 1034: return "Openmatics"; case 1035: return "Jana Care Inc."; case 1036: return "Senix Corporation"; case 1037: return "NorthStar Battery Company, LLC"; case 1038: return "SKF (U.K.) Limited"; case 1039: return "CO-AX Technology, Inc."; case 1040: return "Fender Musical Instruments"; case 1041: return "Luidia Inc"; case 1042: return "SEFAM"; case 1043: return "Wireless Cables Inc"; case 1044: return "Lightning Protection International Pty Ltd"; case 1045: return "Uber Technologies Inc"; case 1046: return "SODA GmbH"; case 1047: return "Fatigue Science"; case 1048: return "Alpine Electronics Inc."; case 1049: return "Novalogy LTD"; case 1050: return "Friday Labs Limited"; case 1051: return "OrthoAccel Technologies"; case 1052: return "WaterGuru, Inc."; case 1053: return "Benning Elektrotechnik und Elektronik GmbH & Co. KG"; case 1054: return "Dell Computer Corporation"; case 1055: return "Kopin Corporation"; case 1056: return "TecBakery GmbH"; case 1057: return "Backbone Labs, Inc."; case 1058: return "DELSEY SA"; case 1059: return "Chargifi Limited"; case 1060: return "Trainesense Ltd."; case 1061: return "Unify Software and Solutions GmbH & Co. KG"; case 1062: return "Husqvarna AB"; case 1063: return "Focus fleet and fuel management inc"; case 1064: return "SmallLoop, LLC"; case 1065: return "Prolon Inc."; case 1066: return "BD Medical"; case 1067: return "iMicroMed Incorporated"; case 1068: return "Ticto N.V."; case 1069: return "Meshtech AS"; case 1070: return "MemCachier Inc."; case 1071: return "Danfoss A/S"; case 1072: return "SnapStyk Inc."; case 1073: return "Amway Corporation"; case 1074: return "Silk Labs, Inc."; case 1075: return "Pillsy Inc."; case 1076: return "Hatch Baby, Inc."; case 1077: return "Blocks Wearables Ltd."; case 1078: return "Drayson Technologies (Europe) Limited"; case 1079: return "eBest IOT Inc."; case 1080: return "Helvar Ltd"; case 1081: return "Radiance Technologies"; case 1082: return "Nuheara Limited"; case 1083: return "Appside co., ltd."; case 1084: return "DeLaval"; case 1085: return "Coiler Corporation"; case 1086: return "Thermomedics, Inc."; case 1087: return "Tentacle Sync GmbH"; case 1088: return "Valencell, Inc."; case 1089: return "iProtoXi Oy"; case 1090: return "SECOM CO., LTD."; case 1091: return "Tucker International LLC"; case 1092: return "Metanate Limited"; case 1093: return "Kobian Canada Inc."; case 1094: return "NETGEAR, Inc."; case 1095: return "Fabtronics Australia Pty Ltd"; case 1096: return "Grand Centrix GmbH"; case 1097: return "1UP USA.com llc"; case 1098: return "SHIMANO INC."; case 1099: return "Nain Inc."; case 1100: return "LifeStyle Lock, LLC"; case 1101: return "VEGA Grieshaber KG"; case 1102: return "Xtrava Inc."; case 1103: return "TTS Tooltechnic Systems AG & Co. KG"; case 1104: return "Teenage Engineering AB"; case 1105: return "Tunstall Nordic AB"; case 1106: return "Svep Design Center AB"; case 1107: return "GreenPeak Technologies BV"; case 1108: return "Sphinx Electronics GmbH & Co KG"; case 1109: return "Atomation"; case 1110: return "Nemik Consulting Inc"; case 1111: return "RF INNOVATION"; case 1112: return "Mini Solution Co., Ltd."; case 1113: return "Lumenetix, Inc"; case 1114: return "2048450 Ontario Inc"; case 1115: return "SPACEEK LTD"; case 1116: return "Delta T Corporation"; case 1117: return "Boston Scientific Corporation"; case 1118: return "Nuviz, Inc."; case 1119: return "Real Time Automation, Inc."; case 1120: return "Kolibree"; case 1121: return "vhf elektronik GmbH"; case 1122: return "Bonsai Systems GmbH"; case 1123: return "Fathom Systems Inc."; case 1124: return "Bellman & Symfon"; case 1125: return "International Forte Group LLC"; case 1126: return "CycleLabs Solutions inc."; case 1127: return "Codenex Oy"; case 1128: return "Kynesim Ltd"; case 1129: return "Palago AB"; case 1130: return "INSIGMA INC."; case 1131: return "PMD Solutions"; case 1132: return "Qingdao Realtime Technology Co., Ltd."; case 1133: return "BEGA Gantenbrink-Leuchten KG"; case 1134: return "Pambor Ltd."; case 1135: return "Develco Products A/S"; case 1136: return "iDesign s.r.l."; case 1137: return "TiVo Corp"; case 1138: return "Control-J Pty Ltd"; case 1139: return "Steelcase, Inc."; case 1140: return "iApartment co., ltd."; case 1141: return "Icom inc."; case 1142: return "Oxstren Wearable Technologies Private Limited"; case 1143: return "Blue Spark Technologies"; case 1144: return "FarSite Communications Limited"; case 1145: return "mywerk system GmbH"; case 1146: return "Sinosun Technology Co., Ltd."; case 1147: return "MIYOSHI ELECTRONICS CORPORATION"; case 1148: return "POWERMAT LTD"; case 1149: return "Occly LLC"; case 1150: return "OurHub Dev IvS"; case 1151: return "Pro-Mark, Inc."; case 1152: return "Dynometrics Inc."; case 1153: return "Quintrax Limited"; case 1154: return "POS Tuning Udo Vosshenrich GmbH & Co. KG"; case 1155: return "Multi Care Systems B.V."; case 1156: return "Revol Technologies Inc"; case 1157: return "SKIDATA AG"; case 1158: return "DEV TECNOLOGIA INDUSTRIA, COMERCIO E MANUTENCAO DE EQUIPAMENTOS LTDA. - ME"; case 1159: return "Centrica Connected Home"; case 1160: return "Automotive Data Solutions Inc"; case 1161: return "Igarashi Engineering"; case 1162: return "Taelek Oy"; case 1163: return "CP Electronics Limited"; case 1164: return "Vectronix AG"; case 1165: return "S-Labs Sp. z o.o."; case 1166: return "Companion Medical, Inc."; case 1167: return "BlueKitchen GmbH"; case 1168: return "Matting AB"; case 1169: return "SOREX - Wireless Solutions GmbH"; case 1170: return "ADC Technology, Inc."; case 1171: return "Lynxemi Pte Ltd"; case 1172: return "SENNHEISER electronic GmbH & Co. KG"; case 1173: return "LMT Mercer Group, Inc"; case 1174: return "Polymorphic Labs LLC"; case 1175: return "Cochlear Limited"; case 1176: return "METER Group, Inc. USA"; case 1177: return "Ruuvi Innovations Ltd."; case 1178: return "Situne AS"; case 1179: return "nVisti, LLC"; case 1180: return "DyOcean"; case 1181: return "Uhlmann & Zacher GmbH"; case 1182: return "AND!XOR LLC"; case 1183: return "tictote AB"; case 1184: return "Vypin, LLC"; case 1185: return "PNI Sensor Corporation"; case 1186: return "ovrEngineered, LLC"; case 1187: return "GT-tronics HK Ltd"; case 1188: return "Herbert Waldmann GmbH & Co. KG"; case 1189: return "Guangzhou FiiO Electronics Technology Co.,Ltd"; case 1190: return "Vinetech Co., Ltd"; case 1191: return "Dallas Logic Corporation"; case 1192: return "BioTex, Inc."; case 1193: return "DISCOVERY SOUND TECHNOLOGY, LLC"; case 1194: return "LINKIO SAS"; case 1195: return "Harbortronics, Inc."; case 1196: return "Undagrid B.V."; case 1197: return "Shure Inc"; case 1198: return "ERM Electronic Systems LTD"; case 1199: return "BIOROWER Handelsagentur GmbH"; case 1200: return "Weba Sport und Med. Artikel GmbH"; case 1201: return "Kartographers Technologies Pvt. Ltd."; case 1202: return "The Shadow on the Moon"; case 1203: return "mobike (Hong Kong) Limited"; case 1204: return "Inuheat Group AB"; case 1205: return "Swiftronix AB"; case 1206: return "Diagnoptics Technologies"; case 1207: return "Analog Devices, Inc."; case 1208: return "Soraa Inc."; case 1209: return "CSR Building Products Limited"; case 1210: return "Crestron Electronics, Inc."; case 1211: return "Neatebox Ltd"; case 1212: return "Draegerwerk AG & Co. KGaA"; case 1213: return "AlbynMedical"; case 1214: return "Averos FZCO"; case 1215: return "VIT Initiative, LLC"; case 1216: return "Statsports International"; case 1217: return "Sospitas, s.r.o."; case 1218: return "Dmet Products Corp."; case 1219: return "Mantracourt Electronics Limited"; case 1220: return "TeAM Hutchins AB"; case 1221: return "Seibert Williams Glass, LLC"; case 1222: return "Insta GmbH"; case 1223: return "Svantek Sp. z o.o."; case 1224: return "Shanghai Flyco Electrical Appliance Co., Ltd."; case 1225: return "Thornwave Labs Inc"; case 1226: return "Steiner-Optik GmbH"; case 1227: return "Novo Nordisk A/S"; case 1228: return "Enflux Inc."; case 1229: return "Safetech Products LLC"; case 1230: return "GOOOLED S.R.L."; case 1231: return "DOM Sicherheitstechnik GmbH & Co. KG"; case 1232: return "Olympus Corporation"; case 1233: return "KTS GmbH"; case 1234: return "Anloq Technologies Inc."; case 1235: return "Queercon, Inc"; case 1236: return "5th Element Ltd"; case 1237: return "Gooee Limited"; case 1238: return "LUGLOC LLC"; case 1239: return "Blincam, Inc."; case 1240: return "FUJIFILM Corporation"; case 1241: return "RandMcNally"; case 1242: return "Franceschi Marina snc"; case 1243: return "Engineered Audio, LLC."; case 1244: return "IOTTIVE (OPC) PRIVATE LIMITED"; case 1245: return "4MOD Technology"; case 1246: return "Lutron Electronics Co., Inc."; case 1247: return "Emerson"; case 1248: return "Guardtec, Inc."; case 1249: return "REACTEC LIMITED"; case 1250: return "EllieGrid"; case 1251: return "Under Armour"; case 1252: return "Woodenshark"; case 1253: return "Avack Oy"; case 1254: return "Smart Solution Technology, Inc."; case 1255: return "REHABTRONICS INC."; case 1256: return "STABILO International"; case 1257: return "Busch Jaeger Elektro GmbH"; case 1258: return "Pacific Bioscience Laboratories, Inc"; case 1259: return "Bird Home Automation GmbH"; case 1260: return "Motorola Solutions"; case 1261: return "R9 Technology, Inc."; case 1262: return "Auxivia"; case 1263: return "DaisyWorks, Inc"; case 1264: return "Kosi Limited"; case 1265: return "Theben AG"; case 1266: return "InDreamer Techsol Private Limited"; case 1267: return "Cerevast Medical"; case 1268: return "ZanCompute Inc."; case 1269: return "Pirelli Tyre S.P.A."; case 1270: return "McLear Limited"; case 1271: return "Shenzhen Huiding Technology Co.,Ltd."; case 1272: return "Convergence Systems Limited"; case 1273: return "Interactio"; case 1274: return "Androtec GmbH"; case 1275: return "Benchmark Drives GmbH & Co. KG"; case 1276: return "SwingLync L. L. C."; case 1277: return "Tapkey GmbH"; case 1278: return "Woosim Systems Inc."; case 1279: return "Microsemi Corporation"; case 1280: return "Wiliot LTD."; case 1281: return "Polaris IND"; case 1282: return "Specifi-Kali LLC"; case 1283: return "Locoroll, Inc"; case 1284: return "PHYPLUS Inc"; case 1285: return "Inplay Technologies LLC"; case 1286: return "Hager"; case 1287: return "Yellowcog"; case 1288: return "Axes System sp. z o. o."; case 1289: return "myLIFTER Inc."; case 1290: return "Shake-on B.V."; case 1291: return "Vibrissa Inc."; case 1292: return "OSRAM GmbH"; case 1293: return "TRSystems GmbH"; case 1294: return "Yichip Microelectronics (Hangzhou) Co.,Ltd."; case 1295: return "Foundation Engineering LLC"; case 1296: return "UNI-ELECTRONICS, INC."; case 1297: return "Brookfield Equinox LLC"; case 1298: return "Soprod SA"; case 1299: return "9974091 Canada Inc."; case 1300: return "FIBRO GmbH"; case 1301: return "RB Controls Co., Ltd."; case 1302: return "Footmarks"; case 1303: return "Amtronic Sverige AB (formerly Amcore AB)"; case 1304: return "MAMORIO.inc"; case 1305: return "Tyto Life LLC"; case 1306: return "Leica Camera AG"; case 1307: return "Angee Technologies Ltd."; case 1308: return "EDPS"; case 1309: return "OFF Line Co., Ltd."; case 1310: return "Detect Blue Limited"; case 1311: return "Setec Pty Ltd"; case 1312: return "Target Corporation"; case 1313: return "IAI Corporation"; case 1314: return "NS Tech, Inc."; case 1315: return "MTG Co., Ltd."; case 1316: return "Hangzhou iMagic Technology Co., Ltd"; case 1317: return "HONGKONG NANO IC TECHNOLOGIES CO., LIMITED"; case 1318: return "Honeywell International Inc."; case 1319: return "Albrecht JUNG"; case 1320: return "Lunera Lighting Inc."; case 1321: return "Lumen UAB"; case 1322: return "Keynes Controls Ltd"; case 1323: return "Novartis AG"; case 1324: return "Geosatis SA"; case 1325: return "EXFO, Inc."; case 1326: return "LEDVANCE GmbH"; case 1327: return "Center ID Corp."; case 1328: return "Adolene, Inc."; case 1329: return "D&M Holdings Inc."; case 1330: return "CRESCO Wireless, Inc."; case 1331: return "Nura Operations Pty Ltd"; case 1332: return "Frontiergadget, Inc."; case 1333: return "Smart Component Technologies Limited"; case 1334: return "ZTR Control Systems LLC"; case 1335: return "MetaLogics Corporation"; case 1336: return "Medela AG"; case 1337: return "OPPLE Lighting Co., Ltd"; case 1338: return "Savitech Corp.,"; case 1339: return "prodigy"; case 1340: return "Screenovate Technologies Ltd"; case 1341: return "TESA SA"; case 1342: return "CLIM8 LIMITED"; case 1343: return "Silergy Corp"; case 1344: return "SilverPlus, Inc"; case 1345: return "Sharknet srl"; case 1346: return "Mist Systems, Inc."; case 1347: return "MIWA LOCK CO.,Ltd"; case 1348: return "OrthoSensor, Inc."; case 1349: return "Candy Hoover Group s.r.l"; case 1350: return "Apexar Technologies S.A."; case 1351: return "LOGICDATA d.o.o."; case 1352: return "Knick Elektronische Messgeraete GmbH & Co. KG"; case 1353: return "Smart Technologies and Investment Limited"; case 1354: return "Linough Inc."; case 1355: return "Advanced Electronic Designs, Inc."; case 1356: return "Carefree Scott Fetzer Co Inc"; case 1357: return "Sensome"; case 1358: return "FORTRONIK storitve d.o.o."; case 1359: return "Sinnoz"; case 1360: return "Versa Networks, Inc."; case 1361: return "Sylero"; case 1362: return "Avempace SARL"; case 1363: return "Nintendo Co., Ltd."; case 1364: return "National Instruments"; case 1365: return "KROHNE Messtechnik GmbH"; case 1366: return "Otodynamics Ltd"; case 1367: return "Arwin Technology Limited"; case 1368: return "benegear, inc."; case 1369: return "Newcon Optik"; case 1370: return "CANDY HOUSE, Inc."; case 1371: return "FRANKLIN TECHNOLOGY INC"; case 1372: return "Lely"; case 1373: return "Valve Corporation"; case 1374: return "Hekatron Vertriebs GmbH"; case 1375: return "PROTECH S.A.S. DI GIRARDI ANDREA & C."; case 1376: return "Sarita CareTech APS (formerly Sarita CareTech IVS)"; case 1377: return "Finder S.p.A."; case 1378: return "Thalmic Labs Inc."; case 1379: return "Steinel Vertrieb GmbH"; case 1380: return "Beghelli Spa"; case 1381: return "Beijing Smartspace Technologies Inc."; case 1382: return "CORE TRANSPORT TECHNOLOGIES NZ LIMITED"; case 1383: return "Xiamen Everesports Goods Co., Ltd"; case 1384: return "Bodyport Inc."; case 1385: return "Audionics System, INC."; case 1386: return "Flipnavi Co.,Ltd."; case 1387: return "Rion Co., Ltd."; case 1388: return "Long Range Systems, LLC"; case 1389: return "Redmond Industrial Group LLC"; case 1390: return "VIZPIN INC."; case 1391: return "BikeFinder AS"; case 1392: return "Consumer Sleep Solutions LLC"; case 1393: return "PSIKICK, INC."; case 1394: return "AntTail.com"; case 1395: return "Lighting Science Group Corp."; case 1396: return "AFFORDABLE ELECTRONICS INC"; case 1397: return "Integral Memroy Plc"; case 1398: return "Globalstar, Inc."; case 1399: return "True Wearables, Inc."; case 1400: return "Wellington Drive Technologies Ltd"; case 1401: return "Ensemble Tech Private Limited"; case 1402: return "OMNI Remotes"; case 1403: return "Duracell U.S. Operations Inc."; case 1404: return "Toor Technologies LLC"; case 1405: return "Instinct Performance"; case 1406: return "Beco, Inc"; case 1407: return "Scuf Gaming International, LLC"; case 1408: return "ARANZ Medical Limited"; case 1409: return "LYS TECHNOLOGIES LTD"; case 1410: return "Breakwall Analytics, LLC"; case 1411: return "Code Blue Communications"; case 1412: return "Gira Giersiepen GmbH & Co. KG"; case 1413: return "Hearing Lab Technology"; case 1414: return "LEGRAND"; case 1415: return "Derichs GmbH"; case 1416: return "ALT-TEKNIK LLC"; case 1417: return "Star Technologies"; case 1418: return "START TODAY CO.,LTD."; case 1419: return "Maxim Integrated Products"; case 1420: return "MERCK Kommanditgesellschaft auf Aktien"; case 1421: return "Jungheinrich Aktiengesellschaft"; case 1422: return "Oculus VR, LLC"; case 1423: return "HENDON SEMICONDUCTORS PTY LTD"; case 1424: return "Pur3 Ltd"; case 1425: return "Viasat Group S.p.A."; case 1426: return "IZITHERM"; case 1427: return "Spaulding Clinical Research"; case 1428: return "Kohler Company"; case 1429: return "Inor Process AB"; case 1430: return "My Smart Blinds"; case 1431: return "RadioPulse Inc"; case 1432: return "rapitag GmbH"; case 1433: return "Lazlo326, LLC."; case 1434: return "Teledyne Lecroy, Inc."; case 1435: return "Dataflow Systems Limited"; case 1436: return "Macrogiga Electronics"; case 1437: return "Tandem Diabetes Care"; case 1438: return "Polycom, Inc."; case 1439: return "Fisher & Paykel Healthcare"; case 1440: return "RCP Software Oy"; case 1441: return "Shanghai Xiaoyi Technology Co.,Ltd."; case 1442: return "ADHERIUM(NZ) LIMITED"; case 1443: return "Axiomware Systems Incorporated"; case 1444: return "O. E. M. Controls, Inc."; case 1445: return "Kiiroo BV"; case 1446: return "Telecon Mobile Limited"; case 1447: return "Sonos Inc"; case 1448: return "Tom Allebrandi Consulting"; case 1449: return "Monidor"; case 1450: return "Tramex Limited"; case 1451: return "Nofence AS"; case 1452: return "GoerTek Dynaudio Co., Ltd."; case 1453: return "INIA"; case 1454: return "CARMATE MFG.CO.,LTD"; case 1455: return "ONvocal"; case 1456: return "NewTec GmbH"; case 1457: return "Medallion Instrumentation Systems"; case 1458: return "CAREL INDUSTRIES S.P.A."; case 1459: return "Parabit Systems, Inc."; case 1460: return "White Horse Scientific ltd"; case 1461: return "verisilicon"; case 1462: return "Elecs Industry Co.,Ltd."; case 1463: return "Beijing Pinecone Electronics Co.,Ltd."; case 1464: return "Ambystoma Labs Inc."; case 1465: return "Suzhou Pairlink Network Technology"; case 1466: return "igloohome"; case 1467: return "Oxford Metrics plc"; case 1468: return "Leviton Mfg. Co., Inc."; case 1469: return "ULC Robotics Inc."; case 1470: return "RFID Global by Softwork SrL"; case 1471: return "Real-World-Systems Corporation"; case 1472: return "Nalu Medical, Inc."; case 1473: return "P.I.Engineering"; case 1474: return "Grote Industries"; case 1475: return "Runtime, Inc."; case 1476: return "Codecoup sp. z o.o. sp. k."; case 1477: return "SELVE GmbH & Co. KG"; case 1478: return "Smart Animal Training Systems, LLC"; case 1479: return "Lippert Components, INC"; case 1480: return "SOMFY SAS"; case 1481: return "TBS Electronics B.V."; case 1482: return "MHL Custom Inc"; case 1483: return "LucentWear LLC"; case 1484: return "WATTS ELECTRONICS"; case 1485: return "RJ Brands LLC"; case 1486: return "V-ZUG Ltd"; case 1487: return "Biowatch SA"; case 1488: return "Anova Applied Electronics"; case 1489: return "Lindab AB"; case 1490: return "frogblue TECHNOLOGY GmbH"; case 1491: return "Acurable Limited"; case 1492: return "LAMPLIGHT Co., Ltd."; case 1493: return "TEGAM, Inc."; case 1494: return "Zhuhai Jieli technology Co.,Ltd"; case 1495: return "modum.io AG"; case 1496: return "Farm Jenny LLC"; case 1497: return "Toyo Electronics Corporation"; case 1498: return "Applied Neural Research Corp"; case 1499: return "Avid Identification Systems, Inc."; case 1500: return "Petronics Inc."; case 1501: return "essentim GmbH"; case 1502: return "QT Medical INC."; case 1503: return "VIRTUALCLINIC.DIRECT LIMITED"; case 1504: return "Viper Design LLC"; case 1505: return "Human, Incorporated"; case 1506: return "stAPPtronics GmbH"; case 1507: return "Elemental Machines, Inc."; case 1508: return "Taiyo Yuden Co., Ltd"; case 1509: return "INEO ENERGY& SYSTEMS"; case 1510: return "Motion Instruments Inc."; case 1511: return "PressurePro"; case 1512: return "COWBOY"; case 1513: return "iconmobile GmbH"; case 1514: return "ACS-Control-System GmbH"; case 1515: return "Bayerische Motoren Werke AG"; case 1516: return "Gycom Svenska AB"; case 1517: return "Fuji Xerox Co., Ltd"; case 1518: return "Glide Inc."; case 1519: return "SIKOM AS"; case 1520: return "beken"; case 1521: return "The Linux Foundation"; case 1522: return "Try and E CO.,LTD."; case 1523: return "SeeScan"; case 1524: return "Clearity, LLC"; case 1525: return "GS TAG"; case 1526: return "DPTechnics"; case 1527: return "TRACMO, INC."; case 1528: return "Anki Inc."; case 1529: return "Hagleitner Hygiene International GmbH"; case 1530: return "Konami Sports Life Co., Ltd."; case 1531: return "Arblet Inc."; case 1532: return "Masbando GmbH"; case 1533: return "Innoseis"; case 1534: return "Niko nv"; case 1535: return "Wellnomics Ltd"; case 1536: return "iRobot Corporation"; case 1537: return "Schrader Electronics"; case 1538: return "Geberit International AG"; case 1539: return "Fourth Evolution Inc"; case 1540: return "Cell2Jack LLC"; case 1541: return "FMW electronic Futterer u. Maier-Wolf OHG"; case 1542: return "John Deere"; case 1543: return "Rookery Technology Ltd"; case 1544: return "KeySafe-Cloud"; case 1545: return "BUCHI Labortechnik AG"; case 1546: return "IQAir AG"; case 1547: return "Triax Technologies Inc"; case 1548: return "Vuzix Corporation"; case 1549: return "TDK Corporation"; case 1550: return "Blueair AB"; case 1551: return "Signify Netherlands"; case 1552: return "ADH GUARDIAN USA LLC"; case 1553: return "Beurer GmbH"; case 1554: return "Playfinity AS"; case 1555: return "Hans Dinslage GmbH"; case 1556: return "OnAsset Intelligence, Inc."; case 1557: return "INTER ACTION Corporation"; case 1558: return "OS42 UG (haftungsbeschraenkt)"; case 1559: return "WIZCONNECTED COMPANY LIMITED"; case 1560: return "Audio-Technica Corporation"; case 1561: return "Six Guys Labs, s.r.o."; case 1562: return "R.W. Beckett Corporation"; case 1563: return "silex technology, inc."; case 1564: return "Univations Limited"; case 1565: return "SENS Innovation ApS"; case 1566: return "Diamond Kinetics, Inc."; case 1567: return "Phrame Inc."; case 1568: return "Forciot Oy"; case 1569: return "Noordung d.o.o."; case 1570: return "Beam Labs, LLC"; case 1571: return "Philadelphia Scientific (U.K.) Limited"; case 1572: return "Biovotion AG"; case 1573: return "Square Panda, Inc."; case 1574: return "Amplifico"; case 1575: return "WEG S.A."; case 1576: return "Ensto Oy"; case 1577: return "PHONEPE PVT LTD"; case 1578: return "Lunatico Astronomia SL"; case 1579: return "MinebeaMitsumi Inc."; case 1580: return "ASPion GmbH"; case 1581: return "Vossloh-Schwabe Deutschland GmbH"; case 1582: return "Procept"; case 1583: return "ONKYO Corporation"; case 1584: return "Asthrea D.O.O."; case 1585: return "Fortiori Design LLC"; case 1586: return "Hugo Muller GmbH & Co KG"; case 1587: return "Wangi Lai PLT"; case 1588: return "Fanstel Corp"; case 1589: return "Crookwood"; case 1590: return "ELECTRONICA INTEGRAL DE SONIDO S.A."; case 1591: return "GiP Innovation Tools GmbH"; case 1592: return "LX SOLUTIONS PTY LIMITED"; case 1593: return "Shenzhen Minew Technologies Co., Ltd."; case 1594: return "Prolojik Limited"; case 1595: return "Kromek Group Plc"; case 1596: return "Contec Medical Systems Co., Ltd."; case 1597: return "Xradio Technology Co.,Ltd."; case 1598: return "The Indoor Lab, LLC"; case 1599: return "LDL TECHNOLOGY"; case 1600: return "Parkifi"; case 1601: return "Revenue Collection Systems FRANCE SAS"; case 1602: return "Bluetrum Technology Co.,Ltd"; case 1603: return "makita corporation"; case 1604: return "Apogee Instruments"; case 1605: return "BM3"; case 1606: return "SGV Group Holding GmbH & Co. KG"; case 1607: return "MED-EL"; case 1608: return "Ultune Technologies"; case 1609: return "Ryeex Technology Co.,Ltd."; case 1610: return "Open Research Institute, Inc."; case 1611: return "Scale-Tec, Ltd"; case 1612: return "Zumtobel Group AG"; case 1613: return "iLOQ Oy"; case 1614: return "KRUXWorks Technologies Private Limited"; case 1615: return "Digital Matter Pty Ltd"; case 1616: return "Coravin, Inc."; case 1617: return "Stasis Labs, Inc."; case 1618: return "ITZ Innovations- und Technologiezentrum GmbH"; case 1619: return "Meggitt SA"; case 1620: return "Ledlenser GmbH & Co. KG"; case 1621: return "Renishaw PLC"; case 1622: return "ZhuHai AdvanPro Technology Company Limited"; case 1623: return "Meshtronix Limited"; case 1624: return "Payex Norge AS"; case 1625: return "UnSeen Technologies Oy"; case 1626: return "Zound Industries International AB"; case 1627: return "Sesam Solutions BV"; case 1628: return "PixArt Imaging Inc."; case 1629: return "Panduit Corp."; case 1630: return "Alo AB"; case 1631: return "Ricoh Company Ltd"; case 1632: return "RTC Industries, Inc."; case 1633: return "Mode Lighting Limited"; case 1634: return "Particle Industries, Inc."; case 1635: return "Advanced Telemetry Systems, Inc."; case 1636: return "RHA TECHNOLOGIES LTD"; case 1637: return "Pure International Limited"; case 1638: return "WTO Werkzeug-Einrichtungen GmbH"; case 1639: return "Spark Technology Labs Inc."; case 1640: return "Bleb Technology srl"; case 1641: return "Livanova USA, Inc."; case 1642: return "Brady Worldwide Inc."; case 1643: return "DewertOkin GmbH"; case 1644: return "Ztove ApS"; case 1645: return "Venso EcoSolutions AB"; case 1646: return "Eurotronik Kranj d.o.o."; case 1647: return "Hug Technology Ltd"; case 1648: return "Gema Switzerland GmbH"; case 1649: return "Buzz Products Ltd."; case 1650: return "Kopi"; case 1651: return "Innova Ideas Limited"; case 1652: return "BeSpoon"; case 1653: return "Deco Enterprises, Inc."; case 1654: return "Expai Solutions Private Limited"; case 1655: return "Innovation First, Inc."; case 1656: return "SABIK Offshore GmbH"; case 1657: return "4iiii Innovations Inc."; case 1658: return "The Energy Conservatory, Inc."; case 1659: return "I.FARM, INC."; case 1660: return "Tile, Inc."; case 1661: return "Form Athletica Inc."; case 1662: return "MbientLab Inc"; case 1663: return "NETGRID S.N.C. DI BISSOLI MATTEO, CAMPOREALE SIMONE, TOGNETTI FEDERICO"; case 1664: return "Mannkind Corporation"; case 1665: return "Trade FIDES a.s."; case 1666: return "Photron Limited"; case 1667: return "Eltako GmbH"; case 1668: return "Dermalapps, LLC"; case 1669: return "Greenwald Industries"; case 1670: return "inQs Co., Ltd."; case 1671: return "Cherry GmbH"; case 1672: return "Amsted Digital Solutions Inc."; case 1673: return "Tacx b.v."; case 1674: return "Raytac Corporation"; case 1675: return "Jiangsu Teranovo Tech Co., Ltd."; case 1676: return "Changzhou Sound Dragon Electronics and Acoustics Co., Ltd"; case 1677: return "JetBeep Inc."; case 1678: return "Razer Inc."; case 1679: return "JRM Group Limited"; case 1680: return "Eccrine Systems, Inc."; case 1681: return "Curie Point AB"; case 1682: return "Georg Fischer AG"; case 1683: return "Hach - Danaher"; case 1684: return "T&A Laboratories LLC"; case 1685: return "Koki Holdings Co., Ltd."; case 1686: return "Gunakar Private Limited"; case 1687: return "Stemco Products Inc"; case 1688: return "Wood IT Security, LLC"; case 1689: return "RandomLab SAS"; case 1690: return "Adero, Inc. (formerly as TrackR, Inc.)"; case 1691: return "Dragonchip Limited"; case 1692: return "Noomi AB"; case 1693: return "Vakaros LLC"; case 1694: return "Delta Electronics, Inc."; case 1695: return "FlowMotion Technologies AS"; case 1696: return "OBIQ Location Technology Inc."; case 1697: return "Cardo Systems, Ltd"; case 1698: return "Globalworx GmbH"; case 1699: return "Nymbus, LLC"; case 1700: return "Sanyo Techno Solutions Tottori Co., Ltd."; case 1701: return "TEKZITEL PTY LTD"; case 1702: return "Roambee Corporation"; case 1703: return "Chipsea Technologies (ShenZhen) Corp."; case 1704: return "GD Midea Air-Conditioning Equipment Co., Ltd."; case 1705: return "Soundmax Electronics Limited"; case 1706: return "Produal Oy"; case 1707: return "HMS Industrial Networks AB"; case 1708: return "Ingchips Technology Co., Ltd."; case 1709: return "InnovaSea Systems Inc."; case 1710: return "SenseQ Inc."; case 1711: return "Shoof Technologies"; case 1712: return "BRK Brands, Inc."; case 1713: return "SimpliSafe, Inc."; case 1714: return "Tussock Innovation 2013 Limited"; case 1715: return "The Hablab ApS"; case 1716: return "Sencilion Oy"; case 1717: return "Wabilogic Ltd."; case 1718: return "Sociometric Solutions, Inc."; case 1719: return "iCOGNIZE GmbH"; case 1720: return "ShadeCraft, Inc"; case 1721: return "Beflex Inc."; case 1722: return "Beaconzone Ltd"; case 1723: return "Leaftronix Analogic Solutions Private Limited"; case 1724: return "TWS Srl"; case 1725: return "ABB Oy"; case 1726: return "HitSeed Oy"; case 1727: return "Delcom Products Inc."; case 1728: return "CAME S.p.A."; case 1729: return "Alarm.com Holdings, Inc"; case 1730: return "Measurlogic Inc."; case 1731: return "King I Electronics.Co.,Ltd"; case 1732: return "Dream Labs GmbH"; case 1733: return "Urban Compass, Inc"; case 1734: return "Simm Tronic Limited"; case 1735: return "Somatix Inc"; case 1736: return "Storz & Bickel GmbH & Co. KG"; case 1737: return "MYLAPS B.V."; case 1738: return "Shenzhen Zhongguang Infotech Technology Development Co., Ltd"; case 1739: return "Dyeware, LLC"; case 1740: return "Dongguan SmartAction Technology Co.,Ltd."; case 1741: return "DIG Corporation"; case 1742: return "FIOR & GENTZ"; case 1743: return "Belparts N.V."; case 1744: return "Etekcity Corporation"; case 1745: return "Meyer Sound Laboratories, Incorporated"; case 1746: return "CeoTronics AG"; case 1747: return "TriTeq Lock and Security, LLC"; case 1748: return "DYNAKODE TECHNOLOGY PRIVATE LIMITED"; case 1749: return "Sensirion AG"; case 1750: return "JCT Healthcare Pty Ltd"; case 1751: return "FUBA Automotive Electronics GmbH"; case 1752: return "AW Company"; case 1753: return "Shanghai Mountain View Silicon Co.,Ltd."; case 1754: return "Zliide Technologies ApS"; case 1755: return "Automatic Labs, Inc."; case 1756: return "Industrial Network Controls, LLC"; case 1757: return "Intellithings Ltd."; case 1758: return "Navcast, Inc."; case 1759: return "Hubbell Lighting, Inc."; case 1760: return "Avaya "; case 1761: return "Milestone AV Technologies LLC"; case 1762: return "Alango Technologies Ltd"; case 1763: return "Spinlock Ltd"; case 1764: return "Aluna"; case 1765: return "OPTEX CO.,LTD."; case 1766: return "NIHON DENGYO KOUSAKU"; case 1767: return "VELUX A/S"; case 1768: return "Almendo Technologies GmbH"; case 1769: return "Zmartfun Electronics, Inc."; case 1770: return "SafeLine Sweden AB"; case 1771: return "Houston Radar LLC"; case 1772: return "Sigur"; case 1773: return "J Neades Ltd"; case 1774: return "Avantis Systems Limited"; case 1775: return "ALCARE Co., Ltd."; case 1776: return "Chargy Technologies, SL"; case 1777: return "Shibutani Co., Ltd."; case 1778: return "Trapper Data AB"; case 1779: return "Alfred International Inc."; case 1780: return "Near Field Solutions Ltd"; case 1781: return "Vigil Technologies Inc."; case 1782: return "Vitulo Plus BV"; case 1783: return "WILKA Schliesstechnik GmbH"; case 1784: return "BodyPlus Technology Co.,Ltd"; case 1785: return "happybrush GmbH"; case 1786: return "Enequi AB"; case 1787: return "Sartorius AG"; case 1788: return "Tom Communication Industrial Co.,Ltd."; case 1789: return "ESS Embedded System Solutions Inc."; case 1790: return "Mahr GmbH"; case 1791: return "Redpine Signals Inc"; case 1792: return "TraqFreq LLC"; case 1793: return "PAFERS TECH"; case 1794: return "Akciju sabiedriba \"SAF TEHNIKA\""; case 1795: return "Beijing Jingdong Century Trading Co., Ltd."; case 1796: return "JBX Designs Inc."; case 1797: return "AB Electrolux"; case 1798: return "Wernher von Braun Center for ASdvanced Research"; case 1799: return "Essity Hygiene and Health Aktiebolag"; case 1800: return "Be Interactive Co., Ltd"; case 1801: return "Carewear Corp."; case 1802: return "Huf Hülsbeck & Fürst GmbH & Co. KG"; case 1803: return "Element Products, Inc."; case 1804: return "Beijing Winner Microelectronics Co.,Ltd"; case 1805: return "SmartSnugg Pty Ltd"; case 1806: return "FiveCo Sarl"; case 1807: return "California Things Inc."; case 1808: return "Audiodo AB"; case 1809: return "ABAX AS"; case 1810: return "Bull Group Company Limited"; case 1811: return "Respiri Limited"; case 1812: return "MindPeace Safety LLC"; case 1813: return "Vgyan Solutions"; case 1814: return "Altonics"; case 1815: return "iQsquare BV"; case 1816: return "IDIBAIX enginneering"; case 1817: return "ECSG"; case 1818: return "REVSMART WEARABLE HK CO LTD"; case 1819: return "Precor"; case 1820: return "F5 Sports, Inc"; case 1821: return "exoTIC Systems"; case 1822: return "DONGGUAN HELE ELECTRONICS CO., LTD"; case 1823: return "Dongguan Liesheng Electronic Co.Ltd"; case 1824: return "Oculeve, Inc."; case 1825: return "Clover Network, Inc."; case 1826: return "Xiamen Eholder Electronics Co.Ltd"; case 1827: return "Ford Motor Company"; case 1828: return "Guangzhou SuperSound Information Technology Co.,Ltd"; case 1829: return "Tedee Sp. z o.o."; case 1830: return "PHC Corporation"; case 1831: return "STALKIT AS"; case 1832: return "Eli Lilly and Company"; case 1833: return "SwaraLink Technologies"; case 1834: return "JMR embedded systems GmbH"; case 1835: return "Bitkey Inc."; case 1836: return "GWA Hygiene GmbH"; case 1837: return "Safera Oy"; case 1838: return "Open Platform Systems LLC"; case 1839: return "OnePlus Electronics (Shenzhen) Co., Ltd."; case 1840: return "Wildlife Acoustics, Inc."; case 1841: return "ABLIC Inc."; case 1842: return "Dairy Tech, Inc."; case 1843: return "Iguanavation, Inc."; case 1844: return "DiUS Computing Pty Ltd"; case 1845: return "UpRight Technologies LTD"; case 1846: return "FrancisFund, LLC"; case 1847: return "LLC Navitek"; case 1848: return "Glass Security Pte Ltd"; case 1849: return "Jiangsu Qinheng Co., Ltd."; case 1850: return "Chandler Systems Inc."; case 1851: return "Fantini Cosmi s.p.a."; case 1852: return "Acubit ApS"; case 1853: return "Beijing Hao Heng Tian Tech Co., Ltd."; case 1854: return "Bluepack S.R.L."; case 1855: return "Beijing Unisoc Technologies Co., Ltd."; case 1856: return "HITIQ LIMITED"; case 1857: return "MAC SRL"; case 1858: return "DML LLC"; case 1859: return "Sanofi"; case 1860: return "SOCOMEC"; case 1861: return "WIZNOVA, Inc."; case 1862: return "Seitec Elektronik GmbH"; case 1863: return "OR Technologies Pty Ltd"; case 1864: return "GuangZhou KuGou Computer Technology Co.Ltd"; case 1865: return "DIAODIAO (Beijing) Technology Co., Ltd."; case 1866: return "Illusory Studios LLC"; case 1867: return "Sarvavid Software Solutions LLP"; case 1868: return "iopool s.a."; case 1869: return "Amtech Systems, LLC"; case 1870: return "EAGLE DETECTION SA"; case 1871: return "MEDIATECH S.R.L."; case 1872: return "Hamilton Professional Services of Canada Incorporated"; case 1873: return "Changsha JEMO IC Design Co.,Ltd"; case 1874: return "Elatec GmbH"; case 1875: return "JLG Industries, Inc."; case 1876: return "Michael Parkin"; case 1877: return "Brother Industries, Ltd"; case 1878: return "Lumens For Less, Inc"; case 1879: return "ELA Innovation"; case 1880: return "umanSense AB"; case 1881: return "Shanghai InGeek Cyber Security Co., Ltd."; case 1882: return "HARMAN CO.,LTD."; case 1883: return "Smart Sensor Devices AB"; case 1884: return "Antitronics Inc."; case 1885: return "RHOMBUS SYSTEMS, INC."; case 1886: return "Katerra Inc."; case 1887: return "Remote Solution Co., LTD."; case 1888: return "Vimar SpA"; case 1889: return "Mantis Tech LLC"; case 1890: return "TerOpta Ltd"; case 1891: return "PIKOLIN S.L."; case 1892: return "WWZN Information Technology Company Limited"; case 1893: return "Voxx International"; case 1894: return "ART AND PROGRAM, INC."; case 1895: return "NITTO DENKO ASIA TECHNICAL CENTRE PTE. LTD."; case 1896: return "Peloton Interactive Inc."; case 1897: return "Force Impact Technologies"; case 1898: return "Dmac Mobile Developments, LLC"; case 1899: return "Engineered Medical Technologies"; case 1900: return "Noodle Technology inc"; case 1901: return "Graesslin GmbH"; case 1902: return "WuQi technologies, Inc."; case 1903: return "Successful Endeavours Pty Ltd"; case 1904: return "InnoCon Medical ApS"; case 1905: return "Corvex Connected Safety"; case 1906: return "Thirdwayv Inc."; case 1907: return "Echoflex Solutions Inc."; case 1908: return "C-MAX Asia Limited"; case 1909: return "4eBusiness GmbH"; case 1910: return "Cyber Transport Control GmbH"; case 1911: return "Cue"; case 1912: return "KOAMTAC INC."; case 1913: return "Loopshore Oy"; case 1914: return "Niruha Systems Private Limited"; case 1915: return "AmaterZ, Inc."; case 1916: return "radius co., ltd."; case 1917: return "Sensority, s.r.o."; case 1918: return "Sparkage Inc."; case 1919: return "Glenview Software Corporation"; case 1920: return "Finch Technologies Ltd."; case 1921: return "Qingping Technology (Beijing) Co., Ltd."; case 1922: return "DeviceDrive AS"; case 1923: return "ESEMBER LIMITED LIABILITY COMPANY"; case 1924: return "audifon GmbH & Co. KG"; case 1925: return "O2 Micro, Inc."; case 1926: return "HLP Controls Pty Limited"; case 1927: return "Pangaea Solution"; case 1928: return "BubblyNet, LLC"; case 1930: return "The Wildflower Foundation"; case 1931: return "Optikam Tech Inc."; case 1932: return "MINIBREW HOLDING B.V"; case 1933: return "Cybex GmbH"; case 1934: return "FUJIMIC NIIGATA, INC."; case 1935: return "Hanna Instruments, Inc."; case 1936: return "KOMPAN A/S"; case 1937: return "Scosche Industries, Inc."; case 1938: return "Provo Craft"; case 1939: return "AEV spol. s r.o."; case 1940: return "The Coca-Cola Company"; case 1941: return "GASTEC CORPORATION"; case 1942: return "StarLeaf Ltd"; case 1943: return "Water-i.d. GmbH"; case 1944: return "HoloKit, Inc."; case 1945: return "PlantChoir Inc."; case 1946: return "GuangDong Oppo Mobile Telecommunications Corp., Ltd."; case 1947: return "CST ELECTRONICS (PROPRIETARY) LIMITED"; case 1948: return "Sky UK Limited"; case 1949: return "Digibale Pty Ltd"; case 1950: return "Smartloxx GmbH"; case 1951: return "Pune Scientific LLP"; case 1952: return "Regent Beleuchtungskorper AG"; case 1953: return "Apollo Neuroscience, Inc."; case 1954: return "Roku, Inc."; case 1955: return "Comcast Cable"; case 1956: return "Xiamen Mage Information Technology Co., Ltd."; case 1957: return "RAB Lighting, Inc."; case 1958: return "Musen Connect, Inc."; case 1959: return "Zume, Inc."; case 1960: return "conbee GmbH"; case 1961: return "Bruel & Kjaer Sound & Vibration"; case 1962: return "The Kroger Co."; case 1963: return "Granite River Solutions, Inc."; case 1964: return "LoupeDeck Oy"; case 1965: return "New H3C Technologies Co.,Ltd"; case 1966: return "Aurea Solucoes Tecnologicas Ltda."; case 1967: return "Hong Kong Bouffalo Lab Limited"; case 1968: return "GV Concepts Inc."; case 1969: return "Thomas Dynamics, LLC"; case 1970: return "Moeco IOT Inc."; case 1971: return "2N TELEKOMUNIKACE a.s."; case 1972: return "Hormann KG Antriebstechnik"; case 1973: return "CRONO CHIP, S.L."; case 1974: return "Soundbrenner Limited"; case 1975: return "ETABLISSEMENTS GEORGES RENAULT"; case 1976: return "iSwip"; case 1977: return "Epona Biotec Limited"; case 1978: return "Battery-Biz Inc."; case 1979: return "EPIC S.R.L."; case 1980: return "KD CIRCUITS LLC"; case 1981: return "Genedrive Diagnostics Ltd"; case 1982: return "Axentia Technologies AB"; case 1983: return "REGULA Ltd."; case 1984: return "Biral AG"; case 1985: return "A.W. Chesterton Company"; case 1986: return "Radinn AB"; case 1987: return "CIMTechniques, Inc."; case 1988: return "Johnson Health Tech NA"; case 1989: return "June Life, Inc."; case 1990: return "Bluenetics GmbH"; case 1991: return "iaconicDesign Inc."; case 1992: return "WRLDS Creations AB"; case 1993: return "Skullcandy, Inc."; case 1994: return "Modul-System HH AB"; case 1995: return "West Pharmaceutical Services, Inc."; case 1996: return "Barnacle Systems Inc."; case 1997: return "Smart Wave Technologies Canada Inc"; case 1998: return "Shanghai Top-Chip Microelectronics Tech. Co., LTD"; case 1999: return "NeoSensory, Inc."; case 2000: return "Hangzhou Tuya Information Technology Co., Ltd"; case 2001: return "Shanghai Panchip Microelectronics Co., Ltd"; case 2002: return "React Accessibility Limited"; case 2003: return "LIVNEX Co.,Ltd."; case 2004: return "Kano Computing Limited"; case 2005: return "hoots classic GmbH"; case 2006: return "ecobee Inc."; case 2007: return "Nanjing Qinheng Microelectronics Co., Ltd"; case 2008: return "SOLUTIONS AMBRA INC."; case 2009: return "Micro-Design, Inc."; case 2010: return "STARLITE Co., Ltd."; case 2011: return "Remedee Labs"; case 2012: return "ThingOS GmbH"; case 2013: return "Linear Circuits"; case 2014: return "Unlimited Engineering SL"; case 2015: return "Snap-on Incorporated"; case 2016: return "Edifier International Limited"; case 2017: return "Lucie Labs"; case 2018: return "Alfred Kaercher SE & Co. KG"; case 2019: return "Audiowise Technology Inc."; case 2020: return "Geeksme S.L."; case 2021: return "Minut, Inc."; case 2022: return "Autogrow Systems Limited"; case 2023: return "Komfort IQ, Inc."; case 2024: return "Packetcraft, Inc."; case 2025: return "Häfele GmbH & Co KG"; case 2026: return "ShapeLog, Inc."; case 2027: return "NOVABASE S.R.L."; case 2028: return "Frecce LLC"; case 2029: return "Joule IQ, INC."; case 2030: return "KidzTek LLC"; case 2031: return "Aktiebolaget Sandvik Coromant"; case 2032: return "e-moola.com Pty Ltd"; case 2033: return "GSM Innovations Pty Ltd"; case 2034: return "SERENE GROUP, INC"; case 2035: return "DIGISINE ENERGYTECH CO. LTD."; case 2036: return "MEDIRLAB Orvosbiologiai Fejleszto Korlatolt Felelossegu Tarsasag"; case 2037: return "Byton North America Corporation"; case 2038: return "Shenzhen TonliScience and Technology Development Co.,Ltd"; case 2039: return "Cesar Systems Ltd."; case 2040: return "quip NYC Inc."; case 2041: return "Direct Communication Solutions, Inc."; case 2042: return "Klipsch Group, Inc."; case 2043: return "Access Co., Ltd"; case 2044: return "Renault SA"; case 2045: return "JSK CO., LTD."; case 2046: return "BIROTA"; case 2047: return "maxon motor ltd."; case 2048: return "Optek"; case 2049: return "CRONUS ELECTRONICS LTD"; case 2050: return "NantSound, Inc."; case 2051: return "Domintell s.a."; case 2052: return "Andon Health Co.,Ltd"; case 2053: return "Urbanminded Ltd"; case 2054: return "TYRI Sweden AB"; case 2055: return "ECD Electronic Components GmbH Dresden"; case 2056: return "SISTEMAS KERN, SOCIEDAD ANÓMINA"; case 2057: return "Trulli Audio"; case 2058: return "Altaneos"; case 2059: return "Nanoleaf Canada Limited"; case 2060: return "Ingy B.V."; case 2061: return "Azbil Co."; case 2062: return "TATTCOM LLC"; case 2063: return "Paradox Engineering SA"; case 2064: return "LECO Corporation"; case 2065: return "Becker Antriebe GmbH"; case 2066: return "Mstream Technologies., Inc."; case 2067: return "Flextronics International USA Inc."; case 2068: return "Ossur hf."; case 2069: return "SKC Inc"; case 2070: return "SPICA SYSTEMS LLC"; case 2071: return "Wangs Alliance Corporation"; case 2072: return "tatwah SA"; case 2073: return "Hunter Douglas Inc"; case 2074: return "Shenzhen Conex"; case 2075: return "DIM3"; case 2076: return "Bobrick Washroom Equipment, Inc."; case 2077: return "Potrykus Holdings and Development LLC"; case 2078: return "iNFORM Technology GmbH"; case 2079: return "eSenseLab LTD"; case 2080: return "Brilliant Home Technology, Inc."; case 2081: return "INOVA Geophysical, Inc."; case 2082: return "adafruit industries"; case 2083: return "Nexite Ltd"; case 2084: return "8Power Limited"; case 2085: return "CME PTE. LTD."; case 2086: return "Hyundai Motor Company"; case 2087: return "Kickmaker"; case 2088: return "Shanghai Suisheng Information Technology Co., Ltd."; case 2089: return "HEXAGON"; case 2090: return "Mitutoyo Corporation"; case 2091: return "shenzhen fitcare electronics Co.,Ltd"; case 2092: return "INGICS TECHNOLOGY CO., LTD."; case 2093: return "INCUS PERFORMANCE LTD."; case 2094: return "ABB S.p.A."; case 2095: return "Blippit AB"; case 2096: return "Core Health and Fitness LLC"; case 2097: return "Foxble, LLC"; case 2098: return "Intermotive,Inc."; case 2099: return "Conneqtech B.V."; case 2100: return "RIKEN KEIKI CO., LTD.,"; case 2101: return "Canopy Growth Corporation"; case 2102: return "Bitwards Oy"; case 2103: return "vivo Mobile Communication Co., Ltd."; case 2104: return "Etymotic Research, Inc."; case 2105: return "A puissance 3"; case 2106: return "BPW Bergische Achsen Kommanditgesellschaft"; case 2107: return "Piaggio Fast Forward"; case 2108: return "BeerTech LTD"; case 2109: return "Tokenize, Inc."; case 2110: return "Zorachka LTD"; case 2111: return "D-Link Corp."; case 2112: return "Down Range Systems LLC"; case 2113: return "General Luminaire (Shanghai) Co., Ltd."; case 2114: return "Tangshan HongJia electronic technology co., LTD."; case 2115: return "FRAGRANCE DELIVERY TECHNOLOGIES LTD"; case 2116: return "Pepperl + Fuchs GmbH"; case 2117: return "Dometic Corporation"; case 2118: return "USound GmbH"; case 2119: return "DNANUDGE LIMITED"; case 2120: return "JUJU JOINTS CANADA CORP."; case 2121: return "Dopple Technologies B.V."; case 2122: return "ARCOM"; case 2123: return "Biotechware SRL"; case 2124: return "ORSO Inc."; case 2125: return "SafePort"; case 2126: return "Carol Cole Company"; case 2127: return "Embedded Fitness B.V."; case 2128: return "Yealink (Xiamen) Network Technology Co.,LTD"; case 2129: return "Subeca, Inc."; case 2130: return "Cognosos, Inc."; case 2131: return "Pektron Group Limited"; case 2132: return "Tap Sound System"; case 2133: return "Helios Hockey, Inc."; case 2134: return "Canopy Growth Corporation"; case 2135: return "Parsyl Inc"; case 2136: return "SOUNDBOKS"; case 2137: return "BlueUp"; case 2138: return "DAKATECH"; case 2139: return "RICOH ELECTRONIC DEVICES CO., LTD."; case 2140: return "ACOS CO.,LTD."; case 2141: return "Guilin Zhishen Information Technology Co.,Ltd."; case 2142: return "Krog Systems LLC"; case 2143: return "COMPEGPS TEAM,SOCIEDAD LIMITADA"; case 2144: return "Alflex Products B.V."; case 2145: return "SmartSensor Labs Ltd"; case 2146: return "SmartDrive Inc."; case 2147: return "Yo-tronics Technology Co., Ltd."; case 2148: return "Rafaelmicro"; case 2149: return "Emergency Lighting Products Limited"; case 2150: return "LAONZ Co.,Ltd"; case 2151: return "Western Digital Techologies, Inc."; case 2152: return "WIOsense GmbH & Co. KG"; case 2153: return "EVVA Sicherheitstechnologie GmbH"; case 2154: return "Odic Incorporated"; case 2155: return "Pacific Track, LLC"; case 2156: return "Revvo Technologies, Inc."; case 2157: return "Biometrika d.o.o."; case 2158: return "Vorwerk Elektrowerke GmbH & Co. KG"; case 2159: return "Trackunit A/S"; case 2160: return "Wyze Labs, Inc"; case 2161: return "Dension Elektronikai Kft. (formerly: Dension Audio Systems Ltd.)"; case 2162: return "11 Health & Technologies Limited"; case 2163: return "Innophase Incorporated"; case 2164: return "Treegreen Limited"; case 2165: return "Berner International LLC"; case 2166: return "SmartResQ ApS"; case 2167: return "Tome, Inc."; case 2168: return "The Chamberlain Group, Inc."; case 2169: return "MIZUNO Corporation"; case 2170: return "ZRF, LLC"; case 2171: return "BYSTAMP"; case 2172: return "Crosscan GmbH"; case 2173: return "Konftel AB"; case 2174: return "1bar.net Limited"; case 2175: return "Phillips Connect Technologies LLC"; case 2176: return "imagiLabs AB"; case 2177: return "Optalert"; case 2178: return "PSYONIC, Inc."; case 2179: return "Wintersteiger AG"; case 2180: return "Controlid Industria, Comercio de Hardware e Servicos de Tecnologia Ltda"; case 2181: return "LEVOLOR, INC."; case 2182: return "Xsens Technologies B.V."; case 2183: return "Hydro-Gear Limited Partnership"; case 2184: return "EnPointe Fencing Pty Ltd"; case 2185: return "XANTHIO"; case 2186: return "sclak s.r.l."; case 2187: return "Tricorder Arraay Technologies LLC"; case 2188: return "GB Solution co.,Ltd"; case 2189: return "Soliton Systems K.K."; case 2190: return "GIGA-TMS INC"; case 2191: return "Tait International Limited"; case 2192: return "NICHIEI INTEC CO., LTD."; case 2193: return "SmartWireless GmbH & Co. KG"; case 2194: return "Ingenieurbuero Birnfeld UG (haftungsbeschraenkt)"; case 2195: return "Maytronics Ltd"; case 2196: return "EPIFIT"; case 2197: return "Gimer medical"; case 2198: return "Nokian Renkaat Oyj"; case 2199: return "Current Lighting Solutions LLC"; case 2200: return "Sensibo, Inc."; case 2201: return "SFS unimarket AG"; case 2202: return "Private limited company \"Teltonika\""; case 2203: return "Saucon Technologies"; case 2204: return "Embedded Devices Co. Company"; case 2205: return "J-J.A.D.E. Enterprise LLC"; case 2206: return "i-SENS, inc."; case 2207: return "Witschi Electronic Ltd"; case 2208: return "Aclara Technologies LLC"; case 2209: return "EXEO TECH CORPORATION"; case 2210: return "Epic Systems Co., Ltd."; case 2211: return "Hoffmann SE"; case 2212: return "Realme Chongqing Mobile Telecommunications Corp., Ltd."; case 2213: return "UMEHEAL Ltd"; case 2214: return "Intelligenceworks Inc."; case 2215: return "TGR 1.618 Limited"; case 2216: return "Shanghai Kfcube Inc"; case 2217: return "Fraunhofer IIS"; case 2218: return "SZ DJI TECHNOLOGY CO.,LTD"; case 2219: return "Coburn Technology, LLC"; case 2220: return "Topre Corporation"; case 2221: return "Kayamatics Limited"; case 2222: return "Moticon ReGo AG"; case 2223: return "Polidea Sp. z o.o."; case 2224: return "Trivedi Advanced Technologies LLC"; case 2225: return "CORE|vision BV"; case 2226: return "PF SCHWEISSTECHNOLOGIE GMBH"; case 2227: return "IONIQ Skincare GmbH & Co. KG"; case 2228: return "Sengled Co., Ltd."; case 2229: return "TransferFi"; case 2230: return "Boehringer Ingelheim Vetmedica GmbH"; case 2231: return "ABB Inc"; case 2232: return "Check Technology Solutions LLC"; case 2233: return "U-Shin Ltd."; case 2234: return "HYPER ICE, INC."; case 2235: return "Tokai-rika co.,ltd."; case 2236: return "Prevayl Limited"; case 2237: return "bf1systems limited"; case 2238: return "ubisys technologies GmbH"; case 2239: return "SIRC Co., Ltd."; case 2240: return "Accent Advanced Systems SLU"; case 2241: return "Rayden.Earth LTD"; case 2242: return "Lindinvent AB"; case 2243: return "CHIPOLO d.o.o."; case 2244: return "CellAssist, LLC"; case 2245: return "J. Wagner GmbH"; case 2246: return "Integra Optics Inc"; case 2247: return "Monadnock Systems Ltd."; case 2248: return "Liteboxer Technologies Inc."; case 2249: return "Noventa AG"; case 2250: return "Nubia Technology Co.,Ltd."; case 2251: return "JT INNOVATIONS LIMITED"; case 2252: return "TGM TECHNOLOGY CO., LTD."; case 2253: return "ifly"; case 2254: return "ZIMI CORPORATION"; case 2255: return "betternotstealmybike UG (with limited liability)"; case 2256: return "ESTOM Infotech Kft."; case 2257: return "Sensovium Inc."; case 2258: return "Virscient Limited"; case 2259: return "Novel Bits, LLC"; case 2260: return "ADATA Technology Co., LTD."; case 2261: return "KEYes"; case 2262: return "Nome Oy"; case 2263: return "Inovonics Corp"; case 2264: return "WARES"; case 2265: return "Pointr Labs Limited"; case 2266: return "Miridia Technology Incorporated"; case 2267: return "Tertium Technology"; case 2268: return "SHENZHEN AUKEY E BUSINESS CO., LTD"; case 2269: return "code-Q"; case 2270: return "Tyco Electronics Corporation a TE Connectivity Ltd Company"; case 2271: return "IRIS OHYAMA CO.,LTD."; case 2272: return "Philia Technology"; case 2273: return "KOZO KEIKAKU ENGINEERING Inc."; case 2274: return "Shenzhen Simo Technology co. LTD"; case 2275: return "Republic Wireless, Inc."; case 2276: return "Rashidov ltd"; case 2277: return "Crowd Connected Ltd"; case 2278: return "Eneso Tecnologia de Adaptacion S.L."; case 2279: return "Barrot Technology Limited"; case 2280: return "Naonext"; case 2281: return "Taiwan Intelligent Home Corp."; case 2282: return "COWBELL ENGINEERING CO.,LTD."; case 2283: return "Beijing Big Moment Technology Co., Ltd."; case 2284: return "Denso Corporation"; case 2285: return "IMI Hydronic Engineering International SA"; case 2286: return "ASKEY"; case 2287: return "Cumulus Digital Systems, Inc"; case 2288: return "Joovv, Inc."; case 2289: return "The L.S. Starrett Company"; case 2290: return "Microoled"; case 2291: return "PSP - Pauli Services & Products GmbH"; case 2292: return "Kodimo Technologies Company Limited"; case 2293: return "Tymtix Technologies Private Limited"; case 2294: return "Dermal Photonics Corporation"; case 2295: return "MTD Products Inc & Affiliates"; case 2296: return "instagrid GmbH"; case 2297: return "Spacelabs Medical Inc."; case 2298: return "Troo Corporation"; case 2299: return "Darkglass Electronics Oy"; case 2300: return "Hill-Rom"; case 2301: return "BioIntelliSense, Inc."; case 2302: return "Ketronixs Sdn Bhd"; case 2308: return "SUNCORPORATION"; case 2309: return "Yandex Services AG"; case 2310: return "Scope Logistical Solutions"; case 2311: return "User Hello, LLC"; case 2312: return "Pinpoint Innovations Limited"; case 2313: return "70mai Co.,Ltd."; case 2314: return "Zhuhai Hoksi Technology CO.,LTD"; case 2315: return "EMBR labs, INC"; case 2316: return "Radiawave Technologies Co.,Ltd."; case 2317: return "IOT Invent GmbH"; case 2318: return "OPTIMUSIOT TECH LLP"; case 2319: return "VC Inc."; case 2320: return "ASR Microelectronics (Shanghai) Co., Ltd."; case 2321: return "Douglas Lighting Controls Inc."; case 2322: return "Nerbio Medical Software Platforms Inc"; case 2323: return "Braveheart Wireless, Inc."; case 2324: return "INEO-SENSE"; case 2325: return "Honda Motor Co., Ltd."; case 2326: return "Ambient Sensors LLC"; case 2327: return "ASR Microelectronics(ShenZhen)Co., Ltd."; case 2328: return "Technosphere Labs Pvt. Ltd."; case 2329: return "NO SMD LIMITED"; case 2330: return "Albertronic BV"; case 2331: return "Luminostics, Inc."; case 2332: return "Oblamatik AG"; case 2333: return "Innokind, Inc."; case 2334: return "Melbot Studios, Sociedad Limitada"; case 2335: return "Myzee Technology"; case 2336: return "Omnisense Limited"; case 2337: return "KAHA PTE. LTD."; case 2338: return "Shanghai MXCHIP Information Technology Co., Ltd."; case 2339: return "JSB TECH PTE LTD"; case 2340: return "Fundacion Tecnalia Research and Innovation"; case 2341: return "Yukai Engineering Inc."; case 2342: return "Gooligum Technologies Pty Ltd"; case 2343: return "ROOQ GmbH"; case 2344: return "AiRISTA"; case 2345: return "Qingdao Haier Technology Co., Ltd."; case 2346: return "Sappl Verwaltungs- und Betriebs GmbH"; case 2347: return "TekHome"; case 2348: return "PCI Private Limited"; case 2349: return "Leggett & Platt, Incorporated"; case 2350: return "PS GmbH"; case 2351: return "C.O.B.O. SpA"; case 2352: return "James Walker RotaBolt Limited"; case 2353: return "BREATHINGS Co., Ltd."; case 2354: return "BarVision, LLC"; case 2355: return "SRAM"; case 2356: return "KiteSpring Inc."; case 2357: return "Reconnect, Inc."; case 2358: return "Elekon AG"; case 2359: return "RealThingks GmbH"; case 2360: return "Henway Technologies, LTD."; case 2361: return "ASTEM Co.,Ltd."; case 2362: return "LinkedSemi Microelectronics (Xiamen) Co., Ltd"; case 2363: return "ENSESO LLC"; case 2364: return "Xenoma Inc."; case 2365: return "Adolf Wuerth GmbH & Co KG"; case 2366: return "Catalyft Labs, Inc."; case 2367: return "JEPICO Corporation"; case 2368: return "Hero Workout GmbH"; case 2369: return "Rivian Automotive, LLC"; case 2370: return "TRANSSION HOLDINGS LIMITED"; case 2371: return "Inovonics Corp."; case 2372: return "Agitron d.o.o."; case 2373: return "Globe (Jiangsu) Co., Ltd"; case 2374: return "AMC International Alfa Metalcraft Corporation AG"; case 2375: return "First Light Technologies Ltd."; case 2376: return "Wearable Link Limited"; case 2377: return "Metronom Health Europe"; case 2378: return "Zwift, Inc."; case 2379: return "Kindeva Drug Delivery L.P."; case 2380: return "GimmiSys GmbH"; case 2381: return "tkLABS INC."; case 2382: return "PassiveBolt, Inc."; case 2383: return "Limited Liability Company \"Mikrotikls\""; case 2384: return "Capetech"; case 2385: return "PPRS"; case 2386: return "Apptricity Corporation"; case 2387: return "LogiLube, LLC"; case 2388: return "Julbo"; case 2389: return "Breville Group"; case 2390: return "Kerlink"; case 2391: return "Ohsung Electronics"; case 2392: return "ZTE Corporation"; case 65535: return "internal use"; default: return "not assigned"; } } bluez-5.82/lib/PaxHeaders/bnep.h0000644000000000000000000000005014015011623013502 xustar0020 atime=1743516237 20 ctime=1743591275 bluez-5.82/lib/bnep.h0000644000000000000000000000627214015011623013172 0ustar00rootroot/* SPDX-License-Identifier: GPL-2.0-or-later */ /* * * BlueZ - Bluetooth protocol stack for Linux * * Copyright (C) 2002-2003 Maxim Krasnyansky * Copyright (C) 2002-2010 Marcel Holtmann * * */ #ifndef __BNEP_H #define __BNEP_H #ifdef __cplusplus extern "C" { #endif #include #ifndef ETH_ALEN #define ETH_ALEN 6 /* from */ #endif /* BNEP UUIDs */ #define BNEP_BASE_UUID 0x0000000000001000800000805F9B34FB #define BNEP_UUID16 0x02 #define BNEP_UUID32 0x04 #define BNEP_UUID128 0x16 #define BNEP_SVC_PANU 0x1115 #define BNEP_SVC_NAP 0x1116 #define BNEP_SVC_GN 0x1117 /* BNEP packet types */ #define BNEP_GENERAL 0x00 #define BNEP_CONTROL 0x01 #define BNEP_COMPRESSED 0x02 #define BNEP_COMPRESSED_SRC_ONLY 0x03 #define BNEP_COMPRESSED_DST_ONLY 0x04 /* BNEP control types */ #define BNEP_CMD_NOT_UNDERSTOOD 0x00 #define BNEP_SETUP_CONN_REQ 0x01 #define BNEP_SETUP_CONN_RSP 0x02 #define BNEP_FILTER_NET_TYPE_SET 0x03 #define BNEP_FILTER_NET_TYPE_RSP 0x04 #define BNEP_FILTER_MULT_ADDR_SET 0x05 #define BNEP_FILTER_MULT_ADDR_RSP 0x06 /* BNEP response messages */ #define BNEP_SUCCESS 0x00 #define BNEP_CONN_INVALID_DST 0x01 #define BNEP_CONN_INVALID_SRC 0x02 #define BNEP_CONN_INVALID_SVC 0x03 #define BNEP_CONN_NOT_ALLOWED 0x04 #define BNEP_FILTER_UNSUPPORTED_REQ 0x01 #define BNEP_FILTER_INVALID_RANGE 0x02 #define BNEP_FILTER_INVALID_MCADDR 0x02 #define BNEP_FILTER_LIMIT_REACHED 0x03 #define BNEP_FILTER_DENIED_SECURITY 0x04 /* L2CAP settings */ #define BNEP_MTU 1691 #define BNEP_FLUSH_TO 0xffff #define BNEP_CONNECT_TO 15 #define BNEP_FILTER_TO 15 #ifndef BNEP_PSM #define BNEP_PSM 0x0f #endif /* BNEP headers */ #define BNEP_TYPE_MASK 0x7f #define BNEP_EXT_HEADER 0x80 struct bnep_setup_conn_req { uint8_t type; uint8_t ctrl; uint8_t uuid_size; uint8_t service[0]; } __attribute__((packed)); struct bnep_set_filter_req { uint8_t type; uint8_t ctrl; uint16_t len; uint8_t list[0]; } __attribute__((packed)); struct bnep_ctrl_cmd_not_understood_cmd { uint8_t type; uint8_t ctrl; uint8_t unkn_ctrl; } __attribute__((packed)); struct bnep_control_rsp { uint8_t type; uint8_t ctrl; uint16_t resp; } __attribute__((packed)); struct bnep_ext_hdr { uint8_t type; uint8_t len; uint8_t data[0]; } __attribute__((packed)); /* BNEP ioctl defines */ #define BNEPCONNADD _IOW('B', 200, int) #define BNEPCONNDEL _IOW('B', 201, int) #define BNEPGETCONNLIST _IOR('B', 210, int) #define BNEPGETCONNINFO _IOR('B', 211, int) #define BNEPGETSUPPFEAT _IOR('B', 212, int) #define BNEP_SETUP_RESPONSE 0 struct bnep_connadd_req { int sock; /* Connected socket */ uint32_t flags; uint16_t role; char device[16]; /* Name of the Ethernet device */ }; struct bnep_conndel_req { uint32_t flags; uint8_t dst[ETH_ALEN]; }; struct bnep_conninfo { uint32_t flags; uint16_t role; uint16_t state; uint8_t dst[ETH_ALEN]; char device[16]; }; struct bnep_connlist_req { uint32_t cnum; struct bnep_conninfo *ci; }; #ifdef __cplusplus } #endif #endif /* __BNEP_H */ bluez-5.82/lib/PaxHeaders/hci.c0000644000000000000000000000005014766002272013331 xustar0020 atime=1743515578 20 ctime=1743591277 bluez-5.82/lib/hci.c0000644000000000000000000017731214766002272013025 0ustar00rootroot// SPDX-License-Identifier: GPL-2.0-or-later /* * * BlueZ - Bluetooth protocol stack for Linux * * Copyright (C) 2000-2001 Qualcomm Incorporated * Copyright (C) 2002-2003 Maxim Krasnyansky * Copyright (C) 2002-2010 Marcel Holtmann * * */ #ifdef HAVE_CONFIG_H #include #endif #define _GNU_SOURCE #include #include #include #include #include #include #include #include #include #include #include #include #include "bluetooth.h" #include "hci.h" #include "hci_lib.h" #ifndef MIN #define MIN(x, y) ((x) < (y) ? (x) : (y)) #endif typedef struct { char *str; unsigned int val; } hci_map; static char *hci_bit2str(const hci_map *m, unsigned int val) { char *str = malloc(120); char *ptr = str; if (!str) return NULL; *ptr = 0; while (m->str) { if ((unsigned int) m->val & val) ptr += sprintf(ptr, "%s ", m->str); m++; } return str; } static int hci_str2bit(const hci_map *map, char *str, unsigned int *val) { char *t, *ptr; const hci_map *m; int set; if (!str || !(str = ptr = strdup(str))) return 0; *val = set = 0; while ((t = strsep(&ptr, ","))) { for (m = map; m->str; m++) { if (!strcasecmp(m->str, t)) { *val |= (unsigned int) m->val; set = 1; } } } free(str); return set; } static char *hci_uint2str(const hci_map *m, unsigned int val) { char *str = malloc(50); char *ptr = str; if (!str) return NULL; *ptr = 0; while (m->str) { if ((unsigned int) m->val == val) { ptr += sprintf(ptr, "%s", m->str); break; } m++; } return str; } static int hci_str2uint(const hci_map *map, char *str, unsigned int *val) { char *t, *ptr; const hci_map *m; int set = 0; if (!str) return 0; str = ptr = strdup(str); while ((t = strsep(&ptr, ","))) { for (m = map; m->str; m++) { if (!strcasecmp(m->str,t)) { *val = (unsigned int) m->val; set = 1; break; } } } free(str); return set; } const char *hci_bustostr(int bus) { switch (bus) { case HCI_VIRTUAL: return "Virtual"; case HCI_USB: return "USB"; case HCI_PCCARD: return "PCCARD"; case HCI_UART: return "UART"; case HCI_RS232: return "RS232"; case HCI_PCI: return "PCI"; case HCI_SDIO: return "SDIO"; case HCI_SPI: return "SPI"; case HCI_I2C: return "I2C"; case HCI_SMD: return "SMD"; case HCI_VIRTIO: return "VIRTIO"; case HCI_IPC: return "IPC"; default: return "Unknown"; } } const char *hci_dtypetostr(int type) { return hci_bustostr(type & 0x0f); } char *hci_typetostr(int type) { switch (type) { case HCI_PRIMARY: return "Primary"; case HCI_AMP: return "AMP"; default: return "Unknown"; } } /* HCI dev flags mapping */ static const hci_map dev_flags_map[] = { { "UP", HCI_UP }, { "INIT", HCI_INIT }, { "RUNNING", HCI_RUNNING }, { "RAW", HCI_RAW }, { "PSCAN", HCI_PSCAN }, { "ISCAN", HCI_ISCAN }, { "INQUIRY", HCI_INQUIRY }, { "AUTH", HCI_AUTH }, { "ENCRYPT", HCI_ENCRYPT }, { NULL } }; char *hci_dflagstostr(uint32_t flags) { char *str = bt_malloc(50); char *ptr = str; const hci_map *m = dev_flags_map; if (!str) return NULL; *ptr = 0; if (!hci_test_bit(HCI_UP, &flags)) ptr += sprintf(ptr, "DOWN "); while (m->str) { if (hci_test_bit(m->val, &flags)) ptr += sprintf(ptr, "%s ", m->str); m++; } return str; } /* HCI packet type mapping */ static const hci_map pkt_type_map[] = { { "DM1", HCI_DM1 }, { "DM3", HCI_DM3 }, { "DM5", HCI_DM5 }, { "DH1", HCI_DH1 }, { "DH3", HCI_DH3 }, { "DH5", HCI_DH5 }, { "HV1", HCI_HV1 }, { "HV2", HCI_HV2 }, { "HV3", HCI_HV3 }, { "2-DH1", HCI_2DH1 }, { "2-DH3", HCI_2DH3 }, { "2-DH5", HCI_2DH5 }, { "3-DH1", HCI_3DH1 }, { "3-DH3", HCI_3DH3 }, { "3-DH5", HCI_3DH5 }, { NULL } }; static const hci_map sco_ptype_map[] = { { "HV1", 0x0001 }, { "HV2", 0x0002 }, { "HV3", 0x0004 }, { "EV3", HCI_EV3 }, { "EV4", HCI_EV4 }, { "EV5", HCI_EV5 }, { "2-EV3", HCI_2EV3 }, { "2-EV5", HCI_2EV5 }, { "3-EV3", HCI_3EV3 }, { "3-EV5", HCI_3EV5 }, { NULL } }; char *hci_ptypetostr(unsigned int ptype) { return hci_bit2str(pkt_type_map, ptype); } int hci_strtoptype(char *str, unsigned int *val) { return hci_str2bit(pkt_type_map, str, val); } char *hci_scoptypetostr(unsigned int ptype) { return hci_bit2str(sco_ptype_map, ptype); } int hci_strtoscoptype(char *str, unsigned int *val) { return hci_str2bit(sco_ptype_map, str, val); } /* Link policy mapping */ static const hci_map link_policy_map[] = { { "NONE", 0 }, { "RSWITCH", HCI_LP_RSWITCH }, { "HOLD", HCI_LP_HOLD }, { "SNIFF", HCI_LP_SNIFF }, { "PARK", HCI_LP_PARK }, { NULL } }; char *hci_lptostr(unsigned int lp) { return hci_bit2str(link_policy_map, lp); } int hci_strtolp(char *str, unsigned int *val) { return hci_str2bit(link_policy_map, str, val); } /* Link mode mapping */ static const hci_map link_mode_map[] = { { "NONE", 0 }, { "ACCEPT", HCI_LM_ACCEPT }, { "CENTRAL", HCI_LM_MASTER }, { "AUTH", HCI_LM_AUTH }, { "ENCRYPT", HCI_LM_ENCRYPT }, { "TRUSTED", HCI_LM_TRUSTED }, { "RELIABLE", HCI_LM_RELIABLE }, { "SECURE", HCI_LM_SECURE }, { NULL } }; char *hci_lmtostr(unsigned int lm) { char *s, *str = bt_malloc(50); if (!str) return NULL; *str = 0; if (!(lm & HCI_LM_MASTER)) strcpy(str, "PERIPHERAL "); s = hci_bit2str(link_mode_map, lm); if (!s) { bt_free(str); return NULL; } strcat(str, s); free(s); return str; } int hci_strtolm(char *str, unsigned int *val) { int ret = hci_str2bit(link_mode_map, str, val); /* Deprecated name. Kept for compatibility. */ if (!!str && strcasestr(str, "MASTER")) { ret = 1; *val |= HCI_LM_MASTER; } return ret; } /* Command mapping */ static const hci_map commands_map[] = { { "Inquiry", 0 }, { "Inquiry Cancel", 1 }, { "Periodic Inquiry Mode", 2 }, { "Exit Periodic Inquiry Mode", 3 }, { "Create Connection", 4 }, { "Disconnect", 5 }, { "Add SCO Connection", 6 }, { "Cancel Create Connection", 7 }, { "Accept Connection Request", 8 }, { "Reject Connection Request", 9 }, { "Link Key Request Reply", 10 }, { "Link Key Request Negative Reply", 11 }, { "PIN Code Request Reply", 12 }, { "PIN Code Request Negative Reply", 13 }, { "Change Connection Packet Type", 14 }, { "Authentication Requested", 15 }, { "Set Connection Encryption", 16 }, { "Change Connection Link Key", 17 }, { "Temporary Link Key", 18 }, { "Remote Name Request", 19 }, { "Cancel Remote Name Request", 20 }, { "Read Remote Supported Features", 21 }, { "Read Remote Extended Features", 22 }, { "Read Remote Version Information", 23 }, { "Read Clock Offset", 24 }, { "Read LMP Handle", 25 }, { "Reserved", 26 }, { "Reserved", 27 }, { "Reserved", 28 }, { "Reserved", 29 }, { "Reserved", 30 }, { "Reserved", 31 }, { "Reserved", 32 }, { "Hold Mode", 33 }, { "Sniff Mode", 34 }, { "Exit Sniff Mode", 35 }, { "Park State", 36 }, { "Exit Park State", 37 }, { "QoS Setup", 38 }, { "Role Discovery", 39 }, { "Switch Role", 40 }, { "Read Link Policy Settings", 41 }, { "Write Link Policy Settings", 42 }, { "Read Default Link Policy Settings", 43 }, { "Write Default Link Policy Settings", 44 }, { "Flow Specification", 45 }, { "Set Event Mask", 46 }, { "Reset", 47 }, { "Set Event Filter", 48 }, { "Flush", 49 }, { "Read PIN Type", 50 }, { "Write PIN Type", 51 }, { "Create New Unit Key", 52 }, { "Read Stored Link Key", 53 }, { "Write Stored Link Key", 54 }, { "Delete Stored Link Key", 55 }, { "Write Local Name", 56 }, { "Read Local Name", 57 }, { "Read Connection Accept Timeout", 58 }, { "Write Connection Accept Timeout", 59 }, { "Read Page Timeout", 60 }, { "Write Page Timeout", 61 }, { "Read Scan Enable", 62 }, { "Write Scan Enable", 63 }, { "Read Page Scan Activity", 64 }, { "Write Page Scan Activity", 65 }, { "Read Inquiry Scan Activity", 66 }, { "Write Inquiry Scan Activity", 67 }, { "Read Authentication Enable", 68 }, { "Write Authentication Enable", 69 }, { "Read Encryption Mode", 70 }, { "Write Encryption Mode", 71 }, { "Read Class Of Device", 72 }, { "Write Class Of Device", 73 }, { "Read Voice Setting", 74 }, { "Write Voice Setting", 75 }, { "Read Automatic Flush Timeout", 76 }, { "Write Automatic Flush Timeout", 77 }, { "Read Num Broadcast Retransmissions", 78 }, { "Write Num Broadcast Retransmissions", 79 }, { "Read Hold Mode Activity", 80 }, { "Write Hold Mode Activity", 81 }, { "Read Transmit Power Level", 82 }, { "Read Synchronous Flow Control Enable", 83 }, { "Write Synchronous Flow Control Enable", 84 }, { "Set Host Controller To Host Flow Control", 85 }, { "Host Buffer Size", 86 }, { "Host Number Of Completed Packets", 87 }, { "Read Link Supervision Timeout", 88 }, { "Write Link Supervision Timeout", 89 }, { "Read Number of Supported IAC", 90 }, { "Read Current IAC LAP", 91 }, { "Write Current IAC LAP", 92 }, { "Read Page Scan Period Mode", 93 }, { "Write Page Scan Period Mode", 94 }, { "Read Page Scan Mode", 95 }, { "Write Page Scan Mode", 96 }, { "Set AFH Channel Classification", 97 }, { "Reserved", 98 }, { "Reserved", 99 }, { "Read Inquiry Scan Type", 100 }, { "Write Inquiry Scan Type", 101 }, { "Read Inquiry Mode", 102 }, { "Write Inquiry Mode", 103 }, { "Read Page Scan Type", 104 }, { "Write Page Scan Type", 105 }, { "Read AFH Channel Assessment Mode", 106 }, { "Write AFH Channel Assessment Mode", 107 }, { "Reserved", 108 }, { "Reserved", 109 }, { "Reserved", 110 }, { "Reserved", 111 }, { "Reserved", 112 }, { "Reserved", 113 }, { "Reserved", 114 }, { "Read Local Version Information", 115 }, { "Read Local Supported Commands", 116 }, { "Read Local Supported Features", 117 }, { "Read Local Extended Features", 118 }, { "Read Buffer Size", 119 }, { "Read Country Code", 120 }, { "Read BD ADDR", 121 }, { "Read Failed Contact Counter", 122 }, { "Reset Failed Contact Counter", 123 }, { "Get Link Quality", 124 }, { "Read RSSI", 125 }, { "Read AFH Channel Map", 126 }, { "Read BD Clock", 127 }, { "Read Loopback Mode", 128 }, { "Write Loopback Mode", 129 }, { "Enable Device Under Test Mode", 130 }, { "Setup Synchronous Connection", 131 }, { "Accept Synchronous Connection", 132 }, { "Reject Synchronous Connection", 133 }, { "Reserved", 134 }, { "Reserved", 135 }, { "Read Extended Inquiry Response", 136 }, { "Write Extended Inquiry Response", 137 }, { "Refresh Encryption Key", 138 }, { "Reserved", 139 }, { "Sniff Subrating", 140 }, { "Read Simple Pairing Mode", 141 }, { "Write Simple Pairing Mode", 142 }, { "Read Local OOB Data", 143 }, { "Read Inquiry Response Transmit Power Level", 144 }, { "Write Inquiry Transmit Power Level", 145 }, { "Read Default Erroneous Data Reporting", 146 }, { "Write Default Erroneous Data Reporting", 147 }, { "Reserved", 148 }, { "Reserved", 149 }, { "Reserved", 150 }, { "IO Capability Request Reply", 151 }, { "User Confirmation Request Reply", 152 }, { "User Confirmation Request Negative Reply", 153 }, { "User Passkey Request Reply", 154 }, { "User Passkey Request Negative Reply", 155 }, { "Remote OOB Data Request Reply", 156 }, { "Write Simple Pairing Debug Mode", 157 }, { "Enhanced Flush", 158 }, { "Remote OOB Data Request Negative Reply", 159 }, { "Reserved", 160 }, { "Reserved", 161 }, { "Send Keypress Notification", 162 }, { "IO Capability Request Negative Reply", 163 }, { "Read Encryption Key Size", 164 }, { "Reserved", 165 }, { "Reserved", 166 }, { "Reserved", 167 }, { "Create Physical Link", 168 }, { "Accept Physical Link", 169 }, { "Disconnect Physical Link", 170 }, { "Create Logical Link", 171 }, { "Accept Logical Link", 172 }, { "Disconnect Logical Link", 173 }, { "Logical Link Cancel", 174 }, { "Flow Specification Modify", 175 }, { "Read Logical Link Accept Timeout", 176 }, { "Write Logical Link Accept Timeout", 177 }, { "Set Event Mask Page 2", 178 }, { "Read Location Data", 179 }, { "Write Location Data", 180 }, { "Read Local AMP Info", 181 }, { "Read Local AMP_ASSOC", 182 }, { "Write Remote AMP_ASSOC", 183 }, { "Read Flow Control Mode", 184 }, { "Write Flow Control Mode", 185 }, { "Read Data Block Size", 186 }, { "Reserved", 187 }, { "Reserved", 188 }, { "Enable AMP Receiver Reports", 189 }, { "AMP Test End", 190 }, { "AMP Test Command", 191 }, { "Read Enhanced Transmit Power Level", 192 }, { "Reserved", 193 }, { "Read Best Effort Flush Timeout", 194 }, { "Write Best Effort Flush Timeout", 195 }, { "Short Range Mode", 196 }, { "Read LE Host Support", 197 }, { "Write LE Host Support", 198 }, { "Reserved", 199 }, { "LE Set Event Mask", 200 }, { "LE Read Buffer Size", 201 }, { "LE Read Local Supported Features", 202 }, { "Reserved", 203 }, { "LE Set Random Address", 204 }, { "LE Set Advertising Parameters", 205 }, { "LE Read Advertising Channel TX Power", 206 }, { "LE Set Advertising Data", 207 }, { "LE Set Scan Response Data", 208 }, { "LE Set Advertise Enable", 209 }, { "LE Set Scan Parameters", 210 }, { "LE Set Scan Enable", 211 }, { "LE Create Connection", 212 }, { "LE Create Connection Cancel", 213 }, { "LE Read Accept List Size", 214 }, { "LE Clear Accept List", 215 }, { "LE Add Device To Accept List", 216 }, { "LE Remove Device From Accept List", 217 }, { "LE Connection Update", 218 }, { "LE Set Host Channel Classification", 219 }, { "LE Read Channel Map", 220 }, { "LE Read Remote Used Features", 221 }, { "LE Encrypt", 222 }, { "LE Rand", 223 }, { "LE Start Encryption", 224 }, { "LE Long Term Key Request Reply", 225 }, { "LE Long Term Key Request Negative Reply", 226 }, { "LE Read Supported States", 227 }, { "LE Receiver Test", 228 }, { "LE Transmitter Test", 229 }, { "LE Test End", 230 }, { "Reserved", 231 }, { NULL } }; char *hci_cmdtostr(unsigned int cmd) { return hci_uint2str(commands_map, cmd); } char *hci_commandstostr(uint8_t *commands, char *pref, int width) { unsigned int maxwidth = width - 3; const hci_map *m; char *off, *ptr, *str; int size = 10; m = commands_map; while (m->str) { if (commands[m->val / 8] & (1 << (m->val % 8))) size += strlen(m->str) + (pref ? strlen(pref) : 0) + 3; m++; } str = bt_malloc(size); if (!str) return NULL; ptr = str; *ptr = '\0'; if (pref) ptr += sprintf(ptr, "%s", pref); off = ptr; m = commands_map; while (m->str) { if (commands[m->val / 8] & (1 << (m->val % 8))) { if (strlen(off) + strlen(m->str) > maxwidth) { ptr += sprintf(ptr, "\n%s", pref ? pref : ""); off = ptr; } ptr += sprintf(ptr, "'%s' ", m->str); } m++; } return str; } /* Version mapping */ static const hci_map ver_map[] = { { "1.0b", 0x00 }, { "1.1", 0x01 }, { "1.2", 0x02 }, { "2.0", 0x03 }, { "2.1", 0x04 }, { "3.0", 0x05 }, { "4.0", 0x06 }, { "4.1", 0x07 }, { "4.2", 0x08 }, { "5.0", 0x09 }, { "5.1", 0x0a }, { "5.2", 0x0b }, { "5.3", 0x0c }, { "5.4", 0x0d }, { NULL } }; char *hci_vertostr(unsigned int ver) { return hci_uint2str(ver_map, ver); } int hci_strtover(char *str, unsigned int *ver) { return hci_str2uint(ver_map, str, ver); } char *lmp_vertostr(unsigned int ver) { return hci_uint2str(ver_map, ver); } int lmp_strtover(char *str, unsigned int *ver) { return hci_str2uint(ver_map, str, ver); } static const hci_map pal_map[] = { { "3.0", 0x01 }, { NULL } }; char *pal_vertostr(unsigned int ver) { return hci_uint2str(pal_map, ver); } int pal_strtover(char *str, unsigned int *ver) { return hci_str2uint(pal_map, str, ver); } /* LMP features mapping */ static const hci_map lmp_features_map[8][9] = { { /* Byte 0 */ { "<3-slot packets>", LMP_3SLOT }, /* Bit 0 */ { "<5-slot packets>", LMP_5SLOT }, /* Bit 1 */ { "", LMP_ENCRYPT }, /* Bit 2 */ { "", LMP_SOFFSET }, /* Bit 3 */ { "", LMP_TACCURACY }, /* Bit 4 */ { "", LMP_RSWITCH }, /* Bit 5 */ { "", LMP_HOLD }, /* Bit 6 */ { "", LMP_SNIFF }, /* Bit 7 */ { NULL } }, { /* Byte 1 */ { "", LMP_PARK }, /* Bit 0 */ { "", LMP_RSSI }, /* Bit 1 */ { "", LMP_QUALITY }, /* Bit 2 */ { "", LMP_SCO }, /* Bit 3 */ { "", LMP_HV2 }, /* Bit 4 */ { "", LMP_HV3 }, /* Bit 5 */ { "", LMP_ULAW }, /* Bit 6 */ { "", LMP_ALAW }, /* Bit 7 */ { NULL } }, { /* Byte 2 */ { "", LMP_CVSD }, /* Bit 0 */ { "", LMP_PSCHEME }, /* Bit 1 */ { "", LMP_PCONTROL }, /* Bit 2 */ { "", LMP_TRSP_SCO }, /* Bit 3 */ { "",LMP_BCAST_ENC }, /* Bit 7 */ { NULL } }, { /* Byte 3 */ { "", 0x01 }, /* Bit 0 */ { "", LMP_EDR_ACL_2M }, /* Bit 1 */ { "", LMP_EDR_ACL_3M }, /* Bit 2 */ { "", LMP_ENH_ISCAN }, /* Bit 3 */ { "", LMP_ILACE_ISCAN }, /* Bit 4 */ { "", LMP_ILACE_PSCAN }, /* Bit 5 */ { "",LMP_RSSI_INQ }, /* Bit 6 */ { "", LMP_ESCO }, /* Bit 7 */ { NULL } }, { /* Byte 4 */ { "", LMP_EV4 }, /* Bit 0 */ { "", LMP_EV5 }, /* Bit 1 */ { "", 0x04 }, /* Bit 2 */ { "", LMP_AFH_CAP_SLV }, /* Bit 3 */ { "", LMP_AFH_CLS_SLV }, /* Bit 4 */ { "
", LMP_NO_BREDR }, /* Bit 5 */ { "", LMP_LE }, /* Bit 6 */ { "<3-slot EDR ACL>", LMP_EDR_3SLOT }, /* Bit 7 */ { NULL } }, { /* Byte 5 */ { "<5-slot EDR ACL>", LMP_EDR_5SLOT }, /* Bit 0 */ { "", LMP_SNIFF_SUBR }, /* Bit 1 */ { "", LMP_PAUSE_ENC }, /* Bit 2 */ { "", LMP_AFH_CAP_MST }, /* Bit 3 */ { "", LMP_AFH_CLS_MST }, /* Bit 4 */ { "", LMP_EDR_ESCO_2M }, /* Bit 5 */ { "", LMP_EDR_ESCO_3M }, /* Bit 6 */ { "<3-slot EDR eSCO>", LMP_EDR_3S_ESCO }, /* Bit 7 */ { NULL } }, { /* Byte 6 */ { "", LMP_EXT_INQ }, /* Bit 0 */ { "", LMP_LE_BREDR }, /* Bit 1 */ { "", 0x04 }, /* Bit 2 */ { "", LMP_SIMPLE_PAIR }, /* Bit 3 */ { "", LMP_ENCAPS_PDU }, /* Bit 4 */ { "", LMP_ERR_DAT_REP }, /* Bit 5 */ { "", LMP_NFLUSH_PKTS }, /* Bit 6 */ { "", 0x80 }, /* Bit 7 */ { NULL } }, { /* Byte 7 */ { "", LMP_LSTO }, /* Bit 1 */ { "", LMP_INQ_TX_PWR }, /* Bit 1 */ { "", LMP_EPC }, /* Bit 2 */ { "", 0x08 }, /* Bit 3 */ { "", 0x10 }, /* Bit 4 */ { "", 0x20 }, /* Bit 5 */ { "", 0x40 }, /* Bit 6 */ { "",LMP_EXT_FEAT }, /* Bit 7 */ { NULL } }, }; char *lmp_featurestostr(uint8_t *features, char *pref, int width) { unsigned int maxwidth = width - 1; char *off, *ptr, *str; int i, size = 10; for (i = 0; i < 8; i++) { const hci_map *m = lmp_features_map[i]; while (m->str) { if (m->val & features[i]) size += strlen(m->str) + (pref ? strlen(pref) : 0) + 1; m++; } } str = bt_malloc(size); if (!str) return NULL; ptr = str; *ptr = '\0'; if (pref) ptr += sprintf(ptr, "%s", pref); off = ptr; for (i = 0; i < 8; i++) { const hci_map *m = lmp_features_map[i]; while (m->str) { if (m->val & features[i]) { if (strlen(off) + strlen(m->str) > maxwidth) { ptr += sprintf(ptr, "\n%s", pref ? pref : ""); off = ptr; } ptr += sprintf(ptr, "%s ", m->str); } m++; } } return str; } /* HCI functions that do not require open device */ int hci_for_each_dev(int flag, int (*func)(int dd, int dev_id, long arg), long arg) { struct hci_dev_list_req *dl; struct hci_dev_req *dr; int dev_id = -1; int i, sk, err = 0; sk = socket(AF_BLUETOOTH, SOCK_RAW | SOCK_CLOEXEC, BTPROTO_HCI); if (sk < 0) return -1; dl = malloc(HCI_MAX_DEV * sizeof(*dr) + sizeof(*dl)); if (!dl) { err = errno; goto done; } memset(dl, 0, HCI_MAX_DEV * sizeof(*dr) + sizeof(*dl)); dl->dev_num = HCI_MAX_DEV; dr = dl->dev_req; if (ioctl(sk, HCIGETDEVLIST, (void *) dl) < 0) { err = errno; goto free; } for (i = 0; i < dl->dev_num; i++, dr++) { if (hci_test_bit(flag, &dr->dev_opt)) if (!func || func(sk, dr->dev_id, arg)) { dev_id = dr->dev_id; break; } } if (dev_id < 0) err = ENODEV; free: free(dl); done: close(sk); errno = err; return dev_id; } static int __other_bdaddr(int dd, int dev_id, long arg) { struct hci_dev_info di = { .dev_id = dev_id }; if (ioctl(dd, HCIGETDEVINFO, (void *) &di)) return 0; if (hci_test_bit(HCI_RAW, &di.flags)) return 0; return bacmp((bdaddr_t *) arg, &di.bdaddr); } static int __same_bdaddr(int dd, int dev_id, long arg) { struct hci_dev_info di = { .dev_id = dev_id }; if (ioctl(dd, HCIGETDEVINFO, (void *) &di)) return 0; return !bacmp((bdaddr_t *) arg, &di.bdaddr); } int hci_get_route(bdaddr_t *bdaddr) { int dev_id; dev_id = hci_for_each_dev(HCI_UP, __other_bdaddr, (long) (bdaddr ? bdaddr : BDADDR_ANY)); if (dev_id < 0) dev_id = hci_for_each_dev(HCI_UP, __same_bdaddr, (long) (bdaddr ? bdaddr : BDADDR_ANY)); return dev_id; } int hci_devid(const char *str) { bdaddr_t ba; int id = -1; if (!strncmp(str, "hci", 3) && strlen(str) >= 4) { id = atoi(str + 3); if (hci_devba(id, &ba) < 0) return -1; } else { errno = ENODEV; str2ba(str, &ba); id = hci_for_each_dev(HCI_UP, __same_bdaddr, (long) &ba); } return id; } int hci_devinfo(int dev_id, struct hci_dev_info *di) { int dd, err, ret; dd = socket(AF_BLUETOOTH, SOCK_RAW | SOCK_CLOEXEC, BTPROTO_HCI); if (dd < 0) return dd; memset(di, 0, sizeof(struct hci_dev_info)); di->dev_id = dev_id; ret = ioctl(dd, HCIGETDEVINFO, (void *) di); err = errno; close(dd); errno = err; return ret; } int hci_devba(int dev_id, bdaddr_t *bdaddr) { struct hci_dev_info di; memset(&di, 0, sizeof(di)); if (hci_devinfo(dev_id, &di)) return -1; if (!hci_test_bit(HCI_UP, &di.flags)) { errno = ENETDOWN; return -1; } bacpy(bdaddr, &di.bdaddr); return 0; } int hci_inquiry(int dev_id, int len, int nrsp, const uint8_t *lap, inquiry_info **ii, long flags) { struct hci_inquiry_req *ir; uint8_t num_rsp = nrsp; void *buf; int dd, size, err, ret = -1; if (nrsp <= 0) { num_rsp = 0; nrsp = 255; } if (dev_id < 0) { dev_id = hci_get_route(NULL); if (dev_id < 0) { errno = ENODEV; return -1; } } dd = socket(AF_BLUETOOTH, SOCK_RAW | SOCK_CLOEXEC, BTPROTO_HCI); if (dd < 0) return dd; buf = malloc(sizeof(*ir) + (sizeof(inquiry_info) * (nrsp))); if (!buf) goto done; ir = buf; ir->dev_id = dev_id; ir->num_rsp = num_rsp; ir->length = len; ir->flags = flags; if (lap) { memcpy(ir->lap, lap, 3); } else { ir->lap[0] = 0x33; ir->lap[1] = 0x8b; ir->lap[2] = 0x9e; } ret = ioctl(dd, HCIINQUIRY, (unsigned long) buf); if (ret < 0) goto free; size = sizeof(inquiry_info) * ir->num_rsp; if (!*ii) *ii = malloc(size); if (*ii) { memcpy((void *) *ii, buf + sizeof(*ir), size); ret = ir->num_rsp; } else ret = -1; free: free(buf); done: err = errno; close(dd); errno = err; return ret; } /* Open HCI device. * Returns device descriptor (dd). */ int hci_open_dev(int dev_id) { struct sockaddr_hci a; int dd, err; /* Check for valid device id */ if (dev_id < 0) { errno = ENODEV; return -1; } /* Create HCI socket */ dd = socket(AF_BLUETOOTH, SOCK_RAW | SOCK_CLOEXEC, BTPROTO_HCI); if (dd < 0) return dd; /* Bind socket to the HCI device */ memset(&a, 0, sizeof(a)); a.hci_family = AF_BLUETOOTH; a.hci_dev = dev_id; if (bind(dd, (struct sockaddr *) &a, sizeof(a)) < 0) goto failed; return dd; failed: err = errno; close(dd); errno = err; return -1; } int hci_close_dev(int dd) { return close(dd); } /* HCI functions that require open device * dd - Device descriptor returned by hci_open_dev. */ int hci_send_cmd(int dd, uint16_t ogf, uint16_t ocf, uint8_t plen, void *param) { uint8_t type = HCI_COMMAND_PKT; hci_command_hdr hc; struct iovec iv[3]; int ivn; hc.opcode = htobs(cmd_opcode_pack(ogf, ocf)); hc.plen= plen; iv[0].iov_base = &type; iv[0].iov_len = 1; iv[1].iov_base = &hc; iv[1].iov_len = HCI_COMMAND_HDR_SIZE; ivn = 2; if (plen) { iv[2].iov_base = param; iv[2].iov_len = plen; ivn = 3; } while (writev(dd, iv, ivn) < 0) { if (errno == EAGAIN || errno == EINTR) continue; return -1; } return 0; } int hci_send_req(int dd, struct hci_request *r, int to) { unsigned char buf[HCI_MAX_EVENT_SIZE], *ptr; uint16_t opcode = htobs(cmd_opcode_pack(r->ogf, r->ocf)); struct hci_filter nf, of; socklen_t olen; hci_event_hdr *hdr; int err, try; olen = sizeof(of); if (getsockopt(dd, SOL_HCI, HCI_FILTER, &of, &olen) < 0) return -1; hci_filter_clear(&nf); hci_filter_set_ptype(HCI_EVENT_PKT, &nf); hci_filter_set_event(EVT_CMD_STATUS, &nf); hci_filter_set_event(EVT_CMD_COMPLETE, &nf); hci_filter_set_event(EVT_LE_META_EVENT, &nf); hci_filter_set_event(r->event, &nf); hci_filter_set_opcode(opcode, &nf); if (setsockopt(dd, SOL_HCI, HCI_FILTER, &nf, sizeof(nf)) < 0) return -1; if (hci_send_cmd(dd, r->ogf, r->ocf, r->clen, r->cparam) < 0) goto failed; try = 10; while (try--) { evt_cmd_complete *cc; evt_cmd_status *cs; evt_remote_name_req_complete *rn; evt_le_meta_event *me; remote_name_req_cp *cp; int len; if (to) { struct pollfd p; int n; p.fd = dd; p.events = POLLIN; while ((n = poll(&p, 1, to)) < 0) { if (errno == EAGAIN || errno == EINTR) continue; goto failed; } if (!n) { errno = ETIMEDOUT; goto failed; } to -= 10; if (to < 0) to = 0; } while ((len = read(dd, buf, sizeof(buf))) < 0) { if (errno == EAGAIN || errno == EINTR) continue; goto failed; } hdr = (void *) (buf + 1); ptr = buf + (1 + HCI_EVENT_HDR_SIZE); len -= (1 + HCI_EVENT_HDR_SIZE); switch (hdr->evt) { case EVT_CMD_STATUS: cs = (void *) ptr; if (cs->opcode != opcode) continue; if (r->event != EVT_CMD_STATUS) { if (cs->status) { errno = EIO; goto failed; } break; } r->rlen = MIN(len, r->rlen); memcpy(r->rparam, ptr, r->rlen); goto done; case EVT_CMD_COMPLETE: cc = (void *) ptr; if (cc->opcode != opcode) continue; ptr += EVT_CMD_COMPLETE_SIZE; len -= EVT_CMD_COMPLETE_SIZE; r->rlen = MIN(len, r->rlen); memcpy(r->rparam, ptr, r->rlen); goto done; case EVT_REMOTE_NAME_REQ_COMPLETE: if (hdr->evt != r->event) break; rn = (void *) ptr; cp = r->cparam; if (bacmp(&rn->bdaddr, &cp->bdaddr)) continue; r->rlen = MIN(len, r->rlen); memcpy(r->rparam, ptr, r->rlen); goto done; case EVT_LE_META_EVENT: me = (void *) ptr; if (me->subevent != r->event) continue; len -= 1; r->rlen = MIN(len, r->rlen); memcpy(r->rparam, me->data, r->rlen); goto done; default: if (hdr->evt != r->event) break; r->rlen = MIN(len, r->rlen); memcpy(r->rparam, ptr, r->rlen); goto done; } } errno = ETIMEDOUT; failed: err = errno; if (setsockopt(dd, SOL_HCI, HCI_FILTER, &of, sizeof(of)) < 0) err = errno; errno = err; return -1; done: if (setsockopt(dd, SOL_HCI, HCI_FILTER, &of, sizeof(of)) < 0) return -1; return 0; } int hci_create_connection(int dd, const bdaddr_t *bdaddr, uint16_t ptype, uint16_t clkoffset, uint8_t rswitch, uint16_t *handle, int to) { evt_conn_complete rp; create_conn_cp cp; struct hci_request rq; memset(&cp, 0, sizeof(cp)); bacpy(&cp.bdaddr, bdaddr); cp.pkt_type = ptype; cp.pscan_rep_mode = 0x02; cp.clock_offset = clkoffset; cp.role_switch = rswitch; memset(&rq, 0, sizeof(rq)); rq.ogf = OGF_LINK_CTL; rq.ocf = OCF_CREATE_CONN; rq.event = EVT_CONN_COMPLETE; rq.cparam = &cp; rq.clen = CREATE_CONN_CP_SIZE; rq.rparam = &rp; rq.rlen = EVT_CONN_COMPLETE_SIZE; if (hci_send_req(dd, &rq, to) < 0) return -1; if (rp.status) { errno = EIO; return -1; } *handle = rp.handle; return 0; } int hci_disconnect(int dd, uint16_t handle, uint8_t reason, int to) { evt_disconn_complete rp; disconnect_cp cp; struct hci_request rq; memset(&cp, 0, sizeof(cp)); cp.handle = handle; cp.reason = reason; memset(&rq, 0, sizeof(rq)); rq.ogf = OGF_LINK_CTL; rq.ocf = OCF_DISCONNECT; rq.event = EVT_DISCONN_COMPLETE; rq.cparam = &cp; rq.clen = DISCONNECT_CP_SIZE; rq.rparam = &rp; rq.rlen = EVT_DISCONN_COMPLETE_SIZE; if (hci_send_req(dd, &rq, to) < 0) return -1; if (rp.status) { errno = EIO; return -1; } return 0; } int hci_le_add_white_list(int dd, const bdaddr_t *bdaddr, uint8_t type, int to) { struct hci_request rq; le_add_device_to_white_list_cp cp; uint8_t status; memset(&cp, 0, sizeof(cp)); cp.bdaddr_type = type; bacpy(&cp.bdaddr, bdaddr); memset(&rq, 0, sizeof(rq)); rq.ogf = OGF_LE_CTL; rq.ocf = OCF_LE_ADD_DEVICE_TO_WHITE_LIST; rq.cparam = &cp; rq.clen = LE_ADD_DEVICE_TO_WHITE_LIST_CP_SIZE; rq.rparam = &status; rq.rlen = 1; if (hci_send_req(dd, &rq, to) < 0) return -1; if (status) { errno = EIO; return -1; } return 0; } int hci_le_rm_white_list(int dd, const bdaddr_t *bdaddr, uint8_t type, int to) { struct hci_request rq; le_remove_device_from_white_list_cp cp; uint8_t status; memset(&cp, 0, sizeof(cp)); cp.bdaddr_type = type; bacpy(&cp.bdaddr, bdaddr); memset(&rq, 0, sizeof(rq)); rq.ogf = OGF_LE_CTL; rq.ocf = OCF_LE_REMOVE_DEVICE_FROM_WHITE_LIST; rq.cparam = &cp; rq.clen = LE_REMOVE_DEVICE_FROM_WHITE_LIST_CP_SIZE; rq.rparam = &status; rq.rlen = 1; if (hci_send_req(dd, &rq, to) < 0) return -1; if (status) { errno = EIO; return -1; } return 0; } int hci_le_read_white_list_size(int dd, uint8_t *size, int to) { struct hci_request rq; le_read_white_list_size_rp rp; memset(&rp, 0, sizeof(rp)); memset(&rq, 0, sizeof(rq)); rq.ogf = OGF_LE_CTL; rq.ocf = OCF_LE_READ_WHITE_LIST_SIZE; rq.rparam = &rp; rq.rlen = LE_READ_WHITE_LIST_SIZE_RP_SIZE; if (hci_send_req(dd, &rq, to) < 0) return -1; if (rp.status) { errno = EIO; return -1; } if (size) *size = rp.size; return 0; } int hci_le_clear_white_list(int dd, int to) { struct hci_request rq; uint8_t status; memset(&rq, 0, sizeof(rq)); rq.ogf = OGF_LE_CTL; rq.ocf = OCF_LE_CLEAR_WHITE_LIST; rq.rparam = &status; rq.rlen = 1; if (hci_send_req(dd, &rq, to) < 0) return -1; if (status) { errno = EIO; return -1; } return 0; } int hci_le_add_resolving_list(int dd, const bdaddr_t *bdaddr, uint8_t type, uint8_t *peer_irk, uint8_t *local_irk, int to) { struct hci_request rq; le_add_device_to_resolv_list_cp cp; uint8_t status; memset(&cp, 0, sizeof(cp)); cp.bdaddr_type = type; bacpy(&cp.bdaddr, bdaddr); if (peer_irk) memcpy(cp.peer_irk, peer_irk, 16); if (local_irk) memcpy(cp.local_irk, local_irk, 16); memset(&rq, 0, sizeof(rq)); rq.ogf = OGF_LE_CTL; rq.ocf = OCF_LE_ADD_DEVICE_TO_RESOLV_LIST; rq.cparam = &cp; rq.clen = LE_ADD_DEVICE_TO_RESOLV_LIST_CP_SIZE; rq.rparam = &status; rq.rlen = 1; if (hci_send_req(dd, &rq, to) < 0) return -1; if (status) { errno = EIO; return -1; } return 0; } int hci_le_rm_resolving_list(int dd, const bdaddr_t *bdaddr, uint8_t type, int to) { struct hci_request rq; le_remove_device_from_resolv_list_cp cp; uint8_t status; memset(&cp, 0, sizeof(cp)); cp.bdaddr_type = type; bacpy(&cp.bdaddr, bdaddr); memset(&rq, 0, sizeof(rq)); rq.ogf = OGF_LE_CTL; rq.ocf = OCF_LE_REMOVE_DEVICE_FROM_RESOLV_LIST; rq.cparam = &cp; rq.clen = LE_REMOVE_DEVICE_FROM_RESOLV_LIST_CP_SIZE; rq.rparam = &status; rq.rlen = 1; if (hci_send_req(dd, &rq, to) < 0) return -1; if (status) { errno = EIO; return -1; } return 0; } int hci_le_clear_resolving_list(int dd, int to) { struct hci_request rq; uint8_t status; memset(&rq, 0, sizeof(rq)); rq.ogf = OGF_LE_CTL; rq.ocf = OCF_LE_CLEAR_RESOLV_LIST; rq.rparam = &status; rq.rlen = 1; if (hci_send_req(dd, &rq, to) < 0) return -1; if (status) { errno = EIO; return -1; } return 0; } int hci_le_read_resolving_list_size(int dd, uint8_t *size, int to) { struct hci_request rq; le_read_resolv_list_size_rp rp; memset(&rp, 0, sizeof(rp)); memset(&rq, 0, sizeof(rq)); rq.ogf = OGF_LE_CTL; rq.ocf = OCF_LE_READ_RESOLV_LIST_SIZE; rq.rparam = &rp; rq.rlen = LE_READ_RESOLV_LIST_SIZE_RP_SIZE; if (hci_send_req(dd, &rq, to) < 0) return -1; if (rp.status) { errno = EIO; return -1; } if (size) *size = rp.size; return 0; } int hci_le_set_address_resolution_enable(int dd, uint8_t enable, int to) { struct hci_request rq; le_set_address_resolution_enable_cp cp; uint8_t status; memset(&cp, 0, sizeof(cp)); cp.enable = enable; memset(&rq, 0, sizeof(rq)); rq.ogf = OGF_LE_CTL; rq.ocf = OCF_LE_SET_ADDRESS_RESOLUTION_ENABLE; rq.cparam = &cp; rq.clen = LE_SET_ADDRESS_RESOLUTION_ENABLE_CP_SIZE; rq.rparam = &status; rq.rlen = 1; if (hci_send_req(dd, &rq, to) < 0) return -1; if (status) { errno = EIO; return -1; } return 0; } int hci_read_local_name(int dd, int len, char *name, int to) { read_local_name_rp rp; struct hci_request rq; memset(&rq, 0, sizeof(rq)); rq.ogf = OGF_HOST_CTL; rq.ocf = OCF_READ_LOCAL_NAME; rq.rparam = &rp; rq.rlen = READ_LOCAL_NAME_RP_SIZE; if (hci_send_req(dd, &rq, to) < 0) return -1; if (rp.status) { errno = EIO; return -1; } rp.name[247] = '\0'; strncpy(name, (char *) rp.name, len); return 0; } int hci_write_local_name(int dd, const char *name, int to) { change_local_name_cp cp; struct hci_request rq; memset(&cp, 0, sizeof(cp)); strncpy((char *) cp.name, name, sizeof(cp.name) - 1); memset(&rq, 0, sizeof(rq)); rq.ogf = OGF_HOST_CTL; rq.ocf = OCF_CHANGE_LOCAL_NAME; rq.cparam = &cp; rq.clen = CHANGE_LOCAL_NAME_CP_SIZE; if (hci_send_req(dd, &rq, to) < 0) return -1; return 0; } int hci_read_remote_name_with_clock_offset(int dd, const bdaddr_t *bdaddr, uint8_t pscan_rep_mode, uint16_t clkoffset, int len, char *name, int to) { evt_remote_name_req_complete rn; remote_name_req_cp cp; struct hci_request rq; memset(&cp, 0, sizeof(cp)); bacpy(&cp.bdaddr, bdaddr); cp.pscan_rep_mode = pscan_rep_mode; cp.clock_offset = clkoffset; memset(&rq, 0, sizeof(rq)); rq.ogf = OGF_LINK_CTL; rq.ocf = OCF_REMOTE_NAME_REQ; rq.cparam = &cp; rq.clen = REMOTE_NAME_REQ_CP_SIZE; rq.event = EVT_REMOTE_NAME_REQ_COMPLETE; rq.rparam = &rn; rq.rlen = EVT_REMOTE_NAME_REQ_COMPLETE_SIZE; if (hci_send_req(dd, &rq, to) < 0) return -1; if (rn.status) { errno = EIO; return -1; } rn.name[247] = '\0'; strncpy(name, (char *) rn.name, len); return 0; } int hci_read_remote_name(int dd, const bdaddr_t *bdaddr, int len, char *name, int to) { return hci_read_remote_name_with_clock_offset(dd, bdaddr, 0x02, 0x0000, len, name, to); } int hci_read_remote_name_cancel(int dd, const bdaddr_t *bdaddr, int to) { remote_name_req_cancel_cp cp; struct hci_request rq; memset(&cp, 0, sizeof(cp)); bacpy(&cp.bdaddr, bdaddr); memset(&rq, 0, sizeof(rq)); rq.ogf = OGF_LINK_CTL; rq.ocf = OCF_REMOTE_NAME_REQ_CANCEL; rq.cparam = &cp; rq.clen = REMOTE_NAME_REQ_CANCEL_CP_SIZE; if (hci_send_req(dd, &rq, to) < 0) return -1; return 0; } int hci_read_remote_version(int dd, uint16_t handle, struct hci_version *ver, int to) { evt_read_remote_version_complete rp; read_remote_version_cp cp; struct hci_request rq; memset(&cp, 0, sizeof(cp)); cp.handle = handle; memset(&rq, 0, sizeof(rq)); rq.ogf = OGF_LINK_CTL; rq.ocf = OCF_READ_REMOTE_VERSION; rq.event = EVT_READ_REMOTE_VERSION_COMPLETE; rq.cparam = &cp; rq.clen = READ_REMOTE_VERSION_CP_SIZE; rq.rparam = &rp; rq.rlen = EVT_READ_REMOTE_VERSION_COMPLETE_SIZE; if (hci_send_req(dd, &rq, to) < 0) return -1; if (rp.status) { errno = EIO; return -1; } ver->manufacturer = btohs(rp.manufacturer); ver->lmp_ver = rp.lmp_ver; ver->lmp_subver = btohs(rp.lmp_subver); return 0; } int hci_read_remote_features(int dd, uint16_t handle, uint8_t *features, int to) { evt_read_remote_features_complete rp; read_remote_features_cp cp; struct hci_request rq; memset(&cp, 0, sizeof(cp)); cp.handle = handle; memset(&rq, 0, sizeof(rq)); rq.ogf = OGF_LINK_CTL; rq.ocf = OCF_READ_REMOTE_FEATURES; rq.event = EVT_READ_REMOTE_FEATURES_COMPLETE; rq.cparam = &cp; rq.clen = READ_REMOTE_FEATURES_CP_SIZE; rq.rparam = &rp; rq.rlen = EVT_READ_REMOTE_FEATURES_COMPLETE_SIZE; if (hci_send_req(dd, &rq, to) < 0) return -1; if (rp.status) { errno = EIO; return -1; } if (features) memcpy(features, rp.features, 8); return 0; } int hci_read_remote_ext_features(int dd, uint16_t handle, uint8_t page, uint8_t *max_page, uint8_t *features, int to) { evt_read_remote_ext_features_complete rp; read_remote_ext_features_cp cp; struct hci_request rq; memset(&cp, 0, sizeof(cp)); cp.handle = handle; cp.page_num = page; memset(&rq, 0, sizeof(rq)); rq.ogf = OGF_LINK_CTL; rq.ocf = OCF_READ_REMOTE_EXT_FEATURES; rq.event = EVT_READ_REMOTE_EXT_FEATURES_COMPLETE; rq.cparam = &cp; rq.clen = READ_REMOTE_EXT_FEATURES_CP_SIZE; rq.rparam = &rp; rq.rlen = EVT_READ_REMOTE_EXT_FEATURES_COMPLETE_SIZE; if (hci_send_req(dd, &rq, to) < 0) return -1; if (rp.status) { errno = EIO; return -1; } if (max_page) *max_page = rp.max_page_num; if (features) memcpy(features, rp.features, 8); return 0; } int hci_read_clock_offset(int dd, uint16_t handle, uint16_t *clkoffset, int to) { evt_read_clock_offset_complete rp; read_clock_offset_cp cp; struct hci_request rq; memset(&cp, 0, sizeof(cp)); cp.handle = handle; memset(&rq, 0, sizeof(rq)); rq.ogf = OGF_LINK_CTL; rq.ocf = OCF_READ_CLOCK_OFFSET; rq.event = EVT_READ_CLOCK_OFFSET_COMPLETE; rq.cparam = &cp; rq.clen = READ_CLOCK_OFFSET_CP_SIZE; rq.rparam = &rp; rq.rlen = EVT_READ_CLOCK_OFFSET_COMPLETE_SIZE; if (hci_send_req(dd, &rq, to) < 0) return -1; if (rp.status) { errno = EIO; return -1; } *clkoffset = rp.clock_offset; return 0; } int hci_read_local_version(int dd, struct hci_version *ver, int to) { read_local_version_rp rp; struct hci_request rq; memset(&rq, 0, sizeof(rq)); rq.ogf = OGF_INFO_PARAM; rq.ocf = OCF_READ_LOCAL_VERSION; rq.rparam = &rp; rq.rlen = READ_LOCAL_VERSION_RP_SIZE; if (hci_send_req(dd, &rq, to) < 0) return -1; if (rp.status) { errno = EIO; return -1; } ver->manufacturer = btohs(rp.manufacturer); ver->hci_ver = rp.hci_ver; ver->hci_rev = btohs(rp.hci_rev); ver->lmp_ver = rp.lmp_ver; ver->lmp_subver = btohs(rp.lmp_subver); return 0; } int hci_read_local_commands(int dd, uint8_t *commands, int to) { read_local_commands_rp rp; struct hci_request rq; memset(&rq, 0, sizeof(rq)); rq.ogf = OGF_INFO_PARAM; rq.ocf = OCF_READ_LOCAL_COMMANDS; rq.rparam = &rp; rq.rlen = READ_LOCAL_COMMANDS_RP_SIZE; if (hci_send_req(dd, &rq, to) < 0) return -1; if (rp.status) { errno = EIO; return -1; } if (commands) memcpy(commands, rp.commands, 64); return 0; } int hci_read_local_features(int dd, uint8_t *features, int to) { read_local_features_rp rp; struct hci_request rq; memset(&rq, 0, sizeof(rq)); rq.ogf = OGF_INFO_PARAM; rq.ocf = OCF_READ_LOCAL_FEATURES; rq.rparam = &rp; rq.rlen = READ_LOCAL_FEATURES_RP_SIZE; if (hci_send_req(dd, &rq, to) < 0) return -1; if (rp.status) { errno = EIO; return -1; } if (features) memcpy(features, rp.features, 8); return 0; } int hci_read_local_ext_features(int dd, uint8_t page, uint8_t *max_page, uint8_t *features, int to) { read_local_ext_features_cp cp; read_local_ext_features_rp rp; struct hci_request rq; cp.page_num = page; memset(&rq, 0, sizeof(rq)); rq.ogf = OGF_INFO_PARAM; rq.ocf = OCF_READ_LOCAL_EXT_FEATURES; rq.cparam = &cp; rq.clen = READ_LOCAL_EXT_FEATURES_CP_SIZE; rq.rparam = &rp; rq.rlen = READ_LOCAL_EXT_FEATURES_RP_SIZE; if (hci_send_req(dd, &rq, to) < 0) return -1; if (rp.status) { errno = EIO; return -1; } if (max_page) *max_page = rp.max_page_num; if (features) memcpy(features, rp.features, 8); return 0; } int hci_read_bd_addr(int dd, bdaddr_t *bdaddr, int to) { read_bd_addr_rp rp; struct hci_request rq; memset(&rq, 0, sizeof(rq)); rq.ogf = OGF_INFO_PARAM; rq.ocf = OCF_READ_BD_ADDR; rq.rparam = &rp; rq.rlen = READ_BD_ADDR_RP_SIZE; if (hci_send_req(dd, &rq, to) < 0) return -1; if (rp.status) { errno = EIO; return -1; } if (bdaddr) bacpy(bdaddr, &rp.bdaddr); return 0; } int hci_read_class_of_dev(int dd, uint8_t *cls, int to) { read_class_of_dev_rp rp; struct hci_request rq; memset(&rq, 0, sizeof(rq)); rq.ogf = OGF_HOST_CTL; rq.ocf = OCF_READ_CLASS_OF_DEV; rq.rparam = &rp; rq.rlen = READ_CLASS_OF_DEV_RP_SIZE; if (hci_send_req(dd, &rq, to) < 0) return -1; if (rp.status) { errno = EIO; return -1; } memcpy(cls, rp.dev_class, 3); return 0; } int hci_write_class_of_dev(int dd, uint32_t cls, int to) { write_class_of_dev_cp cp; struct hci_request rq; memset(&rq, 0, sizeof(rq)); cp.dev_class[0] = cls & 0xff; cp.dev_class[1] = (cls >> 8) & 0xff; cp.dev_class[2] = (cls >> 16) & 0xff; rq.ogf = OGF_HOST_CTL; rq.ocf = OCF_WRITE_CLASS_OF_DEV; rq.cparam = &cp; rq.clen = WRITE_CLASS_OF_DEV_CP_SIZE; return hci_send_req(dd, &rq, to); } int hci_read_voice_setting(int dd, uint16_t *vs, int to) { read_voice_setting_rp rp; struct hci_request rq; memset(&rq, 0, sizeof(rq)); rq.ogf = OGF_HOST_CTL; rq.ocf = OCF_READ_VOICE_SETTING; rq.rparam = &rp; rq.rlen = READ_VOICE_SETTING_RP_SIZE; if (hci_send_req(dd, &rq, to) < 0) return -1; if (rp.status) { errno = EIO; return -1; } *vs = rp.voice_setting; return 0; } int hci_write_voice_setting(int dd, uint16_t vs, int to) { write_voice_setting_cp cp; struct hci_request rq; memset(&rq, 0, sizeof(rq)); cp.voice_setting = vs; rq.ogf = OGF_HOST_CTL; rq.ocf = OCF_WRITE_VOICE_SETTING; rq.cparam = &cp; rq.clen = WRITE_VOICE_SETTING_CP_SIZE; return hci_send_req(dd, &rq, to); } int hci_read_current_iac_lap(int dd, uint8_t *num_iac, uint8_t *lap, int to) { read_current_iac_lap_rp rp; struct hci_request rq; memset(&rq, 0, sizeof(rq)); rq.ogf = OGF_HOST_CTL; rq.ocf = OCF_READ_CURRENT_IAC_LAP; rq.rparam = &rp; rq.rlen = READ_CURRENT_IAC_LAP_RP_SIZE; if (hci_send_req(dd, &rq, to) < 0) return -1; if (rp.status) { errno = EIO; return -1; } *num_iac = rp.num_current_iac; memcpy(lap, rp.lap, rp.num_current_iac * 3); return 0; } int hci_write_current_iac_lap(int dd, uint8_t num_iac, uint8_t *lap, int to) { write_current_iac_lap_cp cp; struct hci_request rq; memset(&cp, 0, sizeof(cp)); cp.num_current_iac = num_iac; memcpy(&cp.lap, lap, num_iac * 3); memset(&rq, 0, sizeof(rq)); rq.ogf = OGF_HOST_CTL; rq.ocf = OCF_WRITE_CURRENT_IAC_LAP; rq.cparam = &cp; rq.clen = num_iac * 3 + 1; return hci_send_req(dd, &rq, to); } int hci_read_stored_link_key(int dd, bdaddr_t *bdaddr, uint8_t all, int to) { read_stored_link_key_cp cp; struct hci_request rq; memset(&cp, 0, sizeof(cp)); bacpy(&cp.bdaddr, bdaddr); cp.read_all = all; memset(&rq, 0, sizeof(rq)); rq.ogf = OGF_HOST_CTL; rq.ocf = OCF_READ_STORED_LINK_KEY; rq.cparam = &cp; rq.clen = READ_STORED_LINK_KEY_CP_SIZE; return hci_send_req(dd, &rq, to); } int hci_write_stored_link_key(int dd, bdaddr_t *bdaddr, uint8_t *key, int to) { unsigned char cp[WRITE_STORED_LINK_KEY_CP_SIZE + 6 + 16]; struct hci_request rq; memset(&cp, 0, sizeof(cp)); cp[0] = 1; bacpy((bdaddr_t *) (cp + 1), bdaddr); memcpy(cp + 7, key, 16); memset(&rq, 0, sizeof(rq)); rq.ogf = OGF_HOST_CTL; rq.ocf = OCF_WRITE_STORED_LINK_KEY; rq.cparam = &cp; rq.clen = WRITE_STORED_LINK_KEY_CP_SIZE + 6 + 16; return hci_send_req(dd, &rq, to); } int hci_delete_stored_link_key(int dd, bdaddr_t *bdaddr, uint8_t all, int to) { delete_stored_link_key_cp cp; struct hci_request rq; memset(&cp, 0, sizeof(cp)); bacpy(&cp.bdaddr, bdaddr); cp.delete_all = all; memset(&rq, 0, sizeof(rq)); rq.ogf = OGF_HOST_CTL; rq.ocf = OCF_DELETE_STORED_LINK_KEY; rq.cparam = &cp; rq.clen = DELETE_STORED_LINK_KEY_CP_SIZE; return hci_send_req(dd, &rq, to); } int hci_authenticate_link(int dd, uint16_t handle, int to) { auth_requested_cp cp; evt_auth_complete rp; struct hci_request rq; cp.handle = handle; rq.ogf = OGF_LINK_CTL; rq.ocf = OCF_AUTH_REQUESTED; rq.event = EVT_AUTH_COMPLETE; rq.cparam = &cp; rq.clen = AUTH_REQUESTED_CP_SIZE; rq.rparam = &rp; rq.rlen = EVT_AUTH_COMPLETE_SIZE; if (hci_send_req(dd, &rq, to) < 0) return -1; if (rp.status) { errno = EIO; return -1; } return 0; } int hci_encrypt_link(int dd, uint16_t handle, uint8_t encrypt, int to) { set_conn_encrypt_cp cp; evt_encrypt_change rp; struct hci_request rq; cp.handle = handle; cp.encrypt = encrypt; rq.ogf = OGF_LINK_CTL; rq.ocf = OCF_SET_CONN_ENCRYPT; rq.event = EVT_ENCRYPT_CHANGE; rq.cparam = &cp; rq.clen = SET_CONN_ENCRYPT_CP_SIZE; rq.rparam = &rp; rq.rlen = EVT_ENCRYPT_CHANGE_SIZE; if (hci_send_req(dd, &rq, to) < 0) return -1; if (rp.status) { errno = EIO; return -1; } return 0; } int hci_change_link_key(int dd, uint16_t handle, int to) { change_conn_link_key_cp cp; evt_change_conn_link_key_complete rp; struct hci_request rq; cp.handle = handle; rq.ogf = OGF_LINK_CTL; rq.ocf = OCF_CHANGE_CONN_LINK_KEY; rq.event = EVT_CHANGE_CONN_LINK_KEY_COMPLETE; rq.cparam = &cp; rq.clen = CHANGE_CONN_LINK_KEY_CP_SIZE; rq.rparam = &rp; rq.rlen = EVT_CHANGE_CONN_LINK_KEY_COMPLETE_SIZE; if (hci_send_req(dd, &rq, to) < 0) return -1; if (rp.status) { errno = EIO; return -1; } return 0; } int hci_switch_role(int dd, bdaddr_t *bdaddr, uint8_t role, int to) { switch_role_cp cp; evt_role_change rp; struct hci_request rq; bacpy(&cp.bdaddr, bdaddr); cp.role = role; rq.ogf = OGF_LINK_POLICY; rq.ocf = OCF_SWITCH_ROLE; rq.cparam = &cp; rq.clen = SWITCH_ROLE_CP_SIZE; rq.rparam = &rp; rq.rlen = EVT_ROLE_CHANGE_SIZE; rq.event = EVT_ROLE_CHANGE; if (hci_send_req(dd, &rq, to) < 0) return -1; if (rp.status) { errno = EIO; return -1; } return 0; } int hci_park_mode(int dd, uint16_t handle, uint16_t max_interval, uint16_t min_interval, int to) { park_mode_cp cp; evt_mode_change rp; struct hci_request rq; memset(&cp, 0, sizeof (cp)); cp.handle = handle; cp.max_interval = max_interval; cp.min_interval = min_interval; memset(&rq, 0, sizeof (rq)); rq.ogf = OGF_LINK_POLICY; rq.ocf = OCF_PARK_MODE; rq.event = EVT_MODE_CHANGE; rq.cparam = &cp; rq.clen = PARK_MODE_CP_SIZE; rq.rparam = &rp; rq.rlen = EVT_MODE_CHANGE_SIZE; if (hci_send_req(dd, &rq, to) < 0) return -1; if (rp.status) { errno = EIO; return -1; } return 0; } int hci_exit_park_mode(int dd, uint16_t handle, int to) { exit_park_mode_cp cp; evt_mode_change rp; struct hci_request rq; memset(&cp, 0, sizeof (cp)); cp.handle = handle; memset (&rq, 0, sizeof (rq)); rq.ogf = OGF_LINK_POLICY; rq.ocf = OCF_EXIT_PARK_MODE; rq.event = EVT_MODE_CHANGE; rq.cparam = &cp; rq.clen = EXIT_PARK_MODE_CP_SIZE; rq.rparam = &rp; rq.rlen = EVT_MODE_CHANGE_SIZE; if (hci_send_req(dd, &rq, to) < 0) return -1; if (rp.status) { errno = EIO; return -1; } return 0; } int hci_read_inquiry_scan_type(int dd, uint8_t *type, int to) { read_inquiry_scan_type_rp rp; struct hci_request rq; memset(&rq, 0, sizeof(rq)); rq.ogf = OGF_HOST_CTL; rq.ocf = OCF_READ_INQUIRY_SCAN_TYPE; rq.rparam = &rp; rq.rlen = READ_INQUIRY_SCAN_TYPE_RP_SIZE; if (hci_send_req(dd, &rq, to) < 0) return -1; if (rp.status) { errno = EIO; return -1; } *type = rp.type; return 0; } int hci_write_inquiry_scan_type(int dd, uint8_t type, int to) { write_inquiry_scan_type_cp cp; write_inquiry_scan_type_rp rp; struct hci_request rq; memset(&cp, 0, sizeof(cp)); cp.type = type; memset(&rq, 0, sizeof(rq)); rq.ogf = OGF_HOST_CTL; rq.ocf = OCF_WRITE_INQUIRY_SCAN_TYPE; rq.cparam = &cp; rq.clen = WRITE_INQUIRY_SCAN_TYPE_CP_SIZE; rq.rparam = &rp; rq.rlen = WRITE_INQUIRY_SCAN_TYPE_RP_SIZE; if (hci_send_req(dd, &rq, to) < 0) return -1; if (rp.status) { errno = EIO; return -1; } return 0; } int hci_read_inquiry_mode(int dd, uint8_t *mode, int to) { read_inquiry_mode_rp rp; struct hci_request rq; memset(&rq, 0, sizeof(rq)); rq.ogf = OGF_HOST_CTL; rq.ocf = OCF_READ_INQUIRY_MODE; rq.rparam = &rp; rq.rlen = READ_INQUIRY_MODE_RP_SIZE; if (hci_send_req(dd, &rq, to) < 0) return -1; if (rp.status) { errno = EIO; return -1; } *mode = rp.mode; return 0; } int hci_write_inquiry_mode(int dd, uint8_t mode, int to) { write_inquiry_mode_cp cp; write_inquiry_mode_rp rp; struct hci_request rq; memset(&cp, 0, sizeof(cp)); cp.mode = mode; memset(&rq, 0, sizeof(rq)); rq.ogf = OGF_HOST_CTL; rq.ocf = OCF_WRITE_INQUIRY_MODE; rq.cparam = &cp; rq.clen = WRITE_INQUIRY_MODE_CP_SIZE; rq.rparam = &rp; rq.rlen = WRITE_INQUIRY_MODE_RP_SIZE; if (hci_send_req(dd, &rq, to) < 0) return -1; if (rp.status) { errno = EIO; return -1; } return 0; } int hci_read_afh_mode(int dd, uint8_t *mode, int to) { read_afh_mode_rp rp; struct hci_request rq; memset(&rq, 0, sizeof(rq)); rq.ogf = OGF_HOST_CTL; rq.ocf = OCF_READ_AFH_MODE; rq.rparam = &rp; rq.rlen = READ_AFH_MODE_RP_SIZE; if (hci_send_req(dd, &rq, to) < 0) return -1; if (rp.status) { errno = EIO; return -1; } *mode = rp.mode; return 0; } int hci_write_afh_mode(int dd, uint8_t mode, int to) { write_afh_mode_cp cp; write_afh_mode_rp rp; struct hci_request rq; memset(&cp, 0, sizeof(cp)); cp.mode = mode; memset(&rq, 0, sizeof(rq)); rq.ogf = OGF_HOST_CTL; rq.ocf = OCF_WRITE_AFH_MODE; rq.cparam = &cp; rq.clen = WRITE_AFH_MODE_CP_SIZE; rq.rparam = &rp; rq.rlen = WRITE_AFH_MODE_RP_SIZE; if (hci_send_req(dd, &rq, to) < 0) return -1; if (rp.status) { errno = EIO; return -1; } return 0; } int hci_read_ext_inquiry_response(int dd, uint8_t *fec, uint8_t *data, int to) { read_ext_inquiry_response_rp rp; struct hci_request rq; memset(&rq, 0, sizeof(rq)); rq.ogf = OGF_HOST_CTL; rq.ocf = OCF_READ_EXT_INQUIRY_RESPONSE; rq.rparam = &rp; rq.rlen = READ_EXT_INQUIRY_RESPONSE_RP_SIZE; if (hci_send_req(dd, &rq, to) < 0) return -1; if (rp.status) { errno = EIO; return -1; } *fec = rp.fec; memcpy(data, rp.data, HCI_MAX_EIR_LENGTH); return 0; } int hci_write_ext_inquiry_response(int dd, uint8_t fec, uint8_t *data, int to) { write_ext_inquiry_response_cp cp; write_ext_inquiry_response_rp rp; struct hci_request rq; memset(&cp, 0, sizeof(cp)); cp.fec = fec; memcpy(cp.data, data, HCI_MAX_EIR_LENGTH); memset(&rq, 0, sizeof(rq)); rq.ogf = OGF_HOST_CTL; rq.ocf = OCF_WRITE_EXT_INQUIRY_RESPONSE; rq.cparam = &cp; rq.clen = WRITE_EXT_INQUIRY_RESPONSE_CP_SIZE; rq.rparam = &rp; rq.rlen = WRITE_EXT_INQUIRY_RESPONSE_RP_SIZE; if (hci_send_req(dd, &rq, to) < 0) return -1; if (rp.status) { errno = EIO; return -1; } return 0; } int hci_read_simple_pairing_mode(int dd, uint8_t *mode, int to) { read_simple_pairing_mode_rp rp; struct hci_request rq; memset(&rq, 0, sizeof(rq)); rq.ogf = OGF_HOST_CTL; rq.ocf = OCF_READ_SIMPLE_PAIRING_MODE; rq.rparam = &rp; rq.rlen = READ_SIMPLE_PAIRING_MODE_RP_SIZE; if (hci_send_req(dd, &rq, to) < 0) return -1; if (rp.status) { errno = EIO; return -1; } *mode = rp.mode; return 0; } int hci_write_simple_pairing_mode(int dd, uint8_t mode, int to) { write_simple_pairing_mode_cp cp; write_simple_pairing_mode_rp rp; struct hci_request rq; memset(&cp, 0, sizeof(cp)); cp.mode = mode; memset(&rq, 0, sizeof(rq)); rq.ogf = OGF_HOST_CTL; rq.ocf = OCF_WRITE_SIMPLE_PAIRING_MODE; rq.cparam = &cp; rq.clen = WRITE_SIMPLE_PAIRING_MODE_CP_SIZE; rq.rparam = &rp; rq.rlen = WRITE_SIMPLE_PAIRING_MODE_RP_SIZE; if (hci_send_req(dd, &rq, to) < 0) return -1; if (rp.status) { errno = EIO; return -1; } return 0; } int hci_read_local_oob_data(int dd, uint8_t *hash, uint8_t *randomizer, int to) { read_local_oob_data_rp rp; struct hci_request rq; memset(&rq, 0, sizeof(rq)); rq.ogf = OGF_HOST_CTL; rq.ocf = OCF_READ_LOCAL_OOB_DATA; rq.rparam = &rp; rq.rlen = READ_LOCAL_OOB_DATA_RP_SIZE; if (hci_send_req(dd, &rq, to) < 0) return -1; if (rp.status) { errno = EIO; return -1; } memcpy(hash, rp.hash, 16); memcpy(randomizer, rp.randomizer, 16); return 0; } int hci_read_inq_response_tx_power_level(int dd, int8_t *level, int to) { read_inq_response_tx_power_level_rp rp; struct hci_request rq; memset(&rq, 0, sizeof(rq)); rq.ogf = OGF_HOST_CTL; rq.ocf = OCF_READ_INQ_RESPONSE_TX_POWER_LEVEL; rq.rparam = &rp; rq.rlen = READ_INQ_RESPONSE_TX_POWER_LEVEL_RP_SIZE; if (hci_send_req(dd, &rq, to) < 0) return -1; if (rp.status) { errno = EIO; return -1; } *level = rp.level; return 0; } int hci_read_inquiry_transmit_power_level(int dd, int8_t *level, int to) { return hci_read_inq_response_tx_power_level(dd, level, to); } int hci_write_inquiry_transmit_power_level(int dd, int8_t level, int to) { write_inquiry_transmit_power_level_cp cp; write_inquiry_transmit_power_level_rp rp; struct hci_request rq; memset(&cp, 0, sizeof(cp)); cp.level = level; memset(&rq, 0, sizeof(rq)); rq.ogf = OGF_HOST_CTL; rq.ocf = OCF_WRITE_INQUIRY_TRANSMIT_POWER_LEVEL; rq.cparam = &cp; rq.clen = WRITE_INQUIRY_TRANSMIT_POWER_LEVEL_CP_SIZE; rq.rparam = &rp; rq.rlen = WRITE_INQUIRY_TRANSMIT_POWER_LEVEL_RP_SIZE; if (hci_send_req(dd, &rq, to) < 0) return -1; if (rp.status) { errno = EIO; return -1; } return 0; } int hci_read_transmit_power_level(int dd, uint16_t handle, uint8_t type, int8_t *level, int to) { read_transmit_power_level_cp cp; read_transmit_power_level_rp rp; struct hci_request rq; memset(&cp, 0, sizeof(cp)); cp.handle = handle; cp.type = type; memset(&rq, 0, sizeof(rq)); rq.ogf = OGF_HOST_CTL; rq.ocf = OCF_READ_TRANSMIT_POWER_LEVEL; rq.cparam = &cp; rq.clen = READ_TRANSMIT_POWER_LEVEL_CP_SIZE; rq.rparam = &rp; rq.rlen = READ_TRANSMIT_POWER_LEVEL_RP_SIZE; if (hci_send_req(dd, &rq, to) < 0) return -1; if (rp.status) { errno = EIO; return -1; } *level = rp.level; return 0; } int hci_read_link_policy(int dd, uint16_t handle, uint16_t *policy, int to) { read_link_policy_rp rp; struct hci_request rq; memset(&rq, 0, sizeof(rq)); rq.ogf = OGF_LINK_POLICY; rq.ocf = OCF_READ_LINK_POLICY; rq.cparam = &handle; rq.clen = 2; rq.rparam = &rp; rq.rlen = READ_LINK_POLICY_RP_SIZE; if (hci_send_req(dd, &rq, to) < 0) return -1; if (rp.status) { errno = EIO; return -1; } *policy = rp.policy; return 0; } int hci_write_link_policy(int dd, uint16_t handle, uint16_t policy, int to) { write_link_policy_cp cp; write_link_policy_rp rp; struct hci_request rq; memset(&cp, 0, sizeof(cp)); cp.handle = handle; cp.policy = policy; memset(&rq, 0, sizeof(rq)); rq.ogf = OGF_LINK_POLICY; rq.ocf = OCF_WRITE_LINK_POLICY; rq.cparam = &cp; rq.clen = WRITE_LINK_POLICY_CP_SIZE; rq.rparam = &rp; rq.rlen = WRITE_LINK_POLICY_RP_SIZE; if (hci_send_req(dd, &rq, to) < 0) return -1; if (rp.status) { errno = EIO; return -1; } return 0; } int hci_read_link_supervision_timeout(int dd, uint16_t handle, uint16_t *timeout, int to) { read_link_supervision_timeout_rp rp; struct hci_request rq; memset(&rq, 0, sizeof(rq)); rq.ogf = OGF_HOST_CTL; rq.ocf = OCF_READ_LINK_SUPERVISION_TIMEOUT; rq.cparam = &handle; rq.clen = 2; rq.rparam = &rp; rq.rlen = READ_LINK_SUPERVISION_TIMEOUT_RP_SIZE; if (hci_send_req(dd, &rq, to) < 0) return -1; if (rp.status) { errno = EIO; return -1; } *timeout = rp.timeout; return 0; } int hci_write_link_supervision_timeout(int dd, uint16_t handle, uint16_t timeout, int to) { write_link_supervision_timeout_cp cp; write_link_supervision_timeout_rp rp; struct hci_request rq; memset(&cp, 0, sizeof(cp)); cp.handle = handle; cp.timeout = timeout; memset(&rq, 0, sizeof(rq)); rq.ogf = OGF_HOST_CTL; rq.ocf = OCF_WRITE_LINK_SUPERVISION_TIMEOUT; rq.cparam = &cp; rq.clen = WRITE_LINK_SUPERVISION_TIMEOUT_CP_SIZE; rq.rparam = &rp; rq.rlen = WRITE_LINK_SUPERVISION_TIMEOUT_RP_SIZE; if (hci_send_req(dd, &rq, to) < 0) return -1; if (rp.status) { errno = EIO; return -1; } return 0; } int hci_set_afh_classification(int dd, uint8_t *map, int to) { set_afh_classification_cp cp; set_afh_classification_rp rp; struct hci_request rq; memset(&cp, 0, sizeof(cp)); memcpy(cp.map, map, 10); memset(&rq, 0, sizeof(rq)); rq.ogf = OGF_HOST_CTL; rq.ocf = OCF_SET_AFH_CLASSIFICATION; rq.cparam = &cp; rq.clen = SET_AFH_CLASSIFICATION_CP_SIZE; rq.rparam = &rp; rq.rlen = SET_AFH_CLASSIFICATION_RP_SIZE; if (hci_send_req(dd, &rq, to) < 0) return -1; if (rp.status) { errno = EIO; return -1; } return 0; } int hci_read_link_quality(int dd, uint16_t handle, uint8_t *link_quality, int to) { read_link_quality_rp rp; struct hci_request rq; memset(&rq, 0, sizeof(rq)); rq.ogf = OGF_STATUS_PARAM; rq.ocf = OCF_READ_LINK_QUALITY; rq.cparam = &handle; rq.clen = 2; rq.rparam = &rp; rq.rlen = READ_LINK_QUALITY_RP_SIZE; if (hci_send_req(dd, &rq, to) < 0) return -1; if (rp.status) { errno = EIO; return -1; } *link_quality = rp.link_quality; return 0; } int hci_read_rssi(int dd, uint16_t handle, int8_t *rssi, int to) { read_rssi_rp rp; struct hci_request rq; memset(&rq, 0, sizeof(rq)); rq.ogf = OGF_STATUS_PARAM; rq.ocf = OCF_READ_RSSI; rq.cparam = &handle; rq.clen = 2; rq.rparam = &rp; rq.rlen = READ_RSSI_RP_SIZE; if (hci_send_req(dd, &rq, to) < 0) return -1; if (rp.status) { errno = EIO; return -1; } *rssi = rp.rssi; return 0; } int hci_read_afh_map(int dd, uint16_t handle, uint8_t *mode, uint8_t *map, int to) { read_afh_map_rp rp; struct hci_request rq; memset(&rq, 0, sizeof(rq)); rq.ogf = OGF_STATUS_PARAM; rq.ocf = OCF_READ_AFH_MAP; rq.cparam = &handle; rq.clen = 2; rq.rparam = &rp; rq.rlen = READ_AFH_MAP_RP_SIZE; if (hci_send_req(dd, &rq, to) < 0) return -1; if (rp.status) { errno = EIO; return -1; } *mode = rp.mode; memcpy(map, rp.map, 10); return 0; } int hci_read_clock(int dd, uint16_t handle, uint8_t which, uint32_t *clock, uint16_t *accuracy, int to) { read_clock_cp cp; read_clock_rp rp; struct hci_request rq; memset(&cp, 0, sizeof(cp)); cp.handle = handle; cp.which_clock = which; memset(&rq, 0, sizeof(rq)); rq.ogf = OGF_STATUS_PARAM; rq.ocf = OCF_READ_CLOCK; rq.cparam = &cp; rq.clen = READ_CLOCK_CP_SIZE; rq.rparam = &rp; rq.rlen = READ_CLOCK_RP_SIZE; if (hci_send_req(dd, &rq, to) < 0) return -1; if (rp.status) { errno = EIO; return -1; } *clock = rp.clock; *accuracy = rp.accuracy; return 0; } int hci_le_set_scan_enable(int dd, uint8_t enable, uint8_t filter_dup, int to) { struct hci_request rq; le_set_scan_enable_cp scan_cp; uint8_t status; memset(&scan_cp, 0, sizeof(scan_cp)); scan_cp.enable = enable; scan_cp.filter_dup = filter_dup; memset(&rq, 0, sizeof(rq)); rq.ogf = OGF_LE_CTL; rq.ocf = OCF_LE_SET_SCAN_ENABLE; rq.cparam = &scan_cp; rq.clen = LE_SET_SCAN_ENABLE_CP_SIZE; rq.rparam = &status; rq.rlen = 1; if (hci_send_req(dd, &rq, to) < 0) return -1; if (status) { errno = EIO; return -1; } return 0; } int hci_le_set_scan_parameters(int dd, uint8_t type, uint16_t interval, uint16_t window, uint8_t own_type, uint8_t filter, int to) { struct hci_request rq; le_set_scan_parameters_cp param_cp; uint8_t status; memset(¶m_cp, 0, sizeof(param_cp)); param_cp.type = type; param_cp.interval = interval; param_cp.window = window; param_cp.own_bdaddr_type = own_type; param_cp.filter = filter; memset(&rq, 0, sizeof(rq)); rq.ogf = OGF_LE_CTL; rq.ocf = OCF_LE_SET_SCAN_PARAMETERS; rq.cparam = ¶m_cp; rq.clen = LE_SET_SCAN_PARAMETERS_CP_SIZE; rq.rparam = &status; rq.rlen = 1; if (hci_send_req(dd, &rq, to) < 0) return -1; if (status) { errno = EIO; return -1; } return 0; } int hci_le_set_advertise_enable(int dd, uint8_t enable, int to) { struct hci_request rq; le_set_advertise_enable_cp adv_cp; uint8_t status; memset(&adv_cp, 0, sizeof(adv_cp)); adv_cp.enable = enable; memset(&rq, 0, sizeof(rq)); rq.ogf = OGF_LE_CTL; rq.ocf = OCF_LE_SET_ADVERTISE_ENABLE; rq.cparam = &adv_cp; rq.clen = LE_SET_ADVERTISE_ENABLE_CP_SIZE; rq.rparam = &status; rq.rlen = 1; if (hci_send_req(dd, &rq, to) < 0) return -1; if (status) { errno = EIO; return -1; } return 0; } int hci_le_create_conn(int dd, uint16_t interval, uint16_t window, uint8_t initiator_filter, uint8_t peer_bdaddr_type, bdaddr_t peer_bdaddr, uint8_t own_bdaddr_type, uint16_t min_interval, uint16_t max_interval, uint16_t latency, uint16_t supervision_timeout, uint16_t min_ce_length, uint16_t max_ce_length, uint16_t *handle, int to) { struct hci_request rq; le_create_connection_cp create_conn_cp; evt_le_connection_complete conn_complete_rp; memset(&create_conn_cp, 0, sizeof(create_conn_cp)); create_conn_cp.interval = interval; create_conn_cp.window = window; create_conn_cp.initiator_filter = initiator_filter; create_conn_cp.peer_bdaddr_type = peer_bdaddr_type; create_conn_cp.peer_bdaddr = peer_bdaddr; create_conn_cp.own_bdaddr_type = own_bdaddr_type; create_conn_cp.min_interval = min_interval; create_conn_cp.max_interval = max_interval; create_conn_cp.latency = latency; create_conn_cp.supervision_timeout = supervision_timeout; create_conn_cp.min_ce_length = min_ce_length; create_conn_cp.max_ce_length = max_ce_length; memset(&rq, 0, sizeof(rq)); rq.ogf = OGF_LE_CTL; rq.ocf = OCF_LE_CREATE_CONN; rq.event = EVT_LE_CONN_COMPLETE; rq.cparam = &create_conn_cp; rq.clen = LE_CREATE_CONN_CP_SIZE; rq.rparam = &conn_complete_rp; rq.rlen = EVT_CONN_COMPLETE_SIZE; if (hci_send_req(dd, &rq, to) < 0) return -1; if (conn_complete_rp.status) { errno = EIO; return -1; } if (handle) *handle = conn_complete_rp.handle; return 0; } int hci_le_conn_update(int dd, uint16_t handle, uint16_t min_interval, uint16_t max_interval, uint16_t latency, uint16_t supervision_timeout, int to) { evt_le_connection_update_complete evt; le_connection_update_cp cp; struct hci_request rq; memset(&cp, 0, sizeof(cp)); cp.handle = handle; cp.min_interval = min_interval; cp.max_interval = max_interval; cp.latency = latency; cp.supervision_timeout = supervision_timeout; cp.min_ce_length = htobs(0x0001); cp.max_ce_length = htobs(0x0001); memset(&rq, 0, sizeof(rq)); rq.ogf = OGF_LE_CTL; rq.ocf = OCF_LE_CONN_UPDATE; rq.cparam = &cp; rq.clen = LE_CONN_UPDATE_CP_SIZE; rq.event = EVT_LE_CONN_UPDATE_COMPLETE; rq.rparam = &evt; rq.rlen = sizeof(evt); if (hci_send_req(dd, &rq, to) < 0) return -1; if (evt.status) { errno = EIO; return -1; } return 0; } int hci_le_read_remote_features(int dd, uint16_t handle, uint8_t *features, int to) { evt_le_read_remote_used_features_complete rp; le_read_remote_used_features_cp cp; struct hci_request rq; memset(&cp, 0, sizeof(cp)); cp.handle = handle; memset(&rq, 0, sizeof(rq)); rq.ogf = OGF_LE_CTL; rq.ocf = OCF_LE_READ_REMOTE_USED_FEATURES; rq.event = EVT_LE_READ_REMOTE_USED_FEATURES_COMPLETE; rq.cparam = &cp; rq.clen = LE_READ_REMOTE_USED_FEATURES_CP_SIZE; rq.rparam = &rp; rq.rlen = EVT_LE_READ_REMOTE_USED_FEATURES_COMPLETE_SIZE; if (hci_send_req(dd, &rq, to) < 0) return -1; if (rp.status) { errno = EIO; return -1; } if (features) memcpy(features, rp.features, 8); return 0; } bluez-5.82/lib/PaxHeaders/cmtp.h0000644000000000000000000000005014015011623013521 xustar0020 atime=1743516861 20 ctime=1743591275 bluez-5.82/lib/cmtp.h0000644000000000000000000000163014015011623013202 0ustar00rootroot/* SPDX-License-Identifier: GPL-2.0-or-later */ /* * * BlueZ - Bluetooth protocol stack for Linux * * Copyright (C) 2002-2010 Marcel Holtmann * * */ #ifndef __CMTP_H #define __CMTP_H #ifdef __cplusplus extern "C" { #endif /* CMTP defaults */ #define CMTP_MINIMUM_MTU 152 #define CMTP_DEFAULT_MTU 672 /* CMTP ioctl defines */ #define CMTPCONNADD _IOW('C', 200, int) #define CMTPCONNDEL _IOW('C', 201, int) #define CMTPGETCONNLIST _IOR('C', 210, int) #define CMTPGETCONNINFO _IOR('C', 211, int) #define CMTP_LOOPBACK 0 struct cmtp_connadd_req { int sock; /* Connected socket */ uint32_t flags; }; struct cmtp_conndel_req { bdaddr_t bdaddr; uint32_t flags; }; struct cmtp_conninfo { bdaddr_t bdaddr; uint32_t flags; uint16_t state; int num; }; struct cmtp_connlist_req { uint32_t cnum; struct cmtp_conninfo *ci; }; #ifdef __cplusplus } #endif #endif /* __CMTP_H */ bluez-5.82/lib/PaxHeaders/sdp_lib.h0000644000000000000000000000005014536422313014204 xustar0020 atime=1743515801 20 ctime=1743591275 bluez-5.82/lib/sdp_lib.h0000644000000000000000000005120114536422313013664 0ustar00rootroot/* SPDX-License-Identifier: GPL-2.0-or-later */ /* * * BlueZ - Bluetooth protocol stack for Linux * * Copyright (C) 2001-2002 Nokia Corporation * Copyright (C) 2002-2003 Maxim Krasnyansky * Copyright (C) 2002-2010 Marcel Holtmann * Copyright (C) 2002-2003 Stephen Crane * * */ #ifndef __SDP_LIB_H #define __SDP_LIB_H #include #include #include #ifdef __cplusplus extern "C" { #endif /* * SDP lists */ typedef void(*sdp_list_func_t)(void *, void *); typedef void(*sdp_free_func_t)(void *); typedef int (*sdp_comp_func_t)(const void *, const void *); sdp_list_t *sdp_list_append(sdp_list_t *list, void *d); sdp_list_t *sdp_list_remove(sdp_list_t *list, void *d); sdp_list_t *sdp_list_insert_sorted(sdp_list_t *list, void *data, sdp_comp_func_t f); void sdp_list_free(sdp_list_t *list, sdp_free_func_t f); static inline int sdp_list_len(const sdp_list_t *list) { int n = 0; for (; list; list = list->next) n++; return n; } static inline sdp_list_t *sdp_list_find(sdp_list_t *list, void *u, sdp_comp_func_t f) { for (; list; list = list->next) if (f(list->data, u) == 0) return list; return NULL; } static inline void sdp_list_foreach(sdp_list_t *list, sdp_list_func_t f, void *u) { for (; list; list = list->next) f(list->data, u); } /* * Values of the flags parameter to sdp_record_register */ #define SDP_RECORD_PERSIST 0x01 #define SDP_DEVICE_RECORD 0x02 /* * Values of the flags parameter to sdp_connect */ #define SDP_RETRY_IF_BUSY 0x01 #define SDP_WAIT_ON_CLOSE 0x02 #define SDP_NON_BLOCKING 0x04 #define SDP_LARGE_MTU 0x08 /* * a session with an SDP server */ typedef struct { int sock; int state; int local; int flags; uint16_t tid; /* Current transaction ID */ void *priv; } sdp_session_t; typedef enum { /* * Attributes are specified as individual elements */ SDP_ATTR_REQ_INDIVIDUAL = 1, /* * Attributes are specified as a range */ SDP_ATTR_REQ_RANGE } sdp_attrreq_type_t; /* * When the pdu_id(type) is a sdp error response, check the status value * to figure out the error reason. For status values 0x0001-0x0006 check * Bluetooth SPEC. If the status is 0xffff, call sdp_get_error function * to get the real reason: * - wrong transaction ID(EPROTO) * - wrong PDU id or(EPROTO) * - I/O error */ typedef void sdp_callback_t(uint8_t type, uint16_t status, uint8_t *rsp, size_t size, void *udata); /* * create an L2CAP connection to a Bluetooth device * * INPUT: * * bdaddr_t *src: * Address of the local device to use to make the connection * (or BDADDR_ANY) * * bdaddr_t *dst: * Address of the SDP server device */ sdp_session_t *sdp_connect(const bdaddr_t *src, const bdaddr_t *dst, uint32_t flags); int sdp_close(sdp_session_t *session); int sdp_get_socket(const sdp_session_t *session); /* * SDP transaction: functions for asynchronous search. */ sdp_session_t *sdp_create(int sk, uint32_t flags); int sdp_get_error(sdp_session_t *session); int sdp_process(sdp_session_t *session); int sdp_set_notify(sdp_session_t *session, sdp_callback_t *func, void *udata); int sdp_service_search_async(sdp_session_t *session, const sdp_list_t *search, uint16_t max_rec_num); int sdp_service_attr_async(sdp_session_t *session, uint32_t handle, sdp_attrreq_type_t reqtype, const sdp_list_t *attrid_list); int sdp_service_search_attr_async(sdp_session_t *session, const sdp_list_t *search, sdp_attrreq_type_t reqtype, const sdp_list_t *attrid_list); uint16_t sdp_gen_tid(sdp_session_t *session); /* * find all devices in the piconet */ int sdp_general_inquiry(inquiry_info *ii, int dev_num, int duration, uint8_t *found); /* flexible extraction of basic attributes - Jean II */ int sdp_get_int_attr(const sdp_record_t *rec, uint16_t attr, int *value); int sdp_get_string_attr(const sdp_record_t *rec, uint16_t attr, char *value, size_t valuelen); /* * Basic sdp data functions */ sdp_data_t *sdp_data_alloc(uint8_t dtd, const void *value); sdp_data_t *sdp_data_alloc_with_length(uint8_t dtd, const void *value, uint32_t length); void sdp_data_free(sdp_data_t *data); sdp_data_t *sdp_data_get(const sdp_record_t *rec, uint16_t attr_id); sdp_data_t *sdp_seq_alloc(void **dtds, void **values, int len); sdp_data_t *sdp_seq_alloc_with_length(void **dtds, void **values, int *length, int len); sdp_data_t *sdp_seq_append(sdp_data_t *seq, sdp_data_t *data); int sdp_attr_add(sdp_record_t *rec, uint16_t attr, sdp_data_t *data); void sdp_attr_remove(sdp_record_t *rec, uint16_t attr); void sdp_attr_replace(sdp_record_t *rec, uint16_t attr, sdp_data_t *data); int sdp_set_uuidseq_attr(sdp_record_t *rec, uint16_t attr, sdp_list_t *seq); int sdp_get_uuidseq_attr(const sdp_record_t *rec, uint16_t attr, sdp_list_t **seqp); /* * NOTE that none of the functions below will update the SDP server, * unless the {register, update}sdp_record_t() function is invoked. * All functions which return an integer value, return 0 on success * or -1 on failure. */ /* * Create an attribute and add it to the service record's attribute list. * This consists of the data type descriptor of the attribute, * the value of the attribute and the attribute identifier. */ int sdp_attr_add_new(sdp_record_t *rec, uint16_t attr, uint8_t dtd, const void *p); /* * Set the information attributes of the service record. * The set of attributes comprises service name, description * and provider name */ void sdp_set_info_attr(sdp_record_t *rec, const char *name, const char *prov, const char *desc); /* * Set the ServiceClassID attribute to the sequence specified by seq. * Note that the identifiers need to be in sorted order from the most * specific to the most generic service class that this service * conforms to. */ static inline int sdp_set_service_classes(sdp_record_t *rec, sdp_list_t *seq) { return sdp_set_uuidseq_attr(rec, SDP_ATTR_SVCLASS_ID_LIST, seq); } /* * Get the service classes to which the service conforms. * * When set, the list contains elements of ServiceClassIdentifer(uint16_t) * ordered from most specific to most generic */ static inline int sdp_get_service_classes(const sdp_record_t *rec, sdp_list_t **seqp) { return sdp_get_uuidseq_attr(rec, SDP_ATTR_SVCLASS_ID_LIST, seqp); } /* * Set the BrowseGroupList attribute to the list specified by seq. * * A service can belong to one or more service groups * and the list comprises such group identifiers (UUIDs) */ static inline int sdp_set_browse_groups(sdp_record_t *rec, sdp_list_t *seq) { return sdp_set_uuidseq_attr(rec, SDP_ATTR_BROWSE_GRP_LIST, seq); } /* * Set the access protocols of the record to those specified in proto */ int sdp_set_access_protos(sdp_record_t *rec, const sdp_list_t *proto); /* * Set the additional access protocols of the record to those specified in proto */ int sdp_set_add_access_protos(sdp_record_t *rec, const sdp_list_t *proto); /* * Get protocol port (i.e. PSM for L2CAP, Channel for RFCOMM) */ int sdp_get_proto_port(const sdp_list_t *list, int proto); /* * Get protocol descriptor. */ sdp_data_t *sdp_get_proto_desc(sdp_list_t *list, int proto); /* * Set the LanguageBase attributes to the values specified in list * (a linked list of sdp_lang_attr_t objects, one for each language in * which user-visible attributes are present). */ int sdp_set_lang_attr(sdp_record_t *rec, const sdp_list_t *list); /* * Set the ServiceInfoTimeToLive attribute of the service. * This is the number of seconds that this record is guaranteed * not to change after being obtained by a client. */ static inline int sdp_set_service_ttl(sdp_record_t *rec, uint32_t ttl) { return sdp_attr_add_new(rec, SDP_ATTR_SVCINFO_TTL, SDP_UINT32, &ttl); } /* * Set the ServiceRecordState attribute of a service. This is * guaranteed to change if there is any kind of modification to * the record. */ static inline int sdp_set_record_state(sdp_record_t *rec, uint32_t state) { return sdp_attr_add_new(rec, SDP_ATTR_RECORD_STATE, SDP_UINT32, &state); } /* * Set the ServiceID attribute of a service. */ void sdp_set_service_id(sdp_record_t *rec, uuid_t uuid); /* * Set the GroupID attribute of a service */ void sdp_set_group_id(sdp_record_t *rec, uuid_t grouuuid); /* * Set the ServiceAvailability attribute of a service. * * Note that this represents the relative availability * of the service: 0x00 means completely unavailable; * 0xFF means maximum availability. */ static inline int sdp_set_service_avail(sdp_record_t *rec, uint8_t avail) { return sdp_attr_add_new(rec, SDP_ATTR_SERVICE_AVAILABILITY, SDP_UINT8, &avail); } /* * Set the profile descriptor list attribute of a record. * * Each element in the list is an object of type * sdp_profile_desc_t which is a definition of the * Bluetooth profile that this service conforms to. */ int sdp_set_profile_descs(sdp_record_t *rec, const sdp_list_t *desc); /* * Set URL attributes of a record. * * ClientExecutableURL: a URL to a client's platform specific (WinCE, * PalmOS) executable code that can be used to access this service. * * DocumentationURL: a URL pointing to service documentation * * IconURL: a URL to an icon that can be used to represent this service. * * Note: pass NULL for any URLs that you don't want to set or remove */ void sdp_set_url_attr(sdp_record_t *rec, const char *clientExecURL, const char *docURL, const char *iconURL); /* * a service search request. * * INPUT : * * sdp_list_t *search * list containing elements of the search * pattern. Each entry in the list is a UUID * of the service to be searched * * uint16_t max_rec_num * An integer specifying the maximum number of * entries that the client can handle in the response. * * OUTPUT : * * int return value * 0 * The request completed successfully. This does not * mean the requested services were found * -1 * The request completed unsuccessfully * * sdp_list_t *rsp_list * This variable is set on a successful return if there are * non-zero service handles. It is a singly linked list of * service record handles (uint16_t) */ int sdp_service_search_req(sdp_session_t *session, const sdp_list_t *search, uint16_t max_rec_num, sdp_list_t **rsp_list); /* * a service attribute request. * * INPUT : * * uint32_t handle * The handle of the service for which the attribute(s) are * requested * * sdp_attrreq_type_t reqtype * Attribute identifiers are 16 bit unsigned integers specified * in one of 2 ways described below : * SDP_ATTR_REQ_INDIVIDUAL - 16bit individual identifiers * They are the actual attribute identifiers in ascending order * * SDP_ATTR_REQ_RANGE - 32bit identifier range * The high-order 16bits is the start of range * the low-order 16bits are the end of range * 0x0000 to 0xFFFF gets all attributes * * sdp_list_t *attrid_list * Singly linked list containing attribute identifiers desired. * Every element is either a uint16_t(attrSpec = SDP_ATTR_REQ_INDIVIDUAL) * or a uint32_t(attrSpec=SDP_ATTR_REQ_RANGE) * * OUTPUT : * int return value * 0 * The request completed successfully. This does not * mean the requested services were found * -1 * The request completed unsuccessfully due to a timeout */ sdp_record_t *sdp_service_attr_req(sdp_session_t *session, uint32_t handle, sdp_attrreq_type_t reqtype, const sdp_list_t *attrid_list); /* * This is a service search request combined with the service * attribute request. First a service class match is done and * for matching service, requested attributes are extracted * * INPUT : * * sdp_list_t *search * Singly linked list containing elements of the search * pattern. Each entry in the list is a UUID(DataTypeSDP_UUID16) * of the service to be searched * * AttributeSpecification attrSpec * Attribute identifiers are 16 bit unsigned integers specified * in one of 2 ways described below : * SDP_ATTR_REQ_INDIVIDUAL - 16bit individual identifiers * They are the actual attribute identifiers in ascending order * * SDP_ATTR_REQ_RANGE - 32bit identifier range * The high-order 16bits is the start of range * the low-order 16bits are the end of range * 0x0000 to 0xFFFF gets all attributes * * sdp_list_t *attrid_list * Singly linked list containing attribute identifiers desired. * Every element is either a uint16_t(attrSpec = SDP_ATTR_REQ_INDIVIDUAL) * or a uint32_t(attrSpec=SDP_ATTR_REQ_RANGE) * * OUTPUT : * int return value * 0 * The request completed successfully. This does not * mean the requested services were found * -1 * The request completed unsuccessfully due to a timeout * * sdp_list_t *rsp_list * This variable is set on a successful return to point to * service(s) found. Each element of this list is of type * sdp_record_t *. */ int sdp_service_search_attr_req(sdp_session_t *session, const sdp_list_t *search, sdp_attrreq_type_t reqtype, const sdp_list_t *attrid_list, sdp_list_t **rsp_list); /* * Allocate/free a service record and its attributes */ sdp_record_t *sdp_record_alloc(void); void sdp_record_free(sdp_record_t *rec); /* * Register a service record. * * Note: It is the responsbility of the Service Provider to create the * record first and set its attributes using setXXX() methods. * * The service provider must then call sdp_record_register() to make * the service record visible to SDP clients. This function returns 0 * on success or -1 on failure (and sets errno). */ int sdp_device_record_register_binary(sdp_session_t *session, bdaddr_t *device, uint8_t *data, uint32_t size, uint8_t flags, uint32_t *handle); int sdp_device_record_register(sdp_session_t *session, bdaddr_t *device, sdp_record_t *rec, uint8_t flags); int sdp_record_register(sdp_session_t *session, sdp_record_t *rec, uint8_t flags); /* * Unregister a service record. */ int sdp_device_record_unregister_binary(sdp_session_t *session, bdaddr_t *device, uint32_t handle); int sdp_device_record_unregister(sdp_session_t *session, bdaddr_t *device, sdp_record_t *rec); int sdp_record_unregister(sdp_session_t *session, sdp_record_t *rec); /* * Update an existing service record. (Calling this function * before a previous call to sdp_record_register() will result * in an error.) */ int sdp_device_record_update_binary(sdp_session_t *session, bdaddr_t *device, uint32_t handle, uint8_t *data, uint32_t size); int sdp_device_record_update(sdp_session_t *session, bdaddr_t *device, const sdp_record_t *rec); int sdp_record_update(sdp_session_t *sess, const sdp_record_t *rec); void sdp_record_print(const sdp_record_t *rec); /* * UUID functions */ uuid_t *sdp_uuid16_create(uuid_t *uuid, uint16_t data); uuid_t *sdp_uuid32_create(uuid_t *uuid, uint32_t data); uuid_t *sdp_uuid128_create(uuid_t *uuid, const void *data); int sdp_uuid16_cmp(const void *p1, const void *p2); int sdp_uuid128_cmp(const void *p1, const void *p2); int sdp_uuid_cmp(const void *p1, const void *p2); uuid_t *sdp_uuid_to_uuid128(const uuid_t *uuid); void sdp_uuid16_to_uuid128(uuid_t *uuid128, const uuid_t *uuid16); void sdp_uuid32_to_uuid128(uuid_t *uuid128, const uuid_t *uuid32); int sdp_uuid128_to_uuid(uuid_t *uuid); int sdp_uuid_to_proto(uuid_t *uuid); int sdp_uuid_extract(const uint8_t *buffer, int bufsize, uuid_t *uuid, int *scanned); void sdp_uuid_print(const uuid_t *uuid); #define MAX_LEN_UUID_STR 37 #define MAX_LEN_PROTOCOL_UUID_STR 8 #define MAX_LEN_SERVICECLASS_UUID_STR 28 #define MAX_LEN_PROFILEDESCRIPTOR_UUID_STR 28 int sdp_uuid2strn(const uuid_t *uuid, char *str, size_t n); int sdp_proto_uuid2strn(const uuid_t *uuid, char *str, size_t n); int sdp_svclass_uuid2strn(const uuid_t *uuid, char *str, size_t n); int sdp_profile_uuid2strn(const uuid_t *uuid, char *str, size_t n); /* * In all the sdp_get_XXX(handle, XXX *xxx) functions below, * the XXX * is set to point to the value, should it exist * and 0 is returned. If the value does not exist, -1 is * returned and errno set to ENODATA. * * In all the methods below, the memory management rules are * simple. Don't free anything! The pointer returned, in the * case of constructed types, is a pointer to the contents * of the sdp_record_t. */ /* * Get the access protocols from the service record */ int sdp_get_access_protos(const sdp_record_t *rec, sdp_list_t **protos); /* * Get the additional access protocols from the service record */ int sdp_get_add_access_protos(const sdp_record_t *rec, sdp_list_t **protos); /* * Extract the list of browse groups to which the service belongs. * When set, seqp contains elements of GroupID (uint16_t) */ static inline int sdp_get_browse_groups(const sdp_record_t *rec, sdp_list_t **seqp) { return sdp_get_uuidseq_attr(rec, SDP_ATTR_BROWSE_GRP_LIST, seqp); } /* * Extract language attribute meta-data of the service record. * For each language in the service record, LangSeq has a struct of type * sdp_lang_attr_t. */ int sdp_get_lang_attr(const sdp_record_t *rec, sdp_list_t **langSeq); /* * Extract the Bluetooth profile descriptor sequence from a record. * Each element in the list is of type sdp_profile_desc_t * which contains the UUID of the profile and its version number * (encoded as major and minor in the high-order 8bits * and low-order 8bits respectively of the uint16_t) */ int sdp_get_profile_descs(const sdp_record_t *rec, sdp_list_t **profDesc); /* * Extract SDP server version numbers * * Note: that this is an attribute of the SDP server only and * contains a list of uint16_t each of which represent the * major and minor SDP version numbers supported by this server */ int sdp_get_server_ver(const sdp_record_t *rec, sdp_list_t **pVnumList); int sdp_get_service_id(const sdp_record_t *rec, uuid_t *uuid); int sdp_get_group_id(const sdp_record_t *rec, uuid_t *uuid); int sdp_get_record_state(const sdp_record_t *rec, uint32_t *svcRecState); int sdp_get_service_avail(const sdp_record_t *rec, uint8_t *svcAvail); int sdp_get_service_ttl(const sdp_record_t *rec, uint32_t *svcTTLInfo); int sdp_get_database_state(const sdp_record_t *rec, uint32_t *svcDBState); static inline int sdp_get_service_name(const sdp_record_t *rec, char *str, size_t len) { return sdp_get_string_attr(rec, SDP_ATTR_SVCNAME_PRIMARY, str, len); } static inline int sdp_get_service_desc(const sdp_record_t *rec, char *str, size_t len) { return sdp_get_string_attr(rec, SDP_ATTR_SVCDESC_PRIMARY, str, len); } static inline int sdp_get_provider_name(const sdp_record_t *rec, char *str, size_t len) { return sdp_get_string_attr(rec, SDP_ATTR_PROVNAME_PRIMARY, str, len); } static inline int sdp_get_doc_url(const sdp_record_t *rec, char *str, size_t len) { return sdp_get_string_attr(rec, SDP_ATTR_DOC_URL, str, len); } static inline int sdp_get_clnt_exec_url(const sdp_record_t *rec, char *str, size_t len) { return sdp_get_string_attr(rec, SDP_ATTR_CLNT_EXEC_URL, str, len); } static inline int sdp_get_icon_url(const sdp_record_t *rec, char *str, size_t len) { return sdp_get_string_attr(rec, SDP_ATTR_ICON_URL, str, len); } /* * Set the supported features * sf should be a list of list with each feature data * Returns 0 on success -1 on fail */ int sdp_set_supp_feat(sdp_record_t *rec, const sdp_list_t *sf); /* * Get the supported features * seqp is set to a list of list with each feature data * Returns 0 on success, if an error occurred -1 is returned and errno is set */ int sdp_get_supp_feat(const sdp_record_t *rec, sdp_list_t **seqp); sdp_record_t *sdp_extract_pdu(const uint8_t *pdata, int bufsize, int *scanned); sdp_record_t *sdp_copy_record(sdp_record_t *rec); void sdp_data_print(sdp_data_t *data); void sdp_print_service_attr(sdp_list_t *alist); int sdp_attrid_comp_func(const void *key1, const void *key2); void sdp_set_seq_len(uint8_t *ptr, uint32_t length); void sdp_set_attrid(sdp_buf_t *pdu, uint16_t id); void sdp_append_to_pdu(sdp_buf_t *dst, sdp_data_t *d); void sdp_append_to_buf(sdp_buf_t *dst, uint8_t *data, uint32_t len); int sdp_gen_pdu(sdp_buf_t *pdu, sdp_data_t *data); int sdp_gen_record_pdu(const sdp_record_t *rec, sdp_buf_t *pdu); int sdp_extract_seqtype(const uint8_t *buf, int bufsize, uint8_t *dtdp, int *size); sdp_data_t *sdp_extract_attr(const uint8_t *pdata, int bufsize, int *extractedLength, sdp_record_t *rec); void sdp_pattern_add_uuid(sdp_record_t *rec, uuid_t *uuid); void sdp_pattern_add_uuidseq(sdp_record_t *rec, sdp_list_t *seq); int sdp_send_req_w4_rsp(sdp_session_t *session, uint8_t *req, uint8_t *rsp, uint32_t reqsize, uint32_t *rspsize); void sdp_add_lang_attr(sdp_record_t *rec); #ifdef __cplusplus } #endif #endif /* __SDP_LIB_H */ bluez-5.82/lib/PaxHeaders/sdp.c0000644000000000000000000000005014643061455013356 xustar0020 atime=1743515834 20 ctime=1743591277 bluez-5.82/lib/sdp.c0000644000000000000000000034052014643061455013043 0ustar00rootroot// SPDX-License-Identifier: GPL-2.0-or-later /* * * BlueZ - Bluetooth protocol stack for Linux * * Copyright (C) 2001-2002 Nokia Corporation * Copyright (C) 2002-2003 Maxim Krasnyansky * Copyright (C) 2002-2010 Marcel Holtmann * Copyright (C) 2002-2003 Stephen Crane * * */ #ifdef HAVE_CONFIG_H #include #endif #include #include #include #include #include #include #include #include #include #include #include #include #include #include "bluetooth.h" #include "hci.h" #include "hci_lib.h" #include "l2cap.h" #include "sdp.h" #include "sdp_lib.h" #define SDPINF(fmt, arg...) syslog(LOG_INFO, fmt "\n", ## arg) #define SDPERR(fmt, arg...) syslog(LOG_ERR, "%s: " fmt "\n", __func__ , ## arg) #define ARRAY_SIZE(arr) (sizeof(arr) / sizeof((arr)[0])) #ifdef SDP_DEBUG #define SDPDBG(fmt, arg...) syslog(LOG_DEBUG, "%s: " fmt "\n", __func__ , ## arg) #else #define SDPDBG(fmt...) #endif static const uint128_t bluetooth_base_uuid = { .data = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x80, 0x00, 0x00, 0x80, 0x5F, 0x9B, 0x34, 0xFB } }; #define SDP_MAX_ATTR_LEN 65535 /* match MTU used by RFCOMM */ #define SDP_LARGE_L2CAP_MTU 1013 static sdp_data_t *sdp_copy_seq(sdp_data_t *data); static int sdp_attr_add_new_with_length(sdp_record_t *rec, uint16_t attr, uint8_t dtd, const void *value, uint32_t len); static int sdp_gen_buffer(sdp_buf_t *buf, sdp_data_t *d); /* Message structure. */ struct tupla { int index; const char *str; }; static const struct tupla Protocol[] = { { SDP_UUID, "SDP" }, { UDP_UUID, "UDP" }, { RFCOMM_UUID, "RFCOMM" }, { TCP_UUID, "TCP" }, { TCS_BIN_UUID, "TCS-BIN" }, { TCS_AT_UUID, "TCS-AT" }, { OBEX_UUID, "OBEX" }, { IP_UUID, "IP" }, { FTP_UUID, "FTP" }, { HTTP_UUID, "HTTP" }, { WSP_UUID, "WSP" }, { BNEP_UUID, "BNEP" }, { UPNP_UUID, "UPNP" }, { HIDP_UUID, "HIDP" }, { HCRP_CTRL_UUID, "HCRP-Ctrl" }, { HCRP_DATA_UUID, "HCRP-Data" }, { HCRP_NOTE_UUID, "HCRP-Notify" }, { AVCTP_UUID, "AVCTP" }, { AVDTP_UUID, "AVDTP" }, { CMTP_UUID, "CMTP" }, { UDI_UUID, "UDI" }, { MCAP_CTRL_UUID, "MCAP-Ctrl" }, { MCAP_DATA_UUID, "MCAP-Data" }, { L2CAP_UUID, "L2CAP" }, { ATT_UUID, "ATT" }, { 0 } }; static const struct tupla ServiceClass[] = { { SDP_SERVER_SVCLASS_ID, "SDP Server" }, { BROWSE_GRP_DESC_SVCLASS_ID, "Browse Group Descriptor" }, { PUBLIC_BROWSE_GROUP, "Public Browse Group" }, { SERIAL_PORT_SVCLASS_ID, "Serial Port" }, { LAN_ACCESS_SVCLASS_ID, "LAN Access Using PPP" }, { DIALUP_NET_SVCLASS_ID, "Dialup Networking" }, { IRMC_SYNC_SVCLASS_ID, "IrMC Sync" }, { OBEX_OBJPUSH_SVCLASS_ID, "OBEX Object Push" }, { OBEX_FILETRANS_SVCLASS_ID, "OBEX File Transfer" }, { IRMC_SYNC_CMD_SVCLASS_ID, "IrMC Sync Command" }, { HEADSET_SVCLASS_ID, "Headset" }, { CORDLESS_TELEPHONY_SVCLASS_ID, "Cordless Telephony" }, { AUDIO_SOURCE_SVCLASS_ID, "Audio Source" }, { AUDIO_SINK_SVCLASS_ID, "Audio Sink" }, { AV_REMOTE_TARGET_SVCLASS_ID, "AV Remote Target" }, { ADVANCED_AUDIO_SVCLASS_ID, "Advanced Audio" }, { AV_REMOTE_SVCLASS_ID, "AV Remote" }, { AV_REMOTE_CONTROLLER_SVCLASS_ID, "AV Remote Controller" }, { INTERCOM_SVCLASS_ID, "Intercom" }, { FAX_SVCLASS_ID, "Fax" }, { HEADSET_AGW_SVCLASS_ID, "Headset Audio Gateway" }, { WAP_SVCLASS_ID, "WAP" }, { WAP_CLIENT_SVCLASS_ID, "WAP Client" }, { PANU_SVCLASS_ID, "PAN User" }, { NAP_SVCLASS_ID, "Network Access Point" }, { GN_SVCLASS_ID, "PAN Group Network" }, { DIRECT_PRINTING_SVCLASS_ID, "Direct Printing" }, { REFERENCE_PRINTING_SVCLASS_ID, "Reference Printing" }, { IMAGING_SVCLASS_ID, "Imaging" }, { IMAGING_RESPONDER_SVCLASS_ID, "Imaging Responder" }, { IMAGING_ARCHIVE_SVCLASS_ID, "Imaging Automatic Archive" }, { IMAGING_REFOBJS_SVCLASS_ID, "Imaging Referenced Objects" }, { HANDSFREE_SVCLASS_ID, "Handsfree" }, { HANDSFREE_AGW_SVCLASS_ID, "Handsfree Audio Gateway" }, { DIRECT_PRT_REFOBJS_SVCLASS_ID, "Direct Printing Ref. Objects" }, { REFLECTED_UI_SVCLASS_ID, "Reflected UI" }, { BASIC_PRINTING_SVCLASS_ID, "Basic Printing" }, { PRINTING_STATUS_SVCLASS_ID, "Printing Status" }, { HID_SVCLASS_ID, "Human Interface Device" }, { HCR_SVCLASS_ID, "Hardcopy Cable Replacement" }, { HCR_PRINT_SVCLASS_ID, "HCR Print" }, { HCR_SCAN_SVCLASS_ID, "HCR Scan" }, { CIP_SVCLASS_ID, "Common ISDN Access" }, { VIDEO_CONF_GW_SVCLASS_ID, "Video Conferencing Gateway" }, { UDI_MT_SVCLASS_ID, "UDI MT" }, { UDI_TA_SVCLASS_ID, "UDI TA" }, { AV_SVCLASS_ID, "Audio/Video" }, { SAP_SVCLASS_ID, "SIM Access" }, { PBAP_PCE_SVCLASS_ID, "Phonebook Access - PCE" }, { PBAP_PSE_SVCLASS_ID, "Phonebook Access - PSE" }, { PBAP_SVCLASS_ID, "Phonebook Access" }, { MAP_MSE_SVCLASS_ID, "Message Access - MAS" }, { MAP_MCE_SVCLASS_ID, "Message Access - MNS" }, { MAP_SVCLASS_ID, "Message Access" }, { PNP_INFO_SVCLASS_ID, "PnP Information" }, { GENERIC_NETWORKING_SVCLASS_ID, "Generic Networking" }, { GENERIC_FILETRANS_SVCLASS_ID, "Generic File Transfer" }, { GENERIC_AUDIO_SVCLASS_ID, "Generic Audio" }, { GENERIC_TELEPHONY_SVCLASS_ID, "Generic Telephony" }, { UPNP_SVCLASS_ID, "UPnP" }, { UPNP_IP_SVCLASS_ID, "UPnP IP" }, { UPNP_PAN_SVCLASS_ID, "UPnP PAN" }, { UPNP_LAP_SVCLASS_ID, "UPnP LAP" }, { UPNP_L2CAP_SVCLASS_ID, "UPnP L2CAP" }, { VIDEO_SOURCE_SVCLASS_ID, "Video Source" }, { VIDEO_SINK_SVCLASS_ID, "Video Sink" }, { VIDEO_DISTRIBUTION_SVCLASS_ID, "Video Distribution" }, { HDP_SVCLASS_ID, "HDP" }, { HDP_SOURCE_SVCLASS_ID, "HDP Source" }, { HDP_SINK_SVCLASS_ID, "HDP Sink" }, { GENERIC_ACCESS_SVCLASS_ID, "Generic Access" }, { GENERIC_ATTRIB_SVCLASS_ID, "Generic Attribute" }, { APPLE_AGENT_SVCLASS_ID, "Apple Agent" }, { 0 } }; #define Profile ServiceClass static const char *string_lookup(const struct tupla *pt0, int index) { const struct tupla *pt; for (pt = pt0; pt->index; pt++) if (pt->index == index) return pt->str; return ""; } static const char *string_lookup_uuid(const struct tupla *pt0, const uuid_t *uuid) { uuid_t tmp_uuid; memcpy(&tmp_uuid, uuid, sizeof(tmp_uuid)); if (sdp_uuid128_to_uuid(&tmp_uuid)) { switch (tmp_uuid.type) { case SDP_UUID16: return string_lookup(pt0, tmp_uuid.value.uuid16); case SDP_UUID32: return string_lookup(pt0, tmp_uuid.value.uuid32); } } return ""; } /* * Prints into a string the Protocol UUID * coping a maximum of n characters. */ static int uuid2str(const struct tupla *message, const uuid_t *uuid, char *str, size_t n) { const char *str2; if (!uuid) { snprintf(str, n, "NULL"); return -2; } switch (uuid->type) { case SDP_UUID16: str2 = string_lookup(message, uuid->value.uuid16); snprintf(str, n, "%s", str2); break; case SDP_UUID32: str2 = string_lookup(message, uuid->value.uuid32); snprintf(str, n, "%s", str2); break; case SDP_UUID128: str2 = string_lookup_uuid(message, uuid); snprintf(str, n, "%s", str2); break; default: snprintf(str, n, "Type of UUID (%x) unknown.", uuid->type); return -1; } return 0; } int sdp_proto_uuid2strn(const uuid_t *uuid, char *str, size_t n) { return uuid2str(Protocol, uuid, str, n); } int sdp_svclass_uuid2strn(const uuid_t *uuid, char *str, size_t n) { return uuid2str(ServiceClass, uuid, str, n); } int sdp_profile_uuid2strn(const uuid_t *uuid, char *str, size_t n) { return uuid2str(Profile, uuid, str, n); } /* * convert the UUID to string, copying a maximum of n characters. */ int sdp_uuid2strn(const uuid_t *uuid, char *str, size_t n) { if (!uuid) { snprintf(str, n, "NULL"); return -2; } switch (uuid->type) { case SDP_UUID16: snprintf(str, n, "%.4x", uuid->value.uuid16); break; case SDP_UUID32: snprintf(str, n, "%.8x", uuid->value.uuid32); break; case SDP_UUID128:{ unsigned int data0; unsigned short data1; unsigned short data2; unsigned short data3; unsigned int data4; unsigned short data5; memcpy(&data0, &uuid->value.uuid128.data[0], 4); memcpy(&data1, &uuid->value.uuid128.data[4], 2); memcpy(&data2, &uuid->value.uuid128.data[6], 2); memcpy(&data3, &uuid->value.uuid128.data[8], 2); memcpy(&data4, &uuid->value.uuid128.data[10], 4); memcpy(&data5, &uuid->value.uuid128.data[14], 2); snprintf(str, n, "%.8x-%.4x-%.4x-%.4x-%.8x%.4x", ntohl(data0), ntohs(data1), ntohs(data2), ntohs(data3), ntohl(data4), ntohs(data5)); } break; default: snprintf(str, n, "Type of UUID (%x) unknown.", uuid->type); return -1; /* Enum type of UUID not set */ } return 0; } #ifdef SDP_DEBUG /* * Function prints the UUID in hex as per defined syntax - * * 4bytes-2bytes-2bytes-2bytes-6bytes * * There is some ugly code, including hardcoding, but * that is just the way it is converting 16 and 32 bit * UUIDs to 128 bit as defined in the SDP doc */ void sdp_uuid_print(const uuid_t *uuid) { if (uuid == NULL) { SDPERR("Null passed to print UUID"); return; } if (uuid->type == SDP_UUID16) { SDPDBG(" uint16_t : 0x%.4x", uuid->value.uuid16); } else if (uuid->type == SDP_UUID32) { SDPDBG(" uint32_t : 0x%.8x", uuid->value.uuid32); } else if (uuid->type == SDP_UUID128) { unsigned int data0; unsigned short data1; unsigned short data2; unsigned short data3; unsigned int data4; unsigned short data5; memcpy(&data0, &uuid->value.uuid128.data[0], 4); memcpy(&data1, &uuid->value.uuid128.data[4], 2); memcpy(&data2, &uuid->value.uuid128.data[6], 2); memcpy(&data3, &uuid->value.uuid128.data[8], 2); memcpy(&data4, &uuid->value.uuid128.data[10], 4); memcpy(&data5, &uuid->value.uuid128.data[14], 2); SDPDBG(" uint128_t : 0x%.8x-%.4x-%.4x-%.4x-%.8x%.4x", ntohl(data0), ntohs(data1), ntohs(data2), ntohs(data3), ntohl(data4), ntohs(data5)); } else SDPERR("Enum type of UUID not set"); } #endif sdp_data_t *sdp_data_alloc_with_length(uint8_t dtd, const void *value, uint32_t length) { sdp_data_t *seq; sdp_data_t *d = bt_malloc0(sizeof(sdp_data_t)); if (!d) return NULL; d->dtd = dtd; d->unitSize = sizeof(uint8_t); switch (dtd) { case SDP_DATA_NIL: break; case SDP_UINT8: d->val.uint8 = *(uint8_t *) value; d->unitSize += sizeof(uint8_t); break; case SDP_INT8: case SDP_BOOL: d->val.int8 = *(int8_t *) value; d->unitSize += sizeof(int8_t); break; case SDP_UINT16: d->val.uint16 = bt_get_unaligned((uint16_t *) value); d->unitSize += sizeof(uint16_t); break; case SDP_INT16: d->val.int16 = bt_get_unaligned((int16_t *) value); d->unitSize += sizeof(int16_t); break; case SDP_UINT32: d->val.uint32 = bt_get_unaligned((uint32_t *) value); d->unitSize += sizeof(uint32_t); break; case SDP_INT32: d->val.int32 = bt_get_unaligned((int32_t *) value); d->unitSize += sizeof(int32_t); break; case SDP_INT64: d->val.int64 = bt_get_unaligned((int64_t *) value); d->unitSize += sizeof(int64_t); break; case SDP_UINT64: d->val.uint64 = bt_get_unaligned((uint64_t *) value); d->unitSize += sizeof(uint64_t); break; case SDP_UINT128: memcpy(&d->val.uint128.data, value, sizeof(uint128_t)); d->unitSize += sizeof(uint128_t); break; case SDP_INT128: memcpy(&d->val.int128.data, value, sizeof(uint128_t)); d->unitSize += sizeof(uint128_t); break; case SDP_UUID16: sdp_uuid16_create(&d->val.uuid, bt_get_unaligned((uint16_t *) value)); d->unitSize += sizeof(uint16_t); break; case SDP_UUID32: sdp_uuid32_create(&d->val.uuid, bt_get_unaligned((uint32_t *) value)); d->unitSize += sizeof(uint32_t); break; case SDP_UUID128: sdp_uuid128_create(&d->val.uuid, value); d->unitSize += sizeof(uint128_t); break; case SDP_URL_STR8: case SDP_URL_STR16: case SDP_TEXT_STR8: case SDP_TEXT_STR16: if (!value) { free(d); return NULL; } d->unitSize += length; if (length <= USHRT_MAX) { d->val.str = bt_malloc0(length + 1); if (!d->val.str) { free(d); return NULL; } memcpy(d->val.str, value, length); } else { SDPERR("Strings of size > USHRT_MAX not supported"); free(d); d = NULL; } break; case SDP_URL_STR32: case SDP_TEXT_STR32: SDPERR("Strings of size > USHRT_MAX not supported"); break; case SDP_ALT8: case SDP_ALT16: case SDP_ALT32: case SDP_SEQ8: case SDP_SEQ16: case SDP_SEQ32: if (dtd == SDP_ALT8 || dtd == SDP_SEQ8) d->unitSize += sizeof(uint8_t); else if (dtd == SDP_ALT16 || dtd == SDP_SEQ16) d->unitSize += sizeof(uint16_t); else if (dtd == SDP_ALT32 || dtd == SDP_SEQ32) d->unitSize += sizeof(uint32_t); seq = (sdp_data_t *)value; d->val.dataseq = seq; for (; seq; seq = seq->next) d->unitSize += seq->unitSize; break; default: free(d); d = NULL; } return d; } sdp_data_t *sdp_data_alloc(uint8_t dtd, const void *value) { uint32_t length; switch (dtd) { case SDP_URL_STR8: case SDP_URL_STR16: case SDP_TEXT_STR8: case SDP_TEXT_STR16: if (!value) return NULL; length = strlen((char *) value); break; default: length = 0; break; } return sdp_data_alloc_with_length(dtd, value, length); } sdp_data_t *sdp_seq_append(sdp_data_t *seq, sdp_data_t *d) { if (seq) { sdp_data_t *p; for (p = seq; p->next; p = p->next); p->next = d; } else seq = d; d->next = NULL; return seq; } sdp_data_t *sdp_seq_alloc_with_length(void **dtds, void **values, int *length, int len) { sdp_data_t *curr = NULL, *seq = NULL; int i; for (i = 0; i < len; i++) { sdp_data_t *data; uint8_t dtd = *(uint8_t *) dtds[i]; if (dtd >= SDP_SEQ8 && dtd <= SDP_ALT32) data = (sdp_data_t *) values[i]; else data = sdp_data_alloc_with_length(dtd, values[i], length[i]); if (!data) { sdp_data_free(seq); return NULL; } if (curr) curr->next = data; else seq = data; curr = data; } return sdp_data_alloc(SDP_SEQ8, seq); } sdp_data_t *sdp_seq_alloc(void **dtds, void **values, int len) { sdp_data_t *curr = NULL, *seq = NULL; int i; for (i = 0; i < len; i++) { sdp_data_t *data; uint8_t dtd = *(uint8_t *) dtds[i]; if (dtd >= SDP_SEQ8 && dtd <= SDP_ALT32) data = (sdp_data_t *) values[i]; else data = sdp_data_alloc(dtd, values[i]); if (!data) { sdp_data_free(seq); return NULL; } if (curr) curr->next = data; else seq = data; curr = data; } return sdp_data_alloc(SDP_SEQ8, seq); } static void extract_svclass_uuid(sdp_data_t *data, uuid_t *uuid) { sdp_data_t *d; if (!data || !SDP_IS_SEQ(data->dtd)) return; d = data->val.dataseq; if (!d) return; if (d->dtd < SDP_UUID16 || d->dtd > SDP_UUID128) return; *uuid = d->val.uuid; } int sdp_attr_add(sdp_record_t *rec, uint16_t attr, sdp_data_t *d) { sdp_data_t *p = sdp_data_get(rec, attr); if (p) return -1; if (!d) return -1; d->attrId = attr; rec->attrlist = sdp_list_insert_sorted(rec->attrlist, d, sdp_attrid_comp_func); if (attr == SDP_ATTR_SVCLASS_ID_LIST) extract_svclass_uuid(d, &rec->svclass); return 0; } void sdp_attr_remove(sdp_record_t *rec, uint16_t attr) { sdp_data_t *d = sdp_data_get(rec, attr); if (d) rec->attrlist = sdp_list_remove(rec->attrlist, d); if (attr == SDP_ATTR_SVCLASS_ID_LIST) memset(&rec->svclass, 0, sizeof(rec->svclass)); } void sdp_set_seq_len(uint8_t *ptr, uint32_t length) { uint8_t dtd = *ptr++; switch (dtd) { case SDP_SEQ8: case SDP_ALT8: case SDP_TEXT_STR8: case SDP_URL_STR8: *ptr = (uint8_t) length; break; case SDP_SEQ16: case SDP_ALT16: case SDP_TEXT_STR16: case SDP_URL_STR16: bt_put_be16(length, ptr); break; case SDP_SEQ32: case SDP_ALT32: case SDP_TEXT_STR32: case SDP_URL_STR32: bt_put_be32(length, ptr); break; } } static int sdp_get_data_type_size(uint8_t dtd) { int size = sizeof(uint8_t); switch (dtd) { case SDP_SEQ8: case SDP_TEXT_STR8: case SDP_URL_STR8: case SDP_ALT8: size += sizeof(uint8_t); break; case SDP_SEQ16: case SDP_TEXT_STR16: case SDP_URL_STR16: case SDP_ALT16: size += sizeof(uint16_t); break; case SDP_SEQ32: case SDP_TEXT_STR32: case SDP_URL_STR32: case SDP_ALT32: size += sizeof(uint32_t); break; } return size; } void sdp_set_attrid(sdp_buf_t *buf, uint16_t attr) { uint8_t *p = buf->data; /* data type for attr */ *p++ = SDP_UINT16; buf->data_size = sizeof(uint8_t); bt_put_be16(attr, p); buf->data_size += sizeof(uint16_t); } static int get_data_size(sdp_buf_t *buf, sdp_data_t *sdpdata) { sdp_data_t *d; int n = 0; for (d = sdpdata->val.dataseq; d; d = d->next) { if (buf->data) n += sdp_gen_pdu(buf, d); else n += sdp_gen_buffer(buf, d); } return n; } static int sdp_get_data_size(sdp_buf_t *buf, sdp_data_t *d) { uint32_t data_size = 0; uint8_t dtd = d->dtd; switch (dtd) { case SDP_DATA_NIL: break; case SDP_UINT8: data_size = sizeof(uint8_t); break; case SDP_UINT16: data_size = sizeof(uint16_t); break; case SDP_UINT32: data_size = sizeof(uint32_t); break; case SDP_UINT64: data_size = sizeof(uint64_t); break; case SDP_UINT128: data_size = sizeof(uint128_t); break; case SDP_INT8: case SDP_BOOL: data_size = sizeof(int8_t); break; case SDP_INT16: data_size = sizeof(int16_t); break; case SDP_INT32: data_size = sizeof(int32_t); break; case SDP_INT64: data_size = sizeof(int64_t); break; case SDP_INT128: data_size = sizeof(uint128_t); break; case SDP_TEXT_STR8: case SDP_TEXT_STR16: case SDP_TEXT_STR32: case SDP_URL_STR8: case SDP_URL_STR16: case SDP_URL_STR32: data_size = d->unitSize - sizeof(uint8_t); break; case SDP_SEQ8: case SDP_SEQ16: case SDP_SEQ32: data_size = get_data_size(buf, d); break; case SDP_ALT8: case SDP_ALT16: case SDP_ALT32: data_size = get_data_size(buf, d); break; case SDP_UUID16: data_size = sizeof(uint16_t); break; case SDP_UUID32: data_size = sizeof(uint32_t); break; case SDP_UUID128: data_size = sizeof(uint128_t); break; default: break; } return data_size; } static int sdp_gen_buffer(sdp_buf_t *buf, sdp_data_t *d) { int orig = buf->buf_size; if (buf->buf_size == 0 && d->dtd == 0) { /* create initial sequence */ buf->buf_size += sizeof(uint8_t); /* reserve space for sequence size */ buf->buf_size += sizeof(uint8_t); } /* attribute length */ buf->buf_size += sizeof(uint8_t) + sizeof(uint16_t); buf->buf_size += sdp_get_data_type_size(d->dtd); buf->buf_size += sdp_get_data_size(buf, d); if (buf->buf_size > UCHAR_MAX && d->dtd == SDP_SEQ8) buf->buf_size += sizeof(uint8_t); return buf->buf_size - orig; } int sdp_gen_pdu(sdp_buf_t *buf, sdp_data_t *d) { uint32_t pdu_size, data_size; unsigned char *src = NULL, is_seq = 0, is_alt = 0; uint16_t u16; uint32_t u32; uint64_t u64; uint128_t u128; uint8_t *seqp = buf->data + buf->data_size; uint32_t orig_data_size = buf->data_size; recalculate: pdu_size = sdp_get_data_type_size(d->dtd); buf->data_size += pdu_size; data_size = sdp_get_data_size(buf, d); if (data_size > UCHAR_MAX && d->dtd == SDP_SEQ8) { buf->data_size = orig_data_size; d->dtd = SDP_SEQ16; goto recalculate; } *seqp = d->dtd; switch (d->dtd) { case SDP_DATA_NIL: break; case SDP_UINT8: src = &d->val.uint8; break; case SDP_UINT16: u16 = htons(d->val.uint16); src = (unsigned char *) &u16; break; case SDP_UINT32: u32 = htonl(d->val.uint32); src = (unsigned char *) &u32; break; case SDP_UINT64: u64 = hton64(d->val.uint64); src = (unsigned char *) &u64; break; case SDP_UINT128: hton128(&d->val.uint128, &u128); src = (unsigned char *) &u128; break; case SDP_INT8: case SDP_BOOL: src = (unsigned char *) &d->val.int8; break; case SDP_INT16: u16 = htons(d->val.int16); src = (unsigned char *) &u16; break; case SDP_INT32: u32 = htonl(d->val.int32); src = (unsigned char *) &u32; break; case SDP_INT64: u64 = hton64(d->val.int64); src = (unsigned char *) &u64; break; case SDP_INT128: hton128(&d->val.int128, &u128); src = (unsigned char *) &u128; break; case SDP_TEXT_STR8: case SDP_TEXT_STR16: case SDP_TEXT_STR32: case SDP_URL_STR8: case SDP_URL_STR16: case SDP_URL_STR32: src = (unsigned char *) d->val.str; sdp_set_seq_len(seqp, data_size); break; case SDP_SEQ8: case SDP_SEQ16: case SDP_SEQ32: is_seq = 1; sdp_set_seq_len(seqp, data_size); break; case SDP_ALT8: case SDP_ALT16: case SDP_ALT32: is_alt = 1; sdp_set_seq_len(seqp, data_size); break; case SDP_UUID16: u16 = htons(d->val.uuid.value.uuid16); src = (unsigned char *) &u16; break; case SDP_UUID32: u32 = htonl(d->val.uuid.value.uuid32); src = (unsigned char *) &u32; break; case SDP_UUID128: src = (unsigned char *) &d->val.uuid.value.uuid128; break; default: break; } if (!is_seq && !is_alt) { if (src && buf->buf_size >= buf->data_size + data_size) { memcpy(buf->data + buf->data_size, src, data_size); buf->data_size += data_size; } else if (d->dtd != SDP_DATA_NIL) { SDPDBG("Gen PDU : Can't copy from invalid source or dest"); } } pdu_size += data_size; return pdu_size; } static void sdp_attr_pdu(void *value, void *udata) { sdp_append_to_pdu((sdp_buf_t *)udata, (sdp_data_t *)value); } static void sdp_attr_size(void *value, void *udata) { sdp_gen_buffer((sdp_buf_t *)udata, (sdp_data_t *)value); } int sdp_gen_record_pdu(const sdp_record_t *rec, sdp_buf_t *buf) { memset(buf, 0, sizeof(sdp_buf_t)); sdp_list_foreach(rec->attrlist, sdp_attr_size, buf); buf->data = bt_malloc0(buf->buf_size); if (!buf->data) return -ENOMEM; buf->data_size = 0; sdp_list_foreach(rec->attrlist, sdp_attr_pdu, buf); return 0; } void sdp_attr_replace(sdp_record_t *rec, uint16_t attr, sdp_data_t *d) { sdp_data_t *p; if (!rec) return; p = sdp_data_get(rec, attr); if (p) { rec->attrlist = sdp_list_remove(rec->attrlist, p); sdp_data_free(p); } d->attrId = attr; rec->attrlist = sdp_list_insert_sorted(rec->attrlist, d, sdp_attrid_comp_func); if (attr == SDP_ATTR_SVCLASS_ID_LIST) extract_svclass_uuid(d, &rec->svclass); } int sdp_attrid_comp_func(const void *key1, const void *key2) { const sdp_data_t *d1 = (const sdp_data_t *)key1; const sdp_data_t *d2 = (const sdp_data_t *)key2; if (d1 && d2) return d1->attrId - d2->attrId; return 0; } static void data_seq_free(sdp_data_t *seq) { sdp_data_t *d = seq->val.dataseq; while (d) { sdp_data_t *next = d->next; sdp_data_free(d); d = next; } } void sdp_data_free(sdp_data_t *d) { if (!d) return; switch (d->dtd) { case SDP_SEQ8: case SDP_SEQ16: case SDP_SEQ32: data_seq_free(d); break; case SDP_URL_STR8: case SDP_URL_STR16: case SDP_URL_STR32: case SDP_TEXT_STR8: case SDP_TEXT_STR16: case SDP_TEXT_STR32: free(d->val.str); break; } free(d); } int sdp_uuid_extract(const uint8_t *p, int bufsize, uuid_t *uuid, int *scanned) { uint8_t type; if (bufsize < (int) sizeof(uint8_t)) { SDPERR("Unexpected end of packet"); return -1; } type = *(const uint8_t *) p; if (!SDP_IS_UUID(type)) { SDPERR("Unknown data type : %d expecting a svc UUID", type); return -1; } p += sizeof(uint8_t); *scanned += sizeof(uint8_t); bufsize -= sizeof(uint8_t); if (type == SDP_UUID16) { if (bufsize < (int) sizeof(uint16_t)) { SDPERR("Not enough room for 16-bit UUID"); return -1; } sdp_uuid16_create(uuid, bt_get_be16(p)); *scanned += sizeof(uint16_t); } else if (type == SDP_UUID32) { if (bufsize < (int) sizeof(uint32_t)) { SDPERR("Not enough room for 32-bit UUID"); return -1; } sdp_uuid32_create(uuid, bt_get_be32(p)); *scanned += sizeof(uint32_t); } else { if (bufsize < (int) sizeof(uint128_t)) { SDPERR("Not enough room for 128-bit UUID"); return -1; } sdp_uuid128_create(uuid, p); *scanned += sizeof(uint128_t); } return 0; } static sdp_data_t *extract_int(const void *p, int bufsize, int *len) { sdp_data_t *d; if (bufsize < (int) sizeof(uint8_t)) { SDPERR("Unexpected end of packet"); return NULL; } d = bt_malloc0(sizeof(sdp_data_t)); if (!d) return NULL; SDPDBG("Extracting integer"); d->dtd = *(uint8_t *) p; p += sizeof(uint8_t); *len += sizeof(uint8_t); bufsize -= sizeof(uint8_t); switch (d->dtd) { case SDP_DATA_NIL: break; case SDP_BOOL: case SDP_INT8: case SDP_UINT8: if (bufsize < (int) sizeof(uint8_t)) { SDPERR("Unexpected end of packet"); free(d); return NULL; } *len += sizeof(uint8_t); d->val.uint8 = *(uint8_t *) p; break; case SDP_INT16: case SDP_UINT16: if (bufsize < (int) sizeof(uint16_t)) { SDPERR("Unexpected end of packet"); free(d); return NULL; } *len += sizeof(uint16_t); d->val.uint16 = bt_get_be16(p); break; case SDP_INT32: case SDP_UINT32: if (bufsize < (int) sizeof(uint32_t)) { SDPERR("Unexpected end of packet"); free(d); return NULL; } *len += sizeof(uint32_t); d->val.uint32 = bt_get_be32(p); break; case SDP_INT64: case SDP_UINT64: if (bufsize < (int) sizeof(uint64_t)) { SDPERR("Unexpected end of packet"); free(d); return NULL; } *len += sizeof(uint64_t); d->val.uint64 = bt_get_be64(p); break; case SDP_INT128: case SDP_UINT128: if (bufsize < (int) sizeof(uint128_t)) { SDPERR("Unexpected end of packet"); free(d); return NULL; } *len += sizeof(uint128_t); ntoh128((uint128_t *) p, &d->val.uint128); break; default: free(d); d = NULL; } return d; } static sdp_data_t *extract_uuid(const uint8_t *p, int bufsize, int *len, sdp_record_t *rec) { sdp_data_t *d = bt_malloc0(sizeof(sdp_data_t)); if (!d) return NULL; SDPDBG("Extracting UUID"); if (sdp_uuid_extract(p, bufsize, &d->val.uuid, len) < 0) { free(d); return NULL; } d->dtd = *p; if (rec) sdp_pattern_add_uuid(rec, &d->val.uuid); return d; } /* * Extract strings from the PDU (could be service description and similar info) */ static sdp_data_t *extract_str(const void *p, int bufsize, int *len) { char *s; int n; sdp_data_t *d; if (bufsize < (int) sizeof(uint8_t)) { SDPERR("Unexpected end of packet"); return NULL; } d = bt_malloc0(sizeof(sdp_data_t)); if (!d) return NULL; d->dtd = *(uint8_t *) p; p += sizeof(uint8_t); *len += sizeof(uint8_t); bufsize -= sizeof(uint8_t); switch (d->dtd) { case SDP_TEXT_STR8: case SDP_URL_STR8: if (bufsize < (int) sizeof(uint8_t)) { SDPERR("Unexpected end of packet"); free(d); return NULL; } n = *(uint8_t *) p; p += sizeof(uint8_t); *len += sizeof(uint8_t); bufsize -= sizeof(uint8_t); break; case SDP_TEXT_STR16: case SDP_URL_STR16: if (bufsize < (int) sizeof(uint16_t)) { SDPERR("Unexpected end of packet"); free(d); return NULL; } n = bt_get_be16(p); p += sizeof(uint16_t); *len += sizeof(uint16_t); bufsize -= sizeof(uint16_t); break; default: SDPERR("Sizeof text string > UINT16_MAX"); free(d); return NULL; } if (bufsize < n) { SDPERR("String too long to fit in packet"); free(d); return NULL; } s = bt_malloc0(n + 1); if (!s) { SDPERR("Not enough memory for incoming string"); free(d); return NULL; } memcpy(s, p, n); *len += n; SDPDBG("Len : %d", n); SDPDBG("Str : %s", s); d->val.str = s; d->unitSize = n + sizeof(uint8_t); return d; } /* * Extract the sequence type and its length, and return offset into buf * or 0 on failure. */ int sdp_extract_seqtype(const uint8_t *buf, int bufsize, uint8_t *dtdp, int *size) { uint8_t dtd; int scanned = sizeof(uint8_t); if (bufsize < (int) sizeof(uint8_t)) { SDPERR("Unexpected end of packet"); return 0; } dtd = *(uint8_t *) buf; buf += sizeof(uint8_t); bufsize -= sizeof(uint8_t); *dtdp = dtd; switch (dtd) { case SDP_SEQ8: case SDP_ALT8: if (bufsize < (int) sizeof(uint8_t)) { SDPERR("Unexpected end of packet"); return 0; } *size = *(uint8_t *) buf; scanned += sizeof(uint8_t); break; case SDP_SEQ16: case SDP_ALT16: if (bufsize < (int) sizeof(uint16_t)) { SDPERR("Unexpected end of packet"); return 0; } *size = bt_get_be16(buf); scanned += sizeof(uint16_t); break; case SDP_SEQ32: case SDP_ALT32: if (bufsize < (int) sizeof(uint32_t)) { SDPERR("Unexpected end of packet"); return 0; } *size = bt_get_be32(buf); scanned += sizeof(uint32_t); break; default: SDPERR("Unknown sequence type, aborting"); return 0; } return scanned; } static sdp_data_t *extract_seq(const void *p, int bufsize, int *len, sdp_record_t *rec) { int seqlen, n = 0; sdp_data_t *curr, *prev; sdp_data_t *d = bt_malloc0(sizeof(sdp_data_t)); if (!d) return NULL; SDPDBG("Extracting SEQ"); *len = sdp_extract_seqtype(p, bufsize, &d->dtd, &seqlen); SDPDBG("Sequence Type : 0x%x length : 0x%x", d->dtd, seqlen); if (*len == 0) return d; if (*len > bufsize) { SDPERR("Packet not big enough to hold sequence."); free(d); return NULL; } p += *len; bufsize -= *len; prev = NULL; while (n < seqlen) { int attrlen = 0; curr = sdp_extract_attr(p, bufsize, &attrlen, rec); if (curr == NULL) break; if (prev) prev->next = curr; else d->val.dataseq = curr; prev = curr; p += attrlen; n += attrlen; bufsize -= attrlen; SDPDBG("Extracted: %d SequenceLength: %d", n, seqlen); } *len += n; return d; } sdp_data_t *sdp_extract_attr(const uint8_t *p, int bufsize, int *size, sdp_record_t *rec) { sdp_data_t *elem; int n = 0; uint8_t dtd; if (bufsize < (int) sizeof(uint8_t)) { SDPERR("Unexpected end of packet"); return NULL; } dtd = *(const uint8_t *)p; SDPDBG("extract_attr: dtd=0x%x", dtd); switch (dtd) { case SDP_DATA_NIL: case SDP_BOOL: case SDP_UINT8: case SDP_UINT16: case SDP_UINT32: case SDP_UINT64: case SDP_UINT128: case SDP_INT8: case SDP_INT16: case SDP_INT32: case SDP_INT64: case SDP_INT128: elem = extract_int(p, bufsize, &n); break; case SDP_UUID16: case SDP_UUID32: case SDP_UUID128: elem = extract_uuid(p, bufsize, &n, rec); break; case SDP_TEXT_STR8: case SDP_TEXT_STR16: case SDP_TEXT_STR32: case SDP_URL_STR8: case SDP_URL_STR16: case SDP_URL_STR32: elem = extract_str(p, bufsize, &n); break; case SDP_SEQ8: case SDP_SEQ16: case SDP_SEQ32: case SDP_ALT8: case SDP_ALT16: case SDP_ALT32: elem = extract_seq(p, bufsize, &n, rec); break; default: SDPERR("Unknown data descriptor : 0x%x terminating", dtd); return NULL; } *size += n; return elem; } #ifdef SDP_DEBUG static void attr_print_func(void *value, void *userData) { sdp_data_t *d = (sdp_data_t *)value; SDPDBG("====================================="); SDPDBG("ATTRIBUTE IDENTIFIER : 0x%x", d->attrId); SDPDBG("ATTRIBUTE VALUE PTR : %p", value); if (d) sdp_data_print(d); else SDPDBG("NULL value"); SDPDBG("====================================="); } void sdp_print_service_attr(sdp_list_t *svcAttrList) { SDPDBG("Printing service attr list %p", svcAttrList); sdp_list_foreach(svcAttrList, attr_print_func, NULL); SDPDBG("Printed service attr list %p", svcAttrList); } #endif sdp_record_t *sdp_extract_pdu(const uint8_t *buf, int bufsize, int *scanned) { int extracted = 0, seqlen = 0; uint8_t dtd; uint16_t attr; sdp_record_t *rec = sdp_record_alloc(); const uint8_t *p = buf; *scanned = sdp_extract_seqtype(buf, bufsize, &dtd, &seqlen); p += *scanned; bufsize -= *scanned; rec->attrlist = NULL; while (extracted < seqlen && bufsize > 0) { int n = sizeof(uint8_t), attrlen = 0; sdp_data_t *data = NULL; SDPDBG("Extract PDU, sequenceLength: %d localExtractedLength: %d", seqlen, extracted); if (bufsize < n + (int) sizeof(uint16_t)) { SDPERR("Unexpected end of packet"); break; } dtd = *(uint8_t *) p; attr = bt_get_be16(p + n); n += sizeof(uint16_t); SDPDBG("DTD of attrId : %d Attr id : 0x%x ", dtd, attr); data = sdp_extract_attr(p + n, bufsize - n, &attrlen, rec); SDPDBG("Attr id : 0x%x attrValueLength : %d", attr, attrlen); n += attrlen; if (data == NULL) { SDPDBG("Terminating extraction of attributes"); break; } if (attr == SDP_ATTR_RECORD_HANDLE) rec->handle = data->val.uint32; if (attr == SDP_ATTR_SVCLASS_ID_LIST) extract_svclass_uuid(data, &rec->svclass); extracted += n; p += n; bufsize -= n; sdp_attr_replace(rec, attr, data); SDPDBG("Extract PDU, seqLength: %d localExtractedLength: %d", seqlen, extracted); } #ifdef SDP_DEBUG SDPDBG("Successful extracting of Svc Rec attributes"); sdp_print_service_attr(rec->attrlist); #endif *scanned += seqlen; return rec; } static void sdp_copy_pattern(void *value, void *udata) { uuid_t *uuid = value; sdp_record_t *rec = udata; sdp_pattern_add_uuid(rec, uuid); } static void *sdp_data_value(sdp_data_t *data, uint32_t *len) { void *val = NULL; switch (data->dtd) { case SDP_DATA_NIL: break; case SDP_UINT8: val = &data->val.uint8; break; case SDP_INT8: case SDP_BOOL: val = &data->val.int8; break; case SDP_UINT16: val = &data->val.uint16; break; case SDP_INT16: val = &data->val.int16; break; case SDP_UINT32: val = &data->val.uint32; break; case SDP_INT32: val = &data->val.int32; break; case SDP_INT64: val = &data->val.int64; break; case SDP_UINT64: val = &data->val.uint64; break; case SDP_UINT128: val = &data->val.uint128; break; case SDP_INT128: val = &data->val.int128; break; case SDP_UUID16: val = &data->val.uuid.value.uuid16; break; case SDP_UUID32: val = &data->val.uuid.value.uuid32; break; case SDP_UUID128: val = &data->val.uuid.value.uuid128; break; case SDP_URL_STR8: case SDP_URL_STR16: case SDP_TEXT_STR8: case SDP_TEXT_STR16: case SDP_URL_STR32: case SDP_TEXT_STR32: val = data->val.str; if (len) *len = data->unitSize - sizeof(uint8_t); break; case SDP_ALT8: case SDP_ALT16: case SDP_ALT32: case SDP_SEQ8: case SDP_SEQ16: case SDP_SEQ32: val = sdp_copy_seq(data->val.dataseq); break; } return val; } static sdp_data_t *sdp_copy_seq(sdp_data_t *data) { sdp_data_t *tmp, *seq = NULL, *cur = NULL; for (tmp = data; tmp; tmp = tmp->next) { sdp_data_t *datatmp; void *value; uint32_t len = 0; value = sdp_data_value(tmp, &len); datatmp = sdp_data_alloc_with_length(tmp->dtd, value, len); if (!datatmp) { sdp_data_free(seq); return NULL; } if (cur) cur->next = datatmp; else seq = datatmp; cur = datatmp; } return seq; } static void sdp_copy_attrlist(void *value, void *udata) { sdp_data_t *data = value; sdp_record_t *rec = udata; void *val; uint32_t len = 0; val = sdp_data_value(data, &len); if (!len) sdp_attr_add_new(rec, data->attrId, data->dtd, val); else sdp_attr_add_new_with_length(rec, data->attrId, data->dtd, val, len); } sdp_record_t *sdp_copy_record(sdp_record_t *rec) { sdp_record_t *cpy; cpy = sdp_record_alloc(); cpy->handle = rec->handle; sdp_list_foreach(rec->pattern, sdp_copy_pattern, cpy); sdp_list_foreach(rec->attrlist, sdp_copy_attrlist, cpy); cpy->svclass = rec->svclass; return cpy; } #ifdef SDP_DEBUG static void print_dataseq(sdp_data_t *p) { sdp_data_t *d; for (d = p; d; d = d->next) sdp_data_print(d); } #endif void sdp_record_print(const sdp_record_t *rec) { sdp_data_t *d = sdp_data_get(rec, SDP_ATTR_SVCNAME_PRIMARY); if (d && SDP_IS_TEXT_STR(d->dtd)) printf("Service Name: %.*s\n", d->unitSize, d->val.str); d = sdp_data_get(rec, SDP_ATTR_SVCDESC_PRIMARY); if (d && SDP_IS_TEXT_STR(d->dtd)) printf("Service Description: %.*s\n", d->unitSize, d->val.str); d = sdp_data_get(rec, SDP_ATTR_PROVNAME_PRIMARY); if (d && SDP_IS_TEXT_STR(d->dtd)) printf("Service Provider: %.*s\n", d->unitSize, d->val.str); } #ifdef SDP_DEBUG void sdp_data_print(sdp_data_t *d) { switch (d->dtd) { case SDP_DATA_NIL: SDPDBG("NIL"); break; case SDP_BOOL: case SDP_UINT8: case SDP_UINT16: case SDP_UINT32: case SDP_UINT64: case SDP_UINT128: case SDP_INT8: case SDP_INT16: case SDP_INT32: case SDP_INT64: case SDP_INT128: SDPDBG("Integer : 0x%x", d->val.uint32); break; case SDP_UUID16: case SDP_UUID32: case SDP_UUID128: SDPDBG("UUID"); sdp_uuid_print(&d->val.uuid); break; case SDP_TEXT_STR8: case SDP_TEXT_STR16: case SDP_TEXT_STR32: SDPDBG("Text : %s", d->val.str); break; case SDP_URL_STR8: case SDP_URL_STR16: case SDP_URL_STR32: SDPDBG("URL : %s", d->val.str); break; case SDP_SEQ8: case SDP_SEQ16: case SDP_SEQ32: print_dataseq(d->val.dataseq); break; case SDP_ALT8: case SDP_ALT16: case SDP_ALT32: SDPDBG("Data Sequence Alternates"); print_dataseq(d->val.dataseq); break; } } #endif sdp_data_t *sdp_data_get(const sdp_record_t *rec, uint16_t attrId) { if (rec && rec->attrlist) { sdp_data_t sdpTemplate; sdp_list_t *p; sdpTemplate.attrId = attrId; p = sdp_list_find(rec->attrlist, &sdpTemplate, sdp_attrid_comp_func); if (p) return p->data; } return NULL; } static int sdp_send_req(sdp_session_t *session, uint8_t *buf, uint32_t size) { uint32_t sent = 0; while (sent < size) { int n = send(session->sock, buf + sent, size - sent, 0); if (n < 0) return -1; sent += n; } return 0; } static int sdp_read_rsp(sdp_session_t *session, uint8_t *buf, uint32_t size) { fd_set readFds; struct timeval timeout = { SDP_RESPONSE_TIMEOUT, 0 }; FD_ZERO(&readFds); FD_SET(session->sock, &readFds); SDPDBG("Waiting for response"); if (select(session->sock + 1, &readFds, NULL, NULL, &timeout) == 0) { SDPERR("Client timed out"); errno = ETIMEDOUT; return -1; } return recv(session->sock, buf, size, 0); } /* * generic send request, wait for response method. */ int sdp_send_req_w4_rsp(sdp_session_t *session, uint8_t *reqbuf, uint8_t *rspbuf, uint32_t reqsize, uint32_t *rspsize) { int n; sdp_pdu_hdr_t *reqhdr = (sdp_pdu_hdr_t *) reqbuf; sdp_pdu_hdr_t *rsphdr = (sdp_pdu_hdr_t *) rspbuf; SDPDBG(""); if (0 > sdp_send_req(session, reqbuf, reqsize)) { SDPERR("Error sending data:%m"); return -1; } n = sdp_read_rsp(session, rspbuf, SDP_RSP_BUFFER_SIZE); if (0 > n) return -1; SDPDBG("Read : %d", n); if (n == 0 || reqhdr->tid != rsphdr->tid) { errno = EPROTO; return -1; } *rspsize = n; return 0; } /* * singly-linked lists (after openobex implementation) */ sdp_list_t *sdp_list_append(sdp_list_t *p, void *d) { sdp_list_t *q, *n = malloc(sizeof(sdp_list_t)); if (!n) return NULL; n->data = d; n->next = 0; if (!p) return n; for (q = p; q->next; q = q->next); q->next = n; return p; } sdp_list_t *sdp_list_remove(sdp_list_t *list, void *d) { sdp_list_t *p, *q; for (q = 0, p = list; p; q = p, p = p->next) if (p->data == d) { if (q) q->next = p->next; else list = p->next; free(p); break; } return list; } sdp_list_t *sdp_list_insert_sorted(sdp_list_t *list, void *d, sdp_comp_func_t f) { sdp_list_t *q, *p, *n; n = malloc(sizeof(sdp_list_t)); if (!n) return NULL; n->data = d; for (q = 0, p = list; p; q = p, p = p->next) if (f(p->data, d) >= 0) break; /* insert between q and p; if !q insert at head */ if (q) q->next = n; else list = n; n->next = p; return list; } /* * Every element of the list points to things which need * to be free()'d. This method frees the list's contents */ void sdp_list_free(sdp_list_t *list, sdp_free_func_t f) { sdp_list_t *next; while (list) { next = list->next; if (f) f(list->data); free(list); list = next; } } static inline int __find_port(sdp_data_t *seq, int proto) { if (!seq || !seq->next) return 0; if (SDP_IS_UUID(seq->dtd) && sdp_uuid_to_proto(&seq->val.uuid) == proto) { seq = seq->next; switch (seq->dtd) { case SDP_UINT8: return seq->val.uint8; case SDP_UINT16: return seq->val.uint16; } } return 0; } int sdp_get_proto_port(const sdp_list_t *list, int proto) { if (proto != L2CAP_UUID && proto != RFCOMM_UUID) { errno = EINVAL; return -1; } for (; list; list = list->next) { sdp_list_t *p; for (p = list->data; p; p = p->next) { sdp_data_t *seq = p->data; int port = __find_port(seq, proto); if (port) return port; } } return 0; } sdp_data_t *sdp_get_proto_desc(sdp_list_t *list, int proto) { for (; list; list = list->next) { sdp_list_t *p; for (p = list->data; p; p = p->next) { sdp_data_t *seq = p->data; if (SDP_IS_UUID(seq->dtd) && sdp_uuid_to_proto(&seq->val.uuid) == proto) return seq->next; } } return NULL; } static int sdp_get_proto_descs(uint16_t attr_id, const sdp_record_t *rec, sdp_list_t **pap) { sdp_data_t *pdlist, *curr; sdp_list_t *ap = NULL; pdlist = sdp_data_get(rec, attr_id); if (pdlist == NULL) { errno = ENODATA; return -1; } SDPDBG("Attribute value type: 0x%02x", pdlist->dtd); if (attr_id == SDP_ATTR_ADD_PROTO_DESC_LIST) { if (!SDP_IS_SEQ(pdlist->dtd)) { errno = EINVAL; return -1; } pdlist = pdlist->val.dataseq; } for (; pdlist; pdlist = pdlist->next) { sdp_list_t *pds = NULL; if (!SDP_IS_SEQ(pdlist->dtd) && !SDP_IS_ALT(pdlist->dtd)) goto failed; for (curr = pdlist->val.dataseq; curr; curr = curr->next) { if (!SDP_IS_SEQ(curr->dtd)) { sdp_list_free(pds, NULL); goto failed; } pds = sdp_list_append(pds, curr->val.dataseq); } ap = sdp_list_append(ap, pds); } *pap = ap; return 0; failed: sdp_list_foreach(ap, (sdp_list_func_t) sdp_list_free, NULL); sdp_list_free(ap, NULL); errno = EINVAL; return -1; } int sdp_get_access_protos(const sdp_record_t *rec, sdp_list_t **pap) { return sdp_get_proto_descs(SDP_ATTR_PROTO_DESC_LIST, rec, pap); } int sdp_get_add_access_protos(const sdp_record_t *rec, sdp_list_t **pap) { return sdp_get_proto_descs(SDP_ATTR_ADD_PROTO_DESC_LIST, rec, pap); } int sdp_get_uuidseq_attr(const sdp_record_t *rec, uint16_t attr, sdp_list_t **seqp) { sdp_data_t *sdpdata = sdp_data_get(rec, attr); *seqp = NULL; if (sdpdata && SDP_IS_SEQ(sdpdata->dtd)) { sdp_data_t *d; for (d = sdpdata->val.dataseq; d; d = d->next) { uuid_t *u; if (d->dtd < SDP_UUID16 || d->dtd > SDP_UUID128) { errno = EINVAL; goto fail; } u = malloc(sizeof(uuid_t)); if (!u) goto fail; *u = d->val.uuid; *seqp = sdp_list_append(*seqp, u); } return 0; } fail: sdp_list_free(*seqp, free); *seqp = NULL; return -1; } int sdp_set_uuidseq_attr(sdp_record_t *rec, uint16_t aid, sdp_list_t *seq) { int status = 0, i, len; void **dtds, **values; uint8_t uuid16 = SDP_UUID16; uint8_t uuid32 = SDP_UUID32; uint8_t uuid128 = SDP_UUID128; sdp_list_t *p; len = sdp_list_len(seq); if (!seq || len == 0) return -1; dtds = malloc(len * sizeof(void *)); if (!dtds) return -1; values = malloc(len * sizeof(void *)); if (!values) { free(dtds); return -1; } for (p = seq, i = 0; i < len; i++, p = p->next) { uuid_t *uuid = p->data; if (uuid) switch (uuid->type) { case SDP_UUID16: dtds[i] = &uuid16; values[i] = &uuid->value.uuid16; break; case SDP_UUID32: dtds[i] = &uuid32; values[i] = &uuid->value.uuid32; break; case SDP_UUID128: dtds[i] = &uuid128; values[i] = &uuid->value.uuid128; break; default: status = -1; break; } else { status = -1; break; } } if (status == 0) { sdp_data_t *data = sdp_seq_alloc(dtds, values, len); sdp_attr_replace(rec, aid, data); sdp_pattern_add_uuidseq(rec, seq); } free(dtds); free(values); return status; } int sdp_get_lang_attr(const sdp_record_t *rec, sdp_list_t **langSeq) { sdp_lang_attr_t *lang; sdp_data_t *sdpdata, *curr_data; *langSeq = NULL; sdpdata = sdp_data_get(rec, SDP_ATTR_LANG_BASE_ATTR_ID_LIST); if (sdpdata == NULL) { errno = ENODATA; return -1; } if (!SDP_IS_SEQ(sdpdata->dtd)) goto invalid; curr_data = sdpdata->val.dataseq; while (curr_data) { sdp_data_t *pCode, *pEncoding, *pOffset; pCode = curr_data; if (pCode->dtd != SDP_UINT16) goto invalid; /* LanguageBaseAttributeIDList entries are always grouped as * triplets */ if (!pCode->next || !pCode->next->next) goto invalid; pEncoding = pCode->next; if (pEncoding->dtd != SDP_UINT16) goto invalid; pOffset = pEncoding->next; if (pOffset->dtd != SDP_UINT16) goto invalid; lang = malloc(sizeof(sdp_lang_attr_t)); if (!lang) { sdp_list_free(*langSeq, free); *langSeq = NULL; return -1; } lang->code_ISO639 = pCode->val.uint16; lang->encoding = pEncoding->val.uint16; lang->base_offset = pOffset->val.uint16; SDPDBG("code_ISO639 : 0x%02x", lang->code_ISO639); SDPDBG("encoding : 0x%02x", lang->encoding); SDPDBG("base_offfset : 0x%02x", lang->base_offset); *langSeq = sdp_list_append(*langSeq, lang); curr_data = pOffset->next; } return 0; invalid: sdp_list_free(*langSeq, free); *langSeq = NULL; errno = EINVAL; return -1; } int sdp_get_profile_descs(const sdp_record_t *rec, sdp_list_t **profDescSeq) { sdp_profile_desc_t *profDesc; sdp_data_t *sdpdata, *seq; *profDescSeq = NULL; sdpdata = sdp_data_get(rec, SDP_ATTR_PFILE_DESC_LIST); if (sdpdata == NULL) { errno = ENODATA; return -1; } if (!SDP_IS_SEQ(sdpdata->dtd) || sdpdata->val.dataseq == NULL) goto invalid; for (seq = sdpdata->val.dataseq; seq; seq = seq->next) { uuid_t *uuid = NULL; uint16_t version = 0x100; if (SDP_IS_UUID(seq->dtd)) { /* Mac OS X 10.7.3 and old Samsung phones do not comply * to the SDP specification for * BluetoothProfileDescriptorList. This workaround * allows to properly parse UUID/version from SDP * record published by these systems. */ sdp_data_t *next = seq->next; uuid = &seq->val.uuid; if (next && next->dtd == SDP_UINT16) { version = next->val.uint16; seq = next; } } else if (SDP_IS_SEQ(seq->dtd)) { sdp_data_t *puuid, *pVnum; puuid = seq->val.dataseq; if (puuid == NULL || !SDP_IS_UUID(puuid->dtd)) goto invalid; uuid = &puuid->val.uuid; pVnum = puuid->next; if (pVnum == NULL || pVnum->dtd != SDP_UINT16) goto invalid; version = pVnum->val.uint16; } else goto invalid; if (uuid != NULL) { profDesc = malloc(sizeof(sdp_profile_desc_t)); if (!profDesc) { sdp_list_free(*profDescSeq, free); *profDescSeq = NULL; return -1; } profDesc->uuid = *uuid; profDesc->version = version; #ifdef SDP_DEBUG sdp_uuid_print(&profDesc->uuid); SDPDBG("Vnum : 0x%04x", profDesc->version); #endif *profDescSeq = sdp_list_append(*profDescSeq, profDesc); } } return 0; invalid: sdp_list_free(*profDescSeq, free); *profDescSeq = NULL; errno = EINVAL; return -1; } int sdp_get_server_ver(const sdp_record_t *rec, sdp_list_t **u16) { sdp_data_t *d, *curr; *u16 = NULL; d = sdp_data_get(rec, SDP_ATTR_VERSION_NUM_LIST); if (d == NULL) { errno = ENODATA; return -1; } if (!SDP_IS_SEQ(d->dtd) || d->val.dataseq == NULL) goto invalid; for (curr = d->val.dataseq; curr; curr = curr->next) { if (curr->dtd != SDP_UINT16) goto invalid; *u16 = sdp_list_append(*u16, &curr->val.uint16); } return 0; invalid: sdp_list_free(*u16, NULL); *u16 = NULL; errno = EINVAL; return -1; } /* flexible extraction of basic attributes - Jean II */ /* How do we expect caller to extract predefined data sequences? */ int sdp_get_int_attr(const sdp_record_t *rec, uint16_t attrid, int *value) { sdp_data_t *sdpdata = sdp_data_get(rec, attrid); if (sdpdata) /* Verify that it is what the caller expects */ if (sdpdata->dtd == SDP_BOOL || sdpdata->dtd == SDP_UINT8 || sdpdata->dtd == SDP_UINT16 || sdpdata->dtd == SDP_UINT32 || sdpdata->dtd == SDP_INT8 || sdpdata->dtd == SDP_INT16 || sdpdata->dtd == SDP_INT32) { *value = sdpdata->val.uint32; return 0; } errno = EINVAL; return -1; } int sdp_get_string_attr(const sdp_record_t *rec, uint16_t attrid, char *value, size_t valuelen) { sdp_data_t *sdpdata = sdp_data_get(rec, attrid); /* Verify that it is what the caller expects */ if (!sdpdata || !SDP_IS_TEXT_STR(sdpdata->dtd)) goto fail; /* Have to copy the NULL terminator too, so check len < valuelen. */ if (strlen(sdpdata->val.str) < valuelen) { strcpy(value, sdpdata->val.str); return 0; } fail: errno = EINVAL; return -1; } #define get_basic_attr(attrID, pAttrValue, fieldName) \ sdp_data_t *data = sdp_data_get(rec, attrID); \ if (data) { \ *pAttrValue = data->val.fieldName; \ return 0; \ } \ errno = EINVAL; \ return -1; int sdp_get_service_id(const sdp_record_t *rec, uuid_t *uuid) { get_basic_attr(SDP_ATTR_SERVICE_ID, uuid, uuid); } int sdp_get_group_id(const sdp_record_t *rec, uuid_t *uuid) { get_basic_attr(SDP_ATTR_GROUP_ID, uuid, uuid); } int sdp_get_record_state(const sdp_record_t *rec, uint32_t *svcRecState) { get_basic_attr(SDP_ATTR_RECORD_STATE, svcRecState, uint32); } int sdp_get_service_avail(const sdp_record_t *rec, uint8_t *svcAvail) { get_basic_attr(SDP_ATTR_SERVICE_AVAILABILITY, svcAvail, uint8); } int sdp_get_service_ttl(const sdp_record_t *rec, uint32_t *svcTTLInfo) { get_basic_attr(SDP_ATTR_SVCINFO_TTL, svcTTLInfo, uint32); } int sdp_get_database_state(const sdp_record_t *rec, uint32_t *svcDBState) { get_basic_attr(SDP_ATTR_SVCDB_STATE, svcDBState, uint32); } /* * NOTE that none of the setXXX() functions below will * actually update the SDP server, unless the * {register, update}sdp_record_t() function is invoked. */ int sdp_attr_add_new(sdp_record_t *rec, uint16_t attr, uint8_t dtd, const void *value) { sdp_data_t *d = sdp_data_alloc(dtd, value); if (d) { sdp_attr_replace(rec, attr, d); return 0; } return -1; } static int sdp_attr_add_new_with_length(sdp_record_t *rec, uint16_t attr, uint8_t dtd, const void *value, uint32_t len) { sdp_data_t *d; d = sdp_data_alloc_with_length(dtd, value, len); if (!d) return -1; sdp_attr_replace(rec, attr, d); return 0; } /* * Set the information attributes of the service * pointed to by rec. The attributes are * service name, description and provider name */ void sdp_set_info_attr(sdp_record_t *rec, const char *name, const char *prov, const char *desc) { if (name) sdp_attr_add_new(rec, SDP_ATTR_SVCNAME_PRIMARY, SDP_TEXT_STR8, name); if (prov) sdp_attr_add_new(rec, SDP_ATTR_PROVNAME_PRIMARY, SDP_TEXT_STR8, prov); if (desc) sdp_attr_add_new(rec, SDP_ATTR_SVCDESC_PRIMARY, SDP_TEXT_STR8, desc); } static sdp_data_t *access_proto_to_dataseq(sdp_record_t *rec, sdp_list_t *proto) { sdp_data_t *seq = NULL; void *dtds[10], *values[10]; void **seqDTDs, **seqs; int i, seqlen; sdp_list_t *p; seqlen = sdp_list_len(proto); seqDTDs = bt_malloc0(seqlen * sizeof(void *)); if (!seqDTDs) return NULL; seqs = malloc(seqlen * sizeof(void *)); if (!seqs) { free(seqDTDs); return NULL; } for (i = 0, p = proto; p; p = p->next, i++) { sdp_list_t *elt = p->data; sdp_data_t *s; uuid_t *uuid = NULL; unsigned int pslen = 0; for (; elt && pslen < ARRAY_SIZE(dtds); elt = elt->next, pslen++) { sdp_data_t *d = elt->data; dtds[pslen] = &d->dtd; switch (d->dtd) { case SDP_UUID16: uuid = (uuid_t *) d; values[pslen] = &uuid->value.uuid16; break; case SDP_UUID32: uuid = (uuid_t *) d; values[pslen] = &uuid->value.uuid32; break; case SDP_UUID128: uuid = (uuid_t *) d; values[pslen] = &uuid->value.uuid128; break; case SDP_UINT8: values[pslen] = &d->val.uint8; break; case SDP_UINT16: values[pslen] = &d->val.uint16; break; case SDP_SEQ8: case SDP_SEQ16: case SDP_SEQ32: values[pslen] = d; break; /* FIXME: more */ } } s = sdp_seq_alloc(dtds, values, pslen); if (s) { seqDTDs[i] = &s->dtd; seqs[i] = s; if (uuid) sdp_pattern_add_uuid(rec, uuid); } } seq = sdp_seq_alloc(seqDTDs, seqs, seqlen); free(seqDTDs); free(seqs); return seq; } /* * sets the access protocols of the service specified * to the value specified in "access_proto" * * Note that if there are alternate mechanisms by * which the service is accessed, then they should * be specified as sequences * * Using a value of NULL for accessProtocols has * effect of removing this attribute (if previously set) * * This function replaces the existing sdp_access_proto_t * structure (if any) with the new one specified. * * returns 0 if successful or -1 if there is a failure. */ int sdp_set_access_protos(sdp_record_t *rec, const sdp_list_t *ap) { const sdp_list_t *p; sdp_data_t *protos = NULL; for (p = ap; p; p = p->next) { sdp_data_t *seq = access_proto_to_dataseq(rec, p->data); protos = sdp_seq_append(protos, seq); } sdp_attr_add(rec, SDP_ATTR_PROTO_DESC_LIST, protos); return 0; } int sdp_set_add_access_protos(sdp_record_t *rec, const sdp_list_t *ap) { const sdp_list_t *p; sdp_data_t *protos = NULL; for (p = ap; p; p = p->next) { sdp_data_t *seq = access_proto_to_dataseq(rec, p->data); protos = sdp_seq_append(protos, seq); } sdp_attr_add(rec, SDP_ATTR_ADD_PROTO_DESC_LIST, protos ? sdp_data_alloc(SDP_SEQ8, protos) : NULL); return 0; } /* * set the "LanguageBase" attributes of the service record * record to the value specified in "langAttrList". * * "langAttrList" is a linked list of "sdp_lang_attr_t" * objects, one for each language in which user visible * attributes are present in the service record. * * Using a value of NULL for langAttrList has * effect of removing this attribute (if previously set) * * This function replaces the exisiting sdp_lang_attr_t * structure (if any) with the new one specified. * * returns 0 if successful or -1 if there is a failure. */ int sdp_set_lang_attr(sdp_record_t *rec, const sdp_list_t *seq) { uint8_t uint16 = SDP_UINT16; int status = 0, i = 0, seqlen = sdp_list_len(seq); void **dtds, **values; const sdp_list_t *p; dtds = malloc(3 * seqlen * sizeof(void *)); if (!dtds) return -1; values = malloc(3 * seqlen * sizeof(void *)); if (!values) { free(dtds); return -1; } for (p = seq; p; p = p->next) { sdp_lang_attr_t *lang = p->data; if (!lang) { status = -1; break; } dtds[i] = &uint16; values[i] = &lang->code_ISO639; i++; dtds[i] = &uint16; values[i] = &lang->encoding; i++; dtds[i] = &uint16; values[i] = &lang->base_offset; i++; } if (status == 0) { sdp_data_t *seq = sdp_seq_alloc(dtds, values, 3 * seqlen); sdp_attr_add(rec, SDP_ATTR_LANG_BASE_ATTR_ID_LIST, seq); } free(dtds); free(values); return status; } /* * set the "ServiceID" attribute of the service. * * This is the UUID of the service. * * returns 0 if successful or -1 if there is a failure. */ void sdp_set_service_id(sdp_record_t *rec, uuid_t uuid) { switch (uuid.type) { case SDP_UUID16: sdp_attr_add_new(rec, SDP_ATTR_SERVICE_ID, SDP_UUID16, &uuid.value.uuid16); break; case SDP_UUID32: sdp_attr_add_new(rec, SDP_ATTR_SERVICE_ID, SDP_UUID32, &uuid.value.uuid32); break; case SDP_UUID128: sdp_attr_add_new(rec, SDP_ATTR_SERVICE_ID, SDP_UUID128, &uuid.value.uuid128); break; } sdp_pattern_add_uuid(rec, &uuid); } /* * set the GroupID attribute of the service record defining a group. * * This is the UUID of the group. * * returns 0 if successful or -1 if there is a failure. */ void sdp_set_group_id(sdp_record_t *rec, uuid_t uuid) { switch (uuid.type) { case SDP_UUID16: sdp_attr_add_new(rec, SDP_ATTR_GROUP_ID, SDP_UUID16, &uuid.value.uuid16); break; case SDP_UUID32: sdp_attr_add_new(rec, SDP_ATTR_GROUP_ID, SDP_UUID32, &uuid.value.uuid32); break; case SDP_UUID128: sdp_attr_add_new(rec, SDP_ATTR_GROUP_ID, SDP_UUID128, &uuid.value.uuid128); break; } sdp_pattern_add_uuid(rec, &uuid); } /* * set the ProfileDescriptorList attribute of the service record * pointed to by record to the value specified in "profileDesc". * * Each element in the list is an object of type * sdp_profile_desc_t which is a definition of the * Bluetooth profile that this service conforms to. * * Using a value of NULL for profileDesc has * effect of removing this attribute (if previously set) * * This function replaces the exisiting ProfileDescriptorList * structure (if any) with the new one specified. * * returns 0 if successful or -1 if there is a failure. */ int sdp_set_profile_descs(sdp_record_t *rec, const sdp_list_t *profiles) { int status = 0; uint8_t uuid16 = SDP_UUID16; uint8_t uuid32 = SDP_UUID32; uint8_t uuid128 = SDP_UUID128; uint8_t uint16 = SDP_UINT16; int i = 0, seqlen = sdp_list_len(profiles); void **seqDTDs, **seqs; const sdp_list_t *p; sdp_data_t *pAPSeq; seqDTDs = malloc(seqlen * sizeof(void *)); if (!seqDTDs) return -1; seqs = malloc(seqlen * sizeof(void *)); if (!seqs) { free(seqDTDs); return -1; } for (p = profiles; p; p = p->next) { sdp_data_t *seq; void *dtds[2], *values[2]; sdp_profile_desc_t *profile = p->data; if (!profile) { status = -1; goto end; } switch (profile->uuid.type) { case SDP_UUID16: dtds[0] = &uuid16; values[0] = &profile->uuid.value.uuid16; break; case SDP_UUID32: dtds[0] = &uuid32; values[0] = &profile->uuid.value.uuid32; break; case SDP_UUID128: dtds[0] = &uuid128; values[0] = &profile->uuid.value.uuid128; break; default: status = -1; goto end; } dtds[1] = &uint16; values[1] = &profile->version; seq = sdp_seq_alloc(dtds, values, 2); if (seq == NULL) { status = -1; goto end; } seqDTDs[i] = &seq->dtd; seqs[i] = seq; sdp_pattern_add_uuid(rec, &profile->uuid); i++; } pAPSeq = sdp_seq_alloc(seqDTDs, seqs, seqlen); sdp_attr_add(rec, SDP_ATTR_PFILE_DESC_LIST, pAPSeq); end: free(seqDTDs); free(seqs); return status; } /* * sets various URL attributes of the service * pointed to by record. The URL include * * client: a URL to the client's * platform specific (WinCE, PalmOS) executable * code that can be used to access this service. * * doc: a URL pointing to service documentation * * icon: a URL to an icon that can be used to represent * this service. * * Note that you need to pass NULL for any URLs * that you don't want to set or remove */ void sdp_set_url_attr(sdp_record_t *rec, const char *client, const char *doc, const char *icon) { sdp_attr_add_new(rec, SDP_ATTR_CLNT_EXEC_URL, SDP_URL_STR8, client); sdp_attr_add_new(rec, SDP_ATTR_DOC_URL, SDP_URL_STR8, doc); sdp_attr_add_new(rec, SDP_ATTR_ICON_URL, SDP_URL_STR8, icon); } uuid_t *sdp_uuid16_create(uuid_t *u, uint16_t val) { memset(u, 0, sizeof(uuid_t)); u->type = SDP_UUID16; u->value.uuid16 = val; return u; } uuid_t *sdp_uuid32_create(uuid_t *u, uint32_t val) { memset(u, 0, sizeof(uuid_t)); u->type = SDP_UUID32; u->value.uuid32 = val; return u; } uuid_t *sdp_uuid128_create(uuid_t *u, const void *val) { memset(u, 0, sizeof(uuid_t)); u->type = SDP_UUID128; memcpy(&u->value.uuid128, val, sizeof(uint128_t)); return u; } /* * UUID comparison function * returns 0 if uuidValue1 == uuidValue2 else -1 */ int sdp_uuid_cmp(const void *p1, const void *p2) { uuid_t *u1 = sdp_uuid_to_uuid128(p1); uuid_t *u2 = sdp_uuid_to_uuid128(p2); int ret; ret = sdp_uuid128_cmp(u1, u2); bt_free(u1); bt_free(u2); return ret; } /* * UUID comparison function * returns 0 if uuidValue1 == uuidValue2 else -1 */ int sdp_uuid16_cmp(const void *p1, const void *p2) { const uuid_t *u1 = p1; const uuid_t *u2 = p2; return memcmp(&u1->value.uuid16, &u2->value.uuid16, sizeof(uint16_t)); } /* * UUID comparison function * returns 0 if uuidValue1 == uuidValue2 else -1 */ int sdp_uuid128_cmp(const void *p1, const void *p2) { const uuid_t *u1 = p1; const uuid_t *u2 = p2; return memcmp(&u1->value.uuid128, &u2->value.uuid128, sizeof(uint128_t)); } /* * 128 to 16 bit and 32 to 16 bit UUID conversion functions * yet to be implemented. Note that the input is in NBO in * both 32 and 128 bit UUIDs and conversion is needed */ void sdp_uuid16_to_uuid128(uuid_t *uuid128, const uuid_t *uuid16) { /* * We have a 16 bit value, which needs to be added to * bytes 3 and 4 (at indices 2 and 3) of the Bluetooth base */ unsigned short data1; /* allocate a 128bit UUID and init to the Bluetooth base UUID */ uuid128->value.uuid128 = bluetooth_base_uuid; uuid128->type = SDP_UUID128; /* extract bytes 2 and 3 of 128bit BT base UUID */ memcpy(&data1, &bluetooth_base_uuid.data[2], 2); /* add the given UUID (16 bits) */ data1 += htons(uuid16->value.uuid16); /* set bytes 2 and 3 of the 128 bit value */ memcpy(&uuid128->value.uuid128.data[2], &data1, 2); } void sdp_uuid32_to_uuid128(uuid_t *uuid128, const uuid_t *uuid32) { /* * We have a 32 bit value, which needs to be added to * bytes 1->4 (at indices 0 thru 3) of the Bluetooth base */ unsigned int data0; /* allocate a 128bit UUID and init to the Bluetooth base UUID */ uuid128->value.uuid128 = bluetooth_base_uuid; uuid128->type = SDP_UUID128; /* extract first 4 bytes */ memcpy(&data0, &bluetooth_base_uuid.data[0], 4); /* add the given UUID (32bits) */ data0 += htonl(uuid32->value.uuid32); /* set the 4 bytes of the 128 bit value */ memcpy(&uuid128->value.uuid128.data[0], &data0, 4); } uuid_t *sdp_uuid_to_uuid128(const uuid_t *uuid) { uuid_t *uuid128 = bt_malloc0(sizeof(uuid_t)); if (!uuid128) return NULL; switch (uuid->type) { case SDP_UUID128: *uuid128 = *uuid; break; case SDP_UUID32: sdp_uuid32_to_uuid128(uuid128, uuid); break; case SDP_UUID16: sdp_uuid16_to_uuid128(uuid128, uuid); break; } return uuid128; } /* * converts a 128-bit uuid to a 16/32-bit one if possible * returns true if uuid contains a 16/32-bit UUID at exit */ int sdp_uuid128_to_uuid(uuid_t *uuid) { const uint128_t *b = &bluetooth_base_uuid; uint128_t *u = &uuid->value.uuid128; uint32_t data; unsigned int i; if (uuid->type != SDP_UUID128) return 1; for (i = 4; i < sizeof(b->data); i++) if (b->data[i] != u->data[i]) return 0; memcpy(&data, u->data, 4); data = htonl(data); if (data <= 0xffff) { uuid->type = SDP_UUID16; uuid->value.uuid16 = (uint16_t) data; } else { uuid->type = SDP_UUID32; uuid->value.uuid32 = data; } return 1; } /* * convert a UUID to the 16-bit short-form */ int sdp_uuid_to_proto(uuid_t *uuid) { uuid_t u = *uuid; if (sdp_uuid128_to_uuid(&u)) { switch (u.type) { case SDP_UUID16: return u.value.uuid16; case SDP_UUID32: return u.value.uuid32; } } return 0; } /* * This function appends data to the PDU buffer "dst" from source "src". * The data length is also computed and set. * Should the PDU length exceed 2^8, then sequence type is * set accordingly and the data is memmove()'d. */ void sdp_append_to_buf(sdp_buf_t *dst, uint8_t *data, uint32_t len) { uint8_t *p = dst->data; uint8_t dtd = *p; SDPDBG("Append src size: %d", len); SDPDBG("Append dst size: %d", dst->data_size); SDPDBG("Dst buffer size: %d", dst->buf_size); if (dst->data_size + len > dst->buf_size) { SDPERR("Cannot append"); return; } if (dst->data_size == 0 && dtd == 0) { /* create initial sequence */ *p = SDP_SEQ8; dst->data_size += sizeof(uint8_t); /* reserve space for sequence size */ dst->data_size += sizeof(uint8_t); } memcpy(dst->data + dst->data_size, data, len); dst->data_size += len; dtd = *(uint8_t *) dst->data; if (dst->data_size > UCHAR_MAX && dtd == SDP_SEQ8) { short offset = sizeof(uint8_t) + sizeof(uint8_t); memmove(dst->data + offset + 1, dst->data + offset, dst->data_size - offset); *p = SDP_SEQ16; dst->data_size += 1; } dtd = *(uint8_t *) p; p += sizeof(uint8_t); switch (dtd) { case SDP_SEQ8: *(uint8_t *) p = dst->data_size - sizeof(uint8_t) - sizeof(uint8_t); break; case SDP_SEQ16: bt_put_be16(dst->data_size - sizeof(uint8_t) - sizeof(uint16_t), p); break; case SDP_SEQ32: bt_put_be32(dst->data_size - sizeof(uint8_t) - sizeof(uint32_t), p); break; } } void sdp_append_to_pdu(sdp_buf_t *pdu, sdp_data_t *d) { sdp_buf_t append; memset(&append, 0, sizeof(sdp_buf_t)); sdp_gen_buffer(&append, d); append.data = malloc(append.buf_size); if (!append.data) return; sdp_set_attrid(&append, d->attrId); sdp_gen_pdu(&append, d); sdp_append_to_buf(pdu, append.data, append.data_size); free(append.data); } /* * Registers an sdp record. * * It is incorrect to call this method on a record that * has been already registered with the server. * * Returns zero on success, otherwise -1 (and sets errno). */ int sdp_device_record_register_binary(sdp_session_t *session, bdaddr_t *device, uint8_t *data, uint32_t size, uint8_t flags, uint32_t *handle) { uint8_t *req, *rsp, *p; uint32_t reqsize, rspsize; sdp_pdu_hdr_t *reqhdr, *rsphdr; int status; SDPDBG(""); if (!session->local) { errno = EREMOTE; return -1; } req = malloc(SDP_REQ_BUFFER_SIZE); rsp = malloc(SDP_RSP_BUFFER_SIZE); if (req == NULL || rsp == NULL) { status = -1; errno = ENOMEM; goto end; } reqhdr = (sdp_pdu_hdr_t *)req; reqhdr->pdu_id = SDP_SVC_REGISTER_REQ; reqhdr->tid = htons(sdp_gen_tid(session)); reqsize = sizeof(sdp_pdu_hdr_t) + 1; p = req + sizeof(sdp_pdu_hdr_t); if (bacmp(device, BDADDR_ANY)) { *p++ = flags | SDP_DEVICE_RECORD; bacpy((bdaddr_t *) p, device); p += sizeof(bdaddr_t); reqsize += sizeof(bdaddr_t); } else *p++ = flags; memcpy(p, data, size); reqsize += size; reqhdr->plen = htons(reqsize - sizeof(sdp_pdu_hdr_t)); status = sdp_send_req_w4_rsp(session, req, rsp, reqsize, &rspsize); if (status < 0) goto end; if (rspsize < sizeof(sdp_pdu_hdr_t)) { SDPERR("Unexpected end of packet"); errno = EPROTO; status = -1; goto end; } rsphdr = (sdp_pdu_hdr_t *) rsp; p = rsp + sizeof(sdp_pdu_hdr_t); if (rsphdr->pdu_id == SDP_ERROR_RSP) { /* Invalid service record */ errno = EINVAL; status = -1; } else if (rsphdr->pdu_id != SDP_SVC_REGISTER_RSP) { errno = EPROTO; status = -1; } else { if (rspsize < sizeof(sdp_pdu_hdr_t) + sizeof(uint32_t)) { SDPERR("Unexpected end of packet"); errno = EPROTO; status = -1; goto end; } if (handle) *handle = bt_get_be32(p); } end: free(req); free(rsp); return status; } int sdp_device_record_register(sdp_session_t *session, bdaddr_t *device, sdp_record_t *rec, uint8_t flags) { sdp_buf_t pdu; uint32_t handle; int err; SDPDBG(""); if (rec->handle && rec->handle != 0xffffffff) { uint32_t handle = rec->handle; sdp_data_t *data = sdp_data_alloc(SDP_UINT32, &handle); sdp_attr_replace(rec, SDP_ATTR_RECORD_HANDLE, data); } if (sdp_gen_record_pdu(rec, &pdu) < 0) { errno = ENOMEM; return -1; } err = sdp_device_record_register_binary(session, device, pdu.data, pdu.data_size, flags, &handle); free(pdu.data); if (err == 0) { sdp_data_t *data = sdp_data_alloc(SDP_UINT32, &handle); rec->handle = handle; sdp_attr_replace(rec, SDP_ATTR_RECORD_HANDLE, data); } return err; } int sdp_record_register(sdp_session_t *session, sdp_record_t *rec, uint8_t flags) { return sdp_device_record_register(session, BDADDR_ANY, rec, flags); } /* * unregister a service record */ int sdp_device_record_unregister_binary(sdp_session_t *session, bdaddr_t *device, uint32_t handle) { uint8_t *reqbuf, *rspbuf, *p; uint32_t reqsize = 0, rspsize = 0; sdp_pdu_hdr_t *reqhdr, *rsphdr; int status; SDPDBG(""); if (handle == SDP_SERVER_RECORD_HANDLE) { errno = EINVAL; return -1; } if (!session->local) { errno = EREMOTE; return -1; } reqbuf = malloc(SDP_REQ_BUFFER_SIZE); rspbuf = malloc(SDP_RSP_BUFFER_SIZE); if (!reqbuf || !rspbuf) { errno = ENOMEM; status = -1; goto end; } reqhdr = (sdp_pdu_hdr_t *) reqbuf; reqhdr->pdu_id = SDP_SVC_REMOVE_REQ; reqhdr->tid = htons(sdp_gen_tid(session)); p = reqbuf + sizeof(sdp_pdu_hdr_t); reqsize = sizeof(sdp_pdu_hdr_t); bt_put_be32(handle, p); reqsize += sizeof(uint32_t); reqhdr->plen = htons(reqsize - sizeof(sdp_pdu_hdr_t)); status = sdp_send_req_w4_rsp(session, reqbuf, rspbuf, reqsize, &rspsize); if (status < 0) goto end; if (rspsize < sizeof(sdp_pdu_hdr_t) + sizeof(uint16_t)) { SDPERR("Unexpected end of packet"); errno = EPROTO; status = -1; goto end; } rsphdr = (sdp_pdu_hdr_t *) rspbuf; p = rspbuf + sizeof(sdp_pdu_hdr_t); if (rsphdr->pdu_id == SDP_ERROR_RSP) { /* For this case the status always is invalid record handle */ errno = EINVAL; status = -1; } else if (rsphdr->pdu_id != SDP_SVC_REMOVE_RSP) { errno = EPROTO; status = -1; } else { uint16_t tmp; memcpy(&tmp, p, sizeof(tmp)); status = tmp; } end: free(reqbuf); free(rspbuf); return status; } int sdp_device_record_unregister(sdp_session_t *session, bdaddr_t *device, sdp_record_t *rec) { int err; err = sdp_device_record_unregister_binary(session, device, rec->handle); if (err == 0) sdp_record_free(rec); return err; } int sdp_record_unregister(sdp_session_t *session, sdp_record_t *rec) { return sdp_device_record_unregister(session, BDADDR_ANY, rec); } /* * modify an existing service record */ int sdp_device_record_update_binary(sdp_session_t *session, bdaddr_t *device, uint32_t handle, uint8_t *data, uint32_t size) { return -1; } int sdp_device_record_update(sdp_session_t *session, bdaddr_t *device, const sdp_record_t *rec) { uint8_t *reqbuf, *rspbuf, *p; uint32_t reqsize, rspsize; sdp_pdu_hdr_t *reqhdr, *rsphdr; uint32_t handle; sdp_buf_t pdu; int status; SDPDBG(""); handle = rec->handle; if (handle == SDP_SERVER_RECORD_HANDLE) { errno = EINVAL; return -1; } if (!session->local) { errno = EREMOTE; return -1; } reqbuf = malloc(SDP_REQ_BUFFER_SIZE); rspbuf = malloc(SDP_RSP_BUFFER_SIZE); if (!reqbuf || !rspbuf) { errno = ENOMEM; status = -1; goto end; } reqhdr = (sdp_pdu_hdr_t *) reqbuf; reqhdr->pdu_id = SDP_SVC_UPDATE_REQ; reqhdr->tid = htons(sdp_gen_tid(session)); p = reqbuf + sizeof(sdp_pdu_hdr_t); reqsize = sizeof(sdp_pdu_hdr_t); bt_put_be32(handle, p); reqsize += sizeof(uint32_t); p += sizeof(uint32_t); if (sdp_gen_record_pdu(rec, &pdu) < 0) { errno = ENOMEM; status = -1; goto end; } memcpy(p, pdu.data, pdu.data_size); reqsize += pdu.data_size; free(pdu.data); reqhdr->plen = htons(reqsize - sizeof(sdp_pdu_hdr_t)); status = sdp_send_req_w4_rsp(session, reqbuf, rspbuf, reqsize, &rspsize); if (status < 0) goto end; if (rspsize < sizeof(sdp_pdu_hdr_t) + sizeof(uint16_t)) { SDPERR("Unexpected end of packet"); errno = EPROTO; status = -1; goto end; } SDPDBG("Send req status : %d", status); rsphdr = (sdp_pdu_hdr_t *) rspbuf; p = rspbuf + sizeof(sdp_pdu_hdr_t); if (rsphdr->pdu_id == SDP_ERROR_RSP) { /* The status can be invalid sintax or invalid record handle */ errno = EINVAL; status = -1; } else if (rsphdr->pdu_id != SDP_SVC_UPDATE_RSP) { errno = EPROTO; status = -1; } else { uint16_t tmp; memcpy(&tmp, p, sizeof(tmp)); status = tmp; } end: free(reqbuf); free(rspbuf); return status; } int sdp_record_update(sdp_session_t *session, const sdp_record_t *rec) { return sdp_device_record_update(session, BDADDR_ANY, rec); } sdp_record_t *sdp_record_alloc(void) { sdp_record_t *rec = bt_malloc0(sizeof(sdp_record_t)); if (!rec) return NULL; rec->handle = 0xffffffff; return rec; } /* * Free the contents of a service record */ void sdp_record_free(sdp_record_t *rec) { sdp_list_free(rec->attrlist, (sdp_free_func_t) sdp_data_free); sdp_list_free(rec->pattern, free); free(rec); } void sdp_pattern_add_uuid(sdp_record_t *rec, uuid_t *uuid) { uuid_t *uuid128 = sdp_uuid_to_uuid128(uuid); SDPDBG("Elements in target pattern : %d", sdp_list_len(rec->pattern)); SDPDBG("Trying to add : 0x%lx", (unsigned long) uuid128); if (sdp_list_find(rec->pattern, uuid128, sdp_uuid128_cmp) == NULL) rec->pattern = sdp_list_insert_sorted(rec->pattern, uuid128, sdp_uuid128_cmp); else bt_free(uuid128); SDPDBG("Elements in target pattern : %d", sdp_list_len(rec->pattern)); } void sdp_pattern_add_uuidseq(sdp_record_t *rec, sdp_list_t *seq) { for (; seq; seq = seq->next) { uuid_t *uuid = (uuid_t *)seq->data; sdp_pattern_add_uuid(rec, uuid); } } /* * Extract a sequence of service record handles from a PDU buffer * and add the entries to a sdp_list_t. Note that the service record * handles are not in "data element sequence" form, but just like * an array of service handles */ static void extract_record_handle_seq(uint8_t *pdu, int bufsize, sdp_list_t **seq, int count, unsigned int *scanned) { sdp_list_t *pSeq = *seq; uint8_t *pdata = pdu; int n; for (n = 0; n < count; n++) { uint32_t *pSvcRec; if (bufsize < (int) sizeof(uint32_t)) { SDPERR("Unexpected end of packet"); break; } pSvcRec = malloc(sizeof(uint32_t)); if (!pSvcRec) break; *pSvcRec = bt_get_be32(pdata); pSeq = sdp_list_append(pSeq, pSvcRec); pdata += sizeof(uint32_t); *scanned += sizeof(uint32_t); bufsize -= sizeof(uint32_t); } *seq = pSeq; } /* * Generate the attribute sequence pdu form * from sdp_list_t elements. Return length of attr seq */ static int gen_dataseq_pdu(uint8_t *dst, const sdp_list_t *seq, uint8_t dtd) { sdp_data_t *dataseq; void **types, **values; sdp_buf_t buf; int i, seqlen = sdp_list_len(seq); /* Fill up the value and the dtd arrays */ SDPDBG(""); SDPDBG("Seq length : %d", seqlen); types = malloc(seqlen * sizeof(void *)); if (!types) return -ENOMEM; values = malloc(seqlen * sizeof(void *)); if (!values) { free(types); return -ENOMEM; } for (i = 0; i < seqlen; i++) { void *data = seq->data; types[i] = &dtd; if (SDP_IS_UUID(dtd)) data = &((uuid_t *)data)->value; values[i] = data; seq = seq->next; } dataseq = sdp_seq_alloc(types, values, seqlen); if (!dataseq) { free(types); free(values); return -ENOMEM; } memset(&buf, 0, sizeof(sdp_buf_t)); sdp_gen_buffer(&buf, dataseq); buf.data = malloc(buf.buf_size); if (!buf.data) { sdp_data_free(dataseq); free(types); free(values); return -ENOMEM; } SDPDBG("Data Seq : 0x%p", seq); seqlen = sdp_gen_pdu(&buf, dataseq); SDPDBG("Copying : %d", buf.data_size); memcpy(dst, buf.data, buf.data_size); sdp_data_free(dataseq); free(types); free(values); free(buf.data); return seqlen; } static int gen_searchseq_pdu(uint8_t *dst, const sdp_list_t *seq) { uuid_t *uuid = seq->data; return gen_dataseq_pdu(dst, seq, uuid->type); } static int gen_attridseq_pdu(uint8_t *dst, const sdp_list_t *seq, uint8_t dataType) { return gen_dataseq_pdu(dst, seq, dataType); } typedef struct { uint8_t length; unsigned char data[16]; } __attribute__ ((packed)) sdp_cstate_t; static int copy_cstate(uint8_t *pdata, int pdata_len, const sdp_cstate_t *cstate) { if (cstate) { uint8_t len = cstate->length; if (len >= pdata_len) { SDPERR("Continuation state size exceeds internal buffer"); len = pdata_len - 1; } *pdata++ = len; memcpy(pdata, cstate->data, len); return len + 1; } *pdata = 0; return 1; } /* * This is a service search request. * * INPUT : * * sdp_list_t *search * Singly linked list containing elements of the search * pattern. Each entry in the list is a UUID (DataTypeSDP_UUID16) * of the service to be searched * * uint16_t max_rec_num * A 16 bit integer which tells the service, the maximum * entries that the client can handle in the response. The * server is obliged not to return > max_rec_num entries * * OUTPUT : * * int return value * 0: * The request completed successfully. This does not * mean the requested services were found * -1: * On any failure and sets errno * * sdp_list_t **rsp_list * This variable is set on a successful return if there are * non-zero service handles. It is a singly linked list of * service record handles (uint16_t) */ int sdp_service_search_req(sdp_session_t *session, const sdp_list_t *search, uint16_t max_rec_num, sdp_list_t **rsp) { int status = 0; uint32_t reqsize = 0, _reqsize; uint32_t rspsize = 0, rsplen; int seqlen = 0; int rec_count; unsigned scanned, pdata_len; uint8_t *pdata, *_pdata; uint8_t *reqbuf, *rspbuf; sdp_pdu_hdr_t *reqhdr, *rsphdr; sdp_cstate_t *cstate = NULL; reqbuf = malloc(SDP_REQ_BUFFER_SIZE); rspbuf = malloc(SDP_RSP_BUFFER_SIZE); if (!reqbuf || !rspbuf) { errno = ENOMEM; status = -1; goto end; } reqhdr = (sdp_pdu_hdr_t *) reqbuf; reqhdr->pdu_id = SDP_SVC_SEARCH_REQ; pdata = reqbuf + sizeof(sdp_pdu_hdr_t); reqsize = sizeof(sdp_pdu_hdr_t); /* add service class IDs for search */ seqlen = gen_searchseq_pdu(pdata, search); if (seqlen < 0) { errno = EINVAL; status = -1; goto end; } SDPDBG("Data seq added : %d", seqlen); /* set the length and increment the pointer */ reqsize += seqlen; pdata += seqlen; /* specify the maximum svc rec count that client expects */ bt_put_be16(max_rec_num, pdata); reqsize += sizeof(uint16_t); pdata += sizeof(uint16_t); _reqsize = reqsize; _pdata = pdata; *rsp = NULL; do { /* Add continuation state or NULL (first time) */ reqsize = _reqsize + copy_cstate(_pdata, SDP_REQ_BUFFER_SIZE - _reqsize, cstate); /* Set the request header's param length */ reqhdr->plen = htons(reqsize - sizeof(sdp_pdu_hdr_t)); reqhdr->tid = htons(sdp_gen_tid(session)); /* * Send the request, wait for response and if * no error, set the appropriate values and return */ status = sdp_send_req_w4_rsp(session, reqbuf, rspbuf, reqsize, &rspsize); if (status < 0) goto end; if (rspsize < sizeof(sdp_pdu_hdr_t)) { SDPERR("Unexpected end of packet"); status = -1; goto end; } rsphdr = (sdp_pdu_hdr_t *) rspbuf; rsplen = ntohs(rsphdr->plen); if (rsphdr->pdu_id == SDP_ERROR_RSP) { SDPDBG("Status : 0x%x", rsphdr->pdu_id); status = -1; goto end; } scanned = 0; pdata = rspbuf + sizeof(sdp_pdu_hdr_t); pdata_len = rspsize - sizeof(sdp_pdu_hdr_t); if (pdata_len < sizeof(uint16_t) + sizeof(uint16_t)) { SDPERR("Unexpected end of packet"); status = -1; goto end; } /* net service record match count */ pdata += sizeof(uint16_t); scanned += sizeof(uint16_t); pdata_len -= sizeof(uint16_t); rec_count = bt_get_be16(pdata); pdata += sizeof(uint16_t); scanned += sizeof(uint16_t); pdata_len -= sizeof(uint16_t); SDPDBG("Current svc count: %d", rec_count); SDPDBG("ResponseLength: %d", rsplen); if (!rec_count) { status = -1; goto end; } extract_record_handle_seq(pdata, pdata_len, rsp, rec_count, &scanned); SDPDBG("BytesScanned : %d", scanned); if (rsplen > scanned) { uint8_t cstate_len; if (rspsize < sizeof(sdp_pdu_hdr_t) + scanned + sizeof(uint8_t)) { SDPERR("Unexpected end of packet: continuation state data missing"); status = -1; goto end; } pdata = rspbuf + sizeof(sdp_pdu_hdr_t) + scanned; cstate_len = *(uint8_t *) pdata; if (cstate_len > 0) { cstate = (sdp_cstate_t *)pdata; SDPDBG("Cont state length: %d", cstate_len); } else cstate = NULL; } } while (cstate); end: free(reqbuf); free(rspbuf); return status; } /* * This is a service attribute request. * * INPUT : * * uint32_t handle * The handle of the service for which the attribute(s) are * requested * * sdp_attrreq_type_t reqtype * Attribute identifiers are 16 bit unsigned integers specified * in one of 2 ways described below : * SDP_ATTR_REQ_INDIVIDUAL - 16bit individual identifiers * They are the actual attribute identifiers in ascending order * * SDP_ATTR_REQ_RANGE - 32bit identifier range * The high-order 16bits is the start of range * the low-order 16bits are the end of range * 0x0000 to 0xFFFF gets all attributes * * sdp_list_t *attrid * Singly linked list containing attribute identifiers desired. * Every element is either a uint16_t(attrSpec = SDP_ATTR_REQ_INDIVIDUAL) * or a uint32_t(attrSpec=SDP_ATTR_REQ_RANGE) * * OUTPUT : * return sdp_record_t * * 0: * On any error and sets errno * !0: * The service record */ sdp_record_t *sdp_service_attr_req(sdp_session_t *session, uint32_t handle, sdp_attrreq_type_t reqtype, const sdp_list_t *attrids) { uint32_t reqsize = 0, _reqsize; uint32_t rspsize = 0, rsp_count; int attr_list_len = 0; int seqlen = 0; unsigned int pdata_len; uint8_t *pdata, *_pdata; uint8_t *reqbuf, *rspbuf; sdp_pdu_hdr_t *reqhdr, *rsphdr; sdp_cstate_t *cstate = NULL; uint8_t cstate_len = 0; sdp_buf_t rsp_concat_buf; sdp_record_t *rec = 0; if (reqtype != SDP_ATTR_REQ_INDIVIDUAL && reqtype != SDP_ATTR_REQ_RANGE) { errno = EINVAL; return NULL; } memset(&rsp_concat_buf, 0, sizeof(sdp_buf_t)); reqbuf = malloc(SDP_REQ_BUFFER_SIZE); rspbuf = malloc(SDP_RSP_BUFFER_SIZE); if (!reqbuf || !rspbuf) { errno = ENOMEM; goto end; } reqhdr = (sdp_pdu_hdr_t *) reqbuf; reqhdr->pdu_id = SDP_SVC_ATTR_REQ; pdata = reqbuf + sizeof(sdp_pdu_hdr_t); reqsize = sizeof(sdp_pdu_hdr_t); /* add the service record handle */ bt_put_be32(handle, pdata); reqsize += sizeof(uint32_t); pdata += sizeof(uint32_t); /* specify the response limit */ bt_put_be16(65535, pdata); reqsize += sizeof(uint16_t); pdata += sizeof(uint16_t); /* get attr seq PDU form */ seqlen = gen_attridseq_pdu(pdata, attrids, reqtype == SDP_ATTR_REQ_INDIVIDUAL? SDP_UINT16 : SDP_UINT32); if (seqlen < 0) { errno = EINVAL; goto end; } pdata += seqlen; reqsize += seqlen; SDPDBG("Attr list length : %d", seqlen); /* save before Continuation State */ _pdata = pdata; _reqsize = reqsize; do { int status; /* add NULL continuation state */ reqsize = _reqsize + copy_cstate(_pdata, SDP_REQ_BUFFER_SIZE - _reqsize, cstate); /* set the request header's param length */ reqhdr->tid = htons(sdp_gen_tid(session)); reqhdr->plen = htons(reqsize - sizeof(sdp_pdu_hdr_t)); status = sdp_send_req_w4_rsp(session, reqbuf, rspbuf, reqsize, &rspsize); if (status < 0) goto end; if (rspsize < sizeof(sdp_pdu_hdr_t)) { SDPERR("Unexpected end of packet"); goto end; } rsphdr = (sdp_pdu_hdr_t *) rspbuf; if (rsphdr->pdu_id == SDP_ERROR_RSP) { SDPDBG("PDU ID : 0x%x", rsphdr->pdu_id); goto end; } pdata = rspbuf + sizeof(sdp_pdu_hdr_t); pdata_len = rspsize - sizeof(sdp_pdu_hdr_t); if (pdata_len < sizeof(uint16_t)) { SDPERR("Unexpected end of packet"); goto end; } rsp_count = bt_get_be16(pdata); attr_list_len += rsp_count; pdata += sizeof(uint16_t); pdata_len -= sizeof(uint16_t); /* * if continuation state set need to re-issue request before * parsing */ if (pdata_len < rsp_count + sizeof(uint8_t)) { SDPERR("Unexpected end of packet: continuation state data missing"); goto end; } cstate_len = *(uint8_t *) (pdata + rsp_count); SDPDBG("Response id : %d", rsphdr->pdu_id); SDPDBG("Attrlist byte count : %d", rsp_count); SDPDBG("sdp_cstate_t length : %d", cstate_len); /* * a split response: concatenate intermediate responses * and the last one (which has cstate_len == 0) */ if (cstate_len > 0 || rsp_concat_buf.data_size != 0) { uint8_t *targetPtr = NULL; cstate = cstate_len > 0 ? (sdp_cstate_t *) (pdata + rsp_count) : 0; /* build concatenated response buffer */ rsp_concat_buf.data = realloc(rsp_concat_buf.data, rsp_concat_buf.data_size + rsp_count); rsp_concat_buf.buf_size = rsp_concat_buf.data_size + rsp_count; targetPtr = rsp_concat_buf.data + rsp_concat_buf.data_size; memcpy(targetPtr, pdata, rsp_count); rsp_concat_buf.data_size += rsp_count; } } while (cstate); if (attr_list_len > 0) { int scanned = 0; if (rsp_concat_buf.data_size != 0) { pdata = rsp_concat_buf.data; pdata_len = rsp_concat_buf.data_size; } rec = sdp_extract_pdu(pdata, pdata_len, &scanned); } end: free(reqbuf); free(rsp_concat_buf.data); free(rspbuf); return rec; } /* * SDP transaction structure for asynchronous search */ struct sdp_transaction { sdp_callback_t *cb; /* called when the transaction finishes */ void *udata; /* client user data */ uint8_t *reqbuf; /* pointer to request PDU */ sdp_buf_t rsp_concat_buf; uint32_t reqsize; /* without cstate */ int err; /* ZERO if success or the errno if failed */ }; /* * Creates a new sdp session for asynchronous search * INPUT: * int sk * non-blocking L2CAP socket * * RETURN: * sdp_session_t * * NULL - On memory allocation failure */ sdp_session_t *sdp_create(int sk, uint32_t flags) { sdp_session_t *session; struct sdp_transaction *t; session = bt_malloc0(sizeof(sdp_session_t)); if (!session) { errno = ENOMEM; return NULL; } session->flags = flags; session->sock = sk; t = bt_malloc0(sizeof(struct sdp_transaction)); if (!t) { errno = ENOMEM; free(session); return NULL; } session->priv = t; return session; } /* * Sets the callback function/user data used to notify the application * that the asynchronous transaction finished. This function must be * called before request an asynchronous search. * * INPUT: * sdp_session_t *session * Current sdp session to be handled * sdp_callback_t *cb * callback to be called when the transaction finishes * void *udata * user data passed to callback * RETURN: * 0 - Success * -1 - Failure */ int sdp_set_notify(sdp_session_t *session, sdp_callback_t *func, void *udata) { struct sdp_transaction *t; if (!session || !session->priv) return -1; t = session->priv; t->cb = func; t->udata = udata; return 0; } /* * This function starts an asynchronous service search request. * The incoming and outgoing data are stored in the transaction structure * buffers. When there is incoming data the sdp_process function must be * called to get the data and handle the continuation state. * * INPUT : * sdp_session_t *session * Current sdp session to be handled * * sdp_list_t *search * Singly linked list containing elements of the search * pattern. Each entry in the list is a UUID (DataTypeSDP_UUID16) * of the service to be searched * * uint16_t max_rec_num * A 16 bit integer which tells the service, the maximum * entries that the client can handle in the response. The * server is obliged not to return > max_rec_num entries * * OUTPUT : * * int return value * 0 - if the request has been sent properly * -1 - On any failure and sets errno */ int sdp_service_search_async(sdp_session_t *session, const sdp_list_t *search, uint16_t max_rec_num) { struct sdp_transaction *t; sdp_pdu_hdr_t *reqhdr; uint8_t *pdata; int cstate_len, seqlen = 0; if (!session || !session->priv) return -1; t = session->priv; /* clean possible allocated buffer */ free(t->rsp_concat_buf.data); memset(&t->rsp_concat_buf, 0, sizeof(sdp_buf_t)); if (!t->reqbuf) { t->reqbuf = malloc(SDP_REQ_BUFFER_SIZE); if (!t->reqbuf) { t->err = ENOMEM; goto end; } } memset(t->reqbuf, 0, SDP_REQ_BUFFER_SIZE); reqhdr = (sdp_pdu_hdr_t *) t->reqbuf; reqhdr->tid = htons(sdp_gen_tid(session)); reqhdr->pdu_id = SDP_SVC_SEARCH_REQ; /* generate PDU */ pdata = t->reqbuf + sizeof(sdp_pdu_hdr_t); t->reqsize = sizeof(sdp_pdu_hdr_t); /* add service class IDs for search */ seqlen = gen_searchseq_pdu(pdata, search); if (seqlen < 0) { t->err = EINVAL; goto end; } SDPDBG("Data seq added : %d", seqlen); /* now set the length and increment the pointer */ t->reqsize += seqlen; pdata += seqlen; bt_put_be16(max_rec_num, pdata); t->reqsize += sizeof(uint16_t); pdata += sizeof(uint16_t); /* set the request header's param length */ cstate_len = copy_cstate(pdata, SDP_REQ_BUFFER_SIZE - t->reqsize, NULL); reqhdr->plen = htons((t->reqsize + cstate_len) - sizeof(sdp_pdu_hdr_t)); if (sdp_send_req(session, t->reqbuf, t->reqsize + cstate_len) < 0) { SDPERR("Error sending data:%m"); t->err = errno; goto end; } return 0; end: free(t->reqbuf); t->reqbuf = NULL; return -1; } /* * This function starts an asynchronous service attribute request. * The incoming and outgoing data are stored in the transaction structure * buffers. When there is incoming data the sdp_process function must be * called to get the data and handle the continuation state. * * INPUT : * sdp_session_t *session * Current sdp session to be handled * * uint32_t handle * The handle of the service for which the attribute(s) are * requested * * sdp_attrreq_type_t reqtype * Attribute identifiers are 16 bit unsigned integers specified * in one of 2 ways described below : * SDP_ATTR_REQ_INDIVIDUAL - 16bit individual identifiers * They are the actual attribute identifiers in ascending order * * SDP_ATTR_REQ_RANGE - 32bit identifier range * The high-order 16bits is the start of range * the low-order 16bits are the end of range * 0x0000 to 0xFFFF gets all attributes * * sdp_list_t *attrid_list * Singly linked list containing attribute identifiers desired. * Every element is either a uint16_t(attrSpec = SDP_ATTR_REQ_INDIVIDUAL) * or a uint32_t(attrSpec=SDP_ATTR_REQ_RANGE) * * OUTPUT : * int return value * 0 - if the request has been sent properly * -1 - On any failure and sets errno */ int sdp_service_attr_async(sdp_session_t *session, uint32_t handle, sdp_attrreq_type_t reqtype, const sdp_list_t *attrid_list) { struct sdp_transaction *t; sdp_pdu_hdr_t *reqhdr; uint8_t *pdata; int cstate_len, seqlen = 0; if (!session || !session->priv) return -1; t = session->priv; /* clean possible allocated buffer */ free(t->rsp_concat_buf.data); memset(&t->rsp_concat_buf, 0, sizeof(sdp_buf_t)); if (!t->reqbuf) { t->reqbuf = malloc(SDP_REQ_BUFFER_SIZE); if (!t->reqbuf) { t->err = ENOMEM; goto end; } } memset(t->reqbuf, 0, SDP_REQ_BUFFER_SIZE); reqhdr = (sdp_pdu_hdr_t *) t->reqbuf; reqhdr->tid = htons(sdp_gen_tid(session)); reqhdr->pdu_id = SDP_SVC_ATTR_REQ; /* generate PDU */ pdata = t->reqbuf + sizeof(sdp_pdu_hdr_t); t->reqsize = sizeof(sdp_pdu_hdr_t); /* add the service record handle */ bt_put_be32(handle, pdata); t->reqsize += sizeof(uint32_t); pdata += sizeof(uint32_t); /* specify the response limit */ bt_put_be16(65535, pdata); t->reqsize += sizeof(uint16_t); pdata += sizeof(uint16_t); /* get attr seq PDU form */ seqlen = gen_attridseq_pdu(pdata, attrid_list, reqtype == SDP_ATTR_REQ_INDIVIDUAL? SDP_UINT16 : SDP_UINT32); if (seqlen < 0) { t->err = EINVAL; goto end; } /* now set the length and increment the pointer */ t->reqsize += seqlen; pdata += seqlen; SDPDBG("Attr list length : %d", seqlen); /* set the request header's param length */ cstate_len = copy_cstate(pdata, SDP_REQ_BUFFER_SIZE - t->reqsize, NULL); reqhdr->plen = htons((t->reqsize + cstate_len) - sizeof(sdp_pdu_hdr_t)); if (sdp_send_req(session, t->reqbuf, t->reqsize + cstate_len) < 0) { SDPERR("Error sending data:%m"); t->err = errno; goto end; } return 0; end: free(t->reqbuf); t->reqbuf = NULL; return -1; } /* * This function starts an asynchronous service search attributes. * It is a service search request combined with attribute request. The incoming * and outgoing data are stored in the transaction structure buffers. When there * is incoming data the sdp_process function must be called to get the data * and handle the continuation state. * * INPUT: * sdp_session_t *session * Current sdp session to be handled * * sdp_list_t *search * Singly linked list containing elements of the search * pattern. Each entry in the list is a UUID(DataTypeSDP_UUID16) * of the service to be searched * * AttributeSpecification attrSpec * Attribute identifiers are 16 bit unsigned integers specified * in one of 2 ways described below : * SDP_ATTR_REQ_INDIVIDUAL - 16bit individual identifiers * They are the actual attribute identifiers in ascending order * * SDP_ATTR_REQ_RANGE - 32bit identifier range * The high-order 16bits is the start of range * the low-order 16bits are the end of range * 0x0000 to 0xFFFF gets all attributes * * sdp_list_t *attrid_list * Singly linked list containing attribute identifiers desired. * Every element is either a uint16_t(attrSpec = SDP_ATTR_REQ_INDIVIDUAL) * or a uint32_t(attrSpec=SDP_ATTR_REQ_RANGE) * * RETURN: * 0 - if the request has been sent properly * -1 - On any failure */ int sdp_service_search_attr_async(sdp_session_t *session, const sdp_list_t *search, sdp_attrreq_type_t reqtype, const sdp_list_t *attrid_list) { struct sdp_transaction *t; sdp_pdu_hdr_t *reqhdr; uint8_t *pdata; int cstate_len, seqlen = 0; if (!session || !session->priv) return -1; t = session->priv; /* clean possible allocated buffer */ free(t->rsp_concat_buf.data); memset(&t->rsp_concat_buf, 0, sizeof(sdp_buf_t)); if (!t->reqbuf) { t->reqbuf = malloc(SDP_REQ_BUFFER_SIZE); if (!t->reqbuf) { t->err = ENOMEM; goto end; } } memset(t->reqbuf, 0, SDP_REQ_BUFFER_SIZE); reqhdr = (sdp_pdu_hdr_t *) t->reqbuf; reqhdr->tid = htons(sdp_gen_tid(session)); reqhdr->pdu_id = SDP_SVC_SEARCH_ATTR_REQ; /* generate PDU */ pdata = t->reqbuf + sizeof(sdp_pdu_hdr_t); t->reqsize = sizeof(sdp_pdu_hdr_t); /* add service class IDs for search */ seqlen = gen_searchseq_pdu(pdata, search); if (seqlen < 0) { t->err = EINVAL; goto end; } SDPDBG("Data seq added : %d", seqlen); /* now set the length and increment the pointer */ t->reqsize += seqlen; pdata += seqlen; bt_put_be16(SDP_MAX_ATTR_LEN, pdata); t->reqsize += sizeof(uint16_t); pdata += sizeof(uint16_t); SDPDBG("Max attr byte count : %d", SDP_MAX_ATTR_LEN); /* get attr seq PDU form */ seqlen = gen_attridseq_pdu(pdata, attrid_list, reqtype == SDP_ATTR_REQ_INDIVIDUAL ? SDP_UINT16 : SDP_UINT32); if (seqlen < 0) { t->err = EINVAL; goto end; } pdata += seqlen; SDPDBG("Attr list length : %d", seqlen); t->reqsize += seqlen; /* set the request header's param length */ cstate_len = copy_cstate(pdata, SDP_REQ_BUFFER_SIZE - t->reqsize, NULL); reqhdr->plen = htons((t->reqsize + cstate_len) - sizeof(sdp_pdu_hdr_t)); if (sdp_send_req(session, t->reqbuf, t->reqsize + cstate_len) < 0) { SDPERR("Error sending data:%m"); t->err = errno; goto end; } return 0; end: free(t->reqbuf); t->reqbuf = NULL; return -1; } /* * Function used to get the error reason after sdp_callback_t function has been called * and the status is 0xffff or if sdp_service_{search, attr, search_attr}_async returns -1. * It indicates that an error NOT related to SDP_ErrorResponse happened. Get errno directly * is not safe because multiple transactions can be triggered. * This function must be used with asynchronous sdp functions only. * * INPUT: * sdp_session_t *session * Current sdp session to be handled * RETURN: * 0 = No error in the current transaction * -1 - if the session is invalid * positive value - the errno value * */ int sdp_get_error(sdp_session_t *session) { struct sdp_transaction *t; if (!session || !session->priv) { SDPERR("Invalid session"); return -1; } t = session->priv; return t->err; } /* * Receive the incoming SDP PDU. This function must be called when there is data * available to be read. On continuation state, the original request (with a new * transaction ID) and the continuation state data will be appended in the initial PDU. * If an error happens or the transaction finishes the callback function will be called. * * INPUT: * sdp_session_t *session * Current sdp session to be handled * RETURN: * 0 - if the transaction is on continuation state * -1 - On any failure or the transaction finished */ int sdp_process(sdp_session_t *session) { struct sdp_transaction *t; sdp_pdu_hdr_t *reqhdr, *rsphdr; sdp_cstate_t *pcstate; uint8_t *pdata, *rspbuf, *targetPtr; int rsp_count, err = -1; size_t size = 0; int n, plen; uint16_t status = 0xffff; uint8_t pdu_id = 0x00; if (!session || !session->priv) { SDPERR("Invalid session"); return -1; } rspbuf = bt_malloc0(SDP_RSP_BUFFER_SIZE); if (!rspbuf) { SDPERR("Response buffer alloc failure:%m (%d)", errno); return -1; } t = session->priv; reqhdr = (sdp_pdu_hdr_t *)t->reqbuf; rsphdr = (sdp_pdu_hdr_t *)rspbuf; pdata = rspbuf + sizeof(sdp_pdu_hdr_t); n = sdp_read_rsp(session, rspbuf, SDP_RSP_BUFFER_SIZE); if (n < 0) { SDPERR("Read response:%m (%d)", errno); t->err = errno; goto end; } if (reqhdr->tid != rsphdr->tid) { t->err = EPROTO; SDPERR("Protocol error: transaction id does not match"); goto end; } if (n != (int) (ntohs(rsphdr->plen) + sizeof(sdp_pdu_hdr_t))) { t->err = EPROTO; SDPERR("Protocol error: invalid length"); goto end; } pdu_id = rsphdr->pdu_id; switch (rsphdr->pdu_id) { uint8_t *ssr_pdata; uint16_t tsrc, csrc; case SDP_SVC_SEARCH_RSP: /* * TSRC: Total Service Record Count (2 bytes) * CSRC: Current Service Record Count (2 bytes) */ ssr_pdata = pdata; tsrc = bt_get_be16(ssr_pdata); ssr_pdata += sizeof(uint16_t); csrc = bt_get_be16(ssr_pdata); /* csrc should never be larger than tsrc */ if (csrc > tsrc) { t->err = EPROTO; SDPERR("Protocol error: wrong current service record count value."); goto end; } SDPDBG("Total svc count: %d", tsrc); SDPDBG("Current svc count: %d", csrc); /* parameter length without continuation state */ plen = sizeof(tsrc) + sizeof(csrc) + csrc * 4; if (t->rsp_concat_buf.data_size == 0) { /* first fragment */ rsp_count = sizeof(tsrc) + sizeof(csrc) + csrc * 4; } else if (t->rsp_concat_buf.data_size >= sizeof(uint16_t) * 2) { /* point to the first csrc */ uint8_t *pcsrc = t->rsp_concat_buf.data + 2; uint16_t tcsrc, tcsrc2; /* FIXME: update the interface later. csrc doesn't need be passed to clients */ pdata += sizeof(uint16_t); /* point to csrc */ /* the first csrc contains the sum of partial csrc responses */ memcpy(&tcsrc, pcsrc, sizeof(tcsrc)); memcpy(&tcsrc2, pdata, sizeof(tcsrc2)); tcsrc += tcsrc2; memcpy(pcsrc, &tcsrc, sizeof(tcsrc)); pdata += sizeof(uint16_t); /* point to the first handle */ rsp_count = csrc * 4; } else { t->err = EPROTO; SDPERR("Protocol error: invalid PDU size"); status = SDP_INVALID_PDU_SIZE; goto end; } status = 0x0000; break; case SDP_SVC_ATTR_RSP: case SDP_SVC_SEARCH_ATTR_RSP: rsp_count = bt_get_be16(pdata); SDPDBG("Attrlist byte count : %d", rsp_count); /* Valid range for rsp_count is 0x0002-0xFFFF */ if (t->rsp_concat_buf.data_size == 0 && rsp_count < 0x0002) { t->err = EPROTO; SDPERR("Protocol error: invalid AttrList size"); status = SDP_INVALID_PDU_SIZE; goto end; } /* * Number of bytes in the AttributeLists parameter(without * continuation state) + AttributeListsByteCount field size. */ plen = sizeof(uint16_t) + rsp_count; pdata += sizeof(uint16_t); /* points to attribute list */ status = 0x0000; break; case SDP_ERROR_RSP: status = bt_get_be16(pdata); size = ntohs(rsphdr->plen); goto end; default: t->err = EPROTO; SDPERR("Illegal PDU ID: 0x%x", rsphdr->pdu_id); goto end; } /* Out of bound check before using rsp_count as offset for * continuation state, which has at least a one byte size * field. */ if ((n - (int) sizeof(sdp_pdu_hdr_t)) < plen + 1) { t->err = EPROTO; SDPERR("Protocol error: invalid PDU size"); status = SDP_INVALID_PDU_SIZE; goto end; } pcstate = (sdp_cstate_t *) (pdata + rsp_count); SDPDBG("Cstate length : %d", pcstate->length); /* * Check out of bound. Continuation state must have at least * 1 byte: ZERO to indicate that it is not a partial response. */ if ((n - (int) sizeof(sdp_pdu_hdr_t)) != (plen + pcstate->length + 1)) { t->err = EPROTO; SDPERR("Protocol error: wrong PDU size."); status = 0xffff; goto end; } /* * This is a split response, need to concatenate intermediate * responses and the last one which will have cstate length == 0 */ t->rsp_concat_buf.data = realloc(t->rsp_concat_buf.data, t->rsp_concat_buf.data_size + rsp_count); targetPtr = t->rsp_concat_buf.data + t->rsp_concat_buf.data_size; t->rsp_concat_buf.buf_size = t->rsp_concat_buf.data_size + rsp_count; memcpy(targetPtr, pdata, rsp_count); t->rsp_concat_buf.data_size += rsp_count; if (pcstate->length > 0) { int reqsize, cstate_len; reqhdr->tid = htons(sdp_gen_tid(session)); /* add continuation state */ cstate_len = copy_cstate(t->reqbuf + t->reqsize, SDP_REQ_BUFFER_SIZE - t->reqsize, pcstate); reqsize = t->reqsize + cstate_len; /* set the request header's param length */ reqhdr->plen = htons(reqsize - sizeof(sdp_pdu_hdr_t)); if (sdp_send_req(session, t->reqbuf, reqsize) < 0) { SDPERR("Error sending data:%m(%d)", errno); status = 0xffff; t->err = errno; goto end; } err = 0; } end: if (err) { if (t->rsp_concat_buf.data_size != 0) { pdata = t->rsp_concat_buf.data; size = t->rsp_concat_buf.data_size; } if (t->cb) t->cb(pdu_id, status, pdata, size, t->udata); } free(rspbuf); return err; } /* * This is a service search request combined with the service * attribute request. First a service class match is done and * for matching service, requested attributes are extracted * * INPUT : * * sdp_list_t *search * Singly linked list containing elements of the search * pattern. Each entry in the list is a UUID(DataTypeSDP_UUID16) * of the service to be searched * * AttributeSpecification attrSpec * Attribute identifiers are 16 bit unsigned integers specified * in one of 2 ways described below : * SDP_ATTR_REQ_INDIVIDUAL - 16bit individual identifiers * They are the actual attribute identifiers in ascending order * * SDP_ATTR_REQ_RANGE - 32bit identifier range * The high-order 16bits is the start of range * the low-order 16bits are the end of range * 0x0000 to 0xFFFF gets all attributes * * sdp_list_t *attrids * Singly linked list containing attribute identifiers desired. * Every element is either a uint16_t(attrSpec = SDP_ATTR_REQ_INDIVIDUAL) * or a uint32_t(attrSpec=SDP_ATTR_REQ_RANGE) * * OUTPUT : * int return value * 0: * The request completed successfully. This does not * mean the requested services were found * -1: * On any error and sets errno * * sdp_list_t **rsp * This variable is set on a successful return to point to * service(s) found. Each element of this list is of type * sdp_record_t* (of the services which matched the search list) */ int sdp_service_search_attr_req(sdp_session_t *session, const sdp_list_t *search, sdp_attrreq_type_t reqtype, const sdp_list_t *attrids, sdp_list_t **rsp) { int status = 0; uint32_t reqsize = 0, _reqsize; uint32_t rspsize = 0; int seqlen = 0, attr_list_len = 0; int rsp_count = 0, cstate_len = 0; unsigned int pdata_len; uint8_t *pdata, *_pdata; uint8_t *reqbuf, *rspbuf; sdp_pdu_hdr_t *reqhdr, *rsphdr; uint8_t dataType; sdp_list_t *rec_list = NULL; sdp_buf_t rsp_concat_buf; sdp_cstate_t *cstate = NULL; if (reqtype != SDP_ATTR_REQ_INDIVIDUAL && reqtype != SDP_ATTR_REQ_RANGE) { errno = EINVAL; return -1; } memset(&rsp_concat_buf, 0, sizeof(sdp_buf_t)); reqbuf = malloc(SDP_REQ_BUFFER_SIZE); rspbuf = malloc(SDP_RSP_BUFFER_SIZE); if (!reqbuf || !rspbuf) { errno = ENOMEM; status = -1; goto end; } reqhdr = (sdp_pdu_hdr_t *) reqbuf; reqhdr->pdu_id = SDP_SVC_SEARCH_ATTR_REQ; /* generate PDU */ pdata = reqbuf + sizeof(sdp_pdu_hdr_t); reqsize = sizeof(sdp_pdu_hdr_t); /* add service class IDs for search */ seqlen = gen_searchseq_pdu(pdata, search); if (seqlen < 0) { errno = EINVAL; status = -1; goto end; } SDPDBG("Data seq added : %d", seqlen); /* now set the length and increment the pointer */ reqsize += seqlen; pdata += seqlen; bt_put_be16(SDP_MAX_ATTR_LEN, pdata); reqsize += sizeof(uint16_t); pdata += sizeof(uint16_t); SDPDBG("Max attr byte count : %d", SDP_MAX_ATTR_LEN); /* get attr seq PDU form */ seqlen = gen_attridseq_pdu(pdata, attrids, reqtype == SDP_ATTR_REQ_INDIVIDUAL ? SDP_UINT16 : SDP_UINT32); if (seqlen < 0) { errno = EINVAL; status = -1; goto end; } pdata += seqlen; SDPDBG("Attr list length : %d", seqlen); reqsize += seqlen; *rsp = 0; /* save before Continuation State */ _pdata = pdata; _reqsize = reqsize; do { reqhdr->tid = htons(sdp_gen_tid(session)); /* add continuation state (can be null) */ reqsize = _reqsize + copy_cstate(_pdata, SDP_REQ_BUFFER_SIZE - _reqsize, cstate); /* set the request header's param length */ reqhdr->plen = htons(reqsize - sizeof(sdp_pdu_hdr_t)); rsphdr = (sdp_pdu_hdr_t *) rspbuf; status = sdp_send_req_w4_rsp(session, reqbuf, rspbuf, reqsize, &rspsize); if (rspsize < sizeof(sdp_pdu_hdr_t)) { SDPERR("Unexpected end of packet"); status = -1; goto end; } if (status < 0) { SDPDBG("Status : 0x%x", rsphdr->pdu_id); goto end; } if (rsphdr->pdu_id == SDP_ERROR_RSP) { status = -1; goto end; } pdata = rspbuf + sizeof(sdp_pdu_hdr_t); pdata_len = rspsize - sizeof(sdp_pdu_hdr_t); if (pdata_len < sizeof(uint16_t)) { SDPERR("Unexpected end of packet"); status = -1; goto end; } rsp_count = bt_get_be16(pdata); attr_list_len += rsp_count; pdata += sizeof(uint16_t); /* pdata points to attribute list */ pdata_len -= sizeof(uint16_t); if (pdata_len < rsp_count + sizeof(uint8_t)) { SDPERR("Unexpected end of packet: continuation state data missing"); status = -1; goto end; } cstate_len = *(uint8_t *) (pdata + rsp_count); SDPDBG("Attrlist byte count : %d", attr_list_len); SDPDBG("Response byte count : %d", rsp_count); SDPDBG("Cstate length : %d", cstate_len); /* * This is a split response, need to concatenate intermediate * responses and the last one which will have cstate_len == 0 */ if (cstate_len > 0 || rsp_concat_buf.data_size != 0) { uint8_t *targetPtr = NULL; cstate = cstate_len > 0 ? (sdp_cstate_t *) (pdata + rsp_count) : 0; /* build concatenated response buffer */ rsp_concat_buf.data = realloc(rsp_concat_buf.data, rsp_concat_buf.data_size + rsp_count); targetPtr = rsp_concat_buf.data + rsp_concat_buf.data_size; rsp_concat_buf.buf_size = rsp_concat_buf.data_size + rsp_count; memcpy(targetPtr, pdata, rsp_count); rsp_concat_buf.data_size += rsp_count; } } while (cstate); if (attr_list_len > 0) { int scanned = 0; if (rsp_concat_buf.data_size != 0) { pdata = rsp_concat_buf.data; pdata_len = rsp_concat_buf.data_size; } /* * Response is a sequence of sequence(s) for one or * more data element sequence(s) representing services * for which attributes are returned */ scanned = sdp_extract_seqtype(pdata, pdata_len, &dataType, &seqlen); SDPDBG("Bytes scanned : %d", scanned); SDPDBG("Seq length : %d", seqlen); if (scanned && seqlen) { pdata += scanned; pdata_len -= scanned; do { int recsize = 0; sdp_record_t *rec = sdp_extract_pdu(pdata, pdata_len, &recsize); if (rec == NULL) { SDPERR("SVC REC is null"); status = -1; goto end; } if (!recsize) { sdp_record_free(rec); break; } scanned += recsize; pdata += recsize; pdata_len -= recsize; SDPDBG("Loc seq length : %d", recsize); SDPDBG("Svc Rec Handle : 0x%x", rec->handle); SDPDBG("Bytes scanned : %d", scanned); SDPDBG("Attrlist byte count : %d", attr_list_len); rec_list = sdp_list_append(rec_list, rec); } while (scanned < attr_list_len && pdata_len > 0); SDPDBG("Successful scan of service attr lists"); *rsp = rec_list; } } end: free(rsp_concat_buf.data); free(reqbuf); free(rspbuf); return status; } /* * Find devices in the piconet. */ int sdp_general_inquiry(inquiry_info *ii, int num_dev, int duration, uint8_t *found) { int n = hci_inquiry(-1, 10, num_dev, NULL, &ii, 0); if (n < 0) { SDPERR("Inquiry failed:%m"); return -1; } *found = n; return 0; } int sdp_close(sdp_session_t *session) { struct sdp_transaction *t; int ret; if (!session) return -1; ret = close(session->sock); t = session->priv; if (t) { free(t->reqbuf); free(t->rsp_concat_buf.data); free(t); } free(session); return ret; } static inline int sdp_is_local(const bdaddr_t *device) { return memcmp(device, BDADDR_LOCAL, sizeof(bdaddr_t)) == 0; } static int sdp_connect_local(sdp_session_t *session) { struct sockaddr_un sa; session->sock = socket(PF_UNIX, SOCK_STREAM | SOCK_CLOEXEC, 0); if (session->sock < 0) return -1; session->local = 1; sa.sun_family = AF_UNIX; strcpy(sa.sun_path, SDP_UNIX_PATH); return connect(session->sock, (struct sockaddr *) &sa, sizeof(sa)); } static int set_l2cap_mtu(int sk, uint16_t mtu) { struct l2cap_options l2o; socklen_t len; memset(&l2o, 0, sizeof(l2o)); len = sizeof(l2o); if (getsockopt(sk, SOL_L2CAP, L2CAP_OPTIONS, &l2o, &len) < 0) return -1; l2o.imtu = mtu; l2o.omtu = mtu; if (setsockopt(sk, SOL_L2CAP, L2CAP_OPTIONS, &l2o, sizeof(l2o)) < 0) return -1; return 0; } static int sdp_connect_l2cap(const bdaddr_t *src, const bdaddr_t *dst, sdp_session_t *session) { uint32_t flags = session->flags; struct sockaddr_l2 sa; int sk; int sockflags = SOCK_SEQPACKET | SOCK_CLOEXEC; if (flags & SDP_NON_BLOCKING) sockflags |= SOCK_NONBLOCK; session->sock = socket(PF_BLUETOOTH, sockflags, BTPROTO_L2CAP); if (session->sock < 0) return -1; session->local = 0; sk = session->sock; memset(&sa, 0, sizeof(sa)); sa.l2_family = AF_BLUETOOTH; sa.l2_psm = 0; if (bacmp(src, BDADDR_ANY)) { sa.l2_bdaddr = *src; if (bind(sk, (struct sockaddr *) &sa, sizeof(sa)) < 0) return -1; } if (flags & SDP_WAIT_ON_CLOSE) { struct linger l = { .l_onoff = 1, .l_linger = 1 }; if (setsockopt(sk, SOL_SOCKET, SO_LINGER, &l, sizeof(l)) < 0) return -1; } if ((flags & SDP_LARGE_MTU) && set_l2cap_mtu(sk, SDP_LARGE_L2CAP_MTU) < 0) return -1; sa.l2_psm = htobs(SDP_PSM); sa.l2_bdaddr = *dst; do { int ret = connect(sk, (struct sockaddr *) &sa, sizeof(sa)); if (!ret) return 0; if (ret < 0 && (flags & SDP_NON_BLOCKING) && (errno == EAGAIN || errno == EINPROGRESS)) return 0; } while (errno == EBUSY && (flags & SDP_RETRY_IF_BUSY)); return -1; } sdp_session_t *sdp_connect(const bdaddr_t *src, const bdaddr_t *dst, uint32_t flags) { sdp_session_t *session; int err; if ((flags & SDP_RETRY_IF_BUSY) && (flags & SDP_NON_BLOCKING)) { errno = EINVAL; return NULL; } session = sdp_create(-1, flags); if (!session) return NULL; if (sdp_is_local(dst)) { if (sdp_connect_local(session) < 0) goto fail; } else { if (sdp_connect_l2cap(src, dst, session) < 0) goto fail; } return session; fail: err = errno; if (session->sock >= 0) close(session->sock); free(session->priv); free(session); errno = err; return NULL; } int sdp_get_socket(const sdp_session_t *session) { return session->sock; } uint16_t sdp_gen_tid(sdp_session_t *session) { return session->tid++; } /* * Set the supported features */ int sdp_set_supp_feat(sdp_record_t *rec, const sdp_list_t *sf) { const sdp_list_t *p, *r; sdp_data_t *feat, *seq_feat; int seqlen, i; void **seqDTDs, **seqVals; seqlen = sdp_list_len(sf); seqDTDs = malloc(seqlen * sizeof(void *)); if (!seqDTDs) return -1; seqVals = malloc(seqlen * sizeof(void *)); if (!seqVals) { free(seqDTDs); return -1; } for (p = sf, i = 0; p; p = p->next, i++) { int plen, j; void **dtds, **vals; int *lengths; plen = sdp_list_len(p->data); dtds = malloc(plen * sizeof(void *)); if (!dtds) goto fail; vals = malloc(plen * sizeof(void *)); if (!vals) { free(dtds); goto fail; } lengths = malloc(plen * sizeof(int)); if (!lengths) { free(dtds); free(vals); goto fail; } for (r = p->data, j = 0; r; r = r->next, j++) { sdp_data_t *data = (sdp_data_t *) r->data; dtds[j] = &data->dtd; switch (data->dtd) { case SDP_URL_STR8: case SDP_URL_STR16: case SDP_TEXT_STR8: case SDP_TEXT_STR16: vals[j] = data->val.str; lengths[j] = data->unitSize - sizeof(uint8_t); break; case SDP_ALT8: case SDP_ALT16: case SDP_ALT32: case SDP_SEQ8: case SDP_SEQ16: case SDP_SEQ32: vals[j] = data->val.dataseq; lengths[j] = 0; break; default: vals[j] = &data->val; lengths[j] = 0; break; } } feat = sdp_seq_alloc_with_length(dtds, vals, lengths, plen); free(dtds); free(vals); free(lengths); if (!feat) goto fail; seqDTDs[i] = &feat->dtd; seqVals[i] = feat; } seq_feat = sdp_seq_alloc(seqDTDs, seqVals, seqlen); if (!seq_feat) goto fail; sdp_attr_replace(rec, SDP_ATTR_SUPPORTED_FEATURES_LIST, seq_feat); free(seqVals); free(seqDTDs); return 0; fail: free(seqVals); free(seqDTDs); return -1; } /* * Get the supported features * If an error occurred -1 is returned and errno is set */ int sdp_get_supp_feat(const sdp_record_t *rec, sdp_list_t **seqp) { sdp_data_t *sdpdata, *d; sdp_list_t *tseq; tseq = NULL; sdpdata = sdp_data_get(rec, SDP_ATTR_SUPPORTED_FEATURES_LIST); if (!sdpdata || !SDP_IS_SEQ(sdpdata->dtd)) return sdp_get_uuidseq_attr(rec, SDP_ATTR_SUPPORTED_FEATURES_LIST, seqp); for (d = sdpdata->val.dataseq; d; d = d->next) { sdp_data_t *dd; sdp_list_t *subseq; if (!SDP_IS_SEQ(d->dtd)) goto fail; subseq = NULL; for (dd = d->val.dataseq; dd; dd = dd->next) { sdp_data_t *data; void *val; int length; switch (dd->dtd) { case SDP_URL_STR8: case SDP_URL_STR16: case SDP_TEXT_STR8: case SDP_TEXT_STR16: val = dd->val.str; length = dd->unitSize - sizeof(uint8_t); break; case SDP_UINT8: case SDP_UINT16: val = &dd->val; length = 0; break; default: sdp_list_free(subseq, free); goto fail; } data = sdp_data_alloc_with_length(dd->dtd, val, length); if (data) subseq = sdp_list_append(subseq, data); } tseq = sdp_list_append(tseq, subseq); } *seqp = tseq; return 0; fail: while (tseq) { sdp_list_t * next; next = tseq->next; sdp_list_free(tseq, free); tseq = next; } errno = EINVAL; return -1; } void sdp_add_lang_attr(sdp_record_t *rec) { sdp_lang_attr_t base_lang; sdp_list_t *langs; base_lang.code_ISO639 = (0x65 << 8) | 0x6e; base_lang.encoding = 106; base_lang.base_offset = SDP_PRIMARY_LANG_BASE; langs = sdp_list_append(0, &base_lang); sdp_set_lang_attr(rec, langs); sdp_list_free(langs, NULL); } bluez-5.82/lib/PaxHeaders/uuid.c0000644000000000000000000000005014572354773013547 xustar0020 atime=1743515841 20 ctime=1743591277 bluez-5.82/lib/uuid.c0000644000000000000000000001440114572354773013230 0ustar00rootroot// SPDX-License-Identifier: GPL-2.0-or-later /* * * BlueZ - Bluetooth protocol stack for Linux * * Copyright (C) 2011 Nokia Corporation * Copyright (C) 2011 Marcel Holtmann * * */ #ifdef HAVE_CONFIG_H #include #endif #include #include #include #include "lib/bluetooth.h" #include "uuid.h" static uint128_t bluetooth_base_uuid = { .data = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x80, 0x00, 0x00, 0x80, 0x5F, 0x9B, 0x34, 0xFB } }; #define BASE_UUID16_OFFSET 2 #define BASE_UUID32_OFFSET 0 static void bt_uuid16_to_uuid128(const bt_uuid_t *src, bt_uuid_t *dst) { uint16_t be16; dst->value.u128 = bluetooth_base_uuid; dst->type = BT_UUID128; /* * No matter the system: 128-bit UUIDs should be stored * as big-endian. 16-bit UUIDs are stored on host order. */ be16 = htons(src->value.u16); memcpy(&dst->value.u128.data[BASE_UUID16_OFFSET], &be16, sizeof(be16)); } static void bt_uuid32_to_uuid128(const bt_uuid_t *src, bt_uuid_t *dst) { uint32_t be32; dst->value.u128 = bluetooth_base_uuid; dst->type = BT_UUID128; /* * No matter the system: 128-bit UUIDs should be stored * as big-endian. 32-bit UUIDs are stored on host order. */ be32 = htonl(src->value.u32); memcpy(&dst->value.u128.data[BASE_UUID32_OFFSET], &be32, sizeof(be32)); } void bt_uuid_to_uuid128(const bt_uuid_t *src, bt_uuid_t *dst) { switch (src->type) { case BT_UUID128: *dst = *src; break; case BT_UUID32: bt_uuid32_to_uuid128(src, dst); break; case BT_UUID16: bt_uuid16_to_uuid128(src, dst); break; case BT_UUID_UNSPEC: default: break; } } static int bt_uuid128_cmp(const bt_uuid_t *u1, const bt_uuid_t *u2) { return memcmp(&u1->value.u128, &u2->value.u128, sizeof(uint128_t)); } int bt_uuid16_create(bt_uuid_t *btuuid, uint16_t value) { memset(btuuid, 0, sizeof(bt_uuid_t)); btuuid->type = BT_UUID16; btuuid->value.u16 = value; return 0; } int bt_uuid32_create(bt_uuid_t *btuuid, uint32_t value) { memset(btuuid, 0, sizeof(bt_uuid_t)); btuuid->type = BT_UUID32; btuuid->value.u32 = value; return 0; } int bt_uuid128_create(bt_uuid_t *btuuid, uint128_t value) { memset(btuuid, 0, sizeof(bt_uuid_t)); btuuid->type = BT_UUID128; btuuid->value.u128 = value; return 0; } int bt_uuid_cmp(const bt_uuid_t *uuid1, const bt_uuid_t *uuid2) { bt_uuid_t u1, u2; bt_uuid_to_uuid128(uuid1, &u1); bt_uuid_to_uuid128(uuid2, &u2); return bt_uuid128_cmp(&u1, &u2); } int bt_uuid16_cmp(const bt_uuid_t *uuid1, uint16_t uuid2) { if (!uuid1 || (uuid1->type != BT_UUID16)) return 0; return (uuid1->value.u16 == uuid2); } /* * convert the UUID to string, copying a maximum of n characters. */ int bt_uuid_to_string(const bt_uuid_t *uuid, char *str, size_t n) { bt_uuid_t tmp; unsigned int data0; unsigned short data1; unsigned short data2; unsigned short data3; unsigned int data4; unsigned short data5; const uint8_t *data; if (!uuid || uuid->type == BT_UUID_UNSPEC) { snprintf(str, n, "NULL"); return -EINVAL; } /* Convert to 128 Bit format */ bt_uuid_to_uuid128(uuid, &tmp); data = (uint8_t *) &tmp.value.u128; memcpy(&data0, &data[0], 4); memcpy(&data1, &data[4], 2); memcpy(&data2, &data[6], 2); memcpy(&data3, &data[8], 2); memcpy(&data4, &data[10], 4); memcpy(&data5, &data[14], 2); snprintf(str, n, "%.8x-%.4x-%.4x-%.4x-%.8x%.4x", ntohl(data0), ntohs(data1), ntohs(data2), ntohs(data3), ntohl(data4), ntohs(data5)); return 0; } static inline int is_uuid128(const char *string) { return (strlen(string) == 36 && string[8] == '-' && string[13] == '-' && string[18] == '-' && string[23] == '-'); } static inline int is_base_uuid128(const char *string) { uint16_t uuid; char dummy[2]; if (!is_uuid128(string)) return 0; return sscanf(string, "0000%04hx-0000-1000-8000-00805%1[fF]9%1[bB]34%1[fF]%1[bB]", &uuid, dummy, dummy, dummy, dummy) == 5; } static inline int is_uuid32(const char *string) { return (strlen(string) == 8 || strlen(string) == 10); } static inline int is_uuid16(const char *string) { return (strlen(string) == 4 || strlen(string) == 6); } static int bt_string_to_uuid16(bt_uuid_t *uuid, const char *string) { uint16_t u16; char *endptr = NULL; u16 = strtol(string, &endptr, 16); if (endptr && (*endptr == '\0' || *endptr == '-')) { bt_uuid16_create(uuid, u16); return 0; } return -EINVAL; } static int bt_string_to_uuid32(bt_uuid_t *uuid, const char *string) { uint32_t u32; char *endptr = NULL; u32 = strtoul(string, &endptr, 16); if (endptr && *endptr == '\0') { bt_uuid32_create(uuid, u32); return 0; } return -EINVAL; } static int bt_string_to_uuid128(bt_uuid_t *uuid, const char *string) { uint32_t data0, data4; uint16_t data1, data2, data3, data5; uint128_t u128; uint8_t *val = (uint8_t *) &u128; if (sscanf(string, "%08x-%04hx-%04hx-%04hx-%08x%04hx", &data0, &data1, &data2, &data3, &data4, &data5) != 6) return -EINVAL; data0 = htonl(data0); data1 = htons(data1); data2 = htons(data2); data3 = htons(data3); data4 = htonl(data4); data5 = htons(data5); memcpy(&val[0], &data0, 4); memcpy(&val[4], &data1, 2); memcpy(&val[6], &data2, 2); memcpy(&val[8], &data3, 2); memcpy(&val[10], &data4, 4); memcpy(&val[14], &data5, 2); bt_uuid128_create(uuid, u128); return 0; } int bt_string_to_uuid(bt_uuid_t *uuid, const char *string) { if (!string) return -EINVAL; if (is_base_uuid128(string)) return bt_string_to_uuid16(uuid, string + 4); else if (is_uuid128(string)) return bt_string_to_uuid128(uuid, string); else if (is_uuid32(string)) return bt_string_to_uuid32(uuid, string); else if (is_uuid16(string)) return bt_string_to_uuid16(uuid, string); return -EINVAL; } int bt_uuid_strcmp(const void *a, const void *b) { bt_uuid_t u1, u2; if (bt_string_to_uuid(&u1, a) < 0) return -EINVAL; if (bt_string_to_uuid(&u2, b) < 0) return -EINVAL; return bt_uuid_cmp(&u1, &u2); } int bt_uuid_to_le(const bt_uuid_t *src, void *dst) { bt_uuid_t uuid; switch (src->type) { case BT_UUID16: bt_put_le16(src->value.u16, dst); return 0; case BT_UUID32: bt_uuid32_to_uuid128(src, &uuid); src = &uuid; /* Fallthrough */ case BT_UUID128: /* Convert from 128-bit BE to LE */ bswap_128(&src->value.u128, dst); return 0; case BT_UUID_UNSPEC: default: return -EINVAL; } } bluez-5.82/lib/PaxHeaders/bluez.pc.in0000644000000000000000000000005012066112416014465 xustar0020 atime=1743515748 20 ctime=1743591275 bluez-5.82/lib/bluez.pc.in0000644000000000000000000000033112066112416014143 0ustar00rootrootprefix=@prefix@ exec_prefix=@exec_prefix@ libdir=@libdir@ includedir=@includedir@ Name: BlueZ Description: Bluetooth protocol stack for Linux Version: @VERSION@ Libs: -L${libdir} -lbluetooth Cflags: -I${includedir} bluez-5.82/lib/PaxHeaders/mgmt.h0000644000000000000000000000005014772767672013562 xustar0020 atime=1743515579 20 ctime=1743591277 bluez-5.82/lib/mgmt.h0000644000000000000000000007422614772767672013256 0ustar00rootroot/* SPDX-License-Identifier: GPL-2.0-or-later */ /* * BlueZ - Bluetooth protocol stack for Linux * * Copyright (C) 2010 Nokia Corporation * Copyright (C) 2010 Marcel Holtmann * * */ #ifndef __packed #define __packed __attribute__((packed)) #endif #ifndef BIT #define BIT(n) (1 << (n)) #endif #define MGMT_INDEX_NONE 0xFFFF #define MGMT_STATUS_SUCCESS 0x00 #define MGMT_STATUS_UNKNOWN_COMMAND 0x01 #define MGMT_STATUS_NOT_CONNECTED 0x02 #define MGMT_STATUS_FAILED 0x03 #define MGMT_STATUS_CONNECT_FAILED 0x04 #define MGMT_STATUS_AUTH_FAILED 0x05 #define MGMT_STATUS_NOT_PAIRED 0x06 #define MGMT_STATUS_NO_RESOURCES 0x07 #define MGMT_STATUS_TIMEOUT 0x08 #define MGMT_STATUS_ALREADY_CONNECTED 0x09 #define MGMT_STATUS_BUSY 0x0a #define MGMT_STATUS_REJECTED 0x0b #define MGMT_STATUS_NOT_SUPPORTED 0x0c #define MGMT_STATUS_INVALID_PARAMS 0x0d #define MGMT_STATUS_DISCONNECTED 0x0e #define MGMT_STATUS_NOT_POWERED 0x0f #define MGMT_STATUS_CANCELLED 0x10 #define MGMT_STATUS_INVALID_INDEX 0x11 #define MGMT_STATUS_RFKILLED 0x12 #define MGMT_STATUS_ALREADY_PAIRED 0x13 #define MGMT_STATUS_PERMISSION_DENIED 0x14 struct mgmt_hdr { uint16_t opcode; uint16_t index; uint16_t len; } __packed; #define MGMT_HDR_SIZE 6 struct mgmt_tlv { uint16_t type; uint8_t length; uint8_t value[]; } __packed; struct mgmt_addr_info { bdaddr_t bdaddr; uint8_t type; } __packed; #define MGMT_OP_READ_VERSION 0x0001 struct mgmt_rp_read_version { uint8_t version; uint16_t revision; } __packed; #define MGMT_OP_READ_COMMANDS 0x0002 struct mgmt_rp_read_commands { uint16_t num_commands; uint16_t num_events; uint16_t opcodes[0]; } __packed; #define MGMT_OP_READ_INDEX_LIST 0x0003 struct mgmt_rp_read_index_list { uint16_t num_controllers; uint16_t index[0]; } __packed; /* Reserve one extra byte for names in management messages so that they * are always guaranteed to be nul-terminated */ #define MGMT_MAX_NAME_LENGTH (248 + 1) #define MGMT_MAX_SHORT_NAME_LENGTH (10 + 1) #define MGMT_SETTING_POWERED BIT(0) #define MGMT_SETTING_CONNECTABLE BIT(1) #define MGMT_SETTING_FAST_CONNECTABLE BIT(2) #define MGMT_SETTING_DISCOVERABLE BIT(3) #define MGMT_SETTING_BONDABLE BIT(4) #define MGMT_SETTING_LINK_SECURITY BIT(5) #define MGMT_SETTING_SSP BIT(6) #define MGMT_SETTING_BREDR BIT(7) #define MGMT_SETTING_HS BIT(8) #define MGMT_SETTING_LE BIT(9) #define MGMT_SETTING_ADVERTISING BIT(10) #define MGMT_SETTING_SECURE_CONN BIT(11) #define MGMT_SETTING_DEBUG_KEYS BIT(12) #define MGMT_SETTING_PRIVACY BIT(13) #define MGMT_SETTING_CONFIGURATION BIT(14) #define MGMT_SETTING_STATIC_ADDRESS BIT(15) #define MGMT_SETTING_PHY_CONFIGURATION BIT(16) #define MGMT_SETTING_WIDEBAND_SPEECH BIT(17) #define MGMT_SETTING_CIS_CENTRAL BIT(18) #define MGMT_SETTING_CIS_PERIPHERAL BIT(19) #define MGMT_SETTING_ISO_BROADCASTER BIT(20) #define MGMT_SETTING_ISO_SYNC_RECEIVER BIT(21) #define MGMT_SETTING_LL_PRIVACY BIT(22) #define MGMT_OP_READ_INFO 0x0004 struct mgmt_rp_read_info { bdaddr_t bdaddr; uint8_t version; uint16_t manufacturer; uint32_t supported_settings; uint32_t current_settings; uint8_t dev_class[3]; uint8_t name[MGMT_MAX_NAME_LENGTH]; uint8_t short_name[MGMT_MAX_SHORT_NAME_LENGTH]; } __packed; struct mgmt_mode { uint8_t val; } __packed; struct mgmt_cod { uint8_t val[3]; } __packed; #define MGMT_OP_SET_POWERED 0x0005 #define MGMT_OP_SET_DISCOVERABLE 0x0006 struct mgmt_cp_set_discoverable { uint8_t val; uint16_t timeout; } __packed; #define MGMT_OP_SET_CONNECTABLE 0x0007 #define MGMT_OP_SET_FAST_CONNECTABLE 0x0008 #define MGMT_OP_SET_BONDABLE 0x0009 #define MGMT_OP_SET_LINK_SECURITY 0x000A #define MGMT_OP_SET_SSP 0x000B #define MGMT_OP_SET_HS 0x000C #define MGMT_OP_SET_LE 0x000D #define MGMT_OP_SET_DEV_CLASS 0x000E struct mgmt_cp_set_dev_class { uint8_t major; uint8_t minor; } __packed; #define MGMT_OP_SET_LOCAL_NAME 0x000F struct mgmt_cp_set_local_name { uint8_t name[MGMT_MAX_NAME_LENGTH]; uint8_t short_name[MGMT_MAX_SHORT_NAME_LENGTH]; } __packed; #define MGMT_OP_ADD_UUID 0x0010 struct mgmt_cp_add_uuid { uint8_t uuid[16]; uint8_t svc_hint; } __packed; #define MGMT_OP_REMOVE_UUID 0x0011 struct mgmt_cp_remove_uuid { uint8_t uuid[16]; } __packed; struct mgmt_link_key_info { struct mgmt_addr_info addr; uint8_t type; uint8_t val[16]; uint8_t pin_len; } __packed; #define MGMT_OP_LOAD_LINK_KEYS 0x0012 struct mgmt_cp_load_link_keys { uint8_t debug_keys; uint16_t key_count; struct mgmt_link_key_info keys[0]; } __packed; struct mgmt_ltk_info { struct mgmt_addr_info addr; uint8_t type; uint8_t central; uint8_t enc_size; uint16_t ediv; uint64_t rand; uint8_t val[16]; } __packed; #define MGMT_OP_LOAD_LONG_TERM_KEYS 0x0013 struct mgmt_cp_load_long_term_keys { uint16_t key_count; struct mgmt_ltk_info keys[0]; } __packed; #define MGMT_OP_DISCONNECT 0x0014 struct mgmt_cp_disconnect { struct mgmt_addr_info addr; } __packed; struct mgmt_rp_disconnect { struct mgmt_addr_info addr; } __packed; #define MGMT_OP_GET_CONNECTIONS 0x0015 struct mgmt_rp_get_connections { uint16_t conn_count; struct mgmt_addr_info addr[0]; } __packed; #define MGMT_OP_PIN_CODE_REPLY 0x0016 struct mgmt_cp_pin_code_reply { struct mgmt_addr_info addr; uint8_t pin_len; uint8_t pin_code[16]; } __packed; #define MGMT_OP_PIN_CODE_NEG_REPLY 0x0017 struct mgmt_cp_pin_code_neg_reply { struct mgmt_addr_info addr; } __packed; #define MGMT_OP_SET_IO_CAPABILITY 0x0018 struct mgmt_cp_set_io_capability { uint8_t io_capability; } __packed; #define MGMT_OP_PAIR_DEVICE 0x0019 struct mgmt_cp_pair_device { struct mgmt_addr_info addr; uint8_t io_cap; } __packed; struct mgmt_rp_pair_device { struct mgmt_addr_info addr; } __packed; #define MGMT_OP_CANCEL_PAIR_DEVICE 0x001A #define MGMT_OP_UNPAIR_DEVICE 0x001B struct mgmt_cp_unpair_device { struct mgmt_addr_info addr; uint8_t disconnect; } __packed; struct mgmt_rp_unpair_device { struct mgmt_addr_info addr; } __packed; #define MGMT_OP_USER_CONFIRM_REPLY 0x001C struct mgmt_cp_user_confirm_reply { struct mgmt_addr_info addr; } __packed; struct mgmt_rp_user_confirm_reply { struct mgmt_addr_info addr; } __packed; #define MGMT_OP_USER_CONFIRM_NEG_REPLY 0x001D #define MGMT_OP_USER_PASSKEY_REPLY 0x001E struct mgmt_cp_user_passkey_reply { struct mgmt_addr_info addr; uint32_t passkey; } __packed; struct mgmt_rp_user_passkey_reply { struct mgmt_addr_info addr; } __packed; #define MGMT_OP_USER_PASSKEY_NEG_REPLY 0x001F struct mgmt_cp_user_passkey_neg_reply { struct mgmt_addr_info addr; } __packed; #define MGMT_OP_READ_LOCAL_OOB_DATA 0x0020 struct mgmt_rp_read_local_oob_data { uint8_t hash192[16]; uint8_t rand192[16]; uint8_t hash256[16]; uint8_t rand256[16]; } __packed; #define MGMT_OP_ADD_REMOTE_OOB_DATA 0x0021 struct mgmt_cp_add_remote_oob_data { struct mgmt_addr_info addr; uint8_t hash192[16]; uint8_t rand192[16]; uint8_t hash256[16]; uint8_t rand256[16]; } __packed; #define MGMT_OP_REMOVE_REMOTE_OOB_DATA 0x0022 struct mgmt_cp_remove_remote_oob_data { struct mgmt_addr_info addr; } __packed; #define MGMT_OP_START_DISCOVERY 0x0023 struct mgmt_cp_start_discovery { uint8_t type; } __packed; #define MGMT_OP_STOP_DISCOVERY 0x0024 struct mgmt_cp_stop_discovery { uint8_t type; } __packed; #define MGMT_OP_CONFIRM_NAME 0x0025 struct mgmt_cp_confirm_name { struct mgmt_addr_info addr; uint8_t name_known; } __packed; struct mgmt_rp_confirm_name { struct mgmt_addr_info addr; } __packed; #define MGMT_OP_BLOCK_DEVICE 0x0026 struct mgmt_cp_block_device { struct mgmt_addr_info addr; } __packed; #define MGMT_OP_UNBLOCK_DEVICE 0x0027 struct mgmt_cp_unblock_device { struct mgmt_addr_info addr; } __packed; #define MGMT_OP_SET_DEVICE_ID 0x0028 struct mgmt_cp_set_device_id { uint16_t source; uint16_t vendor; uint16_t product; uint16_t version; } __packed; #define MGMT_OP_SET_ADVERTISING 0x0029 #define MGMT_OP_SET_BREDR 0x002A #define MGMT_OP_SET_STATIC_ADDRESS 0x002B struct mgmt_cp_set_static_address { bdaddr_t bdaddr; } __packed; #define MGMT_OP_SET_SCAN_PARAMS 0x002C struct mgmt_cp_set_scan_params { uint16_t interval; uint16_t window; } __packed; #define MGMT_OP_SET_SECURE_CONN 0x002D #define MGMT_OP_SET_DEBUG_KEYS 0x002E struct mgmt_irk_info { struct mgmt_addr_info addr; uint8_t val[16]; } __packed; #define MGMT_OP_SET_PRIVACY 0x002F struct mgmt_cp_set_privacy { uint8_t privacy; uint8_t irk[16]; } __packed; #define MGMT_OP_LOAD_IRKS 0x0030 struct mgmt_cp_load_irks { uint16_t irk_count; struct mgmt_irk_info irks[0]; } __packed; #define MGMT_OP_GET_CONN_INFO 0x0031 struct mgmt_cp_get_conn_info { struct mgmt_addr_info addr; } __packed; struct mgmt_rp_get_conn_info { struct mgmt_addr_info addr; int8_t rssi; int8_t tx_power; int8_t max_tx_power; } __packed; #define MGMT_OP_GET_CLOCK_INFO 0x0032 struct mgmt_cp_get_clock_info { struct mgmt_addr_info addr; } __packed; struct mgmt_rp_get_clock_info { struct mgmt_addr_info addr; uint32_t local_clock; uint32_t piconet_clock; uint16_t accuracy; } __packed; #define MGMT_OP_ADD_DEVICE 0x0033 struct mgmt_cp_add_device { struct mgmt_addr_info addr; uint8_t action; } __packed; struct mgmt_rp_add_device { struct mgmt_addr_info addr; } __packed; #define MGMT_OP_REMOVE_DEVICE 0x0034 struct mgmt_cp_remove_device { struct mgmt_addr_info addr; } __packed; struct mgmt_rp_remove_device { struct mgmt_addr_info addr; } __packed; struct mgmt_conn_param { struct mgmt_addr_info addr; uint16_t min_interval; uint16_t max_interval; uint16_t latency; uint16_t timeout; } __packed; #define MGMT_OP_LOAD_CONN_PARAM 0x0035 struct mgmt_cp_load_conn_param { uint16_t param_count; struct mgmt_conn_param params[0]; } __packed; #define MGMT_OP_READ_UNCONF_INDEX_LIST 0x0036 struct mgmt_rp_read_unconf_index_list { uint16_t num_controllers; uint16_t index[0]; } __packed; #define MGMT_OPTION_EXTERNAL_CONFIG 0x00000001 #define MGMT_OPTION_PUBLIC_ADDRESS 0x00000002 #define MGMT_OP_READ_CONFIG_INFO 0x0037 struct mgmt_rp_read_config_info { uint16_t manufacturer; uint32_t supported_options; uint32_t missing_options; } __packed; #define MGMT_OP_SET_EXTERNAL_CONFIG 0x0038 struct mgmt_cp_set_external_config { uint8_t config; } __packed; #define MGMT_OP_SET_PUBLIC_ADDRESS 0x0039 struct mgmt_cp_set_public_address { bdaddr_t bdaddr; } __packed; #define MGMT_OP_START_SERVICE_DISCOVERY 0x003A struct mgmt_cp_start_service_discovery { uint8_t type; int8_t rssi; uint16_t uuid_count; uint8_t uuids[0][16]; } __packed; #define MGMT_OP_READ_LOCAL_OOB_EXT_DATA 0x003B struct mgmt_cp_read_local_oob_ext_data { uint8_t type; } __packed; struct mgmt_rp_read_local_oob_ext_data { uint8_t type; uint16_t eir_len; uint8_t eir[0]; } __packed; #define MGMT_OP_READ_EXT_INDEX_LIST 0x003C struct mgmt_rp_read_ext_index_list { uint16_t num_controllers; struct { uint16_t index; uint8_t type; uint8_t bus; } entry[0]; } __packed; #define MGMT_OP_READ_ADV_FEATURES 0x003D struct mgmt_rp_read_adv_features { uint32_t supported_flags; uint8_t max_adv_data_len; uint8_t max_scan_rsp_len; uint8_t max_instances; uint8_t num_instances; uint8_t instance[0]; } __packed; #define MGMT_OP_ADD_ADVERTISING 0x003E struct mgmt_cp_add_advertising { uint8_t instance; uint32_t flags; uint16_t duration; uint16_t timeout; uint8_t adv_data_len; uint8_t scan_rsp_len; uint8_t data[0]; } __packed; struct mgmt_rp_add_advertising { uint8_t instance; } __packed; #define MGMT_ADV_FLAG_CONNECTABLE BIT(0) #define MGMT_ADV_FLAG_DISCOV BIT(1) #define MGMT_ADV_FLAG_LIMITED_DISCOV BIT(2) #define MGMT_ADV_FLAG_MANAGED_FLAGS BIT(3) #define MGMT_ADV_FLAG_TX_POWER BIT(4) #define MGMT_ADV_FLAG_APPEARANCE BIT(5) #define MGMT_ADV_FLAG_LOCAL_NAME BIT(6) #define MGMT_ADV_FLAG_SEC_1M BIT(7) #define MGMT_ADV_FLAG_SEC_2M BIT(8) #define MGMT_ADV_FLAG_SEC_CODED BIT(9) #define MGMT_ADV_FLAG_CAN_SET_TX_POWER BIT(10) #define MGMT_ADV_FLAG_HW_OFFLOAD BIT(11) #define MGMT_ADV_PARAM_DURATION BIT(12) #define MGMT_ADV_PARAM_TIMEOUT BIT(13) #define MGMT_ADV_PARAM_INTERVALS BIT(14) #define MGMT_ADV_PARAM_TX_POWER BIT(15) #define MGMT_ADV_PARAM_SCAN_RSP BIT(16) #define MGMT_ADV_FLAG_SEC_MASK (MGMT_ADV_FLAG_SEC_1M | MGMT_ADV_FLAG_SEC_2M | \ MGMT_ADV_FLAG_SEC_CODED) #define MGMT_OP_REMOVE_ADVERTISING 0x003F struct mgmt_cp_remove_advertising { uint8_t instance; } __packed; #define MGMT_REMOVE_ADVERTISING_SIZE 1 struct mgmt_rp_remove_advertising { uint8_t instance; } __packed; #define MGMT_OP_GET_ADV_SIZE_INFO 0x0040 struct mgmt_cp_get_adv_size_info { uint8_t instance; uint32_t flags; } __packed; #define MGMT_GET_ADV_SIZE_INFO_SIZE 5 struct mgmt_rp_get_adv_size_info { uint8_t instance; uint32_t flags; uint8_t max_adv_data_len; uint8_t max_scan_rsp_len; } __packed; #define MGMT_OP_START_LIMITED_DISCOVERY 0x0041 #define MGMT_OP_READ_EXT_INFO 0x0042 struct mgmt_rp_read_ext_info { bdaddr_t bdaddr; uint8_t version; uint16_t manufacturer; uint32_t supported_settings; uint32_t current_settings; uint16_t eir_len; uint8_t eir[0]; } __packed; #define MGMT_OP_SET_APPEARANCE 0x0043 struct mgmt_cp_set_appearance { uint16_t appearance; } __packed; #define MGMT_OP_GET_PHY_CONFIGURATION 0x0044 struct mgmt_rp_get_phy_confguration { uint32_t supported_phys; uint32_t configurable_phys; uint32_t selected_phys; } __packed; #define MGMT_PHY_BR_1M_1SLOT BIT(0) #define MGMT_PHY_BR_1M_3SLOT BIT(1) #define MGMT_PHY_BR_1M_5SLOT BIT(2) #define MGMT_PHY_EDR_2M_1SLOT BIT(3) #define MGMT_PHY_EDR_2M_3SLOT BIT(4) #define MGMT_PHY_EDR_2M_5SLOT BIT(5) #define MGMT_PHY_EDR_3M_1SLOT BIT(6) #define MGMT_PHY_EDR_3M_3SLOT BIT(7) #define MGMT_PHY_EDR_3M_5SLOT BIT(8) #define MGMT_PHY_LE_1M_TX BIT(9) #define MGMT_PHY_LE_1M_RX BIT(10) #define MGMT_PHY_LE_2M_TX BIT(11) #define MGMT_PHY_LE_2M_RX BIT(12) #define MGMT_PHY_LE_CODED_TX BIT(13) #define MGMT_PHY_LE_CODED_RX BIT(14) #define MGMT_PHY_LE_TX_MASK (MGMT_PHY_LE_1M_TX | MGMT_PHY_LE_2M_TX | \ MGMT_PHY_LE_CODED_TX) #define MGMT_PHY_LE_RX_MASK (MGMT_PHY_LE_1M_RX | MGMT_PHY_LE_2M_RX | \ MGMT_PHY_LE_CODED_RX) #define MGMT_OP_SET_PHY_CONFIGURATION 0x0045 struct mgmt_cp_set_phy_confguration { uint32_t selected_phys; } __packed; #define MGMT_OP_SET_BLOCKED_KEYS 0x0046 #define MGMT_OP_SET_WIDEBAND_SPEECH 0x0047 #define HCI_BLOCKED_KEY_TYPE_LINKKEY 0x00 #define HCI_BLOCKED_KEY_TYPE_LTK 0x01 #define HCI_BLOCKED_KEY_TYPE_IRK 0x02 struct mgmt_blocked_key_info { uint8_t type; uint8_t val[16]; } __packed; struct mgmt_cp_set_blocked_keys { uint16_t key_count; struct mgmt_blocked_key_info keys[0]; } __packed; #define MGMT_CAP_SEC_FLAGS 0x01 #define MGMT_CAP_MAX_ENC_KEY_SIZE 0x02 #define MGMT_CAP_SMP_MAX_ENC_KEY_SIZE 0x03 #define MGMT_CAP_LE_TX_PWR 0x04 #define MGMT_OP_READ_CONTROLLER_CAP 0x0048 #define MGMT_READ_CONTROLLER_CAP_SIZE 0 struct mgmt_rp_read_controller_cap { uint16_t cap_len; uint8_t cap[0]; } __packed; #define MGMT_OP_READ_EXP_FEATURES_INFO 0x0049 struct mgmt_rp_read_exp_features_info { uint16_t feature_count; struct { uint8_t uuid[16]; uint32_t flags; } features[]; } __packed; #define MGMT_OP_SET_EXP_FEATURE 0x004a struct mgmt_cp_set_exp_feature { uint8_t uuid[16]; uint8_t action; } __packed; #define MGMT_SET_EXP_FEATURE_SIZE 17 struct mgmt_rp_set_exp_feature { uint8_t uuid[16]; uint32_t flags; } __packed; #define MGMT_OP_READ_DEF_SYSTEM_CONFIG 0x004b struct mgmt_rp_read_default_system_config { uint8_t parameters[0]; /* mgmt_tlv */ } __packed; #define MGMT_OP_SET_DEF_SYSTEM_CONFIG 0x004c struct mgmt_cp_set_default_system_config { uint8_t parameters[0]; /* mgmt_tlv */ } __packed; #define MGMT_OP_READ_DEF_RUNTIME_CONFIG 0x004d struct mgmt_rp_read_default_runtime_config { uint8_t parameters[0]; /* mgmt_tlv */ } __packed; #define MGMT_OP_SET_DEF_RUNTIME_CONFIG 0x004e struct mgmt_cp_set_default_runtime_config { uint8_t parameters[0]; /* mgmt_tlv */ } __packed; #define MGMT_OP_GET_DEVICE_FLAGS 0x004F #define MGMT_GET_DEVICE_FLAGS_SIZE 7 struct mgmt_cp_get_device_flags { struct mgmt_addr_info addr; } __packed; struct mgmt_rp_get_device_flags { struct mgmt_addr_info addr; uint32_t supported_flags; uint32_t current_flags; } __packed; #define DEVICE_FLAG_REMOTE_WAKEUP BIT(0) #define DEVICE_FLAG_DEVICE_PRIVACY BIT(1) #define DEVICE_FLAG_ADDRESS_RESOLUTION BIT(2) #define MGMT_OP_SET_DEVICE_FLAGS 0x0050 #define MGMT_SET_DEVICE_FLAGS_SIZE 11 struct mgmt_cp_set_device_flags { struct mgmt_addr_info addr; uint32_t current_flags; } __packed; struct mgmt_rp_set_device_flags { struct mgmt_addr_info addr; } __packed; #define MGMT_ADV_MONITOR_FEATURE_MASK_OR_PATTERNS (1 << 0) #define MGMT_OP_READ_ADV_MONITOR_FEATURES 0x0051 struct mgmt_rp_read_adv_monitor_features { uint32_t supported_features; uint32_t enabled_features; uint16_t max_num_handles; uint8_t max_num_patterns; uint16_t num_handles; uint16_t handles[0]; } __packed; struct mgmt_adv_pattern { uint8_t ad_type; uint8_t offset; uint8_t length; uint8_t value[31]; } __packed; #define MGMT_OP_ADD_ADV_PATTERNS_MONITOR 0x0052 struct mgmt_cp_add_adv_monitor { uint8_t pattern_count; struct mgmt_adv_pattern patterns[0]; } __packed; struct mgmt_rp_add_adv_patterns_monitor { uint16_t monitor_handle; } __packed; #define MGMT_OP_REMOVE_ADV_MONITOR 0x0053 struct mgmt_cp_remove_adv_monitor { uint16_t monitor_handle; } __packed; struct mgmt_rp_remove_adv_monitor { uint16_t monitor_handle; } __packed; #define MGMT_OP_ADD_EXT_ADV_PARAMS 0x0054 struct mgmt_cp_add_ext_adv_params { uint8_t instance; uint32_t flags; uint16_t duration; uint16_t timeout; uint32_t min_interval; uint32_t max_interval; int8_t tx_power; } __packed; struct mgmt_rp_add_ext_adv_params { uint8_t instance; int8_t tx_power; uint8_t max_adv_data_len; uint8_t max_scan_rsp_len; } __packed; #define MGMT_OP_ADD_EXT_ADV_DATA 0x0055 struct mgmt_cp_add_ext_adv_data { uint8_t instance; uint8_t adv_data_len; uint8_t scan_rsp_len; uint8_t data[0]; } __packed; struct mgmt_rp_add_ext_adv_data { uint8_t instance; } __packed; struct mgmt_adv_rssi_thresholds { int8_t high_threshold; uint16_t high_threshold_timeout; int8_t low_threshold; uint16_t low_threshold_timeout; uint8_t sampling_period; } __packed; #define MGMT_OP_ADD_ADV_PATTERNS_MONITOR_RSSI 0x0056 struct mgmt_cp_add_adv_patterns_monitor_rssi { struct mgmt_adv_rssi_thresholds rssi; uint8_t pattern_count; struct mgmt_adv_pattern patterns[0]; } __packed; #define MGMT_OP_SET_MESH_RECEIVER 0x0057 struct mgmt_cp_set_mesh { uint8_t enable; uint16_t window; uint16_t period; uint8_t num_ad_types; uint8_t ad_types[]; } __packed; #define MGMT_OP_MESH_READ_FEATURES 0x0058 struct mgmt_rp_mesh_read_features { uint16_t index; uint8_t max_handles; uint8_t used_handles; uint8_t handles[]; } __packed; #define MGMT_OP_MESH_SEND 0x0059 struct mgmt_cp_mesh_send { struct mgmt_addr_info addr; uint64_t instant; uint16_t delay; uint8_t cnt; uint8_t adv_data_len; uint8_t adv_data[]; } __packed; #define MGMT_OP_MESH_SEND_CANCEL 0x005A struct mgmt_cp_mesh_send_cancel { uint8_t handle; } __packed; #define MGMT_OP_HCI_CMD_SYNC 0x005B struct mgmt_cp_hci_cmd_sync { uint16_t opcode; uint8_t event; uint8_t timeout; uint16_t params_len; uint8_t params[]; } __packed; #define MGMT_EV_CMD_COMPLETE 0x0001 struct mgmt_ev_cmd_complete { uint16_t opcode; uint8_t status; uint8_t data[0]; } __packed; #define MGMT_EV_CMD_STATUS 0x0002 struct mgmt_ev_cmd_status { uint16_t opcode; uint8_t status; } __packed; #define MGMT_EV_CONTROLLER_ERROR 0x0003 struct mgmt_ev_controller_error { uint8_t error_code; } __packed; #define MGMT_EV_INDEX_ADDED 0x0004 #define MGMT_EV_INDEX_REMOVED 0x0005 #define MGMT_EV_NEW_SETTINGS 0x0006 #define MGMT_EV_CLASS_OF_DEV_CHANGED 0x0007 struct mgmt_ev_class_of_dev_changed { uint8_t dev_class[3]; } __packed; #define MGMT_EV_LOCAL_NAME_CHANGED 0x0008 struct mgmt_ev_local_name_changed { uint8_t name[MGMT_MAX_NAME_LENGTH]; uint8_t short_name[MGMT_MAX_SHORT_NAME_LENGTH]; } __packed; #define MGMT_EV_NEW_LINK_KEY 0x0009 struct mgmt_ev_new_link_key { uint8_t store_hint; struct mgmt_link_key_info key; } __packed; #define MGMT_EV_NEW_LONG_TERM_KEY 0x000A struct mgmt_ev_new_long_term_key { uint8_t store_hint; struct mgmt_ltk_info key; } __packed; #define MGMT_EV_DEVICE_CONNECTED 0x000B struct mgmt_ev_device_connected { struct mgmt_addr_info addr; uint32_t flags; uint16_t eir_len; uint8_t eir[0]; } __packed; #define MGMT_DEV_DISCONN_UNKNOWN 0x00 #define MGMT_DEV_DISCONN_TIMEOUT 0x01 #define MGMT_DEV_DISCONN_LOCAL_HOST 0x02 #define MGMT_DEV_DISCONN_REMOTE 0x03 #define MGMT_DEV_DISCONN_LOCAL_HOST_SUSPEND 0x05 #define MGMT_EV_DEVICE_DISCONNECTED 0x000C struct mgmt_ev_device_disconnected { struct mgmt_addr_info addr; uint8_t reason; } __packed; #define MGMT_EV_CONNECT_FAILED 0x000D struct mgmt_ev_connect_failed { struct mgmt_addr_info addr; uint8_t status; } __packed; #define MGMT_EV_PIN_CODE_REQUEST 0x000E struct mgmt_ev_pin_code_request { struct mgmt_addr_info addr; uint8_t secure; } __packed; #define MGMT_EV_USER_CONFIRM_REQUEST 0x000F struct mgmt_ev_user_confirm_request { struct mgmt_addr_info addr; uint8_t confirm_hint; uint32_t value; } __packed; #define MGMT_EV_USER_PASSKEY_REQUEST 0x0010 struct mgmt_ev_user_passkey_request { struct mgmt_addr_info addr; } __packed; #define MGMT_EV_AUTH_FAILED 0x0011 struct mgmt_ev_auth_failed { struct mgmt_addr_info addr; uint8_t status; } __packed; #define MGMT_DEV_FOUND_CONFIRM_NAME BIT(0) #define MGMT_DEV_FOUND_LEGACY_PAIRING BIT(1) #define MGMT_DEV_FOUND_NOT_CONNECTABLE BIT(2) #define MGMT_DEV_FOUND_INITIATED_CONN BIT(3) #define MGMT_DEV_FOUND_NAME_REQUEST_FAILED BIT(4) #define MGMT_DEV_FOUND_SCAN_RSP BIT(5) #define MGMT_EV_DEVICE_FOUND 0x0012 struct mgmt_ev_device_found { struct mgmt_addr_info addr; int8_t rssi; uint32_t flags; uint16_t eir_len; uint8_t eir[0]; } __packed; #define MGMT_EV_DISCOVERING 0x0013 struct mgmt_ev_discovering { uint8_t type; uint8_t discovering; } __packed; #define MGMT_EV_DEVICE_BLOCKED 0x0014 struct mgmt_ev_device_blocked { struct mgmt_addr_info addr; } __packed; #define MGMT_EV_DEVICE_UNBLOCKED 0x0015 struct mgmt_ev_device_unblocked { struct mgmt_addr_info addr; } __packed; #define MGMT_EV_DEVICE_UNPAIRED 0x0016 struct mgmt_ev_device_unpaired { struct mgmt_addr_info addr; } __packed; #define MGMT_EV_PASSKEY_NOTIFY 0x0017 struct mgmt_ev_passkey_notify { struct mgmt_addr_info addr; uint32_t passkey; uint8_t entered; } __packed; #define MGMT_EV_NEW_IRK 0x0018 struct mgmt_ev_new_irk { uint8_t store_hint; bdaddr_t rpa; struct mgmt_irk_info key; } __packed; struct mgmt_csrk_info { struct mgmt_addr_info addr; uint8_t type; uint8_t val[16]; } __packed; #define MGMT_EV_NEW_CSRK 0x0019 struct mgmt_ev_new_csrk { uint8_t store_hint; struct mgmt_csrk_info key; } __packed; #define MGMT_EV_DEVICE_ADDED 0x001a struct mgmt_ev_device_added { struct mgmt_addr_info addr; uint8_t action; } __packed; #define MGMT_EV_DEVICE_REMOVED 0x001b struct mgmt_ev_device_removed { struct mgmt_addr_info addr; } __packed; #define MGMT_EV_NEW_CONN_PARAM 0x001c struct mgmt_ev_new_conn_param { struct mgmt_addr_info addr; uint8_t store_hint; uint16_t min_interval; uint16_t max_interval; uint16_t latency; uint16_t timeout; } __packed; #define MGMT_EV_UNCONF_INDEX_ADDED 0x001d #define MGMT_EV_UNCONF_INDEX_REMOVED 0x001e #define MGMT_EV_NEW_CONFIG_OPTIONS 0x001f #define MGMT_EV_EXT_INDEX_ADDED 0x0020 struct mgmt_ev_ext_index_added { uint8_t type; uint8_t bus; } __packed; #define MGMT_EV_EXT_INDEX_REMOVED 0x0021 struct mgmt_ev_ext_index_removed { uint8_t type; uint8_t bus; } __packed; #define MGMT_EV_LOCAL_OOB_DATA_UPDATED 0x0022 struct mgmt_ev_local_oob_data_updated { uint8_t type; uint16_t eir_len; uint8_t eir[0]; } __packed; #define MGMT_EV_ADVERTISING_ADDED 0x0023 struct mgmt_ev_advertising_added { uint8_t instance; } __packed; #define MGMT_EV_ADVERTISING_REMOVED 0x0024 struct mgmt_ev_advertising_removed { uint8_t instance; } __packed; #define MGMT_EV_EXT_INFO_CHANGED 0x0025 struct mgmt_ev_ext_info_changed { uint16_t eir_len; uint8_t eir[0]; } __packed; #define MGMT_EV_PHY_CONFIGURATION_CHANGED 0x0026 struct mgmt_ev_phy_configuration_changed { uint16_t selected_phys; } __packed; #define MGMT_EV_EXP_FEATURE_CHANGE 0x0027 struct mgmt_ev_exp_feature_changed { uint8_t uuid[16]; uint32_t flags; } __packed; #define MGMT_EV_DEVICE_FLAGS_CHANGED 0x002a struct mgmt_ev_device_flags_changed { struct mgmt_addr_info addr; uint32_t supported_flags; uint32_t current_flags; } __packed; #define MGMT_EV_ADV_MONITOR_ADDED 0x002b struct mgmt_ev_adv_monitor_added { uint16_t monitor_handle; } __packed; #define MGMT_EV_ADV_MONITOR_REMOVED 0x002c struct mgmt_ev_adv_monitor_removed { uint16_t monitor_handle; } __packed; #define MGMT_EV_CONTROLLER_SUSPEND 0x002d struct mgmt_ev_controller_suspend { uint8_t suspend_state; } __packed; #define MGMT_EV_CONTROLLER_RESUME 0x002e struct mgmt_ev_controller_resume { struct mgmt_addr_info addr; uint8_t wake_reason; } __packed; #define MGMT_EV_ADV_MONITOR_DEVICE_FOUND 0x002f struct mgmt_ev_adv_monitor_device_found { uint16_t monitor_handle; struct mgmt_addr_info addr; int8_t rssi; uint32_t flags; uint16_t ad_data_len; uint8_t ad_data[0]; } __packed; #define MGMT_EV_ADV_MONITOR_DEVICE_LOST 0x0030 struct mgmt_ev_adv_monitor_device_lost { uint16_t monitor_handle; struct mgmt_addr_info addr; } __packed; #define MGMT_EV_MESH_DEVICE_FOUND 0x0031 struct mgmt_ev_mesh_device_found { struct mgmt_addr_info addr; int8_t rssi; uint64_t instant; uint32_t flags; uint16_t eir_len; uint8_t eir[]; } __packed; #define MGMT_EV_MESH_PACKET_CMPLT 0x0032 struct mgmt_ev_mesh_pkt_cmplt { uint8_t handle; } __packed; static const char *mgmt_op[] = { "<0x0000>", "Read Version", "Read Commands", "Read Index List", "Read Controller Info", "Set Powered", "Set Discoverable", "Set Connectable", "Set Fast Connectable", /* 0x0008 */ "Set Bondable", "Set Link Security", "Set Secure Simple Pairing", "Set High Speed", "Set Low Energy", "Set Dev Class", "Set Local Name", "Add UUID", /* 0x0010 */ "Remove UUID", "Load Link Keys", "Load Long Term Keys", "Disconnect", "Get Connections", "PIN Code Reply", "PIN Code Neg Reply", "Set IO Capability", /* 0x0018 */ "Pair Device", "Cancel Pair Device", "Unpair Device", "User Confirm Reply", "User Confirm Neg Reply", "User Passkey Reply", "User Passkey Neg Reply", "Read Local OOB Data", /* 0x0020 */ "Add Remote OOB Data", "Remove Remove OOB Data", "Start Discovery", "Stop Discovery", "Confirm Name", "Block Device", "Unblock Device", "Set Device ID", /* 0x0028 */ "Set Advertising", "Set BR/EDR", "Set Static Address", "Set Scan Parameters", "Set Secure Connections", "Set Debug Keys", "Set Privacy", "Load Identity Resolving Keys", /* 0x0030 */ "Get Connection Information", "Get Clock Information", "Add Device", "Remove Device", "Load Connection Parameters", "Read Unconfigured Index List", "Read Controller Configuration Information", "Set External Configuration", /* 0x0038 */ "Set Public Address", "Start Service Discovery", "Read Local Out Of Band Extended Data", "Read Extended Controller Index List", "Read Advertising Features", "Add Advertising", "Remove Advertising", "Get Advertising Size Information", /* 0x0040 */ "Start Limited Discovery", "Read Extended Controller Information", "Set Appearance", "Get PHY Configuration", "Set PHY Configuration", "Set Blocked Keys", "Set Wideband Speech", "Read Controller Capabilities Information", /* 0x0048 */ "Read Experimental Features Information", "Set Experimental Feature", "Read Default System Configuration", "Set Default System Configuration", "Read Default Runtime Configuration", "Set Default Runtime Configuration", "Get Device Flags", "Set Device Flags", /* 0x0050 */ "Read Advertisement Monitor Features", "Add Advertisement Patterns Monitor", "Remove Advertisement Monitor", "Add Extended Advertisement Parameters", /* 0x0054 */ "Add Extended Advertisement Data", "Add Advertisement Patterns Monitor RSSI", "Set Mesh Receiver", "Read Mesh Features", "Mesh Send", "Mesh Send Cancel", }; static const char *mgmt_ev[] = { "<0x0000>", "Command Complete", "Command Status", "Controller Error", "Index Added", "Index Removed", "New Settings", "Class of Device Changed", "Local Name Changed", /* 0x0008 */ "New Link Key", "New Long Term Key", "Device Connected", "Device Disconnected", "Connect Failed", "PIN Code Request", "User Confirm Request", "User Passkey Request", /* 0x0010 */ "Authentication Failed", "Device Found", "Discovering", "Device Blocked", "Device Unblocked", "Device Unpaired", "Passkey Notify", "New Identity Resolving Key", /* 0x0018 */ "New Signature Resolving Key", "Device Added", "Device Removed", "New Connection Parameter", "Unconfigured Index Added", "Unconfigured Index Removed", "New Configuration Options", "Extended Index Added", /* 0x0020 */ "Extended Index Removed", "Local Out Of Band Extended Data Updated", "Advertising Added", "Advertising Removed", "Extended Controller Information Changed", "PHY Configuration Changed", "Experimental Feature Changed", "Default System Configuration Changed", /* 0x0028 */ "Default Runtime Configuration Changed", "Device Flags Changed", "Advertisement Monitor Added", /* 0x002b */ "Advertisement Monitor Removed", "Controller Suspend", "Controller Resume", "Advertisement Monitor Device Found", /* 0x002f */ "Advertisement Monitor Device Lost", "Mesh Packet Found", "Mesh Packet Complete", "PA Sync Established", "BIG Sync Established", "BIG Sync Lost", }; static const char *mgmt_status[] = { "Success", "Unknown Command", "Not Connected", "Failed", "Connect Failed", "Authentication Failed", "Not Paired", "No Resources", "Timeout", "Already Connected", "Busy", "Rejected", "Not Supported", "Invalid Parameters", "Disconnected", "Not Powered", "Cancelled", "Invalid Index", "Blocked through rfkill", "Already Paired", "Permission Denied", "Connection Not Established", }; #ifndef NELEM #define NELEM(x) (sizeof(x) / sizeof((x)[0])) #endif static inline const char *mgmt_opstr(uint16_t op) { if (op >= NELEM(mgmt_op)) return ""; return mgmt_op[op]; } static inline const char *mgmt_evstr(uint16_t ev) { if (ev >= NELEM(mgmt_ev)) return ""; return mgmt_ev[ev]; } static inline const char *mgmt_errstr(uint8_t status) { if (status >= NELEM(mgmt_status)) return ""; return mgmt_status[status]; } bluez-5.82/lib/PaxHeaders/bluetooth.h0000644000000000000000000000005014766002272014600 xustar0020 atime=1743515578 20 ctime=1743591275 bluez-5.82/lib/bluetooth.h0000644000000000000000000002666614766002272014301 0ustar00rootroot/* SPDX-License-Identifier: GPL-2.0-or-later */ /* * * BlueZ - Bluetooth protocol stack for Linux * * Copyright (C) 2000-2001 Qualcomm Incorporated * Copyright (C) 2002-2003 Maxim Krasnyansky * Copyright (C) 2002-2010 Marcel Holtmann * Copyright 2023 NXP * * */ #ifndef __BLUETOOTH_H #define __BLUETOOTH_H #ifdef __cplusplus extern "C" { #endif #include #include #include #include #include #include #ifndef AF_BLUETOOTH #define AF_BLUETOOTH 31 #define PF_BLUETOOTH AF_BLUETOOTH #endif #define BTPROTO_L2CAP 0 #define BTPROTO_HCI 1 #define BTPROTO_SCO 2 #define BTPROTO_RFCOMM 3 #define BTPROTO_BNEP 4 #define BTPROTO_CMTP 5 #define BTPROTO_HIDP 6 #define BTPROTO_AVDTP 7 #define BTPROTO_ISO 8 #define SOL_HCI 0 #define SOL_L2CAP 6 #define SOL_SCO 17 #define SOL_RFCOMM 18 #ifndef SOL_BLUETOOTH #define SOL_BLUETOOTH 274 #endif #define BT_SECURITY 4 struct bt_security { uint8_t level; uint8_t key_size; }; #define BT_SECURITY_SDP 0 #define BT_SECURITY_LOW 1 #define BT_SECURITY_MEDIUM 2 #define BT_SECURITY_HIGH 3 #define BT_SECURITY_FIPS 4 #define BT_DEFER_SETUP 7 #define BT_FLUSHABLE 8 #define BT_FLUSHABLE_OFF 0 #define BT_FLUSHABLE_ON 1 #define BT_POWER 9 struct bt_power { uint8_t force_active; }; #define BT_POWER_FORCE_ACTIVE_OFF 0 #define BT_POWER_FORCE_ACTIVE_ON 1 #define BT_CHANNEL_POLICY 10 /* BR/EDR only (default policy) * AMP controllers cannot be used. * Channel move requests from the remote device are denied. * If the L2CAP channel is currently using AMP, move the channel to BR/EDR. */ #define BT_CHANNEL_POLICY_BREDR_ONLY 0 /* BR/EDR Preferred * Allow use of AMP controllers. * If the L2CAP channel is currently on AMP, move it to BR/EDR. * Channel move requests from the remote device are allowed. */ #define BT_CHANNEL_POLICY_BREDR_PREFERRED 1 /* AMP Preferred * Allow use of AMP controllers * If the L2CAP channel is currently on BR/EDR and AMP controller * resources are available, initiate a channel move to AMP. * Channel move requests from the remote device are allowed. * If the L2CAP socket has not been connected yet, try to create * and configure the channel directly on an AMP controller rather * than BR/EDR. */ #define BT_CHANNEL_POLICY_AMP_PREFERRED 2 #define BT_VOICE 11 struct bt_voice { uint16_t setting; }; #define BT_SNDMTU 12 #define BT_RCVMTU 13 #define BT_VOICE_TRANSPARENT 0x0003 #define BT_VOICE_CVSD_16BIT 0x0060 #define BT_VOICE_TRANSPARENT_16BIT 0x0063 #define BT_PHY 14 #define BT_PHY_BR_1M_1SLOT 0x00000001 #define BT_PHY_BR_1M_3SLOT 0x00000002 #define BT_PHY_BR_1M_5SLOT 0x00000004 #define BT_PHY_EDR_2M_1SLOT 0x00000008 #define BT_PHY_EDR_2M_3SLOT 0x00000010 #define BT_PHY_EDR_2M_5SLOT 0x00000020 #define BT_PHY_EDR_3M_1SLOT 0x00000040 #define BT_PHY_EDR_3M_3SLOT 0x00000080 #define BT_PHY_EDR_3M_5SLOT 0x00000100 #define BT_PHY_LE_1M_TX 0x00000200 #define BT_PHY_LE_1M_RX 0x00000400 #define BT_PHY_LE_2M_TX 0x00000800 #define BT_PHY_LE_2M_RX 0x00001000 #define BT_PHY_LE_CODED_TX 0x00002000 #define BT_PHY_LE_CODED_RX 0x00004000 #define BT_MODE 15 #define BT_MODE_BASIC 0x00 #define BT_MODE_ERTM 0x01 #define BT_MODE_STREAMING 0x02 #define BT_MODE_LE_FLOWCTL 0x03 #define BT_MODE_EXT_FLOWCTL 0x04 #define BT_PKT_STATUS 16 #define BT_SCM_PKT_STATUS 0x03 #define BT_SCM_ERROR 0x04 #define BT_ISO_QOS 17 #define BT_ISO_QOS_CIG_UNSET 0xff #define BT_ISO_QOS_CIS_UNSET 0xff #define BT_ISO_QOS_BIG_UNSET 0xff #define BT_ISO_QOS_BIS_UNSET 0xff #define BT_ISO_SYNC_TIMEOUT 0x07d0 /* 20 secs */ /* For an ISO Broadcaster, this value is used to compute * the desired Periodic Advertising Interval as a function * of SDU interval, based on the formula: * * PA_Interval = SDU_Interval * sync_factor * * This is useful for adjusting how frequent to send PA * announcements for Broadcast Sinks to discover, depending * on scenario. */ #define BT_ISO_SYNC_FACTOR 0x01 #define BT_ISO_QOS_GROUP_UNSET 0xff #define BT_ISO_QOS_STREAM_UNSET 0xff struct bt_iso_io_qos { uint32_t interval; uint16_t latency; uint16_t sdu; uint8_t phy; uint8_t rtn; }; struct bt_iso_ucast_qos { uint8_t cig; uint8_t cis; uint8_t sca; uint8_t packing; uint8_t framing; struct bt_iso_io_qos in; struct bt_iso_io_qos out; }; struct bt_iso_bcast_qos { uint8_t big; uint8_t bis; uint8_t sync_factor; uint8_t packing; uint8_t framing; struct bt_iso_io_qos in; struct bt_iso_io_qos out; uint8_t encryption; uint8_t bcode[16]; uint8_t options; uint16_t skip; uint16_t sync_timeout; uint8_t sync_cte_type; uint8_t mse; uint16_t timeout; }; /* (HCI_MAX_PER_AD_LENGTH - EIR_SERVICE_DATA_LENGTH) */ #define BASE_MAX_LENGTH 248 struct bt_iso_base { uint8_t base_len; uint8_t base[BASE_MAX_LENGTH]; }; struct bt_iso_qos { union { struct bt_iso_ucast_qos ucast; struct bt_iso_bcast_qos bcast; }; }; #define BT_CODEC 19 struct bt_codec { uint8_t id; uint16_t cid; uint16_t vid; uint8_t data_path_id; uint8_t num_caps; struct codec_caps { uint8_t len; uint8_t data[]; } caps[]; } __attribute__((packed)); struct bt_codecs { uint8_t num_codecs; struct bt_codec codecs[]; } __attribute__((packed)); /* Connection and socket states */ enum { BT_CONNECTED = 1, /* Equal to TCP_ESTABLISHED to make net code happy */ BT_OPEN, BT_BOUND, BT_LISTEN, BT_CONNECT, BT_CONNECT2, BT_CONFIG, BT_DISCONN, BT_CLOSED }; #define BT_ISO_BASE 20 #define BT_POLL_ERRQUEUE 21 /* Byte order conversions */ #if __BYTE_ORDER == __LITTLE_ENDIAN #define htobs(d) (d) #define htobl(d) (d) #define htobll(d) (d) #define btohs(d) (d) #define btohl(d) (d) #define btohll(d) (d) #elif __BYTE_ORDER == __BIG_ENDIAN #define htobs(d) bswap_16(d) #define htobl(d) bswap_32(d) #define htobll(d) bswap_64(d) #define btohs(d) bswap_16(d) #define btohl(d) bswap_32(d) #define btohll(d) bswap_64(d) #else #error "Unknown byte order" #endif /* Bluetooth unaligned access */ #define bt_get_unaligned(ptr) \ __extension__ ({ \ struct __attribute__((packed)) { \ __typeof__(*(ptr)) __v; \ } *__p = (__typeof__(__p)) (ptr); \ __p->__v; \ }) #define bt_put_unaligned(val, ptr) \ do { \ struct __attribute__((packed)) { \ __typeof__(*(ptr)) __v; \ } *__p = (__typeof__(__p)) (ptr); \ __p->__v = (val); \ } while(0) #if __BYTE_ORDER == __LITTLE_ENDIAN static inline uint64_t bt_get_le64(const void *ptr) { return bt_get_unaligned((const uint64_t *) ptr); } static inline uint64_t bt_get_be64(const void *ptr) { return bswap_64(bt_get_unaligned((const uint64_t *) ptr)); } static inline uint32_t bt_get_le32(const void *ptr) { return bt_get_unaligned((const uint32_t *) ptr); } static inline uint32_t bt_get_be32(const void *ptr) { return bswap_32(bt_get_unaligned((const uint32_t *) ptr)); } static inline uint16_t bt_get_le16(const void *ptr) { return bt_get_unaligned((const uint16_t *) ptr); } static inline uint16_t bt_get_be16(const void *ptr) { return bswap_16(bt_get_unaligned((const uint16_t *) ptr)); } static inline void bt_put_le64(uint64_t val, const void *ptr) { bt_put_unaligned(val, (uint64_t *) ptr); } static inline void bt_put_be64(uint64_t val, const void *ptr) { bt_put_unaligned(bswap_64(val), (uint64_t *) ptr); } static inline void bt_put_le32(uint32_t val, const void *ptr) { bt_put_unaligned(val, (uint32_t *) ptr); } static inline void bt_put_be32(uint32_t val, const void *ptr) { bt_put_unaligned(bswap_32(val), (uint32_t *) ptr); } static inline void bt_put_le16(uint16_t val, const void *ptr) { bt_put_unaligned(val, (uint16_t *) ptr); } static inline void bt_put_be16(uint16_t val, const void *ptr) { bt_put_unaligned(bswap_16(val), (uint16_t *) ptr); } #elif __BYTE_ORDER == __BIG_ENDIAN static inline uint64_t bt_get_le64(const void *ptr) { return bswap_64(bt_get_unaligned((const uint64_t *) ptr)); } static inline uint64_t bt_get_be64(const void *ptr) { return bt_get_unaligned((const uint64_t *) ptr); } static inline uint32_t bt_get_le32(const void *ptr) { return bswap_32(bt_get_unaligned((const uint32_t *) ptr)); } static inline uint32_t bt_get_be32(const void *ptr) { return bt_get_unaligned((const uint32_t *) ptr); } static inline uint16_t bt_get_le16(const void *ptr) { return bswap_16(bt_get_unaligned((const uint16_t *) ptr)); } static inline uint16_t bt_get_be16(const void *ptr) { return bt_get_unaligned((const uint16_t *) ptr); } static inline void bt_put_le64(uint64_t val, const void *ptr) { bt_put_unaligned(bswap_64(val), (uint64_t *) ptr); } static inline void bt_put_be64(uint64_t val, const void *ptr) { bt_put_unaligned(val, (uint64_t *) ptr); } static inline void bt_put_le32(uint32_t val, const void *ptr) { bt_put_unaligned(bswap_32(val), (uint32_t *) ptr); } static inline void bt_put_be32(uint32_t val, const void *ptr) { bt_put_unaligned(val, (uint32_t *) ptr); } static inline void bt_put_le16(uint16_t val, const void *ptr) { bt_put_unaligned(bswap_16(val), (uint16_t *) ptr); } static inline void bt_put_be16(uint16_t val, const void *ptr) { bt_put_unaligned(val, (uint16_t *) ptr); } #else #error "Unknown byte order" #endif /* BD Address */ typedef struct { uint8_t b[6]; } __attribute__((packed)) bdaddr_t; /* BD Address type */ #define BDADDR_BREDR 0x00 #define BDADDR_LE_PUBLIC 0x01 #define BDADDR_LE_RANDOM 0x02 #define BDADDR_ANY (&(bdaddr_t) {{0, 0, 0, 0, 0, 0}}) #define BDADDR_ALL (&(bdaddr_t) {{0xff, 0xff, 0xff, 0xff, 0xff, 0xff}}) #define BDADDR_LOCAL (&(bdaddr_t) {{0, 0, 0, 0xff, 0xff, 0xff}}) /* Copy, swap, convert BD Address */ static inline int bacmp(const bdaddr_t *ba1, const bdaddr_t *ba2) { return memcmp(ba1, ba2, sizeof(bdaddr_t)); } static inline void bacpy(bdaddr_t *dst, const bdaddr_t *src) { memcpy(dst, src, sizeof(bdaddr_t)); } void baswap(bdaddr_t *dst, const bdaddr_t *src); bdaddr_t *strtoba(const char *str); char *batostr(const bdaddr_t *ba); int ba2str(const bdaddr_t *ba, char *str); int ba2strlc(const bdaddr_t *ba, char *str); int str2ba(const char *str, bdaddr_t *ba); int ba2oui(const bdaddr_t *ba, char *oui); int bachk(const char *str); int baprintf(const char *format, ...); int bafprintf(FILE *stream, const char *format, ...); int basprintf(char *str, const char *format, ...); int basnprintf(char *str, size_t size, const char *format, ...); void *bt_malloc(size_t size); void *bt_malloc0(size_t size); void bt_free(void *ptr); int bt_error(uint16_t code); const char *bt_compidtostr(int id); typedef struct { uint8_t data[3]; } uint24_t; typedef struct { uint8_t data[16]; } uint128_t; static inline void bswap_128(const void *src, void *dst) { const uint8_t *s = (const uint8_t *) src; uint8_t *d = (uint8_t *) dst; int i; for (i = 0; i < 16; i++) d[15 - i] = s[i]; } #if __BYTE_ORDER == __BIG_ENDIAN #define ntoh64(x) (x) static inline void ntoh128(const uint128_t *src, uint128_t *dst) { memcpy(dst, src, sizeof(uint128_t)); } static inline void btoh128(const uint128_t *src, uint128_t *dst) { bswap_128(src, dst); } #else static inline uint64_t ntoh64(uint64_t n) { uint64_t h; uint64_t tmp = ntohl(n & 0x00000000ffffffff); h = ntohl(n >> 32); h |= tmp << 32; return h; } static inline void ntoh128(const uint128_t *src, uint128_t *dst) { bswap_128(src, dst); } static inline void btoh128(const uint128_t *src, uint128_t *dst) { memcpy(dst, src, sizeof(uint128_t)); } #endif #define hton64(x) ntoh64(x) #define hton128(x, y) ntoh128(x, y) #define htob128(x, y) btoh128(x, y) #ifdef __cplusplus } #endif #endif /* __BLUETOOTH_H */ bluez-5.82/lib/PaxHeaders/uuid.h0000644000000000000000000000005014643061455013543 xustar0020 atime=1743515768 20 ctime=1743591277 bluez-5.82/lib/uuid.h0000644000000000000000000002447014643061455013233 0ustar00rootroot/* SPDX-License-Identifier: GPL-2.0-or-later */ /* * * BlueZ - Bluetooth protocol stack for Linux * * Copyright (C) 2011 Nokia Corporation * Copyright (C) 2011 Marcel Holtmann * Copyright 2023 NXP * * */ #ifndef __BLUETOOTH_UUID_H #define __BLUETOOTH_UUID_H #ifdef __cplusplus extern "C" { #endif #include #define GENERIC_AUDIO_UUID "00001203-0000-1000-8000-00805f9b34fb" #define HSP_HS_UUID "00001108-0000-1000-8000-00805f9b34fb" #define HSP_AG_UUID "00001112-0000-1000-8000-00805f9b34fb" #define HFP_HS_UUID "0000111e-0000-1000-8000-00805f9b34fb" #define HFP_AG_UUID "0000111f-0000-1000-8000-00805f9b34fb" #define ADVANCED_AUDIO_UUID "0000110d-0000-1000-8000-00805f9b34fb" #define A2DP_SOURCE_UUID "0000110a-0000-1000-8000-00805f9b34fb" #define A2DP_SINK_UUID "0000110b-0000-1000-8000-00805f9b34fb" #define AVRCP_REMOTE_UUID "0000110e-0000-1000-8000-00805f9b34fb" #define AVRCP_TARGET_UUID "0000110c-0000-1000-8000-00805f9b34fb" #define PANU_UUID "00001115-0000-1000-8000-00805f9b34fb" #define NAP_UUID "00001116-0000-1000-8000-00805f9b34fb" #define GN_UUID "00001117-0000-1000-8000-00805f9b34fb" #define BNEP_SVC_UUID "0000000f-0000-1000-8000-00805f9b34fb" #define PNPID_UUID "00002a50-0000-1000-8000-00805f9b34fb" #define DEVICE_INFORMATION_UUID "0000180a-0000-1000-8000-00805f9b34fb" #define GATT_UUID "00001801-0000-1000-8000-00805f9b34fb" #define IMMEDIATE_ALERT_UUID "00001802-0000-1000-8000-00805f9b34fb" #define LINK_LOSS_UUID "00001803-0000-1000-8000-00805f9b34fb" #define TX_POWER_UUID "00001804-0000-1000-8000-00805f9b34fb" #define BATTERY_UUID "0000180f-0000-1000-8000-00805f9b34fb" #define SCAN_PARAMETERS_UUID "00001813-0000-1000-8000-00805f9b34fb" #define SAP_UUID "0000112D-0000-1000-8000-00805f9b34fb" #define HEART_RATE_UUID "0000180d-0000-1000-8000-00805f9b34fb" #define HEART_RATE_MEASUREMENT_UUID "00002a37-0000-1000-8000-00805f9b34fb" #define BODY_SENSOR_LOCATION_UUID "00002a38-0000-1000-8000-00805f9b34fb" #define HEART_RATE_CONTROL_POINT_UUID "00002a39-0000-1000-8000-00805f9b34fb" #define HEALTH_THERMOMETER_UUID "00001809-0000-1000-8000-00805f9b34fb" #define TEMPERATURE_MEASUREMENT_UUID "00002a1c-0000-1000-8000-00805f9b34fb" #define TEMPERATURE_TYPE_UUID "00002a1d-0000-1000-8000-00805f9b34fb" #define INTERMEDIATE_TEMPERATURE_UUID "00002a1e-0000-1000-8000-00805f9b34fb" #define MEASUREMENT_INTERVAL_UUID "00002a21-0000-1000-8000-00805f9b34fb" #define CYCLING_SC_UUID "00001816-0000-1000-8000-00805f9b34fb" #define CSC_MEASUREMENT_UUID "00002a5b-0000-1000-8000-00805f9b34fb" #define CSC_FEATURE_UUID "00002a5c-0000-1000-8000-00805f9b34fb" #define SENSOR_LOCATION_UUID "00002a5d-0000-1000-8000-00805f9b34fb" #define SC_CONTROL_POINT_UUID "00002a55-0000-1000-8000-00805f9b34fb" #define RFCOMM_UUID_STR "00000003-0000-1000-8000-00805f9b34fb" #define HDP_UUID "00001400-0000-1000-8000-00805f9b34fb" #define HDP_SOURCE_UUID "00001401-0000-1000-8000-00805f9b34fb" #define HDP_SINK_UUID "00001402-0000-1000-8000-00805f9b34fb" #define HID_UUID "00001124-0000-1000-8000-00805f9b34fb" #define HOG_UUID "00001812-0000-1000-8000-00805f9b34fb" #define DUN_GW_UUID "00001103-0000-1000-8000-00805f9b34fb" #define GAP_UUID "00001800-0000-1000-8000-00805f9b34fb" #define PNP_UUID "00001200-0000-1000-8000-00805f9b34fb" #define SPP_UUID "00001101-0000-1000-8000-00805f9b34fb" #define OBEX_SYNC_UUID "00001104-0000-1000-8000-00805f9b34fb" #define OBEX_OPP_UUID "00001105-0000-1000-8000-00805f9b34fb" #define OBEX_FTP_UUID "00001106-0000-1000-8000-00805f9b34fb" #define OBEX_PCE_UUID "0000112e-0000-1000-8000-00805f9b34fb" #define OBEX_PSE_UUID "0000112f-0000-1000-8000-00805f9b34fb" #define OBEX_PBAP_UUID "00001130-0000-1000-8000-00805f9b34fb" #define OBEX_MAS_UUID "00001132-0000-1000-8000-00805f9b34fb" #define OBEX_MNS_UUID "00001133-0000-1000-8000-00805f9b34fb" #define OBEX_MAP_UUID "00001134-0000-1000-8000-00805f9b34fb" /* GATT UUIDs section */ #define GATT_PRIM_SVC_UUID 0x2800 #define GATT_SND_SVC_UUID 0x2801 #define GATT_INCLUDE_UUID 0x2802 #define GATT_CHARAC_UUID 0x2803 /* GATT Characteristic Types */ #define GATT_CHARAC_DEVICE_NAME 0x2A00 #define GATT_CHARAC_APPEARANCE 0x2A01 #define GATT_CHARAC_PERIPHERAL_PRIV_FLAG 0x2A02 #define GATT_CHARAC_RECONNECTION_ADDRESS 0x2A03 #define GATT_CHARAC_PERIPHERAL_PREF_CONN 0x2A04 #define GATT_CHARAC_SERVICE_CHANGED 0x2A05 #define GATT_CHARAC_BATTERY_LEVEL 0x2A19 #define GATT_CHARAC_SYSTEM_ID 0x2A23 #define GATT_CHARAC_MODEL_NUMBER_STRING 0x2A24 #define GATT_CHARAC_SERIAL_NUMBER_STRING 0x2A25 #define GATT_CHARAC_FIRMWARE_REVISION_STRING 0x2A26 #define GATT_CHARAC_HARDWARE_REVISION_STRING 0x2A27 #define GATT_CHARAC_SOFTWARE_REVISION_STRING 0x2A28 #define GATT_CHARAC_MANUFACTURER_NAME_STRING 0x2A29 #define GATT_CHARAC_PNP_ID 0x2A50 #define GATT_CHARAC_CAR 0x2AA6 /* GATT Characteristic Descriptors */ #define GATT_CHARAC_EXT_PROPER_UUID 0x2900 #define GATT_CHARAC_USER_DESC_UUID 0x2901 #define GATT_CLIENT_CHARAC_CFG_UUID 0x2902 #define GATT_SERVER_CHARAC_CFG_UUID 0x2903 #define GATT_CHARAC_FMT_UUID 0x2904 #define GATT_CHARAC_AGREG_FMT_UUID 0x2905 #define GATT_CHARAC_VALID_RANGE_UUID 0x2906 #define GATT_EXTERNAL_REPORT_REFERENCE 0x2907 #define GATT_REPORT_REFERENCE 0x2908 /* GATT Mesh Services */ #define MESH_PROV_SVC_UUID "00001827-0000-1000-8000-00805f9b34fb" #define MESH_PROXY_SVC_UUID "00001828-0000-1000-8000-00805f9b34fb" /* GATT Mesh Characteristic Types */ #define MESH_PROVISIONING_DATA_IN 0x2ADB #define MESH_PROVISIONING_DATA_OUT 0x2ADC #define MESH_PROXY_DATA_IN 0x2ADD #define MESH_PROXY_DATA_OUT 0x2ADE /* GATT Caching attributes */ #define GATT_CHARAC_CLI_FEAT 0x2B29 #define GATT_CHARAC_DB_HASH 0x2B2A /* GATT Server Supported features */ #define GATT_CHARAC_SERVER_FEAT 0x2B3A /* TODO: Update these on final UUID is given */ #define PACS_UUID 0x1850 #define PAC_SINK_CHRC_UUID 0x2bc9 #define PAC_SINK_UUID "00002bc9-0000-1000-8000-00805f9b34fb" #define PAC_SINK_LOC_CHRC_UUID 0x2bca #define PAC_SOURCE_CHRC_UUID 0x2bcb #define PAC_SOURCE_UUID "00002bcb-0000-1000-8000-00805f9b34fb" #define PAC_SOURCE_LOC_CHRC_UUID 0x2bcc #define BCAA_SERVICE 0x1852 #define BCAA_SERVICE_UUID "00001852-0000-1000-8000-00805f9b34fb" #define BAA_SERVICE 0x1851 #define BAA_SERVICE_UUID "00001851-0000-1000-8000-00805f9b34fb" #define ASHA_SERVICE 0xFDF0 #define ASHA_PROFILE_UUID "0000FDF0-0000-1000-8000-00805f9b34fb" #define PAC_CONTEXT 0x2bcd #define PAC_SUPPORTED_CONTEXT 0x2bce #define ASCS_UUID 0x184e #define ASE_SINK_UUID 0x2bc4 #define ASE_SOURCE_UUID 0x2bc5 #define ASE_CP_UUID 0x2bc6 #define BASS_UUID 0x184f #define BCAST_AUDIO_SCAN_CP_UUID 0x2bc7 #define BCAST_RECV_STATE_UUID 0x2bc8 #define VCS_UUID 0x1844 #define VOL_OFFSET_CS_UUID 0x1845 #define AUDIO_INPUT_CS_UUID 0x1843 #define VOL_STATE_CHRC_UUID 0x2B7D #define VOL_CP_CHRC_UUID 0x2B7E #define VOL_FLAG_CHRC_UUID 0x2B7F #define VOCS_STATE_CHAR_UUID 0x2B80 #define VOCS_AUDIO_LOC_CHRC_UUID 0x2B81 #define VOCS_CP_CHRC_UUID 0x2B82 #define VOCS_AUDIO_OP_DESC_CHAR_UUID 0x2B83 #define AICS_INPUT_STATE_CHAR_UUID 0x2B77 #define AICS_GAIN_SETTING_PROP_CHAR_UUID 0x2B78 #define AICS_AUDIO_INPUT_TYPE_CHAR_UUID 0x2B79 #define AICS_INPUT_STATUS_CHAR_UUID 0X2B7A #define AICS_AUDIO_INPUT_CP_CHRC_UUID 0X2B7B #define AICS_INPUT_DESCR_CHAR_UUID 0X2B7C #define GMCS_UUID 0x1849 #define MEDIA_PLAYER_NAME_CHRC_UUID 0x2b93 #define MEDIA_TRACK_CHNGD_CHRC_UUID 0x2b96 #define MEDIA_TRACK_TITLE_CHRC_UUID 0x2b97 #define MEDIA_TRACK_DURATION_CHRC_UUID 0x2b98 #define MEDIA_TRACK_POSTION_CHRC_UUID 0x2b99 #define MEDIA_PLAYBACK_SPEED_CHRC_UUID 0x2b9a #define MEDIA_SEEKING_SPEED_CHRC_UUID 0x2b9b #define MEDIA_PLAYING_ORDER_CHRC_UUID 0x2ba1 #define MEDIA_PLAY_ORDER_SUPPRTD_CHRC_UUID 0x2ba2 #define MEDIA_STATE_CHRC_UUID 0x2ba3 #define MEDIA_CP_CHRC_UUID 0x2ba4 #define MEDIA_CP_OP_SUPPORTED_CHRC_UUID 0x2ba5 #define MEDIA_CONTENT_CONTROL_ID_CHRC_UUID 0x2bba /* Coordinated Set Identification Profile(CSIP) */ #define CSIS_UUID 0x1846 #define CS_SIRK 0x2B84 #define CS_SIZE 0x2B85 #define CS_LOCK 0x2B86 #define CS_RANK 0x2B87 /* Microphone Control Service(MICS) */ #define MICS_UUID 0x184D #define MUTE_CHRC_UUID 0x2BC3 /* Call Control Service(TBS/CCS) */ #define TBS_UUID 0x184B #define GTBS_UUID 0x184C #define BEARER_PROVIDER_NAME_CHRC_UUID 0x2bb3 #define BEARER_UCI_CHRC_UUID 0x2bb4 #define BEARER_TECH_CHRC_UUID 0x2bb5 #define BEARER_URI_SCHEME_CHRC_UUID 0x2bb6 #define BEARER_SIGNAL_STR_CHRC_UUID 0x2bb7 #define BEARER_SIGNAL_INTRVL_CHRC_UUID 0x2bb8 #define CURR_CALL_LIST_CHRC_UUID 0x2bb9 #define BEARER_CCID_CHRC_UUID 0x2bba #define CALL_STATUS_FLAG_CHRC_UUID 0x2bbb #define INCOM_CALL_TARGET_URI_CHRC_UUID 0x2bbc #define CALL_STATE_CHRC_UUID 0x2bbd #define CALL_CTRL_POINT_CHRC_UUID 0x2bbe #define CALL_CTRL_POINT_OPT_OPCODE_CHRC_UUID 0x2bbf #define TERMINATION_REASON_CHRC_UUID 0x2bc0 #define INCOMING_CALL_CHRC_UUID 0x2bc1 #define CALL_FRIENDLY_NAME_CHRC_UUID 0x2bc2 typedef struct { enum { BT_UUID_UNSPEC = 0, BT_UUID16 = 16, BT_UUID32 = 32, BT_UUID128 = 128, } type; union { uint16_t u16; uint32_t u32; uint128_t u128; } value; } bt_uuid_t; int bt_uuid_strcmp(const void *a, const void *b); int bt_uuid16_create(bt_uuid_t *btuuid, uint16_t value); int bt_uuid32_create(bt_uuid_t *btuuid, uint32_t value); int bt_uuid128_create(bt_uuid_t *btuuid, uint128_t value); int bt_uuid_cmp(const bt_uuid_t *uuid1, const bt_uuid_t *uuid2); int bt_uuid16_cmp(const bt_uuid_t *uuid1, uint16_t uuid2); void bt_uuid_to_uuid128(const bt_uuid_t *src, bt_uuid_t *dst); #define MAX_LEN_UUID_STR 37 int bt_uuid_to_string(const bt_uuid_t *uuid, char *str, size_t n); int bt_string_to_uuid(bt_uuid_t *uuid, const char *string); int bt_uuid_to_le(const bt_uuid_t *uuid, void *dst); static inline int bt_uuid_len(const bt_uuid_t *uuid) { return uuid->type / 8; } #ifdef __cplusplus } #endif #endif /* __BLUETOOTH_UUID_H */ bluez-5.82/lib/PaxHeaders/hci.h0000644000000000000000000000005014766002272013336 xustar0020 atime=1743515578 20 ctime=1743591275 bluez-5.82/lib/hci.h0000644000000000000000000017336214766002272013033 0ustar00rootroot/* SPDX-License-Identifier: GPL-2.0-or-later */ /* * * BlueZ - Bluetooth protocol stack for Linux * * Copyright (C) 2000-2001 Qualcomm Incorporated * Copyright (C) 2002-2003 Maxim Krasnyansky * Copyright (C) 2002-2010 Marcel Holtmann * * */ #ifndef __HCI_H #define __HCI_H #ifdef __cplusplus extern "C" { #endif #include #define HCI_MAX_DEV 16 #define HCI_MAX_AMP_SIZE (1492 + 4) #define HCI_MAX_ACL_SIZE 1024 #define HCI_MAX_SCO_SIZE 255 #define HCI_MAX_EVENT_SIZE 260 #define HCI_MAX_FRAME_SIZE (HCI_MAX_AMP_SIZE + 4) /* HCI dev events */ #define HCI_DEV_REG 1 #define HCI_DEV_UNREG 2 #define HCI_DEV_UP 3 #define HCI_DEV_DOWN 4 #define HCI_DEV_SUSPEND 5 #define HCI_DEV_RESUME 6 /* HCI bus types */ #define HCI_VIRTUAL 0 #define HCI_USB 1 #define HCI_PCCARD 2 #define HCI_UART 3 #define HCI_RS232 4 #define HCI_PCI 5 #define HCI_SDIO 6 #define HCI_SPI 7 #define HCI_I2C 8 #define HCI_SMD 9 #define HCI_VIRTIO 10 #define HCI_IPC 11 /* HCI controller types */ #define HCI_PRIMARY 0x00 #define HCI_AMP 0x01 #define HCI_BREDR HCI_PRIMARY /* HCI device flags */ enum { HCI_UP, HCI_INIT, HCI_RUNNING, HCI_PSCAN, HCI_ISCAN, HCI_AUTH, HCI_ENCRYPT, HCI_INQUIRY, HCI_RAW, }; /* LE address type */ enum { LE_PUBLIC_ADDRESS = 0x00, LE_RANDOM_ADDRESS = 0x01 }; /* HCI ioctl defines */ #define HCIDEVUP _IOW('H', 201, int) #define HCIDEVDOWN _IOW('H', 202, int) #define HCIDEVRESET _IOW('H', 203, int) #define HCIDEVRESTAT _IOW('H', 204, int) #define HCIGETDEVLIST _IOR('H', 210, int) #define HCIGETDEVINFO _IOR('H', 211, int) #define HCIGETCONNLIST _IOR('H', 212, int) #define HCIGETCONNINFO _IOR('H', 213, int) #define HCIGETAUTHINFO _IOR('H', 215, int) #define HCISETRAW _IOW('H', 220, int) #define HCISETSCAN _IOW('H', 221, int) #define HCISETAUTH _IOW('H', 222, int) #define HCISETENCRYPT _IOW('H', 223, int) #define HCISETPTYPE _IOW('H', 224, int) #define HCISETLINKPOL _IOW('H', 225, int) #define HCISETLINKMODE _IOW('H', 226, int) #define HCISETACLMTU _IOW('H', 227, int) #define HCISETSCOMTU _IOW('H', 228, int) #define HCIBLOCKADDR _IOW('H', 230, int) #define HCIUNBLOCKADDR _IOW('H', 231, int) #define HCIINQUIRY _IOR('H', 240, int) #ifndef __NO_HCI_DEFS /* HCI Packet types */ #define HCI_COMMAND_PKT 0x01 #define HCI_ACLDATA_PKT 0x02 #define HCI_SCODATA_PKT 0x03 #define HCI_EVENT_PKT 0x04 #define HCI_ISODATA_PKT 0x05 #define HCI_VENDOR_PKT 0xff /* HCI Packet types */ #define HCI_2DH1 0x0002 #define HCI_3DH1 0x0004 #define HCI_DM1 0x0008 #define HCI_DH1 0x0010 #define HCI_2DH3 0x0100 #define HCI_3DH3 0x0200 #define HCI_DM3 0x0400 #define HCI_DH3 0x0800 #define HCI_2DH5 0x1000 #define HCI_3DH5 0x2000 #define HCI_DM5 0x4000 #define HCI_DH5 0x8000 #define HCI_HV1 0x0020 #define HCI_HV2 0x0040 #define HCI_HV3 0x0080 #define HCI_EV3 0x0008 #define HCI_EV4 0x0010 #define HCI_EV5 0x0020 #define HCI_2EV3 0x0040 #define HCI_3EV3 0x0080 #define HCI_2EV5 0x0100 #define HCI_3EV5 0x0200 #define SCO_PTYPE_MASK (HCI_HV1 | HCI_HV2 | HCI_HV3) #define ACL_PTYPE_MASK (HCI_DM1 | HCI_DH1 | HCI_DM3 | HCI_DH3 | HCI_DM5 | HCI_DH5) /* HCI Error codes */ #define HCI_UNKNOWN_COMMAND 0x01 #define HCI_NO_CONNECTION 0x02 #define HCI_HARDWARE_FAILURE 0x03 #define HCI_PAGE_TIMEOUT 0x04 #define HCI_AUTHENTICATION_FAILURE 0x05 #define HCI_PIN_OR_KEY_MISSING 0x06 #define HCI_MEMORY_FULL 0x07 #define HCI_CONNECTION_TIMEOUT 0x08 #define HCI_MAX_NUMBER_OF_CONNECTIONS 0x09 #define HCI_MAX_NUMBER_OF_SCO_CONNECTIONS 0x0a #define HCI_ACL_CONNECTION_EXISTS 0x0b #define HCI_COMMAND_DISALLOWED 0x0c #define HCI_REJECTED_LIMITED_RESOURCES 0x0d #define HCI_REJECTED_SECURITY 0x0e #define HCI_REJECTED_PERSONAL 0x0f #define HCI_HOST_TIMEOUT 0x10 #define HCI_UNSUPPORTED_FEATURE 0x11 #define HCI_INVALID_PARAMETERS 0x12 #define HCI_OE_USER_ENDED_CONNECTION 0x13 #define HCI_OE_LOW_RESOURCES 0x14 #define HCI_OE_POWER_OFF 0x15 #define HCI_CONNECTION_TERMINATED 0x16 #define HCI_REPEATED_ATTEMPTS 0x17 #define HCI_PAIRING_NOT_ALLOWED 0x18 #define HCI_UNKNOWN_LMP_PDU 0x19 #define HCI_UNSUPPORTED_REMOTE_FEATURE 0x1a #define HCI_SCO_OFFSET_REJECTED 0x1b #define HCI_SCO_INTERVAL_REJECTED 0x1c #define HCI_AIR_MODE_REJECTED 0x1d #define HCI_INVALID_LMP_PARAMETERS 0x1e #define HCI_UNSPECIFIED_ERROR 0x1f #define HCI_UNSUPPORTED_LMP_PARAMETER_VALUE 0x20 #define HCI_ROLE_CHANGE_NOT_ALLOWED 0x21 #define HCI_LMP_RESPONSE_TIMEOUT 0x22 #define HCI_LMP_ERROR_TRANSACTION_COLLISION 0x23 #define HCI_LMP_PDU_NOT_ALLOWED 0x24 #define HCI_ENCRYPTION_MODE_NOT_ACCEPTED 0x25 #define HCI_UNIT_LINK_KEY_USED 0x26 #define HCI_QOS_NOT_SUPPORTED 0x27 #define HCI_INSTANT_PASSED 0x28 #define HCI_PAIRING_NOT_SUPPORTED 0x29 #define HCI_TRANSACTION_COLLISION 0x2a #define HCI_QOS_UNACCEPTABLE_PARAMETER 0x2c #define HCI_QOS_REJECTED 0x2d #define HCI_CLASSIFICATION_NOT_SUPPORTED 0x2e #define HCI_INSUFFICIENT_SECURITY 0x2f #define HCI_PARAMETER_OUT_OF_RANGE 0x30 #define HCI_ROLE_SWITCH_PENDING 0x32 #define HCI_SLOT_VIOLATION 0x34 #define HCI_ROLE_SWITCH_FAILED 0x35 #define HCI_EIR_TOO_LARGE 0x36 #define HCI_SIMPLE_PAIRING_NOT_SUPPORTED 0x37 #define HCI_HOST_BUSY_PAIRING 0x38 /* ACL flags */ #define ACL_START_NO_FLUSH 0x00 #define ACL_CONT 0x01 #define ACL_START 0x02 #define ACL_ACTIVE_BCAST 0x04 #define ACL_PICO_BCAST 0x08 /* Baseband links */ #define SCO_LINK 0x00 #define ACL_LINK 0x01 #define ESCO_LINK 0x02 /* LMP features */ #define LMP_3SLOT 0x01 #define LMP_5SLOT 0x02 #define LMP_ENCRYPT 0x04 #define LMP_SOFFSET 0x08 #define LMP_TACCURACY 0x10 #define LMP_RSWITCH 0x20 #define LMP_HOLD 0x40 #define LMP_SNIFF 0x80 #define LMP_PARK 0x01 #define LMP_RSSI 0x02 #define LMP_QUALITY 0x04 #define LMP_SCO 0x08 #define LMP_HV2 0x10 #define LMP_HV3 0x20 #define LMP_ULAW 0x40 #define LMP_ALAW 0x80 #define LMP_CVSD 0x01 #define LMP_PSCHEME 0x02 #define LMP_PCONTROL 0x04 #define LMP_TRSP_SCO 0x08 #define LMP_BCAST_ENC 0x80 #define LMP_EDR_ACL_2M 0x02 #define LMP_EDR_ACL_3M 0x04 #define LMP_ENH_ISCAN 0x08 #define LMP_ILACE_ISCAN 0x10 #define LMP_ILACE_PSCAN 0x20 #define LMP_RSSI_INQ 0x40 #define LMP_ESCO 0x80 #define LMP_EV4 0x01 #define LMP_EV5 0x02 #define LMP_AFH_CAP_SLV 0x08 #define LMP_AFH_CLS_SLV 0x10 #define LMP_NO_BREDR 0x20 #define LMP_LE 0x40 #define LMP_EDR_3SLOT 0x80 #define LMP_EDR_5SLOT 0x01 #define LMP_SNIFF_SUBR 0x02 #define LMP_PAUSE_ENC 0x04 #define LMP_AFH_CAP_MST 0x08 #define LMP_AFH_CLS_MST 0x10 #define LMP_EDR_ESCO_2M 0x20 #define LMP_EDR_ESCO_3M 0x40 #define LMP_EDR_3S_ESCO 0x80 #define LMP_EXT_INQ 0x01 #define LMP_LE_BREDR 0x02 #define LMP_SIMPLE_PAIR 0x08 #define LMP_ENCAPS_PDU 0x10 #define LMP_ERR_DAT_REP 0x20 #define LMP_NFLUSH_PKTS 0x40 #define LMP_LSTO 0x01 #define LMP_INQ_TX_PWR 0x02 #define LMP_EPC 0x04 #define LMP_EXT_FEAT 0x80 /* Extended LMP features */ #define LMP_HOST_SSP 0x01 #define LMP_HOST_LE 0x02 #define LMP_HOST_LE_BREDR 0x04 /* Link policies */ #define HCI_LP_RSWITCH 0x0001 #define HCI_LP_HOLD 0x0002 #define HCI_LP_SNIFF 0x0004 #define HCI_LP_PARK 0x0008 /* Link mode */ #define HCI_LM_ACCEPT 0x8000 #define HCI_LM_MASTER 0x0001 #define HCI_LM_AUTH 0x0002 #define HCI_LM_ENCRYPT 0x0004 #define HCI_LM_TRUSTED 0x0008 #define HCI_LM_RELIABLE 0x0010 #define HCI_LM_SECURE 0x0020 /* Link Key types */ #define HCI_LK_COMBINATION 0x00 #define HCI_LK_LOCAL_UNIT 0x01 #define HCI_LK_REMOTE_UNIT 0x02 #define HCI_LK_DEBUG_COMBINATION 0x03 #define HCI_LK_UNAUTH_COMBINATION 0x04 #define HCI_LK_AUTH_COMBINATION 0x05 #define HCI_LK_CHANGED_COMBINATION 0x06 #define HCI_LK_INVALID 0xFF /* ----- HCI Commands ----- */ /* Link Control */ #define OGF_LINK_CTL 0x01 #define OCF_INQUIRY 0x0001 typedef struct { uint8_t lap[3]; uint8_t length; /* 1.28s units */ uint8_t num_rsp; } __attribute__ ((packed)) inquiry_cp; #define INQUIRY_CP_SIZE 5 typedef struct { uint8_t status; bdaddr_t bdaddr; } __attribute__ ((packed)) status_bdaddr_rp; #define STATUS_BDADDR_RP_SIZE 7 #define OCF_INQUIRY_CANCEL 0x0002 #define OCF_PERIODIC_INQUIRY 0x0003 typedef struct { uint16_t max_period; /* 1.28s units */ uint16_t min_period; /* 1.28s units */ uint8_t lap[3]; uint8_t length; /* 1.28s units */ uint8_t num_rsp; } __attribute__ ((packed)) periodic_inquiry_cp; #define PERIODIC_INQUIRY_CP_SIZE 9 #define OCF_EXIT_PERIODIC_INQUIRY 0x0004 #define OCF_CREATE_CONN 0x0005 typedef struct { bdaddr_t bdaddr; uint16_t pkt_type; uint8_t pscan_rep_mode; uint8_t pscan_mode; uint16_t clock_offset; uint8_t role_switch; } __attribute__ ((packed)) create_conn_cp; #define CREATE_CONN_CP_SIZE 13 #define OCF_DISCONNECT 0x0006 typedef struct { uint16_t handle; uint8_t reason; } __attribute__ ((packed)) disconnect_cp; #define DISCONNECT_CP_SIZE 3 #define OCF_ADD_SCO 0x0007 typedef struct { uint16_t handle; uint16_t pkt_type; } __attribute__ ((packed)) add_sco_cp; #define ADD_SCO_CP_SIZE 4 #define OCF_CREATE_CONN_CANCEL 0x0008 typedef struct { bdaddr_t bdaddr; } __attribute__ ((packed)) create_conn_cancel_cp; #define CREATE_CONN_CANCEL_CP_SIZE 6 #define OCF_ACCEPT_CONN_REQ 0x0009 typedef struct { bdaddr_t bdaddr; uint8_t role; } __attribute__ ((packed)) accept_conn_req_cp; #define ACCEPT_CONN_REQ_CP_SIZE 7 #define OCF_REJECT_CONN_REQ 0x000A typedef struct { bdaddr_t bdaddr; uint8_t reason; } __attribute__ ((packed)) reject_conn_req_cp; #define REJECT_CONN_REQ_CP_SIZE 7 #define OCF_LINK_KEY_REPLY 0x000B typedef struct { bdaddr_t bdaddr; uint8_t link_key[16]; } __attribute__ ((packed)) link_key_reply_cp; #define LINK_KEY_REPLY_CP_SIZE 22 #define OCF_LINK_KEY_NEG_REPLY 0x000C #define OCF_PIN_CODE_REPLY 0x000D typedef struct { bdaddr_t bdaddr; uint8_t pin_len; uint8_t pin_code[16]; } __attribute__ ((packed)) pin_code_reply_cp; #define PIN_CODE_REPLY_CP_SIZE 23 #define OCF_PIN_CODE_NEG_REPLY 0x000E #define OCF_SET_CONN_PTYPE 0x000F typedef struct { uint16_t handle; uint16_t pkt_type; } __attribute__ ((packed)) set_conn_ptype_cp; #define SET_CONN_PTYPE_CP_SIZE 4 #define OCF_AUTH_REQUESTED 0x0011 typedef struct { uint16_t handle; } __attribute__ ((packed)) auth_requested_cp; #define AUTH_REQUESTED_CP_SIZE 2 #define OCF_SET_CONN_ENCRYPT 0x0013 typedef struct { uint16_t handle; uint8_t encrypt; } __attribute__ ((packed)) set_conn_encrypt_cp; #define SET_CONN_ENCRYPT_CP_SIZE 3 #define OCF_CHANGE_CONN_LINK_KEY 0x0015 typedef struct { uint16_t handle; } __attribute__ ((packed)) change_conn_link_key_cp; #define CHANGE_CONN_LINK_KEY_CP_SIZE 2 #define OCF_MASTER_LINK_KEY 0x0017 typedef struct { uint8_t key_flag; } __attribute__ ((packed)) master_link_key_cp; #define MASTER_LINK_KEY_CP_SIZE 1 #define OCF_REMOTE_NAME_REQ 0x0019 typedef struct { bdaddr_t bdaddr; uint8_t pscan_rep_mode; uint8_t pscan_mode; uint16_t clock_offset; } __attribute__ ((packed)) remote_name_req_cp; #define REMOTE_NAME_REQ_CP_SIZE 10 #define OCF_REMOTE_NAME_REQ_CANCEL 0x001A typedef struct { bdaddr_t bdaddr; } __attribute__ ((packed)) remote_name_req_cancel_cp; #define REMOTE_NAME_REQ_CANCEL_CP_SIZE 6 #define OCF_READ_REMOTE_FEATURES 0x001B typedef struct { uint16_t handle; } __attribute__ ((packed)) read_remote_features_cp; #define READ_REMOTE_FEATURES_CP_SIZE 2 #define OCF_READ_REMOTE_EXT_FEATURES 0x001C typedef struct { uint16_t handle; uint8_t page_num; } __attribute__ ((packed)) read_remote_ext_features_cp; #define READ_REMOTE_EXT_FEATURES_CP_SIZE 3 #define OCF_READ_REMOTE_VERSION 0x001D typedef struct { uint16_t handle; } __attribute__ ((packed)) read_remote_version_cp; #define READ_REMOTE_VERSION_CP_SIZE 2 #define OCF_READ_CLOCK_OFFSET 0x001F typedef struct { uint16_t handle; } __attribute__ ((packed)) read_clock_offset_cp; #define READ_CLOCK_OFFSET_CP_SIZE 2 #define OCF_READ_LMP_HANDLE 0x0020 #define OCF_SETUP_SYNC_CONN 0x0028 typedef struct { uint16_t handle; uint32_t tx_bandwith; uint32_t rx_bandwith; uint16_t max_latency; uint16_t voice_setting; uint8_t retrans_effort; uint16_t pkt_type; } __attribute__ ((packed)) setup_sync_conn_cp; #define SETUP_SYNC_CONN_CP_SIZE 17 #define OCF_ACCEPT_SYNC_CONN_REQ 0x0029 typedef struct { bdaddr_t bdaddr; uint32_t tx_bandwith; uint32_t rx_bandwith; uint16_t max_latency; uint16_t voice_setting; uint8_t retrans_effort; uint16_t pkt_type; } __attribute__ ((packed)) accept_sync_conn_req_cp; #define ACCEPT_SYNC_CONN_REQ_CP_SIZE 21 #define OCF_REJECT_SYNC_CONN_REQ 0x002A typedef struct { bdaddr_t bdaddr; uint8_t reason; } __attribute__ ((packed)) reject_sync_conn_req_cp; #define REJECT_SYNC_CONN_REQ_CP_SIZE 7 #define OCF_IO_CAPABILITY_REPLY 0x002B typedef struct { bdaddr_t bdaddr; uint8_t capability; uint8_t oob_data; uint8_t authentication; } __attribute__ ((packed)) io_capability_reply_cp; #define IO_CAPABILITY_REPLY_CP_SIZE 9 #define OCF_USER_CONFIRM_REPLY 0x002C typedef struct { bdaddr_t bdaddr; } __attribute__ ((packed)) user_confirm_reply_cp; #define USER_CONFIRM_REPLY_CP_SIZE 6 #define OCF_USER_CONFIRM_NEG_REPLY 0x002D #define OCF_USER_PASSKEY_REPLY 0x002E typedef struct { bdaddr_t bdaddr; uint32_t passkey; } __attribute__ ((packed)) user_passkey_reply_cp; #define USER_PASSKEY_REPLY_CP_SIZE 10 #define OCF_USER_PASSKEY_NEG_REPLY 0x002F #define OCF_REMOTE_OOB_DATA_REPLY 0x0030 typedef struct { bdaddr_t bdaddr; uint8_t hash[16]; uint8_t randomizer[16]; } __attribute__ ((packed)) remote_oob_data_reply_cp; #define REMOTE_OOB_DATA_REPLY_CP_SIZE 38 #define OCF_REMOTE_OOB_DATA_NEG_REPLY 0x0033 #define OCF_IO_CAPABILITY_NEG_REPLY 0x0034 typedef struct { bdaddr_t bdaddr; uint8_t reason; } __attribute__ ((packed)) io_capability_neg_reply_cp; #define IO_CAPABILITY_NEG_REPLY_CP_SIZE 7 #define OCF_CREATE_PHYSICAL_LINK 0x0035 typedef struct { uint8_t handle; uint8_t key_length; uint8_t key_type; uint8_t key[32]; } __attribute__ ((packed)) create_physical_link_cp; #define CREATE_PHYSICAL_LINK_CP_SIZE 35 #define OCF_ACCEPT_PHYSICAL_LINK 0x0036 typedef struct { uint8_t handle; uint8_t key_length; uint8_t key_type; uint8_t key[32]; } __attribute__ ((packed)) accept_physical_link_cp; #define ACCEPT_PHYSICAL_LINK_CP_SIZE 35 #define OCF_DISCONNECT_PHYSICAL_LINK 0x0037 typedef struct { uint8_t handle; uint8_t reason; } __attribute__ ((packed)) disconnect_physical_link_cp; #define DISCONNECT_PHYSICAL_LINK_CP_SIZE 2 #define OCF_CREATE_LOGICAL_LINK 0x0038 typedef struct { uint8_t handle; uint8_t tx_flow[16]; uint8_t rx_flow[16]; } __attribute__ ((packed)) create_logical_link_cp; #define CREATE_LOGICAL_LINK_CP_SIZE 33 #define OCF_ACCEPT_LOGICAL_LINK 0x0039 #define OCF_DISCONNECT_LOGICAL_LINK 0x003A typedef struct { uint16_t handle; } __attribute__ ((packed)) disconnect_logical_link_cp; #define DISCONNECT_LOGICAL_LINK_CP_SIZE 2 #define OCF_LOGICAL_LINK_CANCEL 0x003B typedef struct { uint8_t handle; uint8_t tx_flow_id; } __attribute__ ((packed)) cancel_logical_link_cp; #define LOGICAL_LINK_CANCEL_CP_SIZE 2 typedef struct { uint8_t status; uint8_t handle; uint8_t tx_flow_id; } __attribute__ ((packed)) cancel_logical_link_rp; #define LOGICAL_LINK_CANCEL_RP_SIZE 3 #define OCF_FLOW_SPEC_MODIFY 0x003C /* Link Policy */ #define OGF_LINK_POLICY 0x02 #define OCF_HOLD_MODE 0x0001 typedef struct { uint16_t handle; uint16_t max_interval; uint16_t min_interval; } __attribute__ ((packed)) hold_mode_cp; #define HOLD_MODE_CP_SIZE 6 #define OCF_SNIFF_MODE 0x0003 typedef struct { uint16_t handle; uint16_t max_interval; uint16_t min_interval; uint16_t attempt; uint16_t timeout; } __attribute__ ((packed)) sniff_mode_cp; #define SNIFF_MODE_CP_SIZE 10 #define OCF_EXIT_SNIFF_MODE 0x0004 typedef struct { uint16_t handle; } __attribute__ ((packed)) exit_sniff_mode_cp; #define EXIT_SNIFF_MODE_CP_SIZE 2 #define OCF_PARK_MODE 0x0005 typedef struct { uint16_t handle; uint16_t max_interval; uint16_t min_interval; } __attribute__ ((packed)) park_mode_cp; #define PARK_MODE_CP_SIZE 6 #define OCF_EXIT_PARK_MODE 0x0006 typedef struct { uint16_t handle; } __attribute__ ((packed)) exit_park_mode_cp; #define EXIT_PARK_MODE_CP_SIZE 2 #define OCF_QOS_SETUP 0x0007 typedef struct { uint8_t service_type; /* 1 = best effort */ uint32_t token_rate; /* Byte per seconds */ uint32_t peak_bandwidth; /* Byte per seconds */ uint32_t latency; /* Microseconds */ uint32_t delay_variation; /* Microseconds */ } __attribute__ ((packed)) hci_qos; #define HCI_QOS_CP_SIZE 17 typedef struct { uint16_t handle; uint8_t flags; /* Reserved */ hci_qos qos; } __attribute__ ((packed)) qos_setup_cp; #define QOS_SETUP_CP_SIZE (3 + HCI_QOS_CP_SIZE) #define OCF_ROLE_DISCOVERY 0x0009 typedef struct { uint16_t handle; } __attribute__ ((packed)) role_discovery_cp; #define ROLE_DISCOVERY_CP_SIZE 2 typedef struct { uint8_t status; uint16_t handle; uint8_t role; } __attribute__ ((packed)) role_discovery_rp; #define ROLE_DISCOVERY_RP_SIZE 4 #define OCF_SWITCH_ROLE 0x000B typedef struct { bdaddr_t bdaddr; uint8_t role; } __attribute__ ((packed)) switch_role_cp; #define SWITCH_ROLE_CP_SIZE 7 #define OCF_READ_LINK_POLICY 0x000C typedef struct { uint16_t handle; } __attribute__ ((packed)) read_link_policy_cp; #define READ_LINK_POLICY_CP_SIZE 2 typedef struct { uint8_t status; uint16_t handle; uint16_t policy; } __attribute__ ((packed)) read_link_policy_rp; #define READ_LINK_POLICY_RP_SIZE 5 #define OCF_WRITE_LINK_POLICY 0x000D typedef struct { uint16_t handle; uint16_t policy; } __attribute__ ((packed)) write_link_policy_cp; #define WRITE_LINK_POLICY_CP_SIZE 4 typedef struct { uint8_t status; uint16_t handle; } __attribute__ ((packed)) write_link_policy_rp; #define WRITE_LINK_POLICY_RP_SIZE 3 #define OCF_READ_DEFAULT_LINK_POLICY 0x000E #define OCF_WRITE_DEFAULT_LINK_POLICY 0x000F #define OCF_FLOW_SPECIFICATION 0x0010 #define OCF_SNIFF_SUBRATING 0x0011 typedef struct { uint16_t handle; uint16_t max_latency; uint16_t min_remote_timeout; uint16_t min_local_timeout; } __attribute__ ((packed)) sniff_subrating_cp; #define SNIFF_SUBRATING_CP_SIZE 8 /* Host Controller and Baseband */ #define OGF_HOST_CTL 0x03 #define OCF_SET_EVENT_MASK 0x0001 typedef struct { uint8_t mask[8]; } __attribute__ ((packed)) set_event_mask_cp; #define SET_EVENT_MASK_CP_SIZE 8 #define OCF_RESET 0x0003 #define OCF_SET_EVENT_FLT 0x0005 typedef struct { uint8_t flt_type; uint8_t cond_type; uint8_t condition[]; } __attribute__ ((packed)) set_event_flt_cp; #define SET_EVENT_FLT_CP_SIZE 2 /* Filter types */ #define FLT_CLEAR_ALL 0x00 #define FLT_INQ_RESULT 0x01 #define FLT_CONN_SETUP 0x02 /* INQ_RESULT Condition types */ #define INQ_RESULT_RETURN_ALL 0x00 #define INQ_RESULT_RETURN_CLASS 0x01 #define INQ_RESULT_RETURN_BDADDR 0x02 /* CONN_SETUP Condition types */ #define CONN_SETUP_ALLOW_ALL 0x00 #define CONN_SETUP_ALLOW_CLASS 0x01 #define CONN_SETUP_ALLOW_BDADDR 0x02 /* CONN_SETUP Conditions */ #define CONN_SETUP_AUTO_OFF 0x01 #define CONN_SETUP_AUTO_ON 0x02 #define OCF_FLUSH 0x0008 #define OCF_READ_PIN_TYPE 0x0009 typedef struct { uint8_t status; uint8_t pin_type; } __attribute__ ((packed)) read_pin_type_rp; #define READ_PIN_TYPE_RP_SIZE 2 #define OCF_WRITE_PIN_TYPE 0x000A typedef struct { uint8_t pin_type; } __attribute__ ((packed)) write_pin_type_cp; #define WRITE_PIN_TYPE_CP_SIZE 1 #define OCF_CREATE_NEW_UNIT_KEY 0x000B #define OCF_READ_STORED_LINK_KEY 0x000D typedef struct { bdaddr_t bdaddr; uint8_t read_all; } __attribute__ ((packed)) read_stored_link_key_cp; #define READ_STORED_LINK_KEY_CP_SIZE 7 typedef struct { uint8_t status; uint16_t max_keys; uint16_t num_keys; } __attribute__ ((packed)) read_stored_link_key_rp; #define READ_STORED_LINK_KEY_RP_SIZE 5 #define OCF_WRITE_STORED_LINK_KEY 0x0011 typedef struct { uint8_t num_keys; /* variable length part */ } __attribute__ ((packed)) write_stored_link_key_cp; #define WRITE_STORED_LINK_KEY_CP_SIZE 1 typedef struct { uint8_t status; uint8_t num_keys; } __attribute__ ((packed)) write_stored_link_key_rp; #define READ_WRITE_LINK_KEY_RP_SIZE 2 #define OCF_DELETE_STORED_LINK_KEY 0x0012 typedef struct { bdaddr_t bdaddr; uint8_t delete_all; } __attribute__ ((packed)) delete_stored_link_key_cp; #define DELETE_STORED_LINK_KEY_CP_SIZE 7 typedef struct { uint8_t status; uint16_t num_keys; } __attribute__ ((packed)) delete_stored_link_key_rp; #define DELETE_STORED_LINK_KEY_RP_SIZE 3 #define HCI_MAX_NAME_LENGTH 248 #define OCF_CHANGE_LOCAL_NAME 0x0013 typedef struct { uint8_t name[HCI_MAX_NAME_LENGTH]; } __attribute__ ((packed)) change_local_name_cp; #define CHANGE_LOCAL_NAME_CP_SIZE 248 #define OCF_READ_LOCAL_NAME 0x0014 typedef struct { uint8_t status; uint8_t name[HCI_MAX_NAME_LENGTH]; } __attribute__ ((packed)) read_local_name_rp; #define READ_LOCAL_NAME_RP_SIZE 249 #define OCF_READ_CONN_ACCEPT_TIMEOUT 0x0015 typedef struct { uint8_t status; uint16_t timeout; } __attribute__ ((packed)) read_conn_accept_timeout_rp; #define READ_CONN_ACCEPT_TIMEOUT_RP_SIZE 3 #define OCF_WRITE_CONN_ACCEPT_TIMEOUT 0x0016 typedef struct { uint16_t timeout; } __attribute__ ((packed)) write_conn_accept_timeout_cp; #define WRITE_CONN_ACCEPT_TIMEOUT_CP_SIZE 2 #define OCF_READ_PAGE_TIMEOUT 0x0017 typedef struct { uint8_t status; uint16_t timeout; } __attribute__ ((packed)) read_page_timeout_rp; #define READ_PAGE_TIMEOUT_RP_SIZE 3 #define OCF_WRITE_PAGE_TIMEOUT 0x0018 typedef struct { uint16_t timeout; } __attribute__ ((packed)) write_page_timeout_cp; #define WRITE_PAGE_TIMEOUT_CP_SIZE 2 #define OCF_READ_SCAN_ENABLE 0x0019 typedef struct { uint8_t status; uint8_t enable; } __attribute__ ((packed)) read_scan_enable_rp; #define READ_SCAN_ENABLE_RP_SIZE 2 #define OCF_WRITE_SCAN_ENABLE 0x001A #define SCAN_DISABLED 0x00 #define SCAN_INQUIRY 0x01 #define SCAN_PAGE 0x02 #define OCF_READ_PAGE_ACTIVITY 0x001B typedef struct { uint8_t status; uint16_t interval; uint16_t window; } __attribute__ ((packed)) read_page_activity_rp; #define READ_PAGE_ACTIVITY_RP_SIZE 5 #define OCF_WRITE_PAGE_ACTIVITY 0x001C typedef struct { uint16_t interval; uint16_t window; } __attribute__ ((packed)) write_page_activity_cp; #define WRITE_PAGE_ACTIVITY_CP_SIZE 4 #define OCF_READ_INQ_ACTIVITY 0x001D typedef struct { uint8_t status; uint16_t interval; uint16_t window; } __attribute__ ((packed)) read_inq_activity_rp; #define READ_INQ_ACTIVITY_RP_SIZE 5 #define OCF_WRITE_INQ_ACTIVITY 0x001E typedef struct { uint16_t interval; uint16_t window; } __attribute__ ((packed)) write_inq_activity_cp; #define WRITE_INQ_ACTIVITY_CP_SIZE 4 #define OCF_READ_AUTH_ENABLE 0x001F #define OCF_WRITE_AUTH_ENABLE 0x0020 #define AUTH_DISABLED 0x00 #define AUTH_ENABLED 0x01 #define OCF_READ_ENCRYPT_MODE 0x0021 #define OCF_WRITE_ENCRYPT_MODE 0x0022 #define ENCRYPT_DISABLED 0x00 #define ENCRYPT_P2P 0x01 #define ENCRYPT_BOTH 0x02 #define OCF_READ_CLASS_OF_DEV 0x0023 typedef struct { uint8_t status; uint8_t dev_class[3]; } __attribute__ ((packed)) read_class_of_dev_rp; #define READ_CLASS_OF_DEV_RP_SIZE 4 #define OCF_WRITE_CLASS_OF_DEV 0x0024 typedef struct { uint8_t dev_class[3]; } __attribute__ ((packed)) write_class_of_dev_cp; #define WRITE_CLASS_OF_DEV_CP_SIZE 3 #define OCF_READ_VOICE_SETTING 0x0025 typedef struct { uint8_t status; uint16_t voice_setting; } __attribute__ ((packed)) read_voice_setting_rp; #define READ_VOICE_SETTING_RP_SIZE 3 #define OCF_WRITE_VOICE_SETTING 0x0026 typedef struct { uint16_t voice_setting; } __attribute__ ((packed)) write_voice_setting_cp; #define WRITE_VOICE_SETTING_CP_SIZE 2 #define OCF_READ_AUTOMATIC_FLUSH_TIMEOUT 0x0027 #define OCF_WRITE_AUTOMATIC_FLUSH_TIMEOUT 0x0028 #define OCF_READ_NUM_BROADCAST_RETRANS 0x0029 #define OCF_WRITE_NUM_BROADCAST_RETRANS 0x002A #define OCF_READ_HOLD_MODE_ACTIVITY 0x002B #define OCF_WRITE_HOLD_MODE_ACTIVITY 0x002C #define OCF_READ_TRANSMIT_POWER_LEVEL 0x002D typedef struct { uint16_t handle; uint8_t type; } __attribute__ ((packed)) read_transmit_power_level_cp; #define READ_TRANSMIT_POWER_LEVEL_CP_SIZE 3 typedef struct { uint8_t status; uint16_t handle; int8_t level; } __attribute__ ((packed)) read_transmit_power_level_rp; #define READ_TRANSMIT_POWER_LEVEL_RP_SIZE 4 #define OCF_READ_SYNC_FLOW_ENABLE 0x002E #define OCF_WRITE_SYNC_FLOW_ENABLE 0x002F #define OCF_SET_CONTROLLER_TO_HOST_FC 0x0031 #define OCF_HOST_BUFFER_SIZE 0x0033 typedef struct { uint16_t acl_mtu; uint8_t sco_mtu; uint16_t acl_max_pkt; uint16_t sco_max_pkt; } __attribute__ ((packed)) host_buffer_size_cp; #define HOST_BUFFER_SIZE_CP_SIZE 7 #define OCF_HOST_NUM_COMP_PKTS 0x0035 typedef struct { uint8_t num_hndl; /* variable length part */ } __attribute__ ((packed)) host_num_comp_pkts_cp; #define HOST_NUM_COMP_PKTS_CP_SIZE 1 #define OCF_READ_LINK_SUPERVISION_TIMEOUT 0x0036 typedef struct { uint8_t status; uint16_t handle; uint16_t timeout; } __attribute__ ((packed)) read_link_supervision_timeout_rp; #define READ_LINK_SUPERVISION_TIMEOUT_RP_SIZE 5 #define OCF_WRITE_LINK_SUPERVISION_TIMEOUT 0x0037 typedef struct { uint16_t handle; uint16_t timeout; } __attribute__ ((packed)) write_link_supervision_timeout_cp; #define WRITE_LINK_SUPERVISION_TIMEOUT_CP_SIZE 4 typedef struct { uint8_t status; uint16_t handle; } __attribute__ ((packed)) write_link_supervision_timeout_rp; #define WRITE_LINK_SUPERVISION_TIMEOUT_RP_SIZE 3 #define OCF_READ_NUM_SUPPORTED_IAC 0x0038 #define MAX_IAC_LAP 0x40 #define OCF_READ_CURRENT_IAC_LAP 0x0039 typedef struct { uint8_t status; uint8_t num_current_iac; uint8_t lap[MAX_IAC_LAP][3]; } __attribute__ ((packed)) read_current_iac_lap_rp; #define READ_CURRENT_IAC_LAP_RP_SIZE 2+3*MAX_IAC_LAP #define OCF_WRITE_CURRENT_IAC_LAP 0x003A typedef struct { uint8_t num_current_iac; uint8_t lap[MAX_IAC_LAP][3]; } __attribute__ ((packed)) write_current_iac_lap_cp; #define WRITE_CURRENT_IAC_LAP_CP_SIZE 1+3*MAX_IAC_LAP #define OCF_READ_PAGE_SCAN_PERIOD_MODE 0x003B #define OCF_WRITE_PAGE_SCAN_PERIOD_MODE 0x003C #define OCF_READ_PAGE_SCAN_MODE 0x003D #define OCF_WRITE_PAGE_SCAN_MODE 0x003E #define OCF_SET_AFH_CLASSIFICATION 0x003F typedef struct { uint8_t map[10]; } __attribute__ ((packed)) set_afh_classification_cp; #define SET_AFH_CLASSIFICATION_CP_SIZE 10 typedef struct { uint8_t status; } __attribute__ ((packed)) set_afh_classification_rp; #define SET_AFH_CLASSIFICATION_RP_SIZE 1 #define OCF_READ_INQUIRY_SCAN_TYPE 0x0042 typedef struct { uint8_t status; uint8_t type; } __attribute__ ((packed)) read_inquiry_scan_type_rp; #define READ_INQUIRY_SCAN_TYPE_RP_SIZE 2 #define OCF_WRITE_INQUIRY_SCAN_TYPE 0x0043 typedef struct { uint8_t type; } __attribute__ ((packed)) write_inquiry_scan_type_cp; #define WRITE_INQUIRY_SCAN_TYPE_CP_SIZE 1 typedef struct { uint8_t status; } __attribute__ ((packed)) write_inquiry_scan_type_rp; #define WRITE_INQUIRY_SCAN_TYPE_RP_SIZE 1 #define OCF_READ_INQUIRY_MODE 0x0044 typedef struct { uint8_t status; uint8_t mode; } __attribute__ ((packed)) read_inquiry_mode_rp; #define READ_INQUIRY_MODE_RP_SIZE 2 #define OCF_WRITE_INQUIRY_MODE 0x0045 typedef struct { uint8_t mode; } __attribute__ ((packed)) write_inquiry_mode_cp; #define WRITE_INQUIRY_MODE_CP_SIZE 1 typedef struct { uint8_t status; } __attribute__ ((packed)) write_inquiry_mode_rp; #define WRITE_INQUIRY_MODE_RP_SIZE 1 #define OCF_READ_PAGE_SCAN_TYPE 0x0046 #define OCF_WRITE_PAGE_SCAN_TYPE 0x0047 #define PAGE_SCAN_TYPE_STANDARD 0x00 #define PAGE_SCAN_TYPE_INTERLACED 0x01 #define OCF_READ_AFH_MODE 0x0048 typedef struct { uint8_t status; uint8_t mode; } __attribute__ ((packed)) read_afh_mode_rp; #define READ_AFH_MODE_RP_SIZE 2 #define OCF_WRITE_AFH_MODE 0x0049 typedef struct { uint8_t mode; } __attribute__ ((packed)) write_afh_mode_cp; #define WRITE_AFH_MODE_CP_SIZE 1 typedef struct { uint8_t status; } __attribute__ ((packed)) write_afh_mode_rp; #define WRITE_AFH_MODE_RP_SIZE 1 #define HCI_MAX_EIR_LENGTH 240 #define OCF_READ_EXT_INQUIRY_RESPONSE 0x0051 typedef struct { uint8_t status; uint8_t fec; uint8_t data[HCI_MAX_EIR_LENGTH]; } __attribute__ ((packed)) read_ext_inquiry_response_rp; #define READ_EXT_INQUIRY_RESPONSE_RP_SIZE 242 #define OCF_WRITE_EXT_INQUIRY_RESPONSE 0x0052 typedef struct { uint8_t fec; uint8_t data[HCI_MAX_EIR_LENGTH]; } __attribute__ ((packed)) write_ext_inquiry_response_cp; #define WRITE_EXT_INQUIRY_RESPONSE_CP_SIZE 241 typedef struct { uint8_t status; } __attribute__ ((packed)) write_ext_inquiry_response_rp; #define WRITE_EXT_INQUIRY_RESPONSE_RP_SIZE 1 #define OCF_REFRESH_ENCRYPTION_KEY 0x0053 typedef struct { uint16_t handle; } __attribute__ ((packed)) refresh_encryption_key_cp; #define REFRESH_ENCRYPTION_KEY_CP_SIZE 2 typedef struct { uint8_t status; } __attribute__ ((packed)) refresh_encryption_key_rp; #define REFRESH_ENCRYPTION_KEY_RP_SIZE 1 #define OCF_READ_SIMPLE_PAIRING_MODE 0x0055 typedef struct { uint8_t status; uint8_t mode; } __attribute__ ((packed)) read_simple_pairing_mode_rp; #define READ_SIMPLE_PAIRING_MODE_RP_SIZE 2 #define OCF_WRITE_SIMPLE_PAIRING_MODE 0x0056 typedef struct { uint8_t mode; } __attribute__ ((packed)) write_simple_pairing_mode_cp; #define WRITE_SIMPLE_PAIRING_MODE_CP_SIZE 1 typedef struct { uint8_t status; } __attribute__ ((packed)) write_simple_pairing_mode_rp; #define WRITE_SIMPLE_PAIRING_MODE_RP_SIZE 1 #define OCF_READ_LOCAL_OOB_DATA 0x0057 typedef struct { uint8_t status; uint8_t hash[16]; uint8_t randomizer[16]; } __attribute__ ((packed)) read_local_oob_data_rp; #define READ_LOCAL_OOB_DATA_RP_SIZE 33 #define OCF_READ_INQ_RESPONSE_TX_POWER_LEVEL 0x0058 typedef struct { uint8_t status; int8_t level; } __attribute__ ((packed)) read_inq_response_tx_power_level_rp; #define READ_INQ_RESPONSE_TX_POWER_LEVEL_RP_SIZE 2 #define OCF_READ_INQUIRY_TRANSMIT_POWER_LEVEL 0x0058 typedef struct { uint8_t status; int8_t level; } __attribute__ ((packed)) read_inquiry_transmit_power_level_rp; #define READ_INQUIRY_TRANSMIT_POWER_LEVEL_RP_SIZE 2 #define OCF_WRITE_INQUIRY_TRANSMIT_POWER_LEVEL 0x0059 typedef struct { int8_t level; } __attribute__ ((packed)) write_inquiry_transmit_power_level_cp; #define WRITE_INQUIRY_TRANSMIT_POWER_LEVEL_CP_SIZE 1 typedef struct { uint8_t status; } __attribute__ ((packed)) write_inquiry_transmit_power_level_rp; #define WRITE_INQUIRY_TRANSMIT_POWER_LEVEL_RP_SIZE 1 #define OCF_READ_DEFAULT_ERROR_DATA_REPORTING 0x005A typedef struct { uint8_t status; uint8_t reporting; } __attribute__ ((packed)) read_default_error_data_reporting_rp; #define READ_DEFAULT_ERROR_DATA_REPORTING_RP_SIZE 2 #define OCF_WRITE_DEFAULT_ERROR_DATA_REPORTING 0x005B typedef struct { uint8_t reporting; } __attribute__ ((packed)) write_default_error_data_reporting_cp; #define WRITE_DEFAULT_ERROR_DATA_REPORTING_CP_SIZE 1 typedef struct { uint8_t status; } __attribute__ ((packed)) write_default_error_data_reporting_rp; #define WRITE_DEFAULT_ERROR_DATA_REPORTING_RP_SIZE 1 #define OCF_ENHANCED_FLUSH 0x005F typedef struct { uint16_t handle; uint8_t type; } __attribute__ ((packed)) enhanced_flush_cp; #define ENHANCED_FLUSH_CP_SIZE 3 #define OCF_SEND_KEYPRESS_NOTIFY 0x0060 typedef struct { bdaddr_t bdaddr; uint8_t type; } __attribute__ ((packed)) send_keypress_notify_cp; #define SEND_KEYPRESS_NOTIFY_CP_SIZE 7 typedef struct { uint8_t status; } __attribute__ ((packed)) send_keypress_notify_rp; #define SEND_KEYPRESS_NOTIFY_RP_SIZE 1 #define OCF_READ_LOGICAL_LINK_ACCEPT_TIMEOUT 0x0061 typedef struct { uint8_t status; uint16_t timeout; } __attribute__ ((packed)) read_log_link_accept_timeout_rp; #define READ_LOGICAL_LINK_ACCEPT_TIMEOUT_RP_SIZE 3 #define OCF_WRITE_LOGICAL_LINK_ACCEPT_TIMEOUT 0x0062 typedef struct { uint16_t timeout; } __attribute__ ((packed)) write_log_link_accept_timeout_cp; #define WRITE_LOGICAL_LINK_ACCEPT_TIMEOUT_CP_SIZE 2 #define OCF_SET_EVENT_MASK_PAGE_2 0x0063 #define OCF_READ_LOCATION_DATA 0x0064 #define OCF_WRITE_LOCATION_DATA 0x0065 #define OCF_READ_FLOW_CONTROL_MODE 0x0066 #define OCF_WRITE_FLOW_CONTROL_MODE 0x0067 #define OCF_READ_ENHANCED_TRANSMIT_POWER_LEVEL 0x0068 typedef struct { uint8_t status; uint16_t handle; int8_t level_gfsk; int8_t level_dqpsk; int8_t level_8dpsk; } __attribute__ ((packed)) read_enhanced_transmit_power_level_rp; #define READ_ENHANCED_TRANSMIT_POWER_LEVEL_RP_SIZE 6 #define OCF_READ_BEST_EFFORT_FLUSH_TIMEOUT 0x0069 typedef struct { uint8_t status; uint32_t timeout; } __attribute__ ((packed)) read_best_effort_flush_timeout_rp; #define READ_BEST_EFFORT_FLUSH_TIMEOUT_RP_SIZE 5 #define OCF_WRITE_BEST_EFFORT_FLUSH_TIMEOUT 0x006A typedef struct { uint16_t handle; uint32_t timeout; } __attribute__ ((packed)) write_best_effort_flush_timeout_cp; #define WRITE_BEST_EFFORT_FLUSH_TIMEOUT_CP_SIZE 6 typedef struct { uint8_t status; } __attribute__ ((packed)) write_best_effort_flush_timeout_rp; #define WRITE_BEST_EFFORT_FLUSH_TIMEOUT_RP_SIZE 1 #define OCF_READ_LE_HOST_SUPPORTED 0x006C typedef struct { uint8_t status; uint8_t le; uint8_t simul; } __attribute__ ((packed)) read_le_host_supported_rp; #define READ_LE_HOST_SUPPORTED_RP_SIZE 3 #define OCF_WRITE_LE_HOST_SUPPORTED 0x006D typedef struct { uint8_t le; uint8_t simul; } __attribute__ ((packed)) write_le_host_supported_cp; #define WRITE_LE_HOST_SUPPORTED_CP_SIZE 2 /* Informational Parameters */ #define OGF_INFO_PARAM 0x04 #define OCF_READ_LOCAL_VERSION 0x0001 typedef struct { uint8_t status; uint8_t hci_ver; uint16_t hci_rev; uint8_t lmp_ver; uint16_t manufacturer; uint16_t lmp_subver; } __attribute__ ((packed)) read_local_version_rp; #define READ_LOCAL_VERSION_RP_SIZE 9 #define OCF_READ_LOCAL_COMMANDS 0x0002 typedef struct { uint8_t status; uint8_t commands[64]; } __attribute__ ((packed)) read_local_commands_rp; #define READ_LOCAL_COMMANDS_RP_SIZE 65 #define OCF_READ_LOCAL_FEATURES 0x0003 typedef struct { uint8_t status; uint8_t features[8]; } __attribute__ ((packed)) read_local_features_rp; #define READ_LOCAL_FEATURES_RP_SIZE 9 #define OCF_READ_LOCAL_EXT_FEATURES 0x0004 typedef struct { uint8_t page_num; } __attribute__ ((packed)) read_local_ext_features_cp; #define READ_LOCAL_EXT_FEATURES_CP_SIZE 1 typedef struct { uint8_t status; uint8_t page_num; uint8_t max_page_num; uint8_t features[8]; } __attribute__ ((packed)) read_local_ext_features_rp; #define READ_LOCAL_EXT_FEATURES_RP_SIZE 11 #define OCF_READ_BUFFER_SIZE 0x0005 typedef struct { uint8_t status; uint16_t acl_mtu; uint8_t sco_mtu; uint16_t acl_max_pkt; uint16_t sco_max_pkt; } __attribute__ ((packed)) read_buffer_size_rp; #define READ_BUFFER_SIZE_RP_SIZE 8 #define OCF_READ_COUNTRY_CODE 0x0007 #define OCF_READ_BD_ADDR 0x0009 typedef struct { uint8_t status; bdaddr_t bdaddr; } __attribute__ ((packed)) read_bd_addr_rp; #define READ_BD_ADDR_RP_SIZE 7 #define OCF_READ_DATA_BLOCK_SIZE 0x000A typedef struct { uint8_t status; uint16_t max_acl_len; uint16_t data_block_len; uint16_t num_blocks; } __attribute__ ((packed)) read_data_block_size_rp; /* Status params */ #define OGF_STATUS_PARAM 0x05 #define OCF_READ_FAILED_CONTACT_COUNTER 0x0001 typedef struct { uint8_t status; uint16_t handle; uint8_t counter; } __attribute__ ((packed)) read_failed_contact_counter_rp; #define READ_FAILED_CONTACT_COUNTER_RP_SIZE 4 #define OCF_RESET_FAILED_CONTACT_COUNTER 0x0002 typedef struct { uint8_t status; uint16_t handle; } __attribute__ ((packed)) reset_failed_contact_counter_rp; #define RESET_FAILED_CONTACT_COUNTER_RP_SIZE 3 #define OCF_READ_LINK_QUALITY 0x0003 typedef struct { uint8_t status; uint16_t handle; uint8_t link_quality; } __attribute__ ((packed)) read_link_quality_rp; #define READ_LINK_QUALITY_RP_SIZE 4 #define OCF_READ_RSSI 0x0005 typedef struct { uint8_t status; uint16_t handle; int8_t rssi; } __attribute__ ((packed)) read_rssi_rp; #define READ_RSSI_RP_SIZE 4 #define OCF_READ_AFH_MAP 0x0006 typedef struct { uint8_t status; uint16_t handle; uint8_t mode; uint8_t map[10]; } __attribute__ ((packed)) read_afh_map_rp; #define READ_AFH_MAP_RP_SIZE 14 #define OCF_READ_CLOCK 0x0007 typedef struct { uint16_t handle; uint8_t which_clock; } __attribute__ ((packed)) read_clock_cp; #define READ_CLOCK_CP_SIZE 3 typedef struct { uint8_t status; uint16_t handle; uint32_t clock; uint16_t accuracy; } __attribute__ ((packed)) read_clock_rp; #define READ_CLOCK_RP_SIZE 9 #define OCF_READ_LOCAL_AMP_INFO 0x0009 typedef struct { uint8_t status; uint8_t amp_status; uint32_t total_bandwidth; uint32_t max_guaranteed_bandwidth; uint32_t min_latency; uint32_t max_pdu_size; uint8_t controller_type; uint16_t pal_caps; uint16_t max_amp_assoc_length; uint32_t max_flush_timeout; uint32_t best_effort_flush_timeout; } __attribute__ ((packed)) read_local_amp_info_rp; #define READ_LOCAL_AMP_INFO_RP_SIZE 31 #define OCF_READ_LOCAL_AMP_ASSOC 0x000A typedef struct { uint8_t handle; uint16_t length_so_far; uint16_t assoc_length; } __attribute__ ((packed)) read_local_amp_assoc_cp; #define READ_LOCAL_AMP_ASSOC_CP_SIZE 5 typedef struct { uint8_t status; uint8_t handle; uint16_t length; uint8_t fragment[HCI_MAX_NAME_LENGTH]; } __attribute__ ((packed)) read_local_amp_assoc_rp; #define READ_LOCAL_AMP_ASSOC_RP_SIZE 252 #define OCF_WRITE_REMOTE_AMP_ASSOC 0x000B typedef struct { uint8_t handle; uint16_t length_so_far; uint16_t remaining_length; uint8_t fragment[HCI_MAX_NAME_LENGTH]; } __attribute__ ((packed)) write_remote_amp_assoc_cp; #define WRITE_REMOTE_AMP_ASSOC_CP_SIZE 253 typedef struct { uint8_t status; uint8_t handle; } __attribute__ ((packed)) write_remote_amp_assoc_rp; #define WRITE_REMOTE_AMP_ASSOC_RP_SIZE 2 /* Testing commands */ #define OGF_TESTING_CMD 0x3e #define OCF_READ_LOOPBACK_MODE 0x0001 #define OCF_WRITE_LOOPBACK_MODE 0x0002 #define OCF_ENABLE_DEVICE_UNDER_TEST_MODE 0x0003 #define OCF_WRITE_SIMPLE_PAIRING_DEBUG_MODE 0x0004 typedef struct { uint8_t mode; } __attribute__ ((packed)) write_simple_pairing_debug_mode_cp; #define WRITE_SIMPLE_PAIRING_DEBUG_MODE_CP_SIZE 1 typedef struct { uint8_t status; } __attribute__ ((packed)) write_simple_pairing_debug_mode_rp; #define WRITE_SIMPLE_PAIRING_DEBUG_MODE_RP_SIZE 1 /* LE commands */ #define OGF_LE_CTL 0x08 #define OCF_LE_SET_EVENT_MASK 0x0001 typedef struct { uint8_t mask[8]; } __attribute__ ((packed)) le_set_event_mask_cp; #define LE_SET_EVENT_MASK_CP_SIZE 8 #define OCF_LE_READ_BUFFER_SIZE 0x0002 typedef struct { uint8_t status; uint16_t pkt_len; uint8_t max_pkt; } __attribute__ ((packed)) le_read_buffer_size_rp; #define LE_READ_BUFFER_SIZE_RP_SIZE 4 #define OCF_LE_READ_LOCAL_SUPPORTED_FEATURES 0x0003 typedef struct { uint8_t status; uint8_t features[8]; } __attribute__ ((packed)) le_read_local_supported_features_rp; #define LE_READ_LOCAL_SUPPORTED_FEATURES_RP_SIZE 9 #define OCF_LE_SET_RANDOM_ADDRESS 0x0005 typedef struct { bdaddr_t bdaddr; } __attribute__ ((packed)) le_set_random_address_cp; #define LE_SET_RANDOM_ADDRESS_CP_SIZE 6 #define OCF_LE_SET_ADVERTISING_PARAMETERS 0x0006 typedef struct { uint16_t min_interval; uint16_t max_interval; uint8_t advtype; uint8_t own_bdaddr_type; uint8_t direct_bdaddr_type; bdaddr_t direct_bdaddr; uint8_t chan_map; uint8_t filter; } __attribute__ ((packed)) le_set_advertising_parameters_cp; #define LE_SET_ADVERTISING_PARAMETERS_CP_SIZE 15 #define OCF_LE_READ_ADVERTISING_CHANNEL_TX_POWER 0x0007 typedef struct { uint8_t status; int8_t level; } __attribute__ ((packed)) le_read_advertising_channel_tx_power_rp; #define LE_READ_ADVERTISING_CHANNEL_TX_POWER_RP_SIZE 2 #define OCF_LE_SET_ADVERTISING_DATA 0x0008 typedef struct { uint8_t length; uint8_t data[31]; } __attribute__ ((packed)) le_set_advertising_data_cp; #define LE_SET_ADVERTISING_DATA_CP_SIZE 32 #define OCF_LE_SET_SCAN_RESPONSE_DATA 0x0009 typedef struct { uint8_t length; uint8_t data[31]; } __attribute__ ((packed)) le_set_scan_response_data_cp; #define LE_SET_SCAN_RESPONSE_DATA_CP_SIZE 32 #define OCF_LE_SET_ADVERTISE_ENABLE 0x000A typedef struct { uint8_t enable; } __attribute__ ((packed)) le_set_advertise_enable_cp; #define LE_SET_ADVERTISE_ENABLE_CP_SIZE 1 #define OCF_LE_SET_SCAN_PARAMETERS 0x000B typedef struct { uint8_t type; uint16_t interval; uint16_t window; uint8_t own_bdaddr_type; uint8_t filter; } __attribute__ ((packed)) le_set_scan_parameters_cp; #define LE_SET_SCAN_PARAMETERS_CP_SIZE 7 #define OCF_LE_SET_SCAN_ENABLE 0x000C typedef struct { uint8_t enable; uint8_t filter_dup; } __attribute__ ((packed)) le_set_scan_enable_cp; #define LE_SET_SCAN_ENABLE_CP_SIZE 2 #define OCF_LE_CREATE_CONN 0x000D typedef struct { uint16_t interval; uint16_t window; uint8_t initiator_filter; uint8_t peer_bdaddr_type; bdaddr_t peer_bdaddr; uint8_t own_bdaddr_type; uint16_t min_interval; uint16_t max_interval; uint16_t latency; uint16_t supervision_timeout; uint16_t min_ce_length; uint16_t max_ce_length; } __attribute__ ((packed)) le_create_connection_cp; #define LE_CREATE_CONN_CP_SIZE 25 #define OCF_LE_CREATE_CONN_CANCEL 0x000E #define OCF_LE_READ_WHITE_LIST_SIZE 0x000F typedef struct { uint8_t status; uint8_t size; } __attribute__ ((packed)) le_read_white_list_size_rp; #define LE_READ_WHITE_LIST_SIZE_RP_SIZE 2 #define OCF_LE_CLEAR_WHITE_LIST 0x0010 #define OCF_LE_ADD_DEVICE_TO_WHITE_LIST 0x0011 typedef struct { uint8_t bdaddr_type; bdaddr_t bdaddr; } __attribute__ ((packed)) le_add_device_to_white_list_cp; #define LE_ADD_DEVICE_TO_WHITE_LIST_CP_SIZE 7 #define OCF_LE_REMOVE_DEVICE_FROM_WHITE_LIST 0x0012 typedef struct { uint8_t bdaddr_type; bdaddr_t bdaddr; } __attribute__ ((packed)) le_remove_device_from_white_list_cp; #define LE_REMOVE_DEVICE_FROM_WHITE_LIST_CP_SIZE 7 #define OCF_LE_CONN_UPDATE 0x0013 typedef struct { uint16_t handle; uint16_t min_interval; uint16_t max_interval; uint16_t latency; uint16_t supervision_timeout; uint16_t min_ce_length; uint16_t max_ce_length; } __attribute__ ((packed)) le_connection_update_cp; #define LE_CONN_UPDATE_CP_SIZE 14 #define OCF_LE_SET_HOST_CHANNEL_CLASSIFICATION 0x0014 typedef struct { uint8_t map[5]; } __attribute__ ((packed)) le_set_host_channel_classification_cp; #define LE_SET_HOST_CHANNEL_CLASSIFICATION_CP_SIZE 5 #define OCF_LE_READ_CHANNEL_MAP 0x0015 typedef struct { uint16_t handle; } __attribute__ ((packed)) le_read_channel_map_cp; #define LE_READ_CHANNEL_MAP_CP_SIZE 2 typedef struct { uint8_t status; uint16_t handle; uint8_t map[5]; } __attribute__ ((packed)) le_read_channel_map_rp; #define LE_READ_CHANNEL_MAP_RP_SIZE 8 #define OCF_LE_READ_REMOTE_USED_FEATURES 0x0016 typedef struct { uint16_t handle; } __attribute__ ((packed)) le_read_remote_used_features_cp; #define LE_READ_REMOTE_USED_FEATURES_CP_SIZE 2 #define OCF_LE_ENCRYPT 0x0017 typedef struct { uint8_t key[16]; uint8_t plaintext[16]; } __attribute__ ((packed)) le_encrypt_cp; #define LE_ENCRYPT_CP_SIZE 32 typedef struct { uint8_t status; uint8_t data[16]; } __attribute__ ((packed)) le_encrypt_rp; #define LE_ENCRYPT_RP_SIZE 17 #define OCF_LE_RAND 0x0018 typedef struct { uint8_t status; uint64_t random; } __attribute__ ((packed)) le_rand_rp; #define LE_RAND_RP_SIZE 9 #define OCF_LE_START_ENCRYPTION 0x0019 typedef struct { uint16_t handle; uint64_t random; uint16_t diversifier; uint8_t key[16]; } __attribute__ ((packed)) le_start_encryption_cp; #define LE_START_ENCRYPTION_CP_SIZE 28 #define OCF_LE_LTK_REPLY 0x001A typedef struct { uint16_t handle; uint8_t key[16]; } __attribute__ ((packed)) le_ltk_reply_cp; #define LE_LTK_REPLY_CP_SIZE 18 typedef struct { uint8_t status; uint16_t handle; } __attribute__ ((packed)) le_ltk_reply_rp; #define LE_LTK_REPLY_RP_SIZE 3 #define OCF_LE_LTK_NEG_REPLY 0x001B typedef struct { uint16_t handle; } __attribute__ ((packed)) le_ltk_neg_reply_cp; #define LE_LTK_NEG_REPLY_CP_SIZE 2 typedef struct { uint8_t status; uint16_t handle; } __attribute__ ((packed)) le_ltk_neg_reply_rp; #define LE_LTK_NEG_REPLY_RP_SIZE 3 #define OCF_LE_READ_SUPPORTED_STATES 0x001C typedef struct { uint8_t status; uint64_t states; } __attribute__ ((packed)) le_read_supported_states_rp; #define LE_READ_SUPPORTED_STATES_RP_SIZE 9 #define OCF_LE_RECEIVER_TEST 0x001D typedef struct { uint8_t frequency; } __attribute__ ((packed)) le_receiver_test_cp; #define LE_RECEIVER_TEST_CP_SIZE 1 #define OCF_LE_TRANSMITTER_TEST 0x001E typedef struct { uint8_t frequency; uint8_t length; uint8_t payload; } __attribute__ ((packed)) le_transmitter_test_cp; #define LE_TRANSMITTER_TEST_CP_SIZE 3 #define OCF_LE_TEST_END 0x001F typedef struct { uint8_t status; uint16_t num_pkts; } __attribute__ ((packed)) le_test_end_rp; #define LE_TEST_END_RP_SIZE 3 #define OCF_LE_ADD_DEVICE_TO_RESOLV_LIST 0x0027 typedef struct { uint8_t bdaddr_type; bdaddr_t bdaddr; uint8_t peer_irk[16]; uint8_t local_irk[16]; } __attribute__ ((packed)) le_add_device_to_resolv_list_cp; #define LE_ADD_DEVICE_TO_RESOLV_LIST_CP_SIZE 39 #define OCF_LE_REMOVE_DEVICE_FROM_RESOLV_LIST 0x0028 typedef struct { uint8_t bdaddr_type; bdaddr_t bdaddr; } __attribute__ ((packed)) le_remove_device_from_resolv_list_cp; #define LE_REMOVE_DEVICE_FROM_RESOLV_LIST_CP_SIZE 7 #define OCF_LE_CLEAR_RESOLV_LIST 0x0029 #define OCF_LE_READ_RESOLV_LIST_SIZE 0x002A typedef struct { uint8_t status; uint8_t size; } __attribute__ ((packed)) le_read_resolv_list_size_rp; #define LE_READ_RESOLV_LIST_SIZE_RP_SIZE 2 #define OCF_LE_SET_ADDRESS_RESOLUTION_ENABLE 0x002D typedef struct { uint8_t enable; } __attribute__ ((packed)) le_set_address_resolution_enable_cp; #define LE_SET_ADDRESS_RESOLUTION_ENABLE_CP_SIZE 1 /* Vendor specific commands */ #define OGF_VENDOR_CMD 0x3f /* ---- HCI Events ---- */ #define EVT_INQUIRY_COMPLETE 0x01 #define EVT_INQUIRY_RESULT 0x02 typedef struct { bdaddr_t bdaddr; uint8_t pscan_rep_mode; uint8_t pscan_period_mode; uint8_t pscan_mode; uint8_t dev_class[3]; uint16_t clock_offset; } __attribute__ ((packed)) inquiry_info; #define INQUIRY_INFO_SIZE 14 #define EVT_CONN_COMPLETE 0x03 typedef struct { uint8_t status; uint16_t handle; bdaddr_t bdaddr; uint8_t link_type; uint8_t encr_mode; } __attribute__ ((packed)) evt_conn_complete; #define EVT_CONN_COMPLETE_SIZE 11 #define EVT_CONN_REQUEST 0x04 typedef struct { bdaddr_t bdaddr; uint8_t dev_class[3]; uint8_t link_type; } __attribute__ ((packed)) evt_conn_request; #define EVT_CONN_REQUEST_SIZE 10 #define EVT_DISCONN_COMPLETE 0x05 typedef struct { uint8_t status; uint16_t handle; uint8_t reason; } __attribute__ ((packed)) evt_disconn_complete; #define EVT_DISCONN_COMPLETE_SIZE 4 #define EVT_AUTH_COMPLETE 0x06 typedef struct { uint8_t status; uint16_t handle; } __attribute__ ((packed)) evt_auth_complete; #define EVT_AUTH_COMPLETE_SIZE 3 #define EVT_REMOTE_NAME_REQ_COMPLETE 0x07 typedef struct { uint8_t status; bdaddr_t bdaddr; uint8_t name[HCI_MAX_NAME_LENGTH]; } __attribute__ ((packed)) evt_remote_name_req_complete; #define EVT_REMOTE_NAME_REQ_COMPLETE_SIZE 255 #define EVT_ENCRYPT_CHANGE 0x08 typedef struct { uint8_t status; uint16_t handle; uint8_t encrypt; } __attribute__ ((packed)) evt_encrypt_change; #define EVT_ENCRYPT_CHANGE_SIZE 4 #define EVT_CHANGE_CONN_LINK_KEY_COMPLETE 0x09 typedef struct { uint8_t status; uint16_t handle; } __attribute__ ((packed)) evt_change_conn_link_key_complete; #define EVT_CHANGE_CONN_LINK_KEY_COMPLETE_SIZE 3 #define EVT_MASTER_LINK_KEY_COMPLETE 0x0A typedef struct { uint8_t status; uint16_t handle; uint8_t key_flag; } __attribute__ ((packed)) evt_master_link_key_complete; #define EVT_MASTER_LINK_KEY_COMPLETE_SIZE 4 #define EVT_READ_REMOTE_FEATURES_COMPLETE 0x0B typedef struct { uint8_t status; uint16_t handle; uint8_t features[8]; } __attribute__ ((packed)) evt_read_remote_features_complete; #define EVT_READ_REMOTE_FEATURES_COMPLETE_SIZE 11 #define EVT_READ_REMOTE_VERSION_COMPLETE 0x0C typedef struct { uint8_t status; uint16_t handle; uint8_t lmp_ver; uint16_t manufacturer; uint16_t lmp_subver; } __attribute__ ((packed)) evt_read_remote_version_complete; #define EVT_READ_REMOTE_VERSION_COMPLETE_SIZE 8 #define EVT_QOS_SETUP_COMPLETE 0x0D typedef struct { uint8_t status; uint16_t handle; uint8_t flags; /* Reserved */ hci_qos qos; } __attribute__ ((packed)) evt_qos_setup_complete; #define EVT_QOS_SETUP_COMPLETE_SIZE (4 + HCI_QOS_CP_SIZE) #define EVT_CMD_COMPLETE 0x0E typedef struct { uint8_t ncmd; uint16_t opcode; } __attribute__ ((packed)) evt_cmd_complete; #define EVT_CMD_COMPLETE_SIZE 3 #define EVT_CMD_STATUS 0x0F typedef struct { uint8_t status; uint8_t ncmd; uint16_t opcode; } __attribute__ ((packed)) evt_cmd_status; #define EVT_CMD_STATUS_SIZE 4 #define EVT_HARDWARE_ERROR 0x10 typedef struct { uint8_t code; } __attribute__ ((packed)) evt_hardware_error; #define EVT_HARDWARE_ERROR_SIZE 1 #define EVT_FLUSH_OCCURRED 0x11 typedef struct { uint16_t handle; } __attribute__ ((packed)) evt_flush_occured; #define EVT_FLUSH_OCCURRED_SIZE 2 #define EVT_ROLE_CHANGE 0x12 typedef struct { uint8_t status; bdaddr_t bdaddr; uint8_t role; } __attribute__ ((packed)) evt_role_change; #define EVT_ROLE_CHANGE_SIZE 8 #define EVT_NUM_COMP_PKTS 0x13 typedef struct { uint8_t num_hndl; /* variable length part */ } __attribute__ ((packed)) evt_num_comp_pkts; #define EVT_NUM_COMP_PKTS_SIZE 1 #define EVT_MODE_CHANGE 0x14 typedef struct { uint8_t status; uint16_t handle; uint8_t mode; uint16_t interval; } __attribute__ ((packed)) evt_mode_change; #define EVT_MODE_CHANGE_SIZE 6 #define EVT_RETURN_LINK_KEYS 0x15 typedef struct { uint8_t num_keys; /* variable length part */ } __attribute__ ((packed)) evt_return_link_keys; #define EVT_RETURN_LINK_KEYS_SIZE 1 #define EVT_PIN_CODE_REQ 0x16 typedef struct { bdaddr_t bdaddr; } __attribute__ ((packed)) evt_pin_code_req; #define EVT_PIN_CODE_REQ_SIZE 6 #define EVT_LINK_KEY_REQ 0x17 typedef struct { bdaddr_t bdaddr; } __attribute__ ((packed)) evt_link_key_req; #define EVT_LINK_KEY_REQ_SIZE 6 #define EVT_LINK_KEY_NOTIFY 0x18 typedef struct { bdaddr_t bdaddr; uint8_t link_key[16]; uint8_t key_type; } __attribute__ ((packed)) evt_link_key_notify; #define EVT_LINK_KEY_NOTIFY_SIZE 23 #define EVT_LOOPBACK_COMMAND 0x19 #define EVT_DATA_BUFFER_OVERFLOW 0x1A typedef struct { uint8_t link_type; } __attribute__ ((packed)) evt_data_buffer_overflow; #define EVT_DATA_BUFFER_OVERFLOW_SIZE 1 #define EVT_MAX_SLOTS_CHANGE 0x1B typedef struct { uint16_t handle; uint8_t max_slots; } __attribute__ ((packed)) evt_max_slots_change; #define EVT_MAX_SLOTS_CHANGE_SIZE 3 #define EVT_READ_CLOCK_OFFSET_COMPLETE 0x1C typedef struct { uint8_t status; uint16_t handle; uint16_t clock_offset; } __attribute__ ((packed)) evt_read_clock_offset_complete; #define EVT_READ_CLOCK_OFFSET_COMPLETE_SIZE 5 #define EVT_CONN_PTYPE_CHANGED 0x1D typedef struct { uint8_t status; uint16_t handle; uint16_t ptype; } __attribute__ ((packed)) evt_conn_ptype_changed; #define EVT_CONN_PTYPE_CHANGED_SIZE 5 #define EVT_QOS_VIOLATION 0x1E typedef struct { uint16_t handle; } __attribute__ ((packed)) evt_qos_violation; #define EVT_QOS_VIOLATION_SIZE 2 #define EVT_PSCAN_REP_MODE_CHANGE 0x20 typedef struct { bdaddr_t bdaddr; uint8_t pscan_rep_mode; } __attribute__ ((packed)) evt_pscan_rep_mode_change; #define EVT_PSCAN_REP_MODE_CHANGE_SIZE 7 #define EVT_FLOW_SPEC_COMPLETE 0x21 typedef struct { uint8_t status; uint16_t handle; uint8_t flags; uint8_t direction; hci_qos qos; } __attribute__ ((packed)) evt_flow_spec_complete; #define EVT_FLOW_SPEC_COMPLETE_SIZE (5 + HCI_QOS_CP_SIZE) #define EVT_INQUIRY_RESULT_WITH_RSSI 0x22 typedef struct { bdaddr_t bdaddr; uint8_t pscan_rep_mode; uint8_t pscan_period_mode; uint8_t dev_class[3]; uint16_t clock_offset; int8_t rssi; } __attribute__ ((packed)) inquiry_info_with_rssi; #define INQUIRY_INFO_WITH_RSSI_SIZE 14 typedef struct { bdaddr_t bdaddr; uint8_t pscan_rep_mode; uint8_t pscan_period_mode; uint8_t pscan_mode; uint8_t dev_class[3]; uint16_t clock_offset; int8_t rssi; } __attribute__ ((packed)) inquiry_info_with_rssi_and_pscan_mode; #define INQUIRY_INFO_WITH_RSSI_AND_PSCAN_MODE_SIZE 15 #define EVT_READ_REMOTE_EXT_FEATURES_COMPLETE 0x23 typedef struct { uint8_t status; uint16_t handle; uint8_t page_num; uint8_t max_page_num; uint8_t features[8]; } __attribute__ ((packed)) evt_read_remote_ext_features_complete; #define EVT_READ_REMOTE_EXT_FEATURES_COMPLETE_SIZE 13 #define EVT_SYNC_CONN_COMPLETE 0x2C typedef struct { uint8_t status; uint16_t handle; bdaddr_t bdaddr; uint8_t link_type; uint8_t trans_interval; uint8_t retrans_window; uint16_t rx_pkt_len; uint16_t tx_pkt_len; uint8_t air_mode; } __attribute__ ((packed)) evt_sync_conn_complete; #define EVT_SYNC_CONN_COMPLETE_SIZE 17 #define EVT_SYNC_CONN_CHANGED 0x2D typedef struct { uint8_t status; uint16_t handle; uint8_t trans_interval; uint8_t retrans_window; uint16_t rx_pkt_len; uint16_t tx_pkt_len; } __attribute__ ((packed)) evt_sync_conn_changed; #define EVT_SYNC_CONN_CHANGED_SIZE 9 #define EVT_SNIFF_SUBRATING 0x2E typedef struct { uint8_t status; uint16_t handle; uint16_t max_tx_latency; uint16_t max_rx_latency; uint16_t min_remote_timeout; uint16_t min_local_timeout; } __attribute__ ((packed)) evt_sniff_subrating; #define EVT_SNIFF_SUBRATING_SIZE 11 #define EVT_EXTENDED_INQUIRY_RESULT 0x2F typedef struct { bdaddr_t bdaddr; uint8_t pscan_rep_mode; uint8_t pscan_period_mode; uint8_t dev_class[3]; uint16_t clock_offset; int8_t rssi; uint8_t data[HCI_MAX_EIR_LENGTH]; } __attribute__ ((packed)) extended_inquiry_info; #define EXTENDED_INQUIRY_INFO_SIZE 254 #define EVT_ENCRYPTION_KEY_REFRESH_COMPLETE 0x30 typedef struct { uint8_t status; uint16_t handle; } __attribute__ ((packed)) evt_encryption_key_refresh_complete; #define EVT_ENCRYPTION_KEY_REFRESH_COMPLETE_SIZE 3 #define EVT_IO_CAPABILITY_REQUEST 0x31 typedef struct { bdaddr_t bdaddr; } __attribute__ ((packed)) evt_io_capability_request; #define EVT_IO_CAPABILITY_REQUEST_SIZE 6 #define EVT_IO_CAPABILITY_RESPONSE 0x32 typedef struct { bdaddr_t bdaddr; uint8_t capability; uint8_t oob_data; uint8_t authentication; } __attribute__ ((packed)) evt_io_capability_response; #define EVT_IO_CAPABILITY_RESPONSE_SIZE 9 #define EVT_USER_CONFIRM_REQUEST 0x33 typedef struct { bdaddr_t bdaddr; uint32_t passkey; } __attribute__ ((packed)) evt_user_confirm_request; #define EVT_USER_CONFIRM_REQUEST_SIZE 10 #define EVT_USER_PASSKEY_REQUEST 0x34 typedef struct { bdaddr_t bdaddr; } __attribute__ ((packed)) evt_user_passkey_request; #define EVT_USER_PASSKEY_REQUEST_SIZE 6 #define EVT_REMOTE_OOB_DATA_REQUEST 0x35 typedef struct { bdaddr_t bdaddr; } __attribute__ ((packed)) evt_remote_oob_data_request; #define EVT_REMOTE_OOB_DATA_REQUEST_SIZE 6 #define EVT_SIMPLE_PAIRING_COMPLETE 0x36 typedef struct { uint8_t status; bdaddr_t bdaddr; } __attribute__ ((packed)) evt_simple_pairing_complete; #define EVT_SIMPLE_PAIRING_COMPLETE_SIZE 7 #define EVT_LINK_SUPERVISION_TIMEOUT_CHANGED 0x38 typedef struct { uint16_t handle; uint16_t timeout; } __attribute__ ((packed)) evt_link_supervision_timeout_changed; #define EVT_LINK_SUPERVISION_TIMEOUT_CHANGED_SIZE 4 #define EVT_ENHANCED_FLUSH_COMPLETE 0x39 typedef struct { uint16_t handle; } __attribute__ ((packed)) evt_enhanced_flush_complete; #define EVT_ENHANCED_FLUSH_COMPLETE_SIZE 2 #define EVT_USER_PASSKEY_NOTIFY 0x3B typedef struct { bdaddr_t bdaddr; uint32_t passkey; } __attribute__ ((packed)) evt_user_passkey_notify; #define EVT_USER_PASSKEY_NOTIFY_SIZE 10 #define EVT_KEYPRESS_NOTIFY 0x3C typedef struct { bdaddr_t bdaddr; uint8_t type; } __attribute__ ((packed)) evt_keypress_notify; #define EVT_KEYPRESS_NOTIFY_SIZE 7 #define EVT_REMOTE_HOST_FEATURES_NOTIFY 0x3D typedef struct { bdaddr_t bdaddr; uint8_t features[8]; } __attribute__ ((packed)) evt_remote_host_features_notify; #define EVT_REMOTE_HOST_FEATURES_NOTIFY_SIZE 14 #define EVT_LE_META_EVENT 0x3E typedef struct { uint8_t subevent; uint8_t data[]; } __attribute__ ((packed)) evt_le_meta_event; #define EVT_LE_META_EVENT_SIZE 1 #define EVT_LE_CONN_COMPLETE 0x01 typedef struct { uint8_t status; uint16_t handle; uint8_t role; uint8_t peer_bdaddr_type; bdaddr_t peer_bdaddr; uint16_t interval; uint16_t latency; uint16_t supervision_timeout; uint8_t master_clock_accuracy; } __attribute__ ((packed)) evt_le_connection_complete; #define EVT_LE_CONN_COMPLETE_SIZE 18 #define EVT_LE_ADVERTISING_REPORT 0x02 typedef struct { uint8_t evt_type; uint8_t bdaddr_type; bdaddr_t bdaddr; uint8_t length; uint8_t data[]; } __attribute__ ((packed)) le_advertising_info; #define LE_ADVERTISING_INFO_SIZE 9 #define EVT_LE_CONN_UPDATE_COMPLETE 0x03 typedef struct { uint8_t status; uint16_t handle; uint16_t interval; uint16_t latency; uint16_t supervision_timeout; } __attribute__ ((packed)) evt_le_connection_update_complete; #define EVT_LE_CONN_UPDATE_COMPLETE_SIZE 9 #define EVT_LE_READ_REMOTE_USED_FEATURES_COMPLETE 0x04 typedef struct { uint8_t status; uint16_t handle; uint8_t features[8]; } __attribute__ ((packed)) evt_le_read_remote_used_features_complete; #define EVT_LE_READ_REMOTE_USED_FEATURES_COMPLETE_SIZE 11 #define EVT_LE_LTK_REQUEST 0x05 typedef struct { uint16_t handle; uint64_t random; uint16_t diversifier; } __attribute__ ((packed)) evt_le_long_term_key_request; #define EVT_LE_LTK_REQUEST_SIZE 12 #define EVT_PHYSICAL_LINK_COMPLETE 0x40 typedef struct { uint8_t status; uint8_t handle; } __attribute__ ((packed)) evt_physical_link_complete; #define EVT_PHYSICAL_LINK_COMPLETE_SIZE 2 #define EVT_CHANNEL_SELECTED 0x41 #define EVT_DISCONNECT_PHYSICAL_LINK_COMPLETE 0x42 typedef struct { uint8_t status; uint8_t handle; uint8_t reason; } __attribute__ ((packed)) evt_disconn_physical_link_complete; #define EVT_DISCONNECT_PHYSICAL_LINK_COMPLETE_SIZE 3 #define EVT_PHYSICAL_LINK_LOSS_EARLY_WARNING 0x43 typedef struct { uint8_t handle; uint8_t reason; } __attribute__ ((packed)) evt_physical_link_loss_warning; #define EVT_PHYSICAL_LINK_LOSS_WARNING_SIZE 2 #define EVT_PHYSICAL_LINK_RECOVERY 0x44 typedef struct { uint8_t handle; } __attribute__ ((packed)) evt_physical_link_recovery; #define EVT_PHYSICAL_LINK_RECOVERY_SIZE 1 #define EVT_LOGICAL_LINK_COMPLETE 0x45 typedef struct { uint8_t status; uint16_t log_handle; uint8_t handle; uint8_t tx_flow_id; } __attribute__ ((packed)) evt_logical_link_complete; #define EVT_LOGICAL_LINK_COMPLETE_SIZE 5 #define EVT_DISCONNECT_LOGICAL_LINK_COMPLETE 0x46 #define EVT_FLOW_SPEC_MODIFY_COMPLETE 0x47 typedef struct { uint8_t status; uint16_t handle; } __attribute__ ((packed)) evt_flow_spec_modify_complete; #define EVT_FLOW_SPEC_MODIFY_COMPLETE_SIZE 3 #define EVT_NUMBER_COMPLETED_BLOCKS 0x48 typedef struct { uint16_t handle; uint16_t num_cmplt_pkts; uint16_t num_cmplt_blks; } __attribute__ ((packed)) cmplt_handle; typedef struct { uint16_t total_num_blocks; uint8_t num_handles; cmplt_handle handles[]; } __attribute__ ((packed)) evt_num_completed_blocks; #define EVT_AMP_STATUS_CHANGE 0x4D typedef struct { uint8_t status; uint8_t amp_status; } __attribute__ ((packed)) evt_amp_status_change; #define EVT_AMP_STATUS_CHANGE_SIZE 2 #define EVT_TESTING 0xFE #define EVT_VENDOR 0xFF /* Internal events generated by BlueZ stack */ #define EVT_STACK_INTERNAL 0xFD typedef struct { uint16_t type; uint8_t data[]; } __attribute__ ((packed)) evt_stack_internal; #define EVT_STACK_INTERNAL_SIZE 2 #define EVT_SI_DEVICE 0x01 typedef struct { uint16_t event; uint16_t dev_id; } __attribute__ ((packed)) evt_si_device; #define EVT_SI_DEVICE_SIZE 4 /* -------- HCI Packet structures -------- */ #define HCI_TYPE_LEN 1 typedef struct { uint16_t opcode; /* OCF & OGF */ uint8_t plen; } __attribute__ ((packed)) hci_command_hdr; #define HCI_COMMAND_HDR_SIZE 3 typedef struct { uint8_t evt; uint8_t plen; } __attribute__ ((packed)) hci_event_hdr; #define HCI_EVENT_HDR_SIZE 2 typedef struct { uint16_t handle; /* Handle & Flags(PB, BC) */ uint16_t dlen; } __attribute__ ((packed)) hci_acl_hdr; #define HCI_ACL_HDR_SIZE 4 typedef struct { uint16_t handle; uint8_t dlen; } __attribute__ ((packed)) hci_sco_hdr; #define HCI_SCO_HDR_SIZE 3 typedef struct { uint16_t device; uint16_t type; uint16_t plen; } __attribute__ ((packed)) hci_msg_hdr; #define HCI_MSG_HDR_SIZE 6 /* Command opcode pack/unpack */ #define cmd_opcode_pack(ogf, ocf) (uint16_t)((ocf & 0x03ff)|(ogf << 10)) #define cmd_opcode_ogf(op) (op >> 10) #define cmd_opcode_ocf(op) (op & 0x03ff) /* ACL handle and flags pack/unpack */ #define acl_handle_pack(h, f) (uint16_t)((h & 0x0fff)|(f << 12)) #define acl_handle(h) (h & 0x0fff) #define acl_flags(h) (h >> 12) #endif /* _NO_HCI_DEFS */ /* HCI Socket options */ #define HCI_DATA_DIR 1 #define HCI_FILTER 2 #define HCI_TIME_STAMP 3 /* HCI CMSG flags */ #define HCI_CMSG_DIR 0x0001 #define HCI_CMSG_TSTAMP 0x0002 struct sockaddr_hci { sa_family_t hci_family; unsigned short hci_dev; unsigned short hci_channel; }; #define HCI_DEV_NONE 0xffff #define HCI_CHANNEL_RAW 0 #define HCI_CHANNEL_USER 1 #define HCI_CHANNEL_MONITOR 2 #define HCI_CHANNEL_CONTROL 3 #define HCI_CHANNEL_LOGGING 4 struct hci_filter { uint32_t type_mask; uint32_t event_mask[2]; uint16_t opcode; }; #define HCI_FLT_TYPE_BITS 31 #define HCI_FLT_EVENT_BITS 63 #define HCI_FLT_OGF_BITS 63 #define HCI_FLT_OCF_BITS 127 /* Ioctl requests structures */ struct hci_dev_stats { uint32_t err_rx; uint32_t err_tx; uint32_t cmd_tx; uint32_t evt_rx; uint32_t acl_tx; uint32_t acl_rx; uint32_t sco_tx; uint32_t sco_rx; uint32_t byte_rx; uint32_t byte_tx; }; struct hci_dev_info { uint16_t dev_id; char name[8]; bdaddr_t bdaddr; uint32_t flags; uint8_t type; uint8_t features[8]; uint32_t pkt_type; uint32_t link_policy; uint32_t link_mode; uint16_t acl_mtu; uint16_t acl_pkts; uint16_t sco_mtu; uint16_t sco_pkts; struct hci_dev_stats stat; }; struct hci_conn_info { uint16_t handle; bdaddr_t bdaddr; uint8_t type; uint8_t out; uint16_t state; uint32_t link_mode; }; struct hci_dev_req { uint16_t dev_id; uint32_t dev_opt; }; struct hci_dev_list_req { uint16_t dev_num; struct hci_dev_req dev_req[]; /* hci_dev_req structures */ }; struct hci_conn_list_req { uint16_t dev_id; uint16_t conn_num; struct hci_conn_info conn_info[]; }; struct hci_conn_info_req { bdaddr_t bdaddr; uint8_t type; struct hci_conn_info conn_info[]; }; struct hci_auth_info_req { bdaddr_t bdaddr; uint8_t type; }; struct hci_inquiry_req { uint16_t dev_id; uint16_t flags; uint8_t lap[3]; uint8_t length; uint8_t num_rsp; }; #define IREQ_CACHE_FLUSH 0x0001 #ifdef __cplusplus } #endif #endif /* __HCI_H */ bluez-5.82/lib/PaxHeaders/hci_lib.h0000644000000000000000000000005014572354773014177 xustar0020 atime=1743515801 20 ctime=1743591275 bluez-5.82/lib/hci_lib.h0000644000000000000000000002314214572354773013662 0ustar00rootroot/* SPDX-License-Identifier: GPL-2.0-or-later */ /* * * BlueZ - Bluetooth protocol stack for Linux * * Copyright (C) 2000-2001 Qualcomm Incorporated * Copyright (C) 2002-2003 Maxim Krasnyansky * Copyright (C) 2002-2010 Marcel Holtmann * * */ #ifndef __HCI_LIB_H #define __HCI_LIB_H #ifdef __cplusplus extern "C" { #endif struct hci_request { uint16_t ogf; uint16_t ocf; int event; void *cparam; int clen; void *rparam; int rlen; }; struct hci_version { uint16_t manufacturer; uint8_t hci_ver; uint16_t hci_rev; uint8_t lmp_ver; uint16_t lmp_subver; }; int hci_open_dev(int dev_id); int hci_close_dev(int dd); int hci_send_cmd(int dd, uint16_t ogf, uint16_t ocf, uint8_t plen, void *param); int hci_send_req(int dd, struct hci_request *req, int timeout); int hci_create_connection(int dd, const bdaddr_t *bdaddr, uint16_t ptype, uint16_t clkoffset, uint8_t rswitch, uint16_t *handle, int to); int hci_disconnect(int dd, uint16_t handle, uint8_t reason, int to); int hci_inquiry(int dev_id, int len, int num_rsp, const uint8_t *lap, inquiry_info **ii, long flags); int hci_devinfo(int dev_id, struct hci_dev_info *di); int hci_devba(int dev_id, bdaddr_t *bdaddr); int hci_devid(const char *str); int hci_read_local_name(int dd, int len, char *name, int to); int hci_write_local_name(int dd, const char *name, int to); int hci_read_remote_name(int dd, const bdaddr_t *bdaddr, int len, char *name, int to); int hci_read_remote_name_with_clock_offset(int dd, const bdaddr_t *bdaddr, uint8_t pscan_rep_mode, uint16_t clkoffset, int len, char *name, int to); int hci_read_remote_name_cancel(int dd, const bdaddr_t *bdaddr, int to); int hci_read_remote_version(int dd, uint16_t handle, struct hci_version *ver, int to); int hci_read_remote_features(int dd, uint16_t handle, uint8_t *features, int to); int hci_read_remote_ext_features(int dd, uint16_t handle, uint8_t page, uint8_t *max_page, uint8_t *features, int to); int hci_read_clock_offset(int dd, uint16_t handle, uint16_t *clkoffset, int to); int hci_read_local_version(int dd, struct hci_version *ver, int to); int hci_read_local_commands(int dd, uint8_t *commands, int to); int hci_read_local_features(int dd, uint8_t *features, int to); int hci_read_local_ext_features(int dd, uint8_t page, uint8_t *max_page, uint8_t *features, int to); int hci_read_bd_addr(int dd, bdaddr_t *bdaddr, int to); int hci_read_class_of_dev(int dd, uint8_t *cls, int to); int hci_write_class_of_dev(int dd, uint32_t cls, int to); int hci_read_voice_setting(int dd, uint16_t *vs, int to); int hci_write_voice_setting(int dd, uint16_t vs, int to); int hci_read_current_iac_lap(int dd, uint8_t *num_iac, uint8_t *lap, int to); int hci_write_current_iac_lap(int dd, uint8_t num_iac, uint8_t *lap, int to); int hci_read_stored_link_key(int dd, bdaddr_t *bdaddr, uint8_t all, int to); int hci_write_stored_link_key(int dd, bdaddr_t *bdaddr, uint8_t *key, int to); int hci_delete_stored_link_key(int dd, bdaddr_t *bdaddr, uint8_t all, int to); int hci_authenticate_link(int dd, uint16_t handle, int to); int hci_encrypt_link(int dd, uint16_t handle, uint8_t encrypt, int to); int hci_change_link_key(int dd, uint16_t handle, int to); int hci_switch_role(int dd, bdaddr_t *bdaddr, uint8_t role, int to); int hci_park_mode(int dd, uint16_t handle, uint16_t max_interval, uint16_t min_interval, int to); int hci_exit_park_mode(int dd, uint16_t handle, int to); int hci_read_inquiry_scan_type(int dd, uint8_t *type, int to); int hci_write_inquiry_scan_type(int dd, uint8_t type, int to); int hci_read_inquiry_mode(int dd, uint8_t *mode, int to); int hci_write_inquiry_mode(int dd, uint8_t mode, int to); int hci_read_afh_mode(int dd, uint8_t *mode, int to); int hci_write_afh_mode(int dd, uint8_t mode, int to); int hci_read_ext_inquiry_response(int dd, uint8_t *fec, uint8_t *data, int to); int hci_write_ext_inquiry_response(int dd, uint8_t fec, uint8_t *data, int to); int hci_read_simple_pairing_mode(int dd, uint8_t *mode, int to); int hci_write_simple_pairing_mode(int dd, uint8_t mode, int to); int hci_read_local_oob_data(int dd, uint8_t *hash, uint8_t *randomizer, int to); int hci_read_inq_response_tx_power_level(int dd, int8_t *level, int to); int hci_read_inquiry_transmit_power_level(int dd, int8_t *level, int to); int hci_write_inquiry_transmit_power_level(int dd, int8_t level, int to); int hci_read_transmit_power_level(int dd, uint16_t handle, uint8_t type, int8_t *level, int to); int hci_read_link_policy(int dd, uint16_t handle, uint16_t *policy, int to); int hci_write_link_policy(int dd, uint16_t handle, uint16_t policy, int to); int hci_read_link_supervision_timeout(int dd, uint16_t handle, uint16_t *timeout, int to); int hci_write_link_supervision_timeout(int dd, uint16_t handle, uint16_t timeout, int to); int hci_set_afh_classification(int dd, uint8_t *map, int to); int hci_read_link_quality(int dd, uint16_t handle, uint8_t *link_quality, int to); int hci_read_rssi(int dd, uint16_t handle, int8_t *rssi, int to); int hci_read_afh_map(int dd, uint16_t handle, uint8_t *mode, uint8_t *map, int to); int hci_read_clock(int dd, uint16_t handle, uint8_t which, uint32_t *clock, uint16_t *accuracy, int to); int hci_le_set_scan_enable(int dev_id, uint8_t enable, uint8_t filter_dup, int to); int hci_le_set_scan_parameters(int dev_id, uint8_t type, uint16_t interval, uint16_t window, uint8_t own_type, uint8_t filter, int to); int hci_le_set_advertise_enable(int dev_id, uint8_t enable, int to); int hci_le_create_conn(int dd, uint16_t interval, uint16_t window, uint8_t initiator_filter, uint8_t peer_bdaddr_type, bdaddr_t peer_bdaddr, uint8_t own_bdaddr_type, uint16_t min_interval, uint16_t max_interval, uint16_t latency, uint16_t supervision_timeout, uint16_t min_ce_length, uint16_t max_ce_length, uint16_t *handle, int to); int hci_le_conn_update(int dd, uint16_t handle, uint16_t min_interval, uint16_t max_interval, uint16_t latency, uint16_t supervision_timeout, int to); int hci_le_add_white_list(int dd, const bdaddr_t *bdaddr, uint8_t type, int to); int hci_le_rm_white_list(int dd, const bdaddr_t *bdaddr, uint8_t type, int to); int hci_le_read_white_list_size(int dd, uint8_t *size, int to); int hci_le_clear_white_list(int dd, int to); int hci_le_add_resolving_list(int dd, const bdaddr_t *bdaddr, uint8_t type, uint8_t *peer_irk, uint8_t *local_irk, int to); int hci_le_rm_resolving_list(int dd, const bdaddr_t *bdaddr, uint8_t type, int to); int hci_le_clear_resolving_list(int dd, int to); int hci_le_read_resolving_list_size(int dd, uint8_t *size, int to); int hci_le_set_address_resolution_enable(int dev_id, uint8_t enable, int to); int hci_le_read_remote_features(int dd, uint16_t handle, uint8_t *features, int to); int hci_for_each_dev(int flag, int(*func)(int dd, int dev_id, long arg), long arg); int hci_get_route(bdaddr_t *bdaddr); const char *hci_bustostr(int bus); char *hci_typetostr(int type); const char *hci_dtypetostr(int type); char *hci_dflagstostr(uint32_t flags); char *hci_ptypetostr(unsigned int ptype); int hci_strtoptype(char *str, unsigned int *val); char *hci_scoptypetostr(unsigned int ptype); int hci_strtoscoptype(char *str, unsigned int *val); char *hci_lptostr(unsigned int ptype); int hci_strtolp(char *str, unsigned int *val); char *hci_lmtostr(unsigned int ptype); int hci_strtolm(char *str, unsigned int *val); char *hci_cmdtostr(unsigned int cmd); char *hci_commandstostr(uint8_t *commands, char *pref, int width); char *hci_vertostr(unsigned int ver); int hci_strtover(char *str, unsigned int *ver); char *lmp_vertostr(unsigned int ver); int lmp_strtover(char *str, unsigned int *ver); char *pal_vertostr(unsigned int ver); int pal_strtover(char *str, unsigned int *ver); char *lmp_featurestostr(uint8_t *features, char *pref, int width); static inline void hci_set_bit(int nr, void *addr) { *((uint32_t *) addr + (nr >> 5)) |= (1 << (nr & 31)); } static inline void hci_clear_bit(int nr, void *addr) { *((uint32_t *) addr + (nr >> 5)) &= ~(1 << (nr & 31)); } static inline int hci_test_bit(int nr, void *addr) { return *((uint32_t *) addr + (nr >> 5)) & (1 << (nr & 31)); } /* HCI filter tools */ static inline void hci_filter_clear(struct hci_filter *f) { memset(f, 0, sizeof(*f)); } static inline void hci_filter_set_ptype(int t, struct hci_filter *f) { hci_set_bit((t == HCI_VENDOR_PKT) ? 0 : (t & HCI_FLT_TYPE_BITS), &f->type_mask); } static inline void hci_filter_clear_ptype(int t, struct hci_filter *f) { hci_clear_bit((t == HCI_VENDOR_PKT) ? 0 : (t & HCI_FLT_TYPE_BITS), &f->type_mask); } static inline int hci_filter_test_ptype(int t, struct hci_filter *f) { return hci_test_bit((t == HCI_VENDOR_PKT) ? 0 : (t & HCI_FLT_TYPE_BITS), &f->type_mask); } static inline void hci_filter_all_ptypes(struct hci_filter *f) { memset((void *) &f->type_mask, 0xff, sizeof(f->type_mask)); } static inline void hci_filter_set_event(int e, struct hci_filter *f) { hci_set_bit((e & HCI_FLT_EVENT_BITS), &f->event_mask); } static inline void hci_filter_clear_event(int e, struct hci_filter *f) { hci_clear_bit((e & HCI_FLT_EVENT_BITS), &f->event_mask); } static inline int hci_filter_test_event(int e, struct hci_filter *f) { return hci_test_bit((e & HCI_FLT_EVENT_BITS), &f->event_mask); } static inline void hci_filter_all_events(struct hci_filter *f) { memset((void *) f->event_mask, 0xff, sizeof(f->event_mask)); } static inline void hci_filter_set_opcode(int opcode, struct hci_filter *f) { f->opcode = opcode; } static inline void hci_filter_clear_opcode(struct hci_filter *f) { f->opcode = 0; } static inline int hci_filter_test_opcode(int opcode, struct hci_filter *f) { return (f->opcode == opcode); } #ifdef __cplusplus } #endif #endif /* __HCI_LIB_H */ bluez-5.82/lib/PaxHeaders/l2cap.h0000644000000000000000000000005014447506754013606 xustar0020 atime=1743515834 20 ctime=1743591275 bluez-5.82/lib/l2cap.h0000644000000000000000000001362214447506754013273 0ustar00rootroot/* SPDX-License-Identifier: GPL-2.0-or-later */ /* * * BlueZ - Bluetooth protocol stack for Linux * * Copyright (C) 2000-2001 Qualcomm Incorporated * Copyright (C) 2002-2003 Maxim Krasnyansky * Copyright (C) 2002-2010 Marcel Holtmann * Copyright (c) 2012 Code Aurora Forum. All rights reserved. * * */ #ifndef __L2CAP_H #define __L2CAP_H #ifdef __cplusplus extern "C" { #endif #include /* L2CAP defaults */ #define L2CAP_DEFAULT_MTU 672 #define L2CAP_DEFAULT_FLUSH_TO 0xFFFF /* L2CAP socket address */ struct sockaddr_l2 { sa_family_t l2_family; unsigned short l2_psm; bdaddr_t l2_bdaddr; unsigned short l2_cid; uint8_t l2_bdaddr_type; }; /* L2CAP socket options */ #define L2CAP_OPTIONS 0x01 struct l2cap_options { uint16_t omtu; uint16_t imtu; uint16_t flush_to; uint8_t mode; uint8_t fcs; uint8_t max_tx; uint16_t txwin_size; }; #define L2CAP_CONNINFO 0x02 struct l2cap_conninfo { uint16_t hci_handle; uint8_t dev_class[3]; }; #define L2CAP_LM 0x03 #define L2CAP_LM_MASTER 0x0001 #define L2CAP_LM_AUTH 0x0002 #define L2CAP_LM_ENCRYPT 0x0004 #define L2CAP_LM_TRUSTED 0x0008 #define L2CAP_LM_RELIABLE 0x0010 #define L2CAP_LM_SECURE 0x0020 /* L2CAP command codes */ #define L2CAP_COMMAND_REJ 0x01 #define L2CAP_CONN_REQ 0x02 #define L2CAP_CONN_RSP 0x03 #define L2CAP_CONF_REQ 0x04 #define L2CAP_CONF_RSP 0x05 #define L2CAP_DISCONN_REQ 0x06 #define L2CAP_DISCONN_RSP 0x07 #define L2CAP_ECHO_REQ 0x08 #define L2CAP_ECHO_RSP 0x09 #define L2CAP_INFO_REQ 0x0a #define L2CAP_INFO_RSP 0x0b #define L2CAP_CREATE_REQ 0x0c #define L2CAP_CREATE_RSP 0x0d #define L2CAP_MOVE_REQ 0x0e #define L2CAP_MOVE_RSP 0x0f #define L2CAP_MOVE_CFM 0x10 #define L2CAP_MOVE_CFM_RSP 0x11 /* L2CAP extended feature mask */ #define L2CAP_FEAT_FLOWCTL 0x00000001 #define L2CAP_FEAT_RETRANS 0x00000002 #define L2CAP_FEAT_BIDIR_QOS 0x00000004 #define L2CAP_FEAT_ERTM 0x00000008 #define L2CAP_FEAT_STREAMING 0x00000010 #define L2CAP_FEAT_FCS 0x00000020 #define L2CAP_FEAT_EXT_FLOW 0x00000040 #define L2CAP_FEAT_FIXED_CHAN 0x00000080 #define L2CAP_FEAT_EXT_WINDOW 0x00000100 #define L2CAP_FEAT_UCD 0x00000200 /* L2CAP fixed channels */ #define L2CAP_FC_L2CAP 0x02 #define L2CAP_FC_CONNLESS 0x04 #define L2CAP_FC_A2MP 0x08 /* L2CAP structures */ typedef struct { uint16_t len; uint16_t cid; } __attribute__ ((packed)) l2cap_hdr; #define L2CAP_HDR_SIZE 4 typedef struct { uint8_t code; uint8_t ident; uint16_t len; } __attribute__ ((packed)) l2cap_cmd_hdr; #define L2CAP_CMD_HDR_SIZE 4 typedef struct { uint16_t reason; } __attribute__ ((packed)) l2cap_cmd_rej; #define L2CAP_CMD_REJ_SIZE 2 typedef struct { uint16_t psm; uint16_t scid; } __attribute__ ((packed)) l2cap_conn_req; #define L2CAP_CONN_REQ_SIZE 4 typedef struct { uint16_t dcid; uint16_t scid; uint16_t result; uint16_t status; } __attribute__ ((packed)) l2cap_conn_rsp; #define L2CAP_CONN_RSP_SIZE 8 /* connect result */ #define L2CAP_CR_SUCCESS 0x0000 #define L2CAP_CR_PEND 0x0001 #define L2CAP_CR_BAD_PSM 0x0002 #define L2CAP_CR_SEC_BLOCK 0x0003 #define L2CAP_CR_NO_MEM 0x0004 /* connect status */ #define L2CAP_CS_NO_INFO 0x0000 #define L2CAP_CS_AUTHEN_PEND 0x0001 #define L2CAP_CS_AUTHOR_PEND 0x0002 typedef struct { uint16_t dcid; uint16_t flags; uint8_t data[0]; } __attribute__ ((packed)) l2cap_conf_req; #define L2CAP_CONF_REQ_SIZE 4 typedef struct { uint16_t scid; uint16_t flags; uint16_t result; uint8_t data[0]; } __attribute__ ((packed)) l2cap_conf_rsp; #define L2CAP_CONF_RSP_SIZE 6 #define L2CAP_CONF_SUCCESS 0x0000 #define L2CAP_CONF_UNACCEPT 0x0001 #define L2CAP_CONF_REJECT 0x0002 #define L2CAP_CONF_UNKNOWN 0x0003 #define L2CAP_CONF_PENDING 0x0004 #define L2CAP_CONF_EFS_REJECT 0x0005 typedef struct { uint8_t type; uint8_t len; uint8_t val[0]; } __attribute__ ((packed)) l2cap_conf_opt; #define L2CAP_CONF_OPT_SIZE 2 #define L2CAP_CONF_MTU 0x01 #define L2CAP_CONF_FLUSH_TO 0x02 #define L2CAP_CONF_QOS 0x03 #define L2CAP_CONF_RFC 0x04 #define L2CAP_CONF_FCS 0x05 #define L2CAP_CONF_EFS 0x06 #define L2CAP_CONF_EWS 0x07 #define L2CAP_CONF_MAX_SIZE 22 #define L2CAP_MODE_BASIC 0x00 #define L2CAP_MODE_RETRANS 0x01 #define L2CAP_MODE_FLOWCTL 0x02 #define L2CAP_MODE_ERTM 0x03 #define L2CAP_MODE_STREAMING 0x04 #define L2CAP_MODE_LE_FLOWCTL 0x80 #define L2CAP_MODE_ECRED 0x81 #define L2CAP_SERVTYPE_NOTRAFFIC 0x00 #define L2CAP_SERVTYPE_BESTEFFORT 0x01 #define L2CAP_SERVTYPE_GUARANTEED 0x02 typedef struct { uint16_t dcid; uint16_t scid; } __attribute__ ((packed)) l2cap_disconn_req; #define L2CAP_DISCONN_REQ_SIZE 4 typedef struct { uint16_t dcid; uint16_t scid; } __attribute__ ((packed)) l2cap_disconn_rsp; #define L2CAP_DISCONN_RSP_SIZE 4 typedef struct { uint16_t type; } __attribute__ ((packed)) l2cap_info_req; #define L2CAP_INFO_REQ_SIZE 2 typedef struct { uint16_t type; uint16_t result; uint8_t data[0]; } __attribute__ ((packed)) l2cap_info_rsp; #define L2CAP_INFO_RSP_SIZE 4 /* info type */ #define L2CAP_IT_CL_MTU 0x0001 #define L2CAP_IT_FEAT_MASK 0x0002 /* info result */ #define L2CAP_IR_SUCCESS 0x0000 #define L2CAP_IR_NOTSUPP 0x0001 typedef struct { uint16_t psm; uint16_t scid; uint8_t id; } __attribute__ ((packed)) l2cap_create_req; #define L2CAP_CREATE_REQ_SIZE 5 typedef struct { uint16_t dcid; uint16_t scid; uint16_t result; uint16_t status; } __attribute__ ((packed)) l2cap_create_rsp; #define L2CAP_CREATE_RSP_SIZE 8 typedef struct { uint16_t icid; uint8_t id; } __attribute__ ((packed)) l2cap_move_req; #define L2CAP_MOVE_REQ_SIZE 3 typedef struct { uint16_t icid; uint16_t result; } __attribute__ ((packed)) l2cap_move_rsp; #define L2CAP_MOVE_RSP_SIZE 4 typedef struct { uint16_t icid; uint16_t result; } __attribute__ ((packed)) l2cap_move_cfm; #define L2CAP_MOVE_CFM_SIZE 4 typedef struct { uint16_t icid; } __attribute__ ((packed)) l2cap_move_cfm_rsp; #define L2CAP_MOVE_CFM_RSP_SIZE 2 #ifdef __cplusplus } #endif #endif /* __L2CAP_H */ bluez-5.82/lib/PaxHeaders/rfcomm.h0000644000000000000000000000005014015011623014041 xustar0020 atime=1743516180 20 ctime=1743591275 bluez-5.82/lib/rfcomm.h0000644000000000000000000000313514015011623013524 0ustar00rootroot/* SPDX-License-Identifier: GPL-2.0-or-later */ /* * * BlueZ - Bluetooth protocol stack for Linux * * Copyright (C) 2002-2003 Maxim Krasnyansky * Copyright (C) 2002-2010 Marcel Holtmann * * */ #ifndef __RFCOMM_H #define __RFCOMM_H #ifdef __cplusplus extern "C" { #endif #include /* RFCOMM defaults */ #define RFCOMM_DEFAULT_MTU 127 #define RFCOMM_PSM 3 /* RFCOMM socket address */ struct sockaddr_rc { sa_family_t rc_family; bdaddr_t rc_bdaddr; uint8_t rc_channel; }; /* RFCOMM socket options */ #define RFCOMM_CONNINFO 0x02 struct rfcomm_conninfo { uint16_t hci_handle; uint8_t dev_class[3]; }; #define RFCOMM_LM 0x03 #define RFCOMM_LM_MASTER 0x0001 #define RFCOMM_LM_AUTH 0x0002 #define RFCOMM_LM_ENCRYPT 0x0004 #define RFCOMM_LM_TRUSTED 0x0008 #define RFCOMM_LM_RELIABLE 0x0010 #define RFCOMM_LM_SECURE 0x0020 /* RFCOMM TTY support */ #define RFCOMM_MAX_DEV 256 #define RFCOMMCREATEDEV _IOW('R', 200, int) #define RFCOMMRELEASEDEV _IOW('R', 201, int) #define RFCOMMGETDEVLIST _IOR('R', 210, int) #define RFCOMMGETDEVINFO _IOR('R', 211, int) struct rfcomm_dev_req { int16_t dev_id; uint32_t flags; bdaddr_t src; bdaddr_t dst; uint8_t channel; }; #define RFCOMM_REUSE_DLC 0 #define RFCOMM_RELEASE_ONHUP 1 #define RFCOMM_HANGUP_NOW 2 #define RFCOMM_TTY_ATTACHED 3 struct rfcomm_dev_info { int16_t id; uint32_t flags; uint16_t state; bdaddr_t src; bdaddr_t dst; uint8_t channel; }; struct rfcomm_dev_list_req { uint16_t dev_num; struct rfcomm_dev_info dev_info[0]; }; #ifdef __cplusplus } #endif #endif /* __RFCOMM_H */ bluez-5.82/lib/PaxHeaders/hidp.h0000644000000000000000000000005014015011623013502 xustar0020 atime=1743516557 20 ctime=1743591275 bluez-5.82/lib/hidp.h0000644000000000000000000000257714015011623013176 0ustar00rootroot/* SPDX-License-Identifier: GPL-2.0-or-later */ /* * * BlueZ - Bluetooth protocol stack for Linux * * Copyright (C) 2003-2010 Marcel Holtmann * * */ #ifndef __HIDP_H #define __HIDP_H #ifdef __cplusplus extern "C" { #endif /* HIDP defaults */ #define HIDP_MINIMUM_MTU 48 #define HIDP_DEFAULT_MTU 48 /* HIDP ioctl defines */ #define HIDPCONNADD _IOW('H', 200, int) #define HIDPCONNDEL _IOW('H', 201, int) #define HIDPGETCONNLIST _IOR('H', 210, int) #define HIDPGETCONNINFO _IOR('H', 211, int) #define HIDP_VIRTUAL_CABLE_UNPLUG 0 #define HIDP_BOOT_PROTOCOL_MODE 1 #define HIDP_BLUETOOTH_VENDOR_ID 9 struct hidp_connadd_req { int ctrl_sock; /* Connected control socket */ int intr_sock; /* Connected interrupt socket */ uint16_t parser; /* Parser version */ uint16_t rd_size; /* Report descriptor size */ uint8_t *rd_data; /* Report descriptor data */ uint8_t country; uint8_t subclass; uint16_t vendor; uint16_t product; uint16_t version; uint32_t flags; uint32_t idle_to; char name[128]; /* Device name */ }; struct hidp_conndel_req { bdaddr_t bdaddr; uint32_t flags; }; struct hidp_conninfo { bdaddr_t bdaddr; uint32_t flags; uint16_t state; uint16_t vendor; uint16_t product; uint16_t version; char name[128]; }; struct hidp_connlist_req { uint32_t cnum; struct hidp_conninfo *ci; }; #ifdef __cplusplus } #endif #endif /* __HIDP_H */ bluez-5.82/lib/PaxHeaders/sco.h0000644000000000000000000000005014015011623013342 xustar0020 atime=1743516217 20 ctime=1743591275 bluez-5.82/lib/sco.h0000644000000000000000000000153514015011623013027 0ustar00rootroot/* SPDX-License-Identifier: GPL-2.0-or-later */ /* * * BlueZ - Bluetooth protocol stack for Linux * * Copyright (C) 2002-2003 Maxim Krasnyansky * Copyright (C) 2002-2010 Marcel Holtmann * * */ #ifndef __SCO_H #define __SCO_H #ifdef __cplusplus extern "C" { #endif /* SCO defaults */ #define SCO_DEFAULT_MTU 500 #define SCO_DEFAULT_FLUSH_TO 0xFFFF #define SCO_CONN_TIMEOUT (HZ * 40) #define SCO_DISCONN_TIMEOUT (HZ * 2) #define SCO_CONN_IDLE_TIMEOUT (HZ * 60) /* SCO socket address */ struct sockaddr_sco { sa_family_t sco_family; bdaddr_t sco_bdaddr; }; /* set/get sockopt defines */ #define SCO_OPTIONS 0x01 struct sco_options { uint16_t mtu; }; #define SCO_CONNINFO 0x02 struct sco_conninfo { uint16_t hci_handle; uint8_t dev_class[3]; }; #ifdef __cplusplus } #endif #endif /* __SCO_H */ bluez-5.82/PaxHeaders/tools0000644000000000000000000000005014773213573012745 xustar0020 atime=1743591291 20 ctime=1743591291 bluez-5.82/tools/0000755000000000000000000000000014773213573012503 5ustar00rootrootbluez-5.82/tools/PaxHeaders/hciattach.10000644000000000000000000000005014773213541015027 xustar0020 atime=1743591265 20 ctime=1743591291 bluez-5.82/tools/hciattach.10000644000000000000000000001120614773213541014510 0ustar00rootroot'\" t .\" Man page generated from reStructuredText. . . .nr rst2man-indent-level 0 . .de1 rstReportMargin \\$1 \\n[an-margin] level \\n[rst2man-indent-level] level margin: \\n[rst2man-indent\\n[rst2man-indent-level]] - \\n[rst2man-indent0] \\n[rst2man-indent1] \\n[rst2man-indent2] .. .de1 INDENT .\" .rstReportMargin pre: . RS \\$1 . nr rst2man-indent\\n[rst2man-indent-level] \\n[an-margin] . nr rst2man-indent-level +1 .\" .rstReportMargin post: .. .de UNINDENT . RE .\" indent \\n[an-margin] .\" old: \\n[rst2man-indent\\n[rst2man-indent-level]] .nr rst2man-indent-level -1 .\" new: \\n[rst2man-indent\\n[rst2man-indent-level]] .in \\n[rst2man-indent\\n[rst2man-indent-level]]u .. .TH "HCIATTACH" "1" "Jan 22, 2002" "BlueZ" "Linux System Administration" .SH NAME hciattach \- attach serial devices via UART HCI to BlueZ stack .SH SYNOPSIS .sp \fBhciattach\fP [\fIOPTIONS\fP] <\fItty\fP> <\fItype|id\fP> [\fIspeed\fP] [\fIflow\fP] [\fIsleep\fP] [\fIbdaddr\fP] .sp \fBhciattach\fP \-1 .SH DESCRIPTION .sp \fBhciattach(1)\fP is used to attach a serial UART to the Bluetooth stack as HCI transport interface. .SH OPTIONS .INDENT 0.0 .TP .B \-i Send break .TP .B \-n Don\(aqt detach from controlling terminal. .TP .B \-p Print the PID when detaching. .TP .BI \-t \ timeout Specify an initialization timeout. Default is 5 seconds. .TP .BI \-s \ speed Specify an initial speed instead of the hardware default. .TP .B \-l List all available configurations. .TP .B \-r Set the HCI device into raw mode. The kernel and bluetooth daemon will ignore it. .TP .B \-h\fP,\fB \-\-help Show help options .UNINDENT .SH ARGUMENTS .INDENT 0.0 .TP .B \fIshow\fP This specifies the serial device to attach. A leading /dev can be omitted. .sp Examples: \fB/dev/ttyS1 ttyS2\fP .TP .B \fItype|id\fP The \fItype\fP or \fIid\fP of the Bluetooth device that is to be attached, i.e. vendor or other device specific identifier. Currently supported types are .UNINDENT .TS box center; l|l. T{ \fItype\fP T} T{ Description T} _ T{ any T} T{ Unspecified HCI_UART interface, no vendor specific options T} _ T{ ericsson T} T{ Ericsson based modules T} _ T{ digi T} T{ Digianswer based cards T} _ T{ xircom T} T{ Xircom PCMCIA cards: Credit Card Adapter and Real Port Adapter T} _ T{ csr T} T{ CSR Casira serial adapter or BrainBoxes serial dongle (BL642) T} _ T{ bboxes T} T{ BrainBoxes PCMCIA card (BL620) T} _ T{ swave T} T{ Silicon Wave kits T} _ T{ bcsp T} T{ Serial adapters using CSR chips with BCSP serial protocol T} _ T{ ath3k T} T{ Atheros AR300x based serial Bluetooth device T} _ T{ intel T} T{ Intel Bluetooth device T} .TE .TS box center; l|l. T{ .nf Supported ID (manufacturer id, product id) .fi T} T{ Description T} _ T{ 0x0105, 0x080a T} T{ Xircom PCMCIA cards: Credit Card Adapter and Real Port Adapter T} _ T{ 0x0160, 0x0002 T} T{ BrainBoxes PCMCIA card (BL620) T} .TE .INDENT 0.0 .TP .B \fIspeed\fP The \fIspeed\fP specifies the UART speed to use. Baudrates higher than 115200bps require vendor specific initializations that are not implemented for all types of devices. In general the following speeds are supported: .sp Supported vendor devices are automatically initialised to their respective best settings. .UNINDENT .TS box center; l. T{ 9600 T} _ T{ 19200 T} _ T{ 38400 T} _ T{ 57600 T} _ T{ 115200 T} _ T{ 230400 T} _ T{ 460800 T} _ T{ 921600 T} .TE .INDENT 0.0 .TP .B \fIflow\fP If the \fIflow\fP is appended to the list of options then hardware flow control is forced on the serial link (\fBCRTSCTS\fP). All above mentioned device types have flow set by default. To force no flow control use \fInoflow\fP instead. .TP .B \fIsleep|nosleep\fP Enables hardware specific power management feature. If \fIsleep\fP is appended to the list of options then this feature is enabled. To disable this feature use \fInosleep\fP instead. All above mentioned device types have \fInosleep\fP set by default. .sp Note: This option will only be valid for hardware which support hardware specific power management enable option from host. .TP .B \fIbdaddr\fP The bdaddr specifies the Bluetooth Address to use. Some devices (like the STLC2500) do not store the Bluetooth address in hardware memory. Instead it must be uploaded during the initialization process. If this argument is specified, then the address will be used to initialize the device. Otherwise, a default address will be used. .UNINDENT .SH RESOURCES .sp .SH REPORTING BUGS .sp .SH AUTHOR Maxim Krasnyansky , Nils Faerber .SH COPYRIGHT Free use of this software is granted under the terms of the GNU Lesser General Public Licenses (LGPL). .\" Generated by docutils manpage writer. . bluez-5.82/tools/PaxHeaders/tester.h0000644000000000000000000000005014772767672014516 xustar0020 atime=1743515579 20 ctime=1743591286 bluez-5.82/tools/tester.h0000644000000000000000000001116014772767672014176 0ustar00rootroot// SPDX-License-Identifier: LGPL-2.1-or-later /* * * BlueZ - Bluetooth protocol stack for Linux * * Copyright (C) 2022 Intel Corporation. * */ #include #include #include #include #include #include #include #include #define SEC_NSEC(_t) ((_t) * 1000000000LL) #define TS_NSEC(_ts) (SEC_NSEC((_ts)->tv_sec) + (_ts)->tv_nsec) #if !HAVE_DECL_SOF_TIMESTAMPING_TX_COMPLETION #define SOF_TIMESTAMPING_TX_COMPLETION (1 << 18) #endif #if !HAVE_DECL_SCM_TSTAMP_COMPLETION #define SCM_TSTAMP_COMPLETION (SCM_TSTAMP_ACK + 1) #endif #define TS_TX_RECORD_MASK (SOF_TIMESTAMPING_TX_RECORD_MASK | \ SOF_TIMESTAMPING_TX_COMPLETION) struct tx_tstamp_data { struct { uint32_t id; uint32_t type; } expect[16]; unsigned int pos; unsigned int count; unsigned int sent; uint32_t so_timestamping; bool stream; }; static inline void tx_tstamp_init(struct tx_tstamp_data *data, uint32_t so_timestamping, bool stream) { memset(data, 0, sizeof(*data)); memset(data->expect, 0xff, sizeof(data->expect)); data->so_timestamping = so_timestamping; data->stream = stream; } static inline int tx_tstamp_expect(struct tx_tstamp_data *data, size_t len) { unsigned int pos = data->count; int steps; if (data->stream && len) data->sent += len - 1; if (data->so_timestamping & SOF_TIMESTAMPING_TX_SCHED) { g_assert(pos < ARRAY_SIZE(data->expect)); data->expect[pos].type = SCM_TSTAMP_SCHED; data->expect[pos].id = data->sent; pos++; } if (data->so_timestamping & SOF_TIMESTAMPING_TX_SOFTWARE) { g_assert(pos < ARRAY_SIZE(data->expect)); data->expect[pos].type = SCM_TSTAMP_SND; data->expect[pos].id = data->sent; pos++; } if (data->so_timestamping & SOF_TIMESTAMPING_TX_COMPLETION) { g_assert(pos < ARRAY_SIZE(data->expect)); data->expect[pos].type = SCM_TSTAMP_COMPLETION; data->expect[pos].id = data->sent; pos++; } if (!data->stream || len) data->sent++; steps = pos - data->count; data->count = pos; return steps; } static inline int tx_tstamp_recv(struct tx_tstamp_data *data, int sk, int len) { unsigned char control[512]; ssize_t ret; char buf[1024]; struct msghdr msg; struct iovec iov; struct cmsghdr *cmsg; struct scm_timestamping *tss = NULL; struct sock_extended_err *serr = NULL; struct timespec now; unsigned int i; iov.iov_base = buf; iov.iov_len = sizeof(buf); memset(&msg, 0, sizeof(msg)); msg.msg_iov = &iov; msg.msg_iovlen = 1; msg.msg_control = control; msg.msg_controllen = sizeof(control); ret = recvmsg(sk, &msg, MSG_ERRQUEUE); if (ret < 0) { if (errno == EAGAIN || errno == EWOULDBLOCK) return data->count - data->pos; tester_warn("Failed to read from errqueue: %s (%d)", strerror(errno), errno); return -EINVAL; } if (data->so_timestamping & SOF_TIMESTAMPING_OPT_TSONLY) { if (ret != 0) { tester_warn("Packet copied back to errqueue"); return -EINVAL; } } else if (len > ret) { tester_warn("Packet not copied back to errqueue: %zd", ret); return -EINVAL; } for (cmsg = CMSG_FIRSTHDR(&msg); cmsg != NULL; cmsg = CMSG_NXTHDR(&msg, cmsg)) { if (cmsg->cmsg_level == SOL_SOCKET && cmsg->cmsg_type == SCM_TIMESTAMPING) { tss = (void *)CMSG_DATA(cmsg); } else if (cmsg->cmsg_level == SOL_BLUETOOTH && cmsg->cmsg_type == BT_SCM_ERROR) { serr = (void *)CMSG_DATA(cmsg); } } if (!tss) { tester_warn("SCM_TIMESTAMPING not found"); return -EINVAL; } if (!serr) { tester_warn("BT_SCM_ERROR not found"); return -EINVAL; } if (serr->ee_errno != ENOMSG || serr->ee_origin != SO_EE_ORIGIN_TIMESTAMPING) { tester_warn("BT_SCM_ERROR wrong for timestamping"); return -EINVAL; } clock_gettime(CLOCK_REALTIME, &now); if (TS_NSEC(&now) < TS_NSEC(tss->ts) || TS_NSEC(&now) > TS_NSEC(tss->ts) + SEC_NSEC(10)) { tester_warn("nonsense in timestamp"); return -EINVAL; } if (data->pos >= data->count) { tester_warn("Too many timestamps"); return -EINVAL; } /* Find first unreceived timestamp of the right type */ for (i = 0; i < data->count; ++i) { if (data->expect[i].type >= 0xffff) continue; if (serr->ee_info == data->expect[i].type) { data->expect[i].type = 0xffff; break; } } if (i == data->count) { tester_warn("Bad timestamp type %u", serr->ee_info); return -EINVAL; } if ((data->so_timestamping & SOF_TIMESTAMPING_OPT_ID) && serr->ee_data != data->expect[i].id) { tester_warn("Bad timestamp id %u", serr->ee_data); return -EINVAL; } tester_print("Got valid TX timestamp %u (type %u, id %u)", i, serr->ee_info, serr->ee_data); ++data->pos; return data->count - data->pos; } bluez-5.82/tools/PaxHeaders/userchan-tester.c0000644000000000000000000000005014131623653016272 xustar0020 atime=1743516874 20 ctime=1743591287 bluez-5.82/tools/userchan-tester.c0000644000000000000000000002152214131623653015755 0ustar00rootroot// SPDX-License-Identifier: GPL-2.0-or-later /* * * BlueZ - Bluetooth protocol stack for Linux * * Copyright (C) 2014-2015 Intel Corporation. All rights reserved. * * */ #ifdef HAVE_CONFIG_H #include #endif #include #include #include #include #include #include #include "lib/bluetooth.h" #include "lib/hci.h" #include "lib/mgmt.h" #include "monitor/bt.h" #include "emulator/bthost.h" #include "emulator/hciemu.h" #include "src/shared/tester.h" #include "src/shared/mgmt.h" #include "src/shared/hci.h" #include "src/shared/util.h" struct test_data { struct mgmt *mgmt; uint16_t mgmt_index; struct hciemu *hciemu; enum hciemu_type hciemu_type; const void *test_data; unsigned int remove_id; struct bt_hci *hci; uint32_t current_settings; }; static void mgmt_debug(const char *str, void *user_data) { const char *prefix = user_data; tester_print("%s%s", prefix, str); } static void read_info_callback(uint8_t status, uint16_t length, const void *param, void *user_data) { struct test_data *data = tester_get_data(); const struct mgmt_rp_read_info *rp = param; char addr[18]; uint16_t manufacturer; uint32_t supported_settings, current_settings; tester_print("Read Info callback"); tester_print(" Status: 0x%02x", status); if (status || !param) { tester_pre_setup_failed(); return; } ba2str(&rp->bdaddr, addr); manufacturer = btohs(rp->manufacturer); supported_settings = btohl(rp->supported_settings); current_settings = btohl(rp->current_settings); tester_print(" Address: %s", addr); tester_print(" Version: 0x%02x", rp->version); tester_print(" Manufacturer: 0x%04x", manufacturer); tester_print(" Supported settings: 0x%08x", supported_settings); tester_print(" Current settings: 0x%08x", current_settings); tester_print(" Class: 0x%02x%02x%02x", rp->dev_class[2], rp->dev_class[1], rp->dev_class[0]); tester_print(" Name: %s", rp->name); tester_print(" Short name: %s", rp->short_name); data->current_settings = current_settings; if (strcmp(hciemu_get_address(data->hciemu), addr)) { tester_pre_setup_failed(); return; } tester_pre_setup_complete(); } static void index_added_callback(uint16_t index, uint16_t length, const void *param, void *user_data) { struct test_data *data = tester_get_data(); tester_print("Index Added callback"); tester_print(" Index: 0x%04x", index); if (data->mgmt_index != MGMT_INDEX_NONE) return; data->mgmt_index = index; mgmt_send(data->mgmt, MGMT_OP_READ_INFO, data->mgmt_index, 0, NULL, read_info_callback, NULL, NULL); } static void index_removed_callback(uint16_t index, uint16_t length, const void *param, void *user_data) { struct test_data *data = tester_get_data(); tester_print("Index Removed callback"); tester_print(" Index: 0x%04x", index); if (index != data->mgmt_index) return; if (data->remove_id) { mgmt_unregister(data->mgmt, data->remove_id); data->remove_id = 0; tester_test_passed(); return; } mgmt_unregister_index(data->mgmt, data->mgmt_index); mgmt_unref(data->mgmt); data->mgmt = NULL; tester_post_teardown_complete(); } static void read_index_list_callback(uint8_t status, uint16_t length, const void *param, void *user_data) { struct test_data *data = tester_get_data(); tester_print("Read Index List callback"); tester_print(" Status: 0x%02x", status); if (status || !param) { tester_pre_setup_failed(); return; } mgmt_register(data->mgmt, MGMT_EV_INDEX_ADDED, MGMT_INDEX_NONE, index_added_callback, NULL, NULL); data->hciemu = hciemu_new(data->hciemu_type); if (!data->hciemu) { tester_warn("Failed to setup HCI emulation"); tester_pre_setup_failed(); } tester_print("New hciemu instance created"); } static void test_pre_setup(const void *test_data) { struct test_data *data = tester_get_data(); data->mgmt = mgmt_new_default(); if (!data->mgmt) { tester_warn("Failed to setup management interface"); tester_pre_setup_failed(); return; } if (tester_use_debug()) mgmt_set_debug(data->mgmt, mgmt_debug, "mgmt: ", NULL); mgmt_send(data->mgmt, MGMT_OP_READ_INDEX_LIST, MGMT_INDEX_NONE, 0, NULL, read_index_list_callback, NULL, NULL); } static void test_post_teardown(const void *test_data) { struct test_data *data = tester_get_data(); mgmt_register(data->mgmt, MGMT_EV_INDEX_REMOVED, data->mgmt_index, index_removed_callback, NULL, NULL); hciemu_unref(data->hciemu); data->hciemu = NULL; } static void test_data_free(void *test_data) { struct test_data *data = test_data; free(data); } static void setup_powered_client_callback(uint8_t status, uint16_t length, const void *param, void *user_data) { if (status != MGMT_STATUS_SUCCESS) { tester_setup_failed(); return; } tester_print("Controller powered on"); tester_setup_complete(); } static void setup_powered(const void *test_data) { struct test_data *data = tester_get_data(); unsigned char param[] = { 0x01 }; tester_print("Powering on controller"); mgmt_send(data->mgmt, MGMT_OP_SET_POWERED, data->mgmt_index, sizeof(param), param, setup_powered_client_callback, NULL, NULL); } static void toggle_powered(const void *test_data); static void toggle_powered_client_callback(uint8_t status, uint16_t length, const void *param, void *user_data) { bool power = PTR_TO_INT(user_data); if (status != MGMT_STATUS_SUCCESS) { tester_setup_failed(); return; } tester_print("Controller powered %s", power ? "on" : "off"); if (power) toggle_powered(false); else tester_setup_complete(); } static void toggle_powered(const void *test_data) { struct test_data *data = tester_get_data(); bool power = PTR_TO_INT(test_data); unsigned char param[1]; param[0] = power ? 0x01 : 0x00; tester_print("Powering %s controller", power ? "on" : "off"); mgmt_send(data->mgmt, MGMT_OP_SET_POWERED, data->mgmt_index, sizeof(param), param, toggle_powered_client_callback, INT_TO_PTR(power), NULL); } static void test_open_success(const void *test_data) { struct test_data *data = tester_get_data(); struct bt_hci *hci; data->remove_id = mgmt_register(data->mgmt, MGMT_EV_INDEX_REMOVED, data->mgmt_index, index_removed_callback, NULL, NULL); hci = bt_hci_new_user_channel(data->mgmt_index); if (hci) { bt_hci_unref(hci); return; } mgmt_unregister(data->mgmt, data->remove_id); data->remove_id = 0; tester_test_failed(); } static void test_open_failed(const void *test_data) { struct test_data *data = tester_get_data(); struct bt_hci *hci; hci = bt_hci_new_user_channel(data->mgmt_index); if (!hci) { tester_test_passed(); return; } bt_hci_unref(hci); tester_test_failed(); } static void close_read_info_callback(uint8_t status, uint16_t length, const void *param, void *user_data) { const struct mgmt_rp_read_info *rp = param; uint32_t current_settings; tester_print("Read Info callback"); tester_print(" Status: 0x%02x", status); if (status || !param) { tester_test_failed(); return; } current_settings = btohl(rp->current_settings); if (current_settings & MGMT_SETTING_POWERED) { tester_print("Controller is powered"); tester_test_failed(); return; } tester_test_passed(); } static void setup_channel_open(const void *test_data) { struct test_data *data = tester_get_data(); /* Check power off */ if (data->current_settings & MGMT_SETTING_POWERED) { tester_print("Controller is powered"); tester_setup_failed(); return; } /* Open Channel */ data->hci = bt_hci_new_user_channel(data->mgmt_index); if (!data->hci) { mgmt_unregister(data->mgmt, data->remove_id); data->remove_id = 0; tester_setup_failed(); return; } tester_print("User Channel Opened"); tester_setup_complete(); } static void test_close_success(const void *test_data) { struct test_data *data = tester_get_data(); tester_print("Close User Channel"); bt_hci_unref(data->hci); /* Check if power is off */ mgmt_send(data->mgmt, MGMT_OP_READ_INFO, data->mgmt_index, 0, NULL, close_read_info_callback, NULL, NULL); } #define test_user(name, data, setup, func) \ do { \ struct test_data *user; \ user = malloc(sizeof(struct test_data)); \ if (!user) \ break; \ user->hciemu_type = HCIEMU_TYPE_BREDR; \ user->mgmt_index = MGMT_INDEX_NONE; \ user->test_data = data; \ user->remove_id = 0; \ tester_add_full(name, data, \ test_pre_setup, setup, func, NULL, \ test_post_teardown, 2, user, test_data_free); \ } while (0) int main(int argc, char *argv[]) { tester_init(&argc, &argv); test_user("User channel open - Success", NULL, NULL, test_open_success); test_user("User channel open - Failed", NULL, setup_powered, test_open_failed); test_user("User channel open - Power Toggle Success", INT_TO_PTR(true), toggle_powered, test_open_success); test_user("User channel close - Success", NULL, setup_channel_open, test_close_success); return tester_run(); } bluez-5.82/tools/PaxHeaders/sdptool.10000644000000000000000000000005014773213547014571 xustar0020 atime=1743591271 20 ctime=1743591291 bluez-5.82/tools/sdptool.10000644000000000000000000000703114773213547014253 0ustar00rootroot.\" Man page generated from reStructuredText. . . .nr rst2man-indent-level 0 . .de1 rstReportMargin \\$1 \\n[an-margin] level \\n[rst2man-indent-level] level margin: \\n[rst2man-indent\\n[rst2man-indent-level]] - \\n[rst2man-indent0] \\n[rst2man-indent1] \\n[rst2man-indent2] .. .de1 INDENT .\" .rstReportMargin pre: . RS \\$1 . nr rst2man-indent\\n[rst2man-indent-level] \\n[an-margin] . nr rst2man-indent-level +1 .\" .rstReportMargin post: .. .de UNINDENT . RE .\" indent \\n[an-margin] .\" old: \\n[rst2man-indent\\n[rst2man-indent-level]] .nr rst2man-indent-level -1 .\" new: \\n[rst2man-indent\\n[rst2man-indent-level]] .in \\n[rst2man-indent\\n[rst2man-indent-level]]u .. .TH "SDPTOOL" "1" "" "BlueZ" "Linux System Administration" .SH NAME sdptool \- control and interrogate SDP servers .SH SYNOPSIS .sp \fBsdptool\fP [\fIOPTIONS\fP] [\fICOMMAND\fP [\fIPARAMETERS\fP]] .SH DESCRIPTION .sp \fBsdptool(1)\fP provides the interface for performing SDP queries on Bluetooth devices, and administering a local SDP database. .SH COMMANDS .sp The following commands are available. In all cases \fBbdaddr\fP specifies the device to search or browse. If \fIlocal\fP is used for \fBbdaddr\fP, then the local SDP database is searched. .sp Services are identified and manipulated with a 4\-byte \fBrecord_handle\fP (NOT the service name). To find a service\(aqs \fBrecord_handle\fP, look for the \(dqService RecHandle\(dq line in the \fBsearch\fP or \fBbrowse\fP results .INDENT 0.0 .TP .B search [\-\-bdaddr bdaddr] [\-\-tree] [\-\-raw] [\-\-xml] service_name Search for services.. .sp Known service names are \fBDID\fP, \fBSP\fP, \fBDUN\fP, \fBLAN\fP, \fBFAX\fP, \fBOPUSH\fP, \fBFTP\fP, \fBHS\fP, \fBHF\fP, \fBHFAG\fP, \fBSAP\fP, \fBNAP\fP, \fBGN\fP, \fBPANU\fP, \fBHCRP\fP, \fBHID\fP, \fBCIP\fP, \fBA2SRC\fP, \fBA2SNK\fP, \fBAVRCT\fP, \fBAVRTG\fP, \fBUDIUE\fP, \fBUDITE\fP and \fBSYNCML\fP\&. .TP .B browse [\-\-tree] [\-\-raw] [\-\-xml] [bdaddr] Browse all available services on the device specified by a Bluetooth address as a parameter. .TP .B records [\-\-tree] [\-\-raw] [\-\-xml] bdaddr Retrieve all possible service records. .TP .B add [ \-\-handle=N \-\-channel=N ] Add a service to the local SDP database. .sp You can specify a handle for this record using the \fB\-\-handle\fP option. .sp You can specify a channel to add the service on using the \fB\-\-channel\fP option. .sp NOTE: Local adapters configuration will not be updated and this command should be used only for SDP testing. .TP .B del record_handle Remove a service from the local SDP database. .sp NOTE: Local adapters configuration will not be updated and this command should be used only for SDP testing. .TP .B get [\-\-tree] [\-\-raw] [\-\-xml] [\-\-bdaddr bdaddr] record_handle Retrieve a service from the local SDP database. .TP .B setattr record_handle attrib_id attrib_value Set or add an attribute to an SDP record. .TP .B setseq record_handle attrib_id attrib_values Set or add an attribute sequence to an SDP record. .UNINDENT .SH OPTIONS .INDENT 0.0 .TP .B \-\-help Displays help on using sdptool. .UNINDENT .SH EXAMPLES .INDENT 0.0 .INDENT 3.5 .sp .EX $ sdptool browse 00:80:98:24:15:6D $ sdptool browse local $ sdptool add DUN $ sdptool del 0x10000 .EE .UNINDENT .UNINDENT .SH RESOURCES .sp .SH REPORTING BUGS .sp .SH AUTHOR Maxim Krasnyansky , Edd Dumbill .SH COPYRIGHT Free use of this software is granted under the terms of the GNU Lesser General Public Licenses (LGPL). .\" Generated by docutils manpage writer. . bluez-5.82/tools/PaxHeaders/hciattach_tialt.c0000644000000000000000000000005014333256743016311 xustar0020 atime=1743516871 20 ctime=1743591285 bluez-5.82/tools/hciattach_tialt.c0000644000000000000000000001323114333256743015772 0ustar00rootroot// SPDX-License-Identifier: GPL-2.0-or-later /* * * BlueZ - Bluetooth protocol stack for Linux * * Copyright (C) 2005-2010 Marcel Holtmann * * */ #ifdef HAVE_CONFIG_H #include #endif #define _GNU_SOURCE #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "lib/bluetooth.h" #include "lib/hci.h" #include "lib/hci_lib.h" #include "hciattach.h" #define FAILIF(x, args...) do { \ if (x) { \ fprintf(stderr, ##args); \ return -1; \ } \ } while(0) typedef struct { uint8_t uart_prefix; hci_event_hdr hci_hdr; evt_cmd_complete cmd_complete; uint8_t status; uint8_t data[16]; } __attribute__((packed)) command_complete_t; static int read_command_complete(int fd, unsigned short opcode, unsigned char len) { command_complete_t resp; /* Read reply. */ FAILIF(read_hci_event(fd, (unsigned char *)&resp, sizeof(resp)) < 0, "Failed to read response"); /* Parse speed-change reply */ FAILIF(resp.uart_prefix != HCI_EVENT_PKT, "Error in response: not an event packet, but 0x%02x!\n", resp.uart_prefix); FAILIF(resp.hci_hdr.evt != EVT_CMD_COMPLETE, /* event must be event-complete */ "Error in response: not a cmd-complete event, " "but 0x%02x!\n", resp.hci_hdr.evt); FAILIF(resp.hci_hdr.plen < 4, /* plen >= 4 for EVT_CMD_COMPLETE */ "Error in response: plen is not >= 4, but 0x%02x!\n", resp.hci_hdr.plen); /* cmd-complete event: opcode */ FAILIF(resp.cmd_complete.opcode != (uint16_t)opcode, "Error in response: opcode is 0x%04x, not 0x%04x!", resp.cmd_complete.opcode, opcode); return resp.status == 0 ? 0 : -1; } typedef struct { uint8_t uart_prefix; hci_command_hdr hci_hdr; uint32_t speed; } __attribute__((packed)) texas_speed_change_cmd_t; static int texas_change_speed(int fd, uint32_t speed) { return 0; } static int texas_load_firmware(int fd, const char *firmware) { int fw = open(firmware, O_RDONLY); fprintf(stdout, "Opening firmware file: %s\n", firmware); FAILIF(fw < 0, "Could not open firmware file %s: %s (%d).\n", firmware, strerror(errno), errno); fprintf(stdout, "Uploading firmware...\n"); do { /* Read each command and wait for a response. */ unsigned char data[1024]; unsigned char cmdp[1 + sizeof(hci_command_hdr)]; hci_command_hdr *cmd = (hci_command_hdr *)(cmdp + 1); int nr; nr = read(fw, cmdp, sizeof(cmdp)); if (!nr) break; FAILIF(nr != sizeof(cmdp), "Could not read H4 + HCI header!\n"); FAILIF(*cmdp != HCI_COMMAND_PKT, "Command is not an H4 command packet!\n"); FAILIF(read(fw, data, cmd->plen) != cmd->plen, "Could not read %d bytes of data for command with opcode %04x!\n", cmd->plen, cmd->opcode); { int nw; #if 0 fprintf(stdout, "\topcode 0x%04x (%d bytes of data).\n", cmd->opcode, cmd->plen); #endif struct iovec iov_cmd[2]; iov_cmd[0].iov_base = cmdp; iov_cmd[0].iov_len = sizeof(cmdp); iov_cmd[1].iov_base = data; iov_cmd[1].iov_len = cmd->plen; nw = writev(fd, iov_cmd, 2); FAILIF(nw != (int) sizeof(cmd) + cmd->plen, "Could not send entire command (sent only %d bytes)!\n", nw); } /* Wait for response */ if (read_command_complete(fd, cmd->opcode, cmd->plen) < 0) { return -1; } } while(1); fprintf(stdout, "Firmware upload successful.\n"); close(fw); return 0; } int texasalt_init(int fd, int speed, struct termios *ti) { struct timespec tm = {0, 50000}; char cmd[4]; unsigned char resp[100]; /* Response */ int n; memset(resp,'\0', 100); /* It is possible to get software version with manufacturer specific HCI command HCI_VS_TI_Version_Number. But the only thing you get more is if this is point-to-point or point-to-multipoint module */ /* Get Manufacturer and LMP version */ cmd[0] = HCI_COMMAND_PKT; cmd[1] = 0x01; cmd[2] = 0x10; cmd[3] = 0x00; do { n = write(fd, cmd, 4); if (n < 0) { perror("Failed to write init command (READ_LOCAL_VERSION_INFORMATION)"); return -1; } if (n < 4) { fprintf(stderr, "Wanted to write 4 bytes, could only write %d. Stop\n", n); return -1; } /* Read reply. */ if (read_hci_event(fd, resp, 100) < 0) { perror("Failed to read init response (READ_LOCAL_VERSION_INFORMATION)"); return -1; } /* Wait for command complete event for our Opcode */ } while (resp[4] != cmd[1] && resp[5] != cmd[2]); /* Verify manufacturer */ if ((resp[11] & 0xFF) != 0x0d) fprintf(stderr,"WARNING : module's manufacturer is not Texas Instrument\n"); /* Print LMP version */ fprintf(stderr, "Texas module LMP version : 0x%02x\n", resp[10] & 0xFF); /* Print LMP subversion */ { unsigned short lmp_subv = resp[13] | (resp[14] << 8); unsigned short brf_chip = (lmp_subv & 0x7c00) >> 10; static const char *c_brf_chip[8] = { "unknown", "unknown", "brf6100", "brf6150", "brf6300", "brf6350", "unknown", "wl1271" }; char fw[100]; fprintf(stderr, "Texas module LMP sub-version : 0x%04x\n", lmp_subv); fprintf(stderr, "\tinternal version freeze: %d\n" "\tsoftware version: %d\n" "\tchip: %s (%d)\n", lmp_subv & 0x7f, ((lmp_subv & 0x8000) >> (15-3)) | ((lmp_subv & 0x380) >> 7), ((brf_chip > 7) ? "unknown" : c_brf_chip[brf_chip]), brf_chip); sprintf(fw, "%s/%s.bin", FIRMWARE_DIR, (brf_chip > 7) ? "unknown" : c_brf_chip[brf_chip]); texas_load_firmware(fd, fw); texas_change_speed(fd, speed); } nanosleep(&tm, NULL); return 0; } bluez-5.82/tools/PaxHeaders/seq2bseq.c0000644000000000000000000000005014015011624014671 xustar0020 atime=1743516257 20 ctime=1743591287 bluez-5.82/tools/seq2bseq.c0000644000000000000000000000674714015011624014370 0ustar00rootroot// SPDX-License-Identifier: GPL-2.0-or-later /* * * BlueZ - Bluetooth protocol stack for Linux * * Copyright (C) 2012-2013 Intel Corporation * * */ #ifdef HAVE_CONFIG_H #include #endif #define _GNU_SOURCE #include #include #include #include #include #include #include #include static int convert_line(int fd, const char *line) { const char *ptr = line; char str[3]; unsigned char val; if (line[0] == '*' || line[0] == '\r' || line[0] == '\n') return 0; while (1) { str[0] = *ptr++; str[1] = *ptr++; str[2] = '\0'; val = strtol(str, NULL, 16); if (write(fd, &val, 1) < 0) return -errno; if (*ptr == '\r' || *ptr == '\n') break; while (*ptr == ' ') ptr++; } return 0; } static void convert_file(const char *input_path, const char *output_path) { size_t line_size = 1024; char line_buffer[line_size]; char *path; const char *ptr; FILE *fp; struct stat st; off_t cur = 0; int fd; if (output_path) { path = strdup(output_path); if (!path) { perror("Failed to allocate string"); return; } } else { ptr = strrchr(input_path, '.'); if (ptr) { path = malloc(ptr - input_path + 6); if (!path) { perror("Failed to allocate string"); return; } strncpy(path, input_path, ptr - input_path); strcpy(path + (ptr - input_path), ".bseq"); } else { if (asprintf(&path, "%s.bseq", input_path) < 0) { perror("Failed to allocate string"); return; } } } printf("Converting %s to %s\n", input_path, path); fd = open(path, O_WRONLY | O_CREAT | O_TRUNC, 0644); free(path); if (fd < 0) { perror("Failed to create output file"); return; } if (stat(input_path, &st) < 0) { fprintf(stderr, "Failed get file size\n"); close(fd); return; } if (st.st_size == 0) { fprintf(stderr, "Empty file\n"); close(fd); return; } fp = fopen(input_path, "r"); if (!fp) { fprintf(stderr, "Failed to open input file\n"); close(fd); return; } while (1) { char *str; int err; str = fgets(line_buffer, line_size - 1, fp); if (!str) break; cur += strlen(str); err = convert_line(fd, str); if (err < 0) { fprintf(stderr, "Failed to convert file (%s)\n", strerror(-err)); break; } } fclose(fp); close(fd); } static void usage(void) { printf("Intel Bluetooth firmware converter\n" "Usage:\n"); printf("\tseq2bseq [options] \n"); printf("Options:\n" "\t-o, --output Provide firmware output file\n" "\t-h, --help Show help options\n"); } static const struct option main_options[] = { { "output", required_argument, NULL, 'o' }, { "version", no_argument, NULL, 'v' }, { "help", no_argument, NULL, 'h' }, { } }; int main(int argc, char *argv[]) { const char *output_path = NULL; int i; for (;;) { int opt; opt = getopt_long(argc, argv, "o:vh", main_options, NULL); if (opt < 0) break; switch (opt) { case 'o': output_path = optarg; break; case 'v': printf("%s\n", VERSION); return EXIT_SUCCESS; case 'h': usage(); return EXIT_SUCCESS; default: return EXIT_FAILURE; } } if (argc - optind < 1) { fprintf(stderr, "No input firmware files provided\n"); return EXIT_FAILURE; } if (output_path && argc - optind > 1) { fprintf(stderr, "Only single input firmware supported\n"); return EXIT_FAILURE; } for (i = optind; i < argc; i++) convert_file(argv[i], output_path); return EXIT_SUCCESS; } bluez-5.82/tools/PaxHeaders/hcitool.rst0000644000000000000000000000005014766002272015207 xustar0020 atime=1743515578 20 ctime=1743591291 bluez-5.82/tools/hcitool.rst0000644000000000000000000001375014766002272014676 0ustar00rootroot======= hcitool ======= ------------------------------- Configure Bluetooth connections ------------------------------- :Authors: - Maxim Krasnyansky - Marcel Holtmann - Fabrizio Gennari :Version: BlueZ :Copyright: Free use of this software is granted under the terms of the GNU Lesser General Public Licenses (LGPL). :Date: Nov 12, 2002 :Manual section: 1 :Manual group: Linux System Administration SYNOPSIS ======== **hcitool** -h **hcitool** *COMMAND* --help **hcitool** [-i *hciX*] [*COMMAND* [*PARAMETERS*]] DESCRIPTION =========== **hcitool(1)** is used to configure Bluetooth connections and send some special command to Bluetooth devices. If no **command** is given, or if the option **-h** is used, *hcitool* prints some usage information and exits. OPTIONS ======= -i The command is applied to device *hciX*, which must be the name of an installed Bluetooth device. If not specified, the command will be sent to the first available Bluetooth device. -h Gives a list of possible commands COMMANDS ======== dev Display local devices inq Inquire remote devices. For each discovered device, Bluetooth device address, clock offset and class are printed. scan Inquire remote devices. For each discovered device, device name are printed. name <*bdaddr*> Print device name of remote device with Bluetooth address *bdaddr*. info <*bdaddr*> Print device name, version and supported features of remote device with Bluetooth address *bdaddr*. spinq Start periodic inquiry process. No inquiry results are printed. epinq Exit periodic inquiry process. cmd <*ogf*> <*ocf*> [*parameters*] Submit an arbitrary HCI command to local device. *ogf*, *ocf* and parameters are hexadecimal bytes. con Display active baseband connections cc [--*role*\=c|p] [--*pkt-type*\=<*ptype*>] <*bdaddr*> Create baseband connection to remote device with Bluetooth address *bdaddr*. Option **--pkt-type** specifies a list of allowed packet types. <*ptype*> is a comma-separated list of packet types, where the possible packet types are **DM1**, **DM3**, **DM5**, **DH1**, **DH3**, **DH5**, **HV1**, **HV2**, **HV3**. Default is to allow all packet types. Option **--role** can have value **c** (do not allow role switch, stay central) or **p** (allow role switch, become peripheral if the peer asks to become central). Default is **c**. dc <*bdaddr*> [*reason*] Delete baseband connection from remote device with Bluetooth address *bdaddr*. The reason can be one of the Bluetooth HCI error codes. Default is **19** for user ended connections. The value must be given in decimal. sr <*bdaddr*> <*role*> Switch role for the baseband connection from the remote device to **central** or **peripheral**. cpt <*bdaddr*> <*ptypes*> Change packet types for baseband connection to device with Bluetooth address *bdaddr*. *ptypes* is a comma-separated list of packet types, where the possible packet types are **DM1**, **DM3**, **DM5**, **DH1**, **DH3**, **DH5**, **HV1**, **HV2**, **HV3**. rssi <*bdaddr*> Display received signal strength information for the connection to the device with Bluetooth address *bdaddr*. lq <*bdaddr*> Display link quality for the connection to the device with Bluetooth address *bdaddr*. tpl <*bdaddr*> [*type*] Display transmit power level for the connection to the device with Bluetooth address *bdaddr*. The *type* can be **0** for the current transmit power level (which is default) or **1** for the maximum transmit power level. afh <*bdaddr*> Display AFH channel map for the connection to the device with Bluetooth address *bdaddr*. lp <*bdaddr*> [*value*] With no value, displays link policy settings for the connection to the device with Bluetooth address *bdaddr*. If *value* is given, sets the link policy settings for that connection to *value*. Possible values are **RSWITCH**, **HOLD**, **SNIFF** and **PARK**. lst <*bdaddr*> [*value*] With no value, displays link supervision timeout for the connection to the device with Bluetooth address *bdaddr*. If *value* is given, sets the link supervision timeout for that connection to *value* slots, or to infinite if value is 0. auth <*bdaddr*> Request authentication for the device with Bluetooth address *bdaddr*. enc <*bdaddr*> [*encrypt*] **enable** or **disable** the encryption for the device with Bluetooth address *bdaddr*. key <*bdaddr*> Change the connection link key for the device with Bluetooth address *bdaddr*. clkoff <*bdaddr*> Read the clock offset for the device with Bluetooth address *bdaddr*. clock [*bdaddr*] [*clock*] Read the clock for the device with Bluetooth address *bdaddr*. The *clock* can be **0** for the local clock or **1** for the piconet clock (which is default). lescan [--*privacy*] [--*passive*] [--*acceptlist*] [--*discovery*\=g|l] [--*duplicates*] Start LE scan leinfo [--*static*] [--*random*] <*bdaddr*> Get LE remote information lealadd [--*random*] <*bdaddr*> Add device to LE Accept List lealrm <*bdaddr*> Remove device from LE Accept List lealsz Read size of LE Accept List lealclr Clear LE Accept List lerladd [--*local_irk*] [--*peer_irk*] [--*random*] <*bdaddr*> Add device to LE Resolving List lerlrm <*bdaddr*> Remove device from LE Resolving List lerlclr Clear LE Resolving List lerlsz Read size of LE Resolving List lerlon Enable LE Address Resolution lerloff Disable LE Address Resolution lecc [--*static*] [--*random*] <*bdaddr*> | [--*acceptlist*] Create a LE Connection ledc <*handle*> [*reason*] Disconnect a LE Connection lecup <*handle*> <*min*> <*max*> <*latency*> <*timeout*> LE Connection Update RESOURCES ========= http://www.bluez.org REPORTING BUGS ============== linux-bluetooth@vger.kernel.org bluez-5.82/tools/PaxHeaders/test-runner.c0000644000000000000000000000005014766002272015446 xustar0020 atime=1743515578 20 ctime=1743591287 bluez-5.82/tools/test-runner.c0000644000000000000000000006547414766002272015147 0ustar00rootroot// SPDX-License-Identifier: LGPL-2.1-or-later /* * * BlueZ - Bluetooth protocol stack for Linux * * Copyright (C) 2012-2014 Intel Corporation. All rights reserved. * * */ #ifdef HAVE_CONFIG_H #include #endif #define _GNU_SOURCE #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "lib/bluetooth.h" #include "lib/hci.h" #include "lib/hci_lib.h" #include "tools/hciattach.h" #ifndef WAIT_ANY #define WAIT_ANY (-1) #endif #define CMDLINE_MAX (2048 * 10) static const char *own_binary; static char **test_argv; static int test_argc; static bool run_auto = false; static bool start_dbus = false; static bool start_dbus_session; static bool start_daemon = false; static bool start_emulator = false; static bool start_monitor = false; static bool qemu_host_cpu = false; static int num_devs = 0; static const char *qemu_binary = NULL; static const char *kernel_image = NULL; static char *audio_server; static const char *qemu_table[] = { "qemu-system-x86_64", "qemu-system-i386", "/usr/bin/qemu-system-x86_64", "/usr/bin/qemu-system-i386", NULL }; static const char *find_qemu(void) { int i; for (i = 0; qemu_table[i]; i++) { struct stat st; if (!stat(qemu_table[i], &st)) return qemu_table[i]; } return NULL; } static const char *kernel_table[] = { "bzImage", "arch/x86/boot/bzImage", "vmlinux", "arch/x86/boot/vmlinux", NULL }; static const char *find_kernel(void) { int i; for (i = 0; kernel_table[i]; i++) { struct stat st; if (!stat(kernel_table[i], &st)) return kernel_table[i]; } return NULL; } static const struct { const char *target; const char *linkpath; } dev_table[] = { { "/proc/self/fd", "/dev/fd" }, { "/proc/self/fd/0", "/dev/stdin" }, { "/proc/self/fd/1", "/dev/stdout" }, { "/proc/self/fd/2", "/dev/stderr" }, { } }; static const struct { const char *fstype; const char *target; const char *options; unsigned long flags; } mount_table[] = { { "sysfs", "/sys", NULL, MS_NOSUID|MS_NOEXEC|MS_NODEV }, { "proc", "/proc", NULL, MS_NOSUID|MS_NOEXEC|MS_NODEV }, { "devtmpfs", "/dev", "mode=0755", MS_NOSUID|MS_STRICTATIME }, { "devpts", "/dev/pts", "mode=0620", MS_NOSUID|MS_NOEXEC }, { "tmpfs", "/dev/shm", "mode=1777", MS_NOSUID|MS_NODEV|MS_STRICTATIME }, { "tmpfs", "/run", "mode=0755", MS_NOSUID|MS_NODEV|MS_STRICTATIME }, { "tmpfs", "/tmp", NULL, 0 }, { "debugfs", "/sys/kernel/debug", NULL, 0 }, { } }; static const char *config_table[] = { "/var/lib/bluetooth", "/etc/bluetooth", "/etc/dbus-1", "/usr/share/dbus-1", NULL }; static void enable_printk(void) { FILE *f; f = fopen("/proc/sys/kernel/printk", "w"); if (!f) { perror("Failed to set printk"); return; } /* Restore printk loglevel, undoing 'quiet' in cmdline (suppress early * on-boot messages), to show WARN_ON etc. Suppress level>=6(INFO), set * default_msg:4(WARN) & min:1, default:7. See man 2 syslog. */ fprintf(f, "6 4 1 7"); fclose(f); } static void prepare_sandbox(void) { int i; for (i = 0; mount_table[i].fstype && mount_table[i].target; i++) { struct stat st; if (lstat(mount_table[i].target, &st) < 0) { printf("Creating %s\n", mount_table[i].target); mkdir(mount_table[i].target, 0755); } printf("Mounting %s to %s\n", mount_table[i].fstype, mount_table[i].target); if (mount(mount_table[i].fstype, mount_table[i].target, mount_table[i].fstype, mount_table[i].flags, mount_table[i].options) < 0) perror("Failed to mount filesystem"); } for (i = 0; dev_table[i].target; i++) { printf("Linking %s to %s\n", dev_table[i].linkpath, dev_table[i].target); if (symlink(dev_table[i].target, dev_table[i].linkpath) < 0) perror("Failed to create device symlink"); } printf("Creating new session group leader\n"); setsid(); printf("Setting controlling terminal\n"); ioctl(STDIN_FILENO, TIOCSCTTY, 1); for (i = 0; config_table[i]; i++) { printf("Creating %s\n", config_table[i]); if (mount("tmpfs", config_table[i], "tmpfs", MS_NOSUID|MS_NOEXEC|MS_NODEV|MS_STRICTATIME, "mode=0755") < 0) perror("Failed to create filesystem"); } enable_printk(); } static char *const qemu_argv[] = { "", "-nodefaults", "-no-user-config", "-monitor", "none", "-display", "none", "-machine", "type=q35,accel=kvm:tcg", "-m", "256M", "-net", "none", "-no-reboot", "-fsdev", "local,id=fsdev-root,path=/,readonly=on,security_model=none," "multidevs=remap", "-device", "virtio-9p-pci,fsdev=fsdev-root,mount_tag=/dev/root", "-chardev", "stdio,id=con,mux=on", "-serial", "chardev:con", NULL }; static char *const qemu_envp[] = { "HOME=/", NULL }; static void check_virtualization(void) { #if defined(__GNUC__) && (defined(__i386__) || defined(__amd64__)) uint32_t ecx; __asm__ __volatile__("cpuid" : "=c" (ecx) : "a" (1) : "memory"); if (!!(ecx & (1 << 5))) printf("Found support for Virtual Machine eXtensions\n"); #endif } static void start_qemu(void) { char cwd[PATH_MAX/2], initcmd[PATH_MAX], testargs[PATH_MAX]; char cmdline[CMDLINE_MAX]; char **argv; int i, pos; check_virtualization(); if (!getcwd(cwd, sizeof(cwd))) strcat(cwd, "/"); if (own_binary[0] == '/') snprintf(initcmd, sizeof(initcmd), "%s", own_binary); else snprintf(initcmd, sizeof(initcmd), "%s/%s", cwd, own_binary); pos = snprintf(testargs, sizeof(testargs), "%s", test_argv[0]); for (i = 1; i < test_argc; i++) { int len = sizeof(testargs) - pos; pos += snprintf(testargs + pos, len, " %s", test_argv[i]); } snprintf(cmdline, sizeof(cmdline), "console=ttyS0,115200n8 earlyprintk=serial " "no_hash_pointers=1 rootfstype=9p " "rootflags=trans=virtio,version=9p2000.u " "acpi=off pci=noacpi noapic quiet ro init=%s " "TESTHOME=%s TESTDBUS=%u TESTDAEMON=%u " "TESTDBUSSESSION=%u XDG_RUNTIME_DIR=/run/user/0 " "TESTMONITOR=%u TESTEMULATOR=%u TESTDEVS=%d " "TESTAUTO=%u TESTAUDIO='%s' TESTARGS=\'%s\'", initcmd, cwd, start_dbus, start_daemon, start_dbus_session, start_monitor, start_emulator, num_devs, run_auto, audio_server ? audio_server : "", testargs); argv = alloca(sizeof(qemu_argv) + (sizeof(char *) * (6 + (num_devs * 4)))); memcpy(argv, qemu_argv, sizeof(qemu_argv)); pos = (sizeof(qemu_argv) / sizeof(char *)) - 1; /* Make sure qemu_binary is not null */ if (!qemu_binary) { fprintf(stderr, "No QEMU binary is set\n"); exit(1); } argv[0] = (char *) qemu_binary; if (qemu_host_cpu) { argv[pos++] = "-cpu"; argv[pos++] = "host"; } argv[pos++] = "-kernel"; argv[pos++] = (char *) kernel_image; argv[pos++] = "-append"; argv[pos++] = (char *) cmdline; for (i = 0; i < num_devs; i++) { const char *path = "/tmp/bt-server-bredr"; char *chrdev, *serdev; chrdev = alloca(48 + strlen(path)); sprintf(chrdev, "socket,path=%s,id=bt%d", path, i); serdev = alloca(48); sprintf(serdev, "pci-serial,chardev=bt%d", i); argv[pos++] = "-chardev"; argv[pos++] = chrdev; argv[pos++] = "-device"; argv[pos++] = serdev; } argv[pos] = NULL; execve(argv[0], argv, qemu_envp); } static int open_serial(const char *path) { struct termios ti; int fd, saved_ldisc, ldisc = N_HCI; fd = open(path, O_RDWR | O_NOCTTY); if (fd < 0) { perror("Failed to open serial port"); return -1; } if (tcflush(fd, TCIOFLUSH) < 0) { perror("Failed to flush serial port"); close(fd); return -1; } if (ioctl(fd, TIOCGETD, &saved_ldisc) < 0) { perror("Failed get serial line discipline"); close(fd); return -1; } /* Switch TTY to raw mode */ memset(&ti, 0, sizeof(ti)); cfmakeraw(&ti); ti.c_cflag |= (B115200 | CLOCAL | CREAD); /* Set flow control */ ti.c_cflag |= CRTSCTS; if (tcsetattr(fd, TCSANOW, &ti) < 0) { perror("Failed to set serial port settings"); close(fd); return -1; } if (ioctl(fd, TIOCSETD, &ldisc) < 0) { perror("Failed set serial line discipline"); close(fd); return -1; } printf("Switched line discipline from %d to %d\n", saved_ldisc, ldisc); return fd; } static int attach_proto(const char *path, unsigned int proto, unsigned int mandatory_flags, unsigned int optional_flags) { unsigned int flags = mandatory_flags | optional_flags; int fd, dev_id; fd = open_serial(path); if (fd < 0) return -1; if (ioctl(fd, HCIUARTSETFLAGS, flags) < 0) { if (errno == EINVAL) { if (ioctl(fd, HCIUARTSETFLAGS, mandatory_flags) < 0) { perror("Failed to set mandatory flags"); close(fd); return -1; } } else { perror("Failed to set flags"); close(fd); return -1; } } if (ioctl(fd, HCIUARTSETPROTO, proto) < 0) { perror("Failed to set protocol"); close(fd); return -1; } dev_id = ioctl(fd, HCIUARTGETDEVICE); if (dev_id < 0) { perror("Failed to get device id"); close(fd); return -1; } printf("Device index %d attached\n", dev_id); return fd; } static void create_dbus_system_conf(void) { FILE *fp; fp = fopen("/etc/dbus-1/system.conf", "we"); if (!fp) return; fputs("\n", fp); fputs("\n", fp); fputs("system\n", fp); fputs("unix:path=/run/dbus/system_bus_socket\n", fp); fputs("\n", fp); fputs("\n", fp); fputs("\n", fp); fputs("\n",fp); fputs("\n", fp); fputs("\n", fp); fputs("\n", fp); fputs("\n",fp); fputs("\n", fp); fputs("\n", fp); fputs("\n", fp); fputs("\n", fp); fputs("\n", fp); fclose(fp); if (symlink("/etc/dbus-1/system.conf", "/usr/share/dbus-1/system.conf") < 0) perror("Failed to create system.conf symlink"); mkdir("/run/dbus", 0755); } static void create_dbus_session_conf(void) { FILE *fp; fp = fopen("/etc/dbus-1/session.conf", "we"); if (!fp) return; fputs("\n", fp); fputs("\n", fp); fputs("session\n", fp); fputs("unix:path=/run/user/0/bus\n", fp); fputs("\n", fp); fputs("\n", fp); fputs("\n", fp); fputs("\n", fp); fputs("\n", fp); fputs("\n", fp); fputs("\n", fp); fputs("\n", fp); fputs("\n", fp); fputs("\n", fp); fputs("\n", fp); fputs("\n", fp); fputs("\n", fp); fclose(fp); if (symlink("/etc/dbus-1/session.conf", "/usr/share/dbus-1/session.conf") < 0) perror("Failed to create session.conf symlink"); if (mkdir("/run/user", 0755) < 0) { fprintf(stderr, "unable to create /run/user directory\n"); return; } if (mkdir("/run/user/0", 0755) < 0) { fprintf(stderr, "unable to create /run/user/0 directory\n"); return; } } static pid_t start_dbus_daemon(bool session) { char *argv[3], *envp[1]; pid_t pid; int i; char *bus_type = session ? "session" : "system"; char *socket_path = session ? "/run/user/0/bus" : "/run/dbus/system_bus_socket"; argv[0] = "/usr/bin/dbus-daemon"; if (session) argv[1] = "--session"; else argv[1] = "--system"; argv[2] = NULL; envp[0] = NULL; printf("Starting D-Bus %s daemon\n", bus_type); pid = fork(); if (pid < 0) { perror("Failed to fork new process"); return -1; } if (pid == 0) { execve(argv[0], argv, envp); exit(EXIT_SUCCESS); } printf("D-Bus %s daemon process %d created\n", bus_type, pid); for (i = 0; i < 20; i++) { struct stat st; if (!stat(socket_path, &st)) { printf("Found D-Bus %s daemon socket\n", bus_type); return pid; } sleep(1); } return -1; } static const char *daemon_table[] = { "bluetoothd", "src/bluetoothd", "/usr/sbin/bluetoothd", "/usr/libexec/bluetooth/bluetoothd", NULL }; static pid_t start_bluetooth_daemon(const char *home) { const char *daemon = NULL; char *argv[6], *envp[2]; pid_t pid; struct stat st; int i; if (chdir(home + 5) < 0) { perror("Failed to change home directory for daemon"); return -1; } for (i = 0; daemon_table[i]; i++) { if (!stat(daemon_table[i], &st)) { daemon = daemon_table[i]; break; } } if (!daemon) { fprintf(stderr, "Failed to locate Bluetooth daemon binary\n"); return -1; } printf("Using Bluetooth daemon %s\n", daemon); argv[0] = (char *) daemon; argv[1] = "--nodetach"; argv[2] = "-d"; argv[3] = NULL; if (!stat("src/main.conf", &st)) { argv[3] = "-f"; argv[4] = "src/main.conf"; argv[5] = NULL; } envp[0] = "DBUS_SYSTEM_BUS_ADDRESS=unix:path=/run/dbus/system_bus_socket"; envp[1] = NULL; printf("Starting Bluetooth daemon\n"); pid = fork(); if (pid < 0) { perror("Failed to fork new process"); return -1; } if (pid == 0) { execve(argv[0], argv, envp); exit(EXIT_SUCCESS); } printf("Bluetooth daemon process %d created\n", pid); return pid; } static const char *test_table[] = { "mgmt-tester", "smp-tester", "l2cap-tester", "rfcomm-tester", "sco-tester", "iso-tester", "mesh-tester", "ioctl-tester", "bnep-tester", "check-selftest", "tools/mgmt-tester", "tools/smp-tester", "tools/l2cap-tester", "tools/rfcomm-tester", "tools/sco-tester", "tools/iso-tester", "tools/mesh-tester", "tools/ioctl-tester", "tools/bnep-tester", "tools/check-selftest", NULL }; static const char *monitor_table[] = { "btmon", "monitor/btmon", "/usr/sbin/btmon", NULL }; static pid_t start_btmon(const char *home) { const char *monitor = NULL; char *argv[3]; pid_t pid; int i; if (chdir(home + 5) < 0) { perror("Failed to change home directory for monitor"); return -1; } for (i = 0; monitor_table[i]; i++) { struct stat st; if (!stat(monitor_table[i], &st)) { monitor = monitor_table[i]; break; } } if (!monitor) { fprintf(stderr, "Failed to locate Monitor binary\n"); return -1; } printf("Using Monitor %s\n", monitor); argv[0] = (char *) monitor; argv[1] = "-t"; argv[2] = NULL; printf("Starting Monitor\n"); pid = fork(); if (pid < 0) { perror("Failed to fork new process"); return -1; } if (pid == 0) { execv(argv[0], argv); exit(EXIT_SUCCESS); } printf("Monitor process %d created\n", pid); return pid; } static const char *btvirt_table[] = { "btvirt", "emulator/btvirt", "/usr/sbin/btvirt", NULL }; static pid_t start_btvirt(const char *home) { const char *btvirt = NULL; char *argv[3]; pid_t pid; int i; if (chdir(home + 5) < 0) { perror("Failed to change home directory for daemon"); return -1; } for (i = 0; btvirt_table[i]; i++) { struct stat st; if (!stat(btvirt_table[i], &st)) { btvirt = btvirt_table[i]; break; } } if (!btvirt) { fprintf(stderr, "Failed to locate btvirt binary\n"); return -1; } printf("Using %s\n", btvirt); argv[0] = (char *) btvirt; argv[1] = "-l"; argv[2] = NULL; printf("Starting Emulator\n"); pid = fork(); if (pid < 0) { perror("Failed to fork new process"); return -1; } if (pid == 0) { execv(argv[0], argv); exit(EXIT_SUCCESS); } printf("Emulator process %d created\n", pid); return pid; } static int create_pipewire_conf(void) { static const char *const dirs[] = { "/run/conf", "/run/conf/wireplumber", "/run/conf/wireplumber/bluetooth.lua.d", "/run/conf/wireplumber/main.lua.d", NULL }; int i; FILE *f; for (i = 0; dirs[i]; ++i) mkdir(dirs[i], 0755); /* Enable only Bluetooth part, disable whatever requires user DBus */ f = fopen("/run/conf/wireplumber/main.lua.d/51-custom.lua", "w"); if (!f) goto fail; fprintf(f, "alsa_monitor.enabled = false\n" "v4l2_monitor.enabled = false\n" "libcamera_monitor.enabled = false\n" "default_access.properties[\"enable-flatpak-portal\"]" " = false\n"); fclose(f); f = fopen("/run/conf/wireplumber/bluetooth.lua.d/51-custom.lua", "w"); if (!f) goto fail; fprintf(f, "bluez_monitor.properties[\"with-logind\"] = false\n" "bluez_midi_monitor.enabled = false\n"); fclose(f); return 0; fail: perror("Failed to create Pipewire config"); return -1; } static int start_audio_server(pid_t pids[2]) { char *daemons[2] = {NULL, NULL}; char wp_exe[PATH_MAX]; char *ptr; char *envp[5]; int i; for (i = 0; i < 2; ++i) pids[i] = -1; daemons[0] = audio_server; ptr = strrchr(audio_server, '/'); if (ptr && !strcmp(ptr, "/pipewire")) { if (create_pipewire_conf()) return -1; snprintf(wp_exe, sizeof(wp_exe), "%.*s/wireplumber", (int)(ptr - audio_server), audio_server); daemons[1] = wp_exe; setenv("PIPEWIRE_RUNTIME_DIR", "/run", 1); } envp[0] = "DBUS_SYSTEM_BUS_ADDRESS=unix:" "path=/run/dbus/system_bus_socket"; envp[1] = "XDG_CONFIG_HOME=/run/conf"; envp[2] = "XDG_STATE_HOME=/run"; envp[3] = "XDG_RUNTIME_DIR=/run"; envp[4] = NULL; for (i = 0; i < 2; ++i) { const char *daemon = daemons[i]; char *argv[2]; pid_t pid; if (!daemon) continue; printf("Starting audio server %s\n", daemon); argv[0] = (char *) daemon; argv[1] = NULL; pid = fork(); if (pid < 0) { perror("Failed to fork new process"); return -1; } if (pid == 0) { execve(argv[0], argv, envp); exit(EXIT_SUCCESS); } pids[i] = pid; printf("Audio server process %d created\n", pid); } return 0; } static void run_command(char *cmdname, char *home) { char *argv[9], *envp[3]; int pos = 0, idx = 0; int serial_fd; pid_t pid, dbus_pid, daemon_pid, monitor_pid, emulator_pid, dbus_session_pid, audio_pid[2]; int i; if (!home) { perror("Invalid parameter: TESTHOME"); return; } if (num_devs) { const char *node = "/dev/ttyS1"; unsigned int basic_flags, extra_flags; printf("Attaching BR/EDR controller to %s\n", node); basic_flags = (1 << HCI_UART_RESET_ON_INIT); extra_flags = (1 << HCI_UART_VND_DETECT); serial_fd = attach_proto(node, HCI_UART_H4, basic_flags, extra_flags); } else serial_fd = -1; if (start_dbus) { create_dbus_system_conf(); dbus_pid = start_dbus_daemon(false); } else dbus_pid = -1; if (start_dbus_session) { create_dbus_session_conf(); dbus_session_pid = start_dbus_daemon(true); } else dbus_session_pid = -1; if (start_daemon) daemon_pid = start_bluetooth_daemon(home); else daemon_pid = -1; if (start_monitor) monitor_pid = start_btmon(home); else monitor_pid = -1; if (start_emulator) emulator_pid = start_btvirt(home); else emulator_pid = -1; if (audio_server) start_audio_server(audio_pid); else audio_pid[0] = audio_pid[1] = -1; start_next: if (!run_auto && !cmdname) { fprintf(stderr, "Missing command argument\n"); return; } if (run_auto) { if (chdir(home + 5) < 0) { perror("Failed to change home test directory"); return; } while (1) { struct stat st; if (!test_table[idx]) return; if (!stat(test_table[idx], &st)) break; idx++; } argv[0] = (char *) test_table[idx]; argv[1] = "-q"; argv[2] = NULL; cmdname = NULL; } pos = 0; envp[pos++] = "TERM=linux"; if (home) envp[pos++] = home; envp[pos] = NULL; printf("Running command %s\n", cmdname ? cmdname : argv[0]); pid = fork(); if (pid < 0) { perror("Failed to fork new process"); if (serial_fd >= 0) close(serial_fd); return; } if (pid == 0) { if (home) { printf("Changing into directory %s\n", home + 5); if (chdir(home + 5) < 0) perror("Failed to change directory"); } if (!cmdname) execve(argv[0], argv, envp); else execl("/bin/sh", "sh", "-c", cmdname, NULL); exit(EXIT_SUCCESS); } printf("New process %d created\n", pid); while (1) { pid_t corpse; int status; corpse = waitpid(WAIT_ANY, &status, 0); if (corpse < 0 || corpse == 0) continue; if (WIFEXITED(status)) printf("Process %d exited with status %d\n", corpse, WEXITSTATUS(status)); else if (WIFSIGNALED(status)) printf("Process %d terminated with signal %d\n", corpse, WTERMSIG(status)); else if (WIFSTOPPED(status)) printf("Process %d stopped with signal %d\n", corpse, WSTOPSIG(status)); else if (WIFCONTINUED(status)) printf("Process %d continued\n", corpse); if (corpse == dbus_pid) { printf("D-Bus system daemon terminated\n"); dbus_pid = -1; } if (corpse == dbus_session_pid) { printf("D-Bus session daemon terminated\n"); dbus_session_pid = -1; } if (corpse == daemon_pid) { printf("Bluetooth daemon terminated\n"); daemon_pid = -1; } if (corpse == emulator_pid) { printf("Bluetooth emulator terminated\n"); emulator_pid = -1; } if (corpse == monitor_pid) { printf("Bluetooth monitor terminated\n"); monitor_pid = -1; } for (i = 0; i < 2; ++i) { if (corpse == audio_pid[i]) { printf("Audio server %d terminated\n", i); audio_pid[i] = -1; } } if (corpse == pid) break; } if (run_auto) { idx++; goto start_next; } for (i = 0; i < 2; ++i) { if (audio_pid[i] > 0) kill(audio_pid[i], SIGTERM); } if (daemon_pid > 0) kill(daemon_pid, SIGTERM); if (dbus_pid > 0) kill(dbus_pid, SIGTERM); if (dbus_session_pid > 0) kill(dbus_session_pid, SIGTERM); if (emulator_pid > 0) kill(dbus_pid, SIGTERM); if (monitor_pid > 0) kill(monitor_pid, SIGTERM); if (serial_fd >= 0) close(serial_fd); } static void run_tests(void) { char cmdline[CMDLINE_MAX], *ptr, *cmds, *home = NULL; FILE *fp; fp = fopen("/proc/cmdline", "re"); if (!fp) { fprintf(stderr, "Failed to open kernel command line\n"); return; } ptr = fgets(cmdline, sizeof(cmdline), fp); fclose(fp); if (!ptr) { fprintf(stderr, "Failed to read kernel command line\n"); return; } ptr = strstr(cmdline, "TESTARGS="); if (!ptr) { fprintf(stderr, "No test command section found\n"); return; } cmds = ptr + 10; ptr = strchr(cmds, '\''); if (!ptr) { fprintf(stderr, "Malformed test command section\n"); return; } *ptr = '\0'; ptr = strstr(cmdline, "TESTAUTO=1"); if (ptr) { printf("Automatic test execution requested\n"); run_auto= true; } ptr = strstr(cmdline, "TESTDEVS=1"); if (ptr) { printf("Attachment of devices requested\n"); num_devs = 1; } ptr = strstr(cmdline, "TESTDBUS=1"); if (ptr) { printf("D-Bus system daemon requested\n"); start_dbus = true; } ptr = strstr(cmdline, "TESTDBUSSESSION=1"); if (ptr) { printf("D-Bus session daemon requested\n"); start_dbus_session = true; } ptr = strstr(cmdline, "TESTDAEMON=1"); if (ptr) { printf("bluetoothd requested\n"); start_daemon = true; } ptr = strstr(cmdline, "TESTMONITOR=1"); if (ptr) { printf("Monitor requested\n"); start_monitor = true; } ptr = strstr(cmdline, "TESTEMULATOR=1"); if (ptr) { printf("Emulator requested\n"); start_emulator = true; } ptr = strstr(cmdline, "TESTAUDIO='"); if (ptr) { const char *start = ptr + 11; const char *end = strchr(start, '\''); if (end && end != start) { audio_server = strndup(start, end - start); printf("Audio server %s requested\n", audio_server); } } ptr = strstr(cmdline, "TESTHOME="); if (ptr) { home = ptr + 4; ptr = strpbrk(home + 9, " \r\n"); if (ptr) *ptr = '\0'; } run_command(cmds, home); } static void usage(void) { printf("test-runner - Automated test execution utility\n" "Usage:\n"); printf("\ttest-runner [options] [--] [args]\n"); printf("Options:\n" "\t-a, --auto Find tests and run them\n" "\t-b, --dbus Start D-Bus system daemon\n" "\t-s, --dbus-session Start D-Bus session daemon\n" "\t-d, --daemon Start bluetoothd\n" "\t-m, --monitor Start btmon\n" "\t-l, --emulator Start btvirt\n" "\t-A, --audio[=path] Start audio server\n" "\t-u, --unix [path] Provide serial device\n" "\t-q, --qemu QEMU binary\n" "\t-H, --qemu-host-cpu Use host CPU (requires KVM support)\n" "\t-k, --kernel Kernel image (bzImage)\n" "\t-h, --help Show help options\n"); } static const struct option main_options[] = { { "all", no_argument, NULL, 'a' }, { "auto", no_argument, NULL, 'a' }, { "dbus", no_argument, NULL, 'b' }, { "dbus-session", no_argument, NULL, 's' }, { "unix", no_argument, NULL, 'u' }, { "daemon", no_argument, NULL, 'd' }, { "emulator", no_argument, NULL, 'l' }, { "monitor", no_argument, NULL, 'm' }, { "qemu", required_argument, NULL, 'q' }, { "qemu-host-cpu", no_argument, NULL, 'H' }, { "kernel", required_argument, NULL, 'k' }, { "audio", optional_argument, NULL, 'A' }, { "version", no_argument, NULL, 'v' }, { "help", no_argument, NULL, 'h' }, { } }; int main(int argc, char *argv[]) { if (getpid() == 1 && getppid() == 0) { prepare_sandbox(); run_tests(); sync(); reboot(RB_AUTOBOOT); return EXIT_SUCCESS; } for (;;) { int opt; opt = getopt_long(argc, argv, "aubdslmq:Hk:A::vh", main_options, NULL); if (opt < 0) break; switch (opt) { case 'a': run_auto = true; break; case 'u': num_devs = 1; break; case 'b': start_dbus = true; break; case 's': start_dbus_session = true; break; case 'd': start_dbus = true; start_daemon = true; break; case 'l': start_emulator = true; break; case 'm': start_monitor = true; break; case 'q': qemu_binary = optarg; break; case 'H': qemu_host_cpu = true; break; case 'k': kernel_image = optarg; break; case 'A': audio_server = optarg ? optarg : "/usr/bin/pipewire"; break; case 'v': printf("%s\n", VERSION); return EXIT_SUCCESS; case 'h': usage(); return EXIT_SUCCESS; default: return EXIT_FAILURE; } } if (run_auto) { if (argc - optind > 0) { fprintf(stderr, "Invalid command line parameters\n"); return EXIT_FAILURE; } } else { if (argc - optind < 1) { fprintf(stderr, "Failed to specify test command\n"); return EXIT_FAILURE; } } own_binary = argv[0]; test_argv = argv + optind; test_argc = argc - optind; if (!qemu_binary) { qemu_binary = find_qemu(); if (!qemu_binary) { fprintf(stderr, "No default QEMU binary found\n"); return EXIT_FAILURE; } } if (!kernel_image) { kernel_image = find_kernel(); if (!kernel_image) { fprintf(stderr, "No default kernel image found\n"); return EXIT_FAILURE; } } printf("Using QEMU binary %s\n", qemu_binary); printf("Using kernel image %s\n", kernel_image); start_qemu(); return EXIT_SUCCESS; } bluez-5.82/tools/PaxHeaders/ciptool.c0000644000000000000000000000005014015011624014615 xustar0020 atime=1743516871 20 ctime=1743591285 bluez-5.82/tools/ciptool.c0000644000000000000000000002256414015011624014307 0ustar00rootroot// SPDX-License-Identifier: GPL-2.0-or-later /* * * BlueZ - Bluetooth protocol stack for Linux * * Copyright (C) 2002-2010 Marcel Holtmann * * */ #ifdef HAVE_CONFIG_H #include #endif #define _GNU_SOURCE #include #include #include #include #include #include #include #include #include #include #include "lib/bluetooth.h" #include "lib/hci.h" #include "lib/hci_lib.h" #include "lib/l2cap.h" #include "lib/sdp.h" #include "lib/sdp_lib.h" #include "lib/cmtp.h" static volatile sig_atomic_t __io_canceled = 0; static void sig_hup(int sig) { return; } static void sig_term(int sig) { __io_canceled = 1; } static char *cmtp_state[] = { "unknown", "connected", "open", "bound", "listening", "connecting", "connecting", "config", "disconnecting", "closed" }; static char *cmtp_flagstostr(uint32_t flags) { static char str[100] = ""; strcat(str, "["); if (flags & (1 << CMTP_LOOPBACK)) strcat(str, "loopback"); strcat(str, "]"); return str; } static int get_psm(bdaddr_t *src, bdaddr_t *dst, unsigned short *psm) { sdp_session_t *s; sdp_list_t *srch, *attrs, *rsp; uuid_t svclass; uint16_t attr; int err; if (!(s = sdp_connect(src, dst, 0))) return -1; sdp_uuid16_create(&svclass, CIP_SVCLASS_ID); srch = sdp_list_append(NULL, &svclass); attr = SDP_ATTR_PROTO_DESC_LIST; attrs = sdp_list_append(NULL, &attr); err = sdp_service_search_attr_req(s, srch, SDP_ATTR_REQ_INDIVIDUAL, attrs, &rsp); sdp_close(s); if (err) return 0; for (; rsp; rsp = rsp->next) { sdp_record_t *rec = (sdp_record_t *) rsp->data; sdp_list_t *protos; if (!sdp_get_access_protos(rec, &protos)) { unsigned short p = sdp_get_proto_port(protos, L2CAP_UUID); if (p > 0) { *psm = p; return 1; } } } return 0; } static int do_connect(int ctl, int dev_id, bdaddr_t *src, bdaddr_t *dst, unsigned short psm, uint32_t flags) { struct cmtp_connadd_req req; struct hci_dev_info di; struct sockaddr_l2 addr; struct l2cap_options opts; socklen_t size; int sk; hci_devinfo(dev_id, &di); if (!(di.link_policy & HCI_LP_RSWITCH)) { printf("Local device is not accepting role switch\n"); } if ((sk = socket(AF_BLUETOOTH, SOCK_SEQPACKET, BTPROTO_L2CAP)) < 0) { perror("Can't create L2CAP socket"); exit(1); } memset(&addr, 0, sizeof(addr)); addr.l2_family = AF_BLUETOOTH; bacpy(&addr.l2_bdaddr, src); if (bind(sk, (struct sockaddr *)&addr, sizeof(addr)) < 0) { perror("Can't bind L2CAP socket"); close(sk); exit(1); } memset(&opts, 0, sizeof(opts)); size = sizeof(opts); if (getsockopt(sk, SOL_L2CAP, L2CAP_OPTIONS, &opts, &size) < 0) { perror("Can't get L2CAP options"); close(sk); exit(1); } opts.imtu = CMTP_DEFAULT_MTU; opts.omtu = CMTP_DEFAULT_MTU; opts.flush_to = 0xffff; if (setsockopt(sk, SOL_L2CAP, L2CAP_OPTIONS, &opts, sizeof(opts)) < 0) { perror("Can't set L2CAP options"); close(sk); exit(1); } memset(&addr, 0, sizeof(addr)); addr.l2_family = AF_BLUETOOTH; bacpy(&addr.l2_bdaddr, dst); addr.l2_psm = htobs(psm); if (connect(sk, (struct sockaddr *)&addr, sizeof(addr)) < 0) { perror("Can't connect L2CAP socket"); close(sk); exit(1); } req.sock = sk; req.flags = flags; if (ioctl(ctl, CMTPCONNADD, &req) < 0) { perror("Can't create connection"); exit(1); } return sk; } static void cmd_show(int ctl, bdaddr_t *bdaddr, int argc, char **argv) { struct cmtp_connlist_req req; struct cmtp_conninfo ci[16]; char addr[18]; unsigned int i; req.cnum = 16; req.ci = ci; if (ioctl(ctl, CMTPGETCONNLIST, &req) < 0) { perror("Can't get connection list"); exit(1); } for (i = 0; i < req.cnum; i++) { ba2str(&ci[i].bdaddr, addr); printf("%d %s %s %s\n", ci[i].num, addr, cmtp_state[ci[i].state], ci[i].flags ? cmtp_flagstostr(ci[i].flags) : ""); } } static void cmd_search(int ctl, bdaddr_t *bdaddr, int argc, char **argv) { inquiry_info *info = NULL; bdaddr_t src, dst; unsigned short psm; int i, dev_id, num_rsp, length, flags; char addr[18]; uint8_t class[3]; ba2str(bdaddr, addr); dev_id = hci_devid(addr); if (dev_id < 0) { dev_id = hci_get_route(NULL); hci_devba(dev_id, &src); } else bacpy(&src, bdaddr); length = 8; /* ~10 seconds */ num_rsp = 0; flags = 0; printf("Searching ...\n"); num_rsp = hci_inquiry(dev_id, length, num_rsp, NULL, &info, flags); for (i = 0; i < num_rsp; i++) { memcpy(class, (info+i)->dev_class, 3); if ((class[1] == 2) && ((class[0] / 4) == 5)) { bacpy(&dst, &(info+i)->bdaddr); ba2str(&dst, addr); printf("\tChecking service for %s\n", addr); if (!get_psm(&src, &dst, &psm)) continue; bt_free(info); printf("\tConnecting to device %s\n", addr); do_connect(ctl, dev_id, &src, &dst, psm, 0); return; } } bt_free(info); fprintf(stderr, "\tNo devices in range or visible\n"); exit(1); } static void cmd_create(int ctl, bdaddr_t *bdaddr, int argc, char **argv) { bdaddr_t src, dst; unsigned short psm; int dev_id; char addr[18]; if (argc < 2) return; str2ba(argv[1], &dst); ba2str(bdaddr, addr); dev_id = hci_devid(addr); if (dev_id < 0) { dev_id = hci_get_route(&dst); hci_devba(dev_id, &src); } else bacpy(&src, bdaddr); if (argc < 3) { if (!get_psm(&src, &dst, &psm)) psm = 4099; } else psm = atoi(argv[2]); do_connect(ctl, dev_id, &src, &dst, psm, 0); } static void cmd_release(int ctl, bdaddr_t *bdaddr, int argc, char **argv) { struct cmtp_conndel_req req; struct cmtp_connlist_req cl; struct cmtp_conninfo ci[16]; if (argc < 2) { cl.cnum = 16; cl.ci = ci; if (ioctl(ctl, CMTPGETCONNLIST, &cl) < 0) { perror("Can't get connection list"); exit(1); } if (cl.cnum == 0) return; if (cl.cnum != 1) { fprintf(stderr, "You have to specifiy the device address.\n"); exit(1); } bacpy(&req.bdaddr, &ci[0].bdaddr); } else str2ba(argv[1], &req.bdaddr); if (ioctl(ctl, CMTPCONNDEL, &req) < 0) { perror("Can't release connection"); exit(1); } } static void cmd_loopback(int ctl, bdaddr_t *bdaddr, int argc, char **argv) { struct cmtp_conndel_req req; struct sigaction sa; struct pollfd p; sigset_t sigs; bdaddr_t src, dst; unsigned short psm; int dev_id, sk; char addr[18]; if (argc < 2) return; str2ba(argv[1], &dst); ba2str(bdaddr, addr); dev_id = hci_devid(addr); if (dev_id < 0) { dev_id = hci_get_route(&dst); hci_devba(dev_id, &src); } else bacpy(&src, bdaddr); ba2str(&dst, addr); printf("Connecting to %s in loopback mode\n", addr); if (argc < 3) { if (!get_psm(&src, &dst, &psm)) psm = 4099; } else psm = atoi(argv[2]); sk = do_connect(ctl, dev_id, &src, &dst, psm, (1 << CMTP_LOOPBACK)); printf("Press CTRL-C for hangup\n"); memset(&sa, 0, sizeof(sa)); sa.sa_flags = SA_NOCLDSTOP; sa.sa_handler = SIG_IGN; sigaction(SIGCHLD, &sa, NULL); sigaction(SIGPIPE, &sa, NULL); sa.sa_handler = sig_term; sigaction(SIGTERM, &sa, NULL); sigaction(SIGINT, &sa, NULL); sa.sa_handler = sig_hup; sigaction(SIGHUP, &sa, NULL); sigfillset(&sigs); sigdelset(&sigs, SIGCHLD); sigdelset(&sigs, SIGPIPE); sigdelset(&sigs, SIGTERM); sigdelset(&sigs, SIGINT); sigdelset(&sigs, SIGHUP); p.fd = sk; p.events = POLLERR | POLLHUP; while (!__io_canceled) { p.revents = 0; if (ppoll(&p, 1, NULL, &sigs) > 0) break; } bacpy(&req.bdaddr, &dst); ioctl(ctl, CMTPCONNDEL, &req); } static struct { char *cmd; char *alt; void (*func)(int ctl, bdaddr_t *bdaddr, int argc, char **argv); char *opt; char *doc; } command[] = { { "show", "list", cmd_show, 0, "Show remote connections" }, { "search", "scan", cmd_search, 0, "Search for a remote device" }, { "connect", "create", cmd_create, "", "Connect a remote device" }, { "release", "disconnect", cmd_release, "[bdaddr]", "Disconnect the remote device" }, { "loopback", "test", cmd_loopback, "", "Loopback test of a device" }, { NULL, NULL, NULL, 0, 0 } }; static void usage(void) { int i; printf("ciptool - Bluetooth Common ISDN Access Profile (CIP)\n\n"); printf("Usage:\n" "\tciptool [options] [command]\n" "\n"); printf("Options:\n" "\t-i [hciX|bdaddr] Local HCI device or BD Address\n" "\t-h, --help Display help\n" "\n"); printf("Commands:\n"); for (i = 0; command[i].cmd; i++) printf("\t%-8s %-10s\t%s\n", command[i].cmd, command[i].opt ? command[i].opt : " ", command[i].doc); printf("\n"); } static struct option main_options[] = { { "help", 0, 0, 'h' }, { "device", 1, 0, 'i' }, { 0, 0, 0, 0 } }; int main(int argc, char *argv[]) { bdaddr_t bdaddr; int i, opt, ctl; bacpy(&bdaddr, BDADDR_ANY); while ((opt = getopt_long(argc, argv, "+i:h", main_options, NULL)) != -1) { switch(opt) { case 'i': if (!strncmp(optarg, "hci", 3)) hci_devba(atoi(optarg + 3), &bdaddr); else str2ba(optarg, &bdaddr); break; case 'h': usage(); exit(0); default: exit(0); } } argc -= optind; argv += optind; optind = 0; if (argc < 1) { usage(); return 0; } if ((ctl = socket(AF_BLUETOOTH, SOCK_RAW, BTPROTO_CMTP)) < 0 ) { perror("Can't open CMTP control socket"); exit(1); } for (i = 0; command[i].cmd; i++) { if (strncmp(command[i].cmd, argv[0], 4) && strncmp(command[i].alt, argv[0], 4)) continue; command[i].func(ctl, &bdaddr, argc, argv); close(ctl); exit(0); } usage(); close(ctl); return 0; } bluez-5.82/tools/PaxHeaders/hcitool.c0000644000000000000000000000005014165411566014624 xustar0020 atime=1743516872 20 ctime=1743591286 bluez-5.82/tools/hcitool.c0000644000000000000000000021520514165411566014312 0ustar00rootroot// SPDX-License-Identifier: GPL-2.0-or-later /* * * BlueZ - Bluetooth protocol stack for Linux * * Copyright (C) 2000-2001 Qualcomm Incorporated * Copyright (C) 2002-2003 Maxim Krasnyansky * Copyright (C) 2002-2010 Marcel Holtmann * * */ #ifdef HAVE_CONFIG_H #include #endif #define _GNU_SOURCE #include #include #include #include #include #include #include #include #include #include #include #include #include "lib/bluetooth.h" #include "lib/hci.h" #include "lib/hci_lib.h" #include "src/oui.h" #ifndef MIN #define MIN(a, b) ((a) < (b) ? (a) : (b)) #endif /* Unofficial value, might still change */ #define LE_LINK 0x80 #define FLAGS_AD_TYPE 0x01 #define FLAGS_LIMITED_MODE_BIT 0x01 #define FLAGS_GENERAL_MODE_BIT 0x02 #define EIR_FLAGS 0x01 /* flags */ #define EIR_UUID16_SOME 0x02 /* 16-bit UUID, more available */ #define EIR_UUID16_ALL 0x03 /* 16-bit UUID, all listed */ #define EIR_UUID32_SOME 0x04 /* 32-bit UUID, more available */ #define EIR_UUID32_ALL 0x05 /* 32-bit UUID, all listed */ #define EIR_UUID128_SOME 0x06 /* 128-bit UUID, more available */ #define EIR_UUID128_ALL 0x07 /* 128-bit UUID, all listed */ #define EIR_NAME_SHORT 0x08 /* shortened local name */ #define EIR_NAME_COMPLETE 0x09 /* complete local name */ #define EIR_TX_POWER 0x0A /* transmit power level */ #define EIR_DEVICE_ID 0x10 /* device ID */ #define for_each_opt(opt, long, short) while ((opt=getopt_long(argc, argv, short ? short:"+", long, NULL)) != -1) static volatile int signal_received = 0; static void usage(void); static int str2buf(const char *str, uint8_t *buf, size_t blen) { int i, dlen; if (str == NULL) return -EINVAL; memset(buf, 0, blen); dlen = MIN((strlen(str) / 2), blen); for (i = 0; i < dlen; i++) sscanf(str + (i * 2), "%02hhX", &buf[i]); return 0; } static int dev_info(int s, int dev_id, long arg) { struct hci_dev_info di = { .dev_id = dev_id }; char addr[18]; if (ioctl(s, HCIGETDEVINFO, (void *) &di)) return 0; ba2str(&di.bdaddr, addr); printf("\t%s\t%s\n", di.name, addr); return 0; } static void helper_arg(int min_num_arg, int max_num_arg, int *argc, char ***argv, const char *usage) { *argc -= optind; /* too many arguments, but when "max_num_arg < min_num_arg" then no limiting (prefer "max_num_arg=-1" to gen infinity) */ if ( (*argc > max_num_arg) && (max_num_arg >= min_num_arg ) ) { fprintf(stderr, "%s: too many arguments (maximal: %i)\n", *argv[0], max_num_arg); printf("%s", usage); exit(1); } /* print usage */ if (*argc < min_num_arg) { fprintf(stderr, "%s: too few arguments (minimal: %i)\n", *argv[0], min_num_arg); printf("%s", usage); exit(0); } *argv += optind; } static char *type2str(uint8_t type) { switch (type) { case SCO_LINK: return "SCO"; case ACL_LINK: return "ACL"; case ESCO_LINK: return "eSCO"; case LE_LINK: return "LE"; default: return "Unknown"; } } static int conn_list(int s, int dev_id, long arg) { struct hci_conn_list_req *cl; struct hci_conn_info *ci; int id = arg; int i; if (id != -1 && dev_id != id) return 0; if (!(cl = malloc(10 * sizeof(*ci) + sizeof(*cl)))) { perror("Can't allocate memory"); exit(1); } cl->dev_id = dev_id; cl->conn_num = 10; ci = cl->conn_info; if (ioctl(s, HCIGETCONNLIST, (void *) cl)) { perror("Can't get connection list"); exit(1); } for (i = 0; i < cl->conn_num; i++, ci++) { char addr[18]; char *str; ba2str(&ci->bdaddr, addr); str = hci_lmtostr(ci->link_mode); printf("\t%s %s %s handle %d state %d lm %s\n", ci->out ? "<" : ">", type2str(ci->type), addr, ci->handle, ci->state, str); bt_free(str); } free(cl); return 0; } static int find_conn(int s, int dev_id, long arg) { struct hci_conn_list_req *cl; struct hci_conn_info *ci; int i; if (!(cl = malloc(10 * sizeof(*ci) + sizeof(*cl)))) { perror("Can't allocate memory"); exit(1); } cl->dev_id = dev_id; cl->conn_num = 10; ci = cl->conn_info; if (ioctl(s, HCIGETCONNLIST, (void *) cl)) { perror("Can't get connection list"); exit(1); } for (i = 0; i < cl->conn_num; i++, ci++) if (!bacmp((bdaddr_t *) arg, &ci->bdaddr)) { free(cl); return 1; } free(cl); return 0; } static void hex_dump(char *pref, int width, unsigned char *buf, int len) { register int i,n; for (i = 0, n = 1; i < len; i++, n++) { if (n == 1) printf("%s", pref); printf("%2.2X ", buf[i]); if (n == width) { printf("\n"); n = 0; } } if (i && n!=1) printf("\n"); } static char *get_minor_device_name(int major, int minor) { switch (major) { case 0: /* misc */ return ""; case 1: /* computer */ switch (minor) { case 0: return "Uncategorized"; case 1: return "Desktop workstation"; case 2: return "Server"; case 3: return "Laptop"; case 4: return "Handheld"; case 5: return "Palm"; case 6: return "Wearable"; } break; case 2: /* phone */ switch (minor) { case 0: return "Uncategorized"; case 1: return "Cellular"; case 2: return "Cordless"; case 3: return "Smart phone"; case 4: return "Wired modem or voice gateway"; case 5: return "Common ISDN Access"; case 6: return "Sim Card Reader"; } break; case 3: /* lan access */ if (minor == 0) return "Uncategorized"; switch (minor / 8) { case 0: return "Fully available"; case 1: return "1-17% utilized"; case 2: return "17-33% utilized"; case 3: return "33-50% utilized"; case 4: return "50-67% utilized"; case 5: return "67-83% utilized"; case 6: return "83-99% utilized"; case 7: return "No service available"; } break; case 4: /* audio/video */ switch (minor) { case 0: return "Uncategorized"; case 1: return "Device conforms to the Headset profile"; case 2: return "Hands-free"; /* 3 is reserved */ case 4: return "Microphone"; case 5: return "Loudspeaker"; case 6: return "Headphones"; case 7: return "Portable Audio"; case 8: return "Car Audio"; case 9: return "Set-top box"; case 10: return "HiFi Audio Device"; case 11: return "VCR"; case 12: return "Video Camera"; case 13: return "Camcorder"; case 14: return "Video Monitor"; case 15: return "Video Display and Loudspeaker"; case 16: return "Video Conferencing"; /* 17 is reserved */ case 18: return "Gaming/Toy"; } break; case 5: /* peripheral */ { static char cls_str[48]; cls_str[0] = 0; switch (minor & 48) { case 16: strncpy(cls_str, "Keyboard", sizeof(cls_str)); break; case 32: strncpy(cls_str, "Pointing device", sizeof(cls_str)); break; case 48: strncpy(cls_str, "Combo keyboard/pointing device", sizeof(cls_str)); break; } if ((minor & 15) && (strlen(cls_str) > 0)) strcat(cls_str, "/"); switch (minor & 15) { case 0: break; case 1: strncat(cls_str, "Joystick", sizeof(cls_str) - strlen(cls_str) - 1); break; case 2: strncat(cls_str, "Gamepad", sizeof(cls_str) - strlen(cls_str) - 1); break; case 3: strncat(cls_str, "Remote control", sizeof(cls_str) - strlen(cls_str) - 1); break; case 4: strncat(cls_str, "Sensing device", sizeof(cls_str) - strlen(cls_str) - 1); break; case 5: strncat(cls_str, "Digitizer tablet", sizeof(cls_str) - strlen(cls_str) - 1); break; case 6: strncat(cls_str, "Card reader", sizeof(cls_str) - strlen(cls_str) - 1); break; default: strncat(cls_str, "(reserved)", sizeof(cls_str) - strlen(cls_str) - 1); break; } if (strlen(cls_str) > 0) return cls_str; break; } case 6: /* imaging */ if (minor & 4) return "Display"; if (minor & 8) return "Camera"; if (minor & 16) return "Scanner"; if (minor & 32) return "Printer"; break; case 7: /* wearable */ switch (minor) { case 1: return "Wrist Watch"; case 2: return "Pager"; case 3: return "Jacket"; case 4: return "Helmet"; case 5: return "Glasses"; } break; case 8: /* toy */ switch (minor) { case 1: return "Robot"; case 2: return "Vehicle"; case 3: return "Doll / Action Figure"; case 4: return "Controller"; case 5: return "Game"; } break; case 63: /* uncategorised */ return ""; } return "Unknown (reserved) minor device class"; } static char *major_classes[] = { "Miscellaneous", "Computer", "Phone", "LAN Access", "Audio/Video", "Peripheral", "Imaging", "Uncategorized" }; /* Display local devices */ static struct option dev_options[] = { { "help", 0, 0, 'h' }, {0, 0, 0, 0 } }; static const char *dev_help = "Usage:\n" "\tdev\n"; static void cmd_dev(int dev_id, int argc, char **argv) { int opt; for_each_opt(opt, dev_options, NULL) { switch (opt) { default: printf("%s", dev_help); return; } } helper_arg(0, 0, &argc, &argv, dev_help); printf("Devices:\n"); hci_for_each_dev(HCI_UP, dev_info, 0); } /* Inquiry */ static struct option inq_options[] = { { "help", 0, 0, 'h' }, { "length", 1, 0, 'l' }, { "numrsp", 1, 0, 'n' }, { "iac", 1, 0, 'i' }, { "flush", 0, 0, 'f' }, { 0, 0, 0, 0 } }; static const char *inq_help = "Usage:\n" "\tinq [--length=N] maximum inquiry duration in 1.28 s units\n" "\t [--numrsp=N] specify maximum number of inquiry responses\n" "\t [--iac=lap] specify the inquiry access code\n" "\t [--flush] flush the inquiry cache\n"; static void cmd_inq(int dev_id, int argc, char **argv) { inquiry_info *info = NULL; uint8_t lap[3] = { 0x33, 0x8b, 0x9e }; int num_rsp, length, flags; char addr[18]; int i, l, opt; length = 8; /* ~10 seconds */ num_rsp = 0; flags = 0; for_each_opt(opt, inq_options, NULL) { switch (opt) { case 'l': length = atoi(optarg); break; case 'n': num_rsp = atoi(optarg); break; case 'i': l = strtoul(optarg, 0, 16); if (!strcasecmp(optarg, "giac")) { l = 0x9e8b33; } else if (!strcasecmp(optarg, "liac")) { l = 0x9e8b00; } if (l < 0x9e8b00 || l > 0x9e8b3f) { printf("Invalid access code 0x%x\n", l); exit(1); } lap[0] = (l & 0xff); lap[1] = (l >> 8) & 0xff; lap[2] = (l >> 16) & 0xff; break; case 'f': flags |= IREQ_CACHE_FLUSH; break; default: printf("%s", inq_help); return; } } helper_arg(0, 0, &argc, &argv, inq_help); printf("Inquiring ...\n"); num_rsp = hci_inquiry(dev_id, length, num_rsp, lap, &info, flags); if (num_rsp < 0) { perror("Inquiry failed."); exit(1); } for (i = 0; i < num_rsp; i++) { ba2str(&(info+i)->bdaddr, addr); printf("\t%s\tclock offset: 0x%4.4x\tclass: 0x%2.2x%2.2x%2.2x\n", addr, btohs((info+i)->clock_offset), (info+i)->dev_class[2], (info+i)->dev_class[1], (info+i)->dev_class[0]); } bt_free(info); } /* Device scanning */ static struct option scan_options[] = { { "help", 0, 0, 'h' }, { "length", 1, 0, 'l' }, { "numrsp", 1, 0, 'n' }, { "iac", 1, 0, 'i' }, { "flush", 0, 0, 'f' }, { "class", 0, 0, 'C' }, { "info", 0, 0, 'I' }, { "oui", 0, 0, 'O' }, { "all", 0, 0, 'A' }, { "ext", 0, 0, 'A' }, { 0, 0, 0, 0 } }; static const char *scan_help = "Usage:\n" "\tscan [--length=N] [--numrsp=N] [--iac=lap] [--flush] [--class] [--info] [--oui] [--refresh]\n"; static void cmd_scan(int dev_id, int argc, char **argv) { inquiry_info *info = NULL; uint8_t lap[3] = { 0x33, 0x8b, 0x9e }; int num_rsp, length, flags; uint8_t cls[3], features[8]; char addr[18], name[249], *comp; struct hci_version version; struct hci_dev_info di; struct hci_conn_info_req *cr; int extcls = 0, extinf = 0, extoui = 0; int i, n, l, opt, dd, cc; length = 8; /* ~10 seconds */ num_rsp = 0; flags = 0; for_each_opt(opt, scan_options, NULL) { switch (opt) { case 'l': length = atoi(optarg); break; case 'n': num_rsp = atoi(optarg); break; case 'i': l = strtoul(optarg, 0, 16); if (!strcasecmp(optarg, "giac")) { l = 0x9e8b33; } else if (!strcasecmp(optarg, "liac")) { l = 0x9e8b00; } else if (l < 0x9e8b00 || l > 0x9e8b3f) { printf("Invalid access code 0x%x\n", l); exit(1); } lap[0] = (l & 0xff); lap[1] = (l >> 8) & 0xff; lap[2] = (l >> 16) & 0xff; break; case 'f': flags |= IREQ_CACHE_FLUSH; break; case 'C': extcls = 1; break; case 'I': extinf = 1; break; case 'O': extoui = 1; break; case 'A': extcls = 1; extinf = 1; extoui = 1; break; default: printf("%s", scan_help); return; } } helper_arg(0, 0, &argc, &argv, scan_help); if (dev_id < 0) { dev_id = hci_get_route(NULL); if (dev_id < 0) { perror("Device is not available"); exit(1); } } if (hci_devinfo(dev_id, &di) < 0) { perror("Can't get device info"); exit(1); } printf("Scanning ...\n"); num_rsp = hci_inquiry(dev_id, length, num_rsp, lap, &info, flags); if (num_rsp < 0) { perror("Inquiry failed"); exit(1); } dd = hci_open_dev(dev_id); if (dd < 0) { perror("HCI device open failed"); free(info); exit(1); } if (extcls || extinf || extoui) printf("\n"); for (i = 0; i < num_rsp; i++) { uint16_t handle = 0; if (!extcls && !extinf && !extoui) { ba2str(&(info+i)->bdaddr, addr); if (hci_read_remote_name_with_clock_offset(dd, &(info+i)->bdaddr, (info+i)->pscan_rep_mode, (info+i)->clock_offset | 0x8000, sizeof(name), name, 100000) < 0) strcpy(name, "n/a"); for (n = 0; n < 248 && name[n]; n++) { if ((unsigned char) name[i] < 32 || name[i] == 127) name[i] = '.'; } name[248] = '\0'; printf("\t%s\t%s\n", addr, name); continue; } ba2str(&(info+i)->bdaddr, addr); printf("BD Address:\t%s [mode %d, clkoffset 0x%4.4x]\n", addr, (info+i)->pscan_rep_mode, btohs((info+i)->clock_offset)); if (extoui) { comp = batocomp(&(info+i)->bdaddr); if (comp) { char oui[9]; ba2oui(&(info+i)->bdaddr, oui); printf("OUI company:\t%s (%s)\n", comp, oui); free(comp); } } cc = 0; if (extinf) { cr = malloc(sizeof(*cr) + sizeof(struct hci_conn_info)); if (cr) { bacpy(&cr->bdaddr, &(info+i)->bdaddr); cr->type = ACL_LINK; if (ioctl(dd, HCIGETCONNINFO, (unsigned long) cr) < 0) { handle = 0; cc = 1; } else { handle = htobs(cr->conn_info->handle); cc = 0; } free(cr); } if (cc) { if (hci_create_connection(dd, &(info+i)->bdaddr, htobs(di.pkt_type & ACL_PTYPE_MASK), (info+i)->clock_offset | 0x8000, 0x01, &handle, 25000) < 0) { handle = 0; cc = 0; } } } if (hci_read_remote_name_with_clock_offset(dd, &(info+i)->bdaddr, (info+i)->pscan_rep_mode, (info+i)->clock_offset | 0x8000, sizeof(name), name, 100000) < 0) { } else { for (n = 0; n < 248 && name[n]; n++) { if ((unsigned char) name[i] < 32 || name[i] == 127) name[i] = '.'; } name[248] = '\0'; } if (strlen(name) > 0) printf("Device name:\t%s\n", name); if (extcls) { memcpy(cls, (info+i)->dev_class, 3); printf("Device class:\t"); if ((cls[1] & 0x1f) > sizeof(major_classes) / sizeof(char *)) printf("Invalid"); else printf("%s, %s", major_classes[cls[1] & 0x1f], get_minor_device_name(cls[1] & 0x1f, cls[0] >> 2)); printf(" (0x%2.2x%2.2x%2.2x)\n", cls[2], cls[1], cls[0]); } if (extinf && handle > 0) { if (hci_read_remote_version(dd, handle, &version, 20000) == 0) { char *ver = lmp_vertostr(version.lmp_ver); printf("Manufacturer:\t%s (%d)\n", bt_compidtostr(version.manufacturer), version.manufacturer); printf("LMP version:\t%s (0x%x) [subver 0x%x]\n", ver ? ver : "n/a", version.lmp_ver, version.lmp_subver); if (ver) bt_free(ver); } if (hci_read_remote_features(dd, handle, features, 20000) == 0) { char *tmp = lmp_featurestostr(features, "\t\t", 63); printf("LMP features:\t0x%2.2x 0x%2.2x 0x%2.2x 0x%2.2x" " 0x%2.2x 0x%2.2x 0x%2.2x 0x%2.2x\n", features[0], features[1], features[2], features[3], features[4], features[5], features[6], features[7]); printf("%s\n", tmp); bt_free(tmp); } if (cc) { usleep(10000); hci_disconnect(dd, handle, HCI_OE_USER_ENDED_CONNECTION, 10000); } } printf("\n"); } bt_free(info); hci_close_dev(dd); } /* Remote name */ static struct option name_options[] = { { "help", 0, 0, 'h' }, { 0, 0, 0, 0 } }; static const char *name_help = "Usage:\n" "\tname \n"; static void cmd_name(int dev_id, int argc, char **argv) { bdaddr_t bdaddr; char name[248]; int opt, dd; for_each_opt(opt, name_options, NULL) { switch (opt) { default: printf("%s", name_help); return; } } helper_arg(1, 1, &argc, &argv, name_help); str2ba(argv[0], &bdaddr); if (dev_id < 0) { dev_id = hci_get_route(&bdaddr); if (dev_id < 0) { fprintf(stderr, "Device is not available.\n"); exit(1); } } dd = hci_open_dev(dev_id); if (dd < 0) { perror("HCI device open failed"); exit(1); } if (hci_read_remote_name(dd, &bdaddr, sizeof(name), name, 25000) == 0) printf("%s\n", name); hci_close_dev(dd); } /* Info about remote device */ static struct option info_options[] = { { "help", 0, 0, 'h' }, { 0, 0, 0, 0 } }; static const char *info_help = "Usage:\n" "\tinfo \n"; static void cmd_info(int dev_id, int argc, char **argv) { bdaddr_t bdaddr; uint16_t handle; uint8_t features[8], max_page = 0; char name[249], *comp, *tmp; struct hci_version version; struct hci_dev_info di; struct hci_conn_info_req *cr; int i, opt, dd, cc = 0; for_each_opt(opt, info_options, NULL) { switch (opt) { default: printf("%s", info_help); return; } } helper_arg(1, 1, &argc, &argv, info_help); str2ba(argv[0], &bdaddr); if (dev_id < 0) dev_id = hci_for_each_dev(HCI_UP, find_conn, (long) &bdaddr); if (dev_id < 0) dev_id = hci_get_route(&bdaddr); if (dev_id < 0) { fprintf(stderr, "Device is not available or not connected.\n"); exit(1); } if (hci_devinfo(dev_id, &di) < 0) { perror("Can't get device info"); exit(1); } printf("Requesting information ...\n"); dd = hci_open_dev(dev_id); if (dd < 0) { perror("HCI device open failed"); exit(1); } cr = malloc(sizeof(*cr) + sizeof(struct hci_conn_info)); if (!cr) { perror("Can't get connection info"); close(dd); exit(1); } bacpy(&cr->bdaddr, &bdaddr); cr->type = ACL_LINK; if (ioctl(dd, HCIGETCONNINFO, (unsigned long) cr) < 0) { if (hci_create_connection(dd, &bdaddr, htobs(di.pkt_type & ACL_PTYPE_MASK), 0, 0x01, &handle, 25000) < 0) { perror("Can't create connection"); free(cr); close(dd); exit(1); } sleep(1); cc = 1; } else handle = htobs(cr->conn_info->handle); free(cr); printf("\tBD Address: %s\n", argv[0]); comp = batocomp(&bdaddr); if (comp) { char oui[9]; ba2oui(&bdaddr, oui); printf("\tOUI Company: %s (%s)\n", comp, oui); free(comp); } if (hci_read_remote_name(dd, &bdaddr, sizeof(name), name, 25000) == 0) printf("\tDevice Name: %s\n", name); if (hci_read_remote_version(dd, handle, &version, 20000) == 0) { char *ver = lmp_vertostr(version.lmp_ver); printf("\tLMP Version: %s (0x%x) LMP Subversion: 0x%x\n" "\tManufacturer: %s (%d)\n", ver ? ver : "n/a", version.lmp_ver, version.lmp_subver, bt_compidtostr(version.manufacturer), version.manufacturer); if (ver) bt_free(ver); } memset(features, 0, sizeof(features)); hci_read_remote_features(dd, handle, features, 20000); if ((di.features[7] & LMP_EXT_FEAT) && (features[7] & LMP_EXT_FEAT)) hci_read_remote_ext_features(dd, handle, 0, &max_page, features, 20000); if (max_page < 1 && (features[6] & LMP_SIMPLE_PAIR)) max_page = 1; printf("\tFeatures%s: 0x%2.2x 0x%2.2x 0x%2.2x 0x%2.2x " "0x%2.2x 0x%2.2x 0x%2.2x 0x%2.2x\n", (max_page > 0) ? " page 0" : "", features[0], features[1], features[2], features[3], features[4], features[5], features[6], features[7]); tmp = lmp_featurestostr(features, "\t\t", 63); printf("%s\n", tmp); bt_free(tmp); for (i = 1; i <= max_page; i++) { if (hci_read_remote_ext_features(dd, handle, i, NULL, features, 20000) < 0) continue; printf("\tFeatures page %d: 0x%2.2x 0x%2.2x 0x%2.2x 0x%2.2x " "0x%2.2x 0x%2.2x 0x%2.2x 0x%2.2x\n", i, features[0], features[1], features[2], features[3], features[4], features[5], features[6], features[7]); } if (cc) { usleep(10000); hci_disconnect(dd, handle, HCI_OE_USER_ENDED_CONNECTION, 10000); } hci_close_dev(dd); } /* Start periodic inquiry */ static struct option spinq_options[] = { { "help", 0, 0, 'h' }, { 0, 0, 0, 0 } }; static const char *spinq_help = "Usage:\n" "\tspinq\n"; static void cmd_spinq(int dev_id, int argc, char **argv) { uint8_t lap[3] = { 0x33, 0x8b, 0x9e }; struct hci_request rq; periodic_inquiry_cp cp; int opt, dd; for_each_opt(opt, spinq_options, NULL) { switch (opt) { default: printf("%s", spinq_help); return; } } helper_arg(0, 0, &argc, &argv, spinq_help); if (dev_id < 0) dev_id = hci_get_route(NULL); dd = hci_open_dev(dev_id); if (dd < 0) { perror("Device open failed"); exit(EXIT_FAILURE); } memset(&cp, 0, sizeof(cp)); memcpy(cp.lap, lap, 3); cp.max_period = htobs(16); cp.min_period = htobs(10); cp.length = 8; cp.num_rsp = 0; memset(&rq, 0, sizeof(rq)); rq.ogf = OGF_LINK_CTL; rq.ocf = OCF_PERIODIC_INQUIRY; rq.cparam = &cp; rq.clen = PERIODIC_INQUIRY_CP_SIZE; if (hci_send_req(dd, &rq, 100) < 0) { perror("Periodic inquiry failed"); exit(EXIT_FAILURE); } hci_close_dev(dd); } /* Exit periodic inquiry */ static struct option epinq_options[] = { { "help", 0, 0, 'h' }, { 0, 0, 0, 0 } }; static const char *epinq_help = "Usage:\n" "\tepinq\n"; static void cmd_epinq(int dev_id, int argc, char **argv) { int opt, dd; for_each_opt(opt, epinq_options, NULL) { switch (opt) { default: printf("%s", epinq_help); return; } } helper_arg(0, 0, &argc, &argv, epinq_help); if (dev_id < 0) dev_id = hci_get_route(NULL); dd = hci_open_dev(dev_id); if (dd < 0) { perror("Device open failed"); exit(EXIT_FAILURE); } if (hci_send_cmd(dd, OGF_LINK_CTL, OCF_EXIT_PERIODIC_INQUIRY, 0, NULL) < 0) { perror("Exit periodic inquiry failed"); exit(EXIT_FAILURE); } hci_close_dev(dd); } /* Send arbitrary HCI commands */ static struct option cmd_options[] = { { "help", 0, 0, 'h' }, { 0, 0, 0, 0 } }; static const char *cmd_help = "Usage:\n" "\tcmd [parameters]\n" "Example:\n" "\tcmd 0x03 0x0013 0x41 0x42 0x43 0x44\n"; static void cmd_cmd(int dev_id, int argc, char **argv) { unsigned char buf[HCI_MAX_EVENT_SIZE], *ptr = buf; struct hci_filter flt; hci_event_hdr *hdr; int i, opt, len, dd; uint16_t ocf; uint8_t ogf; for_each_opt(opt, cmd_options, NULL) { switch (opt) { default: printf("%s", cmd_help); return; } } helper_arg(2, -1, &argc, &argv, cmd_help); if (dev_id < 0) dev_id = hci_get_route(NULL); errno = 0; ogf = strtol(argv[0], NULL, 16); ocf = strtol(argv[1], NULL, 16); if (errno == ERANGE || (ogf > 0x3f) || (ocf > 0x3ff)) { printf("%s", cmd_help); return; } for (i = 2, len = 0; i < argc && len < (int) sizeof(buf); i++, len++) *ptr++ = (uint8_t) strtol(argv[i], NULL, 16); dd = hci_open_dev(dev_id); if (dd < 0) { perror("Device open failed"); exit(EXIT_FAILURE); } /* Setup filter */ hci_filter_clear(&flt); hci_filter_set_ptype(HCI_EVENT_PKT, &flt); hci_filter_all_events(&flt); if (setsockopt(dd, SOL_HCI, HCI_FILTER, &flt, sizeof(flt)) < 0) { perror("HCI filter setup failed"); exit(EXIT_FAILURE); } printf("< HCI Command: ogf 0x%02x, ocf 0x%04x, plen %d\n", ogf, ocf, len); hex_dump(" ", 20, buf, len); fflush(stdout); if (hci_send_cmd(dd, ogf, ocf, len, buf) < 0) { perror("Send failed"); exit(EXIT_FAILURE); } len = read(dd, buf, sizeof(buf)); if (len < 0) { perror("Read failed"); exit(EXIT_FAILURE); } hdr = (void *)(buf + 1); ptr = buf + (1 + HCI_EVENT_HDR_SIZE); len -= (1 + HCI_EVENT_HDR_SIZE); printf("> HCI Event: 0x%02x plen %d\n", hdr->evt, hdr->plen); hex_dump(" ", 20, ptr, len); fflush(stdout); hci_close_dev(dd); } /* Display active connections */ static struct option con_options[] = { { "help", 0, 0, 'h' }, { 0, 0, 0, 0 } }; static const char *con_help = "Usage:\n" "\tcon\n"; static void cmd_con(int dev_id, int argc, char **argv) { int opt; for_each_opt(opt, con_options, NULL) { switch (opt) { default: printf("%s", con_help); return; } } helper_arg(0, 0, &argc, &argv, con_help); printf("Connections:\n"); hci_for_each_dev(HCI_UP, conn_list, dev_id); } /* Create connection */ static struct option cc_options[] = { { "help", 0, 0, 'h' }, { "role", 1, 0, 'r' }, { "ptype", 1, 0, 'p' }, { 0, 0, 0, 0 } }; static const char *cc_help = "Usage:\n" "\tcc [--role=c|p] [--ptype=pkt_types] \n" "Example:\n" "\tcc --ptype=dm1,dh3,dh5 01:02:03:04:05:06\n" "\tcc --role=c 01:02:03:04:05:06\n"; static void cmd_cc(int dev_id, int argc, char **argv) { bdaddr_t bdaddr; uint16_t handle; uint8_t role; unsigned int ptype; int dd, opt; role = 0x01; ptype = HCI_DM1 | HCI_DM3 | HCI_DM5 | HCI_DH1 | HCI_DH3 | HCI_DH5; for_each_opt(opt, cc_options, NULL) { switch (opt) { case 'p': hci_strtoptype(optarg, &ptype); break; case 'r': role = optarg[0] == 'm' || optarg[0] == 'c' ? 0 : 1; break; default: printf("%s", cc_help); return; } } helper_arg(1, 1, &argc, &argv, cc_help); str2ba(argv[0], &bdaddr); if (dev_id < 0) { dev_id = hci_get_route(&bdaddr); if (dev_id < 0) { fprintf(stderr, "Device is not available.\n"); exit(1); } } dd = hci_open_dev(dev_id); if (dd < 0) { perror("HCI device open failed"); exit(1); } if (hci_create_connection(dd, &bdaddr, htobs(ptype), htobs(0x0000), role, &handle, 25000) < 0) perror("Can't create connection"); hci_close_dev(dd); } /* Close connection */ static struct option dc_options[] = { { "help", 0, 0, 'h' }, { 0, 0, 0, 0 } }; static const char *dc_help = "Usage:\n" "\tdc [reason]\n"; static void cmd_dc(int dev_id, int argc, char **argv) { struct hci_conn_info_req *cr; bdaddr_t bdaddr; uint8_t reason; int opt, dd; for_each_opt(opt, dc_options, NULL) { switch (opt) { default: printf("%s", dc_help); return; } } helper_arg(1, 2, &argc, &argv, dc_help); str2ba(argv[0], &bdaddr); reason = (argc > 1) ? atoi(argv[1]) : HCI_OE_USER_ENDED_CONNECTION; if (dev_id < 0) { dev_id = hci_for_each_dev(HCI_UP, find_conn, (long) &bdaddr); if (dev_id < 0) { fprintf(stderr, "Not connected.\n"); exit(1); } } dd = hci_open_dev(dev_id); if (dd < 0) { perror("HCI device open failed"); exit(1); } cr = malloc(sizeof(*cr) + sizeof(struct hci_conn_info)); if (!cr) { perror("Can't allocate memory"); exit(1); } bacpy(&cr->bdaddr, &bdaddr); cr->type = ACL_LINK; if (ioctl(dd, HCIGETCONNINFO, (unsigned long) cr) < 0) { perror("Get connection info failed"); exit(1); } if (hci_disconnect(dd, htobs(cr->conn_info->handle), reason, 10000) < 0) perror("Disconnect failed"); free(cr); hci_close_dev(dd); } /* Role switch */ static struct option sr_options[] = { { "help", 0, 0, 'h' }, { 0, 0, 0, 0 } }; static const char *sr_help = "Usage:\n" "\tsr \n"; static void cmd_sr(int dev_id, int argc, char **argv) { bdaddr_t bdaddr; uint8_t role; int opt, dd; for_each_opt(opt, sr_options, NULL) { switch (opt) { default: printf("%s", sr_help); return; } } helper_arg(2, 2, &argc, &argv, sr_help); str2ba(argv[0], &bdaddr); switch (argv[1][0]) { case 'm': /* Deprecated. Kept for compatibility. */ case 'c': role = 0; break; case 's': /* Deprecated. Kept for compatibility. */ case 'p': role = 1; break; default: role = atoi(argv[1]); break; } if (dev_id < 0) { dev_id = hci_for_each_dev(HCI_UP, find_conn, (long) &bdaddr); if (dev_id < 0) { fprintf(stderr, "Not connected.\n"); exit(1); } } dd = hci_open_dev(dev_id); if (dd < 0) { perror("HCI device open failed"); exit(1); } if (hci_switch_role(dd, &bdaddr, role, 10000) < 0) { perror("Switch role request failed"); exit(1); } hci_close_dev(dd); } /* Read RSSI */ static struct option rssi_options[] = { { "help", 0, 0, 'h' }, { 0, 0, 0, 0 } }; static const char *rssi_help = "Usage:\n" "\trssi \n"; static void cmd_rssi(int dev_id, int argc, char **argv) { struct hci_conn_info_req *cr; bdaddr_t bdaddr; int8_t rssi; int opt, dd; for_each_opt(opt, rssi_options, NULL) { switch (opt) { default: printf("%s", rssi_help); return; } } helper_arg(1, 1, &argc, &argv, rssi_help); str2ba(argv[0], &bdaddr); if (dev_id < 0) { dev_id = hci_for_each_dev(HCI_UP, find_conn, (long) &bdaddr); if (dev_id < 0) { fprintf(stderr, "Not connected.\n"); exit(1); } } dd = hci_open_dev(dev_id); if (dd < 0) { perror("HCI device open failed"); exit(1); } cr = malloc(sizeof(*cr) + sizeof(struct hci_conn_info)); if (!cr) { perror("Can't allocate memory"); exit(1); } bacpy(&cr->bdaddr, &bdaddr); cr->type = ACL_LINK; if (ioctl(dd, HCIGETCONNINFO, (unsigned long) cr) < 0) { perror("Get connection info failed"); exit(1); } if (hci_read_rssi(dd, htobs(cr->conn_info->handle), &rssi, 1000) < 0) { perror("Read RSSI failed"); exit(1); } printf("RSSI return value: %d\n", rssi); free(cr); hci_close_dev(dd); } /* Get link quality */ static struct option lq_options[] = { { "help", 0, 0, 'h' }, { 0, 0, 0, 0 } }; static const char *lq_help = "Usage:\n" "\tlq \n"; static void cmd_lq(int dev_id, int argc, char **argv) { struct hci_conn_info_req *cr; bdaddr_t bdaddr; uint8_t lq; int opt, dd; for_each_opt(opt, lq_options, NULL) { switch (opt) { default: printf("%s", lq_help); return; } } helper_arg(1, 1, &argc, &argv, lq_help); str2ba(argv[0], &bdaddr); if (dev_id < 0) { dev_id = hci_for_each_dev(HCI_UP, find_conn, (long) &bdaddr); if (dev_id < 0) { fprintf(stderr, "Not connected.\n"); exit(1); } } dd = hci_open_dev(dev_id); if (dd < 0) { perror("HCI device open failed"); exit(1); } cr = malloc(sizeof(*cr) + sizeof(struct hci_conn_info)); if (!cr) { perror("Can't allocate memory"); exit(1); } bacpy(&cr->bdaddr, &bdaddr); cr->type = ACL_LINK; if (ioctl(dd, HCIGETCONNINFO, (unsigned long) cr) < 0) { perror("Get connection info failed"); exit(1); } if (hci_read_link_quality(dd, htobs(cr->conn_info->handle), &lq, 1000) < 0) { perror("HCI read_link_quality request failed"); exit(1); } printf("Link quality: %d\n", lq); free(cr); hci_close_dev(dd); } /* Get transmit power level */ static struct option tpl_options[] = { { "help", 0, 0, 'h' }, { 0, 0, 0, 0 } }; static const char *tpl_help = "Usage:\n" "\ttpl [type]\n"; static void cmd_tpl(int dev_id, int argc, char **argv) { struct hci_conn_info_req *cr; bdaddr_t bdaddr; uint8_t type; int8_t level; int opt, dd; for_each_opt(opt, tpl_options, NULL) { switch (opt) { default: printf("%s", tpl_help); return; } } helper_arg(1, 2, &argc, &argv, tpl_help); str2ba(argv[0], &bdaddr); type = (argc > 1) ? atoi(argv[1]) : 0; if (dev_id < 0) { dev_id = hci_for_each_dev(HCI_UP, find_conn, (long) &bdaddr); if (dev_id < 0) { fprintf(stderr, "Not connected.\n"); exit(1); } } dd = hci_open_dev(dev_id); if (dd < 0) { perror("HCI device open failed"); exit(1); } cr = malloc(sizeof(*cr) + sizeof(struct hci_conn_info)); if (!cr) { perror("Can't allocate memory"); exit(1); } bacpy(&cr->bdaddr, &bdaddr); cr->type = ACL_LINK; if (ioctl(dd, HCIGETCONNINFO, (unsigned long) cr) < 0) { perror("Get connection info failed"); exit(1); } if (hci_read_transmit_power_level(dd, htobs(cr->conn_info->handle), type, &level, 1000) < 0) { perror("HCI read transmit power level request failed"); exit(1); } printf("%s transmit power level: %d\n", (type == 0) ? "Current" : "Maximum", level); free(cr); hci_close_dev(dd); } /* Get AFH channel map */ static struct option afh_options[] = { { "help", 0, 0, 'h' }, { 0, 0, 0, 0 } }; static const char *afh_help = "Usage:\n" "\tafh \n"; static void cmd_afh(int dev_id, int argc, char **argv) { struct hci_conn_info_req *cr; bdaddr_t bdaddr; uint16_t handle; uint8_t mode, map[10]; int opt, dd; for_each_opt(opt, afh_options, NULL) { switch (opt) { default: printf("%s", afh_help); return; } } helper_arg(1, 1, &argc, &argv, afh_help); str2ba(argv[0], &bdaddr); if (dev_id < 0) { dev_id = hci_for_each_dev(HCI_UP, find_conn, (long) &bdaddr); if (dev_id < 0) { fprintf(stderr, "Not connected.\n"); exit(1); } } dd = hci_open_dev(dev_id); if (dd < 0) { perror("HCI device open failed"); exit(1); } cr = malloc(sizeof(*cr) + sizeof(struct hci_conn_info)); if (!cr) { perror("Can't allocate memory"); exit(1); } bacpy(&cr->bdaddr, &bdaddr); cr->type = ACL_LINK; if (ioctl(dd, HCIGETCONNINFO, (unsigned long) cr) < 0) { perror("Get connection info failed"); exit(1); } handle = htobs(cr->conn_info->handle); if (hci_read_afh_map(dd, handle, &mode, map, 1000) < 0) { perror("HCI read AFH map request failed"); exit(1); } if (mode == 0x01) { int i; printf("AFH map: 0x"); for (i = 0; i < 10; i++) printf("%02x", map[i]); printf("\n"); } else printf("AFH disabled\n"); free(cr); hci_close_dev(dd); } /* Set connection packet type */ static struct option cpt_options[] = { { "help", 0, 0, 'h' }, { 0, 0, 0, 0 } }; static const char *cpt_help = "Usage:\n" "\tcpt \n"; static void cmd_cpt(int dev_id, int argc, char **argv) { struct hci_conn_info_req *cr; struct hci_request rq; set_conn_ptype_cp cp; evt_conn_ptype_changed rp; bdaddr_t bdaddr; unsigned int ptype; int dd, opt; for_each_opt(opt, cpt_options, NULL) { switch (opt) { default: printf("%s", cpt_help); return; } } helper_arg(2, 2, &argc, &argv, cpt_help); str2ba(argv[0], &bdaddr); hci_strtoptype(argv[1], &ptype); if (dev_id < 0) { dev_id = hci_for_each_dev(HCI_UP, find_conn, (long) &bdaddr); if (dev_id < 0) { fprintf(stderr, "Not connected.\n"); exit(1); } } dd = hci_open_dev(dev_id); if (dd < 0) { perror("HCI device open failed"); exit(1); } cr = malloc(sizeof(*cr) + sizeof(struct hci_conn_info)); if (!cr) { perror("Can't allocate memory"); exit(1); } bacpy(&cr->bdaddr, &bdaddr); cr->type = ACL_LINK; if (ioctl(dd, HCIGETCONNINFO, (unsigned long) cr) < 0) { perror("Get connection info failed"); exit(1); } cp.handle = htobs(cr->conn_info->handle); cp.pkt_type = ptype; memset(&rq, 0, sizeof(rq)); rq.ogf = OGF_LINK_CTL; rq.ocf = OCF_SET_CONN_PTYPE; rq.cparam = &cp; rq.clen = SET_CONN_PTYPE_CP_SIZE; rq.rparam = &rp; rq.rlen = EVT_CONN_PTYPE_CHANGED_SIZE; rq.event = EVT_CONN_PTYPE_CHANGED; if (hci_send_req(dd, &rq, 100) < 0) { perror("Packet type change failed"); exit(1); } free(cr); hci_close_dev(dd); } /* Get/Set link policy settings */ static struct option lp_options[] = { { "help", 0, 0, 'h' }, { 0, 0, 0, 0 } }; static const char *lp_help = "Usage:\n" "\tlp [link policy]\n"; static void cmd_lp(int dev_id, int argc, char **argv) { struct hci_conn_info_req *cr; bdaddr_t bdaddr; uint16_t policy; int opt, dd; for_each_opt(opt, lp_options, NULL) { switch (opt) { default: printf("%s", lp_help); return; } } helper_arg(1, 2, &argc, &argv, lp_help); str2ba(argv[0], &bdaddr); if (dev_id < 0) { dev_id = hci_for_each_dev(HCI_UP, find_conn, (long) &bdaddr); if (dev_id < 0) { fprintf(stderr, "Not connected.\n"); exit(1); } } dd = hci_open_dev(dev_id); if (dd < 0) { perror("HCI device open failed"); exit(1); } cr = malloc(sizeof(*cr) + sizeof(struct hci_conn_info)); if (!cr) { perror("Can't allocate memory"); exit(1); } bacpy(&cr->bdaddr, &bdaddr); cr->type = ACL_LINK; if (ioctl(dd, HCIGETCONNINFO, (unsigned long) cr) < 0) { perror("Get connection info failed"); exit(1); } if (argc == 1) { char *str; if (hci_read_link_policy(dd, htobs(cr->conn_info->handle), &policy, 1000) < 0) { perror("HCI read_link_policy_settings request failed"); exit(1); } policy = btohs(policy); str = hci_lptostr(policy); if (str) { printf("Link policy settings: %s\n", str); bt_free(str); } else { fprintf(stderr, "Invalig settings\n"); exit(1); } } else { unsigned int val; if (hci_strtolp(argv[1], &val) < 0) { fprintf(stderr, "Invalig arguments\n"); exit(1); } policy = val; if (hci_write_link_policy(dd, htobs(cr->conn_info->handle), htobs(policy), 1000) < 0) { perror("HCI write_link_policy_settings request failed"); exit(1); } } free(cr); hci_close_dev(dd); } /* Get/Set link supervision timeout */ static struct option lst_options[] = { { "help", 0, 0, 'h' }, { 0, 0, 0, 0 } }; static const char *lst_help = "Usage:\n" "\tlst [new value in slots]\n"; static void cmd_lst(int dev_id, int argc, char **argv) { struct hci_conn_info_req *cr; bdaddr_t bdaddr; uint16_t timeout; int opt, dd; for_each_opt(opt, lst_options, NULL) { switch (opt) { default: printf("%s", lst_help); return; } } helper_arg(1, 2, &argc, &argv, lst_help); str2ba(argv[0], &bdaddr); if (dev_id < 0) { dev_id = hci_for_each_dev(HCI_UP, find_conn, (long) &bdaddr); if (dev_id < 0) { fprintf(stderr, "Not connected.\n"); exit(1); } } dd = hci_open_dev(dev_id); if (dd < 0) { perror("HCI device open failed"); exit(1); } cr = malloc(sizeof(*cr) + sizeof(struct hci_conn_info)); if (!cr) { perror("Can't allocate memory"); exit(1); } bacpy(&cr->bdaddr, &bdaddr); cr->type = ACL_LINK; if (ioctl(dd, HCIGETCONNINFO, (unsigned long) cr) < 0) { perror("Get connection info failed"); exit(1); } if (argc == 1) { if (hci_read_link_supervision_timeout(dd, htobs(cr->conn_info->handle), &timeout, 1000) < 0) { perror("HCI read_link_supervision_timeout request failed"); exit(1); } timeout = btohs(timeout); if (timeout) printf("Link supervision timeout: %u slots (%.2f msec)\n", timeout, (float) timeout * 0.625); else printf("Link supervision timeout never expires\n"); } else { timeout = strtol(argv[1], NULL, 10); if (hci_write_link_supervision_timeout(dd, htobs(cr->conn_info->handle), htobs(timeout), 1000) < 0) { perror("HCI write_link_supervision_timeout request failed"); exit(1); } } free(cr); hci_close_dev(dd); } /* Request authentication */ static struct option auth_options[] = { { "help", 0, 0, 'h' }, { 0, 0, 0, 0 } }; static const char *auth_help = "Usage:\n" "\tauth \n"; static void cmd_auth(int dev_id, int argc, char **argv) { struct hci_conn_info_req *cr; bdaddr_t bdaddr; int opt, dd; for_each_opt(opt, auth_options, NULL) { switch (opt) { default: printf("%s", auth_help); return; } } helper_arg(1, 1, &argc, &argv, auth_help); str2ba(argv[0], &bdaddr); if (dev_id < 0) { dev_id = hci_for_each_dev(HCI_UP, find_conn, (long) &bdaddr); if (dev_id < 0) { fprintf(stderr, "Not connected.\n"); exit(1); } } dd = hci_open_dev(dev_id); if (dd < 0) { perror("HCI device open failed"); exit(1); } cr = malloc(sizeof(*cr) + sizeof(struct hci_conn_info)); if (!cr) { perror("Can't allocate memory"); exit(1); } bacpy(&cr->bdaddr, &bdaddr); cr->type = ACL_LINK; if (ioctl(dd, HCIGETCONNINFO, (unsigned long) cr) < 0) { perror("Get connection info failed"); exit(1); } if (hci_authenticate_link(dd, htobs(cr->conn_info->handle), 25000) < 0) { perror("HCI authentication request failed"); exit(1); } free(cr); hci_close_dev(dd); } /* Activate encryption */ static struct option enc_options[] = { { "help", 0, 0, 'h' }, { 0, 0, 0, 0 } }; static const char *enc_help = "Usage:\n" "\tenc [encrypt enable]\n"; static void cmd_enc(int dev_id, int argc, char **argv) { struct hci_conn_info_req *cr; bdaddr_t bdaddr; uint8_t encrypt; int opt, dd; for_each_opt(opt, enc_options, NULL) { switch (opt) { default: printf("%s", enc_help); return; } } helper_arg(1, 2, &argc, &argv, enc_help); str2ba(argv[0], &bdaddr); if (dev_id < 0) { dev_id = hci_for_each_dev(HCI_UP, find_conn, (long) &bdaddr); if (dev_id < 0) { fprintf(stderr, "Not connected.\n"); exit(1); } } dd = hci_open_dev(dev_id); if (dd < 0) { perror("HCI device open failed"); exit(1); } cr = malloc(sizeof(*cr) + sizeof(struct hci_conn_info)); if (!cr) { perror("Can't allocate memory"); exit(1); } bacpy(&cr->bdaddr, &bdaddr); cr->type = ACL_LINK; if (ioctl(dd, HCIGETCONNINFO, (unsigned long) cr) < 0) { perror("Get connection info failed"); exit(1); } encrypt = (argc > 1) ? atoi(argv[1]) : 1; if (hci_encrypt_link(dd, htobs(cr->conn_info->handle), encrypt, 25000) < 0) { perror("HCI set encryption request failed"); exit(1); } free(cr); hci_close_dev(dd); } /* Change connection link key */ static struct option key_options[] = { { "help", 0, 0, 'h' }, { 0, 0, 0, 0 } }; static const char *key_help = "Usage:\n" "\tkey \n"; static void cmd_key(int dev_id, int argc, char **argv) { struct hci_conn_info_req *cr; bdaddr_t bdaddr; int opt, dd; for_each_opt(opt, key_options, NULL) { switch (opt) { default: printf("%s", key_help); return; } } helper_arg(1, 1, &argc, &argv, key_help); str2ba(argv[0], &bdaddr); if (dev_id < 0) { dev_id = hci_for_each_dev(HCI_UP, find_conn, (long) &bdaddr); if (dev_id < 0) { fprintf(stderr, "Not connected.\n"); exit(1); } } dd = hci_open_dev(dev_id); if (dd < 0) { perror("HCI device open failed"); exit(1); } cr = malloc(sizeof(*cr) + sizeof(struct hci_conn_info)); if (!cr) { perror("Can't allocate memory"); exit(1); } bacpy(&cr->bdaddr, &bdaddr); cr->type = ACL_LINK; if (ioctl(dd, HCIGETCONNINFO, (unsigned long) cr) < 0) { perror("Get connection info failed"); exit(1); } if (hci_change_link_key(dd, htobs(cr->conn_info->handle), 25000) < 0) { perror("Changing link key failed"); exit(1); } free(cr); hci_close_dev(dd); } /* Read clock offset */ static struct option clkoff_options[] = { { "help", 0, 0, 'h' }, { 0, 0, 0, 0 } }; static const char *clkoff_help = "Usage:\n" "\tclkoff \n"; static void cmd_clkoff(int dev_id, int argc, char **argv) { struct hci_conn_info_req *cr; bdaddr_t bdaddr; uint16_t offset; int opt, dd; for_each_opt(opt, clkoff_options, NULL) { switch (opt) { default: printf("%s", clkoff_help); return; } } helper_arg(1, 1, &argc, &argv, clkoff_help); str2ba(argv[0], &bdaddr); if (dev_id < 0) { dev_id = hci_for_each_dev(HCI_UP, find_conn, (long) &bdaddr); if (dev_id < 0) { fprintf(stderr, "Not connected.\n"); exit(1); } } dd = hci_open_dev(dev_id); if (dd < 0) { perror("HCI device open failed"); exit(1); } cr = malloc(sizeof(*cr) + sizeof(struct hci_conn_info)); if (!cr) { perror("Can't allocate memory"); exit(1); } bacpy(&cr->bdaddr, &bdaddr); cr->type = ACL_LINK; if (ioctl(dd, HCIGETCONNINFO, (unsigned long) cr) < 0) { perror("Get connection info failed"); exit(1); } if (hci_read_clock_offset(dd, htobs(cr->conn_info->handle), &offset, 1000) < 0) { perror("Reading clock offset failed"); exit(1); } printf("Clock offset: 0x%4.4x\n", btohs(offset)); free(cr); hci_close_dev(dd); } /* Read clock */ static struct option clock_options[] = { { "help", 0, 0, 'h' }, { 0, 0, 0, 0 } }; static const char *clock_help = "Usage:\n" "\tclock [bdaddr] [which clock]\n"; static void cmd_clock(int dev_id, int argc, char **argv) { struct hci_conn_info_req *cr; bdaddr_t bdaddr; uint8_t which; uint32_t handle, clock; uint16_t accuracy; int opt, dd; for_each_opt(opt, clock_options, NULL) { switch (opt) { default: printf("%s", clock_help); return; } } helper_arg(0, 2, &argc, &argv, clock_help); if (argc > 0) str2ba(argv[0], &bdaddr); else bacpy(&bdaddr, BDADDR_ANY); if (dev_id < 0 && !bacmp(&bdaddr, BDADDR_ANY)) dev_id = hci_get_route(NULL); if (dev_id < 0) { dev_id = hci_for_each_dev(HCI_UP, find_conn, (long) &bdaddr); if (dev_id < 0) { fprintf(stderr, "Not connected.\n"); exit(1); } } dd = hci_open_dev(dev_id); if (dd < 0) { perror("HCI device open failed"); exit(1); } if (bacmp(&bdaddr, BDADDR_ANY)) { cr = malloc(sizeof(*cr) + sizeof(struct hci_conn_info)); if (!cr) { perror("Can't allocate memory"); exit(1); } bacpy(&cr->bdaddr, &bdaddr); cr->type = ACL_LINK; if (ioctl(dd, HCIGETCONNINFO, (unsigned long) cr) < 0) { perror("Get connection info failed"); free(cr); exit(1); } handle = htobs(cr->conn_info->handle); which = (argc > 1) ? atoi(argv[1]) : 0x01; free(cr); } else { handle = 0x00; which = 0x00; } if (hci_read_clock(dd, handle, which, &clock, &accuracy, 1000) < 0) { perror("Reading clock failed"); exit(1); } accuracy = btohs(accuracy); printf("Clock: 0x%4.4x\n", btohl(clock)); printf("Accuracy: %.2f msec\n", (float) accuracy * 0.3125); hci_close_dev(dd); } static int read_flags(uint8_t *flags, const uint8_t *data, size_t size) { size_t offset; if (!flags || !data) return -EINVAL; offset = 0; while (offset < size) { uint8_t len = data[offset]; uint8_t type; /* Check if it is the end of the significant part */ if (len == 0) break; if (len + offset > size) break; type = data[offset + 1]; if (type == FLAGS_AD_TYPE) { *flags = data[offset + 2]; return 0; } offset += 1 + len; } return -ENOENT; } static int check_report_filter(uint8_t procedure, le_advertising_info *info) { uint8_t flags; /* If no discovery procedure is set, all reports are treat as valid */ if (procedure == 0) return 1; /* Read flags AD type value from the advertising report if it exists */ if (read_flags(&flags, info->data, info->length)) return 0; switch (procedure) { case 'l': /* Limited Discovery Procedure */ if (flags & FLAGS_LIMITED_MODE_BIT) return 1; break; case 'g': /* General Discovery Procedure */ if (flags & (FLAGS_LIMITED_MODE_BIT | FLAGS_GENERAL_MODE_BIT)) return 1; break; default: fprintf(stderr, "Unknown discovery procedure\n"); } return 0; } static void sigint_handler(int sig) { signal_received = sig; } static void eir_parse_name(uint8_t *eir, size_t eir_len, char *buf, size_t buf_len) { size_t offset; offset = 0; while (offset < eir_len) { uint8_t field_len = eir[0]; size_t name_len; /* Check for the end of EIR */ if (field_len == 0) break; if (offset + field_len > eir_len) goto failed; switch (eir[1]) { case EIR_NAME_SHORT: case EIR_NAME_COMPLETE: name_len = field_len - 1; if (name_len > buf_len) goto failed; memcpy(buf, &eir[2], name_len); return; } offset += field_len + 1; eir += field_len + 1; } failed: snprintf(buf, buf_len, "(unknown)"); } static int print_advertising_devices(int dd, uint8_t filter_type) { unsigned char buf[HCI_MAX_EVENT_SIZE], *ptr; struct hci_filter nf, of; struct sigaction sa; socklen_t olen; int len; olen = sizeof(of); if (getsockopt(dd, SOL_HCI, HCI_FILTER, &of, &olen) < 0) { printf("Could not get socket options\n"); return -1; } hci_filter_clear(&nf); hci_filter_set_ptype(HCI_EVENT_PKT, &nf); hci_filter_set_event(EVT_LE_META_EVENT, &nf); if (setsockopt(dd, SOL_HCI, HCI_FILTER, &nf, sizeof(nf)) < 0) { printf("Could not set socket options\n"); return -1; } memset(&sa, 0, sizeof(sa)); sa.sa_flags = SA_NOCLDSTOP; sa.sa_handler = sigint_handler; sigaction(SIGINT, &sa, NULL); while (1) { evt_le_meta_event *meta; le_advertising_info *info; char addr[18]; while ((len = read(dd, buf, sizeof(buf))) < 0) { if (errno == EINTR && signal_received == SIGINT) { len = 0; goto done; } if (errno == EAGAIN || errno == EINTR) continue; goto done; } ptr = buf + (1 + HCI_EVENT_HDR_SIZE); len -= (1 + HCI_EVENT_HDR_SIZE); meta = (void *) ptr; if (meta->subevent != 0x02) goto done; /* Ignoring multiple reports */ info = (le_advertising_info *) (meta->data + 1); if (check_report_filter(filter_type, info)) { char name[30]; memset(name, 0, sizeof(name)); ba2str(&info->bdaddr, addr); eir_parse_name(info->data, info->length, name, sizeof(name) - 1); printf("%s %s\n", addr, name); } } done: setsockopt(dd, SOL_HCI, HCI_FILTER, &of, sizeof(of)); if (len < 0) return -1; return 0; } static struct option lescan_options[] = { { "help", 0, 0, 'h' }, { "static", 0, 0, 's' }, { "privacy", 0, 0, 'p' }, { "passive", 0, 0, 'P' }, { "whitelist", 0, 0, 'w' }, /* Deprecated. Kept for compatibility. */ { "acceptlist", 0, 0, 'a' }, { "discovery", 1, 0, 'd' }, { "duplicates", 0, 0, 'D' }, { 0, 0, 0, 0 } }; static const char *lescan_help = "Usage:\n" "\tlescan [--privacy] enable privacy\n" "\tlescan [--passive] set scan type passive (default active)\n" "\tlescan [--acceptlist] scan for address in the accept list only\n" "\tlescan [--discovery=g|l] enable general or limited discovery" "procedure\n" "\tlescan [--duplicates] don't filter duplicates\n"; static void cmd_lescan(int dev_id, int argc, char **argv) { int err, opt, dd; uint8_t own_type = LE_PUBLIC_ADDRESS; uint8_t scan_type = 0x01; uint8_t filter_type = 0; uint8_t filter_policy = 0x00; uint16_t interval = htobs(0x0010); uint16_t window = htobs(0x0010); uint8_t filter_dup = 0x01; for_each_opt(opt, lescan_options, NULL) { switch (opt) { case 's': own_type = LE_RANDOM_ADDRESS; break; case 'p': own_type = LE_RANDOM_ADDRESS; break; case 'P': scan_type = 0x00; /* Passive */ break; case 'w': /* Deprecated. Kept for compatibility. */ case 'a': filter_policy = 0x01; /* Accept list */ break; case 'd': filter_type = optarg[0]; if (filter_type != 'g' && filter_type != 'l') { fprintf(stderr, "Unknown discovery procedure\n"); exit(1); } interval = htobs(0x0012); window = htobs(0x0012); break; case 'D': filter_dup = 0x00; break; default: printf("%s", lescan_help); return; } } helper_arg(0, 1, &argc, &argv, lescan_help); if (dev_id < 0) dev_id = hci_get_route(NULL); dd = hci_open_dev(dev_id); if (dd < 0) { perror("Could not open device"); exit(1); } err = hci_le_set_scan_parameters(dd, scan_type, interval, window, own_type, filter_policy, 10000); if (err < 0) { perror("Set scan parameters failed"); exit(1); } err = hci_le_set_scan_enable(dd, 0x01, filter_dup, 10000); if (err < 0) { perror("Enable scan failed"); exit(1); } printf("LE Scan ...\n"); err = print_advertising_devices(dd, filter_type); if (err < 0) { perror("Could not receive advertising events"); exit(1); } err = hci_le_set_scan_enable(dd, 0x00, filter_dup, 10000); if (err < 0) { perror("Disable scan failed"); exit(1); } hci_close_dev(dd); } static struct option leinfo_options[] = { { "help", 0, 0, 'h' }, { "static", 0, 0, 's' }, { "random", 0, 0, 'r' }, { 0, 0, 0, 0 } }; static const char *leinfo_help = "Usage:\n" "\tleinfo [--static] [--random] \n"; static void cmd_leinfo(int dev_id, int argc, char **argv) { bdaddr_t bdaddr; uint16_t handle; uint8_t features[8]; struct hci_version version; uint16_t interval, latency, max_ce_length, max_interval, min_ce_length; uint16_t min_interval, supervision_timeout, window; uint8_t initiator_filter, own_bdaddr_type, peer_bdaddr_type; int opt, err, dd; own_bdaddr_type = LE_PUBLIC_ADDRESS; peer_bdaddr_type = LE_PUBLIC_ADDRESS; for_each_opt(opt, leinfo_options, NULL) { switch (opt) { case 's': own_bdaddr_type = LE_RANDOM_ADDRESS; break; case 'r': peer_bdaddr_type = LE_RANDOM_ADDRESS; break; default: printf("%s", leinfo_help); return; } } helper_arg(1, 1, &argc, &argv, leinfo_help); str2ba(argv[0], &bdaddr); printf("Requesting information ...\n"); if (dev_id < 0) dev_id = hci_get_route(NULL); dd = hci_open_dev(dev_id); if (dd < 0) { perror("Could not open device"); exit(1); } interval = htobs(0x0004); window = htobs(0x0004); initiator_filter = 0; min_interval = htobs(0x000F); max_interval = htobs(0x000F); latency = htobs(0x0000); supervision_timeout = htobs(0x0C80); min_ce_length = htobs(0x0000); max_ce_length = htobs(0x0000); err = hci_le_create_conn(dd, interval, window, initiator_filter, peer_bdaddr_type, bdaddr, own_bdaddr_type, min_interval, max_interval, latency, supervision_timeout, min_ce_length, max_ce_length, &handle, 25000); if (err < 0) { perror("Could not create connection"); exit(1); } printf("\tHandle: %d (0x%04x)\n", handle, handle); if (hci_read_remote_version(dd, handle, &version, 20000) == 0) { char *ver = lmp_vertostr(version.lmp_ver); printf("\tLMP Version: %s (0x%x) LMP Subversion: 0x%x\n" "\tManufacturer: %s (%d)\n", ver ? ver : "n/a", version.lmp_ver, version.lmp_subver, bt_compidtostr(version.manufacturer), version.manufacturer); if (ver) bt_free(ver); } memset(features, 0, sizeof(features)); hci_le_read_remote_features(dd, handle, features, 20000); printf("\tFeatures: 0x%2.2x 0x%2.2x 0x%2.2x 0x%2.2x " "0x%2.2x 0x%2.2x 0x%2.2x 0x%2.2x\n", features[0], features[1], features[2], features[3], features[4], features[5], features[6], features[7]); usleep(10000); hci_disconnect(dd, handle, HCI_OE_USER_ENDED_CONNECTION, 10000); hci_close_dev(dd); } static struct option lecc_options[] = { { "help", 0, 0, 'h' }, { "static", 0, 0, 's' }, { "random", 0, 0, 'r' }, { "whitelist", 0, 0, 'w' }, /* Deprecated. Kept for compatibility. */ { "acceptlist", 0, 0, 'a' }, { 0, 0, 0, 0 } }; static const char *lecc_help = "Usage:\n" "\tlecc [--static] [--random] \n" "\tlecc --acceptlist\n"; static void cmd_lecc(int dev_id, int argc, char **argv) { int err, opt, dd; bdaddr_t bdaddr; uint16_t interval, latency, max_ce_length, max_interval, min_ce_length; uint16_t min_interval, supervision_timeout, window, handle; uint8_t initiator_filter, own_bdaddr_type, peer_bdaddr_type; own_bdaddr_type = LE_PUBLIC_ADDRESS; peer_bdaddr_type = LE_PUBLIC_ADDRESS; initiator_filter = 0; /* Use peer address */ for_each_opt(opt, lecc_options, NULL) { switch (opt) { case 's': own_bdaddr_type = LE_RANDOM_ADDRESS; break; case 'r': peer_bdaddr_type = LE_RANDOM_ADDRESS; break; case 'w': /* Deprecated. Kept for compatibility. */ case 'a': initiator_filter = 0x01; /* Use accept list */ break; default: printf("%s", lecc_help); return; } } helper_arg(0, 1, &argc, &argv, lecc_help); if (dev_id < 0) dev_id = hci_get_route(NULL); dd = hci_open_dev(dev_id); if (dd < 0) { perror("Could not open device"); exit(1); } memset(&bdaddr, 0, sizeof(bdaddr_t)); if (argv[0]) str2ba(argv[0], &bdaddr); interval = htobs(0x0004); window = htobs(0x0004); min_interval = htobs(0x000F); max_interval = htobs(0x000F); latency = htobs(0x0000); supervision_timeout = htobs(0x0C80); min_ce_length = htobs(0x0001); max_ce_length = htobs(0x0001); err = hci_le_create_conn(dd, interval, window, initiator_filter, peer_bdaddr_type, bdaddr, own_bdaddr_type, min_interval, max_interval, latency, supervision_timeout, min_ce_length, max_ce_length, &handle, 25000); if (err < 0) { perror("Could not create connection"); exit(1); } printf("Connection handle %d\n", handle); hci_close_dev(dd); } static struct option lealadd_options[] = { { "help", 0, 0, 'h' }, { "random", 0, 0, 'r' }, { 0, 0, 0, 0 } }; static const char *lealadd_help = "Usage:\n" "\tlealadd [--random] \n"; static void cmd_lealadd(int dev_id, int argc, char **argv) { int err, opt, dd; bdaddr_t bdaddr; uint8_t bdaddr_type = LE_PUBLIC_ADDRESS; for_each_opt(opt, lealadd_options, NULL) { switch (opt) { case 'r': bdaddr_type = LE_RANDOM_ADDRESS; break; default: printf("%s", lealadd_help); return; } } helper_arg(1, 1, &argc, &argv, lealadd_help); if (dev_id < 0) dev_id = hci_get_route(NULL); dd = hci_open_dev(dev_id); if (dd < 0) { perror("Could not open device"); exit(1); } str2ba(argv[0], &bdaddr); err = hci_le_add_white_list(dd, &bdaddr, bdaddr_type, 1000); hci_close_dev(dd); if (err < 0) { err = -errno; fprintf(stderr, "Can't add to accept list: %s(%d)\n", strerror(-err), -err); exit(1); } } static struct option lealrm_options[] = { { "help", 0, 0, 'h' }, { 0, 0, 0, 0 } }; static const char *lealrm_help = "Usage:\n" "\tlealrm \n"; static void cmd_lealrm(int dev_id, int argc, char **argv) { int err, opt, dd; bdaddr_t bdaddr; for_each_opt(opt, lealrm_options, NULL) { switch (opt) { default: printf("%s", lealrm_help); return; } } helper_arg(1, 1, &argc, &argv, lealrm_help); if (dev_id < 0) dev_id = hci_get_route(NULL); dd = hci_open_dev(dev_id); if (dd < 0) { perror("Could not open device"); exit(1); } str2ba(argv[0], &bdaddr); err = hci_le_rm_white_list(dd, &bdaddr, LE_PUBLIC_ADDRESS, 1000); hci_close_dev(dd); if (err < 0) { err = errno; fprintf(stderr, "Can't remove from accept list: %s(%d)\n", strerror(err), err); exit(1); } } static struct option lealsz_options[] = { { "help", 0, 0, 'h' }, { 0, 0, 0, 0 } }; static const char *lealsz_help = "Usage:\n" "\tlealsz\n"; static void cmd_lealsz(int dev_id, int argc, char **argv) { int err, dd, opt; uint8_t size; for_each_opt(opt, lealsz_options, NULL) { switch (opt) { default: printf("%s", lealsz_help); return; } } helper_arg(0, 0, &argc, &argv, lealsz_help); if (dev_id < 0) dev_id = hci_get_route(NULL); dd = hci_open_dev(dev_id); if (dd < 0) { perror("Could not open device"); exit(1); } err = hci_le_read_white_list_size(dd, &size, 1000); hci_close_dev(dd); if (err < 0) { err = -errno; fprintf(stderr, "Can't read accept list size: %s(%d)\n", strerror(-err), -err); exit(1); } printf("Accept list size: %d\n", size); } static struct option lealclr_options[] = { { "help", 0, 0, 'h' }, { 0, 0, 0, 0 } }; static const char *lealclr_help = "Usage:\n" "\tlealclr\n"; static void cmd_lealclr(int dev_id, int argc, char **argv) { int err, dd, opt; for_each_opt(opt, lealclr_options, NULL) { switch (opt) { default: printf("%s", lealclr_help); return; } } helper_arg(0, 0, &argc, &argv, lealclr_help); if (dev_id < 0) dev_id = hci_get_route(NULL); dd = hci_open_dev(dev_id); if (dd < 0) { perror("Could not open device"); exit(1); } err = hci_le_clear_white_list(dd, 1000); hci_close_dev(dd); if (err < 0) { err = -errno; fprintf(stderr, "Can't clear accept list: %s(%d)\n", strerror(-err), -err); exit(1); } } static struct option lerladd_options[] = { { "help", 0, 0, 'h' }, { "random", 0, 0, 'r' }, { "local", 1, 0, 'l' }, { "peer", 1, 0, 'p' }, { 0, 0, 0, 0 } }; static const char *lerladd_help = "Usage:\n" "\tlerladd [--local irk] [--peer irk] [--random] \n"; static void cmd_lerladd(int dev_id, int argc, char **argv) { int err, opt, dd; bdaddr_t bdaddr; uint8_t bdaddr_type = LE_PUBLIC_ADDRESS; uint8_t local_irk[16], peer_irk[16]; memset(local_irk, 0, 16); memset(peer_irk, 0, 16); for_each_opt(opt, lerladd_options, NULL) { switch (opt) { case 'r': bdaddr_type = LE_RANDOM_ADDRESS; break; case 'l': str2buf(optarg, local_irk, 16); break; case 'p': str2buf(optarg, peer_irk, 16); break; default: printf("%s", lerladd_help); return; } } helper_arg(1, 1, &argc, &argv, lerladd_help); if (dev_id < 0) dev_id = hci_get_route(NULL); dd = hci_open_dev(dev_id); if (dd < 0) { perror("Could not open device"); exit(1); } str2ba(argv[0], &bdaddr); err = hci_le_add_resolving_list(dd, &bdaddr, bdaddr_type, peer_irk, local_irk, 1000); hci_close_dev(dd); if (err < 0) { err = -errno; fprintf(stderr, "Can't add to resolving list: %s(%d)\n", strerror(-err), -err); exit(1); } } static struct option lerlrm_options[] = { { "help", 0, 0, 'h' }, { 0, 0, 0, 0 } }; static const char *lerlrm_help = "Usage:\n" "\tlerlrm \n"; static void cmd_lerlrm(int dev_id, int argc, char **argv) { int err, opt, dd; bdaddr_t bdaddr; for_each_opt(opt, lerlrm_options, NULL) { switch (opt) { default: printf("%s", lerlrm_help); return; } } helper_arg(1, 1, &argc, &argv, lerlrm_help); if (dev_id < 0) dev_id = hci_get_route(NULL); dd = hci_open_dev(dev_id); if (dd < 0) { perror("Could not open device"); exit(1); } str2ba(argv[0], &bdaddr); err = hci_le_rm_resolving_list(dd, &bdaddr, LE_PUBLIC_ADDRESS, 1000); hci_close_dev(dd); if (err < 0) { err = errno; fprintf(stderr, "Can't remove from resolving list: %s(%d)\n", strerror(err), err); exit(1); } } static struct option lerlclr_options[] = { { "help", 0, 0, 'h' }, { 0, 0, 0, 0 } }; static const char *lerlclr_help = "Usage:\n" "\tlerlclr\n"; static void cmd_lerlclr(int dev_id, int argc, char **argv) { int err, dd, opt; for_each_opt(opt, lerlclr_options, NULL) { switch (opt) { default: printf("%s", lerlclr_help); return; } } helper_arg(0, 0, &argc, &argv, lerlclr_help); if (dev_id < 0) dev_id = hci_get_route(NULL); dd = hci_open_dev(dev_id); if (dd < 0) { perror("Could not open device"); exit(1); } err = hci_le_clear_resolving_list(dd, 1000); hci_close_dev(dd); if (err < 0) { err = -errno; fprintf(stderr, "Can't clear resolving list: %s(%d)\n", strerror(-err), -err); exit(1); } } static struct option lerlsz_options[] = { { "help", 0, 0, 'h' }, { 0, 0, 0, 0 } }; static const char *lerlsz_help = "Usage:\n" "\tlerlsz\n"; static void cmd_lerlsz(int dev_id, int argc, char **argv) { int err, dd, opt; uint8_t size; for_each_opt(opt, lerlsz_options, NULL) { switch (opt) { default: printf("%s", lerlsz_help); return; } } helper_arg(0, 0, &argc, &argv, lerlsz_help); if (dev_id < 0) dev_id = hci_get_route(NULL); dd = hci_open_dev(dev_id); if (dd < 0) { perror("Could not open device"); exit(1); } err = hci_le_read_resolving_list_size(dd, &size, 1000); hci_close_dev(dd); if (err < 0) { err = -errno; fprintf(stderr, "Can't read resolving list size: %s(%d)\n", strerror(-err), -err); exit(1); } printf("Resolving list size: %d\n", size); } static struct option lerlon_options[] = { { "help", 0, 0, 'h' }, { 0, 0, 0, 0 } }; static const char *lerlon_help = "Usage:\n" "\tlerlon\n"; static void cmd_lerlon(int dev_id, int argc, char **argv) { int err, dd, opt; for_each_opt(opt, lerlon_options, NULL) { switch (opt) { default: printf("%s", lerlon_help); return; } } helper_arg(0, 0, &argc, &argv, lerlon_help); if (dev_id < 0) dev_id = hci_get_route(NULL); dd = hci_open_dev(dev_id); if (dd < 0) { perror("Could not open device"); exit(1); } err = hci_le_set_address_resolution_enable(dd, 0x01, 1000); hci_close_dev(dd); if (err < 0) { err = -errno; fprintf(stderr, "Can't set address resolution enable: %s(%d)\n", strerror(-err), -err); exit(1); } } static struct option lerloff_options[] = { { "help", 0, 0, 'h' }, { 0, 0, 0, 0 } }; static const char *lerloff_help = "Usage:\n" "\tlerloff\n"; static void cmd_lerloff(int dev_id, int argc, char **argv) { int err, dd, opt; for_each_opt(opt, lerloff_options, NULL) { switch (opt) { default: printf("%s", lerloff_help); return; } } helper_arg(0, 0, &argc, &argv, lerloff_help); if (dev_id < 0) dev_id = hci_get_route(NULL); dd = hci_open_dev(dev_id); if (dd < 0) { perror("Could not open device"); exit(1); } err = hci_le_set_address_resolution_enable(dd, 0x00, 1000); hci_close_dev(dd); if (err < 0) { err = -errno; fprintf(stderr, "Can't set address resolution enable: %s(%d)\n", strerror(-err), -err); exit(1); } } static struct option ledc_options[] = { { "help", 0, 0, 'h' }, { 0, 0, 0, 0 } }; static const char *ledc_help = "Usage:\n" "\tledc [reason]\n"; static void cmd_ledc(int dev_id, int argc, char **argv) { int err, opt, dd; uint16_t handle; uint8_t reason; for_each_opt(opt, ledc_options, NULL) { switch (opt) { default: printf("%s", ledc_help); return; } } helper_arg(1, 2, &argc, &argv, ledc_help); if (dev_id < 0) dev_id = hci_get_route(NULL); dd = hci_open_dev(dev_id); if (dd < 0) { perror("Could not open device"); exit(1); } handle = atoi(argv[0]); reason = (argc > 1) ? atoi(argv[1]) : HCI_OE_USER_ENDED_CONNECTION; err = hci_disconnect(dd, handle, reason, 10000); if (err < 0) { perror("Could not disconnect"); exit(1); } hci_close_dev(dd); } static struct option lecup_options[] = { { "help", 0, 0, 'h' }, { "handle", 1, 0, 'H' }, { "min", 1, 0, 'm' }, { "max", 1, 0, 'M' }, { "latency", 1, 0, 'l' }, { "timeout", 1, 0, 't' }, { 0, 0, 0, 0 } }; static const char *lecup_help = "Usage:\n" "\tlecup \n" "\tOptions:\n" "\t --handle=<0xXXXX> LE connection handle\n" "\t --min= Range: 0x0006 to 0x0C80\n" "\t --max= Range: 0x0006 to 0x0C80\n" "\t --latency= Peripheral latency. Range: 0x0000 to 0x03E8\n" "\t --timeout=